summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJakub Zelenka <bukka@php.net>2016-06-19 16:20:02 +0100
committerJakub Zelenka <bukka@php.net>2016-06-19 16:20:02 +0100
commitc2b90805e81de43b4fa28fa1b30ed255bfba76b2 (patch)
treea353bb7b1cb958fd4f3e576ab44f3b6ef03a02c2
parentb44cf1a8540d321583a0d83ebca688ebab10d3b0 (diff)
parentd0e84c6f4702dc5dda473851dd33c5e6cee82ee6 (diff)
downloadphp-git-c2b90805e81de43b4fa28fa1b30ed255bfba76b2.tar.gz
Merge branch 'master' into openssl_error_store
-rw-r--r--CODING_STANDARDS2
-rw-r--r--NEWS1463
-rw-r--r--README.GIT-RULES2
-rw-r--r--README.RELEASE_PROCESS12
-rw-r--r--README.md2
-rw-r--r--UPGRADING837
-rw-r--r--UPGRADING.INTERNALS291
-rw-r--r--Zend/tests/001.phpt21
-rw-r--r--Zend/tests/002.phpt31
-rw-r--r--Zend/tests/003.phpt24
-rw-r--r--Zend/tests/030.phpt38
-rw-r--r--Zend/tests/add_006.phpt8
-rw-r--r--Zend/tests/add_007.phpt5
-rw-r--r--Zend/tests/anon/013.phpt15
-rw-r--r--Zend/tests/anon/014.phpt16
-rw-r--r--Zend/tests/assert/expect_015.phpt2
-rw-r--r--Zend/tests/assert/indirect_var_access_misoptimization.phpt19
-rw-r--r--Zend/tests/bug29015.phpt11
-rw-r--r--Zend/tests/bug29883.phpt2
-rw-r--r--Zend/tests/bug31098.phpt2
-rw-r--r--Zend/tests/bug33996.phpt22
-rw-r--r--Zend/tests/bug38047.phpt10
-rw-r--r--Zend/tests/bug41813.phpt2
-rw-r--r--Zend/tests/bug49866.phpt2
-rw-r--r--Zend/tests/bug52484.phpt6
-rw-r--r--Zend/tests/bug52484_2.phpt6
-rw-r--r--Zend/tests/bug52484_3.phpt6
-rw-r--r--Zend/tests/bug52879.phpt2
-rw-r--r--Zend/tests/bug53432.phpt65
-rw-r--r--Zend/tests/bug55705.phpt2
-rw-r--r--Zend/tests/bug62814.phpt20
-rw-r--r--Zend/tests/bug65784.phpt11
-rw-r--r--Zend/tests/bug68370.phpt4
-rw-r--r--Zend/tests/bug68652.phpt1
-rw-r--r--Zend/tests/bug69989_2.phpt42
-rw-r--r--Zend/tests/bug69989_3.phpt44
-rw-r--r--Zend/tests/bug70089.phpt2
-rw-r--r--Zend/tests/bug70685.phpt2
-rw-r--r--Zend/tests/bug70689.phpt6
-rw-r--r--Zend/tests/bug70918.phpt47
-rw-r--r--Zend/tests/bug71196.phpt13
-rw-r--r--Zend/tests/bug71266.phpt22
-rw-r--r--Zend/tests/bug71428.1.phpt4
-rw-r--r--Zend/tests/bug71428.3.phpt4
-rw-r--r--Zend/tests/bug71572.phpt27
-rw-r--r--Zend/tests/bug72107.phpt14
-rw-r--r--Zend/tests/bug72119.phpt5
-rw-r--r--Zend/tests/bug72177.phpt35
-rw-r--r--Zend/tests/bug72177_2.phpt34
-rw-r--r--Zend/tests/bug72188.phpt27
-rw-r--r--Zend/tests/bug72221.phpt11
-rw-r--r--Zend/tests/bug72335.phpt18
-rw-r--r--Zend/tests/bug72373.phpt20
-rw-r--r--Zend/tests/bug72395.phpt10
-rw-r--r--Zend/tests/bug72441.phpt14
-rw-r--r--Zend/tests/closure_027.phpt10
-rw-r--r--Zend/tests/closure_061.phpt4
-rw-r--r--Zend/tests/closure_use_auto_global.phpt16
-rw-r--r--Zend/tests/closure_use_parameter_name.phpt14
-rw-r--r--Zend/tests/closure_use_variable_twice.phpt15
-rw-r--r--Zend/tests/constant_expressions_dynamic.phpt4
-rw-r--r--Zend/tests/constant_expressions_self_referencing_array.phpt2
-rw-r--r--Zend/tests/dynamic_call_005.phpt29
-rw-r--r--Zend/tests/dynamic_call_006.phpt48
-rw-r--r--Zend/tests/dynamic_call_007.phpt17
-rw-r--r--Zend/tests/dynamic_call_008.phpt13
-rw-r--r--Zend/tests/empty_str_offset.phpt12
-rw-r--r--Zend/tests/entry_block_with_predecessors.phpt33
-rw-r--r--Zend/tests/error_reporting06.phpt2
-rw-r--r--Zend/tests/error_reporting07.phpt2
-rw-r--r--Zend/tests/error_reporting08.phpt2
-rw-r--r--Zend/tests/foreach_list_keyed.phpt36
-rw-r--r--Zend/tests/fr47160.phpt34
-rw-r--r--Zend/tests/func_get_args.phpt10
-rw-r--r--Zend/tests/gc_013.phpt4
-rw-r--r--Zend/tests/gc_014.phpt4
-rw-r--r--Zend/tests/gc_015.phpt4
-rw-r--r--Zend/tests/gc_017.phpt5
-rw-r--r--Zend/tests/generators/gc_with_yield_from.phpt45
-rw-r--r--Zend/tests/generators/generator_with_arg_unpacking.phpt12
-rw-r--r--Zend/tests/generators/generator_with_type_check.phpt13
-rw-r--r--Zend/tests/generators/generator_with_type_check_2.phpt22
-rw-r--r--Zend/tests/generators/yield_from_iterator_agregate.phpt17
-rw-r--r--Zend/tests/global_with_side_effect_name.phpt22
-rw-r--r--Zend/tests/incompat_ctx_user.phpt11
-rw-r--r--Zend/tests/indexing_001.phpt41
-rw-r--r--Zend/tests/indirect_call_array_003.phpt11
-rw-r--r--Zend/tests/inference_infinite_loop.phpt17
-rw-r--r--Zend/tests/int_conversion_exponents.phpt52
-rw-r--r--Zend/tests/isset_str_offset.phpt6
-rw-r--r--Zend/tests/jump16.phpt27
-rw-r--r--Zend/tests/jump17.phpt22
-rw-r--r--Zend/tests/list_001.phpt6
-rw-r--r--Zend/tests/list_008.phpt10
-rw-r--r--Zend/tests/list_009.phpt14
-rw-r--r--Zend/tests/list_010.phpt11
-rw-r--r--Zend/tests/list_011.phpt10
-rw-r--r--Zend/tests/list_012.phpt10
-rw-r--r--Zend/tests/list_013.phpt10
-rw-r--r--Zend/tests/list_keyed.phpt71
-rw-r--r--Zend/tests/list_keyed_ArrayAccess.phpt54
-rw-r--r--Zend/tests/list_keyed_conversions.phpt34
-rw-r--r--Zend/tests/list_keyed_evaluation_order.inc60
-rw-r--r--Zend/tests/list_keyed_evaluation_order.phpt35
-rw-r--r--Zend/tests/list_keyed_evaluation_order_2.phpt77
-rw-r--r--Zend/tests/list_keyed_evaluation_order_3.phpt24
-rw-r--r--Zend/tests/list_keyed_evaluation_order_nested.phpt77
-rw-r--r--Zend/tests/list_keyed_non_literals.phpt30
-rw-r--r--Zend/tests/list_keyed_trailing_comma.phpt38
-rw-r--r--Zend/tests/list_keyed_undefined.phpt22
-rw-r--r--Zend/tests/list_mixed_keyed_unkeyed.phpt16
-rw-r--r--Zend/tests/list_mixed_nested_keyed_unkeyed.phpt34
-rw-r--r--Zend/tests/method_argument_binding.phpt47
-rw-r--r--Zend/tests/new_args_without_ctor.phpt10
-rw-r--r--Zend/tests/nullable_types/array.phpt17
-rw-r--r--Zend/tests/nullable_types/contravariant_nullable_param_succeeds.phpt19
-rw-r--r--Zend/tests/nullable_types/contravariant_nullable_return_fails.phpt17
-rw-r--r--Zend/tests/nullable_types/covariant_nullable_param_fails.phpt17
-rw-r--r--Zend/tests/nullable_types/covariant_nullable_return_succeds.phpt16
-rw-r--r--Zend/tests/nullable_types/float.phpt16
-rw-r--r--Zend/tests/nullable_types/int.phpt16
-rw-r--r--Zend/tests/nullable_types/invariant_param_and_return_succeeds.phpt24
-rw-r--r--Zend/tests/nullable_types/nullable_type_parameters_do_not_have_default_value.phpt17
-rw-r--r--Zend/tests/nullable_types/string.phpt16
-rw-r--r--Zend/tests/numeric_string_errors.phpt195
-rw-r--r--Zend/tests/numeric_string_errors_assign.phpt236
-rw-r--r--Zend/tests/oct_overflow_char.phpt10
-rw-r--r--Zend/tests/overloaded_func_001.phpt15
-rw-r--r--Zend/tests/overloaded_func_002.phpt13
-rw-r--r--Zend/tests/parse_str_with_unpack.phpt15
-rw-r--r--Zend/tests/php_errormsg_misoptimization.phpt20
-rw-r--r--Zend/tests/return_types/029.phpt9
-rw-r--r--Zend/tests/return_types/030.phpt23
-rw-r--r--Zend/tests/return_types/031.phpt14
-rw-r--r--Zend/tests/return_types/032.phpt14
-rw-r--r--Zend/tests/return_types/bug71092.phpt4
-rw-r--r--Zend/tests/return_types/void_allowed.phpt20
-rw-r--r--Zend/tests/return_types/void_disallowed1.phpt12
-rw-r--r--Zend/tests/return_types/void_disallowed2.phpt12
-rw-r--r--Zend/tests/return_types/void_parameter.phpt8
-rw-r--r--Zend/tests/self_and.phpt4
-rw-r--r--Zend/tests/self_instanceof_outside_class.phpt17
-rw-r--r--Zend/tests/self_method_or_prop_outside_class.phpt36
-rw-r--r--Zend/tests/self_mod.phpt4
-rw-r--r--Zend/tests/self_or.phpt4
-rw-r--r--Zend/tests/self_xor.phpt4
-rw-r--r--Zend/tests/shift_001.phpt4
-rw-r--r--Zend/tests/shift_002.phpt4
-rw-r--r--Zend/tests/str_offset_001.phpt97
-rw-r--r--Zend/tests/str_offset_003.phpt37
-rw-r--r--Zend/tests/str_offset_004.phpt49
-rw-r--r--Zend/tests/temporary_cleaning_001.phpt2
-rw-r--r--Zend/tests/temporary_cleaning_003.phpt2
-rw-r--r--Zend/tests/temporary_cleaning_004.phpt2
-rw-r--r--Zend/tests/temporary_cleaning_005.phpt2
-rw-r--r--Zend/tests/temporary_cleaning_006.phpt2
-rw-r--r--Zend/tests/temporary_cleaning_008.phpt15
-rw-r--r--Zend/tests/temporary_cleaning_009.phpt27
-rw-r--r--Zend/tests/temporary_cleaning_010.phpt21
-rw-r--r--Zend/tests/temporary_cleaning_011.phpt20
-rw-r--r--Zend/tests/temporary_cleaning_012.phpt20
-rw-r--r--Zend/tests/this_as_global.phpt12
-rw-r--r--Zend/tests/this_as_parameter.phpt11
-rw-r--r--Zend/tests/this_as_static.phpt12
-rw-r--r--Zend/tests/this_in_catch.phpt18
-rw-r--r--Zend/tests/this_in_extract.phpt17
-rw-r--r--Zend/tests/this_in_foreach_001.phpt11
-rw-r--r--Zend/tests/this_in_foreach_002.phpt11
-rw-r--r--Zend/tests/this_in_foreach_003.phpt11
-rw-r--r--Zend/tests/this_in_foreach_004.phpt11
-rw-r--r--Zend/tests/this_in_isset.phpt41
-rw-r--r--Zend/tests/this_in_mb_parse_str.phpt19
-rw-r--r--Zend/tests/this_in_parse_str.phpt17
-rw-r--r--Zend/tests/this_in_unset.phpt8
-rw-r--r--Zend/tests/this_reassign.phpt17
-rw-r--r--Zend/tests/traits/no_static_arg_binding.phpt28
-rw-r--r--Zend/tests/try/bug70228.phpt6
-rw-r--r--Zend/tests/try/bug70228_2.phpt20
-rw-r--r--Zend/tests/try/bug70228_3.phpt31
-rw-r--r--Zend/tests/try/bug70228_4.phpt32
-rw-r--r--Zend/tests/try/bug70228_5.phpt20
-rw-r--r--Zend/tests/try/bug70228_6.phpt18
-rw-r--r--Zend/tests/try/bug70228_7.phpt29
-rw-r--r--Zend/tests/try/bug70228_8.phpt30
-rw-r--r--Zend/tests/try/bug71604.phpt25
-rw-r--r--Zend/tests/try/bug71604_2.phpt39
-rw-r--r--Zend/tests/try/bug71604_3.phpt38
-rw-r--r--Zend/tests/try/bug72213.phpt25
-rw-r--r--Zend/tests/try/bug72213_2.phpt25
-rw-r--r--Zend/tests/try/exceptions.inc6
-rw-r--r--Zend/tests/try/try_finally_021.phpt20
-rw-r--r--Zend/tests/try/try_finally_022.phpt41
-rw-r--r--Zend/tests/try/try_finally_023.phpt37
-rw-r--r--Zend/tests/try/try_finally_024.phpt38
-rw-r--r--Zend/tests/try/try_finally_025.phpt28
-rw-r--r--Zend/tests/try/try_finally_026.phpt37
-rw-r--r--Zend/tests/try/try_finally_027.phpt34
-rw-r--r--Zend/tests/try/try_multicatch_001.phpt19
-rw-r--r--Zend/tests/try/try_multicatch_002.phpt21
-rw-r--r--Zend/tests/try/try_multicatch_003.phpt21
-rw-r--r--Zend/tests/try/try_multicatch_004.phpt21
-rw-r--r--Zend/tests/try/try_multicatch_005.phpt25
-rw-r--r--Zend/tests/try/try_multicatch_006.phpt22
-rw-r--r--Zend/tests/try/try_multicatch_007.phpt22
-rw-r--r--Zend/tests/type_declarations/nullable_typed_return_without_value.phpt14
-rw-r--r--Zend/tests/type_declarations/nullable_void.phpt11
-rw-r--r--Zend/tests/type_declarations/scalar_none.phpt10
-rw-r--r--Zend/tests/type_declarations/typed_return_without_value.phpt14
-rw-r--r--Zend/tests/variadic/adding_additional_optional_parameter_error.phpt2
-rw-r--r--Zend/zend.c116
-rw-r--r--Zend/zend.h13
-rw-r--r--Zend/zend_API.c208
-rw-r--r--Zend/zend_API.h41
-rw-r--r--Zend/zend_alloc.c48
-rw-r--r--Zend/zend_ast.c34
-rw-r--r--Zend/zend_ast.h11
-rw-r--r--Zend/zend_bitset.h91
-rw-r--r--Zend/zend_builtin_functions.c222
-rw-r--r--Zend/zend_closures.c58
-rw-r--r--Zend/zend_closures.h1
-rw-r--r--Zend/zend_compile.c1390
-rw-r--r--Zend/zend_compile.h129
-rw-r--r--Zend/zend_constants.c59
-rw-r--r--Zend/zend_constants.h5
-rw-r--r--Zend/zend_exceptions.c255
-rw-r--r--Zend/zend_execute.c1412
-rw-r--r--Zend/zend_execute.h66
-rw-r--r--Zend/zend_execute_API.c274
-rw-r--r--Zend/zend_extensions.h30
-rw-r--r--Zend/zend_gc.c2
-rw-r--r--Zend/zend_generators.c268
-rw-r--r--Zend/zend_generators.h10
-rw-r--r--Zend/zend_globals.h9
-rw-r--r--Zend/zend_globals_macros.h28
-rw-r--r--Zend/zend_hash.c25
-rw-r--r--Zend/zend_hash.h22
-rw-r--r--Zend/zend_inheritance.c119
-rw-r--r--Zend/zend_ini_parser.y5
-rw-r--r--Zend/zend_interfaces.c2
-rw-r--r--Zend/zend_iterators.c4
-rw-r--r--Zend/zend_language_parser.y119
-rw-r--r--Zend/zend_language_scanner.c909
-rw-r--r--Zend/zend_language_scanner.l127
-rw-r--r--Zend/zend_modules.h2
-rw-r--r--Zend/zend_object_handlers.c259
-rw-r--r--Zend/zend_object_handlers.h6
-rw-r--r--Zend/zend_objects.c63
-rw-r--r--Zend/zend_opcode.c146
-rw-r--r--Zend/zend_operators.c162
-rw-r--r--Zend/zend_operators.h40
-rw-r--r--Zend/zend_portability.h2
-rw-r--r--Zend/zend_smart_str.c60
-rw-r--r--Zend/zend_smart_str.h1
-rw-r--r--Zend/zend_sprintf.c5
-rw-r--r--Zend/zend_string.c51
-rw-r--r--Zend/zend_string.h59
-rw-r--r--Zend/zend_strtod.c14
-rw-r--r--Zend/zend_type_info.h68
-rw-r--r--Zend/zend_types.h38
-rw-r--r--Zend/zend_variables.c114
-rw-r--r--Zend/zend_variables.h18
-rw-r--r--Zend/zend_vm.h3
-rw-r--r--Zend/zend_vm_def.h3839
-rw-r--r--Zend/zend_vm_execute.h37612
-rw-r--r--Zend/zend_vm_execute.skl42
-rw-r--r--Zend/zend_vm_gen.php1586
-rw-r--r--Zend/zend_vm_opcodes.c215
-rw-r--r--Zend/zend_vm_opcodes.h50
-rw-r--r--acinclude.m440
-rw-r--r--configure.in4
-rw-r--r--ext/bz2/tests/bug72447.phpt15
-rw-r--r--ext/curl/interface.c14
-rw-r--r--ext/curl/tests/bug72202.phpt36
-rw-r--r--ext/date/lib/timezonedb.h3167
-rw-r--r--ext/date/php_date.c5
-rw-r--r--ext/date/tests/bug33414-1.phpt8
-rw-r--r--ext/date/tests/date_sun_info_003.phpt42
-rw-r--r--ext/dom/xpath.c4
-rw-r--r--ext/enchant/enchant.c4
-rw-r--r--ext/exif/exif.c6
-rw-r--r--ext/exif/tests/bug50845.jpgbin0 -> 803603 bytes
-rw-r--r--ext/exif/tests/bug50845.phpt140
-rwxr-xr-xext/ext_skel4
-rw-r--r--ext/fileinfo/fileinfo.c4
-rw-r--r--ext/gd/libgd/gd.c54
-rw-r--r--ext/gd/libgd/xbm.c2
-rw-r--r--ext/gd/tests/bug43475.phpt59
-rw-r--r--ext/gd/tests/bug43475.pngbin0 -> 4697 bytes
-rw-r--r--ext/gd/tests/bug53640.phpt2
-rw-r--r--ext/hash/config.m44
-rw-r--r--ext/hash/config.w324
-rw-r--r--ext/hash/hash.c16
-rw-r--r--ext/hash/hash_adler32.c2
-rw-r--r--ext/hash/hash_fnv.c16
-rw-r--r--ext/hash/hash_gost.c14
-rw-r--r--ext/hash/hash_haval.c44
-rw-r--r--ext/hash/hash_joaat.c4
-rw-r--r--ext/hash/hash_md.c42
-rw-r--r--ext/hash/hash_ripemd.c66
-rw-r--r--ext/hash/hash_sha.c162
-rw-r--r--ext/hash/hash_sha3.c238
-rw-r--r--ext/hash/hash_snefru.c14
-rw-r--r--ext/hash/hash_tiger.c30
-rw-r--r--ext/hash/hash_whirlpool.c32
-rw-r--r--ext/hash/php_hash.h10
-rw-r--r--ext/hash/php_hash_adler32.h2
-rw-r--r--ext/hash/php_hash_crc32.h2
-rw-r--r--ext/hash/php_hash_crc32_tables.h4
-rw-r--r--ext/hash/php_hash_fnv.h16
-rw-r--r--ext/hash/php_hash_gost.h6
-rw-r--r--ext/hash/php_hash_gost_tables.h4
-rw-r--r--ext/hash/php_hash_haval.h6
-rw-r--r--ext/hash/php_hash_joaat.h4
-rw-r--r--ext/hash/php_hash_md.h8
-rw-r--r--ext/hash/php_hash_ripemd.h16
-rw-r--r--ext/hash/php_hash_sha.h28
-rw-r--r--ext/hash/php_hash_sha3.h59
-rw-r--r--ext/hash/php_hash_snefru.h4
-rw-r--r--ext/hash/php_hash_snefru_tables.h2
-rw-r--r--ext/hash/php_hash_tiger.h4
-rw-r--r--ext/hash/php_hash_tiger_tables.h2
-rw-r--r--ext/hash/php_hash_whirlpool.h2
-rw-r--r--ext/hash/php_hash_whirlpool_tables.h18
-rw-r--r--ext/hash/tests/hash_algos.phpt14
-rw-r--r--ext/hash/tests/hash_copy_001.phpt36
-rw-r--r--ext/hash/tests/sha3.phpt56
-rw-r--r--ext/hash/tests/sha512-224.phpt13
-rw-r--r--ext/hash/tests/sha512-256.phpt13
-rw-r--r--ext/iconv/iconv.c15
-rw-r--r--ext/iconv/tests/iconv_strpos.phpt5
-rw-r--r--ext/iconv/tests/iconv_strpos_variation3.phpt135
-rw-r--r--ext/iconv/tests/iconv_strpos_variation3_64bit.phpt133
-rw-r--r--ext/iconv/tests/iconv_strpos_variation5.phpt17
-rw-r--r--ext/interbase/tests/interbase.inc2
-rw-r--r--ext/intl/ERROR.CONVENTIONS2
-rw-r--r--ext/intl/calendar/calendar_class.cpp2
-rw-r--r--ext/intl/collator/collator_convert.c8
-rw-r--r--ext/intl/collator/collator_is_numeric.c15
-rw-r--r--ext/intl/dateformat/dateformat_format_object.cpp14
-rw-r--r--ext/intl/grapheme/grapheme_string.c30
-rw-r--r--ext/intl/msgformat/msgformat_class.h9
-rw-r--r--ext/intl/tests/bug69374.phpt24
-rw-r--r--ext/intl/tests/bug69398.phpt22
-rw-r--r--ext/intl/tests/grapheme.phpt24
-rw-r--r--ext/intl/tests/timezone_IDforWindowsID_basic.phpt46
-rw-r--r--ext/intl/tests/timezone_getCanonicalID_error.phpt6
-rw-r--r--ext/intl/tests/timezone_windowsID_basic.phpt43
-rw-r--r--ext/intl/timezone/timezone_class.cpp15
-rw-r--r--ext/intl/timezone/timezone_methods.cpp78
-rw-r--r--ext/intl/timezone/timezone_methods.h5
-rw-r--r--ext/intl/transliterator/transliterator_class.c8
-rw-r--r--ext/json/json.c1
-rw-r--r--ext/json/json_encoder.c11
-rw-r--r--ext/json/json_parser.tab.c165
-rw-r--r--ext/json/json_parser.y163
-rw-r--r--ext/json/php_json.h1
-rw-r--r--ext/json/php_json_parser.h58
-rw-r--r--ext/json/tests/json_encode_u2028_u2029.phpt36
-rw-r--r--ext/libxml/tests/libxml_set_external_entity_loader_error1.phpt10
-rw-r--r--ext/mbstring/mbstring.c38
-rw-r--r--ext/mbstring/php_mbregex.c15
-rw-r--r--ext/mbstring/tests/bug45923.phpt234
-rw-r--r--ext/mbstring/tests/bug72399.phpt10
-rw-r--r--ext/mbstring/tests/mb_ereg_search_setpos.phpt70
-rw-r--r--ext/mbstring/tests/mb_strimwidth.phpt36
-rw-r--r--ext/mbstring/tests/mb_stripos.phpt122
-rw-r--r--ext/mbstring/tests/mb_stripos_variation3.phpt67
-rw-r--r--ext/mbstring/tests/mb_stripos_variation5_Bug45923.phpt22
-rw-r--r--ext/mbstring/tests/mb_strpos.phpt97
-rw-r--r--ext/mbstring/tests/mb_strpos_variation3.phpt62
-rw-r--r--ext/mbstring/tests/mb_strpos_variation5.phpt16
-rw-r--r--ext/mcrypt/tests/mcrypt_module_get_algo_block_size.phpt5
-rw-r--r--ext/mcrypt/tests/mcrypt_module_get_algo_key_size.phpt5
-rw-r--r--ext/mysqli/mysqli.c13
-rw-r--r--ext/mysqli/mysqli_api.c14
-rw-r--r--ext/mysqli/mysqli_fe.c4
-rw-r--r--ext/mysqli/mysqli_priv.h4
-rw-r--r--ext/mysqli/mysqli_prop.c8
-rw-r--r--ext/mysqli/php_mysqli_structs.h4
-rw-r--r--ext/mysqli/tests/mysqli_fetch_object.phpt37
-rw-r--r--ext/mysqli/tests/mysqli_fetch_object_oo.phpt15
-rw-r--r--ext/mysqli/tests/mysqli_get_client_stats.phpt8
-rw-r--r--ext/mysqlnd/config.w327
-rw-r--r--ext/mysqlnd/config9.m48
-rw-r--r--ext/mysqlnd/mysqlnd.h108
-rw-r--r--ext/mysqlnd/mysqlnd_alloc.c216
-rw-r--r--ext/mysqlnd/mysqlnd_alloc.h39
-rw-r--r--ext/mysqlnd/mysqlnd_auth.c348
-rw-r--r--ext/mysqlnd/mysqlnd_auth.h126
-rw-r--r--ext/mysqlnd/mysqlnd_block_alloc.c25
-rw-r--r--ext/mysqlnd/mysqlnd_block_alloc.h1
-rw-r--r--ext/mysqlnd/mysqlnd_charset.c1
-rw-r--r--ext/mysqlnd/mysqlnd_commands.c1470
-rw-r--r--ext/mysqlnd/mysqlnd_commands.h34
-rw-r--r--ext/mysqlnd/mysqlnd_connection.c (renamed from ext/mysqlnd/mysqlnd.c)2113
-rw-r--r--ext/mysqlnd/mysqlnd_connection.h87
-rw-r--r--ext/mysqlnd/mysqlnd_debug.c1
-rw-r--r--ext/mysqlnd/mysqlnd_debug.h12
-rw-r--r--ext/mysqlnd/mysqlnd_driver.c180
-rw-r--r--ext/mysqlnd/mysqlnd_enum_n_def.h43
-rw-r--r--ext/mysqlnd/mysqlnd_ext_plugin.c331
-rw-r--r--ext/mysqlnd/mysqlnd_ext_plugin.h195
-rw-r--r--ext/mysqlnd/mysqlnd_libmysql_compat.h4
-rw-r--r--ext/mysqlnd/mysqlnd_loaddata.c33
-rw-r--r--ext/mysqlnd/mysqlnd_net.c17
-rw-r--r--ext/mysqlnd/mysqlnd_plugin.c1
-rw-r--r--ext/mysqlnd/mysqlnd_plugin.h41
-rw-r--r--ext/mysqlnd/mysqlnd_priv.h205
-rw-r--r--ext/mysqlnd/mysqlnd_protocol_frame_codec.c508
-rw-r--r--ext/mysqlnd/mysqlnd_protocol_frame_codec.h35
-rw-r--r--ext/mysqlnd/mysqlnd_ps.c777
-rw-r--r--ext/mysqlnd/mysqlnd_ps.h54
-rw-r--r--ext/mysqlnd/mysqlnd_ps_codec.c69
-rw-r--r--ext/mysqlnd/mysqlnd_read_buffer.c96
-rw-r--r--ext/mysqlnd/mysqlnd_read_buffer.h25
-rw-r--r--ext/mysqlnd/mysqlnd_result.c449
-rw-r--r--ext/mysqlnd/mysqlnd_result.h9
-rw-r--r--ext/mysqlnd/mysqlnd_result_meta.c26
-rw-r--r--ext/mysqlnd/mysqlnd_result_meta.h2
-rw-r--r--ext/mysqlnd/mysqlnd_reverse_api.c2
-rw-r--r--ext/mysqlnd/mysqlnd_statistics.c47
-rw-r--r--ext/mysqlnd/mysqlnd_statistics.h49
-rw-r--r--ext/mysqlnd/mysqlnd_structs.h685
-rw-r--r--ext/mysqlnd/mysqlnd_vio.c805
-rw-r--r--ext/mysqlnd/mysqlnd_vio.h (renamed from ext/mysqlnd/mysqlnd_net.h)11
-rw-r--r--ext/mysqlnd/mysqlnd_wireprotocol.c974
-rw-r--r--ext/mysqlnd/mysqlnd_wireprotocol.h50
-rw-r--r--ext/mysqlnd/php_mysqlnd.c2
-rw-r--r--ext/mysqlnd/php_mysqlnd.h1
-rw-r--r--ext/oci8/oci8.c170
-rw-r--r--ext/oci8/oci8_interface.c51
-rw-r--r--ext/oci8/oci8_lob.c5
-rw-r--r--ext/opcache/Optimizer/block_pass.c2538
-rw-r--r--ext/opcache/Optimizer/compact_literals.c91
-rw-r--r--ext/opcache/Optimizer/dfa_pass.c650
-rw-r--r--ext/opcache/Optimizer/nop_removal.c70
-rw-r--r--ext/opcache/Optimizer/optimize_func_calls.c52
-rw-r--r--ext/opcache/Optimizer/optimize_temp_vars_5.c55
-rw-r--r--ext/opcache/Optimizer/pass1_5.c107
-rw-r--r--ext/opcache/Optimizer/pass2.c53
-rw-r--r--ext/opcache/Optimizer/pass3.c140
-rw-r--r--ext/opcache/Optimizer/zend_call_graph.c300
-rw-r--r--ext/opcache/Optimizer/zend_call_graph.h83
-rw-r--r--ext/opcache/Optimizer/zend_cfg.c844
-rw-r--r--ext/opcache/Optimizer/zend_cfg.h132
-rw-r--r--ext/opcache/Optimizer/zend_dfg.c248
-rw-r--r--ext/opcache/Optimizer/zend_dfg.h58
-rw-r--r--ext/opcache/Optimizer/zend_dump.c1175
-rw-r--r--ext/opcache/Optimizer/zend_dump.h51
-rw-r--r--ext/opcache/Optimizer/zend_func_info.c1288
-rw-r--r--ext/opcache/Optimizer/zend_func_info.h70
-rw-r--r--ext/opcache/Optimizer/zend_inference.c4085
-rw-r--r--ext/opcache/Optimizer/zend_inference.h263
-rw-r--r--ext/opcache/Optimizer/zend_optimizer.c602
-rw-r--r--ext/opcache/Optimizer/zend_optimizer.h47
-rw-r--r--ext/opcache/Optimizer/zend_optimizer_internal.h84
-rw-r--r--ext/opcache/Optimizer/zend_ssa.c1128
-rw-r--r--ext/opcache/Optimizer/zend_ssa.h168
-rw-r--r--ext/opcache/Optimizer/zend_worklist.h137
-rw-r--r--ext/opcache/ZendAccelerator.c87
-rw-r--r--ext/opcache/ZendAccelerator.h50
-rw-r--r--ext/opcache/config.m410
-rw-r--r--ext/opcache/config.w322
-rw-r--r--ext/opcache/tests/bug71127.phpt2
-rw-r--r--ext/opcache/tests/bug71843.phpt4
-rw-r--r--ext/opcache/tests/issue0140.phpt1
-rw-r--r--ext/opcache/tests/ssa_bug_001.phpt19
-rw-r--r--ext/opcache/tests/ssa_bug_002.phpt16
-rw-r--r--ext/opcache/tests/ssa_bug_003.phpt38
-rw-r--r--ext/opcache/tests/ssa_bug_004.phpt19
-rw-r--r--ext/opcache/tests/ssa_bug_005.phpt24
-rw-r--r--ext/opcache/zend_accelerator_module.c3
-rw-r--r--ext/opcache/zend_accelerator_util_funcs.c112
-rw-r--r--ext/opcache/zend_file_cache.c111
-rw-r--r--ext/opcache/zend_persist.c198
-rw-r--r--ext/opcache/zend_persist_calc.c37
-rw-r--r--ext/openssl/tests/001.phpt2
-rw-r--r--ext/openssl/tests/bug55259.phpt8
-rw-r--r--ext/openssl/tests/openssl_error_string_basic.phpt4
-rw-r--r--ext/openssl/tests/openssl_pkcs7_decrypt_basic.phpt4
-rw-r--r--ext/openssl/tests/openssl_pkcs7_encrypt_basic.phpt4
-rw-r--r--ext/openssl/tests/openssl_pkcs7_sign_basic.phpt2
-rw-r--r--ext/openssl/xp_ssl.c50
-rw-r--r--ext/pcre/php_pcre.c2
-rw-r--r--ext/pdo/pdo.c22
-rw-r--r--ext/pdo/pdo_dbh.c6
-rw-r--r--ext/pdo/pdo_stmt.c4
-rw-r--r--ext/pdo_dblib/pdo_dblib.c6
-rw-r--r--ext/pdo_firebird/tests/testdb.inc2
-rw-r--r--ext/pdo_mysql/mysql_driver.c7
-rw-r--r--ext/pdo_mysql/pdo_mysql.c2
-rw-r--r--ext/pdo_mysql/php_pdo_mysql_int.h6
-rw-r--r--ext/pdo_oci/pdo_oci.c2
-rw-r--r--ext/pdo_oci/tests/pdo_oci_stream_2a.phpt20
-rw-r--r--ext/pdo_odbc/pdo_odbc.c2
-rw-r--r--ext/pdo_odbc/php_pdo_odbc.h10
-rw-r--r--ext/pdo_pgsql/pdo_pgsql.c2
-rw-r--r--ext/pdo_sqlite/pdo_sqlite.c2
-rw-r--r--ext/pdo_sqlite/php_pdo_sqlite.h10
-rw-r--r--ext/pdo_sqlite/sqlite_driver.c4
-rw-r--r--ext/pgsql/pgsql.c144
-rw-r--r--ext/pgsql/php_pgsql.h5
-rw-r--r--ext/pgsql/tests/09notice.phpt55
-rw-r--r--ext/pgsql/tests/bug72195.phpt2
-rw-r--r--ext/phar/phar.c57
-rw-r--r--ext/phar/phar_internal.h45
-rw-r--r--ext/phar/phar_object.c121
-rw-r--r--ext/phar/tar.c24
-rw-r--r--ext/phar/util.c11
-rw-r--r--ext/phar/zip.c36
-rw-r--r--ext/readline/config.w3216
-rw-r--r--ext/readline/php_readline.h2
-rw-r--r--ext/readline/readline.c8
-rw-r--r--ext/readline/readline_cli.c33
-rw-r--r--ext/readline/tests/libedit_info_001-win32.phpt42
-rw-r--r--ext/readline/tests/libedit_info_001.phpt3
-rw-r--r--ext/readline/tests/libedit_write_history_001-win32.phpt29
-rw-r--r--ext/readline/tests/libedit_write_history_001.phpt3
-rw-r--r--ext/reflection/php_reflection.c432
-rw-r--r--ext/reflection/tests/007.phpt14
-rw-r--r--ext/reflection/tests/017.phpt4
-rw-r--r--ext/reflection/tests/ReflectionClassConstant_basic1.phpt194
-rw-r--r--ext/reflection/tests/ReflectionClassConstant_getValue.phpt47
-rw-r--r--ext/reflection/tests/ReflectionClass_isArray.phpt24
-rw-r--r--ext/reflection/tests/ReflectionClass_newInstanceArgs_001.phpt40
-rw-r--r--ext/reflection/tests/ReflectionClass_newInstance_001.phpt25
-rw-r--r--ext/reflection/tests/ReflectionClass_toString_001.phpt21
-rw-r--r--ext/reflection/tests/ReflectionExtension_getClasses_basic.phpt11
-rw-r--r--ext/reflection/tests/ReflectionMethod_invokeArgs_error1.phpt15
-rw-r--r--ext/reflection/tests/ReflectionMethod_invokeArgs_error3.phpt10
-rw-r--r--ext/reflection/tests/ReflectionMethod_invoke_basic.phpt14
-rw-r--r--ext/reflection/tests/ReflectionMethod_invoke_error2.phpt15
-rw-r--r--ext/reflection/tests/ReflectionProperty_getValue_error.phpt15
-rw-r--r--ext/reflection/tests/ReflectionType_possible_types.phpt31
-rw-r--r--ext/reflection/tests/bug29986.phpt10
-rw-r--r--ext/reflection/tests/bug38217.phpt14
-rw-r--r--ext/reflection/tests/bug45765.phpt2
-rw-r--r--ext/session/mod_mm.c22
-rw-r--r--ext/session/mod_user.c11
-rw-r--r--ext/session/mod_user_class.c27
-rw-r--r--ext/session/session.c86
-rw-r--r--ext/session/tests/016.phpt2
-rw-r--r--ext/session/tests/bug55688.phpt2
-rw-r--r--ext/session/tests/bug60634.phpt9
-rw-r--r--ext/session/tests/bug60634_error_1.phpt6
-rw-r--r--ext/session/tests/bug61728.phpt30
-rw-r--r--ext/session/tests/bug67972.phpt3
-rw-r--r--ext/session/tests/bug68063.phpt14
-rw-r--r--ext/session/tests/bug70133.phpt41
-rw-r--r--ext/session/tests/bug71162.phpt79
-rw-r--r--ext/session/tests/bug71186.phpt32
-rw-r--r--ext/session/tests/rfc1867_sid_invalid.phpt4
-rw-r--r--ext/session/tests/session_save_path_variation2.phpt8
-rw-r--r--ext/session/tests/session_save_path_variation3.phpt8
-rw-r--r--ext/session/tests/session_set_save_handler_class_002.phpt2
-rw-r--r--ext/session/tests/session_set_save_handler_class_005.phpt13
-rw-r--r--ext/session/tests/session_set_save_handler_class_012.phpt13
-rw-r--r--ext/session/tests/session_set_save_handler_class_016.phpt6
-rw-r--r--ext/session/tests/session_set_save_handler_class_017.phpt2
-rw-r--r--ext/session/tests/session_set_save_handler_error4.phpt5
-rw-r--r--ext/session/tests/session_set_save_handler_iface_001.phpt2
-rw-r--r--ext/session/tests/session_set_save_handler_iface_002.phpt2
-rw-r--r--ext/session/tests/sessionhandler_open_001.phpt7
-rw-r--r--ext/shmop/php_shmop.h2
-rw-r--r--ext/simplexml/php_simplexml.h3
-rw-r--r--ext/snmp/snmp.c11
-rw-r--r--ext/soap/php_encoding.c21
-rw-r--r--ext/soap/php_packet_soap.c2
-rw-r--r--ext/soap/soap.c2
-rw-r--r--ext/soap/tests/bugs/bug38005.phpt2
-rw-r--r--ext/soap/tests/soap12/soap12-test.inc2
-rw-r--r--ext/spl/spl_directory.c4
-rw-r--r--ext/spl/spl_engine.h4
-rw-r--r--ext/spl/spl_observer.c91
-rw-r--r--ext/sqlite3/libsqlite/sqlite3.c66128
-rw-r--r--ext/sqlite3/libsqlite/sqlite3.h684
-rw-r--r--ext/sqlite3/libsqlite/sqlite3ext.h23
-rw-r--r--ext/sqlite3/sqlite3.c23
-rw-r--r--ext/standard/array.c63
-rw-r--r--ext/standard/assert.c7
-rw-r--r--ext/standard/basic_functions.c69
-rw-r--r--ext/standard/basic_functions.h20
-rw-r--r--ext/standard/config.m495
-rw-r--r--ext/standard/crc32.c4
-rw-r--r--ext/standard/crypt.c63
-rw-r--r--ext/standard/file.c12
-rw-r--r--ext/standard/ftok.c4
-rw-r--r--ext/standard/incomplete_class.c3
-rw-r--r--ext/standard/info.c4
-rw-r--r--ext/standard/lcg.c4
-rw-r--r--ext/standard/math.c14
-rw-r--r--ext/standard/md5.c20
-rw-r--r--ext/standard/md5.h6
-rw-r--r--ext/standard/pack.c13
-rw-r--r--ext/standard/password.c2
-rw-r--r--ext/standard/php_crypt.h2
-rw-r--r--ext/standard/php_crypt_r.c2
-rw-r--r--ext/standard/php_lcg.h4
-rw-r--r--ext/standard/php_rand.h4
-rw-r--r--ext/standard/php_smart_string.h4
-rw-r--r--ext/standard/proc_open.c55
-rw-r--r--ext/standard/rand.c18
-rw-r--r--ext/standard/sha1.c38
-rw-r--r--ext/standard/sha1.h4
-rw-r--r--ext/standard/string.c45
-rw-r--r--ext/standard/tests/array/array_filter_variation10.phpt22
-rw-r--r--ext/standard/tests/array/array_map_error.phpt27
-rw-r--r--ext/standard/tests/array/array_map_variation10.phpt42
-rw-r--r--ext/standard/tests/array/array_map_variation9.phpt45
-rw-r--r--ext/standard/tests/array/array_reduce_variation1.phpt14
-rw-r--r--ext/standard/tests/array/array_udiff_assoc_variation5.phpt14
-rw-r--r--ext/standard/tests/array/array_udiff_uassoc_variation6.phpt13
-rw-r--r--ext/standard/tests/array/array_udiff_variation5.phpt11
-rw-r--r--ext/standard/tests/array/array_uintersect_assoc_variation5.phpt11
-rw-r--r--ext/standard/tests/array/array_uintersect_uassoc_variation6.phpt15
-rw-r--r--ext/standard/tests/array/array_uintersect_variation5.phpt15
-rw-r--r--ext/standard/tests/array/array_walk_error2.phpt50
-rw-r--r--ext/standard/tests/array/array_walk_recursive_error2.phpt50
-rw-r--r--ext/standard/tests/assert/assert_error2.phpt9
-rw-r--r--ext/standard/tests/file/bug38450.phpt3
-rw-r--r--ext/standard/tests/file/bug38450_1.phpt3
-rw-r--r--ext/standard/tests/file/bug38450_2.phpt3
-rw-r--r--ext/standard/tests/file/bug38450_3.phpt2
-rw-r--r--ext/standard/tests/file/bug39673.phpt6
-rw-r--r--ext/standard/tests/file/bug47517.phpt17
-rw-r--r--ext/standard/tests/file/bug52624.phpt2
-rw-r--r--ext/standard/tests/file/file_get_contents_variation5_32bit.phpt236
-rw-r--r--ext/standard/tests/file/file_get_contents_variation5_64bit.phpt (renamed from ext/standard/tests/file/file_get_contents_variation5.phpt)19
-rw-r--r--ext/standard/tests/file/file_put_contents_variation7-win32.phpt6
-rw-r--r--ext/standard/tests/file/file_put_contents_variation7.phpt6
-rw-r--r--ext/standard/tests/file/tempnam_variation2-win32.phpt10
-rw-r--r--ext/standard/tests/file/tempnam_variation2.phpt10
-rw-r--r--ext/standard/tests/file/tempnam_variation3-win32.phpt2
-rw-r--r--ext/standard/tests/file/tempnam_variation4-0.phpt638
-rw-r--r--ext/standard/tests/file/tempnam_variation4-1.phpt638
-rw-r--r--ext/standard/tests/file/tempnam_variation4.phpt1092
-rw-r--r--ext/standard/tests/file/tempnam_variation7-win32.phpt10
-rw-r--r--ext/standard/tests/file/tempnam_variation7.phpt10
-rw-r--r--ext/standard/tests/file/tempnam_variation8-win32.phpt6
-rw-r--r--ext/standard/tests/general_functions/bug72306.phpt23
-rw-r--r--ext/standard/tests/general_functions/floatval.phpt4
-rw-r--r--ext/standard/tests/general_functions/floatval_variation1.phpt7
-rw-r--r--ext/standard/tests/general_functions/getenv.phpt16
-rw-r--r--ext/standard/tests/general_functions/gettype_settype_variation2.phpt16
-rw-r--r--ext/standard/tests/http/http_response_header_01.phpt38
-rw-r--r--ext/standard/tests/http/http_response_header_02.phpt44
-rw-r--r--ext/standard/tests/http/http_response_header_03.phpt45
-rw-r--r--ext/standard/tests/math/decbin_basic.phpt2
-rw-r--r--ext/standard/tests/math/dechex_basic.phpt2
-rw-r--r--ext/standard/tests/math/decoct_basic.phpt2
-rw-r--r--ext/standard/tests/math/pow_variation1.phpt10
-rw-r--r--ext/standard/tests/math/pow_variation1_64bit.phpt10
-rw-r--r--ext/standard/tests/math/pow_variation2.phpt10
-rw-r--r--ext/standard/tests/network/ip.phpt12
-rw-r--r--ext/standard/tests/network/ip_x86_64.phpt12
-rw-r--r--ext/standard/tests/network/long2ip_variation1.phpt33
-rw-r--r--ext/standard/tests/streams/bug60602.phpt2
-rw-r--r--ext/standard/tests/streams/stream_context_tcp_nodelay.phpt22
-rw-r--r--ext/standard/tests/streams/stream_context_tcp_nodelay_fopen.phpt21
-rw-r--r--ext/standard/tests/streams/stream_context_tcp_nodelay_server.phpt47
-rw-r--r--ext/standard/tests/strings/bug40754.phpt6
-rw-r--r--ext/standard/tests/strings/bug53021.phpt6
-rw-r--r--ext/standard/tests/strings/bug55871.phpt2
-rw-r--r--ext/standard/tests/strings/html_entity_decode3.phpt45
-rw-r--r--ext/standard/tests/strings/stripos_basic2.phpt20
-rw-r--r--ext/standard/tests/strings/stripos_error.phpt15
-rw-r--r--ext/standard/tests/strings/stripos_variation14.phpt6
-rw-r--r--ext/standard/tests/strings/strpos.phptbin9999 -> 10239 bytes
-rw-r--r--ext/standard/tests/strings/substr_count_basic.phpt8
-rw-r--r--ext/standard/tests/strings/substr_count_error.phpt40
-rw-r--r--ext/standard/tests/strings/unpack_error.phpt6
-rw-r--r--ext/standard/tests/strings/unpack_offset.phpt17
-rw-r--r--ext/standard/tests/strings/url_t.phpt34
-rw-r--r--ext/standard/tests/url/get_headers_error_001.phpt7
-rw-r--r--ext/standard/tests/url/get_headers_error_003.phpt31
-rw-r--r--ext/standard/tests/url/parse_url_basic_001.phpt40
-rw-r--r--ext/standard/tests/url/parse_url_basic_002.phpt5
-rw-r--r--ext/standard/tests/url/parse_url_basic_003.phpt5
-rw-r--r--ext/standard/tests/url/parse_url_basic_004.phpt5
-rw-r--r--ext/standard/tests/url/parse_url_basic_005.phpt5
-rw-r--r--ext/standard/tests/url/parse_url_basic_006.phpt5
-rw-r--r--ext/standard/tests/url/parse_url_basic_007.phpt5
-rw-r--r--ext/standard/tests/url/parse_url_basic_008.phpt5
-rw-r--r--ext/standard/tests/url/parse_url_basic_009.phpt5
-rw-r--r--ext/standard/tests/url/urls.inc5
-rw-r--r--ext/standard/type.c2
-rw-r--r--ext/standard/url.c23
-rw-r--r--ext/standard/var.c5
-rw-r--r--ext/sysvshm/php_sysvshm.h2
-rw-r--r--ext/wddx/wddx.c6
-rw-r--r--ext/xml/xml.c69
-rw-r--r--ext/xsl/php_xsl.h10
-rw-r--r--ext/xsl/xsltprocessor.c2
-rw-r--r--ext/zip/php_zip.c15
-rw-r--r--ext/zip/php_zip.h17
-rw-r--r--ext/zip/zip_stream.c2
-rw-r--r--ext/zlib/tests/gzfilegzreadfile.phpt2
-rw-r--r--ext/zlib/tests/gzreadgzwrite.phpt2
-rw-r--r--ext/zlib/tests/gzreadgzwriteplain.phpt2
-rw-r--r--main/fopen_wrappers.h11
-rw-r--r--main/main.c20
-rw-r--r--main/network.c23
-rw-r--r--main/php.h10
-rw-r--r--main/php_globals.h1
-rw-r--r--main/php_ini.c2
-rw-r--r--main/php_network.h5
-rw-r--r--main/php_open_temporary_file.c8
-rw-r--r--main/php_open_temporary_file.h5
-rw-r--r--main/php_variables.c19
-rw-r--r--main/php_version.h8
-rw-r--r--main/snprintf.h10
-rw-r--r--main/streams/userspace.c4
-rw-r--r--main/streams/xp_socket.c75
-rwxr-xr-xrun-tests.php40
-rw-r--r--sapi/apache2handler/php_apache.h6
-rw-r--r--sapi/cgi/cgi_main.c141
-rw-r--r--sapi/cli/config.w325
-rw-r--r--sapi/cli/php_cli.c8
-rw-r--r--sapi/cli/php_cli_server.c26
-rw-r--r--sapi/cli/tests/bug43177.phpt4
-rw-r--r--sapi/cli/tests/bug65066_100.phpt1
-rw-r--r--sapi/cli/tests/bug65066_422.phpt1
-rw-r--r--sapi/cli/tests/bug65066_511.phpt1
-rw-r--r--sapi/cli/tests/bug65633.phpt1
-rw-r--r--sapi/cli/tests/bug66606_2.phpt1
-rw-r--r--sapi/cli/tests/bug66830.phpt1
-rw-r--r--sapi/cli/tests/bug67429.phpt2
-rw-r--r--sapi/cli/tests/bug68745.phpt1
-rw-r--r--sapi/cli/tests/bug71005.phpt1
-rw-r--r--sapi/cli/tests/php_cli_server_004.phpt1
-rw-r--r--sapi/cli/tests/php_cli_server_005.phpt1
-rw-r--r--sapi/cli/tests/php_cli_server_006.phpt1
-rw-r--r--sapi/cli/tests/php_cli_server_007.phpt1
-rw-r--r--sapi/cli/tests/php_cli_server_008.phpt2
-rw-r--r--sapi/cli/tests/php_cli_server_009.phpt2
-rw-r--r--sapi/cli/tests/php_cli_server_010.phpt2
-rw-r--r--sapi/cli/tests/php_cli_server_012.phpt1
-rw-r--r--sapi/cli/tests/php_cli_server_013.phpt3
-rw-r--r--sapi/cli/tests/php_cli_server_014.phpt2
-rw-r--r--sapi/cli/tests/php_cli_server_015.phpt1
-rw-r--r--sapi/cli/tests/php_cli_server_017.phpt1
-rw-r--r--sapi/cli/tests/php_cli_server_018.phpt1
-rw-r--r--sapi/cli/tests/php_cli_server_019.phpt1
-rw-r--r--sapi/cli/tests/php_cli_server_020.phpt1
-rw-r--r--sapi/cli/tests/upload_2G.phpt1
-rw-r--r--sapi/fpm/fpm/fpm_conf.c2
-rw-r--r--sapi/fpm/fpm/fpm_events.c3
-rw-r--r--sapi/litespeed/lsapilib.c4
-rw-r--r--sapi/phpdbg/phpdbg_frame.c4
-rw-r--r--sapi/phpdbg/phpdbg_list.c5
-rw-r--r--sapi/phpdbg/phpdbg_opcode.c115
-rw-r--r--sapi/phpdbg/phpdbg_print.c6
-rw-r--r--sapi/phpdbg/phpdbg_prompt.c10
-rw-r--r--sapi/phpdbg/phpdbg_utils.c12
-rw-r--r--sapi/phpdbg/phpdbg_watch.c4
-rw-r--r--sapi/phpdbg/tests/breakpoints_001.phpt22
-rw-r--r--sapi/phpdbg/tests/breakpoints_002.phpt28
-rw-r--r--sapi/phpdbg/tests/breakpoints_003.phpt14
-rw-r--r--sapi/phpdbg/tests/breakpoints_004.phpt28
-rw-r--r--sapi/phpdbg/tests/exceptions_003.phpt2
-rw-r--r--sapi/phpdbg/tests/stepping_001.phpt2
-rwxr-xr-xserver-tests.php14
-rw-r--r--tests/classes/__call_007.phpt12
-rw-r--r--tests/classes/constants_comments_001.phpt34
-rw-r--r--tests/classes/constants_visibility_001.phpt23
-rw-r--r--tests/classes/constants_visibility_002.phpt30
-rw-r--r--tests/classes/constants_visibility_003.phpt30
-rw-r--r--tests/classes/constants_visibility_004.phpt28
-rw-r--r--tests/classes/constants_visibility_005.phpt10
-rw-r--r--tests/classes/constants_visibility_006.phpt11
-rw-r--r--tests/classes/constants_visibility_007.phpt10
-rw-r--r--tests/classes/constants_visibility_error_001.phpt16
-rw-r--r--tests/classes/constants_visibility_error_002.phpt16
-rw-r--r--tests/classes/constants_visibility_error_003.phpt16
-rw-r--r--tests/classes/constants_visibility_error_004.phpt16
-rw-r--r--tests/classes/interface_constant_inheritance_005.phpt12
-rw-r--r--tests/classes/interface_constant_inheritance_006.phpt10
-rw-r--r--tests/classes/interface_constant_inheritance_007.phpt10
-rw-r--r--tests/classes/interfaces_003.phpt2
-rw-r--r--tests/classes/static_this.phpt10
-rw-r--r--tests/lang/045.phpt3
-rw-r--r--tests/lang/bug22592.phpt5
-rw-r--r--tests/lang/bug28800.phpt14
-rw-r--r--tests/lang/bug71897.phpt16
-rw-r--r--tests/lang/operators/bitwiseShiftLeft_variationStr_64bit.phpt20
-rw-r--r--tests/lang/operators/bitwiseShiftRight_variationStr.phpt20
-rw-r--r--tests/lang/operators/modulus_variationStr.phpt28
-rw-r--r--tests/lang/operators/negate_variationStr.phpt18
-rw-r--r--tests/output/ob_010.phpt4
-rw-r--r--win32/build/buildconf.js2
-rw-r--r--win32/build/config.w323
-rw-r--r--win32/build/config.w32.h.in10
-rw-r--r--win32/build/confutils.js13
-rw-r--r--win32/build/default.manifest9
-rw-r--r--win32/ftok.c52
-rw-r--r--win32/ipc.h29
796 files changed, 120828 insertions, 44045 deletions
diff --git a/CODING_STANDARDS b/CODING_STANDARDS
index a3b9d2b7d3..5cf70c92b5 100644
--- a/CODING_STANDARDS
+++ b/CODING_STANDARDS
@@ -165,7 +165,7 @@ User Functions/Methods Naming Conventions
'foobar'
'foo_bar'
-Internal Function Naming Convensions
+Internal Function Naming Conventions
----------------------
1. Functions that are part of the external API should be named
diff --git a/NEWS b/NEWS
index fdb232695f..bc450dad0e 100644
--- a/NEWS
+++ b/NEWS
@@ -1,1397 +1,148 @@
PHP NEWS
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
-?? ??? 2016 PHP 7.0.9
-
-
-
-23 Jun 2016 PHP 7.0.8
-
-- Core:
- . Fixed bug #72221 (segfault, past-the-end access). (Lauri Kenttä)
- . Fixed bug #72218 (If host name cannot be resolved then PHP 7 crashes).
- (Esminis at esminis dot lt)
-
-- FPM:
- . Fixed bug #72308 (fastcgi_finish_request and logging environment
- variables). (Laruence)
-
-- GD:
- . Fixed bug #72337 (invalid dimensions can lead to crash) (Pierre)
-
-- Intl:
- . Fixed bug #64524 (Add intl.use_exceptions to php.ini-*). (Anatol)
-
-- PCRE:
- . Fixed bug #72143 (preg_replace uses int instead of size_t). (Joe)
-
-- PDO_pgsql:
- . Fixed bug #71573 (Segfault (core dumped) if paramno beyond bound).
- (Laruence)
- . Fixed bug #72294 (Segmentation fault/invalid pointer in connection
- with pgsql_stmt_dtor). (Anatol)
-
-- Phpdbg:
- . Fixed bug #72284 (phpdbg fatal errors with coverage). (Bob)
-
-- Postgres:
- . Fixed bug #72195 (pg_pconnect/pg_connect cause use-after-free). (Laruence)
- . Fixed bug #72197 (pg_lo_create arbitrary read). (Anatol)
-
-- Standard:
- . Fixed bug #72369 (array_merge() produces references in PHP7). (Dmitry)
- . Fixed bug #72300 (ignore_user_abort(false) has no effect). (Laruence)
- . Fixed bug #72229 (Wrong reference when serialize/unserialize an object).
- (Laruence)
- . Fixed bug #72193 (dns_get_record returns array containing elements of
- type 'unknown'). (Laruence)
- . Fixed bug #72017 (range() with float step produces unexpected result).
- (Thomas Punt)
-
-- XML:
- . Fixed bug #72206 (xml_parser_create/xml_parser_free leaks mem). (Joe)
-
-- XMLRPC:
- . Fixed bug #72155 (use-after-free caused by get_zval_xmlrpc_type).
- (Joe, Laruence)
-
-- Zip:
- . Fixed ug #72258 (ZipArchive converts filenames to unrecoverable form).
- (Anatol)
-
-26 May 2016 PHP 7.0.7
+?? ??? 2016, PHP 7.1.0alpha2
- Core:
- . Fixed bug #72162 (use-after-free - error_reporting). (Laruence)
- . Add compiler option to disable special case function calls. (Joe)
- . Fixed bug #72101 (crash on complex code). (Dmitry)
- . Fixed bug #72100 (implode() inserts garbage into resulting string when
- joins very big integer). (Mikhail Galanin)
- . Fixed bug #72057 (PHP Hangs when using custom error handler and typehint).
- (Nikita Nefedov)
- . Fixed bug #72038 (Function calls with values to a by-ref parameter don't
- always throw a notice). (Bob)
- . Fixed bug #71737 (Memory leak in closure with parameter named $this).
- (Nikita)
- . Fixed bug #72059 (?? is not allowed on constant expressions). (Bob, Marcio)
- . Fixed bug #72159 (Imported Class Overrides Local Class Name). (Nikita)
-
-- Curl:
- . Fixed bug #68658 (Define CURLE_SSL_CACERT_BADFILE). (Pierrick)
-
-- DBA:
- . Fixed bug #72157 (use-after-free caused by dba_open). (Shm, Laruence)
+ . Implemented RFC: Replace "Missing argument" warning with "Too few
+ arguments" exception. (Dmitry)
+ . Implemented RFC: Fix inconsistent behavior of $this variable. (Dmitry)
+ . Fixed bug #72441 (Segmentation fault: RFC list_keys). (Laruence)
+ . Fixed bug #72395 (list() regression). (Laruence)
+ . Fixed bug #72373 (TypeError after Generator function w/declared return type
+ finishes). (Nikita)
+ . Fixed bug #69489 (tempnam() should raise notice if falling back to temp dir).
+ (Laruence, Anatol)
- GD:
- . Fixed bug #72227 (imagescale out-of-bounds read). (Stas)
-
-- Intl:
- . Fixed #72241 (get_icu_value_internal out-of-bounds read). (Stas)
-
-- JSON:
- . Fixed bug #72069 (Behavior \JsonSerializable different from json_encode).
- (Laruence)
+ . Fixed bug #43475 (Thick styled lines have scrambled patterns). (cmb)
+ . Fixed bug #53640 (XBM images require width to be multiple of 8). (cmb)
- Mbstring:
- . Fixed bug #72164 (Null Pointer Dereference - mb_ereg_replace). (Laruence)
-
-- OCI8:
- . Fixed bug #71600 (oci_fetch_all segfaults when selecting more than eight
- columns). (Tian Yang)
-
-- Opcache:
- . Fixed bug #72014 (Including a file with anonymous classes multiple times
- leads to fatal error). (Laruence)
-
-- OpenSSL:
- . Fixed bug #72165 (Null pointer dereference - openssl_csr_new). (Anatol)
-
-- PCNTL:
- . Fixed bug #72154 (pcntl_wait/pcntl_waitpid array internal structure
- overwrite). (Laruence)
-
-- POSIX:
- . Fixed bug #72133 (php_posix_group_to_array crashes if gr_passwd is NULL).
- (esminis at esminis dot lt)
-
-- Postgres:
- . Fixed bug #72028 (pg_query_params(): NULL converts to empty string).
- (Laruence)
- . Fixed bug #71062 (pg_convert() doesn't accept ISO 8601 for datatype
- timestamp). (denver at timothy dot io)
- . Fixed bug #72151 (mysqli_fetch_object changed behaviour). (Anatol)
-
-- Reflection:
- . Fixed bug #72174 (ReflectionProperty#getValue() causes __isset call).
- (Nikita)
-
-- Session:
- . Fixed bug #71972 (Cyclic references causing session_start(): Failed to
- decode session object). (Laruence)
-
-- Sockets:
- . Added socket_export_stream() function for getting a stream compatible
- resource from a socket resource. (Chris Wright, Bob)
-
-- SPL:
- . Fixed bug #72051 (The reference in CallbackFilterIterator doesn't work as
- expected). (Laruence)
-
-- SQLite3:
- . Fixed bug #68849 (bindValue is not using the right data type). (Anatol)
-
-- Standard:
- . Fixed bug #72075 (Referencing socket resources breaks stream_select).
- (Laruence)
- . Fixed bug #72031 (array_column() against an array of objects discards all
- values matching null). (Nikita)
-
-28 Apr 2016 PHP 7.0.6
-
-- Core:
- . Fixed bug #71930 (_zval_dtor_func: Assertion `(arr)->gc.refcount <= 1'
- failed). (Laruence)
- . Fixed bug #71922 (Crash on assert(new class{})). (Nikita)
- . Fixed bug #71914 (Reference is lost in "switch"). (Laruence)
- . Fixed bug #71871 (Interfaces allow final and abstract functions). (Nikita)
- . Fixed Bug #71859 (zend_objects_store_call_destructors operates on realloced
- memory, crashing). (Laruence)
- . Fixed bug #71841 (EG(error_zval) is not handled well). (Laruence)
- . Fixed bug #71750 (Multiple Heap Overflows in php_raw_url_encode/
- php_url_encode). (Stas)
- . Fixed bug #71731 (Null coalescing operator and ArrayAccess). (Nikita)
- . Fixed bug #71609 (Segmentation fault on ZTS with gethostbyname). (krakjoe)
- . Fixed bug #71414 (Inheritance, traits and interfaces). (krakjoe)
- . Fixed bug #71359 (Null coalescing operator and magic). (krakjoe)
- . Fixed bug #71334 (Cannot access array keys while uksort()). (Nikita)
- . Fixed bug #69659 (ArrayAccess, isset() and the offsetExists method).
- (Nikita)
- . Fixed bug #69537 (__debugInfo with empty string for key gives error).
- (krakjoe)
- . Fixed bug #62059 (ArrayObject and isset are not friends). (Nikita)
- . Fixed bug #71980 (Decorated/Nested Generator is Uncloseable in Finally).
- (Nikita)
-
-- BCmath:
- . Fixed bug #72093 (bcpowmod accepts negative scale and corrupts
- _one_ definition). (Stas)
-
-- Curl:
- . Fixed bug #71831 (CURLOPT_NOPROXY applied as long instead of string).
- (Michael Sierks)
-
-- Date:
- . Fixed bug #71889 (DateInterval::format Segmentation fault). (Thomas Punt)
-
-- EXIF:
- . Fixed bug #72094 (Out of bounds heap read access in exif header processing). (Stas)
-
-- GD:
- . Fixed bug #71912 (libgd: signedness vulnerability). (CVE-2016-3074) (Stas)
-
-- Intl:
- . Fixed bug #71516 (IntlDateFormatter looses locale if pattern is set via
- constructor). (Anatol)
- . Fixed bug #70455 (Missing constant: IntlChar::NO_NUMERIC_VALUE). (Anatol)
- . Fixed bug #70451, #70452 (Inconsistencies in return values of IntlChar
- methods). (Daniel Persson)
- . Fixed bug #68893 (Stackoverflow in datefmt_create). (Anatol)
- . Fixed bug #66289 (Locale::lookup incorrectly returns en or en_US if locale
- is empty). (Anatol)
- . Fixed bug #70484 (selectordinal doesn't work with named parameters).
- (Anatol)
- . Fixed bug #72061 (Out-of-bounds reads in zif_grapheme_stripos with negative
- offset). (Stas)
-
-- ODBC:
- . Fixed bug #63171 (Script hangs after max_execution_time). (Remi)
+ . Fixed bug #72405 (mb_ereg_replace - mbc_to_code (oniguruma) -
+ oob read access). (Laruence)
+ . Fixed bug #72399 (Use-After-Free in MBString (search_re)). (Laruence)
-- Opcache:
- . Fixed bug #71843 (null ptr deref ZEND_RETURN_SPEC_CONST_HANDLER).
- (Laruence)
-
-- PDO:
- . Fixed bug #52098 (Own PDOStatement implementation ignore __call()).
- (Daniel kalaspuffar, Julien)
- . Fixed bug #71447 (Quotes inside comments not properly handled). (Matteo)
-
-- PDO_DBlib:
- . Fixed bug #71943 (dblib_handle_quoter needs to allocate an extra byte).
- (Adam Baratz)
- . Add DBLIB-specific attributes for controlling timeouts. (Adam Baratz)
-
-- PDO_pgsql:
- . Fixed bug #62498 (pdo_pgsql inefficient when getColumnMeta() is used).
- (Joseph Bylund)
-
-- Postgres:
- . Fixed bug #71820 (pg_fetch_object binds parameters before call
- constructor). (Anatol)
- . Fixed bug #71998 (Function pg_insert does not insert when column
- type = inet). (Anatol)
-
-- SOAP:
- . Fixed bug #71986 (Nested foreach assign-by-reference creates broken
- variables). (Laruence)
-
-- SPL:
- . Fixed bug #71838 (Deserializing serialized SPLObjectStorage-Object can't
- access properties in PHP). (Nikita)
- . Fixed bug #71735 (Double-free in SplDoublyLinkedList::offsetSet). (Stas)
- . Fixed bug #67582 (Cloned SplObjectStorage with overwritten getHash fails
- offsetExists()). (Nikita)
- . Fixed bug #52339 (SPL autoloader breaks class_exists()). (Nikita)
+- Sqlite3:
+ . Implemented FR #72385 (Update SQLite bundle lib(3.13.0)). (Laruence)
- Standard:
- . Fixed bug #71995 (Returning the same var twice from __sleep() produces
- broken serialized data). (Laruence)
- . Fixed bug #71940 (Unserialize crushes on restore object reference).
+ . Fixed bug #72306 (Heap overflow through proc_open and $env parameter).
(Laruence)
- . Fixed bug #71969 (str_replace returns an incorrect resulting array after
- a foreach by reference). (Laruence)
- . Fixed bug #71891 (header_register_callback() and
- register_shutdown_function()). (Laruence)
- . Fixed bug #71884 (Null pointer deref (segfault) in
- stream_context_get_default). (Laruence)
- . Fixed bug #71840 (Unserialize accepts wrongly data). (Ryat, Laruence)
- . Fixed bug #71837 (Wrong arrays behaviour). (Laruence)
- . Fixed bug #71827 (substr_replace bug, string length). (krakjoe)
- . Fixed bug #67512 (php_crypt() crashes if crypt_r() does not exist or
- _REENTRANT is not defined). (Nikita)
- . Fixed bug #72116 (array_fill optimization breaks implementation). (Bob)
-
-- XML:
- . Fixed bug #72099 (xml_parse_into_struct segmentation fault). (Stas)
-- Zip:
- . Fixed bug #71923 (integer overflow in ZipArchive::getFrom*).
- (CVE-2016-3078) (Stas)
+- Streams:
+ . Fixed bug #72439 (Stream socket with remote address leads to a segmentation
+ fault). (Laruence)
-31 Mar 2016 PHP 7.0.5
+09 Jun 2016, PHP 7.1.0alpha1
- Core:
- . Huge pages disabled by default. (Rasmus)
- . Added ability to enable huge pages in Zend Memory Manager through
- the environment variable USE_ZEND_ALLOC_HUGE_PAGES=1. (Dmitry)
- . Fixed bug #71756 (Call-by-reference widens scope to uninvolved functions
- when used in switch). (Laruence)
- . Fixed bug #71729 (Possible crash in zend_bin_strtod, zend_oct_strtod,
- zend_hex_strtod). (Laruence)
- . Fixed bug #71695 (Global variables are reserved before execution).
- (Laruence)
- . Fixed bug #71629 (Out-of-bounds access in php_url_decode in context
- php_stream_url_wrap_rfc2397). (mt at debian dot org)
- . Fixed bug #71622 (Strings used in pass-as-reference cannot be used to
- invoke C::$callable()). (Bob)
- . Fixed bug #71596 (Segmentation fault on ZTS with date function
- (setlocale)). (Anatol)
- . Fixed bug #71535 (Integer overflow in zend_mm_alloc_heap()). (Dmitry)
- . Fixed bug #71470 (Leaked 1 hashtable iterators). (Nikita)
- . Fixed bug #71575 (ISO C does not allow extra ‘;’ outside of a function).
- (asgrim)
- . Fixed bug #71724 (yield from does not count EOLs). (Nikita)
- . Fixed bug #71767 (ReflectionMethod::getDocComment returns the wrong
- comment). (Grigorii Sokolik)
- . Fixed bug #71806 (php_strip_whitespace() fails on some numerical values).
- (Nikita)
- . Fixed bug #71624 (`php -R` (PHP_MODE_PROCESS_STDIN) is broken).
- (Sean DuBois)
-
-- CLI Server:
- . Fixed bug #69953 (Support MKCALENDAR request method). (Christoph)
-
-- Curl:
- . Fixed bug #71694 (Support constant CURLM_ADDED_ALREADY). (mpyw)
-
-- Date:
- . Fixed bug #71635 (DatePeriod::getEndDate segfault). (Thomas Punt)
-
-- Fileinfo:
- . Fixed bug #71527 (Buffer over-write in finfo_open with malformed magic
- file). (CVE-2015-8865) (Anatol)
-
-- libxml:
- . Fixed bug #71536 (Access Violation crashes php-cgi.exe). (Anatol)
-
-- mbstring:
- . Fixed bug #71906 (AddressSanitizer: negative-size-param (-1) in
- mbfl_strcut). (CVE-2016-4073) (Stas)
-
-- ODBC:
- . Fixed bug #47803, #69526 (Executing prepared statements is succesfull only
- for the first two statements). (einavitamar at gmail dot com, Anatol)
-
-- PCRE:
- . Fixed bug #71659 (segmentation fault in pcre running twig tests).
- (nish dot aravamudan at canonical dot com)
-
-- PDO_DBlib:
- . Fixed bug #54648 (PDO::MSSQL forces format of datetime fields).
- (steven dot lambeth at gmx dot de, Anatol)
-
-- Phar:
- . Fixed bug #71625 (Crash in php7.dll with bad phar filename). (Anatol)
- . Fixed bug #71317 (PharData fails to open specific file). (Jos Elstgeest)
- . Fixed bug #71860 (Invalid memory write in phar on filename with \0 in
- name). (CVE-2016-4072) (Stas)
-
-- phpdbg:
- . Fixed crash when advancing (except step) inside an internal function. (Bob)
-
-- Session:
- . Fixed bug #71683 (Null pointer dereference in zend_hash_str_find_bucket).
- (Yasuo)
-
-- SNMP:
- . Fixed bug #71704 (php_snmp_error() Format String Vulnerability).
- (CVE-2016-4071) (andrew at jmpesp dot org)
-
-- SPL:
- . Fixed bug #71617 (private properties lost when unserializing ArrayObject).
+ . Added nullable types. (Levi, Dmitry)
+ . Added DFA optimization framework based on e-SSA form. (Dmitry, Nikita)
+ . Added specialized opcode handlers (e.g. ZEND_ADD_LONG_NO_OVERFLOW).
+ (Dmitry)
+ . Change statement and fcall extension handlers to accept frame. (Joe)
+ . Implemented safe execution timeout handling, that prevents random crashes
+ after "Maximum execution time exceeded" error. (Dmitry)
+ . Fixed bug #53432 (Assignment via string index access on an empty string
+ converts to array). (Nikita)
+ . Fixed bug #62210 (Exceptions can leak temporary variables). (Dmitry, Bob)
+ . Fixed bug #62814 (It is possible to stiffen child class members visibility).
(Nikita)
-
-- Standard:
- . Fixed bug #71660 (array_column behaves incorrectly after foreach by
- reference). (Laruence)
- . Fixed bug #71798 (Integer Overflow in php_raw_url_encode). (CVE-2016-4070)
- (taoguangchen at icloud dot com, Stas)
-
-- Zip:
- . Update bundled libzip to 1.1.2. (Remi, Anatol)
-
-03 Mar 2016 PHP 7.0.4
-
-- Core:
- . Fixed bug (Low probability segfault in zend_arena). (Laruence)
- . Fixed bug #71441 (Typehinted Generator with return in try/finally crashes).
- (Bob)
- . Fixed bug #71442 (forward_static_call crash). (Laruence)
- . Fixed bug #71443 (Segfault using built-in webserver with intl using
- symfony). (Laruence)
- . Fixed bug #71449 (An integer overflow bug in php_implode()). (Stas)
- . Fixed bug #71450 (An integer overflow bug in php_str_to_str_ex()). (Stas)
- . Fixed bug #71474 (Crash because of VM stack corruption on Magento2).
+ . Fixed bug #69989 (Generators don't participate in cycle GC). (Nikita)
+ . Fixed bug #70228 (Memleak if return in finally block). (Dmitry)
+ . Fixed bug #71266 (Missing separation of properties HT in foreach etc).
(Dmitry)
- . Fixed bug #71485 (Return typehint on internal func causes Fatal error
- when it throws exception). (Laruence)
- . Fixed bug #71529 (Variable references on array elements don't work when
- using count). (Nikita)
- . Fixed bug #71601 (finally block not executed after yield from). (Bob)
- . Fixed bug #71637 (Multiple Heap Overflow due to integer overflows in
- xml/filter_url/addcslashes). (CVE-2016-4344, CVE-2016-4345, CVE-2016-4346)
- (Stas)
-
-- CLI server:
- . Fixed bug #71559 (Built-in HTTP server, we can download file in web by bug).
- (Johannes, Anatol)
-
-- CURL:
- . Fixed bug #71523 (Copied handle with new option CURLOPT_HTTPHEADER crashes
- while curl_multi_exec). (Laruence)
- . Fixed memory leak in curl_getinfo(). (Leigh)
-
-- Date:
- . Fixed bug #71525 (Calls to date_modify will mutate timelib_rel_time,
- causing date_date_set issues). (Sean DuBois)
-
-- Fileinfo:
- . Fixed bug #71434 (finfo throws notice for specific python file). (Laruence)
-
-- FPM:
- . Fixed bug #62172 (FPM not working with Apache httpd 2.4 balancer/fcgi
- setup). (Matt Haught, Remi)
- . Fixed bug #71269 (php-fpm dumped core). (Mickaël)
-
-- Opcache:
- . Fixed bug #71584 (Possible use-after-free of ZCG(cwd) in Zend Opcache).
- (Yussuf Khalil)
-
-- PCRE:
- . Fixed bug #71537 (PCRE segfault from Opcache). (Laruence)
-
-- phpdbg:
- . Fixed inherited functions from unspecified files being included in
- phpdbg_get_executable(). (Bob)
-
-- SOAP:
- . Fixed bug #71610 (Type Confusion Vulnerability - SOAP /
- make_http_soap_request()). (CVE-2016-3185) (Stas)
-
-- Standard:
- . Fixed bug #71603 (compact() maintains references in php7). (Laruence)
- . Fixed bug #70720 (strip_tags improper php code parsing). (Julien)
-
-- XMLRPC:
- . Fixed bug #71501 (xmlrpc_encode_request ignores encoding option). (Hieu Le)
-
-- Zip:
- . Fixed bug #71561 (NULL pointer dereference in Zip::ExtractTo). (Laruence)
-
-04 Feb 2016 PHP 7.0.3
-
-- Core:
- . Added support for new HTTP 451 code. (Julien)
- . Fixed bug #71039 (exec functions ignore length but look for NULL
- termination). (Anatol)
- . Fixed bug #71089 (No check to duplicate zend_extension). (Remi)
- . Fixed bug #71201 (round() segfault on 64-bit builds). (Anatol)
- . Fixed bug #71221 (Null pointer deref (segfault) in get_defined_vars via
- ob_start). (hugh at allthethings dot co dot nz)
- . Fixed bug #71248 (Wrong interface is enforced). (Dmitry)
- . Fixed bug #71273 (A wrong ext directory setup in php.ini leads to crash).
- (Anatol)
- . Fixed Bug #71275 (Bad method called on cloning an object having a trait).
- (Bob)
- . Fixed bug #71297 (Memory leak with consecutive yield from). (Bob)
- . Fixed bug #71300 (Segfault in zend_fetch_string_offset). (Laruence)
- . Fixed bug #71314 (var_export(INF) prints INF.0). (Andrea)
- . Fixed bug #71323 (Output of stream_get_meta_data can be falsified by its
- input). (Leo Gaspard)
- . Fixed bug #71336 (Wrong is_ref on properties as exposed via
- get_object_vars()). (Laruence)
- . Fixed bug #71459 (Integer overflow in iptcembed()). (Stas)
-
-- Apache2handler:
- . Fix >2G Content-Length headers in apache2handler. (Adam Harvey)
-
-- CURL:
- . Fixed bug #71227 (Can't compile php_curl statically). (Anatol)
- . Fixed bug #71225 (curl_setopt() fails to set CURLOPT_POSTFIELDS with
- reference to CURLFile). (Laruence)
-
-- GD:
- . Improved fix for bug #70976. (Remi)
-
-- Interbase:
- . Fixed Bug #71305 (Crash when optional resource is omitted).
- (Laruence, Anatol)
-
-- LDAP:
- . Fixed bug #71249 (ldap_mod_replace/ldap_mod_add store value as string
- "Array"). (Laruence)
-
-- mbstring:
- . Fixed bug #71397 (mb_send_mail segmentation fault). (Andrea, Yasuo)
-
-- OpenSSL:
- . Fixed bug #71475 (openssl_seal() uninitialized memory usage). (Stas)
-
-- PCRE:
- . Upgraded pcrelib to 8.38. (CVE-2015-8383, CVE-2015-8386, CVE-2015-8387,
- CVE-2015-8389, CVE-2015-8390, CVE-2015-8391, CVE-2015-8393, CVE-2015-8394)
-
-- Phar:
- . Fixed bug #71354 (Heap corruption in tar/zip/phar parser). (CVE-2016-4342)
- (Stas)
- . Fixed bug #71331 (Uninitialized pointer in phar_make_dirstream()).
- (CVE-2016-4343) (Stas)
- . Fixed bug #71391 (NULL Pointer Dereference in phar_tar_setupmetadata()).
- (Stas)
- . Fixed bug #71488 (Stack overflow when decompressing tar archives).
- (CVE-2016-2554) (Stas)
-
-- SOAP:
- . Fixed bug #70979 (crash with bad soap request). (Anatol)
-
-- SPL:
- . Fixed bug #71204 (segfault if clean spl_autoload_funcs while autoloading).
- (Laruence)
- . Fixed bug #71202 (Autoload function registered by another not activated
- immediately). (Laruence)
- . Fixed bug #71311 (Use-after-free vulnerability in SPL(ArrayObject,
- unserialize)). (Sean Heelan)
- . Fixed bug #71313 (Use-after-free vulnerability in SPL(SplObjectStorage,
- unserialize)). (Sean Heelan)
-
-- Standard:
- . Fixed bug #71287 (Error message contains hexadecimal instead of decimal
- number). (Laruence)
- . Fixed bug #71264 (file_put_contents() returns unexpected value when
- filesystem runs full). (Laruence)
- . Fixed bug #71245 (file_get_contents() ignores "header" context option if
- it's a reference). (Laruence)
- . Fixed bug #71220 (Null pointer deref (segfault) in compact via ob_start).
- (hugh at allthethings dot co dot nz)
- . Fixed bug #71190 (substr_replace converts integers in original $search
- array to strings). (Laruence)
- . Fixed bug #71188 (str_replace converts integers in original $search array
- to strings). (Laruence)
- . Fixed bug #71132, #71197 (range() segfaults). (Thomas Punt)
-
-- WDDX:
- . Fixed bug #71335 (Type Confusion in WDDX Packet Deserialization). (Stas)
-
-07 Jan 2016 PHP 7.0.2
-
-- Core:
- . Fixed bug #71165 (-DGC_BENCH=1 doesn't work on PHP7).
- (y dot uchiyama dot 1015 at gmail dot com)
- . Fixed bug #71163 (Segmentation Fault: cleanup_unfinished_calls). (Laruence)
- . Fixed bug #71109 (ZEND_MOD_CONFLICTS("xdebug") doesn't work). (Laruence)
- . Fixed bug #71092 (Segmentation fault with return type hinting). (Laruence)
- . Fixed bug memleak in header_register_callback. (Laruence)
- . Fixed bug #71067 (Local object in class method stays in memory for each
- call). (Laruence)
- . Fixed bug #66909 (configure fails utf8_to_mutf7 test). (Michael Orlitzky)
- . Fixed bug #70781 (Extension tests fail on dynamic ext dependency).
- (Francois Laupretre)
- . Fixed bug #71089 (No check to duplicate zend_extension). (Remi)
- . Fixed bug #71086 (Invalid numeric literal parse error within
- highlight_string() function). (Nikita)
- . Fixed bug #71154 (Incorrect HT iterator invalidation causes iterator reuse).
+ . Fixed bug #71604 (Aborted Generators continue after nested finally).
(Nikita)
- . Fixed bug #52355 (Negating zero does not produce negative zero). (Andrea)
- . Fixed bug #66179 (var_export() exports float as integer). (Andrea)
- . Fixed bug #70804 (Unary add on negative zero produces positive zero).
- (Andrea)
-
-- CURL:
- . Fixed bug #71144 (Sementation fault when using cURL with ZTS).
- (Michael Maroszek, Laruence)
+ . Fixed bug #71572 (String offset assignment from an empty string inserts
+ null byte). (Francois)
+ . Fixed bug #71897 (ASCII 0x7F Delete control character permitted in
+ identifiers). (Andrea)
+ . Fixed bug #72188 (Nested try/finally blocks losing return value). (Dmitry)
+ . Fixed bug #72213 (Finally leaks on nested exceptions). (Dmitry, Nikita)
+ . Implemented the RFC `Support Class Constant Visibility`. (Sean DuBois,
+ Reeze Xia, Dmitry)
+ . Added void return type. (Andrea)
+ . Added support for negative string offsets in string offset syntax and
+ various string functions. (Francois)
+ . Added a form of the list() construct where keys can be specified. (Andrea)
+ . Number operators taking numeric strings now emit E_NOTICEs or E_WARNINGs
+ when given malformed numeric strings. (Andrea)
+ . (int), intval() where $base is 10 or unspecified, settype(), decbin(),
+ decoct(), dechex(), integer operators and other conversions now always
+ respect scientific notation in numeric strings. (Andrea)
+ . Implemented the RFC `Catching multiple exception types`. (Bronislaw Bialek,
+ Pierrick)
+ . Raise a compile-time warning on octal escape sequence overflow. (Sara)
+ . Added [] = as alternative construct to list() =. (Bob)
+ . Implemented logging to syslog with dynamic error levels. (Jani Ollikainen)
+ . Fixed bug #47517 (php-cgi.exe missing UAC manifest).
+ (maxdax15801 at users noreply github com)
-- DBA:
- . Fixed key leak with invalid resource. (Laruence)
-
-- Filter:
- . Fixed bug #71063 (filter_input(INPUT_ENV, ..) does not work). (Reeze Xia)
+- Apache2handler:
+ . Enable per-module logging in Apache 2.4+. (Martin Vobruba)
-- FPM:
- . Fixed bug #70755 (fpm_log.c memory leak and buffer overflow). (Stas)
+- CLI Server:
+ . Fixed bug #71276 (Built-in webserver does not send Date header).
+ (see at seos fr)
- FTP:
. Implemented FR #55651 (Option to ignore the returned FTP PASV address).
(abrender at elitehosts dot com)
-- GD:
- . Fixed bug #70976 (Memory Read via gdImageRotateInterpolated Array Index
- Out of Bounds). (CVE-2016-1903) (emmanuel dot law at gmail dot com)
-
-- Mbstring:
- . Fixed bug #71066 (mb_send_mail: Program terminated with signal SIGSEGV,
- Segmentation fault). (Laruence)
-
-- Opcache:
- . Fixed bug #71127 (Define in auto_prepend_file is overwrite). (Laruence)
-
-- PCRE:
- . Fixed bug #71178 (preg_replace with arrays creates [0] in replace array
- if not already set). (Laruence)
-
-- Readline:
- . Fixed bug #71094 (readline_completion_function corrupts static array on
- second TAB). (Nikita)
-
-- Session:
- . Fixed bug #71122 (Session GC may not remove obsolete session data). (Yasuo)
-
-- SPL:
- . Fixed bug #71077 (ReflectionMethod for ArrayObject constructor returns
- wrong number of parameters). (Laruence)
- . Fixed bug #71153 (Performance Degradation in ArrayIterator with large
- arrays). (Nikita)
-
-- Standard:
- . Fixed bug #71270 (Heap BufferOver Flow in escapeshell functions).
- (CVE-2016-1904) (emmanuel dot law at gmail dot com)
-
-- WDDX:
- . Fixed bug #70661 (Use After Free Vulnerability in WDDX Packet
- Deserialization). (taoguangchen at icloud dot com)
- . Fixed bug #70741 (Session WDDX Packet Deserialization Type Confusion
- Vulnerability). (taoguangchen at icloud dot com)
-
-- XMLRPC:
- . Fixed bug #70728 (Type Confusion Vulnerability in PHP_to_XMLRPC_worker).
- (Julien)
-
-17 Dec 2015, PHP 7.0.1
-
-- Core:
- . Fixed bug #71105 (Format String Vulnerability in Class Name Error Message).
- (CVE-2015-8617) (andrew at jmpesp dot org)
- . Fixed bug #70831 (Compile fails on system with 160 CPUs). (Daniel Axtens)
- . Fixed bug #71006 (symbol referencing errors on Sparc/Solaris). (Dmitry)
- . Fixed bug #70997 (When using parentClass:: instead of parent::, static
- context changed). (Dmitry)
- . Fixed bug #70970 (Segfault when combining error handler with output
- buffering). (Laruence)
- . Fixed bug #70967 (Weird error handling for __toString when Error is
- thrown). (Laruence)
- . Fixed bug #70958 (Invalid opcode while using ::class as trait method
- paramater default value). (Laruence)
- . Fixed bug #70944 (try{ } finally{} can create infinite chains of
- exceptions). (Laruence)
- . Fixed bug #70931 (Two errors messages are in conflict). (dams, Laruence)
- . Fixed bug #70904 (yield from incorrectly marks valid generator as
- finished). (Bob)
- . Fixed bug #70899 (buildconf failure in extensions). (Bob, Reeze)
- . Fixed bug #61751 (SAPI build problem on AIX: Undefined symbol:
- php_register_internal_extensions). (Lior Kaplan)
- . Fixed \int (or generally every scalar type name with leading backslash)
- to not be accepted as type name. (Bob)
- . Fixed exception not being thrown immediately into a generator yielding
- from an array. (Bob)
- . Fixed bug #70987 (static::class within Closure::call() causes segfault).
- (Andrea)
- . Fixed bug #71013 (Incorrect exception handler with yield from). (Bob)
- . Fixed double free in error condition of format printer. (Bob)
-
-- CLI server:
- . Fixed bug #71005 (Segfault in php_cli_server_dispatch_router()). (Adam)
-
- Intl:
- . Fixed bug #71020 (Use after free in Collator::sortWithSortKeys).
- (CVE-2015-8616) (emmanuel dot law at gmail dot com, Laruence)
-
-- Mysqlnd:
- . Fixed bug #68077 (LOAD DATA LOCAL INFILE / open_basedir restriction).
- (Laruence)
- . Fixed bug #68344 (MySQLi does not provide way to disable peer certificate
- validation) by introducing MYSQLI_CLIENT_SSL_DONT_VERIFY_SERVER_CERT
- connection flag. (Andrey)
-
-- OCI8:
- . Fixed LOB implementation size_t/zend_long mismatch reported by gcov.
- (Senthil)
-
-- Opcache:
- . Fixed bug #71024 (Unable to use PHP 7.0 x64 side-by-side with PHP 5.6 x32
- on the same server). (Anatol)
- . Fixed bug #70991 (zend_file_cache.c:710: error: array type has incomplete
- element type). (Laruence)
- . Fixed bug #70977 (Segmentation fault with opcache.huge_code_pages=1).
- (Laruence)
-
-- PDO_Firebird:
- . Fixed bug #60052 (Integer returned as a 64bit integer on X64_86). (Mariuz)
-
-- Phpdbg:
- . Fixed stderr being written to stdout. (Bob)
-
-- Reflection:
- . Fixed bug #71018 (ReflectionProperty::setValue() behavior changed).
- (Laruence)
- . Fixed bug #70982 (setStaticPropertyValue behaviors inconsistently with
- 5.6). (Laruence)
-
-- Soap:
- . Fixed bug #70993 (Array key references break argument processing).
- (Laruence)
-
-- SPL:
- . Fixed bug #71028 (Undefined index with ArrayIterator). (Laruence)
-
-- SQLite3:
- . Fixed bug #71049 (SQLite3Stmt::execute() releases bound parameter instead
- of internal buffer). (Laruence)
-
-- Standard:
- . Fixed bug #70999 (php_random_bytes: called object is not a function).
- (Scott)
- . Fixed bug #70960 (ReflectionFunction for array_unique returns wrong number
- of parameters). (Laruence)
-
-- Streams/Socket:
- . Add IPV6_V6ONLY constant / make it usable in stream contexts. (Bob)
-
-03 Dec 2015, PHP 7.0.0
-
-- Core:
- . Fixed bug #70947 (INI parser segfault with INI_SCANNER_TYPED). (Laruence)
- . Fixed bug #70914 (zend_throw_or_error() format string vulnerability).
- (Taoguang Chen)
- . Fixed bug #70912 (Null ptr dereference instantiating class with invalid
- array property). (Laruence)
- . Fixed bug #70895, #70898 (null ptr deref and segfault with crafted calable).
- (Anatol, Laruence)
- . Fixed bug #70249 (Segmentation fault while running PHPUnit tests on
- phpBB 3.2-dev). (Laruence)
- . Fixed bug #70805 (Segmentation faults whilst running Drupal 8 test suite).
- (Dmitry, Laruence)
- . Fixed bug #70842 (Persistent Stream Segmentation Fault). (Caleb Champlin)
- . Fixed bug #70862 (Several functions do not check return code of
- php_stream_copy_to_mem()). (Anatol)
- . Fixed bug #70863 (Incorect logic to increment_function for proxy objects).
- (Anatol)
- . Fixed bug #70323 (Regression in zend_fetch_debug_backtrace() can cause
- segfaults). (Aharvey, Laruence)
- . Fixed bug #70873 (Regression on private static properties access).
- (Laruence)
- . Fixed bug #70748 (Segfault in ini_lex () at Zend/zend_ini_scanner.l).
- (Laruence)
- . Fixed bug #70689 (Exception handler does not work as expected). (Laruence)
- . Fixed bug #70430 (Stack buffer overflow in zend_language_parser()). (Nikita)
- . Fixed bug #70782 (null ptr deref and segfault (zend_get_class_fetch_type)).
- (Nikita)
- . Fixed bug #70785 (Infinite loop due to exception during identical
- comparison). (Laruence)
- . Fixed bug #70630 (Closure::call/bind() crash with ReflectionFunction->
- getClosure()). (Dmitry, Bob)
- . Fixed bug #70662 (Duplicate array key via undefined index error handler).
- (Nikita)
- . Fixed bug #70681 (Segfault when binding $this of internal instance method
- to null). (Nikita)
- . Fixed bug #70685 (Segfault for getClosure() internal method rebind with
- invalid $this). (Nikita)
- . Added zend_internal_function.reserved[] fields. (Dmitry)
- . Fixed bug #70557 (Memleak on return type verifying failed). (Laruence)
- . Fixed bug #70555 (fun_get_arg() on unsetted vars return UNKNOW). (Laruence)
- . Fixed bug #70548 (Redundant information printed in case of uncaught engine
- exception). (Laruence)
- . Fixed bug #70547 (unsetting function variables corrupts backtrace).
- (Laruence)
- . Fixed bug #70528 (assert() with instanceof adds apostrophes around class
- name). (Laruence)
- . Fixed bug #70481 (Memory leak in auto_global_copy_ctor() in ZTS build).
- (Laruence)
- . Fixed bug #70431 (Memory leak in php_ini.c). (Senthil, Laruence)
- . Fixed bug #70478 (**= does no longer work). (Bob)
- . Fixed bug #70398 (SIGSEGV, Segmentation fault zend_ast_destroy_ex).
- (Dmitry, Bob, Laruence)
- . Fixed bug #70332 (Wrong behavior while returning reference on object).
- (Laruence, Dmitry)
- . Fixed bug #70300 (Syntactical inconsistency with new group use syntax).
- (marcio dot web2 at gmail dot com)
- . Fixed bug #70321 (Magic getter breaks reference to array property).
- (Laruence)
- . Fixed bug #70187 (Notice: unserialize(): Unexpected end of serialized
- data). (Dmitry)
- . Fixed bug #70145 (From field incorrectly parsed from headers). (Anatol)
- . Fixed bug #70370 (Bundled libtool.m4 doesn't handle FreeBSD 10 when
- building extensions). (Adam)
- . Fixed bug causing exception traces with anon classes to be truncated. (Bob)
- . Fixed bug #70397 (Segmentation fault when using Closure::call and yield).
- (Bob)
- . Fixed bug #70299 (Memleak while assigning object offsetGet result).
- (Laruence)
- . Fixed bug #70288 (Apache crash related to ZEND_SEND_REF). (Laruence)
- . Fixed bug #70262 (Accessing array crashes PHP 7.0beta3).
- (Laruence, Dmitry)
- . Fixed bug #70258 (Segfault if do_resize fails to allocated memory).
- (Laruence)
- . Fixed bug #70253 (segfault at _efree () in zend_alloc.c:1389). (Laruence)
- . Fixed bug #70240 (Segfault when doing unset($var());). (Laruence)
- . Fixed bug #70223 (Incrementing value returned by magic getter). (Laruence)
- . Fixed bug #70215 (Segfault when __invoke is static). (Bob)
- . Fixed bug #70207 (Finally is broken with opcache). (Laruence, Dmitry)
- . Fixed bug #70173 (ZVAL_COPY_VALUE_EX broken for 32bit Solaris Sparc).
- (Laruence, cmb)
- . Fixed bug #69487 (SAPI may truncate POST data). (cmb)
- . Fixed bug #70198 (Checking liveness does not work as expected).
- (Shafreeck Sea, Anatol Belski)
- . Fixed bug #70241,#70293 (Skipped assertions affect Generator returns). (Bob)
- . Fixed bug #70239 (Creating a huge array doesn't result in exhausted,
- but segfault). (Laruence, Anatol)
- . Fixed "finally" issues. (Nikita, Dmitry)
- . Fixed bug #70098 (Real memory usage doesn't decrease). (Dmitry)
- . Fixed bug #70159 (__CLASS__ is lost in closures). (Julien)
- . Fixed bug #70156 (Segfault in zend_find_alias_name). (Laruence)
- . Fixed bug #70124 (null ptr deref / seg fault in ZEND_HANDLE_EXCEPTION).
- (Laruence)
- . Fixed bug #70117 (Unexpected return type error). (Laruence)
- . Fixed bug #70106 (Inheritance by anonymous class). (Bob)
- . Fixed bug #69674 (SIGSEGV array.c:953). (cmb)
- . Fixed bug #70164 (__COMPILER_HALT_OFFSET__ under namespace is not defined).
- (Bob)
- . Fixed bug #70108 (sometimes empty $_SERVER['QUERY_STRING']). (Anatol)
- . Fixed bug #70179 ($this refcount issue). (Bob)
- . Fixed bug #69896 ('asm' operand has impossible constraints). (Anatol)
- . Fixed bug #70183 (null pointer deref (segfault) in zend_eval_const_expr).
- (Hugh Davenport)
- . Fixed bug #70182 (Segfault in ZEND_ASSIGN_DIV_SPEC_CV_UNUSED_HANDLER).
- (Hugh Davenport)
- . Fixed bug #69793 (Remotely triggerable stack exhaustion via recursive
- method calls). (Stas)
- . Fixed bug #69892 (Different arrays compare indentical due to integer key
- truncation). (Nikita)
- . Fixed bug #70121 (unserialize() could lead to unexpected methods execution
- / NULL pointer deref). (Stas)
- . Fixed bug #70089 (segfault at ZEND_FETCH_DIM_W_SPEC_VAR_CONST_HANDLER ()).
- (Laruence)
- . Fixed bug #70057 (Build failure on 32-bit Mac OS X 10.6.8: recursive
- inlining). (Laruence)
- . Fixed bug #70012 (Exception lost with nested finally block). (Laruence)
- . Fixed bug #69996 (Changing the property of a cloned object affects the
- original). (Dmitry, Laruence)
- . Fixed bug #70083 (Use after free with assign by ref to overloaded objects).
- (Bob)
- . Fixed bug #70006 (cli - function with default arg = STDOUT crash output).
- (Laruence)
- . Fixed bug #69521 (Segfault in gc_collect_cycles()).
- (arjen at react dot com, Laruence)
- . Improved zend_string API. (Francois Laupretre)
- . Fixed bug #69955 (Segfault when trying to combine [] and assign-op on
- ArrayAccess object). (Laruence)
- . Fixed bug #69957 (Different ways of handling div/mod/intdiv). (Bob)
- . Fixed bug #69900 (Too long timeout on pipes). (Anatol)
- . Fixed bug #69872 (uninitialised value in strtr with array). (Laruence)
- . Fixed bug #69868 (Invalid read of size 1 in zend_compile_short_circuiting).
- (Laruence)
- . Fixed bug #69849 (Broken output of apache_request_headers). (Kalle)
- . Fixed bug #69840 (iconv_substr() doesn't work with UTF-16BE). (Kalle)
- . 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 #69802 (Reflection on Closure::__invoke borks type hint class
- name). (Dmitry)
- . 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)
- . Fixed bug #69835 (phpinfo() does not report many Windows SKUs).
- (Christian Wenz)
- . Fixed bug #69889 (Null coalesce operator doesn't work for string offsets).
- (Nikita)
- . Fixed bug #69891 (Unexpected array comparison result). (Nikita)
- . Fixed bug #69892 (Different arrays compare indentical due to integer key
- truncation). (Nikita)
- . Fixed bug #69893 (Strict comparison between integer and empty string keys
- crashes). (Nikita)
- . 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)
- . Fixed bug #69427 (Segfault on magic method __call of private method in
- superclass). (Laruence)
- . Improved __call() and __callStatic() magic method handling. Now they are
- called in a stackless way using ZEND_CALL_TRAMPOLINE opcode, without
- additional stack frame. (Laruence, Dmitry)
- . Optimized strings concatenation. (Dmitry, Laruence)
- . Fixed weird operators behavior. Division by zero now emits warning and
- returns +/-INF, modulo by zero and intdid() throws an exception, shifts
- by negative offset throw exceptions. Compile-time evaluation of division
- by zero is disabled. (Dmitry, Andrea, Nikita)
- . Fixed bug #69371 (Hash table collision leads to inaccessible array keys).
- (Laruence)
- . Fixed bug #68933 (Invalid read of size 8 in zend_std_read_property).
- (Laruence, arjen at react dot com)
- . Fixed bug #68252 (segfault in Zend/zend_hash.c in function
- _zend_hash_del_el). (Laruence)
- . Fixed bug #65598 (Closure executed via static autoload incorrectly marked as
- static). (Nikita)
- . Fixed bug #66811 (Cannot access static::class in lambda, writen outside of a
- class). (Nikita)
- . Fixed bug #69568 (call a private function in closure failed). (Nikita)
- . Added PHP_INT_MIN constant. (Andrea)
- . Added Closure::call() method. (Andrea)
- . Fixed bug #67959 (Segfault when calling phpversion('spl')). (Florian)
- . Implemented the RFC `Catchable "Call to a member function bar() on a
- non-object"`. (Timm)
- . Added options parameter for unserialize allowing to specify acceptable
- classes (https://wiki.php.net/rfc/secure_unserialize). (Stas)
- . Fixed bug #63734 (Garbage collector can free zvals that are still
- referenced). (Dmitry)
- . Removed ZEND_ACC_FINAL_CLASS, promoting ZEND_ACC_FINAL as final class
- modifier. (Guilherme Blanco)
- . is_long() & is_integer() is now an alias of is_int(). (Kalle)
- . Implemented FR #55467 (phpinfo: PHP Variables with $ and single quotes). (Kalle)
- . Added ?? operator. (Andrea)
- . Added <=> operator. (Andrea)
- . Added \u{xxxxx} Unicode Codepoint Escape Syntax. (Andrea)
- . Fixed oversight where define() did not support arrays yet const syntax did.
- (Andrea, Dmitry)
- . Use "integer" and "float" instead of "long" and "double" in ZPP, type hint
- and conversion error messages. (Andrea)
- . Implemented FR #55428 (E_RECOVERABLE_ERROR when output buffering in output
- buffering handler). (Kalle)
- . Removed scoped calls of non-static methods from an incompatible $this
- context. (Nikita)
- . Removed support for #-style comments in ini files. (Nikita)
- . Removed support for assigning the result of new by reference. (Nikita)
- . Invalid octal literals in source code now produce compile errors, fixes
- PHPSadness #31. (Andrea)
- . Removed dl() function on fpm-fcgi. (Nikita)
- . Removed support for hexadecimal numeric strings. (Nikita)
- . Removed obsolete extensions and SAPIs. See the full list in UPGRADING. (Anatol)
- . Added NULL byte protection to exec, system and passthru. (Yasuo)
- . Added error_clear_last() function. (Reeze Xia)
- . Fixed bug #68797 (Number 2.2250738585072012e-308 converted incorrectly).
- (Anatol)
- . Improved zend_qsort(using hybrid sorting algo) for better performance,
- and also renamed zend_qsort to zend_sort. (Laruence)
- . Added stable sorting algo zend_insert_sort. (Laruence)
- . Improved zend_memnchr(using sunday algo) for better performance. (Laruence)
- . Implemented the RFC `Scalar Type Decalarations v0.5`. (Anthony)
- . Implemented the RFC `Group Use Declarations`. (Marcio)
- . Implemented the RFC `Continue Output Buffering`. (Mike)
- . 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 `Context Sensitive Lexer`. (Marcio Almada)
- . Fixed bug #69511 (Off-by-one buffer overflow in php_sys_readlink).
- (Jan Starke, Anatol)
-
-- CLI server:
- . Fixed bug #68291 (404 on urls with '+'). (cmb)
- . Fixed bug #66606 (Sets HTTP_CONTENT_TYPE but not CONTENT_TYPE).
- (wusuopu, cmb)
- . Fixed bug #70264 (CLI server directory traversal). (cmb)
- . Fixed bug #69655 (php -S changes MKCALENDAR request method to MKCOL). (cmb)
- . Fixed bug #64878 (304 responses return Content-Type header). (cmb)
- . 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)
-
-- COM:
- . Fixed bug #69939 (Casting object to bool returns false). (Kalle)
-
-- Curl:
- . Fixed bug #70330 (Segmentation Fault with multiple "curl_copy_handle").
- (Laruence)
- . Fixed bug #70163 (curl_setopt_array() type confusion). (Laruence)
- . Fixed bug #70065 (curl_getinfo() returns corrupted values). (Anatol)
- . Fixed bug #69831 (Segmentation fault in curl_getinfo). (im dot denisenko at
- yahoo dot com)
- . Fixed bug #68937 (Segfault in curl_multi_exec). (Laruence)
- . Removed support for unsafe file uploads. (Nikita)
-
-- Date:
- . Fixed bug #70245 (strtotime does not emit warning when 2nd parameter is
- object or string). (cmb)
- . Fixed bug #70266 (DateInterval::__construct.interval_spec is not supposed to
- be optional). (cmb)
- . Fixed bug #70277 (new DateTimeZone($foo) is ignoring text after null byte).
- (cmb)
- . Fixed day_of_week function as it could sometimes return negative values
- internally. (Derick)
- . Removed $is_dst parameter from mktime() and gmmktime(). (Nikita)
- . Removed date.timezone warning
- (https://wiki.php.net/rfc/date.timezone_warning_removal). (Bob)
- . Added "v" DateTime format modifier to get the 3-digit version of fraction
- of seconds. (Mariano Iglesias)
- . Implemented FR #69089 (Added DateTime::RFC3339_EXTENDED to output in
- RFC3339 Extended format which includes fraction of seconds). (Mariano
- Iglesias)
-
-- DBA:
- . Fixed bug #62490 (dba_delete returns true on missing item (inifile)). (Mike)
- . Fixed bug #68711 (useless comparisons). (bugreports at internot dot info)
-
-- DOM:
- . Fixed bug #70558 ("Couldn't fetch" error in
- DOMDocument::registerNodeClass()). (Laruence)
- . Fixed bug #70001 (Assigning to DOMNode::textContent does additional entity
- encoding). (cmb)
- . Fixed bug #69846 (Segmenation fault (access violation) when iterating over
- DOMNodeList). (Anatol Belski)
- . Made DOMNode::textContent writeable. (Tjerk)
-
-- EXIF:
- . Fixed bug #70385 (Buffer over-read in exif_read_data with TIFF IFD tag byte
- value of 32 bytes). (Stas)
-
-- Fileinfo:
- . Fixed bug #66242 (libmagic: don't assume char is signed). (ArdB)
-
-- Filter:
- . New FILTER_VALIDATE_DOMAIN and better RFC conformance for FILTER_VALIDATE_URL. (Kevin Dunglas)
-
-- FPM:
- . Fixed bug #70538 ("php-fpm -i" crashes). (rainer dot jung at
- kippdata dot de)
- . Fixed bug #70279 (HTTP Authorization Header is sometimes passed to newer
- reqeusts). (Laruence)
- . Fixed bug #68945 (Unknown admin values segfault pools). (Laruence)
- . Fixed bug #65933 (Cannot specify config lines longer than 1024 bytes). (Chris Wright)
- . Implemented FR #67106 (Split main fpm config). (Elan Ruusamäe, Remi)
-
-- FTP:
- . Fixed bug #69082 (FTPS support on Windows). (Anatol)
-
-- GD:
- . Fixed bug #53156 (imagerectangle problem with point ordering). (cmb)
- . Fixed bug #66387 (Stack overflow with imagefilltoborder). (CVE-2015-8874)
- (cmb)
- . Fixed bug #70102 (imagecreatefromwebm() shifts colors). (cmb)
- . Fixed bug #66590 (imagewebp() doesn't pad to even length). (cmb)
- . Fixed bug #66882 (imagerotate by -90 degrees truncates image by 1px). (cmb)
- . Fixed bug #70064 (imagescale(..., IMG_BICUBIC) leaks memory). (cmb)
- . Fixed bug #69024 (imagescale segfault with palette based image). (cmb)
- . Fixed bug #53154 (Zero-height rectangle has whiskers). (cmb)
- . Fixed bug #67447 (imagecrop() add a black line when cropping). (cmb)
- . Fixed bug #68714 (copy 'n paste error). (cmb)
- . Fixed bug #66339 (PHP segfaults in imagexbm). (cmb)
- . Fixed bug #70047 (gd_info() doesn't report WebP support). (cmb)
- . Replace libvpx with libwebp for bundled libgd. (cmb, Anatol)
- . Fixed bug #61221 (imagegammacorrect function loses alpha channel). (cmb)
- . Made fontFetch's path parser thread-safe. (Sara)
- . Removed T1Lib support. (Kalle)
-
-- GMP:
- . Fixed bug #70284 (Use after free vulnerability in unserialize() with GMP).
- (stas)
+ . Added IntlTimeZone::getWindowsID() and
+ IntlTimeZone::getIDForWindowsID(). (Sara)
+ . Fixed bug #69374 (IntlDateFormatter formatObject returns wrong utf8 value).
+ (lenhatanh86 at gmail com)
+ . Fixed bug #69398 (IntlDateFormatter formatObject returns wrong value when
+ time style is NONE). (lenhatanh86 at gmail com)
-- hash:
- . Fixed bug #70312 (HAVAL gives wrong hashes in specific cases). (letsgolee
- at naver dot com)
-
-- IMAP:
- . Fixed bug #70158 (Building with static imap fails). (cmb)
- . Fixed bug #69998 (curl multi leaking memory). (Pierrick)
-
-- Intl:
- . Fixed bug #70453 (IntlChar::foldCase() incorrect arguments and missing
- constants). (cmb)
- . Fixed bug #70454 (IntlChar::forDigit second parameter should be optional).
- (cmb, colinodell)
- . Removed deprecated aliases datefmt_set_timezone_id() and
- IntlDateFormatter::setTimeZoneID(). (Nikita)
+- Hash:
+ . Added SHA3 fixed mode algorithms (224, 256, 384, and 512 bit). (Sara)
+ . Added SHA512/256 and SHA512/224 algorithms. (Sara)
- JSON:
- . Fixed bug #62010 (json_decode produces invalid byte-sequences).
- (Jakub Zelenka)
- . Fixed bug #68546 (json_decode() Fatal error: Cannot access property
- started with '\0'). (Jakub Zelenka)
- . Replace non-free JSON parser with a parser from Jsond extension, fixes #63520
- (JSON extension includes a problematic license statement). (Jakub Zelenka)
- . Fixed bug #68938 (json_decode() decodes empty string without error).
- (jeremy at bat-country dot us)
-
-- LDAP:
- . Fixed bug #47222 (Implement LDAP_OPT_DIAGNOSTIC_MESSAGE). (Andreas Heigl)
-
-- 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 bug #70625 (mcrypt_encrypt() won't return data when no IV was
- specified under RC4). (Nikita)
- . Fixed bug #69833 (mcrypt fd caching not working). (Anatol)
- . 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)
-
-- Mysqli:
- . Fixed bug #32490 (constructor of mysqli has wrong name). (cmb)
-
-- Mysqlnd:
- . Fixed bug #70949 (SQL Result Sets With NULL Can Cause Fatal Memory Errors).
- (Laruence)
- . Fixed bug #70384 (mysqli_real_query():Unknown type 245 sent by the server).
- (Andrey)
- . Fixed bug #70456 (mysqlnd doesn't activate TCP keep-alive when connecting to
- a server). (Sergei Turchanov)
- . Fixed bug #70572 segfault in mysqlnd_connect. (Andrey, Remi)
- . Fixed Bug #69796 (mysqli_stmt::fetch doesn't assign null values to
- bound variables). (Laruence)
-
-- OCI8:
- . Fixed memory leak with LOBs. (Senthil)
- . Fixed bug #68298 (OCI int overflow) (Senthil).
- . Corrected oci8 hash destructors to prevent segfaults, and a few other fixes.
- (Cameron Porter)
-
-- ODBC:
- . Fixed bug #69975 (PHP segfaults when accessing nvarchar(max) defined
- columns). (cmb)
+ . Exported JSON parser API including json_parser_method that can be used
+ for implementing custom logic when parsing JSON. (Jakub Zelenka)
+ . Escaped U+2028 and U+2029 when JSON_UNESCAPED_UNICODE is supplied as
+ json_encode options and added JSON_UNESCAPED_LINE_TERMINATORS to restore
+ the previous behaviour. (Eddie Kohler)
-- Opcache:
- . Fixed bug #70656 (require() statement broken after opcache_reset() or a
- few hours of use). (Laruence)
- . Fixed bug #70843 (Segmentation fault on MacOSX with
- opcache.file_cache_only=1). (Laruence)
- . Fixed bug #70724 (Undefined Symbols from opcache.so on Mac OS X 10.10).
- (Laruence)
- . Fixed compatibility with Windows 10 (see also bug #70652). (Anatol)
- . Attmpt to fix "Unable to reattach to base address" problem. (Matt Ficken)
- . Fixed bug #70423 (Warning Internal error: wrong size calculation). (Anatol)
- . Fixed bug #70237 (Empty while and do-while segmentation fault with opcode
- on CLI enabled). (Dmitry, Laruence)
- . Fixed bug #70111 (Segfault when a function uses both an explicit return
- type and an explicit cast). (Laruence)
- . Fixed bug #70058 (Build fails when building for i386). (Laruence)
- . Fixed bug #70022 (Crash with opcache using opcache.file_cache_only=1).
- (Anatol)
- . Removed opcache.load_comments configuration directive. Now doc comments
- loading costs nothing and always enabled. (Dmitry)
- . Fixed bug #69838 (Wrong size calculation for function table). (Anatol)
- . 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
- generation is turned on. (Laruence)
- . Fixed bug #68644 (strlen incorrect : mbstring + func_overload=2 +UTF-8
- + Opcache). (Laruence)
-
-- OpenSSL:
- . Require at least OpenSSL version 0.9.8. (Jakub Zelenka)
- . Fixed bug #68312 (Lookup for openssl.cnf causes a message box). (Anatol)
- . Fixed bug #55259 (openssl extension does not get the DH parameters from
- DH key resource). (Jakub Zelenka)
- . Fixed bug #70395 (Missing ARG_INFO for openssl_seal()). (cmb)
- . Fixed bug #60632 (openssl_seal fails with AES). (Jakub Zelenka)
- . Implemented FR #70438 (Add IV parameter for openssl_seal and openssl_open)
- (Jakub Zelenka)
- . Fixed bug #70014 (openssl_random_pseudo_bytes() is not cryptographically
- secure). (CVE-2015-8867) (Stas)
- . Fixed bug #69882 (OpenSSL error "key values mismatch" after
- openssl_pkcs12_read with extra cert). (Tomasz Sawicki)
- . Added "alpn_protocols" SSL context option allowing encrypted client/server
- streams to negotiate alternative protocols using the ALPN TLS extension when
- built against OpenSSL 1.0.2 or newer. Negotiated protocol information is
- accessible through stream_get_meta_data() output.
- . Removed "CN_match" and "SNI_server_name" SSL context options. Use automatic
- detection or the "peer_name" option instead. (Nikita)
-
-- Pcntl:
- . Fixed bug #70386 (Can't compile on NetBSD because of missing WCONTINUED
- and WIFCONTINUED). (Matteo)
- . Fixed bug #60509 (pcntl_signal doesn't decrease ref-count of old handler
- when setting SIG_DFL). (Julien)
- . Implemented FR #68505 (Added wifcontinued and wcontinued). (xilon-jul)
- . Added rusage support to pcntl_wait() and pcntl_waitpid(). (Anton Stepanenko,
- Tony)
-
-- PCRE:
- . Fixed bug #70232 (Incorrect bump-along behavior with \K and empty string
- match). (cmb)
- . Fixed bug #70345 (Multiple vulnerabilities related to PCRE functions).
- (Anatol Belski)
- . Fixed bug #70232 (Incorrect bump-along behavior with \K and empty string
- match). (cmb)
- . Fixed bug #53823 (preg_replace: * qualifier on unicode replace garbles the
- string). (cmb)
- . Fixed bug #69864 (Segfault in preg_replace_callback). (cmb, ab)
-
-- PDO:
- . Fixed bug #70861 (Segmentation fault in pdo_parse_params() during Drupal 8
- test suite). (Anatol)
- . Fixed bug #70389 (PDO constructor changes unrelated variables). (Laruence)
- . Fixed bug #70272 (Segfault in pdo_mysql). (Laruence)
- . Fixed bug #70221 (persistent sqlite connection + custom function
- segfaults). (Laruence)
- . Removed support for the /e (PREG_REPLACE_EVAL) modifier. (Nikita)
- . Fixed bug #59450 (./configure fails with "Cannot find php_pdo_driver.h").
- (maxime dot besson at smile dot fr)
-
-- PDO_DBlib:
- . Fixed bug #69757 (Segmentation fault on nextRowset).
- (miracle at rpz dot name)
-
-- PDO_mysql:
- . Fixed bug #68424 (Add new PDO mysql connection attr to control multi
- statements option). (peter dot wolanin at acquia dot com)
-
-- PDO_OCI:
- . Fixed bug #70308 (PDO::ATTR_PREFETCH is ignored). (Chris Jones)
-
-- PDO_pgsql:
- . Fixed bug #69752 (PDOStatement::execute() leaks memory with DML
- Statements when closeCuror() is u). (Philip Hofstetter)
- . Removed PGSQL_ATTR_DISABLE_NATIVE_PREPARED_STATEMENT attribute in favor of
- ATTR_EMULATE_PREPARES). (Nikita)
-
-- Phar:
- . Fixed bug #69720 (Null pointer dereference in phar_get_fp_offset()). (Stas)
- . FIxed bug #70433 (Uninitialized pointer in phar_make_dirstream when zip
- entry filename is "/"). (Stas)
- . Improved fix for bug #69441. (Anatol Belski)
- . Fixed bug #70019 (Files extracted from archive may be placed outside of
- destination directory). (Anatol Belski)
+- PDO_Firebird:
+ . Fixed bug #60052 (Integer returned as a 64bit integer on X86_64). (Mariuz)
-- Phpdbg:
- . Fixed bug #70614 (incorrect exit code in -rr mode with Exceptions). (Bob)
- . Fixed bug #70532 (phpdbg must respect set_exception_handler). (Bob)
- . Fixed bug #70531 (Run and quit mode (-qrr) should not fallback to
- interactive mode). (Bob)
- . Fixed bug #70533 (Help overview (-h) does not rpint anything under Windows).
- (Anatol)
- . Fixed bug #70449 (PHP won't compile on 10.4 and 10.5 because of missing
- constants). (Bob)
- . Fixed bug #70214 (FASYNC not defined, needs sys/file.h include). (Bob)
- . Fixed bug #70138 (Segfault when displaying memory leaks). (Bob)
+- Pgsql:
+ . Implemented FR #31021 (pg_last_notice() is needed to get all notice
+ messages). (Yasuo)
+ . Implemented FR #48532 (Allow pg_fetch_all() to index numerically). (Yasuo)
- Reflection:
- . Fixed bug #70650 (Wrong docblock assignment). (Marcio)
- . Fixed bug #70674 (ReflectionFunction::getClosure() leaks memory when used
- for internal functions). (Dmitry, Bob)
- . Fixed bug causing bogus traces for ReflectionGenerator::getTrace(). (Bob)
- . Fixed inheritance chain of Reflector interface. (Tjerk)
- . Added ReflectionGenerator class. (Bob)
- . Added reflection support for return types and type declarations. (Sara,
- Matteo)
+ . Fix #72209 (ReflectionProperty::getValue() doesn't fail if object doesn't match type). (Joe)
- Session:
- . Fixed bug #70876 (Segmentation fault when regenerating session id with
- strict mode). (Laruence)
- . Fixed bug #70529 (Session read causes "String is not zero-terminated" error).
+ . Improved fix for bug #68063 (Empty session IDs do still start sessions).
(Yasuo)
- . Fixed bug #70013 (Reference to $_SESSION is lost after a call to
- session_regenerate_id()). (Yasuo)
- . Fixed bug #69952 (Data integrity issues accessing superglobals by
- reference). (Bob)
- . Fixed bug #67694 (Regression in session_regenerate_id()). (Tjerk)
- . Fixed bug #68941 (mod_files.sh is a bash-script). (bugzilla at ii.nl, Yasuo)
-
-- SOAP:
- . Fixed bug #70940 (Segfault in soap / type_to_string). (Remi)
- . Fixed bug #70900 (SoapClient systematic out of memory error). (Dmitry)
- . Fixed bug #70875 (Segmentation fault if wsdl has no targetNamespace
- attribute). (Matteo)
- . Fixed bug #70715 (Segmentation fault inside soap client). (Laruence)
- . Fixed bug #70709 (SOAP Client generates Segfault). (Laruence)
- . Fixed bug #70388 (SOAP serialize_function_call() type confusion / RCE).
- (Stas)
- . Fixed bug #70081 (SoapClient info leak / null pointer dereference via
- multiple type confusions). (Stas)
- . Fixed bug #70079 (Segmentation fault after more than 100 SoapClient
- calls). (Laruence)
- . Fixed bug #70032 (make_http_soap_request calls
- zend_hash_get_current_key_ex(,,,NULL). (Laruence)
- . Fixed bug #68361 (Segmentation fault on SoapClient::__getTypes). (Laruence)
-
-- SPL:
- . Fixed bug #70959 (ArrayObject unserialize does not restore protected
- fields). (Laruence)
- . Fixed bug #70853 (SplFixedArray throws exception when using ref variable
- as index). (Laruence)
- . Fixed bug #70868 (PCRE JIT and pattern reuse segfault). (Laruence)
- . Fixed bug #70730 (Incorrect ArrayObject serialization if unset is called
- in serialize()). (Laruence)
- . Fixed bug #70573 (Cloning SplPriorityQueue leads to memory leaks). (Dmitry)
- . Fixed bug #70303 (Incorrect constructor reflection for ArrayObject). (cmb)
- . Fixed bug #70068 (Dangling pointer in the unserialization of ArrayObject
- items). (sean.heelan)
- . Fixed bug #70166 (Use After Free Vulnerability in unserialize() with
- SPLArrayObject). (taoguangchen at icloud dot com)
- . Fixed bug #70168 (Use After Free Vulnerability in unserialize() with
- SplObjectStorage). (taoguangchen at icloud dot com)
- . Fixed bug #70169 (Use After Free Vulnerability in unserialize() with
- SplDoublyLinkedList). (taoguangchen at icloud dot com)
- . Fixed bug #70053 (MutlitpleIterator array-keys incompatible change in
- PHP 7). (Tjerk)
- . Fixed bug #69970 (Use-after-free vulnerability in
- spl_recursive_it_move_forward_ex()). (Laruence)
- . Fixed bug #69845 (ArrayObject with ARRAY_AS_PROPS broken). (Dmitry)
- . Changed ArrayIterator implementation using zend_hash_iterator_... API.
- Allowed modification of iterated ArrayObject using the same behavior
- as proposed in `Fix "foreach" behavior`. Removed "Array was modified
- outside object and internal position is no longer valid" hack. (Dmitry)
- . Implemented FR #67886 (SplPriorityQueue/SplHeap doesn't expose extractFlags
- nor curruption state). (Julien)
- . Fixed bug #66405 (RecursiveDirectoryIterator::CURRENT_AS_PATHNAME
- breaks the RecursiveIterator). (Paul Garvin)
+ . Fixed bug #71038 (session_start() returns TRUE on failure).
+ Session save handlers must return 'string' always for successful read.
+ i.e. Non-existing session read must return empty string. PHP 7.0 is made
+ not to tolerate buggy return value. (Yasuo)
+ . Fixed bug #71394 (session_regenerate_id() must close opened session on
+ errors). (Yasuo)
- SQLite3:
- . Fixed bug #70571 (Memory leak in sqlite3_do_callback). (Adam)
- . Fixed bug #69972 (Use-after-free vulnerability in
- sqlite3SafetyCheckSickOrOk()). (Laruence)
- . Fixed bug #69897 (segfault when manually constructing SQLite3Result).
- (Kalle)
- . Fixed bug #68260 (SQLite3Result::fetchArray declares wrong
- required_num_args). (Julien)
+ . Implemented FR #71159 (Upgraded bundled SQLite lib to 3.9.2). (Laruence)
- Standard:
- . Fixed count on symbol tables. (Laruence)
- . Fixed bug #70963 (Unserialize shows UNKNOWN in result). (Laruence)
- . Fixed bug #70910 (extract() breaks variable references). (Laruence)
- . Fixed bug #70808 (array_merge_recursive corrupts memory of unset items).
- (Laruence)
- . Fixed bug #70667 (strtr() causes invalid writes and a crashes). (Dmitry)
- . Fixed bug #70668 (array_keys() doesn't respect references when $strict is
- true). (Bob, Dmitry)
- . Implemented the RFC `Random Functions Throwing Exceptions in PHP 7`.
- (Sammy Kaye Powers, Anthony)
- . Fixed bug #70487 (pack('x') produces an error). (Nikita)
- . Fixed bug #70342 (changing configuration with ignore_user_abort(true) isn't
- working). (Laruence)
- . Fixed bug #70295 (Segmentation fault with setrawcookie). (Bob)
- . Fixed bug #67131 (setcookie() conditional for empty values not met). (cmb)
- . Fixed bug #70365 (Use-after-free vulnerability in unserialize() with
- SplObjectStorage). (taoguangchen at icloud dot com)
- . Fixed bug #70366 (Use-after-free vulnerability in unserialize() with
- SplDoublyLinkedList). (taoguangchen at icloud dot com)
- . Fixed bug #70250 (extract() turns array elements to references).
- (Laruence)
- . Fixed bug #70211 (php 7 ZEND_HASH_IF_FULL_DO_RESIZE use after free).
- (Laruence)
- . Fixed bug #70208 (Assert breaking access on objects). (Bob)
- . Fixed bug #70140 (str_ireplace/php_string_tolower - Arbitrary Code
- Execution). (CVE-2015-6527) (Laruence)
- . Implemented FR #70112 (Allow "dirname" to go up various times). (Remi)
- . Fixed bug #36365 (scandir duplicates file name at every 65535th file). (cmb)
- . Fixed bug #70096 (Repeated iptcembed() adds superfluous FF bytes). (cmb)
- . Fixed bug #70018 (exec does not strip all whitespace). (Laruence)
- . Fixed bug #69983 (get_browser fails with user agent of null).
- (Kalle, cmb, Laruence)
- . Fixed bug #69976 (Unable to parse "all" urls with colon char). (cmb)
- . Fixed bug #69768 (escapeshell*() doesn't cater to !). (cmb)
- . Fixed bug #62922 (Truncating entire string should result in string).
- (Nikita)
- . 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)
- . Fixed bug #69299 (Regression in array_filter's $flag argument in PHP 7).
- (Laruence)
- . Removed call_user_method() and call_user_method_array() functions. (Kalle)
- . Fixed user session handlers (See rfc:session.user.return-value). (Sara)
- . Added intdiv() function. (Andrea)
- . Improved precision of log() function for base 2 and 10. (Marc Bennewitz)
- . Remove string category support in setlocale(). (Nikita)
- . Remove set_magic_quotes_runtime() and its alias magic_quotes_runtime().
- (Nikita)
- . 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)
- . 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 #70361 (HTTP stream wrapper doesn't close keep-alive connections).
- (Niklas Keller)
- . Fixed bug #68532 (convert.base64-encode omits padding bytes).
- (blaesius at krumedia dot de)
- . Removed set_socket_blocking() in favor of its alias stream_set_blocking().
- (Nikita)
-
-- Tokenizer:
- . Fixed bug #69430 (token_get_all has new irrecoverable errors). (Nikita)
-
-- XMLReader:
- . Fixed bug #70309 (XmlReader read generates extra output). (Anatol)
-
-- XMLRPC
- . Fixed bug #70526 (xmlrpc_set_type returns false on success). (Laruence)
-
-- XSL:
- . Fixed bug #70678 (PHP7 returns true when false is expected). (Felipe)
- . Fixed bug #70535 (XSLT: free(): invalid pointer). (Laruence)
- . Fixed bug #69782 (NULL pointer dereference). (Stas)
- . Fixed bug #64776 (The XSLT extension is not thread safe). (Mike)
- . Removed xsl.security_prefs ini option. (Nikita)
-
-- Zlib:
- . Added deflate_init(), deflate_add(), inflate_init(), inflate_add()
- functions allowing incremental/streaming compression/decompression.
- (Daniel Lowrey & Bob Weinand)
-
-- Zip:
- . Fixed bug #70322 (ZipArchive::close() doesn't indicate errors).
- (CVE-2014-9767) (cmb)
- . Fixed bug #70350 (ZipArchive::extractTo allows for directory traversal when
- creating directories). (neal at fb dot com)
- . Added ZipArchive::setCompressionName and ZipArchive::setCompressionIndex
- methods. (Remi, Cedric Delmas)
- . Update bundled libzip to 1.0.1. (Remi, Anatol)
- . Fixed bug #67161 (ZipArchive::getStream() returns NULL for certain file).
- (Christoph M. Becker)
-
+ . Fixed bug #71100 (long2ip() doesn't accept integers in strict mode).
+ (Laruence)
+ . Implemented FR #55716 (Add an option to pass a custom stream context to
+ get_headers()). (Ferenc)
+ . Additional validation for parse_url() for login/pass components).
+ (Ilia) (Julien)
+ . Implemented FR #69359 (Provide a way to fetch the current environment
+ variables). (Ferenc)
+ . unpack() function accepts an additional optional argument $offset. (Dmitry)
+ . Implemented #51879 stream context socket option tcp_nodelay (Joe)
+
+<<< NOTE: Insert NEWS from last stable release here prior to actual release! >>>
diff --git a/README.GIT-RULES b/README.GIT-RULES
index 5f35cc4f3e..a88a8c9f32 100644
--- a/README.GIT-RULES
+++ b/README.GIT-RULES
@@ -79,7 +79,7 @@ The next few rules are more of a technical nature::
should be noted in both PHP-5.4/NEWS and PHP-5.5/NEWS but
not master, which is not a public released version yet.
- 3. Do not commit multiple file and dump all messages in one commit. If you
+ 3. Do not commit multiple files and dump all messages in one commit. If you
modified several unrelated files, commit each group separately and
provide a nice commit message for each one. See example below.
diff --git a/README.RELEASE_PROCESS b/README.RELEASE_PROCESS
index 227df6e920..3411ed903e 100644
--- a/README.RELEASE_PROCESS
+++ b/README.RELEASE_PROCESS
@@ -17,7 +17,7 @@ It is recommended to do so a couple of days before the packaging day, to
have enough time to investigate failures, communicate with the authors and
commit the fixes.
The RM for the branch is also responsible for keeping the CI green on
-ongoing bases between the releases. Check the CI status for your branch
+ongoing basis between the releases. Check the CI status for your branch
periodically and resolve the failures ASAP. See more in:
https://wiki.php.net/rfc/travis_ci
@@ -31,7 +31,7 @@ team (Bjori) on hand.
6. Verify the tags to be extra sure everything was tagged properly.
-7. Moving extensions from/to PECL requires write acces to the destination.
+7. Moving extensions from/to PECL requires write access to the destination.
Most developers should have this.
Moving extensions from php-src to PECL
@@ -271,8 +271,8 @@ to upgrade.
10. Wait an hour or two, then send a mail to php-announce@lists.php.net,
php-general@lists.php.net and internals@lists.php.net with a text similar to
http://news.php.net/php.internals/17222.
-Please make sure that the mail to php-announce@ is its own completely seperate email.
-This is to make sure that repiles to the announcement on php-general@ or internals@
+Please make sure that the mail to php-announce@ is its own completely separate email.
+This is to make sure that replies to the announcement on php-general@ or internals@
will not accidentally hit the php-announce@ mailinglist.
Re-releasing the same version (or -pl)
@@ -309,6 +309,6 @@ to upgrade.
5. Wait an hour or two, then send a mail to php-announce@lists.php.net,
php-general@lists.php.net and internals@lists.php.net with a text similar to
the news entry.
-Please make sure that the mail to php-announce@ is its own completely seperate email.
-This is to make sure that repiles to the announcement on php-general@ or internals@
+Please make sure that the mail to php-announce@ is its own completely separate email.
+This is to make sure that replies to the announcement on php-general@ or internals@
will not accidentally hit the php-announce@ mailinglist.
diff --git a/README.md b/README.md
index 7618ffe383..074481b1c8 100644
--- a/README.md
+++ b/README.md
@@ -10,7 +10,7 @@ Pull Requests
=============
PHP accepts pull requests via github. Discussions are done on github, but
depending on the topic can also be relayed to the official PHP developer
-mailinglist internals@lists.php.net.
+mailing list internals@lists.php.net.
New features require an RFC and must be accepted by the developers.
See https://wiki.php.net/rfc and https://wiki.php.net/rfc/voting for more
diff --git a/UPGRADING b/UPGRADING
index 46a9323572..215d58e2e3 100644
--- a/UPGRADING
+++ b/UPGRADING
@@ -1,4 +1,4 @@
-PHP 7.0 UPGRADE NOTES
+PHP 7.1 UPGRADE NOTES
1. Backward Incompatible Changes
2. New Features
@@ -19,799 +19,158 @@
1. Backward Incompatible Changes
========================================
-Language changes
-================
-
-Changes to variable handling
-----------------------------
-
-* Indirect variable, property and method references are now interpreted with
- left-to-right semantics. Some examples:
-
- $$foo['bar']['baz'] // interpreted as ($$foo)['bar']['baz']
- $foo->$bar['baz'] // interpreted as ($foo->$bar)['baz']
- $foo->$bar['baz']() // interpreted as ($foo->$bar)['baz']()
- Foo::$bar['baz']() // interpreted as (Foo::$bar)['baz']()
-
- To restore the previous behavior add explicit curly braces:
-
- ${$foo['bar']['baz']}
- $foo->{$bar['baz']}
- $foo->{$bar['baz']}()
- Foo::{$bar['baz']}()
-
-* The global keyword now only accepts simple variables. Instead of
-
- global $$foo->bar;
-
- it is now required to write the following:
-
- global ${$foo->bar};
-
-* Parentheses around variables or function calls no longer have any influence
- on behavior. For example the following code, where the result of a function
- call is passed to a by-reference function
-
- function getArray() { return [1, 2, 3]; }
-
- $last = array_pop(getArray());
- // Strict Standards: Only variables should be passed by reference
- $last = array_pop((getArray()));
- // Strict Standards: Only variables should be passed by reference
-
- will now throw a strict standards error regardless of whether parentheses
- are used. Previously no notice was generated in the second case.
-
-* Array elements or object properties that are automatically created during
- by-reference assignments will now result in a different order. For example
-
- $array = [];
- $array["a"] =& $array["b"];
- $array["b"] = 1;
- var_dump($array);
-
- now results in the array ["a" => 1, "b" => 1], while previously the result
- was ["b" => 1, "a" => 1];
-
-Relevant RFCs:
-* https://wiki.php.net/rfc/uniform_variable_syntax
-* https://wiki.php.net/rfc/abstract_syntax_tree
-
-Changes to list()
------------------
-
-* list() will no longer assign variables in reverse order. For example
-
- list($array[], $array[], $array[]) = [1, 2, 3];
- var_dump($array);
-
- will now result in $array == [1, 2, 3] rather than [3, 2, 1]. Note that only
- the **order** of the assignments changed, but the assigned values stay the
- same. E.g. a normal usage like
-
- list($a, $b, $c) = [1, 2, 3];
- // $a = 1; $b = 2; $c = 3;
-
- will retain its current behavior.
-
-* Empty list() assignments are no longer allowed. As such all of the following
- are invalid:
-
- list() = $a;
- list(,,) = $a;
- list($x, list(), $y) = $a;
-
-* list() no longer supports unpacking strings (while previously this was only
- supported in some cases). The code
-
- $string = "xy";
- list($x, $y) = $string;
-
- will now result in $x == null and $y == null (without notices) instead of
- $x == "x" and $y == "y". Furthermore list() is now always guaranteed to
- work with objects implementing ArrayAccess, e.g.
-
- list($a, $b) = (object) new ArrayObject([0, 1]);
-
- will now result in $a == 0 and $b == 1. Previously both $a and $b were null.
-
-Relevant RFCs:
-* https://wiki.php.net/rfc/abstract_syntax_tree#changes_to_list
-* https://wiki.php.net/rfc/fix_list_behavior_inconsistency
-
-Changes to foreach
-------------------
-
-* Iteration with foreach() no longer has any effect on the internal array
- pointer, which can be accessed through the current()/next()/etc family of
- functions. For example
-
- $array = [0, 1, 2];
- foreach ($array as &$val) {
- var_dump(current($array));
- }
-
- will now print the value int(0) three times. Previously the output was int(1),
- int(2) and bool(false).
-
-* When iterating arrays by-value, foreach will now always operate on a copy of
- the array, as such changes to the array during iteration will not influence
- iteration behavior. For example
-
- $array = [0, 1, 2];
- $ref =& $array; // Necessary to trigger the old behavior
- foreach ($array as $val) {
- var_dump($val);
- unset($array[1]);
- }
-
- will now print all three elements (0 1 2), while previously the second element
- 1 was skipped (0 2).
-
-* When iterating arrays by-reference, modifications to the array will continue
- to influence the iteration. However PHP will now do a better job of
- maintaining a correct position in a number of cases. E.g. appending to an
- array during by-reference iteration
-
- $array = [0];
- foreach ($array as &$val) {
- var_dump($val);
- $array[1] = 1;
- }
-
- will now iterate over the appended element as well. As such the output of this
- example will now be "int(0) int(1)", while previously it was only "int(0)".
-
-* Iteration of plain (non-Traversable) objects by-value or by-reference will
- behave like by-reference iteration of arrays. This matches the previous
- behavior apart from the more accurate position management mentioned in the
- previous point.
-
-* Iteration of Traversable objects remains unchanged.
-
-Relevant RFC: https://wiki.php.net/rfc/php7_foreach
-
-Changes to parameter handling
------------------------------
-
-* It is no longer possible to define two function parameters with the same name.
- For example, the following method will trigger a compile-time error:
-
- public function foo($a, $b, $unused, $unused) {
- // ...
- }
-
- Code like this should be changed to use distinct parameter names, for example:
-
- public function foo($a, $b, $unused1, $unused2) {
- // ...
- }
-
-* The func_get_arg() and func_get_args() functions will no longer return the
- original value that was passed to a parameter and will instead provide the
- current value (which might have been modified). For example
-
- function foo($x) {
- $x++;
- var_dump(func_get_arg(0));
- }
- foo(1);
-
- will now print "2" instead of "1". This code should be changed to either
- perform modifications only after calling func_get_arg(s)
-
- function foo($x) {
- var_dump(func_get_arg(0));
- $x++;
- }
-
- or avoid modifying the parameters altogether:
-
- function foo($x) {
- $newX = $x + 1;
- var_dump(func_get_arg(0));
- }
-
-* Similarly exception backtraces will no longer display the original value that
- was passed to a function and show the modified value instead. For example
-
- function foo($x) {
- $x = 42;
- throw new Exception;
- }
- foo("string");
-
- will now result in the stack trace
-
- Stack trace:
- #0 file.php(4): foo(42)
- #1 {main}
-
- while previously it was:
-
- Stack trace:
- #0 file.php(4): foo('string')
- #1 {main}
-
- While this should not impact runtime behavior of your code, it is worthwhile
- to be aware of this difference for debugging purposes.
-
- The same limitation also applies to debug_backtrace() and other functions
- inspecting function arguments.
-
-Relevant RFC: https://wiki.php.net/phpng
-
-Changes to integer handling
----------------------------
-
-* Invalid octal literals (containing digits larger than 7) now produce compile
- errors. For example, the following is no longer valid:
-
- $i = 0781; // 8 is not a valid octal digit!
-
- Previously the invalid digits (and any following valid digits) were simply
- ignored. As such $i previously held the value 7, because the last two digits
- were silently discarded.
-
-* Bitwise shifts by negative numbers will now throw an ArithmeticError:
-
- var_dump(1 >> -1);
- // ArithmeticError: Bit shift by negative number
-
-* Left bitwise shifts by a number of bits beyond the bit width of an integer
- will always result in 0:
-
- var_dump(1 << 64); // int(0)
-
- Previously the behavior of this code was dependent on the used CPU
- architecture. For example on x86 (including x86-64) the result was int(1),
- because the shift operand was wrapped.
-
-* Similarly right bitwise shifts by a number of bits beyond the bit width of an
- integer will always result in 0 or -1 (depending on sign):
-
- var_dump(1 >> 64); // int(0)
- var_dump(-1 >> 64); // int(-1)
-
-Relevant RFC: https://wiki.php.net/rfc/integer_semantics
-
-Changes to string handling
---------------------------
-
-* Strings that contain hexadecimal numbers are no longer considered to be
- numeric and don't receive special treatment anymore. Some examples of the
- new behavior:
-
- var_dump("0x123" == "291"); // bool(false) (previously true)
- var_dump(is_numeric("0x123")); // bool(false) (previously true)
- var_dump("0xe" + "0x1"); // int(0) (previously 16)
-
- var_dump(substr("foo", "0x1")); // string(3) "foo" (previously "oo")
- // Notice: A non well formed numeric value encountered
-
- filter_var() can be used to check if a string contains a hexadecimal number
- or convert such a string into an integer:
-
- $str = "0xffff";
- $int = filter_var($str, FILTER_VALIDATE_INT, FILTER_FLAG_ALLOW_HEX);
- if (false === $int) {
- throw new Exception("Invalid integer!");
- }
- var_dump($int); // int(65535)
-
-* Due to the addition of the Unicode Codepoint Escape Syntax for double-quoted
- strings and heredocs, "\u{" followed by an invalid sequence will now result in
- an error:
-
- $str = "\u{xyz}"; // Fatal error: Invalid UTF-8 codepoint escape sequence
-
- To avoid this the leading backslash should be escaped:
-
- $str = "\\u{xyz}"; // Works fine
-
- However, "\u" without a following { is unaffected. As such the following code
- won't error and will work the same as before:
-
- $str = "\u202e"; // Works fine
-
-Relevant RFCs:
-* https://wiki.php.net/rfc/remove_hex_support_in_numeric_strings
-* https://wiki.php.net/rfc/unicode_escape
-
-Changes to error handling
--------------------------
-
-* There are now two exception classes: Exception and Error. Both classes
- implement a new interface Throwable. Type hints in exception handling code
- may need to be changed to account for this.
-
-* Some fatal errors and recoverable fatal errors now throw an Error instead.
- As Error is a separate class from Exception, these exceptions will not be
- caught by existing try/catch blocks.
-
- For the recoverable fatal errors which have been converted into an exception,
- it is no longer possible to silently ignore the error from an error handler.
- In particular, it is no longer possible to ignore type hint failures.
-
-* Parser errors now generate a ParseError that extends Error. Error
- handling for eval()s on potentially invalid code should be changed to catch
- ParseError in addition to the previous return value / error_get_last()
- based handling.
-
-* Constructors of internal classes will now always throw an exception on
- failure. Previously some constructors returned NULL or an unusable object.
-
-* The error level of some E_STRICT notices has been changed.
-
-Relevant RFCs:
-* https://wiki.php.net/rfc/engine_exceptions_for_php7
-* https://wiki.php.net/rfc/throwable-interface
-* https://wiki.php.net/rfc/internal_constructor_behaviour
-* https://wiki.php.net/rfc/reclassify_e_strict
-
-Other language changes
-----------------------
-
-* Removed support for static calls to non-static methods from an incompatible
- $this context. In this case $this will not be defined, but the call will be
- allowed with a deprecation notice. An example:
-
- class A {
- public function test() { var_dump($this); }
- }
-
- // Note: Does NOT extend A
- class B {
- public function callNonStaticMethodOfA() { A::test(); }
- }
-
- (new B)->callNonStaticMethodOfA();
-
- // Deprecated: Non-static method A::test() should not be called statically
- // Notice: Undefined variable $this
- NULL
-
- Note that this only applies to calls from an incompatible context. If class B
- extended from A the call would be allowed without any notices.
-
-* It is no longer possible to use the following class, interface and trait names
- (case-insensitive):
-
- bool
- int
- float
- string
- null
- false
- true
-
- This applies to class/interface/trait declarations, class_alias() and use
- statements.
-
- Furthermore the following class, interface and trait names are now reserved
- for future use, but do not yet throw an error when used:
-
- resource
- object
- mixed
- numeric
-
-* The yield language construct no longer requires parentheses when used in an
- expression context. It is now a right-associative operator with precedence
- between the "print" and "=>" operators. This can result in different behavior
- in some cases, for example:
-
- echo yield -1;
- // Was previously interpreted as
- echo (yield) - 1;
- // And is now interpreted as
- echo yield (-1);
-
- yield $foo or die;
- // Was previously interpreted as
- yield ($foo or die);
- // And is now interpreted as
- (yield $foo) or die;
-
- Such cases can always be resolved by adding additional parentheses.
-
- . Removed ASP (<%) and script (<script language=php>) tags.
- (RFC: https://wiki.php.net/rfc/remove_alternative_php_tags)
- . Removed support for assigning the result of new by reference.
- . Removed support for scoped calls to non-static methods from an incompatible
- $this context. See details in https://wiki.php.net/rfc/incompat_ctx.
- . Removed support for #-style comments in ini files. Use ;-style comments
- instead.
- . $HTTP_RAW_POST_DATA is no longer available. Use the php://input stream instead.
-
-Standard library changes
-========================
-
- . substr() now returns an empty string instead of FALSE when the truncation happens on boundaries.
- . call_user_method() and call_user_method_array() no longer exists.
- . ob_start() no longer issues an E_ERROR, but instead an E_RECOVERABLE_ERROR in case an
- output buffer is created in an output buffer handler.
- . The internal sorting algorithm has been improved, what may result in
- different sort order of elements that compare as equal.
- . Removed dl() function on fpm-fcgi.
- . setcookie() with an empty cookie name now issues a WARNING and doesn't send an empty set-cookie header line anymore.
-
-Other
-=====
-
-- Curl:
- . Removed support for disabling the CURLOPT_SAFE_UPLOAD option. All curl file
- uploads must use the curl_file / CURLFile APIs.
-
-- Date:
- . Removed $is_dst parameter from mktime() and gmmktime().
-
-- DBA
- . dba_delete() now returns false if the key was not found for the inifile
- handler, too.
-
-- GMP
- . Requires libgmp version 4.2 or newer now.
- . gmp_setbit() and gmp_clrbit() now return FALSE for negative indices, making
- them consistent with other GMP functions.
-
-- Intl:
- . Removed deprecated aliases datefmt_set_timezone_id() and
- 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().
- . Removed deprecated mcrypt_ecb(), mcrypt_cbc(), mcrypt_cfb() and mcrypt_ofb()
- functions in favor of mcrypt_encrypt() and mcrypt_decrypt() with an
- MCRYPT_MODE_* flag.
-
-- Session
- . session_start() accepts all INI settings as array. e.g. ['cache_limiter'=>'private']
- sets session.cache_limiter=private. It also supports 'read_and_close' which closes
- session data immediately after read data.
- . Save handler accepts validate_sid(), update_timestamp() which validates session
- ID existence, updates timestamp of session data. Compatibility of old user defined
- save handler is retained.
- . SessionUpdateTimestampHandlerInterface is added. validateSid(), updateTimestamp()
- is defined in the interface.
- . session.lazy_write(default=On) INI setting enables only write session data when
- session data is updated.
-
-- Opcache
- . Removed opcache.load_comments configuration directive. Now doc comments
- loading costs nothing and always enabled.
-
-- OpenSSL:
- . Removed the "rsa_key_size" SSL context option in favor of automatically
- setting the appropriate size given the negotiated crypto algorithm.
- . Removed "CN_match" and "SNI_server_name" SSL context options. Use automatic
- detection or the "peer_name" option instead.
-
-- PCRE:
- . Removed support for /e (PREG_REPLACE_EVAL) modifier. Use
- preg_replace_callback() instead.
-
-- PDO_pgsql:
- . Removed PGSQL_ATTR_DISABLE_NATIVE_PREPARED_STATEMENT attribute in favor of
- ATTR_EMULATE_PREPARES.
-
-- Standard:
- . Removed string category support in setlocale(). Use the LC_* constants
- instead.
- . Removed set_magic_quotes_runtime() and its alias magic_quotes_runtime().
+- Core:
+ . 'void' can no longer be used as the name of a class, interface, or trait.
+ This applies to declarations, class_alias() and use statements.
+ . (int), intval() where $base is 10 or unspecified, settype(), decbin(),
+ decoct(), dechex(), integer operators and other conversions now always
+ respect scientific notation in numeric strings.
+ (RFC: https://wiki.php.net/rfc/invalid_strings_in_arithmetic)
+ . The ASCII 0x7F Delete control character is no longer permitted in unquoted
+ identifiers in source code.
+ . The following functions may no longer be called dynamically using $func(),
+ call_user_func(), array_map() or similar:
+ . extract()
+ . compact()
+ . get_defined_vars()
+ . func_get_args()
+ . func_get_arg()
+ . func_num_args()
+ . parse_str() with one argument
+ . mb_parse_str() with one argument
+ . assert() with a string argument
+ (RFC: https://wiki.php.net/rfc/forbid_dynamic_scope_introspection)
+ . If the error_log is set to syslog, the PHP error levels are mapped to the
+ syslog error levels. This brings finer differentiation in the error logs
+ in contrary to the previous approach where all the errors are loggged with
+ the notice level only.
- JSON:
- . Rejected RFC 7159 incompatible number formats in json_decode string -
- top level (07, 0xff, .1, -.1) and all levels ([1.], [1.e1])
- . Calling json_decode with 1st argument equal to empty PHP string or value that
- after casting to string is empty string (NULL, FALSE) results in JSON syntax error.
-
-- Stream:
- . Removed set_socket_blocking() in favor of its alias stream_set_blocking().
-
-- XSL:
- . Removed xsl.security_prefs ini option. Use XsltProcessor::setSecurityPrefs()
- instead.
+ . When calling json_encode with JSON_UNESCAPED_UNICODE option, U+2028 and
+ U+2029 are escaped.
========================================
2. New Features
========================================
-
- Core
- . Added group use declarations.
- (RFC: https://wiki.php.net/rfc/group_use_declarations)
- . Added null coalesce operator (??).
- (RFC: https://wiki.php.net/rfc/isset_ternary)
- . Support for strings with length >= 2^31 bytes in 64 bit builds.
- . Closure::call() method added (works only with userland classes).
- . Added \u{xxxxxx} Unicode Codepoint Escape Syntax for double-quoted strings
- and heredocs.
- . define() now supports arrays as constant values, fixing an oversight where
- define() did not support arrays yet const syntax did.
- . Added the comparison operator (<=>), aka the spaceship operator.
- (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 declarations 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)
-
-- Opcache
- . Added second level file based opcode cache. It may be enabled by setting
- opcache.file_cache=<DIR> configuration directive in php.ini. The second
- level cache may improve performance when SHM is full, at server restart or
- SHM reset. In addition, it's possibe to use file cache without SHM at all,
- using opcache.file_cache_only=1 (this may be useful for sharing hosting),
- and disable file cache consistency check, to speedup loading at the cost of
- safety, using opcache.file_cache_consistency_checks=0.
- . Added ability to move PHP code pages (PHP TEXT segment) into HUGE pages.
- It's possible to enable/disable this feature in php.ini through
- opcache.huge_code_pages=0/1. OS should be configured to provide huge pages.
- . Added Windows only opcache.file_cache_fallback=1 ini option, which implies
- the implemented fallback mechanism. When OPcache was not able to reattach
- the shared memory segment to the desired address and opcache.file_cache
- is on, opcache.file_cache_only=1 will be automatically enforced.
-
-- OpenSSL
- . Added "alpn_protocols" SSL context option allowing encrypted client/server
- streams to negotiate alternative protocols using the ALPN TLS extension when
- built against OpenSSL 1.0.2 or newer. Negotiated protocol information is
- accessible through stream_get_meta_data() output.
-
-- Reflection
- . 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.
-
-- Stream:
- . New Windows only stream context options was added to allow blocking reads
- on pipes. To enable it, pass array("pipe" => array("blocking" => true))
- when creating the stream context. Be aware, that this option can under
- circumstances cause dead locks on the pipe buffer. However it can be useful
- in several CLI use case scenarios.
+ . Added void return type, which requires that a function not return a value.
+ (RFC: https://wiki.php.net/rfc/void_return_type)
+ . String offset access now supports negative references, which will be
+ counted from the end of the string.
+ (RFC: https://wiki.php.net/rfc/negative-string-offsets)
+ . Added a form of the list() construct where keys can be specified.
+ (RFC: https://wiki.php.net/rfc/list_keys)
+ . Added [] = as alternative construct to list() =.
+ (RFC: https://wiki.php.net/rfc/short_list_syntax)
+ . Number operators taking numeric strings now emit "A non well formed numeric
+ value encountered" E_NOTICEs for leading-numeric strings, and "A
+ non-numeric value encountered" E_WARNINGs for non-numeric strings.
+ This always applies to the +, -, *, /, **, %, << and >> operators, and
+ their assignment counterparts +=, -=, *=, /=, **=, %=, <<= and >>=.
+ For the bitwise operators |, & and ^, and their assignment counterparts
+ |=, &= and ^=, this only applies where only one operand is a string.
+ Note that this never applies to the bitwise NOT operator, ~, which does not
+ handle numeric strings, nor to the increment and decrement operators
+ ++ and --, which have a unique approach to handling numeric strings.
+ (RFC: https://wiki.php.net/rfc/invalid_strings_in_arithmetic)
========================================
3. Changes in SAPI modules
========================================
-- FPM
- . Fixed bug #65933 (Cannot specify config lines longer than 1024 bytes).
- . Listen = port now listen on all addresses (IPv6 and IPv4-mapped).
-
========================================
4. Deprecated Functionality
========================================
-- Core
- . PHP 4 style constructors, where the constructor name is the same as the
- class name, are now deprecated.
- . Static calls to non-static methods are now deprecated.
-
-- OpenSSL
- . The "capture_session_meta" SSL context option is now deprecated. Meta
- data concerning active crypto on a stream resource is now accessible
- through the return result from stream_get_meta_data().
-
========================================
5. Changed Functions
========================================
-
-- unserialize():
- . Added second parameter for unserialize function
- (RFC: https://wiki.php.net/rfc/secure_unserialize) allowing to specify
- 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.
- . New Windows only configuration option "blocking_pipes" can be used to
- force blocking reads on child process pipes. This covers several
- edge cases in CLI usage however can lead to dead locks. Also, this
- correlates with the new stream context options for pipes.
-
-- array_column():
- The function now supports an array of objects as well as two-dimensional
- arrays. Only public properties are considered, and objects that make use of
- __get() for dynamic properties must also implement __isset().
-
-- stream_context_create()
- It accepts now a Windows only configuration
- array("pipe" => array("blocking" => <boolean>)) which forces blocking reads
- on pipes. This option should be used carefully because due to the
- platform restrictions dead locks on pipe buffers are possible.
-
-- dirname()
- A new optional argument ($levels) allow to go up various times
- dirname(dirname($foo)) => dirname($foo, 2);
-
-- debug_zval_dump
- It prints now "int" instead of "long", and "float" instead of "double".
+- get_headers() has an extra parameter which allows passing a custom stream
+ context.
+- The first $varname argument for getenv() is no longer mandatory, the
+ current environment variables will be returned as an associative array
+ when omitted.
+- json_encode() accepts new option JSON_UNESCAPED_LINE_TERMINATORS that
+ disables escaping of U+2028 and U+2029 characters when
+ JSON_UNESCAPED_UNICODE is supplied.
+- long2ip() accepts integer as parameter now
+- pg_last_notice() accepts optional long parameter to specify operation.
+ PGSQL_NOTICE_LAST - Get last notice (Default)
+ PGSQL_NOTICE_ALL - Get all stored notices
+ PGSQL_NOTICE_CLEAR - Remove all stored notices
+ It returns empty string or array on successful PGSQL_NOTICE_LAST/ALL calls.
+ It returned FALSE for empty notice previously.
+- pg_fetch_all() accepts 2nd optional result type parameter like
+ pg_fetch_row().
+- pg_select() accepts 4th optional result type parameter like pg_fetch_row().
+- parse_url() is more restrictive now and supports RFC3986.
+- unpack() accepts an additional optional $offset argument. '@' format code
+ (that specifes an absolute position) is applyed to input data after
+ the $offset argument.
+- strpos(), stripos(), substr_count(), grapheme_strpos(), grapheme_stripos(),
+ grapheme_extract(), iconv_strpos(), mb_strimwidth(), mb_ereg_search_setpos(),
+ mb_strpos() and mb_stripos() now accept negative string offsets.
+- substr_count() and mb_strimwidth() additionally also accept negative length.
+- file_get_contents() accepts a negative seek offset if the stream is seekable.
+- tempnam() throws a notice when failing back to the system temp dir.
========================================
6. New Functions
========================================
-- GMP
- . Added gmp_random_seed().
-
-- PCRE:
- . Added preg_replace_callback_array function
- (RFC: https://wiki.php.net/rfc/preg_replace_callback_array)
-
-- Standard
- . Added intdiv() function for integer division.
- . Added error_clear_last() function to reset error state.
-
-- Zip:
- . Added ZipArchive::setCompressionIndex() and ZipArchive::setCompressionName()
- for setting the compression method.
-
-- Zlib:
- . Added deflate_init(), deflate_add(), inflate_init(), inflate_add()
- functions allowing incremental/streaming compression/decompression.
-
========================================
7. New Classes and Interfaces
========================================
-- ReflectionGenerator
-- ReflectionType
-
========================================
8. Removed Extensions and SAPIs
========================================
-- sapi/aolserver
-- sapi/apache
-- sapi/apache_hooks
-- sapi/apache2filter
-- sapi/caudium
-- sapi/continuity
-- sapi/isapi
-- sapi/milter
-- sapi/nsapi
-- sapi/phttpd
-- sapi/pi3web
-- sapi/roxen
-- sapi/thttpd
-- sapi/tux
-- sapi/webjames
-- ext/mssql
-- ext/mysql
-- ext/sybase_ct
-- 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
========================================
-
-- Mhash
- Mhash is not an extension anymore, use function_exists("mhash") to check whether
- it is avaliable.
-
-- GD
- The bundled libgd requires libwebp instead of libvpx for the WebP functionality.
-
-- Openssl
- minimum supported OpenSSL version series was raised to 0.9.8
+- SQLite3:
+ . Upgraded bundled SQLite lib to 3.13.0
========================================
10. New Global Constants
========================================
-- Core
- . PHP_INT_MIN added.
-
-- PCRE
- . This error constant is added to signal errors due to stack size limitations
- when PCRE JIT support is enabled:
- . PREG_JIT_STACKLIMIT_ERROR
-
-- Zlib
- . These constants are added to control flush behavior with the new
- incremental deflate_add() and inflate_add() functions:
- . ZLIB_NO_FLUSH
- . ZLIB_PARTIAL_FLUSH
- . ZLIB_SYNC_FLUSH
- . ZLIB_FULL_FLUSH
- . 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'
+- JSON:
+ . JSON_UNESCAPED_LINE_TERMINATORS
-- Zip
- . Filename encoding flags, as of 7.0.8
- - ZipArchive::FL_ENC_GUESS
- - ZipArchive::FL_ENC_RAW
- - ZipArchive::FL_ENC_STRICT
- - ZipArchive::FL_ENC_UTF_8
- - ZipArchive::FL_ENC_CP437
+- Pgsql:
+ PGSQL_NOTICE_LAST
+ PGSQL_NOTICE_ALL
+ PGSQL_NOTICE_CLEAR
========================================
11. Changes to INI File Handling
========================================
-- Core
- . Removed asp_tags ini directive. Trying to enable it will result in a fatal
- error.
- . Removed always_populate_raw_post_data ini directive.
-
========================================
12. Windows Support
========================================
-- Core
- . Support for native 64 bit integers in 64 bit builds.
- . Support for large files in 64 bit builds.
- . Support for getrusage()
+- Core:
+ . Support for ftok()
-- ftp
- . The ftp extension is always shipped shared
- . For SSL support, the dependency on the openssl extension was abolished. Instead
- it depends alone on the openssl library. If it's present at the compile time,
- ftp_ssl_connect() is enabled automatically.
+- FCGI
+ . PHP_FCGI_CHILDREN is respected. If this environment variable is defined,
+ the first php-fcgi.exe process will exec the specified number of children.
+ Those will share the same TCP socket.
-- imap
- . Static building of ext/imap is disabled
+- readline:
+ . The readline extension is supported through the WinEditLine library
+ (http://mingweditline.sourceforge.net/). Thereby, the interactive CLI
+ shell is supported as well (php.exe -a).
-- odbc
- . The odbc extension is always shipped shared
+ It is well known, but nevertheless is worth mentioning again, that
+ the readline extension is not thread safe and will never be. Thus,
+ the usage of it with any true thread safe SAPI (like Apache mod_winnt) is
+ strongely discouraged.
========================================
13. Other Changes
========================================
-- Core
- . Instead of being undefined and platform-dependent, NaN and Infinity will
- always be zero when cast to integer.
- . Calling a method on a non-object now raises a catchable error instead of a
- fatal error; see: https://wiki.php.net/rfc/catchable-call-to-member-of-non-object
- . Error messages for zend_parse_parameters, type hints and conversions now
- always say "integer" and "float" instead of "long" and "double".
- . Output buffering now continues to work for an aborted connection if
- ignore_user_abort is set to true.
- . Zend Extensions API was extended with zend_extension.op_array_persist_calc()
- and zend_extensions.op_array_persist() handlers. They allow to store (or
- reset) associated with op_array addition information in Opcache Shared
- Memory.
- . zend_internal_function.reserved[] array was introduced to allow association
- of aditional information with internal functions. In PHP-5 it was possible
- to use zend_function.op_array.reserved[] even for internal functions, but
- now we don't allocate extra space.
diff --git a/UPGRADING.INTERNALS b/UPGRADING.INTERNALS
index b263805337..6e30f7cad8 100644
--- a/UPGRADING.INTERNALS
+++ b/UPGRADING.INTERNALS
@@ -1,25 +1,7 @@
-PHP 7.0 INTERNALS UPGRADE NOTES
+PHP 7.1 INTERNALS UPGRADE NOTES
0. Wiki Examples
1. Internal API changes
- e. New data types
- f. zend_parse_parameters() specs
- g. sprintf() formats
- h. HashTable API
- i. New portable macros for large file support
- j. New portable macros for integers
- k. get_class_entry object handler info
- l. get_class_name object handler info
- m. Other portable macros info
- n. ZEND_ENGINE_2 removal
- o. Updated final class modifier
- p. TSRM changes
- q. gc_collect_cycles() is now hookable
- r. PDO uses size_t for lengths
- s. Resource API changes
- t. Optimized strings concatenation.
- u. Streams changes
- v. Sort algorithm changes
2. Build system changes
a. Unix build system changes
@@ -39,219 +21,6 @@ changes. See: https://wiki.php.net/phpng-upgrading
1. Internal API changes
========================
- e. New data types
-
- String
-
- Besides the old way of accepting the strings with 's', the new 'S' ZPP spec
- was introduced. It expects an argument of the type zend_string *. String lengths
- in it are bound to the size_t datatype.
-
- Integer types
-
- Integers do no more depend on the firm 'long' type. Instead a platform
- dependent integer type is used, it is called zend_long. That datatype is
- defined dynamically to guarantee the consistent 64 bit support. The zval
- field representing user land integer it bound to zend_long.
-
- Signed integer is defined as zend_long, unsigned integer as zend_ulong
- inside Zend.
-
- Other datatypes
-
- zend_off_t - portable off_t analogue
- zend_stat_t - portable 'struct stat' analogue
-
- These datatypes are declared to be portable across platforms. Thus, direct
- usage of the functions like fseek, stat, etc. as well as direct usage of
- off_t and struct stat is strongly not recommended. Instead the portable
- macros should be used.
-
- zend_fseek - portable fseek equivalent
- zend_ftell - portable ftell equivalent
- zend_lseek - portable lseek equivalent
- zend_fstat - portable fstat equivalent
- zend_stat - portable stat equivalent
-
- f. zend_parse_parameters() specs
-
- The new spec 'S' introduced, which expects an argument of type zend_string *.
- The 'l' spec expects a parameter of the type zend_long, not long anymore.
- The 's' spec expects parameters of the type char * and size_t, no int anymore.
-
- g. sprintf() formats
-
- New printf modifier 'p' was implemented to platform independently output zend_long,
- zend_ulong and php_size_t datatypes. That modifier can be used with 'd', 'u', 'x' and 'o'
- printf format specs with spprintf, snprintf and the wrapping printf implementations.
- %pu is sufficient for both zend_ulong and php_size_t. the code using %p spec to output
- pointer address might need to be enclosed into #ifdef when it unlickily followed by 'd',
- 'u', 'x' or 'o'.
-
- The only exceptions are the snprintf and zend_sprintf functions yet, because in some cases
- they can use the implemenations available on the system, not the PHP one. With snprintf the
- macros ZEND_LONG_FMT and ZEND_ULONG_FMT should be used.
-
- h. HashTable API
-
- Datatype for array indexes was changed to zend_ulong, for string keys to zend_string *.
-
- i. New portable macros for large file support
-
- Function(s) Alias Comment
- stat, _stat64 zend_stat for use with zend_stat_t
- fstat, _fstat64 zend_fstat for use with zend_stat_t
- lseek, _lseeki64 zend_lseek for use with zend_off_t
- ftell, _ftelli64 zend_ftell for use with zend_off_t
- fseek, _fseeki64 zend_fseek for use with zend_off_t
-
- j. New portable macros for integers
-
- Function(s) Alias Comment
- snprintf with "%ld" or "%lld", _ltoa_s, _i64toa_s ZEND_LTOA for use with zend_long
- atol, atoll, _atoi64 ZEND_ATOL for use with zend_long
- strtol, strtoll, _strtoi64 ZEND_STRTOL for use with zend_long
- strtoul, strtoull, _strtoui64 ZEND_STRTOUL for use with zend_long
- abs, llabs, _abs64 ZEND_ABS for use with zend_long
- - ZEND_LONG_MAX replaces LONG_MAX where appropriate
- - ZEND_LONG_MIN replaces LONG_MIN where appropriate
- - ZEND_ULONG_MAX replaces ULONG_MAX where appropriate
- - SIZEOF_ZEND_LONG reworked SIZEOF_ZEND_LONG representing the size of zend_long datatype
- - ZEND_SIZE_MAX Max value of size_t
- - Z_L casts an integral constant to zend_long
- - Z_UL casts an integral constant to zend_ulong
-
- The macro ZEND_ENABLE_ZVAL_LONG64 reveals whether zval operates on 64 or 32 bit integer.
-
- k. The get_class_entry object handler is no longer available. Instead
- zend_object.ce is always used.
-
- l. The get_class_name object handler is now only used for displaying class
- names in debugging functions like var_dump(). It is no longer used in
- get_class(), get_parent_class() or similar.
-
- The handler is now obligatory, no longer accepts a `parent` argument and
- must return a non-NULL zend_string*, which will be released by the caller.
-
- m. Other portable macros info
-
- ZEND_SECURE_ZERO - zeroes chunk of memory
- ZEND_VALID_SOCKET - validates a php_socket_t variable
-
- ZEND_FASTCALL is defined to use __vectorcall convention on VS2013 and above
- ZEND_NORETURN is defined as __declspec(noreturn) on VS
-
- n. The ZEND_ENGINE_2 macro has been removed. A ZEND_ENGINE_3 macro has been added.
-
- o. Removed ZEND_ACC_FINAL_CLASS in favour of ZEND_ACC_FINAL, turning final class
- modifier now a different class entry flag. Update your extensions.
-
- p. TSRM changes
-
- The TSRM layer undergone significant changes. It is not necessary anymore to
- pass the tsrm_ls pointer explicitly. The TSRMLS_* macros should not be used
- in any new developments.
-
- The recommended way accessing globals in TS builds is by implementing it
- to use a local thread unique pointer to the corresponding data pool. These
- are the new macros that serve to achieve this goal:
-
- - ZEND_TSRMG - based on TSRMG and if static pointer for data pool access is
- implemented, will use it
- - ZEND_TSRMLS_CACHE_EXTERN - to be used in a header shared across multiple
- C/C++ files
- - ZEND_TSRMLS_CACHE_DEFINE - to be used in the main extension C/C++ file
- - ZEND_TSRMLS_CACHE_UPDATE - to be integrated at the top of the globals
- ctor or RINIT function
- - ZEND_ENABLE_STATIC_TSRMLS_CACHE - to be used in the config.[m4|w32] for
- enabling the globals access per thread unique pointer
- - TSRMLS_CACHE - expands to the variable name which links to the thread
- unique data pool
-
- Note that usually it will work without implementing the globals access per
- a thread unique pointer, however it might cause a slowdown. The reason is
- that the access model to the extension/SAPI own globals as well as the
- access to the core globals is determined in the compilation phase depending
- on whether ZEND_ENABLE_STATIC_TSRMLS_CACHE macro is defined.
-
- Due to the current compiler evolution state, the TSRMLS_CACHE pointer
- (when implemented) has to be present and updated in every binary unit which
- is not compiled statically into PHP. So any DLL, DSO, EXE, etc. has to take
- care about defining and updating it's TSRMLS cache. An update should happen
- before any globals was accessed.
-
- Porting an extension or SAPI is usually as easy as removing all the TSRMLS_*
- 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.
-
- When working with CTOR/DTOR for the extension globals, only the data passed
- as parameters should be touched. For example, don't use code like
- free(MYEXT_G(vars)); inside globals destructor, as it possibly can destroy
- the data from a wrong thread. Instead use the pointer to the data delivered
- into the destructor. So the previous example could look like
- free(my_struct->vars); given the income was casted to (struct my_struct *).
-
- A new macro was introduced to simplify the declaration to the extension
- globals. A simplified declaration looks as follows
-
- #define MYEXT_G(v) ZEND_MODULE_GLOBALS_ACCESSOR(myext, v)
-
- Another new macro ZEND_MODULE_GLOBALS_BULK(myext) delivers the corresponding
- globals struct as a whole.
-
- Two new storage specifiers was introduced.
-
- ZEND_EXT_TLS - expand to an appropriate thread specific storage specifier in
- the thread safe build, expands to empty in a non thread safe
- build.
- ZEND_TLS - expands to an appropriate thread specific storage specifier
- with static visibility in the thread safe build, expands to
- static specifier in a non thread safe build.
-
- Variables declared with these storage specifiers can not be shared across
- threads. While ZEND_TLS enforces the local scope visibility, ZEND_EXT_TLS
- pertains to the visiblity across several compilation units. In both cases,
- there's no portable way to achieve the visibility across shared objects.
- Thus, these specifiers are not compatible with ZEND_API and PHPAPI specifiers.
-
- q. gc_collect_cycles() is now a function pointer, and can be replaced in the
- same manner as zend_execute_ex() if needed (for example, to include the
- time spent in the garbage collector in a profiler). The default
- implementation has been renamed to zend_gc_collect_cycles(), and is
- exported with ZEND_API.
-
- r. In accordance with general use of size_t as string length, all PDO API
- functions now use size_t for string length.
-
- s. Removed ZEND_REGISTER/FETCH_RESOURCE, use zend_fetch_resource and
- and zend_register_resource instead, zend_fetch_resource accepts a zend_resource *
- as first argument, if you still need to fetch a resource from zval, use
- 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_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 allocate and construct the resulting string once.
-
- u. Streams changes
- - It is possible to do blocking reads on Windows pipes. It can be done either
- using context stream option PHP_STREAM_OPTION_PIPE_BLOCKING or
- adding STREAM_USE_BLOCKING_PIPE when opening the stream.
-
- v. Sort algorithm changes
- - Improved zend_qsort(using hybrid sorting algo) for better performance,
- and also renamed zend_qsort to zend_sort.
- - Added stable sorting algo zend_insert_sort.
-
- w. Tick functions internal API change
- Tick functions have different declaration now. They expect a single
- parameter while before they were parameterless. When registering the
- tick function a value for this parameter must be passed. This value will be
- provided to the tick function on every execution.
-
========================
2. Build system changes
========================
@@ -260,65 +29,7 @@ changes. See: https://wiki.php.net/phpng-upgrading
b. Windows build system changes
- - Besides Visual Studio, building with Clang or Intel Composer is now
- possible. To enable an alternative toolset, add the option
- --with-toolset=[vs,clang,icc] to the configure line. The default
- toolset is vs. Still clang or icc need the correct environment
- which involves many tools from the vs toolset.
-
- The toolset option is supported by phpize as well.
-
- AWARENESS The only recommended and supported toolset to produce production
- ready binaries is Visual Studio. Still other compilers can be used now for
- testing and analyzing purposes.
-
- - configure.js now produces response files which are passed to the linker
- and library manager. This solves the issues with the long command lines
- which can exceed the OS limit.
-
- - with clang toolset, an option --with-uncritical-warn-choke is available,
- 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 for release builds and can be disabled with the special
- "disable" keyword.
-
========================
3. Module changes
========================
- Session:
-
- - PS_MOD_UPDATE_TIMESTAMP() session save handler definition is added. New
- save handler should use PS_MOD_UPDATE_TIMESTAMP() definition. Save handler
- must not refer/change PS() variables directly from save handler. Use
- parameters.
- - PS_MOD()/PS_MOD_SID() macro exist only for transition. Beware these
- save handlers are less secure and slower than PS_MOD_UPDATE_TIMESTAMP().
- - PS(invalid_session_id) was removed. It was never worked as it supposed.
- To report invalid session ID, use PS_VALIDATE_SID() handler.
- - PS_VALIDATE_SID() defines session ID validation handler. This handler
- must validate if requested session ID exists in session data storage or not.
- 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 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.
- mod_files.c may be used as reference implementation.
-
- Openssl:
- - several ext/openssl functions require the inclusion of the applink shim
- as documented in the OpenSSL FAQ https://www.openssl.org/support/faq.html#PROG2
- to properly work on Windows. While this primarily affects the OpenSSL
- functionality, the shim needs to be included into the executable file
- which loads the extension DLL, not into the extension DLL itself. Alternatively
- it can be compiled and linked with the main program as a separate object file.
- More information and explanation in the linked OpenSSL FAQ.
-
- Thus, this primarily affects any WAMP or other redistributions bundling the
- official PHP release zipballs or building from sources. The applink shim is
- already integrated with all the PHP executables from the official distribution
- starting with 7.0.0beta1.
-
diff --git a/Zend/tests/001.phpt b/Zend/tests/001.phpt
index bec7d8adbc..bbd4ea3ece 100644
--- a/Zend/tests/001.phpt
+++ b/Zend/tests/001.phpt
@@ -17,11 +17,20 @@ function test3($a, $b) {
test1();
test2(1);
-test2();
+try {
+ test2();
+} catch (Throwable $e) {
+ echo "Exception: " . $e->getMessage() . "\n";
+}
+
test3(1,2);
call_user_func("test1");
-call_user_func("test3", 1);
+try {
+ call_user_func("test3", 1);
+} catch (Throwable $e) {
+ echo "Exception: " . $e->getMessage() . "\n";
+}
call_user_func("test3", 1, 2);
class test {
@@ -38,14 +47,10 @@ echo "Done\n";
--EXPECTF--
int(0)
int(1)
-
-Warning: Missing argument 1 for test2(), called in %s on line %d
-int(0)
+Exception: Too few arguments to function test2(), 0 passed in %s001.php on line 18 and exactly 1 expected
int(2)
int(0)
-
-Warning: Missing argument 2 for test3()%s
-int(1)
+Exception: Too few arguments to function test3(), 1 passed in %s001.php on line 27 and exactly 2 expected
int(2)
int(1)
diff --git a/Zend/tests/002.phpt b/Zend/tests/002.phpt
index b01c7fa329..1728330c08 100644
--- a/Zend/tests/002.phpt
+++ b/Zend/tests/002.phpt
@@ -23,11 +23,19 @@ function test3($a, $b) {
test1();
test1(10);
test2(1);
-test2();
+try {
+ test2();
+} catch (Throwable $e) {
+ echo "Exception: " . $e->getMessage() . "\n";
+}
test3(1,2);
call_user_func("test1");
-call_user_func("test3", 1);
+try {
+ call_user_func("test3", 1);
+} catch (Throwable $e) {
+ echo "Exception: " . $e->getMessage() . "\n";
+}
call_user_func("test3", 1, 2);
class test {
@@ -62,14 +70,7 @@ int(1)
Warning: func_get_arg(): Argument 1 not passed to function in %s on line %d
bool(false)
-
-Warning: Missing argument 1 for test2(), called in %s on line %d and defined in %s on line %d
-
-Warning: func_get_arg(): Argument 0 not passed to function in %s on line %d
-bool(false)
-
-Warning: func_get_arg(): Argument 1 not passed to function in %s on line %d
-bool(false)
+Exception: Too few arguments to function test2(), 0 passed in %s002.php on line %d and exactly 1 expected
int(1)
int(2)
@@ -84,15 +85,7 @@ bool(false)
Warning: func_get_arg(): Argument 1 not passed to function in %s on line %d
bool(false)
-
-Warning: Missing argument 2 for test3()%s
-int(1)
-
-Warning: func_get_arg(): Argument 1 not passed to function in %s on line %d
-bool(false)
-
-Warning: func_get_arg(): Argument 2 not passed to function in %s on line %d
-bool(false)
+Exception: Too few arguments to function test3(), 1 passed in %s002.php on line %d and exactly 2 expected
int(1)
int(2)
diff --git a/Zend/tests/003.phpt b/Zend/tests/003.phpt
index 5c3b83d25e..91daa705d3 100644
--- a/Zend/tests/003.phpt
+++ b/Zend/tests/003.phpt
@@ -18,11 +18,19 @@ function test3($a, $b) {
test1();
test1(10);
test2(1);
-test2();
+try {
+ test2();
+} catch (Throwable $e) {
+ echo "Exception: " . $e->getMessage() . "\n";
+}
test3(1,2);
call_user_func("test1");
-call_user_func("test3", 1);
+try {
+ call_user_func("test3", 1);
+} catch (Throwable $e) {
+ echo "Exception: " . $e->getMessage() . "\n";
+}
call_user_func("test3", 1, 2);
class test {
@@ -47,10 +55,7 @@ array(1) {
[0]=>
int(1)
}
-
-Warning: Missing argument 1 for test2(), called in %s on line %d and defined in %s on line %d
-array(0) {
-}
+Exception: Too few arguments to function test2(), 0 passed in %s003.php on line %d and exactly 1 expected
array(2) {
[0]=>
int(1)
@@ -59,12 +64,7 @@ array(2) {
}
array(0) {
}
-
-Warning: Missing argument 2 for test3()%s
-array(1) {
- [0]=>
- int(1)
-}
+Exception: Too few arguments to function test3(), 1 passed in %s003.php on line %d and exactly 2 expected
array(2) {
[0]=>
int(1)
diff --git a/Zend/tests/030.phpt b/Zend/tests/030.phpt
index 8afcb66bd8..2318420319 100644
--- a/Zend/tests/030.phpt
+++ b/Zend/tests/030.phpt
@@ -31,40 +31,4 @@ $test->bar();
?>
--EXPECTF--
-object(Exception)#%d (7) {
- ["message":protected]=>
- string(3) "foo"
- ["string":"Exception":private]=>
- string(0) ""
- ["code":protected]=>
- int(0)
- ["file":protected]=>
- string(%d) "%s030.php"
- ["line":protected]=>
- int(%d)
- ["trace":"Exception":private]=>
- array(1) {
- [0]=>
- array(6) {
- ["file"]=>
- string(%d) "%s030.php"
- ["line"]=>
- int(%d)
- ["function"]=>
- string(3) "bar"
- ["class"]=>
- string(3) "foo"
- ["type"]=>
- string(2) "->"
- ["args"]=>
- array(0) {
- }
- }
- }
- ["previous":"Exception":private]=>
- NULL
-}
-'test' => '0'
-'test_2' => '1'
-'test_3' => '2'
-ok
+Fatal error: Cannot re-assign $this in %s030.php on line 11
diff --git a/Zend/tests/add_006.phpt b/Zend/tests/add_006.phpt
index d56df2f329..fe1c0830e2 100644
--- a/Zend/tests/add_006.phpt
+++ b/Zend/tests/add_006.phpt
@@ -38,11 +38,19 @@ var_dump($c);
echo "Done\n";
?>
--EXPECTF--
+
+Warning: A non-numeric value encountered in %s on line %d
int(75636)
+
+Notice: A non well formed numeric value encountered in %s on line %d
int(951858)
int(48550510)
float(75661.68)
+
+Warning: A non-numeric value encountered in %s on line %d
int(75636)
+
+Notice: A non well formed numeric value encountered in %s on line %d
int(951858)
int(48550510)
float(75661.68)
diff --git a/Zend/tests/add_007.phpt b/Zend/tests/add_007.phpt
index 66f5405706..089b24ae0b 100644
--- a/Zend/tests/add_007.phpt
+++ b/Zend/tests/add_007.phpt
@@ -19,8 +19,13 @@ var_dump($c);
echo "Done\n";
?>
--EXPECTF--
+
+Warning: A non-numeric value encountered in %s on line %d
+
Exception: Unsupported operand types
+Warning: A non-numeric value encountered in %s on line %d
+
Fatal error: Uncaught Error: Unsupported operand types in %s:%d
Stack trace:
#0 {main}
diff --git a/Zend/tests/anon/013.phpt b/Zend/tests/anon/013.phpt
new file mode 100644
index 0000000000..72ba3d61b7
--- /dev/null
+++ b/Zend/tests/anon/013.phpt
@@ -0,0 +1,15 @@
+--TEST--
+closure binding to anonymous class
+--FILE--
+<?php
+$class = new class {};
+$foo = function() {
+ return $this;
+};
+
+$closure = Closure::bind($foo, $class, $class);
+var_dump($closure());
+?>
+--EXPECTF--
+object(class@anonymous)#1 (0) {
+}
diff --git a/Zend/tests/anon/014.phpt b/Zend/tests/anon/014.phpt
new file mode 100644
index 0000000000..cacac47857
--- /dev/null
+++ b/Zend/tests/anon/014.phpt
@@ -0,0 +1,16 @@
+--TEST--
+anonymous class trait binding
+--FILE--
+<?php
+trait TaskTrait {
+ function run() {
+ return 'Running...';
+ }
+}
+$class = new class() {
+ use TaskTrait;
+};
+var_dump($class->run());
+?>
+--EXPECTF--
+string(10) "Running..."
diff --git a/Zend/tests/assert/expect_015.phpt b/Zend/tests/assert/expect_015.phpt
index 030913f7f9..0c53c75c4d 100644
--- a/Zend/tests/assert/expect_015.phpt
+++ b/Zend/tests/assert/expect_015.phpt
@@ -154,7 +154,7 @@ Warning: assert(): assert(0 && ($a = function () {
$x = $a ? $b : $c;
$x = $a ?: $c;
$x = $a ?? $b;
- list($a, $b, $c) = [1, 2 => 'x', 'z' => 'c'];
+ [$a, $b, $c] = [1, 2 => 'x', 'z' => 'c'];
@foo();
$y = clone $x;
yield 1 => 2;
diff --git a/Zend/tests/assert/indirect_var_access_misoptimization.phpt b/Zend/tests/assert/indirect_var_access_misoptimization.phpt
new file mode 100644
index 0000000000..61c193ab60
--- /dev/null
+++ b/Zend/tests/assert/indirect_var_access_misoptimization.phpt
@@ -0,0 +1,19 @@
+--TEST--
+Misoptimization when variable is modified by assert()
+--INI--
+zend.assertions=1
+--FILE--
+<?php
+
+function test() {
+ $i = 0;
+ assert('$i = new stdClass');
+ $i += 1;
+ var_dump($i);
+}
+test();
+
+?>
+--EXPECTF--
+Notice: Object of class stdClass could not be converted to int in %s on line %d
+int(2)
diff --git a/Zend/tests/bug29015.phpt b/Zend/tests/bug29015.phpt
index a36ed923f3..d4231d10b1 100644
--- a/Zend/tests/bug29015.phpt
+++ b/Zend/tests/bug29015.phpt
@@ -6,9 +6,16 @@ $a = new stdClass();
$x = "";
$a->$x = "string('')";
var_dump($a);
+$a->{"\0"} = 42;
+var_dump($a);
?>
--EXPECTF--
-Fatal error: Uncaught Error: Cannot access empty property in %sbug29015.php:4
+object(stdClass)#1 (1) {
+ [""]=>
+ string(10) "string('')"
+}
+
+Fatal error: Uncaught Error: Cannot access property started with '\0' in %s:%d
Stack trace:
#0 {main}
- thrown in %sbug29015.php on line 4
+ thrown in %s on line %d
diff --git a/Zend/tests/bug29883.phpt b/Zend/tests/bug29883.phpt
index c92f147ff7..b6ad99aeaf 100644
--- a/Zend/tests/bug29883.phpt
+++ b/Zend/tests/bug29883.phpt
@@ -3,7 +3,7 @@ Bug #29883 (isset gives invalid values on strings)
--FILE--
<?php
$x = "bug";
-var_dump(isset($x[-1]));
+var_dump(isset($x[-10]));
var_dump(isset($x["1"]));
echo $x["1"]."\n";
?>
diff --git a/Zend/tests/bug31098.phpt b/Zend/tests/bug31098.phpt
index 23cec9bbf4..31823a1aa5 100644
--- a/Zend/tests/bug31098.phpt
+++ b/Zend/tests/bug31098.phpt
@@ -18,7 +18,7 @@ var_dump(isset($a['b']));
$simpleString = "Bogus String Text";
echo isset($simpleString->wrong)?"bug\n":"ok\n";
echo isset($simpleString["wrong"])?"bug\n":"ok\n";
-echo isset($simpleString[-1])?"bug\n":"ok\n";
+echo isset($simpleString[-20])?"bug\n":"ok\n";
echo isset($simpleString[0])?"ok\n":"bug\n";
echo isset($simpleString["0"])?"ok\n":"bug\n";
echo isset($simpleString["16"])?"ok\n":"bug\n";
diff --git a/Zend/tests/bug33996.phpt b/Zend/tests/bug33996.phpt
index c399ee9975..3936eb8845 100644
--- a/Zend/tests/bug33996.phpt
+++ b/Zend/tests/bug33996.phpt
@@ -19,15 +19,19 @@ function NormalTest($a)
echo "Hi!";
}
-NormalTest();
-FooTest();
+try {
+ NormalTest();
+} catch (Throwable $e) {
+ echo "Exception: " . $e->getMessage() . "\n";
+}
+try {
+ FooTest();
+} catch (Throwable $e) {
+ echo "Exception: " . $e->getMessage() . "\n";
+}
FooTest(new Foo());
?>
--EXPECTF--
-Warning: Missing argument 1 for NormalTest(), called in %sbug33996.php on line %d and defined in %sbug33996.php on line %d
-Hi!
-Fatal error: Uncaught TypeError: Argument 1 passed to FooTest() must be an instance of Foo, none given, called in %sbug33996.php on line %d and defined in %sbug33996.php:%d
-Stack trace:
-#0 %s(%d): FooTest()
-#1 {main}
- thrown in %sbug33996.php on line %d
+Exception: Too few arguments to function NormalTest(), 0 passed in %sbug33996.php on line 18 and exactly 1 expected
+Exception: Too few arguments to function FooTest(), 0 passed in %sbug33996.php on line 23 and exactly 1 expected
+Hello!
diff --git a/Zend/tests/bug38047.phpt b/Zend/tests/bug38047.phpt
index 5e7b3efc94..09739d23eb 100644
--- a/Zend/tests/bug38047.phpt
+++ b/Zend/tests/bug38047.phpt
@@ -43,7 +43,9 @@ Non-static method A::A_ftk() should not be called statically
1 %sbug38047.php:13 get_error_context()
2 %sbug38047.php:36 kalus_error_handler()
-Missing argument 1 for A::A_ftk(), called in %sbug38047.php on line 36 and defined
-1 %sbug38047.php:13 get_error_context()
-2 %sbug38047.php:7 kalus_error_handler()
-3 %sbug38047.php:36 A_ftk()
+
+Fatal error: Uncaught Error: Too few arguments to function A::A_ftk(), 0 passed in %sbug38047.php on line 36 and exactly 1 expected in %sbug38047.php:7
+Stack trace:
+#0 %sbug38047.php(36): A::A_ftk()
+#1 {main}
+ thrown in %sbug38047.php on line 7
diff --git a/Zend/tests/bug41813.phpt b/Zend/tests/bug41813.phpt
index 0bb693075a..9a609d09c8 100644
--- a/Zend/tests/bug41813.phpt
+++ b/Zend/tests/bug41813.phpt
@@ -9,7 +9,7 @@ $foo[0]->bar = "xyz";
echo "Done\n";
?>
--EXPECTF--
-Fatal error: Uncaught Error: Cannot use string offset as an array in %s:%d
+Fatal error: Uncaught Error: Cannot use string offset as an object in %s:%d
Stack trace:
#0 {main}
thrown in %s on line %d
diff --git a/Zend/tests/bug49866.phpt b/Zend/tests/bug49866.phpt
index 3d96a59cd5..0b7c224c01 100644
--- a/Zend/tests/bug49866.phpt
+++ b/Zend/tests/bug49866.phpt
@@ -7,7 +7,7 @@ $b = &$a[1];
$b = "f";
echo $a;
--EXPECTF--
-Fatal error: Uncaught Error: Cannot create references to/from string offsets nor overloaded objects in %sbug49866.php:3
+Fatal error: Uncaught Error: Cannot create references to/from string offsets in %sbug49866.php:3
Stack trace:
#0 {main}
thrown in %sbug49866.php on line 3
diff --git a/Zend/tests/bug52484.phpt b/Zend/tests/bug52484.phpt
index 053529614d..1d2c2d7cdc 100644
--- a/Zend/tests/bug52484.phpt
+++ b/Zend/tests/bug52484.phpt
@@ -10,14 +10,14 @@ class A {
}
$a = new A();
-$prop = null;
+$prop = "\0";
unset($a->$prop);
?>
--EXPECTF--
-Fatal error: Uncaught Error: Cannot access empty property in %s:%d
+Fatal error: Uncaught Error: Cannot access property started with '\0' in %s:%d
Stack trace:
-#0 %s(%d): A->__unset('')
+#0 %s(%d): A->__unset('\x00')
#1 {main}
thrown in %s on line %d
diff --git a/Zend/tests/bug52484_2.phpt b/Zend/tests/bug52484_2.phpt
index 6bb927535c..3b12950c66 100644
--- a/Zend/tests/bug52484_2.phpt
+++ b/Zend/tests/bug52484_2.phpt
@@ -10,14 +10,14 @@ class A {
}
$a = new A();
-$prop = null;
+$prop = "\0";
$a->$prop = 2;
?>
--EXPECTF--
-Fatal error: Uncaught Error: Cannot access empty property in %s:%d
+Fatal error: Uncaught Error: Cannot access property started with '\0' in %s:%d
Stack trace:
-#0 %s(%d): A->__set('', 2)
+#0 %s(%d): A->__set('\x00', 2)
#1 {main}
thrown in %s on line %d
diff --git a/Zend/tests/bug52484_3.phpt b/Zend/tests/bug52484_3.phpt
index af32bc9be7..408dd453fd 100644
--- a/Zend/tests/bug52484_3.phpt
+++ b/Zend/tests/bug52484_3.phpt
@@ -10,14 +10,14 @@ class A {
}
$a = new A();
-$prop = null;
+$prop = "\0";
var_dump($a->$prop);
?>
--EXPECTF--
-Fatal error: Uncaught Error: Cannot access empty property in %s:%d
+Fatal error: Uncaught Error: Cannot access property started with '\0' in %s:%d
Stack trace:
-#0 %s(%d): A->__get('')
+#0 %s(%d): A->__get('\x00')
#1 {main}
thrown in %s on line %d
diff --git a/Zend/tests/bug52879.phpt b/Zend/tests/bug52879.phpt
index 0193be4b45..6c3232f32d 100644
--- a/Zend/tests/bug52879.phpt
+++ b/Zend/tests/bug52879.phpt
@@ -8,7 +8,7 @@ class MyClass {
$this->myRef = $value;
}
}
-$myGlobal=new MyClass($myGlobal);
+$myGlobal=new MyClass();
$myGlobal->myRef=&$myGlobal;
$myGlobal->myNonExistentProperty="ok\n";
echo $myGlobal;
diff --git a/Zend/tests/bug53432.phpt b/Zend/tests/bug53432.phpt
new file mode 100644
index 0000000000..83df599313
--- /dev/null
+++ b/Zend/tests/bug53432.phpt
@@ -0,0 +1,65 @@
+--TEST--
+Bug #53432: Assignment via string index access on an empty string converts to array
+--FILE--
+<?php
+
+$str = '';
+var_dump($str[0] = 'a');
+var_dump($str);
+
+$str = '';
+var_dump($str[5] = 'a');
+var_dump($str);
+
+$str = '';
+var_dump($str[-1] = 'a');
+var_dump($str);
+
+$str = '';
+var_dump($str['foo'] = 'a');
+var_dump($str);
+
+$str = '';
+try {
+ var_dump($str[] = 'a');
+} catch (Error $e) {
+ echo "Error: {$e->getMessage()}\n";
+}
+var_dump($str);
+
+$str = '';
+try {
+ var_dump($str[0] += 1);
+} catch (Error $e) {
+ echo "Error: {$e->getMessage()}\n";
+}
+var_dump($str);
+
+$str = '';
+try {
+ var_dump($str[0][0] = 'a');
+} catch (Error $e) {
+ echo "Error: {$e->getMessage()}\n";
+}
+var_dump($str);
+
+?>
+--EXPECTF--
+string(1) "a"
+string(1) "a"
+string(1) "a"
+string(6) " a"
+
+Warning: Illegal string offset: -1 in %s on line %d
+NULL
+string(0) ""
+
+Warning: Illegal string offset 'foo' in %s on line %d
+string(1) "a"
+string(1) "a"
+Error: [] operator not supported for strings
+string(0) ""
+Error: Cannot use assign-op operators with string offsets
+string(0) ""
+Error: Cannot use string offset as an array
+string(0) ""
diff --git a/Zend/tests/bug55705.phpt b/Zend/tests/bug55705.phpt
index f051bca6dc..fc17139344 100644
--- a/Zend/tests/bug55705.phpt
+++ b/Zend/tests/bug55705.phpt
@@ -6,7 +6,7 @@ function f(callable $c) {}
f();
?>
--EXPECTF--
-Fatal error: Uncaught TypeError: Argument 1 passed to f() must be callable, none given, called in %s on line 3 and defined in %s:%d
+Fatal error: Uncaught Error: Too few arguments to function f(), 0 passed in %s on line 3 and exactly 1 expected in %s:2
Stack trace:
#0 %s(%d): f()
#1 {main}
diff --git a/Zend/tests/bug62814.phpt b/Zend/tests/bug62814.phpt
new file mode 100644
index 0000000000..6646aa283f
--- /dev/null
+++ b/Zend/tests/bug62814.phpt
@@ -0,0 +1,20 @@
+--TEST--
+Bug #62814: It is possible to stiffen child class members visibility
+--FILE--
+<?php
+
+class A {
+ private function test() { }
+}
+
+class B extends A {
+ protected function test() { }
+}
+
+class C extends B {
+ private function test() { }
+}
+
+?>
+--EXPECTF--
+Fatal error: Access level to C::test() must be protected (as in class B) or weaker in %s on line %d
diff --git a/Zend/tests/bug65784.phpt b/Zend/tests/bug65784.phpt
index c079b3d282..663ea95761 100644
--- a/Zend/tests/bug65784.phpt
+++ b/Zend/tests/bug65784.phpt
@@ -57,8 +57,13 @@ $bar = foo3();
string(9) "not catch"
NULL
-Fatal error: Uncaught Error: Class 'NotExists' not found in %sbug65784.php:%d
+Fatal error: Uncaught Exception: not catched in %sbug65784.php:42
Stack trace:
-#0 %s(%d): foo3()
+#0 %sbug65784.php(52): foo3()
#1 {main}
- thrown in %sbug65784.php on line %d
+
+Next Error: Class 'NotExists' not found in %sbug65784.php:46
+Stack trace:
+#0 %sbug65784.php(52): foo3()
+#1 {main}
+ thrown in %sbug65784.php on line 46
diff --git a/Zend/tests/bug68370.phpt b/Zend/tests/bug68370.phpt
index 25589bf455..73411ca9b9 100644
--- a/Zend/tests/bug68370.phpt
+++ b/Zend/tests/bug68370.phpt
@@ -13,6 +13,4 @@ $x = $c->test();
print_r($x);
unset($c, $x);
--EXPECTF--
-Array
-(
-)
+Fatal error: Cannot unset $this in %sbug68370.php on line 4
diff --git a/Zend/tests/bug68652.phpt b/Zend/tests/bug68652.phpt
index e86312ba63..8e54af2e34 100644
--- a/Zend/tests/bug68652.phpt
+++ b/Zend/tests/bug68652.phpt
@@ -28,6 +28,7 @@ class Bar {
}
public function __destruct() {
+ if (!isset(self::$instance)) return;
Foo::getInstance();
}
}
diff --git a/Zend/tests/bug69989_2.phpt b/Zend/tests/bug69989_2.phpt
new file mode 100644
index 0000000000..a6f320da3b
--- /dev/null
+++ b/Zend/tests/bug69989_2.phpt
@@ -0,0 +1,42 @@
+--TEST--
+Collection of some cycles on unfinished generators
+--FILE--
+<?php
+
+// CV
+function gen1() {
+ $gen = yield;
+ yield;
+}
+
+$gen = gen1();
+$gen->send($gen);
+
+// This
+class Test {
+ public $gen;
+ public function gen2() {
+ yield;
+ }
+}
+
+$test = new Test;
+$test->gen = $test->gen2();
+
+// Closure object
+$gen3 = (function() use (&$gen3) {
+ yield;
+})();
+
+// Yield from array
+function gen4() {
+ yield from [yield];
+}
+
+$gen = gen4();
+$gen->send($gen);
+
+?>
+===DONE===
+--EXPECT--
+===DONE===
diff --git a/Zend/tests/bug69989_3.phpt b/Zend/tests/bug69989_3.phpt
new file mode 100644
index 0000000000..260819197b
--- /dev/null
+++ b/Zend/tests/bug69989_3.phpt
@@ -0,0 +1,44 @@
+--TEST--
+Generator cycle collection edge cases
+--FILE--
+<?php
+
+// Extra args
+function gen1() {
+ yield;
+}
+$obj = new stdClass;
+$obj->gen = gen1($obj);
+
+// Symtable
+function gen2() {
+ $varName = 'a';
+ $$varName = yield;
+ yield;
+}
+$gen = gen2();
+$gen->send($gen);
+
+// Symtable indirect
+function gen3() {
+ $varName = 'a';
+ $$varName = 42;
+ $var = yield;
+ yield;
+}
+$gen = gen3();
+$gen->send($gen);
+
+// Yield from root
+function gen4() {
+ yield from yield;
+}
+$gen = gen4();
+$gen2 = gen4($gen);
+$gen2->send([1, 2, 3]);
+$gen->send($gen2);
+
+?>
+===DONE===
+--EXPECT--
+===DONE===
diff --git a/Zend/tests/bug70089.phpt b/Zend/tests/bug70089.phpt
index c61db00c9e..e1884d9dac 100644
--- a/Zend/tests/bug70089.phpt
+++ b/Zend/tests/bug70089.phpt
@@ -34,4 +34,4 @@ try {
string(36) "Cannot use string offset as an array"
string(27) "Cannot unset string offsets"
string(41) "Only variables can be passed by reference"
-string(64) "Cannot increment/decrement overloaded objects nor string offsets"
+string(41) "Cannot increment/decrement string offsets"
diff --git a/Zend/tests/bug70685.phpt b/Zend/tests/bug70685.phpt
index 7a49ff1825..8ae97f1bf0 100644
--- a/Zend/tests/bug70685.phpt
+++ b/Zend/tests/bug70685.phpt
@@ -15,7 +15,7 @@ var_dump($c);
?>
--EXPECTF--
-Warning: Cannot bind internal method SplDoublyLinkedList::count() to object of class cls in %s on line %d
+Warning: Cannot bind method SplDoublyLinkedList::count() to object of class cls in %s on line %d
NULL
Warning: Cannot rebind scope of closure created by ReflectionFunctionAbstract::getClosure() in %s on line %d
diff --git a/Zend/tests/bug70689.phpt b/Zend/tests/bug70689.phpt
index e3feeed9b0..286b6f4225 100644
--- a/Zend/tests/bug70689.phpt
+++ b/Zend/tests/bug70689.phpt
@@ -19,4 +19,8 @@ try {
?>
--EXPECTF--
-Missing argument 1 for foo(), called in %sbug70689.php on line %d and defined
+Fatal error: Uncaught Error: Too few arguments to function foo(), 0 passed in %sbug70689.php on line 12 and exactly 1 expected in %sbug70689.php:3
+Stack trace:
+#0 %sbug70689.php(12): foo()
+#1 {main}
+ thrown in %sbug70689.php on line 3
diff --git a/Zend/tests/bug70918.phpt b/Zend/tests/bug70918.phpt
new file mode 100644
index 0000000000..81e2192d8a
--- /dev/null
+++ b/Zend/tests/bug70918.phpt
@@ -0,0 +1,47 @@
+--TEST--
+Bug #70918 (Segfault using static outside of class scope)
+--FILE--
+<?php
+try {
+ static::x;
+} catch (Error $e) {
+ var_dump($e->getMessage());
+}
+
+try {
+ parent::x;
+} catch (Error $e) {
+ var_dump($e->getMessage());
+}
+
+try {
+ self::x;
+} catch (Error $e) {
+ var_dump($e->getMessage());
+}
+
+try {
+ new static;
+} catch (Error $e) {
+ var_dump($e->getMessage());
+}
+
+try {
+ static::x();
+} catch (Error $e) {
+ var_dump($e->getMessage());
+}
+
+try {
+ static::$i;
+} catch (Error $e) {
+ var_dump($e->getMessage());
+}
+?>
+--EXPECT--
+string(52) "Cannot access static:: when no class scope is active"
+string(52) "Cannot access parent:: when no class scope is active"
+string(50) "Cannot access self:: when no class scope is active"
+string(52) "Cannot access static:: when no class scope is active"
+string(52) "Cannot access static:: when no class scope is active"
+string(52) "Cannot access static:: when no class scope is active"
diff --git a/Zend/tests/bug71196.phpt b/Zend/tests/bug71196.phpt
new file mode 100644
index 0000000000..ca25f9f4fc
--- /dev/null
+++ b/Zend/tests/bug71196.phpt
@@ -0,0 +1,13 @@
+--TEST--
+Bug #71196 (Memory leak with out-of-order live ranges)
+--FILE--
+<?php
+try {
+ $a = "1";
+ [1, (y().$a.$a) . ($a.$a)];
+} catch (Error $e) {
+ var_dump($e->getMessage());
+}
+?>
+--EXPECT--
+string(30) "Call to undefined function y()"
diff --git a/Zend/tests/bug71266.phpt b/Zend/tests/bug71266.phpt
new file mode 100644
index 0000000000..d67c6f6363
--- /dev/null
+++ b/Zend/tests/bug71266.phpt
@@ -0,0 +1,22 @@
+--TEST--
+Bug #71266 (Missing separation of properties HT in foreach etc)
+--FILE--
+<?php
+$one = 1;
+$two = 2;
+$arr = ['foo' => $one, 'bar' => $two];
+$obj = (object) $arr;
+foreach ($obj as $val) {
+ var_dump($val);
+ $obj->bar = 42;
+}
+
+$arr = ['foo' => $one, 'bar' => $two];
+$obj = (object) $arr;
+next($obj);
+var_dump(current($arr));
+?>
+--EXPECT--
+int(1)
+int(42)
+int(1)
diff --git a/Zend/tests/bug71428.1.phpt b/Zend/tests/bug71428.1.phpt
index fbf342380f..e4d3a22f67 100644
--- a/Zend/tests/bug71428.1.phpt
+++ b/Zend/tests/bug71428.1.phpt
@@ -1,7 +1,5 @@
--TEST--
bug #71428.1: inheritance with null default values
---XFAIL--
-This is a BC break
--FILE--
<?php
class A {
@@ -11,5 +9,5 @@ class B extends A {
public function m(array $a = []) {}
}
--EXPECTF--
-Warning: Declaration of B::m(array $a = Array) should be compatible with A::m(array $a = NULL) in %sbug71428.1.php on line 7
+Warning: Declaration of B::m(array $a = Array) should be compatible with A::m(?array $a = NULL) in %sbug71428.1.php on line 7
diff --git a/Zend/tests/bug71428.3.phpt b/Zend/tests/bug71428.3.phpt
index 65d397052e..558e87c56e 100644
--- a/Zend/tests/bug71428.3.phpt
+++ b/Zend/tests/bug71428.3.phpt
@@ -1,7 +1,5 @@
--TEST--
bug #71428: Validation type inheritance with = NULL
---XFAIL--
-This is a BC break
--FILE--
<?php
class A { }
@@ -9,5 +7,5 @@ class B { public function m(A $a = NULL, $n) { echo "B.m";} };
class C extends B { public function m(A $a , $n) { echo "C.m";} };
?>
--EXPECTF--
-Warning: Declaration of C::m(A $a, $n) should be compatible with B::m(A $a = NULL, $n) in %sbug71428.3.php on line 4
+Warning: Declaration of C::m(A $a, $n) should be compatible with B::m(?A $a, $n) in %sbug71428.3.php on line 4
diff --git a/Zend/tests/bug71572.phpt b/Zend/tests/bug71572.phpt
new file mode 100644
index 0000000000..4a823ec72f
--- /dev/null
+++ b/Zend/tests/bug71572.phpt
@@ -0,0 +1,27 @@
+--TEST--
+Bug #71572: String offset assignment from an empty string inserts null byte
+--FILE--
+<?php
+
+$str = "abc";
+var_dump($str{0} = "");
+var_dump($str{1} = "");
+var_dump($str{3} = "");
+var_dump($str{10} = "");
+var_dump($str);
+?>
+==DONE==
+--EXPECTF--
+Warning: Cannot assign an empty string to a string offset in %s on line %d
+NULL
+
+Warning: Cannot assign an empty string to a string offset in %s on line %d
+NULL
+
+Warning: Cannot assign an empty string to a string offset in %s on line %d
+NULL
+
+Warning: Cannot assign an empty string to a string offset in %s on line %d
+NULL
+string(3) "abc"
+==DONE== \ No newline at end of file
diff --git a/Zend/tests/bug72107.phpt b/Zend/tests/bug72107.phpt
new file mode 100644
index 0000000000..3f4c46cf70
--- /dev/null
+++ b/Zend/tests/bug72107.phpt
@@ -0,0 +1,14 @@
+--TEST--
+Bug #72107: Segfault when using func_get_args as error handler
+--FILE--
+<?php
+set_error_handler('func_get_args');
+function test($a) {
+ echo $undef;
+}
+test(1);
+?>
+--EXPECTF--
+Warning: Cannot call func_get_args() dynamically in %s on line %d
+
+Notice: Undefined variable: undef in %s on line %d
diff --git a/Zend/tests/bug72119.phpt b/Zend/tests/bug72119.phpt
index b8f070a25a..064381ada0 100644
--- a/Zend/tests/bug72119.phpt
+++ b/Zend/tests/bug72119.phpt
@@ -14,5 +14,6 @@ class Hello implements Foo {
}
echo "OK\n";
?>
---EXPECT--
-OK
+--EXPECTF--
+Fatal error: Declaration of Hello::bar(array $baz = Array) must be compatible with Foo::bar(?array $baz = NULL) in %s on line %d
+
diff --git a/Zend/tests/bug72177.phpt b/Zend/tests/bug72177.phpt
new file mode 100644
index 0000000000..b5658d354a
--- /dev/null
+++ b/Zend/tests/bug72177.phpt
@@ -0,0 +1,35 @@
+--TEST--
+Bug #72177 Scope issue in __destruct after ReflectionProperty::setValue()
+--FILE--
+<?php
+class Child
+{
+ protected $bar;
+
+ public function __destruct()
+ {
+ $this->bar = null;
+ }
+}
+
+class Parnt
+{
+ protected $child;
+
+ public function doSomething()
+ {
+ $this->child = new Child();
+
+ $prop = new \ReflectionProperty($this, 'child');
+ $prop->setAccessible(true);
+ $prop->setValue($this, null);
+ }
+}
+
+$p = new Parnt();
+$p->doSomething();
+
+echo "OK\n";
+?>
+--EXPECT--
+OK
diff --git a/Zend/tests/bug72177_2.phpt b/Zend/tests/bug72177_2.phpt
new file mode 100644
index 0000000000..718d6c061e
--- /dev/null
+++ b/Zend/tests/bug72177_2.phpt
@@ -0,0 +1,34 @@
+--TEST--
+Bug #72177 Scope issue in __destruct after ReflectionProperty::setValue()
+--FILE--
+<?php
+class Foo
+{
+ private $bar = 'bar';
+
+ public function __construct()
+ {
+ unset($this->bar);
+ }
+}
+
+class Bar extends Foo
+{
+ private $baz = 'baz';
+ private static $tab = 'tab';
+
+ public function __get(string $name)
+ {
+ var_dump($this->baz);
+ var_dump(self::$tab);
+ return $name;
+ }
+}
+
+$r = new ReflectionProperty(Foo::class, 'bar');
+
+$r->setAccessible(true);
+echo "OK\n";
+?>
+--EXPECT--
+OK
diff --git a/Zend/tests/bug72188.phpt b/Zend/tests/bug72188.phpt
new file mode 100644
index 0000000000..0deab6b81b
--- /dev/null
+++ b/Zend/tests/bug72188.phpt
@@ -0,0 +1,27 @@
+--TEST--
+Bug #72188 (Nested try/finally blocks losing return value)
+--FILE--
+<?php
+function test() {
+ try {
+ return 5;
+ } finally {
+ try {
+ echo 1;
+ } finally {
+ echo 2;
+ }
+ }
+}
+
+
+
+$a = test();
+if($a !== 5) {
+ echo "FAILED: expected 5, received ", var_export($a), PHP_EOL;
+} else {
+ echo "Passed", PHP_EOL;
+}
+?>
+--EXPECT--
+12Passed
diff --git a/Zend/tests/bug72221.phpt b/Zend/tests/bug72221.phpt
new file mode 100644
index 0000000000..8f30099cab
--- /dev/null
+++ b/Zend/tests/bug72221.phpt
@@ -0,0 +1,11 @@
+--TEST--
+Bug #72221 (Segmentation fault in stream_get_line / zend_memnstr_ex)
+--FILE--
+<?php
+$fp = fopen("php://memory", "r+");
+fwrite($fp, str_repeat("baad", 1024*1024));
+rewind($fp);
+stream_get_line($fp, 1024*1024*2, "aaaa");
+echo "Done\n";
+--EXPECT--
+Done
diff --git a/Zend/tests/bug72335.phpt b/Zend/tests/bug72335.phpt
new file mode 100644
index 0000000000..854de34281
--- /dev/null
+++ b/Zend/tests/bug72335.phpt
@@ -0,0 +1,18 @@
+--TEST--
+Misoptimize due to type narrowing
+--FILE--
+<?php
+
+function test() {
+ $b = false;
+ $x = (1<<53)+1;
+ do {
+ $x = 1.0 * ($x - (1<<53));
+ } while ($b);
+ return $x;
+}
+var_dump(test());
+
+?>
+--EXPECT--
+float(1)
diff --git a/Zend/tests/bug72373.phpt b/Zend/tests/bug72373.phpt
new file mode 100644
index 0000000000..67377c5ba7
--- /dev/null
+++ b/Zend/tests/bug72373.phpt
@@ -0,0 +1,20 @@
+--TEST--
+Bug #72373: TypeError after Generator function w/declared return type finishes
+--FILE--
+<?php
+
+function foo() : Generator {
+ yield 1;
+ yield 2;
+ yield 3;
+}
+
+foreach (foo() as $bar) {
+ echo $bar . "\n";
+}
+
+?>
+--EXPECT--
+1
+2
+3
diff --git a/Zend/tests/bug72395.phpt b/Zend/tests/bug72395.phpt
new file mode 100644
index 0000000000..e89ecdd5ad
--- /dev/null
+++ b/Zend/tests/bug72395.phpt
@@ -0,0 +1,10 @@
+--TEST--
+Bug #72395 (list() regression)
+--FILE--
+<?php
+list(,,$a,,$b,) = array(1, 2, 3, 4, 5, 6);
+var_dump($a, $b);
+?>
+--EXPECT--
+int(3)
+int(5)
diff --git a/Zend/tests/bug72441.phpt b/Zend/tests/bug72441.phpt
new file mode 100644
index 0000000000..af57b3adb0
--- /dev/null
+++ b/Zend/tests/bug72441.phpt
@@ -0,0 +1,14 @@
+--TEST--
+Bug #72441 (Segmentation fault: RFC list_keys)
+--FILE--
+<?php
+
+$array = [];
+
+list(
+ '' => $foo,
+ $bar
+) = $array;
+?>
+--EXPECTF--
+Fatal error: Cannot mix keyed and unkeyed array entries in assignments in %sbug72441.php on line %d
diff --git a/Zend/tests/closure_027.phpt b/Zend/tests/closure_027.phpt
index db42ae9307..76754f9fba 100644
--- a/Zend/tests/closure_027.phpt
+++ b/Zend/tests/closure_027.phpt
@@ -13,7 +13,11 @@ test(function() { return new stdclass; });
test(function() { });
$a = function($x) use ($y) {};
-test($a);
+try {
+ test($a);
+} catch (Throwable $e) {
+ echo "Exception: " . $e->getMessage() . "\n";
+}
test(new stdclass);
@@ -24,9 +28,7 @@ object(stdClass)#%d (0) {
NULL
Notice: Undefined variable: y in %s on line %d
-
-Warning: Missing argument 1 for {closure}(), called in %s on line %d and defined in %s on line %d
-NULL
+Exception: Too few arguments to function {closure}(), 0 passed in %s on line %d and exactly 1 expected
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:
diff --git a/Zend/tests/closure_061.phpt b/Zend/tests/closure_061.phpt
index 83ad16d2e1..1aa579a409 100644
--- a/Zend/tests/closure_061.phpt
+++ b/Zend/tests/closure_061.phpt
@@ -184,7 +184,7 @@ bindTo(new ClsChild, Cls::class):
Success!
bindTo(new ClsUnrelated, Cls::class):
-Success!
+Cannot bind method Cls::method() to object of class ClsUnrelated
bindTo(new Cls, null):
Cannot rebind scope of closure created by ReflectionFunctionAbstract::getClosure()
@@ -205,7 +205,7 @@ bindTo(new SplStack, SplDoublyLinkedList::class):
Success!
bindTo(new ClsUnrelated, SplDoublyLinkedList::class):
-Cannot bind internal method SplDoublyLinkedList::count() to object of class ClsUnrelated
+Cannot bind method SplDoublyLinkedList::count() to object of class ClsUnrelated
bindTo(null, null):
Cannot unbind $this of internal method
diff --git a/Zend/tests/closure_use_auto_global.phpt b/Zend/tests/closure_use_auto_global.phpt
new file mode 100644
index 0000000000..9ab0897e12
--- /dev/null
+++ b/Zend/tests/closure_use_auto_global.phpt
@@ -0,0 +1,16 @@
+--TEST--
+Cannot use() auto-global
+--FILE--
+<?php
+
+function test() {
+ $fn = function() use($GLOBALS) {
+ var_dump($GLOBALS);
+ };
+ $fn();
+}
+test();
+
+?>
+--EXPECTF--
+Fatal error: Cannot use auto-global as lexical variable in %s on line %d
diff --git a/Zend/tests/closure_use_parameter_name.phpt b/Zend/tests/closure_use_parameter_name.phpt
new file mode 100644
index 0000000000..7ecc6d8dd1
--- /dev/null
+++ b/Zend/tests/closure_use_parameter_name.phpt
@@ -0,0 +1,14 @@
+--TEST--
+Can't use name of lexical variable for parameter
+--FILE--
+<?php
+
+$a = 1;
+$fn = function ($a) use ($a) {
+ var_dump($a);
+};
+$fn(2);
+
+?>
+--EXPECTF--
+Fatal error: Cannot use lexical variable $a as a parameter name in %s on line %d
diff --git a/Zend/tests/closure_use_variable_twice.phpt b/Zend/tests/closure_use_variable_twice.phpt
new file mode 100644
index 0000000000..06c2645809
--- /dev/null
+++ b/Zend/tests/closure_use_variable_twice.phpt
@@ -0,0 +1,15 @@
+--TEST--
+Closure cannot use one variable twice
+--FILE--
+<?php
+
+$a = 1;
+$fn = function() use ($a, &$a) {
+ $a = 2;
+};
+$fn();
+var_dump($a);
+
+?>
+--EXPECTF--
+Fatal error: Cannot use variable $a twice in %s on line %d
diff --git a/Zend/tests/constant_expressions_dynamic.phpt b/Zend/tests/constant_expressions_dynamic.phpt
index d038591036..686dcc1d11 100644
--- a/Zend/tests/constant_expressions_dynamic.phpt
+++ b/Zend/tests/constant_expressions_dynamic.phpt
@@ -44,7 +44,9 @@ var_dump(
);
?>
---EXPECT--
+--EXPECTF--
+
+Warning: A non-numeric value encountered in %s on line %d
int(3)
string(4) "1foo"
bool(false)
diff --git a/Zend/tests/constant_expressions_self_referencing_array.phpt b/Zend/tests/constant_expressions_self_referencing_array.phpt
index 63f2b20ef5..c584b5d503 100644
--- a/Zend/tests/constant_expressions_self_referencing_array.phpt
+++ b/Zend/tests/constant_expressions_self_referencing_array.phpt
@@ -9,7 +9,7 @@ class A {
var_dump(A::FOO);
?>
--EXPECTF--
-Fatal error: Uncaught Error: Cannot declare self-referencing constant 'self::FOO' in %s:%d
+Fatal error: Uncaught Error: Cannot declare self-referencing constant 'self::BAR' in %s:%d
Stack trace:
#0 {main}
thrown in %s on line %d
diff --git a/Zend/tests/dynamic_call_005.phpt b/Zend/tests/dynamic_call_005.phpt
new file mode 100644
index 0000000000..840e298b82
--- /dev/null
+++ b/Zend/tests/dynamic_call_005.phpt
@@ -0,0 +1,29 @@
+--TEST--
+Dynamic calls to scope introspection functions are forbidden
+--FILE--
+<?php
+
+function test_calls($func) {
+ $i = 1;
+
+ array_map($func, [['i' => new stdClass]]);
+ var_dump($i);
+
+ $func(['i' => new stdClass]);
+ var_dump($i);
+
+ call_user_func($func, ['i' => new stdClass]);
+ var_dump($i);
+}
+test_calls('extract');
+
+?>
+--EXPECTF--
+Warning: Cannot call extract() dynamically in %s on line %d
+int(1)
+
+Warning: Cannot call extract() dynamically in %s on line %d
+int(1)
+
+Warning: Cannot call extract() dynamically in %s on line %d
+int(1)
diff --git a/Zend/tests/dynamic_call_006.phpt b/Zend/tests/dynamic_call_006.phpt
new file mode 100644
index 0000000000..058e22fda0
--- /dev/null
+++ b/Zend/tests/dynamic_call_006.phpt
@@ -0,0 +1,48 @@
+--TEST--
+Dynamic calls to scope introspection functions are forbidden (function variations)
+--FILE--
+<?php
+function test() {
+ $func = 'extract';
+ $func(['a' => 'b']);
+
+ $func = 'compact';
+ $func(['a']);
+
+ $func = 'parse_str';
+ $func('a=b');
+
+ $func = 'get_defined_vars';
+ $func();
+
+ $func = 'assert';
+ $func('1==2');
+
+ $func = 'func_get_args';
+ $func();
+
+ $func = 'func_get_arg';
+ $func(1);
+
+ $func = 'func_num_args';
+ $func();
+}
+test();
+
+?>
+--EXPECTF--
+Warning: Cannot call extract() dynamically in %s on line %d
+
+Warning: Cannot call compact() dynamically in %s on line %d
+
+Warning: Cannot call parse_str() with a single argument dynamically in %s on line %d
+
+Warning: Cannot call get_defined_vars() dynamically in %s on line %d
+
+Warning: Cannot call assert() with string argument dynamically in %s on line %d
+
+Warning: Cannot call func_get_args() dynamically in %s on line %d
+
+Warning: Cannot call func_get_arg() dynamically in %s on line %d
+
+Warning: Cannot call func_num_args() dynamically in %s on line %d
diff --git a/Zend/tests/dynamic_call_007.phpt b/Zend/tests/dynamic_call_007.phpt
new file mode 100644
index 0000000000..61ae182914
--- /dev/null
+++ b/Zend/tests/dynamic_call_007.phpt
@@ -0,0 +1,17 @@
+--TEST--
+Dynamic calls to scope introspection functions are forbidden (misoptimization)
+--FILE--
+<?php
+
+function test() {
+ $i = 1;
+ array_map('extract', [['i' => new stdClass]]);
+ $i += 1;
+ var_dump($i);
+}
+test();
+
+?>
+--EXPECTF--
+Warning: Cannot call extract() dynamically in %s on line %d
+int(2)
diff --git a/Zend/tests/dynamic_call_008.phpt b/Zend/tests/dynamic_call_008.phpt
new file mode 100644
index 0000000000..24240472d1
--- /dev/null
+++ b/Zend/tests/dynamic_call_008.phpt
@@ -0,0 +1,13 @@
+--TEST--
+Don't optimize dynamic call to non-dynamic one if it drops the warning
+--FILE--
+<?php
+
+function test() {
+ ((string) 'extract')(['a' => 42]);
+}
+test();
+
+?>
+--EXPECTF--
+Warning: Cannot call extract() dynamically in %s on line %d
diff --git a/Zend/tests/empty_str_offset.phpt b/Zend/tests/empty_str_offset.phpt
index 486c052dc4..49e175dd21 100644
--- a/Zend/tests/empty_str_offset.phpt
+++ b/Zend/tests/empty_str_offset.phpt
@@ -8,6 +8,8 @@ print "- empty ---\n";
$str = "test0123";
var_dump(empty($str[-1]));
+var_dump(empty($str[-10]));
+var_dump(empty($str[-4])); // 0
var_dump(empty($str[0]));
var_dump(empty($str[1]));
var_dump(empty($str[4])); // 0
@@ -17,6 +19,8 @@ var_dump(empty($str[10000]));
// non-numeric offsets
print "- string ---\n";
var_dump(empty($str['-1']));
+var_dump(empty($str['-10']));
+var_dump(empty($str['-4'])); // 0
var_dump(empty($str['0']));
var_dump(empty($str['1']));
var_dump(empty($str['4'])); // 0
@@ -31,6 +35,8 @@ print "- null ---\n";
var_dump(empty($str[null]));
print "- double ---\n";
var_dump(empty($str[-1.1]));
+var_dump(empty($str[-10.5]));
+var_dump(empty($str[-4.1]));
var_dump(empty($str[-0.8]));
var_dump(empty($str[-0.1]));
var_dump(empty($str[0.2]));
@@ -50,6 +56,8 @@ print "done\n";
?>
--EXPECTF--
- empty ---
+bool(false)
+bool(true)
bool(true)
bool(false)
bool(false)
@@ -58,6 +66,8 @@ bool(false)
bool(true)
bool(true)
- string ---
+bool(false)
+bool(true)
bool(true)
bool(false)
bool(false)
@@ -72,6 +82,8 @@ bool(true)
- null ---
bool(false)
- double ---
+bool(false)
+bool(true)
bool(true)
bool(false)
bool(false)
diff --git a/Zend/tests/entry_block_with_predecessors.phpt b/Zend/tests/entry_block_with_predecessors.phpt
new file mode 100644
index 0000000000..ffc3147f1c
--- /dev/null
+++ b/Zend/tests/entry_block_with_predecessors.phpt
@@ -0,0 +1,33 @@
+--TEST--
+For SSA form the entry block should have no predecessors
+--FILE--
+<?php
+
+function test() {
+ while (true) {
+ var_dump($a + 1);
+ $a = 1;
+ if (@$i++) {
+ break;
+ }
+ }
+}
+
+function test2() {
+ while (true) {
+ $a = 42;
+ if (@$i++ > 1) {
+ break;
+ }
+ $a = new stdClass;
+ }
+}
+
+test();
+test2();
+
+?>
+--EXPECTF--
+Notice: Undefined variable: a in %s on line %d
+int(1)
+int(2)
diff --git a/Zend/tests/error_reporting06.phpt b/Zend/tests/error_reporting06.phpt
index 285a623f2b..175ea37e80 100644
--- a/Zend/tests/error_reporting06.phpt
+++ b/Zend/tests/error_reporting06.phpt
@@ -11,7 +11,7 @@ function foo1($arg) {
function foo2($arg) {
}
-function foo3($arg) {
+function foo3() {
echo $undef3;
throw new Exception("test");
}
diff --git a/Zend/tests/error_reporting07.phpt b/Zend/tests/error_reporting07.phpt
index c63efae604..09b7690d3c 100644
--- a/Zend/tests/error_reporting07.phpt
+++ b/Zend/tests/error_reporting07.phpt
@@ -11,7 +11,7 @@ function foo1($arg) {
function foo2($arg) {
}
-function foo3($arg) {
+function foo3() {
echo $undef3;
throw new Exception("test");
}
diff --git a/Zend/tests/error_reporting08.phpt b/Zend/tests/error_reporting08.phpt
index edf3292779..c9945046ee 100644
--- a/Zend/tests/error_reporting08.phpt
+++ b/Zend/tests/error_reporting08.phpt
@@ -11,7 +11,7 @@ function foo1($arg) {
function foo2($arg) {
}
-function foo3($arg) {
+function foo3() {
error_reporting(E_ALL|E_STRICT);
echo $undef3;
throw new Exception("test");
diff --git a/Zend/tests/foreach_list_keyed.phpt b/Zend/tests/foreach_list_keyed.phpt
new file mode 100644
index 0000000000..f5fab4e342
--- /dev/null
+++ b/Zend/tests/foreach_list_keyed.phpt
@@ -0,0 +1,36 @@
+--TEST--
+foreach with list syntax, keyed
+--FILE--
+<?php
+
+$points = [
+ ["x" => 1, "y" => 2],
+ ["x" => 2, "y" => 1]
+];
+
+foreach ($points as list("x" => $x, "y" => $y)) {
+ var_dump($x, $y);
+}
+
+echo PHP_EOL;
+
+$invertedPoints = [
+ "x" => [1, 2],
+ "y" => [2, 1]
+];
+
+foreach ($invertedPoints as list(0 => $row1, 1 => $row2)) {
+ var_dump($row1, $row2);
+}
+
+?>
+--EXPECT--
+int(1)
+int(2)
+int(2)
+int(1)
+
+int(1)
+int(2)
+int(2)
+int(1)
diff --git a/Zend/tests/fr47160.phpt b/Zend/tests/fr47160.phpt
index ed2f15f990..786183c0c1 100644
--- a/Zend/tests/fr47160.phpt
+++ b/Zend/tests/fr47160.phpt
@@ -5,7 +5,7 @@ Calling method from array
class Hello {
public function world($x) {
- echo "Hello, $x\n"; return $this;
+ echo "Hello, $x\n";return $this;
}
}
@@ -37,8 +37,16 @@ class Magic3 {
}
$f = array('Hello','world');
-var_dump($f('you'));
-var_dump(call_user_func($f, 'you'));
+try {
+ var_dump($f('you'));
+} catch (Throwable $e) {
+ echo "Exception: " . $e->getMessage() . "\n";
+}
+try {
+ var_dump(call_user_func($f, 'you'));
+} catch (Throwable $e) {
+ echo "Exception: " . $e->getMessage() . "\n";
+}
printf("-----\n");
@@ -101,35 +109,31 @@ var_dump(call_user_func($f, 'you'));
--EXPECTF--
Deprecated: Non-static method Hello::world() should not be called statically in %s on line %d
Hello, you
-
-Notice: Undefined variable: this in %s on line %d
-NULL
+Exception: Using $this when not in object context
Deprecated: %son-static method Hello::world() should not be called statically in %s on line %d
Hello, you
-
-Notice: Undefined variable: this in %s on line %d
-NULL
+Exception: Using $this when not in object context
-----
Hello, again
-object(Hello)#1 (0) {
+object(Hello)#%d (0) {
}
Hello, again
-object(Hello)#1 (0) {
+object(Hello)#%d (0) {
}
-----
Hello, there
-object(Hello)#2 (0) {
+object(Hello)#%d (0) {
}
Hello, there
-object(Hello)#2 (0) {
+object(Hello)#%d (0) {
}
-----
Hello, devs
-object(Hello)#4 (0) {
+object(Hello)#%d (0) {
}
Hello, devs
-object(Hello)#4 (0) {
+object(Hello)#%d (0) {
}
-----
Magic::__call called (foo)!
diff --git a/Zend/tests/func_get_args.phpt b/Zend/tests/func_get_args.phpt
new file mode 100644
index 0000000000..eea8ae4354
--- /dev/null
+++ b/Zend/tests/func_get_args.phpt
@@ -0,0 +1,10 @@
+--TEST--
+Testing func_get_args()
+--FILE--
+<?php
+
+func_get_args();
+
+?>
+--EXPECTF--
+Warning: func_get_args(): Called from the global scope - no function context in %s on line 3
diff --git a/Zend/tests/gc_013.phpt b/Zend/tests/gc_013.phpt
index 9209ca2b40..0c5424086a 100644
--- a/Zend/tests/gc_013.phpt
+++ b/Zend/tests/gc_013.phpt
@@ -10,9 +10,9 @@ for ($i = 0; $i < 10001; $i++) {
}
$a[] = "xxx";
unset($a);
-var_dump(gc_collect_cycles());
+var_dump(gc_collect_cycles() > 0);
echo "ok\n";
?>
--EXPECT--
-int(2)
+bool(true)
ok
diff --git a/Zend/tests/gc_014.phpt b/Zend/tests/gc_014.phpt
index cd5a15e681..4fd8948af5 100644
--- a/Zend/tests/gc_014.phpt
+++ b/Zend/tests/gc_014.phpt
@@ -12,9 +12,9 @@ for ($i = 0; $i < 10001; $i++) {
unset($b);
$a->b = "xxx";
unset($a);
-var_dump(gc_collect_cycles());
+var_dump(gc_collect_cycles() > 0);
echo "ok\n";
?>
--EXPECT--
-int(2)
+bool(true)
ok
diff --git a/Zend/tests/gc_015.phpt b/Zend/tests/gc_015.phpt
index df85836ed8..24acddeae1 100644
--- a/Zend/tests/gc_015.phpt
+++ b/Zend/tests/gc_015.phpt
@@ -12,9 +12,9 @@ $a->b = "xxx";
unset($c);
unset($a);
unset($b);
-var_dump(gc_collect_cycles());
+var_dump(gc_collect_cycles() > 0);
echo "ok\n";
?>
--EXPECT--
-int(2)
+bool(true)
ok
diff --git a/Zend/tests/gc_017.phpt b/Zend/tests/gc_017.phpt
index 50be61025f..a1a8c3eaf6 100644
--- a/Zend/tests/gc_017.phpt
+++ b/Zend/tests/gc_017.phpt
@@ -10,7 +10,6 @@ class Node {
public $parent;
function __construct($name) {
$this->name = $name;
- $this->children = array();
$this->parent = null;
}
function insert($node) {
@@ -32,12 +31,12 @@ $a->insert($c);
unset($a);
unset($b);
unset($c);
-var_dump(gc_collect_cycles());
+var_dump(gc_collect_cycles() >= 7);
echo "ok\n"
?>
--EXPECTF--
string(1) "%s"
string(1) "%s"
string(1) "%s"
-int(10)
+bool(true)
ok
diff --git a/Zend/tests/generators/gc_with_yield_from.phpt b/Zend/tests/generators/gc_with_yield_from.phpt
new file mode 100644
index 0000000000..8c05561990
--- /dev/null
+++ b/Zend/tests/generators/gc_with_yield_from.phpt
@@ -0,0 +1,45 @@
+--TEST--
+Verify yield from on generators being properly cycle collected
+--FILE--
+<?php
+
+function root() {
+ global $gens; // create cyclic reference to root
+ try {
+ yield 1;
+ } finally {
+ var_dump($gens);
+ }
+}
+
+function gen($x) {
+ global $gens;
+ yield from $gens[] = $x ? gen(--$x) : root();
+}
+
+$gen = $gens[] = gen(2);
+var_dump($gen->current());
+unset($gen, $gens);
+print "collect\n";
+gc_collect_cycles();
+print "end\n";
+
+?>
+--EXPECT--
+int(1)
+collect
+array(4) {
+ [0]=>
+ object(Generator)#1 (0) {
+ }
+ [1]=>
+ object(Generator)#2 (0) {
+ }
+ [2]=>
+ object(Generator)#3 (0) {
+ }
+ [3]=>
+ object(Generator)#4 (0) {
+ }
+}
+end
diff --git a/Zend/tests/generators/generator_with_arg_unpacking.phpt b/Zend/tests/generators/generator_with_arg_unpacking.phpt
new file mode 100644
index 0000000000..edf0bafd31
--- /dev/null
+++ b/Zend/tests/generators/generator_with_arg_unpacking.phpt
@@ -0,0 +1,12 @@
+--TEST--
+Generators with arguments unpacking
+--FILE--
+<?php
+(function() { yield; })(...range(1, 16384));
+call_user_func_array(function() { yield; }, range(1, 16384));
+$g = (function() { yield; })(...range(1, 16384));
+$g = call_user_func_array(function() { yield; }, range(1, 16384));
+echo "OK\n";
+?>
+--EXPECT--
+OK \ No newline at end of file
diff --git a/Zend/tests/generators/generator_with_type_check.phpt b/Zend/tests/generators/generator_with_type_check.phpt
new file mode 100644
index 0000000000..2aa16532dc
--- /dev/null
+++ b/Zend/tests/generators/generator_with_type_check.phpt
@@ -0,0 +1,13 @@
+--TEST--
+Generator wit type check
+--FILE--
+<?php
+function gen(array $a) { yield; }
+gen(42);
+?>
+--EXPECTF--
+Fatal error: Uncaught TypeError: Argument 1 passed to gen() must be of the type array, integer given, called in %sgenerator_with_type_check.php on line 3 and defined in %sgenerator_with_type_check.php:2
+Stack trace:
+#0 %sgenerator_with_type_check.php(3): gen(42)
+#1 {main}
+ thrown in %sgenerator_with_type_check.php on line 2
diff --git a/Zend/tests/generators/generator_with_type_check_2.phpt b/Zend/tests/generators/generator_with_type_check_2.phpt
new file mode 100644
index 0000000000..d8ea4fbf0d
--- /dev/null
+++ b/Zend/tests/generators/generator_with_type_check_2.phpt
@@ -0,0 +1,22 @@
+--TEST--
+Generator wit type check
+--FILE--
+<?php
+function gen(array $a) { yield; }
+try {
+ gen(42);
+} catch (TypeError $e) {
+ echo $e->getMessage()."\n";
+}
+
+try {
+ foreach (gen(42) as $val) {
+ var_dump($val);
+ }
+} catch (TypeError $e) {
+ echo $e->getMessage()."\n";
+}
+?>
+--EXPECTF--
+Argument 1 passed to gen() must be of the type array, integer given, called in %sgenerator_with_type_check_2.php on line 4
+Argument 1 passed to gen() must be of the type array, integer given, called in %sgenerator_with_type_check_2.php on line 10
diff --git a/Zend/tests/generators/yield_from_iterator_agregate.phpt b/Zend/tests/generators/yield_from_iterator_agregate.phpt
new file mode 100644
index 0000000000..3bd61e0b5a
--- /dev/null
+++ b/Zend/tests/generators/yield_from_iterator_agregate.phpt
@@ -0,0 +1,17 @@
+--TEST--
+yield from with an IteratorAggregate
+--FILE--
+<?php
+class foo implements \IteratorAggregate {
+ public $prop = 1;
+ function getIterator() {
+ var_dump($this->prop);
+ yield;
+ }
+}
+(function(){
+ yield from new foo;
+})()->next();
+?>
+--EXPECT--
+int(1)
diff --git a/Zend/tests/global_with_side_effect_name.phpt b/Zend/tests/global_with_side_effect_name.phpt
new file mode 100644
index 0000000000..a1ee240c48
--- /dev/null
+++ b/Zend/tests/global_with_side_effect_name.phpt
@@ -0,0 +1,22 @@
+--TEST--
+Global variable import using a name with side effects
+--FILE--
+<?php
+
+function sf($arg) {
+ echo "called\n";
+ return $arg;
+}
+
+function test() {
+ global ${sf("a")};
+ var_dump($a);
+}
+
+$a = 42;
+test();
+
+?>
+--EXPECT--
+called
+int(42)
diff --git a/Zend/tests/incompat_ctx_user.phpt b/Zend/tests/incompat_ctx_user.phpt
index f05268cee9..25c95ba219 100644
--- a/Zend/tests/incompat_ctx_user.phpt
+++ b/Zend/tests/incompat_ctx_user.phpt
@@ -10,11 +10,12 @@ class B {
function bar() { A::foo(); }
}
$b = new B;
-$b->bar();
-
+try {
+ $b->bar();
+} catch (Throwable $e) {
+ echo "Exception: " . $e->getMessage() . "\n";
+}
?>
--EXPECTF--
Deprecated: Non-static method A::foo() should not be called statically in %s on line %d
-
-Notice: Undefined variable: this in %s on line %d
-string(1) "A"
+Exception: Using $this when not in object context
diff --git a/Zend/tests/indexing_001.phpt b/Zend/tests/indexing_001.phpt
index 0e466ab8ce..f247a420b5 100644
--- a/Zend/tests/indexing_001.phpt
+++ b/Zend/tests/indexing_001.phpt
@@ -12,7 +12,7 @@ foreach ($testvalues as $testvalue) {
}
echo "\n*** Indexing - Testing reference assignment with key ***\n";
-$testvalues=array(null, 0, 1, true, false,'',0.1,array());
+$testvalues=array(null, 0, 1, true, false,0.1,array());
foreach ($testvalues as $testvalue) {
$testvalue['foo']=&$array;
@@ -20,7 +20,7 @@ foreach ($testvalues as $testvalue) {
}
echo "*** Indexing - Testing value assignment no key ***\n";
$array=array(1);
-$testvalues=array(null, 0, 1, true, false,'',0.1,array());
+$testvalues=array(null, 0, 1, true, false,0.1,array());
foreach ($testvalues as $testvalue) {
$testvalue[]=$array;
@@ -28,7 +28,7 @@ foreach ($testvalues as $testvalue) {
}
echo "\n*** Indexing - Testing reference assignment no key ***\n";
-$testvalues=array(null, 0, 1, true, false,'',0.1,array());
+$testvalues=array(null, 0, 1, true, false,0.1,array());
foreach ($testvalues as $testvalue) {
$testvalue[]=&$array;
@@ -63,13 +63,11 @@ array(1) {
int(1)
}
}
-array(1) {
- ["foo"]=>
- array(1) {
- [0]=>
- int(1)
- }
-}
+
+Warning: Illegal string offset 'foo' in %s on line %d
+
+Notice: Array to string conversion in %s on line %d
+string(1) "A"
Warning: Illegal string offset 'foo' in %s on line %d
@@ -110,13 +108,6 @@ array(1) {
int(1)
}
}
-array(1) {
- ["foo"]=>
- &array(1) {
- [0]=>
- int(1)
- }
-}
Warning: Cannot use a scalar value as an array in %s on line %d
float(0.1)
@@ -151,13 +142,6 @@ array(1) {
int(1)
}
}
-array(1) {
- [0]=>
- array(1) {
- [0]=>
- int(1)
- }
-}
Warning: Cannot use a scalar value as an array in %s on line %d
float(0.1)
@@ -193,13 +177,6 @@ array(1) {
int(1)
}
}
-array(1) {
- [0]=>
- &array(1) {
- [0]=>
- int(1)
- }
-}
Warning: Cannot use a scalar value as an array in %s on line %d
float(0.1)
@@ -211,4 +188,4 @@ array(1) {
}
}
-Done \ No newline at end of file
+Done
diff --git a/Zend/tests/indirect_call_array_003.phpt b/Zend/tests/indirect_call_array_003.phpt
index 498c580c48..f1dde491f6 100644
--- a/Zend/tests/indirect_call_array_003.phpt
+++ b/Zend/tests/indirect_call_array_003.phpt
@@ -17,8 +17,11 @@ class foo {
}
$arr = array('foo', 'abc');
-$arr();
-
+try {
+ $arr();
+} catch (Throwable $e) {
+ echo "Exception: " . $e->getMessage() . "\n";
+}
$foo = new foo;
$arr = array($foo, 'abc');
$arr();
@@ -28,9 +31,7 @@ $arr();
--EXPECTF--
From foo::__callStatic:
string(3) "abc"
-
-Notice: Undefined variable: this in %s on line %d
-NULL
+Exception: Using $this when not in object context
From foo::__call:
string(3) "abc"
object(foo)#%d (0) {
diff --git a/Zend/tests/inference_infinite_loop.phpt b/Zend/tests/inference_infinite_loop.phpt
new file mode 100644
index 0000000000..1e94ea8040
--- /dev/null
+++ b/Zend/tests/inference_infinite_loop.phpt
@@ -0,0 +1,17 @@
+--TEST--
+Type inference should not result in infinite loop
+--FILE--
+<?php
+
+function test() {
+ $b = false;
+ do {
+ $a = $a + PHP_INT_MAX + 2;
+ $a = 0;
+ } while ($b);
+}
+test();
+
+?>
+--EXPECTF--
+Notice: Undefined variable: a in %s on line %d
diff --git a/Zend/tests/int_conversion_exponents.phpt b/Zend/tests/int_conversion_exponents.phpt
new file mode 100644
index 0000000000..d924cb7b81
--- /dev/null
+++ b/Zend/tests/int_conversion_exponents.phpt
@@ -0,0 +1,52 @@
+--TEST--
+Integer conversion from scientific notation
+--FILE--
+<?php
+
+var_dump((int)"1.2345e9");
+var_dump((int)"-1.2345e9");
+var_dump(intval("1.2345e9"));
+var_dump(intval("-1.2345e9"));
+var_dump("1.2345e9" % PHP_INT_MAX);
+var_dump("-1.2345e9" % PHP_INT_MIN);
+var_dump("1.2345e9" | 0);
+var_dump("-1.2345e9" | 0);
+
+echo PHP_EOL;
+
+var_dump((int)" 1.2345e9 abc");
+var_dump((int)" -1.2345e9 abc");
+var_dump(intval(" 1.2345e9 abc"));
+var_dump(intval(" -1.2345e9 abc"));
+var_dump(" 1.2345e9 abc" % PHP_INT_MAX);
+var_dump(" -1.2345e9 abc" % PHP_INT_MIN);
+var_dump(" 1.2345e9 abc" | 0);
+var_dump(" -1.2345e9 abc" | 0);
+
+?>
+--EXPECTF--
+int(1234500000)
+int(-1234500000)
+int(1234500000)
+int(-1234500000)
+int(1234500000)
+int(-1234500000)
+int(1234500000)
+int(-1234500000)
+
+int(1234500000)
+int(-1234500000)
+int(1234500000)
+int(-1234500000)
+
+Notice: A non well formed numeric value encountered in %s on line %d
+int(1234500000)
+
+Notice: A non well formed numeric value encountered in %s on line %d
+int(-1234500000)
+
+Notice: A non well formed numeric value encountered in %s on line %d
+int(1234500000)
+
+Notice: A non well formed numeric value encountered in %s on line %d
+int(-1234500000)
diff --git a/Zend/tests/isset_str_offset.phpt b/Zend/tests/isset_str_offset.phpt
index 7a9164a381..d693f80a52 100644
--- a/Zend/tests/isset_str_offset.phpt
+++ b/Zend/tests/isset_str_offset.phpt
@@ -8,6 +8,7 @@ print "- isset ---\n";
$str = "test0123";
var_dump(isset($str[-1]));
+var_dump(isset($str[-10]));
var_dump(isset($str[0]));
var_dump(isset($str[1]));
var_dump(isset($str[4])); // 0
@@ -17,6 +18,7 @@ var_dump(isset($str[10000]));
// non-numeric offsets
print "- string ---\n";
var_dump(isset($str['-1']));
+var_dump(isset($str['-10']));
var_dump(isset($str['0']));
var_dump(isset($str['1']));
var_dump(isset($str['4'])); // 0
@@ -31,6 +33,7 @@ print "- null ---\n";
var_dump(isset($str[null]));
print "- double ---\n";
var_dump(isset($str[-1.1]));
+var_dump(isset($str[-10.5]));
var_dump(isset($str[-0.8]));
var_dump(isset($str[-0.1]));
var_dump(isset($str[0.2]));
@@ -50,6 +53,7 @@ print "done\n";
?>
--EXPECTF--
- isset ---
+bool(true)
bool(false)
bool(true)
bool(true)
@@ -58,6 +62,7 @@ bool(true)
bool(false)
bool(false)
- string ---
+bool(true)
bool(false)
bool(true)
bool(true)
@@ -72,6 +77,7 @@ bool(false)
- null ---
bool(true)
- double ---
+bool(true)
bool(false)
bool(true)
bool(true)
diff --git a/Zend/tests/jump16.phpt b/Zend/tests/jump16.phpt
new file mode 100644
index 0000000000..cc820c4a6c
--- /dev/null
+++ b/Zend/tests/jump16.phpt
@@ -0,0 +1,27 @@
+--TEST--
+jump 16: goto into try/catch
+--FILE--
+<?php
+goto a;
+try {
+ echo "1";
+a:
+ echo "2";
+ throw new Exception();
+} catch (Exception $e) {
+ echo "3";
+}
+echo "4";
+goto b;
+try {
+ echo "5";
+ throw new Exception();
+} catch (Exception $e) {
+ echo "6";
+b:
+ echo "7";
+}
+echo "8\n";
+?>
+--EXPECT--
+23478
diff --git a/Zend/tests/jump17.phpt b/Zend/tests/jump17.phpt
new file mode 100644
index 0000000000..92d3be511b
--- /dev/null
+++ b/Zend/tests/jump17.phpt
@@ -0,0 +1,22 @@
+--TEST--
+jump 17: goto into try/catch with finally
+--FILE--
+<?php
+goto b;
+try {
+ echo "1";
+a:
+ echo "2";
+ throw new Exception();
+} catch (Exception $e) {
+ echo "3";
+b:
+ echo "4";
+} finally {
+ echo "5";
+c:
+ echo "6";
+}
+echo "7\n";
+--EXPECT--
+4567
diff --git a/Zend/tests/list_001.phpt b/Zend/tests/list_001.phpt
index a9fff55004..4e0053edee 100644
--- a/Zend/tests/list_001.phpt
+++ b/Zend/tests/list_001.phpt
@@ -5,6 +5,8 @@
list($a, list($b)) = array(new stdclass, array(new stdclass));
var_dump($a, $b);
+[$a, [$b]] = array(new stdclass, array(new stdclass));
+var_dump($a, $b);
?>
--EXPECT--
@@ -12,3 +14,7 @@ object(stdClass)#1 (0) {
}
object(stdClass)#2 (0) {
}
+object(stdClass)#3 (0) {
+}
+object(stdClass)#4 (0) {
+}
diff --git a/Zend/tests/list_008.phpt b/Zend/tests/list_008.phpt
new file mode 100644
index 0000000000..de8160c77e
--- /dev/null
+++ b/Zend/tests/list_008.phpt
@@ -0,0 +1,10 @@
+--TEST--
+Assignment to invalid list() value
+--FILE--
+<?php
+
+[42] = [1];
+
+?>
+--EXPECTF--
+Fatal error: Assignments can only happen to writable values in %s on line %d
diff --git a/Zend/tests/list_009.phpt b/Zend/tests/list_009.phpt
new file mode 100644
index 0000000000..c28ca8000a
--- /dev/null
+++ b/Zend/tests/list_009.phpt
@@ -0,0 +1,14 @@
+--TEST--
+list with by-reference assignment should fail
+--FILE--
+<?php
+
+$a = [1];
+[&$foo] = $a;
+$foo = 2;
+
+var_dump($a);
+
+?>
+--EXPECTF--
+Fatal error: [] and list() assignments cannot be by reference in %s on line %d
diff --git a/Zend/tests/list_010.phpt b/Zend/tests/list_010.phpt
new file mode 100644
index 0000000000..a89ffda5cd
--- /dev/null
+++ b/Zend/tests/list_010.phpt
@@ -0,0 +1,11 @@
+--TEST--
+Do not allow mixing [] and list()
+--FILE--
+<?php
+
+list([$a]) = [[1]];
+var_dump($a);
+
+?>
+--EXPECTF--
+Fatal error: Cannot mix [] and list() in %s on line %d
diff --git a/Zend/tests/list_011.phpt b/Zend/tests/list_011.phpt
new file mode 100644
index 0000000000..316498c411
--- /dev/null
+++ b/Zend/tests/list_011.phpt
@@ -0,0 +1,10 @@
+--TEST--
+Disallow list() usage as if it were an array
+--FILE--
+<?php
+
+var_dump(list(1, 2, 3));
+
+?>
+--EXPECTF--
+Parse error: syntax error, unexpected ')', expecting '=' in %s on line %d
diff --git a/Zend/tests/list_012.phpt b/Zend/tests/list_012.phpt
new file mode 100644
index 0000000000..072d28c01d
--- /dev/null
+++ b/Zend/tests/list_012.phpt
@@ -0,0 +1,10 @@
+--TEST--
+Disallow empty elements in normal arrays
+--FILE--
+<?php
+
+var_dump([, 1, 2]);
+
+?>
+--EXPECTF--
+Fatal error: Cannot use empty array elements in arrays in %s on line %d
diff --git a/Zend/tests/list_013.phpt b/Zend/tests/list_013.phpt
new file mode 100644
index 0000000000..2869f5f423
--- /dev/null
+++ b/Zend/tests/list_013.phpt
@@ -0,0 +1,10 @@
+--TEST--
+Disallow tail empty elements in normal arrays
+--FILE--
+<?php
+
+var_dump([1, 2, ,]);
+
+?>
+--EXPECTF--
+Fatal error: Cannot use empty array elements in arrays in %s on line %d
diff --git a/Zend/tests/list_keyed.phpt b/Zend/tests/list_keyed.phpt
new file mode 100644
index 0000000000..b549ed9bf5
--- /dev/null
+++ b/Zend/tests/list_keyed.phpt
@@ -0,0 +1,71 @@
+--TEST--
+list() with keys
+--FILE--
+<?php
+
+$antonyms = [
+ "good" => "bad",
+ "happy" => "sad",
+];
+
+list("good" => $good_antonym, "happy" => $happy_antonym) = $antonyms;
+var_dump($good_antonym, $happy_antonym);
+
+echo PHP_EOL;
+
+$powersOfTwo = [
+ 1 => 2,
+ 2 => 4,
+ 3 => 8
+];
+
+list(1 => $two_1, 2 => $two_2, 3 => $two_3) = $powersOfTwo;
+var_dump($two_1, $two_2, $two_3);
+
+echo PHP_EOL;
+
+$contrivedMixedKeyTypesExample = [
+ 7 => "the best PHP version",
+ "elePHPant" => "the cutest mascot"
+];
+
+list(7 => $seven, "elePHPant" => $elePHPant) = $contrivedMixedKeyTypesExample;
+var_dump($seven, $elePHPant);
+
+echo PHP_EOL;
+
+$allTogetherNow = [
+ "antonyms" => $antonyms,
+ "powersOfTwo" => $powersOfTwo,
+ "contrivedMixedKeyTypesExample" => $contrivedMixedKeyTypesExample
+];
+
+list(
+ "antonyms" => list("good" => $good_antonym, "happy" => $happy_antonym),
+ "powersOfTwo" => list(1 => $two_1, 2 => $two_2, 3 => $two_3),
+ "contrivedMixedKeyTypesExample" => list(7 => $seven, "elePHPant" => $elePHPant)
+) = $allTogetherNow;
+
+var_dump($good_antonym, $happy_antonym);
+var_dump($two_1, $two_2, $two_3);
+var_dump($seven, $elePHPant);
+
+?>
+--EXPECT--
+string(3) "bad"
+string(3) "sad"
+
+int(2)
+int(4)
+int(8)
+
+string(20) "the best PHP version"
+string(17) "the cutest mascot"
+
+string(3) "bad"
+string(3) "sad"
+int(2)
+int(4)
+int(8)
+string(20) "the best PHP version"
+string(17) "the cutest mascot"
diff --git a/Zend/tests/list_keyed_ArrayAccess.phpt b/Zend/tests/list_keyed_ArrayAccess.phpt
new file mode 100644
index 0000000000..1bb2013036
--- /dev/null
+++ b/Zend/tests/list_keyed_ArrayAccess.phpt
@@ -0,0 +1,54 @@
+--TEST--
+list() with keys and ArrayAccess
+--FILE--
+<?php
+
+$antonymObject = new ArrayObject;
+$antonymObject["good"] = "bad";
+$antonymObject["happy"] = "sad";
+
+list("good" => $good, "happy" => $happy) = $antonymObject;
+var_dump($good, $happy);
+
+echo PHP_EOL;
+
+$stdClassCollection = new SplObjectStorage;
+$foo = new StdClass;
+$stdClassCollection[$foo] = "foo";
+$bar = new StdClass;
+$stdClassCollection[$bar] = "bar";
+
+list($foo => $fooStr, $bar => $barStr) = $stdClassCollection;
+var_dump($fooStr, $barStr);
+
+echo PHP_EOL;
+
+class IndexPrinter implements ArrayAccess
+{
+ public function offsetGet($offset) {
+ echo "GET ";
+ var_dump($offset);
+ }
+ public function offsetSet($offset, $value) {
+ }
+ public function offsetExists($offset) {
+ }
+ public function offsetUnset($offset) {
+ }
+}
+
+$op = new IndexPrinter;
+list(123 => $x) = $op;
+// PHP shouldn't convert this to an integer offset, because it's ArrayAccess
+list("123" => $x) = $op;
+
+?>
+--EXPECT--
+string(3) "bad"
+string(3) "sad"
+
+string(3) "foo"
+string(3) "bar"
+
+GET int(123)
+GET string(3) "123"
diff --git a/Zend/tests/list_keyed_conversions.phpt b/Zend/tests/list_keyed_conversions.phpt
new file mode 100644
index 0000000000..bf0349b327
--- /dev/null
+++ b/Zend/tests/list_keyed_conversions.phpt
@@ -0,0 +1,34 @@
+--TEST--
+list() with non-integer-or-string keys
+--FILE--
+<?php
+
+$results = [
+ 0 => 0,
+ 1 => 1,
+ "" => ""
+];
+
+list(NULL => $NULL, 1.5 => $float, FALSE => $FALSE, TRUE => $TRUE) = $results;
+var_dump($NULL, $float, $FALSE, $TRUE);
+
+echo PHP_EOL;
+
+list("0" => $zeroString, "1" => $oneString) = $results;
+var_dump($zeroString, $oneString);
+
+list(STDIN => $resource) = [];
+
+?>
+--EXPECTF--
+string(0) ""
+int(1)
+int(0)
+int(1)
+
+int(0)
+int(1)
+
+Notice: Resource ID#%d used as offset, casting to integer (%d) in %s on line %d
+
+Notice: Undefined offset: 1 in %s on line %d
diff --git a/Zend/tests/list_keyed_evaluation_order.inc b/Zend/tests/list_keyed_evaluation_order.inc
new file mode 100644
index 0000000000..490a6d84fe
--- /dev/null
+++ b/Zend/tests/list_keyed_evaluation_order.inc
@@ -0,0 +1,60 @@
+<?php declare(strict_types=1);
+
+// Observer objects for the Zend/tests/list_keyed_evaluation_order.* tests
+
+class Stringable
+{
+ private $name;
+ public function __construct(string $name) {
+ $this->name = $name;
+ }
+
+ public function __toString(): string {
+ echo "$this->name evaluated.", PHP_EOL;
+ return $this->name;
+ }
+}
+
+class Indexable implements ArrayAccess
+{
+ private $array;
+ public function __construct(array $array) {
+ $this->array = $array;
+ }
+
+ public function offsetExists($offset): bool {
+ echo "Existence of offset $offset checked for.", PHP_EOL;
+ return isset($this->array[$offset]);
+ }
+
+ public function offsetUnset($offset): void {
+ unset($this->array[$offset]);
+ echo "Offset $offset removed.", PHP_EOL;
+ }
+
+ public function offsetGet($offset) {
+ echo "Offset $offset retrieved.", PHP_EOL;
+ return $this->array[$offset];
+ }
+
+ public function offsetSet($offset, $value): void {
+ $this->array[$offset] = $value;
+ echo "Offset $offset set to $value.", PHP_EOL;
+ }
+}
+
+class IndexableRetrievable
+{
+ private $label;
+ private $indexable;
+
+ public function __construct(string $label, Indexable $indexable) {
+ $this->label = $label;
+ $this->indexable = $indexable;
+ }
+
+ public function getIndexable(): Indexable {
+ echo "Indexable $this->label retrieved.", PHP_EOL;
+ return $this->indexable;
+ }
+}
diff --git a/Zend/tests/list_keyed_evaluation_order.phpt b/Zend/tests/list_keyed_evaluation_order.phpt
new file mode 100644
index 0000000000..0f0652b6a9
--- /dev/null
+++ b/Zend/tests/list_keyed_evaluation_order.phpt
@@ -0,0 +1,35 @@
+--TEST--
+list() with keys, evaluation order
+--FILE--
+<?php
+
+require_once "list_keyed_evaluation_order.inc";
+
+$a = new Stringable("A");
+$c = new Stringable("C");
+
+$e = new IndexableRetrievable("E", new Indexable(["A" => "value for offset A", "C" => "value for offset C"]));
+
+$store = new Indexable([]);
+
+// list($a => $b, $c => $d) = $e;
+// Should be evaluated in the order:
+// 1. Evaluate $e
+// 2. Evaluate $a
+// 3. Evaluate $e[$a]
+// 4. Assign $b from $e[$a]
+// 5. Evaluate $c
+// 6. Evaluate $e[$c]
+// 7. Assign $c from $e[$a]
+
+list((string)$a => $store["B"], (string)$c => $store["D"]) = $e->getIndexable();
+
+?>
+--EXPECT--
+Indexable E retrieved.
+A evaluated.
+Offset A retrieved.
+Offset B set to value for offset A.
+C evaluated.
+Offset C retrieved.
+Offset D set to value for offset C.
diff --git a/Zend/tests/list_keyed_evaluation_order_2.phpt b/Zend/tests/list_keyed_evaluation_order_2.phpt
new file mode 100644
index 0000000000..ddfba68c46
--- /dev/null
+++ b/Zend/tests/list_keyed_evaluation_order_2.phpt
@@ -0,0 +1,77 @@
+--TEST--
+list() with keys, evaluation order #2
+--FILE--
+<?php
+
+// All the following should print 'a' then 'b'
+
+list($a, $b) = ['a', 'b'];
+var_dump($a);
+var_dump($b);
+
+list(0 => $a, 1 => $b) = ['a', 'b'];
+var_dump($a);
+var_dump($b);
+
+list(1 => $b, 0 => $a) = ['a', 'b'];
+var_dump($a);
+var_dump($b);
+
+$arr = [];
+list($arr[], $arr[]) = ['a', 'b'];
+var_dump($arr[0]);
+var_dump($arr[1]);
+
+$arr = [];
+list(0 => $arr[], 1 => $arr[]) = ['a', 'b'];
+var_dump($arr[0]);
+var_dump($arr[1]);
+
+$arr = [];
+list(1 => $arr[], 0 => $arr[]) = ['b', 'a'];
+var_dump($arr[0]);
+var_dump($arr[1]);
+
+$arr = [];
+list(list(1 => $arr[], 0 => $arr[])) = [['b', 'a']];
+var_dump($arr[0]);
+var_dump($arr[1]);
+
+$arr = [];
+list('key1' => $arr[], 'key2' => $arr[]) = ['key2' => 'b', 'key1' => 'a'];
+var_dump($arr[0]);
+var_dump($arr[1]);
+
+// This should print 'foo'
+$a = 0;
+list($a => $a) = ['foo', 'bar'];
+var_dump($a);
+
+// This should print 'bar' then 'foo'
+$a = 0;
+$b = 1;
+list($b => $a, $a => $c) = ['bar' => 'foo', 1 => 'bar'];
+var_dump($a);
+var_dump($c);
+
+?>
+--EXPECT--
+string(1) "a"
+string(1) "b"
+string(1) "a"
+string(1) "b"
+string(1) "a"
+string(1) "b"
+string(1) "a"
+string(1) "b"
+string(1) "a"
+string(1) "b"
+string(1) "a"
+string(1) "b"
+string(1) "a"
+string(1) "b"
+string(1) "a"
+string(1) "b"
+string(3) "foo"
+string(3) "bar"
+string(3) "foo"
diff --git a/Zend/tests/list_keyed_evaluation_order_3.phpt b/Zend/tests/list_keyed_evaluation_order_3.phpt
new file mode 100644
index 0000000000..7850834c3b
--- /dev/null
+++ b/Zend/tests/list_keyed_evaluation_order_3.phpt
@@ -0,0 +1,24 @@
+--TEST--
+list() with keys, evaluation order #3
+--FILE--
+<?php
+
+$i = 0;
+$a = [
+ 0 => [
+ 'b' => 'bar',
+ 'a' => 'foo',
+ ],
+ 1 => 'a',
+ 3 => 'b',
+];
+list($a[$i++] => $a[$i++], $a[$i++] => $a[$i++]) = $a[$i++];
+var_dump($i); // should be 5
+var_dump($a[2]); // should be 'foo'
+var_dump($a[4]); // should be 'bar'
+
+?>
+--EXPECT--
+int(5)
+string(3) "foo"
+string(3) "bar"
diff --git a/Zend/tests/list_keyed_evaluation_order_nested.phpt b/Zend/tests/list_keyed_evaluation_order_nested.phpt
new file mode 100644
index 0000000000..8a7725d4ea
--- /dev/null
+++ b/Zend/tests/list_keyed_evaluation_order_nested.phpt
@@ -0,0 +1,77 @@
+--TEST--
+list() with keys, evaluation order: nested
+--FILE--
+<?php
+
+require_once "list_keyed_evaluation_order.inc";
+
+$a = new Stringable("A");
+$c = new Stringable("C");
+$f = new Stringable("F");
+$g = new Stringable("G");
+$i = new Stringable("I");
+
+$k = new IndexableRetrievable("K", new Indexable([
+ "A" => "offset value for A",
+ "C" => new Indexable([
+ 0 => "offset value for 0",
+ 1 => "offset value for 1"
+ ]),
+ "F" => new Indexable([
+ "G" => "offset value for G",
+ "I" => "offset value for I"
+ ])
+]));
+
+$store = new Indexable([]);
+
+// list($a => $b, $c => list($d, $e), $f => list($g => $h, $i => $j)) = $k;
+// Should be evaluated in the order:
+// 1. Evaluate $k
+// 2. Evaluate $a
+// 3. Evaluate $k[$a]
+// 4. Assign $b from $k[$a]
+// 5. Evaluate $c
+// 6. Evaluate $k[$c]
+// 7. Evaluate $k[$c][0]
+// 8. Assign $d from $k[$c][0]
+// 9. Evaluate $k[$c][1]
+// 10. Assign $e from $k[$c][1]
+// 11. Evaluate $f
+// 12. Evaluate $k[$f]
+// 13. Evaluate $g
+// 14. Evaluate $k[$f][$g]
+// 15. Assign $h from $k[$f][$g]
+// 16. Evaluate $i
+// 17. Evaluate $k[$f][$i]
+// 18. Assign $j from $k[$f][$i]
+
+list(
+ (string)$a => $store["B"],
+ (string)$c => list($store["D"], $store["E"]),
+ (string)$f => list(
+ (string)$g => $store["H"],
+ (string)$i => $store["J"]
+ )
+) = $k->getIndexable();
+
+?>
+--EXPECT--
+Indexable K retrieved.
+A evaluated.
+Offset A retrieved.
+Offset B set to offset value for A.
+C evaluated.
+Offset C retrieved.
+Offset 0 retrieved.
+Offset D set to offset value for 0.
+Offset 1 retrieved.
+Offset E set to offset value for 1.
+F evaluated.
+Offset F retrieved.
+G evaluated.
+Offset G retrieved.
+Offset H set to offset value for G.
+I evaluated.
+Offset I retrieved.
+Offset J set to offset value for I.
diff --git a/Zend/tests/list_keyed_non_literals.phpt b/Zend/tests/list_keyed_non_literals.phpt
new file mode 100644
index 0000000000..80f22eda22
--- /dev/null
+++ b/Zend/tests/list_keyed_non_literals.phpt
@@ -0,0 +1,30 @@
+--TEST--
+list() with constant keys
+--FILE--
+<?php
+
+$arr = [
+ 1 => "one",
+ 2 => "two",
+ 3 => "three"
+];
+
+const COMPILE_TIME_RESOLVABLE = 1;
+
+define('PROBABLY_NOT_COMPILE_TIME_RESOLVABLE', file_get_contents("data:text/plain,2"));
+
+$probablyNotCompileTimeResolvable3 = cos(0) * 3;
+
+list(
+ COMPILE_TIME_RESOLVABLE => $one,
+ PROBABLY_NOT_COMPILE_TIME_RESOLVABLE => $two,
+ $probablyNotCompileTimeResolvable3 => $three
+) = $arr;
+
+var_dump($one, $two, $three);
+
+?>
+--EXPECTF--
+string(3) "one"
+string(3) "two"
+string(5) "three"
diff --git a/Zend/tests/list_keyed_trailing_comma.phpt b/Zend/tests/list_keyed_trailing_comma.phpt
new file mode 100644
index 0000000000..e0af0aed21
--- /dev/null
+++ b/Zend/tests/list_keyed_trailing_comma.phpt
@@ -0,0 +1,38 @@
+--TEST--
+list() with keys and a trailing comma
+--FILE--
+<?php
+
+$antonyms = [
+ "good" => "bad",
+ "happy" => "sad",
+];
+
+list(
+ "good" => $good,
+ "happy" => $happy
+) = $antonyms;
+
+var_dump($good, $happy);
+
+echo PHP_EOL;
+
+$antonyms = [
+ "good" => "bad",
+ "happy" => "sad",
+];
+
+list(
+ "good" => $good,
+ "happy" => $happy,
+) = $antonyms;
+
+var_dump($good, $happy);
+
+?>
+--EXPECT--
+string(3) "bad"
+string(3) "sad"
+
+string(3) "bad"
+string(3) "sad"
diff --git a/Zend/tests/list_keyed_undefined.phpt b/Zend/tests/list_keyed_undefined.phpt
new file mode 100644
index 0000000000..a18e3b4d20
--- /dev/null
+++ b/Zend/tests/list_keyed_undefined.phpt
@@ -0,0 +1,22 @@
+--TEST--
+list() with undefined keys
+--FILE--
+<?php
+
+$contrivedMixedKeyTypesExample = [
+ 7 => "the best PHP version",
+ "elePHPant" => "the cutest mascot"
+];
+
+list(5 => $five, "duke" => $duke) = $contrivedMixedKeyTypesExample;
+
+var_dump($five, $duke);
+
+?>
+--EXPECTF--
+
+Notice: Undefined offset: 5 in %s on line %d
+
+Notice: Undefined index: duke in %s on line %d
+NULL
+NULL
diff --git a/Zend/tests/list_mixed_keyed_unkeyed.phpt b/Zend/tests/list_mixed_keyed_unkeyed.phpt
new file mode 100644
index 0000000000..245d20f31f
--- /dev/null
+++ b/Zend/tests/list_mixed_keyed_unkeyed.phpt
@@ -0,0 +1,16 @@
+--TEST--
+list() with both keyed and unkeyed elements
+--FILE--
+<?php
+
+$contrivedKeyedAndUnkeyedArrayExample = [
+ 0,
+ 1 => 1,
+ "foo" => "bar"
+];
+
+list($zero, 1 => $one, "foo" => $foo) = $contrivedKeyedAndUnkeyedArrayExample;
+
+?>
+--EXPECTF--
+Fatal error: Cannot mix keyed and unkeyed array entries in assignments in %s on line %d
diff --git a/Zend/tests/list_mixed_nested_keyed_unkeyed.phpt b/Zend/tests/list_mixed_nested_keyed_unkeyed.phpt
new file mode 100644
index 0000000000..3087775b76
--- /dev/null
+++ b/Zend/tests/list_mixed_nested_keyed_unkeyed.phpt
@@ -0,0 +1,34 @@
+--TEST--
+list() with nested unkeyed and keyed list()
+--FILE--
+<?php
+
+$points = [
+ ["x" => 1, "y" => 2],
+ ["x" => 2, "y" => 1]
+];
+
+list(list("x" => $x1, "y" => $y1), list("x" => $x2, "y" => $y2)) = $points;
+var_dump($x1, $y1, $x2, $y2);
+
+echo PHP_EOL;
+
+$invertedPoints = [
+ "x" => [1, 2],
+ "y" => [2, 1]
+];
+
+list("x" => list($x1, $x2), "y" => list($y1, $y2)) = $invertedPoints;
+var_dump($x1, $y1, $x2, $y2);
+
+?>
+--EXPECT--
+int(1)
+int(2)
+int(2)
+int(1)
+
+int(1)
+int(2)
+int(2)
+int(1)
diff --git a/Zend/tests/method_argument_binding.phpt b/Zend/tests/method_argument_binding.phpt
new file mode 100644
index 0000000000..731cc4fb32
--- /dev/null
+++ b/Zend/tests/method_argument_binding.phpt
@@ -0,0 +1,47 @@
+--TEST--
+Edge cases in compile-time method argument binding
+--FILE--
+<?php
+
+class A {
+ private function method($x) {}
+}
+
+class B extends A {
+ public function test() {
+ $x = 1;
+ $this->method($x);
+ var_dump($x);
+ }
+}
+
+class C extends B {
+ public function method(&$x) {
+ ++$x;
+ }
+}
+
+(new C)->test();
+
+class D {
+ private final function method(&$x) {
+ ++$x;
+ }
+}
+
+class E extends D {
+ public function __call($name, $args) { }
+
+ public function test() {
+ $this->method($x);
+ }
+}
+
+(new E)->test();
+
+?>
+--EXPECTF--
+Warning: Declaration of C::method(&$x) should be compatible with A::method($x) in %s on line %d
+int(2)
+
+Notice: Undefined variable: x in %s on line %d
diff --git a/Zend/tests/new_args_without_ctor.phpt b/Zend/tests/new_args_without_ctor.phpt
new file mode 100644
index 0000000000..91456890d2
--- /dev/null
+++ b/Zend/tests/new_args_without_ctor.phpt
@@ -0,0 +1,10 @@
+--TEST--
+Argument of new on class without constructor are evaluated
+--FILE--
+<?php
+
+new stdClass(print 'a', print 'b');
+
+?>
+--EXPECT--
+ab
diff --git a/Zend/tests/nullable_types/array.phpt b/Zend/tests/nullable_types/array.phpt
new file mode 100644
index 0000000000..7b0a2ed4d5
--- /dev/null
+++ b/Zend/tests/nullable_types/array.phpt
@@ -0,0 +1,17 @@
+--TEST--
+Explicitly nullable array type
+--FILE--
+<?php
+
+function _array_(?array $v): ?array {
+ return $v;
+}
+
+var_dump(_array_(null));
+var_dump(_array_([]));
+
+--EXPECT--
+NULL
+array(0) {
+}
+
diff --git a/Zend/tests/nullable_types/contravariant_nullable_param_succeeds.phpt b/Zend/tests/nullable_types/contravariant_nullable_param_succeeds.phpt
new file mode 100644
index 0000000000..f4d1e315fc
--- /dev/null
+++ b/Zend/tests/nullable_types/contravariant_nullable_param_succeeds.phpt
@@ -0,0 +1,19 @@
+--TEST--
+Subtype can add nullability to a parameter (contravariance)
+
+--FILE--
+<?php
+
+interface A {
+ function method(int $p);
+}
+
+class B implements A {
+ function method(?int $p) { }
+}
+
+$b = new B();
+$b->method(null);
+
+--EXPECT--
+
diff --git a/Zend/tests/nullable_types/contravariant_nullable_return_fails.phpt b/Zend/tests/nullable_types/contravariant_nullable_return_fails.phpt
new file mode 100644
index 0000000000..c9be479ead
--- /dev/null
+++ b/Zend/tests/nullable_types/contravariant_nullable_return_fails.phpt
@@ -0,0 +1,17 @@
+--TEST--
+Return type cannot add nullability (contravariance)
+
+--FILE--
+<?php
+
+interface A {
+ function method(): int;
+}
+
+interface B extends A {
+ function method(): ?int;
+}
+
+--EXPECTF--
+Fatal error: Declaration of B::method(): ?int must be compatible with A::method(): int in %s on line %d
+
diff --git a/Zend/tests/nullable_types/covariant_nullable_param_fails.phpt b/Zend/tests/nullable_types/covariant_nullable_param_fails.phpt
new file mode 100644
index 0000000000..65b2858e6b
--- /dev/null
+++ b/Zend/tests/nullable_types/covariant_nullable_param_fails.phpt
@@ -0,0 +1,17 @@
+--TEST--
+Subtype cannot remove nullable parameter (covariance)
+
+--FILE--
+<?php
+
+interface A {
+ function method(?int $p);
+}
+
+class B implements A {
+ function method(int $p) { }
+}
+
+--EXPECTF--
+Fatal error: Declaration of B::method(int $p) must be compatible with A::method(?int $p) in %s on line %d
+
diff --git a/Zend/tests/nullable_types/covariant_nullable_return_succeds.phpt b/Zend/tests/nullable_types/covariant_nullable_return_succeds.phpt
new file mode 100644
index 0000000000..5776f9b99d
--- /dev/null
+++ b/Zend/tests/nullable_types/covariant_nullable_return_succeds.phpt
@@ -0,0 +1,16 @@
+--TEST--
+Nullable covariant return types
+
+--FILE--
+<?php
+
+interface A {
+ function method(): ?int;
+}
+
+interface B extends A {
+ function method(): int;
+}
+
+--EXPECT--
+
diff --git a/Zend/tests/nullable_types/float.phpt b/Zend/tests/nullable_types/float.phpt
new file mode 100644
index 0000000000..8e44524cf0
--- /dev/null
+++ b/Zend/tests/nullable_types/float.phpt
@@ -0,0 +1,16 @@
+--TEST--
+Explicitly nullable float type
+--FILE--
+<?php
+
+function _float_(?float $v): ?float {
+ return $v;
+}
+
+var_dump(_float_(null));
+var_dump(_float_(1.3));
+
+--EXPECT--
+NULL
+float(1.3)
+
diff --git a/Zend/tests/nullable_types/int.phpt b/Zend/tests/nullable_types/int.phpt
new file mode 100644
index 0000000000..ec75132edb
--- /dev/null
+++ b/Zend/tests/nullable_types/int.phpt
@@ -0,0 +1,16 @@
+--TEST--
+Explicitly nullable int type
+--FILE--
+<?php
+
+function _int_(?int $v): ?int {
+ return $v;
+}
+
+var_dump(_int_(null));
+var_dump(_int_(1));
+
+--EXPECT--
+NULL
+int(1)
+
diff --git a/Zend/tests/nullable_types/invariant_param_and_return_succeeds.phpt b/Zend/tests/nullable_types/invariant_param_and_return_succeeds.phpt
new file mode 100644
index 0000000000..0542e52c0f
--- /dev/null
+++ b/Zend/tests/nullable_types/invariant_param_and_return_succeeds.phpt
@@ -0,0 +1,24 @@
+--TEST--
+Invariant parameter and return types work with nullable types
+
+--FILE--
+<?php
+
+interface A {
+ function method(?int $i): ?int;
+}
+
+class B implements A {
+ function method(?int $i): ?int {
+ return $i;
+ }
+}
+
+$b = new B();
+var_dump($b->method(null));
+var_dump($b->method(1));
+
+--EXPECT--
+NULL
+int(1)
+
diff --git a/Zend/tests/nullable_types/nullable_type_parameters_do_not_have_default_value.phpt b/Zend/tests/nullable_types/nullable_type_parameters_do_not_have_default_value.phpt
new file mode 100644
index 0000000000..5de93bb7d6
--- /dev/null
+++ b/Zend/tests/nullable_types/nullable_type_parameters_do_not_have_default_value.phpt
@@ -0,0 +1,17 @@
+--TEST--
+Explicit nullable types do not imply a default value
+
+--FILE--
+<?php
+
+function f(?callable $p) {}
+
+f();
+
+--EXPECTF--
+Fatal error: Uncaught Error: Too few arguments to function f(), 0 passed in %snullable_type_parameters_do_not_have_default_value.php on line %d and exactly 1 expected in %s:%d
+Stack trace:
+#%d %s
+#%d %s
+ thrown in %s on line %d
+
diff --git a/Zend/tests/nullable_types/string.phpt b/Zend/tests/nullable_types/string.phpt
new file mode 100644
index 0000000000..ffc6591b6b
--- /dev/null
+++ b/Zend/tests/nullable_types/string.phpt
@@ -0,0 +1,16 @@
+--TEST--
+Explicitly nullable string type
+--FILE--
+<?php
+
+function _string_(?string $v): ?string {
+ return $v;
+}
+
+var_dump(_string_(null));
+var_dump(_string_("php"));
+
+--EXPECT--
+NULL
+string(3) "php"
+
diff --git a/Zend/tests/numeric_string_errors.phpt b/Zend/tests/numeric_string_errors.phpt
new file mode 100644
index 0000000000..e98c58dda7
--- /dev/null
+++ b/Zend/tests/numeric_string_errors.phpt
@@ -0,0 +1,195 @@
+--TEST--
+Invalid numeric string E_WARNINGs and E_NOTICEs
+--FILE--
+<?php
+
+var_dump("2 Lorem" + "3 ipsum");
+var_dump("dolor" + "sit");
+echo "---", PHP_EOL;
+var_dump("5 amet," - "7 consectetur");
+var_dump("adipiscing" - "elit,");
+echo "---", PHP_EOL;
+var_dump("11 sed" * "13 do");
+var_dump("eiusmod" * "tempor");
+echo "---", PHP_EOL;
+var_dump("17 incididunt" / "19 ut");
+var_dump("labore" / "et");
+echo "---", PHP_EOL;
+var_dump("23 dolore" ** "29 magna");
+var_dump("aliqua." ** "Ut");
+echo "---", PHP_EOL;
+var_dump("31 enim" % "37 ad");
+try {
+ var_dump("minim" % "veniam,");
+} catch (DivisionByZeroError $e) {
+}
+echo "---", PHP_EOL;
+var_dump("41 minim" << "43 veniam,");
+var_dump("quis" << "nostrud");
+echo "---", PHP_EOL;
+var_dump("47 exercitation" >> "53 ullamco");
+var_dump("laboris" >> "nisi");
+echo "---", PHP_EOL;
+var_dump("59 ut" | 61);
+var_dump(67 | "71 aliquip");
+var_dump("ex" | 73);
+var_dump(79 | "ea");
+echo "---", PHP_EOL;
+var_dump("83 commodo" & 89);
+var_dump(97 & "101 consequat.");
+var_dump("Duis" & 103);
+var_dump(107 & "aute");
+echo "---", PHP_EOL;
+var_dump("109 irure" ^ 113);
+var_dump(127 ^ "131 dolor");
+var_dump("in" ^ 137);
+var_dump(139 ^ "reprehenderit");
+echo "---", PHP_EOL;
+var_dump(+"149 in");
+var_dump(+"voluptate");
+echo "---", PHP_EOL;
+var_dump(-"151 velit");
+var_dump(-"esse");
+?>
+--EXPECTF--
+
+Notice: A non well formed numeric value encountered in %s on line %d
+
+Notice: A non well formed numeric value encountered in %s on line %d
+int(5)
+
+Warning: A non-numeric value encountered in %s on line %d
+
+Warning: A non-numeric value encountered in %s on line %d
+int(0)
+---
+
+Notice: A non well formed numeric value encountered in %s on line %d
+
+Notice: A non well formed numeric value encountered in %s on line %d
+int(-2)
+
+Warning: A non-numeric value encountered in %s on line %d
+
+Warning: A non-numeric value encountered in %s on line %d
+int(0)
+---
+
+Notice: A non well formed numeric value encountered in %s on line %d
+
+Notice: A non well formed numeric value encountered in %s on line %d
+int(143)
+
+Warning: A non-numeric value encountered in %s on line %d
+
+Warning: A non-numeric value encountered in %s on line %d
+int(0)
+---
+
+Notice: A non well formed numeric value encountered in %s on line %d
+
+Notice: A non well formed numeric value encountered in %s on line %d
+float(0.89473684210526)
+
+Warning: A non-numeric value encountered in %s on line %d
+
+Warning: A non-numeric value encountered in %s on line %d
+
+Warning: Division by zero in %s on line %d
+float(NAN)
+---
+
+Notice: A non well formed numeric value encountered in %s on line %d
+
+Notice: A non well formed numeric value encountered in %s on line %d
+float(3.0910586430935E+39)
+
+Warning: A non-numeric value encountered in %s on line %d
+
+Warning: A non-numeric value encountered in %s on line %d
+int(1)
+---
+
+Notice: A non well formed numeric value encountered in %s on line %d
+
+Notice: A non well formed numeric value encountered in %s on line %d
+int(31)
+
+Warning: A non-numeric value encountered in %s on line %d
+
+Warning: A non-numeric value encountered in %s on line %d
+---
+
+Notice: A non well formed numeric value encountered in %s on line %d
+
+Notice: A non well formed numeric value encountered in %s on line %d
+int(%d)
+
+Warning: A non-numeric value encountered in %s on line %d
+
+Warning: A non-numeric value encountered in %s on line %d
+int(0)
+---
+
+Notice: A non well formed numeric value encountered in %s on line %d
+
+Notice: A non well formed numeric value encountered in %s on line %d
+int(0)
+
+Warning: A non-numeric value encountered in %s on line %d
+
+Warning: A non-numeric value encountered in %s on line %d
+int(0)
+---
+
+Notice: A non well formed numeric value encountered in %s on line %d
+int(63)
+
+Notice: A non well formed numeric value encountered in %s on line %d
+int(71)
+
+Warning: A non-numeric value encountered in %s on line %d
+int(73)
+
+Warning: A non-numeric value encountered in %s on line %d
+int(79)
+---
+
+Notice: A non well formed numeric value encountered in %s on line %d
+int(81)
+
+Notice: A non well formed numeric value encountered in %s on line %d
+int(97)
+
+Warning: A non-numeric value encountered in %s on line %d
+int(0)
+
+Warning: A non-numeric value encountered in %s on line %d
+int(0)
+---
+
+Notice: A non well formed numeric value encountered in %s on line %d
+int(28)
+
+Notice: A non well formed numeric value encountered in %s on line %d
+int(252)
+
+Warning: A non-numeric value encountered in %s on line %d
+int(137)
+
+Warning: A non-numeric value encountered in %s on line %d
+int(139)
+---
+
+Notice: A non well formed numeric value encountered in %s on line %d
+int(149)
+
+Warning: A non-numeric value encountered in %s on line %d
+int(0)
+---
+
+Notice: A non well formed numeric value encountered in %s on line %d
+int(-151)
+
+Warning: A non-numeric value encountered in %s on line %d
+int(0)
diff --git a/Zend/tests/numeric_string_errors_assign.phpt b/Zend/tests/numeric_string_errors_assign.phpt
new file mode 100644
index 0000000000..8d882aadcc
--- /dev/null
+++ b/Zend/tests/numeric_string_errors_assign.phpt
@@ -0,0 +1,236 @@
+--TEST--
+Invalid numeric string E_WARNINGs and E_NOTICEs, combined assignment operations
+--FILE--
+<?php
+
+// prevents CT eval
+function foxcache($val) {
+ return [$val][0];
+}
+
+$a = foxcache("2 Lorem");
+$a += "3 ipsum";
+var_dump($a);
+$a = foxcache("dolor");
+$a += "sit";
+var_dump($a);
+echo "---", PHP_EOL;
+$a = foxcache("5 amet,");
+$a -= "7 consectetur";
+var_dump($a);
+$a = foxcache("adipiscing");
+$a -= "elit,";
+var_dump($a);
+echo "---", PHP_EOL;
+$a = foxcache("11 sed");
+$a *= "13 do";
+var_dump($a);
+$a = foxcache("eiusmod");
+$a *= "tempor";
+var_dump($a);
+echo "---", PHP_EOL;
+$a = foxcache("17 incididunt");
+$a /= "19 ut";
+var_dump($a);
+$a = foxcache("labore");
+$a /= "et";
+var_dump($a);
+echo "---", PHP_EOL;
+$a = foxcache("23 dolore");
+$a **= "29 magna";
+var_dump($a);
+$a = foxcache("aliqua.");
+$a **= "Ut";
+var_dump($a);
+echo "---", PHP_EOL;
+$a = foxcache("31 enim");
+$a %= "37 ad";
+var_dump($a);
+try {
+ $a = foxcache("minim");
+ $a %= "veniam,";
+ var_dump($a);
+} catch (DivisionByZeroError $e) {
+}
+echo "---", PHP_EOL;
+$a = foxcache("41 minim");
+$a <<= "43 veniam,";
+var_dump($a);
+$a = foxcache("quis");
+$a <<= "nostrud";
+var_dump($a);
+echo "---", PHP_EOL;
+$a = foxcache("47 exercitation");
+$a >>= "53 ullamco";
+var_dump($a);
+$a = foxcache("laboris");
+$a >>= "nisi";
+var_dump($a);
+echo "---", PHP_EOL;
+$a = foxcache("59 ut");
+$a |= 61;
+var_dump($a);
+$a = foxcache(67);
+$a |= "71 aliquip";
+var_dump($a);
+$a = foxcache("ex");
+$a |= 73;
+var_dump($a);
+$a = foxcache(79);
+$a |= "ea";
+var_dump($a);
+echo "---", PHP_EOL;
+$a = foxcache("83 commodo");
+$a &= 89;
+var_dump($a);
+$a = foxcache(97);
+$a &= "101 consequat.";
+var_dump($a);
+$a = foxcache("Duis");
+$a &= 103;
+var_dump($a);
+$a = foxcache(107);
+$a &= "aute";
+var_dump($a);
+echo "---", PHP_EOL;
+$a = foxcache("109 irure");
+$a ^= 113;
+var_dump($a);
+$a = foxcache(127);
+$a ^= "131 dolor";
+var_dump($a);
+$a = foxcache("in");
+$a ^= 137;
+var_dump($a);
+$a = foxcache(139);
+$a ^= "reprehenderit";
+var_dump($a);
+?>
+--EXPECTF--
+
+Notice: A non well formed numeric value encountered in %s on line %d
+
+Notice: A non well formed numeric value encountered in %s on line %d
+int(5)
+
+Warning: A non-numeric value encountered in %s on line %d
+
+Warning: A non-numeric value encountered in %s on line %d
+int(0)
+---
+
+Notice: A non well formed numeric value encountered in %s on line %d
+
+Notice: A non well formed numeric value encountered in %s on line %d
+int(-2)
+
+Warning: A non-numeric value encountered in %s on line %d
+
+Warning: A non-numeric value encountered in %s on line %d
+int(0)
+---
+
+Notice: A non well formed numeric value encountered in %s on line %d
+
+Notice: A non well formed numeric value encountered in %s on line %d
+int(143)
+
+Warning: A non-numeric value encountered in %s on line %d
+
+Warning: A non-numeric value encountered in %s on line %d
+int(0)
+---
+
+Notice: A non well formed numeric value encountered in %s on line %d
+
+Notice: A non well formed numeric value encountered in %s on line %d
+float(0.89473684210526)
+
+Warning: A non-numeric value encountered in %s on line %d
+
+Warning: A non-numeric value encountered in %s on line %d
+
+Warning: Division by zero in %s on line %d
+float(NAN)
+---
+
+Notice: A non well formed numeric value encountered in %s on line %d
+
+Notice: A non well formed numeric value encountered in %s on line %d
+float(3.0910586430935E+39)
+
+Warning: A non-numeric value encountered in %s on line %d
+
+Warning: A non-numeric value encountered in %s on line %d
+int(1)
+---
+
+Notice: A non well formed numeric value encountered in %s on line %d
+
+Notice: A non well formed numeric value encountered in %s on line %d
+int(31)
+
+Warning: A non-numeric value encountered in %s on line %d
+
+Warning: A non-numeric value encountered in %s on line %d
+---
+
+Notice: A non well formed numeric value encountered in %s on line %d
+
+Notice: A non well formed numeric value encountered in %s on line %d
+int(%d)
+
+Warning: A non-numeric value encountered in %s on line %d
+
+Warning: A non-numeric value encountered in %s on line %d
+int(0)
+---
+
+Notice: A non well formed numeric value encountered in %s on line %d
+
+Notice: A non well formed numeric value encountered in %s on line %d
+int(0)
+
+Warning: A non-numeric value encountered in %s on line %d
+
+Warning: A non-numeric value encountered in %s on line %d
+int(0)
+---
+
+Notice: A non well formed numeric value encountered in %s on line %d
+int(63)
+
+Notice: A non well formed numeric value encountered in %s on line %d
+int(71)
+
+Warning: A non-numeric value encountered in %s on line %d
+int(73)
+
+Warning: A non-numeric value encountered in %s on line %d
+int(79)
+---
+
+Notice: A non well formed numeric value encountered in %s on line %d
+int(81)
+
+Notice: A non well formed numeric value encountered in %s on line %d
+int(97)
+
+Warning: A non-numeric value encountered in %s on line %d
+int(0)
+
+Warning: A non-numeric value encountered in %s on line %d
+int(0)
+---
+
+Notice: A non well formed numeric value encountered in %s on line %d
+int(28)
+
+Notice: A non well formed numeric value encountered in %s on line %d
+int(252)
+
+Warning: A non-numeric value encountered in %s on line %d
+int(137)
+
+Warning: A non-numeric value encountered in %s on line %d
+int(139)
diff --git a/Zend/tests/oct_overflow_char.phpt b/Zend/tests/oct_overflow_char.phpt
new file mode 100644
index 0000000000..32060d8593
--- /dev/null
+++ b/Zend/tests/oct_overflow_char.phpt
@@ -0,0 +1,10 @@
+--TEST--
+Octal overflow in string interpolation
+--FILE--
+<?php
+
+// "abc", ordinarily 'b' would be \142, but we'll deliberately overflow the value by \400
+echo "\141\542\143\n";
+--EXPECTF--
+Warning: Octal escape sequence overflow \542 is greater than \377 in %s%eoct_overflow_char.php on line 4
+abc
diff --git a/Zend/tests/overloaded_func_001.phpt b/Zend/tests/overloaded_func_001.phpt
new file mode 100644
index 0000000000..2702772a46
--- /dev/null
+++ b/Zend/tests/overloaded_func_001.phpt
@@ -0,0 +1,15 @@
+--TEST--
+Overloaded function 001
+--SKIPIF--
+<?php
+if (!PHP_DEBUG) die("skip only run in debug version");
+?>
+--FILE--
+<?php
+var_dump(_ZendTestClass::test());
+?>
+--EXPECTF--
+Fatal error: Uncaught Error: Cannot call overloaded function for non-object in %soverloaded_func_001.php:%d
+Stack trace:
+#0 {main}
+ thrown in %soverloaded_func_001.php on line %d
diff --git a/Zend/tests/overloaded_func_002.phpt b/Zend/tests/overloaded_func_002.phpt
new file mode 100644
index 0000000000..6c16965919
--- /dev/null
+++ b/Zend/tests/overloaded_func_002.phpt
@@ -0,0 +1,13 @@
+--TEST--
+Overloaded function 002
+--SKIPIF--
+<?php
+if (!PHP_DEBUG) die("skip only run in debug version");
+?>
+--FILE--
+<?php
+$a = new _ZendTestClass();
+var_dump($a->{trim(" test")}());
+?>
+--EXPECT--
+string(4) "test"
diff --git a/Zend/tests/parse_str_with_unpack.phpt b/Zend/tests/parse_str_with_unpack.phpt
new file mode 100644
index 0000000000..50b296d710
--- /dev/null
+++ b/Zend/tests/parse_str_with_unpack.phpt
@@ -0,0 +1,15 @@
+--TEST--
+Calling parse_str through argument unpacking
+--FILE--
+<?php
+
+function test() {
+ $i = 0;
+ parse_str(...["i=41"]);
+ var_dump($i + 1);
+}
+test();
+
+?>
+--EXPECT--
+int(42)
diff --git a/Zend/tests/php_errormsg_misoptimization.phpt b/Zend/tests/php_errormsg_misoptimization.phpt
new file mode 100644
index 0000000000..c121c1021a
--- /dev/null
+++ b/Zend/tests/php_errormsg_misoptimization.phpt
@@ -0,0 +1,20 @@
+--TEST--
+The variable $php_errormsg shouldn't be optimized as it may be unpredictably modified
+--INI--
+track_errors=1
+--FILE--
+<?php
+
+function test() {
+ $php_errormsg = 1;
+ echo $undef;
+ var_dump($php_errormsg + 1);
+}
+test();
+
+?>
+--EXPECTF--
+Notice: Undefined variable: undef in %s on line %d
+
+Warning: A non-numeric value encountered in %s on line %d
+int(1)
diff --git a/Zend/tests/return_types/029.phpt b/Zend/tests/return_types/029.phpt
index 011182df37..adc07cde7f 100644
--- a/Zend/tests/return_types/029.phpt
+++ b/Zend/tests/return_types/029.phpt
@@ -9,14 +9,19 @@ function foo() : array {
try {
throw new Exception("xxxx");
} finally {
- return ;
+ return null;
}
}
foo();
?>
--EXPECTF--
-Fatal error: Uncaught TypeError: Return value of foo() must be of the type array, none returned in %s29.php:%d
+Fatal error: Uncaught Exception: xxxx in %s:%d
+Stack trace:
+#0 %s(%d): foo()
+#1 {main}
+
+Next TypeError: Return value of foo() must be of the type array, null returned in %s29.php:%d
Stack trace:
#0 %s(%d): foo()
#1 {main}
diff --git a/Zend/tests/return_types/030.phpt b/Zend/tests/return_types/030.phpt
new file mode 100644
index 0000000000..d1ceac8329
--- /dev/null
+++ b/Zend/tests/return_types/030.phpt
@@ -0,0 +1,23 @@
+--TEST--
+Nullable return value
+--FILE--
+<?php
+function foo($x) : ?array {
+ return $x;
+}
+
+foo([]);
+echo "ok\n";
+foo(null);
+echo "ok\n";
+foo(0);
+?>
+--EXPECTF--
+ok
+ok
+
+Fatal error: Uncaught TypeError: Return value of foo() must be of the type array, integer returned in %s030.php:3
+Stack trace:
+#0 %s030.php(10): foo(0)
+#1 {main}
+ thrown in %s030.php on line 3
diff --git a/Zend/tests/return_types/031.phpt b/Zend/tests/return_types/031.phpt
new file mode 100644
index 0000000000..91ee2f8ce4
--- /dev/null
+++ b/Zend/tests/return_types/031.phpt
@@ -0,0 +1,14 @@
+--TEST--
+Nullable return type inheritance rules (non-nullable and nullable)
+--FILE--
+<?php
+class A {
+ function foo(): int {}
+}
+class B extends A {
+ function foo(): ?int {}
+}
+?>
+DONE
+--EXPECTF--
+Fatal error: Declaration of B::foo(): ?int must be compatible with A::foo(): int in %s031.php on line 7 \ No newline at end of file
diff --git a/Zend/tests/return_types/032.phpt b/Zend/tests/return_types/032.phpt
new file mode 100644
index 0000000000..00790b5d60
--- /dev/null
+++ b/Zend/tests/return_types/032.phpt
@@ -0,0 +1,14 @@
+--TEST--
+Nullable return type inheritance rules (nullable and non-nullable)
+--FILE--
+<?php
+class A {
+ function foo(): ?int {}
+}
+class B extends A {
+ function foo(): int {}
+}
+?>
+DONE
+--EXPECT--
+DONE
diff --git a/Zend/tests/return_types/bug71092.phpt b/Zend/tests/return_types/bug71092.phpt
index a1ebc79085..1d4fe983b3 100644
--- a/Zend/tests/return_types/bug71092.phpt
+++ b/Zend/tests/return_types/bug71092.phpt
@@ -9,14 +9,14 @@ function boom(): array {
$data = [['id']];
switch ($data[0]) {
case ['id']:
- return;
+ return null;
}
}
boom();
?>
--EXPECTF--
-Fatal error: Uncaught TypeError: Return value of boom() must be of the type array, none returned in %sbug71092.php:%d
+Fatal error: Uncaught TypeError: Return value of boom() must be of the type array, null returned in %sbug71092.php:%d
Stack trace:
#0 %s(%d): boom()
#1 {main}
diff --git a/Zend/tests/return_types/void_allowed.phpt b/Zend/tests/return_types/void_allowed.phpt
new file mode 100644
index 0000000000..8f07c7392e
--- /dev/null
+++ b/Zend/tests/return_types/void_allowed.phpt
@@ -0,0 +1,20 @@
+--TEST--
+void return type: acceptable cases
+--FILE--
+<?php
+
+function foo(): void {
+ // okay
+}
+
+foo();
+
+function bar(): void {
+ return; // okay
+}
+
+bar();
+
+echo "OK!", PHP_EOL;
+--EXPECT--
+OK!
diff --git a/Zend/tests/return_types/void_disallowed1.phpt b/Zend/tests/return_types/void_disallowed1.phpt
new file mode 100644
index 0000000000..365e2060bf
--- /dev/null
+++ b/Zend/tests/return_types/void_disallowed1.phpt
@@ -0,0 +1,12 @@
+--TEST--
+void return type: unacceptable cases: explicit NULL return
+--FILE--
+<?php
+
+function foo(): void {
+ return NULL; // not permitted in a void function
+}
+
+// Note the lack of function call: function validated at compile-time
+--EXPECTF--
+Fatal error: A void function must not return a value (did you mean "return;" instead of "return null;"?) in %s on line %d
diff --git a/Zend/tests/return_types/void_disallowed2.phpt b/Zend/tests/return_types/void_disallowed2.phpt
new file mode 100644
index 0000000000..7bbc3ac24f
--- /dev/null
+++ b/Zend/tests/return_types/void_disallowed2.phpt
@@ -0,0 +1,12 @@
+--TEST--
+void return type: unacceptable cases: explicit return of some other value
+--FILE--
+<?php
+
+function foo(): void {
+ return -1; // not permitted in a void function
+}
+
+// Note the lack of function call: function validated at compile-time
+--EXPECTF--
+Fatal error: A void function must not return a value in %s on line %d
diff --git a/Zend/tests/return_types/void_parameter.phpt b/Zend/tests/return_types/void_parameter.phpt
new file mode 100644
index 0000000000..4c6e918406
--- /dev/null
+++ b/Zend/tests/return_types/void_parameter.phpt
@@ -0,0 +1,8 @@
+--TEST--
+void return type: not valid as a parameter type
+--FILE--
+<?php
+
+function foobar(void $a) {}
+--EXPECTF--
+Fatal error: void cannot be used as a parameter type in %s on line %d
diff --git a/Zend/tests/self_and.phpt b/Zend/tests/self_and.phpt
index cdcde77992..44db877e92 100644
--- a/Zend/tests/self_and.phpt
+++ b/Zend/tests/self_and.phpt
@@ -20,6 +20,10 @@ echo "Done\n";
?>
--EXPECTF--
int(18)
+
+Warning: A non-numeric value encountered in %s on line %d
int(0)
+
+Notice: A non well formed numeric value encountered in %s on line %d
int(33)
Done
diff --git a/Zend/tests/self_instanceof_outside_class.phpt b/Zend/tests/self_instanceof_outside_class.phpt
new file mode 100644
index 0000000000..4caef37883
--- /dev/null
+++ b/Zend/tests/self_instanceof_outside_class.phpt
@@ -0,0 +1,17 @@
+--TEST--
+instanceof self outside a class
+--FILE--
+<?php
+
+$fn = function() {
+ try {
+ new stdClass instanceof self;
+ } catch (Error $e) {
+ echo $e->getMessage(), "\n";
+ }
+};
+$fn();
+
+?>
+--EXPECT--
+Cannot access self:: when no class scope is active
diff --git a/Zend/tests/self_method_or_prop_outside_class.phpt b/Zend/tests/self_method_or_prop_outside_class.phpt
new file mode 100644
index 0000000000..e4a499def8
--- /dev/null
+++ b/Zend/tests/self_method_or_prop_outside_class.phpt
@@ -0,0 +1,36 @@
+--TEST--
+Accessing self:: properties or methods outside a class
+--FILE--
+<?php
+
+$fn = function() {
+ $str = "foo";
+ try {
+ self::${$str . "bar"};
+ } catch (Error $e) {
+ echo $e->getMessage(), "\n";
+ }
+ try {
+ unset(self::${$str . "bar"});
+ } catch (Error $e) {
+ echo $e->getMessage(), "\n";
+ }
+ try {
+ isset(self::${$str . "bar"});
+ } catch (Error $e) {
+ echo $e->getMessage(), "\n";
+ }
+ try {
+ self::{$str . "bar"}();
+ } catch (Error $e) {
+ echo $e->getMessage(), "\n";
+ }
+};
+$fn();
+
+?>
+--EXPECT--
+Cannot access self:: when no class scope is active
+Cannot access self:: when no class scope is active
+Cannot access self:: when no class scope is active
+Cannot access self:: when no class scope is active
diff --git a/Zend/tests/self_mod.phpt b/Zend/tests/self_mod.phpt
index 19e45d88fc..0b10987aeb 100644
--- a/Zend/tests/self_mod.phpt
+++ b/Zend/tests/self_mod.phpt
@@ -20,6 +20,10 @@ echo "Done\n";
?>
--EXPECTF--
int(13)
+
+Warning: A non-numeric value encountered in %s on line %d
int(0)
+
+Notice: A non well formed numeric value encountered in %s on line %d
int(3)
Done
diff --git a/Zend/tests/self_or.phpt b/Zend/tests/self_or.phpt
index ae667bff16..8ace518bde 100644
--- a/Zend/tests/self_or.phpt
+++ b/Zend/tests/self_or.phpt
@@ -20,6 +20,10 @@ echo "Done\n";
?>
--EXPECTF--
int(127)
+
+Warning: A non-numeric value encountered in %s on line %d
int(11)
+
+Notice: A non well formed numeric value encountered in %s on line %d
int(45345)
Done
diff --git a/Zend/tests/self_xor.phpt b/Zend/tests/self_xor.phpt
index a7e43f539d..c097930d6d 100644
--- a/Zend/tests/self_xor.phpt
+++ b/Zend/tests/self_xor.phpt
@@ -20,6 +20,10 @@ echo "Done\n";
?>
--EXPECTF--
int(109)
+
+Warning: A non-numeric value encountered in %s on line %d
int(11)
+
+Notice: A non well formed numeric value encountered in %s on line %d
int(45312)
Done
diff --git a/Zend/tests/shift_001.phpt b/Zend/tests/shift_001.phpt
index aeb399452d..7546f1a6d8 100644
--- a/Zend/tests/shift_001.phpt
+++ b/Zend/tests/shift_001.phpt
@@ -20,6 +20,10 @@ echo "Done\n";
?>
--EXPECTF--
int(492)
+
+Warning: A non-numeric value encountered in %s on line %d
int(0)
+
+Notice: A non well formed numeric value encountered in %s on line %d
int(362760)
Done
diff --git a/Zend/tests/shift_002.phpt b/Zend/tests/shift_002.phpt
index 4d8421a566..6288152585 100644
--- a/Zend/tests/shift_002.phpt
+++ b/Zend/tests/shift_002.phpt
@@ -20,6 +20,10 @@ echo "Done\n";
?>
--EXPECTF--
int(30)
+
+Warning: A non-numeric value encountered in %s on line %d
int(0)
+
+Notice: A non well formed numeric value encountered in %s on line %d
int(5668)
Done
diff --git a/Zend/tests/str_offset_001.phpt b/Zend/tests/str_offset_001.phpt
index 8a6b91b49a..3317674857 100644
--- a/Zend/tests/str_offset_001.phpt
+++ b/Zend/tests/str_offset_001.phpt
@@ -1,51 +1,46 @@
---TEST--
-string offset 001
---FILE--
-<?php
-function foo($x) {
- var_dump($x);
-}
-
-$str = "abc";
-var_dump($str[-1]);
-var_dump($str[0]);
-var_dump($str[1]);
-var_dump($str[2]);
-var_dump($str[3]);
-var_dump($str[1][0]);
-var_dump($str[2][1]);
-
-foo($str[-1]);
-foo($str[0]);
-foo($str[1]);
-foo($str[2]);
-foo($str[3]);
-foo($str[1][0]);
-foo($str[2][1]);
-?>
---EXPECTF--
-Notice: Uninitialized string offset: -1 in %sstr_offset_001.php on line %d
-string(0) ""
-string(1) "a"
-string(1) "b"
-string(1) "c"
-
-Notice: Uninitialized string offset: 3 in %sstr_offset_001.php on line %d
-string(0) ""
-string(1) "b"
-
-Notice: Uninitialized string offset: 1 in %sstr_offset_001.php on line %d
-string(0) ""
-
-Notice: Uninitialized string offset: -1 in %sstr_offset_001.php on line %d
-string(0) ""
-string(1) "a"
-string(1) "b"
-string(1) "c"
-
-Notice: Uninitialized string offset: 3 in %sstr_offset_001.php on line %d
-string(0) ""
-string(1) "b"
-
-Notice: Uninitialized string offset: 1 in %sstr_offset_001.php on line %d
-string(0) ""
+--TEST--
+string offset 001
+--FILE--
+<?php
+// Test positive or null string offsets
+
+function foo($x) {
+ var_dump($x);
+}
+
+$str = "abc";
+var_dump($str[0]);
+var_dump($str[1]);
+var_dump($str[2]);
+var_dump($str[3]);
+var_dump($str[1][0]);
+var_dump($str[2][1]);
+
+foo($str[0]);
+foo($str[1]);
+foo($str[2]);
+foo($str[3]);
+foo($str[1][0]);
+foo($str[2][1]);
+?>
+--EXPECTF--
+string(1) "a"
+string(1) "b"
+string(1) "c"
+
+Notice: Uninitialized string offset: 3 in %sstr_offset_001.php on line %d
+string(0) ""
+string(1) "b"
+
+Notice: Uninitialized string offset: 1 in %sstr_offset_001.php on line %d
+string(0) ""
+string(1) "a"
+string(1) "b"
+string(1) "c"
+
+Notice: Uninitialized string offset: 3 in %sstr_offset_001.php on line %d
+string(0) ""
+string(1) "b"
+
+Notice: Uninitialized string offset: 1 in %sstr_offset_001.php on line %d
+string(0) ""
diff --git a/Zend/tests/str_offset_003.phpt b/Zend/tests/str_offset_003.phpt
new file mode 100644
index 0000000000..e357ac0c01
--- /dev/null
+++ b/Zend/tests/str_offset_003.phpt
@@ -0,0 +1,37 @@
+--TEST--
+string offset 003
+--FILE--
+<?php
+// Test negative string offsets
+
+function foo($x) {
+ var_dump($x);
+}
+
+$str = "abcdef";
+var_dump($str[-10]);
+var_dump($str[-3]);
+var_dump($str[2][-2]);
+var_dump($str[2][-1]);
+
+foo($str[-10]);
+foo($str[-3]);
+foo($str[2][-2]);
+foo($str[2][-1]);
+?>
+--EXPECTF--
+Notice: Uninitialized string offset: -10 in %sstr_offset_003.php on line %d
+string(0) ""
+string(1) "d"
+
+Notice: Uninitialized string offset: -2 in %sstr_offset_003.php on line %d
+string(0) ""
+string(1) "c"
+
+Notice: Uninitialized string offset: -10 in %sstr_offset_003.php on line %d
+string(0) ""
+string(1) "d"
+
+Notice: Uninitialized string offset: -2 in %sstr_offset_003.php on line %d
+string(0) ""
+string(1) "c"
diff --git a/Zend/tests/str_offset_004.phpt b/Zend/tests/str_offset_004.phpt
new file mode 100644
index 0000000000..c8ce607535
--- /dev/null
+++ b/Zend/tests/str_offset_004.phpt
@@ -0,0 +1,49 @@
+--TEST--
+string offset 004
+--FILE--
+<?php
+// Test assignments using (positive and negative) string offsets
+
+$str = "abcdefghijklmno";
+$i = 3;
+$j = -4;
+
+$str{2} = 'C';
+var_dump($str);
+
+$str{$i} = 'Z';
+var_dump($str);
+
+$str{-5} = 'P';
+var_dump($str);
+
+$str{$j} = 'Q';
+var_dump($str);
+
+$str{-20} = 'Y';
+var_dump($str);
+
+$str{-strlen($str)} = strtoupper($str{0}); /* An exotic ucfirst() ;) */
+var_dump($str);
+
+$str{20} = 'N';
+var_dump($str);
+
+$str{-2} = 'UFO';
+var_dump($str);
+
+$str{-$i} = $str{$j*2};
+var_dump($str);
+?>
+--EXPECTF--
+string(15) "abCdefghijklmno"
+string(15) "abCZefghijklmno"
+string(15) "abCZefghijPlmno"
+string(15) "abCZefghijPQmno"
+
+Warning: Illegal string offset: -20 in %sstr_offset_004.php on line %d
+string(15) "abCZefghijPQmno"
+string(15) "AbCZefghijPQmno"
+string(21) "AbCZefghijPQmno N"
+string(21) "AbCZefghijPQmno UN"
+string(21) "AbCZefghijPQmno nUN"
diff --git a/Zend/tests/temporary_cleaning_001.phpt b/Zend/tests/temporary_cleaning_001.phpt
index 40340bc3da..f2ccbb35b8 100644
--- a/Zend/tests/temporary_cleaning_001.phpt
+++ b/Zend/tests/temporary_cleaning_001.phpt
@@ -1,7 +1,5 @@
--TEST--
Temporary leak on exception
---XFAIL--
-See Bug #62210 and attempt to fix it in "tmp_livelibess" branch
--FILE--
<?php
diff --git a/Zend/tests/temporary_cleaning_003.phpt b/Zend/tests/temporary_cleaning_003.phpt
index 0f7d9450eb..acff8c85f0 100644
--- a/Zend/tests/temporary_cleaning_003.phpt
+++ b/Zend/tests/temporary_cleaning_003.phpt
@@ -1,7 +1,5 @@
--TEST--
Fundamental memory leak test on temporaries
---XFAIL--
-See Bug #62210 and attempt to fix it in "tmp_livelibess" branch
--FILE--
<?php
diff --git a/Zend/tests/temporary_cleaning_004.phpt b/Zend/tests/temporary_cleaning_004.phpt
index e2b093654f..b8a02516b0 100644
--- a/Zend/tests/temporary_cleaning_004.phpt
+++ b/Zend/tests/temporary_cleaning_004.phpt
@@ -1,7 +1,5 @@
--TEST--
Temporary leak with switch
---XFAIL--
-See Bug #62210 and attempt to fix it in "tmp_livelibess" branch
--FILE--
<?php
diff --git a/Zend/tests/temporary_cleaning_005.phpt b/Zend/tests/temporary_cleaning_005.phpt
index f671c32543..e8c7febe0b 100644
--- a/Zend/tests/temporary_cleaning_005.phpt
+++ b/Zend/tests/temporary_cleaning_005.phpt
@@ -1,7 +1,5 @@
--TEST--
Temporary leak with foreach
---XFAIL--
-See Bug #62210 and attempt to fix it in "tmp_livelibess" branch
--FILE--
<?php
diff --git a/Zend/tests/temporary_cleaning_006.phpt b/Zend/tests/temporary_cleaning_006.phpt
index 435e7b12dd..a7936d3915 100644
--- a/Zend/tests/temporary_cleaning_006.phpt
+++ b/Zend/tests/temporary_cleaning_006.phpt
@@ -1,7 +1,5 @@
--TEST--
Exception after separation during indirect write to fcall result
---XFAIL--
-See Bug #62210 and attempt to fix it in "tmp_livelibess" branch
--FILE--
<?php
diff --git a/Zend/tests/temporary_cleaning_008.phpt b/Zend/tests/temporary_cleaning_008.phpt
new file mode 100644
index 0000000000..fabd3b4b38
--- /dev/null
+++ b/Zend/tests/temporary_cleaning_008.phpt
@@ -0,0 +1,15 @@
+--TEST--
+Optimization of constant switch expression
+--FILE--
+<?php
+try {
+ switch ("1" . (int)2) {
+ case 12:
+ throw new Exception();
+ }
+} catch (Exception $e) {
+ echo "exception\n";
+}
+?>
+--EXPECT--
+exception
diff --git a/Zend/tests/temporary_cleaning_009.phpt b/Zend/tests/temporary_cleaning_009.phpt
new file mode 100644
index 0000000000..32a84a6ffd
--- /dev/null
+++ b/Zend/tests/temporary_cleaning_009.phpt
@@ -0,0 +1,27 @@
+--TEST--
+Live range & free on return
+--FILE--
+<?php
+class bar {
+ public $foo = 1;
+ public $bar = 1;
+
+ function __destruct() {
+ throw $this->foo;
+ }
+}
+foreach (new bar as &$foo) {
+ try {
+ $foo = new Exception;
+ return; // frees the loop variable
+ } catch (Exception $e) {
+ echo "exception\n";
+ }
+}
+echo "end\n";
+?>
+--EXPECTF--
+Fatal error: Uncaught Exception in %stemporary_cleaning_009.php:12
+Stack trace:
+#0 {main}
+ thrown in %stemporary_cleaning_009.php on line 12
diff --git a/Zend/tests/temporary_cleaning_010.phpt b/Zend/tests/temporary_cleaning_010.phpt
new file mode 100644
index 0000000000..e4456041b2
--- /dev/null
+++ b/Zend/tests/temporary_cleaning_010.phpt
@@ -0,0 +1,21 @@
+--TEST--
+Live range & throw from finally
+--FILE--
+<?php
+function test() {
+ try {
+ $a = [1, 2, 3];
+ return $a + [];
+ } finally {
+ throw new Exception;
+ }
+}
+
+try {
+ test();
+} catch (Exception $e) {
+ echo "exception\n";
+}
+?>
+--EXPECT--
+exception
diff --git a/Zend/tests/temporary_cleaning_011.phpt b/Zend/tests/temporary_cleaning_011.phpt
new file mode 100644
index 0000000000..e4a6af3ab9
--- /dev/null
+++ b/Zend/tests/temporary_cleaning_011.phpt
@@ -0,0 +1,20 @@
+--TEST--
+Live range & lists
+--FILE--
+<?php
+class A {
+ function __destruct() {
+ throw new Exception();
+ }
+}
+$b = new A();
+$x = 0;
+$c = [[$x,$x]];
+try {
+ list($a, $b) = $c[0];
+} catch (Exception $e) {
+ echo "exception\n";
+}
+?>
+--EXPECT--
+exception
diff --git a/Zend/tests/temporary_cleaning_012.phpt b/Zend/tests/temporary_cleaning_012.phpt
new file mode 100644
index 0000000000..fdbea6d41d
--- /dev/null
+++ b/Zend/tests/temporary_cleaning_012.phpt
@@ -0,0 +1,20 @@
+--TEST--
+Live range of ZEND_NEW must be assigned to correct variable
+--FILE--
+<?php
+
+class Foo {
+ public static function test() {
+ self::$property = new self;
+ }
+}
+
+try {
+ Foo::test();
+} catch (Error $e) {
+ echo $e->getMessage(), "\n";
+}
+
+?>
+--EXPECT--
+Access to undeclared static property: Foo::$property
diff --git a/Zend/tests/this_as_global.phpt b/Zend/tests/this_as_global.phpt
new file mode 100644
index 0000000000..0ba9ade1b9
--- /dev/null
+++ b/Zend/tests/this_as_global.phpt
@@ -0,0 +1,12 @@
+--TEST--
+$this as global variable
+--FILE--
+<?php
+function foo() {
+ global $this;
+ var_dump($this);
+}
+foo();
+?>
+--EXPECTF--
+Fatal error: Cannot use $this as global variable in %sthis_as_global.php on line 3
diff --git a/Zend/tests/this_as_parameter.phpt b/Zend/tests/this_as_parameter.phpt
new file mode 100644
index 0000000000..93101969a7
--- /dev/null
+++ b/Zend/tests/this_as_parameter.phpt
@@ -0,0 +1,11 @@
+--TEST--
+$this as parameter
+--FILE--
+<?php
+function foo($this) {
+ var_dump($this);
+}
+foo(5);
+?>
+--EXPECTF--
+Fatal error: Cannot use $this as parameter in %sthis_as_parameter.php on line 2
diff --git a/Zend/tests/this_as_static.phpt b/Zend/tests/this_as_static.phpt
new file mode 100644
index 0000000000..f094449c12
--- /dev/null
+++ b/Zend/tests/this_as_static.phpt
@@ -0,0 +1,12 @@
+--TEST--
+$this as static variable
+--FILE--
+<?php
+function foo() {
+ static $this;
+ var_dump($this);
+}
+foo();
+?>
+--EXPECTF--
+Fatal error: Cannot use $this as static variable in %sthis_as_static.php on line 3
diff --git a/Zend/tests/this_in_catch.phpt b/Zend/tests/this_in_catch.phpt
new file mode 100644
index 0000000000..d621bb18ea
--- /dev/null
+++ b/Zend/tests/this_in_catch.phpt
@@ -0,0 +1,18 @@
+--TEST--
+$this in catch
+--FILE--
+<?php
+class C {
+ function foo() {
+ try {
+ throw new Exception();
+ } catch (Exception $this) {
+ }
+ var_dump($this);
+ }
+}
+$obj = new C;
+$obj->foo();
+?>
+--EXPECTF--
+Fatal error: Cannot re-assign $this in %sthis_in_catch.php on line 6
diff --git a/Zend/tests/this_in_extract.phpt b/Zend/tests/this_in_extract.phpt
new file mode 100644
index 0000000000..de8da3d0a5
--- /dev/null
+++ b/Zend/tests/this_in_extract.phpt
@@ -0,0 +1,17 @@
+--TEST--
+$this re-assign in extract()
+--FILE--
+<?php
+function foo() {
+ extract(["this"=>42]);
+ var_dump($this);
+}
+foo();
+?>
+--EXPECTF--
+Fatal error: Uncaught Error: Cannot re-assign $this in %sthis_in_extract.php:3
+Stack trace:
+#0 %sthis_in_extract.php(3): extract(Array)
+#1 %sthis_in_extract.php(6): foo()
+#2 {main}
+ thrown in %sthis_in_extract.php on line 3
diff --git a/Zend/tests/this_in_foreach_001.phpt b/Zend/tests/this_in_foreach_001.phpt
new file mode 100644
index 0000000000..a724338b90
--- /dev/null
+++ b/Zend/tests/this_in_foreach_001.phpt
@@ -0,0 +1,11 @@
+--TEST--
+$this in foreach
+--FILE--
+<?php
+$a = [1];
+foreach ($a as $this) {
+ var_dump($this);
+}
+?>
+--EXPECTF--
+Fatal error: Cannot re-assign $this in %sthis_in_foreach_001.php on line 3
diff --git a/Zend/tests/this_in_foreach_002.phpt b/Zend/tests/this_in_foreach_002.phpt
new file mode 100644
index 0000000000..511ea36a24
--- /dev/null
+++ b/Zend/tests/this_in_foreach_002.phpt
@@ -0,0 +1,11 @@
+--TEST--
+$this in foreach
+--FILE--
+<?php
+$a = [1];
+foreach ($a as $this => $dummy) {
+ var_dump($this);
+}
+?>
+--EXPECTF--
+Fatal error: Cannot re-assign $this in %sthis_in_foreach_002.php on line 3
diff --git a/Zend/tests/this_in_foreach_003.phpt b/Zend/tests/this_in_foreach_003.phpt
new file mode 100644
index 0000000000..5f5b5ae0d8
--- /dev/null
+++ b/Zend/tests/this_in_foreach_003.phpt
@@ -0,0 +1,11 @@
+--TEST--
+$this in foreach
+--FILE--
+<?php
+$a = [1];
+foreach ($a as &$this) {
+ var_dump($this);
+}
+?>
+--EXPECTF--
+Fatal error: Cannot re-assign $this in %sthis_in_foreach_003.php on line 3
diff --git a/Zend/tests/this_in_foreach_004.phpt b/Zend/tests/this_in_foreach_004.phpt
new file mode 100644
index 0000000000..13bfbf18b4
--- /dev/null
+++ b/Zend/tests/this_in_foreach_004.phpt
@@ -0,0 +1,11 @@
+--TEST--
+$this in foreach
+--FILE--
+<?php
+$a = [[1]];
+foreach ($a as list($this)) {
+ var_dump($this);
+}
+?>
+--EXPECTF--
+Fatal error: Cannot re-assign $this in %sthis_in_foreach_004.php on line 3
diff --git a/Zend/tests/this_in_isset.phpt b/Zend/tests/this_in_isset.phpt
new file mode 100644
index 0000000000..2ae335df93
--- /dev/null
+++ b/Zend/tests/this_in_isset.phpt
@@ -0,0 +1,41 @@
+--TEST--
+$this in isset
+--FILE--
+<?php
+var_dump(isset($this));
+try {
+ var_dump(isset($this->foo));
+} catch (Throwable $e) {
+ echo "exception\n";
+}
+try {
+ var_dump(isset($this->foo->bar));
+} catch (Throwable $e) {
+ echo "exception\n";
+}
+try {
+ var_dump(isset($this[0]));
+} catch (Throwable $e) {
+ echo "exception\n";
+}
+
+class A extends ArrayObject {
+ public $foo = 5;
+ function foo() {
+ $this[0] = 5;
+ var_dump(isset($this));
+ var_dump(isset($this->foo));
+ var_dump(isset($this[0]));
+ }
+}
+$a = new A();
+$a->foo();
+?>
+--EXPECT--
+bool(false)
+exception
+exception
+exception
+bool(true)
+bool(true)
+bool(true)
diff --git a/Zend/tests/this_in_mb_parse_str.phpt b/Zend/tests/this_in_mb_parse_str.phpt
new file mode 100644
index 0000000000..8dfac94722
--- /dev/null
+++ b/Zend/tests/this_in_mb_parse_str.phpt
@@ -0,0 +1,19 @@
+--TEST--
+$this re-assign in mb_parse_str()
+--SKIPIF--
+<?php extension_loaded('mbstring') or die('skip mbstring not available'); ?>
+--FILE--
+<?php
+function foo() {
+ mb_parse_str("this=42");
+ var_dump($this);
+}
+foo();
+?>
+--EXPECTF--
+Fatal error: Uncaught Error: Cannot re-assign $this in %sthis_in_mb_parse_str.php:3
+Stack trace:
+#0 %sthis_in_mb_parse_str.php(3): mb_parse_str('this=42')
+#1 %sthis_in_mb_parse_str.php(6): foo()
+#2 {main}
+ thrown in %sthis_in_mb_parse_str.php on line 3
diff --git a/Zend/tests/this_in_parse_str.phpt b/Zend/tests/this_in_parse_str.phpt
new file mode 100644
index 0000000000..4540d282cc
--- /dev/null
+++ b/Zend/tests/this_in_parse_str.phpt
@@ -0,0 +1,17 @@
+--TEST--
+$this re-assign in parse_str()
+--FILE--
+<?php
+function foo() {
+ parse_str("this=42");
+ var_dump($this);
+}
+foo();
+?>
+--EXPECTF--
+Fatal error: Uncaught Error: Cannot re-assign $this in %sthis_in_parse_str.php:3
+Stack trace:
+#0 %sthis_in_parse_str.php(3): parse_str('this=42')
+#1 %sthis_in_parse_str.php(6): foo()
+#2 {main}
+ thrown in %sthis_in_parse_str.php on line 3
diff --git a/Zend/tests/this_in_unset.phpt b/Zend/tests/this_in_unset.phpt
new file mode 100644
index 0000000000..bc815049f9
--- /dev/null
+++ b/Zend/tests/this_in_unset.phpt
@@ -0,0 +1,8 @@
+--TEST--
+$this in unset
+--FILE--
+<?php
+unset($this);
+?>
+--EXPECTF--
+Fatal error: Cannot unset $this in %sthis_in_unset.php on line 2
diff --git a/Zend/tests/this_reassign.phpt b/Zend/tests/this_reassign.phpt
new file mode 100644
index 0000000000..d965ef4701
--- /dev/null
+++ b/Zend/tests/this_reassign.phpt
@@ -0,0 +1,17 @@
+--TEST--
+$this re-assign
+--FILE--
+<?php
+function foo() {
+ $a = "this";
+ $$a = 0;
+ var_dump($$a);
+}
+foo();
+?>
+--EXPECTF--
+Fatal error: Uncaught Error: Cannot re-assign $this in %sthis_reassign.php:4
+Stack trace:
+#0 %sthis_reassign.php(7): foo()
+#1 {main}
+ thrown in %sthis_reassign.php on line 4
diff --git a/Zend/tests/traits/no_static_arg_binding.phpt b/Zend/tests/traits/no_static_arg_binding.phpt
new file mode 100644
index 0000000000..3134cf621b
--- /dev/null
+++ b/Zend/tests/traits/no_static_arg_binding.phpt
@@ -0,0 +1,28 @@
+--TEST--
+Don't statically bind arguments for self:: calls in traits
+--FILE--
+<?php
+
+trait T {
+ public static function method($arg) {
+ }
+ public static function call() {
+ $i = 0;
+ self::method($i);
+ var_dump($i);
+ }
+}
+
+class C {
+ use T;
+
+ public static function method(&$arg) {
+ $arg++;
+ }
+}
+
+C::call();
+
+?>
+--EXPECT--
+int(1)
diff --git a/Zend/tests/try/bug70228.phpt b/Zend/tests/try/bug70228.phpt
index 23f5864740..8b812517a3 100644
--- a/Zend/tests/try/bug70228.phpt
+++ b/Zend/tests/try/bug70228.phpt
@@ -1,16 +1,14 @@
--TEST--
Bug #70228 (memleak if return in finally block)
---XFAIL--
-See https://bugs.php.net/bug.php?id=70228
--FILE--
<?php
function foo() {
try { return str_repeat("a", 2); }
- finally { return true; }
+ finally { return str_repeat("b", 2); }
}
var_dump(foo());
?>
--EXPECT--
-string(3) "bar"
+string(2) "bb"
diff --git a/Zend/tests/try/bug70228_2.phpt b/Zend/tests/try/bug70228_2.phpt
new file mode 100644
index 0000000000..c988e706ce
--- /dev/null
+++ b/Zend/tests/try/bug70228_2.phpt
@@ -0,0 +1,20 @@
+--TEST--
+Bug #70228 (memleak if return in finally block)
+--FILE--
+<?php
+function test() {
+ try {
+ throw new Exception(1);
+ } finally {
+ try {
+ throw new Exception(2);
+ } finally {
+ return 42;
+ }
+ }
+}
+
+var_dump(test());
+?>
+--EXPECT--
+int(42)
diff --git a/Zend/tests/try/bug70228_3.phpt b/Zend/tests/try/bug70228_3.phpt
new file mode 100644
index 0000000000..55dbe4f891
--- /dev/null
+++ b/Zend/tests/try/bug70228_3.phpt
@@ -0,0 +1,31 @@
+--TEST--
+Bug #70228 (memleak if return in finally block)
+--FILE--
+<?php
+function test() {
+ try {
+ throw new Exception(1);
+ } finally {
+ try {
+ try {
+ } finally {
+ return 42;
+ }
+ } finally {
+ throw new Exception(2);
+ }
+ }
+}
+
+try {
+ var_dump(test());
+} catch (Exception $e) {
+ do {
+ echo $e->getMessage() . "\n";
+ $e = $e->getPrevious();
+ } while ($e);
+}
+?>
+--EXPECT--
+2
+1
diff --git a/Zend/tests/try/bug70228_4.phpt b/Zend/tests/try/bug70228_4.phpt
new file mode 100644
index 0000000000..f0ab3b0c2c
--- /dev/null
+++ b/Zend/tests/try/bug70228_4.phpt
@@ -0,0 +1,32 @@
+--TEST--
+Bug #70228 (memleak if return in finally block)
+--FILE--
+<?php
+function test() {
+ try {
+ throw new Exception(1);
+ } finally {
+ try {
+ try {
+ try {
+ } finally {
+ return 42;
+ }
+ } finally {
+ throw new Exception(3);
+ }
+ } catch (Exception $e) {}
+ }
+}
+
+try {
+ var_dump(test());
+} catch (Exception $e) {
+ do {
+ echo $e->getMessage() . "\n";
+ $e = $e->getPrevious();
+ } while ($e);
+}
+?>
+--EXPECT--
+1
diff --git a/Zend/tests/try/bug70228_5.phpt b/Zend/tests/try/bug70228_5.phpt
new file mode 100644
index 0000000000..29cbf4910d
--- /dev/null
+++ b/Zend/tests/try/bug70228_5.phpt
@@ -0,0 +1,20 @@
+--TEST--
+Bug #70228 (memleak if return hidden by throw in finally block)
+--FILE--
+<?php
+function test() {
+ try {
+ return str_repeat("a", 2);
+ } finally {
+ throw new Exception("ops");
+ }
+}
+
+try {
+ test();
+} catch (Exception $e) {
+ echo $e->getMessage(), "\n";
+}
+?>
+--EXPECT--
+ops
diff --git a/Zend/tests/try/bug70228_6.phpt b/Zend/tests/try/bug70228_6.phpt
new file mode 100644
index 0000000000..fc68657f4c
--- /dev/null
+++ b/Zend/tests/try/bug70228_6.phpt
@@ -0,0 +1,18 @@
+--TEST--
+Bug #70228 (memleak if return in finally block)
+--FILE--
+<?php
+function test($x) {
+ foreach ($x as $v) {
+ try {
+ return str_repeat("a", 2);
+ } finally {
+ return 42;
+ }
+ }
+}
+
+var_dump(test([1]));
+?>
+--EXPECT--
+int(42)
diff --git a/Zend/tests/try/bug70228_7.phpt b/Zend/tests/try/bug70228_7.phpt
new file mode 100644
index 0000000000..4b8a80351c
--- /dev/null
+++ b/Zend/tests/try/bug70228_7.phpt
@@ -0,0 +1,29 @@
+--TEST--
+Bug #70228 (memleak if return in finally block)
+--FILE--
+<?php
+
+function foo() {
+ $array = [1, 2, $n = 3];
+ foreach ($array as $value) {
+ var_dump($value);
+ try {
+ try {
+ foreach ($array as $_) {
+ return str_repeat("a", 2);
+ }
+ } finally {
+ throw new Exception;
+ }
+ } catch (Exception $e) { }
+ }
+}
+
+foo();
+?>
+===DONE===
+--EXPECT--
+int(1)
+int(2)
+int(3)
+===DONE===
diff --git a/Zend/tests/try/bug70228_8.phpt b/Zend/tests/try/bug70228_8.phpt
new file mode 100644
index 0000000000..ad13113c71
--- /dev/null
+++ b/Zend/tests/try/bug70228_8.phpt
@@ -0,0 +1,30 @@
+--TEST--
+Bug #70228 (memleak if return in finally block)
+--FILE--
+<?php
+
+function foo() {
+ $array = [1, 2, $n = 3];
+ foreach ($array as $value) {
+ var_dump($value);
+ try {
+ try {
+ switch (str_repeat("b", 2)) {
+ case "bb":
+ return str_repeat("a", 2);
+ }
+ } finally {
+ throw new Exception;
+ }
+ } catch (Exception $e) { }
+ }
+}
+
+foo();
+?>
+===DONE===
+--EXPECT--
+int(1)
+int(2)
+int(3)
+===DONE===
diff --git a/Zend/tests/try/bug71604.phpt b/Zend/tests/try/bug71604.phpt
new file mode 100644
index 0000000000..79803b93ea
--- /dev/null
+++ b/Zend/tests/try/bug71604.phpt
@@ -0,0 +1,25 @@
+--TEST--
+Bug #71604: Aborted Generators continue after nested finally
+--FILE--
+<?php
+function gen() {
+ try {
+ try {
+ yield;
+ } finally {
+ print "INNER\n";
+ }
+ } catch (Exception $e) {
+ print "EX\n";
+ } finally {
+ print "OUTER\n";
+ }
+ print "NOTREACHED\n";
+}
+
+gen()->current();
+
+?>
+--EXPECT--
+INNER
+OUTER
diff --git a/Zend/tests/try/bug71604_2.phpt b/Zend/tests/try/bug71604_2.phpt
new file mode 100644
index 0000000000..8736cd8347
--- /dev/null
+++ b/Zend/tests/try/bug71604_2.phpt
@@ -0,0 +1,39 @@
+--TEST--
+Bug #71604: Aborted Generators continue after nested finally (2)
+--FILE--
+<?php
+
+function gen() {
+ try {
+ throw new Exception(1);
+ } finally {
+ try {
+ throw new Exception(2);
+ } finally {
+ try {
+ yield;
+ } finally {
+ }
+ }
+ }
+}
+
+try {
+ gen()->rewind();
+} catch (Exception $e) {
+ echo $e, "\n";
+}
+
+?>
+--EXPECTF--
+Exception: 1 in %s:%d
+Stack trace:
+#0 [internal function]: gen()
+#1 %s(%d): Generator->rewind()
+#2 {main}
+
+Next Exception: 2 in %s:%d
+Stack trace:
+#0 [internal function]: gen()
+#1 %s(%d): Generator->rewind()
+#2 {main}
diff --git a/Zend/tests/try/bug71604_3.phpt b/Zend/tests/try/bug71604_3.phpt
new file mode 100644
index 0000000000..058c9a70a8
--- /dev/null
+++ b/Zend/tests/try/bug71604_3.phpt
@@ -0,0 +1,38 @@
+--TEST--
+Bug #71604: Aborted Generators continue after nested finally (3)
+--FILE--
+<?php
+
+function gen() {
+ try {
+ throw new Exception(1);
+ } finally {
+ try {
+ yield;
+ } finally {
+ try {
+ throw new Exception(2);
+ } finally {
+ }
+ }
+ }
+}
+
+try {
+ gen()->rewind();
+} catch (Exception $e) {
+ echo $e, "\n";
+}
+
+?>
+--EXPECTF--
+Exception: 1 in %s:%d
+Stack trace:
+#0 [internal function]: gen()
+#1 %s(%d): Generator->rewind()
+#2 {main}
+
+Next Exception: 2 in %s:%d
+Stack trace:
+#0 %s(%d): gen()
+#1 {main}
diff --git a/Zend/tests/try/bug72213.phpt b/Zend/tests/try/bug72213.phpt
new file mode 100644
index 0000000000..624050295e
--- /dev/null
+++ b/Zend/tests/try/bug72213.phpt
@@ -0,0 +1,25 @@
+--TEST--
+Bug #72213 (Finally leaks on nested exceptions)
+--FILE--
+<?php
+function test() {
+ try {
+ throw new Exception('a');
+ } finally {
+ try {
+ throw new Exception('b');
+ } finally {
+ }
+ }
+}
+
+try {
+ test();
+} catch (Exception $e) {
+ var_dump($e->getMessage());
+ var_dump($e->getPrevious()->getMessage());
+}
+?>
+--EXPECT--
+string(1) "b"
+string(1) "a"
diff --git a/Zend/tests/try/bug72213_2.phpt b/Zend/tests/try/bug72213_2.phpt
new file mode 100644
index 0000000000..790abe125d
--- /dev/null
+++ b/Zend/tests/try/bug72213_2.phpt
@@ -0,0 +1,25 @@
+--TEST--
+Bug #72213 (Finally leaks on nested exceptions)
+--FILE--
+<?php
+function test() {
+ try {
+ throw new Exception(1);
+ } finally {
+ try {
+ try {
+ throw new Exception(2);
+ } finally {
+ }
+ } catch (Exception $e) {
+ }
+ }
+}
+
+try {
+ test();
+} catch (Exception $e) {
+ echo "caught {$e->getMessage()}\n";
+}
+--EXPECT--
+caught 1
diff --git a/Zend/tests/try/exceptions.inc b/Zend/tests/try/exceptions.inc
new file mode 100644
index 0000000000..68cb1c62f7
--- /dev/null
+++ b/Zend/tests/try/exceptions.inc
@@ -0,0 +1,6 @@
+<?php
+
+class Exception1 extends Exception {}
+class Exception2 extends Exception {}
+class Exception3 extends Exception {}
+class Exception4 extends Exception {}
diff --git a/Zend/tests/try/try_finally_021.phpt b/Zend/tests/try/try_finally_021.phpt
new file mode 100644
index 0000000000..ed162f40d0
--- /dev/null
+++ b/Zend/tests/try/try_finally_021.phpt
@@ -0,0 +1,20 @@
+--TEST--
+Live range & return from finally
+--FILE--
+<?php
+$array = [1];
+foreach ([0] as $_) {
+ foreach ($array as $v) {
+ try {
+ echo "ok\n";
+ return;
+ } finally {
+ echo "ok\n";
+ return;
+ }
+ }
+}
+?>
+--EXPECT--
+ok
+ok
diff --git a/Zend/tests/try/try_finally_022.phpt b/Zend/tests/try/try_finally_022.phpt
new file mode 100644
index 0000000000..51f6a26419
--- /dev/null
+++ b/Zend/tests/try/try_finally_022.phpt
@@ -0,0 +1,41 @@
+--TEST--
+Try finally (exception in "return" statement)
+--FILE--
+<?php
+class A {
+ public $x = 1;
+ public $y = 2;
+ function __destruct() {
+ throw new Exception();
+ }
+}
+try{
+ $a = 0;
+ switch ($a) {
+ case 0:
+ }
+ switch ($a) {
+ case 0:
+ }
+ switch ($a) {
+ case 0:
+ }
+ foreach([new stdClass()] as $x) {
+ foreach(new A() as $a) {
+ foreach([new stdClass()] as $y) {
+ try {
+ if (0) { echo "0" . (int)5; }
+ return $a;
+ } catch (Exception $e) {
+ echo "exception1\n";
+ }
+ }
+ }
+ }
+} catch (Exception $e) {
+ echo "exception2\n";
+}
+?>
+--EXPECT--
+exception2
+
diff --git a/Zend/tests/try/try_finally_023.phpt b/Zend/tests/try/try_finally_023.phpt
new file mode 100644
index 0000000000..e88eddb3b2
--- /dev/null
+++ b/Zend/tests/try/try_finally_023.phpt
@@ -0,0 +1,37 @@
+--TEST--
+Loop var dtor throwing exception during return inside try/catch inside finally
+--FILE--
+<?php
+
+class Dtor {
+ public function __destruct() {
+ throw new Exception(2);
+ }
+}
+
+function test() {
+ try {
+ throw new Exception(1);
+ } finally {
+ try {
+ foreach ([new Dtor] as $v) {
+ unset($v);
+ return 42;
+ }
+ } catch (Exception $e) {
+ }
+ }
+}
+
+try {
+ test();
+} catch (Exception $e) {
+ echo $e, "\n";
+}
+
+?>
+--EXPECTF--
+Exception: 1 in %s:%d
+Stack trace:
+#0 %s(%d): test()
+#1 {main}
diff --git a/Zend/tests/try/try_finally_024.phpt b/Zend/tests/try/try_finally_024.phpt
new file mode 100644
index 0000000000..eb0b26acbf
--- /dev/null
+++ b/Zend/tests/try/try_finally_024.phpt
@@ -0,0 +1,38 @@
+--TEST--
+Exception in finally inside finally following try/catch containing throwing try/finally
+--FILE--
+<?php
+
+function test() {
+ try {
+ throw new Exception(1);
+ } finally {
+ try {
+ try {
+ } finally {
+ throw new Exception(2);
+ }
+ } catch (Exception $e) {}
+ try {
+ } finally {
+ throw new Exception(3);
+ }
+ }
+}
+
+try {
+ test();
+} catch (Exception $e) {
+ echo $e, "\n";
+}
+?>
+--EXPECTF--
+Exception: 1 in %s:%d
+Stack trace:
+#0 %s(%d): test()
+#1 {main}
+
+Next Exception: 3 in %s:%d
+Stack trace:
+#0 %s(%d): test()
+#1 {main}
diff --git a/Zend/tests/try/try_finally_025.phpt b/Zend/tests/try/try_finally_025.phpt
new file mode 100644
index 0000000000..7ca535bcf6
--- /dev/null
+++ b/Zend/tests/try/try_finally_025.phpt
@@ -0,0 +1,28 @@
+--TEST--
+Throw in try of try/finally inside catch
+--FILE--
+<?php
+
+function test() {
+ try {
+ throw new Exception(1);
+ } catch (Exception $e) {
+ try {
+ throw new Exception(2);
+ } finally {
+ }
+ }
+}
+
+try {
+ test();
+} catch (Exception $e) {
+ echo $e, "\n";
+}
+
+?>
+--EXPECTF--
+Exception: 2 in %s:%d
+Stack trace:
+#0 %s(%d): test()
+#1 {main}
diff --git a/Zend/tests/try/try_finally_026.phpt b/Zend/tests/try/try_finally_026.phpt
new file mode 100644
index 0000000000..29a5de3b3a
--- /dev/null
+++ b/Zend/tests/try/try_finally_026.phpt
@@ -0,0 +1,37 @@
+--TEST--
+Throw in finally inside catch inside finally
+--FILE--
+<?php
+
+function test() {
+ try {
+ throw new Exception(1);
+ } finally {
+ try {
+ throw new Exception(2);
+ } catch (Exception $e) {
+ try {
+ } finally {
+ throw new Exception(3);
+ }
+ }
+ }
+}
+
+try {
+ test();
+} catch (Exception $e) {
+ echo $e, "\n";
+}
+
+?>
+--EXPECTF--
+Exception: 1 in %s:%d
+Stack trace:
+#0 %s(%d): test()
+#1 {main}
+
+Next Exception: 3 in %s:%d
+Stack trace:
+#0 %s(%d): test()
+#1 {main}
diff --git a/Zend/tests/try/try_finally_027.phpt b/Zend/tests/try/try_finally_027.phpt
new file mode 100644
index 0000000000..1e66479eb0
--- /dev/null
+++ b/Zend/tests/try/try_finally_027.phpt
@@ -0,0 +1,34 @@
+--TEST--
+Return in try with throw in finally, inside other finally
+--FILE--
+<?php
+
+function test() {
+ try {
+ throw new Exception(1);
+ } finally {
+ try {
+ return 42;
+ } finally {
+ throw new Exception(2);
+ }
+ }
+}
+
+try {
+ test();
+} catch (Exception $e) {
+ echo $e, "\n";
+}
+
+?>
+--EXPECTF--
+Exception: 1 in %s:%d
+Stack trace:
+#0 %s(%d): test()
+#1 {main}
+
+Next Exception: 2 in %s:%d
+Stack trace:
+#0 %s(%d): test()
+#1 {main}
diff --git a/Zend/tests/try/try_multicatch_001.phpt b/Zend/tests/try/try_multicatch_001.phpt
new file mode 100644
index 0000000000..0dffd32c72
--- /dev/null
+++ b/Zend/tests/try/try_multicatch_001.phpt
@@ -0,0 +1,19 @@
+--TEST--
+Parsing test
+--FILE--
+<?php
+
+require_once __DIR__ . '/exceptions.inc';
+
+try {
+ echo 'TRY' . PHP_EOL;
+} catch(Exception1 | Exception2 $e) {
+ echo 'Exception';
+} finally {
+ echo 'FINALLY' . PHP_EOL;
+}
+
+?>
+--EXPECT--
+TRY
+FINALLY
diff --git a/Zend/tests/try/try_multicatch_002.phpt b/Zend/tests/try/try_multicatch_002.phpt
new file mode 100644
index 0000000000..0e70fec7eb
--- /dev/null
+++ b/Zend/tests/try/try_multicatch_002.phpt
@@ -0,0 +1,21 @@
+--TEST--
+Catch first exception in the multicatch
+--FILE--
+<?php
+
+require_once __DIR__ . '/exceptions.inc';
+
+try {
+ echo 'TRY' . PHP_EOL;
+ throw new Exception1;
+} catch(Exception1 | Exception2 | Exception3 $e) {
+ echo get_class($e) . PHP_EOL;
+} finally {
+ echo 'FINALLY' . PHP_EOL;
+}
+
+?>
+--EXPECT--
+TRY
+Exception1
+FINALLY
diff --git a/Zend/tests/try/try_multicatch_003.phpt b/Zend/tests/try/try_multicatch_003.phpt
new file mode 100644
index 0000000000..6aed1a2b09
--- /dev/null
+++ b/Zend/tests/try/try_multicatch_003.phpt
@@ -0,0 +1,21 @@
+--TEST--
+Catch second exception in the multicatch
+--FILE--
+<?php
+
+require_once __DIR__ . '/exceptions.inc';
+
+try {
+ echo 'TRY' . PHP_EOL;
+ throw new Exception2;
+} catch(Exception1 | Exception2 | Exception3 $e) {
+ echo get_class($e) . PHP_EOL;
+} finally {
+ echo 'FINALLY' . PHP_EOL;
+}
+
+?>
+--EXPECT--
+TRY
+Exception2
+FINALLY
diff --git a/Zend/tests/try/try_multicatch_004.phpt b/Zend/tests/try/try_multicatch_004.phpt
new file mode 100644
index 0000000000..d8b245a767
--- /dev/null
+++ b/Zend/tests/try/try_multicatch_004.phpt
@@ -0,0 +1,21 @@
+--TEST--
+Catch last exception in the multicatch
+--FILE--
+<?php
+
+require_once __DIR__ . '/exceptions.inc';
+
+try {
+ echo 'TRY' . PHP_EOL;
+ throw new Exception3;
+} catch(Exception1 | Exception2 | Exception3 $e) {
+ echo get_class($e) . PHP_EOL;
+} finally {
+ echo 'FINALLY' . PHP_EOL;
+}
+
+?>
+--EXPECT--
+TRY
+Exception3
+FINALLY
diff --git a/Zend/tests/try/try_multicatch_005.phpt b/Zend/tests/try/try_multicatch_005.phpt
new file mode 100644
index 0000000000..cc3fc890fa
--- /dev/null
+++ b/Zend/tests/try/try_multicatch_005.phpt
@@ -0,0 +1,25 @@
+--TEST--
+Catch exception in the nested multicatch
+--FILE--
+<?php
+
+require_once __DIR__ . '/exceptions.inc';
+
+try {
+ try {
+ echo 'TRY' . PHP_EOL;
+ throw new Exception3;
+ } catch (Exception1 | Exception3 $e) {
+ echo get_class($e) . PHP_EOL;
+ }
+} catch(Exception2 | Exception3 $e) {
+ echo 'Should never be executed';
+} finally {
+ echo 'FINALLY' . PHP_EOL;
+}
+
+?>
+--EXPECT--
+TRY
+Exception3
+FINALLY
diff --git a/Zend/tests/try/try_multicatch_006.phpt b/Zend/tests/try/try_multicatch_006.phpt
new file mode 100644
index 0000000000..e4505f1b24
--- /dev/null
+++ b/Zend/tests/try/try_multicatch_006.phpt
@@ -0,0 +1,22 @@
+--TEST--
+Catch first exception in the second multicatch
+--FILE--
+<?php
+require_once __DIR__ . '/exceptions.inc';
+
+try {
+ echo 'TRY' . PHP_EOL;
+ throw new Exception3;
+} catch(Exception1 | Exception2 $e) {
+ echo get_class($e) . PHP_EOL;
+} catch(Exception3 | Exception4 $e) {
+ echo get_class($e) . PHP_EOL;
+} finally {
+ echo 'FINALLY' . PHP_EOL;
+}
+?>
+--EXPECT--
+TRY
+Exception3
+FINALLY
+
diff --git a/Zend/tests/try/try_multicatch_007.phpt b/Zend/tests/try/try_multicatch_007.phpt
new file mode 100644
index 0000000000..aa073b0592
--- /dev/null
+++ b/Zend/tests/try/try_multicatch_007.phpt
@@ -0,0 +1,22 @@
+--TEST--
+Catch second exception in the second multicatch
+--FILE--
+<?php
+require_once __DIR__ . '/exceptions.inc';
+
+try {
+ echo 'TRY' . PHP_EOL;
+ throw new Exception4;
+} catch(Exception1 | Exception2 $e) {
+ echo get_class($e) . PHP_EOL;
+} catch(Exception3 | Exception4 $e) {
+ echo get_class($e) . PHP_EOL;
+} finally {
+ echo 'FINALLY' . PHP_EOL;
+}
+?>
+--EXPECT--
+TRY
+Exception4
+FINALLY
+
diff --git a/Zend/tests/type_declarations/nullable_typed_return_without_value.phpt b/Zend/tests/type_declarations/nullable_typed_return_without_value.phpt
new file mode 100644
index 0000000000..b16e7c6434
--- /dev/null
+++ b/Zend/tests/type_declarations/nullable_typed_return_without_value.phpt
@@ -0,0 +1,14 @@
+--TEST--
+Nullable typed return without value generates friendlier error message
+--FILE--
+<?php
+
+function test() : ?int {
+ return;
+}
+
+test();
+
+?>
+--EXPECTF--
+Fatal error: A function with return type must return a value (did you mean "return null;" instead of "return;"?) in %s on line %d
diff --git a/Zend/tests/type_declarations/nullable_void.phpt b/Zend/tests/type_declarations/nullable_void.phpt
new file mode 100644
index 0000000000..4ff0edb0d8
--- /dev/null
+++ b/Zend/tests/type_declarations/nullable_void.phpt
@@ -0,0 +1,11 @@
+--TEST--
+Void cannot be nullable
+--FILE--
+<?php
+
+function test() : ?void {
+}
+
+?>
+--EXPECTF--
+Fatal error: Void type cannot be nullable in %s on line %d
diff --git a/Zend/tests/type_declarations/scalar_none.phpt b/Zend/tests/type_declarations/scalar_none.phpt
index 3bec609599..025276adef 100644
--- a/Zend/tests/type_declarations/scalar_none.phpt
+++ b/Zend/tests/type_declarations/scalar_none.phpt
@@ -28,20 +28,20 @@ foreach ($functions as $type => $function) {
echo "Testing $type:", PHP_EOL;
try {
var_dump($function());
- } catch (TypeError $e) {
+ } catch (Throwable $e) {
echo "*** Caught " . $e->getMessage() . PHP_EOL;
}
}
echo PHP_EOL . "Done";
--EXPECTF--
Testing int:
-*** Caught Argument 1 passed to {closure}() must be of the type integer, none given, called in %s on line %d
+*** Caught Too few arguments to function {closure}(), 0 passed in %s on line %d and exactly 1 expected
Testing float:
-*** Caught Argument 1 passed to {closure}() must be of the type float, none given, called in %s on line %d
+*** Caught Too few arguments to function {closure}(), 0 passed in %s on line %d and exactly 1 expected
Testing string:
-*** Caught Argument 1 passed to {closure}() must be of the type string, none given, called in %s on line %d
+*** Caught Too few arguments to function {closure}(), 0 passed in %s on line %d and exactly 1 expected
Testing bool:
-*** Caught Argument 1 passed to {closure}() must be of the type boolean, none given, called in %s on line %d
+*** Caught Too few arguments to function {closure}(), 0 passed in %s on line %d and exactly 1 expected
Testing int nullable:
NULL
Testing float nullable:
diff --git a/Zend/tests/type_declarations/typed_return_without_value.phpt b/Zend/tests/type_declarations/typed_return_without_value.phpt
new file mode 100644
index 0000000000..1bf932becb
--- /dev/null
+++ b/Zend/tests/type_declarations/typed_return_without_value.phpt
@@ -0,0 +1,14 @@
+--TEST--
+Typed return without value generates compile-time error
+--FILE--
+<?php
+
+function test() : int {
+ return;
+}
+
+test();
+
+?>
+--EXPECTF--
+Fatal error: A function with return type must return a value in %s on line %d
diff --git a/Zend/tests/variadic/adding_additional_optional_parameter_error.phpt b/Zend/tests/variadic/adding_additional_optional_parameter_error.phpt
index da96264609..4c6f36f10b 100644
--- a/Zend/tests/variadic/adding_additional_optional_parameter_error.phpt
+++ b/Zend/tests/variadic/adding_additional_optional_parameter_error.phpt
@@ -13,4 +13,4 @@ class MySQL implements DB {
?>
--EXPECTF--
-Fatal error: Declaration of MySQL::query($query, int $extraParam = NULL, string ...$params) must be compatible with DB::query($query, string ...$params) in %s on line %d
+Fatal error: Declaration of MySQL::query($query, ?int $extraParam = NULL, string ...$params) must be compatible with DB::query($query, string ...$params) in %s on line %d
diff --git a/Zend/zend.c b/Zend/zend.c
index d036152307..42cd65ed9c 100644
--- a/Zend/zend.c
+++ b/Zend/zend.c
@@ -31,6 +31,7 @@
#include "zend_vm.h"
#include "zend_dtrace.h"
#include "zend_virtual_cwd.h"
+#include "zend_smart_str.h"
#ifdef ZTS
# define GLOBAL_FUNCTION_TABLE global_function_table
@@ -159,7 +160,9 @@ static uint zend_version_info_length;
#define ZEND_CORE_VERSION_INFO "Zend Engine v" ZEND_VERSION ", Copyright (c) 1998-2016 Zend Technologies\n"
#define PRINT_ZVAL_INDENT 4
-static void print_hash(zend_write_func_t write_func, HashTable *ht, int indent, zend_bool is_object) /* {{{ */
+static void zend_print_zval_r_to_buf(smart_str *buf, zval *expr, int indent);
+
+static void print_hash(smart_str *buf, HashTable *ht, int indent, zend_bool is_object) /* {{{ */
{
zval *tmp;
zend_string *string_key;
@@ -167,54 +170,46 @@ static void print_hash(zend_write_func_t write_func, HashTable *ht, int indent,
int i;
for (i = 0; i < indent; i++) {
- ZEND_PUTS_EX(" ");
+ smart_str_appendc(buf, ' ');
}
- ZEND_PUTS_EX("(\n");
+ smart_str_appends(buf, "(\n");
indent += PRINT_ZVAL_INDENT;
- ZEND_HASH_FOREACH_KEY_VAL(ht, num_key, string_key, tmp) {
- if (Z_TYPE_P(tmp) == IS_INDIRECT) {
- tmp = Z_INDIRECT_P(tmp);
- if (Z_TYPE_P(tmp) == IS_UNDEF) {
- continue;
- }
- }
+ ZEND_HASH_FOREACH_KEY_VAL_IND(ht, num_key, string_key, tmp) {
for (i = 0; i < indent; i++) {
- ZEND_PUTS_EX(" ");
+ smart_str_appendc(buf, ' ');
}
- ZEND_PUTS_EX("[");
+ smart_str_appendc(buf, '[');
if (string_key) {
if (is_object) {
const char *prop_name, *class_name;
size_t prop_len;
int mangled = zend_unmangle_property_name_ex(string_key, &class_name, &prop_name, &prop_len);
- ZEND_WRITE_EX(prop_name, prop_len);
+ smart_str_appendl(buf, prop_name, prop_len);
if (class_name && mangled == SUCCESS) {
- if (class_name[0]=='*') {
- ZEND_PUTS_EX(":protected");
+ if (class_name[0] == '*') {
+ smart_str_appends(buf, ":protected");
} else {
- ZEND_PUTS_EX(":");
- ZEND_PUTS_EX(class_name);
- ZEND_PUTS_EX(":private");
+ smart_str_appends(buf, ":");
+ smart_str_appends(buf, class_name);
+ smart_str_appends(buf, ":private");
}
}
} else {
- ZEND_WRITE_EX(ZSTR_VAL(string_key), ZSTR_LEN(string_key));
+ smart_str_append(buf, string_key);
}
} else {
- char key[25];
- snprintf(key, sizeof(key), ZEND_LONG_FMT, num_key);
- ZEND_PUTS_EX(key);
+ smart_str_append_long(buf, num_key);
}
- ZEND_PUTS_EX("] => ");
- zend_print_zval_r_ex(write_func, tmp, indent+PRINT_ZVAL_INDENT);
- ZEND_PUTS_EX("\n");
+ smart_str_appends(buf, "] => ");
+ zend_print_zval_r_to_buf(buf, tmp, indent+PRINT_ZVAL_INDENT);
+ smart_str_appends(buf, "\n");
} ZEND_HASH_FOREACH_END();
indent -= PRINT_ZVAL_INDENT;
for (i = 0; i < indent; i++) {
- ZEND_PUTS_EX(" ");
+ smart_str_appendc(buf, ' ');
}
- ZEND_PUTS_EX(")\n");
+ smart_str_appends(buf, ")\n");
}
/* }}} */
@@ -254,17 +249,11 @@ ZEND_API int zend_make_printable_zval(zval *expr, zval *expr_copy) /* {{{ */
ZEND_API size_t zend_print_zval(zval *expr, int indent) /* {{{ */
{
- return zend_print_zval_ex(zend_write, expr, indent);
-}
-/* }}} */
-
-ZEND_API size_t zend_print_zval_ex(zend_write_func_t write_func, zval *expr, int indent) /* {{{ */
-{
zend_string *str = zval_get_string(expr);
size_t len = ZSTR_LEN(str);
if (len != 0) {
- write_func(ZSTR_VAL(str), len);
+ zend_write(ZSTR_VAL(str), len);
}
zend_string_release(str);
@@ -319,25 +308,19 @@ ZEND_API void zend_print_flat_zval_r(zval *expr) /* {{{ */
}
/* }}} */
-ZEND_API void zend_print_zval_r(zval *expr, int indent) /* {{{ */
-{
- zend_print_zval_r_ex(zend_write, expr, indent);
-}
-/* }}} */
-
-ZEND_API void zend_print_zval_r_ex(zend_write_func_t write_func, zval *expr, int indent) /* {{{ */
+static void zend_print_zval_r_to_buf(smart_str *buf, zval *expr, int indent) /* {{{ */
{
ZVAL_DEREF(expr);
switch (Z_TYPE_P(expr)) {
case IS_ARRAY:
- ZEND_PUTS_EX("Array\n");
+ smart_str_appends(buf, "Array\n");
if (ZEND_HASH_APPLY_PROTECTION(Z_ARRVAL_P(expr)) &&
++Z_ARRVAL_P(expr)->u.v.nApplyCount>1) {
- ZEND_PUTS_EX(" *RECURSION*");
+ smart_str_appends(buf, " *RECURSION*");
Z_ARRVAL_P(expr)->u.v.nApplyCount--;
return;
}
- print_hash(write_func, Z_ARRVAL_P(expr), indent, 0);
+ print_hash(buf, Z_ARRVAL_P(expr), indent, 0);
if (ZEND_HASH_APPLY_PROTECTION(Z_ARRVAL_P(expr))) {
Z_ARRVAL_P(expr)->u.v.nApplyCount--;
}
@@ -348,12 +331,12 @@ ZEND_API void zend_print_zval_r_ex(zend_write_func_t write_func, zval *expr, int
int is_temp;
zend_string *class_name = Z_OBJ_HANDLER_P(expr, get_class_name)(Z_OBJ_P(expr));
- ZEND_PUTS_EX(ZSTR_VAL(class_name));
+ smart_str_appends(buf, ZSTR_VAL(class_name));
zend_string_release(class_name);
- ZEND_PUTS_EX(" Object\n");
+ smart_str_appends(buf, " Object\n");
if (Z_OBJ_APPLY_COUNT_P(expr) > 0) {
- ZEND_PUTS_EX(" *RECURSION*");
+ smart_str_appends(buf, " *RECURSION*");
return;
}
if ((properties = Z_OBJDEBUG_P(expr, is_temp)) == NULL) {
@@ -361,7 +344,7 @@ ZEND_API void zend_print_zval_r_ex(zend_write_func_t write_func, zval *expr, int
}
Z_OBJ_INC_APPLY_COUNT_P(expr);
- print_hash(write_func, properties, indent, 1);
+ print_hash(buf, properties, indent, 1);
Z_OBJ_DEC_APPLY_COUNT_P(expr);
if (is_temp) {
@@ -370,13 +353,37 @@ ZEND_API void zend_print_zval_r_ex(zend_write_func_t write_func, zval *expr, int
}
break;
}
+ case IS_LONG:
+ smart_str_append_long(buf, Z_LVAL_P(expr));
+ break;
default:
- zend_print_zval_ex(write_func, expr, indent);
+ {
+ zend_string *str = zval_get_string(expr);
+ smart_str_append(buf, str);
+ zend_string_release(str);
+ }
break;
}
}
/* }}} */
+ZEND_API zend_string *zend_print_zval_r_to_str(zval *expr, int indent) /* {{{ */
+{
+ smart_str buf = {0};
+ zend_print_zval_r_to_buf(&buf, expr, indent);
+ smart_str_0(&buf);
+ return buf.s;
+}
+/* }}} */
+
+ZEND_API void zend_print_zval_r(zval *expr, int indent) /* {{{ */
+{
+ zend_string *str = zend_print_zval_r_to_str(expr, indent);
+ zend_write(ZSTR_VAL(str), ZSTR_LEN(str));
+ zend_string_release(str);
+}
+/* }}} */
+
static FILE *zend_fopen_wrapper(const char *filename, zend_string **opened_path) /* {{{ */
{
if (opened_path) {
@@ -501,9 +508,11 @@ static void compiler_globals_ctor(zend_compiler_globals *compiler_globals) /* {{
}
compiler_globals->script_encoding_list = NULL;
- zend_interned_empty_string_init(&compiler_globals->empty_string);
+ compiler_globals->empty_string = zend_zts_interned_string_init("", sizeof("")-1);
memset(compiler_globals->one_char_string, 0, sizeof(compiler_globals->one_char_string));
+
+ zend_known_interned_strings_init(&compiler_globals->known_strings, &compiler_globals->known_strings_count);
}
/* }}} */
@@ -529,7 +538,10 @@ static void compiler_globals_dtor(zend_compiler_globals *compiler_globals) /* {{
}
compiler_globals->last_static_member = 0;
- zend_interned_empty_string_free(&compiler_globals->empty_string);
+ zend_zts_interned_string_free(&compiler_globals->empty_string);
+
+ compiler_globals->known_strings = NULL;
+ compiler_globals->known_strings_count = 0;
}
/* }}} */
@@ -622,7 +634,7 @@ static zend_bool php_auto_globals_create_globals(zend_string *name) /* {{{ */
zval globals;
ZVAL_ARR(&globals, &EG(symbol_table));
- Z_TYPE_INFO_P(&globals) = IS_ARRAY | (IS_TYPE_SYMBOLTABLE << Z_TYPE_FLAGS_SHIFT);
+ Z_TYPE_INFO_P(&globals) = IS_ARRAY;
ZVAL_NEW_REF(&globals, &globals);
zend_hash_update(&EG(symbol_table), name, &globals);
return 0;
diff --git a/Zend/zend.h b/Zend/zend.h
index ebaddd74b6..42130cadf8 100644
--- a/Zend/zend.h
+++ b/Zend/zend.h
@@ -22,7 +22,7 @@
#ifndef ZEND_H
#define ZEND_H
-#define ZEND_VERSION "3.0.0"
+#define ZEND_VERSION "3.1.0-dev"
#define ZEND_ENGINE_3
@@ -39,6 +39,7 @@
#include "zend_variables.h"
#include "zend_iterators.h"
#include "zend_stream.h"
+#include "zend_smart_str_public.h"
#ifdef ZEND_SIGNALS
# include "zend_signal.h"
@@ -61,7 +62,7 @@
#define USED_RET() \
(!EX(prev_execute_data) || \
!ZEND_USER_CODE(EX(prev_execute_data)->func->common.type) || \
- !(EX(prev_execute_data)->opline->result_type & EXT_TYPE_UNUSED))
+ (EX(prev_execute_data)->opline->result_type != IS_UNUSED))
#ifdef ZEND_ENABLE_STATIC_TSRMLS_CACHE
#define ZEND_TSRMG TSRMG_STATIC
@@ -89,11 +90,6 @@ ZEND_API ZEND_COLD ZEND_NORETURN void zend_error_noreturn(int type, const char *
# define zend_error_noreturn zend_error
#endif
-/* overloaded elements data types */
-#define OE_IS_ARRAY (1<<0)
-#define OE_IS_OBJECT (1<<1)
-#define OE_IS_METHOD (1<<2)
-
struct _zend_serialize_data;
struct _zend_unserialize_data;
@@ -248,10 +244,9 @@ ZEND_API ZEND_COLD void _zend_bailout(char *filename, uint lineno);
ZEND_API char *get_zend_version(void);
ZEND_API int zend_make_printable_zval(zval *expr, zval *expr_copy);
ZEND_API size_t zend_print_zval(zval *expr, int indent);
-ZEND_API size_t zend_print_zval_ex(zend_write_func_t write_func, zval *expr, int indent);
ZEND_API void zend_print_zval_r(zval *expr, int indent);
+ZEND_API zend_string *zend_print_zval_r_to_str(zval *expr, int indent);
ZEND_API void zend_print_flat_zval_r(zval *expr);
-ZEND_API void zend_print_zval_r_ex(zend_write_func_t write_func, zval *expr, int indent);
ZEND_API ZEND_COLD void zend_output_debug_string(zend_bool trigger_break, const char *format, ...) ZEND_ATTRIBUTE_FORMAT(printf, 2, 3);
ZEND_API void zend_activate(void);
diff --git a/Zend/zend_API.c b/Zend/zend_API.c
index a488dba52f..18d8382934 100644
--- a/Zend/zend_API.c
+++ b/Zend/zend_API.c
@@ -184,6 +184,8 @@ ZEND_API char *zend_get_type_by_const(int type) /* {{{ */
return "callable";
case IS_ARRAY:
return "array";
+ case IS_VOID:
+ return "void";
default:
return "unknown";
}
@@ -198,7 +200,7 @@ ZEND_API char *zend_zval_type_name(const zval *arg) /* {{{ */
/* }}} */
#ifdef FAST_ZPP
-ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_paramers_count_error(int num_args, int min_num_args, int max_num_args) /* {{{ */
+ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_parameters_count_error(int num_args, int min_num_args, int max_num_args) /* {{{ */
{
zend_function *active_function = EG(current_execute_data)->func;
const char *class_name = active_function->common.scope ? ZSTR_VAL(active_function->common.scope->name) : "";
@@ -214,7 +216,7 @@ ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_paramers_count_error(int num_ar
}
/* }}} */
-ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_paramer_type_error(int num, zend_expected_type expected_type, zval *arg) /* {{{ */
+ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_parameter_type_error(int num, zend_expected_type expected_type, zval *arg) /* {{{ */
{
const char *space;
const char *class_name = get_active_class_name(&space);
@@ -228,7 +230,7 @@ ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_paramer_type_error(int num, zen
}
/* }}} */
-ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_paramer_class_error(int num, char *name, zval *arg) /* {{{ */
+ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_parameter_class_error(int num, char *name, zval *arg) /* {{{ */
{
const char *space;
const char *class_name = get_active_class_name(&space);
@@ -368,11 +370,7 @@ ZEND_API int ZEND_FASTCALL zend_parse_arg_long_cap_weak(zval *arg, zend_long *de
if (UNEXPECTED(zend_isnan(Z_DVAL_P(arg)))) {
return 0;
}
- if (UNEXPECTED(!ZEND_DOUBLE_FITS_LONG(Z_DVAL_P(arg)))) {
- *dest = (Z_DVAL_P(arg) > 0) ? ZEND_LONG_MAX : ZEND_LONG_MIN;
- } else {
- *dest = zend_dval_to_lval(Z_DVAL_P(arg));
- }
+ *dest = zend_dval_to_lval_cap(Z_DVAL_P(arg));
} else if (EXPECTED(Z_TYPE_P(arg) == IS_STRING)) {
double d;
int type;
@@ -382,11 +380,7 @@ ZEND_API int ZEND_FASTCALL zend_parse_arg_long_cap_weak(zval *arg, zend_long *de
if (UNEXPECTED(zend_isnan(d))) {
return 0;
}
- if (UNEXPECTED(!ZEND_DOUBLE_FITS_LONG(d))) {
- *dest = (d > 0) ? ZEND_LONG_MAX : ZEND_LONG_MIN;
- } else {
- *dest = zend_dval_to_lval(d);
- }
+ *dest = zend_dval_to_lval_cap(d);
} else {
return 0;
}
@@ -501,6 +495,7 @@ static const char *zend_parse_arg_impl(int arg_num, zval *arg, va_list *va, cons
const char *spec_walk = *spec;
char c = *spec_walk++;
int check_null = 0;
+ int separate = 0;
zval *real_arg = arg;
/* scan through modifiers */
@@ -509,6 +504,7 @@ static const char *zend_parse_arg_impl(int arg_num, zval *arg, va_list *va, cons
if (*spec_walk == '/') {
SEPARATE_ZVAL_NOREF(arg);
real_arg = arg;
+ separate = 1;
} else if (*spec_walk == '!') {
check_null = 1;
} else {
@@ -628,7 +624,7 @@ static const char *zend_parse_arg_impl(int arg_num, zval *arg, va_list *va, cons
{
HashTable **p = va_arg(*va, HashTable **);
- if (!zend_parse_arg_array_ht(arg, p, check_null, c == 'H')) {
+ if (!zend_parse_arg_array_ht(arg, p, check_null, c == 'H', separate)) {
return "array";
}
}
@@ -1069,11 +1065,11 @@ ZEND_API int _array_init(zval *arg, uint32_t size ZEND_FILE_LINE_DC) /* {{{ */
ZEND_API void zend_merge_properties(zval *obj, HashTable *properties) /* {{{ */
{
const zend_object_handlers *obj_ht = Z_OBJ_HT_P(obj);
- zend_class_entry *old_scope = EG(scope);
+ zend_class_entry *old_scope = EG(fake_scope);
zend_string *key;
zval *value;
- EG(scope) = Z_OBJCE_P(obj);
+ EG(fake_scope) = Z_OBJCE_P(obj);
ZEND_HASH_FOREACH_STR_KEY_VAL(properties, key, value) {
if (key) {
zval member;
@@ -1082,7 +1078,7 @@ ZEND_API void zend_merge_properties(zval *obj, HashTable *properties) /* {{{ */
obj_ht->write_property(obj, &member, value, NULL);
}
} ZEND_HASH_FOREACH_END();
- EG(scope) = old_scope;
+ EG(fake_scope) = old_scope;
}
/* }}} */
@@ -1125,17 +1121,15 @@ ZEND_API int zend_update_class_constants(zend_class_entry *class_type) /* {{{ */
}
}
} 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;
+ zend_class_constant *c;
zval *val;
zend_property_info *prop_info;
- *scope = class_type;
- ZEND_HASH_FOREACH_VAL(&class_type->constants_table, val) {
- ZVAL_DEREF(val);
+ ZEND_HASH_FOREACH_PTR(&class_type->constants_table, c) {
+ val = &c->value;
if (Z_CONSTANT_P(val)) {
- if (UNEXPECTED(zval_update_constant_ex(val, 1, class_type) != SUCCESS)) {
+ if (UNEXPECTED(zval_update_constant_ex(val, class_type) != SUCCESS)) {
return FAILURE;
}
}
@@ -1152,8 +1146,7 @@ ZEND_API int zend_update_class_constants(zend_class_entry *class_type) /* {{{ */
}
ZVAL_DEREF(val);
if (Z_CONSTANT_P(val)) {
- *scope = ce;
- if (UNEXPECTED(zval_update_constant_ex(val, 1, NULL) != SUCCESS)) {
+ if (UNEXPECTED(zval_update_constant_ex(val, ce) != SUCCESS)) {
return FAILURE;
}
}
@@ -1161,8 +1154,6 @@ ZEND_API int zend_update_class_constants(zend_class_entry *class_type) /* {{{ */
} ZEND_HASH_FOREACH_END();
ce = ce->parent;
}
-
- *scope = old_scope;
}
}
return SUCCESS;
@@ -1194,11 +1185,11 @@ ZEND_API void object_properties_init_ex(zend_object *object, HashTable *properti
{
object->properties = properties;
if (object->ce->default_properties_count) {
- zval *prop;
- zend_string *key;
- zend_property_info *property_info;
+ zval *prop;
+ zend_string *key;
+ zend_property_info *property_info;
- ZEND_HASH_FOREACH_STR_KEY_VAL(properties, key, prop) {
+ ZEND_HASH_FOREACH_STR_KEY_VAL(properties, key, prop) {
property_info = zend_get_property_info(object->ce, key, 1);
if (property_info != ZEND_WRONG_PROPERTY_INFO &&
property_info &&
@@ -1226,15 +1217,15 @@ ZEND_API void object_properties_load(zend_object *object, HashTable *properties)
size_t prop_name_len;
if (zend_unmangle_property_name_ex(key, &class_name, &prop_name, &prop_name_len) == SUCCESS) {
zend_string *pname = zend_string_init(prop_name, prop_name_len, 0);
- zend_class_entry *prev_scope = EG(scope);
+ zend_class_entry *prev_scope = EG(fake_scope);
if (class_name && class_name[0] != '*') {
zend_string *cname = zend_string_init(class_name, strlen(class_name), 0);
- EG(scope) = zend_lookup_class(cname);
+ EG(fake_scope) = zend_lookup_class(cname);
zend_string_release(cname);
}
property_info = zend_get_property_info(object->ce, pname, 1);
zend_string_release(pname);
- EG(scope) = prev_scope;
+ EG(fake_scope) = prev_scope;
} else {
property_info = ZEND_WRONG_PROPERTY_INFO;
}
@@ -2824,7 +2815,7 @@ ZEND_API int zend_disable_class(char *class_name, size_t class_name_length) /* {
}
/* }}} */
-static int zend_is_callable_check_class(zend_string *name, zend_fcall_info_cache *fcc, int *strict_class, char **error) /* {{{ */
+static int zend_is_callable_check_class(zend_string *name, zend_class_entry *scope, zend_fcall_info_cache *fcc, int *strict_class, char **error) /* {{{ */
{
int ret = 0;
zend_class_entry *ce;
@@ -2837,24 +2828,24 @@ static int zend_is_callable_check_class(zend_string *name, zend_fcall_info_cache
*strict_class = 0;
if (zend_string_equals_literal(lcname, "self")) {
- if (!EG(scope)) {
+ if (!scope) {
if (error) *error = estrdup("cannot access self:: when no class scope is active");
} else {
fcc->called_scope = zend_get_called_scope(EG(current_execute_data));
- fcc->calling_scope = EG(scope);
+ fcc->calling_scope = scope;
if (!fcc->object) {
fcc->object = zend_get_this_object(EG(current_execute_data));
}
ret = 1;
}
} else if (zend_string_equals_literal(lcname, "parent")) {
- if (!EG(scope)) {
+ if (!scope) {
if (error) *error = estrdup("cannot access parent:: when no class scope is active");
- } else if (!EG(scope)->parent) {
+ } else if (!scope->parent) {
if (error) *error = estrdup("cannot access parent:: when current class scope has no parent");
} else {
fcc->called_scope = zend_get_called_scope(EG(current_execute_data));
- fcc->calling_scope = EG(scope)->parent;
+ fcc->calling_scope = scope->parent;
if (!fcc->object) {
fcc->object = zend_get_this_object(EG(current_execute_data));
}
@@ -2916,9 +2907,9 @@ static int zend_is_callable_check_func(int check_flags, zval *callable, zend_fca
zend_string *lmname;
const char *colon;
size_t clen, mlen;
- zend_class_entry *last_scope;
HashTable *ftable;
int call_via_handler = 0;
+ zend_class_entry *scope;
ALLOCA_FLAG(use_heap)
if (error) {
@@ -2979,19 +2970,18 @@ static int zend_is_callable_check_func(int check_flags, zval *callable, zend_fca
/* This is a compound name.
* Try to fetch class and then find static method. */
- last_scope = EG(scope);
if (ce_org) {
- EG(scope) = ce_org;
+ scope = ce_org;
+ } else {
+ scope = zend_get_executed_scope();
}
cname = zend_string_init(Z_STRVAL_P(callable), clen, 0);
- if (!zend_is_callable_check_class(cname, fcc, &strict_class, error)) {
+ if (!zend_is_callable_check_class(cname, scope, fcc, &strict_class, error)) {
zend_string_release(cname);
- EG(scope) = last_scope;
return 0;
}
zend_string_release(cname);
- EG(scope) = last_scope;
ftable = &fcc->calling_scope->function_table;
if (ce_org && !instanceof_function(ce_org, fcc->calling_scope)) {
@@ -3025,14 +3015,17 @@ static int zend_is_callable_check_func(int check_flags, zval *callable, zend_fca
} else if ((fcc->function_handler = zend_hash_find_ptr(ftable, lmname)) != NULL) {
retval = 1;
if ((fcc->function_handler->op_array.fn_flags & ZEND_ACC_CHANGED) &&
- !strict_class && EG(scope) &&
- instanceof_function(fcc->function_handler->common.scope, EG(scope))) {
- zend_function *priv_fbc;
-
- if ((priv_fbc = zend_hash_find_ptr(&EG(scope)->function_table, lmname)) != NULL
- && priv_fbc->common.fn_flags & ZEND_ACC_PRIVATE
- && priv_fbc->common.scope == EG(scope)) {
- fcc->function_handler = priv_fbc;
+ !strict_class) {
+ scope = zend_get_executed_scope();
+ if (scope &&
+ instanceof_function(fcc->function_handler->common.scope, scope)) {
+ zend_function *priv_fbc;
+
+ if ((priv_fbc = zend_hash_find_ptr(&scope->function_table, lmname)) != NULL
+ && priv_fbc->common.fn_flags & ZEND_ACC_PRIVATE
+ && priv_fbc->common.scope == scope) {
+ fcc->function_handler = priv_fbc;
+ }
}
}
if ((check_flags & IS_CALLABLE_CHECK_NO_ACCESS) == 0 &&
@@ -3040,13 +3033,15 @@ static int zend_is_callable_check_func(int check_flags, zval *callable, zend_fca
((fcc->object && fcc->calling_scope->__call) ||
(!fcc->object && fcc->calling_scope->__callstatic)))) {
if (fcc->function_handler->op_array.fn_flags & ZEND_ACC_PRIVATE) {
- if (!zend_check_private(fcc->function_handler, fcc->object ? fcc->object->ce : EG(scope), lmname)) {
+ scope = zend_get_executed_scope();
+ if (!zend_check_private(fcc->function_handler, fcc->object ? fcc->object->ce : scope, lmname)) {
retval = 0;
fcc->function_handler = NULL;
goto get_function_via_handler;
}
} else if (fcc->function_handler->common.fn_flags & ZEND_ACC_PROTECTED) {
- if (!zend_check_protected(fcc->function_handler->common.scope, EG(scope))) {
+ scope = zend_get_executed_scope();
+ if (!zend_check_protected(fcc->function_handler->common.scope, scope)) {
retval = 0;
fcc->function_handler = NULL;
goto get_function_via_handler;
@@ -3137,7 +3132,8 @@ get_function_via_handler:
}
if (retval && (check_flags & IS_CALLABLE_CHECK_NO_ACCESS) == 0) {
if (fcc->function_handler->op_array.fn_flags & ZEND_ACC_PRIVATE) {
- if (!zend_check_private(fcc->function_handler, fcc->object ? fcc->object->ce : EG(scope), lmname)) {
+ scope = zend_get_executed_scope();
+ if (!zend_check_private(fcc->function_handler, fcc->object ? fcc->object->ce : scope, lmname)) {
if (error) {
if (*error) {
efree(*error);
@@ -3147,7 +3143,8 @@ get_function_via_handler:
retval = 0;
}
} else if ((fcc->function_handler->common.fn_flags & ZEND_ACC_PROTECTED)) {
- if (!zend_check_protected(fcc->function_handler->common.scope, EG(scope))) {
+ scope = zend_get_executed_scope();
+ if (!zend_check_protected(fcc->function_handler->common.scope, scope)) {
if (error) {
if (*error) {
efree(*error);
@@ -3284,7 +3281,7 @@ again:
return 1;
}
- if (!zend_is_callable_check_class(Z_STR_P(obj), fcc, &strict_class, error)) {
+ if (!zend_is_callable_check_class(Z_STR_P(obj), zend_get_executed_scope(), fcc, &strict_class, error)) {
return 0;
}
@@ -3418,14 +3415,12 @@ ZEND_API int zend_fcall_info_init(zval *callable, uint check_flags, zend_fcall_i
}
fci->size = sizeof(*fci);
- fci->function_table = fcc->calling_scope ? &fcc->calling_scope->function_table : EG(function_table);
fci->object = fcc->object;
ZVAL_COPY_VALUE(&fci->function_name, callable);
fci->retval = NULL;
fci->param_count = 0;
fci->params = NULL;
fci->no_separation = 1;
- fci->symbol_table = NULL;
return SUCCESS;
}
@@ -3744,13 +3739,51 @@ ZEND_API int zend_declare_property_stringl(zend_class_entry *ce, const char *nam
}
/* }}} */
-ZEND_API int zend_declare_class_constant(zend_class_entry *ce, const char *name, size_t name_length, zval *value) /* {{{ */
+ZEND_API int zend_declare_class_constant_ex(zend_class_entry *ce, zend_string *name, zval *value, int access_type, zend_string *doc_comment) /* {{{ */
{
+ zend_class_constant *c;
+
+ if (ce->ce_flags & ZEND_ACC_INTERFACE) {
+ if (access_type != ZEND_ACC_PUBLIC) {
+ zend_error_noreturn(E_COMPILE_ERROR, "Access type for interface constant %s::%s must be public", ZSTR_VAL(ce->name), ZSTR_VAL(name));
+ }
+ }
+
+ if (zend_string_equals_literal_ci(name, "class")) {
+ zend_error_noreturn(ce->type == ZEND_INTERNAL_CLASS ? E_CORE_ERROR : E_COMPILE_ERROR,
+ "A class constant must not be called 'class'; it is reserved for class name fetching");
+ }
+
+ if (ce->type == ZEND_INTERNAL_CLASS) {
+ c = pemalloc(sizeof(zend_class_constant), 1);
+ } else {
+ c = zend_arena_alloc(&CG(arena), sizeof(zend_class_constant));
+ }
+ ZVAL_COPY_VALUE(&c->value, value);
+ Z_ACCESS_FLAGS(c->value) = access_type;
+ c->doc_comment = doc_comment;
+ c->ce = ce;
if (Z_CONSTANT_P(value)) {
ce->ce_flags &= ~ZEND_ACC_CONSTANTS_UPDATED;
}
- return zend_hash_str_update(&ce->constants_table, name, name_length, value) ?
- SUCCESS : FAILURE;
+
+ if (!zend_hash_add_ptr(&ce->constants_table, name, c)) {
+ zend_error_noreturn(ce->type == ZEND_INTERNAL_CLASS ? E_CORE_ERROR : E_COMPILE_ERROR,
+ "Cannot redefine class constant %s::%s", ZSTR_VAL(ce->name), ZSTR_VAL(name));
+ }
+
+ return SUCCESS;
+}
+/* }}} */
+
+ZEND_API int zend_declare_class_constant(zend_class_entry *ce, const char *name, size_t name_length, zval *value) /* {{{ */
+{
+ int ret;
+
+ zend_string *key = zend_string_init(name, name_length, ce->type & ZEND_INTERNAL_CLASS);
+ ret = zend_declare_class_constant_ex(ce, key, value, ZEND_ACC_PUBLIC, NULL);
+ zend_string_release(key);
+ return ret;
}
/* }}} */
@@ -3808,9 +3841,9 @@ ZEND_API int zend_declare_class_constant_string(zend_class_entry *ce, const char
ZEND_API void zend_update_property_ex(zend_class_entry *scope, zval *object, zend_string *name, zval *value) /* {{{ */
{
zval property;
- zend_class_entry *old_scope = EG(scope);
+ zend_class_entry *old_scope = EG(fake_scope);
- EG(scope) = scope;
+ EG(fake_scope) = scope;
if (!Z_OBJ_HT_P(object)->write_property) {
zend_error_noreturn(E_CORE_ERROR, "Property %s of class %s cannot be updated", name, ZSTR_VAL(Z_OBJCE_P(object)->name));
@@ -3818,16 +3851,16 @@ ZEND_API void zend_update_property_ex(zend_class_entry *scope, zval *object, zen
ZVAL_STR(&property, name);
Z_OBJ_HT_P(object)->write_property(object, &property, value, NULL);
- EG(scope) = old_scope;
+ EG(fake_scope) = old_scope;
}
/* }}} */
ZEND_API void zend_update_property(zend_class_entry *scope, zval *object, const char *name, size_t name_length, zval *value) /* {{{ */
{
zval property;
- zend_class_entry *old_scope = EG(scope);
+ zend_class_entry *old_scope = EG(fake_scope);
- EG(scope) = scope;
+ EG(fake_scope) = scope;
if (!Z_OBJ_HT_P(object)->write_property) {
zend_error_noreturn(E_CORE_ERROR, "Property %s of class %s cannot be updated", name, ZSTR_VAL(Z_OBJCE_P(object)->name));
@@ -3836,7 +3869,7 @@ ZEND_API void zend_update_property(zend_class_entry *scope, zval *object, const
Z_OBJ_HT_P(object)->write_property(object, &property, value, NULL);
zval_ptr_dtor(&property);
- EG(scope) = old_scope;
+ EG(fake_scope) = old_scope;
}
/* }}} */
@@ -3908,12 +3941,12 @@ ZEND_API void zend_update_property_stringl(zend_class_entry *scope, zval *object
ZEND_API int zend_update_static_property(zend_class_entry *scope, const char *name, size_t name_length, zval *value) /* {{{ */
{
zval *property;
- zend_class_entry *old_scope = EG(scope);
+ zend_class_entry *old_scope = EG(fake_scope);
zend_string *key = zend_string_init(name, name_length, 0);
- EG(scope) = scope;
+ EG(fake_scope) = scope;
property = zend_std_get_static_property(scope, key, 0);
- EG(scope) = old_scope;
+ EG(fake_scope) = old_scope;
zend_string_free(key);
if (!property) {
return FAILURE;
@@ -4000,22 +4033,33 @@ ZEND_API int zend_update_static_property_stringl(zend_class_entry *scope, const
}
/* }}} */
-ZEND_API zval *zend_read_property(zend_class_entry *scope, zval *object, const char *name, size_t name_length, zend_bool silent, zval *rv) /* {{{ */
+ZEND_API zval *zend_read_property_ex(zend_class_entry *scope, zval *object, zend_string *name, zend_bool silent, zval *rv) /* {{{ */
{
zval property, *value;
- zend_class_entry *old_scope = EG(scope);
+ zend_class_entry *old_scope = EG(fake_scope);
- EG(scope) = scope;
+ EG(fake_scope) = scope;
if (!Z_OBJ_HT_P(object)->read_property) {
zend_error_noreturn(E_CORE_ERROR, "Property %s of class %s cannot be read", name, ZSTR_VAL(Z_OBJCE_P(object)->name));
}
- ZVAL_STRINGL(&property, name, name_length);
+ ZVAL_STR(&property, name);
value = Z_OBJ_HT_P(object)->read_property(object, &property, silent?BP_VAR_IS:BP_VAR_R, NULL, rv);
- zval_ptr_dtor(&property);
- EG(scope) = old_scope;
+ EG(fake_scope) = old_scope;
+ return value;
+}
+/* }}} */
+
+ZEND_API zval *zend_read_property(zend_class_entry *scope, zval *object, const char *name, size_t name_length, zend_bool silent, zval *rv) /* {{{ */
+{
+ zval *value;
+ zend_string *str;
+
+ str = zend_string_init(name, name_length, 0);
+ value = zend_read_property_ex(scope, object, str, silent, rv);
+ zend_string_release(str);
return value;
}
/* }}} */
@@ -4023,12 +4067,12 @@ ZEND_API zval *zend_read_property(zend_class_entry *scope, zval *object, const c
ZEND_API zval *zend_read_static_property(zend_class_entry *scope, const char *name, size_t name_length, zend_bool silent) /* {{{ */
{
zval *property;
- zend_class_entry *old_scope = EG(scope);
+ zend_class_entry *old_scope = EG(fake_scope);
zend_string *key = zend_string_init(name, name_length, 0);
- EG(scope) = scope;
+ EG(fake_scope) = scope;
property = zend_std_get_static_property(scope, key, silent);
- EG(scope) = old_scope;
+ EG(fake_scope) = old_scope;
zend_string_free(key);
return property;
diff --git a/Zend/zend_API.h b/Zend/zend_API.h
index 822dbb08b8..85ae8b91d4 100644
--- a/Zend/zend_API.h
+++ b/Zend/zend_API.h
@@ -43,9 +43,7 @@ typedef struct _zend_function_entry {
typedef struct _zend_fcall_info {
size_t size;
- HashTable *function_table;
zval function_name;
- zend_array *symbol_table;
zval *retval;
zval *params;
zend_object *object;
@@ -324,6 +322,7 @@ ZEND_API int zend_declare_property_double(zend_class_entry *ce, const char *name
ZEND_API int zend_declare_property_string(zend_class_entry *ce, const char *name, size_t name_length, const char *value, int access_type);
ZEND_API int zend_declare_property_stringl(zend_class_entry *ce, const char *name, size_t name_length, const char *value, size_t value_len, int access_type);
+ZEND_API int zend_declare_class_constant_ex(zend_class_entry *ce, zend_string *name, zval *value, int access_type, zend_string *doc_comment);
ZEND_API int zend_declare_class_constant(zend_class_entry *ce, const char *name, size_t name_length, zval *value);
ZEND_API int zend_declare_class_constant_null(zend_class_entry *ce, const char *name, size_t name_length);
ZEND_API int zend_declare_class_constant_long(zend_class_entry *ce, const char *name, size_t name_length, zend_long value);
@@ -352,13 +351,14 @@ ZEND_API int zend_update_static_property_double(zend_class_entry *scope, const c
ZEND_API int zend_update_static_property_string(zend_class_entry *scope, const char *name, size_t name_length, const char *value);
ZEND_API int zend_update_static_property_stringl(zend_class_entry *scope, const char *name, size_t name_length, const char *value, size_t value_length);
+ZEND_API zval *zend_read_property_ex(zend_class_entry *scope, zval *object, zend_string *name, zend_bool silent, zval *rv);
ZEND_API zval *zend_read_property(zend_class_entry *scope, zval *object, const char *name, size_t name_length, zend_bool silent, zval *rv);
ZEND_API zval *zend_read_static_property(zend_class_entry *scope, const char *name, size_t name_length, zend_bool silent);
ZEND_API char *zend_get_type_by_const(int type);
-#define getThis() (Z_OBJ(EX(This)) ? &EX(This) : NULL)
+#define getThis() ((Z_TYPE(EX(This)) == IS_OBJECT) ? &EX(This) : NULL)
#define ZEND_IS_METHOD_CALL() (EX(func)->common.scope != NULL)
#define WRONG_PARAM_COUNT ZEND_WRONG_PARAM_COUNT()
@@ -468,8 +468,12 @@ ZEND_API int add_property_zval_ex(zval *arg, const char *key, size_t key_len, zv
#define add_property_zval(__arg, __key, __value) add_property_zval_ex(__arg, __key, strlen(__key), __value)
-ZEND_API int call_user_function(HashTable *function_table, zval *object, zval *function_name, zval *retval_ptr, uint32_t param_count, zval params[]);
-ZEND_API int call_user_function_ex(HashTable *function_table, zval *object, zval *function_name, zval *retval_ptr, uint32_t param_count, zval params[], int no_separation, zend_array *symbol_table);
+ZEND_API int _call_user_function_ex(zval *object, zval *function_name, zval *retval_ptr, uint32_t param_count, zval params[], int no_separation);
+
+#define call_user_function(function_table, object, function_name, retval_ptr, param_count, params) \
+ _call_user_function_ex(object, function_name, retval_ptr, param_count, params, 1)
+#define call_user_function_ex(function_table, object, function_name, retval_ptr, param_count, params, no_separation, symbol_table) \
+ _call_user_function_ex(object, function_name, retval_ptr, param_count, params, no_separation)
ZEND_API extern const zend_fcall_info empty_fcall_info;
ZEND_API extern const zend_fcall_info_cache empty_fcall_info_cache;
@@ -539,6 +543,7 @@ ZEND_API void zend_attach_symbol_table(zend_execute_data *execute_data);
ZEND_API void zend_detach_symbol_table(zend_execute_data *execute_data);
ZEND_API int zend_set_local_var(zend_string *name, zval *value, int force);
ZEND_API int zend_set_local_var_str(const char *name, size_t len, zval *value, int force);
+ZEND_API int zend_forbid_dynamic_call(const char *func_name);
ZEND_API zend_string *zend_find_alias_name(zend_class_entry *ce, zend_string *name);
ZEND_API zend_string *zend_resolve_method_name(zend_class_entry *ce, zend_function *f);
@@ -691,9 +696,9 @@ typedef enum _zend_expected_type {
Z_EXPECTED_LAST
} zend_expected_type;
-ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_paramers_count_error(int num_args, int min_num_args, int max_num_args);
-ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_paramer_type_error(int num, zend_expected_type expected_type, zval *arg);
-ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_paramer_class_error(int num, char *name, zval *arg);
+ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_parameters_count_error(int num_args, int min_num_args, int max_num_args);
+ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_parameter_type_error(int num, zend_expected_type expected_type, zval *arg);
+ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_parameter_class_error(int num, char *name, zval *arg);
ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_callback_error(int severity, int num, char *error);
#define ZPP_ERROR_OK 0
@@ -728,7 +733,7 @@ ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_callback_error(int severity, in
(UNEXPECTED(_num_args > _max_num_args) && \
EXPECTED(_max_num_args >= 0))) { \
if (!(_flags & ZEND_PARSE_PARAMS_QUIET)) { \
- zend_wrong_paramers_count_error(_num_args, _min_num_args, _max_num_args); \
+ zend_wrong_parameters_count_error(_num_args, _min_num_args, _max_num_args); \
} \
error_code = ZPP_ERROR_FAILURE; \
break; \
@@ -746,9 +751,9 @@ ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_callback_error(int severity, in
if (error_code == ZPP_ERROR_WRONG_CALLBACK) { \
zend_wrong_callback_error(E_WARNING, _i, _error); \
} else if (error_code == ZPP_ERROR_WRONG_CLASS) { \
- zend_wrong_paramer_class_error(_i, _error, _arg); \
+ zend_wrong_parameter_class_error(_i, _error, _arg); \
} else if (error_code == ZPP_ERROR_WRONG_ARG) { \
- zend_wrong_paramer_type_error(_i, _expected_type, _arg); \
+ zend_wrong_parameter_type_error(_i, _expected_type, _arg); \
} \
} \
failure; \
@@ -857,7 +862,7 @@ ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_callback_error(int severity, in
/* old "h" */
#define Z_PARAM_ARRAY_HT_EX(dest, check_null, separate) \
Z_PARAM_PROLOGUE(separate); \
- if (UNEXPECTED(!zend_parse_arg_array_ht(_arg, &dest, check_null, 0))) { \
+ if (UNEXPECTED(!zend_parse_arg_array_ht(_arg, &dest, check_null, 0, separate))) { \
_expected_type = Z_EXPECTED_ARRAY; \
error_code = ZPP_ERROR_WRONG_ARG; \
break; \
@@ -869,7 +874,7 @@ ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_callback_error(int severity, in
/* old "H" */
#define Z_PARAM_ARRAY_OR_OBJECT_HT_EX(dest, check_null, separate) \
Z_PARAM_PROLOGUE(separate); \
- if (UNEXPECTED(!zend_parse_arg_array_ht(_arg, &dest, check_null, 1))) { \
+ if (UNEXPECTED(!zend_parse_arg_array_ht(_arg, &dest, check_null, 1, separate))) { \
_expected_type = Z_EXPECTED_ARRAY; \
error_code = ZPP_ERROR_WRONG_ARG; \
break; \
@@ -1172,11 +1177,19 @@ static zend_always_inline int zend_parse_arg_array(zval *arg, zval **dest, int c
return 1;
}
-static zend_always_inline int zend_parse_arg_array_ht(zval *arg, HashTable **dest, int check_null, int or_object)
+static zend_always_inline int zend_parse_arg_array_ht(zval *arg, HashTable **dest, int check_null, int or_object, int separate)
{
if (EXPECTED(Z_TYPE_P(arg) == IS_ARRAY)) {
*dest = Z_ARRVAL_P(arg);
} else if (or_object && EXPECTED(Z_TYPE_P(arg) == IS_OBJECT)) {
+ if (separate
+ && Z_OBJ_P(arg)->properties
+ && UNEXPECTED(GC_REFCOUNT(Z_OBJ_P(arg)->properties) > 1)) {
+ if (EXPECTED(!(GC_FLAGS(Z_OBJ_P(arg)->properties) & IS_ARRAY_IMMUTABLE))) {
+ GC_REFCOUNT(Z_OBJ_P(arg)->properties)--;
+ }
+ Z_OBJ_P(arg)->properties = zend_array_dup(Z_OBJ_P(arg)->properties);
+ }
*dest = Z_OBJ_HT_P(arg)->get_properties(arg);
} else if (check_null && EXPECTED(Z_TYPE_P(arg) == IS_NULL)) {
*dest = NULL;
diff --git a/Zend/zend_alloc.c b/Zend/zend_alloc.c
index 1876559317..a7e3808e7c 100644
--- a/Zend/zend_alloc.c
+++ b/Zend/zend_alloc.c
@@ -58,6 +58,7 @@
#include "zend_globals.h"
#include "zend_operators.h"
#include "zend_multiply.h"
+#include "zend_bitset.h"
#ifdef HAVE_SIGNAL_H
# include <signal.h>
@@ -546,45 +547,6 @@ static zend_always_inline int zend_mm_bitset_nts(zend_mm_bitset bitset)
#endif
}
-/* number of trailing zero bits (0x01 -> 1; 0x40 -> 6; 0x00 -> LEN) */
-static zend_always_inline int zend_mm_bitset_ntz(zend_mm_bitset bitset)
-{
-#if (defined(__GNUC__) || __has_builtin(__builtin_ctzl)) && SIZEOF_ZEND_LONG == SIZEOF_LONG && defined(PHP_HAVE_BUILTIN_CTZL)
- return __builtin_ctzl(bitset);
-#elif (defined(__GNUC__) || __has_builtin(__builtin_ctzll)) && defined(PHP_HAVE_BUILTIN_CTZLL)
- return __builtin_ctzll(bitset);
-#elif defined(_WIN32)
- unsigned long index;
-
-#if defined(_WIN64)
- if (!BitScanForward64(&index, bitset)) {
-#else
- if (!BitScanForward(&index, bitset)) {
-#endif
- /* undefined behavior */
- return 32;
- }
-
- return (int)index;
-#else
- int n;
-
- if (bitset == (zend_mm_bitset)0) return ZEND_MM_BITSET_LEN;
-
- n = 1;
-#if SIZEOF_ZEND_LONG == 8
- if (sizeof(zend_mm_bitset) == 8) {
- if ((bitset & 0xffffffff) == 0) {n += 32; bitset = bitset >> Z_UL(32);}
- }
-#endif
- if ((bitset & 0x0000ffff) == 0) {n += 16; bitset = bitset >> 16;}
- if ((bitset & 0x000000ff) == 0) {n += 8; bitset = bitset >> 8;}
- if ((bitset & 0x0000000f) == 0) {n += 4; bitset = bitset >> 4;}
- if ((bitset & 0x00000003) == 0) {n += 2; bitset = bitset >> 2;}
- return n - (bitset & 1);
-#endif
-}
-
static zend_always_inline int zend_mm_bitset_find_zero(zend_mm_bitset *bitset, int size)
{
int i = 0;
@@ -606,7 +568,7 @@ static zend_always_inline int zend_mm_bitset_find_one(zend_mm_bitset *bitset, in
do {
zend_mm_bitset tmp = bitset[i];
if (tmp != 0) {
- return i * ZEND_MM_BITSET_LEN + zend_mm_bitset_ntz(tmp);
+ return i * ZEND_MM_BITSET_LEN + zend_ulong_ntz(tmp);
}
i++;
} while (i < size);
@@ -941,7 +903,7 @@ static void *zend_mm_alloc_pages(zend_mm_heap *heap, int pages_count ZEND_FILE_L
tmp = *(bitset++);
}
/* find first 1 bit */
- len = (i + zend_mm_bitset_ntz(tmp)) - page_num;
+ len = (i + zend_ulong_ntz(tmp)) - page_num;
if (len >= pages_count) {
goto found;
}
@@ -998,7 +960,7 @@ static void *zend_mm_alloc_pages(zend_mm_heap *heap, int pages_count ZEND_FILE_L
tmp = *(bitset++);
}
/* find first 1 bit */
- len = i + zend_mm_bitset_ntz(tmp) - page_num;
+ len = i + zend_ulong_ntz(tmp) - page_num;
if (len >= pages_count) {
if (len == pages_count) {
goto found;
@@ -1862,7 +1824,7 @@ static zend_mm_heap *zend_mm_init(void)
heap->peak = 0;
#endif
#if ZEND_MM_LIMIT
- heap->limit = (Z_L(-1) >> Z_L(1));
+ heap->limit = ((size_t)Z_L(-1) >> (size_t)Z_L(1));
heap->overflow = 0;
#endif
#if ZEND_MM_CUSTOM
diff --git a/Zend/zend_ast.c b/Zend/zend_ast.c
index 11980f10ee..236d0783fc 100644
--- a/Zend/zend_ast.c
+++ b/Zend/zend_ast.c
@@ -256,23 +256,23 @@ ZEND_API int zend_ast_evaluate(zval *result, zend_ast *ast, zend_class_entry *sc
case ZEND_AST_ZVAL:
{
zval *zv = zend_ast_get_zval(ast);
- if (scope) {
- /* class constants may be updated in-place */
- if (Z_OPT_CONSTANT_P(zv)) {
- if (UNEXPECTED(zval_update_constant_ex(zv, 1, scope) != SUCCESS)) {
+
+ if (Z_OPT_CONSTANT_P(zv)) {
+ if (!(Z_TYPE_FLAGS_P(zv) & IS_TYPE_IMMUTABLE)) {
+ if (UNEXPECTED(zval_update_constant_ex(zv, scope) != SUCCESS)) {
ret = FAILURE;
break;
}
- }
- ZVAL_DUP(result, zv);
- } else {
- ZVAL_DUP(result, zv);
- if (Z_OPT_CONSTANT_P(result)) {
- if (UNEXPECTED(zval_update_constant_ex(result, 1, scope) != SUCCESS)) {
+ ZVAL_COPY(result, zv);
+ } else {
+ ZVAL_COPY_VALUE(result, zv);
+ if (UNEXPECTED(zval_update_constant_ex(result, scope) != SUCCESS)) {
ret = FAILURE;
break;
}
}
+ } else {
+ ZVAL_COPY(result, zv);
}
break;
}
@@ -776,19 +776,22 @@ static void zend_ast_export_encaps_list(smart_str *str, char quote, zend_ast_lis
}
}
-static void zend_ast_export_name_list(smart_str *str, zend_ast_list *list, int indent)
+static void zend_ast_export_name_list_ex(smart_str *str, zend_ast_list *list, int indent, const char *separator)
{
uint32_t i = 0;
while (i < list->children) {
if (i != 0) {
- smart_str_appends(str, ", ");
+ smart_str_appends(str, separator);
}
zend_ast_export_name(str, list->child[i], 0, indent);
i++;
}
}
+#define zend_ast_export_name_list(s, l, i) zend_ast_export_name_list_ex(s, l, i, ", ")
+#define zend_ast_export_catch_name_list(s, l, i) zend_ast_export_name_list_ex(s, l, i, "|")
+
static void zend_ast_export_var_list(smart_str *str, zend_ast_list *list, int indent)
{
uint32_t i = 0;
@@ -1099,11 +1102,6 @@ tail_call:
simple_list:
zend_ast_export_list(str, (zend_ast_list*)ast, 1, 20, indent);
break;
- case ZEND_AST_LIST:
- smart_str_appends(str, "list(");
- zend_ast_export_list(str, (zend_ast_list*)ast, 1, 20, indent);
- smart_str_appendc(str, ')');
- break;
case ZEND_AST_ARRAY:
smart_str_appendc(str, '[');
zend_ast_export_list(str, (zend_ast_list*)ast, 1, 20, indent);
@@ -1588,7 +1586,7 @@ simple_list:
break;
case ZEND_AST_CATCH:
smart_str_appends(str, "} catch (");
- zend_ast_export_ns_name(str, ast->child[0], 0, indent);
+ zend_ast_export_catch_name_list(str, zend_ast_get_list(ast->child[0]), indent);
smart_str_appends(str, " $");
zend_ast_export_var(str, ast->child[1], 0, indent);
smart_str_appends(str, ") {\n");
diff --git a/Zend/zend_ast.h b/Zend/zend_ast.h
index 0daec66069..1bf5f3bae5 100644
--- a/Zend/zend_ast.h
+++ b/Zend/zend_ast.h
@@ -42,7 +42,6 @@ enum _zend_ast_kind {
/* list nodes */
ZEND_AST_ARG_LIST = 1 << ZEND_AST_IS_LIST_SHIFT,
- ZEND_AST_LIST,
ZEND_AST_ARRAY,
ZEND_AST_ENCAPS_LIST,
ZEND_AST_EXPR_LIST,
@@ -124,7 +123,6 @@ enum _zend_ast_kind {
ZEND_AST_SWITCH,
ZEND_AST_SWITCH_CASE,
ZEND_AST_DECLARE,
- ZEND_AST_CONST_ELEM,
ZEND_AST_USE_TRAIT,
ZEND_AST_TRAIT_PRECEDENCE,
ZEND_AST_METHOD_REFERENCE,
@@ -142,6 +140,7 @@ enum _zend_ast_kind {
ZEND_AST_CATCH,
ZEND_AST_PARAM,
ZEND_AST_PROP_ELEM,
+ ZEND_AST_CONST_ELEM,
/* 4 child nodes */
ZEND_AST_FOR = 4 << ZEND_AST_NUM_CHILDREN_SHIFT,
@@ -267,5 +266,11 @@ static zend_always_inline zend_ast *zend_ast_create_assign_op(uint32_t opcode, z
static zend_always_inline zend_ast *zend_ast_create_cast(uint32_t type, zend_ast *op0) {
return zend_ast_create_ex(ZEND_AST_CAST, type, op0);
}
-
+static zend_always_inline zend_ast *zend_ast_list_rtrim(zend_ast *ast) {
+ zend_ast_list *list = zend_ast_get_list(ast);
+ if (list->children && list->child[list->children - 1] == NULL) {
+ list->children--;
+ }
+ return ast;
+}
#endif
diff --git a/Zend/zend_bitset.h b/Zend/zend_bitset.h
index e3c2ec748d..12c58b1a41 100644
--- a/Zend/zend_bitset.h
+++ b/Zend/zend_bitset.h
@@ -36,6 +36,47 @@ typedef zend_ulong *zend_bitset;
# define ZEND_BITSET_BIT_NUM(n) ((n) % (sizeof(zend_long) * 8))
#endif
+#define ZEND_BITSET_ALLOCA(n, use_heap) \
+ (zend_bitset)do_alloca((n) * ZEND_BITSET_ELM_SIZE, use_heap)
+
+/* Number of trailing zero bits (0x01 -> 0; 0x40 -> 6; 0x00 -> LEN) */
+static zend_always_inline int zend_ulong_ntz(zend_ulong num)
+{
+#if (defined(__GNUC__) || __has_builtin(__builtin_ctzl)) \
+ && SIZEOF_ZEND_LONG == SIZEOF_LONG && defined(PHP_HAVE_BUILTIN_CTZL)
+ return __builtin_ctzl(num);
+#elif (defined(__GNUC__) || __has_builtin(__builtin_ctzll)) && defined(PHP_HAVE_BUILTIN_CTZLL)
+ return __builtin_ctzll(num);
+#elif defined(_WIN32)
+ unsigned long index;
+
+#if defined(_WIN64)
+ if (!BitScanForward64(&index, num)) {
+#else
+ if (!BitScanForward(&index, num)) {
+#endif
+ /* undefined behavior */
+ return 32;
+ }
+
+ return (int) index;
+#else
+ int n;
+
+ if (num == Z_UL(0)) return ZEND_MM_BITSET_LEN;
+
+ n = 1;
+#if SIZEOF_ZEND_LONG == 8
+ if ((num & 0xffffffff) == 0) {n += 32; num = num >> Z_UL(32);}
+#endif
+ if ((num & 0x0000ffff) == 0) {n += 16; num = num >> 16;}
+ if ((num & 0x000000ff) == 0) {n += 8; num = num >> 8;}
+ if ((num & 0x0000000f) == 0) {n += 4; num = num >> 4;}
+ if ((num & 0x00000003) == 0) {n += 2; num = num >> 2;}
+ return n - (num & 1);
+#endif
+}
+
/* Returns the number of zend_ulong words needed to store a bitset that is N
bits long. */
static inline uint32_t zend_bitset_len(uint32_t n)
@@ -43,9 +84,6 @@ static inline uint32_t zend_bitset_len(uint32_t n)
return (n + ((sizeof(zend_long) * 8) - 1)) / (sizeof(zend_long) * 8);
}
-#define ZEND_BITSET_ALLOCA(n, use_heap) \
- (zend_bitset)do_alloca((n) * ZEND_BITSET_ELM_SIZE, use_heap)
-
static inline zend_bool zend_bitset_in(zend_bitset set, uint32_t n)
{
return (set[ZEND_BITSET_ELM_NUM(n)] & (Z_UL(1) << ZEND_BITSET_BIT_NUM(n))) != Z_UL(0);
@@ -137,19 +175,25 @@ static inline void zend_bitset_union_with_difference(zend_bitset set1, zend_bits
}
}
+static inline zend_bool zend_bitset_subset(zend_bitset set1, zend_bitset set2, uint32_t len)
+{
+ uint32_t i;
+
+ for (i = 0; i < len; i++) {
+ if (set1[i] & ~set2[i]) {
+ return 0;
+ }
+ }
+ return 1;
+}
+
static inline int zend_bitset_first(zend_bitset set, uint32_t len)
{
uint32_t i;
for (i = 0; i < len; i++) {
if (set[i]) {
- int j = ZEND_BITSET_ELM_SIZE * 8 * i;
- zend_ulong x = set[i];
- while ((x & Z_UL(1)) == 0) {
- x = x >> Z_UL(1);
- j++;
- }
- return j;
+ return ZEND_BITSET_ELM_SIZE * 8 * i + zend_ulong_ntz(set[i]);
}
}
return -1; /* empty set */
@@ -174,6 +218,33 @@ static inline int zend_bitset_last(zend_bitset set, uint32_t len)
return -1; /* empty set */
}
+#define ZEND_BITSET_FOREACH(set, len, bit) do { \
+ zend_bitset _set = (set); \
+ uint32_t _i, _len = (len); \
+ for (_i = 0; _i < _len; _i++) { \
+ zend_ulong _x = _set[_i]; \
+ if (_x) { \
+ (bit) = ZEND_BITSET_ELM_SIZE * 8 * _i; \
+ for (; _x != 0; _x >>= Z_UL(1), (bit)++) { \
+ if (!(_x & Z_UL(1))) continue;
+
+#define ZEND_BITSET_REVERSE_FOREACH(set, len, bit) do { \
+ zend_bitset _set = (set); \
+ uint32_t _i = (len); \
+ zend_ulong _test = Z_UL(1) << (ZEND_BITSET_ELM_SIZE * 8 - 1); \
+ while (_i-- > 0) { \
+ zend_ulong _x = _set[_i]; \
+ if (_x) { \
+ (bit) = ZEND_BITSET_ELM_SIZE * 8 * (_i + 1) - 1; \
+ for (; _x != 0; _x <<= Z_UL(1), (bit)--) { \
+ if (!(_x & _test)) continue; \
+
+#define ZEND_BITSET_FOREACH_END() \
+ } \
+ } \
+ } \
+} while (0)
+
#endif /* _ZEND_BITSET_H_ */
/*
diff --git a/Zend/zend_builtin_functions.c b/Zend/zend_builtin_functions.c
index 558a1b2ac6..5ae536f425 100644
--- a/Zend/zend_builtin_functions.c
+++ b/Zend/zend_builtin_functions.c
@@ -31,6 +31,12 @@
#undef ZEND_TEST_EXCEPTIONS
+#if ZEND_DEBUG
+static zend_class_entry *zend_test_interface;
+static zend_class_entry *zend_test_class;
+static zend_object_handlers zend_test_class_handlers;
+#endif
+
static ZEND_FUNCTION(zend_version);
static ZEND_FUNCTION(func_num_args);
static ZEND_FUNCTION(func_get_arg);
@@ -257,6 +263,51 @@ ZEND_END_ARG_INFO()
/* }}} */
+#if ZEND_DEBUG
+static zend_object *zend_test_class_new(zend_class_entry *class_type) /* {{{ */ {
+ zend_object *obj = zend_objects_new(class_type);
+ obj->handlers = &zend_test_class_handlers;
+ return obj;
+}
+/* }}} */
+
+static zend_function *zend_test_class_method_get(zend_object **object, zend_string *name, const zval *key) /* {{{ */ {
+ zend_internal_function *fptr = emalloc(sizeof(zend_internal_function));
+ fptr->type = ZEND_OVERLOADED_FUNCTION_TEMPORARY;
+ fptr->num_args = 1;
+ fptr->arg_info = NULL;
+ fptr->scope = (*object)->ce;
+ fptr->fn_flags = ZEND_ACC_CALL_VIA_HANDLER;
+ fptr->function_name = zend_string_copy(name);
+ fptr->handler = ZEND_FN(zend_test_func);
+ zend_set_function_arg_flags((zend_function*)fptr);
+
+ return (zend_function*)fptr;
+}
+/* }}} */
+
+static zend_function *zend_test_class_static_method_get(zend_class_entry *ce, zend_string *name) /* {{{ */ {
+ zend_internal_function *fptr = emalloc(sizeof(zend_internal_function));
+ fptr->type = ZEND_OVERLOADED_FUNCTION;
+ fptr->num_args = 1;
+ fptr->arg_info = NULL;
+ fptr->scope = ce;
+ fptr->fn_flags = ZEND_ACC_CALL_VIA_HANDLER|ZEND_ACC_STATIC;
+ fptr->function_name = name;
+ fptr->handler = ZEND_FN(zend_test_func);
+ zend_set_function_arg_flags((zend_function*)fptr);
+
+ return (zend_function*)fptr;
+}
+/* }}} */
+
+static int zend_test_class_call_method(zend_string *method, zend_object *object, INTERNAL_FUNCTION_PARAMETERS) /* {{{ */ {
+ RETVAL_STR(zend_string_copy(method));
+ return 0;
+}
+/* }}} */
+#endif
+
static const zend_function_entry builtin_functions[] = { /* {{{ */
ZEND_FE(zend_version, arginfo_zend__void)
ZEND_FE(func_num_args, arginfo_zend__void)
@@ -339,6 +390,21 @@ ZEND_MINIT_FUNCTION(core) { /* {{{ */
zend_register_default_classes();
+#if ZEND_DEBUG
+ INIT_CLASS_ENTRY(class_entry, "_ZendTestInterface", NULL);
+ zend_test_interface = zend_register_internal_interface(&class_entry);
+ zend_declare_class_constant_long(zend_test_interface, ZEND_STRL("DUMMY"), 0);
+ INIT_CLASS_ENTRY(class_entry, "_ZendTestClass", NULL);
+ zend_test_class = zend_register_internal_class_ex(&class_entry, NULL);
+ zend_class_implements(zend_test_class, 1, zend_test_interface);
+ zend_test_class->create_object = zend_test_class_new;
+ zend_test_class->get_static_method = zend_test_class_static_method_get;
+
+ memcpy(&zend_test_class_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
+ zend_test_class_handlers.get_method = zend_test_class_method_get;
+ zend_test_class_handlers.call_method = zend_test_class_call_method;
+#endif
+
return SUCCESS;
}
/* }}} */
@@ -425,12 +491,16 @@ ZEND_FUNCTION(func_num_args)
{
zend_execute_data *ex = EX(prev_execute_data);
- if (!(ZEND_CALL_INFO(ex) & ZEND_CALL_CODE)) {
- RETURN_LONG(ZEND_CALL_NUM_ARGS(ex));
- } else {
+ if (ZEND_CALL_INFO(ex) & ZEND_CALL_CODE) {
zend_error(E_WARNING, "func_num_args(): Called from the global scope - no function context");
RETURN_LONG(-1);
}
+
+ if (zend_forbid_dynamic_call("func_num_args()") == FAILURE) {
+ RETURN_LONG(-1);
+ }
+
+ RETURN_LONG(ZEND_CALL_NUM_ARGS(ex));
}
/* }}} */
@@ -458,15 +528,19 @@ ZEND_FUNCTION(func_get_arg)
RETURN_FALSE;
}
+ if (zend_forbid_dynamic_call("func_get_arg()") == FAILURE) {
+ RETURN_FALSE;
+ }
+
arg_count = ZEND_CALL_NUM_ARGS(ex);
- if (requested_offset >= arg_count) {
+ if ((zend_ulong)requested_offset >= arg_count) {
zend_error(E_WARNING, "func_get_arg(): Argument " ZEND_LONG_FMT " not passed to function", requested_offset);
RETURN_FALSE;
}
first_extra_arg = ex->func->op_array.num_args;
- if (requested_offset >= first_extra_arg && (ZEND_CALL_NUM_ARGS(ex) > first_extra_arg)) {
+ if ((zend_ulong)requested_offset >= first_extra_arg && (ZEND_CALL_NUM_ARGS(ex) > first_extra_arg)) {
arg = ZEND_CALL_VAR_NUM(ex, ex->func->op_array.last_var + ex->func->op_array.T) + (requested_offset - first_extra_arg);
} else {
arg = ZEND_CALL_ARG(ex, requested_offset + 1);
@@ -492,6 +566,10 @@ ZEND_FUNCTION(func_get_args)
RETURN_FALSE;
}
+ if (zend_forbid_dynamic_call("func_get_args()") == FAILURE) {
+ RETURN_FALSE;
+ }
+
arg_count = ZEND_CALL_NUM_ARGS(ex);
array_init_size(return_value, arg_count);
@@ -670,7 +748,7 @@ ZEND_FUNCTION(each)
if (Z_REFCOUNTED_P(entry)) Z_ADDREF_P(entry);
}
zend_hash_index_add_new(Z_ARRVAL_P(return_value), 1, entry);
- zend_hash_str_add_new(Z_ARRVAL_P(return_value), "value", sizeof("value")-1, entry);
+ zend_hash_add_new(Z_ARRVAL_P(return_value), CG(known_strings)[ZEND_STR_VALUE], entry);
/* add the key elements */
if (zend_hash_get_current_key(target_hash, &key, &num_key) == HASH_KEY_IS_STRING) {
@@ -680,7 +758,7 @@ ZEND_FUNCTION(each)
ZVAL_LONG(&tmp, num_key);
}
zend_hash_index_add_new(Z_ARRVAL_P(return_value), 0, &tmp);
- zend_hash_str_add_new(Z_ARRVAL_P(return_value), "key", sizeof("key")-1, &tmp);
+ zend_hash_add_new(Z_ARRVAL_P(return_value), CG(known_strings)[ZEND_STR_KEY], &tmp);
zend_hash_move_forward(target_hash);
}
/* }}} */
@@ -710,7 +788,7 @@ ZEND_FUNCTION(error_reporting)
zend_ini_entry *p = EG(error_reporting_ini_entry);
if (!p) {
- p = zend_hash_str_find_ptr(EG(ini_directives), "error_reporting", sizeof("error_reporting")-1);
+ p = zend_hash_find_ptr(EG(ini_directives), CG(known_strings)[ZEND_STR_ERROR_REPORTING]);
if (p) {
EG(error_reporting_ini_entry) = p;
} else {
@@ -722,7 +800,7 @@ ZEND_FUNCTION(error_reporting)
ALLOC_HASHTABLE(EG(modified_ini_directives));
zend_hash_init(EG(modified_ini_directives), 8, NULL, NULL, 0);
}
- if (EXPECTED(zend_hash_str_add_ptr(EG(modified_ini_directives), "error_reporting", sizeof("error_reporting")-1, p) != NULL)) {
+ if (EXPECTED(zend_hash_add_ptr(EG(modified_ini_directives), CG(known_strings)[ZEND_STR_ERROR_REPORTING], p) != NULL)) {
p->orig_value = p->value;
p->orig_modifiable = p->modifiable;
p->modified = 1;
@@ -917,7 +995,7 @@ ZEND_FUNCTION(defined)
ZEND_PARSE_PARAMETERS_END();
#endif
- if (zend_get_constant_ex(name, NULL, ZEND_FETCH_CLASS_SILENT)) {
+ if (zend_get_constant_ex(name, zend_get_executed_scope(), ZEND_FETCH_CLASS_SILENT)) {
RETURN_TRUE;
} else {
RETURN_FALSE;
@@ -936,8 +1014,10 @@ ZEND_FUNCTION(get_class)
}
if (!obj) {
- if (EG(scope)) {
- RETURN_STR_COPY(EG(scope)->name);
+ zend_class_entry *scope = zend_get_executed_scope();
+
+ if (scope) {
+ RETURN_STR_COPY(scope->name);
} else {
zend_error(E_WARNING, "get_class() called without object from outside a class");
RETURN_FALSE;
@@ -961,8 +1041,11 @@ ZEND_FUNCTION(get_called_class)
called_scope = zend_get_called_scope(execute_data);
if (called_scope) {
RETURN_STR_COPY(called_scope->name);
- } else if (!EG(scope)) {
- zend_error(E_WARNING, "get_called_class() called from outside a class");
+ } else {
+ zend_class_entry *scope = zend_get_executed_scope();
+ if (!scope) {
+ zend_error(E_WARNING, "get_called_class() called from outside a class");
+ }
}
RETURN_FALSE;
}
@@ -980,7 +1063,7 @@ ZEND_FUNCTION(get_parent_class)
}
if (!ZEND_NUM_ARGS()) {
- ce = EG(scope);
+ ce = zend_get_executed_scope();
if (ce && ce->parent) {
RETURN_STR_COPY(ce->parent->name);
} else {
@@ -1077,7 +1160,7 @@ ZEND_FUNCTION(is_a)
/* }}} */
/* {{{ add_class_vars */
-static void add_class_vars(zend_class_entry *ce, int statics, zval *return_value)
+static void add_class_vars(zend_class_entry *scope, zend_class_entry *ce, int statics, zval *return_value)
{
zend_property_info *prop_info;
zval *prop, prop_copy;
@@ -1085,12 +1168,12 @@ static void add_class_vars(zend_class_entry *ce, int statics, zval *return_value
ZEND_HASH_FOREACH_STR_KEY_PTR(&ce->properties_info, key, prop_info) {
if (((prop_info->flags & ZEND_ACC_SHADOW) &&
- prop_info->ce != EG(scope)) ||
+ prop_info->ce != scope) ||
((prop_info->flags & ZEND_ACC_PROTECTED) &&
- !zend_check_protected(prop_info->ce, EG(scope))) ||
+ !zend_check_protected(prop_info->ce, scope)) ||
((prop_info->flags & ZEND_ACC_PRIVATE) &&
- ce != EG(scope) &&
- prop_info->ce != EG(scope))) {
+ ce != scope &&
+ prop_info->ce != scope)) {
continue;
}
prop = NULL;
@@ -1115,7 +1198,7 @@ static void add_class_vars(zend_class_entry *ce, int statics, zval *return_value
/* this is necessary to make it able to work with default array
* properties, returned to user */
if (Z_OPT_CONSTANT_P(prop)) {
- if (UNEXPECTED(zval_update_constant_ex(prop, 0, NULL) != SUCCESS)) {
+ if (UNEXPECTED(zval_update_constant_ex(prop, NULL) != SUCCESS)) {
return;
}
}
@@ -1130,7 +1213,7 @@ static void add_class_vars(zend_class_entry *ce, int statics, zval *return_value
ZEND_FUNCTION(get_class_vars)
{
zend_string *class_name;
- zend_class_entry *ce;
+ zend_class_entry *ce, *scope;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &class_name) == FAILURE) {
return;
@@ -1146,8 +1229,9 @@ ZEND_FUNCTION(get_class_vars)
return;
}
}
- add_class_vars(ce, 0, return_value);
- add_class_vars(ce, 1, return_value);
+ scope = zend_get_executed_scope();
+ add_class_vars(scope, ce, 0, return_value);
+ add_class_vars(scope, ce, 1, return_value);
}
}
/* }}} */
@@ -1245,6 +1329,7 @@ ZEND_FUNCTION(get_class_methods)
zval *klass;
zval method_name;
zend_class_entry *ce = NULL;
+ zend_class_entry *scope;
zend_function *mptr;
zend_string *key;
@@ -1263,15 +1348,16 @@ ZEND_FUNCTION(get_class_methods)
}
array_init(return_value);
+ scope = zend_get_executed_scope();
ZEND_HASH_FOREACH_STR_KEY_PTR(&ce->function_table, key, mptr) {
if ((mptr->common.fn_flags & ZEND_ACC_PUBLIC)
- || (EG(scope) &&
+ || (scope &&
(((mptr->common.fn_flags & ZEND_ACC_PROTECTED) &&
- zend_check_protected(mptr->common.scope, EG(scope)))
+ zend_check_protected(mptr->common.scope, scope))
|| ((mptr->common.fn_flags & ZEND_ACC_PRIVATE) &&
- EG(scope) == mptr->common.scope)))) {
+ scope == mptr->common.scope)))) {
size_t len = ZSTR_LEN(mptr->common.function_name);
/* Do not display old-style inherited constructors */
@@ -1950,8 +2036,12 @@ ZEND_FUNCTION(get_defined_functions)
Returns an associative array of names and values of all currently defined variable names (variables in the current scope) */
ZEND_FUNCTION(get_defined_vars)
{
- zend_array *symbol_table = zend_rebuild_symbol_table();
+ zend_array *symbol_table;
+ if (zend_forbid_dynamic_call("get_defined_vars()") == FAILURE) {
+ return;
+ }
+ symbol_table = zend_rebuild_symbol_table();
if (UNEXPECTED(symbol_table == NULL)) {
return;
}
@@ -2037,17 +2127,16 @@ ZEND_FUNCTION(zend_test_func)
{
zval *arg1, *arg2;
- zend_get_parameters(ZEND_NUM_ARGS(), 2, &arg1, &arg2);
+ zend_parse_parameters(ZEND_NUM_ARGS(), "|zz", &arg1, &arg2);
}
ZEND_FUNCTION(zend_test_func2)
{
zval *arg1, *arg2;
- zend_get_parameters(ZEND_NUM_ARGS(), 2, &arg1, &arg2);
+ zend_parse_parameters(ZEND_NUM_ARGS(), "|zz", &arg1, &arg2);
}
-
#ifdef ZTS
ZEND_FUNCTION(zend_thread_id)
{
@@ -2369,7 +2458,7 @@ ZEND_FUNCTION(debug_print_backtrace)
}
/* $this may be passed into regular internal functions */
- object = Z_OBJ(call->This);
+ object = (Z_TYPE(call->This) == IS_OBJECT) ? Z_OBJ(call->This) : NULL;
if (call->func) {
func = call->func;
@@ -2497,7 +2586,7 @@ ZEND_API void zend_fetch_debug_backtrace(zval *return_value, int skip_last, int
zend_string *function_name;
zend_string *filename;
zend_string *include_filename = NULL;
- zval stack_frame;
+ zval stack_frame, tmp;
array_init(return_value);
@@ -2559,8 +2648,10 @@ ZEND_API void zend_fetch_debug_backtrace(zval *return_value, int skip_last, int
} else {
lineno = skip->opline->lineno;
}
- add_assoc_str_ex(&stack_frame, "file", sizeof("file")-1, zend_string_copy(filename));
- add_assoc_long_ex(&stack_frame, "line", sizeof("line")-1, lineno);
+ ZVAL_STR_COPY(&tmp, filename);
+ zend_hash_add_new(Z_ARRVAL(stack_frame), CG(known_strings)[ZEND_STR_FILE], &tmp);
+ ZVAL_LONG(&tmp, lineno);
+ zend_hash_add_new(Z_ARRVAL(stack_frame), CG(known_strings)[ZEND_STR_LINE], &tmp);
/* try to fetch args only if an FCALL was just made - elsewise we're in the middle of a function
* and debug_baktrace() might have been called by the error_handler. in this case we don't
@@ -2577,8 +2668,10 @@ ZEND_API void zend_fetch_debug_backtrace(zval *return_value, int skip_last, int
break;
}
if (prev->func && ZEND_USER_CODE(prev->func->common.type)) {
- add_assoc_str_ex(&stack_frame, "file", sizeof("file")-1, zend_string_copy(prev->func->op_array.filename));
- add_assoc_long_ex(&stack_frame, "line", sizeof("line")-1, prev->opline->lineno);
+ ZVAL_STR_COPY(&tmp, prev->func->op_array.filename);
+ zend_hash_add_new(Z_ARRVAL(stack_frame), CG(known_strings)[ZEND_STR_FILE], &tmp);
+ ZVAL_LONG(&tmp, prev->opline->lineno);
+ zend_hash_add_new(Z_ARRVAL(stack_frame), CG(known_strings)[ZEND_STR_LINE], &tmp);
break;
}
prev_call = prev;
@@ -2588,7 +2681,7 @@ ZEND_API void zend_fetch_debug_backtrace(zval *return_value, int skip_last, int
}
/* $this may be passed into regular internal functions */
- object = call ? Z_OBJ(call->This) : NULL;
+ object = (call && (Z_TYPE(call->This) == IS_OBJECT)) ? Z_OBJ(call->This) : NULL;
if (call && call->func) {
func = call->func;
@@ -2603,66 +2696,69 @@ ZEND_API void zend_fetch_debug_backtrace(zval *return_value, int skip_last, int
}
if (function_name) {
- add_assoc_str_ex(&stack_frame, "function", sizeof("function")-1, zend_string_copy(function_name));
+ ZVAL_STR_COPY(&tmp, function_name);
+ zend_hash_add_new(Z_ARRVAL(stack_frame), CG(known_strings)[ZEND_STR_FUNCTION], &tmp);
if (object) {
if (func->common.scope) {
- add_assoc_str_ex(&stack_frame, "class", sizeof("class")-1, zend_string_copy(func->common.scope->name));
+ ZVAL_STR_COPY(&tmp, func->common.scope->name);
} else {
- add_assoc_str_ex(&stack_frame, "class", sizeof("class")-1, zend_string_copy(object->ce->name));
+ ZVAL_STR_COPY(&tmp, object->ce->name);
}
+ zend_hash_add_new(Z_ARRVAL(stack_frame), CG(known_strings)[ZEND_STR_CLASS], &tmp);
if ((options & DEBUG_BACKTRACE_PROVIDE_OBJECT) != 0) {
- zval zv;
- ZVAL_OBJ(&zv, object);
- add_assoc_zval_ex(&stack_frame, "object", sizeof("object")-1, &zv);
- Z_ADDREF(zv);
+ ZVAL_OBJ(&tmp, object);
+ zend_hash_add_new(Z_ARRVAL(stack_frame), CG(known_strings)[ZEND_STR_OBJECT], &tmp);
+ Z_ADDREF(tmp);
}
- add_assoc_string_ex(&stack_frame, "type", sizeof("type")-1, "->");
+ ZVAL_INTERNED_STR(&tmp, CG(known_strings)[ZEND_STR_OBJECT_OPERATOR]);
+ zend_hash_add_new(Z_ARRVAL(stack_frame), CG(known_strings)[ZEND_STR_TYPE], &tmp);
} else if (func->common.scope) {
- add_assoc_str_ex(&stack_frame, "class", sizeof("class")-1, zend_string_copy(func->common.scope->name));
- add_assoc_string_ex(&stack_frame, "type", sizeof("type")-1, "::");
+ ZVAL_STR_COPY(&tmp, func->common.scope->name);
+ zend_hash_add_new(Z_ARRVAL(stack_frame), CG(known_strings)[ZEND_STR_CLASS], &tmp);
+ ZVAL_INTERNED_STR(&tmp, CG(known_strings)[ZEND_STR_PAAMAYIM_NEKUDOTAYIM]);
+ zend_hash_add_new(Z_ARRVAL(stack_frame), CG(known_strings)[ZEND_STR_TYPE], &tmp);
}
if ((options & DEBUG_BACKTRACE_IGNORE_ARGS) == 0 &&
func->type != ZEND_EVAL_CODE) {
- zval args;
- debug_backtrace_get_args(call, &args);
- add_assoc_zval_ex(&stack_frame, "args", sizeof("args")-1, &args);
+ debug_backtrace_get_args(call, &tmp);
+ zend_hash_add_new(Z_ARRVAL(stack_frame), CG(known_strings)[ZEND_STR_ARGS], &tmp);
}
} else {
/* i know this is kinda ugly, but i'm trying to avoid extra cycles in the main execution loop */
zend_bool build_filename_arg = 1;
- const char *pseudo_function_name;
+ zend_string *pseudo_function_name;
if (!ptr->func || !ZEND_USER_CODE(ptr->func->common.type) || ptr->opline->opcode != ZEND_INCLUDE_OR_EVAL) {
/* can happen when calling eval from a custom sapi */
- pseudo_function_name = "unknown";
+ pseudo_function_name = CG(known_strings)[ZEND_STR_UNKNOWN];
build_filename_arg = 0;
} else
switch (ptr->opline->extended_value) {
case ZEND_EVAL:
- pseudo_function_name = "eval";
+ pseudo_function_name = CG(known_strings)[ZEND_STR_EVAL];
build_filename_arg = 0;
break;
case ZEND_INCLUDE:
- pseudo_function_name = "include";
+ pseudo_function_name = CG(known_strings)[ZEND_STR_INCLUDE];
break;
case ZEND_REQUIRE:
- pseudo_function_name = "require";
+ pseudo_function_name = CG(known_strings)[ZEND_STR_REQUIRE];
break;
case ZEND_INCLUDE_ONCE:
- pseudo_function_name = "include_once";
+ pseudo_function_name = CG(known_strings)[ZEND_STR_INCLUDE_ONCE];
break;
case ZEND_REQUIRE_ONCE:
- pseudo_function_name = "require_once";
+ pseudo_function_name = CG(known_strings)[ZEND_STR_REQUIRE_ONCE];
break;
default:
/* this can actually happen if you use debug_backtrace() in your error_handler and
* you're in the top-scope */
- pseudo_function_name = "unknown";
+ pseudo_function_name = CG(known_strings)[ZEND_STR_UNKNOWN];
build_filename_arg = 0;
break;
}
@@ -2676,11 +2772,13 @@ ZEND_API void zend_fetch_debug_backtrace(zval *return_value, int skip_last, int
if we have called include in the frame above - this is the file we have included.
*/
- add_next_index_str(&arg_array, zend_string_copy(include_filename));
- add_assoc_zval_ex(&stack_frame, "args", sizeof("args")-1, &arg_array);
+ ZVAL_STR_COPY(&tmp, include_filename);
+ zend_hash_next_index_insert_new(Z_ARRVAL(arg_array), &tmp);
+ zend_hash_add_new(Z_ARRVAL(stack_frame), CG(known_strings)[ZEND_STR_ARGS], &arg_array);
}
- add_assoc_string_ex(&stack_frame, "function", sizeof("function")-1, (char *) pseudo_function_name);
+ ZVAL_INTERNED_STR(&tmp, pseudo_function_name);
+ zend_hash_add_new(Z_ARRVAL(stack_frame), CG(known_strings)[ZEND_STR_FUNCTION], &tmp);
}
zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &stack_frame);
diff --git a/Zend/zend_closures.c b/Zend/zend_closures.c
index def114c4ec..810ac6a66c 100644
--- a/Zend/zend_closures.c
+++ b/Zend/zend_closures.c
@@ -52,17 +52,11 @@ static zend_object_handlers closure_handlers;
ZEND_METHOD(Closure, __invoke) /* {{{ */
{
zend_function *func = EX(func);
- zval *arguments;
+ zval *arguments = ZEND_CALL_ARG(execute_data, 1);
- arguments = emalloc(sizeof(zval) * ZEND_NUM_ARGS());
- if (zend_get_parameters_array_ex(ZEND_NUM_ARGS(), arguments) == FAILURE) {
- efree(arguments);
- zend_throw_error(NULL, "Cannot get arguments for calling closure");
- RETVAL_FALSE;
- } else if (call_user_function_ex(CG(function_table), NULL, getThis(), return_value, ZEND_NUM_ARGS(), arguments, 1, NULL) == FAILURE) {
+ if (call_user_function_ex(CG(function_table), NULL, getThis(), return_value, ZEND_NUM_ARGS(), arguments, 1, NULL) == FAILURE) {
RETVAL_FALSE;
}
- efree(arguments);
/* destruct the function also, then - we have allocated it in get_method */
zend_string_release(func->internal_function.function_name);
@@ -77,16 +71,17 @@ static zend_bool zend_valid_closure_binding(
zend_closure *closure, zval *newthis, zend_class_entry *scope) /* {{{ */
{
zend_function *func = &closure->func;
+ zend_bool is_fake_closure = (func->common.fn_flags & ZEND_ACC_FAKE_CLOSURE) != 0;
if (newthis) {
if (func->common.fn_flags & ZEND_ACC_STATIC) {
zend_error(E_WARNING, "Cannot bind an instance to a static closure");
return 0;
}
- if (func->type == ZEND_INTERNAL_FUNCTION && func->common.scope &&
+ if (is_fake_closure && func->common.scope &&
!instanceof_function(Z_OBJCE_P(newthis), func->common.scope)) {
/* Binding incompatible $this to an internal method is not supported. */
- zend_error(E_WARNING, "Cannot bind internal method %s::%s() to object of class %s",
+ zend_error(E_WARNING, "Cannot bind method %s::%s() to object of class %s",
ZSTR_VAL(func->common.scope->name),
ZSTR_VAL(func->common.function_name),
ZSTR_VAL(Z_OBJCE_P(newthis)->name));
@@ -105,7 +100,7 @@ static zend_bool zend_valid_closure_binding(
return 0;
}
- if ((func->common.fn_flags & ZEND_ACC_FAKE_CLOSURE) && scope != func->common.scope) {
+ if (is_fake_closure && scope != func->common.scope) {
zend_error(E_WARNING, "Cannot rebind scope of closure created by ReflectionFunctionAbstract::getClosure()");
return 0;
}
@@ -276,7 +271,7 @@ ZEND_API zend_function *zend_get_closure_invoke_method(zend_object *object) /* {
invoke->internal_function.handler = ZEND_MN(Closure___invoke);
invoke->internal_function.module = 0;
invoke->internal_function.scope = zend_ce_closure;
- invoke->internal_function.function_name = zend_string_init(ZEND_INVOKE_FUNC_NAME, sizeof(ZEND_INVOKE_FUNC_NAME)-1, 0);
+ invoke->internal_function.function_name = CG(known_strings)[ZEND_STR_MAGIC_INVOKE];
return invoke;
}
/* }}} */
@@ -387,23 +382,16 @@ static zend_object *zend_closure_clone(zval *zobject) /* {{{ */
int zend_closure_get_closure(zval *obj, zend_class_entry **ce_ptr, zend_function **fptr_ptr, zend_object **obj_ptr) /* {{{ */
{
- zend_closure *closure;
-
- if (Z_TYPE_P(obj) != IS_OBJECT) {
- return FAILURE;
- }
-
- closure = (zend_closure *)Z_OBJ_P(obj);
+ zend_closure *closure = (zend_closure *)Z_OBJ_P(obj);
*fptr_ptr = &closure->func;
*ce_ptr = closure->called_scope;
- if (obj_ptr) {
- if (Z_TYPE(closure->this_ptr) != IS_UNDEF) {
- *obj_ptr = Z_OBJ(closure->this_ptr);
- } else {
- *obj_ptr = NULL;
- }
+ if (Z_TYPE(closure->this_ptr) != IS_UNDEF) {
+ *obj_ptr = Z_OBJ(closure->this_ptr);
+ } else {
+ *obj_ptr = NULL;
}
+
return SUCCESS;
}
/* }}} */
@@ -423,12 +411,12 @@ static HashTable *zend_closure_get_debug_info(zval *object, int *is_temp) /* {{{
if (closure->func.type == ZEND_USER_FUNCTION && closure->func.op_array.static_variables) {
HashTable *static_variables = closure->func.op_array.static_variables;
ZVAL_ARR(&val, zend_array_dup(static_variables));
- zend_hash_str_update(debug_info, "static", sizeof("static")-1, &val);
+ zend_hash_update(debug_info, CG(known_strings)[ZEND_STR_STATIC], &val);
}
if (Z_TYPE(closure->this_ptr) != IS_UNDEF) {
Z_ADDREF(closure->this_ptr);
- zend_hash_str_update(debug_info, "this", sizeof("this")-1, &closure->this_ptr);
+ zend_hash_update(debug_info, CG(known_strings)[ZEND_STR_THIS], &closure->this_ptr);
}
if (arg_info &&
@@ -522,7 +510,6 @@ void zend_register_closure_ce(void) /* {{{ */
memcpy(&closure_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
closure_handlers.free_obj = zend_closure_free_storage;
- closure_handlers.clone_obj = NULL;
closure_handlers.get_constructor = zend_closure_get_constructor;
closure_handlers.get_method = zend_closure_get_method;
closure_handlers.write_property = zend_closure_write_property;
@@ -566,11 +553,8 @@ ZEND_API void zend_create_closure(zval *res, zend_function *func, zend_class_ent
closure->func.common.prototype = (zend_function*)closure;
closure->func.common.fn_flags |= ZEND_ACC_CLOSURE;
if (closure->func.op_array.static_variables) {
- HashTable *static_variables = closure->func.op_array.static_variables;
-
- ALLOC_HASHTABLE(closure->func.op_array.static_variables);
- zend_hash_init(closure->func.op_array.static_variables, zend_hash_num_elements(static_variables), NULL, ZVAL_PTR_DTOR, 0);
- zend_hash_apply_with_arguments(static_variables, zval_copy_static_var, 1, closure->func.op_array.static_variables);
+ closure->func.op_array.static_variables =
+ zend_array_dup(closure->func.op_array.static_variables);
}
if (UNEXPECTED(!closure->func.op_array.run_time_cache)) {
closure->func.op_array.run_time_cache = func->op_array.run_time_cache = zend_arena_alloc(&CG(arena), func->op_array.cache_size);
@@ -625,6 +609,14 @@ ZEND_API void zend_create_fake_closure(zval *res, zend_function *func, zend_clas
}
/* }}} */
+void zend_closure_bind_var(zval *closure_zv, zend_string *var_name, zval *var) /* {{{ */
+{
+ zend_closure *closure = (zend_closure *) Z_OBJ_P(closure_zv);
+ HashTable *static_variables = closure->func.op_array.static_variables;
+ zend_hash_update(static_variables, var_name, var);
+}
+/* }}} */
+
/*
* Local variables:
* tab-width: 4
diff --git a/Zend/zend_closures.h b/Zend/zend_closures.h
index 095dc54c24..7f8bac430c 100644
--- a/Zend/zend_closures.h
+++ b/Zend/zend_closures.h
@@ -25,6 +25,7 @@
BEGIN_EXTERN_C()
void zend_register_closure_ce(void);
+void zend_closure_bind_var(zval *closure_zv, zend_string *var_name, zval *var);
extern ZEND_API zend_class_entry *zend_ce_closure;
diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c
index 593c19e015..1d544c6561 100644
--- a/Zend/zend_compile.c
+++ b/Zend/zend_compile.c
@@ -60,7 +60,7 @@ typedef struct _zend_loop_var {
uint32_t var_num;
union {
uint32_t try_catch_offset;
- uint32_t brk_cont_offset;
+ uint32_t live_range_offset;
} u;
} zend_loop_var;
@@ -86,6 +86,8 @@ ZEND_API zend_compiler_globals compiler_globals;
ZEND_API zend_executor_globals executor_globals;
#endif
+static zend_op *zend_emit_op(znode *result, zend_uchar opcode, znode *op1, znode *op2);
+
static void zend_destroy_property_info_internal(zval *zv) /* {{{ */
{
zend_property_info *property_info = Z_PTR_P(zv);
@@ -95,6 +97,12 @@ static void zend_destroy_property_info_internal(zval *zv) /* {{{ */
}
/* }}} */
+static void zend_destroy_class_constant_internal(zval *zv) /* {{{ */
+{
+ free(Z_PTR_P(zv));
+}
+/* }}} */
+
static zend_string *zend_new_interned_string_safe(zend_string *str) /* {{{ */ {
zend_string *interned_str;
@@ -151,6 +159,7 @@ static const struct reserved_class_name reserved_class_names[] = {
{ZEND_STRL("static")},
{ZEND_STRL("string")},
{ZEND_STRL("true")},
+ {ZEND_STRL("void")},
{NULL, 0}
};
@@ -194,6 +203,7 @@ static const builtin_type_info builtin_types[] = {
{ZEND_STRL("float"), IS_DOUBLE},
{ZEND_STRL("string"), IS_STRING},
{ZEND_STRL("bool"), _IS_BOOL},
+ {ZEND_STRL("void"), IS_VOID},
{NULL, 0, IS_UNDEF}
};
@@ -221,16 +231,23 @@ void zend_oparray_context_begin(zend_oparray_context *prev_context) /* {{{ */
CG(context).opcodes_size = INITIAL_OP_ARRAY_SIZE;
CG(context).vars_size = 0;
CG(context).literals_size = 0;
- CG(context).current_brk_cont = -1;
CG(context).backpatch_count = 0;
CG(context).in_finally = 0;
CG(context).fast_call_var = -1;
+ CG(context).try_catch_offset = -1;
+ CG(context).current_brk_cont = -1;
+ CG(context).last_brk_cont = 0;
+ CG(context).brk_cont_array = NULL;
CG(context).labels = NULL;
}
/* }}} */
void zend_oparray_context_end(zend_oparray_context *prev_context) /* {{{ */
{
+ if (CG(context).brk_cont_array) {
+ efree(CG(context).brk_cont_array);
+ CG(context).brk_cont_array = NULL;
+ }
if (CG(context).labels) {
zend_hash_destroy(CG(context).labels);
FREE_HASHTABLE(CG(context).labels);
@@ -570,22 +587,111 @@ void zend_stop_lexing(void)
LANG_SCNG(yy_cursor) = LANG_SCNG(yy_limit);
}
+static uint32_t zend_start_live_range(zend_op_array *op_array, uint32_t start) /* {{{ */
+{
+ zend_live_range *range;
+
+ op_array->last_live_range++;
+ op_array->live_range = erealloc(op_array->live_range, sizeof(zend_live_range) * op_array->last_live_range);
+ range = op_array->live_range + op_array->last_live_range - 1;
+ range->start = start;
+ return op_array->last_live_range - 1;
+}
+/* }}} */
+
+static uint32_t zend_start_live_range_ex(zend_op_array *op_array, uint32_t start) /* {{{ */
+{
+ if (op_array->last_live_range == 0 ||
+ op_array->live_range[op_array->last_live_range - 1].start <= start) {
+ return zend_start_live_range(op_array, start);
+ } else {
+ /* Live ranges have to be sorted by "start" field */
+ uint32_t n = op_array->last_live_range;
+
+ /* move early ranges to make a room */
+ op_array->last_live_range = n + 1;
+ op_array->live_range = erealloc(op_array->live_range, sizeof(zend_live_range) * op_array->last_live_range);
+ do {
+ op_array->live_range[n] = op_array->live_range[n-1];
+ n--;
+ } while (n != 0 && op_array->live_range[n-1].start > start);
+
+ /* initialize new range */
+ op_array->live_range[n].start = start;
+
+ /* update referens to live-ranges from stack */
+ if (!zend_stack_is_empty(&CG(loop_var_stack))) {
+ zend_loop_var *loop_var = zend_stack_top(&CG(loop_var_stack));
+ zend_loop_var *base = zend_stack_base(&CG(loop_var_stack));
+ int check_opcodes = 0;
+
+ for (; loop_var >= base; loop_var--) {
+ if (loop_var->opcode == ZEND_RETURN) {
+ /* Stack separator */
+ break;
+ } else if (loop_var->opcode == ZEND_FREE ||
+ loop_var->opcode == ZEND_FE_FREE) {
+ if (loop_var->u.live_range_offset >= n) {
+ loop_var->u.live_range_offset++;
+ check_opcodes = 1;
+ } else {
+ break;
+ }
+ }
+ }
+
+ /* update previously generated FREE/FE_FREE opcodes */
+ if (check_opcodes) {
+ zend_op *opline = op_array->opcodes + op_array->live_range[n+1].start;
+ zend_op *end = op_array->opcodes + op_array->last;
+
+ while (opline < end) {
+ if ((opline->opcode == ZEND_FREE ||
+ opline->opcode == ZEND_FE_FREE) &&
+ (opline->extended_value & ZEND_FREE_ON_RETURN) &&
+ opline->op2.num >= n) {
+ opline->op2.num++;
+ }
+ opline++;
+ }
+ }
+ }
+ return n;
+ }
+}
+/* }}} */
+
+static void zend_end_live_range(zend_op_array *op_array, uint32_t offset, uint32_t end, uint32_t kind, uint32_t var) /* {{{ */
+{
+ zend_live_range *range = op_array->live_range + offset;
+
+ if (range->start == end && offset == op_array->last_live_range - 1) {
+ op_array->last_live_range--;
+ } else {
+ range->end = end;
+ range->var = (var * sizeof(zval)) | kind;
+ }
+}
+/* }}} */
+
static inline void zend_begin_loop(zend_uchar free_opcode, const znode *loop_var) /* {{{ */
{
zend_brk_cont_element *brk_cont_element;
int parent = CG(context).current_brk_cont;
zend_loop_var info = {0};
- CG(context).current_brk_cont = CG(active_op_array)->last_brk_cont;
- brk_cont_element = get_next_brk_cont_element(CG(active_op_array));
+ CG(context).current_brk_cont = CG(context).last_brk_cont;
+ brk_cont_element = get_next_brk_cont_element();
brk_cont_element->parent = parent;
if (loop_var && (loop_var->op_type & (IS_VAR|IS_TMP_VAR))) {
+ uint32_t start = get_next_op_number(CG(active_op_array));
+
info.opcode = free_opcode;
info.var_type = loop_var->op_type;
info.var_num = loop_var->u.op.var;
- info.u.brk_cont_offset = CG(context).current_brk_cont;
- brk_cont_element->start = get_next_op_number(CG(active_op_array));
+ info.u.live_range_offset = zend_start_live_range(CG(active_op_array), start);
+ brk_cont_element->start = start;
} else {
info.opcode = ZEND_NOP;
/* The start field is used to free temporary variables in case of exceptions.
@@ -597,14 +703,22 @@ static inline void zend_begin_loop(zend_uchar free_opcode, const znode *loop_var
}
/* }}} */
-static inline void zend_end_loop(int cont_addr) /* {{{ */
+static inline void zend_end_loop(int cont_addr, const znode *var_node) /* {{{ */
{
+ uint32_t end = get_next_op_number(CG(active_op_array));
zend_brk_cont_element *brk_cont_element
- = &CG(active_op_array)->brk_cont_array[CG(context).current_brk_cont];
+ = &CG(context).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));
+ brk_cont_element->brk = end;
CG(context).current_brk_cont = brk_cont_element->parent;
+ if (brk_cont_element->start != -1) {
+ zend_loop_var *loop_var = zend_stack_top(&CG(loop_var_stack));
+ zend_end_live_range(CG(active_op_array), loop_var->u.live_range_offset, end,
+ loop_var->opcode == ZEND_FE_FREE ? ZEND_LIVE_LOOP : ZEND_LIVE_TMPVAR,
+ var_node->u.op.var);
+ }
+
zend_stack_del_top(&CG(loop_var_stack));
}
/* }}} */
@@ -624,11 +738,7 @@ void zend_do_free(znode *op1) /* {{{ */
}
}
- opline = get_next_op(CG(active_op_array));
-
- opline->opcode = ZEND_FREE;
- SET_NODE(opline->op1, op1);
- SET_UNUSED(opline->op2);
+ zend_emit_op(NULL, ZEND_FREE, op1, NULL);
} else if (op1->op_type == IS_VAR) {
zend_op *opline = &CG(active_op_array)->opcodes[CG(active_op_array)->last-1];
while (opline->opcode == ZEND_END_SILENCE ||
@@ -640,38 +750,30 @@ void zend_do_free(znode *op1) /* {{{ */
&& opline->result.var == op1->u.op.var) {
if (opline->opcode == ZEND_FETCH_R ||
opline->opcode == ZEND_FETCH_DIM_R ||
- opline->opcode == ZEND_FETCH_OBJ_R) {
+ opline->opcode == ZEND_FETCH_OBJ_R ||
+ opline->opcode == ZEND_FETCH_STATIC_PROP_R) {
/* It's very rare and useless case. It's better to use
additional FREE opcode and simplify the FETCH handlers
their selves */
- opline = get_next_op(CG(active_op_array));
- opline->opcode = ZEND_FREE;
- SET_NODE(opline->op1, op1);
- SET_UNUSED(opline->op2);
+ zend_emit_op(NULL, ZEND_FREE, op1, NULL);
+ } else if (opline->opcode == ZEND_FETCH_THIS) {
+ opline->opcode = ZEND_NOP;
+ opline->result_type = IS_UNUSED;
} else {
- opline->result_type |= EXT_TYPE_UNUSED;
+ opline->result_type = IS_UNUSED;
}
} else {
while (opline >= CG(active_op_array)->opcodes) {
if (opline->opcode == ZEND_FETCH_LIST &&
opline->op1_type == IS_VAR &&
opline->op1.var == op1->u.op.var) {
- opline = get_next_op(CG(active_op_array));
-
- opline->opcode = ZEND_FREE;
- SET_NODE(opline->op1, op1);
- SET_UNUSED(opline->op2);
+ zend_emit_op(NULL, ZEND_FREE, op1, NULL);
return;
}
if (opline->result_type == IS_VAR
&& opline->result.var == op1->u.op.var) {
if (opline->opcode == ZEND_NEW) {
- opline->result_type |= EXT_TYPE_UNUSED;
- opline = &CG(active_op_array)->opcodes[CG(active_op_array)->last-1];
- while (opline->opcode != ZEND_DO_FCALL || opline->op1.num != ZEND_CALL_CTOR) {
- opline--;
- }
- opline->op1.num |= ZEND_CALL_CTOR_RESULT_UNUSED;
+ zend_emit_op(NULL, ZEND_FREE, op1, NULL);
}
break;
}
@@ -952,24 +1054,24 @@ ZEND_API void function_add_ref(zend_function *function) /* {{{ */
ZEND_API int do_bind_function(const zend_op_array *op_array, const zend_op *opline, HashTable *function_table, zend_bool compile_time) /* {{{ */
{
zend_function *function, *new_function;
- zval *op1, *op2;
+ zval *lcname, *rtd_key;
if (compile_time) {
- op1 = CT_CONSTANT_EX(op_array, opline->op1.constant);
- op2 = CT_CONSTANT_EX(op_array, opline->op2.constant);
+ lcname = CT_CONSTANT_EX(op_array, opline->op1.constant);
+ rtd_key = lcname + 1;
} else {
- op1 = RT_CONSTANT(op_array, opline->op1);
- op2 = RT_CONSTANT(op_array, opline->op2);
+ lcname = RT_CONSTANT(op_array, opline->op1);
+ rtd_key = lcname + 1;
}
- function = zend_hash_find_ptr(function_table, Z_STR_P(op1));
+ function = zend_hash_find_ptr(function_table, Z_STR_P(rtd_key));
new_function = zend_arena_alloc(&CG(arena), sizeof(zend_op_array));
memcpy(new_function, function, sizeof(zend_op_array));
- if (zend_hash_add_ptr(function_table, Z_STR_P(op2), new_function) == NULL) {
+ if (zend_hash_add_ptr(function_table, Z_STR_P(lcname), new_function) == NULL) {
int error_level = compile_time ? E_COMPILE_ERROR : E_ERROR;
zend_function *old_function;
- if ((old_function = zend_hash_find_ptr(function_table, Z_STR_P(op2))) != NULL
+ if ((old_function = zend_hash_find_ptr(function_table, Z_STR_P(lcname))) != NULL
&& old_function->type == ZEND_USER_FUNCTION
&& old_function->op_array.last > 0) {
zend_error_noreturn(error_level, "Cannot redeclare %s() (previously declared in %s:%d)",
@@ -993,21 +1095,21 @@ ZEND_API int do_bind_function(const zend_op_array *op_array, const zend_op *opli
ZEND_API zend_class_entry *do_bind_class(const zend_op_array* op_array, const zend_op *opline, HashTable *class_table, zend_bool compile_time) /* {{{ */
{
zend_class_entry *ce;
- zval *op1, *op2;
+ zval *lcname, *rtd_key;
if (compile_time) {
- op1 = CT_CONSTANT_EX(op_array, opline->op1.constant);
- op2 = CT_CONSTANT_EX(op_array, opline->op2.constant);
+ lcname = CT_CONSTANT_EX(op_array, opline->op1.constant);
+ rtd_key = lcname + 1;
} else {
- op1 = RT_CONSTANT(op_array, opline->op1);
- op2 = RT_CONSTANT(op_array, opline->op2);
+ lcname = RT_CONSTANT(op_array, opline->op1);
+ rtd_key = lcname + 1;
}
- if ((ce = zend_hash_find_ptr(class_table, Z_STR_P(op1))) == NULL) {
- zend_error_noreturn(E_COMPILE_ERROR, "Internal Zend error - Missing class information for %s", Z_STRVAL_P(op1));
+ if ((ce = zend_hash_find_ptr(class_table, Z_STR_P(rtd_key))) == NULL) {
+ zend_error_noreturn(E_COMPILE_ERROR, "Internal Zend error - Missing class information for %s", Z_STRVAL_P(rtd_key));
return NULL;
}
ce->refcount++;
- if (zend_hash_add_ptr(class_table, Z_STR_P(op2), ce) == NULL) {
+ if (zend_hash_add_ptr(class_table, Z_STR_P(lcname), ce) == NULL) {
ce->refcount--;
if (!compile_time) {
/* If we're in compile time, in practice, it's quite possible
@@ -1030,17 +1132,17 @@ 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_class_entry *ce;
- zval *op1, *op2;
+ zval *lcname, *rtd_key;
if (compile_time) {
- op1 = CT_CONSTANT_EX(op_array, opline->op1.constant);
- op2 = CT_CONSTANT_EX(op_array, opline->op2.constant);
+ lcname = CT_CONSTANT_EX(op_array, opline->op1.constant);
+ rtd_key = lcname + 1;
} else {
- op1 = RT_CONSTANT(op_array, opline->op1);
- op2 = RT_CONSTANT(op_array, opline->op2);
+ lcname = RT_CONSTANT(op_array, opline->op1);
+ rtd_key = lcname + 1;
}
- ce = zend_hash_find_ptr(class_table, Z_STR_P(op1));
+ ce = zend_hash_find_ptr(class_table, Z_STR_P(rtd_key));
if (!ce) {
if (!compile_time) {
@@ -1049,12 +1151,12 @@ ZEND_API zend_class_entry *do_bind_inherited_class(const zend_op_array *op_array
* so we shut up about it. This allows the if (!defined('FOO')) { return; }
* approach to work.
*/
- zend_error_noreturn(E_COMPILE_ERROR, "Cannot declare %s %s, because the name is already in use", zend_get_object_type(Z_OBJCE_P(op2)), Z_STRVAL_P(op2));
+ zend_error_noreturn(E_COMPILE_ERROR, "Cannot declare %s, because the name is already in use", zend_get_object_type(Z_OBJCE_P(lcname)), Z_STRVAL_P(lcname));
}
return NULL;
}
- if (zend_hash_exists(class_table, Z_STR_P(op2))) {
+ if (zend_hash_exists(class_table, Z_STR_P(lcname))) {
zend_error_noreturn(E_COMPILE_ERROR, "Cannot declare %s %s, because the name is already in use", zend_get_object_type(ce), ZSTR_VAL(ce->name));
}
@@ -1063,7 +1165,7 @@ ZEND_API zend_class_entry *do_bind_inherited_class(const zend_op_array *op_array
ce->refcount++;
/* Register the derived class */
- if (zend_hash_add_ptr(class_table, Z_STR_P(op2), ce) == NULL) {
+ if (zend_hash_add_ptr(class_table, Z_STR_P(lcname), ce) == NULL) {
zend_error_noreturn(E_COMPILE_ERROR, "Cannot declare %s %s, because the name is already in use", zend_get_object_type(ce), ZSTR_VAL(ce->name));
}
return ce;
@@ -1137,9 +1239,9 @@ void zend_do_early_binding(void) /* {{{ */
return;
}
- zend_hash_del(table, Z_STR_P(CT_CONSTANT(opline->op1)));
+ zend_hash_del(table, Z_STR_P(CT_CONSTANT(opline->op1)+1));
+ zend_del_literal(CG(active_op_array), opline->op1.constant+1);
zend_del_literal(CG(active_op_array), opline->op1.constant);
- zend_del_literal(CG(active_op_array), opline->op2.constant);
MAKE_NOP(opline);
}
/* }}} */
@@ -1150,19 +1252,19 @@ static void zend_mark_function_as_generator() /* {{{ */
zend_error_noreturn(E_COMPILE_ERROR,
"The \"yield\" expression can only be used inside a function");
}
+
if (CG(active_op_array)->fn_flags & ZEND_ACC_HAS_RETURN_TYPE) {
const char *msg = "Generators may only declare a return type of Generator, Iterator or Traversable, %s is not permitted";
- if (!CG(active_op_array)->arg_info[-1].class_name) {
- zend_error_noreturn(E_COMPILE_ERROR, msg,
- zend_get_type_by_const(CG(active_op_array)->arg_info[-1].type_hint));
+ zend_arg_info return_info = CG(active_op_array)->arg_info[-1];
+
+ if (!return_info.class_name) {
+ zend_error_noreturn(E_COMPILE_ERROR, msg, zend_get_type_by_const(return_info.type_hint));
}
- if (!(ZSTR_LEN(CG(active_op_array)->arg_info[-1].class_name) == sizeof("Traversable")-1
- && zend_binary_strcasecmp(ZSTR_VAL(CG(active_op_array)->arg_info[-1].class_name), sizeof("Traversable")-1, "Traversable", sizeof("Traversable")-1) == 0) &&
- !(ZSTR_LEN(CG(active_op_array)->arg_info[-1].class_name) == sizeof("Iterator")-1
- && zend_binary_strcasecmp(ZSTR_VAL(CG(active_op_array)->arg_info[-1].class_name), sizeof("Iterator")-1, "Iterator", sizeof("Iterator")-1) == 0) &&
- !(ZSTR_LEN(CG(active_op_array)->arg_info[-1].class_name) == sizeof("Generator")-1
- && zend_binary_strcasecmp(ZSTR_VAL(CG(active_op_array)->arg_info[-1].class_name), sizeof("Generator")-1, "Generator", sizeof("Generator")-1) == 0)) {
- zend_error_noreturn(E_COMPILE_ERROR, msg, ZSTR_VAL(CG(active_op_array)->arg_info[-1].class_name));
+
+ if (!zend_string_equals_literal_ci(return_info.class_name, "Traversable")
+ && !zend_string_equals_literal_ci(return_info.class_name, "Iterator")
+ && !zend_string_equals_literal_ci(return_info.class_name, "Generator")) {
+ zend_error_noreturn(E_COMPILE_ERROR, msg, ZSTR_VAL(return_info.class_name));
}
}
@@ -1416,14 +1518,15 @@ static zend_bool zend_try_compile_const_expr_resolve_class_name(zval *zv, zend_a
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);
+ zend_class_constant *cc;
zval *c;
if (class_name_refers_to_active_ce(class_name, fetch_type)) {
- c = zend_hash_find(&CG(active_class_entry)->constants_table, name);
+ cc = zend_hash_find_ptr(&CG(active_class_entry)->constants_table, name);
} else if (fetch_type == ZEND_FETCH_CLASS_DEFAULT && !(CG(compiler_options) & ZEND_COMPILE_NO_CONSTANT_SUBSTITUTION)) {
zend_class_entry *ce = zend_hash_find_ptr_lc(CG(class_table), ZSTR_VAL(class_name), ZSTR_LEN(class_name));
if (ce) {
- c = zend_hash_find(&ce->constants_table, name);
+ cc = zend_hash_find_ptr(&ce->constants_table, name);
} else {
return 0;
}
@@ -1435,8 +1538,14 @@ static zend_bool zend_try_ct_eval_class_const(zval *zv, zend_string *class_name,
return 0;
}
+ if (!cc || !zend_verify_const_access(cc, CG(active_class_entry))) {
+ return 0;
+ }
+
+ c = &cc->value;
+
/* Substitute case-sensitive (or lowercase) persistent class constants */
- if (c && Z_TYPE_P(c) < IS_OBJECT) {
+ if (Z_TYPE_P(c) < IS_OBJECT) {
ZVAL_DUP(zv, c);
return 1;
}
@@ -1617,7 +1726,6 @@ again:
ZEND_API void zend_initialize_class_data(zend_class_entry *ce, zend_bool nullify_handlers) /* {{{ */
{
zend_bool persistent_hashes = (ce->type == ZEND_INTERNAL_CLASS) ? 1 : 0;
- dtor_func_t zval_ptr_dtor_func = ((persistent_hashes) ? ZVAL_INTERNAL_PTR_DTOR : ZVAL_PTR_DTOR);
ce->refcount = 1;
ce->ce_flags = ZEND_ACC_CONSTANTS_UPDATED;
@@ -1629,7 +1737,7 @@ ZEND_API void zend_initialize_class_data(zend_class_entry *ce, zend_bool nullify
ce->default_properties_table = NULL;
ce->default_static_members_table = NULL;
zend_hash_init_ex(&ce->properties_info, 8, NULL, (persistent_hashes ? zend_destroy_property_info_internal : NULL), persistent_hashes, 0);
- zend_hash_init_ex(&ce->constants_table, 8, NULL, zval_ptr_dtor_func, persistent_hashes, 0);
+ zend_hash_init_ex(&ce->constants_table, 8, NULL, (persistent_hashes ? zend_destroy_class_constant_internal : NULL), persistent_hashes, 0);
zend_hash_init_ex(&ce->function_table, 8, NULL, ZEND_FUNCTION_DTOR, persistent_hashes, 0);
if (ce->type == ZEND_INTERNAL_CLASS) {
@@ -1821,25 +1929,30 @@ ZEND_API size_t zend_dirname(char *path, size_t len)
static void zend_adjust_for_fetch_type(zend_op *opline, uint32_t type) /* {{{ */
{
+ zend_uchar factor = (opline->opcode == ZEND_FETCH_STATIC_PROP_R) ? 1 : 3;
+
+ if (opline->opcode == ZEND_FETCH_THIS) {
+ return;
+ }
+
switch (type & BP_VAR_MASK) {
case BP_VAR_R:
return;
case BP_VAR_W:
- case BP_VAR_REF:
- opline->opcode += 3;
+ opline->opcode += 1 * factor;
return;
case BP_VAR_RW:
- opline->opcode += 6;
+ opline->opcode += 2 * factor;
return;
case BP_VAR_IS:
- opline->opcode += 9;
+ opline->opcode += 3 * factor;
return;
case BP_VAR_FUNC_ARG:
- opline->opcode += 12;
+ opline->opcode += 4 * factor;
opline->extended_value |= type >> BP_VAR_SHIFT;
return;
case BP_VAR_UNSET:
- opline->opcode += 15;
+ opline->opcode += 5 * factor;
return;
EMPTY_SWITCH_DEFAULT_CASE()
}
@@ -1862,6 +1975,136 @@ static inline void zend_make_tmp_result(znode *result, zend_op *opline) /* {{{ *
}
/* }}} */
+static void zend_find_live_range(zend_op *opline, zend_uchar type, uint32_t var) /* {{{ */
+{
+ zend_op *def = opline;
+
+ while (def != CG(active_op_array)->opcodes) {
+ def--;
+ if (def->result_type == type && def->result.var == var) {
+ if (def->opcode == ZEND_ADD_ARRAY_ELEMENT ||
+ def->opcode == ZEND_ROPE_ADD) {
+ /* not a real definition */
+ continue;
+ } else if (def->opcode == ZEND_JMPZ_EX ||
+ def->opcode == ZEND_JMPNZ_EX ||
+ def->opcode == ZEND_BOOL ||
+ def->opcode == ZEND_BOOL_NOT) {
+ /* result IS_BOOL, it does't have to be destroyed */
+ break;
+ } else if (def->opcode == ZEND_DECLARE_CLASS ||
+ def->opcode == ZEND_DECLARE_INHERITED_CLASS ||
+ def->opcode == ZEND_DECLARE_INHERITED_CLASS_DELAYED ||
+ def->opcode == ZEND_DECLARE_ANON_CLASS ||
+ def->opcode == ZEND_DECLARE_ANON_INHERITED_CLASS) {
+ /* classes don't have to be destroyed */
+ break;
+ } else if (def->opcode == ZEND_FAST_CALL) {
+ /* fast_calls don't have to be destroyed */
+ break;
+ } else if (def->opcode == ZEND_NEW) {
+ /* Objects created via ZEND_NEW are only fully initialized
+ * after the DO_FCALL (constructor call) */
+ def = CG(active_op_array)->opcodes + def->op2.opline_num - 1;
+ if (def + 1 == opline) {
+ break;
+ }
+ }
+
+ zend_end_live_range(CG(active_op_array),
+ zend_start_live_range_ex(CG(active_op_array),
+ def + 1 - CG(active_op_array)->opcodes),
+ opline - CG(active_op_array)->opcodes,
+ ZEND_LIVE_TMPVAR, var);
+ break;
+ }
+ }
+}
+/* }}} */
+
+static zend_always_inline int zend_is_def_range(zend_op *opline, zend_uchar type, uint32_t var) /* {{{ */
+{
+ while (1) {
+ if (opline->result_type == type && opline->result.var == var) {
+ return opline->opcode != ZEND_ADD_ARRAY_ELEMENT &&
+ opline->opcode != ZEND_ROPE_ADD;
+ } else if (opline->opcode == ZEND_OP_DATA) {
+ return (opline-1)->result_type == type &&
+ (opline-1)->result.var == var;
+ } else if (opline->opcode == ZEND_END_SILENCE ||
+ opline->opcode == ZEND_NOP ||
+ opline->opcode == ZEND_EXT_NOP ||
+ opline->opcode == ZEND_EXT_STMT ||
+ opline->opcode == ZEND_EXT_FCALL_BEGIN ||
+ opline->opcode == ZEND_EXT_FCALL_END ||
+ opline->opcode == ZEND_TICKS) {
+ opline--;
+ } else {
+ return 0;
+ }
+ }
+}
+/* }}} */
+
+static void zend_check_live_ranges(zend_op *opline) /* {{{ */
+{
+ if ((opline->op1_type & (IS_VAR|IS_TMP_VAR)) &&
+ !zend_is_def_range(opline - 1, opline->op1_type, opline->op1.var)) {
+
+ if (opline->opcode == ZEND_OP_DATA) {
+ if (!zend_is_def_range(opline - 2, opline->op1_type, opline->op1.var)) {
+ zend_find_live_range(opline - 1, opline->op1_type, opline->op1.var);
+ }
+ } else if (opline->opcode == ZEND_INIT_STATIC_METHOD_CALL ||
+ opline->opcode == ZEND_NEW ||
+ opline->opcode == ZEND_FETCH_CLASS_CONSTANT ||
+ opline->opcode == ZEND_ADD_INTERFACE ||
+ opline->opcode == ZEND_ADD_TRAIT ||
+ opline->opcode == ZEND_BIND_TRAITS ||
+ opline->opcode == ZEND_VERIFY_ABSTRACT_CLASS) {
+ /* classes don't have to be destroyed */
+ } else if (opline->opcode == ZEND_FAST_RET) {
+ /* fast_calls don't have to be destroyed */
+ } else if (opline->opcode == ZEND_CASE ||
+ opline->opcode == ZEND_FE_FETCH_R ||
+ opline->opcode == ZEND_FE_FETCH_RW ||
+ opline->opcode == ZEND_FE_FREE ||
+ opline->opcode == ZEND_ROPE_ADD ||
+ opline->opcode == ZEND_ROPE_END ||
+ opline->opcode == ZEND_END_SILENCE ||
+ opline->opcode == ZEND_FETCH_LIST ||
+ opline->opcode == ZEND_VERIFY_RETURN_TYPE ||
+ opline->opcode == ZEND_BIND_LEXICAL) {
+ /* these opcodes are handled separately */
+ } else {
+ zend_find_live_range(opline, opline->op1_type, opline->op1.var);
+ }
+ }
+
+ if ((opline->op2_type & (IS_VAR|IS_TMP_VAR)) &&
+ !zend_is_def_range(opline - 1, opline->op2_type, opline->op2.var)) {
+
+ if (opline->opcode == ZEND_OP_DATA) {
+ if (!zend_is_def_range(opline - 2, opline->op2_type, opline->op2.var)) {
+ zend_find_live_range(opline-1, opline->op2_type, opline->op2.var);
+ }
+ } else if (opline->opcode == ZEND_FETCH_STATIC_PROP_R ||
+ opline->opcode == ZEND_FETCH_STATIC_PROP_W ||
+ opline->opcode == ZEND_FETCH_STATIC_PROP_RW ||
+ opline->opcode == ZEND_FETCH_STATIC_PROP_IS ||
+ opline->opcode == ZEND_FETCH_STATIC_PROP_FUNC_ARG ||
+ opline->opcode == ZEND_FETCH_STATIC_PROP_UNSET ||
+ opline->opcode == ZEND_UNSET_STATIC_PROP ||
+ opline->opcode == ZEND_ISSET_ISEMPTY_STATIC_PROP ||
+ opline->opcode == ZEND_INSTANCEOF) {
+ /* classes don't have to be destroyed */
+ } else {
+ zend_find_live_range(opline, opline->op2_type, opline->op2.var);
+ }
+ }
+}
+/* }}} */
+
static zend_op *zend_emit_op(znode *result, zend_uchar opcode, znode *op1, znode *op2) /* {{{ */
{
zend_op *opline = get_next_op(CG(active_op_array));
@@ -1879,6 +2122,8 @@ static zend_op *zend_emit_op(znode *result, zend_uchar opcode, znode *op1, znode
SET_NODE(opline->op2, op2);
}
+ zend_check_live_ranges(opline);
+
if (result) {
zend_make_var_result(result, opline);
}
@@ -1903,6 +2148,8 @@ static zend_op *zend_emit_op_tmp(znode *result, zend_uchar opcode, znode *op1, z
SET_NODE(opline->op2, op2);
}
+ zend_check_live_ranges(opline);
+
if (result) {
zend_make_tmp_result(result, opline);
}
@@ -2017,16 +2264,58 @@ static zend_op *zend_delayed_compile_end(uint32_t offset) /* {{{ */
for (i = offset; i < count; ++i) {
opline = get_next_op(CG(active_op_array));
memcpy(opline, &oplines[i], sizeof(zend_op));
+ zend_check_live_ranges(opline);
}
CG(delayed_oplines_stack).top = offset;
return opline;
}
/* }}} */
-static void zend_emit_return_type_check(znode *expr, zend_arg_info *return_info) /* {{{ */
+static void zend_emit_return_type_check(
+ znode *expr, zend_arg_info *return_info, zend_bool implicit) /* {{{ */
{
+ /* `return ...;` is illegal in a void function (but `return;` isn't) */
+ if (return_info->type_hint == IS_VOID) {
+ if (expr) {
+ if (expr->op_type == IS_CONST && Z_TYPE(expr->u.constant) == IS_NULL) {
+ zend_error_noreturn(E_COMPILE_ERROR,
+ "A void function must not return a value "
+ "(did you mean \"return;\" instead of \"return null;\"?)");
+ } else {
+ zend_error_noreturn(E_COMPILE_ERROR, "A void function must not return a value");
+ }
+ }
+ /* we don't need run-time check */
+ return;
+ }
+
if (return_info->type_hint != IS_UNDEF) {
- zend_op *opline = zend_emit_op(NULL, ZEND_VERIFY_RETURN_TYPE, expr, NULL);
+ zend_op *opline;
+
+ if (!expr && !implicit) {
+ if (return_info->allow_null) {
+ zend_error_noreturn(E_COMPILE_ERROR,
+ "A function with return type must return a value "
+ "(did you mean \"return null;\" instead of \"return;\"?)");
+ } else {
+ zend_error_noreturn(E_COMPILE_ERROR,
+ "A function with return type must return a value");
+ }
+ }
+
+ if (expr && expr->op_type == IS_CONST) {
+ if ((return_info->type_hint == Z_TYPE(expr->u.constant))
+ ||((return_info->type_hint == _IS_BOOL)
+ && (Z_TYPE(expr->u.constant) == IS_FALSE
+ || Z_TYPE(expr->u.constant) == IS_TRUE))
+ || (return_info->allow_null
+ && Z_TYPE(expr->u.constant) == IS_NULL)) {
+ /* we don't need run-time check */
+ return;
+ }
+ }
+
+ opline = zend_emit_op(NULL, ZEND_VERIFY_RETURN_TYPE, expr, NULL);
if (expr && expr->op_type == IS_CONST) {
opline->result_type = expr->op_type = IS_TMP_VAR;
opline->result.var = expr->u.op.var = get_temporary_variable(CG(active_op_array));
@@ -2041,19 +2330,20 @@ static void zend_emit_return_type_check(znode *expr, zend_arg_info *return_info)
}
/* }}} */
-void zend_emit_final_return(zval *zv) /* {{{ */
+void zend_emit_final_return(int return_one) /* {{{ */
{
znode zn;
zend_op *ret;
zend_bool returns_reference = (CG(active_op_array)->fn_flags & ZEND_ACC_RETURN_REFERENCE) != 0;
- if (CG(active_op_array)->fn_flags & ZEND_ACC_HAS_RETURN_TYPE) {
- zend_emit_return_type_check(NULL, CG(active_op_array)->arg_info - 1);
+ if (CG(active_op_array)->fn_flags & ZEND_ACC_HAS_RETURN_TYPE
+ && !(CG(active_op_array)->fn_flags & ZEND_ACC_GENERATOR)) {
+ zend_emit_return_type_check(NULL, CG(active_op_array)->arg_info - 1, 1);
}
zn.op_type = IS_CONST;
- if (zv) {
- ZVAL_COPY_VALUE(&zn.u.constant, zv);
+ if (return_one) {
+ ZVAL_LONG(&zn.u.constant, 1);
} else {
ZVAL_NULL(&zn.u.constant);
}
@@ -2172,6 +2462,61 @@ static zend_op *zend_compile_class_ref(znode *result, zend_ast *name_ast, int th
}
/* }}} */
+static void zend_compile_class_ref_ex(znode *result, zend_ast *name_ast, uint32_t fetch_flags) /* {{{ */
+{
+ uint32_t fetch_type;
+
+ if (name_ast->kind != ZEND_AST_ZVAL) {
+ znode name_node;
+
+ zend_compile_expr(&name_node, name_ast);
+
+ if (name_node.op_type == IS_CONST) {
+ zend_string *name;
+
+ if (Z_TYPE(name_node.u.constant) != IS_STRING) {
+ zend_error_noreturn(E_COMPILE_ERROR, "Illegal class name");
+ }
+
+ name = Z_STR(name_node.u.constant);
+ fetch_type = zend_get_class_fetch_type(name);
+
+ if (fetch_type == ZEND_FETCH_CLASS_DEFAULT) {
+ result->op_type = IS_CONST;
+ ZVAL_STR(&result->u.constant, zend_resolve_class_name(name, ZEND_NAME_FQ));
+ } else {
+ zend_ensure_valid_class_fetch_type(fetch_type);
+ result->op_type = IS_UNUSED;
+ result->u.op.num = fetch_type | fetch_flags;
+ }
+
+ zend_string_release(name);
+ } else {
+ zend_op *opline = zend_emit_op(result, ZEND_FETCH_CLASS, NULL, &name_node);
+ opline->extended_value = ZEND_FETCH_CLASS_DEFAULT | fetch_flags;
+ }
+ return;
+ }
+
+ /* Fully qualified names are always default refs */
+ if (name_ast->attr == ZEND_NAME_FQ) {
+ result->op_type = IS_CONST;
+ ZVAL_STR(&result->u.constant, zend_resolve_class_name_ast(name_ast));
+ return;
+ }
+
+ fetch_type = zend_get_class_fetch_type(zend_ast_get_str(name_ast));
+ if (ZEND_FETCH_CLASS_DEFAULT == fetch_type) {
+ result->op_type = IS_CONST;
+ ZVAL_STR(&result->u.constant, zend_resolve_class_name_ast(name_ast));
+ } else {
+ zend_ensure_valid_class_fetch_type(fetch_type);
+ result->op_type = IS_UNUSED;
+ result->u.op.num = fetch_type | fetch_flags;
+ }
+}
+/* }}} */
+
static int zend_try_compile_cv(znode *result, zend_ast *ast) /* {{{ */
{
zend_ast *name_ast = ast->child[0];
@@ -2189,9 +2534,6 @@ static int zend_try_compile_cv(znode *result, zend_ast *ast) /* {{{ */
/* lookup_cv may be using another zend_string instance */
name = CG(active_op_array)->vars[EX_VAR_TO_NUM(result->u.op.var)];
- if (zend_string_equals_literal(name, "this")) {
- CG(active_op_array)->this_var = result->u.op.var;
- }
return SUCCESS;
}
@@ -2222,22 +2564,31 @@ static zend_op *zend_compile_simple_var_no_cv(znode *result, zend_ast *ast, uint
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);
- }
}
return opline;
}
/* }}} */
+static zend_bool is_this_fetch(zend_ast *ast) /* {{{ */
+{
+ if (ast->kind == ZEND_AST_VAR && ast->child[0]->kind == ZEND_AST_ZVAL) {
+ zval *name = zend_ast_get_zval(ast->child[0]);
+ return Z_TYPE_P(name) == IS_STRING && zend_string_equals_literal(Z_STR_P(name), "this");
+ }
+
+ return 0;
+}
+/* }}} */
+
static void zend_compile_simple_var(znode *result, zend_ast *ast, uint32_t type, int delayed) /* {{{ */
{
- if (zend_try_compile_cv(result, ast) == FAILURE) {
+ zend_op *opline;
+
+ if (is_this_fetch(ast)) {
+ opline = zend_emit_op(result, ZEND_FETCH_THIS, NULL, NULL);
+ zend_adjust_for_fetch_type(opline, type);
+ } else if (zend_try_compile_cv(result, ast) == FAILURE) {
zend_op *opline = zend_compile_simple_var_no_cv(result, ast, type, delayed);
zend_adjust_for_fetch_type(opline, type);
}
@@ -2260,13 +2611,13 @@ static void zend_separate_if_call_and_write(znode *node, zend_ast *ast, uint32_t
void zend_delayed_compile_var(znode *result, zend_ast *ast, uint32_t type);
void zend_compile_assign(znode *result, zend_ast *ast);
-static void zend_compile_list_assign(znode *result, zend_ast *ast, znode *expr_node);
+static void zend_compile_list_assign(znode *result, zend_ast *ast, znode *expr_node, zend_bool old_style);
static inline void zend_emit_assign_znode(zend_ast *var_ast, znode *value_node) /* {{{ */
{
znode dummy_node;
- if (var_ast->kind == ZEND_AST_LIST) {
- zend_compile_list_assign(&dummy_node, var_ast, value_node);
+ if (var_ast->kind == ZEND_AST_ARRAY) {
+ zend_compile_list_assign(&dummy_node, var_ast, value_node, var_ast->attr);
} else {
zend_ast *assign_ast = zend_ast_create(ZEND_AST_ASSIGN, var_ast,
zend_ast_create_znode(value_node));
@@ -2318,17 +2669,6 @@ void zend_compile_dim(znode *result, zend_ast *ast, uint32_t type) /* {{{ */
}
/* }}} */
-static zend_bool is_this_fetch(zend_ast *ast) /* {{{ */
-{
- if (ast->kind == ZEND_AST_VAR && ast->child[0]->kind == ZEND_AST_ZVAL) {
- zval *name = zend_ast_get_zval(ast->child[0]);
- return Z_TYPE_P(name) == IS_STRING && zend_string_equals_literal(Z_STR_P(name), "this");
- }
-
- return 0;
-}
-/* }}} */
-
static zend_op *zend_delayed_compile_prop(znode *result, zend_ast *ast, uint32_t type) /* {{{ */
{
zend_ast *obj_ast = ast->child[0];
@@ -2378,19 +2718,14 @@ zend_op *zend_compile_static_prop_common(znode *result, zend_ast *ast, uint32_t
znode class_node, prop_node;
zend_op *opline;
- if (zend_is_const_default_class_ref(class_ast)) {
- class_node.op_type = IS_CONST;
- ZVAL_STR(&class_node.u.constant, zend_resolve_class_name_ast(class_ast));
- } else {
- zend_compile_class_ref(&class_node, class_ast, 1);
- }
+ zend_compile_class_ref_ex(&class_node, class_ast, ZEND_FETCH_CLASS_EXCEPTION);
zend_compile_expr(&prop_node, prop_ast);
if (delayed) {
- opline = zend_delayed_emit_op(result, ZEND_FETCH_R, &prop_node, NULL);
+ opline = zend_delayed_emit_op(result, ZEND_FETCH_STATIC_PROP_R, &prop_node, NULL);
} else {
- opline = zend_emit_op(result, ZEND_FETCH_R, &prop_node, NULL);
+ opline = zend_emit_op(result, ZEND_FETCH_STATIC_PROP_R, &prop_node, NULL);
}
if (opline->op1_type == IS_CONST) {
convert_to_string(CT_CONSTANT(opline->op1));
@@ -2403,7 +2738,6 @@ zend_op *zend_compile_static_prop_common(znode *result, zend_ast *ast, uint32_t
} else {
SET_NODE(opline->op2, &class_node);
}
- opline->extended_value |= ZEND_FETCH_STATIC_MEMBER;
return opline;
}
@@ -2416,19 +2750,35 @@ void zend_compile_static_prop(znode *result, zend_ast *ast, uint32_t type, int d
}
/* }}} */
-static void zend_compile_list_assign(znode *result, zend_ast *ast, znode *expr_node) /* {{{ */
+static void zend_verify_list_assign_target(zend_ast *var_ast, zend_bool old_style) /* {{{ */ {
+ if (var_ast->kind == ZEND_AST_ARRAY) {
+ if (old_style != var_ast->attr) {
+ zend_error(E_COMPILE_ERROR, "Cannot mix [] and list()");
+ }
+ } else if (!zend_can_write_to_variable(var_ast)) {
+ zend_error(E_COMPILE_ERROR, "Assignments can only happen to writable values");
+ }
+}
+/* }}} */
+
+static void zend_compile_unkeyed_list_assign(zend_ast_list *list, znode *expr_node, zend_bool old_style) /* {{{ */
{
- zend_ast_list *list = zend_ast_get_list(ast);
uint32_t i;
zend_bool has_elems = 0;
for (i = 0; i < list->children; ++i) {
- zend_ast *var_ast = list->child[i];
+ zend_ast *elem_ast = list->child[i];
+ zend_ast *var_ast;
znode fetch_result, dim_node;
- if (var_ast == NULL) {
+ if (elem_ast == NULL) {
continue;
}
+ if (elem_ast->attr) {
+ zend_error(E_COMPILE_ERROR, "[] and list() assignments cannot be by reference");
+ }
+
+ var_ast = elem_ast->child[0];
has_elems = 1;
dim_node.op_type = IS_CONST;
@@ -2438,14 +2788,69 @@ static void zend_compile_list_assign(znode *result, zend_ast *ast, znode *expr_n
Z_TRY_ADDREF(expr_node->u.constant);
}
+ if (elem_ast->child[1] != NULL) {
+ zend_error(E_COMPILE_ERROR, "Cannot mix keyed and unkeyed array entries in assignments");
+ }
+
+ zend_verify_list_assign_target(var_ast, old_style);
+
zend_emit_op(&fetch_result, ZEND_FETCH_LIST, expr_node, &dim_node);
zend_emit_assign_znode(var_ast, &fetch_result);
}
- if (!has_elems) {
+ if (has_elems == 0) {
zend_error_noreturn(E_COMPILE_ERROR, "Cannot use empty list");
}
+}
+/* }}} */
+
+static void zend_compile_keyed_list_assign(zend_ast_list *list, znode *expr_node, zend_bool old_style) /* {{{ */
+{
+ uint32_t i;
+
+ for (i = 0; i < list->children; ++i) {
+ zend_ast *pair_ast = list->child[i];
+ zend_ast *var_ast = pair_ast->child[0];
+ zend_ast *key_ast = pair_ast->child[1];
+ znode fetch_result, dim_node;
+
+ if (pair_ast->attr) {
+ zend_error(E_COMPILE_ERROR, "[] and list() assignments cannot be by reference");
+ }
+
+ if (key_ast == NULL) {
+ zend_error(E_COMPILE_ERROR, "Cannot mix keyed and unkeyed array entries in assignments");
+ }
+
+ zend_compile_expr(&dim_node, key_ast);
+
+ if (expr_node->op_type == IS_CONST) {
+ Z_TRY_ADDREF(expr_node->u.constant);
+ }
+
+ if (var_ast == NULL) {
+ zend_error(E_COMPILE_ERROR, "Cannot use empty array entries in keyed array");
+ }
+
+ zend_verify_list_assign_target(var_ast, old_style);
+
+ zend_emit_op(&fetch_result, ZEND_FETCH_LIST, expr_node, &dim_node);
+ zend_emit_assign_znode(var_ast, &fetch_result);
+ }
+}
+/* }}} */
+
+static void zend_compile_list_assign(znode *result, zend_ast *ast, znode *expr_node, zend_bool old_style) /* {{{ */
+{
+ zend_ast_list *list = zend_ast_get_list(ast);
+
+ if (list->children > 0 && list->child[0] != NULL && list->child[0]->child[1] != NULL /* has key */) {
+ zend_compile_keyed_list_assign(list, expr_node, old_style);
+ } else {
+ zend_compile_unkeyed_list_assign(list, expr_node, old_style);
+ }
+
*result = *expr_node;
}
/* }}} */
@@ -2493,13 +2898,16 @@ zend_bool zend_list_has_assign_to(zend_ast *list_ast, zend_string *name) /* {{{
zend_ast_list *list = zend_ast_get_list(list_ast);
uint32_t i;
for (i = 0; i < list->children; i++) {
- zend_ast *var_ast = list->child[i];
- if (!var_ast) {
+ zend_ast *elem_ast = list->child[i];
+ zend_ast *var_ast;
+
+ if (!elem_ast) {
continue;
}
+ var_ast = elem_ast->child[0];
/* Recursively check nested list()s */
- if (var_ast->kind == ZEND_AST_LIST && zend_list_has_assign_to(var_ast, name)) {
+ if (var_ast->kind == ZEND_AST_ARRAY && zend_list_has_assign_to(var_ast, name)) {
return 1;
}
@@ -2559,7 +2967,8 @@ void zend_compile_assign(znode *result, zend_ast *ast) /* {{{ */
offset = zend_delayed_compile_begin();
zend_delayed_compile_dim(result, var_ast, BP_VAR_W);
- if (zend_is_assign_to_self(var_ast, expr_ast)) {
+ if (zend_is_assign_to_self(var_ast, expr_ast)
+ && !is_this_fetch(expr_ast)) {
/* $a[0] = $a should evaluate the right $a first */
zend_compile_simple_var_no_cv(&expr_node, expr_ast, BP_VAR_R, 0);
} else {
@@ -2581,7 +2990,7 @@ void zend_compile_assign(znode *result, zend_ast *ast) /* {{{ */
zend_emit_op_data(&expr_node);
return;
- case ZEND_AST_LIST:
+ case ZEND_AST_ARRAY:
if (zend_list_has_assign_to_self(var_ast, expr_ast)) {
/* list($a, $b) = $a should evaluate the right $a first */
zend_compile_simple_var_no_cv(&expr_node, expr_ast, BP_VAR_R, 0);
@@ -2589,7 +2998,7 @@ void zend_compile_assign(znode *result, zend_ast *ast) /* {{{ */
zend_compile_expr(&expr_node, expr_ast);
}
- zend_compile_list_assign(result, var_ast, &expr_node);
+ zend_compile_list_assign(result, var_ast, &expr_node, var_ast->attr);
return;
EMPTY_SWITCH_DEFAULT_CASE();
}
@@ -2612,7 +3021,7 @@ void zend_compile_assign_ref(znode *result, zend_ast *ast) /* {{{ */
offset = zend_delayed_compile_begin();
zend_delayed_compile_var(&target_node, target_ast, BP_VAR_W);
- zend_delayed_compile_var(&source_node, source_ast, BP_VAR_REF);
+ zend_delayed_compile_var(&source_node, source_ast, BP_VAR_W);
zend_delayed_compile_end(offset);
if (source_node.op_type != IS_VAR && zend_is_call(source_ast)) {
@@ -2620,9 +3029,6 @@ void zend_compile_assign_ref(znode *result, zend_ast *ast) /* {{{ */
}
opline = zend_emit_op(result, ZEND_ASSIGN_REF, &target_node, &source_node);
- if (!result) {
- opline->result_type |= EXT_TYPE_UNUSED;
- }
if (zend_is_call(source_ast)) {
opline->extended_value = ZEND_RETURNS_FUNCTION;
@@ -2688,7 +3094,6 @@ void zend_compile_compound_assign(znode *result, zend_ast *ast) /* {{{ */
uint32_t zend_compile_args(zend_ast *ast, zend_function *fbc) /* {{{ */
{
- /* TODO.AST &var error */
zend_ast_list *args = zend_ast_get_list(ast);
uint32_t i;
zend_bool uses_arg_unpack = 0;
@@ -2701,7 +3106,6 @@ uint32_t zend_compile_args(zend_ast *ast, zend_function *fbc) /* {{{ */
znode arg_node;
zend_op *opline;
zend_uchar opcode;
- zend_ulong flags = 0;
if (arg->kind == ZEND_AST_UNPACK) {
uses_arg_unpack = 1;
@@ -2727,13 +3131,16 @@ uint32_t zend_compile_args(zend_ast *ast, zend_function *fbc) /* {{{ */
/* Function call was converted into builtin instruction */
opcode = ZEND_SEND_VAL;
} else {
- opcode = ZEND_SEND_VAR_NO_REF;
- flags |= ZEND_ARG_SEND_FUNCTION;
- if (fbc && ARG_SHOULD_BE_SENT_BY_REF(fbc, arg_num)) {
- flags |= ZEND_ARG_SEND_BY_REF;
- if (ARG_MAY_BE_SENT_BY_REF(fbc, arg_num)) {
- flags |= ZEND_ARG_SEND_SILENT;
+ if (fbc) {
+ if (ARG_MUST_BE_SENT_BY_REF(fbc, arg_num)) {
+ opcode = ZEND_SEND_VAR_NO_REF;
+ } else if (ARG_MAY_BE_SENT_BY_REF(fbc, arg_num)) {
+ opcode = ZEND_SEND_VAL;
+ } else {
+ opcode = ZEND_SEND_VAR;
}
+ } else {
+ opcode = ZEND_SEND_VAR_NO_REF_EX;
}
}
} else if (fbc) {
@@ -2753,9 +3160,17 @@ uint32_t zend_compile_args(zend_ast *ast, zend_function *fbc) /* {{{ */
zend_compile_expr(&arg_node, arg);
ZEND_ASSERT(arg_node.op_type != IS_CV);
if (arg_node.op_type == IS_VAR) {
- opcode = ZEND_SEND_VAR_NO_REF;
- if (fbc && ARG_MUST_BE_SENT_BY_REF(fbc, arg_num)) {
- flags |= ZEND_ARG_SEND_BY_REF;
+ /* pass ++$a or something similar */
+ if (fbc) {
+ if (ARG_MUST_BE_SENT_BY_REF(fbc, arg_num)) {
+ opcode = ZEND_SEND_VAR_NO_REF;
+ } else if (ARG_MAY_BE_SENT_BY_REF(fbc, arg_num)) {
+ opcode = ZEND_SEND_VAL;
+ } else {
+ opcode = ZEND_SEND_VAR;
+ }
+ } else {
+ opcode = ZEND_SEND_VAR_NO_REF_EX;
}
} else {
if (fbc) {
@@ -2769,51 +3184,35 @@ uint32_t zend_compile_args(zend_ast *ast, zend_function *fbc) /* {{{ */
}
}
- opline = get_next_op(CG(active_op_array));
- opline->opcode = opcode;
- SET_NODE(opline->op1, &arg_node);
- SET_UNUSED(opline->op2);
+ opline = zend_emit_op(NULL, opcode, &arg_node, NULL);
opline->op2.opline_num = arg_num;
opline->result.var = (uint32_t)(zend_intptr_t)ZEND_CALL_ARG(NULL, arg_num);
-
- if (opcode == ZEND_SEND_VAR_NO_REF) {
- if (fbc) {
- flags |= ZEND_ARG_COMPILE_TIME_BOUND;
- }
- if ((flags & ZEND_ARG_COMPILE_TIME_BOUND) && !(flags & ZEND_ARG_SEND_BY_REF)) {
- opline->opcode = ZEND_SEND_VAR;
- opline->extended_value = ZEND_ARG_COMPILE_TIME_BOUND;
- } else {
- opline->extended_value = flags;
- }
- } else if (fbc) {
- opline->extended_value = ZEND_ARG_COMPILE_TIME_BOUND;
- }
}
return arg_count;
}
/* }}} */
-ZEND_API zend_uchar zend_get_call_op(zend_uchar init_op, zend_function *fbc) /* {{{ */
+ZEND_API zend_uchar zend_get_call_op(const zend_op *init_op, zend_function *fbc) /* {{{ */
{
- if (fbc) {
+ if (fbc && init_op->opcode == ZEND_INIT_FCALL) {
if (fbc->type == ZEND_INTERNAL_FUNCTION) {
- if (!zend_execute_internal &&
- !fbc->common.scope &&
- !(fbc->common.fn_flags & (ZEND_ACC_ABSTRACT|ZEND_ACC_DEPRECATED|ZEND_ACC_HAS_TYPE_HINTS|ZEND_ACC_RETURN_REFERENCE))) {
- return ZEND_DO_ICALL;
+ if (!zend_execute_internal) {
+ if (!(fbc->common.fn_flags & (ZEND_ACC_ABSTRACT|ZEND_ACC_DEPRECATED|ZEND_ACC_HAS_TYPE_HINTS|ZEND_ACC_RETURN_REFERENCE))) {
+ return ZEND_DO_ICALL;
+ } else {
+ return ZEND_DO_FCALL_BY_NAME;
+ }
}
} else {
- if (zend_execute_ex == execute_ex &&
- !(fbc->common.fn_flags & ZEND_ACC_GENERATOR)) {
+ if (zend_execute_ex == execute_ex) {
return ZEND_DO_UCALL;
}
}
} else if (zend_execute_ex == execute_ex &&
!zend_execute_internal &&
- (init_op == ZEND_INIT_FCALL_BY_NAME ||
- init_op == ZEND_INIT_NS_FCALL_BY_NAME)) {
+ (init_op->opcode == ZEND_INIT_FCALL_BY_NAME ||
+ init_op->opcode == ZEND_INIT_NS_FCALL_BY_NAME)) {
return ZEND_DO_FCALL_BY_NAME;
}
return ZEND_DO_FCALL;
@@ -2839,7 +3238,7 @@ void zend_compile_call_common(znode *result, zend_ast *args_ast, zend_function *
}
call_flags = (opline->opcode == ZEND_NEW ? ZEND_CALL_CTOR : 0);
- opline = zend_emit_op(result, zend_get_call_op(opline->opcode, fbc), NULL, NULL);
+ opline = zend_emit_op(result, zend_get_call_op(opline, fbc), NULL, NULL);
opline->op1.num = call_flags;
zend_do_extended_fcall_end();
@@ -2875,13 +3274,14 @@ void zend_compile_ns_call(znode *result, znode *name_node, zend_ast *args_ast) /
void zend_compile_dynamic_call(znode *result, znode *name_node, zend_ast *args_ast) /* {{{ */
{
- zend_op *opline = get_next_op(CG(active_op_array));
if (name_node->op_type == IS_CONST && Z_TYPE(name_node->u.constant) == IS_STRING) {
const char *colon;
zend_string *str = Z_STR(name_node->u.constant);
if ((colon = zend_memrchr(ZSTR_VAL(str), ':', ZSTR_LEN(str))) != NULL && colon > ZSTR_VAL(str) && *(colon - 1) == ':') {
zend_string *class = zend_string_init(ZSTR_VAL(str), colon - ZSTR_VAL(str) - 1, 0);
zend_string *method = zend_string_init(colon + 1, ZSTR_LEN(str) - (colon - ZSTR_VAL(str)) - 1, 0);
+ zend_op *opline = get_next_op(CG(active_op_array));
+
opline->opcode = ZEND_INIT_STATIC_METHOD_CALL;
opline->op1_type = IS_CONST;
opline->op1.constant = zend_add_class_name_literal(CG(active_op_array), class);
@@ -2890,6 +3290,8 @@ void zend_compile_dynamic_call(znode *result, znode *name_node, zend_ast *args_a
zend_alloc_cache_slot(opline->op2.constant);
zval_ptr_dtor(&name_node->u.constant);
} else {
+ zend_op *opline = get_next_op(CG(active_op_array));
+
opline->opcode = ZEND_INIT_FCALL_BY_NAME;
SET_UNUSED(opline->op1);
opline->op2_type = IS_CONST;
@@ -2897,9 +3299,7 @@ void zend_compile_dynamic_call(znode *result, znode *name_node, zend_ast *args_a
zend_alloc_cache_slot(opline->op2.constant);
}
} else {
- opline->opcode = ZEND_INIT_DYNAMIC_CALL;
- SET_UNUSED(opline->op1);
- SET_NODE(opline->op2, name_node);
+ zend_emit_op(NULL, ZEND_INIT_DYNAMIC_CALL, NULL, name_node);
}
zend_compile_call_common(result, args_ast, NULL);
@@ -2971,6 +3371,14 @@ int zend_compile_func_defined(znode *result, zend_ast_list *args) /* {{{ */
return FAILURE;
}
+ if (zend_try_ct_eval_const(&result->u.constant, name, 0)) {
+ zend_string_release(name);
+ zval_ptr_dtor(&result->u.constant);
+ ZVAL_TRUE(&result->u.constant);
+ result->op_type = IS_CONST;
+ return SUCCESS;
+ }
+
opline = zend_emit_op_tmp(result, ZEND_DEFINED, NULL, NULL);
opline->op1_type = IS_CONST;
LITERAL_STR(opline->op1, name);
@@ -2987,6 +3395,46 @@ int zend_compile_func_defined(znode *result, zend_ast_list *args) /* {{{ */
}
/* }}} */
+int zend_compile_func_chr(znode *result, zend_ast_list *args) /* {{{ */
+{
+
+ if (args->children == 1 &&
+ args->child[0]->kind == ZEND_AST_ZVAL &&
+ Z_TYPE_P(zend_ast_get_zval(args->child[0])) == IS_LONG) {
+
+ zend_long c = Z_LVAL_P(zend_ast_get_zval(args->child[0])) & 0xff;
+
+ result->op_type = IS_CONST;
+ if (CG(one_char_string)[c]) {
+ ZVAL_INTERNED_STR(&result->u.constant, CG(one_char_string)[c]);
+ } else {
+ ZVAL_NEW_STR(&result->u.constant, zend_string_alloc(1, 0));
+ Z_STRVAL_P(&result->u.constant)[0] = (char)c;
+ Z_STRVAL_P(&result->u.constant)[1] = '\0';
+ }
+ return SUCCESS;
+ } else {
+ return FAILURE;
+ }
+}
+/* }}} */
+
+int zend_compile_func_ord(znode *result, zend_ast_list *args) /* {{{ */
+{
+ if (args->children == 1 &&
+ args->child[0]->kind == ZEND_AST_ZVAL &&
+ Z_TYPE_P(zend_ast_get_zval(args->child[0])) == IS_STRING) {
+
+ result->op_type = IS_CONST;
+ ZVAL_LONG(&result->u.constant, (unsigned char)Z_STRVAL_P(zend_ast_get_zval(args->child[0]))[0]);
+ return SUCCESS;
+ } else {
+ return FAILURE;
+ }
+}
+/* }}} */
+
+
static int zend_try_compile_ct_bound_init_user_func(zend_ast *name_ast, uint32_t num_args) /* {{{ */
{
zend_string *name, *lcname;
@@ -3097,24 +3545,6 @@ int zend_compile_func_cuf(znode *result, zend_ast_list *args, zend_string *lcnam
}
/* }}} */
-static void zend_compile_assert_side_effects(zend_ast *ast) /* {{{ */
-{
- int i;
- int children = zend_ast_is_list(ast) ? zend_ast_get_list(ast)->children : zend_ast_get_num_children(ast);
-
- for (i = 0; i < children; i++) {
- zend_ast *child = (zend_ast_is_list(ast) ? zend_ast_get_list(ast)->child : ast->child)[i];
- if (child) {
- if (child->kind == ZEND_AST_YIELD) {
- zend_mark_function_as_generator();
- } else if (ast->kind >= ZEND_AST_IS_LIST_SHIFT) {
- zend_compile_assert_side_effects(child);
- }
- }
- }
-}
-/* }}} */
-
static int zend_compile_assert(znode *result, zend_ast_list *args, zend_string *name, zend_function *fbc) /* {{{ */
{
if (EG(assertions) >= 0) {
@@ -3148,22 +3578,22 @@ static int zend_compile_assert(znode *result, zend_ast_list *args, zend_string *
zend_compile_call_common(result, (zend_ast*)args, fbc);
- CG(active_op_array)->opcodes[check_op_number].op2.opline_num = get_next_op_number(CG(active_op_array));
+ opline = &CG(active_op_array)->opcodes[check_op_number];
+ opline->op2.opline_num = get_next_op_number(CG(active_op_array));
+ SET_NODE(opline->result, result);
} else {
if (!fbc) {
zend_string_release(name);
}
result->op_type = IS_CONST;
ZVAL_TRUE(&result->u.constant);
-
- zend_compile_assert_side_effects((zend_ast *) args);
}
return SUCCESS;
}
/* }}} */
-int zend_try_compile_special_func(znode *result, zend_string *lcname, zend_ast_list *args, zend_function *fbc) /* {{{ */
+int zend_try_compile_special_func(znode *result, zend_string *lcname, zend_ast_list *args, zend_function *fbc, uint32_t type) /* {{{ */
{
if (fbc->internal_function.handler == ZEND_FN(display_disabled_function)) {
return FAILURE;
@@ -3203,6 +3633,10 @@ int zend_try_compile_special_func(znode *result, zend_string *lcname, zend_ast_l
return zend_compile_func_typecheck(result, args, IS_RESOURCE);
} else if (zend_string_equals_literal(lcname, "defined")) {
return zend_compile_func_defined(result, args);
+ } else if (zend_string_equals_literal(lcname, "chr") && type == BP_VAR_R) {
+ return zend_compile_func_chr(result, args);
+ } else if (zend_string_equals_literal(lcname, "ord") && type == BP_VAR_R) {
+ return zend_compile_func_ord(result, args);
} else if (zend_string_equals_literal(lcname, "call_user_func_array")) {
return zend_compile_func_cufa(result, args, lcname);
} else if (zend_string_equals_literal(lcname, "call_user_func")) {
@@ -3257,7 +3691,7 @@ void zend_compile_call(znode *result, zend_ast *ast, uint32_t type) /* {{{ */
}
if (zend_try_compile_special_func(result, lcname,
- zend_ast_get_list(args_ast), fbc) == SUCCESS
+ zend_ast_get_list(args_ast), fbc, type) == SUCCESS
) {
zend_string_release(lcname);
zval_ptr_dtor(&name_node.u.constant);
@@ -3283,6 +3717,7 @@ void zend_compile_method_call(znode *result, zend_ast *ast, uint32_t type) /* {{
znode obj_node, method_node;
zend_op *opline;
+ zend_function *fbc = NULL;
if (is_this_fetch(obj_ast)) {
obj_node.op_type = IS_UNUSED;
@@ -3306,7 +3741,20 @@ void zend_compile_method_call(znode *result, zend_ast *ast, uint32_t type) /* {{
SET_NODE(opline->op2, &method_node);
}
- zend_compile_call_common(result, args_ast, NULL);
+ /* Check if this calls a known method on $this */
+ if (opline->op1_type == IS_UNUSED && opline->op2_type == IS_CONST &&
+ CG(active_class_entry) && zend_is_scope_known()) {
+ zend_string *lcname = Z_STR_P(CT_CONSTANT(opline->op2) + 1);
+ fbc = zend_hash_find_ptr(&CG(active_class_entry)->function_table, lcname);
+
+ /* We only know the exact method that is being called if it is either private or final.
+ * Otherwise an overriding method in a child class may be called. */
+ if (fbc && !(fbc->common.fn_flags & (ZEND_ACC_PRIVATE|ZEND_ACC_FINAL))) {
+ fbc = NULL;
+ }
+ }
+
+ zend_compile_call_common(result, args_ast, fbc);
}
/* }}} */
@@ -3324,15 +3772,9 @@ void zend_compile_static_call(znode *result, zend_ast *ast, uint32_t type) /* {{
znode class_node, method_node;
zend_op *opline;
- zend_ulong extended_value = 0;
+ zend_function *fbc = NULL;
- if (zend_is_const_default_class_ref(class_ast)) {
- class_node.op_type = IS_CONST;
- ZVAL_STR(&class_node.u.constant, zend_resolve_class_name_ast(class_ast));
- } else {
- opline = zend_compile_class_ref(&class_node, class_ast, 1);
- extended_value = opline->extended_value;
- }
+ zend_compile_class_ref_ex(&class_node, class_ast, ZEND_FETCH_CLASS_EXCEPTION);
zend_compile_expr(&method_node, method_ast);
if (method_node.op_type == IS_CONST) {
@@ -3348,7 +3790,6 @@ void zend_compile_static_call(znode *result, zend_ast *ast, uint32_t type) /* {{
opline = get_next_op(CG(active_op_array));
opline->opcode = ZEND_INIT_STATIC_METHOD_CALL;
- opline->extended_value = extended_value;
zend_set_class_name_op1(opline, &class_node);
@@ -3364,8 +3805,30 @@ void zend_compile_static_call(znode *result, zend_ast *ast, uint32_t type) /* {{
} else {
SET_NODE(opline->op2, &method_node);
}
+ zend_check_live_ranges(opline);
- zend_compile_call_common(result, args_ast, NULL);
+ /* Check if we already know which method we're calling */
+ if (opline->op2_type == IS_CONST) {
+ zend_class_entry *ce = NULL;
+ if (opline->op1_type == IS_CONST) {
+ zend_string *lcname = Z_STR_P(CT_CONSTANT(opline->op1) + 1);
+ ce = zend_hash_find_ptr(CG(class_table), lcname);
+ if (!ce && CG(active_class_entry)
+ && zend_string_equals_ci(CG(active_class_entry)->name, lcname)) {
+ ce = CG(active_class_entry);
+ }
+ } else if (opline->op1_type == IS_UNUSED
+ && (opline->op1.num & ZEND_FETCH_CLASS_MASK) == ZEND_FETCH_CLASS_SELF
+ && zend_is_scope_known()) {
+ ce = CG(active_class_entry);
+ }
+ if (ce) {
+ zend_string *lcname = Z_STR_P(CT_CONSTANT(opline->op2) + 1);
+ fbc = zend_hash_find_ptr(&ce->function_table, lcname);
+ }
+ }
+
+ zend_compile_call_common(result, args_ast, fbc);
}
/* }}} */
@@ -3380,10 +3843,7 @@ void zend_compile_new(znode *result, zend_ast *ast) /* {{{ */
zend_op *opline;
uint32_t opnum;
- if (zend_is_const_default_class_ref(class_ast)) {
- class_node.op_type = IS_CONST;
- ZVAL_STR(&class_node.u.constant, zend_resolve_class_name_ast(class_ast));
- } else if (class_ast->kind == ZEND_AST_CLASS) {
+ if (class_ast->kind == ZEND_AST_CLASS) {
uint32_t dcl_opnum = get_next_op_number(CG(active_op_array));
zend_compile_class_decl(class_ast);
/* jump over anon class declaration */
@@ -3393,9 +3853,9 @@ void zend_compile_new(znode *result, zend_ast *ast) /* {{{ */
}
class_node.op_type = opline->result_type;
class_node.u.op.var = opline->result.var;
- opline->op1.opline_num = get_next_op_number(CG(active_op_array));
+ opline->extended_value = get_next_op_number(CG(active_op_array));
} else {
- zend_compile_class_ref(&class_node, class_ast, 1);
+ zend_compile_class_ref_ex(&class_node, class_ast, ZEND_FETCH_CLASS_EXCEPTION);
}
opnum = get_next_op_number(CG(active_op_array));
@@ -3412,7 +3872,8 @@ void zend_compile_new(znode *result, zend_ast *ast) /* {{{ */
zend_compile_call_common(&ctor_result, args_ast, NULL);
zend_do_free(&ctor_result);
- /* New jumps over ctor call if ctor does not exist */
+ /* We save the position of DO_FCALL for convenience in find_live_range().
+ * This info is not preserved for runtime. */
opline = &CG(active_op_array)->opcodes[opnum];
opline->op2.opline_num = get_next_op_number(CG(active_op_array));
}
@@ -3425,7 +3886,7 @@ void zend_compile_clone(znode *result, zend_ast *ast) /* {{{ */
znode obj_node;
zend_compile_expr(&obj_node, obj_ast);
- zend_emit_op(result, ZEND_CLONE, &obj_node, NULL);
+ zend_emit_op_tmp(result, ZEND_CLONE, &obj_node, NULL);
}
/* }}} */
@@ -3441,23 +3902,33 @@ void zend_compile_global_var(zend_ast *ast) /* {{{ */
convert_to_string(&name_node.u.constant);
}
- if (zend_try_compile_cv(&result, var_ast) == SUCCESS) {
+ if (is_this_fetch(var_ast)) {
+ zend_error_noreturn(E_COMPILE_ERROR, "Cannot use $this as global variable");
+ } else if (zend_try_compile_cv(&result, var_ast) == SUCCESS) {
zend_op *opline = zend_emit_op(NULL, ZEND_BIND_GLOBAL, &result, &name_node);
zend_alloc_cache_slot(opline->op2.constant);
} else {
- zend_emit_op(&result, ZEND_FETCH_W, &name_node, NULL);
+ /* name_ast should be evaluated only. FETCH_GLOBAL_LOCK instructs FETCH_W
+ * to not free the name_node operand, so it can be reused in the following
+ * ASSIGN_REF, which then frees it. */
+ zend_op *opline = zend_emit_op(&result, ZEND_FETCH_W, &name_node, NULL);
+ opline->extended_value = ZEND_FETCH_GLOBAL_LOCK;
- // TODO.AST Avoid double fetch
- //opline->extended_value = ZEND_FETCH_GLOBAL_LOCK;
+ if (name_node.op_type == IS_CONST) {
+ zend_string_addref(Z_STR(name_node.u.constant));
+ }
- zend_emit_assign_ref_znode(var_ast, &result);
+ zend_emit_assign_ref_znode(
+ zend_ast_create(ZEND_AST_VAR, zend_ast_create_znode(&name_node)),
+ &result
+ );
}
}
/* }}} */
static void zend_compile_static_var_common(zend_ast *var_ast, zval *value, zend_bool by_ref) /* {{{ */
{
- znode var_node, result;
+ znode var_node;
zend_op *opline;
zend_compile_expr(&var_node, var_ast);
@@ -3478,16 +3949,14 @@ static void zend_compile_static_var_common(zend_ast *var_ast, zval *value, zend_
}
zend_hash_update(CG(active_op_array)->static_variables, Z_STR(var_node.u.constant), value);
- opline = zend_emit_op(&result, by_ref ? ZEND_FETCH_W : ZEND_FETCH_R, &var_node, NULL);
- opline->extended_value = ZEND_FETCH_STATIC;
-
- if (by_ref) {
- zend_ast *fetch_ast = zend_ast_create(ZEND_AST_VAR, var_ast);
- zend_emit_assign_ref_znode(fetch_ast, &result);
- } else {
- zend_ast *fetch_ast = zend_ast_create(ZEND_AST_VAR, var_ast);
- zend_emit_assign_znode(fetch_ast, &result);
+ if (zend_string_equals_literal(Z_STR(var_node.u.constant), "this")) {
+ zend_error_noreturn(E_COMPILE_ERROR, "Cannot use $this as static variable");
}
+
+ opline = zend_emit_op(NULL, ZEND_BIND_STATIC, NULL, &var_node);
+ opline->op1_type = IS_CV;
+ opline->op1.var = lookup_cv(CG(active_op_array), zend_string_copy(Z_STR(var_node.u.constant)));
+ opline->extended_value = by_ref;
}
/* }}} */
@@ -3517,7 +3986,9 @@ void zend_compile_unset(zend_ast *ast) /* {{{ */
switch (var_ast->kind) {
case ZEND_AST_VAR:
- if (zend_try_compile_cv(&var_node, var_ast) == SUCCESS) {
+ if (is_this_fetch(var_ast)) {
+ zend_error_noreturn(E_COMPILE_ERROR, "Cannot unset $this");
+ } else if (zend_try_compile_cv(&var_node, var_ast) == SUCCESS) {
opline = zend_emit_op(NULL, ZEND_UNSET_VAR, &var_node, NULL);
opline->extended_value = ZEND_FETCH_LOCAL | ZEND_QUICK_SET;
} else {
@@ -3535,14 +4006,14 @@ void zend_compile_unset(zend_ast *ast) /* {{{ */
return;
case ZEND_AST_STATIC_PROP:
opline = zend_compile_static_prop_common(NULL, var_ast, BP_VAR_UNSET, 0);
- opline->opcode = ZEND_UNSET_VAR;
+ opline->opcode = ZEND_UNSET_STATIC_PROP;
return;
EMPTY_SWITCH_DEFAULT_CASE()
}
}
/* }}} */
-static int zend_handle_loops_and_finally_ex(zend_long depth) /* {{{ */
+static int zend_handle_loops_and_finally_ex(zend_long depth, znode *return_value) /* {{{ */
{
zend_loop_var *base;
zend_loop_var *loop_var = zend_stack_top(&CG(loop_var_stack));
@@ -3559,8 +4030,18 @@ static int zend_handle_loops_and_finally_ex(zend_long depth) /* {{{ */
opline->result_type = IS_TMP_VAR;
opline->result.var = loop_var->var_num;
SET_UNUSED(opline->op1);
- SET_UNUSED(opline->op2);
+ if (return_value) {
+ SET_NODE(opline->op2, return_value);
+ } else {
+ SET_UNUSED(opline->op2);
+ }
opline->op1.num = loop_var->u.try_catch_offset;
+ } else if (loop_var->opcode == ZEND_DISCARD_EXCEPTION) {
+ zend_op *opline = get_next_op(CG(active_op_array));
+ opline->opcode = ZEND_DISCARD_EXCEPTION;
+ opline->op1_type = IS_TMP_VAR;
+ opline->op1.var = loop_var->var_num;
+ SET_UNUSED(opline->op2);
} else if (loop_var->opcode == ZEND_RETURN) {
/* Stack separator */
break;
@@ -3572,13 +4053,13 @@ static int zend_handle_loops_and_finally_ex(zend_long depth) /* {{{ */
} else {
zend_op *opline;
- ZEND_ASSERT(loop_var->var_type == IS_VAR || loop_var->var_type == IS_TMP_VAR);
+ ZEND_ASSERT(loop_var->var_type & (IS_VAR|IS_TMP_VAR));
opline = get_next_op(CG(active_op_array));
opline->opcode = loop_var->opcode;
opline->op1_type = loop_var->var_type;
opline->op1.var = loop_var->var_num;
SET_UNUSED(opline->op2);
- opline->op2.num = loop_var->u.brk_cont_offset;
+ opline->op2.num = loop_var->u.live_range_offset;
opline->extended_value = ZEND_FREE_ON_RETURN;
depth--;
}
@@ -3587,9 +4068,9 @@ static int zend_handle_loops_and_finally_ex(zend_long depth) /* {{{ */
}
/* }}} */
-static int zend_handle_loops_and_finally(void) /* {{{ */
+static int zend_handle_loops_and_finally(znode *return_value) /* {{{ */
{
- return zend_handle_loops_and_finally_ex(zend_stack_count(&CG(loop_var_stack)) + 1);
+ return zend_handle_loops_and_finally_ex(zend_stack_count(&CG(loop_var_stack)) + 1, return_value);
}
/* }}} */
@@ -3605,23 +4086,18 @@ void zend_compile_return(zend_ast *ast) /* {{{ */
expr_node.op_type = IS_CONST;
ZVAL_NULL(&expr_node.u.constant);
} else if (by_ref && zend_is_variable(expr_ast) && !zend_is_call(expr_ast)) {
- zend_compile_var(&expr_node, expr_ast, BP_VAR_REF);
+ zend_compile_var(&expr_node, expr_ast, BP_VAR_W);
} else {
zend_compile_expr(&expr_node, expr_ast);
}
- if (CG(context).in_finally) {
- opline = zend_emit_op(NULL, ZEND_DISCARD_EXCEPTION, NULL, NULL);
- opline->op1_type = IS_TMP_VAR;
- opline->op1.var = CG(context).fast_call_var;
- }
-
/* Generator return types are handled separately */
if (!(CG(active_op_array)->fn_flags & ZEND_ACC_GENERATOR) && CG(active_op_array)->fn_flags & ZEND_ACC_HAS_RETURN_TYPE) {
- zend_emit_return_type_check(expr_ast ? &expr_node : NULL, CG(active_op_array)->arg_info - 1);
+ zend_emit_return_type_check(
+ expr_ast ? &expr_node : NULL, CG(active_op_array)->arg_info - 1, 0);
}
- zend_handle_loops_and_finally();
+ zend_handle_loops_and_finally((expr_node.op_type & (IS_TMP_VAR | IS_VAR)) ? &expr_node : NULL);
opline = zend_emit_op(NULL, by_ref ? ZEND_RETURN_BY_REF : ZEND_RETURN,
&expr_node, NULL);
@@ -3691,7 +4167,7 @@ void zend_compile_break_continue(zend_ast *ast) /* {{{ */
zend_error_noreturn(E_COMPILE_ERROR, "'%s' not in the 'loop' or 'switch' context",
ast->kind == ZEND_AST_BREAK ? "break" : "continue");
} else {
- if (!zend_handle_loops_and_finally_ex(depth)) {
+ if (!zend_handle_loops_and_finally_ex(depth, NULL)) {
zend_error_noreturn(E_COMPILE_ERROR, "Cannot '%s' %d level%s",
ast->kind == ZEND_AST_BREAK ? "break" : "continue",
depth, depth == 1 ? "" : "s");
@@ -3724,14 +4200,14 @@ void zend_resolve_goto_label(zend_op_array *op_array, zend_op *opline) /* {{{ */
ZVAL_NULL(label);
current = opline->extended_value;
- for (; current != dest->brk_cont; current = op_array->brk_cont_array[current].parent) {
+ for (; current != dest->brk_cont; current = CG(context).brk_cont_array[current].parent) {
if (current == -1) {
CG(in_compilation) = 1;
CG(active_op_array) = op_array;
CG(zend_lineno) = opline->lineno;
zend_error_noreturn(E_COMPILE_ERROR, "'goto' into loop or switch statement is disallowed");
}
- if (op_array->brk_cont_array[current].start >= 0) {
+ if (CG(context).brk_cont_array[current].start >= 0) {
remove_oplines--;
}
}
@@ -3774,7 +4250,7 @@ void zend_compile_goto(zend_ast *ast) /* {{{ */
zend_compile_expr(&label_node, label_ast);
/* Label resolution and unwinding adjustments happen in pass two. */
- zend_handle_loops_and_finally();
+ zend_handle_loops_and_finally(NULL);
opline = zend_emit_op(NULL, ZEND_GOTO, NULL, &label_node);
opline->op1.num = get_next_op_number(CG(active_op_array)) - opnum_start - 1;
opline->extended_value = CG(context).current_brk_cont;
@@ -3820,7 +4296,7 @@ void zend_compile_while(zend_ast *ast) /* {{{ */
zend_emit_cond_jump(ZEND_JMPNZ, &cond_node, opnum_start);
- zend_end_loop(opnum_cond);
+ zend_end_loop(opnum_cond, NULL);
}
/* }}} */
@@ -3842,7 +4318,7 @@ void zend_compile_do_while(zend_ast *ast) /* {{{ */
zend_emit_cond_jump(ZEND_JMPNZ, &cond_node, opnum_start);
- zend_end_loop(opnum_cond);
+ zend_end_loop(opnum_cond, NULL);
}
/* }}} */
@@ -3898,7 +4374,7 @@ void zend_compile_for(zend_ast *ast) /* {{{ */
zend_emit_cond_jump(ZEND_JMPNZ, &result, opnum_start);
- zend_end_loop(opnum_loop);
+ zend_end_loop(opnum_loop, NULL);
}
/* }}} */
@@ -3920,7 +4396,7 @@ void zend_compile_foreach(zend_ast *ast) /* {{{ */
if (key_ast->kind == ZEND_AST_REF) {
zend_error_noreturn(E_COMPILE_ERROR, "Key element cannot be a reference");
}
- if (key_ast->kind == ZEND_AST_LIST) {
+ if (key_ast->kind == ZEND_AST_ARRAY) {
zend_error_noreturn(E_COMPILE_ERROR, "Cannot use list as key element");
}
}
@@ -3942,10 +4418,14 @@ 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);
+ zend_begin_loop(ZEND_FE_FREE, &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);
- if (value_ast->kind == ZEND_AST_VAR &&
+ if (is_this_fetch(value_ast)) {
+ zend_error_noreturn(E_COMPILE_ERROR, "Cannot re-assign $this");
+ } else if (value_ast->kind == ZEND_AST_VAR &&
zend_try_compile_cv(&value_node, value_ast) == SUCCESS) {
SET_NODE(opline->op2, &value_node);
} else {
@@ -3965,8 +4445,6 @@ void zend_compile_foreach(zend_ast *ast) /* {{{ */
zend_emit_assign_znode(key_ast, &key_node);
}
- zend_begin_loop(ZEND_FE_FREE, &reset_node);
-
zend_compile_stmt(stmt_ast);
zend_emit_jump(opnum_fetch);
@@ -3977,9 +4455,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);
+ zend_end_loop(opnum_fetch, &reset_node);
- zend_emit_op(NULL, ZEND_FE_FREE, &reset_node, NULL);
+ opline = zend_emit_op(NULL, ZEND_FE_FREE, &reset_node, NULL);
}
/* }}} */
@@ -4099,10 +4577,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)));
+ zend_end_loop(get_next_op_number(CG(active_op_array)), &expr_node);
- if (expr_node.op_type == IS_VAR || expr_node.op_type == IS_TMP_VAR) {
- zend_emit_op(NULL, ZEND_FREE, &expr_node, NULL);
+ if (expr_node.op_type & (IS_VAR|IS_TMP_VAR)) {
+ /* don't use emit_op() to prevent automatic live-range construction */
+ opline = get_next_op(CG(active_op_array));
+ opline->opcode = ZEND_FREE;
+ SET_NODE(opline->op1, &expr_node);
+ SET_UNUSED(opline->op2);
} else if (expr_node.op_type == IS_CONST) {
zval_dtor(&expr_node.u.constant);
}
@@ -4117,10 +4599,12 @@ void zend_compile_try(zend_ast *ast) /* {{{ */
zend_ast_list *catches = zend_ast_get_list(ast->child[1]);
zend_ast *finally_ast = ast->child[2];
- uint32_t i;
+ uint32_t i, j;
zend_op *opline;
uint32_t try_catch_offset;
uint32_t *jmp_opnums = safe_emalloc(sizeof(uint32_t), catches->children, 0);
+ uint32_t orig_fast_call_var = CG(context).fast_call_var;
+ uint32_t orig_try_catch_offset = CG(context).try_catch_offset;
if (catches->children == 0 && !finally_ast) {
zend_error_noreturn(E_COMPILE_ERROR, "Cannot use try without catch or finally");
@@ -4143,8 +4627,8 @@ void zend_compile_try(zend_ast *ast) /* {{{ */
zend_loop_var fast_call;
if (!(CG(active_op_array)->fn_flags & ZEND_ACC_HAS_FINALLY_BLOCK)) {
CG(active_op_array)->fn_flags |= ZEND_ACC_HAS_FINALLY_BLOCK;
- CG(context).fast_call_var = get_temporary_variable(CG(active_op_array));
}
+ CG(context).fast_call_var = get_temporary_variable(CG(active_op_array));
/* Push FAST_CALL on unwind stack */
fast_call.opcode = ZEND_FAST_CALL;
@@ -4154,6 +4638,8 @@ void zend_compile_try(zend_ast *ast) /* {{{ */
zend_stack_push(&CG(loop_var_stack), &fast_call);
}
+ CG(context).try_catch_offset = try_catch_offset;
+
zend_compile_stmt(try_ast);
if (catches->children != 0) {
@@ -4162,34 +4648,57 @@ void zend_compile_try(zend_ast *ast) /* {{{ */
for (i = 0; i < catches->children; ++i) {
zend_ast *catch_ast = catches->child[i];
- zend_ast *class_ast = catch_ast->child[0];
+ zend_ast_list *classes = zend_ast_get_list(catch_ast->child[0]);
zend_ast *var_ast = catch_ast->child[1];
zend_ast *stmt_ast = catch_ast->child[2];
zval *var_name = zend_ast_get_zval(var_ast);
zend_bool is_last_catch = (i + 1 == catches->children);
+ uint32_t *jmp_multicatch = safe_emalloc(sizeof(uint32_t), classes->children - 1, 0);
uint32_t opnum_catch;
- if (!zend_is_const_default_class_ref(class_ast)) {
- zend_error_noreturn(E_COMPILE_ERROR, "Bad class name in the catch statement");
- }
+ CG(zend_lineno) = catch_ast->lineno;
- opnum_catch = get_next_op_number(CG(active_op_array));
- if (i == 0) {
- CG(active_op_array)->try_catch_array[try_catch_offset].catch_op = opnum_catch;
- }
+ for (j = 0; j < classes->children; j++) {
- CG(zend_lineno) = catch_ast->lineno;
+ zend_ast *class_ast = classes->child[j];
+ zend_bool is_last_class = (j + 1 == classes->children);
- opline = get_next_op(CG(active_op_array));
- opline->opcode = ZEND_CATCH;
- opline->op1_type = IS_CONST;
- opline->op1.constant = zend_add_class_name_literal(CG(active_op_array),
- zend_resolve_class_name_ast(class_ast));
+ if (!zend_is_const_default_class_ref(class_ast)) {
+ zend_error_noreturn(E_COMPILE_ERROR, "Bad class name in the catch statement");
+ }
- opline->op2_type = IS_CV;
- opline->op2.var = lookup_cv(CG(active_op_array), zend_string_copy(Z_STR_P(var_name)));
- opline->result.num = is_last_catch;
+ opnum_catch = get_next_op_number(CG(active_op_array));
+ if (i == 0 && j == 0) {
+ CG(active_op_array)->try_catch_array[try_catch_offset].catch_op = opnum_catch;
+ }
+
+ opline = get_next_op(CG(active_op_array));
+ opline->opcode = ZEND_CATCH;
+ opline->op1_type = IS_CONST;
+ opline->op1.constant = zend_add_class_name_literal(CG(active_op_array),
+ zend_resolve_class_name_ast(class_ast));
+
+ if (zend_string_equals_literal(Z_STR_P(var_name), "this")) {
+ zend_error_noreturn(E_COMPILE_ERROR, "Cannot re-assign $this");
+ }
+
+ opline->op2_type = IS_CV;
+ opline->op2.var = lookup_cv(CG(active_op_array), zend_string_copy(Z_STR_P(var_name)));
+
+ opline->result.num = is_last_catch && is_last_class;
+
+ if (!is_last_class) {
+ jmp_multicatch[j] = zend_emit_jump(0);
+ opline->extended_value = get_next_op_number(CG(active_op_array));
+ }
+ }
+
+ for (j = 0; j < classes->children - 1; j++) {
+ zend_update_jump_target_to_next(jmp_multicatch[j]);
+ }
+
+ efree(jmp_multicatch);
zend_compile_stmt(stmt_ast);
@@ -4198,7 +4707,9 @@ void zend_compile_try(zend_ast *ast) /* {{{ */
}
opline = &CG(active_op_array)->opcodes[opnum_catch];
- opline->extended_value = get_next_op_number(CG(active_op_array));
+ if (!is_last_catch) {
+ opline->extended_value = get_next_op_number(CG(active_op_array));
+ }
}
for (i = 0; i < catches->children; ++i) {
@@ -4206,11 +4717,18 @@ void zend_compile_try(zend_ast *ast) /* {{{ */
}
if (finally_ast) {
+ zend_loop_var discard_exception;
uint32_t opnum_jmp = get_next_op_number(CG(active_op_array)) + 1;
/* Pop FAST_CALL from unwind stack */
zend_stack_del_top(&CG(loop_var_stack));
+ /* Push DISCARD_EXCEPTION on unwind stack */
+ discard_exception.opcode = ZEND_DISCARD_EXCEPTION;
+ discard_exception.var_type = IS_TMP_VAR;
+ discard_exception.var_num = CG(context).fast_call_var;
+ zend_stack_push(&CG(loop_var_stack), &discard_exception);
+
CG(zend_lineno) = finally_ast->lineno;
opline = zend_emit_op(NULL, ZEND_FAST_CALL, NULL, NULL);
@@ -4231,10 +4749,18 @@ void zend_compile_try(zend_ast *ast) /* {{{ */
opline = zend_emit_op(NULL, ZEND_FAST_RET, NULL, NULL);
opline->op1_type = IS_TMP_VAR;
opline->op1.var = CG(context).fast_call_var;
+ opline->op2.num = orig_try_catch_offset;
zend_update_jump_target_to_next(opnum_jmp);
+
+ CG(context).fast_call_var = orig_fast_call_var;
+
+ /* Pop DISCARD_EXCEPTION from unwind stack */
+ zend_stack_del_top(&CG(loop_var_stack));
}
+ CG(context).try_catch_offset = orig_try_catch_offset;
+
efree(jmp_opnums);
}
/* }}} */
@@ -4414,7 +4940,7 @@ static void zend_compile_typename(zend_ast *ast, zend_arg_info *arg_info) /* {{{
zend_uchar type = zend_lookup_builtin_type_by_name(class_name);
if (type != 0) {
- if (ast->attr != ZEND_NAME_NOT_FQ) {
+ if ((ast->attr & ZEND_NAME_NOT_FQ) != ZEND_NAME_NOT_FQ) {
zend_error_noreturn(E_COMPILE_ERROR,
"Scalar type declaration '%s' must be unqualified",
ZSTR_VAL(zend_string_tolower(class_name)));
@@ -4454,8 +4980,17 @@ 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->attr & ZEND_TYPE_NULLABLE) {
+ arg_infos->allow_null = 1;
+ return_type_ast->attr &= ~ZEND_TYPE_NULLABLE;
+ }
+
zend_compile_typename(return_type_ast, arg_infos);
+ if (arg_infos->type_hint == IS_VOID && arg_infos->allow_null) {
+ zend_error_noreturn(E_COMPILE_ERROR, "Void type cannot be nullable");
+ }
+
arg_infos++;
op_array->fn_flags |= ZEND_ACC_HAS_RETURN_TYPE;
} else {
@@ -4491,11 +5026,7 @@ void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast) /* {{{ */
zend_error_noreturn(E_COMPILE_ERROR, "Redefinition of parameter $%s",
ZSTR_VAL(name));
} else if (zend_string_equals_literal(name, "this")) {
- if ((op_array->scope || (op_array->fn_flags & ZEND_ACC_CLOSURE))
- && (op_array->fn_flags & ZEND_ACC_STATIC) == 0) {
- zend_error_noreturn(E_COMPILE_ERROR, "Cannot use $this as parameter");
- }
- op_array->this_var = var_node.u.op.var;
+ zend_error_noreturn(E_COMPILE_ERROR, "Cannot use $this as parameter");
}
if (op_array->fn_flags & ZEND_ACC_VARIADIC) {
@@ -4542,12 +5073,17 @@ void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast) /* {{{ */
&& (Z_TYPE(default_node.u.constant) == IS_NULL
|| (Z_TYPE(default_node.u.constant) == IS_CONSTANT
&& strcasecmp(Z_STRVAL(default_node.u.constant), "NULL") == 0));
+ zend_bool is_explicitly_nullable = (type_ast->attr & ZEND_TYPE_NULLABLE) == ZEND_TYPE_NULLABLE;
op_array->fn_flags |= ZEND_ACC_HAS_TYPE_HINTS;
- arg_info->allow_null = has_null_default;
+ arg_info->allow_null = has_null_default || is_explicitly_nullable;
zend_compile_typename(type_ast, arg_info);
+ if (arg_info->type_hint == IS_VOID) {
+ zend_error_noreturn(E_COMPILE_ERROR, "void cannot be used as a parameter type");
+ }
+
if (type_ast->kind == ZEND_AST_TYPE) {
if (arg_info->type_hint == IS_ARRAY) {
if (default_ast && !has_null_default
@@ -4623,23 +5159,61 @@ void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast) /* {{{ */
}
/* }}} */
+static void zend_compile_closure_binding(znode *closure, zend_ast *uses_ast) /* {{{ */
+{
+ zend_ast_list *list = zend_ast_get_list(uses_ast);
+ uint32_t i;
+
+ for (i = 0; i < list->children; ++i) {
+ zend_ast *var_name_ast = list->child[i];
+ zend_string *var_name = zend_ast_get_str(var_name_ast);
+ zend_bool by_ref = var_name_ast->attr;
+ zend_op *opline;
+
+ if (zend_string_equals_literal(var_name, "this")) {
+ zend_error_noreturn(E_COMPILE_ERROR, "Cannot use $this as lexical variable");
+ }
+
+ if (zend_is_auto_global(var_name)) {
+ zend_error_noreturn(E_COMPILE_ERROR, "Cannot use auto-global as lexical variable");
+ }
+
+ opline = zend_emit_op(NULL, ZEND_BIND_LEXICAL, closure, NULL);
+ opline->op2_type = IS_CV;
+ opline->op2.var = lookup_cv(CG(active_op_array), zend_string_copy(var_name));
+ opline->extended_value = by_ref;
+ }
+}
+/* }}} */
+
void zend_compile_closure_uses(zend_ast *ast) /* {{{ */
{
+ zend_op_array *op_array = CG(active_op_array);
zend_ast_list *list = zend_ast_get_list(ast);
uint32_t i;
for (i = 0; i < list->children; ++i) {
zend_ast *var_ast = list->child[i];
- zend_string *name = zend_ast_get_str(var_ast);
+ zend_string *var_name = zend_ast_get_str(var_ast);
zend_bool by_ref = var_ast->attr;
zval zv;
+ ZVAL_NULL(&zv);
- if (zend_string_equals_literal(name, "this")) {
- zend_error_noreturn(E_COMPILE_ERROR, "Cannot use $this as lexical variable");
+ if (op_array->static_variables
+ && zend_hash_exists(op_array->static_variables, var_name)) {
+ zend_error_noreturn(E_COMPILE_ERROR,
+ "Cannot use variable $%s twice", ZSTR_VAL(var_name));
}
- ZVAL_NULL(&zv);
- Z_CONST_FLAGS(zv) = by_ref ? IS_LEXICAL_REF : IS_LEXICAL_VAR;
+ {
+ int i;
+ for (i = 0; i < op_array->last_var; i++) {
+ if (zend_string_equals(op_array->vars[i], var_name)) {
+ zend_error_noreturn(E_COMPILE_ERROR,
+ "Cannot use lexical variable $%s as a parameter name", ZSTR_VAL(var_name));
+ }
+ }
+ }
zend_compile_static_var_common(var_ast, &zv, by_ref);
}
@@ -4819,7 +5393,7 @@ void zend_begin_method_decl(zend_op_array *op_array, zend_string *name, zend_boo
static void zend_begin_func_decl(znode *result, zend_op_array *op_array, zend_ast_decl *decl) /* {{{ */
{
zend_ast *params_ast = decl->child[0];
- zend_string *unqualified_name, *name, *lcname;
+ zend_string *unqualified_name, *name, *lcname, *key;
zend_op *opline;
unqualified_name = decl->name;
@@ -4842,22 +5416,21 @@ static void zend_begin_func_decl(znode *result, zend_op_array *op_array, zend_as
ZEND_AUTOLOAD_FUNC_NAME);
}
+ key = zend_build_runtime_definition_key(lcname, decl->lex_pos);
+ zend_hash_update_ptr(CG(function_table), key, op_array);
+
if (op_array->fn_flags & ZEND_ACC_CLOSURE) {
opline = zend_emit_op_tmp(result, ZEND_DECLARE_LAMBDA_FUNCTION, NULL, NULL);
+ opline->op1_type = IS_CONST;
+ LITERAL_STR(opline->op1, key);
} else {
opline = get_next_op(CG(active_op_array));
opline->opcode = ZEND_DECLARE_FUNCTION;
- opline->op2_type = IS_CONST;
- LITERAL_STR(opline->op2, zend_string_copy(lcname));
- }
-
- {
- zend_string *key = zend_build_runtime_definition_key(lcname, decl->lex_pos);
-
opline->op1_type = IS_CONST;
- LITERAL_STR(opline->op1, key);
-
- zend_hash_update_ptr(CG(function_table), key, op_array);
+ LITERAL_STR(opline->op1, zend_string_copy(lcname));
+ /* RTD key is placed after lcname literal in op1 */
+ zend_add_literal_string(CG(active_op_array), &key);
+ SET_UNUSED(opline->op2);
}
zend_string_release(lcname);
@@ -4895,6 +5468,9 @@ void zend_compile_func_decl(znode *result, zend_ast *ast) /* {{{ */
zend_begin_method_decl(op_array, decl->name, has_body);
} else {
zend_begin_func_decl(result, op_array, decl);
+ if (uses_ast) {
+ zend_compile_closure_binding(result, uses_ast);
+ }
}
CG(active_op_array) = op_array;
@@ -4915,6 +5491,10 @@ void zend_compile_func_decl(znode *result, zend_ast *ast) /* {{{ */
}
zend_compile_params(params_ast, return_type_ast);
+ if (CG(active_op_array)->fn_flags & ZEND_ACC_GENERATOR) {
+ zend_mark_function_as_generator();
+ zend_emit_op(NULL, ZEND_GENERATOR_CREATE, NULL, NULL);
+ }
if (uses_ast) {
zend_compile_closure_uses(uses_ast);
}
@@ -4929,7 +5509,7 @@ void zend_compile_func_decl(znode *result, zend_ast *ast) /* {{{ */
CG(zend_lineno) = decl->end_lineno;
zend_do_extended_info();
- zend_emit_final_return(NULL);
+ zend_emit_final_return(0);
pass_two(CG(active_op_array));
zend_oparray_context_end(&orig_oparray_context);
@@ -5008,25 +5588,25 @@ void zend_compile_class_const_decl(zend_ast *ast) /* {{{ */
zend_ast *const_ast = list->child[i];
zend_ast *name_ast = const_ast->child[0];
zend_ast *value_ast = const_ast->child[1];
+ zend_ast *doc_comment_ast = const_ast->child[2];
zend_string *name = zend_ast_get_str(name_ast);
+ zend_string *doc_comment = doc_comment_ast ? zend_string_copy(zend_ast_get_str(doc_comment_ast)) : NULL;
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");
+ if (UNEXPECTED(ast->attr & (ZEND_ACC_STATIC|ZEND_ACC_ABSTRACT|ZEND_ACC_FINAL))) {
+ if (ast->attr & ZEND_ACC_STATIC) {
+ zend_error_noreturn(E_COMPILE_ERROR, "Cannot use 'static' as constant modifier");
+ } else if (ast->attr & ZEND_ACC_ABSTRACT) {
+ zend_error_noreturn(E_COMPILE_ERROR, "Cannot use 'abstract' as constant modifier");
+ } else if (ast->attr & ZEND_ACC_FINAL) {
+ zend_error_noreturn(E_COMPILE_ERROR, "Cannot use 'final' as constant modifier");
+ }
}
zend_const_expr_to_zval(&value_zv, value_ast);
name = zend_new_interned_string_safe(name);
- if (zend_hash_add(&ce->constants_table, name, &value_zv) == NULL) {
- zend_error_noreturn(E_COMPILE_ERROR, "Cannot redefine class constant %s::%s",
- ZSTR_VAL(ce->name), ZSTR_VAL(name));
- }
-
- if (Z_CONSTANT(value_zv)) {
- ce->ce_flags &= ~ZEND_ACC_CONSTANTS_UPDATED;
- }
+ zend_declare_class_constant_ex(ce, name, &value_zv, ast->attr, doc_comment);
}
}
/* }}} */
@@ -5277,37 +5857,34 @@ void zend_compile_class_decl(zend_ast *ast) /* {{{ */
opline = get_next_op(CG(active_op_array));
zend_make_var_result(&declare_node, opline);
- // TODO.AST drop this
GET_NODE(&FC(implementing_class), opline->result);
- opline->op2_type = IS_CONST;
- LITERAL_STR(opline->op2, lcname);
+ opline->op1_type = IS_CONST;
+ LITERAL_STR(opline->op1, lcname);
if (decl->flags & ZEND_ACC_ANON_CLASS) {
if (extends_ast) {
opline->opcode = ZEND_DECLARE_ANON_INHERITED_CLASS;
- opline->extended_value = extends_node.u.op.var;
+ SET_NODE(opline->op2, &extends_node);
} else {
opline->opcode = ZEND_DECLARE_ANON_CLASS;
}
- opline->op1_type = IS_UNUSED;
-
zend_hash_update_ptr(CG(class_table), lcname, ce);
} else {
zend_string *key;
if (extends_ast) {
opline->opcode = ZEND_DECLARE_INHERITED_CLASS;
- opline->extended_value = extends_node.u.op.var;
+ SET_NODE(opline->op2, &extends_node);
} else {
opline->opcode = ZEND_DECLARE_CLASS;
+ SET_UNUSED(opline->op2);
}
key = zend_build_runtime_definition_key(lcname, decl->lex_pos);
-
- opline->op1_type = IS_CONST;
- LITERAL_STR(opline->op1, key);
+ /* RTD key is placed after lcname literal in op1 */
+ zend_add_literal_string(CG(active_op_array), &key);
zend_hash_update_ptr(CG(class_table), key, ce);
}
@@ -5804,6 +6381,35 @@ static zend_bool zend_try_ct_eval_magic_const(zval *zv, zend_ast *ast) /* {{{ */
}
/* }}} */
+ZEND_API zend_bool zend_binary_op_produces_numeric_string_error(uint32_t opcode, zval *op1, zval *op2) /* {{{ */
+{
+ if (!(opcode == ZEND_ADD || opcode == ZEND_SUB || opcode == ZEND_MUL || opcode == ZEND_DIV
+ || opcode == ZEND_POW || opcode == ZEND_MOD || opcode == ZEND_SL || opcode == ZEND_SR
+ || opcode == ZEND_BW_OR || opcode == ZEND_BW_AND || opcode == ZEND_BW_XOR)) {
+ return 0;
+ }
+
+ /* While basic arithmetic operators always produce numeric string errors,
+ * bitwise operators don't produce errors if both operands are strings */
+ if ((opcode == ZEND_BW_OR || opcode == ZEND_BW_AND || opcode == ZEND_BW_XOR)
+ && Z_TYPE_P(op1) == IS_STRING && Z_TYPE_P(op2) == IS_STRING) {
+ return 0;
+ }
+
+ if (Z_TYPE_P(op1) == IS_STRING
+ && !is_numeric_string(Z_STRVAL_P(op1), Z_STRLEN_P(op1), NULL, NULL, 0)) {
+ return 1;
+ }
+
+ if (Z_TYPE_P(op2) == IS_STRING
+ && !is_numeric_string(Z_STRVAL_P(op2), Z_STRLEN_P(op2), NULL, NULL, 0)) {
+ return 1;
+ }
+
+ return 0;
+}
+/* }}} */
+
static inline zend_bool zend_try_ct_eval_binary_op(zval *result, uint32_t opcode, zval *op1, zval *op2) /* {{{ */
{
binary_op_type fn = get_binary_op(opcode);
@@ -5817,6 +6423,11 @@ static inline zend_bool zend_try_ct_eval_binary_op(zval *result, uint32_t opcode
return 0;
}
+ /* don't evaluate numeric string error-producing operations at compile-time */
+ if (zend_binary_op_produces_numeric_string_error(opcode, op1, op2)) {
+ return 0;
+ }
+
fn(result, op1, op2);
return 1;
}
@@ -5829,11 +6440,11 @@ static inline void zend_ct_eval_unary_op(zval *result, uint32_t opcode, zval *op
}
/* }}} */
-static inline void zend_ct_eval_unary_pm(zval *result, zend_ast_kind kind, zval *op) /* {{{ */
+static inline zend_bool zend_try_ct_eval_unary_pm(zval *result, zend_ast_kind kind, zval *op) /* {{{ */
{
zval left;
ZVAL_LONG(&left, (kind == ZEND_AST_UNARY_PLUS) ? 1 : -1);
- mul_function(result, &left, op);
+ return zend_try_ct_eval_binary_op(result, ZEND_MUL, &left, op);
}
/* }}} */
@@ -5851,14 +6462,22 @@ static zend_bool zend_try_ct_eval_array(zval *result, zend_ast *ast) /* {{{ */
uint32_t i;
zend_bool is_constant = 1;
+ if (ast->attr) {
+ zend_error(E_COMPILE_ERROR, "Cannot use list() as standalone expression");
+ }
+
/* First ensure that *all* child nodes are constant and by-val */
for (i = 0; i < list->children; ++i) {
zend_ast *elem_ast = list->child[i];
- zend_bool by_ref = elem_ast->attr;
+
+ if (elem_ast == NULL) {
+ zend_error(E_COMPILE_ERROR, "Cannot use empty array elements in arrays");
+ }
+
zend_eval_const_expr(&elem_ast->child[0]);
zend_eval_const_expr(&elem_ast->child[1]);
- if (by_ref || elem_ast->child[0]->kind != ZEND_AST_ZVAL
+ if (elem_ast->attr /* by_ref */ || elem_ast->child[0]->kind != ZEND_AST_ZVAL
|| (elem_ast->child[1] && elem_ast->child[1]->kind != ZEND_AST_ZVAL)
) {
is_constant = 0;
@@ -6031,10 +6650,11 @@ void zend_compile_unary_pm(znode *result, zend_ast *ast) /* {{{ */
zend_compile_expr(&expr_node, expr_ast);
if (expr_node.op_type == IS_CONST) {
- result->op_type = IS_CONST;
- zend_ct_eval_unary_pm(&result->u.constant, ast->kind, &expr_node.u.constant);
- zval_ptr_dtor(&expr_node.u.constant);
- return;
+ if (zend_try_ct_eval_unary_pm(&result->u.constant, ast->kind, &expr_node.u.constant)) {
+ result->op_type = IS_CONST;
+ zval_ptr_dtor(&expr_node.u.constant);
+ return;
+ }
}
lefthand_node.op_type = IS_CONST;
@@ -6288,7 +6908,7 @@ void zend_compile_yield(znode *result, zend_ast *ast) /* {{{ */
if (value_ast) {
if (returns_by_ref && zend_is_variable(value_ast) && !zend_is_call(value_ast)) {
- zend_compile_var(&value_node, value_ast, BP_VAR_REF);
+ zend_compile_var(&value_node, value_ast, BP_VAR_W);
} else {
zend_compile_expr(&value_node, value_ast);
}
@@ -6334,13 +6954,8 @@ void zend_compile_instanceof(znode *result, zend_ast *ast) /* {{{ */
"instanceof expects an object instance, constant given");
}
- if (zend_is_const_default_class_ref(class_ast)) {
- class_node.op_type = IS_CONST;
- ZVAL_STR(&class_node.u.constant, zend_resolve_class_name_ast(class_ast));
- } else {
- opline = zend_compile_class_ref(&class_node, class_ast, 0);
- opline->extended_value |= ZEND_FETCH_CLASS_NO_AUTOLOAD;
- }
+ zend_compile_class_ref_ex(&class_node, class_ast,
+ ZEND_FETCH_CLASS_NO_AUTOLOAD | ZEND_FETCH_CLASS_EXCEPTION);
opline = zend_emit_op_tmp(result, ZEND_INSTANCEOF, &obj_node, NULL);
@@ -6394,7 +7009,9 @@ void zend_compile_isset_or_empty(znode *result, zend_ast *ast) /* {{{ */
switch (var_ast->kind) {
case ZEND_AST_VAR:
- if (zend_try_compile_cv(&var_node, var_ast) == SUCCESS) {
+ if (is_this_fetch(var_ast)) {
+ opline = zend_emit_op(result, ZEND_ISSET_ISEMPTY_THIS, NULL, NULL);
+ } else if (zend_try_compile_cv(&var_node, var_ast) == SUCCESS) {
opline = zend_emit_op(result, ZEND_ISSET_ISEMPTY_VAR, &var_node, NULL);
opline->extended_value = ZEND_FETCH_LOCAL | ZEND_QUICK_SET;
} else {
@@ -6412,7 +7029,7 @@ void zend_compile_isset_or_empty(znode *result, zend_ast *ast) /* {{{ */
break;
case ZEND_AST_STATIC_PROP:
opline = zend_compile_static_prop_common(result, var_ast, BP_VAR_IS, 0);
- opline->opcode = ZEND_ISSET_ISEMPTY_VAR;
+ opline->opcode = ZEND_ISSET_ISEMPTY_STATIC_PROP;
break;
EMPTY_SWITCH_DEFAULT_CASE()
}
@@ -6426,10 +7043,9 @@ void zend_compile_silence(znode *result, zend_ast *ast) /* {{{ */
{
zend_ast *expr_ast = ast->child[0];
znode silence_node;
- uint32_t begin_opline_num, end_opline_num;
- zend_brk_cont_element *brk_cont_element;
+ uint32_t range;
- begin_opline_num = get_next_op_number(CG(active_op_array));
+ range = zend_start_live_range(CG(active_op_array), get_next_op_number(CG(active_op_array)));
zend_emit_op_tmp(&silence_node, ZEND_BEGIN_SILENCE, NULL, NULL);
if (expr_ast->kind == ZEND_AST_VAR) {
@@ -6440,15 +7056,12 @@ void zend_compile_silence(znode *result, zend_ast *ast) /* {{{ */
zend_compile_expr(result, expr_ast);
}
- end_opline_num = get_next_op_number(CG(active_op_array));
- zend_emit_op(NULL, ZEND_END_SILENCE, &silence_node, NULL);
-
/* Store BEGIN_SILENCE/END_SILENCE pair to restore previous
* EG(error_reporting) value on exception */
- brk_cont_element = get_next_brk_cont_element(CG(active_op_array));
- brk_cont_element->start = begin_opline_num;
- brk_cont_element->cont = brk_cont_element->brk = end_opline_num;
- brk_cont_element->parent = -1;
+ zend_end_live_range(CG(active_op_array), range, get_next_op_number(CG(active_op_array)),
+ ZEND_LIVE_SILENCE, silence_node.u.op.var);
+
+ zend_emit_op(NULL, ZEND_END_SILENCE, &silence_node, NULL);
}
/* }}} */
@@ -6484,12 +7097,18 @@ void zend_compile_array(znode *result, zend_ast *ast) /* {{{ */
for (i = 0; i < list->children; ++i) {
zend_ast *elem_ast = list->child[i];
- zend_ast *value_ast = elem_ast->child[0];
- zend_ast *key_ast = elem_ast->child[1];
- zend_bool by_ref = elem_ast->attr;
-
+ zend_ast *value_ast, *key_ast;
+ zend_bool by_ref;
znode value_node, key_node, *key_node_ptr = NULL;
+ if (elem_ast == NULL) {
+ zend_error(E_COMPILE_ERROR, "Cannot use empty array elements in arrays");
+ }
+
+ value_ast = elem_ast->child[0];
+ key_ast = elem_ast->child[1];
+ by_ref = elem_ast->attr;
+
if (key_ast) {
zend_compile_expr(&key_node, key_ast);
zend_handle_numeric_op(&key_node);
@@ -6526,7 +7145,7 @@ void zend_compile_array(znode *result, zend_ast *ast) /* {{{ */
/* Add a flag to INIT_ARRAY if we know this array cannot be packed */
if (!packed) {
- ZEND_ASSERT(opnum_init != -1);
+ ZEND_ASSERT(opnum_init != (uint32_t)-1);
opline = &CG(active_op_array)->opcodes[opnum_init];
opline->extended_value |= ZEND_ARRAY_NOT_PACKED;
}
@@ -6592,7 +7211,6 @@ void zend_compile_class_const(znode *result, zend_ast *ast) /* {{{ */
znode class_node, const_node;
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) {
@@ -6608,31 +7226,26 @@ void zend_compile_class_const(znode *result, zend_ast *ast) /* {{{ */
zend_eval_const_expr(&const_ast);
if (class_ast->kind == ZEND_AST_ZVAL) {
+ zend_string *resolved_name;
+
resolved_name = zend_resolve_class_name_ast(class_ast);
if (const_ast->kind == ZEND_AST_ZVAL && zend_try_ct_eval_class_const(&result->u.constant, resolved_name, zend_ast_get_str(const_ast))) {
result->op_type = IS_CONST;
zend_string_release(resolved_name);
return;
}
+ zend_string_release(resolved_name);
}
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;
- ZVAL_STR(&class_node.u.constant, resolved_name);
- } else {
- if (class_ast->kind == ZEND_AST_ZVAL) {
- zend_string_release(resolved_name);
- }
- zend_compile_class_ref(&class_node, class_ast, 1);
- }
+ zend_compile_class_ref_ex(&class_node, class_ast, ZEND_FETCH_CLASS_EXCEPTION);
zend_compile_expr(&const_node, const_ast);
- opline = zend_emit_op_tmp(result, ZEND_FETCH_CONSTANT, NULL, &const_node);
+ opline = zend_emit_op_tmp(result, ZEND_FETCH_CLASS_CONSTANT, NULL, &const_node);
zend_set_class_name_op1(opline, &class_node);
@@ -6774,10 +7387,7 @@ static void zend_compile_encaps_list(znode *result, zend_ast *ast) /* {{{ */
GET_NODE(result, opline->result);
} else {
uint32_t var;
- zend_brk_cont_element *info = get_next_brk_cont_element(CG(active_op_array));
- info->start = rope_init_lineno;
- info->parent = CG(context).current_brk_cont;
- info->cont = info->brk = opline - CG(active_op_array)->opcodes;
+ uint32_t range = zend_start_live_range(CG(active_op_array), rope_init_lineno);
init_opline->extended_value = j;
opline->opcode = ZEND_ROPE_END;
@@ -6791,15 +7401,19 @@ static void zend_compile_encaps_list(znode *result, zend_ast *ast) /* {{{ */
get_temporary_variable(CG(active_op_array));
i--;
}
+
+ zend_end_live_range(CG(active_op_array), range, opline - CG(active_op_array)->opcodes,
+ ZEND_LIVE_ROPE, var);
+
/* Update all the previous opcodes to use the same variable */
while (opline != init_opline) {
opline--;
if (opline->opcode == ZEND_ROPE_ADD &&
- opline->result.var == -1) {
+ opline->result.var == (uint32_t)-1) {
opline->op1.var = var;
opline->result.var = var;
} else if (opline->opcode == ZEND_ROPE_INIT &&
- opline->result.var == -1) {
+ opline->result.var == (uint32_t)-1) {
opline->result.var = var;
}
}
@@ -7275,9 +7889,7 @@ void zend_compile_var(znode *result, zend_ast *ast, uint32_t type) /* {{{ */
*result = *zend_ast_get_znode(ast);
return;
default:
- if (type == BP_VAR_W || type == BP_VAR_REF
- || type == BP_VAR_RW || type == BP_VAR_UNSET
- ) {
+ if (type == BP_VAR_W || type == BP_VAR_RW || type == BP_VAR_UNSET) {
zend_error_noreturn(E_COMPILE_ERROR,
"Cannot use temporary expression in write context");
}
@@ -7387,7 +7999,9 @@ void zend_eval_const_expr(zend_ast **ast_ptr) /* {{{ */
return;
}
- zend_ct_eval_unary_pm(&result, ast->kind, zend_ast_get_zval(ast->child[0]));
+ if (!zend_try_ct_eval_unary_pm(&result, ast->kind, zend_ast_get_zval(ast->child[0]))) {
+ return;
+ }
break;
case ZEND_AST_COALESCE:
/* Set isset fetch indicator here, opcache disallows runtime altering of the AST */
diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h
index bbd2df7f2e..302f4598dc 100644
--- a/Zend/zend_compile.h
+++ b/Zend/zend_compile.h
@@ -31,8 +31,6 @@
#include "zend_llist.h"
-#define DEBUG_ZEND 0
-
#define SET_UNUSED(op) op ## _type = IS_UNUSED
#define MAKE_NOP(opline) do { \
@@ -111,18 +109,6 @@ typedef struct _zend_declarables {
zend_long ticks;
} zend_declarables;
-/* Compilation context that is different for each op array. */
-typedef struct _zend_oparray_context {
- uint32_t opcodes_size;
- int vars_size;
- int literals_size;
- int current_brk_cont;
- int backpatch_count;
- int in_finally;
- uint32_t fast_call_var;
- HashTable *labels;
-} zend_oparray_context;
-
/* Compilation context that is different for each file, but shared between op arrays. */
typedef struct _zend_file_context {
zend_declarables declarables;
@@ -185,6 +171,33 @@ typedef struct _zend_try_catch_element {
uint32_t finally_end;
} zend_try_catch_element;
+#define ZEND_LIVE_TMPVAR 0
+#define ZEND_LIVE_LOOP 1
+#define ZEND_LIVE_SILENCE 2
+#define ZEND_LIVE_ROPE 3
+#define ZEND_LIVE_MASK 3
+
+typedef struct _zend_live_range {
+ uint32_t var; /* low bits are used for variable type (ZEND_LIVE_* macros) */
+ uint32_t start;
+ uint32_t end;
+} zend_live_range;
+
+/* Compilation context that is different for each op array. */
+typedef struct _zend_oparray_context {
+ uint32_t opcodes_size;
+ int vars_size;
+ int literals_size;
+ int backpatch_count;
+ int in_finally;
+ uint32_t fast_call_var;
+ uint32_t try_catch_offset;
+ int current_brk_cont;
+ int last_brk_cont;
+ zend_brk_cont_element *brk_cont_array;
+ HashTable *labels;
+} zend_oparray_context;
+
/* method flags (types) */
#define ZEND_ACC_STATIC 0x01
#define ZEND_ACC_ABSTRACT 0x02
@@ -296,6 +309,12 @@ typedef struct _zend_property_info {
#define OBJ_PROP_TO_NUM(offset) \
((offset - OBJ_PROP_TO_OFFSET(0)) / sizeof(zval))
+typedef struct _zend_class_constant {
+ zval value; /* access flags are stored in reserved: zval.u2.access_flags */
+ zend_string *doc_comment;
+ zend_class_entry *ce;
+} zend_class_constant;
+
/* arg_info for internal functions */
typedef struct _zend_internal_arg_info {
const char *name;
@@ -345,8 +364,6 @@ struct _zend_op_array {
uint32_t *refcount;
- uint32_t this_var;
-
uint32_t last;
zend_op *opcodes;
@@ -354,9 +371,9 @@ struct _zend_op_array {
uint32_t T;
zend_string **vars;
- int last_brk_cont;
+ int last_live_range;
int last_try_catch;
- zend_brk_cont_element *brk_cont_array;
+ zend_live_range *live_range;
zend_try_catch_element *try_catch_array;
/* static variables support */
@@ -403,6 +420,7 @@ typedef struct _zend_internal_function {
union _zend_function {
zend_uchar type; /* MUST be the first element of this struct! */
+ uint32_t quick_arg_flags;
struct {
zend_uchar type; /* never used */
@@ -433,7 +451,6 @@ struct _zend_execute_data {
zval *return_value;
zend_function *func; /* executed funcrion */
zval This; /* this + call_info + num_args */
- zend_class_entry *called_scope;
zend_execute_data *prev_execute_data;
zend_array *symbol_table;
#if ZEND_EX_USE_RUN_TIME_CACHE
@@ -450,13 +467,17 @@ struct _zend_execute_data {
#define ZEND_CALL_TOP (1 << 1)
#define ZEND_CALL_FREE_EXTRA_ARGS (1 << 2) /* equal to IS_TYPE_REFCOUNTED */
#define ZEND_CALL_CTOR (1 << 3)
-#define ZEND_CALL_CTOR_RESULT_UNUSED (1 << 4)
+#define ZEND_CALL_HAS_SYMBOL_TABLE (1 << 4)
#define ZEND_CALL_CLOSURE (1 << 5)
#define ZEND_CALL_RELEASE_THIS (1 << 6)
#define ZEND_CALL_ALLOCATED (1 << 7)
+#define ZEND_CALL_GENERATOR (1 << 8)
+#define ZEND_CALL_DYNAMIC (1 << 9)
+
+#define ZEND_CALL_INFO_SHIFT 16
#define ZEND_CALL_INFO(call) \
- (Z_TYPE_INFO((call)->This) >> 24)
+ (Z_TYPE_INFO((call)->This) >> ZEND_CALL_INFO_SHIFT)
#define ZEND_CALL_KIND_EX(call_info) \
(call_info & (ZEND_CALL_CODE | ZEND_CALL_TOP))
@@ -464,12 +485,12 @@ struct _zend_execute_data {
#define ZEND_CALL_KIND(call) \
ZEND_CALL_KIND_EX(ZEND_CALL_INFO(call))
-#define ZEND_SET_CALL_INFO(call, info) do { \
- Z_TYPE_INFO((call)->This) = IS_OBJECT_EX | ((info) << 24); \
+#define ZEND_SET_CALL_INFO(call, object, info) do { \
+ Z_TYPE_INFO((call)->This) = ((object) ? IS_OBJECT_EX : IS_UNDEF) | ((info) << ZEND_CALL_INFO_SHIFT); \
} while (0)
#define ZEND_ADD_CALL_FLAG_EX(call_info, flag) do { \
- call_info |= ((flag) << 24); \
+ call_info |= ((flag) << ZEND_CALL_INFO_SHIFT); \
} while (0)
#define ZEND_ADD_CALL_FLAG(call, flag) do { \
@@ -514,7 +535,10 @@ struct _zend_execute_data {
#define EX_VAR(n) ZEND_CALL_VAR(execute_data, n)
#define EX_VAR_NUM(n) ZEND_CALL_VAR_NUM(execute_data, n)
-#define EX_VAR_TO_NUM(n) (ZEND_CALL_VAR(NULL, n) - ZEND_CALL_VAR_NUM(NULL, 0))
+#define EX_VAR_TO_NUM(n) ((uint32_t)(ZEND_CALL_VAR(NULL, n) - ZEND_CALL_VAR_NUM(NULL, 0)))
+
+#define ZEND_OPLINE_TO_OFFSET(opline, target) \
+ ((char*)(target) - (char*)(opline))
#define ZEND_OPLINE_NUM_TO_OFFSET(op_array, opline, opline_num) \
((char*)&(op_array)->opcodes[opline_num] - (char*)(opline))
@@ -531,6 +555,10 @@ struct _zend_execute_data {
# define OP_JMP_ADDR(opline, node) \
(node).jmp_addr
+# define ZEND_SET_OP_JMP_ADDR(opline, node, val) do { \
+ (node).jmp_addr = (val); \
+ } while (0)
+
/* convert jump target from compile-time to run-time */
# define ZEND_PASS_TWO_UPDATE_JMP_TARGET(op_array, opline, node) do { \
(node).jmp_addr = (op_array)->opcodes + (node).opline_num; \
@@ -547,6 +575,10 @@ struct _zend_execute_data {
# define OP_JMP_ADDR(opline, node) \
ZEND_OFFSET_TO_OPLINE(opline, (node).jmp_offset)
+# define ZEND_SET_OP_JMP_ADDR(opline, node, val) do { \
+ (node).jmp_offset = ZEND_OPLINE_TO_OFFSET(opline, val); \
+ } while (0)
+
/* convert jump target from compile-time to run-time */
# define ZEND_PASS_TWO_UPDATE_JMP_TARGET(op_array, opline, node) do { \
(node).jmp_offset = ZEND_OPLINE_NUM_TO_OFFSET(op_array, opline, (node).opline_num); \
@@ -652,8 +684,6 @@ struct _zend_execute_data {
#define IS_UNUSED (1<<3) /* Unused variable */
#define IS_CV (1<<4) /* Compiled variable */
-#define EXT_TYPE_UNUSED (1<<5)
-
#include "zend_globals.h"
BEGIN_EXTERN_C()
@@ -694,7 +724,7 @@ ZEND_API unary_op_type get_unary_op(int opcode);
ZEND_API binary_op_type get_binary_op(int opcode);
void zend_stop_lexing(void);
-void zend_emit_final_return(zval *zv);
+void zend_emit_final_return(int return_one);
zend_ast *zend_ast_append_str(zend_ast *left, zend_ast *right);
uint32_t zend_add_class_modifier(uint32_t flags, uint32_t new_flag);
uint32_t zend_add_member_modifier(uint32_t flags, uint32_t new_flag);
@@ -755,12 +785,12 @@ zend_op *get_next_op(zend_op_array *op_array);
void init_op(zend_op *op);
int get_next_op_number(zend_op_array *op_array);
ZEND_API int pass_two(zend_op_array *op_array);
-zend_brk_cont_element *get_next_brk_cont_element(zend_op_array *op_array);
+zend_brk_cont_element *get_next_brk_cont_element(void);
ZEND_API zend_bool zend_is_compiling(void);
ZEND_API char *zend_make_compiled_string_description(const char *name);
ZEND_API void zend_initialize_class_data(zend_class_entry *ce, zend_bool nullify_handlers);
uint32_t zend_get_class_fetch_type(zend_string *name);
-ZEND_API zend_uchar zend_get_call_op(zend_uchar init_op, zend_function *fbc);
+ZEND_API zend_uchar zend_get_call_op(const zend_op *init_op, zend_function *fbc);
typedef zend_bool (*zend_auto_global_callback)(zend_string *name);
typedef struct _zend_auto_global {
@@ -802,16 +832,6 @@ ZEND_API void zend_assert_valid_class_name(const zend_string *const_name);
#define ZEND_FETCH_CLASS_SILENT 0x0100
#define ZEND_FETCH_CLASS_EXCEPTION 0x0200
-/* variable parsing type (compile-time) */
-#define ZEND_PARSED_MEMBER (1<<0)
-#define ZEND_PARSED_METHOD_CALL (1<<1)
-#define ZEND_PARSED_STATIC_MEMBER (1<<2)
-#define ZEND_PARSED_FUNCTION_CALL (1<<3)
-#define ZEND_PARSED_VARIABLE (1<<4)
-#define ZEND_PARSED_REFERENCE_VARIABLE (1<<5)
-#define ZEND_PARSED_NEW (1<<6)
-#define ZEND_PARSED_LIST_EXPR (1<<7)
-
#define ZEND_PARAM_REF (1<<0)
#define ZEND_PARAM_VARIADIC (1<<1)
@@ -819,8 +839,7 @@ ZEND_API void zend_assert_valid_class_name(const zend_string *const_name);
#define ZEND_NAME_NOT_FQ 1
#define ZEND_NAME_RELATIVE 2
-/* unset types */
-#define ZEND_UNSET_REG 0
+#define ZEND_TYPE_NULLABLE (1<<8)
/* var status for backpatching */
#define BP_VAR_R 0
@@ -829,7 +848,6 @@ ZEND_API void zend_assert_valid_class_name(const zend_string *const_name);
#define BP_VAR_IS 3
#define BP_VAR_FUNC_ARG 4
#define BP_VAR_UNSET 5
-#define BP_VAR_REF 6 /* right-hand side of by-ref assignment */
/* Bottom 3 bits are the type, top bits are arg num for BP_VAR_FUNC_ARG */
#define BP_VAR_SHIFT 3
@@ -860,10 +878,7 @@ ZEND_API void zend_assert_valid_class_name(const zend_string *const_name);
/* global/local fetches */
#define ZEND_FETCH_GLOBAL 0x00000000
#define ZEND_FETCH_LOCAL 0x10000000
-#define ZEND_FETCH_STATIC 0x20000000
-#define ZEND_FETCH_STATIC_MEMBER 0x30000000
#define ZEND_FETCH_GLOBAL_LOCK 0x40000000
-#define ZEND_FETCH_LEXICAL 0x50000000
#define ZEND_FETCH_TYPE_MASK 0x70000000
@@ -878,13 +893,6 @@ ZEND_API void zend_assert_valid_class_name(const zend_string *const_name);
#define ZEND_FREE_ON_RETURN (1<<0)
-#define ZEND_MEMBER_FUNC_CALL (1<<0)
-
-#define ZEND_ARG_SEND_BY_REF (1<<0)
-#define ZEND_ARG_COMPILE_TIME_BOUND (1<<1)
-#define ZEND_ARG_SEND_FUNCTION (1<<2)
-#define ZEND_ARG_SEND_SILENT (1<<3)
-
#define ZEND_SEND_BY_VAL 0
#define ZEND_SEND_BY_REF 1
#define ZEND_SEND_PREFER_REF 2
@@ -917,16 +925,16 @@ static zend_always_inline int zend_check_arg_send_type(const zend_function *zf,
#ifdef WORDS_BIGENDIAN
# define ZEND_SET_ARG_FLAG(zf, arg_num, mask) do { \
- *(uint32_t*)&(zf)->type |= ((mask) << ((arg_num) - 1) * 2); \
+ (zf)->quick_arg_flags |= ((mask) << ((arg_num) - 1) * 2); \
} while (0)
# define ZEND_CHECK_ARG_FLAG(zf, arg_num, mask) \
- (((*((uint32_t*)&((zf)->type))) >> (((arg_num) - 1) * 2)) & (mask))
+ (((zf)->quick_arg_flags >> (((arg_num) - 1) * 2)) & (mask))
#else
# define ZEND_SET_ARG_FLAG(zf, arg_num, mask) do { \
- *(uint32_t*)&(zf)->type |= (((mask) << 6) << (arg_num) * 2); \
+ (zf)->quick_arg_flags |= (((mask) << 6) << (arg_num) * 2); \
} while (0)
# define ZEND_CHECK_ARG_FLAG(zf, arg_num, mask) \
- (((*(uint32_t*)&(zf)->type) >> (((arg_num) + 3) * 2)) & (mask))
+ (((zf)->quick_arg_flags >> (((arg_num) + 3) * 2)) & (mask))
#endif
#define QUICK_ARG_MUST_BE_SENT_BY_REF(zf, arg_num) \
@@ -945,11 +953,6 @@ static zend_always_inline int zend_check_arg_send_type(const zend_function *zf,
#define ZEND_RETURNS_FUNCTION 1<<0
#define ZEND_RETURNS_VALUE 1<<1
-#define ZEND_FAST_RET_TO_CATCH 1
-#define ZEND_FAST_RET_TO_FINALLY 2
-
-#define ZEND_FAST_CALL_FROM_FINALLY 1
-
#define ZEND_ARRAY_ELEMENT_REF (1<<0)
#define ZEND_ARRAY_NOT_PACKED (1<<1)
#define ZEND_ARRAY_SIZE_SHIFT 2
@@ -1020,6 +1023,8 @@ END_EXTERN_C()
/* The default value for CG(compiler_options) during eval() */
#define ZEND_COMPILE_DEFAULT_FOR_EVAL 0
+ZEND_API zend_bool zend_binary_op_produces_numeric_string_error(uint32_t opcode, zval *op1, zval *op2);
+
#endif /* ZEND_COMPILE_H */
/*
diff --git a/Zend/zend_constants.c b/Zend/zend_constants.c
index 41edcd93d8..3e09f0cbb8 100644
--- a/Zend/zend_constants.c
+++ b/Zend/zend_constants.c
@@ -33,7 +33,7 @@ void free_zend_constant(zval *zv)
zend_constant *c = Z_PTR_P(zv);
if (!(c->flags & CONST_PERSISTENT)) {
- zval_dtor(&c->value);
+ zval_ptr_dtor(&c->value);
} else {
zval_internal_dtor(&c->value);
}
@@ -251,6 +251,18 @@ static zend_constant *zend_get_special_constant(const char *name, size_t name_le
}
}
+ZEND_API int zend_verify_const_access(zend_class_constant *c, zend_class_entry *scope) /* {{{ */
+{
+ if (Z_ACCESS_FLAGS(c->value) & ZEND_ACC_PUBLIC) {
+ return 1;
+ } else if (Z_ACCESS_FLAGS(c->value) & ZEND_ACC_PRIVATE) {
+ return (c->ce == scope);
+ } else {
+ ZEND_ASSERT(Z_ACCESS_FLAGS(c->value) & ZEND_ACC_PROTECTED);
+ return zend_check_protected(c->ce, scope);
+ }
+}
+/* }}} */
ZEND_API zval *zend_get_constant_str(const char *name, size_t name_len)
{
@@ -294,7 +306,7 @@ ZEND_API zval *zend_get_constant(zend_string *name)
return c ? &c->value : NULL;
}
-ZEND_API zval *zend_get_constant_ex(zend_string *cname, zend_class_entry *scope, zend_ulong flags)
+ZEND_API zval *zend_get_constant_ex(zend_string *cname, zend_class_entry *scope, uint32_t flags)
{
zend_constant *c;
const char *colon;
@@ -317,14 +329,6 @@ ZEND_API zval *zend_get_constant_ex(zend_string *cname, zend_class_entry *scope,
zend_string *class_name = zend_string_init(name, class_name_len, 0);
zval *ret_constant = NULL;
- if (!scope) {
- if (EG(current_execute_data)) {
- scope = EG(scope);
- } else {
- scope = CG(active_class_entry);
- }
- }
-
if (zend_string_equals_literal_ci(class_name, "self")) {
if (UNEXPECTED(!scope)) {
zend_throw_error(NULL, "Cannot access self:: when no class scope is active");
@@ -351,24 +355,39 @@ ZEND_API zval *zend_get_constant_ex(zend_string *cname, zend_class_entry *scope,
ce = zend_fetch_class(class_name, flags);
}
if (ce) {
- ret_constant = zend_hash_find(&ce->constants_table, constant_name);
- if (ret_constant == NULL) {
+ zend_class_constant *c = zend_hash_find_ptr(&ce->constants_table, constant_name);
+ if (c == NULL) {
if ((flags & ZEND_FETCH_CLASS_SILENT) == 0) {
zend_throw_error(NULL, "Undefined class constant '%s::%s'", ZSTR_VAL(class_name), ZSTR_VAL(constant_name));
+ goto failure;
+ }
+ ret_constant = NULL;
+ } else {
+ if (!zend_verify_const_access(c, scope)) {
+ zend_throw_error(NULL, "Cannot access %s const %s::%s", zend_visibility_string(Z_ACCESS_FLAGS(c->value)), ZSTR_VAL(class_name), ZSTR_VAL(constant_name));
+ goto failure;
}
- } else if (Z_ISREF_P(ret_constant)) {
- ret_constant = Z_REFVAL_P(ret_constant);
+ ret_constant = &c->value;
}
}
-failure:
- zend_string_release(class_name);
- zend_string_free(constant_name);
if (ret_constant && Z_CONSTANT_P(ret_constant)) {
- if (UNEXPECTED(zval_update_constant_ex(ret_constant, 1, ce) != SUCCESS)) {
- return NULL;
+ if (Z_TYPE_P(ret_constant) == IS_CONSTANT_AST) {
+ if (IS_CONSTANT_VISITED(ret_constant)) {
+ zend_throw_error(NULL, "Cannot declare self-referencing constant '%s::%s'", ZSTR_VAL(class_name), ZSTR_VAL(constant_name));
+ goto failure;
+ }
+ MARK_CONSTANT_VISITED(ret_constant);
}
+ if (UNEXPECTED(zval_update_constant_ex(ret_constant, ce) != SUCCESS)) {
+ RESET_CONSTANT_VISITED(ret_constant);
+ goto failure;
+ }
+ RESET_CONSTANT_VISITED(ret_constant);
}
+failure:
+ zend_string_release(class_name);
+ zend_string_free(constant_name);
return ret_constant;
}
@@ -417,7 +436,7 @@ failure:
}
}
-zend_constant *zend_quick_get_constant(const zval *key, zend_ulong flags)
+zend_constant *zend_quick_get_constant(const zval *key, uint32_t flags)
{
zend_constant *c;
diff --git a/Zend/zend_constants.h b/Zend/zend_constants.h
index 292d1d305f..cd9a51950a 100644
--- a/Zend/zend_constants.h
+++ b/Zend/zend_constants.h
@@ -65,9 +65,10 @@ int zend_startup_constants(void);
int zend_shutdown_constants(void);
void zend_register_standard_constants(void);
void clean_non_persistent_constants(void);
+ZEND_API int zend_verify_const_access(zend_class_constant *c, zend_class_entry *ce);
ZEND_API zval *zend_get_constant(zend_string *name);
ZEND_API zval *zend_get_constant_str(const char *name, size_t name_len);
-ZEND_API zval *zend_get_constant_ex(zend_string *name, zend_class_entry *scope, zend_ulong flags);
+ZEND_API zval *zend_get_constant_ex(zend_string *name, zend_class_entry *scope, uint32_t flags);
ZEND_API void zend_register_bool_constant(const char *name, size_t name_len, zend_bool bval, int flags, int module_number);
ZEND_API void zend_register_null_constant(const char *name, size_t name_len, int flags, int module_number);
ZEND_API void zend_register_long_constant(const char *name, size_t name_len, zend_long lval, int flags, int module_number);
@@ -76,7 +77,7 @@ ZEND_API void zend_register_string_constant(const char *name, size_t name_len, c
ZEND_API void zend_register_stringl_constant(const char *name, size_t name_len, char *strval, size_t strlen, int flags, int module_number);
ZEND_API int zend_register_constant(zend_constant *c);
void zend_copy_constants(HashTable *target, HashTable *sourc);
-zend_constant *zend_quick_get_constant(const zval *key, zend_ulong flags);
+zend_constant *zend_quick_get_constant(const zval *key, uint32_t flags);
END_EXTERN_C()
#define ZEND_CONSTANT_DTOR free_zend_constant
diff --git a/Zend/zend_exceptions.c b/Zend/zend_exceptions.c
index b52dc2362c..4a85e4e73a 100644
--- a/Zend/zend_exceptions.c
+++ b/Zend/zend_exceptions.c
@@ -58,17 +58,19 @@ static int zend_implement_throwable(zend_class_entry *interface, zend_class_entr
}
/* }}} */
-static inline zend_class_entry *i_get_exception_base(zval *object)
+static inline zend_class_entry *i_get_exception_base(zval *object) /* {{{ */
{
return instanceof_function(Z_OBJCE_P(object), zend_ce_exception) ? zend_ce_exception : zend_ce_error;
}
+/* }}} */
-ZEND_API zend_class_entry *zend_get_exception_base(zval *object)
+ZEND_API zend_class_entry *zend_get_exception_base(zval *object) /* {{{ */
{
return i_get_exception_base(object);
}
+/* }}} */
-void zend_exception_set_previous(zend_object *exception, zend_object *add_previous)
+void zend_exception_set_previous(zend_object *exception, zend_object *add_previous) /* {{{ */
{
zval *previous, *ancestor, *ex;
zval pv, zv, rv;
@@ -85,24 +87,25 @@ void zend_exception_set_previous(zend_object *exception, zend_object *add_previo
ZVAL_OBJ(&zv, exception);
ex = &zv;
do {
- ancestor = zend_read_property(i_get_exception_base(&pv), &pv, "previous", sizeof("previous")-1, 1, &rv);
+ ancestor = zend_read_property_ex(i_get_exception_base(&pv), &pv, CG(known_strings)[ZEND_STR_PREVIOUS], 1, &rv);
while (Z_TYPE_P(ancestor) == IS_OBJECT) {
if (Z_OBJ_P(ancestor) == Z_OBJ_P(ex)) {
OBJ_RELEASE(add_previous);
return;
}
- ancestor = zend_read_property(i_get_exception_base(ancestor), ancestor, "previous", sizeof("previous")-1, 1, &rv);
+ ancestor = zend_read_property_ex(i_get_exception_base(ancestor), ancestor, CG(known_strings)[ZEND_STR_PREVIOUS], 1, &rv);
}
base_ce = i_get_exception_base(ex);
- previous = zend_read_property(base_ce, ex, "previous", sizeof("previous")-1, 1, &rv);
+ previous = zend_read_property_ex(base_ce, ex, CG(known_strings)[ZEND_STR_PREVIOUS], 1, &rv);
if (Z_TYPE_P(previous) == IS_NULL) {
- zend_update_property(base_ce, ex, "previous", sizeof("previous")-1, &pv);
+ zend_update_property_ex(base_ce, ex, CG(known_strings)[ZEND_STR_PREVIOUS], &pv);
GC_REFCOUNT(add_previous)--;
return;
}
ex = previous;
} while (Z_OBJ_P(ex) != add_previous);
}
+/* }}} */
void zend_exception_save(void) /* {{{ */
{
@@ -195,7 +198,7 @@ ZEND_API void zend_clear_exception(void) /* {{{ */
static zend_object *zend_default_exception_new_ex(zend_class_entry *class_type, int skip_top_traces) /* {{{ */
{
- zval obj;
+ zval obj, tmp;
zend_object *object;
zval trace;
zend_class_entry *base_ce;
@@ -216,13 +219,18 @@ static zend_object *zend_default_exception_new_ex(zend_class_entry *class_type,
base_ce = i_get_exception_base(&obj);
if (EXPECTED(class_type != zend_ce_parse_error || !(filename = zend_get_compiled_filename()))) {
- zend_update_property_string(base_ce, &obj, "file", sizeof("file")-1, zend_get_executed_filename());
- zend_update_property_long(base_ce, &obj, "line", sizeof("line")-1, zend_get_executed_lineno());
+ ZVAL_STRING(&tmp, zend_get_executed_filename());
+ zend_update_property_ex(base_ce, &obj, CG(known_strings)[ZEND_STR_FILE], &tmp);
+ zval_ptr_dtor(&tmp);
+ ZVAL_LONG(&tmp, zend_get_executed_lineno());
+ zend_update_property_ex(base_ce, &obj, CG(known_strings)[ZEND_STR_LINE], &tmp);
} else {
- zend_update_property_str(base_ce, &obj, "file", sizeof("file")-1, filename);
- zend_update_property_long(base_ce, &obj, "line", sizeof("line")-1, zend_get_compiled_lineno());
+ ZVAL_STR(&tmp, filename);
+ zend_update_property_ex(base_ce, &obj, CG(known_strings)[ZEND_STR_FILE], &tmp);
+ ZVAL_LONG(&tmp, zend_get_compiled_lineno());
+ zend_update_property_ex(base_ce, &obj, CG(known_strings)[ZEND_STR_LINE], &tmp);
}
- zend_update_property(base_ce, &obj, "trace", sizeof("trace")-1, &trace);
+ zend_update_property_ex(base_ce, &obj, CG(known_strings)[ZEND_STR_TRACE], &trace);
return object;
}
@@ -255,7 +263,7 @@ ZEND_METHOD(exception, __construct)
{
zend_string *message = NULL;
zend_long code = 0;
- zval *object, *previous = NULL;
+ zval tmp, *object, *previous = NULL;
zend_class_entry *base_ce;
int argc = ZEND_NUM_ARGS();
@@ -265,8 +273,10 @@ ZEND_METHOD(exception, __construct)
if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, argc, "|SlO!", &message, &code, &previous, zend_ce_throwable) == FAILURE) {
zend_class_entry *ce;
- if (execute_data->called_scope) {
- ce = execute_data->called_scope;
+ if (Z_TYPE(EX(This)) == IS_OBJECT) {
+ ce = Z_OBJCE(EX(This));
+ } else if (Z_CE(EX(This))) {
+ ce = Z_CE(EX(This));
} else {
ce = base_ce;
}
@@ -275,27 +285,29 @@ ZEND_METHOD(exception, __construct)
}
if (message) {
- zend_update_property_str(base_ce, object, "message", sizeof("message")-1, message);
+ ZVAL_STR(&tmp, message);
+ zend_update_property_ex(base_ce, object, CG(known_strings)[ZEND_STR_MESSAGE], &tmp);
}
if (code) {
- zend_update_property_long(base_ce, object, "code", sizeof("code")-1, code);
+ ZVAL_LONG(&tmp, code);
+ zend_update_property_ex(base_ce, object, CG(known_strings)[ZEND_STR_CODE], &tmp);
}
if (previous) {
- zend_update_property(base_ce, object, "previous", sizeof("previous")-1, previous);
+ zend_update_property_ex(base_ce, object, CG(known_strings)[ZEND_STR_PREVIOUS], previous);
}
}
/* }}} */
/* {{{ proto Exception::__wakeup()
Exception unserialize checks */
-#define CHECK_EXC_TYPE(name, type) \
+#define CHECK_EXC_TYPE(id, type) \
ZVAL_UNDEF(&value); \
- pvalue = zend_read_property(i_get_exception_base(object), (object), name, sizeof(name) - 1, 1, &value); \
+ pvalue = zend_read_property_ex(i_get_exception_base(object), (object), CG(known_strings)[id], 1, &value); \
if(Z_TYPE_P(pvalue) != IS_UNDEF && Z_TYPE_P(pvalue) != type) { \
zval tmp; \
- ZVAL_STRINGL(&tmp, name, sizeof(name) - 1); \
+ ZVAL_STR_COPY(&tmp, CG(known_strings)[id]); \
Z_OBJ_HANDLER_P(object, unset_property)(object, &tmp, NULL); \
zval_ptr_dtor(&tmp); \
}
@@ -304,13 +316,13 @@ ZEND_METHOD(exception, __wakeup)
{
zval value, *pvalue;
zval *object = getThis();
- CHECK_EXC_TYPE("message", IS_STRING);
- CHECK_EXC_TYPE("string", IS_STRING);
- CHECK_EXC_TYPE("code", IS_LONG);
- CHECK_EXC_TYPE("file", IS_STRING);
- CHECK_EXC_TYPE("line", IS_LONG);
- CHECK_EXC_TYPE("trace", IS_ARRAY);
- CHECK_EXC_TYPE("previous", IS_OBJECT);
+ CHECK_EXC_TYPE(ZEND_STR_MESSAGE, IS_STRING);
+ CHECK_EXC_TYPE(ZEND_STR_STRING, IS_STRING);
+ CHECK_EXC_TYPE(ZEND_STR_CODE, IS_LONG);
+ CHECK_EXC_TYPE(ZEND_STR_FILE, IS_STRING);
+ CHECK_EXC_TYPE(ZEND_STR_LINE, IS_LONG);
+ CHECK_EXC_TYPE(ZEND_STR_TRACE, IS_ARRAY);
+ CHECK_EXC_TYPE(ZEND_STR_PREVIOUS, IS_OBJECT);
}
/* }}} */
@@ -320,15 +332,17 @@ ZEND_METHOD(error_exception, __construct)
{
char *message = NULL, *filename = NULL;
zend_long code = 0, severity = E_ERROR, lineno;
- zval *object, *previous = NULL;
+ zval tmp, *object, *previous = NULL;
int argc = ZEND_NUM_ARGS();
size_t message_len, filename_len;
if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, argc, "|sllslO!", &message, &message_len, &code, &severity, &filename, &filename_len, &lineno, &previous, zend_ce_throwable) == FAILURE) {
zend_class_entry *ce;
- if (execute_data->called_scope) {
- ce = execute_data->called_scope;
+ if (Z_TYPE(EX(This)) == IS_OBJECT) {
+ ce = Z_OBJCE(EX(This));
+ } else if (Z_CE(EX(This))) {
+ ce = Z_CE(EX(This));
} else {
ce = zend_ce_error_exception;
}
@@ -339,25 +353,32 @@ ZEND_METHOD(error_exception, __construct)
object = getThis();
if (message) {
- zend_update_property_string(zend_ce_exception, object, "message", sizeof("message")-1, message);
+ ZVAL_STRING(&tmp, message);
+ zend_update_property_ex(zend_ce_exception, object, CG(known_strings)[ZEND_STR_MESSAGE], &tmp);
+ zval_ptr_dtor(&tmp);
}
if (code) {
- zend_update_property_long(zend_ce_exception, object, "code", sizeof("code")-1, code);
+ ZVAL_LONG(&tmp, code);
+ zend_update_property_ex(zend_ce_exception, object, CG(known_strings)[ZEND_STR_CODE], &tmp);
}
if (previous) {
- zend_update_property(zend_ce_exception, object, "previous", sizeof("previous")-1, previous);
+ zend_update_property_ex(zend_ce_exception, object, CG(known_strings)[ZEND_STR_PREVIOUS], previous);
}
- zend_update_property_long(zend_ce_error_exception, object, "severity", sizeof("severity")-1, severity);
+ ZVAL_LONG(&tmp, severity);
+ zend_update_property_ex(zend_ce_exception, object, CG(known_strings)[ZEND_STR_SEVERITY], &tmp);
if (argc >= 4) {
- zend_update_property_string(zend_ce_exception, object, "file", sizeof("file")-1, filename);
+ ZVAL_STRING(&tmp, filename);
+ zend_update_property_ex(zend_ce_exception, object, CG(known_strings)[ZEND_STR_FILE], &tmp);
+ zval_ptr_dtor(&tmp);
if (argc < 5) {
lineno = 0; /* invalidate lineno */
}
- zend_update_property_long(zend_ce_exception, object, "line", sizeof("line")-1, lineno);
+ ZVAL_LONG(&tmp, lineno);
+ zend_update_property_ex(zend_ce_exception, object, CG(known_strings)[ZEND_STR_LINE], &tmp);
}
}
/* }}} */
@@ -367,10 +388,10 @@ ZEND_METHOD(error_exception, __construct)
return; \
}
-#define GET_PROPERTY(object, name) \
- zend_read_property(i_get_exception_base(object), (object), name, sizeof(name) - 1, 0, &rv)
-#define GET_PROPERTY_SILENT(object, name) \
- zend_read_property(i_get_exception_base(object), (object), name, sizeof(name) - 1, 1, &rv)
+#define GET_PROPERTY(object, id) \
+ zend_read_property_ex(i_get_exception_base(object), (object), CG(known_strings)[id], 0, &rv)
+#define GET_PROPERTY_SILENT(object, id) \
+ zend_read_property_ex(i_get_exception_base(object), (object), CG(known_strings)[id], 1, &rv)
/* {{{ proto string Exception|Error::getFile()
Get the file in which the exception occurred */
@@ -380,7 +401,7 @@ ZEND_METHOD(exception, getFile)
DEFAULT_0_PARAMS;
- ZVAL_COPY(return_value, GET_PROPERTY(getThis(), "file"));
+ ZVAL_COPY(return_value, GET_PROPERTY(getThis(), ZEND_STR_FILE));
}
/* }}} */
@@ -392,7 +413,7 @@ ZEND_METHOD(exception, getLine)
DEFAULT_0_PARAMS;
- ZVAL_COPY(return_value, GET_PROPERTY(getThis(), "line"));
+ ZVAL_COPY(return_value, GET_PROPERTY(getThis(), ZEND_STR_LINE));
}
/* }}} */
@@ -404,7 +425,7 @@ ZEND_METHOD(exception, getMessage)
DEFAULT_0_PARAMS;
- ZVAL_COPY(return_value, GET_PROPERTY(getThis(), "message"));
+ ZVAL_COPY(return_value, GET_PROPERTY(getThis(), ZEND_STR_MESSAGE));
}
/* }}} */
@@ -416,7 +437,7 @@ ZEND_METHOD(exception, getCode)
DEFAULT_0_PARAMS;
- ZVAL_COPY(return_value, GET_PROPERTY(getThis(), "code"));
+ ZVAL_COPY(return_value, GET_PROPERTY(getThis(), ZEND_STR_CODE));
}
/* }}} */
@@ -428,7 +449,7 @@ ZEND_METHOD(exception, getTrace)
DEFAULT_0_PARAMS;
- ZVAL_COPY(return_value, GET_PROPERTY(getThis(), "trace"));
+ ZVAL_COPY(return_value, GET_PROPERTY(getThis(), ZEND_STR_TRACE));
}
/* }}} */
@@ -440,80 +461,23 @@ ZEND_METHOD(error_exception, getSeverity)
DEFAULT_0_PARAMS;
- ZVAL_COPY(return_value, GET_PROPERTY(getThis(), "severity"));
+ ZVAL_COPY(return_value, GET_PROPERTY(getThis(), ZEND_STR_SEVERITY));
}
/* }}} */
#define TRACE_APPEND_KEY(key) do { \
- tmp = zend_hash_str_find(ht, key, sizeof(key)-1); \
+ tmp = zend_hash_find(ht, key); \
if (tmp) { \
if (Z_TYPE_P(tmp) != IS_STRING) { \
- zend_error(E_WARNING, "Value for %s is no string", key); \
+ zend_error(E_WARNING, "Value for %s is no string", \
+ ZSTR_VAL(key)); \
smart_str_appends(str, "[unknown]"); \
} else { \
- smart_str_appends(str, Z_STRVAL_P(tmp)); \
+ smart_str_appends(str, Z_STRVAL_P(tmp)); \
} \
} \
} while (0)
-/* Windows uses VK_ESCAPE instead of \e */
-#ifndef VK_ESCAPE
-#define VK_ESCAPE '\e'
-#endif
-
-static size_t compute_escaped_string_len(const char *s, size_t l) {
- size_t i, len = l;
- for (i = 0; i < l; ++i) {
- char c = s[i];
- if (c == '\n' || c == '\r' || c == '\t' ||
- c == '\f' || c == '\v' || c == '\\' || c == VK_ESCAPE) {
- len += 1;
- } else if (c < 32 || c > 126) {
- len += 3;
- }
- }
- return len;
-}
-
-static void smart_str_append_escaped(smart_str *str, const char *s, size_t l) {
- char *res;
- size_t i, len = compute_escaped_string_len(s, l);
-
- smart_str_alloc(str, len, 0);
- res = &ZSTR_VAL(str->s)[ZSTR_LEN(str->s)];
- ZSTR_LEN(str->s) += len;
-
- for (i = 0; i < l; ++i) {
- unsigned char c = s[i];
- if (c < 32 || c == '\\' || c > 126) {
- *res++ = '\\';
- switch (c) {
- case '\n': *res++ = 'n'; break;
- case '\r': *res++ = 'r'; break;
- case '\t': *res++ = 't'; break;
- case '\f': *res++ = 'f'; break;
- case '\v': *res++ = 'v'; break;
- case '\\': *res++ = '\\'; break;
- case VK_ESCAPE: *res++ = 'e'; break;
- default:
- *res++ = 'x';
- if ((c >> 4) < 10) {
- *res++ = (c >> 4) + '0';
- } else {
- *res++ = (c >> 4) + 'A' - 10;
- }
- if ((c & 0xf) < 10) {
- *res++ = (c & 0xf) + '0';
- } else {
- *res++ = (c & 0xf) + 'A' - 10;
- }
- }
- } else {
- *res++ = c;
- }
- }
-}
-
static void _build_trace_args(zval *arg, smart_str *str) /* {{{ */
{
/* the trivial way would be to do
@@ -580,14 +544,14 @@ static void _build_trace_string(smart_str *str, HashTable *ht, uint32_t num) /*
smart_str_append_long(str, num);
smart_str_appendc(str, ' ');
- file = zend_hash_str_find(ht, "file", sizeof("file")-1);
+ file = zend_hash_find(ht, CG(known_strings)[ZEND_STR_FILE]);
if (file) {
if (Z_TYPE_P(file) != IS_STRING) {
zend_error(E_WARNING, "Function name is no string");
smart_str_appends(str, "[unknown function]");
} else{
zend_long line;
- tmp = zend_hash_str_find(ht, "line", sizeof("line")-1);
+ tmp = zend_hash_find(ht, CG(known_strings)[ZEND_STR_LINE]);
if (tmp) {
if (Z_TYPE_P(tmp) == IS_LONG) {
line = Z_LVAL_P(tmp);
@@ -606,11 +570,11 @@ static void _build_trace_string(smart_str *str, HashTable *ht, uint32_t num) /*
} else {
smart_str_appends(str, "[internal function]: ");
}
- TRACE_APPEND_KEY("class");
- TRACE_APPEND_KEY("type");
- TRACE_APPEND_KEY("function");
+ TRACE_APPEND_KEY(CG(known_strings)[ZEND_STR_CLASS]);
+ TRACE_APPEND_KEY(CG(known_strings)[ZEND_STR_TYPE]);
+ TRACE_APPEND_KEY(CG(known_strings)[ZEND_STR_FUNCTION]);
smart_str_appendc(str, '(');
- tmp = zend_hash_str_find(ht, "args", sizeof("args")-1);
+ tmp = zend_hash_find(ht, CG(known_strings)[ZEND_STR_ARGS]);
if (tmp) {
if (Z_TYPE_P(tmp) == IS_ARRAY) {
size_t last_len = ZSTR_LEN(str->s);
@@ -647,7 +611,7 @@ ZEND_METHOD(exception, getTraceAsString)
object = getThis();
base_ce = i_get_exception_base(object);
- trace = zend_read_property(base_ce, object, "trace", sizeof("trace")-1, 1, &rv);
+ trace = zend_read_property_ex(base_ce, object, CG(known_strings)[ZEND_STR_TRACE], 1, &rv);
if (Z_TYPE_P(trace) != IS_ARRAY) {
RETURN_FALSE;
}
@@ -677,7 +641,7 @@ ZEND_METHOD(exception, getPrevious)
DEFAULT_0_PARAMS;
- ZVAL_COPY(return_value, GET_PROPERTY_SILENT(getThis(), "previous"));
+ ZVAL_COPY(return_value, GET_PROPERTY_SILENT(getThis(), ZEND_STR_PREVIOUS));
} /* }}} */
size_t zend_spprintf(char **message, size_t max_len, const char *format, ...) /* {{{ */
@@ -712,25 +676,24 @@ ZEND_METHOD(exception, __toString)
zend_class_entry *base_ce;
zend_string *str;
zend_fcall_info fci;
- zval fname, rv;
+ zval rv, tmp;
+ zend_string *fname;
DEFAULT_0_PARAMS;
str = ZSTR_EMPTY_ALLOC();
exception = getThis();
- ZVAL_STRINGL(&fname, "gettraceasstring", sizeof("gettraceasstring")-1);
+ fname = zend_string_init("gettraceasstring", sizeof("gettraceasstring")-1, 0);
while (exception && Z_TYPE_P(exception) == IS_OBJECT && instanceof_function(Z_OBJCE_P(exception), zend_ce_throwable)) {
zend_string *prev_str = str;
- zend_string *message = zval_get_string(GET_PROPERTY(exception, "message"));
- zend_string *file = zval_get_string(GET_PROPERTY(exception, "file"));
- zend_long line = zval_get_long(GET_PROPERTY(exception, "line"));
+ zend_string *message = zval_get_string(GET_PROPERTY(exception, ZEND_STR_MESSAGE));
+ zend_string *file = zval_get_string(GET_PROPERTY(exception, ZEND_STR_FILE));
+ zend_long line = zval_get_long(GET_PROPERTY(exception, ZEND_STR_LINE));
fci.size = sizeof(fci);
- fci.function_table = &Z_OBJCE_P(exception)->function_table;
- ZVAL_COPY_VALUE(&fci.function_name, &fname);
- fci.symbol_table = NULL;
+ ZVAL_STR(&fci.function_name, fname);
fci.object = Z_OBJ_P(exception);
fci.retval = &trace;
fci.param_count = 0;
@@ -769,16 +732,17 @@ ZEND_METHOD(exception, __toString)
zend_string_release(file);
zval_ptr_dtor(&trace);
- exception = GET_PROPERTY(exception, "previous");
+ exception = GET_PROPERTY(exception, ZEND_STR_PREVIOUS);
}
- zval_dtor(&fname);
+ zend_string_release(fname);
exception = getThis();
base_ce = i_get_exception_base(exception);
/* We store the result in the private property string so we can access
* the result in uncaught exception handlers without memleaks. */
- zend_update_property_str(base_ce, exception, "string", sizeof("string")-1, str);
+ ZVAL_STR(&tmp, str);
+ zend_update_property_ex(base_ce, exception, CG(known_strings)[ZEND_STR_STRING], &tmp);
RETURN_STR(str);
}
@@ -919,7 +883,7 @@ ZEND_API zend_class_entry *zend_get_error_exception(void)
ZEND_API ZEND_COLD zend_object *zend_throw_exception(zend_class_entry *exception_ce, const char *message, zend_long code) /* {{{ */
{
- zval ex;
+ zval ex, tmp;
if (exception_ce) {
if (!instanceof_function(exception_ce, zend_ce_throwable)) {
@@ -933,10 +897,13 @@ ZEND_API ZEND_COLD zend_object *zend_throw_exception(zend_class_entry *exception
if (message) {
- zend_update_property_string(exception_ce, &ex, "message", sizeof("message")-1, message);
+ ZVAL_STRING(&tmp, message);
+ zend_update_property_ex(exception_ce, &ex, CG(known_strings)[ZEND_STR_MESSAGE], &tmp);
+ zval_ptr_dtor(&tmp);
}
if (code) {
- zend_update_property_long(exception_ce, &ex, "code", sizeof("code")-1, code);
+ ZVAL_LONG(&tmp, code);
+ zend_update_property_ex(exception_ce, &ex, CG(known_strings)[ZEND_STR_CODE], &tmp);
}
zend_throw_exception_internal(&ex);
@@ -961,10 +928,11 @@ ZEND_API ZEND_COLD zend_object *zend_throw_exception_ex(zend_class_entry *except
ZEND_API ZEND_COLD zend_object *zend_throw_error_exception(zend_class_entry *exception_ce, const char *message, zend_long code, int severity) /* {{{ */
{
- zval ex;
+ zval ex, tmp;
zend_object *obj = zend_throw_exception(exception_ce, message, code);
ZVAL_OBJ(&ex, obj);
- zend_update_property_long(zend_ce_error_exception, &ex, "severity", sizeof("severity")-1, severity);
+ ZVAL_LONG(&tmp, severity);
+ zend_update_property_ex(zend_ce_error_exception, &ex, CG(known_strings)[ZEND_STR_SEVERITY], &tmp);
return obj;
}
/* }}} */
@@ -979,7 +947,7 @@ static void zend_error_va(int type, const char *file, uint lineno, const char *f
}
/* }}} */
-static void zend_error_helper(int type, const char *filename, const uint lineno, const char *format, ...)
+static void zend_error_helper(int type, const char *filename, const uint lineno, const char *format, ...) /* {{{ */
{
va_list va;
@@ -987,6 +955,7 @@ static void zend_error_helper(int type, const char *filename, const uint lineno,
zend_error_cb(type, filename, lineno, format, va);
va_end(va);
}
+/* }}} */
/* This function doesn't return if it uses E_ERROR */
ZEND_API ZEND_COLD void zend_exception_error(zend_object *ex, int severity) /* {{{ */
@@ -998,9 +967,9 @@ ZEND_API ZEND_COLD void zend_exception_error(zend_object *ex, int severity) /* {
ce_exception = Z_OBJCE(exception);
EG(exception) = NULL;
if (ce_exception == zend_ce_parse_error) {
- zend_string *message = zval_get_string(GET_PROPERTY(&exception, "message"));
- zend_string *file = zval_get_string(GET_PROPERTY_SILENT(&exception, "file"));
- zend_long line = zval_get_long(GET_PROPERTY_SILENT(&exception, "line"));
+ zend_string *message = zval_get_string(GET_PROPERTY(&exception, ZEND_STR_MESSAGE));
+ zend_string *file = zval_get_string(GET_PROPERTY_SILENT(&exception, ZEND_STR_FILE));
+ zend_long line = zval_get_long(GET_PROPERTY_SILENT(&exception, ZEND_STR_LINE));
zend_error_helper(E_PARSE, ZSTR_VAL(file), line, "%s", ZSTR_VAL(message));
@@ -1016,7 +985,7 @@ ZEND_API ZEND_COLD void zend_exception_error(zend_object *ex, int severity) /* {
if (Z_TYPE(tmp) != IS_STRING) {
zend_error(E_WARNING, "%s::__toString() must return a string", ZSTR_VAL(ce_exception->name));
} else {
- zend_update_property(i_get_exception_base(&exception), &exception, "string", sizeof("string")-1, &tmp);
+ zend_update_property_ex(i_get_exception_base(&exception), &exception, CG(known_strings)[ZEND_STR_STRING], &tmp);
}
}
zval_ptr_dtor(&tmp);
@@ -1027,8 +996,8 @@ ZEND_API ZEND_COLD void zend_exception_error(zend_object *ex, int severity) /* {
ZVAL_OBJ(&zv, EG(exception));
/* do the best we can to inform about the inner exception */
if (instanceof_function(ce_exception, zend_ce_exception) || instanceof_function(ce_exception, zend_ce_error)) {
- file = zval_get_string(GET_PROPERTY_SILENT(&zv, "file"));
- line = zval_get_long(GET_PROPERTY_SILENT(&zv, "line"));
+ file = zval_get_string(GET_PROPERTY_SILENT(&zv, ZEND_STR_FILE));
+ line = zval_get_long(GET_PROPERTY_SILENT(&zv, ZEND_STR_LINE));
}
zend_error_va(E_WARNING, (file && ZSTR_LEN(file) > 0) ? ZSTR_VAL(file) : NULL, line,
@@ -1040,9 +1009,9 @@ ZEND_API ZEND_COLD void zend_exception_error(zend_object *ex, int severity) /* {
}
}
- str = zval_get_string(GET_PROPERTY_SILENT(&exception, "string"));
- file = zval_get_string(GET_PROPERTY_SILENT(&exception, "file"));
- line = zval_get_long(GET_PROPERTY_SILENT(&exception, "line"));
+ str = zval_get_string(GET_PROPERTY_SILENT(&exception, ZEND_STR_STRING));
+ file = zval_get_string(GET_PROPERTY_SILENT(&exception, ZEND_STR_FILE));
+ line = zval_get_long(GET_PROPERTY_SILENT(&exception, ZEND_STR_LINE));
zend_error_va(severity, (file && ZSTR_LEN(file) > 0) ? ZSTR_VAL(file) : NULL, line,
"Uncaught %s\n thrown", ZSTR_VAL(str));
diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c
index cda98fd8a4..a41c9dd594 100644
--- a/Zend/zend_execute.c
+++ b/Zend/zend_execute.c
@@ -40,6 +40,7 @@
#include "zend_vm.h"
#include "zend_dtrace.h"
#include "zend_inheritance.h"
+#include "zend_type_info.h"
/* Virtual current working directory support */
#include "zend_virtual_cwd.h"
@@ -64,17 +65,17 @@ typedef int (ZEND_FASTCALL *incdec_t)(zval *);
#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 */
-static void zend_extension_statement_handler(const zend_extension *extension, zend_op_array *op_array);
-static void zend_extension_fcall_begin_handler(const zend_extension *extension, zend_op_array *op_array);
-static void zend_extension_fcall_end_handler(const zend_extension *extension, zend_op_array *op_array);
+static void zend_extension_statement_handler(const zend_extension *extension, zend_execute_data *frame);
+static void zend_extension_fcall_begin_handler(const zend_extension *extension, zend_execute_data *frame);
+static void zend_extension_fcall_end_handler(const zend_extension *extension, zend_execute_data *frame);
-#define RETURN_VALUE_USED(opline) (!((opline)->result_type & EXT_TYPE_UNUSED))
+#define RETURN_VALUE_USED(opline) ((opline)->result_type != IS_UNUSED)
static ZEND_FUNCTION(pass)
{
}
-static const zend_internal_function zend_pass_function = {
+ZEND_API const zend_internal_function zend_pass_function = {
ZEND_INTERNAL_FUNCTION, /* type */
{0, 0, 0}, /* arg_flags */
0, /* fn_flags */
@@ -85,7 +86,8 @@ static const zend_internal_function zend_pass_function = {
0, /* required_num_args */
NULL, /* arg_info */
ZEND_FN(pass), /* handler */
- NULL /* module */
+ NULL, /* module */
+ {NULL,NULL,NULL,NULL} /* reserved */
};
#undef zval_ptr_dtor
@@ -94,13 +96,10 @@ static const zend_internal_function zend_pass_function = {
#define READY_TO_DESTROY(zv) \
(UNEXPECTED(zv) && Z_REFCOUNTED_P(zv) && Z_REFCOUNT_P(zv) == 1)
-#define EXTRACT_ZVAL_PTR(zv, check_null) do { \
+#define EXTRACT_ZVAL_PTR(zv) do { \
zval *__zv = (zv); \
if (EXPECTED(Z_TYPE_P(__zv) == IS_INDIRECT)) { \
- if (!(check_null) || \
- EXPECTED(Z_INDIRECT_P(__zv))) { \
- ZVAL_COPY(__zv, Z_INDIRECT_P(__zv)); \
- } \
+ ZVAL_COPY(__zv, Z_INDIRECT_P(__zv)); \
} \
} while (0)
@@ -119,21 +118,8 @@ static const zend_internal_function zend_pass_function = {
zval_ptr_dtor_nogc(should_free); \
}
-/* End of zend_execute_locks.h */
-
#define CV_DEF_OF(i) (EX(func)->op_array.vars[i])
-#define CTOR_CALL_BIT 0x1
-#define CTOR_USED_BIT 0x2
-
-#define IS_CTOR_CALL(ce) (((zend_uintptr_t)(ce)) & CTOR_CALL_BIT)
-#define IS_CTOR_USED(ce) (((zend_uintptr_t)(ce)) & CTOR_USED_BIT)
-
-#define ENCODE_CTOR(ce, used) \
- ((zend_class_entry*)(((zend_uintptr_t)(ce)) | CTOR_CALL_BIT | ((used) ? CTOR_USED_BIT : 0)))
-#define DECODE_CTOR(ce) \
- ((zend_class_entry*)(((zend_uintptr_t)(ce)) & ~(CTOR_CALL_BIT|CTOR_USED_BIT)))
-
#define ZEND_VM_MAIN_STACK_PAGE_SLOTS (16 * 1024) /* should be a power of 2 */
#define ZEND_VM_GENERATOR_STACK_PAGE_SLOTS (256)
@@ -150,7 +136,7 @@ static const zend_internal_function zend_pass_function = {
static zend_always_inline zend_vm_stack zend_vm_stack_new_page(size_t size, zend_vm_stack prev) {
zend_vm_stack page = (zend_vm_stack)emalloc(size);
- page->top = ZEND_VM_STACK_ELEMETS(page);
+ page->top = ZEND_VM_STACK_ELEMENTS(page);
page->end = (zval*)((char*)page + size);
page->prev = prev;
return page;
@@ -400,6 +386,11 @@ static zend_always_inline zval *_get_zval_ptr_cv_undef_BP_VAR_RW(const zend_exec
return EX_VAR(var);
}
+static zend_always_inline zval *_get_zval_ptr_cv_undef_BP_VAR_UNSET(const zend_execute_data *execute_data, uint32_t var)
+{
+ return EX_VAR(var);
+}
+
static zend_always_inline zval *_get_zval_ptr_cv_deref_BP_VAR_W(const zend_execute_data *execute_data, uint32_t var)
{
zval *ret = EX_VAR(var);
@@ -627,7 +618,7 @@ static zend_always_inline zend_class_entry* zend_verify_arg_class_kind(const zen
return zend_fetch_class(cur_arg_info->class_name, (ZEND_FETCH_CLASS_AUTO | ZEND_FETCH_CLASS_NO_AUTOLOAD));
}
-static ZEND_COLD void zend_verify_arg_error(const zend_function *zf, uint32_t arg_num, const char *need_msg, const char *need_kind, const char *given_msg, const char *given_kind, zval *arg)
+static ZEND_COLD void zend_verify_arg_error(const zend_function *zf, uint32_t arg_num, const char *need_msg, const char *need_kind, const char *given_msg, const char *given_kind)
{
zend_execute_data *ptr = EG(current_execute_data)->prev_execute_data;
const char *fname = ZSTR_VAL(zf->common.function_name);
@@ -655,19 +646,19 @@ static ZEND_COLD void zend_verify_arg_error(const zend_function *zf, uint32_t ar
}
}
-static int is_null_constant(zval *default_value)
+static int is_null_constant(zend_class_entry *scope, zval *default_value)
{
if (Z_CONSTANT_P(default_value)) {
zval constant;
- ZVAL_COPY_VALUE(&constant, default_value);
- if (UNEXPECTED(zval_update_constant_ex(&constant, 0, NULL) != SUCCESS)) {
+ ZVAL_COPY(&constant, default_value);
+ if (UNEXPECTED(zval_update_constant_ex(&constant, scope) != SUCCESS)) {
return 0;
}
if (Z_TYPE(constant) == IS_NULL) {
return 1;
}
- zval_dtor(&constant);
+ zval_ptr_dtor(&constant);
}
return 0;
}
@@ -753,25 +744,25 @@ static int zend_verify_internal_arg_type(zend_function *zf, uint32_t arg_num, zv
if (cur_arg_info->class_name) {
need_msg = zend_verify_internal_arg_class_kind((zend_internal_arg_info*)cur_arg_info, &class_name, &ce);
if (!ce || !instanceof_function(Z_OBJCE_P(arg), ce)) {
- zend_verify_arg_error(zf, arg_num, need_msg, class_name, "instance of ", ZSTR_VAL(Z_OBJCE_P(arg)->name), arg);
+ zend_verify_arg_error(zf, arg_num, need_msg, class_name, "instance of ", ZSTR_VAL(Z_OBJCE_P(arg)->name));
return 0;
}
}
} else if (Z_TYPE_P(arg) != IS_NULL || !cur_arg_info->allow_null) {
if (cur_arg_info->class_name) {
need_msg = zend_verify_internal_arg_class_kind((zend_internal_arg_info*)cur_arg_info, &class_name, &ce);
- zend_verify_arg_error(zf, arg_num, need_msg, class_name, zend_zval_type_name(arg), "", arg);
+ zend_verify_arg_error(zf, arg_num, need_msg, class_name, zend_zval_type_name(arg), "");
return 0;
} else if (cur_arg_info->type_hint == IS_CALLABLE) {
if (!zend_is_callable(arg, IS_CALLABLE_CHECK_SILENT, NULL)) {
- zend_verify_arg_error(zf, arg_num, "be callable", "", zend_zval_type_name(arg), "", arg);
+ zend_verify_arg_error(zf, arg_num, "be callable", "", zend_zval_type_name(arg), "");
return 0;
}
} else if (cur_arg_info->type_hint == _IS_BOOL &&
EXPECTED(Z_TYPE_P(arg) == IS_FALSE || Z_TYPE_P(arg) == IS_TRUE)) {
/* pass */
} else if (UNEXPECTED(!zend_verify_scalar_type_hint(cur_arg_info->type_hint, arg, ZEND_CALL_USES_STRICT_TYPES(EG(current_execute_data))))) {
- zend_verify_arg_error(zf, arg_num, "be of the type ", zend_get_type_by_const(cur_arg_info->type_hint), zend_zval_type_name(arg), "", arg);
+ zend_verify_arg_error(zf, arg_num, "be of the type ", zend_get_type_by_const(cur_arg_info->type_hint), zend_zval_type_name(arg), "");
return 0;
}
}
@@ -779,6 +770,23 @@ static int zend_verify_internal_arg_type(zend_function *zf, uint32_t arg_num, zv
return 1;
}
+static zend_never_inline int zend_verify_internal_arg_types(zend_function *fbc, zend_execute_data *call)
+{
+ uint32_t i;
+ uint32_t num_args = ZEND_CALL_NUM_ARGS(call);
+ zval *p = ZEND_CALL_ARG(call, 1);
+
+ for (i = 0; i < num_args; ++i) {
+ if (UNEXPECTED(!zend_verify_internal_arg_type(fbc, i + 1, p))) {
+ EG(current_execute_data) = call->prev_execute_data;
+ zend_vm_stack_free_args(call);
+ return 0;
+ }
+ p++;
+ }
+ return 1;
+}
+
static zend_always_inline int zend_verify_arg_type(zend_function *zf, uint32_t arg_num, zval *arg, zval *default_value, void **cache_slot)
{
zend_arg_info *cur_arg_info;
@@ -802,7 +810,7 @@ static zend_always_inline int zend_verify_arg_type(zend_function *zf, uint32_t a
} else {
ce = zend_verify_arg_class_kind(cur_arg_info);
if (UNEXPECTED(!ce)) {
- zend_verify_arg_error(zf, arg_num, "be an instance of ", ZSTR_VAL(cur_arg_info->class_name), "instance of ", ZSTR_VAL(Z_OBJCE_P(arg)->name), arg);
+ zend_verify_arg_error(zf, arg_num, "be an instance of ", ZSTR_VAL(cur_arg_info->class_name), "instance of ", ZSTR_VAL(Z_OBJCE_P(arg)->name));
return 0;
}
*cache_slot = (void*)ce;
@@ -811,11 +819,11 @@ static zend_always_inline int zend_verify_arg_type(zend_function *zf, uint32_t a
need_msg =
(ce->ce_flags & ZEND_ACC_INTERFACE) ?
"implement interface " : "be an instance of ";
- zend_verify_arg_error(zf, arg_num, need_msg, ZSTR_VAL(ce->name), "instance of ", ZSTR_VAL(Z_OBJCE_P(arg)->name), arg);
+ zend_verify_arg_error(zf, arg_num, need_msg, ZSTR_VAL(ce->name), "instance of ", ZSTR_VAL(Z_OBJCE_P(arg)->name));
return 0;
}
}
- } else if (Z_TYPE_P(arg) != IS_NULL || !(cur_arg_info->allow_null || (default_value && is_null_constant(default_value)))) {
+ } else if (Z_TYPE_P(arg) != IS_NULL || !(cur_arg_info->allow_null || (default_value && is_null_constant(zf->common.scope, default_value)))) {
if (cur_arg_info->class_name) {
if (EXPECTED(*cache_slot)) {
ce = (zend_class_entry*)*cache_slot;
@@ -823,9 +831,9 @@ static zend_always_inline int zend_verify_arg_type(zend_function *zf, uint32_t a
ce = zend_verify_arg_class_kind(cur_arg_info);
if (UNEXPECTED(!ce)) {
if (Z_TYPE_P(arg) == IS_OBJECT) {
- zend_verify_arg_error(zf, arg_num, "be an instance of ", ZSTR_VAL(cur_arg_info->class_name), "instance of ", ZSTR_VAL(Z_OBJCE_P(arg)->name), arg);
+ zend_verify_arg_error(zf, arg_num, "be an instance of ", ZSTR_VAL(cur_arg_info->class_name), "instance of ", ZSTR_VAL(Z_OBJCE_P(arg)->name));
} else {
- zend_verify_arg_error(zf, arg_num, "be an instance of ", ZSTR_VAL(cur_arg_info->class_name), "", zend_zval_type_name(arg), arg);
+ zend_verify_arg_error(zf, arg_num, "be an instance of ", ZSTR_VAL(cur_arg_info->class_name), "", zend_zval_type_name(arg));
}
return 0;
}
@@ -834,18 +842,18 @@ static zend_always_inline int zend_verify_arg_type(zend_function *zf, uint32_t a
need_msg =
(ce->ce_flags & ZEND_ACC_INTERFACE) ?
"implement interface " : "be an instance of ";
- zend_verify_arg_error(zf, arg_num, need_msg, ZSTR_VAL(ce->name), zend_zval_type_name(arg), "", arg);
+ zend_verify_arg_error(zf, arg_num, need_msg, ZSTR_VAL(ce->name), zend_zval_type_name(arg), "");
return 0;
} else if (cur_arg_info->type_hint == IS_CALLABLE) {
if (!zend_is_callable(arg, IS_CALLABLE_CHECK_SILENT, NULL)) {
- zend_verify_arg_error(zf, arg_num, "be callable", "", zend_zval_type_name(arg), "", arg);
+ zend_verify_arg_error(zf, arg_num, "be callable", "", zend_zval_type_name(arg), "");
return 0;
}
} else if (cur_arg_info->type_hint == _IS_BOOL &&
EXPECTED(Z_TYPE_P(arg) == IS_FALSE || Z_TYPE_P(arg) == IS_TRUE)) {
/* pass */
} else if (UNEXPECTED(!zend_verify_scalar_type_hint(cur_arg_info->type_hint, arg, ZEND_ARG_USES_STRICT_TYPES()))) {
- zend_verify_arg_error(zf, arg_num, "be of the type ", zend_get_type_by_const(cur_arg_info->type_hint), zend_zval_type_name(arg), "", arg);
+ zend_verify_arg_error(zf, arg_num, "be of the type ", zend_get_type_by_const(cur_arg_info->type_hint), zend_zval_type_name(arg), "");
return 0;
}
}
@@ -853,60 +861,28 @@ static zend_always_inline int zend_verify_arg_type(zend_function *zf, uint32_t a
return 1;
}
-static zend_always_inline int zend_verify_missing_arg_type(zend_function *zf, uint32_t arg_num, void **cache_slot)
+ZEND_API ZEND_COLD void ZEND_FASTCALL zend_missing_arg_error(zend_execute_data *execute_data)
{
- zend_arg_info *cur_arg_info;
- char *need_msg;
- zend_class_entry *ce;
+ zend_execute_data *ptr = EX(prev_execute_data);
- if (EXPECTED(arg_num <= zf->common.num_args)) {
- cur_arg_info = &zf->common.arg_info[arg_num-1];
- } else if (UNEXPECTED(zf->common.fn_flags & ZEND_ACC_VARIADIC)) {
- cur_arg_info = &zf->common.arg_info[zf->common.num_args];
+ if (ptr && ptr->func && ZEND_USER_CODE(ptr->func->common.type)) {
+ zend_throw_error(NULL, "Too few arguments to function %s%s%s(), %d passed in %s on line %d and %s %d expected",
+ EX(func)->common.scope ? ZSTR_VAL(EX(func)->common.scope->name) : "",
+ EX(func)->common.scope ? "::" : "",
+ ZSTR_VAL(EX(func)->common.function_name),
+ EX_NUM_ARGS(),
+ ZSTR_VAL(ptr->func->op_array.filename),
+ ptr->opline->lineno,
+ EX(func)->common.required_num_args == EX(func)->common.num_args ? "exactly" : "at least",
+ EX(func)->common.required_num_args);
} else {
- return 1;
- }
-
- if (cur_arg_info->type_hint) {
- if (cur_arg_info->class_name) {
- if (EXPECTED(*cache_slot)) {
- ce = (zend_class_entry*)*cache_slot;
- } else {
- ce = zend_verify_arg_class_kind(cur_arg_info);
- if (UNEXPECTED(!ce)) {
- zend_verify_arg_error(zf, arg_num, "be an instance of ", ZSTR_VAL(cur_arg_info->class_name), "none", "", NULL);
- return 0;
- }
- *cache_slot = (void*)ce;
- }
- need_msg =
- (ce->ce_flags & ZEND_ACC_INTERFACE) ?
- "implement interface " : "be an instance of ";
- zend_verify_arg_error(zf, arg_num, need_msg, ZSTR_VAL(ce->name), "none", "", NULL);
- } else if (cur_arg_info->type_hint == IS_CALLABLE) {
- zend_verify_arg_error(zf, arg_num, "be callable", "", "none", "", NULL);
- } else {
- zend_verify_arg_error(zf, arg_num, "be of the type ", zend_get_type_by_const(cur_arg_info->type_hint), "none", "", NULL);
- }
- return 0;
- }
- return 1;
-}
-
-static ZEND_COLD void zend_verify_missing_arg(zend_execute_data *execute_data, uint32_t arg_num, void **cache_slot)
-{
- if (EXPECTED(!(EX(func)->common.fn_flags & ZEND_ACC_HAS_TYPE_HINTS)) ||
- UNEXPECTED(zend_verify_missing_arg_type(EX(func), arg_num, cache_slot))) {
- const char *class_name = EX(func)->common.scope ? ZSTR_VAL(EX(func)->common.scope->name) : "";
- const char *space = EX(func)->common.scope ? "::" : "";
- const char *func_name = EX(func)->common.function_name ? ZSTR_VAL(EX(func)->common.function_name) : "main";
- zend_execute_data *ptr = EX(prev_execute_data);
-
- if (ptr && ptr->func && ZEND_USER_CODE(ptr->func->common.type)) {
- zend_error(E_WARNING, "Missing argument %u for %s%s%s(), called in %s on line %d and defined", arg_num, class_name, space, func_name, ZSTR_VAL(ptr->func->op_array.filename), ptr->opline->lineno);
- } else {
- zend_error(E_WARNING, "Missing argument %u for %s%s%s()", arg_num, class_name, space, func_name);
- }
+ zend_throw_error(NULL, "Too few arguments to function %s%s%s(), %d passed and %s %d expected",
+ EX(func)->common.scope ? ZSTR_VAL(EX(func)->common.scope->name) : "",
+ EX(func)->common.scope ? "::" : "",
+ ZSTR_VAL(EX(func)->common.function_name),
+ EX_NUM_ARGS(),
+ EX(func)->common.required_num_args == EX(func)->common.num_args ? "exactly" : "at least",
+ EX(func)->common.required_num_args);
}
}
@@ -928,6 +904,7 @@ static ZEND_COLD void zend_verify_return_error(const zend_function *zf, const ch
fclass, fsep, fname, need_msg, need_kind, returned_msg, returned_kind);
}
+#if ZEND_DEBUG
static ZEND_COLD void zend_verify_internal_return_error(const zend_function *zf, const char *need_msg, const char *need_kind, const char *returned_msg, const char *returned_kind)
{
const char *fname = ZSTR_VAL(zf->common.function_name);
@@ -946,7 +923,24 @@ static ZEND_COLD void zend_verify_internal_return_error(const zend_function *zf,
fclass, fsep, fname, need_msg, need_kind, returned_msg, returned_kind);
}
-#if ZEND_DEBUG
+static ZEND_COLD void zend_verify_void_return_error(const zend_function *zf, const char *returned_msg, const char *returned_kind)
+{
+ const char *fname = ZSTR_VAL(zf->common.function_name);
+ const char *fsep;
+ const char *fclass;
+
+ if (zf->common.scope) {
+ fsep = "::";
+ fclass = ZSTR_VAL(zf->common.scope->name);
+ } else {
+ fsep = "";
+ fclass = "";
+ }
+
+ zend_type_error("%s%s%s() must not return a value, %s%s returned",
+ fclass, fsep, fname, returned_msg, returned_kind);
+}
+
static int zend_verify_internal_return_type(zend_function *zf, zval *ret)
{
zend_arg_info *ret_info = zf->common.arg_info - 1;
@@ -975,6 +969,8 @@ static int zend_verify_internal_return_type(zend_function *zf, zval *ret)
} else if (ret_info->type_hint == _IS_BOOL &&
EXPECTED(Z_TYPE_P(ret) == IS_FALSE || Z_TYPE_P(ret) == IS_TRUE)) {
/* pass */
+ } else if (ret_info->type_hint == IS_VOID) {
+ zend_verify_void_return_error(zf, zend_zval_type_name(ret), "");
} else {
/* Use strict check to verify return value of internal function */
zend_verify_internal_return_error(zf, "be of the type ", zend_get_type_by_const(ret_info->type_hint), zend_zval_type_name(ret), "");
@@ -1035,6 +1031,12 @@ static zend_always_inline void zend_verify_return_type(zend_function *zf, zval *
} else if (ret_info->type_hint == _IS_BOOL &&
EXPECTED(Z_TYPE_P(ret) == IS_FALSE || Z_TYPE_P(ret) == IS_TRUE)) {
/* pass */
+ /* There would be a check here for the IS_VOID type hint, which
+ * would trigger an error because a value had been returned.
+ * However, zend_compile.c already does a compile-time check
+ * that bans `return ...;` within a void function. Thus we can skip
+ * this part of the runtime check for non-internal functions.
+ */
} else if (UNEXPECTED(!zend_verify_scalar_type_hint(ret_info->type_hint, ret, ZEND_RET_USES_STRICT_TYPES()))) {
zend_verify_return_error(zf, "be of the type ", zend_get_type_by_const(ret_info->type_hint), zend_zval_type_name(ret), "");
}
@@ -1048,7 +1050,7 @@ static ZEND_COLD int zend_verify_missing_return_type(zend_function *zf, void **c
char *need_msg;
zend_class_entry *ce;
- if (ret_info->type_hint) {
+ if (ret_info->type_hint && EXPECTED(ret_info->type_hint != IS_VOID)) {
if (ret_info->class_name) {
if (EXPECTED(*cache_slot)) {
ce = (zend_class_entry*)*cache_slot;
@@ -1075,196 +1077,14 @@ static ZEND_COLD int zend_verify_missing_return_type(zend_function *zf, void **c
return 1;
}
-static zend_always_inline void zend_assign_to_object(zval *retval, zval *object, uint32_t object_op_type, zval *property_name, uint32_t property_op_type, int value_type, znode_op value_op, const zend_execute_data *execute_data, void **cache_slot)
+static zend_never_inline void zend_assign_to_object_dim(zval *object, zval *dim, zval *value)
{
- zend_free_op free_value;
- zval *value = get_zval_ptr_r(value_type, value_op, execute_data, &free_value);
- zval tmp;
-
- if (object_op_type != IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) != IS_OBJECT)) {
- do {
- if (object_op_type == IS_VAR && UNEXPECTED(object == &EG(error_zval))) {
- if (retval) {
- ZVAL_NULL(retval);
- }
- FREE_OP(free_value);
- return;
- }
- if (Z_ISREF_P(object)) {
- object = Z_REFVAL_P(object);
- if (EXPECTED(Z_TYPE_P(object) == IS_OBJECT)) {
- break;
- }
- }
- if (EXPECTED(Z_TYPE_P(object) <= IS_FALSE ||
- (Z_TYPE_P(object) == IS_STRING && Z_STRLEN_P(object) == 0))) {
- zend_object *obj;
-
- zval_ptr_dtor(object);
- object_init(object);
- Z_ADDREF_P(object);
- obj = Z_OBJ_P(object);
- zend_error(E_WARNING, "Creating default object from empty value");
- if (GC_REFCOUNT(obj) == 1) {
- /* the enclosing container was deleted, obj is unreferenced */
- if (retval) {
- ZVAL_NULL(retval);
- }
- FREE_OP(free_value);
- OBJ_RELEASE(obj);
- return;
- }
- Z_DELREF_P(object);
- } else {
- zend_error(E_WARNING, "Attempt to assign property of non-object");
- if (retval) {
- ZVAL_NULL(retval);
- }
- FREE_OP(free_value);
- return;
- }
- } while (0);
- }
-
- if (property_op_type == IS_CONST &&
- EXPECTED(Z_OBJCE_P(object) == CACHED_PTR_EX(cache_slot))) {
- uint32_t prop_offset = (uint32_t)(intptr_t)CACHED_PTR_EX(cache_slot + 1);
- zend_object *zobj = Z_OBJ_P(object);
- zval *property;
-
- if (EXPECTED(prop_offset != (uint32_t)ZEND_DYNAMIC_PROPERTY_OFFSET)) {
- property = OBJ_PROP(zobj, prop_offset);
- if (Z_TYPE_P(property) != IS_UNDEF) {
-fast_assign:
- value = zend_assign_to_variable(property, value, value_type);
- if (retval && EXPECTED(!EG(exception))) {
- ZVAL_COPY(retval, value);
- }
- return;
- }
- } else {
- if (EXPECTED(zobj->properties != NULL)) {
- if (UNEXPECTED(GC_REFCOUNT(zobj->properties) > 1)) {
- if (EXPECTED(!(GC_FLAGS(zobj->properties) & IS_ARRAY_IMMUTABLE))) {
- GC_REFCOUNT(zobj->properties)--;
- }
- zobj->properties = zend_array_dup(zobj->properties);
- }
- property = zend_hash_find(zobj->properties, Z_STR_P(property_name));
- if (property) {
- goto fast_assign;
- }
- }
-
- if (!zobj->ce->__set) {
-
- if (EXPECTED(zobj->properties == NULL)) {
- rebuild_object_properties(zobj);
- }
- /* separate our value if necessary */
- if (value_type == IS_CONST) {
- if (UNEXPECTED(Z_OPT_COPYABLE_P(value))) {
- ZVAL_COPY_VALUE(&tmp, value);
- zval_copy_ctor_func(&tmp);
- value = &tmp;
- }
- } else if (value_type != IS_TMP_VAR) {
- if (Z_ISREF_P(value)) {
- if (value_type == IS_VAR) {
- zend_reference *ref = Z_REF_P(value);
- if (--(GC_REFCOUNT(ref)) == 0) {
- ZVAL_COPY_VALUE(&tmp, Z_REFVAL_P(value));
- efree_size(ref, sizeof(zend_reference));
- value = &tmp;
- } else {
- value = Z_REFVAL_P(value);
- if (Z_REFCOUNTED_P(value)) {
- Z_ADDREF_P(value);
- }
- }
- } else {
- value = Z_REFVAL_P(value);
- if (Z_REFCOUNTED_P(value)) {
- Z_ADDREF_P(value);
- }
- }
- } else if (value_type == IS_CV && Z_REFCOUNTED_P(value)) {
- Z_ADDREF_P(value);
- }
- }
- zend_hash_add_new(zobj->properties, Z_STR_P(property_name), value);
- if (retval) {
- ZVAL_COPY(retval, value);
- }
- return;
- }
- }
- }
-
- if (!Z_OBJ_HT_P(object)->write_property) {
- zend_error(E_WARNING, "Attempt to assign property of non-object");
- if (retval) {
- ZVAL_NULL(retval);
- }
- FREE_OP(free_value);
- return;
- }
-
- /* separate our value if necessary */
- if (value_type == IS_CONST) {
- if (UNEXPECTED(Z_OPT_COPYABLE_P(value))) {
- ZVAL_COPY_VALUE(&tmp, value);
- zval_copy_ctor_func(&tmp);
- value = &tmp;
- }
- } else if (value_type != IS_TMP_VAR) {
- ZVAL_DEREF(value);
- }
-
- Z_OBJ_HT_P(object)->write_property(object, property_name, value, cache_slot);
-
- if (retval && EXPECTED(!EG(exception))) {
- ZVAL_COPY(retval, value);
- }
- if (value_type == IS_CONST) {
- zval_ptr_dtor_nogc(value);
- } else {
- FREE_OP(free_value);
- }
-}
-
-static zend_never_inline void zend_assign_to_object_dim(zval *retval, zval *object, zval *property_name, int value_type, znode_op value_op, const zend_execute_data *execute_data)
-{
- zend_free_op free_value;
- zval *value = get_zval_ptr_deref(value_type, value_op, execute_data, &free_value, BP_VAR_R);
- zval tmp;
-
- /* Note: property_name in this case is really the array index! */
- if (!Z_OBJ_HT_P(object)->write_dimension) {
+ if (UNEXPECTED(!Z_OBJ_HT_P(object)->write_dimension)) {
zend_throw_error(NULL, "Cannot use object as array");
- FREE_OP(free_value);
return;
}
- /* separate our value if necessary */
- if (value_type == IS_CONST) {
- if (UNEXPECTED(Z_OPT_COPYABLE_P(value))) {
- ZVAL_COPY_VALUE(&tmp, value);
- zval_copy_ctor_func(&tmp);
- value = &tmp;
- }
- }
-
- Z_OBJ_HT_P(object)->write_dimension(object, property_name, value);
-
- if (retval && EXPECTED(!EG(exception))) {
- ZVAL_COPY(retval, value);
- }
- if (value_type == IS_CONST) {
- zval_ptr_dtor_nogc(value);
- } else {
- FREE_OP(free_value);
- }
+ Z_OBJ_HT_P(object)->write_dimension(object, dim, value);
}
static zend_never_inline void zend_binary_assign_op_obj_dim(zval *object, zval *property, zval *value, zval *retval, binary_op_type binary_op)
@@ -1301,48 +1121,221 @@ static zend_never_inline void zend_binary_assign_op_obj_dim(zval *object, zval *
}
}
-static void zend_assign_to_string_offset(zval *str, zend_long offset, zval *value, zval *result)
+static zend_never_inline zend_long zend_check_string_offset(zval *dim, int type)
+{
+ zend_long offset;
+
+try_again:
+ if (UNEXPECTED(Z_TYPE_P(dim) != IS_LONG)) {
+ switch(Z_TYPE_P(dim)) {
+ case IS_STRING:
+ if (IS_LONG == is_numeric_string(Z_STRVAL_P(dim), Z_STRLEN_P(dim), NULL, NULL, -1)) {
+ break;
+ }
+ if (type != BP_VAR_UNSET) {
+ zend_error(E_WARNING, "Illegal string offset '%s'", Z_STRVAL_P(dim));
+ }
+ break;
+ case IS_UNDEF:
+ zval_undefined_cv(EG(current_execute_data)->opline->op2.var, EG(current_execute_data));
+ case IS_DOUBLE:
+ case IS_NULL:
+ case IS_FALSE:
+ case IS_TRUE:
+ zend_error(E_NOTICE, "String offset cast occurred");
+ break;
+ case IS_REFERENCE:
+ dim = Z_REFVAL_P(dim);
+ goto try_again;
+ default:
+ zend_error(E_WARNING, "Illegal offset type");
+ break;
+ }
+
+ offset = _zval_get_long_func(dim);
+ } else {
+ offset = Z_LVAL_P(dim);
+ }
+
+ return offset;
+}
+
+static zend_never_inline ZEND_COLD void zend_wrong_string_offset(void)
+{
+ const char *msg = NULL;
+ const zend_op *opline = EG(current_execute_data)->opline;
+ const zend_op *end;
+ uint32_t var;
+
+ switch (opline->opcode) {
+ case ZEND_ASSIGN_ADD:
+ case ZEND_ASSIGN_SUB:
+ case ZEND_ASSIGN_MUL:
+ case ZEND_ASSIGN_DIV:
+ case ZEND_ASSIGN_MOD:
+ case ZEND_ASSIGN_SL:
+ case ZEND_ASSIGN_SR:
+ case ZEND_ASSIGN_CONCAT:
+ case ZEND_ASSIGN_BW_OR:
+ case ZEND_ASSIGN_BW_AND:
+ case ZEND_ASSIGN_BW_XOR:
+ case ZEND_ASSIGN_POW:
+ msg = "Cannot use assign-op operators with string offsets";
+ break;
+ case ZEND_FETCH_DIM_W:
+ case ZEND_FETCH_DIM_RW:
+ case ZEND_FETCH_DIM_FUNC_ARG:
+ case ZEND_FETCH_DIM_UNSET:
+ /* TODO: Encode the "reason" into opline->extended_value??? */
+ var = opline->result.var;
+ opline++;
+ end = EG(current_execute_data)->func->op_array.opcodes +
+ EG(current_execute_data)->func->op_array.last;
+ while (opline < end) {
+ if (opline->op1_type == IS_VAR && opline->op1.var == var) {
+ switch (opline->opcode) {
+ case ZEND_ASSIGN_ADD:
+ case ZEND_ASSIGN_SUB:
+ case ZEND_ASSIGN_MUL:
+ case ZEND_ASSIGN_DIV:
+ case ZEND_ASSIGN_MOD:
+ case ZEND_ASSIGN_SL:
+ case ZEND_ASSIGN_SR:
+ case ZEND_ASSIGN_CONCAT:
+ case ZEND_ASSIGN_BW_OR:
+ case ZEND_ASSIGN_BW_AND:
+ case ZEND_ASSIGN_BW_XOR:
+ case ZEND_ASSIGN_POW:
+ if (opline->extended_value == ZEND_ASSIGN_OBJ) {
+ msg = "Cannot use string offset as an object";
+ } else if (opline->extended_value == ZEND_ASSIGN_DIM) {
+ msg = "Cannot use string offset as an array";
+ } else {
+ msg = "Cannot use assign-op operators with string offsets";
+ }
+ break;
+ case ZEND_PRE_INC_OBJ:
+ case ZEND_PRE_DEC_OBJ:
+ case ZEND_POST_INC_OBJ:
+ case ZEND_POST_DEC_OBJ:
+ case ZEND_PRE_INC:
+ case ZEND_PRE_DEC:
+ case ZEND_POST_INC:
+ case ZEND_POST_DEC:
+ msg = "Cannot increment/decrement string offsets";
+ break;
+ case ZEND_FETCH_DIM_W:
+ case ZEND_FETCH_DIM_RW:
+ case ZEND_FETCH_DIM_FUNC_ARG:
+ case ZEND_FETCH_DIM_UNSET:
+ case ZEND_ASSIGN_DIM:
+ msg = "Cannot use string offset as an array";
+ break;
+ case ZEND_FETCH_OBJ_W:
+ case ZEND_FETCH_OBJ_RW:
+ case ZEND_FETCH_OBJ_FUNC_ARG:
+ case ZEND_FETCH_OBJ_UNSET:
+ case ZEND_ASSIGN_OBJ:
+ msg = "Cannot use string offset as an object";
+ break;
+ case ZEND_ASSIGN_REF:
+ case ZEND_ADD_ARRAY_ELEMENT:
+ case ZEND_INIT_ARRAY:
+ msg = "Cannot create references to/from string offsets";
+ break;
+ case ZEND_RETURN_BY_REF:
+ msg = "Cannot return string offsets by reference";
+ break;
+ case ZEND_UNSET_DIM:
+ case ZEND_UNSET_OBJ:
+ msg = "Cannot unset string offsets";
+ break;
+ case ZEND_YIELD:
+ msg = "Cannot yield string offsets by reference";
+ break;
+ case ZEND_SEND_REF:
+ case ZEND_SEND_VAR_EX:
+ msg = "Only variables can be passed by reference";
+ break;
+ EMPTY_SWITCH_DEFAULT_CASE();
+ }
+ break;
+ }
+ if (opline->op2_type == IS_VAR && opline->op2.var == var) {
+ ZEND_ASSERT(opline->opcode == ZEND_ASSIGN_REF);
+ msg = "Cannot create references to/from string offsets";
+ break;
+ }
+ }
+ break;
+ EMPTY_SWITCH_DEFAULT_CASE();
+ }
+ ZEND_ASSERT(msg != NULL);
+ zend_throw_error(NULL, msg);
+}
+
+static zend_never_inline void zend_assign_to_string_offset(zval *str, zval *dim, zval *value, zval *result)
{
zend_string *old_str;
+ zend_uchar c;
+ size_t string_len;
+ zend_long offset;
- if (offset < 0) {
+ offset = zend_check_string_offset(dim, BP_VAR_W);
+ if (offset < (zend_long)(-Z_STRLEN_P(str))) {
+ /* Error on negative offset */
zend_error(E_WARNING, "Illegal string offset: " ZEND_LONG_FMT, offset);
- zend_string_release(Z_STR_P(str));
if (result) {
ZVAL_NULL(result);
}
return;
}
- old_str = Z_STR_P(str);
+ if (Z_TYPE_P(value) != IS_STRING) {
+ /* Convert to string, just the time to pick the 1st byte */
+ zend_string *tmp = zval_get_string(value);
+
+ string_len = ZSTR_LEN(tmp);
+ c = (zend_uchar)ZSTR_VAL(tmp)[0];
+ zend_string_release(tmp);
+ } else {
+ string_len = Z_STRLEN_P(value);
+ c = (zend_uchar)Z_STRVAL_P(value)[0];
+ }
+
+ if (string_len == 0) {
+ /* Error on empty input string */
+ zend_error(E_WARNING, "Cannot assign an empty string to a string offset");
+ if (result) {
+ ZVAL_NULL(result);
+ }
+ return;
+ }
+
+ if (offset < 0) { /* Handle negative offset */
+ offset += (zend_long)Z_STRLEN_P(str);
+ }
+
if ((size_t)offset >= Z_STRLEN_P(str)) {
+ /* Extend string if needed */
zend_long old_len = Z_STRLEN_P(str);
Z_STR_P(str) = zend_string_extend(Z_STR_P(str), offset + 1, 0);
Z_TYPE_INFO_P(str) = IS_STRING_EX;
memset(Z_STRVAL_P(str) + old_len, ' ', offset - old_len);
Z_STRVAL_P(str)[offset+1] = 0;
} else if (!Z_REFCOUNTED_P(str)) {
+ old_str = Z_STR_P(str);
Z_STR_P(str) = zend_string_init(Z_STRVAL_P(str), Z_STRLEN_P(str), 0);
Z_TYPE_INFO_P(str) = IS_STRING_EX;
- }
-
- if (Z_TYPE_P(value) != IS_STRING) {
- zend_string *tmp = zval_get_string(value);
-
- Z_STRVAL_P(str)[offset] = ZSTR_VAL(tmp)[0];
- zend_string_release(tmp);
+ zend_string_release(old_str);
} else {
- Z_STRVAL_P(str)[offset] = Z_STRVAL_P(value)[0];
+ SEPARATE_STRING(str);
}
- /*
- * the value of an assignment to a string offset is undefined
- T(result->u.var).var = &T->str_offset.str;
- */
- zend_string_release(old_str);
- if (result) {
- zend_uchar c = (zend_uchar)Z_STRVAL_P(str)[offset];
+ Z_STRVAL_P(str)[offset] = c;
+ if (result) {
+ /* Return the new character */
if (CG(one_char_string)[c]) {
ZVAL_INTERNED_STR(result, CG(one_char_string)[c]);
} else {
@@ -1483,26 +1476,26 @@ static zend_never_inline void zend_assign_op_overloaded_property(zval *object, z
}
/* Utility Functions for Extensions */
-static void zend_extension_statement_handler(const zend_extension *extension, zend_op_array *op_array)
+static void zend_extension_statement_handler(const zend_extension *extension, zend_execute_data *frame)
{
if (extension->statement_handler) {
- extension->statement_handler(op_array);
+ extension->statement_handler(frame);
}
}
-static void zend_extension_fcall_begin_handler(const zend_extension *extension, zend_op_array *op_array)
+static void zend_extension_fcall_begin_handler(const zend_extension *extension, zend_execute_data *frame)
{
if (extension->fcall_begin_handler) {
- extension->fcall_begin_handler(op_array);
+ extension->fcall_begin_handler(frame);
}
}
-static void zend_extension_fcall_end_handler(const zend_extension *extension, zend_op_array *op_array)
+static void zend_extension_fcall_end_handler(const zend_extension *extension, zend_execute_data *frame)
{
if (extension->fcall_end_handler) {
- extension->fcall_end_handler(op_array);
+ extension->fcall_end_handler(frame);
}
}
@@ -1514,18 +1507,9 @@ static zend_always_inline HashTable *zend_get_target_symbol_table(zend_execute_d
if (EXPECTED(fetch_type == ZEND_FETCH_GLOBAL_LOCK) ||
EXPECTED(fetch_type == ZEND_FETCH_GLOBAL)) {
ht = &EG(symbol_table);
- } else if (EXPECTED(fetch_type == ZEND_FETCH_STATIC)) {
- ZEND_ASSERT(EX(func)->op_array.static_variables != NULL);
- ht = EX(func)->op_array.static_variables;
- if (GC_REFCOUNT(ht) > 1) {
- if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE)) {
- GC_REFCOUNT(ht)--;
- }
- EX(func)->op_array.static_variables = ht = zend_array_dup(ht);
- }
} else {
ZEND_ASSERT(fetch_type == ZEND_FETCH_LOCAL);
- if (!EX(symbol_table)) {
+ if (!(EX_CALL_INFO() & ZEND_CALL_HAS_SYMBOL_TABLE)) {
zend_rebuild_symbol_table();
}
ht = EX(symbol_table);
@@ -1543,24 +1527,24 @@ try_again:
if (EXPECTED(Z_TYPE_P(dim) == IS_LONG)) {
hval = Z_LVAL_P(dim);
num_index:
- retval = zend_hash_index_find(ht, hval);
- if (retval == NULL) {
- switch (type) {
- case BP_VAR_R:
- 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_LONG_FMT, hval);
- retval = zend_hash_index_update(ht, hval, &EG(uninitialized_zval));
- break;
- case BP_VAR_W:
- retval = zend_hash_index_add_new(ht, hval, &EG(uninitialized_zval));
- break;
- }
+ ZEND_HASH_INDEX_FIND(ht, hval, retval, num_undef);
+ return retval;
+num_undef:
+ switch (type) {
+ case BP_VAR_R:
+ 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_LONG_FMT, hval);
+ retval = zend_hash_index_update(ht, hval, &EG(uninitialized_zval));
+ break;
+ case BP_VAR_W:
+ retval = zend_hash_index_add_new(ht, hval, &EG(uninitialized_zval));
+ break;
}
} else if (EXPECTED(Z_TYPE_P(dim) == IS_STRING)) {
offset_key = Z_STR_P(dim);
@@ -1613,6 +1597,9 @@ str_index:
}
} else {
switch (Z_TYPE_P(dim)) {
+ case IS_UNDEF:
+ zval_undefined_cv(EG(current_execute_data)->opline->op2.var, EG(current_execute_data));
+ /* break missing intentionally */
case IS_NULL:
offset_key = ZSTR_EMPTY_ALLOC();
goto str_index;
@@ -1635,61 +1622,30 @@ str_index:
default:
zend_error(E_WARNING, "Illegal offset type");
retval = (type == BP_VAR_W || type == BP_VAR_RW) ?
- &EG(error_zval) : &EG(uninitialized_zval);
+ NULL : &EG(uninitialized_zval);
}
}
return retval;
}
-static zend_never_inline zend_long zend_check_string_offset(zval *dim, int type)
+static zend_never_inline zval* ZEND_FASTCALL zend_fetch_dimension_address_inner_W(HashTable *ht, const zval *dim)
{
- zend_long offset;
-
-try_again:
- if (UNEXPECTED(Z_TYPE_P(dim) != IS_LONG)) {
- switch(Z_TYPE_P(dim)) {
- case IS_STRING:
- if (IS_LONG == is_numeric_string(Z_STRVAL_P(dim), Z_STRLEN_P(dim), NULL, NULL, -1)) {
- break;
- }
- if (type != BP_VAR_UNSET) {
- zend_error(E_WARNING, "Illegal string offset '%s'", Z_STRVAL_P(dim));
- }
- break;
- case IS_DOUBLE:
- case IS_NULL:
- case IS_FALSE:
- case IS_TRUE:
- zend_error(E_NOTICE, "String offset cast occurred");
- break;
- case IS_REFERENCE:
- dim = Z_REFVAL_P(dim);
- goto try_again;
- default:
- zend_error(E_WARNING, "Illegal offset type");
- break;
- }
-
- offset = zval_get_long(dim);
- } else {
- offset = Z_LVAL_P(dim);
- }
+ return zend_fetch_dimension_address_inner(ht, dim, IS_TMP_VAR, BP_VAR_W);
+}
- return offset;
+static zend_never_inline zval* ZEND_FASTCALL zend_fetch_dimension_address_inner_W_CONST(HashTable *ht, const zval *dim)
+{
+ return zend_fetch_dimension_address_inner(ht, dim, IS_CONST, BP_VAR_W);
}
-static zend_always_inline zend_long zend_fetch_string_offset(zval *container, zval *dim, int type)
+static zend_never_inline zval* ZEND_FASTCALL zend_fetch_dimension_address_inner_RW(HashTable *ht, const zval *dim)
{
- zend_long offset = zend_check_string_offset(dim, type);
+ return zend_fetch_dimension_address_inner(ht, dim, IS_TMP_VAR, BP_VAR_RW);
+}
- if (Z_REFCOUNTED_P(container)) {
- if (Z_REFCOUNT_P(container) > 1) {
- Z_DELREF_P(container);
- zval_copy_ctor_func(container);
- }
- Z_ADDREF_P(container);
- }
- return offset;
+static zend_never_inline zval* ZEND_FASTCALL zend_fetch_dimension_address_inner_RW_CONST(HashTable *ht, const zval *dim)
+{
+ return zend_fetch_dimension_address_inner(ht, dim, IS_CONST, BP_VAR_RW);
}
static zend_always_inline void zend_fetch_dimension_address(zval *result, zval *container, zval *dim, int dim_type, int type)
@@ -1704,10 +1660,15 @@ fetch_from_array:
retval = zend_hash_next_index_insert(Z_ARRVAL_P(container), &EG(uninitialized_zval));
if (UNEXPECTED(retval == NULL)) {
zend_error(E_WARNING, "Cannot add element to the array as the next element is already occupied");
- retval = &EG(error_zval);
+ ZVAL_ERROR(result);
+ return;
}
} else {
retval = zend_fetch_dimension_address_inner(Z_ARRVAL_P(container), dim, dim_type, type);
+ if (UNEXPECTED(!retval)) {
+ ZVAL_ERROR(result);
+ return;
+ }
}
ZVAL_INDIRECT(result, retval);
return;
@@ -1717,26 +1678,22 @@ fetch_from_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:
- ZVAL_NEW_ARR(container);
- zend_hash_init(Z_ARRVAL_P(container), 8, NULL, ZVAL_PTR_DTOR, 0);
- goto fetch_from_array;
- }
-
+ if (UNEXPECTED(Z_TYPE_P(container) == IS_STRING)) {
if (dim == NULL) {
zend_throw_error(NULL, "[] operator not supported for strings");
- ZVAL_INDIRECT(result, &EG(error_zval));
} else {
zend_check_string_offset(dim, type);
- ZVAL_INDIRECT(result, NULL); /* wrong string offset */
+ zend_wrong_string_offset();
}
+ ZVAL_ERROR(result);
} else if (EXPECTED(Z_TYPE_P(container) == IS_OBJECT)) {
+ if (/*dim_type == IS_CV &&*/ dim && UNEXPECTED(Z_TYPE_P(dim) == IS_UNDEF)) {
+ zval_undefined_cv(EG(current_execute_data)->opline->op2.var, EG(current_execute_data));
+ dim = &EG(uninitialized_zval);
+ }
if (!Z_OBJ_HT_P(container)->read_dimension) {
zend_throw_error(NULL, "Cannot use object as array");
- retval = &EG(error_zval);
+ ZVAL_ERROR(result);
} else {
retval = Z_OBJ_HT_P(container)->read_dimension(container, dim, type, result);
@@ -1769,25 +1726,35 @@ convert_to_array:
ZVAL_INDIRECT(result, retval);
}
} else {
- ZVAL_INDIRECT(result, &EG(error_zval));
+ ZVAL_ERROR(result);
}
}
- } else if (EXPECTED(Z_TYPE_P(container) <= IS_FALSE)) {
- if (UNEXPECTED(container == &EG(error_zval))) {
- ZVAL_INDIRECT(result, &EG(error_zval));
- } else if (type != BP_VAR_UNSET) {
- goto convert_to_array;
- } else {
- /* for read-mode only */
- ZVAL_NULL(result);
- }
} else {
- if (type == BP_VAR_UNSET) {
- zend_error(E_WARNING, "Cannot unset offset in a non-array variable");
- ZVAL_NULL(result);
+ if (type != BP_VAR_W && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) {
+ zval_undefined_cv(EG(current_execute_data)->opline->op1.var, EG(current_execute_data));
+ }
+ if (/*dim_type == IS_CV &&*/ dim && UNEXPECTED(Z_TYPE_P(dim) == IS_UNDEF)) {
+ zval_undefined_cv(EG(current_execute_data)->opline->op2.var, EG(current_execute_data));
+ }
+ if (EXPECTED(Z_TYPE_P(container) <= IS_FALSE)) {
+ if (type != BP_VAR_UNSET) {
+ ZVAL_NEW_ARR(container);
+ zend_hash_init(Z_ARRVAL_P(container), 8, NULL, ZVAL_PTR_DTOR, 0);
+ goto fetch_from_array;
+ } else {
+ /* for read-mode only */
+ ZVAL_NULL(result);
+ }
+ } else if (EXPECTED(Z_ISERROR_P(container))) {
+ ZVAL_ERROR(result);
} else {
- zend_error(E_WARNING, "Cannot use a scalar value as an array");
- ZVAL_INDIRECT(result, &EG(error_zval));
+ if (type == BP_VAR_UNSET) {
+ zend_error(E_WARNING, "Cannot unset offset in a non-array variable");
+ ZVAL_NULL(result);
+ } else {
+ zend_error(E_WARNING, "Cannot use a scalar value as an array");
+ ZVAL_ERROR(result);
+ }
}
}
}
@@ -1807,22 +1774,24 @@ static zend_never_inline void zend_fetch_dimension_address_UNSET(zval *result, z
zend_fetch_dimension_address(result, container_ptr, dim, dim_type, BP_VAR_UNSET);
}
-static zend_always_inline void zend_fetch_dimension_address_read(zval *result, zval *container, zval *dim, int dim_type, int type)
+static zend_always_inline void zend_fetch_dimension_address_read(zval *result, zval *container, zval *dim, int dim_type, int type, int support_strings, int slow)
{
zval *retval;
- 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);
- return;
- } else if (EXPECTED(Z_TYPE_P(container) == IS_REFERENCE)) {
- container = Z_REFVAL_P(container);
+ if (!slow) {
if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) {
- goto try_array;
+try_array:
+ retval = zend_fetch_dimension_address_inner(Z_ARRVAL_P(container), dim, dim_type, type);
+ ZVAL_COPY(result, retval);
+ 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 (support_strings && EXPECTED(Z_TYPE_P(container) == IS_STRING)) {
zend_long offset;
try_string_offset:
@@ -1839,6 +1808,8 @@ try_string_offset:
}
zend_error(E_WARNING, "Illegal string offset '%s'", Z_STRVAL_P(dim));
break;
+ case IS_UNDEF:
+ zval_undefined_cv(EG(current_execute_data)->opline->op2.var, EG(current_execute_data));
case IS_DOUBLE:
case IS_NULL:
case IS_FALSE:
@@ -1855,12 +1826,12 @@ try_string_offset:
break;
}
- offset = zval_get_long(dim);
+ offset = _zval_get_long_func(dim);
} else {
offset = Z_LVAL_P(dim);
}
- if (UNEXPECTED(offset < 0) || UNEXPECTED(Z_STRLEN_P(container) <= (size_t)offset)) {
+ if (UNEXPECTED(Z_STRLEN_P(container) < (size_t)((offset < 0) ? -offset : (offset + 1)))) {
if (type != BP_VAR_IS) {
zend_error(E_NOTICE, "Uninitialized string offset: %pd", offset);
ZVAL_EMPTY_STRING(result);
@@ -1868,15 +1839,24 @@ try_string_offset:
ZVAL_NULL(result);
}
} else {
- zend_uchar c = (zend_uchar)Z_STRVAL_P(container)[offset];
+ zend_uchar c;
+ zend_long real_offset;
+
+ real_offset = (UNEXPECTED(offset < 0)) /* Handle negative offset */
+ ? (zend_long)Z_STRLEN_P(container) + offset : offset;
+ c = (zend_uchar)Z_STRVAL_P(container)[real_offset];
if (CG(one_char_string)[c]) {
ZVAL_INTERNED_STR(result, CG(one_char_string)[c]);
} else {
- ZVAL_NEW_STR(result, zend_string_init(Z_STRVAL_P(container) + offset, 1, 0));
+ ZVAL_NEW_STR(result, zend_string_init(Z_STRVAL_P(container) + real_offset, 1, 0));
}
}
} else if (EXPECTED(Z_TYPE_P(container) == IS_OBJECT)) {
+ if (/*dim_type == IS_CV &&*/ UNEXPECTED(Z_TYPE_P(dim) == IS_UNDEF)) {
+ zval_undefined_cv(EG(current_execute_data)->opline->op2.var, EG(current_execute_data));
+ dim = &EG(uninitialized_zval);
+ }
if (!Z_OBJ_HT_P(container)->read_dimension) {
zend_throw_error(NULL, "Cannot use object as array");
ZVAL_NULL(result);
@@ -1893,18 +1873,34 @@ try_string_offset:
}
}
} else {
+ if (type != BP_VAR_IS && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) {
+ zval_undefined_cv(EG(current_execute_data)->opline->op1.var, EG(current_execute_data));
+ }
+ if (/*dim_type == IS_CV &&*/ UNEXPECTED(Z_TYPE_P(dim) == IS_UNDEF)) {
+ zval_undefined_cv(EG(current_execute_data)->opline->op2.var, EG(current_execute_data));
+ }
ZVAL_NULL(result);
}
}
static zend_never_inline void zend_fetch_dimension_address_read_R(zval *result, zval *container, zval *dim, int dim_type)
{
- zend_fetch_dimension_address_read(result, container, dim, dim_type, BP_VAR_R);
+ zend_fetch_dimension_address_read(result, container, dim, dim_type, BP_VAR_R, 1, 0);
+}
+
+static zend_never_inline void zend_fetch_dimension_address_read_R_slow(zval *result, zval *container, zval *dim)
+{
+ zend_fetch_dimension_address_read(result, container, dim, IS_CV, BP_VAR_R, 1, 1);
}
static zend_never_inline void zend_fetch_dimension_address_read_IS(zval *result, zval *container, zval *dim, int dim_type)
{
- zend_fetch_dimension_address_read(result, container, dim, dim_type, BP_VAR_IS);
+ zend_fetch_dimension_address_read(result, container, dim, dim_type, BP_VAR_IS, 1, 0);
+}
+
+static zend_never_inline void zend_fetch_dimension_address_read_LIST(zval *result, zval *container, zval *dim)
+{
+ zend_fetch_dimension_address_read(result, container, dim, IS_TMP_VAR, BP_VAR_R, 0, 0);
}
ZEND_API void zend_fetch_dimension_by_zval(zval *result, zval *container, zval *dim)
@@ -1914,7 +1910,7 @@ ZEND_API void zend_fetch_dimension_by_zval(zval *result, zval *container, zval *
ZEND_API void zend_fetch_dimension_by_zval_is(zval *result, zval *container, zval *dim, int dim_type)
{
- zend_fetch_dimension_address_read(result, container, dim, dim_type, BP_VAR_IS);
+ zend_fetch_dimension_address_read(result, container, dim, dim_type, BP_VAR_IS, 1, 0);
}
@@ -1922,11 +1918,6 @@ static zend_always_inline void zend_fetch_property_address(zval *result, zval *c
{
if (container_op_type != IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT)) {
do {
- if (container_op_type == IS_VAR && UNEXPECTED(container == &EG(error_zval))) {
- ZVAL_INDIRECT(result, &EG(error_zval));
- return;
- }
-
if (Z_ISREF_P(container)) {
container = Z_REFVAL_P(container);
if (EXPECTED(Z_TYPE_P(container) == IS_OBJECT)) {
@@ -1941,8 +1932,10 @@ static zend_always_inline void zend_fetch_property_address(zval *result, zval *c
zval_ptr_dtor_nogc(container);
object_init(container);
} else {
- zend_error(E_WARNING, "Attempt to modify property of non-object");
- ZVAL_INDIRECT(result, &EG(error_zval));
+ if (container_op_type != IS_VAR || EXPECTED(!Z_ISERROR_P(container))) {
+ zend_error(E_WARNING, "Attempt to modify property of non-object");
+ }
+ ZVAL_ERROR(result);
return;
}
} while (0);
@@ -1977,6 +1970,7 @@ static zend_always_inline void zend_fetch_property_address(zval *result, zval *c
zval *ptr = Z_OBJ_HT_P(container)->get_property_ptr_ptr(container, prop_ptr, type, cache_slot);
if (NULL == ptr) {
if (EXPECTED(Z_OBJ_HT_P(container)->read_property)) {
+use_read_property:
ptr = Z_OBJ_HT_P(container)->read_property(container, prop_ptr, type, cache_slot, result);
if (ptr != result) {
ZVAL_INDIRECT(result, ptr);
@@ -1985,21 +1979,16 @@ static zend_always_inline void zend_fetch_property_address(zval *result, zval *c
}
} else {
zend_throw_error(NULL, "Cannot access undefined property for object with overloaded property access");
- ZVAL_INDIRECT(result, &EG(error_zval));
+ ZVAL_ERROR(result);
}
} else {
ZVAL_INDIRECT(result, ptr);
}
} else if (EXPECTED(Z_OBJ_HT_P(container)->read_property)) {
- zval *ptr = Z_OBJ_HT_P(container)->read_property(container, prop_ptr, type, cache_slot, result);
- if (ptr != result) {
- ZVAL_INDIRECT(result, ptr);
- } else if (UNEXPECTED(Z_ISREF_P(ptr) && Z_REFCOUNT_P(ptr) == 1)) {
- ZVAL_UNREF(ptr);
- }
+ goto use_read_property;
} else {
zend_error(E_WARNING, "This object doesn't support property references");
- ZVAL_INDIRECT(result, &EG(error_zval));
+ ZVAL_ERROR(result);
}
}
@@ -2064,7 +2053,7 @@ static zend_always_inline void i_free_compiled_variables(zend_execute_data *exec
if (!Z_DELREF_P(cv)) {
zend_refcounted *r = Z_COUNTED_P(cv);
ZVAL_NULL(cv);
- zval_dtor_func_for_ptr(r);
+ zval_dtor_func(r);
} else {
GC_ZVAL_CHECK_POSSIBLE_ROOT(cv);
}
@@ -2080,16 +2069,17 @@ void zend_free_compiled_variables(zend_execute_data *execute_data) /* {{{ */
}
/* }}} */
-#ifdef ZEND_WIN32
-# define ZEND_VM_INTERRUPT_CHECK() do { \
- if (EG(timed_out)) { \
- zend_timeout(0); \
+static zend_never_inline ZEND_COLD ZEND_NORETURN void ZEND_FASTCALL zend_interrupt(void) /* {{{ */
+{
+ zend_timeout(0);
+}
+/* }}} */
+
+#define ZEND_VM_INTERRUPT_CHECK() do { \
+ if (UNEXPECTED(EG(timed_out))) { \
+ zend_interrupt(); \
} \
} while (0)
-#else
-# define ZEND_VM_INTERRUPT_CHECK() do { \
- } while (0)
-#endif
/*
* Stack Frame Layout (the whole stack frame is allocated at once)
@@ -2111,7 +2101,7 @@ void zend_free_compiled_variables(zend_execute_data *execute_data) /* {{{ */
* +----------------------------------------+
*/
-static zend_always_inline void i_init_func_execute_data(zend_execute_data *execute_data, zend_op_array *op_array, zval *return_value, int check_this) /* {{{ */
+static zend_always_inline void i_init_func_execute_data(zend_execute_data *execute_data, zend_op_array *op_array, zval *return_value) /* {{{ */
{
uint32_t first_extra_arg, num_args;
ZEND_ASSERT(EX(func) == (zend_function*)op_array);
@@ -2169,20 +2159,18 @@ static zend_always_inline void i_init_func_execute_data(zend_execute_data *execu
} while (var != end);
}
- if (check_this && op_array->this_var != (uint32_t)-1 && EXPECTED(Z_OBJ(EX(This)))) {
- ZVAL_OBJ(EX_VAR(op_array->this_var), Z_OBJ(EX(This)));
- GC_REFCOUNT(Z_OBJ(EX(This)))++;
- }
-
- if (UNEXPECTED(!op_array->run_time_cache)) {
- op_array->run_time_cache = zend_arena_alloc(&CG(arena), op_array->cache_size);
- memset(op_array->run_time_cache, 0, op_array->cache_size);
- }
EX_LOAD_RUN_TIME_CACHE(op_array);
EX_LOAD_LITERALS(op_array);
EG(current_execute_data) = execute_data;
- ZEND_VM_INTERRUPT_CHECK();
+}
+/* }}} */
+
+static zend_never_inline void ZEND_FASTCALL init_func_run_time_cache(zend_op_array *op_array) /* {{{ */
+{
+ ZEND_ASSERT(op_array->run_time_cache == NULL);
+ op_array->run_time_cache = zend_arena_alloc(&CG(arena), op_array->cache_size);
+ memset(op_array->run_time_cache, 0, op_array->cache_size);
}
/* }}} */
@@ -2194,13 +2182,6 @@ static zend_always_inline void i_init_code_execute_data(zend_execute_data *execu
EX(call) = NULL;
EX(return_value) = return_value;
- if (UNEXPECTED(op_array->this_var != (uint32_t)-1) && EXPECTED(Z_OBJ(EX(This)))) {
- GC_REFCOUNT(Z_OBJ(EX(This)))++;
- if (!zend_hash_str_add(EX(symbol_table), "this", sizeof("this")-1, &EX(This))) {
- GC_REFCOUNT(Z_OBJ(EX(This)))--;
- }
- }
-
zend_attach_symbol_table(execute_data);
if (!op_array->run_time_cache) {
@@ -2211,7 +2192,6 @@ static zend_always_inline void i_init_code_execute_data(zend_execute_data *execu
EX_LOAD_LITERALS(op_array);
EG(current_execute_data) = execute_data;
- ZEND_VM_INTERRUPT_CHECK();
}
/* }}} */
@@ -2223,14 +2203,7 @@ static zend_always_inline void i_init_execute_data(zend_execute_data *execute_da
EX(call) = NULL;
EX(return_value) = return_value;
- if (UNEXPECTED(EX(symbol_table) != NULL)) {
- if (UNEXPECTED(op_array->this_var != (uint32_t)-1) && EXPECTED(Z_OBJ(EX(This)))) {
- GC_REFCOUNT(Z_OBJ(EX(This)))++;
- if (!zend_hash_str_add(EX(symbol_table), "this", sizeof("this")-1, &EX(This))) {
- GC_REFCOUNT(Z_OBJ(EX(This)))--;
- }
- }
-
+ if (EX_CALL_INFO() & ZEND_CALL_HAS_SYMBOL_TABLE) {
zend_attach_symbol_table(execute_data);
} else {
uint32_t first_extra_arg, num_args;
@@ -2283,11 +2256,6 @@ static zend_always_inline void i_init_execute_data(zend_execute_data *execute_da
var++;
} while (var != end);
}
-
- if (op_array->this_var != (uint32_t)-1 && EXPECTED(Z_OBJ(EX(This)))) {
- ZVAL_OBJ(EX_VAR(op_array->this_var), Z_OBJ(EX(This)));
- GC_REFCOUNT(Z_OBJ(EX(This)))++;
- }
}
if (!op_array->run_time_cache) {
@@ -2302,65 +2270,6 @@ static zend_always_inline void i_init_execute_data(zend_execute_data *execute_da
EX_LOAD_LITERALS(op_array);
EG(current_execute_data) = execute_data;
- ZEND_VM_INTERRUPT_CHECK();
-}
-/* }}} */
-
-ZEND_API zend_execute_data *zend_create_generator_execute_data(zend_execute_data *call, zend_op_array *op_array, zval *return_value) /* {{{ */
-{
- /*
- * Normally the execute_data is allocated on the VM stack (because it does
- * not actually do any allocation and thus is faster). For generators
- * though this behavior would be suboptimal, because the (rather large)
- * structure would have to be copied back and forth every time execution is
- * suspended or resumed. That's why for generators the execution context
- * is allocated using a separate VM stack, thus allowing to save and
- * restore it simply by replacing a pointer.
- */
- zend_execute_data *execute_data;
- uint32_t num_args = ZEND_CALL_NUM_ARGS(call);
- size_t stack_size = (ZEND_CALL_FRAME_SLOT + MAX(op_array->last_var + op_array->T, num_args)) * sizeof(zval);
- uint32_t call_info;
-
- EG(vm_stack) = zend_vm_stack_new_page(
- EXPECTED(stack_size < ZEND_VM_STACK_FREE_PAGE_SIZE(1)) ?
- ZEND_VM_STACK_PAGE_SIZE(1) :
- ZEND_VM_STACK_PAGE_ALIGNED_SIZE(1, stack_size),
- NULL);
- EG(vm_stack_top) = EG(vm_stack)->top;
- EG(vm_stack_end) = EG(vm_stack)->end;
-
- call_info = ZEND_CALL_TOP_FUNCTION | ZEND_CALL_ALLOCATED | (ZEND_CALL_INFO(call) & (ZEND_CALL_CLOSURE|ZEND_CALL_RELEASE_THIS));
- if (Z_OBJ(call->This)) {
- call_info |= ZEND_CALL_RELEASE_THIS;
- }
- execute_data = zend_vm_stack_push_call_frame(
- call_info,
- (zend_function*)op_array,
- num_args,
- call->called_scope,
- Z_OBJ(call->This));
- EX(prev_execute_data) = NULL;
- EX_NUM_ARGS() = num_args;
-
- /* copy arguments */
- if (num_args > 0) {
- zval *arg_src = ZEND_CALL_ARG(call, 1);
- zval *arg_dst = ZEND_CALL_ARG(execute_data, 1);
- zval *end = arg_src + num_args;
-
- do {
- ZVAL_COPY_VALUE(arg_dst, arg_src);
- arg_src++;
- arg_dst++;
- } while (arg_src != end);
- }
-
- EX(symbol_table) = NULL;
-
- i_init_func_execute_data(execute_data, op_array, return_value, 1);
-
- return execute_data;
}
/* }}} */
@@ -2374,6 +2283,10 @@ ZEND_API void zend_init_execute_data(zend_execute_data *execute_data, zend_op_ar
static zend_always_inline zend_bool zend_is_by_ref_func_arg_fetch(const zend_op *opline, zend_execute_data *call) /* {{{ */
{
uint32_t arg_num = opline->extended_value & ZEND_FETCH_ARG_MASK;
+
+ if (EXPECTED(arg_num <= MAX_ARG_FLAG_NUM)) {
+ return QUICK_ARG_SHOULD_BE_SENT_BY_REF(call->func, arg_num);
+ }
return ARG_SHOULD_BE_SENT_BY_REF(call->func, arg_num);
}
/* }}} */
@@ -2386,7 +2299,7 @@ static zend_execute_data *zend_vm_stack_copy_call_frame(zend_execute_data *call,
/* copy call frame into new stack segment */
new_call = zend_vm_stack_extend(used_stack * sizeof(zval));
*new_call = *call;
- ZEND_SET_CALL_INFO(new_call, ZEND_CALL_INFO(new_call) | ZEND_CALL_ALLOCATED);
+ ZEND_ADD_CALL_FLAG(new_call, ZEND_CALL_ALLOCATED);
if (passed_args) {
zval *src = ZEND_CALL_ARG(call, 1);
@@ -2403,7 +2316,7 @@ static zend_execute_data *zend_vm_stack_copy_call_frame(zend_execute_data *call,
EG(vm_stack)->prev->top = (zval*)call;
/* delete previous stack segment if it becames empty */
- if (UNEXPECTED(EG(vm_stack)->prev->top == ZEND_VM_STACK_ELEMETS(EG(vm_stack)->prev))) {
+ if (UNEXPECTED(EG(vm_stack)->prev->top == ZEND_VM_STACK_ELEMENTS(EG(vm_stack)->prev))) {
zend_vm_stack r = EG(vm_stack)->prev;
EG(vm_stack)->prev = r->prev;
@@ -2486,6 +2399,7 @@ static void cleanup_unfinished_calls(zend_execute_data *execute_data, uint32_t o
case ZEND_SEND_VAR_EX:
case ZEND_SEND_REF:
case ZEND_SEND_VAR_NO_REF:
+ case ZEND_SEND_VAR_NO_REF_EX:
case ZEND_SEND_USER:
if (level == 0) {
ZEND_CALL_NUM_ARGS(call) = opline->op2.num;
@@ -2537,9 +2451,7 @@ static void cleanup_unfinished_calls(zend_execute_data *execute_data, uint32_t o
if (ZEND_CALL_INFO(call) & ZEND_CALL_RELEASE_THIS) {
if (ZEND_CALL_INFO(call) & ZEND_CALL_CTOR) {
- if (!(ZEND_CALL_INFO(call) & ZEND_CALL_CTOR_RESULT_UNUSED)) {
- GC_REFCOUNT(Z_OBJ(call->This))--;
- }
+ GC_REFCOUNT(Z_OBJ(call->This))--;
if (GC_REFCOUNT(Z_OBJ(call->This)) == 1) {
zend_object_store_ctor_failed(Z_OBJ(call->This));
}
@@ -2565,30 +2477,29 @@ static void cleanup_live_vars(zend_execute_data *execute_data, uint32_t op_num,
{
int i;
- for (i = 0; i < EX(func)->op_array.last_brk_cont; i++) {
- const zend_brk_cont_element *brk_cont = &EX(func)->op_array.brk_cont_array[i];
- if (brk_cont->start < 0) {
- continue;
- } else if (brk_cont->start > op_num) {
+ for (i = 0; i < EX(func)->op_array.last_live_range; i++) {
+ const zend_live_range *range = &EX(func)->op_array.live_range[i];
+ if (range->start > op_num) {
/* further blocks will not be relevant... */
break;
- } else if (op_num < brk_cont->brk) {
- if (!catch_op_num || catch_op_num >= brk_cont->brk) {
- zend_op *brk_opline = &EX(func)->op_array.opcodes[brk_cont->brk];
-
- if (brk_opline->opcode == ZEND_FREE) {
- zval_ptr_dtor_nogc(EX_VAR(brk_opline->op1.var));
- } else if (brk_opline->opcode == ZEND_FE_FREE) {
- zval *var = EX_VAR(brk_opline->op1.var);
+ } else if (op_num < range->end) {
+ if (!catch_op_num || catch_op_num >= range->end) {
+ uint32_t kind = range->var & ZEND_LIVE_MASK;
+ uint32_t var_num = range->var & ~ZEND_LIVE_MASK;
+ zval *var = EX_VAR(var_num);
+
+ if (kind == ZEND_LIVE_TMPVAR) {
+ zval_ptr_dtor_nogc(var);
+ } else if (kind == ZEND_LIVE_LOOP) {
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_ROPE_END) {
- zend_string **rope = (zend_string **) EX_VAR(brk_opline->op1.var);
+ } else if (kind == ZEND_LIVE_ROPE) {
+ zend_string **rope = (zend_string **)var;
zend_op *last = EX(func)->op_array.opcodes + op_num;
while ((last->opcode != ZEND_ROPE_ADD && last->opcode != ZEND_ROPE_INIT)
- || last->result.var != brk_opline->op1.var) {
+ || last->result.var != var_num) {
ZEND_ASSERT(last >= EX(func)->op_array.opcodes);
last--;
}
@@ -2600,10 +2511,10 @@ static void cleanup_live_vars(zend_execute_data *execute_data, uint32_t op_num,
zend_string_release(rope[j]);
} while (j--);
}
- } else if (brk_opline->opcode == ZEND_END_SILENCE) {
+ } else if (kind == ZEND_LIVE_SILENCE) {
/* restore previous error_reporting value */
- if (!EG(error_reporting) && Z_LVAL_P(EX_VAR(brk_opline->op1.var)) != 0) {
- EG(error_reporting) = Z_LVAL_P(EX_VAR(brk_opline->op1.var));
+ if (!EG(error_reporting) && Z_LVAL_P(var) != 0) {
+ EG(error_reporting) = Z_LVAL_P(var);
}
}
}
@@ -2617,6 +2528,356 @@ void zend_cleanup_unfinished_execution(zend_execute_data *execute_data, uint32_t
cleanup_live_vars(execute_data, op_num, catch_op_num);
}
+static void zend_swap_operands(zend_op *op) /* {{{ */
+{
+ znode_op tmp;
+ zend_uchar tmp_type;
+
+ tmp = op->op1;
+ tmp_type = op->op1_type;
+ op->op1 = op->op2;
+ op->op1_type = op->op2_type;
+ op->op2 = tmp;
+ op->op2_type = tmp_type;
+}
+/* }}} */
+
+static zend_never_inline zend_execute_data *zend_init_dynamic_call_string(zend_string *function, uint32_t num_args) /* {{{ */
+{
+ zend_function *fbc;
+ zval *func;
+ zend_class_entry *called_scope;
+ zend_string *lcname;
+ const char *colon;
+
+ if ((colon = zend_memrchr(ZSTR_VAL(function), ':', ZSTR_LEN(function))) != NULL &&
+ colon > ZSTR_VAL(function) &&
+ *(colon-1) == ':'
+ ) {
+ zend_string *mname;
+ size_t cname_length = colon - ZSTR_VAL(function) - 1;
+ size_t mname_length = ZSTR_LEN(function) - cname_length - (sizeof("::") - 1);
+
+ lcname = zend_string_init(ZSTR_VAL(function), cname_length, 0);
+
+ called_scope = zend_fetch_class_by_name(lcname, NULL, ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION);
+ if (UNEXPECTED(called_scope == NULL)) {
+ zend_string_release(lcname);
+ return NULL;
+ }
+
+ mname = zend_string_init(ZSTR_VAL(function) + (cname_length + sizeof("::") - 1), mname_length, 0);
+
+ if (called_scope->get_static_method) {
+ fbc = called_scope->get_static_method(called_scope, mname);
+ } else {
+ fbc = zend_std_get_static_method(called_scope, mname, NULL);
+ }
+ if (UNEXPECTED(fbc == NULL)) {
+ if (EXPECTED(!EG(exception))) {
+ zend_throw_error(NULL, "Call to undefined method %s::%s()", ZSTR_VAL(called_scope->name), ZSTR_VAL(mname));
+ }
+ zend_string_release(lcname);
+ zend_string_release(mname);
+ return NULL;
+ }
+
+ zend_string_release(lcname);
+ zend_string_release(mname);
+
+ if (UNEXPECTED(!(fbc->common.fn_flags & ZEND_ACC_STATIC))) {
+ if (fbc->common.fn_flags & ZEND_ACC_ALLOW_STATIC) {
+ zend_error(E_DEPRECATED,
+ "Non-static method %s::%s() should not be called statically",
+ ZSTR_VAL(fbc->common.scope->name), ZSTR_VAL(fbc->common.function_name));
+ if (UNEXPECTED(EG(exception) != NULL)) {
+ return NULL;
+ }
+ } else {
+ zend_throw_error(
+ zend_ce_error,
+ "Non-static method %s::%s() cannot be called statically",
+ ZSTR_VAL(fbc->common.scope->name), ZSTR_VAL(fbc->common.function_name));
+ return NULL;
+ }
+ }
+ } else {
+ if (ZSTR_VAL(function)[0] == '\\') {
+ lcname = zend_string_alloc(ZSTR_LEN(function) - 1, 0);
+ zend_str_tolower_copy(ZSTR_VAL(lcname), ZSTR_VAL(function) + 1, ZSTR_LEN(function) - 1);
+ } else {
+ lcname = zend_string_tolower(function);
+ }
+ if (UNEXPECTED((func = zend_hash_find(EG(function_table), lcname)) == NULL)) {
+ zend_throw_error(NULL, "Call to undefined function %s()", ZSTR_VAL(function));
+ zend_string_release(lcname);
+ return NULL;
+ }
+ zend_string_release(lcname);
+
+ fbc = Z_FUNC_P(func);
+ called_scope = NULL;
+ }
+
+ if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!fbc->op_array.run_time_cache)) {
+ init_func_run_time_cache(&fbc->op_array);
+ }
+
+ return zend_vm_stack_push_call_frame(ZEND_CALL_NESTED_FUNCTION | ZEND_CALL_DYNAMIC,
+ fbc, num_args, called_scope, NULL);
+}
+/* }}} */
+
+static zend_never_inline zend_execute_data *zend_init_dynamic_call_object(zval *function, uint32_t num_args) /* {{{ */
+{
+ zend_function *fbc;
+ zend_class_entry *called_scope;
+ zend_object *object;
+ uint32_t call_info = ZEND_CALL_NESTED_FUNCTION | ZEND_CALL_DYNAMIC;
+
+ if (EXPECTED(Z_OBJ_HANDLER_P(function, get_closure)) &&
+ EXPECTED(Z_OBJ_HANDLER_P(function, get_closure)(function, &called_scope, &fbc, &object) == SUCCESS)) {
+
+ if (fbc->common.fn_flags & ZEND_ACC_CLOSURE) {
+ /* Delay closure destruction until its invocation */
+ ZEND_ASSERT(GC_TYPE((zend_object*)fbc->common.prototype) == IS_OBJECT);
+ GC_REFCOUNT((zend_object*)fbc->common.prototype)++;
+ call_info |= ZEND_CALL_CLOSURE;
+ } else if (object) {
+ call_info |= ZEND_CALL_RELEASE_THIS;
+ GC_REFCOUNT(object)++; /* For $this pointer */
+ }
+ } else {
+ zend_throw_error(NULL, "Function name must be a string");
+ return NULL;
+ }
+
+ if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!fbc->op_array.run_time_cache)) {
+ init_func_run_time_cache(&fbc->op_array);
+ }
+
+ return zend_vm_stack_push_call_frame(call_info,
+ fbc, num_args, called_scope, object);
+}
+/* }}} */
+
+static zend_never_inline zend_execute_data *zend_init_dynamic_call_array(zend_array *function, uint32_t num_args) /* {{{ */
+{
+ zend_function *fbc;
+ zend_class_entry *called_scope;
+ zend_object *object;
+ uint32_t call_info = ZEND_CALL_NESTED_FUNCTION | ZEND_CALL_DYNAMIC;
+
+ if (zend_hash_num_elements(function) == 2) {
+ zval *obj;
+ zval *method;
+ obj = zend_hash_index_find(function, 0);
+ method = zend_hash_index_find(function, 1);
+
+ if (UNEXPECTED(!obj) || UNEXPECTED(!method)) {
+ zend_throw_error(NULL, "Array callback has to contain indices 0 and 1");
+ return NULL;
+ }
+
+ ZVAL_DEREF(obj);
+ if (UNEXPECTED(Z_TYPE_P(obj) != IS_STRING) && UNEXPECTED(Z_TYPE_P(obj) != IS_OBJECT)) {
+ zend_throw_error(NULL, "First array member is not a valid class name or object");
+ return NULL;
+ }
+
+ ZVAL_DEREF(method);
+ if (UNEXPECTED(Z_TYPE_P(method) != IS_STRING)) {
+ zend_throw_error(NULL, "Second array member is not a valid method");
+ return NULL;
+ }
+
+ if (Z_TYPE_P(obj) == IS_STRING) {
+ object = NULL;
+ called_scope = zend_fetch_class_by_name(Z_STR_P(obj), NULL, ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION);
+ if (UNEXPECTED(called_scope == NULL)) {
+ return NULL;
+ }
+
+ if (called_scope->get_static_method) {
+ fbc = called_scope->get_static_method(called_scope, Z_STR_P(method));
+ } else {
+ fbc = zend_std_get_static_method(called_scope, Z_STR_P(method), NULL);
+ }
+ if (UNEXPECTED(fbc == NULL)) {
+ if (EXPECTED(!EG(exception))) {
+ zend_throw_error(NULL, "Call to undefined method %s::%s()", ZSTR_VAL(called_scope->name), Z_STRVAL_P(method));
+ }
+ return NULL;
+ }
+ if (!(fbc->common.fn_flags & ZEND_ACC_STATIC)) {
+ if (fbc->common.fn_flags & ZEND_ACC_ALLOW_STATIC) {
+ zend_error(E_DEPRECATED,
+ "Non-static method %s::%s() should not be called statically",
+ ZSTR_VAL(fbc->common.scope->name), ZSTR_VAL(fbc->common.function_name));
+ if (UNEXPECTED(EG(exception) != NULL)) {
+ return NULL;
+ }
+ } else {
+ zend_throw_error(
+ zend_ce_error,
+ "Non-static method %s::%s() cannot be called statically",
+ ZSTR_VAL(fbc->common.scope->name), ZSTR_VAL(fbc->common.function_name));
+ return NULL;
+ }
+ }
+ } else {
+ called_scope = Z_OBJCE_P(obj);
+ object = Z_OBJ_P(obj);
+
+ fbc = Z_OBJ_HT_P(obj)->get_method(&object, Z_STR_P(method), NULL);
+ if (UNEXPECTED(fbc == NULL)) {
+ if (EXPECTED(!EG(exception))) {
+ zend_throw_error(NULL, "Call to undefined method %s::%s()", ZSTR_VAL(object->ce->name), Z_STRVAL_P(method));
+ }
+ return NULL;
+ }
+
+ if ((fbc->common.fn_flags & ZEND_ACC_STATIC) != 0) {
+ object = NULL;
+ } else {
+ call_info |= ZEND_CALL_RELEASE_THIS;
+ GC_REFCOUNT(object)++; /* For $this pointer */
+ }
+ }
+ } else {
+ zend_throw_error(NULL, "Function name must be a string");
+ return NULL;
+ }
+
+ if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!fbc->op_array.run_time_cache)) {
+ init_func_run_time_cache(&fbc->op_array);
+ }
+
+ return zend_vm_stack_push_call_frame(call_info,
+ fbc, num_args, called_scope, object);
+}
+/* }}} */
+
+#define ZEND_FAKE_OP_ARRAY ((zend_op_array*)(zend_intptr_t)-1)
+
+static zend_never_inline zend_op_array* ZEND_FASTCALL zend_include_or_eval(zval *inc_filename, int type) /* {{{ */
+{
+ zend_op_array *new_op_array = NULL;
+ zval tmp_inc_filename;
+
+ ZVAL_UNDEF(&tmp_inc_filename);
+ if (Z_TYPE_P(inc_filename) != IS_STRING) {
+ ZVAL_STR(&tmp_inc_filename, zval_get_string(inc_filename));
+ inc_filename = &tmp_inc_filename;
+ }
+
+ if (type != ZEND_EVAL && strlen(Z_STRVAL_P(inc_filename)) != Z_STRLEN_P(inc_filename)) {
+ if (type == ZEND_INCLUDE_ONCE || type == ZEND_INCLUDE) {
+ zend_message_dispatcher(ZMSG_FAILED_INCLUDE_FOPEN, Z_STRVAL_P(inc_filename));
+ } else {
+ zend_message_dispatcher(ZMSG_FAILED_REQUIRE_FOPEN, Z_STRVAL_P(inc_filename));
+ }
+ } else {
+ switch (type) {
+ case ZEND_INCLUDE_ONCE:
+ case ZEND_REQUIRE_ONCE: {
+ zend_file_handle file_handle;
+ zend_string *resolved_path;
+
+ resolved_path = zend_resolve_path(Z_STRVAL_P(inc_filename), (int)Z_STRLEN_P(inc_filename));
+ if (resolved_path) {
+ if (zend_hash_exists(&EG(included_files), resolved_path)) {
+ goto already_compiled;
+ }
+ } else {
+ resolved_path = zend_string_copy(Z_STR_P(inc_filename));
+ }
+
+ if (SUCCESS == zend_stream_open(ZSTR_VAL(resolved_path), &file_handle)) {
+
+ if (!file_handle.opened_path) {
+ file_handle.opened_path = zend_string_copy(resolved_path);
+ }
+
+ if (zend_hash_add_empty_element(&EG(included_files), file_handle.opened_path)) {
+ zend_op_array *op_array = zend_compile_file(&file_handle, (type==ZEND_INCLUDE_ONCE?ZEND_INCLUDE:ZEND_REQUIRE));
+ zend_destroy_file_handle(&file_handle);
+ zend_string_release(resolved_path);
+ if (Z_TYPE(tmp_inc_filename) != IS_UNDEF) {
+ zend_string_release(Z_STR(tmp_inc_filename));
+ }
+ return op_array;
+ } else {
+ zend_file_handle_dtor(&file_handle);
+already_compiled:
+ new_op_array = ZEND_FAKE_OP_ARRAY;
+ }
+ } else {
+ if (type == ZEND_INCLUDE_ONCE) {
+ zend_message_dispatcher(ZMSG_FAILED_INCLUDE_FOPEN, Z_STRVAL_P(inc_filename));
+ } else {
+ zend_message_dispatcher(ZMSG_FAILED_REQUIRE_FOPEN, Z_STRVAL_P(inc_filename));
+ }
+ }
+ zend_string_release(resolved_path);
+ }
+ break;
+ case ZEND_INCLUDE:
+ case ZEND_REQUIRE:
+ new_op_array = compile_filename(type, inc_filename);
+ break;
+ case ZEND_EVAL: {
+ char *eval_desc = zend_make_compiled_string_description("eval()'d code");
+ new_op_array = zend_compile_string(inc_filename, eval_desc);
+ efree(eval_desc);
+ }
+ break;
+ EMPTY_SWITCH_DEFAULT_CASE()
+ }
+ }
+ if (Z_TYPE(tmp_inc_filename) != IS_UNDEF) {
+ zend_string_release(Z_STR(tmp_inc_filename));
+ }
+ return new_op_array;
+}
+/* }}} */
+
+static zend_never_inline int zend_do_fcall_overloaded(zend_function *fbc, zend_execute_data *call, zval *ret) /* {{{ */
+{
+ zend_object *object;
+
+ /* Not sure what should be done here if it's a static method */
+ if (UNEXPECTED(Z_TYPE(call->This) != IS_OBJECT)) {
+ zend_vm_stack_free_args(call);
+ if (fbc->type == ZEND_OVERLOADED_FUNCTION_TEMPORARY) {
+ zend_string_release(fbc->common.function_name);
+ }
+ efree(fbc);
+ zend_vm_stack_free_call_frame(call);
+
+ zend_throw_error(NULL, "Cannot call overloaded function for non-object");
+ return 0;
+ }
+
+ object = Z_OBJ(call->This);
+
+ ZVAL_NULL(ret);
+
+ EG(current_execute_data) = call;
+ object->handlers->call_method(fbc->common.function_name, object, call, ret);
+ EG(current_execute_data) = call->prev_execute_data;
+
+ zend_vm_stack_free_args(call);
+
+ if (fbc->type == ZEND_OVERLOADED_FUNCTION_TEMPORARY) {
+ zend_string_release(fbc->common.function_name);
+ }
+ efree(fbc);
+
+ return 1;
+}
+/* }}} */
+
#ifdef HAVE_GCC_GLOBAL_REGS
# if defined(__GNUC__) && ZEND_GCC_VERSION >= 4008 && defined(i386)
# define ZEND_VM_FP_GLOBAL_REG "%esi"
@@ -2702,10 +2963,34 @@ void zend_cleanup_unfinished_execution(zend_execute_data *execute_data, uint32_t
} \
ZEND_VM_CONTINUE(); \
} while (0)
+# define ZEND_VM_SMART_BRANCH_JMPZ(_result, _check) do { \
+ if ((_check) && UNEXPECTED(EG(exception))) { \
+ HANDLE_EXCEPTION(); \
+ } \
+ if (_result) { \
+ ZEND_VM_SET_NEXT_OPCODE(opline + 2); \
+ } else { \
+ ZEND_VM_SET_OPCODE(OP_JMP_ADDR(opline + 1, (opline+1)->op2)); \
+ } \
+ ZEND_VM_CONTINUE(); \
+ } while (0)
+# define ZEND_VM_SMART_BRANCH_JMPNZ(_result, _check) do { \
+ if ((_check) && UNEXPECTED(EG(exception))) { \
+ HANDLE_EXCEPTION(); \
+ } \
+ if (!(_result)) { \
+ ZEND_VM_SET_NEXT_OPCODE(opline + 2); \
+ } else { \
+ ZEND_VM_SET_OPCODE(OP_JMP_ADDR(opline + 1, (opline+1)->op2)); \
+ } \
+ ZEND_VM_CONTINUE(); \
+ } while (0)
#else
# define ZEND_VM_REPEATABLE_OPCODE
# define ZEND_VM_REPEAT_OPCODE(_opcode)
# define ZEND_VM_SMART_BRANCH(_result, _check)
+# define ZEND_VM_SMART_BRANCH_JMPZ(_result, _check)
+# define ZEND_VM_SMART_BRANCH_JMPNZ(_result, _check)
#endif
#ifdef __GNUC__
@@ -2756,11 +3041,6 @@ ZEND_API int ZEND_FASTCALL zend_check_arg_type(zend_function *zf, uint32_t arg_n
return zend_verify_arg_type(zf, arg_num, arg, default_value, cache_slot);
}
-ZEND_API void ZEND_FASTCALL zend_check_missing_arg(zend_execute_data *execute_data, uint32_t arg_num, void **cache_slot)
-{
- zend_verify_missing_arg(execute_data, arg_num, cache_slot);
-}
-
/*
* Local variables:
* tab-width: 4
diff --git a/Zend/zend_execute.h b/Zend/zend_execute.h
index c47667b8f0..82c6a4a415 100644
--- a/Zend/zend_execute.h
+++ b/Zend/zend_execute.h
@@ -37,7 +37,6 @@ void init_executor(void);
void shutdown_executor(void);
void shutdown_destructors(void);
ZEND_API void zend_init_execute_data(zend_execute_data *execute_data, zend_op_array *op_array, zval *return_value);
-ZEND_API zend_execute_data *zend_create_generator_execute_data(zend_execute_data *call, zend_op_array *op_array, zval *return_value);
ZEND_API void zend_execute(zend_op_array *op_array, zval *return_value);
ZEND_API void execute_ex(zend_execute_data *execute_data);
ZEND_API void execute_internal(zend_execute_data *execute_data, zval *return_value);
@@ -50,9 +49,12 @@ ZEND_API int zend_eval_stringl(char *str, size_t str_len, zval *retval_ptr, char
ZEND_API int zend_eval_string_ex(char *str, zval *retval_ptr, char *string_name, int handle_exceptions);
ZEND_API int zend_eval_stringl_ex(char *str, size_t str_len, zval *retval_ptr, char *string_name, int handle_exceptions);
+/* export zend_pass_function to allow comparisons against it */
+extern ZEND_API const zend_internal_function zend_pass_function;
+
ZEND_API void ZEND_FASTCALL zend_check_internal_arg_type(zend_function *zf, uint32_t arg_num, zval *arg);
ZEND_API int ZEND_FASTCALL zend_check_arg_type(zend_function *zf, uint32_t arg_num, zval *arg, zval *default_value, void **cache_slot);
-ZEND_API void ZEND_FASTCALL zend_check_missing_arg(zend_execute_data *execute_data, uint32_t arg_num, void **cache_slot);
+ZEND_API ZEND_COLD void ZEND_FASTCALL zend_missing_arg_error(zend_execute_data *execute_data);
static zend_always_inline zval* zend_assign_to_variable(zval *variable_ptr, zval *value, zend_uchar value_type)
{
@@ -84,12 +86,7 @@ static zend_always_inline zval* zend_assign_to_variable(zval *variable_ptr, zval
garbage = Z_COUNTED_P(variable_ptr);
if (--GC_REFCOUNT(garbage) == 0) {
ZVAL_COPY_VALUE(variable_ptr, value);
- if (value_type == IS_CONST) {
- /* IS_CONST can't be IS_OBJECT, IS_RESOURCE or IS_REFERENCE */
- if (UNEXPECTED(Z_OPT_COPYABLE_P(variable_ptr))) {
- zval_copy_ctor_func(variable_ptr);
- }
- } else if (value_type == IS_CV) {
+ if (value_type & (IS_CONST|IS_CV)) {
if (UNEXPECTED(Z_OPT_REFCOUNTED_P(variable_ptr))) {
Z_ADDREF_P(variable_ptr);
}
@@ -100,7 +97,7 @@ static zend_always_inline zval* zend_assign_to_variable(zval *variable_ptr, zval
Z_ADDREF_P(variable_ptr);
}
}
- zval_dtor_func_for_ptr(garbage);
+ zval_dtor_func(garbage);
return variable_ptr;
} else { /* we need to split */
/* optimized version of GC_ZVAL_CHECK_POSSIBLE_ROOT(variable_ptr) */
@@ -113,12 +110,7 @@ static zend_always_inline zval* zend_assign_to_variable(zval *variable_ptr, zval
} while (0);
ZVAL_COPY_VALUE(variable_ptr, value);
- if (value_type == IS_CONST) {
- /* IS_CONST can't be IS_OBJECT, IS_RESOURCE or IS_REFERENCE */
- if (UNEXPECTED(Z_OPT_COPYABLE_P(variable_ptr))) {
- zval_copy_ctor_func(variable_ptr);
- }
- } else if (value_type == IS_CV) {
+ if (value_type & (IS_CONST|IS_CV)) {
if (UNEXPECTED(Z_OPT_REFCOUNTED_P(variable_ptr))) {
Z_ADDREF_P(variable_ptr);
}
@@ -132,8 +124,8 @@ static zend_always_inline zval* zend_assign_to_variable(zval *variable_ptr, zval
return variable_ptr;
}
-ZEND_API int zval_update_constant(zval *pp, zend_bool inline_change);
-ZEND_API int zval_update_constant_ex(zval *pp, zend_bool inline_change, zend_class_entry *scope);
+ZEND_API int zval_update_constant(zval *pp);
+ZEND_API int zval_update_constant_ex(zval *pp, zend_class_entry *scope);
/* dedicated Zend executor functions - do not use! */
struct _zend_vm_stack {
@@ -145,7 +137,7 @@ struct _zend_vm_stack {
#define ZEND_VM_STACK_HEADER_SLOTS \
((ZEND_MM_ALIGNED_SIZE(sizeof(struct _zend_vm_stack)) + ZEND_MM_ALIGNED_SIZE(sizeof(zval)) - 1) / ZEND_MM_ALIGNED_SIZE(sizeof(zval)))
-#define ZEND_VM_STACK_ELEMETS(stack) \
+#define ZEND_VM_STACK_ELEMENTS(stack) \
(((zval*)(stack)) + ZEND_VM_STACK_HEADER_SLOTS)
/*
@@ -167,6 +159,19 @@ ZEND_API void zend_vm_stack_init(void);
ZEND_API void zend_vm_stack_destroy(void);
ZEND_API void* zend_vm_stack_extend(size_t size);
+static zend_always_inline void zend_vm_init_call_frame(zend_execute_data *call, uint32_t call_info, zend_function *func, uint32_t num_args, zend_class_entry *called_scope, zend_object *object)
+{
+ call->func = func;
+ if (object) {
+ Z_OBJ(call->This) = object;
+ ZEND_SET_CALL_INFO(call, 1, call_info);
+ } else {
+ Z_CE(call->This) = called_scope;
+ ZEND_SET_CALL_INFO(call, 0, call_info);
+ }
+ ZEND_CALL_NUM_ARGS(call) = num_args;
+}
+
static zend_always_inline zend_execute_data *zend_vm_stack_push_call_frame_ex(uint32_t used_stack, uint32_t call_info, zend_function *func, uint32_t num_args, zend_class_entry *called_scope, zend_object *object)
{
zend_execute_data *call = (zend_execute_data*)EG(vm_stack_top);
@@ -175,19 +180,14 @@ static zend_always_inline zend_execute_data *zend_vm_stack_push_call_frame_ex(ui
if (UNEXPECTED(used_stack > (size_t)(((char*)EG(vm_stack_end)) - (char*)call))) {
call = (zend_execute_data*)zend_vm_stack_extend(used_stack);
- ZEND_SET_CALL_INFO(call, call_info | ZEND_CALL_ALLOCATED);
+ ZEND_ASSERT_VM_STACK_GLOBAL;
+ zend_vm_init_call_frame(call, call_info | ZEND_CALL_ALLOCATED, func, num_args, called_scope, object);
+ return call;
} else {
EG(vm_stack_top) = (zval*)((char*)call + used_stack);
- ZEND_SET_CALL_INFO(call, call_info);
+ zend_vm_init_call_frame(call, call_info, func, num_args, called_scope, object);
+ return call;
}
-
- ZEND_ASSERT_VM_STACK_GLOBAL;
-
- call->func = func;
- Z_OBJ(call->This) = object;
- ZEND_CALL_NUM_ARGS(call) = num_args;
- call->called_scope = called_scope;
- return call;
}
static zend_always_inline uint32_t zend_vm_calc_used_stack(uint32_t num_args, zend_function *func)
@@ -219,7 +219,7 @@ static zend_always_inline void zend_vm_stack_free_extra_args_ex(uint32_t call_in
if (!Z_DELREF_P(p)) {
zend_refcounted *r = Z_COUNTED_P(p);
ZVAL_NULL(p);
- zval_dtor_func_for_ptr(r);
+ zval_dtor_func(r);
} else {
GC_ZVAL_CHECK_POSSIBLE_ROOT(p);
}
@@ -247,7 +247,7 @@ static zend_always_inline void zend_vm_stack_free_args(zend_execute_data *call)
if (!Z_DELREF_P(p)) {
zend_refcounted *r = Z_COUNTED_P(p);
ZVAL_NULL(p);
- zval_dtor_func_for_ptr(r);
+ zval_dtor_func(r);
}
}
} while (p != end);
@@ -260,14 +260,13 @@ static zend_always_inline void zend_vm_stack_free_call_frame_ex(uint32_t call_in
if (UNEXPECTED(call_info & ZEND_CALL_ALLOCATED)) {
zend_vm_stack p = EG(vm_stack);
-
zend_vm_stack prev = p->prev;
+ ZEND_ASSERT(call == (zend_execute_data*)ZEND_VM_STACK_ELEMENTS(EG(vm_stack)));
EG(vm_stack_top) = prev->top;
EG(vm_stack_end) = prev->end;
EG(vm_stack) = prev;
efree(p);
-
} else {
EG(vm_stack_top) = (zval*)call;
}
@@ -286,11 +285,12 @@ ZEND_API const char *get_active_function_name(void);
ZEND_API const char *zend_get_executed_filename(void);
ZEND_API zend_string *zend_get_executed_filename_ex(void);
ZEND_API uint zend_get_executed_lineno(void);
+ZEND_API zend_class_entry *zend_get_executed_scope(void);
ZEND_API zend_bool zend_is_executing(void);
ZEND_API void zend_set_timeout(zend_long seconds, int reset_signals);
ZEND_API void zend_unset_timeout(void);
-ZEND_API void zend_timeout(int dummy);
+ZEND_API ZEND_NORETURN void zend_timeout(int dummy);
ZEND_API zend_class_entry *zend_fetch_class(zend_string *class_name, int fetch_type);
ZEND_API zend_class_entry *zend_fetch_class_by_name(zend_string *class_name, const zval *key, int fetch_type);
void zend_verify_abstract_class(zend_class_entry *ce);
diff --git a/Zend/zend_execute_API.c b/Zend/zend_execute_API.c
index 1cbbc1a0a1..f7bce4f4e5 100644
--- a/Zend/zend_execute_API.c
+++ b/Zend/zend_execute_API.c
@@ -38,12 +38,15 @@
#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
ZEND_API void (*zend_execute_ex)(zend_execute_data *execute_data);
ZEND_API void (*zend_execute_internal)(zend_execute_data *execute_data, zval *return_value);
/* true globals */
-ZEND_API const zend_fcall_info empty_fcall_info = { 0, NULL, {{0}, {{0}}, {0}}, NULL, NULL, NULL, NULL, 0, 0 };
+ZEND_API const zend_fcall_info empty_fcall_info = { 0, {{0}, {{0}}, {0}}, NULL, NULL, NULL, 0, 0 };
ZEND_API const zend_fcall_info_cache empty_fcall_info_cache = { 0, NULL, NULL, NULL, NULL };
#ifdef ZEND_WIN32
@@ -130,7 +133,7 @@ void init_executor(void) /* {{{ */
zend_init_fpu();
ZVAL_NULL(&EG(uninitialized_zval));
- ZVAL_NULL(&EG(error_zval));
+ ZVAL_ERROR(&EG(error_zval));
/* destroys stack frame, therefore makes core dumps worthless */
#if 0&&ZEND_DEBUG
original_sigsegv_handler = signal(SIGSEGV, zend_handle_sigsegv);
@@ -169,14 +172,12 @@ void init_executor(void) /* {{{ */
zend_objects_store_init(&EG(objects_store), 1024);
EG(full_tables_cleanup) = 0;
-#ifdef ZEND_WIN32
EG(timed_out) = 0;
-#endif
EG(exception) = NULL;
EG(prev_exception) = NULL;
- EG(scope) = NULL;
+ EG(fake_scope) = NULL;
EG(ht_iterators_count) = sizeof(EG(ht_iterators_slots)) / sizeof(HashTableIterator);
EG(ht_iterators_used) = 0;
@@ -522,6 +523,21 @@ ZEND_API uint zend_get_executed_lineno(void) /* {{{ */
}
/* }}} */
+ZEND_API zend_class_entry *zend_get_executed_scope(void) /* {{{ */
+{
+ zend_execute_data *ex = EG(current_execute_data);
+
+ while (1) {
+ if (!ex) {
+ return NULL;
+ } else if (ex->func && (ZEND_USER_CODE(ex->func->type) || ex->func->common.scope)) {
+ return ex->func->common.scope;
+ }
+ ex = ex->prev_execute_data;
+ }
+}
+/* }}} */
+
ZEND_API zend_bool zend_is_executing(void) /* {{{ */
{
return EG(current_execute_data) != 0;
@@ -545,21 +561,18 @@ ZEND_API void _zval_internal_ptr_dtor(zval *zval_ptr ZEND_FILE_LINE_DC) /* {{{ *
}
/* }}} */
-#define IS_VISITED_CONSTANT 0x80
-#define IS_CONSTANT_VISITED(p) (Z_TYPE_P(p) & IS_VISITED_CONSTANT)
-#define MARK_CONSTANT_VISITED(p) Z_TYPE_INFO_P(p) |= IS_VISITED_CONSTANT
-#define RESET_CONSTANT_VISITED(p) Z_TYPE_INFO_P(p) &= ~IS_VISITED_CONSTANT
-
-ZEND_API int zval_update_constant_ex(zval *p, zend_bool inline_change, zend_class_entry *scope) /* {{{ */
+ZEND_API int zval_update_constant_ex(zval *p, zend_class_entry *scope) /* {{{ */
{
zval *const_value;
char *colon;
+ zend_bool inline_change;
- if (IS_CONSTANT_VISITED(p)) {
- zend_throw_error(NULL, "Cannot declare self-referencing constant '%s'", Z_STRVAL_P(p));
- return FAILURE;
- } else if (Z_TYPE_P(p) == IS_CONSTANT) {
-
+ if (Z_TYPE_P(p) == IS_CONSTANT) {
+ if (IS_CONSTANT_VISITED(p)) {
+ zend_throw_error(NULL, "Cannot declare self-referencing constant '%s'", Z_STRVAL_P(p));
+ return FAILURE;
+ }
+ inline_change = (Z_TYPE_FLAGS_P(p) & IS_TYPE_IMMUTABLE) == 0;
SEPARATE_ZVAL_NOREF(p);
MARK_CONSTANT_VISITED(p);
if (Z_CONST_FLAGS_P(p) & IS_CONSTANT_CLASS) {
@@ -567,8 +580,8 @@ ZEND_API int zval_update_constant_ex(zval *p, zend_bool inline_change, zend_clas
if (inline_change) {
zend_string_release(Z_STR_P(p));
}
- if (EG(scope) && EG(scope)->name) {
- ZVAL_STR_COPY(p, EG(scope)->name);
+ if (scope && scope->name) {
+ ZVAL_STR_COPY(p, scope->name);
} else {
ZVAL_EMPTY_STRING(p);
}
@@ -595,21 +608,8 @@ ZEND_API int zval_update_constant_ex(zval *p, zend_bool inline_change, zend_clas
Z_TYPE_FLAGS_P(p) = IS_TYPE_REFCOUNTED | IS_TYPE_COPYABLE;
}
}
- if (actual[0] == '\\') {
- if (inline_change) {
- memmove(Z_STRVAL_P(p), Z_STRVAL_P(p)+1, Z_STRLEN_P(p));
- --Z_STRLEN_P(p);
- } else {
- ++actual;
- }
- --actual_len;
- }
if ((Z_CONST_FLAGS_P(p) & IS_CONSTANT_UNQUALIFIED) == 0) {
- if (ZSTR_VAL(save)[0] == '\\') {
- zend_throw_error(NULL, "Undefined constant '%s'", ZSTR_VAL(save) + 1);
- } else {
- zend_throw_error(NULL, "Undefined constant '%s'", ZSTR_VAL(save));
- }
+ zend_throw_error(NULL, "Undefined constant '%s'", ZSTR_VAL(save));
if (inline_change) {
zend_string_release(save);
}
@@ -633,17 +633,12 @@ ZEND_API int zval_update_constant_ex(zval *p, zend_bool inline_change, zend_clas
zend_string_release(Z_STR_P(p));
}
ZVAL_COPY_VALUE(p, const_value);
- if (Z_OPT_CONSTANT_P(p)) {
- if (UNEXPECTED(zval_update_constant_ex(p, 1, NULL) != SUCCESS)) {
- RESET_CONSTANT_VISITED(p);
- return FAILURE;
- }
- }
zval_opt_copy_ctor(p);
}
} else if (Z_TYPE_P(p) == IS_CONSTANT_AST) {
zval tmp;
+ inline_change = (Z_TYPE_FLAGS_P(p) & IS_TYPE_IMMUTABLE) == 0;
if (UNEXPECTED(zend_ast_evaluate(&tmp, Z_ASTVAL_P(p), scope) != SUCCESS)) {
return FAILURE;
}
@@ -656,31 +651,23 @@ ZEND_API int zval_update_constant_ex(zval *p, zend_bool inline_change, zend_clas
}
/* }}} */
-ZEND_API int zval_update_constant(zval *pp, zend_bool inline_change) /* {{{ */
-{
- return zval_update_constant_ex(pp, inline_change, NULL);
-}
-/* }}} */
-
-int call_user_function(HashTable *function_table, zval *object, zval *function_name, zval *retval_ptr, uint32_t param_count, zval params[]) /* {{{ */
+ZEND_API int zval_update_constant(zval *pp) /* {{{ */
{
- return call_user_function_ex(function_table, object, function_name, retval_ptr, param_count, params, 1, NULL);
+ return zval_update_constant_ex(pp, EG(current_execute_data) ? zend_get_executed_scope() : CG(active_class_entry));
}
/* }}} */
-int call_user_function_ex(HashTable *function_table, zval *object, zval *function_name, zval *retval_ptr, uint32_t param_count, zval params[], int no_separation, zend_array *symbol_table) /* {{{ */
+int _call_user_function_ex(zval *object, zval *function_name, zval *retval_ptr, uint32_t param_count, zval params[], int no_separation) /* {{{ */
{
zend_fcall_info fci;
fci.size = sizeof(fci);
- fci.function_table = function_table;
fci.object = object ? Z_OBJ_P(object) : NULL;
ZVAL_COPY_VALUE(&fci.function_name, function_name);
fci.retval = retval_ptr;
fci.param_count = param_count;
fci.params = params;
fci.no_separation = (zend_bool) no_separation;
- fci.symbol_table = symbol_table;
return zend_call_function(&fci, NULL);
}
@@ -689,11 +676,9 @@ int call_user_function_ex(HashTable *function_table, zval *object, zval *functio
int zend_call_function(zend_fcall_info *fci, zend_fcall_info_cache *fci_cache) /* {{{ */
{
uint32_t i;
- zend_class_entry *calling_scope = NULL;
zend_execute_data *call, dummy_execute_data;
zend_fcall_info_cache fci_cache_local;
zend_function *func;
- zend_class_entry *orig_scope;
ZVAL_UNDEF(fci->retval);
@@ -713,8 +698,6 @@ int zend_call_function(zend_fcall_info *fci, zend_fcall_info_cache *fci_cache) /
break;
}
- orig_scope = EG(scope);
-
/* Initialize execute_data */
if (!EG(current_execute_data)) {
/* This only happens when we're called outside any execute()'s
@@ -770,10 +753,11 @@ int zend_call_function(zend_fcall_info *fci, zend_fcall_info_cache *fci_cache) /
}
func = fci_cache->function_handler;
- call = zend_vm_stack_push_call_frame(ZEND_CALL_TOP_FUNCTION,
- func, fci->param_count, fci_cache->called_scope, fci_cache->object);
- calling_scope = fci_cache->calling_scope;
- fci->object = fci_cache->object;
+ fci->object = (func->common.fn_flags & ZEND_ACC_STATIC) ?
+ NULL : fci_cache->object;
+
+ call = zend_vm_stack_push_call_frame(ZEND_CALL_TOP_FUNCTION | ZEND_CALL_DYNAMIC,
+ func, fci->param_count, fci_cache->called_scope, fci->object);
if (fci->object &&
(!EG(objects_store).object_buckets ||
!IS_OBJ_VALID(EG(objects_store).object_buckets[fci->object->handle]))) {
@@ -842,31 +826,16 @@ int zend_call_function(zend_fcall_info *fci, zend_fcall_info_cache *fci_cache) /
ZVAL_COPY_VALUE(param, arg);
}
- EG(scope) = calling_scope;
- if (func->common.fn_flags & ZEND_ACC_STATIC) {
- fci->object = NULL;
- }
- Z_OBJ(call->This) = fci->object;
-
if (UNEXPECTED(func->op_array.fn_flags & ZEND_ACC_CLOSURE)) {
ZEND_ASSERT(GC_TYPE((zend_object*)func->op_array.prototype) == IS_OBJECT);
GC_REFCOUNT((zend_object*)func->op_array.prototype)++;
ZEND_ADD_CALL_FLAG(call, ZEND_CALL_CLOSURE);
}
- /* PHP-7 doesn't support symbol_table substitution for functions */
- ZEND_ASSERT(fci->symbol_table == NULL);
-
if (func->type == ZEND_USER_FUNCTION) {
int call_via_handler = (func->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE) != 0;
- EG(scope) = func->common.scope;
- call->symbol_table = fci->symbol_table;
- if (EXPECTED((func->op_array.fn_flags & ZEND_ACC_GENERATOR) == 0)) {
- zend_init_execute_data(call, &func->op_array, fci->retval);
- zend_execute_ex(call);
- } else {
- zend_generator_create_zval(call, &func->op_array, fci->retval);
- }
+ zend_init_execute_data(call, &func->op_array, fci->retval);
+ zend_execute_ex(call);
if (call_via_handler) {
/* We must re-initialize function again */
fci_cache->initialized = 0;
@@ -874,9 +843,6 @@ int zend_call_function(zend_fcall_info *fci, zend_fcall_info_cache *fci_cache) /
} else if (func->type == ZEND_INTERNAL_FUNCTION) {
int call_via_handler = (func->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE) != 0;
ZVAL_NULL(fci->retval);
- if (func->common.scope) {
- EG(scope) = func->common.scope;
- }
call->prev_execute_data = EG(current_execute_data);
call->return_value = NULL; /* this is not a constructor call */
EG(current_execute_data) = call;
@@ -930,7 +896,6 @@ int zend_call_function(zend_fcall_info *fci, zend_fcall_info_cache *fci_cache) /
}
}
- EG(scope) = orig_scope;
zend_vm_stack_free_call_frame(call);
if (EG(current_execute_data) == &dummy_execute_data) {
@@ -987,7 +952,7 @@ ZEND_API zend_class_entry *zend_lookup_class_ex(zend_string *name, const zval *k
}
if (!EG(autoload_func)) {
- zend_function *func = zend_hash_str_find_ptr(EG(function_table), ZEND_AUTOLOAD_FUNC_NAME, sizeof(ZEND_AUTOLOAD_FUNC_NAME) - 1);
+ zend_function *func = zend_hash_find_ptr(EG(function_table), CG(known_strings)[ZEND_STR_MAGIC_AUTOLOAD]);
if (func) {
EG(autoload_func) = func;
} else {
@@ -1000,7 +965,7 @@ ZEND_API zend_class_entry *zend_lookup_class_ex(zend_string *name, const zval *k
}
/* Verify class name before passing it to __autoload() */
- if (strspn(ZSTR_VAL(name), "0123456789_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ\177\200\201\202\203\204\205\206\207\210\211\212\213\214\215\216\217\220\221\222\223\224\225\226\227\230\231\232\233\234\235\236\237\240\241\242\243\244\245\246\247\250\251\252\253\254\255\256\257\260\261\262\263\264\265\266\267\270\271\272\273\274\275\276\277\300\301\302\303\304\305\306\307\310\311\312\313\314\315\316\317\320\321\322\323\324\325\326\327\330\331\332\333\334\335\336\337\340\341\342\343\344\345\346\347\350\351\352\353\354\355\356\357\360\361\362\363\364\365\366\367\370\371\372\373\374\375\376\377\\") != ZSTR_LEN(name)) {
+ if (strspn(ZSTR_VAL(name), "0123456789_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ\200\201\202\203\204\205\206\207\210\211\212\213\214\215\216\217\220\221\222\223\224\225\226\227\230\231\232\233\234\235\236\237\240\241\242\243\244\245\246\247\250\251\252\253\254\255\256\257\260\261\262\263\264\265\266\267\270\271\272\273\274\275\276\277\300\301\302\303\304\305\306\307\310\311\312\313\314\315\316\317\320\321\322\323\324\325\326\327\330\331\332\333\334\335\336\337\340\341\342\343\344\345\346\347\350\351\352\353\354\355\356\357\360\361\362\363\364\365\366\367\370\371\372\373\374\375\376\377\\") != ZSTR_LEN(name)) {
if (!key) {
zend_string_release(lc_name);
}
@@ -1028,9 +993,7 @@ ZEND_API zend_class_entry *zend_lookup_class_ex(zend_string *name, const zval *k
}
fcall_info.size = sizeof(fcall_info);
- fcall_info.function_table = EG(function_table);
ZVAL_STR_COPY(&fcall_info.function_name, EG(autoload_func)->common.function_name);
- fcall_info.symbol_table = NULL;
fcall_info.retval = &local_retval;
fcall_info.param_count = 1;
fcall_info.params = args;
@@ -1072,11 +1035,13 @@ ZEND_API zend_class_entry *zend_lookup_class(zend_string *name) /* {{{ */
ZEND_API zend_class_entry *zend_get_called_scope(zend_execute_data *ex) /* {{{ */
{
while (ex) {
- if (ex->called_scope) {
- return ex->called_scope;
+ if (Z_TYPE(ex->This) == IS_OBJECT) {
+ return Z_OBJCE(ex->This);
+ } else if (Z_CE(ex->This)) {
+ return Z_CE(ex->This);
} else if (ex->func) {
if (ex->func->type != ZEND_INTERNAL_FUNCTION || ex->func->common.scope) {
- return ex->called_scope;
+ return NULL;
}
}
ex = ex->prev_execute_data;
@@ -1088,11 +1053,11 @@ ZEND_API zend_class_entry *zend_get_called_scope(zend_execute_data *ex) /* {{{ *
ZEND_API zend_object *zend_get_this_object(zend_execute_data *ex) /* {{{ */
{
while (ex) {
- if (Z_OBJ(ex->This)) {
+ if (Z_TYPE(ex->This) == IS_OBJECT) {
return Z_OBJ(ex->This);
} else if (ex->func) {
if (ex->func->type != ZEND_INTERNAL_FUNCTION || ex->func->common.scope) {
- return Z_OBJ(ex->This);
+ return NULL;
}
}
ex = ex->prev_execute_data;
@@ -1188,8 +1153,63 @@ ZEND_API int zend_eval_string_ex(char *str, zval *retval_ptr, char *string_name,
}
/* }}} */
-ZEND_API void zend_timeout(int dummy) /* {{{ */
+static void zend_set_timeout_ex(zend_long seconds, int reset_signals);
+
+ZEND_API ZEND_NORETURN void zend_timeout(int dummy) /* {{{ */
{
+#if defined(PHP_WIN32)
+# ifndef ZTS
+ /* No action is needed if we're timed out because zero seconds are
+ just ignored. Also, the hard timeout needs to be respected. If the
+ timer is not restarted properly, it could hang in the shutdown
+ function. */
+ if (EG(hard_timeout) > 0) {
+ EG(timed_out) = 0;
+ zend_set_timeout_ex(EG(hard_timeout), 1);
+ /* XXX Abused, introduce an additional flag if the value needs to be kept. */
+ EG(hard_timeout) = 0;
+ }
+# endif
+#else
+ EG(timed_out) = 0;
+ zend_set_timeout_ex(0, 1);
+#endif
+
+ zend_error_noreturn(E_ERROR, "Maximum execution time of %pd second%s exceeded", EG(timeout_seconds), EG(timeout_seconds) == 1 ? "" : "s");
+}
+/* }}} */
+
+#ifndef ZEND_WIN32
+static void zend_timeout_handler(int dummy) /* {{{ */
+{
+#ifndef ZTS
+ if (EG(timed_out)) {
+ /* Die on hard timeout */
+ const char *error_filename = NULL;
+ uint error_lineno = 0;
+ char *log_buffer = NULL;
+
+ if (zend_is_compiling()) {
+ error_filename = ZSTR_VAL(zend_get_compiled_filename());
+ error_lineno = zend_get_compiled_lineno();
+ } else if (zend_is_executing()) {
+ error_filename = zend_get_executed_filename();
+ if (error_filename[0] == '[') { /* [no active file] */
+ error_filename = NULL;
+ error_lineno = 0;
+ } else {
+ error_lineno = zend_get_executed_lineno();
+ }
+ }
+ if (!error_filename) {
+ error_filename = "Unknown";
+ }
+
+ zend_spprintf(&log_buffer, 0, "\nFatal error: Maximum execution time of %pd+%pd seconds exceeded (terminated) in %s on line %d\n", EG(timeout_seconds), EG(hard_timeout), error_filename, error_lineno);
+ write(2, log_buffer, strlen(log_buffer));
+ _exit(1);
+ }
+#endif
if (zend_on_timeout) {
#ifdef ZEND_SIGNALS
@@ -1204,9 +1224,17 @@ ZEND_API void zend_timeout(int dummy) /* {{{ */
zend_on_timeout(EG(timeout_seconds));
}
- zend_error_noreturn(E_ERROR, "Maximum execution time of %pd second%s exceeded", EG(timeout_seconds), EG(timeout_seconds) == 1 ? "" : "s");
+ EG(timed_out) = 1;
+
+#ifndef ZTS
+ if (EG(hard_timeout) > 0) {
+ /* Set hard timeout */
+ zend_set_timeout_ex(EG(hard_timeout), 1);
+ }
+#endif
}
/* }}} */
+#endif
#ifdef ZEND_WIN32
VOID CALLBACK tq_timer_cb(PVOID arg, BOOLEAN timed_out)
@@ -1229,11 +1257,9 @@ VOID CALLBACK tq_timer_cb(PVOID arg, BOOLEAN timed_out)
#define SIGPROF 27
#endif
-void zend_set_timeout(zend_long seconds, int reset_signals) /* {{{ */
+static void zend_set_timeout_ex(zend_long seconds, int reset_signals) /* {{{ */
{
- EG(timeout_seconds) = seconds;
-
#ifdef ZEND_WIN32
if(!seconds) {
return;
@@ -1244,7 +1270,6 @@ void zend_set_timeout(zend_long seconds, int reset_signals) /* {{{ */
delete and recreate. */
if (NULL != tq_timer) {
if (!DeleteTimerQueueTimer(NULL, tq_timer, NULL)) {
- EG(timed_out) = 0;
tq_timer = NULL;
zend_error_noreturn(E_ERROR, "Could not delete queued timer");
return;
@@ -1254,12 +1279,10 @@ void zend_set_timeout(zend_long seconds, int reset_signals) /* {{{ */
/* XXX passing NULL means the default timer queue provided by the system is used */
if (!CreateTimerQueueTimer(&tq_timer, NULL, (WAITORTIMERCALLBACK)tq_timer_cb, (VOID*)&EG(timed_out), seconds*1000, 0, WT_EXECUTEONLYONCE)) {
- EG(timed_out) = 0;
tq_timer = NULL;
zend_error_noreturn(E_ERROR, "Could not queue new timer");
return;
}
- EG(timed_out) = 0;
#else
# ifdef HAVE_SETITIMER
{
@@ -1282,15 +1305,23 @@ void zend_set_timeout(zend_long seconds, int reset_signals) /* {{{ */
if (reset_signals) {
# ifdef ZEND_SIGNALS
- zend_signal(signo, zend_timeout);
+ zend_signal(signo, zend_timeout_handler);
# else
sigset_t sigset;
-
- signal(signo, zend_timeout);
+# ifdef HAVE_SIGACTION
+ struct sigaction act;
+
+ act.sa_handler = zend_timeout_handler;
+ sigemptyset(&act.sa_mask);
+ act.sa_flags = SA_RESETHAND | SA_NODEFER;
+ sigaction(signo, &act, NULL);
+# else
+ signal(signo, zend_timeout_handler);
+# endif /* HAVE_SIGACTION */
sigemptyset(&sigset);
sigaddset(&sigset, signo);
sigprocmask(SIG_UNBLOCK, &sigset, NULL);
-# endif
+# endif /* ZEND_SIGNALS */
}
}
# endif /* HAVE_SETITIMER */
@@ -1298,6 +1329,15 @@ void zend_set_timeout(zend_long seconds, int reset_signals) /* {{{ */
}
/* }}} */
+void zend_set_timeout(zend_long seconds, int reset_signals) /* {{{ */
+{
+
+ EG(timeout_seconds) = seconds;
+ zend_set_timeout_ex(seconds, reset_signals);
+ EG(timed_out) = 0;
+}
+/* }}} */
+
void zend_unset_timeout(void) /* {{{ */
{
#ifdef ZEND_WIN32
@@ -1325,31 +1365,34 @@ void zend_unset_timeout(void) /* {{{ */
#endif
}
# endif
+ EG(timed_out) = 0;
#endif
}
/* }}} */
zend_class_entry *zend_fetch_class(zend_string *class_name, int fetch_type) /* {{{ */
{
- zend_class_entry *ce;
+ zend_class_entry *ce, *scope;
int fetch_sub_type = fetch_type & ZEND_FETCH_CLASS_MASK;
check_fetch_type:
switch (fetch_sub_type) {
case ZEND_FETCH_CLASS_SELF:
- if (UNEXPECTED(!EG(scope))) {
+ scope = zend_get_executed_scope();
+ if (UNEXPECTED(!scope)) {
zend_throw_or_error(fetch_type, NULL, "Cannot access self:: when no class scope is active");
}
- return EG(scope);
+ return scope;
case ZEND_FETCH_CLASS_PARENT:
- if (UNEXPECTED(!EG(scope))) {
+ scope = zend_get_executed_scope();
+ if (UNEXPECTED(!scope)) {
zend_throw_or_error(fetch_type, NULL, "Cannot access parent:: when no class scope is active");
return NULL;
}
- if (UNEXPECTED(!EG(scope)->parent)) {
+ if (UNEXPECTED(!scope->parent)) {
zend_throw_or_error(fetch_type, NULL, "Cannot access parent:: when current class scope has no parent");
}
- return EG(scope)->parent;
+ return scope->parent;
case ZEND_FETCH_CLASS_STATIC:
ce = zend_get_called_scope(EG(current_execute_data));
if (UNEXPECTED(!ce)) {
@@ -1484,10 +1527,11 @@ ZEND_API zend_array *zend_rebuild_symbol_table(void) /* {{{ */
if (!ex) {
return NULL;
}
- if (ex->symbol_table) {
+ if (ZEND_CALL_INFO(ex) & ZEND_CALL_HAS_SYMBOL_TABLE) {
return ex->symbol_table;
}
+ ZEND_ADD_CALL_FLAG(ex, ZEND_CALL_HAS_SYMBOL_TABLE);
if (EG(symtable_cache_ptr) >= EG(symtable_cache)) {
/*printf("Cache hit! Reusing %x\n", symtable_cache[symtable_cache_ptr]);*/
symbol_table = ex->symbol_table = *(EG(symtable_cache_ptr)--);
@@ -1588,7 +1632,7 @@ ZEND_API int zend_set_local_var(zend_string *name, zval *value, int force) /* {{
}
if (execute_data) {
- if (!execute_data->symbol_table) {
+ if (!(EX_CALL_INFO() & ZEND_CALL_HAS_SYMBOL_TABLE)) {
zend_ulong h = zend_string_hash_val(name);
zend_op_array *op_array = &execute_data->func->op_array;
@@ -1630,7 +1674,7 @@ ZEND_API int zend_set_local_var_str(const char *name, size_t len, zval *value, i
}
if (execute_data) {
- if (!execute_data->symbol_table) {
+ if (!(EX_CALL_INFO() & ZEND_CALL_HAS_SYMBOL_TABLE)) {
zend_ulong h = zend_hash_func(name, len);
zend_op_array *op_array = &execute_data->func->op_array;
if (EXPECTED(op_array->last_var)) {
@@ -1663,6 +1707,20 @@ ZEND_API int zend_set_local_var_str(const char *name, size_t len, zval *value, i
}
/* }}} */
+ZEND_API int zend_forbid_dynamic_call(const char *func_name) /* {{{ */
+{
+ zend_execute_data *ex = EG(current_execute_data);
+ ZEND_ASSERT(ex != NULL && ex->func != NULL);
+
+ if (ZEND_CALL_INFO(ex) & ZEND_CALL_DYNAMIC) {
+ zend_error(E_WARNING, "Cannot call %s dynamically", func_name);
+ return FAILURE;
+ }
+
+ return SUCCESS;
+}
+/* }}} */
+
/*
* Local variables:
* tab-width: 4
diff --git a/Zend/zend_extensions.h b/Zend/zend_extensions.h
index 06cb43d2ed..862c18ad75 100644
--- a/Zend/zend_extensions.h
+++ b/Zend/zend_extensions.h
@@ -25,10 +25,28 @@
#include "zend_compile.h"
#include "zend_build.h"
+/*
+The constants below are derived from ext/opcache/ZendAccelerator.h
+
+You can use the following macro to check the extension API version for compatibilities:
+
+#define ZEND_EXTENSION_API_NO_5_0_X 220040412
+#define ZEND_EXTENSION_API_NO_5_1_X 220051025
+#define ZEND_EXTENSION_API_NO_5_2_X 220060519
+#define ZEND_EXTENSION_API_NO_5_3_X 220090626
+#define ZEND_EXTENSION_API_NO_5_4_X 220100525
+#define ZEND_EXTENSION_API_NO_5_5_X 220121212
+#define ZEND_EXTENSION_API_NO_5_6_X 220131226
+#define ZEND_EXTENSION_API_NO_7_0_X 320151012
+
+#if ZEND_EXTENSION_API_NO < ZEND_EXTENSION_API_NO_5_5_X
+ // do something for php versions lower than 5.5.x
+#endif
+*/
+
/* The first number is the engine version and the rest is the date (YYYYMMDD).
- * This way engine 2/3 API no. is always greater than engine 1 API no..
- */
-#define ZEND_EXTENSION_API_NO 320151012
+ * This way engine 2/3 API no. is always greater than engine 1 API no.. */
+#define ZEND_EXTENSION_API_NO 320160303
typedef struct _zend_extension_version_info {
int zend_extension_api_no;
@@ -49,9 +67,9 @@ typedef void (*message_handler_func_t)(int message, void *arg);
typedef void (*op_array_handler_func_t)(zend_op_array *op_array);
-typedef void (*statement_handler_func_t)(zend_op_array *op_array);
-typedef void (*fcall_begin_handler_func_t)(zend_op_array *op_array);
-typedef void (*fcall_end_handler_func_t)(zend_op_array *op_array);
+typedef void (*statement_handler_func_t)(zend_execute_data *frame);
+typedef void (*fcall_begin_handler_func_t)(zend_execute_data *frame);
+typedef void (*fcall_end_handler_func_t)(zend_execute_data *frame);
typedef void (*op_array_ctor_func_t)(zend_op_array *op_array);
typedef void (*op_array_dtor_func_t)(zend_op_array *op_array);
diff --git a/Zend/zend_gc.c b/Zend/zend_gc.c
index 821ac4d9dc..87f4fb87d6 100644
--- a/Zend/zend_gc.c
+++ b/Zend/zend_gc.c
@@ -243,7 +243,7 @@ ZEND_API void ZEND_FASTCALL gc_possible_root(zend_refcounted *ref)
gc_collect_cycles();
GC_REFCOUNT(ref)--;
if (UNEXPECTED(GC_REFCOUNT(ref)) == 0) {
- zval_dtor_func_for_ptr(ref);
+ zval_dtor_func(ref);
return;
}
if (UNEXPECTED(GC_INFO(ref))) {
diff --git a/Zend/zend_generators.c b/Zend/zend_generators.c
index 89b919a48c..8460b9da14 100644
--- a/Zend/zend_generators.c
+++ b/Zend/zend_generators.c
@@ -31,6 +31,71 @@ static zend_object_handlers zend_generator_handlers;
static zend_object *zend_generator_create(zend_class_entry *class_type);
+static void zend_restore_call_stack(zend_generator *generator) /* {{{ */
+{
+ zend_execute_data *call, *new_call, *prev_call = NULL;
+
+ call = generator->frozen_call_stack;
+ do {
+ new_call = zend_vm_stack_push_call_frame(
+ (ZEND_CALL_INFO(call) & ~ZEND_CALL_ALLOCATED),
+ call->func,
+ ZEND_CALL_NUM_ARGS(call),
+ (Z_TYPE(call->This) == IS_UNDEF) ?
+ (zend_class_entry*)Z_OBJ(call->This) : NULL,
+ (Z_TYPE(call->This) != IS_UNDEF) ?
+ Z_OBJ(call->This) : NULL);
+ memcpy(((zval*)new_call) + ZEND_CALL_FRAME_SLOT, ((zval*)call) + ZEND_CALL_FRAME_SLOT, ZEND_CALL_NUM_ARGS(call) * sizeof(zval));
+ new_call->prev_execute_data = prev_call;
+ prev_call = new_call;
+
+ call = call->prev_execute_data;
+ } while (call);
+ generator->execute_data->call = prev_call;
+ efree(generator->frozen_call_stack);
+ generator->frozen_call_stack = NULL;
+}
+/* }}} */
+
+static zend_execute_data* zend_freeze_call_stack(zend_execute_data *execute_data) /* {{{ */
+{
+ size_t used_stack;
+ zend_execute_data *call, *new_call, *prev_call = NULL;
+ zval *stack;
+
+ /* calculate required stack size */
+ used_stack = 0;
+ call = EX(call);
+ do {
+ used_stack += ZEND_CALL_FRAME_SLOT + ZEND_CALL_NUM_ARGS(call);
+ call = call->prev_execute_data;
+ } while (call);
+
+ stack = emalloc(used_stack * sizeof(zval));
+
+ /* save stack, linking frames in reverse order */
+ call = EX(call);
+ do {
+ size_t frame_size = ZEND_CALL_FRAME_SLOT + ZEND_CALL_NUM_ARGS(call);
+
+ new_call = (zend_execute_data*)(stack + used_stack - frame_size);
+ memcpy(new_call, call, frame_size * sizeof(zval));
+ used_stack -= frame_size;
+ new_call->prev_execute_data = prev_call;
+ prev_call = new_call;
+
+ new_call = call->prev_execute_data;
+ zend_vm_stack_free_call_frame(call);
+ call = new_call;
+ } while (call);
+
+ execute_data->call = NULL;
+ ZEND_ASSERT(prev_call == (zend_execute_data*)stack);
+
+ return prev_call;
+}
+/* }}} */
+
static void zend_generator_cleanup_unfinished_execution(
zend_generator *generator, uint32_t catch_op_num) /* {{{ */
{
@@ -40,21 +105,10 @@ static void zend_generator_cleanup_unfinished_execution(
/* -1 required because we want the last run opcode, not the next to-be-run one. */
uint32_t op_num = execute_data->opline - execute_data->func->op_array.opcodes - 1;
- /* There may be calls to zend_vm_stack_free_call_frame(), which modifies the VM stack
- * globals, so need to load/restore those. */
- zend_vm_stack original_stack = EG(vm_stack);
- original_stack->top = EG(vm_stack_top);
- EG(vm_stack_top) = generator->stack->top;
- EG(vm_stack_end) = generator->stack->end;
- EG(vm_stack) = generator->stack;
-
- zend_cleanup_unfinished_execution(execute_data, op_num, catch_op_num);
-
- generator->stack = EG(vm_stack);
- generator->stack->top = EG(vm_stack_top);
- EG(vm_stack_top) = original_stack->top;
- EG(vm_stack_end) = original_stack->end;
- EG(vm_stack) = original_stack;
+ if (UNEXPECTED(generator->frozen_call_stack)) {
+ zend_restore_call_stack(generator);
+ }
+ zend_cleanup_unfinished_execution(execute_data, op_num, 0);
}
}
/* }}} */
@@ -64,13 +118,13 @@ ZEND_API void zend_generator_close(zend_generator *generator, zend_bool finished
if (EXPECTED(generator->execute_data)) {
zend_execute_data *execute_data = generator->execute_data;
- if (execute_data->symbol_table) {
+ if (EX_CALL_INFO() & ZEND_CALL_HAS_SYMBOL_TABLE) {
zend_clean_and_cache_symbol_table(execute_data->symbol_table);
}
/* always free the CV's, in the symtable are only not-free'd IS_INDIRECT's */
zend_free_compiled_variables(execute_data);
- if (Z_OBJ(execute_data->This)) {
+ if (EX_CALL_INFO() & ZEND_CALL_RELEASE_THIS) {
OBJ_RELEASE(Z_OBJ(execute_data->This));
}
@@ -94,7 +148,13 @@ ZEND_API void zend_generator_close(zend_generator *generator, zend_bool finished
OBJ_RELEASE((zend_object *) EX(func)->common.prototype);
}
- efree(generator->stack);
+ /* Free GC buffer. GC for closed generators doesn't need an allocated buffer */
+ if (generator->gc_buffer) {
+ efree(generator->gc_buffer);
+ generator->gc_buffer = NULL;
+ }
+
+ efree(generator->execute_data);
generator->execute_data = NULL;
}
}
@@ -194,12 +254,103 @@ static void zend_generator_free_storage(zend_object *object) /* {{{ */
}
/* }}} */
+static uint32_t calc_gc_buffer_size(zend_generator *generator) /* {{{ */
+{
+ uint32_t size = 4; /* value, key, retval, values */
+ if (generator->execute_data) {
+ zend_execute_data *execute_data = generator->execute_data;
+ zend_op_array *op_array = &EX(func)->op_array;
+
+ /* Compiled variables */
+ if (!(EX_CALL_INFO() & ZEND_CALL_HAS_SYMBOL_TABLE)) {
+ size += op_array->last_var;
+ }
+ /* Extra args */
+ if (EX_CALL_INFO() & ZEND_CALL_FREE_EXTRA_ARGS) {
+ size += EX_NUM_ARGS() - op_array->num_args;
+ }
+ size += Z_TYPE(execute_data->This) == IS_OBJECT; /* $this */
+ size += (EX_CALL_INFO() & ZEND_CALL_CLOSURE) != 0; /* Closure object */
+
+ /* Yield from root references */
+ if (generator->node.children == 0) {
+ zend_generator *child = generator, *root = generator->node.ptr.root;
+ while (root != child) {
+ child = child->node.parent;
+ size++;
+ }
+ }
+ }
+ return size;
+}
+/* }}} */
+
static HashTable *zend_generator_get_gc(zval *object, zval **table, int *n) /* {{{ */
{
zend_generator *generator = (zend_generator*) Z_OBJ_P(object);
- *table = &generator->value;
- *n = 3;
- return NULL;
+ zend_execute_data *execute_data = generator->execute_data;
+ zend_op_array *op_array;
+ zval *gc_buffer;
+ uint32_t gc_buffer_size;
+
+ if (!execute_data) {
+ /* If the generator has been closed, it can only hold on to three values: The value, key
+ * and retval. These three zvals are stored sequentially starting at &generator->value. */
+ *table = &generator->value;
+ *n = 3;
+ return NULL;
+ }
+
+ op_array = &EX(func)->op_array;
+ gc_buffer_size = calc_gc_buffer_size(generator);
+ if (generator->gc_buffer_size < gc_buffer_size) {
+ generator->gc_buffer = safe_erealloc(generator->gc_buffer, sizeof(zval), gc_buffer_size, 0);
+ generator->gc_buffer_size = gc_buffer_size;
+ }
+
+ *n = gc_buffer_size;
+ *table = gc_buffer = generator->gc_buffer;
+
+ ZVAL_COPY_VALUE(gc_buffer++, &generator->value);
+ ZVAL_COPY_VALUE(gc_buffer++, &generator->key);
+ ZVAL_COPY_VALUE(gc_buffer++, &generator->retval);
+ ZVAL_COPY_VALUE(gc_buffer++, &generator->values);
+
+ if (!(EX_CALL_INFO() & ZEND_CALL_HAS_SYMBOL_TABLE)) {
+ uint32_t i, num_cvs = EX(func)->op_array.last_var;
+ for (i = 0; i < num_cvs; i++) {
+ ZVAL_COPY_VALUE(gc_buffer++, EX_VAR_NUM(i));
+ }
+ }
+
+ if (EX_CALL_INFO() & ZEND_CALL_FREE_EXTRA_ARGS) {
+ zval *zv = EX_VAR_NUM(op_array->last_var + op_array->T);
+ zval *end = zv + (EX_NUM_ARGS() - op_array->num_args);
+ while (zv != end) {
+ ZVAL_COPY_VALUE(gc_buffer++, zv++);
+ }
+ }
+
+ if (Z_TYPE(execute_data->This) == IS_OBJECT) {
+ ZVAL_OBJ(gc_buffer++, Z_OBJ(execute_data->This));
+ }
+ if (EX_CALL_INFO() & ZEND_CALL_CLOSURE) {
+ ZVAL_OBJ(gc_buffer++, (zend_object *) EX(func)->common.prototype);
+ }
+
+ if (generator->node.children == 0) {
+ zend_generator *child = generator, *root = generator->node.ptr.root;
+ while (root != child) {
+ child = child->node.parent;
+ ZVAL_OBJ(gc_buffer++, &child->std);
+ }
+ }
+
+ if (EX_CALL_INFO() & ZEND_CALL_HAS_SYMBOL_TABLE) {
+ return execute_data->symbol_table;
+ } else {
+ return NULL;
+ }
}
/* }}} */
@@ -228,44 +379,6 @@ static zend_object *zend_generator_create(zend_class_entry *class_type) /* {{{ *
}
/* }}} */
-/* Requires globals EG(scope), EG(This) and EG(current_execute_data). */
-ZEND_API void zend_generator_create_zval(zend_execute_data *call, zend_op_array *op_array, zval *return_value) /* {{{ */
-{
- zend_generator *generator;
- zend_execute_data *current_execute_data;
- zend_execute_data *execute_data;
- zend_vm_stack current_stack = EG(vm_stack);
-
- current_stack->top = EG(vm_stack_top);
-
- /* Create new execution context. We have to back up and restore EG(current_execute_data) here. */
- current_execute_data = EG(current_execute_data);
- execute_data = zend_create_generator_execute_data(call, op_array, return_value);
- EG(current_execute_data) = current_execute_data;
-
- object_init_ex(return_value, zend_ce_generator);
-
- if (Z_OBJ(call->This)) {
- Z_ADDREF(call->This);
- }
-
- /* Save execution context in generator object. */
- generator = (zend_generator *) Z_OBJ_P(return_value);
- generator->execute_data = execute_data;
- generator->stack = EG(vm_stack);
- generator->stack->top = EG(vm_stack_top);
- EG(vm_stack_top) = current_stack->top;
- EG(vm_stack_end) = current_stack->end;
- EG(vm_stack) = current_stack;
-
- /* EX(return_value) keeps pointer to zend_object (not a real zval) */
- execute_data->return_value = (zval*)generator;
-
- memset(&generator->execute_fake, 0, sizeof(zend_execute_data));
- Z_OBJ(generator->execute_fake.This) = (zend_object *) generator;
-}
-/* }}} */
-
static ZEND_COLD zend_function *zend_generator_get_constructor(zend_object *object) /* {{{ */
{
zend_throw_error(NULL, "The \"Generator\" class is reserved for internal use and cannot be manually instantiated");
@@ -276,7 +389,7 @@ static ZEND_COLD zend_function *zend_generator_get_constructor(zend_object *obje
ZEND_API zend_execute_data *zend_generator_check_placeholder_frame(zend_execute_data *ptr)
{
- if (!ptr->func && Z_OBJ(ptr->This)) {
+ if (!ptr->func && Z_TYPE(ptr->This) == IS_OBJECT) {
if (Z_OBJCE(ptr->This) == zend_ce_generator) {
zend_generator *generator = (zend_generator *) Z_OBJ(ptr->This);
zend_generator *root = (generator->node.children < 1 ? generator : generator->node.ptr.leaf)->node.ptr.root;
@@ -297,6 +410,8 @@ ZEND_API zend_execute_data *zend_generator_check_placeholder_frame(zend_execute_
static void zend_generator_throw_exception(zend_generator *generator, zval *exception)
{
+ zend_execute_data *original_execute_data = EG(current_execute_data);
+
/* if we don't stop an array/iterator yield from, the exception will only reach the generator after the values were all iterated over */
if (UNEXPECTED(Z_TYPE(generator->values) != IS_UNDEF)) {
zval_ptr_dtor(&generator->values);
@@ -305,7 +420,6 @@ static void zend_generator_throw_exception(zend_generator *generator, zval *exce
/* Throw the exception in the context of the generator. Decrementing the opline
* to pretend the exception happened during the YIELD opcode. */
- zend_execute_data *original_execute_data = EG(current_execute_data);
EG(current_execute_data) = generator->execute_data;
generator->execute_data->opline--;
if (exception) {
@@ -666,16 +780,9 @@ try_again:
{
/* Backup executor globals */
zend_execute_data *original_execute_data = EG(current_execute_data);
- zend_class_entry *original_scope = EG(scope);
- zend_vm_stack original_stack = EG(vm_stack);
- original_stack->top = EG(vm_stack_top);
/* Set executor globals */
EG(current_execute_data) = generator->execute_data;
- EG(scope) = generator->execute_data->func->common.scope;
- EG(vm_stack_top) = generator->stack->top;
- EG(vm_stack_end) = generator->stack->end;
- EG(vm_stack) = generator->stack;
/* We want the backtrace to look as if the generator function was
* called from whatever method we are current running (e.g. next()).
@@ -689,23 +796,25 @@ try_again:
orig_generator->execute_fake.prev_execute_data = original_execute_data;
}
+ if (UNEXPECTED(generator->frozen_call_stack)) {
+ /* Restore frozen call-stack */
+ zend_restore_call_stack(generator);
+ }
+
/* Resume execution */
generator->flags |= ZEND_GENERATOR_CURRENTLY_RUNNING;
zend_execute_ex(generator->execute_data);
generator->flags &= ~ZEND_GENERATOR_CURRENTLY_RUNNING;
- /* Unlink generator call_frame from the caller and backup vm_stack_top */
- if (EXPECTED(generator->execute_data)) {
- generator->stack = EG(vm_stack);
- generator->stack->top = EG(vm_stack_top);
+ generator->frozen_call_stack = NULL;
+ if (EXPECTED(generator->execute_data) &&
+ UNEXPECTED(generator->execute_data->call)) {
+ /* Frize call-stack */
+ generator->frozen_call_stack = zend_freeze_call_stack(generator->execute_data);
}
/* Restore executor globals */
EG(current_execute_data) = original_execute_data;
- EG(scope) = original_scope;
- EG(vm_stack_top) = original_stack->top;
- EG(vm_stack_end) = original_stack->end;
- EG(vm_stack) = original_stack;
/* If an exception was thrown in the generator we have to internally
* rethrow it in the parent scope.
@@ -731,7 +840,7 @@ try_again:
}
/* }}} */
-static void inline zend_generator_ensure_initialized(zend_generator *generator) /* {{{ */
+static inline void zend_generator_ensure_initialized(zend_generator *generator) /* {{{ */
{
if (UNEXPECTED(Z_TYPE(generator->value) == IS_UNDEF) && EXPECTED(generator->execute_data) && EXPECTED(generator->node.parent == NULL)) {
generator->flags |= ZEND_GENERATOR_DO_INIT;
@@ -742,7 +851,7 @@ static void inline zend_generator_ensure_initialized(zend_generator *generator)
}
/* }}} */
-static void inline zend_generator_rewind(zend_generator *generator) /* {{{ */
+static inline void zend_generator_rewind(zend_generator *generator) /* {{{ */
{
zend_generator_ensure_initialized(generator);
@@ -1065,7 +1174,8 @@ static zend_object_iterator_funcs zend_generator_iterator_functions = {
zend_generator_iterator_get_data,
zend_generator_iterator_get_key,
zend_generator_iterator_move_forward,
- zend_generator_iterator_rewind
+ zend_generator_iterator_rewind,
+ NULL
};
zend_object_iterator *zend_generator_get_iterator(zend_class_entry *ce, zval *object, int by_ref) /* {{{ */
diff --git a/Zend/zend_generators.h b/Zend/zend_generators.h
index 26ee646b65..6aabcc6a4d 100644
--- a/Zend/zend_generators.h
+++ b/Zend/zend_generators.h
@@ -62,8 +62,8 @@ struct _zend_generator {
/* The suspended execution context. */
zend_execute_data *execute_data;
- /* The separate stack used by generator */
- zend_vm_stack stack;
+ /* Frozen call stack for "yield" used in context of other calls */
+ zend_execute_data *frozen_call_stack;
/* Current value */
zval value;
@@ -82,7 +82,7 @@ struct _zend_generator {
* by-value foreach. */
zval values;
- /* Node of waiting generators when multiple "yield *" expressions
+ /* Node of waiting generators when multiple "yield from" expressions
* are nested. */
zend_generator_node node;
@@ -91,6 +91,9 @@ struct _zend_generator {
/* ZEND_GENERATOR_* flags */
zend_uchar flags;
+
+ zval *gc_buffer;
+ uint32_t gc_buffer_size;
};
static const zend_uchar ZEND_GENERATOR_CURRENTLY_RUNNING = 0x1;
@@ -99,7 +102,6 @@ static const zend_uchar ZEND_GENERATOR_AT_FIRST_YIELD = 0x4;
static const zend_uchar ZEND_GENERATOR_DO_INIT = 0x8;
void zend_register_generator_ce(void);
-ZEND_API void zend_generator_create_zval(zend_execute_data *call, zend_op_array *op_array, zval *return_value);
ZEND_API void zend_generator_close(zend_generator *generator, zend_bool finished_execution);
ZEND_API void zend_generator_resume(zend_generator *generator);
diff --git a/Zend/zend_globals.h b/Zend/zend_globals.h
index a6a89377f7..b560038b7a 100644
--- a/Zend/zend_globals.h
+++ b/Zend/zend_globals.h
@@ -101,6 +101,7 @@ struct _zend_compiler_globals {
zend_bool increment_lineno;
zend_string *doc_comment;
+ uint32_t extra_fn_flags;
uint32_t compiler_options; /* set of ZEND_COMPILE_* constants */
@@ -113,6 +114,8 @@ struct _zend_compiler_globals {
zend_string *empty_string;
zend_string *one_char_string[256];
+ zend_string **known_strings;
+ uint32_t known_strings_count;
HashTable interned_strings;
@@ -161,7 +164,7 @@ struct _zend_executor_globals {
zend_vm_stack vm_stack;
struct _zend_execute_data *current_execute_data;
- zend_class_entry *scope;
+ zend_class_entry *fake_scope; /* used to avoid checks accessing properties */
zend_long precision;
@@ -174,8 +177,10 @@ struct _zend_executor_globals {
/* for extended information support */
zend_bool no_extensions;
-#ifdef ZEND_WIN32
zend_bool timed_out;
+ zend_long hard_timeout;
+
+#ifdef ZEND_WIN32
OSVERSIONINFOEX windows_version_info;
#endif
diff --git a/Zend/zend_globals_macros.h b/Zend/zend_globals_macros.h
index d2baef3555..c0a9efbfbf 100644
--- a/Zend/zend_globals_macros.h
+++ b/Zend/zend_globals_macros.h
@@ -68,34 +68,6 @@ extern ZEND_API zend_ini_scanner_globals ini_scanner_globals;
END_EXTERN_C()
-/* For limited downwards source compatibility */
-#define CLS_FETCH()
-#define ELS_FETCH()
-#define ALS_FETCH()
-#define PLS_FETCH()
-#define SLS_FETCH()
-#define CLS_D
-#define ELS_D
-#define ALS_D
-#define PLS_D
-#define SLS_D
-#define CLS_DC
-#define ELS_DC
-#define ALS_DC
-#define PLS_DC
-#define SLS_DC
-#define CLS_C
-#define ELS_C
-#define ALS_C
-#define PLS_C
-#define SLS_C
-#define CLS_CC
-#define ELS_CC
-#define ALS_CC
-#define PLS_CC
-#define SLS_CC
-
-
#endif /* ZEND_GLOBALS_MACROS_H */
/*
diff --git a/Zend/zend_hash.c b/Zend/zend_hash.c
index 5404187a97..95652d598d 100644
--- a/Zend/zend_hash.c
+++ b/Zend/zend_hash.c
@@ -92,7 +92,7 @@ static void _zend_is_inconsistent(const HashTable *ht, const char *file, int lin
static void ZEND_FASTCALL zend_hash_do_resize(HashTable *ht);
-static uint32_t zend_always_inline zend_hash_check_size(uint32_t nSize)
+static zend_always_inline uint32_t zend_hash_check_size(uint32_t nSize)
{
#if defined(ZEND_WIN32)
unsigned long index;
@@ -127,7 +127,7 @@ static uint32_t zend_always_inline zend_hash_check_size(uint32_t nSize)
#endif
}
-static void zend_always_inline zend_hash_real_init_ex(HashTable *ht, int packed)
+static zend_always_inline void zend_hash_real_init_ex(HashTable *ht, int packed)
{
HT_ASSERT(GC_REFCOUNT(ht) == 1);
ZEND_ASSERT(!((ht)->u.flags & HASH_FLAG_INITIALIZED));
@@ -139,7 +139,7 @@ static void zend_always_inline zend_hash_real_init_ex(HashTable *ht, int packed)
(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;
- if (EXPECTED(ht->nTableMask == -8)) {
+ if (EXPECTED(ht->nTableMask == (uint32_t)-8)) {
Bucket *arData = ht->arData;
HT_HASH_EX(arData, -8) = -1;
@@ -156,7 +156,7 @@ static void zend_always_inline zend_hash_real_init_ex(HashTable *ht, int packed)
}
}
-static void zend_always_inline zend_hash_check_init(HashTable *ht, int packed)
+static zend_always_inline void zend_hash_check_init(HashTable *ht, int packed)
{
HT_ASSERT(GC_REFCOUNT(ht) == 1);
if (UNEXPECTED(!((ht)->u.flags & HASH_FLAG_INITIALIZED))) {
@@ -1525,7 +1525,6 @@ ZEND_API void ZEND_FASTCALL zend_hash_apply(HashTable *ht, apply_func_t apply_fu
int result;
IS_CONSISTENT(ht);
- HT_ASSERT(GC_REFCOUNT(ht) == 1);
HASH_PROTECT_RECURSION(ht);
for (idx = 0; idx < ht->nNumUsed; idx++) {
@@ -1534,6 +1533,7 @@ ZEND_API void ZEND_FASTCALL zend_hash_apply(HashTable *ht, apply_func_t apply_fu
result = apply_func(&p->val);
if (result & ZEND_HASH_APPLY_REMOVE) {
+ HT_ASSERT(GC_REFCOUNT(ht) == 1);
_zend_hash_del_el(ht, HT_IDX_TO_HASH(idx), p);
}
if (result & ZEND_HASH_APPLY_STOP) {
@@ -1551,7 +1551,6 @@ ZEND_API void ZEND_FASTCALL zend_hash_apply_with_argument(HashTable *ht, apply_f
int result;
IS_CONSISTENT(ht);
- HT_ASSERT(GC_REFCOUNT(ht) == 1);
HASH_PROTECT_RECURSION(ht);
for (idx = 0; idx < ht->nNumUsed; idx++) {
@@ -1560,6 +1559,7 @@ ZEND_API void ZEND_FASTCALL zend_hash_apply_with_argument(HashTable *ht, apply_f
result = apply_func(&p->val, argument);
if (result & ZEND_HASH_APPLY_REMOVE) {
+ HT_ASSERT(GC_REFCOUNT(ht) == 1);
_zend_hash_del_el(ht, HT_IDX_TO_HASH(idx), p);
}
if (result & ZEND_HASH_APPLY_STOP) {
@@ -1579,7 +1579,6 @@ ZEND_API void ZEND_FASTCALL zend_hash_apply_with_arguments(HashTable *ht, apply_
int result;
IS_CONSISTENT(ht);
- HT_ASSERT(GC_REFCOUNT(ht) == 1);
HASH_PROTECT_RECURSION(ht);
@@ -1593,6 +1592,7 @@ ZEND_API void ZEND_FASTCALL zend_hash_apply_with_arguments(HashTable *ht, apply_
result = apply_func(&p->val, num_args, args, &hash_key);
if (result & ZEND_HASH_APPLY_REMOVE) {
+ HT_ASSERT(GC_REFCOUNT(ht) == 1);
_zend_hash_del_el(ht, HT_IDX_TO_HASH(idx), p);
}
if (result & ZEND_HASH_APPLY_STOP) {
@@ -1613,7 +1613,6 @@ ZEND_API void ZEND_FASTCALL zend_hash_reverse_apply(HashTable *ht, apply_func_t
int result;
IS_CONSISTENT(ht);
- HT_ASSERT(GC_REFCOUNT(ht) == 1);
HASH_PROTECT_RECURSION(ht);
idx = ht->nNumUsed;
@@ -1625,6 +1624,7 @@ ZEND_API void ZEND_FASTCALL zend_hash_reverse_apply(HashTable *ht, apply_func_t
result = apply_func(&p->val);
if (result & ZEND_HASH_APPLY_REMOVE) {
+ HT_ASSERT(GC_REFCOUNT(ht) == 1);
_zend_hash_del_el(ht, HT_IDX_TO_HASH(idx), p);
}
if (result & ZEND_HASH_APPLY_STOP) {
@@ -2038,6 +2038,15 @@ ZEND_API zval* ZEND_FASTCALL zend_hash_index_find(const HashTable *ht, zend_ulon
return p ? &p->val : NULL;
}
+ZEND_API zval* ZEND_FASTCALL _zend_hash_index_find(const HashTable *ht, zend_ulong h)
+{
+ Bucket *p;
+
+ IS_CONSISTENT(ht);
+
+ p = zend_hash_index_find_bucket(ht, h);
+ return p ? &p->val : NULL;
+}
ZEND_API zend_bool ZEND_FASTCALL zend_hash_index_exists(const HashTable *ht, zend_ulong h)
{
diff --git a/Zend/zend_hash.h b/Zend/zend_hash.h
index f4c2c30fbc..ba4704fdd9 100644
--- a/Zend/zend_hash.h
+++ b/Zend/zend_hash.h
@@ -154,6 +154,26 @@ ZEND_API void ZEND_FASTCALL zend_hash_del_bucket(HashTable *ht, Bucket *p);
ZEND_API zval* ZEND_FASTCALL zend_hash_find(const HashTable *ht, zend_string *key);
ZEND_API zval* ZEND_FASTCALL zend_hash_str_find(const HashTable *ht, const char *key, size_t len);
ZEND_API zval* ZEND_FASTCALL zend_hash_index_find(const HashTable *ht, zend_ulong h);
+ZEND_API zval* ZEND_FASTCALL _zend_hash_index_find(const HashTable *ht, zend_ulong h);
+
+#define ZEND_HASH_INDEX_FIND(_ht, _h, _ret, _not_found) do { \
+ if (EXPECTED((_ht)->u.flags & HASH_FLAG_PACKED)) { \
+ if (EXPECTED((_h) < (_ht)->nNumUsed)) { \
+ _ret = &_ht->arData[_h].val; \
+ if (UNEXPECTED(Z_TYPE_P(_ret) == IS_UNDEF)) { \
+ goto _not_found; \
+ } \
+ } else { \
+ goto _not_found; \
+ } \
+ } else { \
+ _ret = _zend_hash_index_find(_ht, _h); \
+ if (UNEXPECTED(_ret == NULL)) { \
+ goto _not_found; \
+ } \
+ } \
+ } while (0)
+
/* Misc */
ZEND_API zend_bool ZEND_FASTCALL zend_hash_exists(const HashTable *ht, zend_string *key);
@@ -248,7 +268,7 @@ END_EXTERN_C()
static zend_always_inline int _zend_handle_numeric_str(const char *key, size_t length, zend_ulong *idx)
{
- register const char *tmp = key;
+ const char *tmp = key;
if (*tmp > '9') {
return 0;
diff --git a/Zend/zend_inheritance.c b/Zend/zend_inheritance.c
index 845500c041..44abfb6ffb 100644
--- a/Zend/zend_inheritance.c
+++ b/Zend/zend_inheritance.c
@@ -319,13 +319,11 @@ static zend_bool zend_do_perform_implementation_check(const zend_function *fe, c
return 0;
}
-#if 0
// This introduces BC break described at https://bugs.php.net/bug.php?id=72119
if (proto_arg_info->type_hint && proto_arg_info->allow_null && !fe_arg_info->allow_null) {
/* incompatible nullability */
return 0;
}
-#endif
/* by-ref constraints on arguments are invariant */
if (fe_arg_info->pass_by_reference != proto_arg_info->pass_by_reference) {
@@ -344,6 +342,10 @@ static zend_bool zend_do_perform_implementation_check(const zend_function *fe, c
if (!zend_do_perform_type_hint_check(fe, fe->common.arg_info - 1, proto, proto->common.arg_info - 1)) {
return 0;
}
+
+ if (fe->common.arg_info[-1].allow_null && !proto->common.arg_info[-1].allow_null) {
+ return 0;
+ }
}
return 1;
}
@@ -351,6 +353,11 @@ static zend_bool zend_do_perform_implementation_check(const zend_function *fe, c
static ZEND_COLD void zend_append_type_hint(smart_str *str, const zend_function *fptr, zend_arg_info *arg_info, int return_hint) /* {{{ */
{
+
+ if (arg_info->type_hint != IS_UNDEF && arg_info->allow_null) {
+ smart_str_appendc(str, '?');
+ }
+
if (arg_info->class_name) {
const char *class_name;
size_t class_name_len;
@@ -491,8 +498,6 @@ static ZEND_COLD zend_string *zend_get_function_declaration(const zend_function
} else {
smart_str_appends(&str, "NULL");
}
- } else if (arg_info->type_hint && arg_info->allow_null) {
- smart_str_appends(&str, " = NULL");
}
if (++i < num_args) {
@@ -549,17 +554,17 @@ static void do_inheritance_check_on_method(zend_function *child, zend_function *
zend_error_noreturn(E_COMPILE_ERROR, "Cannot make non abstract method %s::%s() abstract in class %s", ZEND_FN_SCOPE_NAME(parent), ZSTR_VAL(child->common.function_name), ZEND_FN_SCOPE_NAME(child));
}
+ /* Prevent derived classes from restricting access that was available in parent classes */
+ if (UNEXPECTED((child_flags & ZEND_ACC_PPP_MASK) > (parent_flags & ZEND_ACC_PPP_MASK))) {
+ zend_error_noreturn(E_COMPILE_ERROR, "Access level to %s::%s() must be %s (as in class %s)%s", ZEND_FN_SCOPE_NAME(child), ZSTR_VAL(child->common.function_name), zend_visibility_string(parent_flags), ZEND_FN_SCOPE_NAME(parent), (parent_flags&ZEND_ACC_PUBLIC) ? "" : " or weaker");
+ }
+
+ if (((child_flags & ZEND_ACC_PPP_MASK) < (parent_flags & ZEND_ACC_PPP_MASK))
+ && ((parent_flags & ZEND_ACC_PPP_MASK) & ZEND_ACC_PRIVATE)) {
+ child->common.fn_flags |= ZEND_ACC_CHANGED;
+ }
if (parent_flags & ZEND_ACC_CHANGED) {
child->common.fn_flags |= ZEND_ACC_CHANGED;
- } else {
- /* Prevent derived classes from restricting access that was available in parent classes
- */
- if (UNEXPECTED((child_flags & ZEND_ACC_PPP_MASK) > (parent_flags & ZEND_ACC_PPP_MASK))) {
- zend_error_noreturn(E_COMPILE_ERROR, "Access level to %s::%s() must be %s (as in class %s)%s", ZEND_FN_SCOPE_NAME(child), ZSTR_VAL(child->common.function_name), zend_visibility_string(parent_flags), ZEND_FN_SCOPE_NAME(parent), (parent_flags&ZEND_ACC_PUBLIC) ? "" : " or weaker");
- } else if (((child_flags & ZEND_ACC_PPP_MASK) < (parent_flags & ZEND_ACC_PPP_MASK))
- && ((parent_flags & ZEND_ACC_PPP_MASK) & ZEND_ACC_PRIVATE)) {
- child->common.fn_flags |= ZEND_ACC_CHANGED;
- }
}
if (parent_flags & ZEND_ACC_PRIVATE) {
@@ -590,7 +595,8 @@ static void do_inheritance_check_on_method(zend_function *child, zend_function *
error_verb = "must";
} else if ((parent->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) &&
(!(child->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) ||
- !zend_do_perform_type_hint_check(child, child->common.arg_info - 1, parent, parent->common.arg_info - 1))) {
+ !zend_do_perform_type_hint_check(child, child->common.arg_info - 1, parent, parent->common.arg_info - 1) ||
+ (child->common.arg_info[-1].allow_null && !parent->common.arg_info[-1].allow_null))) {
error_level = E_COMPILE_ERROR;
error_verb = "must";
} else {
@@ -660,7 +666,7 @@ static void do_inherit_property(zend_property_info *parent_info, zend_string *ke
int parent_num = OBJ_PROP_TO_NUM(parent_info->offset);
int child_num = OBJ_PROP_TO_NUM(child_info->offset);
- /* Don't keep default properties in GC (thry may be freed by opcache) */
+ /* Don't keep default properties in GC (they may be freed by opcache) */
zval_ptr_dtor_nogc(&(ce->default_properties_table[parent_num]));
ce->default_properties_table[parent_num] = ce->default_properties_table[child_num];
ZVAL_UNDEF(&ce->default_properties_table[child_num]);
@@ -736,21 +742,29 @@ ZEND_API void zend_do_inherit_interfaces(zend_class_entry *ce, const zend_class_
}
/* }}} */
-static void do_inherit_class_constant(zend_string *name, zval *zv, zend_class_entry *ce, zend_class_entry *parent_ce) /* {{{ */
+static void do_inherit_class_constant(zend_string *name, zend_class_constant *parent_const, zend_class_entry *ce) /* {{{ */
{
- if (!zend_hash_exists(&ce->constants_table, name)) {
- if (!Z_ISREF_P(zv)) {
- if (parent_ce->type == ZEND_INTERNAL_CLASS) {
- ZVAL_NEW_PERSISTENT_REF(zv, zv);
- } else {
- ZVAL_NEW_REF(zv, zv);
- }
+ zend_class_constant *c = zend_hash_find_ptr(&ce->constants_table, name);
+
+ if (c != NULL) {
+ if (UNEXPECTED((Z_ACCESS_FLAGS(c->value) & ZEND_ACC_PPP_MASK) > (Z_ACCESS_FLAGS(parent_const->value) & ZEND_ACC_PPP_MASK))) {
+ zend_error_noreturn(E_COMPILE_ERROR, "Access level to %s::%s must be %s (as in class %s)%s",
+ ZSTR_VAL(ce->name), ZSTR_VAL(name), zend_visibility_string(Z_ACCESS_FLAGS(parent_const->value)), ZSTR_VAL(ce->parent->name), (Z_ACCESS_FLAGS(parent_const->value) & ZEND_ACC_PUBLIC) ? "" : " or weaker");
}
- if (Z_CONSTANT_P(Z_REFVAL_P(zv))) {
+ } else if (!(Z_ACCESS_FLAGS(parent_const->value) & ZEND_ACC_PRIVATE)) {
+ if (Z_CONSTANT(parent_const->value)) {
ce->ce_flags &= ~ZEND_ACC_CONSTANTS_UPDATED;
}
- Z_ADDREF_P(zv);
- _zend_hash_append(&ce->constants_table, name, zv);
+ if (ce->type & ZEND_INTERNAL_CLASS) {
+ if (Z_REFCOUNTED(parent_const->value)) {
+ Z_ADDREF(parent_const->value);
+ }
+ c = pemalloc(sizeof(zend_class_constant), 1);
+ memcpy(c, parent_const, sizeof(zend_class_constant));
+ } else {
+ c = parent_const;
+ }
+ _zend_hash_append_ptr(&ce->constants_table, name, c);
}
}
/* }}} */
@@ -760,7 +774,6 @@ ZEND_API void zend_do_inheritance(zend_class_entry *ce, zend_class_entry *parent
zend_property_info *property_info;
zend_function *func;
zend_string *key;
- zval *zv;
if (UNEXPECTED(ce->ce_flags & ZEND_ACC_INTERFACE)) {
/* Interface can only inherit other interfaces */
@@ -897,12 +910,14 @@ ZEND_API void zend_do_inheritance(zend_class_entry *ce, zend_class_entry *parent
}
if (zend_hash_num_elements(&parent_ce->constants_table)) {
+ zend_class_constant *c;
+
zend_hash_extend(&ce->constants_table,
zend_hash_num_elements(&ce->constants_table) +
zend_hash_num_elements(&parent_ce->constants_table), 0);
- ZEND_HASH_FOREACH_STR_KEY_VAL(&parent_ce->constants_table, key, zv) {
- do_inherit_class_constant(key, zv, ce, parent_ce);
+ ZEND_HASH_FOREACH_STR_KEY_PTR(&parent_ce->constants_table, key, c) {
+ do_inherit_class_constant(key, c, ce);
} ZEND_HASH_FOREACH_END();
}
@@ -932,14 +947,12 @@ ZEND_API void zend_do_inheritance(zend_class_entry *ce, zend_class_entry *parent
}
/* }}} */
-static zend_bool do_inherit_constant_check(HashTable *child_constants_table, zval *parent_constant, zend_string *name, const zend_class_entry *iface) /* {{{ */
+static zend_bool do_inherit_constant_check(HashTable *child_constants_table, zend_class_constant *parent_constant, zend_string *name, const zend_class_entry *iface) /* {{{ */
{
- zval *old_constant;
+ zend_class_constant *old_constant;
- if ((old_constant = zend_hash_find(child_constants_table, name)) != NULL) {
- if (!Z_ISREF_P(old_constant) ||
- !Z_ISREF_P(parent_constant) ||
- Z_REFVAL_P(old_constant) != Z_REFVAL_P(parent_constant)) {
+ if ((old_constant = zend_hash_find_ptr(child_constants_table, name)) != NULL) {
+ if (old_constant->ce != parent_constant->ce) {
zend_error_noreturn(E_COMPILE_ERROR, "Cannot inherit previously-inherited or override constant %s from interface %s", ZSTR_VAL(name), ZSTR_VAL(iface->name));
}
return 0;
@@ -948,21 +961,23 @@ static zend_bool do_inherit_constant_check(HashTable *child_constants_table, zva
}
/* }}} */
-static void do_inherit_iface_constant(zend_string *name, zval *zv, zend_class_entry *ce, zend_class_entry *iface) /* {{{ */
+static void do_inherit_iface_constant(zend_string *name, zend_class_constant *c, zend_class_entry *ce, zend_class_entry *iface) /* {{{ */
{
- if (do_inherit_constant_check(&ce->constants_table, zv, name, iface)) {
- if (!Z_ISREF_P(zv)) {
- if (iface->type == ZEND_INTERNAL_CLASS) {
- ZVAL_NEW_PERSISTENT_REF(zv, zv);
- } else {
- ZVAL_NEW_REF(zv, zv);
- }
- }
- Z_ADDREF_P(zv);
- if (Z_CONSTANT_P(Z_REFVAL_P(zv))) {
+ if (do_inherit_constant_check(&ce->constants_table, c, name, iface)) {
+ zend_class_constant *ct;
+ if (Z_CONSTANT(c->value)) {
ce->ce_flags &= ~ZEND_ACC_CONSTANTS_UPDATED;
}
- zend_hash_update(&ce->constants_table, name, zv);
+ if (ce->type & ZEND_INTERNAL_CLASS) {
+ if (Z_REFCOUNTED(c->value)) {
+ Z_ADDREF(c->value);
+ }
+ ct = pemalloc(sizeof(zend_class_constant), 1);
+ memcpy(ct, c, sizeof(zend_class_constant));
+ } else {
+ ct = c;
+ }
+ zend_hash_update_ptr(&ce->constants_table, name, ct);
}
}
/* }}} */
@@ -974,7 +989,7 @@ ZEND_API void zend_do_implement_interface(zend_class_entry *ce, zend_class_entry
uint32_t parent_iface_num = ce->parent ? ce->parent->num_interfaces : 0;
zend_function *func;
zend_string *key;
- zval *zv;
+ zend_class_constant *c;
for (i = 0; i < ce->num_interfaces; i++) {
if (ce->interfaces[i] == NULL) {
@@ -990,8 +1005,8 @@ ZEND_API void zend_do_implement_interface(zend_class_entry *ce, zend_class_entry
}
if (ignore) {
/* Check for attempt to redeclare interface constants */
- ZEND_HASH_FOREACH_STR_KEY_VAL(&ce->constants_table, key, zv) {
- do_inherit_constant_check(&iface->constants_table, zv, key, iface);
+ ZEND_HASH_FOREACH_STR_KEY_PTR(&ce->constants_table, key, c) {
+ do_inherit_constant_check(&iface->constants_table, c, key, iface);
} ZEND_HASH_FOREACH_END();
} else {
if (ce->num_interfaces >= current_iface_num) {
@@ -1003,8 +1018,8 @@ ZEND_API void zend_do_implement_interface(zend_class_entry *ce, zend_class_entry
}
ce->interfaces[ce->num_interfaces++] = iface;
- ZEND_HASH_FOREACH_STR_KEY_VAL(&iface->constants_table, key, zv) {
- do_inherit_iface_constant(key, zv, ce, iface);
+ ZEND_HASH_FOREACH_STR_KEY_PTR(&iface->constants_table, key, c) {
+ do_inherit_iface_constant(key, c, ce, iface);
} ZEND_HASH_FOREACH_END();
ZEND_HASH_FOREACH_STR_KEY_PTR(&iface->function_table, key, func) {
diff --git a/Zend/zend_ini_parser.y b/Zend/zend_ini_parser.y
index ae04d2b70c..1078259f22 100644
--- a/Zend/zend_ini_parser.y
+++ b/Zend/zend_ini_parser.y
@@ -132,11 +132,10 @@ static void zend_ini_get_constant(zval *result, zval *name)
if (!memchr(Z_STRVAL_P(name), ':', Z_STRLEN_P(name))
&& (c = zend_get_constant(Z_STR_P(name))) != 0) {
if (Z_TYPE_P(c) != IS_STRING) {
- ZVAL_COPY_VALUE(&tmp, c);
+ ZVAL_DUP(&tmp, c);
if (Z_OPT_CONSTANT(tmp)) {
- zval_update_constant_ex(&tmp, 1, NULL);
+ zval_update_constant_ex(&tmp, NULL);
}
- zval_opt_copy_ctor(&tmp);
convert_to_string(&tmp);
c = &tmp;
}
diff --git a/Zend/zend_interfaces.c b/Zend/zend_interfaces.c
index 6ad8966870..2dadb39b17 100644
--- a/Zend/zend_interfaces.c
+++ b/Zend/zend_interfaces.c
@@ -55,12 +55,10 @@ ZEND_API zval* zend_call_method(zval *object, zend_class_entry *obj_ce, zend_fun
fci.param_count = param_count;
fci.params = params;
fci.no_separation = 1;
- fci.symbol_table = NULL;
if (!fn_proxy && !obj_ce) {
/* no interest in caching and no information already present that is
* needed later inside zend_call_function. */
- fci.function_table = !object ? EG(function_table) : NULL;
result = zend_call_function(&fci, NULL);
zval_ptr_dtor(&fci.function_name);
} else {
diff --git a/Zend/zend_iterators.c b/Zend/zend_iterators.c
index 2f5a23e0f0..b08fc24831 100644
--- a/Zend/zend_iterators.c
+++ b/Zend/zend_iterators.c
@@ -90,8 +90,8 @@ ZEND_API void zend_iterator_dtor(zend_object_iterator *iter)
ZEND_API zend_object_iterator* zend_iterator_unwrap(zval *array_ptr)
{
- if (Z_TYPE_P(array_ptr) &&
- Z_OBJ_HT_P(array_ptr) == &iterator_object_handlers) {
+ ZEND_ASSERT(Z_TYPE_P(array_ptr) == IS_OBJECT);
+ if (Z_OBJ_HT_P(array_ptr) == &iterator_object_handlers) {
return (zend_object_iterator *)Z_OBJ_P(array_ptr);
}
return NULL;
diff --git a/Zend/zend_language_parser.y b/Zend/zend_language_parser.y
index 9f99453cd4..43cf22bfac 100644
--- a/Zend/zend_language_parser.y
+++ b/Zend/zend_language_parser.y
@@ -241,21 +241,22 @@ static YYSIZE_T zend_yytnamerr(char*, const char*);
%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> encaps_var encaps_var_offset isset_variables
%type <ast> top_statement_list use_declarations const_list inner_statement_list if_stmt
%type <ast> alt_if_stmt for_exprs switch_case_list global_var_list static_var_list
-%type <ast> echo_expr_list unset_variables catch_list parameter_list class_statement_list
+%type <ast> echo_expr_list unset_variables catch_name_list 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 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> lexical_var_list encaps_list
+%type <ast> array_pair non_empty_array_pair_list array_pair_list possible_array_pair
+%type <ast> isset_variable type return_type type_expr
%type <ast> identifier
%type <num> returns_ref function is_reference is_variadic variable_modifiers
%type <num> method_modifiers non_empty_member_modifiers member_modifier
-%type <num> class_modifiers class_modifier use_type
+%type <num> class_modifiers class_modifier use_type backup_fn_flags
%type <str> backup_doc_comment
@@ -454,10 +455,15 @@ statement:
catch_list:
/* empty */
{ $$ = zend_ast_create_list(0, ZEND_AST_CATCH_LIST); }
- | catch_list T_CATCH '(' name T_VARIABLE ')' '{' inner_statement_list '}'
+ | catch_list T_CATCH '(' catch_name_list T_VARIABLE ')' '{' inner_statement_list '}'
{ $$ = zend_ast_list_add($1, zend_ast_create(ZEND_AST_CATCH, $4, $5, $8)); }
;
+catch_name_list:
+ name { $$ = zend_ast_create_list(1, ZEND_AST_NAME_LIST, $1); }
+ | catch_name_list '|' name { $$ = zend_ast_list_add($1, $3); }
+;
+
finally_statement:
/* empty */ { $$ = NULL; }
| T_FINALLY '{' inner_statement_list '}' { $$ = $3; }
@@ -474,9 +480,9 @@ unset_variable:
function_declaration_statement:
function returns_ref T_STRING backup_doc_comment '(' parameter_list ')' return_type
- '{' inner_statement_list '}'
- { $$ = zend_ast_create_decl(ZEND_AST_FUNC_DECL, $2, $1, $4,
- zend_ast_get_str($3), $6, NULL, $10, $8); }
+ backup_fn_flags '{' inner_statement_list '}' backup_fn_flags
+ { $$ = zend_ast_create_decl(ZEND_AST_FUNC_DECL, $2 | $13, $1, $4,
+ zend_ast_get_str($3), $6, NULL, $11, $8); CG(extra_fn_flags) = $9; }
;
is_reference:
@@ -538,7 +544,8 @@ implements_list:
foreach_variable:
variable { $$ = $1; }
| '&' variable { $$ = zend_ast_create(ZEND_AST_REF, $2); }
- | T_LIST '(' assignment_list ')' { $$ = $3; }
+ | T_LIST '(' array_pair_list ')' { $3->attr = 1; $$ = $3; }
+ | '[' array_pair_list ']' { $$ = $2; }
;
for_statement:
@@ -637,7 +644,12 @@ parameter:
optional_type:
/* empty */ { $$ = NULL; }
- | type { $$ = $1; }
+ | type_expr { $$ = $1; }
+;
+
+type_expr:
+ type { $$ = $1; }
+ | '?' type { $$ = $2; $$->attr |= ZEND_TYPE_NULLABLE; }
;
type:
@@ -648,7 +660,7 @@ type:
return_type:
/* empty */ { $$ = NULL; }
- | ':' type { $$ = $2; }
+ | ':' type_expr { $$ = $2; }
;
argument_list:
@@ -701,14 +713,14 @@ class_statement_list:
class_statement:
variable_modifiers property_list ';'
{ $$ = $2; $$->attr = $1; }
- | T_CONST class_const_list ';'
- { $$ = $2; RESET_DOC_COMMENT(); }
+ | method_modifiers T_CONST class_const_list ';'
+ { $$ = $3; $$->attr = $1; }
| T_USE name_list trait_adaptations
{ $$ = zend_ast_create(ZEND_AST_USE_TRAIT, $2, $3); }
| method_modifiers function returns_ref identifier backup_doc_comment '(' parameter_list ')'
- return_type method_body
- { $$ = zend_ast_create_decl(ZEND_AST_METHOD, $3 | $1, $2, $5,
- zend_ast_get_str($4), $7, NULL, $10, $9); }
+ return_type backup_fn_flags method_body backup_fn_flags
+ { $$ = zend_ast_create_decl(ZEND_AST_METHOD, $3 | $1 | $12, $2, $5,
+ zend_ast_get_str($4), $7, NULL, $11, $9); CG(extra_fn_flags) = $10; }
;
name_list:
@@ -810,11 +822,11 @@ class_const_list:
;
class_const_decl:
- identifier '=' expr { $$ = zend_ast_create(ZEND_AST_CONST_ELEM, $1, $3); }
+ identifier '=' expr backup_doc_comment { $$ = zend_ast_create(ZEND_AST_CONST_ELEM, $1, $3, ($4 ? zend_ast_create_zval_from_str($4) : NULL)); }
;
const_decl:
- T_STRING '=' expr { $$ = zend_ast_create(ZEND_AST_CONST_ELEM, $1, $3); }
+ T_STRING '=' expr backup_doc_comment { $$ = zend_ast_create(ZEND_AST_CONST_ELEM, $1, $3, ($4 ? zend_ast_create_zval_from_str($4) : NULL)); }
;
echo_expr_list:
@@ -853,8 +865,10 @@ new_expr:
;
expr_without_variable:
- T_LIST '(' assignment_list ')' '=' expr
- { $$ = zend_ast_create(ZEND_AST_ASSIGN, $3, $6); }
+ T_LIST '(' array_pair_list ')' '=' expr
+ { $3->attr = 1; $$ = zend_ast_create(ZEND_AST_ASSIGN, $3, $6); }
+ | '[' array_pair_list ']' '=' expr
+ { $$ = zend_ast_create(ZEND_AST_ASSIGN, $2, $5); }
| variable '=' expr
{ $$ = zend_ast_create(ZEND_AST_ASSIGN, $1, $3); }
| variable '=' '&' variable
@@ -955,20 +969,20 @@ expr_without_variable:
| scalar { $$ = $1; }
| '`' backticks_expr '`' { $$ = zend_ast_create(ZEND_AST_SHELL_EXEC, $2); }
| T_PRINT expr { $$ = zend_ast_create(ZEND_AST_PRINT, $2); }
- | T_YIELD { $$ = zend_ast_create(ZEND_AST_YIELD, NULL, NULL); }
- | T_YIELD expr { $$ = zend_ast_create(ZEND_AST_YIELD, $2, NULL); }
- | T_YIELD expr T_DOUBLE_ARROW expr { $$ = zend_ast_create(ZEND_AST_YIELD, $4, $2); }
- | T_YIELD_FROM expr { $$ = zend_ast_create(ZEND_AST_YIELD_FROM, $2); }
+ | T_YIELD { $$ = zend_ast_create(ZEND_AST_YIELD, NULL, NULL); CG(extra_fn_flags) |= ZEND_ACC_GENERATOR; }
+ | T_YIELD expr { $$ = zend_ast_create(ZEND_AST_YIELD, $2, NULL); CG(extra_fn_flags) |= ZEND_ACC_GENERATOR; }
+ | T_YIELD expr T_DOUBLE_ARROW expr { $$ = zend_ast_create(ZEND_AST_YIELD, $4, $2); CG(extra_fn_flags) |= ZEND_ACC_GENERATOR; }
+ | T_YIELD_FROM expr { $$ = zend_ast_create(ZEND_AST_YIELD_FROM, $2); CG(extra_fn_flags) |= ZEND_ACC_GENERATOR; }
| function returns_ref backup_doc_comment '(' parameter_list ')' lexical_vars return_type
- '{' inner_statement_list '}'
- { $$ = zend_ast_create_decl(ZEND_AST_CLOSURE, $2, $1, $3,
+ backup_fn_flags '{' inner_statement_list '}' backup_fn_flags
+ { $$ = zend_ast_create_decl(ZEND_AST_CLOSURE, $2 | $13, $1, $3,
zend_string_init("{closure}", sizeof("{closure}") - 1, 0),
- $5, $7, $10, $8); }
+ $5, $7, $11, $8); CG(extra_fn_flags) = $9; }
| T_STATIC function returns_ref backup_doc_comment '(' parameter_list ')' lexical_vars
- return_type '{' inner_statement_list '}'
- { $$ = zend_ast_create_decl(ZEND_AST_CLOSURE, $3 | ZEND_ACC_STATIC, $2, $4,
+ return_type backup_fn_flags '{' inner_statement_list '}' backup_fn_flags
+ { $$ = zend_ast_create_decl(ZEND_AST_CLOSURE, $3 | $14 | ZEND_ACC_STATIC, $2, $4,
zend_string_init("{closure}", sizeof("{closure}") - 1, 0),
- $6, $8, $11, $9); }
+ $6, $8, $12, $9); CG(extra_fn_flags) = $10; }
;
function:
@@ -979,6 +993,10 @@ backup_doc_comment:
/* empty */ { $$ = CG(doc_comment); CG(doc_comment) = NULL; }
;
+backup_fn_flags:
+ /* empty */ { $$ = CG(extra_fn_flags); CG(extra_fn_flags) = 0; }
+;
+
returns_ref:
/* empty */ { $$ = 0; }
| '&' { $$ = ZEND_ACC_RETURN_REFERENCE; }
@@ -1012,7 +1030,7 @@ function_call:
class_name:
T_STATIC
- { zval zv; ZVAL_STRINGL(&zv, "static", sizeof("static")-1);
+ { zval zv; ZVAL_INTERNED_STR(&zv, CG(known_strings)[ZEND_STR_STATIC]);
$$ = zend_ast_create_zval_ex(&zv, ZEND_NAME_NOT_FQ); }
| name { $$ = $1; }
;
@@ -1075,11 +1093,6 @@ constant:
{ $$ = zend_ast_create(ZEND_AST_CLASS_CONST, $1, $3); }
;
-possible_comma:
- /* empty */
- | ','
-;
-
expr:
variable { $$ = $1; }
| expr_without_variable { $$ = $1; }
@@ -1169,40 +1182,36 @@ property_name:
| simple_variable { $$ = zend_ast_create(ZEND_AST_VAR, $1); }
;
-assignment_list:
- assignment_list ',' assignment_list_element
- { $$ = zend_ast_list_add($1, $3); }
- | assignment_list_element
- { $$ = zend_ast_create_list(1, ZEND_AST_LIST, $1); }
-;
-
-assignment_list_element:
- variable { $$ = $1; }
- | T_LIST '(' assignment_list ')' { $$ = $3; }
- | /* empty */ { $$ = NULL; }
+array_pair_list:
+ non_empty_array_pair_list
+ { /* allow single trailing comma */ $$ = zend_ast_list_rtrim($1); }
;
-
-array_pair_list:
- /* empty */ { $$ = zend_ast_create_list(0, ZEND_AST_ARRAY); }
- | non_empty_array_pair_list possible_comma { $$ = $1; }
+possible_array_pair:
+ /* empty */ { $$ = NULL; }
+ | array_pair { $$ = $1; }
;
non_empty_array_pair_list:
- non_empty_array_pair_list ',' array_pair
+ non_empty_array_pair_list ',' possible_array_pair
{ $$ = zend_ast_list_add($1, $3); }
- | array_pair
+ | possible_array_pair
{ $$ = zend_ast_create_list(1, ZEND_AST_ARRAY, $1); }
;
array_pair:
expr T_DOUBLE_ARROW expr
{ $$ = zend_ast_create(ZEND_AST_ARRAY_ELEM, $3, $1); }
- | expr { $$ = zend_ast_create(ZEND_AST_ARRAY_ELEM, $1, NULL); }
+ | expr
+ { $$ = zend_ast_create(ZEND_AST_ARRAY_ELEM, $1, NULL); }
| expr T_DOUBLE_ARROW '&' variable
{ $$ = zend_ast_create_ex(ZEND_AST_ARRAY_ELEM, 1, $4, $1); }
| '&' variable
{ $$ = zend_ast_create_ex(ZEND_AST_ARRAY_ELEM, 1, $2, NULL); }
+ | expr T_DOUBLE_ARROW T_LIST '(' array_pair_list ')'
+ { $5->attr = 1; $$ = zend_ast_create(ZEND_AST_ARRAY_ELEM, $5, $1); }
+ | T_LIST '(' array_pair_list ')'
+ { $3->attr = 1; $$ = zend_ast_create(ZEND_AST_ARRAY_ELEM, $3, NULL); }
;
encaps_list:
diff --git a/Zend/zend_language_scanner.c b/Zend/zend_language_scanner.c
index 9df9828eb4..939e22bc26 100644
--- a/Zend/zend_language_scanner.c
+++ b/Zend/zend_language_scanner.c
@@ -118,7 +118,7 @@ do { \
#define SET_DOUBLE_QUOTES_SCANNED_LENGTH(len) SCNG(scanned_string_len) = (len)
#define GET_DOUBLE_QUOTES_SCANNED_LENGTH() SCNG(scanned_string_len)
-#define IS_LABEL_START(c) (((c) >= 'a' && (c) <= 'z') || ((c) >= 'A' && (c) <= 'Z') || (c) == '_' || (c) >= 0x7F)
+#define IS_LABEL_START(c) (((c) >= 'a' && (c) <= 'z') || ((c) >= 'A' && (c) <= 'Z') || (c) == '_' || (c) >= 0x80)
#define ZEND_IS_OCT(c) ((c)>='0' && (c)<='7')
#define ZEND_IS_HEX(c) (((c)>='0' && (c)<='9') || ((c)>='a' && (c)<='f') || ((c)>='A' && (c)<='F'))
@@ -180,6 +180,7 @@ void startup_scanner(void)
{
CG(parse_error) = 0;
CG(doc_comment) = NULL;
+ CG(extra_fn_flags) = 0;
zend_stack_init(&SCNG(state_stack), sizeof(int));
zend_ptr_stack_init(&SCNG(heredoc_label_stack));
}
@@ -570,6 +571,48 @@ ZEND_API int open_file_for_scanning(zend_file_handle *file_handle)
}
END_EXTERN_C()
+static zend_op_array *zend_compile(int type)
+{
+ zend_op_array *op_array = NULL;
+ zend_bool original_in_compilation = CG(in_compilation);
+
+ CG(in_compilation) = 1;
+ CG(ast) = NULL;
+ CG(ast_arena) = zend_arena_create(1024 * 32);
+
+ if (!zendparse()) {
+ zend_file_context original_file_context;
+ zend_oparray_context original_oparray_context;
+ zend_op_array *original_active_op_array = CG(active_op_array);
+
+ op_array = emalloc(sizeof(zend_op_array));
+ init_op_array(op_array, type, INITIAL_OP_ARRAY_SIZE);
+ CG(active_op_array) = op_array;
+
+ if (zend_ast_process) {
+ zend_ast_process(CG(ast));
+ }
+
+ zend_file_context_begin(&original_file_context);
+ zend_oparray_context_begin(&original_oparray_context);
+ zend_compile_top_stmt(CG(ast));
+ zend_emit_final_return(type == ZEND_USER_FUNCTION);
+ op_array->line_start = 1;
+ op_array->line_end = CG(zend_lineno);
+ pass_two(op_array);
+ zend_oparray_context_end(&original_oparray_context);
+ zend_file_context_end(&original_file_context);
+
+ CG(active_op_array) = original_active_op_array;
+ }
+
+ zend_ast_destroy(CG(ast));
+ zend_arena_destroy(CG(ast_arena));
+
+ CG(in_compilation) = original_in_compilation;
+
+ return op_array;
+}
ZEND_API zend_op_array *compile_file(zend_file_handle *file_handle, int type)
{
@@ -585,41 +628,7 @@ ZEND_API zend_op_array *compile_file(zend_file_handle *file_handle, int type)
zend_message_dispatcher(ZMSG_FAILED_INCLUDE_FOPEN, file_handle->filename);
}
} else {
- zend_bool original_in_compilation = CG(in_compilation);
- CG(in_compilation) = 1;
-
- CG(ast) = NULL;
- CG(ast_arena) = zend_arena_create(1024 * 32);
- if (!zendparse()) {
- zval retval_zv;
- zend_file_context original_file_context;
- zend_oparray_context original_oparray_context;
- zend_op_array *original_active_op_array = CG(active_op_array);
- op_array = emalloc(sizeof(zend_op_array));
- init_op_array(op_array, ZEND_USER_FUNCTION, INITIAL_OP_ARRAY_SIZE);
- CG(active_op_array) = op_array;
- ZVAL_LONG(&retval_zv, 1);
-
- if (zend_ast_process) {
- zend_ast_process(CG(ast));
- }
-
- zend_file_context_begin(&original_file_context);
- zend_oparray_context_begin(&original_oparray_context);
- zend_compile_top_stmt(CG(ast));
- zend_emit_final_return(&retval_zv);
- op_array->line_start = 1;
- op_array->line_end = CG(zend_lineno);
- pass_two(op_array);
- zend_oparray_context_end(&original_oparray_context);
- zend_file_context_end(&original_file_context);
-
- CG(active_op_array) = original_active_op_array;
- }
-
- zend_ast_destroy(CG(ast));
- zend_arena_destroy(CG(ast_arena));
- CG(in_compilation) = original_in_compilation;
+ op_array = zend_compile(ZEND_USER_FUNCTION);
}
zend_restore_lexical_state(&original_lex_state);
@@ -734,13 +743,11 @@ ZEND_API size_t zend_get_scanned_file_offset(void)
return offset;
}
-
zend_op_array *compile_string(zval *source_string, char *filename)
{
zend_lex_state original_lex_state;
zend_op_array *op_array = NULL;
zval tmp;
- zend_bool original_in_compilation = CG(in_compilation);
if (Z_STRLEN_P(source_string)==0) {
return NULL;
@@ -750,45 +757,15 @@ zend_op_array *compile_string(zval *source_string, char *filename)
convert_to_string(&tmp);
source_string = &tmp;
- CG(in_compilation) = 1;
zend_save_lexical_state(&original_lex_state);
if (zend_prepare_string_for_scanning(source_string, filename) == SUCCESS) {
- CG(ast) = NULL;
- CG(ast_arena) = zend_arena_create(1024 * 32);
BEGIN(ST_IN_SCRIPTING);
-
- if (!zendparse()) {
- zend_file_context original_file_context;
- zend_oparray_context original_oparray_context;
- zend_op_array *original_active_op_array = CG(active_op_array);
- op_array = emalloc(sizeof(zend_op_array));
- init_op_array(op_array, ZEND_EVAL_CODE, INITIAL_OP_ARRAY_SIZE);
- CG(active_op_array) = op_array;
-
- if (zend_ast_process) {
- zend_ast_process(CG(ast));
- }
-
- zend_file_context_begin(&original_file_context);
- zend_oparray_context_begin(&original_oparray_context);
- zend_compile_top_stmt(CG(ast));
- zend_emit_final_return(NULL);
- op_array->line_start = 1;
- op_array->line_end = CG(zend_lineno);
- pass_two(op_array);
- zend_oparray_context_end(&original_oparray_context);
- zend_file_context_end(&original_file_context);
-
- CG(active_op_array) = original_active_op_array;
- }
-
- zend_ast_destroy(CG(ast));
- zend_arena_destroy(CG(ast_arena));
+ op_array = zend_compile(ZEND_EVAL_CODE);
}
zend_restore_lexical_state(&original_lex_state);
zval_dtor(&tmp);
- CG(in_compilation) = original_in_compilation;
+
return op_array;
}
@@ -1069,6 +1046,12 @@ static int zend_scan_escape_string(zval *zendlval, char *str, int len, char quot
Z_STRLEN_P(zendlval)--;
}
}
+ if (octal_buf[2] &&
+ (octal_buf[0] > '3')) {
+ /* 3 octit values must not overflow 0xFF (\377) */
+ zend_error(E_COMPILE_WARNING, "Octal escape sequence overflow \\%s is greater than \\377", octal_buf);
+ }
+
*t++ = (char) ZEND_STRTOL(octal_buf, NULL, 8);
} else {
*t++ = '\\';
@@ -1117,7 +1100,7 @@ restart:
SCNG(yy_text) = YYCURSOR;
-#line 1121 "Zend/zend_language_scanner.c"
+#line 1104 "Zend/zend_language_scanner.c"
{
YYCTYPE yych;
unsigned int yyaccept = 0;
@@ -1171,7 +1154,7 @@ yyc_INITIAL:
yy3:
YYDEBUG(3, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1813 "Zend/zend_language_scanner.l"
+#line 1796 "Zend/zend_language_scanner.l"
{
if (YYCURSOR > YYLIMIT) {
RETURN_TOKEN(END);
@@ -1216,7 +1199,7 @@ inline_char_handler:
HANDLE_NEWLINES(yytext, yyleng);
RETURN_TOKEN(T_INLINE_HTML);
}
-#line 1220 "Zend/zend_language_scanner.c"
+#line 1203 "Zend/zend_language_scanner.c"
yy4:
YYDEBUG(4, *YYCURSOR);
yych = *++YYCURSOR;
@@ -1234,7 +1217,7 @@ yy5:
yy6:
YYDEBUG(6, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1804 "Zend/zend_language_scanner.l"
+#line 1787 "Zend/zend_language_scanner.l"
{
if (CG(short_tags)) {
BEGIN(ST_IN_SCRIPTING);
@@ -1243,18 +1226,18 @@ yy6:
goto inline_char_handler;
}
}
-#line 1247 "Zend/zend_language_scanner.c"
+#line 1230 "Zend/zend_language_scanner.c"
yy7:
YYDEBUG(7, *YYCURSOR);
++YYCURSOR;
YYDEBUG(8, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1791 "Zend/zend_language_scanner.l"
+#line 1774 "Zend/zend_language_scanner.l"
{
BEGIN(ST_IN_SCRIPTING);
RETURN_TOKEN(T_OPEN_TAG_WITH_ECHO);
}
-#line 1258 "Zend/zend_language_scanner.c"
+#line 1241 "Zend/zend_language_scanner.c"
yy9:
YYDEBUG(9, *YYCURSOR);
yych = *++YYCURSOR;
@@ -1285,13 +1268,13 @@ yy13:
yy14:
YYDEBUG(14, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1797 "Zend/zend_language_scanner.l"
+#line 1780 "Zend/zend_language_scanner.l"
{
HANDLE_NEWLINE(yytext[yyleng-1]);
BEGIN(ST_IN_SCRIPTING);
RETURN_TOKEN(T_OPEN_TAG);
}
-#line 1295 "Zend/zend_language_scanner.c"
+#line 1278 "Zend/zend_language_scanner.c"
yy15:
YYDEBUG(15, *YYCURSOR);
++YYCURSOR;
@@ -1316,7 +1299,7 @@ yyc_ST_BACKQUOTE:
0, 128, 128, 128, 128, 128, 128, 128,
128, 128, 128, 128, 128, 128, 128, 128,
128, 128, 128, 128, 128, 128, 128, 128,
- 128, 128, 128, 0, 0, 0, 0, 128,
+ 128, 128, 128, 0, 0, 0, 0, 0,
128, 128, 128, 128, 128, 128, 128, 128,
128, 128, 128, 128, 128, 128, 128, 128,
128, 128, 128, 128, 128, 128, 128, 128,
@@ -1355,13 +1338,13 @@ yyc_ST_BACKQUOTE:
if (yych >= 'a') goto yy26;
} else {
if (yych <= '{') goto yy29;
- if (yych >= 0x7F) goto yy26;
+ if (yych >= 0x80) goto yy26;
}
}
yy19:
YYDEBUG(19, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 2223 "Zend/zend_language_scanner.l"
+#line 2206 "Zend/zend_language_scanner.l"
{
if (YYCURSOR > YYLIMIT) {
RETURN_TOKEN(END);
@@ -1402,7 +1385,7 @@ yy19:
zend_scan_escape_string(zendlval, yytext, yyleng, '`');
RETURN_TOKEN(T_ENCAPSED_AND_WHITESPACE);
}
-#line 1406 "Zend/zend_language_scanner.c"
+#line 1389 "Zend/zend_language_scanner.c"
yy20:
YYDEBUG(20, *YYCURSOR);
yych = *++YYCURSOR;
@@ -1413,12 +1396,12 @@ yy21:
++YYCURSOR;
YYDEBUG(22, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 2167 "Zend/zend_language_scanner.l"
+#line 2150 "Zend/zend_language_scanner.l"
{
BEGIN(ST_IN_SCRIPTING);
RETURN_TOKEN('`');
}
-#line 1422 "Zend/zend_language_scanner.c"
+#line 1405 "Zend/zend_language_scanner.c"
yy23:
YYDEBUG(23, *YYCURSOR);
yych = *++YYCURSOR;
@@ -1428,14 +1411,14 @@ yy24:
++YYCURSOR;
YYDEBUG(25, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 2154 "Zend/zend_language_scanner.l"
+#line 2137 "Zend/zend_language_scanner.l"
{
Z_LVAL_P(zendlval) = (zend_long) '{';
yy_push_state(ST_IN_SCRIPTING);
yyless(1);
RETURN_TOKEN(T_CURLY_OPEN);
}
-#line 1439 "Zend/zend_language_scanner.c"
+#line 1422 "Zend/zend_language_scanner.c"
yy26:
YYDEBUG(26, *YYCURSOR);
yyaccept = 0;
@@ -1451,23 +1434,23 @@ yy26:
yy28:
YYDEBUG(28, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1878 "Zend/zend_language_scanner.l"
+#line 1861 "Zend/zend_language_scanner.l"
{
zend_copy_value(zendlval, (yytext+1), (yyleng-1));
RETURN_TOKEN(T_VARIABLE);
}
-#line 1460 "Zend/zend_language_scanner.c"
+#line 1443 "Zend/zend_language_scanner.c"
yy29:
YYDEBUG(29, *YYCURSOR);
++YYCURSOR;
YYDEBUG(30, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1596 "Zend/zend_language_scanner.l"
+#line 1579 "Zend/zend_language_scanner.l"
{
yy_push_state(ST_LOOKING_FOR_VARNAME);
RETURN_TOKEN(T_DOLLAR_OPEN_CURLY_BRACES);
}
-#line 1471 "Zend/zend_language_scanner.c"
+#line 1454 "Zend/zend_language_scanner.c"
yy31:
YYDEBUG(31, *YYCURSOR);
yych = *++YYCURSOR;
@@ -1481,14 +1464,14 @@ yy33:
++YYCURSOR;
YYDEBUG(34, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1871 "Zend/zend_language_scanner.l"
+#line 1854 "Zend/zend_language_scanner.l"
{
yyless(yyleng - 1);
yy_push_state(ST_VAR_OFFSET);
zend_copy_value(zendlval, (yytext+1), (yyleng-1));
RETURN_TOKEN(T_VARIABLE);
}
-#line 1492 "Zend/zend_language_scanner.c"
+#line 1475 "Zend/zend_language_scanner.c"
yy35:
YYDEBUG(35, *YYCURSOR);
yych = *++YYCURSOR;
@@ -1499,21 +1482,21 @@ yy35:
} else {
if (yych <= '`') goto yy32;
if (yych <= 'z') goto yy36;
- if (yych <= '~') goto yy32;
+ if (yych <= 0x7F) goto yy32;
}
yy36:
YYDEBUG(36, *YYCURSOR);
++YYCURSOR;
YYDEBUG(37, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1862 "Zend/zend_language_scanner.l"
+#line 1845 "Zend/zend_language_scanner.l"
{
yyless(yyleng - 3);
yy_push_state(ST_LOOKING_FOR_PROPERTY);
zend_copy_value(zendlval, (yytext+1), (yyleng-1));
RETURN_TOKEN(T_VARIABLE);
}
-#line 1517 "Zend/zend_language_scanner.c"
+#line 1500 "Zend/zend_language_scanner.c"
}
/* *********************************** */
yyc_ST_DOUBLE_QUOTES:
@@ -1534,7 +1517,7 @@ yyc_ST_DOUBLE_QUOTES:
0, 128, 128, 128, 128, 128, 128, 128,
128, 128, 128, 128, 128, 128, 128, 128,
128, 128, 128, 128, 128, 128, 128, 128,
- 128, 128, 128, 0, 0, 0, 0, 128,
+ 128, 128, 128, 0, 0, 0, 0, 0,
128, 128, 128, 128, 128, 128, 128, 128,
128, 128, 128, 128, 128, 128, 128, 128,
128, 128, 128, 128, 128, 128, 128, 128,
@@ -1575,13 +1558,13 @@ yy40:
if (yych >= 'a') goto yy48;
} else {
if (yych <= '{') goto yy51;
- if (yych >= 0x7F) goto yy48;
+ if (yych >= 0x80) goto yy48;
}
}
yy41:
YYDEBUG(41, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 2173 "Zend/zend_language_scanner.l"
+#line 2156 "Zend/zend_language_scanner.l"
{
if (GET_DOUBLE_QUOTES_SCANNED_LENGTH()) {
YYCURSOR += GET_DOUBLE_QUOTES_SCANNED_LENGTH() - 1;
@@ -1630,7 +1613,7 @@ double_quotes_scan_done:
zend_scan_escape_string(zendlval, yytext, yyleng, '"');
RETURN_TOKEN(T_ENCAPSED_AND_WHITESPACE);
}
-#line 1634 "Zend/zend_language_scanner.c"
+#line 1617 "Zend/zend_language_scanner.c"
yy42:
YYDEBUG(42, *YYCURSOR);
yych = *++YYCURSOR;
@@ -1641,12 +1624,12 @@ yy43:
++YYCURSOR;
YYDEBUG(44, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 2162 "Zend/zend_language_scanner.l"
+#line 2145 "Zend/zend_language_scanner.l"
{
BEGIN(ST_IN_SCRIPTING);
RETURN_TOKEN('"');
}
-#line 1650 "Zend/zend_language_scanner.c"
+#line 1633 "Zend/zend_language_scanner.c"
yy45:
YYDEBUG(45, *YYCURSOR);
yych = *++YYCURSOR;
@@ -1656,14 +1639,14 @@ yy46:
++YYCURSOR;
YYDEBUG(47, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 2154 "Zend/zend_language_scanner.l"
+#line 2137 "Zend/zend_language_scanner.l"
{
Z_LVAL_P(zendlval) = (zend_long) '{';
yy_push_state(ST_IN_SCRIPTING);
yyless(1);
RETURN_TOKEN(T_CURLY_OPEN);
}
-#line 1667 "Zend/zend_language_scanner.c"
+#line 1650 "Zend/zend_language_scanner.c"
yy48:
YYDEBUG(48, *YYCURSOR);
yyaccept = 0;
@@ -1679,23 +1662,23 @@ yy48:
yy50:
YYDEBUG(50, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1878 "Zend/zend_language_scanner.l"
+#line 1861 "Zend/zend_language_scanner.l"
{
zend_copy_value(zendlval, (yytext+1), (yyleng-1));
RETURN_TOKEN(T_VARIABLE);
}
-#line 1688 "Zend/zend_language_scanner.c"
+#line 1671 "Zend/zend_language_scanner.c"
yy51:
YYDEBUG(51, *YYCURSOR);
++YYCURSOR;
YYDEBUG(52, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1596 "Zend/zend_language_scanner.l"
+#line 1579 "Zend/zend_language_scanner.l"
{
yy_push_state(ST_LOOKING_FOR_VARNAME);
RETURN_TOKEN(T_DOLLAR_OPEN_CURLY_BRACES);
}
-#line 1699 "Zend/zend_language_scanner.c"
+#line 1682 "Zend/zend_language_scanner.c"
yy53:
YYDEBUG(53, *YYCURSOR);
yych = *++YYCURSOR;
@@ -1709,14 +1692,14 @@ yy55:
++YYCURSOR;
YYDEBUG(56, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1871 "Zend/zend_language_scanner.l"
+#line 1854 "Zend/zend_language_scanner.l"
{
yyless(yyleng - 1);
yy_push_state(ST_VAR_OFFSET);
zend_copy_value(zendlval, (yytext+1), (yyleng-1));
RETURN_TOKEN(T_VARIABLE);
}
-#line 1720 "Zend/zend_language_scanner.c"
+#line 1703 "Zend/zend_language_scanner.c"
yy57:
YYDEBUG(57, *YYCURSOR);
yych = *++YYCURSOR;
@@ -1727,21 +1710,21 @@ yy57:
} else {
if (yych <= '`') goto yy54;
if (yych <= 'z') goto yy58;
- if (yych <= '~') goto yy54;
+ if (yych <= 0x7F) goto yy54;
}
yy58:
YYDEBUG(58, *YYCURSOR);
++YYCURSOR;
YYDEBUG(59, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1862 "Zend/zend_language_scanner.l"
+#line 1845 "Zend/zend_language_scanner.l"
{
yyless(yyleng - 3);
yy_push_state(ST_LOOKING_FOR_PROPERTY);
zend_copy_value(zendlval, (yytext+1), (yyleng-1));
RETURN_TOKEN(T_VARIABLE);
}
-#line 1745 "Zend/zend_language_scanner.c"
+#line 1728 "Zend/zend_language_scanner.c"
}
/* *********************************** */
yyc_ST_END_HEREDOC:
@@ -1752,7 +1735,7 @@ yyc_ST_END_HEREDOC:
++YYCURSOR;
YYDEBUG(63, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 2140 "Zend/zend_language_scanner.l"
+#line 2123 "Zend/zend_language_scanner.l"
{
zend_heredoc_label *heredoc_label = zend_ptr_stack_pop(&SCNG(heredoc_label_stack));
@@ -1765,7 +1748,7 @@ yyc_ST_END_HEREDOC:
BEGIN(ST_IN_SCRIPTING);
RETURN_TOKEN(T_END_HEREDOC);
}
-#line 1769 "Zend/zend_language_scanner.c"
+#line 1752 "Zend/zend_language_scanner.c"
/* *********************************** */
yyc_ST_HEREDOC:
{
@@ -1785,7 +1768,7 @@ yyc_ST_HEREDOC:
0, 128, 128, 128, 128, 128, 128, 128,
128, 128, 128, 128, 128, 128, 128, 128,
128, 128, 128, 128, 128, 128, 128, 128,
- 128, 128, 128, 0, 0, 0, 0, 128,
+ 128, 128, 128, 0, 0, 0, 0, 0,
128, 128, 128, 128, 128, 128, 128, 128,
128, 128, 128, 128, 128, 128, 128, 128,
128, 128, 128, 128, 128, 128, 128, 128,
@@ -1821,13 +1804,13 @@ yy66:
if (yych >= 'a') goto yy72;
} else {
if (yych <= '{') goto yy75;
- if (yych >= 0x7F) goto yy72;
+ if (yych >= 0x80) goto yy72;
}
}
yy67:
YYDEBUG(67, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 2265 "Zend/zend_language_scanner.l"
+#line 2248 "Zend/zend_language_scanner.l"
{
int newline = 0;
@@ -1900,7 +1883,7 @@ heredoc_scan_done:
zend_scan_escape_string(zendlval, yytext, yyleng - newline, 0);
RETURN_TOKEN(T_ENCAPSED_AND_WHITESPACE);
}
-#line 1904 "Zend/zend_language_scanner.c"
+#line 1887 "Zend/zend_language_scanner.c"
yy68:
YYDEBUG(68, *YYCURSOR);
yych = *++YYCURSOR;
@@ -1915,14 +1898,14 @@ yy70:
++YYCURSOR;
YYDEBUG(71, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 2154 "Zend/zend_language_scanner.l"
+#line 2137 "Zend/zend_language_scanner.l"
{
Z_LVAL_P(zendlval) = (zend_long) '{';
yy_push_state(ST_IN_SCRIPTING);
yyless(1);
RETURN_TOKEN(T_CURLY_OPEN);
}
-#line 1926 "Zend/zend_language_scanner.c"
+#line 1909 "Zend/zend_language_scanner.c"
yy72:
YYDEBUG(72, *YYCURSOR);
yyaccept = 0;
@@ -1938,23 +1921,23 @@ yy72:
yy74:
YYDEBUG(74, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1878 "Zend/zend_language_scanner.l"
+#line 1861 "Zend/zend_language_scanner.l"
{
zend_copy_value(zendlval, (yytext+1), (yyleng-1));
RETURN_TOKEN(T_VARIABLE);
}
-#line 1947 "Zend/zend_language_scanner.c"
+#line 1930 "Zend/zend_language_scanner.c"
yy75:
YYDEBUG(75, *YYCURSOR);
++YYCURSOR;
YYDEBUG(76, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1596 "Zend/zend_language_scanner.l"
+#line 1579 "Zend/zend_language_scanner.l"
{
yy_push_state(ST_LOOKING_FOR_VARNAME);
RETURN_TOKEN(T_DOLLAR_OPEN_CURLY_BRACES);
}
-#line 1958 "Zend/zend_language_scanner.c"
+#line 1941 "Zend/zend_language_scanner.c"
yy77:
YYDEBUG(77, *YYCURSOR);
yych = *++YYCURSOR;
@@ -1968,14 +1951,14 @@ yy79:
++YYCURSOR;
YYDEBUG(80, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1871 "Zend/zend_language_scanner.l"
+#line 1854 "Zend/zend_language_scanner.l"
{
yyless(yyleng - 1);
yy_push_state(ST_VAR_OFFSET);
zend_copy_value(zendlval, (yytext+1), (yyleng-1));
RETURN_TOKEN(T_VARIABLE);
}
-#line 1979 "Zend/zend_language_scanner.c"
+#line 1962 "Zend/zend_language_scanner.c"
yy81:
YYDEBUG(81, *YYCURSOR);
yych = *++YYCURSOR;
@@ -1986,21 +1969,21 @@ yy81:
} else {
if (yych <= '`') goto yy78;
if (yych <= 'z') goto yy82;
- if (yych <= '~') goto yy78;
+ if (yych <= 0x7F) goto yy78;
}
yy82:
YYDEBUG(82, *YYCURSOR);
++YYCURSOR;
YYDEBUG(83, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1862 "Zend/zend_language_scanner.l"
+#line 1845 "Zend/zend_language_scanner.l"
{
yyless(yyleng - 3);
yy_push_state(ST_LOOKING_FOR_PROPERTY);
zend_copy_value(zendlval, (yytext+1), (yyleng-1));
RETURN_TOKEN(T_VARIABLE);
}
-#line 2004 "Zend/zend_language_scanner.c"
+#line 1987 "Zend/zend_language_scanner.c"
}
/* *********************************** */
yyc_ST_IN_SCRIPTING:
@@ -2021,7 +2004,7 @@ yyc_ST_IN_SCRIPTING:
0, 36, 36, 36, 36, 36, 36, 4,
4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4,
- 4, 4, 4, 0, 0, 0, 0, 4,
+ 4, 4, 4, 0, 0, 0, 0, 0,
4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4,
@@ -2072,7 +2055,8 @@ yyc_ST_IN_SCRIPTING:
case 0x1C:
case 0x1D:
case 0x1E:
- case 0x1F: goto yy147;
+ case 0x1F:
+ case 0x7F: goto yy147;
case '\t':
case '\n':
case '\r':
@@ -2183,12 +2167,12 @@ yy86:
yy87:
YYDEBUG(87, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1901 "Zend/zend_language_scanner.l"
+#line 1884 "Zend/zend_language_scanner.l"
{
zend_copy_value(zendlval, yytext, yyleng);
RETURN_TOKEN(T_STRING);
}
-#line 2192 "Zend/zend_language_scanner.c"
+#line 2176 "Zend/zend_language_scanner.c"
yy88:
YYDEBUG(88, *YYCURSOR);
yych = *++YYCURSOR;
@@ -2420,11 +2404,11 @@ yy101:
yy102:
YYDEBUG(102, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1585 "Zend/zend_language_scanner.l"
+#line 1568 "Zend/zend_language_scanner.l"
{
RETURN_TOKEN(yytext[0]);
}
-#line 2428 "Zend/zend_language_scanner.c"
+#line 2412 "Zend/zend_language_scanner.c"
yy103:
YYDEBUG(103, *YYCURSOR);
++YYCURSOR;
@@ -2433,12 +2417,12 @@ yy103:
yy104:
YYDEBUG(104, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1300 "Zend/zend_language_scanner.l"
+#line 1283 "Zend/zend_language_scanner.l"
{
HANDLE_NEWLINES(yytext, yyleng);
RETURN_TOKEN(T_WHITESPACE);
}
-#line 2442 "Zend/zend_language_scanner.c"
+#line 2426 "Zend/zend_language_scanner.c"
yy105:
YYDEBUG(105, *YYCURSOR);
yych = *++YYCURSOR;
@@ -2449,11 +2433,11 @@ yy106:
++YYCURSOR;
YYDEBUG(107, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1325 "Zend/zend_language_scanner.l"
+#line 1308 "Zend/zend_language_scanner.l"
{
RETURN_TOKEN(T_NS_SEPARATOR);
}
-#line 2457 "Zend/zend_language_scanner.c"
+#line 2441 "Zend/zend_language_scanner.c"
yy108:
YYDEBUG(108, *YYCURSOR);
yyaccept = 1;
@@ -2670,7 +2654,7 @@ yy129:
} else {
if (yych <= '`') goto yy102;
if (yych <= 'z') goto yy169;
- if (yych <= '~') goto yy102;
+ if (yych <= 0x7F) goto yy102;
goto yy169;
}
yy130:
@@ -2682,18 +2666,18 @@ yy131:
++YYCURSOR;
YYDEBUG(132, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1590 "Zend/zend_language_scanner.l"
+#line 1573 "Zend/zend_language_scanner.l"
{
yy_push_state(ST_IN_SCRIPTING);
RETURN_TOKEN('{');
}
-#line 2691 "Zend/zend_language_scanner.c"
+#line 2675 "Zend/zend_language_scanner.c"
yy133:
YYDEBUG(133, *YYCURSOR);
++YYCURSOR;
YYDEBUG(134, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1602 "Zend/zend_language_scanner.l"
+#line 1585 "Zend/zend_language_scanner.l"
{
RESET_DOC_COMMENT();
if (!zend_stack_is_empty(&SCNG(state_stack))) {
@@ -2701,7 +2685,7 @@ yy133:
}
RETURN_TOKEN('}');
}
-#line 2705 "Zend/zend_language_scanner.c"
+#line 2689 "Zend/zend_language_scanner.c"
yy135:
YYDEBUG(135, *YYCURSOR);
yyaccept = 2;
@@ -2729,7 +2713,7 @@ yy135:
yy136:
YYDEBUG(136, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1655 "Zend/zend_language_scanner.l"
+#line 1638 "Zend/zend_language_scanner.l"
{
char *end;
if (yyleng < MAX_LENGTH_OF_LONG - 1) { /* Won't overflow */
@@ -2774,7 +2758,7 @@ yy136:
ZEND_ASSERT(!errno);
RETURN_TOKEN(T_LNUMBER);
}
-#line 2778 "Zend/zend_language_scanner.c"
+#line 2762 "Zend/zend_language_scanner.c"
yy137:
YYDEBUG(137, *YYCURSOR);
yyaccept = 2;
@@ -2802,7 +2786,7 @@ yy139:
yy140:
YYDEBUG(140, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1907 "Zend/zend_language_scanner.l"
+#line 1890 "Zend/zend_language_scanner.l"
{
while (YYCURSOR < YYLIMIT) {
switch (*YYCURSOR++) {
@@ -2831,14 +2815,14 @@ yy140:
RETURN_TOKEN(T_COMMENT);
}
-#line 2835 "Zend/zend_language_scanner.c"
+#line 2819 "Zend/zend_language_scanner.c"
yy141:
YYDEBUG(141, *YYCURSOR);
++YYCURSOR;
yy142:
YYDEBUG(142, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1975 "Zend/zend_language_scanner.l"
+#line 1958 "Zend/zend_language_scanner.l"
{
register char *s, *t;
char *end;
@@ -2906,14 +2890,14 @@ yy142:
}
RETURN_TOKEN(T_CONSTANT_ENCAPSED_STRING);
}
-#line 2910 "Zend/zend_language_scanner.c"
+#line 2894 "Zend/zend_language_scanner.c"
yy143:
YYDEBUG(143, *YYCURSOR);
++YYCURSOR;
yy144:
YYDEBUG(144, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 2044 "Zend/zend_language_scanner.l"
+#line 2027 "Zend/zend_language_scanner.l"
{
int bprefix = (yytext[0] != '"') ? 1 : 0;
@@ -2954,24 +2938,24 @@ yy144:
BEGIN(ST_DOUBLE_QUOTES);
RETURN_TOKEN('"');
}
-#line 2958 "Zend/zend_language_scanner.c"
+#line 2942 "Zend/zend_language_scanner.c"
yy145:
YYDEBUG(145, *YYCURSOR);
++YYCURSOR;
YYDEBUG(146, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 2134 "Zend/zend_language_scanner.l"
+#line 2117 "Zend/zend_language_scanner.l"
{
BEGIN(ST_BACKQUOTE);
RETURN_TOKEN('`');
}
-#line 2969 "Zend/zend_language_scanner.c"
+#line 2953 "Zend/zend_language_scanner.c"
yy147:
YYDEBUG(147, *YYCURSOR);
++YYCURSOR;
YYDEBUG(148, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 2396 "Zend/zend_language_scanner.l"
+#line 2379 "Zend/zend_language_scanner.l"
{
if (YYCURSOR > YYLIMIT) {
RETURN_TOKEN(END);
@@ -2980,7 +2964,7 @@ yy147:
zend_error(E_COMPILE_WARNING,"Unexpected character in input: '%c' (ASCII=%d) state=%d", yytext[0], yytext[0], YYSTATE);
goto restart;
}
-#line 2984 "Zend/zend_language_scanner.c"
+#line 2968 "Zend/zend_language_scanner.c"
yy149:
YYDEBUG(149, *YYCURSOR);
++YYCURSOR;
@@ -3007,7 +2991,7 @@ yy151:
yy153:
YYDEBUG(153, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1749 "Zend/zend_language_scanner.l"
+#line 1732 "Zend/zend_language_scanner.l"
{
const char *end;
@@ -3016,7 +3000,7 @@ yy153:
ZEND_ASSERT(end == yytext + yyleng);
RETURN_TOKEN(T_DNUMBER);
}
-#line 3020 "Zend/zend_language_scanner.c"
+#line 3004 "Zend/zend_language_scanner.c"
yy154:
YYDEBUG(154, *YYCURSOR);
yyaccept = 2;
@@ -3112,7 +3096,7 @@ yy163:
}
YYDEBUG(165, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1627 "Zend/zend_language_scanner.l"
+#line 1610 "Zend/zend_language_scanner.l"
{
char *bin = yytext + 2; /* Skip "0b" */
int len = yyleng - 2;
@@ -3140,7 +3124,7 @@ yy163:
RETURN_TOKEN(T_DNUMBER);
}
}
-#line 3144 "Zend/zend_language_scanner.c"
+#line 3128 "Zend/zend_language_scanner.c"
yy166:
YYDEBUG(166, *YYCURSOR);
++YYCURSOR;
@@ -3152,7 +3136,7 @@ yy166:
}
YYDEBUG(168, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1700 "Zend/zend_language_scanner.l"
+#line 1683 "Zend/zend_language_scanner.l"
{
char *hex = yytext + 2; /* Skip "0x" */
int len = yyleng - 2;
@@ -3180,7 +3164,7 @@ yy166:
RETURN_TOKEN(T_DNUMBER);
}
}
-#line 3184 "Zend/zend_language_scanner.c"
+#line 3168 "Zend/zend_language_scanner.c"
yy169:
YYDEBUG(169, *YYCURSOR);
++YYCURSOR;
@@ -3199,18 +3183,18 @@ yy169:
if (yych <= '_') goto yy169;
} else {
if (yych <= 'z') goto yy169;
- if (yych >= 0x7F) goto yy169;
+ if (yych >= 0x80) goto yy169;
}
}
yy171:
YYDEBUG(171, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1878 "Zend/zend_language_scanner.l"
+#line 1861 "Zend/zend_language_scanner.l"
{
zend_copy_value(zendlval, (yytext+1), (yyleng-1));
RETURN_TOKEN(T_VARIABLE);
}
-#line 3214 "Zend/zend_language_scanner.c"
+#line 3198 "Zend/zend_language_scanner.c"
yy172:
YYDEBUG(172, *YYCURSOR);
yych = *++YYCURSOR;
@@ -3224,11 +3208,11 @@ yy173:
}
YYDEBUG(174, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1573 "Zend/zend_language_scanner.l"
+#line 1556 "Zend/zend_language_scanner.l"
{
RETURN_TOKEN(T_LOGICAL_XOR);
}
-#line 3232 "Zend/zend_language_scanner.c"
+#line 3216 "Zend/zend_language_scanner.c"
yy175:
YYDEBUG(175, *YYCURSOR);
++YYCURSOR;
@@ -3237,71 +3221,71 @@ yy175:
}
YYDEBUG(176, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1565 "Zend/zend_language_scanner.l"
+#line 1548 "Zend/zend_language_scanner.l"
{
RETURN_TOKEN(T_LOGICAL_OR);
}
-#line 3245 "Zend/zend_language_scanner.c"
+#line 3229 "Zend/zend_language_scanner.c"
yy177:
YYDEBUG(177, *YYCURSOR);
++YYCURSOR;
YYDEBUG(178, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1553 "Zend/zend_language_scanner.l"
+#line 1536 "Zend/zend_language_scanner.l"
{
RETURN_TOKEN(T_XOR_EQUAL);
}
-#line 3255 "Zend/zend_language_scanner.c"
+#line 3239 "Zend/zend_language_scanner.c"
yy179:
YYDEBUG(179, *YYCURSOR);
++YYCURSOR;
YYDEBUG(180, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1557 "Zend/zend_language_scanner.l"
+#line 1540 "Zend/zend_language_scanner.l"
{
RETURN_TOKEN(T_BOOLEAN_OR);
}
-#line 3265 "Zend/zend_language_scanner.c"
+#line 3249 "Zend/zend_language_scanner.c"
yy181:
YYDEBUG(181, *YYCURSOR);
++YYCURSOR;
YYDEBUG(182, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1549 "Zend/zend_language_scanner.l"
+#line 1532 "Zend/zend_language_scanner.l"
{
RETURN_TOKEN(T_OR_EQUAL);
}
-#line 3275 "Zend/zend_language_scanner.c"
+#line 3259 "Zend/zend_language_scanner.c"
yy183:
YYDEBUG(183, *YYCURSOR);
++YYCURSOR;
YYDEBUG(184, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1561 "Zend/zend_language_scanner.l"
+#line 1544 "Zend/zend_language_scanner.l"
{
RETURN_TOKEN(T_BOOLEAN_AND);
}
-#line 3285 "Zend/zend_language_scanner.c"
+#line 3269 "Zend/zend_language_scanner.c"
yy185:
YYDEBUG(185, *YYCURSOR);
++YYCURSOR;
YYDEBUG(186, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1545 "Zend/zend_language_scanner.l"
+#line 1528 "Zend/zend_language_scanner.l"
{
RETURN_TOKEN(T_AND_EQUAL);
}
-#line 3295 "Zend/zend_language_scanner.c"
+#line 3279 "Zend/zend_language_scanner.c"
yy187:
YYDEBUG(187, *YYCURSOR);
++YYCURSOR;
YYDEBUG(188, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1533 "Zend/zend_language_scanner.l"
+#line 1516 "Zend/zend_language_scanner.l"
{
RETURN_TOKEN(T_MOD_EQUAL);
}
-#line 3305 "Zend/zend_language_scanner.c"
+#line 3289 "Zend/zend_language_scanner.c"
yy189:
YYDEBUG(189, *YYCURSOR);
yyaccept = 4;
@@ -3310,7 +3294,7 @@ yy189:
yy190:
YYDEBUG(190, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1936 "Zend/zend_language_scanner.l"
+#line 1919 "Zend/zend_language_scanner.l"
{
int doc_com;
@@ -3343,7 +3327,7 @@ yy190:
RETURN_TOKEN(T_COMMENT);
}
-#line 3347 "Zend/zend_language_scanner.c"
+#line 3331 "Zend/zend_language_scanner.c"
yy191:
YYDEBUG(191, *YYCURSOR);
yych = *++YYCURSOR;
@@ -3353,11 +3337,11 @@ yy192:
++YYCURSOR;
YYDEBUG(193, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1525 "Zend/zend_language_scanner.l"
+#line 1508 "Zend/zend_language_scanner.l"
{
RETURN_TOKEN(T_DIV_EQUAL);
}
-#line 3361 "Zend/zend_language_scanner.c"
+#line 3345 "Zend/zend_language_scanner.c"
yy194:
YYDEBUG(194, *YYCURSOR);
yych = *++YYCURSOR;
@@ -3381,62 +3365,62 @@ yy197:
if ((yych = *YYCURSOR) == '=') goto yy201;
YYDEBUG(198, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1517 "Zend/zend_language_scanner.l"
+#line 1500 "Zend/zend_language_scanner.l"
{
RETURN_TOKEN(T_POW);
}
-#line 3389 "Zend/zend_language_scanner.c"
+#line 3373 "Zend/zend_language_scanner.c"
yy199:
YYDEBUG(199, *YYCURSOR);
++YYCURSOR;
YYDEBUG(200, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1513 "Zend/zend_language_scanner.l"
+#line 1496 "Zend/zend_language_scanner.l"
{
RETURN_TOKEN(T_MUL_EQUAL);
}
-#line 3399 "Zend/zend_language_scanner.c"
+#line 3383 "Zend/zend_language_scanner.c"
yy201:
YYDEBUG(201, *YYCURSOR);
++YYCURSOR;
YYDEBUG(202, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1521 "Zend/zend_language_scanner.l"
+#line 1504 "Zend/zend_language_scanner.l"
{
RETURN_TOKEN(T_POW_EQUAL);
}
-#line 3409 "Zend/zend_language_scanner.c"
+#line 3393 "Zend/zend_language_scanner.c"
yy203:
YYDEBUG(203, *YYCURSOR);
++YYCURSOR;
if ((yych = *YYCURSOR) == '=') goto yy207;
YYDEBUG(204, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1581 "Zend/zend_language_scanner.l"
+#line 1564 "Zend/zend_language_scanner.l"
{
RETURN_TOKEN(T_SR);
}
-#line 3420 "Zend/zend_language_scanner.c"
+#line 3404 "Zend/zend_language_scanner.c"
yy205:
YYDEBUG(205, *YYCURSOR);
++YYCURSOR;
YYDEBUG(206, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1501 "Zend/zend_language_scanner.l"
+#line 1484 "Zend/zend_language_scanner.l"
{
RETURN_TOKEN(T_IS_GREATER_OR_EQUAL);
}
-#line 3430 "Zend/zend_language_scanner.c"
+#line 3414 "Zend/zend_language_scanner.c"
yy207:
YYDEBUG(207, *YYCURSOR);
++YYCURSOR;
YYDEBUG(208, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1541 "Zend/zend_language_scanner.l"
+#line 1524 "Zend/zend_language_scanner.l"
{
RETURN_TOKEN(T_SR_EQUAL);
}
-#line 3440 "Zend/zend_language_scanner.c"
+#line 3424 "Zend/zend_language_scanner.c"
yy209:
YYDEBUG(209, *YYCURSOR);
yyaccept = 5;
@@ -3447,53 +3431,53 @@ yy209:
yy210:
YYDEBUG(210, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1577 "Zend/zend_language_scanner.l"
+#line 1560 "Zend/zend_language_scanner.l"
{
RETURN_TOKEN(T_SL);
}
-#line 3455 "Zend/zend_language_scanner.c"
+#line 3439 "Zend/zend_language_scanner.c"
yy211:
YYDEBUG(211, *YYCURSOR);
++YYCURSOR;
if ((yych = *YYCURSOR) == '>') goto yy215;
YYDEBUG(212, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1497 "Zend/zend_language_scanner.l"
+#line 1480 "Zend/zend_language_scanner.l"
{
RETURN_TOKEN(T_IS_SMALLER_OR_EQUAL);
}
-#line 3466 "Zend/zend_language_scanner.c"
+#line 3450 "Zend/zend_language_scanner.c"
yy213:
YYDEBUG(213, *YYCURSOR);
++YYCURSOR;
yy214:
YYDEBUG(214, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1489 "Zend/zend_language_scanner.l"
+#line 1472 "Zend/zend_language_scanner.l"
{
RETURN_TOKEN(T_IS_NOT_EQUAL);
}
-#line 3477 "Zend/zend_language_scanner.c"
+#line 3461 "Zend/zend_language_scanner.c"
yy215:
YYDEBUG(215, *YYCURSOR);
++YYCURSOR;
YYDEBUG(216, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1493 "Zend/zend_language_scanner.l"
+#line 1476 "Zend/zend_language_scanner.l"
{
RETURN_TOKEN(T_SPACESHIP);
}
-#line 3487 "Zend/zend_language_scanner.c"
+#line 3471 "Zend/zend_language_scanner.c"
yy217:
YYDEBUG(217, *YYCURSOR);
++YYCURSOR;
YYDEBUG(218, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1537 "Zend/zend_language_scanner.l"
+#line 1520 "Zend/zend_language_scanner.l"
{
RETURN_TOKEN(T_SL_EQUAL);
}
-#line 3497 "Zend/zend_language_scanner.c"
+#line 3481 "Zend/zend_language_scanner.c"
yy219:
YYDEBUG(219, *YYCURSOR);
++YYCURSOR;
@@ -3516,7 +3500,7 @@ yy219:
if (yych != '_') goto yy157;
} else {
if (yych <= 'z') goto yy221;
- if (yych <= '~') goto yy157;
+ if (yych <= 0x7F) goto yy157;
}
}
yy221:
@@ -3543,7 +3527,7 @@ yy221:
} else {
if (yych <= '`') goto yy157;
if (yych <= 'z') goto yy221;
- if (yych <= '~') goto yy157;
+ if (yych <= 0x7F) goto yy157;
goto yy221;
}
}
@@ -3582,7 +3566,7 @@ yy226:
goto yy157;
} else {
if (yych <= 'z') goto yy225;
- if (yych <= '~') goto yy157;
+ if (yych <= 0x7F) goto yy157;
goto yy225;
}
}
@@ -3598,7 +3582,7 @@ yy228:
yy229:
YYDEBUG(229, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 2086 "Zend/zend_language_scanner.l"
+#line 2069 "Zend/zend_language_scanner.l"
{
char *s;
int bprefix = (yytext[0] != '<') ? 1 : 0;
@@ -3645,7 +3629,7 @@ yy229:
RETURN_TOKEN(T_START_HEREDOC);
}
-#line 3649 "Zend/zend_language_scanner.c"
+#line 3633 "Zend/zend_language_scanner.c"
yy230:
YYDEBUG(230, *YYCURSOR);
yych = *++YYCURSOR;
@@ -3673,7 +3657,7 @@ yy232:
goto yy157;
} else {
if (yych <= 'z') goto yy231;
- if (yych <= '~') goto yy157;
+ if (yych <= 0x7F) goto yy157;
goto yy231;
}
}
@@ -3685,31 +3669,31 @@ yy233:
++YYCURSOR;
YYDEBUG(235, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1481 "Zend/zend_language_scanner.l"
+#line 1464 "Zend/zend_language_scanner.l"
{
RETURN_TOKEN(T_IS_NOT_IDENTICAL);
}
-#line 3693 "Zend/zend_language_scanner.c"
+#line 3677 "Zend/zend_language_scanner.c"
yy236:
YYDEBUG(236, *YYCURSOR);
++YYCURSOR;
YYDEBUG(237, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1505 "Zend/zend_language_scanner.l"
+#line 1488 "Zend/zend_language_scanner.l"
{
RETURN_TOKEN(T_PLUS_EQUAL);
}
-#line 3703 "Zend/zend_language_scanner.c"
+#line 3687 "Zend/zend_language_scanner.c"
yy238:
YYDEBUG(238, *YYCURSOR);
++YYCURSOR;
YYDEBUG(239, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1469 "Zend/zend_language_scanner.l"
+#line 1452 "Zend/zend_language_scanner.l"
{
RETURN_TOKEN(T_INC);
}
-#line 3713 "Zend/zend_language_scanner.c"
+#line 3697 "Zend/zend_language_scanner.c"
yy240:
YYDEBUG(240, *YYCURSOR);
yych = *++YYCURSOR;
@@ -3728,42 +3712,42 @@ yy242:
}
YYDEBUG(243, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1457 "Zend/zend_language_scanner.l"
+#line 1440 "Zend/zend_language_scanner.l"
{
RETURN_TOKEN(T_LIST);
}
-#line 3736 "Zend/zend_language_scanner.c"
+#line 3720 "Zend/zend_language_scanner.c"
yy244:
YYDEBUG(244, *YYCURSOR);
++YYCURSOR;
if ((yych = *YYCURSOR) == '=') goto yy248;
YYDEBUG(245, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1485 "Zend/zend_language_scanner.l"
+#line 1468 "Zend/zend_language_scanner.l"
{
RETURN_TOKEN(T_IS_EQUAL);
}
-#line 3747 "Zend/zend_language_scanner.c"
+#line 3731 "Zend/zend_language_scanner.c"
yy246:
YYDEBUG(246, *YYCURSOR);
++YYCURSOR;
YYDEBUG(247, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1453 "Zend/zend_language_scanner.l"
+#line 1436 "Zend/zend_language_scanner.l"
{
RETURN_TOKEN(T_DOUBLE_ARROW);
}
-#line 3757 "Zend/zend_language_scanner.c"
+#line 3741 "Zend/zend_language_scanner.c"
yy248:
YYDEBUG(248, *YYCURSOR);
++YYCURSOR;
YYDEBUG(249, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1477 "Zend/zend_language_scanner.l"
+#line 1460 "Zend/zend_language_scanner.l"
{
RETURN_TOKEN(T_IS_IDENTICAL);
}
-#line 3767 "Zend/zend_language_scanner.c"
+#line 3751 "Zend/zend_language_scanner.c"
yy250:
YYDEBUG(250, *YYCURSOR);
yych = *++YYCURSOR;
@@ -3893,11 +3877,11 @@ yy266:
}
YYDEBUG(269, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1786 "Zend/zend_language_scanner.l"
+#line 1769 "Zend/zend_language_scanner.l"
{
RETURN_TOKEN(T_NS_C);
}
-#line 3901 "Zend/zend_language_scanner.c"
+#line 3885 "Zend/zend_language_scanner.c"
yy270:
YYDEBUG(270, *YYCURSOR);
yych = *++YYCURSOR;
@@ -3917,11 +3901,11 @@ yy271:
}
YYDEBUG(274, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1782 "Zend/zend_language_scanner.l"
+#line 1765 "Zend/zend_language_scanner.l"
{
RETURN_TOKEN(T_DIR);
}
-#line 3925 "Zend/zend_language_scanner.c"
+#line 3909 "Zend/zend_language_scanner.c"
yy275:
YYDEBUG(275, *YYCURSOR);
yych = *++YYCURSOR;
@@ -3946,11 +3930,11 @@ yy277:
}
YYDEBUG(280, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1774 "Zend/zend_language_scanner.l"
+#line 1757 "Zend/zend_language_scanner.l"
{
RETURN_TOKEN(T_LINE);
}
-#line 3954 "Zend/zend_language_scanner.c"
+#line 3938 "Zend/zend_language_scanner.c"
yy281:
YYDEBUG(281, *YYCURSOR);
yych = *++YYCURSOR;
@@ -3985,11 +3969,11 @@ yy285:
}
YYDEBUG(288, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1770 "Zend/zend_language_scanner.l"
+#line 1753 "Zend/zend_language_scanner.l"
{
RETURN_TOKEN(T_METHOD_C);
}
-#line 3993 "Zend/zend_language_scanner.c"
+#line 3977 "Zend/zend_language_scanner.c"
yy289:
YYDEBUG(289, *YYCURSOR);
yych = *++YYCURSOR;
@@ -4040,11 +4024,11 @@ yy296:
}
YYDEBUG(299, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1766 "Zend/zend_language_scanner.l"
+#line 1749 "Zend/zend_language_scanner.l"
{
RETURN_TOKEN(T_FUNC_C);
}
-#line 4048 "Zend/zend_language_scanner.c"
+#line 4032 "Zend/zend_language_scanner.c"
yy300:
YYDEBUG(300, *YYCURSOR);
yych = *++YYCURSOR;
@@ -4064,11 +4048,11 @@ yy301:
}
YYDEBUG(304, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1778 "Zend/zend_language_scanner.l"
+#line 1761 "Zend/zend_language_scanner.l"
{
RETURN_TOKEN(T_FILE);
}
-#line 4072 "Zend/zend_language_scanner.c"
+#line 4056 "Zend/zend_language_scanner.c"
yy305:
YYDEBUG(305, *YYCURSOR);
yych = *++YYCURSOR;
@@ -4098,11 +4082,11 @@ yy308:
}
YYDEBUG(311, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1762 "Zend/zend_language_scanner.l"
+#line 1745 "Zend/zend_language_scanner.l"
{
RETURN_TOKEN(T_TRAIT_C);
}
-#line 4106 "Zend/zend_language_scanner.c"
+#line 4090 "Zend/zend_language_scanner.c"
yy312:
YYDEBUG(312, *YYCURSOR);
yych = *++YYCURSOR;
@@ -4132,11 +4116,11 @@ yy315:
}
YYDEBUG(318, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1758 "Zend/zend_language_scanner.l"
+#line 1741 "Zend/zend_language_scanner.l"
{
RETURN_TOKEN(T_CLASS_C);
}
-#line 4140 "Zend/zend_language_scanner.c"
+#line 4124 "Zend/zend_language_scanner.c"
yy319:
YYDEBUG(319, *YYCURSOR);
yych = *++YYCURSOR;
@@ -4198,11 +4182,11 @@ yy330:
}
YYDEBUG(331, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1421 "Zend/zend_language_scanner.l"
+#line 1404 "Zend/zend_language_scanner.l"
{
RETURN_TOKEN(T_HALT_COMPILER);
}
-#line 4206 "Zend/zend_language_scanner.c"
+#line 4190 "Zend/zend_language_scanner.c"
yy332:
YYDEBUG(332, *YYCURSOR);
yych = *++YYCURSOR;
@@ -4222,11 +4206,11 @@ yy334:
}
YYDEBUG(335, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1401 "Zend/zend_language_scanner.l"
+#line 1384 "Zend/zend_language_scanner.l"
{
RETURN_TOKEN(T_USE);
}
-#line 4230 "Zend/zend_language_scanner.c"
+#line 4214 "Zend/zend_language_scanner.c"
yy336:
YYDEBUG(336, *YYCURSOR);
yych = *++YYCURSOR;
@@ -4245,11 +4229,11 @@ yy338:
}
YYDEBUG(339, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1449 "Zend/zend_language_scanner.l"
+#line 1432 "Zend/zend_language_scanner.l"
{
RETURN_TOKEN(T_UNSET);
}
-#line 4253 "Zend/zend_language_scanner.c"
+#line 4237 "Zend/zend_language_scanner.c"
yy340:
YYDEBUG(340, *YYCURSOR);
++YYCURSOR;
@@ -4421,11 +4405,11 @@ yy355:
++YYCURSOR;
YYDEBUG(357, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1349 "Zend/zend_language_scanner.l"
+#line 1332 "Zend/zend_language_scanner.l"
{
RETURN_TOKEN(T_INT_CAST);
}
-#line 4429 "Zend/zend_language_scanner.c"
+#line 4413 "Zend/zend_language_scanner.c"
yy358:
YYDEBUG(358, *YYCURSOR);
yych = *++YYCURSOR;
@@ -4469,11 +4453,11 @@ yy363:
++YYCURSOR;
YYDEBUG(366, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1353 "Zend/zend_language_scanner.l"
+#line 1336 "Zend/zend_language_scanner.l"
{
RETURN_TOKEN(T_DOUBLE_CAST);
}
-#line 4477 "Zend/zend_language_scanner.c"
+#line 4461 "Zend/zend_language_scanner.c"
yy367:
YYDEBUG(367, *YYCURSOR);
yych = *++YYCURSOR;
@@ -4543,11 +4527,11 @@ yy377:
++YYCURSOR;
YYDEBUG(380, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1357 "Zend/zend_language_scanner.l"
+#line 1340 "Zend/zend_language_scanner.l"
{
RETURN_TOKEN(T_STRING_CAST);
}
-#line 4551 "Zend/zend_language_scanner.c"
+#line 4535 "Zend/zend_language_scanner.c"
yy381:
YYDEBUG(381, *YYCURSOR);
yych = *++YYCURSOR;
@@ -4580,11 +4564,11 @@ yy384:
++YYCURSOR;
YYDEBUG(387, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1361 "Zend/zend_language_scanner.l"
+#line 1344 "Zend/zend_language_scanner.l"
{
RETURN_TOKEN(T_ARRAY_CAST);
}
-#line 4588 "Zend/zend_language_scanner.c"
+#line 4572 "Zend/zend_language_scanner.c"
yy388:
YYDEBUG(388, *YYCURSOR);
yych = *++YYCURSOR;
@@ -4622,11 +4606,11 @@ yy392:
++YYCURSOR;
YYDEBUG(395, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1365 "Zend/zend_language_scanner.l"
+#line 1348 "Zend/zend_language_scanner.l"
{
RETURN_TOKEN(T_OBJECT_CAST);
}
-#line 4630 "Zend/zend_language_scanner.c"
+#line 4614 "Zend/zend_language_scanner.c"
yy396:
YYDEBUG(396, *YYCURSOR);
yych = *++YYCURSOR;
@@ -4667,11 +4651,11 @@ yy401:
++YYCURSOR;
YYDEBUG(403, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1369 "Zend/zend_language_scanner.l"
+#line 1352 "Zend/zend_language_scanner.l"
{
RETURN_TOKEN(T_BOOL_CAST);
}
-#line 4675 "Zend/zend_language_scanner.c"
+#line 4659 "Zend/zend_language_scanner.c"
yy404:
YYDEBUG(404, *YYCURSOR);
yych = *++YYCURSOR;
@@ -4731,11 +4715,11 @@ yy412:
++YYCURSOR;
YYDEBUG(415, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1373 "Zend/zend_language_scanner.l"
+#line 1356 "Zend/zend_language_scanner.l"
{
RETURN_TOKEN(T_UNSET_CAST);
}
-#line 4739 "Zend/zend_language_scanner.c"
+#line 4723 "Zend/zend_language_scanner.c"
yy416:
YYDEBUG(416, *YYCURSOR);
yych = *++YYCURSOR;
@@ -4749,11 +4733,11 @@ yy417:
}
YYDEBUG(418, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1345 "Zend/zend_language_scanner.l"
+#line 1328 "Zend/zend_language_scanner.l"
{
RETURN_TOKEN(T_VAR);
}
-#line 4757 "Zend/zend_language_scanner.c"
+#line 4741 "Zend/zend_language_scanner.c"
yy419:
YYDEBUG(419, *YYCURSOR);
yych = *++YYCURSOR;
@@ -4773,11 +4757,11 @@ yy421:
}
YYDEBUG(422, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1337 "Zend/zend_language_scanner.l"
+#line 1320 "Zend/zend_language_scanner.l"
{
RETURN_TOKEN(T_NEW);
}
-#line 4781 "Zend/zend_language_scanner.c"
+#line 4765 "Zend/zend_language_scanner.c"
yy423:
YYDEBUG(423, *YYCURSOR);
yych = *++YYCURSOR;
@@ -4816,11 +4800,11 @@ yy429:
}
YYDEBUG(430, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1397 "Zend/zend_language_scanner.l"
+#line 1380 "Zend/zend_language_scanner.l"
{
RETURN_TOKEN(T_NAMESPACE);
}
-#line 4824 "Zend/zend_language_scanner.c"
+#line 4808 "Zend/zend_language_scanner.c"
yy431:
YYDEBUG(431, *YYCURSOR);
++YYCURSOR;
@@ -4829,22 +4813,22 @@ yy431:
yy432:
YYDEBUG(432, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1969 "Zend/zend_language_scanner.l"
+#line 1952 "Zend/zend_language_scanner.l"
{
BEGIN(INITIAL);
RETURN_TOKEN(T_CLOSE_TAG); /* implicit ';' at php-end tag */
}
-#line 4838 "Zend/zend_language_scanner.c"
+#line 4822 "Zend/zend_language_scanner.c"
yy433:
YYDEBUG(433, *YYCURSOR);
++YYCURSOR;
YYDEBUG(434, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1333 "Zend/zend_language_scanner.l"
+#line 1316 "Zend/zend_language_scanner.l"
{
RETURN_TOKEN(T_COALESCE);
}
-#line 4848 "Zend/zend_language_scanner.c"
+#line 4832 "Zend/zend_language_scanner.c"
yy435:
YYDEBUG(435, *YYCURSOR);
yych = *++YYCURSOR;
@@ -4875,11 +4859,11 @@ yy439:
++YYCURSOR;
YYDEBUG(440, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1529 "Zend/zend_language_scanner.l"
+#line 1512 "Zend/zend_language_scanner.l"
{
RETURN_TOKEN(T_CONCAT_EQUAL);
}
-#line 4883 "Zend/zend_language_scanner.c"
+#line 4867 "Zend/zend_language_scanner.c"
yy441:
YYDEBUG(441, *YYCURSOR);
yych = *++YYCURSOR;
@@ -4888,21 +4872,21 @@ yy441:
++YYCURSOR;
YYDEBUG(443, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1329 "Zend/zend_language_scanner.l"
+#line 1312 "Zend/zend_language_scanner.l"
{
RETURN_TOKEN(T_ELLIPSIS);
}
-#line 4896 "Zend/zend_language_scanner.c"
+#line 4880 "Zend/zend_language_scanner.c"
yy444:
YYDEBUG(444, *YYCURSOR);
++YYCURSOR;
YYDEBUG(445, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1321 "Zend/zend_language_scanner.l"
+#line 1304 "Zend/zend_language_scanner.l"
{
RETURN_TOKEN(T_PAAMAYIM_NEKUDOTAYIM);
}
-#line 4906 "Zend/zend_language_scanner.c"
+#line 4890 "Zend/zend_language_scanner.c"
yy446:
YYDEBUG(446, *YYCURSOR);
++YYCURSOR;
@@ -4924,32 +4908,32 @@ yy448:
++YYCURSOR;
YYDEBUG(449, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1509 "Zend/zend_language_scanner.l"
+#line 1492 "Zend/zend_language_scanner.l"
{
RETURN_TOKEN(T_MINUS_EQUAL);
}
-#line 4932 "Zend/zend_language_scanner.c"
+#line 4916 "Zend/zend_language_scanner.c"
yy450:
YYDEBUG(450, *YYCURSOR);
++YYCURSOR;
YYDEBUG(451, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1473 "Zend/zend_language_scanner.l"
+#line 1456 "Zend/zend_language_scanner.l"
{
RETURN_TOKEN(T_DEC);
}
-#line 4942 "Zend/zend_language_scanner.c"
+#line 4926 "Zend/zend_language_scanner.c"
yy452:
YYDEBUG(452, *YYCURSOR);
++YYCURSOR;
YYDEBUG(453, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1295 "Zend/zend_language_scanner.l"
+#line 1278 "Zend/zend_language_scanner.l"
{
yy_push_state(ST_LOOKING_FOR_PROPERTY);
RETURN_TOKEN(T_OBJECT_OPERATOR);
}
-#line 4953 "Zend/zend_language_scanner.c"
+#line 4937 "Zend/zend_language_scanner.c"
yy454:
YYDEBUG(454, *YYCURSOR);
yych = *++YYCURSOR;
@@ -4994,11 +4978,11 @@ yy459:
}
YYDEBUG(460, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1445 "Zend/zend_language_scanner.l"
+#line 1428 "Zend/zend_language_scanner.l"
{
RETURN_TOKEN(T_PUBLIC);
}
-#line 5002 "Zend/zend_language_scanner.c"
+#line 4986 "Zend/zend_language_scanner.c"
yy461:
YYDEBUG(461, *YYCURSOR);
yych = *++YYCURSOR;
@@ -5053,11 +5037,11 @@ yy468:
}
YYDEBUG(469, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1441 "Zend/zend_language_scanner.l"
+#line 1424 "Zend/zend_language_scanner.l"
{
RETURN_TOKEN(T_PROTECTED);
}
-#line 5061 "Zend/zend_language_scanner.c"
+#line 5045 "Zend/zend_language_scanner.c"
yy470:
YYDEBUG(470, *YYCURSOR);
yych = *++YYCURSOR;
@@ -5087,11 +5071,11 @@ yy474:
}
YYDEBUG(475, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1437 "Zend/zend_language_scanner.l"
+#line 1420 "Zend/zend_language_scanner.l"
{
RETURN_TOKEN(T_PRIVATE);
}
-#line 5095 "Zend/zend_language_scanner.c"
+#line 5079 "Zend/zend_language_scanner.c"
yy476:
YYDEBUG(476, *YYCURSOR);
++YYCURSOR;
@@ -5100,11 +5084,11 @@ yy476:
}
YYDEBUG(477, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1271 "Zend/zend_language_scanner.l"
+#line 1254 "Zend/zend_language_scanner.l"
{
RETURN_TOKEN(T_PRINT);
}
-#line 5108 "Zend/zend_language_scanner.c"
+#line 5092 "Zend/zend_language_scanner.c"
yy478:
YYDEBUG(478, *YYCURSOR);
yych = *++YYCURSOR;
@@ -5129,11 +5113,11 @@ yy481:
}
YYDEBUG(482, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1263 "Zend/zend_language_scanner.l"
+#line 1246 "Zend/zend_language_scanner.l"
{
RETURN_TOKEN(T_GOTO);
}
-#line 5137 "Zend/zend_language_scanner.c"
+#line 5121 "Zend/zend_language_scanner.c"
yy483:
YYDEBUG(483, *YYCURSOR);
yych = *++YYCURSOR;
@@ -5157,11 +5141,11 @@ yy486:
}
YYDEBUG(487, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1409 "Zend/zend_language_scanner.l"
+#line 1392 "Zend/zend_language_scanner.l"
{
RETURN_TOKEN(T_GLOBAL);
}
-#line 5165 "Zend/zend_language_scanner.c"
+#line 5149 "Zend/zend_language_scanner.c"
yy488:
YYDEBUG(488, *YYCURSOR);
yych = *++YYCURSOR;
@@ -5198,11 +5182,11 @@ yy494:
}
YYDEBUG(495, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1255 "Zend/zend_language_scanner.l"
+#line 1238 "Zend/zend_language_scanner.l"
{
RETURN_TOKEN(T_BREAK);
}
-#line 5206 "Zend/zend_language_scanner.c"
+#line 5190 "Zend/zend_language_scanner.c"
yy496:
YYDEBUG(496, *YYCURSOR);
yych = *++YYCURSOR;
@@ -5242,11 +5226,11 @@ yy502:
}
YYDEBUG(503, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1239 "Zend/zend_language_scanner.l"
+#line 1222 "Zend/zend_language_scanner.l"
{
RETURN_TOKEN(T_SWITCH);
}
-#line 5250 "Zend/zend_language_scanner.c"
+#line 5234 "Zend/zend_language_scanner.c"
yy504:
YYDEBUG(504, *YYCURSOR);
yych = *++YYCURSOR;
@@ -5270,11 +5254,11 @@ yy507:
}
YYDEBUG(508, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1425 "Zend/zend_language_scanner.l"
+#line 1408 "Zend/zend_language_scanner.l"
{
RETURN_TOKEN(T_STATIC);
}
-#line 5278 "Zend/zend_language_scanner.c"
+#line 5262 "Zend/zend_language_scanner.c"
yy509:
YYDEBUG(509, *YYCURSOR);
yych = *++YYCURSOR;
@@ -5301,11 +5285,11 @@ yy512:
}
YYDEBUG(513, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1235 "Zend/zend_language_scanner.l"
+#line 1218 "Zend/zend_language_scanner.l"
{
RETURN_TOKEN(T_AS);
}
-#line 5309 "Zend/zend_language_scanner.c"
+#line 5293 "Zend/zend_language_scanner.c"
yy514:
YYDEBUG(514, *YYCURSOR);
yych = *++YYCURSOR;
@@ -5324,11 +5308,11 @@ yy516:
}
YYDEBUG(517, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1461 "Zend/zend_language_scanner.l"
+#line 1444 "Zend/zend_language_scanner.l"
{
RETURN_TOKEN(T_ARRAY);
}
-#line 5332 "Zend/zend_language_scanner.c"
+#line 5316 "Zend/zend_language_scanner.c"
yy518:
YYDEBUG(518, *YYCURSOR);
++YYCURSOR;
@@ -5337,11 +5321,11 @@ yy518:
}
YYDEBUG(519, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1569 "Zend/zend_language_scanner.l"
+#line 1552 "Zend/zend_language_scanner.l"
{
RETURN_TOKEN(T_LOGICAL_AND);
}
-#line 5345 "Zend/zend_language_scanner.c"
+#line 5329 "Zend/zend_language_scanner.c"
yy520:
YYDEBUG(520, *YYCURSOR);
yych = *++YYCURSOR;
@@ -5375,11 +5359,11 @@ yy525:
}
YYDEBUG(526, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1429 "Zend/zend_language_scanner.l"
+#line 1412 "Zend/zend_language_scanner.l"
{
RETURN_TOKEN(T_ABSTRACT);
}
-#line 5383 "Zend/zend_language_scanner.c"
+#line 5367 "Zend/zend_language_scanner.c"
yy527:
YYDEBUG(527, *YYCURSOR);
yych = *++YYCURSOR;
@@ -5403,11 +5387,11 @@ yy530:
}
YYDEBUG(531, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1195 "Zend/zend_language_scanner.l"
+#line 1178 "Zend/zend_language_scanner.l"
{
RETURN_TOKEN(T_WHILE);
}
-#line 5411 "Zend/zend_language_scanner.c"
+#line 5395 "Zend/zend_language_scanner.c"
yy532:
YYDEBUG(532, *YYCURSOR);
++YYCURSOR;
@@ -5416,11 +5400,11 @@ yy532:
}
YYDEBUG(533, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1179 "Zend/zend_language_scanner.l"
+#line 1162 "Zend/zend_language_scanner.l"
{
RETURN_TOKEN(T_IF);
}
-#line 5424 "Zend/zend_language_scanner.c"
+#line 5408 "Zend/zend_language_scanner.c"
yy534:
YYDEBUG(534, *YYCURSOR);
yych = *++YYCURSOR;
@@ -5472,11 +5456,11 @@ yy539:
}
YYDEBUG(540, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1413 "Zend/zend_language_scanner.l"
+#line 1396 "Zend/zend_language_scanner.l"
{
RETURN_TOKEN(T_ISSET);
}
-#line 5480 "Zend/zend_language_scanner.c"
+#line 5464 "Zend/zend_language_scanner.c"
yy541:
YYDEBUG(541, *YYCURSOR);
yych = *++YYCURSOR;
@@ -5524,17 +5508,17 @@ yy547:
if (yych <= '_') goto yy549;
} else {
if (yych <= 'z') goto yy149;
- if (yych >= 0x7F) goto yy149;
+ if (yych >= 0x80) goto yy149;
}
}
yy548:
YYDEBUG(548, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1381 "Zend/zend_language_scanner.l"
+#line 1364 "Zend/zend_language_scanner.l"
{
RETURN_TOKEN(T_INCLUDE);
}
-#line 5538 "Zend/zend_language_scanner.c"
+#line 5522 "Zend/zend_language_scanner.c"
yy549:
YYDEBUG(549, *YYCURSOR);
yych = *++YYCURSOR;
@@ -5563,11 +5547,11 @@ yy553:
}
YYDEBUG(554, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1385 "Zend/zend_language_scanner.l"
+#line 1368 "Zend/zend_language_scanner.l"
{
RETURN_TOKEN(T_INCLUDE_ONCE);
}
-#line 5571 "Zend/zend_language_scanner.c"
+#line 5555 "Zend/zend_language_scanner.c"
yy555:
YYDEBUG(555, *YYCURSOR);
yych = *++YYCURSOR;
@@ -5601,11 +5585,11 @@ yy560:
}
YYDEBUG(561, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1279 "Zend/zend_language_scanner.l"
+#line 1262 "Zend/zend_language_scanner.l"
{
RETURN_TOKEN(T_INTERFACE);
}
-#line 5609 "Zend/zend_language_scanner.c"
+#line 5593 "Zend/zend_language_scanner.c"
yy562:
YYDEBUG(562, *YYCURSOR);
yych = *++YYCURSOR;
@@ -5655,11 +5639,11 @@ yy568:
}
YYDEBUG(569, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1405 "Zend/zend_language_scanner.l"
+#line 1388 "Zend/zend_language_scanner.l"
{
RETURN_TOKEN(T_INSTEADOF);
}
-#line 5663 "Zend/zend_language_scanner.c"
+#line 5647 "Zend/zend_language_scanner.c"
yy570:
YYDEBUG(570, *YYCURSOR);
yych = *++YYCURSOR;
@@ -5688,11 +5672,11 @@ yy574:
}
YYDEBUG(575, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1231 "Zend/zend_language_scanner.l"
+#line 1214 "Zend/zend_language_scanner.l"
{
RETURN_TOKEN(T_INSTANCEOF);
}
-#line 5696 "Zend/zend_language_scanner.c"
+#line 5680 "Zend/zend_language_scanner.c"
yy576:
YYDEBUG(576, *YYCURSOR);
yych = *++YYCURSOR;
@@ -5736,11 +5720,11 @@ yy583:
}
YYDEBUG(584, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1291 "Zend/zend_language_scanner.l"
+#line 1274 "Zend/zend_language_scanner.l"
{
RETURN_TOKEN(T_IMPLEMENTS);
}
-#line 5744 "Zend/zend_language_scanner.c"
+#line 5728 "Zend/zend_language_scanner.c"
yy585:
YYDEBUG(585, *YYCURSOR);
yych = *++YYCURSOR;
@@ -5768,11 +5752,11 @@ yy586:
}
YYDEBUG(588, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1163 "Zend/zend_language_scanner.l"
+#line 1146 "Zend/zend_language_scanner.l"
{
RETURN_TOKEN(T_TRY);
}
-#line 5776 "Zend/zend_language_scanner.c"
+#line 5760 "Zend/zend_language_scanner.c"
yy589:
YYDEBUG(589, *YYCURSOR);
yych = *++YYCURSOR;
@@ -5791,11 +5775,11 @@ yy591:
}
YYDEBUG(592, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1283 "Zend/zend_language_scanner.l"
+#line 1266 "Zend/zend_language_scanner.l"
{
RETURN_TOKEN(T_TRAIT);
}
-#line 5799 "Zend/zend_language_scanner.c"
+#line 5783 "Zend/zend_language_scanner.c"
yy593:
YYDEBUG(593, *YYCURSOR);
yych = *++YYCURSOR;
@@ -5814,11 +5798,11 @@ yy595:
}
YYDEBUG(596, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1175 "Zend/zend_language_scanner.l"
+#line 1158 "Zend/zend_language_scanner.l"
{
RETURN_TOKEN(T_THROW);
}
-#line 5822 "Zend/zend_language_scanner.c"
+#line 5806 "Zend/zend_language_scanner.c"
yy597:
YYDEBUG(597, *YYCURSOR);
yych = *++YYCURSOR;
@@ -5851,11 +5835,11 @@ yy600:
yy601:
YYDEBUG(601, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1159 "Zend/zend_language_scanner.l"
+#line 1142 "Zend/zend_language_scanner.l"
{
RETURN_TOKEN(T_YIELD);
}
-#line 5859 "Zend/zend_language_scanner.c"
+#line 5843 "Zend/zend_language_scanner.c"
yy602:
YYDEBUG(602, *YYCURSOR);
++YYCURSOR;
@@ -5897,12 +5881,12 @@ yy607:
++YYCURSOR;
YYDEBUG(608, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1154 "Zend/zend_language_scanner.l"
+#line 1137 "Zend/zend_language_scanner.l"
{
HANDLE_NEWLINES(yytext, yyleng);
RETURN_TOKEN(T_YIELD_FROM);
}
-#line 5906 "Zend/zend_language_scanner.c"
+#line 5890 "Zend/zend_language_scanner.c"
yy609:
YYDEBUG(609, *YYCURSOR);
yych = *++YYCURSOR;
@@ -5957,17 +5941,17 @@ yy615:
if (yych <= '_') goto yy617;
} else {
if (yych <= 'z') goto yy149;
- if (yych >= 0x7F) goto yy149;
+ if (yych >= 0x80) goto yy149;
}
}
yy616:
YYDEBUG(616, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1389 "Zend/zend_language_scanner.l"
+#line 1372 "Zend/zend_language_scanner.l"
{
RETURN_TOKEN(T_REQUIRE);
}
-#line 5971 "Zend/zend_language_scanner.c"
+#line 5955 "Zend/zend_language_scanner.c"
yy617:
YYDEBUG(617, *YYCURSOR);
yych = *++YYCURSOR;
@@ -5996,11 +5980,11 @@ yy621:
}
YYDEBUG(622, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1393 "Zend/zend_language_scanner.l"
+#line 1376 "Zend/zend_language_scanner.l"
{
RETURN_TOKEN(T_REQUIRE_ONCE);
}
-#line 6004 "Zend/zend_language_scanner.c"
+#line 5988 "Zend/zend_language_scanner.c"
yy623:
YYDEBUG(623, *YYCURSOR);
yych = *++YYCURSOR;
@@ -6019,11 +6003,11 @@ yy625:
}
YYDEBUG(626, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1150 "Zend/zend_language_scanner.l"
+#line 1133 "Zend/zend_language_scanner.l"
{
RETURN_TOKEN(T_RETURN);
}
-#line 6027 "Zend/zend_language_scanner.c"
+#line 6011 "Zend/zend_language_scanner.c"
yy627:
YYDEBUG(627, *YYCURSOR);
yych = *++YYCURSOR;
@@ -6113,11 +6097,11 @@ yy636:
}
YYDEBUG(637, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1259 "Zend/zend_language_scanner.l"
+#line 1242 "Zend/zend_language_scanner.l"
{
RETURN_TOKEN(T_CONTINUE);
}
-#line 6121 "Zend/zend_language_scanner.c"
+#line 6105 "Zend/zend_language_scanner.c"
yy638:
YYDEBUG(638, *YYCURSOR);
++YYCURSOR;
@@ -6126,11 +6110,11 @@ yy638:
}
YYDEBUG(639, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1146 "Zend/zend_language_scanner.l"
+#line 1129 "Zend/zend_language_scanner.l"
{
RETURN_TOKEN(T_CONST);
}
-#line 6134 "Zend/zend_language_scanner.c"
+#line 6118 "Zend/zend_language_scanner.c"
yy640:
YYDEBUG(640, *YYCURSOR);
yych = *++YYCURSOR;
@@ -6155,11 +6139,11 @@ yy643:
}
YYDEBUG(644, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1341 "Zend/zend_language_scanner.l"
+#line 1324 "Zend/zend_language_scanner.l"
{
RETURN_TOKEN(T_CLONE);
}
-#line 6163 "Zend/zend_language_scanner.c"
+#line 6147 "Zend/zend_language_scanner.c"
yy645:
YYDEBUG(645, *YYCURSOR);
yych = *++YYCURSOR;
@@ -6173,11 +6157,11 @@ yy646:
}
YYDEBUG(647, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1275 "Zend/zend_language_scanner.l"
+#line 1258 "Zend/zend_language_scanner.l"
{
RETURN_TOKEN(T_CLASS);
}
-#line 6181 "Zend/zend_language_scanner.c"
+#line 6165 "Zend/zend_language_scanner.c"
yy648:
YYDEBUG(648, *YYCURSOR);
yych = *++YYCURSOR;
@@ -6223,11 +6207,11 @@ yy655:
}
YYDEBUG(656, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1465 "Zend/zend_language_scanner.l"
+#line 1448 "Zend/zend_language_scanner.l"
{
RETURN_TOKEN(T_CALLABLE);
}
-#line 6231 "Zend/zend_language_scanner.c"
+#line 6215 "Zend/zend_language_scanner.c"
yy657:
YYDEBUG(657, *YYCURSOR);
++YYCURSOR;
@@ -6236,11 +6220,11 @@ yy657:
}
YYDEBUG(658, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1247 "Zend/zend_language_scanner.l"
+#line 1230 "Zend/zend_language_scanner.l"
{
RETURN_TOKEN(T_CASE);
}
-#line 6244 "Zend/zend_language_scanner.c"
+#line 6228 "Zend/zend_language_scanner.c"
yy659:
YYDEBUG(659, *YYCURSOR);
yych = *++YYCURSOR;
@@ -6254,11 +6238,11 @@ yy660:
}
YYDEBUG(661, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1167 "Zend/zend_language_scanner.l"
+#line 1150 "Zend/zend_language_scanner.l"
{
RETURN_TOKEN(T_CATCH);
}
-#line 6262 "Zend/zend_language_scanner.c"
+#line 6246 "Zend/zend_language_scanner.c"
yy662:
YYDEBUG(662, *YYCURSOR);
yych = *++YYCURSOR;
@@ -6309,11 +6293,11 @@ yy670:
}
YYDEBUG(671, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1142 "Zend/zend_language_scanner.l"
+#line 1125 "Zend/zend_language_scanner.l"
{
RETURN_TOKEN(T_FUNCTION);
}
-#line 6317 "Zend/zend_language_scanner.c"
+#line 6301 "Zend/zend_language_scanner.c"
yy672:
YYDEBUG(672, *YYCURSOR);
++YYCURSOR;
@@ -6331,17 +6315,17 @@ yy672:
} else {
if (yych <= 'e') goto yy674;
if (yych <= 'z') goto yy149;
- if (yych >= 0x7F) goto yy149;
+ if (yych >= 0x80) goto yy149;
}
}
yy673:
YYDEBUG(673, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1207 "Zend/zend_language_scanner.l"
+#line 1190 "Zend/zend_language_scanner.l"
{
RETURN_TOKEN(T_FOR);
}
-#line 6345 "Zend/zend_language_scanner.c"
+#line 6329 "Zend/zend_language_scanner.c"
yy674:
YYDEBUG(674, *YYCURSOR);
yych = *++YYCURSOR;
@@ -6365,11 +6349,11 @@ yy677:
}
YYDEBUG(678, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1215 "Zend/zend_language_scanner.l"
+#line 1198 "Zend/zend_language_scanner.l"
{
RETURN_TOKEN(T_FOREACH);
}
-#line 6373 "Zend/zend_language_scanner.c"
+#line 6357 "Zend/zend_language_scanner.c"
yy679:
YYDEBUG(679, *YYCURSOR);
yych = *++YYCURSOR;
@@ -6397,17 +6381,17 @@ yy681:
} else {
if (yych <= 'l') goto yy683;
if (yych <= 'z') goto yy149;
- if (yych >= 0x7F) goto yy149;
+ if (yych >= 0x80) goto yy149;
}
}
yy682:
YYDEBUG(682, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1433 "Zend/zend_language_scanner.l"
+#line 1416 "Zend/zend_language_scanner.l"
{
RETURN_TOKEN(T_FINAL);
}
-#line 6411 "Zend/zend_language_scanner.c"
+#line 6395 "Zend/zend_language_scanner.c"
yy683:
YYDEBUG(683, *YYCURSOR);
yych = *++YYCURSOR;
@@ -6421,11 +6405,11 @@ yy684:
}
YYDEBUG(685, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1171 "Zend/zend_language_scanner.l"
+#line 1154 "Zend/zend_language_scanner.l"
{
RETURN_TOKEN(T_FINALLY);
}
-#line 6429 "Zend/zend_language_scanner.c"
+#line 6413 "Zend/zend_language_scanner.c"
yy686:
YYDEBUG(686, *YYCURSOR);
yych = *++YYCURSOR;
@@ -6456,11 +6440,11 @@ yy688:
}
YYDEBUG(689, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1203 "Zend/zend_language_scanner.l"
+#line 1186 "Zend/zend_language_scanner.l"
{
RETURN_TOKEN(T_DO);
}
-#line 6464 "Zend/zend_language_scanner.c"
+#line 6448 "Zend/zend_language_scanner.c"
yy690:
YYDEBUG(690, *YYCURSOR);
++YYCURSOR;
@@ -6469,11 +6453,11 @@ yy690:
}
YYDEBUG(691, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1138 "Zend/zend_language_scanner.l"
+#line 1121 "Zend/zend_language_scanner.l"
{
RETURN_TOKEN(T_EXIT);
}
-#line 6477 "Zend/zend_language_scanner.c"
+#line 6461 "Zend/zend_language_scanner.c"
yy692:
YYDEBUG(692, *YYCURSOR);
yych = *++YYCURSOR;
@@ -6508,11 +6492,11 @@ yy697:
}
YYDEBUG(698, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1251 "Zend/zend_language_scanner.l"
+#line 1234 "Zend/zend_language_scanner.l"
{
RETURN_TOKEN(T_DEFAULT);
}
-#line 6516 "Zend/zend_language_scanner.c"
+#line 6500 "Zend/zend_language_scanner.c"
yy699:
YYDEBUG(699, *YYCURSOR);
yych = *++YYCURSOR;
@@ -6536,11 +6520,11 @@ yy702:
}
YYDEBUG(703, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1223 "Zend/zend_language_scanner.l"
+#line 1206 "Zend/zend_language_scanner.l"
{
RETURN_TOKEN(T_DECLARE);
}
-#line 6544 "Zend/zend_language_scanner.c"
+#line 6528 "Zend/zend_language_scanner.c"
yy704:
YYDEBUG(704, *YYCURSOR);
yych = *++YYCURSOR;
@@ -6620,11 +6604,11 @@ yy715:
}
YYDEBUG(716, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1287 "Zend/zend_language_scanner.l"
+#line 1270 "Zend/zend_language_scanner.l"
{
RETURN_TOKEN(T_EXTENDS);
}
-#line 6628 "Zend/zend_language_scanner.c"
+#line 6612 "Zend/zend_language_scanner.c"
yy717:
YYDEBUG(717, *YYCURSOR);
++YYCURSOR;
@@ -6633,11 +6617,11 @@ yy717:
}
YYDEBUG(718, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1134 "Zend/zend_language_scanner.l"
+#line 1117 "Zend/zend_language_scanner.l"
{
RETURN_TOKEN(T_EXIT);
}
-#line 6641 "Zend/zend_language_scanner.c"
+#line 6625 "Zend/zend_language_scanner.c"
yy719:
YYDEBUG(719, *YYCURSOR);
yych = *++YYCURSOR;
@@ -6651,11 +6635,11 @@ yy720:
}
YYDEBUG(721, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1377 "Zend/zend_language_scanner.l"
+#line 1360 "Zend/zend_language_scanner.l"
{
RETURN_TOKEN(T_EVAL);
}
-#line 6659 "Zend/zend_language_scanner.c"
+#line 6643 "Zend/zend_language_scanner.c"
yy722:
YYDEBUG(722, *YYCURSOR);
yych = *++YYCURSOR;
@@ -6725,11 +6709,11 @@ yy731:
}
YYDEBUG(732, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1199 "Zend/zend_language_scanner.l"
+#line 1182 "Zend/zend_language_scanner.l"
{
RETURN_TOKEN(T_ENDWHILE);
}
-#line 6733 "Zend/zend_language_scanner.c"
+#line 6717 "Zend/zend_language_scanner.c"
yy733:
YYDEBUG(733, *YYCURSOR);
yych = *++YYCURSOR;
@@ -6758,11 +6742,11 @@ yy737:
}
YYDEBUG(738, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1243 "Zend/zend_language_scanner.l"
+#line 1226 "Zend/zend_language_scanner.l"
{
RETURN_TOKEN(T_ENDSWITCH);
}
-#line 6766 "Zend/zend_language_scanner.c"
+#line 6750 "Zend/zend_language_scanner.c"
yy739:
YYDEBUG(739, *YYCURSOR);
++YYCURSOR;
@@ -6771,11 +6755,11 @@ yy739:
}
YYDEBUG(740, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1187 "Zend/zend_language_scanner.l"
+#line 1170 "Zend/zend_language_scanner.l"
{
RETURN_TOKEN(T_ENDIF);
}
-#line 6779 "Zend/zend_language_scanner.c"
+#line 6763 "Zend/zend_language_scanner.c"
yy741:
YYDEBUG(741, *YYCURSOR);
yych = *++YYCURSOR;
@@ -6798,17 +6782,17 @@ yy742:
} else {
if (yych <= 'e') goto yy744;
if (yych <= 'z') goto yy149;
- if (yych >= 0x7F) goto yy149;
+ if (yych >= 0x80) goto yy149;
}
}
yy743:
YYDEBUG(743, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1211 "Zend/zend_language_scanner.l"
+#line 1194 "Zend/zend_language_scanner.l"
{
RETURN_TOKEN(T_ENDFOR);
}
-#line 6812 "Zend/zend_language_scanner.c"
+#line 6796 "Zend/zend_language_scanner.c"
yy744:
YYDEBUG(744, *YYCURSOR);
yych = *++YYCURSOR;
@@ -6832,11 +6816,11 @@ yy747:
}
YYDEBUG(748, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1219 "Zend/zend_language_scanner.l"
+#line 1202 "Zend/zend_language_scanner.l"
{
RETURN_TOKEN(T_ENDFOREACH);
}
-#line 6840 "Zend/zend_language_scanner.c"
+#line 6824 "Zend/zend_language_scanner.c"
yy749:
YYDEBUG(749, *YYCURSOR);
yych = *++YYCURSOR;
@@ -6870,11 +6854,11 @@ yy754:
}
YYDEBUG(755, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1227 "Zend/zend_language_scanner.l"
+#line 1210 "Zend/zend_language_scanner.l"
{
RETURN_TOKEN(T_ENDDECLARE);
}
-#line 6878 "Zend/zend_language_scanner.c"
+#line 6862 "Zend/zend_language_scanner.c"
yy756:
YYDEBUG(756, *YYCURSOR);
yych = *++YYCURSOR;
@@ -6893,11 +6877,11 @@ yy758:
}
YYDEBUG(759, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1417 "Zend/zend_language_scanner.l"
+#line 1400 "Zend/zend_language_scanner.l"
{
RETURN_TOKEN(T_EMPTY);
}
-#line 6901 "Zend/zend_language_scanner.c"
+#line 6885 "Zend/zend_language_scanner.c"
yy760:
YYDEBUG(760, *YYCURSOR);
yych = *++YYCURSOR;
@@ -6920,17 +6904,17 @@ yy761:
} else {
if (yych <= 'i') goto yy763;
if (yych <= 'z') goto yy149;
- if (yych >= 0x7F) goto yy149;
+ if (yych >= 0x80) goto yy149;
}
}
yy762:
YYDEBUG(762, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1191 "Zend/zend_language_scanner.l"
+#line 1174 "Zend/zend_language_scanner.l"
{
RETURN_TOKEN(T_ELSE);
}
-#line 6934 "Zend/zend_language_scanner.c"
+#line 6918 "Zend/zend_language_scanner.c"
yy763:
YYDEBUG(763, *YYCURSOR);
yych = *++YYCURSOR;
@@ -6944,11 +6928,11 @@ yy764:
}
YYDEBUG(765, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1183 "Zend/zend_language_scanner.l"
+#line 1166 "Zend/zend_language_scanner.l"
{
RETURN_TOKEN(T_ELSEIF);
}
-#line 6952 "Zend/zend_language_scanner.c"
+#line 6936 "Zend/zend_language_scanner.c"
yy766:
YYDEBUG(766, *YYCURSOR);
yych = *++YYCURSOR;
@@ -6962,11 +6946,11 @@ yy767:
}
YYDEBUG(768, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1267 "Zend/zend_language_scanner.l"
+#line 1250 "Zend/zend_language_scanner.l"
{
RETURN_TOKEN(T_ECHO);
}
-#line 6970 "Zend/zend_language_scanner.c"
+#line 6954 "Zend/zend_language_scanner.c"
}
/* *********************************** */
yyc_ST_LOOKING_FOR_PROPERTY:
@@ -6987,7 +6971,7 @@ yyc_ST_LOOKING_FOR_PROPERTY:
0, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64,
- 64, 64, 64, 0, 0, 0, 0, 64,
+ 64, 64, 64, 0, 0, 0, 0, 0,
64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64,
@@ -7027,7 +7011,7 @@ yyc_ST_LOOKING_FOR_PROPERTY:
} else {
if (yych <= '`') goto yy777;
if (yych <= 'z') goto yy775;
- if (yych <= '~') goto yy777;
+ if (yych <= 0x7F) goto yy777;
goto yy775;
}
}
@@ -7039,12 +7023,12 @@ yy771:
yy772:
YYDEBUG(772, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1300 "Zend/zend_language_scanner.l"
+#line 1283 "Zend/zend_language_scanner.l"
{
HANDLE_NEWLINES(yytext, yyleng);
RETURN_TOKEN(T_WHITESPACE);
}
-#line 7048 "Zend/zend_language_scanner.c"
+#line 7032 "Zend/zend_language_scanner.c"
yy773:
YYDEBUG(773, *YYCURSOR);
++YYCURSOR;
@@ -7052,13 +7036,13 @@ yy773:
yy774:
YYDEBUG(774, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1315 "Zend/zend_language_scanner.l"
+#line 1298 "Zend/zend_language_scanner.l"
{
yyless(0);
yy_pop_state();
goto restart;
}
-#line 7062 "Zend/zend_language_scanner.c"
+#line 7046 "Zend/zend_language_scanner.c"
yy775:
YYDEBUG(775, *YYCURSOR);
++YYCURSOR;
@@ -7067,13 +7051,13 @@ yy775:
yy776:
YYDEBUG(776, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1309 "Zend/zend_language_scanner.l"
+#line 1292 "Zend/zend_language_scanner.l"
{
yy_pop_state();
zend_copy_value(zendlval, yytext, yyleng);
RETURN_TOKEN(T_STRING);
}
-#line 7077 "Zend/zend_language_scanner.c"
+#line 7061 "Zend/zend_language_scanner.c"
yy777:
YYDEBUG(777, *YYCURSOR);
yych = *++YYCURSOR;
@@ -7094,11 +7078,11 @@ yy780:
++YYCURSOR;
YYDEBUG(781, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1305 "Zend/zend_language_scanner.l"
+#line 1288 "Zend/zend_language_scanner.l"
{
RETURN_TOKEN(T_OBJECT_OPERATOR);
}
-#line 7102 "Zend/zend_language_scanner.c"
+#line 7086 "Zend/zend_language_scanner.c"
yy782:
YYDEBUG(782, *YYCURSOR);
++YYCURSOR;
@@ -7130,7 +7114,7 @@ yyc_ST_LOOKING_FOR_VARNAME:
0, 128, 128, 128, 128, 128, 128, 128,
128, 128, 128, 128, 128, 128, 128, 128,
128, 128, 128, 128, 128, 128, 128, 128,
- 128, 128, 128, 0, 0, 0, 0, 128,
+ 128, 128, 128, 0, 0, 0, 0, 0,
128, 128, 128, 128, 128, 128, 128, 128,
128, 128, 128, 128, 128, 128, 128, 128,
128, 128, 128, 128, 128, 128, 128, 128,
@@ -7158,7 +7142,7 @@ yyc_ST_LOOKING_FOR_VARNAME:
} else {
if (yych <= '`') goto yy788;
if (yych <= 'z') goto yy786;
- if (yych <= '~') goto yy788;
+ if (yych <= 0x7F) goto yy788;
}
yy786:
YYDEBUG(786, *YYCURSOR);
@@ -7177,20 +7161,21 @@ yy786:
if (yych <= '`') goto yy787;
if (yych <= 'z') goto yy790;
} else {
- if (yych != '~') goto yy790;
+ if (yych <= '}') goto yy790;
+ if (yych >= 0x80) goto yy790;
}
}
yy787:
YYDEBUG(787, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1620 "Zend/zend_language_scanner.l"
+#line 1603 "Zend/zend_language_scanner.l"
{
yyless(0);
yy_pop_state();
yy_push_state(ST_IN_SCRIPTING);
goto restart;
}
-#line 7194 "Zend/zend_language_scanner.c"
+#line 7179 "Zend/zend_language_scanner.c"
yy788:
YYDEBUG(788, *YYCURSOR);
yych = *++YYCURSOR;
@@ -7215,7 +7200,7 @@ yy792:
++YYCURSOR;
YYDEBUG(793, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1611 "Zend/zend_language_scanner.l"
+#line 1594 "Zend/zend_language_scanner.l"
{
yyless(yyleng - 1);
zend_copy_value(zendlval, yytext, yyleng);
@@ -7223,7 +7208,7 @@ yy792:
yy_push_state(ST_IN_SCRIPTING);
RETURN_TOKEN(T_STRING_VARNAME);
}
-#line 7227 "Zend/zend_language_scanner.c"
+#line 7212 "Zend/zend_language_scanner.c"
}
/* *********************************** */
yyc_ST_NOWDOC:
@@ -7234,7 +7219,7 @@ yyc_ST_NOWDOC:
++YYCURSOR;
YYDEBUG(797, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 2339 "Zend/zend_language_scanner.l"
+#line 2322 "Zend/zend_language_scanner.l"
{
int newline = 0;
@@ -7290,7 +7275,7 @@ nowdoc_scan_done:
HANDLE_NEWLINES(yytext, yyleng - newline);
RETURN_TOKEN(T_ENCAPSED_AND_WHITESPACE);
}
-#line 7294 "Zend/zend_language_scanner.c"
+#line 7279 "Zend/zend_language_scanner.c"
/* *********************************** */
yyc_ST_VAR_OFFSET:
{
@@ -7310,7 +7295,7 @@ yyc_ST_VAR_OFFSET:
0, 80, 80, 80, 80, 80, 80, 16,
16, 16, 16, 16, 16, 16, 16, 16,
16, 16, 16, 16, 16, 16, 16, 16,
- 16, 16, 16, 0, 0, 0, 0, 16,
+ 16, 16, 16, 0, 0, 0, 0, 0,
16, 16, 16, 16, 16, 16, 16, 16,
16, 16, 16, 16, 16, 16, 16, 16,
16, 16, 16, 16, 16, 16, 16, 16,
@@ -7331,7 +7316,7 @@ yyc_ST_VAR_OFFSET:
YYDEBUG(798, *YYCURSOR);
YYFILL(3);
yych = *YYCURSOR;
- if (yych <= '/') {
+ if (yych <= '0') {
if (yych <= ' ') {
if (yych <= '\f') {
if (yych <= 0x08) goto yy812;
@@ -7349,34 +7334,32 @@ yyc_ST_VAR_OFFSET:
goto yy803;
} else {
if (yych == '\'') goto yy808;
- goto yy807;
+ if (yych <= '/') goto yy807;
}
}
} else {
- if (yych <= '\\') {
- if (yych <= '@') {
- if (yych <= '0') goto yy800;
+ if (yych <= ']') {
+ if (yych <= 'Z') {
if (yych <= '9') goto yy802;
- goto yy807;
+ if (yych <= '@') goto yy807;
+ goto yy810;
} else {
- if (yych <= 'Z') goto yy810;
if (yych <= '[') goto yy807;
- goto yy808;
+ if (yych <= '\\') goto yy808;
+ goto yy805;
}
} else {
- if (yych <= '_') {
- if (yych <= ']') goto yy805;
- if (yych <= '^') goto yy807;
- goto yy810;
+ if (yych <= '`') {
+ if (yych == '_') goto yy810;
+ goto yy807;
} else {
- if (yych <= '`') goto yy807;
if (yych <= 'z') goto yy810;
if (yych <= '~') goto yy807;
+ if (yych <= 0x7F) goto yy812;
goto yy810;
}
}
}
-yy800:
YYDEBUG(800, *YYCURSOR);
yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
@@ -7397,7 +7380,7 @@ yy800:
yy801:
YYDEBUG(801, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1728 "Zend/zend_language_scanner.l"
+#line 1711 "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;
@@ -7413,7 +7396,7 @@ string:
}
RETURN_TOKEN(T_NUM_STRING);
}
-#line 7417 "Zend/zend_language_scanner.c"
+#line 7400 "Zend/zend_language_scanner.c"
yy802:
YYDEBUG(802, *YYCURSOR);
yych = *++YYCURSOR;
@@ -7428,28 +7411,28 @@ yy803:
} else {
if (yych <= '`') goto yy804;
if (yych <= 'z') goto yy816;
- if (yych >= 0x7F) goto yy816;
+ if (yych >= 0x80) goto yy816;
}
yy804:
YYDEBUG(804, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1888 "Zend/zend_language_scanner.l"
+#line 1871 "Zend/zend_language_scanner.l"
{
/* Only '[' can be valid, but returning other tokens will allow a more explicit parse error */
RETURN_TOKEN(yytext[0]);
}
-#line 7442 "Zend/zend_language_scanner.c"
+#line 7425 "Zend/zend_language_scanner.c"
yy805:
YYDEBUG(805, *YYCURSOR);
++YYCURSOR;
YYDEBUG(806, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1883 "Zend/zend_language_scanner.l"
+#line 1866 "Zend/zend_language_scanner.l"
{
yy_pop_state();
RETURN_TOKEN(']');
}
-#line 7453 "Zend/zend_language_scanner.c"
+#line 7436 "Zend/zend_language_scanner.c"
yy807:
YYDEBUG(807, *YYCURSOR);
yych = *++YYCURSOR;
@@ -7459,7 +7442,7 @@ yy808:
++YYCURSOR;
YYDEBUG(809, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1893 "Zend/zend_language_scanner.l"
+#line 1876 "Zend/zend_language_scanner.l"
{
/* Invalid rule to return a more explicit parse error with proper line number */
yyless(0);
@@ -7467,7 +7450,7 @@ yy808:
ZVAL_NULL(zendlval);
RETURN_TOKEN(T_ENCAPSED_AND_WHITESPACE);
}
-#line 7471 "Zend/zend_language_scanner.c"
+#line 7454 "Zend/zend_language_scanner.c"
yy810:
YYDEBUG(810, *YYCURSOR);
++YYCURSOR;
@@ -7476,18 +7459,18 @@ yy810:
yy811:
YYDEBUG(811, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1901 "Zend/zend_language_scanner.l"
+#line 1884 "Zend/zend_language_scanner.l"
{
zend_copy_value(zendlval, yytext, yyleng);
RETURN_TOKEN(T_STRING);
}
-#line 7485 "Zend/zend_language_scanner.c"
+#line 7468 "Zend/zend_language_scanner.c"
yy812:
YYDEBUG(812, *YYCURSOR);
++YYCURSOR;
YYDEBUG(813, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 2396 "Zend/zend_language_scanner.l"
+#line 2379 "Zend/zend_language_scanner.l"
{
if (YYCURSOR > YYLIMIT) {
RETURN_TOKEN(END);
@@ -7496,7 +7479,7 @@ yy812:
zend_error(E_COMPILE_WARNING,"Unexpected character in input: '%c' (ASCII=%d) state=%d", yytext[0], yytext[0], YYSTATE);
goto restart;
}
-#line 7500 "Zend/zend_language_scanner.c"
+#line 7483 "Zend/zend_language_scanner.c"
yy814:
YYDEBUG(814, *YYCURSOR);
++YYCURSOR;
@@ -7526,18 +7509,18 @@ yy816:
if (yych <= '_') goto yy816;
} else {
if (yych <= 'z') goto yy816;
- if (yych >= 0x7F) goto yy816;
+ if (yych >= 0x80) goto yy816;
}
}
yy818:
YYDEBUG(818, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1878 "Zend/zend_language_scanner.l"
+#line 1861 "Zend/zend_language_scanner.l"
{
zend_copy_value(zendlval, (yytext+1), (yyleng-1));
RETURN_TOKEN(T_VARIABLE);
}
-#line 7541 "Zend/zend_language_scanner.c"
+#line 7524 "Zend/zend_language_scanner.c"
yy819:
YYDEBUG(819, *YYCURSOR);
++YYCURSOR;
@@ -7577,12 +7560,12 @@ yy824:
yy826:
YYDEBUG(826, *YYCURSOR);
yyleng = YYCURSOR - SCNG(yy_text);
-#line 1744 "Zend/zend_language_scanner.l"
+#line 1727 "Zend/zend_language_scanner.l"
{ /* Offset must be treated as a string */
ZVAL_STRINGL(zendlval, yytext, yyleng);
RETURN_TOKEN(T_NUM_STRING);
}
-#line 7586 "Zend/zend_language_scanner.c"
+#line 7569 "Zend/zend_language_scanner.c"
yy827:
YYDEBUG(827, *YYCURSOR);
++YYCURSOR;
@@ -7605,6 +7588,6 @@ yy829:
goto yy826;
}
}
-#line 2405 "Zend/zend_language_scanner.l"
+#line 2388 "Zend/zend_language_scanner.l"
}
diff --git a/Zend/zend_language_scanner.l b/Zend/zend_language_scanner.l
index 8c5a4e47f5..122c6d4eda 100644
--- a/Zend/zend_language_scanner.l
+++ b/Zend/zend_language_scanner.l
@@ -116,7 +116,7 @@ do { \
#define SET_DOUBLE_QUOTES_SCANNED_LENGTH(len) SCNG(scanned_string_len) = (len)
#define GET_DOUBLE_QUOTES_SCANNED_LENGTH() SCNG(scanned_string_len)
-#define IS_LABEL_START(c) (((c) >= 'a' && (c) <= 'z') || ((c) >= 'A' && (c) <= 'Z') || (c) == '_' || (c) >= 0x7F)
+#define IS_LABEL_START(c) (((c) >= 'a' && (c) <= 'z') || ((c) >= 'A' && (c) <= 'Z') || (c) == '_' || (c) >= 0x80)
#define ZEND_IS_OCT(c) ((c)>='0' && (c)<='7')
#define ZEND_IS_HEX(c) (((c)>='0' && (c)<='9') || ((c)>='a' && (c)<='f') || ((c)>='A' && (c)<='F'))
@@ -178,6 +178,7 @@ void startup_scanner(void)
{
CG(parse_error) = 0;
CG(doc_comment) = NULL;
+ CG(extra_fn_flags) = 0;
zend_stack_init(&SCNG(state_stack), sizeof(int));
zend_ptr_stack_init(&SCNG(heredoc_label_stack));
}
@@ -568,6 +569,48 @@ ZEND_API int open_file_for_scanning(zend_file_handle *file_handle)
}
END_EXTERN_C()
+static zend_op_array *zend_compile(int type)
+{
+ zend_op_array *op_array = NULL;
+ zend_bool original_in_compilation = CG(in_compilation);
+
+ CG(in_compilation) = 1;
+ CG(ast) = NULL;
+ CG(ast_arena) = zend_arena_create(1024 * 32);
+
+ if (!zendparse()) {
+ zend_file_context original_file_context;
+ zend_oparray_context original_oparray_context;
+ zend_op_array *original_active_op_array = CG(active_op_array);
+
+ op_array = emalloc(sizeof(zend_op_array));
+ init_op_array(op_array, type, INITIAL_OP_ARRAY_SIZE);
+ CG(active_op_array) = op_array;
+
+ if (zend_ast_process) {
+ zend_ast_process(CG(ast));
+ }
+
+ zend_file_context_begin(&original_file_context);
+ zend_oparray_context_begin(&original_oparray_context);
+ zend_compile_top_stmt(CG(ast));
+ zend_emit_final_return(type == ZEND_USER_FUNCTION);
+ op_array->line_start = 1;
+ op_array->line_end = CG(zend_lineno);
+ pass_two(op_array);
+ zend_oparray_context_end(&original_oparray_context);
+ zend_file_context_end(&original_file_context);
+
+ CG(active_op_array) = original_active_op_array;
+ }
+
+ zend_ast_destroy(CG(ast));
+ zend_arena_destroy(CG(ast_arena));
+
+ CG(in_compilation) = original_in_compilation;
+
+ return op_array;
+}
ZEND_API zend_op_array *compile_file(zend_file_handle *file_handle, int type)
{
@@ -583,41 +626,7 @@ ZEND_API zend_op_array *compile_file(zend_file_handle *file_handle, int type)
zend_message_dispatcher(ZMSG_FAILED_INCLUDE_FOPEN, file_handle->filename);
}
} else {
- zend_bool original_in_compilation = CG(in_compilation);
- CG(in_compilation) = 1;
-
- CG(ast) = NULL;
- CG(ast_arena) = zend_arena_create(1024 * 32);
- if (!zendparse()) {
- zval retval_zv;
- zend_file_context original_file_context;
- zend_oparray_context original_oparray_context;
- zend_op_array *original_active_op_array = CG(active_op_array);
- op_array = emalloc(sizeof(zend_op_array));
- init_op_array(op_array, ZEND_USER_FUNCTION, INITIAL_OP_ARRAY_SIZE);
- CG(active_op_array) = op_array;
- ZVAL_LONG(&retval_zv, 1);
-
- if (zend_ast_process) {
- zend_ast_process(CG(ast));
- }
-
- zend_file_context_begin(&original_file_context);
- zend_oparray_context_begin(&original_oparray_context);
- zend_compile_top_stmt(CG(ast));
- zend_emit_final_return(&retval_zv);
- op_array->line_start = 1;
- op_array->line_end = CG(zend_lineno);
- pass_two(op_array);
- zend_oparray_context_end(&original_oparray_context);
- zend_file_context_end(&original_file_context);
-
- CG(active_op_array) = original_active_op_array;
- }
-
- zend_ast_destroy(CG(ast));
- zend_arena_destroy(CG(ast_arena));
- CG(in_compilation) = original_in_compilation;
+ op_array = zend_compile(ZEND_USER_FUNCTION);
}
zend_restore_lexical_state(&original_lex_state);
@@ -732,13 +741,11 @@ ZEND_API size_t zend_get_scanned_file_offset(void)
return offset;
}
-
zend_op_array *compile_string(zval *source_string, char *filename)
{
zend_lex_state original_lex_state;
zend_op_array *op_array = NULL;
zval tmp;
- zend_bool original_in_compilation = CG(in_compilation);
if (Z_STRLEN_P(source_string)==0) {
return NULL;
@@ -748,45 +755,15 @@ zend_op_array *compile_string(zval *source_string, char *filename)
convert_to_string(&tmp);
source_string = &tmp;
- CG(in_compilation) = 1;
zend_save_lexical_state(&original_lex_state);
if (zend_prepare_string_for_scanning(source_string, filename) == SUCCESS) {
- CG(ast) = NULL;
- CG(ast_arena) = zend_arena_create(1024 * 32);
BEGIN(ST_IN_SCRIPTING);
-
- if (!zendparse()) {
- zend_file_context original_file_context;
- zend_oparray_context original_oparray_context;
- zend_op_array *original_active_op_array = CG(active_op_array);
- op_array = emalloc(sizeof(zend_op_array));
- init_op_array(op_array, ZEND_EVAL_CODE, INITIAL_OP_ARRAY_SIZE);
- CG(active_op_array) = op_array;
-
- if (zend_ast_process) {
- zend_ast_process(CG(ast));
- }
-
- zend_file_context_begin(&original_file_context);
- zend_oparray_context_begin(&original_oparray_context);
- zend_compile_top_stmt(CG(ast));
- zend_emit_final_return(NULL);
- op_array->line_start = 1;
- op_array->line_end = CG(zend_lineno);
- pass_two(op_array);
- zend_oparray_context_end(&original_oparray_context);
- zend_file_context_end(&original_file_context);
-
- CG(active_op_array) = original_active_op_array;
- }
-
- zend_ast_destroy(CG(ast));
- zend_arena_destroy(CG(ast_arena));
+ op_array = zend_compile(ZEND_EVAL_CODE);
}
zend_restore_lexical_state(&original_lex_state);
zval_dtor(&tmp);
- CG(in_compilation) = original_in_compilation;
+
return op_array;
}
@@ -1067,6 +1044,12 @@ static int zend_scan_escape_string(zval *zendlval, char *str, int len, char quot
Z_STRLEN_P(zendlval)--;
}
}
+ if (octal_buf[2] &&
+ (octal_buf[0] > '3')) {
+ /* 3 octit values must not overflow 0xFF (\377) */
+ zend_error(E_COMPILE_WARNING, "Octal escape sequence overflow \\%s is greater than \\377", octal_buf);
+ }
+
*t++ = (char) ZEND_STRTOL(octal_buf, NULL, 8);
} else {
*t++ = '\\';
@@ -1121,7 +1104,7 @@ DNUM ([0-9]*"."[0-9]+)|([0-9]+"."[0-9]*)
EXPONENT_DNUM (({LNUM}|{DNUM})[eE][+-]?{LNUM})
HNUM "0x"[0-9a-fA-F]+
BNUM "0b"[01]+
-LABEL [a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*
+LABEL [a-zA-Z_\x80-\xff][a-zA-Z0-9_\x80-\xff]*
WHITESPACE [ \n\r\t]+
TABS_AND_SPACES [ \t]*
TOKENS [;:,.\[\]()|^&+-/*=%!~$<>?@]
@@ -1859,7 +1842,7 @@ inline_char_handler:
/* Make sure a label character follows "->", otherwise there is no property
* and "->" will be taken literally
*/
-<ST_DOUBLE_QUOTES,ST_HEREDOC,ST_BACKQUOTE>"$"{LABEL}"->"[a-zA-Z_\x7f-\xff] {
+<ST_DOUBLE_QUOTES,ST_HEREDOC,ST_BACKQUOTE>"$"{LABEL}"->"[a-zA-Z_\x80-\xff] {
yyless(yyleng - 3);
yy_push_state(ST_LOOKING_FOR_PROPERTY);
zend_copy_value(zendlval, (yytext+1), (yyleng-1));
diff --git a/Zend/zend_modules.h b/Zend/zend_modules.h
index 07e1937e03..b388b7cb1a 100644
--- a/Zend/zend_modules.h
+++ b/Zend/zend_modules.h
@@ -33,7 +33,7 @@
#define ZEND_MODULE_INFO_FUNC_ARGS zend_module_entry *zend_module
#define ZEND_MODULE_INFO_FUNC_ARGS_PASSTHRU zend_module
-#define ZEND_MODULE_API_NO 20151012
+#define ZEND_MODULE_API_NO 20160303
#ifdef ZTS
#define USING_ZTS 1
#else
diff --git a/Zend/zend_object_handlers.c b/Zend/zend_object_handlers.c
index ba3e84bf7d..86e71e30a2 100644
--- a/Zend/zend_object_handlers.c
+++ b/Zend/zend_object_handlers.c
@@ -191,6 +191,9 @@ ZEND_API HashTable *zend_std_get_debug_info(zval *object, int *is_temp) /* {{{ *
static void zend_std_call_getter(zval *object, zval *member, zval *retval) /* {{{ */
{
zend_class_entry *ce = Z_OBJCE_P(object);
+ zend_class_entry *orig_fake_scope = EG(fake_scope);
+
+ EG(fake_scope) = NULL;
/* __get handler is called with one argument:
property name
@@ -202,6 +205,8 @@ static void zend_std_call_getter(zval *object, zval *member, zval *retval) /* {{
zend_call_method_with_1_params(object, ce, &ce->__get, ZEND_GET_FUNC_NAME, retval, member);
zval_ptr_dtor(member);
+
+ EG(fake_scope) = orig_fake_scope;
}
/* }}} */
@@ -210,6 +215,9 @@ static int zend_std_call_setter(zval *object, zval *member, zval *value) /* {{{
zval retval;
int result;
zend_class_entry *ce = Z_OBJCE_P(object);
+ zend_class_entry *orig_fake_scope = EG(fake_scope);
+
+ EG(fake_scope) = NULL;
if (Z_REFCOUNTED_P(member)) Z_ADDREF_P(member);
if (Z_REFCOUNTED_P(value)) Z_ADDREF_P(value);
@@ -228,8 +236,10 @@ static int zend_std_call_setter(zval *object, zval *member, zval *value) /* {{{
if (Z_TYPE(retval) != IS_UNDEF) {
result = i_zend_is_true(&retval) ? SUCCESS : FAILURE;
zval_ptr_dtor(&retval);
+ EG(fake_scope) = orig_fake_scope;
return result;
} else {
+ EG(fake_scope) = orig_fake_scope;
return FAILURE;
}
}
@@ -238,6 +248,9 @@ static int zend_std_call_setter(zval *object, zval *member, zval *value) /* {{{
static void zend_std_call_unsetter(zval *object, zval *member) /* {{{ */
{
zend_class_entry *ce = Z_OBJCE_P(object);
+ zend_class_entry *orig_fake_scope = EG(fake_scope);
+
+ EG(fake_scope) = NULL;
/* __unset handler is called with one argument:
property name
@@ -248,12 +261,17 @@ static void zend_std_call_unsetter(zval *object, zval *member) /* {{{ */
zend_call_method_with_1_params(object, ce, &ce->__unset, ZEND_UNSET_FUNC_NAME, NULL, member);
zval_ptr_dtor(member);
+
+ EG(fake_scope) = orig_fake_scope;
}
/* }}} */
static void zend_std_call_issetter(zval *object, zval *member, zval *retval) /* {{{ */
{
zend_class_entry *ce = Z_OBJCE_P(object);
+ zend_class_entry *orig_fake_scope = EG(fake_scope);
+
+ EG(fake_scope) = NULL;
/* __isset handler is called with one argument:
property name
@@ -266,17 +284,31 @@ static void zend_std_call_issetter(zval *object, zval *member, zval *retval) /*
zend_call_method_with_1_params(object, ce, &ce->__isset, ZEND_ISSET_FUNC_NAME, retval, member);
zval_ptr_dtor(member);
+
+ EG(fake_scope) = orig_fake_scope;
}
/* }}} */
static zend_always_inline int zend_verify_property_access(zend_property_info *property_info, zend_class_entry *ce) /* {{{ */
{
+ zend_class_entry *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));
+ if (EG(fake_scope)) {
+ scope = EG(fake_scope);
+ } else {
+ scope = zend_get_executed_scope();
+ }
+ return (ce == scope || property_info->ce == scope);
} else if (property_info->flags & ZEND_ACC_PROTECTED) {
- return zend_check_protected(property_info->ce, EG(scope));
+ if (EG(fake_scope)) {
+ scope = EG(fake_scope);
+ } else {
+ scope = zend_get_executed_scope();
+ }
+ return zend_check_protected(property_info->ce, scope);
}
return 0;
}
@@ -301,18 +333,15 @@ static zend_always_inline uint32_t zend_get_property_offset(zend_class_entry *ce
zval *zv;
zend_property_info *property_info = NULL;
uint32_t flags;
+ zend_class_entry *scope;
if (cache_slot && EXPECTED(ce == CACHED_PTR_EX(cache_slot))) {
return (uint32_t)(intptr_t)CACHED_PTR_EX(cache_slot + 1);
}
- if (UNEXPECTED(ZSTR_VAL(member)[0] == '\0')) {
+ if (UNEXPECTED(ZSTR_VAL(member)[0] == '\0' && ZSTR_LEN(member) != 0)) {
if (!silent) {
- if (ZSTR_LEN(member) == 0) {
- zend_throw_error(NULL, "Cannot access empty property");
- } else {
- zend_throw_error(NULL, "Cannot access property started with '\\0'");
- }
+ zend_throw_error(NULL, "Cannot access property started with '\\0'");
}
return ZEND_WRONG_PROPERTY_OFFSET;
}
@@ -347,10 +376,16 @@ static zend_always_inline uint32_t zend_get_property_offset(zend_class_entry *ce
}
}
- if (EG(scope) != ce
- && EG(scope)
- && is_derived_class(ce, EG(scope))
- && (zv = zend_hash_find(&EG(scope)->properties_info, member)) != NULL
+ if (EG(fake_scope)) {
+ scope = EG(fake_scope);
+ } else {
+ scope = zend_get_executed_scope();
+ }
+
+ if (scope != ce
+ && scope
+ && is_derived_class(ce, scope)
+ && (zv = zend_hash_find(&scope->properties_info, member)) != NULL
&& ((zend_property_info*)Z_PTR_P(zv))->flags & ZEND_ACC_PRIVATE) {
property_info = (zend_property_info*)Z_PTR_P(zv);
if (UNEXPECTED((property_info->flags & ZEND_ACC_STATIC) != 0)) {
@@ -383,14 +418,11 @@ ZEND_API zend_property_info *zend_get_property_info(zend_class_entry *ce, zend_s
zval *zv;
zend_property_info *property_info = NULL;
uint32_t flags;
+ zend_class_entry *scope;
- if (UNEXPECTED(ZSTR_VAL(member)[0] == '\0')) {
+ if (UNEXPECTED(ZSTR_VAL(member)[0] == '\0' && ZSTR_LEN(member) != 0)) {
if (!silent) {
- if (ZSTR_LEN(member) == 0) {
- zend_throw_error(NULL, "Cannot access empty property");
- } else {
- zend_throw_error(NULL, "Cannot access property started with '\\0'");
- }
+ zend_throw_error(NULL, "Cannot access property started with '\\0'");
}
return ZEND_WRONG_PROPERTY_INFO;
}
@@ -424,10 +456,16 @@ ZEND_API zend_property_info *zend_get_property_info(zend_class_entry *ce, zend_s
}
}
- if (EG(scope) != ce
- && EG(scope)
- && is_derived_class(ce, EG(scope))
- && (zv = zend_hash_find(&EG(scope)->properties_info, member)) != NULL
+ if (EG(fake_scope)) {
+ scope = EG(fake_scope);
+ } else {
+ scope = zend_get_executed_scope();
+ }
+
+ if (scope != ce
+ && scope
+ && is_derived_class(ce, scope)
+ && (zv = zend_hash_find(&scope->properties_info, member)) != NULL
&& ((zend_property_info*)Z_PTR_P(zv))->flags & ZEND_ACC_PRIVATE) {
property_info = (zend_property_info*)Z_PTR_P(zv);
} else if (UNEXPECTED(property_info == NULL)) {
@@ -486,31 +524,60 @@ ZEND_API int zend_check_property_access(zend_object *zobj, zend_string *prop_inf
/* }}} */
static void zend_property_guard_dtor(zval *el) /* {{{ */ {
- efree_size(Z_PTR_P(el), sizeof(zend_ulong));
+ uint32_t *ptr = (uint32_t*)Z_PTR_P(el);
+ if (EXPECTED(!(((zend_uintptr_t)ptr) & 1))) {
+ efree_size(ptr, sizeof(uint32_t));
+ }
}
/* }}} */
-static zend_long *zend_get_property_guard(zend_object *zobj, zend_string *member) /* {{{ */
+ZEND_API uint32_t *zend_get_property_guard(zend_object *zobj, zend_string *member) /* {{{ */
{
HashTable *guards;
- zend_long stub, *guard;
+ zval *zv;
+ uint32_t *ptr;
ZEND_ASSERT(GC_FLAGS(zobj) & IS_OBJ_USE_GUARDS);
- if (GC_FLAGS(zobj) & IS_OBJ_HAS_GUARDS) {
- guards = Z_PTR(zobj->properties_table[zobj->ce->default_properties_count]);
+ zv = zobj->properties_table + zobj->ce->default_properties_count;
+ if (EXPECTED(Z_TYPE_P(zv) == IS_STRING)) {
+ zend_string *str = Z_STR_P(zv);
+ if (EXPECTED(str == member) ||
+ /* hash values are always pred-calculated here */
+ (EXPECTED(ZSTR_H(str) == ZSTR_H(member)) &&
+ EXPECTED(ZSTR_LEN(str) == ZSTR_LEN(member)) &&
+ EXPECTED(memcmp(ZSTR_VAL(str), ZSTR_VAL(member), ZSTR_LEN(member)) == 0))) {
+ return &zv->u2.property_guard;
+ } else if (EXPECTED(zv->u2.property_guard == 0)) {
+ zend_string_release(Z_STR_P(zv));
+ ZVAL_STR_COPY(zv, member);
+ return &zv->u2.property_guard;
+ } else {
+ ALLOC_HASHTABLE(guards);
+ zend_hash_init(guards, 8, NULL, zend_property_guard_dtor, 0);
+ /* mark pointer as "special" using low bit */
+ zend_hash_add_new_ptr(guards, member,
+ (void*)(((zend_uintptr_t)&zv->u2.property_guard) | 1));
+ zend_string_release(Z_STR_P(zv));
+ ZVAL_ARR(zv, guards);
+ }
+ } else if (EXPECTED(Z_TYPE_P(zv) == IS_ARRAY)) {
+ guards = Z_ARRVAL_P(zv);
ZEND_ASSERT(guards != NULL);
- if ((guard = (zend_long *)zend_hash_find_ptr(guards, member)) != NULL) {
- return guard;
+ zv = zend_hash_find(guards, member);
+ if (zv != NULL) {
+ return (uint32_t*)(((zend_uintptr_t)Z_PTR_P(zv)) & ~1);
}
} else {
- ALLOC_HASHTABLE(guards);
- zend_hash_init(guards, 8, NULL, zend_property_guard_dtor, 0);
- Z_PTR(zobj->properties_table[zobj->ce->default_properties_count]) = guards;
+ ZEND_ASSERT(Z_TYPE_P(zv) == IS_UNDEF);
GC_FLAGS(zobj) |= IS_OBJ_HAS_GUARDS;
- }
-
- stub = 0;
- return (zend_long *)zend_hash_add_mem(guards, member, &stub, sizeof(zend_ulong));
+ ZVAL_STR_COPY(zv, member);
+ zv->u2.property_guard = 0;
+ return &zv->u2.property_guard;
+ }
+ /* we have to allocate uint32_t separately because ht->arData may be reallocated */
+ ptr = (uint32_t*)emalloc(sizeof(uint32_t));
+ *ptr = 0;
+ return (uint32_t*)zend_hash_add_new_ptr(guards, member, ptr);
}
/* }}} */
@@ -555,7 +622,7 @@ zval *zend_std_read_property(zval *object, zval *member, int type, void **cache_
/* magic isset */
if ((type == BP_VAR_IS) && zobj->ce->__isset) {
zval tmp_object, tmp_result;
- zend_long *guard = zend_get_property_guard(zobj, Z_STR_P(member));
+ uint32_t *guard = zend_get_property_guard(zobj, Z_STR_P(member));
if (!((*guard) & IN_ISSET)) {
ZVAL_COPY(&tmp_object, object);
@@ -579,7 +646,7 @@ zval *zend_std_read_property(zval *object, zval *member, int type, void **cache_
/* magic get */
if (zobj->ce->__get) {
- zend_long *guard = zend_get_property_guard(zobj, Z_STR_P(member));
+ uint32_t *guard = zend_get_property_guard(zobj, Z_STR_P(member));
if (!((*guard) & IN_GET)) {
zval tmp_object;
@@ -604,16 +671,10 @@ zval *zend_std_read_property(zval *object, zval *member, int type, void **cache_
zval_ptr_dtor(&tmp_object);
goto exit;
} else {
- if (Z_STRVAL_P(member)[0] == '\0') {
- if (Z_STRLEN_P(member) == 0) {
- zend_throw_error(NULL, "Cannot access empty property");
- retval = &EG(uninitialized_zval);
- goto exit;
- } else {
- zend_throw_error(NULL, "Cannot access property started with '\\0'");
- retval = &EG(uninitialized_zval);
- goto exit;
- }
+ if (Z_STRVAL_P(member)[0] == '\0' && Z_STRLEN_P(member) != 0) {
+ zend_throw_error(NULL, "Cannot access property started with '\\0'");
+ retval = &EG(uninitialized_zval);
+ goto exit;
}
}
}
@@ -674,7 +735,7 @@ found:
/* magic set */
if (zobj->ce->__set) {
- zend_long *guard = zend_get_property_guard(zobj, Z_STR_P(member));
+ uint32_t *guard = zend_get_property_guard(zobj, Z_STR_P(member));
if (!((*guard) & IN_SET)) {
zval tmp_object;
@@ -689,14 +750,9 @@ found:
} else if (EXPECTED(property_offset != ZEND_WRONG_PROPERTY_OFFSET)) {
goto write_std_property;
} else {
- if (Z_STRVAL_P(member)[0] == '\0') {
- if (Z_STRLEN_P(member) == 0) {
- zend_throw_error(NULL, "Cannot access empty property");
- goto exit;
- } else {
- zend_throw_error(NULL, "Cannot access property started with '\\0'");
- goto exit;
- }
+ if (Z_STRVAL_P(member)[0] == '\0' && Z_STRLEN_P(member) != 0) {
+ zend_throw_error(NULL, "Cannot access property started with '\\0'");
+ goto exit;
}
}
} else if (EXPECTED(property_offset != ZEND_WRONG_PROPERTY_OFFSET)) {
@@ -944,7 +1000,7 @@ static void zend_std_unset_property(zval *object, zval *member, void **cache_slo
/* magic unset */
if (zobj->ce->__unset) {
- zend_long *guard = zend_get_property_guard(zobj, Z_STR_P(member));
+ uint32_t *guard = zend_get_property_guard(zobj, Z_STR_P(member));
if (!((*guard) & IN_UNSET)) {
zval tmp_object;
@@ -955,14 +1011,9 @@ static void zend_std_unset_property(zval *object, zval *member, void **cache_slo
(*guard) &= ~IN_UNSET;
zval_ptr_dtor(&tmp_object);
} else {
- if (Z_STRVAL_P(member)[0] == '\0') {
- if (Z_STRLEN_P(member) == 0) {
- zend_throw_error(NULL, "Cannot access empty property");
- goto exit;
- } else {
- zend_throw_error(NULL, "Cannot access property started with '\\0'");
- goto exit;
- }
+ if (Z_STRVAL_P(member)[0] == '\0' && Z_STRLEN_P(member) != 0) {
+ zend_throw_error(NULL, "Cannot access property started with '\\0'");
+ goto exit;
}
}
}
@@ -995,6 +1046,7 @@ static void zend_std_unset_dimension(zval *object, zval *offset) /* {{{ */
static inline zend_function *zend_check_private_int(zend_function *fbc, zend_class_entry *ce, zend_string *function_name) /* {{{ */
{
zval *func;
+ zend_class_entry *scope;
if (!ce) {
return 0;
@@ -1006,7 +1058,8 @@ static inline zend_function *zend_check_private_int(zend_function *fbc, zend_cla
* 2. One of our parent classes are the same as the scope, and it contains
* a private function with the same name that has the same scope.
*/
- if (fbc->common.scope == ce && EG(scope) == ce) {
+ scope = zend_get_executed_scope();
+ if (fbc->common.scope == ce && scope == ce) {
/* rule #1 checks out ok, allow the function call */
return fbc;
}
@@ -1015,11 +1068,11 @@ static inline zend_function *zend_check_private_int(zend_function *fbc, zend_cla
/* Check rule #2 */
ce = ce->parent;
while (ce) {
- if (ce == EG(scope)) {
+ if (ce == scope) {
if ((func = zend_hash_find(&ce->function_table, function_name))) {
fbc = Z_FUNC_P(func);
if (fbc->common.fn_flags & ZEND_ACC_PRIVATE
- && fbc->common.scope == EG(scope)) {
+ && fbc->common.scope == scope) {
return fbc;
}
}
@@ -1088,7 +1141,6 @@ ZEND_API zend_function *zend_get_call_trampoline_func(zend_class_entry *ce, zend
if (is_static) {
func->fn_flags |= ZEND_ACC_STATIC;
}
- func->this_var = -1;
func->opcodes = &EG(call_trampoline_op);
func->prototype = fbc;
@@ -1123,6 +1175,7 @@ static union _zend_function *zend_std_get_method(zend_object **obj_ptr, zend_str
zval *func;
zend_function *fbc;
zend_string *lc_method_name;
+ zend_class_entry *scope = NULL;
ALLOCA_FLAG(use_heap);
if (EXPECTED(key != NULL)) {
@@ -1161,7 +1214,8 @@ static union _zend_function *zend_std_get_method(zend_object **obj_ptr, zend_str
if (zobj->ce->__call) {
fbc = zend_get_user_call_function(zobj->ce, method_name);
} else {
- zend_throw_error(NULL, "Call to %s method %s::%s() from context '%s'", zend_visibility_string(fbc->common.fn_flags), ZEND_FN_SCOPE_NAME(fbc), ZSTR_VAL(method_name), EG(scope) ? ZSTR_VAL(EG(scope)->name) : "");
+ scope = zend_get_executed_scope();
+ zend_throw_error(NULL, "Call to %s method %s::%s() from context '%s'", zend_visibility_string(fbc->common.fn_flags), ZEND_FN_SCOPE_NAME(fbc), ZSTR_VAL(method_name), scope ? ZSTR_VAL(scope->name) : "");
fbc = NULL;
}
}
@@ -1169,26 +1223,29 @@ static union _zend_function *zend_std_get_method(zend_object **obj_ptr, zend_str
/* Ensure that we haven't overridden a private function and end up calling
* the overriding public function...
*/
- if (EG(scope) &&
- is_derived_class(fbc->common.scope, EG(scope)) &&
- fbc->op_array.fn_flags & ZEND_ACC_CHANGED) {
- if ((func = zend_hash_find(&EG(scope)->function_table, lc_method_name)) != NULL) {
- zend_function *priv_fbc = Z_FUNC_P(func);
- if (priv_fbc->common.fn_flags & ZEND_ACC_PRIVATE
- && priv_fbc->common.scope == EG(scope)) {
- fbc = priv_fbc;
+ if (fbc->op_array.fn_flags & (ZEND_ACC_CHANGED|ZEND_ACC_PROTECTED)) {
+ scope = zend_get_executed_scope();
+ }
+ if (fbc->op_array.fn_flags & ZEND_ACC_CHANGED) {
+ if (scope && is_derived_class(fbc->common.scope, scope)) {
+ if ((func = zend_hash_find(&scope->function_table, lc_method_name)) != NULL) {
+ zend_function *priv_fbc = Z_FUNC_P(func);
+ if (priv_fbc->common.fn_flags & ZEND_ACC_PRIVATE
+ && priv_fbc->common.scope == scope) {
+ fbc = priv_fbc;
+ }
}
}
}
- if ((fbc->common.fn_flags & ZEND_ACC_PROTECTED)) {
+ if (fbc->common.fn_flags & ZEND_ACC_PROTECTED) {
/* Ensure that if we're calling a protected function, we're allowed to do so.
* If we're not and __call() handler exists, invoke it, otherwise error out.
*/
- if (UNEXPECTED(!zend_check_protected(zend_get_function_root_class(fbc), EG(scope)))) {
+ if (UNEXPECTED(!zend_check_protected(zend_get_function_root_class(fbc), scope))) {
if (zobj->ce->__call) {
fbc = zend_get_user_call_function(zobj->ce, method_name);
} else {
- zend_throw_error(NULL, "Call to %s method %s::%s() from context '%s'", zend_visibility_string(fbc->common.fn_flags), ZEND_FN_SCOPE_NAME(fbc), ZSTR_VAL(method_name), EG(scope) ? ZSTR_VAL(EG(scope)->name) : "");
+ zend_throw_error(NULL, "Call to %s method %s::%s() from context '%s'", zend_visibility_string(fbc->common.fn_flags), ZEND_FN_SCOPE_NAME(fbc), ZSTR_VAL(method_name), scope ? ZSTR_VAL(scope->name) : "");
fbc = NULL;
}
}
@@ -1214,6 +1271,7 @@ ZEND_API zend_function *zend_std_get_static_method(zend_class_entry *ce, zend_st
char *lc_class_name;
zend_string *lc_function_name;
zend_object *object;
+ zend_class_entry *scope;
if (EXPECTED(key != NULL)) {
lc_function_name = Z_STR_P(key);
@@ -1274,25 +1332,27 @@ ZEND_API zend_function *zend_std_get_static_method(zend_class_entry *ce, zend_st
/* Ensure that if we're calling a private function, we're allowed to do so.
*/
- updated_fbc = zend_check_private_int(fbc, EG(scope), lc_function_name);
+ scope = zend_get_executed_scope();
+ updated_fbc = zend_check_private_int(fbc, scope, lc_function_name);
if (EXPECTED(updated_fbc != NULL)) {
fbc = updated_fbc;
} else {
if (ce->__callstatic) {
fbc = zend_get_user_callstatic_function(ce, function_name);
} else {
- zend_throw_error(NULL, "Call to %s method %s::%s() from context '%s'", zend_visibility_string(fbc->common.fn_flags), ZEND_FN_SCOPE_NAME(fbc), ZSTR_VAL(function_name), EG(scope) ? ZSTR_VAL(EG(scope)->name) : "");
+ zend_throw_error(NULL, "Call to %s method %s::%s() from context '%s'", zend_visibility_string(fbc->common.fn_flags), ZEND_FN_SCOPE_NAME(fbc), ZSTR_VAL(function_name), scope ? ZSTR_VAL(scope->name) : "");
fbc = NULL;
}
}
} else if ((fbc->common.fn_flags & ZEND_ACC_PROTECTED)) {
/* Ensure that if we're calling a protected function, we're allowed to do so.
*/
- if (UNEXPECTED(!zend_check_protected(zend_get_function_root_class(fbc), EG(scope)))) {
+ scope = zend_get_executed_scope();
+ if (UNEXPECTED(!zend_check_protected(zend_get_function_root_class(fbc), scope))) {
if (ce->__callstatic) {
fbc = zend_get_user_callstatic_function(ce, function_name);
} else {
- zend_throw_error(NULL, "Call to %s method %s::%s() from context '%s'", zend_visibility_string(fbc->common.fn_flags), ZEND_FN_SCOPE_NAME(fbc), ZSTR_VAL(function_name), EG(scope) ? ZSTR_VAL(EG(scope)->name) : "");
+ zend_throw_error(NULL, "Call to %s method %s::%s() from context '%s'", zend_visibility_string(fbc->common.fn_flags), ZEND_FN_SCOPE_NAME(fbc), ZSTR_VAL(function_name), scope ? ZSTR_VAL(scope->name) : "");
fbc = NULL;
}
}
@@ -1356,6 +1416,7 @@ ZEND_API ZEND_COLD zend_bool zend_std_unset_static_property(zend_class_entry *ce
ZEND_API union _zend_function *zend_std_get_constructor(zend_object *zobj) /* {{{ */
{
zend_function *constructor = zobj->ce->constructor;
+ zend_class_entry *scope;
if (constructor) {
if (constructor->op_array.fn_flags & ZEND_ACC_PUBLIC) {
@@ -1363,9 +1424,14 @@ ZEND_API union _zend_function *zend_std_get_constructor(zend_object *zobj) /* {{
} else if (constructor->op_array.fn_flags & ZEND_ACC_PRIVATE) {
/* Ensure that if we're calling a private function, we're allowed to do so.
*/
- if (UNEXPECTED(constructor->common.scope != EG(scope))) {
- if (EG(scope)) {
- zend_throw_error(NULL, "Call to private %s::%s() from context '%s'", ZSTR_VAL(constructor->common.scope->name), ZSTR_VAL(constructor->common.function_name), ZSTR_VAL(EG(scope)->name));
+ if (EG(fake_scope)) {
+ scope = EG(fake_scope);
+ } else {
+ scope = zend_get_executed_scope();
+ }
+ if (UNEXPECTED(constructor->common.scope != scope)) {
+ if (scope) {
+ zend_throw_error(NULL, "Call to private %s::%s() from context '%s'", ZSTR_VAL(constructor->common.scope->name), ZSTR_VAL(constructor->common.function_name), ZSTR_VAL(scope->name));
constructor = NULL;
} else {
zend_throw_error(NULL, "Call to private %s::%s() from invalid context", ZSTR_VAL(constructor->common.scope->name), ZSTR_VAL(constructor->common.function_name));
@@ -1377,9 +1443,14 @@ ZEND_API union _zend_function *zend_std_get_constructor(zend_object *zobj) /* {{
* Constructors only have prototype if they are defined by an interface but
* it is the compilers responsibility to take care of the prototype.
*/
- if (UNEXPECTED(!zend_check_protected(zend_get_function_root_class(constructor), EG(scope)))) {
- if (EG(scope)) {
- zend_throw_error(NULL, "Call to protected %s::%s() from context '%s'", ZSTR_VAL(constructor->common.scope->name), ZSTR_VAL(constructor->common.function_name), ZSTR_VAL(EG(scope)->name));
+ if (EG(fake_scope)) {
+ scope = EG(fake_scope);
+ } else {
+ scope = zend_get_executed_scope();
+ }
+ if (UNEXPECTED(!zend_check_protected(zend_get_function_root_class(constructor), scope))) {
+ if (scope) {
+ zend_throw_error(NULL, "Call to protected %s::%s() from context '%s'", ZSTR_VAL(constructor->common.scope->name), ZSTR_VAL(constructor->common.function_name), ZSTR_VAL(scope->name));
constructor = NULL;
} else {
zend_throw_error(NULL, "Call to protected %s::%s() from invalid context", ZSTR_VAL(constructor->common.scope->name), ZSTR_VAL(constructor->common.function_name));
@@ -1508,7 +1579,7 @@ found:
result = 0;
if ((has_set_exists != 2) && zobj->ce->__isset) {
- zend_long *guard = zend_get_property_guard(zobj, Z_STR_P(member));
+ uint32_t *guard = zend_get_property_guard(zobj, Z_STR_P(member));
if (!((*guard) & IN_ISSET)) {
zval rv;
@@ -1636,7 +1707,7 @@ int zend_std_get_closure(zval *obj, zend_class_entry **ce_ptr, zend_function **f
ce = Z_OBJCE_P(obj);
- if ((func = zend_hash_str_find(&ce->function_table, ZEND_INVOKE_FUNC_NAME, sizeof(ZEND_INVOKE_FUNC_NAME)-1)) == NULL) {
+ if ((func = zend_hash_find(&ce->function_table, CG(known_strings)[ZEND_STR_MAGIC_INVOKE])) == NULL) {
return FAILURE;
}
*fptr_ptr = Z_FUNC_P(func);
diff --git a/Zend/zend_object_handlers.h b/Zend/zend_object_handlers.h
index 0b51db6743..2e397093ac 100644
--- a/Zend/zend_object_handlers.h
+++ b/Zend/zend_object_handlers.h
@@ -27,8 +27,8 @@ struct _zend_property_info;
#define ZEND_WRONG_PROPERTY_INFO \
((struct _zend_property_info*)((zend_intptr_t)-1))
-#define ZEND_DYNAMIC_PROPERTY_OFFSET (-1)
-#define ZEND_WRONG_PROPERTY_OFFSET (-2)
+#define ZEND_DYNAMIC_PROPERTY_OFFSET ((uint32_t)(-1))
+#define ZEND_WRONG_PROPERTY_OFFSET ((uint32_t)(-2))
/* The following rule applies to read_property() and read_dimension() implementations:
If you return a zval which is not otherwise referenced by the extension or the engine's
@@ -180,6 +180,8 @@ ZEND_API int zend_check_property_access(zend_object *zobj, zend_string *prop_inf
ZEND_API zend_function *zend_get_call_trampoline_func(zend_class_entry *ce, zend_string *method_name, int is_static);
+ZEND_API uint32_t *zend_get_property_guard(zend_object *zobj, zend_string *member);
+
#define zend_free_trampoline(func) do { \
if ((func) == &EG(trampoline)) { \
EG(trampoline).common.function_name = NULL; \
diff --git a/Zend/zend_objects.c b/Zend/zend_objects.c
index bde5954dff..1e358996ed 100644
--- a/Zend/zend_objects.c
+++ b/Zend/zend_objects.c
@@ -46,7 +46,6 @@ ZEND_API void zend_object_std_init(zend_object *object, zend_class_entry *ce)
}
if (UNEXPECTED(ce->ce_flags & ZEND_ACC_USE_GUARDS)) {
GC_FLAGS(object) |= IS_OBJ_USE_GUARDS;
- Z_PTR_P(p) = NULL;
ZVAL_UNDEF(p);
}
}
@@ -71,11 +70,17 @@ ZEND_API void zend_object_std_dtor(zend_object *object)
} while (p != end);
}
if (UNEXPECTED(GC_FLAGS(object) & IS_OBJ_HAS_GUARDS)) {
- HashTable *guards = Z_PTR_P(p);
+ if (EXPECTED(Z_TYPE_P(p) == IS_STRING)) {
+ zend_string_release(Z_STR_P(p));
+ } else {
+ HashTable *guards;
- ZEND_ASSERT(guards != NULL);
- zend_hash_destroy(guards);
- FREE_HASHTABLE(guards);
+ ZEND_ASSERT(Z_TYPE_P(p) == IS_ARRAY);
+ guards = Z_ARRVAL_P(p);
+ ZEND_ASSERT(guards != NULL);
+ zend_hash_destroy(guards);
+ FREE_HASHTABLE(guards);
+ }
}
}
@@ -86,51 +91,52 @@ ZEND_API void zend_objects_destroy_object(zend_object *object)
if (destructor) {
zend_object *old_exception;
zval obj;
+ zend_class_entry *orig_fake_scope;
if (destructor->op_array.fn_flags & (ZEND_ACC_PRIVATE|ZEND_ACC_PROTECTED)) {
if (destructor->op_array.fn_flags & ZEND_ACC_PRIVATE) {
/* Ensure that if we're calling a private function, we're allowed to do so.
*/
- if (object->ce != EG(scope)) {
- zend_class_entry *ce = object->ce;
+ if (EG(current_execute_data)) {
+ zend_class_entry *scope = zend_get_executed_scope();
- if (EG(current_execute_data)) {
+ if (object->ce != scope) {
zend_throw_error(NULL,
"Call to private %s::__destruct() from context '%s'",
- ZSTR_VAL(ce->name),
- EG(scope) ? ZSTR_VAL(EG(scope)->name) : "");
- } else {
- zend_error(E_WARNING,
- "Call to private %s::__destruct() from context '%s' during shutdown ignored",
- ZSTR_VAL(ce->name),
- EG(scope) ? ZSTR_VAL(EG(scope)->name) : "");
+ ZSTR_VAL(object->ce->name),
+ scope ? ZSTR_VAL(scope->name) : "");
+ return;
}
+ } else {
+ zend_error(E_WARNING,
+ "Call to private %s::__destruct() from context '' during shutdown ignored",
+ ZSTR_VAL(object->ce->name));
return;
}
} else {
/* Ensure that if we're calling a protected function, we're allowed to do so.
*/
- if (!zend_check_protected(zend_get_function_root_class(destructor), EG(scope))) {
- zend_class_entry *ce = object->ce;
+ if (EG(current_execute_data)) {
+ zend_class_entry *scope = zend_get_executed_scope();
- if (EG(current_execute_data)) {
+ if (!zend_check_protected(zend_get_function_root_class(destructor), scope)) {
zend_throw_error(NULL,
"Call to protected %s::__destruct() from context '%s'",
- ZSTR_VAL(ce->name),
- EG(scope) ? ZSTR_VAL(EG(scope)->name) : "");
- } else {
- zend_error(E_WARNING,
- "Call to protected %s::__destruct() from context '%s' during shutdown ignored",
- ZSTR_VAL(ce->name),
- EG(scope) ? ZSTR_VAL(EG(scope)->name) : "");
+ ZSTR_VAL(object->ce->name),
+ scope ? ZSTR_VAL(scope->name) : "");
+ return;
}
+ } else {
+ zend_error(E_WARNING,
+ "Call to protected %s::__destruct() from context '' during shutdown ignored",
+ ZSTR_VAL(object->ce->name));
return;
}
}
}
+ GC_REFCOUNT(object)++;
ZVAL_OBJ(&obj, object);
- Z_ADDREF(obj);
/* Make sure that destructors are protected from previously thrown exceptions.
* For example, if an exception was thrown in a function and when the function's
@@ -145,6 +151,8 @@ ZEND_API void zend_objects_destroy_object(zend_object *object)
EG(exception) = NULL;
}
}
+ orig_fake_scope = EG(fake_scope);
+ EG(fake_scope) = NULL;
zend_call_method_with_0_params(&obj, object->ce, &destructor, ZEND_DESTRUCTOR_FUNC_NAME, NULL);
if (old_exception) {
if (EG(exception)) {
@@ -154,6 +162,7 @@ ZEND_API void zend_objects_destroy_object(zend_object *object)
}
}
zval_ptr_dtor(&obj);
+ EG(fake_scope) = orig_fake_scope;
}
}
@@ -227,7 +236,7 @@ ZEND_API void zend_objects_clone_members(zend_object *new_object, zend_object *o
zval new_obj;
ZVAL_OBJ(&new_obj, new_object);
- zval_copy_ctor(&new_obj);
+ Z_ADDREF(new_obj);
zend_call_method_with_0_params(&new_obj, old_object->ce, &old_object->ce->clone, ZEND_CLONE_FUNC_NAME, NULL);
zval_ptr_dtor(&new_obj);
}
diff --git a/Zend/zend_opcode.c b/Zend/zend_opcode.c
index 2cf32b9c0a..4e7469edb9 100644
--- a/Zend/zend_opcode.c
+++ b/Zend/zend_opcode.c
@@ -78,15 +78,13 @@ void init_op_array(zend_op_array *op_array, zend_uchar type, int initial_ops_siz
op_array->scope = NULL;
op_array->prototype = NULL;
- op_array->brk_cont_array = NULL;
+ op_array->live_range = NULL;
op_array->try_catch_array = NULL;
- op_array->last_brk_cont = 0;
+ op_array->last_live_range = 0;
op_array->static_variables = NULL;
op_array->last_try_catch = 0;
- op_array->this_var = -1;
-
op_array->fn_flags = 0;
op_array->early_binding = -1;
@@ -287,7 +285,19 @@ ZEND_API void destroy_zend_class(zval *zv)
zend_hash_destroy(&ce->properties_info);
zend_string_release(ce->name);
zend_hash_destroy(&ce->function_table);
- zend_hash_destroy(&ce->constants_table);
+ if (zend_hash_num_elements(&ce->constants_table)) {
+ zend_class_constant *c;
+
+ ZEND_HASH_FOREACH_PTR(&ce->constants_table, c) {
+ if (c->ce == ce) {
+ zval_ptr_dtor(&c->value);
+ if (c->doc_comment) {
+ zend_string_release(c->doc_comment);
+ }
+ }
+ } ZEND_HASH_FOREACH_END();
+ zend_hash_destroy(&ce->constants_table);
+ }
if (ce->num_interfaces > 0 && ce->interfaces) {
efree(ce->interfaces);
}
@@ -322,7 +332,17 @@ ZEND_API void destroy_zend_class(zval *zv)
zend_hash_destroy(&ce->properties_info);
zend_string_release(ce->name);
zend_hash_destroy(&ce->function_table);
- zend_hash_destroy(&ce->constants_table);
+ if (zend_hash_num_elements(&ce->constants_table)) {
+ zend_class_constant *c;
+
+ ZEND_HASH_FOREACH_PTR(&ce->constants_table, c) {
+ zval_internal_ptr_dtor(&c->value);
+ if (c->doc_comment && c->ce == ce) {
+ zend_string_release(c->doc_comment);
+ }
+ } ZEND_HASH_FOREACH_END();
+ zend_hash_destroy(&ce->constants_table);
+ }
if (ce->num_interfaces > 0) {
free(ce->interfaces);
}
@@ -387,8 +407,8 @@ ZEND_API void destroy_op_array(zend_op_array *op_array)
if (op_array->doc_comment) {
zend_string_release(op_array->doc_comment);
}
- if (op_array->brk_cont_array) {
- efree(op_array->brk_cont_array);
+ if (op_array->live_range) {
+ efree(op_array->live_range);
}
if (op_array->try_catch_array) {
efree(op_array->try_catch_array);
@@ -450,11 +470,11 @@ int get_next_op_number(zend_op_array *op_array)
return op_array->last;
}
-zend_brk_cont_element *get_next_brk_cont_element(zend_op_array *op_array)
+zend_brk_cont_element *get_next_brk_cont_element(void)
{
- op_array->last_brk_cont++;
- op_array->brk_cont_array = erealloc(op_array->brk_cont_array, sizeof(zend_brk_cont_element)*op_array->last_brk_cont);
- return &op_array->brk_cont_array[op_array->last_brk_cont-1];
+ CG(context).last_brk_cont++;
+ CG(context).brk_cont_array = erealloc(CG(context).brk_cont_array, sizeof(zend_brk_cont_element) * CG(context).last_brk_cont);
+ return &CG(context).brk_cont_array[CG(context).last_brk_cont-1];
}
static void zend_update_extended_info(zend_op_array *op_array)
@@ -512,60 +532,12 @@ static void zend_check_finally_breakout(zend_op_array *op_array, uint32_t op_num
}
}
-static void zend_resolve_fast_call(zend_op_array *op_array, uint32_t op_num)
-{
- int i;
- uint32_t finally_op_num = 0;
-
- for (i = 0; i < op_array->last_try_catch; i++) {
- if (op_num >= op_array->try_catch_array[i].finally_op
- && op_num < op_array->try_catch_array[i].finally_end) {
- finally_op_num = op_array->try_catch_array[i].finally_op;
- }
- }
-
- if (finally_op_num) {
- /* Must be ZEND_FAST_CALL */
- ZEND_ASSERT(op_array->opcodes[finally_op_num - 2].opcode == ZEND_FAST_CALL);
- op_array->opcodes[op_num].extended_value = ZEND_FAST_CALL_FROM_FINALLY;
- op_array->opcodes[op_num].op2.opline_num = finally_op_num - 2;
- }
-}
-
-static void zend_resolve_finally_ret(zend_op_array *op_array, uint32_t op_num)
-{
- int i;
- uint32_t catch_op_num = 0, finally_op_num = 0;
-
- for (i = 0; i < op_array->last_try_catch; i++) {
- if (op_array->try_catch_array[i].try_op > op_num) {
- break;
- }
- if (op_num < op_array->try_catch_array[i].finally_op) {
- finally_op_num = op_array->try_catch_array[i].finally_op;
- }
- if (op_num < op_array->try_catch_array[i].catch_op) {
- catch_op_num = op_array->try_catch_array[i].catch_op;
- }
- }
-
- if (finally_op_num && (!catch_op_num || catch_op_num >= finally_op_num)) {
- /* in case of unhandled exception return to upward finally block */
- op_array->opcodes[op_num].extended_value = ZEND_FAST_RET_TO_FINALLY;
- op_array->opcodes[op_num].op2.opline_num = finally_op_num;
- } else if (catch_op_num) {
- /* in case of unhandled exception return to upward catch block */
- op_array->opcodes[op_num].extended_value = ZEND_FAST_RET_TO_CATCH;
- op_array->opcodes[op_num].op2.opline_num = catch_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];
+ jmp_to = &CG(context).brk_cont_array[array_offset];
if (nest_levels > 1) {
array_offset = jmp_to->parent;
}
@@ -608,19 +580,8 @@ ZEND_API int pass_two(zend_op_array *op_array)
switch (opline->opcode) {
case ZEND_FAST_CALL:
opline->op1.opline_num = op_array->try_catch_array[opline->op1.num].finally_op;
- zend_resolve_fast_call(op_array, opline - op_array->opcodes);
ZEND_PASS_TWO_UPDATE_JMP_TARGET(op_array, opline, opline->op1);
break;
- case ZEND_FAST_RET:
- zend_resolve_finally_ret(op_array, opline - op_array->opcodes);
- break;
- case ZEND_DECLARE_ANON_INHERITED_CLASS:
- ZEND_PASS_TWO_UPDATE_JMP_TARGET(op_array, opline, opline->op1);
- /* break omitted intentionally */
- case ZEND_DECLARE_INHERITED_CLASS:
- case ZEND_DECLARE_INHERITED_CLASS_DELAYED:
- opline->extended_value = (uint32_t)(zend_intptr_t)ZEND_CALL_VAR_NUM(NULL, op_array->last_var + opline->extended_value);
- break;
case ZEND_BRK:
case ZEND_CONT:
{
@@ -642,7 +603,6 @@ ZEND_API int pass_two(zend_op_array *op_array)
}
/* break omitted intentionally */
case ZEND_JMP:
- case ZEND_DECLARE_ANON_CLASS:
ZEND_PASS_TWO_UPDATE_JMP_TARGET(op_array, opline, opline->op1);
break;
case ZEND_JMPZNZ:
@@ -655,29 +615,31 @@ ZEND_API int pass_two(zend_op_array *op_array)
case ZEND_JMPNZ_EX:
case ZEND_JMP_SET:
case ZEND_COALESCE:
- case ZEND_NEW:
case ZEND_FE_RESET_R:
case ZEND_FE_RESET_RW:
+ ZEND_PASS_TWO_UPDATE_JMP_TARGET(op_array, opline, opline->op2);
+ break;
case ZEND_ASSERT_CHECK:
+ {
+ /* If result of assert is unused, result of check is unused as well */
+ zend_op *call = &op_array->opcodes[opline->op2.opline_num - 1];
+ if (call->opcode == ZEND_EXT_FCALL_END) {
+ call--;
+ }
+ if (call->result_type == IS_UNUSED) {
+ opline->result_type = IS_UNUSED;
+ }
ZEND_PASS_TWO_UPDATE_JMP_TARGET(op_array, opline, opline->op2);
break;
+ }
+ case ZEND_DECLARE_ANON_CLASS:
+ case ZEND_DECLARE_ANON_INHERITED_CLASS:
+ case ZEND_CATCH:
case ZEND_FE_FETCH_R:
case ZEND_FE_FETCH_RW:
+ /* absolute index to relative offset */
opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, opline, opline->extended_value);
break;
- case ZEND_VERIFY_RETURN_TYPE:
- if (op_array->fn_flags & ZEND_ACC_GENERATOR) {
- if (opline->op1_type != IS_UNUSED) {
- zend_op *ret = opline;
- do ret++; while (ret->opcode != ZEND_RETURN);
-
- ret->op1 = opline->op1;
- ret->op1_type = opline->op1_type;
- }
-
- MAKE_NOP(opline);
- }
- break;
case ZEND_RETURN:
case ZEND_RETURN_BY_REF:
if (op_array->fn_flags & ZEND_ACC_GENERATOR) {
@@ -702,6 +664,16 @@ ZEND_API int pass_two(zend_op_array *op_array)
opline++;
}
+ if (op_array->live_range) {
+ int i;
+
+ for (i = 0; i < op_array->last_live_range; i++) {
+ op_array->live_range[i].var =
+ (uint32_t)(zend_intptr_t)ZEND_CALL_VAR_NUM(NULL, op_array->last_var + (op_array->live_range[i].var / sizeof(zval))) |
+ (op_array->live_range[i].var & ZEND_LIVE_MASK);
+ }
+ }
+
op_array->fn_flags |= ZEND_ACC_DONE_PASS_TWO;
return 0;
}
diff --git a/Zend/zend_operators.c b/Zend/zend_operators.c
index a02551b1bd..4159e43e53 100644
--- a/Zend/zend_operators.c
+++ b/Zend/zend_operators.c
@@ -136,18 +136,7 @@ ZEND_API zend_long ZEND_FASTCALL zend_atol(const char *str, int str_len) /* {{{
}
/* }}} */
-static zend_always_inline void zend_unwrap_reference(zval *op) /* {{{ */
-{
- if (Z_REFCOUNT_P(op) == 1) {
- ZVAL_UNREF(op);
- } else {
- Z_DELREF_P(op);
- ZVAL_COPY(op, Z_REFVAL_P(op));
- }
-}
-/* }}} */
-
-ZEND_API void ZEND_FASTCALL convert_scalar_to_number(zval *op) /* {{{ */
+void ZEND_FASTCALL _convert_scalar_to_number(zval *op, zend_bool silent) /* {{{ */
{
try_again:
switch (Z_TYPE_P(op)) {
@@ -159,8 +148,11 @@ try_again:
zend_string *str;
str = Z_STR_P(op);
- if ((Z_TYPE_INFO_P(op)=is_numeric_string(ZSTR_VAL(str), ZSTR_LEN(str), &Z_LVAL_P(op), &Z_DVAL_P(op), 1)) == 0) {
+ if ((Z_TYPE_INFO_P(op)=is_numeric_string(ZSTR_VAL(str), ZSTR_LEN(str), &Z_LVAL_P(op), &Z_DVAL_P(op), silent ? 1 : -1)) == 0) {
ZVAL_LONG(op, 0);
+ if (!silent) {
+ zend_error(E_WARNING, "A non-numeric value encountered");
+ }
}
zend_string_release(str);
break;
@@ -186,18 +178,27 @@ try_again:
}
/* }}} */
+ZEND_API void ZEND_FASTCALL convert_scalar_to_number(zval *op) /* {{{ */
+{
+ _convert_scalar_to_number(op, 1);
+}
+/* }}} */
+
/* {{{ zendi_convert_scalar_to_number */
-#define zendi_convert_scalar_to_number(op, holder, result) \
+#define zendi_convert_scalar_to_number(op, holder, result, silent) \
if (op==result) { \
if (Z_TYPE_P(op) != IS_LONG) { \
- convert_scalar_to_number(op); \
+ _convert_scalar_to_number(op, silent); \
} \
} else { \
switch (Z_TYPE_P(op)) { \
case IS_STRING: \
{ \
- if ((Z_TYPE_INFO(holder)=is_numeric_string(Z_STRVAL_P(op), Z_STRLEN_P(op), &Z_LVAL(holder), &Z_DVAL(holder), 1)) == 0) { \
+ if ((Z_TYPE_INFO(holder)=is_numeric_string(Z_STRVAL_P(op), Z_STRLEN_P(op), &Z_LVAL(holder), &Z_DVAL(holder), silent ? 1 : -1)) == 0) { \
ZVAL_LONG(&(holder), 0); \
+ if (!silent) { \
+ zend_error(E_WARNING, "A non-numeric value encountered"); \
+ } \
} \
(op) = &(holder); \
break; \
@@ -258,7 +259,7 @@ try_again:
} \
} \
ZEND_TRY_BINARY_OP1_OBJECT_OPERATION(op, op_func); \
- op1_lval = _zval_get_long_func(op1); \
+ op1_lval = _zval_get_long_func_noisy(op1); \
} else { \
op1_lval = Z_LVAL_P(op1); \
} \
@@ -273,7 +274,7 @@ try_again:
} \
} \
ZEND_TRY_BINARY_OP2_OBJECT_OPERATION(op); \
- op2_lval = _zval_get_long_func(op2); \
+ op2_lval = _zval_get_long_func_noisy(op2); \
} else { \
op2_lval = Z_LVAL_P(op2); \
} \
@@ -313,8 +314,11 @@ try_again:
case IS_STRING:
{
zend_string *str = Z_STR_P(op);
-
- ZVAL_LONG(op, ZEND_STRTOL(ZSTR_VAL(str), NULL, base));
+ if (base == 10) {
+ ZVAL_LONG(op, zval_get_long(op));
+ } else {
+ ZVAL_LONG(op, ZEND_STRTOL(ZSTR_VAL(str), NULL, base));
+ }
zend_string_release(str);
}
break;
@@ -595,26 +599,26 @@ try_again:
if (Z_OBJ_HT_P(op)->get_properties) {
HashTable *obj_ht = Z_OBJ_HT_P(op)->get_properties(op);
if (obj_ht) {
- zval arr;
+ zend_array *arr;
if (!Z_OBJCE_P(op)->default_properties_count &&
obj_ht == Z_OBJ_P(op)->properties &&
!ZEND_HASH_GET_APPLY_COUNT(Z_OBJ_P(op)->properties)) {
/* fast copy */
if (EXPECTED(Z_OBJ_P(op)->handlers == &std_object_handlers)) {
- ZVAL_ARR(&arr, obj_ht);
+ arr = obj_ht;
if (EXPECTED(!(GC_FLAGS(Z_OBJ_P(op)->properties) & IS_ARRAY_IMMUTABLE))) {
GC_REFCOUNT(Z_OBJ_P(op)->properties)++;
}
} else {
- ZVAL_ARR(&arr, zend_array_dup(obj_ht));
+ arr = zend_array_dup(obj_ht);
}
zval_dtor(op);
- ZVAL_COPY_VALUE(op, &arr);
+ ZVAL_ARR(op, arr);
} else {
- ZVAL_ARR(&arr, zend_array_dup(obj_ht));
+ arr = zend_array_dup(obj_ht);
zval_dtor(op);
- ZVAL_COPY_VALUE(op, &arr);
+ ZVAL_ARR(op, arr);
}
return;
}
@@ -673,7 +677,7 @@ try_again:
zval tmp;
ZVAL_COPY_VALUE(&tmp, op);
object_init(op);
- zend_hash_str_add_new(Z_OBJPROP_P(op), "scalar", sizeof("scalar")-1, &tmp);
+ zend_hash_add_new(Z_OBJPROP_P(op), CG(known_strings)[ZEND_STR_SCALAR], &tmp);
break;
}
}
@@ -728,10 +732,11 @@ ZEND_API void multi_convert_to_string_ex(int argc, ...) /* {{{ */
}
/* }}} */
-ZEND_API zend_long ZEND_FASTCALL _zval_get_long_func(zval *op) /* {{{ */
+static zend_always_inline zend_long ZEND_FASTCALL _zval_get_long_func_ex(zval *op, zend_bool silent) /* {{{ */
{
try_again:
switch (Z_TYPE_P(op)) {
+ case IS_UNDEF:
case IS_NULL:
case IS_FALSE:
return 0;
@@ -744,7 +749,26 @@ try_again:
case IS_DOUBLE:
return zend_dval_to_lval(Z_DVAL_P(op));
case IS_STRING:
- return ZEND_STRTOL(Z_STRVAL_P(op), NULL, 10);
+ {
+ zend_uchar type;
+ zend_long lval;
+ double dval;
+ if (0 == (type = is_numeric_string(Z_STRVAL_P(op), Z_STRLEN_P(op), &lval, &dval, silent ? 1 : -1))) {
+ if (!silent) {
+ zend_error(E_WARNING, "A non-numeric value encountered");
+ }
+ return 0;
+ } else if (EXPECTED(type == IS_LONG)) {
+ return lval;
+ } else {
+ /* Previously we used strtol here, not is_numeric_string,
+ * and strtol gives you LONG_MAX/_MIN on overflow.
+ * We use use saturating conversion to emulate strtol()'s
+ * behaviour.
+ */
+ return zend_dval_to_lval_cap(dval);
+ }
+ }
case IS_ARRAY:
return zend_hash_num_elements(Z_ARRVAL_P(op)) ? 1 : 0;
case IS_OBJECT:
@@ -766,6 +790,18 @@ try_again:
}
/* }}} */
+ZEND_API zend_long ZEND_FASTCALL _zval_get_long_func(zval *op) /* {{{ */
+{
+ return _zval_get_long_func_ex(op, 1);
+}
+/* }}} */
+
+static zend_long ZEND_FASTCALL _zval_get_long_func_noisy(zval *op) /* {{{ */
+{
+ return _zval_get_long_func_ex(op, 0);
+}
+/* }}} */
+
ZEND_API double ZEND_FASTCALL _zval_get_double_func(zval *op) /* {{{ */
{
try_again:
@@ -871,20 +907,9 @@ ZEND_API int ZEND_FASTCALL add_function(zval *result, zval *op1, zval *op2) /* {
while (1) {
switch (TYPE_PAIR(Z_TYPE_P(op1), Z_TYPE_P(op2))) {
- case TYPE_PAIR(IS_LONG, IS_LONG): {
- zend_long lval = Z_LVAL_P(op1) + Z_LVAL_P(op2);
-
- /* check for overflow by comparing sign bits */
- if ((Z_LVAL_P(op1) & LONG_SIGN_MASK) == (Z_LVAL_P(op2) & LONG_SIGN_MASK)
- && (Z_LVAL_P(op1) & LONG_SIGN_MASK) != (lval & LONG_SIGN_MASK)) {
-
- ZVAL_DOUBLE(result, (double) Z_LVAL_P(op1) + (double) Z_LVAL_P(op2));
- } else {
- ZVAL_LONG(result, lval);
- }
+ case TYPE_PAIR(IS_LONG, IS_LONG):
+ fast_long_add_function(result, op1, op2);
return SUCCESS;
- }
-
case TYPE_PAIR(IS_LONG, IS_DOUBLE):
ZVAL_DOUBLE(result, ((double)Z_LVAL_P(op1)) + Z_DVAL_P(op2));
return SUCCESS;
@@ -916,8 +941,8 @@ ZEND_API int ZEND_FASTCALL add_function(zval *result, zval *op1, zval *op2) /* {
} else if (!converted) {
ZEND_TRY_BINARY_OBJECT_OPERATION(ZEND_ADD, add_function);
- zendi_convert_scalar_to_number(op1, op1_copy, result);
- zendi_convert_scalar_to_number(op2, op2_copy, result);
+ zendi_convert_scalar_to_number(op1, op1_copy, result, 0);
+ zendi_convert_scalar_to_number(op2, op2_copy, result, 0);
converted = 1;
} else {
zend_throw_error(NULL, "Unsupported operand types");
@@ -935,20 +960,9 @@ ZEND_API int ZEND_FASTCALL sub_function(zval *result, zval *op1, zval *op2) /* {
while (1) {
switch (TYPE_PAIR(Z_TYPE_P(op1), Z_TYPE_P(op2))) {
- case TYPE_PAIR(IS_LONG, IS_LONG): {
- zend_long lval = Z_LVAL_P(op1) - Z_LVAL_P(op2);
-
- /* check for overflow by comparing sign bits */
- if ((Z_LVAL_P(op1) & LONG_SIGN_MASK) != (Z_LVAL_P(op2) & LONG_SIGN_MASK)
- && (Z_LVAL_P(op1) & LONG_SIGN_MASK) != (lval & LONG_SIGN_MASK)) {
-
- ZVAL_DOUBLE(result, (double) Z_LVAL_P(op1) - (double) Z_LVAL_P(op2));
- } else {
- ZVAL_LONG(result, lval);
- }
+ case TYPE_PAIR(IS_LONG, IS_LONG):
+ fast_long_sub_function(result, op1, op2);
return SUCCESS;
-
- }
case TYPE_PAIR(IS_LONG, IS_DOUBLE):
ZVAL_DOUBLE(result, ((double)Z_LVAL_P(op1)) - Z_DVAL_P(op2));
return SUCCESS;
@@ -969,8 +983,8 @@ ZEND_API int ZEND_FASTCALL sub_function(zval *result, zval *op1, zval *op2) /* {
} else if (!converted) {
ZEND_TRY_BINARY_OBJECT_OPERATION(ZEND_SUB, sub_function);
- zendi_convert_scalar_to_number(op1, op1_copy, result);
- zendi_convert_scalar_to_number(op2, op2_copy, result);
+ zendi_convert_scalar_to_number(op1, op1_copy, result, 0);
+ zendi_convert_scalar_to_number(op2, op2_copy, result, 0);
converted = 1;
} else {
zend_throw_error(NULL, "Unsupported operand types");
@@ -1016,8 +1030,8 @@ ZEND_API int ZEND_FASTCALL mul_function(zval *result, zval *op1, zval *op2) /* {
} else if (!converted) {
ZEND_TRY_BINARY_OBJECT_OPERATION(ZEND_MUL, mul_function);
- zendi_convert_scalar_to_number(op1, op1_copy, result);
- zendi_convert_scalar_to_number(op2, op2_copy, result);
+ zendi_convert_scalar_to_number(op1, op1_copy, result, 0);
+ zendi_convert_scalar_to_number(op2, op2_copy, result, 0);
converted = 1;
} else {
zend_throw_error(NULL, "Unsupported operand types");
@@ -1098,13 +1112,13 @@ ZEND_API int ZEND_FASTCALL pow_function(zval *result, zval *op1, zval *op2) /* {
ZVAL_LONG(result, 0);
return SUCCESS;
} else {
- zendi_convert_scalar_to_number(op1, op1_copy, result);
+ zendi_convert_scalar_to_number(op1, op1_copy, result, 0);
}
if (Z_TYPE_P(op2) == IS_ARRAY) {
ZVAL_LONG(result, 1L);
return SUCCESS;
} else {
- zendi_convert_scalar_to_number(op2, op2_copy, result);
+ zendi_convert_scalar_to_number(op2, op2_copy, result, 0);
}
converted = 1;
} else {
@@ -1169,8 +1183,8 @@ ZEND_API int ZEND_FASTCALL div_function(zval *result, zval *op1, zval *op2) /* {
} else if (!converted) {
ZEND_TRY_BINARY_OBJECT_OPERATION(ZEND_DIV, div_function);
- zendi_convert_scalar_to_number(op1, op1_copy, result);
- zendi_convert_scalar_to_number(op2, op2_copy, result);
+ zendi_convert_scalar_to_number(op1, op1_copy, result, 0);
+ zendi_convert_scalar_to_number(op2, op2_copy, result, 0);
converted = 1;
} else {
zend_throw_error(NULL, "Unsupported operand types");
@@ -1377,13 +1391,13 @@ ZEND_API int ZEND_FASTCALL bitwise_or_function(zval *result, zval *op1, zval *op
if (UNEXPECTED(Z_TYPE_P(op1) != IS_LONG)) {
ZEND_TRY_BINARY_OP1_OBJECT_OPERATION(ZEND_BW_OR, bitwise_or_function);
- op1_lval = _zval_get_long_func(op1);
+ op1_lval = _zval_get_long_func_noisy(op1);
} else {
op1_lval = Z_LVAL_P(op1);
}
if (UNEXPECTED(Z_TYPE_P(op2) != IS_LONG)) {
ZEND_TRY_BINARY_OP2_OBJECT_OPERATION(ZEND_BW_OR);
- op2_lval = _zval_get_long_func(op2);
+ op2_lval = _zval_get_long_func_noisy(op2);
} else {
op2_lval = Z_LVAL_P(op2);
}
@@ -1444,13 +1458,13 @@ 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_and_function);
- op1_lval = _zval_get_long_func(op1);
+ op1_lval = _zval_get_long_func_noisy(op1);
} else {
op1_lval = Z_LVAL_P(op1);
}
if (UNEXPECTED(Z_TYPE_P(op2) != IS_LONG)) {
ZEND_TRY_BINARY_OP2_OBJECT_OPERATION(ZEND_BW_AND);
- op2_lval = _zval_get_long_func(op2);
+ op2_lval = _zval_get_long_func_noisy(op2);
} else {
op2_lval = Z_LVAL_P(op2);
}
@@ -1511,13 +1525,13 @@ 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_xor_function);
- op1_lval = _zval_get_long_func(op1);
+ op1_lval = _zval_get_long_func_noisy(op1);
} else {
op1_lval = Z_LVAL_P(op1);
}
if (UNEXPECTED(Z_TYPE_P(op2) != IS_LONG)) {
ZEND_TRY_BINARY_OP2_OBJECT_OPERATION(ZEND_BW_XOR);
- op2_lval = _zval_get_long_func(op2);
+ op2_lval = _zval_get_long_func_noisy(op2);
} else {
op2_lval = Z_LVAL_P(op2);
}
@@ -1944,8 +1958,8 @@ ZEND_API int ZEND_FASTCALL compare_function(zval *result, zval *op1, zval *op2)
ZVAL_LONG(result, zval_is_true(op1) ? 0 : -1);
return SUCCESS;
} else {
- zendi_convert_scalar_to_number(op1, op1_copy, result);
- zendi_convert_scalar_to_number(op2, op2_copy, result);
+ zendi_convert_scalar_to_number(op1, op1_copy, result, 1);
+ zendi_convert_scalar_to_number(op2, op2_copy, result, 1);
converted = 1;
}
} else if (Z_TYPE_P(op1)==IS_ARRAY) {
@@ -2896,6 +2910,8 @@ static zend_always_inline void zend_memnstr_ex_pre(unsigned int td[], const char
td[(unsigned char)needle[i]] = i + 1;
}
} else {
+ size_t i;
+
for (i = 0; i < needle_len; i++) {
td[(unsigned char)needle[i]] = (int)needle_len - i;
}
diff --git a/Zend/zend_operators.h b/Zend/zend_operators.h
index f0ad0300fb..370502282d 100644
--- a/Zend/zend_operators.h
+++ b/Zend/zend_operators.h
@@ -122,6 +122,16 @@ static zend_always_inline zend_long zend_dval_to_lval(double d)
return (zend_long)d;
}
#endif
+
+static zend_always_inline zend_long zend_dval_to_lval_cap(double d)
+{
+ if (UNEXPECTED(!zend_finite(d)) || UNEXPECTED(zend_isnan(d))) {
+ return 0;
+ } else if (!ZEND_DOUBLE_FITS_LONG(d)) {
+ return (d > 0 ? ZEND_LONG_MAX : ZEND_LONG_MIN);
+ }
+ return (zend_long)d;
+}
/* }}} */
#define ZEND_IS_DIGIT(c) ((c) >= '0' && (c) <= '9')
@@ -185,7 +195,7 @@ zend_memnstr(const char *haystack, const char *needle, size_t needle_len, const
static zend_always_inline const void *zend_memrchr(const void *s, int c, size_t n)
{
- register const unsigned char *e;
+ const unsigned char *e;
if (n <= 0) {
return NULL;
}
@@ -446,7 +456,7 @@ static zend_always_inline void fast_long_increment_function(zval *op1)
: "r"(&op1->value),
"n"(IS_DOUBLE),
"n"(ZVAL_OFFSETOF_TYPE)
- : "cc");
+ : "cc", "memory");
#elif defined(__GNUC__) && defined(__x86_64__)
__asm__(
"incq (%0)\n\t"
@@ -459,7 +469,7 @@ static zend_always_inline void fast_long_increment_function(zval *op1)
: "r"(&op1->value),
"n"(IS_DOUBLE),
"n"(ZVAL_OFFSETOF_TYPE)
- : "cc");
+ : "cc", "memory");
#else
if (UNEXPECTED(Z_LVAL_P(op1) == ZEND_LONG_MAX)) {
/* switch to double */
@@ -484,7 +494,7 @@ static zend_always_inline void fast_long_decrement_function(zval *op1)
: "r"(&op1->value),
"n"(IS_DOUBLE),
"n"(ZVAL_OFFSETOF_TYPE)
- : "cc");
+ : "cc", "memory");
#elif defined(__GNUC__) && defined(__x86_64__)
__asm__(
"decq (%0)\n\t"
@@ -497,7 +507,7 @@ static zend_always_inline void fast_long_decrement_function(zval *op1)
: "r"(&op1->value),
"n"(IS_DOUBLE),
"n"(ZVAL_OFFSETOF_TYPE)
- : "cc");
+ : "cc", "memory");
#else
if (UNEXPECTED(Z_LVAL_P(op1) == ZEND_LONG_MIN)) {
/* switch to double */
@@ -532,7 +542,7 @@ static zend_always_inline void fast_long_add_function(zval *result, zval *op1, z
"n"(IS_LONG),
"n"(IS_DOUBLE),
"n"(ZVAL_OFFSETOF_TYPE)
- : "eax","cc");
+ : "eax","cc", "memory");
#elif defined(__GNUC__) && defined(__x86_64__)
__asm__(
"movq (%1), %%rax\n\t"
@@ -555,7 +565,7 @@ static zend_always_inline void fast_long_add_function(zval *result, zval *op1, z
"n"(IS_LONG),
"n"(IS_DOUBLE),
"n"(ZVAL_OFFSETOF_TYPE)
- : "rax","cc");
+ : "rax","cc", "memory");
#else
/*
* 'result' may alias with op1 or op2, so we need to
@@ -622,7 +632,7 @@ static zend_always_inline void fast_long_sub_function(zval *result, zval *op1, z
"n"(IS_LONG),
"n"(IS_DOUBLE),
"n"(ZVAL_OFFSETOF_TYPE)
- : "eax","cc");
+ : "eax","cc", "memory");
#elif defined(__GNUC__) && defined(__x86_64__)
__asm__(
"movq (%1), %%rax\n\t"
@@ -649,7 +659,7 @@ static zend_always_inline void fast_long_sub_function(zval *result, zval *op1, z
"n"(IS_LONG),
"n"(IS_DOUBLE),
"n"(ZVAL_OFFSETOF_TYPE)
- : "rax","cc");
+ : "rax","cc", "memory");
#else
ZVAL_LONG(result, Z_LVAL_P(op1) - Z_LVAL_P(op2));
@@ -811,6 +821,18 @@ static zend_always_inline char *zend_print_long_to_buf(char *buf, zend_long num)
ZEND_API zend_string* ZEND_FASTCALL zend_long_to_str(zend_long num);
+static zend_always_inline void zend_unwrap_reference(zval *op) /* {{{ */
+{
+ if (Z_REFCOUNT_P(op) == 1) {
+ ZVAL_UNREF(op);
+ } else {
+ Z_DELREF_P(op);
+ ZVAL_COPY(op, Z_REFVAL_P(op));
+ }
+}
+/* }}} */
+
+
END_EXTERN_C()
#endif
diff --git a/Zend/zend_portability.h b/Zend/zend_portability.h
index f187129416..298dfb53fa 100644
--- a/Zend/zend_portability.h
+++ b/Zend/zend_portability.h
@@ -284,7 +284,7 @@ char *alloca();
# endif
# elif defined(_MSC_VER)
# define zend_always_inline __forceinline
-# define zend_never_inline
+# define zend_never_inline __declspec(noinline)
# else
# if __has_attribute(always_inline)
# define zend_always_inline inline __attribute__((always_inline))
diff --git a/Zend/zend_smart_str.c b/Zend/zend_smart_str.c
index bf29649972..c9b97acd1d 100644
--- a/Zend/zend_smart_str.c
+++ b/Zend/zend_smart_str.c
@@ -17,7 +17,7 @@
*/
#include <zend.h>
-#include "zend_smart_str_public.h"
+#include "zend_smart_str.h"
#define SMART_STR_OVERHEAD (ZEND_MM_OVERHEAD + _ZSTR_HEADER_SIZE)
@@ -59,3 +59,61 @@ ZEND_API void ZEND_FASTCALL smart_str_realloc(smart_str *str, size_t len)
str->s = (zend_string *) realloc(str->s, _ZSTR_HEADER_SIZE + str->a + 1);
}
}
+
+/* Windows uses VK_ESCAPE instead of \e */
+#ifndef VK_ESCAPE
+#define VK_ESCAPE '\e'
+#endif
+
+static size_t zend_compute_escaped_string_len(const char *s, size_t l) {
+ size_t i, len = l;
+ for (i = 0; i < l; ++i) {
+ char c = s[i];
+ if (c == '\n' || c == '\r' || c == '\t' ||
+ c == '\f' || c == '\v' || c == '\\' || c == VK_ESCAPE) {
+ len += 1;
+ } else if (c < 32 || c > 126) {
+ len += 3;
+ }
+ }
+ return len;
+}
+
+ZEND_API void ZEND_FASTCALL smart_str_append_escaped(smart_str *str, const char *s, size_t l) {
+ char *res;
+ size_t i, len = zend_compute_escaped_string_len(s, l);
+
+ smart_str_alloc(str, len, 0);
+ res = &ZSTR_VAL(str->s)[ZSTR_LEN(str->s)];
+ ZSTR_LEN(str->s) += len;
+
+ for (i = 0; i < l; ++i) {
+ unsigned char c = s[i];
+ if (c < 32 || c == '\\' || c > 126) {
+ *res++ = '\\';
+ switch (c) {
+ case '\n': *res++ = 'n'; break;
+ case '\r': *res++ = 'r'; break;
+ case '\t': *res++ = 't'; break;
+ case '\f': *res++ = 'f'; break;
+ case '\v': *res++ = 'v'; break;
+ case '\\': *res++ = '\\'; break;
+ case VK_ESCAPE: *res++ = 'e'; break;
+ default:
+ *res++ = 'x';
+ if ((c >> 4) < 10) {
+ *res++ = (c >> 4) + '0';
+ } else {
+ *res++ = (c >> 4) + 'A' - 10;
+ }
+ if ((c & 0xf) < 10) {
+ *res++ = (c & 0xf) + '0';
+ } else {
+ *res++ = (c & 0xf) + 'A' - 10;
+ }
+ }
+ } else {
+ *res++ = c;
+ }
+ }
+}
diff --git a/Zend/zend_smart_str.h b/Zend/zend_smart_str.h
index 7f8b55e9cf..f31d53e019 100644
--- a/Zend/zend_smart_str.h
+++ b/Zend/zend_smart_str.h
@@ -45,6 +45,7 @@ BEGIN_EXTERN_C()
ZEND_API void ZEND_FASTCALL smart_str_erealloc(smart_str *str, size_t len);
ZEND_API void ZEND_FASTCALL smart_str_realloc(smart_str *str, size_t len);
+ZEND_API void ZEND_FASTCALL smart_str_append_escaped(smart_str *str, const char *s, size_t l);
END_EXTERN_C()
diff --git a/Zend/zend_sprintf.c b/Zend/zend_sprintf.c
index 2620a8ed41..37d876d423 100644
--- a/Zend/zend_sprintf.c
+++ b/Zend/zend_sprintf.c
@@ -30,13 +30,14 @@
#if ZEND_BROKEN_SPRINTF
int zend_sprintf(char *buffer, const char *format, ...)
{
+ int len;
va_list args;
va_start(args, format);
- vsprintf(buffer, format, args);
+ len = vsprintf(buffer, format, args);
va_end(args);
- return strlen(buffer);
+ return len;
}
#endif
diff --git a/Zend/zend_string.c b/Zend/zend_string.c
index 03d77e1293..7b64dd6256 100644
--- a/Zend/zend_string.c
+++ b/Zend/zend_string.c
@@ -42,6 +42,42 @@ static void _str_dtor(zval *zv)
}
#endif
+/* Readonly, so assigned also per thread. */
+static const zend_string **known_interned_strings = NULL;
+static uint32_t known_interned_strings_count = 0;
+
+ZEND_API uint32_t zend_intern_known_strings(const char **strings, uint32_t count)
+{
+ uint32_t i, old_count = known_interned_strings_count;
+
+ known_interned_strings = perealloc(known_interned_strings, sizeof(char*) * (old_count + count), 1);
+ for (i = 0; i < count; i++) {
+#ifndef ZTS
+ zend_string *str = zend_string_init(strings[i], strlen(strings[i]), 1);
+ known_interned_strings[known_interned_strings_count + i] =
+ zend_new_interned_string_int(str);
+#else
+ known_interned_strings[known_interned_strings_count + i] =
+ zend_zts_interned_string_init(strings[i], strlen(strings[i]));
+#endif
+ }
+ known_interned_strings_count = old_count + count;
+ return old_count;
+}
+
+static const char *known_strings[] = {
+#define _ZEND_STR_DSC(id, str) str,
+ZEND_KNOWN_STRINGS(_ZEND_STR_DSC)
+#undef _ZEND_STR_DSC
+ NULL
+};
+
+void zend_known_interned_strings_init(zend_string ***strings, uint32_t *count)
+{
+ *strings = (zend_string **)known_interned_strings;
+ *count = known_interned_strings_count;
+}
+
void zend_interned_strings_init(void)
{
#ifndef ZTS
@@ -63,6 +99,10 @@ void zend_interned_strings_init(void)
/* one char strings (the actual interned strings are going to be created by ext/opcache) */
memset(CG(one_char_string), 0, sizeof(CG(one_char_string)));
+ /* known strings */
+ zend_intern_known_strings(known_strings, (sizeof(known_strings) / sizeof(known_strings[0])) - 1);
+ zend_known_interned_strings_init(&CG(known_strings), &CG(known_strings_count));
+
zend_new_interned_string = zend_new_interned_string_int;
zend_interned_strings_snapshot = zend_interned_strings_snapshot_int;
zend_interned_strings_restore = zend_interned_strings_restore_int;
@@ -72,7 +112,18 @@ void zend_interned_strings_dtor(void)
{
#ifndef ZTS
zend_hash_destroy(&CG(interned_strings));
+#else
+ uint32_t i;
+
+ for (i = 0; i < CG(known_strings_count); i++) {
+ zend_zts_interned_string_free(&CG(known_strings)[i]);
+ }
#endif
+ free(CG(known_strings));
+ CG(known_strings) = NULL;
+ CG(known_strings_count) = 0;
+ known_interned_strings = NULL;
+ known_interned_strings_count = 0;
}
static zend_string *zend_new_interned_string_int(zend_string *str)
diff --git a/Zend/zend_string.h b/Zend/zend_string.h
index 722fc3ae46..8a1ac6f506 100644
--- a/Zend/zend_string.h
+++ b/Zend/zend_string.h
@@ -32,6 +32,7 @@ ZEND_API extern void (*zend_interned_strings_restore)(void);
ZEND_API zend_ulong zend_hash_func(const char *str, size_t len);
void zend_interned_strings_init(void);
void zend_interned_strings_dtor(void);
+void zend_known_interned_strings_init(zend_string ***, uint32_t *);
END_EXTERN_C()
@@ -323,7 +324,7 @@ static zend_always_inline zend_bool zend_string_equals(zend_string *s1, zend_str
static zend_always_inline zend_ulong zend_inline_hash_func(const char *str, size_t len)
{
- register zend_ulong hash = Z_UL(5381);
+ zend_ulong hash = Z_UL(5381);
/* variant with the hash unrolled eight times */
for (; len >= 8; len -= 8) {
@@ -358,29 +359,67 @@ EMPTY_SWITCH_DEFAULT_CASE()
#endif
}
-static zend_always_inline void zend_interned_empty_string_init(zend_string **s)
+#ifdef ZTS
+static zend_always_inline zend_string* zend_zts_interned_string_init(const char *val, size_t len)
{
zend_string *str;
- str = zend_string_alloc(sizeof("")-1, 1);
- ZSTR_VAL(str)[0] = '\000';
+ str = zend_string_init(val, len, 1);
-#ifndef ZTS
- *s = zend_new_interned_string(str);
-#else
zend_string_hash_val(str);
GC_FLAGS(str) |= IS_STR_INTERNED;
- *s = str;
-#endif
+ return str;
}
-static zend_always_inline void zend_interned_empty_string_free(zend_string **s)
+static zend_always_inline void zend_zts_interned_string_free(zend_string **s)
{
if (NULL != *s) {
free(*s);
*s = NULL;
}
}
+#endif
+
+#define ZEND_KNOWN_STRINGS(_) \
+ _(ZEND_STR_FILE, "file") \
+ _(ZEND_STR_LINE, "line") \
+ _(ZEND_STR_FUNCTION, "function") \
+ _(ZEND_STR_CLASS, "class") \
+ _(ZEND_STR_OBJECT, "object") \
+ _(ZEND_STR_TYPE, "type") \
+ _(ZEND_STR_OBJECT_OPERATOR, "->") \
+ _(ZEND_STR_PAAMAYIM_NEKUDOTAYIM, "::") \
+ _(ZEND_STR_ARGS, "args") \
+ _(ZEND_STR_UNKNOWN, "unknown") \
+ _(ZEND_STR_EVAL, "eval") \
+ _(ZEND_STR_INCLUDE, "include") \
+ _(ZEND_STR_REQUIRE, "require") \
+ _(ZEND_STR_INCLUDE_ONCE, "include_once") \
+ _(ZEND_STR_REQUIRE_ONCE, "require_once") \
+ _(ZEND_STR_SCALAR, "scalar") \
+ _(ZEND_STR_ERROR_REPORTING, "error_reporting") \
+ _(ZEND_STR_STATIC, "static") \
+ _(ZEND_STR_THIS, "this") \
+ _(ZEND_STR_VALUE, "value") \
+ _(ZEND_STR_KEY, "key") \
+ _(ZEND_STR_MAGIC_AUTOLOAD, "__autoload") \
+ _(ZEND_STR_MAGIC_INVOKE, "__invoke") \
+ _(ZEND_STR_PREVIOUS, "previous") \
+ _(ZEND_STR_CODE, "code") \
+ _(ZEND_STR_MESSAGE, "message") \
+ _(ZEND_STR_SEVERITY, "severity") \
+ _(ZEND_STR_STRING, "string") \
+ _(ZEND_STR_TRACE, "trace") \
+
+
+typedef enum _zend_known_string_id {
+#define _ZEND_STR_ID(id, str) id,
+ZEND_KNOWN_STRINGS(_ZEND_STR_ID)
+#undef _ZEND_STR_ID
+ ZEND_STR_LAST_KNOWN
+} zend_known_string_id;
+
+ZEND_API uint32_t zend_intern_known_strings(const char **strings, uint32_t count);
#endif /* ZEND_STRING_H */
diff --git a/Zend/zend_strtod.c b/Zend/zend_strtod.c
index 889a31685c..a524e64aa1 100644
--- a/Zend/zend_strtod.c
+++ b/Zend/zend_strtod.c
@@ -4416,13 +4416,6 @@ ZEND_API double zend_hex_strtod(const char *str, const char **endptr)
int any = 0;
double value = 0;
- if (s[0] == '\0' || s[1] == '\0') {
- if (endptr != NULL) {
- *endptr = str;
- }
- return 0.0;
- }
-
if (*s == '0' && (s[1] == 'x' || s[1] == 'X')) {
s += 2;
}
@@ -4491,13 +4484,6 @@ ZEND_API double zend_bin_strtod(const char *str, const char **endptr)
double value = 0;
int any = 0;
- if (str[0] == '\0' || str[1] == '\0') {
- if (endptr != NULL) {
- *endptr = str;
- }
- return 0.0;
- }
-
if ('0' == *s && ('b' == s[1] || 'B' == s[1])) {
s += 2;
}
diff --git a/Zend/zend_type_info.h b/Zend/zend_type_info.h
new file mode 100644
index 0000000000..bd3c3713d9
--- /dev/null
+++ b/Zend/zend_type_info.h
@@ -0,0 +1,68 @@
+/*
+ +----------------------------------------------------------------------+
+ | Zend Engine |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1998-2016 Zend Technologies Ltd. (http://www.zend.com) |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 2.00 of the Zend 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.zend.com/license/2_00.txt. |
+ | If you did not receive a copy of the Zend license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@zend.com so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Dmitry Stogov <dmitry@zend.com> |
+ +----------------------------------------------------------------------+
+*/
+
+/* $Id$ */
+
+#ifndef ZEND_TYPE_INFO_H
+#define ZEND_TYPE_INFO_H
+
+#include "zend_types.h"
+
+#define MAY_BE_UNDEF (1 << IS_UNDEF)
+#define MAY_BE_NULL (1 << IS_NULL)
+#define MAY_BE_FALSE (1 << IS_FALSE)
+#define MAY_BE_TRUE (1 << IS_TRUE)
+#define MAY_BE_LONG (1 << IS_LONG)
+#define MAY_BE_DOUBLE (1 << IS_DOUBLE)
+#define MAY_BE_STRING (1 << IS_STRING)
+#define MAY_BE_ARRAY (1 << IS_ARRAY)
+#define MAY_BE_OBJECT (1 << IS_OBJECT)
+#define MAY_BE_RESOURCE (1 << IS_RESOURCE)
+#define MAY_BE_ANY (MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG|MAY_BE_DOUBLE|MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)
+#define MAY_BE_REF (1 << IS_REFERENCE) /* may be reference */
+
+#define MAY_BE_ARRAY_SHIFT (IS_REFERENCE)
+
+#define MAY_BE_ARRAY_OF_NULL (MAY_BE_NULL << MAY_BE_ARRAY_SHIFT)
+#define MAY_BE_ARRAY_OF_FALSE (MAY_BE_FALSE << MAY_BE_ARRAY_SHIFT)
+#define MAY_BE_ARRAY_OF_TRUE (MAY_BE_TRUE << MAY_BE_ARRAY_SHIFT)
+#define MAY_BE_ARRAY_OF_LONG (MAY_BE_LONG << MAY_BE_ARRAY_SHIFT)
+#define MAY_BE_ARRAY_OF_DOUBLE (MAY_BE_DOUBLE << MAY_BE_ARRAY_SHIFT)
+#define MAY_BE_ARRAY_OF_STRING (MAY_BE_STRING << MAY_BE_ARRAY_SHIFT)
+#define MAY_BE_ARRAY_OF_ARRAY (MAY_BE_ARRAY << MAY_BE_ARRAY_SHIFT)
+#define MAY_BE_ARRAY_OF_OBJECT (MAY_BE_OBJECT << MAY_BE_ARRAY_SHIFT)
+#define MAY_BE_ARRAY_OF_RESOURCE (MAY_BE_RESOURCE << MAY_BE_ARRAY_SHIFT)
+#define MAY_BE_ARRAY_OF_ANY (MAY_BE_ANY << MAY_BE_ARRAY_SHIFT)
+#define MAY_BE_ARRAY_OF_REF (MAY_BE_REF << MAY_BE_ARRAY_SHIFT)
+
+#define MAY_BE_ARRAY_KEY_LONG (1<<21)
+#define MAY_BE_ARRAY_KEY_STRING (1<<22)
+#define MAY_BE_ARRAY_KEY_ANY (MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_KEY_STRING)
+
+#define MAY_BE_ERROR (1<<23)
+#define MAY_BE_CLASS (1<<24)
+
+#endif /* ZEND_TYPE_INFO_H */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * indent-tabs-mode: t
+ * End:
+ */
diff --git a/Zend/zend_types.h b/Zend/zend_types.h
index e141f8a544..45c2ebdf70 100644
--- a/Zend/zend_types.h
+++ b/Zend/zend_types.h
@@ -131,13 +131,14 @@ struct _zval_struct {
uint32_t type_info;
} u1;
union {
- uint32_t var_flags;
uint32_t next; /* hash collision chain */
uint32_t cache_slot; /* literal cache slot */
uint32_t lineno; /* line number (for ast nodes) */
uint32_t num_args; /* arguments number for EX(This) */
uint32_t fe_pos; /* foreach position */
uint32_t fe_iter_idx; /* foreach iterator index */
+ uint32_t access_flags; /* class constant access flags */
+ uint32_t property_guard; /* single property guard */
} u2;
};
@@ -274,7 +275,7 @@ typedef struct _HashTableIterator {
struct _zend_object {
zend_refcounted_h gc;
- uint32_t handle;
+ uint32_t handle; // TODO: may be removed ???
zend_class_entry *ce;
const zend_object_handlers *handlers;
HashTable *properties;
@@ -283,7 +284,7 @@ struct _zend_object {
struct _zend_resource {
zend_refcounted_h gc;
- int handle;
+ int handle; // TODO: may be removed ???
int type;
void *ptr;
};
@@ -318,10 +319,12 @@ struct _zend_ast_ref {
/* fake types */
#define _IS_BOOL 13
#define IS_CALLABLE 14
+#define IS_VOID 18
/* internal types */
#define IS_INDIRECT 15
#define IS_PTR 17
+#define _IS_ERROR 19
static zend_always_inline zend_uchar zval_get_type(const zval* pz) {
return pz->u1.v.type;
@@ -342,9 +345,6 @@ static zend_always_inline zend_uchar zval_get_type(const zval* pz) {
#define Z_CONST_FLAGS(zval) (zval).u1.v.const_flags
#define Z_CONST_FLAGS_P(zval_p) Z_CONST_FLAGS(*(zval_p))
-#define Z_VAR_FLAGS(zval) (zval).u2.var_flags
-#define Z_VAR_FLAGS_P(zval_p) Z_VAR_FLAGS(*(zval_p))
-
#define Z_TYPE_INFO(zval) (zval).u1.type_info
#define Z_TYPE_INFO_P(zval_p) Z_TYPE_INFO(*(zval_p))
@@ -360,6 +360,9 @@ static zend_always_inline zend_uchar zval_get_type(const zval* pz) {
#define Z_FE_ITER(zval) (zval).u2.fe_iter_idx
#define Z_FE_ITER_P(zval_p) Z_FE_ITER(*(zval_p))
+#define Z_ACCESS_FLAGS(zval) (zval).u2.access_flags
+#define Z_ACCESS_FLAGS_P(zval_p) Z_ACCESS_FLAGS(*(zval_p))
+
#define Z_COUNTED(zval) (zval).value.counted
#define Z_COUNTED_P(zval_p) Z_COUNTED(*(zval_p))
@@ -392,7 +395,6 @@ static zend_always_inline zend_uchar zval_get_type(const zval* pz) {
#define IS_TYPE_REFCOUNTED (1<<2)
#define IS_TYPE_COLLECTABLE (1<<3)
#define IS_TYPE_COPYABLE (1<<4)
-#define IS_TYPE_SYMBOLTABLE (1<<5)
/* extended types */
#define IS_INTERNED_STRING_EX IS_STRING
@@ -408,13 +410,13 @@ static zend_always_inline zend_uchar zval_get_type(const zval* pz) {
/* zval.u1.v.const_flags */
#define IS_CONSTANT_UNQUALIFIED 0x010
-#define IS_LEXICAL_VAR 0x020
-#define IS_LEXICAL_REF 0x040
+#define IS_CONSTANT_VISITED_MARK 0x020
#define IS_CONSTANT_CLASS 0x080 /* __CLASS__ in trait */
#define IS_CONSTANT_IN_NAMESPACE 0x100 /* used only in opline->extended_value */
-/* zval.u2.var_flags */
-#define IS_VAR_RET_REF (1<<0) /* return by by reference */
+#define IS_CONSTANT_VISITED(p) (Z_CONST_FLAGS_P(p) & IS_CONSTANT_VISITED_MARK)
+#define MARK_CONSTANT_VISITED(p) Z_CONST_FLAGS_P(p) |= IS_CONSTANT_VISITED_MARK
+#define RESET_CONSTANT_VISITED(p) Z_CONST_FLAGS_P(p) &= ~IS_CONSTANT_VISITED_MARK
/* string flags (zval.value->gc.u.flags) */
#define IS_STR_PERSISTENT (1<<0) /* allocated using malloc */
@@ -469,9 +471,6 @@ static zend_always_inline zend_uchar zval_get_type(const zval* pz) {
#define Z_IMMUTABLE(zval) ((Z_TYPE_FLAGS(zval) & IS_TYPE_IMMUTABLE) != 0)
#define Z_IMMUTABLE_P(zval_p) Z_IMMUTABLE(*(zval_p))
-#define Z_SYMBOLTABLE(zval) ((Z_TYPE_FLAGS(zval) & IS_TYPE_SYMBOLTABLE) != 0)
-#define Z_SYMBOLTABLE_P(zval_p) Z_SYMBOLTABLE(*(zval_p))
-
/* the following Z_OPT_* macros make better code when Z_TYPE_INFO accessed before */
#define Z_OPT_TYPE(zval) (Z_TYPE_INFO(zval) & Z_TYPE_MASK)
#define Z_OPT_TYPE_P(zval_p) Z_OPT_TYPE(*(zval_p))
@@ -503,6 +502,9 @@ static zend_always_inline zend_uchar zval_get_type(const zval* pz) {
#define Z_ISNULL(zval) (Z_TYPE(zval) == IS_NULL)
#define Z_ISNULL_P(zval_p) Z_ISNULL(*(zval_p))
+#define Z_ISERROR(zval) (Z_TYPE(zval) == _IS_ERROR)
+#define Z_ISERROR_P(zval_p) Z_ISERROR(*(zval_p))
+
#define Z_LVAL(zval) (zval).value.lval
#define Z_LVAL_P(zval_p) Z_LVAL(*(zval_p))
@@ -782,6 +784,10 @@ static zend_always_inline zend_uchar zval_get_type(const zval* pz) {
Z_TYPE_INFO_P(z) = IS_PTR; \
} while (0)
+#define ZVAL_ERROR(z) do { \
+ Z_TYPE_INFO_P(z) = _IS_ERROR; \
+ } while (0)
+
#define Z_REFCOUNT_P(pz) zval_refcount_p(pz)
#define Z_SET_REFCOUNT_P(pz, rc) zval_set_refcount_p(pz, rc)
#define Z_ADDREF_P(pz) zval_addref_p(pz)
@@ -808,7 +814,7 @@ static zend_always_inline zend_uchar zval_get_type(const zval* pz) {
#define Z_TRY_DELREF(z) Z_TRY_DELREF_P(&(z))
static zend_always_inline uint32_t zval_refcount_p(zval* pz) {
- ZEND_ASSERT(Z_REFCOUNTED_P(pz) || Z_IMMUTABLE_P(pz) || Z_SYMBOLTABLE_P(pz));
+ ZEND_ASSERT(Z_REFCOUNTED_P(pz) || Z_IMMUTABLE_P(pz));
return GC_REFCOUNT(Z_COUNTED_P(pz));
}
@@ -922,7 +928,7 @@ static zend_always_inline uint32_t zval_delref_p(zval* pz) {
#define SEPARATE_ARRAY(zv) do { \
zval *_zv = (zv); \
zend_array *_arr = Z_ARR_P(_zv); \
- if (GC_REFCOUNT(_arr) > 1) { \
+ if (UNEXPECTED(GC_REFCOUNT(_arr) > 1)) { \
if (!Z_IMMUTABLE_P(_zv)) { \
GC_REFCOUNT(_arr)--; \
} \
diff --git a/Zend/zend_variables.c b/Zend/zend_variables.c
index 98bad6878f..dc75353e62 100644
--- a/Zend/zend_variables.c
+++ b/Zend/zend_variables.c
@@ -35,64 +35,11 @@ ZEND_API void ZEND_FASTCALL _zval_dtor_func(zend_refcounted *p ZEND_FILE_LINE_DC
case IS_CONSTANT: {
zend_string *str = (zend_string*)p;
CHECK_ZVAL_STRING_REL(str);
- zend_string_release(str);
- break;
- }
- case IS_ARRAY: {
- zend_array *arr = (zend_array*)p;
- ZEND_ASSERT(GC_REFCOUNT(arr) <= 1);
- zend_array_destroy(arr);
- break;
- }
- case IS_CONSTANT_AST: {
- zend_ast_ref *ast = (zend_ast_ref*)p;
-
- zend_ast_destroy_and_free(ast->ast);
- efree_size(ast, sizeof(zend_ast_ref));
- break;
- }
- case IS_OBJECT: {
- zend_object *obj = (zend_object*)p;
-
- OBJ_RELEASE(obj);
- break;
- }
- case IS_RESOURCE: {
- zend_resource *res = (zend_resource*)p;
-
- if (--GC_REFCOUNT(res) == 0) {
- /* destroy resource */
- zend_list_free(res);
- }
- break;
- }
- case IS_REFERENCE: {
- zend_reference *ref = (zend_reference*)p;
- if (--GC_REFCOUNT(ref) == 0) {
-
- i_zval_ptr_dtor(&ref->val ZEND_FILE_LINE_RELAY_CC);
- efree_size(ref, sizeof(zend_reference));
- }
- break;
- }
- default:
- break;
- }
-}
-
-ZEND_API void ZEND_FASTCALL _zval_dtor_func_for_ptr(zend_refcounted *p ZEND_FILE_LINE_DC)
-{
- switch (GC_TYPE(p)) {
- case IS_STRING:
- case IS_CONSTANT: {
- zend_string *str = (zend_string*)p;
- CHECK_ZVAL_STRING_REL(str);
zend_string_free(str);
break;
}
case IS_ARRAY: {
zend_array *arr = (zend_array*)p;
-
zend_array_destroy(arr);
break;
}
@@ -225,12 +172,8 @@ ZEND_API void ZEND_FASTCALL _zval_copy_ctor_func(zval *zvalue ZEND_FILE_LINE_DC)
CHECK_ZVAL_STRING_REL(Z_STR_P(zvalue));
Z_STR_P(zvalue) = zend_string_dup(Z_STR_P(zvalue), 0);
} else if (EXPECTED(Z_TYPE_P(zvalue) == IS_CONSTANT_AST)) {
- zend_ast_ref *ast = emalloc(sizeof(zend_ast_ref));
-
- GC_REFCOUNT(ast) = 1;
- GC_TYPE_INFO(ast) = IS_CONSTANT_AST;
- ast->ast = zend_ast_copy(Z_ASTVAL_P(zvalue));
- Z_AST_P(zvalue) = ast;
+ zend_ast *copy = zend_ast_copy(Z_ASTVAL_P(zvalue));
+ ZVAL_NEW_AST(zvalue, copy);
}
}
@@ -267,59 +210,6 @@ ZEND_API void _zval_internal_ptr_dtor_wrapper(zval *zval_ptr)
}
#endif
-ZEND_API int zval_copy_static_var(zval *p, int num_args, va_list args, zend_hash_key *key) /* {{{ */
-{
- zend_array *symbol_table;
- HashTable *target = va_arg(args, HashTable*);
- zend_bool is_ref;
- zval tmp;
-
- if (Z_CONST_FLAGS_P(p) & (IS_LEXICAL_VAR|IS_LEXICAL_REF)) {
- is_ref = Z_CONST_FLAGS_P(p) & IS_LEXICAL_REF;
-
- symbol_table = zend_rebuild_symbol_table();
- p = zend_hash_find(symbol_table, key->key);
- if (!p) {
- p = &tmp;
- ZVAL_NULL(&tmp);
- if (is_ref) {
- ZVAL_NEW_REF(&tmp, &tmp);
- zend_hash_add_new(symbol_table, key->key, &tmp);
- Z_ADDREF_P(p);
- } else {
- zend_error(E_NOTICE,"Undefined variable: %s", ZSTR_VAL(key->key));
- }
- } else {
- if (Z_TYPE_P(p) == IS_INDIRECT) {
- p = Z_INDIRECT_P(p);
- if (Z_TYPE_P(p) == IS_UNDEF) {
- if (!is_ref) {
- zend_error(E_NOTICE,"Undefined variable: %s", ZSTR_VAL(key->key));
- p = &tmp;
- ZVAL_NULL(&tmp);
- } else {
- ZVAL_NULL(p);
- }
- }
- }
- if (is_ref) {
- ZVAL_MAKE_REF(p);
- Z_ADDREF_P(p);
- } else if (Z_ISREF_P(p)) {
- ZVAL_DUP(&tmp, Z_REFVAL_P(p));
- p = &tmp;
- } else if (Z_REFCOUNTED_P(p)) {
- Z_ADDREF_P(p);
- }
- }
- } else if (Z_REFCOUNTED_P(p)) {
- Z_ADDREF_P(p);
- }
- zend_hash_add(target, key->key, p);
- return ZEND_HASH_APPLY_KEEP;
-}
-/* }}} */
-
/*
* Local variables:
* tab-width: 4
diff --git a/Zend/zend_variables.h b/Zend/zend_variables.h
index bbcc4d175d..0afe68760d 100644
--- a/Zend/zend_variables.h
+++ b/Zend/zend_variables.h
@@ -29,25 +29,15 @@
BEGIN_EXTERN_C()
ZEND_API void ZEND_FASTCALL _zval_dtor_func(zend_refcounted *p ZEND_FILE_LINE_DC);
-ZEND_API void ZEND_FASTCALL _zval_dtor_func_for_ptr(zend_refcounted *p ZEND_FILE_LINE_DC);
ZEND_API void ZEND_FASTCALL _zval_copy_ctor_func(zval *zvalue ZEND_FILE_LINE_DC);
#define zval_dtor_func(zv) _zval_dtor_func(zv ZEND_FILE_LINE_CC)
-#define zval_dtor_func_for_ptr(zv) _zval_dtor_func_for_ptr(zv ZEND_FILE_LINE_CC)
#define zval_copy_ctor_func(zv) _zval_copy_ctor_func(zv ZEND_FILE_LINE_CC)
-static zend_always_inline void _zval_dtor(zval *zvalue ZEND_FILE_LINE_DC)
-{
- if (!Z_REFCOUNTED_P(zvalue)) {
- return;
- }
- _zval_dtor_func(Z_COUNTED_P(zvalue) ZEND_FILE_LINE_RELAY_CC);
-}
-
static zend_always_inline void _zval_ptr_dtor_nogc(zval *zval_ptr ZEND_FILE_LINE_DC)
{
if (Z_REFCOUNTED_P(zval_ptr) && !Z_DELREF_P(zval_ptr)) {
- _zval_dtor_func_for_ptr(Z_COUNTED_P(zval_ptr) ZEND_FILE_LINE_RELAY_CC);
+ _zval_dtor_func(Z_COUNTED_P(zval_ptr) ZEND_FILE_LINE_RELAY_CC);
}
}
@@ -55,7 +45,7 @@ static zend_always_inline void i_zval_ptr_dtor(zval *zval_ptr ZEND_FILE_LINE_DC)
{
if (Z_REFCOUNTED_P(zval_ptr)) {
if (!Z_DELREF_P(zval_ptr)) {
- _zval_dtor_func_for_ptr(Z_COUNTED_P(zval_ptr) ZEND_FILE_LINE_RELAY_CC);
+ _zval_dtor_func(Z_COUNTED_P(zval_ptr) ZEND_FILE_LINE_RELAY_CC);
} else {
GC_ZVAL_CHECK_POSSIBLE_ROOT(zval_ptr);
}
@@ -106,8 +96,6 @@ static zend_always_inline void _zval_opt_copy_ctor_no_imm(zval *zvalue ZEND_FILE
}
}
-ZEND_API int zval_copy_static_var(zval *p, int num_args, va_list args, zend_hash_key *key);
-
ZEND_API size_t zend_print_variable(zval *var);
ZEND_API void _zval_ptr_dtor(zval *zval_ptr ZEND_FILE_LINE_DC);
ZEND_API void _zval_internal_dtor_for_ptr(zval *zvalue ZEND_FILE_LINE_DC);
@@ -118,7 +106,7 @@ ZEND_API void _zval_dtor_wrapper(zval *zvalue);
#define zval_opt_copy_ctor(zvalue) _zval_opt_copy_ctor((zvalue) ZEND_FILE_LINE_CC)
#define zval_copy_ctor_no_imm(zvalue) _zval_copy_ctor_no_imm((zvalue) ZEND_FILE_LINE_CC)
#define zval_opt_copy_ctor_no_imm(zvalue) _zval_opt_copy_ctor_no_imm((zvalue) ZEND_FILE_LINE_CC)
-#define zval_dtor(zvalue) _zval_dtor((zvalue) ZEND_FILE_LINE_CC)
+#define zval_dtor(zvalue) zval_ptr_dtor_nogc(zvalue)
#define zval_ptr_dtor(zval_ptr) _zval_ptr_dtor((zval_ptr) ZEND_FILE_LINE_CC)
#define zval_ptr_dtor_nogc(zval_ptr) _zval_ptr_dtor_nogc((zval_ptr) ZEND_FILE_LINE_CC)
#define zval_internal_dtor(zvalue) _zval_internal_dtor((zvalue) ZEND_FILE_LINE_CC)
diff --git a/Zend/zend_vm.h b/Zend/zend_vm.h
index cae56c1c0b..39cbd8ee9c 100644
--- a/Zend/zend_vm.h
+++ b/Zend/zend_vm.h
@@ -25,6 +25,9 @@ BEGIN_EXTERN_C()
ZEND_API void zend_vm_use_old_executor(void);
ZEND_API void zend_vm_set_opcode_handler(zend_op* opcode);
+ZEND_API void zend_vm_set_opcode_handler_ex(zend_op* opcode, uint32_t op1_info, uint32_t op2_info, uint32_t res_info);
+ZEND_API void zend_serialize_opcode_handler(zend_op *op);
+ZEND_API void zend_deserialize_opcode_handler(zend_op *op);
ZEND_API int zend_vm_call_opcode_handler(zend_execute_data *ex);
END_EXTERN_C()
diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h
index ce7c6faea2..4742a62b58 100644
--- a/Zend/zend_vm_def.h
+++ b/Zend/zend_vm_def.h
@@ -290,7 +290,7 @@ ZEND_VM_HANDLER(8, ZEND_CONCAT, CONST|TMPVAR|CV, CONST|TMPVAR|CV)
!ZSTR_IS_INTERNED(op1_str) && GC_REFCOUNT(op1_str) == 1) {
size_t len = ZSTR_LEN(op1_str);
- str = zend_string_realloc(op1_str, len + ZSTR_LEN(op2_str), 0);
+ str = zend_string_extend(op1_str, len + ZSTR_LEN(op2_str), 0);
memcpy(ZSTR_VAL(str) + len, ZSTR_VAL(op2_str), ZSTR_LEN(op2_str)+1);
ZVAL_NEW_STR(EX_VAR(opline->result.var), str);
break;
@@ -685,7 +685,7 @@ ZEND_VM_HANDLER(13, ZEND_BOOL_NOT, CONST|TMPVAR|CV, ANY)
ZVAL_FALSE(EX_VAR(opline->result.var));
} else if (EXPECTED(Z_TYPE_INFO_P(val) <= IS_TRUE)) {
ZVAL_TRUE(EX_VAR(opline->result.var));
- if (UNEXPECTED(Z_TYPE_INFO_P(val) == IS_UNDEF)) {
+ if (OP1_TYPE == IS_CV && UNEXPECTED(Z_TYPE_INFO_P(val) == IS_UNDEF)) {
SAVE_OPLINE();
GET_OP1_UNDEF_CV(val, BP_VAR_R);
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
@@ -699,7 +699,7 @@ ZEND_VM_HANDLER(13, ZEND_BOOL_NOT, CONST|TMPVAR|CV, ANY)
ZEND_VM_NEXT_OPCODE();
}
-ZEND_VM_HELPER_EX(zend_binary_assign_op_obj_helper, VAR|UNUSED|CV, CONST|TMPVAR|CV, binary_op_type binary_op)
+ZEND_VM_HELPER(zend_binary_assign_op_obj_helper, VAR|UNUSED|CV, CONST|TMPVAR|CV, binary_op_type binary_op)
{
USE_OPLINE
zend_free_op free_op1, free_op2, free_op_data1;
@@ -711,7 +711,7 @@ ZEND_VM_HELPER_EX(zend_binary_assign_op_obj_helper, VAR|UNUSED|CV, CONST|TMPVAR|
SAVE_OPLINE();
object = GET_OP1_OBJ_ZVAL_PTR_PTR(BP_VAR_RW);
- if (OP1_TYPE == IS_UNUSED && UNEXPECTED(Z_OBJ_P(object) == NULL)) {
+ if (OP1_TYPE == IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
FREE_UNFETCHED_OP((opline+1)->op1_type, (opline+1)->op1.var);
FREE_UNFETCHED_OP2();
@@ -720,13 +720,6 @@ ZEND_VM_HELPER_EX(zend_binary_assign_op_obj_helper, VAR|UNUSED|CV, CONST|TMPVAR|
property = GET_OP2_ZVAL_PTR(BP_VAR_R);
- if (OP1_TYPE == IS_VAR && UNEXPECTED(object == NULL)) {
- zend_throw_error(NULL, "Cannot use string offset as an object");
- FREE_UNFETCHED_OP((opline+1)->op1_type, (opline+1)->op1.var);
- FREE_OP2();
- HANDLE_EXCEPTION();
- }
-
do {
value = get_zval_ptr_r((opline+1)->op1_type, (opline+1)->op1, execute_data, &free_op_data1);
@@ -744,7 +737,7 @@ ZEND_VM_HELPER_EX(zend_binary_assign_op_obj_helper, VAR|UNUSED|CV, CONST|TMPVAR|
/* here we are sure we are dealing with an object */
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)) {
- if (UNEXPECTED(zptr == &EG(error_zval))) {
+ if (UNEXPECTED(Z_ISERROR_P(zptr))) {
if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
ZVAL_NULL(EX_VAR(opline->result.var));
}
@@ -769,70 +762,89 @@ ZEND_VM_HELPER_EX(zend_binary_assign_op_obj_helper, VAR|UNUSED|CV, CONST|TMPVAR|
ZEND_VM_NEXT_OPCODE_EX(1, 2);
}
-ZEND_VM_HELPER_EX(zend_binary_assign_op_dim_helper, VAR|UNUSED|CV, CONST|TMPVAR|UNUSED|CV, binary_op_type binary_op)
+ZEND_VM_HELPER(zend_binary_assign_op_dim_helper, VAR|CV, CONST|TMPVAR|UNUSED|CV, binary_op_type binary_op)
{
USE_OPLINE
zend_free_op free_op1, free_op2, free_op_data1;
- zval *var_ptr, rv;
+ zval *var_ptr;
zval *value, *container, *dim;
SAVE_OPLINE();
- container = GET_OP1_OBJ_ZVAL_PTR_PTR(BP_VAR_RW);
- if (OP1_TYPE == IS_UNUSED && UNEXPECTED(Z_OBJ_P(container) == NULL)) {
- zend_throw_error(NULL, "Using $this when not in object context");
- FREE_UNFETCHED_OP((opline+1)->op1_type, (opline+1)->op1.var);
- FREE_UNFETCHED_OP2();
- HANDLE_EXCEPTION();
- }
- if (OP1_TYPE == IS_VAR && UNEXPECTED(container == NULL)) {
- zend_throw_error(NULL, "Cannot use string offset as an array");
- FREE_UNFETCHED_OP((opline+1)->op1_type, (opline+1)->op1.var);
- FREE_UNFETCHED_OP2();
- HANDLE_EXCEPTION();
- }
+ container = GET_OP1_OBJ_ZVAL_PTR_PTR_UNDEF(BP_VAR_RW);
- dim = GET_OP2_ZVAL_PTR(BP_VAR_R);
+ if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) {
+ZEND_VM_C_LABEL(assign_dim_op_array):
+ SEPARATE_ARRAY(container);
+ZEND_VM_C_LABEL(assign_dim_op_new_array):
+ if (OP2_TYPE == IS_UNUSED) {
+ var_ptr = zend_hash_next_index_insert(Z_ARRVAL_P(container), &EG(uninitialized_zval));
+ if (UNEXPECTED(!var_ptr)) {
+ zend_error(E_WARNING, "Cannot add element to the array as the next element is already occupied");
+ ZEND_VM_C_GOTO(assign_dim_op_ret_null);
+ }
+ } else {
+ dim = GET_OP2_ZVAL_PTR_UNDEF(BP_VAR_R);
- do {
- if (OP1_TYPE == IS_UNUSED || UNEXPECTED(Z_TYPE_P(container) != IS_ARRAY)) {
- if (OP1_TYPE != IS_UNUSED) {
- ZVAL_DEREF(container);
+ if (OP2_TYPE == IS_CONST) {
+ var_ptr = zend_fetch_dimension_address_inner_RW_CONST(Z_ARRVAL_P(container), dim);
+ } else {
+ var_ptr = zend_fetch_dimension_address_inner_RW(Z_ARRVAL_P(container), dim);
}
- if (OP1_TYPE == IS_UNUSED || EXPECTED(Z_TYPE_P(container) == IS_OBJECT)) {
- value = get_zval_ptr_r((opline+1)->op1_type, (opline+1)->op1, execute_data, &free_op_data1);
- zend_binary_assign_op_obj_dim(container, dim, value, UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL, binary_op);
- break;
+ if (UNEXPECTED(!var_ptr)) {
+ ZEND_VM_C_GOTO(assign_dim_op_ret_null);
}
+ ZVAL_DEREF(var_ptr);
+ SEPARATE_ZVAL_NOREF(var_ptr);
}
- zend_fetch_dimension_address_RW(&rv, container, dim, OP2_TYPE);
value = get_zval_ptr_r((opline+1)->op1_type, (opline+1)->op1, execute_data, &free_op_data1);
- ZEND_ASSERT(Z_TYPE(rv) == IS_INDIRECT);
- var_ptr = Z_INDIRECT(rv);
- if (UNEXPECTED(var_ptr == NULL)) {
- zend_throw_error(NULL, "Cannot use assign-op operators with overloaded objects nor string offsets");
- FREE_OP2();
- FREE_OP(free_op_data1);
- FREE_OP1_VAR_PTR();
- HANDLE_EXCEPTION();
- }
+ binary_op(var_ptr, var_ptr, value);
- if (UNEXPECTED(var_ptr == &EG(error_zval))) {
- if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
- ZVAL_NULL(EX_VAR(opline->result.var));
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), var_ptr);
+ }
+ } else {
+ if (EXPECTED(Z_ISREF_P(container))) {
+ container = Z_REFVAL_P(container);
+ if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) {
+ ZEND_VM_C_GOTO(assign_dim_op_array);
}
- } else {
- ZVAL_DEREF(var_ptr);
- SEPARATE_ZVAL_NOREF(var_ptr);
+ } else if (OP1_TYPE == IS_CV && UNEXPECTED(Z_TYPE_INFO_P(container) == IS_UNDEF)) {
+ container = GET_OP1_UNDEF_CV(container, BP_VAR_RW);
+ZEND_VM_C_LABEL(assign_dim_op_convert_to_array):
+ ZVAL_NEW_ARR(container);
+ zend_hash_init(Z_ARRVAL_P(container), 8, NULL, ZVAL_PTR_DTOR, 0);
+ ZEND_VM_C_GOTO(assign_dim_op_new_array);
+ }
- binary_op(var_ptr, var_ptr, value);
+ dim = GET_OP2_ZVAL_PTR(BP_VAR_R);
- if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
- ZVAL_COPY(EX_VAR(opline->result.var), var_ptr);
+ if (EXPECTED(Z_TYPE_P(container) == IS_OBJECT)) {
+ value = get_zval_ptr_r((opline+1)->op1_type, (opline+1)->op1, execute_data, &free_op_data1);
+ zend_binary_assign_op_obj_dim(container, dim, value, UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL, binary_op);
+ } else {
+ if (UNEXPECTED(Z_TYPE_P(container) == IS_STRING)) {
+ if (OP2_TYPE == IS_UNUSED) {
+ zend_throw_error(NULL, "[] operator not supported for strings");
+ } else {
+ zend_check_string_offset(dim, BP_VAR_RW);
+ zend_wrong_string_offset();
+ }
+ } else if (EXPECTED(Z_TYPE_P(container) <= IS_FALSE)) {
+ ZEND_VM_C_GOTO(assign_dim_op_convert_to_array);
+ } else {
+ if (UNEXPECTED(OP1_TYPE != IS_VAR || EXPECTED(!Z_ISERROR_P(container)))) {
+ zend_error(E_WARNING, "Cannot use a scalar value as an array");
+ }
+ZEND_VM_C_LABEL(assign_dim_op_ret_null):
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
}
+ value = get_zval_ptr_r((opline+1)->op1_type, (opline+1)->op1, execute_data, &free_op_data1);
}
- } while (0);
+ }
FREE_OP2();
FREE_OP(free_op_data1);
@@ -840,7 +852,7 @@ ZEND_VM_HELPER_EX(zend_binary_assign_op_dim_helper, VAR|UNUSED|CV, CONST|TMPVAR|
ZEND_VM_NEXT_OPCODE_EX(1, 2);
}
-ZEND_VM_HELPER_EX(zend_binary_assign_op_helper, VAR|CV, CONST|TMPVAR|CV, binary_op_type binary_op)
+ZEND_VM_HELPER(zend_binary_assign_op_helper, VAR|CV, CONST|TMPVAR|CV, binary_op_type binary_op)
{
USE_OPLINE
zend_free_op free_op1, free_op2;
@@ -851,13 +863,7 @@ ZEND_VM_HELPER_EX(zend_binary_assign_op_helper, VAR|CV, CONST|TMPVAR|CV, binary_
value = GET_OP2_ZVAL_PTR(BP_VAR_R);
var_ptr = GET_OP1_ZVAL_PTR_PTR(BP_VAR_RW);
- if (OP1_TYPE == IS_VAR && UNEXPECTED(var_ptr == NULL)) {
- zend_throw_error(NULL, "Cannot use assign-op operators with overloaded objects nor string offsets");
- FREE_OP2();
- HANDLE_EXCEPTION();
- }
-
- if (OP1_TYPE == IS_VAR && UNEXPECTED(var_ptr == &EG(error_zval))) {
+ if (OP1_TYPE == IS_VAR && UNEXPECTED(Z_ISERROR_P(var_ptr))) {
if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
ZVAL_NULL(EX_VAR(opline->result.var));
}
@@ -877,247 +883,235 @@ ZEND_VM_HELPER_EX(zend_binary_assign_op_helper, VAR|CV, CONST|TMPVAR|CV, binary_
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
-ZEND_VM_HANDLER(23, ZEND_ASSIGN_ADD, VAR|UNUSED|CV, CONST|TMPVAR|UNUSED|CV)
+ZEND_VM_HANDLER(23, ZEND_ASSIGN_ADD, VAR|UNUSED|THIS|CV, CONST|TMPVAR|UNUSED|NEXT|CV, DIM_OBJ, SPEC(DIM_OBJ))
{
#if !defined(ZEND_VM_SPEC) || (OP2_TYPE != IS_UNUSED)
USE_OPLINE
# if !defined(ZEND_VM_SPEC) || (OP1_TYPE != IS_UNUSED)
if (EXPECTED(opline->extended_value == 0)) {
- ZEND_VM_DISPATCH_TO_HELPER_EX(zend_binary_assign_op_helper, binary_op, add_function);
+ ZEND_VM_DISPATCH_TO_HELPER(zend_binary_assign_op_helper, binary_op, add_function);
}
-# endif
if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
- ZEND_VM_DISPATCH_TO_HELPER_EX(zend_binary_assign_op_dim_helper, binary_op, add_function);
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_DISPATCH_TO_HELPER_EX(zend_binary_assign_op_obj_helper, binary_op, add_function);
+ ZEND_VM_DISPATCH_TO_HELPER(zend_binary_assign_op_dim_helper, binary_op, add_function);
}
+# endif
+ ZEND_VM_DISPATCH_TO_HELPER(zend_binary_assign_op_obj_helper, binary_op, add_function);
#else
- ZEND_VM_DISPATCH_TO_HELPER_EX(zend_binary_assign_op_dim_helper, binary_op, add_function);
+ ZEND_VM_DISPATCH_TO_HELPER(zend_binary_assign_op_dim_helper, binary_op, add_function);
#endif
}
-ZEND_VM_HANDLER(24, ZEND_ASSIGN_SUB, VAR|UNUSED|CV, CONST|TMPVAR|UNUSED|CV)
+ZEND_VM_HANDLER(24, ZEND_ASSIGN_SUB, VAR|UNUSED|THIS|CV, CONST|TMPVAR|UNUSED|NEXT|CV, DIM_OBJ, SPEC(DIM_OBJ))
{
#if !defined(ZEND_VM_SPEC) || (OP2_TYPE != IS_UNUSED)
USE_OPLINE
# if !defined(ZEND_VM_SPEC) || (OP1_TYPE != IS_UNUSED)
if (EXPECTED(opline->extended_value == 0)) {
- ZEND_VM_DISPATCH_TO_HELPER_EX(zend_binary_assign_op_helper, binary_op, sub_function);
+ ZEND_VM_DISPATCH_TO_HELPER(zend_binary_assign_op_helper, binary_op, sub_function);
}
-# endif
if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
- ZEND_VM_DISPATCH_TO_HELPER_EX(zend_binary_assign_op_dim_helper, binary_op, sub_function);
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_DISPATCH_TO_HELPER_EX(zend_binary_assign_op_obj_helper, binary_op, sub_function);
+ ZEND_VM_DISPATCH_TO_HELPER(zend_binary_assign_op_dim_helper, binary_op, sub_function);
}
+# endif
+ ZEND_VM_DISPATCH_TO_HELPER(zend_binary_assign_op_obj_helper, binary_op, sub_function);
#else
- ZEND_VM_DISPATCH_TO_HELPER_EX(zend_binary_assign_op_dim_helper, binary_op, sub_function);
+ ZEND_VM_DISPATCH_TO_HELPER(zend_binary_assign_op_dim_helper, binary_op, sub_function);
#endif
}
-ZEND_VM_HANDLER(25, ZEND_ASSIGN_MUL, VAR|UNUSED|CV, CONST|TMPVAR|UNUSED|CV)
+ZEND_VM_HANDLER(25, ZEND_ASSIGN_MUL, VAR|UNUSED|THIS|CV, CONST|TMPVAR|UNUSED|NEXT|CV, DIM_OBJ, SPEC(DIM_OBJ))
{
#if !defined(ZEND_VM_SPEC) || (OP2_TYPE != IS_UNUSED)
USE_OPLINE
# if !defined(ZEND_VM_SPEC) || (OP1_TYPE != IS_UNUSED)
if (EXPECTED(opline->extended_value == 0)) {
- ZEND_VM_DISPATCH_TO_HELPER_EX(zend_binary_assign_op_helper, binary_op, mul_function);
+ ZEND_VM_DISPATCH_TO_HELPER(zend_binary_assign_op_helper, binary_op, mul_function);
}
-# endif
if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
- ZEND_VM_DISPATCH_TO_HELPER_EX(zend_binary_assign_op_dim_helper, binary_op, mul_function);
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_DISPATCH_TO_HELPER_EX(zend_binary_assign_op_obj_helper, binary_op, mul_function);
+ ZEND_VM_DISPATCH_TO_HELPER(zend_binary_assign_op_dim_helper, binary_op, mul_function);
}
+# endif
+ ZEND_VM_DISPATCH_TO_HELPER(zend_binary_assign_op_obj_helper, binary_op, mul_function);
#else
- ZEND_VM_DISPATCH_TO_HELPER_EX(zend_binary_assign_op_dim_helper, binary_op, mul_function);
+ ZEND_VM_DISPATCH_TO_HELPER(zend_binary_assign_op_dim_helper, binary_op, mul_function);
#endif
}
-ZEND_VM_HANDLER(26, ZEND_ASSIGN_DIV, VAR|UNUSED|CV, CONST|TMPVAR|UNUSED|CV)
+ZEND_VM_HANDLER(26, ZEND_ASSIGN_DIV, VAR|UNUSED|THIS|CV, CONST|TMPVAR|UNUSED|NEXT|CV, DIM_OBJ, SPEC(DIM_OBJ))
{
#if !defined(ZEND_VM_SPEC) || (OP2_TYPE != IS_UNUSED)
USE_OPLINE
# if !defined(ZEND_VM_SPEC) || (OP1_TYPE != IS_UNUSED)
if (EXPECTED(opline->extended_value == 0)) {
- ZEND_VM_DISPATCH_TO_HELPER_EX(zend_binary_assign_op_helper, binary_op, div_function);
+ ZEND_VM_DISPATCH_TO_HELPER(zend_binary_assign_op_helper, binary_op, div_function);
}
-# endif
if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
- ZEND_VM_DISPATCH_TO_HELPER_EX(zend_binary_assign_op_dim_helper, binary_op, div_function);
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_DISPATCH_TO_HELPER_EX(zend_binary_assign_op_obj_helper, binary_op, div_function);
+ ZEND_VM_DISPATCH_TO_HELPER(zend_binary_assign_op_dim_helper, binary_op, div_function);
}
+# endif
+ ZEND_VM_DISPATCH_TO_HELPER(zend_binary_assign_op_obj_helper, binary_op, div_function);
#else
- ZEND_VM_DISPATCH_TO_HELPER_EX(zend_binary_assign_op_dim_helper, binary_op, div_function);
+ ZEND_VM_DISPATCH_TO_HELPER(zend_binary_assign_op_dim_helper, binary_op, div_function);
#endif
}
-ZEND_VM_HANDLER(27, ZEND_ASSIGN_MOD, VAR|UNUSED|CV, CONST|TMPVAR|UNUSED|CV)
+ZEND_VM_HANDLER(27, ZEND_ASSIGN_MOD, VAR|UNUSED|THIS|CV, CONST|TMPVAR|UNUSED|NEXT|CV, DIM_OBJ, SPEC(DIM_OBJ))
{
#if !defined(ZEND_VM_SPEC) || (OP2_TYPE != IS_UNUSED)
USE_OPLINE
# if !defined(ZEND_VM_SPEC) || (OP1_TYPE != IS_UNUSED)
if (EXPECTED(opline->extended_value == 0)) {
- ZEND_VM_DISPATCH_TO_HELPER_EX(zend_binary_assign_op_helper, binary_op, mod_function);
+ ZEND_VM_DISPATCH_TO_HELPER(zend_binary_assign_op_helper, binary_op, mod_function);
}
-# endif
if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
- ZEND_VM_DISPATCH_TO_HELPER_EX(zend_binary_assign_op_dim_helper, binary_op, mod_function);
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_DISPATCH_TO_HELPER_EX(zend_binary_assign_op_obj_helper, binary_op, mod_function);
+ ZEND_VM_DISPATCH_TO_HELPER(zend_binary_assign_op_dim_helper, binary_op, mod_function);
}
+# endif
+ ZEND_VM_DISPATCH_TO_HELPER(zend_binary_assign_op_obj_helper, binary_op, mod_function);
#else
- ZEND_VM_DISPATCH_TO_HELPER_EX(zend_binary_assign_op_dim_helper, binary_op, mod_function);
+ ZEND_VM_DISPATCH_TO_HELPER(zend_binary_assign_op_dim_helper, binary_op, mod_function);
#endif
}
-ZEND_VM_HANDLER(28, ZEND_ASSIGN_SL, VAR|UNUSED|CV, CONST|TMPVAR|UNUSED|CV)
+ZEND_VM_HANDLER(28, ZEND_ASSIGN_SL, VAR|UNUSED|THIS|CV, CONST|TMPVAR|UNUSED|NEXT|CV, DIM_OBJ, SPEC(DIM_OBJ))
{
#if !defined(ZEND_VM_SPEC) || (OP2_TYPE != IS_UNUSED)
USE_OPLINE
# if !defined(ZEND_VM_SPEC) || (OP1_TYPE != IS_UNUSED)
if (EXPECTED(opline->extended_value == 0)) {
- ZEND_VM_DISPATCH_TO_HELPER_EX(zend_binary_assign_op_helper, binary_op, shift_left_function);
+ ZEND_VM_DISPATCH_TO_HELPER(zend_binary_assign_op_helper, binary_op, shift_left_function);
}
-# endif
if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
- ZEND_VM_DISPATCH_TO_HELPER_EX(zend_binary_assign_op_dim_helper, binary_op, shift_left_function);
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_DISPATCH_TO_HELPER_EX(zend_binary_assign_op_obj_helper, binary_op, shift_left_function);
+ ZEND_VM_DISPATCH_TO_HELPER(zend_binary_assign_op_dim_helper, binary_op, shift_left_function);
}
+# endif
+ ZEND_VM_DISPATCH_TO_HELPER(zend_binary_assign_op_obj_helper, binary_op, shift_left_function);
#else
- ZEND_VM_DISPATCH_TO_HELPER_EX(zend_binary_assign_op_dim_helper, binary_op, shift_left_function);
+ ZEND_VM_DISPATCH_TO_HELPER(zend_binary_assign_op_dim_helper, binary_op, shift_left_function);
#endif
}
-ZEND_VM_HANDLER(29, ZEND_ASSIGN_SR, VAR|UNUSED|CV, CONST|TMPVAR|UNUSED|CV)
+ZEND_VM_HANDLER(29, ZEND_ASSIGN_SR, VAR|UNUSED|THIS|CV, CONST|TMPVAR|UNUSED|NEXT|CV, DIM_OBJ, SPEC(DIM_OBJ))
{
#if !defined(ZEND_VM_SPEC) || (OP2_TYPE != IS_UNUSED)
USE_OPLINE
# if !defined(ZEND_VM_SPEC) || (OP1_TYPE != IS_UNUSED)
if (EXPECTED(opline->extended_value == 0)) {
- ZEND_VM_DISPATCH_TO_HELPER_EX(zend_binary_assign_op_helper, binary_op, shift_right_function);
+ ZEND_VM_DISPATCH_TO_HELPER(zend_binary_assign_op_helper, binary_op, shift_right_function);
}
-# endif
if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
- ZEND_VM_DISPATCH_TO_HELPER_EX(zend_binary_assign_op_dim_helper, binary_op, shift_right_function);
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_DISPATCH_TO_HELPER_EX(zend_binary_assign_op_obj_helper, binary_op, shift_right_function);
+ ZEND_VM_DISPATCH_TO_HELPER(zend_binary_assign_op_dim_helper, binary_op, shift_right_function);
}
+# endif
+ ZEND_VM_DISPATCH_TO_HELPER(zend_binary_assign_op_obj_helper, binary_op, shift_right_function);
#else
- ZEND_VM_DISPATCH_TO_HELPER_EX(zend_binary_assign_op_dim_helper, binary_op, shift_right_function);
+ ZEND_VM_DISPATCH_TO_HELPER(zend_binary_assign_op_dim_helper, binary_op, shift_right_function);
#endif
}
-ZEND_VM_HANDLER(30, ZEND_ASSIGN_CONCAT, VAR|UNUSED|CV, CONST|TMPVAR|UNUSED|CV)
+ZEND_VM_HANDLER(30, ZEND_ASSIGN_CONCAT, VAR|UNUSED|THIS|CV, CONST|TMPVAR|UNUSED|NEXT|CV, DIM_OBJ, SPEC(DIM_OBJ))
{
#if !defined(ZEND_VM_SPEC) || (OP2_TYPE != IS_UNUSED)
USE_OPLINE
# if !defined(ZEND_VM_SPEC) || (OP1_TYPE != IS_UNUSED)
if (EXPECTED(opline->extended_value == 0)) {
- ZEND_VM_DISPATCH_TO_HELPER_EX(zend_binary_assign_op_helper, binary_op, concat_function);
+ ZEND_VM_DISPATCH_TO_HELPER(zend_binary_assign_op_helper, binary_op, concat_function);
}
-# endif
if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
- ZEND_VM_DISPATCH_TO_HELPER_EX(zend_binary_assign_op_dim_helper, binary_op, concat_function);
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_DISPATCH_TO_HELPER_EX(zend_binary_assign_op_obj_helper, binary_op, concat_function);
+ ZEND_VM_DISPATCH_TO_HELPER(zend_binary_assign_op_dim_helper, binary_op, concat_function);
}
+# endif
+ ZEND_VM_DISPATCH_TO_HELPER(zend_binary_assign_op_obj_helper, binary_op, concat_function);
#else
- ZEND_VM_DISPATCH_TO_HELPER_EX(zend_binary_assign_op_dim_helper, binary_op, concat_function);
+ ZEND_VM_DISPATCH_TO_HELPER(zend_binary_assign_op_dim_helper, binary_op, concat_function);
#endif
}
-ZEND_VM_HANDLER(31, ZEND_ASSIGN_BW_OR, VAR|UNUSED|CV, CONST|TMPVAR|UNUSED|CV)
+ZEND_VM_HANDLER(31, ZEND_ASSIGN_BW_OR, VAR|UNUSED|THIS|CV, CONST|TMPVAR|UNUSED|NEXT|CV, DIM_OBJ, SPEC(DIM_OBJ))
{
#if !defined(ZEND_VM_SPEC) || (OP2_TYPE != IS_UNUSED)
USE_OPLINE
# if !defined(ZEND_VM_SPEC) || (OP1_TYPE != IS_UNUSED)
if (EXPECTED(opline->extended_value == 0)) {
- ZEND_VM_DISPATCH_TO_HELPER_EX(zend_binary_assign_op_helper, binary_op, bitwise_or_function);
+ ZEND_VM_DISPATCH_TO_HELPER(zend_binary_assign_op_helper, binary_op, bitwise_or_function);
}
-# endif
if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
- ZEND_VM_DISPATCH_TO_HELPER_EX(zend_binary_assign_op_dim_helper, binary_op, bitwise_or_function);
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_DISPATCH_TO_HELPER_EX(zend_binary_assign_op_obj_helper, binary_op, bitwise_or_function);
+ ZEND_VM_DISPATCH_TO_HELPER(zend_binary_assign_op_dim_helper, binary_op, bitwise_or_function);
}
+# endif
+ ZEND_VM_DISPATCH_TO_HELPER(zend_binary_assign_op_obj_helper, binary_op, bitwise_or_function);
#else
- ZEND_VM_DISPATCH_TO_HELPER_EX(zend_binary_assign_op_dim_helper, binary_op, bitwise_or_function);
+ ZEND_VM_DISPATCH_TO_HELPER(zend_binary_assign_op_dim_helper, binary_op, bitwise_or_function);
#endif
}
-ZEND_VM_HANDLER(32, ZEND_ASSIGN_BW_AND, VAR|UNUSED|CV, CONST|TMPVAR|UNUSED|CV)
+ZEND_VM_HANDLER(32, ZEND_ASSIGN_BW_AND, VAR|UNUSED|THIS|CV, CONST|TMPVAR|UNUSED|NEXT|CV, DIM_OBJ, SPEC(DIM_OBJ))
{
#if !defined(ZEND_VM_SPEC) || (OP2_TYPE != IS_UNUSED)
USE_OPLINE
# if !defined(ZEND_VM_SPEC) || (OP1_TYPE != IS_UNUSED)
if (EXPECTED(opline->extended_value == 0)) {
- ZEND_VM_DISPATCH_TO_HELPER_EX(zend_binary_assign_op_helper, binary_op, bitwise_and_function);
+ ZEND_VM_DISPATCH_TO_HELPER(zend_binary_assign_op_helper, binary_op, bitwise_and_function);
}
-# endif
if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
- ZEND_VM_DISPATCH_TO_HELPER_EX(zend_binary_assign_op_dim_helper, binary_op, bitwise_and_function);
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_DISPATCH_TO_HELPER_EX(zend_binary_assign_op_obj_helper, binary_op, bitwise_and_function);
+ ZEND_VM_DISPATCH_TO_HELPER(zend_binary_assign_op_dim_helper, binary_op, bitwise_and_function);
}
+# endif
+ ZEND_VM_DISPATCH_TO_HELPER(zend_binary_assign_op_obj_helper, binary_op, bitwise_and_function);
#else
- ZEND_VM_DISPATCH_TO_HELPER_EX(zend_binary_assign_op_dim_helper, binary_op, bitwise_and_function);
+ ZEND_VM_DISPATCH_TO_HELPER(zend_binary_assign_op_dim_helper, binary_op, bitwise_and_function);
#endif
}
-ZEND_VM_HANDLER(33, ZEND_ASSIGN_BW_XOR, VAR|UNUSED|CV, CONST|TMPVAR|UNUSED|CV)
+ZEND_VM_HANDLER(33, ZEND_ASSIGN_BW_XOR, VAR|UNUSED|THIS|CV, CONST|TMPVAR|UNUSED|NEXT|CV, DIM_OBJ, SPEC(DIM_OBJ))
{
#if !defined(ZEND_VM_SPEC) || (OP2_TYPE != IS_UNUSED)
USE_OPLINE
# if !defined(ZEND_VM_SPEC) || (OP1_TYPE != IS_UNUSED)
if (EXPECTED(opline->extended_value == 0)) {
- ZEND_VM_DISPATCH_TO_HELPER_EX(zend_binary_assign_op_helper, binary_op, bitwise_xor_function);
+ ZEND_VM_DISPATCH_TO_HELPER(zend_binary_assign_op_helper, binary_op, bitwise_xor_function);
}
-# endif
if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
- ZEND_VM_DISPATCH_TO_HELPER_EX(zend_binary_assign_op_dim_helper, binary_op, bitwise_xor_function);
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_DISPATCH_TO_HELPER_EX(zend_binary_assign_op_obj_helper, binary_op, bitwise_xor_function);
+ ZEND_VM_DISPATCH_TO_HELPER(zend_binary_assign_op_dim_helper, binary_op, bitwise_xor_function);
}
+# endif
+ ZEND_VM_DISPATCH_TO_HELPER(zend_binary_assign_op_obj_helper, binary_op, bitwise_xor_function);
#else
- ZEND_VM_DISPATCH_TO_HELPER_EX(zend_binary_assign_op_dim_helper, binary_op, bitwise_xor_function);
+ ZEND_VM_DISPATCH_TO_HELPER(zend_binary_assign_op_dim_helper, binary_op, bitwise_xor_function);
#endif
}
-ZEND_VM_HANDLER(167, ZEND_ASSIGN_POW, VAR|UNUSED|CV, CONST|TMPVAR|UNUSED|CV)
+ZEND_VM_HANDLER(167, ZEND_ASSIGN_POW, VAR|UNUSED|THIS|CV, CONST|TMPVAR|UNUSED|NEXT|CV, DIM_OBJ, SPEC(DIM_OBJ))
{
#if !defined(ZEND_VM_SPEC) || (OP2_TYPE != IS_UNUSED)
USE_OPLINE
# if !defined(ZEND_VM_SPEC) || (OP1_TYPE != IS_UNUSED)
if (EXPECTED(opline->extended_value == 0)) {
- ZEND_VM_DISPATCH_TO_HELPER_EX(zend_binary_assign_op_helper, binary_op, pow_function);
+ ZEND_VM_DISPATCH_TO_HELPER(zend_binary_assign_op_helper, binary_op, pow_function);
}
-# endif
if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
- ZEND_VM_DISPATCH_TO_HELPER_EX(zend_binary_assign_op_dim_helper, binary_op, pow_function);
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_DISPATCH_TO_HELPER_EX(zend_binary_assign_op_obj_helper, binary_op, pow_function);
+ ZEND_VM_DISPATCH_TO_HELPER(zend_binary_assign_op_dim_helper, binary_op, pow_function);
}
+# endif
+ ZEND_VM_DISPATCH_TO_HELPER(zend_binary_assign_op_obj_helper, binary_op, pow_function);
#else
- ZEND_VM_DISPATCH_TO_HELPER_EX(zend_binary_assign_op_dim_helper, binary_op, pow_function);
+ ZEND_VM_DISPATCH_TO_HELPER(zend_binary_assign_op_dim_helper, binary_op, pow_function);
#endif
}
-ZEND_VM_HELPER_EX(zend_pre_incdec_property_helper, VAR|UNUSED|CV, CONST|TMPVAR|CV, int inc)
+ZEND_VM_HELPER(zend_pre_incdec_property_helper, VAR|UNUSED|CV, CONST|TMPVAR|CV, int inc)
{
USE_OPLINE
zend_free_op free_op1, free_op2;
@@ -1128,7 +1122,7 @@ ZEND_VM_HELPER_EX(zend_pre_incdec_property_helper, VAR|UNUSED|CV, CONST|TMPVAR|C
SAVE_OPLINE();
object = GET_OP1_OBJ_ZVAL_PTR_PTR(BP_VAR_RW);
- if (OP1_TYPE == IS_UNUSED && UNEXPECTED(Z_OBJ_P(object) == NULL)) {
+ if (OP1_TYPE == IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
FREE_UNFETCHED_OP2();
HANDLE_EXCEPTION();
@@ -1136,12 +1130,6 @@ ZEND_VM_HELPER_EX(zend_pre_incdec_property_helper, VAR|UNUSED|CV, CONST|TMPVAR|C
property = GET_OP2_ZVAL_PTR(BP_VAR_R);
- if (OP1_TYPE == IS_VAR && UNEXPECTED(object == NULL)) {
- zend_throw_error(NULL, "Cannot increment/decrement overloaded objects nor string offsets");
- FREE_OP2();
- HANDLE_EXCEPTION();
- }
-
do {
if (OP1_TYPE != IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) != IS_OBJECT)) {
ZVAL_DEREF(object);
@@ -1157,7 +1145,7 @@ ZEND_VM_HELPER_EX(zend_pre_incdec_property_helper, VAR|UNUSED|CV, CONST|TMPVAR|C
/* here we are sure we are dealing with an object */
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)) {
- if (UNEXPECTED(zptr == &EG(error_zval))) {
+ if (UNEXPECTED(Z_ISERROR_P(zptr))) {
if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
ZVAL_NULL(EX_VAR(opline->result.var));
}
@@ -1192,17 +1180,17 @@ ZEND_VM_HELPER_EX(zend_pre_incdec_property_helper, VAR|UNUSED|CV, CONST|TMPVAR|C
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
-ZEND_VM_HANDLER(132, ZEND_PRE_INC_OBJ, VAR|UNUSED|CV, CONST|TMPVAR|CV)
+ZEND_VM_HANDLER(132, ZEND_PRE_INC_OBJ, VAR|UNUSED|THIS|CV, CONST|TMPVAR|CV)
{
- ZEND_VM_DISPATCH_TO_HELPER_EX(zend_pre_incdec_property_helper, inc, 1);
+ ZEND_VM_DISPATCH_TO_HELPER(zend_pre_incdec_property_helper, inc, 1);
}
-ZEND_VM_HANDLER(133, ZEND_PRE_DEC_OBJ, VAR|UNUSED|CV, CONST|TMPVAR|CV)
+ZEND_VM_HANDLER(133, ZEND_PRE_DEC_OBJ, VAR|UNUSED|THIS|CV, CONST|TMPVAR|CV)
{
- ZEND_VM_DISPATCH_TO_HELPER_EX(zend_pre_incdec_property_helper, inc, 0);
+ ZEND_VM_DISPATCH_TO_HELPER(zend_pre_incdec_property_helper, inc, 0);
}
-ZEND_VM_HELPER_EX(zend_post_incdec_property_helper, VAR|UNUSED|CV, CONST|TMPVAR|CV, int inc)
+ZEND_VM_HELPER(zend_post_incdec_property_helper, VAR|UNUSED|CV, CONST|TMPVAR|CV, int inc)
{
USE_OPLINE
zend_free_op free_op1, free_op2;
@@ -1213,7 +1201,7 @@ ZEND_VM_HELPER_EX(zend_post_incdec_property_helper, VAR|UNUSED|CV, CONST|TMPVAR|
SAVE_OPLINE();
object = GET_OP1_OBJ_ZVAL_PTR_PTR(BP_VAR_RW);
- if (OP1_TYPE == IS_UNUSED && UNEXPECTED(Z_OBJ_P(object) == NULL)) {
+ if (OP1_TYPE == IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
FREE_UNFETCHED_OP2();
HANDLE_EXCEPTION();
@@ -1221,12 +1209,6 @@ ZEND_VM_HELPER_EX(zend_post_incdec_property_helper, VAR|UNUSED|CV, CONST|TMPVAR|
property = GET_OP2_ZVAL_PTR(BP_VAR_R);
- if (OP1_TYPE == IS_VAR && UNEXPECTED(object == NULL)) {
- zend_throw_error(NULL, "Cannot increment/decrement overloaded objects nor string offsets");
- FREE_OP2();
- HANDLE_EXCEPTION();
- }
-
do {
if (OP1_TYPE != IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) != IS_OBJECT)) {
ZVAL_DEREF(object);
@@ -1241,7 +1223,7 @@ 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)) {
- if (UNEXPECTED(zptr == &EG(error_zval))) {
+ if (UNEXPECTED(Z_ISERROR_P(zptr))) {
ZVAL_NULL(EX_VAR(opline->result.var));
} else {
if (EXPECTED(Z_TYPE_P(zptr) == IS_LONG)) {
@@ -1272,17 +1254,17 @@ ZEND_VM_HELPER_EX(zend_post_incdec_property_helper, VAR|UNUSED|CV, CONST|TMPVAR|
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
-ZEND_VM_HANDLER(134, ZEND_POST_INC_OBJ, VAR|UNUSED|CV, CONST|TMPVAR|CV)
+ZEND_VM_HANDLER(134, ZEND_POST_INC_OBJ, VAR|UNUSED|THIS|CV, CONST|TMPVAR|CV)
{
- ZEND_VM_DISPATCH_TO_HELPER_EX(zend_post_incdec_property_helper, inc, 1);
+ ZEND_VM_DISPATCH_TO_HELPER(zend_post_incdec_property_helper, inc, 1);
}
-ZEND_VM_HANDLER(135, ZEND_POST_DEC_OBJ, VAR|UNUSED|CV, CONST|TMPVAR|CV)
+ZEND_VM_HANDLER(135, ZEND_POST_DEC_OBJ, VAR|UNUSED|THIS|CV, CONST|TMPVAR|CV)
{
- ZEND_VM_DISPATCH_TO_HELPER_EX(zend_post_incdec_property_helper, inc, 0);
+ ZEND_VM_DISPATCH_TO_HELPER(zend_post_incdec_property_helper, inc, 0);
}
-ZEND_VM_HANDLER(34, ZEND_PRE_INC, VAR|CV, ANY)
+ZEND_VM_HANDLER(34, ZEND_PRE_INC, VAR|CV, ANY, SPEC(RETVAL))
{
USE_OPLINE
zend_free_op free_op1;
@@ -1290,12 +1272,6 @@ ZEND_VM_HANDLER(34, ZEND_PRE_INC, VAR|CV, ANY)
var_ptr = GET_OP1_ZVAL_PTR_PTR_UNDEF(BP_VAR_RW);
- if (OP1_TYPE == IS_VAR && UNEXPECTED(var_ptr == NULL)) {
- SAVE_OPLINE();
- zend_throw_error(NULL, "Cannot increment/decrement overloaded objects nor string offsets");
- HANDLE_EXCEPTION();
- }
-
if (EXPECTED(Z_TYPE_P(var_ptr) == IS_LONG)) {
fast_long_increment_function(var_ptr);
if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
@@ -1304,7 +1280,7 @@ ZEND_VM_HANDLER(34, ZEND_PRE_INC, VAR|CV, ANY)
ZEND_VM_NEXT_OPCODE();
}
- if (OP1_TYPE == IS_VAR && UNEXPECTED(var_ptr == &EG(error_zval))) {
+ if (OP1_TYPE == IS_VAR && UNEXPECTED(Z_ISERROR_P(var_ptr))) {
if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
ZVAL_NULL(EX_VAR(opline->result.var));
}
@@ -1328,7 +1304,7 @@ ZEND_VM_HANDLER(34, ZEND_PRE_INC, VAR|CV, ANY)
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
-ZEND_VM_HANDLER(35, ZEND_PRE_DEC, VAR|CV, ANY)
+ZEND_VM_HANDLER(35, ZEND_PRE_DEC, VAR|CV, ANY, SPEC(RETVAL))
{
USE_OPLINE
zend_free_op free_op1;
@@ -1336,12 +1312,6 @@ ZEND_VM_HANDLER(35, ZEND_PRE_DEC, VAR|CV, ANY)
var_ptr = GET_OP1_ZVAL_PTR_PTR_UNDEF(BP_VAR_RW);
- if (OP1_TYPE == IS_VAR && UNEXPECTED(var_ptr == NULL)) {
- SAVE_OPLINE();
- zend_throw_error(NULL, "Cannot increment/decrement overloaded objects nor string offsets");
- HANDLE_EXCEPTION();
- }
-
if (EXPECTED(Z_TYPE_P(var_ptr) == IS_LONG)) {
fast_long_decrement_function(var_ptr);
if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
@@ -1350,7 +1320,7 @@ ZEND_VM_HANDLER(35, ZEND_PRE_DEC, VAR|CV, ANY)
ZEND_VM_NEXT_OPCODE();
}
- if (OP1_TYPE == IS_VAR && UNEXPECTED(var_ptr == &EG(error_zval))) {
+ if (OP1_TYPE == IS_VAR && UNEXPECTED(Z_ISERROR_P(var_ptr))) {
if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
ZVAL_NULL(EX_VAR(opline->result.var));
}
@@ -1382,19 +1352,13 @@ ZEND_VM_HANDLER(36, ZEND_POST_INC, VAR|CV, ANY)
var_ptr = GET_OP1_ZVAL_PTR_PTR_UNDEF(BP_VAR_RW);
- if (OP1_TYPE == IS_VAR && UNEXPECTED(var_ptr == NULL)) {
- SAVE_OPLINE();
- zend_throw_error(NULL, "Cannot increment/decrement overloaded objects nor string offsets");
- HANDLE_EXCEPTION();
- }
-
if (EXPECTED(Z_TYPE_P(var_ptr) == IS_LONG)) {
ZVAL_COPY_VALUE(EX_VAR(opline->result.var), var_ptr);
fast_long_increment_function(var_ptr);
ZEND_VM_NEXT_OPCODE();
}
- if (OP1_TYPE == IS_VAR && UNEXPECTED(var_ptr == &EG(error_zval))) {
+ if (OP1_TYPE == IS_VAR && UNEXPECTED(Z_ISERROR_P(var_ptr))) {
ZVAL_NULL(EX_VAR(opline->result.var));
ZEND_VM_NEXT_OPCODE();
}
@@ -1421,19 +1385,13 @@ ZEND_VM_HANDLER(37, ZEND_POST_DEC, VAR|CV, ANY)
var_ptr = GET_OP1_ZVAL_PTR_PTR_UNDEF(BP_VAR_RW);
- if (OP1_TYPE == IS_VAR && UNEXPECTED(var_ptr == NULL)) {
- SAVE_OPLINE();
- zend_throw_error(NULL, "Cannot increment/decrement overloaded objects nor string offsets");
- HANDLE_EXCEPTION();
- }
-
if (EXPECTED(Z_TYPE_P(var_ptr) == IS_LONG)) {
ZVAL_COPY_VALUE(EX_VAR(opline->result.var), var_ptr);
fast_long_decrement_function(var_ptr);
ZEND_VM_NEXT_OPCODE();
}
- if (OP1_TYPE == IS_VAR && UNEXPECTED(var_ptr == &EG(error_zval))) {
+ if (OP1_TYPE == IS_VAR && UNEXPECTED(Z_ISERROR_P(var_ptr))) {
ZVAL_NULL(EX_VAR(opline->result.var));
ZEND_VM_NEXT_OPCODE();
}
@@ -1482,7 +1440,7 @@ ZEND_VM_HANDLER(40, ZEND_ECHO, CONST|TMPVAR|CV, ANY)
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
-ZEND_VM_HELPER_EX(zend_fetch_var_address_helper, CONST|TMPVAR|CV, UNUSED|CONST|VAR, int type)
+ZEND_VM_HELPER(zend_fetch_var_address_helper, CONST|TMPVAR|CV, UNUSED, int type)
{
USE_OPLINE
zend_free_op free_op1;
@@ -1506,64 +1464,70 @@ ZEND_VM_HELPER_EX(zend_fetch_var_address_helper, CONST|TMPVAR|CV, UNUSED|CONST|V
name = zval_get_string(varname);
}
- if (OP2_TYPE != IS_UNUSED) {
- zend_class_entry *ce;
-
- if (OP2_TYPE == IS_CONST) {
- if (OP1_TYPE == IS_CONST && EXPECTED((ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)))) != NULL)) {
- retval = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)) + sizeof(void*));
-
- /* check if static properties were destoyed */
- if (UNEXPECTED(CE_STATIC_MEMBERS(ce) == NULL)) {
- zend_throw_error(NULL, "Access to undeclared static property: %s::$%s", ZSTR_VAL(ce->name), ZSTR_VAL(name));
- FREE_OP1();
- HANDLE_EXCEPTION();
- }
+ target_symbol_table = zend_get_target_symbol_table(execute_data, opline->extended_value & ZEND_FETCH_TYPE_MASK);
+ retval = zend_hash_find(target_symbol_table, name);
+ if (retval == NULL) {
+ if (UNEXPECTED(zend_string_equals(name, CG(known_strings)[ZEND_STR_THIS]))) {
+ zval *result;
- ZEND_VM_C_GOTO(fetch_var_return);
- } else if (UNEXPECTED((ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)))) == NULL)) {
- ce = zend_fetch_class_by_name(Z_STR_P(EX_CONSTANT(opline->op2)), EX_CONSTANT(opline->op2) + 1, ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION);
- if (UNEXPECTED(ce == NULL)) {
- if (OP1_TYPE != IS_CONST) {
- zend_string_release(name);
+ZEND_VM_C_LABEL(fetch_this):
+ result = EX_VAR(opline->result.var);
+ switch (type) {
+ case BP_VAR_R:
+ if (EXPECTED(Z_TYPE(EX(This)) == IS_OBJECT)) {
+ ZVAL_OBJ(result, Z_OBJ(EX(This)));
+ Z_ADDREF_P(result);
+ } else {
+ ZVAL_NULL(result);
+ zend_error(E_NOTICE,"Undefined variable: this");
}
- FREE_OP1();
- ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
- }
- CACHE_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)), ce);
- }
- } else {
- ce = Z_CE_P(EX_VAR(opline->op2.var));
- if (OP1_TYPE == IS_CONST &&
- (retval = CACHED_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)), ce)) != NULL) {
-
- /* check if static properties were destoyed */
- if (UNEXPECTED(CE_STATIC_MEMBERS(ce) == NULL)) {
- zend_throw_error(NULL, "Access to undeclared static property: %s::$%s", ZSTR_VAL(ce->name), ZSTR_VAL(name));
- FREE_OP1();
- HANDLE_EXCEPTION();
- }
-
- ZEND_VM_C_GOTO(fetch_var_return);
+ break;
+ case BP_VAR_IS:
+ if (EXPECTED(Z_TYPE(EX(This)) == IS_OBJECT)) {
+ ZVAL_OBJ(result, Z_OBJ(EX(This)));
+ Z_ADDREF_P(result);
+ } else {
+ ZVAL_NULL(result);
+ }
+ break;
+ case BP_VAR_RW:
+ case BP_VAR_W:
+ zend_throw_error(NULL, "Cannot re-assign $this");
+ break;
+ case BP_VAR_UNSET:
+ zend_throw_error(NULL, "Cannot unset $this");
+ break;
+ EMPTY_SWITCH_DEFAULT_CASE()
}
- }
- retval = zend_std_get_static_property(ce, name, 0);
- if (UNEXPECTED(EG(exception))) {
if (OP1_TYPE != IS_CONST) {
zend_string_release(name);
}
- FREE_OP1();
- HANDLE_EXCEPTION();
+ ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
- if (OP1_TYPE == IS_CONST && retval) {
- CACHE_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)), ce, retval);
+ switch (type) {
+ case BP_VAR_R:
+ case BP_VAR_UNSET:
+ zend_error(E_NOTICE,"Undefined variable: %s", ZSTR_VAL(name));
+ /* break missing intentionally */
+ case BP_VAR_IS:
+ retval = &EG(uninitialized_zval);
+ break;
+ case BP_VAR_RW:
+ zend_error(E_NOTICE,"Undefined variable: %s", ZSTR_VAL(name));
+ retval = zend_hash_update(target_symbol_table, name, &EG(uninitialized_zval));
+ break;
+ case BP_VAR_W:
+ retval = zend_hash_add_new(target_symbol_table, name, &EG(uninitialized_zval));
+ break;
+ EMPTY_SWITCH_DEFAULT_CASE()
}
-
- FREE_OP1();
- } else {
- target_symbol_table = zend_get_target_symbol_table(execute_data, opline->extended_value & ZEND_FETCH_TYPE_MASK);
- retval = zend_hash_find(target_symbol_table, name);
- if (retval == NULL) {
+ /* GLOBAL or $$name variable may be an INDIRECT pointer to CV */
+ } else if (Z_TYPE_P(retval) == IS_INDIRECT) {
+ retval = Z_INDIRECT_P(retval);
+ if (Z_TYPE_P(retval) == IS_UNDEF) {
+ if (UNEXPECTED(zend_string_equals(name, CG(known_strings)[ZEND_STR_THIS]))) {
+ ZEND_VM_C_GOTO(fetch_this);
+ }
switch (type) {
case BP_VAR_R:
case BP_VAR_UNSET:
@@ -1574,52 +1538,164 @@ ZEND_VM_HELPER_EX(zend_fetch_var_address_helper, CONST|TMPVAR|CV, UNUSED|CONST|V
break;
case BP_VAR_RW:
zend_error(E_NOTICE,"Undefined variable: %s", ZSTR_VAL(name));
- retval = zend_hash_update(target_symbol_table, name, &EG(uninitialized_zval));
- break;
+ /* break missing intentionally */
case BP_VAR_W:
- retval = zend_hash_add_new(target_symbol_table, name, &EG(uninitialized_zval));
+ ZVAL_NULL(retval);
break;
EMPTY_SWITCH_DEFAULT_CASE()
}
- /* GLOBAL or $$name variable may be an INDIRECT pointer to CV */
- } else if (Z_TYPE_P(retval) == IS_INDIRECT) {
- retval = Z_INDIRECT_P(retval);
- if (Z_TYPE_P(retval) == IS_UNDEF) {
- switch (type) {
- case BP_VAR_R:
- case BP_VAR_UNSET:
- zend_error(E_NOTICE,"Undefined variable: %s", ZSTR_VAL(name));
- /* break missing intentionally */
- case BP_VAR_IS:
- retval = &EG(uninitialized_zval);
- break;
- case BP_VAR_RW:
- zend_error(E_NOTICE,"Undefined variable: %s", ZSTR_VAL(name));
- /* break missing intentionally */
- case BP_VAR_W:
- ZVAL_NULL(retval);
- break;
- EMPTY_SWITCH_DEFAULT_CASE()
+ }
+ }
+
+ if ((opline->extended_value & ZEND_FETCH_TYPE_MASK) != ZEND_FETCH_GLOBAL_LOCK) {
+ FREE_OP1();
+ }
+
+ if (OP1_TYPE != IS_CONST) {
+ zend_string_release(name);
+ }
+
+ ZEND_ASSERT(retval != NULL);
+ if (type == BP_VAR_R || type == BP_VAR_IS) {
+ if (/*type == BP_VAR_R &&*/ Z_ISREF_P(retval) && Z_REFCOUNT_P(retval) == 1) {
+ ZVAL_UNREF(retval);
+ }
+ ZVAL_COPY(EX_VAR(opline->result.var), retval);
+ } else {
+ ZVAL_INDIRECT(EX_VAR(opline->result.var), retval);
+ }
+ ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
+}
+
+ZEND_VM_HANDLER(80, ZEND_FETCH_R, CONST|TMPVAR|CV, UNUSED, VAR_FETCH)
+{
+ ZEND_VM_DISPATCH_TO_HELPER(zend_fetch_var_address_helper, type, BP_VAR_R);
+}
+
+ZEND_VM_HANDLER(83, ZEND_FETCH_W, CONST|TMPVAR|CV, UNUSED, VAR_FETCH)
+{
+ ZEND_VM_DISPATCH_TO_HELPER(zend_fetch_var_address_helper, type, BP_VAR_W);
+}
+
+ZEND_VM_HANDLER(86, ZEND_FETCH_RW, CONST|TMPVAR|CV, UNUSED, VAR_FETCH)
+{
+ ZEND_VM_DISPATCH_TO_HELPER(zend_fetch_var_address_helper, type, BP_VAR_RW);
+}
+
+ZEND_VM_HANDLER(92, ZEND_FETCH_FUNC_ARG, CONST|TMPVAR|CV, UNUSED, VAR_FETCH|ARG_NUM)
+{
+ USE_OPLINE
+
+ if (zend_is_by_ref_func_arg_fetch(opline, EX(call))) {
+ ZEND_VM_DISPATCH_TO_HELPER(zend_fetch_var_address_helper, type, BP_VAR_W);
+ } else {
+ ZEND_VM_DISPATCH_TO_HELPER(zend_fetch_var_address_helper, type, BP_VAR_R);
+ }
+}
+
+ZEND_VM_HANDLER(95, ZEND_FETCH_UNSET, CONST|TMPVAR|CV, UNUSED, VAR_FETCH)
+{
+ ZEND_VM_DISPATCH_TO_HELPER(zend_fetch_var_address_helper, type, BP_VAR_UNSET);
+}
+
+ZEND_VM_HANDLER(89, ZEND_FETCH_IS, CONST|TMPVAR|CV, UNUSED, VAR_FETCH)
+{
+ ZEND_VM_DISPATCH_TO_HELPER(zend_fetch_var_address_helper, type, BP_VAR_IS);
+}
+
+ZEND_VM_HELPER(zend_fetch_static_prop_helper, CONST|TMPVAR|CV, UNUSED|CONST|VAR, int type)
+{
+ USE_OPLINE
+ zend_free_op free_op1;
+ zval *varname;
+ zval *retval;
+ zend_string *name;
+ zend_class_entry *ce;
+
+ SAVE_OPLINE();
+ varname = GET_OP1_ZVAL_PTR_UNDEF(BP_VAR_R);
+
+ if (OP1_TYPE == IS_CONST) {
+ name = Z_STR_P(varname);
+ } else if (EXPECTED(Z_TYPE_P(varname) == IS_STRING)) {
+ 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);
+ }
+
+ if (OP2_TYPE == IS_CONST) {
+ if (OP1_TYPE == IS_CONST && EXPECTED((ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)))) != NULL)) {
+ retval = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)) + sizeof(void*));
+
+ /* check if static properties were destoyed */
+ if (UNEXPECTED(CE_STATIC_MEMBERS(ce) == NULL)) {
+ zend_throw_error(NULL, "Access to undeclared static property: %s::$%s", ZSTR_VAL(ce->name), ZSTR_VAL(name));
+ FREE_OP1();
+ HANDLE_EXCEPTION();
+ }
+
+ ZEND_VM_C_GOTO(fetch_static_prop_return);
+ } else if (UNEXPECTED((ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)))) == NULL)) {
+ ce = zend_fetch_class_by_name(Z_STR_P(EX_CONSTANT(opline->op2)), EX_CONSTANT(opline->op2) + 1, ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION);
+ if (UNEXPECTED(ce == NULL)) {
+ if (OP1_TYPE != IS_CONST) {
+ zend_string_release(name);
}
+ FREE_OP1();
+ ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
+ CACHE_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)), ce);
}
- if ((opline->extended_value & ZEND_FETCH_TYPE_MASK) == ZEND_FETCH_STATIC) {
- if (Z_CONSTANT_P(retval)) {
- if (UNEXPECTED(zval_update_constant_ex(retval, 1, NULL) != SUCCESS)) {
- FREE_OP1();
- HANDLE_EXCEPTION();
+ } else {
+ if (OP2_TYPE == IS_UNUSED) {
+ ce = zend_fetch_class(NULL, opline->op2.num);
+ if (UNEXPECTED(ce == NULL)) {
+ ZEND_ASSERT(EG(exception));
+ if (OP1_TYPE != IS_CONST) {
+ zend_string_release(name);
}
+ FREE_OP1();
+ HANDLE_EXCEPTION();
}
- } else if ((opline->extended_value & ZEND_FETCH_TYPE_MASK) != ZEND_FETCH_GLOBAL_LOCK) {
- FREE_OP1();
+ } else {
+ ce = Z_CE_P(EX_VAR(opline->op2.var));
+ }
+ if (OP1_TYPE == IS_CONST &&
+ (retval = CACHED_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)), ce)) != NULL) {
+
+ /* check if static properties were destoyed */
+ if (UNEXPECTED(CE_STATIC_MEMBERS(ce) == NULL)) {
+ zend_throw_error(NULL, "Access to undeclared static property: %s::$%s", ZSTR_VAL(ce->name), ZSTR_VAL(name));
+ FREE_OP1();
+ HANDLE_EXCEPTION();
+ }
+
+ ZEND_VM_C_GOTO(fetch_static_prop_return);
}
}
+ retval = zend_std_get_static_property(ce, name, 0);
+ if (UNEXPECTED(EG(exception))) {
+ if (OP1_TYPE != IS_CONST) {
+ zend_string_release(name);
+ }
+ FREE_OP1();
+ HANDLE_EXCEPTION();
+ }
+ if (OP1_TYPE == IS_CONST && retval) {
+ CACHE_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)), ce, retval);
+ }
+
+ FREE_OP1();
if (OP1_TYPE != IS_CONST) {
zend_string_release(name);
}
-ZEND_VM_C_LABEL(fetch_var_return):
+ZEND_VM_C_LABEL(fetch_static_prop_return):
ZEND_ASSERT(retval != NULL);
if (type == BP_VAR_R || type == BP_VAR_IS) {
if (/*type == BP_VAR_R &&*/ Z_ISREF_P(retval) && Z_REFCOUNT_P(retval) == 1) {
@@ -1632,57 +1708,79 @@ ZEND_VM_C_LABEL(fetch_var_return):
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
-ZEND_VM_HANDLER(80, ZEND_FETCH_R, CONST|TMPVAR|CV, UNUSED|CONST|VAR)
+ZEND_VM_HANDLER(173, ZEND_FETCH_STATIC_PROP_R, CONST|TMPVAR|CV, UNUSED|CLASS_FETCH|CONST|VAR)
{
- ZEND_VM_DISPATCH_TO_HELPER_EX(zend_fetch_var_address_helper, type, BP_VAR_R);
+ ZEND_VM_DISPATCH_TO_HELPER(zend_fetch_static_prop_helper, type, BP_VAR_R);
}
-ZEND_VM_HANDLER(83, ZEND_FETCH_W, CONST|TMPVAR|CV, UNUSED|CONST|VAR)
+ZEND_VM_HANDLER(174, ZEND_FETCH_STATIC_PROP_W, CONST|TMPVAR|CV, UNUSED|CLASS_FETCH|CONST|VAR)
{
- ZEND_VM_DISPATCH_TO_HELPER_EX(zend_fetch_var_address_helper, type, BP_VAR_W);
+ ZEND_VM_DISPATCH_TO_HELPER(zend_fetch_static_prop_helper, type, BP_VAR_W);
}
-ZEND_VM_HANDLER(86, ZEND_FETCH_RW, CONST|TMPVAR|CV, UNUSED|CONST|VAR)
+ZEND_VM_HANDLER(175, ZEND_FETCH_STATIC_PROP_RW, CONST|TMPVAR|CV, UNUSED|CLASS_FETCH|CONST|VAR)
{
- ZEND_VM_DISPATCH_TO_HELPER_EX(zend_fetch_var_address_helper, type, BP_VAR_RW);
+ ZEND_VM_DISPATCH_TO_HELPER(zend_fetch_static_prop_helper, type, BP_VAR_RW);
}
-ZEND_VM_HANDLER(92, ZEND_FETCH_FUNC_ARG, CONST|TMPVAR|CV, UNUSED|CONST|VAR)
+ZEND_VM_HANDLER(177, ZEND_FETCH_STATIC_PROP_FUNC_ARG, CONST|TMPVAR|CV, UNUSED|CLASS_FETCH|CONST|VAR, NUM)
{
USE_OPLINE
if (zend_is_by_ref_func_arg_fetch(opline, EX(call))) {
- ZEND_VM_DISPATCH_TO_HELPER_EX(zend_fetch_var_address_helper, type, BP_VAR_W);
+ ZEND_VM_DISPATCH_TO_HELPER(zend_fetch_static_prop_helper, type, BP_VAR_W);
} else {
- ZEND_VM_DISPATCH_TO_HELPER_EX(zend_fetch_var_address_helper, type, BP_VAR_R);
+ ZEND_VM_DISPATCH_TO_HELPER(zend_fetch_static_prop_helper, type, BP_VAR_R);
}
}
-ZEND_VM_HANDLER(95, ZEND_FETCH_UNSET, CONST|TMPVAR|CV, UNUSED|CONST|VAR)
+ZEND_VM_HANDLER(178, ZEND_FETCH_STATIC_PROP_UNSET, CONST|TMPVAR|CV, UNUSED|CLASS_FETCH|CONST|VAR)
{
- ZEND_VM_DISPATCH_TO_HELPER_EX(zend_fetch_var_address_helper, type, BP_VAR_UNSET);
+ ZEND_VM_DISPATCH_TO_HELPER(zend_fetch_static_prop_helper, type, BP_VAR_UNSET);
}
-ZEND_VM_HANDLER(89, ZEND_FETCH_IS, CONST|TMPVAR|CV, UNUSED|CONST|VAR)
+ZEND_VM_HANDLER(176, ZEND_FETCH_STATIC_PROP_IS, CONST|TMPVAR|CV, UNUSED|CLASS_FETCH|CONST|VAR)
{
- ZEND_VM_DISPATCH_TO_HELPER_EX(zend_fetch_var_address_helper, type, BP_VAR_IS);
+ ZEND_VM_DISPATCH_TO_HELPER(zend_fetch_static_prop_helper, type, BP_VAR_IS);
}
ZEND_VM_HANDLER(81, ZEND_FETCH_DIM_R, CONST|TMPVAR|CV, CONST|TMPVAR|CV)
{
USE_OPLINE
zend_free_op free_op1, free_op2;
- zval *container;
+ zval *container, *dim, *value, *result;
SAVE_OPLINE();
- container = GET_OP1_ZVAL_PTR(BP_VAR_R);
- zend_fetch_dimension_address_read_R(EX_VAR(opline->result.var), container, GET_OP2_ZVAL_PTR(BP_VAR_R), OP2_TYPE);
+ container = GET_OP1_ZVAL_PTR_UNDEF(BP_VAR_R);
+ dim = GET_OP2_ZVAL_PTR_UNDEF(BP_VAR_R);
+ if (OP1_TYPE != IS_CONST) {
+ if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) {
+ZEND_VM_C_LABEL(fetch_dim_r_array):
+ value = zend_fetch_dimension_address_inner(Z_ARRVAL_P(container), dim, OP2_TYPE, BP_VAR_R);
+ result = EX_VAR(opline->result.var);
+ ZVAL_COPY(result, value);
+ } else if (EXPECTED(Z_TYPE_P(container) == IS_REFERENCE)) {
+ container = Z_REFVAL_P(container);
+ if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) {
+ ZEND_VM_C_GOTO(fetch_dim_r_array);
+ } else {
+ ZEND_VM_C_GOTO(fetch_dim_r_slow);
+ }
+ } else {
+ZEND_VM_C_LABEL(fetch_dim_r_slow):
+ result = EX_VAR(opline->result.var);
+ zend_fetch_dimension_address_read_R_slow(result, container, dim);
+ }
+ } else {
+ result = EX_VAR(opline->result.var);
+ zend_fetch_dimension_address_read_R(result, container, dim, OP2_TYPE);
+ }
FREE_OP2();
FREE_OP1();
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
-ZEND_VM_HANDLER(84, ZEND_FETCH_DIM_W, VAR|CV, CONST|TMPVAR|UNUSED|CV)
+ZEND_VM_HANDLER(84, ZEND_FETCH_DIM_W, VAR|CV, CONST|TMPVAR|UNUSED|NEXT|CV)
{
USE_OPLINE
zend_free_op free_op1, free_op2;
@@ -1691,36 +1789,28 @@ ZEND_VM_HANDLER(84, ZEND_FETCH_DIM_W, VAR|CV, CONST|TMPVAR|UNUSED|CV)
SAVE_OPLINE();
container = GET_OP1_ZVAL_PTR_PTR_UNDEF(BP_VAR_W);
- if (OP1_TYPE == IS_VAR && UNEXPECTED(container == NULL)) {
- zend_throw_error(NULL, "Cannot use string offset as an array");
- HANDLE_EXCEPTION();
- }
- zend_fetch_dimension_address_W(EX_VAR(opline->result.var), container, GET_OP2_ZVAL_PTR(BP_VAR_R), OP2_TYPE);
+ zend_fetch_dimension_address_W(EX_VAR(opline->result.var), container, GET_OP2_ZVAL_PTR_UNDEF(BP_VAR_R), OP2_TYPE);
FREE_OP2();
if (OP1_TYPE == IS_VAR && READY_TO_DESTROY(free_op1)) {
- EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var), 1);
+ EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var));
}
FREE_OP1_VAR_PTR();
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
-ZEND_VM_HANDLER(87, ZEND_FETCH_DIM_RW, VAR|CV, CONST|TMPVAR|UNUSED|CV)
+ZEND_VM_HANDLER(87, ZEND_FETCH_DIM_RW, VAR|CV, CONST|TMPVAR|UNUSED|NEXT|CV)
{
USE_OPLINE
zend_free_op free_op1, free_op2;
zval *container;
SAVE_OPLINE();
- container = GET_OP1_ZVAL_PTR_PTR(BP_VAR_RW);
+ container = GET_OP1_ZVAL_PTR_PTR_UNDEF(BP_VAR_RW);
- if (OP1_TYPE == IS_VAR && UNEXPECTED(container == NULL)) {
- zend_throw_error(NULL, "Cannot use string offset as an array");
- HANDLE_EXCEPTION();
- }
- zend_fetch_dimension_address_RW(EX_VAR(opline->result.var), container, GET_OP2_ZVAL_PTR(BP_VAR_R), OP2_TYPE);
+ zend_fetch_dimension_address_RW(EX_VAR(opline->result.var), container, GET_OP2_ZVAL_PTR_UNDEF(BP_VAR_R), OP2_TYPE);
FREE_OP2();
if (OP1_TYPE == IS_VAR && READY_TO_DESTROY(free_op1)) {
- EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var), 1);
+ EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var));
}
FREE_OP1_VAR_PTR();
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
@@ -1733,14 +1823,14 @@ ZEND_VM_HANDLER(90, ZEND_FETCH_DIM_IS, CONST|TMPVAR|CV, CONST|TMPVAR|CV)
zval *container;
SAVE_OPLINE();
- container = GET_OP1_ZVAL_PTR(BP_VAR_IS);
- zend_fetch_dimension_address_read_IS(EX_VAR(opline->result.var), container, GET_OP2_ZVAL_PTR(BP_VAR_R), OP2_TYPE);
+ container = GET_OP1_ZVAL_PTR_UNDEF(BP_VAR_IS);
+ zend_fetch_dimension_address_read_IS(EX_VAR(opline->result.var), container, GET_OP2_ZVAL_PTR_UNDEF(BP_VAR_R), OP2_TYPE);
FREE_OP2();
FREE_OP1();
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
-ZEND_VM_HANDLER(93, ZEND_FETCH_DIM_FUNC_ARG, CONST|TMP|VAR|CV, CONST|TMPVAR|UNUSED|CV)
+ZEND_VM_HANDLER(93, ZEND_FETCH_DIM_FUNC_ARG, CONST|TMP|VAR|CV, CONST|TMPVAR|UNUSED|NEXT|CV, NUM)
{
USE_OPLINE
zval *container;
@@ -1749,21 +1839,16 @@ ZEND_VM_HANDLER(93, ZEND_FETCH_DIM_FUNC_ARG, CONST|TMP|VAR|CV, CONST|TMPVAR|UNUS
SAVE_OPLINE();
if (zend_is_by_ref_func_arg_fetch(opline, EX(call))) {
- if (OP1_TYPE == IS_CONST || OP1_TYPE == IS_TMP_VAR) {
+ if ((OP1_TYPE & (IS_CONST|IS_TMP_VAR))) {
zend_throw_error(NULL, "Cannot use temporary expression in write context");
FREE_UNFETCHED_OP2();
FREE_UNFETCHED_OP1();
HANDLE_EXCEPTION();
}
container = GET_OP1_ZVAL_PTR_PTR_UNDEF(BP_VAR_W);
- if (OP1_TYPE == IS_VAR && UNEXPECTED(container == NULL)) {
- zend_throw_error(NULL, "Cannot use string offset as an array");
- FREE_UNFETCHED_OP2();
- HANDLE_EXCEPTION();
- }
- zend_fetch_dimension_address_W(EX_VAR(opline->result.var), container, GET_OP2_ZVAL_PTR(BP_VAR_R), OP2_TYPE);
+ zend_fetch_dimension_address_W(EX_VAR(opline->result.var), container, GET_OP2_ZVAL_PTR_UNDEF(BP_VAR_R), OP2_TYPE);
if (OP1_TYPE == IS_VAR && READY_TO_DESTROY(free_op1)) {
- EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var), 1);
+ EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var));
}
FREE_OP2();
FREE_OP1_VAR_PTR();
@@ -1774,8 +1859,8 @@ ZEND_VM_HANDLER(93, ZEND_FETCH_DIM_FUNC_ARG, CONST|TMP|VAR|CV, CONST|TMPVAR|UNUS
FREE_UNFETCHED_OP1();
HANDLE_EXCEPTION();
}
- container = GET_OP1_ZVAL_PTR(BP_VAR_R);
- zend_fetch_dimension_address_read_R(EX_VAR(opline->result.var), container, GET_OP2_ZVAL_PTR(BP_VAR_R), OP2_TYPE);
+ container = GET_OP1_ZVAL_PTR_UNDEF(BP_VAR_R);
+ zend_fetch_dimension_address_read_R(EX_VAR(opline->result.var), container, GET_OP2_ZVAL_PTR_UNDEF(BP_VAR_R), OP2_TYPE);
FREE_OP2();
FREE_OP1();
}
@@ -1789,23 +1874,18 @@ ZEND_VM_HANDLER(96, ZEND_FETCH_DIM_UNSET, VAR|CV, CONST|TMPVAR|CV)
zval *container;
SAVE_OPLINE();
- container = GET_OP1_ZVAL_PTR_PTR(BP_VAR_UNSET);
+ container = GET_OP1_ZVAL_PTR_PTR_UNDEF(BP_VAR_UNSET);
- if (OP1_TYPE == IS_VAR && UNEXPECTED(container == NULL)) {
- zend_throw_error(NULL, "Cannot use string offset as an array");
- FREE_UNFETCHED_OP2();
- HANDLE_EXCEPTION();
- }
- zend_fetch_dimension_address_UNSET(EX_VAR(opline->result.var), container, GET_OP2_ZVAL_PTR(BP_VAR_R), OP2_TYPE);
+ zend_fetch_dimension_address_UNSET(EX_VAR(opline->result.var), container, GET_OP2_ZVAL_PTR_UNDEF(BP_VAR_R), OP2_TYPE);
FREE_OP2();
if (OP1_TYPE == IS_VAR && READY_TO_DESTROY(free_op1)) {
- EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var), 1);
+ EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var));
}
FREE_OP1_VAR_PTR();
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
-ZEND_VM_HANDLER(82, ZEND_FETCH_OBJ_R, CONST|TMP|VAR|UNUSED|CV, CONST|TMPVAR|CV)
+ZEND_VM_HANDLER(82, ZEND_FETCH_OBJ_R, CONST|TMP|VAR|UNUSED|THIS|CV, CONST|TMPVAR|CV)
{
USE_OPLINE
zend_free_op free_op1;
@@ -1816,7 +1896,7 @@ ZEND_VM_HANDLER(82, ZEND_FETCH_OBJ_R, CONST|TMP|VAR|UNUSED|CV, CONST|TMPVAR|CV)
SAVE_OPLINE();
container = GET_OP1_OBJ_ZVAL_PTR(BP_VAR_R);
- if (OP1_TYPE == IS_UNUSED && UNEXPECTED(Z_OBJ_P(container) == NULL)) {
+ if (OP1_TYPE == IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
FREE_UNFETCHED_OP2();
HANDLE_EXCEPTION();
@@ -1878,7 +1958,7 @@ ZEND_VM_C_LABEL(fetch_obj_r_no_object):
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
-ZEND_VM_HANDLER(85, ZEND_FETCH_OBJ_W, VAR|UNUSED|CV, CONST|TMPVAR|CV)
+ZEND_VM_HANDLER(85, ZEND_FETCH_OBJ_W, VAR|UNUSED|THIS|CV, CONST|TMPVAR|CV)
{
USE_OPLINE
zend_free_op free_op1, free_op2;
@@ -1889,27 +1969,22 @@ ZEND_VM_HANDLER(85, ZEND_FETCH_OBJ_W, VAR|UNUSED|CV, CONST|TMPVAR|CV)
property = GET_OP2_ZVAL_PTR(BP_VAR_R);
container = GET_OP1_OBJ_ZVAL_PTR_PTR_UNDEF(BP_VAR_W);
- if (OP1_TYPE == IS_UNUSED && UNEXPECTED(Z_OBJ_P(container) == NULL)) {
+ if (OP1_TYPE == IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
FREE_OP2();
HANDLE_EXCEPTION();
}
- if (OP1_TYPE == IS_VAR && UNEXPECTED(container == NULL)) {
- zend_throw_error(NULL, "Cannot use string offset as an object");
- FREE_OP2();
- 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);
FREE_OP2();
if (OP1_TYPE == IS_VAR && READY_TO_DESTROY(free_op1)) {
- EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var), 0);
+ EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var));
}
FREE_OP1_VAR_PTR();
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
-ZEND_VM_HANDLER(88, ZEND_FETCH_OBJ_RW, VAR|UNUSED|CV, CONST|TMPVAR|CV)
+ZEND_VM_HANDLER(88, ZEND_FETCH_OBJ_RW, VAR|UNUSED|THIS|CV, CONST|TMPVAR|CV)
{
USE_OPLINE
zend_free_op free_op1, free_op2;
@@ -1920,26 +1995,21 @@ ZEND_VM_HANDLER(88, ZEND_FETCH_OBJ_RW, VAR|UNUSED|CV, CONST|TMPVAR|CV)
property = GET_OP2_ZVAL_PTR(BP_VAR_R);
container = GET_OP1_OBJ_ZVAL_PTR_PTR(BP_VAR_RW);
- if (OP1_TYPE == IS_UNUSED && UNEXPECTED(Z_OBJ_P(container) == NULL)) {
+ if (OP1_TYPE == IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
FREE_OP2();
HANDLE_EXCEPTION();
}
- if (OP1_TYPE == IS_VAR && UNEXPECTED(container == NULL)) {
- zend_throw_error(NULL, "Cannot use string offset as an object");
- FREE_OP2();
- 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);
FREE_OP2();
if (OP1_TYPE == IS_VAR && READY_TO_DESTROY(free_op1)) {
- EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var), 0);
+ EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var));
}
FREE_OP1_VAR_PTR();
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
-ZEND_VM_HANDLER(91, ZEND_FETCH_OBJ_IS, CONST|TMPVAR|UNUSED|CV, CONST|TMPVAR|CV)
+ZEND_VM_HANDLER(91, ZEND_FETCH_OBJ_IS, CONST|TMPVAR|UNUSED|THIS|CV, CONST|TMPVAR|CV)
{
USE_OPLINE
zend_free_op free_op1;
@@ -1950,7 +2020,7 @@ ZEND_VM_HANDLER(91, ZEND_FETCH_OBJ_IS, CONST|TMPVAR|UNUSED|CV, CONST|TMPVAR|CV)
SAVE_OPLINE();
container = GET_OP1_OBJ_ZVAL_PTR(BP_VAR_IS);
- if (OP1_TYPE == IS_UNUSED && UNEXPECTED(Z_OBJ_P(container) == NULL)) {
+ if (OP1_TYPE == IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
FREE_UNFETCHED_OP2();
HANDLE_EXCEPTION();
@@ -2012,7 +2082,7 @@ ZEND_VM_C_LABEL(fetch_obj_is_no_object):
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
-ZEND_VM_HANDLER(94, ZEND_FETCH_OBJ_FUNC_ARG, CONST|TMP|VAR|UNUSED|CV, CONST|TMPVAR|CV)
+ZEND_VM_HANDLER(94, ZEND_FETCH_OBJ_FUNC_ARG, CONST|TMP|VAR|UNUSED|THIS|CV, CONST|TMPVAR|CV, NUM)
{
USE_OPLINE
zval *container;
@@ -2026,26 +2096,21 @@ ZEND_VM_HANDLER(94, ZEND_FETCH_OBJ_FUNC_ARG, CONST|TMP|VAR|UNUSED|CV, CONST|TMPV
property = GET_OP2_ZVAL_PTR(BP_VAR_R);
container = GET_OP1_OBJ_ZVAL_PTR_PTR_UNDEF(BP_VAR_W);
- if (OP1_TYPE == IS_UNUSED && UNEXPECTED(Z_OBJ_P(container) == NULL)) {
+ if (OP1_TYPE == IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
FREE_OP2();
HANDLE_EXCEPTION();
}
- if (OP1_TYPE == IS_CONST || OP1_TYPE == IS_TMP_VAR) {
+ if ((OP1_TYPE & (IS_CONST|IS_TMP_VAR))) {
zend_throw_error(NULL, "Cannot use temporary expression in write context");
FREE_OP2();
FREE_OP1_VAR_PTR();
HANDLE_EXCEPTION();
}
- if (OP1_TYPE == IS_VAR && UNEXPECTED(container == NULL)) {
- zend_throw_error(NULL, "Cannot use string offset as an object");
- FREE_OP2();
- 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);
FREE_OP2();
if (OP1_TYPE == IS_VAR && READY_TO_DESTROY(free_op1)) {
- EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var), 0);
+ EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var));
}
FREE_OP1_VAR_PTR();
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
@@ -2054,7 +2119,7 @@ ZEND_VM_HANDLER(94, ZEND_FETCH_OBJ_FUNC_ARG, CONST|TMP|VAR|UNUSED|CV, CONST|TMPV
}
}
-ZEND_VM_HANDLER(97, ZEND_FETCH_OBJ_UNSET, VAR|UNUSED|CV, CONST|TMPVAR|CV)
+ZEND_VM_HANDLER(97, ZEND_FETCH_OBJ_UNSET, VAR|UNUSED|THIS|CV, CONST|TMPVAR|CV)
{
USE_OPLINE
zend_free_op free_op1, free_op2;
@@ -2063,7 +2128,7 @@ ZEND_VM_HANDLER(97, ZEND_FETCH_OBJ_UNSET, VAR|UNUSED|CV, CONST|TMPVAR|CV)
SAVE_OPLINE();
container = GET_OP1_OBJ_ZVAL_PTR_PTR(BP_VAR_UNSET);
- if (OP1_TYPE == IS_UNUSED && UNEXPECTED(Z_OBJ_P(container) == NULL)) {
+ if (OP1_TYPE == IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
FREE_UNFETCHED_OP2();
HANDLE_EXCEPTION();
@@ -2071,100 +2136,190 @@ ZEND_VM_HANDLER(97, ZEND_FETCH_OBJ_UNSET, VAR|UNUSED|CV, CONST|TMPVAR|CV)
property = GET_OP2_ZVAL_PTR(BP_VAR_R);
- if (OP1_TYPE == IS_VAR && UNEXPECTED(container == NULL)) {
- zend_throw_error(NULL, "Cannot use string offset as an object");
- FREE_OP2();
- 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);
FREE_OP2();
if (OP1_TYPE == IS_VAR && READY_TO_DESTROY(free_op1)) {
- EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var), 0);
+ EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var));
}
FREE_OP1_VAR_PTR();
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
-ZEND_VM_HANDLER(98, ZEND_FETCH_LIST, CONST|TMPVAR|CV, CONST)
+ZEND_VM_HANDLER(98, ZEND_FETCH_LIST, CONST|TMPVAR|CV, CONST|TMPVAR|CV)
{
USE_OPLINE
- zend_free_op free_op1;
+ zend_free_op free_op1, free_op2;
zval *container;
SAVE_OPLINE();
container = GET_OP1_ZVAL_PTR_UNDEF(BP_VAR_R);
-
-ZEND_VM_C_LABEL(try_fetch_list):
- if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) {
- zval *value = zend_hash_index_find(Z_ARRVAL_P(container), Z_LVAL_P(EX_CONSTANT(opline->op2)));
-
- if (UNEXPECTED(value == NULL)) {
- zend_error(E_NOTICE,"Undefined offset: " ZEND_ULONG_FMT, Z_LVAL_P(EX_CONSTANT(opline->op2)));
- ZVAL_NULL(EX_VAR(opline->result.var));
- } else {
- ZVAL_COPY(EX_VAR(opline->result.var), value);
- }
- } 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);
-
- if (retval) {
- if (result != retval) {
- ZVAL_COPY(result, retval);
- }
- } else {
- ZVAL_NULL(result);
- }
- } 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));
- }
+ zend_fetch_dimension_address_read_LIST(EX_VAR(opline->result.var), container, GET_OP2_ZVAL_PTR_UNDEF(BP_VAR_R));
+ FREE_OP2();
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
-ZEND_VM_HANDLER(136, ZEND_ASSIGN_OBJ, VAR|UNUSED|CV, CONST|TMPVAR|CV)
+ZEND_VM_HANDLER(136, ZEND_ASSIGN_OBJ, VAR|UNUSED|THIS|CV, CONST|TMPVAR|CV, SPEC(OP_DATA=CONST|TMP|VAR|CV))
{
USE_OPLINE
- zend_free_op free_op1, free_op2;
- zval *object;
- zval *property_name;
+ zend_free_op free_op1, free_op2, free_op_data;
+ zval *object, *property_name, *value, tmp;
SAVE_OPLINE();
object = GET_OP1_OBJ_ZVAL_PTR_PTR_UNDEF(BP_VAR_W);
- if (OP1_TYPE == IS_UNUSED && UNEXPECTED(Z_OBJ_P(object) == NULL)) {
+ if (OP1_TYPE == IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
FREE_UNFETCHED_OP2();
HANDLE_EXCEPTION();
}
property_name = GET_OP2_ZVAL_PTR(BP_VAR_R);
+ value = GET_OP_DATA_ZVAL_PTR(BP_VAR_R);
- if (OP1_TYPE == IS_VAR && UNEXPECTED(object == NULL)) {
- zend_throw_error(NULL, "Cannot use string offset as an array");
- FREE_OP2();
- HANDLE_EXCEPTION();
+ if (OP1_TYPE != IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) != IS_OBJECT)) {
+ do {
+ if (Z_ISREF_P(object)) {
+ object = Z_REFVAL_P(object);
+ if (EXPECTED(Z_TYPE_P(object) == IS_OBJECT)) {
+ break;
+ }
+ }
+ if (EXPECTED(Z_TYPE_P(object) <= IS_FALSE ||
+ (Z_TYPE_P(object) == IS_STRING && Z_STRLEN_P(object) == 0))) {
+ zend_object *obj;
+
+ zval_ptr_dtor(object);
+ object_init(object);
+ Z_ADDREF_P(object);
+ obj = Z_OBJ_P(object);
+ zend_error(E_WARNING, "Creating default object from empty value");
+ if (GC_REFCOUNT(obj) == 1) {
+ /* the enclosing container was deleted, obj is unreferenced */
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+ FREE_OP_DATA();
+ OBJ_RELEASE(obj);
+ ZEND_VM_C_GOTO(exit_assign_obj);
+ }
+ Z_DELREF_P(object);
+ } else {
+ if (OP1_TYPE != IS_VAR || EXPECTED(!Z_ISERROR_P(object))) {
+ zend_error(E_WARNING, "Attempt to assign property of non-object");
+ }
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+ FREE_OP_DATA();
+ ZEND_VM_C_GOTO(exit_assign_obj);
+ }
+ } while (0);
+ }
+
+ if (OP2_TYPE == IS_CONST &&
+ EXPECTED(Z_OBJCE_P(object) == CACHED_PTR(Z_CACHE_SLOT_P(property_name)))) {
+ uint32_t prop_offset = (uint32_t)(intptr_t)CACHED_PTR(Z_CACHE_SLOT_P(property_name) + sizeof(void*));
+ zend_object *zobj = Z_OBJ_P(object);
+ zval *property;
+
+ if (EXPECTED(prop_offset != (uint32_t)ZEND_DYNAMIC_PROPERTY_OFFSET)) {
+ property = OBJ_PROP(zobj, prop_offset);
+ if (Z_TYPE_P(property) != IS_UNDEF) {
+ZEND_VM_C_LABEL(fast_assign_obj):
+ value = zend_assign_to_variable(property, value, OP_DATA_TYPE);
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+ ZEND_VM_C_GOTO(exit_assign_obj);
+ }
+ } else {
+ if (EXPECTED(zobj->properties != NULL)) {
+ if (UNEXPECTED(GC_REFCOUNT(zobj->properties) > 1)) {
+ if (EXPECTED(!(GC_FLAGS(zobj->properties) & IS_ARRAY_IMMUTABLE))) {
+ GC_REFCOUNT(zobj->properties)--;
+ }
+ zobj->properties = zend_array_dup(zobj->properties);
+ }
+ property = zend_hash_find(zobj->properties, Z_STR_P(property_name));
+ if (property) {
+ ZEND_VM_C_GOTO(fast_assign_obj);
+ }
+ }
+
+ if (!zobj->ce->__set) {
+
+ if (EXPECTED(zobj->properties == NULL)) {
+ rebuild_object_properties(zobj);
+ }
+ if (OP_DATA_TYPE == IS_CONST) {
+ if (UNEXPECTED(Z_OPT_REFCOUNTED_P(value))) {
+ Z_ADDREF_P(value);
+ }
+ } else if (OP_DATA_TYPE != IS_TMP_VAR) {
+ if (Z_ISREF_P(value)) {
+ if (OP_DATA_TYPE == IS_VAR) {
+ zend_reference *ref = Z_REF_P(value);
+ if (--GC_REFCOUNT(ref) == 0) {
+ ZVAL_COPY_VALUE(&tmp, Z_REFVAL_P(value));
+ efree_size(ref, sizeof(zend_reference));
+ value = &tmp;
+ } else {
+ value = Z_REFVAL_P(value);
+ if (Z_REFCOUNTED_P(value)) {
+ Z_ADDREF_P(value);
+ }
+ }
+ } else {
+ value = Z_REFVAL_P(value);
+ if (Z_REFCOUNTED_P(value)) {
+ Z_ADDREF_P(value);
+ }
+ }
+ } else if (OP_DATA_TYPE == IS_CV && Z_REFCOUNTED_P(value)) {
+ Z_ADDREF_P(value);
+ }
+ }
+ zend_hash_add_new(zobj->properties, Z_STR_P(property_name), value);
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+ ZEND_VM_C_GOTO(exit_assign_obj);
+ }
+ }
}
- 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));
+
+ if (!Z_OBJ_HT_P(object)->write_property) {
+ zend_error(E_WARNING, "Attempt to assign property of non-object");
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+ FREE_OP_DATA();
+ ZEND_VM_C_GOTO(exit_assign_obj);
+ }
+
+ if (OP_DATA_TYPE == IS_CV || OP_DATA_TYPE == IS_VAR) {
+ ZVAL_DEREF(value);
+ }
+
+ Z_OBJ_HT_P(object)->write_property(object, property_name, value, (OP2_TYPE == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property_name)) : NULL);
+
+ if (UNEXPECTED(RETURN_VALUE_USED(opline)) && EXPECTED(!EG(exception))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+ FREE_OP_DATA();
+ZEND_VM_C_LABEL(exit_assign_obj):
FREE_OP2();
FREE_OP1_VAR_PTR();
/* assign_obj has two opcodes! */
ZEND_VM_NEXT_OPCODE_EX(1, 2);
}
-ZEND_VM_HANDLER(147, ZEND_ASSIGN_DIM, VAR|CV, CONST|TMPVAR|UNUSED|CV)
+ZEND_VM_HANDLER(147, ZEND_ASSIGN_DIM, VAR|CV, CONST|TMPVAR|UNUSED|NEXT|CV, SPEC(OP_DATA=CONST|TMP|VAR|CV))
{
USE_OPLINE
zend_free_op free_op1;
zval *object_ptr;
- zend_free_op free_op2, free_op_data1;
+ zend_free_op free_op2, free_op_data;
zval *value;
zval *variable_ptr;
zval *dim;
@@ -2172,40 +2327,31 @@ ZEND_VM_HANDLER(147, ZEND_ASSIGN_DIM, VAR|CV, CONST|TMPVAR|UNUSED|CV)
SAVE_OPLINE();
object_ptr = GET_OP1_ZVAL_PTR_PTR_UNDEF(BP_VAR_W);
- if (OP1_TYPE == IS_VAR && UNEXPECTED(object_ptr == NULL)) {
- zend_throw_error(NULL, "Cannot use string offset as an array");
- FREE_UNFETCHED_OP((opline+1)->op1_type, (opline+1)->op1.var);
- FREE_UNFETCHED_OP2();
- HANDLE_EXCEPTION();
- }
-
if (EXPECTED(Z_TYPE_P(object_ptr) == IS_ARRAY)) {
ZEND_VM_C_LABEL(try_assign_dim_array):
+ SEPARATE_ARRAY(object_ptr);
if (OP2_TYPE == IS_UNUSED) {
- SEPARATE_ARRAY(object_ptr);
variable_ptr = zend_hash_next_index_insert(Z_ARRVAL_P(object_ptr), &EG(uninitialized_zval));
if (UNEXPECTED(variable_ptr == NULL)) {
zend_error(E_WARNING, "Cannot add element to the array as the next element is already occupied");
- variable_ptr = &EG(error_zval);
+ ZEND_VM_C_GOTO(assign_dim_error);
}
} else {
- dim = GET_OP2_ZVAL_PTR(BP_VAR_R);
- SEPARATE_ARRAY(object_ptr);
- variable_ptr = zend_fetch_dimension_address_inner(Z_ARRVAL_P(object_ptr), dim, OP2_TYPE, BP_VAR_W);
- FREE_OP2();
- }
- value = get_zval_ptr_r((opline+1)->op1_type, (opline+1)->op1, execute_data, &free_op_data1);
- if (UNEXPECTED(variable_ptr == &EG(error_zval))) {
- FREE_OP(free_op_data1);
- if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
- ZVAL_NULL(EX_VAR(opline->result.var));
+ dim = GET_OP2_ZVAL_PTR_UNDEF(BP_VAR_R);
+ if (OP2_TYPE == IS_CONST) {
+ variable_ptr = zend_fetch_dimension_address_inner_W_CONST(Z_ARRVAL_P(object_ptr), dim);
+ } else {
+ variable_ptr = zend_fetch_dimension_address_inner_W(Z_ARRVAL_P(object_ptr), dim);
}
- } else {
- value = zend_assign_to_variable(variable_ptr, value, (opline+1)->op1_type);
- if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
- ZVAL_COPY(EX_VAR(opline->result.var), value);
+ if (UNEXPECTED(variable_ptr == NULL)) {
+ ZEND_VM_C_GOTO(assign_dim_error);
}
}
+ value = GET_OP_DATA_ZVAL_PTR(BP_VAR_R);
+ value = zend_assign_to_variable(variable_ptr, value, OP_DATA_TYPE);
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
} else {
if (EXPECTED(Z_ISREF_P(object_ptr))) {
object_ptr = Z_REFVAL_P(object_ptr);
@@ -2214,58 +2360,53 @@ ZEND_VM_C_LABEL(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);
+ dim = GET_OP2_ZVAL_PTR(BP_VAR_R);
+ value = GET_OP_DATA_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_throw_error(NULL, "[] 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(object_ptr, dim, value);
- 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_r_deref((opline+1)->op1_type, (opline+1)->op1, execute_data, &free_op_data1);
- 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);
- }
+ if (UNEXPECTED(RETURN_VALUE_USED(opline)) && EXPECTED(!EG(exception))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+
+ FREE_OP_DATA();
+ } else if (EXPECTED(Z_TYPE_P(object_ptr) == IS_STRING)) {
+ if (OP2_TYPE == IS_UNUSED) {
+ zend_throw_error(NULL, "[] operator not supported for strings");
+ FREE_UNFETCHED_OP_DATA();
+ FREE_OP1_VAR_PTR();
+ HANDLE_EXCEPTION();
} 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);
+ dim = GET_OP2_ZVAL_PTR(BP_VAR_R);
+ value = GET_OP_DATA_ZVAL_PTR_DEREF(BP_VAR_R);
+ zend_assign_to_string_offset(object_ptr, dim, value, (UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL));
+ FREE_OP_DATA();
}
} 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);
+ 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 {
- zend_error(E_WARNING, "Cannot use a scalar value as an array");
-ZEND_VM_C_LABEL(assign_dim_clean):
+ if (OP1_TYPE != IS_VAR || EXPECTED(!Z_ISERROR_P(object_ptr))) {
+ zend_error(E_WARNING, "Cannot use a scalar value as an array");
+ }
dim = GET_OP2_ZVAL_PTR(BP_VAR_R);
- FREE_OP2();
- value = get_zval_ptr_r((opline+1)->op1_type, (opline+1)->op1, execute_data, &free_op_data1);
- FREE_OP(free_op_data1);
+ZEND_VM_C_LABEL(assign_dim_error):
+ FREE_UNFETCHED_OP_DATA();
if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
ZVAL_NULL(EX_VAR(opline->result.var));
}
}
}
+ if (OP2_TYPE != IS_UNUSED) {
+ FREE_OP2();
+ }
FREE_OP1_VAR_PTR();
/* assign_dim has two opcodes! */
ZEND_VM_NEXT_OPCODE_EX(1, 2);
}
-ZEND_VM_HANDLER(38, ZEND_ASSIGN, VAR|CV, CONST|TMP|VAR|CV)
+ZEND_VM_HANDLER(38, ZEND_ASSIGN, VAR|CV, CONST|TMP|VAR|CV, SPEC(RETVAL))
{
USE_OPLINE
zend_free_op free_op1, free_op2;
@@ -2276,7 +2417,7 @@ ZEND_VM_HANDLER(38, ZEND_ASSIGN, VAR|CV, CONST|TMP|VAR|CV)
value = GET_OP2_ZVAL_PTR(BP_VAR_R);
variable_ptr = GET_OP1_ZVAL_PTR_PTR_UNDEF(BP_VAR_W);
- if (OP1_TYPE == IS_VAR && UNEXPECTED(variable_ptr == &EG(error_zval))) {
+ if (OP1_TYPE == IS_VAR && UNEXPECTED(Z_ISERROR_P(variable_ptr))) {
FREE_OP2();
if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
ZVAL_NULL(EX_VAR(opline->result.var));
@@ -2293,7 +2434,7 @@ ZEND_VM_HANDLER(38, ZEND_ASSIGN, VAR|CV, CONST|TMP|VAR|CV)
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
-ZEND_VM_HANDLER(39, ZEND_ASSIGN_REF, VAR|CV, VAR|CV)
+ZEND_VM_HANDLER(39, ZEND_ASSIGN_REF, VAR|CV, VAR|CV, SRC)
{
USE_OPLINE
zend_free_op free_op1, free_op2;
@@ -2302,54 +2443,49 @@ ZEND_VM_HANDLER(39, ZEND_ASSIGN_REF, VAR|CV, VAR|CV)
SAVE_OPLINE();
value_ptr = GET_OP2_ZVAL_PTR_PTR(BP_VAR_W);
+ variable_ptr = GET_OP1_ZVAL_PTR_PTR_UNDEF(BP_VAR_W);
- if (OP2_TYPE == IS_VAR && UNEXPECTED(value_ptr == NULL)) {
- zend_throw_error(NULL, "Cannot create references to/from string offsets nor overloaded objects");
- FREE_UNFETCHED_OP1();
- HANDLE_EXCEPTION();
- }
if (OP1_TYPE == IS_VAR &&
UNEXPECTED(Z_TYPE_P(EX_VAR(opline->op1.var)) != IS_INDIRECT) &&
- UNEXPECTED(!Z_ISREF_P(EX_VAR(opline->op1.var)))) {
+ UNEXPECTED(!Z_ISREF_P(EX_VAR(opline->op1.var))) &&
+ UNEXPECTED(!Z_ISERROR_P(EX_VAR(opline->op1.var)))) {
+
zend_throw_error(NULL, "Cannot assign by reference to overloaded object");
FREE_OP2_VAR_PTR();
HANDLE_EXCEPTION();
- }
- if (OP2_TYPE == IS_VAR &&
- (value_ptr == &EG(uninitialized_zval) ||
- (opline->extended_value == ZEND_RETURNS_FUNCTION &&
- !(Z_VAR_FLAGS_P(value_ptr) & IS_VAR_RET_REF)))) {
- if (!OP2_FREE && UNEXPECTED(Z_TYPE_P(EX_VAR(opline->op2.var)) != IS_INDIRECT)) { /* undo the effect of get_zval_ptr_ptr() */
- Z_TRY_ADDREF_P(value_ptr);
- }
+
+ } else if (OP2_TYPE == IS_VAR &&
+ opline->extended_value == ZEND_RETURNS_FUNCTION &&
+ UNEXPECTED(!Z_ISREF_P(value_ptr))) {
zend_error(E_NOTICE, "Only variables should be assigned by reference");
if (UNEXPECTED(EG(exception) != NULL)) {
FREE_OP2_VAR_PTR();
HANDLE_EXCEPTION();
}
- ZEND_VM_DISPATCH_TO_HANDLER(ZEND_ASSIGN);
- }
- variable_ptr = GET_OP1_ZVAL_PTR_PTR_UNDEF(BP_VAR_W);
- if (OP1_TYPE == IS_VAR && UNEXPECTED(variable_ptr == NULL)) {
- zend_throw_error(NULL, "Cannot create references to/from string offsets nor overloaded objects");
- FREE_OP2_VAR_PTR();
- HANDLE_EXCEPTION();
- }
- if ((OP1_TYPE == IS_VAR && UNEXPECTED(variable_ptr == &EG(error_zval))) ||
- (OP2_TYPE == IS_VAR && UNEXPECTED(value_ptr == &EG(error_zval)))) {
- variable_ptr = &EG(uninitialized_zval);
+ value_ptr = zend_assign_to_variable(variable_ptr, value_ptr, OP2_TYPE);
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value_ptr);
+ }
+ /* zend_assign_to_variable() always takes care of op2, never free it! */
+
} else {
- zend_assign_to_variable_reference(variable_ptr, value_ptr);
- }
- if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
- ZVAL_COPY(EX_VAR(opline->result.var), variable_ptr);
+ if ((OP1_TYPE == IS_VAR && UNEXPECTED(Z_ISERROR_P(variable_ptr))) ||
+ (OP2_TYPE == IS_VAR && UNEXPECTED(Z_ISERROR_P(value_ptr)))) {
+ variable_ptr = &EG(uninitialized_zval);
+ } else {
+ zend_assign_to_variable_reference(variable_ptr, value_ptr);
+ }
+
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), variable_ptr);
+ }
+
+ FREE_OP2_VAR_PTR();
}
FREE_OP1_VAR_PTR();
- FREE_OP2_VAR_PTR();
-
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
@@ -2358,44 +2494,73 @@ ZEND_VM_HELPER(zend_leave_helper, ANY, ANY)
zend_execute_data *old_execute_data;
uint32_t call_info = EX_CALL_INFO();
- if (EXPECTED(ZEND_CALL_KIND_EX(call_info) == ZEND_CALL_NESTED_FUNCTION)) {
- zend_object *object;
+ if (EXPECTED((call_info & (ZEND_CALL_CODE|ZEND_CALL_TOP|ZEND_CALL_HAS_SYMBOL_TABLE|ZEND_CALL_FREE_EXTRA_ARGS|ZEND_CALL_ALLOCATED)) == 0)) {
+ i_free_compiled_variables(execute_data);
+
+ EG(current_execute_data) = EX(prev_execute_data);
+ if (UNEXPECTED(call_info & ZEND_CALL_RELEASE_THIS)) {
+ zend_object *object = Z_OBJ(execute_data->This);
+#if 0
+ if (UNEXPECTED(EG(exception) != NULL) && (EX(opline)->op1.num & ZEND_CALL_CTOR)) {
+#else
+ if (UNEXPECTED(EG(exception) != NULL) && (call_info & ZEND_CALL_CTOR)) {
+#endif
+ GC_REFCOUNT(object)--;
+ if (GC_REFCOUNT(object) == 1) {
+ zend_object_store_ctor_failed(object);
+ }
+ }
+ OBJ_RELEASE(object);
+ } else if (UNEXPECTED(call_info & ZEND_CALL_CLOSURE)) {
+ OBJ_RELEASE((zend_object*)execute_data->func->op_array.prototype);
+ }
+ EG(vm_stack_top) = (zval*)execute_data;
+ execute_data = EX(prev_execute_data);
+
+ if (UNEXPECTED(EG(exception) != NULL)) {
+ const zend_op *old_opline = EX(opline);
+ zend_throw_exception_internal(NULL);
+ if (RETURN_VALUE_USED(old_opline)) {
+ zval_ptr_dtor(EX_VAR(old_opline->result.var));
+ }
+ HANDLE_EXCEPTION_LEAVE();
+ }
+ LOAD_NEXT_OPLINE();
+ ZEND_VM_LEAVE();
+ } else if (EXPECTED((call_info & (ZEND_CALL_CODE|ZEND_CALL_TOP)) == 0)) {
i_free_compiled_variables(execute_data);
- if (UNEXPECTED(EX(symbol_table) != NULL)) {
+
+ if (UNEXPECTED(call_info & ZEND_CALL_HAS_SYMBOL_TABLE)) {
zend_clean_and_cache_symbol_table(EX(symbol_table));
}
- zend_vm_stack_free_extra_args_ex(call_info, execute_data);
- old_execute_data = execute_data;
- execute_data = EG(current_execute_data) = EX(prev_execute_data);
- if (UNEXPECTED(call_info & ZEND_CALL_CLOSURE)) {
- OBJ_RELEASE((zend_object*)old_execute_data->func->op_array.prototype);
- }
+ EG(current_execute_data) = EX(prev_execute_data);
if (UNEXPECTED(call_info & ZEND_CALL_RELEASE_THIS)) {
- object = Z_OBJ(old_execute_data->This);
+ zend_object *object = Z_OBJ(execute_data->This);
#if 0
if (UNEXPECTED(EG(exception) != NULL) && (EX(opline)->op1.num & ZEND_CALL_CTOR)) {
- if (!(EX(opline)->op1.num & ZEND_CALL_CTOR_RESULT_UNUSED)) {
#else
if (UNEXPECTED(EG(exception) != NULL) && (call_info & ZEND_CALL_CTOR)) {
- if (!(call_info & ZEND_CALL_CTOR_RESULT_UNUSED)) {
#endif
- GC_REFCOUNT(object)--;
- }
+ GC_REFCOUNT(object)--;
if (GC_REFCOUNT(object) == 1) {
zend_object_store_ctor_failed(object);
}
}
OBJ_RELEASE(object);
+ } else if (UNEXPECTED(call_info & ZEND_CALL_CLOSURE)) {
+ OBJ_RELEASE((zend_object*)execute_data->func->op_array.prototype);
}
- EG(scope) = EX(func)->op_array.scope;
+ zend_vm_stack_free_extra_args_ex(call_info, execute_data);
+ old_execute_data = execute_data;
+ execute_data = EX(prev_execute_data);
zend_vm_stack_free_call_frame_ex(call_info, old_execute_data);
if (UNEXPECTED(EG(exception) != NULL)) {
const zend_op *old_opline = EX(opline);
zend_throw_exception_internal(NULL);
- if (old_opline->opcode != ZEND_HANDLE_EXCEPTION && RETURN_VALUE_USED(old_opline)) {
+ if (RETURN_VALUE_USED(old_opline)) {
zval_ptr_dtor(EX_VAR(old_opline->result.var));
}
HANDLE_EXCEPTION_LEAVE();
@@ -2403,8 +2568,7 @@ ZEND_VM_HELPER(zend_leave_helper, ANY, ANY)
LOAD_NEXT_OPLINE();
ZEND_VM_LEAVE();
- }
- if (EXPECTED((ZEND_CALL_KIND_EX(call_info) & ZEND_CALL_TOP) == 0)) {
+ } else if (EXPECTED((call_info & ZEND_CALL_TOP) == 0)) {
zend_detach_symbol_table(execute_data);
destroy_op_array(&EX(func)->op_array);
efree_size(EX(func), sizeof(zend_op_array));
@@ -2421,23 +2585,26 @@ ZEND_VM_HELPER(zend_leave_helper, ANY, ANY)
LOAD_NEXT_OPLINE();
ZEND_VM_LEAVE();
} else {
- if (ZEND_CALL_KIND_EX(call_info) == ZEND_CALL_TOP_FUNCTION) {
+ if (EXPECTED((call_info & ZEND_CALL_CODE) == 0)) {
i_free_compiled_variables(execute_data);
- if (UNEXPECTED(EX(symbol_table) != NULL)) {
- zend_clean_and_cache_symbol_table(EX(symbol_table));
+ if (UNEXPECTED(call_info & (ZEND_CALL_HAS_SYMBOL_TABLE|ZEND_CALL_FREE_EXTRA_ARGS))) {
+ if (UNEXPECTED(call_info & ZEND_CALL_HAS_SYMBOL_TABLE)) {
+ zend_clean_and_cache_symbol_table(EX(symbol_table));
+ }
+ zend_vm_stack_free_extra_args_ex(call_info, execute_data);
}
- zend_vm_stack_free_extra_args_ex(call_info, execute_data);
EG(current_execute_data) = EX(prev_execute_data);
if (UNEXPECTED(call_info & ZEND_CALL_CLOSURE)) {
OBJ_RELEASE((zend_object*)EX(func)->op_array.prototype);
}
+ ZEND_VM_RETURN();
} else /* if (call_kind == ZEND_CALL_TOP_CODE) */ {
zend_array *symbol_table = EX(symbol_table);
zend_detach_symbol_table(execute_data);
old_execute_data = EX(prev_execute_data);
while (old_execute_data) {
- if (old_execute_data->func && ZEND_USER_CODE(old_execute_data->func->op_array.type)) {
+ if (old_execute_data->func && (ZEND_CALL_INFO(old_execute_data) & ZEND_CALL_HAS_SYMBOL_TABLE)) {
if (old_execute_data->symbol_table == symbol_table) {
zend_attach_symbol_table(old_execute_data);
}
@@ -2446,13 +2613,12 @@ ZEND_VM_HELPER(zend_leave_helper, ANY, ANY)
old_execute_data = old_execute_data->prev_execute_data;
}
EG(current_execute_data) = EX(prev_execute_data);
+ ZEND_VM_RETURN();
}
-
- ZEND_VM_RETURN();
}
}
-ZEND_VM_HANDLER(42, ZEND_JMP, ANY, ANY)
+ZEND_VM_HANDLER(42, ZEND_JMP, JMP_ADDR, ANY)
{
USE_OPLINE
@@ -2460,7 +2626,7 @@ ZEND_VM_HANDLER(42, ZEND_JMP, ANY, ANY)
ZEND_VM_CONTINUE();
}
-ZEND_VM_HANDLER(43, ZEND_JMPZ, CONST|TMPVAR|CV, ANY)
+ZEND_VM_HANDLER(43, ZEND_JMPZ, CONST|TMPVAR|CV, JMP_ADDR)
{
USE_OPLINE
zend_free_op free_op1;
@@ -2495,7 +2661,7 @@ ZEND_VM_HANDLER(43, ZEND_JMPZ, CONST|TMPVAR|CV, ANY)
ZEND_VM_JMP(opline);
}
-ZEND_VM_HANDLER(44, ZEND_JMPNZ, CONST|TMPVAR|CV, ANY)
+ZEND_VM_HANDLER(44, ZEND_JMPNZ, CONST|TMPVAR|CV, JMP_ADDR)
{
USE_OPLINE
zend_free_op free_op1;
@@ -2529,7 +2695,7 @@ ZEND_VM_HANDLER(44, ZEND_JMPNZ, CONST|TMPVAR|CV, ANY)
ZEND_VM_JMP(opline);
}
-ZEND_VM_HANDLER(45, ZEND_JMPZNZ, CONST|TMPVAR|CV, ANY)
+ZEND_VM_HANDLER(45, ZEND_JMPZNZ, CONST|TMPVAR|CV, JMP_ADDR, JMP_ADDR)
{
USE_OPLINE
zend_free_op free_op1;
@@ -2566,7 +2732,7 @@ ZEND_VM_HANDLER(45, ZEND_JMPZNZ, CONST|TMPVAR|CV, ANY)
ZEND_VM_JMP(opline);
}
-ZEND_VM_HANDLER(46, ZEND_JMPZ_EX, CONST|TMPVAR|CV, ANY)
+ZEND_VM_HANDLER(46, ZEND_JMPZ_EX, CONST|TMPVAR|CV, JMP_ADDR)
{
USE_OPLINE
zend_free_op free_op1;
@@ -2609,7 +2775,7 @@ ZEND_VM_HANDLER(46, ZEND_JMPZ_EX, CONST|TMPVAR|CV, ANY)
ZEND_VM_JMP(opline);
}
-ZEND_VM_HANDLER(47, ZEND_JMPNZ_EX, CONST|TMPVAR|CV, ANY)
+ZEND_VM_HANDLER(47, ZEND_JMPNZ_EX, CONST|TMPVAR|CV, JMP_ADDR)
{
USE_OPLINE
zend_free_op free_op1;
@@ -2649,7 +2815,7 @@ ZEND_VM_HANDLER(47, ZEND_JMPNZ_EX, CONST|TMPVAR|CV, ANY)
ZEND_VM_JMP(opline);
}
-ZEND_VM_HANDLER(70, ZEND_FREE, TMPVAR, ANY)
+ZEND_VM_HANDLER(70, ZEND_FREE, TMPVAR, LIVE_RANGE)
{
USE_OPLINE
@@ -2658,7 +2824,7 @@ ZEND_VM_HANDLER(70, ZEND_FREE, TMPVAR, ANY)
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
-ZEND_VM_HANDLER(127, ZEND_FE_FREE, TMPVAR, ANY)
+ZEND_VM_HANDLER(127, ZEND_FE_FREE, TMPVAR, LIVE_RANGE)
{
zval *var;
USE_OPLINE
@@ -2739,7 +2905,7 @@ ZEND_VM_HANDLER(53, ZEND_FAST_CONCAT, CONST|TMPVAR|CV, CONST|TMPVAR|CV)
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
-ZEND_VM_HANDLER(54, ZEND_ROPE_INIT, UNUSED, CONST|TMPVAR|CV)
+ZEND_VM_HANDLER(54, ZEND_ROPE_INIT, UNUSED, CONST|TMPVAR|CV, NUM)
{
USE_OPLINE
zend_free_op free_op2;
@@ -2772,7 +2938,7 @@ ZEND_VM_HANDLER(54, ZEND_ROPE_INIT, UNUSED, CONST|TMPVAR|CV)
ZEND_VM_NEXT_OPCODE();
}
-ZEND_VM_HANDLER(55, ZEND_ROPE_ADD, TMP, CONST|TMPVAR|CV)
+ZEND_VM_HANDLER(55, ZEND_ROPE_ADD, TMP, CONST|TMPVAR|CV, NUM)
{
USE_OPLINE
zend_free_op free_op2;
@@ -2805,7 +2971,7 @@ ZEND_VM_HANDLER(55, ZEND_ROPE_ADD, TMP, CONST|TMPVAR|CV)
ZEND_VM_NEXT_OPCODE();
}
-ZEND_VM_HANDLER(56, ZEND_ROPE_END, TMP, CONST|TMPVAR|CV)
+ZEND_VM_HANDLER(56, ZEND_ROPE_END, TMP, CONST|TMPVAR|CV, NUM)
{
USE_OPLINE
zend_free_op free_op2;
@@ -2858,7 +3024,7 @@ ZEND_VM_HANDLER(56, ZEND_ROPE_END, TMP, CONST|TMPVAR|CV)
ZEND_VM_NEXT_OPCODE();
}
-ZEND_VM_HANDLER(109, ZEND_FETCH_CLASS, ANY, CONST|TMPVAR|UNUSED|CV)
+ZEND_VM_HANDLER(109, ZEND_FETCH_CLASS, ANY, CONST|TMPVAR|UNUSED|CV, CLASS_FETCH)
{
USE_OPLINE
@@ -2901,7 +3067,7 @@ ZEND_VM_C_LABEL(try_class_name):
}
}
-ZEND_VM_HANDLER(112, ZEND_INIT_METHOD_CALL, CONST|TMPVAR|UNUSED|CV, CONST|TMPVAR|CV)
+ZEND_VM_HANDLER(112, ZEND_INIT_METHOD_CALL, CONST|TMPVAR|UNUSED|THIS|CV, CONST|TMPVAR|CV, NUM)
{
USE_OPLINE
zval *function_name;
@@ -2940,7 +3106,7 @@ ZEND_VM_HANDLER(112, ZEND_INIT_METHOD_CALL, CONST|TMPVAR|UNUSED|CV, CONST|TMPVAR
object = GET_OP1_OBJ_ZVAL_PTR_UNDEF(BP_VAR_R);
- if (OP1_TYPE == IS_UNUSED && UNEXPECTED(Z_OBJ_P(object) == NULL)) {
+ if (OP1_TYPE == IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
FREE_OP2();
HANDLE_EXCEPTION();
@@ -3000,6 +3166,9 @@ ZEND_VM_HANDLER(112, ZEND_INIT_METHOD_CALL, CONST|TMPVAR|UNUSED|CV, CONST|TMPVAR
EXPECTED(obj == orig_obj)) {
CACHE_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(function_name), called_scope, fbc);
}
+ if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!fbc->op_array.run_time_cache)) {
+ init_func_run_time_cache(&fbc->op_array);
+ }
}
call_info = ZEND_CALL_NESTED_FUNCTION;
@@ -3022,7 +3191,7 @@ ZEND_VM_HANDLER(112, ZEND_INIT_METHOD_CALL, CONST|TMPVAR|UNUSED|CV, CONST|TMPVAR
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
-ZEND_VM_HANDLER(113, ZEND_INIT_STATIC_METHOD_CALL, CONST|VAR, CONST|TMPVAR|UNUSED|CV)
+ZEND_VM_HANDLER(113, ZEND_INIT_STATIC_METHOD_CALL, UNUSED|CLASS_FETCH|CONST|VAR, CONST|TMPVAR|UNUSED|CONSTRUCTOR|CV, NUM)
{
USE_OPLINE
zval *function_name;
@@ -3047,6 +3216,13 @@ ZEND_VM_HANDLER(113, ZEND_INIT_STATIC_METHOD_CALL, CONST|VAR, CONST|TMPVAR|UNUSE
}
CACHE_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)), ce);
}
+ } else if (OP1_TYPE == IS_UNUSED) {
+ ce = zend_fetch_class(NULL, opline->op1.num);
+ if (UNEXPECTED(ce == NULL)) {
+ ZEND_ASSERT(EG(exception));
+ FREE_UNFETCHED_OP2();
+ HANDLE_EXCEPTION();
+ }
} else {
ce = Z_CE_P(EX_VAR(opline->op1.var));
}
@@ -3105,6 +3281,9 @@ ZEND_VM_HANDLER(113, ZEND_INIT_STATIC_METHOD_CALL, CONST|VAR, CONST|TMPVAR|UNUSE
CACHE_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(function_name), ce, fbc);
}
}
+ if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!fbc->op_array.run_time_cache)) {
+ init_func_run_time_cache(&fbc->op_array);
+ }
if (OP2_TYPE != IS_CONST) {
FREE_OP2();
}
@@ -3113,16 +3292,19 @@ ZEND_VM_HANDLER(113, ZEND_INIT_STATIC_METHOD_CALL, CONST|VAR, CONST|TMPVAR|UNUSE
zend_throw_error(NULL, "Cannot call constructor");
HANDLE_EXCEPTION();
}
- if (Z_OBJ(EX(This)) && Z_OBJ(EX(This))->ce != ce->constructor->common.scope && (ce->constructor->common.fn_flags & ZEND_ACC_PRIVATE)) {
+ if (Z_TYPE(EX(This)) == IS_OBJECT && Z_OBJ(EX(This))->ce != ce->constructor->common.scope && (ce->constructor->common.fn_flags & ZEND_ACC_PRIVATE)) {
zend_throw_error(NULL, "Cannot call private %s::__construct()", ZSTR_VAL(ce->name));
HANDLE_EXCEPTION();
}
fbc = ce->constructor;
+ if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!fbc->op_array.run_time_cache)) {
+ init_func_run_time_cache(&fbc->op_array);
+ }
}
object = NULL;
if (!(fbc->common.fn_flags & ZEND_ACC_STATIC)) {
- if (Z_OBJ(EX(This)) && instanceof_function(Z_OBJCE(EX(This)), ce)) {
+ if (Z_TYPE(EX(This)) == IS_OBJECT && instanceof_function(Z_OBJCE(EX(This)), ce)) {
object = Z_OBJ(EX(This));
ce = object->ce;
} else {
@@ -3147,11 +3329,15 @@ ZEND_VM_HANDLER(113, ZEND_INIT_STATIC_METHOD_CALL, CONST|VAR, CONST|TMPVAR|UNUSE
}
}
- if (OP1_TYPE != IS_CONST) {
+ if (OP1_TYPE == IS_UNUSED) {
/* previous opcode is ZEND_FETCH_CLASS */
- if (((opline-1)->extended_value & ZEND_FETCH_CLASS_MASK) == ZEND_FETCH_CLASS_PARENT ||
- ((opline-1)->extended_value & ZEND_FETCH_CLASS_MASK) == ZEND_FETCH_CLASS_SELF) {
- ce = EX(called_scope);
+ if ((opline->op1.num & ZEND_FETCH_CLASS_MASK) == ZEND_FETCH_CLASS_PARENT ||
+ (opline->op1.num & ZEND_FETCH_CLASS_MASK) == ZEND_FETCH_CLASS_SELF) {
+ if (Z_TYPE(EX(This)) == IS_OBJECT) {
+ ce = Z_OBJCE(EX(This));
+ } else {
+ ce = Z_CE(EX(This));
+ }
}
}
@@ -3163,24 +3349,27 @@ ZEND_VM_HANDLER(113, ZEND_INIT_STATIC_METHOD_CALL, CONST|VAR, CONST|TMPVAR|UNUSE
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
-ZEND_VM_HANDLER(59, ZEND_INIT_FCALL_BY_NAME, ANY, CONST)
+ZEND_VM_HANDLER(59, ZEND_INIT_FCALL_BY_NAME, ANY, CONST, NUM)
{
USE_OPLINE
zend_function *fbc;
zval *function_name, *func;
zend_execute_data *call;
- fbc = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)));
+ function_name = (zval*)EX_CONSTANT(opline->op2);
+ fbc = CACHED_PTR(Z_CACHE_SLOT_P(function_name));
if (UNEXPECTED(fbc == NULL)) {
- function_name = (zval*)(EX_CONSTANT(opline->op2)+1);
- func = zend_hash_find(EG(function_table), Z_STR_P(function_name));
+ func = zend_hash_find(EG(function_table), Z_STR_P(function_name+1));
if (UNEXPECTED(func == NULL)) {
SAVE_OPLINE();
- zend_throw_error(NULL, "Call to undefined function %s()", Z_STRVAL_P(EX_CONSTANT(opline->op2)));
+ zend_throw_error(NULL, "Call to undefined function %s()", Z_STRVAL_P(function_name));
HANDLE_EXCEPTION();
}
fbc = Z_FUNC_P(func);
- CACHE_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)), fbc);
+ if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!fbc->op_array.run_time_cache)) {
+ init_func_run_time_cache(&fbc->op_array);
+ }
+ CACHE_PTR(Z_CACHE_SLOT_P(function_name), fbc);
}
call = zend_vm_stack_push_call_frame(ZEND_CALL_NESTED_FUNCTION,
fbc, opline->extended_value, NULL, NULL);
@@ -3190,198 +3379,24 @@ ZEND_VM_HANDLER(59, ZEND_INIT_FCALL_BY_NAME, ANY, CONST)
ZEND_VM_NEXT_OPCODE();
}
-ZEND_VM_HANDLER(128, ZEND_INIT_DYNAMIC_CALL, ANY, CONST|TMPVAR|CV)
+ZEND_VM_HANDLER(128, ZEND_INIT_DYNAMIC_CALL, ANY, CONST|TMPVAR|CV, NUM)
{
USE_OPLINE
- zend_function *fbc;
- zval *function_name, *func;
- zend_string *lcname;
zend_free_op free_op2;
- zend_class_entry *called_scope;
- zend_object *object;
+ zval *function_name;
zend_execute_data *call;
- uint32_t call_info = ZEND_CALL_NESTED_FUNCTION;
SAVE_OPLINE();
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)) {
- const char *colon;
-
- if ((colon = zend_memrchr(Z_STRVAL_P(function_name), ':', Z_STRLEN_P(function_name))) != NULL &&
- colon > Z_STRVAL_P(function_name) &&
- *(colon-1) == ':'
- ) {
- zend_string *mname;
- size_t cname_length = colon - Z_STRVAL_P(function_name) - 1;
- size_t mname_length = Z_STRLEN_P(function_name) - cname_length - (sizeof("::") - 1);
-
- lcname = zend_string_init(Z_STRVAL_P(function_name), cname_length, 0);
-
- object = NULL;
- called_scope = zend_fetch_class_by_name(lcname, NULL, ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION);
- if (UNEXPECTED(called_scope == NULL)) {
- zend_string_release(lcname);
- ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
- }
-
- mname = zend_string_init(Z_STRVAL_P(function_name) + (cname_length + sizeof("::") - 1), mname_length, 0);
-
- if (called_scope->get_static_method) {
- fbc = called_scope->get_static_method(called_scope, mname);
- } else {
- fbc = zend_std_get_static_method(called_scope, mname, NULL);
- }
- if (UNEXPECTED(fbc == NULL)) {
- if (EXPECTED(!EG(exception))) {
- zend_throw_error(NULL, "Call to undefined method %s::%s()", ZSTR_VAL(called_scope->name), ZSTR_VAL(mname));
- }
- zend_string_release(lcname);
- zend_string_release(mname);
- FREE_OP2();
- HANDLE_EXCEPTION();
- }
-
- zend_string_release(lcname);
- zend_string_release(mname);
-
- if (!(fbc->common.fn_flags & ZEND_ACC_STATIC)) {
- if (fbc->common.fn_flags & ZEND_ACC_ALLOW_STATIC) {
- zend_error(E_DEPRECATED,
- "Non-static method %s::%s() should not be called statically",
- ZSTR_VAL(fbc->common.scope->name), ZSTR_VAL(fbc->common.function_name));
- if (UNEXPECTED(EG(exception) != NULL)) {
- HANDLE_EXCEPTION();
- }
- } else {
- zend_throw_error(
- zend_ce_error,
- "Non-static method %s::%s() cannot be called statically",
- ZSTR_VAL(fbc->common.scope->name), ZSTR_VAL(fbc->common.function_name));
- FREE_OP2();
- HANDLE_EXCEPTION();
- }
- }
- } else {
- if (Z_STRVAL_P(function_name)[0] == '\\') {
- lcname = zend_string_alloc(Z_STRLEN_P(function_name) - 1, 0);
- zend_str_tolower_copy(ZSTR_VAL(lcname), Z_STRVAL_P(function_name) + 1, Z_STRLEN_P(function_name) - 1);
- } else {
- lcname = zend_string_tolower(Z_STR_P(function_name));
- }
- if (UNEXPECTED((func = zend_hash_find(EG(function_table), lcname)) == NULL)) {
- zend_throw_error(NULL, "Call to undefined function %s()", Z_STRVAL_P(function_name));
- zend_string_release(lcname);
- FREE_OP2();
- HANDLE_EXCEPTION();
- }
- zend_string_release(lcname);
-
- fbc = Z_FUNC_P(func);
- called_scope = NULL;
- object = NULL;
- }
- FREE_OP2();
- } else if (OP2_TYPE != IS_CONST &&
- EXPECTED(Z_TYPE_P(function_name) == IS_OBJECT) &&
- Z_OBJ_HANDLER_P(function_name, get_closure) &&
- Z_OBJ_HANDLER_P(function_name, get_closure)(function_name, &called_scope, &fbc, &object) == SUCCESS) {
- if (fbc->common.fn_flags & ZEND_ACC_CLOSURE) {
- /* Delay closure destruction until its invocation */
- ZEND_ASSERT(GC_TYPE((zend_object*)fbc->common.prototype) == IS_OBJECT);
- GC_REFCOUNT((zend_object*)fbc->common.prototype)++;
- call_info |= ZEND_CALL_CLOSURE;
- } else if (object) {
- call_info |= ZEND_CALL_RELEASE_THIS;
- GC_REFCOUNT(object)++; /* For $this pointer */
- }
- FREE_OP2();
- } else if (EXPECTED(Z_TYPE_P(function_name) == IS_ARRAY) &&
- zend_hash_num_elements(Z_ARRVAL_P(function_name)) == 2) {
- zval *obj;
- zval *method;
- obj = zend_hash_index_find(Z_ARRVAL_P(function_name), 0);
- method = zend_hash_index_find(Z_ARRVAL_P(function_name), 1);
-
- if (!obj || !method) {
- zend_throw_error(NULL, "Array callback has to contain indices 0 and 1");
- FREE_OP2();
- HANDLE_EXCEPTION();
- }
-
- ZVAL_DEREF(obj);
- if (Z_TYPE_P(obj) != IS_STRING && Z_TYPE_P(obj) != IS_OBJECT) {
- zend_throw_error(NULL, "First array member is not a valid class name or object");
- FREE_OP2();
- HANDLE_EXCEPTION();
- }
-
- ZVAL_DEREF(method);
- if (Z_TYPE_P(method) != IS_STRING) {
- zend_throw_error(NULL, "Second array member is not a valid method");
- FREE_OP2();
- HANDLE_EXCEPTION();
- }
-
- if (Z_TYPE_P(obj) == IS_STRING) {
- object = NULL;
- called_scope = zend_fetch_class_by_name(Z_STR_P(obj), NULL, ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION);
- if (UNEXPECTED(called_scope == NULL)) {
- ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
- }
-
- if (called_scope->get_static_method) {
- fbc = called_scope->get_static_method(called_scope, Z_STR_P(method));
- } else {
- fbc = zend_std_get_static_method(called_scope, Z_STR_P(method), NULL);
- }
- if (UNEXPECTED(fbc == NULL)) {
- if (EXPECTED(!EG(exception))) {
- zend_throw_error(NULL, "Call to undefined method %s::%s()", ZSTR_VAL(called_scope->name), Z_STRVAL_P(method));
- }
- FREE_OP2();
- HANDLE_EXCEPTION();
- }
- if (!(fbc->common.fn_flags & ZEND_ACC_STATIC)) {
- if (fbc->common.fn_flags & ZEND_ACC_ALLOW_STATIC) {
- zend_error(E_DEPRECATED,
- "Non-static method %s::%s() should not be called statically",
- ZSTR_VAL(fbc->common.scope->name), ZSTR_VAL(fbc->common.function_name));
- if (UNEXPECTED(EG(exception) != NULL)) {
- HANDLE_EXCEPTION();
- }
- } else {
- zend_throw_error(
- zend_ce_error,
- "Non-static method %s::%s() cannot be called statically",
- ZSTR_VAL(fbc->common.scope->name), ZSTR_VAL(fbc->common.function_name));
- FREE_OP2();
- HANDLE_EXCEPTION();
- }
- }
- } else {
- called_scope = Z_OBJCE_P(obj);
- object = Z_OBJ_P(obj);
-
- fbc = Z_OBJ_HT_P(obj)->get_method(&object, Z_STR_P(method), NULL);
- if (UNEXPECTED(fbc == NULL)) {
- if (EXPECTED(!EG(exception))) {
- zend_throw_error(NULL, "Call to undefined method %s::%s()", ZSTR_VAL(object->ce->name), Z_STRVAL_P(method));
- }
- FREE_OP2();
- HANDLE_EXCEPTION();
- }
-
- if ((fbc->common.fn_flags & ZEND_ACC_STATIC) != 0) {
- object = NULL;
- } else {
- call_info |= ZEND_CALL_RELEASE_THIS;
- GC_REFCOUNT(object)++; /* For $this pointer */
- }
- }
- FREE_OP2();
- } else if ((OP2_TYPE & (IS_VAR|IS_CV)) && Z_TYPE_P(function_name) == IS_REFERENCE) {
+ call = zend_init_dynamic_call_string(Z_STR_P(function_name), opline->extended_value);
+ } else if (OP2_TYPE != IS_CONST && EXPECTED(Z_TYPE_P(function_name) == IS_OBJECT)) {
+ call = zend_init_dynamic_call_object(function_name, opline->extended_value);
+ } else if (EXPECTED(Z_TYPE_P(function_name) == IS_ARRAY)) {
+ call = zend_init_dynamic_call_array(Z_ARRVAL_P(function_name), opline->extended_value);
+ } else if ((OP2_TYPE & (IS_VAR|IS_CV)) && EXPECTED(Z_TYPE_P(function_name) == IS_REFERENCE)) {
function_name = Z_REFVAL_P(function_name);
ZEND_VM_C_GOTO(try_function_name);
} else {
@@ -3392,18 +3407,22 @@ ZEND_VM_C_LABEL(try_function_name):
}
}
zend_throw_error(NULL, "Function name must be a string");
- FREE_OP2();
+ call = NULL;
+ }
+
+ FREE_OP2();
+
+ if (UNEXPECTED(!call)) {
HANDLE_EXCEPTION();
}
- call = zend_vm_stack_push_call_frame(call_info,
- fbc, opline->extended_value, called_scope, object);
+
call->prev_execute_data = EX(call);
EX(call) = call;
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
-ZEND_VM_HANDLER(118, ZEND_INIT_USER_CALL, CONST, CONST|TMPVAR|CV)
+ZEND_VM_HANDLER(118, ZEND_INIT_USER_CALL, CONST, CONST|TMPVAR|CV, NUM)
{
USE_OPLINE
zend_free_op free_op2;
@@ -3414,12 +3433,14 @@ ZEND_VM_HANDLER(118, ZEND_INIT_USER_CALL, CONST, CONST|TMPVAR|CV)
zend_class_entry *called_scope;
zend_object *object;
zend_execute_data *call;
- uint32_t call_info = ZEND_CALL_NESTED_FUNCTION;
+ uint32_t call_info = ZEND_CALL_NESTED_FUNCTION | ZEND_CALL_DYNAMIC;
SAVE_OPLINE();
function_name = GET_OP2_ZVAL_PTR(BP_VAR_R);
if (zend_is_callable_ex(function_name, NULL, 0, NULL, &fcc, &error)) {
func = fcc.function_handler;
+ called_scope = fcc.called_scope;
+ object = fcc.object;
if (func->common.fn_flags & ZEND_ACC_CLOSURE) {
/* Delay closure destruction until its invocation */
if (OP2_TYPE & (IS_VAR|IS_CV)) {
@@ -3428,10 +3449,7 @@ ZEND_VM_HANDLER(118, ZEND_INIT_USER_CALL, CONST, CONST|TMPVAR|CV)
ZEND_ASSERT(GC_TYPE((zend_object*)func->common.prototype) == IS_OBJECT);
GC_REFCOUNT((zend_object*)func->common.prototype)++;
call_info |= ZEND_CALL_CLOSURE;
- }
- called_scope = fcc.called_scope;
- object = fcc.object;
- if (object) {
+ } else if (object) {
call_info |= ZEND_CALL_RELEASE_THIS;
GC_REFCOUNT(object)++; /* For $this pointer */
}
@@ -3445,6 +3463,9 @@ ZEND_VM_HANDLER(118, ZEND_INIT_USER_CALL, CONST, CONST|TMPVAR|CV)
HANDLE_EXCEPTION();
}
}
+ if (EXPECTED(func->type == ZEND_USER_FUNCTION) && UNEXPECTED(!func->op_array.run_time_cache)) {
+ init_func_run_time_cache(&func->op_array);
+ }
} else {
zend_internal_type_error(EX_USES_STRICT_TYPES(), "%s() expects parameter 1 to be a valid callback, %s", Z_STRVAL_P(EX_CONSTANT(opline->op1)), error);
efree(error);
@@ -3462,7 +3483,7 @@ ZEND_VM_HANDLER(118, ZEND_INIT_USER_CALL, CONST, CONST|TMPVAR|CV)
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
-ZEND_VM_HANDLER(69, ZEND_INIT_NS_FCALL_BY_NAME, ANY, CONST)
+ZEND_VM_HANDLER(69, ZEND_INIT_NS_FCALL_BY_NAME, ANY, CONST, NUM)
{
USE_OPLINE
zval *func_name;
@@ -3485,6 +3506,9 @@ ZEND_VM_HANDLER(69, ZEND_INIT_NS_FCALL_BY_NAME, ANY, CONST)
}
fbc = Z_FUNC_P(func);
CACHE_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)), fbc);
+ if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!fbc->op_array.run_time_cache)) {
+ init_func_run_time_cache(&fbc->op_array);
+ }
}
call = zend_vm_stack_push_call_frame(ZEND_CALL_NESTED_FUNCTION,
@@ -3495,7 +3519,7 @@ ZEND_VM_HANDLER(69, ZEND_INIT_NS_FCALL_BY_NAME, ANY, CONST)
ZEND_VM_NEXT_OPCODE();
}
-ZEND_VM_HANDLER(61, ZEND_INIT_FCALL, ANY, CONST)
+ZEND_VM_HANDLER(61, ZEND_INIT_FCALL, NUM, CONST, NUM)
{
USE_OPLINE
zend_free_op free_op2;
@@ -3514,6 +3538,9 @@ ZEND_VM_HANDLER(61, ZEND_INIT_FCALL, ANY, CONST)
}
fbc = Z_FUNC_P(func);
CACHE_PTR(Z_CACHE_SLOT_P(fname), fbc);
+ if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!fbc->op_array.run_time_cache)) {
+ init_func_run_time_cache(&fbc->op_array);
+ }
}
call = zend_vm_stack_push_call_frame_ex(
@@ -3525,12 +3552,13 @@ ZEND_VM_HANDLER(61, ZEND_INIT_FCALL, ANY, CONST)
ZEND_VM_NEXT_OPCODE();
}
-ZEND_VM_HANDLER(129, ZEND_DO_ICALL, ANY, ANY)
+ZEND_VM_HANDLER(129, ZEND_DO_ICALL, ANY, ANY, SPEC(RETVAL))
{
USE_OPLINE
zend_execute_data *call = EX(call);
zend_function *fbc = call->func;
zval *ret;
+ zval retval;
SAVE_OPLINE();
EX(call) = call->prev_execute_data;
@@ -3538,9 +3566,8 @@ ZEND_VM_HANDLER(129, ZEND_DO_ICALL, ANY, ANY)
call->prev_execute_data = execute_data;
EG(current_execute_data) = call;
- ret = EX_VAR(opline->result.var);
+ ret = RETURN_VALUE_USED(opline) ? EX_VAR(opline->result.var) : &retval;
ZVAL_NULL(ret);
- Z_VAR_FLAGS_P(ret) = 0;
fbc->internal_function.handler(call, ret);
@@ -3548,7 +3575,8 @@ ZEND_VM_HANDLER(129, ZEND_DO_ICALL, ANY, ANY)
ZEND_ASSERT(
EG(exception) || !call->func ||
!(call->func->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) ||
- zend_verify_internal_return_type(call->func, EX_VAR(opline->result.var)));
+ zend_verify_internal_return_type(call->func, ret));
+ ZEND_ASSERT(!Z_ISREF_P(ret));
#endif
EG(current_execute_data) = call->prev_execute_data;
@@ -3556,7 +3584,7 @@ ZEND_VM_HANDLER(129, ZEND_DO_ICALL, ANY, ANY)
zend_vm_stack_free_call_frame(call);
if (!RETURN_VALUE_USED(opline)) {
- zval_ptr_dtor(EX_VAR(opline->result.var));
+ zval_ptr_dtor(ret);
}
if (UNEXPECTED(EG(exception) != NULL)) {
@@ -3567,11 +3595,11 @@ ZEND_VM_HANDLER(129, ZEND_DO_ICALL, ANY, ANY)
HANDLE_EXCEPTION();
}
- ZEND_VM_INTERRUPT_CHECK();
- ZEND_VM_NEXT_OPCODE();
+ ZEND_VM_SET_OPCODE(opline + 1);
+ ZEND_VM_CONTINUE();
}
-ZEND_VM_HANDLER(130, ZEND_DO_UCALL, ANY, ANY)
+ZEND_VM_HANDLER(130, ZEND_DO_UCALL, ANY, ANY, SPEC(RETVAL))
{
USE_OPLINE
zend_execute_data *call = EX(call);
@@ -3581,22 +3609,19 @@ ZEND_VM_HANDLER(130, ZEND_DO_UCALL, ANY, ANY)
SAVE_OPLINE();
EX(call) = call->prev_execute_data;
- EG(scope) = NULL;
ret = NULL;
- call->symbol_table = NULL;
if (RETURN_VALUE_USED(opline)) {
ret = EX_VAR(opline->result.var);
ZVAL_NULL(ret);
- Z_VAR_FLAGS_P(ret) = 0;
}
call->prev_execute_data = execute_data;
- i_init_func_execute_data(call, &fbc->op_array, ret, 0);
+ i_init_func_execute_data(call, &fbc->op_array, ret);
ZEND_VM_ENTER();
}
-ZEND_VM_HANDLER(131, ZEND_DO_FCALL_BY_NAME, ANY, ANY)
+ZEND_VM_HANDLER(131, ZEND_DO_FCALL_BY_NAME, ANY, ANY, SPEC(RETVAL))
{
USE_OPLINE
zend_execute_data *call = EX(call);
@@ -3607,33 +3632,18 @@ ZEND_VM_HANDLER(131, ZEND_DO_FCALL_BY_NAME, ANY, ANY)
EX(call) = call->prev_execute_data;
if (EXPECTED(fbc->type == ZEND_USER_FUNCTION)) {
- EG(scope) = NULL;
- if (UNEXPECTED((fbc->common.fn_flags & ZEND_ACC_GENERATOR) != 0)) {
- if (EXPECTED(RETURN_VALUE_USED(opline))) {
- ret = EX_VAR(opline->result.var);
- zend_generator_create_zval(call, &fbc->op_array, ret);
- Z_VAR_FLAGS_P(ret) = 0;
- } else {
- zend_vm_stack_free_args(call);
- }
-
- zend_vm_stack_free_call_frame(call);
- } else {
- ret = NULL;
- call->symbol_table = NULL;
- if (RETURN_VALUE_USED(opline)) {
- ret = EX_VAR(opline->result.var);
- ZVAL_NULL(ret);
- Z_VAR_FLAGS_P(ret) = 0;
- }
+ ret = NULL;
+ if (RETURN_VALUE_USED(opline)) {
+ ret = EX_VAR(opline->result.var);
+ ZVAL_NULL(ret);
+ }
- call->prev_execute_data = execute_data;
- i_init_func_execute_data(call, &fbc->op_array, ret, 0);
+ call->prev_execute_data = execute_data;
+ i_init_func_execute_data(call, &fbc->op_array, ret);
- ZEND_VM_ENTER();
- }
- EG(scope) = EX(func)->op_array.scope;
+ ZEND_VM_ENTER();
} else {
+ zval retval;
ZEND_ASSERT(fbc->type == ZEND_INTERNAL_FUNCTION);
if (UNEXPECTED((fbc->common.fn_flags & ZEND_ACC_DEPRECATED) != 0)) {
@@ -3649,34 +3659,25 @@ ZEND_VM_HANDLER(131, ZEND_DO_FCALL_BY_NAME, ANY, ANY)
call->prev_execute_data = execute_data;
EG(current_execute_data) = call;
- if (fbc->common.fn_flags & ZEND_ACC_HAS_TYPE_HINTS) {
- uint32_t i;
- uint32_t num_args = ZEND_CALL_NUM_ARGS(call);
- zval *p = ZEND_CALL_ARG(call, 1);
-
- for (i = 0; i < num_args; ++i) {
- if (UNEXPECTED(!zend_verify_internal_arg_type(fbc, i + 1, p))) {
- EG(current_execute_data) = call->prev_execute_data;
- zend_vm_stack_free_args(call);
- zend_vm_stack_free_call_frame(call);
- zend_throw_exception_internal(NULL);
- HANDLE_EXCEPTION();
- }
- p++;
- }
+ if (UNEXPECTED(fbc->common.fn_flags & ZEND_ACC_HAS_TYPE_HINTS)
+ && UNEXPECTED(!zend_verify_internal_arg_types(fbc, call))) {
+ zend_vm_stack_free_call_frame(call);
+ zend_throw_exception_internal(NULL);
+ HANDLE_EXCEPTION();
}
- ret = EX_VAR(opline->result.var);
+ ret = RETURN_VALUE_USED(opline) ? EX_VAR(opline->result.var) : &retval;
ZVAL_NULL(ret);
- Z_VAR_FLAGS_P(ret) = (fbc->common.fn_flags & ZEND_ACC_RETURN_REFERENCE) != 0 ? IS_VAR_RET_REF : 0;
fbc->internal_function.handler(call, ret);
#if ZEND_DEBUG
- ZEND_ASSERT(
- EG(exception) || !call->func ||
- !(call->func->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) ||
- zend_verify_internal_return_type(call->func, EX_VAR(opline->result.var)));
+ if (!EG(exception) && call->func) {
+ ZEND_ASSERT(!(call->func->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) ||
+ zend_verify_internal_return_type(call->func, ret));
+ ZEND_ASSERT((call->func->common.fn_flags & ZEND_ACC_RETURN_REFERENCE)
+ ? Z_ISREF_P(ret) : !Z_ISREF_P(ret));
+ }
#endif
EG(current_execute_data) = call->prev_execute_data;
@@ -3684,7 +3685,7 @@ ZEND_VM_HANDLER(131, ZEND_DO_FCALL_BY_NAME, ANY, ANY)
zend_vm_stack_free_call_frame(call);
if (!RETURN_VALUE_USED(opline)) {
- zval_ptr_dtor(EX_VAR(opline->result.var));
+ zval_ptr_dtor(ret);
}
}
@@ -3695,11 +3696,11 @@ ZEND_VM_HANDLER(131, ZEND_DO_FCALL_BY_NAME, ANY, ANY)
}
HANDLE_EXCEPTION();
}
- ZEND_VM_INTERRUPT_CHECK();
- ZEND_VM_NEXT_OPCODE();
+ ZEND_VM_SET_OPCODE(opline + 1);
+ ZEND_VM_CONTINUE();
}
-ZEND_VM_HANDLER(60, ZEND_DO_FCALL, ANY, ANY)
+ZEND_VM_HANDLER(60, ZEND_DO_FCALL, ANY, ANY, SPEC(RETVAL))
{
USE_OPLINE
zend_execute_data *call = EX(call);
@@ -3728,73 +3729,37 @@ ZEND_VM_HANDLER(60, ZEND_DO_FCALL, ANY, ANY)
LOAD_OPLINE();
if (EXPECTED(fbc->type == ZEND_USER_FUNCTION)) {
- EG(scope) = fbc->common.scope;
- if (UNEXPECTED((fbc->common.fn_flags & ZEND_ACC_GENERATOR) != 0)) {
- if (EXPECTED(RETURN_VALUE_USED(opline))) {
- ret = EX_VAR(opline->result.var);
- zend_generator_create_zval(call, &fbc->op_array, ret);
- Z_VAR_FLAGS_P(ret) = 0;
- } else {
- if (UNEXPECTED(ZEND_CALL_INFO(call) & ZEND_CALL_CLOSURE)) {
- OBJ_RELEASE((zend_object*)fbc->op_array.prototype);
- }
- zend_vm_stack_free_args(call);
- }
- } else {
- ret = NULL;
- call->symbol_table = NULL;
- if (RETURN_VALUE_USED(opline)) {
- ret = EX_VAR(opline->result.var);
- ZVAL_NULL(ret);
- Z_VAR_FLAGS_P(ret) = 0;
- }
+ ret = NULL;
+ if (RETURN_VALUE_USED(opline)) {
+ ret = EX_VAR(opline->result.var);
+ ZVAL_NULL(ret);
+ }
- call->prev_execute_data = execute_data;
- i_init_func_execute_data(call, &fbc->op_array, ret, 1);
+ call->prev_execute_data = execute_data;
+ i_init_func_execute_data(call, &fbc->op_array, ret);
- if (EXPECTED(zend_execute_ex == execute_ex)) {
- ZEND_VM_ENTER();
- } else {
- ZEND_ADD_CALL_FLAG(call, ZEND_CALL_TOP);
- zend_execute_ex(call);
- }
+ if (EXPECTED(zend_execute_ex == execute_ex)) {
+ ZEND_VM_ENTER();
+ } else {
+ ZEND_ADD_CALL_FLAG(call, ZEND_CALL_TOP);
+ zend_execute_ex(call);
}
} else if (EXPECTED(fbc->type < ZEND_USER_FUNCTION)) {
- int should_change_scope = 0;
-
- if (fbc->common.scope) {
- should_change_scope = 1;
- EG(scope) = fbc->common.scope;
- }
+ zval retval;
call->prev_execute_data = execute_data;
EG(current_execute_data) = call;
- if (fbc->common.fn_flags & ZEND_ACC_HAS_TYPE_HINTS) {
- uint32_t i;
- uint32_t num_args = ZEND_CALL_NUM_ARGS(call);
- zval *p = ZEND_CALL_ARG(call, 1);
-
- for (i = 0; i < num_args; ++i) {
- if (UNEXPECTED(!zend_verify_internal_arg_type(fbc, i + 1, p))) {
- EG(current_execute_data) = call->prev_execute_data;
- zend_vm_stack_free_args(call);
- if (RETURN_VALUE_USED(opline)) {
- ZVAL_UNDEF(EX_VAR(opline->result.var));
- }
- if (UNEXPECTED(should_change_scope)) {
- ZEND_VM_C_GOTO(fcall_end_change_scope);
- } else {
- ZEND_VM_C_GOTO(fcall_end);
- }
- }
- p++;
+ if (UNEXPECTED(fbc->common.fn_flags & ZEND_ACC_HAS_TYPE_HINTS)
+ && UNEXPECTED(!zend_verify_internal_arg_types(fbc, call))) {
+ if (RETURN_VALUE_USED(opline)) {
+ ZVAL_UNDEF(EX_VAR(opline->result.var));
}
+ ZEND_VM_C_GOTO(fcall_end);
}
- ret = EX_VAR(opline->result.var);
+ ret = RETURN_VALUE_USED(opline) ? EX_VAR(opline->result.var) : &retval;
ZVAL_NULL(ret);
- Z_VAR_FLAGS_P(ret) = (fbc->common.fn_flags & ZEND_ACC_RETURN_REFERENCE) != 0 ? IS_VAR_RET_REF : 0;
if (!zend_execute_internal) {
/* saves one function call if zend_execute_internal is not used */
@@ -3804,83 +3769,53 @@ ZEND_VM_HANDLER(60, ZEND_DO_FCALL, ANY, ANY)
}
#if ZEND_DEBUG
- ZEND_ASSERT(
- EG(exception) || !call->func ||
- !(call->func->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) ||
- zend_verify_internal_return_type(call->func, EX_VAR(opline->result.var)));
+ if (!EG(exception) && call->func) {
+ ZEND_ASSERT(!(call->func->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) ||
+ zend_verify_internal_return_type(call->func, ret));
+ ZEND_ASSERT((call->func->common.fn_flags & ZEND_ACC_RETURN_REFERENCE)
+ ? Z_ISREF_P(ret) : !Z_ISREF_P(ret));
+ }
#endif
EG(current_execute_data) = call->prev_execute_data;
zend_vm_stack_free_args(call);
if (!RETURN_VALUE_USED(opline)) {
- zval_ptr_dtor(EX_VAR(opline->result.var));
+ zval_ptr_dtor(ret);
}
- if (UNEXPECTED(should_change_scope)) {
- ZEND_VM_C_GOTO(fcall_end_change_scope);
- } else {
- ZEND_VM_C_GOTO(fcall_end);
- }
} else { /* ZEND_OVERLOADED_FUNCTION */
- /* Not sure what should be done here if it's a static method */
- object = Z_OBJ(call->This);
- if (UNEXPECTED(object == NULL)) {
- zend_vm_stack_free_args(call);
- if (fbc->type == ZEND_OVERLOADED_FUNCTION_TEMPORARY) {
- zend_string_release(fbc->common.function_name);
- }
- efree(fbc);
- zend_vm_stack_free_call_frame(call);
-
- zend_throw_error(NULL, "Cannot call overloaded function for non-object");
- HANDLE_EXCEPTION();
- }
-
- EG(scope) = fbc->common.scope;
+ zval retval;
- ZVAL_NULL(EX_VAR(opline->result.var));
+ ret = RETURN_VALUE_USED(opline) ? EX_VAR(opline->result.var) : &retval;
call->prev_execute_data = execute_data;
- EG(current_execute_data) = call;
- object->handlers->call_method(fbc->common.function_name, object, call, EX_VAR(opline->result.var));
- EG(current_execute_data) = call->prev_execute_data;
-
- zend_vm_stack_free_args(call);
- if (fbc->type == ZEND_OVERLOADED_FUNCTION_TEMPORARY) {
- zend_string_release(fbc->common.function_name);
+ if (UNEXPECTED(!zend_do_fcall_overloaded(fbc, call, ret))) {
+ HANDLE_EXCEPTION();
}
- efree(fbc);
if (!RETURN_VALUE_USED(opline)) {
- zval_ptr_dtor(EX_VAR(opline->result.var));
- } else {
- Z_VAR_FLAGS_P(EX_VAR(opline->result.var)) = 0;
+ zval_ptr_dtor(ret);
}
}
-ZEND_VM_C_LABEL(fcall_end_change_scope):
+ZEND_VM_C_LABEL(fcall_end):
if (UNEXPECTED(ZEND_CALL_INFO(call) & ZEND_CALL_RELEASE_THIS)) {
object = Z_OBJ(call->This);
#if 0
if (UNEXPECTED(EG(exception) != NULL) && (opline->op1.num & ZEND_CALL_CTOR)) {
- if (!(opline->op1.num & ZEND_CALL_CTOR_RESULT_UNUSED)) {
#else
if (UNEXPECTED(EG(exception) != NULL) && (ZEND_CALL_INFO(call) & ZEND_CALL_CTOR)) {
- if (!(ZEND_CALL_INFO(call) & ZEND_CALL_CTOR_RESULT_UNUSED)) {
#endif
- GC_REFCOUNT(object)--;
- }
+ GC_REFCOUNT(object)--;
if (GC_REFCOUNT(object) == 1) {
zend_object_store_ctor_failed(object);
}
}
OBJ_RELEASE(object);
}
- EG(scope) = EX(func)->op_array.scope;
-ZEND_VM_C_LABEL(fcall_end):
zend_vm_stack_free_call_frame(call);
if (UNEXPECTED(EG(exception) != NULL)) {
zend_throw_exception_internal(NULL);
@@ -3890,8 +3825,8 @@ ZEND_VM_C_LABEL(fcall_end):
HANDLE_EXCEPTION();
}
- ZEND_VM_INTERRUPT_CHECK();
- ZEND_VM_NEXT_OPCODE();
+ ZEND_VM_SET_OPCODE(opline + 1);
+ ZEND_VM_CONTINUE();
}
ZEND_VM_HANDLER(124, ZEND_VERIFY_RETURN_TYPE, CONST|TMP|VAR|UNUSED|CV, UNUSED)
@@ -3942,8 +3877,6 @@ ZEND_VM_HANDLER(124, ZEND_VERIFY_RETURN_TYPE, CONST|TMP|VAR|UNUSED|CV, UNUSED)
if (UNEXPECTED(EG(exception) != NULL)) {
if (OP1_TYPE == IS_CONST) {
zval_ptr_dtor_nogc(retval_ptr);
- } else {
- FREE_OP1();
}
}
#endif
@@ -3955,53 +3888,68 @@ ZEND_VM_HANDLER(62, ZEND_RETURN, CONST|TMP|VAR|CV, ANY)
{
USE_OPLINE
zval *retval_ptr;
+ zval *return_value;
zend_free_op free_op1;
retval_ptr = GET_OP1_ZVAL_PTR_UNDEF(BP_VAR_R);
+ return_value = EX(return_value);
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);
- if (EX(return_value)) {
- ZVAL_NULL(EX(return_value));
+ if (return_value) {
+ ZVAL_NULL(return_value);
}
- } else if (!EX(return_value)) {
- if (OP1_TYPE == IS_VAR || OP1_TYPE == IS_TMP_VAR ) {
+ } else if (!return_value) {
+ if (OP1_TYPE & (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));
+ zval_dtor_func(Z_COUNTED_P(free_op1));
}
}
} else {
- if (OP1_TYPE == IS_CONST || OP1_TYPE == IS_TMP_VAR) {
- ZVAL_COPY_VALUE(EX(return_value), retval_ptr);
+ if ((OP1_TYPE & (IS_CONST|IS_TMP_VAR))) {
+ ZVAL_COPY_VALUE(return_value, retval_ptr);
if (OP1_TYPE == IS_CONST) {
- if (UNEXPECTED(Z_OPT_COPYABLE_P(EX(return_value)))) {
- zval_copy_ctor_func(EX(return_value));
+ if (UNEXPECTED(Z_OPT_REFCOUNTED_P(return_value))) {
+ Z_ADDREF_P(return_value);
}
}
} else if (OP1_TYPE == IS_CV) {
- ZVAL_DEREF(retval_ptr);
- ZVAL_COPY(EX(return_value), retval_ptr);
+ if (Z_OPT_REFCOUNTED_P(retval_ptr)) {
+ if (EXPECTED(!Z_OPT_ISREF_P(retval_ptr))) {
+ ZVAL_COPY_VALUE(return_value, retval_ptr);
+ if (EXPECTED(!(EX_CALL_INFO() & ZEND_CALL_CODE))) {
+ ZVAL_NULL(retval_ptr);
+ } else {
+ Z_ADDREF_P(return_value);
+ }
+ } else {
+ retval_ptr = Z_REFVAL_P(retval_ptr);
+ ZVAL_COPY(return_value, retval_ptr);
+ }
+ } else {
+ ZVAL_COPY_VALUE(return_value, retval_ptr);
+ }
} else /* if (OP1_TYPE == IS_VAR) */ {
if (UNEXPECTED(Z_ISREF_P(retval_ptr))) {
zend_refcounted *ref = Z_COUNTED_P(retval_ptr);
retval_ptr = Z_REFVAL_P(retval_ptr);
- ZVAL_COPY_VALUE(EX(return_value), retval_ptr);
+ ZVAL_COPY_VALUE(return_value, retval_ptr);
if (UNEXPECTED(--GC_REFCOUNT(ref) == 0)) {
efree_size(ref, sizeof(zend_reference));
} else if (Z_OPT_REFCOUNTED_P(retval_ptr)) {
Z_ADDREF_P(retval_ptr);
}
} else {
- ZVAL_COPY_VALUE(EX(return_value), retval_ptr);
+ ZVAL_COPY_VALUE(return_value, retval_ptr);
}
}
}
ZEND_VM_DISPATCH_TO_HELPER(zend_leave_helper);
}
-ZEND_VM_HANDLER(111, ZEND_RETURN_BY_REF, CONST|TMP|VAR|CV, ANY)
+ZEND_VM_HANDLER(111, ZEND_RETURN_BY_REF, CONST|TMP|VAR|CV, ANY, SRC)
{
USE_OPLINE
zval *retval_ptr;
@@ -4010,21 +3958,23 @@ ZEND_VM_HANDLER(111, ZEND_RETURN_BY_REF, CONST|TMP|VAR|CV, ANY)
SAVE_OPLINE();
do {
- if (OP1_TYPE == IS_CONST || OP1_TYPE == IS_TMP_VAR ||
+ if ((OP1_TYPE & (IS_CONST|IS_TMP_VAR)) ||
(OP1_TYPE == IS_VAR && opline->extended_value == ZEND_RETURNS_VALUE)) {
/* Not supposed to happen, but we'll allow it */
zend_error(E_NOTICE, "Only variable references should be returned by reference");
retval_ptr = GET_OP1_ZVAL_PTR(BP_VAR_R);
if (!EX(return_value)) {
- if (OP1_TYPE == IS_TMP_VAR) {
- FREE_OP1();
- }
+ FREE_OP1();
} else {
- ZVAL_COPY_VALUE(EX(return_value), retval_ptr);
- Z_VAR_FLAGS_P(EX(return_value)) = IS_VAR_RET_REF;
- if (OP1_TYPE != IS_TMP_VAR) {
- zval_opt_copy_ctor_no_imm(EX(return_value));
+ if (OP1_TYPE == IS_VAR && UNEXPECTED(Z_ISREF_P(retval_ptr))) {
+ ZVAL_COPY_VALUE(EX(return_value), retval_ptr);
+ break;
+ }
+
+ ZVAL_NEW_REF(EX(return_value), retval_ptr);
+ if (OP1_TYPE == IS_CONST) {
+ if (Z_REFCOUNTED_P(retval_ptr)) Z_ADDREF_P(retval_ptr);
}
}
break;
@@ -4032,20 +3982,14 @@ ZEND_VM_HANDLER(111, ZEND_RETURN_BY_REF, CONST|TMP|VAR|CV, ANY)
retval_ptr = GET_OP1_ZVAL_PTR_PTR(BP_VAR_W);
- if (OP1_TYPE == IS_VAR && UNEXPECTED(retval_ptr == NULL)) {
- zend_throw_error(NULL, "Cannot return string offsets by reference");
- HANDLE_EXCEPTION();
- }
-
if (OP1_TYPE == IS_VAR) {
if (retval_ptr == &EG(uninitialized_zval) ||
- (opline->extended_value == ZEND_RETURNS_FUNCTION &&
- !(Z_VAR_FLAGS_P(retval_ptr) & IS_VAR_RET_REF))) {
+ (opline->extended_value == ZEND_RETURNS_FUNCTION && !Z_ISREF_P(retval_ptr))) {
zend_error(E_NOTICE, "Only variable references should be returned by reference");
if (EX(return_value)) {
ZVAL_NEW_REF(EX(return_value), retval_ptr);
- Z_VAR_FLAGS_P(EX(return_value)) = IS_VAR_RET_REF;
- if (Z_REFCOUNTED_P(retval_ptr)) Z_ADDREF_P(retval_ptr);
+ } else {
+ FREE_OP1_VAR_PTR();
}
break;
}
@@ -4055,14 +3999,82 @@ ZEND_VM_HANDLER(111, ZEND_RETURN_BY_REF, CONST|TMP|VAR|CV, ANY)
ZVAL_MAKE_REF(retval_ptr);
Z_ADDREF_P(retval_ptr);
ZVAL_REF(EX(return_value), Z_REF_P(retval_ptr));
- Z_VAR_FLAGS_P(EX(return_value)) = IS_VAR_RET_REF;
}
+
+ FREE_OP1_VAR_PTR();
} while (0);
- FREE_OP1_VAR_PTR();
ZEND_VM_DISPATCH_TO_HELPER(zend_leave_helper);
}
+ZEND_VM_HANDLER(41, ZEND_GENERATOR_CREATE, ANY, ANY)
+{
+ zval *return_value = EX(return_value);
+
+ if (EXPECTED(return_value)) {
+ USE_OPLINE
+ zend_generator *generator;
+ zend_execute_data *gen_execute_data;
+ uint32_t num_args, used_stack, call_info;
+
+ object_init_ex(return_value, zend_ce_generator);
+
+ /*
+ * Normally the execute_data is allocated on the VM stack (because it does
+ * not actually do any allocation and thus is faster). For generators
+ * though this behavior would be suboptimal, because the (rather large)
+ * structure would have to be copied back and forth every time execution is
+ * suspended or resumed. That's why for generators the execution context
+ * is allocated on heap.
+ */
+ num_args = EX_NUM_ARGS();
+ used_stack = (ZEND_CALL_FRAME_SLOT + num_args + EX(func)->op_array.last_var + EX(func)->op_array.T - MIN(EX(func)->op_array.num_args, num_args)) * sizeof(zval);
+ gen_execute_data = (zend_execute_data*)emalloc(used_stack);
+ memcpy(gen_execute_data, execute_data, used_stack);
+
+ /* Save execution context in generator object. */
+ generator = (zend_generator *) Z_OBJ_P(EX(return_value));
+ generator->execute_data = gen_execute_data;
+ generator->frozen_call_stack = NULL;
+ generator->execute_fake.opline = NULL;
+ generator->execute_fake.func = NULL;
+ generator->execute_fake.prev_execute_data = NULL;
+ ZVAL_OBJ(&generator->execute_fake.This, (zend_object *) generator);
+
+ gen_execute_data->opline = opline + 1;
+ /* EX(return_value) keeps pointer to zend_object (not a real zval) */
+ gen_execute_data->return_value = (zval*)generator;
+ call_info = Z_TYPE_INFO(EX(This));
+ if ((call_info & Z_TYPE_MASK) == IS_OBJECT
+ && !(call_info & ((ZEND_CALL_CLOSURE|ZEND_CALL_RELEASE_THIS) << ZEND_CALL_INFO_SHIFT))) {
+ ZEND_ADD_CALL_FLAG_EX(call_info, ZEND_CALL_RELEASE_THIS);
+ Z_ADDREF(gen_execute_data->This);
+ }
+ ZEND_ADD_CALL_FLAG_EX(call_info, (ZEND_CALL_TOP_FUNCTION | ZEND_CALL_ALLOCATED | ZEND_CALL_GENERATOR));
+ Z_TYPE_INFO(gen_execute_data->This) = call_info;
+ gen_execute_data->prev_execute_data = NULL;
+
+ call_info = EX_CALL_INFO();
+ EG(current_execute_data) = EX(prev_execute_data);
+ if (EXPECTED(!(call_info & (ZEND_CALL_TOP|ZEND_CALL_ALLOCATED)))) {
+ EG(vm_stack_top) = (zval*)execute_data;
+ execute_data = EX(prev_execute_data);
+ LOAD_NEXT_OPLINE();
+ ZEND_VM_LEAVE();
+ } else if (EXPECTED(!(call_info & ZEND_CALL_TOP))) {
+ zend_execute_data *old_execute_data = execute_data;
+ execute_data = EX(prev_execute_data);
+ zend_vm_stack_free_call_frame_ex(call_info, old_execute_data);
+ LOAD_NEXT_OPLINE();
+ ZEND_VM_LEAVE();
+ } else {
+ ZEND_VM_RETURN();
+ }
+ } else {
+ ZEND_VM_DISPATCH_TO_HELPER(zend_leave_helper);
+ }
+}
+
ZEND_VM_HANDLER(161, ZEND_GENERATOR_RETURN, CONST|TMP|VAR|CV, ANY)
{
USE_OPLINE
@@ -4075,11 +4087,11 @@ ZEND_VM_HANDLER(161, ZEND_GENERATOR_RETURN, CONST|TMP|VAR|CV, ANY)
retval = GET_OP1_ZVAL_PTR(BP_VAR_R);
/* Copy return value into generator->retval */
- if (OP1_TYPE == IS_CONST || OP1_TYPE == IS_TMP_VAR) {
+ if ((OP1_TYPE & (IS_CONST|IS_TMP_VAR))) {
ZVAL_COPY_VALUE(&generator->retval, retval);
if (OP1_TYPE == IS_CONST) {
- if (UNEXPECTED(Z_OPT_COPYABLE(generator->retval))) {
- zval_copy_ctor_func(&generator->retval);
+ if (UNEXPECTED(Z_OPT_REFCOUNTED(generator->retval))) {
+ Z_ADDREF(generator->retval);
}
}
} else if (OP1_TYPE == IS_CV) {
@@ -4148,7 +4160,7 @@ ZEND_VM_HANDLER(108, ZEND_THROW, CONST|TMP|VAR|CV, ANY)
HANDLE_EXCEPTION();
}
-ZEND_VM_HANDLER(107, ZEND_CATCH, CONST, CV)
+ZEND_VM_HANDLER(107, ZEND_CATCH, CONST, CV, JMP_ADDR)
{
USE_OPLINE
zend_class_entry *ce, *catch_ce;
@@ -4158,8 +4170,8 @@ ZEND_VM_HANDLER(107, ZEND_CATCH, CONST, CV)
/* Check whether an exception has been thrown, if not, jump over code */
zend_exception_restore();
if (EG(exception) == NULL) {
- ZEND_VM_SET_OPCODE(&EX(func)->op_array.opcodes[opline->extended_value]);
- ZEND_VM_CONTINUE(); /* CHECK_ME */
+ ZEND_VM_SET_RELATIVE_OPCODE(opline, opline->extended_value);
+ ZEND_VM_CONTINUE();
}
catch_ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)));
if (UNEXPECTED(catch_ce == NULL)) {
@@ -4181,8 +4193,8 @@ ZEND_VM_HANDLER(107, ZEND_CATCH, CONST, CV)
zend_throw_exception_internal(NULL);
HANDLE_EXCEPTION();
}
- ZEND_VM_SET_OPCODE(&EX(func)->op_array.opcodes[opline->extended_value]);
- ZEND_VM_CONTINUE(); /* CHECK_ME */
+ ZEND_VM_SET_RELATIVE_OPCODE(opline, opline->extended_value);
+ ZEND_VM_CONTINUE();
}
}
@@ -4198,7 +4210,7 @@ ZEND_VM_HANDLER(107, ZEND_CATCH, CONST, CV)
}
}
-ZEND_VM_HANDLER(65, ZEND_SEND_VAL, CONST|TMP, ANY)
+ZEND_VM_HANDLER(65, ZEND_SEND_VAL, CONST|TMPVAR, NUM)
{
USE_OPLINE
zval *value, *arg;
@@ -4208,14 +4220,14 @@ ZEND_VM_HANDLER(65, ZEND_SEND_VAL, CONST|TMP, ANY)
arg = ZEND_CALL_VAR(EX(call), opline->result.var);
ZVAL_COPY_VALUE(arg, value);
if (OP1_TYPE == IS_CONST) {
- if (UNEXPECTED(Z_OPT_COPYABLE_P(arg))) {
- zval_copy_ctor_func(arg);
+ if (UNEXPECTED(Z_OPT_REFCOUNTED_P(arg))) {
+ Z_ADDREF_P(arg);
}
}
ZEND_VM_NEXT_OPCODE();
}
-ZEND_VM_HANDLER(116, ZEND_SEND_VAL_EX, CONST|TMP, ANY)
+ZEND_VM_HANDLER(116, ZEND_SEND_VAL_EX, CONST|TMP, NUM, SPEC(QUICK_ARG))
{
USE_OPLINE
zval *value, *arg;
@@ -4239,14 +4251,14 @@ ZEND_VM_C_LABEL(send_val_by_ref):
arg = ZEND_CALL_VAR(EX(call), opline->result.var);
ZVAL_COPY_VALUE(arg, value);
if (OP1_TYPE == IS_CONST) {
- if (UNEXPECTED(Z_OPT_COPYABLE_P(arg))) {
- zval_copy_ctor_func(arg);
+ if (UNEXPECTED(Z_OPT_REFCOUNTED_P(arg))) {
+ Z_ADDREF_P(arg);
}
}
ZEND_VM_NEXT_OPCODE();
}
-ZEND_VM_HANDLER(117, ZEND_SEND_VAR, VAR|CV, ANY)
+ZEND_VM_HANDLER(117, ZEND_SEND_VAR, VAR|CV, NUM)
{
USE_OPLINE
zval *varptr, *arg;
@@ -4285,41 +4297,66 @@ ZEND_VM_HANDLER(117, ZEND_SEND_VAR, VAR|CV, ANY)
ZEND_VM_NEXT_OPCODE();
}
-ZEND_VM_HANDLER(106, ZEND_SEND_VAR_NO_REF, VAR, ANY)
+ZEND_VM_HANDLER(106, ZEND_SEND_VAR_NO_REF, VAR, NUM)
{
USE_OPLINE
zend_free_op free_op1;
zval *varptr, *arg;
- if (!(opline->extended_value & ZEND_ARG_COMPILE_TIME_BOUND)) {
- if (!ARG_SHOULD_BE_SENT_BY_REF(EX(call)->func, opline->op2.num)) {
+ varptr = GET_OP1_ZVAL_PTR(BP_VAR_R);
+ arg = ZEND_CALL_VAR(EX(call), opline->result.var);
+ ZVAL_COPY_VALUE(arg, varptr);
+
+ if (EXPECTED(Z_ISREF_P(varptr))) {
+ ZEND_VM_NEXT_OPCODE();
+ }
+
+ SAVE_OPLINE();
+ zend_error(E_NOTICE, "Only variables should be passed by reference");
+ ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
+}
+
+ZEND_VM_HANDLER(50, ZEND_SEND_VAR_NO_REF_EX, VAR, NUM, SPEC(QUICK_ARG))
+{
+ USE_OPLINE
+ zend_free_op free_op1;
+ zval *varptr, *arg;
+ uint32_t arg_num = opline->op2.num;
+
+ if (EXPECTED(arg_num <= MAX_ARG_FLAG_NUM)) {
+ if (!QUICK_ARG_SHOULD_BE_SENT_BY_REF(EX(call)->func, arg_num)) {
ZEND_VM_DISPATCH_TO_HANDLER(ZEND_SEND_VAR);
}
- }
- varptr = GET_OP1_ZVAL_PTR(BP_VAR_R);
+ varptr = GET_OP1_ZVAL_PTR(BP_VAR_R);
+ arg = ZEND_CALL_VAR(EX(call), opline->result.var);
+ ZVAL_COPY_VALUE(arg, varptr);
- if (EXPECTED(Z_ISREF_P(varptr) ||
- ((opline->extended_value & ZEND_ARG_COMPILE_TIME_BOUND) ?
- (opline->extended_value & ZEND_ARG_SEND_SILENT) :
- ARG_MAY_BE_SENT_BY_REF(EX(call)->func, opline->op2.num
- )))) {
+ if (EXPECTED(Z_ISREF_P(varptr) ||
+ QUICK_ARG_MAY_BE_SENT_BY_REF(EX(call)->func, arg_num))) {
+ ZEND_VM_NEXT_OPCODE();
+ }
+ } else {
+ if (!ARG_SHOULD_BE_SENT_BY_REF(EX(call)->func, arg_num)) {
+ ZEND_VM_DISPATCH_TO_HANDLER(ZEND_SEND_VAR);
+ }
+
+ varptr = GET_OP1_ZVAL_PTR(BP_VAR_R);
arg = ZEND_CALL_VAR(EX(call), opline->result.var);
ZVAL_COPY_VALUE(arg, varptr);
- ZEND_VM_NEXT_OPCODE();
+ if (EXPECTED(Z_ISREF_P(varptr) ||
+ ARG_MAY_BE_SENT_BY_REF(EX(call)->func, arg_num))) {
+ ZEND_VM_NEXT_OPCODE();
+ }
}
SAVE_OPLINE();
zend_error(E_NOTICE, "Only variables should be passed by reference");
-
- arg = ZEND_CALL_VAR(EX(call), opline->result.var);
- ZVAL_COPY_VALUE(arg, varptr);
-
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
-ZEND_VM_HANDLER(67, ZEND_SEND_REF, VAR|CV, ANY)
+ZEND_VM_HANDLER(67, ZEND_SEND_REF, VAR|CV, NUM)
{
USE_OPLINE
zend_free_op free_op1;
@@ -4328,16 +4365,10 @@ ZEND_VM_HANDLER(67, ZEND_SEND_REF, VAR|CV, ANY)
SAVE_OPLINE();
varptr = GET_OP1_ZVAL_PTR_PTR(BP_VAR_W);
- if (OP1_TYPE == IS_VAR && UNEXPECTED(varptr == NULL)) {
- zend_throw_error(NULL, "Only variables can be passed by reference");
- arg = ZEND_CALL_VAR(EX(call), opline->result.var);
- ZVAL_UNDEF(arg);
- HANDLE_EXCEPTION();
- }
-
arg = ZEND_CALL_VAR(EX(call), opline->result.var);
- if (OP1_TYPE == IS_VAR && UNEXPECTED(varptr == &EG(error_zval))) {
- ZVAL_NEW_REF(arg, &EG(uninitialized_zval));
+ if (OP1_TYPE == IS_VAR && UNEXPECTED(Z_ISERROR_P(varptr))) {
+ ZVAL_NEW_EMPTY_REF(arg);
+ ZVAL_NULL(Z_REFVAL_P(arg));
ZEND_VM_NEXT_OPCODE();
}
@@ -4354,7 +4385,7 @@ ZEND_VM_HANDLER(67, ZEND_SEND_REF, VAR|CV, ANY)
ZEND_VM_NEXT_OPCODE();
}
-ZEND_VM_HANDLER(66, ZEND_SEND_VAR_EX, VAR|CV, ANY)
+ZEND_VM_HANDLER(66, ZEND_SEND_VAR_EX, VAR|CV, NUM, SPEC(QUICK_ARG))
{
USE_OPLINE
zval *varptr, *arg;
@@ -4486,35 +4517,32 @@ ZEND_VM_C_LABEL(send_again):
if (iter->funcs->rewind) {
iter->funcs->rewind(iter);
- if (UNEXPECTED(EG(exception) != NULL)) {
- ZEND_VM_C_GOTO(unpack_iter_dtor);
- }
}
for (; iter->funcs->valid(iter) == SUCCESS; ++arg_num) {
zval *arg, *top;
if (UNEXPECTED(EG(exception) != NULL)) {
- ZEND_VM_C_GOTO(unpack_iter_dtor);
+ break;
}
arg = iter->funcs->get_current_data(iter);
if (UNEXPECTED(EG(exception) != NULL)) {
- ZEND_VM_C_GOTO(unpack_iter_dtor);
+ break;
}
if (iter->funcs->get_current_key) {
zval key;
iter->funcs->get_current_key(iter, &key);
if (UNEXPECTED(EG(exception) != NULL)) {
- ZEND_VM_C_GOTO(unpack_iter_dtor);
+ break;
}
if (Z_TYPE(key) == IS_STRING) {
zend_throw_error(NULL,
"Cannot unpack Traversable with string keys");
zend_string_release(Z_STR(key));
- ZEND_VM_C_GOTO(unpack_iter_dtor);
+ break;
}
zval_dtor(&key);
@@ -4542,12 +4570,8 @@ ZEND_VM_C_LABEL(send_again):
ZEND_CALL_NUM_ARGS(EX(call))++;
iter->funcs->move_forward(iter);
- if (UNEXPECTED(EG(exception) != NULL)) {
- ZEND_VM_C_GOTO(unpack_iter_dtor);
- }
}
-ZEND_VM_C_LABEL(unpack_iter_dtor):
zend_iterator_dtor(iter);
}
} else if (EXPECTED(Z_ISREF_P(args))) {
@@ -4569,7 +4593,6 @@ ZEND_VM_HANDLER(119, ZEND_SEND_ARRAY, ANY, ANY)
USE_OPLINE
zend_free_op free_op1;
zval *args;
- SAVE_OPLINE();
SAVE_OPLINE();
args = GET_OP1_ZVAL_PTR(BP_VAR_R);
@@ -4585,13 +4608,12 @@ ZEND_VM_HANDLER(119, ZEND_SEND_ARRAY, ANY, ANY)
if (ZEND_CALL_INFO(EX(call)) & ZEND_CALL_CLOSURE) {
OBJ_RELEASE((zend_object*)EX(call)->func->common.prototype);
}
- if (Z_OBJ(EX(call)->This)) {
+ if (Z_TYPE(EX(call)->This) == IS_OBJECT) {
OBJ_RELEASE(Z_OBJ(EX(call)->This));
}
EX(call)->func = (zend_function*)&zend_pass_function;
- EX(call)->called_scope = NULL;
Z_OBJ(EX(call)->This) = NULL;
- ZEND_SET_CALL_INFO(EX(call), ZEND_CALL_INFO(EX(call)) & ~ZEND_CALL_RELEASE_THIS);
+ ZEND_SET_CALL_INFO(EX(call), 0, ZEND_CALL_INFO(EX(call)) & ~ZEND_CALL_RELEASE_THIS);
} else {
uint32_t arg_num;
HashTable *ht;
@@ -4617,13 +4639,12 @@ ZEND_VM_C_LABEL(send_array):
if (ZEND_CALL_INFO(EX(call)) & ZEND_CALL_CLOSURE) {
OBJ_RELEASE((zend_object*)EX(call)->func->common.prototype);
}
- if (Z_OBJ(EX(call)->This)) {
+ if (Z_TYPE(EX(call)->This) == IS_OBJECT) {
OBJ_RELEASE(Z_OBJ(EX(call)->This));
}
EX(call)->func = (zend_function*)&zend_pass_function;
- EX(call)->called_scope = NULL;
Z_OBJ(EX(call)->This) = NULL;
- ZEND_SET_CALL_INFO(EX(call), ZEND_CALL_INFO(EX(call)) & ~ZEND_CALL_RELEASE_THIS);
+ ZEND_SET_CALL_INFO(EX(call), 0, ZEND_CALL_INFO(EX(call)) & ~ZEND_CALL_RELEASE_THIS);
break;
}
}
@@ -4644,7 +4665,7 @@ ZEND_VM_C_LABEL(send_array):
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
-ZEND_VM_HANDLER(120, ZEND_SEND_USER, VAR|CV, ANY)
+ZEND_VM_HANDLER(120, ZEND_SEND_USER, VAR|CV, NUM)
{
USE_OPLINE
zval *arg, *param;
@@ -4667,14 +4688,13 @@ ZEND_VM_HANDLER(120, ZEND_SEND_USER, VAR|CV, ANY)
if (ZEND_CALL_INFO(EX(call)) & ZEND_CALL_CLOSURE) {
OBJ_RELEASE((zend_object*)EX(call)->func->common.prototype);
}
- if (Z_OBJ(EX(call)->This)) {
+ if (Z_TYPE(EX(call)->This) == IS_OBJECT) {
OBJ_RELEASE(Z_OBJ(EX(call)->This));
}
ZVAL_UNDEF(param);
EX(call)->func = (zend_function*)&zend_pass_function;
- EX(call)->called_scope = NULL;
Z_OBJ(EX(call)->This) = NULL;
- ZEND_SET_CALL_INFO(EX(call), ZEND_CALL_INFO(EX(call)) & ~ZEND_CALL_RELEASE_THIS);
+ ZEND_SET_CALL_INFO(EX(call), 0, ZEND_CALL_INFO(EX(call)) & ~ZEND_CALL_RELEASE_THIS);
FREE_OP1();
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
@@ -4693,15 +4713,15 @@ ZEND_VM_HANDLER(120, ZEND_SEND_USER, VAR|CV, ANY)
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
-ZEND_VM_HANDLER(63, ZEND_RECV, ANY, ANY)
+ZEND_VM_HANDLER(63, ZEND_RECV, NUM, ANY)
{
USE_OPLINE
uint32_t arg_num = opline->op1.num;
if (UNEXPECTED(arg_num > EX_NUM_ARGS())) {
SAVE_OPLINE();
- zend_verify_missing_arg(execute_data, arg_num, CACHE_ADDR(opline->op2.num));
- ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
+ zend_missing_arg_error(execute_data);
+ HANDLE_EXCEPTION();
} else if (UNEXPECTED((EX(func)->op_array.fn_flags & ZEND_ACC_HAS_TYPE_HINTS) != 0)) {
zval *param = _get_zval_ptr_cv_undef_BP_VAR_W(execute_data, opline->result.var);
@@ -4714,7 +4734,7 @@ ZEND_VM_HANDLER(63, ZEND_RECV, ANY, ANY)
ZEND_VM_NEXT_OPCODE();
}
-ZEND_VM_HANDLER(64, ZEND_RECV_INIT, ANY, CONST)
+ZEND_VM_HANDLER(64, ZEND_RECV_INIT, NUM, CONST)
{
USE_OPLINE
uint32_t arg_num;
@@ -4725,18 +4745,13 @@ ZEND_VM_HANDLER(64, ZEND_RECV_INIT, ANY, CONST)
arg_num = opline->op1.num;
param = _get_zval_ptr_cv_undef_BP_VAR_W(execute_data, opline->result.var);
if (arg_num > EX_NUM_ARGS()) {
- ZVAL_COPY_VALUE(param, EX_CONSTANT(opline->op2));
+ ZVAL_COPY(param, EX_CONSTANT(opline->op2));
if (Z_OPT_CONSTANT_P(param)) {
SAVE_OPLINE();
- if (UNEXPECTED(zval_update_constant_ex(param, 0, NULL) != SUCCESS)) {
+ if (UNEXPECTED(zval_update_constant_ex(param, EX(func)->op_array.scope) != SUCCESS)) {
ZVAL_UNDEF(param);
HANDLE_EXCEPTION();
}
- } else {
- /* IS_CONST can't be IS_OBJECT, IS_RESOURCE or IS_REFERENCE */
- if (UNEXPECTED(Z_OPT_COPYABLE_P(param))) {
- zval_copy_ctor_func(param);
- }
}
}
@@ -4753,7 +4768,7 @@ ZEND_VM_HANDLER(64, ZEND_RECV_INIT, ANY, CONST)
ZEND_VM_NEXT_OPCODE();
}
-ZEND_VM_HANDLER(164, ZEND_RECV_VARIADIC, ANY, ANY)
+ZEND_VM_HANDLER(164, ZEND_RECV_VARIADIC, NUM, ANY)
{
USE_OPLINE
uint32_t arg_num = opline->op1.num;
@@ -4804,7 +4819,7 @@ ZEND_VM_HANDLER(52, ZEND_BOOL, CONST|TMPVAR|CV, ANY)
ZVAL_TRUE(EX_VAR(opline->result.var));
} else if (EXPECTED(Z_TYPE_INFO_P(val) <= IS_TRUE)) {
ZVAL_FALSE(EX_VAR(opline->result.var));
- if (UNEXPECTED(Z_TYPE_INFO_P(val) == IS_UNDEF)) {
+ if (OP1_TYPE == IS_CV && UNEXPECTED(Z_TYPE_INFO_P(val) == IS_UNDEF)) {
SAVE_OPLINE();
GET_OP1_UNDEF_CV(val, BP_VAR_R);
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
@@ -4892,12 +4907,13 @@ ZEND_VM_HANDLER(48, ZEND_CASE, CONST|TMPVAR|CV, CONST|TMPVAR|CV)
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
-ZEND_VM_HANDLER(68, ZEND_NEW, CONST|VAR, ANY)
+ZEND_VM_HANDLER(68, ZEND_NEW, UNUSED|CLASS_FETCH|CONST|VAR, ANY, NUM)
{
USE_OPLINE
- zval object_zval;
+ zval *result;
zend_function *constructor;
zend_class_entry *ce;
+ zend_execute_data *call;
SAVE_OPLINE();
if (OP1_TYPE == IS_CONST) {
@@ -4909,54 +4925,65 @@ ZEND_VM_HANDLER(68, ZEND_NEW, CONST|VAR, ANY)
}
CACHE_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)), ce);
}
+ } else if (OP1_TYPE == IS_UNUSED) {
+ ce = zend_fetch_class(NULL, opline->op1.num);
+ if (UNEXPECTED(ce == NULL)) {
+ ZEND_ASSERT(EG(exception));
+ HANDLE_EXCEPTION();
+ }
} else {
ce = Z_CE_P(EX_VAR(opline->op1.var));
}
- if (UNEXPECTED(object_init_ex(&object_zval, ce) != SUCCESS)) {
+
+ result = EX_VAR(opline->result.var);
+ if (UNEXPECTED(object_init_ex(result, ce) != SUCCESS)) {
HANDLE_EXCEPTION();
}
- constructor = Z_OBJ_HT(object_zval)->get_constructor(Z_OBJ(object_zval));
+ constructor = Z_OBJ_HT_P(result)->get_constructor(Z_OBJ_P(result));
if (constructor == NULL) {
- if (EXPECTED(RETURN_VALUE_USED(opline))) {
- ZVAL_COPY_VALUE(EX_VAR(opline->result.var), &object_zval);
- } else {
- OBJ_RELEASE(Z_OBJ(object_zval));
+ /* If there are no arguments, skip over the DO_FCALL opcode. We check if the next
+ * opcode is DO_FCALL in case EXT instructions are used. */
+ if (EXPECTED(opline->extended_value == 0 && (opline+1)->opcode == ZEND_DO_FCALL)) {
+ ZEND_VM_NEXT_OPCODE_EX(1, 2);
}
- ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2));
+
+ /* Perform a dummy function call */
+ call = zend_vm_stack_push_call_frame(
+ ZEND_CALL_FUNCTION, (zend_function *) &zend_pass_function,
+ opline->extended_value, NULL, NULL);
} else {
+ if (EXPECTED(constructor->type == ZEND_USER_FUNCTION) && UNEXPECTED(!constructor->op_array.run_time_cache)) {
+ init_func_run_time_cache(&constructor->op_array);
+ }
/* We are not handling overloaded classes right now */
- zend_execute_data *call = zend_vm_stack_push_call_frame(
- ZEND_CALL_FUNCTION | ZEND_CALL_RELEASE_THIS | ZEND_CALL_CTOR |
- (EXPECTED(RETURN_VALUE_USED(opline)) ? 0 : ZEND_CALL_CTOR_RESULT_UNUSED),
+ call = zend_vm_stack_push_call_frame(
+ ZEND_CALL_FUNCTION | ZEND_CALL_RELEASE_THIS | ZEND_CALL_CTOR,
constructor,
opline->extended_value,
ce,
- Z_OBJ(object_zval));
- call->prev_execute_data = EX(call);
- EX(call) = call;
-
- if (EXPECTED(RETURN_VALUE_USED(opline))) {
- ZVAL_COPY(EX_VAR(opline->result.var), &object_zval);
- }
-
- ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
+ Z_OBJ_P(result));
+ Z_ADDREF_P(result);
}
+
+ call->prev_execute_data = EX(call);
+ EX(call) = call;
+ ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
-ZEND_VM_HANDLER(110, ZEND_CLONE, CONST|TMPVAR|UNUSED|CV, ANY)
+ZEND_VM_HANDLER(110, ZEND_CLONE, CONST|TMPVAR|UNUSED|THIS|CV, ANY)
{
USE_OPLINE
zend_free_op free_op1;
zval *obj;
- zend_class_entry *ce;
+ zend_class_entry *ce, *scope;
zend_function *clone;
zend_object_clone_obj_t clone_call;
SAVE_OPLINE();
obj = GET_OP1_OBJ_ZVAL_PTR_UNDEF(BP_VAR_R);
- if (OP1_TYPE == IS_UNUSED && UNEXPECTED(Z_OBJ_P(obj) == NULL)) {
+ if (OP1_TYPE == IS_UNUSED && UNEXPECTED(Z_TYPE_P(obj) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
HANDLE_EXCEPTION();
}
@@ -4983,156 +5010,171 @@ ZEND_VM_HANDLER(110, ZEND_CLONE, CONST|TMPVAR|UNUSED|CV, ANY)
} while (0);
ce = Z_OBJCE_P(obj);
- clone = ce ? ce->clone : NULL;
- clone_call = Z_OBJ_HT_P(obj)->clone_obj;
+ clone = ce->clone;
+ clone_call = Z_OBJ_HT_P(obj)->clone_obj;
if (UNEXPECTED(clone_call == NULL)) {
- if (ce) {
- zend_throw_error(NULL, "Trying to clone an uncloneable object of class %s", ZSTR_VAL(ce->name));
- } else {
- zend_throw_error(NULL, "Trying to clone an uncloneable object");
- }
+ zend_throw_error(NULL, "Trying to clone an uncloneable object of class %s", ZSTR_VAL(ce->name));
FREE_OP1();
HANDLE_EXCEPTION();
}
- if (ce && clone) {
+ if (clone) {
if (clone->op_array.fn_flags & ZEND_ACC_PRIVATE) {
/* Ensure that if we're calling a private function, we're allowed to do so.
*/
- if (UNEXPECTED(ce != EG(scope))) {
- zend_throw_error(NULL, "Call to private %s::__clone() from context '%s'", ZSTR_VAL(ce->name), EG(scope) ? ZSTR_VAL(EG(scope)->name) : "");
+ scope = EX(func)->op_array.scope;
+ if (UNEXPECTED(ce != scope)) {
+ zend_throw_error(NULL, "Call to private %s::__clone() from context '%s'", ZSTR_VAL(ce->name), scope ? ZSTR_VAL(scope->name) : "");
FREE_OP1();
HANDLE_EXCEPTION();
}
} else if ((clone->common.fn_flags & ZEND_ACC_PROTECTED)) {
/* Ensure that if we're calling a protected function, we're allowed to do so.
*/
- if (UNEXPECTED(!zend_check_protected(zend_get_function_root_class(clone), EG(scope)))) {
- zend_throw_error(NULL, "Call to protected %s::__clone() from context '%s'", ZSTR_VAL(ce->name), EG(scope) ? ZSTR_VAL(EG(scope)->name) : "");
+ scope = EX(func)->op_array.scope;
+ if (UNEXPECTED(!zend_check_protected(zend_get_function_root_class(clone), scope))) {
+ zend_throw_error(NULL, "Call to protected %s::__clone() from context '%s'", ZSTR_VAL(ce->name), scope ? ZSTR_VAL(scope->name) : "");
FREE_OP1();
HANDLE_EXCEPTION();
}
}
}
- if (EXPECTED(EG(exception) == NULL)) {
- ZVAL_OBJ(EX_VAR(opline->result.var), clone_call(obj));
- if (UNEXPECTED(!RETURN_VALUE_USED(opline)) || UNEXPECTED(EG(exception) != NULL)) {
- OBJ_RELEASE(Z_OBJ_P(EX_VAR(opline->result.var)));
- }
+ ZVAL_OBJ(EX_VAR(opline->result.var), clone_call(obj));
+ if (UNEXPECTED(EG(exception) != NULL)) {
+ OBJ_RELEASE(Z_OBJ_P(EX_VAR(opline->result.var)));
}
+
FREE_OP1();
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
-ZEND_VM_HANDLER(99, ZEND_FETCH_CONSTANT, VAR|CONST|UNUSED, CONST)
+ZEND_VM_HANDLER(99, ZEND_FETCH_CONSTANT, UNUSED, CONST, CONST_FETCH)
{
USE_OPLINE
+ zend_constant *c;
SAVE_OPLINE();
- if (OP1_TYPE == IS_UNUSED) {
- zend_constant *c;
-
- if (EXPECTED(CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2))))) {
- c = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)));
- } else if ((c = zend_quick_get_constant(EX_CONSTANT(opline->op2) + 1, opline->extended_value)) == NULL) {
- if ((opline->extended_value & IS_CONSTANT_UNQUALIFIED) != 0) {
- char *actual = (char *)zend_memrchr(Z_STRVAL_P(EX_CONSTANT(opline->op2)), '\\', Z_STRLEN_P(EX_CONSTANT(opline->op2)));
- if (!actual) {
- ZVAL_STR_COPY(EX_VAR(opline->result.var), Z_STR_P(EX_CONSTANT(opline->op2)));
- } else {
- actual++;
- ZVAL_STRINGL(EX_VAR(opline->result.var),
- actual, Z_STRLEN_P(EX_CONSTANT(opline->op2)) - (actual - Z_STRVAL_P(EX_CONSTANT(opline->op2))));
- }
- /* non-qualified constant - allow text substitution */
- zend_error(E_NOTICE, "Use of undefined constant %s - assumed '%s'",
- Z_STRVAL_P(EX_VAR(opline->result.var)), Z_STRVAL_P(EX_VAR(opline->result.var)));
- ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
+
+ if (EXPECTED(CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2))))) {
+ c = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)));
+ } else if ((c = zend_quick_get_constant(EX_CONSTANT(opline->op2) + 1, opline->extended_value)) == NULL) {
+ if ((opline->extended_value & IS_CONSTANT_UNQUALIFIED) != 0) {
+ char *actual = (char *)zend_memrchr(Z_STRVAL_P(EX_CONSTANT(opline->op2)), '\\', Z_STRLEN_P(EX_CONSTANT(opline->op2)));
+ if (!actual) {
+ ZVAL_STR_COPY(EX_VAR(opline->result.var), Z_STR_P(EX_CONSTANT(opline->op2)));
} else {
- zend_throw_error(NULL, "Undefined constant '%s'", Z_STRVAL_P(EX_CONSTANT(opline->op2)));
- HANDLE_EXCEPTION();
+ actual++;
+ ZVAL_STRINGL(EX_VAR(opline->result.var),
+ actual, Z_STRLEN_P(EX_CONSTANT(opline->op2)) - (actual - Z_STRVAL_P(EX_CONSTANT(opline->op2))));
}
+ /* non-qualified constant - allow text substitution */
+ zend_error(E_NOTICE, "Use of undefined constant %s - assumed '%s'",
+ Z_STRVAL_P(EX_VAR(opline->result.var)), Z_STRVAL_P(EX_VAR(opline->result.var)));
+ ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
} else {
- CACHE_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)), c);
+ zend_throw_error(NULL, "Undefined constant '%s'", Z_STRVAL_P(EX_CONSTANT(opline->op2)));
+ HANDLE_EXCEPTION();
}
+ } else {
+ CACHE_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)), c);
+ }
+
#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
+ 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;
- do {
- if (OP1_TYPE == IS_CONST) {
- if (EXPECTED(CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2))))) {
- value = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)));
- ZVAL_DEREF(value);
+ ZEND_VM_NEXT_OPCODE();
+}
+
+ZEND_VM_HANDLER(181, ZEND_FETCH_CLASS_CONSTANT, VAR|CONST|UNUSED|CLASS_FETCH, CONST)
+{
+ zend_class_entry *ce, *scope;
+ zend_class_constant *c;
+ zval *value;
+ USE_OPLINE
+
+ SAVE_OPLINE();
+
+ do {
+ if (OP1_TYPE == IS_CONST) {
+ if (EXPECTED(CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2))))) {
+ value = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)));
#ifdef ZTS
- ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)));
+ ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)));
#endif
- break;
- } else if (EXPECTED(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(ce == NULL)) {
- if (EXPECTED(!EG(exception))) {
- zend_throw_error(NULL, "Class '%s' not found", Z_STRVAL_P(EX_CONSTANT(opline->op1)));
- }
- HANDLE_EXCEPTION();
+ break;
+ } else if (EXPECTED(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(ce == NULL)) {
+ if (EXPECTED(!EG(exception))) {
+ zend_throw_error(NULL, "Class '%s' not found", Z_STRVAL_P(EX_CONSTANT(opline->op1)));
}
- CACHE_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)), ce);
+ HANDLE_EXCEPTION();
+ }
+ CACHE_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)), ce);
+ }
+ } else {
+ if (OP1_TYPE == IS_UNUSED) {
+ ce = zend_fetch_class(NULL, opline->op1.num);
+ if (UNEXPECTED(ce == NULL)) {
+ ZEND_ASSERT(EG(exception));
+ 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;
- }
}
+ if ((value = CACHED_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)), ce)) != NULL) {
+ break;
+ }
+ }
- 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);
+ if (EXPECTED((c = zend_hash_find_ptr(&ce->constants_table, Z_STR_P(EX_CONSTANT(opline->op2)))) != NULL)) {
+ scope = EX(func)->op_array.scope;
+ if (!zend_verify_const_access(c, scope)) {
+ zend_throw_error(NULL, "Cannot access %s const %s::%s", zend_visibility_string(Z_ACCESS_FLAGS(c->value)), ZSTR_VAL(ce->name), Z_STRVAL_P(EX_CONSTANT(opline->op2)));
+ HANDLE_EXCEPTION();
+ }
+ value = &c->value;
+ if (Z_CONSTANT_P(value)) {
+ zval_update_constant_ex(value, ce);
+ if (UNEXPECTED(EG(exception) != NULL)) {
+ HANDLE_EXCEPTION();
}
+ }
+ if (OP1_TYPE == IS_CONST) {
+ CACHE_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)), value);
} else {
- zend_throw_error(NULL, "Undefined class constant '%s'", Z_STRVAL_P(EX_CONSTANT(opline->op2)));
- HANDLE_EXCEPTION();
+ CACHE_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)), ce, value);
}
- } while (0);
-#ifdef ZTS
- if (ce->type == ZEND_INTERNAL_CLASS) {
- ZVAL_DUP(EX_VAR(opline->result.var), value);
} else {
- ZVAL_COPY(EX_VAR(opline->result.var), value);
+ zend_throw_error(NULL, "Undefined class constant '%s'", Z_STRVAL_P(EX_CONSTANT(opline->op2)));
+ HANDLE_EXCEPTION();
}
-#else
+ } while (0);
+
+#ifdef ZTS
+ if (ce->type == ZEND_INTERNAL_CLASS) {
+ ZVAL_DUP(EX_VAR(opline->result.var), value);
+ } else {
ZVAL_COPY(EX_VAR(opline->result.var), value);
-#endif
}
+#else
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+#endif
+
ZEND_VM_NEXT_OPCODE();
}
-ZEND_VM_HANDLER(72, ZEND_ADD_ARRAY_ELEMENT, CONST|TMP|VAR|CV, CONST|TMPVAR|UNUSED|CV)
+ZEND_VM_HANDLER(72, ZEND_ADD_ARRAY_ELEMENT, CONST|TMP|VAR|CV, CONST|TMPVAR|UNUSED|NEXT|CV, REF)
{
USE_OPLINE
zend_free_op free_op1;
@@ -5142,11 +5184,6 @@ ZEND_VM_HANDLER(72, ZEND_ADD_ARRAY_ELEMENT, CONST|TMP|VAR|CV, CONST|TMPVAR|UNUSE
if ((OP1_TYPE == IS_VAR || OP1_TYPE == IS_CV) &&
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_throw_error(NULL, "Cannot create references to/from string offsets");
- zend_array_destroy(Z_ARRVAL_P(EX_VAR(opline->result.var)));
- HANDLE_EXCEPTION();
- }
ZVAL_MAKE_REF(expr_ptr);
Z_ADDREF_P(expr_ptr);
FREE_OP1_VAR_PTR();
@@ -5155,10 +5192,8 @@ ZEND_VM_HANDLER(72, ZEND_ADD_ARRAY_ELEMENT, CONST|TMP|VAR|CV, CONST|TMPVAR|UNUSE
if (OP1_TYPE == IS_TMP_VAR) {
/* pass */
} else if (OP1_TYPE == IS_CONST) {
- 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;
+ if (Z_REFCOUNTED_P(expr_ptr)) {
+ Z_ADDREF_P(expr_ptr);
}
} else if (OP1_TYPE == IS_CV) {
ZVAL_DEREF(expr_ptr);
@@ -5231,7 +5266,7 @@ ZEND_VM_C_LABEL(num_index):
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
-ZEND_VM_HANDLER(71, ZEND_INIT_ARRAY, CONST|TMP|VAR|UNUSED|CV, CONST|TMPVAR|UNUSED|CV)
+ZEND_VM_HANDLER(71, ZEND_INIT_ARRAY, CONST|TMP|VAR|UNUSED|CV, CONST|TMPVAR|UNUSED|NEXT|CV, ARRAY_INIT|REF)
{
zval *array;
uint32_t size;
@@ -5262,7 +5297,7 @@ ZEND_VM_HANDLER(71, ZEND_INIT_ARRAY, CONST|TMP|VAR|UNUSED|CV, CONST|TMPVAR|UNUSE
}
}
-ZEND_VM_HANDLER(21, ZEND_CAST, CONST|TMP|VAR|CV, ANY)
+ZEND_VM_HANDLER(21, ZEND_CAST, CONST|TMP|VAR|CV, ANY, TYPE)
{
USE_OPLINE
zend_free_op free_op1;
@@ -5310,11 +5345,9 @@ ZEND_VM_HANDLER(21, ZEND_CAST, CONST|TMP|VAR|CV, ANY)
if (Z_TYPE_P(expr) == opline->extended_value) {
ZVAL_COPY_VALUE(result, expr);
if (OP1_TYPE == IS_CONST) {
- if (UNEXPECTED(Z_OPT_COPYABLE_P(result))) {
- zval_copy_ctor_func(result);
- }
+ if (UNEXPECTED(Z_OPT_REFCOUNTED_P(result))) Z_ADDREF_P(result);
} else if (OP1_TYPE != IS_TMP_VAR) {
- if (Z_OPT_REFCOUNTED_P(expr)) Z_ADDREF_P(expr);
+ if (Z_OPT_REFCOUNTED_P(result)) Z_ADDREF_P(result);
}
FREE_OP1_IF_VAR();
@@ -5328,9 +5361,7 @@ ZEND_VM_HANDLER(21, ZEND_CAST, CONST|TMP|VAR|CV, ANY)
if (Z_TYPE_P(expr) != IS_NULL) {
expr = zend_hash_index_add_new(Z_ARRVAL_P(result), 0, expr);
if (OP1_TYPE == IS_CONST) {
- if (UNEXPECTED(Z_OPT_COPYABLE_P(expr))) {
- zval_copy_ctor_func(expr);
- }
+ if (UNEXPECTED(Z_OPT_REFCOUNTED_P(expr))) Z_ADDREF_P(expr);
} else {
if (Z_OPT_REFCOUNTED_P(expr)) Z_ADDREF_P(expr);
}
@@ -5344,11 +5375,9 @@ ZEND_VM_HANDLER(21, ZEND_CAST, CONST|TMP|VAR|CV, ANY)
if (Z_TYPE_P(expr) != IS_ARRAY) {
object_init(result);
if (Z_TYPE_P(expr) != IS_NULL) {
- expr = zend_hash_str_add_new(Z_OBJPROP_P(result), "scalar", sizeof("scalar")-1, expr);
+ expr = zend_hash_add_new(Z_OBJPROP_P(result), CG(known_strings)[ZEND_STR_SCALAR], expr);
if (OP1_TYPE == IS_CONST) {
- if (UNEXPECTED(Z_OPT_COPYABLE_P(expr))) {
- zval_copy_ctor_func(expr);
- }
+ if (UNEXPECTED(Z_OPT_REFCOUNTED_P(expr))) Z_ADDREF_P(expr);
} else {
if (Z_OPT_REFCOUNTED_P(expr)) Z_ADDREF_P(expr);
}
@@ -5364,92 +5393,23 @@ ZEND_VM_HANDLER(21, ZEND_CAST, CONST|TMP|VAR|CV, ANY)
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
-ZEND_VM_HANDLER(73, ZEND_INCLUDE_OR_EVAL, CONST|TMPVAR|CV, ANY)
+ZEND_VM_HANDLER(73, ZEND_INCLUDE_OR_EVAL, CONST|TMPVAR|CV, ANY, EVAL)
{
USE_OPLINE
- zend_op_array *new_op_array=NULL;
+ zend_op_array *new_op_array;
zend_free_op free_op1;
zval *inc_filename;
- zval tmp_inc_filename;
- zend_bool failure_retval=0;
SAVE_OPLINE();
- 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;
- }
-
- if (opline->extended_value != ZEND_EVAL && strlen(Z_STRVAL_P(inc_filename)) != Z_STRLEN_P(inc_filename)) {
- if (opline->extended_value == ZEND_INCLUDE_ONCE || opline->extended_value == ZEND_INCLUDE) {
- zend_message_dispatcher(ZMSG_FAILED_INCLUDE_FOPEN, Z_STRVAL_P(inc_filename));
- } else {
- zend_message_dispatcher(ZMSG_FAILED_REQUIRE_FOPEN, Z_STRVAL_P(inc_filename));
- }
- } else {
- switch (opline->extended_value) {
- case ZEND_INCLUDE_ONCE:
- case ZEND_REQUIRE_ONCE: {
- zend_file_handle file_handle;
- zend_string *resolved_path;
-
- resolved_path = zend_resolve_path(Z_STRVAL_P(inc_filename), (int)Z_STRLEN_P(inc_filename));
- if (resolved_path) {
- failure_retval = zend_hash_exists(&EG(included_files), resolved_path);
- } else {
- resolved_path = zend_string_copy(Z_STR_P(inc_filename));
- }
-
- if (failure_retval) {
- /* do nothing, file already included */
- } else if (SUCCESS == zend_stream_open(ZSTR_VAL(resolved_path), &file_handle)) {
-
- if (!file_handle.opened_path) {
- file_handle.opened_path = zend_string_copy(resolved_path);
- }
-
- if (zend_hash_add_empty_element(&EG(included_files), file_handle.opened_path)) {
- new_op_array = zend_compile_file(&file_handle, (opline->extended_value==ZEND_INCLUDE_ONCE?ZEND_INCLUDE:ZEND_REQUIRE));
- zend_destroy_file_handle(&file_handle);
- } else {
- zend_file_handle_dtor(&file_handle);
- failure_retval=1;
- }
- } else {
- if (opline->extended_value == ZEND_INCLUDE_ONCE) {
- zend_message_dispatcher(ZMSG_FAILED_INCLUDE_FOPEN, Z_STRVAL_P(inc_filename));
- } else {
- zend_message_dispatcher(ZMSG_FAILED_REQUIRE_FOPEN, Z_STRVAL_P(inc_filename));
- }
- }
- zend_string_release(resolved_path);
- }
- break;
- case ZEND_INCLUDE:
- case ZEND_REQUIRE:
- new_op_array = compile_filename(opline->extended_value, inc_filename);
- break;
- case ZEND_EVAL: {
- char *eval_desc = zend_make_compiled_string_description("eval()'d code");
-
- new_op_array = zend_compile_string(inc_filename, eval_desc);
- efree(eval_desc);
- }
- break;
- EMPTY_SWITCH_DEFAULT_CASE()
- }
- }
- if (Z_TYPE(tmp_inc_filename) != IS_UNDEF) {
- zend_string_release(Z_STR(tmp_inc_filename));
- }
+ inc_filename = GET_OP1_ZVAL_PTR(BP_VAR_R);
+ new_op_array = zend_include_or_eval(inc_filename, opline->extended_value);
FREE_OP1();
if (UNEXPECTED(EG(exception) != NULL)) {
HANDLE_EXCEPTION();
+ } else if (new_op_array == ZEND_FAKE_OP_ARRAY) {
+ if (RETURN_VALUE_USED(opline)) {
+ ZVAL_TRUE(EX_VAR(opline->result.var));
+ }
} else if (EXPECTED(new_op_array != NULL)) {
zval *return_value = NULL;
zend_execute_data *call;
@@ -5458,19 +5418,21 @@ ZEND_VM_HANDLER(73, ZEND_INCLUDE_OR_EVAL, CONST|TMPVAR|CV, ANY)
return_value = EX_VAR(opline->result.var);
}
- new_op_array->scope = EG(scope);
+ new_op_array->scope = EX(func)->op_array.scope;
- call = zend_vm_stack_push_call_frame(ZEND_CALL_NESTED_CODE,
- (zend_function*)new_op_array, 0, EX(called_scope), Z_OBJ(EX(This)));
+ call = zend_vm_stack_push_call_frame(ZEND_CALL_NESTED_CODE | ZEND_CALL_HAS_SYMBOL_TABLE,
+ (zend_function*)new_op_array, 0,
+ Z_TYPE(EX(This)) != IS_OBJECT ? Z_CE(EX(This)) : NULL,
+ Z_TYPE(EX(This)) == IS_OBJECT ? Z_OBJ(EX(This)) : NULL);
- if (EX(symbol_table)) {
+ if (EX_CALL_INFO() & ZEND_CALL_HAS_SYMBOL_TABLE) {
call->symbol_table = EX(symbol_table);
} else {
call->symbol_table = zend_rebuild_symbol_table();
}
call->prev_execute_data = execute_data;
- i_init_code_execute_data(call, new_op_array, return_value);
+ i_init_code_execute_data(call, new_op_array, return_value);
if (EXPECTED(zend_execute_ex == execute_ex)) {
ZEND_VM_ENTER();
} else {
@@ -5485,15 +5447,14 @@ ZEND_VM_HANDLER(73, ZEND_INCLUDE_OR_EVAL, CONST|TMPVAR|CV, ANY)
zend_throw_exception_internal(NULL);
HANDLE_EXCEPTION();
}
-
} else if (RETURN_VALUE_USED(opline)) {
- ZVAL_BOOL(EX_VAR(opline->result.var), failure_retval);
+ ZVAL_FALSE(EX_VAR(opline->result.var));
}
- ZEND_VM_INTERRUPT_CHECK();
- ZEND_VM_NEXT_OPCODE();
+ ZEND_VM_SET_OPCODE(opline + 1);
+ ZEND_VM_CONTINUE();
}
-ZEND_VM_HANDLER(74, ZEND_UNSET_VAR, CONST|TMPVAR|CV, UNUSED|CONST|VAR)
+ZEND_VM_HANDLER(74, ZEND_UNSET_VAR, CONST|TMPVAR|CV, UNUSED, VAR_FETCH|ISSET)
{
USE_OPLINE
zval tmp, *varname;
@@ -5502,7 +5463,6 @@ ZEND_VM_HANDLER(74, ZEND_UNSET_VAR, CONST|TMPVAR|CV, UNUSED|CONST|VAR)
SAVE_OPLINE();
if (OP1_TYPE == IS_CV &&
- OP2_TYPE == IS_UNUSED &&
(opline->extended_value & ZEND_QUICK_SET)) {
zval *var = EX_VAR(opline->op1.var);
@@ -5511,7 +5471,7 @@ ZEND_VM_HANDLER(74, ZEND_UNSET_VAR, CONST|TMPVAR|CV, UNUSED|CONST|VAR)
if (!--GC_REFCOUNT(garbage)) {
ZVAL_UNDEF(var);
- zval_dtor_func_for_ptr(garbage);
+ zval_dtor_func(garbage);
} else {
zval *z = var;
ZVAL_DEREF(z);
@@ -5539,33 +5499,66 @@ ZEND_VM_HANDLER(74, ZEND_UNSET_VAR, CONST|TMPVAR|CV, UNUSED|CONST|VAR)
varname = &tmp;
}
- if (OP2_TYPE != IS_UNUSED) {
- zend_class_entry *ce;
+ target_symbol_table = zend_get_target_symbol_table(execute_data, opline->extended_value & ZEND_FETCH_TYPE_MASK);
+ zend_hash_del_ind(target_symbol_table, Z_STR_P(varname));
- if (OP2_TYPE == IS_CONST) {
- ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)));
+ if (OP1_TYPE != IS_CONST && Z_TYPE(tmp) != IS_UNDEF) {
+ zend_string_release(Z_STR(tmp));
+ }
+ FREE_OP1();
+ ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
+}
+
+ZEND_VM_HANDLER(179, ZEND_UNSET_STATIC_PROP, CONST|TMPVAR|CV, UNUSED|CLASS_FETCH|CONST|VAR)
+{
+ USE_OPLINE
+ zval tmp, *varname;
+ zend_class_entry *ce;
+ zend_free_op free_op1;
+
+ SAVE_OPLINE();
+
+ 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;
+ }
+
+ if (OP2_TYPE == IS_CONST) {
+ ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)));
+ if (UNEXPECTED(ce == NULL)) {
+ ce = zend_fetch_class_by_name(Z_STR_P(EX_CONSTANT(opline->op2)), EX_CONSTANT(opline->op2) + 1, ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION);
if (UNEXPECTED(ce == NULL)) {
- ce = zend_fetch_class_by_name(Z_STR_P(EX_CONSTANT(opline->op2)), EX_CONSTANT(opline->op2) + 1, ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION);
- if (UNEXPECTED(ce == NULL)) {
- if (EXPECTED(!EG(exception))) {
- zend_throw_error(NULL, "Class '%s' not found", Z_STRVAL_P(EX_CONSTANT(opline->op2)));
- }
- if (OP1_TYPE != IS_CONST && Z_TYPE(tmp) != IS_UNDEF) {
- zend_string_release(Z_STR(tmp));
- }
- FREE_OP1();
- HANDLE_EXCEPTION();
+ if (EXPECTED(!EG(exception))) {
+ zend_throw_error(NULL, "Class '%s' not found", Z_STRVAL_P(EX_CONSTANT(opline->op2)));
}
- CACHE_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)), ce);
+ if (OP1_TYPE != IS_CONST && Z_TYPE(tmp) != IS_UNDEF) {
+ zend_string_release(Z_STR(tmp));
+ }
+ FREE_OP1();
+ HANDLE_EXCEPTION();
}
- } else {
- ce = Z_CE_P(EX_VAR(opline->op2.var));
+ CACHE_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)), ce);
+ }
+ } else if (OP2_TYPE == IS_UNUSED) {
+ ce = zend_fetch_class(NULL, opline->op2.num);
+ if (UNEXPECTED(ce == NULL)) {
+ ZEND_ASSERT(EG(exception));
+ if (OP1_TYPE != IS_CONST && Z_TYPE(tmp) != IS_UNDEF) {
+ zend_string_release(Z_STR(tmp));
+ }
+ FREE_OP1();
+ HANDLE_EXCEPTION();
}
- zend_std_unset_static_property(ce, Z_STR_P(varname));
} else {
- target_symbol_table = zend_get_target_symbol_table(execute_data, opline->extended_value & ZEND_FETCH_TYPE_MASK);
- zend_hash_del_ind(target_symbol_table, Z_STR_P(varname));
+ ce = Z_CE_P(EX_VAR(opline->op2.var));
}
+ zend_std_unset_static_property(ce, Z_STR_P(varname));
if (OP1_TYPE != IS_CONST && Z_TYPE(tmp) != IS_UNDEF) {
zend_string_release(Z_STR(tmp));
@@ -5574,7 +5567,7 @@ ZEND_VM_HANDLER(74, ZEND_UNSET_VAR, CONST|TMPVAR|CV, UNUSED|CONST|VAR)
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
-ZEND_VM_HANDLER(75, ZEND_UNSET_DIM, VAR|UNUSED|CV, CONST|TMPVAR|CV)
+ZEND_VM_HANDLER(75, ZEND_UNSET_DIM, VAR|CV, CONST|TMPVAR|CV)
{
USE_OPLINE
zend_free_op free_op1, free_op2;
@@ -5584,21 +5577,11 @@ ZEND_VM_HANDLER(75, ZEND_UNSET_DIM, VAR|UNUSED|CV, CONST|TMPVAR|CV)
zend_string *key;
SAVE_OPLINE();
- container = GET_OP1_OBJ_ZVAL_PTR_PTR(BP_VAR_UNSET);
- if (OP1_TYPE == IS_UNUSED && UNEXPECTED(Z_OBJ_P(container) == NULL)) {
- zend_throw_error(NULL, "Using $this when not in object context");
- FREE_UNFETCHED_OP2();
- HANDLE_EXCEPTION();
- }
- if (OP1_TYPE == IS_VAR && UNEXPECTED(container == NULL)) {
- zend_throw_error(NULL, "Cannot unset string offsets");
- FREE_UNFETCHED_OP2();
- HANDLE_EXCEPTION();
- }
+ container = GET_OP1_OBJ_ZVAL_PTR_PTR_UNDEF(BP_VAR_UNSET);
offset = GET_OP2_ZVAL_PTR_UNDEF(BP_VAR_R);
do {
- if (OP1_TYPE != IS_UNUSED && EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) {
+ if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) {
HashTable *ht;
ZEND_VM_C_LABEL(unset_dim_array):
@@ -5648,16 +5631,19 @@ ZEND_VM_C_LABEL(num_index_dim):
zend_error(E_WARNING, "Illegal offset type in unset");
}
break;
- } else if (OP1_TYPE != IS_UNUSED && Z_ISREF_P(container)) {
+ } else if (Z_ISREF_P(container)) {
container = Z_REFVAL_P(container);
if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) {
ZEND_VM_C_GOTO(unset_dim_array);
}
}
+ if (OP1_TYPE == IS_CV && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) {
+ container = GET_OP1_UNDEF_CV(container, BP_VAR_R);
+ }
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 || EXPECTED(Z_TYPE_P(container) == IS_OBJECT)) {
+ if (EXPECTED(Z_TYPE_P(container) == IS_OBJECT)) {
if (UNEXPECTED(Z_OBJ_HT_P(container)->unset_dimension == NULL)) {
zend_throw_error(NULL, "Cannot use object as array");
} else {
@@ -5673,7 +5659,7 @@ ZEND_VM_C_LABEL(num_index_dim):
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
-ZEND_VM_HANDLER(76, ZEND_UNSET_OBJ, VAR|UNUSED|CV, CONST|TMPVAR|CV)
+ZEND_VM_HANDLER(76, ZEND_UNSET_OBJ, VAR|UNUSED|THIS|CV, CONST|TMPVAR|CV)
{
USE_OPLINE
zend_free_op free_op1, free_op2;
@@ -5682,16 +5668,11 @@ ZEND_VM_HANDLER(76, ZEND_UNSET_OBJ, VAR|UNUSED|CV, CONST|TMPVAR|CV)
SAVE_OPLINE();
container = GET_OP1_OBJ_ZVAL_PTR_PTR(BP_VAR_UNSET);
- if (OP1_TYPE == IS_UNUSED && UNEXPECTED(Z_OBJ_P(container) == NULL)) {
+ if (OP1_TYPE == IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
FREE_UNFETCHED_OP2();
HANDLE_EXCEPTION();
}
- if (OP1_TYPE == IS_VAR && UNEXPECTED(container == NULL)) {
- zend_throw_error(NULL, "Cannot unset string offsets");
- FREE_UNFETCHED_OP2();
- HANDLE_EXCEPTION();
- }
offset = GET_OP2_ZVAL_PTR(BP_VAR_R);
do {
@@ -5717,7 +5698,7 @@ ZEND_VM_HANDLER(76, ZEND_UNSET_OBJ, VAR|UNUSED|CV, CONST|TMPVAR|CV)
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
-ZEND_VM_HANDLER(77, ZEND_FE_RESET_R, CONST|TMP|VAR|CV, ANY)
+ZEND_VM_HANDLER(77, ZEND_FE_RESET_R, CONST|TMP|VAR|CV, JMP_ADDR)
{
USE_OPLINE
zend_free_op free_op1;
@@ -5747,6 +5728,13 @@ ZEND_VM_HANDLER(77, ZEND_FE_RESET_R, CONST|TMP|VAR|CV, ANY)
if (OP1_TYPE != IS_TMP_VAR) {
Z_ADDREF_P(array_ptr);
}
+ if (Z_OBJ_P(array_ptr)->properties
+ && UNEXPECTED(GC_REFCOUNT(Z_OBJ_P(array_ptr)->properties) > 1)) {
+ if (EXPECTED(!(GC_FLAGS(Z_OBJ_P(array_ptr)->properties) & IS_ARRAY_IMMUTABLE))) {
+ GC_REFCOUNT(Z_OBJ_P(array_ptr)->properties)--;
+ }
+ Z_OBJ_P(array_ptr)->properties = zend_array_dup(Z_OBJ_P(array_ptr)->properties);
+ }
fe_ht = Z_OBJPROP_P(array_ptr);
pos = 0;
p = fe_ht->arData;
@@ -5822,7 +5810,7 @@ ZEND_VM_HANDLER(77, ZEND_FE_RESET_R, CONST|TMP|VAR|CV, ANY)
}
}
-ZEND_VM_HANDLER(125, ZEND_FE_RESET_RW, CONST|TMP|VAR|CV, ANY)
+ZEND_VM_HANDLER(125, ZEND_FE_RESET_RW, CONST|TMP|VAR|CV, JMP_ADDR)
{
USE_OPLINE
zend_free_op free_op1;
@@ -5893,6 +5881,13 @@ ZEND_VM_HANDLER(125, ZEND_FE_RESET_RW, CONST|TMP|VAR|CV, ANY)
array_ptr = EX_VAR(opline->result.var);
ZVAL_COPY_VALUE(array_ptr, array_ref);
}
+ if (Z_OBJ_P(array_ptr)->properties
+ && UNEXPECTED(GC_REFCOUNT(Z_OBJ_P(array_ptr)->properties) > 1)) {
+ if (EXPECTED(!(GC_FLAGS(Z_OBJ_P(array_ptr)->properties) & IS_ARRAY_IMMUTABLE))) {
+ GC_REFCOUNT(Z_OBJ_P(array_ptr)->properties)--;
+ }
+ Z_OBJ_P(array_ptr)->properties = zend_array_dup(Z_OBJ_P(array_ptr)->properties);
+ }
fe_ht = Z_OBJPROP_P(array_ptr);
p = fe_ht->arData;
while (1) {
@@ -5987,7 +5982,7 @@ ZEND_VM_HANDLER(125, ZEND_FE_RESET_RW, CONST|TMP|VAR|CV, ANY)
}
}
-ZEND_VM_HANDLER(78, ZEND_FE_FETCH_R, VAR, ANY)
+ZEND_VM_HANDLER(78, ZEND_FE_FETCH_R, VAR, ANY, JMP_ADDR)
{
USE_OPLINE
zval *array;
@@ -6026,7 +6021,7 @@ ZEND_VM_HANDLER(78, ZEND_FE_FETCH_R, VAR, ANY)
break;
}
Z_FE_POS_P(array) = pos + 1;
- if (opline->result_type == IS_TMP_VAR) {
+ if (opline->result_type & (IS_TMP_VAR|IS_CV)) {
if (!p->key) {
ZVAL_LONG(EX_VAR(opline->result.var), p->h);
} else {
@@ -6070,7 +6065,7 @@ ZEND_VM_HANDLER(78, ZEND_FE_FETCH_R, VAR, ANY)
pos++;
p++;
}
- if (opline->result_type == IS_TMP_VAR) {
+ if (opline->result_type & (IS_TMP_VAR|IS_CV)) {
if (UNEXPECTED(!p->key)) {
ZVAL_LONG(EX_VAR(opline->result.var), p->h);
} else if (ZSTR_VAL(p->key)[0]) {
@@ -6105,13 +6100,11 @@ ZEND_VM_HANDLER(78, ZEND_FE_FETCH_R, VAR, ANY)
* In case that ever happens we need an additional flag. */
iter->funcs->move_forward(iter);
if (UNEXPECTED(EG(exception) != NULL)) {
- zval_ptr_dtor(array);
HANDLE_EXCEPTION();
}
if (UNEXPECTED(iter->funcs->valid(iter) == FAILURE)) {
/* reached end of iteration */
if (UNEXPECTED(EG(exception) != NULL)) {
- zval_ptr_dtor(array);
HANDLE_EXCEPTION();
}
ZEND_VM_C_GOTO(fe_fetch_r_exit);
@@ -6119,18 +6112,16 @@ ZEND_VM_HANDLER(78, ZEND_FE_FETCH_R, VAR, ANY)
}
value = iter->funcs->get_current_data(iter);
if (UNEXPECTED(EG(exception) != NULL)) {
- zval_ptr_dtor(array);
HANDLE_EXCEPTION();
}
if (!value) {
/* failure in get_current_data */
ZEND_VM_C_GOTO(fe_fetch_r_exit);
}
- if (opline->result_type == IS_TMP_VAR) {
+ if (opline->result_type & (IS_TMP_VAR|IS_CV)) {
if (iter->funcs->get_current_key) {
iter->funcs->get_current_key(iter, EX_VAR(opline->result.var));
if (UNEXPECTED(EG(exception) != NULL)) {
- zval_ptr_dtor(array);
HANDLE_EXCEPTION();
}
} else {
@@ -6164,7 +6155,7 @@ ZEND_VM_C_LABEL(fe_fetch_r_exit):
ZEND_VM_NEXT_OPCODE();
}
-ZEND_VM_HANDLER(126, ZEND_FE_FETCH_RW, VAR, ANY)
+ZEND_VM_HANDLER(126, ZEND_FE_FETCH_RW, VAR, ANY, JMP_ADDR)
{
USE_OPLINE
zval *array;
@@ -6204,7 +6195,7 @@ ZEND_VM_HANDLER(126, ZEND_FE_FETCH_RW, VAR, ANY)
}
break;
}
- if (opline->result_type == IS_TMP_VAR) {
+ if (opline->result_type & (IS_TMP_VAR|IS_CV)) {
if (!p->key) {
ZVAL_LONG(EX_VAR(opline->result.var), p->h);
} else {
@@ -6262,7 +6253,7 @@ ZEND_VM_HANDLER(126, ZEND_FE_FETCH_RW, VAR, ANY)
pos++;
p++;
}
- if (opline->result_type == IS_TMP_VAR) {
+ if (opline->result_type & (IS_TMP_VAR|IS_CV)) {
if (UNEXPECTED(!p->key)) {
ZVAL_LONG(EX_VAR(opline->result.var), p->h);
} else if (ZSTR_VAL(p->key)[0]) {
@@ -6297,13 +6288,11 @@ ZEND_VM_HANDLER(126, ZEND_FE_FETCH_RW, VAR, ANY)
* In case that ever happens we need an additional flag. */
iter->funcs->move_forward(iter);
if (UNEXPECTED(EG(exception) != NULL)) {
- zval_ptr_dtor(array);
HANDLE_EXCEPTION();
}
if (UNEXPECTED(iter->funcs->valid(iter) == FAILURE)) {
/* reached end of iteration */
if (UNEXPECTED(EG(exception) != NULL)) {
- zval_ptr_dtor(array);
HANDLE_EXCEPTION();
}
ZEND_VM_C_GOTO(fe_fetch_w_exit);
@@ -6311,18 +6300,16 @@ ZEND_VM_HANDLER(126, ZEND_FE_FETCH_RW, VAR, ANY)
}
value = iter->funcs->get_current_data(iter);
if (UNEXPECTED(EG(exception) != NULL)) {
- zval_ptr_dtor(array);
HANDLE_EXCEPTION();
}
if (!value) {
/* failure in get_current_data */
ZEND_VM_C_GOTO(fe_fetch_w_exit);
}
- if (opline->result_type == IS_TMP_VAR) {
+ if (opline->result_type & (IS_TMP_VAR|IS_CV)) {
if (iter->funcs->get_current_key) {
iter->funcs->get_current_key(iter, EX_VAR(opline->result.var));
if (UNEXPECTED(EG(exception) != NULL)) {
- zval_ptr_dtor(array);
HANDLE_EXCEPTION();
}
} else {
@@ -6365,14 +6352,13 @@ ZEND_VM_C_LABEL(fe_fetch_w_exit):
ZEND_VM_NEXT_OPCODE();
}
-ZEND_VM_HANDLER(114, ZEND_ISSET_ISEMPTY_VAR, CONST|TMPVAR|CV, UNUSED|CONST|VAR)
+ZEND_VM_HANDLER(114, ZEND_ISSET_ISEMPTY_VAR, CONST|TMPVAR|CV, UNUSED, VAR_FETCH|ISSET)
{
USE_OPLINE
zval *value;
int result;
if (OP1_TYPE == IS_CV &&
- OP2_TYPE == IS_UNUSED &&
(opline->extended_value & ZEND_QUICK_SET)) {
value = EX_VAR(opline->op1.var);
if (opline->extended_value & ZEND_ISSET) {
@@ -6393,6 +6379,7 @@ ZEND_VM_HANDLER(114, ZEND_ISSET_ISEMPTY_VAR, CONST|TMPVAR|CV, UNUSED|CONST|VAR)
} else {
zend_free_op free_op1;
zval tmp, *varname;
+ HashTable *target_symbol_table;
SAVE_OPLINE();
varname = GET_OP1_ZVAL_PTR(BP_VAR_IS);
@@ -6402,56 +6389,14 @@ ZEND_VM_HANDLER(114, ZEND_ISSET_ISEMPTY_VAR, CONST|TMPVAR|CV, UNUSED|CONST|VAR)
varname = &tmp;
}
- if (OP2_TYPE != IS_UNUSED) {
- zend_class_entry *ce;
-
- if (OP2_TYPE == IS_CONST) {
- if (OP1_TYPE == IS_CONST && EXPECTED((ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)))) != NULL)) {
- value = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)) + sizeof(void*));
-
- /* check if static properties were destoyed */
- if (UNEXPECTED(CE_STATIC_MEMBERS(ce) == NULL)) {
- value = NULL;
- }
-
- ZEND_VM_C_GOTO(is_var_return);
- } else if (UNEXPECTED((ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)))) == NULL)) {
- ce = zend_fetch_class_by_name(Z_STR_P(EX_CONSTANT(opline->op2)), EX_CONSTANT(opline->op2) + 1, ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION);
- if (UNEXPECTED(ce == NULL)) {
- ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
- }
- CACHE_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)), ce);
- }
- } else {
- ce = Z_CE_P(EX_VAR(opline->op2.var));
- if (OP1_TYPE == IS_CONST &&
- (value = CACHED_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)), ce)) != NULL) {
-
- /* check if static properties were destoyed */
- if (UNEXPECTED(CE_STATIC_MEMBERS(ce) == NULL)) {
- value = NULL;
- }
-
- ZEND_VM_C_GOTO(is_var_return);
- }
- }
-
- value = zend_std_get_static_property(ce, Z_STR_P(varname), 1);
-
- if (OP1_TYPE == IS_CONST && value) {
- CACHE_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)), ce, value);
- }
- } else {
- HashTable *target_symbol_table = zend_get_target_symbol_table(execute_data, opline->extended_value & ZEND_FETCH_TYPE_MASK);
- value = zend_hash_find_ind(target_symbol_table, Z_STR_P(varname));
- }
+ target_symbol_table = zend_get_target_symbol_table(execute_data, opline->extended_value & ZEND_FETCH_TYPE_MASK);
+ value = zend_hash_find_ind(target_symbol_table, Z_STR_P(varname));
if (OP1_TYPE != IS_CONST && Z_TYPE(tmp) != IS_UNDEF) {
zend_string_release(Z_STR(tmp));
}
FREE_OP1();
-ZEND_VM_C_LABEL(is_var_return):
if (opline->extended_value & ZEND_ISSET) {
result = value && Z_TYPE_P(value) > IS_NULL &&
(!Z_ISREF_P(value) || Z_TYPE_P(Z_REFVAL_P(value)) != IS_NULL);
@@ -6465,7 +6410,91 @@ ZEND_VM_C_LABEL(is_var_return):
}
}
-ZEND_VM_HANDLER(115, ZEND_ISSET_ISEMPTY_DIM_OBJ, CONST|TMPVAR|UNUSED|CV, CONST|TMPVAR|CV)
+ZEND_VM_HANDLER(180, ZEND_ISSET_ISEMPTY_STATIC_PROP, CONST|TMPVAR|CV, UNUSED|CLASS_FETCH|CONST|VAR, ISSET)
+{
+ USE_OPLINE
+ zval *value;
+ int result;
+ zend_free_op free_op1;
+ zval tmp, *varname;
+ zend_class_entry *ce;
+
+ SAVE_OPLINE();
+ varname = GET_OP1_ZVAL_PTR(BP_VAR_IS);
+ ZVAL_UNDEF(&tmp);
+ if (OP1_TYPE != IS_CONST && Z_TYPE_P(varname) != IS_STRING) {
+ ZVAL_STR(&tmp, zval_get_string(varname));
+ varname = &tmp;
+ }
+
+ if (OP2_TYPE == IS_CONST) {
+ if (OP1_TYPE == IS_CONST && EXPECTED((ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)))) != NULL)) {
+ value = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)) + sizeof(void*));
+
+ /* check if static properties were destoyed */
+ if (UNEXPECTED(CE_STATIC_MEMBERS(ce) == NULL)) {
+ value = NULL;
+ }
+
+ ZEND_VM_C_GOTO(is_static_prop_return);
+ } else if (UNEXPECTED((ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)))) == NULL)) {
+ ce = zend_fetch_class_by_name(Z_STR_P(EX_CONSTANT(opline->op2)), EX_CONSTANT(opline->op2) + 1, ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION);
+ if (UNEXPECTED(ce == NULL)) {
+ ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
+ }
+ CACHE_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)), ce);
+ }
+ } else {
+ if (OP2_TYPE == IS_UNUSED) {
+ ce = zend_fetch_class(NULL, opline->op2.num);
+ if (UNEXPECTED(ce == NULL)) {
+ ZEND_ASSERT(EG(exception));
+ if (OP1_TYPE != IS_CONST && Z_TYPE(tmp) != IS_UNDEF) {
+ zend_string_release(Z_STR(tmp));
+ }
+ FREE_OP1();
+ HANDLE_EXCEPTION();
+ }
+ } else {
+ ce = Z_CE_P(EX_VAR(opline->op2.var));
+ }
+ if (OP1_TYPE == IS_CONST &&
+ (value = CACHED_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)), ce)) != NULL) {
+
+ /* check if static properties were destoyed */
+ if (UNEXPECTED(CE_STATIC_MEMBERS(ce) == NULL)) {
+ value = NULL;
+ }
+
+ ZEND_VM_C_GOTO(is_static_prop_return);
+ }
+ }
+
+ value = zend_std_get_static_property(ce, Z_STR_P(varname), 1);
+
+ if (OP1_TYPE == IS_CONST && value) {
+ CACHE_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)), ce, value);
+ }
+
+ if (OP1_TYPE != IS_CONST && Z_TYPE(tmp) != IS_UNDEF) {
+ zend_string_release(Z_STR(tmp));
+ }
+ FREE_OP1();
+
+ZEND_VM_C_LABEL(is_static_prop_return):
+ if (opline->extended_value & ZEND_ISSET) {
+ result = value && Z_TYPE_P(value) > IS_NULL &&
+ (!Z_ISREF_P(value) || Z_TYPE_P(Z_REFVAL_P(value)) != IS_NULL);
+ } else /* if (opline->extended_value & ZEND_ISEMPTY) */ {
+ result = !value || !i_zend_is_true(value);
+ }
+
+ ZEND_VM_SMART_BRANCH(result, 1);
+ ZVAL_BOOL(EX_VAR(opline->result.var), result);
+ ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
+}
+
+ZEND_VM_HANDLER(115, ZEND_ISSET_ISEMPTY_DIM_OBJ, CONST|TMPVAR|CV, CONST|TMPVAR|CV, ISSET)
{
USE_OPLINE
zend_free_op free_op1, free_op2;
@@ -6475,17 +6504,10 @@ ZEND_VM_HANDLER(115, ZEND_ISSET_ISEMPTY_DIM_OBJ, CONST|TMPVAR|UNUSED|CV, CONST|T
zval *offset;
SAVE_OPLINE();
- container = GET_OP1_OBJ_ZVAL_PTR(BP_VAR_IS);
-
- if (OP1_TYPE == IS_UNUSED && UNEXPECTED(Z_OBJ_P(container) == NULL)) {
- zend_throw_error(NULL, "Using $this when not in object context");
- FREE_UNFETCHED_OP2();
- HANDLE_EXCEPTION();
- }
-
+ container = GET_OP1_OBJ_ZVAL_PTR_UNDEF(BP_VAR_IS);
offset = GET_OP2_ZVAL_PTR_UNDEF(BP_VAR_R);
- if (OP1_TYPE != IS_UNUSED && EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) {
+ if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) {
HashTable *ht;
zval *value;
zend_string *str;
@@ -6552,8 +6574,7 @@ ZEND_VM_C_LABEL(num_index_prop):
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 ((OP1_TYPE != IS_CONST && EXPECTED(Z_TYPE_P(container) == IS_OBJECT))) {
if (EXPECTED(Z_OBJ_HT_P(container)->has_dimension)) {
result =
((opline->extended_value & ZEND_ISSET) == 0) ^
@@ -6568,6 +6589,9 @@ ZEND_VM_C_LABEL(num_index_prop):
if (EXPECTED(Z_TYPE_P(offset) == IS_LONG)) {
lval = Z_LVAL_P(offset);
ZEND_VM_C_LABEL(isset_str_offset):
+ if (UNEXPECTED(lval < 0)) { /* Handle negative offset */
+ lval += (zend_long)Z_STRLEN_P(container);
+ }
if (EXPECTED(lval >= 0) && (size_t)lval < Z_STRLEN_P(container)) {
if (opline->extended_value & ZEND_ISSET) {
result = 1;
@@ -6602,7 +6626,7 @@ ZEND_VM_C_LABEL(isset_dim_obj_exit):
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
-ZEND_VM_HANDLER(148, ZEND_ISSET_ISEMPTY_PROP_OBJ, CONST|TMPVAR|UNUSED|CV, CONST|TMPVAR|CV)
+ZEND_VM_HANDLER(148, ZEND_ISSET_ISEMPTY_PROP_OBJ, CONST|TMPVAR|UNUSED|THIS|CV, CONST|TMPVAR|CV, ISSET)
{
USE_OPLINE
zend_free_op free_op1, free_op2;
@@ -6613,7 +6637,7 @@ ZEND_VM_HANDLER(148, ZEND_ISSET_ISEMPTY_PROP_OBJ, CONST|TMPVAR|UNUSED|CV, CONST|
SAVE_OPLINE();
container = GET_OP1_OBJ_ZVAL_PTR(BP_VAR_IS);
- if (OP1_TYPE == IS_UNUSED && UNEXPECTED(Z_OBJ_P(container) == NULL)) {
+ if (OP1_TYPE == IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
FREE_UNFETCHED_OP2();
HANDLE_EXCEPTION();
@@ -6688,7 +6712,7 @@ ZEND_VM_HANDLER(57, ZEND_BEGIN_SILENCE, ANY, ANY)
do {
EG(error_reporting) = 0;
if (!EG(error_reporting_ini_entry)) {
- zend_ini_entry *p = zend_hash_str_find_ptr(EG(ini_directives), "error_reporting", sizeof("error_reporting")-1);
+ zend_ini_entry *p = zend_hash_find_ptr(EG(ini_directives), CG(known_strings)[ZEND_STR_ERROR_REPORTING]);
if (p) {
EG(error_reporting_ini_entry) = p;
} else {
@@ -6700,7 +6724,7 @@ ZEND_VM_HANDLER(57, ZEND_BEGIN_SILENCE, ANY, ANY)
ALLOC_HASHTABLE(EG(modified_ini_directives));
zend_hash_init(EG(modified_ini_directives), 8, NULL, NULL, 0);
}
- if (EXPECTED(zend_hash_str_add_ptr(EG(modified_ini_directives), "error_reporting", sizeof("error_reporting")-1, EG(error_reporting_ini_entry)) != NULL)) {
+ if (EXPECTED(zend_hash_add_ptr(EG(modified_ini_directives), CG(known_strings)[ZEND_STR_ERROR_REPORTING], EG(error_reporting_ini_entry)) != NULL)) {
EG(error_reporting_ini_entry)->orig_value = EG(error_reporting_ini_entry)->value;
EG(error_reporting_ini_entry)->orig_modifiable = EG(error_reporting_ini_entry)->modifiable;
EG(error_reporting_ini_entry)->modified = 1;
@@ -6721,7 +6745,7 @@ ZEND_VM_HANDLER(58, ZEND_END_SILENCE, TMP, ANY)
ZEND_VM_NEXT_OPCODE();
}
-ZEND_VM_HANDLER(152, ZEND_JMP_SET, CONST|TMP|VAR|CV, ANY)
+ZEND_VM_HANDLER(152, ZEND_JMP_SET, CONST|TMP|VAR|CV, JMP_ADDR)
{
USE_OPLINE
zend_free_op free_op1;
@@ -6738,20 +6762,20 @@ ZEND_VM_HANDLER(152, ZEND_JMP_SET, CONST|TMP|VAR|CV, ANY)
value = Z_REFVAL_P(value);
}
if (i_zend_is_true(value)) {
- ZVAL_COPY_VALUE(EX_VAR(opline->result.var), value);
+ zval *result = EX_VAR(opline->result.var);
+
+ ZVAL_COPY_VALUE(result, value);
if (OP1_TYPE == IS_CONST) {
- if (UNEXPECTED(Z_OPT_COPYABLE_P(value))) {
- zval_copy_ctor_func(EX_VAR(opline->result.var));
- }
+ if (UNEXPECTED(Z_OPT_REFCOUNTED_P(result))) Z_ADDREF_P(result);
} else if (OP1_TYPE == IS_CV) {
- if (Z_OPT_REFCOUNTED_P(value)) Z_ADDREF_P(value);
+ if (Z_OPT_REFCOUNTED_P(result)) Z_ADDREF_P(result);
} else if (OP1_TYPE == IS_VAR && ref) {
zend_reference *r = Z_REF_P(ref);
if (UNEXPECTED(--GC_REFCOUNT(r) == 0)) {
efree_size(r, sizeof(zend_reference));
- } else if (Z_OPT_REFCOUNTED_P(value)) {
- Z_ADDREF_P(value);
+ } else if (Z_OPT_REFCOUNTED_P(result)) {
+ Z_ADDREF_P(result);
}
}
ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2));
@@ -6761,7 +6785,7 @@ ZEND_VM_HANDLER(152, ZEND_JMP_SET, CONST|TMP|VAR|CV, ANY)
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
-ZEND_VM_HANDLER(169, ZEND_COALESCE, CONST|TMP|VAR|CV, ANY)
+ZEND_VM_HANDLER(169, ZEND_COALESCE, CONST|TMP|VAR|CV, JMP_ADDR)
{
USE_OPLINE
zend_free_op free_op1;
@@ -6779,20 +6803,19 @@ ZEND_VM_HANDLER(169, ZEND_COALESCE, CONST|TMP|VAR|CV, ANY)
}
if (Z_TYPE_P(value) > IS_NULL) {
- ZVAL_COPY_VALUE(EX_VAR(opline->result.var), value);
+ zval *result = EX_VAR(opline->result.var);
+ ZVAL_COPY_VALUE(result, value);
if (OP1_TYPE == IS_CONST) {
- if (UNEXPECTED(Z_OPT_COPYABLE_P(value))) {
- zval_copy_ctor_func(EX_VAR(opline->result.var));
- }
+ if (UNEXPECTED(Z_OPT_REFCOUNTED_P(result))) Z_ADDREF_P(result);
} else if (OP1_TYPE == IS_CV) {
- if (Z_OPT_REFCOUNTED_P(value)) Z_ADDREF_P(value);
+ if (Z_OPT_REFCOUNTED_P(result)) Z_ADDREF_P(result);
} else if (OP1_TYPE == IS_VAR && ref) {
zend_reference *r = Z_REF_P(ref);
if (UNEXPECTED(--GC_REFCOUNT(r) == 0)) {
efree_size(r, sizeof(zend_reference));
- } else if (Z_OPT_REFCOUNTED_P(value)) {
- Z_ADDREF_P(value);
+ } else if (Z_OPT_REFCOUNTED_P(result)) {
+ Z_ADDREF_P(result);
}
}
ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2));
@@ -6834,8 +6857,8 @@ ZEND_VM_HANDLER(22, ZEND_QM_ASSIGN, CONST|TMP|VAR|CV, ANY)
} else {
ZVAL_COPY_VALUE(result, value);
if (OP1_TYPE == IS_CONST) {
- if (UNEXPECTED(Z_OPT_COPYABLE_P(value))) {
- zval_copy_ctor_func(result);
+ if (UNEXPECTED(Z_OPT_REFCOUNTED_P(result))) {
+ Z_ADDREF_P(result);
}
}
}
@@ -6848,7 +6871,7 @@ ZEND_VM_HANDLER(101, ZEND_EXT_STMT, ANY, ANY)
if (!EG(no_extensions)) {
SAVE_OPLINE();
- zend_llist_apply_with_argument(&zend_extensions, (llist_apply_with_arg_func_t) zend_extension_statement_handler, EX(func));
+ zend_llist_apply_with_argument(&zend_extensions, (llist_apply_with_arg_func_t) zend_extension_statement_handler, execute_data);
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
ZEND_VM_NEXT_OPCODE();
@@ -6860,7 +6883,7 @@ ZEND_VM_HANDLER(102, ZEND_EXT_FCALL_BEGIN, ANY, ANY)
if (!EG(no_extensions)) {
SAVE_OPLINE();
- zend_llist_apply_with_argument(&zend_extensions, (llist_apply_with_arg_func_t) zend_extension_fcall_begin_handler, EX(func));
+ zend_llist_apply_with_argument(&zend_extensions, (llist_apply_with_arg_func_t) zend_extension_fcall_begin_handler, execute_data);
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
ZEND_VM_NEXT_OPCODE();
@@ -6872,7 +6895,7 @@ ZEND_VM_HANDLER(103, ZEND_EXT_FCALL_END, ANY, ANY)
if (!EG(no_extensions)) {
SAVE_OPLINE();
- zend_llist_apply_with_argument(&zend_extensions, (llist_apply_with_arg_func_t) zend_extension_fcall_end_handler, EX(func));
+ zend_llist_apply_with_argument(&zend_extensions, (llist_apply_with_arg_func_t) zend_extension_fcall_end_handler, execute_data);
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
ZEND_VM_NEXT_OPCODE();
@@ -6887,41 +6910,42 @@ ZEND_VM_HANDLER(139, ZEND_DECLARE_CLASS, ANY, ANY)
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
-ZEND_VM_HANDLER(140, ZEND_DECLARE_INHERITED_CLASS, ANY, ANY)
+ZEND_VM_HANDLER(140, ZEND_DECLARE_INHERITED_CLASS, ANY, VAR)
{
USE_OPLINE
SAVE_OPLINE();
- Z_CE_P(EX_VAR(opline->result.var)) = do_bind_inherited_class(&EX(func)->op_array, opline, EG(class_table), Z_CE_P(EX_VAR(opline->extended_value)), 0);
+ Z_CE_P(EX_VAR(opline->result.var)) = do_bind_inherited_class(&EX(func)->op_array, opline, EG(class_table), Z_CE_P(EX_VAR(opline->op2.var)), 0);
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
-ZEND_VM_HANDLER(145, ZEND_DECLARE_INHERITED_CLASS_DELAYED, ANY, ANY)
+ZEND_VM_HANDLER(145, ZEND_DECLARE_INHERITED_CLASS_DELAYED, ANY, VAR)
{
USE_OPLINE
zval *zce, *orig_zce;
SAVE_OPLINE();
- if ((zce = zend_hash_find(EG(class_table), Z_STR_P(EX_CONSTANT(opline->op2)))) == NULL ||
- ((orig_zce = zend_hash_find(EG(class_table), Z_STR_P(EX_CONSTANT(opline->op1)))) != NULL &&
+ if ((zce = zend_hash_find(EG(class_table), Z_STR_P(EX_CONSTANT(opline->op1)))) == NULL ||
+ ((orig_zce = zend_hash_find(EG(class_table), Z_STR_P(EX_CONSTANT(opline->op1)+1))) != NULL &&
Z_CE_P(zce) != Z_CE_P(orig_zce))) {
- do_bind_inherited_class(&EX(func)->op_array, opline, EG(class_table), Z_CE_P(EX_VAR(opline->extended_value)), 0);
+ do_bind_inherited_class(&EX(func)->op_array, opline, EG(class_table), Z_CE_P(EX_VAR(opline->op2.var)), 0);
}
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
-ZEND_VM_HANDLER(171, ZEND_DECLARE_ANON_CLASS, ANY, ANY)
+ZEND_VM_HANDLER(171, ZEND_DECLARE_ANON_CLASS, ANY, ANY, JMP_ADDR)
{
zend_class_entry *ce;
USE_OPLINE
SAVE_OPLINE();
- ce = zend_hash_find_ptr(EG(class_table), Z_STR_P(EX_CONSTANT(opline->op2)));
+ ce = zend_hash_find_ptr(EG(class_table), Z_STR_P(EX_CONSTANT(opline->op1)));
Z_CE_P(EX_VAR(opline->result.var)) = ce;
ZEND_ASSERT(ce != NULL);
if (ce->ce_flags & ZEND_ACC_ANON_BOUND) {
- ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op1));
+ ZEND_VM_SET_RELATIVE_OPCODE(opline, opline->extended_value);
+ ZEND_VM_CONTINUE();
}
if (!(ce->ce_flags & (ZEND_ACC_INTERFACE|ZEND_ACC_IMPLEMENT_INTERFACES|ZEND_ACC_IMPLEMENT_TRAITS))) {
@@ -6931,21 +6955,22 @@ ZEND_VM_HANDLER(171, ZEND_DECLARE_ANON_CLASS, ANY, ANY)
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
-ZEND_VM_HANDLER(172, ZEND_DECLARE_ANON_INHERITED_CLASS, ANY, ANY)
+ZEND_VM_HANDLER(172, ZEND_DECLARE_ANON_INHERITED_CLASS, ANY, VAR, JMP_ADDR)
{
zend_class_entry *ce;
USE_OPLINE
SAVE_OPLINE();
- ce = zend_hash_find_ptr(EG(class_table), Z_STR_P(EX_CONSTANT(opline->op2)));
+ ce = zend_hash_find_ptr(EG(class_table), Z_STR_P(EX_CONSTANT(opline->op1)));
Z_CE_P(EX_VAR(opline->result.var)) = ce;
ZEND_ASSERT(ce != NULL);
if (ce->ce_flags & ZEND_ACC_ANON_BOUND) {
- ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op1));
+ ZEND_VM_SET_RELATIVE_OPCODE(opline, opline->extended_value);
+ ZEND_VM_CONTINUE();
}
- zend_do_inheritance(ce, Z_CE_P(EX_VAR(opline->extended_value)));
+ zend_do_inheritance(ce, Z_CE_P(EX_VAR(opline->op2.var)));
ce->ce_flags |= ZEND_ACC_ANON_BOUND;
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
@@ -6959,7 +6984,7 @@ ZEND_VM_HANDLER(141, ZEND_DECLARE_FUNCTION, ANY, ANY)
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
-ZEND_VM_HANDLER(105, ZEND_TICKS, ANY, ANY)
+ZEND_VM_HANDLER(105, ZEND_TICKS, ANY, ANY, NUM)
{
USE_OPLINE
@@ -6974,7 +6999,7 @@ ZEND_VM_HANDLER(105, ZEND_TICKS, ANY, ANY)
ZEND_VM_NEXT_OPCODE();
}
-ZEND_VM_HANDLER(138, ZEND_INSTANCEOF, TMPVAR|CV, CONST|VAR)
+ZEND_VM_HANDLER(138, ZEND_INSTANCEOF, TMPVAR|CV, UNUSED|CLASS_FETCH|CONST|VAR)
{
USE_OPLINE
zend_free_op free_op1;
@@ -6999,6 +7024,13 @@ ZEND_VM_C_LABEL(try_instanceof):
}
CACHE_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)), ce);
}
+ } else if (OP2_TYPE == IS_UNUSED) {
+ ce = zend_fetch_class(NULL, opline->op2.num);
+ if (UNEXPECTED(ce == NULL)) {
+ ZEND_ASSERT(EG(exception));
+ FREE_OP1();
+ HANDLE_EXCEPTION();
+ }
} else {
ce = Z_CE_P(EX_VAR(opline->op2.var));
}
@@ -7092,14 +7124,72 @@ ZEND_VM_HANDLER(155, ZEND_BIND_TRAITS, ANY, ANY)
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
-ZEND_VM_HANDLER(149, ZEND_HANDLE_EXCEPTION, ANY, ANY)
+ZEND_VM_HELPER(zend_dispatch_try_catch_finally_helper, ANY, ANY, uint32_t try_catch_offset, uint32_t op_num)
{
- uint32_t op_num = EG(opline_before_exception) - EX(func)->op_array.opcodes;
- int i;
- uint32_t catch_op_num = 0, finally_op_num = 0, finally_op_end = 0;
- int in_finally = 0;
+ /* May be NULL during generator closing (only finally blocks are executed) */
+ zend_object *ex = EG(exception);
+
+ /* Walk try/catch/finally structures upwards, performing the necessary actions */
+ while (try_catch_offset != (uint32_t) -1) {
+ zend_try_catch_element *try_catch =
+ &EX(func)->op_array.try_catch_array[try_catch_offset];
+
+ if (op_num < try_catch->catch_op && ex) {
+ /* Go to catch block */
+ cleanup_live_vars(execute_data, op_num, try_catch->catch_op);
+ ZEND_VM_SET_OPCODE(&EX(func)->op_array.opcodes[try_catch->catch_op]);
+ ZEND_VM_CONTINUE();
+
+ } else if (op_num < try_catch->finally_op) {
+ /* Go to finally block */
+ zval *fast_call = EX_VAR(EX(func)->op_array.opcodes[try_catch->finally_end].op1.var);
+ cleanup_live_vars(execute_data, op_num, try_catch->finally_op);
+ Z_OBJ_P(fast_call) = EG(exception);
+ EG(exception) = NULL;
+ fast_call->u2.lineno = (uint32_t)-1;
+ ZEND_VM_SET_OPCODE(&EX(func)->op_array.opcodes[try_catch->finally_op]);
+ ZEND_VM_CONTINUE();
+
+ } else if (op_num < try_catch->finally_end) {
+ zval *fast_call = EX_VAR(EX(func)->op_array.opcodes[try_catch->finally_end].op1.var);
- ZEND_VM_INTERRUPT_CHECK();
+ /* cleanup incomplete RETURN statement */
+ if (fast_call->u2.lineno != (uint32_t)-1
+ && (EX(func)->op_array.opcodes[fast_call->u2.lineno].op2_type & (IS_TMP_VAR | IS_VAR))) {
+ zval *return_value = EX_VAR(EX(func)->op_array.opcodes[fast_call->u2.lineno].op2.var);
+
+ zval_ptr_dtor(return_value);
+ }
+
+ /* Chain potential exception from wrapping finally block */
+ if (Z_OBJ_P(fast_call)) {
+ if (ex) {
+ zend_exception_set_previous(ex, Z_OBJ_P(fast_call));
+ } else {
+ EG(exception) = Z_OBJ_P(fast_call);
+ }
+ ex = Z_OBJ_P(fast_call);
+ }
+ }
+
+ try_catch_offset--;
+ }
+
+ /* Uncaught exception */
+ cleanup_live_vars(execute_data, op_num, 0);
+ if (UNEXPECTED((EX_CALL_INFO() & ZEND_CALL_GENERATOR) != 0)) {
+ zend_generator *generator = zend_get_running_generator(execute_data);
+ zend_generator_close(generator, 1);
+ ZEND_VM_RETURN();
+ } else {
+ ZEND_VM_DISPATCH_TO_HELPER(zend_leave_helper);
+ }
+}
+
+ZEND_VM_HANDLER(149, ZEND_HANDLE_EXCEPTION, ANY, ANY)
+{
+ uint32_t throw_op_num = EG(opline_before_exception) - EX(func)->op_array.opcodes;
+ uint32_t i, current_try_catch_offset = (uint32_t) -1;
{
const zend_op *exc_opline = EG(opline_before_exception);
@@ -7107,68 +7197,27 @@ ZEND_VM_HANDLER(149, ZEND_HANDLE_EXCEPTION, ANY, ANY)
&& exc_opline->extended_value & ZEND_FREE_ON_RETURN) {
/* exceptions thrown because of loop var destruction on return/break/...
* are logically thrown at the end of the foreach loop, so adjust the
- * op_num.
+ * throw_op_num.
*/
- op_num = EX(func)->op_array.brk_cont_array[exc_opline->op2.num].brk;
+ throw_op_num = EX(func)->op_array.live_range[exc_opline->op2.num].end;
}
}
+ /* Find the innermost try/catch/finally the exception was thrown in */
for (i = 0; i < EX(func)->op_array.last_try_catch; i++) {
- if (EX(func)->op_array.try_catch_array[i].try_op > op_num) {
+ zend_try_catch_element *try_catch = &EX(func)->op_array.try_catch_array[i];
+ if (try_catch->try_op > throw_op_num) {
/* further blocks will not be relevant... */
break;
}
- in_finally = 0;
- if (op_num < EX(func)->op_array.try_catch_array[i].catch_op) {
- catch_op_num = EX(func)->op_array.try_catch_array[i].catch_op;
- }
- if (op_num < EX(func)->op_array.try_catch_array[i].finally_op) {
- finally_op_num = EX(func)->op_array.try_catch_array[i].finally_op;
- finally_op_end = EX(func)->op_array.try_catch_array[i].finally_end;
- }
- if (op_num >= EX(func)->op_array.try_catch_array[i].finally_op &&
- op_num < EX(func)->op_array.try_catch_array[i].finally_end) {
- finally_op_end = EX(func)->op_array.try_catch_array[i].finally_end;
- in_finally = 1;
+ if (throw_op_num < try_catch->catch_op || throw_op_num < try_catch->finally_end) {
+ current_try_catch_offset = i;
}
}
- cleanup_unfinished_calls(execute_data, op_num);
+ cleanup_unfinished_calls(execute_data, throw_op_num);
- if (finally_op_num && (!catch_op_num || catch_op_num >= finally_op_num)) {
- zval *fast_call = EX_VAR(EX(func)->op_array.opcodes[finally_op_end].op1.var);
-
- cleanup_live_vars(execute_data, op_num, finally_op_num);
- if (in_finally && Z_OBJ_P(fast_call)) {
- zend_exception_set_previous(EG(exception), Z_OBJ_P(fast_call));
- }
- Z_OBJ_P(fast_call) = EG(exception);
- EG(exception) = NULL;
- fast_call->u2.lineno = (uint32_t)-1;
- ZEND_VM_SET_OPCODE(&EX(func)->op_array.opcodes[finally_op_num]);
- ZEND_VM_CONTINUE();
- } else {
- cleanup_live_vars(execute_data, op_num, catch_op_num);
- if (in_finally) {
- /* we are going out of current finally scope */
- zval *fast_call = EX_VAR(EX(func)->op_array.opcodes[finally_op_end].op1.var);
-
- if (Z_OBJ_P(fast_call)) {
- zend_exception_set_previous(EG(exception), Z_OBJ_P(fast_call));
- Z_OBJ_P(fast_call) = NULL;
- }
- }
- if (catch_op_num) {
- ZEND_VM_SET_OPCODE(&EX(func)->op_array.opcodes[catch_op_num]);
- ZEND_VM_CONTINUE();
- } else if (UNEXPECTED((EX(func)->op_array.fn_flags & ZEND_ACC_GENERATOR) != 0)) {
- zend_generator *generator = zend_get_running_generator(execute_data);
- zend_generator_close(generator, 1);
- ZEND_VM_RETURN();
- } else {
- ZEND_VM_DISPATCH_TO_HELPER(zend_leave_helper);
- }
- }
+ ZEND_VM_DISPATCH_TO_HELPER(zend_dispatch_try_catch_finally_helper, try_catch_offset, current_try_catch_offset, op_num, throw_op_num);
}
ZEND_VM_HANDLER(146, ZEND_VERIFY_ABSTRACT_CLASS, ANY, ANY)
@@ -7193,7 +7242,7 @@ ZEND_VM_HANDLER(150, ZEND_USER_OPCODE, ANY, ANY)
case ZEND_USER_OPCODE_CONTINUE:
ZEND_VM_CONTINUE();
case ZEND_USER_OPCODE_RETURN:
- if (UNEXPECTED((EX(func)->op_array.fn_flags & ZEND_ACC_GENERATOR) != 0)) {
+ if (UNEXPECTED((EX_CALL_INFO() & ZEND_CALL_GENERATOR) != 0)) {
zend_generator *generator = zend_get_running_generator(execute_data);
zend_generator_close(generator, 1);
ZEND_VM_RETURN();
@@ -7223,18 +7272,13 @@ ZEND_VM_HANDLER(143, ZEND_DECLARE_CONST, CONST, CONST)
name = GET_OP1_ZVAL_PTR(BP_VAR_R);
val = GET_OP2_ZVAL_PTR(BP_VAR_R);
- ZVAL_COPY_VALUE(&c.value, val);
+ ZVAL_COPY(&c.value, val);
if (Z_OPT_CONSTANT(c.value)) {
- if (UNEXPECTED(zval_update_constant_ex(&c.value, 0, NULL) != SUCCESS)) {
+ if (UNEXPECTED(zval_update_constant_ex(&c.value, EX(func)->op_array.scope) != SUCCESS)) {
FREE_OP1();
FREE_OP2();
HANDLE_EXCEPTION();
}
- } else {
- /* IS_CONST can't be IS_OBJECT, IS_RESOURCE or IS_REFERENCE */
- if (UNEXPECTED(Z_OPT_COPYABLE(c.value))) {
- zval_copy_ctor_func(&c.value);
- }
}
c.flags = CONST_CS; /* non persistent, case sensetive */
c.name = zend_string_dup(Z_STR_P(name), 0);
@@ -7252,20 +7296,28 @@ ZEND_VM_HANDLER(153, ZEND_DECLARE_LAMBDA_FUNCTION, CONST, UNUSED)
{
USE_OPLINE
zval *zfunc;
+ zval *object;
+ zend_class_entry *called_scope;
SAVE_OPLINE();
zfunc = zend_hash_find(EG(function_table), Z_STR_P(EX_CONSTANT(opline->op1)));
ZEND_ASSERT(zfunc != NULL && Z_FUNC_P(zfunc)->type == ZEND_USER_FUNCTION);
- if (UNEXPECTED((Z_FUNC_P(zfunc)->common.fn_flags & ZEND_ACC_STATIC) ||
+ if (Z_TYPE(EX(This)) == IS_OBJECT) {
+ called_scope = Z_OBJCE(EX(This));
+ if (UNEXPECTED((Z_FUNC_P(zfunc)->common.fn_flags & ZEND_ACC_STATIC) ||
(EX(func)->common.fn_flags & ZEND_ACC_STATIC))) {
- zend_create_closure(EX_VAR(opline->result.var), Z_FUNC_P(zfunc),
- EG(scope), EX(called_scope), NULL);
+ object = NULL;
+ } else {
+ object = &EX(This);
+ }
} else {
- zend_create_closure(EX_VAR(opline->result.var), Z_FUNC_P(zfunc),
- EG(scope), EX(called_scope), Z_OBJ(EX(This)) ? &EX(This) : NULL);
+ called_scope = Z_CE(EX(This));
+ object = NULL;
}
+ zend_create_closure(EX_VAR(opline->result.var), Z_FUNC_P(zfunc),
+ EX(func)->op_array.scope, called_scope, object);
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
@@ -7279,16 +7331,13 @@ ZEND_VM_HANDLER(156, ZEND_SEPARATE, VAR, UNUSED)
if (UNEXPECTED(Z_ISREF_P(var_ptr))) {
if (UNEXPECTED(Z_REFCOUNT_P(var_ptr) == 1)) {
ZVAL_UNREF(var_ptr);
- } else if (!(Z_VAR_FLAGS_P(var_ptr) & IS_VAR_RET_REF)) {
- Z_DELREF_P(var_ptr);
- ZVAL_COPY(var_ptr, Z_REFVAL_P(var_ptr));
}
}
ZEND_VM_NEXT_OPCODE();
}
-ZEND_VM_HANDLER(160, ZEND_YIELD, CONST|TMP|VAR|CV|UNUSED, CONST|TMP|VAR|CV|UNUSED)
+ZEND_VM_HANDLER(160, ZEND_YIELD, CONST|TMP|VAR|CV|UNUSED, CONST|TMP|VAR|CV|UNUSED, SRC)
{
USE_OPLINE
@@ -7315,7 +7364,7 @@ ZEND_VM_HANDLER(160, ZEND_YIELD, CONST|TMP|VAR|CV|UNUSED, CONST|TMP|VAR|CV|UNUSE
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) {
+ if (OP1_TYPE & (IS_CONST|IS_TMP_VAR)) {
zval *value;
zend_error(E_NOTICE, "Only variable references should be yielded by reference");
@@ -7323,25 +7372,19 @@ 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 (OP1_TYPE == IS_CONST) {
- if (UNEXPECTED(Z_OPT_COPYABLE(generator->value))) {
- zval_copy_ctor_func(&generator->value);
+ if (UNEXPECTED(Z_OPT_REFCOUNTED(generator->value))) {
+ Z_ADDREF(generator->value);
}
}
} else {
zval *value_ptr = GET_OP1_ZVAL_PTR_PTR(BP_VAR_W);
- if (OP1_TYPE == IS_VAR && UNEXPECTED(value_ptr == NULL)) {
- zend_throw_error(NULL, "Cannot yield string offsets by reference");
- FREE_UNFETCHED_OP2();
- HANDLE_EXCEPTION();
- }
-
/* If a function call result is yielded and the function did
* not return by reference we throw a notice. */
if (OP1_TYPE == IS_VAR &&
(value_ptr == &EG(uninitialized_zval) ||
(opline->extended_value == ZEND_RETURNS_FUNCTION &&
- !(Z_VAR_FLAGS_P(value_ptr) & IS_VAR_RET_REF)))) {
+ !Z_ISREF_P(value_ptr)))) {
zend_error(E_NOTICE, "Only variable references should be yielded by reference");
} else {
ZVAL_MAKE_REF(value_ptr);
@@ -7356,8 +7399,8 @@ 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_COPY_VALUE(&generator->value, value);
- if (UNEXPECTED(Z_OPT_COPYABLE(generator->value))) {
- zval_copy_ctor_func(&generator->value);
+ if (UNEXPECTED(Z_OPT_REFCOUNTED(generator->value))) {
+ Z_ADDREF(generator->value);
}
} else if (OP1_TYPE == IS_TMP_VAR) {
ZVAL_COPY_VALUE(&generator->value, value);
@@ -7384,8 +7427,8 @@ 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_COPY_VALUE(&generator->key, key);
- if (UNEXPECTED(Z_OPT_COPYABLE(generator->key))) {
- zval_copy_ctor_func(&generator->key);
+ if (UNEXPECTED(Z_OPT_REFCOUNTED(generator->key))) {
+ Z_ADDREF(generator->key);
}
} else if (OP2_TYPE == IS_TMP_VAR) {
ZVAL_COPY_VALUE(&generator->key, key);
@@ -7533,73 +7576,57 @@ ZEND_VM_HANDLER(159, ZEND_DISCARD_EXCEPTION, ANY, ANY)
{
USE_OPLINE
zval *fast_call = EX_VAR(opline->op1.var);
+ SAVE_OPLINE();
- /* check for delayed exception */
+ /* cleanup incomplete RETURN statement */
+ if (fast_call->u2.lineno != (uint32_t)-1
+ && (EX(func)->op_array.opcodes[fast_call->u2.lineno].op2_type & (IS_TMP_VAR | IS_VAR))) {
+ zval *return_value = EX_VAR(EX(func)->op_array.opcodes[fast_call->u2.lineno].op2.var);
+
+ zval_ptr_dtor(return_value);
+ }
+
+ /* cleanup delayed exception */
if (Z_OBJ_P(fast_call) != NULL) {
- SAVE_OPLINE();
/* discard the previously thrown exception */
OBJ_RELEASE(Z_OBJ_P(fast_call));
Z_OBJ_P(fast_call) = NULL;
}
- ZEND_VM_NEXT_OPCODE();
+ ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
-ZEND_VM_HANDLER(162, ZEND_FAST_CALL, ANY, ANY)
+ZEND_VM_HANDLER(162, ZEND_FAST_CALL, JMP_ADDR, ANY)
{
USE_OPLINE
zval *fast_call = EX_VAR(opline->result.var);
- if (opline->extended_value == ZEND_FAST_CALL_FROM_FINALLY && UNEXPECTED(Z_OBJ_P(fast_call) != NULL)) {
- fast_call->u2.lineno = (uint32_t)-1;
- } else {
- Z_OBJ_P(fast_call) = NULL;
- /* set return address */
- fast_call->u2.lineno = opline - EX(func)->op_array.opcodes;
- }
+ Z_OBJ_P(fast_call) = NULL;
+ /* set return address */
+ fast_call->u2.lineno = opline - EX(func)->op_array.opcodes;
ZEND_VM_SET_OPCODE(OP_JMP_ADDR(opline, opline->op1));
ZEND_VM_CONTINUE();
}
-ZEND_VM_HANDLER(163, ZEND_FAST_RET, ANY, ANY)
+ZEND_VM_HANDLER(163, ZEND_FAST_RET, ANY, TRY_CATCH)
{
USE_OPLINE
zval *fast_call = EX_VAR(opline->op1.var);
+ uint32_t current_try_catch_offset, current_op_num;
if (fast_call->u2.lineno != (uint32_t)-1) {
const zend_op *fast_ret = EX(func)->op_array.opcodes + fast_call->u2.lineno;
+
ZEND_VM_SET_OPCODE(fast_ret + 1);
- if (fast_ret->extended_value & ZEND_FAST_CALL_FROM_FINALLY) {
- fast_call->u2.lineno = fast_ret->op2.opline_num;
- }
ZEND_VM_CONTINUE();
- } else {
- /* special case for unhandled exceptions */
- USE_OPLINE
-
- if (opline->extended_value == ZEND_FAST_RET_TO_FINALLY) {
- cleanup_live_vars(execute_data, opline - EX(func)->op_array.opcodes, opline->op2.opline_num);
- ZEND_VM_SET_OPCODE(&EX(func)->op_array.opcodes[opline->op2.opline_num]);
- ZEND_VM_CONTINUE();
- } else {
- EG(exception) = Z_OBJ_P(fast_call);
- Z_OBJ_P(fast_call) = NULL;
- if (opline->extended_value == ZEND_FAST_RET_TO_CATCH) {
- cleanup_live_vars(execute_data, opline - EX(func)->op_array.opcodes, opline->op2.opline_num);
- ZEND_VM_SET_OPCODE(&EX(func)->op_array.opcodes[opline->op2.opline_num]);
- ZEND_VM_CONTINUE();
- } else {
- cleanup_live_vars(execute_data, opline - EX(func)->op_array.opcodes, 0);
- if (UNEXPECTED((EX(func)->op_array.fn_flags & ZEND_ACC_GENERATOR) != 0)) {
- zend_generator *generator = zend_get_running_generator(execute_data);
- zend_generator_close(generator, 1);
- ZEND_VM_RETURN();
- } else {
- ZEND_VM_DISPATCH_TO_HELPER(zend_leave_helper);
- }
- }
- }
}
+
+ /* special case for unhandled exceptions */
+ EG(exception) = Z_OBJ_P(fast_call);
+ Z_OBJ_P(fast_call) = NULL;
+ current_try_catch_offset = opline->op2.num;
+ current_op_num = opline - EX(func)->op_array.opcodes;
+ ZEND_VM_DISPATCH_TO_HELPER(zend_dispatch_try_catch_finally_helper, try_catch_offset, current_try_catch_offset, op_num, current_op_num);
}
ZEND_VM_HANDLER(168, ZEND_BIND_GLOBAL, CV, CONST)
@@ -7610,6 +7637,7 @@ ZEND_VM_HANDLER(168, ZEND_BIND_GLOBAL, CV, CONST)
zval *value;
zval *variable_ptr;
uint32_t idx;
+ zend_reference *ref;
ZEND_VM_REPEATABLE_OPCODE
@@ -7652,32 +7680,27 @@ ZEND_VM_C_LABEL(check_indirect):
}
}
- do {
- zend_reference *ref;
+ if (UNEXPECTED(!Z_ISREF_P(value))) {
+ ref = (zend_reference*)emalloc(sizeof(zend_reference));
+ GC_REFCOUNT(ref) = 2;
+ GC_TYPE_INFO(ref) = IS_REFERENCE;
+ ZVAL_COPY_VALUE(&ref->val, value);
+ Z_REF_P(value) = ref;
+ Z_TYPE_INFO_P(value) = IS_REFERENCE_EX;
+ } else {
+ ref = Z_REF_P(value);
+ GC_REFCOUNT(ref)++;
+ }
- if (UNEXPECTED(!Z_ISREF_P(value))) {
- ref = (zend_reference*)emalloc(sizeof(zend_reference));
- GC_REFCOUNT(ref) = 2;
- GC_TYPE_INFO(ref) = IS_REFERENCE;
- ZVAL_COPY_VALUE(&ref->val, value);
- Z_REF_P(value) = ref;
- Z_TYPE_INFO_P(value) = IS_REFERENCE_EX;
- } else {
- ref = Z_REF_P(value);
- GC_REFCOUNT(ref)++;
- }
+ variable_ptr = GET_OP1_ZVAL_PTR_PTR_UNDEF(BP_VAR_W);
- variable_ptr = GET_OP1_ZVAL_PTR_PTR_UNDEF(BP_VAR_W);
-
- if (UNEXPECTED(Z_REFCOUNTED_P(variable_ptr))) {
- uint32_t refcnt = Z_DELREF_P(variable_ptr);
+ if (UNEXPECTED(Z_REFCOUNTED_P(variable_ptr))) {
+ uint32_t refcnt = Z_DELREF_P(variable_ptr);
- if (UNEXPECTED(variable_ptr == value)) {
- break;
- }
+ if (EXPECTED(variable_ptr != value)) {
if (refcnt == 0) {
SAVE_OPLINE();
- zval_dtor_func_for_ptr(Z_COUNTED_P(variable_ptr));
+ zval_dtor_func(Z_COUNTED_P(variable_ptr));
if (UNEXPECTED(EG(exception))) {
ZVAL_NULL(variable_ptr);
HANDLE_EXCEPTION();
@@ -7686,8 +7709,8 @@ ZEND_VM_C_LABEL(check_indirect):
GC_ZVAL_CHECK_POSSIBLE_ROOT(variable_ptr);
}
}
- ZVAL_REF(variable_ptr, ref);
- } while (0);
+ }
+ ZVAL_REF(variable_ptr, ref);
ZEND_VM_REPEAT_OPCODE(ZEND_BIND_GLOBAL);
ZEND_VM_NEXT_OPCODE();
@@ -7736,7 +7759,7 @@ ZEND_VM_C_LABEL(try_strlen):
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
-ZEND_VM_HANDLER(123, ZEND_TYPE_CHECK, CONST|TMP|VAR|CV, ANY)
+ZEND_VM_HANDLER(123, ZEND_TYPE_CHECK, CONST|TMP|VAR|CV, ANY, TYPE)
{
USE_OPLINE
zval *value;
@@ -7749,7 +7772,7 @@ ZEND_VM_HANDLER(123, ZEND_TYPE_CHECK, CONST|TMP|VAR|CV, ANY)
if (OP1_TYPE != IS_CONST && UNEXPECTED(Z_TYPE_P(value) == IS_OBJECT)) {
zend_class_entry *ce = Z_OBJCE_P(value);
- if (UNEXPECTED(ZSTR_LEN(ce->name) != sizeof("__PHP_Incomplete_Class") - 1) ||
+ if (EXPECTED(ZSTR_LEN(ce->name) != sizeof("__PHP_Incomplete_Class") - 1) ||
EXPECTED(memcmp(ZSTR_VAL(ce->name), "__PHP_Incomplete_Class", sizeof("__PHP_Incomplete_Class") - 1) != 0)) {
result = 1;
}
@@ -7782,7 +7805,6 @@ ZEND_VM_HANDLER(122, ZEND_DEFINED, CONST, ANY)
result = 1;
} else if ((c = zend_quick_get_constant(EX_CONSTANT(opline->op1), 0)) == NULL) {
result = 0;
- ZVAL_FALSE(EX_VAR(opline->result.var));
} else {
CACHE_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)), c);
result = 1;
@@ -7792,16 +7814,14 @@ ZEND_VM_HANDLER(122, ZEND_DEFINED, CONST, ANY)
ZEND_VM_NEXT_OPCODE();
}
-ZEND_VM_HANDLER(151, ZEND_ASSERT_CHECK, ANY, ANY)
+ZEND_VM_HANDLER(151, ZEND_ASSERT_CHECK, ANY, JMP_ADDR)
{
USE_OPLINE
if (EG(assertions) <= 0) {
zend_op *target = OP_JMP_ADDR(opline, opline->op2);
- zend_op *result = target - 1;
- SKIP_EXT_OPLINE(result);
- if (RETURN_VALUE_USED(result)) {
- ZVAL_TRUE(EX_VAR(result->result.var));
+ if (RETURN_VALUE_USED(opline)) {
+ ZVAL_TRUE(EX_VAR(opline->result.var));
}
ZEND_VM_JMP(target);
} else {
@@ -7809,15 +7829,17 @@ ZEND_VM_HANDLER(151, ZEND_ASSERT_CHECK, ANY, ANY)
}
}
-ZEND_VM_HANDLER(157, ZEND_FETCH_CLASS_NAME, ANY, ANY)
+ZEND_VM_HANDLER(157, ZEND_FETCH_CLASS_NAME, ANY, ANY, CLASS_FETCH)
{
uint32_t fetch_type;
+ zend_class_entry *called_scope, *scope;
USE_OPLINE
SAVE_OPLINE();
fetch_type = opline->extended_value;
- if (UNEXPECTED(EG(scope) == NULL)) {
+ scope = EX(func)->op_array.scope;
+ if (UNEXPECTED(scope == NULL)) {
zend_throw_error(NULL, "Cannot use \"%s\" when no class scope is active",
fetch_type == ZEND_FETCH_CLASS_SELF ? "self" :
fetch_type == ZEND_FETCH_CLASS_PARENT ? "parent" : "static");
@@ -7826,18 +7848,23 @@ ZEND_VM_HANDLER(157, ZEND_FETCH_CLASS_NAME, ANY, ANY)
switch (fetch_type) {
case ZEND_FETCH_CLASS_SELF:
- ZVAL_STR_COPY(EX_VAR(opline->result.var), EG(scope)->name);
+ ZVAL_STR_COPY(EX_VAR(opline->result.var), scope->name);
break;
case ZEND_FETCH_CLASS_PARENT:
- if (UNEXPECTED(EG(scope)->parent == NULL)) {
+ if (UNEXPECTED(scope->parent == NULL)) {
zend_throw_error(NULL,
"Cannot use \"parent\" when current class scope has no parent");
HANDLE_EXCEPTION();
}
- ZVAL_STR_COPY(EX_VAR(opline->result.var), EG(scope)->parent->name);
+ ZVAL_STR_COPY(EX_VAR(opline->result.var), scope->parent->name);
break;
case ZEND_FETCH_CLASS_STATIC:
- ZVAL_STR_COPY(EX_VAR(opline->result.var), EX(called_scope)->name);
+ if (Z_TYPE(EX(This)) == IS_OBJECT) {
+ called_scope = Z_OBJCE(EX(This));
+ } else {
+ called_scope = Z_CE(EX(This));
+ }
+ ZVAL_STR_COPY(EX_VAR(opline->result.var), called_scope->name);
break;
EMPTY_SWITCH_DEFAULT_CASE()
}
@@ -7887,10 +7914,10 @@ ZEND_VM_HANDLER(158, ZEND_CALL_TRAMPOLINE, ANY, ANY)
ZEND_ASSERT(!(fbc->common.fn_flags & ZEND_ACC_GENERATOR));
- call->symbol_table = NULL;
- i_init_func_execute_data(call, &fbc->op_array,
- ret, (fbc->common.fn_flags & ZEND_ACC_STATIC) == 0);
-
+ if (UNEXPECTED(!fbc->op_array.run_time_cache)) {
+ init_func_run_time_cache(&fbc->op_array);
+ }
+ i_init_func_execute_data(call, &fbc->op_array, ret);
if (EXPECTED(zend_execute_ex == execute_ex)) {
ZEND_VM_ENTER();
} else {
@@ -7904,32 +7931,19 @@ ZEND_VM_HANDLER(158, ZEND_CALL_TRAMPOLINE, ANY, ANY)
EG(current_execute_data) = call;
- if (fbc->common.fn_flags & ZEND_ACC_HAS_TYPE_HINTS) {
- uint32_t i;
- uint32_t num_args = ZEND_CALL_NUM_ARGS(call);
- zval *p = ZEND_CALL_ARG(call, 1);
-
- EG(current_execute_data) = call;
-
- for (i = 0; i < num_args; ++i) {
- if (UNEXPECTED(!zend_verify_internal_arg_type(fbc, i + 1, p))) {
- EG(current_execute_data) = call->prev_execute_data;
- zend_vm_stack_free_args(call);
- zend_vm_stack_free_call_frame(call);
- if (ret) {
- ZVAL_UNDEF(ret);
- }
- ZEND_VM_C_GOTO(call_trampoline_end);
- }
- p++;
+ if (UNEXPECTED(fbc->common.fn_flags & ZEND_ACC_HAS_TYPE_HINTS)
+ && UNEXPECTED(!zend_verify_internal_arg_types(fbc, call))) {
+ zend_vm_stack_free_call_frame(call);
+ if (ret) {
+ ZVAL_UNDEF(ret);
}
+ ZEND_VM_C_GOTO(call_trampoline_end);
}
if (ret == NULL) {
ZVAL_NULL(&retval);
ret = &retval;
}
- Z_VAR_FLAGS_P(ret) = (fbc->common.fn_flags & ZEND_ACC_RETURN_REFERENCE) != 0 ? IS_VAR_RET_REF : 0;
if (!zend_execute_internal) {
/* saves one function call if zend_execute_internal is not used */
@@ -7942,7 +7956,7 @@ ZEND_VM_HANDLER(158, ZEND_CALL_TRAMPOLINE, ANY, ANY)
ZEND_ASSERT(
EG(exception) || !call->func ||
!(call->func->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) ||
- zend_verify_internal_return_type(call->func, EX_VAR(opline->result.var)));
+ zend_verify_internal_return_type(call->func, ret));
#endif
EG(current_execute_data) = call->prev_execute_data;
@@ -7967,7 +7981,6 @@ ZEND_VM_C_LABEL(call_trampoline_end):
zend_object *object = Z_OBJ(call->This);
OBJ_RELEASE(object);
}
- EG(scope) = EX(func)->op_array.scope;
zend_vm_stack_free_call_frame(call);
if (UNEXPECTED(EG(exception) != NULL)) {
@@ -7982,4 +7995,612 @@ ZEND_VM_C_LABEL(call_trampoline_end):
ZEND_VM_LEAVE();
}
+ZEND_VM_HANDLER(182, ZEND_BIND_LEXICAL, TMP, CV, REF)
+{
+ USE_OPLINE
+ zend_free_op free_op1, free_op2;
+ zval *closure, *var;
+ zend_string *var_name;
+
+ closure = GET_OP1_ZVAL_PTR(BP_VAR_R);
+ if (opline->extended_value) {
+ /* By-ref binding */
+ var = GET_OP2_ZVAL_PTR(BP_VAR_W);
+ ZVAL_MAKE_REF(var);
+ Z_ADDREF_P(var);
+ } else {
+ var = GET_OP2_ZVAL_PTR_UNDEF(BP_VAR_R);
+ if (UNEXPECTED(Z_ISUNDEF_P(var))) {
+ SAVE_OPLINE();
+ var = GET_OP2_UNDEF_CV(var, BP_VAR_R);
+ if (UNEXPECTED(EG(exception))) {
+ HANDLE_EXCEPTION();
+ }
+ }
+ ZVAL_DEREF(var);
+ Z_TRY_ADDREF_P(var);
+ }
+
+ var_name = CV_DEF_OF(EX_VAR_TO_NUM(opline->op2.var));
+ zend_closure_bind_var(closure, var_name, var);
+ ZEND_VM_NEXT_OPCODE();
+}
+
+ZEND_VM_HANDLER(183, ZEND_BIND_STATIC, CV, CONST, REF)
+{
+ USE_OPLINE
+ zend_free_op free_op1, free_op2;
+ HashTable *ht;
+ zval *varname;
+ zval *value;
+ zval *variable_ptr;
+
+ SAVE_OPLINE();
+ variable_ptr = GET_OP1_ZVAL_PTR_PTR_UNDEF(BP_VAR_W);
+ zval_ptr_dtor(variable_ptr);
+
+ ht = EX(func)->op_array.static_variables;
+ ZEND_ASSERT(ht != NULL);
+ if (GC_REFCOUNT(ht) > 1) {
+ if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE)) {
+ GC_REFCOUNT(ht)--;
+ }
+ EX(func)->op_array.static_variables = ht = zend_array_dup(ht);
+ }
+
+ varname = GET_OP2_ZVAL_PTR(BP_VAR_R);
+ value = zend_hash_find(ht, Z_STR_P(varname));
+
+ if (opline->extended_value) {
+ if (Z_CONSTANT_P(value)) {
+ if (UNEXPECTED(zval_update_constant_ex(value, EX(func)->op_array.scope) != SUCCESS)) {
+ ZVAL_NULL(variable_ptr);
+ HANDLE_EXCEPTION();
+ }
+ }
+ if (UNEXPECTED(!Z_ISREF_P(value))) {
+ zend_reference *ref = (zend_reference*)emalloc(sizeof(zend_reference));
+ GC_REFCOUNT(ref) = 2;
+ GC_TYPE_INFO(ref) = IS_REFERENCE;
+ ZVAL_COPY_VALUE(&ref->val, value);
+ Z_REF_P(value) = ref;
+ Z_TYPE_INFO_P(value) = IS_REFERENCE_EX;
+ ZVAL_REF(variable_ptr, ref);
+ } else {
+ Z_ADDREF_P(value);
+ ZVAL_REF(variable_ptr, Z_REF_P(value));
+ }
+ } else {
+ ZVAL_COPY(variable_ptr, value);
+ }
+
+ ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
+}
+
+ZEND_VM_HANDLER(184, ZEND_FETCH_THIS, UNUSED, UNUSED)
+{
+ USE_OPLINE
+
+ if (EXPECTED(Z_TYPE(EX(This)) == IS_OBJECT)) {
+ zval *result = EX_VAR(opline->result.var);
+
+ ZVAL_OBJ(result, Z_OBJ(EX(This)));
+ Z_ADDREF_P(result);
+ ZEND_VM_NEXT_OPCODE();
+ } else {
+ SAVE_OPLINE();
+ zend_throw_error(NULL, "Using $this when not in object context");
+ HANDLE_EXCEPTION();
+ }
+}
+
+ZEND_VM_HANDLER(186, ZEND_ISSET_ISEMPTY_THIS, UNUSED, UNUSED)
+{
+ USE_OPLINE
+
+ ZVAL_BOOL(EX_VAR(opline->result.var),
+ (opline->extended_value & ZEND_ISSET) ?
+ (Z_TYPE(EX(This)) == IS_OBJECT) :
+ (Z_TYPE(EX(This)) != IS_OBJECT));
+ ZEND_VM_NEXT_OPCODE();
+}
+
+ZEND_VM_TYPE_SPEC_HANDLER(ZEND_ADD, (res_info == MAY_BE_LONG && op1_info == MAY_BE_LONG && op2_info == MAY_BE_LONG), ZEND_ADD_LONG_NO_OVERFLOW, CONST|TMPVARCV, CONST|TMPVARCV, SPEC(NO_CONST_CONST,COMMUTATIVE))
+{
+ USE_OPLINE
+ zval *op1, *op2, *result;
+
+ op1 = GET_OP1_ZVAL_PTR_UNDEF(BP_VAR_R);
+ op2 = GET_OP2_ZVAL_PTR_UNDEF(BP_VAR_R);
+ result = EX_VAR(opline->result.var);
+ ZVAL_LONG(result, Z_LVAL_P(op1) + Z_LVAL_P(op2));
+ ZEND_VM_NEXT_OPCODE();
+}
+
+ZEND_VM_TYPE_SPEC_HANDLER(ZEND_ADD, (op1_info == MAY_BE_LONG && op2_info == MAY_BE_LONG), ZEND_ADD_LONG, CONST|TMPVARCV, CONST|TMPVARCV, SPEC(NO_CONST_CONST,COMMUTATIVE))
+{
+ USE_OPLINE
+ zval *op1, *op2, *result;
+
+ op1 = GET_OP1_ZVAL_PTR_UNDEF(BP_VAR_R);
+ op2 = GET_OP2_ZVAL_PTR_UNDEF(BP_VAR_R);
+ result = EX_VAR(opline->result.var);
+ fast_long_add_function(result, op1, op2);
+ ZEND_VM_NEXT_OPCODE();
+}
+
+ZEND_VM_TYPE_SPEC_HANDLER(ZEND_ADD, (op1_info == MAY_BE_DOUBLE && op2_info == MAY_BE_DOUBLE), ZEND_ADD_DOUBLE, CONST|TMPVARCV, CONST|TMPVARCV, SPEC(NO_CONST_CONST,COMMUTATIVE))
+{
+ USE_OPLINE
+ zval *op1, *op2, *result;
+
+ op1 = GET_OP1_ZVAL_PTR_UNDEF(BP_VAR_R);
+ op2 = GET_OP2_ZVAL_PTR_UNDEF(BP_VAR_R);
+ result = EX_VAR(opline->result.var);
+ ZVAL_DOUBLE(result, Z_DVAL_P(op1) + Z_DVAL_P(op2));
+ ZEND_VM_NEXT_OPCODE();
+}
+
+ZEND_VM_TYPE_SPEC_HANDLER(ZEND_SUB, (res_info == MAY_BE_LONG && op1_info == MAY_BE_LONG && op2_info == MAY_BE_LONG), ZEND_SUB_LONG_NO_OVERFLOW, CONST|TMPVARCV, CONST|TMPVARCV, SPEC(NO_CONST_CONST))
+{
+ USE_OPLINE
+ zval *op1, *op2, *result;
+
+ op1 = GET_OP1_ZVAL_PTR_UNDEF(BP_VAR_R);
+ op2 = GET_OP2_ZVAL_PTR_UNDEF(BP_VAR_R);
+ result = EX_VAR(opline->result.var);
+ ZVAL_LONG(result, Z_LVAL_P(op1) - Z_LVAL_P(op2));
+ ZEND_VM_NEXT_OPCODE();
+}
+
+ZEND_VM_TYPE_SPEC_HANDLER(ZEND_SUB, (op1_info == MAY_BE_LONG && op2_info == MAY_BE_LONG), ZEND_SUB_LONG, CONST|TMPVARCV, CONST|TMPVARCV, SPEC(NO_CONST_CONST))
+{
+ USE_OPLINE
+ zval *op1, *op2, *result;
+
+ op1 = GET_OP1_ZVAL_PTR_UNDEF(BP_VAR_R);
+ op2 = GET_OP2_ZVAL_PTR_UNDEF(BP_VAR_R);
+ result = EX_VAR(opline->result.var);
+ fast_long_sub_function(result, op1, op2);
+ ZEND_VM_NEXT_OPCODE();
+}
+
+ZEND_VM_TYPE_SPEC_HANDLER(ZEND_SUB, (op1_info == MAY_BE_DOUBLE && op2_info == MAY_BE_DOUBLE), ZEND_SUB_DOUBLE, CONST|TMPVARCV, CONST|TMPVARCV, SPEC(NO_CONST_CONST))
+{
+ USE_OPLINE
+ zval *op1, *op2, *result;
+
+ op1 = GET_OP1_ZVAL_PTR_UNDEF(BP_VAR_R);
+ op2 = GET_OP2_ZVAL_PTR_UNDEF(BP_VAR_R);
+ result = EX_VAR(opline->result.var);
+ ZVAL_DOUBLE(result, Z_DVAL_P(op1) - Z_DVAL_P(op2));
+ ZEND_VM_NEXT_OPCODE();
+}
+
+ZEND_VM_TYPE_SPEC_HANDLER(ZEND_MUL, (res_info == MAY_BE_LONG && op1_info == MAY_BE_LONG && op2_info == MAY_BE_LONG), ZEND_MUL_LONG_NO_OVERFLOW, CONST|TMPVARCV, CONST|TMPVARCV, SPEC(NO_CONST_CONST,COMMUTATIVE))
+{
+ USE_OPLINE
+ zval *op1, *op2, *result;
+
+ op1 = GET_OP1_ZVAL_PTR_UNDEF(BP_VAR_R);
+ op2 = GET_OP2_ZVAL_PTR_UNDEF(BP_VAR_R);
+ result = EX_VAR(opline->result.var);
+ ZVAL_LONG(result, Z_LVAL_P(op1) * Z_LVAL_P(op2));
+ ZEND_VM_NEXT_OPCODE();
+}
+
+ZEND_VM_TYPE_SPEC_HANDLER(ZEND_MUL, (op1_info == MAY_BE_LONG && op2_info == MAY_BE_LONG), ZEND_MUL_LONG, CONST|TMPVARCV, CONST|TMPVARCV, SPEC(NO_CONST_CONST,COMMUTATIVE))
+{
+ USE_OPLINE
+ zval *op1, *op2, *result;
+ zend_long overflow;
+
+ op1 = GET_OP1_ZVAL_PTR_UNDEF(BP_VAR_R);
+ op2 = GET_OP2_ZVAL_PTR_UNDEF(BP_VAR_R);
+ result = EX_VAR(opline->result.var);
+ ZEND_SIGNED_MULTIPLY_LONG(Z_LVAL_P(op1), Z_LVAL_P(op2), Z_LVAL_P(result), Z_DVAL_P(result), overflow);
+ Z_TYPE_INFO_P(result) = overflow ? IS_DOUBLE : IS_LONG;
+ ZEND_VM_NEXT_OPCODE();
+}
+
+ZEND_VM_TYPE_SPEC_HANDLER(ZEND_MUL, (op1_info == MAY_BE_DOUBLE && op2_info == MAY_BE_DOUBLE), ZEND_MUL_DOUBLE, CONST|TMPVARCV, CONST|TMPVARCV, SPEC(NO_CONST_CONST,COMMUTATIVE))
+{
+ USE_OPLINE
+ zval *op1, *op2, *result;
+
+ op1 = GET_OP1_ZVAL_PTR_UNDEF(BP_VAR_R);
+ op2 = GET_OP2_ZVAL_PTR_UNDEF(BP_VAR_R);
+ result = EX_VAR(opline->result.var);
+ ZVAL_DOUBLE(result, Z_DVAL_P(op1) * Z_DVAL_P(op2));
+ ZEND_VM_NEXT_OPCODE();
+}
+
+ZEND_VM_TYPE_SPEC_HANDLER(ZEND_IS_EQUAL, (op1_info == MAY_BE_LONG && op2_info == MAY_BE_LONG), ZEND_IS_EQUAL_LONG, CONST|TMPVARCV, CONST|TMPVARCV, SPEC(SMART_BRANCH,NO_CONST_CONST,COMMUTATIVE))
+{
+ USE_OPLINE
+ zval *op1, *op2;
+ int result;
+
+ op1 = GET_OP1_ZVAL_PTR_UNDEF(BP_VAR_R);
+ op2 = GET_OP2_ZVAL_PTR_UNDEF(BP_VAR_R);
+ result = (Z_LVAL_P(op1) == Z_LVAL_P(op2));
+ ZEND_VM_SMART_BRANCH(result, 0);
+ ZVAL_BOOL(EX_VAR(opline->result.var), result);
+ ZEND_VM_NEXT_OPCODE();
+}
+
+ZEND_VM_TYPE_SPEC_HANDLER(ZEND_IS_EQUAL, (op1_info == MAY_BE_DOUBLE && op2_info == MAY_BE_DOUBLE), ZEND_IS_EQUAL_DOUBLE, CONST|TMPVARCV, CONST|TMPVARCV, SPEC(SMART_BRANCH,NO_CONST_CONST,COMMUTATIVE))
+{
+ USE_OPLINE
+ zval *op1, *op2;
+ int result;
+
+ op1 = GET_OP1_ZVAL_PTR_UNDEF(BP_VAR_R);
+ op2 = GET_OP2_ZVAL_PTR_UNDEF(BP_VAR_R);
+ result = (Z_DVAL_P(op1) == Z_DVAL_P(op2));
+ ZEND_VM_SMART_BRANCH(result, 0);
+ ZVAL_BOOL(EX_VAR(opline->result.var), result);
+ ZEND_VM_NEXT_OPCODE();
+}
+
+ZEND_VM_TYPE_SPEC_HANDLER(ZEND_IS_NOT_EQUAL, (op1_info == MAY_BE_LONG && op2_info == MAY_BE_LONG), ZEND_IS_NOT_EQUAL_LONG, CONST|TMPVARCV, CONST|TMPVARCV, SPEC(SMART_BRANCH,NO_CONST_CONST,COMMUTATIVE))
+{
+ USE_OPLINE
+ zval *op1, *op2;
+ int result;
+
+ op1 = GET_OP1_ZVAL_PTR_UNDEF(BP_VAR_R);
+ op2 = GET_OP2_ZVAL_PTR_UNDEF(BP_VAR_R);
+ result = (Z_LVAL_P(op1) != Z_LVAL_P(op2));
+ ZEND_VM_SMART_BRANCH(result, 0);
+ ZVAL_BOOL(EX_VAR(opline->result.var), result);
+ ZEND_VM_NEXT_OPCODE();
+}
+
+ZEND_VM_TYPE_SPEC_HANDLER(ZEND_IS_NOT_EQUAL, (op1_info == MAY_BE_DOUBLE && op2_info == MAY_BE_DOUBLE), ZEND_IS_NOT_EQUAL_DOUBLE, CONST|TMPVARCV, CONST|TMPVARCV, SPEC(SMART_BRANCH,NO_CONST_CONST,COMMUTATIVE))
+{
+ USE_OPLINE
+ zval *op1, *op2;
+ int result;
+
+ op1 = GET_OP1_ZVAL_PTR_UNDEF(BP_VAR_R);
+ op2 = GET_OP2_ZVAL_PTR_UNDEF(BP_VAR_R);
+ result = (Z_DVAL_P(op1) != Z_DVAL_P(op2));
+ ZEND_VM_SMART_BRANCH(result, 0);
+ ZVAL_BOOL(EX_VAR(opline->result.var), result);
+ ZEND_VM_NEXT_OPCODE();
+}
+
+ZEND_VM_TYPE_SPEC_HANDLER(ZEND_IS_SMALLER, (op1_info == MAY_BE_LONG && op2_info == MAY_BE_LONG), ZEND_IS_SMALLER_LONG, CONST|TMPVARCV, CONST|TMPVARCV, SPEC(SMART_BRANCH,NO_CONST_CONST))
+{
+ USE_OPLINE
+ zval *op1, *op2;
+ int result;
+
+ op1 = GET_OP1_ZVAL_PTR_UNDEF(BP_VAR_R);
+ op2 = GET_OP2_ZVAL_PTR_UNDEF(BP_VAR_R);
+ result = (Z_LVAL_P(op1) < Z_LVAL_P(op2));
+ ZEND_VM_SMART_BRANCH(result, 0);
+ ZVAL_BOOL(EX_VAR(opline->result.var), result);
+ ZEND_VM_NEXT_OPCODE();
+}
+
+ZEND_VM_TYPE_SPEC_HANDLER(ZEND_IS_SMALLER, (op1_info == MAY_BE_DOUBLE && op2_info == MAY_BE_DOUBLE), ZEND_IS_SMALLER_DOUBLE, CONST|TMPVARCV, CONST|TMPVARCV, SPEC(SMART_BRANCH,NO_CONST_CONST))
+{
+ USE_OPLINE
+ zval *op1, *op2;
+ int result;
+
+ op1 = GET_OP1_ZVAL_PTR_UNDEF(BP_VAR_R);
+ op2 = GET_OP2_ZVAL_PTR_UNDEF(BP_VAR_R);
+ result = (Z_DVAL_P(op1) < Z_DVAL_P(op2));
+ ZEND_VM_SMART_BRANCH(result, 0);
+ ZVAL_BOOL(EX_VAR(opline->result.var), result);
+ ZEND_VM_NEXT_OPCODE();
+}
+
+ZEND_VM_TYPE_SPEC_HANDLER(ZEND_IS_SMALLER_OR_EQUAL, (op1_info == MAY_BE_LONG && op2_info == MAY_BE_LONG), ZEND_IS_SMALLER_OR_EQUAL_LONG, CONST|TMPVARCV, CONST|TMPVARCV, SPEC(SMART_BRANCH,NO_CONST_CONST))
+{
+ USE_OPLINE
+ zval *op1, *op2;
+ int result;
+
+ op1 = GET_OP1_ZVAL_PTR_UNDEF(BP_VAR_R);
+ op2 = GET_OP2_ZVAL_PTR_UNDEF(BP_VAR_R);
+ result = (Z_LVAL_P(op1) <= Z_LVAL_P(op2));
+ ZEND_VM_SMART_BRANCH(result, 0);
+ ZVAL_BOOL(EX_VAR(opline->result.var), result);
+ ZEND_VM_NEXT_OPCODE();
+}
+
+ZEND_VM_TYPE_SPEC_HANDLER(ZEND_IS_SMALLER_OR_EQUAL, (op1_info == MAY_BE_DOUBLE && op2_info == MAY_BE_DOUBLE), ZEND_IS_SMALLER_OR_EQUAL_DOUBLE, CONST|TMPVARCV, CONST|TMPVARCV, SPEC(SMART_BRANCH,NO_CONST_CONST))
+{
+ USE_OPLINE
+ zval *op1, *op2;
+ int result;
+
+ op1 = GET_OP1_ZVAL_PTR_UNDEF(BP_VAR_R);
+ op2 = GET_OP2_ZVAL_PTR_UNDEF(BP_VAR_R);
+ result = (Z_DVAL_P(op1) <= Z_DVAL_P(op2));
+ ZEND_VM_SMART_BRANCH(result, 0);
+ ZVAL_BOOL(EX_VAR(opline->result.var), result);
+ ZEND_VM_NEXT_OPCODE();
+}
+
+ZEND_VM_TYPE_SPEC_HANDLER(ZEND_PRE_INC, (res_info == MAY_BE_LONG && op1_info == MAY_BE_LONG), ZEND_PRE_INC_LONG_NO_OVERFLOW, TMPVARCV, ANY, SPEC(RETVAL))
+{
+ USE_OPLINE
+ zval *var_ptr;
+
+ var_ptr = GET_OP1_ZVAL_PTR_PTR_UNDEF(BP_VAR_RW);
+ Z_LVAL_P(var_ptr)++;
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_LONG(EX_VAR(opline->result.var), Z_LVAL_P(var_ptr));
+ }
+ ZEND_VM_NEXT_OPCODE();
+}
+
+ZEND_VM_TYPE_SPEC_HANDLER(ZEND_PRE_INC, (op1_info == MAY_BE_LONG), ZEND_PRE_INC_LONG, TMPVARCV, ANY, SPEC(RETVAL))
+{
+ USE_OPLINE
+ zval *var_ptr;
+
+ var_ptr = GET_OP1_ZVAL_PTR_PTR_UNDEF(BP_VAR_RW);
+ fast_long_increment_function(var_ptr);
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_COPY_VALUE(EX_VAR(opline->result.var), var_ptr);
+ }
+ ZEND_VM_NEXT_OPCODE();
+}
+
+ZEND_VM_TYPE_SPEC_HANDLER(ZEND_PRE_INC, (op1_info == (MAY_BE_LONG|MAY_BE_DOUBLE)), ZEND_PRE_INC_LONG_OR_DOUBLE, TMPVARCV, ANY, SPEC(RETVAL))
+{
+ USE_OPLINE
+ zval *var_ptr;
+
+ var_ptr = GET_OP1_ZVAL_PTR_PTR_UNDEF(BP_VAR_RW);
+ if (EXPECTED(Z_TYPE_P(var_ptr) == IS_LONG)) {
+ fast_long_increment_function(var_ptr);
+ } else {
+ Z_DVAL_P(var_ptr)++;
+ }
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_COPY_VALUE(EX_VAR(opline->result.var), var_ptr);
+ }
+ ZEND_VM_NEXT_OPCODE();
+}
+
+ZEND_VM_TYPE_SPEC_HANDLER(ZEND_PRE_DEC, (res_info == MAY_BE_LONG && op1_info == MAY_BE_LONG), ZEND_PRE_DEC_LONG_NO_OVERFLOW, TMPVARCV, ANY, SPEC(RETVAL))
+{
+ USE_OPLINE
+ zval *var_ptr;
+
+ var_ptr = GET_OP1_ZVAL_PTR_PTR_UNDEF(BP_VAR_RW);
+ Z_LVAL_P(var_ptr)--;
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_LONG(EX_VAR(opline->result.var), Z_LVAL_P(var_ptr));
+ }
+ ZEND_VM_NEXT_OPCODE();
+}
+
+ZEND_VM_TYPE_SPEC_HANDLER(ZEND_PRE_DEC, (op1_info == MAY_BE_LONG), ZEND_PRE_DEC_LONG, TMPVARCV, ANY, SPEC(RETVAL))
+{
+ USE_OPLINE
+ zval *var_ptr;
+
+ var_ptr = GET_OP1_ZVAL_PTR_PTR_UNDEF(BP_VAR_RW);
+ fast_long_decrement_function(var_ptr);
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_COPY_VALUE(EX_VAR(opline->result.var), var_ptr);
+ }
+ ZEND_VM_NEXT_OPCODE();
+}
+
+ZEND_VM_TYPE_SPEC_HANDLER(ZEND_PRE_DEC, (op1_info == (MAY_BE_LONG|MAY_BE_DOUBLE)), ZEND_PRE_DEC_LONG_OR_DOUBLE, TMPVARCV, ANY, SPEC(RETVAL))
+{
+ USE_OPLINE
+ zval *var_ptr;
+
+ var_ptr = GET_OP1_ZVAL_PTR_PTR_UNDEF(BP_VAR_RW);
+ if (EXPECTED(Z_TYPE_P(var_ptr) == IS_LONG)) {
+ fast_long_decrement_function(var_ptr);
+ } else {
+ Z_DVAL_P(var_ptr)--;
+ }
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_COPY_VALUE(EX_VAR(opline->result.var), var_ptr);
+ }
+ ZEND_VM_NEXT_OPCODE();
+}
+
+ZEND_VM_TYPE_SPEC_HANDLER(ZEND_POST_INC, (res_info == MAY_BE_LONG && op1_info == MAY_BE_LONG), ZEND_POST_INC_LONG_NO_OVERFLOW, TMPVARCV, ANY)
+{
+ USE_OPLINE
+ zval *var_ptr;
+
+ var_ptr = GET_OP1_ZVAL_PTR_PTR_UNDEF(BP_VAR_RW);
+ ZVAL_LONG(EX_VAR(opline->result.var), Z_LVAL_P(var_ptr));
+ Z_LVAL_P(var_ptr)++;
+ ZEND_VM_NEXT_OPCODE();
+}
+
+ZEND_VM_TYPE_SPEC_HANDLER(ZEND_POST_INC, (op1_info == MAY_BE_LONG), ZEND_POST_INC_LONG, TMPVARCV, ANY)
+{
+ USE_OPLINE
+ zval *var_ptr;
+
+ var_ptr = GET_OP1_ZVAL_PTR_PTR_UNDEF(BP_VAR_RW);
+ ZVAL_LONG(EX_VAR(opline->result.var), Z_LVAL_P(var_ptr));
+ fast_long_increment_function(var_ptr);
+ ZEND_VM_NEXT_OPCODE();
+}
+
+ZEND_VM_TYPE_SPEC_HANDLER(ZEND_POST_INC, (op1_info == (MAY_BE_LONG|MAY_BE_DOUBLE)), ZEND_POST_INC_LONG_OR_DOUBLE, TMPVARCV, ANY)
+{
+ USE_OPLINE
+ zval *var_ptr;
+
+ var_ptr = GET_OP1_ZVAL_PTR_PTR_UNDEF(BP_VAR_RW);
+ ZVAL_COPY_VALUE(EX_VAR(opline->result.var), var_ptr);
+ if (EXPECTED(Z_TYPE_P(var_ptr) == IS_LONG)) {
+ fast_long_increment_function(var_ptr);
+ } else {
+ Z_DVAL_P(var_ptr)++;
+ }
+ ZEND_VM_NEXT_OPCODE();
+}
+
+ZEND_VM_TYPE_SPEC_HANDLER(ZEND_POST_DEC, (res_info == MAY_BE_LONG && op1_info == MAY_BE_LONG), ZEND_POST_DEC_LONG_NO_OVERFLOW, TMPVARCV, ANY)
+{
+ USE_OPLINE
+ zval *var_ptr;
+
+ var_ptr = GET_OP1_ZVAL_PTR_PTR_UNDEF(BP_VAR_RW);
+ ZVAL_LONG(EX_VAR(opline->result.var), Z_LVAL_P(var_ptr));
+ Z_LVAL_P(var_ptr)--;
+ ZEND_VM_NEXT_OPCODE();
+}
+
+ZEND_VM_TYPE_SPEC_HANDLER(ZEND_POST_DEC, (op1_info == MAY_BE_LONG), ZEND_POST_DEC_LONG, TMPVARCV, ANY)
+{
+ USE_OPLINE
+ zval *var_ptr;
+
+ var_ptr = GET_OP1_ZVAL_PTR_PTR_UNDEF(BP_VAR_RW);
+ ZVAL_LONG(EX_VAR(opline->result.var), Z_LVAL_P(var_ptr));
+ fast_long_decrement_function(var_ptr);
+ ZEND_VM_NEXT_OPCODE();
+}
+
+ZEND_VM_TYPE_SPEC_HANDLER(ZEND_POST_DEC, (op1_info == (MAY_BE_LONG|MAY_BE_DOUBLE)), ZEND_POST_DEC_LONG_OR_DOUBLE, TMPVARCV, ANY)
+{
+ USE_OPLINE
+ zval *var_ptr;
+
+ var_ptr = GET_OP1_ZVAL_PTR_PTR_UNDEF(BP_VAR_RW);
+ ZVAL_COPY_VALUE(EX_VAR(opline->result.var), var_ptr);
+ if (EXPECTED(Z_TYPE_P(var_ptr) == IS_LONG)) {
+ fast_long_decrement_function(var_ptr);
+ } else {
+ Z_DVAL_P(var_ptr)--;
+ }
+ ZEND_VM_NEXT_OPCODE();
+}
+
+ZEND_VM_TYPE_SPEC_HANDLER(ZEND_QM_ASSIGN, (op1_info == MAY_BE_DOUBLE), ZEND_QM_ASSIGN_DOUBLE, CONST|TMPVARCV, ANY)
+{
+ USE_OPLINE
+ zend_free_op free_op1;
+ zval *value;
+
+ value = GET_OP1_ZVAL_PTR_UNDEF(BP_VAR_R);
+ ZVAL_DOUBLE(EX_VAR(opline->result.var), Z_DVAL_P(value));
+ ZEND_VM_NEXT_OPCODE();
+}
+
+ZEND_VM_TYPE_SPEC_HANDLER(ZEND_QM_ASSIGN, (!(op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-(MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG|MAY_BE_DOUBLE)))), ZEND_QM_ASSIGN_NOREF, CONST|TMPVARCV, ANY)
+{
+ USE_OPLINE
+ zend_free_op free_op1;
+ zval *value;
+
+ value = GET_OP1_ZVAL_PTR_UNDEF(BP_VAR_R);
+ ZVAL_COPY_VALUE(EX_VAR(opline->result.var), value);
+ ZEND_VM_NEXT_OPCODE();
+}
+
+ZEND_VM_TYPE_SPEC_HANDLER(ZEND_FETCH_DIM_R, (!(op2_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))), ZEND_FETCH_DIM_R_INDEX, CONST|TMPVAR|CV, CONST|TMPVAR|CV)
+{
+ USE_OPLINE
+ zend_free_op free_op1, free_op2;
+ zval *container, *dim, *value;
+ zend_long offset;
+
+ container = GET_OP1_ZVAL_PTR_UNDEF(BP_VAR_R);
+ dim = GET_OP2_ZVAL_PTR_UNDEF(BP_VAR_R);
+ if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) {
+ZEND_VM_C_LABEL(fetch_dim_r_index_array):
+ if (EXPECTED(Z_TYPE_P(dim) == IS_LONG)) {
+ offset = Z_LVAL_P(dim);
+ } else {
+ offset = zval_get_long(dim);
+ }
+ ZEND_HASH_INDEX_FIND(Z_ARRVAL_P(container), offset, value, ZEND_VM_C_LABEL(fetch_dim_r_index_undef));
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ if (OP1_TYPE & (IS_TMP_VAR|IS_VAR)) {
+ SAVE_OPLINE();
+ FREE_OP1();
+ ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
+ } else {
+ ZEND_VM_NEXT_OPCODE();
+ }
+ } else if (OP1_TYPE != IS_CONST && EXPECTED(Z_TYPE_P(container) == IS_REFERENCE)) {
+ container = Z_REFVAL_P(container);
+ if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) {
+ ZEND_VM_C_GOTO(fetch_dim_r_index_array);
+ } else {
+ ZEND_VM_C_GOTO(fetch_dim_r_index_slow);
+ }
+ } else {
+ZEND_VM_C_LABEL(fetch_dim_r_index_slow):
+ SAVE_OPLINE();
+ zend_fetch_dimension_address_read_R_slow(EX_VAR(opline->result.var), container, dim);
+ FREE_OP1();
+ ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
+ }
+
+ZEND_VM_C_LABEL(fetch_dim_r_index_undef):
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ SAVE_OPLINE();
+ zend_error(E_NOTICE, "Undefined offset: " ZEND_LONG_FMT, offset);
+ FREE_OP1();
+ ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
+}
+
+ZEND_VM_TYPE_SPEC_HANDLER(ZEND_SEND_VAR, (op1_info & (MAY_BE_UNDEF|MAY_BE_REF)) == 0, ZEND_SEND_VAR_SIMPLE, CV|VAR, NUM)
+{
+ USE_OPLINE
+ zval *varptr, *arg;
+ zend_free_op free_op1;
+
+ varptr = GET_OP1_ZVAL_PTR_UNDEF(BP_VAR_R);
+ arg = ZEND_CALL_VAR(EX(call), opline->result.var);
+
+ if (OP1_TYPE == IS_CV) {
+ ZVAL_COPY(arg, varptr);
+ } else /* if (OP1_TYPE == IS_VAR) */ {
+ ZVAL_COPY_VALUE(arg, varptr);
+ }
+
+ ZEND_VM_NEXT_OPCODE();
+}
+
+ZEND_VM_TYPE_SPEC_HANDLER(ZEND_SEND_VAR_EX, (op1_info & (MAY_BE_UNDEF|MAY_BE_REF)) == 0, ZEND_SEND_VAR_EX_SIMPLE, CV|VAR, NUM, SPEC(QUICK_ARG))
+{
+ USE_OPLINE
+ zval *varptr, *arg;
+ zend_free_op free_op1;
+ uint32_t arg_num = opline->op2.num;
+
+ if (EXPECTED(arg_num <= MAX_ARG_FLAG_NUM)) {
+ if (QUICK_ARG_SHOULD_BE_SENT_BY_REF(EX(call)->func, arg_num)) {
+ ZEND_VM_C_GOTO(send_var_by_ref_simple);
+ }
+ } else if (ARG_SHOULD_BE_SENT_BY_REF(EX(call)->func, arg_num)) {
+ZEND_VM_C_LABEL(send_var_by_ref_simple):
+ ZEND_VM_DISPATCH_TO_HANDLER(ZEND_SEND_REF);
+ }
+
+ varptr = GET_OP1_ZVAL_PTR_UNDEF(BP_VAR_R);
+ arg = ZEND_CALL_VAR(EX(call), opline->result.var);
+
+ if (OP1_TYPE == IS_CV) {
+ ZVAL_COPY(arg, varptr);
+ } else /* if (OP1_TYPE == IS_VAR) */ {
+ ZVAL_COPY_VALUE(arg, varptr);
+ }
+
+ ZEND_VM_NEXT_OPCODE();
+}
+
ZEND_VM_DEFINE_OP(137, ZEND_OP_DATA);
diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h
index aba8081585..b0da3c043d 100644
--- a/Zend/zend_vm_execute.h
+++ b/Zend/zend_vm_execute.h
@@ -306,7 +306,18 @@ static zend_uchar zend_user_opcodes[256] = {0,
241,242,243,244,245,246,247,248,249,250,251,252,253,254,255
};
+#define SPEC_START_MASK 0x0000ffff
+#define SPEC_RULE_OP1 0x00010000
+#define SPEC_RULE_OP2 0x00020000
+#define SPEC_RULE_OP_DATA 0x00040000
+#define SPEC_RULE_RETVAL 0x00080000
+#define SPEC_RULE_QUICK_ARG 0x00100000
+#define SPEC_RULE_SMART_BRANCH 0x00200000
+#define SPEC_RULE_DIM_OBJ 0x00400000
+
+static const uint32_t *zend_spec_handlers;
static const void **zend_opcode_handlers;
+static int zend_handlers_count;
static const void *zend_vm_get_opcode_handler(zend_uchar opcode, const zend_op* op);
@@ -377,7 +388,7 @@ typedef ZEND_OPCODE_HANDLER_RET (ZEND_FASTCALL *opcode_handler_t) (ZEND_OPCODE_H
#define HANDLE_EXCEPTION() LOAD_OPLINE(); ZEND_VM_CONTINUE()
#define HANDLE_EXCEPTION_LEAVE() LOAD_OPLINE(); ZEND_VM_LEAVE()
#if defined(ZEND_VM_FP_GLOBAL_REG)
-# define ZEND_VM_ENTER() execute_data = EG(current_execute_data); LOAD_OPLINE(); ZEND_VM_CONTINUE()
+# define ZEND_VM_ENTER() execute_data = EG(current_execute_data); LOAD_OPLINE(); ZEND_VM_INTERRUPT_CHECK(); ZEND_VM_CONTINUE()
# define ZEND_VM_LEAVE() ZEND_VM_CONTINUE()
#elif defined(ZEND_VM_IP_GLOBAL_REG)
# define ZEND_VM_ENTER() opline = EG(current_execute_data)->opline; return 1
@@ -405,10 +416,11 @@ ZEND_API void execute_ex(zend_execute_data *ex)
LOAD_OPLINE();
+ ZEND_VM_INTERRUPT_CHECK();
while (1) {
#if !defined(ZEND_VM_FP_GLOBAL_REG) || !defined(ZEND_VM_IP_GLOBAL_REG)
- int ret;
+ int ret;
#endif
#if defined(ZEND_VM_FP_GLOBAL_REG) && defined(ZEND_VM_IP_GLOBAL_REG)
((opcode_handler_t)OPLINE->handler)(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
@@ -425,6 +437,7 @@ ZEND_API void execute_ex(zend_execute_data *ex)
#else
if (EXPECTED(ret > 0)) {
execute_data = EG(current_execute_data);
+ ZEND_VM_INTERRUPT_CHECK();
} else {
# ifdef ZEND_VM_IP_GLOBAL_REG
opline = orig_opline;
@@ -446,7 +459,7 @@ ZEND_API void zend_execute(zend_op_array *op_array, zval *return_value)
return;
}
- execute_data = zend_vm_stack_push_call_frame(ZEND_CALL_TOP_CODE,
+ execute_data = zend_vm_stack_push_call_frame(ZEND_CALL_TOP_CODE | ZEND_CALL_HAS_SYMBOL_TABLE,
(zend_function*)op_array, 0, zend_get_called_scope(EG(current_execute_data)), zend_get_this_object(EG(current_execute_data)));
if (EG(current_execute_data)) {
execute_data->symbol_table = zend_rebuild_symbol_table();
@@ -464,44 +477,73 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_leave_helper_SPEC(ZEND_OPCODE_
zend_execute_data *old_execute_data;
uint32_t call_info = EX_CALL_INFO();
- if (EXPECTED(ZEND_CALL_KIND_EX(call_info) == ZEND_CALL_NESTED_FUNCTION)) {
- zend_object *object;
+ if (EXPECTED((call_info & (ZEND_CALL_CODE|ZEND_CALL_TOP|ZEND_CALL_HAS_SYMBOL_TABLE|ZEND_CALL_FREE_EXTRA_ARGS|ZEND_CALL_ALLOCATED)) == 0)) {
+ i_free_compiled_variables(execute_data);
+
+ EG(current_execute_data) = EX(prev_execute_data);
+ if (UNEXPECTED(call_info & ZEND_CALL_RELEASE_THIS)) {
+ zend_object *object = Z_OBJ(execute_data->This);
+#if 0
+ if (UNEXPECTED(EG(exception) != NULL) && (EX(opline)->op1.num & ZEND_CALL_CTOR)) {
+#else
+ if (UNEXPECTED(EG(exception) != NULL) && (call_info & ZEND_CALL_CTOR)) {
+#endif
+ GC_REFCOUNT(object)--;
+ if (GC_REFCOUNT(object) == 1) {
+ zend_object_store_ctor_failed(object);
+ }
+ }
+ OBJ_RELEASE(object);
+ } else if (UNEXPECTED(call_info & ZEND_CALL_CLOSURE)) {
+ OBJ_RELEASE((zend_object*)execute_data->func->op_array.prototype);
+ }
+ EG(vm_stack_top) = (zval*)execute_data;
+ execute_data = EX(prev_execute_data);
+ if (UNEXPECTED(EG(exception) != NULL)) {
+ const zend_op *old_opline = EX(opline);
+ zend_throw_exception_internal(NULL);
+ if (RETURN_VALUE_USED(old_opline)) {
+ zval_ptr_dtor(EX_VAR(old_opline->result.var));
+ }
+ HANDLE_EXCEPTION_LEAVE();
+ }
+
+ LOAD_NEXT_OPLINE();
+ ZEND_VM_LEAVE();
+ } else if (EXPECTED((call_info & (ZEND_CALL_CODE|ZEND_CALL_TOP)) == 0)) {
i_free_compiled_variables(execute_data);
- if (UNEXPECTED(EX(symbol_table) != NULL)) {
+
+ if (UNEXPECTED(call_info & ZEND_CALL_HAS_SYMBOL_TABLE)) {
zend_clean_and_cache_symbol_table(EX(symbol_table));
}
- zend_vm_stack_free_extra_args_ex(call_info, execute_data);
- old_execute_data = execute_data;
- execute_data = EG(current_execute_data) = EX(prev_execute_data);
- if (UNEXPECTED(call_info & ZEND_CALL_CLOSURE)) {
- OBJ_RELEASE((zend_object*)old_execute_data->func->op_array.prototype);
- }
+ EG(current_execute_data) = EX(prev_execute_data);
if (UNEXPECTED(call_info & ZEND_CALL_RELEASE_THIS)) {
- object = Z_OBJ(old_execute_data->This);
+ zend_object *object = Z_OBJ(execute_data->This);
#if 0
if (UNEXPECTED(EG(exception) != NULL) && (EX(opline)->op1.num & ZEND_CALL_CTOR)) {
- if (!(EX(opline)->op1.num & ZEND_CALL_CTOR_RESULT_UNUSED)) {
#else
if (UNEXPECTED(EG(exception) != NULL) && (call_info & ZEND_CALL_CTOR)) {
- if (!(call_info & ZEND_CALL_CTOR_RESULT_UNUSED)) {
#endif
- GC_REFCOUNT(object)--;
- }
+ GC_REFCOUNT(object)--;
if (GC_REFCOUNT(object) == 1) {
zend_object_store_ctor_failed(object);
}
}
OBJ_RELEASE(object);
+ } else if (UNEXPECTED(call_info & ZEND_CALL_CLOSURE)) {
+ OBJ_RELEASE((zend_object*)execute_data->func->op_array.prototype);
}
- EG(scope) = EX(func)->op_array.scope;
+ zend_vm_stack_free_extra_args_ex(call_info, execute_data);
+ old_execute_data = execute_data;
+ execute_data = EX(prev_execute_data);
zend_vm_stack_free_call_frame_ex(call_info, old_execute_data);
if (UNEXPECTED(EG(exception) != NULL)) {
const zend_op *old_opline = EX(opline);
zend_throw_exception_internal(NULL);
- if (old_opline->opcode != ZEND_HANDLE_EXCEPTION && RETURN_VALUE_USED(old_opline)) {
+ if (RETURN_VALUE_USED(old_opline)) {
zval_ptr_dtor(EX_VAR(old_opline->result.var));
}
HANDLE_EXCEPTION_LEAVE();
@@ -509,8 +551,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_leave_helper_SPEC(ZEND_OPCODE_
LOAD_NEXT_OPLINE();
ZEND_VM_LEAVE();
- }
- if (EXPECTED((ZEND_CALL_KIND_EX(call_info) & ZEND_CALL_TOP) == 0)) {
+ } else if (EXPECTED((call_info & ZEND_CALL_TOP) == 0)) {
zend_detach_symbol_table(execute_data);
destroy_op_array(&EX(func)->op_array);
efree_size(EX(func), sizeof(zend_op_array));
@@ -527,23 +568,26 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_leave_helper_SPEC(ZEND_OPCODE_
LOAD_NEXT_OPLINE();
ZEND_VM_LEAVE();
} else {
- if (ZEND_CALL_KIND_EX(call_info) == ZEND_CALL_TOP_FUNCTION) {
+ if (EXPECTED((call_info & ZEND_CALL_CODE) == 0)) {
i_free_compiled_variables(execute_data);
- if (UNEXPECTED(EX(symbol_table) != NULL)) {
- zend_clean_and_cache_symbol_table(EX(symbol_table));
+ if (UNEXPECTED(call_info & (ZEND_CALL_HAS_SYMBOL_TABLE|ZEND_CALL_FREE_EXTRA_ARGS))) {
+ if (UNEXPECTED(call_info & ZEND_CALL_HAS_SYMBOL_TABLE)) {
+ zend_clean_and_cache_symbol_table(EX(symbol_table));
+ }
+ zend_vm_stack_free_extra_args_ex(call_info, execute_data);
}
- zend_vm_stack_free_extra_args_ex(call_info, execute_data);
EG(current_execute_data) = EX(prev_execute_data);
if (UNEXPECTED(call_info & ZEND_CALL_CLOSURE)) {
OBJ_RELEASE((zend_object*)EX(func)->op_array.prototype);
}
+ ZEND_VM_RETURN();
} else /* if (call_kind == ZEND_CALL_TOP_CODE) */ {
zend_array *symbol_table = EX(symbol_table);
zend_detach_symbol_table(execute_data);
old_execute_data = EX(prev_execute_data);
while (old_execute_data) {
- if (old_execute_data->func && ZEND_USER_CODE(old_execute_data->func->op_array.type)) {
+ if (old_execute_data->func && (ZEND_CALL_INFO(old_execute_data) & ZEND_CALL_HAS_SYMBOL_TABLE)) {
if (old_execute_data->symbol_table == symbol_table) {
zend_attach_symbol_table(old_execute_data);
}
@@ -552,9 +596,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_leave_helper_SPEC(ZEND_OPCODE_
old_execute_data = old_execute_data->prev_execute_data;
}
EG(current_execute_data) = EX(prev_execute_data);
+ ZEND_VM_RETURN();
}
-
- ZEND_VM_RETURN();
}
}
@@ -566,12 +609,13 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_JMP_SPEC_HANDLER(ZEND_OPCODE_H
ZEND_VM_CONTINUE();
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_ICALL_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_ICALL_SPEC_RETVAL_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
zend_execute_data *call = EX(call);
zend_function *fbc = call->func;
zval *ret;
+ zval retval;
SAVE_OPLINE();
EX(call) = call->prev_execute_data;
@@ -579,9 +623,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_ICALL_SPEC_HANDLER(ZEND_OPC
call->prev_execute_data = execute_data;
EG(current_execute_data) = call;
- ret = EX_VAR(opline->result.var);
+ ret = 0 ? EX_VAR(opline->result.var) : &retval;
ZVAL_NULL(ret);
- Z_VAR_FLAGS_P(ret) = 0;
fbc->internal_function.handler(call, ret);
@@ -589,30 +632,78 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_ICALL_SPEC_HANDLER(ZEND_OPC
ZEND_ASSERT(
EG(exception) || !call->func ||
!(call->func->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) ||
- zend_verify_internal_return_type(call->func, EX_VAR(opline->result.var)));
+ zend_verify_internal_return_type(call->func, ret));
+ ZEND_ASSERT(!Z_ISREF_P(ret));
#endif
EG(current_execute_data) = call->prev_execute_data;
zend_vm_stack_free_args(call);
zend_vm_stack_free_call_frame(call);
- if (!RETURN_VALUE_USED(opline)) {
- zval_ptr_dtor(EX_VAR(opline->result.var));
+ if (!0) {
+ zval_ptr_dtor(ret);
}
if (UNEXPECTED(EG(exception) != NULL)) {
zend_throw_exception_internal(NULL);
- if (RETURN_VALUE_USED(opline)) {
+ if (0) {
zval_ptr_dtor(EX_VAR(opline->result.var));
}
HANDLE_EXCEPTION();
}
- ZEND_VM_INTERRUPT_CHECK();
- ZEND_VM_NEXT_OPCODE();
+ ZEND_VM_SET_OPCODE(opline + 1);
+ ZEND_VM_CONTINUE();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_ICALL_SPEC_RETVAL_USED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zend_execute_data *call = EX(call);
+ zend_function *fbc = call->func;
+ zval *ret;
+ zval retval;
+
+ SAVE_OPLINE();
+ EX(call) = call->prev_execute_data;
+
+ call->prev_execute_data = execute_data;
+ EG(current_execute_data) = call;
+
+ ret = 1 ? EX_VAR(opline->result.var) : &retval;
+ ZVAL_NULL(ret);
+
+ fbc->internal_function.handler(call, ret);
+
+#if ZEND_DEBUG
+ ZEND_ASSERT(
+ EG(exception) || !call->func ||
+ !(call->func->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) ||
+ zend_verify_internal_return_type(call->func, ret));
+ ZEND_ASSERT(!Z_ISREF_P(ret));
+#endif
+
+ EG(current_execute_data) = call->prev_execute_data;
+ zend_vm_stack_free_args(call);
+ zend_vm_stack_free_call_frame(call);
+
+ if (!1) {
+ zval_ptr_dtor(ret);
+ }
+
+ if (UNEXPECTED(EG(exception) != NULL)) {
+ zend_throw_exception_internal(NULL);
+ if (1) {
+ zval_ptr_dtor(EX_VAR(opline->result.var));
+ }
+ HANDLE_EXCEPTION();
+ }
+
+ ZEND_VM_SET_OPCODE(opline + 1);
+ ZEND_VM_CONTINUE();
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_UCALL_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_UCALL_SPEC_RETVAL_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
zend_execute_data *call = EX(call);
@@ -622,22 +713,41 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_UCALL_SPEC_HANDLER(ZEND_OPC
SAVE_OPLINE();
EX(call) = call->prev_execute_data;
- EG(scope) = NULL;
ret = NULL;
- call->symbol_table = NULL;
- if (RETURN_VALUE_USED(opline)) {
+ if (0) {
ret = EX_VAR(opline->result.var);
ZVAL_NULL(ret);
- Z_VAR_FLAGS_P(ret) = 0;
}
call->prev_execute_data = execute_data;
- i_init_func_execute_data(call, &fbc->op_array, ret, 0);
+ i_init_func_execute_data(call, &fbc->op_array, ret);
ZEND_VM_ENTER();
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_FCALL_BY_NAME_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_UCALL_SPEC_RETVAL_USED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zend_execute_data *call = EX(call);
+ zend_function *fbc = call->func;
+ zval *ret;
+
+ SAVE_OPLINE();
+ EX(call) = call->prev_execute_data;
+
+ ret = NULL;
+ if (1) {
+ ret = EX_VAR(opline->result.var);
+ ZVAL_NULL(ret);
+ }
+
+ call->prev_execute_data = execute_data;
+ i_init_func_execute_data(call, &fbc->op_array, ret);
+
+ ZEND_VM_ENTER();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_FCALL_BY_NAME_SPEC_RETVAL_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
zend_execute_data *call = EX(call);
@@ -648,33 +758,97 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_FCALL_BY_NAME_SPEC_HANDLER(
EX(call) = call->prev_execute_data;
if (EXPECTED(fbc->type == ZEND_USER_FUNCTION)) {
- EG(scope) = NULL;
- if (UNEXPECTED((fbc->common.fn_flags & ZEND_ACC_GENERATOR) != 0)) {
- if (EXPECTED(RETURN_VALUE_USED(opline))) {
- ret = EX_VAR(opline->result.var);
- zend_generator_create_zval(call, &fbc->op_array, ret);
- Z_VAR_FLAGS_P(ret) = 0;
- } else {
- zend_vm_stack_free_args(call);
+ ret = NULL;
+ if (0) {
+ ret = EX_VAR(opline->result.var);
+ ZVAL_NULL(ret);
+ }
+
+ call->prev_execute_data = execute_data;
+ i_init_func_execute_data(call, &fbc->op_array, ret);
+
+ ZEND_VM_ENTER();
+ } else {
+ zval retval;
+ ZEND_ASSERT(fbc->type == ZEND_INTERNAL_FUNCTION);
+
+ if (UNEXPECTED((fbc->common.fn_flags & ZEND_ACC_DEPRECATED) != 0)) {
+ zend_error(E_DEPRECATED, "Function %s%s%s() is deprecated",
+ fbc->common.scope ? ZSTR_VAL(fbc->common.scope->name) : "",
+ fbc->common.scope ? "::" : "",
+ ZSTR_VAL(fbc->common.function_name));
+ if (UNEXPECTED(EG(exception) != NULL)) {
+ HANDLE_EXCEPTION();
}
+ }
+
+ call->prev_execute_data = execute_data;
+ EG(current_execute_data) = call;
+ if (UNEXPECTED(fbc->common.fn_flags & ZEND_ACC_HAS_TYPE_HINTS)
+ && UNEXPECTED(!zend_verify_internal_arg_types(fbc, call))) {
zend_vm_stack_free_call_frame(call);
- } else {
- ret = NULL;
- call->symbol_table = NULL;
- if (RETURN_VALUE_USED(opline)) {
- ret = EX_VAR(opline->result.var);
- ZVAL_NULL(ret);
- Z_VAR_FLAGS_P(ret) = 0;
- }
+ zend_throw_exception_internal(NULL);
+ HANDLE_EXCEPTION();
+ }
+
+ ret = 0 ? EX_VAR(opline->result.var) : &retval;
+ ZVAL_NULL(ret);
- call->prev_execute_data = execute_data;
- i_init_func_execute_data(call, &fbc->op_array, ret, 0);
+ fbc->internal_function.handler(call, ret);
- ZEND_VM_ENTER();
+#if ZEND_DEBUG
+ if (!EG(exception) && call->func) {
+ ZEND_ASSERT(!(call->func->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) ||
+ zend_verify_internal_return_type(call->func, ret));
+ ZEND_ASSERT((call->func->common.fn_flags & ZEND_ACC_RETURN_REFERENCE)
+ ? Z_ISREF_P(ret) : !Z_ISREF_P(ret));
}
- EG(scope) = EX(func)->op_array.scope;
+#endif
+
+ EG(current_execute_data) = call->prev_execute_data;
+ zend_vm_stack_free_args(call);
+ zend_vm_stack_free_call_frame(call);
+
+ if (!0) {
+ zval_ptr_dtor(ret);
+ }
+ }
+
+ if (UNEXPECTED(EG(exception) != NULL)) {
+ zend_throw_exception_internal(NULL);
+ if (0) {
+ zval_ptr_dtor(EX_VAR(opline->result.var));
+ }
+ HANDLE_EXCEPTION();
+ }
+ ZEND_VM_SET_OPCODE(opline + 1);
+ ZEND_VM_CONTINUE();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_FCALL_BY_NAME_SPEC_RETVAL_USED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zend_execute_data *call = EX(call);
+ zend_function *fbc = call->func;
+ zval *ret;
+
+ SAVE_OPLINE();
+ EX(call) = call->prev_execute_data;
+
+ if (EXPECTED(fbc->type == ZEND_USER_FUNCTION)) {
+ ret = NULL;
+ if (1) {
+ ret = EX_VAR(opline->result.var);
+ ZVAL_NULL(ret);
+ }
+
+ call->prev_execute_data = execute_data;
+ i_init_func_execute_data(call, &fbc->op_array, ret);
+
+ ZEND_VM_ENTER();
} else {
+ zval retval;
ZEND_ASSERT(fbc->type == ZEND_INTERNAL_FUNCTION);
if (UNEXPECTED((fbc->common.fn_flags & ZEND_ACC_DEPRECATED) != 0)) {
@@ -690,57 +864,48 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_FCALL_BY_NAME_SPEC_HANDLER(
call->prev_execute_data = execute_data;
EG(current_execute_data) = call;
- if (fbc->common.fn_flags & ZEND_ACC_HAS_TYPE_HINTS) {
- uint32_t i;
- uint32_t num_args = ZEND_CALL_NUM_ARGS(call);
- zval *p = ZEND_CALL_ARG(call, 1);
-
- for (i = 0; i < num_args; ++i) {
- if (UNEXPECTED(!zend_verify_internal_arg_type(fbc, i + 1, p))) {
- EG(current_execute_data) = call->prev_execute_data;
- zend_vm_stack_free_args(call);
- zend_vm_stack_free_call_frame(call);
- zend_throw_exception_internal(NULL);
- HANDLE_EXCEPTION();
- }
- p++;
- }
+ if (UNEXPECTED(fbc->common.fn_flags & ZEND_ACC_HAS_TYPE_HINTS)
+ && UNEXPECTED(!zend_verify_internal_arg_types(fbc, call))) {
+ zend_vm_stack_free_call_frame(call);
+ zend_throw_exception_internal(NULL);
+ HANDLE_EXCEPTION();
}
- ret = EX_VAR(opline->result.var);
+ ret = 1 ? EX_VAR(opline->result.var) : &retval;
ZVAL_NULL(ret);
- Z_VAR_FLAGS_P(ret) = (fbc->common.fn_flags & ZEND_ACC_RETURN_REFERENCE) != 0 ? IS_VAR_RET_REF : 0;
fbc->internal_function.handler(call, ret);
#if ZEND_DEBUG
- ZEND_ASSERT(
- EG(exception) || !call->func ||
- !(call->func->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) ||
- zend_verify_internal_return_type(call->func, EX_VAR(opline->result.var)));
+ if (!EG(exception) && call->func) {
+ ZEND_ASSERT(!(call->func->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) ||
+ zend_verify_internal_return_type(call->func, ret));
+ ZEND_ASSERT((call->func->common.fn_flags & ZEND_ACC_RETURN_REFERENCE)
+ ? Z_ISREF_P(ret) : !Z_ISREF_P(ret));
+ }
#endif
EG(current_execute_data) = call->prev_execute_data;
zend_vm_stack_free_args(call);
zend_vm_stack_free_call_frame(call);
- if (!RETURN_VALUE_USED(opline)) {
- zval_ptr_dtor(EX_VAR(opline->result.var));
+ if (!1) {
+ zval_ptr_dtor(ret);
}
}
if (UNEXPECTED(EG(exception) != NULL)) {
zend_throw_exception_internal(NULL);
- if (RETURN_VALUE_USED(opline)) {
+ if (1) {
zval_ptr_dtor(EX_VAR(opline->result.var));
}
HANDLE_EXCEPTION();
}
- ZEND_VM_INTERRUPT_CHECK();
- ZEND_VM_NEXT_OPCODE();
+ ZEND_VM_SET_OPCODE(opline + 1);
+ ZEND_VM_CONTINUE();
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_FCALL_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_FCALL_SPEC_RETVAL_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
zend_execute_data *call = EX(call);
@@ -769,73 +934,37 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_FCALL_SPEC_HANDLER(ZEND_OPC
LOAD_OPLINE();
if (EXPECTED(fbc->type == ZEND_USER_FUNCTION)) {
- EG(scope) = fbc->common.scope;
- if (UNEXPECTED((fbc->common.fn_flags & ZEND_ACC_GENERATOR) != 0)) {
- if (EXPECTED(RETURN_VALUE_USED(opline))) {
- ret = EX_VAR(opline->result.var);
- zend_generator_create_zval(call, &fbc->op_array, ret);
- Z_VAR_FLAGS_P(ret) = 0;
- } else {
- if (UNEXPECTED(ZEND_CALL_INFO(call) & ZEND_CALL_CLOSURE)) {
- OBJ_RELEASE((zend_object*)fbc->op_array.prototype);
- }
- zend_vm_stack_free_args(call);
- }
- } else {
- ret = NULL;
- call->symbol_table = NULL;
- if (RETURN_VALUE_USED(opline)) {
- ret = EX_VAR(opline->result.var);
- ZVAL_NULL(ret);
- Z_VAR_FLAGS_P(ret) = 0;
- }
+ ret = NULL;
+ if (0) {
+ ret = EX_VAR(opline->result.var);
+ ZVAL_NULL(ret);
+ }
- call->prev_execute_data = execute_data;
- i_init_func_execute_data(call, &fbc->op_array, ret, 1);
+ call->prev_execute_data = execute_data;
+ i_init_func_execute_data(call, &fbc->op_array, ret);
- if (EXPECTED(zend_execute_ex == execute_ex)) {
- ZEND_VM_ENTER();
- } else {
- ZEND_ADD_CALL_FLAG(call, ZEND_CALL_TOP);
- zend_execute_ex(call);
- }
+ if (EXPECTED(zend_execute_ex == execute_ex)) {
+ ZEND_VM_ENTER();
+ } else {
+ ZEND_ADD_CALL_FLAG(call, ZEND_CALL_TOP);
+ zend_execute_ex(call);
}
} else if (EXPECTED(fbc->type < ZEND_USER_FUNCTION)) {
- int should_change_scope = 0;
-
- if (fbc->common.scope) {
- should_change_scope = 1;
- EG(scope) = fbc->common.scope;
- }
+ zval retval;
call->prev_execute_data = execute_data;
EG(current_execute_data) = call;
- if (fbc->common.fn_flags & ZEND_ACC_HAS_TYPE_HINTS) {
- uint32_t i;
- uint32_t num_args = ZEND_CALL_NUM_ARGS(call);
- zval *p = ZEND_CALL_ARG(call, 1);
-
- for (i = 0; i < num_args; ++i) {
- if (UNEXPECTED(!zend_verify_internal_arg_type(fbc, i + 1, p))) {
- EG(current_execute_data) = call->prev_execute_data;
- zend_vm_stack_free_args(call);
- if (RETURN_VALUE_USED(opline)) {
- ZVAL_UNDEF(EX_VAR(opline->result.var));
- }
- if (UNEXPECTED(should_change_scope)) {
- goto fcall_end_change_scope;
- } else {
- goto fcall_end;
- }
- }
- p++;
+ if (UNEXPECTED(fbc->common.fn_flags & ZEND_ACC_HAS_TYPE_HINTS)
+ && UNEXPECTED(!zend_verify_internal_arg_types(fbc, call))) {
+ if (0) {
+ ZVAL_UNDEF(EX_VAR(opline->result.var));
}
+ goto fcall_end;
}
- ret = EX_VAR(opline->result.var);
+ ret = 0 ? EX_VAR(opline->result.var) : &retval;
ZVAL_NULL(ret);
- Z_VAR_FLAGS_P(ret) = (fbc->common.fn_flags & ZEND_ACC_RETURN_REFERENCE) != 0 ? IS_VAR_RET_REF : 0;
if (!zend_execute_internal) {
/* saves one function call if zend_execute_internal is not used */
@@ -845,94 +974,261 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_FCALL_SPEC_HANDLER(ZEND_OPC
}
#if ZEND_DEBUG
- ZEND_ASSERT(
- EG(exception) || !call->func ||
- !(call->func->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) ||
- zend_verify_internal_return_type(call->func, EX_VAR(opline->result.var)));
+ if (!EG(exception) && call->func) {
+ ZEND_ASSERT(!(call->func->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) ||
+ zend_verify_internal_return_type(call->func, ret));
+ ZEND_ASSERT((call->func->common.fn_flags & ZEND_ACC_RETURN_REFERENCE)
+ ? Z_ISREF_P(ret) : !Z_ISREF_P(ret));
+ }
#endif
EG(current_execute_data) = call->prev_execute_data;
zend_vm_stack_free_args(call);
- if (!RETURN_VALUE_USED(opline)) {
- zval_ptr_dtor(EX_VAR(opline->result.var));
+ if (!0) {
+ zval_ptr_dtor(ret);
}
- if (UNEXPECTED(should_change_scope)) {
- goto fcall_end_change_scope;
- } else {
- goto fcall_end;
- }
} else { /* ZEND_OVERLOADED_FUNCTION */
- /* Not sure what should be done here if it's a static method */
+ zval retval;
+
+ ret = 0 ? EX_VAR(opline->result.var) : &retval;
+
+ call->prev_execute_data = execute_data;
+
+ if (UNEXPECTED(!zend_do_fcall_overloaded(fbc, call, ret))) {
+ HANDLE_EXCEPTION();
+ }
+
+ if (!0) {
+ zval_ptr_dtor(ret);
+ }
+ }
+
+fcall_end:
+ if (UNEXPECTED(ZEND_CALL_INFO(call) & ZEND_CALL_RELEASE_THIS)) {
object = Z_OBJ(call->This);
- if (UNEXPECTED(object == NULL)) {
- zend_vm_stack_free_args(call);
- if (fbc->type == ZEND_OVERLOADED_FUNCTION_TEMPORARY) {
- zend_string_release(fbc->common.function_name);
+#if 0
+ if (UNEXPECTED(EG(exception) != NULL) && (opline->op1.num & ZEND_CALL_CTOR)) {
+#else
+ if (UNEXPECTED(EG(exception) != NULL) && (ZEND_CALL_INFO(call) & ZEND_CALL_CTOR)) {
+#endif
+ GC_REFCOUNT(object)--;
+ if (GC_REFCOUNT(object) == 1) {
+ zend_object_store_ctor_failed(object);
}
- efree(fbc);
- zend_vm_stack_free_call_frame(call);
+ }
+ OBJ_RELEASE(object);
+ }
+
+ zend_vm_stack_free_call_frame(call);
+ if (UNEXPECTED(EG(exception) != NULL)) {
+ zend_throw_exception_internal(NULL);
+ if (0) {
+ zval_ptr_dtor(EX_VAR(opline->result.var));
+ }
+ HANDLE_EXCEPTION();
+ }
+
+ ZEND_VM_SET_OPCODE(opline + 1);
+ ZEND_VM_CONTINUE();
+}
- zend_throw_error(NULL, "Cannot call overloaded function for non-object");
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_FCALL_SPEC_RETVAL_USED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zend_execute_data *call = EX(call);
+ zend_function *fbc = call->func;
+ zend_object *object;
+ zval *ret;
+
+ SAVE_OPLINE();
+ EX(call) = call->prev_execute_data;
+ if (UNEXPECTED((fbc->common.fn_flags & (ZEND_ACC_ABSTRACT|ZEND_ACC_DEPRECATED)) != 0)) {
+ if (UNEXPECTED((fbc->common.fn_flags & ZEND_ACC_ABSTRACT) != 0)) {
+ zend_throw_error(NULL, "Cannot call abstract method %s::%s()", ZSTR_VAL(fbc->common.scope->name), ZSTR_VAL(fbc->common.function_name));
HANDLE_EXCEPTION();
}
+ if (UNEXPECTED((fbc->common.fn_flags & ZEND_ACC_DEPRECATED) != 0)) {
+ zend_error(E_DEPRECATED, "Function %s%s%s() is deprecated",
+ fbc->common.scope ? ZSTR_VAL(fbc->common.scope->name) : "",
+ fbc->common.scope ? "::" : "",
+ ZSTR_VAL(fbc->common.function_name));
+ if (UNEXPECTED(EG(exception) != NULL)) {
+ HANDLE_EXCEPTION();
+ }
+ }
+ }
+
+ LOAD_OPLINE();
- EG(scope) = fbc->common.scope;
+ if (EXPECTED(fbc->type == ZEND_USER_FUNCTION)) {
+ ret = NULL;
+ if (1) {
+ ret = EX_VAR(opline->result.var);
+ ZVAL_NULL(ret);
+ }
- ZVAL_NULL(EX_VAR(opline->result.var));
+ call->prev_execute_data = execute_data;
+ i_init_func_execute_data(call, &fbc->op_array, ret);
+
+ if (EXPECTED(zend_execute_ex == execute_ex)) {
+ ZEND_VM_ENTER();
+ } else {
+ ZEND_ADD_CALL_FLAG(call, ZEND_CALL_TOP);
+ zend_execute_ex(call);
+ }
+ } else if (EXPECTED(fbc->type < ZEND_USER_FUNCTION)) {
+ zval retval;
call->prev_execute_data = execute_data;
EG(current_execute_data) = call;
- object->handlers->call_method(fbc->common.function_name, object, call, EX_VAR(opline->result.var));
- EG(current_execute_data) = call->prev_execute_data;
+ if (UNEXPECTED(fbc->common.fn_flags & ZEND_ACC_HAS_TYPE_HINTS)
+ && UNEXPECTED(!zend_verify_internal_arg_types(fbc, call))) {
+ if (1) {
+ ZVAL_UNDEF(EX_VAR(opline->result.var));
+ }
+ goto fcall_end;
+ }
+
+ ret = 1 ? EX_VAR(opline->result.var) : &retval;
+ ZVAL_NULL(ret);
+
+ if (!zend_execute_internal) {
+ /* saves one function call if zend_execute_internal is not used */
+ fbc->internal_function.handler(call, ret);
+ } else {
+ zend_execute_internal(call, ret);
+ }
+
+#if ZEND_DEBUG
+ if (!EG(exception) && call->func) {
+ ZEND_ASSERT(!(call->func->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) ||
+ zend_verify_internal_return_type(call->func, ret));
+ ZEND_ASSERT((call->func->common.fn_flags & ZEND_ACC_RETURN_REFERENCE)
+ ? Z_ISREF_P(ret) : !Z_ISREF_P(ret));
+ }
+#endif
+
+ EG(current_execute_data) = call->prev_execute_data;
zend_vm_stack_free_args(call);
- if (fbc->type == ZEND_OVERLOADED_FUNCTION_TEMPORARY) {
- zend_string_release(fbc->common.function_name);
+ if (!1) {
+ zval_ptr_dtor(ret);
}
- efree(fbc);
- if (!RETURN_VALUE_USED(opline)) {
- zval_ptr_dtor(EX_VAR(opline->result.var));
- } else {
- Z_VAR_FLAGS_P(EX_VAR(opline->result.var)) = 0;
+ } else { /* ZEND_OVERLOADED_FUNCTION */
+ zval retval;
+
+ ret = 1 ? EX_VAR(opline->result.var) : &retval;
+
+ call->prev_execute_data = execute_data;
+
+ if (UNEXPECTED(!zend_do_fcall_overloaded(fbc, call, ret))) {
+ HANDLE_EXCEPTION();
+ }
+
+ if (!1) {
+ zval_ptr_dtor(ret);
}
}
-fcall_end_change_scope:
+fcall_end:
if (UNEXPECTED(ZEND_CALL_INFO(call) & ZEND_CALL_RELEASE_THIS)) {
object = Z_OBJ(call->This);
#if 0
if (UNEXPECTED(EG(exception) != NULL) && (opline->op1.num & ZEND_CALL_CTOR)) {
- if (!(opline->op1.num & ZEND_CALL_CTOR_RESULT_UNUSED)) {
#else
if (UNEXPECTED(EG(exception) != NULL) && (ZEND_CALL_INFO(call) & ZEND_CALL_CTOR)) {
- if (!(ZEND_CALL_INFO(call) & ZEND_CALL_CTOR_RESULT_UNUSED)) {
#endif
- GC_REFCOUNT(object)--;
- }
+ GC_REFCOUNT(object)--;
if (GC_REFCOUNT(object) == 1) {
zend_object_store_ctor_failed(object);
}
}
OBJ_RELEASE(object);
}
- EG(scope) = EX(func)->op_array.scope;
-fcall_end:
zend_vm_stack_free_call_frame(call);
if (UNEXPECTED(EG(exception) != NULL)) {
zend_throw_exception_internal(NULL);
- if (RETURN_VALUE_USED(opline)) {
+ if (1) {
zval_ptr_dtor(EX_VAR(opline->result.var));
}
HANDLE_EXCEPTION();
}
- ZEND_VM_INTERRUPT_CHECK();
- ZEND_VM_NEXT_OPCODE();
+ ZEND_VM_SET_OPCODE(opline + 1);
+ ZEND_VM_CONTINUE();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_GENERATOR_CREATE_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ zval *return_value = EX(return_value);
+
+ if (EXPECTED(return_value)) {
+ USE_OPLINE
+ zend_generator *generator;
+ zend_execute_data *gen_execute_data;
+ uint32_t num_args, used_stack, call_info;
+
+ object_init_ex(return_value, zend_ce_generator);
+
+ /*
+ * Normally the execute_data is allocated on the VM stack (because it does
+ * not actually do any allocation and thus is faster). For generators
+ * though this behavior would be suboptimal, because the (rather large)
+ * structure would have to be copied back and forth every time execution is
+ * suspended or resumed. That's why for generators the execution context
+ * is allocated on heap.
+ */
+ num_args = EX_NUM_ARGS();
+ used_stack = (ZEND_CALL_FRAME_SLOT + num_args + EX(func)->op_array.last_var + EX(func)->op_array.T - MIN(EX(func)->op_array.num_args, num_args)) * sizeof(zval);
+ gen_execute_data = (zend_execute_data*)emalloc(used_stack);
+ memcpy(gen_execute_data, execute_data, used_stack);
+
+ /* Save execution context in generator object. */
+ generator = (zend_generator *) Z_OBJ_P(EX(return_value));
+ generator->execute_data = gen_execute_data;
+ generator->frozen_call_stack = NULL;
+ generator->execute_fake.opline = NULL;
+ generator->execute_fake.func = NULL;
+ generator->execute_fake.prev_execute_data = NULL;
+ ZVAL_OBJ(&generator->execute_fake.This, (zend_object *) generator);
+
+ gen_execute_data->opline = opline + 1;
+ /* EX(return_value) keeps pointer to zend_object (not a real zval) */
+ gen_execute_data->return_value = (zval*)generator;
+ call_info = Z_TYPE_INFO(EX(This));
+ if ((call_info & Z_TYPE_MASK) == IS_OBJECT
+ && !(call_info & ((ZEND_CALL_CLOSURE|ZEND_CALL_RELEASE_THIS) << ZEND_CALL_INFO_SHIFT))) {
+ ZEND_ADD_CALL_FLAG_EX(call_info, ZEND_CALL_RELEASE_THIS);
+ Z_ADDREF(gen_execute_data->This);
+ }
+ ZEND_ADD_CALL_FLAG_EX(call_info, (ZEND_CALL_TOP_FUNCTION | ZEND_CALL_ALLOCATED | ZEND_CALL_GENERATOR));
+ Z_TYPE_INFO(gen_execute_data->This) = call_info;
+ gen_execute_data->prev_execute_data = NULL;
+
+ call_info = EX_CALL_INFO();
+ EG(current_execute_data) = EX(prev_execute_data);
+ if (EXPECTED(!(call_info & (ZEND_CALL_TOP|ZEND_CALL_ALLOCATED)))) {
+ EG(vm_stack_top) = (zval*)execute_data;
+ execute_data = EX(prev_execute_data);
+ LOAD_NEXT_OPLINE();
+ ZEND_VM_LEAVE();
+ } else if (EXPECTED(!(call_info & ZEND_CALL_TOP))) {
+ zend_execute_data *old_execute_data = execute_data;
+ execute_data = EX(prev_execute_data);
+ zend_vm_stack_free_call_frame_ex(call_info, old_execute_data);
+ LOAD_NEXT_OPLINE();
+ ZEND_VM_LEAVE();
+ } else {
+ ZEND_VM_RETURN();
+ }
+ } else {
+ ZEND_VM_TAIL_CALL(zend_leave_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU));
+ }
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SEND_UNPACK_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@@ -1018,35 +1314,32 @@ send_again:
if (iter->funcs->rewind) {
iter->funcs->rewind(iter);
- if (UNEXPECTED(EG(exception) != NULL)) {
- goto unpack_iter_dtor;
- }
}
for (; iter->funcs->valid(iter) == SUCCESS; ++arg_num) {
zval *arg, *top;
if (UNEXPECTED(EG(exception) != NULL)) {
- goto unpack_iter_dtor;
+ break;
}
arg = iter->funcs->get_current_data(iter);
if (UNEXPECTED(EG(exception) != NULL)) {
- goto unpack_iter_dtor;
+ break;
}
if (iter->funcs->get_current_key) {
zval key;
iter->funcs->get_current_key(iter, &key);
if (UNEXPECTED(EG(exception) != NULL)) {
- goto unpack_iter_dtor;
+ break;
}
if (Z_TYPE(key) == IS_STRING) {
zend_throw_error(NULL,
"Cannot unpack Traversable with string keys");
zend_string_release(Z_STR(key));
- goto unpack_iter_dtor;
+ break;
}
zval_dtor(&key);
@@ -1074,12 +1367,8 @@ send_again:
ZEND_CALL_NUM_ARGS(EX(call))++;
iter->funcs->move_forward(iter);
- if (UNEXPECTED(EG(exception) != NULL)) {
- goto unpack_iter_dtor;
- }
}
-unpack_iter_dtor:
zend_iterator_dtor(iter);
}
} else if (EXPECTED(Z_ISREF_P(args))) {
@@ -1101,7 +1390,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SEND_ARRAY_SPEC_HANDLER(ZEND_O
USE_OPLINE
zend_free_op free_op1;
zval *args;
- SAVE_OPLINE();
SAVE_OPLINE();
args = get_zval_ptr(opline->op1_type, opline->op1, execute_data, &free_op1, BP_VAR_R);
@@ -1117,13 +1405,12 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SEND_ARRAY_SPEC_HANDLER(ZEND_O
if (ZEND_CALL_INFO(EX(call)) & ZEND_CALL_CLOSURE) {
OBJ_RELEASE((zend_object*)EX(call)->func->common.prototype);
}
- if (Z_OBJ(EX(call)->This)) {
+ if (Z_TYPE(EX(call)->This) == IS_OBJECT) {
OBJ_RELEASE(Z_OBJ(EX(call)->This));
}
EX(call)->func = (zend_function*)&zend_pass_function;
- EX(call)->called_scope = NULL;
Z_OBJ(EX(call)->This) = NULL;
- ZEND_SET_CALL_INFO(EX(call), ZEND_CALL_INFO(EX(call)) & ~ZEND_CALL_RELEASE_THIS);
+ ZEND_SET_CALL_INFO(EX(call), 0, ZEND_CALL_INFO(EX(call)) & ~ZEND_CALL_RELEASE_THIS);
} else {
uint32_t arg_num;
HashTable *ht;
@@ -1149,13 +1436,12 @@ send_array:
if (ZEND_CALL_INFO(EX(call)) & ZEND_CALL_CLOSURE) {
OBJ_RELEASE((zend_object*)EX(call)->func->common.prototype);
}
- if (Z_OBJ(EX(call)->This)) {
+ if (Z_TYPE(EX(call)->This) == IS_OBJECT) {
OBJ_RELEASE(Z_OBJ(EX(call)->This));
}
EX(call)->func = (zend_function*)&zend_pass_function;
- EX(call)->called_scope = NULL;
Z_OBJ(EX(call)->This) = NULL;
- ZEND_SET_CALL_INFO(EX(call), ZEND_CALL_INFO(EX(call)) & ~ZEND_CALL_RELEASE_THIS);
+ ZEND_SET_CALL_INFO(EX(call), 0, ZEND_CALL_INFO(EX(call)) & ~ZEND_CALL_RELEASE_THIS);
break;
}
}
@@ -1183,8 +1469,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_RECV_SPEC_HANDLER(ZEND_OPCODE_
if (UNEXPECTED(arg_num > EX_NUM_ARGS())) {
SAVE_OPLINE();
- zend_verify_missing_arg(execute_data, arg_num, CACHE_ADDR(opline->op2.num));
- ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
+ zend_missing_arg_error(execute_data);
+ HANDLE_EXCEPTION();
} else if (UNEXPECTED((EX(func)->op_array.fn_flags & ZEND_ACC_HAS_TYPE_HINTS) != 0)) {
zval *param = _get_zval_ptr_cv_undef_BP_VAR_W(execute_data, opline->result.var);
@@ -1247,7 +1533,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_BEGIN_SILENCE_SPEC_HANDLER(ZEN
do {
EG(error_reporting) = 0;
if (!EG(error_reporting_ini_entry)) {
- zend_ini_entry *p = zend_hash_str_find_ptr(EG(ini_directives), "error_reporting", sizeof("error_reporting")-1);
+ zend_ini_entry *p = zend_hash_find_ptr(EG(ini_directives), CG(known_strings)[ZEND_STR_ERROR_REPORTING]);
if (p) {
EG(error_reporting_ini_entry) = p;
} else {
@@ -1259,7 +1545,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_BEGIN_SILENCE_SPEC_HANDLER(ZEN
ALLOC_HASHTABLE(EG(modified_ini_directives));
zend_hash_init(EG(modified_ini_directives), 8, NULL, NULL, 0);
}
- if (EXPECTED(zend_hash_str_add_ptr(EG(modified_ini_directives), "error_reporting", sizeof("error_reporting")-1, EG(error_reporting_ini_entry)) != NULL)) {
+ if (EXPECTED(zend_hash_add_ptr(EG(modified_ini_directives), CG(known_strings)[ZEND_STR_ERROR_REPORTING], EG(error_reporting_ini_entry)) != NULL)) {
EG(error_reporting_ini_entry)->orig_value = EG(error_reporting_ini_entry)->value;
EG(error_reporting_ini_entry)->orig_modifiable = EG(error_reporting_ini_entry)->modifiable;
EG(error_reporting_ini_entry)->modified = 1;
@@ -1276,7 +1562,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_EXT_STMT_SPEC_HANDLER(ZEND_OPC
if (!EG(no_extensions)) {
SAVE_OPLINE();
- zend_llist_apply_with_argument(&zend_extensions, (llist_apply_with_arg_func_t) zend_extension_statement_handler, EX(func));
+ zend_llist_apply_with_argument(&zend_extensions, (llist_apply_with_arg_func_t) zend_extension_statement_handler, execute_data);
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
ZEND_VM_NEXT_OPCODE();
@@ -1288,7 +1574,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_EXT_FCALL_BEGIN_SPEC_HANDLER(Z
if (!EG(no_extensions)) {
SAVE_OPLINE();
- zend_llist_apply_with_argument(&zend_extensions, (llist_apply_with_arg_func_t) zend_extension_fcall_begin_handler, EX(func));
+ zend_llist_apply_with_argument(&zend_extensions, (llist_apply_with_arg_func_t) zend_extension_fcall_begin_handler, execute_data);
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
ZEND_VM_NEXT_OPCODE();
@@ -1300,7 +1586,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_EXT_FCALL_END_SPEC_HANDLER(ZEN
if (!EG(no_extensions)) {
SAVE_OPLINE();
- zend_llist_apply_with_argument(&zend_extensions, (llist_apply_with_arg_func_t) zend_extension_fcall_end_handler, EX(func));
+ zend_llist_apply_with_argument(&zend_extensions, (llist_apply_with_arg_func_t) zend_extension_fcall_end_handler, execute_data);
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
ZEND_VM_NEXT_OPCODE();
@@ -1315,41 +1601,19 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DECLARE_CLASS_SPEC_HANDLER(ZEN
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DECLARE_INHERITED_CLASS_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
-{
- USE_OPLINE
-
- SAVE_OPLINE();
- Z_CE_P(EX_VAR(opline->result.var)) = do_bind_inherited_class(&EX(func)->op_array, opline, EG(class_table), Z_CE_P(EX_VAR(opline->extended_value)), 0);
- ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
-}
-
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DECLARE_INHERITED_CLASS_DELAYED_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
-{
- USE_OPLINE
- zval *zce, *orig_zce;
-
- SAVE_OPLINE();
- if ((zce = zend_hash_find(EG(class_table), Z_STR_P(EX_CONSTANT(opline->op2)))) == NULL ||
- ((orig_zce = zend_hash_find(EG(class_table), Z_STR_P(EX_CONSTANT(opline->op1)))) != NULL &&
- Z_CE_P(zce) != Z_CE_P(orig_zce))) {
- do_bind_inherited_class(&EX(func)->op_array, opline, EG(class_table), Z_CE_P(EX_VAR(opline->extended_value)), 0);
- }
- ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
-}
-
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DECLARE_ANON_CLASS_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
zend_class_entry *ce;
USE_OPLINE
SAVE_OPLINE();
- ce = zend_hash_find_ptr(EG(class_table), Z_STR_P(EX_CONSTANT(opline->op2)));
+ ce = zend_hash_find_ptr(EG(class_table), Z_STR_P(EX_CONSTANT(opline->op1)));
Z_CE_P(EX_VAR(opline->result.var)) = ce;
ZEND_ASSERT(ce != NULL);
if (ce->ce_flags & ZEND_ACC_ANON_BOUND) {
- ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op1));
+ ZEND_VM_SET_RELATIVE_OPCODE(opline, opline->extended_value);
+ ZEND_VM_CONTINUE();
}
if (!(ce->ce_flags & (ZEND_ACC_INTERFACE|ZEND_ACC_IMPLEMENT_INTERFACES|ZEND_ACC_IMPLEMENT_TRAITS))) {
@@ -1359,25 +1623,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DECLARE_ANON_CLASS_SPEC_HANDLE
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DECLARE_ANON_INHERITED_CLASS_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
-{
- zend_class_entry *ce;
- USE_OPLINE
-
- SAVE_OPLINE();
- ce = zend_hash_find_ptr(EG(class_table), Z_STR_P(EX_CONSTANT(opline->op2)));
- Z_CE_P(EX_VAR(opline->result.var)) = ce;
- ZEND_ASSERT(ce != NULL);
-
- if (ce->ce_flags & ZEND_ACC_ANON_BOUND) {
- ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op1));
- }
-
- zend_do_inheritance(ce, Z_CE_P(EX_VAR(opline->extended_value)));
- ce->ce_flags |= ZEND_ACC_ANON_BOUND;
- ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
-}
-
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DECLARE_FUNCTION_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
@@ -1452,14 +1697,72 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_BIND_TRAITS_SPEC_HANDLER(ZEND_
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_HANDLE_EXCEPTION_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_dispatch_try_catch_finally_helper_SPEC(uint32_t try_catch_offset, uint32_t op_num ZEND_OPCODE_HANDLER_ARGS_DC)
{
- uint32_t op_num = EG(opline_before_exception) - EX(func)->op_array.opcodes;
- int i;
- uint32_t catch_op_num = 0, finally_op_num = 0, finally_op_end = 0;
- int in_finally = 0;
+ /* May be NULL during generator closing (only finally blocks are executed) */
+ zend_object *ex = EG(exception);
- ZEND_VM_INTERRUPT_CHECK();
+ /* Walk try/catch/finally structures upwards, performing the necessary actions */
+ while (try_catch_offset != (uint32_t) -1) {
+ zend_try_catch_element *try_catch =
+ &EX(func)->op_array.try_catch_array[try_catch_offset];
+
+ if (op_num < try_catch->catch_op && ex) {
+ /* Go to catch block */
+ cleanup_live_vars(execute_data, op_num, try_catch->catch_op);
+ ZEND_VM_SET_OPCODE(&EX(func)->op_array.opcodes[try_catch->catch_op]);
+ ZEND_VM_CONTINUE();
+
+ } else if (op_num < try_catch->finally_op) {
+ /* Go to finally block */
+ zval *fast_call = EX_VAR(EX(func)->op_array.opcodes[try_catch->finally_end].op1.var);
+ cleanup_live_vars(execute_data, op_num, try_catch->finally_op);
+ Z_OBJ_P(fast_call) = EG(exception);
+ EG(exception) = NULL;
+ fast_call->u2.lineno = (uint32_t)-1;
+ ZEND_VM_SET_OPCODE(&EX(func)->op_array.opcodes[try_catch->finally_op]);
+ ZEND_VM_CONTINUE();
+
+ } else if (op_num < try_catch->finally_end) {
+ zval *fast_call = EX_VAR(EX(func)->op_array.opcodes[try_catch->finally_end].op1.var);
+
+ /* cleanup incomplete RETURN statement */
+ if (fast_call->u2.lineno != (uint32_t)-1
+ && (EX(func)->op_array.opcodes[fast_call->u2.lineno].op2_type & (IS_TMP_VAR | IS_VAR))) {
+ zval *return_value = EX_VAR(EX(func)->op_array.opcodes[fast_call->u2.lineno].op2.var);
+
+ zval_ptr_dtor(return_value);
+ }
+
+ /* Chain potential exception from wrapping finally block */
+ if (Z_OBJ_P(fast_call)) {
+ if (ex) {
+ zend_exception_set_previous(ex, Z_OBJ_P(fast_call));
+ } else {
+ EG(exception) = Z_OBJ_P(fast_call);
+ }
+ ex = Z_OBJ_P(fast_call);
+ }
+ }
+
+ try_catch_offset--;
+ }
+
+ /* Uncaught exception */
+ cleanup_live_vars(execute_data, op_num, 0);
+ if (UNEXPECTED((EX_CALL_INFO() & ZEND_CALL_GENERATOR) != 0)) {
+ zend_generator *generator = zend_get_running_generator(execute_data);
+ zend_generator_close(generator, 1);
+ ZEND_VM_RETURN();
+ } else {
+ ZEND_VM_TAIL_CALL(zend_leave_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU));
+ }
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_HANDLE_EXCEPTION_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ uint32_t throw_op_num = EG(opline_before_exception) - EX(func)->op_array.opcodes;
+ uint32_t i, current_try_catch_offset = (uint32_t) -1;
{
const zend_op *exc_opline = EG(opline_before_exception);
@@ -1467,68 +1770,27 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_HANDLE_EXCEPTION_SPEC_HANDLER(
&& exc_opline->extended_value & ZEND_FREE_ON_RETURN) {
/* exceptions thrown because of loop var destruction on return/break/...
* are logically thrown at the end of the foreach loop, so adjust the
- * op_num.
+ * throw_op_num.
*/
- op_num = EX(func)->op_array.brk_cont_array[exc_opline->op2.num].brk;
+ throw_op_num = EX(func)->op_array.live_range[exc_opline->op2.num].end;
}
}
+ /* Find the innermost try/catch/finally the exception was thrown in */
for (i = 0; i < EX(func)->op_array.last_try_catch; i++) {
- if (EX(func)->op_array.try_catch_array[i].try_op > op_num) {
+ zend_try_catch_element *try_catch = &EX(func)->op_array.try_catch_array[i];
+ if (try_catch->try_op > throw_op_num) {
/* further blocks will not be relevant... */
break;
}
- in_finally = 0;
- if (op_num < EX(func)->op_array.try_catch_array[i].catch_op) {
- catch_op_num = EX(func)->op_array.try_catch_array[i].catch_op;
- }
- if (op_num < EX(func)->op_array.try_catch_array[i].finally_op) {
- finally_op_num = EX(func)->op_array.try_catch_array[i].finally_op;
- finally_op_end = EX(func)->op_array.try_catch_array[i].finally_end;
- }
- if (op_num >= EX(func)->op_array.try_catch_array[i].finally_op &&
- op_num < EX(func)->op_array.try_catch_array[i].finally_end) {
- finally_op_end = EX(func)->op_array.try_catch_array[i].finally_end;
- in_finally = 1;
+ if (throw_op_num < try_catch->catch_op || throw_op_num < try_catch->finally_end) {
+ current_try_catch_offset = i;
}
}
- cleanup_unfinished_calls(execute_data, op_num);
-
- if (finally_op_num && (!catch_op_num || catch_op_num >= finally_op_num)) {
- zval *fast_call = EX_VAR(EX(func)->op_array.opcodes[finally_op_end].op1.var);
+ cleanup_unfinished_calls(execute_data, throw_op_num);
- cleanup_live_vars(execute_data, op_num, finally_op_num);
- if (in_finally && Z_OBJ_P(fast_call)) {
- zend_exception_set_previous(EG(exception), Z_OBJ_P(fast_call));
- }
- Z_OBJ_P(fast_call) = EG(exception);
- EG(exception) = NULL;
- fast_call->u2.lineno = (uint32_t)-1;
- ZEND_VM_SET_OPCODE(&EX(func)->op_array.opcodes[finally_op_num]);
- ZEND_VM_CONTINUE();
- } else {
- cleanup_live_vars(execute_data, op_num, catch_op_num);
- if (in_finally) {
- /* we are going out of current finally scope */
- zval *fast_call = EX_VAR(EX(func)->op_array.opcodes[finally_op_end].op1.var);
-
- if (Z_OBJ_P(fast_call)) {
- zend_exception_set_previous(EG(exception), Z_OBJ_P(fast_call));
- Z_OBJ_P(fast_call) = NULL;
- }
- }
- if (catch_op_num) {
- ZEND_VM_SET_OPCODE(&EX(func)->op_array.opcodes[catch_op_num]);
- ZEND_VM_CONTINUE();
- } else if (UNEXPECTED((EX(func)->op_array.fn_flags & ZEND_ACC_GENERATOR) != 0)) {
- zend_generator *generator = zend_get_running_generator(execute_data);
- zend_generator_close(generator, 1);
- ZEND_VM_RETURN();
- } else {
- ZEND_VM_TAIL_CALL(zend_leave_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU));
- }
- }
+ ZEND_VM_TAIL_CALL(zend_dispatch_try_catch_finally_helper_SPEC(current_try_catch_offset, throw_op_num ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_VERIFY_ABSTRACT_CLASS_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@@ -1553,7 +1815,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_USER_OPCODE_SPEC_HANDLER(ZEND_
case ZEND_USER_OPCODE_CONTINUE:
ZEND_VM_CONTINUE();
case ZEND_USER_OPCODE_RETURN:
- if (UNEXPECTED((EX(func)->op_array.fn_flags & ZEND_ACC_GENERATOR) != 0)) {
+ if (UNEXPECTED((EX_CALL_INFO() & ZEND_CALL_GENERATOR) != 0)) {
zend_generator *generator = zend_get_running_generator(execute_data);
zend_generator_close(generator, 1);
ZEND_VM_RETURN();
@@ -1575,16 +1837,24 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DISCARD_EXCEPTION_SPEC_HANDLER
{
USE_OPLINE
zval *fast_call = EX_VAR(opline->op1.var);
+ SAVE_OPLINE();
+
+ /* cleanup incomplete RETURN statement */
+ if (fast_call->u2.lineno != (uint32_t)-1
+ && (EX(func)->op_array.opcodes[fast_call->u2.lineno].op2_type & (IS_TMP_VAR | IS_VAR))) {
+ zval *return_value = EX_VAR(EX(func)->op_array.opcodes[fast_call->u2.lineno].op2.var);
- /* check for delayed exception */
+ zval_ptr_dtor(return_value);
+ }
+
+ /* cleanup delayed exception */
if (Z_OBJ_P(fast_call) != NULL) {
- SAVE_OPLINE();
/* discard the previously thrown exception */
OBJ_RELEASE(Z_OBJ_P(fast_call));
Z_OBJ_P(fast_call) = NULL;
}
- ZEND_VM_NEXT_OPCODE();
+ ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FAST_CALL_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@@ -1592,13 +1862,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FAST_CALL_SPEC_HANDLER(ZEND_OP
USE_OPLINE
zval *fast_call = EX_VAR(opline->result.var);
- if (opline->extended_value == ZEND_FAST_CALL_FROM_FINALLY && UNEXPECTED(Z_OBJ_P(fast_call) != NULL)) {
- fast_call->u2.lineno = (uint32_t)-1;
- } else {
- Z_OBJ_P(fast_call) = NULL;
- /* set return address */
- fast_call->u2.lineno = opline - EX(func)->op_array.opcodes;
- }
+ Z_OBJ_P(fast_call) = NULL;
+ /* set return address */
+ fast_call->u2.lineno = opline - EX(func)->op_array.opcodes;
ZEND_VM_SET_OPCODE(OP_JMP_ADDR(opline, opline->op1));
ZEND_VM_CONTINUE();
}
@@ -1607,41 +1873,21 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FAST_RET_SPEC_HANDLER(ZEND_OPC
{
USE_OPLINE
zval *fast_call = EX_VAR(opline->op1.var);
+ uint32_t current_try_catch_offset, current_op_num;
if (fast_call->u2.lineno != (uint32_t)-1) {
const zend_op *fast_ret = EX(func)->op_array.opcodes + fast_call->u2.lineno;
+
ZEND_VM_SET_OPCODE(fast_ret + 1);
- if (fast_ret->extended_value & ZEND_FAST_CALL_FROM_FINALLY) {
- fast_call->u2.lineno = fast_ret->op2.opline_num;
- }
ZEND_VM_CONTINUE();
- } else {
- /* special case for unhandled exceptions */
- USE_OPLINE
-
- if (opline->extended_value == ZEND_FAST_RET_TO_FINALLY) {
- cleanup_live_vars(execute_data, opline - EX(func)->op_array.opcodes, opline->op2.opline_num);
- ZEND_VM_SET_OPCODE(&EX(func)->op_array.opcodes[opline->op2.opline_num]);
- ZEND_VM_CONTINUE();
- } else {
- EG(exception) = Z_OBJ_P(fast_call);
- Z_OBJ_P(fast_call) = NULL;
- if (opline->extended_value == ZEND_FAST_RET_TO_CATCH) {
- cleanup_live_vars(execute_data, opline - EX(func)->op_array.opcodes, opline->op2.opline_num);
- ZEND_VM_SET_OPCODE(&EX(func)->op_array.opcodes[opline->op2.opline_num]);
- ZEND_VM_CONTINUE();
- } else {
- cleanup_live_vars(execute_data, opline - EX(func)->op_array.opcodes, 0);
- if (UNEXPECTED((EX(func)->op_array.fn_flags & ZEND_ACC_GENERATOR) != 0)) {
- zend_generator *generator = zend_get_running_generator(execute_data);
- zend_generator_close(generator, 1);
- ZEND_VM_RETURN();
- } else {
- ZEND_VM_TAIL_CALL(zend_leave_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU));
- }
- }
- }
}
+
+ /* special case for unhandled exceptions */
+ EG(exception) = Z_OBJ_P(fast_call);
+ Z_OBJ_P(fast_call) = NULL;
+ current_try_catch_offset = opline->op2.num;
+ current_op_num = opline - EX(func)->op_array.opcodes;
+ ZEND_VM_TAIL_CALL(zend_dispatch_try_catch_finally_helper_SPEC(current_try_catch_offset, current_op_num ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSERT_CHECK_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@@ -1650,10 +1896,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSERT_CHECK_SPEC_HANDLER(ZEND
if (EG(assertions) <= 0) {
zend_op *target = OP_JMP_ADDR(opline, opline->op2);
- zend_op *result = target - 1;
- SKIP_EXT_OPLINE(result);
- if (RETURN_VALUE_USED(result)) {
- ZVAL_TRUE(EX_VAR(result->result.var));
+ if (RETURN_VALUE_USED(opline)) {
+ ZVAL_TRUE(EX_VAR(opline->result.var));
}
ZEND_VM_JMP(target);
} else {
@@ -1664,12 +1908,14 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSERT_CHECK_SPEC_HANDLER(ZEND
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_NAME_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
uint32_t fetch_type;
+ zend_class_entry *called_scope, *scope;
USE_OPLINE
SAVE_OPLINE();
fetch_type = opline->extended_value;
- if (UNEXPECTED(EG(scope) == NULL)) {
+ scope = EX(func)->op_array.scope;
+ if (UNEXPECTED(scope == NULL)) {
zend_throw_error(NULL, "Cannot use \"%s\" when no class scope is active",
fetch_type == ZEND_FETCH_CLASS_SELF ? "self" :
fetch_type == ZEND_FETCH_CLASS_PARENT ? "parent" : "static");
@@ -1678,18 +1924,23 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_NAME_SPEC_HANDLER(
switch (fetch_type) {
case ZEND_FETCH_CLASS_SELF:
- ZVAL_STR_COPY(EX_VAR(opline->result.var), EG(scope)->name);
+ ZVAL_STR_COPY(EX_VAR(opline->result.var), scope->name);
break;
case ZEND_FETCH_CLASS_PARENT:
- if (UNEXPECTED(EG(scope)->parent == NULL)) {
+ if (UNEXPECTED(scope->parent == NULL)) {
zend_throw_error(NULL,
"Cannot use \"parent\" when current class scope has no parent");
HANDLE_EXCEPTION();
}
- ZVAL_STR_COPY(EX_VAR(opline->result.var), EG(scope)->parent->name);
+ ZVAL_STR_COPY(EX_VAR(opline->result.var), scope->parent->name);
break;
case ZEND_FETCH_CLASS_STATIC:
- ZVAL_STR_COPY(EX_VAR(opline->result.var), EX(called_scope)->name);
+ if (Z_TYPE(EX(This)) == IS_OBJECT) {
+ called_scope = Z_OBJCE(EX(This));
+ } else {
+ called_scope = Z_CE(EX(This));
+ }
+ ZVAL_STR_COPY(EX_VAR(opline->result.var), called_scope->name);
break;
EMPTY_SWITCH_DEFAULT_CASE()
}
@@ -1739,10 +1990,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CALL_TRAMPOLINE_SPEC_HANDLER(Z
ZEND_ASSERT(!(fbc->common.fn_flags & ZEND_ACC_GENERATOR));
- call->symbol_table = NULL;
- i_init_func_execute_data(call, &fbc->op_array,
- ret, (fbc->common.fn_flags & ZEND_ACC_STATIC) == 0);
-
+ if (UNEXPECTED(!fbc->op_array.run_time_cache)) {
+ init_func_run_time_cache(&fbc->op_array);
+ }
+ i_init_func_execute_data(call, &fbc->op_array, ret);
if (EXPECTED(zend_execute_ex == execute_ex)) {
ZEND_VM_ENTER();
} else {
@@ -1756,32 +2007,19 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CALL_TRAMPOLINE_SPEC_HANDLER(Z
EG(current_execute_data) = call;
- if (fbc->common.fn_flags & ZEND_ACC_HAS_TYPE_HINTS) {
- uint32_t i;
- uint32_t num_args = ZEND_CALL_NUM_ARGS(call);
- zval *p = ZEND_CALL_ARG(call, 1);
-
- EG(current_execute_data) = call;
-
- for (i = 0; i < num_args; ++i) {
- if (UNEXPECTED(!zend_verify_internal_arg_type(fbc, i + 1, p))) {
- EG(current_execute_data) = call->prev_execute_data;
- zend_vm_stack_free_args(call);
- zend_vm_stack_free_call_frame(call);
- if (ret) {
- ZVAL_UNDEF(ret);
- }
- goto call_trampoline_end;
- }
- p++;
+ if (UNEXPECTED(fbc->common.fn_flags & ZEND_ACC_HAS_TYPE_HINTS)
+ && UNEXPECTED(!zend_verify_internal_arg_types(fbc, call))) {
+ zend_vm_stack_free_call_frame(call);
+ if (ret) {
+ ZVAL_UNDEF(ret);
}
+ goto call_trampoline_end;
}
if (ret == NULL) {
ZVAL_NULL(&retval);
ret = &retval;
}
- Z_VAR_FLAGS_P(ret) = (fbc->common.fn_flags & ZEND_ACC_RETURN_REFERENCE) != 0 ? IS_VAR_RET_REF : 0;
if (!zend_execute_internal) {
/* saves one function call if zend_execute_internal is not used */
@@ -1794,7 +2032,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CALL_TRAMPOLINE_SPEC_HANDLER(Z
ZEND_ASSERT(
EG(exception) || !call->func ||
!(call->func->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) ||
- zend_verify_internal_return_type(call->func, EX_VAR(opline->result.var)));
+ zend_verify_internal_return_type(call->func, ret));
#endif
EG(current_execute_data) = call->prev_execute_data;
@@ -1819,7 +2057,6 @@ call_trampoline_end:
zend_object *object = Z_OBJ(call->This);
OBJ_RELEASE(object);
}
- EG(scope) = EX(func)->op_array.scope;
zend_vm_stack_free_call_frame(call);
if (UNEXPECTED(EG(exception) != NULL)) {
@@ -1883,17 +2120,20 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_FCALL_BY_NAME_SPEC_CONST_
zval *function_name, *func;
zend_execute_data *call;
- fbc = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)));
+ function_name = (zval*)EX_CONSTANT(opline->op2);
+ fbc = CACHED_PTR(Z_CACHE_SLOT_P(function_name));
if (UNEXPECTED(fbc == NULL)) {
- function_name = (zval*)(EX_CONSTANT(opline->op2)+1);
- func = zend_hash_find(EG(function_table), Z_STR_P(function_name));
+ func = zend_hash_find(EG(function_table), Z_STR_P(function_name+1));
if (UNEXPECTED(func == NULL)) {
SAVE_OPLINE();
- zend_throw_error(NULL, "Call to undefined function %s()", Z_STRVAL_P(EX_CONSTANT(opline->op2)));
+ zend_throw_error(NULL, "Call to undefined function %s()", Z_STRVAL_P(function_name));
HANDLE_EXCEPTION();
}
fbc = Z_FUNC_P(func);
- CACHE_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)), fbc);
+ if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!fbc->op_array.run_time_cache)) {
+ init_func_run_time_cache(&fbc->op_array);
+ }
+ CACHE_PTR(Z_CACHE_SLOT_P(function_name), fbc);
}
call = zend_vm_stack_push_call_frame(ZEND_CALL_NESTED_FUNCTION,
fbc, opline->extended_value, NULL, NULL);
@@ -1906,195 +2146,21 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_FCALL_BY_NAME_SPEC_CONST_
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_DYNAMIC_CALL_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
- zend_function *fbc;
- zval *function_name, *func;
- zend_string *lcname;
- zend_class_entry *called_scope;
- zend_object *object;
+ zval *function_name;
zend_execute_data *call;
- uint32_t call_info = ZEND_CALL_NESTED_FUNCTION;
SAVE_OPLINE();
function_name = EX_CONSTANT(opline->op2);
try_function_name:
if (IS_CONST != IS_CONST && EXPECTED(Z_TYPE_P(function_name) == IS_STRING)) {
- const char *colon;
-
- if ((colon = zend_memrchr(Z_STRVAL_P(function_name), ':', Z_STRLEN_P(function_name))) != NULL &&
- colon > Z_STRVAL_P(function_name) &&
- *(colon-1) == ':'
- ) {
- zend_string *mname;
- size_t cname_length = colon - Z_STRVAL_P(function_name) - 1;
- size_t mname_length = Z_STRLEN_P(function_name) - cname_length - (sizeof("::") - 1);
-
- lcname = zend_string_init(Z_STRVAL_P(function_name), cname_length, 0);
-
- object = NULL;
- called_scope = zend_fetch_class_by_name(lcname, NULL, ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION);
- if (UNEXPECTED(called_scope == NULL)) {
- zend_string_release(lcname);
- ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
- }
-
- mname = zend_string_init(Z_STRVAL_P(function_name) + (cname_length + sizeof("::") - 1), mname_length, 0);
-
- if (called_scope->get_static_method) {
- fbc = called_scope->get_static_method(called_scope, mname);
- } else {
- fbc = zend_std_get_static_method(called_scope, mname, NULL);
- }
- if (UNEXPECTED(fbc == NULL)) {
- if (EXPECTED(!EG(exception))) {
- zend_throw_error(NULL, "Call to undefined method %s::%s()", ZSTR_VAL(called_scope->name), ZSTR_VAL(mname));
- }
- zend_string_release(lcname);
- zend_string_release(mname);
-
- HANDLE_EXCEPTION();
- }
-
- zend_string_release(lcname);
- zend_string_release(mname);
-
- if (!(fbc->common.fn_flags & ZEND_ACC_STATIC)) {
- if (fbc->common.fn_flags & ZEND_ACC_ALLOW_STATIC) {
- zend_error(E_DEPRECATED,
- "Non-static method %s::%s() should not be called statically",
- ZSTR_VAL(fbc->common.scope->name), ZSTR_VAL(fbc->common.function_name));
- if (UNEXPECTED(EG(exception) != NULL)) {
- HANDLE_EXCEPTION();
- }
- } else {
- zend_throw_error(
- zend_ce_error,
- "Non-static method %s::%s() cannot be called statically",
- ZSTR_VAL(fbc->common.scope->name), ZSTR_VAL(fbc->common.function_name));
-
- HANDLE_EXCEPTION();
- }
- }
- } else {
- if (Z_STRVAL_P(function_name)[0] == '\\') {
- lcname = zend_string_alloc(Z_STRLEN_P(function_name) - 1, 0);
- zend_str_tolower_copy(ZSTR_VAL(lcname), Z_STRVAL_P(function_name) + 1, Z_STRLEN_P(function_name) - 1);
- } else {
- lcname = zend_string_tolower(Z_STR_P(function_name));
- }
- if (UNEXPECTED((func = zend_hash_find(EG(function_table), lcname)) == NULL)) {
- zend_throw_error(NULL, "Call to undefined function %s()", Z_STRVAL_P(function_name));
- zend_string_release(lcname);
-
- HANDLE_EXCEPTION();
- }
- zend_string_release(lcname);
-
- fbc = Z_FUNC_P(func);
- called_scope = NULL;
- object = NULL;
- }
-
- } else if (IS_CONST != IS_CONST &&
- EXPECTED(Z_TYPE_P(function_name) == IS_OBJECT) &&
- Z_OBJ_HANDLER_P(function_name, get_closure) &&
- Z_OBJ_HANDLER_P(function_name, get_closure)(function_name, &called_scope, &fbc, &object) == SUCCESS) {
- if (fbc->common.fn_flags & ZEND_ACC_CLOSURE) {
- /* Delay closure destruction until its invocation */
- ZEND_ASSERT(GC_TYPE((zend_object*)fbc->common.prototype) == IS_OBJECT);
- GC_REFCOUNT((zend_object*)fbc->common.prototype)++;
- call_info |= ZEND_CALL_CLOSURE;
- } else if (object) {
- call_info |= ZEND_CALL_RELEASE_THIS;
- GC_REFCOUNT(object)++; /* For $this pointer */
- }
-
- } else if (EXPECTED(Z_TYPE_P(function_name) == IS_ARRAY) &&
- zend_hash_num_elements(Z_ARRVAL_P(function_name)) == 2) {
- zval *obj;
- zval *method;
- obj = zend_hash_index_find(Z_ARRVAL_P(function_name), 0);
- method = zend_hash_index_find(Z_ARRVAL_P(function_name), 1);
-
- if (!obj || !method) {
- zend_throw_error(NULL, "Array callback has to contain indices 0 and 1");
-
- HANDLE_EXCEPTION();
- }
-
- ZVAL_DEREF(obj);
- if (Z_TYPE_P(obj) != IS_STRING && Z_TYPE_P(obj) != IS_OBJECT) {
- zend_throw_error(NULL, "First array member is not a valid class name or object");
-
- HANDLE_EXCEPTION();
- }
-
- ZVAL_DEREF(method);
- if (Z_TYPE_P(method) != IS_STRING) {
- zend_throw_error(NULL, "Second array member is not a valid method");
-
- HANDLE_EXCEPTION();
- }
-
- if (Z_TYPE_P(obj) == IS_STRING) {
- object = NULL;
- called_scope = zend_fetch_class_by_name(Z_STR_P(obj), NULL, ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION);
- if (UNEXPECTED(called_scope == NULL)) {
- ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
- }
-
- if (called_scope->get_static_method) {
- fbc = called_scope->get_static_method(called_scope, Z_STR_P(method));
- } else {
- fbc = zend_std_get_static_method(called_scope, Z_STR_P(method), NULL);
- }
- if (UNEXPECTED(fbc == NULL)) {
- if (EXPECTED(!EG(exception))) {
- zend_throw_error(NULL, "Call to undefined method %s::%s()", ZSTR_VAL(called_scope->name), Z_STRVAL_P(method));
- }
-
- HANDLE_EXCEPTION();
- }
- if (!(fbc->common.fn_flags & ZEND_ACC_STATIC)) {
- if (fbc->common.fn_flags & ZEND_ACC_ALLOW_STATIC) {
- zend_error(E_DEPRECATED,
- "Non-static method %s::%s() should not be called statically",
- ZSTR_VAL(fbc->common.scope->name), ZSTR_VAL(fbc->common.function_name));
- if (UNEXPECTED(EG(exception) != NULL)) {
- HANDLE_EXCEPTION();
- }
- } else {
- zend_throw_error(
- zend_ce_error,
- "Non-static method %s::%s() cannot be called statically",
- ZSTR_VAL(fbc->common.scope->name), ZSTR_VAL(fbc->common.function_name));
-
- HANDLE_EXCEPTION();
- }
- }
- } else {
- called_scope = Z_OBJCE_P(obj);
- object = Z_OBJ_P(obj);
-
- fbc = Z_OBJ_HT_P(obj)->get_method(&object, Z_STR_P(method), NULL);
- if (UNEXPECTED(fbc == NULL)) {
- if (EXPECTED(!EG(exception))) {
- zend_throw_error(NULL, "Call to undefined method %s::%s()", ZSTR_VAL(object->ce->name), Z_STRVAL_P(method));
- }
-
- HANDLE_EXCEPTION();
- }
-
- if ((fbc->common.fn_flags & ZEND_ACC_STATIC) != 0) {
- object = NULL;
- } else {
- call_info |= ZEND_CALL_RELEASE_THIS;
- GC_REFCOUNT(object)++; /* For $this pointer */
- }
- }
-
- } else if ((IS_CONST & (IS_VAR|IS_CV)) && Z_TYPE_P(function_name) == IS_REFERENCE) {
+ call = zend_init_dynamic_call_string(Z_STR_P(function_name), opline->extended_value);
+ } else if (IS_CONST != IS_CONST && EXPECTED(Z_TYPE_P(function_name) == IS_OBJECT)) {
+ call = zend_init_dynamic_call_object(function_name, opline->extended_value);
+ } else if (EXPECTED(Z_TYPE_P(function_name) == IS_ARRAY)) {
+ call = zend_init_dynamic_call_array(Z_ARRVAL_P(function_name), opline->extended_value);
+ } else if ((IS_CONST & (IS_VAR|IS_CV)) && EXPECTED(Z_TYPE_P(function_name) == IS_REFERENCE)) {
function_name = Z_REFVAL_P(function_name);
goto try_function_name;
} else {
@@ -2105,11 +2171,13 @@ try_function_name:
}
}
zend_throw_error(NULL, "Function name must be a string");
+ call = NULL;
+ }
+ if (UNEXPECTED(!call)) {
HANDLE_EXCEPTION();
}
- call = zend_vm_stack_push_call_frame(call_info,
- fbc, opline->extended_value, called_scope, object);
+
call->prev_execute_data = EX(call);
EX(call) = call;
@@ -2139,6 +2207,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_NS_FCALL_BY_NAME_SPEC_CON
}
fbc = Z_FUNC_P(func);
CACHE_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)), fbc);
+ if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!fbc->op_array.run_time_cache)) {
+ init_func_run_time_cache(&fbc->op_array);
+ }
}
call = zend_vm_stack_push_call_frame(ZEND_CALL_NESTED_FUNCTION,
@@ -2168,6 +2239,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_FCALL_SPEC_CONST_HANDLER(
}
fbc = Z_FUNC_P(func);
CACHE_PTR(Z_CACHE_SLOT_P(fname), fbc);
+ if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!fbc->op_array.run_time_cache)) {
+ init_func_run_time_cache(&fbc->op_array);
+ }
}
call = zend_vm_stack_push_call_frame_ex(
@@ -2190,18 +2264,13 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_RECV_INIT_SPEC_CONST_HANDLER(Z
arg_num = opline->op1.num;
param = _get_zval_ptr_cv_undef_BP_VAR_W(execute_data, opline->result.var);
if (arg_num > EX_NUM_ARGS()) {
- ZVAL_COPY_VALUE(param, EX_CONSTANT(opline->op2));
+ ZVAL_COPY(param, EX_CONSTANT(opline->op2));
if (Z_OPT_CONSTANT_P(param)) {
SAVE_OPLINE();
- if (UNEXPECTED(zval_update_constant_ex(param, 0, NULL) != SUCCESS)) {
+ if (UNEXPECTED(zval_update_constant_ex(param, EX(func)->op_array.scope) != SUCCESS)) {
ZVAL_UNDEF(param);
HANDLE_EXCEPTION();
}
- } else {
- /* IS_CONST can't be IS_OBJECT, IS_RESOURCE or IS_REFERENCE */
- if (UNEXPECTED(Z_OPT_COPYABLE_P(param))) {
- zval_copy_ctor_func(param);
- }
}
}
@@ -2242,6 +2311,49 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ADD_INTERFACE_SPEC_CONST_HANDL
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DECLARE_INHERITED_CLASS_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+
+ SAVE_OPLINE();
+ Z_CE_P(EX_VAR(opline->result.var)) = do_bind_inherited_class(&EX(func)->op_array, opline, EG(class_table), Z_CE_P(EX_VAR(opline->op2.var)), 0);
+ ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DECLARE_INHERITED_CLASS_DELAYED_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zval *zce, *orig_zce;
+
+ SAVE_OPLINE();
+ if ((zce = zend_hash_find(EG(class_table), Z_STR_P(EX_CONSTANT(opline->op1)))) == NULL ||
+ ((orig_zce = zend_hash_find(EG(class_table), Z_STR_P(EX_CONSTANT(opline->op1)+1))) != NULL &&
+ Z_CE_P(zce) != Z_CE_P(orig_zce))) {
+ do_bind_inherited_class(&EX(func)->op_array, opline, EG(class_table), Z_CE_P(EX_VAR(opline->op2.var)), 0);
+ }
+ ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DECLARE_ANON_INHERITED_CLASS_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ zend_class_entry *ce;
+ USE_OPLINE
+
+ SAVE_OPLINE();
+ ce = zend_hash_find_ptr(EG(class_table), Z_STR_P(EX_CONSTANT(opline->op1)));
+ Z_CE_P(EX_VAR(opline->result.var)) = ce;
+ ZEND_ASSERT(ce != NULL);
+
+ if (ce->ce_flags & ZEND_ACC_ANON_BOUND) {
+ ZEND_VM_SET_RELATIVE_OPCODE(opline, opline->extended_value);
+ ZEND_VM_CONTINUE();
+ }
+
+ zend_do_inheritance(ce, Z_CE_P(EX_VAR(opline->op2.var)));
+ ce->ce_flags |= ZEND_ACC_ANON_BOUND;
+ ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
+}
+
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_SPEC_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
@@ -2329,195 +2441,21 @@ try_class_name:
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_DYNAMIC_CALL_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
- zend_function *fbc;
- zval *function_name, *func;
- zend_string *lcname;
- zend_class_entry *called_scope;
- zend_object *object;
+ zval *function_name;
zend_execute_data *call;
- uint32_t call_info = ZEND_CALL_NESTED_FUNCTION;
SAVE_OPLINE();
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)) {
- const char *colon;
-
- if ((colon = zend_memrchr(Z_STRVAL_P(function_name), ':', Z_STRLEN_P(function_name))) != NULL &&
- colon > Z_STRVAL_P(function_name) &&
- *(colon-1) == ':'
- ) {
- zend_string *mname;
- size_t cname_length = colon - Z_STRVAL_P(function_name) - 1;
- size_t mname_length = Z_STRLEN_P(function_name) - cname_length - (sizeof("::") - 1);
-
- lcname = zend_string_init(Z_STRVAL_P(function_name), cname_length, 0);
-
- object = NULL;
- called_scope = zend_fetch_class_by_name(lcname, NULL, ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION);
- if (UNEXPECTED(called_scope == NULL)) {
- zend_string_release(lcname);
- ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
- }
-
- mname = zend_string_init(Z_STRVAL_P(function_name) + (cname_length + sizeof("::") - 1), mname_length, 0);
-
- if (called_scope->get_static_method) {
- fbc = called_scope->get_static_method(called_scope, mname);
- } else {
- fbc = zend_std_get_static_method(called_scope, mname, NULL);
- }
- if (UNEXPECTED(fbc == NULL)) {
- if (EXPECTED(!EG(exception))) {
- zend_throw_error(NULL, "Call to undefined method %s::%s()", ZSTR_VAL(called_scope->name), ZSTR_VAL(mname));
- }
- zend_string_release(lcname);
- zend_string_release(mname);
-
- HANDLE_EXCEPTION();
- }
-
- zend_string_release(lcname);
- zend_string_release(mname);
-
- if (!(fbc->common.fn_flags & ZEND_ACC_STATIC)) {
- if (fbc->common.fn_flags & ZEND_ACC_ALLOW_STATIC) {
- zend_error(E_DEPRECATED,
- "Non-static method %s::%s() should not be called statically",
- ZSTR_VAL(fbc->common.scope->name), ZSTR_VAL(fbc->common.function_name));
- if (UNEXPECTED(EG(exception) != NULL)) {
- HANDLE_EXCEPTION();
- }
- } else {
- zend_throw_error(
- zend_ce_error,
- "Non-static method %s::%s() cannot be called statically",
- ZSTR_VAL(fbc->common.scope->name), ZSTR_VAL(fbc->common.function_name));
-
- HANDLE_EXCEPTION();
- }
- }
- } else {
- if (Z_STRVAL_P(function_name)[0] == '\\') {
- lcname = zend_string_alloc(Z_STRLEN_P(function_name) - 1, 0);
- zend_str_tolower_copy(ZSTR_VAL(lcname), Z_STRVAL_P(function_name) + 1, Z_STRLEN_P(function_name) - 1);
- } else {
- lcname = zend_string_tolower(Z_STR_P(function_name));
- }
- if (UNEXPECTED((func = zend_hash_find(EG(function_table), lcname)) == NULL)) {
- zend_throw_error(NULL, "Call to undefined function %s()", Z_STRVAL_P(function_name));
- zend_string_release(lcname);
-
- HANDLE_EXCEPTION();
- }
- zend_string_release(lcname);
-
- fbc = Z_FUNC_P(func);
- called_scope = NULL;
- object = NULL;
- }
-
- } else if (IS_CV != IS_CONST &&
- EXPECTED(Z_TYPE_P(function_name) == IS_OBJECT) &&
- Z_OBJ_HANDLER_P(function_name, get_closure) &&
- Z_OBJ_HANDLER_P(function_name, get_closure)(function_name, &called_scope, &fbc, &object) == SUCCESS) {
- if (fbc->common.fn_flags & ZEND_ACC_CLOSURE) {
- /* Delay closure destruction until its invocation */
- ZEND_ASSERT(GC_TYPE((zend_object*)fbc->common.prototype) == IS_OBJECT);
- GC_REFCOUNT((zend_object*)fbc->common.prototype)++;
- call_info |= ZEND_CALL_CLOSURE;
- } else if (object) {
- call_info |= ZEND_CALL_RELEASE_THIS;
- GC_REFCOUNT(object)++; /* For $this pointer */
- }
-
- } else if (EXPECTED(Z_TYPE_P(function_name) == IS_ARRAY) &&
- zend_hash_num_elements(Z_ARRVAL_P(function_name)) == 2) {
- zval *obj;
- zval *method;
- obj = zend_hash_index_find(Z_ARRVAL_P(function_name), 0);
- method = zend_hash_index_find(Z_ARRVAL_P(function_name), 1);
-
- if (!obj || !method) {
- zend_throw_error(NULL, "Array callback has to contain indices 0 and 1");
-
- HANDLE_EXCEPTION();
- }
-
- ZVAL_DEREF(obj);
- if (Z_TYPE_P(obj) != IS_STRING && Z_TYPE_P(obj) != IS_OBJECT) {
- zend_throw_error(NULL, "First array member is not a valid class name or object");
-
- HANDLE_EXCEPTION();
- }
-
- ZVAL_DEREF(method);
- if (Z_TYPE_P(method) != IS_STRING) {
- zend_throw_error(NULL, "Second array member is not a valid method");
-
- HANDLE_EXCEPTION();
- }
-
- if (Z_TYPE_P(obj) == IS_STRING) {
- object = NULL;
- called_scope = zend_fetch_class_by_name(Z_STR_P(obj), NULL, ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION);
- if (UNEXPECTED(called_scope == NULL)) {
- ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
- }
-
- if (called_scope->get_static_method) {
- fbc = called_scope->get_static_method(called_scope, Z_STR_P(method));
- } else {
- fbc = zend_std_get_static_method(called_scope, Z_STR_P(method), NULL);
- }
- if (UNEXPECTED(fbc == NULL)) {
- if (EXPECTED(!EG(exception))) {
- zend_throw_error(NULL, "Call to undefined method %s::%s()", ZSTR_VAL(called_scope->name), Z_STRVAL_P(method));
- }
-
- HANDLE_EXCEPTION();
- }
- if (!(fbc->common.fn_flags & ZEND_ACC_STATIC)) {
- if (fbc->common.fn_flags & ZEND_ACC_ALLOW_STATIC) {
- zend_error(E_DEPRECATED,
- "Non-static method %s::%s() should not be called statically",
- ZSTR_VAL(fbc->common.scope->name), ZSTR_VAL(fbc->common.function_name));
- if (UNEXPECTED(EG(exception) != NULL)) {
- HANDLE_EXCEPTION();
- }
- } else {
- zend_throw_error(
- zend_ce_error,
- "Non-static method %s::%s() cannot be called statically",
- ZSTR_VAL(fbc->common.scope->name), ZSTR_VAL(fbc->common.function_name));
-
- HANDLE_EXCEPTION();
- }
- }
- } else {
- called_scope = Z_OBJCE_P(obj);
- object = Z_OBJ_P(obj);
-
- fbc = Z_OBJ_HT_P(obj)->get_method(&object, Z_STR_P(method), NULL);
- if (UNEXPECTED(fbc == NULL)) {
- if (EXPECTED(!EG(exception))) {
- zend_throw_error(NULL, "Call to undefined method %s::%s()", ZSTR_VAL(object->ce->name), Z_STRVAL_P(method));
- }
-
- HANDLE_EXCEPTION();
- }
-
- if ((fbc->common.fn_flags & ZEND_ACC_STATIC) != 0) {
- object = NULL;
- } else {
- call_info |= ZEND_CALL_RELEASE_THIS;
- GC_REFCOUNT(object)++; /* For $this pointer */
- }
- }
-
- } else if ((IS_CV & (IS_VAR|IS_CV)) && Z_TYPE_P(function_name) == IS_REFERENCE) {
+ call = zend_init_dynamic_call_string(Z_STR_P(function_name), opline->extended_value);
+ } else if (IS_CV != IS_CONST && EXPECTED(Z_TYPE_P(function_name) == IS_OBJECT)) {
+ call = zend_init_dynamic_call_object(function_name, opline->extended_value);
+ } else if (EXPECTED(Z_TYPE_P(function_name) == IS_ARRAY)) {
+ call = zend_init_dynamic_call_array(Z_ARRVAL_P(function_name), opline->extended_value);
+ } else if ((IS_CV & (IS_VAR|IS_CV)) && EXPECTED(Z_TYPE_P(function_name) == IS_REFERENCE)) {
function_name = Z_REFVAL_P(function_name);
goto try_function_name;
} else {
@@ -2528,11 +2466,13 @@ try_function_name:
}
}
zend_throw_error(NULL, "Function name must be a string");
+ call = NULL;
+ }
+ if (UNEXPECTED(!call)) {
HANDLE_EXCEPTION();
}
- call = zend_vm_stack_push_call_frame(call_info,
- fbc, opline->extended_value, called_scope, object);
+
call->prev_execute_data = EX(call);
EX(call) = call;
@@ -2585,195 +2525,21 @@ try_class_name:
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_DYNAMIC_CALL_SPEC_TMPVAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
- zend_function *fbc;
- zval *function_name, *func;
- zend_string *lcname;
zend_free_op free_op2;
- zend_class_entry *called_scope;
- zend_object *object;
+ zval *function_name;
zend_execute_data *call;
- uint32_t call_info = ZEND_CALL_NESTED_FUNCTION;
SAVE_OPLINE();
function_name = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2);
try_function_name:
if ((IS_TMP_VAR|IS_VAR) != IS_CONST && EXPECTED(Z_TYPE_P(function_name) == IS_STRING)) {
- const char *colon;
-
- if ((colon = zend_memrchr(Z_STRVAL_P(function_name), ':', Z_STRLEN_P(function_name))) != NULL &&
- colon > Z_STRVAL_P(function_name) &&
- *(colon-1) == ':'
- ) {
- zend_string *mname;
- size_t cname_length = colon - Z_STRVAL_P(function_name) - 1;
- size_t mname_length = Z_STRLEN_P(function_name) - cname_length - (sizeof("::") - 1);
-
- lcname = zend_string_init(Z_STRVAL_P(function_name), cname_length, 0);
-
- object = NULL;
- called_scope = zend_fetch_class_by_name(lcname, NULL, ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION);
- if (UNEXPECTED(called_scope == NULL)) {
- zend_string_release(lcname);
- ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
- }
-
- mname = zend_string_init(Z_STRVAL_P(function_name) + (cname_length + sizeof("::") - 1), mname_length, 0);
-
- if (called_scope->get_static_method) {
- fbc = called_scope->get_static_method(called_scope, mname);
- } else {
- fbc = zend_std_get_static_method(called_scope, mname, NULL);
- }
- if (UNEXPECTED(fbc == NULL)) {
- if (EXPECTED(!EG(exception))) {
- zend_throw_error(NULL, "Call to undefined method %s::%s()", ZSTR_VAL(called_scope->name), ZSTR_VAL(mname));
- }
- zend_string_release(lcname);
- zend_string_release(mname);
- zval_ptr_dtor_nogc(free_op2);
- HANDLE_EXCEPTION();
- }
-
- zend_string_release(lcname);
- zend_string_release(mname);
-
- if (!(fbc->common.fn_flags & ZEND_ACC_STATIC)) {
- if (fbc->common.fn_flags & ZEND_ACC_ALLOW_STATIC) {
- zend_error(E_DEPRECATED,
- "Non-static method %s::%s() should not be called statically",
- ZSTR_VAL(fbc->common.scope->name), ZSTR_VAL(fbc->common.function_name));
- if (UNEXPECTED(EG(exception) != NULL)) {
- HANDLE_EXCEPTION();
- }
- } else {
- zend_throw_error(
- zend_ce_error,
- "Non-static method %s::%s() cannot be called statically",
- ZSTR_VAL(fbc->common.scope->name), ZSTR_VAL(fbc->common.function_name));
- zval_ptr_dtor_nogc(free_op2);
- HANDLE_EXCEPTION();
- }
- }
- } else {
- if (Z_STRVAL_P(function_name)[0] == '\\') {
- lcname = zend_string_alloc(Z_STRLEN_P(function_name) - 1, 0);
- zend_str_tolower_copy(ZSTR_VAL(lcname), Z_STRVAL_P(function_name) + 1, Z_STRLEN_P(function_name) - 1);
- } else {
- lcname = zend_string_tolower(Z_STR_P(function_name));
- }
- if (UNEXPECTED((func = zend_hash_find(EG(function_table), lcname)) == NULL)) {
- zend_throw_error(NULL, "Call to undefined function %s()", Z_STRVAL_P(function_name));
- zend_string_release(lcname);
- zval_ptr_dtor_nogc(free_op2);
- HANDLE_EXCEPTION();
- }
- zend_string_release(lcname);
-
- fbc = Z_FUNC_P(func);
- called_scope = NULL;
- object = NULL;
- }
- zval_ptr_dtor_nogc(free_op2);
- } else if ((IS_TMP_VAR|IS_VAR) != IS_CONST &&
- EXPECTED(Z_TYPE_P(function_name) == IS_OBJECT) &&
- Z_OBJ_HANDLER_P(function_name, get_closure) &&
- Z_OBJ_HANDLER_P(function_name, get_closure)(function_name, &called_scope, &fbc, &object) == SUCCESS) {
- if (fbc->common.fn_flags & ZEND_ACC_CLOSURE) {
- /* Delay closure destruction until its invocation */
- ZEND_ASSERT(GC_TYPE((zend_object*)fbc->common.prototype) == IS_OBJECT);
- GC_REFCOUNT((zend_object*)fbc->common.prototype)++;
- call_info |= ZEND_CALL_CLOSURE;
- } else if (object) {
- call_info |= ZEND_CALL_RELEASE_THIS;
- GC_REFCOUNT(object)++; /* For $this pointer */
- }
- zval_ptr_dtor_nogc(free_op2);
- } else if (EXPECTED(Z_TYPE_P(function_name) == IS_ARRAY) &&
- zend_hash_num_elements(Z_ARRVAL_P(function_name)) == 2) {
- zval *obj;
- zval *method;
- obj = zend_hash_index_find(Z_ARRVAL_P(function_name), 0);
- method = zend_hash_index_find(Z_ARRVAL_P(function_name), 1);
-
- if (!obj || !method) {
- zend_throw_error(NULL, "Array callback has to contain indices 0 and 1");
- zval_ptr_dtor_nogc(free_op2);
- HANDLE_EXCEPTION();
- }
-
- ZVAL_DEREF(obj);
- if (Z_TYPE_P(obj) != IS_STRING && Z_TYPE_P(obj) != IS_OBJECT) {
- zend_throw_error(NULL, "First array member is not a valid class name or object");
- zval_ptr_dtor_nogc(free_op2);
- HANDLE_EXCEPTION();
- }
-
- ZVAL_DEREF(method);
- if (Z_TYPE_P(method) != IS_STRING) {
- zend_throw_error(NULL, "Second array member is not a valid method");
- zval_ptr_dtor_nogc(free_op2);
- HANDLE_EXCEPTION();
- }
-
- if (Z_TYPE_P(obj) == IS_STRING) {
- object = NULL;
- called_scope = zend_fetch_class_by_name(Z_STR_P(obj), NULL, ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION);
- if (UNEXPECTED(called_scope == NULL)) {
- ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
- }
-
- if (called_scope->get_static_method) {
- fbc = called_scope->get_static_method(called_scope, Z_STR_P(method));
- } else {
- fbc = zend_std_get_static_method(called_scope, Z_STR_P(method), NULL);
- }
- if (UNEXPECTED(fbc == NULL)) {
- if (EXPECTED(!EG(exception))) {
- zend_throw_error(NULL, "Call to undefined method %s::%s()", ZSTR_VAL(called_scope->name), Z_STRVAL_P(method));
- }
- zval_ptr_dtor_nogc(free_op2);
- HANDLE_EXCEPTION();
- }
- if (!(fbc->common.fn_flags & ZEND_ACC_STATIC)) {
- if (fbc->common.fn_flags & ZEND_ACC_ALLOW_STATIC) {
- zend_error(E_DEPRECATED,
- "Non-static method %s::%s() should not be called statically",
- ZSTR_VAL(fbc->common.scope->name), ZSTR_VAL(fbc->common.function_name));
- if (UNEXPECTED(EG(exception) != NULL)) {
- HANDLE_EXCEPTION();
- }
- } else {
- zend_throw_error(
- zend_ce_error,
- "Non-static method %s::%s() cannot be called statically",
- ZSTR_VAL(fbc->common.scope->name), ZSTR_VAL(fbc->common.function_name));
- zval_ptr_dtor_nogc(free_op2);
- HANDLE_EXCEPTION();
- }
- }
- } else {
- called_scope = Z_OBJCE_P(obj);
- object = Z_OBJ_P(obj);
-
- fbc = Z_OBJ_HT_P(obj)->get_method(&object, Z_STR_P(method), NULL);
- if (UNEXPECTED(fbc == NULL)) {
- if (EXPECTED(!EG(exception))) {
- zend_throw_error(NULL, "Call to undefined method %s::%s()", ZSTR_VAL(object->ce->name), Z_STRVAL_P(method));
- }
- zval_ptr_dtor_nogc(free_op2);
- HANDLE_EXCEPTION();
- }
-
- if ((fbc->common.fn_flags & ZEND_ACC_STATIC) != 0) {
- object = NULL;
- } else {
- call_info |= ZEND_CALL_RELEASE_THIS;
- GC_REFCOUNT(object)++; /* For $this pointer */
- }
- }
- zval_ptr_dtor_nogc(free_op2);
- } else if (((IS_TMP_VAR|IS_VAR) & (IS_VAR|IS_CV)) && Z_TYPE_P(function_name) == IS_REFERENCE) {
+ call = zend_init_dynamic_call_string(Z_STR_P(function_name), opline->extended_value);
+ } else if ((IS_TMP_VAR|IS_VAR) != IS_CONST && EXPECTED(Z_TYPE_P(function_name) == IS_OBJECT)) {
+ call = zend_init_dynamic_call_object(function_name, opline->extended_value);
+ } else if (EXPECTED(Z_TYPE_P(function_name) == IS_ARRAY)) {
+ call = zend_init_dynamic_call_array(Z_ARRVAL_P(function_name), opline->extended_value);
+ } else if (((IS_TMP_VAR|IS_VAR) & (IS_VAR|IS_CV)) && EXPECTED(Z_TYPE_P(function_name) == IS_REFERENCE)) {
function_name = Z_REFVAL_P(function_name);
goto try_function_name;
} else {
@@ -2784,11 +2550,15 @@ try_function_name:
}
}
zend_throw_error(NULL, "Function name must be a string");
- zval_ptr_dtor_nogc(free_op2);
+ call = NULL;
+ }
+
+ zval_ptr_dtor_nogc(free_op2);
+
+ if (UNEXPECTED(!call)) {
HANDLE_EXCEPTION();
}
- call = zend_vm_stack_push_call_frame(call_info,
- fbc, opline->extended_value, called_scope, object);
+
call->prev_execute_data = EX(call);
EX(call) = call;
@@ -2818,7 +2588,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_BOOL_NOT_SPEC_CONST_HANDLER(ZE
ZVAL_FALSE(EX_VAR(opline->result.var));
} else if (EXPECTED(Z_TYPE_INFO_P(val) <= IS_TRUE)) {
ZVAL_TRUE(EX_VAR(opline->result.var));
- if (UNEXPECTED(Z_TYPE_INFO_P(val) == IS_UNDEF)) {
+ if (IS_CONST == IS_CV && UNEXPECTED(Z_TYPE_INFO_P(val) == IS_UNDEF)) {
SAVE_OPLINE();
GET_OP1_UNDEF_CV(val, BP_VAR_R);
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
@@ -3054,46 +2824,61 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_RETURN_SPEC_CONST_HANDLER(ZEND
{
USE_OPLINE
zval *retval_ptr;
+ zval *return_value;
zend_free_op free_op1;
retval_ptr = EX_CONSTANT(opline->op1);
+ return_value = EX(return_value);
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);
- if (EX(return_value)) {
- ZVAL_NULL(EX(return_value));
+ if (return_value) {
+ ZVAL_NULL(return_value);
}
- } else if (!EX(return_value)) {
- if (IS_CONST == IS_VAR || IS_CONST == IS_TMP_VAR ) {
+ } else if (!return_value) {
+ if (IS_CONST & (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));
+ zval_dtor_func(Z_COUNTED_P(free_op1));
}
}
} else {
- if (IS_CONST == IS_CONST || IS_CONST == IS_TMP_VAR) {
- ZVAL_COPY_VALUE(EX(return_value), retval_ptr);
+ if ((IS_CONST & (IS_CONST|IS_TMP_VAR))) {
+ ZVAL_COPY_VALUE(return_value, retval_ptr);
if (IS_CONST == IS_CONST) {
- if (UNEXPECTED(Z_OPT_COPYABLE_P(EX(return_value)))) {
- zval_copy_ctor_func(EX(return_value));
+ if (UNEXPECTED(Z_OPT_REFCOUNTED_P(return_value))) {
+ Z_ADDREF_P(return_value);
}
}
} else if (IS_CONST == IS_CV) {
- ZVAL_DEREF(retval_ptr);
- ZVAL_COPY(EX(return_value), retval_ptr);
+ if (Z_OPT_REFCOUNTED_P(retval_ptr)) {
+ if (EXPECTED(!Z_OPT_ISREF_P(retval_ptr))) {
+ ZVAL_COPY_VALUE(return_value, retval_ptr);
+ if (EXPECTED(!(EX_CALL_INFO() & ZEND_CALL_CODE))) {
+ ZVAL_NULL(retval_ptr);
+ } else {
+ Z_ADDREF_P(return_value);
+ }
+ } else {
+ retval_ptr = Z_REFVAL_P(retval_ptr);
+ ZVAL_COPY(return_value, retval_ptr);
+ }
+ } else {
+ ZVAL_COPY_VALUE(return_value, retval_ptr);
+ }
} else /* if (IS_CONST == IS_VAR) */ {
if (UNEXPECTED(Z_ISREF_P(retval_ptr))) {
zend_refcounted *ref = Z_COUNTED_P(retval_ptr);
retval_ptr = Z_REFVAL_P(retval_ptr);
- ZVAL_COPY_VALUE(EX(return_value), retval_ptr);
+ ZVAL_COPY_VALUE(return_value, retval_ptr);
if (UNEXPECTED(--GC_REFCOUNT(ref) == 0)) {
efree_size(ref, sizeof(zend_reference));
} else if (Z_OPT_REFCOUNTED_P(retval_ptr)) {
Z_ADDREF_P(retval_ptr);
}
} else {
- ZVAL_COPY_VALUE(EX(return_value), retval_ptr);
+ ZVAL_COPY_VALUE(return_value, retval_ptr);
}
}
}
@@ -3109,21 +2894,23 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_RETURN_BY_REF_SPEC_CONST_HANDL
SAVE_OPLINE();
do {
- if (IS_CONST == IS_CONST || IS_CONST == IS_TMP_VAR ||
+ if ((IS_CONST & (IS_CONST|IS_TMP_VAR)) ||
(IS_CONST == IS_VAR && opline->extended_value == ZEND_RETURNS_VALUE)) {
/* Not supposed to happen, but we'll allow it */
zend_error(E_NOTICE, "Only variable references should be returned by reference");
retval_ptr = EX_CONSTANT(opline->op1);
if (!EX(return_value)) {
- if (IS_CONST == IS_TMP_VAR) {
- }
} else {
- ZVAL_COPY_VALUE(EX(return_value), retval_ptr);
- Z_VAR_FLAGS_P(EX(return_value)) = IS_VAR_RET_REF;
- if (IS_CONST != IS_TMP_VAR) {
- zval_opt_copy_ctor_no_imm(EX(return_value));
+ if (IS_CONST == IS_VAR && UNEXPECTED(Z_ISREF_P(retval_ptr))) {
+ ZVAL_COPY_VALUE(EX(return_value), retval_ptr);
+ break;
+ }
+
+ ZVAL_NEW_REF(EX(return_value), retval_ptr);
+ if (IS_CONST == IS_CONST) {
+ if (Z_REFCOUNTED_P(retval_ptr)) Z_ADDREF_P(retval_ptr);
}
}
break;
@@ -3131,20 +2918,14 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_RETURN_BY_REF_SPEC_CONST_HANDL
retval_ptr = NULL;
- if (IS_CONST == IS_VAR && UNEXPECTED(retval_ptr == NULL)) {
- zend_throw_error(NULL, "Cannot return string offsets by reference");
- HANDLE_EXCEPTION();
- }
-
if (IS_CONST == IS_VAR) {
if (retval_ptr == &EG(uninitialized_zval) ||
- (opline->extended_value == ZEND_RETURNS_FUNCTION &&
- !(Z_VAR_FLAGS_P(retval_ptr) & IS_VAR_RET_REF))) {
+ (opline->extended_value == ZEND_RETURNS_FUNCTION && !Z_ISREF_P(retval_ptr))) {
zend_error(E_NOTICE, "Only variable references should be returned by reference");
if (EX(return_value)) {
ZVAL_NEW_REF(EX(return_value), retval_ptr);
- Z_VAR_FLAGS_P(EX(return_value)) = IS_VAR_RET_REF;
- if (Z_REFCOUNTED_P(retval_ptr)) Z_ADDREF_P(retval_ptr);
+ } else {
+
}
break;
}
@@ -3154,8 +2935,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_RETURN_BY_REF_SPEC_CONST_HANDL
ZVAL_MAKE_REF(retval_ptr);
Z_ADDREF_P(retval_ptr);
ZVAL_REF(EX(return_value), Z_REF_P(retval_ptr));
- Z_VAR_FLAGS_P(EX(return_value)) = IS_VAR_RET_REF;
}
+
} while (0);
ZEND_VM_TAIL_CALL(zend_leave_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU));
@@ -3173,11 +2954,11 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_GENERATOR_RETURN_SPEC_CONST_HA
retval = EX_CONSTANT(opline->op1);
/* Copy return value into generator->retval */
- if (IS_CONST == IS_CONST || IS_CONST == IS_TMP_VAR) {
+ if ((IS_CONST & (IS_CONST|IS_TMP_VAR))) {
ZVAL_COPY_VALUE(&generator->retval, retval);
if (IS_CONST == IS_CONST) {
- if (UNEXPECTED(Z_OPT_COPYABLE(generator->retval))) {
- zval_copy_ctor_func(&generator->retval);
+ if (UNEXPECTED(Z_OPT_REFCOUNTED(generator->retval))) {
+ Z_ADDREF(generator->retval);
}
}
} else if (IS_CONST == IS_CV) {
@@ -3256,8 +3037,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SEND_VAL_SPEC_CONST_HANDLER(ZE
arg = ZEND_CALL_VAR(EX(call), opline->result.var);
ZVAL_COPY_VALUE(arg, value);
if (IS_CONST == IS_CONST) {
- if (UNEXPECTED(Z_OPT_COPYABLE_P(arg))) {
- zval_copy_ctor_func(arg);
+ if (UNEXPECTED(Z_OPT_REFCOUNTED_P(arg))) {
+ Z_ADDREF_P(arg);
}
}
ZEND_VM_NEXT_OPCODE();
@@ -3270,7 +3051,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SEND_VAL_EX_SPEC_CONST_HANDLER
uint32_t arg_num = opline->op2.num;
- if (EXPECTED(arg_num <= MAX_ARG_FLAG_NUM)) {
+ if (EXPECTED(0)) {
if (QUICK_ARG_MUST_BE_SENT_BY_REF(EX(call)->func, arg_num)) {
goto send_val_by_ref;
}
@@ -3287,8 +3068,39 @@ send_val_by_ref:
arg = ZEND_CALL_VAR(EX(call), opline->result.var);
ZVAL_COPY_VALUE(arg, value);
if (IS_CONST == IS_CONST) {
- if (UNEXPECTED(Z_OPT_COPYABLE_P(arg))) {
- zval_copy_ctor_func(arg);
+ if (UNEXPECTED(Z_OPT_REFCOUNTED_P(arg))) {
+ Z_ADDREF_P(arg);
+ }
+ }
+ ZEND_VM_NEXT_OPCODE();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SEND_VAL_EX_SPEC_CONST_QUICK_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zval *value, *arg;
+
+ uint32_t arg_num = opline->op2.num;
+
+ if (EXPECTED(1)) {
+ if (QUICK_ARG_MUST_BE_SENT_BY_REF(EX(call)->func, arg_num)) {
+ goto send_val_by_ref;
+ }
+ } else if (ARG_MUST_BE_SENT_BY_REF(EX(call)->func, arg_num)) {
+send_val_by_ref:
+ SAVE_OPLINE();
+ zend_throw_error(NULL, "Cannot pass parameter %d by reference", arg_num);
+
+ arg = ZEND_CALL_VAR(EX(call), opline->result.var);
+ ZVAL_UNDEF(arg);
+ HANDLE_EXCEPTION();
+ }
+ value = EX_CONSTANT(opline->op1);
+ arg = ZEND_CALL_VAR(EX(call), opline->result.var);
+ ZVAL_COPY_VALUE(arg, value);
+ if (IS_CONST == IS_CONST) {
+ if (UNEXPECTED(Z_OPT_REFCOUNTED_P(arg))) {
+ Z_ADDREF_P(arg);
}
}
ZEND_VM_NEXT_OPCODE();
@@ -3305,7 +3117,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_BOOL_SPEC_CONST_HANDLER(ZEND_O
ZVAL_TRUE(EX_VAR(opline->result.var));
} else if (EXPECTED(Z_TYPE_INFO_P(val) <= IS_TRUE)) {
ZVAL_FALSE(EX_VAR(opline->result.var));
- if (UNEXPECTED(Z_TYPE_INFO_P(val) == IS_UNDEF)) {
+ if (IS_CONST == IS_CV && UNEXPECTED(Z_TYPE_INFO_P(val) == IS_UNDEF)) {
SAVE_OPLINE();
GET_OP1_UNDEF_CV(val, BP_VAR_R);
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
@@ -3322,9 +3134,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_BOOL_SPEC_CONST_HANDLER(ZEND_O
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_NEW_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
- zval object_zval;
+ zval *result;
zend_function *constructor;
zend_class_entry *ce;
+ zend_execute_data *call;
SAVE_OPLINE();
if (IS_CONST == IS_CONST) {
@@ -3336,39 +3149,50 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_NEW_SPEC_CONST_HANDLER(ZEND_OP
}
CACHE_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)), ce);
}
+ } else if (IS_CONST == IS_UNUSED) {
+ ce = zend_fetch_class(NULL, opline->op1.num);
+ if (UNEXPECTED(ce == NULL)) {
+ ZEND_ASSERT(EG(exception));
+ HANDLE_EXCEPTION();
+ }
} else {
ce = Z_CE_P(EX_VAR(opline->op1.var));
}
- if (UNEXPECTED(object_init_ex(&object_zval, ce) != SUCCESS)) {
+
+ result = EX_VAR(opline->result.var);
+ if (UNEXPECTED(object_init_ex(result, ce) != SUCCESS)) {
HANDLE_EXCEPTION();
}
- constructor = Z_OBJ_HT(object_zval)->get_constructor(Z_OBJ(object_zval));
+ constructor = Z_OBJ_HT_P(result)->get_constructor(Z_OBJ_P(result));
if (constructor == NULL) {
- if (EXPECTED(RETURN_VALUE_USED(opline))) {
- ZVAL_COPY_VALUE(EX_VAR(opline->result.var), &object_zval);
- } else {
- OBJ_RELEASE(Z_OBJ(object_zval));
+ /* If there are no arguments, skip over the DO_FCALL opcode. We check if the next
+ * opcode is DO_FCALL in case EXT instructions are used. */
+ if (EXPECTED(opline->extended_value == 0 && (opline+1)->opcode == ZEND_DO_FCALL)) {
+ ZEND_VM_NEXT_OPCODE_EX(1, 2);
}
- ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2));
+
+ /* Perform a dummy function call */
+ call = zend_vm_stack_push_call_frame(
+ ZEND_CALL_FUNCTION, (zend_function *) &zend_pass_function,
+ opline->extended_value, NULL, NULL);
} else {
+ if (EXPECTED(constructor->type == ZEND_USER_FUNCTION) && UNEXPECTED(!constructor->op_array.run_time_cache)) {
+ init_func_run_time_cache(&constructor->op_array);
+ }
/* We are not handling overloaded classes right now */
- zend_execute_data *call = zend_vm_stack_push_call_frame(
- ZEND_CALL_FUNCTION | ZEND_CALL_RELEASE_THIS | ZEND_CALL_CTOR |
- (EXPECTED(RETURN_VALUE_USED(opline)) ? 0 : ZEND_CALL_CTOR_RESULT_UNUSED),
+ call = zend_vm_stack_push_call_frame(
+ ZEND_CALL_FUNCTION | ZEND_CALL_RELEASE_THIS | ZEND_CALL_CTOR,
constructor,
opline->extended_value,
ce,
- Z_OBJ(object_zval));
- call->prev_execute_data = EX(call);
- EX(call) = call;
-
- if (EXPECTED(RETURN_VALUE_USED(opline))) {
- ZVAL_COPY(EX_VAR(opline->result.var), &object_zval);
- }
-
- ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
+ Z_OBJ_P(result));
+ Z_ADDREF_P(result);
}
+
+ call->prev_execute_data = EX(call);
+ EX(call) = call;
+ ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CLONE_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@@ -3376,14 +3200,14 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CLONE_SPEC_CONST_HANDLER(ZEND_
USE_OPLINE
zval *obj;
- zend_class_entry *ce;
+ zend_class_entry *ce, *scope;
zend_function *clone;
zend_object_clone_obj_t clone_call;
SAVE_OPLINE();
obj = EX_CONSTANT(opline->op1);
- if (IS_CONST == IS_UNUSED && UNEXPECTED(Z_OBJ_P(obj) == NULL)) {
+ if (IS_CONST == IS_UNUSED && UNEXPECTED(Z_TYPE_P(obj) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
HANDLE_EXCEPTION();
}
@@ -3410,43 +3234,39 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CLONE_SPEC_CONST_HANDLER(ZEND_
} while (0);
ce = Z_OBJCE_P(obj);
- clone = ce ? ce->clone : NULL;
- clone_call = Z_OBJ_HT_P(obj)->clone_obj;
+ clone = ce->clone;
+ clone_call = Z_OBJ_HT_P(obj)->clone_obj;
if (UNEXPECTED(clone_call == NULL)) {
- if (ce) {
- zend_throw_error(NULL, "Trying to clone an uncloneable object of class %s", ZSTR_VAL(ce->name));
- } else {
- zend_throw_error(NULL, "Trying to clone an uncloneable object");
- }
+ zend_throw_error(NULL, "Trying to clone an uncloneable object of class %s", ZSTR_VAL(ce->name));
HANDLE_EXCEPTION();
}
- if (ce && clone) {
+ if (clone) {
if (clone->op_array.fn_flags & ZEND_ACC_PRIVATE) {
/* Ensure that if we're calling a private function, we're allowed to do so.
*/
- if (UNEXPECTED(ce != EG(scope))) {
- zend_throw_error(NULL, "Call to private %s::__clone() from context '%s'", ZSTR_VAL(ce->name), EG(scope) ? ZSTR_VAL(EG(scope)->name) : "");
+ scope = EX(func)->op_array.scope;
+ if (UNEXPECTED(ce != scope)) {
+ zend_throw_error(NULL, "Call to private %s::__clone() from context '%s'", ZSTR_VAL(ce->name), scope ? ZSTR_VAL(scope->name) : "");
HANDLE_EXCEPTION();
}
} else if ((clone->common.fn_flags & ZEND_ACC_PROTECTED)) {
/* Ensure that if we're calling a protected function, we're allowed to do so.
*/
- if (UNEXPECTED(!zend_check_protected(zend_get_function_root_class(clone), EG(scope)))) {
- zend_throw_error(NULL, "Call to protected %s::__clone() from context '%s'", ZSTR_VAL(ce->name), EG(scope) ? ZSTR_VAL(EG(scope)->name) : "");
+ scope = EX(func)->op_array.scope;
+ if (UNEXPECTED(!zend_check_protected(zend_get_function_root_class(clone), scope))) {
+ zend_throw_error(NULL, "Call to protected %s::__clone() from context '%s'", ZSTR_VAL(ce->name), scope ? ZSTR_VAL(scope->name) : "");
HANDLE_EXCEPTION();
}
}
}
- if (EXPECTED(EG(exception) == NULL)) {
- ZVAL_OBJ(EX_VAR(opline->result.var), clone_call(obj));
- if (UNEXPECTED(!RETURN_VALUE_USED(opline)) || UNEXPECTED(EG(exception) != NULL)) {
- OBJ_RELEASE(Z_OBJ_P(EX_VAR(opline->result.var)));
- }
+ ZVAL_OBJ(EX_VAR(opline->result.var), clone_call(obj));
+ if (UNEXPECTED(EG(exception) != NULL)) {
+ OBJ_RELEASE(Z_OBJ_P(EX_VAR(opline->result.var)));
}
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
@@ -3500,11 +3320,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CAST_SPEC_CONST_HANDLER(ZEND_O
if (Z_TYPE_P(expr) == opline->extended_value) {
ZVAL_COPY_VALUE(result, expr);
if (IS_CONST == IS_CONST) {
- if (UNEXPECTED(Z_OPT_COPYABLE_P(result))) {
- zval_copy_ctor_func(result);
- }
+ if (UNEXPECTED(Z_OPT_REFCOUNTED_P(result))) Z_ADDREF_P(result);
} else if (IS_CONST != IS_TMP_VAR) {
- if (Z_OPT_REFCOUNTED_P(expr)) Z_ADDREF_P(expr);
+ if (Z_OPT_REFCOUNTED_P(result)) Z_ADDREF_P(result);
}
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
@@ -3517,9 +3335,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CAST_SPEC_CONST_HANDLER(ZEND_O
if (Z_TYPE_P(expr) != IS_NULL) {
expr = zend_hash_index_add_new(Z_ARRVAL_P(result), 0, expr);
if (IS_CONST == IS_CONST) {
- if (UNEXPECTED(Z_OPT_COPYABLE_P(expr))) {
- zval_copy_ctor_func(expr);
- }
+ if (UNEXPECTED(Z_OPT_REFCOUNTED_P(expr))) Z_ADDREF_P(expr);
} else {
if (Z_OPT_REFCOUNTED_P(expr)) Z_ADDREF_P(expr);
}
@@ -3533,11 +3349,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CAST_SPEC_CONST_HANDLER(ZEND_O
if (Z_TYPE_P(expr) != IS_ARRAY) {
object_init(result);
if (Z_TYPE_P(expr) != IS_NULL) {
- expr = zend_hash_str_add_new(Z_OBJPROP_P(result), "scalar", sizeof("scalar")-1, expr);
+ expr = zend_hash_add_new(Z_OBJPROP_P(result), CG(known_strings)[ZEND_STR_SCALAR], expr);
if (IS_CONST == IS_CONST) {
- if (UNEXPECTED(Z_OPT_COPYABLE_P(expr))) {
- zval_copy_ctor_func(expr);
- }
+ if (UNEXPECTED(Z_OPT_REFCOUNTED_P(expr))) Z_ADDREF_P(expr);
} else {
if (Z_OPT_REFCOUNTED_P(expr)) Z_ADDREF_P(expr);
}
@@ -3555,89 +3369,20 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CAST_SPEC_CONST_HANDLER(ZEND_O
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INCLUDE_OR_EVAL_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
- zend_op_array *new_op_array=NULL;
+ zend_op_array *new_op_array;
zval *inc_filename;
- zval tmp_inc_filename;
- zend_bool failure_retval=0;
SAVE_OPLINE();
inc_filename = EX_CONSTANT(opline->op1);
-
- 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;
- }
-
- if (opline->extended_value != ZEND_EVAL && strlen(Z_STRVAL_P(inc_filename)) != Z_STRLEN_P(inc_filename)) {
- if (opline->extended_value == ZEND_INCLUDE_ONCE || opline->extended_value == ZEND_INCLUDE) {
- zend_message_dispatcher(ZMSG_FAILED_INCLUDE_FOPEN, Z_STRVAL_P(inc_filename));
- } else {
- zend_message_dispatcher(ZMSG_FAILED_REQUIRE_FOPEN, Z_STRVAL_P(inc_filename));
- }
- } else {
- switch (opline->extended_value) {
- case ZEND_INCLUDE_ONCE:
- case ZEND_REQUIRE_ONCE: {
- zend_file_handle file_handle;
- zend_string *resolved_path;
-
- resolved_path = zend_resolve_path(Z_STRVAL_P(inc_filename), (int)Z_STRLEN_P(inc_filename));
- if (resolved_path) {
- failure_retval = zend_hash_exists(&EG(included_files), resolved_path);
- } else {
- resolved_path = zend_string_copy(Z_STR_P(inc_filename));
- }
-
- if (failure_retval) {
- /* do nothing, file already included */
- } else if (SUCCESS == zend_stream_open(ZSTR_VAL(resolved_path), &file_handle)) {
-
- if (!file_handle.opened_path) {
- file_handle.opened_path = zend_string_copy(resolved_path);
- }
-
- if (zend_hash_add_empty_element(&EG(included_files), file_handle.opened_path)) {
- new_op_array = zend_compile_file(&file_handle, (opline->extended_value==ZEND_INCLUDE_ONCE?ZEND_INCLUDE:ZEND_REQUIRE));
- zend_destroy_file_handle(&file_handle);
- } else {
- zend_file_handle_dtor(&file_handle);
- failure_retval=1;
- }
- } else {
- if (opline->extended_value == ZEND_INCLUDE_ONCE) {
- zend_message_dispatcher(ZMSG_FAILED_INCLUDE_FOPEN, Z_STRVAL_P(inc_filename));
- } else {
- zend_message_dispatcher(ZMSG_FAILED_REQUIRE_FOPEN, Z_STRVAL_P(inc_filename));
- }
- }
- zend_string_release(resolved_path);
- }
- break;
- case ZEND_INCLUDE:
- case ZEND_REQUIRE:
- new_op_array = compile_filename(opline->extended_value, inc_filename);
- break;
- case ZEND_EVAL: {
- char *eval_desc = zend_make_compiled_string_description("eval()'d code");
-
- new_op_array = zend_compile_string(inc_filename, eval_desc);
- efree(eval_desc);
- }
- break;
- EMPTY_SWITCH_DEFAULT_CASE()
- }
- }
- if (Z_TYPE(tmp_inc_filename) != IS_UNDEF) {
- zend_string_release(Z_STR(tmp_inc_filename));
- }
+ new_op_array = zend_include_or_eval(inc_filename, opline->extended_value);
if (UNEXPECTED(EG(exception) != NULL)) {
HANDLE_EXCEPTION();
+ } else if (new_op_array == ZEND_FAKE_OP_ARRAY) {
+ if (RETURN_VALUE_USED(opline)) {
+ ZVAL_TRUE(EX_VAR(opline->result.var));
+ }
} else if (EXPECTED(new_op_array != NULL)) {
zval *return_value = NULL;
zend_execute_data *call;
@@ -3646,19 +3391,21 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INCLUDE_OR_EVAL_SPEC_CONST_HAN
return_value = EX_VAR(opline->result.var);
}
- new_op_array->scope = EG(scope);
+ new_op_array->scope = EX(func)->op_array.scope;
- call = zend_vm_stack_push_call_frame(ZEND_CALL_NESTED_CODE,
- (zend_function*)new_op_array, 0, EX(called_scope), Z_OBJ(EX(This)));
+ call = zend_vm_stack_push_call_frame(ZEND_CALL_NESTED_CODE | ZEND_CALL_HAS_SYMBOL_TABLE,
+ (zend_function*)new_op_array, 0,
+ Z_TYPE(EX(This)) != IS_OBJECT ? Z_CE(EX(This)) : NULL,
+ Z_TYPE(EX(This)) == IS_OBJECT ? Z_OBJ(EX(This)) : NULL);
- if (EX(symbol_table)) {
+ if (EX_CALL_INFO() & ZEND_CALL_HAS_SYMBOL_TABLE) {
call->symbol_table = EX(symbol_table);
} else {
call->symbol_table = zend_rebuild_symbol_table();
}
call->prev_execute_data = execute_data;
- i_init_code_execute_data(call, new_op_array, return_value);
+ i_init_code_execute_data(call, new_op_array, return_value);
if (EXPECTED(zend_execute_ex == execute_ex)) {
ZEND_VM_ENTER();
} else {
@@ -3673,12 +3420,11 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INCLUDE_OR_EVAL_SPEC_CONST_HAN
zend_throw_exception_internal(NULL);
HANDLE_EXCEPTION();
}
-
} else if (RETURN_VALUE_USED(opline)) {
- ZVAL_BOOL(EX_VAR(opline->result.var), failure_retval);
+ ZVAL_FALSE(EX_VAR(opline->result.var));
}
- ZEND_VM_INTERRUPT_CHECK();
- ZEND_VM_NEXT_OPCODE();
+ ZEND_VM_SET_OPCODE(opline + 1);
+ ZEND_VM_CONTINUE();
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FE_RESET_R_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@@ -3710,6 +3456,13 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FE_RESET_R_SPEC_CONST_HANDLER(
if (IS_CONST != IS_TMP_VAR) {
Z_ADDREF_P(array_ptr);
}
+ if (Z_OBJ_P(array_ptr)->properties
+ && UNEXPECTED(GC_REFCOUNT(Z_OBJ_P(array_ptr)->properties) > 1)) {
+ if (EXPECTED(!(GC_FLAGS(Z_OBJ_P(array_ptr)->properties) & IS_ARRAY_IMMUTABLE))) {
+ GC_REFCOUNT(Z_OBJ_P(array_ptr)->properties)--;
+ }
+ Z_OBJ_P(array_ptr)->properties = zend_array_dup(Z_OBJ_P(array_ptr)->properties);
+ }
fe_ht = Z_OBJPROP_P(array_ptr);
pos = 0;
p = fe_ht->arData;
@@ -3853,6 +3606,13 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_CONST_HANDLER
array_ptr = EX_VAR(opline->result.var);
ZVAL_COPY_VALUE(array_ptr, array_ref);
}
+ if (Z_OBJ_P(array_ptr)->properties
+ && UNEXPECTED(GC_REFCOUNT(Z_OBJ_P(array_ptr)->properties) > 1)) {
+ if (EXPECTED(!(GC_FLAGS(Z_OBJ_P(array_ptr)->properties) & IS_ARRAY_IMMUTABLE))) {
+ GC_REFCOUNT(Z_OBJ_P(array_ptr)->properties)--;
+ }
+ Z_OBJ_P(array_ptr)->properties = zend_array_dup(Z_OBJ_P(array_ptr)->properties);
+ }
fe_ht = Z_OBJPROP_P(array_ptr);
p = fe_ht->arData;
while (1) {
@@ -3992,20 +3752,20 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_JMP_SET_SPEC_CONST_HANDLER(ZEN
value = Z_REFVAL_P(value);
}
if (i_zend_is_true(value)) {
- ZVAL_COPY_VALUE(EX_VAR(opline->result.var), value);
+ zval *result = EX_VAR(opline->result.var);
+
+ ZVAL_COPY_VALUE(result, value);
if (IS_CONST == IS_CONST) {
- if (UNEXPECTED(Z_OPT_COPYABLE_P(value))) {
- zval_copy_ctor_func(EX_VAR(opline->result.var));
- }
+ if (UNEXPECTED(Z_OPT_REFCOUNTED_P(result))) Z_ADDREF_P(result);
} else if (IS_CONST == IS_CV) {
- if (Z_OPT_REFCOUNTED_P(value)) Z_ADDREF_P(value);
+ if (Z_OPT_REFCOUNTED_P(result)) Z_ADDREF_P(result);
} else if (IS_CONST == IS_VAR && ref) {
zend_reference *r = Z_REF_P(ref);
if (UNEXPECTED(--GC_REFCOUNT(r) == 0)) {
efree_size(r, sizeof(zend_reference));
- } else if (Z_OPT_REFCOUNTED_P(value)) {
- Z_ADDREF_P(value);
+ } else if (Z_OPT_REFCOUNTED_P(result)) {
+ Z_ADDREF_P(result);
}
}
ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2));
@@ -4032,20 +3792,19 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_COALESCE_SPEC_CONST_HANDLER(ZE
}
if (Z_TYPE_P(value) > IS_NULL) {
- ZVAL_COPY_VALUE(EX_VAR(opline->result.var), value);
+ zval *result = EX_VAR(opline->result.var);
+ ZVAL_COPY_VALUE(result, value);
if (IS_CONST == IS_CONST) {
- if (UNEXPECTED(Z_OPT_COPYABLE_P(value))) {
- zval_copy_ctor_func(EX_VAR(opline->result.var));
- }
+ if (UNEXPECTED(Z_OPT_REFCOUNTED_P(result))) Z_ADDREF_P(result);
} else if (IS_CONST == IS_CV) {
- if (Z_OPT_REFCOUNTED_P(value)) Z_ADDREF_P(value);
+ if (Z_OPT_REFCOUNTED_P(result)) Z_ADDREF_P(result);
} else if (IS_CONST == IS_VAR && ref) {
zend_reference *r = Z_REF_P(ref);
if (UNEXPECTED(--GC_REFCOUNT(r) == 0)) {
efree_size(r, sizeof(zend_reference));
- } else if (Z_OPT_REFCOUNTED_P(value)) {
- Z_ADDREF_P(value);
+ } else if (Z_OPT_REFCOUNTED_P(result)) {
+ Z_ADDREF_P(result);
}
}
ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2));
@@ -4086,8 +3845,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_QM_ASSIGN_SPEC_CONST_HANDLER(Z
} else {
ZVAL_COPY_VALUE(result, value);
if (IS_CONST == IS_CONST) {
- if (UNEXPECTED(Z_OPT_COPYABLE_P(value))) {
- zval_copy_ctor_func(result);
+ if (UNEXPECTED(Z_OPT_REFCOUNTED_P(result))) {
+ Z_ADDREF_P(result);
}
}
}
@@ -4246,7 +4005,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_TYPE_CHECK_SPEC_CONST_HANDLER(
if (IS_CONST != IS_CONST && UNEXPECTED(Z_TYPE_P(value) == IS_OBJECT)) {
zend_class_entry *ce = Z_OBJCE_P(value);
- if (UNEXPECTED(ZSTR_LEN(ce->name) != sizeof("__PHP_Incomplete_Class") - 1) ||
+ if (EXPECTED(ZSTR_LEN(ce->name) != sizeof("__PHP_Incomplete_Class") - 1) ||
EXPECTED(memcmp(ZSTR_VAL(ce->name), "__PHP_Incomplete_Class", sizeof("__PHP_Incomplete_Class") - 1) != 0)) {
result = 1;
}
@@ -4279,7 +4038,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DEFINED_SPEC_CONST_HANDLER(ZEN
result = 1;
} else if ((c = zend_quick_get_constant(EX_CONSTANT(opline->op1), 0)) == NULL) {
result = 0;
- ZVAL_FALSE(EX_VAR(opline->result.var));
} else {
CACHE_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)), c);
result = 1;
@@ -4289,6 +4047,28 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DEFINED_SPEC_CONST_HANDLER(ZEN
ZEND_VM_NEXT_OPCODE();
}
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_QM_ASSIGN_DOUBLE_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+
+ zval *value;
+
+ value = EX_CONSTANT(opline->op1);
+ ZVAL_DOUBLE(EX_VAR(opline->result.var), Z_DVAL_P(value));
+ ZEND_VM_NEXT_OPCODE();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_QM_ASSIGN_NOREF_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+
+ zval *value;
+
+ value = EX_CONSTANT(opline->op1);
+ ZVAL_COPY_VALUE(EX_VAR(opline->result.var), value);
+ ZEND_VM_NEXT_OPCODE();
+}
+
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ADD_SPEC_CONST_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
@@ -4554,7 +4334,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CONCAT_SPEC_CONST_CONST_HANDLE
!ZSTR_IS_INTERNED(op1_str) && GC_REFCOUNT(op1_str) == 1) {
size_t len = ZSTR_LEN(op1_str);
- str = zend_string_realloc(op1_str, len + ZSTR_LEN(op2_str), 0);
+ str = zend_string_extend(op1_str, len + ZSTR_LEN(op2_str), 0);
memcpy(ZSTR_VAL(str) + len, ZSTR_VAL(op2_str), ZSTR_LEN(op2_str)+1);
ZVAL_NEW_STR(EX_VAR(opline->result.var), str);
break;
@@ -4926,14 +4706,14 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_BOOL_XOR_SPEC_CONST_CONST_HAND
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_fetch_var_address_helper_SPEC_CONST_CONST(int type ZEND_OPCODE_HANDLER_ARGS_DC)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_fetch_static_prop_helper_SPEC_CONST_CONST(int type ZEND_OPCODE_HANDLER_ARGS_DC)
{
USE_OPLINE
zval *varname;
zval *retval;
zend_string *name;
- HashTable *target_symbol_table;
+ zend_class_entry *ce;
SAVE_OPLINE();
varname = EX_CONSTANT(opline->op1);
@@ -4950,119 +4730,73 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_fetch_var_address_helper_SPEC_
name = zval_get_string(varname);
}
- if (IS_CONST != IS_UNUSED) {
- zend_class_entry *ce;
-
- if (IS_CONST == IS_CONST) {
- if (IS_CONST == IS_CONST && EXPECTED((ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)))) != NULL)) {
- retval = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)) + sizeof(void*));
-
- /* check if static properties were destoyed */
- if (UNEXPECTED(CE_STATIC_MEMBERS(ce) == NULL)) {
- zend_throw_error(NULL, "Access to undeclared static property: %s::$%s", ZSTR_VAL(ce->name), ZSTR_VAL(name));
+ if (IS_CONST == IS_CONST) {
+ if (IS_CONST == IS_CONST && EXPECTED((ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)))) != NULL)) {
+ retval = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)) + sizeof(void*));
- HANDLE_EXCEPTION();
- }
+ /* check if static properties were destoyed */
+ if (UNEXPECTED(CE_STATIC_MEMBERS(ce) == NULL)) {
+ zend_throw_error(NULL, "Access to undeclared static property: %s::$%s", ZSTR_VAL(ce->name), ZSTR_VAL(name));
- goto fetch_var_return;
- } else if (UNEXPECTED((ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)))) == NULL)) {
- ce = zend_fetch_class_by_name(Z_STR_P(EX_CONSTANT(opline->op2)), EX_CONSTANT(opline->op2) + 1, ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION);
- if (UNEXPECTED(ce == NULL)) {
- if (IS_CONST != IS_CONST) {
- zend_string_release(name);
- }
-
- ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
- }
- CACHE_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)), ce);
+ HANDLE_EXCEPTION();
}
- } else {
- ce = Z_CE_P(EX_VAR(opline->op2.var));
- if (IS_CONST == IS_CONST &&
- (retval = CACHED_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)), ce)) != NULL) {
-
- /* check if static properties were destoyed */
- if (UNEXPECTED(CE_STATIC_MEMBERS(ce) == NULL)) {
- zend_throw_error(NULL, "Access to undeclared static property: %s::$%s", ZSTR_VAL(ce->name), ZSTR_VAL(name));
- HANDLE_EXCEPTION();
+ goto fetch_static_prop_return;
+ } else if (UNEXPECTED((ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)))) == NULL)) {
+ ce = zend_fetch_class_by_name(Z_STR_P(EX_CONSTANT(opline->op2)), EX_CONSTANT(opline->op2) + 1, ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION);
+ if (UNEXPECTED(ce == NULL)) {
+ if (IS_CONST != IS_CONST) {
+ zend_string_release(name);
}
- goto fetch_var_return;
- }
- }
- retval = zend_std_get_static_property(ce, name, 0);
- if (UNEXPECTED(EG(exception))) {
- if (IS_CONST != IS_CONST) {
- zend_string_release(name);
+ ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
-
- HANDLE_EXCEPTION();
+ CACHE_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)), ce);
}
- if (IS_CONST == IS_CONST && retval) {
- CACHE_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)), ce, retval);
- }
-
} else {
- target_symbol_table = zend_get_target_symbol_table(execute_data, opline->extended_value & ZEND_FETCH_TYPE_MASK);
- retval = zend_hash_find(target_symbol_table, name);
- if (retval == NULL) {
- switch (type) {
- case BP_VAR_R:
- case BP_VAR_UNSET:
- zend_error(E_NOTICE,"Undefined variable: %s", ZSTR_VAL(name));
- /* break missing intentionally */
- case BP_VAR_IS:
- retval = &EG(uninitialized_zval);
- break;
- case BP_VAR_RW:
- zend_error(E_NOTICE,"Undefined variable: %s", ZSTR_VAL(name));
- retval = zend_hash_update(target_symbol_table, name, &EG(uninitialized_zval));
- break;
- case BP_VAR_W:
- retval = zend_hash_add_new(target_symbol_table, name, &EG(uninitialized_zval));
- break;
- EMPTY_SWITCH_DEFAULT_CASE()
- }
- /* GLOBAL or $$name variable may be an INDIRECT pointer to CV */
- } else if (Z_TYPE_P(retval) == IS_INDIRECT) {
- retval = Z_INDIRECT_P(retval);
- if (Z_TYPE_P(retval) == IS_UNDEF) {
- switch (type) {
- case BP_VAR_R:
- case BP_VAR_UNSET:
- zend_error(E_NOTICE,"Undefined variable: %s", ZSTR_VAL(name));
- /* break missing intentionally */
- case BP_VAR_IS:
- retval = &EG(uninitialized_zval);
- break;
- case BP_VAR_RW:
- zend_error(E_NOTICE,"Undefined variable: %s", ZSTR_VAL(name));
- /* break missing intentionally */
- case BP_VAR_W:
- ZVAL_NULL(retval);
- break;
- EMPTY_SWITCH_DEFAULT_CASE()
+ if (IS_CONST == IS_UNUSED) {
+ ce = zend_fetch_class(NULL, opline->op2.num);
+ if (UNEXPECTED(ce == NULL)) {
+ ZEND_ASSERT(EG(exception));
+ if (IS_CONST != IS_CONST) {
+ zend_string_release(name);
}
+
+ HANDLE_EXCEPTION();
}
+ } else {
+ ce = Z_CE_P(EX_VAR(opline->op2.var));
}
- if ((opline->extended_value & ZEND_FETCH_TYPE_MASK) == ZEND_FETCH_STATIC) {
- if (Z_CONSTANT_P(retval)) {
- if (UNEXPECTED(zval_update_constant_ex(retval, 1, NULL) != SUCCESS)) {
+ if (IS_CONST == IS_CONST &&
+ (retval = CACHED_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)), ce)) != NULL) {
- HANDLE_EXCEPTION();
- }
+ /* check if static properties were destoyed */
+ if (UNEXPECTED(CE_STATIC_MEMBERS(ce) == NULL)) {
+ zend_throw_error(NULL, "Access to undeclared static property: %s::$%s", ZSTR_VAL(ce->name), ZSTR_VAL(name));
+
+ HANDLE_EXCEPTION();
}
- } else if ((opline->extended_value & ZEND_FETCH_TYPE_MASK) != ZEND_FETCH_GLOBAL_LOCK) {
+ goto fetch_static_prop_return;
}
}
+ retval = zend_std_get_static_property(ce, name, 0);
+ if (UNEXPECTED(EG(exception))) {
+ if (IS_CONST != IS_CONST) {
+ zend_string_release(name);
+ }
+
+ HANDLE_EXCEPTION();
+ }
+ if (IS_CONST == IS_CONST && retval) {
+ CACHE_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)), ce, retval);
+ }
if (IS_CONST != IS_CONST) {
zend_string_release(name);
}
-fetch_var_return:
+fetch_static_prop_return:
ZEND_ASSERT(retval != NULL);
if (type == BP_VAR_R || type == BP_VAR_IS) {
if (/*type == BP_VAR_R &&*/ Z_ISREF_P(retval) && Z_REFCOUNT_P(retval) == 1) {
@@ -5075,51 +4809,73 @@ fetch_var_return:
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_R_SPEC_CONST_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_STATIC_PROP_R_SPEC_CONST_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
- ZEND_VM_TAIL_CALL(zend_fetch_var_address_helper_SPEC_CONST_CONST(BP_VAR_R ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_fetch_static_prop_helper_SPEC_CONST_CONST(BP_VAR_R ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_W_SPEC_CONST_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_STATIC_PROP_W_SPEC_CONST_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
- ZEND_VM_TAIL_CALL(zend_fetch_var_address_helper_SPEC_CONST_CONST(BP_VAR_W ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_fetch_static_prop_helper_SPEC_CONST_CONST(BP_VAR_W ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_RW_SPEC_CONST_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_STATIC_PROP_RW_SPEC_CONST_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
- ZEND_VM_TAIL_CALL(zend_fetch_var_address_helper_SPEC_CONST_CONST(BP_VAR_RW ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_fetch_static_prop_helper_SPEC_CONST_CONST(BP_VAR_RW ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_FUNC_ARG_SPEC_CONST_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_STATIC_PROP_FUNC_ARG_SPEC_CONST_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
if (zend_is_by_ref_func_arg_fetch(opline, EX(call))) {
- ZEND_VM_TAIL_CALL(zend_fetch_var_address_helper_SPEC_CONST_CONST(BP_VAR_W ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_fetch_static_prop_helper_SPEC_CONST_CONST(BP_VAR_W ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
} else {
- ZEND_VM_TAIL_CALL(zend_fetch_var_address_helper_SPEC_CONST_CONST(BP_VAR_R ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_fetch_static_prop_helper_SPEC_CONST_CONST(BP_VAR_R ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_UNSET_SPEC_CONST_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_STATIC_PROP_UNSET_SPEC_CONST_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
- ZEND_VM_TAIL_CALL(zend_fetch_var_address_helper_SPEC_CONST_CONST(BP_VAR_UNSET ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_fetch_static_prop_helper_SPEC_CONST_CONST(BP_VAR_UNSET ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_IS_SPEC_CONST_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_STATIC_PROP_IS_SPEC_CONST_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
- ZEND_VM_TAIL_CALL(zend_fetch_var_address_helper_SPEC_CONST_CONST(BP_VAR_IS ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_fetch_static_prop_helper_SPEC_CONST_CONST(BP_VAR_IS ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_DIM_R_SPEC_CONST_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
- zval *container;
+ zval *container, *dim, *value, *result;
SAVE_OPLINE();
container = EX_CONSTANT(opline->op1);
- zend_fetch_dimension_address_read_R(EX_VAR(opline->result.var), container, EX_CONSTANT(opline->op2), IS_CONST);
+ dim = EX_CONSTANT(opline->op2);
+ if (IS_CONST != IS_CONST) {
+ if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) {
+fetch_dim_r_array:
+ value = zend_fetch_dimension_address_inner(Z_ARRVAL_P(container), dim, IS_CONST, BP_VAR_R);
+ result = EX_VAR(opline->result.var);
+ ZVAL_COPY(result, value);
+ } else if (EXPECTED(Z_TYPE_P(container) == IS_REFERENCE)) {
+ container = Z_REFVAL_P(container);
+ if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) {
+ goto fetch_dim_r_array;
+ } else {
+ goto fetch_dim_r_slow;
+ }
+ } else {
+fetch_dim_r_slow:
+ result = EX_VAR(opline->result.var);
+ zend_fetch_dimension_address_read_R_slow(result, container, dim);
+ }
+ } else {
+ result = EX_VAR(opline->result.var);
+ zend_fetch_dimension_address_read_R(result, container, dim, IS_CONST);
+ }
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
@@ -5148,21 +4904,16 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_DIM_FUNC_ARG_SPEC_CONST_
SAVE_OPLINE();
if (zend_is_by_ref_func_arg_fetch(opline, EX(call))) {
- if (IS_CONST == IS_CONST || IS_CONST == IS_TMP_VAR) {
+ if ((IS_CONST & (IS_CONST|IS_TMP_VAR))) {
zend_throw_error(NULL, "Cannot use temporary expression in write context");
HANDLE_EXCEPTION();
}
container = NULL;
- if (IS_CONST == IS_VAR && UNEXPECTED(container == NULL)) {
- zend_throw_error(NULL, "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);
if (IS_CONST == IS_VAR && READY_TO_DESTROY(free_op1)) {
- EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var), 1);
+ EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var));
}
@@ -5192,7 +4943,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_R_SPEC_CONST_CONST_H
SAVE_OPLINE();
container = EX_CONSTANT(opline->op1);
- if (IS_CONST == IS_UNUSED && UNEXPECTED(Z_OBJ_P(container) == NULL)) {
+ if (IS_CONST == IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
HANDLE_EXCEPTION();
@@ -5264,7 +5015,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_IS_SPEC_CONST_CONST_
SAVE_OPLINE();
container = EX_CONSTANT(opline->op1);
- if (IS_CONST == IS_UNUSED && UNEXPECTED(Z_OBJ_P(container) == NULL)) {
+ if (IS_CONST == IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
HANDLE_EXCEPTION();
@@ -5339,26 +5090,21 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_FUNC_ARG_SPEC_CONST_
property = EX_CONSTANT(opline->op2);
container = NULL;
- if (IS_CONST == IS_UNUSED && UNEXPECTED(Z_OBJ_P(container) == NULL)) {
+ if (IS_CONST == IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
HANDLE_EXCEPTION();
}
- if (IS_CONST == IS_CONST || IS_CONST == IS_TMP_VAR) {
+ if ((IS_CONST & (IS_CONST|IS_TMP_VAR))) {
zend_throw_error(NULL, "Cannot use temporary expression in write context");
HANDLE_EXCEPTION();
}
- if (IS_CONST == IS_VAR && UNEXPECTED(container == NULL)) {
- zend_throw_error(NULL, "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);
if (IS_CONST == IS_VAR && READY_TO_DESTROY(free_op1)) {
- EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var), 0);
+ EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var));
}
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
@@ -5375,39 +5121,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_LIST_SPEC_CONST_CONST_HA
SAVE_OPLINE();
container = EX_CONSTANT(opline->op1);
+ zend_fetch_dimension_address_read_LIST(EX_VAR(opline->result.var), container, EX_CONSTANT(opline->op2));
-try_fetch_list:
- if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) {
- zval *value = zend_hash_index_find(Z_ARRVAL_P(container), Z_LVAL_P(EX_CONSTANT(opline->op2)));
-
- if (UNEXPECTED(value == NULL)) {
- zend_error(E_NOTICE,"Undefined offset: " ZEND_ULONG_FMT, Z_LVAL_P(EX_CONSTANT(opline->op2)));
- ZVAL_NULL(EX_VAR(opline->result.var));
- } else {
- ZVAL_COPY(EX_VAR(opline->result.var), value);
- }
- } 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);
-
- if (retval) {
- if (result != retval) {
- ZVAL_COPY(result, retval);
- }
- } else {
- ZVAL_NULL(result);
- }
- } 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));
- }
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
@@ -5517,7 +5232,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_SPEC_CONST_CO
object = EX_CONSTANT(opline->op1);
- if (IS_CONST == IS_UNUSED && UNEXPECTED(Z_OBJ_P(object) == NULL)) {
+ if (IS_CONST == IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
HANDLE_EXCEPTION();
@@ -5577,6 +5292,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_SPEC_CONST_CO
EXPECTED(obj == orig_obj)) {
CACHE_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(function_name), called_scope, fbc);
}
+ if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!fbc->op_array.run_time_cache)) {
+ init_func_run_time_cache(&fbc->op_array);
+ }
}
call_info = ZEND_CALL_NESTED_FUNCTION;
@@ -5622,6 +5340,13 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_STATIC_METHOD_CALL_SPEC_C
}
CACHE_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)), ce);
}
+ } else if (IS_CONST == IS_UNUSED) {
+ ce = zend_fetch_class(NULL, opline->op1.num);
+ if (UNEXPECTED(ce == NULL)) {
+ ZEND_ASSERT(EG(exception));
+
+ HANDLE_EXCEPTION();
+ }
} else {
ce = Z_CE_P(EX_VAR(opline->op1.var));
}
@@ -5680,6 +5405,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_STATIC_METHOD_CALL_SPEC_C
CACHE_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(function_name), ce, fbc);
}
}
+ if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!fbc->op_array.run_time_cache)) {
+ init_func_run_time_cache(&fbc->op_array);
+ }
if (IS_CONST != IS_CONST) {
}
@@ -5688,16 +5416,19 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_STATIC_METHOD_CALL_SPEC_C
zend_throw_error(NULL, "Cannot call constructor");
HANDLE_EXCEPTION();
}
- if (Z_OBJ(EX(This)) && Z_OBJ(EX(This))->ce != ce->constructor->common.scope && (ce->constructor->common.fn_flags & ZEND_ACC_PRIVATE)) {
+ if (Z_TYPE(EX(This)) == IS_OBJECT && Z_OBJ(EX(This))->ce != ce->constructor->common.scope && (ce->constructor->common.fn_flags & ZEND_ACC_PRIVATE)) {
zend_throw_error(NULL, "Cannot call private %s::__construct()", ZSTR_VAL(ce->name));
HANDLE_EXCEPTION();
}
fbc = ce->constructor;
+ if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!fbc->op_array.run_time_cache)) {
+ init_func_run_time_cache(&fbc->op_array);
+ }
}
object = NULL;
if (!(fbc->common.fn_flags & ZEND_ACC_STATIC)) {
- if (Z_OBJ(EX(This)) && instanceof_function(Z_OBJCE(EX(This)), ce)) {
+ if (Z_TYPE(EX(This)) == IS_OBJECT && instanceof_function(Z_OBJCE(EX(This)), ce)) {
object = Z_OBJ(EX(This));
ce = object->ce;
} else {
@@ -5722,11 +5453,15 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_STATIC_METHOD_CALL_SPEC_C
}
}
- if (IS_CONST != IS_CONST) {
+ if (IS_CONST == IS_UNUSED) {
/* previous opcode is ZEND_FETCH_CLASS */
- if (((opline-1)->extended_value & ZEND_FETCH_CLASS_MASK) == ZEND_FETCH_CLASS_PARENT ||
- ((opline-1)->extended_value & ZEND_FETCH_CLASS_MASK) == ZEND_FETCH_CLASS_SELF) {
- ce = EX(called_scope);
+ if ((opline->op1.num & ZEND_FETCH_CLASS_MASK) == ZEND_FETCH_CLASS_PARENT ||
+ (opline->op1.num & ZEND_FETCH_CLASS_MASK) == ZEND_FETCH_CLASS_SELF) {
+ if (Z_TYPE(EX(This)) == IS_OBJECT) {
+ ce = Z_OBJCE(EX(This));
+ } else {
+ ce = Z_CE(EX(This));
+ }
}
}
@@ -5749,12 +5484,14 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_USER_CALL_SPEC_CONST_CONS
zend_class_entry *called_scope;
zend_object *object;
zend_execute_data *call;
- uint32_t call_info = ZEND_CALL_NESTED_FUNCTION;
+ uint32_t call_info = ZEND_CALL_NESTED_FUNCTION | ZEND_CALL_DYNAMIC;
SAVE_OPLINE();
function_name = EX_CONSTANT(opline->op2);
if (zend_is_callable_ex(function_name, NULL, 0, NULL, &fcc, &error)) {
func = fcc.function_handler;
+ called_scope = fcc.called_scope;
+ object = fcc.object;
if (func->common.fn_flags & ZEND_ACC_CLOSURE) {
/* Delay closure destruction until its invocation */
if (IS_CONST & (IS_VAR|IS_CV)) {
@@ -5763,10 +5500,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_USER_CALL_SPEC_CONST_CONS
ZEND_ASSERT(GC_TYPE((zend_object*)func->common.prototype) == IS_OBJECT);
GC_REFCOUNT((zend_object*)func->common.prototype)++;
call_info |= ZEND_CALL_CLOSURE;
- }
- called_scope = fcc.called_scope;
- object = fcc.object;
- if (object) {
+ } else if (object) {
call_info |= ZEND_CALL_RELEASE_THIS;
GC_REFCOUNT(object)++; /* For $this pointer */
}
@@ -5780,6 +5514,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_USER_CALL_SPEC_CONST_CONS
HANDLE_EXCEPTION();
}
}
+ if (EXPECTED(func->type == ZEND_USER_FUNCTION) && UNEXPECTED(!func->op_array.run_time_cache)) {
+ init_func_run_time_cache(&func->op_array);
+ }
} else {
zend_internal_type_error(EX_USES_STRICT_TYPES(), "%s() expects parameter 1 to be a valid callback, %s", Z_STRVAL_P(EX_CONSTANT(opline->op1)), error);
efree(error);
@@ -5870,110 +5607,84 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CASE_SPEC_CONST_CONST_HANDLER(
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CONSTANT_SPEC_CONST_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_CONSTANT_SPEC_CONST_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
+ zend_class_entry *ce, *scope;
+ zend_class_constant *c;
+ zval *value;
USE_OPLINE
SAVE_OPLINE();
- if (IS_CONST == IS_UNUSED) {
- zend_constant *c;
-
- if (EXPECTED(CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2))))) {
- c = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)));
- } else if ((c = zend_quick_get_constant(EX_CONSTANT(opline->op2) + 1, opline->extended_value)) == NULL) {
- if ((opline->extended_value & IS_CONSTANT_UNQUALIFIED) != 0) {
- char *actual = (char *)zend_memrchr(Z_STRVAL_P(EX_CONSTANT(opline->op2)), '\\', Z_STRLEN_P(EX_CONSTANT(opline->op2)));
- if (!actual) {
- ZVAL_STR_COPY(EX_VAR(opline->result.var), Z_STR_P(EX_CONSTANT(opline->op2)));
- } else {
- actual++;
- ZVAL_STRINGL(EX_VAR(opline->result.var),
- actual, Z_STRLEN_P(EX_CONSTANT(opline->op2)) - (actual - Z_STRVAL_P(EX_CONSTANT(opline->op2))));
- }
- /* non-qualified constant - allow text substitution */
- zend_error(E_NOTICE, "Use of undefined constant %s - assumed '%s'",
- Z_STRVAL_P(EX_VAR(opline->result.var)), Z_STRVAL_P(EX_VAR(opline->result.var)));
- ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
- } else {
- zend_throw_error(NULL, "Undefined constant '%s'", Z_STRVAL_P(EX_CONSTANT(opline->op2)));
- HANDLE_EXCEPTION();
- }
- } else {
- CACHE_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)), c);
- }
-#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;
- do {
- if (IS_CONST == IS_CONST) {
- if (EXPECTED(CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2))))) {
- value = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)));
- ZVAL_DEREF(value);
+ do {
+ if (IS_CONST == IS_CONST) {
+ if (EXPECTED(CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2))))) {
+ value = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)));
#ifdef ZTS
- ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)));
+ ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)));
#endif
- break;
- } else if (EXPECTED(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(ce == NULL)) {
- if (EXPECTED(!EG(exception))) {
- zend_throw_error(NULL, "Class '%s' not found", Z_STRVAL_P(EX_CONSTANT(opline->op1)));
- }
- HANDLE_EXCEPTION();
+ break;
+ } else if (EXPECTED(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(ce == NULL)) {
+ if (EXPECTED(!EG(exception))) {
+ zend_throw_error(NULL, "Class '%s' not found", Z_STRVAL_P(EX_CONSTANT(opline->op1)));
}
- CACHE_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)), ce);
+ HANDLE_EXCEPTION();
+ }
+ CACHE_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)), ce);
+ }
+ } else {
+ if (IS_CONST == IS_UNUSED) {
+ ce = zend_fetch_class(NULL, opline->op1.num);
+ if (UNEXPECTED(ce == NULL)) {
+ ZEND_ASSERT(EG(exception));
+ 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;
- }
}
+ if ((value = CACHED_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)), ce)) != NULL) {
+ break;
+ }
+ }
- 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);
+ if (EXPECTED((c = zend_hash_find_ptr(&ce->constants_table, Z_STR_P(EX_CONSTANT(opline->op2)))) != NULL)) {
+ scope = EX(func)->op_array.scope;
+ if (!zend_verify_const_access(c, scope)) {
+ zend_throw_error(NULL, "Cannot access %s const %s::%s", zend_visibility_string(Z_ACCESS_FLAGS(c->value)), ZSTR_VAL(ce->name), Z_STRVAL_P(EX_CONSTANT(opline->op2)));
+ HANDLE_EXCEPTION();
+ }
+ value = &c->value;
+ if (Z_CONSTANT_P(value)) {
+ zval_update_constant_ex(value, ce);
+ if (UNEXPECTED(EG(exception) != NULL)) {
+ HANDLE_EXCEPTION();
}
+ }
+ if (IS_CONST == IS_CONST) {
+ CACHE_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)), value);
} else {
- zend_throw_error(NULL, "Undefined class constant '%s'", Z_STRVAL_P(EX_CONSTANT(opline->op2)));
- HANDLE_EXCEPTION();
+ CACHE_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)), ce, value);
}
- } while (0);
-#ifdef ZTS
- if (ce->type == ZEND_INTERNAL_CLASS) {
- ZVAL_DUP(EX_VAR(opline->result.var), value);
} else {
- ZVAL_COPY(EX_VAR(opline->result.var), value);
+ zend_throw_error(NULL, "Undefined class constant '%s'", Z_STRVAL_P(EX_CONSTANT(opline->op2)));
+ HANDLE_EXCEPTION();
}
-#else
+ } while (0);
+
+#ifdef ZTS
+ if (ce->type == ZEND_INTERNAL_CLASS) {
+ ZVAL_DUP(EX_VAR(opline->result.var), value);
+ } else {
ZVAL_COPY(EX_VAR(opline->result.var), value);
-#endif
}
+#else
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+#endif
+
ZEND_VM_NEXT_OPCODE();
}
@@ -5987,11 +5698,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ADD_ARRAY_ELEMENT_SPEC_CONST_C
if ((IS_CONST == IS_VAR || IS_CONST == IS_CV) &&
UNEXPECTED(opline->extended_value & ZEND_ARRAY_ELEMENT_REF)) {
expr_ptr = NULL;
- if (IS_CONST == IS_VAR && UNEXPECTED(expr_ptr == NULL)) {
- zend_throw_error(NULL, "Cannot create references to/from string offsets");
- zend_array_destroy(Z_ARRVAL_P(EX_VAR(opline->result.var)));
- HANDLE_EXCEPTION();
- }
ZVAL_MAKE_REF(expr_ptr);
Z_ADDREF_P(expr_ptr);
@@ -6000,10 +5706,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ADD_ARRAY_ELEMENT_SPEC_CONST_C
if (IS_CONST == IS_TMP_VAR) {
/* pass */
} else if (IS_CONST == IS_CONST) {
- 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;
+ if (Z_REFCOUNTED_P(expr_ptr)) {
+ Z_ADDREF_P(expr_ptr);
}
} else if (IS_CONST == IS_CV) {
ZVAL_DEREF(expr_ptr);
@@ -6107,40 +5811,14 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_ARRAY_SPEC_CONST_CONST_HA
}
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_UNSET_VAR_SPEC_CONST_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_UNSET_STATIC_PROP_SPEC_CONST_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
zval tmp, *varname;
- HashTable *target_symbol_table;
+ zend_class_entry *ce;
SAVE_OPLINE();
- if (IS_CONST == IS_CV &&
- IS_CONST == IS_UNUSED &&
- (opline->extended_value & ZEND_QUICK_SET)) {
- zval *var = EX_VAR(opline->op1.var);
-
- if (Z_REFCOUNTED_P(var)) {
- zend_refcounted *garbage = Z_COUNTED_P(var);
-
- if (!--GC_REFCOUNT(garbage)) {
- ZVAL_UNDEF(var);
- zval_dtor_func_for_ptr(garbage);
- } else {
- zval *z = var;
- ZVAL_DEREF(z);
- if (Z_COLLECTABLE_P(z) && UNEXPECTED(!Z_GC_INFO_P(z))) {
- ZVAL_UNDEF(var);
- gc_possible_root(Z_COUNTED_P(z));
- } else {
- ZVAL_UNDEF(var);
- }
- }
- } else {
- ZVAL_UNDEF(var);
- }
- ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
- }
varname = EX_CONSTANT(opline->op1);
@@ -6153,33 +5831,36 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_UNSET_VAR_SPEC_CONST_CONST_HAN
varname = &tmp;
}
- if (IS_CONST != IS_UNUSED) {
- zend_class_entry *ce;
-
- if (IS_CONST == IS_CONST) {
- ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)));
+ if (IS_CONST == IS_CONST) {
+ ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)));
+ if (UNEXPECTED(ce == NULL)) {
+ ce = zend_fetch_class_by_name(Z_STR_P(EX_CONSTANT(opline->op2)), EX_CONSTANT(opline->op2) + 1, ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION);
if (UNEXPECTED(ce == NULL)) {
- ce = zend_fetch_class_by_name(Z_STR_P(EX_CONSTANT(opline->op2)), EX_CONSTANT(opline->op2) + 1, ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION);
- if (UNEXPECTED(ce == NULL)) {
- if (EXPECTED(!EG(exception))) {
- zend_throw_error(NULL, "Class '%s' not found", Z_STRVAL_P(EX_CONSTANT(opline->op2)));
- }
- if (IS_CONST != IS_CONST && Z_TYPE(tmp) != IS_UNDEF) {
- zend_string_release(Z_STR(tmp));
- }
-
- HANDLE_EXCEPTION();
+ if (EXPECTED(!EG(exception))) {
+ zend_throw_error(NULL, "Class '%s' not found", Z_STRVAL_P(EX_CONSTANT(opline->op2)));
}
- CACHE_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)), ce);
+ if (IS_CONST != IS_CONST && Z_TYPE(tmp) != IS_UNDEF) {
+ zend_string_release(Z_STR(tmp));
+ }
+
+ HANDLE_EXCEPTION();
}
- } else {
- ce = Z_CE_P(EX_VAR(opline->op2.var));
+ CACHE_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)), ce);
+ }
+ } else if (IS_CONST == IS_UNUSED) {
+ ce = zend_fetch_class(NULL, opline->op2.num);
+ if (UNEXPECTED(ce == NULL)) {
+ ZEND_ASSERT(EG(exception));
+ if (IS_CONST != IS_CONST && Z_TYPE(tmp) != IS_UNDEF) {
+ zend_string_release(Z_STR(tmp));
+ }
+
+ HANDLE_EXCEPTION();
}
- zend_std_unset_static_property(ce, Z_STR_P(varname));
} else {
- target_symbol_table = zend_get_target_symbol_table(execute_data, opline->extended_value & ZEND_FETCH_TYPE_MASK);
- zend_hash_del_ind(target_symbol_table, Z_STR_P(varname));
+ ce = Z_CE_P(EX_VAR(opline->op2.var));
}
+ zend_std_unset_static_property(ce, Z_STR_P(varname));
if (IS_CONST != IS_CONST && Z_TYPE(tmp) != IS_UNDEF) {
zend_string_release(Z_STR(tmp));
@@ -6188,103 +5869,87 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_UNSET_VAR_SPEC_CONST_CONST_HAN
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ISSET_ISEMPTY_VAR_SPEC_CONST_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ISSET_ISEMPTY_STATIC_PROP_SPEC_CONST_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
zval *value;
int result;
- if (IS_CONST == IS_CV &&
- IS_CONST == IS_UNUSED &&
- (opline->extended_value & ZEND_QUICK_SET)) {
- value = EX_VAR(opline->op1.var);
- if (opline->extended_value & ZEND_ISSET) {
- result =
- Z_TYPE_P(value) > IS_NULL &&
- (!Z_ISREF_P(value) || Z_TYPE_P(Z_REFVAL_P(value)) != IS_NULL);
- } else /* if (opline->extended_value & ZEND_ISEMPTY) */ {
- SAVE_OPLINE();
- result = !i_zend_is_true(value);
- if (UNEXPECTED(EG(exception))) {
- HANDLE_EXCEPTION();
- }
- }
- ZEND_VM_SMART_BRANCH(result, 0);
- ZVAL_BOOL(EX_VAR(opline->result.var), result);
- ZEND_VM_SET_NEXT_OPCODE(opline + 1);
- ZEND_VM_CONTINUE();
- } else {
-
- zval tmp, *varname;
-
- SAVE_OPLINE();
- varname = EX_CONSTANT(opline->op1);
- ZVAL_UNDEF(&tmp);
- if (IS_CONST != IS_CONST && Z_TYPE_P(varname) != IS_STRING) {
- ZVAL_STR(&tmp, zval_get_string(varname));
- varname = &tmp;
- }
-
- if (IS_CONST != IS_UNUSED) {
- zend_class_entry *ce;
-
- if (IS_CONST == IS_CONST) {
- if (IS_CONST == IS_CONST && EXPECTED((ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)))) != NULL)) {
- value = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)) + sizeof(void*));
-
- /* check if static properties were destoyed */
- if (UNEXPECTED(CE_STATIC_MEMBERS(ce) == NULL)) {
- value = NULL;
- }
+ zval tmp, *varname;
+ zend_class_entry *ce;
- goto is_var_return;
- } else if (UNEXPECTED((ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)))) == NULL)) {
- ce = zend_fetch_class_by_name(Z_STR_P(EX_CONSTANT(opline->op2)), EX_CONSTANT(opline->op2) + 1, ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION);
- if (UNEXPECTED(ce == NULL)) {
- ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
- }
- CACHE_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)), ce);
- }
- } else {
- ce = Z_CE_P(EX_VAR(opline->op2.var));
- if (IS_CONST == IS_CONST &&
- (value = CACHED_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)), ce)) != NULL) {
+ SAVE_OPLINE();
+ varname = EX_CONSTANT(opline->op1);
+ ZVAL_UNDEF(&tmp);
+ if (IS_CONST != IS_CONST && Z_TYPE_P(varname) != IS_STRING) {
+ ZVAL_STR(&tmp, zval_get_string(varname));
+ varname = &tmp;
+ }
- /* check if static properties were destoyed */
- if (UNEXPECTED(CE_STATIC_MEMBERS(ce) == NULL)) {
- value = NULL;
- }
+ if (IS_CONST == IS_CONST) {
+ if (IS_CONST == IS_CONST && EXPECTED((ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)))) != NULL)) {
+ value = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)) + sizeof(void*));
- goto is_var_return;
- }
+ /* check if static properties were destoyed */
+ if (UNEXPECTED(CE_STATIC_MEMBERS(ce) == NULL)) {
+ value = NULL;
}
- value = zend_std_get_static_property(ce, Z_STR_P(varname), 1);
+ goto is_static_prop_return;
+ } else if (UNEXPECTED((ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)))) == NULL)) {
+ ce = zend_fetch_class_by_name(Z_STR_P(EX_CONSTANT(opline->op2)), EX_CONSTANT(opline->op2) + 1, ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION);
+ if (UNEXPECTED(ce == NULL)) {
+ ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
+ }
+ CACHE_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)), ce);
+ }
+ } else {
+ if (IS_CONST == IS_UNUSED) {
+ ce = zend_fetch_class(NULL, opline->op2.num);
+ if (UNEXPECTED(ce == NULL)) {
+ ZEND_ASSERT(EG(exception));
+ if (IS_CONST != IS_CONST && Z_TYPE(tmp) != IS_UNDEF) {
+ zend_string_release(Z_STR(tmp));
+ }
- if (IS_CONST == IS_CONST && value) {
- CACHE_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)), ce, value);
+ HANDLE_EXCEPTION();
}
} else {
- HashTable *target_symbol_table = zend_get_target_symbol_table(execute_data, opline->extended_value & ZEND_FETCH_TYPE_MASK);
- value = zend_hash_find_ind(target_symbol_table, Z_STR_P(varname));
+ ce = Z_CE_P(EX_VAR(opline->op2.var));
}
+ if (IS_CONST == IS_CONST &&
+ (value = CACHED_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)), ce)) != NULL) {
- if (IS_CONST != IS_CONST && Z_TYPE(tmp) != IS_UNDEF) {
- zend_string_release(Z_STR(tmp));
- }
+ /* check if static properties were destoyed */
+ if (UNEXPECTED(CE_STATIC_MEMBERS(ce) == NULL)) {
+ value = NULL;
+ }
-is_var_return:
- if (opline->extended_value & ZEND_ISSET) {
- result = value && Z_TYPE_P(value) > IS_NULL &&
- (!Z_ISREF_P(value) || Z_TYPE_P(Z_REFVAL_P(value)) != IS_NULL);
- } else /* if (opline->extended_value & ZEND_ISEMPTY) */ {
- result = !value || !i_zend_is_true(value);
+ goto is_static_prop_return;
}
+ }
- ZEND_VM_SMART_BRANCH(result, 1);
- ZVAL_BOOL(EX_VAR(opline->result.var), result);
- ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
+ value = zend_std_get_static_property(ce, Z_STR_P(varname), 1);
+
+ if (IS_CONST == IS_CONST && value) {
+ CACHE_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)), ce, value);
}
+
+ if (IS_CONST != IS_CONST && Z_TYPE(tmp) != IS_UNDEF) {
+ zend_string_release(Z_STR(tmp));
+ }
+
+is_static_prop_return:
+ if (opline->extended_value & ZEND_ISSET) {
+ result = value && Z_TYPE_P(value) > IS_NULL &&
+ (!Z_ISREF_P(value) || Z_TYPE_P(Z_REFVAL_P(value)) != IS_NULL);
+ } else /* if (opline->extended_value & ZEND_ISEMPTY) */ {
+ result = !value || !i_zend_is_true(value);
+ }
+
+ ZEND_VM_SMART_BRANCH(result, 1);
+ ZVAL_BOOL(EX_VAR(opline->result.var), result);
+ ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ISSET_ISEMPTY_DIM_OBJ_SPEC_CONST_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@@ -6298,16 +5963,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ISSET_ISEMPTY_DIM_OBJ_SPEC_CON
SAVE_OPLINE();
container = EX_CONSTANT(opline->op1);
-
- if (IS_CONST == IS_UNUSED && UNEXPECTED(Z_OBJ_P(container) == NULL)) {
- zend_throw_error(NULL, "Using $this when not in object context");
-
- HANDLE_EXCEPTION();
- }
-
offset = EX_CONSTANT(opline->op2);
- if (IS_CONST != IS_UNUSED && EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) {
+ if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) {
HashTable *ht;
zval *value;
zend_string *str;
@@ -6374,8 +6032,7 @@ num_index_prop:
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 ((IS_CONST != IS_CONST && EXPECTED(Z_TYPE_P(container) == IS_OBJECT))) {
if (EXPECTED(Z_OBJ_HT_P(container)->has_dimension)) {
result =
((opline->extended_value & ZEND_ISSET) == 0) ^
@@ -6390,6 +6047,9 @@ num_index_prop:
if (EXPECTED(Z_TYPE_P(offset) == IS_LONG)) {
lval = Z_LVAL_P(offset);
isset_str_offset:
+ if (UNEXPECTED(lval < 0)) { /* Handle negative offset */
+ lval += (zend_long)Z_STRLEN_P(container);
+ }
if (EXPECTED(lval >= 0) && (size_t)lval < Z_STRLEN_P(container)) {
if (opline->extended_value & ZEND_ISSET) {
result = 1;
@@ -6435,7 +6095,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ISSET_ISEMPTY_PROP_OBJ_SPEC_CO
SAVE_OPLINE();
container = EX_CONSTANT(opline->op1);
- if (IS_CONST == IS_UNUSED && UNEXPECTED(Z_OBJ_P(container) == NULL)) {
+ if (IS_CONST == IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
HANDLE_EXCEPTION();
@@ -6482,18 +6142,13 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DECLARE_CONST_SPEC_CONST_CONST
name = EX_CONSTANT(opline->op1);
val = EX_CONSTANT(opline->op2);
- ZVAL_COPY_VALUE(&c.value, val);
+ ZVAL_COPY(&c.value, val);
if (Z_OPT_CONSTANT(c.value)) {
- if (UNEXPECTED(zval_update_constant_ex(&c.value, 0, NULL) != SUCCESS)) {
+ if (UNEXPECTED(zval_update_constant_ex(&c.value, EX(func)->op_array.scope) != SUCCESS)) {
HANDLE_EXCEPTION();
}
- } else {
- /* IS_CONST can't be IS_OBJECT, IS_RESOURCE or IS_REFERENCE */
- if (UNEXPECTED(Z_OPT_COPYABLE(c.value))) {
- zval_copy_ctor_func(&c.value);
- }
}
c.flags = CONST_CS; /* non persistent, case sensetive */
c.name = zend_string_dup(Z_STR_P(name), 0);
@@ -6533,7 +6188,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_CONST_CONST_HANDLER
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) {
+ if (IS_CONST & (IS_CONST|IS_TMP_VAR)) {
zval *value;
zend_error(E_NOTICE, "Only variable references should be yielded by reference");
@@ -6541,25 +6196,19 @@ 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 (IS_CONST == IS_CONST) {
- if (UNEXPECTED(Z_OPT_COPYABLE(generator->value))) {
- zval_copy_ctor_func(&generator->value);
+ if (UNEXPECTED(Z_OPT_REFCOUNTED(generator->value))) {
+ Z_ADDREF(generator->value);
}
}
} else {
zval *value_ptr = NULL;
- if (IS_CONST == IS_VAR && UNEXPECTED(value_ptr == NULL)) {
- zend_throw_error(NULL, "Cannot yield string offsets by reference");
-
- HANDLE_EXCEPTION();
- }
-
/* If a function call result is yielded and the function did
* not return by reference we throw a notice. */
if (IS_CONST == IS_VAR &&
(value_ptr == &EG(uninitialized_zval) ||
(opline->extended_value == ZEND_RETURNS_FUNCTION &&
- !(Z_VAR_FLAGS_P(value_ptr) & IS_VAR_RET_REF)))) {
+ !Z_ISREF_P(value_ptr)))) {
zend_error(E_NOTICE, "Only variable references should be yielded by reference");
} else {
ZVAL_MAKE_REF(value_ptr);
@@ -6573,8 +6222,8 @@ 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_COPY_VALUE(&generator->value, value);
- if (UNEXPECTED(Z_OPT_COPYABLE(generator->value))) {
- zval_copy_ctor_func(&generator->value);
+ if (UNEXPECTED(Z_OPT_REFCOUNTED(generator->value))) {
+ Z_ADDREF(generator->value);
}
} else if (IS_CONST == IS_TMP_VAR) {
ZVAL_COPY_VALUE(&generator->value, value);
@@ -6601,8 +6250,8 @@ 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_COPY_VALUE(&generator->key, key);
- if (UNEXPECTED(Z_OPT_COPYABLE(generator->key))) {
- zval_copy_ctor_func(&generator->key);
+ if (UNEXPECTED(Z_OPT_REFCOUNTED(generator->key))) {
+ Z_ADDREF(generator->key);
}
} else if (IS_CONST == IS_TMP_VAR) {
ZVAL_COPY_VALUE(&generator->key, key);
@@ -6647,6 +6296,54 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_CONST_CONST_HANDLER
ZEND_VM_RETURN();
}
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_DIM_R_INDEX_SPEC_CONST_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+
+ zval *container, *dim, *value;
+ zend_long offset;
+
+ container = EX_CONSTANT(opline->op1);
+ dim = EX_CONSTANT(opline->op2);
+ if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) {
+fetch_dim_r_index_array:
+ if (EXPECTED(Z_TYPE_P(dim) == IS_LONG)) {
+ offset = Z_LVAL_P(dim);
+ } else {
+ offset = zval_get_long(dim);
+ }
+ ZEND_HASH_INDEX_FIND(Z_ARRVAL_P(container), offset, value, fetch_dim_r_index_undef);
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ if (IS_CONST & (IS_TMP_VAR|IS_VAR)) {
+ SAVE_OPLINE();
+
+ ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
+ } else {
+ ZEND_VM_NEXT_OPCODE();
+ }
+ } else if (IS_CONST != IS_CONST && EXPECTED(Z_TYPE_P(container) == IS_REFERENCE)) {
+ container = Z_REFVAL_P(container);
+ if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) {
+ goto fetch_dim_r_index_array;
+ } else {
+ goto fetch_dim_r_index_slow;
+ }
+ } else {
+fetch_dim_r_index_slow:
+ SAVE_OPLINE();
+ zend_fetch_dimension_address_read_R_slow(EX_VAR(opline->result.var), container, dim);
+
+ ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
+ }
+
+fetch_dim_r_index_undef:
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ SAVE_OPLINE();
+ zend_error(E_NOTICE, "Undefined offset: " ZEND_LONG_FMT, offset);
+
+ ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
+}
+
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_IDENTICAL_SPEC_CONST_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
@@ -6710,7 +6407,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_CONST_TMP_HANDLER(Z
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) {
+ if (IS_CONST & (IS_CONST|IS_TMP_VAR)) {
zval *value;
zend_error(E_NOTICE, "Only variable references should be yielded by reference");
@@ -6718,25 +6415,19 @@ 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 (IS_CONST == IS_CONST) {
- if (UNEXPECTED(Z_OPT_COPYABLE(generator->value))) {
- zval_copy_ctor_func(&generator->value);
+ if (UNEXPECTED(Z_OPT_REFCOUNTED(generator->value))) {
+ Z_ADDREF(generator->value);
}
}
} else {
zval *value_ptr = NULL;
- if (IS_CONST == IS_VAR && UNEXPECTED(value_ptr == NULL)) {
- zend_throw_error(NULL, "Cannot yield string offsets by reference");
- zval_ptr_dtor_nogc(EX_VAR(opline->op2.var));
- HANDLE_EXCEPTION();
- }
-
/* If a function call result is yielded and the function did
* not return by reference we throw a notice. */
if (IS_CONST == IS_VAR &&
(value_ptr == &EG(uninitialized_zval) ||
(opline->extended_value == ZEND_RETURNS_FUNCTION &&
- !(Z_VAR_FLAGS_P(value_ptr) & IS_VAR_RET_REF)))) {
+ !Z_ISREF_P(value_ptr)))) {
zend_error(E_NOTICE, "Only variable references should be yielded by reference");
} else {
ZVAL_MAKE_REF(value_ptr);
@@ -6750,8 +6441,8 @@ 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_COPY_VALUE(&generator->value, value);
- if (UNEXPECTED(Z_OPT_COPYABLE(generator->value))) {
- zval_copy_ctor_func(&generator->value);
+ if (UNEXPECTED(Z_OPT_REFCOUNTED(generator->value))) {
+ Z_ADDREF(generator->value);
}
} else if (IS_CONST == IS_TMP_VAR) {
ZVAL_COPY_VALUE(&generator->value, value);
@@ -6778,8 +6469,8 @@ 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_COPY_VALUE(&generator->key, key);
- if (UNEXPECTED(Z_OPT_COPYABLE(generator->key))) {
- zval_copy_ctor_func(&generator->key);
+ if (UNEXPECTED(Z_OPT_REFCOUNTED(generator->key))) {
+ Z_ADDREF(generator->key);
}
} else if (IS_TMP_VAR == IS_TMP_VAR) {
ZVAL_COPY_VALUE(&generator->key, key);
@@ -6860,14 +6551,14 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_NOT_IDENTICAL_SPEC_CONST_VA
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_fetch_var_address_helper_SPEC_CONST_VAR(int type ZEND_OPCODE_HANDLER_ARGS_DC)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_fetch_static_prop_helper_SPEC_CONST_VAR(int type ZEND_OPCODE_HANDLER_ARGS_DC)
{
USE_OPLINE
zval *varname;
zval *retval;
zend_string *name;
- HashTable *target_symbol_table;
+ zend_class_entry *ce;
SAVE_OPLINE();
varname = EX_CONSTANT(opline->op1);
@@ -6884,119 +6575,73 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_fetch_var_address_helper_SPEC_
name = zval_get_string(varname);
}
- if (IS_VAR != IS_UNUSED) {
- zend_class_entry *ce;
-
- if (IS_VAR == IS_CONST) {
- if (IS_CONST == IS_CONST && EXPECTED((ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)))) != NULL)) {
- retval = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)) + sizeof(void*));
+ if (IS_VAR == IS_CONST) {
+ if (IS_CONST == IS_CONST && EXPECTED((ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)))) != NULL)) {
+ retval = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)) + sizeof(void*));
- /* check if static properties were destoyed */
- if (UNEXPECTED(CE_STATIC_MEMBERS(ce) == NULL)) {
- zend_throw_error(NULL, "Access to undeclared static property: %s::$%s", ZSTR_VAL(ce->name), ZSTR_VAL(name));
+ /* check if static properties were destoyed */
+ if (UNEXPECTED(CE_STATIC_MEMBERS(ce) == NULL)) {
+ zend_throw_error(NULL, "Access to undeclared static property: %s::$%s", ZSTR_VAL(ce->name), ZSTR_VAL(name));
- HANDLE_EXCEPTION();
- }
-
- goto fetch_var_return;
- } else if (UNEXPECTED((ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)))) == NULL)) {
- ce = zend_fetch_class_by_name(Z_STR_P(EX_CONSTANT(opline->op2)), EX_CONSTANT(opline->op2) + 1, ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION);
- if (UNEXPECTED(ce == NULL)) {
- if (IS_CONST != IS_CONST) {
- zend_string_release(name);
- }
-
- ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
- }
- CACHE_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)), ce);
+ HANDLE_EXCEPTION();
}
- } else {
- ce = Z_CE_P(EX_VAR(opline->op2.var));
- if (IS_CONST == IS_CONST &&
- (retval = CACHED_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)), ce)) != NULL) {
- /* check if static properties were destoyed */
- if (UNEXPECTED(CE_STATIC_MEMBERS(ce) == NULL)) {
- zend_throw_error(NULL, "Access to undeclared static property: %s::$%s", ZSTR_VAL(ce->name), ZSTR_VAL(name));
-
- HANDLE_EXCEPTION();
+ goto fetch_static_prop_return;
+ } else if (UNEXPECTED((ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)))) == NULL)) {
+ ce = zend_fetch_class_by_name(Z_STR_P(EX_CONSTANT(opline->op2)), EX_CONSTANT(opline->op2) + 1, ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION);
+ if (UNEXPECTED(ce == NULL)) {
+ if (IS_CONST != IS_CONST) {
+ zend_string_release(name);
}
- goto fetch_var_return;
- }
- }
- retval = zend_std_get_static_property(ce, name, 0);
- if (UNEXPECTED(EG(exception))) {
- if (IS_CONST != IS_CONST) {
- zend_string_release(name);
+ ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
-
- HANDLE_EXCEPTION();
+ CACHE_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)), ce);
}
- if (IS_CONST == IS_CONST && retval) {
- CACHE_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)), ce, retval);
- }
-
} else {
- target_symbol_table = zend_get_target_symbol_table(execute_data, opline->extended_value & ZEND_FETCH_TYPE_MASK);
- retval = zend_hash_find(target_symbol_table, name);
- if (retval == NULL) {
- switch (type) {
- case BP_VAR_R:
- case BP_VAR_UNSET:
- zend_error(E_NOTICE,"Undefined variable: %s", ZSTR_VAL(name));
- /* break missing intentionally */
- case BP_VAR_IS:
- retval = &EG(uninitialized_zval);
- break;
- case BP_VAR_RW:
- zend_error(E_NOTICE,"Undefined variable: %s", ZSTR_VAL(name));
- retval = zend_hash_update(target_symbol_table, name, &EG(uninitialized_zval));
- break;
- case BP_VAR_W:
- retval = zend_hash_add_new(target_symbol_table, name, &EG(uninitialized_zval));
- break;
- EMPTY_SWITCH_DEFAULT_CASE()
- }
- /* GLOBAL or $$name variable may be an INDIRECT pointer to CV */
- } else if (Z_TYPE_P(retval) == IS_INDIRECT) {
- retval = Z_INDIRECT_P(retval);
- if (Z_TYPE_P(retval) == IS_UNDEF) {
- switch (type) {
- case BP_VAR_R:
- case BP_VAR_UNSET:
- zend_error(E_NOTICE,"Undefined variable: %s", ZSTR_VAL(name));
- /* break missing intentionally */
- case BP_VAR_IS:
- retval = &EG(uninitialized_zval);
- break;
- case BP_VAR_RW:
- zend_error(E_NOTICE,"Undefined variable: %s", ZSTR_VAL(name));
- /* break missing intentionally */
- case BP_VAR_W:
- ZVAL_NULL(retval);
- break;
- EMPTY_SWITCH_DEFAULT_CASE()
+ if (IS_VAR == IS_UNUSED) {
+ ce = zend_fetch_class(NULL, opline->op2.num);
+ if (UNEXPECTED(ce == NULL)) {
+ ZEND_ASSERT(EG(exception));
+ if (IS_CONST != IS_CONST) {
+ zend_string_release(name);
}
+
+ HANDLE_EXCEPTION();
}
+ } else {
+ ce = Z_CE_P(EX_VAR(opline->op2.var));
}
- if ((opline->extended_value & ZEND_FETCH_TYPE_MASK) == ZEND_FETCH_STATIC) {
- if (Z_CONSTANT_P(retval)) {
- if (UNEXPECTED(zval_update_constant_ex(retval, 1, NULL) != SUCCESS)) {
+ if (IS_CONST == IS_CONST &&
+ (retval = CACHED_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)), ce)) != NULL) {
- HANDLE_EXCEPTION();
- }
+ /* check if static properties were destoyed */
+ if (UNEXPECTED(CE_STATIC_MEMBERS(ce) == NULL)) {
+ zend_throw_error(NULL, "Access to undeclared static property: %s::$%s", ZSTR_VAL(ce->name), ZSTR_VAL(name));
+
+ HANDLE_EXCEPTION();
}
- } else if ((opline->extended_value & ZEND_FETCH_TYPE_MASK) != ZEND_FETCH_GLOBAL_LOCK) {
+ goto fetch_static_prop_return;
}
}
+ retval = zend_std_get_static_property(ce, name, 0);
+ if (UNEXPECTED(EG(exception))) {
+ if (IS_CONST != IS_CONST) {
+ zend_string_release(name);
+ }
+
+ HANDLE_EXCEPTION();
+ }
+ if (IS_CONST == IS_CONST && retval) {
+ CACHE_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)), ce, retval);
+ }
if (IS_CONST != IS_CONST) {
zend_string_release(name);
}
-fetch_var_return:
+fetch_static_prop_return:
ZEND_ASSERT(retval != NULL);
if (type == BP_VAR_R || type == BP_VAR_IS) {
if (/*type == BP_VAR_R &&*/ Z_ISREF_P(retval) && Z_REFCOUNT_P(retval) == 1) {
@@ -7009,76 +6654,50 @@ fetch_var_return:
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_R_SPEC_CONST_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_STATIC_PROP_R_SPEC_CONST_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
- ZEND_VM_TAIL_CALL(zend_fetch_var_address_helper_SPEC_CONST_VAR(BP_VAR_R ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_fetch_static_prop_helper_SPEC_CONST_VAR(BP_VAR_R ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_W_SPEC_CONST_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_STATIC_PROP_W_SPEC_CONST_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
- ZEND_VM_TAIL_CALL(zend_fetch_var_address_helper_SPEC_CONST_VAR(BP_VAR_W ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_fetch_static_prop_helper_SPEC_CONST_VAR(BP_VAR_W ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_RW_SPEC_CONST_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_STATIC_PROP_RW_SPEC_CONST_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
- ZEND_VM_TAIL_CALL(zend_fetch_var_address_helper_SPEC_CONST_VAR(BP_VAR_RW ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_fetch_static_prop_helper_SPEC_CONST_VAR(BP_VAR_RW ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_FUNC_ARG_SPEC_CONST_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_STATIC_PROP_FUNC_ARG_SPEC_CONST_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
if (zend_is_by_ref_func_arg_fetch(opline, EX(call))) {
- ZEND_VM_TAIL_CALL(zend_fetch_var_address_helper_SPEC_CONST_VAR(BP_VAR_W ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_fetch_static_prop_helper_SPEC_CONST_VAR(BP_VAR_W ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
} else {
- ZEND_VM_TAIL_CALL(zend_fetch_var_address_helper_SPEC_CONST_VAR(BP_VAR_R ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_fetch_static_prop_helper_SPEC_CONST_VAR(BP_VAR_R ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_UNSET_SPEC_CONST_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_STATIC_PROP_UNSET_SPEC_CONST_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
- ZEND_VM_TAIL_CALL(zend_fetch_var_address_helper_SPEC_CONST_VAR(BP_VAR_UNSET ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_fetch_static_prop_helper_SPEC_CONST_VAR(BP_VAR_UNSET ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_IS_SPEC_CONST_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_STATIC_PROP_IS_SPEC_CONST_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
- ZEND_VM_TAIL_CALL(zend_fetch_var_address_helper_SPEC_CONST_VAR(BP_VAR_IS ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_fetch_static_prop_helper_SPEC_CONST_VAR(BP_VAR_IS ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_UNSET_VAR_SPEC_CONST_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_UNSET_STATIC_PROP_SPEC_CONST_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
zval tmp, *varname;
- HashTable *target_symbol_table;
+ zend_class_entry *ce;
SAVE_OPLINE();
- if (IS_CONST == IS_CV &&
- IS_VAR == IS_UNUSED &&
- (opline->extended_value & ZEND_QUICK_SET)) {
- zval *var = EX_VAR(opline->op1.var);
-
- if (Z_REFCOUNTED_P(var)) {
- zend_refcounted *garbage = Z_COUNTED_P(var);
-
- if (!--GC_REFCOUNT(garbage)) {
- ZVAL_UNDEF(var);
- zval_dtor_func_for_ptr(garbage);
- } else {
- zval *z = var;
- ZVAL_DEREF(z);
- if (Z_COLLECTABLE_P(z) && UNEXPECTED(!Z_GC_INFO_P(z))) {
- ZVAL_UNDEF(var);
- gc_possible_root(Z_COUNTED_P(z));
- } else {
- ZVAL_UNDEF(var);
- }
- }
- } else {
- ZVAL_UNDEF(var);
- }
- ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
- }
varname = EX_CONSTANT(opline->op1);
@@ -7091,33 +6710,36 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_UNSET_VAR_SPEC_CONST_VAR_HANDL
varname = &tmp;
}
- if (IS_VAR != IS_UNUSED) {
- zend_class_entry *ce;
-
- if (IS_VAR == IS_CONST) {
- ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)));
+ if (IS_VAR == IS_CONST) {
+ ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)));
+ if (UNEXPECTED(ce == NULL)) {
+ ce = zend_fetch_class_by_name(Z_STR_P(EX_CONSTANT(opline->op2)), EX_CONSTANT(opline->op2) + 1, ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION);
if (UNEXPECTED(ce == NULL)) {
- ce = zend_fetch_class_by_name(Z_STR_P(EX_CONSTANT(opline->op2)), EX_CONSTANT(opline->op2) + 1, ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION);
- if (UNEXPECTED(ce == NULL)) {
- if (EXPECTED(!EG(exception))) {
- zend_throw_error(NULL, "Class '%s' not found", Z_STRVAL_P(EX_CONSTANT(opline->op2)));
- }
- if (IS_CONST != IS_CONST && Z_TYPE(tmp) != IS_UNDEF) {
- zend_string_release(Z_STR(tmp));
- }
-
- HANDLE_EXCEPTION();
+ if (EXPECTED(!EG(exception))) {
+ zend_throw_error(NULL, "Class '%s' not found", Z_STRVAL_P(EX_CONSTANT(opline->op2)));
}
- CACHE_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)), ce);
+ if (IS_CONST != IS_CONST && Z_TYPE(tmp) != IS_UNDEF) {
+ zend_string_release(Z_STR(tmp));
+ }
+
+ HANDLE_EXCEPTION();
}
- } else {
- ce = Z_CE_P(EX_VAR(opline->op2.var));
+ CACHE_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)), ce);
+ }
+ } else if (IS_VAR == IS_UNUSED) {
+ ce = zend_fetch_class(NULL, opline->op2.num);
+ if (UNEXPECTED(ce == NULL)) {
+ ZEND_ASSERT(EG(exception));
+ if (IS_CONST != IS_CONST && Z_TYPE(tmp) != IS_UNDEF) {
+ zend_string_release(Z_STR(tmp));
+ }
+
+ HANDLE_EXCEPTION();
}
- zend_std_unset_static_property(ce, Z_STR_P(varname));
} else {
- target_symbol_table = zend_get_target_symbol_table(execute_data, opline->extended_value & ZEND_FETCH_TYPE_MASK);
- zend_hash_del_ind(target_symbol_table, Z_STR_P(varname));
+ ce = Z_CE_P(EX_VAR(opline->op2.var));
}
+ zend_std_unset_static_property(ce, Z_STR_P(varname));
if (IS_CONST != IS_CONST && Z_TYPE(tmp) != IS_UNDEF) {
zend_string_release(Z_STR(tmp));
@@ -7126,103 +6748,87 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_UNSET_VAR_SPEC_CONST_VAR_HANDL
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ISSET_ISEMPTY_VAR_SPEC_CONST_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ISSET_ISEMPTY_STATIC_PROP_SPEC_CONST_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
zval *value;
int result;
- if (IS_CONST == IS_CV &&
- IS_VAR == IS_UNUSED &&
- (opline->extended_value & ZEND_QUICK_SET)) {
- value = EX_VAR(opline->op1.var);
- if (opline->extended_value & ZEND_ISSET) {
- result =
- Z_TYPE_P(value) > IS_NULL &&
- (!Z_ISREF_P(value) || Z_TYPE_P(Z_REFVAL_P(value)) != IS_NULL);
- } else /* if (opline->extended_value & ZEND_ISEMPTY) */ {
- SAVE_OPLINE();
- result = !i_zend_is_true(value);
- if (UNEXPECTED(EG(exception))) {
- HANDLE_EXCEPTION();
- }
- }
- ZEND_VM_SMART_BRANCH(result, 0);
- ZVAL_BOOL(EX_VAR(opline->result.var), result);
- ZEND_VM_SET_NEXT_OPCODE(opline + 1);
- ZEND_VM_CONTINUE();
- } else {
-
- zval tmp, *varname;
-
- SAVE_OPLINE();
- varname = EX_CONSTANT(opline->op1);
- ZVAL_UNDEF(&tmp);
- if (IS_CONST != IS_CONST && Z_TYPE_P(varname) != IS_STRING) {
- ZVAL_STR(&tmp, zval_get_string(varname));
- varname = &tmp;
- }
-
- if (IS_VAR != IS_UNUSED) {
- zend_class_entry *ce;
-
- if (IS_VAR == IS_CONST) {
- if (IS_CONST == IS_CONST && EXPECTED((ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)))) != NULL)) {
- value = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)) + sizeof(void*));
-
- /* check if static properties were destoyed */
- if (UNEXPECTED(CE_STATIC_MEMBERS(ce) == NULL)) {
- value = NULL;
- }
+ zval tmp, *varname;
+ zend_class_entry *ce;
- goto is_var_return;
- } else if (UNEXPECTED((ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)))) == NULL)) {
- ce = zend_fetch_class_by_name(Z_STR_P(EX_CONSTANT(opline->op2)), EX_CONSTANT(opline->op2) + 1, ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION);
- if (UNEXPECTED(ce == NULL)) {
- ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
- }
- CACHE_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)), ce);
- }
- } else {
- ce = Z_CE_P(EX_VAR(opline->op2.var));
- if (IS_CONST == IS_CONST &&
- (value = CACHED_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)), ce)) != NULL) {
+ SAVE_OPLINE();
+ varname = EX_CONSTANT(opline->op1);
+ ZVAL_UNDEF(&tmp);
+ if (IS_CONST != IS_CONST && Z_TYPE_P(varname) != IS_STRING) {
+ ZVAL_STR(&tmp, zval_get_string(varname));
+ varname = &tmp;
+ }
- /* check if static properties were destoyed */
- if (UNEXPECTED(CE_STATIC_MEMBERS(ce) == NULL)) {
- value = NULL;
- }
+ if (IS_VAR == IS_CONST) {
+ if (IS_CONST == IS_CONST && EXPECTED((ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)))) != NULL)) {
+ value = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)) + sizeof(void*));
- goto is_var_return;
- }
+ /* check if static properties were destoyed */
+ if (UNEXPECTED(CE_STATIC_MEMBERS(ce) == NULL)) {
+ value = NULL;
}
- value = zend_std_get_static_property(ce, Z_STR_P(varname), 1);
+ goto is_static_prop_return;
+ } else if (UNEXPECTED((ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)))) == NULL)) {
+ ce = zend_fetch_class_by_name(Z_STR_P(EX_CONSTANT(opline->op2)), EX_CONSTANT(opline->op2) + 1, ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION);
+ if (UNEXPECTED(ce == NULL)) {
+ ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
+ }
+ CACHE_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)), ce);
+ }
+ } else {
+ if (IS_VAR == IS_UNUSED) {
+ ce = zend_fetch_class(NULL, opline->op2.num);
+ if (UNEXPECTED(ce == NULL)) {
+ ZEND_ASSERT(EG(exception));
+ if (IS_CONST != IS_CONST && Z_TYPE(tmp) != IS_UNDEF) {
+ zend_string_release(Z_STR(tmp));
+ }
- if (IS_CONST == IS_CONST && value) {
- CACHE_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)), ce, value);
+ HANDLE_EXCEPTION();
}
} else {
- HashTable *target_symbol_table = zend_get_target_symbol_table(execute_data, opline->extended_value & ZEND_FETCH_TYPE_MASK);
- value = zend_hash_find_ind(target_symbol_table, Z_STR_P(varname));
+ ce = Z_CE_P(EX_VAR(opline->op2.var));
}
+ if (IS_CONST == IS_CONST &&
+ (value = CACHED_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)), ce)) != NULL) {
- if (IS_CONST != IS_CONST && Z_TYPE(tmp) != IS_UNDEF) {
- zend_string_release(Z_STR(tmp));
- }
+ /* check if static properties were destoyed */
+ if (UNEXPECTED(CE_STATIC_MEMBERS(ce) == NULL)) {
+ value = NULL;
+ }
-is_var_return:
- if (opline->extended_value & ZEND_ISSET) {
- result = value && Z_TYPE_P(value) > IS_NULL &&
- (!Z_ISREF_P(value) || Z_TYPE_P(Z_REFVAL_P(value)) != IS_NULL);
- } else /* if (opline->extended_value & ZEND_ISEMPTY) */ {
- result = !value || !i_zend_is_true(value);
+ goto is_static_prop_return;
}
+ }
- ZEND_VM_SMART_BRANCH(result, 1);
- ZVAL_BOOL(EX_VAR(opline->result.var), result);
- ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
+ value = zend_std_get_static_property(ce, Z_STR_P(varname), 1);
+
+ if (IS_CONST == IS_CONST && value) {
+ CACHE_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)), ce, value);
+ }
+
+ if (IS_CONST != IS_CONST && Z_TYPE(tmp) != IS_UNDEF) {
+ zend_string_release(Z_STR(tmp));
+ }
+
+is_static_prop_return:
+ if (opline->extended_value & ZEND_ISSET) {
+ result = value && Z_TYPE_P(value) > IS_NULL &&
+ (!Z_ISREF_P(value) || Z_TYPE_P(Z_REFVAL_P(value)) != IS_NULL);
+ } else /* if (opline->extended_value & ZEND_ISEMPTY) */ {
+ result = !value || !i_zend_is_true(value);
}
+
+ ZEND_VM_SMART_BRANCH(result, 1);
+ ZVAL_BOOL(EX_VAR(opline->result.var), result);
+ ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_CONST_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@@ -7252,7 +6858,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_CONST_VAR_HANDLER(Z
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) {
+ if (IS_CONST & (IS_CONST|IS_TMP_VAR)) {
zval *value;
zend_error(E_NOTICE, "Only variable references should be yielded by reference");
@@ -7260,25 +6866,19 @@ 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 (IS_CONST == IS_CONST) {
- if (UNEXPECTED(Z_OPT_COPYABLE(generator->value))) {
- zval_copy_ctor_func(&generator->value);
+ if (UNEXPECTED(Z_OPT_REFCOUNTED(generator->value))) {
+ Z_ADDREF(generator->value);
}
}
} else {
zval *value_ptr = NULL;
- if (IS_CONST == IS_VAR && UNEXPECTED(value_ptr == NULL)) {
- zend_throw_error(NULL, "Cannot yield string offsets by reference");
- zval_ptr_dtor_nogc(EX_VAR(opline->op2.var));
- HANDLE_EXCEPTION();
- }
-
/* If a function call result is yielded and the function did
* not return by reference we throw a notice. */
if (IS_CONST == IS_VAR &&
(value_ptr == &EG(uninitialized_zval) ||
(opline->extended_value == ZEND_RETURNS_FUNCTION &&
- !(Z_VAR_FLAGS_P(value_ptr) & IS_VAR_RET_REF)))) {
+ !Z_ISREF_P(value_ptr)))) {
zend_error(E_NOTICE, "Only variable references should be yielded by reference");
} else {
ZVAL_MAKE_REF(value_ptr);
@@ -7292,8 +6892,8 @@ 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_COPY_VALUE(&generator->value, value);
- if (UNEXPECTED(Z_OPT_COPYABLE(generator->value))) {
- zval_copy_ctor_func(&generator->value);
+ if (UNEXPECTED(Z_OPT_REFCOUNTED(generator->value))) {
+ Z_ADDREF(generator->value);
}
} else if (IS_CONST == IS_TMP_VAR) {
ZVAL_COPY_VALUE(&generator->value, value);
@@ -7320,8 +6920,8 @@ 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_COPY_VALUE(&generator->key, key);
- if (UNEXPECTED(Z_OPT_COPYABLE(generator->key))) {
- zval_copy_ctor_func(&generator->key);
+ if (UNEXPECTED(Z_OPT_REFCOUNTED(generator->key))) {
+ Z_ADDREF(generator->key);
}
} else if (IS_VAR == IS_TMP_VAR) {
ZVAL_COPY_VALUE(&generator->key, key);
@@ -7390,63 +6990,70 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_fetch_var_address_helper_SPEC_
name = zval_get_string(varname);
}
- if (IS_UNUSED != IS_UNUSED) {
- zend_class_entry *ce;
-
- if (IS_UNUSED == IS_CONST) {
- if (IS_CONST == IS_CONST && EXPECTED((ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)))) != NULL)) {
- retval = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)) + sizeof(void*));
-
- /* check if static properties were destoyed */
- if (UNEXPECTED(CE_STATIC_MEMBERS(ce) == NULL)) {
- zend_throw_error(NULL, "Access to undeclared static property: %s::$%s", ZSTR_VAL(ce->name), ZSTR_VAL(name));
+ target_symbol_table = zend_get_target_symbol_table(execute_data, opline->extended_value & ZEND_FETCH_TYPE_MASK);
+ retval = zend_hash_find(target_symbol_table, name);
+ if (retval == NULL) {
+ if (UNEXPECTED(zend_string_equals(name, CG(known_strings)[ZEND_STR_THIS]))) {
+ zval *result;
- HANDLE_EXCEPTION();
- }
-
- goto fetch_var_return;
- } else if (UNEXPECTED((ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)))) == NULL)) {
- ce = zend_fetch_class_by_name(Z_STR_P(EX_CONSTANT(opline->op2)), EX_CONSTANT(opline->op2) + 1, ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION);
- if (UNEXPECTED(ce == NULL)) {
- if (IS_CONST != IS_CONST) {
- zend_string_release(name);
+fetch_this:
+ result = EX_VAR(opline->result.var);
+ switch (type) {
+ case BP_VAR_R:
+ if (EXPECTED(Z_TYPE(EX(This)) == IS_OBJECT)) {
+ ZVAL_OBJ(result, Z_OBJ(EX(This)));
+ Z_ADDREF_P(result);
+ } else {
+ ZVAL_NULL(result);
+ zend_error(E_NOTICE,"Undefined variable: this");
}
-
- ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
- }
- CACHE_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)), ce);
- }
- } else {
- ce = Z_CE_P(EX_VAR(opline->op2.var));
- if (IS_CONST == IS_CONST &&
- (retval = CACHED_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)), ce)) != NULL) {
-
- /* check if static properties were destoyed */
- if (UNEXPECTED(CE_STATIC_MEMBERS(ce) == NULL)) {
- zend_throw_error(NULL, "Access to undeclared static property: %s::$%s", ZSTR_VAL(ce->name), ZSTR_VAL(name));
-
- HANDLE_EXCEPTION();
- }
-
- goto fetch_var_return;
+ break;
+ case BP_VAR_IS:
+ if (EXPECTED(Z_TYPE(EX(This)) == IS_OBJECT)) {
+ ZVAL_OBJ(result, Z_OBJ(EX(This)));
+ Z_ADDREF_P(result);
+ } else {
+ ZVAL_NULL(result);
+ }
+ break;
+ case BP_VAR_RW:
+ case BP_VAR_W:
+ zend_throw_error(NULL, "Cannot re-assign $this");
+ break;
+ case BP_VAR_UNSET:
+ zend_throw_error(NULL, "Cannot unset $this");
+ break;
+ EMPTY_SWITCH_DEFAULT_CASE()
}
- }
- retval = zend_std_get_static_property(ce, name, 0);
- if (UNEXPECTED(EG(exception))) {
if (IS_CONST != IS_CONST) {
zend_string_release(name);
}
-
- HANDLE_EXCEPTION();
+ ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
- if (IS_CONST == IS_CONST && retval) {
- CACHE_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)), ce, retval);
+ switch (type) {
+ case BP_VAR_R:
+ case BP_VAR_UNSET:
+ zend_error(E_NOTICE,"Undefined variable: %s", ZSTR_VAL(name));
+ /* break missing intentionally */
+ case BP_VAR_IS:
+ retval = &EG(uninitialized_zval);
+ break;
+ case BP_VAR_RW:
+ zend_error(E_NOTICE,"Undefined variable: %s", ZSTR_VAL(name));
+ retval = zend_hash_update(target_symbol_table, name, &EG(uninitialized_zval));
+ break;
+ case BP_VAR_W:
+ retval = zend_hash_add_new(target_symbol_table, name, &EG(uninitialized_zval));
+ break;
+ EMPTY_SWITCH_DEFAULT_CASE()
}
-
- } else {
- target_symbol_table = zend_get_target_symbol_table(execute_data, opline->extended_value & ZEND_FETCH_TYPE_MASK);
- retval = zend_hash_find(target_symbol_table, name);
- if (retval == NULL) {
+ /* GLOBAL or $$name variable may be an INDIRECT pointer to CV */
+ } else if (Z_TYPE_P(retval) == IS_INDIRECT) {
+ retval = Z_INDIRECT_P(retval);
+ if (Z_TYPE_P(retval) == IS_UNDEF) {
+ if (UNEXPECTED(zend_string_equals(name, CG(known_strings)[ZEND_STR_THIS]))) {
+ goto fetch_this;
+ }
switch (type) {
case BP_VAR_R:
case BP_VAR_UNSET:
@@ -7457,52 +7064,23 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_fetch_var_address_helper_SPEC_
break;
case BP_VAR_RW:
zend_error(E_NOTICE,"Undefined variable: %s", ZSTR_VAL(name));
- retval = zend_hash_update(target_symbol_table, name, &EG(uninitialized_zval));
- break;
+ /* break missing intentionally */
case BP_VAR_W:
- retval = zend_hash_add_new(target_symbol_table, name, &EG(uninitialized_zval));
+ ZVAL_NULL(retval);
break;
EMPTY_SWITCH_DEFAULT_CASE()
}
- /* GLOBAL or $$name variable may be an INDIRECT pointer to CV */
- } else if (Z_TYPE_P(retval) == IS_INDIRECT) {
- retval = Z_INDIRECT_P(retval);
- if (Z_TYPE_P(retval) == IS_UNDEF) {
- switch (type) {
- case BP_VAR_R:
- case BP_VAR_UNSET:
- zend_error(E_NOTICE,"Undefined variable: %s", ZSTR_VAL(name));
- /* break missing intentionally */
- case BP_VAR_IS:
- retval = &EG(uninitialized_zval);
- break;
- case BP_VAR_RW:
- zend_error(E_NOTICE,"Undefined variable: %s", ZSTR_VAL(name));
- /* break missing intentionally */
- case BP_VAR_W:
- ZVAL_NULL(retval);
- break;
- EMPTY_SWITCH_DEFAULT_CASE()
- }
- }
}
- if ((opline->extended_value & ZEND_FETCH_TYPE_MASK) == ZEND_FETCH_STATIC) {
- if (Z_CONSTANT_P(retval)) {
- if (UNEXPECTED(zval_update_constant_ex(retval, 1, NULL) != SUCCESS)) {
+ }
- HANDLE_EXCEPTION();
- }
- }
- } else if ((opline->extended_value & ZEND_FETCH_TYPE_MASK) != ZEND_FETCH_GLOBAL_LOCK) {
+ if ((opline->extended_value & ZEND_FETCH_TYPE_MASK) != ZEND_FETCH_GLOBAL_LOCK) {
- }
}
if (IS_CONST != IS_CONST) {
zend_string_release(name);
}
-fetch_var_return:
ZEND_ASSERT(retval != NULL);
if (type == BP_VAR_R || type == BP_VAR_IS) {
if (/*type == BP_VAR_R &&*/ Z_ISREF_P(retval) && Z_REFCOUNT_P(retval) == 1) {
@@ -7551,6 +7129,145 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_IS_SPEC_CONST_UNUSED_HAN
ZEND_VM_TAIL_CALL(zend_fetch_var_address_helper_SPEC_CONST_UNUSED(BP_VAR_IS ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_fetch_static_prop_helper_SPEC_CONST_UNUSED(int type ZEND_OPCODE_HANDLER_ARGS_DC)
+{
+ USE_OPLINE
+
+ zval *varname;
+ zval *retval;
+ zend_string *name;
+ zend_class_entry *ce;
+
+ SAVE_OPLINE();
+ varname = EX_CONSTANT(opline->op1);
+
+ if (IS_CONST == IS_CONST) {
+ name = Z_STR_P(varname);
+ } else if (EXPECTED(Z_TYPE_P(varname) == IS_STRING)) {
+ 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);
+ }
+
+ if (IS_UNUSED == IS_CONST) {
+ if (IS_CONST == IS_CONST && EXPECTED((ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)))) != NULL)) {
+ retval = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)) + sizeof(void*));
+
+ /* check if static properties were destoyed */
+ if (UNEXPECTED(CE_STATIC_MEMBERS(ce) == NULL)) {
+ zend_throw_error(NULL, "Access to undeclared static property: %s::$%s", ZSTR_VAL(ce->name), ZSTR_VAL(name));
+
+ HANDLE_EXCEPTION();
+ }
+
+ goto fetch_static_prop_return;
+ } else if (UNEXPECTED((ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)))) == NULL)) {
+ ce = zend_fetch_class_by_name(Z_STR_P(EX_CONSTANT(opline->op2)), EX_CONSTANT(opline->op2) + 1, ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION);
+ if (UNEXPECTED(ce == NULL)) {
+ if (IS_CONST != IS_CONST) {
+ zend_string_release(name);
+ }
+
+ ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
+ }
+ CACHE_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)), ce);
+ }
+ } else {
+ if (IS_UNUSED == IS_UNUSED) {
+ ce = zend_fetch_class(NULL, opline->op2.num);
+ if (UNEXPECTED(ce == NULL)) {
+ ZEND_ASSERT(EG(exception));
+ if (IS_CONST != IS_CONST) {
+ zend_string_release(name);
+ }
+
+ HANDLE_EXCEPTION();
+ }
+ } else {
+ ce = Z_CE_P(EX_VAR(opline->op2.var));
+ }
+ if (IS_CONST == IS_CONST &&
+ (retval = CACHED_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)), ce)) != NULL) {
+
+ /* check if static properties were destoyed */
+ if (UNEXPECTED(CE_STATIC_MEMBERS(ce) == NULL)) {
+ zend_throw_error(NULL, "Access to undeclared static property: %s::$%s", ZSTR_VAL(ce->name), ZSTR_VAL(name));
+
+ HANDLE_EXCEPTION();
+ }
+
+ goto fetch_static_prop_return;
+ }
+ }
+ retval = zend_std_get_static_property(ce, name, 0);
+ if (UNEXPECTED(EG(exception))) {
+ if (IS_CONST != IS_CONST) {
+ zend_string_release(name);
+ }
+
+ HANDLE_EXCEPTION();
+ }
+ if (IS_CONST == IS_CONST && retval) {
+ CACHE_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)), ce, retval);
+ }
+
+ if (IS_CONST != IS_CONST) {
+ zend_string_release(name);
+ }
+
+fetch_static_prop_return:
+ ZEND_ASSERT(retval != NULL);
+ if (type == BP_VAR_R || type == BP_VAR_IS) {
+ if (/*type == BP_VAR_R &&*/ Z_ISREF_P(retval) && Z_REFCOUNT_P(retval) == 1) {
+ ZVAL_UNREF(retval);
+ }
+ ZVAL_COPY(EX_VAR(opline->result.var), retval);
+ } else {
+ ZVAL_INDIRECT(EX_VAR(opline->result.var), retval);
+ }
+ ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_STATIC_PROP_R_SPEC_CONST_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ ZEND_VM_TAIL_CALL(zend_fetch_static_prop_helper_SPEC_CONST_UNUSED(BP_VAR_R ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_STATIC_PROP_W_SPEC_CONST_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ ZEND_VM_TAIL_CALL(zend_fetch_static_prop_helper_SPEC_CONST_UNUSED(BP_VAR_W ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_STATIC_PROP_RW_SPEC_CONST_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ ZEND_VM_TAIL_CALL(zend_fetch_static_prop_helper_SPEC_CONST_UNUSED(BP_VAR_RW ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_STATIC_PROP_FUNC_ARG_SPEC_CONST_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+
+ if (zend_is_by_ref_func_arg_fetch(opline, EX(call))) {
+ ZEND_VM_TAIL_CALL(zend_fetch_static_prop_helper_SPEC_CONST_UNUSED(BP_VAR_W ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ } else {
+ ZEND_VM_TAIL_CALL(zend_fetch_static_prop_helper_SPEC_CONST_UNUSED(BP_VAR_R ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_STATIC_PROP_UNSET_SPEC_CONST_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ ZEND_VM_TAIL_CALL(zend_fetch_static_prop_helper_SPEC_CONST_UNUSED(BP_VAR_UNSET ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_STATIC_PROP_IS_SPEC_CONST_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ ZEND_VM_TAIL_CALL(zend_fetch_static_prop_helper_SPEC_CONST_UNUSED(BP_VAR_IS ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+}
+
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_DIM_FUNC_ARG_SPEC_CONST_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
@@ -7560,21 +7277,16 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_DIM_FUNC_ARG_SPEC_CONST_
SAVE_OPLINE();
if (zend_is_by_ref_func_arg_fetch(opline, EX(call))) {
- if (IS_CONST == IS_CONST || IS_CONST == IS_TMP_VAR) {
+ if ((IS_CONST & (IS_CONST|IS_TMP_VAR))) {
zend_throw_error(NULL, "Cannot use temporary expression in write context");
HANDLE_EXCEPTION();
}
container = NULL;
- if (IS_CONST == IS_VAR && UNEXPECTED(container == NULL)) {
- zend_throw_error(NULL, "Cannot use string offset as an array");
-
- HANDLE_EXCEPTION();
- }
zend_fetch_dimension_address_W(EX_VAR(opline->result.var), container, NULL, IS_UNUSED);
if (IS_CONST == IS_VAR && READY_TO_DESTROY(free_op1)) {
- EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var), 1);
+ EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var));
}
@@ -7618,6 +7330,13 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_STATIC_METHOD_CALL_SPEC_C
}
CACHE_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)), ce);
}
+ } else if (IS_CONST == IS_UNUSED) {
+ ce = zend_fetch_class(NULL, opline->op1.num);
+ if (UNEXPECTED(ce == NULL)) {
+ ZEND_ASSERT(EG(exception));
+
+ HANDLE_EXCEPTION();
+ }
} else {
ce = Z_CE_P(EX_VAR(opline->op1.var));
}
@@ -7676,6 +7395,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_STATIC_METHOD_CALL_SPEC_C
CACHE_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(function_name), ce, fbc);
}
}
+ if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!fbc->op_array.run_time_cache)) {
+ init_func_run_time_cache(&fbc->op_array);
+ }
if (IS_UNUSED != IS_CONST) {
}
@@ -7684,16 +7406,19 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_STATIC_METHOD_CALL_SPEC_C
zend_throw_error(NULL, "Cannot call constructor");
HANDLE_EXCEPTION();
}
- if (Z_OBJ(EX(This)) && Z_OBJ(EX(This))->ce != ce->constructor->common.scope && (ce->constructor->common.fn_flags & ZEND_ACC_PRIVATE)) {
+ if (Z_TYPE(EX(This)) == IS_OBJECT && Z_OBJ(EX(This))->ce != ce->constructor->common.scope && (ce->constructor->common.fn_flags & ZEND_ACC_PRIVATE)) {
zend_throw_error(NULL, "Cannot call private %s::__construct()", ZSTR_VAL(ce->name));
HANDLE_EXCEPTION();
}
fbc = ce->constructor;
+ if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!fbc->op_array.run_time_cache)) {
+ init_func_run_time_cache(&fbc->op_array);
+ }
}
object = NULL;
if (!(fbc->common.fn_flags & ZEND_ACC_STATIC)) {
- if (Z_OBJ(EX(This)) && instanceof_function(Z_OBJCE(EX(This)), ce)) {
+ if (Z_TYPE(EX(This)) == IS_OBJECT && instanceof_function(Z_OBJCE(EX(This)), ce)) {
object = Z_OBJ(EX(This));
ce = object->ce;
} else {
@@ -7718,11 +7443,15 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_STATIC_METHOD_CALL_SPEC_C
}
}
- if (IS_CONST != IS_CONST) {
+ if (IS_CONST == IS_UNUSED) {
/* previous opcode is ZEND_FETCH_CLASS */
- if (((opline-1)->extended_value & ZEND_FETCH_CLASS_MASK) == ZEND_FETCH_CLASS_PARENT ||
- ((opline-1)->extended_value & ZEND_FETCH_CLASS_MASK) == ZEND_FETCH_CLASS_SELF) {
- ce = EX(called_scope);
+ if ((opline->op1.num & ZEND_FETCH_CLASS_MASK) == ZEND_FETCH_CLASS_PARENT ||
+ (opline->op1.num & ZEND_FETCH_CLASS_MASK) == ZEND_FETCH_CLASS_SELF) {
+ if (Z_TYPE(EX(This)) == IS_OBJECT) {
+ ce = Z_OBJCE(EX(This));
+ } else {
+ ce = Z_CE(EX(This));
+ }
}
}
@@ -7782,8 +7511,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_VERIFY_RETURN_TYPE_SPEC_CONST_
if (UNEXPECTED(EG(exception) != NULL)) {
if (IS_CONST == IS_CONST) {
zval_ptr_dtor_nogc(retval_ptr);
- } else {
-
}
}
#endif
@@ -7801,11 +7528,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ADD_ARRAY_ELEMENT_SPEC_CONST_U
if ((IS_CONST == IS_VAR || IS_CONST == IS_CV) &&
UNEXPECTED(opline->extended_value & ZEND_ARRAY_ELEMENT_REF)) {
expr_ptr = NULL;
- if (IS_CONST == IS_VAR && UNEXPECTED(expr_ptr == NULL)) {
- zend_throw_error(NULL, "Cannot create references to/from string offsets");
- zend_array_destroy(Z_ARRVAL_P(EX_VAR(opline->result.var)));
- HANDLE_EXCEPTION();
- }
ZVAL_MAKE_REF(expr_ptr);
Z_ADDREF_P(expr_ptr);
@@ -7814,10 +7536,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ADD_ARRAY_ELEMENT_SPEC_CONST_U
if (IS_CONST == IS_TMP_VAR) {
/* pass */
} else if (IS_CONST == IS_CONST) {
- 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;
+ if (Z_REFCOUNTED_P(expr_ptr)) {
+ Z_ADDREF_P(expr_ptr);
}
} else if (IS_CONST == IS_CV) {
ZVAL_DEREF(expr_ptr);
@@ -7930,7 +7650,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_UNSET_VAR_SPEC_CONST_UNUSED_HA
SAVE_OPLINE();
if (IS_CONST == IS_CV &&
- IS_UNUSED == IS_UNUSED &&
(opline->extended_value & ZEND_QUICK_SET)) {
zval *var = EX_VAR(opline->op1.var);
@@ -7939,7 +7658,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_UNSET_VAR_SPEC_CONST_UNUSED_HA
if (!--GC_REFCOUNT(garbage)) {
ZVAL_UNDEF(var);
- zval_dtor_func_for_ptr(garbage);
+ zval_dtor_func(garbage);
} else {
zval *z = var;
ZVAL_DEREF(z);
@@ -7967,33 +7686,66 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_UNSET_VAR_SPEC_CONST_UNUSED_HA
varname = &tmp;
}
- if (IS_UNUSED != IS_UNUSED) {
- zend_class_entry *ce;
+ target_symbol_table = zend_get_target_symbol_table(execute_data, opline->extended_value & ZEND_FETCH_TYPE_MASK);
+ zend_hash_del_ind(target_symbol_table, Z_STR_P(varname));
- if (IS_UNUSED == IS_CONST) {
- ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)));
- if (UNEXPECTED(ce == NULL)) {
- ce = zend_fetch_class_by_name(Z_STR_P(EX_CONSTANT(opline->op2)), EX_CONSTANT(opline->op2) + 1, ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION);
- if (UNEXPECTED(ce == NULL)) {
- if (EXPECTED(!EG(exception))) {
- zend_throw_error(NULL, "Class '%s' not found", Z_STRVAL_P(EX_CONSTANT(opline->op2)));
- }
- if (IS_CONST != IS_CONST && Z_TYPE(tmp) != IS_UNDEF) {
- zend_string_release(Z_STR(tmp));
- }
+ if (IS_CONST != IS_CONST && Z_TYPE(tmp) != IS_UNDEF) {
+ zend_string_release(Z_STR(tmp));
+ }
- HANDLE_EXCEPTION();
+ ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_UNSET_STATIC_PROP_SPEC_CONST_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zval tmp, *varname;
+ zend_class_entry *ce;
+
+
+ SAVE_OPLINE();
+
+ varname = EX_CONSTANT(opline->op1);
+
+ 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;
+ }
+
+ if (IS_UNUSED == IS_CONST) {
+ ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)));
+ if (UNEXPECTED(ce == NULL)) {
+ ce = zend_fetch_class_by_name(Z_STR_P(EX_CONSTANT(opline->op2)), EX_CONSTANT(opline->op2) + 1, ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION);
+ if (UNEXPECTED(ce == NULL)) {
+ if (EXPECTED(!EG(exception))) {
+ zend_throw_error(NULL, "Class '%s' not found", Z_STRVAL_P(EX_CONSTANT(opline->op2)));
}
- CACHE_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)), ce);
+ if (IS_CONST != IS_CONST && Z_TYPE(tmp) != IS_UNDEF) {
+ zend_string_release(Z_STR(tmp));
+ }
+
+ HANDLE_EXCEPTION();
}
- } else {
- ce = Z_CE_P(EX_VAR(opline->op2.var));
+ CACHE_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)), ce);
+ }
+ } else if (IS_UNUSED == IS_UNUSED) {
+ ce = zend_fetch_class(NULL, opline->op2.num);
+ if (UNEXPECTED(ce == NULL)) {
+ ZEND_ASSERT(EG(exception));
+ if (IS_CONST != IS_CONST && Z_TYPE(tmp) != IS_UNDEF) {
+ zend_string_release(Z_STR(tmp));
+ }
+
+ HANDLE_EXCEPTION();
}
- zend_std_unset_static_property(ce, Z_STR_P(varname));
} else {
- target_symbol_table = zend_get_target_symbol_table(execute_data, opline->extended_value & ZEND_FETCH_TYPE_MASK);
- zend_hash_del_ind(target_symbol_table, Z_STR_P(varname));
+ ce = Z_CE_P(EX_VAR(opline->op2.var));
}
+ zend_std_unset_static_property(ce, Z_STR_P(varname));
if (IS_CONST != IS_CONST && Z_TYPE(tmp) != IS_UNDEF) {
zend_string_release(Z_STR(tmp));
@@ -8009,7 +7761,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ISSET_ISEMPTY_VAR_SPEC_CONST_U
int result;
if (IS_CONST == IS_CV &&
- IS_UNUSED == IS_UNUSED &&
(opline->extended_value & ZEND_QUICK_SET)) {
value = EX_VAR(opline->op1.var);
if (opline->extended_value & ZEND_ISSET) {
@@ -8030,6 +7781,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ISSET_ISEMPTY_VAR_SPEC_CONST_U
} else {
zval tmp, *varname;
+ HashTable *target_symbol_table;
SAVE_OPLINE();
varname = EX_CONSTANT(opline->op1);
@@ -8039,55 +7791,13 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ISSET_ISEMPTY_VAR_SPEC_CONST_U
varname = &tmp;
}
- if (IS_UNUSED != IS_UNUSED) {
- zend_class_entry *ce;
-
- if (IS_UNUSED == IS_CONST) {
- if (IS_CONST == IS_CONST && EXPECTED((ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)))) != NULL)) {
- value = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)) + sizeof(void*));
-
- /* check if static properties were destoyed */
- if (UNEXPECTED(CE_STATIC_MEMBERS(ce) == NULL)) {
- value = NULL;
- }
-
- goto is_var_return;
- } else if (UNEXPECTED((ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)))) == NULL)) {
- ce = zend_fetch_class_by_name(Z_STR_P(EX_CONSTANT(opline->op2)), EX_CONSTANT(opline->op2) + 1, ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION);
- if (UNEXPECTED(ce == NULL)) {
- ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
- }
- CACHE_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)), ce);
- }
- } else {
- ce = Z_CE_P(EX_VAR(opline->op2.var));
- if (IS_CONST == IS_CONST &&
- (value = CACHED_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)), ce)) != NULL) {
-
- /* check if static properties were destoyed */
- if (UNEXPECTED(CE_STATIC_MEMBERS(ce) == NULL)) {
- value = NULL;
- }
-
- goto is_var_return;
- }
- }
-
- value = zend_std_get_static_property(ce, Z_STR_P(varname), 1);
-
- if (IS_CONST == IS_CONST && value) {
- CACHE_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)), ce, value);
- }
- } else {
- HashTable *target_symbol_table = zend_get_target_symbol_table(execute_data, opline->extended_value & ZEND_FETCH_TYPE_MASK);
- value = zend_hash_find_ind(target_symbol_table, Z_STR_P(varname));
- }
+ target_symbol_table = zend_get_target_symbol_table(execute_data, opline->extended_value & ZEND_FETCH_TYPE_MASK);
+ value = zend_hash_find_ind(target_symbol_table, Z_STR_P(varname));
if (IS_CONST != IS_CONST && Z_TYPE(tmp) != IS_UNDEF) {
zend_string_release(Z_STR(tmp));
}
-is_var_return:
if (opline->extended_value & ZEND_ISSET) {
result = value && Z_TYPE_P(value) > IS_NULL &&
(!Z_ISREF_P(value) || Z_TYPE_P(Z_REFVAL_P(value)) != IS_NULL);
@@ -8101,24 +7811,115 @@ is_var_return:
}
}
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ISSET_ISEMPTY_STATIC_PROP_SPEC_CONST_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zval *value;
+ int result;
+
+ zval tmp, *varname;
+ zend_class_entry *ce;
+
+ SAVE_OPLINE();
+ varname = EX_CONSTANT(opline->op1);
+ ZVAL_UNDEF(&tmp);
+ if (IS_CONST != IS_CONST && Z_TYPE_P(varname) != IS_STRING) {
+ ZVAL_STR(&tmp, zval_get_string(varname));
+ varname = &tmp;
+ }
+
+ if (IS_UNUSED == IS_CONST) {
+ if (IS_CONST == IS_CONST && EXPECTED((ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)))) != NULL)) {
+ value = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)) + sizeof(void*));
+
+ /* check if static properties were destoyed */
+ if (UNEXPECTED(CE_STATIC_MEMBERS(ce) == NULL)) {
+ value = NULL;
+ }
+
+ goto is_static_prop_return;
+ } else if (UNEXPECTED((ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)))) == NULL)) {
+ ce = zend_fetch_class_by_name(Z_STR_P(EX_CONSTANT(opline->op2)), EX_CONSTANT(opline->op2) + 1, ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION);
+ if (UNEXPECTED(ce == NULL)) {
+ ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
+ }
+ CACHE_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)), ce);
+ }
+ } else {
+ if (IS_UNUSED == IS_UNUSED) {
+ ce = zend_fetch_class(NULL, opline->op2.num);
+ if (UNEXPECTED(ce == NULL)) {
+ ZEND_ASSERT(EG(exception));
+ if (IS_CONST != IS_CONST && Z_TYPE(tmp) != IS_UNDEF) {
+ zend_string_release(Z_STR(tmp));
+ }
+
+ HANDLE_EXCEPTION();
+ }
+ } else {
+ ce = Z_CE_P(EX_VAR(opline->op2.var));
+ }
+ if (IS_CONST == IS_CONST &&
+ (value = CACHED_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)), ce)) != NULL) {
+
+ /* check if static properties were destoyed */
+ if (UNEXPECTED(CE_STATIC_MEMBERS(ce) == NULL)) {
+ value = NULL;
+ }
+
+ goto is_static_prop_return;
+ }
+ }
+
+ value = zend_std_get_static_property(ce, Z_STR_P(varname), 1);
+
+ if (IS_CONST == IS_CONST && value) {
+ CACHE_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)), ce, value);
+ }
+
+ if (IS_CONST != IS_CONST && Z_TYPE(tmp) != IS_UNDEF) {
+ zend_string_release(Z_STR(tmp));
+ }
+
+is_static_prop_return:
+ if (opline->extended_value & ZEND_ISSET) {
+ result = value && Z_TYPE_P(value) > IS_NULL &&
+ (!Z_ISREF_P(value) || Z_TYPE_P(Z_REFVAL_P(value)) != IS_NULL);
+ } else /* if (opline->extended_value & ZEND_ISEMPTY) */ {
+ result = !value || !i_zend_is_true(value);
+ }
+
+ ZEND_VM_SMART_BRANCH(result, 1);
+ ZVAL_BOOL(EX_VAR(opline->result.var), result);
+ ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
+}
+
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DECLARE_LAMBDA_FUNCTION_SPEC_CONST_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
zval *zfunc;
+ zval *object;
+ zend_class_entry *called_scope;
SAVE_OPLINE();
zfunc = zend_hash_find(EG(function_table), Z_STR_P(EX_CONSTANT(opline->op1)));
ZEND_ASSERT(zfunc != NULL && Z_FUNC_P(zfunc)->type == ZEND_USER_FUNCTION);
- if (UNEXPECTED((Z_FUNC_P(zfunc)->common.fn_flags & ZEND_ACC_STATIC) ||
+ if (Z_TYPE(EX(This)) == IS_OBJECT) {
+ called_scope = Z_OBJCE(EX(This));
+ if (UNEXPECTED((Z_FUNC_P(zfunc)->common.fn_flags & ZEND_ACC_STATIC) ||
(EX(func)->common.fn_flags & ZEND_ACC_STATIC))) {
- zend_create_closure(EX_VAR(opline->result.var), Z_FUNC_P(zfunc),
- EG(scope), EX(called_scope), NULL);
+ object = NULL;
+ } else {
+ object = &EX(This);
+ }
} else {
- zend_create_closure(EX_VAR(opline->result.var), Z_FUNC_P(zfunc),
- EG(scope), EX(called_scope), Z_OBJ(EX(This)) ? &EX(This) : NULL);
+ called_scope = Z_CE(EX(This));
+ object = NULL;
}
+ zend_create_closure(EX_VAR(opline->result.var), Z_FUNC_P(zfunc),
+ EX(func)->op_array.scope, called_scope, object);
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
@@ -8150,7 +7951,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_CONST_UNUSED_HANDLE
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) {
+ if (IS_CONST & (IS_CONST|IS_TMP_VAR)) {
zval *value;
zend_error(E_NOTICE, "Only variable references should be yielded by reference");
@@ -8158,25 +7959,19 @@ 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 (IS_CONST == IS_CONST) {
- if (UNEXPECTED(Z_OPT_COPYABLE(generator->value))) {
- zval_copy_ctor_func(&generator->value);
+ if (UNEXPECTED(Z_OPT_REFCOUNTED(generator->value))) {
+ Z_ADDREF(generator->value);
}
}
} else {
zval *value_ptr = NULL;
- if (IS_CONST == IS_VAR && UNEXPECTED(value_ptr == NULL)) {
- zend_throw_error(NULL, "Cannot yield string offsets by reference");
-
- HANDLE_EXCEPTION();
- }
-
/* If a function call result is yielded and the function did
* not return by reference we throw a notice. */
if (IS_CONST == IS_VAR &&
(value_ptr == &EG(uninitialized_zval) ||
(opline->extended_value == ZEND_RETURNS_FUNCTION &&
- !(Z_VAR_FLAGS_P(value_ptr) & IS_VAR_RET_REF)))) {
+ !Z_ISREF_P(value_ptr)))) {
zend_error(E_NOTICE, "Only variable references should be yielded by reference");
} else {
ZVAL_MAKE_REF(value_ptr);
@@ -8190,8 +7985,8 @@ 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_COPY_VALUE(&generator->value, value);
- if (UNEXPECTED(Z_OPT_COPYABLE(generator->value))) {
- zval_copy_ctor_func(&generator->value);
+ if (UNEXPECTED(Z_OPT_REFCOUNTED(generator->value))) {
+ Z_ADDREF(generator->value);
}
} else if (IS_CONST == IS_TMP_VAR) {
ZVAL_COPY_VALUE(&generator->value, value);
@@ -8218,8 +8013,8 @@ 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_COPY_VALUE(&generator->key, key);
- if (UNEXPECTED(Z_OPT_COPYABLE(generator->key))) {
- zval_copy_ctor_func(&generator->key);
+ if (UNEXPECTED(Z_OPT_REFCOUNTED(generator->key))) {
+ Z_ADDREF(generator->key);
}
} else if (IS_UNUSED == IS_TMP_VAR) {
ZVAL_COPY_VALUE(&generator->key, key);
@@ -8529,7 +8324,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CONCAT_SPEC_CONST_CV_HANDLER(Z
!ZSTR_IS_INTERNED(op1_str) && GC_REFCOUNT(op1_str) == 1) {
size_t len = ZSTR_LEN(op1_str);
- str = zend_string_realloc(op1_str, len + ZSTR_LEN(op2_str), 0);
+ str = zend_string_extend(op1_str, len + ZSTR_LEN(op2_str), 0);
memcpy(ZSTR_VAL(str) + len, ZSTR_VAL(op2_str), ZSTR_LEN(op2_str)+1);
ZVAL_NEW_STR(EX_VAR(opline->result.var), str);
break;
@@ -8905,11 +8700,33 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_DIM_R_SPEC_CONST_CV_HAND
{
USE_OPLINE
- zval *container;
+ zval *container, *dim, *value, *result;
SAVE_OPLINE();
container = EX_CONSTANT(opline->op1);
- zend_fetch_dimension_address_read_R(EX_VAR(opline->result.var), container, _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var), IS_CV);
+ dim = _get_zval_ptr_cv_undef(execute_data, opline->op2.var);
+ if (IS_CONST != IS_CONST) {
+ if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) {
+fetch_dim_r_array:
+ value = zend_fetch_dimension_address_inner(Z_ARRVAL_P(container), dim, IS_CV, BP_VAR_R);
+ result = EX_VAR(opline->result.var);
+ ZVAL_COPY(result, value);
+ } else if (EXPECTED(Z_TYPE_P(container) == IS_REFERENCE)) {
+ container = Z_REFVAL_P(container);
+ if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) {
+ goto fetch_dim_r_array;
+ } else {
+ goto fetch_dim_r_slow;
+ }
+ } else {
+fetch_dim_r_slow:
+ result = EX_VAR(opline->result.var);
+ zend_fetch_dimension_address_read_R_slow(result, container, dim);
+ }
+ } else {
+ result = EX_VAR(opline->result.var);
+ zend_fetch_dimension_address_read_R(result, container, dim, IS_CV);
+ }
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
@@ -8923,7 +8740,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_DIM_IS_SPEC_CONST_CV_HAN
SAVE_OPLINE();
container = EX_CONSTANT(opline->op1);
- zend_fetch_dimension_address_read_IS(EX_VAR(opline->result.var), container, _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var), IS_CV);
+ zend_fetch_dimension_address_read_IS(EX_VAR(opline->result.var), container, _get_zval_ptr_cv_undef(execute_data, opline->op2.var), IS_CV);
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
@@ -8938,21 +8755,16 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_DIM_FUNC_ARG_SPEC_CONST_
SAVE_OPLINE();
if (zend_is_by_ref_func_arg_fetch(opline, EX(call))) {
- if (IS_CONST == IS_CONST || IS_CONST == IS_TMP_VAR) {
+ if ((IS_CONST & (IS_CONST|IS_TMP_VAR))) {
zend_throw_error(NULL, "Cannot use temporary expression in write context");
HANDLE_EXCEPTION();
}
container = NULL;
- if (IS_CONST == IS_VAR && UNEXPECTED(container == NULL)) {
- zend_throw_error(NULL, "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);
+ zend_fetch_dimension_address_W(EX_VAR(opline->result.var), container, _get_zval_ptr_cv_undef(execute_data, opline->op2.var), IS_CV);
if (IS_CONST == IS_VAR && READY_TO_DESTROY(free_op1)) {
- EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var), 1);
+ EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var));
}
@@ -8964,7 +8776,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_DIM_FUNC_ARG_SPEC_CONST_
HANDLE_EXCEPTION();
}
container = EX_CONSTANT(opline->op1);
- zend_fetch_dimension_address_read_R(EX_VAR(opline->result.var), container, _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var), IS_CV);
+ zend_fetch_dimension_address_read_R(EX_VAR(opline->result.var), container, _get_zval_ptr_cv_undef(execute_data, opline->op2.var), IS_CV);
}
@@ -8982,7 +8794,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_R_SPEC_CONST_CV_HAND
SAVE_OPLINE();
container = EX_CONSTANT(opline->op1);
- if (IS_CONST == IS_UNUSED && UNEXPECTED(Z_OBJ_P(container) == NULL)) {
+ if (IS_CONST == IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
HANDLE_EXCEPTION();
@@ -9054,7 +8866,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_IS_SPEC_CONST_CV_HAN
SAVE_OPLINE();
container = EX_CONSTANT(opline->op1);
- if (IS_CONST == IS_UNUSED && UNEXPECTED(Z_OBJ_P(container) == NULL)) {
+ if (IS_CONST == IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
HANDLE_EXCEPTION();
@@ -9129,26 +8941,21 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_FUNC_ARG_SPEC_CONST_
property = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var);
container = NULL;
- if (IS_CONST == IS_UNUSED && UNEXPECTED(Z_OBJ_P(container) == NULL)) {
+ if (IS_CONST == IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
HANDLE_EXCEPTION();
}
- if (IS_CONST == IS_CONST || IS_CONST == IS_TMP_VAR) {
+ if ((IS_CONST & (IS_CONST|IS_TMP_VAR))) {
zend_throw_error(NULL, "Cannot use temporary expression in write context");
HANDLE_EXCEPTION();
}
- if (IS_CONST == IS_VAR && UNEXPECTED(container == NULL)) {
- zend_throw_error(NULL, "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);
if (IS_CONST == IS_VAR && READY_TO_DESTROY(free_op1)) {
- EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var), 0);
+ EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var));
}
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
@@ -9157,6 +8964,19 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_FUNC_ARG_SPEC_CONST_
}
}
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_LIST_SPEC_CONST_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+
+ zval *container;
+
+ SAVE_OPLINE();
+ container = EX_CONSTANT(opline->op1);
+ zend_fetch_dimension_address_read_LIST(EX_VAR(opline->result.var), container, _get_zval_ptr_cv_undef(execute_data, opline->op2.var));
+
+ ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
+}
+
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FAST_CONCAT_SPEC_CONST_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
@@ -9263,7 +9083,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_SPEC_CONST_CV
object = EX_CONSTANT(opline->op1);
- if (IS_CONST == IS_UNUSED && UNEXPECTED(Z_OBJ_P(object) == NULL)) {
+ if (IS_CONST == IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
HANDLE_EXCEPTION();
@@ -9323,6 +9143,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_SPEC_CONST_CV
EXPECTED(obj == orig_obj)) {
CACHE_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(function_name), called_scope, fbc);
}
+ if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!fbc->op_array.run_time_cache)) {
+ init_func_run_time_cache(&fbc->op_array);
+ }
}
call_info = ZEND_CALL_NESTED_FUNCTION;
@@ -9368,6 +9191,13 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_STATIC_METHOD_CALL_SPEC_C
}
CACHE_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)), ce);
}
+ } else if (IS_CONST == IS_UNUSED) {
+ ce = zend_fetch_class(NULL, opline->op1.num);
+ if (UNEXPECTED(ce == NULL)) {
+ ZEND_ASSERT(EG(exception));
+
+ HANDLE_EXCEPTION();
+ }
} else {
ce = Z_CE_P(EX_VAR(opline->op1.var));
}
@@ -9426,6 +9256,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_STATIC_METHOD_CALL_SPEC_C
CACHE_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(function_name), ce, fbc);
}
}
+ if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!fbc->op_array.run_time_cache)) {
+ init_func_run_time_cache(&fbc->op_array);
+ }
if (IS_CV != IS_CONST) {
}
@@ -9434,16 +9267,19 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_STATIC_METHOD_CALL_SPEC_C
zend_throw_error(NULL, "Cannot call constructor");
HANDLE_EXCEPTION();
}
- if (Z_OBJ(EX(This)) && Z_OBJ(EX(This))->ce != ce->constructor->common.scope && (ce->constructor->common.fn_flags & ZEND_ACC_PRIVATE)) {
+ if (Z_TYPE(EX(This)) == IS_OBJECT && Z_OBJ(EX(This))->ce != ce->constructor->common.scope && (ce->constructor->common.fn_flags & ZEND_ACC_PRIVATE)) {
zend_throw_error(NULL, "Cannot call private %s::__construct()", ZSTR_VAL(ce->name));
HANDLE_EXCEPTION();
}
fbc = ce->constructor;
+ if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!fbc->op_array.run_time_cache)) {
+ init_func_run_time_cache(&fbc->op_array);
+ }
}
object = NULL;
if (!(fbc->common.fn_flags & ZEND_ACC_STATIC)) {
- if (Z_OBJ(EX(This)) && instanceof_function(Z_OBJCE(EX(This)), ce)) {
+ if (Z_TYPE(EX(This)) == IS_OBJECT && instanceof_function(Z_OBJCE(EX(This)), ce)) {
object = Z_OBJ(EX(This));
ce = object->ce;
} else {
@@ -9468,11 +9304,15 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_STATIC_METHOD_CALL_SPEC_C
}
}
- if (IS_CONST != IS_CONST) {
+ if (IS_CONST == IS_UNUSED) {
/* previous opcode is ZEND_FETCH_CLASS */
- if (((opline-1)->extended_value & ZEND_FETCH_CLASS_MASK) == ZEND_FETCH_CLASS_PARENT ||
- ((opline-1)->extended_value & ZEND_FETCH_CLASS_MASK) == ZEND_FETCH_CLASS_SELF) {
- ce = EX(called_scope);
+ if ((opline->op1.num & ZEND_FETCH_CLASS_MASK) == ZEND_FETCH_CLASS_PARENT ||
+ (opline->op1.num & ZEND_FETCH_CLASS_MASK) == ZEND_FETCH_CLASS_SELF) {
+ if (Z_TYPE(EX(This)) == IS_OBJECT) {
+ ce = Z_OBJCE(EX(This));
+ } else {
+ ce = Z_CE(EX(This));
+ }
}
}
@@ -9495,12 +9335,14 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_USER_CALL_SPEC_CONST_CV_H
zend_class_entry *called_scope;
zend_object *object;
zend_execute_data *call;
- uint32_t call_info = ZEND_CALL_NESTED_FUNCTION;
+ uint32_t call_info = ZEND_CALL_NESTED_FUNCTION | ZEND_CALL_DYNAMIC;
SAVE_OPLINE();
function_name = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var);
if (zend_is_callable_ex(function_name, NULL, 0, NULL, &fcc, &error)) {
func = fcc.function_handler;
+ called_scope = fcc.called_scope;
+ object = fcc.object;
if (func->common.fn_flags & ZEND_ACC_CLOSURE) {
/* Delay closure destruction until its invocation */
if (IS_CV & (IS_VAR|IS_CV)) {
@@ -9509,10 +9351,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_USER_CALL_SPEC_CONST_CV_H
ZEND_ASSERT(GC_TYPE((zend_object*)func->common.prototype) == IS_OBJECT);
GC_REFCOUNT((zend_object*)func->common.prototype)++;
call_info |= ZEND_CALL_CLOSURE;
- }
- called_scope = fcc.called_scope;
- object = fcc.object;
- if (object) {
+ } else if (object) {
call_info |= ZEND_CALL_RELEASE_THIS;
GC_REFCOUNT(object)++; /* For $this pointer */
}
@@ -9526,6 +9365,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_USER_CALL_SPEC_CONST_CV_H
HANDLE_EXCEPTION();
}
}
+ if (EXPECTED(func->type == ZEND_USER_FUNCTION) && UNEXPECTED(!func->op_array.run_time_cache)) {
+ init_func_run_time_cache(&func->op_array);
+ }
} else {
zend_internal_type_error(EX_USES_STRICT_TYPES(), "%s() expects parameter 1 to be a valid callback, %s", Z_STRVAL_P(EX_CONSTANT(opline->op1)), error);
efree(error);
@@ -9552,8 +9394,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CATCH_SPEC_CONST_CV_HANDLER(ZE
/* Check whether an exception has been thrown, if not, jump over code */
zend_exception_restore();
if (EG(exception) == NULL) {
- ZEND_VM_SET_OPCODE(&EX(func)->op_array.opcodes[opline->extended_value]);
- ZEND_VM_CONTINUE(); /* CHECK_ME */
+ ZEND_VM_SET_RELATIVE_OPCODE(opline, opline->extended_value);
+ ZEND_VM_CONTINUE();
}
catch_ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)));
if (UNEXPECTED(catch_ce == NULL)) {
@@ -9575,8 +9417,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CATCH_SPEC_CONST_CV_HANDLER(ZE
zend_throw_exception_internal(NULL);
HANDLE_EXCEPTION();
}
- ZEND_VM_SET_OPCODE(&EX(func)->op_array.opcodes[opline->extended_value]);
- ZEND_VM_CONTINUE(); /* CHECK_ME */
+ ZEND_VM_SET_RELATIVE_OPCODE(opline, opline->extended_value);
+ ZEND_VM_CONTINUE();
}
}
@@ -9676,11 +9518,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ADD_ARRAY_ELEMENT_SPEC_CONST_C
if ((IS_CONST == IS_VAR || IS_CONST == IS_CV) &&
UNEXPECTED(opline->extended_value & ZEND_ARRAY_ELEMENT_REF)) {
expr_ptr = NULL;
- if (IS_CONST == IS_VAR && UNEXPECTED(expr_ptr == NULL)) {
- zend_throw_error(NULL, "Cannot create references to/from string offsets");
- zend_array_destroy(Z_ARRVAL_P(EX_VAR(opline->result.var)));
- HANDLE_EXCEPTION();
- }
ZVAL_MAKE_REF(expr_ptr);
Z_ADDREF_P(expr_ptr);
@@ -9689,10 +9526,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ADD_ARRAY_ELEMENT_SPEC_CONST_C
if (IS_CONST == IS_TMP_VAR) {
/* pass */
} else if (IS_CONST == IS_CONST) {
- 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;
+ if (Z_REFCOUNTED_P(expr_ptr)) {
+ Z_ADDREF_P(expr_ptr);
}
} else if (IS_CONST == IS_CV) {
ZVAL_DEREF(expr_ptr);
@@ -9807,16 +9642,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ISSET_ISEMPTY_DIM_OBJ_SPEC_CON
SAVE_OPLINE();
container = EX_CONSTANT(opline->op1);
-
- if (IS_CONST == IS_UNUSED && UNEXPECTED(Z_OBJ_P(container) == NULL)) {
- zend_throw_error(NULL, "Using $this when not in object context");
-
- HANDLE_EXCEPTION();
- }
-
offset = _get_zval_ptr_cv_undef(execute_data, opline->op2.var);
- if (IS_CONST != IS_UNUSED && EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) {
+ if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) {
HashTable *ht;
zval *value;
zend_string *str;
@@ -9883,8 +9711,7 @@ num_index_prop:
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 ((IS_CONST != IS_CONST && EXPECTED(Z_TYPE_P(container) == IS_OBJECT))) {
if (EXPECTED(Z_OBJ_HT_P(container)->has_dimension)) {
result =
((opline->extended_value & ZEND_ISSET) == 0) ^
@@ -9899,6 +9726,9 @@ num_index_prop:
if (EXPECTED(Z_TYPE_P(offset) == IS_LONG)) {
lval = Z_LVAL_P(offset);
isset_str_offset:
+ if (UNEXPECTED(lval < 0)) { /* Handle negative offset */
+ lval += (zend_long)Z_STRLEN_P(container);
+ }
if (EXPECTED(lval >= 0) && (size_t)lval < Z_STRLEN_P(container)) {
if (opline->extended_value & ZEND_ISSET) {
result = 1;
@@ -9944,7 +9774,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ISSET_ISEMPTY_PROP_OBJ_SPEC_CO
SAVE_OPLINE();
container = EX_CONSTANT(opline->op1);
- if (IS_CONST == IS_UNUSED && UNEXPECTED(Z_OBJ_P(container) == NULL)) {
+ if (IS_CONST == IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
HANDLE_EXCEPTION();
@@ -10006,7 +9836,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_CONST_CV_HANDLER(ZE
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) {
+ if (IS_CONST & (IS_CONST|IS_TMP_VAR)) {
zval *value;
zend_error(E_NOTICE, "Only variable references should be yielded by reference");
@@ -10014,25 +9844,19 @@ 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 (IS_CONST == IS_CONST) {
- if (UNEXPECTED(Z_OPT_COPYABLE(generator->value))) {
- zval_copy_ctor_func(&generator->value);
+ if (UNEXPECTED(Z_OPT_REFCOUNTED(generator->value))) {
+ Z_ADDREF(generator->value);
}
}
} else {
zval *value_ptr = NULL;
- if (IS_CONST == IS_VAR && UNEXPECTED(value_ptr == NULL)) {
- zend_throw_error(NULL, "Cannot yield string offsets by reference");
-
- HANDLE_EXCEPTION();
- }
-
/* If a function call result is yielded and the function did
* not return by reference we throw a notice. */
if (IS_CONST == IS_VAR &&
(value_ptr == &EG(uninitialized_zval) ||
(opline->extended_value == ZEND_RETURNS_FUNCTION &&
- !(Z_VAR_FLAGS_P(value_ptr) & IS_VAR_RET_REF)))) {
+ !Z_ISREF_P(value_ptr)))) {
zend_error(E_NOTICE, "Only variable references should be yielded by reference");
} else {
ZVAL_MAKE_REF(value_ptr);
@@ -10046,8 +9870,8 @@ 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_COPY_VALUE(&generator->value, value);
- if (UNEXPECTED(Z_OPT_COPYABLE(generator->value))) {
- zval_copy_ctor_func(&generator->value);
+ if (UNEXPECTED(Z_OPT_REFCOUNTED(generator->value))) {
+ Z_ADDREF(generator->value);
}
} else if (IS_CONST == IS_TMP_VAR) {
ZVAL_COPY_VALUE(&generator->value, value);
@@ -10074,8 +9898,8 @@ 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_COPY_VALUE(&generator->key, key);
- if (UNEXPECTED(Z_OPT_COPYABLE(generator->key))) {
- zval_copy_ctor_func(&generator->key);
+ if (UNEXPECTED(Z_OPT_REFCOUNTED(generator->key))) {
+ Z_ADDREF(generator->key);
}
} else if (IS_CV == IS_TMP_VAR) {
ZVAL_COPY_VALUE(&generator->key, key);
@@ -10120,6 +9944,54 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_CONST_CV_HANDLER(ZE
ZEND_VM_RETURN();
}
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_DIM_R_INDEX_SPEC_CONST_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+
+ zval *container, *dim, *value;
+ zend_long offset;
+
+ container = EX_CONSTANT(opline->op1);
+ dim = _get_zval_ptr_cv_undef(execute_data, opline->op2.var);
+ if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) {
+fetch_dim_r_index_array:
+ if (EXPECTED(Z_TYPE_P(dim) == IS_LONG)) {
+ offset = Z_LVAL_P(dim);
+ } else {
+ offset = zval_get_long(dim);
+ }
+ ZEND_HASH_INDEX_FIND(Z_ARRVAL_P(container), offset, value, fetch_dim_r_index_undef);
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ if (IS_CONST & (IS_TMP_VAR|IS_VAR)) {
+ SAVE_OPLINE();
+
+ ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
+ } else {
+ ZEND_VM_NEXT_OPCODE();
+ }
+ } else if (IS_CONST != IS_CONST && EXPECTED(Z_TYPE_P(container) == IS_REFERENCE)) {
+ container = Z_REFVAL_P(container);
+ if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) {
+ goto fetch_dim_r_index_array;
+ } else {
+ goto fetch_dim_r_index_slow;
+ }
+ } else {
+fetch_dim_r_index_slow:
+ SAVE_OPLINE();
+ zend_fetch_dimension_address_read_R_slow(EX_VAR(opline->result.var), container, dim);
+
+ ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
+ }
+
+fetch_dim_r_index_undef:
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ SAVE_OPLINE();
+ zend_error(E_NOTICE, "Undefined offset: " ZEND_LONG_FMT, offset);
+
+ ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
+}
+
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ADD_SPEC_CONST_TMPVAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
@@ -10385,7 +10257,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CONCAT_SPEC_CONST_TMPVAR_HANDL
!ZSTR_IS_INTERNED(op1_str) && GC_REFCOUNT(op1_str) == 1) {
size_t len = ZSTR_LEN(op1_str);
- str = zend_string_realloc(op1_str, len + ZSTR_LEN(op2_str), 0);
+ str = zend_string_extend(op1_str, len + ZSTR_LEN(op2_str), 0);
memcpy(ZSTR_VAL(str) + len, ZSTR_VAL(op2_str), ZSTR_LEN(op2_str)+1);
ZVAL_NEW_STR(EX_VAR(opline->result.var), str);
break;
@@ -10725,11 +10597,33 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_DIM_R_SPEC_CONST_TMPVAR_
{
USE_OPLINE
zend_free_op free_op2;
- zval *container;
+ zval *container, *dim, *value, *result;
SAVE_OPLINE();
container = EX_CONSTANT(opline->op1);
- zend_fetch_dimension_address_read_R(EX_VAR(opline->result.var), container, _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2), (IS_TMP_VAR|IS_VAR));
+ dim = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2);
+ if (IS_CONST != IS_CONST) {
+ if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) {
+fetch_dim_r_array:
+ value = zend_fetch_dimension_address_inner(Z_ARRVAL_P(container), dim, (IS_TMP_VAR|IS_VAR), BP_VAR_R);
+ result = EX_VAR(opline->result.var);
+ ZVAL_COPY(result, value);
+ } else if (EXPECTED(Z_TYPE_P(container) == IS_REFERENCE)) {
+ container = Z_REFVAL_P(container);
+ if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) {
+ goto fetch_dim_r_array;
+ } else {
+ goto fetch_dim_r_slow;
+ }
+ } else {
+fetch_dim_r_slow:
+ result = EX_VAR(opline->result.var);
+ zend_fetch_dimension_address_read_R_slow(result, container, dim);
+ }
+ } else {
+ result = EX_VAR(opline->result.var);
+ zend_fetch_dimension_address_read_R(result, container, dim, (IS_TMP_VAR|IS_VAR));
+ }
zval_ptr_dtor_nogc(free_op2);
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
@@ -10758,21 +10652,16 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_DIM_FUNC_ARG_SPEC_CONST_
SAVE_OPLINE();
if (zend_is_by_ref_func_arg_fetch(opline, EX(call))) {
- if (IS_CONST == IS_CONST || IS_CONST == IS_TMP_VAR) {
+ if ((IS_CONST & (IS_CONST|IS_TMP_VAR))) {
zend_throw_error(NULL, "Cannot use temporary expression in write context");
zval_ptr_dtor_nogc(EX_VAR(opline->op2.var));
HANDLE_EXCEPTION();
}
container = NULL;
- if (IS_CONST == IS_VAR && UNEXPECTED(container == NULL)) {
- zend_throw_error(NULL, "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));
if (IS_CONST == IS_VAR && READY_TO_DESTROY(free_op1)) {
- EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var), 1);
+ EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var));
}
zval_ptr_dtor_nogc(free_op2);
@@ -10802,7 +10691,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_R_SPEC_CONST_TMPVAR_
SAVE_OPLINE();
container = EX_CONSTANT(opline->op1);
- if (IS_CONST == IS_UNUSED && UNEXPECTED(Z_OBJ_P(container) == NULL)) {
+ if (IS_CONST == IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
zval_ptr_dtor_nogc(EX_VAR(opline->op2.var));
HANDLE_EXCEPTION();
@@ -10875,7 +10764,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_IS_SPEC_CONST_TMPVAR
SAVE_OPLINE();
container = EX_CONSTANT(opline->op1);
- if (IS_CONST == IS_UNUSED && UNEXPECTED(Z_OBJ_P(container) == NULL)) {
+ if (IS_CONST == IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
zval_ptr_dtor_nogc(EX_VAR(opline->op2.var));
HANDLE_EXCEPTION();
@@ -10951,26 +10840,21 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_FUNC_ARG_SPEC_CONST_
property = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2);
container = NULL;
- if (IS_CONST == IS_UNUSED && UNEXPECTED(Z_OBJ_P(container) == NULL)) {
+ if (IS_CONST == IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
zval_ptr_dtor_nogc(free_op2);
HANDLE_EXCEPTION();
}
- if (IS_CONST == IS_CONST || IS_CONST == IS_TMP_VAR) {
+ if ((IS_CONST & (IS_CONST|IS_TMP_VAR))) {
zend_throw_error(NULL, "Cannot use temporary expression in write context");
zval_ptr_dtor_nogc(free_op2);
HANDLE_EXCEPTION();
}
- if (IS_CONST == IS_VAR && UNEXPECTED(container == NULL)) {
- zend_throw_error(NULL, "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);
zval_ptr_dtor_nogc(free_op2);
if (IS_CONST == IS_VAR && READY_TO_DESTROY(free_op1)) {
- EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var), 0);
+ EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var));
}
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
@@ -10979,6 +10863,19 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_FUNC_ARG_SPEC_CONST_
}
}
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_LIST_SPEC_CONST_TMPVAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zend_free_op free_op2;
+ zval *container;
+
+ SAVE_OPLINE();
+ container = EX_CONSTANT(opline->op1);
+ zend_fetch_dimension_address_read_LIST(EX_VAR(opline->result.var), container, _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2));
+ zval_ptr_dtor_nogc(free_op2);
+ ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
+}
+
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FAST_CONCAT_SPEC_CONST_TMPVAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
@@ -11085,7 +10982,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_SPEC_CONST_TM
object = EX_CONSTANT(opline->op1);
- if (IS_CONST == IS_UNUSED && UNEXPECTED(Z_OBJ_P(object) == NULL)) {
+ if (IS_CONST == IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
zval_ptr_dtor_nogc(free_op2);
HANDLE_EXCEPTION();
@@ -11145,6 +11042,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_SPEC_CONST_TM
EXPECTED(obj == orig_obj)) {
CACHE_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(function_name), called_scope, fbc);
}
+ if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!fbc->op_array.run_time_cache)) {
+ init_func_run_time_cache(&fbc->op_array);
+ }
}
call_info = ZEND_CALL_NESTED_FUNCTION;
@@ -11191,6 +11091,13 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_STATIC_METHOD_CALL_SPEC_C
}
CACHE_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)), ce);
}
+ } else if (IS_CONST == IS_UNUSED) {
+ ce = zend_fetch_class(NULL, opline->op1.num);
+ if (UNEXPECTED(ce == NULL)) {
+ ZEND_ASSERT(EG(exception));
+ zval_ptr_dtor_nogc(EX_VAR(opline->op2.var));
+ HANDLE_EXCEPTION();
+ }
} else {
ce = Z_CE_P(EX_VAR(opline->op1.var));
}
@@ -11249,6 +11156,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_STATIC_METHOD_CALL_SPEC_C
CACHE_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(function_name), ce, fbc);
}
}
+ if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!fbc->op_array.run_time_cache)) {
+ init_func_run_time_cache(&fbc->op_array);
+ }
if ((IS_TMP_VAR|IS_VAR) != IS_CONST) {
zval_ptr_dtor_nogc(free_op2);
}
@@ -11257,16 +11167,19 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_STATIC_METHOD_CALL_SPEC_C
zend_throw_error(NULL, "Cannot call constructor");
HANDLE_EXCEPTION();
}
- if (Z_OBJ(EX(This)) && Z_OBJ(EX(This))->ce != ce->constructor->common.scope && (ce->constructor->common.fn_flags & ZEND_ACC_PRIVATE)) {
+ if (Z_TYPE(EX(This)) == IS_OBJECT && Z_OBJ(EX(This))->ce != ce->constructor->common.scope && (ce->constructor->common.fn_flags & ZEND_ACC_PRIVATE)) {
zend_throw_error(NULL, "Cannot call private %s::__construct()", ZSTR_VAL(ce->name));
HANDLE_EXCEPTION();
}
fbc = ce->constructor;
+ if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!fbc->op_array.run_time_cache)) {
+ init_func_run_time_cache(&fbc->op_array);
+ }
}
object = NULL;
if (!(fbc->common.fn_flags & ZEND_ACC_STATIC)) {
- if (Z_OBJ(EX(This)) && instanceof_function(Z_OBJCE(EX(This)), ce)) {
+ if (Z_TYPE(EX(This)) == IS_OBJECT && instanceof_function(Z_OBJCE(EX(This)), ce)) {
object = Z_OBJ(EX(This));
ce = object->ce;
} else {
@@ -11291,11 +11204,15 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_STATIC_METHOD_CALL_SPEC_C
}
}
- if (IS_CONST != IS_CONST) {
+ if (IS_CONST == IS_UNUSED) {
/* previous opcode is ZEND_FETCH_CLASS */
- if (((opline-1)->extended_value & ZEND_FETCH_CLASS_MASK) == ZEND_FETCH_CLASS_PARENT ||
- ((opline-1)->extended_value & ZEND_FETCH_CLASS_MASK) == ZEND_FETCH_CLASS_SELF) {
- ce = EX(called_scope);
+ if ((opline->op1.num & ZEND_FETCH_CLASS_MASK) == ZEND_FETCH_CLASS_PARENT ||
+ (opline->op1.num & ZEND_FETCH_CLASS_MASK) == ZEND_FETCH_CLASS_SELF) {
+ if (Z_TYPE(EX(This)) == IS_OBJECT) {
+ ce = Z_OBJCE(EX(This));
+ } else {
+ ce = Z_CE(EX(This));
+ }
}
}
@@ -11318,12 +11235,14 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_USER_CALL_SPEC_CONST_TMPV
zend_class_entry *called_scope;
zend_object *object;
zend_execute_data *call;
- uint32_t call_info = ZEND_CALL_NESTED_FUNCTION;
+ uint32_t call_info = ZEND_CALL_NESTED_FUNCTION | ZEND_CALL_DYNAMIC;
SAVE_OPLINE();
function_name = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2);
if (zend_is_callable_ex(function_name, NULL, 0, NULL, &fcc, &error)) {
func = fcc.function_handler;
+ called_scope = fcc.called_scope;
+ object = fcc.object;
if (func->common.fn_flags & ZEND_ACC_CLOSURE) {
/* Delay closure destruction until its invocation */
if ((IS_TMP_VAR|IS_VAR) & (IS_VAR|IS_CV)) {
@@ -11332,10 +11251,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_USER_CALL_SPEC_CONST_TMPV
ZEND_ASSERT(GC_TYPE((zend_object*)func->common.prototype) == IS_OBJECT);
GC_REFCOUNT((zend_object*)func->common.prototype)++;
call_info |= ZEND_CALL_CLOSURE;
- }
- called_scope = fcc.called_scope;
- object = fcc.object;
- if (object) {
+ } else if (object) {
call_info |= ZEND_CALL_RELEASE_THIS;
GC_REFCOUNT(object)++; /* For $this pointer */
}
@@ -11349,6 +11265,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_USER_CALL_SPEC_CONST_TMPV
HANDLE_EXCEPTION();
}
}
+ if (EXPECTED(func->type == ZEND_USER_FUNCTION) && UNEXPECTED(!func->op_array.run_time_cache)) {
+ init_func_run_time_cache(&func->op_array);
+ }
} else {
zend_internal_type_error(EX_USES_STRICT_TYPES(), "%s() expects parameter 1 to be a valid callback, %s", Z_STRVAL_P(EX_CONSTANT(opline->op1)), error);
efree(error);
@@ -11450,11 +11369,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ADD_ARRAY_ELEMENT_SPEC_CONST_T
if ((IS_CONST == IS_VAR || IS_CONST == IS_CV) &&
UNEXPECTED(opline->extended_value & ZEND_ARRAY_ELEMENT_REF)) {
expr_ptr = NULL;
- if (IS_CONST == IS_VAR && UNEXPECTED(expr_ptr == NULL)) {
- zend_throw_error(NULL, "Cannot create references to/from string offsets");
- zend_array_destroy(Z_ARRVAL_P(EX_VAR(opline->result.var)));
- HANDLE_EXCEPTION();
- }
ZVAL_MAKE_REF(expr_ptr);
Z_ADDREF_P(expr_ptr);
@@ -11463,10 +11377,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ADD_ARRAY_ELEMENT_SPEC_CONST_T
if (IS_CONST == IS_TMP_VAR) {
/* pass */
} else if (IS_CONST == IS_CONST) {
- 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;
+ if (Z_REFCOUNTED_P(expr_ptr)) {
+ Z_ADDREF_P(expr_ptr);
}
} else if (IS_CONST == IS_CV) {
ZVAL_DEREF(expr_ptr);
@@ -11581,16 +11493,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ISSET_ISEMPTY_DIM_OBJ_SPEC_CON
SAVE_OPLINE();
container = EX_CONSTANT(opline->op1);
-
- if (IS_CONST == IS_UNUSED && UNEXPECTED(Z_OBJ_P(container) == NULL)) {
- zend_throw_error(NULL, "Using $this when not in object context");
- zval_ptr_dtor_nogc(EX_VAR(opline->op2.var));
- HANDLE_EXCEPTION();
- }
-
offset = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2);
- if (IS_CONST != IS_UNUSED && EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) {
+ if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) {
HashTable *ht;
zval *value;
zend_string *str;
@@ -11657,8 +11562,7 @@ num_index_prop:
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 ((IS_CONST != IS_CONST && EXPECTED(Z_TYPE_P(container) == IS_OBJECT))) {
if (EXPECTED(Z_OBJ_HT_P(container)->has_dimension)) {
result =
((opline->extended_value & ZEND_ISSET) == 0) ^
@@ -11673,6 +11577,9 @@ num_index_prop:
if (EXPECTED(Z_TYPE_P(offset) == IS_LONG)) {
lval = Z_LVAL_P(offset);
isset_str_offset:
+ if (UNEXPECTED(lval < 0)) { /* Handle negative offset */
+ lval += (zend_long)Z_STRLEN_P(container);
+ }
if (EXPECTED(lval >= 0) && (size_t)lval < Z_STRLEN_P(container)) {
if (opline->extended_value & ZEND_ISSET) {
result = 1;
@@ -11718,7 +11625,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ISSET_ISEMPTY_PROP_OBJ_SPEC_CO
SAVE_OPLINE();
container = EX_CONSTANT(opline->op1);
- if (IS_CONST == IS_UNUSED && UNEXPECTED(Z_OBJ_P(container) == NULL)) {
+ if (IS_CONST == IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
zval_ptr_dtor_nogc(EX_VAR(opline->op2.var));
HANDLE_EXCEPTION();
@@ -11754,50 +11661,559 @@ isset_no_object:
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_DIM_R_INDEX_SPEC_CONST_TMPVAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zend_free_op free_op2;
+ zval *container, *dim, *value;
+ zend_long offset;
+
+ container = EX_CONSTANT(opline->op1);
+ dim = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2);
+ if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) {
+fetch_dim_r_index_array:
+ if (EXPECTED(Z_TYPE_P(dim) == IS_LONG)) {
+ offset = Z_LVAL_P(dim);
+ } else {
+ offset = zval_get_long(dim);
+ }
+ ZEND_HASH_INDEX_FIND(Z_ARRVAL_P(container), offset, value, fetch_dim_r_index_undef);
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ if (IS_CONST & (IS_TMP_VAR|IS_VAR)) {
+ SAVE_OPLINE();
+
+ ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
+ } else {
+ ZEND_VM_NEXT_OPCODE();
+ }
+ } else if (IS_CONST != IS_CONST && EXPECTED(Z_TYPE_P(container) == IS_REFERENCE)) {
+ container = Z_REFVAL_P(container);
+ if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) {
+ goto fetch_dim_r_index_array;
+ } else {
+ goto fetch_dim_r_index_slow;
+ }
+ } else {
+fetch_dim_r_index_slow:
+ SAVE_OPLINE();
+ zend_fetch_dimension_address_read_R_slow(EX_VAR(opline->result.var), container, dim);
+
+ ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
+ }
+
+fetch_dim_r_index_undef:
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ SAVE_OPLINE();
+ zend_error(E_NOTICE, "Undefined offset: " ZEND_LONG_FMT, offset);
+
+ ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ADD_LONG_NO_OVERFLOW_SPEC_CONST_TMPVARCV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zval *op1, *op2, *result;
+
+ op1 = EX_CONSTANT(opline->op1);
+ op2 = EX_VAR(opline->op2.var);
+ result = EX_VAR(opline->result.var);
+ ZVAL_LONG(result, Z_LVAL_P(op1) + Z_LVAL_P(op2));
+ ZEND_VM_NEXT_OPCODE();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ADD_LONG_SPEC_CONST_TMPVARCV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zval *op1, *op2, *result;
+
+ op1 = EX_CONSTANT(opline->op1);
+ op2 = EX_VAR(opline->op2.var);
+ result = EX_VAR(opline->result.var);
+ fast_long_add_function(result, op1, op2);
+ ZEND_VM_NEXT_OPCODE();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ADD_DOUBLE_SPEC_CONST_TMPVARCV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zval *op1, *op2, *result;
+
+ op1 = EX_CONSTANT(opline->op1);
+ op2 = EX_VAR(opline->op2.var);
+ result = EX_VAR(opline->result.var);
+ ZVAL_DOUBLE(result, Z_DVAL_P(op1) + Z_DVAL_P(op2));
+ ZEND_VM_NEXT_OPCODE();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SUB_LONG_NO_OVERFLOW_SPEC_CONST_TMPVARCV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zval *op1, *op2, *result;
+
+ op1 = EX_CONSTANT(opline->op1);
+ op2 = EX_VAR(opline->op2.var);
+ result = EX_VAR(opline->result.var);
+ ZVAL_LONG(result, Z_LVAL_P(op1) - Z_LVAL_P(op2));
+ ZEND_VM_NEXT_OPCODE();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SUB_LONG_SPEC_CONST_TMPVARCV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zval *op1, *op2, *result;
+
+ op1 = EX_CONSTANT(opline->op1);
+ op2 = EX_VAR(opline->op2.var);
+ result = EX_VAR(opline->result.var);
+ fast_long_sub_function(result, op1, op2);
+ ZEND_VM_NEXT_OPCODE();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SUB_DOUBLE_SPEC_CONST_TMPVARCV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zval *op1, *op2, *result;
+
+ op1 = EX_CONSTANT(opline->op1);
+ op2 = EX_VAR(opline->op2.var);
+ result = EX_VAR(opline->result.var);
+ ZVAL_DOUBLE(result, Z_DVAL_P(op1) - Z_DVAL_P(op2));
+ ZEND_VM_NEXT_OPCODE();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_MUL_LONG_NO_OVERFLOW_SPEC_CONST_TMPVARCV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zval *op1, *op2, *result;
+
+ op1 = EX_CONSTANT(opline->op1);
+ op2 = EX_VAR(opline->op2.var);
+ result = EX_VAR(opline->result.var);
+ ZVAL_LONG(result, Z_LVAL_P(op1) * Z_LVAL_P(op2));
+ ZEND_VM_NEXT_OPCODE();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_MUL_LONG_SPEC_CONST_TMPVARCV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zval *op1, *op2, *result;
+ zend_long overflow;
+
+ op1 = EX_CONSTANT(opline->op1);
+ op2 = EX_VAR(opline->op2.var);
+ result = EX_VAR(opline->result.var);
+ ZEND_SIGNED_MULTIPLY_LONG(Z_LVAL_P(op1), Z_LVAL_P(op2), Z_LVAL_P(result), Z_DVAL_P(result), overflow);
+ Z_TYPE_INFO_P(result) = overflow ? IS_DOUBLE : IS_LONG;
+ ZEND_VM_NEXT_OPCODE();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_MUL_DOUBLE_SPEC_CONST_TMPVARCV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zval *op1, *op2, *result;
+
+ op1 = EX_CONSTANT(opline->op1);
+ op2 = EX_VAR(opline->op2.var);
+ result = EX_VAR(opline->result.var);
+ ZVAL_DOUBLE(result, Z_DVAL_P(op1) * Z_DVAL_P(op2));
+ ZEND_VM_NEXT_OPCODE();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_EQUAL_LONG_SPEC_CONST_TMPVARCV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zval *op1, *op2;
+ int result;
+
+ op1 = EX_CONSTANT(opline->op1);
+ op2 = EX_VAR(opline->op2.var);
+ result = (Z_LVAL_P(op1) == Z_LVAL_P(op2));
+
+ ZVAL_BOOL(EX_VAR(opline->result.var), result);
+ ZEND_VM_NEXT_OPCODE();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_EQUAL_LONG_SPEC_CONST_TMPVARCV_JMPZ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zval *op1, *op2;
+ int result;
+
+ op1 = EX_CONSTANT(opline->op1);
+ op2 = EX_VAR(opline->op2.var);
+ result = (Z_LVAL_P(op1) == Z_LVAL_P(op2));
+ ZEND_VM_SMART_BRANCH_JMPZ(result, 0);
+ ZVAL_BOOL(EX_VAR(opline->result.var), result);
+ ZEND_VM_NEXT_OPCODE();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_EQUAL_LONG_SPEC_CONST_TMPVARCV_JMPNZ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zval *op1, *op2;
+ int result;
+
+ op1 = EX_CONSTANT(opline->op1);
+ op2 = EX_VAR(opline->op2.var);
+ result = (Z_LVAL_P(op1) == Z_LVAL_P(op2));
+ ZEND_VM_SMART_BRANCH_JMPNZ(result, 0);
+ ZVAL_BOOL(EX_VAR(opline->result.var), result);
+ ZEND_VM_NEXT_OPCODE();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zval *op1, *op2;
+ int result;
+
+ op1 = EX_CONSTANT(opline->op1);
+ op2 = EX_VAR(opline->op2.var);
+ result = (Z_DVAL_P(op1) == Z_DVAL_P(op2));
+
+ ZVAL_BOOL(EX_VAR(opline->result.var), result);
+ ZEND_VM_NEXT_OPCODE();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_JMPZ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zval *op1, *op2;
+ int result;
+
+ op1 = EX_CONSTANT(opline->op1);
+ op2 = EX_VAR(opline->op2.var);
+ result = (Z_DVAL_P(op1) == Z_DVAL_P(op2));
+ ZEND_VM_SMART_BRANCH_JMPZ(result, 0);
+ ZVAL_BOOL(EX_VAR(opline->result.var), result);
+ ZEND_VM_NEXT_OPCODE();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_JMPNZ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zval *op1, *op2;
+ int result;
+
+ op1 = EX_CONSTANT(opline->op1);
+ op2 = EX_VAR(opline->op2.var);
+ result = (Z_DVAL_P(op1) == Z_DVAL_P(op2));
+ ZEND_VM_SMART_BRANCH_JMPNZ(result, 0);
+ ZVAL_BOOL(EX_VAR(opline->result.var), result);
+ ZEND_VM_NEXT_OPCODE();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_NOT_EQUAL_LONG_SPEC_CONST_TMPVARCV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zval *op1, *op2;
+ int result;
+
+ op1 = EX_CONSTANT(opline->op1);
+ op2 = EX_VAR(opline->op2.var);
+ result = (Z_LVAL_P(op1) != Z_LVAL_P(op2));
+
+ ZVAL_BOOL(EX_VAR(opline->result.var), result);
+ ZEND_VM_NEXT_OPCODE();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_NOT_EQUAL_LONG_SPEC_CONST_TMPVARCV_JMPZ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zval *op1, *op2;
+ int result;
+
+ op1 = EX_CONSTANT(opline->op1);
+ op2 = EX_VAR(opline->op2.var);
+ result = (Z_LVAL_P(op1) != Z_LVAL_P(op2));
+ ZEND_VM_SMART_BRANCH_JMPZ(result, 0);
+ ZVAL_BOOL(EX_VAR(opline->result.var), result);
+ ZEND_VM_NEXT_OPCODE();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_NOT_EQUAL_LONG_SPEC_CONST_TMPVARCV_JMPNZ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zval *op1, *op2;
+ int result;
+
+ op1 = EX_CONSTANT(opline->op1);
+ op2 = EX_VAR(opline->op2.var);
+ result = (Z_LVAL_P(op1) != Z_LVAL_P(op2));
+ ZEND_VM_SMART_BRANCH_JMPNZ(result, 0);
+ ZVAL_BOOL(EX_VAR(opline->result.var), result);
+ ZEND_VM_NEXT_OPCODE();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zval *op1, *op2;
+ int result;
+
+ op1 = EX_CONSTANT(opline->op1);
+ op2 = EX_VAR(opline->op2.var);
+ result = (Z_DVAL_P(op1) != Z_DVAL_P(op2));
+
+ ZVAL_BOOL(EX_VAR(opline->result.var), result);
+ ZEND_VM_NEXT_OPCODE();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_JMPZ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zval *op1, *op2;
+ int result;
+
+ op1 = EX_CONSTANT(opline->op1);
+ op2 = EX_VAR(opline->op2.var);
+ result = (Z_DVAL_P(op1) != Z_DVAL_P(op2));
+ ZEND_VM_SMART_BRANCH_JMPZ(result, 0);
+ ZVAL_BOOL(EX_VAR(opline->result.var), result);
+ ZEND_VM_NEXT_OPCODE();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_JMPNZ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zval *op1, *op2;
+ int result;
+
+ op1 = EX_CONSTANT(opline->op1);
+ op2 = EX_VAR(opline->op2.var);
+ result = (Z_DVAL_P(op1) != Z_DVAL_P(op2));
+ ZEND_VM_SMART_BRANCH_JMPNZ(result, 0);
+ ZVAL_BOOL(EX_VAR(opline->result.var), result);
+ ZEND_VM_NEXT_OPCODE();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zval *op1, *op2;
+ int result;
+
+ op1 = EX_CONSTANT(opline->op1);
+ op2 = EX_VAR(opline->op2.var);
+ result = (Z_LVAL_P(op1) < Z_LVAL_P(op2));
+
+ ZVAL_BOOL(EX_VAR(opline->result.var), result);
+ ZEND_VM_NEXT_OPCODE();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV_JMPZ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zval *op1, *op2;
+ int result;
+
+ op1 = EX_CONSTANT(opline->op1);
+ op2 = EX_VAR(opline->op2.var);
+ result = (Z_LVAL_P(op1) < Z_LVAL_P(op2));
+ ZEND_VM_SMART_BRANCH_JMPZ(result, 0);
+ ZVAL_BOOL(EX_VAR(opline->result.var), result);
+ ZEND_VM_NEXT_OPCODE();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV_JMPNZ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zval *op1, *op2;
+ int result;
+
+ op1 = EX_CONSTANT(opline->op1);
+ op2 = EX_VAR(opline->op2.var);
+ result = (Z_LVAL_P(op1) < Z_LVAL_P(op2));
+ ZEND_VM_SMART_BRANCH_JMPNZ(result, 0);
+ ZVAL_BOOL(EX_VAR(opline->result.var), result);
+ ZEND_VM_NEXT_OPCODE();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zval *op1, *op2;
+ int result;
+
+ op1 = EX_CONSTANT(opline->op1);
+ op2 = EX_VAR(opline->op2.var);
+ result = (Z_DVAL_P(op1) < Z_DVAL_P(op2));
+
+ ZVAL_BOOL(EX_VAR(opline->result.var), result);
+ ZEND_VM_NEXT_OPCODE();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV_JMPZ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zval *op1, *op2;
+ int result;
+
+ op1 = EX_CONSTANT(opline->op1);
+ op2 = EX_VAR(opline->op2.var);
+ result = (Z_DVAL_P(op1) < Z_DVAL_P(op2));
+ ZEND_VM_SMART_BRANCH_JMPZ(result, 0);
+ ZVAL_BOOL(EX_VAR(opline->result.var), result);
+ ZEND_VM_NEXT_OPCODE();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV_JMPNZ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zval *op1, *op2;
+ int result;
+
+ op1 = EX_CONSTANT(opline->op1);
+ op2 = EX_VAR(opline->op2.var);
+ result = (Z_DVAL_P(op1) < Z_DVAL_P(op2));
+ ZEND_VM_SMART_BRANCH_JMPNZ(result, 0);
+ ZVAL_BOOL(EX_VAR(opline->result.var), result);
+ ZEND_VM_NEXT_OPCODE();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zval *op1, *op2;
+ int result;
+
+ op1 = EX_CONSTANT(opline->op1);
+ op2 = EX_VAR(opline->op2.var);
+ result = (Z_LVAL_P(op1) <= Z_LVAL_P(op2));
+
+ ZVAL_BOOL(EX_VAR(opline->result.var), result);
+ ZEND_VM_NEXT_OPCODE();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV_JMPZ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zval *op1, *op2;
+ int result;
+
+ op1 = EX_CONSTANT(opline->op1);
+ op2 = EX_VAR(opline->op2.var);
+ result = (Z_LVAL_P(op1) <= Z_LVAL_P(op2));
+ ZEND_VM_SMART_BRANCH_JMPZ(result, 0);
+ ZVAL_BOOL(EX_VAR(opline->result.var), result);
+ ZEND_VM_NEXT_OPCODE();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV_JMPNZ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zval *op1, *op2;
+ int result;
+
+ op1 = EX_CONSTANT(opline->op1);
+ op2 = EX_VAR(opline->op2.var);
+ result = (Z_LVAL_P(op1) <= Z_LVAL_P(op2));
+ ZEND_VM_SMART_BRANCH_JMPNZ(result, 0);
+ ZVAL_BOOL(EX_VAR(opline->result.var), result);
+ ZEND_VM_NEXT_OPCODE();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zval *op1, *op2;
+ int result;
+
+ op1 = EX_CONSTANT(opline->op1);
+ op2 = EX_VAR(opline->op2.var);
+ result = (Z_DVAL_P(op1) <= Z_DVAL_P(op2));
+
+ ZVAL_BOOL(EX_VAR(opline->result.var), result);
+ ZEND_VM_NEXT_OPCODE();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_JMPZ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zval *op1, *op2;
+ int result;
+
+ op1 = EX_CONSTANT(opline->op1);
+ op2 = EX_VAR(opline->op2.var);
+ result = (Z_DVAL_P(op1) <= Z_DVAL_P(op2));
+ ZEND_VM_SMART_BRANCH_JMPZ(result, 0);
+ ZVAL_BOOL(EX_VAR(opline->result.var), result);
+ ZEND_VM_NEXT_OPCODE();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_JMPNZ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zval *op1, *op2;
+ int result;
+
+ op1 = EX_CONSTANT(opline->op1);
+ op2 = EX_VAR(opline->op2.var);
+ result = (Z_DVAL_P(op1) <= Z_DVAL_P(op2));
+ ZEND_VM_SMART_BRANCH_JMPNZ(result, 0);
+ ZVAL_BOOL(EX_VAR(opline->result.var), result);
+ ZEND_VM_NEXT_OPCODE();
+}
+
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_RETURN_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
zval *retval_ptr;
+ zval *return_value;
zend_free_op free_op1;
retval_ptr = _get_zval_ptr_tmp(opline->op1.var, execute_data, &free_op1);
+ return_value = EX(return_value);
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);
- if (EX(return_value)) {
- ZVAL_NULL(EX(return_value));
+ if (return_value) {
+ ZVAL_NULL(return_value);
}
- } else if (!EX(return_value)) {
- if (IS_TMP_VAR == IS_VAR || IS_TMP_VAR == IS_TMP_VAR ) {
+ } else if (!return_value) {
+ if (IS_TMP_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));
+ zval_dtor_func(Z_COUNTED_P(free_op1));
}
}
} else {
- if (IS_TMP_VAR == IS_CONST || IS_TMP_VAR == IS_TMP_VAR) {
- ZVAL_COPY_VALUE(EX(return_value), retval_ptr);
+ if ((IS_TMP_VAR & (IS_CONST|IS_TMP_VAR))) {
+ ZVAL_COPY_VALUE(return_value, retval_ptr);
if (IS_TMP_VAR == IS_CONST) {
- if (UNEXPECTED(Z_OPT_COPYABLE_P(EX(return_value)))) {
- zval_copy_ctor_func(EX(return_value));
+ if (UNEXPECTED(Z_OPT_REFCOUNTED_P(return_value))) {
+ Z_ADDREF_P(return_value);
}
}
} else if (IS_TMP_VAR == IS_CV) {
- ZVAL_DEREF(retval_ptr);
- ZVAL_COPY(EX(return_value), retval_ptr);
+ if (Z_OPT_REFCOUNTED_P(retval_ptr)) {
+ if (EXPECTED(!Z_OPT_ISREF_P(retval_ptr))) {
+ ZVAL_COPY_VALUE(return_value, retval_ptr);
+ if (EXPECTED(!(EX_CALL_INFO() & ZEND_CALL_CODE))) {
+ ZVAL_NULL(retval_ptr);
+ } else {
+ Z_ADDREF_P(return_value);
+ }
+ } else {
+ retval_ptr = Z_REFVAL_P(retval_ptr);
+ ZVAL_COPY(return_value, retval_ptr);
+ }
+ } else {
+ ZVAL_COPY_VALUE(return_value, retval_ptr);
+ }
} else /* if (IS_TMP_VAR == IS_VAR) */ {
if (UNEXPECTED(Z_ISREF_P(retval_ptr))) {
zend_refcounted *ref = Z_COUNTED_P(retval_ptr);
retval_ptr = Z_REFVAL_P(retval_ptr);
- ZVAL_COPY_VALUE(EX(return_value), retval_ptr);
+ ZVAL_COPY_VALUE(return_value, retval_ptr);
if (UNEXPECTED(--GC_REFCOUNT(ref) == 0)) {
efree_size(ref, sizeof(zend_reference));
} else if (Z_OPT_REFCOUNTED_P(retval_ptr)) {
Z_ADDREF_P(retval_ptr);
}
} else {
- ZVAL_COPY_VALUE(EX(return_value), retval_ptr);
+ ZVAL_COPY_VALUE(return_value, retval_ptr);
}
}
}
@@ -11813,21 +12229,23 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_RETURN_BY_REF_SPEC_TMP_HANDLER
SAVE_OPLINE();
do {
- if (IS_TMP_VAR == IS_CONST || IS_TMP_VAR == IS_TMP_VAR ||
+ if ((IS_TMP_VAR & (IS_CONST|IS_TMP_VAR)) ||
(IS_TMP_VAR == IS_VAR && opline->extended_value == ZEND_RETURNS_VALUE)) {
/* Not supposed to happen, but we'll allow it */
zend_error(E_NOTICE, "Only variable references should be returned by reference");
retval_ptr = _get_zval_ptr_tmp(opline->op1.var, execute_data, &free_op1);
if (!EX(return_value)) {
- if (IS_TMP_VAR == IS_TMP_VAR) {
- zval_ptr_dtor_nogc(free_op1);
- }
+ zval_ptr_dtor_nogc(free_op1);
} else {
- ZVAL_COPY_VALUE(EX(return_value), retval_ptr);
- Z_VAR_FLAGS_P(EX(return_value)) = IS_VAR_RET_REF;
- if (IS_TMP_VAR != IS_TMP_VAR) {
- zval_opt_copy_ctor_no_imm(EX(return_value));
+ if (IS_TMP_VAR == IS_VAR && UNEXPECTED(Z_ISREF_P(retval_ptr))) {
+ ZVAL_COPY_VALUE(EX(return_value), retval_ptr);
+ break;
+ }
+
+ ZVAL_NEW_REF(EX(return_value), retval_ptr);
+ if (IS_TMP_VAR == IS_CONST) {
+ if (Z_REFCOUNTED_P(retval_ptr)) Z_ADDREF_P(retval_ptr);
}
}
break;
@@ -11835,20 +12253,14 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_RETURN_BY_REF_SPEC_TMP_HANDLER
retval_ptr = NULL;
- if (IS_TMP_VAR == IS_VAR && UNEXPECTED(retval_ptr == NULL)) {
- zend_throw_error(NULL, "Cannot return string offsets by reference");
- HANDLE_EXCEPTION();
- }
-
if (IS_TMP_VAR == IS_VAR) {
if (retval_ptr == &EG(uninitialized_zval) ||
- (opline->extended_value == ZEND_RETURNS_FUNCTION &&
- !(Z_VAR_FLAGS_P(retval_ptr) & IS_VAR_RET_REF))) {
+ (opline->extended_value == ZEND_RETURNS_FUNCTION && !Z_ISREF_P(retval_ptr))) {
zend_error(E_NOTICE, "Only variable references should be returned by reference");
if (EX(return_value)) {
ZVAL_NEW_REF(EX(return_value), retval_ptr);
- Z_VAR_FLAGS_P(EX(return_value)) = IS_VAR_RET_REF;
- if (Z_REFCOUNTED_P(retval_ptr)) Z_ADDREF_P(retval_ptr);
+ } else {
+
}
break;
}
@@ -11858,8 +12270,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_RETURN_BY_REF_SPEC_TMP_HANDLER
ZVAL_MAKE_REF(retval_ptr);
Z_ADDREF_P(retval_ptr);
ZVAL_REF(EX(return_value), Z_REF_P(retval_ptr));
- Z_VAR_FLAGS_P(EX(return_value)) = IS_VAR_RET_REF;
}
+
} while (0);
ZEND_VM_TAIL_CALL(zend_leave_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU));
@@ -11877,11 +12289,11 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_GENERATOR_RETURN_SPEC_TMP_HAND
retval = _get_zval_ptr_tmp(opline->op1.var, execute_data, &free_op1);
/* Copy return value into generator->retval */
- if (IS_TMP_VAR == IS_CONST || IS_TMP_VAR == IS_TMP_VAR) {
+ if ((IS_TMP_VAR & (IS_CONST|IS_TMP_VAR))) {
ZVAL_COPY_VALUE(&generator->retval, retval);
if (IS_TMP_VAR == IS_CONST) {
- if (UNEXPECTED(Z_OPT_COPYABLE(generator->retval))) {
- zval_copy_ctor_func(&generator->retval);
+ if (UNEXPECTED(Z_OPT_REFCOUNTED(generator->retval))) {
+ Z_ADDREF(generator->retval);
}
}
} else if (IS_TMP_VAR == IS_CV) {
@@ -11950,31 +12362,45 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_THROW_SPEC_TMP_HANDLER(ZEND_OP
HANDLE_EXCEPTION();
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SEND_VAL_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SEND_VAL_EX_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
zval *value, *arg;
zend_free_op free_op1;
+ uint32_t arg_num = opline->op2.num;
+ if (EXPECTED(0)) {
+ if (QUICK_ARG_MUST_BE_SENT_BY_REF(EX(call)->func, arg_num)) {
+ goto send_val_by_ref;
+ }
+ } else if (ARG_MUST_BE_SENT_BY_REF(EX(call)->func, arg_num)) {
+send_val_by_ref:
+ SAVE_OPLINE();
+ zend_throw_error(NULL, "Cannot pass parameter %d by reference", arg_num);
+ zval_ptr_dtor_nogc(EX_VAR(opline->op1.var));
+ arg = ZEND_CALL_VAR(EX(call), opline->result.var);
+ ZVAL_UNDEF(arg);
+ HANDLE_EXCEPTION();
+ }
value = _get_zval_ptr_tmp(opline->op1.var, execute_data, &free_op1);
arg = ZEND_CALL_VAR(EX(call), opline->result.var);
ZVAL_COPY_VALUE(arg, value);
if (IS_TMP_VAR == IS_CONST) {
- if (UNEXPECTED(Z_OPT_COPYABLE_P(arg))) {
- zval_copy_ctor_func(arg);
+ if (UNEXPECTED(Z_OPT_REFCOUNTED_P(arg))) {
+ Z_ADDREF_P(arg);
}
}
ZEND_VM_NEXT_OPCODE();
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SEND_VAL_EX_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SEND_VAL_EX_SPEC_TMP_QUICK_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
zval *value, *arg;
zend_free_op free_op1;
uint32_t arg_num = opline->op2.num;
- if (EXPECTED(arg_num <= MAX_ARG_FLAG_NUM)) {
+ if (EXPECTED(1)) {
if (QUICK_ARG_MUST_BE_SENT_BY_REF(EX(call)->func, arg_num)) {
goto send_val_by_ref;
}
@@ -11991,8 +12417,8 @@ send_val_by_ref:
arg = ZEND_CALL_VAR(EX(call), opline->result.var);
ZVAL_COPY_VALUE(arg, value);
if (IS_TMP_VAR == IS_CONST) {
- if (UNEXPECTED(Z_OPT_COPYABLE_P(arg))) {
- zval_copy_ctor_func(arg);
+ if (UNEXPECTED(Z_OPT_REFCOUNTED_P(arg))) {
+ Z_ADDREF_P(arg);
}
}
ZEND_VM_NEXT_OPCODE();
@@ -12046,11 +12472,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CAST_SPEC_TMP_HANDLER(ZEND_OPC
if (Z_TYPE_P(expr) == opline->extended_value) {
ZVAL_COPY_VALUE(result, expr);
if (IS_TMP_VAR == IS_CONST) {
- if (UNEXPECTED(Z_OPT_COPYABLE_P(result))) {
- zval_copy_ctor_func(result);
- }
+ if (UNEXPECTED(Z_OPT_REFCOUNTED_P(result))) Z_ADDREF_P(result);
} else if (IS_TMP_VAR != IS_TMP_VAR) {
- if (Z_OPT_REFCOUNTED_P(expr)) Z_ADDREF_P(expr);
+ if (Z_OPT_REFCOUNTED_P(result)) Z_ADDREF_P(result);
}
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
@@ -12063,9 +12487,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CAST_SPEC_TMP_HANDLER(ZEND_OPC
if (Z_TYPE_P(expr) != IS_NULL) {
expr = zend_hash_index_add_new(Z_ARRVAL_P(result), 0, expr);
if (IS_TMP_VAR == IS_CONST) {
- if (UNEXPECTED(Z_OPT_COPYABLE_P(expr))) {
- zval_copy_ctor_func(expr);
- }
+ if (UNEXPECTED(Z_OPT_REFCOUNTED_P(expr))) Z_ADDREF_P(expr);
} else {
if (Z_OPT_REFCOUNTED_P(expr)) Z_ADDREF_P(expr);
}
@@ -12079,11 +12501,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CAST_SPEC_TMP_HANDLER(ZEND_OPC
if (Z_TYPE_P(expr) != IS_ARRAY) {
object_init(result);
if (Z_TYPE_P(expr) != IS_NULL) {
- expr = zend_hash_str_add_new(Z_OBJPROP_P(result), "scalar", sizeof("scalar")-1, expr);
+ expr = zend_hash_add_new(Z_OBJPROP_P(result), CG(known_strings)[ZEND_STR_SCALAR], expr);
if (IS_TMP_VAR == IS_CONST) {
- if (UNEXPECTED(Z_OPT_COPYABLE_P(expr))) {
- zval_copy_ctor_func(expr);
- }
+ if (UNEXPECTED(Z_OPT_REFCOUNTED_P(expr))) Z_ADDREF_P(expr);
} else {
if (Z_OPT_REFCOUNTED_P(expr)) Z_ADDREF_P(expr);
}
@@ -12128,6 +12548,13 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FE_RESET_R_SPEC_TMP_HANDLER(ZE
if (IS_TMP_VAR != IS_TMP_VAR) {
Z_ADDREF_P(array_ptr);
}
+ if (Z_OBJ_P(array_ptr)->properties
+ && UNEXPECTED(GC_REFCOUNT(Z_OBJ_P(array_ptr)->properties) > 1)) {
+ if (EXPECTED(!(GC_FLAGS(Z_OBJ_P(array_ptr)->properties) & IS_ARRAY_IMMUTABLE))) {
+ GC_REFCOUNT(Z_OBJ_P(array_ptr)->properties)--;
+ }
+ Z_OBJ_P(array_ptr)->properties = zend_array_dup(Z_OBJ_P(array_ptr)->properties);
+ }
fe_ht = Z_OBJPROP_P(array_ptr);
pos = 0;
p = fe_ht->arData;
@@ -12272,6 +12699,13 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_TMP_HANDLER(Z
array_ptr = EX_VAR(opline->result.var);
ZVAL_COPY_VALUE(array_ptr, array_ref);
}
+ if (Z_OBJ_P(array_ptr)->properties
+ && UNEXPECTED(GC_REFCOUNT(Z_OBJ_P(array_ptr)->properties) > 1)) {
+ if (EXPECTED(!(GC_FLAGS(Z_OBJ_P(array_ptr)->properties) & IS_ARRAY_IMMUTABLE))) {
+ GC_REFCOUNT(Z_OBJ_P(array_ptr)->properties)--;
+ }
+ Z_OBJ_P(array_ptr)->properties = zend_array_dup(Z_OBJ_P(array_ptr)->properties);
+ }
fe_ht = Z_OBJPROP_P(array_ptr);
p = fe_ht->arData;
while (1) {
@@ -12392,20 +12826,20 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_JMP_SET_SPEC_TMP_HANDLER(ZEND_
value = Z_REFVAL_P(value);
}
if (i_zend_is_true(value)) {
- ZVAL_COPY_VALUE(EX_VAR(opline->result.var), value);
+ zval *result = EX_VAR(opline->result.var);
+
+ ZVAL_COPY_VALUE(result, value);
if (IS_TMP_VAR == IS_CONST) {
- if (UNEXPECTED(Z_OPT_COPYABLE_P(value))) {
- zval_copy_ctor_func(EX_VAR(opline->result.var));
- }
+ if (UNEXPECTED(Z_OPT_REFCOUNTED_P(result))) Z_ADDREF_P(result);
} else if (IS_TMP_VAR == IS_CV) {
- if (Z_OPT_REFCOUNTED_P(value)) Z_ADDREF_P(value);
+ if (Z_OPT_REFCOUNTED_P(result)) Z_ADDREF_P(result);
} else if (IS_TMP_VAR == IS_VAR && ref) {
zend_reference *r = Z_REF_P(ref);
if (UNEXPECTED(--GC_REFCOUNT(r) == 0)) {
efree_size(r, sizeof(zend_reference));
- } else if (Z_OPT_REFCOUNTED_P(value)) {
- Z_ADDREF_P(value);
+ } else if (Z_OPT_REFCOUNTED_P(result)) {
+ Z_ADDREF_P(result);
}
}
ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2));
@@ -12433,20 +12867,19 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_COALESCE_SPEC_TMP_HANDLER(ZEND
}
if (Z_TYPE_P(value) > IS_NULL) {
- ZVAL_COPY_VALUE(EX_VAR(opline->result.var), value);
+ zval *result = EX_VAR(opline->result.var);
+ ZVAL_COPY_VALUE(result, value);
if (IS_TMP_VAR == IS_CONST) {
- if (UNEXPECTED(Z_OPT_COPYABLE_P(value))) {
- zval_copy_ctor_func(EX_VAR(opline->result.var));
- }
+ if (UNEXPECTED(Z_OPT_REFCOUNTED_P(result))) Z_ADDREF_P(result);
} else if (IS_TMP_VAR == IS_CV) {
- if (Z_OPT_REFCOUNTED_P(value)) Z_ADDREF_P(value);
+ if (Z_OPT_REFCOUNTED_P(result)) Z_ADDREF_P(result);
} else if (IS_TMP_VAR == IS_VAR && ref) {
zend_reference *r = Z_REF_P(ref);
if (UNEXPECTED(--GC_REFCOUNT(r) == 0)) {
efree_size(r, sizeof(zend_reference));
- } else if (Z_OPT_REFCOUNTED_P(value)) {
- Z_ADDREF_P(value);
+ } else if (Z_OPT_REFCOUNTED_P(result)) {
+ Z_ADDREF_P(result);
}
}
ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2));
@@ -12488,8 +12921,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_QM_ASSIGN_SPEC_TMP_HANDLER(ZEN
} else {
ZVAL_COPY_VALUE(result, value);
if (IS_TMP_VAR == IS_CONST) {
- if (UNEXPECTED(Z_OPT_COPYABLE_P(value))) {
- zval_copy_ctor_func(result);
+ if (UNEXPECTED(Z_OPT_REFCOUNTED_P(result))) {
+ Z_ADDREF_P(result);
}
}
}
@@ -12606,7 +13039,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_TYPE_CHECK_SPEC_TMP_HANDLER(ZE
if (IS_TMP_VAR != IS_CONST && UNEXPECTED(Z_TYPE_P(value) == IS_OBJECT)) {
zend_class_entry *ce = Z_OBJCE_P(value);
- if (UNEXPECTED(ZSTR_LEN(ce->name) != sizeof("__PHP_Incomplete_Class") - 1) ||
+ if (EXPECTED(ZSTR_LEN(ce->name) != sizeof("__PHP_Incomplete_Class") - 1) ||
EXPECTED(memcmp(ZSTR_VAL(ce->name), "__PHP_Incomplete_Class", sizeof("__PHP_Incomplete_Class") - 1) != 0)) {
result = 1;
}
@@ -12674,21 +13107,16 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_DIM_FUNC_ARG_SPEC_TMP_CO
SAVE_OPLINE();
if (zend_is_by_ref_func_arg_fetch(opline, EX(call))) {
- if (IS_TMP_VAR == IS_CONST || IS_TMP_VAR == IS_TMP_VAR) {
+ if ((IS_TMP_VAR & (IS_CONST|IS_TMP_VAR))) {
zend_throw_error(NULL, "Cannot use temporary expression in write context");
zval_ptr_dtor_nogc(EX_VAR(opline->op1.var));
HANDLE_EXCEPTION();
}
container = NULL;
- if (IS_TMP_VAR == IS_VAR && UNEXPECTED(container == NULL)) {
- zend_throw_error(NULL, "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);
if (IS_TMP_VAR == IS_VAR && READY_TO_DESTROY(free_op1)) {
- EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var), 1);
+ EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var));
}
@@ -12718,7 +13146,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_R_SPEC_TMP_CONST_HAN
SAVE_OPLINE();
container = _get_zval_ptr_tmp(opline->op1.var, execute_data, &free_op1);
- if (IS_TMP_VAR == IS_UNUSED && UNEXPECTED(Z_OBJ_P(container) == NULL)) {
+ if (IS_TMP_VAR == IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
HANDLE_EXCEPTION();
@@ -12793,26 +13221,21 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_FUNC_ARG_SPEC_TMP_CO
property = EX_CONSTANT(opline->op2);
container = NULL;
- if (IS_TMP_VAR == IS_UNUSED && UNEXPECTED(Z_OBJ_P(container) == NULL)) {
+ if (IS_TMP_VAR == IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
HANDLE_EXCEPTION();
}
- if (IS_TMP_VAR == IS_CONST || IS_TMP_VAR == IS_TMP_VAR) {
+ if ((IS_TMP_VAR & (IS_CONST|IS_TMP_VAR))) {
zend_throw_error(NULL, "Cannot use temporary expression in write context");
HANDLE_EXCEPTION();
}
- if (IS_TMP_VAR == IS_VAR && UNEXPECTED(container == NULL)) {
- zend_throw_error(NULL, "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);
if (IS_TMP_VAR == IS_VAR && READY_TO_DESTROY(free_op1)) {
- EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var), 0);
+ EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var));
}
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
@@ -12917,11 +13340,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ADD_ARRAY_ELEMENT_SPEC_TMP_CON
if ((IS_TMP_VAR == IS_VAR || IS_TMP_VAR == IS_CV) &&
UNEXPECTED(opline->extended_value & ZEND_ARRAY_ELEMENT_REF)) {
expr_ptr = NULL;
- if (IS_TMP_VAR == IS_VAR && UNEXPECTED(expr_ptr == NULL)) {
- zend_throw_error(NULL, "Cannot create references to/from string offsets");
- zend_array_destroy(Z_ARRVAL_P(EX_VAR(opline->result.var)));
- HANDLE_EXCEPTION();
- }
ZVAL_MAKE_REF(expr_ptr);
Z_ADDREF_P(expr_ptr);
@@ -12930,10 +13348,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ADD_ARRAY_ELEMENT_SPEC_TMP_CON
if (IS_TMP_VAR == IS_TMP_VAR) {
/* pass */
} else if (IS_TMP_VAR == IS_CONST) {
- 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;
+ if (Z_REFCOUNTED_P(expr_ptr)) {
+ Z_ADDREF_P(expr_ptr);
}
} else if (IS_TMP_VAR == IS_CV) {
ZVAL_DEREF(expr_ptr);
@@ -13064,7 +13480,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_TMP_CONST_HANDLER(Z
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) {
+ if (IS_TMP_VAR & (IS_CONST|IS_TMP_VAR)) {
zval *value;
zend_error(E_NOTICE, "Only variable references should be yielded by reference");
@@ -13072,25 +13488,19 @@ 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 (IS_TMP_VAR == IS_CONST) {
- if (UNEXPECTED(Z_OPT_COPYABLE(generator->value))) {
- zval_copy_ctor_func(&generator->value);
+ if (UNEXPECTED(Z_OPT_REFCOUNTED(generator->value))) {
+ Z_ADDREF(generator->value);
}
}
} else {
zval *value_ptr = NULL;
- if (IS_TMP_VAR == IS_VAR && UNEXPECTED(value_ptr == NULL)) {
- zend_throw_error(NULL, "Cannot yield string offsets by reference");
-
- HANDLE_EXCEPTION();
- }
-
/* If a function call result is yielded and the function did
* not return by reference we throw a notice. */
if (IS_TMP_VAR == IS_VAR &&
(value_ptr == &EG(uninitialized_zval) ||
(opline->extended_value == ZEND_RETURNS_FUNCTION &&
- !(Z_VAR_FLAGS_P(value_ptr) & IS_VAR_RET_REF)))) {
+ !Z_ISREF_P(value_ptr)))) {
zend_error(E_NOTICE, "Only variable references should be yielded by reference");
} else {
ZVAL_MAKE_REF(value_ptr);
@@ -13104,8 +13514,8 @@ 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_COPY_VALUE(&generator->value, value);
- if (UNEXPECTED(Z_OPT_COPYABLE(generator->value))) {
- zval_copy_ctor_func(&generator->value);
+ if (UNEXPECTED(Z_OPT_REFCOUNTED(generator->value))) {
+ Z_ADDREF(generator->value);
}
} else if (IS_TMP_VAR == IS_TMP_VAR) {
ZVAL_COPY_VALUE(&generator->value, value);
@@ -13132,8 +13542,8 @@ 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_COPY_VALUE(&generator->key, key);
- if (UNEXPECTED(Z_OPT_COPYABLE(generator->key))) {
- zval_copy_ctor_func(&generator->key);
+ if (UNEXPECTED(Z_OPT_REFCOUNTED(generator->key))) {
+ Z_ADDREF(generator->key);
}
} else if (IS_CONST == IS_TMP_VAR) {
ZVAL_COPY_VALUE(&generator->key, key);
@@ -13241,7 +13651,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_TMP_TMP_HANDLER(ZEN
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) {
+ if (IS_TMP_VAR & (IS_CONST|IS_TMP_VAR)) {
zval *value;
zend_error(E_NOTICE, "Only variable references should be yielded by reference");
@@ -13249,25 +13659,19 @@ 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 (IS_TMP_VAR == IS_CONST) {
- if (UNEXPECTED(Z_OPT_COPYABLE(generator->value))) {
- zval_copy_ctor_func(&generator->value);
+ if (UNEXPECTED(Z_OPT_REFCOUNTED(generator->value))) {
+ Z_ADDREF(generator->value);
}
}
} else {
zval *value_ptr = NULL;
- if (IS_TMP_VAR == IS_VAR && UNEXPECTED(value_ptr == NULL)) {
- zend_throw_error(NULL, "Cannot yield string offsets by reference");
- zval_ptr_dtor_nogc(EX_VAR(opline->op2.var));
- HANDLE_EXCEPTION();
- }
-
/* If a function call result is yielded and the function did
* not return by reference we throw a notice. */
if (IS_TMP_VAR == IS_VAR &&
(value_ptr == &EG(uninitialized_zval) ||
(opline->extended_value == ZEND_RETURNS_FUNCTION &&
- !(Z_VAR_FLAGS_P(value_ptr) & IS_VAR_RET_REF)))) {
+ !Z_ISREF_P(value_ptr)))) {
zend_error(E_NOTICE, "Only variable references should be yielded by reference");
} else {
ZVAL_MAKE_REF(value_ptr);
@@ -13281,8 +13685,8 @@ 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_COPY_VALUE(&generator->value, value);
- if (UNEXPECTED(Z_OPT_COPYABLE(generator->value))) {
- zval_copy_ctor_func(&generator->value);
+ if (UNEXPECTED(Z_OPT_REFCOUNTED(generator->value))) {
+ Z_ADDREF(generator->value);
}
} else if (IS_TMP_VAR == IS_TMP_VAR) {
ZVAL_COPY_VALUE(&generator->value, value);
@@ -13309,8 +13713,8 @@ 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_COPY_VALUE(&generator->key, key);
- if (UNEXPECTED(Z_OPT_COPYABLE(generator->key))) {
- zval_copy_ctor_func(&generator->key);
+ if (UNEXPECTED(Z_OPT_REFCOUNTED(generator->key))) {
+ Z_ADDREF(generator->key);
}
} else if (IS_TMP_VAR == IS_TMP_VAR) {
ZVAL_COPY_VALUE(&generator->key, key);
@@ -13418,7 +13822,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_TMP_VAR_HANDLER(ZEN
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) {
+ if (IS_TMP_VAR & (IS_CONST|IS_TMP_VAR)) {
zval *value;
zend_error(E_NOTICE, "Only variable references should be yielded by reference");
@@ -13426,25 +13830,19 @@ 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 (IS_TMP_VAR == IS_CONST) {
- if (UNEXPECTED(Z_OPT_COPYABLE(generator->value))) {
- zval_copy_ctor_func(&generator->value);
+ if (UNEXPECTED(Z_OPT_REFCOUNTED(generator->value))) {
+ Z_ADDREF(generator->value);
}
}
} else {
zval *value_ptr = NULL;
- if (IS_TMP_VAR == IS_VAR && UNEXPECTED(value_ptr == NULL)) {
- zend_throw_error(NULL, "Cannot yield string offsets by reference");
- zval_ptr_dtor_nogc(EX_VAR(opline->op2.var));
- HANDLE_EXCEPTION();
- }
-
/* If a function call result is yielded and the function did
* not return by reference we throw a notice. */
if (IS_TMP_VAR == IS_VAR &&
(value_ptr == &EG(uninitialized_zval) ||
(opline->extended_value == ZEND_RETURNS_FUNCTION &&
- !(Z_VAR_FLAGS_P(value_ptr) & IS_VAR_RET_REF)))) {
+ !Z_ISREF_P(value_ptr)))) {
zend_error(E_NOTICE, "Only variable references should be yielded by reference");
} else {
ZVAL_MAKE_REF(value_ptr);
@@ -13458,8 +13856,8 @@ 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_COPY_VALUE(&generator->value, value);
- if (UNEXPECTED(Z_OPT_COPYABLE(generator->value))) {
- zval_copy_ctor_func(&generator->value);
+ if (UNEXPECTED(Z_OPT_REFCOUNTED(generator->value))) {
+ Z_ADDREF(generator->value);
}
} else if (IS_TMP_VAR == IS_TMP_VAR) {
ZVAL_COPY_VALUE(&generator->value, value);
@@ -13486,8 +13884,8 @@ 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_COPY_VALUE(&generator->key, key);
- if (UNEXPECTED(Z_OPT_COPYABLE(generator->key))) {
- zval_copy_ctor_func(&generator->key);
+ if (UNEXPECTED(Z_OPT_REFCOUNTED(generator->key))) {
+ Z_ADDREF(generator->key);
}
} else if (IS_VAR == IS_TMP_VAR) {
ZVAL_COPY_VALUE(&generator->key, key);
@@ -13541,21 +13939,16 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_DIM_FUNC_ARG_SPEC_TMP_UN
SAVE_OPLINE();
if (zend_is_by_ref_func_arg_fetch(opline, EX(call))) {
- if (IS_TMP_VAR == IS_CONST || IS_TMP_VAR == IS_TMP_VAR) {
+ if ((IS_TMP_VAR & (IS_CONST|IS_TMP_VAR))) {
zend_throw_error(NULL, "Cannot use temporary expression in write context");
zval_ptr_dtor_nogc(EX_VAR(opline->op1.var));
HANDLE_EXCEPTION();
}
container = NULL;
- if (IS_TMP_VAR == IS_VAR && UNEXPECTED(container == NULL)) {
- zend_throw_error(NULL, "Cannot use string offset as an array");
-
- HANDLE_EXCEPTION();
- }
zend_fetch_dimension_address_W(EX_VAR(opline->result.var), container, NULL, IS_UNUSED);
if (IS_TMP_VAR == IS_VAR && READY_TO_DESTROY(free_op1)) {
- EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var), 1);
+ EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var));
}
@@ -13622,8 +14015,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_VERIFY_RETURN_TYPE_SPEC_TMP_UN
if (UNEXPECTED(EG(exception) != NULL)) {
if (IS_TMP_VAR == IS_CONST) {
zval_ptr_dtor_nogc(retval_ptr);
- } else {
- zval_ptr_dtor_nogc(free_op1);
}
}
#endif
@@ -13641,11 +14032,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ADD_ARRAY_ELEMENT_SPEC_TMP_UNU
if ((IS_TMP_VAR == IS_VAR || IS_TMP_VAR == IS_CV) &&
UNEXPECTED(opline->extended_value & ZEND_ARRAY_ELEMENT_REF)) {
expr_ptr = NULL;
- if (IS_TMP_VAR == IS_VAR && UNEXPECTED(expr_ptr == NULL)) {
- zend_throw_error(NULL, "Cannot create references to/from string offsets");
- zend_array_destroy(Z_ARRVAL_P(EX_VAR(opline->result.var)));
- HANDLE_EXCEPTION();
- }
ZVAL_MAKE_REF(expr_ptr);
Z_ADDREF_P(expr_ptr);
@@ -13654,10 +14040,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ADD_ARRAY_ELEMENT_SPEC_TMP_UNU
if (IS_TMP_VAR == IS_TMP_VAR) {
/* pass */
} else if (IS_TMP_VAR == IS_CONST) {
- 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;
+ if (Z_REFCOUNTED_P(expr_ptr)) {
+ Z_ADDREF_P(expr_ptr);
}
} else if (IS_TMP_VAR == IS_CV) {
ZVAL_DEREF(expr_ptr);
@@ -13788,7 +14172,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_TMP_UNUSED_HANDLER(
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) {
+ if (IS_TMP_VAR & (IS_CONST|IS_TMP_VAR)) {
zval *value;
zend_error(E_NOTICE, "Only variable references should be yielded by reference");
@@ -13796,25 +14180,19 @@ 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 (IS_TMP_VAR == IS_CONST) {
- if (UNEXPECTED(Z_OPT_COPYABLE(generator->value))) {
- zval_copy_ctor_func(&generator->value);
+ if (UNEXPECTED(Z_OPT_REFCOUNTED(generator->value))) {
+ Z_ADDREF(generator->value);
}
}
} else {
zval *value_ptr = NULL;
- if (IS_TMP_VAR == IS_VAR && UNEXPECTED(value_ptr == NULL)) {
- zend_throw_error(NULL, "Cannot yield string offsets by reference");
-
- HANDLE_EXCEPTION();
- }
-
/* If a function call result is yielded and the function did
* not return by reference we throw a notice. */
if (IS_TMP_VAR == IS_VAR &&
(value_ptr == &EG(uninitialized_zval) ||
(opline->extended_value == ZEND_RETURNS_FUNCTION &&
- !(Z_VAR_FLAGS_P(value_ptr) & IS_VAR_RET_REF)))) {
+ !Z_ISREF_P(value_ptr)))) {
zend_error(E_NOTICE, "Only variable references should be yielded by reference");
} else {
ZVAL_MAKE_REF(value_ptr);
@@ -13828,8 +14206,8 @@ 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_COPY_VALUE(&generator->value, value);
- if (UNEXPECTED(Z_OPT_COPYABLE(generator->value))) {
- zval_copy_ctor_func(&generator->value);
+ if (UNEXPECTED(Z_OPT_REFCOUNTED(generator->value))) {
+ Z_ADDREF(generator->value);
}
} else if (IS_TMP_VAR == IS_TMP_VAR) {
ZVAL_COPY_VALUE(&generator->value, value);
@@ -13856,8 +14234,8 @@ 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_COPY_VALUE(&generator->key, key);
- if (UNEXPECTED(Z_OPT_COPYABLE(generator->key))) {
- zval_copy_ctor_func(&generator->key);
+ if (UNEXPECTED(Z_OPT_REFCOUNTED(generator->key))) {
+ Z_ADDREF(generator->key);
}
} else if (IS_UNUSED == IS_TMP_VAR) {
ZVAL_COPY_VALUE(&generator->key, key);
@@ -13947,21 +14325,16 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_DIM_FUNC_ARG_SPEC_TMP_CV
SAVE_OPLINE();
if (zend_is_by_ref_func_arg_fetch(opline, EX(call))) {
- if (IS_TMP_VAR == IS_CONST || IS_TMP_VAR == IS_TMP_VAR) {
+ if ((IS_TMP_VAR & (IS_CONST|IS_TMP_VAR))) {
zend_throw_error(NULL, "Cannot use temporary expression in write context");
zval_ptr_dtor_nogc(EX_VAR(opline->op1.var));
HANDLE_EXCEPTION();
}
container = NULL;
- if (IS_TMP_VAR == IS_VAR && UNEXPECTED(container == NULL)) {
- zend_throw_error(NULL, "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);
+ zend_fetch_dimension_address_W(EX_VAR(opline->result.var), container, _get_zval_ptr_cv_undef(execute_data, opline->op2.var), IS_CV);
if (IS_TMP_VAR == IS_VAR && READY_TO_DESTROY(free_op1)) {
- EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var), 1);
+ EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var));
}
@@ -13973,7 +14346,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_DIM_FUNC_ARG_SPEC_TMP_CV
HANDLE_EXCEPTION();
}
container = _get_zval_ptr_tmp(opline->op1.var, execute_data, &free_op1);
- zend_fetch_dimension_address_read_R(EX_VAR(opline->result.var), container, _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var), IS_CV);
+ zend_fetch_dimension_address_read_R(EX_VAR(opline->result.var), container, _get_zval_ptr_cv_undef(execute_data, opline->op2.var), IS_CV);
zval_ptr_dtor_nogc(free_op1);
}
@@ -13991,7 +14364,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_R_SPEC_TMP_CV_HANDLE
SAVE_OPLINE();
container = _get_zval_ptr_tmp(opline->op1.var, execute_data, &free_op1);
- if (IS_TMP_VAR == IS_UNUSED && UNEXPECTED(Z_OBJ_P(container) == NULL)) {
+ if (IS_TMP_VAR == IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
HANDLE_EXCEPTION();
@@ -14066,26 +14439,21 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_FUNC_ARG_SPEC_TMP_CV
property = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var);
container = NULL;
- if (IS_TMP_VAR == IS_UNUSED && UNEXPECTED(Z_OBJ_P(container) == NULL)) {
+ if (IS_TMP_VAR == IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
HANDLE_EXCEPTION();
}
- if (IS_TMP_VAR == IS_CONST || IS_TMP_VAR == IS_TMP_VAR) {
+ if ((IS_TMP_VAR & (IS_CONST|IS_TMP_VAR))) {
zend_throw_error(NULL, "Cannot use temporary expression in write context");
HANDLE_EXCEPTION();
}
- if (IS_TMP_VAR == IS_VAR && UNEXPECTED(container == NULL)) {
- zend_throw_error(NULL, "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);
if (IS_TMP_VAR == IS_VAR && READY_TO_DESTROY(free_op1)) {
- EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var), 0);
+ EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var));
}
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
@@ -14190,11 +14558,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ADD_ARRAY_ELEMENT_SPEC_TMP_CV_
if ((IS_TMP_VAR == IS_VAR || IS_TMP_VAR == IS_CV) &&
UNEXPECTED(opline->extended_value & ZEND_ARRAY_ELEMENT_REF)) {
expr_ptr = NULL;
- if (IS_TMP_VAR == IS_VAR && UNEXPECTED(expr_ptr == NULL)) {
- zend_throw_error(NULL, "Cannot create references to/from string offsets");
- zend_array_destroy(Z_ARRVAL_P(EX_VAR(opline->result.var)));
- HANDLE_EXCEPTION();
- }
ZVAL_MAKE_REF(expr_ptr);
Z_ADDREF_P(expr_ptr);
@@ -14203,10 +14566,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ADD_ARRAY_ELEMENT_SPEC_TMP_CV_
if (IS_TMP_VAR == IS_TMP_VAR) {
/* pass */
} else if (IS_TMP_VAR == IS_CONST) {
- 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;
+ if (Z_REFCOUNTED_P(expr_ptr)) {
+ Z_ADDREF_P(expr_ptr);
}
} else if (IS_TMP_VAR == IS_CV) {
ZVAL_DEREF(expr_ptr);
@@ -14337,7 +14698,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_TMP_CV_HANDLER(ZEND
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) {
+ if (IS_TMP_VAR & (IS_CONST|IS_TMP_VAR)) {
zval *value;
zend_error(E_NOTICE, "Only variable references should be yielded by reference");
@@ -14345,25 +14706,19 @@ 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 (IS_TMP_VAR == IS_CONST) {
- if (UNEXPECTED(Z_OPT_COPYABLE(generator->value))) {
- zval_copy_ctor_func(&generator->value);
+ if (UNEXPECTED(Z_OPT_REFCOUNTED(generator->value))) {
+ Z_ADDREF(generator->value);
}
}
} else {
zval *value_ptr = NULL;
- if (IS_TMP_VAR == IS_VAR && UNEXPECTED(value_ptr == NULL)) {
- zend_throw_error(NULL, "Cannot yield string offsets by reference");
-
- HANDLE_EXCEPTION();
- }
-
/* If a function call result is yielded and the function did
* not return by reference we throw a notice. */
if (IS_TMP_VAR == IS_VAR &&
(value_ptr == &EG(uninitialized_zval) ||
(opline->extended_value == ZEND_RETURNS_FUNCTION &&
- !(Z_VAR_FLAGS_P(value_ptr) & IS_VAR_RET_REF)))) {
+ !Z_ISREF_P(value_ptr)))) {
zend_error(E_NOTICE, "Only variable references should be yielded by reference");
} else {
ZVAL_MAKE_REF(value_ptr);
@@ -14377,8 +14732,8 @@ 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_COPY_VALUE(&generator->value, value);
- if (UNEXPECTED(Z_OPT_COPYABLE(generator->value))) {
- zval_copy_ctor_func(&generator->value);
+ if (UNEXPECTED(Z_OPT_REFCOUNTED(generator->value))) {
+ Z_ADDREF(generator->value);
}
} else if (IS_TMP_VAR == IS_TMP_VAR) {
ZVAL_COPY_VALUE(&generator->value, value);
@@ -14405,8 +14760,8 @@ 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_COPY_VALUE(&generator->key, key);
- if (UNEXPECTED(Z_OPT_COPYABLE(generator->key))) {
- zval_copy_ctor_func(&generator->key);
+ if (UNEXPECTED(Z_OPT_REFCOUNTED(generator->key))) {
+ Z_ADDREF(generator->key);
}
} else if (IS_CV == IS_TMP_VAR) {
ZVAL_COPY_VALUE(&generator->key, key);
@@ -14451,6 +14806,37 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_TMP_CV_HANDLER(ZEND
ZEND_VM_RETURN();
}
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_BIND_LEXICAL_SPEC_TMP_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zend_free_op free_op1;
+ zval *closure, *var;
+ zend_string *var_name;
+
+ closure = _get_zval_ptr_tmp(opline->op1.var, execute_data, &free_op1);
+ if (opline->extended_value) {
+ /* By-ref binding */
+ var = _get_zval_ptr_cv_BP_VAR_W(execute_data, opline->op2.var);
+ ZVAL_MAKE_REF(var);
+ Z_ADDREF_P(var);
+ } else {
+ var = _get_zval_ptr_cv_undef(execute_data, opline->op2.var);
+ if (UNEXPECTED(Z_ISUNDEF_P(var))) {
+ SAVE_OPLINE();
+ var = GET_OP2_UNDEF_CV(var, BP_VAR_R);
+ if (UNEXPECTED(EG(exception))) {
+ HANDLE_EXCEPTION();
+ }
+ }
+ ZVAL_DEREF(var);
+ Z_TRY_ADDREF_P(var);
+ }
+
+ var_name = CV_DEF_OF(EX_VAR_TO_NUM(opline->op2.var));
+ zend_closure_bind_var(closure, var_name, var);
+ ZEND_VM_NEXT_OPCODE();
+}
+
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_DIM_FUNC_ARG_SPEC_TMP_TMPVAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
@@ -14460,21 +14846,16 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_DIM_FUNC_ARG_SPEC_TMP_TM
SAVE_OPLINE();
if (zend_is_by_ref_func_arg_fetch(opline, EX(call))) {
- if (IS_TMP_VAR == IS_CONST || IS_TMP_VAR == IS_TMP_VAR) {
+ if ((IS_TMP_VAR & (IS_CONST|IS_TMP_VAR))) {
zend_throw_error(NULL, "Cannot use temporary expression in write context");
zval_ptr_dtor_nogc(EX_VAR(opline->op2.var));
zval_ptr_dtor_nogc(EX_VAR(opline->op1.var));
HANDLE_EXCEPTION();
}
container = NULL;
- if (IS_TMP_VAR == IS_VAR && UNEXPECTED(container == NULL)) {
- zend_throw_error(NULL, "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));
if (IS_TMP_VAR == IS_VAR && READY_TO_DESTROY(free_op1)) {
- EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var), 1);
+ EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var));
}
zval_ptr_dtor_nogc(free_op2);
@@ -14504,7 +14885,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_R_SPEC_TMP_TMPVAR_HA
SAVE_OPLINE();
container = _get_zval_ptr_tmp(opline->op1.var, execute_data, &free_op1);
- if (IS_TMP_VAR == IS_UNUSED && UNEXPECTED(Z_OBJ_P(container) == NULL)) {
+ if (IS_TMP_VAR == IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
zval_ptr_dtor_nogc(EX_VAR(opline->op2.var));
HANDLE_EXCEPTION();
@@ -14580,26 +14961,21 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_FUNC_ARG_SPEC_TMP_TM
property = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2);
container = NULL;
- if (IS_TMP_VAR == IS_UNUSED && UNEXPECTED(Z_OBJ_P(container) == NULL)) {
+ if (IS_TMP_VAR == IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
zval_ptr_dtor_nogc(free_op2);
HANDLE_EXCEPTION();
}
- if (IS_TMP_VAR == IS_CONST || IS_TMP_VAR == IS_TMP_VAR) {
+ if ((IS_TMP_VAR & (IS_CONST|IS_TMP_VAR))) {
zend_throw_error(NULL, "Cannot use temporary expression in write context");
zval_ptr_dtor_nogc(free_op2);
HANDLE_EXCEPTION();
}
- if (IS_TMP_VAR == IS_VAR && UNEXPECTED(container == NULL)) {
- zend_throw_error(NULL, "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);
zval_ptr_dtor_nogc(free_op2);
if (IS_TMP_VAR == IS_VAR && READY_TO_DESTROY(free_op1)) {
- EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var), 0);
+ EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var));
}
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
@@ -14704,11 +15080,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ADD_ARRAY_ELEMENT_SPEC_TMP_TMP
if ((IS_TMP_VAR == IS_VAR || IS_TMP_VAR == IS_CV) &&
UNEXPECTED(opline->extended_value & ZEND_ARRAY_ELEMENT_REF)) {
expr_ptr = NULL;
- if (IS_TMP_VAR == IS_VAR && UNEXPECTED(expr_ptr == NULL)) {
- zend_throw_error(NULL, "Cannot create references to/from string offsets");
- zend_array_destroy(Z_ARRVAL_P(EX_VAR(opline->result.var)));
- HANDLE_EXCEPTION();
- }
ZVAL_MAKE_REF(expr_ptr);
Z_ADDREF_P(expr_ptr);
@@ -14717,10 +15088,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ADD_ARRAY_ELEMENT_SPEC_TMP_TMP
if (IS_TMP_VAR == IS_TMP_VAR) {
/* pass */
} else if (IS_TMP_VAR == IS_CONST) {
- 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;
+ if (Z_REFCOUNTED_P(expr_ptr)) {
+ Z_ADDREF_P(expr_ptr);
}
} else if (IS_TMP_VAR == IS_CV) {
ZVAL_DEREF(expr_ptr);
@@ -14824,7 +15193,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_ARRAY_SPEC_TMP_TMPVAR_HAN
}
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_PRE_INC_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_PRE_INC_SPEC_VAR_RETVAL_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
zend_free_op free_op1;
@@ -14832,22 +15201,56 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_PRE_INC_SPEC_VAR_HANDLER(ZEND_
var_ptr = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1);
- if (IS_VAR == IS_VAR && UNEXPECTED(var_ptr == NULL)) {
- SAVE_OPLINE();
- zend_throw_error(NULL, "Cannot increment/decrement overloaded objects nor string offsets");
- HANDLE_EXCEPTION();
+ if (EXPECTED(Z_TYPE_P(var_ptr) == IS_LONG)) {
+ fast_long_increment_function(var_ptr);
+ if (UNEXPECTED(0)) {
+ ZVAL_COPY_VALUE(EX_VAR(opline->result.var), var_ptr);
+ }
+ ZEND_VM_NEXT_OPCODE();
}
+ if (IS_VAR == IS_VAR && UNEXPECTED(Z_ISERROR_P(var_ptr))) {
+ if (UNEXPECTED(0)) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+ ZEND_VM_NEXT_OPCODE();
+ }
+
+ SAVE_OPLINE();
+ if (IS_VAR == IS_CV && UNEXPECTED(Z_TYPE_P(var_ptr) == IS_UNDEF)) {
+ var_ptr = GET_OP1_UNDEF_CV(var_ptr, BP_VAR_RW);
+ }
+ ZVAL_DEREF(var_ptr);
+ SEPARATE_ZVAL_NOREF(var_ptr);
+
+ increment_function(var_ptr);
+
+ if (UNEXPECTED(0)) {
+ ZVAL_COPY(EX_VAR(opline->result.var), var_ptr);
+ }
+
+ if (UNEXPECTED(free_op1)) {zval_ptr_dtor_nogc(free_op1);};
+ ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_PRE_INC_SPEC_VAR_RETVAL_USED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zend_free_op free_op1;
+ zval *var_ptr;
+
+ var_ptr = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1);
+
if (EXPECTED(Z_TYPE_P(var_ptr) == IS_LONG)) {
fast_long_increment_function(var_ptr);
- if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ if (UNEXPECTED(1)) {
ZVAL_COPY_VALUE(EX_VAR(opline->result.var), var_ptr);
}
ZEND_VM_NEXT_OPCODE();
}
- if (IS_VAR == IS_VAR && UNEXPECTED(var_ptr == &EG(error_zval))) {
- if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ if (IS_VAR == IS_VAR && UNEXPECTED(Z_ISERROR_P(var_ptr))) {
+ if (UNEXPECTED(1)) {
ZVAL_NULL(EX_VAR(opline->result.var));
}
ZEND_VM_NEXT_OPCODE();
@@ -14862,7 +15265,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_PRE_INC_SPEC_VAR_HANDLER(ZEND_
increment_function(var_ptr);
- if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ if (UNEXPECTED(1)) {
ZVAL_COPY(EX_VAR(opline->result.var), var_ptr);
}
@@ -14870,7 +15273,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_PRE_INC_SPEC_VAR_HANDLER(ZEND_
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_PRE_DEC_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_PRE_DEC_SPEC_VAR_RETVAL_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
zend_free_op free_op1;
@@ -14878,22 +15281,56 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_PRE_DEC_SPEC_VAR_HANDLER(ZEND_
var_ptr = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1);
- if (IS_VAR == IS_VAR && UNEXPECTED(var_ptr == NULL)) {
- SAVE_OPLINE();
- zend_throw_error(NULL, "Cannot increment/decrement overloaded objects nor string offsets");
- HANDLE_EXCEPTION();
+ if (EXPECTED(Z_TYPE_P(var_ptr) == IS_LONG)) {
+ fast_long_decrement_function(var_ptr);
+ if (UNEXPECTED(0)) {
+ ZVAL_COPY_VALUE(EX_VAR(opline->result.var), var_ptr);
+ }
+ ZEND_VM_NEXT_OPCODE();
+ }
+
+ if (IS_VAR == IS_VAR && UNEXPECTED(Z_ISERROR_P(var_ptr))) {
+ if (UNEXPECTED(0)) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+ ZEND_VM_NEXT_OPCODE();
+ }
+
+ SAVE_OPLINE();
+ if (IS_VAR == IS_CV && UNEXPECTED(Z_TYPE_P(var_ptr) == IS_UNDEF)) {
+ var_ptr = GET_OP1_UNDEF_CV(var_ptr, BP_VAR_RW);
+ }
+ ZVAL_DEREF(var_ptr);
+ SEPARATE_ZVAL_NOREF(var_ptr);
+
+ decrement_function(var_ptr);
+
+ if (UNEXPECTED(0)) {
+ ZVAL_COPY(EX_VAR(opline->result.var), var_ptr);
}
+ if (UNEXPECTED(free_op1)) {zval_ptr_dtor_nogc(free_op1);};
+ ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_PRE_DEC_SPEC_VAR_RETVAL_USED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zend_free_op free_op1;
+ zval *var_ptr;
+
+ var_ptr = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1);
+
if (EXPECTED(Z_TYPE_P(var_ptr) == IS_LONG)) {
fast_long_decrement_function(var_ptr);
- if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ if (UNEXPECTED(1)) {
ZVAL_COPY_VALUE(EX_VAR(opline->result.var), var_ptr);
}
ZEND_VM_NEXT_OPCODE();
}
- if (IS_VAR == IS_VAR && UNEXPECTED(var_ptr == &EG(error_zval))) {
- if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ if (IS_VAR == IS_VAR && UNEXPECTED(Z_ISERROR_P(var_ptr))) {
+ if (UNEXPECTED(1)) {
ZVAL_NULL(EX_VAR(opline->result.var));
}
ZEND_VM_NEXT_OPCODE();
@@ -14908,7 +15345,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_PRE_DEC_SPEC_VAR_HANDLER(ZEND_
decrement_function(var_ptr);
- if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ if (UNEXPECTED(1)) {
ZVAL_COPY(EX_VAR(opline->result.var), var_ptr);
}
@@ -14924,19 +15361,13 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_POST_INC_SPEC_VAR_HANDLER(ZEND
var_ptr = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1);
- if (IS_VAR == IS_VAR && UNEXPECTED(var_ptr == NULL)) {
- SAVE_OPLINE();
- zend_throw_error(NULL, "Cannot increment/decrement overloaded objects nor string offsets");
- HANDLE_EXCEPTION();
- }
-
if (EXPECTED(Z_TYPE_P(var_ptr) == IS_LONG)) {
ZVAL_COPY_VALUE(EX_VAR(opline->result.var), var_ptr);
fast_long_increment_function(var_ptr);
ZEND_VM_NEXT_OPCODE();
}
- if (IS_VAR == IS_VAR && UNEXPECTED(var_ptr == &EG(error_zval))) {
+ if (IS_VAR == IS_VAR && UNEXPECTED(Z_ISERROR_P(var_ptr))) {
ZVAL_NULL(EX_VAR(opline->result.var));
ZEND_VM_NEXT_OPCODE();
}
@@ -14963,19 +15394,13 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_POST_DEC_SPEC_VAR_HANDLER(ZEND
var_ptr = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1);
- if (IS_VAR == IS_VAR && UNEXPECTED(var_ptr == NULL)) {
- SAVE_OPLINE();
- zend_throw_error(NULL, "Cannot increment/decrement overloaded objects nor string offsets");
- HANDLE_EXCEPTION();
- }
-
if (EXPECTED(Z_TYPE_P(var_ptr) == IS_LONG)) {
ZVAL_COPY_VALUE(EX_VAR(opline->result.var), var_ptr);
fast_long_decrement_function(var_ptr);
ZEND_VM_NEXT_OPCODE();
}
- if (IS_VAR == IS_VAR && UNEXPECTED(var_ptr == &EG(error_zval))) {
+ if (IS_VAR == IS_VAR && UNEXPECTED(Z_ISERROR_P(var_ptr))) {
ZVAL_NULL(EX_VAR(opline->result.var));
ZEND_VM_NEXT_OPCODE();
}
@@ -14998,46 +15423,61 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_RETURN_SPEC_VAR_HANDLER(ZEND_O
{
USE_OPLINE
zval *retval_ptr;
+ zval *return_value;
zend_free_op free_op1;
retval_ptr = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1);
+ return_value = EX(return_value);
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);
- if (EX(return_value)) {
- ZVAL_NULL(EX(return_value));
+ if (return_value) {
+ ZVAL_NULL(return_value);
}
- } else if (!EX(return_value)) {
- if (IS_VAR == IS_VAR || IS_VAR == IS_TMP_VAR ) {
+ } else if (!return_value) {
+ if (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));
+ zval_dtor_func(Z_COUNTED_P(free_op1));
}
}
} else {
- if (IS_VAR == IS_CONST || IS_VAR == IS_TMP_VAR) {
- ZVAL_COPY_VALUE(EX(return_value), retval_ptr);
+ if ((IS_VAR & (IS_CONST|IS_TMP_VAR))) {
+ ZVAL_COPY_VALUE(return_value, retval_ptr);
if (IS_VAR == IS_CONST) {
- if (UNEXPECTED(Z_OPT_COPYABLE_P(EX(return_value)))) {
- zval_copy_ctor_func(EX(return_value));
+ if (UNEXPECTED(Z_OPT_REFCOUNTED_P(return_value))) {
+ Z_ADDREF_P(return_value);
}
}
} else if (IS_VAR == IS_CV) {
- ZVAL_DEREF(retval_ptr);
- ZVAL_COPY(EX(return_value), retval_ptr);
+ if (Z_OPT_REFCOUNTED_P(retval_ptr)) {
+ if (EXPECTED(!Z_OPT_ISREF_P(retval_ptr))) {
+ ZVAL_COPY_VALUE(return_value, retval_ptr);
+ if (EXPECTED(!(EX_CALL_INFO() & ZEND_CALL_CODE))) {
+ ZVAL_NULL(retval_ptr);
+ } else {
+ Z_ADDREF_P(return_value);
+ }
+ } else {
+ retval_ptr = Z_REFVAL_P(retval_ptr);
+ ZVAL_COPY(return_value, retval_ptr);
+ }
+ } else {
+ ZVAL_COPY_VALUE(return_value, retval_ptr);
+ }
} else /* if (IS_VAR == IS_VAR) */ {
if (UNEXPECTED(Z_ISREF_P(retval_ptr))) {
zend_refcounted *ref = Z_COUNTED_P(retval_ptr);
retval_ptr = Z_REFVAL_P(retval_ptr);
- ZVAL_COPY_VALUE(EX(return_value), retval_ptr);
+ ZVAL_COPY_VALUE(return_value, retval_ptr);
if (UNEXPECTED(--GC_REFCOUNT(ref) == 0)) {
efree_size(ref, sizeof(zend_reference));
} else if (Z_OPT_REFCOUNTED_P(retval_ptr)) {
Z_ADDREF_P(retval_ptr);
}
} else {
- ZVAL_COPY_VALUE(EX(return_value), retval_ptr);
+ ZVAL_COPY_VALUE(return_value, retval_ptr);
}
}
}
@@ -15053,21 +15493,23 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_RETURN_BY_REF_SPEC_VAR_HANDLER
SAVE_OPLINE();
do {
- if (IS_VAR == IS_CONST || IS_VAR == IS_TMP_VAR ||
+ if ((IS_VAR & (IS_CONST|IS_TMP_VAR)) ||
(IS_VAR == IS_VAR && opline->extended_value == ZEND_RETURNS_VALUE)) {
/* Not supposed to happen, but we'll allow it */
zend_error(E_NOTICE, "Only variable references should be returned by reference");
retval_ptr = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1);
if (!EX(return_value)) {
- if (IS_VAR == IS_TMP_VAR) {
- zval_ptr_dtor_nogc(free_op1);
- }
+ zval_ptr_dtor_nogc(free_op1);
} else {
- ZVAL_COPY_VALUE(EX(return_value), retval_ptr);
- Z_VAR_FLAGS_P(EX(return_value)) = IS_VAR_RET_REF;
- if (IS_VAR != IS_TMP_VAR) {
- zval_opt_copy_ctor_no_imm(EX(return_value));
+ if (IS_VAR == IS_VAR && UNEXPECTED(Z_ISREF_P(retval_ptr))) {
+ ZVAL_COPY_VALUE(EX(return_value), retval_ptr);
+ break;
+ }
+
+ ZVAL_NEW_REF(EX(return_value), retval_ptr);
+ if (IS_VAR == IS_CONST) {
+ if (Z_REFCOUNTED_P(retval_ptr)) Z_ADDREF_P(retval_ptr);
}
}
break;
@@ -15075,20 +15517,14 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_RETURN_BY_REF_SPEC_VAR_HANDLER
retval_ptr = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1);
- if (IS_VAR == IS_VAR && UNEXPECTED(retval_ptr == NULL)) {
- zend_throw_error(NULL, "Cannot return string offsets by reference");
- HANDLE_EXCEPTION();
- }
-
if (IS_VAR == IS_VAR) {
if (retval_ptr == &EG(uninitialized_zval) ||
- (opline->extended_value == ZEND_RETURNS_FUNCTION &&
- !(Z_VAR_FLAGS_P(retval_ptr) & IS_VAR_RET_REF))) {
+ (opline->extended_value == ZEND_RETURNS_FUNCTION && !Z_ISREF_P(retval_ptr))) {
zend_error(E_NOTICE, "Only variable references should be returned by reference");
if (EX(return_value)) {
ZVAL_NEW_REF(EX(return_value), retval_ptr);
- Z_VAR_FLAGS_P(EX(return_value)) = IS_VAR_RET_REF;
- if (Z_REFCOUNTED_P(retval_ptr)) Z_ADDREF_P(retval_ptr);
+ } else {
+ if (UNEXPECTED(free_op1)) {zval_ptr_dtor_nogc(free_op1);};
}
break;
}
@@ -15098,11 +15534,11 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_RETURN_BY_REF_SPEC_VAR_HANDLER
ZVAL_MAKE_REF(retval_ptr);
Z_ADDREF_P(retval_ptr);
ZVAL_REF(EX(return_value), Z_REF_P(retval_ptr));
- Z_VAR_FLAGS_P(EX(return_value)) = IS_VAR_RET_REF;
}
+
+ if (UNEXPECTED(free_op1)) {zval_ptr_dtor_nogc(free_op1);};
} while (0);
- if (UNEXPECTED(free_op1)) {zval_ptr_dtor_nogc(free_op1);};
ZEND_VM_TAIL_CALL(zend_leave_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU));
}
@@ -15118,11 +15554,11 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_GENERATOR_RETURN_SPEC_VAR_HAND
retval = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1);
/* Copy return value into generator->retval */
- if (IS_VAR == IS_CONST || IS_VAR == IS_TMP_VAR) {
+ if ((IS_VAR & (IS_CONST|IS_TMP_VAR))) {
ZVAL_COPY_VALUE(&generator->retval, retval);
if (IS_VAR == IS_CONST) {
- if (UNEXPECTED(Z_OPT_COPYABLE(generator->retval))) {
- zval_copy_ctor_func(&generator->retval);
+ if (UNEXPECTED(Z_OPT_REFCOUNTED(generator->retval))) {
+ Z_ADDREF(generator->retval);
}
}
} else if (IS_VAR == IS_CV) {
@@ -15236,31 +15672,96 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SEND_VAR_NO_REF_SPEC_VAR_HANDL
zend_free_op free_op1;
zval *varptr, *arg;
- if (!(opline->extended_value & ZEND_ARG_COMPILE_TIME_BOUND)) {
- if (!ARG_SHOULD_BE_SENT_BY_REF(EX(call)->func, opline->op2.num)) {
+ varptr = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1);
+ arg = ZEND_CALL_VAR(EX(call), opline->result.var);
+ ZVAL_COPY_VALUE(arg, varptr);
+
+ if (EXPECTED(Z_ISREF_P(varptr))) {
+ ZEND_VM_NEXT_OPCODE();
+ }
+
+ SAVE_OPLINE();
+ zend_error(E_NOTICE, "Only variables should be passed by reference");
+ ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SEND_VAR_NO_REF_EX_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zend_free_op free_op1;
+ zval *varptr, *arg;
+ uint32_t arg_num = opline->op2.num;
+
+ if (EXPECTED(0)) {
+ if (!QUICK_ARG_SHOULD_BE_SENT_BY_REF(EX(call)->func, arg_num)) {
ZEND_VM_TAIL_CALL(ZEND_SEND_VAR_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU));
}
- }
- varptr = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1);
+ varptr = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1);
+ arg = ZEND_CALL_VAR(EX(call), opline->result.var);
+ ZVAL_COPY_VALUE(arg, varptr);
- if (EXPECTED(Z_ISREF_P(varptr) ||
- ((opline->extended_value & ZEND_ARG_COMPILE_TIME_BOUND) ?
- (opline->extended_value & ZEND_ARG_SEND_SILENT) :
- ARG_MAY_BE_SENT_BY_REF(EX(call)->func, opline->op2.num
- )))) {
+ if (EXPECTED(Z_ISREF_P(varptr) ||
+ QUICK_ARG_MAY_BE_SENT_BY_REF(EX(call)->func, arg_num))) {
+ ZEND_VM_NEXT_OPCODE();
+ }
+ } else {
+ if (!ARG_SHOULD_BE_SENT_BY_REF(EX(call)->func, arg_num)) {
+ ZEND_VM_TAIL_CALL(ZEND_SEND_VAR_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU));
+ }
+
+ varptr = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1);
arg = ZEND_CALL_VAR(EX(call), opline->result.var);
ZVAL_COPY_VALUE(arg, varptr);
- ZEND_VM_NEXT_OPCODE();
+ if (EXPECTED(Z_ISREF_P(varptr) ||
+ ARG_MAY_BE_SENT_BY_REF(EX(call)->func, arg_num))) {
+ ZEND_VM_NEXT_OPCODE();
+ }
}
SAVE_OPLINE();
zend_error(E_NOTICE, "Only variables should be passed by reference");
+ ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
+}
- arg = ZEND_CALL_VAR(EX(call), opline->result.var);
- ZVAL_COPY_VALUE(arg, varptr);
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SEND_VAR_NO_REF_EX_SPEC_VAR_QUICK_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zend_free_op free_op1;
+ zval *varptr, *arg;
+ uint32_t arg_num = opline->op2.num;
+ if (EXPECTED(1)) {
+ if (!QUICK_ARG_SHOULD_BE_SENT_BY_REF(EX(call)->func, arg_num)) {
+ ZEND_VM_TAIL_CALL(ZEND_SEND_VAR_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU));
+ }
+
+ varptr = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1);
+ arg = ZEND_CALL_VAR(EX(call), opline->result.var);
+ ZVAL_COPY_VALUE(arg, varptr);
+
+ if (EXPECTED(Z_ISREF_P(varptr) ||
+ QUICK_ARG_MAY_BE_SENT_BY_REF(EX(call)->func, arg_num))) {
+ ZEND_VM_NEXT_OPCODE();
+ }
+ } else {
+ if (!ARG_SHOULD_BE_SENT_BY_REF(EX(call)->func, arg_num)) {
+ ZEND_VM_TAIL_CALL(ZEND_SEND_VAR_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU));
+ }
+
+ varptr = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1);
+ arg = ZEND_CALL_VAR(EX(call), opline->result.var);
+ ZVAL_COPY_VALUE(arg, varptr);
+
+ if (EXPECTED(Z_ISREF_P(varptr) ||
+ ARG_MAY_BE_SENT_BY_REF(EX(call)->func, arg_num))) {
+ ZEND_VM_NEXT_OPCODE();
+ }
+ }
+
+ SAVE_OPLINE();
+ zend_error(E_NOTICE, "Only variables should be passed by reference");
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
@@ -15273,16 +15774,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SEND_REF_SPEC_VAR_HANDLER(ZEND
SAVE_OPLINE();
varptr = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1);
- if (IS_VAR == IS_VAR && UNEXPECTED(varptr == NULL)) {
- zend_throw_error(NULL, "Only variables can be passed by reference");
- arg = ZEND_CALL_VAR(EX(call), opline->result.var);
- ZVAL_UNDEF(arg);
- HANDLE_EXCEPTION();
- }
-
arg = ZEND_CALL_VAR(EX(call), opline->result.var);
- if (IS_VAR == IS_VAR && UNEXPECTED(varptr == &EG(error_zval))) {
- ZVAL_NEW_REF(arg, &EG(uninitialized_zval));
+ if (IS_VAR == IS_VAR && UNEXPECTED(Z_ISERROR_P(varptr))) {
+ ZVAL_NEW_EMPTY_REF(arg);
+ ZVAL_NULL(Z_REFVAL_P(arg));
ZEND_VM_NEXT_OPCODE();
}
@@ -15306,7 +15801,56 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SEND_VAR_EX_SPEC_VAR_HANDLER(Z
zend_free_op free_op1;
uint32_t arg_num = opline->op2.num;
- if (EXPECTED(arg_num <= MAX_ARG_FLAG_NUM)) {
+ if (EXPECTED(0)) {
+ if (QUICK_ARG_SHOULD_BE_SENT_BY_REF(EX(call)->func, arg_num)) {
+ goto send_var_by_ref;
+ }
+ } else if (ARG_SHOULD_BE_SENT_BY_REF(EX(call)->func, arg_num)) {
+send_var_by_ref:
+ ZEND_VM_TAIL_CALL(ZEND_SEND_REF_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU));
+ }
+
+ varptr = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1);
+ if (IS_VAR == IS_CV && UNEXPECTED(Z_TYPE_INFO_P(varptr) == IS_UNDEF)) {
+ SAVE_OPLINE();
+ GET_OP1_UNDEF_CV(varptr, BP_VAR_R);
+ arg = ZEND_CALL_VAR(EX(call), opline->result.var);
+ ZVAL_NULL(arg);
+ ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
+ }
+
+ arg = ZEND_CALL_VAR(EX(call), opline->result.var);
+
+ if (IS_VAR == IS_CV) {
+ ZVAL_OPT_DEREF(varptr);
+ ZVAL_COPY(arg, varptr);
+ } else /* if (IS_VAR == IS_VAR) */ {
+ if (UNEXPECTED(Z_ISREF_P(varptr))) {
+ zend_refcounted *ref = Z_COUNTED_P(varptr);
+
+ varptr = Z_REFVAL_P(varptr);
+ ZVAL_COPY_VALUE(arg, varptr);
+ if (UNEXPECTED(--GC_REFCOUNT(ref) == 0)) {
+ efree_size(ref, sizeof(zend_reference));
+ } else if (Z_OPT_REFCOUNTED_P(arg)) {
+ Z_ADDREF_P(arg);
+ }
+ } else {
+ ZVAL_COPY_VALUE(arg, varptr);
+ }
+ }
+
+ ZEND_VM_NEXT_OPCODE();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SEND_VAR_EX_SPEC_VAR_QUICK_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zval *varptr, *arg;
+ zend_free_op free_op1;
+ uint32_t arg_num = opline->op2.num;
+
+ if (EXPECTED(1)) {
if (QUICK_ARG_SHOULD_BE_SENT_BY_REF(EX(call)->func, arg_num)) {
goto send_var_by_ref;
}
@@ -15371,14 +15915,13 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SEND_USER_SPEC_VAR_HANDLER(ZEN
if (ZEND_CALL_INFO(EX(call)) & ZEND_CALL_CLOSURE) {
OBJ_RELEASE((zend_object*)EX(call)->func->common.prototype);
}
- if (Z_OBJ(EX(call)->This)) {
+ if (Z_TYPE(EX(call)->This) == IS_OBJECT) {
OBJ_RELEASE(Z_OBJ(EX(call)->This));
}
ZVAL_UNDEF(param);
EX(call)->func = (zend_function*)&zend_pass_function;
- EX(call)->called_scope = NULL;
Z_OBJ(EX(call)->This) = NULL;
- ZEND_SET_CALL_INFO(EX(call), ZEND_CALL_INFO(EX(call)) & ~ZEND_CALL_RELEASE_THIS);
+ ZEND_SET_CALL_INFO(EX(call), 0, ZEND_CALL_INFO(EX(call)) & ~ZEND_CALL_RELEASE_THIS);
zval_ptr_dtor_nogc(free_op1);
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
@@ -15400,9 +15943,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SEND_USER_SPEC_VAR_HANDLER(ZEN
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_NEW_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
- zval object_zval;
+ zval *result;
zend_function *constructor;
zend_class_entry *ce;
+ zend_execute_data *call;
SAVE_OPLINE();
if (IS_VAR == IS_CONST) {
@@ -15414,39 +15958,50 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_NEW_SPEC_VAR_HANDLER(ZEND_OPCO
}
CACHE_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)), ce);
}
+ } else if (IS_VAR == IS_UNUSED) {
+ ce = zend_fetch_class(NULL, opline->op1.num);
+ if (UNEXPECTED(ce == NULL)) {
+ ZEND_ASSERT(EG(exception));
+ HANDLE_EXCEPTION();
+ }
} else {
ce = Z_CE_P(EX_VAR(opline->op1.var));
}
- if (UNEXPECTED(object_init_ex(&object_zval, ce) != SUCCESS)) {
+
+ result = EX_VAR(opline->result.var);
+ if (UNEXPECTED(object_init_ex(result, ce) != SUCCESS)) {
HANDLE_EXCEPTION();
}
- constructor = Z_OBJ_HT(object_zval)->get_constructor(Z_OBJ(object_zval));
+ constructor = Z_OBJ_HT_P(result)->get_constructor(Z_OBJ_P(result));
if (constructor == NULL) {
- if (EXPECTED(RETURN_VALUE_USED(opline))) {
- ZVAL_COPY_VALUE(EX_VAR(opline->result.var), &object_zval);
- } else {
- OBJ_RELEASE(Z_OBJ(object_zval));
+ /* If there are no arguments, skip over the DO_FCALL opcode. We check if the next
+ * opcode is DO_FCALL in case EXT instructions are used. */
+ if (EXPECTED(opline->extended_value == 0 && (opline+1)->opcode == ZEND_DO_FCALL)) {
+ ZEND_VM_NEXT_OPCODE_EX(1, 2);
}
- ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2));
+
+ /* Perform a dummy function call */
+ call = zend_vm_stack_push_call_frame(
+ ZEND_CALL_FUNCTION, (zend_function *) &zend_pass_function,
+ opline->extended_value, NULL, NULL);
} else {
+ if (EXPECTED(constructor->type == ZEND_USER_FUNCTION) && UNEXPECTED(!constructor->op_array.run_time_cache)) {
+ init_func_run_time_cache(&constructor->op_array);
+ }
/* We are not handling overloaded classes right now */
- zend_execute_data *call = zend_vm_stack_push_call_frame(
- ZEND_CALL_FUNCTION | ZEND_CALL_RELEASE_THIS | ZEND_CALL_CTOR |
- (EXPECTED(RETURN_VALUE_USED(opline)) ? 0 : ZEND_CALL_CTOR_RESULT_UNUSED),
+ call = zend_vm_stack_push_call_frame(
+ ZEND_CALL_FUNCTION | ZEND_CALL_RELEASE_THIS | ZEND_CALL_CTOR,
constructor,
opline->extended_value,
ce,
- Z_OBJ(object_zval));
- call->prev_execute_data = EX(call);
- EX(call) = call;
-
- if (EXPECTED(RETURN_VALUE_USED(opline))) {
- ZVAL_COPY(EX_VAR(opline->result.var), &object_zval);
- }
-
- ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
+ Z_OBJ_P(result));
+ Z_ADDREF_P(result);
}
+
+ call->prev_execute_data = EX(call);
+ EX(call) = call;
+ ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CAST_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@@ -15497,11 +16052,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CAST_SPEC_VAR_HANDLER(ZEND_OPC
if (Z_TYPE_P(expr) == opline->extended_value) {
ZVAL_COPY_VALUE(result, expr);
if (IS_VAR == IS_CONST) {
- if (UNEXPECTED(Z_OPT_COPYABLE_P(result))) {
- zval_copy_ctor_func(result);
- }
+ if (UNEXPECTED(Z_OPT_REFCOUNTED_P(result))) Z_ADDREF_P(result);
} else if (IS_VAR != IS_TMP_VAR) {
- if (Z_OPT_REFCOUNTED_P(expr)) Z_ADDREF_P(expr);
+ if (Z_OPT_REFCOUNTED_P(result)) Z_ADDREF_P(result);
}
zval_ptr_dtor_nogc(free_op1);
@@ -15515,9 +16068,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CAST_SPEC_VAR_HANDLER(ZEND_OPC
if (Z_TYPE_P(expr) != IS_NULL) {
expr = zend_hash_index_add_new(Z_ARRVAL_P(result), 0, expr);
if (IS_VAR == IS_CONST) {
- if (UNEXPECTED(Z_OPT_COPYABLE_P(expr))) {
- zval_copy_ctor_func(expr);
- }
+ if (UNEXPECTED(Z_OPT_REFCOUNTED_P(expr))) Z_ADDREF_P(expr);
} else {
if (Z_OPT_REFCOUNTED_P(expr)) Z_ADDREF_P(expr);
}
@@ -15531,11 +16082,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CAST_SPEC_VAR_HANDLER(ZEND_OPC
if (Z_TYPE_P(expr) != IS_ARRAY) {
object_init(result);
if (Z_TYPE_P(expr) != IS_NULL) {
- expr = zend_hash_str_add_new(Z_OBJPROP_P(result), "scalar", sizeof("scalar")-1, expr);
+ expr = zend_hash_add_new(Z_OBJPROP_P(result), CG(known_strings)[ZEND_STR_SCALAR], expr);
if (IS_VAR == IS_CONST) {
- if (UNEXPECTED(Z_OPT_COPYABLE_P(expr))) {
- zval_copy_ctor_func(expr);
- }
+ if (UNEXPECTED(Z_OPT_REFCOUNTED_P(expr))) Z_ADDREF_P(expr);
} else {
if (Z_OPT_REFCOUNTED_P(expr)) Z_ADDREF_P(expr);
}
@@ -15581,6 +16130,13 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FE_RESET_R_SPEC_VAR_HANDLER(ZE
if (IS_VAR != IS_TMP_VAR) {
Z_ADDREF_P(array_ptr);
}
+ if (Z_OBJ_P(array_ptr)->properties
+ && UNEXPECTED(GC_REFCOUNT(Z_OBJ_P(array_ptr)->properties) > 1)) {
+ if (EXPECTED(!(GC_FLAGS(Z_OBJ_P(array_ptr)->properties) & IS_ARRAY_IMMUTABLE))) {
+ GC_REFCOUNT(Z_OBJ_P(array_ptr)->properties)--;
+ }
+ Z_OBJ_P(array_ptr)->properties = zend_array_dup(Z_OBJ_P(array_ptr)->properties);
+ }
fe_ht = Z_OBJPROP_P(array_ptr);
pos = 0;
p = fe_ht->arData;
@@ -15727,6 +16283,13 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_VAR_HANDLER(Z
array_ptr = EX_VAR(opline->result.var);
ZVAL_COPY_VALUE(array_ptr, array_ref);
}
+ if (Z_OBJ_P(array_ptr)->properties
+ && UNEXPECTED(GC_REFCOUNT(Z_OBJ_P(array_ptr)->properties) > 1)) {
+ if (EXPECTED(!(GC_FLAGS(Z_OBJ_P(array_ptr)->properties) & IS_ARRAY_IMMUTABLE))) {
+ GC_REFCOUNT(Z_OBJ_P(array_ptr)->properties)--;
+ }
+ Z_OBJ_P(array_ptr)->properties = zend_array_dup(Z_OBJ_P(array_ptr)->properties);
+ }
fe_ht = Z_OBJPROP_P(array_ptr);
p = fe_ht->arData;
while (1) {
@@ -15860,7 +16423,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FE_FETCH_R_SPEC_VAR_HANDLER(ZE
break;
}
Z_FE_POS_P(array) = pos + 1;
- if (opline->result_type == IS_TMP_VAR) {
+ if (opline->result_type & (IS_TMP_VAR|IS_CV)) {
if (!p->key) {
ZVAL_LONG(EX_VAR(opline->result.var), p->h);
} else {
@@ -15904,7 +16467,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FE_FETCH_R_SPEC_VAR_HANDLER(ZE
pos++;
p++;
}
- if (opline->result_type == IS_TMP_VAR) {
+ if (opline->result_type & (IS_TMP_VAR|IS_CV)) {
if (UNEXPECTED(!p->key)) {
ZVAL_LONG(EX_VAR(opline->result.var), p->h);
} else if (ZSTR_VAL(p->key)[0]) {
@@ -15939,13 +16502,11 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FE_FETCH_R_SPEC_VAR_HANDLER(ZE
* In case that ever happens we need an additional flag. */
iter->funcs->move_forward(iter);
if (UNEXPECTED(EG(exception) != NULL)) {
- zval_ptr_dtor(array);
HANDLE_EXCEPTION();
}
if (UNEXPECTED(iter->funcs->valid(iter) == FAILURE)) {
/* reached end of iteration */
if (UNEXPECTED(EG(exception) != NULL)) {
- zval_ptr_dtor(array);
HANDLE_EXCEPTION();
}
goto fe_fetch_r_exit;
@@ -15953,18 +16514,16 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FE_FETCH_R_SPEC_VAR_HANDLER(ZE
}
value = iter->funcs->get_current_data(iter);
if (UNEXPECTED(EG(exception) != NULL)) {
- zval_ptr_dtor(array);
HANDLE_EXCEPTION();
}
if (!value) {
/* failure in get_current_data */
goto fe_fetch_r_exit;
}
- if (opline->result_type == IS_TMP_VAR) {
+ if (opline->result_type & (IS_TMP_VAR|IS_CV)) {
if (iter->funcs->get_current_key) {
iter->funcs->get_current_key(iter, EX_VAR(opline->result.var));
if (UNEXPECTED(EG(exception) != NULL)) {
- zval_ptr_dtor(array);
HANDLE_EXCEPTION();
}
} else {
@@ -16038,7 +16597,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FE_FETCH_RW_SPEC_VAR_HANDLER(Z
}
break;
}
- if (opline->result_type == IS_TMP_VAR) {
+ if (opline->result_type & (IS_TMP_VAR|IS_CV)) {
if (!p->key) {
ZVAL_LONG(EX_VAR(opline->result.var), p->h);
} else {
@@ -16096,7 +16655,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FE_FETCH_RW_SPEC_VAR_HANDLER(Z
pos++;
p++;
}
- if (opline->result_type == IS_TMP_VAR) {
+ if (opline->result_type & (IS_TMP_VAR|IS_CV)) {
if (UNEXPECTED(!p->key)) {
ZVAL_LONG(EX_VAR(opline->result.var), p->h);
} else if (ZSTR_VAL(p->key)[0]) {
@@ -16131,13 +16690,11 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FE_FETCH_RW_SPEC_VAR_HANDLER(Z
* In case that ever happens we need an additional flag. */
iter->funcs->move_forward(iter);
if (UNEXPECTED(EG(exception) != NULL)) {
- zval_ptr_dtor(array);
HANDLE_EXCEPTION();
}
if (UNEXPECTED(iter->funcs->valid(iter) == FAILURE)) {
/* reached end of iteration */
if (UNEXPECTED(EG(exception) != NULL)) {
- zval_ptr_dtor(array);
HANDLE_EXCEPTION();
}
goto fe_fetch_w_exit;
@@ -16145,18 +16702,16 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FE_FETCH_RW_SPEC_VAR_HANDLER(Z
}
value = iter->funcs->get_current_data(iter);
if (UNEXPECTED(EG(exception) != NULL)) {
- zval_ptr_dtor(array);
HANDLE_EXCEPTION();
}
if (!value) {
/* failure in get_current_data */
goto fe_fetch_w_exit;
}
- if (opline->result_type == IS_TMP_VAR) {
+ if (opline->result_type & (IS_TMP_VAR|IS_CV)) {
if (iter->funcs->get_current_key) {
iter->funcs->get_current_key(iter, EX_VAR(opline->result.var));
if (UNEXPECTED(EG(exception) != NULL)) {
- zval_ptr_dtor(array);
HANDLE_EXCEPTION();
}
} else {
@@ -16216,20 +16771,20 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_JMP_SET_SPEC_VAR_HANDLER(ZEND_
value = Z_REFVAL_P(value);
}
if (i_zend_is_true(value)) {
- ZVAL_COPY_VALUE(EX_VAR(opline->result.var), value);
+ zval *result = EX_VAR(opline->result.var);
+
+ ZVAL_COPY_VALUE(result, value);
if (IS_VAR == IS_CONST) {
- if (UNEXPECTED(Z_OPT_COPYABLE_P(value))) {
- zval_copy_ctor_func(EX_VAR(opline->result.var));
- }
+ if (UNEXPECTED(Z_OPT_REFCOUNTED_P(result))) Z_ADDREF_P(result);
} else if (IS_VAR == IS_CV) {
- if (Z_OPT_REFCOUNTED_P(value)) Z_ADDREF_P(value);
+ if (Z_OPT_REFCOUNTED_P(result)) Z_ADDREF_P(result);
} else if (IS_VAR == IS_VAR && ref) {
zend_reference *r = Z_REF_P(ref);
if (UNEXPECTED(--GC_REFCOUNT(r) == 0)) {
efree_size(r, sizeof(zend_reference));
- } else if (Z_OPT_REFCOUNTED_P(value)) {
- Z_ADDREF_P(value);
+ } else if (Z_OPT_REFCOUNTED_P(result)) {
+ Z_ADDREF_P(result);
}
}
ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2));
@@ -16257,20 +16812,19 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_COALESCE_SPEC_VAR_HANDLER(ZEND
}
if (Z_TYPE_P(value) > IS_NULL) {
- ZVAL_COPY_VALUE(EX_VAR(opline->result.var), value);
+ zval *result = EX_VAR(opline->result.var);
+ ZVAL_COPY_VALUE(result, value);
if (IS_VAR == IS_CONST) {
- if (UNEXPECTED(Z_OPT_COPYABLE_P(value))) {
- zval_copy_ctor_func(EX_VAR(opline->result.var));
- }
+ if (UNEXPECTED(Z_OPT_REFCOUNTED_P(result))) Z_ADDREF_P(result);
} else if (IS_VAR == IS_CV) {
- if (Z_OPT_REFCOUNTED_P(value)) Z_ADDREF_P(value);
+ if (Z_OPT_REFCOUNTED_P(result)) Z_ADDREF_P(result);
} else if (IS_VAR == IS_VAR && ref) {
zend_reference *r = Z_REF_P(ref);
if (UNEXPECTED(--GC_REFCOUNT(r) == 0)) {
efree_size(r, sizeof(zend_reference));
- } else if (Z_OPT_REFCOUNTED_P(value)) {
- Z_ADDREF_P(value);
+ } else if (Z_OPT_REFCOUNTED_P(result)) {
+ Z_ADDREF_P(result);
}
}
ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2));
@@ -16312,8 +16866,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_QM_ASSIGN_SPEC_VAR_HANDLER(ZEN
} else {
ZVAL_COPY_VALUE(result, value);
if (IS_VAR == IS_CONST) {
- if (UNEXPECTED(Z_OPT_COPYABLE_P(value))) {
- zval_copy_ctor_func(result);
+ if (UNEXPECTED(Z_OPT_REFCOUNTED_P(result))) {
+ Z_ADDREF_P(result);
}
}
}
@@ -16432,7 +16986,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_TYPE_CHECK_SPEC_VAR_HANDLER(ZE
if (IS_VAR != IS_CONST && UNEXPECTED(Z_TYPE_P(value) == IS_OBJECT)) {
zend_class_entry *ce = Z_OBJCE_P(value);
- if (UNEXPECTED(ZSTR_LEN(ce->name) != sizeof("__PHP_Incomplete_Class") - 1) ||
+ if (EXPECTED(ZSTR_LEN(ce->name) != sizeof("__PHP_Incomplete_Class") - 1) ||
EXPECTED(memcmp(ZSTR_VAL(ce->name), "__PHP_Incomplete_Class", sizeof("__PHP_Incomplete_Class") - 1) != 0)) {
result = 1;
}
@@ -16455,6 +17009,80 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_TYPE_CHECK_SPEC_VAR_HANDLER(ZE
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SEND_VAR_SIMPLE_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zval *varptr, *arg;
+ zend_free_op free_op1;
+
+ varptr = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1);
+ arg = ZEND_CALL_VAR(EX(call), opline->result.var);
+
+ if (IS_VAR == IS_CV) {
+ ZVAL_COPY(arg, varptr);
+ } else /* if (IS_VAR == IS_VAR) */ {
+ ZVAL_COPY_VALUE(arg, varptr);
+ }
+
+ ZEND_VM_NEXT_OPCODE();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SEND_VAR_EX_SIMPLE_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zval *varptr, *arg;
+ zend_free_op free_op1;
+ uint32_t arg_num = opline->op2.num;
+
+ if (EXPECTED(0)) {
+ if (QUICK_ARG_SHOULD_BE_SENT_BY_REF(EX(call)->func, arg_num)) {
+ goto send_var_by_ref_simple;
+ }
+ } else if (ARG_SHOULD_BE_SENT_BY_REF(EX(call)->func, arg_num)) {
+send_var_by_ref_simple:
+ ZEND_VM_TAIL_CALL(ZEND_SEND_REF_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU));
+ }
+
+ varptr = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1);
+ arg = ZEND_CALL_VAR(EX(call), opline->result.var);
+
+ if (IS_VAR == IS_CV) {
+ ZVAL_COPY(arg, varptr);
+ } else /* if (IS_VAR == IS_VAR) */ {
+ ZVAL_COPY_VALUE(arg, varptr);
+ }
+
+ ZEND_VM_NEXT_OPCODE();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SEND_VAR_EX_SIMPLE_SPEC_VAR_QUICK_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zval *varptr, *arg;
+ zend_free_op free_op1;
+ uint32_t arg_num = opline->op2.num;
+
+ if (EXPECTED(1)) {
+ if (QUICK_ARG_SHOULD_BE_SENT_BY_REF(EX(call)->func, arg_num)) {
+ goto send_var_by_ref_simple;
+ }
+ } else if (ARG_SHOULD_BE_SENT_BY_REF(EX(call)->func, arg_num)) {
+send_var_by_ref_simple:
+ ZEND_VM_TAIL_CALL(ZEND_SEND_REF_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU));
+ }
+
+ varptr = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1);
+ arg = ZEND_CALL_VAR(EX(call), opline->result.var);
+
+ if (IS_VAR == IS_CV) {
+ ZVAL_COPY(arg, varptr);
+ } else /* if (IS_VAR == IS_VAR) */ {
+ ZVAL_COPY_VALUE(arg, varptr);
+ }
+
+ ZEND_VM_NEXT_OPCODE();
+}
+
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_IDENTICAL_SPEC_VAR_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
@@ -16503,7 +17131,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_binary_assign_op_obj_helper_SP
SAVE_OPLINE();
object = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1);
- if (IS_VAR == IS_UNUSED && UNEXPECTED(Z_OBJ_P(object) == NULL)) {
+ if (IS_VAR == IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
FREE_UNFETCHED_OP((opline+1)->op1_type, (opline+1)->op1.var);
@@ -16512,13 +17140,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_binary_assign_op_obj_helper_SP
property = EX_CONSTANT(opline->op2);
- if (IS_VAR == IS_VAR && UNEXPECTED(object == NULL)) {
- zend_throw_error(NULL, "Cannot use string offset as an object");
- FREE_UNFETCHED_OP((opline+1)->op1_type, (opline+1)->op1.var);
-
- HANDLE_EXCEPTION();
- }
-
do {
value = get_zval_ptr_r((opline+1)->op1_type, (opline+1)->op1, execute_data, &free_op_data1);
@@ -16536,7 +17157,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_binary_assign_op_obj_helper_SP
/* here we are sure we are dealing with an object */
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)) {
- if (UNEXPECTED(zptr == &EG(error_zval))) {
+ if (UNEXPECTED(Z_ISERROR_P(zptr))) {
if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
ZVAL_NULL(EX_VAR(opline->result.var));
}
@@ -16565,66 +17186,85 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_binary_assign_op_dim_helper_SP
{
USE_OPLINE
zend_free_op free_op1, free_op_data1;
- zval *var_ptr, rv;
+ zval *var_ptr;
zval *value, *container, *dim;
SAVE_OPLINE();
container = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1);
- if (IS_VAR == IS_UNUSED && UNEXPECTED(Z_OBJ_P(container) == NULL)) {
- zend_throw_error(NULL, "Using $this when not in object context");
- FREE_UNFETCHED_OP((opline+1)->op1_type, (opline+1)->op1.var);
- HANDLE_EXCEPTION();
- }
- if (IS_VAR == IS_VAR && UNEXPECTED(container == NULL)) {
- zend_throw_error(NULL, "Cannot use string offset as an array");
- FREE_UNFETCHED_OP((opline+1)->op1_type, (opline+1)->op1.var);
-
- HANDLE_EXCEPTION();
- }
-
- dim = EX_CONSTANT(opline->op2);
+ if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) {
+assign_dim_op_array:
+ SEPARATE_ARRAY(container);
+assign_dim_op_new_array:
+ if (IS_CONST == IS_UNUSED) {
+ var_ptr = zend_hash_next_index_insert(Z_ARRVAL_P(container), &EG(uninitialized_zval));
+ if (UNEXPECTED(!var_ptr)) {
+ zend_error(E_WARNING, "Cannot add element to the array as the next element is already occupied");
+ goto assign_dim_op_ret_null;
+ }
+ } else {
+ dim = EX_CONSTANT(opline->op2);
- do {
- if (IS_VAR == IS_UNUSED || UNEXPECTED(Z_TYPE_P(container) != IS_ARRAY)) {
- if (IS_VAR != IS_UNUSED) {
- ZVAL_DEREF(container);
+ if (IS_CONST == IS_CONST) {
+ var_ptr = zend_fetch_dimension_address_inner_RW_CONST(Z_ARRVAL_P(container), dim);
+ } else {
+ var_ptr = zend_fetch_dimension_address_inner_RW(Z_ARRVAL_P(container), dim);
}
- if (IS_VAR == IS_UNUSED || EXPECTED(Z_TYPE_P(container) == IS_OBJECT)) {
- value = get_zval_ptr_r((opline+1)->op1_type, (opline+1)->op1, execute_data, &free_op_data1);
- zend_binary_assign_op_obj_dim(container, dim, value, UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL, binary_op);
- break;
+ if (UNEXPECTED(!var_ptr)) {
+ goto assign_dim_op_ret_null;
}
+ ZVAL_DEREF(var_ptr);
+ SEPARATE_ZVAL_NOREF(var_ptr);
}
- zend_fetch_dimension_address_RW(&rv, container, dim, IS_CONST);
value = get_zval_ptr_r((opline+1)->op1_type, (opline+1)->op1, execute_data, &free_op_data1);
- ZEND_ASSERT(Z_TYPE(rv) == IS_INDIRECT);
- var_ptr = Z_INDIRECT(rv);
- if (UNEXPECTED(var_ptr == NULL)) {
- zend_throw_error(NULL, "Cannot use assign-op operators with overloaded objects nor string offsets");
+ binary_op(var_ptr, var_ptr, value);
- FREE_OP(free_op_data1);
- if (UNEXPECTED(free_op1)) {zval_ptr_dtor_nogc(free_op1);};
- HANDLE_EXCEPTION();
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), var_ptr);
}
-
- if (UNEXPECTED(var_ptr == &EG(error_zval))) {
- if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
- ZVAL_NULL(EX_VAR(opline->result.var));
+ } else {
+ if (EXPECTED(Z_ISREF_P(container))) {
+ container = Z_REFVAL_P(container);
+ if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) {
+ goto assign_dim_op_array;
}
- } else {
- ZVAL_DEREF(var_ptr);
- SEPARATE_ZVAL_NOREF(var_ptr);
+ } else if (IS_VAR == IS_CV && UNEXPECTED(Z_TYPE_INFO_P(container) == IS_UNDEF)) {
+ container = GET_OP1_UNDEF_CV(container, BP_VAR_RW);
+assign_dim_op_convert_to_array:
+ ZVAL_NEW_ARR(container);
+ zend_hash_init(Z_ARRVAL_P(container), 8, NULL, ZVAL_PTR_DTOR, 0);
+ goto assign_dim_op_new_array;
+ }
- binary_op(var_ptr, var_ptr, value);
+ dim = EX_CONSTANT(opline->op2);
- if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
- ZVAL_COPY(EX_VAR(opline->result.var), var_ptr);
+ if (EXPECTED(Z_TYPE_P(container) == IS_OBJECT)) {
+ value = get_zval_ptr_r((opline+1)->op1_type, (opline+1)->op1, execute_data, &free_op_data1);
+ zend_binary_assign_op_obj_dim(container, dim, value, UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL, binary_op);
+ } else {
+ if (UNEXPECTED(Z_TYPE_P(container) == IS_STRING)) {
+ if (IS_CONST == IS_UNUSED) {
+ zend_throw_error(NULL, "[] operator not supported for strings");
+ } else {
+ zend_check_string_offset(dim, BP_VAR_RW);
+ zend_wrong_string_offset();
+ }
+ } else if (EXPECTED(Z_TYPE_P(container) <= IS_FALSE)) {
+ goto assign_dim_op_convert_to_array;
+ } else {
+ if (UNEXPECTED(IS_VAR != IS_VAR || EXPECTED(!Z_ISERROR_P(container)))) {
+ zend_error(E_WARNING, "Cannot use a scalar value as an array");
+ }
+assign_dim_op_ret_null:
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
}
+ value = get_zval_ptr_r((opline+1)->op1_type, (opline+1)->op1, execute_data, &free_op_data1);
}
- } while (0);
+ }
FREE_OP(free_op_data1);
if (UNEXPECTED(free_op1)) {zval_ptr_dtor_nogc(free_op1);};
@@ -16642,13 +17282,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_binary_assign_op_helper_SPEC_V
value = EX_CONSTANT(opline->op2);
var_ptr = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1);
- if (IS_VAR == IS_VAR && UNEXPECTED(var_ptr == NULL)) {
- zend_throw_error(NULL, "Cannot use assign-op operators with overloaded objects nor string offsets");
-
- HANDLE_EXCEPTION();
- }
-
- if (IS_VAR == IS_VAR && UNEXPECTED(var_ptr == &EG(error_zval))) {
+ if (IS_VAR == IS_VAR && UNEXPECTED(Z_ISERROR_P(var_ptr))) {
if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
ZVAL_NULL(EX_VAR(opline->result.var));
}
@@ -16673,15 +17307,52 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_ADD_SPEC_VAR_CONST_HAND
USE_OPLINE
# if 0 || (IS_VAR != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
+ if (EXPECTED(1)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_VAR_CONST(add_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CONST(add_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_CONST(add_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CONST(add_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_ADD_SPEC_VAR_CONST_DIM_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || (IS_CONST != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_VAR != IS_UNUSED)
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_VAR_CONST(add_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+ if (EXPECTED(1)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CONST(add_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_CONST(add_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CONST(add_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_ADD_SPEC_VAR_CONST_OBJ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || (IS_CONST != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_VAR != IS_UNUSED)
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_VAR_CONST(add_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CONST(add_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_CONST(add_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_CONST(add_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#else
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CONST(add_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#endif
@@ -16693,15 +17364,52 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_SUB_SPEC_VAR_CONST_HAND
USE_OPLINE
# if 0 || (IS_VAR != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
+ if (EXPECTED(1)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_VAR_CONST(sub_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CONST(sub_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_CONST(sub_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CONST(sub_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_SUB_SPEC_VAR_CONST_DIM_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || (IS_CONST != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_VAR != IS_UNUSED)
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_VAR_CONST(sub_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(1)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CONST(sub_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_CONST(sub_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_CONST(sub_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CONST(sub_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_SUB_SPEC_VAR_CONST_OBJ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || (IS_CONST != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_VAR != IS_UNUSED)
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_VAR_CONST(sub_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CONST(sub_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_CONST(sub_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#else
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CONST(sub_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#endif
@@ -16713,15 +17421,52 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_MUL_SPEC_VAR_CONST_HAND
USE_OPLINE
# if 0 || (IS_VAR != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
+ if (EXPECTED(1)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_VAR_CONST(mul_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CONST(mul_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_CONST(mul_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CONST(mul_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_MUL_SPEC_VAR_CONST_DIM_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || (IS_CONST != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_VAR != IS_UNUSED)
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_VAR_CONST(mul_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(1)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CONST(mul_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_CONST(mul_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CONST(mul_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_MUL_SPEC_VAR_CONST_OBJ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || (IS_CONST != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_VAR != IS_UNUSED)
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_VAR_CONST(mul_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CONST(mul_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_CONST(mul_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_CONST(mul_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#else
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CONST(mul_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#endif
@@ -16733,15 +17478,52 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIV_SPEC_VAR_CONST_HAND
USE_OPLINE
# if 0 || (IS_VAR != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
+ if (EXPECTED(1)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_VAR_CONST(div_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CONST(div_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_CONST(div_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CONST(div_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIV_SPEC_VAR_CONST_DIM_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || (IS_CONST != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_VAR != IS_UNUSED)
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_VAR_CONST(div_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+ if (EXPECTED(1)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CONST(div_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_CONST(div_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CONST(div_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIV_SPEC_VAR_CONST_OBJ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || (IS_CONST != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_VAR != IS_UNUSED)
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_VAR_CONST(div_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CONST(div_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_CONST(div_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_CONST(div_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#else
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CONST(div_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#endif
@@ -16753,15 +17535,52 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_MOD_SPEC_VAR_CONST_HAND
USE_OPLINE
# if 0 || (IS_VAR != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
+ if (EXPECTED(1)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_VAR_CONST(mod_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CONST(mod_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_CONST(mod_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CONST(mod_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_MOD_SPEC_VAR_CONST_DIM_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || (IS_CONST != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_VAR != IS_UNUSED)
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_VAR_CONST(mod_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(1)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CONST(mod_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_CONST(mod_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CONST(mod_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_MOD_SPEC_VAR_CONST_OBJ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || (IS_CONST != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_VAR != IS_UNUSED)
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_VAR_CONST(mod_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CONST(mod_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_CONST(mod_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_CONST(mod_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#else
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CONST(mod_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#endif
@@ -16773,15 +17592,52 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_SL_SPEC_VAR_CONST_HANDL
USE_OPLINE
# if 0 || (IS_VAR != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
+ if (EXPECTED(1)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_VAR_CONST(shift_left_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CONST(shift_left_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_CONST(shift_left_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CONST(shift_left_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_SL_SPEC_VAR_CONST_DIM_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || (IS_CONST != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_VAR != IS_UNUSED)
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_VAR_CONST(shift_left_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+ if (EXPECTED(1)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CONST(shift_left_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_CONST(shift_left_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CONST(shift_left_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_SL_SPEC_VAR_CONST_OBJ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || (IS_CONST != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_VAR != IS_UNUSED)
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_VAR_CONST(shift_left_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CONST(shift_left_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_CONST(shift_left_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_CONST(shift_left_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#else
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CONST(shift_left_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#endif
@@ -16793,15 +17649,52 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_SR_SPEC_VAR_CONST_HANDL
USE_OPLINE
# if 0 || (IS_VAR != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
+ if (EXPECTED(1)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_VAR_CONST(shift_right_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CONST(shift_right_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_CONST(shift_right_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CONST(shift_right_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_SR_SPEC_VAR_CONST_DIM_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || (IS_CONST != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_VAR != IS_UNUSED)
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_VAR_CONST(shift_right_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(1)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CONST(shift_right_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_CONST(shift_right_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CONST(shift_right_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_SR_SPEC_VAR_CONST_OBJ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || (IS_CONST != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_VAR != IS_UNUSED)
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_VAR_CONST(shift_right_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CONST(shift_right_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_CONST(shift_right_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_CONST(shift_right_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#else
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CONST(shift_right_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#endif
@@ -16813,15 +17706,52 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_CONCAT_SPEC_VAR_CONST_H
USE_OPLINE
# if 0 || (IS_VAR != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
+ if (EXPECTED(1)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_VAR_CONST(concat_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CONST(concat_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_CONST(concat_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CONST(concat_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_CONCAT_SPEC_VAR_CONST_DIM_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || (IS_CONST != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_VAR != IS_UNUSED)
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_VAR_CONST(concat_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+ if (EXPECTED(1)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CONST(concat_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_CONST(concat_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CONST(concat_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_CONCAT_SPEC_VAR_CONST_OBJ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || (IS_CONST != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_VAR != IS_UNUSED)
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_VAR_CONST(concat_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CONST(concat_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_CONST(concat_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_CONST(concat_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#else
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CONST(concat_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#endif
@@ -16833,15 +17763,52 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_BW_OR_SPEC_VAR_CONST_HA
USE_OPLINE
# if 0 || (IS_VAR != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
+ if (EXPECTED(1)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_VAR_CONST(bitwise_or_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CONST(bitwise_or_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_CONST(bitwise_or_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CONST(bitwise_or_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_BW_OR_SPEC_VAR_CONST_DIM_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || (IS_CONST != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_VAR != IS_UNUSED)
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_VAR_CONST(bitwise_or_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(1)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CONST(bitwise_or_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_CONST(bitwise_or_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CONST(bitwise_or_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_BW_OR_SPEC_VAR_CONST_OBJ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || (IS_CONST != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_VAR != IS_UNUSED)
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_VAR_CONST(bitwise_or_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CONST(bitwise_or_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_CONST(bitwise_or_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_CONST(bitwise_or_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#else
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CONST(bitwise_or_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#endif
@@ -16853,15 +17820,52 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_BW_AND_SPEC_VAR_CONST_H
USE_OPLINE
# if 0 || (IS_VAR != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
+ if (EXPECTED(1)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_VAR_CONST(bitwise_and_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CONST(bitwise_and_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_CONST(bitwise_and_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CONST(bitwise_and_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_BW_AND_SPEC_VAR_CONST_DIM_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || (IS_CONST != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_VAR != IS_UNUSED)
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_VAR_CONST(bitwise_and_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(1)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CONST(bitwise_and_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_CONST(bitwise_and_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_CONST(bitwise_and_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CONST(bitwise_and_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_BW_AND_SPEC_VAR_CONST_OBJ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || (IS_CONST != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_VAR != IS_UNUSED)
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_VAR_CONST(bitwise_and_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CONST(bitwise_and_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_CONST(bitwise_and_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#else
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CONST(bitwise_and_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#endif
@@ -16873,15 +17877,52 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_BW_XOR_SPEC_VAR_CONST_H
USE_OPLINE
# if 0 || (IS_VAR != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
+ if (EXPECTED(1)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_VAR_CONST(bitwise_xor_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CONST(bitwise_xor_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_CONST(bitwise_xor_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CONST(bitwise_xor_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_BW_XOR_SPEC_VAR_CONST_DIM_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || (IS_CONST != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_VAR != IS_UNUSED)
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_VAR_CONST(bitwise_xor_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(1)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CONST(bitwise_xor_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_CONST(bitwise_xor_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CONST(bitwise_xor_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_BW_XOR_SPEC_VAR_CONST_OBJ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || (IS_CONST != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_VAR != IS_UNUSED)
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_VAR_CONST(bitwise_xor_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CONST(bitwise_xor_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_CONST(bitwise_xor_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_CONST(bitwise_xor_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#else
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CONST(bitwise_xor_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#endif
@@ -16893,15 +17934,52 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_POW_SPEC_VAR_CONST_HAND
USE_OPLINE
# if 0 || (IS_VAR != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
+ if (EXPECTED(1)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_VAR_CONST(pow_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CONST(pow_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_CONST(pow_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CONST(pow_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_POW_SPEC_VAR_CONST_DIM_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || (IS_CONST != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_VAR != IS_UNUSED)
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_VAR_CONST(pow_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+ if (EXPECTED(1)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CONST(pow_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_CONST(pow_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CONST(pow_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_POW_SPEC_VAR_CONST_OBJ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || (IS_CONST != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_VAR != IS_UNUSED)
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_VAR_CONST(pow_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CONST(pow_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_CONST(pow_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_CONST(pow_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#else
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CONST(pow_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#endif
@@ -16918,7 +17996,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_pre_incdec_property_helper_SPE
SAVE_OPLINE();
object = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1);
- if (IS_VAR == IS_UNUSED && UNEXPECTED(Z_OBJ_P(object) == NULL)) {
+ if (IS_VAR == IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
HANDLE_EXCEPTION();
@@ -16926,12 +18004,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_pre_incdec_property_helper_SPE
property = EX_CONSTANT(opline->op2);
- if (IS_VAR == IS_VAR && UNEXPECTED(object == NULL)) {
- zend_throw_error(NULL, "Cannot increment/decrement overloaded objects nor string offsets");
-
- HANDLE_EXCEPTION();
- }
-
do {
if (IS_VAR != IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) != IS_OBJECT)) {
ZVAL_DEREF(object);
@@ -16947,7 +18019,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_pre_incdec_property_helper_SPE
/* here we are sure we are dealing with an object */
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)) {
- if (UNEXPECTED(zptr == &EG(error_zval))) {
+ if (UNEXPECTED(Z_ISERROR_P(zptr))) {
if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
ZVAL_NULL(EX_VAR(opline->result.var));
}
@@ -17002,7 +18074,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_post_incdec_property_helper_SP
SAVE_OPLINE();
object = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1);
- if (IS_VAR == IS_UNUSED && UNEXPECTED(Z_OBJ_P(object) == NULL)) {
+ if (IS_VAR == IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
HANDLE_EXCEPTION();
@@ -17010,12 +18082,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_post_incdec_property_helper_SP
property = EX_CONSTANT(opline->op2);
- if (IS_VAR == IS_VAR && UNEXPECTED(object == NULL)) {
- zend_throw_error(NULL, "Cannot increment/decrement overloaded objects nor string offsets");
-
- HANDLE_EXCEPTION();
- }
-
do {
if (IS_VAR != IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) != IS_OBJECT)) {
ZVAL_DEREF(object);
@@ -17030,7 +18096,7 @@ 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)) {
- if (UNEXPECTED(zptr == &EG(error_zval))) {
+ if (UNEXPECTED(Z_ISERROR_P(zptr))) {
ZVAL_NULL(EX_VAR(opline->result.var));
} else {
if (EXPECTED(Z_TYPE_P(zptr) == IS_LONG)) {
@@ -17079,14 +18145,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_DIM_W_SPEC_VAR_CONST_HAN
SAVE_OPLINE();
container = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1);
- if (IS_VAR == IS_VAR && UNEXPECTED(container == NULL)) {
- zend_throw_error(NULL, "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);
if (IS_VAR == IS_VAR && READY_TO_DESTROY(free_op1)) {
- EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var), 1);
+ EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var));
}
if (UNEXPECTED(free_op1)) {zval_ptr_dtor_nogc(free_op1);};
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
@@ -17101,14 +18163,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_DIM_RW_SPEC_VAR_CONST_HA
SAVE_OPLINE();
container = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1);
- if (IS_VAR == IS_VAR && UNEXPECTED(container == NULL)) {
- zend_throw_error(NULL, "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);
if (IS_VAR == IS_VAR && READY_TO_DESTROY(free_op1)) {
- EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var), 1);
+ EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var));
}
if (UNEXPECTED(free_op1)) {zval_ptr_dtor_nogc(free_op1);};
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
@@ -17123,21 +18181,16 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_DIM_FUNC_ARG_SPEC_VAR_CO
SAVE_OPLINE();
if (zend_is_by_ref_func_arg_fetch(opline, EX(call))) {
- if (IS_VAR == IS_CONST || IS_VAR == IS_TMP_VAR) {
+ if ((IS_VAR & (IS_CONST|IS_TMP_VAR))) {
zend_throw_error(NULL, "Cannot use temporary expression in write context");
zval_ptr_dtor_nogc(EX_VAR(opline->op1.var));
HANDLE_EXCEPTION();
}
container = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1);
- if (IS_VAR == IS_VAR && UNEXPECTED(container == NULL)) {
- zend_throw_error(NULL, "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);
if (IS_VAR == IS_VAR && READY_TO_DESTROY(free_op1)) {
- EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var), 1);
+ EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var));
}
if (UNEXPECTED(free_op1)) {zval_ptr_dtor_nogc(free_op1);};
@@ -17165,15 +18218,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_DIM_UNSET_SPEC_VAR_CONST
SAVE_OPLINE();
container = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1);
- if (IS_VAR == IS_VAR && UNEXPECTED(container == NULL)) {
- zend_throw_error(NULL, "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);
if (IS_VAR == IS_VAR && READY_TO_DESTROY(free_op1)) {
- EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var), 1);
+ EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var));
}
if (UNEXPECTED(free_op1)) {zval_ptr_dtor_nogc(free_op1);};
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
@@ -17190,7 +18238,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_R_SPEC_VAR_CONST_HAN
SAVE_OPLINE();
container = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1);
- if (IS_VAR == IS_UNUSED && UNEXPECTED(Z_OBJ_P(container) == NULL)) {
+ if (IS_VAR == IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
HANDLE_EXCEPTION();
@@ -17262,21 +18310,16 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_W_SPEC_VAR_CONST_HAN
property = EX_CONSTANT(opline->op2);
container = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1);
- if (IS_VAR == IS_UNUSED && UNEXPECTED(Z_OBJ_P(container) == NULL)) {
+ if (IS_VAR == IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
HANDLE_EXCEPTION();
}
- if (IS_VAR == IS_VAR && UNEXPECTED(container == NULL)) {
- zend_throw_error(NULL, "Cannot use string offset as an object");
-
- 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);
if (IS_VAR == IS_VAR && READY_TO_DESTROY(free_op1)) {
- EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var), 0);
+ EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var));
}
if (UNEXPECTED(free_op1)) {zval_ptr_dtor_nogc(free_op1);};
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
@@ -17293,20 +18336,15 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_RW_SPEC_VAR_CONST_HA
property = EX_CONSTANT(opline->op2);
container = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1);
- if (IS_VAR == IS_UNUSED && UNEXPECTED(Z_OBJ_P(container) == NULL)) {
+ if (IS_VAR == IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
HANDLE_EXCEPTION();
}
- if (IS_VAR == IS_VAR && UNEXPECTED(container == NULL)) {
- zend_throw_error(NULL, "Cannot use string offset as an object");
-
- 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);
if (IS_VAR == IS_VAR && READY_TO_DESTROY(free_op1)) {
- EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var), 0);
+ EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var));
}
if (UNEXPECTED(free_op1)) {zval_ptr_dtor_nogc(free_op1);};
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
@@ -17326,26 +18364,21 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_FUNC_ARG_SPEC_VAR_CO
property = EX_CONSTANT(opline->op2);
container = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1);
- if (IS_VAR == IS_UNUSED && UNEXPECTED(Z_OBJ_P(container) == NULL)) {
+ if (IS_VAR == IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
HANDLE_EXCEPTION();
}
- if (IS_VAR == IS_CONST || IS_VAR == IS_TMP_VAR) {
+ if ((IS_VAR & (IS_CONST|IS_TMP_VAR))) {
zend_throw_error(NULL, "Cannot use temporary expression in write context");
if (UNEXPECTED(free_op1)) {zval_ptr_dtor_nogc(free_op1);};
HANDLE_EXCEPTION();
}
- if (IS_VAR == IS_VAR && UNEXPECTED(container == NULL)) {
- zend_throw_error(NULL, "Cannot use string offset as an object");
-
- 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);
if (IS_VAR == IS_VAR && READY_TO_DESTROY(free_op1)) {
- EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var), 0);
+ EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var));
}
if (UNEXPECTED(free_op1)) {zval_ptr_dtor_nogc(free_op1);};
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
@@ -17363,7 +18396,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_UNSET_SPEC_VAR_CONST
SAVE_OPLINE();
container = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1);
- if (IS_VAR == IS_UNUSED && UNEXPECTED(Z_OBJ_P(container) == NULL)) {
+ if (IS_VAR == IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
HANDLE_EXCEPTION();
@@ -17371,56 +18404,645 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_UNSET_SPEC_VAR_CONST
property = EX_CONSTANT(opline->op2);
- if (IS_VAR == IS_VAR && UNEXPECTED(container == NULL)) {
- zend_throw_error(NULL, "Cannot use string offset as an object");
-
- 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);
if (IS_VAR == IS_VAR && READY_TO_DESTROY(free_op1)) {
- EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var), 0);
+ EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var));
}
if (UNEXPECTED(free_op1)) {zval_ptr_dtor_nogc(free_op1);};
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_CONST_OP_DATA_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
zend_free_op free_op1;
- zval *object;
- zval *property_name;
+ zval *object, *property_name, *value, tmp;
SAVE_OPLINE();
object = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1);
- if (IS_VAR == IS_UNUSED && UNEXPECTED(Z_OBJ_P(object) == NULL)) {
+ if (IS_VAR == IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
HANDLE_EXCEPTION();
}
property_name = EX_CONSTANT(opline->op2);
+ value = EX_CONSTANT((opline+1)->op1);
- if (IS_VAR == IS_VAR && UNEXPECTED(object == NULL)) {
- zend_throw_error(NULL, "Cannot use string offset as an array");
+ if (IS_VAR != IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) != IS_OBJECT)) {
+ do {
+ if (Z_ISREF_P(object)) {
+ object = Z_REFVAL_P(object);
+ if (EXPECTED(Z_TYPE_P(object) == IS_OBJECT)) {
+ break;
+ }
+ }
+ if (EXPECTED(Z_TYPE_P(object) <= IS_FALSE ||
+ (Z_TYPE_P(object) == IS_STRING && Z_STRLEN_P(object) == 0))) {
+ zend_object *obj;
+
+ zval_ptr_dtor(object);
+ object_init(object);
+ Z_ADDREF_P(object);
+ obj = Z_OBJ_P(object);
+ zend_error(E_WARNING, "Creating default object from empty value");
+ if (GC_REFCOUNT(obj) == 1) {
+ /* the enclosing container was deleted, obj is unreferenced */
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+
+ OBJ_RELEASE(obj);
+ goto exit_assign_obj;
+ }
+ Z_DELREF_P(object);
+ } else {
+ if (IS_VAR != IS_VAR || EXPECTED(!Z_ISERROR_P(object))) {
+ zend_error(E_WARNING, "Attempt to assign property of non-object");
+ }
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+
+ goto exit_assign_obj;
+ }
+ } while (0);
+ }
+
+ if (IS_CONST == IS_CONST &&
+ EXPECTED(Z_OBJCE_P(object) == CACHED_PTR(Z_CACHE_SLOT_P(property_name)))) {
+ uint32_t prop_offset = (uint32_t)(intptr_t)CACHED_PTR(Z_CACHE_SLOT_P(property_name) + sizeof(void*));
+ zend_object *zobj = Z_OBJ_P(object);
+ zval *property;
+
+ if (EXPECTED(prop_offset != (uint32_t)ZEND_DYNAMIC_PROPERTY_OFFSET)) {
+ property = OBJ_PROP(zobj, prop_offset);
+ if (Z_TYPE_P(property) != IS_UNDEF) {
+fast_assign_obj:
+ value = zend_assign_to_variable(property, value, IS_CONST);
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+ goto exit_assign_obj;
+ }
+ } else {
+ if (EXPECTED(zobj->properties != NULL)) {
+ if (UNEXPECTED(GC_REFCOUNT(zobj->properties) > 1)) {
+ if (EXPECTED(!(GC_FLAGS(zobj->properties) & IS_ARRAY_IMMUTABLE))) {
+ GC_REFCOUNT(zobj->properties)--;
+ }
+ zobj->properties = zend_array_dup(zobj->properties);
+ }
+ property = zend_hash_find(zobj->properties, Z_STR_P(property_name));
+ if (property) {
+ goto fast_assign_obj;
+ }
+ }
+
+ if (!zobj->ce->__set) {
+
+ if (EXPECTED(zobj->properties == NULL)) {
+ rebuild_object_properties(zobj);
+ }
+ if (IS_CONST == IS_CONST) {
+ if (UNEXPECTED(Z_OPT_REFCOUNTED_P(value))) {
+ Z_ADDREF_P(value);
+ }
+ } else if (IS_CONST != IS_TMP_VAR) {
+ if (Z_ISREF_P(value)) {
+ if (IS_CONST == IS_VAR) {
+ zend_reference *ref = Z_REF_P(value);
+ if (--GC_REFCOUNT(ref) == 0) {
+ ZVAL_COPY_VALUE(&tmp, Z_REFVAL_P(value));
+ efree_size(ref, sizeof(zend_reference));
+ value = &tmp;
+ } else {
+ value = Z_REFVAL_P(value);
+ if (Z_REFCOUNTED_P(value)) {
+ Z_ADDREF_P(value);
+ }
+ }
+ } else {
+ value = Z_REFVAL_P(value);
+ if (Z_REFCOUNTED_P(value)) {
+ Z_ADDREF_P(value);
+ }
+ }
+ } else if (IS_CONST == IS_CV && Z_REFCOUNTED_P(value)) {
+ Z_ADDREF_P(value);
+ }
+ }
+ zend_hash_add_new(zobj->properties, Z_STR_P(property_name), value);
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+ goto exit_assign_obj;
+ }
+ }
+ }
+
+ if (!Z_OBJ_HT_P(object)->write_property) {
+ zend_error(E_WARNING, "Attempt to assign property of non-object");
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+
+ goto exit_assign_obj;
+ }
+
+ if (IS_CONST == IS_CV || IS_CONST == IS_VAR) {
+ ZVAL_DEREF(value);
+ }
+
+ Z_OBJ_HT_P(object)->write_property(object, property_name, value, (IS_CONST == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property_name)) : NULL);
+
+ if (UNEXPECTED(RETURN_VALUE_USED(opline)) && EXPECTED(!EG(exception))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+
+exit_assign_obj:
+
+ if (UNEXPECTED(free_op1)) {zval_ptr_dtor_nogc(free_op1);};
+ /* assign_obj has two opcodes! */
+ ZEND_VM_NEXT_OPCODE_EX(1, 2);
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_CONST_OP_DATA_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zend_free_op free_op1, free_op_data;
+ zval *object, *property_name, *value, tmp;
+
+ SAVE_OPLINE();
+ object = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1);
+
+ if (IS_VAR == IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) == IS_UNDEF)) {
+ zend_throw_error(NULL, "Using $this when not in object context");
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));
+
+ property_name = EX_CONSTANT(opline->op2);
+ value = _get_zval_ptr_tmp((opline+1)->op1.var, execute_data, &free_op_data);
+
+ if (IS_VAR != IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) != IS_OBJECT)) {
+ do {
+ if (Z_ISREF_P(object)) {
+ object = Z_REFVAL_P(object);
+ if (EXPECTED(Z_TYPE_P(object) == IS_OBJECT)) {
+ break;
+ }
+ }
+ if (EXPECTED(Z_TYPE_P(object) <= IS_FALSE ||
+ (Z_TYPE_P(object) == IS_STRING && Z_STRLEN_P(object) == 0))) {
+ zend_object *obj;
+
+ zval_ptr_dtor(object);
+ object_init(object);
+ Z_ADDREF_P(object);
+ obj = Z_OBJ_P(object);
+ zend_error(E_WARNING, "Creating default object from empty value");
+ if (GC_REFCOUNT(obj) == 1) {
+ /* the enclosing container was deleted, obj is unreferenced */
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+ zval_ptr_dtor_nogc(free_op_data);
+ OBJ_RELEASE(obj);
+ goto exit_assign_obj;
+ }
+ Z_DELREF_P(object);
+ } else {
+ if (IS_VAR != IS_VAR || EXPECTED(!Z_ISERROR_P(object))) {
+ zend_error(E_WARNING, "Attempt to assign property of non-object");
+ }
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+ zval_ptr_dtor_nogc(free_op_data);
+ goto exit_assign_obj;
+ }
+ } while (0);
+ }
+
+ if (IS_CONST == IS_CONST &&
+ EXPECTED(Z_OBJCE_P(object) == CACHED_PTR(Z_CACHE_SLOT_P(property_name)))) {
+ uint32_t prop_offset = (uint32_t)(intptr_t)CACHED_PTR(Z_CACHE_SLOT_P(property_name) + sizeof(void*));
+ zend_object *zobj = Z_OBJ_P(object);
+ zval *property;
+
+ if (EXPECTED(prop_offset != (uint32_t)ZEND_DYNAMIC_PROPERTY_OFFSET)) {
+ property = OBJ_PROP(zobj, prop_offset);
+ if (Z_TYPE_P(property) != IS_UNDEF) {
+fast_assign_obj:
+ value = zend_assign_to_variable(property, value, IS_TMP_VAR);
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+ goto exit_assign_obj;
+ }
+ } else {
+ if (EXPECTED(zobj->properties != NULL)) {
+ if (UNEXPECTED(GC_REFCOUNT(zobj->properties) > 1)) {
+ if (EXPECTED(!(GC_FLAGS(zobj->properties) & IS_ARRAY_IMMUTABLE))) {
+ GC_REFCOUNT(zobj->properties)--;
+ }
+ zobj->properties = zend_array_dup(zobj->properties);
+ }
+ property = zend_hash_find(zobj->properties, Z_STR_P(property_name));
+ if (property) {
+ goto fast_assign_obj;
+ }
+ }
+
+ if (!zobj->ce->__set) {
+
+ if (EXPECTED(zobj->properties == NULL)) {
+ rebuild_object_properties(zobj);
+ }
+ if (IS_TMP_VAR == IS_CONST) {
+ if (UNEXPECTED(Z_OPT_REFCOUNTED_P(value))) {
+ Z_ADDREF_P(value);
+ }
+ } else if (IS_TMP_VAR != IS_TMP_VAR) {
+ if (Z_ISREF_P(value)) {
+ if (IS_TMP_VAR == IS_VAR) {
+ zend_reference *ref = Z_REF_P(value);
+ if (--GC_REFCOUNT(ref) == 0) {
+ ZVAL_COPY_VALUE(&tmp, Z_REFVAL_P(value));
+ efree_size(ref, sizeof(zend_reference));
+ value = &tmp;
+ } else {
+ value = Z_REFVAL_P(value);
+ if (Z_REFCOUNTED_P(value)) {
+ Z_ADDREF_P(value);
+ }
+ }
+ } else {
+ value = Z_REFVAL_P(value);
+ if (Z_REFCOUNTED_P(value)) {
+ Z_ADDREF_P(value);
+ }
+ }
+ } else if (IS_TMP_VAR == IS_CV && Z_REFCOUNTED_P(value)) {
+ Z_ADDREF_P(value);
+ }
+ }
+ zend_hash_add_new(zobj->properties, Z_STR_P(property_name), value);
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+ goto exit_assign_obj;
+ }
+ }
+ }
+
+ if (!Z_OBJ_HT_P(object)->write_property) {
+ zend_error(E_WARNING, "Attempt to assign property of non-object");
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+ zval_ptr_dtor_nogc(free_op_data);
+ goto exit_assign_obj;
+ }
+
+ if (IS_TMP_VAR == IS_CV || IS_TMP_VAR == IS_VAR) {
+ ZVAL_DEREF(value);
+ }
+
+ Z_OBJ_HT_P(object)->write_property(object, property_name, value, (IS_CONST == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property_name)) : NULL);
+
+ if (UNEXPECTED(RETURN_VALUE_USED(opline)) && EXPECTED(!EG(exception))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+ zval_ptr_dtor_nogc(free_op_data);
+exit_assign_obj:
if (UNEXPECTED(free_op1)) {zval_ptr_dtor_nogc(free_op1);};
/* assign_obj has two opcodes! */
ZEND_VM_NEXT_OPCODE_EX(1, 2);
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_VAR_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_CONST_OP_DATA_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zend_free_op free_op1, free_op_data;
+ zval *object, *property_name, *value, tmp;
+
+ SAVE_OPLINE();
+ object = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1);
+
+ if (IS_VAR == IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) == IS_UNDEF)) {
+ zend_throw_error(NULL, "Using $this when not in object context");
+
+ HANDLE_EXCEPTION();
+ }
+
+ property_name = EX_CONSTANT(opline->op2);
+ value = _get_zval_ptr_var((opline+1)->op1.var, execute_data, &free_op_data);
+
+ if (IS_VAR != IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) != IS_OBJECT)) {
+ do {
+ if (Z_ISREF_P(object)) {
+ object = Z_REFVAL_P(object);
+ if (EXPECTED(Z_TYPE_P(object) == IS_OBJECT)) {
+ break;
+ }
+ }
+ if (EXPECTED(Z_TYPE_P(object) <= IS_FALSE ||
+ (Z_TYPE_P(object) == IS_STRING && Z_STRLEN_P(object) == 0))) {
+ zend_object *obj;
+
+ zval_ptr_dtor(object);
+ object_init(object);
+ Z_ADDREF_P(object);
+ obj = Z_OBJ_P(object);
+ zend_error(E_WARNING, "Creating default object from empty value");
+ if (GC_REFCOUNT(obj) == 1) {
+ /* the enclosing container was deleted, obj is unreferenced */
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+ zval_ptr_dtor_nogc(free_op_data);
+ OBJ_RELEASE(obj);
+ goto exit_assign_obj;
+ }
+ Z_DELREF_P(object);
+ } else {
+ if (IS_VAR != IS_VAR || EXPECTED(!Z_ISERROR_P(object))) {
+ zend_error(E_WARNING, "Attempt to assign property of non-object");
+ }
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+ zval_ptr_dtor_nogc(free_op_data);
+ goto exit_assign_obj;
+ }
+ } while (0);
+ }
+
+ if (IS_CONST == IS_CONST &&
+ EXPECTED(Z_OBJCE_P(object) == CACHED_PTR(Z_CACHE_SLOT_P(property_name)))) {
+ uint32_t prop_offset = (uint32_t)(intptr_t)CACHED_PTR(Z_CACHE_SLOT_P(property_name) + sizeof(void*));
+ zend_object *zobj = Z_OBJ_P(object);
+ zval *property;
+
+ if (EXPECTED(prop_offset != (uint32_t)ZEND_DYNAMIC_PROPERTY_OFFSET)) {
+ property = OBJ_PROP(zobj, prop_offset);
+ if (Z_TYPE_P(property) != IS_UNDEF) {
+fast_assign_obj:
+ value = zend_assign_to_variable(property, value, IS_VAR);
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+ goto exit_assign_obj;
+ }
+ } else {
+ if (EXPECTED(zobj->properties != NULL)) {
+ if (UNEXPECTED(GC_REFCOUNT(zobj->properties) > 1)) {
+ if (EXPECTED(!(GC_FLAGS(zobj->properties) & IS_ARRAY_IMMUTABLE))) {
+ GC_REFCOUNT(zobj->properties)--;
+ }
+ zobj->properties = zend_array_dup(zobj->properties);
+ }
+ property = zend_hash_find(zobj->properties, Z_STR_P(property_name));
+ if (property) {
+ goto fast_assign_obj;
+ }
+ }
+
+ if (!zobj->ce->__set) {
+
+ if (EXPECTED(zobj->properties == NULL)) {
+ rebuild_object_properties(zobj);
+ }
+ if (IS_VAR == IS_CONST) {
+ if (UNEXPECTED(Z_OPT_REFCOUNTED_P(value))) {
+ Z_ADDREF_P(value);
+ }
+ } else if (IS_VAR != IS_TMP_VAR) {
+ if (Z_ISREF_P(value)) {
+ if (IS_VAR == IS_VAR) {
+ zend_reference *ref = Z_REF_P(value);
+ if (--GC_REFCOUNT(ref) == 0) {
+ ZVAL_COPY_VALUE(&tmp, Z_REFVAL_P(value));
+ efree_size(ref, sizeof(zend_reference));
+ value = &tmp;
+ } else {
+ value = Z_REFVAL_P(value);
+ if (Z_REFCOUNTED_P(value)) {
+ Z_ADDREF_P(value);
+ }
+ }
+ } else {
+ value = Z_REFVAL_P(value);
+ if (Z_REFCOUNTED_P(value)) {
+ Z_ADDREF_P(value);
+ }
+ }
+ } else if (IS_VAR == IS_CV && Z_REFCOUNTED_P(value)) {
+ Z_ADDREF_P(value);
+ }
+ }
+ zend_hash_add_new(zobj->properties, Z_STR_P(property_name), value);
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+ goto exit_assign_obj;
+ }
+ }
+ }
+
+ if (!Z_OBJ_HT_P(object)->write_property) {
+ zend_error(E_WARNING, "Attempt to assign property of non-object");
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+ zval_ptr_dtor_nogc(free_op_data);
+ goto exit_assign_obj;
+ }
+
+ if (IS_VAR == IS_CV || IS_VAR == IS_VAR) {
+ ZVAL_DEREF(value);
+ }
+
+ Z_OBJ_HT_P(object)->write_property(object, property_name, value, (IS_CONST == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property_name)) : NULL);
+
+ if (UNEXPECTED(RETURN_VALUE_USED(opline)) && EXPECTED(!EG(exception))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+ zval_ptr_dtor_nogc(free_op_data);
+exit_assign_obj:
+
+ if (UNEXPECTED(free_op1)) {zval_ptr_dtor_nogc(free_op1);};
+ /* assign_obj has two opcodes! */
+ ZEND_VM_NEXT_OPCODE_EX(1, 2);
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_CONST_OP_DATA_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zend_free_op free_op1;
+ zval *object, *property_name, *value, tmp;
+
+ SAVE_OPLINE();
+ object = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1);
+
+ if (IS_VAR == IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) == IS_UNDEF)) {
+ zend_throw_error(NULL, "Using $this when not in object context");
+
+ HANDLE_EXCEPTION();
+ }
+
+ property_name = EX_CONSTANT(opline->op2);
+ value = _get_zval_ptr_cv_BP_VAR_R(execute_data, (opline+1)->op1.var);
+
+ if (IS_VAR != IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) != IS_OBJECT)) {
+ do {
+ if (Z_ISREF_P(object)) {
+ object = Z_REFVAL_P(object);
+ if (EXPECTED(Z_TYPE_P(object) == IS_OBJECT)) {
+ break;
+ }
+ }
+ if (EXPECTED(Z_TYPE_P(object) <= IS_FALSE ||
+ (Z_TYPE_P(object) == IS_STRING && Z_STRLEN_P(object) == 0))) {
+ zend_object *obj;
+
+ zval_ptr_dtor(object);
+ object_init(object);
+ Z_ADDREF_P(object);
+ obj = Z_OBJ_P(object);
+ zend_error(E_WARNING, "Creating default object from empty value");
+ if (GC_REFCOUNT(obj) == 1) {
+ /* the enclosing container was deleted, obj is unreferenced */
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+
+ OBJ_RELEASE(obj);
+ goto exit_assign_obj;
+ }
+ Z_DELREF_P(object);
+ } else {
+ if (IS_VAR != IS_VAR || EXPECTED(!Z_ISERROR_P(object))) {
+ zend_error(E_WARNING, "Attempt to assign property of non-object");
+ }
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+
+ goto exit_assign_obj;
+ }
+ } while (0);
+ }
+
+ if (IS_CONST == IS_CONST &&
+ EXPECTED(Z_OBJCE_P(object) == CACHED_PTR(Z_CACHE_SLOT_P(property_name)))) {
+ uint32_t prop_offset = (uint32_t)(intptr_t)CACHED_PTR(Z_CACHE_SLOT_P(property_name) + sizeof(void*));
+ zend_object *zobj = Z_OBJ_P(object);
+ zval *property;
+
+ if (EXPECTED(prop_offset != (uint32_t)ZEND_DYNAMIC_PROPERTY_OFFSET)) {
+ property = OBJ_PROP(zobj, prop_offset);
+ if (Z_TYPE_P(property) != IS_UNDEF) {
+fast_assign_obj:
+ value = zend_assign_to_variable(property, value, IS_CV);
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+ goto exit_assign_obj;
+ }
+ } else {
+ if (EXPECTED(zobj->properties != NULL)) {
+ if (UNEXPECTED(GC_REFCOUNT(zobj->properties) > 1)) {
+ if (EXPECTED(!(GC_FLAGS(zobj->properties) & IS_ARRAY_IMMUTABLE))) {
+ GC_REFCOUNT(zobj->properties)--;
+ }
+ zobj->properties = zend_array_dup(zobj->properties);
+ }
+ property = zend_hash_find(zobj->properties, Z_STR_P(property_name));
+ if (property) {
+ goto fast_assign_obj;
+ }
+ }
+
+ if (!zobj->ce->__set) {
+
+ if (EXPECTED(zobj->properties == NULL)) {
+ rebuild_object_properties(zobj);
+ }
+ if (IS_CV == IS_CONST) {
+ if (UNEXPECTED(Z_OPT_REFCOUNTED_P(value))) {
+ Z_ADDREF_P(value);
+ }
+ } else if (IS_CV != IS_TMP_VAR) {
+ if (Z_ISREF_P(value)) {
+ if (IS_CV == IS_VAR) {
+ zend_reference *ref = Z_REF_P(value);
+ if (--GC_REFCOUNT(ref) == 0) {
+ ZVAL_COPY_VALUE(&tmp, Z_REFVAL_P(value));
+ efree_size(ref, sizeof(zend_reference));
+ value = &tmp;
+ } else {
+ value = Z_REFVAL_P(value);
+ if (Z_REFCOUNTED_P(value)) {
+ Z_ADDREF_P(value);
+ }
+ }
+ } else {
+ value = Z_REFVAL_P(value);
+ if (Z_REFCOUNTED_P(value)) {
+ Z_ADDREF_P(value);
+ }
+ }
+ } else if (IS_CV == IS_CV && Z_REFCOUNTED_P(value)) {
+ Z_ADDREF_P(value);
+ }
+ }
+ zend_hash_add_new(zobj->properties, Z_STR_P(property_name), value);
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+ goto exit_assign_obj;
+ }
+ }
+ }
+
+ if (!Z_OBJ_HT_P(object)->write_property) {
+ zend_error(E_WARNING, "Attempt to assign property of non-object");
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+
+ goto exit_assign_obj;
+ }
+
+ if (IS_CV == IS_CV || IS_CV == IS_VAR) {
+ ZVAL_DEREF(value);
+ }
+
+ Z_OBJ_HT_P(object)->write_property(object, property_name, value, (IS_CONST == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property_name)) : NULL);
+
+ if (UNEXPECTED(RETURN_VALUE_USED(opline)) && EXPECTED(!EG(exception))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+
+exit_assign_obj:
+
+ if (UNEXPECTED(free_op1)) {zval_ptr_dtor_nogc(free_op1);};
+ /* assign_obj has two opcodes! */
+ ZEND_VM_NEXT_OPCODE_EX(1, 2);
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_VAR_CONST_OP_DATA_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
zend_free_op free_op1;
zval *object_ptr;
- zend_free_op free_op_data1;
+
zval *value;
zval *variable_ptr;
zval *dim;
@@ -17428,40 +19050,214 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_VAR_CONST_HAND
SAVE_OPLINE();
object_ptr = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1);
- if (IS_VAR == IS_VAR && UNEXPECTED(object_ptr == NULL)) {
- zend_throw_error(NULL, "Cannot use string offset as an array");
- FREE_UNFETCHED_OP((opline+1)->op1_type, (opline+1)->op1.var);
+ if (EXPECTED(Z_TYPE_P(object_ptr) == IS_ARRAY)) {
+try_assign_dim_array:
+ SEPARATE_ARRAY(object_ptr);
+ if (IS_CONST == IS_UNUSED) {
+ variable_ptr = zend_hash_next_index_insert(Z_ARRVAL_P(object_ptr), &EG(uninitialized_zval));
+ if (UNEXPECTED(variable_ptr == NULL)) {
+ zend_error(E_WARNING, "Cannot add element to the array as the next element is already occupied");
+ goto assign_dim_error;
+ }
+ } else {
+ dim = EX_CONSTANT(opline->op2);
+ if (IS_CONST == IS_CONST) {
+ variable_ptr = zend_fetch_dimension_address_inner_W_CONST(Z_ARRVAL_P(object_ptr), dim);
+ } else {
+ variable_ptr = zend_fetch_dimension_address_inner_W(Z_ARRVAL_P(object_ptr), dim);
+ }
+ if (UNEXPECTED(variable_ptr == NULL)) {
+ goto assign_dim_error;
+ }
+ }
+ value = EX_CONSTANT((opline+1)->op1);
+ value = zend_assign_to_variable(variable_ptr, value, IS_CONST);
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+ } 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)) {
+ dim = EX_CONSTANT(opline->op2);
+ value = EX_CONSTANT((opline+1)->op1);
- HANDLE_EXCEPTION();
+ zend_assign_to_object_dim(object_ptr, dim, value);
+
+ if (UNEXPECTED(RETURN_VALUE_USED(opline)) && EXPECTED(!EG(exception))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+
+ } else if (EXPECTED(Z_TYPE_P(object_ptr) == IS_STRING)) {
+ if (IS_CONST == IS_UNUSED) {
+ zend_throw_error(NULL, "[] operator not supported for strings");
+
+ if (UNEXPECTED(free_op1)) {zval_ptr_dtor_nogc(free_op1);};
+ HANDLE_EXCEPTION();
+ } else {
+ dim = EX_CONSTANT(opline->op2);
+ value = EX_CONSTANT((opline+1)->op1);
+ zend_assign_to_string_offset(object_ptr, dim, value, (UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL));
+
+ }
+ } else if (EXPECTED(Z_TYPE_P(object_ptr) <= IS_FALSE)) {
+ 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 (IS_VAR != IS_VAR || EXPECTED(!Z_ISERROR_P(object_ptr))) {
+ zend_error(E_WARNING, "Cannot use a scalar value as an array");
+ }
+ dim = EX_CONSTANT(opline->op2);
+assign_dim_error:
+
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+ }
}
+ if (IS_CONST != IS_UNUSED) {
+
+ }
+ if (UNEXPECTED(free_op1)) {zval_ptr_dtor_nogc(free_op1);};
+ /* assign_dim has two opcodes! */
+ ZEND_VM_NEXT_OPCODE_EX(1, 2);
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_VAR_CONST_OP_DATA_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zend_free_op free_op1;
+ zval *object_ptr;
+ zend_free_op free_op_data;
+ zval *value;
+ zval *variable_ptr;
+ zval *dim;
+
+ SAVE_OPLINE();
+ object_ptr = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1);
if (EXPECTED(Z_TYPE_P(object_ptr) == IS_ARRAY)) {
try_assign_dim_array:
+ SEPARATE_ARRAY(object_ptr);
if (IS_CONST == IS_UNUSED) {
- SEPARATE_ARRAY(object_ptr);
variable_ptr = zend_hash_next_index_insert(Z_ARRVAL_P(object_ptr), &EG(uninitialized_zval));
if (UNEXPECTED(variable_ptr == NULL)) {
zend_error(E_WARNING, "Cannot add element to the array as the next element is already occupied");
- variable_ptr = &EG(error_zval);
+ goto assign_dim_error;
}
} else {
dim = EX_CONSTANT(opline->op2);
- SEPARATE_ARRAY(object_ptr);
- variable_ptr = zend_fetch_dimension_address_inner(Z_ARRVAL_P(object_ptr), dim, IS_CONST, BP_VAR_W);
-
+ if (IS_CONST == IS_CONST) {
+ variable_ptr = zend_fetch_dimension_address_inner_W_CONST(Z_ARRVAL_P(object_ptr), dim);
+ } else {
+ variable_ptr = zend_fetch_dimension_address_inner_W(Z_ARRVAL_P(object_ptr), dim);
+ }
+ if (UNEXPECTED(variable_ptr == NULL)) {
+ goto assign_dim_error;
+ }
}
- value = get_zval_ptr_r((opline+1)->op1_type, (opline+1)->op1, execute_data, &free_op_data1);
- if (UNEXPECTED(variable_ptr == &EG(error_zval))) {
- FREE_OP(free_op_data1);
+ value = _get_zval_ptr_tmp((opline+1)->op1.var, execute_data, &free_op_data);
+ value = zend_assign_to_variable(variable_ptr, value, IS_TMP_VAR);
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+ } 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)) {
+ dim = EX_CONSTANT(opline->op2);
+ value = _get_zval_ptr_tmp((opline+1)->op1.var, execute_data, &free_op_data);
+
+ zend_assign_to_object_dim(object_ptr, dim, value);
+
+ if (UNEXPECTED(RETURN_VALUE_USED(opline)) && EXPECTED(!EG(exception))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+
+ zval_ptr_dtor_nogc(free_op_data);
+ } else if (EXPECTED(Z_TYPE_P(object_ptr) == IS_STRING)) {
+ if (IS_CONST == IS_UNUSED) {
+ zend_throw_error(NULL, "[] operator not supported for strings");
+ zval_ptr_dtor_nogc(EX_VAR((opline+1)->op1.var));
+ if (UNEXPECTED(free_op1)) {zval_ptr_dtor_nogc(free_op1);};
+ HANDLE_EXCEPTION();
+ } else {
+ dim = EX_CONSTANT(opline->op2);
+ value = _get_zval_ptr_tmp((opline+1)->op1.var, execute_data, &free_op_data);
+ zend_assign_to_string_offset(object_ptr, dim, value, (UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL));
+ zval_ptr_dtor_nogc(free_op_data);
+ }
+ } else if (EXPECTED(Z_TYPE_P(object_ptr) <= IS_FALSE)) {
+ 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 (IS_VAR != IS_VAR || EXPECTED(!Z_ISERROR_P(object_ptr))) {
+ zend_error(E_WARNING, "Cannot use a scalar value as an array");
+ }
+ dim = EX_CONSTANT(opline->op2);
+assign_dim_error:
+ zval_ptr_dtor_nogc(EX_VAR((opline+1)->op1.var));
if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
ZVAL_NULL(EX_VAR(opline->result.var));
}
+ }
+ }
+ if (IS_CONST != IS_UNUSED) {
+
+ }
+ if (UNEXPECTED(free_op1)) {zval_ptr_dtor_nogc(free_op1);};
+ /* assign_dim has two opcodes! */
+ ZEND_VM_NEXT_OPCODE_EX(1, 2);
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_VAR_CONST_OP_DATA_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zend_free_op free_op1;
+ zval *object_ptr;
+ zend_free_op free_op_data;
+ zval *value;
+ zval *variable_ptr;
+ zval *dim;
+
+ SAVE_OPLINE();
+ object_ptr = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1);
+
+ if (EXPECTED(Z_TYPE_P(object_ptr) == IS_ARRAY)) {
+try_assign_dim_array:
+ SEPARATE_ARRAY(object_ptr);
+ if (IS_CONST == IS_UNUSED) {
+ variable_ptr = zend_hash_next_index_insert(Z_ARRVAL_P(object_ptr), &EG(uninitialized_zval));
+ if (UNEXPECTED(variable_ptr == NULL)) {
+ zend_error(E_WARNING, "Cannot add element to the array as the next element is already occupied");
+ goto assign_dim_error;
+ }
} else {
- value = zend_assign_to_variable(variable_ptr, value, (opline+1)->op1_type);
- if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
- ZVAL_COPY(EX_VAR(opline->result.var), value);
+ dim = EX_CONSTANT(opline->op2);
+ if (IS_CONST == IS_CONST) {
+ variable_ptr = zend_fetch_dimension_address_inner_W_CONST(Z_ARRVAL_P(object_ptr), dim);
+ } else {
+ variable_ptr = zend_fetch_dimension_address_inner_W(Z_ARRVAL_P(object_ptr), dim);
+ }
+ if (UNEXPECTED(variable_ptr == NULL)) {
+ goto assign_dim_error;
}
}
+ value = _get_zval_ptr_var((opline+1)->op1.var, execute_data, &free_op_data);
+ value = zend_assign_to_variable(variable_ptr, value, IS_VAR);
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
} else {
if (EXPECTED(Z_ISREF_P(object_ptr))) {
object_ptr = Z_REFVAL_P(object_ptr);
@@ -17470,58 +19266,144 @@ try_assign_dim_array:
}
}
if (EXPECTED(Z_TYPE_P(object_ptr) == IS_OBJECT)) {
+ dim = EX_CONSTANT(opline->op2);
+ value = _get_zval_ptr_var((opline+1)->op1.var, execute_data, &free_op_data);
- zval *property_name = EX_CONSTANT(opline->op2);
+ zend_assign_to_object_dim(object_ptr, dim, value);
- 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);
+ if (UNEXPECTED(RETURN_VALUE_USED(opline)) && EXPECTED(!EG(exception))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+ zval_ptr_dtor_nogc(free_op_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_throw_error(NULL, "[] operator not supported for strings");
- FREE_UNFETCHED_OP((opline+1)->op1_type, (opline+1)->op1.var);
- if (UNEXPECTED(free_op1)) {zval_ptr_dtor_nogc(free_op1);};
- HANDLE_EXCEPTION();
- } else {
- zend_long offset;
+ if (IS_CONST == IS_UNUSED) {
+ zend_throw_error(NULL, "[] operator not supported for strings");
+ zval_ptr_dtor_nogc(EX_VAR((opline+1)->op1.var));
+ if (UNEXPECTED(free_op1)) {zval_ptr_dtor_nogc(free_op1);};
+ HANDLE_EXCEPTION();
+ } else {
+ dim = EX_CONSTANT(opline->op2);
+ value = _get_zval_ptr_var_deref((opline+1)->op1.var, execute_data, &free_op_data);
+ zend_assign_to_string_offset(object_ptr, dim, value, (UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL));
+ zval_ptr_dtor_nogc(free_op_data);
+ }
+ } else if (EXPECTED(Z_TYPE_P(object_ptr) <= IS_FALSE)) {
+ 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 (IS_VAR != IS_VAR || EXPECTED(!Z_ISERROR_P(object_ptr))) {
+ zend_error(E_WARNING, "Cannot use a scalar value as an array");
+ }
+ dim = EX_CONSTANT(opline->op2);
+assign_dim_error:
+ zval_ptr_dtor_nogc(EX_VAR((opline+1)->op1.var));
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+ }
+ }
+ if (IS_CONST != IS_UNUSED) {
- dim = EX_CONSTANT(opline->op2);
- offset = zend_fetch_string_offset(object_ptr, dim, BP_VAR_W);
+ }
+ if (UNEXPECTED(free_op1)) {zval_ptr_dtor_nogc(free_op1);};
+ /* assign_dim has two opcodes! */
+ ZEND_VM_NEXT_OPCODE_EX(1, 2);
+}
- value = get_zval_ptr_r_deref((opline+1)->op1_type, (opline+1)->op1, execute_data, &free_op_data1);
- 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);
- }
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_VAR_CONST_OP_DATA_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zend_free_op free_op1;
+ zval *object_ptr;
+
+ zval *value;
+ zval *variable_ptr;
+ zval *dim;
+
+ SAVE_OPLINE();
+ object_ptr = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1);
+
+ if (EXPECTED(Z_TYPE_P(object_ptr) == IS_ARRAY)) {
+try_assign_dim_array:
+ SEPARATE_ARRAY(object_ptr);
+ if (IS_CONST == IS_UNUSED) {
+ variable_ptr = zend_hash_next_index_insert(Z_ARRVAL_P(object_ptr), &EG(uninitialized_zval));
+ if (UNEXPECTED(variable_ptr == NULL)) {
+ zend_error(E_WARNING, "Cannot add element to the array as the next element is already occupied");
+ goto assign_dim_error;
+ }
+ } else {
+ dim = EX_CONSTANT(opline->op2);
+ if (IS_CONST == IS_CONST) {
+ variable_ptr = zend_fetch_dimension_address_inner_W_CONST(Z_ARRVAL_P(object_ptr), dim);
} 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);
+ variable_ptr = zend_fetch_dimension_address_inner_W(Z_ARRVAL_P(object_ptr), dim);
+ }
+ if (UNEXPECTED(variable_ptr == NULL)) {
+ goto assign_dim_error;
+ }
+ }
+ value = _get_zval_ptr_cv_BP_VAR_R(execute_data, (opline+1)->op1.var);
+ value = zend_assign_to_variable(variable_ptr, value, IS_CV);
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+ } 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;
}
- } 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;
+ }
+ if (EXPECTED(Z_TYPE_P(object_ptr) == IS_OBJECT)) {
+ dim = EX_CONSTANT(opline->op2);
+ value = _get_zval_ptr_cv_BP_VAR_R(execute_data, (opline+1)->op1.var);
+
+ zend_assign_to_object_dim(object_ptr, dim, value);
+
+ if (UNEXPECTED(RETURN_VALUE_USED(opline)) && EXPECTED(!EG(exception))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+
+ } else if (EXPECTED(Z_TYPE_P(object_ptr) == IS_STRING)) {
+ if (IS_CONST == IS_UNUSED) {
+ zend_throw_error(NULL, "[] operator not supported for strings");
+
+ if (UNEXPECTED(free_op1)) {zval_ptr_dtor_nogc(free_op1);};
+ HANDLE_EXCEPTION();
+ } else {
+ dim = EX_CONSTANT(opline->op2);
+ value = _get_zval_ptr_cv_deref_BP_VAR_R(execute_data, (opline+1)->op1.var);
+ zend_assign_to_string_offset(object_ptr, dim, value, (UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL));
+
}
- goto assign_dim_convert_to_array;
+ } else if (EXPECTED(Z_TYPE_P(object_ptr) <= IS_FALSE)) {
+ 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 {
- zend_error(E_WARNING, "Cannot use a scalar value as an array");
-assign_dim_clean:
+ if (IS_VAR != IS_VAR || EXPECTED(!Z_ISERROR_P(object_ptr))) {
+ zend_error(E_WARNING, "Cannot use a scalar value as an array");
+ }
dim = EX_CONSTANT(opline->op2);
+assign_dim_error:
- value = get_zval_ptr_r((opline+1)->op1_type, (opline+1)->op1, execute_data, &free_op_data1);
- FREE_OP(free_op_data1);
if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
ZVAL_NULL(EX_VAR(opline->result.var));
}
}
}
+ if (IS_CONST != IS_UNUSED) {
+
+ }
if (UNEXPECTED(free_op1)) {zval_ptr_dtor_nogc(free_op1);};
/* assign_dim has two opcodes! */
ZEND_VM_NEXT_OPCODE_EX(1, 2);
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_SPEC_VAR_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_SPEC_VAR_CONST_RETVAL_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
zend_free_op free_op1;
@@ -17532,14 +19414,42 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_SPEC_VAR_CONST_HANDLER(
value = EX_CONSTANT(opline->op2);
variable_ptr = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1);
- if (IS_VAR == IS_VAR && UNEXPECTED(variable_ptr == &EG(error_zval))) {
+ if (IS_VAR == IS_VAR && UNEXPECTED(Z_ISERROR_P(variable_ptr))) {
- if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ if (UNEXPECTED(0)) {
ZVAL_NULL(EX_VAR(opline->result.var));
}
} else {
value = zend_assign_to_variable(variable_ptr, value, IS_CONST);
- if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ if (UNEXPECTED(0)) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+ if (UNEXPECTED(free_op1)) {zval_ptr_dtor_nogc(free_op1);};
+ /* zend_assign_to_variable() always takes care of op2, never free it! */
+ }
+
+ ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_SPEC_VAR_CONST_RETVAL_USED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zend_free_op free_op1;
+ zval *value;
+ zval *variable_ptr;
+
+ SAVE_OPLINE();
+ value = EX_CONSTANT(opline->op2);
+ variable_ptr = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1);
+
+ if (IS_VAR == IS_VAR && UNEXPECTED(Z_ISERROR_P(variable_ptr))) {
+
+ if (UNEXPECTED(1)) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+ } else {
+ value = zend_assign_to_variable(variable_ptr, value, IS_CONST);
+ if (UNEXPECTED(1)) {
ZVAL_COPY(EX_VAR(opline->result.var), value);
}
if (UNEXPECTED(free_op1)) {zval_ptr_dtor_nogc(free_op1);};
@@ -17574,6 +19484,13 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_STATIC_METHOD_CALL_SPEC_V
}
CACHE_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)), ce);
}
+ } else if (IS_VAR == IS_UNUSED) {
+ ce = zend_fetch_class(NULL, opline->op1.num);
+ if (UNEXPECTED(ce == NULL)) {
+ ZEND_ASSERT(EG(exception));
+
+ HANDLE_EXCEPTION();
+ }
} else {
ce = Z_CE_P(EX_VAR(opline->op1.var));
}
@@ -17632,6 +19549,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_STATIC_METHOD_CALL_SPEC_V
CACHE_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(function_name), ce, fbc);
}
}
+ if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!fbc->op_array.run_time_cache)) {
+ init_func_run_time_cache(&fbc->op_array);
+ }
if (IS_CONST != IS_CONST) {
}
@@ -17640,16 +19560,19 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_STATIC_METHOD_CALL_SPEC_V
zend_throw_error(NULL, "Cannot call constructor");
HANDLE_EXCEPTION();
}
- if (Z_OBJ(EX(This)) && Z_OBJ(EX(This))->ce != ce->constructor->common.scope && (ce->constructor->common.fn_flags & ZEND_ACC_PRIVATE)) {
+ if (Z_TYPE(EX(This)) == IS_OBJECT && Z_OBJ(EX(This))->ce != ce->constructor->common.scope && (ce->constructor->common.fn_flags & ZEND_ACC_PRIVATE)) {
zend_throw_error(NULL, "Cannot call private %s::__construct()", ZSTR_VAL(ce->name));
HANDLE_EXCEPTION();
}
fbc = ce->constructor;
+ if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!fbc->op_array.run_time_cache)) {
+ init_func_run_time_cache(&fbc->op_array);
+ }
}
object = NULL;
if (!(fbc->common.fn_flags & ZEND_ACC_STATIC)) {
- if (Z_OBJ(EX(This)) && instanceof_function(Z_OBJCE(EX(This)), ce)) {
+ if (Z_TYPE(EX(This)) == IS_OBJECT && instanceof_function(Z_OBJCE(EX(This)), ce)) {
object = Z_OBJ(EX(This));
ce = object->ce;
} else {
@@ -17674,11 +19597,15 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_STATIC_METHOD_CALL_SPEC_V
}
}
- if (IS_VAR != IS_CONST) {
+ if (IS_VAR == IS_UNUSED) {
/* previous opcode is ZEND_FETCH_CLASS */
- if (((opline-1)->extended_value & ZEND_FETCH_CLASS_MASK) == ZEND_FETCH_CLASS_PARENT ||
- ((opline-1)->extended_value & ZEND_FETCH_CLASS_MASK) == ZEND_FETCH_CLASS_SELF) {
- ce = EX(called_scope);
+ if ((opline->op1.num & ZEND_FETCH_CLASS_MASK) == ZEND_FETCH_CLASS_PARENT ||
+ (opline->op1.num & ZEND_FETCH_CLASS_MASK) == ZEND_FETCH_CLASS_SELF) {
+ if (Z_TYPE(EX(This)) == IS_OBJECT) {
+ ce = Z_OBJCE(EX(This));
+ } else {
+ ce = Z_CE(EX(This));
+ }
}
}
@@ -17690,110 +19617,84 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_STATIC_METHOD_CALL_SPEC_V
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CONSTANT_SPEC_VAR_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_CONSTANT_SPEC_VAR_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
+ zend_class_entry *ce, *scope;
+ zend_class_constant *c;
+ zval *value;
USE_OPLINE
SAVE_OPLINE();
- if (IS_VAR == IS_UNUSED) {
- zend_constant *c;
-
- if (EXPECTED(CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2))))) {
- c = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)));
- } else if ((c = zend_quick_get_constant(EX_CONSTANT(opline->op2) + 1, opline->extended_value)) == NULL) {
- if ((opline->extended_value & IS_CONSTANT_UNQUALIFIED) != 0) {
- char *actual = (char *)zend_memrchr(Z_STRVAL_P(EX_CONSTANT(opline->op2)), '\\', Z_STRLEN_P(EX_CONSTANT(opline->op2)));
- if (!actual) {
- ZVAL_STR_COPY(EX_VAR(opline->result.var), Z_STR_P(EX_CONSTANT(opline->op2)));
- } else {
- actual++;
- ZVAL_STRINGL(EX_VAR(opline->result.var),
- actual, Z_STRLEN_P(EX_CONSTANT(opline->op2)) - (actual - Z_STRVAL_P(EX_CONSTANT(opline->op2))));
- }
- /* non-qualified constant - allow text substitution */
- zend_error(E_NOTICE, "Use of undefined constant %s - assumed '%s'",
- Z_STRVAL_P(EX_VAR(opline->result.var)), Z_STRVAL_P(EX_VAR(opline->result.var)));
- ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
- } else {
- zend_throw_error(NULL, "Undefined constant '%s'", Z_STRVAL_P(EX_CONSTANT(opline->op2)));
- HANDLE_EXCEPTION();
- }
- } else {
- CACHE_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)), c);
- }
-#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;
- do {
- if (IS_VAR == IS_CONST) {
- if (EXPECTED(CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2))))) {
- value = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)));
- ZVAL_DEREF(value);
+ do {
+ if (IS_VAR == IS_CONST) {
+ if (EXPECTED(CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2))))) {
+ value = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)));
#ifdef ZTS
- ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)));
+ ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)));
#endif
- break;
- } else if (EXPECTED(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(ce == NULL)) {
- if (EXPECTED(!EG(exception))) {
- zend_throw_error(NULL, "Class '%s' not found", Z_STRVAL_P(EX_CONSTANT(opline->op1)));
- }
- HANDLE_EXCEPTION();
+ break;
+ } else if (EXPECTED(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(ce == NULL)) {
+ if (EXPECTED(!EG(exception))) {
+ zend_throw_error(NULL, "Class '%s' not found", Z_STRVAL_P(EX_CONSTANT(opline->op1)));
}
- CACHE_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)), ce);
+ HANDLE_EXCEPTION();
+ }
+ CACHE_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)), ce);
+ }
+ } else {
+ if (IS_VAR == IS_UNUSED) {
+ ce = zend_fetch_class(NULL, opline->op1.num);
+ if (UNEXPECTED(ce == NULL)) {
+ ZEND_ASSERT(EG(exception));
+ 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;
- }
}
+ if ((value = CACHED_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)), ce)) != NULL) {
+ break;
+ }
+ }
- 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);
+ if (EXPECTED((c = zend_hash_find_ptr(&ce->constants_table, Z_STR_P(EX_CONSTANT(opline->op2)))) != NULL)) {
+ scope = EX(func)->op_array.scope;
+ if (!zend_verify_const_access(c, scope)) {
+ zend_throw_error(NULL, "Cannot access %s const %s::%s", zend_visibility_string(Z_ACCESS_FLAGS(c->value)), ZSTR_VAL(ce->name), Z_STRVAL_P(EX_CONSTANT(opline->op2)));
+ HANDLE_EXCEPTION();
+ }
+ value = &c->value;
+ if (Z_CONSTANT_P(value)) {
+ zval_update_constant_ex(value, ce);
+ if (UNEXPECTED(EG(exception) != NULL)) {
+ HANDLE_EXCEPTION();
}
+ }
+ if (IS_VAR == IS_CONST) {
+ CACHE_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)), value);
} else {
- zend_throw_error(NULL, "Undefined class constant '%s'", Z_STRVAL_P(EX_CONSTANT(opline->op2)));
- HANDLE_EXCEPTION();
+ CACHE_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)), ce, value);
}
- } while (0);
-#ifdef ZTS
- if (ce->type == ZEND_INTERNAL_CLASS) {
- ZVAL_DUP(EX_VAR(opline->result.var), value);
} else {
- ZVAL_COPY(EX_VAR(opline->result.var), value);
+ zend_throw_error(NULL, "Undefined class constant '%s'", Z_STRVAL_P(EX_CONSTANT(opline->op2)));
+ HANDLE_EXCEPTION();
}
-#else
+ } while (0);
+
+#ifdef ZTS
+ if (ce->type == ZEND_INTERNAL_CLASS) {
+ ZVAL_DUP(EX_VAR(opline->result.var), value);
+ } else {
ZVAL_COPY(EX_VAR(opline->result.var), value);
-#endif
}
+#else
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+#endif
+
ZEND_VM_NEXT_OPCODE();
}
@@ -17807,11 +19708,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ADD_ARRAY_ELEMENT_SPEC_VAR_CON
if ((IS_VAR == IS_VAR || IS_VAR == IS_CV) &&
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_throw_error(NULL, "Cannot create references to/from string offsets");
- zend_array_destroy(Z_ARRVAL_P(EX_VAR(opline->result.var)));
- HANDLE_EXCEPTION();
- }
ZVAL_MAKE_REF(expr_ptr);
Z_ADDREF_P(expr_ptr);
if (UNEXPECTED(free_op1)) {zval_ptr_dtor_nogc(free_op1);};
@@ -17820,10 +19716,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ADD_ARRAY_ELEMENT_SPEC_VAR_CON
if (IS_VAR == IS_TMP_VAR) {
/* pass */
} else if (IS_VAR == IS_CONST) {
- 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;
+ if (Z_REFCOUNTED_P(expr_ptr)) {
+ Z_ADDREF_P(expr_ptr);
}
} else if (IS_VAR == IS_CV) {
ZVAL_DEREF(expr_ptr);
@@ -17938,20 +19832,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_UNSET_DIM_SPEC_VAR_CONST_HANDL
SAVE_OPLINE();
container = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1);
- if (IS_VAR == IS_UNUSED && UNEXPECTED(Z_OBJ_P(container) == NULL)) {
- zend_throw_error(NULL, "Using $this when not in object context");
-
- HANDLE_EXCEPTION();
- }
- if (IS_VAR == IS_VAR && UNEXPECTED(container == NULL)) {
- zend_throw_error(NULL, "Cannot unset string offsets");
-
- HANDLE_EXCEPTION();
- }
offset = EX_CONSTANT(opline->op2);
do {
- if (IS_VAR != IS_UNUSED && EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) {
+ if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) {
HashTable *ht;
unset_dim_array:
@@ -18001,16 +19885,19 @@ num_index_dim:
zend_error(E_WARNING, "Illegal offset type in unset");
}
break;
- } else if (IS_VAR != IS_UNUSED && Z_ISREF_P(container)) {
+ } else if (Z_ISREF_P(container)) {
container = Z_REFVAL_P(container);
if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) {
goto unset_dim_array;
}
}
+ if (IS_VAR == IS_CV && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) {
+ container = GET_OP1_UNDEF_CV(container, BP_VAR_R);
+ }
if (IS_CONST == IS_CV && UNEXPECTED(Z_TYPE_P(offset) == IS_UNDEF)) {
offset = GET_OP2_UNDEF_CV(offset, BP_VAR_R);
}
- if (IS_VAR == IS_UNUSED || EXPECTED(Z_TYPE_P(container) == IS_OBJECT)) {
+ if (EXPECTED(Z_TYPE_P(container) == IS_OBJECT)) {
if (UNEXPECTED(Z_OBJ_HT_P(container)->unset_dimension == NULL)) {
zend_throw_error(NULL, "Cannot use object as array");
} else {
@@ -18034,16 +19921,11 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_UNSET_OBJ_SPEC_VAR_CONST_HANDL
SAVE_OPLINE();
container = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1);
- if (IS_VAR == IS_UNUSED && UNEXPECTED(Z_OBJ_P(container) == NULL)) {
+ if (IS_VAR == IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
HANDLE_EXCEPTION();
}
- if (IS_VAR == IS_VAR && UNEXPECTED(container == NULL)) {
- zend_throw_error(NULL, "Cannot unset string offsets");
-
- HANDLE_EXCEPTION();
- }
offset = EX_CONSTANT(opline->op2);
do {
@@ -18095,7 +19977,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_VAR_CONST_HANDLER(Z
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) {
+ if (IS_VAR & (IS_CONST|IS_TMP_VAR)) {
zval *value;
zend_error(E_NOTICE, "Only variable references should be yielded by reference");
@@ -18103,25 +19985,19 @@ 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 (IS_VAR == IS_CONST) {
- if (UNEXPECTED(Z_OPT_COPYABLE(generator->value))) {
- zval_copy_ctor_func(&generator->value);
+ if (UNEXPECTED(Z_OPT_REFCOUNTED(generator->value))) {
+ Z_ADDREF(generator->value);
}
}
} else {
zval *value_ptr = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1);
- if (IS_VAR == IS_VAR && UNEXPECTED(value_ptr == NULL)) {
- zend_throw_error(NULL, "Cannot yield string offsets by reference");
-
- HANDLE_EXCEPTION();
- }
-
/* If a function call result is yielded and the function did
* not return by reference we throw a notice. */
if (IS_VAR == IS_VAR &&
(value_ptr == &EG(uninitialized_zval) ||
(opline->extended_value == ZEND_RETURNS_FUNCTION &&
- !(Z_VAR_FLAGS_P(value_ptr) & IS_VAR_RET_REF)))) {
+ !Z_ISREF_P(value_ptr)))) {
zend_error(E_NOTICE, "Only variable references should be yielded by reference");
} else {
ZVAL_MAKE_REF(value_ptr);
@@ -18136,8 +20012,8 @@ 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_COPY_VALUE(&generator->value, value);
- if (UNEXPECTED(Z_OPT_COPYABLE(generator->value))) {
- zval_copy_ctor_func(&generator->value);
+ if (UNEXPECTED(Z_OPT_REFCOUNTED(generator->value))) {
+ Z_ADDREF(generator->value);
}
} else if (IS_VAR == IS_TMP_VAR) {
ZVAL_COPY_VALUE(&generator->value, value);
@@ -18164,8 +20040,8 @@ 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_COPY_VALUE(&generator->key, key);
- if (UNEXPECTED(Z_OPT_COPYABLE(generator->key))) {
- zval_copy_ctor_func(&generator->key);
+ if (UNEXPECTED(Z_OPT_REFCOUNTED(generator->key))) {
+ Z_ADDREF(generator->key);
}
} else if (IS_CONST == IS_TMP_VAR) {
ZVAL_COPY_VALUE(&generator->key, key);
@@ -18246,7 +20122,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_NOT_IDENTICAL_SPEC_VAR_TMP_
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_SPEC_VAR_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_SPEC_VAR_TMP_RETVAL_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
zend_free_op free_op1, free_op2;
@@ -18257,14 +20133,42 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_SPEC_VAR_TMP_HANDLER(ZE
value = _get_zval_ptr_tmp(opline->op2.var, execute_data, &free_op2);
variable_ptr = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1);
- if (IS_VAR == IS_VAR && UNEXPECTED(variable_ptr == &EG(error_zval))) {
+ if (IS_VAR == IS_VAR && UNEXPECTED(Z_ISERROR_P(variable_ptr))) {
zval_ptr_dtor_nogc(free_op2);
- if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ if (UNEXPECTED(0)) {
ZVAL_NULL(EX_VAR(opline->result.var));
}
} else {
value = zend_assign_to_variable(variable_ptr, value, IS_TMP_VAR);
- if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ if (UNEXPECTED(0)) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+ if (UNEXPECTED(free_op1)) {zval_ptr_dtor_nogc(free_op1);};
+ /* zend_assign_to_variable() always takes care of op2, never free it! */
+ }
+
+ ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_SPEC_VAR_TMP_RETVAL_USED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zend_free_op free_op1, free_op2;
+ zval *value;
+ zval *variable_ptr;
+
+ SAVE_OPLINE();
+ value = _get_zval_ptr_tmp(opline->op2.var, execute_data, &free_op2);
+ variable_ptr = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1);
+
+ if (IS_VAR == IS_VAR && UNEXPECTED(Z_ISERROR_P(variable_ptr))) {
+ zval_ptr_dtor_nogc(free_op2);
+ if (UNEXPECTED(1)) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+ } else {
+ value = zend_assign_to_variable(variable_ptr, value, IS_TMP_VAR);
+ if (UNEXPECTED(1)) {
ZVAL_COPY(EX_VAR(opline->result.var), value);
}
if (UNEXPECTED(free_op1)) {zval_ptr_dtor_nogc(free_op1);};
@@ -18301,7 +20205,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_VAR_TMP_HANDLER(ZEN
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) {
+ if (IS_VAR & (IS_CONST|IS_TMP_VAR)) {
zval *value;
zend_error(E_NOTICE, "Only variable references should be yielded by reference");
@@ -18309,25 +20213,19 @@ 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 (IS_VAR == IS_CONST) {
- if (UNEXPECTED(Z_OPT_COPYABLE(generator->value))) {
- zval_copy_ctor_func(&generator->value);
+ if (UNEXPECTED(Z_OPT_REFCOUNTED(generator->value))) {
+ Z_ADDREF(generator->value);
}
}
} else {
zval *value_ptr = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1);
- if (IS_VAR == IS_VAR && UNEXPECTED(value_ptr == NULL)) {
- zend_throw_error(NULL, "Cannot yield string offsets by reference");
- zval_ptr_dtor_nogc(EX_VAR(opline->op2.var));
- HANDLE_EXCEPTION();
- }
-
/* If a function call result is yielded and the function did
* not return by reference we throw a notice. */
if (IS_VAR == IS_VAR &&
(value_ptr == &EG(uninitialized_zval) ||
(opline->extended_value == ZEND_RETURNS_FUNCTION &&
- !(Z_VAR_FLAGS_P(value_ptr) & IS_VAR_RET_REF)))) {
+ !Z_ISREF_P(value_ptr)))) {
zend_error(E_NOTICE, "Only variable references should be yielded by reference");
} else {
ZVAL_MAKE_REF(value_ptr);
@@ -18342,8 +20240,8 @@ 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_COPY_VALUE(&generator->value, value);
- if (UNEXPECTED(Z_OPT_COPYABLE(generator->value))) {
- zval_copy_ctor_func(&generator->value);
+ if (UNEXPECTED(Z_OPT_REFCOUNTED(generator->value))) {
+ Z_ADDREF(generator->value);
}
} else if (IS_VAR == IS_TMP_VAR) {
ZVAL_COPY_VALUE(&generator->value, value);
@@ -18370,8 +20268,8 @@ 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_COPY_VALUE(&generator->key, key);
- if (UNEXPECTED(Z_OPT_COPYABLE(generator->key))) {
- zval_copy_ctor_func(&generator->key);
+ if (UNEXPECTED(Z_OPT_REFCOUNTED(generator->key))) {
+ Z_ADDREF(generator->key);
}
} else if (IS_TMP_VAR == IS_TMP_VAR) {
ZVAL_COPY_VALUE(&generator->key, key);
@@ -18452,7 +20350,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_NOT_IDENTICAL_SPEC_VAR_VAR_
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_SPEC_VAR_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_SPEC_VAR_VAR_RETVAL_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
zend_free_op free_op1, free_op2;
@@ -18463,14 +20361,42 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_SPEC_VAR_VAR_HANDLER(ZE
value = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2);
variable_ptr = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1);
- if (IS_VAR == IS_VAR && UNEXPECTED(variable_ptr == &EG(error_zval))) {
+ if (IS_VAR == IS_VAR && UNEXPECTED(Z_ISERROR_P(variable_ptr))) {
zval_ptr_dtor_nogc(free_op2);
- if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ if (UNEXPECTED(0)) {
ZVAL_NULL(EX_VAR(opline->result.var));
}
} else {
value = zend_assign_to_variable(variable_ptr, value, IS_VAR);
- if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ if (UNEXPECTED(0)) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+ if (UNEXPECTED(free_op1)) {zval_ptr_dtor_nogc(free_op1);};
+ /* zend_assign_to_variable() always takes care of op2, never free it! */
+ }
+
+ ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_SPEC_VAR_VAR_RETVAL_USED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zend_free_op free_op1, free_op2;
+ zval *value;
+ zval *variable_ptr;
+
+ SAVE_OPLINE();
+ value = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2);
+ variable_ptr = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1);
+
+ if (IS_VAR == IS_VAR && UNEXPECTED(Z_ISERROR_P(variable_ptr))) {
+ zval_ptr_dtor_nogc(free_op2);
+ if (UNEXPECTED(1)) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+ } else {
+ value = zend_assign_to_variable(variable_ptr, value, IS_VAR);
+ if (UNEXPECTED(1)) {
ZVAL_COPY(EX_VAR(opline->result.var), value);
}
if (UNEXPECTED(free_op1)) {zval_ptr_dtor_nogc(free_op1);};
@@ -18489,54 +20415,49 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_REF_SPEC_VAR_VAR_HANDLE
SAVE_OPLINE();
value_ptr = _get_zval_ptr_ptr_var(opline->op2.var, execute_data, &free_op2);
+ variable_ptr = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1);
- if (IS_VAR == IS_VAR && UNEXPECTED(value_ptr == NULL)) {
- zend_throw_error(NULL, "Cannot create references to/from string offsets nor overloaded objects");
- zval_ptr_dtor_nogc(EX_VAR(opline->op1.var));
- HANDLE_EXCEPTION();
- }
if (IS_VAR == IS_VAR &&
UNEXPECTED(Z_TYPE_P(EX_VAR(opline->op1.var)) != IS_INDIRECT) &&
- UNEXPECTED(!Z_ISREF_P(EX_VAR(opline->op1.var)))) {
+ UNEXPECTED(!Z_ISREF_P(EX_VAR(opline->op1.var))) &&
+ UNEXPECTED(!Z_ISERROR_P(EX_VAR(opline->op1.var)))) {
+
zend_throw_error(NULL, "Cannot assign by reference to overloaded object");
if (UNEXPECTED(free_op2)) {zval_ptr_dtor_nogc(free_op2);};
HANDLE_EXCEPTION();
- }
- if (IS_VAR == IS_VAR &&
- (value_ptr == &EG(uninitialized_zval) ||
- (opline->extended_value == ZEND_RETURNS_FUNCTION &&
- !(Z_VAR_FLAGS_P(value_ptr) & IS_VAR_RET_REF)))) {
- if (!(free_op2 != NULL) && UNEXPECTED(Z_TYPE_P(EX_VAR(opline->op2.var)) != IS_INDIRECT)) { /* undo the effect of get_zval_ptr_ptr() */
- Z_TRY_ADDREF_P(value_ptr);
- }
+
+ } else if (IS_VAR == IS_VAR &&
+ opline->extended_value == ZEND_RETURNS_FUNCTION &&
+ UNEXPECTED(!Z_ISREF_P(value_ptr))) {
zend_error(E_NOTICE, "Only variables should be assigned by reference");
if (UNEXPECTED(EG(exception) != NULL)) {
if (UNEXPECTED(free_op2)) {zval_ptr_dtor_nogc(free_op2);};
HANDLE_EXCEPTION();
}
- ZEND_VM_TAIL_CALL(ZEND_ASSIGN_SPEC_VAR_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU));
- }
- variable_ptr = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1);
- if (IS_VAR == IS_VAR && UNEXPECTED(variable_ptr == NULL)) {
- zend_throw_error(NULL, "Cannot create references to/from string offsets nor overloaded objects");
- if (UNEXPECTED(free_op2)) {zval_ptr_dtor_nogc(free_op2);};
- HANDLE_EXCEPTION();
- }
- if ((IS_VAR == IS_VAR && UNEXPECTED(variable_ptr == &EG(error_zval))) ||
- (IS_VAR == IS_VAR && UNEXPECTED(value_ptr == &EG(error_zval)))) {
- variable_ptr = &EG(uninitialized_zval);
+ value_ptr = zend_assign_to_variable(variable_ptr, value_ptr, IS_VAR);
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value_ptr);
+ }
+ /* zend_assign_to_variable() always takes care of op2, never free it! */
+
} else {
- zend_assign_to_variable_reference(variable_ptr, value_ptr);
- }
- if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
- ZVAL_COPY(EX_VAR(opline->result.var), variable_ptr);
+ if ((IS_VAR == IS_VAR && UNEXPECTED(Z_ISERROR_P(variable_ptr))) ||
+ (IS_VAR == IS_VAR && UNEXPECTED(Z_ISERROR_P(value_ptr)))) {
+ variable_ptr = &EG(uninitialized_zval);
+ } else {
+ zend_assign_to_variable_reference(variable_ptr, value_ptr);
+ }
+
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), variable_ptr);
+ }
+
+ if (UNEXPECTED(free_op2)) {zval_ptr_dtor_nogc(free_op2);};
}
if (UNEXPECTED(free_op1)) {zval_ptr_dtor_nogc(free_op1);};
- if (UNEXPECTED(free_op2)) {zval_ptr_dtor_nogc(free_op2);};
-
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
@@ -18567,7 +20488,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_VAR_VAR_HANDLER(ZEN
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) {
+ if (IS_VAR & (IS_CONST|IS_TMP_VAR)) {
zval *value;
zend_error(E_NOTICE, "Only variable references should be yielded by reference");
@@ -18575,25 +20496,19 @@ 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 (IS_VAR == IS_CONST) {
- if (UNEXPECTED(Z_OPT_COPYABLE(generator->value))) {
- zval_copy_ctor_func(&generator->value);
+ if (UNEXPECTED(Z_OPT_REFCOUNTED(generator->value))) {
+ Z_ADDREF(generator->value);
}
}
} else {
zval *value_ptr = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1);
- if (IS_VAR == IS_VAR && UNEXPECTED(value_ptr == NULL)) {
- zend_throw_error(NULL, "Cannot yield string offsets by reference");
- zval_ptr_dtor_nogc(EX_VAR(opline->op2.var));
- HANDLE_EXCEPTION();
- }
-
/* If a function call result is yielded and the function did
* not return by reference we throw a notice. */
if (IS_VAR == IS_VAR &&
(value_ptr == &EG(uninitialized_zval) ||
(opline->extended_value == ZEND_RETURNS_FUNCTION &&
- !(Z_VAR_FLAGS_P(value_ptr) & IS_VAR_RET_REF)))) {
+ !Z_ISREF_P(value_ptr)))) {
zend_error(E_NOTICE, "Only variable references should be yielded by reference");
} else {
ZVAL_MAKE_REF(value_ptr);
@@ -18608,8 +20523,8 @@ 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_COPY_VALUE(&generator->value, value);
- if (UNEXPECTED(Z_OPT_COPYABLE(generator->value))) {
- zval_copy_ctor_func(&generator->value);
+ if (UNEXPECTED(Z_OPT_REFCOUNTED(generator->value))) {
+ Z_ADDREF(generator->value);
}
} else if (IS_VAR == IS_TMP_VAR) {
ZVAL_COPY_VALUE(&generator->value, value);
@@ -18636,8 +20551,8 @@ 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_COPY_VALUE(&generator->key, key);
- if (UNEXPECTED(Z_OPT_COPYABLE(generator->key))) {
- zval_copy_ctor_func(&generator->key);
+ if (UNEXPECTED(Z_OPT_REFCOUNTED(generator->key))) {
+ Z_ADDREF(generator->key);
}
} else if (IS_VAR == IS_TMP_VAR) {
ZVAL_COPY_VALUE(&generator->key, key);
@@ -18686,307 +20601,314 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_binary_assign_op_dim_helper_SP
{
USE_OPLINE
zend_free_op free_op1, free_op_data1;
- zval *var_ptr, rv;
+ zval *var_ptr;
zval *value, *container, *dim;
SAVE_OPLINE();
container = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1);
- if (IS_VAR == IS_UNUSED && UNEXPECTED(Z_OBJ_P(container) == NULL)) {
- zend_throw_error(NULL, "Using $this when not in object context");
- FREE_UNFETCHED_OP((opline+1)->op1_type, (opline+1)->op1.var);
-
- HANDLE_EXCEPTION();
- }
- if (IS_VAR == IS_VAR && UNEXPECTED(container == NULL)) {
- zend_throw_error(NULL, "Cannot use string offset as an array");
- FREE_UNFETCHED_OP((opline+1)->op1_type, (opline+1)->op1.var);
-
- HANDLE_EXCEPTION();
- }
- dim = NULL;
+ if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) {
+assign_dim_op_array:
+ SEPARATE_ARRAY(container);
+assign_dim_op_new_array:
+ if (IS_UNUSED == IS_UNUSED) {
+ var_ptr = zend_hash_next_index_insert(Z_ARRVAL_P(container), &EG(uninitialized_zval));
+ if (UNEXPECTED(!var_ptr)) {
+ zend_error(E_WARNING, "Cannot add element to the array as the next element is already occupied");
+ goto assign_dim_op_ret_null;
+ }
+ } else {
+ dim = NULL;
- do {
- if (IS_VAR == IS_UNUSED || UNEXPECTED(Z_TYPE_P(container) != IS_ARRAY)) {
- if (IS_VAR != IS_UNUSED) {
- ZVAL_DEREF(container);
+ if (IS_UNUSED == IS_CONST) {
+ var_ptr = zend_fetch_dimension_address_inner_RW_CONST(Z_ARRVAL_P(container), dim);
+ } else {
+ var_ptr = zend_fetch_dimension_address_inner_RW(Z_ARRVAL_P(container), dim);
}
- if (IS_VAR == IS_UNUSED || EXPECTED(Z_TYPE_P(container) == IS_OBJECT)) {
- value = get_zval_ptr_r((opline+1)->op1_type, (opline+1)->op1, execute_data, &free_op_data1);
- zend_binary_assign_op_obj_dim(container, dim, value, UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL, binary_op);
- break;
+ if (UNEXPECTED(!var_ptr)) {
+ goto assign_dim_op_ret_null;
}
+ ZVAL_DEREF(var_ptr);
+ SEPARATE_ZVAL_NOREF(var_ptr);
}
- zend_fetch_dimension_address_RW(&rv, container, dim, IS_UNUSED);
value = get_zval_ptr_r((opline+1)->op1_type, (opline+1)->op1, execute_data, &free_op_data1);
- ZEND_ASSERT(Z_TYPE(rv) == IS_INDIRECT);
- var_ptr = Z_INDIRECT(rv);
- if (UNEXPECTED(var_ptr == NULL)) {
- zend_throw_error(NULL, "Cannot use assign-op operators with overloaded objects nor string offsets");
+ binary_op(var_ptr, var_ptr, value);
- FREE_OP(free_op_data1);
- if (UNEXPECTED(free_op1)) {zval_ptr_dtor_nogc(free_op1);};
- HANDLE_EXCEPTION();
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), var_ptr);
}
-
- if (UNEXPECTED(var_ptr == &EG(error_zval))) {
- if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
- ZVAL_NULL(EX_VAR(opline->result.var));
+ } else {
+ if (EXPECTED(Z_ISREF_P(container))) {
+ container = Z_REFVAL_P(container);
+ if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) {
+ goto assign_dim_op_array;
}
- } else {
- ZVAL_DEREF(var_ptr);
- SEPARATE_ZVAL_NOREF(var_ptr);
+ } else if (IS_VAR == IS_CV && UNEXPECTED(Z_TYPE_INFO_P(container) == IS_UNDEF)) {
+ container = GET_OP1_UNDEF_CV(container, BP_VAR_RW);
+assign_dim_op_convert_to_array:
+ ZVAL_NEW_ARR(container);
+ zend_hash_init(Z_ARRVAL_P(container), 8, NULL, ZVAL_PTR_DTOR, 0);
+ goto assign_dim_op_new_array;
+ }
- binary_op(var_ptr, var_ptr, value);
+ dim = NULL;
- if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
- ZVAL_COPY(EX_VAR(opline->result.var), var_ptr);
+ if (EXPECTED(Z_TYPE_P(container) == IS_OBJECT)) {
+ value = get_zval_ptr_r((opline+1)->op1_type, (opline+1)->op1, execute_data, &free_op_data1);
+ zend_binary_assign_op_obj_dim(container, dim, value, UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL, binary_op);
+ } else {
+ if (UNEXPECTED(Z_TYPE_P(container) == IS_STRING)) {
+ if (IS_UNUSED == IS_UNUSED) {
+ zend_throw_error(NULL, "[] operator not supported for strings");
+ } else {
+ zend_check_string_offset(dim, BP_VAR_RW);
+ zend_wrong_string_offset();
+ }
+ } else if (EXPECTED(Z_TYPE_P(container) <= IS_FALSE)) {
+ goto assign_dim_op_convert_to_array;
+ } else {
+ if (UNEXPECTED(IS_VAR != IS_VAR || EXPECTED(!Z_ISERROR_P(container)))) {
+ zend_error(E_WARNING, "Cannot use a scalar value as an array");
+ }
+assign_dim_op_ret_null:
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
}
+ value = get_zval_ptr_r((opline+1)->op1_type, (opline+1)->op1, execute_data, &free_op_data1);
}
- } while (0);
+ }
FREE_OP(free_op_data1);
if (UNEXPECTED(free_op1)) {zval_ptr_dtor_nogc(free_op1);};
ZEND_VM_NEXT_OPCODE_EX(1, 2);
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_ADD_SPEC_VAR_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_ADD_SPEC_VAR_UNUSED_DIM_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
#if 0 || (IS_UNUSED != IS_UNUSED)
USE_OPLINE
# if 0 || (IS_VAR != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_VAR_UNUSED(add_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
-# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
+ if (EXPECTED(1)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_UNUSED(add_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_UNUSED(add_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_UNUSED(add_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#else
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_UNUSED(add_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#endif
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_SUB_SPEC_VAR_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_SUB_SPEC_VAR_UNUSED_DIM_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
#if 0 || (IS_UNUSED != IS_UNUSED)
USE_OPLINE
# if 0 || (IS_VAR != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_VAR_UNUSED(sub_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
-# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
+ if (EXPECTED(1)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_UNUSED(sub_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_UNUSED(sub_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_UNUSED(sub_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#else
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_UNUSED(sub_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#endif
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_MUL_SPEC_VAR_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_MUL_SPEC_VAR_UNUSED_DIM_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
#if 0 || (IS_UNUSED != IS_UNUSED)
USE_OPLINE
# if 0 || (IS_VAR != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_VAR_UNUSED(mul_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
-# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
+ if (EXPECTED(1)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_UNUSED(mul_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_UNUSED(mul_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_UNUSED(mul_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#else
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_UNUSED(mul_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#endif
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIV_SPEC_VAR_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIV_SPEC_VAR_UNUSED_DIM_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
#if 0 || (IS_UNUSED != IS_UNUSED)
USE_OPLINE
# if 0 || (IS_VAR != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_VAR_UNUSED(div_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
-# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
+ if (EXPECTED(1)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_UNUSED(div_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_UNUSED(div_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_UNUSED(div_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#else
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_UNUSED(div_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#endif
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_MOD_SPEC_VAR_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_MOD_SPEC_VAR_UNUSED_DIM_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
#if 0 || (IS_UNUSED != IS_UNUSED)
USE_OPLINE
# if 0 || (IS_VAR != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_VAR_UNUSED(mod_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
-# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
+ if (EXPECTED(1)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_UNUSED(mod_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_UNUSED(mod_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_UNUSED(mod_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#else
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_UNUSED(mod_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#endif
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_SL_SPEC_VAR_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_SL_SPEC_VAR_UNUSED_DIM_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
#if 0 || (IS_UNUSED != IS_UNUSED)
USE_OPLINE
# if 0 || (IS_VAR != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_VAR_UNUSED(shift_left_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
-# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
+ if (EXPECTED(1)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_UNUSED(shift_left_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_UNUSED(shift_left_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_UNUSED(shift_left_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#else
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_UNUSED(shift_left_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#endif
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_SR_SPEC_VAR_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_SR_SPEC_VAR_UNUSED_DIM_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
#if 0 || (IS_UNUSED != IS_UNUSED)
USE_OPLINE
# if 0 || (IS_VAR != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_VAR_UNUSED(shift_right_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
-# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
+ if (EXPECTED(1)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_UNUSED(shift_right_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_UNUSED(shift_right_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_UNUSED(shift_right_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#else
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_UNUSED(shift_right_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#endif
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_CONCAT_SPEC_VAR_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_CONCAT_SPEC_VAR_UNUSED_DIM_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
#if 0 || (IS_UNUSED != IS_UNUSED)
USE_OPLINE
# if 0 || (IS_VAR != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_VAR_UNUSED(concat_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
-# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
+ if (EXPECTED(1)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_UNUSED(concat_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_UNUSED(concat_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_UNUSED(concat_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#else
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_UNUSED(concat_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#endif
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_BW_OR_SPEC_VAR_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_BW_OR_SPEC_VAR_UNUSED_DIM_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
#if 0 || (IS_UNUSED != IS_UNUSED)
USE_OPLINE
# if 0 || (IS_VAR != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_VAR_UNUSED(bitwise_or_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
-# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
+ if (EXPECTED(1)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_UNUSED(bitwise_or_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_UNUSED(bitwise_or_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_UNUSED(bitwise_or_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#else
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_UNUSED(bitwise_or_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#endif
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_BW_AND_SPEC_VAR_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_BW_AND_SPEC_VAR_UNUSED_DIM_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
#if 0 || (IS_UNUSED != IS_UNUSED)
USE_OPLINE
# if 0 || (IS_VAR != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_VAR_UNUSED(bitwise_and_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
-# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
+ if (EXPECTED(1)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_UNUSED(bitwise_and_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_UNUSED(bitwise_and_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_UNUSED(bitwise_and_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#else
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_UNUSED(bitwise_and_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#endif
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_BW_XOR_SPEC_VAR_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_BW_XOR_SPEC_VAR_UNUSED_DIM_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
#if 0 || (IS_UNUSED != IS_UNUSED)
USE_OPLINE
# if 0 || (IS_VAR != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_VAR_UNUSED(bitwise_xor_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
-# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
+ if (EXPECTED(1)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_UNUSED(bitwise_xor_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_UNUSED(bitwise_xor_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_UNUSED(bitwise_xor_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#else
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_UNUSED(bitwise_xor_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#endif
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_POW_SPEC_VAR_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_POW_SPEC_VAR_UNUSED_DIM_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
#if 0 || (IS_UNUSED != IS_UNUSED)
USE_OPLINE
# if 0 || (IS_VAR != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_VAR_UNUSED(pow_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
-# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
+ if (EXPECTED(1)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_UNUSED(pow_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_UNUSED(pow_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_UNUSED(pow_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#else
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_UNUSED(pow_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#endif
@@ -19001,14 +20923,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_DIM_W_SPEC_VAR_UNUSED_HA
SAVE_OPLINE();
container = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1);
- if (IS_VAR == IS_VAR && UNEXPECTED(container == NULL)) {
- zend_throw_error(NULL, "Cannot use string offset as an array");
- HANDLE_EXCEPTION();
- }
zend_fetch_dimension_address_W(EX_VAR(opline->result.var), container, NULL, IS_UNUSED);
if (IS_VAR == IS_VAR && READY_TO_DESTROY(free_op1)) {
- EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var), 1);
+ EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var));
}
if (UNEXPECTED(free_op1)) {zval_ptr_dtor_nogc(free_op1);};
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
@@ -19023,14 +20941,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_DIM_RW_SPEC_VAR_UNUSED_H
SAVE_OPLINE();
container = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1);
- if (IS_VAR == IS_VAR && UNEXPECTED(container == NULL)) {
- zend_throw_error(NULL, "Cannot use string offset as an array");
- HANDLE_EXCEPTION();
- }
zend_fetch_dimension_address_RW(EX_VAR(opline->result.var), container, NULL, IS_UNUSED);
if (IS_VAR == IS_VAR && READY_TO_DESTROY(free_op1)) {
- EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var), 1);
+ EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var));
}
if (UNEXPECTED(free_op1)) {zval_ptr_dtor_nogc(free_op1);};
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
@@ -19045,21 +20959,16 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_DIM_FUNC_ARG_SPEC_VAR_UN
SAVE_OPLINE();
if (zend_is_by_ref_func_arg_fetch(opline, EX(call))) {
- if (IS_VAR == IS_CONST || IS_VAR == IS_TMP_VAR) {
+ if ((IS_VAR & (IS_CONST|IS_TMP_VAR))) {
zend_throw_error(NULL, "Cannot use temporary expression in write context");
zval_ptr_dtor_nogc(EX_VAR(opline->op1.var));
HANDLE_EXCEPTION();
}
container = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1);
- if (IS_VAR == IS_VAR && UNEXPECTED(container == NULL)) {
- zend_throw_error(NULL, "Cannot use string offset as an array");
-
- HANDLE_EXCEPTION();
- }
zend_fetch_dimension_address_W(EX_VAR(opline->result.var), container, NULL, IS_UNUSED);
if (IS_VAR == IS_VAR && READY_TO_DESTROY(free_op1)) {
- EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var), 1);
+ EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var));
}
if (UNEXPECTED(free_op1)) {zval_ptr_dtor_nogc(free_op1);};
@@ -19078,12 +20987,12 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_DIM_FUNC_ARG_SPEC_VAR_UN
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_VAR_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_VAR_UNUSED_OP_DATA_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
zend_free_op free_op1;
zval *object_ptr;
- zend_free_op free_op_data1;
+
zval *value;
zval *variable_ptr;
zval *dim;
@@ -19091,40 +21000,214 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_VAR_UNUSED_HAN
SAVE_OPLINE();
object_ptr = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1);
- if (IS_VAR == IS_VAR && UNEXPECTED(object_ptr == NULL)) {
- zend_throw_error(NULL, "Cannot use string offset as an array");
- FREE_UNFETCHED_OP((opline+1)->op1_type, (opline+1)->op1.var);
+ if (EXPECTED(Z_TYPE_P(object_ptr) == IS_ARRAY)) {
+try_assign_dim_array:
+ SEPARATE_ARRAY(object_ptr);
+ if (IS_UNUSED == IS_UNUSED) {
+ variable_ptr = zend_hash_next_index_insert(Z_ARRVAL_P(object_ptr), &EG(uninitialized_zval));
+ if (UNEXPECTED(variable_ptr == NULL)) {
+ zend_error(E_WARNING, "Cannot add element to the array as the next element is already occupied");
+ goto assign_dim_error;
+ }
+ } else {
+ dim = NULL;
+ if (IS_UNUSED == IS_CONST) {
+ variable_ptr = zend_fetch_dimension_address_inner_W_CONST(Z_ARRVAL_P(object_ptr), dim);
+ } else {
+ variable_ptr = zend_fetch_dimension_address_inner_W(Z_ARRVAL_P(object_ptr), dim);
+ }
+ if (UNEXPECTED(variable_ptr == NULL)) {
+ goto assign_dim_error;
+ }
+ }
+ value = EX_CONSTANT((opline+1)->op1);
+ value = zend_assign_to_variable(variable_ptr, value, IS_CONST);
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+ } 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)) {
+ dim = NULL;
+ value = EX_CONSTANT((opline+1)->op1);
- HANDLE_EXCEPTION();
+ zend_assign_to_object_dim(object_ptr, dim, value);
+
+ if (UNEXPECTED(RETURN_VALUE_USED(opline)) && EXPECTED(!EG(exception))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+
+ } else if (EXPECTED(Z_TYPE_P(object_ptr) == IS_STRING)) {
+ if (IS_UNUSED == IS_UNUSED) {
+ zend_throw_error(NULL, "[] operator not supported for strings");
+
+ if (UNEXPECTED(free_op1)) {zval_ptr_dtor_nogc(free_op1);};
+ HANDLE_EXCEPTION();
+ } else {
+ dim = NULL;
+ value = EX_CONSTANT((opline+1)->op1);
+ zend_assign_to_string_offset(object_ptr, dim, value, (UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL));
+
+ }
+ } else if (EXPECTED(Z_TYPE_P(object_ptr) <= IS_FALSE)) {
+ 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 (IS_VAR != IS_VAR || EXPECTED(!Z_ISERROR_P(object_ptr))) {
+ zend_error(E_WARNING, "Cannot use a scalar value as an array");
+ }
+ dim = NULL;
+assign_dim_error:
+
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+ }
}
+ if (IS_UNUSED != IS_UNUSED) {
+
+ }
+ if (UNEXPECTED(free_op1)) {zval_ptr_dtor_nogc(free_op1);};
+ /* assign_dim has two opcodes! */
+ ZEND_VM_NEXT_OPCODE_EX(1, 2);
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_VAR_UNUSED_OP_DATA_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zend_free_op free_op1;
+ zval *object_ptr;
+ zend_free_op free_op_data;
+ zval *value;
+ zval *variable_ptr;
+ zval *dim;
+
+ SAVE_OPLINE();
+ object_ptr = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1);
if (EXPECTED(Z_TYPE_P(object_ptr) == IS_ARRAY)) {
try_assign_dim_array:
+ SEPARATE_ARRAY(object_ptr);
if (IS_UNUSED == IS_UNUSED) {
- SEPARATE_ARRAY(object_ptr);
variable_ptr = zend_hash_next_index_insert(Z_ARRVAL_P(object_ptr), &EG(uninitialized_zval));
if (UNEXPECTED(variable_ptr == NULL)) {
zend_error(E_WARNING, "Cannot add element to the array as the next element is already occupied");
- variable_ptr = &EG(error_zval);
+ goto assign_dim_error;
}
} else {
dim = NULL;
- SEPARATE_ARRAY(object_ptr);
- variable_ptr = zend_fetch_dimension_address_inner(Z_ARRVAL_P(object_ptr), dim, IS_UNUSED, BP_VAR_W);
-
+ if (IS_UNUSED == IS_CONST) {
+ variable_ptr = zend_fetch_dimension_address_inner_W_CONST(Z_ARRVAL_P(object_ptr), dim);
+ } else {
+ variable_ptr = zend_fetch_dimension_address_inner_W(Z_ARRVAL_P(object_ptr), dim);
+ }
+ if (UNEXPECTED(variable_ptr == NULL)) {
+ goto assign_dim_error;
+ }
}
- value = get_zval_ptr_r((opline+1)->op1_type, (opline+1)->op1, execute_data, &free_op_data1);
- if (UNEXPECTED(variable_ptr == &EG(error_zval))) {
- FREE_OP(free_op_data1);
+ value = _get_zval_ptr_tmp((opline+1)->op1.var, execute_data, &free_op_data);
+ value = zend_assign_to_variable(variable_ptr, value, IS_TMP_VAR);
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+ } 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)) {
+ dim = NULL;
+ value = _get_zval_ptr_tmp((opline+1)->op1.var, execute_data, &free_op_data);
+
+ zend_assign_to_object_dim(object_ptr, dim, value);
+
+ if (UNEXPECTED(RETURN_VALUE_USED(opline)) && EXPECTED(!EG(exception))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+
+ zval_ptr_dtor_nogc(free_op_data);
+ } else if (EXPECTED(Z_TYPE_P(object_ptr) == IS_STRING)) {
+ if (IS_UNUSED == IS_UNUSED) {
+ zend_throw_error(NULL, "[] operator not supported for strings");
+ zval_ptr_dtor_nogc(EX_VAR((opline+1)->op1.var));
+ if (UNEXPECTED(free_op1)) {zval_ptr_dtor_nogc(free_op1);};
+ HANDLE_EXCEPTION();
+ } else {
+ dim = NULL;
+ value = _get_zval_ptr_tmp((opline+1)->op1.var, execute_data, &free_op_data);
+ zend_assign_to_string_offset(object_ptr, dim, value, (UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL));
+ zval_ptr_dtor_nogc(free_op_data);
+ }
+ } else if (EXPECTED(Z_TYPE_P(object_ptr) <= IS_FALSE)) {
+ 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 (IS_VAR != IS_VAR || EXPECTED(!Z_ISERROR_P(object_ptr))) {
+ zend_error(E_WARNING, "Cannot use a scalar value as an array");
+ }
+ dim = NULL;
+assign_dim_error:
+ zval_ptr_dtor_nogc(EX_VAR((opline+1)->op1.var));
if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
ZVAL_NULL(EX_VAR(opline->result.var));
}
+ }
+ }
+ if (IS_UNUSED != IS_UNUSED) {
+
+ }
+ if (UNEXPECTED(free_op1)) {zval_ptr_dtor_nogc(free_op1);};
+ /* assign_dim has two opcodes! */
+ ZEND_VM_NEXT_OPCODE_EX(1, 2);
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_VAR_UNUSED_OP_DATA_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zend_free_op free_op1;
+ zval *object_ptr;
+ zend_free_op free_op_data;
+ zval *value;
+ zval *variable_ptr;
+ zval *dim;
+
+ SAVE_OPLINE();
+ object_ptr = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1);
+
+ if (EXPECTED(Z_TYPE_P(object_ptr) == IS_ARRAY)) {
+try_assign_dim_array:
+ SEPARATE_ARRAY(object_ptr);
+ if (IS_UNUSED == IS_UNUSED) {
+ variable_ptr = zend_hash_next_index_insert(Z_ARRVAL_P(object_ptr), &EG(uninitialized_zval));
+ if (UNEXPECTED(variable_ptr == NULL)) {
+ zend_error(E_WARNING, "Cannot add element to the array as the next element is already occupied");
+ goto assign_dim_error;
+ }
} else {
- value = zend_assign_to_variable(variable_ptr, value, (opline+1)->op1_type);
- if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
- ZVAL_COPY(EX_VAR(opline->result.var), value);
+ dim = NULL;
+ if (IS_UNUSED == IS_CONST) {
+ variable_ptr = zend_fetch_dimension_address_inner_W_CONST(Z_ARRVAL_P(object_ptr), dim);
+ } else {
+ variable_ptr = zend_fetch_dimension_address_inner_W(Z_ARRVAL_P(object_ptr), dim);
+ }
+ if (UNEXPECTED(variable_ptr == NULL)) {
+ goto assign_dim_error;
}
}
+ value = _get_zval_ptr_var((opline+1)->op1.var, execute_data, &free_op_data);
+ value = zend_assign_to_variable(variable_ptr, value, IS_VAR);
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
} else {
if (EXPECTED(Z_ISREF_P(object_ptr))) {
object_ptr = Z_REFVAL_P(object_ptr);
@@ -19133,52 +21216,138 @@ try_assign_dim_array:
}
}
if (EXPECTED(Z_TYPE_P(object_ptr) == IS_OBJECT)) {
+ dim = NULL;
+ value = _get_zval_ptr_var((opline+1)->op1.var, execute_data, &free_op_data);
- zval *property_name = NULL;
+ zend_assign_to_object_dim(object_ptr, dim, value);
- 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);
+ if (UNEXPECTED(RETURN_VALUE_USED(opline)) && EXPECTED(!EG(exception))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+ zval_ptr_dtor_nogc(free_op_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_throw_error(NULL, "[] operator not supported for strings");
- FREE_UNFETCHED_OP((opline+1)->op1_type, (opline+1)->op1.var);
- if (UNEXPECTED(free_op1)) {zval_ptr_dtor_nogc(free_op1);};
- HANDLE_EXCEPTION();
- } else {
- zend_long offset;
+ if (IS_UNUSED == IS_UNUSED) {
+ zend_throw_error(NULL, "[] operator not supported for strings");
+ zval_ptr_dtor_nogc(EX_VAR((opline+1)->op1.var));
+ if (UNEXPECTED(free_op1)) {zval_ptr_dtor_nogc(free_op1);};
+ HANDLE_EXCEPTION();
+ } else {
+ dim = NULL;
+ value = _get_zval_ptr_var_deref((opline+1)->op1.var, execute_data, &free_op_data);
+ zend_assign_to_string_offset(object_ptr, dim, value, (UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL));
+ zval_ptr_dtor_nogc(free_op_data);
+ }
+ } else if (EXPECTED(Z_TYPE_P(object_ptr) <= IS_FALSE)) {
+ 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 (IS_VAR != IS_VAR || EXPECTED(!Z_ISERROR_P(object_ptr))) {
+ zend_error(E_WARNING, "Cannot use a scalar value as an array");
+ }
+ dim = NULL;
+assign_dim_error:
+ zval_ptr_dtor_nogc(EX_VAR((opline+1)->op1.var));
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+ }
+ }
+ if (IS_UNUSED != IS_UNUSED) {
+
+ }
+ if (UNEXPECTED(free_op1)) {zval_ptr_dtor_nogc(free_op1);};
+ /* assign_dim has two opcodes! */
+ ZEND_VM_NEXT_OPCODE_EX(1, 2);
+}
- dim = NULL;
- offset = zend_fetch_string_offset(object_ptr, dim, BP_VAR_W);
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_VAR_UNUSED_OP_DATA_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zend_free_op free_op1;
+ zval *object_ptr;
- value = get_zval_ptr_r_deref((opline+1)->op1_type, (opline+1)->op1, execute_data, &free_op_data1);
- 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);
- }
+ zval *value;
+ zval *variable_ptr;
+ zval *dim;
+
+ SAVE_OPLINE();
+ object_ptr = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1);
+
+ if (EXPECTED(Z_TYPE_P(object_ptr) == IS_ARRAY)) {
+try_assign_dim_array:
+ SEPARATE_ARRAY(object_ptr);
+ if (IS_UNUSED == IS_UNUSED) {
+ variable_ptr = zend_hash_next_index_insert(Z_ARRVAL_P(object_ptr), &EG(uninitialized_zval));
+ if (UNEXPECTED(variable_ptr == NULL)) {
+ zend_error(E_WARNING, "Cannot add element to the array as the next element is already occupied");
+ goto assign_dim_error;
+ }
+ } else {
+ dim = NULL;
+ if (IS_UNUSED == IS_CONST) {
+ variable_ptr = zend_fetch_dimension_address_inner_W_CONST(Z_ARRVAL_P(object_ptr), dim);
} 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);
+ variable_ptr = zend_fetch_dimension_address_inner_W(Z_ARRVAL_P(object_ptr), dim);
+ }
+ if (UNEXPECTED(variable_ptr == NULL)) {
+ goto assign_dim_error;
+ }
+ }
+ value = _get_zval_ptr_cv_BP_VAR_R(execute_data, (opline+1)->op1.var);
+ value = zend_assign_to_variable(variable_ptr, value, IS_CV);
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+ } 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;
}
- } 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;
+ }
+ if (EXPECTED(Z_TYPE_P(object_ptr) == IS_OBJECT)) {
+ dim = NULL;
+ value = _get_zval_ptr_cv_BP_VAR_R(execute_data, (opline+1)->op1.var);
+
+ zend_assign_to_object_dim(object_ptr, dim, value);
+
+ if (UNEXPECTED(RETURN_VALUE_USED(opline)) && EXPECTED(!EG(exception))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
}
- goto assign_dim_convert_to_array;
+
+ } else if (EXPECTED(Z_TYPE_P(object_ptr) == IS_STRING)) {
+ if (IS_UNUSED == IS_UNUSED) {
+ zend_throw_error(NULL, "[] operator not supported for strings");
+
+ if (UNEXPECTED(free_op1)) {zval_ptr_dtor_nogc(free_op1);};
+ HANDLE_EXCEPTION();
+ } else {
+ dim = NULL;
+ value = _get_zval_ptr_cv_deref_BP_VAR_R(execute_data, (opline+1)->op1.var);
+ zend_assign_to_string_offset(object_ptr, dim, value, (UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL));
+
+ }
+ } else if (EXPECTED(Z_TYPE_P(object_ptr) <= IS_FALSE)) {
+ 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 {
- zend_error(E_WARNING, "Cannot use a scalar value as an array");
-assign_dim_clean:
+ if (IS_VAR != IS_VAR || EXPECTED(!Z_ISERROR_P(object_ptr))) {
+ zend_error(E_WARNING, "Cannot use a scalar value as an array");
+ }
dim = NULL;
+assign_dim_error:
- value = get_zval_ptr_r((opline+1)->op1_type, (opline+1)->op1, execute_data, &free_op_data1);
- FREE_OP(free_op_data1);
if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
ZVAL_NULL(EX_VAR(opline->result.var));
}
}
}
+ if (IS_UNUSED != IS_UNUSED) {
+
+ }
if (UNEXPECTED(free_op1)) {zval_ptr_dtor_nogc(free_op1);};
/* assign_dim has two opcodes! */
ZEND_VM_NEXT_OPCODE_EX(1, 2);
@@ -19209,6 +21378,13 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_STATIC_METHOD_CALL_SPEC_V
}
CACHE_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)), ce);
}
+ } else if (IS_VAR == IS_UNUSED) {
+ ce = zend_fetch_class(NULL, opline->op1.num);
+ if (UNEXPECTED(ce == NULL)) {
+ ZEND_ASSERT(EG(exception));
+
+ HANDLE_EXCEPTION();
+ }
} else {
ce = Z_CE_P(EX_VAR(opline->op1.var));
}
@@ -19267,6 +21443,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_STATIC_METHOD_CALL_SPEC_V
CACHE_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(function_name), ce, fbc);
}
}
+ if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!fbc->op_array.run_time_cache)) {
+ init_func_run_time_cache(&fbc->op_array);
+ }
if (IS_UNUSED != IS_CONST) {
}
@@ -19275,16 +21454,19 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_STATIC_METHOD_CALL_SPEC_V
zend_throw_error(NULL, "Cannot call constructor");
HANDLE_EXCEPTION();
}
- if (Z_OBJ(EX(This)) && Z_OBJ(EX(This))->ce != ce->constructor->common.scope && (ce->constructor->common.fn_flags & ZEND_ACC_PRIVATE)) {
+ if (Z_TYPE(EX(This)) == IS_OBJECT && Z_OBJ(EX(This))->ce != ce->constructor->common.scope && (ce->constructor->common.fn_flags & ZEND_ACC_PRIVATE)) {
zend_throw_error(NULL, "Cannot call private %s::__construct()", ZSTR_VAL(ce->name));
HANDLE_EXCEPTION();
}
fbc = ce->constructor;
+ if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!fbc->op_array.run_time_cache)) {
+ init_func_run_time_cache(&fbc->op_array);
+ }
}
object = NULL;
if (!(fbc->common.fn_flags & ZEND_ACC_STATIC)) {
- if (Z_OBJ(EX(This)) && instanceof_function(Z_OBJCE(EX(This)), ce)) {
+ if (Z_TYPE(EX(This)) == IS_OBJECT && instanceof_function(Z_OBJCE(EX(This)), ce)) {
object = Z_OBJ(EX(This));
ce = object->ce;
} else {
@@ -19309,11 +21491,15 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_STATIC_METHOD_CALL_SPEC_V
}
}
- if (IS_VAR != IS_CONST) {
+ if (IS_VAR == IS_UNUSED) {
/* previous opcode is ZEND_FETCH_CLASS */
- if (((opline-1)->extended_value & ZEND_FETCH_CLASS_MASK) == ZEND_FETCH_CLASS_PARENT ||
- ((opline-1)->extended_value & ZEND_FETCH_CLASS_MASK) == ZEND_FETCH_CLASS_SELF) {
- ce = EX(called_scope);
+ if ((opline->op1.num & ZEND_FETCH_CLASS_MASK) == ZEND_FETCH_CLASS_PARENT ||
+ (opline->op1.num & ZEND_FETCH_CLASS_MASK) == ZEND_FETCH_CLASS_SELF) {
+ if (Z_TYPE(EX(This)) == IS_OBJECT) {
+ ce = Z_OBJCE(EX(This));
+ } else {
+ ce = Z_CE(EX(This));
+ }
}
}
@@ -19373,8 +21559,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_VERIFY_RETURN_TYPE_SPEC_VAR_UN
if (UNEXPECTED(EG(exception) != NULL)) {
if (IS_VAR == IS_CONST) {
zval_ptr_dtor_nogc(retval_ptr);
- } else {
- zval_ptr_dtor_nogc(free_op1);
}
}
#endif
@@ -19392,11 +21576,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ADD_ARRAY_ELEMENT_SPEC_VAR_UNU
if ((IS_VAR == IS_VAR || IS_VAR == IS_CV) &&
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_throw_error(NULL, "Cannot create references to/from string offsets");
- zend_array_destroy(Z_ARRVAL_P(EX_VAR(opline->result.var)));
- HANDLE_EXCEPTION();
- }
ZVAL_MAKE_REF(expr_ptr);
Z_ADDREF_P(expr_ptr);
if (UNEXPECTED(free_op1)) {zval_ptr_dtor_nogc(free_op1);};
@@ -19405,10 +21584,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ADD_ARRAY_ELEMENT_SPEC_VAR_UNU
if (IS_VAR == IS_TMP_VAR) {
/* pass */
} else if (IS_VAR == IS_CONST) {
- 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;
+ if (Z_REFCOUNTED_P(expr_ptr)) {
+ Z_ADDREF_P(expr_ptr);
}
} else if (IS_VAR == IS_CV) {
ZVAL_DEREF(expr_ptr);
@@ -19521,9 +21698,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SEPARATE_SPEC_VAR_UNUSED_HANDL
if (UNEXPECTED(Z_ISREF_P(var_ptr))) {
if (UNEXPECTED(Z_REFCOUNT_P(var_ptr) == 1)) {
ZVAL_UNREF(var_ptr);
- } else if (!(Z_VAR_FLAGS_P(var_ptr) & IS_VAR_RET_REF)) {
- Z_DELREF_P(var_ptr);
- ZVAL_COPY(var_ptr, Z_REFVAL_P(var_ptr));
}
}
@@ -19557,7 +21731,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_VAR_UNUSED_HANDLER(
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) {
+ if (IS_VAR & (IS_CONST|IS_TMP_VAR)) {
zval *value;
zend_error(E_NOTICE, "Only variable references should be yielded by reference");
@@ -19565,25 +21739,19 @@ 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 (IS_VAR == IS_CONST) {
- if (UNEXPECTED(Z_OPT_COPYABLE(generator->value))) {
- zval_copy_ctor_func(&generator->value);
+ if (UNEXPECTED(Z_OPT_REFCOUNTED(generator->value))) {
+ Z_ADDREF(generator->value);
}
}
} else {
zval *value_ptr = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1);
- if (IS_VAR == IS_VAR && UNEXPECTED(value_ptr == NULL)) {
- zend_throw_error(NULL, "Cannot yield string offsets by reference");
-
- HANDLE_EXCEPTION();
- }
-
/* If a function call result is yielded and the function did
* not return by reference we throw a notice. */
if (IS_VAR == IS_VAR &&
(value_ptr == &EG(uninitialized_zval) ||
(opline->extended_value == ZEND_RETURNS_FUNCTION &&
- !(Z_VAR_FLAGS_P(value_ptr) & IS_VAR_RET_REF)))) {
+ !Z_ISREF_P(value_ptr)))) {
zend_error(E_NOTICE, "Only variable references should be yielded by reference");
} else {
ZVAL_MAKE_REF(value_ptr);
@@ -19598,8 +21766,8 @@ 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_COPY_VALUE(&generator->value, value);
- if (UNEXPECTED(Z_OPT_COPYABLE(generator->value))) {
- zval_copy_ctor_func(&generator->value);
+ if (UNEXPECTED(Z_OPT_REFCOUNTED(generator->value))) {
+ Z_ADDREF(generator->value);
}
} else if (IS_VAR == IS_TMP_VAR) {
ZVAL_COPY_VALUE(&generator->value, value);
@@ -19626,8 +21794,8 @@ 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_COPY_VALUE(&generator->key, key);
- if (UNEXPECTED(Z_OPT_COPYABLE(generator->key))) {
- zval_copy_ctor_func(&generator->key);
+ if (UNEXPECTED(Z_OPT_REFCOUNTED(generator->key))) {
+ Z_ADDREF(generator->key);
}
} else if (IS_UNUSED == IS_TMP_VAR) {
ZVAL_COPY_VALUE(&generator->key, key);
@@ -19720,7 +21888,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_binary_assign_op_obj_helper_SP
SAVE_OPLINE();
object = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1);
- if (IS_VAR == IS_UNUSED && UNEXPECTED(Z_OBJ_P(object) == NULL)) {
+ if (IS_VAR == IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
FREE_UNFETCHED_OP((opline+1)->op1_type, (opline+1)->op1.var);
@@ -19729,13 +21897,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_binary_assign_op_obj_helper_SP
property = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var);
- if (IS_VAR == IS_VAR && UNEXPECTED(object == NULL)) {
- zend_throw_error(NULL, "Cannot use string offset as an object");
- FREE_UNFETCHED_OP((opline+1)->op1_type, (opline+1)->op1.var);
-
- HANDLE_EXCEPTION();
- }
-
do {
value = get_zval_ptr_r((opline+1)->op1_type, (opline+1)->op1, execute_data, &free_op_data1);
@@ -19753,7 +21914,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_binary_assign_op_obj_helper_SP
/* here we are sure we are dealing with an object */
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)) {
- if (UNEXPECTED(zptr == &EG(error_zval))) {
+ if (UNEXPECTED(Z_ISERROR_P(zptr))) {
if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
ZVAL_NULL(EX_VAR(opline->result.var));
}
@@ -19782,66 +21943,85 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_binary_assign_op_dim_helper_SP
{
USE_OPLINE
zend_free_op free_op1, free_op_data1;
- zval *var_ptr, rv;
+ zval *var_ptr;
zval *value, *container, *dim;
SAVE_OPLINE();
container = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1);
- if (IS_VAR == IS_UNUSED && UNEXPECTED(Z_OBJ_P(container) == NULL)) {
- zend_throw_error(NULL, "Using $this when not in object context");
- FREE_UNFETCHED_OP((opline+1)->op1_type, (opline+1)->op1.var);
-
- HANDLE_EXCEPTION();
- }
- if (IS_VAR == IS_VAR && UNEXPECTED(container == NULL)) {
- zend_throw_error(NULL, "Cannot use string offset as an array");
- FREE_UNFETCHED_OP((opline+1)->op1_type, (opline+1)->op1.var);
- HANDLE_EXCEPTION();
- }
-
- dim = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var);
+ if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) {
+assign_dim_op_array:
+ SEPARATE_ARRAY(container);
+assign_dim_op_new_array:
+ if (IS_CV == IS_UNUSED) {
+ var_ptr = zend_hash_next_index_insert(Z_ARRVAL_P(container), &EG(uninitialized_zval));
+ if (UNEXPECTED(!var_ptr)) {
+ zend_error(E_WARNING, "Cannot add element to the array as the next element is already occupied");
+ goto assign_dim_op_ret_null;
+ }
+ } else {
+ dim = _get_zval_ptr_cv_undef(execute_data, opline->op2.var);
- do {
- if (IS_VAR == IS_UNUSED || UNEXPECTED(Z_TYPE_P(container) != IS_ARRAY)) {
- if (IS_VAR != IS_UNUSED) {
- ZVAL_DEREF(container);
+ if (IS_CV == IS_CONST) {
+ var_ptr = zend_fetch_dimension_address_inner_RW_CONST(Z_ARRVAL_P(container), dim);
+ } else {
+ var_ptr = zend_fetch_dimension_address_inner_RW(Z_ARRVAL_P(container), dim);
}
- if (IS_VAR == IS_UNUSED || EXPECTED(Z_TYPE_P(container) == IS_OBJECT)) {
- value = get_zval_ptr_r((opline+1)->op1_type, (opline+1)->op1, execute_data, &free_op_data1);
- zend_binary_assign_op_obj_dim(container, dim, value, UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL, binary_op);
- break;
+ if (UNEXPECTED(!var_ptr)) {
+ goto assign_dim_op_ret_null;
}
+ ZVAL_DEREF(var_ptr);
+ SEPARATE_ZVAL_NOREF(var_ptr);
}
- zend_fetch_dimension_address_RW(&rv, container, dim, IS_CV);
value = get_zval_ptr_r((opline+1)->op1_type, (opline+1)->op1, execute_data, &free_op_data1);
- ZEND_ASSERT(Z_TYPE(rv) == IS_INDIRECT);
- var_ptr = Z_INDIRECT(rv);
- if (UNEXPECTED(var_ptr == NULL)) {
- zend_throw_error(NULL, "Cannot use assign-op operators with overloaded objects nor string offsets");
+ binary_op(var_ptr, var_ptr, value);
- FREE_OP(free_op_data1);
- if (UNEXPECTED(free_op1)) {zval_ptr_dtor_nogc(free_op1);};
- HANDLE_EXCEPTION();
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), var_ptr);
}
-
- if (UNEXPECTED(var_ptr == &EG(error_zval))) {
- if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
- ZVAL_NULL(EX_VAR(opline->result.var));
+ } else {
+ if (EXPECTED(Z_ISREF_P(container))) {
+ container = Z_REFVAL_P(container);
+ if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) {
+ goto assign_dim_op_array;
}
- } else {
- ZVAL_DEREF(var_ptr);
- SEPARATE_ZVAL_NOREF(var_ptr);
+ } else if (IS_VAR == IS_CV && UNEXPECTED(Z_TYPE_INFO_P(container) == IS_UNDEF)) {
+ container = GET_OP1_UNDEF_CV(container, BP_VAR_RW);
+assign_dim_op_convert_to_array:
+ ZVAL_NEW_ARR(container);
+ zend_hash_init(Z_ARRVAL_P(container), 8, NULL, ZVAL_PTR_DTOR, 0);
+ goto assign_dim_op_new_array;
+ }
- binary_op(var_ptr, var_ptr, value);
+ dim = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var);
- if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
- ZVAL_COPY(EX_VAR(opline->result.var), var_ptr);
+ if (EXPECTED(Z_TYPE_P(container) == IS_OBJECT)) {
+ value = get_zval_ptr_r((opline+1)->op1_type, (opline+1)->op1, execute_data, &free_op_data1);
+ zend_binary_assign_op_obj_dim(container, dim, value, UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL, binary_op);
+ } else {
+ if (UNEXPECTED(Z_TYPE_P(container) == IS_STRING)) {
+ if (IS_CV == IS_UNUSED) {
+ zend_throw_error(NULL, "[] operator not supported for strings");
+ } else {
+ zend_check_string_offset(dim, BP_VAR_RW);
+ zend_wrong_string_offset();
+ }
+ } else if (EXPECTED(Z_TYPE_P(container) <= IS_FALSE)) {
+ goto assign_dim_op_convert_to_array;
+ } else {
+ if (UNEXPECTED(IS_VAR != IS_VAR || EXPECTED(!Z_ISERROR_P(container)))) {
+ zend_error(E_WARNING, "Cannot use a scalar value as an array");
+ }
+assign_dim_op_ret_null:
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
}
+ value = get_zval_ptr_r((opline+1)->op1_type, (opline+1)->op1, execute_data, &free_op_data1);
}
- } while (0);
+ }
FREE_OP(free_op_data1);
if (UNEXPECTED(free_op1)) {zval_ptr_dtor_nogc(free_op1);};
@@ -19859,13 +22039,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_binary_assign_op_helper_SPEC_V
value = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var);
var_ptr = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1);
- if (IS_VAR == IS_VAR && UNEXPECTED(var_ptr == NULL)) {
- zend_throw_error(NULL, "Cannot use assign-op operators with overloaded objects nor string offsets");
-
- HANDLE_EXCEPTION();
- }
-
- if (IS_VAR == IS_VAR && UNEXPECTED(var_ptr == &EG(error_zval))) {
+ if (IS_VAR == IS_VAR && UNEXPECTED(Z_ISERROR_P(var_ptr))) {
if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
ZVAL_NULL(EX_VAR(opline->result.var));
}
@@ -19890,15 +22064,52 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_ADD_SPEC_VAR_CV_HANDLER
USE_OPLINE
# if 0 || (IS_VAR != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
+ if (EXPECTED(1)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_VAR_CV(add_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CV(add_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_CV(add_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CV(add_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_ADD_SPEC_VAR_CV_DIM_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || (IS_CV != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_VAR != IS_UNUSED)
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_VAR_CV(add_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(1)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CV(add_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_CV(add_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_CV(add_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CV(add_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_ADD_SPEC_VAR_CV_OBJ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || (IS_CV != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_VAR != IS_UNUSED)
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_VAR_CV(add_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CV(add_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_CV(add_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#else
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CV(add_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#endif
@@ -19910,15 +22121,52 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_SUB_SPEC_VAR_CV_HANDLER
USE_OPLINE
# if 0 || (IS_VAR != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
+ if (EXPECTED(1)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_VAR_CV(sub_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CV(sub_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_CV(sub_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CV(sub_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_SUB_SPEC_VAR_CV_DIM_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || (IS_CV != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_VAR != IS_UNUSED)
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_VAR_CV(sub_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+ if (EXPECTED(1)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CV(sub_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_CV(sub_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CV(sub_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_SUB_SPEC_VAR_CV_OBJ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || (IS_CV != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_VAR != IS_UNUSED)
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_VAR_CV(sub_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CV(sub_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_CV(sub_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_CV(sub_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#else
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CV(sub_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#endif
@@ -19930,15 +22178,52 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_MUL_SPEC_VAR_CV_HANDLER
USE_OPLINE
# if 0 || (IS_VAR != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
+ if (EXPECTED(1)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_VAR_CV(mul_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CV(mul_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_CV(mul_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CV(mul_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_MUL_SPEC_VAR_CV_DIM_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || (IS_CV != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_VAR != IS_UNUSED)
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_VAR_CV(mul_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(1)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CV(mul_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_CV(mul_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_CV(mul_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CV(mul_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_MUL_SPEC_VAR_CV_OBJ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || (IS_CV != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_VAR != IS_UNUSED)
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_VAR_CV(mul_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CV(mul_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_CV(mul_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#else
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CV(mul_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#endif
@@ -19950,15 +22235,52 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIV_SPEC_VAR_CV_HANDLER
USE_OPLINE
# if 0 || (IS_VAR != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
+ if (EXPECTED(1)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_VAR_CV(div_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CV(div_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_CV(div_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CV(div_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIV_SPEC_VAR_CV_DIM_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || (IS_CV != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_VAR != IS_UNUSED)
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_VAR_CV(div_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+ if (EXPECTED(1)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CV(div_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_CV(div_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CV(div_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIV_SPEC_VAR_CV_OBJ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || (IS_CV != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_VAR != IS_UNUSED)
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_VAR_CV(div_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CV(div_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_CV(div_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_CV(div_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#else
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CV(div_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#endif
@@ -19970,15 +22292,52 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_MOD_SPEC_VAR_CV_HANDLER
USE_OPLINE
# if 0 || (IS_VAR != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
+ if (EXPECTED(1)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_VAR_CV(mod_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CV(mod_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_CV(mod_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CV(mod_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_MOD_SPEC_VAR_CV_DIM_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || (IS_CV != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_VAR != IS_UNUSED)
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_VAR_CV(mod_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(1)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CV(mod_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_CV(mod_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_CV(mod_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CV(mod_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_MOD_SPEC_VAR_CV_OBJ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || (IS_CV != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_VAR != IS_UNUSED)
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_VAR_CV(mod_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CV(mod_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_CV(mod_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#else
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CV(mod_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#endif
@@ -19990,15 +22349,52 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_SL_SPEC_VAR_CV_HANDLER(
USE_OPLINE
# if 0 || (IS_VAR != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
+ if (EXPECTED(1)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_VAR_CV(shift_left_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CV(shift_left_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_CV(shift_left_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CV(shift_left_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_SL_SPEC_VAR_CV_DIM_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || (IS_CV != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_VAR != IS_UNUSED)
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_VAR_CV(shift_left_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(1)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CV(shift_left_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_CV(shift_left_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CV(shift_left_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_SL_SPEC_VAR_CV_OBJ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || (IS_CV != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_VAR != IS_UNUSED)
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_VAR_CV(shift_left_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CV(shift_left_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_CV(shift_left_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_CV(shift_left_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#else
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CV(shift_left_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#endif
@@ -20010,15 +22406,52 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_SR_SPEC_VAR_CV_HANDLER(
USE_OPLINE
# if 0 || (IS_VAR != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
+ if (EXPECTED(1)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_VAR_CV(shift_right_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CV(shift_right_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_CV(shift_right_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CV(shift_right_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_SR_SPEC_VAR_CV_DIM_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || (IS_CV != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_VAR != IS_UNUSED)
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_VAR_CV(shift_right_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(1)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CV(shift_right_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_CV(shift_right_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_CV(shift_right_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CV(shift_right_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_SR_SPEC_VAR_CV_OBJ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || (IS_CV != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_VAR != IS_UNUSED)
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_VAR_CV(shift_right_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CV(shift_right_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_CV(shift_right_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#else
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CV(shift_right_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#endif
@@ -20030,15 +22463,52 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_CONCAT_SPEC_VAR_CV_HAND
USE_OPLINE
# if 0 || (IS_VAR != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
+ if (EXPECTED(1)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_VAR_CV(concat_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CV(concat_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_CV(concat_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CV(concat_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_CONCAT_SPEC_VAR_CV_DIM_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || (IS_CV != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_VAR != IS_UNUSED)
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_VAR_CV(concat_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(1)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CV(concat_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_CV(concat_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CV(concat_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_CONCAT_SPEC_VAR_CV_OBJ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || (IS_CV != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_VAR != IS_UNUSED)
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_VAR_CV(concat_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CV(concat_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_CV(concat_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_CV(concat_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#else
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CV(concat_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#endif
@@ -20050,15 +22520,52 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_BW_OR_SPEC_VAR_CV_HANDL
USE_OPLINE
# if 0 || (IS_VAR != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
+ if (EXPECTED(1)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_VAR_CV(bitwise_or_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CV(bitwise_or_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_CV(bitwise_or_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CV(bitwise_or_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_BW_OR_SPEC_VAR_CV_DIM_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || (IS_CV != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_VAR != IS_UNUSED)
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_VAR_CV(bitwise_or_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(1)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CV(bitwise_or_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_CV(bitwise_or_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_CV(bitwise_or_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CV(bitwise_or_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_BW_OR_SPEC_VAR_CV_OBJ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || (IS_CV != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_VAR != IS_UNUSED)
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_VAR_CV(bitwise_or_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CV(bitwise_or_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_CV(bitwise_or_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#else
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CV(bitwise_or_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#endif
@@ -20070,15 +22577,52 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_BW_AND_SPEC_VAR_CV_HAND
USE_OPLINE
# if 0 || (IS_VAR != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
+ if (EXPECTED(1)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_VAR_CV(bitwise_and_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CV(bitwise_and_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_CV(bitwise_and_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CV(bitwise_and_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_BW_AND_SPEC_VAR_CV_DIM_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || (IS_CV != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_VAR != IS_UNUSED)
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_VAR_CV(bitwise_and_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(1)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CV(bitwise_and_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_CV(bitwise_and_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CV(bitwise_and_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_BW_AND_SPEC_VAR_CV_OBJ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || (IS_CV != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_VAR != IS_UNUSED)
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_VAR_CV(bitwise_and_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CV(bitwise_and_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_CV(bitwise_and_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_CV(bitwise_and_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#else
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CV(bitwise_and_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#endif
@@ -20090,15 +22634,52 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_BW_XOR_SPEC_VAR_CV_HAND
USE_OPLINE
# if 0 || (IS_VAR != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
+ if (EXPECTED(1)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_VAR_CV(bitwise_xor_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CV(bitwise_xor_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_CV(bitwise_xor_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CV(bitwise_xor_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_BW_XOR_SPEC_VAR_CV_DIM_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || (IS_CV != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_VAR != IS_UNUSED)
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_VAR_CV(bitwise_xor_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+ if (EXPECTED(1)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CV(bitwise_xor_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_CV(bitwise_xor_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CV(bitwise_xor_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_BW_XOR_SPEC_VAR_CV_OBJ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || (IS_CV != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_VAR != IS_UNUSED)
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_VAR_CV(bitwise_xor_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CV(bitwise_xor_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_CV(bitwise_xor_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_CV(bitwise_xor_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#else
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CV(bitwise_xor_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#endif
@@ -20110,15 +22691,52 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_POW_SPEC_VAR_CV_HANDLER
USE_OPLINE
# if 0 || (IS_VAR != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
+ if (EXPECTED(1)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_VAR_CV(pow_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CV(pow_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_CV(pow_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CV(pow_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_POW_SPEC_VAR_CV_DIM_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || (IS_CV != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_VAR != IS_UNUSED)
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_VAR_CV(pow_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(1)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CV(pow_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_CV(pow_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CV(pow_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_POW_SPEC_VAR_CV_OBJ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || (IS_CV != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_VAR != IS_UNUSED)
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_VAR_CV(pow_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CV(pow_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_CV(pow_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_CV(pow_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#else
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_CV(pow_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#endif
@@ -20135,7 +22753,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_pre_incdec_property_helper_SPE
SAVE_OPLINE();
object = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1);
- if (IS_VAR == IS_UNUSED && UNEXPECTED(Z_OBJ_P(object) == NULL)) {
+ if (IS_VAR == IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
HANDLE_EXCEPTION();
@@ -20143,12 +22761,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_pre_incdec_property_helper_SPE
property = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var);
- if (IS_VAR == IS_VAR && UNEXPECTED(object == NULL)) {
- zend_throw_error(NULL, "Cannot increment/decrement overloaded objects nor string offsets");
-
- HANDLE_EXCEPTION();
- }
-
do {
if (IS_VAR != IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) != IS_OBJECT)) {
ZVAL_DEREF(object);
@@ -20164,7 +22776,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_pre_incdec_property_helper_SPE
/* here we are sure we are dealing with an object */
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)) {
- if (UNEXPECTED(zptr == &EG(error_zval))) {
+ if (UNEXPECTED(Z_ISERROR_P(zptr))) {
if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
ZVAL_NULL(EX_VAR(opline->result.var));
}
@@ -20219,7 +22831,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_post_incdec_property_helper_SP
SAVE_OPLINE();
object = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1);
- if (IS_VAR == IS_UNUSED && UNEXPECTED(Z_OBJ_P(object) == NULL)) {
+ if (IS_VAR == IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
HANDLE_EXCEPTION();
@@ -20227,12 +22839,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_post_incdec_property_helper_SP
property = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var);
- if (IS_VAR == IS_VAR && UNEXPECTED(object == NULL)) {
- zend_throw_error(NULL, "Cannot increment/decrement overloaded objects nor string offsets");
-
- HANDLE_EXCEPTION();
- }
-
do {
if (IS_VAR != IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) != IS_OBJECT)) {
ZVAL_DEREF(object);
@@ -20247,7 +22853,7 @@ 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)) {
- if (UNEXPECTED(zptr == &EG(error_zval))) {
+ if (UNEXPECTED(Z_ISERROR_P(zptr))) {
ZVAL_NULL(EX_VAR(opline->result.var));
} else {
if (EXPECTED(Z_TYPE_P(zptr) == IS_LONG)) {
@@ -20296,14 +22902,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_DIM_W_SPEC_VAR_CV_HANDLE
SAVE_OPLINE();
container = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1);
- if (IS_VAR == IS_VAR && UNEXPECTED(container == NULL)) {
- zend_throw_error(NULL, "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);
+ zend_fetch_dimension_address_W(EX_VAR(opline->result.var), container, _get_zval_ptr_cv_undef(execute_data, opline->op2.var), IS_CV);
if (IS_VAR == IS_VAR && READY_TO_DESTROY(free_op1)) {
- EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var), 1);
+ EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var));
}
if (UNEXPECTED(free_op1)) {zval_ptr_dtor_nogc(free_op1);};
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
@@ -20318,14 +22920,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_DIM_RW_SPEC_VAR_CV_HANDL
SAVE_OPLINE();
container = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1);
- if (IS_VAR == IS_VAR && UNEXPECTED(container == NULL)) {
- zend_throw_error(NULL, "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);
+ zend_fetch_dimension_address_RW(EX_VAR(opline->result.var), container, _get_zval_ptr_cv_undef(execute_data, opline->op2.var), IS_CV);
if (IS_VAR == IS_VAR && READY_TO_DESTROY(free_op1)) {
- EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var), 1);
+ EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var));
}
if (UNEXPECTED(free_op1)) {zval_ptr_dtor_nogc(free_op1);};
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
@@ -20340,21 +22938,16 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_DIM_FUNC_ARG_SPEC_VAR_CV
SAVE_OPLINE();
if (zend_is_by_ref_func_arg_fetch(opline, EX(call))) {
- if (IS_VAR == IS_CONST || IS_VAR == IS_TMP_VAR) {
+ if ((IS_VAR & (IS_CONST|IS_TMP_VAR))) {
zend_throw_error(NULL, "Cannot use temporary expression in write context");
zval_ptr_dtor_nogc(EX_VAR(opline->op1.var));
HANDLE_EXCEPTION();
}
container = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1);
- if (IS_VAR == IS_VAR && UNEXPECTED(container == NULL)) {
- zend_throw_error(NULL, "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);
+ zend_fetch_dimension_address_W(EX_VAR(opline->result.var), container, _get_zval_ptr_cv_undef(execute_data, opline->op2.var), IS_CV);
if (IS_VAR == IS_VAR && READY_TO_DESTROY(free_op1)) {
- EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var), 1);
+ EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var));
}
if (UNEXPECTED(free_op1)) {zval_ptr_dtor_nogc(free_op1);};
@@ -20366,7 +22959,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_DIM_FUNC_ARG_SPEC_VAR_CV
HANDLE_EXCEPTION();
}
container = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1);
- zend_fetch_dimension_address_read_R(EX_VAR(opline->result.var), container, _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var), IS_CV);
+ zend_fetch_dimension_address_read_R(EX_VAR(opline->result.var), container, _get_zval_ptr_cv_undef(execute_data, opline->op2.var), IS_CV);
zval_ptr_dtor_nogc(free_op1);
}
@@ -20382,15 +22975,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_DIM_UNSET_SPEC_VAR_CV_HA
SAVE_OPLINE();
container = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1);
- if (IS_VAR == IS_VAR && UNEXPECTED(container == NULL)) {
- zend_throw_error(NULL, "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);
+ zend_fetch_dimension_address_UNSET(EX_VAR(opline->result.var), container, _get_zval_ptr_cv_undef(execute_data, opline->op2.var), IS_CV);
if (IS_VAR == IS_VAR && READY_TO_DESTROY(free_op1)) {
- EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var), 1);
+ EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var));
}
if (UNEXPECTED(free_op1)) {zval_ptr_dtor_nogc(free_op1);};
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
@@ -20407,7 +22995,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_R_SPEC_VAR_CV_HANDLE
SAVE_OPLINE();
container = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1);
- if (IS_VAR == IS_UNUSED && UNEXPECTED(Z_OBJ_P(container) == NULL)) {
+ if (IS_VAR == IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
HANDLE_EXCEPTION();
@@ -20479,21 +23067,16 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_W_SPEC_VAR_CV_HANDLE
property = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var);
container = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1);
- if (IS_VAR == IS_UNUSED && UNEXPECTED(Z_OBJ_P(container) == NULL)) {
+ if (IS_VAR == IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
HANDLE_EXCEPTION();
}
- if (IS_VAR == IS_VAR && UNEXPECTED(container == NULL)) {
- zend_throw_error(NULL, "Cannot use string offset as an object");
-
- 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);
if (IS_VAR == IS_VAR && READY_TO_DESTROY(free_op1)) {
- EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var), 0);
+ EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var));
}
if (UNEXPECTED(free_op1)) {zval_ptr_dtor_nogc(free_op1);};
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
@@ -20510,20 +23093,15 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_RW_SPEC_VAR_CV_HANDL
property = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var);
container = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1);
- if (IS_VAR == IS_UNUSED && UNEXPECTED(Z_OBJ_P(container) == NULL)) {
+ if (IS_VAR == IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
HANDLE_EXCEPTION();
}
- if (IS_VAR == IS_VAR && UNEXPECTED(container == NULL)) {
- zend_throw_error(NULL, "Cannot use string offset as an object");
-
- 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);
if (IS_VAR == IS_VAR && READY_TO_DESTROY(free_op1)) {
- EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var), 0);
+ EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var));
}
if (UNEXPECTED(free_op1)) {zval_ptr_dtor_nogc(free_op1);};
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
@@ -20543,26 +23121,21 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_FUNC_ARG_SPEC_VAR_CV
property = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var);
container = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1);
- if (IS_VAR == IS_UNUSED && UNEXPECTED(Z_OBJ_P(container) == NULL)) {
+ if (IS_VAR == IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
HANDLE_EXCEPTION();
}
- if (IS_VAR == IS_CONST || IS_VAR == IS_TMP_VAR) {
+ if ((IS_VAR & (IS_CONST|IS_TMP_VAR))) {
zend_throw_error(NULL, "Cannot use temporary expression in write context");
if (UNEXPECTED(free_op1)) {zval_ptr_dtor_nogc(free_op1);};
HANDLE_EXCEPTION();
}
- if (IS_VAR == IS_VAR && UNEXPECTED(container == NULL)) {
- zend_throw_error(NULL, "Cannot use string offset as an object");
-
- 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);
if (IS_VAR == IS_VAR && READY_TO_DESTROY(free_op1)) {
- EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var), 0);
+ EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var));
}
if (UNEXPECTED(free_op1)) {zval_ptr_dtor_nogc(free_op1);};
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
@@ -20580,7 +23153,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_UNSET_SPEC_VAR_CV_HA
SAVE_OPLINE();
container = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1);
- if (IS_VAR == IS_UNUSED && UNEXPECTED(Z_OBJ_P(container) == NULL)) {
+ if (IS_VAR == IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
HANDLE_EXCEPTION();
@@ -20588,56 +23161,645 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_UNSET_SPEC_VAR_CV_HA
property = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var);
- if (IS_VAR == IS_VAR && UNEXPECTED(container == NULL)) {
- zend_throw_error(NULL, "Cannot use string offset as an object");
-
- 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);
if (IS_VAR == IS_VAR && READY_TO_DESTROY(free_op1)) {
- EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var), 0);
+ EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var));
}
if (UNEXPECTED(free_op1)) {zval_ptr_dtor_nogc(free_op1);};
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_CV_OP_DATA_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
zend_free_op free_op1;
- zval *object;
- zval *property_name;
+ zval *object, *property_name, *value, tmp;
+
+ SAVE_OPLINE();
+ object = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1);
+
+ if (IS_VAR == IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) == IS_UNDEF)) {
+ zend_throw_error(NULL, "Using $this when not in object context");
+
+ HANDLE_EXCEPTION();
+ }
+
+ property_name = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var);
+ value = EX_CONSTANT((opline+1)->op1);
+
+ if (IS_VAR != IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) != IS_OBJECT)) {
+ do {
+ if (Z_ISREF_P(object)) {
+ object = Z_REFVAL_P(object);
+ if (EXPECTED(Z_TYPE_P(object) == IS_OBJECT)) {
+ break;
+ }
+ }
+ if (EXPECTED(Z_TYPE_P(object) <= IS_FALSE ||
+ (Z_TYPE_P(object) == IS_STRING && Z_STRLEN_P(object) == 0))) {
+ zend_object *obj;
+
+ zval_ptr_dtor(object);
+ object_init(object);
+ Z_ADDREF_P(object);
+ obj = Z_OBJ_P(object);
+ zend_error(E_WARNING, "Creating default object from empty value");
+ if (GC_REFCOUNT(obj) == 1) {
+ /* the enclosing container was deleted, obj is unreferenced */
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+
+ OBJ_RELEASE(obj);
+ goto exit_assign_obj;
+ }
+ Z_DELREF_P(object);
+ } else {
+ if (IS_VAR != IS_VAR || EXPECTED(!Z_ISERROR_P(object))) {
+ zend_error(E_WARNING, "Attempt to assign property of non-object");
+ }
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+
+ goto exit_assign_obj;
+ }
+ } while (0);
+ }
+
+ if (IS_CV == IS_CONST &&
+ EXPECTED(Z_OBJCE_P(object) == CACHED_PTR(Z_CACHE_SLOT_P(property_name)))) {
+ uint32_t prop_offset = (uint32_t)(intptr_t)CACHED_PTR(Z_CACHE_SLOT_P(property_name) + sizeof(void*));
+ zend_object *zobj = Z_OBJ_P(object);
+ zval *property;
+
+ if (EXPECTED(prop_offset != (uint32_t)ZEND_DYNAMIC_PROPERTY_OFFSET)) {
+ property = OBJ_PROP(zobj, prop_offset);
+ if (Z_TYPE_P(property) != IS_UNDEF) {
+fast_assign_obj:
+ value = zend_assign_to_variable(property, value, IS_CONST);
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+ goto exit_assign_obj;
+ }
+ } else {
+ if (EXPECTED(zobj->properties != NULL)) {
+ if (UNEXPECTED(GC_REFCOUNT(zobj->properties) > 1)) {
+ if (EXPECTED(!(GC_FLAGS(zobj->properties) & IS_ARRAY_IMMUTABLE))) {
+ GC_REFCOUNT(zobj->properties)--;
+ }
+ zobj->properties = zend_array_dup(zobj->properties);
+ }
+ property = zend_hash_find(zobj->properties, Z_STR_P(property_name));
+ if (property) {
+ goto fast_assign_obj;
+ }
+ }
+
+ if (!zobj->ce->__set) {
+
+ if (EXPECTED(zobj->properties == NULL)) {
+ rebuild_object_properties(zobj);
+ }
+ if (IS_CONST == IS_CONST) {
+ if (UNEXPECTED(Z_OPT_REFCOUNTED_P(value))) {
+ Z_ADDREF_P(value);
+ }
+ } else if (IS_CONST != IS_TMP_VAR) {
+ if (Z_ISREF_P(value)) {
+ if (IS_CONST == IS_VAR) {
+ zend_reference *ref = Z_REF_P(value);
+ if (--GC_REFCOUNT(ref) == 0) {
+ ZVAL_COPY_VALUE(&tmp, Z_REFVAL_P(value));
+ efree_size(ref, sizeof(zend_reference));
+ value = &tmp;
+ } else {
+ value = Z_REFVAL_P(value);
+ if (Z_REFCOUNTED_P(value)) {
+ Z_ADDREF_P(value);
+ }
+ }
+ } else {
+ value = Z_REFVAL_P(value);
+ if (Z_REFCOUNTED_P(value)) {
+ Z_ADDREF_P(value);
+ }
+ }
+ } else if (IS_CONST == IS_CV && Z_REFCOUNTED_P(value)) {
+ Z_ADDREF_P(value);
+ }
+ }
+ zend_hash_add_new(zobj->properties, Z_STR_P(property_name), value);
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+ goto exit_assign_obj;
+ }
+ }
+ }
+
+ if (!Z_OBJ_HT_P(object)->write_property) {
+ zend_error(E_WARNING, "Attempt to assign property of non-object");
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+
+ goto exit_assign_obj;
+ }
+
+ if (IS_CONST == IS_CV || IS_CONST == IS_VAR) {
+ ZVAL_DEREF(value);
+ }
+
+ Z_OBJ_HT_P(object)->write_property(object, property_name, value, (IS_CV == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property_name)) : NULL);
+
+ if (UNEXPECTED(RETURN_VALUE_USED(opline)) && EXPECTED(!EG(exception))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+
+exit_assign_obj:
+
+ if (UNEXPECTED(free_op1)) {zval_ptr_dtor_nogc(free_op1);};
+ /* assign_obj has two opcodes! */
+ ZEND_VM_NEXT_OPCODE_EX(1, 2);
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_CV_OP_DATA_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zend_free_op free_op1, free_op_data;
+ zval *object, *property_name, *value, tmp;
+
+ SAVE_OPLINE();
+ object = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1);
+
+ if (IS_VAR == IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) == IS_UNDEF)) {
+ zend_throw_error(NULL, "Using $this when not in object context");
+
+ HANDLE_EXCEPTION();
+ }
+
+ property_name = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var);
+ value = _get_zval_ptr_tmp((opline+1)->op1.var, execute_data, &free_op_data);
+
+ if (IS_VAR != IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) != IS_OBJECT)) {
+ do {
+ if (Z_ISREF_P(object)) {
+ object = Z_REFVAL_P(object);
+ if (EXPECTED(Z_TYPE_P(object) == IS_OBJECT)) {
+ break;
+ }
+ }
+ if (EXPECTED(Z_TYPE_P(object) <= IS_FALSE ||
+ (Z_TYPE_P(object) == IS_STRING && Z_STRLEN_P(object) == 0))) {
+ zend_object *obj;
+
+ zval_ptr_dtor(object);
+ object_init(object);
+ Z_ADDREF_P(object);
+ obj = Z_OBJ_P(object);
+ zend_error(E_WARNING, "Creating default object from empty value");
+ if (GC_REFCOUNT(obj) == 1) {
+ /* the enclosing container was deleted, obj is unreferenced */
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+ zval_ptr_dtor_nogc(free_op_data);
+ OBJ_RELEASE(obj);
+ goto exit_assign_obj;
+ }
+ Z_DELREF_P(object);
+ } else {
+ if (IS_VAR != IS_VAR || EXPECTED(!Z_ISERROR_P(object))) {
+ zend_error(E_WARNING, "Attempt to assign property of non-object");
+ }
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+ zval_ptr_dtor_nogc(free_op_data);
+ goto exit_assign_obj;
+ }
+ } while (0);
+ }
+
+ if (IS_CV == IS_CONST &&
+ EXPECTED(Z_OBJCE_P(object) == CACHED_PTR(Z_CACHE_SLOT_P(property_name)))) {
+ uint32_t prop_offset = (uint32_t)(intptr_t)CACHED_PTR(Z_CACHE_SLOT_P(property_name) + sizeof(void*));
+ zend_object *zobj = Z_OBJ_P(object);
+ zval *property;
+
+ if (EXPECTED(prop_offset != (uint32_t)ZEND_DYNAMIC_PROPERTY_OFFSET)) {
+ property = OBJ_PROP(zobj, prop_offset);
+ if (Z_TYPE_P(property) != IS_UNDEF) {
+fast_assign_obj:
+ value = zend_assign_to_variable(property, value, IS_TMP_VAR);
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+ goto exit_assign_obj;
+ }
+ } else {
+ if (EXPECTED(zobj->properties != NULL)) {
+ if (UNEXPECTED(GC_REFCOUNT(zobj->properties) > 1)) {
+ if (EXPECTED(!(GC_FLAGS(zobj->properties) & IS_ARRAY_IMMUTABLE))) {
+ GC_REFCOUNT(zobj->properties)--;
+ }
+ zobj->properties = zend_array_dup(zobj->properties);
+ }
+ property = zend_hash_find(zobj->properties, Z_STR_P(property_name));
+ if (property) {
+ goto fast_assign_obj;
+ }
+ }
+
+ if (!zobj->ce->__set) {
+
+ if (EXPECTED(zobj->properties == NULL)) {
+ rebuild_object_properties(zobj);
+ }
+ if (IS_TMP_VAR == IS_CONST) {
+ if (UNEXPECTED(Z_OPT_REFCOUNTED_P(value))) {
+ Z_ADDREF_P(value);
+ }
+ } else if (IS_TMP_VAR != IS_TMP_VAR) {
+ if (Z_ISREF_P(value)) {
+ if (IS_TMP_VAR == IS_VAR) {
+ zend_reference *ref = Z_REF_P(value);
+ if (--GC_REFCOUNT(ref) == 0) {
+ ZVAL_COPY_VALUE(&tmp, Z_REFVAL_P(value));
+ efree_size(ref, sizeof(zend_reference));
+ value = &tmp;
+ } else {
+ value = Z_REFVAL_P(value);
+ if (Z_REFCOUNTED_P(value)) {
+ Z_ADDREF_P(value);
+ }
+ }
+ } else {
+ value = Z_REFVAL_P(value);
+ if (Z_REFCOUNTED_P(value)) {
+ Z_ADDREF_P(value);
+ }
+ }
+ } else if (IS_TMP_VAR == IS_CV && Z_REFCOUNTED_P(value)) {
+ Z_ADDREF_P(value);
+ }
+ }
+ zend_hash_add_new(zobj->properties, Z_STR_P(property_name), value);
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+ goto exit_assign_obj;
+ }
+ }
+ }
+
+ if (!Z_OBJ_HT_P(object)->write_property) {
+ zend_error(E_WARNING, "Attempt to assign property of non-object");
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+ zval_ptr_dtor_nogc(free_op_data);
+ goto exit_assign_obj;
+ }
+
+ if (IS_TMP_VAR == IS_CV || IS_TMP_VAR == IS_VAR) {
+ ZVAL_DEREF(value);
+ }
+
+ Z_OBJ_HT_P(object)->write_property(object, property_name, value, (IS_CV == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property_name)) : NULL);
+
+ if (UNEXPECTED(RETURN_VALUE_USED(opline)) && EXPECTED(!EG(exception))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+ zval_ptr_dtor_nogc(free_op_data);
+exit_assign_obj:
+
+ if (UNEXPECTED(free_op1)) {zval_ptr_dtor_nogc(free_op1);};
+ /* assign_obj has two opcodes! */
+ ZEND_VM_NEXT_OPCODE_EX(1, 2);
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_CV_OP_DATA_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zend_free_op free_op1, free_op_data;
+ zval *object, *property_name, *value, tmp;
SAVE_OPLINE();
object = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1);
- if (IS_VAR == IS_UNUSED && UNEXPECTED(Z_OBJ_P(object) == NULL)) {
+ if (IS_VAR == IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
HANDLE_EXCEPTION();
}
property_name = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var);
+ value = _get_zval_ptr_var((opline+1)->op1.var, execute_data, &free_op_data);
+
+ if (IS_VAR != IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) != IS_OBJECT)) {
+ do {
+ if (Z_ISREF_P(object)) {
+ object = Z_REFVAL_P(object);
+ if (EXPECTED(Z_TYPE_P(object) == IS_OBJECT)) {
+ break;
+ }
+ }
+ if (EXPECTED(Z_TYPE_P(object) <= IS_FALSE ||
+ (Z_TYPE_P(object) == IS_STRING && Z_STRLEN_P(object) == 0))) {
+ zend_object *obj;
+
+ zval_ptr_dtor(object);
+ object_init(object);
+ Z_ADDREF_P(object);
+ obj = Z_OBJ_P(object);
+ zend_error(E_WARNING, "Creating default object from empty value");
+ if (GC_REFCOUNT(obj) == 1) {
+ /* the enclosing container was deleted, obj is unreferenced */
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+ zval_ptr_dtor_nogc(free_op_data);
+ OBJ_RELEASE(obj);
+ goto exit_assign_obj;
+ }
+ Z_DELREF_P(object);
+ } else {
+ if (IS_VAR != IS_VAR || EXPECTED(!Z_ISERROR_P(object))) {
+ zend_error(E_WARNING, "Attempt to assign property of non-object");
+ }
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+ zval_ptr_dtor_nogc(free_op_data);
+ goto exit_assign_obj;
+ }
+ } while (0);
+ }
+
+ if (IS_CV == IS_CONST &&
+ EXPECTED(Z_OBJCE_P(object) == CACHED_PTR(Z_CACHE_SLOT_P(property_name)))) {
+ uint32_t prop_offset = (uint32_t)(intptr_t)CACHED_PTR(Z_CACHE_SLOT_P(property_name) + sizeof(void*));
+ zend_object *zobj = Z_OBJ_P(object);
+ zval *property;
+
+ if (EXPECTED(prop_offset != (uint32_t)ZEND_DYNAMIC_PROPERTY_OFFSET)) {
+ property = OBJ_PROP(zobj, prop_offset);
+ if (Z_TYPE_P(property) != IS_UNDEF) {
+fast_assign_obj:
+ value = zend_assign_to_variable(property, value, IS_VAR);
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+ goto exit_assign_obj;
+ }
+ } else {
+ if (EXPECTED(zobj->properties != NULL)) {
+ if (UNEXPECTED(GC_REFCOUNT(zobj->properties) > 1)) {
+ if (EXPECTED(!(GC_FLAGS(zobj->properties) & IS_ARRAY_IMMUTABLE))) {
+ GC_REFCOUNT(zobj->properties)--;
+ }
+ zobj->properties = zend_array_dup(zobj->properties);
+ }
+ property = zend_hash_find(zobj->properties, Z_STR_P(property_name));
+ if (property) {
+ goto fast_assign_obj;
+ }
+ }
+
+ if (!zobj->ce->__set) {
+
+ if (EXPECTED(zobj->properties == NULL)) {
+ rebuild_object_properties(zobj);
+ }
+ if (IS_VAR == IS_CONST) {
+ if (UNEXPECTED(Z_OPT_REFCOUNTED_P(value))) {
+ Z_ADDREF_P(value);
+ }
+ } else if (IS_VAR != IS_TMP_VAR) {
+ if (Z_ISREF_P(value)) {
+ if (IS_VAR == IS_VAR) {
+ zend_reference *ref = Z_REF_P(value);
+ if (--GC_REFCOUNT(ref) == 0) {
+ ZVAL_COPY_VALUE(&tmp, Z_REFVAL_P(value));
+ efree_size(ref, sizeof(zend_reference));
+ value = &tmp;
+ } else {
+ value = Z_REFVAL_P(value);
+ if (Z_REFCOUNTED_P(value)) {
+ Z_ADDREF_P(value);
+ }
+ }
+ } else {
+ value = Z_REFVAL_P(value);
+ if (Z_REFCOUNTED_P(value)) {
+ Z_ADDREF_P(value);
+ }
+ }
+ } else if (IS_VAR == IS_CV && Z_REFCOUNTED_P(value)) {
+ Z_ADDREF_P(value);
+ }
+ }
+ zend_hash_add_new(zobj->properties, Z_STR_P(property_name), value);
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+ goto exit_assign_obj;
+ }
+ }
+ }
+
+ if (!Z_OBJ_HT_P(object)->write_property) {
+ zend_error(E_WARNING, "Attempt to assign property of non-object");
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+ zval_ptr_dtor_nogc(free_op_data);
+ goto exit_assign_obj;
+ }
+
+ if (IS_VAR == IS_CV || IS_VAR == IS_VAR) {
+ ZVAL_DEREF(value);
+ }
+
+ Z_OBJ_HT_P(object)->write_property(object, property_name, value, (IS_CV == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property_name)) : NULL);
+
+ if (UNEXPECTED(RETURN_VALUE_USED(opline)) && EXPECTED(!EG(exception))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+ zval_ptr_dtor_nogc(free_op_data);
+exit_assign_obj:
+
+ if (UNEXPECTED(free_op1)) {zval_ptr_dtor_nogc(free_op1);};
+ /* assign_obj has two opcodes! */
+ ZEND_VM_NEXT_OPCODE_EX(1, 2);
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_CV_OP_DATA_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zend_free_op free_op1;
+ zval *object, *property_name, *value, tmp;
+
+ SAVE_OPLINE();
+ object = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1);
- if (IS_VAR == IS_VAR && UNEXPECTED(object == NULL)) {
- zend_throw_error(NULL, "Cannot use string offset as an array");
+ if (IS_VAR == IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) == IS_UNDEF)) {
+ zend_throw_error(NULL, "Using $this when not in object context");
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));
+
+ property_name = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var);
+ value = _get_zval_ptr_cv_BP_VAR_R(execute_data, (opline+1)->op1.var);
+
+ if (IS_VAR != IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) != IS_OBJECT)) {
+ do {
+ if (Z_ISREF_P(object)) {
+ object = Z_REFVAL_P(object);
+ if (EXPECTED(Z_TYPE_P(object) == IS_OBJECT)) {
+ break;
+ }
+ }
+ if (EXPECTED(Z_TYPE_P(object) <= IS_FALSE ||
+ (Z_TYPE_P(object) == IS_STRING && Z_STRLEN_P(object) == 0))) {
+ zend_object *obj;
+
+ zval_ptr_dtor(object);
+ object_init(object);
+ Z_ADDREF_P(object);
+ obj = Z_OBJ_P(object);
+ zend_error(E_WARNING, "Creating default object from empty value");
+ if (GC_REFCOUNT(obj) == 1) {
+ /* the enclosing container was deleted, obj is unreferenced */
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+
+ OBJ_RELEASE(obj);
+ goto exit_assign_obj;
+ }
+ Z_DELREF_P(object);
+ } else {
+ if (IS_VAR != IS_VAR || EXPECTED(!Z_ISERROR_P(object))) {
+ zend_error(E_WARNING, "Attempt to assign property of non-object");
+ }
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+
+ goto exit_assign_obj;
+ }
+ } while (0);
+ }
+
+ if (IS_CV == IS_CONST &&
+ EXPECTED(Z_OBJCE_P(object) == CACHED_PTR(Z_CACHE_SLOT_P(property_name)))) {
+ uint32_t prop_offset = (uint32_t)(intptr_t)CACHED_PTR(Z_CACHE_SLOT_P(property_name) + sizeof(void*));
+ zend_object *zobj = Z_OBJ_P(object);
+ zval *property;
+
+ if (EXPECTED(prop_offset != (uint32_t)ZEND_DYNAMIC_PROPERTY_OFFSET)) {
+ property = OBJ_PROP(zobj, prop_offset);
+ if (Z_TYPE_P(property) != IS_UNDEF) {
+fast_assign_obj:
+ value = zend_assign_to_variable(property, value, IS_CV);
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+ goto exit_assign_obj;
+ }
+ } else {
+ if (EXPECTED(zobj->properties != NULL)) {
+ if (UNEXPECTED(GC_REFCOUNT(zobj->properties) > 1)) {
+ if (EXPECTED(!(GC_FLAGS(zobj->properties) & IS_ARRAY_IMMUTABLE))) {
+ GC_REFCOUNT(zobj->properties)--;
+ }
+ zobj->properties = zend_array_dup(zobj->properties);
+ }
+ property = zend_hash_find(zobj->properties, Z_STR_P(property_name));
+ if (property) {
+ goto fast_assign_obj;
+ }
+ }
+
+ if (!zobj->ce->__set) {
+
+ if (EXPECTED(zobj->properties == NULL)) {
+ rebuild_object_properties(zobj);
+ }
+ if (IS_CV == IS_CONST) {
+ if (UNEXPECTED(Z_OPT_REFCOUNTED_P(value))) {
+ Z_ADDREF_P(value);
+ }
+ } else if (IS_CV != IS_TMP_VAR) {
+ if (Z_ISREF_P(value)) {
+ if (IS_CV == IS_VAR) {
+ zend_reference *ref = Z_REF_P(value);
+ if (--GC_REFCOUNT(ref) == 0) {
+ ZVAL_COPY_VALUE(&tmp, Z_REFVAL_P(value));
+ efree_size(ref, sizeof(zend_reference));
+ value = &tmp;
+ } else {
+ value = Z_REFVAL_P(value);
+ if (Z_REFCOUNTED_P(value)) {
+ Z_ADDREF_P(value);
+ }
+ }
+ } else {
+ value = Z_REFVAL_P(value);
+ if (Z_REFCOUNTED_P(value)) {
+ Z_ADDREF_P(value);
+ }
+ }
+ } else if (IS_CV == IS_CV && Z_REFCOUNTED_P(value)) {
+ Z_ADDREF_P(value);
+ }
+ }
+ zend_hash_add_new(zobj->properties, Z_STR_P(property_name), value);
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+ goto exit_assign_obj;
+ }
+ }
+ }
+
+ if (!Z_OBJ_HT_P(object)->write_property) {
+ zend_error(E_WARNING, "Attempt to assign property of non-object");
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+
+ goto exit_assign_obj;
+ }
+
+ if (IS_CV == IS_CV || IS_CV == IS_VAR) {
+ ZVAL_DEREF(value);
+ }
+
+ Z_OBJ_HT_P(object)->write_property(object, property_name, value, (IS_CV == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property_name)) : NULL);
+
+ if (UNEXPECTED(RETURN_VALUE_USED(opline)) && EXPECTED(!EG(exception))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+
+exit_assign_obj:
if (UNEXPECTED(free_op1)) {zval_ptr_dtor_nogc(free_op1);};
/* assign_obj has two opcodes! */
ZEND_VM_NEXT_OPCODE_EX(1, 2);
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_VAR_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_VAR_CV_OP_DATA_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
zend_free_op free_op1;
zval *object_ptr;
- zend_free_op free_op_data1;
+
zval *value;
zval *variable_ptr;
zval *dim;
@@ -20645,40 +23807,214 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_VAR_CV_HANDLER
SAVE_OPLINE();
object_ptr = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1);
- if (IS_VAR == IS_VAR && UNEXPECTED(object_ptr == NULL)) {
- zend_throw_error(NULL, "Cannot use string offset as an array");
- FREE_UNFETCHED_OP((opline+1)->op1_type, (opline+1)->op1.var);
+ if (EXPECTED(Z_TYPE_P(object_ptr) == IS_ARRAY)) {
+try_assign_dim_array:
+ SEPARATE_ARRAY(object_ptr);
+ if (IS_CV == IS_UNUSED) {
+ variable_ptr = zend_hash_next_index_insert(Z_ARRVAL_P(object_ptr), &EG(uninitialized_zval));
+ if (UNEXPECTED(variable_ptr == NULL)) {
+ zend_error(E_WARNING, "Cannot add element to the array as the next element is already occupied");
+ goto assign_dim_error;
+ }
+ } else {
+ dim = _get_zval_ptr_cv_undef(execute_data, opline->op2.var);
+ if (IS_CV == IS_CONST) {
+ variable_ptr = zend_fetch_dimension_address_inner_W_CONST(Z_ARRVAL_P(object_ptr), dim);
+ } else {
+ variable_ptr = zend_fetch_dimension_address_inner_W(Z_ARRVAL_P(object_ptr), dim);
+ }
+ if (UNEXPECTED(variable_ptr == NULL)) {
+ goto assign_dim_error;
+ }
+ }
+ value = EX_CONSTANT((opline+1)->op1);
+ value = zend_assign_to_variable(variable_ptr, value, IS_CONST);
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+ } 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)) {
+ dim = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var);
+ value = EX_CONSTANT((opline+1)->op1);
- HANDLE_EXCEPTION();
+ zend_assign_to_object_dim(object_ptr, dim, value);
+
+ if (UNEXPECTED(RETURN_VALUE_USED(opline)) && EXPECTED(!EG(exception))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+
+ } else if (EXPECTED(Z_TYPE_P(object_ptr) == IS_STRING)) {
+ if (IS_CV == IS_UNUSED) {
+ zend_throw_error(NULL, "[] operator not supported for strings");
+
+ if (UNEXPECTED(free_op1)) {zval_ptr_dtor_nogc(free_op1);};
+ HANDLE_EXCEPTION();
+ } else {
+ dim = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var);
+ value = EX_CONSTANT((opline+1)->op1);
+ zend_assign_to_string_offset(object_ptr, dim, value, (UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL));
+
+ }
+ } else if (EXPECTED(Z_TYPE_P(object_ptr) <= IS_FALSE)) {
+ 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 (IS_VAR != IS_VAR || EXPECTED(!Z_ISERROR_P(object_ptr))) {
+ zend_error(E_WARNING, "Cannot use a scalar value as an array");
+ }
+ dim = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var);
+assign_dim_error:
+
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+ }
}
+ if (IS_CV != IS_UNUSED) {
+
+ }
+ if (UNEXPECTED(free_op1)) {zval_ptr_dtor_nogc(free_op1);};
+ /* assign_dim has two opcodes! */
+ ZEND_VM_NEXT_OPCODE_EX(1, 2);
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_VAR_CV_OP_DATA_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zend_free_op free_op1;
+ zval *object_ptr;
+ zend_free_op free_op_data;
+ zval *value;
+ zval *variable_ptr;
+ zval *dim;
+
+ SAVE_OPLINE();
+ object_ptr = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1);
if (EXPECTED(Z_TYPE_P(object_ptr) == IS_ARRAY)) {
try_assign_dim_array:
+ SEPARATE_ARRAY(object_ptr);
if (IS_CV == IS_UNUSED) {
- SEPARATE_ARRAY(object_ptr);
variable_ptr = zend_hash_next_index_insert(Z_ARRVAL_P(object_ptr), &EG(uninitialized_zval));
if (UNEXPECTED(variable_ptr == NULL)) {
zend_error(E_WARNING, "Cannot add element to the array as the next element is already occupied");
- variable_ptr = &EG(error_zval);
+ goto assign_dim_error;
}
} else {
+ dim = _get_zval_ptr_cv_undef(execute_data, opline->op2.var);
+ if (IS_CV == IS_CONST) {
+ variable_ptr = zend_fetch_dimension_address_inner_W_CONST(Z_ARRVAL_P(object_ptr), dim);
+ } else {
+ variable_ptr = zend_fetch_dimension_address_inner_W(Z_ARRVAL_P(object_ptr), dim);
+ }
+ if (UNEXPECTED(variable_ptr == NULL)) {
+ goto assign_dim_error;
+ }
+ }
+ value = _get_zval_ptr_tmp((opline+1)->op1.var, execute_data, &free_op_data);
+ value = zend_assign_to_variable(variable_ptr, value, IS_TMP_VAR);
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+ } 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)) {
dim = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var);
- SEPARATE_ARRAY(object_ptr);
- variable_ptr = zend_fetch_dimension_address_inner(Z_ARRVAL_P(object_ptr), dim, IS_CV, BP_VAR_W);
+ value = _get_zval_ptr_tmp((opline+1)->op1.var, execute_data, &free_op_data);
- }
- value = get_zval_ptr_r((opline+1)->op1_type, (opline+1)->op1, execute_data, &free_op_data1);
- if (UNEXPECTED(variable_ptr == &EG(error_zval))) {
- FREE_OP(free_op_data1);
+ zend_assign_to_object_dim(object_ptr, dim, value);
+
+ if (UNEXPECTED(RETURN_VALUE_USED(opline)) && EXPECTED(!EG(exception))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+
+ zval_ptr_dtor_nogc(free_op_data);
+ } else if (EXPECTED(Z_TYPE_P(object_ptr) == IS_STRING)) {
+ if (IS_CV == IS_UNUSED) {
+ zend_throw_error(NULL, "[] operator not supported for strings");
+ zval_ptr_dtor_nogc(EX_VAR((opline+1)->op1.var));
+ if (UNEXPECTED(free_op1)) {zval_ptr_dtor_nogc(free_op1);};
+ HANDLE_EXCEPTION();
+ } else {
+ dim = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var);
+ value = _get_zval_ptr_tmp((opline+1)->op1.var, execute_data, &free_op_data);
+ zend_assign_to_string_offset(object_ptr, dim, value, (UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL));
+ zval_ptr_dtor_nogc(free_op_data);
+ }
+ } else if (EXPECTED(Z_TYPE_P(object_ptr) <= IS_FALSE)) {
+ 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 (IS_VAR != IS_VAR || EXPECTED(!Z_ISERROR_P(object_ptr))) {
+ zend_error(E_WARNING, "Cannot use a scalar value as an array");
+ }
+ dim = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var);
+assign_dim_error:
+ zval_ptr_dtor_nogc(EX_VAR((opline+1)->op1.var));
if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
ZVAL_NULL(EX_VAR(opline->result.var));
}
+ }
+ }
+ if (IS_CV != IS_UNUSED) {
+
+ }
+ if (UNEXPECTED(free_op1)) {zval_ptr_dtor_nogc(free_op1);};
+ /* assign_dim has two opcodes! */
+ ZEND_VM_NEXT_OPCODE_EX(1, 2);
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_VAR_CV_OP_DATA_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zend_free_op free_op1;
+ zval *object_ptr;
+ zend_free_op free_op_data;
+ zval *value;
+ zval *variable_ptr;
+ zval *dim;
+
+ SAVE_OPLINE();
+ object_ptr = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1);
+
+ if (EXPECTED(Z_TYPE_P(object_ptr) == IS_ARRAY)) {
+try_assign_dim_array:
+ SEPARATE_ARRAY(object_ptr);
+ if (IS_CV == IS_UNUSED) {
+ variable_ptr = zend_hash_next_index_insert(Z_ARRVAL_P(object_ptr), &EG(uninitialized_zval));
+ if (UNEXPECTED(variable_ptr == NULL)) {
+ zend_error(E_WARNING, "Cannot add element to the array as the next element is already occupied");
+ goto assign_dim_error;
+ }
} else {
- value = zend_assign_to_variable(variable_ptr, value, (opline+1)->op1_type);
- if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
- ZVAL_COPY(EX_VAR(opline->result.var), value);
+ dim = _get_zval_ptr_cv_undef(execute_data, opline->op2.var);
+ if (IS_CV == IS_CONST) {
+ variable_ptr = zend_fetch_dimension_address_inner_W_CONST(Z_ARRVAL_P(object_ptr), dim);
+ } else {
+ variable_ptr = zend_fetch_dimension_address_inner_W(Z_ARRVAL_P(object_ptr), dim);
+ }
+ if (UNEXPECTED(variable_ptr == NULL)) {
+ goto assign_dim_error;
}
}
+ value = _get_zval_ptr_var((opline+1)->op1.var, execute_data, &free_op_data);
+ value = zend_assign_to_variable(variable_ptr, value, IS_VAR);
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
} else {
if (EXPECTED(Z_ISREF_P(object_ptr))) {
object_ptr = Z_REFVAL_P(object_ptr);
@@ -20687,58 +24023,144 @@ try_assign_dim_array:
}
}
if (EXPECTED(Z_TYPE_P(object_ptr) == IS_OBJECT)) {
+ dim = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var);
+ value = _get_zval_ptr_var((opline+1)->op1.var, execute_data, &free_op_data);
- zval *property_name = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var);
+ zend_assign_to_object_dim(object_ptr, dim, value);
- 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);
+ if (UNEXPECTED(RETURN_VALUE_USED(opline)) && EXPECTED(!EG(exception))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+ zval_ptr_dtor_nogc(free_op_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_throw_error(NULL, "[] operator not supported for strings");
- FREE_UNFETCHED_OP((opline+1)->op1_type, (opline+1)->op1.var);
- if (UNEXPECTED(free_op1)) {zval_ptr_dtor_nogc(free_op1);};
- HANDLE_EXCEPTION();
- } else {
- zend_long offset;
+ if (IS_CV == IS_UNUSED) {
+ zend_throw_error(NULL, "[] operator not supported for strings");
+ zval_ptr_dtor_nogc(EX_VAR((opline+1)->op1.var));
+ if (UNEXPECTED(free_op1)) {zval_ptr_dtor_nogc(free_op1);};
+ HANDLE_EXCEPTION();
+ } else {
+ dim = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var);
+ value = _get_zval_ptr_var_deref((opline+1)->op1.var, execute_data, &free_op_data);
+ zend_assign_to_string_offset(object_ptr, dim, value, (UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL));
+ zval_ptr_dtor_nogc(free_op_data);
+ }
+ } else if (EXPECTED(Z_TYPE_P(object_ptr) <= IS_FALSE)) {
+ 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 (IS_VAR != IS_VAR || EXPECTED(!Z_ISERROR_P(object_ptr))) {
+ zend_error(E_WARNING, "Cannot use a scalar value as an array");
+ }
+ dim = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var);
+assign_dim_error:
+ zval_ptr_dtor_nogc(EX_VAR((opline+1)->op1.var));
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+ }
+ }
+ if (IS_CV != IS_UNUSED) {
- dim = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var);
- offset = zend_fetch_string_offset(object_ptr, dim, BP_VAR_W);
+ }
+ if (UNEXPECTED(free_op1)) {zval_ptr_dtor_nogc(free_op1);};
+ /* assign_dim has two opcodes! */
+ ZEND_VM_NEXT_OPCODE_EX(1, 2);
+}
- value = get_zval_ptr_r_deref((opline+1)->op1_type, (opline+1)->op1, execute_data, &free_op_data1);
- 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);
- }
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_VAR_CV_OP_DATA_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zend_free_op free_op1;
+ zval *object_ptr;
+
+ zval *value;
+ zval *variable_ptr;
+ zval *dim;
+
+ SAVE_OPLINE();
+ object_ptr = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1);
+
+ if (EXPECTED(Z_TYPE_P(object_ptr) == IS_ARRAY)) {
+try_assign_dim_array:
+ SEPARATE_ARRAY(object_ptr);
+ if (IS_CV == IS_UNUSED) {
+ variable_ptr = zend_hash_next_index_insert(Z_ARRVAL_P(object_ptr), &EG(uninitialized_zval));
+ if (UNEXPECTED(variable_ptr == NULL)) {
+ zend_error(E_WARNING, "Cannot add element to the array as the next element is already occupied");
+ goto assign_dim_error;
+ }
+ } else {
+ dim = _get_zval_ptr_cv_undef(execute_data, opline->op2.var);
+ if (IS_CV == IS_CONST) {
+ variable_ptr = zend_fetch_dimension_address_inner_W_CONST(Z_ARRVAL_P(object_ptr), dim);
} 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);
+ variable_ptr = zend_fetch_dimension_address_inner_W(Z_ARRVAL_P(object_ptr), dim);
+ }
+ if (UNEXPECTED(variable_ptr == NULL)) {
+ goto assign_dim_error;
+ }
+ }
+ value = _get_zval_ptr_cv_BP_VAR_R(execute_data, (opline+1)->op1.var);
+ value = zend_assign_to_variable(variable_ptr, value, IS_CV);
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+ } 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;
}
- } 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;
+ }
+ if (EXPECTED(Z_TYPE_P(object_ptr) == IS_OBJECT)) {
+ dim = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var);
+ value = _get_zval_ptr_cv_BP_VAR_R(execute_data, (opline+1)->op1.var);
+
+ zend_assign_to_object_dim(object_ptr, dim, value);
+
+ if (UNEXPECTED(RETURN_VALUE_USED(opline)) && EXPECTED(!EG(exception))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
}
- goto assign_dim_convert_to_array;
+
+ } else if (EXPECTED(Z_TYPE_P(object_ptr) == IS_STRING)) {
+ if (IS_CV == IS_UNUSED) {
+ zend_throw_error(NULL, "[] operator not supported for strings");
+
+ if (UNEXPECTED(free_op1)) {zval_ptr_dtor_nogc(free_op1);};
+ HANDLE_EXCEPTION();
+ } else {
+ dim = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var);
+ value = _get_zval_ptr_cv_deref_BP_VAR_R(execute_data, (opline+1)->op1.var);
+ zend_assign_to_string_offset(object_ptr, dim, value, (UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL));
+
+ }
+ } else if (EXPECTED(Z_TYPE_P(object_ptr) <= IS_FALSE)) {
+ 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 {
- zend_error(E_WARNING, "Cannot use a scalar value as an array");
-assign_dim_clean:
+ if (IS_VAR != IS_VAR || EXPECTED(!Z_ISERROR_P(object_ptr))) {
+ zend_error(E_WARNING, "Cannot use a scalar value as an array");
+ }
dim = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var);
+assign_dim_error:
- value = get_zval_ptr_r((opline+1)->op1_type, (opline+1)->op1, execute_data, &free_op_data1);
- FREE_OP(free_op_data1);
if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
ZVAL_NULL(EX_VAR(opline->result.var));
}
}
}
+ if (IS_CV != IS_UNUSED) {
+
+ }
if (UNEXPECTED(free_op1)) {zval_ptr_dtor_nogc(free_op1);};
/* assign_dim has two opcodes! */
ZEND_VM_NEXT_OPCODE_EX(1, 2);
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_SPEC_VAR_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_SPEC_VAR_CV_RETVAL_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
zend_free_op free_op1;
@@ -20749,14 +24171,42 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_SPEC_VAR_CV_HANDLER(ZEN
value = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var);
variable_ptr = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1);
- if (IS_VAR == IS_VAR && UNEXPECTED(variable_ptr == &EG(error_zval))) {
+ if (IS_VAR == IS_VAR && UNEXPECTED(Z_ISERROR_P(variable_ptr))) {
- if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ if (UNEXPECTED(0)) {
ZVAL_NULL(EX_VAR(opline->result.var));
}
} else {
value = zend_assign_to_variable(variable_ptr, value, IS_CV);
- if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ if (UNEXPECTED(0)) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+ if (UNEXPECTED(free_op1)) {zval_ptr_dtor_nogc(free_op1);};
+ /* zend_assign_to_variable() always takes care of op2, never free it! */
+ }
+
+ ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_SPEC_VAR_CV_RETVAL_USED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zend_free_op free_op1;
+ zval *value;
+ zval *variable_ptr;
+
+ SAVE_OPLINE();
+ value = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var);
+ variable_ptr = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1);
+
+ if (IS_VAR == IS_VAR && UNEXPECTED(Z_ISERROR_P(variable_ptr))) {
+
+ if (UNEXPECTED(1)) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+ } else {
+ value = zend_assign_to_variable(variable_ptr, value, IS_CV);
+ if (UNEXPECTED(1)) {
ZVAL_COPY(EX_VAR(opline->result.var), value);
}
if (UNEXPECTED(free_op1)) {zval_ptr_dtor_nogc(free_op1);};
@@ -20775,53 +24225,48 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_REF_SPEC_VAR_CV_HANDLER
SAVE_OPLINE();
value_ptr = _get_zval_ptr_cv_BP_VAR_W(execute_data, opline->op2.var);
+ variable_ptr = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1);
- if (IS_CV == IS_VAR && UNEXPECTED(value_ptr == NULL)) {
- zend_throw_error(NULL, "Cannot create references to/from string offsets nor overloaded objects");
- zval_ptr_dtor_nogc(EX_VAR(opline->op1.var));
- HANDLE_EXCEPTION();
- }
if (IS_VAR == IS_VAR &&
UNEXPECTED(Z_TYPE_P(EX_VAR(opline->op1.var)) != IS_INDIRECT) &&
- UNEXPECTED(!Z_ISREF_P(EX_VAR(opline->op1.var)))) {
+ UNEXPECTED(!Z_ISREF_P(EX_VAR(opline->op1.var))) &&
+ UNEXPECTED(!Z_ISERROR_P(EX_VAR(opline->op1.var)))) {
+
zend_throw_error(NULL, "Cannot assign by reference to overloaded object");
HANDLE_EXCEPTION();
- }
- if (IS_CV == IS_VAR &&
- (value_ptr == &EG(uninitialized_zval) ||
- (opline->extended_value == ZEND_RETURNS_FUNCTION &&
- !(Z_VAR_FLAGS_P(value_ptr) & IS_VAR_RET_REF)))) {
- if (!0 && UNEXPECTED(Z_TYPE_P(EX_VAR(opline->op2.var)) != IS_INDIRECT)) { /* undo the effect of get_zval_ptr_ptr() */
- Z_TRY_ADDREF_P(value_ptr);
- }
+
+ } else if (IS_CV == IS_VAR &&
+ opline->extended_value == ZEND_RETURNS_FUNCTION &&
+ UNEXPECTED(!Z_ISREF_P(value_ptr))) {
zend_error(E_NOTICE, "Only variables should be assigned by reference");
if (UNEXPECTED(EG(exception) != NULL)) {
HANDLE_EXCEPTION();
}
- ZEND_VM_TAIL_CALL(ZEND_ASSIGN_SPEC_VAR_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU));
- }
- variable_ptr = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1);
- if (IS_VAR == IS_VAR && UNEXPECTED(variable_ptr == NULL)) {
- zend_throw_error(NULL, "Cannot create references to/from string offsets nor overloaded objects");
+ value_ptr = zend_assign_to_variable(variable_ptr, value_ptr, IS_CV);
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value_ptr);
+ }
+ /* zend_assign_to_variable() always takes care of op2, never free it! */
- HANDLE_EXCEPTION();
- }
- if ((IS_VAR == IS_VAR && UNEXPECTED(variable_ptr == &EG(error_zval))) ||
- (IS_CV == IS_VAR && UNEXPECTED(value_ptr == &EG(error_zval)))) {
- variable_ptr = &EG(uninitialized_zval);
} else {
- zend_assign_to_variable_reference(variable_ptr, value_ptr);
- }
- if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
- ZVAL_COPY(EX_VAR(opline->result.var), variable_ptr);
+ if ((IS_VAR == IS_VAR && UNEXPECTED(Z_ISERROR_P(variable_ptr))) ||
+ (IS_CV == IS_VAR && UNEXPECTED(Z_ISERROR_P(value_ptr)))) {
+ variable_ptr = &EG(uninitialized_zval);
+ } else {
+ zend_assign_to_variable_reference(variable_ptr, value_ptr);
+ }
+
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), variable_ptr);
+ }
+
}
if (UNEXPECTED(free_op1)) {zval_ptr_dtor_nogc(free_op1);};
-
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
@@ -20850,6 +24295,13 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_STATIC_METHOD_CALL_SPEC_V
}
CACHE_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)), ce);
}
+ } else if (IS_VAR == IS_UNUSED) {
+ ce = zend_fetch_class(NULL, opline->op1.num);
+ if (UNEXPECTED(ce == NULL)) {
+ ZEND_ASSERT(EG(exception));
+
+ HANDLE_EXCEPTION();
+ }
} else {
ce = Z_CE_P(EX_VAR(opline->op1.var));
}
@@ -20908,6 +24360,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_STATIC_METHOD_CALL_SPEC_V
CACHE_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(function_name), ce, fbc);
}
}
+ if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!fbc->op_array.run_time_cache)) {
+ init_func_run_time_cache(&fbc->op_array);
+ }
if (IS_CV != IS_CONST) {
}
@@ -20916,16 +24371,19 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_STATIC_METHOD_CALL_SPEC_V
zend_throw_error(NULL, "Cannot call constructor");
HANDLE_EXCEPTION();
}
- if (Z_OBJ(EX(This)) && Z_OBJ(EX(This))->ce != ce->constructor->common.scope && (ce->constructor->common.fn_flags & ZEND_ACC_PRIVATE)) {
+ if (Z_TYPE(EX(This)) == IS_OBJECT && Z_OBJ(EX(This))->ce != ce->constructor->common.scope && (ce->constructor->common.fn_flags & ZEND_ACC_PRIVATE)) {
zend_throw_error(NULL, "Cannot call private %s::__construct()", ZSTR_VAL(ce->name));
HANDLE_EXCEPTION();
}
fbc = ce->constructor;
+ if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!fbc->op_array.run_time_cache)) {
+ init_func_run_time_cache(&fbc->op_array);
+ }
}
object = NULL;
if (!(fbc->common.fn_flags & ZEND_ACC_STATIC)) {
- if (Z_OBJ(EX(This)) && instanceof_function(Z_OBJCE(EX(This)), ce)) {
+ if (Z_TYPE(EX(This)) == IS_OBJECT && instanceof_function(Z_OBJCE(EX(This)), ce)) {
object = Z_OBJ(EX(This));
ce = object->ce;
} else {
@@ -20950,11 +24408,15 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_STATIC_METHOD_CALL_SPEC_V
}
}
- if (IS_VAR != IS_CONST) {
+ if (IS_VAR == IS_UNUSED) {
/* previous opcode is ZEND_FETCH_CLASS */
- if (((opline-1)->extended_value & ZEND_FETCH_CLASS_MASK) == ZEND_FETCH_CLASS_PARENT ||
- ((opline-1)->extended_value & ZEND_FETCH_CLASS_MASK) == ZEND_FETCH_CLASS_SELF) {
- ce = EX(called_scope);
+ if ((opline->op1.num & ZEND_FETCH_CLASS_MASK) == ZEND_FETCH_CLASS_PARENT ||
+ (opline->op1.num & ZEND_FETCH_CLASS_MASK) == ZEND_FETCH_CLASS_SELF) {
+ if (Z_TYPE(EX(This)) == IS_OBJECT) {
+ ce = Z_OBJCE(EX(This));
+ } else {
+ ce = Z_CE(EX(This));
+ }
}
}
@@ -20976,11 +24438,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ADD_ARRAY_ELEMENT_SPEC_VAR_CV_
if ((IS_VAR == IS_VAR || IS_VAR == IS_CV) &&
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_throw_error(NULL, "Cannot create references to/from string offsets");
- zend_array_destroy(Z_ARRVAL_P(EX_VAR(opline->result.var)));
- HANDLE_EXCEPTION();
- }
ZVAL_MAKE_REF(expr_ptr);
Z_ADDREF_P(expr_ptr);
if (UNEXPECTED(free_op1)) {zval_ptr_dtor_nogc(free_op1);};
@@ -20989,10 +24446,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ADD_ARRAY_ELEMENT_SPEC_VAR_CV_
if (IS_VAR == IS_TMP_VAR) {
/* pass */
} else if (IS_VAR == IS_CONST) {
- 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;
+ if (Z_REFCOUNTED_P(expr_ptr)) {
+ Z_ADDREF_P(expr_ptr);
}
} else if (IS_VAR == IS_CV) {
ZVAL_DEREF(expr_ptr);
@@ -21107,20 +24562,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_UNSET_DIM_SPEC_VAR_CV_HANDLER(
SAVE_OPLINE();
container = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1);
- if (IS_VAR == IS_UNUSED && UNEXPECTED(Z_OBJ_P(container) == NULL)) {
- zend_throw_error(NULL, "Using $this when not in object context");
-
- HANDLE_EXCEPTION();
- }
- if (IS_VAR == IS_VAR && UNEXPECTED(container == NULL)) {
- zend_throw_error(NULL, "Cannot unset string offsets");
-
- HANDLE_EXCEPTION();
- }
offset = _get_zval_ptr_cv_undef(execute_data, opline->op2.var);
do {
- if (IS_VAR != IS_UNUSED && EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) {
+ if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) {
HashTable *ht;
unset_dim_array:
@@ -21170,16 +24615,19 @@ num_index_dim:
zend_error(E_WARNING, "Illegal offset type in unset");
}
break;
- } else if (IS_VAR != IS_UNUSED && Z_ISREF_P(container)) {
+ } else if (Z_ISREF_P(container)) {
container = Z_REFVAL_P(container);
if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) {
goto unset_dim_array;
}
}
+ if (IS_VAR == IS_CV && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) {
+ container = GET_OP1_UNDEF_CV(container, BP_VAR_R);
+ }
if (IS_CV == IS_CV && UNEXPECTED(Z_TYPE_P(offset) == IS_UNDEF)) {
offset = GET_OP2_UNDEF_CV(offset, BP_VAR_R);
}
- if (IS_VAR == IS_UNUSED || EXPECTED(Z_TYPE_P(container) == IS_OBJECT)) {
+ if (EXPECTED(Z_TYPE_P(container) == IS_OBJECT)) {
if (UNEXPECTED(Z_OBJ_HT_P(container)->unset_dimension == NULL)) {
zend_throw_error(NULL, "Cannot use object as array");
} else {
@@ -21203,16 +24651,11 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_UNSET_OBJ_SPEC_VAR_CV_HANDLER(
SAVE_OPLINE();
container = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1);
- if (IS_VAR == IS_UNUSED && UNEXPECTED(Z_OBJ_P(container) == NULL)) {
+ if (IS_VAR == IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
HANDLE_EXCEPTION();
}
- if (IS_VAR == IS_VAR && UNEXPECTED(container == NULL)) {
- zend_throw_error(NULL, "Cannot unset string offsets");
-
- HANDLE_EXCEPTION();
- }
offset = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var);
do {
@@ -21264,7 +24707,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_VAR_CV_HANDLER(ZEND
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) {
+ if (IS_VAR & (IS_CONST|IS_TMP_VAR)) {
zval *value;
zend_error(E_NOTICE, "Only variable references should be yielded by reference");
@@ -21272,25 +24715,19 @@ 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 (IS_VAR == IS_CONST) {
- if (UNEXPECTED(Z_OPT_COPYABLE(generator->value))) {
- zval_copy_ctor_func(&generator->value);
+ if (UNEXPECTED(Z_OPT_REFCOUNTED(generator->value))) {
+ Z_ADDREF(generator->value);
}
}
} else {
zval *value_ptr = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1);
- if (IS_VAR == IS_VAR && UNEXPECTED(value_ptr == NULL)) {
- zend_throw_error(NULL, "Cannot yield string offsets by reference");
-
- HANDLE_EXCEPTION();
- }
-
/* If a function call result is yielded and the function did
* not return by reference we throw a notice. */
if (IS_VAR == IS_VAR &&
(value_ptr == &EG(uninitialized_zval) ||
(opline->extended_value == ZEND_RETURNS_FUNCTION &&
- !(Z_VAR_FLAGS_P(value_ptr) & IS_VAR_RET_REF)))) {
+ !Z_ISREF_P(value_ptr)))) {
zend_error(E_NOTICE, "Only variable references should be yielded by reference");
} else {
ZVAL_MAKE_REF(value_ptr);
@@ -21305,8 +24742,8 @@ 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_COPY_VALUE(&generator->value, value);
- if (UNEXPECTED(Z_OPT_COPYABLE(generator->value))) {
- zval_copy_ctor_func(&generator->value);
+ if (UNEXPECTED(Z_OPT_REFCOUNTED(generator->value))) {
+ Z_ADDREF(generator->value);
}
} else if (IS_VAR == IS_TMP_VAR) {
ZVAL_COPY_VALUE(&generator->value, value);
@@ -21333,8 +24770,8 @@ 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_COPY_VALUE(&generator->key, key);
- if (UNEXPECTED(Z_OPT_COPYABLE(generator->key))) {
- zval_copy_ctor_func(&generator->key);
+ if (UNEXPECTED(Z_OPT_REFCOUNTED(generator->key))) {
+ Z_ADDREF(generator->key);
}
} else if (IS_CV == IS_TMP_VAR) {
ZVAL_COPY_VALUE(&generator->key, key);
@@ -21391,7 +24828,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_binary_assign_op_obj_helper_SP
SAVE_OPLINE();
object = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1);
- if (IS_VAR == IS_UNUSED && UNEXPECTED(Z_OBJ_P(object) == NULL)) {
+ if (IS_VAR == IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
FREE_UNFETCHED_OP((opline+1)->op1_type, (opline+1)->op1.var);
zval_ptr_dtor_nogc(EX_VAR(opline->op2.var));
@@ -21400,13 +24837,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_binary_assign_op_obj_helper_SP
property = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2);
- if (IS_VAR == IS_VAR && UNEXPECTED(object == NULL)) {
- zend_throw_error(NULL, "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();
- }
-
do {
value = get_zval_ptr_r((opline+1)->op1_type, (opline+1)->op1, execute_data, &free_op_data1);
@@ -21424,7 +24854,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_binary_assign_op_obj_helper_SP
/* here we are sure we are dealing with an object */
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)) {
- if (UNEXPECTED(zptr == &EG(error_zval))) {
+ if (UNEXPECTED(Z_ISERROR_P(zptr))) {
if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
ZVAL_NULL(EX_VAR(opline->result.var));
}
@@ -21453,66 +24883,85 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_binary_assign_op_dim_helper_SP
{
USE_OPLINE
zend_free_op free_op1, free_op2, free_op_data1;
- zval *var_ptr, rv;
+ zval *var_ptr;
zval *value, *container, *dim;
SAVE_OPLINE();
container = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1);
- if (IS_VAR == IS_UNUSED && UNEXPECTED(Z_OBJ_P(container) == NULL)) {
- zend_throw_error(NULL, "Using $this when not in object context");
- FREE_UNFETCHED_OP((opline+1)->op1_type, (opline+1)->op1.var);
- zval_ptr_dtor_nogc(EX_VAR(opline->op2.var));
- HANDLE_EXCEPTION();
- }
- if (IS_VAR == IS_VAR && UNEXPECTED(container == NULL)) {
- zend_throw_error(NULL, "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();
- }
- dim = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2);
+ if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) {
+assign_dim_op_array:
+ SEPARATE_ARRAY(container);
+assign_dim_op_new_array:
+ if ((IS_TMP_VAR|IS_VAR) == IS_UNUSED) {
+ var_ptr = zend_hash_next_index_insert(Z_ARRVAL_P(container), &EG(uninitialized_zval));
+ if (UNEXPECTED(!var_ptr)) {
+ zend_error(E_WARNING, "Cannot add element to the array as the next element is already occupied");
+ goto assign_dim_op_ret_null;
+ }
+ } else {
+ dim = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2);
- do {
- if (IS_VAR == IS_UNUSED || UNEXPECTED(Z_TYPE_P(container) != IS_ARRAY)) {
- if (IS_VAR != IS_UNUSED) {
- ZVAL_DEREF(container);
+ if ((IS_TMP_VAR|IS_VAR) == IS_CONST) {
+ var_ptr = zend_fetch_dimension_address_inner_RW_CONST(Z_ARRVAL_P(container), dim);
+ } else {
+ var_ptr = zend_fetch_dimension_address_inner_RW(Z_ARRVAL_P(container), dim);
}
- if (IS_VAR == IS_UNUSED || EXPECTED(Z_TYPE_P(container) == IS_OBJECT)) {
- value = get_zval_ptr_r((opline+1)->op1_type, (opline+1)->op1, execute_data, &free_op_data1);
- zend_binary_assign_op_obj_dim(container, dim, value, UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL, binary_op);
- break;
+ if (UNEXPECTED(!var_ptr)) {
+ goto assign_dim_op_ret_null;
}
+ ZVAL_DEREF(var_ptr);
+ SEPARATE_ZVAL_NOREF(var_ptr);
}
- zend_fetch_dimension_address_RW(&rv, container, dim, (IS_TMP_VAR|IS_VAR));
value = get_zval_ptr_r((opline+1)->op1_type, (opline+1)->op1, execute_data, &free_op_data1);
- ZEND_ASSERT(Z_TYPE(rv) == IS_INDIRECT);
- var_ptr = Z_INDIRECT(rv);
- if (UNEXPECTED(var_ptr == NULL)) {
- zend_throw_error(NULL, "Cannot use assign-op operators with overloaded objects nor string offsets");
- zval_ptr_dtor_nogc(free_op2);
- FREE_OP(free_op_data1);
- if (UNEXPECTED(free_op1)) {zval_ptr_dtor_nogc(free_op1);};
- HANDLE_EXCEPTION();
- }
+ binary_op(var_ptr, var_ptr, value);
- if (UNEXPECTED(var_ptr == &EG(error_zval))) {
- if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
- ZVAL_NULL(EX_VAR(opline->result.var));
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), var_ptr);
+ }
+ } else {
+ if (EXPECTED(Z_ISREF_P(container))) {
+ container = Z_REFVAL_P(container);
+ if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) {
+ goto assign_dim_op_array;
}
- } else {
- ZVAL_DEREF(var_ptr);
- SEPARATE_ZVAL_NOREF(var_ptr);
+ } else if (IS_VAR == IS_CV && UNEXPECTED(Z_TYPE_INFO_P(container) == IS_UNDEF)) {
+ container = GET_OP1_UNDEF_CV(container, BP_VAR_RW);
+assign_dim_op_convert_to_array:
+ ZVAL_NEW_ARR(container);
+ zend_hash_init(Z_ARRVAL_P(container), 8, NULL, ZVAL_PTR_DTOR, 0);
+ goto assign_dim_op_new_array;
+ }
- binary_op(var_ptr, var_ptr, value);
+ dim = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2);
- if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
- ZVAL_COPY(EX_VAR(opline->result.var), var_ptr);
+ if (EXPECTED(Z_TYPE_P(container) == IS_OBJECT)) {
+ value = get_zval_ptr_r((opline+1)->op1_type, (opline+1)->op1, execute_data, &free_op_data1);
+ zend_binary_assign_op_obj_dim(container, dim, value, UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL, binary_op);
+ } else {
+ if (UNEXPECTED(Z_TYPE_P(container) == IS_STRING)) {
+ if ((IS_TMP_VAR|IS_VAR) == IS_UNUSED) {
+ zend_throw_error(NULL, "[] operator not supported for strings");
+ } else {
+ zend_check_string_offset(dim, BP_VAR_RW);
+ zend_wrong_string_offset();
+ }
+ } else if (EXPECTED(Z_TYPE_P(container) <= IS_FALSE)) {
+ goto assign_dim_op_convert_to_array;
+ } else {
+ if (UNEXPECTED(IS_VAR != IS_VAR || EXPECTED(!Z_ISERROR_P(container)))) {
+ zend_error(E_WARNING, "Cannot use a scalar value as an array");
+ }
+assign_dim_op_ret_null:
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
}
+ value = get_zval_ptr_r((opline+1)->op1_type, (opline+1)->op1, execute_data, &free_op_data1);
}
- } while (0);
+ }
zval_ptr_dtor_nogc(free_op2);
FREE_OP(free_op_data1);
@@ -21531,13 +24980,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_binary_assign_op_helper_SPEC_V
value = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2);
var_ptr = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1);
- if (IS_VAR == IS_VAR && UNEXPECTED(var_ptr == NULL)) {
- zend_throw_error(NULL, "Cannot use assign-op operators with overloaded objects nor string offsets");
- zval_ptr_dtor_nogc(free_op2);
- HANDLE_EXCEPTION();
- }
-
- if (IS_VAR == IS_VAR && UNEXPECTED(var_ptr == &EG(error_zval))) {
+ if (IS_VAR == IS_VAR && UNEXPECTED(Z_ISERROR_P(var_ptr))) {
if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
ZVAL_NULL(EX_VAR(opline->result.var));
}
@@ -21563,15 +25006,52 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_ADD_SPEC_VAR_TMPVAR_HAN
USE_OPLINE
# if 0 || (IS_VAR != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
+ if (EXPECTED(1)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_VAR_TMPVAR(add_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_TMPVAR(add_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_TMPVAR(add_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_TMPVAR(add_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_ADD_SPEC_VAR_TMPVAR_DIM_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || ((IS_TMP_VAR|IS_VAR) != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_VAR != IS_UNUSED)
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_VAR_TMPVAR(add_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(1)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_TMPVAR(add_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_TMPVAR(add_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_TMPVAR(add_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_TMPVAR(add_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_ADD_SPEC_VAR_TMPVAR_OBJ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || ((IS_TMP_VAR|IS_VAR) != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_VAR != IS_UNUSED)
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_VAR_TMPVAR(add_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_TMPVAR(add_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_TMPVAR(add_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#else
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_TMPVAR(add_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#endif
@@ -21583,15 +25063,52 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_SUB_SPEC_VAR_TMPVAR_HAN
USE_OPLINE
# if 0 || (IS_VAR != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
+ if (EXPECTED(1)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_VAR_TMPVAR(sub_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_TMPVAR(sub_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_TMPVAR(sub_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_TMPVAR(sub_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_SUB_SPEC_VAR_TMPVAR_DIM_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || ((IS_TMP_VAR|IS_VAR) != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_VAR != IS_UNUSED)
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_VAR_TMPVAR(sub_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+ if (EXPECTED(1)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_TMPVAR(sub_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_TMPVAR(sub_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_TMPVAR(sub_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_SUB_SPEC_VAR_TMPVAR_OBJ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || ((IS_TMP_VAR|IS_VAR) != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_VAR != IS_UNUSED)
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_VAR_TMPVAR(sub_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_TMPVAR(sub_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_TMPVAR(sub_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_TMPVAR(sub_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#else
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_TMPVAR(sub_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#endif
@@ -21603,15 +25120,52 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_MUL_SPEC_VAR_TMPVAR_HAN
USE_OPLINE
# if 0 || (IS_VAR != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
+ if (EXPECTED(1)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_VAR_TMPVAR(mul_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_TMPVAR(mul_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_TMPVAR(mul_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_TMPVAR(mul_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_MUL_SPEC_VAR_TMPVAR_DIM_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || ((IS_TMP_VAR|IS_VAR) != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_VAR != IS_UNUSED)
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_VAR_TMPVAR(mul_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(1)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_TMPVAR(mul_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_TMPVAR(mul_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_TMPVAR(mul_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_TMPVAR(mul_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_MUL_SPEC_VAR_TMPVAR_OBJ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || ((IS_TMP_VAR|IS_VAR) != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_VAR != IS_UNUSED)
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_VAR_TMPVAR(mul_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_TMPVAR(mul_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_TMPVAR(mul_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#else
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_TMPVAR(mul_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#endif
@@ -21623,15 +25177,52 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIV_SPEC_VAR_TMPVAR_HAN
USE_OPLINE
# if 0 || (IS_VAR != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
+ if (EXPECTED(1)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_VAR_TMPVAR(div_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_TMPVAR(div_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_TMPVAR(div_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_TMPVAR(div_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIV_SPEC_VAR_TMPVAR_DIM_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || ((IS_TMP_VAR|IS_VAR) != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_VAR != IS_UNUSED)
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_VAR_TMPVAR(div_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+ if (EXPECTED(1)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_TMPVAR(div_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_TMPVAR(div_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_TMPVAR(div_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIV_SPEC_VAR_TMPVAR_OBJ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || ((IS_TMP_VAR|IS_VAR) != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_VAR != IS_UNUSED)
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_VAR_TMPVAR(div_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_TMPVAR(div_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_TMPVAR(div_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_TMPVAR(div_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#else
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_TMPVAR(div_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#endif
@@ -21643,15 +25234,52 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_MOD_SPEC_VAR_TMPVAR_HAN
USE_OPLINE
# if 0 || (IS_VAR != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
+ if (EXPECTED(1)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_VAR_TMPVAR(mod_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_TMPVAR(mod_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_TMPVAR(mod_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_TMPVAR(mod_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_MOD_SPEC_VAR_TMPVAR_DIM_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || ((IS_TMP_VAR|IS_VAR) != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_VAR != IS_UNUSED)
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_VAR_TMPVAR(mod_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(1)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_TMPVAR(mod_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_TMPVAR(mod_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_TMPVAR(mod_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_TMPVAR(mod_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_MOD_SPEC_VAR_TMPVAR_OBJ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || ((IS_TMP_VAR|IS_VAR) != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_VAR != IS_UNUSED)
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_VAR_TMPVAR(mod_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_TMPVAR(mod_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_TMPVAR(mod_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#else
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_TMPVAR(mod_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#endif
@@ -21663,15 +25291,52 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_SL_SPEC_VAR_TMPVAR_HAND
USE_OPLINE
# if 0 || (IS_VAR != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
+ if (EXPECTED(1)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_VAR_TMPVAR(shift_left_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_TMPVAR(shift_left_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_TMPVAR(shift_left_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_TMPVAR(shift_left_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_SL_SPEC_VAR_TMPVAR_DIM_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || ((IS_TMP_VAR|IS_VAR) != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_VAR != IS_UNUSED)
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_VAR_TMPVAR(shift_left_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(1)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_TMPVAR(shift_left_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_TMPVAR(shift_left_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_TMPVAR(shift_left_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_TMPVAR(shift_left_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_SL_SPEC_VAR_TMPVAR_OBJ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || ((IS_TMP_VAR|IS_VAR) != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_VAR != IS_UNUSED)
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_VAR_TMPVAR(shift_left_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_TMPVAR(shift_left_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_TMPVAR(shift_left_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#else
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_TMPVAR(shift_left_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#endif
@@ -21683,15 +25348,52 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_SR_SPEC_VAR_TMPVAR_HAND
USE_OPLINE
# if 0 || (IS_VAR != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
+ if (EXPECTED(1)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_VAR_TMPVAR(shift_right_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_TMPVAR(shift_right_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_TMPVAR(shift_right_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_TMPVAR(shift_right_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_SR_SPEC_VAR_TMPVAR_DIM_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || ((IS_TMP_VAR|IS_VAR) != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_VAR != IS_UNUSED)
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_VAR_TMPVAR(shift_right_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(1)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_TMPVAR(shift_right_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_TMPVAR(shift_right_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_TMPVAR(shift_right_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_TMPVAR(shift_right_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_SR_SPEC_VAR_TMPVAR_OBJ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || ((IS_TMP_VAR|IS_VAR) != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_VAR != IS_UNUSED)
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_VAR_TMPVAR(shift_right_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_TMPVAR(shift_right_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_TMPVAR(shift_right_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#else
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_TMPVAR(shift_right_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#endif
@@ -21703,15 +25405,52 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_CONCAT_SPEC_VAR_TMPVAR_
USE_OPLINE
# if 0 || (IS_VAR != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
+ if (EXPECTED(1)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_VAR_TMPVAR(concat_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_TMPVAR(concat_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_TMPVAR(concat_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_TMPVAR(concat_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_CONCAT_SPEC_VAR_TMPVAR_DIM_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || ((IS_TMP_VAR|IS_VAR) != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_VAR != IS_UNUSED)
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_VAR_TMPVAR(concat_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+ if (EXPECTED(1)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_TMPVAR(concat_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_TMPVAR(concat_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_TMPVAR(concat_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_CONCAT_SPEC_VAR_TMPVAR_OBJ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || ((IS_TMP_VAR|IS_VAR) != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_VAR != IS_UNUSED)
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_VAR_TMPVAR(concat_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_TMPVAR(concat_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_TMPVAR(concat_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_TMPVAR(concat_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#else
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_TMPVAR(concat_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#endif
@@ -21723,15 +25462,52 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_BW_OR_SPEC_VAR_TMPVAR_H
USE_OPLINE
# if 0 || (IS_VAR != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
+ if (EXPECTED(1)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_VAR_TMPVAR(bitwise_or_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_TMPVAR(bitwise_or_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_TMPVAR(bitwise_or_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_TMPVAR(bitwise_or_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_BW_OR_SPEC_VAR_TMPVAR_DIM_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || ((IS_TMP_VAR|IS_VAR) != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_VAR != IS_UNUSED)
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_VAR_TMPVAR(bitwise_or_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(1)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_TMPVAR(bitwise_or_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_TMPVAR(bitwise_or_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_TMPVAR(bitwise_or_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_TMPVAR(bitwise_or_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_BW_OR_SPEC_VAR_TMPVAR_OBJ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || ((IS_TMP_VAR|IS_VAR) != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_VAR != IS_UNUSED)
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_VAR_TMPVAR(bitwise_or_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_TMPVAR(bitwise_or_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_TMPVAR(bitwise_or_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#else
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_TMPVAR(bitwise_or_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#endif
@@ -21743,15 +25519,52 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_BW_AND_SPEC_VAR_TMPVAR_
USE_OPLINE
# if 0 || (IS_VAR != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
+ if (EXPECTED(1)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_VAR_TMPVAR(bitwise_and_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_TMPVAR(bitwise_and_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_TMPVAR(bitwise_and_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_TMPVAR(bitwise_and_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_BW_AND_SPEC_VAR_TMPVAR_DIM_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || ((IS_TMP_VAR|IS_VAR) != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_VAR != IS_UNUSED)
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_VAR_TMPVAR(bitwise_and_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(1)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_TMPVAR(bitwise_and_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_TMPVAR(bitwise_and_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_TMPVAR(bitwise_and_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_TMPVAR(bitwise_and_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_BW_AND_SPEC_VAR_TMPVAR_OBJ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || ((IS_TMP_VAR|IS_VAR) != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_VAR != IS_UNUSED)
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_VAR_TMPVAR(bitwise_and_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_TMPVAR(bitwise_and_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_TMPVAR(bitwise_and_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#else
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_TMPVAR(bitwise_and_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#endif
@@ -21763,15 +25576,52 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_BW_XOR_SPEC_VAR_TMPVAR_
USE_OPLINE
# if 0 || (IS_VAR != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
+ if (EXPECTED(1)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_VAR_TMPVAR(bitwise_xor_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_TMPVAR(bitwise_xor_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_TMPVAR(bitwise_xor_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_TMPVAR(bitwise_xor_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_BW_XOR_SPEC_VAR_TMPVAR_DIM_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || ((IS_TMP_VAR|IS_VAR) != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_VAR != IS_UNUSED)
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_VAR_TMPVAR(bitwise_xor_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(1)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_TMPVAR(bitwise_xor_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_TMPVAR(bitwise_xor_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_TMPVAR(bitwise_xor_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_TMPVAR(bitwise_xor_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_BW_XOR_SPEC_VAR_TMPVAR_OBJ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || ((IS_TMP_VAR|IS_VAR) != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_VAR != IS_UNUSED)
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_VAR_TMPVAR(bitwise_xor_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_TMPVAR(bitwise_xor_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_TMPVAR(bitwise_xor_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#else
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_TMPVAR(bitwise_xor_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#endif
@@ -21783,15 +25633,52 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_POW_SPEC_VAR_TMPVAR_HAN
USE_OPLINE
# if 0 || (IS_VAR != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
+ if (EXPECTED(1)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_VAR_TMPVAR(pow_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_TMPVAR(pow_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_TMPVAR(pow_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_TMPVAR(pow_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_POW_SPEC_VAR_TMPVAR_DIM_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || ((IS_TMP_VAR|IS_VAR) != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_VAR != IS_UNUSED)
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_VAR_TMPVAR(pow_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(1)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_TMPVAR(pow_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_TMPVAR(pow_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_TMPVAR(pow_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_POW_SPEC_VAR_TMPVAR_OBJ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || ((IS_TMP_VAR|IS_VAR) != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_VAR != IS_UNUSED)
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_VAR_TMPVAR(pow_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_TMPVAR(pow_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_TMPVAR(pow_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_VAR_TMPVAR(pow_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#else
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_VAR_TMPVAR(pow_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#endif
@@ -21808,7 +25695,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_pre_incdec_property_helper_SPE
SAVE_OPLINE();
object = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1);
- if (IS_VAR == IS_UNUSED && UNEXPECTED(Z_OBJ_P(object) == NULL)) {
+ if (IS_VAR == IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
zval_ptr_dtor_nogc(EX_VAR(opline->op2.var));
HANDLE_EXCEPTION();
@@ -21816,12 +25703,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_pre_incdec_property_helper_SPE
property = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2);
- if (IS_VAR == IS_VAR && UNEXPECTED(object == NULL)) {
- zend_throw_error(NULL, "Cannot increment/decrement overloaded objects nor string offsets");
- zval_ptr_dtor_nogc(free_op2);
- HANDLE_EXCEPTION();
- }
-
do {
if (IS_VAR != IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) != IS_OBJECT)) {
ZVAL_DEREF(object);
@@ -21837,7 +25718,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_pre_incdec_property_helper_SPE
/* here we are sure we are dealing with an object */
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)) {
- if (UNEXPECTED(zptr == &EG(error_zval))) {
+ if (UNEXPECTED(Z_ISERROR_P(zptr))) {
if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
ZVAL_NULL(EX_VAR(opline->result.var));
}
@@ -21893,7 +25774,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_post_incdec_property_helper_SP
SAVE_OPLINE();
object = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1);
- if (IS_VAR == IS_UNUSED && UNEXPECTED(Z_OBJ_P(object) == NULL)) {
+ if (IS_VAR == IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
zval_ptr_dtor_nogc(EX_VAR(opline->op2.var));
HANDLE_EXCEPTION();
@@ -21901,12 +25782,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_post_incdec_property_helper_SP
property = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2);
- if (IS_VAR == IS_VAR && UNEXPECTED(object == NULL)) {
- zend_throw_error(NULL, "Cannot increment/decrement overloaded objects nor string offsets");
- zval_ptr_dtor_nogc(free_op2);
- HANDLE_EXCEPTION();
- }
-
do {
if (IS_VAR != IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) != IS_OBJECT)) {
ZVAL_DEREF(object);
@@ -21921,7 +25796,7 @@ 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)) {
- if (UNEXPECTED(zptr == &EG(error_zval))) {
+ if (UNEXPECTED(Z_ISERROR_P(zptr))) {
ZVAL_NULL(EX_VAR(opline->result.var));
} else {
if (EXPECTED(Z_TYPE_P(zptr) == IS_LONG)) {
@@ -21971,14 +25846,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_DIM_W_SPEC_VAR_TMPVAR_HA
SAVE_OPLINE();
container = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1);
- if (IS_VAR == IS_VAR && UNEXPECTED(container == NULL)) {
- zend_throw_error(NULL, "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));
zval_ptr_dtor_nogc(free_op2);
if (IS_VAR == IS_VAR && READY_TO_DESTROY(free_op1)) {
- EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var), 1);
+ EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var));
}
if (UNEXPECTED(free_op1)) {zval_ptr_dtor_nogc(free_op1);};
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
@@ -21993,14 +25864,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_DIM_RW_SPEC_VAR_TMPVAR_H
SAVE_OPLINE();
container = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1);
- if (IS_VAR == IS_VAR && UNEXPECTED(container == NULL)) {
- zend_throw_error(NULL, "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));
zval_ptr_dtor_nogc(free_op2);
if (IS_VAR == IS_VAR && READY_TO_DESTROY(free_op1)) {
- EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var), 1);
+ EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var));
}
if (UNEXPECTED(free_op1)) {zval_ptr_dtor_nogc(free_op1);};
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
@@ -22015,21 +25882,16 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_DIM_FUNC_ARG_SPEC_VAR_TM
SAVE_OPLINE();
if (zend_is_by_ref_func_arg_fetch(opline, EX(call))) {
- if (IS_VAR == IS_CONST || IS_VAR == IS_TMP_VAR) {
+ if ((IS_VAR & (IS_CONST|IS_TMP_VAR))) {
zend_throw_error(NULL, "Cannot use temporary expression in write context");
zval_ptr_dtor_nogc(EX_VAR(opline->op2.var));
zval_ptr_dtor_nogc(EX_VAR(opline->op1.var));
HANDLE_EXCEPTION();
}
container = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1);
- if (IS_VAR == IS_VAR && UNEXPECTED(container == NULL)) {
- zend_throw_error(NULL, "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));
if (IS_VAR == IS_VAR && READY_TO_DESTROY(free_op1)) {
- EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var), 1);
+ EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var));
}
zval_ptr_dtor_nogc(free_op2);
if (UNEXPECTED(free_op1)) {zval_ptr_dtor_nogc(free_op1);};
@@ -22057,15 +25919,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_DIM_UNSET_SPEC_VAR_TMPVA
SAVE_OPLINE();
container = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1);
- if (IS_VAR == IS_VAR && UNEXPECTED(container == NULL)) {
- zend_throw_error(NULL, "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));
zval_ptr_dtor_nogc(free_op2);
if (IS_VAR == IS_VAR && READY_TO_DESTROY(free_op1)) {
- EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var), 1);
+ EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var));
}
if (UNEXPECTED(free_op1)) {zval_ptr_dtor_nogc(free_op1);};
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
@@ -22082,7 +25939,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_R_SPEC_VAR_TMPVAR_HA
SAVE_OPLINE();
container = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1);
- if (IS_VAR == IS_UNUSED && UNEXPECTED(Z_OBJ_P(container) == NULL)) {
+ if (IS_VAR == IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
zval_ptr_dtor_nogc(EX_VAR(opline->op2.var));
HANDLE_EXCEPTION();
@@ -22155,21 +26012,16 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_W_SPEC_VAR_TMPVAR_HA
property = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2);
container = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1);
- if (IS_VAR == IS_UNUSED && UNEXPECTED(Z_OBJ_P(container) == NULL)) {
+ if (IS_VAR == IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
zval_ptr_dtor_nogc(free_op2);
HANDLE_EXCEPTION();
}
- if (IS_VAR == IS_VAR && UNEXPECTED(container == NULL)) {
- zend_throw_error(NULL, "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_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);
zval_ptr_dtor_nogc(free_op2);
if (IS_VAR == IS_VAR && READY_TO_DESTROY(free_op1)) {
- EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var), 0);
+ EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var));
}
if (UNEXPECTED(free_op1)) {zval_ptr_dtor_nogc(free_op1);};
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
@@ -22186,20 +26038,15 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_RW_SPEC_VAR_TMPVAR_H
property = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2);
container = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1);
- if (IS_VAR == IS_UNUSED && UNEXPECTED(Z_OBJ_P(container) == NULL)) {
+ if (IS_VAR == IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
zval_ptr_dtor_nogc(free_op2);
HANDLE_EXCEPTION();
}
- if (IS_VAR == IS_VAR && UNEXPECTED(container == NULL)) {
- zend_throw_error(NULL, "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_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);
zval_ptr_dtor_nogc(free_op2);
if (IS_VAR == IS_VAR && READY_TO_DESTROY(free_op1)) {
- EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var), 0);
+ EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var));
}
if (UNEXPECTED(free_op1)) {zval_ptr_dtor_nogc(free_op1);};
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
@@ -22219,26 +26066,21 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_FUNC_ARG_SPEC_VAR_TM
property = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2);
container = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1);
- if (IS_VAR == IS_UNUSED && UNEXPECTED(Z_OBJ_P(container) == NULL)) {
+ if (IS_VAR == IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
zval_ptr_dtor_nogc(free_op2);
HANDLE_EXCEPTION();
}
- if (IS_VAR == IS_CONST || IS_VAR == IS_TMP_VAR) {
+ if ((IS_VAR & (IS_CONST|IS_TMP_VAR))) {
zend_throw_error(NULL, "Cannot use temporary expression in write context");
zval_ptr_dtor_nogc(free_op2);
if (UNEXPECTED(free_op1)) {zval_ptr_dtor_nogc(free_op1);};
HANDLE_EXCEPTION();
}
- if (IS_VAR == IS_VAR && UNEXPECTED(container == NULL)) {
- zend_throw_error(NULL, "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_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);
zval_ptr_dtor_nogc(free_op2);
if (IS_VAR == IS_VAR && READY_TO_DESTROY(free_op1)) {
- EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var), 0);
+ EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var));
}
if (UNEXPECTED(free_op1)) {zval_ptr_dtor_nogc(free_op1);};
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
@@ -22256,7 +26098,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_UNSET_SPEC_VAR_TMPVA
SAVE_OPLINE();
container = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1);
- if (IS_VAR == IS_UNUSED && UNEXPECTED(Z_OBJ_P(container) == NULL)) {
+ if (IS_VAR == IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
zval_ptr_dtor_nogc(EX_VAR(opline->op2.var));
HANDLE_EXCEPTION();
@@ -22264,56 +26106,645 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_UNSET_SPEC_VAR_TMPVA
property = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2);
- if (IS_VAR == IS_VAR && UNEXPECTED(container == NULL)) {
- zend_throw_error(NULL, "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_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);
zval_ptr_dtor_nogc(free_op2);
if (IS_VAR == IS_VAR && READY_TO_DESTROY(free_op1)) {
- EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var), 0);
+ EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var));
}
if (UNEXPECTED(free_op1)) {zval_ptr_dtor_nogc(free_op1);};
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_TMPVAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_TMPVAR_OP_DATA_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
zend_free_op free_op1, free_op2;
- zval *object;
- zval *property_name;
+ zval *object, *property_name, *value, tmp;
SAVE_OPLINE();
object = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1);
- if (IS_VAR == IS_UNUSED && UNEXPECTED(Z_OBJ_P(object) == NULL)) {
+ if (IS_VAR == IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
zval_ptr_dtor_nogc(EX_VAR(opline->op2.var));
HANDLE_EXCEPTION();
}
property_name = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2);
+ value = EX_CONSTANT((opline+1)->op1);
- if (IS_VAR == IS_VAR && UNEXPECTED(object == NULL)) {
- zend_throw_error(NULL, "Cannot use string offset as an array");
- zval_ptr_dtor_nogc(free_op2);
+ if (IS_VAR != IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) != IS_OBJECT)) {
+ do {
+ if (Z_ISREF_P(object)) {
+ object = Z_REFVAL_P(object);
+ if (EXPECTED(Z_TYPE_P(object) == IS_OBJECT)) {
+ break;
+ }
+ }
+ if (EXPECTED(Z_TYPE_P(object) <= IS_FALSE ||
+ (Z_TYPE_P(object) == IS_STRING && Z_STRLEN_P(object) == 0))) {
+ zend_object *obj;
+
+ zval_ptr_dtor(object);
+ object_init(object);
+ Z_ADDREF_P(object);
+ obj = Z_OBJ_P(object);
+ zend_error(E_WARNING, "Creating default object from empty value");
+ if (GC_REFCOUNT(obj) == 1) {
+ /* the enclosing container was deleted, obj is unreferenced */
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+
+ OBJ_RELEASE(obj);
+ goto exit_assign_obj;
+ }
+ Z_DELREF_P(object);
+ } else {
+ if (IS_VAR != IS_VAR || EXPECTED(!Z_ISERROR_P(object))) {
+ zend_error(E_WARNING, "Attempt to assign property of non-object");
+ }
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+
+ goto exit_assign_obj;
+ }
+ } while (0);
+ }
+
+ if ((IS_TMP_VAR|IS_VAR) == IS_CONST &&
+ EXPECTED(Z_OBJCE_P(object) == CACHED_PTR(Z_CACHE_SLOT_P(property_name)))) {
+ uint32_t prop_offset = (uint32_t)(intptr_t)CACHED_PTR(Z_CACHE_SLOT_P(property_name) + sizeof(void*));
+ zend_object *zobj = Z_OBJ_P(object);
+ zval *property;
+
+ if (EXPECTED(prop_offset != (uint32_t)ZEND_DYNAMIC_PROPERTY_OFFSET)) {
+ property = OBJ_PROP(zobj, prop_offset);
+ if (Z_TYPE_P(property) != IS_UNDEF) {
+fast_assign_obj:
+ value = zend_assign_to_variable(property, value, IS_CONST);
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+ goto exit_assign_obj;
+ }
+ } else {
+ if (EXPECTED(zobj->properties != NULL)) {
+ if (UNEXPECTED(GC_REFCOUNT(zobj->properties) > 1)) {
+ if (EXPECTED(!(GC_FLAGS(zobj->properties) & IS_ARRAY_IMMUTABLE))) {
+ GC_REFCOUNT(zobj->properties)--;
+ }
+ zobj->properties = zend_array_dup(zobj->properties);
+ }
+ property = zend_hash_find(zobj->properties, Z_STR_P(property_name));
+ if (property) {
+ goto fast_assign_obj;
+ }
+ }
+
+ if (!zobj->ce->__set) {
+
+ if (EXPECTED(zobj->properties == NULL)) {
+ rebuild_object_properties(zobj);
+ }
+ if (IS_CONST == IS_CONST) {
+ if (UNEXPECTED(Z_OPT_REFCOUNTED_P(value))) {
+ Z_ADDREF_P(value);
+ }
+ } else if (IS_CONST != IS_TMP_VAR) {
+ if (Z_ISREF_P(value)) {
+ if (IS_CONST == IS_VAR) {
+ zend_reference *ref = Z_REF_P(value);
+ if (--GC_REFCOUNT(ref) == 0) {
+ ZVAL_COPY_VALUE(&tmp, Z_REFVAL_P(value));
+ efree_size(ref, sizeof(zend_reference));
+ value = &tmp;
+ } else {
+ value = Z_REFVAL_P(value);
+ if (Z_REFCOUNTED_P(value)) {
+ Z_ADDREF_P(value);
+ }
+ }
+ } else {
+ value = Z_REFVAL_P(value);
+ if (Z_REFCOUNTED_P(value)) {
+ Z_ADDREF_P(value);
+ }
+ }
+ } else if (IS_CONST == IS_CV && Z_REFCOUNTED_P(value)) {
+ Z_ADDREF_P(value);
+ }
+ }
+ zend_hash_add_new(zobj->properties, Z_STR_P(property_name), value);
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+ goto exit_assign_obj;
+ }
+ }
+ }
+
+ if (!Z_OBJ_HT_P(object)->write_property) {
+ zend_error(E_WARNING, "Attempt to assign property of non-object");
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+
+ goto exit_assign_obj;
+ }
+
+ if (IS_CONST == IS_CV || IS_CONST == IS_VAR) {
+ ZVAL_DEREF(value);
+ }
+
+ Z_OBJ_HT_P(object)->write_property(object, property_name, value, ((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property_name)) : NULL);
+
+ if (UNEXPECTED(RETURN_VALUE_USED(opline)) && EXPECTED(!EG(exception))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+
+exit_assign_obj:
+ zval_ptr_dtor_nogc(free_op2);
+ if (UNEXPECTED(free_op1)) {zval_ptr_dtor_nogc(free_op1);};
+ /* assign_obj has two opcodes! */
+ ZEND_VM_NEXT_OPCODE_EX(1, 2);
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_TMPVAR_OP_DATA_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zend_free_op free_op1, free_op2, free_op_data;
+ zval *object, *property_name, *value, tmp;
+
+ SAVE_OPLINE();
+ object = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1);
+
+ if (IS_VAR == IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) == IS_UNDEF)) {
+ zend_throw_error(NULL, "Using $this when not in object context");
+ zval_ptr_dtor_nogc(EX_VAR(opline->op2.var));
+ HANDLE_EXCEPTION();
+ }
+
+ property_name = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2);
+ value = _get_zval_ptr_tmp((opline+1)->op1.var, execute_data, &free_op_data);
+
+ if (IS_VAR != IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) != IS_OBJECT)) {
+ do {
+ if (Z_ISREF_P(object)) {
+ object = Z_REFVAL_P(object);
+ if (EXPECTED(Z_TYPE_P(object) == IS_OBJECT)) {
+ break;
+ }
+ }
+ if (EXPECTED(Z_TYPE_P(object) <= IS_FALSE ||
+ (Z_TYPE_P(object) == IS_STRING && Z_STRLEN_P(object) == 0))) {
+ zend_object *obj;
+
+ zval_ptr_dtor(object);
+ object_init(object);
+ Z_ADDREF_P(object);
+ obj = Z_OBJ_P(object);
+ zend_error(E_WARNING, "Creating default object from empty value");
+ if (GC_REFCOUNT(obj) == 1) {
+ /* the enclosing container was deleted, obj is unreferenced */
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+ zval_ptr_dtor_nogc(free_op_data);
+ OBJ_RELEASE(obj);
+ goto exit_assign_obj;
+ }
+ Z_DELREF_P(object);
+ } else {
+ if (IS_VAR != IS_VAR || EXPECTED(!Z_ISERROR_P(object))) {
+ zend_error(E_WARNING, "Attempt to assign property of non-object");
+ }
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+ zval_ptr_dtor_nogc(free_op_data);
+ goto exit_assign_obj;
+ }
+ } while (0);
+ }
+
+ if ((IS_TMP_VAR|IS_VAR) == IS_CONST &&
+ EXPECTED(Z_OBJCE_P(object) == CACHED_PTR(Z_CACHE_SLOT_P(property_name)))) {
+ uint32_t prop_offset = (uint32_t)(intptr_t)CACHED_PTR(Z_CACHE_SLOT_P(property_name) + sizeof(void*));
+ zend_object *zobj = Z_OBJ_P(object);
+ zval *property;
+
+ if (EXPECTED(prop_offset != (uint32_t)ZEND_DYNAMIC_PROPERTY_OFFSET)) {
+ property = OBJ_PROP(zobj, prop_offset);
+ if (Z_TYPE_P(property) != IS_UNDEF) {
+fast_assign_obj:
+ value = zend_assign_to_variable(property, value, IS_TMP_VAR);
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+ goto exit_assign_obj;
+ }
+ } else {
+ if (EXPECTED(zobj->properties != NULL)) {
+ if (UNEXPECTED(GC_REFCOUNT(zobj->properties) > 1)) {
+ if (EXPECTED(!(GC_FLAGS(zobj->properties) & IS_ARRAY_IMMUTABLE))) {
+ GC_REFCOUNT(zobj->properties)--;
+ }
+ zobj->properties = zend_array_dup(zobj->properties);
+ }
+ property = zend_hash_find(zobj->properties, Z_STR_P(property_name));
+ if (property) {
+ goto fast_assign_obj;
+ }
+ }
+
+ if (!zobj->ce->__set) {
+
+ if (EXPECTED(zobj->properties == NULL)) {
+ rebuild_object_properties(zobj);
+ }
+ if (IS_TMP_VAR == IS_CONST) {
+ if (UNEXPECTED(Z_OPT_REFCOUNTED_P(value))) {
+ Z_ADDREF_P(value);
+ }
+ } else if (IS_TMP_VAR != IS_TMP_VAR) {
+ if (Z_ISREF_P(value)) {
+ if (IS_TMP_VAR == IS_VAR) {
+ zend_reference *ref = Z_REF_P(value);
+ if (--GC_REFCOUNT(ref) == 0) {
+ ZVAL_COPY_VALUE(&tmp, Z_REFVAL_P(value));
+ efree_size(ref, sizeof(zend_reference));
+ value = &tmp;
+ } else {
+ value = Z_REFVAL_P(value);
+ if (Z_REFCOUNTED_P(value)) {
+ Z_ADDREF_P(value);
+ }
+ }
+ } else {
+ value = Z_REFVAL_P(value);
+ if (Z_REFCOUNTED_P(value)) {
+ Z_ADDREF_P(value);
+ }
+ }
+ } else if (IS_TMP_VAR == IS_CV && Z_REFCOUNTED_P(value)) {
+ Z_ADDREF_P(value);
+ }
+ }
+ zend_hash_add_new(zobj->properties, Z_STR_P(property_name), value);
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+ goto exit_assign_obj;
+ }
+ }
+ }
+
+ if (!Z_OBJ_HT_P(object)->write_property) {
+ zend_error(E_WARNING, "Attempt to assign property of non-object");
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+ zval_ptr_dtor_nogc(free_op_data);
+ goto exit_assign_obj;
+ }
+
+ if (IS_TMP_VAR == IS_CV || IS_TMP_VAR == IS_VAR) {
+ ZVAL_DEREF(value);
+ }
+
+ Z_OBJ_HT_P(object)->write_property(object, property_name, value, ((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property_name)) : NULL);
+
+ if (UNEXPECTED(RETURN_VALUE_USED(opline)) && EXPECTED(!EG(exception))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+ zval_ptr_dtor_nogc(free_op_data);
+exit_assign_obj:
+ zval_ptr_dtor_nogc(free_op2);
+ if (UNEXPECTED(free_op1)) {zval_ptr_dtor_nogc(free_op1);};
+ /* assign_obj has two opcodes! */
+ ZEND_VM_NEXT_OPCODE_EX(1, 2);
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_TMPVAR_OP_DATA_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zend_free_op free_op1, free_op2, free_op_data;
+ zval *object, *property_name, *value, tmp;
+
+ SAVE_OPLINE();
+ object = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1);
+
+ if (IS_VAR == IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) == IS_UNDEF)) {
+ zend_throw_error(NULL, "Using $this when not in object context");
+ zval_ptr_dtor_nogc(EX_VAR(opline->op2.var));
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));
+
+ property_name = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2);
+ value = _get_zval_ptr_var((opline+1)->op1.var, execute_data, &free_op_data);
+
+ if (IS_VAR != IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) != IS_OBJECT)) {
+ do {
+ if (Z_ISREF_P(object)) {
+ object = Z_REFVAL_P(object);
+ if (EXPECTED(Z_TYPE_P(object) == IS_OBJECT)) {
+ break;
+ }
+ }
+ if (EXPECTED(Z_TYPE_P(object) <= IS_FALSE ||
+ (Z_TYPE_P(object) == IS_STRING && Z_STRLEN_P(object) == 0))) {
+ zend_object *obj;
+
+ zval_ptr_dtor(object);
+ object_init(object);
+ Z_ADDREF_P(object);
+ obj = Z_OBJ_P(object);
+ zend_error(E_WARNING, "Creating default object from empty value");
+ if (GC_REFCOUNT(obj) == 1) {
+ /* the enclosing container was deleted, obj is unreferenced */
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+ zval_ptr_dtor_nogc(free_op_data);
+ OBJ_RELEASE(obj);
+ goto exit_assign_obj;
+ }
+ Z_DELREF_P(object);
+ } else {
+ if (IS_VAR != IS_VAR || EXPECTED(!Z_ISERROR_P(object))) {
+ zend_error(E_WARNING, "Attempt to assign property of non-object");
+ }
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+ zval_ptr_dtor_nogc(free_op_data);
+ goto exit_assign_obj;
+ }
+ } while (0);
+ }
+
+ if ((IS_TMP_VAR|IS_VAR) == IS_CONST &&
+ EXPECTED(Z_OBJCE_P(object) == CACHED_PTR(Z_CACHE_SLOT_P(property_name)))) {
+ uint32_t prop_offset = (uint32_t)(intptr_t)CACHED_PTR(Z_CACHE_SLOT_P(property_name) + sizeof(void*));
+ zend_object *zobj = Z_OBJ_P(object);
+ zval *property;
+
+ if (EXPECTED(prop_offset != (uint32_t)ZEND_DYNAMIC_PROPERTY_OFFSET)) {
+ property = OBJ_PROP(zobj, prop_offset);
+ if (Z_TYPE_P(property) != IS_UNDEF) {
+fast_assign_obj:
+ value = zend_assign_to_variable(property, value, IS_VAR);
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+ goto exit_assign_obj;
+ }
+ } else {
+ if (EXPECTED(zobj->properties != NULL)) {
+ if (UNEXPECTED(GC_REFCOUNT(zobj->properties) > 1)) {
+ if (EXPECTED(!(GC_FLAGS(zobj->properties) & IS_ARRAY_IMMUTABLE))) {
+ GC_REFCOUNT(zobj->properties)--;
+ }
+ zobj->properties = zend_array_dup(zobj->properties);
+ }
+ property = zend_hash_find(zobj->properties, Z_STR_P(property_name));
+ if (property) {
+ goto fast_assign_obj;
+ }
+ }
+
+ if (!zobj->ce->__set) {
+
+ if (EXPECTED(zobj->properties == NULL)) {
+ rebuild_object_properties(zobj);
+ }
+ if (IS_VAR == IS_CONST) {
+ if (UNEXPECTED(Z_OPT_REFCOUNTED_P(value))) {
+ Z_ADDREF_P(value);
+ }
+ } else if (IS_VAR != IS_TMP_VAR) {
+ if (Z_ISREF_P(value)) {
+ if (IS_VAR == IS_VAR) {
+ zend_reference *ref = Z_REF_P(value);
+ if (--GC_REFCOUNT(ref) == 0) {
+ ZVAL_COPY_VALUE(&tmp, Z_REFVAL_P(value));
+ efree_size(ref, sizeof(zend_reference));
+ value = &tmp;
+ } else {
+ value = Z_REFVAL_P(value);
+ if (Z_REFCOUNTED_P(value)) {
+ Z_ADDREF_P(value);
+ }
+ }
+ } else {
+ value = Z_REFVAL_P(value);
+ if (Z_REFCOUNTED_P(value)) {
+ Z_ADDREF_P(value);
+ }
+ }
+ } else if (IS_VAR == IS_CV && Z_REFCOUNTED_P(value)) {
+ Z_ADDREF_P(value);
+ }
+ }
+ zend_hash_add_new(zobj->properties, Z_STR_P(property_name), value);
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+ goto exit_assign_obj;
+ }
+ }
+ }
+
+ if (!Z_OBJ_HT_P(object)->write_property) {
+ zend_error(E_WARNING, "Attempt to assign property of non-object");
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+ zval_ptr_dtor_nogc(free_op_data);
+ goto exit_assign_obj;
+ }
+
+ if (IS_VAR == IS_CV || IS_VAR == IS_VAR) {
+ ZVAL_DEREF(value);
+ }
+
+ Z_OBJ_HT_P(object)->write_property(object, property_name, value, ((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property_name)) : NULL);
+
+ if (UNEXPECTED(RETURN_VALUE_USED(opline)) && EXPECTED(!EG(exception))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+ zval_ptr_dtor_nogc(free_op_data);
+exit_assign_obj:
zval_ptr_dtor_nogc(free_op2);
if (UNEXPECTED(free_op1)) {zval_ptr_dtor_nogc(free_op1);};
/* assign_obj has two opcodes! */
ZEND_VM_NEXT_OPCODE_EX(1, 2);
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_VAR_TMPVAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_TMPVAR_OP_DATA_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zend_free_op free_op1, free_op2;
+ zval *object, *property_name, *value, tmp;
+
+ SAVE_OPLINE();
+ object = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1);
+
+ if (IS_VAR == IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) == IS_UNDEF)) {
+ zend_throw_error(NULL, "Using $this when not in object context");
+ zval_ptr_dtor_nogc(EX_VAR(opline->op2.var));
+ HANDLE_EXCEPTION();
+ }
+
+ property_name = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2);
+ value = _get_zval_ptr_cv_BP_VAR_R(execute_data, (opline+1)->op1.var);
+
+ if (IS_VAR != IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) != IS_OBJECT)) {
+ do {
+ if (Z_ISREF_P(object)) {
+ object = Z_REFVAL_P(object);
+ if (EXPECTED(Z_TYPE_P(object) == IS_OBJECT)) {
+ break;
+ }
+ }
+ if (EXPECTED(Z_TYPE_P(object) <= IS_FALSE ||
+ (Z_TYPE_P(object) == IS_STRING && Z_STRLEN_P(object) == 0))) {
+ zend_object *obj;
+
+ zval_ptr_dtor(object);
+ object_init(object);
+ Z_ADDREF_P(object);
+ obj = Z_OBJ_P(object);
+ zend_error(E_WARNING, "Creating default object from empty value");
+ if (GC_REFCOUNT(obj) == 1) {
+ /* the enclosing container was deleted, obj is unreferenced */
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+
+ OBJ_RELEASE(obj);
+ goto exit_assign_obj;
+ }
+ Z_DELREF_P(object);
+ } else {
+ if (IS_VAR != IS_VAR || EXPECTED(!Z_ISERROR_P(object))) {
+ zend_error(E_WARNING, "Attempt to assign property of non-object");
+ }
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+
+ goto exit_assign_obj;
+ }
+ } while (0);
+ }
+
+ if ((IS_TMP_VAR|IS_VAR) == IS_CONST &&
+ EXPECTED(Z_OBJCE_P(object) == CACHED_PTR(Z_CACHE_SLOT_P(property_name)))) {
+ uint32_t prop_offset = (uint32_t)(intptr_t)CACHED_PTR(Z_CACHE_SLOT_P(property_name) + sizeof(void*));
+ zend_object *zobj = Z_OBJ_P(object);
+ zval *property;
+
+ if (EXPECTED(prop_offset != (uint32_t)ZEND_DYNAMIC_PROPERTY_OFFSET)) {
+ property = OBJ_PROP(zobj, prop_offset);
+ if (Z_TYPE_P(property) != IS_UNDEF) {
+fast_assign_obj:
+ value = zend_assign_to_variable(property, value, IS_CV);
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+ goto exit_assign_obj;
+ }
+ } else {
+ if (EXPECTED(zobj->properties != NULL)) {
+ if (UNEXPECTED(GC_REFCOUNT(zobj->properties) > 1)) {
+ if (EXPECTED(!(GC_FLAGS(zobj->properties) & IS_ARRAY_IMMUTABLE))) {
+ GC_REFCOUNT(zobj->properties)--;
+ }
+ zobj->properties = zend_array_dup(zobj->properties);
+ }
+ property = zend_hash_find(zobj->properties, Z_STR_P(property_name));
+ if (property) {
+ goto fast_assign_obj;
+ }
+ }
+
+ if (!zobj->ce->__set) {
+
+ if (EXPECTED(zobj->properties == NULL)) {
+ rebuild_object_properties(zobj);
+ }
+ if (IS_CV == IS_CONST) {
+ if (UNEXPECTED(Z_OPT_REFCOUNTED_P(value))) {
+ Z_ADDREF_P(value);
+ }
+ } else if (IS_CV != IS_TMP_VAR) {
+ if (Z_ISREF_P(value)) {
+ if (IS_CV == IS_VAR) {
+ zend_reference *ref = Z_REF_P(value);
+ if (--GC_REFCOUNT(ref) == 0) {
+ ZVAL_COPY_VALUE(&tmp, Z_REFVAL_P(value));
+ efree_size(ref, sizeof(zend_reference));
+ value = &tmp;
+ } else {
+ value = Z_REFVAL_P(value);
+ if (Z_REFCOUNTED_P(value)) {
+ Z_ADDREF_P(value);
+ }
+ }
+ } else {
+ value = Z_REFVAL_P(value);
+ if (Z_REFCOUNTED_P(value)) {
+ Z_ADDREF_P(value);
+ }
+ }
+ } else if (IS_CV == IS_CV && Z_REFCOUNTED_P(value)) {
+ Z_ADDREF_P(value);
+ }
+ }
+ zend_hash_add_new(zobj->properties, Z_STR_P(property_name), value);
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+ goto exit_assign_obj;
+ }
+ }
+ }
+
+ if (!Z_OBJ_HT_P(object)->write_property) {
+ zend_error(E_WARNING, "Attempt to assign property of non-object");
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+
+ goto exit_assign_obj;
+ }
+
+ if (IS_CV == IS_CV || IS_CV == IS_VAR) {
+ ZVAL_DEREF(value);
+ }
+
+ Z_OBJ_HT_P(object)->write_property(object, property_name, value, ((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property_name)) : NULL);
+
+ if (UNEXPECTED(RETURN_VALUE_USED(opline)) && EXPECTED(!EG(exception))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+
+exit_assign_obj:
+ zval_ptr_dtor_nogc(free_op2);
+ if (UNEXPECTED(free_op1)) {zval_ptr_dtor_nogc(free_op1);};
+ /* assign_obj has two opcodes! */
+ ZEND_VM_NEXT_OPCODE_EX(1, 2);
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_VAR_TMPVAR_OP_DATA_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
zend_free_op free_op1;
zval *object_ptr;
- zend_free_op free_op2, free_op_data1;
+ zend_free_op free_op2;
zval *value;
zval *variable_ptr;
zval *dim;
@@ -22321,40 +26752,214 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_VAR_TMPVAR_HAN
SAVE_OPLINE();
object_ptr = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1);
- if (IS_VAR == IS_VAR && UNEXPECTED(object_ptr == NULL)) {
- zend_throw_error(NULL, "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();
+ if (EXPECTED(Z_TYPE_P(object_ptr) == IS_ARRAY)) {
+try_assign_dim_array:
+ SEPARATE_ARRAY(object_ptr);
+ if ((IS_TMP_VAR|IS_VAR) == IS_UNUSED) {
+ variable_ptr = zend_hash_next_index_insert(Z_ARRVAL_P(object_ptr), &EG(uninitialized_zval));
+ if (UNEXPECTED(variable_ptr == NULL)) {
+ zend_error(E_WARNING, "Cannot add element to the array as the next element is already occupied");
+ goto assign_dim_error;
+ }
+ } else {
+ dim = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2);
+ if ((IS_TMP_VAR|IS_VAR) == IS_CONST) {
+ variable_ptr = zend_fetch_dimension_address_inner_W_CONST(Z_ARRVAL_P(object_ptr), dim);
+ } else {
+ variable_ptr = zend_fetch_dimension_address_inner_W(Z_ARRVAL_P(object_ptr), dim);
+ }
+ if (UNEXPECTED(variable_ptr == NULL)) {
+ goto assign_dim_error;
+ }
+ }
+ value = EX_CONSTANT((opline+1)->op1);
+ value = zend_assign_to_variable(variable_ptr, value, IS_CONST);
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+ } 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)) {
+ dim = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2);
+ value = EX_CONSTANT((opline+1)->op1);
+
+ zend_assign_to_object_dim(object_ptr, dim, value);
+
+ if (UNEXPECTED(RETURN_VALUE_USED(opline)) && EXPECTED(!EG(exception))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+
+ } else if (EXPECTED(Z_TYPE_P(object_ptr) == IS_STRING)) {
+ if ((IS_TMP_VAR|IS_VAR) == IS_UNUSED) {
+ zend_throw_error(NULL, "[] operator not supported for strings");
+
+ if (UNEXPECTED(free_op1)) {zval_ptr_dtor_nogc(free_op1);};
+ HANDLE_EXCEPTION();
+ } else {
+ dim = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2);
+ value = EX_CONSTANT((opline+1)->op1);
+ zend_assign_to_string_offset(object_ptr, dim, value, (UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL));
+
+ }
+ } else if (EXPECTED(Z_TYPE_P(object_ptr) <= IS_FALSE)) {
+ 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 (IS_VAR != IS_VAR || EXPECTED(!Z_ISERROR_P(object_ptr))) {
+ zend_error(E_WARNING, "Cannot use a scalar value as an array");
+ }
+ dim = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2);
+assign_dim_error:
+
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+ }
}
+ if ((IS_TMP_VAR|IS_VAR) != IS_UNUSED) {
+ zval_ptr_dtor_nogc(free_op2);
+ }
+ if (UNEXPECTED(free_op1)) {zval_ptr_dtor_nogc(free_op1);};
+ /* assign_dim has two opcodes! */
+ ZEND_VM_NEXT_OPCODE_EX(1, 2);
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_VAR_TMPVAR_OP_DATA_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zend_free_op free_op1;
+ zval *object_ptr;
+ zend_free_op free_op2, free_op_data;
+ zval *value;
+ zval *variable_ptr;
+ zval *dim;
+
+ SAVE_OPLINE();
+ object_ptr = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1);
if (EXPECTED(Z_TYPE_P(object_ptr) == IS_ARRAY)) {
try_assign_dim_array:
+ SEPARATE_ARRAY(object_ptr);
if ((IS_TMP_VAR|IS_VAR) == IS_UNUSED) {
- SEPARATE_ARRAY(object_ptr);
variable_ptr = zend_hash_next_index_insert(Z_ARRVAL_P(object_ptr), &EG(uninitialized_zval));
if (UNEXPECTED(variable_ptr == NULL)) {
zend_error(E_WARNING, "Cannot add element to the array as the next element is already occupied");
- variable_ptr = &EG(error_zval);
+ goto assign_dim_error;
}
} else {
dim = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2);
- SEPARATE_ARRAY(object_ptr);
- variable_ptr = zend_fetch_dimension_address_inner(Z_ARRVAL_P(object_ptr), dim, (IS_TMP_VAR|IS_VAR), BP_VAR_W);
- zval_ptr_dtor_nogc(free_op2);
+ if ((IS_TMP_VAR|IS_VAR) == IS_CONST) {
+ variable_ptr = zend_fetch_dimension_address_inner_W_CONST(Z_ARRVAL_P(object_ptr), dim);
+ } else {
+ variable_ptr = zend_fetch_dimension_address_inner_W(Z_ARRVAL_P(object_ptr), dim);
+ }
+ if (UNEXPECTED(variable_ptr == NULL)) {
+ goto assign_dim_error;
+ }
}
- value = get_zval_ptr_r((opline+1)->op1_type, (opline+1)->op1, execute_data, &free_op_data1);
- if (UNEXPECTED(variable_ptr == &EG(error_zval))) {
- FREE_OP(free_op_data1);
+ value = _get_zval_ptr_tmp((opline+1)->op1.var, execute_data, &free_op_data);
+ value = zend_assign_to_variable(variable_ptr, value, IS_TMP_VAR);
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+ } 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)) {
+ dim = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2);
+ value = _get_zval_ptr_tmp((opline+1)->op1.var, execute_data, &free_op_data);
+
+ zend_assign_to_object_dim(object_ptr, dim, value);
+
+ if (UNEXPECTED(RETURN_VALUE_USED(opline)) && EXPECTED(!EG(exception))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+
+ zval_ptr_dtor_nogc(free_op_data);
+ } else if (EXPECTED(Z_TYPE_P(object_ptr) == IS_STRING)) {
+ if ((IS_TMP_VAR|IS_VAR) == IS_UNUSED) {
+ zend_throw_error(NULL, "[] operator not supported for strings");
+ zval_ptr_dtor_nogc(EX_VAR((opline+1)->op1.var));
+ if (UNEXPECTED(free_op1)) {zval_ptr_dtor_nogc(free_op1);};
+ HANDLE_EXCEPTION();
+ } else {
+ dim = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2);
+ value = _get_zval_ptr_tmp((opline+1)->op1.var, execute_data, &free_op_data);
+ zend_assign_to_string_offset(object_ptr, dim, value, (UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL));
+ zval_ptr_dtor_nogc(free_op_data);
+ }
+ } else if (EXPECTED(Z_TYPE_P(object_ptr) <= IS_FALSE)) {
+ 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 (IS_VAR != IS_VAR || EXPECTED(!Z_ISERROR_P(object_ptr))) {
+ zend_error(E_WARNING, "Cannot use a scalar value as an array");
+ }
+ dim = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2);
+assign_dim_error:
+ zval_ptr_dtor_nogc(EX_VAR((opline+1)->op1.var));
if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
ZVAL_NULL(EX_VAR(opline->result.var));
}
+ }
+ }
+ if ((IS_TMP_VAR|IS_VAR) != IS_UNUSED) {
+ zval_ptr_dtor_nogc(free_op2);
+ }
+ if (UNEXPECTED(free_op1)) {zval_ptr_dtor_nogc(free_op1);};
+ /* assign_dim has two opcodes! */
+ ZEND_VM_NEXT_OPCODE_EX(1, 2);
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_VAR_TMPVAR_OP_DATA_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zend_free_op free_op1;
+ zval *object_ptr;
+ zend_free_op free_op2, free_op_data;
+ zval *value;
+ zval *variable_ptr;
+ zval *dim;
+
+ SAVE_OPLINE();
+ object_ptr = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1);
+
+ if (EXPECTED(Z_TYPE_P(object_ptr) == IS_ARRAY)) {
+try_assign_dim_array:
+ SEPARATE_ARRAY(object_ptr);
+ if ((IS_TMP_VAR|IS_VAR) == IS_UNUSED) {
+ variable_ptr = zend_hash_next_index_insert(Z_ARRVAL_P(object_ptr), &EG(uninitialized_zval));
+ if (UNEXPECTED(variable_ptr == NULL)) {
+ zend_error(E_WARNING, "Cannot add element to the array as the next element is already occupied");
+ goto assign_dim_error;
+ }
} else {
- value = zend_assign_to_variable(variable_ptr, value, (opline+1)->op1_type);
- if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
- ZVAL_COPY(EX_VAR(opline->result.var), value);
+ dim = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2);
+ if ((IS_TMP_VAR|IS_VAR) == IS_CONST) {
+ variable_ptr = zend_fetch_dimension_address_inner_W_CONST(Z_ARRVAL_P(object_ptr), dim);
+ } else {
+ variable_ptr = zend_fetch_dimension_address_inner_W(Z_ARRVAL_P(object_ptr), dim);
+ }
+ if (UNEXPECTED(variable_ptr == NULL)) {
+ goto assign_dim_error;
}
}
+ value = _get_zval_ptr_var((opline+1)->op1.var, execute_data, &free_op_data);
+ value = zend_assign_to_variable(variable_ptr, value, IS_VAR);
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
} else {
if (EXPECTED(Z_ISREF_P(object_ptr))) {
object_ptr = Z_REFVAL_P(object_ptr);
@@ -22363,52 +26968,138 @@ 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);
+ dim = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2);
+ value = _get_zval_ptr_var((opline+1)->op1.var, execute_data, &free_op_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);
- zval_ptr_dtor_nogc(free_op2);
+ zend_assign_to_object_dim(object_ptr, dim, value);
+
+ if (UNEXPECTED(RETURN_VALUE_USED(opline)) && EXPECTED(!EG(exception))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+
+ zval_ptr_dtor_nogc(free_op_data);
} 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_throw_error(NULL, "[] operator not supported for strings");
- FREE_UNFETCHED_OP((opline+1)->op1_type, (opline+1)->op1.var);
- if (UNEXPECTED(free_op1)) {zval_ptr_dtor_nogc(free_op1);};
- HANDLE_EXCEPTION();
- } else {
- zend_long offset;
+ if ((IS_TMP_VAR|IS_VAR) == IS_UNUSED) {
+ zend_throw_error(NULL, "[] operator not supported for strings");
+ zval_ptr_dtor_nogc(EX_VAR((opline+1)->op1.var));
+ if (UNEXPECTED(free_op1)) {zval_ptr_dtor_nogc(free_op1);};
+ HANDLE_EXCEPTION();
+ } else {
+ dim = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2);
+ value = _get_zval_ptr_var_deref((opline+1)->op1.var, execute_data, &free_op_data);
+ zend_assign_to_string_offset(object_ptr, dim, value, (UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL));
+ zval_ptr_dtor_nogc(free_op_data);
+ }
+ } else if (EXPECTED(Z_TYPE_P(object_ptr) <= IS_FALSE)) {
+ 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 (IS_VAR != IS_VAR || EXPECTED(!Z_ISERROR_P(object_ptr))) {
+ zend_error(E_WARNING, "Cannot use a scalar value as an array");
+ }
+ dim = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2);
+assign_dim_error:
+ zval_ptr_dtor_nogc(EX_VAR((opline+1)->op1.var));
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+ }
+ }
+ if ((IS_TMP_VAR|IS_VAR) != IS_UNUSED) {
+ zval_ptr_dtor_nogc(free_op2);
+ }
+ if (UNEXPECTED(free_op1)) {zval_ptr_dtor_nogc(free_op1);};
+ /* assign_dim has two opcodes! */
+ ZEND_VM_NEXT_OPCODE_EX(1, 2);
+}
- 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_r_deref((opline+1)->op1_type, (opline+1)->op1, execute_data, &free_op_data1);
- 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);
- }
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_VAR_TMPVAR_OP_DATA_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zend_free_op free_op1;
+ zval *object_ptr;
+ zend_free_op free_op2;
+ zval *value;
+ zval *variable_ptr;
+ zval *dim;
+
+ SAVE_OPLINE();
+ object_ptr = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1);
+
+ if (EXPECTED(Z_TYPE_P(object_ptr) == IS_ARRAY)) {
+try_assign_dim_array:
+ SEPARATE_ARRAY(object_ptr);
+ if ((IS_TMP_VAR|IS_VAR) == IS_UNUSED) {
+ variable_ptr = zend_hash_next_index_insert(Z_ARRVAL_P(object_ptr), &EG(uninitialized_zval));
+ if (UNEXPECTED(variable_ptr == NULL)) {
+ zend_error(E_WARNING, "Cannot add element to the array as the next element is already occupied");
+ goto assign_dim_error;
+ }
+ } else {
+ dim = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2);
+ if ((IS_TMP_VAR|IS_VAR) == IS_CONST) {
+ variable_ptr = zend_fetch_dimension_address_inner_W_CONST(Z_ARRVAL_P(object_ptr), dim);
} 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);
+ variable_ptr = zend_fetch_dimension_address_inner_W(Z_ARRVAL_P(object_ptr), dim);
+ }
+ if (UNEXPECTED(variable_ptr == NULL)) {
+ goto assign_dim_error;
+ }
+ }
+ value = _get_zval_ptr_cv_BP_VAR_R(execute_data, (opline+1)->op1.var);
+ value = zend_assign_to_variable(variable_ptr, value, IS_CV);
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+ } 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;
}
- } 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;
+ }
+ if (EXPECTED(Z_TYPE_P(object_ptr) == IS_OBJECT)) {
+ dim = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2);
+ value = _get_zval_ptr_cv_BP_VAR_R(execute_data, (opline+1)->op1.var);
+
+ zend_assign_to_object_dim(object_ptr, dim, value);
+
+ if (UNEXPECTED(RETURN_VALUE_USED(opline)) && EXPECTED(!EG(exception))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+
+ } else if (EXPECTED(Z_TYPE_P(object_ptr) == IS_STRING)) {
+ if ((IS_TMP_VAR|IS_VAR) == IS_UNUSED) {
+ zend_throw_error(NULL, "[] operator not supported for strings");
+
+ if (UNEXPECTED(free_op1)) {zval_ptr_dtor_nogc(free_op1);};
+ HANDLE_EXCEPTION();
+ } else {
+ dim = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2);
+ value = _get_zval_ptr_cv_deref_BP_VAR_R(execute_data, (opline+1)->op1.var);
+ zend_assign_to_string_offset(object_ptr, dim, value, (UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL));
+
}
- goto assign_dim_convert_to_array;
+ } else if (EXPECTED(Z_TYPE_P(object_ptr) <= IS_FALSE)) {
+ 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 {
- zend_error(E_WARNING, "Cannot use a scalar value as an array");
-assign_dim_clean:
+ if (IS_VAR != IS_VAR || EXPECTED(!Z_ISERROR_P(object_ptr))) {
+ zend_error(E_WARNING, "Cannot use a scalar value as an array");
+ }
dim = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2);
- zval_ptr_dtor_nogc(free_op2);
- value = get_zval_ptr_r((opline+1)->op1_type, (opline+1)->op1, execute_data, &free_op_data1);
- FREE_OP(free_op_data1);
+assign_dim_error:
+
if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
ZVAL_NULL(EX_VAR(opline->result.var));
}
}
}
+ if ((IS_TMP_VAR|IS_VAR) != IS_UNUSED) {
+ zval_ptr_dtor_nogc(free_op2);
+ }
if (UNEXPECTED(free_op1)) {zval_ptr_dtor_nogc(free_op1);};
/* assign_dim has two opcodes! */
ZEND_VM_NEXT_OPCODE_EX(1, 2);
@@ -22439,6 +27130,13 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_STATIC_METHOD_CALL_SPEC_V
}
CACHE_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)), ce);
}
+ } else if (IS_VAR == IS_UNUSED) {
+ ce = zend_fetch_class(NULL, opline->op1.num);
+ if (UNEXPECTED(ce == NULL)) {
+ ZEND_ASSERT(EG(exception));
+ zval_ptr_dtor_nogc(EX_VAR(opline->op2.var));
+ HANDLE_EXCEPTION();
+ }
} else {
ce = Z_CE_P(EX_VAR(opline->op1.var));
}
@@ -22497,6 +27195,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_STATIC_METHOD_CALL_SPEC_V
CACHE_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(function_name), ce, fbc);
}
}
+ if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!fbc->op_array.run_time_cache)) {
+ init_func_run_time_cache(&fbc->op_array);
+ }
if ((IS_TMP_VAR|IS_VAR) != IS_CONST) {
zval_ptr_dtor_nogc(free_op2);
}
@@ -22505,16 +27206,19 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_STATIC_METHOD_CALL_SPEC_V
zend_throw_error(NULL, "Cannot call constructor");
HANDLE_EXCEPTION();
}
- if (Z_OBJ(EX(This)) && Z_OBJ(EX(This))->ce != ce->constructor->common.scope && (ce->constructor->common.fn_flags & ZEND_ACC_PRIVATE)) {
+ if (Z_TYPE(EX(This)) == IS_OBJECT && Z_OBJ(EX(This))->ce != ce->constructor->common.scope && (ce->constructor->common.fn_flags & ZEND_ACC_PRIVATE)) {
zend_throw_error(NULL, "Cannot call private %s::__construct()", ZSTR_VAL(ce->name));
HANDLE_EXCEPTION();
}
fbc = ce->constructor;
+ if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!fbc->op_array.run_time_cache)) {
+ init_func_run_time_cache(&fbc->op_array);
+ }
}
object = NULL;
if (!(fbc->common.fn_flags & ZEND_ACC_STATIC)) {
- if (Z_OBJ(EX(This)) && instanceof_function(Z_OBJCE(EX(This)), ce)) {
+ if (Z_TYPE(EX(This)) == IS_OBJECT && instanceof_function(Z_OBJCE(EX(This)), ce)) {
object = Z_OBJ(EX(This));
ce = object->ce;
} else {
@@ -22539,11 +27243,15 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_STATIC_METHOD_CALL_SPEC_V
}
}
- if (IS_VAR != IS_CONST) {
+ if (IS_VAR == IS_UNUSED) {
/* previous opcode is ZEND_FETCH_CLASS */
- if (((opline-1)->extended_value & ZEND_FETCH_CLASS_MASK) == ZEND_FETCH_CLASS_PARENT ||
- ((opline-1)->extended_value & ZEND_FETCH_CLASS_MASK) == ZEND_FETCH_CLASS_SELF) {
- ce = EX(called_scope);
+ if ((opline->op1.num & ZEND_FETCH_CLASS_MASK) == ZEND_FETCH_CLASS_PARENT ||
+ (opline->op1.num & ZEND_FETCH_CLASS_MASK) == ZEND_FETCH_CLASS_SELF) {
+ if (Z_TYPE(EX(This)) == IS_OBJECT) {
+ ce = Z_OBJCE(EX(This));
+ } else {
+ ce = Z_CE(EX(This));
+ }
}
}
@@ -22565,11 +27273,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ADD_ARRAY_ELEMENT_SPEC_VAR_TMP
if ((IS_VAR == IS_VAR || IS_VAR == IS_CV) &&
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_throw_error(NULL, "Cannot create references to/from string offsets");
- zend_array_destroy(Z_ARRVAL_P(EX_VAR(opline->result.var)));
- HANDLE_EXCEPTION();
- }
ZVAL_MAKE_REF(expr_ptr);
Z_ADDREF_P(expr_ptr);
if (UNEXPECTED(free_op1)) {zval_ptr_dtor_nogc(free_op1);};
@@ -22578,10 +27281,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ADD_ARRAY_ELEMENT_SPEC_VAR_TMP
if (IS_VAR == IS_TMP_VAR) {
/* pass */
} else if (IS_VAR == IS_CONST) {
- 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;
+ if (Z_REFCOUNTED_P(expr_ptr)) {
+ Z_ADDREF_P(expr_ptr);
}
} else if (IS_VAR == IS_CV) {
ZVAL_DEREF(expr_ptr);
@@ -22696,20 +27397,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_UNSET_DIM_SPEC_VAR_TMPVAR_HAND
SAVE_OPLINE();
container = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1);
- if (IS_VAR == IS_UNUSED && UNEXPECTED(Z_OBJ_P(container) == NULL)) {
- zend_throw_error(NULL, "Using $this when not in object context");
- zval_ptr_dtor_nogc(EX_VAR(opline->op2.var));
- HANDLE_EXCEPTION();
- }
- if (IS_VAR == IS_VAR && UNEXPECTED(container == NULL)) {
- zend_throw_error(NULL, "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);
do {
- if (IS_VAR != IS_UNUSED && EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) {
+ if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) {
HashTable *ht;
unset_dim_array:
@@ -22759,16 +27450,19 @@ num_index_dim:
zend_error(E_WARNING, "Illegal offset type in unset");
}
break;
- } else if (IS_VAR != IS_UNUSED && Z_ISREF_P(container)) {
+ } else if (Z_ISREF_P(container)) {
container = Z_REFVAL_P(container);
if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) {
goto unset_dim_array;
}
}
+ if (IS_VAR == IS_CV && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) {
+ container = GET_OP1_UNDEF_CV(container, BP_VAR_R);
+ }
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_VAR == IS_UNUSED || EXPECTED(Z_TYPE_P(container) == IS_OBJECT)) {
+ if (EXPECTED(Z_TYPE_P(container) == IS_OBJECT)) {
if (UNEXPECTED(Z_OBJ_HT_P(container)->unset_dimension == NULL)) {
zend_throw_error(NULL, "Cannot use object as array");
} else {
@@ -22793,16 +27487,11 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_UNSET_OBJ_SPEC_VAR_TMPVAR_HAND
SAVE_OPLINE();
container = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1);
- if (IS_VAR == IS_UNUSED && UNEXPECTED(Z_OBJ_P(container) == NULL)) {
+ if (IS_VAR == IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
zval_ptr_dtor_nogc(EX_VAR(opline->op2.var));
HANDLE_EXCEPTION();
}
- if (IS_VAR == IS_VAR && UNEXPECTED(container == NULL)) {
- zend_throw_error(NULL, "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);
do {
@@ -22828,19 +27517,83 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_UNSET_OBJ_SPEC_VAR_TMPVAR_HAND
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_NEW_SPEC_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zval *result;
+ zend_function *constructor;
+ zend_class_entry *ce;
+ zend_execute_data *call;
+
+ SAVE_OPLINE();
+ if (IS_UNUSED == IS_CONST) {
+ ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)));
+ if (UNEXPECTED(ce == NULL)) {
+ 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(ce == NULL)) {
+ ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
+ }
+ CACHE_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)), ce);
+ }
+ } else if (IS_UNUSED == IS_UNUSED) {
+ ce = zend_fetch_class(NULL, opline->op1.num);
+ if (UNEXPECTED(ce == NULL)) {
+ ZEND_ASSERT(EG(exception));
+ HANDLE_EXCEPTION();
+ }
+ } else {
+ ce = Z_CE_P(EX_VAR(opline->op1.var));
+ }
+
+ result = EX_VAR(opline->result.var);
+ if (UNEXPECTED(object_init_ex(result, ce) != SUCCESS)) {
+ HANDLE_EXCEPTION();
+ }
+
+ constructor = Z_OBJ_HT_P(result)->get_constructor(Z_OBJ_P(result));
+ if (constructor == NULL) {
+ /* If there are no arguments, skip over the DO_FCALL opcode. We check if the next
+ * opcode is DO_FCALL in case EXT instructions are used. */
+ if (EXPECTED(opline->extended_value == 0 && (opline+1)->opcode == ZEND_DO_FCALL)) {
+ ZEND_VM_NEXT_OPCODE_EX(1, 2);
+ }
+
+ /* Perform a dummy function call */
+ call = zend_vm_stack_push_call_frame(
+ ZEND_CALL_FUNCTION, (zend_function *) &zend_pass_function,
+ opline->extended_value, NULL, NULL);
+ } else {
+ if (EXPECTED(constructor->type == ZEND_USER_FUNCTION) && UNEXPECTED(!constructor->op_array.run_time_cache)) {
+ init_func_run_time_cache(&constructor->op_array);
+ }
+ /* We are not handling overloaded classes right now */
+ call = zend_vm_stack_push_call_frame(
+ ZEND_CALL_FUNCTION | ZEND_CALL_RELEASE_THIS | ZEND_CALL_CTOR,
+ constructor,
+ opline->extended_value,
+ ce,
+ Z_OBJ_P(result));
+ Z_ADDREF_P(result);
+ }
+
+ call->prev_execute_data = EX(call);
+ EX(call) = call;
+ ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
+}
+
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CLONE_SPEC_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
zval *obj;
- zend_class_entry *ce;
+ zend_class_entry *ce, *scope;
zend_function *clone;
zend_object_clone_obj_t clone_call;
SAVE_OPLINE();
obj = _get_obj_zval_ptr_unused(execute_data);
- if (IS_UNUSED == IS_UNUSED && UNEXPECTED(Z_OBJ_P(obj) == NULL)) {
+ if (IS_UNUSED == IS_UNUSED && UNEXPECTED(Z_TYPE_P(obj) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
HANDLE_EXCEPTION();
}
@@ -22867,43 +27620,39 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CLONE_SPEC_UNUSED_HANDLER(ZEND
} while (0);
ce = Z_OBJCE_P(obj);
- clone = ce ? ce->clone : NULL;
- clone_call = Z_OBJ_HT_P(obj)->clone_obj;
+ clone = ce->clone;
+ clone_call = Z_OBJ_HT_P(obj)->clone_obj;
if (UNEXPECTED(clone_call == NULL)) {
- if (ce) {
- zend_throw_error(NULL, "Trying to clone an uncloneable object of class %s", ZSTR_VAL(ce->name));
- } else {
- zend_throw_error(NULL, "Trying to clone an uncloneable object");
- }
+ zend_throw_error(NULL, "Trying to clone an uncloneable object of class %s", ZSTR_VAL(ce->name));
HANDLE_EXCEPTION();
}
- if (ce && clone) {
+ if (clone) {
if (clone->op_array.fn_flags & ZEND_ACC_PRIVATE) {
/* Ensure that if we're calling a private function, we're allowed to do so.
*/
- if (UNEXPECTED(ce != EG(scope))) {
- zend_throw_error(NULL, "Call to private %s::__clone() from context '%s'", ZSTR_VAL(ce->name), EG(scope) ? ZSTR_VAL(EG(scope)->name) : "");
+ scope = EX(func)->op_array.scope;
+ if (UNEXPECTED(ce != scope)) {
+ zend_throw_error(NULL, "Call to private %s::__clone() from context '%s'", ZSTR_VAL(ce->name), scope ? ZSTR_VAL(scope->name) : "");
HANDLE_EXCEPTION();
}
} else if ((clone->common.fn_flags & ZEND_ACC_PROTECTED)) {
/* Ensure that if we're calling a protected function, we're allowed to do so.
*/
- if (UNEXPECTED(!zend_check_protected(zend_get_function_root_class(clone), EG(scope)))) {
- zend_throw_error(NULL, "Call to protected %s::__clone() from context '%s'", ZSTR_VAL(ce->name), EG(scope) ? ZSTR_VAL(EG(scope)->name) : "");
+ scope = EX(func)->op_array.scope;
+ if (UNEXPECTED(!zend_check_protected(zend_get_function_root_class(clone), scope))) {
+ zend_throw_error(NULL, "Call to protected %s::__clone() from context '%s'", ZSTR_VAL(ce->name), scope ? ZSTR_VAL(scope->name) : "");
HANDLE_EXCEPTION();
}
}
}
- if (EXPECTED(EG(exception) == NULL)) {
- ZVAL_OBJ(EX_VAR(opline->result.var), clone_call(obj));
- if (UNEXPECTED(!RETURN_VALUE_USED(opline)) || UNEXPECTED(EG(exception) != NULL)) {
- OBJ_RELEASE(Z_OBJ_P(EX_VAR(opline->result.var)));
- }
+ ZVAL_OBJ(EX_VAR(opline->result.var), clone_call(obj));
+ if (UNEXPECTED(EG(exception) != NULL)) {
+ OBJ_RELEASE(Z_OBJ_P(EX_VAR(opline->result.var)));
}
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
@@ -22950,7 +27699,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_binary_assign_op_obj_helper_SP
SAVE_OPLINE();
object = _get_obj_zval_ptr_unused(execute_data);
- if (IS_UNUSED == IS_UNUSED && UNEXPECTED(Z_OBJ_P(object) == NULL)) {
+ if (IS_UNUSED == IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
FREE_UNFETCHED_OP((opline+1)->op1_type, (opline+1)->op1.var);
@@ -22959,13 +27708,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_binary_assign_op_obj_helper_SP
property = EX_CONSTANT(opline->op2);
- if (IS_UNUSED == IS_VAR && UNEXPECTED(object == NULL)) {
- zend_throw_error(NULL, "Cannot use string offset as an object");
- FREE_UNFETCHED_OP((opline+1)->op1_type, (opline+1)->op1.var);
-
- HANDLE_EXCEPTION();
- }
-
do {
value = get_zval_ptr_r((opline+1)->op1_type, (opline+1)->op1, execute_data, &free_op_data1);
@@ -22983,7 +27725,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_binary_assign_op_obj_helper_SP
/* here we are sure we are dealing with an object */
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)) {
- if (UNEXPECTED(zptr == &EG(error_zval))) {
+ if (UNEXPECTED(Z_ISERROR_P(zptr))) {
if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
ZVAL_NULL(EX_VAR(opline->result.var));
}
@@ -23008,311 +27750,229 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_binary_assign_op_obj_helper_SP
ZEND_VM_NEXT_OPCODE_EX(1, 2);
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_binary_assign_op_dim_helper_SPEC_UNUSED_CONST(binary_op_type binary_op ZEND_OPCODE_HANDLER_ARGS_DC)
-{
- USE_OPLINE
- zend_free_op free_op_data1;
- zval *var_ptr, rv;
- zval *value, *container, *dim;
-
- SAVE_OPLINE();
- container = _get_obj_zval_ptr_unused(execute_data);
- if (IS_UNUSED == IS_UNUSED && UNEXPECTED(Z_OBJ_P(container) == NULL)) {
- zend_throw_error(NULL, "Using $this when not in object context");
- FREE_UNFETCHED_OP((opline+1)->op1_type, (opline+1)->op1.var);
-
- HANDLE_EXCEPTION();
- }
- if (IS_UNUSED == IS_VAR && UNEXPECTED(container == NULL)) {
- zend_throw_error(NULL, "Cannot use string offset as an array");
- FREE_UNFETCHED_OP((opline+1)->op1_type, (opline+1)->op1.var);
-
- HANDLE_EXCEPTION();
- }
-
- dim = EX_CONSTANT(opline->op2);
-
- do {
- if (IS_UNUSED == IS_UNUSED || UNEXPECTED(Z_TYPE_P(container) != IS_ARRAY)) {
- if (IS_UNUSED != IS_UNUSED) {
- ZVAL_DEREF(container);
- }
- if (IS_UNUSED == IS_UNUSED || EXPECTED(Z_TYPE_P(container) == IS_OBJECT)) {
- value = get_zval_ptr_r((opline+1)->op1_type, (opline+1)->op1, execute_data, &free_op_data1);
- zend_binary_assign_op_obj_dim(container, dim, value, UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL, binary_op);
- break;
- }
- }
-
- zend_fetch_dimension_address_RW(&rv, container, dim, IS_CONST);
- value = get_zval_ptr_r((opline+1)->op1_type, (opline+1)->op1, execute_data, &free_op_data1);
- ZEND_ASSERT(Z_TYPE(rv) == IS_INDIRECT);
- var_ptr = Z_INDIRECT(rv);
-
- if (UNEXPECTED(var_ptr == NULL)) {
- zend_throw_error(NULL, "Cannot use assign-op operators with overloaded objects nor string offsets");
-
- FREE_OP(free_op_data1);
-
- HANDLE_EXCEPTION();
- }
-
- if (UNEXPECTED(var_ptr == &EG(error_zval))) {
- if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
- ZVAL_NULL(EX_VAR(opline->result.var));
- }
- } else {
- ZVAL_DEREF(var_ptr);
- SEPARATE_ZVAL_NOREF(var_ptr);
-
- binary_op(var_ptr, var_ptr, value);
-
- if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
- ZVAL_COPY(EX_VAR(opline->result.var), var_ptr);
- }
- }
- } while (0);
-
- FREE_OP(free_op_data1);
-
- ZEND_VM_NEXT_OPCODE_EX(1, 2);
-}
-
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_ADD_SPEC_UNUSED_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_ADD_SPEC_UNUSED_CONST_OBJ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
#if 0 || (IS_CONST != IS_UNUSED)
USE_OPLINE
# if 0 || (IS_UNUSED != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_UNUSED_CONST(add_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
-# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_UNUSED_CONST(add_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_UNUSED_CONST(add_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_UNUSED_CONST(add_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#else
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_UNUSED_CONST(add_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#endif
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_SUB_SPEC_UNUSED_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_SUB_SPEC_UNUSED_CONST_OBJ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
#if 0 || (IS_CONST != IS_UNUSED)
USE_OPLINE
# if 0 || (IS_UNUSED != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_UNUSED_CONST(sub_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
-# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_UNUSED_CONST(sub_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_UNUSED_CONST(sub_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_UNUSED_CONST(sub_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#else
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_UNUSED_CONST(sub_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#endif
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_MUL_SPEC_UNUSED_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_MUL_SPEC_UNUSED_CONST_OBJ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
#if 0 || (IS_CONST != IS_UNUSED)
USE_OPLINE
# if 0 || (IS_UNUSED != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_UNUSED_CONST(mul_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
-# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_UNUSED_CONST(mul_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_UNUSED_CONST(mul_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_UNUSED_CONST(mul_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#else
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_UNUSED_CONST(mul_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#endif
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIV_SPEC_UNUSED_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIV_SPEC_UNUSED_CONST_OBJ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
#if 0 || (IS_CONST != IS_UNUSED)
USE_OPLINE
# if 0 || (IS_UNUSED != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_UNUSED_CONST(div_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
-# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_UNUSED_CONST(div_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_UNUSED_CONST(div_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_UNUSED_CONST(div_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#else
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_UNUSED_CONST(div_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#endif
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_MOD_SPEC_UNUSED_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_MOD_SPEC_UNUSED_CONST_OBJ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
#if 0 || (IS_CONST != IS_UNUSED)
USE_OPLINE
# if 0 || (IS_UNUSED != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_UNUSED_CONST(mod_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
-# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_UNUSED_CONST(mod_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_UNUSED_CONST(mod_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_UNUSED_CONST(mod_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#else
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_UNUSED_CONST(mod_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#endif
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_SL_SPEC_UNUSED_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_SL_SPEC_UNUSED_CONST_OBJ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
#if 0 || (IS_CONST != IS_UNUSED)
USE_OPLINE
# if 0 || (IS_UNUSED != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_UNUSED_CONST(shift_left_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
-# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_UNUSED_CONST(shift_left_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_UNUSED_CONST(shift_left_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_UNUSED_CONST(shift_left_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#else
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_UNUSED_CONST(shift_left_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#endif
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_SR_SPEC_UNUSED_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_SR_SPEC_UNUSED_CONST_OBJ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
#if 0 || (IS_CONST != IS_UNUSED)
USE_OPLINE
# if 0 || (IS_UNUSED != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_UNUSED_CONST(shift_right_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
-# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_UNUSED_CONST(shift_right_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_UNUSED_CONST(shift_right_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_UNUSED_CONST(shift_right_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#else
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_UNUSED_CONST(shift_right_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#endif
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_CONCAT_SPEC_UNUSED_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_CONCAT_SPEC_UNUSED_CONST_OBJ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
#if 0 || (IS_CONST != IS_UNUSED)
USE_OPLINE
# if 0 || (IS_UNUSED != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_UNUSED_CONST(concat_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
-# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_UNUSED_CONST(concat_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_UNUSED_CONST(concat_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_UNUSED_CONST(concat_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#else
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_UNUSED_CONST(concat_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#endif
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_BW_OR_SPEC_UNUSED_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_BW_OR_SPEC_UNUSED_CONST_OBJ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
#if 0 || (IS_CONST != IS_UNUSED)
USE_OPLINE
# if 0 || (IS_UNUSED != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_UNUSED_CONST(bitwise_or_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
-# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_UNUSED_CONST(bitwise_or_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_UNUSED_CONST(bitwise_or_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_UNUSED_CONST(bitwise_or_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#else
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_UNUSED_CONST(bitwise_or_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#endif
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_BW_AND_SPEC_UNUSED_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_BW_AND_SPEC_UNUSED_CONST_OBJ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
#if 0 || (IS_CONST != IS_UNUSED)
USE_OPLINE
# if 0 || (IS_UNUSED != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_UNUSED_CONST(bitwise_and_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
-# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_UNUSED_CONST(bitwise_and_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_UNUSED_CONST(bitwise_and_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_UNUSED_CONST(bitwise_and_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#else
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_UNUSED_CONST(bitwise_and_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#endif
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_BW_XOR_SPEC_UNUSED_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_BW_XOR_SPEC_UNUSED_CONST_OBJ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
#if 0 || (IS_CONST != IS_UNUSED)
USE_OPLINE
# if 0 || (IS_UNUSED != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_UNUSED_CONST(bitwise_xor_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
-# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_UNUSED_CONST(bitwise_xor_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_UNUSED_CONST(bitwise_xor_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_UNUSED_CONST(bitwise_xor_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#else
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_UNUSED_CONST(bitwise_xor_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#endif
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_POW_SPEC_UNUSED_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_POW_SPEC_UNUSED_CONST_OBJ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
#if 0 || (IS_CONST != IS_UNUSED)
USE_OPLINE
# if 0 || (IS_UNUSED != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_UNUSED_CONST(pow_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
-# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_UNUSED_CONST(pow_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_UNUSED_CONST(pow_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_UNUSED_CONST(pow_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#else
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_UNUSED_CONST(pow_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#endif
@@ -23329,7 +27989,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_pre_incdec_property_helper_SPE
SAVE_OPLINE();
object = _get_obj_zval_ptr_unused(execute_data);
- if (IS_UNUSED == IS_UNUSED && UNEXPECTED(Z_OBJ_P(object) == NULL)) {
+ if (IS_UNUSED == IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
HANDLE_EXCEPTION();
@@ -23337,12 +27997,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_pre_incdec_property_helper_SPE
property = EX_CONSTANT(opline->op2);
- if (IS_UNUSED == IS_VAR && UNEXPECTED(object == NULL)) {
- zend_throw_error(NULL, "Cannot increment/decrement overloaded objects nor string offsets");
-
- HANDLE_EXCEPTION();
- }
-
do {
if (IS_UNUSED != IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) != IS_OBJECT)) {
ZVAL_DEREF(object);
@@ -23358,7 +28012,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_pre_incdec_property_helper_SPE
/* here we are sure we are dealing with an object */
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)) {
- if (UNEXPECTED(zptr == &EG(error_zval))) {
+ if (UNEXPECTED(Z_ISERROR_P(zptr))) {
if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
ZVAL_NULL(EX_VAR(opline->result.var));
}
@@ -23413,7 +28067,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_post_incdec_property_helper_SP
SAVE_OPLINE();
object = _get_obj_zval_ptr_unused(execute_data);
- if (IS_UNUSED == IS_UNUSED && UNEXPECTED(Z_OBJ_P(object) == NULL)) {
+ if (IS_UNUSED == IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
HANDLE_EXCEPTION();
@@ -23421,12 +28075,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_post_incdec_property_helper_SP
property = EX_CONSTANT(opline->op2);
- if (IS_UNUSED == IS_VAR && UNEXPECTED(object == NULL)) {
- zend_throw_error(NULL, "Cannot increment/decrement overloaded objects nor string offsets");
-
- HANDLE_EXCEPTION();
- }
-
do {
if (IS_UNUSED != IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) != IS_OBJECT)) {
ZVAL_DEREF(object);
@@ -23441,7 +28089,7 @@ 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)) {
- if (UNEXPECTED(zptr == &EG(error_zval))) {
+ if (UNEXPECTED(Z_ISERROR_P(zptr))) {
ZVAL_NULL(EX_VAR(opline->result.var));
} else {
if (EXPECTED(Z_TYPE_P(zptr) == IS_LONG)) {
@@ -23492,7 +28140,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_R_SPEC_UNUSED_CONST_
SAVE_OPLINE();
container = _get_obj_zval_ptr_unused(execute_data);
- if (IS_UNUSED == IS_UNUSED && UNEXPECTED(Z_OBJ_P(container) == NULL)) {
+ if (IS_UNUSED == IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
HANDLE_EXCEPTION();
@@ -23564,21 +28212,16 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_W_SPEC_UNUSED_CONST_
property = EX_CONSTANT(opline->op2);
container = _get_obj_zval_ptr_unused(execute_data);
- if (IS_UNUSED == IS_UNUSED && UNEXPECTED(Z_OBJ_P(container) == NULL)) {
+ if (IS_UNUSED == IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
HANDLE_EXCEPTION();
}
- if (IS_UNUSED == IS_VAR && UNEXPECTED(container == NULL)) {
- zend_throw_error(NULL, "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);
if (IS_UNUSED == IS_VAR && READY_TO_DESTROY(free_op1)) {
- EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var), 0);
+ EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var));
}
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
@@ -23595,20 +28238,15 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_RW_SPEC_UNUSED_CONST
property = EX_CONSTANT(opline->op2);
container = _get_obj_zval_ptr_unused(execute_data);
- if (IS_UNUSED == IS_UNUSED && UNEXPECTED(Z_OBJ_P(container) == NULL)) {
+ if (IS_UNUSED == IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
HANDLE_EXCEPTION();
}
- if (IS_UNUSED == IS_VAR && UNEXPECTED(container == NULL)) {
- zend_throw_error(NULL, "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);
if (IS_UNUSED == IS_VAR && READY_TO_DESTROY(free_op1)) {
- EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var), 0);
+ EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var));
}
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
@@ -23625,7 +28263,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_IS_SPEC_UNUSED_CONST
SAVE_OPLINE();
container = _get_obj_zval_ptr_unused(execute_data);
- if (IS_UNUSED == IS_UNUSED && UNEXPECTED(Z_OBJ_P(container) == NULL)) {
+ if (IS_UNUSED == IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
HANDLE_EXCEPTION();
@@ -23700,26 +28338,21 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_FUNC_ARG_SPEC_UNUSED
property = EX_CONSTANT(opline->op2);
container = _get_obj_zval_ptr_unused(execute_data);
- if (IS_UNUSED == IS_UNUSED && UNEXPECTED(Z_OBJ_P(container) == NULL)) {
+ if (IS_UNUSED == IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
HANDLE_EXCEPTION();
}
- if (IS_UNUSED == IS_CONST || IS_UNUSED == IS_TMP_VAR) {
+ if ((IS_UNUSED & (IS_CONST|IS_TMP_VAR))) {
zend_throw_error(NULL, "Cannot use temporary expression in write context");
HANDLE_EXCEPTION();
}
- if (IS_UNUSED == IS_VAR && UNEXPECTED(container == NULL)) {
- zend_throw_error(NULL, "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);
if (IS_UNUSED == IS_VAR && READY_TO_DESTROY(free_op1)) {
- EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var), 0);
+ EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var));
}
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
@@ -23737,7 +28370,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_UNSET_SPEC_UNUSED_CO
SAVE_OPLINE();
container = _get_obj_zval_ptr_unused(execute_data);
- if (IS_UNUSED == IS_UNUSED && UNEXPECTED(Z_OBJ_P(container) == NULL)) {
+ if (IS_UNUSED == IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
HANDLE_EXCEPTION();
@@ -23745,44 +28378,633 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_UNSET_SPEC_UNUSED_CO
property = EX_CONSTANT(opline->op2);
- if (IS_UNUSED == IS_VAR && UNEXPECTED(container == NULL)) {
- zend_throw_error(NULL, "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);
if (IS_UNUSED == IS_VAR && READY_TO_DESTROY(free_op1)) {
- EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var), 0);
+ EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var));
}
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_CONST_OP_DATA_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
- zval *object;
- zval *property_name;
+ zval *object, *property_name, *value, tmp;
SAVE_OPLINE();
object = _get_obj_zval_ptr_unused(execute_data);
- if (IS_UNUSED == IS_UNUSED && UNEXPECTED(Z_OBJ_P(object) == NULL)) {
+ if (IS_UNUSED == IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
HANDLE_EXCEPTION();
}
property_name = EX_CONSTANT(opline->op2);
+ value = EX_CONSTANT((opline+1)->op1);
+
+ if (IS_UNUSED != IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) != IS_OBJECT)) {
+ do {
+ if (Z_ISREF_P(object)) {
+ object = Z_REFVAL_P(object);
+ if (EXPECTED(Z_TYPE_P(object) == IS_OBJECT)) {
+ break;
+ }
+ }
+ if (EXPECTED(Z_TYPE_P(object) <= IS_FALSE ||
+ (Z_TYPE_P(object) == IS_STRING && Z_STRLEN_P(object) == 0))) {
+ zend_object *obj;
+
+ zval_ptr_dtor(object);
+ object_init(object);
+ Z_ADDREF_P(object);
+ obj = Z_OBJ_P(object);
+ zend_error(E_WARNING, "Creating default object from empty value");
+ if (GC_REFCOUNT(obj) == 1) {
+ /* the enclosing container was deleted, obj is unreferenced */
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+
+ OBJ_RELEASE(obj);
+ goto exit_assign_obj;
+ }
+ Z_DELREF_P(object);
+ } else {
+ if (IS_UNUSED != IS_VAR || EXPECTED(!Z_ISERROR_P(object))) {
+ zend_error(E_WARNING, "Attempt to assign property of non-object");
+ }
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+
+ goto exit_assign_obj;
+ }
+ } while (0);
+ }
- if (IS_UNUSED == IS_VAR && UNEXPECTED(object == NULL)) {
- zend_throw_error(NULL, "Cannot use string offset as an array");
+ if (IS_CONST == IS_CONST &&
+ EXPECTED(Z_OBJCE_P(object) == CACHED_PTR(Z_CACHE_SLOT_P(property_name)))) {
+ uint32_t prop_offset = (uint32_t)(intptr_t)CACHED_PTR(Z_CACHE_SLOT_P(property_name) + sizeof(void*));
+ zend_object *zobj = Z_OBJ_P(object);
+ zval *property;
+
+ if (EXPECTED(prop_offset != (uint32_t)ZEND_DYNAMIC_PROPERTY_OFFSET)) {
+ property = OBJ_PROP(zobj, prop_offset);
+ if (Z_TYPE_P(property) != IS_UNDEF) {
+fast_assign_obj:
+ value = zend_assign_to_variable(property, value, IS_CONST);
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+ goto exit_assign_obj;
+ }
+ } else {
+ if (EXPECTED(zobj->properties != NULL)) {
+ if (UNEXPECTED(GC_REFCOUNT(zobj->properties) > 1)) {
+ if (EXPECTED(!(GC_FLAGS(zobj->properties) & IS_ARRAY_IMMUTABLE))) {
+ GC_REFCOUNT(zobj->properties)--;
+ }
+ zobj->properties = zend_array_dup(zobj->properties);
+ }
+ property = zend_hash_find(zobj->properties, Z_STR_P(property_name));
+ if (property) {
+ goto fast_assign_obj;
+ }
+ }
+
+ if (!zobj->ce->__set) {
+
+ if (EXPECTED(zobj->properties == NULL)) {
+ rebuild_object_properties(zobj);
+ }
+ if (IS_CONST == IS_CONST) {
+ if (UNEXPECTED(Z_OPT_REFCOUNTED_P(value))) {
+ Z_ADDREF_P(value);
+ }
+ } else if (IS_CONST != IS_TMP_VAR) {
+ if (Z_ISREF_P(value)) {
+ if (IS_CONST == IS_VAR) {
+ zend_reference *ref = Z_REF_P(value);
+ if (--GC_REFCOUNT(ref) == 0) {
+ ZVAL_COPY_VALUE(&tmp, Z_REFVAL_P(value));
+ efree_size(ref, sizeof(zend_reference));
+ value = &tmp;
+ } else {
+ value = Z_REFVAL_P(value);
+ if (Z_REFCOUNTED_P(value)) {
+ Z_ADDREF_P(value);
+ }
+ }
+ } else {
+ value = Z_REFVAL_P(value);
+ if (Z_REFCOUNTED_P(value)) {
+ Z_ADDREF_P(value);
+ }
+ }
+ } else if (IS_CONST == IS_CV && Z_REFCOUNTED_P(value)) {
+ Z_ADDREF_P(value);
+ }
+ }
+ zend_hash_add_new(zobj->properties, Z_STR_P(property_name), value);
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+ goto exit_assign_obj;
+ }
+ }
+ }
+
+ if (!Z_OBJ_HT_P(object)->write_property) {
+ zend_error(E_WARNING, "Attempt to assign property of non-object");
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+
+ goto exit_assign_obj;
+ }
+
+ if (IS_CONST == IS_CV || IS_CONST == IS_VAR) {
+ ZVAL_DEREF(value);
+ }
+
+ Z_OBJ_HT_P(object)->write_property(object, property_name, value, (IS_CONST == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property_name)) : NULL);
+
+ if (UNEXPECTED(RETURN_VALUE_USED(opline)) && EXPECTED(!EG(exception))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+
+exit_assign_obj:
+
+
+ /* assign_obj has two opcodes! */
+ ZEND_VM_NEXT_OPCODE_EX(1, 2);
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_CONST_OP_DATA_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zend_free_op free_op_data;
+ zval *object, *property_name, *value, tmp;
+
+ SAVE_OPLINE();
+ object = _get_obj_zval_ptr_unused(execute_data);
+
+ if (IS_UNUSED == IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) == IS_UNDEF)) {
+ zend_throw_error(NULL, "Using $this when not in object context");
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));
+
+ property_name = EX_CONSTANT(opline->op2);
+ value = _get_zval_ptr_tmp((opline+1)->op1.var, execute_data, &free_op_data);
+
+ if (IS_UNUSED != IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) != IS_OBJECT)) {
+ do {
+ if (Z_ISREF_P(object)) {
+ object = Z_REFVAL_P(object);
+ if (EXPECTED(Z_TYPE_P(object) == IS_OBJECT)) {
+ break;
+ }
+ }
+ if (EXPECTED(Z_TYPE_P(object) <= IS_FALSE ||
+ (Z_TYPE_P(object) == IS_STRING && Z_STRLEN_P(object) == 0))) {
+ zend_object *obj;
+
+ zval_ptr_dtor(object);
+ object_init(object);
+ Z_ADDREF_P(object);
+ obj = Z_OBJ_P(object);
+ zend_error(E_WARNING, "Creating default object from empty value");
+ if (GC_REFCOUNT(obj) == 1) {
+ /* the enclosing container was deleted, obj is unreferenced */
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+ zval_ptr_dtor_nogc(free_op_data);
+ OBJ_RELEASE(obj);
+ goto exit_assign_obj;
+ }
+ Z_DELREF_P(object);
+ } else {
+ if (IS_UNUSED != IS_VAR || EXPECTED(!Z_ISERROR_P(object))) {
+ zend_error(E_WARNING, "Attempt to assign property of non-object");
+ }
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+ zval_ptr_dtor_nogc(free_op_data);
+ goto exit_assign_obj;
+ }
+ } while (0);
+ }
+
+ if (IS_CONST == IS_CONST &&
+ EXPECTED(Z_OBJCE_P(object) == CACHED_PTR(Z_CACHE_SLOT_P(property_name)))) {
+ uint32_t prop_offset = (uint32_t)(intptr_t)CACHED_PTR(Z_CACHE_SLOT_P(property_name) + sizeof(void*));
+ zend_object *zobj = Z_OBJ_P(object);
+ zval *property;
+
+ if (EXPECTED(prop_offset != (uint32_t)ZEND_DYNAMIC_PROPERTY_OFFSET)) {
+ property = OBJ_PROP(zobj, prop_offset);
+ if (Z_TYPE_P(property) != IS_UNDEF) {
+fast_assign_obj:
+ value = zend_assign_to_variable(property, value, IS_TMP_VAR);
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+ goto exit_assign_obj;
+ }
+ } else {
+ if (EXPECTED(zobj->properties != NULL)) {
+ if (UNEXPECTED(GC_REFCOUNT(zobj->properties) > 1)) {
+ if (EXPECTED(!(GC_FLAGS(zobj->properties) & IS_ARRAY_IMMUTABLE))) {
+ GC_REFCOUNT(zobj->properties)--;
+ }
+ zobj->properties = zend_array_dup(zobj->properties);
+ }
+ property = zend_hash_find(zobj->properties, Z_STR_P(property_name));
+ if (property) {
+ goto fast_assign_obj;
+ }
+ }
+
+ if (!zobj->ce->__set) {
+
+ if (EXPECTED(zobj->properties == NULL)) {
+ rebuild_object_properties(zobj);
+ }
+ if (IS_TMP_VAR == IS_CONST) {
+ if (UNEXPECTED(Z_OPT_REFCOUNTED_P(value))) {
+ Z_ADDREF_P(value);
+ }
+ } else if (IS_TMP_VAR != IS_TMP_VAR) {
+ if (Z_ISREF_P(value)) {
+ if (IS_TMP_VAR == IS_VAR) {
+ zend_reference *ref = Z_REF_P(value);
+ if (--GC_REFCOUNT(ref) == 0) {
+ ZVAL_COPY_VALUE(&tmp, Z_REFVAL_P(value));
+ efree_size(ref, sizeof(zend_reference));
+ value = &tmp;
+ } else {
+ value = Z_REFVAL_P(value);
+ if (Z_REFCOUNTED_P(value)) {
+ Z_ADDREF_P(value);
+ }
+ }
+ } else {
+ value = Z_REFVAL_P(value);
+ if (Z_REFCOUNTED_P(value)) {
+ Z_ADDREF_P(value);
+ }
+ }
+ } else if (IS_TMP_VAR == IS_CV && Z_REFCOUNTED_P(value)) {
+ Z_ADDREF_P(value);
+ }
+ }
+ zend_hash_add_new(zobj->properties, Z_STR_P(property_name), value);
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+ goto exit_assign_obj;
+ }
+ }
+ }
+
+ if (!Z_OBJ_HT_P(object)->write_property) {
+ zend_error(E_WARNING, "Attempt to assign property of non-object");
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+ zval_ptr_dtor_nogc(free_op_data);
+ goto exit_assign_obj;
+ }
+
+ if (IS_TMP_VAR == IS_CV || IS_TMP_VAR == IS_VAR) {
+ ZVAL_DEREF(value);
+ }
+
+ Z_OBJ_HT_P(object)->write_property(object, property_name, value, (IS_CONST == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property_name)) : NULL);
+
+ if (UNEXPECTED(RETURN_VALUE_USED(opline)) && EXPECTED(!EG(exception))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+ zval_ptr_dtor_nogc(free_op_data);
+exit_assign_obj:
+
+
+ /* assign_obj has two opcodes! */
+ ZEND_VM_NEXT_OPCODE_EX(1, 2);
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_CONST_OP_DATA_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zend_free_op free_op_data;
+ zval *object, *property_name, *value, tmp;
+
+ SAVE_OPLINE();
+ object = _get_obj_zval_ptr_unused(execute_data);
+
+ if (IS_UNUSED == IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) == IS_UNDEF)) {
+ zend_throw_error(NULL, "Using $this when not in object context");
+
+ HANDLE_EXCEPTION();
+ }
+
+ property_name = EX_CONSTANT(opline->op2);
+ value = _get_zval_ptr_var((opline+1)->op1.var, execute_data, &free_op_data);
+
+ if (IS_UNUSED != IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) != IS_OBJECT)) {
+ do {
+ if (Z_ISREF_P(object)) {
+ object = Z_REFVAL_P(object);
+ if (EXPECTED(Z_TYPE_P(object) == IS_OBJECT)) {
+ break;
+ }
+ }
+ if (EXPECTED(Z_TYPE_P(object) <= IS_FALSE ||
+ (Z_TYPE_P(object) == IS_STRING && Z_STRLEN_P(object) == 0))) {
+ zend_object *obj;
+
+ zval_ptr_dtor(object);
+ object_init(object);
+ Z_ADDREF_P(object);
+ obj = Z_OBJ_P(object);
+ zend_error(E_WARNING, "Creating default object from empty value");
+ if (GC_REFCOUNT(obj) == 1) {
+ /* the enclosing container was deleted, obj is unreferenced */
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+ zval_ptr_dtor_nogc(free_op_data);
+ OBJ_RELEASE(obj);
+ goto exit_assign_obj;
+ }
+ Z_DELREF_P(object);
+ } else {
+ if (IS_UNUSED != IS_VAR || EXPECTED(!Z_ISERROR_P(object))) {
+ zend_error(E_WARNING, "Attempt to assign property of non-object");
+ }
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+ zval_ptr_dtor_nogc(free_op_data);
+ goto exit_assign_obj;
+ }
+ } while (0);
+ }
+
+ if (IS_CONST == IS_CONST &&
+ EXPECTED(Z_OBJCE_P(object) == CACHED_PTR(Z_CACHE_SLOT_P(property_name)))) {
+ uint32_t prop_offset = (uint32_t)(intptr_t)CACHED_PTR(Z_CACHE_SLOT_P(property_name) + sizeof(void*));
+ zend_object *zobj = Z_OBJ_P(object);
+ zval *property;
+
+ if (EXPECTED(prop_offset != (uint32_t)ZEND_DYNAMIC_PROPERTY_OFFSET)) {
+ property = OBJ_PROP(zobj, prop_offset);
+ if (Z_TYPE_P(property) != IS_UNDEF) {
+fast_assign_obj:
+ value = zend_assign_to_variable(property, value, IS_VAR);
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+ goto exit_assign_obj;
+ }
+ } else {
+ if (EXPECTED(zobj->properties != NULL)) {
+ if (UNEXPECTED(GC_REFCOUNT(zobj->properties) > 1)) {
+ if (EXPECTED(!(GC_FLAGS(zobj->properties) & IS_ARRAY_IMMUTABLE))) {
+ GC_REFCOUNT(zobj->properties)--;
+ }
+ zobj->properties = zend_array_dup(zobj->properties);
+ }
+ property = zend_hash_find(zobj->properties, Z_STR_P(property_name));
+ if (property) {
+ goto fast_assign_obj;
+ }
+ }
+
+ if (!zobj->ce->__set) {
+
+ if (EXPECTED(zobj->properties == NULL)) {
+ rebuild_object_properties(zobj);
+ }
+ if (IS_VAR == IS_CONST) {
+ if (UNEXPECTED(Z_OPT_REFCOUNTED_P(value))) {
+ Z_ADDREF_P(value);
+ }
+ } else if (IS_VAR != IS_TMP_VAR) {
+ if (Z_ISREF_P(value)) {
+ if (IS_VAR == IS_VAR) {
+ zend_reference *ref = Z_REF_P(value);
+ if (--GC_REFCOUNT(ref) == 0) {
+ ZVAL_COPY_VALUE(&tmp, Z_REFVAL_P(value));
+ efree_size(ref, sizeof(zend_reference));
+ value = &tmp;
+ } else {
+ value = Z_REFVAL_P(value);
+ if (Z_REFCOUNTED_P(value)) {
+ Z_ADDREF_P(value);
+ }
+ }
+ } else {
+ value = Z_REFVAL_P(value);
+ if (Z_REFCOUNTED_P(value)) {
+ Z_ADDREF_P(value);
+ }
+ }
+ } else if (IS_VAR == IS_CV && Z_REFCOUNTED_P(value)) {
+ Z_ADDREF_P(value);
+ }
+ }
+ zend_hash_add_new(zobj->properties, Z_STR_P(property_name), value);
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+ goto exit_assign_obj;
+ }
+ }
+ }
+
+ if (!Z_OBJ_HT_P(object)->write_property) {
+ zend_error(E_WARNING, "Attempt to assign property of non-object");
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+ zval_ptr_dtor_nogc(free_op_data);
+ goto exit_assign_obj;
+ }
+
+ if (IS_VAR == IS_CV || IS_VAR == IS_VAR) {
+ ZVAL_DEREF(value);
+ }
+
+ Z_OBJ_HT_P(object)->write_property(object, property_name, value, (IS_CONST == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property_name)) : NULL);
+
+ if (UNEXPECTED(RETURN_VALUE_USED(opline)) && EXPECTED(!EG(exception))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+ zval_ptr_dtor_nogc(free_op_data);
+exit_assign_obj:
+
+
+ /* assign_obj has two opcodes! */
+ ZEND_VM_NEXT_OPCODE_EX(1, 2);
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_CONST_OP_DATA_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+
+ zval *object, *property_name, *value, tmp;
+
+ SAVE_OPLINE();
+ object = _get_obj_zval_ptr_unused(execute_data);
+
+ if (IS_UNUSED == IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) == IS_UNDEF)) {
+ zend_throw_error(NULL, "Using $this when not in object context");
+
+ HANDLE_EXCEPTION();
+ }
+
+ property_name = EX_CONSTANT(opline->op2);
+ value = _get_zval_ptr_cv_BP_VAR_R(execute_data, (opline+1)->op1.var);
+
+ if (IS_UNUSED != IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) != IS_OBJECT)) {
+ do {
+ if (Z_ISREF_P(object)) {
+ object = Z_REFVAL_P(object);
+ if (EXPECTED(Z_TYPE_P(object) == IS_OBJECT)) {
+ break;
+ }
+ }
+ if (EXPECTED(Z_TYPE_P(object) <= IS_FALSE ||
+ (Z_TYPE_P(object) == IS_STRING && Z_STRLEN_P(object) == 0))) {
+ zend_object *obj;
+
+ zval_ptr_dtor(object);
+ object_init(object);
+ Z_ADDREF_P(object);
+ obj = Z_OBJ_P(object);
+ zend_error(E_WARNING, "Creating default object from empty value");
+ if (GC_REFCOUNT(obj) == 1) {
+ /* the enclosing container was deleted, obj is unreferenced */
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+
+ OBJ_RELEASE(obj);
+ goto exit_assign_obj;
+ }
+ Z_DELREF_P(object);
+ } else {
+ if (IS_UNUSED != IS_VAR || EXPECTED(!Z_ISERROR_P(object))) {
+ zend_error(E_WARNING, "Attempt to assign property of non-object");
+ }
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+
+ goto exit_assign_obj;
+ }
+ } while (0);
+ }
+
+ if (IS_CONST == IS_CONST &&
+ EXPECTED(Z_OBJCE_P(object) == CACHED_PTR(Z_CACHE_SLOT_P(property_name)))) {
+ uint32_t prop_offset = (uint32_t)(intptr_t)CACHED_PTR(Z_CACHE_SLOT_P(property_name) + sizeof(void*));
+ zend_object *zobj = Z_OBJ_P(object);
+ zval *property;
+
+ if (EXPECTED(prop_offset != (uint32_t)ZEND_DYNAMIC_PROPERTY_OFFSET)) {
+ property = OBJ_PROP(zobj, prop_offset);
+ if (Z_TYPE_P(property) != IS_UNDEF) {
+fast_assign_obj:
+ value = zend_assign_to_variable(property, value, IS_CV);
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+ goto exit_assign_obj;
+ }
+ } else {
+ if (EXPECTED(zobj->properties != NULL)) {
+ if (UNEXPECTED(GC_REFCOUNT(zobj->properties) > 1)) {
+ if (EXPECTED(!(GC_FLAGS(zobj->properties) & IS_ARRAY_IMMUTABLE))) {
+ GC_REFCOUNT(zobj->properties)--;
+ }
+ zobj->properties = zend_array_dup(zobj->properties);
+ }
+ property = zend_hash_find(zobj->properties, Z_STR_P(property_name));
+ if (property) {
+ goto fast_assign_obj;
+ }
+ }
+
+ if (!zobj->ce->__set) {
+
+ if (EXPECTED(zobj->properties == NULL)) {
+ rebuild_object_properties(zobj);
+ }
+ if (IS_CV == IS_CONST) {
+ if (UNEXPECTED(Z_OPT_REFCOUNTED_P(value))) {
+ Z_ADDREF_P(value);
+ }
+ } else if (IS_CV != IS_TMP_VAR) {
+ if (Z_ISREF_P(value)) {
+ if (IS_CV == IS_VAR) {
+ zend_reference *ref = Z_REF_P(value);
+ if (--GC_REFCOUNT(ref) == 0) {
+ ZVAL_COPY_VALUE(&tmp, Z_REFVAL_P(value));
+ efree_size(ref, sizeof(zend_reference));
+ value = &tmp;
+ } else {
+ value = Z_REFVAL_P(value);
+ if (Z_REFCOUNTED_P(value)) {
+ Z_ADDREF_P(value);
+ }
+ }
+ } else {
+ value = Z_REFVAL_P(value);
+ if (Z_REFCOUNTED_P(value)) {
+ Z_ADDREF_P(value);
+ }
+ }
+ } else if (IS_CV == IS_CV && Z_REFCOUNTED_P(value)) {
+ Z_ADDREF_P(value);
+ }
+ }
+ zend_hash_add_new(zobj->properties, Z_STR_P(property_name), value);
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+ goto exit_assign_obj;
+ }
+ }
+ }
+
+ if (!Z_OBJ_HT_P(object)->write_property) {
+ zend_error(E_WARNING, "Attempt to assign property of non-object");
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+
+ goto exit_assign_obj;
+ }
+
+ if (IS_CV == IS_CV || IS_CV == IS_VAR) {
+ ZVAL_DEREF(value);
+ }
+
+ Z_OBJ_HT_P(object)->write_property(object, property_name, value, (IS_CONST == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property_name)) : NULL);
+
+ if (UNEXPECTED(RETURN_VALUE_USED(opline)) && EXPECTED(!EG(exception))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+
+exit_assign_obj:
/* assign_obj has two opcodes! */
@@ -23861,7 +29083,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_SPEC_UNUSED_C
object = _get_obj_zval_ptr_unused(execute_data);
- if (IS_UNUSED == IS_UNUSED && UNEXPECTED(Z_OBJ_P(object) == NULL)) {
+ if (IS_UNUSED == IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
HANDLE_EXCEPTION();
@@ -23921,6 +29143,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_SPEC_UNUSED_C
EXPECTED(obj == orig_obj)) {
CACHE_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(function_name), called_scope, fbc);
}
+ if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!fbc->op_array.run_time_cache)) {
+ init_func_run_time_cache(&fbc->op_array);
+ }
}
call_info = ZEND_CALL_NESTED_FUNCTION;
@@ -23941,110 +29166,286 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_SPEC_UNUSED_C
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CONSTANT_SPEC_UNUSED_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_STATIC_METHOD_CALL_SPEC_UNUSED_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
+ zval *function_name;
+ zend_class_entry *ce;
+ zend_object *object;
+ zend_function *fbc;
+ zend_execute_data *call;
SAVE_OPLINE();
- if (IS_UNUSED == IS_UNUSED) {
- zend_constant *c;
-
- if (EXPECTED(CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2))))) {
- c = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)));
- } else if ((c = zend_quick_get_constant(EX_CONSTANT(opline->op2) + 1, opline->extended_value)) == NULL) {
- if ((opline->extended_value & IS_CONSTANT_UNQUALIFIED) != 0) {
- char *actual = (char *)zend_memrchr(Z_STRVAL_P(EX_CONSTANT(opline->op2)), '\\', Z_STRLEN_P(EX_CONSTANT(opline->op2)));
- if (!actual) {
- ZVAL_STR_COPY(EX_VAR(opline->result.var), Z_STR_P(EX_CONSTANT(opline->op2)));
- } else {
- actual++;
- ZVAL_STRINGL(EX_VAR(opline->result.var),
- actual, Z_STRLEN_P(EX_CONSTANT(opline->op2)) - (actual - Z_STRVAL_P(EX_CONSTANT(opline->op2))));
+
+ if (IS_UNUSED == IS_CONST) {
+ /* no function found. try a static method in class */
+ ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)));
+ if (UNEXPECTED(ce == NULL)) {
+ 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(ce == NULL)) {
+ if (UNEXPECTED(EG(exception) != NULL)) {
+ HANDLE_EXCEPTION();
}
- /* non-qualified constant - allow text substitution */
- zend_error(E_NOTICE, "Use of undefined constant %s - assumed '%s'",
- Z_STRVAL_P(EX_VAR(opline->result.var)), Z_STRVAL_P(EX_VAR(opline->result.var)));
- ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
- } else {
- zend_throw_error(NULL, "Undefined constant '%s'", Z_STRVAL_P(EX_CONSTANT(opline->op2)));
+ zend_throw_error(NULL, "Class '%s' not found", Z_STRVAL_P(EX_CONSTANT(opline->op1)));
HANDLE_EXCEPTION();
}
+ CACHE_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)), ce);
+ }
+ } else if (IS_UNUSED == IS_UNUSED) {
+ ce = zend_fetch_class(NULL, opline->op1.num);
+ if (UNEXPECTED(ce == NULL)) {
+ ZEND_ASSERT(EG(exception));
+
+ HANDLE_EXCEPTION();
+ }
+ } else {
+ ce = Z_CE_P(EX_VAR(opline->op1.var));
+ }
+
+ if (IS_UNUSED == IS_CONST &&
+ IS_CONST == IS_CONST &&
+ EXPECTED((fbc = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)))) != NULL)) {
+ /* nothing to do */
+ } else if (IS_UNUSED != IS_CONST &&
+ IS_CONST == IS_CONST &&
+ (fbc = CACHED_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)), ce))) {
+ /* do nothing */
+ } else if (IS_CONST != IS_UNUSED) {
+
+
+ function_name = EX_CONSTANT(opline->op2);
+ if (IS_CONST != IS_CONST) {
+ if (UNEXPECTED(Z_TYPE_P(function_name) != IS_STRING)) {
+ do {
+ if (IS_CONST & (IS_VAR|IS_CV) && Z_ISREF_P(function_name)) {
+ function_name = Z_REFVAL_P(function_name);
+ if (EXPECTED(Z_TYPE_P(function_name) == IS_STRING)) {
+ break;
+ }
+ } 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();
+ }
+ }
+ zend_throw_error(NULL, "Function name must be a string");
+
+ HANDLE_EXCEPTION();
+ } while (0);
+ }
+ }
+
+ if (ce->get_static_method) {
+ fbc = ce->get_static_method(ce, Z_STR_P(function_name));
} else {
- CACHE_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)), c);
+ fbc = zend_std_get_static_method(ce, Z_STR_P(function_name), ((IS_CONST == IS_CONST) ? (EX_CONSTANT(opline->op2) + 1) : NULL));
}
-#ifdef ZTS
- if (c->flags & CONST_PERSISTENT) {
- ZVAL_DUP(EX_VAR(opline->result.var), &c->value);
+ if (UNEXPECTED(fbc == NULL)) {
+ if (EXPECTED(!EG(exception))) {
+ zend_throw_error(NULL, "Call to undefined method %s::%s()", ZSTR_VAL(ce->name), Z_STRVAL_P(function_name));
+ }
+
+ HANDLE_EXCEPTION();
+ }
+ if (IS_CONST == IS_CONST &&
+ EXPECTED(fbc->type <= ZEND_USER_FUNCTION) &&
+ EXPECTED(!(fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_TRAMPOLINE|ZEND_ACC_NEVER_CACHE)))) {
+ if (IS_UNUSED == IS_CONST) {
+ CACHE_PTR(Z_CACHE_SLOT_P(function_name), fbc);
+ } else {
+ CACHE_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(function_name), ce, fbc);
+ }
+ }
+ if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!fbc->op_array.run_time_cache)) {
+ init_func_run_time_cache(&fbc->op_array);
+ }
+ if (IS_CONST != IS_CONST) {
+
+ }
+ } else {
+ if (UNEXPECTED(ce->constructor == NULL)) {
+ zend_throw_error(NULL, "Cannot call constructor");
+ HANDLE_EXCEPTION();
+ }
+ if (Z_TYPE(EX(This)) == IS_OBJECT && Z_OBJ(EX(This))->ce != ce->constructor->common.scope && (ce->constructor->common.fn_flags & ZEND_ACC_PRIVATE)) {
+ zend_throw_error(NULL, "Cannot call private %s::__construct()", ZSTR_VAL(ce->name));
+ HANDLE_EXCEPTION();
+ }
+ fbc = ce->constructor;
+ if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!fbc->op_array.run_time_cache)) {
+ init_func_run_time_cache(&fbc->op_array);
+ }
+ }
+
+ object = NULL;
+ if (!(fbc->common.fn_flags & ZEND_ACC_STATIC)) {
+ if (Z_TYPE(EX(This)) == IS_OBJECT && instanceof_function(Z_OBJCE(EX(This)), ce)) {
+ object = Z_OBJ(EX(This));
+ ce = object->ce;
} else {
- ZVAL_COPY(EX_VAR(opline->result.var), &c->value);
+ if (fbc->common.fn_flags & ZEND_ACC_ALLOW_STATIC) {
+ /* Allowed for PHP 4 compatibility. */
+ zend_error(
+ E_DEPRECATED,
+ "Non-static method %s::%s() should not be called statically",
+ ZSTR_VAL(fbc->common.scope->name), ZSTR_VAL(fbc->common.function_name));
+ if (UNEXPECTED(EG(exception) != NULL)) {
+ HANDLE_EXCEPTION();
+ }
+ } else {
+ /* An internal function assumes $this is present and won't check that.
+ * So PHP would crash by allowing the call. */
+ zend_throw_error(
+ zend_ce_error,
+ "Non-static method %s::%s() cannot be called statically",
+ ZSTR_VAL(fbc->common.scope->name), ZSTR_VAL(fbc->common.function_name));
+ HANDLE_EXCEPTION();
+ }
}
-#else
+ }
+
+ if (IS_UNUSED == IS_UNUSED) {
+ /* previous opcode is ZEND_FETCH_CLASS */
+ if ((opline->op1.num & ZEND_FETCH_CLASS_MASK) == ZEND_FETCH_CLASS_PARENT ||
+ (opline->op1.num & ZEND_FETCH_CLASS_MASK) == ZEND_FETCH_CLASS_SELF) {
+ if (Z_TYPE(EX(This)) == IS_OBJECT) {
+ ce = Z_OBJCE(EX(This));
+ } else {
+ ce = Z_CE(EX(This));
+ }
+ }
+ }
+
+ call = zend_vm_stack_push_call_frame(ZEND_CALL_NESTED_FUNCTION,
+ fbc, opline->extended_value, ce, object);
+ call->prev_execute_data = EX(call);
+ EX(call) = call;
+
+ ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CONSTANT_SPEC_UNUSED_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zend_constant *c;
+
+ SAVE_OPLINE();
+
+ if (EXPECTED(CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2))))) {
+ c = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)));
+ } else if ((c = zend_quick_get_constant(EX_CONSTANT(opline->op2) + 1, opline->extended_value)) == NULL) {
+ if ((opline->extended_value & IS_CONSTANT_UNQUALIFIED) != 0) {
+ char *actual = (char *)zend_memrchr(Z_STRVAL_P(EX_CONSTANT(opline->op2)), '\\', Z_STRLEN_P(EX_CONSTANT(opline->op2)));
+ if (!actual) {
+ ZVAL_STR_COPY(EX_VAR(opline->result.var), Z_STR_P(EX_CONSTANT(opline->op2)));
+ } else {
+ actual++;
+ ZVAL_STRINGL(EX_VAR(opline->result.var),
+ actual, Z_STRLEN_P(EX_CONSTANT(opline->op2)) - (actual - Z_STRVAL_P(EX_CONSTANT(opline->op2))));
+ }
+ /* non-qualified constant - allow text substitution */
+ zend_error(E_NOTICE, "Use of undefined constant %s - assumed '%s'",
+ Z_STRVAL_P(EX_VAR(opline->result.var)), Z_STRVAL_P(EX_VAR(opline->result.var)));
+ ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
+ } else {
+ zend_throw_error(NULL, "Undefined constant '%s'", Z_STRVAL_P(EX_CONSTANT(opline->op2)));
+ HANDLE_EXCEPTION();
+ }
+ } else {
+ CACHE_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)), c);
+ }
+
+#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;
- do {
- if (IS_UNUSED == IS_CONST) {
- if (EXPECTED(CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2))))) {
- value = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)));
- ZVAL_DEREF(value);
+ ZEND_VM_NEXT_OPCODE();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_CONSTANT_SPEC_UNUSED_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ zend_class_entry *ce, *scope;
+ zend_class_constant *c;
+ zval *value;
+ USE_OPLINE
+
+ SAVE_OPLINE();
+
+ do {
+ if (IS_UNUSED == IS_CONST) {
+ if (EXPECTED(CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2))))) {
+ value = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)));
#ifdef ZTS
- ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)));
+ ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)));
#endif
- break;
- } else if (EXPECTED(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(ce == NULL)) {
- if (EXPECTED(!EG(exception))) {
- zend_throw_error(NULL, "Class '%s' not found", Z_STRVAL_P(EX_CONSTANT(opline->op1)));
- }
- HANDLE_EXCEPTION();
+ break;
+ } else if (EXPECTED(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(ce == NULL)) {
+ if (EXPECTED(!EG(exception))) {
+ zend_throw_error(NULL, "Class '%s' not found", Z_STRVAL_P(EX_CONSTANT(opline->op1)));
}
- CACHE_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)), ce);
+ HANDLE_EXCEPTION();
+ }
+ CACHE_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)), ce);
+ }
+ } else {
+ if (IS_UNUSED == IS_UNUSED) {
+ ce = zend_fetch_class(NULL, opline->op1.num);
+ if (UNEXPECTED(ce == NULL)) {
+ ZEND_ASSERT(EG(exception));
+ 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;
- }
}
+ if ((value = CACHED_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)), ce)) != NULL) {
+ break;
+ }
+ }
- 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);
+ if (EXPECTED((c = zend_hash_find_ptr(&ce->constants_table, Z_STR_P(EX_CONSTANT(opline->op2)))) != NULL)) {
+ scope = EX(func)->op_array.scope;
+ if (!zend_verify_const_access(c, scope)) {
+ zend_throw_error(NULL, "Cannot access %s const %s::%s", zend_visibility_string(Z_ACCESS_FLAGS(c->value)), ZSTR_VAL(ce->name), Z_STRVAL_P(EX_CONSTANT(opline->op2)));
+ HANDLE_EXCEPTION();
+ }
+ value = &c->value;
+ if (Z_CONSTANT_P(value)) {
+ zval_update_constant_ex(value, ce);
+ if (UNEXPECTED(EG(exception) != NULL)) {
+ HANDLE_EXCEPTION();
}
+ }
+ if (IS_UNUSED == IS_CONST) {
+ CACHE_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)), value);
} else {
- zend_throw_error(NULL, "Undefined class constant '%s'", Z_STRVAL_P(EX_CONSTANT(opline->op2)));
- HANDLE_EXCEPTION();
+ CACHE_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)), ce, value);
}
- } while (0);
-#ifdef ZTS
- if (ce->type == ZEND_INTERNAL_CLASS) {
- ZVAL_DUP(EX_VAR(opline->result.var), value);
} else {
- ZVAL_COPY(EX_VAR(opline->result.var), value);
+ zend_throw_error(NULL, "Undefined class constant '%s'", Z_STRVAL_P(EX_CONSTANT(opline->op2)));
+ HANDLE_EXCEPTION();
}
-#else
+ } while (0);
+
+#ifdef ZTS
+ if (ce->type == ZEND_INTERNAL_CLASS) {
+ ZVAL_DUP(EX_VAR(opline->result.var), value);
+ } else {
ZVAL_COPY(EX_VAR(opline->result.var), value);
-#endif
}
+#else
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+#endif
+
ZEND_VM_NEXT_OPCODE();
}
@@ -24079,104 +29480,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_ARRAY_SPEC_UNUSED_CONST_H
}
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_UNSET_DIM_SPEC_UNUSED_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
-{
- USE_OPLINE
-
- zval *container;
- zval *offset;
- zend_ulong hval;
- zend_string *key;
-
- SAVE_OPLINE();
- container = _get_obj_zval_ptr_unused(execute_data);
- if (IS_UNUSED == IS_UNUSED && UNEXPECTED(Z_OBJ_P(container) == NULL)) {
- zend_throw_error(NULL, "Using $this when not in object context");
-
- HANDLE_EXCEPTION();
- }
- if (IS_UNUSED == IS_VAR && UNEXPECTED(container == NULL)) {
- zend_throw_error(NULL, "Cannot unset string offsets");
-
- HANDLE_EXCEPTION();
- }
- offset = EX_CONSTANT(opline->op2);
-
- 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:
- if (EXPECTED(Z_TYPE_P(offset) == IS_STRING)) {
- key = Z_STR_P(offset);
- if (IS_CONST != IS_CONST) {
- if (ZEND_HANDLE_NUMERIC(key, hval)) {
- goto num_index_dim;
- }
- }
-str_index_dim:
- if (ht == &EG(symbol_table)) {
- zend_delete_global_variable(key);
- } else {
- zend_hash_del(ht, key);
- }
- } 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 = ZSTR_EMPTY_ALLOC();
- goto str_index_dim;
- } else if (Z_TYPE_P(offset) == IS_FALSE) {
- hval = 0;
- goto num_index_dim;
- } else if (Z_TYPE_P(offset) == IS_TRUE) {
- hval = 1;
- goto num_index_dim;
- } else if (Z_TYPE_P(offset) == IS_RESOURCE) {
- hval = Z_RES_HANDLE_P(offset);
- goto num_index_dim;
- } else if (IS_CONST == IS_CV && Z_TYPE_P(offset) == IS_UNDEF) {
- GET_OP2_UNDEF_CV(offset, BP_VAR_R);
- key = ZSTR_EMPTY_ALLOC();
- goto str_index_dim;
- } else {
- zend_error(E_WARNING, "Illegal offset type in unset");
- }
- 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;
- }
- }
- 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 || EXPECTED(Z_TYPE_P(container) == IS_OBJECT)) {
- if (UNEXPECTED(Z_OBJ_HT_P(container)->unset_dimension == NULL)) {
- zend_throw_error(NULL, "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_throw_error(NULL, "Cannot unset string offsets");
- }
- } while (0);
-
-
- ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
-}
-
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_UNSET_OBJ_SPEC_UNUSED_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
@@ -24186,16 +29489,11 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_UNSET_OBJ_SPEC_UNUSED_CONST_HA
SAVE_OPLINE();
container = _get_obj_zval_ptr_unused(execute_data);
- if (IS_UNUSED == IS_UNUSED && UNEXPECTED(Z_OBJ_P(container) == NULL)) {
+ if (IS_UNUSED == IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
HANDLE_EXCEPTION();
}
- if (IS_UNUSED == IS_VAR && UNEXPECTED(container == NULL)) {
- zend_throw_error(NULL, "Cannot unset string offsets");
-
- HANDLE_EXCEPTION();
- }
offset = EX_CONSTANT(opline->op2);
do {
@@ -24220,143 +29518,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_UNSET_OBJ_SPEC_UNUSED_CONST_HA
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ISSET_ISEMPTY_DIM_OBJ_SPEC_UNUSED_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
-{
- USE_OPLINE
-
- zval *container;
- int result;
- zend_ulong hval;
- zval *offset;
-
- SAVE_OPLINE();
- container = _get_obj_zval_ptr_unused(execute_data);
-
- if (IS_UNUSED == IS_UNUSED && UNEXPECTED(Z_OBJ_P(container) == NULL)) {
- zend_throw_error(NULL, "Using $this when not in object context");
-
- HANDLE_EXCEPTION();
- }
-
- offset = EX_CONSTANT(opline->op2);
-
- if (IS_UNUSED != IS_UNUSED && EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) {
- 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);
- if (IS_CONST != IS_CONST) {
- if (ZEND_HANDLE_NUMERIC(str, hval)) {
- goto num_index_prop;
- }
- }
-str_index_prop:
- value = zend_hash_find_ind(ht, str);
- } else if (EXPECTED(Z_TYPE_P(offset) == IS_LONG)) {
- hval = Z_LVAL_P(offset);
-num_index_prop:
- value = zend_hash_index_find(ht, hval);
- } 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 = ZSTR_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 = ZSTR_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) {
- /* > IS_NULL means not IS_UNDEF and not IS_NULL */
- result = value != NULL && Z_TYPE_P(value) > IS_NULL &&
- (!Z_ISREF_P(value) || Z_TYPE_P(Z_REFVAL_P(value)) != IS_NULL);
- } else /* if (opline->extended_value & ZEND_ISEMPTY) */ {
- result = (value == NULL || !i_zend_is_true(value));
- }
- 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 =
- ((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");
- goto isset_not_found;
- }
- } else if (EXPECTED(Z_TYPE_P(container) == IS_STRING)) { /* string offsets */
- zend_long lval;
-
- 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))) {
- lval = zval_get_long(offset);
- goto isset_str_offset;
- }
- goto isset_not_found;
- }
- } 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);
- ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
-}
-
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ISSET_ISEMPTY_PROP_OBJ_SPEC_UNUSED_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
@@ -24368,7 +29529,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ISSET_ISEMPTY_PROP_OBJ_SPEC_UN
SAVE_OPLINE();
container = _get_obj_zval_ptr_unused(execute_data);
- if (IS_UNUSED == IS_UNUSED && UNEXPECTED(Z_OBJ_P(container) == NULL)) {
+ if (IS_UNUSED == IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
HANDLE_EXCEPTION();
@@ -24430,7 +29591,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_UNUSED_CONST_HANDLE
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) {
+ if (IS_UNUSED & (IS_CONST|IS_TMP_VAR)) {
zval *value;
zend_error(E_NOTICE, "Only variable references should be yielded by reference");
@@ -24438,25 +29599,19 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_UNUSED_CONST_HANDLE
value = NULL;
ZVAL_COPY_VALUE(&generator->value, value);
if (IS_UNUSED == IS_CONST) {
- if (UNEXPECTED(Z_OPT_COPYABLE(generator->value))) {
- zval_copy_ctor_func(&generator->value);
+ if (UNEXPECTED(Z_OPT_REFCOUNTED(generator->value))) {
+ Z_ADDREF(generator->value);
}
}
} else {
zval *value_ptr = NULL;
- if (IS_UNUSED == IS_VAR && UNEXPECTED(value_ptr == NULL)) {
- zend_throw_error(NULL, "Cannot yield string offsets by reference");
-
- HANDLE_EXCEPTION();
- }
-
/* If a function call result is yielded and the function did
* not return by reference we throw a notice. */
if (IS_UNUSED == IS_VAR &&
(value_ptr == &EG(uninitialized_zval) ||
(opline->extended_value == ZEND_RETURNS_FUNCTION &&
- !(Z_VAR_FLAGS_P(value_ptr) & IS_VAR_RET_REF)))) {
+ !Z_ISREF_P(value_ptr)))) {
zend_error(E_NOTICE, "Only variable references should be yielded by reference");
} else {
ZVAL_MAKE_REF(value_ptr);
@@ -24470,8 +29625,8 @@ 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_COPY_VALUE(&generator->value, value);
- if (UNEXPECTED(Z_OPT_COPYABLE(generator->value))) {
- zval_copy_ctor_func(&generator->value);
+ if (UNEXPECTED(Z_OPT_REFCOUNTED(generator->value))) {
+ Z_ADDREF(generator->value);
}
} else if (IS_UNUSED == IS_TMP_VAR) {
ZVAL_COPY_VALUE(&generator->value, value);
@@ -24498,8 +29653,8 @@ 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_COPY_VALUE(&generator->key, key);
- if (UNEXPECTED(Z_OPT_COPYABLE(generator->key))) {
- zval_copy_ctor_func(&generator->key);
+ if (UNEXPECTED(Z_OPT_REFCOUNTED(generator->key))) {
+ Z_ADDREF(generator->key);
}
} else if (IS_CONST == IS_TMP_VAR) {
ZVAL_COPY_VALUE(&generator->key, key);
@@ -24571,7 +29726,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_UNUSED_TMP_HANDLER(
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) {
+ if (IS_UNUSED & (IS_CONST|IS_TMP_VAR)) {
zval *value;
zend_error(E_NOTICE, "Only variable references should be yielded by reference");
@@ -24579,25 +29734,19 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_UNUSED_TMP_HANDLER(
value = NULL;
ZVAL_COPY_VALUE(&generator->value, value);
if (IS_UNUSED == IS_CONST) {
- if (UNEXPECTED(Z_OPT_COPYABLE(generator->value))) {
- zval_copy_ctor_func(&generator->value);
+ if (UNEXPECTED(Z_OPT_REFCOUNTED(generator->value))) {
+ Z_ADDREF(generator->value);
}
}
} else {
zval *value_ptr = NULL;
- if (IS_UNUSED == IS_VAR && UNEXPECTED(value_ptr == NULL)) {
- zend_throw_error(NULL, "Cannot yield string offsets by reference");
- zval_ptr_dtor_nogc(EX_VAR(opline->op2.var));
- HANDLE_EXCEPTION();
- }
-
/* If a function call result is yielded and the function did
* not return by reference we throw a notice. */
if (IS_UNUSED == IS_VAR &&
(value_ptr == &EG(uninitialized_zval) ||
(opline->extended_value == ZEND_RETURNS_FUNCTION &&
- !(Z_VAR_FLAGS_P(value_ptr) & IS_VAR_RET_REF)))) {
+ !Z_ISREF_P(value_ptr)))) {
zend_error(E_NOTICE, "Only variable references should be yielded by reference");
} else {
ZVAL_MAKE_REF(value_ptr);
@@ -24611,8 +29760,8 @@ 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_COPY_VALUE(&generator->value, value);
- if (UNEXPECTED(Z_OPT_COPYABLE(generator->value))) {
- zval_copy_ctor_func(&generator->value);
+ if (UNEXPECTED(Z_OPT_REFCOUNTED(generator->value))) {
+ Z_ADDREF(generator->value);
}
} else if (IS_UNUSED == IS_TMP_VAR) {
ZVAL_COPY_VALUE(&generator->value, value);
@@ -24639,8 +29788,8 @@ 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_COPY_VALUE(&generator->key, key);
- if (UNEXPECTED(Z_OPT_COPYABLE(generator->key))) {
- zval_copy_ctor_func(&generator->key);
+ if (UNEXPECTED(Z_OPT_REFCOUNTED(generator->key))) {
+ Z_ADDREF(generator->key);
}
} else if (IS_TMP_VAR == IS_TMP_VAR) {
ZVAL_COPY_VALUE(&generator->key, key);
@@ -24712,7 +29861,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_UNUSED_VAR_HANDLER(
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) {
+ if (IS_UNUSED & (IS_CONST|IS_TMP_VAR)) {
zval *value;
zend_error(E_NOTICE, "Only variable references should be yielded by reference");
@@ -24720,25 +29869,19 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_UNUSED_VAR_HANDLER(
value = NULL;
ZVAL_COPY_VALUE(&generator->value, value);
if (IS_UNUSED == IS_CONST) {
- if (UNEXPECTED(Z_OPT_COPYABLE(generator->value))) {
- zval_copy_ctor_func(&generator->value);
+ if (UNEXPECTED(Z_OPT_REFCOUNTED(generator->value))) {
+ Z_ADDREF(generator->value);
}
}
} else {
zval *value_ptr = NULL;
- if (IS_UNUSED == IS_VAR && UNEXPECTED(value_ptr == NULL)) {
- zend_throw_error(NULL, "Cannot yield string offsets by reference");
- zval_ptr_dtor_nogc(EX_VAR(opline->op2.var));
- HANDLE_EXCEPTION();
- }
-
/* If a function call result is yielded and the function did
* not return by reference we throw a notice. */
if (IS_UNUSED == IS_VAR &&
(value_ptr == &EG(uninitialized_zval) ||
(opline->extended_value == ZEND_RETURNS_FUNCTION &&
- !(Z_VAR_FLAGS_P(value_ptr) & IS_VAR_RET_REF)))) {
+ !Z_ISREF_P(value_ptr)))) {
zend_error(E_NOTICE, "Only variable references should be yielded by reference");
} else {
ZVAL_MAKE_REF(value_ptr);
@@ -24752,8 +29895,8 @@ 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_COPY_VALUE(&generator->value, value);
- if (UNEXPECTED(Z_OPT_COPYABLE(generator->value))) {
- zval_copy_ctor_func(&generator->value);
+ if (UNEXPECTED(Z_OPT_REFCOUNTED(generator->value))) {
+ Z_ADDREF(generator->value);
}
} else if (IS_UNUSED == IS_TMP_VAR) {
ZVAL_COPY_VALUE(&generator->value, value);
@@ -24780,8 +29923,8 @@ 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_COPY_VALUE(&generator->key, key);
- if (UNEXPECTED(Z_OPT_COPYABLE(generator->key))) {
- zval_copy_ctor_func(&generator->key);
+ if (UNEXPECTED(Z_OPT_REFCOUNTED(generator->key))) {
+ Z_ADDREF(generator->key);
}
} else if (IS_VAR == IS_TMP_VAR) {
ZVAL_COPY_VALUE(&generator->key, key);
@@ -24826,314 +29969,162 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_UNUSED_VAR_HANDLER(
ZEND_VM_RETURN();
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_binary_assign_op_dim_helper_SPEC_UNUSED_UNUSED(binary_op_type binary_op ZEND_OPCODE_HANDLER_ARGS_DC)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_STATIC_METHOD_CALL_SPEC_UNUSED_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
- zend_free_op free_op_data1;
- zval *var_ptr, rv;
- zval *value, *container, *dim;
+ zval *function_name;
+ zend_class_entry *ce;
+ zend_object *object;
+ zend_function *fbc;
+ zend_execute_data *call;
SAVE_OPLINE();
- container = _get_obj_zval_ptr_unused(execute_data);
- if (IS_UNUSED == IS_UNUSED && UNEXPECTED(Z_OBJ_P(container) == NULL)) {
- zend_throw_error(NULL, "Using $this when not in object context");
- FREE_UNFETCHED_OP((opline+1)->op1_type, (opline+1)->op1.var);
-
- HANDLE_EXCEPTION();
- }
- if (IS_UNUSED == IS_VAR && UNEXPECTED(container == NULL)) {
- zend_throw_error(NULL, "Cannot use string offset as an array");
- FREE_UNFETCHED_OP((opline+1)->op1_type, (opline+1)->op1.var);
-
- HANDLE_EXCEPTION();
- }
-
- dim = NULL;
- do {
- if (IS_UNUSED == IS_UNUSED || UNEXPECTED(Z_TYPE_P(container) != IS_ARRAY)) {
- if (IS_UNUSED != IS_UNUSED) {
- ZVAL_DEREF(container);
- }
- if (IS_UNUSED == IS_UNUSED || EXPECTED(Z_TYPE_P(container) == IS_OBJECT)) {
- value = get_zval_ptr_r((opline+1)->op1_type, (opline+1)->op1, execute_data, &free_op_data1);
- zend_binary_assign_op_obj_dim(container, dim, value, UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL, binary_op);
- break;
+ if (IS_UNUSED == IS_CONST) {
+ /* no function found. try a static method in class */
+ ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)));
+ if (UNEXPECTED(ce == NULL)) {
+ 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(ce == NULL)) {
+ if (UNEXPECTED(EG(exception) != NULL)) {
+ HANDLE_EXCEPTION();
+ }
+ zend_throw_error(NULL, "Class '%s' not found", Z_STRVAL_P(EX_CONSTANT(opline->op1)));
+ HANDLE_EXCEPTION();
}
+ CACHE_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)), ce);
}
-
- zend_fetch_dimension_address_RW(&rv, container, dim, IS_UNUSED);
- value = get_zval_ptr_r((opline+1)->op1_type, (opline+1)->op1, execute_data, &free_op_data1);
- ZEND_ASSERT(Z_TYPE(rv) == IS_INDIRECT);
- var_ptr = Z_INDIRECT(rv);
-
- if (UNEXPECTED(var_ptr == NULL)) {
- zend_throw_error(NULL, "Cannot use assign-op operators with overloaded objects nor string offsets");
-
- FREE_OP(free_op_data1);
+ } else if (IS_UNUSED == IS_UNUSED) {
+ ce = zend_fetch_class(NULL, opline->op1.num);
+ if (UNEXPECTED(ce == NULL)) {
+ ZEND_ASSERT(EG(exception));
HANDLE_EXCEPTION();
}
-
- if (UNEXPECTED(var_ptr == &EG(error_zval))) {
- if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
- ZVAL_NULL(EX_VAR(opline->result.var));
- }
- } else {
- ZVAL_DEREF(var_ptr);
- SEPARATE_ZVAL_NOREF(var_ptr);
-
- binary_op(var_ptr, var_ptr, value);
-
- if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
- ZVAL_COPY(EX_VAR(opline->result.var), var_ptr);
- }
- }
- } while (0);
-
- FREE_OP(free_op_data1);
-
- ZEND_VM_NEXT_OPCODE_EX(1, 2);
-}
-
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_ADD_SPEC_UNUSED_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
-{
-#if 0 || (IS_UNUSED != IS_UNUSED)
- USE_OPLINE
-
-# if 0 || (IS_UNUSED != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_UNUSED_UNUSED(add_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- }
-# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_UNUSED_UNUSED(add_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_UNUSED_UNUSED(add_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- }
-#else
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_UNUSED_UNUSED(add_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
-#endif
-}
-
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_SUB_SPEC_UNUSED_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
-{
-#if 0 || (IS_UNUSED != IS_UNUSED)
- USE_OPLINE
-
-# if 0 || (IS_UNUSED != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_UNUSED_UNUSED(sub_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- }
-# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_UNUSED_UNUSED(sub_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_UNUSED_UNUSED(sub_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- }
-#else
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_UNUSED_UNUSED(sub_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
-#endif
-}
-
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_MUL_SPEC_UNUSED_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
-{
-#if 0 || (IS_UNUSED != IS_UNUSED)
- USE_OPLINE
-
-# if 0 || (IS_UNUSED != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_UNUSED_UNUSED(mul_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- }
-# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_UNUSED_UNUSED(mul_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_UNUSED_UNUSED(mul_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- }
-#else
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_UNUSED_UNUSED(mul_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
-#endif
-}
-
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIV_SPEC_UNUSED_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
-{
-#if 0 || (IS_UNUSED != IS_UNUSED)
- USE_OPLINE
-
-# if 0 || (IS_UNUSED != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_UNUSED_UNUSED(div_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- }
-# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_UNUSED_UNUSED(div_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_UNUSED_UNUSED(div_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- }
-#else
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_UNUSED_UNUSED(div_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
-#endif
-}
-
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_MOD_SPEC_UNUSED_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
-{
-#if 0 || (IS_UNUSED != IS_UNUSED)
- USE_OPLINE
-
-# if 0 || (IS_UNUSED != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_UNUSED_UNUSED(mod_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- }
-# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_UNUSED_UNUSED(mod_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_UNUSED_UNUSED(mod_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ } else {
+ ce = Z_CE_P(EX_VAR(opline->op1.var));
}
-#else
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_UNUSED_UNUSED(mod_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
-#endif
-}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_SL_SPEC_UNUSED_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
-{
-#if 0 || (IS_UNUSED != IS_UNUSED)
- USE_OPLINE
-
-# if 0 || (IS_UNUSED != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_UNUSED_UNUSED(shift_left_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- }
-# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_UNUSED_UNUSED(shift_left_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_UNUSED_UNUSED(shift_left_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- }
-#else
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_UNUSED_UNUSED(shift_left_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
-#endif
-}
+ if (IS_UNUSED == IS_CONST &&
+ IS_UNUSED == IS_CONST &&
+ EXPECTED((fbc = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)))) != NULL)) {
+ /* nothing to do */
+ } else if (IS_UNUSED != IS_CONST &&
+ IS_UNUSED == IS_CONST &&
+ (fbc = CACHED_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)), ce))) {
+ /* do nothing */
+ } else if (IS_UNUSED != IS_UNUSED) {
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_SR_SPEC_UNUSED_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
-{
-#if 0 || (IS_UNUSED != IS_UNUSED)
- USE_OPLINE
-# if 0 || (IS_UNUSED != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_UNUSED_UNUSED(shift_right_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- }
-# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_UNUSED_UNUSED(shift_right_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_UNUSED_UNUSED(shift_right_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- }
-#else
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_UNUSED_UNUSED(shift_right_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
-#endif
-}
+ function_name = NULL;
+ if (IS_UNUSED != IS_CONST) {
+ if (UNEXPECTED(Z_TYPE_P(function_name) != IS_STRING)) {
+ do {
+ if (IS_UNUSED & (IS_VAR|IS_CV) && Z_ISREF_P(function_name)) {
+ function_name = Z_REFVAL_P(function_name);
+ if (EXPECTED(Z_TYPE_P(function_name) == IS_STRING)) {
+ break;
+ }
+ } else 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();
+ }
+ }
+ zend_throw_error(NULL, "Function name must be a string");
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_CONCAT_SPEC_UNUSED_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
-{
-#if 0 || (IS_UNUSED != IS_UNUSED)
- USE_OPLINE
+ HANDLE_EXCEPTION();
+ } while (0);
+ }
+ }
-# if 0 || (IS_UNUSED != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_UNUSED_UNUSED(concat_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- }
-# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_UNUSED_UNUSED(concat_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_UNUSED_UNUSED(concat_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- }
-#else
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_UNUSED_UNUSED(concat_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
-#endif
-}
+ if (ce->get_static_method) {
+ fbc = ce->get_static_method(ce, Z_STR_P(function_name));
+ } else {
+ fbc = zend_std_get_static_method(ce, Z_STR_P(function_name), ((IS_UNUSED == IS_CONST) ? (EX_CONSTANT(opline->op2) + 1) : NULL));
+ }
+ if (UNEXPECTED(fbc == NULL)) {
+ if (EXPECTED(!EG(exception))) {
+ zend_throw_error(NULL, "Call to undefined method %s::%s()", ZSTR_VAL(ce->name), Z_STRVAL_P(function_name));
+ }
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_BW_OR_SPEC_UNUSED_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
-{
-#if 0 || (IS_UNUSED != IS_UNUSED)
- USE_OPLINE
+ HANDLE_EXCEPTION();
+ }
+ if (IS_UNUSED == IS_CONST &&
+ EXPECTED(fbc->type <= ZEND_USER_FUNCTION) &&
+ EXPECTED(!(fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_TRAMPOLINE|ZEND_ACC_NEVER_CACHE)))) {
+ if (IS_UNUSED == IS_CONST) {
+ CACHE_PTR(Z_CACHE_SLOT_P(function_name), fbc);
+ } else {
+ CACHE_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(function_name), ce, fbc);
+ }
+ }
+ if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!fbc->op_array.run_time_cache)) {
+ init_func_run_time_cache(&fbc->op_array);
+ }
+ if (IS_UNUSED != IS_CONST) {
-# if 0 || (IS_UNUSED != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_UNUSED_UNUSED(bitwise_or_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- }
-# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_UNUSED_UNUSED(bitwise_or_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_UNUSED_UNUSED(bitwise_or_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ } else {
+ if (UNEXPECTED(ce->constructor == NULL)) {
+ zend_throw_error(NULL, "Cannot call constructor");
+ HANDLE_EXCEPTION();
+ }
+ if (Z_TYPE(EX(This)) == IS_OBJECT && Z_OBJ(EX(This))->ce != ce->constructor->common.scope && (ce->constructor->common.fn_flags & ZEND_ACC_PRIVATE)) {
+ zend_throw_error(NULL, "Cannot call private %s::__construct()", ZSTR_VAL(ce->name));
+ HANDLE_EXCEPTION();
+ }
+ fbc = ce->constructor;
+ if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!fbc->op_array.run_time_cache)) {
+ init_func_run_time_cache(&fbc->op_array);
+ }
}
-#else
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_UNUSED_UNUSED(bitwise_or_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
-#endif
-}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_BW_AND_SPEC_UNUSED_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
-{
-#if 0 || (IS_UNUSED != IS_UNUSED)
- USE_OPLINE
-
-# if 0 || (IS_UNUSED != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_UNUSED_UNUSED(bitwise_and_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- }
-# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_UNUSED_UNUSED(bitwise_and_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_UNUSED_UNUSED(bitwise_and_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ object = NULL;
+ if (!(fbc->common.fn_flags & ZEND_ACC_STATIC)) {
+ if (Z_TYPE(EX(This)) == IS_OBJECT && instanceof_function(Z_OBJCE(EX(This)), ce)) {
+ object = Z_OBJ(EX(This));
+ ce = object->ce;
+ } else {
+ if (fbc->common.fn_flags & ZEND_ACC_ALLOW_STATIC) {
+ /* Allowed for PHP 4 compatibility. */
+ zend_error(
+ E_DEPRECATED,
+ "Non-static method %s::%s() should not be called statically",
+ ZSTR_VAL(fbc->common.scope->name), ZSTR_VAL(fbc->common.function_name));
+ if (UNEXPECTED(EG(exception) != NULL)) {
+ HANDLE_EXCEPTION();
+ }
+ } else {
+ /* An internal function assumes $this is present and won't check that.
+ * So PHP would crash by allowing the call. */
+ zend_throw_error(
+ zend_ce_error,
+ "Non-static method %s::%s() cannot be called statically",
+ ZSTR_VAL(fbc->common.scope->name), ZSTR_VAL(fbc->common.function_name));
+ HANDLE_EXCEPTION();
+ }
+ }
}
-#else
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_UNUSED_UNUSED(bitwise_and_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
-#endif
-}
-
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_BW_XOR_SPEC_UNUSED_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
-{
-#if 0 || (IS_UNUSED != IS_UNUSED)
- USE_OPLINE
-# if 0 || (IS_UNUSED != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_UNUSED_UNUSED(bitwise_xor_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- }
-# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_UNUSED_UNUSED(bitwise_xor_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_UNUSED_UNUSED(bitwise_xor_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ if (IS_UNUSED == IS_UNUSED) {
+ /* previous opcode is ZEND_FETCH_CLASS */
+ if ((opline->op1.num & ZEND_FETCH_CLASS_MASK) == ZEND_FETCH_CLASS_PARENT ||
+ (opline->op1.num & ZEND_FETCH_CLASS_MASK) == ZEND_FETCH_CLASS_SELF) {
+ if (Z_TYPE(EX(This)) == IS_OBJECT) {
+ ce = Z_OBJCE(EX(This));
+ } else {
+ ce = Z_CE(EX(This));
+ }
+ }
}
-#else
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_UNUSED_UNUSED(bitwise_xor_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
-#endif
-}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_POW_SPEC_UNUSED_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
-{
-#if 0 || (IS_UNUSED != IS_UNUSED)
- USE_OPLINE
+ call = zend_vm_stack_push_call_frame(ZEND_CALL_NESTED_FUNCTION,
+ fbc, opline->extended_value, ce, object);
+ call->prev_execute_data = EX(call);
+ EX(call) = call;
-# if 0 || (IS_UNUSED != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_UNUSED_UNUSED(pow_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- }
-# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_UNUSED_UNUSED(pow_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_UNUSED_UNUSED(pow_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- }
-#else
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_UNUSED_UNUSED(pow_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
-#endif
+ ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_VERIFY_RETURN_TYPE_SPEC_UNUSED_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@@ -25184,8 +30175,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_VERIFY_RETURN_TYPE_SPEC_UNUSED
if (UNEXPECTED(EG(exception) != NULL)) {
if (IS_UNUSED == IS_CONST) {
zval_ptr_dtor_nogc(retval_ptr);
- } else {
-
}
}
#endif
@@ -25251,7 +30240,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_UNUSED_UNUSED_HANDL
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) {
+ if (IS_UNUSED & (IS_CONST|IS_TMP_VAR)) {
zval *value;
zend_error(E_NOTICE, "Only variable references should be yielded by reference");
@@ -25259,25 +30248,19 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_UNUSED_UNUSED_HANDL
value = NULL;
ZVAL_COPY_VALUE(&generator->value, value);
if (IS_UNUSED == IS_CONST) {
- if (UNEXPECTED(Z_OPT_COPYABLE(generator->value))) {
- zval_copy_ctor_func(&generator->value);
+ if (UNEXPECTED(Z_OPT_REFCOUNTED(generator->value))) {
+ Z_ADDREF(generator->value);
}
}
} else {
zval *value_ptr = NULL;
- if (IS_UNUSED == IS_VAR && UNEXPECTED(value_ptr == NULL)) {
- zend_throw_error(NULL, "Cannot yield string offsets by reference");
-
- HANDLE_EXCEPTION();
- }
-
/* If a function call result is yielded and the function did
* not return by reference we throw a notice. */
if (IS_UNUSED == IS_VAR &&
(value_ptr == &EG(uninitialized_zval) ||
(opline->extended_value == ZEND_RETURNS_FUNCTION &&
- !(Z_VAR_FLAGS_P(value_ptr) & IS_VAR_RET_REF)))) {
+ !Z_ISREF_P(value_ptr)))) {
zend_error(E_NOTICE, "Only variable references should be yielded by reference");
} else {
ZVAL_MAKE_REF(value_ptr);
@@ -25291,8 +30274,8 @@ 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_COPY_VALUE(&generator->value, value);
- if (UNEXPECTED(Z_OPT_COPYABLE(generator->value))) {
- zval_copy_ctor_func(&generator->value);
+ if (UNEXPECTED(Z_OPT_REFCOUNTED(generator->value))) {
+ Z_ADDREF(generator->value);
}
} else if (IS_UNUSED == IS_TMP_VAR) {
ZVAL_COPY_VALUE(&generator->value, value);
@@ -25319,8 +30302,8 @@ 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_COPY_VALUE(&generator->key, key);
- if (UNEXPECTED(Z_OPT_COPYABLE(generator->key))) {
- zval_copy_ctor_func(&generator->key);
+ if (UNEXPECTED(Z_OPT_REFCOUNTED(generator->key))) {
+ Z_ADDREF(generator->key);
}
} else if (IS_UNUSED == IS_TMP_VAR) {
ZVAL_COPY_VALUE(&generator->key, key);
@@ -25365,6 +30348,34 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_UNUSED_UNUSED_HANDL
ZEND_VM_RETURN();
}
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_THIS_SPEC_UNUSED_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+
+ if (EXPECTED(Z_TYPE(EX(This)) == IS_OBJECT)) {
+ zval *result = EX_VAR(opline->result.var);
+
+ ZVAL_OBJ(result, Z_OBJ(EX(This)));
+ Z_ADDREF_P(result);
+ ZEND_VM_NEXT_OPCODE();
+ } else {
+ SAVE_OPLINE();
+ zend_throw_error(NULL, "Using $this when not in object context");
+ HANDLE_EXCEPTION();
+ }
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ISSET_ISEMPTY_THIS_SPEC_UNUSED_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+
+ ZVAL_BOOL(EX_VAR(opline->result.var),
+ (opline->extended_value & ZEND_ISSET) ?
+ (Z_TYPE(EX(This)) == IS_OBJECT) :
+ (Z_TYPE(EX(This)) != IS_OBJECT));
+ ZEND_VM_NEXT_OPCODE();
+}
+
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_binary_assign_op_obj_helper_SPEC_UNUSED_CV(binary_op_type binary_op ZEND_OPCODE_HANDLER_ARGS_DC)
{
USE_OPLINE
@@ -25377,7 +30388,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_binary_assign_op_obj_helper_SP
SAVE_OPLINE();
object = _get_obj_zval_ptr_unused(execute_data);
- if (IS_UNUSED == IS_UNUSED && UNEXPECTED(Z_OBJ_P(object) == NULL)) {
+ if (IS_UNUSED == IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
FREE_UNFETCHED_OP((opline+1)->op1_type, (opline+1)->op1.var);
@@ -25386,13 +30397,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_binary_assign_op_obj_helper_SP
property = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var);
- if (IS_UNUSED == IS_VAR && UNEXPECTED(object == NULL)) {
- zend_throw_error(NULL, "Cannot use string offset as an object");
- FREE_UNFETCHED_OP((opline+1)->op1_type, (opline+1)->op1.var);
-
- HANDLE_EXCEPTION();
- }
-
do {
value = get_zval_ptr_r((opline+1)->op1_type, (opline+1)->op1, execute_data, &free_op_data1);
@@ -25410,7 +30414,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_binary_assign_op_obj_helper_SP
/* here we are sure we are dealing with an object */
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)) {
- if (UNEXPECTED(zptr == &EG(error_zval))) {
+ if (UNEXPECTED(Z_ISERROR_P(zptr))) {
if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
ZVAL_NULL(EX_VAR(opline->result.var));
}
@@ -25435,311 +30439,229 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_binary_assign_op_obj_helper_SP
ZEND_VM_NEXT_OPCODE_EX(1, 2);
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_binary_assign_op_dim_helper_SPEC_UNUSED_CV(binary_op_type binary_op ZEND_OPCODE_HANDLER_ARGS_DC)
-{
- USE_OPLINE
- zend_free_op free_op_data1;
- zval *var_ptr, rv;
- zval *value, *container, *dim;
-
- SAVE_OPLINE();
- container = _get_obj_zval_ptr_unused(execute_data);
- if (IS_UNUSED == IS_UNUSED && UNEXPECTED(Z_OBJ_P(container) == NULL)) {
- zend_throw_error(NULL, "Using $this when not in object context");
- FREE_UNFETCHED_OP((opline+1)->op1_type, (opline+1)->op1.var);
-
- HANDLE_EXCEPTION();
- }
- if (IS_UNUSED == IS_VAR && UNEXPECTED(container == NULL)) {
- zend_throw_error(NULL, "Cannot use string offset as an array");
- FREE_UNFETCHED_OP((opline+1)->op1_type, (opline+1)->op1.var);
-
- HANDLE_EXCEPTION();
- }
-
- dim = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var);
-
- do {
- if (IS_UNUSED == IS_UNUSED || UNEXPECTED(Z_TYPE_P(container) != IS_ARRAY)) {
- if (IS_UNUSED != IS_UNUSED) {
- ZVAL_DEREF(container);
- }
- if (IS_UNUSED == IS_UNUSED || EXPECTED(Z_TYPE_P(container) == IS_OBJECT)) {
- value = get_zval_ptr_r((opline+1)->op1_type, (opline+1)->op1, execute_data, &free_op_data1);
- zend_binary_assign_op_obj_dim(container, dim, value, UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL, binary_op);
- break;
- }
- }
-
- zend_fetch_dimension_address_RW(&rv, container, dim, IS_CV);
- value = get_zval_ptr_r((opline+1)->op1_type, (opline+1)->op1, execute_data, &free_op_data1);
- ZEND_ASSERT(Z_TYPE(rv) == IS_INDIRECT);
- var_ptr = Z_INDIRECT(rv);
-
- if (UNEXPECTED(var_ptr == NULL)) {
- zend_throw_error(NULL, "Cannot use assign-op operators with overloaded objects nor string offsets");
-
- FREE_OP(free_op_data1);
-
- HANDLE_EXCEPTION();
- }
-
- if (UNEXPECTED(var_ptr == &EG(error_zval))) {
- if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
- ZVAL_NULL(EX_VAR(opline->result.var));
- }
- } else {
- ZVAL_DEREF(var_ptr);
- SEPARATE_ZVAL_NOREF(var_ptr);
-
- binary_op(var_ptr, var_ptr, value);
-
- if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
- ZVAL_COPY(EX_VAR(opline->result.var), var_ptr);
- }
- }
- } while (0);
-
- FREE_OP(free_op_data1);
-
- ZEND_VM_NEXT_OPCODE_EX(1, 2);
-}
-
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_ADD_SPEC_UNUSED_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_ADD_SPEC_UNUSED_CV_OBJ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
#if 0 || (IS_CV != IS_UNUSED)
USE_OPLINE
# if 0 || (IS_UNUSED != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_UNUSED_CV(add_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
-# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_UNUSED_CV(add_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_UNUSED_CV(add_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_UNUSED_CV(add_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#else
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_UNUSED_CV(add_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#endif
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_SUB_SPEC_UNUSED_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_SUB_SPEC_UNUSED_CV_OBJ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
#if 0 || (IS_CV != IS_UNUSED)
USE_OPLINE
# if 0 || (IS_UNUSED != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_UNUSED_CV(sub_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
-# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_UNUSED_CV(sub_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_UNUSED_CV(sub_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_UNUSED_CV(sub_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#else
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_UNUSED_CV(sub_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#endif
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_MUL_SPEC_UNUSED_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_MUL_SPEC_UNUSED_CV_OBJ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
#if 0 || (IS_CV != IS_UNUSED)
USE_OPLINE
# if 0 || (IS_UNUSED != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_UNUSED_CV(mul_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
-# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_UNUSED_CV(mul_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_UNUSED_CV(mul_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_UNUSED_CV(mul_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#else
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_UNUSED_CV(mul_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#endif
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIV_SPEC_UNUSED_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIV_SPEC_UNUSED_CV_OBJ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
#if 0 || (IS_CV != IS_UNUSED)
USE_OPLINE
# if 0 || (IS_UNUSED != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_UNUSED_CV(div_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
-# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_UNUSED_CV(div_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_UNUSED_CV(div_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_UNUSED_CV(div_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#else
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_UNUSED_CV(div_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#endif
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_MOD_SPEC_UNUSED_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_MOD_SPEC_UNUSED_CV_OBJ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
#if 0 || (IS_CV != IS_UNUSED)
USE_OPLINE
# if 0 || (IS_UNUSED != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_UNUSED_CV(mod_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
-# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_UNUSED_CV(mod_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_UNUSED_CV(mod_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_UNUSED_CV(mod_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#else
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_UNUSED_CV(mod_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#endif
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_SL_SPEC_UNUSED_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_SL_SPEC_UNUSED_CV_OBJ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
#if 0 || (IS_CV != IS_UNUSED)
USE_OPLINE
# if 0 || (IS_UNUSED != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_UNUSED_CV(shift_left_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
-# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_UNUSED_CV(shift_left_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_UNUSED_CV(shift_left_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_UNUSED_CV(shift_left_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#else
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_UNUSED_CV(shift_left_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#endif
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_SR_SPEC_UNUSED_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_SR_SPEC_UNUSED_CV_OBJ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
#if 0 || (IS_CV != IS_UNUSED)
USE_OPLINE
# if 0 || (IS_UNUSED != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_UNUSED_CV(shift_right_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
-# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_UNUSED_CV(shift_right_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_UNUSED_CV(shift_right_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_UNUSED_CV(shift_right_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#else
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_UNUSED_CV(shift_right_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#endif
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_CONCAT_SPEC_UNUSED_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_CONCAT_SPEC_UNUSED_CV_OBJ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
#if 0 || (IS_CV != IS_UNUSED)
USE_OPLINE
# if 0 || (IS_UNUSED != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_UNUSED_CV(concat_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
-# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_UNUSED_CV(concat_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_UNUSED_CV(concat_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_UNUSED_CV(concat_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#else
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_UNUSED_CV(concat_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#endif
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_BW_OR_SPEC_UNUSED_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_BW_OR_SPEC_UNUSED_CV_OBJ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
#if 0 || (IS_CV != IS_UNUSED)
USE_OPLINE
# if 0 || (IS_UNUSED != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_UNUSED_CV(bitwise_or_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
-# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_UNUSED_CV(bitwise_or_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_UNUSED_CV(bitwise_or_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_UNUSED_CV(bitwise_or_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#else
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_UNUSED_CV(bitwise_or_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#endif
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_BW_AND_SPEC_UNUSED_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_BW_AND_SPEC_UNUSED_CV_OBJ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
#if 0 || (IS_CV != IS_UNUSED)
USE_OPLINE
# if 0 || (IS_UNUSED != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_UNUSED_CV(bitwise_and_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
-# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_UNUSED_CV(bitwise_and_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_UNUSED_CV(bitwise_and_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_UNUSED_CV(bitwise_and_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#else
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_UNUSED_CV(bitwise_and_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#endif
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_BW_XOR_SPEC_UNUSED_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_BW_XOR_SPEC_UNUSED_CV_OBJ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
#if 0 || (IS_CV != IS_UNUSED)
USE_OPLINE
# if 0 || (IS_UNUSED != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_UNUSED_CV(bitwise_xor_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
-# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_UNUSED_CV(bitwise_xor_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_UNUSED_CV(bitwise_xor_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_UNUSED_CV(bitwise_xor_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#else
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_UNUSED_CV(bitwise_xor_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#endif
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_POW_SPEC_UNUSED_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_POW_SPEC_UNUSED_CV_OBJ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
#if 0 || (IS_CV != IS_UNUSED)
USE_OPLINE
# if 0 || (IS_UNUSED != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_UNUSED_CV(pow_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
-# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_UNUSED_CV(pow_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_UNUSED_CV(pow_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_UNUSED_CV(pow_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#else
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_UNUSED_CV(pow_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#endif
@@ -25756,7 +30678,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_pre_incdec_property_helper_SPE
SAVE_OPLINE();
object = _get_obj_zval_ptr_unused(execute_data);
- if (IS_UNUSED == IS_UNUSED && UNEXPECTED(Z_OBJ_P(object) == NULL)) {
+ if (IS_UNUSED == IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
HANDLE_EXCEPTION();
@@ -25764,12 +30686,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_pre_incdec_property_helper_SPE
property = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var);
- if (IS_UNUSED == IS_VAR && UNEXPECTED(object == NULL)) {
- zend_throw_error(NULL, "Cannot increment/decrement overloaded objects nor string offsets");
-
- HANDLE_EXCEPTION();
- }
-
do {
if (IS_UNUSED != IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) != IS_OBJECT)) {
ZVAL_DEREF(object);
@@ -25785,7 +30701,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_pre_incdec_property_helper_SPE
/* here we are sure we are dealing with an object */
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)) {
- if (UNEXPECTED(zptr == &EG(error_zval))) {
+ if (UNEXPECTED(Z_ISERROR_P(zptr))) {
if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
ZVAL_NULL(EX_VAR(opline->result.var));
}
@@ -25840,7 +30756,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_post_incdec_property_helper_SP
SAVE_OPLINE();
object = _get_obj_zval_ptr_unused(execute_data);
- if (IS_UNUSED == IS_UNUSED && UNEXPECTED(Z_OBJ_P(object) == NULL)) {
+ if (IS_UNUSED == IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
HANDLE_EXCEPTION();
@@ -25848,12 +30764,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_post_incdec_property_helper_SP
property = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var);
- if (IS_UNUSED == IS_VAR && UNEXPECTED(object == NULL)) {
- zend_throw_error(NULL, "Cannot increment/decrement overloaded objects nor string offsets");
-
- HANDLE_EXCEPTION();
- }
-
do {
if (IS_UNUSED != IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) != IS_OBJECT)) {
ZVAL_DEREF(object);
@@ -25868,7 +30778,7 @@ 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)) {
- if (UNEXPECTED(zptr == &EG(error_zval))) {
+ if (UNEXPECTED(Z_ISERROR_P(zptr))) {
ZVAL_NULL(EX_VAR(opline->result.var));
} else {
if (EXPECTED(Z_TYPE_P(zptr) == IS_LONG)) {
@@ -25919,7 +30829,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_R_SPEC_UNUSED_CV_HAN
SAVE_OPLINE();
container = _get_obj_zval_ptr_unused(execute_data);
- if (IS_UNUSED == IS_UNUSED && UNEXPECTED(Z_OBJ_P(container) == NULL)) {
+ if (IS_UNUSED == IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
HANDLE_EXCEPTION();
@@ -25991,21 +30901,16 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_W_SPEC_UNUSED_CV_HAN
property = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var);
container = _get_obj_zval_ptr_unused(execute_data);
- if (IS_UNUSED == IS_UNUSED && UNEXPECTED(Z_OBJ_P(container) == NULL)) {
+ if (IS_UNUSED == IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
HANDLE_EXCEPTION();
}
- if (IS_UNUSED == IS_VAR && UNEXPECTED(container == NULL)) {
- zend_throw_error(NULL, "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);
if (IS_UNUSED == IS_VAR && READY_TO_DESTROY(free_op1)) {
- EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var), 0);
+ EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var));
}
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
@@ -26022,20 +30927,15 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_RW_SPEC_UNUSED_CV_HA
property = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var);
container = _get_obj_zval_ptr_unused(execute_data);
- if (IS_UNUSED == IS_UNUSED && UNEXPECTED(Z_OBJ_P(container) == NULL)) {
+ if (IS_UNUSED == IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
HANDLE_EXCEPTION();
}
- if (IS_UNUSED == IS_VAR && UNEXPECTED(container == NULL)) {
- zend_throw_error(NULL, "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);
if (IS_UNUSED == IS_VAR && READY_TO_DESTROY(free_op1)) {
- EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var), 0);
+ EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var));
}
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
@@ -26052,7 +30952,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_IS_SPEC_UNUSED_CV_HA
SAVE_OPLINE();
container = _get_obj_zval_ptr_unused(execute_data);
- if (IS_UNUSED == IS_UNUSED && UNEXPECTED(Z_OBJ_P(container) == NULL)) {
+ if (IS_UNUSED == IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
HANDLE_EXCEPTION();
@@ -26127,26 +31027,21 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_FUNC_ARG_SPEC_UNUSED
property = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var);
container = _get_obj_zval_ptr_unused(execute_data);
- if (IS_UNUSED == IS_UNUSED && UNEXPECTED(Z_OBJ_P(container) == NULL)) {
+ if (IS_UNUSED == IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
HANDLE_EXCEPTION();
}
- if (IS_UNUSED == IS_CONST || IS_UNUSED == IS_TMP_VAR) {
+ if ((IS_UNUSED & (IS_CONST|IS_TMP_VAR))) {
zend_throw_error(NULL, "Cannot use temporary expression in write context");
HANDLE_EXCEPTION();
}
- if (IS_UNUSED == IS_VAR && UNEXPECTED(container == NULL)) {
- zend_throw_error(NULL, "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);
if (IS_UNUSED == IS_VAR && READY_TO_DESTROY(free_op1)) {
- EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var), 0);
+ EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var));
}
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
@@ -26164,7 +31059,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_UNSET_SPEC_UNUSED_CV
SAVE_OPLINE();
container = _get_obj_zval_ptr_unused(execute_data);
- if (IS_UNUSED == IS_UNUSED && UNEXPECTED(Z_OBJ_P(container) == NULL)) {
+ if (IS_UNUSED == IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
HANDLE_EXCEPTION();
@@ -26172,44 +31067,633 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_UNSET_SPEC_UNUSED_CV
property = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var);
- if (IS_UNUSED == IS_VAR && UNEXPECTED(container == NULL)) {
- zend_throw_error(NULL, "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);
if (IS_UNUSED == IS_VAR && READY_TO_DESTROY(free_op1)) {
- EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var), 0);
+ EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var));
}
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_CV_OP_DATA_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
- zval *object;
- zval *property_name;
+ zval *object, *property_name, *value, tmp;
SAVE_OPLINE();
object = _get_obj_zval_ptr_unused(execute_data);
- if (IS_UNUSED == IS_UNUSED && UNEXPECTED(Z_OBJ_P(object) == NULL)) {
+ if (IS_UNUSED == IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
HANDLE_EXCEPTION();
}
property_name = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var);
+ value = EX_CONSTANT((opline+1)->op1);
+
+ if (IS_UNUSED != IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) != IS_OBJECT)) {
+ do {
+ if (Z_ISREF_P(object)) {
+ object = Z_REFVAL_P(object);
+ if (EXPECTED(Z_TYPE_P(object) == IS_OBJECT)) {
+ break;
+ }
+ }
+ if (EXPECTED(Z_TYPE_P(object) <= IS_FALSE ||
+ (Z_TYPE_P(object) == IS_STRING && Z_STRLEN_P(object) == 0))) {
+ zend_object *obj;
+
+ zval_ptr_dtor(object);
+ object_init(object);
+ Z_ADDREF_P(object);
+ obj = Z_OBJ_P(object);
+ zend_error(E_WARNING, "Creating default object from empty value");
+ if (GC_REFCOUNT(obj) == 1) {
+ /* the enclosing container was deleted, obj is unreferenced */
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+
+ OBJ_RELEASE(obj);
+ goto exit_assign_obj;
+ }
+ Z_DELREF_P(object);
+ } else {
+ if (IS_UNUSED != IS_VAR || EXPECTED(!Z_ISERROR_P(object))) {
+ zend_error(E_WARNING, "Attempt to assign property of non-object");
+ }
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
- if (IS_UNUSED == IS_VAR && UNEXPECTED(object == NULL)) {
- zend_throw_error(NULL, "Cannot use string offset as an array");
+ goto exit_assign_obj;
+ }
+ } while (0);
+ }
+
+ if (IS_CV == IS_CONST &&
+ EXPECTED(Z_OBJCE_P(object) == CACHED_PTR(Z_CACHE_SLOT_P(property_name)))) {
+ uint32_t prop_offset = (uint32_t)(intptr_t)CACHED_PTR(Z_CACHE_SLOT_P(property_name) + sizeof(void*));
+ zend_object *zobj = Z_OBJ_P(object);
+ zval *property;
+
+ if (EXPECTED(prop_offset != (uint32_t)ZEND_DYNAMIC_PROPERTY_OFFSET)) {
+ property = OBJ_PROP(zobj, prop_offset);
+ if (Z_TYPE_P(property) != IS_UNDEF) {
+fast_assign_obj:
+ value = zend_assign_to_variable(property, value, IS_CONST);
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+ goto exit_assign_obj;
+ }
+ } else {
+ if (EXPECTED(zobj->properties != NULL)) {
+ if (UNEXPECTED(GC_REFCOUNT(zobj->properties) > 1)) {
+ if (EXPECTED(!(GC_FLAGS(zobj->properties) & IS_ARRAY_IMMUTABLE))) {
+ GC_REFCOUNT(zobj->properties)--;
+ }
+ zobj->properties = zend_array_dup(zobj->properties);
+ }
+ property = zend_hash_find(zobj->properties, Z_STR_P(property_name));
+ if (property) {
+ goto fast_assign_obj;
+ }
+ }
+
+ if (!zobj->ce->__set) {
+
+ if (EXPECTED(zobj->properties == NULL)) {
+ rebuild_object_properties(zobj);
+ }
+ if (IS_CONST == IS_CONST) {
+ if (UNEXPECTED(Z_OPT_REFCOUNTED_P(value))) {
+ Z_ADDREF_P(value);
+ }
+ } else if (IS_CONST != IS_TMP_VAR) {
+ if (Z_ISREF_P(value)) {
+ if (IS_CONST == IS_VAR) {
+ zend_reference *ref = Z_REF_P(value);
+ if (--GC_REFCOUNT(ref) == 0) {
+ ZVAL_COPY_VALUE(&tmp, Z_REFVAL_P(value));
+ efree_size(ref, sizeof(zend_reference));
+ value = &tmp;
+ } else {
+ value = Z_REFVAL_P(value);
+ if (Z_REFCOUNTED_P(value)) {
+ Z_ADDREF_P(value);
+ }
+ }
+ } else {
+ value = Z_REFVAL_P(value);
+ if (Z_REFCOUNTED_P(value)) {
+ Z_ADDREF_P(value);
+ }
+ }
+ } else if (IS_CONST == IS_CV && Z_REFCOUNTED_P(value)) {
+ Z_ADDREF_P(value);
+ }
+ }
+ zend_hash_add_new(zobj->properties, Z_STR_P(property_name), value);
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+ goto exit_assign_obj;
+ }
+ }
+ }
+
+ if (!Z_OBJ_HT_P(object)->write_property) {
+ zend_error(E_WARNING, "Attempt to assign property of non-object");
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+
+ goto exit_assign_obj;
+ }
+
+ if (IS_CONST == IS_CV || IS_CONST == IS_VAR) {
+ ZVAL_DEREF(value);
+ }
+
+ Z_OBJ_HT_P(object)->write_property(object, property_name, value, (IS_CV == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property_name)) : NULL);
+
+ if (UNEXPECTED(RETURN_VALUE_USED(opline)) && EXPECTED(!EG(exception))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+
+exit_assign_obj:
+
+
+ /* assign_obj has two opcodes! */
+ ZEND_VM_NEXT_OPCODE_EX(1, 2);
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_CV_OP_DATA_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zend_free_op free_op_data;
+ zval *object, *property_name, *value, tmp;
+
+ SAVE_OPLINE();
+ object = _get_obj_zval_ptr_unused(execute_data);
+
+ if (IS_UNUSED == IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) == IS_UNDEF)) {
+ zend_throw_error(NULL, "Using $this when not in object context");
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));
+
+ property_name = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var);
+ value = _get_zval_ptr_tmp((opline+1)->op1.var, execute_data, &free_op_data);
+
+ if (IS_UNUSED != IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) != IS_OBJECT)) {
+ do {
+ if (Z_ISREF_P(object)) {
+ object = Z_REFVAL_P(object);
+ if (EXPECTED(Z_TYPE_P(object) == IS_OBJECT)) {
+ break;
+ }
+ }
+ if (EXPECTED(Z_TYPE_P(object) <= IS_FALSE ||
+ (Z_TYPE_P(object) == IS_STRING && Z_STRLEN_P(object) == 0))) {
+ zend_object *obj;
+
+ zval_ptr_dtor(object);
+ object_init(object);
+ Z_ADDREF_P(object);
+ obj = Z_OBJ_P(object);
+ zend_error(E_WARNING, "Creating default object from empty value");
+ if (GC_REFCOUNT(obj) == 1) {
+ /* the enclosing container was deleted, obj is unreferenced */
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+ zval_ptr_dtor_nogc(free_op_data);
+ OBJ_RELEASE(obj);
+ goto exit_assign_obj;
+ }
+ Z_DELREF_P(object);
+ } else {
+ if (IS_UNUSED != IS_VAR || EXPECTED(!Z_ISERROR_P(object))) {
+ zend_error(E_WARNING, "Attempt to assign property of non-object");
+ }
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+ zval_ptr_dtor_nogc(free_op_data);
+ goto exit_assign_obj;
+ }
+ } while (0);
+ }
+
+ if (IS_CV == IS_CONST &&
+ EXPECTED(Z_OBJCE_P(object) == CACHED_PTR(Z_CACHE_SLOT_P(property_name)))) {
+ uint32_t prop_offset = (uint32_t)(intptr_t)CACHED_PTR(Z_CACHE_SLOT_P(property_name) + sizeof(void*));
+ zend_object *zobj = Z_OBJ_P(object);
+ zval *property;
+
+ if (EXPECTED(prop_offset != (uint32_t)ZEND_DYNAMIC_PROPERTY_OFFSET)) {
+ property = OBJ_PROP(zobj, prop_offset);
+ if (Z_TYPE_P(property) != IS_UNDEF) {
+fast_assign_obj:
+ value = zend_assign_to_variable(property, value, IS_TMP_VAR);
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+ goto exit_assign_obj;
+ }
+ } else {
+ if (EXPECTED(zobj->properties != NULL)) {
+ if (UNEXPECTED(GC_REFCOUNT(zobj->properties) > 1)) {
+ if (EXPECTED(!(GC_FLAGS(zobj->properties) & IS_ARRAY_IMMUTABLE))) {
+ GC_REFCOUNT(zobj->properties)--;
+ }
+ zobj->properties = zend_array_dup(zobj->properties);
+ }
+ property = zend_hash_find(zobj->properties, Z_STR_P(property_name));
+ if (property) {
+ goto fast_assign_obj;
+ }
+ }
+
+ if (!zobj->ce->__set) {
+
+ if (EXPECTED(zobj->properties == NULL)) {
+ rebuild_object_properties(zobj);
+ }
+ if (IS_TMP_VAR == IS_CONST) {
+ if (UNEXPECTED(Z_OPT_REFCOUNTED_P(value))) {
+ Z_ADDREF_P(value);
+ }
+ } else if (IS_TMP_VAR != IS_TMP_VAR) {
+ if (Z_ISREF_P(value)) {
+ if (IS_TMP_VAR == IS_VAR) {
+ zend_reference *ref = Z_REF_P(value);
+ if (--GC_REFCOUNT(ref) == 0) {
+ ZVAL_COPY_VALUE(&tmp, Z_REFVAL_P(value));
+ efree_size(ref, sizeof(zend_reference));
+ value = &tmp;
+ } else {
+ value = Z_REFVAL_P(value);
+ if (Z_REFCOUNTED_P(value)) {
+ Z_ADDREF_P(value);
+ }
+ }
+ } else {
+ value = Z_REFVAL_P(value);
+ if (Z_REFCOUNTED_P(value)) {
+ Z_ADDREF_P(value);
+ }
+ }
+ } else if (IS_TMP_VAR == IS_CV && Z_REFCOUNTED_P(value)) {
+ Z_ADDREF_P(value);
+ }
+ }
+ zend_hash_add_new(zobj->properties, Z_STR_P(property_name), value);
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+ goto exit_assign_obj;
+ }
+ }
+ }
+
+ if (!Z_OBJ_HT_P(object)->write_property) {
+ zend_error(E_WARNING, "Attempt to assign property of non-object");
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+ zval_ptr_dtor_nogc(free_op_data);
+ goto exit_assign_obj;
+ }
+
+ if (IS_TMP_VAR == IS_CV || IS_TMP_VAR == IS_VAR) {
+ ZVAL_DEREF(value);
+ }
+
+ Z_OBJ_HT_P(object)->write_property(object, property_name, value, (IS_CV == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property_name)) : NULL);
+
+ if (UNEXPECTED(RETURN_VALUE_USED(opline)) && EXPECTED(!EG(exception))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+ zval_ptr_dtor_nogc(free_op_data);
+exit_assign_obj:
+
+
+ /* assign_obj has two opcodes! */
+ ZEND_VM_NEXT_OPCODE_EX(1, 2);
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_CV_OP_DATA_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zend_free_op free_op_data;
+ zval *object, *property_name, *value, tmp;
+
+ SAVE_OPLINE();
+ object = _get_obj_zval_ptr_unused(execute_data);
+
+ if (IS_UNUSED == IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) == IS_UNDEF)) {
+ zend_throw_error(NULL, "Using $this when not in object context");
+
+ HANDLE_EXCEPTION();
+ }
+
+ property_name = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var);
+ value = _get_zval_ptr_var((opline+1)->op1.var, execute_data, &free_op_data);
+
+ if (IS_UNUSED != IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) != IS_OBJECT)) {
+ do {
+ if (Z_ISREF_P(object)) {
+ object = Z_REFVAL_P(object);
+ if (EXPECTED(Z_TYPE_P(object) == IS_OBJECT)) {
+ break;
+ }
+ }
+ if (EXPECTED(Z_TYPE_P(object) <= IS_FALSE ||
+ (Z_TYPE_P(object) == IS_STRING && Z_STRLEN_P(object) == 0))) {
+ zend_object *obj;
+
+ zval_ptr_dtor(object);
+ object_init(object);
+ Z_ADDREF_P(object);
+ obj = Z_OBJ_P(object);
+ zend_error(E_WARNING, "Creating default object from empty value");
+ if (GC_REFCOUNT(obj) == 1) {
+ /* the enclosing container was deleted, obj is unreferenced */
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+ zval_ptr_dtor_nogc(free_op_data);
+ OBJ_RELEASE(obj);
+ goto exit_assign_obj;
+ }
+ Z_DELREF_P(object);
+ } else {
+ if (IS_UNUSED != IS_VAR || EXPECTED(!Z_ISERROR_P(object))) {
+ zend_error(E_WARNING, "Attempt to assign property of non-object");
+ }
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+ zval_ptr_dtor_nogc(free_op_data);
+ goto exit_assign_obj;
+ }
+ } while (0);
+ }
+
+ if (IS_CV == IS_CONST &&
+ EXPECTED(Z_OBJCE_P(object) == CACHED_PTR(Z_CACHE_SLOT_P(property_name)))) {
+ uint32_t prop_offset = (uint32_t)(intptr_t)CACHED_PTR(Z_CACHE_SLOT_P(property_name) + sizeof(void*));
+ zend_object *zobj = Z_OBJ_P(object);
+ zval *property;
+
+ if (EXPECTED(prop_offset != (uint32_t)ZEND_DYNAMIC_PROPERTY_OFFSET)) {
+ property = OBJ_PROP(zobj, prop_offset);
+ if (Z_TYPE_P(property) != IS_UNDEF) {
+fast_assign_obj:
+ value = zend_assign_to_variable(property, value, IS_VAR);
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+ goto exit_assign_obj;
+ }
+ } else {
+ if (EXPECTED(zobj->properties != NULL)) {
+ if (UNEXPECTED(GC_REFCOUNT(zobj->properties) > 1)) {
+ if (EXPECTED(!(GC_FLAGS(zobj->properties) & IS_ARRAY_IMMUTABLE))) {
+ GC_REFCOUNT(zobj->properties)--;
+ }
+ zobj->properties = zend_array_dup(zobj->properties);
+ }
+ property = zend_hash_find(zobj->properties, Z_STR_P(property_name));
+ if (property) {
+ goto fast_assign_obj;
+ }
+ }
+
+ if (!zobj->ce->__set) {
+
+ if (EXPECTED(zobj->properties == NULL)) {
+ rebuild_object_properties(zobj);
+ }
+ if (IS_VAR == IS_CONST) {
+ if (UNEXPECTED(Z_OPT_REFCOUNTED_P(value))) {
+ Z_ADDREF_P(value);
+ }
+ } else if (IS_VAR != IS_TMP_VAR) {
+ if (Z_ISREF_P(value)) {
+ if (IS_VAR == IS_VAR) {
+ zend_reference *ref = Z_REF_P(value);
+ if (--GC_REFCOUNT(ref) == 0) {
+ ZVAL_COPY_VALUE(&tmp, Z_REFVAL_P(value));
+ efree_size(ref, sizeof(zend_reference));
+ value = &tmp;
+ } else {
+ value = Z_REFVAL_P(value);
+ if (Z_REFCOUNTED_P(value)) {
+ Z_ADDREF_P(value);
+ }
+ }
+ } else {
+ value = Z_REFVAL_P(value);
+ if (Z_REFCOUNTED_P(value)) {
+ Z_ADDREF_P(value);
+ }
+ }
+ } else if (IS_VAR == IS_CV && Z_REFCOUNTED_P(value)) {
+ Z_ADDREF_P(value);
+ }
+ }
+ zend_hash_add_new(zobj->properties, Z_STR_P(property_name), value);
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+ goto exit_assign_obj;
+ }
+ }
+ }
+
+ if (!Z_OBJ_HT_P(object)->write_property) {
+ zend_error(E_WARNING, "Attempt to assign property of non-object");
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+ zval_ptr_dtor_nogc(free_op_data);
+ goto exit_assign_obj;
+ }
+
+ if (IS_VAR == IS_CV || IS_VAR == IS_VAR) {
+ ZVAL_DEREF(value);
+ }
+
+ Z_OBJ_HT_P(object)->write_property(object, property_name, value, (IS_CV == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property_name)) : NULL);
+
+ if (UNEXPECTED(RETURN_VALUE_USED(opline)) && EXPECTED(!EG(exception))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+ zval_ptr_dtor_nogc(free_op_data);
+exit_assign_obj:
+
+
+ /* assign_obj has two opcodes! */
+ ZEND_VM_NEXT_OPCODE_EX(1, 2);
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_CV_OP_DATA_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+
+ zval *object, *property_name, *value, tmp;
+
+ SAVE_OPLINE();
+ object = _get_obj_zval_ptr_unused(execute_data);
+
+ if (IS_UNUSED == IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) == IS_UNDEF)) {
+ zend_throw_error(NULL, "Using $this when not in object context");
+
+ HANDLE_EXCEPTION();
+ }
+
+ property_name = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var);
+ value = _get_zval_ptr_cv_BP_VAR_R(execute_data, (opline+1)->op1.var);
+
+ if (IS_UNUSED != IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) != IS_OBJECT)) {
+ do {
+ if (Z_ISREF_P(object)) {
+ object = Z_REFVAL_P(object);
+ if (EXPECTED(Z_TYPE_P(object) == IS_OBJECT)) {
+ break;
+ }
+ }
+ if (EXPECTED(Z_TYPE_P(object) <= IS_FALSE ||
+ (Z_TYPE_P(object) == IS_STRING && Z_STRLEN_P(object) == 0))) {
+ zend_object *obj;
+
+ zval_ptr_dtor(object);
+ object_init(object);
+ Z_ADDREF_P(object);
+ obj = Z_OBJ_P(object);
+ zend_error(E_WARNING, "Creating default object from empty value");
+ if (GC_REFCOUNT(obj) == 1) {
+ /* the enclosing container was deleted, obj is unreferenced */
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+
+ OBJ_RELEASE(obj);
+ goto exit_assign_obj;
+ }
+ Z_DELREF_P(object);
+ } else {
+ if (IS_UNUSED != IS_VAR || EXPECTED(!Z_ISERROR_P(object))) {
+ zend_error(E_WARNING, "Attempt to assign property of non-object");
+ }
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+
+ goto exit_assign_obj;
+ }
+ } while (0);
+ }
+
+ if (IS_CV == IS_CONST &&
+ EXPECTED(Z_OBJCE_P(object) == CACHED_PTR(Z_CACHE_SLOT_P(property_name)))) {
+ uint32_t prop_offset = (uint32_t)(intptr_t)CACHED_PTR(Z_CACHE_SLOT_P(property_name) + sizeof(void*));
+ zend_object *zobj = Z_OBJ_P(object);
+ zval *property;
+
+ if (EXPECTED(prop_offset != (uint32_t)ZEND_DYNAMIC_PROPERTY_OFFSET)) {
+ property = OBJ_PROP(zobj, prop_offset);
+ if (Z_TYPE_P(property) != IS_UNDEF) {
+fast_assign_obj:
+ value = zend_assign_to_variable(property, value, IS_CV);
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+ goto exit_assign_obj;
+ }
+ } else {
+ if (EXPECTED(zobj->properties != NULL)) {
+ if (UNEXPECTED(GC_REFCOUNT(zobj->properties) > 1)) {
+ if (EXPECTED(!(GC_FLAGS(zobj->properties) & IS_ARRAY_IMMUTABLE))) {
+ GC_REFCOUNT(zobj->properties)--;
+ }
+ zobj->properties = zend_array_dup(zobj->properties);
+ }
+ property = zend_hash_find(zobj->properties, Z_STR_P(property_name));
+ if (property) {
+ goto fast_assign_obj;
+ }
+ }
+
+ if (!zobj->ce->__set) {
+
+ if (EXPECTED(zobj->properties == NULL)) {
+ rebuild_object_properties(zobj);
+ }
+ if (IS_CV == IS_CONST) {
+ if (UNEXPECTED(Z_OPT_REFCOUNTED_P(value))) {
+ Z_ADDREF_P(value);
+ }
+ } else if (IS_CV != IS_TMP_VAR) {
+ if (Z_ISREF_P(value)) {
+ if (IS_CV == IS_VAR) {
+ zend_reference *ref = Z_REF_P(value);
+ if (--GC_REFCOUNT(ref) == 0) {
+ ZVAL_COPY_VALUE(&tmp, Z_REFVAL_P(value));
+ efree_size(ref, sizeof(zend_reference));
+ value = &tmp;
+ } else {
+ value = Z_REFVAL_P(value);
+ if (Z_REFCOUNTED_P(value)) {
+ Z_ADDREF_P(value);
+ }
+ }
+ } else {
+ value = Z_REFVAL_P(value);
+ if (Z_REFCOUNTED_P(value)) {
+ Z_ADDREF_P(value);
+ }
+ }
+ } else if (IS_CV == IS_CV && Z_REFCOUNTED_P(value)) {
+ Z_ADDREF_P(value);
+ }
+ }
+ zend_hash_add_new(zobj->properties, Z_STR_P(property_name), value);
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+ goto exit_assign_obj;
+ }
+ }
+ }
+
+ if (!Z_OBJ_HT_P(object)->write_property) {
+ zend_error(E_WARNING, "Attempt to assign property of non-object");
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+
+ goto exit_assign_obj;
+ }
+
+ if (IS_CV == IS_CV || IS_CV == IS_VAR) {
+ ZVAL_DEREF(value);
+ }
+
+ Z_OBJ_HT_P(object)->write_property(object, property_name, value, (IS_CV == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property_name)) : NULL);
+
+ if (UNEXPECTED(RETURN_VALUE_USED(opline)) && EXPECTED(!EG(exception))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+
+exit_assign_obj:
/* assign_obj has two opcodes! */
@@ -26288,7 +31772,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_SPEC_UNUSED_C
object = _get_obj_zval_ptr_unused(execute_data);
- if (IS_UNUSED == IS_UNUSED && UNEXPECTED(Z_OBJ_P(object) == NULL)) {
+ if (IS_UNUSED == IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
HANDLE_EXCEPTION();
@@ -26348,6 +31832,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_SPEC_UNUSED_C
EXPECTED(obj == orig_obj)) {
CACHE_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(function_name), called_scope, fbc);
}
+ if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!fbc->op_array.run_time_cache)) {
+ init_func_run_time_cache(&fbc->op_array);
+ }
}
call_info = ZEND_CALL_NESTED_FUNCTION;
@@ -26368,6 +31855,164 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_SPEC_UNUSED_C
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_STATIC_METHOD_CALL_SPEC_UNUSED_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zval *function_name;
+ zend_class_entry *ce;
+ zend_object *object;
+ zend_function *fbc;
+ zend_execute_data *call;
+
+ SAVE_OPLINE();
+
+ if (IS_UNUSED == IS_CONST) {
+ /* no function found. try a static method in class */
+ ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)));
+ if (UNEXPECTED(ce == NULL)) {
+ 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(ce == NULL)) {
+ if (UNEXPECTED(EG(exception) != NULL)) {
+ HANDLE_EXCEPTION();
+ }
+ zend_throw_error(NULL, "Class '%s' not found", Z_STRVAL_P(EX_CONSTANT(opline->op1)));
+ HANDLE_EXCEPTION();
+ }
+ CACHE_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)), ce);
+ }
+ } else if (IS_UNUSED == IS_UNUSED) {
+ ce = zend_fetch_class(NULL, opline->op1.num);
+ if (UNEXPECTED(ce == NULL)) {
+ ZEND_ASSERT(EG(exception));
+
+ HANDLE_EXCEPTION();
+ }
+ } else {
+ ce = Z_CE_P(EX_VAR(opline->op1.var));
+ }
+
+ if (IS_UNUSED == IS_CONST &&
+ IS_CV == IS_CONST &&
+ EXPECTED((fbc = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)))) != NULL)) {
+ /* nothing to do */
+ } else if (IS_UNUSED != IS_CONST &&
+ IS_CV == IS_CONST &&
+ (fbc = CACHED_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)), ce))) {
+ /* do nothing */
+ } else if (IS_CV != IS_UNUSED) {
+
+
+ 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)) {
+ do {
+ if (IS_CV & (IS_VAR|IS_CV) && Z_ISREF_P(function_name)) {
+ function_name = Z_REFVAL_P(function_name);
+ if (EXPECTED(Z_TYPE_P(function_name) == IS_STRING)) {
+ break;
+ }
+ } 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();
+ }
+ }
+ zend_throw_error(NULL, "Function name must be a string");
+
+ HANDLE_EXCEPTION();
+ } while (0);
+ }
+ }
+
+ if (ce->get_static_method) {
+ fbc = ce->get_static_method(ce, Z_STR_P(function_name));
+ } else {
+ fbc = zend_std_get_static_method(ce, Z_STR_P(function_name), ((IS_CV == IS_CONST) ? (EX_CONSTANT(opline->op2) + 1) : NULL));
+ }
+ if (UNEXPECTED(fbc == NULL)) {
+ if (EXPECTED(!EG(exception))) {
+ zend_throw_error(NULL, "Call to undefined method %s::%s()", ZSTR_VAL(ce->name), Z_STRVAL_P(function_name));
+ }
+
+ HANDLE_EXCEPTION();
+ }
+ if (IS_CV == IS_CONST &&
+ EXPECTED(fbc->type <= ZEND_USER_FUNCTION) &&
+ EXPECTED(!(fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_TRAMPOLINE|ZEND_ACC_NEVER_CACHE)))) {
+ if (IS_UNUSED == IS_CONST) {
+ CACHE_PTR(Z_CACHE_SLOT_P(function_name), fbc);
+ } else {
+ CACHE_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(function_name), ce, fbc);
+ }
+ }
+ if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!fbc->op_array.run_time_cache)) {
+ init_func_run_time_cache(&fbc->op_array);
+ }
+ if (IS_CV != IS_CONST) {
+
+ }
+ } else {
+ if (UNEXPECTED(ce->constructor == NULL)) {
+ zend_throw_error(NULL, "Cannot call constructor");
+ HANDLE_EXCEPTION();
+ }
+ if (Z_TYPE(EX(This)) == IS_OBJECT && Z_OBJ(EX(This))->ce != ce->constructor->common.scope && (ce->constructor->common.fn_flags & ZEND_ACC_PRIVATE)) {
+ zend_throw_error(NULL, "Cannot call private %s::__construct()", ZSTR_VAL(ce->name));
+ HANDLE_EXCEPTION();
+ }
+ fbc = ce->constructor;
+ if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!fbc->op_array.run_time_cache)) {
+ init_func_run_time_cache(&fbc->op_array);
+ }
+ }
+
+ object = NULL;
+ if (!(fbc->common.fn_flags & ZEND_ACC_STATIC)) {
+ if (Z_TYPE(EX(This)) == IS_OBJECT && instanceof_function(Z_OBJCE(EX(This)), ce)) {
+ object = Z_OBJ(EX(This));
+ ce = object->ce;
+ } else {
+ if (fbc->common.fn_flags & ZEND_ACC_ALLOW_STATIC) {
+ /* Allowed for PHP 4 compatibility. */
+ zend_error(
+ E_DEPRECATED,
+ "Non-static method %s::%s() should not be called statically",
+ ZSTR_VAL(fbc->common.scope->name), ZSTR_VAL(fbc->common.function_name));
+ if (UNEXPECTED(EG(exception) != NULL)) {
+ HANDLE_EXCEPTION();
+ }
+ } else {
+ /* An internal function assumes $this is present and won't check that.
+ * So PHP would crash by allowing the call. */
+ zend_throw_error(
+ zend_ce_error,
+ "Non-static method %s::%s() cannot be called statically",
+ ZSTR_VAL(fbc->common.scope->name), ZSTR_VAL(fbc->common.function_name));
+ HANDLE_EXCEPTION();
+ }
+ }
+ }
+
+ if (IS_UNUSED == IS_UNUSED) {
+ /* previous opcode is ZEND_FETCH_CLASS */
+ if ((opline->op1.num & ZEND_FETCH_CLASS_MASK) == ZEND_FETCH_CLASS_PARENT ||
+ (opline->op1.num & ZEND_FETCH_CLASS_MASK) == ZEND_FETCH_CLASS_SELF) {
+ if (Z_TYPE(EX(This)) == IS_OBJECT) {
+ ce = Z_OBJCE(EX(This));
+ } else {
+ ce = Z_CE(EX(This));
+ }
+ }
+ }
+
+ call = zend_vm_stack_push_call_frame(ZEND_CALL_NESTED_FUNCTION,
+ fbc, opline->extended_value, ce, object);
+ call->prev_execute_data = EX(call);
+ EX(call) = call;
+
+ ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
+}
+
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_ARRAY_SPEC_UNUSED_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
zval *array;
@@ -26399,104 +32044,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_ARRAY_SPEC_UNUSED_CV_HAND
}
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_UNSET_DIM_SPEC_UNUSED_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
-{
- USE_OPLINE
-
- zval *container;
- zval *offset;
- zend_ulong hval;
- zend_string *key;
-
- SAVE_OPLINE();
- container = _get_obj_zval_ptr_unused(execute_data);
- if (IS_UNUSED == IS_UNUSED && UNEXPECTED(Z_OBJ_P(container) == NULL)) {
- zend_throw_error(NULL, "Using $this when not in object context");
-
- HANDLE_EXCEPTION();
- }
- if (IS_UNUSED == IS_VAR && UNEXPECTED(container == NULL)) {
- zend_throw_error(NULL, "Cannot unset string offsets");
-
- HANDLE_EXCEPTION();
- }
- offset = _get_zval_ptr_cv_undef(execute_data, opline->op2.var);
-
- 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:
- if (EXPECTED(Z_TYPE_P(offset) == IS_STRING)) {
- key = Z_STR_P(offset);
- if (IS_CV != IS_CONST) {
- if (ZEND_HANDLE_NUMERIC(key, hval)) {
- goto num_index_dim;
- }
- }
-str_index_dim:
- if (ht == &EG(symbol_table)) {
- zend_delete_global_variable(key);
- } else {
- zend_hash_del(ht, key);
- }
- } 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 = ZSTR_EMPTY_ALLOC();
- goto str_index_dim;
- } else if (Z_TYPE_P(offset) == IS_FALSE) {
- hval = 0;
- goto num_index_dim;
- } else if (Z_TYPE_P(offset) == IS_TRUE) {
- hval = 1;
- goto num_index_dim;
- } else if (Z_TYPE_P(offset) == IS_RESOURCE) {
- hval = Z_RES_HANDLE_P(offset);
- goto num_index_dim;
- } else if (IS_CV == IS_CV && Z_TYPE_P(offset) == IS_UNDEF) {
- GET_OP2_UNDEF_CV(offset, BP_VAR_R);
- key = ZSTR_EMPTY_ALLOC();
- goto str_index_dim;
- } else {
- zend_error(E_WARNING, "Illegal offset type in unset");
- }
- 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;
- }
- }
- 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 || EXPECTED(Z_TYPE_P(container) == IS_OBJECT)) {
- if (UNEXPECTED(Z_OBJ_HT_P(container)->unset_dimension == NULL)) {
- zend_throw_error(NULL, "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_throw_error(NULL, "Cannot unset string offsets");
- }
- } while (0);
-
-
- ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
-}
-
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_UNSET_OBJ_SPEC_UNUSED_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
@@ -26506,16 +32053,11 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_UNSET_OBJ_SPEC_UNUSED_CV_HANDL
SAVE_OPLINE();
container = _get_obj_zval_ptr_unused(execute_data);
- if (IS_UNUSED == IS_UNUSED && UNEXPECTED(Z_OBJ_P(container) == NULL)) {
+ if (IS_UNUSED == IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
HANDLE_EXCEPTION();
}
- if (IS_UNUSED == IS_VAR && UNEXPECTED(container == NULL)) {
- zend_throw_error(NULL, "Cannot unset string offsets");
-
- HANDLE_EXCEPTION();
- }
offset = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var);
do {
@@ -26540,143 +32082,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_UNSET_OBJ_SPEC_UNUSED_CV_HANDL
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ISSET_ISEMPTY_DIM_OBJ_SPEC_UNUSED_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
-{
- USE_OPLINE
-
- zval *container;
- int result;
- zend_ulong hval;
- zval *offset;
-
- SAVE_OPLINE();
- container = _get_obj_zval_ptr_unused(execute_data);
-
- if (IS_UNUSED == IS_UNUSED && UNEXPECTED(Z_OBJ_P(container) == NULL)) {
- zend_throw_error(NULL, "Using $this when not in object context");
-
- HANDLE_EXCEPTION();
- }
-
- offset = _get_zval_ptr_cv_undef(execute_data, opline->op2.var);
-
- if (IS_UNUSED != IS_UNUSED && EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) {
- 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);
- if (IS_CV != IS_CONST) {
- if (ZEND_HANDLE_NUMERIC(str, hval)) {
- goto num_index_prop;
- }
- }
-str_index_prop:
- value = zend_hash_find_ind(ht, str);
- } else if (EXPECTED(Z_TYPE_P(offset) == IS_LONG)) {
- hval = Z_LVAL_P(offset);
-num_index_prop:
- value = zend_hash_index_find(ht, hval);
- } 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 = ZSTR_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 = ZSTR_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) {
- /* > IS_NULL means not IS_UNDEF and not IS_NULL */
- result = value != NULL && Z_TYPE_P(value) > IS_NULL &&
- (!Z_ISREF_P(value) || Z_TYPE_P(Z_REFVAL_P(value)) != IS_NULL);
- } else /* if (opline->extended_value & ZEND_ISEMPTY) */ {
- result = (value == NULL || !i_zend_is_true(value));
- }
- 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 =
- ((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");
- goto isset_not_found;
- }
- } else if (EXPECTED(Z_TYPE_P(container) == IS_STRING)) { /* string offsets */
- zend_long lval;
-
- 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))) {
- lval = zval_get_long(offset);
- goto isset_str_offset;
- }
- goto isset_not_found;
- }
- } 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);
- ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
-}
-
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ISSET_ISEMPTY_PROP_OBJ_SPEC_UNUSED_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
@@ -26688,7 +32093,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ISSET_ISEMPTY_PROP_OBJ_SPEC_UN
SAVE_OPLINE();
container = _get_obj_zval_ptr_unused(execute_data);
- if (IS_UNUSED == IS_UNUSED && UNEXPECTED(Z_OBJ_P(container) == NULL)) {
+ if (IS_UNUSED == IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
HANDLE_EXCEPTION();
@@ -26750,7 +32155,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_UNUSED_CV_HANDLER(Z
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) {
+ if (IS_UNUSED & (IS_CONST|IS_TMP_VAR)) {
zval *value;
zend_error(E_NOTICE, "Only variable references should be yielded by reference");
@@ -26758,25 +32163,19 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_UNUSED_CV_HANDLER(Z
value = NULL;
ZVAL_COPY_VALUE(&generator->value, value);
if (IS_UNUSED == IS_CONST) {
- if (UNEXPECTED(Z_OPT_COPYABLE(generator->value))) {
- zval_copy_ctor_func(&generator->value);
+ if (UNEXPECTED(Z_OPT_REFCOUNTED(generator->value))) {
+ Z_ADDREF(generator->value);
}
}
} else {
zval *value_ptr = NULL;
- if (IS_UNUSED == IS_VAR && UNEXPECTED(value_ptr == NULL)) {
- zend_throw_error(NULL, "Cannot yield string offsets by reference");
-
- HANDLE_EXCEPTION();
- }
-
/* If a function call result is yielded and the function did
* not return by reference we throw a notice. */
if (IS_UNUSED == IS_VAR &&
(value_ptr == &EG(uninitialized_zval) ||
(opline->extended_value == ZEND_RETURNS_FUNCTION &&
- !(Z_VAR_FLAGS_P(value_ptr) & IS_VAR_RET_REF)))) {
+ !Z_ISREF_P(value_ptr)))) {
zend_error(E_NOTICE, "Only variable references should be yielded by reference");
} else {
ZVAL_MAKE_REF(value_ptr);
@@ -26790,8 +32189,8 @@ 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_COPY_VALUE(&generator->value, value);
- if (UNEXPECTED(Z_OPT_COPYABLE(generator->value))) {
- zval_copy_ctor_func(&generator->value);
+ if (UNEXPECTED(Z_OPT_REFCOUNTED(generator->value))) {
+ Z_ADDREF(generator->value);
}
} else if (IS_UNUSED == IS_TMP_VAR) {
ZVAL_COPY_VALUE(&generator->value, value);
@@ -26818,8 +32217,8 @@ 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_COPY_VALUE(&generator->key, key);
- if (UNEXPECTED(Z_OPT_COPYABLE(generator->key))) {
- zval_copy_ctor_func(&generator->key);
+ if (UNEXPECTED(Z_OPT_REFCOUNTED(generator->key))) {
+ Z_ADDREF(generator->key);
}
} else if (IS_CV == IS_TMP_VAR) {
ZVAL_COPY_VALUE(&generator->key, key);
@@ -26876,7 +32275,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_binary_assign_op_obj_helper_SP
SAVE_OPLINE();
object = _get_obj_zval_ptr_unused(execute_data);
- if (IS_UNUSED == IS_UNUSED && UNEXPECTED(Z_OBJ_P(object) == NULL)) {
+ if (IS_UNUSED == IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
FREE_UNFETCHED_OP((opline+1)->op1_type, (opline+1)->op1.var);
zval_ptr_dtor_nogc(EX_VAR(opline->op2.var));
@@ -26885,13 +32284,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_binary_assign_op_obj_helper_SP
property = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2);
- if (IS_UNUSED == IS_VAR && UNEXPECTED(object == NULL)) {
- zend_throw_error(NULL, "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();
- }
-
do {
value = get_zval_ptr_r((opline+1)->op1_type, (opline+1)->op1, execute_data, &free_op_data1);
@@ -26909,7 +32301,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_binary_assign_op_obj_helper_SP
/* here we are sure we are dealing with an object */
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)) {
- if (UNEXPECTED(zptr == &EG(error_zval))) {
+ if (UNEXPECTED(Z_ISERROR_P(zptr))) {
if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
ZVAL_NULL(EX_VAR(opline->result.var));
}
@@ -26934,312 +32326,229 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_binary_assign_op_obj_helper_SP
ZEND_VM_NEXT_OPCODE_EX(1, 2);
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_binary_assign_op_dim_helper_SPEC_UNUSED_TMPVAR(binary_op_type binary_op ZEND_OPCODE_HANDLER_ARGS_DC)
-{
- USE_OPLINE
- zend_free_op free_op2, free_op_data1;
- zval *var_ptr, rv;
- zval *value, *container, *dim;
-
- SAVE_OPLINE();
- container = _get_obj_zval_ptr_unused(execute_data);
- if (IS_UNUSED == IS_UNUSED && UNEXPECTED(Z_OBJ_P(container) == NULL)) {
- zend_throw_error(NULL, "Using $this when not in object context");
- FREE_UNFETCHED_OP((opline+1)->op1_type, (opline+1)->op1.var);
- zval_ptr_dtor_nogc(EX_VAR(opline->op2.var));
- HANDLE_EXCEPTION();
- }
- if (IS_UNUSED == IS_VAR && UNEXPECTED(container == NULL)) {
- zend_throw_error(NULL, "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();
- }
-
- dim = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2);
-
- do {
- if (IS_UNUSED == IS_UNUSED || UNEXPECTED(Z_TYPE_P(container) != IS_ARRAY)) {
- if (IS_UNUSED != IS_UNUSED) {
- ZVAL_DEREF(container);
- }
- if (IS_UNUSED == IS_UNUSED || EXPECTED(Z_TYPE_P(container) == IS_OBJECT)) {
- value = get_zval_ptr_r((opline+1)->op1_type, (opline+1)->op1, execute_data, &free_op_data1);
- zend_binary_assign_op_obj_dim(container, dim, value, UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL, binary_op);
- break;
- }
- }
-
- zend_fetch_dimension_address_RW(&rv, container, dim, (IS_TMP_VAR|IS_VAR));
- value = get_zval_ptr_r((opline+1)->op1_type, (opline+1)->op1, execute_data, &free_op_data1);
- ZEND_ASSERT(Z_TYPE(rv) == IS_INDIRECT);
- var_ptr = Z_INDIRECT(rv);
-
- if (UNEXPECTED(var_ptr == NULL)) {
- zend_throw_error(NULL, "Cannot use assign-op operators with overloaded objects nor string offsets");
- zval_ptr_dtor_nogc(free_op2);
- FREE_OP(free_op_data1);
-
- HANDLE_EXCEPTION();
- }
-
- if (UNEXPECTED(var_ptr == &EG(error_zval))) {
- if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
- ZVAL_NULL(EX_VAR(opline->result.var));
- }
- } else {
- ZVAL_DEREF(var_ptr);
- SEPARATE_ZVAL_NOREF(var_ptr);
-
- binary_op(var_ptr, var_ptr, value);
-
- if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
- ZVAL_COPY(EX_VAR(opline->result.var), var_ptr);
- }
- }
- } while (0);
-
- zval_ptr_dtor_nogc(free_op2);
- FREE_OP(free_op_data1);
-
- ZEND_VM_NEXT_OPCODE_EX(1, 2);
-}
-
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_ADD_SPEC_UNUSED_TMPVAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_ADD_SPEC_UNUSED_TMPVAR_OBJ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
#if 0 || ((IS_TMP_VAR|IS_VAR) != IS_UNUSED)
USE_OPLINE
# if 0 || (IS_UNUSED != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_UNUSED_TMPVAR(add_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
-# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_UNUSED_TMPVAR(add_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_UNUSED_TMPVAR(add_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_UNUSED_TMPVAR(add_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#else
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_UNUSED_TMPVAR(add_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#endif
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_SUB_SPEC_UNUSED_TMPVAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_SUB_SPEC_UNUSED_TMPVAR_OBJ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
#if 0 || ((IS_TMP_VAR|IS_VAR) != IS_UNUSED)
USE_OPLINE
# if 0 || (IS_UNUSED != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_UNUSED_TMPVAR(sub_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
-# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_UNUSED_TMPVAR(sub_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_UNUSED_TMPVAR(sub_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_UNUSED_TMPVAR(sub_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#else
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_UNUSED_TMPVAR(sub_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#endif
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_MUL_SPEC_UNUSED_TMPVAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_MUL_SPEC_UNUSED_TMPVAR_OBJ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
#if 0 || ((IS_TMP_VAR|IS_VAR) != IS_UNUSED)
USE_OPLINE
# if 0 || (IS_UNUSED != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_UNUSED_TMPVAR(mul_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
-# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_UNUSED_TMPVAR(mul_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_UNUSED_TMPVAR(mul_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_UNUSED_TMPVAR(mul_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#else
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_UNUSED_TMPVAR(mul_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#endif
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIV_SPEC_UNUSED_TMPVAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIV_SPEC_UNUSED_TMPVAR_OBJ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
#if 0 || ((IS_TMP_VAR|IS_VAR) != IS_UNUSED)
USE_OPLINE
# if 0 || (IS_UNUSED != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_UNUSED_TMPVAR(div_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
-# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_UNUSED_TMPVAR(div_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_UNUSED_TMPVAR(div_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_UNUSED_TMPVAR(div_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#else
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_UNUSED_TMPVAR(div_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#endif
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_MOD_SPEC_UNUSED_TMPVAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_MOD_SPEC_UNUSED_TMPVAR_OBJ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
#if 0 || ((IS_TMP_VAR|IS_VAR) != IS_UNUSED)
USE_OPLINE
# if 0 || (IS_UNUSED != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_UNUSED_TMPVAR(mod_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
-# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_UNUSED_TMPVAR(mod_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_UNUSED_TMPVAR(mod_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_UNUSED_TMPVAR(mod_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#else
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_UNUSED_TMPVAR(mod_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#endif
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_SL_SPEC_UNUSED_TMPVAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_SL_SPEC_UNUSED_TMPVAR_OBJ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
#if 0 || ((IS_TMP_VAR|IS_VAR) != IS_UNUSED)
USE_OPLINE
# if 0 || (IS_UNUSED != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_UNUSED_TMPVAR(shift_left_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
-# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_UNUSED_TMPVAR(shift_left_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_UNUSED_TMPVAR(shift_left_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_UNUSED_TMPVAR(shift_left_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#else
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_UNUSED_TMPVAR(shift_left_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#endif
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_SR_SPEC_UNUSED_TMPVAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_SR_SPEC_UNUSED_TMPVAR_OBJ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
#if 0 || ((IS_TMP_VAR|IS_VAR) != IS_UNUSED)
USE_OPLINE
# if 0 || (IS_UNUSED != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_UNUSED_TMPVAR(shift_right_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
-# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_UNUSED_TMPVAR(shift_right_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_UNUSED_TMPVAR(shift_right_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_UNUSED_TMPVAR(shift_right_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#else
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_UNUSED_TMPVAR(shift_right_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#endif
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_CONCAT_SPEC_UNUSED_TMPVAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_CONCAT_SPEC_UNUSED_TMPVAR_OBJ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
#if 0 || ((IS_TMP_VAR|IS_VAR) != IS_UNUSED)
USE_OPLINE
# if 0 || (IS_UNUSED != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_UNUSED_TMPVAR(concat_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
-# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_UNUSED_TMPVAR(concat_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_UNUSED_TMPVAR(concat_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_UNUSED_TMPVAR(concat_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#else
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_UNUSED_TMPVAR(concat_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#endif
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_BW_OR_SPEC_UNUSED_TMPVAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_BW_OR_SPEC_UNUSED_TMPVAR_OBJ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
#if 0 || ((IS_TMP_VAR|IS_VAR) != IS_UNUSED)
USE_OPLINE
# if 0 || (IS_UNUSED != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_UNUSED_TMPVAR(bitwise_or_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
-# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_UNUSED_TMPVAR(bitwise_or_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_UNUSED_TMPVAR(bitwise_or_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_UNUSED_TMPVAR(bitwise_or_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#else
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_UNUSED_TMPVAR(bitwise_or_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#endif
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_BW_AND_SPEC_UNUSED_TMPVAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_BW_AND_SPEC_UNUSED_TMPVAR_OBJ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
#if 0 || ((IS_TMP_VAR|IS_VAR) != IS_UNUSED)
USE_OPLINE
# if 0 || (IS_UNUSED != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_UNUSED_TMPVAR(bitwise_and_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
-# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_UNUSED_TMPVAR(bitwise_and_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_UNUSED_TMPVAR(bitwise_and_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_UNUSED_TMPVAR(bitwise_and_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#else
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_UNUSED_TMPVAR(bitwise_and_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#endif
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_BW_XOR_SPEC_UNUSED_TMPVAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_BW_XOR_SPEC_UNUSED_TMPVAR_OBJ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
#if 0 || ((IS_TMP_VAR|IS_VAR) != IS_UNUSED)
USE_OPLINE
# if 0 || (IS_UNUSED != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_UNUSED_TMPVAR(bitwise_xor_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
-# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_UNUSED_TMPVAR(bitwise_xor_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_UNUSED_TMPVAR(bitwise_xor_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_UNUSED_TMPVAR(bitwise_xor_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#else
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_UNUSED_TMPVAR(bitwise_xor_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#endif
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_POW_SPEC_UNUSED_TMPVAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_POW_SPEC_UNUSED_TMPVAR_OBJ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
#if 0 || ((IS_TMP_VAR|IS_VAR) != IS_UNUSED)
USE_OPLINE
# if 0 || (IS_UNUSED != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_UNUSED_TMPVAR(pow_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
-# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_UNUSED_TMPVAR(pow_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_UNUSED_TMPVAR(pow_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_UNUSED_TMPVAR(pow_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#else
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_UNUSED_TMPVAR(pow_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#endif
@@ -27256,7 +32565,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_pre_incdec_property_helper_SPE
SAVE_OPLINE();
object = _get_obj_zval_ptr_unused(execute_data);
- if (IS_UNUSED == IS_UNUSED && UNEXPECTED(Z_OBJ_P(object) == NULL)) {
+ if (IS_UNUSED == IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
zval_ptr_dtor_nogc(EX_VAR(opline->op2.var));
HANDLE_EXCEPTION();
@@ -27264,12 +32573,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_pre_incdec_property_helper_SPE
property = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2);
- if (IS_UNUSED == IS_VAR && UNEXPECTED(object == NULL)) {
- zend_throw_error(NULL, "Cannot increment/decrement overloaded objects nor string offsets");
- zval_ptr_dtor_nogc(free_op2);
- HANDLE_EXCEPTION();
- }
-
do {
if (IS_UNUSED != IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) != IS_OBJECT)) {
ZVAL_DEREF(object);
@@ -27285,7 +32588,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_pre_incdec_property_helper_SPE
/* here we are sure we are dealing with an object */
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)) {
- if (UNEXPECTED(zptr == &EG(error_zval))) {
+ if (UNEXPECTED(Z_ISERROR_P(zptr))) {
if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
ZVAL_NULL(EX_VAR(opline->result.var));
}
@@ -27341,7 +32644,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_post_incdec_property_helper_SP
SAVE_OPLINE();
object = _get_obj_zval_ptr_unused(execute_data);
- if (IS_UNUSED == IS_UNUSED && UNEXPECTED(Z_OBJ_P(object) == NULL)) {
+ if (IS_UNUSED == IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
zval_ptr_dtor_nogc(EX_VAR(opline->op2.var));
HANDLE_EXCEPTION();
@@ -27349,12 +32652,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_post_incdec_property_helper_SP
property = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2);
- if (IS_UNUSED == IS_VAR && UNEXPECTED(object == NULL)) {
- zend_throw_error(NULL, "Cannot increment/decrement overloaded objects nor string offsets");
- zval_ptr_dtor_nogc(free_op2);
- HANDLE_EXCEPTION();
- }
-
do {
if (IS_UNUSED != IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) != IS_OBJECT)) {
ZVAL_DEREF(object);
@@ -27369,7 +32666,7 @@ 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)) {
- if (UNEXPECTED(zptr == &EG(error_zval))) {
+ if (UNEXPECTED(Z_ISERROR_P(zptr))) {
ZVAL_NULL(EX_VAR(opline->result.var));
} else {
if (EXPECTED(Z_TYPE_P(zptr) == IS_LONG)) {
@@ -27421,7 +32718,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_R_SPEC_UNUSED_TMPVAR
SAVE_OPLINE();
container = _get_obj_zval_ptr_unused(execute_data);
- if (IS_UNUSED == IS_UNUSED && UNEXPECTED(Z_OBJ_P(container) == NULL)) {
+ if (IS_UNUSED == IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
zval_ptr_dtor_nogc(EX_VAR(opline->op2.var));
HANDLE_EXCEPTION();
@@ -27494,21 +32791,16 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_W_SPEC_UNUSED_TMPVAR
property = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2);
container = _get_obj_zval_ptr_unused(execute_data);
- if (IS_UNUSED == IS_UNUSED && UNEXPECTED(Z_OBJ_P(container) == NULL)) {
+ if (IS_UNUSED == IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
zval_ptr_dtor_nogc(free_op2);
HANDLE_EXCEPTION();
}
- if (IS_UNUSED == IS_VAR && UNEXPECTED(container == NULL)) {
- zend_throw_error(NULL, "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);
zval_ptr_dtor_nogc(free_op2);
if (IS_UNUSED == IS_VAR && READY_TO_DESTROY(free_op1)) {
- EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var), 0);
+ EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var));
}
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
@@ -27525,20 +32817,15 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_RW_SPEC_UNUSED_TMPVA
property = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2);
container = _get_obj_zval_ptr_unused(execute_data);
- if (IS_UNUSED == IS_UNUSED && UNEXPECTED(Z_OBJ_P(container) == NULL)) {
+ if (IS_UNUSED == IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
zval_ptr_dtor_nogc(free_op2);
HANDLE_EXCEPTION();
}
- if (IS_UNUSED == IS_VAR && UNEXPECTED(container == NULL)) {
- zend_throw_error(NULL, "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);
zval_ptr_dtor_nogc(free_op2);
if (IS_UNUSED == IS_VAR && READY_TO_DESTROY(free_op1)) {
- EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var), 0);
+ EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var));
}
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
@@ -27555,7 +32842,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_IS_SPEC_UNUSED_TMPVA
SAVE_OPLINE();
container = _get_obj_zval_ptr_unused(execute_data);
- if (IS_UNUSED == IS_UNUSED && UNEXPECTED(Z_OBJ_P(container) == NULL)) {
+ if (IS_UNUSED == IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
zval_ptr_dtor_nogc(EX_VAR(opline->op2.var));
HANDLE_EXCEPTION();
@@ -27631,26 +32918,21 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_FUNC_ARG_SPEC_UNUSED
property = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2);
container = _get_obj_zval_ptr_unused(execute_data);
- if (IS_UNUSED == IS_UNUSED && UNEXPECTED(Z_OBJ_P(container) == NULL)) {
+ if (IS_UNUSED == IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
zval_ptr_dtor_nogc(free_op2);
HANDLE_EXCEPTION();
}
- if (IS_UNUSED == IS_CONST || IS_UNUSED == IS_TMP_VAR) {
+ if ((IS_UNUSED & (IS_CONST|IS_TMP_VAR))) {
zend_throw_error(NULL, "Cannot use temporary expression in write context");
zval_ptr_dtor_nogc(free_op2);
HANDLE_EXCEPTION();
}
- if (IS_UNUSED == IS_VAR && UNEXPECTED(container == NULL)) {
- zend_throw_error(NULL, "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);
zval_ptr_dtor_nogc(free_op2);
if (IS_UNUSED == IS_VAR && READY_TO_DESTROY(free_op1)) {
- EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var), 0);
+ EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var));
}
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
@@ -27668,7 +32950,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_UNSET_SPEC_UNUSED_TM
SAVE_OPLINE();
container = _get_obj_zval_ptr_unused(execute_data);
- if (IS_UNUSED == IS_UNUSED && UNEXPECTED(Z_OBJ_P(container) == NULL)) {
+ if (IS_UNUSED == IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
zval_ptr_dtor_nogc(EX_VAR(opline->op2.var));
HANDLE_EXCEPTION();
@@ -27676,44 +32958,633 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_UNSET_SPEC_UNUSED_TM
property = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2);
- if (IS_UNUSED == IS_VAR && UNEXPECTED(container == NULL)) {
- zend_throw_error(NULL, "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);
zval_ptr_dtor_nogc(free_op2);
if (IS_UNUSED == IS_VAR && READY_TO_DESTROY(free_op1)) {
- EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var), 0);
+ EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var));
}
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_TMPVAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_TMPVAR_OP_DATA_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
zend_free_op free_op2;
- zval *object;
- zval *property_name;
+ zval *object, *property_name, *value, tmp;
SAVE_OPLINE();
object = _get_obj_zval_ptr_unused(execute_data);
- if (IS_UNUSED == IS_UNUSED && UNEXPECTED(Z_OBJ_P(object) == NULL)) {
+ if (IS_UNUSED == IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
zval_ptr_dtor_nogc(EX_VAR(opline->op2.var));
HANDLE_EXCEPTION();
}
property_name = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2);
+ value = EX_CONSTANT((opline+1)->op1);
- if (IS_UNUSED == IS_VAR && UNEXPECTED(object == NULL)) {
- zend_throw_error(NULL, "Cannot use string offset as an array");
- zval_ptr_dtor_nogc(free_op2);
+ if (IS_UNUSED != IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) != IS_OBJECT)) {
+ do {
+ if (Z_ISREF_P(object)) {
+ object = Z_REFVAL_P(object);
+ if (EXPECTED(Z_TYPE_P(object) == IS_OBJECT)) {
+ break;
+ }
+ }
+ if (EXPECTED(Z_TYPE_P(object) <= IS_FALSE ||
+ (Z_TYPE_P(object) == IS_STRING && Z_STRLEN_P(object) == 0))) {
+ zend_object *obj;
+
+ zval_ptr_dtor(object);
+ object_init(object);
+ Z_ADDREF_P(object);
+ obj = Z_OBJ_P(object);
+ zend_error(E_WARNING, "Creating default object from empty value");
+ if (GC_REFCOUNT(obj) == 1) {
+ /* the enclosing container was deleted, obj is unreferenced */
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+
+ OBJ_RELEASE(obj);
+ goto exit_assign_obj;
+ }
+ Z_DELREF_P(object);
+ } else {
+ if (IS_UNUSED != IS_VAR || EXPECTED(!Z_ISERROR_P(object))) {
+ zend_error(E_WARNING, "Attempt to assign property of non-object");
+ }
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+
+ goto exit_assign_obj;
+ }
+ } while (0);
+ }
+
+ if ((IS_TMP_VAR|IS_VAR) == IS_CONST &&
+ EXPECTED(Z_OBJCE_P(object) == CACHED_PTR(Z_CACHE_SLOT_P(property_name)))) {
+ uint32_t prop_offset = (uint32_t)(intptr_t)CACHED_PTR(Z_CACHE_SLOT_P(property_name) + sizeof(void*));
+ zend_object *zobj = Z_OBJ_P(object);
+ zval *property;
+
+ if (EXPECTED(prop_offset != (uint32_t)ZEND_DYNAMIC_PROPERTY_OFFSET)) {
+ property = OBJ_PROP(zobj, prop_offset);
+ if (Z_TYPE_P(property) != IS_UNDEF) {
+fast_assign_obj:
+ value = zend_assign_to_variable(property, value, IS_CONST);
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+ goto exit_assign_obj;
+ }
+ } else {
+ if (EXPECTED(zobj->properties != NULL)) {
+ if (UNEXPECTED(GC_REFCOUNT(zobj->properties) > 1)) {
+ if (EXPECTED(!(GC_FLAGS(zobj->properties) & IS_ARRAY_IMMUTABLE))) {
+ GC_REFCOUNT(zobj->properties)--;
+ }
+ zobj->properties = zend_array_dup(zobj->properties);
+ }
+ property = zend_hash_find(zobj->properties, Z_STR_P(property_name));
+ if (property) {
+ goto fast_assign_obj;
+ }
+ }
+
+ if (!zobj->ce->__set) {
+
+ if (EXPECTED(zobj->properties == NULL)) {
+ rebuild_object_properties(zobj);
+ }
+ if (IS_CONST == IS_CONST) {
+ if (UNEXPECTED(Z_OPT_REFCOUNTED_P(value))) {
+ Z_ADDREF_P(value);
+ }
+ } else if (IS_CONST != IS_TMP_VAR) {
+ if (Z_ISREF_P(value)) {
+ if (IS_CONST == IS_VAR) {
+ zend_reference *ref = Z_REF_P(value);
+ if (--GC_REFCOUNT(ref) == 0) {
+ ZVAL_COPY_VALUE(&tmp, Z_REFVAL_P(value));
+ efree_size(ref, sizeof(zend_reference));
+ value = &tmp;
+ } else {
+ value = Z_REFVAL_P(value);
+ if (Z_REFCOUNTED_P(value)) {
+ Z_ADDREF_P(value);
+ }
+ }
+ } else {
+ value = Z_REFVAL_P(value);
+ if (Z_REFCOUNTED_P(value)) {
+ Z_ADDREF_P(value);
+ }
+ }
+ } else if (IS_CONST == IS_CV && Z_REFCOUNTED_P(value)) {
+ Z_ADDREF_P(value);
+ }
+ }
+ zend_hash_add_new(zobj->properties, Z_STR_P(property_name), value);
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+ goto exit_assign_obj;
+ }
+ }
+ }
+
+ if (!Z_OBJ_HT_P(object)->write_property) {
+ zend_error(E_WARNING, "Attempt to assign property of non-object");
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+
+ goto exit_assign_obj;
+ }
+
+ if (IS_CONST == IS_CV || IS_CONST == IS_VAR) {
+ ZVAL_DEREF(value);
+ }
+
+ Z_OBJ_HT_P(object)->write_property(object, property_name, value, ((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property_name)) : NULL);
+
+ if (UNEXPECTED(RETURN_VALUE_USED(opline)) && EXPECTED(!EG(exception))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+
+exit_assign_obj:
+ zval_ptr_dtor_nogc(free_op2);
+
+ /* assign_obj has two opcodes! */
+ ZEND_VM_NEXT_OPCODE_EX(1, 2);
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_TMPVAR_OP_DATA_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zend_free_op free_op2, free_op_data;
+ zval *object, *property_name, *value, tmp;
+
+ SAVE_OPLINE();
+ object = _get_obj_zval_ptr_unused(execute_data);
+
+ if (IS_UNUSED == IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) == IS_UNDEF)) {
+ zend_throw_error(NULL, "Using $this when not in object context");
+ zval_ptr_dtor_nogc(EX_VAR(opline->op2.var));
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));
+
+ property_name = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2);
+ value = _get_zval_ptr_tmp((opline+1)->op1.var, execute_data, &free_op_data);
+
+ if (IS_UNUSED != IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) != IS_OBJECT)) {
+ do {
+ if (Z_ISREF_P(object)) {
+ object = Z_REFVAL_P(object);
+ if (EXPECTED(Z_TYPE_P(object) == IS_OBJECT)) {
+ break;
+ }
+ }
+ if (EXPECTED(Z_TYPE_P(object) <= IS_FALSE ||
+ (Z_TYPE_P(object) == IS_STRING && Z_STRLEN_P(object) == 0))) {
+ zend_object *obj;
+
+ zval_ptr_dtor(object);
+ object_init(object);
+ Z_ADDREF_P(object);
+ obj = Z_OBJ_P(object);
+ zend_error(E_WARNING, "Creating default object from empty value");
+ if (GC_REFCOUNT(obj) == 1) {
+ /* the enclosing container was deleted, obj is unreferenced */
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+ zval_ptr_dtor_nogc(free_op_data);
+ OBJ_RELEASE(obj);
+ goto exit_assign_obj;
+ }
+ Z_DELREF_P(object);
+ } else {
+ if (IS_UNUSED != IS_VAR || EXPECTED(!Z_ISERROR_P(object))) {
+ zend_error(E_WARNING, "Attempt to assign property of non-object");
+ }
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+ zval_ptr_dtor_nogc(free_op_data);
+ goto exit_assign_obj;
+ }
+ } while (0);
+ }
+
+ if ((IS_TMP_VAR|IS_VAR) == IS_CONST &&
+ EXPECTED(Z_OBJCE_P(object) == CACHED_PTR(Z_CACHE_SLOT_P(property_name)))) {
+ uint32_t prop_offset = (uint32_t)(intptr_t)CACHED_PTR(Z_CACHE_SLOT_P(property_name) + sizeof(void*));
+ zend_object *zobj = Z_OBJ_P(object);
+ zval *property;
+
+ if (EXPECTED(prop_offset != (uint32_t)ZEND_DYNAMIC_PROPERTY_OFFSET)) {
+ property = OBJ_PROP(zobj, prop_offset);
+ if (Z_TYPE_P(property) != IS_UNDEF) {
+fast_assign_obj:
+ value = zend_assign_to_variable(property, value, IS_TMP_VAR);
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+ goto exit_assign_obj;
+ }
+ } else {
+ if (EXPECTED(zobj->properties != NULL)) {
+ if (UNEXPECTED(GC_REFCOUNT(zobj->properties) > 1)) {
+ if (EXPECTED(!(GC_FLAGS(zobj->properties) & IS_ARRAY_IMMUTABLE))) {
+ GC_REFCOUNT(zobj->properties)--;
+ }
+ zobj->properties = zend_array_dup(zobj->properties);
+ }
+ property = zend_hash_find(zobj->properties, Z_STR_P(property_name));
+ if (property) {
+ goto fast_assign_obj;
+ }
+ }
+
+ if (!zobj->ce->__set) {
+
+ if (EXPECTED(zobj->properties == NULL)) {
+ rebuild_object_properties(zobj);
+ }
+ if (IS_TMP_VAR == IS_CONST) {
+ if (UNEXPECTED(Z_OPT_REFCOUNTED_P(value))) {
+ Z_ADDREF_P(value);
+ }
+ } else if (IS_TMP_VAR != IS_TMP_VAR) {
+ if (Z_ISREF_P(value)) {
+ if (IS_TMP_VAR == IS_VAR) {
+ zend_reference *ref = Z_REF_P(value);
+ if (--GC_REFCOUNT(ref) == 0) {
+ ZVAL_COPY_VALUE(&tmp, Z_REFVAL_P(value));
+ efree_size(ref, sizeof(zend_reference));
+ value = &tmp;
+ } else {
+ value = Z_REFVAL_P(value);
+ if (Z_REFCOUNTED_P(value)) {
+ Z_ADDREF_P(value);
+ }
+ }
+ } else {
+ value = Z_REFVAL_P(value);
+ if (Z_REFCOUNTED_P(value)) {
+ Z_ADDREF_P(value);
+ }
+ }
+ } else if (IS_TMP_VAR == IS_CV && Z_REFCOUNTED_P(value)) {
+ Z_ADDREF_P(value);
+ }
+ }
+ zend_hash_add_new(zobj->properties, Z_STR_P(property_name), value);
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+ goto exit_assign_obj;
+ }
+ }
+ }
+
+ if (!Z_OBJ_HT_P(object)->write_property) {
+ zend_error(E_WARNING, "Attempt to assign property of non-object");
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+ zval_ptr_dtor_nogc(free_op_data);
+ goto exit_assign_obj;
+ }
+
+ if (IS_TMP_VAR == IS_CV || IS_TMP_VAR == IS_VAR) {
+ ZVAL_DEREF(value);
+ }
+
+ Z_OBJ_HT_P(object)->write_property(object, property_name, value, ((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property_name)) : NULL);
+
+ if (UNEXPECTED(RETURN_VALUE_USED(opline)) && EXPECTED(!EG(exception))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+ zval_ptr_dtor_nogc(free_op_data);
+exit_assign_obj:
+ zval_ptr_dtor_nogc(free_op2);
+
+ /* assign_obj has two opcodes! */
+ ZEND_VM_NEXT_OPCODE_EX(1, 2);
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_TMPVAR_OP_DATA_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zend_free_op free_op2, free_op_data;
+ zval *object, *property_name, *value, tmp;
+
+ SAVE_OPLINE();
+ object = _get_obj_zval_ptr_unused(execute_data);
+
+ if (IS_UNUSED == IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) == IS_UNDEF)) {
+ zend_throw_error(NULL, "Using $this when not in object context");
+ zval_ptr_dtor_nogc(EX_VAR(opline->op2.var));
+ HANDLE_EXCEPTION();
+ }
+
+ property_name = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2);
+ value = _get_zval_ptr_var((opline+1)->op1.var, execute_data, &free_op_data);
+
+ if (IS_UNUSED != IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) != IS_OBJECT)) {
+ do {
+ if (Z_ISREF_P(object)) {
+ object = Z_REFVAL_P(object);
+ if (EXPECTED(Z_TYPE_P(object) == IS_OBJECT)) {
+ break;
+ }
+ }
+ if (EXPECTED(Z_TYPE_P(object) <= IS_FALSE ||
+ (Z_TYPE_P(object) == IS_STRING && Z_STRLEN_P(object) == 0))) {
+ zend_object *obj;
+
+ zval_ptr_dtor(object);
+ object_init(object);
+ Z_ADDREF_P(object);
+ obj = Z_OBJ_P(object);
+ zend_error(E_WARNING, "Creating default object from empty value");
+ if (GC_REFCOUNT(obj) == 1) {
+ /* the enclosing container was deleted, obj is unreferenced */
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+ zval_ptr_dtor_nogc(free_op_data);
+ OBJ_RELEASE(obj);
+ goto exit_assign_obj;
+ }
+ Z_DELREF_P(object);
+ } else {
+ if (IS_UNUSED != IS_VAR || EXPECTED(!Z_ISERROR_P(object))) {
+ zend_error(E_WARNING, "Attempt to assign property of non-object");
+ }
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+ zval_ptr_dtor_nogc(free_op_data);
+ goto exit_assign_obj;
+ }
+ } while (0);
+ }
+
+ if ((IS_TMP_VAR|IS_VAR) == IS_CONST &&
+ EXPECTED(Z_OBJCE_P(object) == CACHED_PTR(Z_CACHE_SLOT_P(property_name)))) {
+ uint32_t prop_offset = (uint32_t)(intptr_t)CACHED_PTR(Z_CACHE_SLOT_P(property_name) + sizeof(void*));
+ zend_object *zobj = Z_OBJ_P(object);
+ zval *property;
+
+ if (EXPECTED(prop_offset != (uint32_t)ZEND_DYNAMIC_PROPERTY_OFFSET)) {
+ property = OBJ_PROP(zobj, prop_offset);
+ if (Z_TYPE_P(property) != IS_UNDEF) {
+fast_assign_obj:
+ value = zend_assign_to_variable(property, value, IS_VAR);
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+ goto exit_assign_obj;
+ }
+ } else {
+ if (EXPECTED(zobj->properties != NULL)) {
+ if (UNEXPECTED(GC_REFCOUNT(zobj->properties) > 1)) {
+ if (EXPECTED(!(GC_FLAGS(zobj->properties) & IS_ARRAY_IMMUTABLE))) {
+ GC_REFCOUNT(zobj->properties)--;
+ }
+ zobj->properties = zend_array_dup(zobj->properties);
+ }
+ property = zend_hash_find(zobj->properties, Z_STR_P(property_name));
+ if (property) {
+ goto fast_assign_obj;
+ }
+ }
+
+ if (!zobj->ce->__set) {
+
+ if (EXPECTED(zobj->properties == NULL)) {
+ rebuild_object_properties(zobj);
+ }
+ if (IS_VAR == IS_CONST) {
+ if (UNEXPECTED(Z_OPT_REFCOUNTED_P(value))) {
+ Z_ADDREF_P(value);
+ }
+ } else if (IS_VAR != IS_TMP_VAR) {
+ if (Z_ISREF_P(value)) {
+ if (IS_VAR == IS_VAR) {
+ zend_reference *ref = Z_REF_P(value);
+ if (--GC_REFCOUNT(ref) == 0) {
+ ZVAL_COPY_VALUE(&tmp, Z_REFVAL_P(value));
+ efree_size(ref, sizeof(zend_reference));
+ value = &tmp;
+ } else {
+ value = Z_REFVAL_P(value);
+ if (Z_REFCOUNTED_P(value)) {
+ Z_ADDREF_P(value);
+ }
+ }
+ } else {
+ value = Z_REFVAL_P(value);
+ if (Z_REFCOUNTED_P(value)) {
+ Z_ADDREF_P(value);
+ }
+ }
+ } else if (IS_VAR == IS_CV && Z_REFCOUNTED_P(value)) {
+ Z_ADDREF_P(value);
+ }
+ }
+ zend_hash_add_new(zobj->properties, Z_STR_P(property_name), value);
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+ goto exit_assign_obj;
+ }
+ }
+ }
+
+ if (!Z_OBJ_HT_P(object)->write_property) {
+ zend_error(E_WARNING, "Attempt to assign property of non-object");
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+ zval_ptr_dtor_nogc(free_op_data);
+ goto exit_assign_obj;
+ }
+
+ if (IS_VAR == IS_CV || IS_VAR == IS_VAR) {
+ ZVAL_DEREF(value);
+ }
+
+ Z_OBJ_HT_P(object)->write_property(object, property_name, value, ((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property_name)) : NULL);
+
+ if (UNEXPECTED(RETURN_VALUE_USED(opline)) && EXPECTED(!EG(exception))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+ zval_ptr_dtor_nogc(free_op_data);
+exit_assign_obj:
+ zval_ptr_dtor_nogc(free_op2);
+
+ /* assign_obj has two opcodes! */
+ ZEND_VM_NEXT_OPCODE_EX(1, 2);
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_TMPVAR_OP_DATA_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zend_free_op free_op2;
+ zval *object, *property_name, *value, tmp;
+
+ SAVE_OPLINE();
+ object = _get_obj_zval_ptr_unused(execute_data);
+
+ if (IS_UNUSED == IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) == IS_UNDEF)) {
+ zend_throw_error(NULL, "Using $this when not in object context");
+ zval_ptr_dtor_nogc(EX_VAR(opline->op2.var));
+ HANDLE_EXCEPTION();
+ }
+
+ property_name = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2);
+ value = _get_zval_ptr_cv_BP_VAR_R(execute_data, (opline+1)->op1.var);
+
+ if (IS_UNUSED != IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) != IS_OBJECT)) {
+ do {
+ if (Z_ISREF_P(object)) {
+ object = Z_REFVAL_P(object);
+ if (EXPECTED(Z_TYPE_P(object) == IS_OBJECT)) {
+ break;
+ }
+ }
+ if (EXPECTED(Z_TYPE_P(object) <= IS_FALSE ||
+ (Z_TYPE_P(object) == IS_STRING && Z_STRLEN_P(object) == 0))) {
+ zend_object *obj;
+
+ zval_ptr_dtor(object);
+ object_init(object);
+ Z_ADDREF_P(object);
+ obj = Z_OBJ_P(object);
+ zend_error(E_WARNING, "Creating default object from empty value");
+ if (GC_REFCOUNT(obj) == 1) {
+ /* the enclosing container was deleted, obj is unreferenced */
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+
+ OBJ_RELEASE(obj);
+ goto exit_assign_obj;
+ }
+ Z_DELREF_P(object);
+ } else {
+ if (IS_UNUSED != IS_VAR || EXPECTED(!Z_ISERROR_P(object))) {
+ zend_error(E_WARNING, "Attempt to assign property of non-object");
+ }
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+
+ goto exit_assign_obj;
+ }
+ } while (0);
+ }
+
+ if ((IS_TMP_VAR|IS_VAR) == IS_CONST &&
+ EXPECTED(Z_OBJCE_P(object) == CACHED_PTR(Z_CACHE_SLOT_P(property_name)))) {
+ uint32_t prop_offset = (uint32_t)(intptr_t)CACHED_PTR(Z_CACHE_SLOT_P(property_name) + sizeof(void*));
+ zend_object *zobj = Z_OBJ_P(object);
+ zval *property;
+
+ if (EXPECTED(prop_offset != (uint32_t)ZEND_DYNAMIC_PROPERTY_OFFSET)) {
+ property = OBJ_PROP(zobj, prop_offset);
+ if (Z_TYPE_P(property) != IS_UNDEF) {
+fast_assign_obj:
+ value = zend_assign_to_variable(property, value, IS_CV);
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+ goto exit_assign_obj;
+ }
+ } else {
+ if (EXPECTED(zobj->properties != NULL)) {
+ if (UNEXPECTED(GC_REFCOUNT(zobj->properties) > 1)) {
+ if (EXPECTED(!(GC_FLAGS(zobj->properties) & IS_ARRAY_IMMUTABLE))) {
+ GC_REFCOUNT(zobj->properties)--;
+ }
+ zobj->properties = zend_array_dup(zobj->properties);
+ }
+ property = zend_hash_find(zobj->properties, Z_STR_P(property_name));
+ if (property) {
+ goto fast_assign_obj;
+ }
+ }
+
+ if (!zobj->ce->__set) {
+
+ if (EXPECTED(zobj->properties == NULL)) {
+ rebuild_object_properties(zobj);
+ }
+ if (IS_CV == IS_CONST) {
+ if (UNEXPECTED(Z_OPT_REFCOUNTED_P(value))) {
+ Z_ADDREF_P(value);
+ }
+ } else if (IS_CV != IS_TMP_VAR) {
+ if (Z_ISREF_P(value)) {
+ if (IS_CV == IS_VAR) {
+ zend_reference *ref = Z_REF_P(value);
+ if (--GC_REFCOUNT(ref) == 0) {
+ ZVAL_COPY_VALUE(&tmp, Z_REFVAL_P(value));
+ efree_size(ref, sizeof(zend_reference));
+ value = &tmp;
+ } else {
+ value = Z_REFVAL_P(value);
+ if (Z_REFCOUNTED_P(value)) {
+ Z_ADDREF_P(value);
+ }
+ }
+ } else {
+ value = Z_REFVAL_P(value);
+ if (Z_REFCOUNTED_P(value)) {
+ Z_ADDREF_P(value);
+ }
+ }
+ } else if (IS_CV == IS_CV && Z_REFCOUNTED_P(value)) {
+ Z_ADDREF_P(value);
+ }
+ }
+ zend_hash_add_new(zobj->properties, Z_STR_P(property_name), value);
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+ goto exit_assign_obj;
+ }
+ }
+ }
+
+ if (!Z_OBJ_HT_P(object)->write_property) {
+ zend_error(E_WARNING, "Attempt to assign property of non-object");
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+
+ goto exit_assign_obj;
+ }
+
+ if (IS_CV == IS_CV || IS_CV == IS_VAR) {
+ ZVAL_DEREF(value);
+ }
+
+ Z_OBJ_HT_P(object)->write_property(object, property_name, value, ((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property_name)) : NULL);
+
+ if (UNEXPECTED(RETURN_VALUE_USED(opline)) && EXPECTED(!EG(exception))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+
+exit_assign_obj:
zval_ptr_dtor_nogc(free_op2);
/* assign_obj has two opcodes! */
@@ -27792,7 +33663,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_SPEC_UNUSED_T
object = _get_obj_zval_ptr_unused(execute_data);
- if (IS_UNUSED == IS_UNUSED && UNEXPECTED(Z_OBJ_P(object) == NULL)) {
+ if (IS_UNUSED == IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
zval_ptr_dtor_nogc(free_op2);
HANDLE_EXCEPTION();
@@ -27852,6 +33723,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_SPEC_UNUSED_T
EXPECTED(obj == orig_obj)) {
CACHE_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(function_name), called_scope, fbc);
}
+ if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!fbc->op_array.run_time_cache)) {
+ init_func_run_time_cache(&fbc->op_array);
+ }
}
call_info = ZEND_CALL_NESTED_FUNCTION;
@@ -27873,6 +33747,164 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_SPEC_UNUSED_T
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_STATIC_METHOD_CALL_SPEC_UNUSED_TMPVAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zval *function_name;
+ zend_class_entry *ce;
+ zend_object *object;
+ zend_function *fbc;
+ zend_execute_data *call;
+
+ SAVE_OPLINE();
+
+ if (IS_UNUSED == IS_CONST) {
+ /* no function found. try a static method in class */
+ ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)));
+ if (UNEXPECTED(ce == NULL)) {
+ 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(ce == NULL)) {
+ if (UNEXPECTED(EG(exception) != NULL)) {
+ HANDLE_EXCEPTION();
+ }
+ zend_throw_error(NULL, "Class '%s' not found", Z_STRVAL_P(EX_CONSTANT(opline->op1)));
+ HANDLE_EXCEPTION();
+ }
+ CACHE_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)), ce);
+ }
+ } else if (IS_UNUSED == IS_UNUSED) {
+ ce = zend_fetch_class(NULL, opline->op1.num);
+ if (UNEXPECTED(ce == NULL)) {
+ ZEND_ASSERT(EG(exception));
+ zval_ptr_dtor_nogc(EX_VAR(opline->op2.var));
+ HANDLE_EXCEPTION();
+ }
+ } else {
+ ce = Z_CE_P(EX_VAR(opline->op1.var));
+ }
+
+ if (IS_UNUSED == IS_CONST &&
+ (IS_TMP_VAR|IS_VAR) == IS_CONST &&
+ EXPECTED((fbc = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)))) != NULL)) {
+ /* nothing to do */
+ } else if (IS_UNUSED != IS_CONST &&
+ (IS_TMP_VAR|IS_VAR) == IS_CONST &&
+ (fbc = CACHED_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)), ce))) {
+ /* do nothing */
+ } else if ((IS_TMP_VAR|IS_VAR) != IS_UNUSED) {
+ zend_free_op free_op2;
+
+ 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)) {
+ do {
+ if ((IS_TMP_VAR|IS_VAR) & (IS_VAR|IS_CV) && Z_ISREF_P(function_name)) {
+ function_name = Z_REFVAL_P(function_name);
+ if (EXPECTED(Z_TYPE_P(function_name) == IS_STRING)) {
+ break;
+ }
+ } 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();
+ }
+ }
+ zend_throw_error(NULL, "Function name must be a string");
+ zval_ptr_dtor_nogc(free_op2);
+ HANDLE_EXCEPTION();
+ } while (0);
+ }
+ }
+
+ if (ce->get_static_method) {
+ fbc = ce->get_static_method(ce, Z_STR_P(function_name));
+ } else {
+ fbc = zend_std_get_static_method(ce, Z_STR_P(function_name), (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? (EX_CONSTANT(opline->op2) + 1) : NULL));
+ }
+ if (UNEXPECTED(fbc == NULL)) {
+ if (EXPECTED(!EG(exception))) {
+ zend_throw_error(NULL, "Call to undefined method %s::%s()", ZSTR_VAL(ce->name), Z_STRVAL_P(function_name));
+ }
+ zval_ptr_dtor_nogc(free_op2);
+ HANDLE_EXCEPTION();
+ }
+ if ((IS_TMP_VAR|IS_VAR) == IS_CONST &&
+ EXPECTED(fbc->type <= ZEND_USER_FUNCTION) &&
+ EXPECTED(!(fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_TRAMPOLINE|ZEND_ACC_NEVER_CACHE)))) {
+ if (IS_UNUSED == IS_CONST) {
+ CACHE_PTR(Z_CACHE_SLOT_P(function_name), fbc);
+ } else {
+ CACHE_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(function_name), ce, fbc);
+ }
+ }
+ if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!fbc->op_array.run_time_cache)) {
+ init_func_run_time_cache(&fbc->op_array);
+ }
+ if ((IS_TMP_VAR|IS_VAR) != IS_CONST) {
+ zval_ptr_dtor_nogc(free_op2);
+ }
+ } else {
+ if (UNEXPECTED(ce->constructor == NULL)) {
+ zend_throw_error(NULL, "Cannot call constructor");
+ HANDLE_EXCEPTION();
+ }
+ if (Z_TYPE(EX(This)) == IS_OBJECT && Z_OBJ(EX(This))->ce != ce->constructor->common.scope && (ce->constructor->common.fn_flags & ZEND_ACC_PRIVATE)) {
+ zend_throw_error(NULL, "Cannot call private %s::__construct()", ZSTR_VAL(ce->name));
+ HANDLE_EXCEPTION();
+ }
+ fbc = ce->constructor;
+ if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!fbc->op_array.run_time_cache)) {
+ init_func_run_time_cache(&fbc->op_array);
+ }
+ }
+
+ object = NULL;
+ if (!(fbc->common.fn_flags & ZEND_ACC_STATIC)) {
+ if (Z_TYPE(EX(This)) == IS_OBJECT && instanceof_function(Z_OBJCE(EX(This)), ce)) {
+ object = Z_OBJ(EX(This));
+ ce = object->ce;
+ } else {
+ if (fbc->common.fn_flags & ZEND_ACC_ALLOW_STATIC) {
+ /* Allowed for PHP 4 compatibility. */
+ zend_error(
+ E_DEPRECATED,
+ "Non-static method %s::%s() should not be called statically",
+ ZSTR_VAL(fbc->common.scope->name), ZSTR_VAL(fbc->common.function_name));
+ if (UNEXPECTED(EG(exception) != NULL)) {
+ HANDLE_EXCEPTION();
+ }
+ } else {
+ /* An internal function assumes $this is present and won't check that.
+ * So PHP would crash by allowing the call. */
+ zend_throw_error(
+ zend_ce_error,
+ "Non-static method %s::%s() cannot be called statically",
+ ZSTR_VAL(fbc->common.scope->name), ZSTR_VAL(fbc->common.function_name));
+ HANDLE_EXCEPTION();
+ }
+ }
+ }
+
+ if (IS_UNUSED == IS_UNUSED) {
+ /* previous opcode is ZEND_FETCH_CLASS */
+ if ((opline->op1.num & ZEND_FETCH_CLASS_MASK) == ZEND_FETCH_CLASS_PARENT ||
+ (opline->op1.num & ZEND_FETCH_CLASS_MASK) == ZEND_FETCH_CLASS_SELF) {
+ if (Z_TYPE(EX(This)) == IS_OBJECT) {
+ ce = Z_OBJCE(EX(This));
+ } else {
+ ce = Z_CE(EX(This));
+ }
+ }
+ }
+
+ call = zend_vm_stack_push_call_frame(ZEND_CALL_NESTED_FUNCTION,
+ fbc, opline->extended_value, ce, object);
+ call->prev_execute_data = EX(call);
+ EX(call) = call;
+
+ ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
+}
+
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_ARRAY_SPEC_UNUSED_TMPVAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
zval *array;
@@ -27904,105 +33936,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_ARRAY_SPEC_UNUSED_TMPVAR_
}
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_UNSET_DIM_SPEC_UNUSED_TMPVAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
-{
- USE_OPLINE
- zend_free_op free_op2;
- zval *container;
- zval *offset;
- zend_ulong hval;
- zend_string *key;
-
- SAVE_OPLINE();
- container = _get_obj_zval_ptr_unused(execute_data);
- if (IS_UNUSED == IS_UNUSED && UNEXPECTED(Z_OBJ_P(container) == NULL)) {
- zend_throw_error(NULL, "Using $this when not in object context");
- zval_ptr_dtor_nogc(EX_VAR(opline->op2.var));
- HANDLE_EXCEPTION();
- }
- if (IS_UNUSED == IS_VAR && UNEXPECTED(container == NULL)) {
- zend_throw_error(NULL, "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);
-
- 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:
- 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(key, hval)) {
- goto num_index_dim;
- }
- }
-str_index_dim:
- if (ht == &EG(symbol_table)) {
- zend_delete_global_variable(key);
- } else {
- zend_hash_del(ht, key);
- }
- } 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 = ZSTR_EMPTY_ALLOC();
- goto str_index_dim;
- } else if (Z_TYPE_P(offset) == IS_FALSE) {
- hval = 0;
- goto num_index_dim;
- } else if (Z_TYPE_P(offset) == IS_TRUE) {
- hval = 1;
- goto num_index_dim;
- } else if (Z_TYPE_P(offset) == IS_RESOURCE) {
- hval = Z_RES_HANDLE_P(offset);
- goto num_index_dim;
- } else if ((IS_TMP_VAR|IS_VAR) == IS_CV && Z_TYPE_P(offset) == IS_UNDEF) {
- GET_OP2_UNDEF_CV(offset, BP_VAR_R);
- key = ZSTR_EMPTY_ALLOC();
- goto str_index_dim;
- } else {
- zend_error(E_WARNING, "Illegal offset type in unset");
- }
- 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;
- }
- }
- 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 || EXPECTED(Z_TYPE_P(container) == IS_OBJECT)) {
- if (UNEXPECTED(Z_OBJ_HT_P(container)->unset_dimension == NULL)) {
- zend_throw_error(NULL, "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_throw_error(NULL, "Cannot unset string offsets");
- }
- } while (0);
-
- zval_ptr_dtor_nogc(free_op2);
-
- ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
-}
-
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_UNSET_OBJ_SPEC_UNUSED_TMPVAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
@@ -28012,16 +33945,11 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_UNSET_OBJ_SPEC_UNUSED_TMPVAR_H
SAVE_OPLINE();
container = _get_obj_zval_ptr_unused(execute_data);
- if (IS_UNUSED == IS_UNUSED && UNEXPECTED(Z_OBJ_P(container) == NULL)) {
+ if (IS_UNUSED == IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
zval_ptr_dtor_nogc(EX_VAR(opline->op2.var));
HANDLE_EXCEPTION();
}
- if (IS_UNUSED == IS_VAR && UNEXPECTED(container == NULL)) {
- zend_throw_error(NULL, "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);
do {
@@ -28047,143 +33975,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_UNSET_OBJ_SPEC_UNUSED_TMPVAR_H
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ISSET_ISEMPTY_DIM_OBJ_SPEC_UNUSED_TMPVAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
-{
- USE_OPLINE
- zend_free_op free_op2;
- zval *container;
- int result;
- zend_ulong hval;
- zval *offset;
-
- SAVE_OPLINE();
- container = _get_obj_zval_ptr_unused(execute_data);
-
- if (IS_UNUSED == IS_UNUSED && UNEXPECTED(Z_OBJ_P(container) == NULL)) {
- zend_throw_error(NULL, "Using $this when not in object context");
- zval_ptr_dtor_nogc(EX_VAR(opline->op2.var));
- HANDLE_EXCEPTION();
- }
-
- offset = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2);
-
- if (IS_UNUSED != IS_UNUSED && EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) {
- 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);
- if ((IS_TMP_VAR|IS_VAR) != IS_CONST) {
- if (ZEND_HANDLE_NUMERIC(str, hval)) {
- goto num_index_prop;
- }
- }
-str_index_prop:
- value = zend_hash_find_ind(ht, str);
- } else if (EXPECTED(Z_TYPE_P(offset) == IS_LONG)) {
- hval = Z_LVAL_P(offset);
-num_index_prop:
- value = zend_hash_index_find(ht, hval);
- } 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 = ZSTR_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 = ZSTR_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) {
- /* > IS_NULL means not IS_UNDEF and not IS_NULL */
- result = value != NULL && Z_TYPE_P(value) > IS_NULL &&
- (!Z_ISREF_P(value) || Z_TYPE_P(Z_REFVAL_P(value)) != IS_NULL);
- } else /* if (opline->extended_value & ZEND_ISEMPTY) */ {
- result = (value == NULL || !i_zend_is_true(value));
- }
- 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 =
- ((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");
- goto isset_not_found;
- }
- } else if (EXPECTED(Z_TYPE_P(container) == IS_STRING)) { /* string offsets */
- zend_long lval;
-
- 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))) {
- lval = zval_get_long(offset);
- goto isset_str_offset;
- }
- goto isset_not_found;
- }
- } 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);
- ZVAL_BOOL(EX_VAR(opline->result.var), result);
- ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
-}
-
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ISSET_ISEMPTY_PROP_OBJ_SPEC_UNUSED_TMPVAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
@@ -28195,7 +33986,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ISSET_ISEMPTY_PROP_OBJ_SPEC_UN
SAVE_OPLINE();
container = _get_obj_zval_ptr_unused(execute_data);
- if (IS_UNUSED == IS_UNUSED && UNEXPECTED(Z_OBJ_P(container) == NULL)) {
+ if (IS_UNUSED == IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
zval_ptr_dtor_nogc(EX_VAR(opline->op2.var));
HANDLE_EXCEPTION();
@@ -28254,7 +34045,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_BOOL_NOT_SPEC_CV_HANDLER(ZEND_
ZVAL_FALSE(EX_VAR(opline->result.var));
} else if (EXPECTED(Z_TYPE_INFO_P(val) <= IS_TRUE)) {
ZVAL_TRUE(EX_VAR(opline->result.var));
- if (UNEXPECTED(Z_TYPE_INFO_P(val) == IS_UNDEF)) {
+ if (IS_CV == IS_CV && UNEXPECTED(Z_TYPE_INFO_P(val) == IS_UNDEF)) {
SAVE_OPLINE();
GET_OP1_UNDEF_CV(val, BP_VAR_R);
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
@@ -28268,7 +34059,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_BOOL_NOT_SPEC_CV_HANDLER(ZEND_
ZEND_VM_NEXT_OPCODE();
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_PRE_INC_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_PRE_INC_SPEC_CV_RETVAL_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
@@ -28276,22 +34067,55 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_PRE_INC_SPEC_CV_HANDLER(ZEND_O
var_ptr = _get_zval_ptr_cv_undef_BP_VAR_RW(execute_data, opline->op1.var);
- if (IS_CV == IS_VAR && UNEXPECTED(var_ptr == NULL)) {
- SAVE_OPLINE();
- zend_throw_error(NULL, "Cannot increment/decrement overloaded objects nor string offsets");
- HANDLE_EXCEPTION();
+ if (EXPECTED(Z_TYPE_P(var_ptr) == IS_LONG)) {
+ fast_long_increment_function(var_ptr);
+ if (UNEXPECTED(0)) {
+ ZVAL_COPY_VALUE(EX_VAR(opline->result.var), var_ptr);
+ }
+ ZEND_VM_NEXT_OPCODE();
+ }
+
+ if (IS_CV == IS_VAR && UNEXPECTED(Z_ISERROR_P(var_ptr))) {
+ if (UNEXPECTED(0)) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+ ZEND_VM_NEXT_OPCODE();
+ }
+
+ SAVE_OPLINE();
+ if (IS_CV == IS_CV && UNEXPECTED(Z_TYPE_P(var_ptr) == IS_UNDEF)) {
+ var_ptr = GET_OP1_UNDEF_CV(var_ptr, BP_VAR_RW);
+ }
+ ZVAL_DEREF(var_ptr);
+ SEPARATE_ZVAL_NOREF(var_ptr);
+
+ increment_function(var_ptr);
+
+ if (UNEXPECTED(0)) {
+ ZVAL_COPY(EX_VAR(opline->result.var), var_ptr);
}
+ ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_PRE_INC_SPEC_CV_RETVAL_USED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+
+ zval *var_ptr;
+
+ var_ptr = _get_zval_ptr_cv_undef_BP_VAR_RW(execute_data, opline->op1.var);
+
if (EXPECTED(Z_TYPE_P(var_ptr) == IS_LONG)) {
fast_long_increment_function(var_ptr);
- if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ if (UNEXPECTED(1)) {
ZVAL_COPY_VALUE(EX_VAR(opline->result.var), var_ptr);
}
ZEND_VM_NEXT_OPCODE();
}
- if (IS_CV == IS_VAR && UNEXPECTED(var_ptr == &EG(error_zval))) {
- if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ if (IS_CV == IS_VAR && UNEXPECTED(Z_ISERROR_P(var_ptr))) {
+ if (UNEXPECTED(1)) {
ZVAL_NULL(EX_VAR(opline->result.var));
}
ZEND_VM_NEXT_OPCODE();
@@ -28306,14 +34130,14 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_PRE_INC_SPEC_CV_HANDLER(ZEND_O
increment_function(var_ptr);
- if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ if (UNEXPECTED(1)) {
ZVAL_COPY(EX_VAR(opline->result.var), var_ptr);
}
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_PRE_DEC_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_PRE_DEC_SPEC_CV_RETVAL_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
@@ -28321,22 +34145,55 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_PRE_DEC_SPEC_CV_HANDLER(ZEND_O
var_ptr = _get_zval_ptr_cv_undef_BP_VAR_RW(execute_data, opline->op1.var);
- if (IS_CV == IS_VAR && UNEXPECTED(var_ptr == NULL)) {
- SAVE_OPLINE();
- zend_throw_error(NULL, "Cannot increment/decrement overloaded objects nor string offsets");
- HANDLE_EXCEPTION();
+ if (EXPECTED(Z_TYPE_P(var_ptr) == IS_LONG)) {
+ fast_long_decrement_function(var_ptr);
+ if (UNEXPECTED(0)) {
+ ZVAL_COPY_VALUE(EX_VAR(opline->result.var), var_ptr);
+ }
+ ZEND_VM_NEXT_OPCODE();
}
+ if (IS_CV == IS_VAR && UNEXPECTED(Z_ISERROR_P(var_ptr))) {
+ if (UNEXPECTED(0)) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+ ZEND_VM_NEXT_OPCODE();
+ }
+
+ SAVE_OPLINE();
+ if (IS_CV == IS_CV && UNEXPECTED(Z_TYPE_P(var_ptr) == IS_UNDEF)) {
+ var_ptr = GET_OP1_UNDEF_CV(var_ptr, BP_VAR_RW);
+ }
+ ZVAL_DEREF(var_ptr);
+ SEPARATE_ZVAL_NOREF(var_ptr);
+
+ decrement_function(var_ptr);
+
+ if (UNEXPECTED(0)) {
+ ZVAL_COPY(EX_VAR(opline->result.var), var_ptr);
+ }
+
+ ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_PRE_DEC_SPEC_CV_RETVAL_USED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+
+ zval *var_ptr;
+
+ var_ptr = _get_zval_ptr_cv_undef_BP_VAR_RW(execute_data, opline->op1.var);
+
if (EXPECTED(Z_TYPE_P(var_ptr) == IS_LONG)) {
fast_long_decrement_function(var_ptr);
- if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ if (UNEXPECTED(1)) {
ZVAL_COPY_VALUE(EX_VAR(opline->result.var), var_ptr);
}
ZEND_VM_NEXT_OPCODE();
}
- if (IS_CV == IS_VAR && UNEXPECTED(var_ptr == &EG(error_zval))) {
- if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ if (IS_CV == IS_VAR && UNEXPECTED(Z_ISERROR_P(var_ptr))) {
+ if (UNEXPECTED(1)) {
ZVAL_NULL(EX_VAR(opline->result.var));
}
ZEND_VM_NEXT_OPCODE();
@@ -28351,7 +34208,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_PRE_DEC_SPEC_CV_HANDLER(ZEND_O
decrement_function(var_ptr);
- if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ if (UNEXPECTED(1)) {
ZVAL_COPY(EX_VAR(opline->result.var), var_ptr);
}
@@ -28366,19 +34223,13 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_POST_INC_SPEC_CV_HANDLER(ZEND_
var_ptr = _get_zval_ptr_cv_undef_BP_VAR_RW(execute_data, opline->op1.var);
- if (IS_CV == IS_VAR && UNEXPECTED(var_ptr == NULL)) {
- SAVE_OPLINE();
- zend_throw_error(NULL, "Cannot increment/decrement overloaded objects nor string offsets");
- HANDLE_EXCEPTION();
- }
-
if (EXPECTED(Z_TYPE_P(var_ptr) == IS_LONG)) {
ZVAL_COPY_VALUE(EX_VAR(opline->result.var), var_ptr);
fast_long_increment_function(var_ptr);
ZEND_VM_NEXT_OPCODE();
}
- if (IS_CV == IS_VAR && UNEXPECTED(var_ptr == &EG(error_zval))) {
+ if (IS_CV == IS_VAR && UNEXPECTED(Z_ISERROR_P(var_ptr))) {
ZVAL_NULL(EX_VAR(opline->result.var));
ZEND_VM_NEXT_OPCODE();
}
@@ -28404,19 +34255,13 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_POST_DEC_SPEC_CV_HANDLER(ZEND_
var_ptr = _get_zval_ptr_cv_undef_BP_VAR_RW(execute_data, opline->op1.var);
- if (IS_CV == IS_VAR && UNEXPECTED(var_ptr == NULL)) {
- SAVE_OPLINE();
- zend_throw_error(NULL, "Cannot increment/decrement overloaded objects nor string offsets");
- HANDLE_EXCEPTION();
- }
-
if (EXPECTED(Z_TYPE_P(var_ptr) == IS_LONG)) {
ZVAL_COPY_VALUE(EX_VAR(opline->result.var), var_ptr);
fast_long_decrement_function(var_ptr);
ZEND_VM_NEXT_OPCODE();
}
- if (IS_CV == IS_VAR && UNEXPECTED(var_ptr == &EG(error_zval))) {
+ if (IS_CV == IS_VAR && UNEXPECTED(Z_ISERROR_P(var_ptr))) {
ZVAL_NULL(EX_VAR(opline->result.var));
ZEND_VM_NEXT_OPCODE();
}
@@ -28656,46 +34501,61 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_RETURN_SPEC_CV_HANDLER(ZEND_OP
{
USE_OPLINE
zval *retval_ptr;
+ zval *return_value;
zend_free_op free_op1;
retval_ptr = _get_zval_ptr_cv_undef(execute_data, opline->op1.var);
+ return_value = EX(return_value);
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);
- if (EX(return_value)) {
- ZVAL_NULL(EX(return_value));
+ if (return_value) {
+ ZVAL_NULL(return_value);
}
- } else if (!EX(return_value)) {
- if (IS_CV == IS_VAR || IS_CV == IS_TMP_VAR ) {
+ } else if (!return_value) {
+ if (IS_CV & (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));
+ zval_dtor_func(Z_COUNTED_P(free_op1));
}
}
} else {
- if (IS_CV == IS_CONST || IS_CV == IS_TMP_VAR) {
- ZVAL_COPY_VALUE(EX(return_value), retval_ptr);
+ if ((IS_CV & (IS_CONST|IS_TMP_VAR))) {
+ ZVAL_COPY_VALUE(return_value, retval_ptr);
if (IS_CV == IS_CONST) {
- if (UNEXPECTED(Z_OPT_COPYABLE_P(EX(return_value)))) {
- zval_copy_ctor_func(EX(return_value));
+ if (UNEXPECTED(Z_OPT_REFCOUNTED_P(return_value))) {
+ Z_ADDREF_P(return_value);
}
}
} else if (IS_CV == IS_CV) {
- ZVAL_DEREF(retval_ptr);
- ZVAL_COPY(EX(return_value), retval_ptr);
+ if (Z_OPT_REFCOUNTED_P(retval_ptr)) {
+ if (EXPECTED(!Z_OPT_ISREF_P(retval_ptr))) {
+ ZVAL_COPY_VALUE(return_value, retval_ptr);
+ if (EXPECTED(!(EX_CALL_INFO() & ZEND_CALL_CODE))) {
+ ZVAL_NULL(retval_ptr);
+ } else {
+ Z_ADDREF_P(return_value);
+ }
+ } else {
+ retval_ptr = Z_REFVAL_P(retval_ptr);
+ ZVAL_COPY(return_value, retval_ptr);
+ }
+ } else {
+ ZVAL_COPY_VALUE(return_value, retval_ptr);
+ }
} else /* if (IS_CV == IS_VAR) */ {
if (UNEXPECTED(Z_ISREF_P(retval_ptr))) {
zend_refcounted *ref = Z_COUNTED_P(retval_ptr);
retval_ptr = Z_REFVAL_P(retval_ptr);
- ZVAL_COPY_VALUE(EX(return_value), retval_ptr);
+ ZVAL_COPY_VALUE(return_value, retval_ptr);
if (UNEXPECTED(--GC_REFCOUNT(ref) == 0)) {
efree_size(ref, sizeof(zend_reference));
} else if (Z_OPT_REFCOUNTED_P(retval_ptr)) {
Z_ADDREF_P(retval_ptr);
}
} else {
- ZVAL_COPY_VALUE(EX(return_value), retval_ptr);
+ ZVAL_COPY_VALUE(return_value, retval_ptr);
}
}
}
@@ -28711,21 +34571,23 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_RETURN_BY_REF_SPEC_CV_HANDLER(
SAVE_OPLINE();
do {
- if (IS_CV == IS_CONST || IS_CV == IS_TMP_VAR ||
+ if ((IS_CV & (IS_CONST|IS_TMP_VAR)) ||
(IS_CV == IS_VAR && opline->extended_value == ZEND_RETURNS_VALUE)) {
/* Not supposed to happen, but we'll allow it */
zend_error(E_NOTICE, "Only variable references should be returned by reference");
retval_ptr = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var);
if (!EX(return_value)) {
- if (IS_CV == IS_TMP_VAR) {
- }
} else {
- ZVAL_COPY_VALUE(EX(return_value), retval_ptr);
- Z_VAR_FLAGS_P(EX(return_value)) = IS_VAR_RET_REF;
- if (IS_CV != IS_TMP_VAR) {
- zval_opt_copy_ctor_no_imm(EX(return_value));
+ if (IS_CV == IS_VAR && UNEXPECTED(Z_ISREF_P(retval_ptr))) {
+ ZVAL_COPY_VALUE(EX(return_value), retval_ptr);
+ break;
+ }
+
+ ZVAL_NEW_REF(EX(return_value), retval_ptr);
+ if (IS_CV == IS_CONST) {
+ if (Z_REFCOUNTED_P(retval_ptr)) Z_ADDREF_P(retval_ptr);
}
}
break;
@@ -28733,20 +34595,14 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_RETURN_BY_REF_SPEC_CV_HANDLER(
retval_ptr = _get_zval_ptr_cv_BP_VAR_W(execute_data, opline->op1.var);
- if (IS_CV == IS_VAR && UNEXPECTED(retval_ptr == NULL)) {
- zend_throw_error(NULL, "Cannot return string offsets by reference");
- HANDLE_EXCEPTION();
- }
-
if (IS_CV == IS_VAR) {
if (retval_ptr == &EG(uninitialized_zval) ||
- (opline->extended_value == ZEND_RETURNS_FUNCTION &&
- !(Z_VAR_FLAGS_P(retval_ptr) & IS_VAR_RET_REF))) {
+ (opline->extended_value == ZEND_RETURNS_FUNCTION && !Z_ISREF_P(retval_ptr))) {
zend_error(E_NOTICE, "Only variable references should be returned by reference");
if (EX(return_value)) {
ZVAL_NEW_REF(EX(return_value), retval_ptr);
- Z_VAR_FLAGS_P(EX(return_value)) = IS_VAR_RET_REF;
- if (Z_REFCOUNTED_P(retval_ptr)) Z_ADDREF_P(retval_ptr);
+ } else {
+
}
break;
}
@@ -28756,8 +34612,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_RETURN_BY_REF_SPEC_CV_HANDLER(
ZVAL_MAKE_REF(retval_ptr);
Z_ADDREF_P(retval_ptr);
ZVAL_REF(EX(return_value), Z_REF_P(retval_ptr));
- Z_VAR_FLAGS_P(EX(return_value)) = IS_VAR_RET_REF;
}
+
} while (0);
ZEND_VM_TAIL_CALL(zend_leave_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU));
@@ -28775,11 +34631,11 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_GENERATOR_RETURN_SPEC_CV_HANDL
retval = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var);
/* Copy return value into generator->retval */
- if (IS_CV == IS_CONST || IS_CV == IS_TMP_VAR) {
+ if ((IS_CV & (IS_CONST|IS_TMP_VAR))) {
ZVAL_COPY_VALUE(&generator->retval, retval);
if (IS_CV == IS_CONST) {
- if (UNEXPECTED(Z_OPT_COPYABLE(generator->retval))) {
- zval_copy_ctor_func(&generator->retval);
+ if (UNEXPECTED(Z_OPT_REFCOUNTED(generator->retval))) {
+ Z_ADDREF(generator->retval);
}
}
} else if (IS_CV == IS_CV) {
@@ -28896,16 +34752,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SEND_REF_SPEC_CV_HANDLER(ZEND_
SAVE_OPLINE();
varptr = _get_zval_ptr_cv_BP_VAR_W(execute_data, opline->op1.var);
- if (IS_CV == IS_VAR && UNEXPECTED(varptr == NULL)) {
- zend_throw_error(NULL, "Only variables can be passed by reference");
- arg = ZEND_CALL_VAR(EX(call), opline->result.var);
- ZVAL_UNDEF(arg);
- HANDLE_EXCEPTION();
- }
-
arg = ZEND_CALL_VAR(EX(call), opline->result.var);
- if (IS_CV == IS_VAR && UNEXPECTED(varptr == &EG(error_zval))) {
- ZVAL_NEW_REF(arg, &EG(uninitialized_zval));
+ if (IS_CV == IS_VAR && UNEXPECTED(Z_ISERROR_P(varptr))) {
+ ZVAL_NEW_EMPTY_REF(arg);
+ ZVAL_NULL(Z_REFVAL_P(arg));
ZEND_VM_NEXT_OPCODE();
}
@@ -28928,7 +34778,56 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SEND_VAR_EX_SPEC_CV_HANDLER(ZE
uint32_t arg_num = opline->op2.num;
- if (EXPECTED(arg_num <= MAX_ARG_FLAG_NUM)) {
+ if (EXPECTED(0)) {
+ if (QUICK_ARG_SHOULD_BE_SENT_BY_REF(EX(call)->func, arg_num)) {
+ goto send_var_by_ref;
+ }
+ } else if (ARG_SHOULD_BE_SENT_BY_REF(EX(call)->func, arg_num)) {
+send_var_by_ref:
+ ZEND_VM_TAIL_CALL(ZEND_SEND_REF_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU));
+ }
+
+ varptr = _get_zval_ptr_cv_undef(execute_data, opline->op1.var);
+ if (IS_CV == IS_CV && UNEXPECTED(Z_TYPE_INFO_P(varptr) == IS_UNDEF)) {
+ SAVE_OPLINE();
+ GET_OP1_UNDEF_CV(varptr, BP_VAR_R);
+ arg = ZEND_CALL_VAR(EX(call), opline->result.var);
+ ZVAL_NULL(arg);
+ ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
+ }
+
+ arg = ZEND_CALL_VAR(EX(call), opline->result.var);
+
+ if (IS_CV == IS_CV) {
+ ZVAL_OPT_DEREF(varptr);
+ ZVAL_COPY(arg, varptr);
+ } else /* if (IS_CV == IS_VAR) */ {
+ if (UNEXPECTED(Z_ISREF_P(varptr))) {
+ zend_refcounted *ref = Z_COUNTED_P(varptr);
+
+ varptr = Z_REFVAL_P(varptr);
+ ZVAL_COPY_VALUE(arg, varptr);
+ if (UNEXPECTED(--GC_REFCOUNT(ref) == 0)) {
+ efree_size(ref, sizeof(zend_reference));
+ } else if (Z_OPT_REFCOUNTED_P(arg)) {
+ Z_ADDREF_P(arg);
+ }
+ } else {
+ ZVAL_COPY_VALUE(arg, varptr);
+ }
+ }
+
+ ZEND_VM_NEXT_OPCODE();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SEND_VAR_EX_SPEC_CV_QUICK_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zval *varptr, *arg;
+
+ uint32_t arg_num = opline->op2.num;
+
+ if (EXPECTED(1)) {
if (QUICK_ARG_SHOULD_BE_SENT_BY_REF(EX(call)->func, arg_num)) {
goto send_var_by_ref;
}
@@ -28993,14 +34892,13 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SEND_USER_SPEC_CV_HANDLER(ZEND
if (ZEND_CALL_INFO(EX(call)) & ZEND_CALL_CLOSURE) {
OBJ_RELEASE((zend_object*)EX(call)->func->common.prototype);
}
- if (Z_OBJ(EX(call)->This)) {
+ if (Z_TYPE(EX(call)->This) == IS_OBJECT) {
OBJ_RELEASE(Z_OBJ(EX(call)->This));
}
ZVAL_UNDEF(param);
EX(call)->func = (zend_function*)&zend_pass_function;
- EX(call)->called_scope = NULL;
Z_OBJ(EX(call)->This) = NULL;
- ZEND_SET_CALL_INFO(EX(call), ZEND_CALL_INFO(EX(call)) & ~ZEND_CALL_RELEASE_THIS);
+ ZEND_SET_CALL_INFO(EX(call), 0, ZEND_CALL_INFO(EX(call)) & ~ZEND_CALL_RELEASE_THIS);
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
@@ -29028,7 +34926,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_BOOL_SPEC_CV_HANDLER(ZEND_OPCO
ZVAL_TRUE(EX_VAR(opline->result.var));
} else if (EXPECTED(Z_TYPE_INFO_P(val) <= IS_TRUE)) {
ZVAL_FALSE(EX_VAR(opline->result.var));
- if (UNEXPECTED(Z_TYPE_INFO_P(val) == IS_UNDEF)) {
+ if (IS_CV == IS_CV && UNEXPECTED(Z_TYPE_INFO_P(val) == IS_UNDEF)) {
SAVE_OPLINE();
GET_OP1_UNDEF_CV(val, BP_VAR_R);
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
@@ -29047,14 +34945,14 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CLONE_SPEC_CV_HANDLER(ZEND_OPC
USE_OPLINE
zval *obj;
- zend_class_entry *ce;
+ zend_class_entry *ce, *scope;
zend_function *clone;
zend_object_clone_obj_t clone_call;
SAVE_OPLINE();
obj = _get_zval_ptr_cv_undef(execute_data, opline->op1.var);
- if (IS_CV == IS_UNUSED && UNEXPECTED(Z_OBJ_P(obj) == NULL)) {
+ if (IS_CV == IS_UNUSED && UNEXPECTED(Z_TYPE_P(obj) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
HANDLE_EXCEPTION();
}
@@ -29081,43 +34979,39 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CLONE_SPEC_CV_HANDLER(ZEND_OPC
} while (0);
ce = Z_OBJCE_P(obj);
- clone = ce ? ce->clone : NULL;
- clone_call = Z_OBJ_HT_P(obj)->clone_obj;
+ clone = ce->clone;
+ clone_call = Z_OBJ_HT_P(obj)->clone_obj;
if (UNEXPECTED(clone_call == NULL)) {
- if (ce) {
- zend_throw_error(NULL, "Trying to clone an uncloneable object of class %s", ZSTR_VAL(ce->name));
- } else {
- zend_throw_error(NULL, "Trying to clone an uncloneable object");
- }
+ zend_throw_error(NULL, "Trying to clone an uncloneable object of class %s", ZSTR_VAL(ce->name));
HANDLE_EXCEPTION();
}
- if (ce && clone) {
+ if (clone) {
if (clone->op_array.fn_flags & ZEND_ACC_PRIVATE) {
/* Ensure that if we're calling a private function, we're allowed to do so.
*/
- if (UNEXPECTED(ce != EG(scope))) {
- zend_throw_error(NULL, "Call to private %s::__clone() from context '%s'", ZSTR_VAL(ce->name), EG(scope) ? ZSTR_VAL(EG(scope)->name) : "");
+ scope = EX(func)->op_array.scope;
+ if (UNEXPECTED(ce != scope)) {
+ zend_throw_error(NULL, "Call to private %s::__clone() from context '%s'", ZSTR_VAL(ce->name), scope ? ZSTR_VAL(scope->name) : "");
HANDLE_EXCEPTION();
}
} else if ((clone->common.fn_flags & ZEND_ACC_PROTECTED)) {
/* Ensure that if we're calling a protected function, we're allowed to do so.
*/
- if (UNEXPECTED(!zend_check_protected(zend_get_function_root_class(clone), EG(scope)))) {
- zend_throw_error(NULL, "Call to protected %s::__clone() from context '%s'", ZSTR_VAL(ce->name), EG(scope) ? ZSTR_VAL(EG(scope)->name) : "");
+ scope = EX(func)->op_array.scope;
+ if (UNEXPECTED(!zend_check_protected(zend_get_function_root_class(clone), scope))) {
+ zend_throw_error(NULL, "Call to protected %s::__clone() from context '%s'", ZSTR_VAL(ce->name), scope ? ZSTR_VAL(scope->name) : "");
HANDLE_EXCEPTION();
}
}
}
- if (EXPECTED(EG(exception) == NULL)) {
- ZVAL_OBJ(EX_VAR(opline->result.var), clone_call(obj));
- if (UNEXPECTED(!RETURN_VALUE_USED(opline)) || UNEXPECTED(EG(exception) != NULL)) {
- OBJ_RELEASE(Z_OBJ_P(EX_VAR(opline->result.var)));
- }
+ ZVAL_OBJ(EX_VAR(opline->result.var), clone_call(obj));
+ if (UNEXPECTED(EG(exception) != NULL)) {
+ OBJ_RELEASE(Z_OBJ_P(EX_VAR(opline->result.var)));
}
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
@@ -29171,11 +35065,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CAST_SPEC_CV_HANDLER(ZEND_OPCO
if (Z_TYPE_P(expr) == opline->extended_value) {
ZVAL_COPY_VALUE(result, expr);
if (IS_CV == IS_CONST) {
- if (UNEXPECTED(Z_OPT_COPYABLE_P(result))) {
- zval_copy_ctor_func(result);
- }
+ if (UNEXPECTED(Z_OPT_REFCOUNTED_P(result))) Z_ADDREF_P(result);
} else if (IS_CV != IS_TMP_VAR) {
- if (Z_OPT_REFCOUNTED_P(expr)) Z_ADDREF_P(expr);
+ if (Z_OPT_REFCOUNTED_P(result)) Z_ADDREF_P(result);
}
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
@@ -29188,9 +35080,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CAST_SPEC_CV_HANDLER(ZEND_OPCO
if (Z_TYPE_P(expr) != IS_NULL) {
expr = zend_hash_index_add_new(Z_ARRVAL_P(result), 0, expr);
if (IS_CV == IS_CONST) {
- if (UNEXPECTED(Z_OPT_COPYABLE_P(expr))) {
- zval_copy_ctor_func(expr);
- }
+ if (UNEXPECTED(Z_OPT_REFCOUNTED_P(expr))) Z_ADDREF_P(expr);
} else {
if (Z_OPT_REFCOUNTED_P(expr)) Z_ADDREF_P(expr);
}
@@ -29204,11 +35094,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CAST_SPEC_CV_HANDLER(ZEND_OPCO
if (Z_TYPE_P(expr) != IS_ARRAY) {
object_init(result);
if (Z_TYPE_P(expr) != IS_NULL) {
- expr = zend_hash_str_add_new(Z_OBJPROP_P(result), "scalar", sizeof("scalar")-1, expr);
+ expr = zend_hash_add_new(Z_OBJPROP_P(result), CG(known_strings)[ZEND_STR_SCALAR], expr);
if (IS_CV == IS_CONST) {
- if (UNEXPECTED(Z_OPT_COPYABLE_P(expr))) {
- zval_copy_ctor_func(expr);
- }
+ if (UNEXPECTED(Z_OPT_REFCOUNTED_P(expr))) Z_ADDREF_P(expr);
} else {
if (Z_OPT_REFCOUNTED_P(expr)) Z_ADDREF_P(expr);
}
@@ -29226,89 +35114,20 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CAST_SPEC_CV_HANDLER(ZEND_OPCO
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INCLUDE_OR_EVAL_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
- zend_op_array *new_op_array=NULL;
+ zend_op_array *new_op_array;
zval *inc_filename;
- zval tmp_inc_filename;
- zend_bool failure_retval=0;
SAVE_OPLINE();
- 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;
- }
-
- if (opline->extended_value != ZEND_EVAL && strlen(Z_STRVAL_P(inc_filename)) != Z_STRLEN_P(inc_filename)) {
- if (opline->extended_value == ZEND_INCLUDE_ONCE || opline->extended_value == ZEND_INCLUDE) {
- zend_message_dispatcher(ZMSG_FAILED_INCLUDE_FOPEN, Z_STRVAL_P(inc_filename));
- } else {
- zend_message_dispatcher(ZMSG_FAILED_REQUIRE_FOPEN, Z_STRVAL_P(inc_filename));
- }
- } else {
- switch (opline->extended_value) {
- case ZEND_INCLUDE_ONCE:
- case ZEND_REQUIRE_ONCE: {
- zend_file_handle file_handle;
- zend_string *resolved_path;
-
- resolved_path = zend_resolve_path(Z_STRVAL_P(inc_filename), (int)Z_STRLEN_P(inc_filename));
- if (resolved_path) {
- failure_retval = zend_hash_exists(&EG(included_files), resolved_path);
- } else {
- resolved_path = zend_string_copy(Z_STR_P(inc_filename));
- }
-
- if (failure_retval) {
- /* do nothing, file already included */
- } else if (SUCCESS == zend_stream_open(ZSTR_VAL(resolved_path), &file_handle)) {
-
- if (!file_handle.opened_path) {
- file_handle.opened_path = zend_string_copy(resolved_path);
- }
-
- if (zend_hash_add_empty_element(&EG(included_files), file_handle.opened_path)) {
- new_op_array = zend_compile_file(&file_handle, (opline->extended_value==ZEND_INCLUDE_ONCE?ZEND_INCLUDE:ZEND_REQUIRE));
- zend_destroy_file_handle(&file_handle);
- } else {
- zend_file_handle_dtor(&file_handle);
- failure_retval=1;
- }
- } else {
- if (opline->extended_value == ZEND_INCLUDE_ONCE) {
- zend_message_dispatcher(ZMSG_FAILED_INCLUDE_FOPEN, Z_STRVAL_P(inc_filename));
- } else {
- zend_message_dispatcher(ZMSG_FAILED_REQUIRE_FOPEN, Z_STRVAL_P(inc_filename));
- }
- }
- zend_string_release(resolved_path);
- }
- break;
- case ZEND_INCLUDE:
- case ZEND_REQUIRE:
- new_op_array = compile_filename(opline->extended_value, inc_filename);
- break;
- case ZEND_EVAL: {
- char *eval_desc = zend_make_compiled_string_description("eval()'d code");
-
- new_op_array = zend_compile_string(inc_filename, eval_desc);
- efree(eval_desc);
- }
- break;
- EMPTY_SWITCH_DEFAULT_CASE()
- }
- }
- if (Z_TYPE(tmp_inc_filename) != IS_UNDEF) {
- zend_string_release(Z_STR(tmp_inc_filename));
- }
+ inc_filename = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var);
+ new_op_array = zend_include_or_eval(inc_filename, opline->extended_value);
if (UNEXPECTED(EG(exception) != NULL)) {
HANDLE_EXCEPTION();
+ } else if (new_op_array == ZEND_FAKE_OP_ARRAY) {
+ if (RETURN_VALUE_USED(opline)) {
+ ZVAL_TRUE(EX_VAR(opline->result.var));
+ }
} else if (EXPECTED(new_op_array != NULL)) {
zval *return_value = NULL;
zend_execute_data *call;
@@ -29317,19 +35136,21 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INCLUDE_OR_EVAL_SPEC_CV_HANDLE
return_value = EX_VAR(opline->result.var);
}
- new_op_array->scope = EG(scope);
+ new_op_array->scope = EX(func)->op_array.scope;
- call = zend_vm_stack_push_call_frame(ZEND_CALL_NESTED_CODE,
- (zend_function*)new_op_array, 0, EX(called_scope), Z_OBJ(EX(This)));
+ call = zend_vm_stack_push_call_frame(ZEND_CALL_NESTED_CODE | ZEND_CALL_HAS_SYMBOL_TABLE,
+ (zend_function*)new_op_array, 0,
+ Z_TYPE(EX(This)) != IS_OBJECT ? Z_CE(EX(This)) : NULL,
+ Z_TYPE(EX(This)) == IS_OBJECT ? Z_OBJ(EX(This)) : NULL);
- if (EX(symbol_table)) {
+ if (EX_CALL_INFO() & ZEND_CALL_HAS_SYMBOL_TABLE) {
call->symbol_table = EX(symbol_table);
} else {
call->symbol_table = zend_rebuild_symbol_table();
}
call->prev_execute_data = execute_data;
- i_init_code_execute_data(call, new_op_array, return_value);
+ i_init_code_execute_data(call, new_op_array, return_value);
if (EXPECTED(zend_execute_ex == execute_ex)) {
ZEND_VM_ENTER();
} else {
@@ -29344,12 +35165,11 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INCLUDE_OR_EVAL_SPEC_CV_HANDLE
zend_throw_exception_internal(NULL);
HANDLE_EXCEPTION();
}
-
} else if (RETURN_VALUE_USED(opline)) {
- ZVAL_BOOL(EX_VAR(opline->result.var), failure_retval);
+ ZVAL_FALSE(EX_VAR(opline->result.var));
}
- ZEND_VM_INTERRUPT_CHECK();
- ZEND_VM_NEXT_OPCODE();
+ ZEND_VM_SET_OPCODE(opline + 1);
+ ZEND_VM_CONTINUE();
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FE_RESET_R_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@@ -29381,6 +35201,13 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FE_RESET_R_SPEC_CV_HANDLER(ZEN
if (IS_CV != IS_TMP_VAR) {
Z_ADDREF_P(array_ptr);
}
+ if (Z_OBJ_P(array_ptr)->properties
+ && UNEXPECTED(GC_REFCOUNT(Z_OBJ_P(array_ptr)->properties) > 1)) {
+ if (EXPECTED(!(GC_FLAGS(Z_OBJ_P(array_ptr)->properties) & IS_ARRAY_IMMUTABLE))) {
+ GC_REFCOUNT(Z_OBJ_P(array_ptr)->properties)--;
+ }
+ Z_OBJ_P(array_ptr)->properties = zend_array_dup(Z_OBJ_P(array_ptr)->properties);
+ }
fe_ht = Z_OBJPROP_P(array_ptr);
pos = 0;
p = fe_ht->arData;
@@ -29524,6 +35351,13 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_CV_HANDLER(ZE
array_ptr = EX_VAR(opline->result.var);
ZVAL_COPY_VALUE(array_ptr, array_ref);
}
+ if (Z_OBJ_P(array_ptr)->properties
+ && UNEXPECTED(GC_REFCOUNT(Z_OBJ_P(array_ptr)->properties) > 1)) {
+ if (EXPECTED(!(GC_FLAGS(Z_OBJ_P(array_ptr)->properties) & IS_ARRAY_IMMUTABLE))) {
+ GC_REFCOUNT(Z_OBJ_P(array_ptr)->properties)--;
+ }
+ Z_OBJ_P(array_ptr)->properties = zend_array_dup(Z_OBJ_P(array_ptr)->properties);
+ }
fe_ht = Z_OBJPROP_P(array_ptr);
p = fe_ht->arData;
while (1) {
@@ -29663,20 +35497,20 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_JMP_SET_SPEC_CV_HANDLER(ZEND_O
value = Z_REFVAL_P(value);
}
if (i_zend_is_true(value)) {
- ZVAL_COPY_VALUE(EX_VAR(opline->result.var), value);
+ zval *result = EX_VAR(opline->result.var);
+
+ ZVAL_COPY_VALUE(result, value);
if (IS_CV == IS_CONST) {
- if (UNEXPECTED(Z_OPT_COPYABLE_P(value))) {
- zval_copy_ctor_func(EX_VAR(opline->result.var));
- }
+ if (UNEXPECTED(Z_OPT_REFCOUNTED_P(result))) Z_ADDREF_P(result);
} else if (IS_CV == IS_CV) {
- if (Z_OPT_REFCOUNTED_P(value)) Z_ADDREF_P(value);
+ if (Z_OPT_REFCOUNTED_P(result)) Z_ADDREF_P(result);
} else if (IS_CV == IS_VAR && ref) {
zend_reference *r = Z_REF_P(ref);
if (UNEXPECTED(--GC_REFCOUNT(r) == 0)) {
efree_size(r, sizeof(zend_reference));
- } else if (Z_OPT_REFCOUNTED_P(value)) {
- Z_ADDREF_P(value);
+ } else if (Z_OPT_REFCOUNTED_P(result)) {
+ Z_ADDREF_P(result);
}
}
ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2));
@@ -29703,20 +35537,19 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_COALESCE_SPEC_CV_HANDLER(ZEND_
}
if (Z_TYPE_P(value) > IS_NULL) {
- ZVAL_COPY_VALUE(EX_VAR(opline->result.var), value);
+ zval *result = EX_VAR(opline->result.var);
+ ZVAL_COPY_VALUE(result, value);
if (IS_CV == IS_CONST) {
- if (UNEXPECTED(Z_OPT_COPYABLE_P(value))) {
- zval_copy_ctor_func(EX_VAR(opline->result.var));
- }
+ if (UNEXPECTED(Z_OPT_REFCOUNTED_P(result))) Z_ADDREF_P(result);
} else if (IS_CV == IS_CV) {
- if (Z_OPT_REFCOUNTED_P(value)) Z_ADDREF_P(value);
+ if (Z_OPT_REFCOUNTED_P(result)) Z_ADDREF_P(result);
} else if (IS_CV == IS_VAR && ref) {
zend_reference *r = Z_REF_P(ref);
if (UNEXPECTED(--GC_REFCOUNT(r) == 0)) {
efree_size(r, sizeof(zend_reference));
- } else if (Z_OPT_REFCOUNTED_P(value)) {
- Z_ADDREF_P(value);
+ } else if (Z_OPT_REFCOUNTED_P(result)) {
+ Z_ADDREF_P(result);
}
}
ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2));
@@ -29757,8 +35590,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_QM_ASSIGN_SPEC_CV_HANDLER(ZEND
} else {
ZVAL_COPY_VALUE(result, value);
if (IS_CV == IS_CONST) {
- if (UNEXPECTED(Z_OPT_COPYABLE_P(value))) {
- zval_copy_ctor_func(result);
+ if (UNEXPECTED(Z_OPT_REFCOUNTED_P(result))) {
+ Z_ADDREF_P(result);
}
}
}
@@ -29917,7 +35750,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_TYPE_CHECK_SPEC_CV_HANDLER(ZEN
if (IS_CV != IS_CONST && UNEXPECTED(Z_TYPE_P(value) == IS_OBJECT)) {
zend_class_entry *ce = Z_OBJCE_P(value);
- if (UNEXPECTED(ZSTR_LEN(ce->name) != sizeof("__PHP_Incomplete_Class") - 1) ||
+ if (EXPECTED(ZSTR_LEN(ce->name) != sizeof("__PHP_Incomplete_Class") - 1) ||
EXPECTED(memcmp(ZSTR_VAL(ce->name), "__PHP_Incomplete_Class", sizeof("__PHP_Incomplete_Class") - 1) != 0)) {
result = 1;
}
@@ -29940,6 +35773,80 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_TYPE_CHECK_SPEC_CV_HANDLER(ZEN
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SEND_VAR_SIMPLE_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zval *varptr, *arg;
+
+
+ varptr = _get_zval_ptr_cv_undef(execute_data, opline->op1.var);
+ arg = ZEND_CALL_VAR(EX(call), opline->result.var);
+
+ if (IS_CV == IS_CV) {
+ ZVAL_COPY(arg, varptr);
+ } else /* if (IS_CV == IS_VAR) */ {
+ ZVAL_COPY_VALUE(arg, varptr);
+ }
+
+ ZEND_VM_NEXT_OPCODE();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SEND_VAR_EX_SIMPLE_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zval *varptr, *arg;
+
+ uint32_t arg_num = opline->op2.num;
+
+ if (EXPECTED(0)) {
+ if (QUICK_ARG_SHOULD_BE_SENT_BY_REF(EX(call)->func, arg_num)) {
+ goto send_var_by_ref_simple;
+ }
+ } else if (ARG_SHOULD_BE_SENT_BY_REF(EX(call)->func, arg_num)) {
+send_var_by_ref_simple:
+ ZEND_VM_TAIL_CALL(ZEND_SEND_REF_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU));
+ }
+
+ varptr = _get_zval_ptr_cv_undef(execute_data, opline->op1.var);
+ arg = ZEND_CALL_VAR(EX(call), opline->result.var);
+
+ if (IS_CV == IS_CV) {
+ ZVAL_COPY(arg, varptr);
+ } else /* if (IS_CV == IS_VAR) */ {
+ ZVAL_COPY_VALUE(arg, varptr);
+ }
+
+ ZEND_VM_NEXT_OPCODE();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SEND_VAR_EX_SIMPLE_SPEC_CV_QUICK_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zval *varptr, *arg;
+
+ uint32_t arg_num = opline->op2.num;
+
+ if (EXPECTED(1)) {
+ if (QUICK_ARG_SHOULD_BE_SENT_BY_REF(EX(call)->func, arg_num)) {
+ goto send_var_by_ref_simple;
+ }
+ } else if (ARG_SHOULD_BE_SENT_BY_REF(EX(call)->func, arg_num)) {
+send_var_by_ref_simple:
+ ZEND_VM_TAIL_CALL(ZEND_SEND_REF_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU));
+ }
+
+ varptr = _get_zval_ptr_cv_undef(execute_data, opline->op1.var);
+ arg = ZEND_CALL_VAR(EX(call), opline->result.var);
+
+ if (IS_CV == IS_CV) {
+ ZVAL_COPY(arg, varptr);
+ } else /* if (IS_CV == IS_VAR) */ {
+ ZVAL_COPY_VALUE(arg, varptr);
+ }
+
+ ZEND_VM_NEXT_OPCODE();
+}
+
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ADD_SPEC_CV_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
@@ -30205,7 +36112,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CONCAT_SPEC_CV_CONST_HANDLER(Z
!ZSTR_IS_INTERNED(op1_str) && GC_REFCOUNT(op1_str) == 1) {
size_t len = ZSTR_LEN(op1_str);
- str = zend_string_realloc(op1_str, len + ZSTR_LEN(op2_str), 0);
+ str = zend_string_extend(op1_str, len + ZSTR_LEN(op2_str), 0);
memcpy(ZSTR_VAL(str) + len, ZSTR_VAL(op2_str), ZSTR_LEN(op2_str)+1);
ZVAL_NEW_STR(EX_VAR(opline->result.var), str);
break;
@@ -30589,7 +36496,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_binary_assign_op_obj_helper_SP
SAVE_OPLINE();
object = _get_zval_ptr_cv_BP_VAR_RW(execute_data, opline->op1.var);
- if (IS_CV == IS_UNUSED && UNEXPECTED(Z_OBJ_P(object) == NULL)) {
+ if (IS_CV == IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
FREE_UNFETCHED_OP((opline+1)->op1_type, (opline+1)->op1.var);
@@ -30598,13 +36505,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_binary_assign_op_obj_helper_SP
property = EX_CONSTANT(opline->op2);
- if (IS_CV == IS_VAR && UNEXPECTED(object == NULL)) {
- zend_throw_error(NULL, "Cannot use string offset as an object");
- FREE_UNFETCHED_OP((opline+1)->op1_type, (opline+1)->op1.var);
-
- HANDLE_EXCEPTION();
- }
-
do {
value = get_zval_ptr_r((opline+1)->op1_type, (opline+1)->op1, execute_data, &free_op_data1);
@@ -30622,7 +36522,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_binary_assign_op_obj_helper_SP
/* here we are sure we are dealing with an object */
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)) {
- if (UNEXPECTED(zptr == &EG(error_zval))) {
+ if (UNEXPECTED(Z_ISERROR_P(zptr))) {
if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
ZVAL_NULL(EX_VAR(opline->result.var));
}
@@ -30651,66 +36551,85 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_binary_assign_op_dim_helper_SP
{
USE_OPLINE
zend_free_op free_op_data1;
- zval *var_ptr, rv;
+ zval *var_ptr;
zval *value, *container, *dim;
SAVE_OPLINE();
- container = _get_zval_ptr_cv_BP_VAR_RW(execute_data, opline->op1.var);
- if (IS_CV == IS_UNUSED && UNEXPECTED(Z_OBJ_P(container) == NULL)) {
- zend_throw_error(NULL, "Using $this when not in object context");
- FREE_UNFETCHED_OP((opline+1)->op1_type, (opline+1)->op1.var);
+ container = _get_zval_ptr_cv_undef_BP_VAR_RW(execute_data, opline->op1.var);
- HANDLE_EXCEPTION();
- }
- if (IS_CV == IS_VAR && UNEXPECTED(container == NULL)) {
- zend_throw_error(NULL, "Cannot use string offset as an array");
- FREE_UNFETCHED_OP((opline+1)->op1_type, (opline+1)->op1.var);
-
- HANDLE_EXCEPTION();
- }
-
- dim = EX_CONSTANT(opline->op2);
+ if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) {
+assign_dim_op_array:
+ SEPARATE_ARRAY(container);
+assign_dim_op_new_array:
+ if (IS_CONST == IS_UNUSED) {
+ var_ptr = zend_hash_next_index_insert(Z_ARRVAL_P(container), &EG(uninitialized_zval));
+ if (UNEXPECTED(!var_ptr)) {
+ zend_error(E_WARNING, "Cannot add element to the array as the next element is already occupied");
+ goto assign_dim_op_ret_null;
+ }
+ } else {
+ dim = EX_CONSTANT(opline->op2);
- do {
- if (IS_CV == IS_UNUSED || UNEXPECTED(Z_TYPE_P(container) != IS_ARRAY)) {
- if (IS_CV != IS_UNUSED) {
- ZVAL_DEREF(container);
+ if (IS_CONST == IS_CONST) {
+ var_ptr = zend_fetch_dimension_address_inner_RW_CONST(Z_ARRVAL_P(container), dim);
+ } else {
+ var_ptr = zend_fetch_dimension_address_inner_RW(Z_ARRVAL_P(container), dim);
}
- if (IS_CV == IS_UNUSED || EXPECTED(Z_TYPE_P(container) == IS_OBJECT)) {
- value = get_zval_ptr_r((opline+1)->op1_type, (opline+1)->op1, execute_data, &free_op_data1);
- zend_binary_assign_op_obj_dim(container, dim, value, UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL, binary_op);
- break;
+ if (UNEXPECTED(!var_ptr)) {
+ goto assign_dim_op_ret_null;
}
+ ZVAL_DEREF(var_ptr);
+ SEPARATE_ZVAL_NOREF(var_ptr);
}
- zend_fetch_dimension_address_RW(&rv, container, dim, IS_CONST);
value = get_zval_ptr_r((opline+1)->op1_type, (opline+1)->op1, execute_data, &free_op_data1);
- ZEND_ASSERT(Z_TYPE(rv) == IS_INDIRECT);
- var_ptr = Z_INDIRECT(rv);
- if (UNEXPECTED(var_ptr == NULL)) {
- zend_throw_error(NULL, "Cannot use assign-op operators with overloaded objects nor string offsets");
-
- FREE_OP(free_op_data1);
+ binary_op(var_ptr, var_ptr, value);
- HANDLE_EXCEPTION();
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), var_ptr);
}
-
- if (UNEXPECTED(var_ptr == &EG(error_zval))) {
- if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
- ZVAL_NULL(EX_VAR(opline->result.var));
+ } else {
+ if (EXPECTED(Z_ISREF_P(container))) {
+ container = Z_REFVAL_P(container);
+ if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) {
+ goto assign_dim_op_array;
}
- } else {
- ZVAL_DEREF(var_ptr);
- SEPARATE_ZVAL_NOREF(var_ptr);
+ } else if (IS_CV == IS_CV && UNEXPECTED(Z_TYPE_INFO_P(container) == IS_UNDEF)) {
+ container = GET_OP1_UNDEF_CV(container, BP_VAR_RW);
+assign_dim_op_convert_to_array:
+ ZVAL_NEW_ARR(container);
+ zend_hash_init(Z_ARRVAL_P(container), 8, NULL, ZVAL_PTR_DTOR, 0);
+ goto assign_dim_op_new_array;
+ }
- binary_op(var_ptr, var_ptr, value);
+ dim = EX_CONSTANT(opline->op2);
- if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
- ZVAL_COPY(EX_VAR(opline->result.var), var_ptr);
+ if (EXPECTED(Z_TYPE_P(container) == IS_OBJECT)) {
+ value = get_zval_ptr_r((opline+1)->op1_type, (opline+1)->op1, execute_data, &free_op_data1);
+ zend_binary_assign_op_obj_dim(container, dim, value, UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL, binary_op);
+ } else {
+ if (UNEXPECTED(Z_TYPE_P(container) == IS_STRING)) {
+ if (IS_CONST == IS_UNUSED) {
+ zend_throw_error(NULL, "[] operator not supported for strings");
+ } else {
+ zend_check_string_offset(dim, BP_VAR_RW);
+ zend_wrong_string_offset();
+ }
+ } else if (EXPECTED(Z_TYPE_P(container) <= IS_FALSE)) {
+ goto assign_dim_op_convert_to_array;
+ } else {
+ if (UNEXPECTED(IS_CV != IS_VAR || EXPECTED(!Z_ISERROR_P(container)))) {
+ zend_error(E_WARNING, "Cannot use a scalar value as an array");
+ }
+assign_dim_op_ret_null:
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
}
+ value = get_zval_ptr_r((opline+1)->op1_type, (opline+1)->op1, execute_data, &free_op_data1);
}
- } while (0);
+ }
FREE_OP(free_op_data1);
@@ -30728,13 +36647,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_binary_assign_op_helper_SPEC_C
value = EX_CONSTANT(opline->op2);
var_ptr = _get_zval_ptr_cv_BP_VAR_RW(execute_data, opline->op1.var);
- if (IS_CV == IS_VAR && UNEXPECTED(var_ptr == NULL)) {
- zend_throw_error(NULL, "Cannot use assign-op operators with overloaded objects nor string offsets");
-
- HANDLE_EXCEPTION();
- }
-
- if (IS_CV == IS_VAR && UNEXPECTED(var_ptr == &EG(error_zval))) {
+ if (IS_CV == IS_VAR && UNEXPECTED(Z_ISERROR_P(var_ptr))) {
if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
ZVAL_NULL(EX_VAR(opline->result.var));
}
@@ -30759,15 +36672,52 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_ADD_SPEC_CV_CONST_HANDL
USE_OPLINE
# if 0 || (IS_CV != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
+ if (EXPECTED(1)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_CV_CONST(add_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CONST(add_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_CONST(add_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CONST(add_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_ADD_SPEC_CV_CONST_DIM_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || (IS_CONST != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_CV != IS_UNUSED)
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_CV_CONST(add_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(1)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CONST(add_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_CONST(add_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CONST(add_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_ADD_SPEC_CV_CONST_OBJ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || (IS_CONST != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_CV != IS_UNUSED)
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_CV_CONST(add_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CONST(add_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_CONST(add_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_CONST(add_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#else
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CONST(add_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#endif
@@ -30779,15 +36729,52 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_SUB_SPEC_CV_CONST_HANDL
USE_OPLINE
# if 0 || (IS_CV != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
+ if (EXPECTED(1)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_CV_CONST(sub_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CONST(sub_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_CONST(sub_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CONST(sub_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_SUB_SPEC_CV_CONST_DIM_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || (IS_CONST != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_CV != IS_UNUSED)
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_CV_CONST(sub_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+ if (EXPECTED(1)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CONST(sub_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_CONST(sub_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CONST(sub_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_SUB_SPEC_CV_CONST_OBJ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || (IS_CONST != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_CV != IS_UNUSED)
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_CV_CONST(sub_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CONST(sub_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_CONST(sub_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_CONST(sub_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#else
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CONST(sub_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#endif
@@ -30799,15 +36786,52 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_MUL_SPEC_CV_CONST_HANDL
USE_OPLINE
# if 0 || (IS_CV != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
+ if (EXPECTED(1)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_CV_CONST(mul_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CONST(mul_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_CONST(mul_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CONST(mul_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_MUL_SPEC_CV_CONST_DIM_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || (IS_CONST != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_CV != IS_UNUSED)
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_CV_CONST(mul_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(1)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CONST(mul_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_CONST(mul_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CONST(mul_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_MUL_SPEC_CV_CONST_OBJ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || (IS_CONST != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_CV != IS_UNUSED)
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_CV_CONST(mul_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CONST(mul_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_CONST(mul_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_CONST(mul_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#else
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CONST(mul_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#endif
@@ -30819,15 +36843,52 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIV_SPEC_CV_CONST_HANDL
USE_OPLINE
# if 0 || (IS_CV != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
+ if (EXPECTED(1)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_CV_CONST(div_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CONST(div_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_CONST(div_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CONST(div_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIV_SPEC_CV_CONST_DIM_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || (IS_CONST != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_CV != IS_UNUSED)
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_CV_CONST(div_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(1)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CONST(div_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_CONST(div_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_CONST(div_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CONST(div_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIV_SPEC_CV_CONST_OBJ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || (IS_CONST != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_CV != IS_UNUSED)
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_CV_CONST(div_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CONST(div_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_CONST(div_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#else
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CONST(div_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#endif
@@ -30839,15 +36900,52 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_MOD_SPEC_CV_CONST_HANDL
USE_OPLINE
# if 0 || (IS_CV != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
+ if (EXPECTED(1)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_CV_CONST(mod_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CONST(mod_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_CONST(mod_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CONST(mod_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_MOD_SPEC_CV_CONST_DIM_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || (IS_CONST != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_CV != IS_UNUSED)
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_CV_CONST(mod_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(1)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CONST(mod_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_CONST(mod_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_CONST(mod_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CONST(mod_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_MOD_SPEC_CV_CONST_OBJ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || (IS_CONST != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_CV != IS_UNUSED)
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_CV_CONST(mod_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CONST(mod_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_CONST(mod_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#else
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CONST(mod_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#endif
@@ -30859,15 +36957,52 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_SL_SPEC_CV_CONST_HANDLE
USE_OPLINE
# if 0 || (IS_CV != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
+ if (EXPECTED(1)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_CV_CONST(shift_left_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CONST(shift_left_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_CONST(shift_left_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CONST(shift_left_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_SL_SPEC_CV_CONST_DIM_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || (IS_CONST != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_CV != IS_UNUSED)
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_CV_CONST(shift_left_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+ if (EXPECTED(1)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CONST(shift_left_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_CONST(shift_left_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CONST(shift_left_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_SL_SPEC_CV_CONST_OBJ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || (IS_CONST != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_CV != IS_UNUSED)
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_CV_CONST(shift_left_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CONST(shift_left_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_CONST(shift_left_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_CONST(shift_left_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#else
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CONST(shift_left_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#endif
@@ -30879,15 +37014,52 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_SR_SPEC_CV_CONST_HANDLE
USE_OPLINE
# if 0 || (IS_CV != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
+ if (EXPECTED(1)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_CV_CONST(shift_right_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CONST(shift_right_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_CONST(shift_right_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CONST(shift_right_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_SR_SPEC_CV_CONST_DIM_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || (IS_CONST != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_CV != IS_UNUSED)
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_CV_CONST(shift_right_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(1)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CONST(shift_right_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_CONST(shift_right_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_CONST(shift_right_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CONST(shift_right_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_SR_SPEC_CV_CONST_OBJ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || (IS_CONST != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_CV != IS_UNUSED)
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_CV_CONST(shift_right_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CONST(shift_right_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_CONST(shift_right_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#else
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CONST(shift_right_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#endif
@@ -30899,15 +37071,52 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_CONCAT_SPEC_CV_CONST_HA
USE_OPLINE
# if 0 || (IS_CV != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
+ if (EXPECTED(1)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_CV_CONST(concat_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CONST(concat_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_CONST(concat_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CONST(concat_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_CONCAT_SPEC_CV_CONST_DIM_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || (IS_CONST != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_CV != IS_UNUSED)
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_CV_CONST(concat_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(1)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CONST(concat_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_CONST(concat_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_CONST(concat_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CONST(concat_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_CONCAT_SPEC_CV_CONST_OBJ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || (IS_CONST != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_CV != IS_UNUSED)
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_CV_CONST(concat_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CONST(concat_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_CONST(concat_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#else
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CONST(concat_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#endif
@@ -30919,15 +37128,52 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_BW_OR_SPEC_CV_CONST_HAN
USE_OPLINE
# if 0 || (IS_CV != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
+ if (EXPECTED(1)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_CV_CONST(bitwise_or_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CONST(bitwise_or_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_CONST(bitwise_or_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CONST(bitwise_or_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_BW_OR_SPEC_CV_CONST_DIM_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || (IS_CONST != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_CV != IS_UNUSED)
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_CV_CONST(bitwise_or_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(1)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CONST(bitwise_or_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_CONST(bitwise_or_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_CONST(bitwise_or_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CONST(bitwise_or_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_BW_OR_SPEC_CV_CONST_OBJ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || (IS_CONST != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_CV != IS_UNUSED)
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_CV_CONST(bitwise_or_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CONST(bitwise_or_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_CONST(bitwise_or_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#else
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CONST(bitwise_or_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#endif
@@ -30939,15 +37185,52 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_BW_AND_SPEC_CV_CONST_HA
USE_OPLINE
# if 0 || (IS_CV != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
+ if (EXPECTED(1)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_CV_CONST(bitwise_and_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CONST(bitwise_and_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_CONST(bitwise_and_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CONST(bitwise_and_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_BW_AND_SPEC_CV_CONST_DIM_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || (IS_CONST != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_CV != IS_UNUSED)
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_CV_CONST(bitwise_and_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(1)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CONST(bitwise_and_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_CONST(bitwise_and_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_CONST(bitwise_and_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CONST(bitwise_and_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_BW_AND_SPEC_CV_CONST_OBJ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || (IS_CONST != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_CV != IS_UNUSED)
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_CV_CONST(bitwise_and_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CONST(bitwise_and_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_CONST(bitwise_and_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#else
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CONST(bitwise_and_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#endif
@@ -30959,15 +37242,52 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_BW_XOR_SPEC_CV_CONST_HA
USE_OPLINE
# if 0 || (IS_CV != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
+ if (EXPECTED(1)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_CV_CONST(bitwise_xor_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CONST(bitwise_xor_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_CONST(bitwise_xor_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CONST(bitwise_xor_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_BW_XOR_SPEC_CV_CONST_DIM_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || (IS_CONST != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_CV != IS_UNUSED)
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_CV_CONST(bitwise_xor_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+ if (EXPECTED(1)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CONST(bitwise_xor_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_CONST(bitwise_xor_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CONST(bitwise_xor_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_BW_XOR_SPEC_CV_CONST_OBJ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || (IS_CONST != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_CV != IS_UNUSED)
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_CV_CONST(bitwise_xor_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CONST(bitwise_xor_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_CONST(bitwise_xor_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_CONST(bitwise_xor_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#else
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CONST(bitwise_xor_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#endif
@@ -30979,15 +37299,52 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_POW_SPEC_CV_CONST_HANDL
USE_OPLINE
# if 0 || (IS_CV != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
+ if (EXPECTED(1)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_CV_CONST(pow_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CONST(pow_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_CONST(pow_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CONST(pow_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_POW_SPEC_CV_CONST_DIM_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || (IS_CONST != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_CV != IS_UNUSED)
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_CV_CONST(pow_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(1)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CONST(pow_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_CONST(pow_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_CONST(pow_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CONST(pow_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_POW_SPEC_CV_CONST_OBJ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || (IS_CONST != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_CV != IS_UNUSED)
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_CV_CONST(pow_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CONST(pow_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_CONST(pow_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#else
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CONST(pow_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#endif
@@ -31004,7 +37361,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_pre_incdec_property_helper_SPE
SAVE_OPLINE();
object = _get_zval_ptr_cv_BP_VAR_RW(execute_data, opline->op1.var);
- if (IS_CV == IS_UNUSED && UNEXPECTED(Z_OBJ_P(object) == NULL)) {
+ if (IS_CV == IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
HANDLE_EXCEPTION();
@@ -31012,12 +37369,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_pre_incdec_property_helper_SPE
property = EX_CONSTANT(opline->op2);
- if (IS_CV == IS_VAR && UNEXPECTED(object == NULL)) {
- zend_throw_error(NULL, "Cannot increment/decrement overloaded objects nor string offsets");
-
- HANDLE_EXCEPTION();
- }
-
do {
if (IS_CV != IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) != IS_OBJECT)) {
ZVAL_DEREF(object);
@@ -31033,7 +37384,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_pre_incdec_property_helper_SPE
/* here we are sure we are dealing with an object */
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)) {
- if (UNEXPECTED(zptr == &EG(error_zval))) {
+ if (UNEXPECTED(Z_ISERROR_P(zptr))) {
if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
ZVAL_NULL(EX_VAR(opline->result.var));
}
@@ -31088,7 +37439,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_post_incdec_property_helper_SP
SAVE_OPLINE();
object = _get_zval_ptr_cv_BP_VAR_RW(execute_data, opline->op1.var);
- if (IS_CV == IS_UNUSED && UNEXPECTED(Z_OBJ_P(object) == NULL)) {
+ if (IS_CV == IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
HANDLE_EXCEPTION();
@@ -31096,12 +37447,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_post_incdec_property_helper_SP
property = EX_CONSTANT(opline->op2);
- if (IS_CV == IS_VAR && UNEXPECTED(object == NULL)) {
- zend_throw_error(NULL, "Cannot increment/decrement overloaded objects nor string offsets");
-
- HANDLE_EXCEPTION();
- }
-
do {
if (IS_CV != IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) != IS_OBJECT)) {
ZVAL_DEREF(object);
@@ -31116,7 +37461,7 @@ 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)) {
- if (UNEXPECTED(zptr == &EG(error_zval))) {
+ if (UNEXPECTED(Z_ISERROR_P(zptr))) {
ZVAL_NULL(EX_VAR(opline->result.var));
} else {
if (EXPECTED(Z_TYPE_P(zptr) == IS_LONG)) {
@@ -31156,14 +37501,14 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_POST_DEC_OBJ_SPEC_CV_CONST_HAN
ZEND_VM_TAIL_CALL(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)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_fetch_static_prop_helper_SPEC_CV_CONST(int type ZEND_OPCODE_HANDLER_ARGS_DC)
{
USE_OPLINE
zval *varname;
zval *retval;
zend_string *name;
- HashTable *target_symbol_table;
+ zend_class_entry *ce;
SAVE_OPLINE();
varname = _get_zval_ptr_cv_undef(execute_data, opline->op1.var);
@@ -31180,119 +37525,73 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_fetch_var_address_helper_SPEC_
name = zval_get_string(varname);
}
- if (IS_CONST != IS_UNUSED) {
- zend_class_entry *ce;
-
- if (IS_CONST == IS_CONST) {
- if (IS_CV == IS_CONST && EXPECTED((ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)))) != NULL)) {
- retval = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)) + sizeof(void*));
-
- /* check if static properties were destoyed */
- if (UNEXPECTED(CE_STATIC_MEMBERS(ce) == NULL)) {
- zend_throw_error(NULL, "Access to undeclared static property: %s::$%s", ZSTR_VAL(ce->name), ZSTR_VAL(name));
-
- HANDLE_EXCEPTION();
- }
+ if (IS_CONST == IS_CONST) {
+ if (IS_CV == IS_CONST && EXPECTED((ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)))) != NULL)) {
+ retval = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)) + sizeof(void*));
- goto fetch_var_return;
- } else if (UNEXPECTED((ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)))) == NULL)) {
- ce = zend_fetch_class_by_name(Z_STR_P(EX_CONSTANT(opline->op2)), EX_CONSTANT(opline->op2) + 1, ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION);
- if (UNEXPECTED(ce == NULL)) {
- if (IS_CV != IS_CONST) {
- zend_string_release(name);
- }
+ /* check if static properties were destoyed */
+ if (UNEXPECTED(CE_STATIC_MEMBERS(ce) == NULL)) {
+ zend_throw_error(NULL, "Access to undeclared static property: %s::$%s", ZSTR_VAL(ce->name), ZSTR_VAL(name));
- ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
- }
- CACHE_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)), ce);
+ HANDLE_EXCEPTION();
}
- } else {
- ce = Z_CE_P(EX_VAR(opline->op2.var));
- if (IS_CV == IS_CONST &&
- (retval = CACHED_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)), ce)) != NULL) {
- /* check if static properties were destoyed */
- if (UNEXPECTED(CE_STATIC_MEMBERS(ce) == NULL)) {
- zend_throw_error(NULL, "Access to undeclared static property: %s::$%s", ZSTR_VAL(ce->name), ZSTR_VAL(name));
-
- HANDLE_EXCEPTION();
+ goto fetch_static_prop_return;
+ } else if (UNEXPECTED((ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)))) == NULL)) {
+ ce = zend_fetch_class_by_name(Z_STR_P(EX_CONSTANT(opline->op2)), EX_CONSTANT(opline->op2) + 1, ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION);
+ if (UNEXPECTED(ce == NULL)) {
+ if (IS_CV != IS_CONST) {
+ zend_string_release(name);
}
- goto fetch_var_return;
- }
- }
- retval = zend_std_get_static_property(ce, name, 0);
- if (UNEXPECTED(EG(exception))) {
- if (IS_CV != IS_CONST) {
- zend_string_release(name);
+ ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
-
- HANDLE_EXCEPTION();
- }
- if (IS_CV == IS_CONST && retval) {
- CACHE_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)), ce, retval);
+ CACHE_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)), ce);
}
-
} else {
- target_symbol_table = zend_get_target_symbol_table(execute_data, opline->extended_value & ZEND_FETCH_TYPE_MASK);
- retval = zend_hash_find(target_symbol_table, name);
- if (retval == NULL) {
- switch (type) {
- case BP_VAR_R:
- case BP_VAR_UNSET:
- zend_error(E_NOTICE,"Undefined variable: %s", ZSTR_VAL(name));
- /* break missing intentionally */
- case BP_VAR_IS:
- retval = &EG(uninitialized_zval);
- break;
- case BP_VAR_RW:
- zend_error(E_NOTICE,"Undefined variable: %s", ZSTR_VAL(name));
- retval = zend_hash_update(target_symbol_table, name, &EG(uninitialized_zval));
- break;
- case BP_VAR_W:
- retval = zend_hash_add_new(target_symbol_table, name, &EG(uninitialized_zval));
- break;
- EMPTY_SWITCH_DEFAULT_CASE()
- }
- /* GLOBAL or $$name variable may be an INDIRECT pointer to CV */
- } else if (Z_TYPE_P(retval) == IS_INDIRECT) {
- retval = Z_INDIRECT_P(retval);
- if (Z_TYPE_P(retval) == IS_UNDEF) {
- switch (type) {
- case BP_VAR_R:
- case BP_VAR_UNSET:
- zend_error(E_NOTICE,"Undefined variable: %s", ZSTR_VAL(name));
- /* break missing intentionally */
- case BP_VAR_IS:
- retval = &EG(uninitialized_zval);
- break;
- case BP_VAR_RW:
- zend_error(E_NOTICE,"Undefined variable: %s", ZSTR_VAL(name));
- /* break missing intentionally */
- case BP_VAR_W:
- ZVAL_NULL(retval);
- break;
- EMPTY_SWITCH_DEFAULT_CASE()
+ if (IS_CONST == IS_UNUSED) {
+ ce = zend_fetch_class(NULL, opline->op2.num);
+ if (UNEXPECTED(ce == NULL)) {
+ ZEND_ASSERT(EG(exception));
+ if (IS_CV != IS_CONST) {
+ zend_string_release(name);
}
+
+ HANDLE_EXCEPTION();
}
+ } else {
+ ce = Z_CE_P(EX_VAR(opline->op2.var));
}
- if ((opline->extended_value & ZEND_FETCH_TYPE_MASK) == ZEND_FETCH_STATIC) {
- if (Z_CONSTANT_P(retval)) {
- if (UNEXPECTED(zval_update_constant_ex(retval, 1, NULL) != SUCCESS)) {
+ if (IS_CV == IS_CONST &&
+ (retval = CACHED_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)), ce)) != NULL) {
- HANDLE_EXCEPTION();
- }
+ /* check if static properties were destoyed */
+ if (UNEXPECTED(CE_STATIC_MEMBERS(ce) == NULL)) {
+ zend_throw_error(NULL, "Access to undeclared static property: %s::$%s", ZSTR_VAL(ce->name), ZSTR_VAL(name));
+
+ HANDLE_EXCEPTION();
}
- } else if ((opline->extended_value & ZEND_FETCH_TYPE_MASK) != ZEND_FETCH_GLOBAL_LOCK) {
+ goto fetch_static_prop_return;
}
}
+ retval = zend_std_get_static_property(ce, name, 0);
+ if (UNEXPECTED(EG(exception))) {
+ if (IS_CV != IS_CONST) {
+ zend_string_release(name);
+ }
+
+ HANDLE_EXCEPTION();
+ }
+ if (IS_CV == IS_CONST && retval) {
+ CACHE_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)), ce, retval);
+ }
if (IS_CV != IS_CONST) {
zend_string_release(name);
}
-fetch_var_return:
+fetch_static_prop_return:
ZEND_ASSERT(retval != NULL);
if (type == BP_VAR_R || type == BP_VAR_IS) {
if (/*type == BP_VAR_R &&*/ Z_ISREF_P(retval) && Z_REFCOUNT_P(retval) == 1) {
@@ -31305,51 +37604,73 @@ fetch_var_return:
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_R_SPEC_CV_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_STATIC_PROP_R_SPEC_CV_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
- ZEND_VM_TAIL_CALL(zend_fetch_var_address_helper_SPEC_CV_CONST(BP_VAR_R ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_fetch_static_prop_helper_SPEC_CV_CONST(BP_VAR_R ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_W_SPEC_CV_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_STATIC_PROP_W_SPEC_CV_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
- ZEND_VM_TAIL_CALL(zend_fetch_var_address_helper_SPEC_CV_CONST(BP_VAR_W ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_fetch_static_prop_helper_SPEC_CV_CONST(BP_VAR_W ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_RW_SPEC_CV_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_STATIC_PROP_RW_SPEC_CV_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
- ZEND_VM_TAIL_CALL(zend_fetch_var_address_helper_SPEC_CV_CONST(BP_VAR_RW ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_fetch_static_prop_helper_SPEC_CV_CONST(BP_VAR_RW ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_FUNC_ARG_SPEC_CV_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_STATIC_PROP_FUNC_ARG_SPEC_CV_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
if (zend_is_by_ref_func_arg_fetch(opline, EX(call))) {
- ZEND_VM_TAIL_CALL(zend_fetch_var_address_helper_SPEC_CV_CONST(BP_VAR_W ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_fetch_static_prop_helper_SPEC_CV_CONST(BP_VAR_W ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
} else {
- ZEND_VM_TAIL_CALL(zend_fetch_var_address_helper_SPEC_CV_CONST(BP_VAR_R ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_fetch_static_prop_helper_SPEC_CV_CONST(BP_VAR_R ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_UNSET_SPEC_CV_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_STATIC_PROP_UNSET_SPEC_CV_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
- ZEND_VM_TAIL_CALL(zend_fetch_var_address_helper_SPEC_CV_CONST(BP_VAR_UNSET ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_fetch_static_prop_helper_SPEC_CV_CONST(BP_VAR_UNSET ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_IS_SPEC_CV_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_STATIC_PROP_IS_SPEC_CV_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
- ZEND_VM_TAIL_CALL(zend_fetch_var_address_helper_SPEC_CV_CONST(BP_VAR_IS ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_fetch_static_prop_helper_SPEC_CV_CONST(BP_VAR_IS ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_DIM_R_SPEC_CV_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
- zval *container;
+ zval *container, *dim, *value, *result;
SAVE_OPLINE();
- container = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var);
- zend_fetch_dimension_address_read_R(EX_VAR(opline->result.var), container, EX_CONSTANT(opline->op2), IS_CONST);
+ container = _get_zval_ptr_cv_undef(execute_data, opline->op1.var);
+ dim = EX_CONSTANT(opline->op2);
+ if (IS_CV != IS_CONST) {
+ if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) {
+fetch_dim_r_array:
+ value = zend_fetch_dimension_address_inner(Z_ARRVAL_P(container), dim, IS_CONST, BP_VAR_R);
+ result = EX_VAR(opline->result.var);
+ ZVAL_COPY(result, value);
+ } else if (EXPECTED(Z_TYPE_P(container) == IS_REFERENCE)) {
+ container = Z_REFVAL_P(container);
+ if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) {
+ goto fetch_dim_r_array;
+ } else {
+ goto fetch_dim_r_slow;
+ }
+ } else {
+fetch_dim_r_slow:
+ result = EX_VAR(opline->result.var);
+ zend_fetch_dimension_address_read_R_slow(result, container, dim);
+ }
+ } else {
+ result = EX_VAR(opline->result.var);
+ zend_fetch_dimension_address_read_R(result, container, dim, IS_CONST);
+ }
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
@@ -31364,14 +37685,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_DIM_W_SPEC_CV_CONST_HAND
SAVE_OPLINE();
container = _get_zval_ptr_cv_undef_BP_VAR_W(execute_data, opline->op1.var);
- if (IS_CV == IS_VAR && UNEXPECTED(container == NULL)) {
- zend_throw_error(NULL, "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);
if (IS_CV == IS_VAR && READY_TO_DESTROY(free_op1)) {
- EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var), 1);
+ EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var));
}
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
@@ -31384,16 +37701,12 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_DIM_RW_SPEC_CV_CONST_HAN
zval *container;
SAVE_OPLINE();
- container = _get_zval_ptr_cv_BP_VAR_RW(execute_data, opline->op1.var);
+ container = _get_zval_ptr_cv_undef_BP_VAR_RW(execute_data, opline->op1.var);
- if (IS_CV == IS_VAR && UNEXPECTED(container == NULL)) {
- zend_throw_error(NULL, "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);
if (IS_CV == IS_VAR && READY_TO_DESTROY(free_op1)) {
- EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var), 1);
+ EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var));
}
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
@@ -31406,7 +37719,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_DIM_IS_SPEC_CV_CONST_HAN
zval *container;
SAVE_OPLINE();
- container = _get_zval_ptr_cv_BP_VAR_IS(execute_data, opline->op1.var);
+ container = _get_zval_ptr_cv_undef(execute_data, opline->op1.var);
zend_fetch_dimension_address_read_IS(EX_VAR(opline->result.var), container, EX_CONSTANT(opline->op2), IS_CONST);
@@ -31422,21 +37735,16 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_DIM_FUNC_ARG_SPEC_CV_CON
SAVE_OPLINE();
if (zend_is_by_ref_func_arg_fetch(opline, EX(call))) {
- if (IS_CV == IS_CONST || IS_CV == IS_TMP_VAR) {
+ if ((IS_CV & (IS_CONST|IS_TMP_VAR))) {
zend_throw_error(NULL, "Cannot use temporary expression in write context");
HANDLE_EXCEPTION();
}
container = _get_zval_ptr_cv_undef_BP_VAR_W(execute_data, opline->op1.var);
- if (IS_CV == IS_VAR && UNEXPECTED(container == NULL)) {
- zend_throw_error(NULL, "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);
if (IS_CV == IS_VAR && READY_TO_DESTROY(free_op1)) {
- EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var), 1);
+ EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var));
}
@@ -31447,7 +37755,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_DIM_FUNC_ARG_SPEC_CV_CON
HANDLE_EXCEPTION();
}
- container = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var);
+ container = _get_zval_ptr_cv_undef(execute_data, opline->op1.var);
zend_fetch_dimension_address_read_R(EX_VAR(opline->result.var), container, EX_CONSTANT(opline->op2), IS_CONST);
@@ -31462,17 +37770,12 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_DIM_UNSET_SPEC_CV_CONST_
zval *container;
SAVE_OPLINE();
- container = _get_zval_ptr_cv_BP_VAR_UNSET(execute_data, opline->op1.var);
+ container = _get_zval_ptr_cv_undef_BP_VAR_UNSET(execute_data, opline->op1.var);
- if (IS_CV == IS_VAR && UNEXPECTED(container == NULL)) {
- zend_throw_error(NULL, "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);
if (IS_CV == IS_VAR && READY_TO_DESTROY(free_op1)) {
- EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var), 1);
+ EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var));
}
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
@@ -31489,7 +37792,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_R_SPEC_CV_CONST_HAND
SAVE_OPLINE();
container = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var);
- if (IS_CV == IS_UNUSED && UNEXPECTED(Z_OBJ_P(container) == NULL)) {
+ if (IS_CV == IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
HANDLE_EXCEPTION();
@@ -31561,21 +37864,16 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_W_SPEC_CV_CONST_HAND
property = EX_CONSTANT(opline->op2);
container = _get_zval_ptr_cv_undef_BP_VAR_W(execute_data, opline->op1.var);
- if (IS_CV == IS_UNUSED && UNEXPECTED(Z_OBJ_P(container) == NULL)) {
+ if (IS_CV == IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
HANDLE_EXCEPTION();
}
- if (IS_CV == IS_VAR && UNEXPECTED(container == NULL)) {
- zend_throw_error(NULL, "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);
if (IS_CV == IS_VAR && READY_TO_DESTROY(free_op1)) {
- EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var), 0);
+ EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var));
}
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
@@ -31592,20 +37890,15 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_RW_SPEC_CV_CONST_HAN
property = EX_CONSTANT(opline->op2);
container = _get_zval_ptr_cv_BP_VAR_RW(execute_data, opline->op1.var);
- if (IS_CV == IS_UNUSED && UNEXPECTED(Z_OBJ_P(container) == NULL)) {
+ if (IS_CV == IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
HANDLE_EXCEPTION();
}
- if (IS_CV == IS_VAR && UNEXPECTED(container == NULL)) {
- zend_throw_error(NULL, "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);
if (IS_CV == IS_VAR && READY_TO_DESTROY(free_op1)) {
- EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var), 0);
+ EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var));
}
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
@@ -31622,7 +37915,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_IS_SPEC_CV_CONST_HAN
SAVE_OPLINE();
container = _get_zval_ptr_cv_BP_VAR_IS(execute_data, opline->op1.var);
- if (IS_CV == IS_UNUSED && UNEXPECTED(Z_OBJ_P(container) == NULL)) {
+ if (IS_CV == IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
HANDLE_EXCEPTION();
@@ -31697,26 +37990,21 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_FUNC_ARG_SPEC_CV_CON
property = EX_CONSTANT(opline->op2);
container = _get_zval_ptr_cv_undef_BP_VAR_W(execute_data, opline->op1.var);
- if (IS_CV == IS_UNUSED && UNEXPECTED(Z_OBJ_P(container) == NULL)) {
+ if (IS_CV == IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
HANDLE_EXCEPTION();
}
- if (IS_CV == IS_CONST || IS_CV == IS_TMP_VAR) {
+ if ((IS_CV & (IS_CONST|IS_TMP_VAR))) {
zend_throw_error(NULL, "Cannot use temporary expression in write context");
HANDLE_EXCEPTION();
}
- if (IS_CV == IS_VAR && UNEXPECTED(container == NULL)) {
- zend_throw_error(NULL, "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);
if (IS_CV == IS_VAR && READY_TO_DESTROY(free_op1)) {
- EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var), 0);
+ EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var));
}
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
@@ -31734,7 +38022,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_UNSET_SPEC_CV_CONST_
SAVE_OPLINE();
container = _get_zval_ptr_cv_BP_VAR_UNSET(execute_data, opline->op1.var);
- if (IS_CV == IS_UNUSED && UNEXPECTED(Z_OBJ_P(container) == NULL)) {
+ if (IS_CV == IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
HANDLE_EXCEPTION();
@@ -31742,15 +38030,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_UNSET_SPEC_CV_CONST_
property = EX_CONSTANT(opline->op2);
- if (IS_CV == IS_VAR && UNEXPECTED(container == NULL)) {
- zend_throw_error(NULL, "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);
if (IS_CV == IS_VAR && READY_TO_DESTROY(free_op1)) {
- EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var), 0);
+ EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var));
}
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
@@ -31764,78 +38047,641 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_LIST_SPEC_CV_CONST_HANDL
SAVE_OPLINE();
container = _get_zval_ptr_cv_undef(execute_data, opline->op1.var);
+ zend_fetch_dimension_address_read_LIST(EX_VAR(opline->result.var), container, EX_CONSTANT(opline->op2));
-try_fetch_list:
- if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) {
- zval *value = zend_hash_index_find(Z_ARRVAL_P(container), Z_LVAL_P(EX_CONSTANT(opline->op2)));
+ ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_CONST_OP_DATA_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+
+ zval *object, *property_name, *value, tmp;
+
+ SAVE_OPLINE();
+ object = _get_zval_ptr_cv_undef_BP_VAR_W(execute_data, opline->op1.var);
+
+ if (IS_CV == IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) == IS_UNDEF)) {
+ zend_throw_error(NULL, "Using $this when not in object context");
+
+ HANDLE_EXCEPTION();
+ }
+
+ property_name = EX_CONSTANT(opline->op2);
+ value = EX_CONSTANT((opline+1)->op1);
+
+ if (IS_CV != IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) != IS_OBJECT)) {
+ do {
+ if (Z_ISREF_P(object)) {
+ object = Z_REFVAL_P(object);
+ if (EXPECTED(Z_TYPE_P(object) == IS_OBJECT)) {
+ break;
+ }
+ }
+ if (EXPECTED(Z_TYPE_P(object) <= IS_FALSE ||
+ (Z_TYPE_P(object) == IS_STRING && Z_STRLEN_P(object) == 0))) {
+ zend_object *obj;
+
+ zval_ptr_dtor(object);
+ object_init(object);
+ Z_ADDREF_P(object);
+ obj = Z_OBJ_P(object);
+ zend_error(E_WARNING, "Creating default object from empty value");
+ if (GC_REFCOUNT(obj) == 1) {
+ /* the enclosing container was deleted, obj is unreferenced */
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+
+ OBJ_RELEASE(obj);
+ goto exit_assign_obj;
+ }
+ Z_DELREF_P(object);
+ } else {
+ if (IS_CV != IS_VAR || EXPECTED(!Z_ISERROR_P(object))) {
+ zend_error(E_WARNING, "Attempt to assign property of non-object");
+ }
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+
+ goto exit_assign_obj;
+ }
+ } while (0);
+ }
+
+ if (IS_CONST == IS_CONST &&
+ EXPECTED(Z_OBJCE_P(object) == CACHED_PTR(Z_CACHE_SLOT_P(property_name)))) {
+ uint32_t prop_offset = (uint32_t)(intptr_t)CACHED_PTR(Z_CACHE_SLOT_P(property_name) + sizeof(void*));
+ zend_object *zobj = Z_OBJ_P(object);
+ zval *property;
+
+ if (EXPECTED(prop_offset != (uint32_t)ZEND_DYNAMIC_PROPERTY_OFFSET)) {
+ property = OBJ_PROP(zobj, prop_offset);
+ if (Z_TYPE_P(property) != IS_UNDEF) {
+fast_assign_obj:
+ value = zend_assign_to_variable(property, value, IS_CONST);
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+ goto exit_assign_obj;
+ }
+ } else {
+ if (EXPECTED(zobj->properties != NULL)) {
+ if (UNEXPECTED(GC_REFCOUNT(zobj->properties) > 1)) {
+ if (EXPECTED(!(GC_FLAGS(zobj->properties) & IS_ARRAY_IMMUTABLE))) {
+ GC_REFCOUNT(zobj->properties)--;
+ }
+ zobj->properties = zend_array_dup(zobj->properties);
+ }
+ property = zend_hash_find(zobj->properties, Z_STR_P(property_name));
+ if (property) {
+ goto fast_assign_obj;
+ }
+ }
+
+ if (!zobj->ce->__set) {
+
+ if (EXPECTED(zobj->properties == NULL)) {
+ rebuild_object_properties(zobj);
+ }
+ if (IS_CONST == IS_CONST) {
+ if (UNEXPECTED(Z_OPT_REFCOUNTED_P(value))) {
+ Z_ADDREF_P(value);
+ }
+ } else if (IS_CONST != IS_TMP_VAR) {
+ if (Z_ISREF_P(value)) {
+ if (IS_CONST == IS_VAR) {
+ zend_reference *ref = Z_REF_P(value);
+ if (--GC_REFCOUNT(ref) == 0) {
+ ZVAL_COPY_VALUE(&tmp, Z_REFVAL_P(value));
+ efree_size(ref, sizeof(zend_reference));
+ value = &tmp;
+ } else {
+ value = Z_REFVAL_P(value);
+ if (Z_REFCOUNTED_P(value)) {
+ Z_ADDREF_P(value);
+ }
+ }
+ } else {
+ value = Z_REFVAL_P(value);
+ if (Z_REFCOUNTED_P(value)) {
+ Z_ADDREF_P(value);
+ }
+ }
+ } else if (IS_CONST == IS_CV && Z_REFCOUNTED_P(value)) {
+ Z_ADDREF_P(value);
+ }
+ }
+ zend_hash_add_new(zobj->properties, Z_STR_P(property_name), value);
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+ goto exit_assign_obj;
+ }
+ }
+ }
- if (UNEXPECTED(value == NULL)) {
- zend_error(E_NOTICE,"Undefined offset: " ZEND_ULONG_FMT, Z_LVAL_P(EX_CONSTANT(opline->op2)));
+ if (!Z_OBJ_HT_P(object)->write_property) {
+ zend_error(E_WARNING, "Attempt to assign property of non-object");
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+
+ goto exit_assign_obj;
+ }
+
+ if (IS_CONST == IS_CV || IS_CONST == IS_VAR) {
+ ZVAL_DEREF(value);
+ }
+
+ Z_OBJ_HT_P(object)->write_property(object, property_name, value, (IS_CONST == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property_name)) : NULL);
+
+ if (UNEXPECTED(RETURN_VALUE_USED(opline)) && EXPECTED(!EG(exception))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+
+exit_assign_obj:
+
+
+ /* assign_obj has two opcodes! */
+ ZEND_VM_NEXT_OPCODE_EX(1, 2);
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_CONST_OP_DATA_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zend_free_op free_op_data;
+ zval *object, *property_name, *value, tmp;
+
+ SAVE_OPLINE();
+ object = _get_zval_ptr_cv_undef_BP_VAR_W(execute_data, opline->op1.var);
+
+ if (IS_CV == IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) == IS_UNDEF)) {
+ zend_throw_error(NULL, "Using $this when not in object context");
+
+ HANDLE_EXCEPTION();
+ }
+
+ property_name = EX_CONSTANT(opline->op2);
+ value = _get_zval_ptr_tmp((opline+1)->op1.var, execute_data, &free_op_data);
+
+ if (IS_CV != IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) != IS_OBJECT)) {
+ do {
+ if (Z_ISREF_P(object)) {
+ object = Z_REFVAL_P(object);
+ if (EXPECTED(Z_TYPE_P(object) == IS_OBJECT)) {
+ break;
+ }
+ }
+ if (EXPECTED(Z_TYPE_P(object) <= IS_FALSE ||
+ (Z_TYPE_P(object) == IS_STRING && Z_STRLEN_P(object) == 0))) {
+ zend_object *obj;
+
+ zval_ptr_dtor(object);
+ object_init(object);
+ Z_ADDREF_P(object);
+ obj = Z_OBJ_P(object);
+ zend_error(E_WARNING, "Creating default object from empty value");
+ if (GC_REFCOUNT(obj) == 1) {
+ /* the enclosing container was deleted, obj is unreferenced */
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+ zval_ptr_dtor_nogc(free_op_data);
+ OBJ_RELEASE(obj);
+ goto exit_assign_obj;
+ }
+ Z_DELREF_P(object);
+ } else {
+ if (IS_CV != IS_VAR || EXPECTED(!Z_ISERROR_P(object))) {
+ zend_error(E_WARNING, "Attempt to assign property of non-object");
+ }
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+ zval_ptr_dtor_nogc(free_op_data);
+ goto exit_assign_obj;
+ }
+ } while (0);
+ }
+
+ if (IS_CONST == IS_CONST &&
+ EXPECTED(Z_OBJCE_P(object) == CACHED_PTR(Z_CACHE_SLOT_P(property_name)))) {
+ uint32_t prop_offset = (uint32_t)(intptr_t)CACHED_PTR(Z_CACHE_SLOT_P(property_name) + sizeof(void*));
+ zend_object *zobj = Z_OBJ_P(object);
+ zval *property;
+
+ if (EXPECTED(prop_offset != (uint32_t)ZEND_DYNAMIC_PROPERTY_OFFSET)) {
+ property = OBJ_PROP(zobj, prop_offset);
+ if (Z_TYPE_P(property) != IS_UNDEF) {
+fast_assign_obj:
+ value = zend_assign_to_variable(property, value, IS_TMP_VAR);
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+ goto exit_assign_obj;
+ }
} else {
- ZVAL_COPY(EX_VAR(opline->result.var), value);
+ if (EXPECTED(zobj->properties != NULL)) {
+ if (UNEXPECTED(GC_REFCOUNT(zobj->properties) > 1)) {
+ if (EXPECTED(!(GC_FLAGS(zobj->properties) & IS_ARRAY_IMMUTABLE))) {
+ GC_REFCOUNT(zobj->properties)--;
+ }
+ zobj->properties = zend_array_dup(zobj->properties);
+ }
+ property = zend_hash_find(zobj->properties, Z_STR_P(property_name));
+ if (property) {
+ goto fast_assign_obj;
+ }
+ }
+
+ if (!zobj->ce->__set) {
+
+ if (EXPECTED(zobj->properties == NULL)) {
+ rebuild_object_properties(zobj);
+ }
+ if (IS_TMP_VAR == IS_CONST) {
+ if (UNEXPECTED(Z_OPT_REFCOUNTED_P(value))) {
+ Z_ADDREF_P(value);
+ }
+ } else if (IS_TMP_VAR != IS_TMP_VAR) {
+ if (Z_ISREF_P(value)) {
+ if (IS_TMP_VAR == IS_VAR) {
+ zend_reference *ref = Z_REF_P(value);
+ if (--GC_REFCOUNT(ref) == 0) {
+ ZVAL_COPY_VALUE(&tmp, Z_REFVAL_P(value));
+ efree_size(ref, sizeof(zend_reference));
+ value = &tmp;
+ } else {
+ value = Z_REFVAL_P(value);
+ if (Z_REFCOUNTED_P(value)) {
+ Z_ADDREF_P(value);
+ }
+ }
+ } else {
+ value = Z_REFVAL_P(value);
+ if (Z_REFCOUNTED_P(value)) {
+ Z_ADDREF_P(value);
+ }
+ }
+ } else if (IS_TMP_VAR == IS_CV && Z_REFCOUNTED_P(value)) {
+ Z_ADDREF_P(value);
+ }
+ }
+ zend_hash_add_new(zobj->properties, Z_STR_P(property_name), value);
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+ goto exit_assign_obj;
+ }
}
- } 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);
+ }
+
+ if (!Z_OBJ_HT_P(object)->write_property) {
+ zend_error(E_WARNING, "Attempt to assign property of non-object");
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+ zval_ptr_dtor_nogc(free_op_data);
+ goto exit_assign_obj;
+ }
+
+ if (IS_TMP_VAR == IS_CV || IS_TMP_VAR == IS_VAR) {
+ ZVAL_DEREF(value);
+ }
+
+ Z_OBJ_HT_P(object)->write_property(object, property_name, value, (IS_CONST == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property_name)) : NULL);
+
+ if (UNEXPECTED(RETURN_VALUE_USED(opline)) && EXPECTED(!EG(exception))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+ zval_ptr_dtor_nogc(free_op_data);
+exit_assign_obj:
+
+
+ /* assign_obj has two opcodes! */
+ ZEND_VM_NEXT_OPCODE_EX(1, 2);
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_CONST_OP_DATA_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zend_free_op free_op_data;
+ zval *object, *property_name, *value, tmp;
+
+ SAVE_OPLINE();
+ object = _get_zval_ptr_cv_undef_BP_VAR_W(execute_data, opline->op1.var);
+
+ if (IS_CV == IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) == IS_UNDEF)) {
+ zend_throw_error(NULL, "Using $this when not in object context");
+
+ HANDLE_EXCEPTION();
+ }
+
+ property_name = EX_CONSTANT(opline->op2);
+ value = _get_zval_ptr_var((opline+1)->op1.var, execute_data, &free_op_data);
+
+ if (IS_CV != IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) != IS_OBJECT)) {
+ do {
+ if (Z_ISREF_P(object)) {
+ object = Z_REFVAL_P(object);
+ if (EXPECTED(Z_TYPE_P(object) == IS_OBJECT)) {
+ break;
+ }
+ }
+ if (EXPECTED(Z_TYPE_P(object) <= IS_FALSE ||
+ (Z_TYPE_P(object) == IS_STRING && Z_STRLEN_P(object) == 0))) {
+ zend_object *obj;
- if (retval) {
- if (result != retval) {
- ZVAL_COPY(result, retval);
+ zval_ptr_dtor(object);
+ object_init(object);
+ Z_ADDREF_P(object);
+ obj = Z_OBJ_P(object);
+ zend_error(E_WARNING, "Creating default object from empty value");
+ if (GC_REFCOUNT(obj) == 1) {
+ /* the enclosing container was deleted, obj is unreferenced */
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+ zval_ptr_dtor_nogc(free_op_data);
+ OBJ_RELEASE(obj);
+ goto exit_assign_obj;
+ }
+ Z_DELREF_P(object);
+ } else {
+ if (IS_CV != IS_VAR || EXPECTED(!Z_ISERROR_P(object))) {
+ zend_error(E_WARNING, "Attempt to assign property of non-object");
+ }
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+ zval_ptr_dtor_nogc(free_op_data);
+ goto exit_assign_obj;
+ }
+ } while (0);
+ }
+
+ if (IS_CONST == IS_CONST &&
+ EXPECTED(Z_OBJCE_P(object) == CACHED_PTR(Z_CACHE_SLOT_P(property_name)))) {
+ uint32_t prop_offset = (uint32_t)(intptr_t)CACHED_PTR(Z_CACHE_SLOT_P(property_name) + sizeof(void*));
+ zend_object *zobj = Z_OBJ_P(object);
+ zval *property;
+
+ if (EXPECTED(prop_offset != (uint32_t)ZEND_DYNAMIC_PROPERTY_OFFSET)) {
+ property = OBJ_PROP(zobj, prop_offset);
+ if (Z_TYPE_P(property) != IS_UNDEF) {
+fast_assign_obj:
+ value = zend_assign_to_variable(property, value, IS_VAR);
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+ goto exit_assign_obj;
}
} else {
- ZVAL_NULL(result);
+ if (EXPECTED(zobj->properties != NULL)) {
+ if (UNEXPECTED(GC_REFCOUNT(zobj->properties) > 1)) {
+ if (EXPECTED(!(GC_FLAGS(zobj->properties) & IS_ARRAY_IMMUTABLE))) {
+ GC_REFCOUNT(zobj->properties)--;
+ }
+ zobj->properties = zend_array_dup(zobj->properties);
+ }
+ property = zend_hash_find(zobj->properties, Z_STR_P(property_name));
+ if (property) {
+ goto fast_assign_obj;
+ }
+ }
+
+ if (!zobj->ce->__set) {
+
+ if (EXPECTED(zobj->properties == NULL)) {
+ rebuild_object_properties(zobj);
+ }
+ if (IS_VAR == IS_CONST) {
+ if (UNEXPECTED(Z_OPT_REFCOUNTED_P(value))) {
+ Z_ADDREF_P(value);
+ }
+ } else if (IS_VAR != IS_TMP_VAR) {
+ if (Z_ISREF_P(value)) {
+ if (IS_VAR == IS_VAR) {
+ zend_reference *ref = Z_REF_P(value);
+ if (--GC_REFCOUNT(ref) == 0) {
+ ZVAL_COPY_VALUE(&tmp, Z_REFVAL_P(value));
+ efree_size(ref, sizeof(zend_reference));
+ value = &tmp;
+ } else {
+ value = Z_REFVAL_P(value);
+ if (Z_REFCOUNTED_P(value)) {
+ Z_ADDREF_P(value);
+ }
+ }
+ } else {
+ value = Z_REFVAL_P(value);
+ if (Z_REFCOUNTED_P(value)) {
+ Z_ADDREF_P(value);
+ }
+ }
+ } else if (IS_VAR == IS_CV && Z_REFCOUNTED_P(value)) {
+ Z_ADDREF_P(value);
+ }
+ }
+ zend_hash_add_new(zobj->properties, Z_STR_P(property_name), value);
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+ goto exit_assign_obj;
+ }
}
- } 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);
+ }
+
+ if (!Z_OBJ_HT_P(object)->write_property) {
+ zend_error(E_WARNING, "Attempt to assign property of non-object");
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
}
- ZVAL_NULL(EX_VAR(opline->result.var));
+ zval_ptr_dtor_nogc(free_op_data);
+ goto exit_assign_obj;
}
- ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
+
+ if (IS_VAR == IS_CV || IS_VAR == IS_VAR) {
+ ZVAL_DEREF(value);
+ }
+
+ Z_OBJ_HT_P(object)->write_property(object, property_name, value, (IS_CONST == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property_name)) : NULL);
+
+ if (UNEXPECTED(RETURN_VALUE_USED(opline)) && EXPECTED(!EG(exception))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+ zval_ptr_dtor_nogc(free_op_data);
+exit_assign_obj:
+
+
+ /* assign_obj has two opcodes! */
+ ZEND_VM_NEXT_OPCODE_EX(1, 2);
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_CONST_OP_DATA_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
- zval *object;
- zval *property_name;
+ zval *object, *property_name, *value, tmp;
SAVE_OPLINE();
object = _get_zval_ptr_cv_undef_BP_VAR_W(execute_data, opline->op1.var);
- if (IS_CV == IS_UNUSED && UNEXPECTED(Z_OBJ_P(object) == NULL)) {
+ if (IS_CV == IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
HANDLE_EXCEPTION();
}
property_name = EX_CONSTANT(opline->op2);
+ value = _get_zval_ptr_cv_BP_VAR_R(execute_data, (opline+1)->op1.var);
- if (IS_CV == IS_VAR && UNEXPECTED(object == NULL)) {
- zend_throw_error(NULL, "Cannot use string offset as an array");
+ if (IS_CV != IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) != IS_OBJECT)) {
+ do {
+ if (Z_ISREF_P(object)) {
+ object = Z_REFVAL_P(object);
+ if (EXPECTED(Z_TYPE_P(object) == IS_OBJECT)) {
+ break;
+ }
+ }
+ if (EXPECTED(Z_TYPE_P(object) <= IS_FALSE ||
+ (Z_TYPE_P(object) == IS_STRING && Z_STRLEN_P(object) == 0))) {
+ zend_object *obj;
- HANDLE_EXCEPTION();
+ zval_ptr_dtor(object);
+ object_init(object);
+ Z_ADDREF_P(object);
+ obj = Z_OBJ_P(object);
+ zend_error(E_WARNING, "Creating default object from empty value");
+ if (GC_REFCOUNT(obj) == 1) {
+ /* the enclosing container was deleted, obj is unreferenced */
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+
+ OBJ_RELEASE(obj);
+ goto exit_assign_obj;
+ }
+ Z_DELREF_P(object);
+ } else {
+ if (IS_CV != IS_VAR || EXPECTED(!Z_ISERROR_P(object))) {
+ zend_error(E_WARNING, "Attempt to assign property of non-object");
+ }
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+
+ goto exit_assign_obj;
+ }
+ } while (0);
}
- 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));
+
+ if (IS_CONST == IS_CONST &&
+ EXPECTED(Z_OBJCE_P(object) == CACHED_PTR(Z_CACHE_SLOT_P(property_name)))) {
+ uint32_t prop_offset = (uint32_t)(intptr_t)CACHED_PTR(Z_CACHE_SLOT_P(property_name) + sizeof(void*));
+ zend_object *zobj = Z_OBJ_P(object);
+ zval *property;
+
+ if (EXPECTED(prop_offset != (uint32_t)ZEND_DYNAMIC_PROPERTY_OFFSET)) {
+ property = OBJ_PROP(zobj, prop_offset);
+ if (Z_TYPE_P(property) != IS_UNDEF) {
+fast_assign_obj:
+ value = zend_assign_to_variable(property, value, IS_CV);
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+ goto exit_assign_obj;
+ }
+ } else {
+ if (EXPECTED(zobj->properties != NULL)) {
+ if (UNEXPECTED(GC_REFCOUNT(zobj->properties) > 1)) {
+ if (EXPECTED(!(GC_FLAGS(zobj->properties) & IS_ARRAY_IMMUTABLE))) {
+ GC_REFCOUNT(zobj->properties)--;
+ }
+ zobj->properties = zend_array_dup(zobj->properties);
+ }
+ property = zend_hash_find(zobj->properties, Z_STR_P(property_name));
+ if (property) {
+ goto fast_assign_obj;
+ }
+ }
+
+ if (!zobj->ce->__set) {
+
+ if (EXPECTED(zobj->properties == NULL)) {
+ rebuild_object_properties(zobj);
+ }
+ if (IS_CV == IS_CONST) {
+ if (UNEXPECTED(Z_OPT_REFCOUNTED_P(value))) {
+ Z_ADDREF_P(value);
+ }
+ } else if (IS_CV != IS_TMP_VAR) {
+ if (Z_ISREF_P(value)) {
+ if (IS_CV == IS_VAR) {
+ zend_reference *ref = Z_REF_P(value);
+ if (--GC_REFCOUNT(ref) == 0) {
+ ZVAL_COPY_VALUE(&tmp, Z_REFVAL_P(value));
+ efree_size(ref, sizeof(zend_reference));
+ value = &tmp;
+ } else {
+ value = Z_REFVAL_P(value);
+ if (Z_REFCOUNTED_P(value)) {
+ Z_ADDREF_P(value);
+ }
+ }
+ } else {
+ value = Z_REFVAL_P(value);
+ if (Z_REFCOUNTED_P(value)) {
+ Z_ADDREF_P(value);
+ }
+ }
+ } else if (IS_CV == IS_CV && Z_REFCOUNTED_P(value)) {
+ Z_ADDREF_P(value);
+ }
+ }
+ zend_hash_add_new(zobj->properties, Z_STR_P(property_name), value);
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+ goto exit_assign_obj;
+ }
+ }
+ }
+
+ if (!Z_OBJ_HT_P(object)->write_property) {
+ zend_error(E_WARNING, "Attempt to assign property of non-object");
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+
+ goto exit_assign_obj;
+ }
+
+ if (IS_CV == IS_CV || IS_CV == IS_VAR) {
+ ZVAL_DEREF(value);
+ }
+
+ Z_OBJ_HT_P(object)->write_property(object, property_name, value, (IS_CONST == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property_name)) : NULL);
+
+ if (UNEXPECTED(RETURN_VALUE_USED(opline)) && EXPECTED(!EG(exception))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+
+exit_assign_obj:
/* assign_obj has two opcodes! */
ZEND_VM_NEXT_OPCODE_EX(1, 2);
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_CV_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_CV_CONST_OP_DATA_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
zval *object_ptr;
- zend_free_op free_op_data1;
+
zval *value;
zval *variable_ptr;
zval *dim;
@@ -31843,40 +38689,214 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_CV_CONST_HANDL
SAVE_OPLINE();
object_ptr = _get_zval_ptr_cv_undef_BP_VAR_W(execute_data, opline->op1.var);
- if (IS_CV == IS_VAR && UNEXPECTED(object_ptr == NULL)) {
- zend_throw_error(NULL, "Cannot use string offset as an array");
- FREE_UNFETCHED_OP((opline+1)->op1_type, (opline+1)->op1.var);
+ if (EXPECTED(Z_TYPE_P(object_ptr) == IS_ARRAY)) {
+try_assign_dim_array:
+ SEPARATE_ARRAY(object_ptr);
+ if (IS_CONST == IS_UNUSED) {
+ variable_ptr = zend_hash_next_index_insert(Z_ARRVAL_P(object_ptr), &EG(uninitialized_zval));
+ if (UNEXPECTED(variable_ptr == NULL)) {
+ zend_error(E_WARNING, "Cannot add element to the array as the next element is already occupied");
+ goto assign_dim_error;
+ }
+ } else {
+ dim = EX_CONSTANT(opline->op2);
+ if (IS_CONST == IS_CONST) {
+ variable_ptr = zend_fetch_dimension_address_inner_W_CONST(Z_ARRVAL_P(object_ptr), dim);
+ } else {
+ variable_ptr = zend_fetch_dimension_address_inner_W(Z_ARRVAL_P(object_ptr), dim);
+ }
+ if (UNEXPECTED(variable_ptr == NULL)) {
+ goto assign_dim_error;
+ }
+ }
+ value = EX_CONSTANT((opline+1)->op1);
+ value = zend_assign_to_variable(variable_ptr, value, IS_CONST);
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+ } 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)) {
+ dim = EX_CONSTANT(opline->op2);
+ value = EX_CONSTANT((opline+1)->op1);
- HANDLE_EXCEPTION();
+ zend_assign_to_object_dim(object_ptr, dim, value);
+
+ if (UNEXPECTED(RETURN_VALUE_USED(opline)) && EXPECTED(!EG(exception))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+
+ } else if (EXPECTED(Z_TYPE_P(object_ptr) == IS_STRING)) {
+ if (IS_CONST == IS_UNUSED) {
+ zend_throw_error(NULL, "[] operator not supported for strings");
+
+
+ HANDLE_EXCEPTION();
+ } else {
+ dim = EX_CONSTANT(opline->op2);
+ value = EX_CONSTANT((opline+1)->op1);
+ zend_assign_to_string_offset(object_ptr, dim, value, (UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL));
+
+ }
+ } else if (EXPECTED(Z_TYPE_P(object_ptr) <= IS_FALSE)) {
+ 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 (IS_CV != IS_VAR || EXPECTED(!Z_ISERROR_P(object_ptr))) {
+ zend_error(E_WARNING, "Cannot use a scalar value as an array");
+ }
+ dim = EX_CONSTANT(opline->op2);
+assign_dim_error:
+
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+ }
}
+ if (IS_CONST != IS_UNUSED) {
+
+ }
+
+ /* assign_dim has two opcodes! */
+ ZEND_VM_NEXT_OPCODE_EX(1, 2);
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_CV_CONST_OP_DATA_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+
+ zval *object_ptr;
+ zend_free_op free_op_data;
+ zval *value;
+ zval *variable_ptr;
+ zval *dim;
+
+ SAVE_OPLINE();
+ object_ptr = _get_zval_ptr_cv_undef_BP_VAR_W(execute_data, opline->op1.var);
if (EXPECTED(Z_TYPE_P(object_ptr) == IS_ARRAY)) {
try_assign_dim_array:
+ SEPARATE_ARRAY(object_ptr);
if (IS_CONST == IS_UNUSED) {
- SEPARATE_ARRAY(object_ptr);
variable_ptr = zend_hash_next_index_insert(Z_ARRVAL_P(object_ptr), &EG(uninitialized_zval));
if (UNEXPECTED(variable_ptr == NULL)) {
zend_error(E_WARNING, "Cannot add element to the array as the next element is already occupied");
- variable_ptr = &EG(error_zval);
+ goto assign_dim_error;
}
} else {
dim = EX_CONSTANT(opline->op2);
- SEPARATE_ARRAY(object_ptr);
- variable_ptr = zend_fetch_dimension_address_inner(Z_ARRVAL_P(object_ptr), dim, IS_CONST, BP_VAR_W);
-
+ if (IS_CONST == IS_CONST) {
+ variable_ptr = zend_fetch_dimension_address_inner_W_CONST(Z_ARRVAL_P(object_ptr), dim);
+ } else {
+ variable_ptr = zend_fetch_dimension_address_inner_W(Z_ARRVAL_P(object_ptr), dim);
+ }
+ if (UNEXPECTED(variable_ptr == NULL)) {
+ goto assign_dim_error;
+ }
}
- value = get_zval_ptr_r((opline+1)->op1_type, (opline+1)->op1, execute_data, &free_op_data1);
- if (UNEXPECTED(variable_ptr == &EG(error_zval))) {
- FREE_OP(free_op_data1);
+ value = _get_zval_ptr_tmp((opline+1)->op1.var, execute_data, &free_op_data);
+ value = zend_assign_to_variable(variable_ptr, value, IS_TMP_VAR);
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+ } 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)) {
+ dim = EX_CONSTANT(opline->op2);
+ value = _get_zval_ptr_tmp((opline+1)->op1.var, execute_data, &free_op_data);
+
+ zend_assign_to_object_dim(object_ptr, dim, value);
+
+ if (UNEXPECTED(RETURN_VALUE_USED(opline)) && EXPECTED(!EG(exception))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+
+ zval_ptr_dtor_nogc(free_op_data);
+ } else if (EXPECTED(Z_TYPE_P(object_ptr) == IS_STRING)) {
+ if (IS_CONST == IS_UNUSED) {
+ zend_throw_error(NULL, "[] operator not supported for strings");
+ zval_ptr_dtor_nogc(EX_VAR((opline+1)->op1.var));
+
+ HANDLE_EXCEPTION();
+ } else {
+ dim = EX_CONSTANT(opline->op2);
+ value = _get_zval_ptr_tmp((opline+1)->op1.var, execute_data, &free_op_data);
+ zend_assign_to_string_offset(object_ptr, dim, value, (UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL));
+ zval_ptr_dtor_nogc(free_op_data);
+ }
+ } else if (EXPECTED(Z_TYPE_P(object_ptr) <= IS_FALSE)) {
+ 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 (IS_CV != IS_VAR || EXPECTED(!Z_ISERROR_P(object_ptr))) {
+ zend_error(E_WARNING, "Cannot use a scalar value as an array");
+ }
+ dim = EX_CONSTANT(opline->op2);
+assign_dim_error:
+ zval_ptr_dtor_nogc(EX_VAR((opline+1)->op1.var));
if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
ZVAL_NULL(EX_VAR(opline->result.var));
}
+ }
+ }
+ if (IS_CONST != IS_UNUSED) {
+
+ }
+
+ /* assign_dim has two opcodes! */
+ ZEND_VM_NEXT_OPCODE_EX(1, 2);
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_CV_CONST_OP_DATA_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+
+ zval *object_ptr;
+ zend_free_op free_op_data;
+ zval *value;
+ zval *variable_ptr;
+ zval *dim;
+
+ SAVE_OPLINE();
+ object_ptr = _get_zval_ptr_cv_undef_BP_VAR_W(execute_data, opline->op1.var);
+
+ if (EXPECTED(Z_TYPE_P(object_ptr) == IS_ARRAY)) {
+try_assign_dim_array:
+ SEPARATE_ARRAY(object_ptr);
+ if (IS_CONST == IS_UNUSED) {
+ variable_ptr = zend_hash_next_index_insert(Z_ARRVAL_P(object_ptr), &EG(uninitialized_zval));
+ if (UNEXPECTED(variable_ptr == NULL)) {
+ zend_error(E_WARNING, "Cannot add element to the array as the next element is already occupied");
+ goto assign_dim_error;
+ }
} else {
- value = zend_assign_to_variable(variable_ptr, value, (opline+1)->op1_type);
- if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
- ZVAL_COPY(EX_VAR(opline->result.var), value);
+ dim = EX_CONSTANT(opline->op2);
+ if (IS_CONST == IS_CONST) {
+ variable_ptr = zend_fetch_dimension_address_inner_W_CONST(Z_ARRVAL_P(object_ptr), dim);
+ } else {
+ variable_ptr = zend_fetch_dimension_address_inner_W(Z_ARRVAL_P(object_ptr), dim);
+ }
+ if (UNEXPECTED(variable_ptr == NULL)) {
+ goto assign_dim_error;
}
}
+ value = _get_zval_ptr_var((opline+1)->op1.var, execute_data, &free_op_data);
+ value = zend_assign_to_variable(variable_ptr, value, IS_VAR);
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
} else {
if (EXPECTED(Z_ISREF_P(object_ptr))) {
object_ptr = Z_REFVAL_P(object_ptr);
@@ -31885,58 +38905,144 @@ try_assign_dim_array:
}
}
if (EXPECTED(Z_TYPE_P(object_ptr) == IS_OBJECT)) {
+ dim = EX_CONSTANT(opline->op2);
+ value = _get_zval_ptr_var((opline+1)->op1.var, execute_data, &free_op_data);
- zval *property_name = EX_CONSTANT(opline->op2);
+ zend_assign_to_object_dim(object_ptr, dim, value);
- 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);
+ if (UNEXPECTED(RETURN_VALUE_USED(opline)) && EXPECTED(!EG(exception))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+ zval_ptr_dtor_nogc(free_op_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_throw_error(NULL, "[] operator not supported for strings");
- FREE_UNFETCHED_OP((opline+1)->op1_type, (opline+1)->op1.var);
+ if (IS_CONST == IS_UNUSED) {
+ zend_throw_error(NULL, "[] operator not supported for strings");
+ zval_ptr_dtor_nogc(EX_VAR((opline+1)->op1.var));
- HANDLE_EXCEPTION();
- } else {
- zend_long offset;
+ HANDLE_EXCEPTION();
+ } else {
+ dim = EX_CONSTANT(opline->op2);
+ value = _get_zval_ptr_var_deref((opline+1)->op1.var, execute_data, &free_op_data);
+ zend_assign_to_string_offset(object_ptr, dim, value, (UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL));
+ zval_ptr_dtor_nogc(free_op_data);
+ }
+ } else if (EXPECTED(Z_TYPE_P(object_ptr) <= IS_FALSE)) {
+ 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 (IS_CV != IS_VAR || EXPECTED(!Z_ISERROR_P(object_ptr))) {
+ zend_error(E_WARNING, "Cannot use a scalar value as an array");
+ }
+ dim = EX_CONSTANT(opline->op2);
+assign_dim_error:
+ zval_ptr_dtor_nogc(EX_VAR((opline+1)->op1.var));
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+ }
+ }
+ if (IS_CONST != IS_UNUSED) {
- dim = EX_CONSTANT(opline->op2);
- offset = zend_fetch_string_offset(object_ptr, dim, BP_VAR_W);
+ }
- value = get_zval_ptr_r_deref((opline+1)->op1_type, (opline+1)->op1, execute_data, &free_op_data1);
- 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);
- }
+ /* assign_dim has two opcodes! */
+ ZEND_VM_NEXT_OPCODE_EX(1, 2);
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_CV_CONST_OP_DATA_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+
+ zval *object_ptr;
+
+ zval *value;
+ zval *variable_ptr;
+ zval *dim;
+
+ SAVE_OPLINE();
+ object_ptr = _get_zval_ptr_cv_undef_BP_VAR_W(execute_data, opline->op1.var);
+
+ if (EXPECTED(Z_TYPE_P(object_ptr) == IS_ARRAY)) {
+try_assign_dim_array:
+ SEPARATE_ARRAY(object_ptr);
+ if (IS_CONST == IS_UNUSED) {
+ variable_ptr = zend_hash_next_index_insert(Z_ARRVAL_P(object_ptr), &EG(uninitialized_zval));
+ if (UNEXPECTED(variable_ptr == NULL)) {
+ zend_error(E_WARNING, "Cannot add element to the array as the next element is already occupied");
+ goto assign_dim_error;
+ }
+ } else {
+ dim = EX_CONSTANT(opline->op2);
+ if (IS_CONST == IS_CONST) {
+ variable_ptr = zend_fetch_dimension_address_inner_W_CONST(Z_ARRVAL_P(object_ptr), dim);
} 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);
+ variable_ptr = zend_fetch_dimension_address_inner_W(Z_ARRVAL_P(object_ptr), dim);
+ }
+ if (UNEXPECTED(variable_ptr == NULL)) {
+ goto assign_dim_error;
+ }
+ }
+ value = _get_zval_ptr_cv_BP_VAR_R(execute_data, (opline+1)->op1.var);
+ value = zend_assign_to_variable(variable_ptr, value, IS_CV);
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+ } 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;
}
- } 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;
+ }
+ if (EXPECTED(Z_TYPE_P(object_ptr) == IS_OBJECT)) {
+ dim = EX_CONSTANT(opline->op2);
+ value = _get_zval_ptr_cv_BP_VAR_R(execute_data, (opline+1)->op1.var);
+
+ zend_assign_to_object_dim(object_ptr, dim, value);
+
+ if (UNEXPECTED(RETURN_VALUE_USED(opline)) && EXPECTED(!EG(exception))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
}
- goto assign_dim_convert_to_array;
+
+ } else if (EXPECTED(Z_TYPE_P(object_ptr) == IS_STRING)) {
+ if (IS_CONST == IS_UNUSED) {
+ zend_throw_error(NULL, "[] operator not supported for strings");
+
+
+ HANDLE_EXCEPTION();
+ } else {
+ dim = EX_CONSTANT(opline->op2);
+ value = _get_zval_ptr_cv_deref_BP_VAR_R(execute_data, (opline+1)->op1.var);
+ zend_assign_to_string_offset(object_ptr, dim, value, (UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL));
+
+ }
+ } else if (EXPECTED(Z_TYPE_P(object_ptr) <= IS_FALSE)) {
+ 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 {
- zend_error(E_WARNING, "Cannot use a scalar value as an array");
-assign_dim_clean:
+ if (IS_CV != IS_VAR || EXPECTED(!Z_ISERROR_P(object_ptr))) {
+ zend_error(E_WARNING, "Cannot use a scalar value as an array");
+ }
dim = EX_CONSTANT(opline->op2);
+assign_dim_error:
- value = get_zval_ptr_r((opline+1)->op1_type, (opline+1)->op1, execute_data, &free_op_data1);
- FREE_OP(free_op_data1);
if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
ZVAL_NULL(EX_VAR(opline->result.var));
}
}
}
+ if (IS_CONST != IS_UNUSED) {
+
+ }
/* assign_dim has two opcodes! */
ZEND_VM_NEXT_OPCODE_EX(1, 2);
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_SPEC_CV_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_SPEC_CV_CONST_RETVAL_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
@@ -31947,14 +39053,42 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_SPEC_CV_CONST_HANDLER(Z
value = EX_CONSTANT(opline->op2);
variable_ptr = _get_zval_ptr_cv_undef_BP_VAR_W(execute_data, opline->op1.var);
- if (IS_CV == IS_VAR && UNEXPECTED(variable_ptr == &EG(error_zval))) {
+ if (IS_CV == IS_VAR && UNEXPECTED(Z_ISERROR_P(variable_ptr))) {
- if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ if (UNEXPECTED(0)) {
ZVAL_NULL(EX_VAR(opline->result.var));
}
} else {
value = zend_assign_to_variable(variable_ptr, value, IS_CONST);
- if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ if (UNEXPECTED(0)) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+
+ /* zend_assign_to_variable() always takes care of op2, never free it! */
+ }
+
+ ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_SPEC_CV_CONST_RETVAL_USED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+
+ zval *value;
+ zval *variable_ptr;
+
+ SAVE_OPLINE();
+ value = EX_CONSTANT(opline->op2);
+ variable_ptr = _get_zval_ptr_cv_undef_BP_VAR_W(execute_data, opline->op1.var);
+
+ if (IS_CV == IS_VAR && UNEXPECTED(Z_ISERROR_P(variable_ptr))) {
+
+ if (UNEXPECTED(1)) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+ } else {
+ value = zend_assign_to_variable(variable_ptr, value, IS_CONST);
+ if (UNEXPECTED(1)) {
ZVAL_COPY(EX_VAR(opline->result.var), value);
}
@@ -32070,7 +39204,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_SPEC_CV_CONST
object = _get_zval_ptr_cv_undef(execute_data, opline->op1.var);
- if (IS_CV == IS_UNUSED && UNEXPECTED(Z_OBJ_P(object) == NULL)) {
+ if (IS_CV == IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
HANDLE_EXCEPTION();
@@ -32130,6 +39264,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_SPEC_CV_CONST
EXPECTED(obj == orig_obj)) {
CACHE_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(function_name), called_scope, fbc);
}
+ if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!fbc->op_array.run_time_cache)) {
+ init_func_run_time_cache(&fbc->op_array);
+ }
}
call_info = ZEND_CALL_NESTED_FUNCTION;
@@ -32234,11 +39371,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ADD_ARRAY_ELEMENT_SPEC_CV_CONS
if ((IS_CV == IS_VAR || IS_CV == IS_CV) &&
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_throw_error(NULL, "Cannot create references to/from string offsets");
- zend_array_destroy(Z_ARRVAL_P(EX_VAR(opline->result.var)));
- HANDLE_EXCEPTION();
- }
ZVAL_MAKE_REF(expr_ptr);
Z_ADDREF_P(expr_ptr);
@@ -32247,10 +39379,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ADD_ARRAY_ELEMENT_SPEC_CV_CONS
if (IS_CV == IS_TMP_VAR) {
/* pass */
} else if (IS_CV == IS_CONST) {
- 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;
+ if (Z_REFCOUNTED_P(expr_ptr)) {
+ Z_ADDREF_P(expr_ptr);
}
} else if (IS_CV == IS_CV) {
ZVAL_DEREF(expr_ptr);
@@ -32354,40 +39484,14 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_ARRAY_SPEC_CV_CONST_HANDL
}
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_UNSET_VAR_SPEC_CV_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_UNSET_STATIC_PROP_SPEC_CV_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
zval tmp, *varname;
- HashTable *target_symbol_table;
+ zend_class_entry *ce;
SAVE_OPLINE();
- if (IS_CV == IS_CV &&
- IS_CONST == IS_UNUSED &&
- (opline->extended_value & ZEND_QUICK_SET)) {
- zval *var = EX_VAR(opline->op1.var);
-
- if (Z_REFCOUNTED_P(var)) {
- zend_refcounted *garbage = Z_COUNTED_P(var);
-
- if (!--GC_REFCOUNT(garbage)) {
- ZVAL_UNDEF(var);
- zval_dtor_func_for_ptr(garbage);
- } else {
- zval *z = var;
- ZVAL_DEREF(z);
- if (Z_COLLECTABLE_P(z) && UNEXPECTED(!Z_GC_INFO_P(z))) {
- ZVAL_UNDEF(var);
- gc_possible_root(Z_COUNTED_P(z));
- } else {
- ZVAL_UNDEF(var);
- }
- }
- } else {
- ZVAL_UNDEF(var);
- }
- ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
- }
varname = _get_zval_ptr_cv_undef(execute_data, opline->op1.var);
@@ -32400,33 +39504,36 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_UNSET_VAR_SPEC_CV_CONST_HANDLE
varname = &tmp;
}
- if (IS_CONST != IS_UNUSED) {
- zend_class_entry *ce;
-
- if (IS_CONST == IS_CONST) {
- ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)));
+ if (IS_CONST == IS_CONST) {
+ ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)));
+ if (UNEXPECTED(ce == NULL)) {
+ ce = zend_fetch_class_by_name(Z_STR_P(EX_CONSTANT(opline->op2)), EX_CONSTANT(opline->op2) + 1, ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION);
if (UNEXPECTED(ce == NULL)) {
- ce = zend_fetch_class_by_name(Z_STR_P(EX_CONSTANT(opline->op2)), EX_CONSTANT(opline->op2) + 1, ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION);
- if (UNEXPECTED(ce == NULL)) {
- if (EXPECTED(!EG(exception))) {
- zend_throw_error(NULL, "Class '%s' not found", Z_STRVAL_P(EX_CONSTANT(opline->op2)));
- }
- if (IS_CV != IS_CONST && Z_TYPE(tmp) != IS_UNDEF) {
- zend_string_release(Z_STR(tmp));
- }
-
- HANDLE_EXCEPTION();
+ if (EXPECTED(!EG(exception))) {
+ zend_throw_error(NULL, "Class '%s' not found", Z_STRVAL_P(EX_CONSTANT(opline->op2)));
}
- CACHE_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)), ce);
+ if (IS_CV != IS_CONST && Z_TYPE(tmp) != IS_UNDEF) {
+ zend_string_release(Z_STR(tmp));
+ }
+
+ HANDLE_EXCEPTION();
}
- } else {
- ce = Z_CE_P(EX_VAR(opline->op2.var));
+ CACHE_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)), ce);
+ }
+ } else if (IS_CONST == IS_UNUSED) {
+ ce = zend_fetch_class(NULL, opline->op2.num);
+ if (UNEXPECTED(ce == NULL)) {
+ ZEND_ASSERT(EG(exception));
+ if (IS_CV != IS_CONST && Z_TYPE(tmp) != IS_UNDEF) {
+ zend_string_release(Z_STR(tmp));
+ }
+
+ HANDLE_EXCEPTION();
}
- zend_std_unset_static_property(ce, Z_STR_P(varname));
} else {
- target_symbol_table = zend_get_target_symbol_table(execute_data, opline->extended_value & ZEND_FETCH_TYPE_MASK);
- zend_hash_del_ind(target_symbol_table, Z_STR_P(varname));
+ ce = Z_CE_P(EX_VAR(opline->op2.var));
}
+ zend_std_unset_static_property(ce, Z_STR_P(varname));
if (IS_CV != IS_CONST && Z_TYPE(tmp) != IS_UNDEF) {
zend_string_release(Z_STR(tmp));
@@ -32445,21 +39552,11 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_UNSET_DIM_SPEC_CV_CONST_HANDLE
zend_string *key;
SAVE_OPLINE();
- container = _get_zval_ptr_cv_BP_VAR_UNSET(execute_data, opline->op1.var);
- if (IS_CV == IS_UNUSED && UNEXPECTED(Z_OBJ_P(container) == NULL)) {
- zend_throw_error(NULL, "Using $this when not in object context");
-
- HANDLE_EXCEPTION();
- }
- if (IS_CV == IS_VAR && UNEXPECTED(container == NULL)) {
- zend_throw_error(NULL, "Cannot unset string offsets");
-
- HANDLE_EXCEPTION();
- }
+ container = _get_zval_ptr_cv_undef_BP_VAR_UNSET(execute_data, opline->op1.var);
offset = EX_CONSTANT(opline->op2);
do {
- if (IS_CV != IS_UNUSED && EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) {
+ if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) {
HashTable *ht;
unset_dim_array:
@@ -32509,16 +39606,19 @@ num_index_dim:
zend_error(E_WARNING, "Illegal offset type in unset");
}
break;
- } else if (IS_CV != IS_UNUSED && Z_ISREF_P(container)) {
+ } else if (Z_ISREF_P(container)) {
container = Z_REFVAL_P(container);
if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) {
goto unset_dim_array;
}
}
+ if (IS_CV == IS_CV && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) {
+ container = GET_OP1_UNDEF_CV(container, BP_VAR_R);
+ }
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 || EXPECTED(Z_TYPE_P(container) == IS_OBJECT)) {
+ if (EXPECTED(Z_TYPE_P(container) == IS_OBJECT)) {
if (UNEXPECTED(Z_OBJ_HT_P(container)->unset_dimension == NULL)) {
zend_throw_error(NULL, "Cannot use object as array");
} else {
@@ -32542,16 +39642,11 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_UNSET_OBJ_SPEC_CV_CONST_HANDLE
SAVE_OPLINE();
container = _get_zval_ptr_cv_BP_VAR_UNSET(execute_data, opline->op1.var);
- if (IS_CV == IS_UNUSED && UNEXPECTED(Z_OBJ_P(container) == NULL)) {
+ if (IS_CV == IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
HANDLE_EXCEPTION();
}
- if (IS_CV == IS_VAR && UNEXPECTED(container == NULL)) {
- zend_throw_error(NULL, "Cannot unset string offsets");
-
- HANDLE_EXCEPTION();
- }
offset = EX_CONSTANT(opline->op2);
do {
@@ -32576,103 +39671,87 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_UNSET_OBJ_SPEC_CV_CONST_HANDLE
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ISSET_ISEMPTY_VAR_SPEC_CV_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ISSET_ISEMPTY_STATIC_PROP_SPEC_CV_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
zval *value;
int result;
- if (IS_CV == IS_CV &&
- IS_CONST == IS_UNUSED &&
- (opline->extended_value & ZEND_QUICK_SET)) {
- value = EX_VAR(opline->op1.var);
- if (opline->extended_value & ZEND_ISSET) {
- result =
- Z_TYPE_P(value) > IS_NULL &&
- (!Z_ISREF_P(value) || Z_TYPE_P(Z_REFVAL_P(value)) != IS_NULL);
- } else /* if (opline->extended_value & ZEND_ISEMPTY) */ {
- SAVE_OPLINE();
- result = !i_zend_is_true(value);
- if (UNEXPECTED(EG(exception))) {
- HANDLE_EXCEPTION();
- }
- }
- ZEND_VM_SMART_BRANCH(result, 0);
- ZVAL_BOOL(EX_VAR(opline->result.var), result);
- ZEND_VM_SET_NEXT_OPCODE(opline + 1);
- ZEND_VM_CONTINUE();
- } else {
-
- zval tmp, *varname;
-
- SAVE_OPLINE();
- varname = _get_zval_ptr_cv_BP_VAR_IS(execute_data, opline->op1.var);
- ZVAL_UNDEF(&tmp);
- if (IS_CV != IS_CONST && Z_TYPE_P(varname) != IS_STRING) {
- ZVAL_STR(&tmp, zval_get_string(varname));
- varname = &tmp;
- }
-
- if (IS_CONST != IS_UNUSED) {
- zend_class_entry *ce;
-
- if (IS_CONST == IS_CONST) {
- if (IS_CV == IS_CONST && EXPECTED((ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)))) != NULL)) {
- value = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)) + sizeof(void*));
-
- /* check if static properties were destoyed */
- if (UNEXPECTED(CE_STATIC_MEMBERS(ce) == NULL)) {
- value = NULL;
- }
+ zval tmp, *varname;
+ zend_class_entry *ce;
- goto is_var_return;
- } else if (UNEXPECTED((ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)))) == NULL)) {
- ce = zend_fetch_class_by_name(Z_STR_P(EX_CONSTANT(opline->op2)), EX_CONSTANT(opline->op2) + 1, ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION);
- if (UNEXPECTED(ce == NULL)) {
- ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
- }
- CACHE_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)), ce);
- }
- } else {
- ce = Z_CE_P(EX_VAR(opline->op2.var));
- if (IS_CV == IS_CONST &&
- (value = CACHED_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)), ce)) != NULL) {
+ SAVE_OPLINE();
+ varname = _get_zval_ptr_cv_BP_VAR_IS(execute_data, opline->op1.var);
+ ZVAL_UNDEF(&tmp);
+ if (IS_CV != IS_CONST && Z_TYPE_P(varname) != IS_STRING) {
+ ZVAL_STR(&tmp, zval_get_string(varname));
+ varname = &tmp;
+ }
- /* check if static properties were destoyed */
- if (UNEXPECTED(CE_STATIC_MEMBERS(ce) == NULL)) {
- value = NULL;
- }
+ if (IS_CONST == IS_CONST) {
+ if (IS_CV == IS_CONST && EXPECTED((ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)))) != NULL)) {
+ value = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)) + sizeof(void*));
- goto is_var_return;
- }
+ /* check if static properties were destoyed */
+ if (UNEXPECTED(CE_STATIC_MEMBERS(ce) == NULL)) {
+ value = NULL;
}
- value = zend_std_get_static_property(ce, Z_STR_P(varname), 1);
+ goto is_static_prop_return;
+ } else if (UNEXPECTED((ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)))) == NULL)) {
+ ce = zend_fetch_class_by_name(Z_STR_P(EX_CONSTANT(opline->op2)), EX_CONSTANT(opline->op2) + 1, ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION);
+ if (UNEXPECTED(ce == NULL)) {
+ ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
+ }
+ CACHE_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)), ce);
+ }
+ } else {
+ if (IS_CONST == IS_UNUSED) {
+ ce = zend_fetch_class(NULL, opline->op2.num);
+ if (UNEXPECTED(ce == NULL)) {
+ ZEND_ASSERT(EG(exception));
+ if (IS_CV != IS_CONST && Z_TYPE(tmp) != IS_UNDEF) {
+ zend_string_release(Z_STR(tmp));
+ }
- if (IS_CV == IS_CONST && value) {
- CACHE_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)), ce, value);
+ HANDLE_EXCEPTION();
}
} else {
- HashTable *target_symbol_table = zend_get_target_symbol_table(execute_data, opline->extended_value & ZEND_FETCH_TYPE_MASK);
- value = zend_hash_find_ind(target_symbol_table, Z_STR_P(varname));
+ ce = Z_CE_P(EX_VAR(opline->op2.var));
}
+ if (IS_CV == IS_CONST &&
+ (value = CACHED_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)), ce)) != NULL) {
- if (IS_CV != IS_CONST && Z_TYPE(tmp) != IS_UNDEF) {
- zend_string_release(Z_STR(tmp));
- }
+ /* check if static properties were destoyed */
+ if (UNEXPECTED(CE_STATIC_MEMBERS(ce) == NULL)) {
+ value = NULL;
+ }
-is_var_return:
- if (opline->extended_value & ZEND_ISSET) {
- result = value && Z_TYPE_P(value) > IS_NULL &&
- (!Z_ISREF_P(value) || Z_TYPE_P(Z_REFVAL_P(value)) != IS_NULL);
- } else /* if (opline->extended_value & ZEND_ISEMPTY) */ {
- result = !value || !i_zend_is_true(value);
+ goto is_static_prop_return;
}
+ }
- ZEND_VM_SMART_BRANCH(result, 1);
- ZVAL_BOOL(EX_VAR(opline->result.var), result);
- ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
+ value = zend_std_get_static_property(ce, Z_STR_P(varname), 1);
+
+ if (IS_CV == IS_CONST && value) {
+ CACHE_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)), ce, value);
}
+
+ if (IS_CV != IS_CONST && Z_TYPE(tmp) != IS_UNDEF) {
+ zend_string_release(Z_STR(tmp));
+ }
+
+is_static_prop_return:
+ if (opline->extended_value & ZEND_ISSET) {
+ result = value && Z_TYPE_P(value) > IS_NULL &&
+ (!Z_ISREF_P(value) || Z_TYPE_P(Z_REFVAL_P(value)) != IS_NULL);
+ } else /* if (opline->extended_value & ZEND_ISEMPTY) */ {
+ result = !value || !i_zend_is_true(value);
+ }
+
+ ZEND_VM_SMART_BRANCH(result, 1);
+ ZVAL_BOOL(EX_VAR(opline->result.var), result);
+ ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ISSET_ISEMPTY_DIM_OBJ_SPEC_CV_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@@ -32685,17 +39764,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ISSET_ISEMPTY_DIM_OBJ_SPEC_CV_
zval *offset;
SAVE_OPLINE();
- container = _get_zval_ptr_cv_BP_VAR_IS(execute_data, opline->op1.var);
-
- if (IS_CV == IS_UNUSED && UNEXPECTED(Z_OBJ_P(container) == NULL)) {
- zend_throw_error(NULL, "Using $this when not in object context");
-
- HANDLE_EXCEPTION();
- }
-
+ container = _get_zval_ptr_cv_undef(execute_data, opline->op1.var);
offset = EX_CONSTANT(opline->op2);
- if (IS_CV != IS_UNUSED && EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) {
+ if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) {
HashTable *ht;
zval *value;
zend_string *str;
@@ -32762,8 +39834,7 @@ num_index_prop:
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 ((IS_CV != IS_CONST && EXPECTED(Z_TYPE_P(container) == IS_OBJECT))) {
if (EXPECTED(Z_OBJ_HT_P(container)->has_dimension)) {
result =
((opline->extended_value & ZEND_ISSET) == 0) ^
@@ -32778,6 +39849,9 @@ num_index_prop:
if (EXPECTED(Z_TYPE_P(offset) == IS_LONG)) {
lval = Z_LVAL_P(offset);
isset_str_offset:
+ if (UNEXPECTED(lval < 0)) { /* Handle negative offset */
+ lval += (zend_long)Z_STRLEN_P(container);
+ }
if (EXPECTED(lval >= 0) && (size_t)lval < Z_STRLEN_P(container)) {
if (opline->extended_value & ZEND_ISSET) {
result = 1;
@@ -32823,7 +39897,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ISSET_ISEMPTY_PROP_OBJ_SPEC_CV
SAVE_OPLINE();
container = _get_zval_ptr_cv_BP_VAR_IS(execute_data, opline->op1.var);
- if (IS_CV == IS_UNUSED && UNEXPECTED(Z_OBJ_P(container) == NULL)) {
+ if (IS_CV == IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
HANDLE_EXCEPTION();
@@ -32883,6 +39957,13 @@ try_instanceof:
}
CACHE_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)), ce);
}
+ } else if (IS_CONST == IS_UNUSED) {
+ ce = zend_fetch_class(NULL, opline->op2.num);
+ if (UNEXPECTED(ce == NULL)) {
+ ZEND_ASSERT(EG(exception));
+
+ HANDLE_EXCEPTION();
+ }
} else {
ce = Z_CE_P(EX_VAR(opline->op2.var));
}
@@ -32929,7 +40010,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_CV_CONST_HANDLER(ZE
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) {
+ if (IS_CV & (IS_CONST|IS_TMP_VAR)) {
zval *value;
zend_error(E_NOTICE, "Only variable references should be yielded by reference");
@@ -32937,25 +40018,19 @@ 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 (IS_CV == IS_CONST) {
- if (UNEXPECTED(Z_OPT_COPYABLE(generator->value))) {
- zval_copy_ctor_func(&generator->value);
+ if (UNEXPECTED(Z_OPT_REFCOUNTED(generator->value))) {
+ Z_ADDREF(generator->value);
}
}
} else {
zval *value_ptr = _get_zval_ptr_cv_BP_VAR_W(execute_data, opline->op1.var);
- if (IS_CV == IS_VAR && UNEXPECTED(value_ptr == NULL)) {
- zend_throw_error(NULL, "Cannot yield string offsets by reference");
-
- HANDLE_EXCEPTION();
- }
-
/* If a function call result is yielded and the function did
* not return by reference we throw a notice. */
if (IS_CV == IS_VAR &&
(value_ptr == &EG(uninitialized_zval) ||
(opline->extended_value == ZEND_RETURNS_FUNCTION &&
- !(Z_VAR_FLAGS_P(value_ptr) & IS_VAR_RET_REF)))) {
+ !Z_ISREF_P(value_ptr)))) {
zend_error(E_NOTICE, "Only variable references should be yielded by reference");
} else {
ZVAL_MAKE_REF(value_ptr);
@@ -32969,8 +40044,8 @@ 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_COPY_VALUE(&generator->value, value);
- if (UNEXPECTED(Z_OPT_COPYABLE(generator->value))) {
- zval_copy_ctor_func(&generator->value);
+ if (UNEXPECTED(Z_OPT_REFCOUNTED(generator->value))) {
+ Z_ADDREF(generator->value);
}
} else if (IS_CV == IS_TMP_VAR) {
ZVAL_COPY_VALUE(&generator->value, value);
@@ -32997,8 +40072,8 @@ 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_COPY_VALUE(&generator->key, key);
- if (UNEXPECTED(Z_OPT_COPYABLE(generator->key))) {
- zval_copy_ctor_func(&generator->key);
+ if (UNEXPECTED(Z_OPT_REFCOUNTED(generator->key))) {
+ Z_ADDREF(generator->key);
}
} else if (IS_CONST == IS_TMP_VAR) {
ZVAL_COPY_VALUE(&generator->key, key);
@@ -33051,6 +40126,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_BIND_GLOBAL_SPEC_CV_CONST_HAND
zval *value;
zval *variable_ptr;
uint32_t idx;
+ zend_reference *ref;
ZEND_VM_REPEATABLE_OPCODE
@@ -33093,32 +40169,27 @@ check_indirect:
}
}
- do {
- zend_reference *ref;
-
- if (UNEXPECTED(!Z_ISREF_P(value))) {
- ref = (zend_reference*)emalloc(sizeof(zend_reference));
- GC_REFCOUNT(ref) = 2;
- GC_TYPE_INFO(ref) = IS_REFERENCE;
- ZVAL_COPY_VALUE(&ref->val, value);
- Z_REF_P(value) = ref;
- Z_TYPE_INFO_P(value) = IS_REFERENCE_EX;
- } else {
- ref = Z_REF_P(value);
- GC_REFCOUNT(ref)++;
- }
+ if (UNEXPECTED(!Z_ISREF_P(value))) {
+ ref = (zend_reference*)emalloc(sizeof(zend_reference));
+ GC_REFCOUNT(ref) = 2;
+ GC_TYPE_INFO(ref) = IS_REFERENCE;
+ ZVAL_COPY_VALUE(&ref->val, value);
+ Z_REF_P(value) = ref;
+ Z_TYPE_INFO_P(value) = IS_REFERENCE_EX;
+ } else {
+ ref = Z_REF_P(value);
+ GC_REFCOUNT(ref)++;
+ }
- variable_ptr = _get_zval_ptr_cv_undef_BP_VAR_W(execute_data, opline->op1.var);
+ variable_ptr = _get_zval_ptr_cv_undef_BP_VAR_W(execute_data, opline->op1.var);
- if (UNEXPECTED(Z_REFCOUNTED_P(variable_ptr))) {
- uint32_t refcnt = Z_DELREF_P(variable_ptr);
+ if (UNEXPECTED(Z_REFCOUNTED_P(variable_ptr))) {
+ uint32_t refcnt = Z_DELREF_P(variable_ptr);
- if (UNEXPECTED(variable_ptr == value)) {
- break;
- }
+ if (EXPECTED(variable_ptr != value)) {
if (refcnt == 0) {
SAVE_OPLINE();
- zval_dtor_func_for_ptr(Z_COUNTED_P(variable_ptr));
+ zval_dtor_func(Z_COUNTED_P(variable_ptr));
if (UNEXPECTED(EG(exception))) {
ZVAL_NULL(variable_ptr);
HANDLE_EXCEPTION();
@@ -33127,13 +40198,112 @@ check_indirect:
GC_ZVAL_CHECK_POSSIBLE_ROOT(variable_ptr);
}
}
- ZVAL_REF(variable_ptr, ref);
- } while (0);
+ }
+ ZVAL_REF(variable_ptr, ref);
ZEND_VM_REPEAT_OPCODE(ZEND_BIND_GLOBAL);
ZEND_VM_NEXT_OPCODE();
}
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_BIND_STATIC_SPEC_CV_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+
+ HashTable *ht;
+ zval *varname;
+ zval *value;
+ zval *variable_ptr;
+
+ SAVE_OPLINE();
+ variable_ptr = _get_zval_ptr_cv_undef_BP_VAR_W(execute_data, opline->op1.var);
+ zval_ptr_dtor(variable_ptr);
+
+ ht = EX(func)->op_array.static_variables;
+ ZEND_ASSERT(ht != NULL);
+ if (GC_REFCOUNT(ht) > 1) {
+ if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE)) {
+ GC_REFCOUNT(ht)--;
+ }
+ EX(func)->op_array.static_variables = ht = zend_array_dup(ht);
+ }
+
+ varname = EX_CONSTANT(opline->op2);
+ value = zend_hash_find(ht, Z_STR_P(varname));
+
+ if (opline->extended_value) {
+ if (Z_CONSTANT_P(value)) {
+ if (UNEXPECTED(zval_update_constant_ex(value, EX(func)->op_array.scope) != SUCCESS)) {
+ ZVAL_NULL(variable_ptr);
+ HANDLE_EXCEPTION();
+ }
+ }
+ if (UNEXPECTED(!Z_ISREF_P(value))) {
+ zend_reference *ref = (zend_reference*)emalloc(sizeof(zend_reference));
+ GC_REFCOUNT(ref) = 2;
+ GC_TYPE_INFO(ref) = IS_REFERENCE;
+ ZVAL_COPY_VALUE(&ref->val, value);
+ Z_REF_P(value) = ref;
+ Z_TYPE_INFO_P(value) = IS_REFERENCE_EX;
+ ZVAL_REF(variable_ptr, ref);
+ } else {
+ Z_ADDREF_P(value);
+ ZVAL_REF(variable_ptr, Z_REF_P(value));
+ }
+ } else {
+ ZVAL_COPY(variable_ptr, value);
+ }
+
+ ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_DIM_R_INDEX_SPEC_CV_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+
+ zval *container, *dim, *value;
+ zend_long offset;
+
+ container = _get_zval_ptr_cv_undef(execute_data, opline->op1.var);
+ dim = EX_CONSTANT(opline->op2);
+ if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) {
+fetch_dim_r_index_array:
+ if (EXPECTED(Z_TYPE_P(dim) == IS_LONG)) {
+ offset = Z_LVAL_P(dim);
+ } else {
+ offset = zval_get_long(dim);
+ }
+ ZEND_HASH_INDEX_FIND(Z_ARRVAL_P(container), offset, value, fetch_dim_r_index_undef);
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ if (IS_CV & (IS_TMP_VAR|IS_VAR)) {
+ SAVE_OPLINE();
+
+ ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
+ } else {
+ ZEND_VM_NEXT_OPCODE();
+ }
+ } else if (IS_CV != IS_CONST && EXPECTED(Z_TYPE_P(container) == IS_REFERENCE)) {
+ container = Z_REFVAL_P(container);
+ if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) {
+ goto fetch_dim_r_index_array;
+ } else {
+ goto fetch_dim_r_index_slow;
+ }
+ } else {
+fetch_dim_r_index_slow:
+ SAVE_OPLINE();
+ zend_fetch_dimension_address_read_R_slow(EX_VAR(opline->result.var), container, dim);
+
+ ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
+ }
+
+fetch_dim_r_index_undef:
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ SAVE_OPLINE();
+ zend_error(E_NOTICE, "Undefined offset: " ZEND_LONG_FMT, offset);
+
+ ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
+}
+
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_IDENTICAL_SPEC_CV_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
@@ -33170,7 +40340,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_NOT_IDENTICAL_SPEC_CV_TMP_H
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_SPEC_CV_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_SPEC_CV_TMP_RETVAL_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
zend_free_op free_op2;
@@ -33181,14 +40351,42 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_SPEC_CV_TMP_HANDLER(ZEN
value = _get_zval_ptr_tmp(opline->op2.var, execute_data, &free_op2);
variable_ptr = _get_zval_ptr_cv_undef_BP_VAR_W(execute_data, opline->op1.var);
- if (IS_CV == IS_VAR && UNEXPECTED(variable_ptr == &EG(error_zval))) {
+ if (IS_CV == IS_VAR && UNEXPECTED(Z_ISERROR_P(variable_ptr))) {
zval_ptr_dtor_nogc(free_op2);
- if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ if (UNEXPECTED(0)) {
ZVAL_NULL(EX_VAR(opline->result.var));
}
} else {
value = zend_assign_to_variable(variable_ptr, value, IS_TMP_VAR);
- if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ if (UNEXPECTED(0)) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+
+ /* zend_assign_to_variable() always takes care of op2, never free it! */
+ }
+
+ ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_SPEC_CV_TMP_RETVAL_USED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zend_free_op free_op2;
+ zval *value;
+ zval *variable_ptr;
+
+ SAVE_OPLINE();
+ value = _get_zval_ptr_tmp(opline->op2.var, execute_data, &free_op2);
+ variable_ptr = _get_zval_ptr_cv_undef_BP_VAR_W(execute_data, opline->op1.var);
+
+ if (IS_CV == IS_VAR && UNEXPECTED(Z_ISERROR_P(variable_ptr))) {
+ zval_ptr_dtor_nogc(free_op2);
+ if (UNEXPECTED(1)) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+ } else {
+ value = zend_assign_to_variable(variable_ptr, value, IS_TMP_VAR);
+ if (UNEXPECTED(1)) {
ZVAL_COPY(EX_VAR(opline->result.var), value);
}
@@ -33225,7 +40423,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_CV_TMP_HANDLER(ZEND
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) {
+ if (IS_CV & (IS_CONST|IS_TMP_VAR)) {
zval *value;
zend_error(E_NOTICE, "Only variable references should be yielded by reference");
@@ -33233,25 +40431,19 @@ 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 (IS_CV == IS_CONST) {
- if (UNEXPECTED(Z_OPT_COPYABLE(generator->value))) {
- zval_copy_ctor_func(&generator->value);
+ if (UNEXPECTED(Z_OPT_REFCOUNTED(generator->value))) {
+ Z_ADDREF(generator->value);
}
}
} else {
zval *value_ptr = _get_zval_ptr_cv_BP_VAR_W(execute_data, opline->op1.var);
- if (IS_CV == IS_VAR && UNEXPECTED(value_ptr == NULL)) {
- zend_throw_error(NULL, "Cannot yield string offsets by reference");
- zval_ptr_dtor_nogc(EX_VAR(opline->op2.var));
- HANDLE_EXCEPTION();
- }
-
/* If a function call result is yielded and the function did
* not return by reference we throw a notice. */
if (IS_CV == IS_VAR &&
(value_ptr == &EG(uninitialized_zval) ||
(opline->extended_value == ZEND_RETURNS_FUNCTION &&
- !(Z_VAR_FLAGS_P(value_ptr) & IS_VAR_RET_REF)))) {
+ !Z_ISREF_P(value_ptr)))) {
zend_error(E_NOTICE, "Only variable references should be yielded by reference");
} else {
ZVAL_MAKE_REF(value_ptr);
@@ -33265,8 +40457,8 @@ 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_COPY_VALUE(&generator->value, value);
- if (UNEXPECTED(Z_OPT_COPYABLE(generator->value))) {
- zval_copy_ctor_func(&generator->value);
+ if (UNEXPECTED(Z_OPT_REFCOUNTED(generator->value))) {
+ Z_ADDREF(generator->value);
}
} else if (IS_CV == IS_TMP_VAR) {
ZVAL_COPY_VALUE(&generator->value, value);
@@ -33293,8 +40485,8 @@ 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_COPY_VALUE(&generator->key, key);
- if (UNEXPECTED(Z_OPT_COPYABLE(generator->key))) {
- zval_copy_ctor_func(&generator->key);
+ if (UNEXPECTED(Z_OPT_REFCOUNTED(generator->key))) {
+ Z_ADDREF(generator->key);
}
} else if (IS_TMP_VAR == IS_TMP_VAR) {
ZVAL_COPY_VALUE(&generator->key, key);
@@ -33375,14 +40567,14 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_NOT_IDENTICAL_SPEC_CV_VAR_H
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_fetch_var_address_helper_SPEC_CV_VAR(int type ZEND_OPCODE_HANDLER_ARGS_DC)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_fetch_static_prop_helper_SPEC_CV_VAR(int type ZEND_OPCODE_HANDLER_ARGS_DC)
{
USE_OPLINE
zval *varname;
zval *retval;
zend_string *name;
- HashTable *target_symbol_table;
+ zend_class_entry *ce;
SAVE_OPLINE();
varname = _get_zval_ptr_cv_undef(execute_data, opline->op1.var);
@@ -33399,119 +40591,73 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_fetch_var_address_helper_SPEC_
name = zval_get_string(varname);
}
- if (IS_VAR != IS_UNUSED) {
- zend_class_entry *ce;
-
- if (IS_VAR == IS_CONST) {
- if (IS_CV == IS_CONST && EXPECTED((ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)))) != NULL)) {
- retval = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)) + sizeof(void*));
-
- /* check if static properties were destoyed */
- if (UNEXPECTED(CE_STATIC_MEMBERS(ce) == NULL)) {
- zend_throw_error(NULL, "Access to undeclared static property: %s::$%s", ZSTR_VAL(ce->name), ZSTR_VAL(name));
-
- HANDLE_EXCEPTION();
- }
+ if (IS_VAR == IS_CONST) {
+ if (IS_CV == IS_CONST && EXPECTED((ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)))) != NULL)) {
+ retval = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)) + sizeof(void*));
- goto fetch_var_return;
- } else if (UNEXPECTED((ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)))) == NULL)) {
- ce = zend_fetch_class_by_name(Z_STR_P(EX_CONSTANT(opline->op2)), EX_CONSTANT(opline->op2) + 1, ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION);
- if (UNEXPECTED(ce == NULL)) {
- if (IS_CV != IS_CONST) {
- zend_string_release(name);
- }
+ /* check if static properties were destoyed */
+ if (UNEXPECTED(CE_STATIC_MEMBERS(ce) == NULL)) {
+ zend_throw_error(NULL, "Access to undeclared static property: %s::$%s", ZSTR_VAL(ce->name), ZSTR_VAL(name));
- ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
- }
- CACHE_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)), ce);
+ HANDLE_EXCEPTION();
}
- } else {
- ce = Z_CE_P(EX_VAR(opline->op2.var));
- if (IS_CV == IS_CONST &&
- (retval = CACHED_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)), ce)) != NULL) {
- /* check if static properties were destoyed */
- if (UNEXPECTED(CE_STATIC_MEMBERS(ce) == NULL)) {
- zend_throw_error(NULL, "Access to undeclared static property: %s::$%s", ZSTR_VAL(ce->name), ZSTR_VAL(name));
-
- HANDLE_EXCEPTION();
+ goto fetch_static_prop_return;
+ } else if (UNEXPECTED((ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)))) == NULL)) {
+ ce = zend_fetch_class_by_name(Z_STR_P(EX_CONSTANT(opline->op2)), EX_CONSTANT(opline->op2) + 1, ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION);
+ if (UNEXPECTED(ce == NULL)) {
+ if (IS_CV != IS_CONST) {
+ zend_string_release(name);
}
- goto fetch_var_return;
+ ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
+ CACHE_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)), ce);
}
- retval = zend_std_get_static_property(ce, name, 0);
- if (UNEXPECTED(EG(exception))) {
- if (IS_CV != IS_CONST) {
- zend_string_release(name);
- }
-
- HANDLE_EXCEPTION();
- }
- if (IS_CV == IS_CONST && retval) {
- CACHE_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)), ce, retval);
- }
-
} else {
- target_symbol_table = zend_get_target_symbol_table(execute_data, opline->extended_value & ZEND_FETCH_TYPE_MASK);
- retval = zend_hash_find(target_symbol_table, name);
- if (retval == NULL) {
- switch (type) {
- case BP_VAR_R:
- case BP_VAR_UNSET:
- zend_error(E_NOTICE,"Undefined variable: %s", ZSTR_VAL(name));
- /* break missing intentionally */
- case BP_VAR_IS:
- retval = &EG(uninitialized_zval);
- break;
- case BP_VAR_RW:
- zend_error(E_NOTICE,"Undefined variable: %s", ZSTR_VAL(name));
- retval = zend_hash_update(target_symbol_table, name, &EG(uninitialized_zval));
- break;
- case BP_VAR_W:
- retval = zend_hash_add_new(target_symbol_table, name, &EG(uninitialized_zval));
- break;
- EMPTY_SWITCH_DEFAULT_CASE()
- }
- /* GLOBAL or $$name variable may be an INDIRECT pointer to CV */
- } else if (Z_TYPE_P(retval) == IS_INDIRECT) {
- retval = Z_INDIRECT_P(retval);
- if (Z_TYPE_P(retval) == IS_UNDEF) {
- switch (type) {
- case BP_VAR_R:
- case BP_VAR_UNSET:
- zend_error(E_NOTICE,"Undefined variable: %s", ZSTR_VAL(name));
- /* break missing intentionally */
- case BP_VAR_IS:
- retval = &EG(uninitialized_zval);
- break;
- case BP_VAR_RW:
- zend_error(E_NOTICE,"Undefined variable: %s", ZSTR_VAL(name));
- /* break missing intentionally */
- case BP_VAR_W:
- ZVAL_NULL(retval);
- break;
- EMPTY_SWITCH_DEFAULT_CASE()
+ if (IS_VAR == IS_UNUSED) {
+ ce = zend_fetch_class(NULL, opline->op2.num);
+ if (UNEXPECTED(ce == NULL)) {
+ ZEND_ASSERT(EG(exception));
+ if (IS_CV != IS_CONST) {
+ zend_string_release(name);
}
+
+ HANDLE_EXCEPTION();
}
+ } else {
+ ce = Z_CE_P(EX_VAR(opline->op2.var));
}
- if ((opline->extended_value & ZEND_FETCH_TYPE_MASK) == ZEND_FETCH_STATIC) {
- if (Z_CONSTANT_P(retval)) {
- if (UNEXPECTED(zval_update_constant_ex(retval, 1, NULL) != SUCCESS)) {
+ if (IS_CV == IS_CONST &&
+ (retval = CACHED_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)), ce)) != NULL) {
- HANDLE_EXCEPTION();
- }
+ /* check if static properties were destoyed */
+ if (UNEXPECTED(CE_STATIC_MEMBERS(ce) == NULL)) {
+ zend_throw_error(NULL, "Access to undeclared static property: %s::$%s", ZSTR_VAL(ce->name), ZSTR_VAL(name));
+
+ HANDLE_EXCEPTION();
}
- } else if ((opline->extended_value & ZEND_FETCH_TYPE_MASK) != ZEND_FETCH_GLOBAL_LOCK) {
+ goto fetch_static_prop_return;
}
}
+ retval = zend_std_get_static_property(ce, name, 0);
+ if (UNEXPECTED(EG(exception))) {
+ if (IS_CV != IS_CONST) {
+ zend_string_release(name);
+ }
+
+ HANDLE_EXCEPTION();
+ }
+ if (IS_CV == IS_CONST && retval) {
+ CACHE_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)), ce, retval);
+ }
if (IS_CV != IS_CONST) {
zend_string_release(name);
}
-fetch_var_return:
+fetch_static_prop_return:
ZEND_ASSERT(retval != NULL);
if (type == BP_VAR_R || type == BP_VAR_IS) {
if (/*type == BP_VAR_R &&*/ Z_ISREF_P(retval) && Z_REFCOUNT_P(retval) == 1) {
@@ -33524,43 +40670,43 @@ fetch_var_return:
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_R_SPEC_CV_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_STATIC_PROP_R_SPEC_CV_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
- ZEND_VM_TAIL_CALL(zend_fetch_var_address_helper_SPEC_CV_VAR(BP_VAR_R ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_fetch_static_prop_helper_SPEC_CV_VAR(BP_VAR_R ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_W_SPEC_CV_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_STATIC_PROP_W_SPEC_CV_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
- ZEND_VM_TAIL_CALL(zend_fetch_var_address_helper_SPEC_CV_VAR(BP_VAR_W ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_fetch_static_prop_helper_SPEC_CV_VAR(BP_VAR_W ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_RW_SPEC_CV_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_STATIC_PROP_RW_SPEC_CV_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
- ZEND_VM_TAIL_CALL(zend_fetch_var_address_helper_SPEC_CV_VAR(BP_VAR_RW ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_fetch_static_prop_helper_SPEC_CV_VAR(BP_VAR_RW ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_FUNC_ARG_SPEC_CV_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_STATIC_PROP_FUNC_ARG_SPEC_CV_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
if (zend_is_by_ref_func_arg_fetch(opline, EX(call))) {
- ZEND_VM_TAIL_CALL(zend_fetch_var_address_helper_SPEC_CV_VAR(BP_VAR_W ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_fetch_static_prop_helper_SPEC_CV_VAR(BP_VAR_W ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
} else {
- ZEND_VM_TAIL_CALL(zend_fetch_var_address_helper_SPEC_CV_VAR(BP_VAR_R ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_fetch_static_prop_helper_SPEC_CV_VAR(BP_VAR_R ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_UNSET_SPEC_CV_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_STATIC_PROP_UNSET_SPEC_CV_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
- ZEND_VM_TAIL_CALL(zend_fetch_var_address_helper_SPEC_CV_VAR(BP_VAR_UNSET ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_fetch_static_prop_helper_SPEC_CV_VAR(BP_VAR_UNSET ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_IS_SPEC_CV_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_STATIC_PROP_IS_SPEC_CV_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
- ZEND_VM_TAIL_CALL(zend_fetch_var_address_helper_SPEC_CV_VAR(BP_VAR_IS ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_fetch_static_prop_helper_SPEC_CV_VAR(BP_VAR_IS ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_SPEC_CV_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_SPEC_CV_VAR_RETVAL_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
zend_free_op free_op2;
@@ -33571,14 +40717,42 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_SPEC_CV_VAR_HANDLER(ZEN
value = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2);
variable_ptr = _get_zval_ptr_cv_undef_BP_VAR_W(execute_data, opline->op1.var);
- if (IS_CV == IS_VAR && UNEXPECTED(variable_ptr == &EG(error_zval))) {
+ if (IS_CV == IS_VAR && UNEXPECTED(Z_ISERROR_P(variable_ptr))) {
zval_ptr_dtor_nogc(free_op2);
- if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ if (UNEXPECTED(0)) {
ZVAL_NULL(EX_VAR(opline->result.var));
}
} else {
value = zend_assign_to_variable(variable_ptr, value, IS_VAR);
- if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ if (UNEXPECTED(0)) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+
+ /* zend_assign_to_variable() always takes care of op2, never free it! */
+ }
+
+ ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_SPEC_CV_VAR_RETVAL_USED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zend_free_op free_op2;
+ zval *value;
+ zval *variable_ptr;
+
+ SAVE_OPLINE();
+ value = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2);
+ variable_ptr = _get_zval_ptr_cv_undef_BP_VAR_W(execute_data, opline->op1.var);
+
+ if (IS_CV == IS_VAR && UNEXPECTED(Z_ISERROR_P(variable_ptr))) {
+ zval_ptr_dtor_nogc(free_op2);
+ if (UNEXPECTED(1)) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+ } else {
+ value = zend_assign_to_variable(variable_ptr, value, IS_VAR);
+ if (UNEXPECTED(1)) {
ZVAL_COPY(EX_VAR(opline->result.var), value);
}
@@ -33597,90 +40771,59 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_REF_SPEC_CV_VAR_HANDLER
SAVE_OPLINE();
value_ptr = _get_zval_ptr_ptr_var(opline->op2.var, execute_data, &free_op2);
+ variable_ptr = _get_zval_ptr_cv_undef_BP_VAR_W(execute_data, opline->op1.var);
- if (IS_VAR == IS_VAR && UNEXPECTED(value_ptr == NULL)) {
- zend_throw_error(NULL, "Cannot create references to/from string offsets nor overloaded objects");
-
- HANDLE_EXCEPTION();
- }
if (IS_CV == IS_VAR &&
UNEXPECTED(Z_TYPE_P(EX_VAR(opline->op1.var)) != IS_INDIRECT) &&
- UNEXPECTED(!Z_ISREF_P(EX_VAR(opline->op1.var)))) {
+ UNEXPECTED(!Z_ISREF_P(EX_VAR(opline->op1.var))) &&
+ UNEXPECTED(!Z_ISERROR_P(EX_VAR(opline->op1.var)))) {
+
zend_throw_error(NULL, "Cannot assign by reference to overloaded object");
if (UNEXPECTED(free_op2)) {zval_ptr_dtor_nogc(free_op2);};
HANDLE_EXCEPTION();
- }
- if (IS_VAR == IS_VAR &&
- (value_ptr == &EG(uninitialized_zval) ||
- (opline->extended_value == ZEND_RETURNS_FUNCTION &&
- !(Z_VAR_FLAGS_P(value_ptr) & IS_VAR_RET_REF)))) {
- if (!(free_op2 != NULL) && UNEXPECTED(Z_TYPE_P(EX_VAR(opline->op2.var)) != IS_INDIRECT)) { /* undo the effect of get_zval_ptr_ptr() */
- Z_TRY_ADDREF_P(value_ptr);
- }
+
+ } else if (IS_VAR == IS_VAR &&
+ opline->extended_value == ZEND_RETURNS_FUNCTION &&
+ UNEXPECTED(!Z_ISREF_P(value_ptr))) {
zend_error(E_NOTICE, "Only variables should be assigned by reference");
if (UNEXPECTED(EG(exception) != NULL)) {
if (UNEXPECTED(free_op2)) {zval_ptr_dtor_nogc(free_op2);};
HANDLE_EXCEPTION();
}
- ZEND_VM_TAIL_CALL(ZEND_ASSIGN_SPEC_CV_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU));
- }
- variable_ptr = _get_zval_ptr_cv_undef_BP_VAR_W(execute_data, opline->op1.var);
- if (IS_CV == IS_VAR && UNEXPECTED(variable_ptr == NULL)) {
- zend_throw_error(NULL, "Cannot create references to/from string offsets nor overloaded objects");
- if (UNEXPECTED(free_op2)) {zval_ptr_dtor_nogc(free_op2);};
- HANDLE_EXCEPTION();
- }
- if ((IS_CV == IS_VAR && UNEXPECTED(variable_ptr == &EG(error_zval))) ||
- (IS_VAR == IS_VAR && UNEXPECTED(value_ptr == &EG(error_zval)))) {
- variable_ptr = &EG(uninitialized_zval);
+ value_ptr = zend_assign_to_variable(variable_ptr, value_ptr, IS_VAR);
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value_ptr);
+ }
+ /* zend_assign_to_variable() always takes care of op2, never free it! */
+
} else {
- zend_assign_to_variable_reference(variable_ptr, value_ptr);
- }
- if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
- ZVAL_COPY(EX_VAR(opline->result.var), variable_ptr);
- }
+ if ((IS_CV == IS_VAR && UNEXPECTED(Z_ISERROR_P(variable_ptr))) ||
+ (IS_VAR == IS_VAR && UNEXPECTED(Z_ISERROR_P(value_ptr)))) {
+ variable_ptr = &EG(uninitialized_zval);
+ } else {
+ zend_assign_to_variable_reference(variable_ptr, value_ptr);
+ }
- if (UNEXPECTED(free_op2)) {zval_ptr_dtor_nogc(free_op2);};
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), variable_ptr);
+ }
+
+ if (UNEXPECTED(free_op2)) {zval_ptr_dtor_nogc(free_op2);};
+ }
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_UNSET_VAR_SPEC_CV_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_UNSET_STATIC_PROP_SPEC_CV_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
zval tmp, *varname;
- HashTable *target_symbol_table;
+ zend_class_entry *ce;
SAVE_OPLINE();
- if (IS_CV == IS_CV &&
- IS_VAR == IS_UNUSED &&
- (opline->extended_value & ZEND_QUICK_SET)) {
- zval *var = EX_VAR(opline->op1.var);
-
- if (Z_REFCOUNTED_P(var)) {
- zend_refcounted *garbage = Z_COUNTED_P(var);
-
- if (!--GC_REFCOUNT(garbage)) {
- ZVAL_UNDEF(var);
- zval_dtor_func_for_ptr(garbage);
- } else {
- zval *z = var;
- ZVAL_DEREF(z);
- if (Z_COLLECTABLE_P(z) && UNEXPECTED(!Z_GC_INFO_P(z))) {
- ZVAL_UNDEF(var);
- gc_possible_root(Z_COUNTED_P(z));
- } else {
- ZVAL_UNDEF(var);
- }
- }
- } else {
- ZVAL_UNDEF(var);
- }
- ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
- }
varname = _get_zval_ptr_cv_undef(execute_data, opline->op1.var);
@@ -33693,33 +40836,36 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_UNSET_VAR_SPEC_CV_VAR_HANDLER(
varname = &tmp;
}
- if (IS_VAR != IS_UNUSED) {
- zend_class_entry *ce;
-
- if (IS_VAR == IS_CONST) {
- ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)));
+ if (IS_VAR == IS_CONST) {
+ ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)));
+ if (UNEXPECTED(ce == NULL)) {
+ ce = zend_fetch_class_by_name(Z_STR_P(EX_CONSTANT(opline->op2)), EX_CONSTANT(opline->op2) + 1, ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION);
if (UNEXPECTED(ce == NULL)) {
- ce = zend_fetch_class_by_name(Z_STR_P(EX_CONSTANT(opline->op2)), EX_CONSTANT(opline->op2) + 1, ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION);
- if (UNEXPECTED(ce == NULL)) {
- if (EXPECTED(!EG(exception))) {
- zend_throw_error(NULL, "Class '%s' not found", Z_STRVAL_P(EX_CONSTANT(opline->op2)));
- }
- if (IS_CV != IS_CONST && Z_TYPE(tmp) != IS_UNDEF) {
- zend_string_release(Z_STR(tmp));
- }
-
- HANDLE_EXCEPTION();
+ if (EXPECTED(!EG(exception))) {
+ zend_throw_error(NULL, "Class '%s' not found", Z_STRVAL_P(EX_CONSTANT(opline->op2)));
}
- CACHE_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)), ce);
+ if (IS_CV != IS_CONST && Z_TYPE(tmp) != IS_UNDEF) {
+ zend_string_release(Z_STR(tmp));
+ }
+
+ HANDLE_EXCEPTION();
}
- } else {
- ce = Z_CE_P(EX_VAR(opline->op2.var));
+ CACHE_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)), ce);
+ }
+ } else if (IS_VAR == IS_UNUSED) {
+ ce = zend_fetch_class(NULL, opline->op2.num);
+ if (UNEXPECTED(ce == NULL)) {
+ ZEND_ASSERT(EG(exception));
+ if (IS_CV != IS_CONST && Z_TYPE(tmp) != IS_UNDEF) {
+ zend_string_release(Z_STR(tmp));
+ }
+
+ HANDLE_EXCEPTION();
}
- zend_std_unset_static_property(ce, Z_STR_P(varname));
} else {
- target_symbol_table = zend_get_target_symbol_table(execute_data, opline->extended_value & ZEND_FETCH_TYPE_MASK);
- zend_hash_del_ind(target_symbol_table, Z_STR_P(varname));
+ ce = Z_CE_P(EX_VAR(opline->op2.var));
}
+ zend_std_unset_static_property(ce, Z_STR_P(varname));
if (IS_CV != IS_CONST && Z_TYPE(tmp) != IS_UNDEF) {
zend_string_release(Z_STR(tmp));
@@ -33728,103 +40874,87 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_UNSET_VAR_SPEC_CV_VAR_HANDLER(
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ISSET_ISEMPTY_VAR_SPEC_CV_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ISSET_ISEMPTY_STATIC_PROP_SPEC_CV_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
zval *value;
int result;
- if (IS_CV == IS_CV &&
- IS_VAR == IS_UNUSED &&
- (opline->extended_value & ZEND_QUICK_SET)) {
- value = EX_VAR(opline->op1.var);
- if (opline->extended_value & ZEND_ISSET) {
- result =
- Z_TYPE_P(value) > IS_NULL &&
- (!Z_ISREF_P(value) || Z_TYPE_P(Z_REFVAL_P(value)) != IS_NULL);
- } else /* if (opline->extended_value & ZEND_ISEMPTY) */ {
- SAVE_OPLINE();
- result = !i_zend_is_true(value);
- if (UNEXPECTED(EG(exception))) {
- HANDLE_EXCEPTION();
- }
- }
- ZEND_VM_SMART_BRANCH(result, 0);
- ZVAL_BOOL(EX_VAR(opline->result.var), result);
- ZEND_VM_SET_NEXT_OPCODE(opline + 1);
- ZEND_VM_CONTINUE();
- } else {
-
- zval tmp, *varname;
-
- SAVE_OPLINE();
- varname = _get_zval_ptr_cv_BP_VAR_IS(execute_data, opline->op1.var);
- ZVAL_UNDEF(&tmp);
- if (IS_CV != IS_CONST && Z_TYPE_P(varname) != IS_STRING) {
- ZVAL_STR(&tmp, zval_get_string(varname));
- varname = &tmp;
- }
-
- if (IS_VAR != IS_UNUSED) {
- zend_class_entry *ce;
-
- if (IS_VAR == IS_CONST) {
- if (IS_CV == IS_CONST && EXPECTED((ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)))) != NULL)) {
- value = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)) + sizeof(void*));
-
- /* check if static properties were destoyed */
- if (UNEXPECTED(CE_STATIC_MEMBERS(ce) == NULL)) {
- value = NULL;
- }
+ zval tmp, *varname;
+ zend_class_entry *ce;
- goto is_var_return;
- } else if (UNEXPECTED((ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)))) == NULL)) {
- ce = zend_fetch_class_by_name(Z_STR_P(EX_CONSTANT(opline->op2)), EX_CONSTANT(opline->op2) + 1, ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION);
- if (UNEXPECTED(ce == NULL)) {
- ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
- }
- CACHE_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)), ce);
- }
- } else {
- ce = Z_CE_P(EX_VAR(opline->op2.var));
- if (IS_CV == IS_CONST &&
- (value = CACHED_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)), ce)) != NULL) {
+ SAVE_OPLINE();
+ varname = _get_zval_ptr_cv_BP_VAR_IS(execute_data, opline->op1.var);
+ ZVAL_UNDEF(&tmp);
+ if (IS_CV != IS_CONST && Z_TYPE_P(varname) != IS_STRING) {
+ ZVAL_STR(&tmp, zval_get_string(varname));
+ varname = &tmp;
+ }
- /* check if static properties were destoyed */
- if (UNEXPECTED(CE_STATIC_MEMBERS(ce) == NULL)) {
- value = NULL;
- }
+ if (IS_VAR == IS_CONST) {
+ if (IS_CV == IS_CONST && EXPECTED((ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)))) != NULL)) {
+ value = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)) + sizeof(void*));
- goto is_var_return;
- }
+ /* check if static properties were destoyed */
+ if (UNEXPECTED(CE_STATIC_MEMBERS(ce) == NULL)) {
+ value = NULL;
}
- value = zend_std_get_static_property(ce, Z_STR_P(varname), 1);
+ goto is_static_prop_return;
+ } else if (UNEXPECTED((ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)))) == NULL)) {
+ ce = zend_fetch_class_by_name(Z_STR_P(EX_CONSTANT(opline->op2)), EX_CONSTANT(opline->op2) + 1, ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION);
+ if (UNEXPECTED(ce == NULL)) {
+ ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
+ }
+ CACHE_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)), ce);
+ }
+ } else {
+ if (IS_VAR == IS_UNUSED) {
+ ce = zend_fetch_class(NULL, opline->op2.num);
+ if (UNEXPECTED(ce == NULL)) {
+ ZEND_ASSERT(EG(exception));
+ if (IS_CV != IS_CONST && Z_TYPE(tmp) != IS_UNDEF) {
+ zend_string_release(Z_STR(tmp));
+ }
- if (IS_CV == IS_CONST && value) {
- CACHE_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)), ce, value);
+ HANDLE_EXCEPTION();
}
} else {
- HashTable *target_symbol_table = zend_get_target_symbol_table(execute_data, opline->extended_value & ZEND_FETCH_TYPE_MASK);
- value = zend_hash_find_ind(target_symbol_table, Z_STR_P(varname));
+ ce = Z_CE_P(EX_VAR(opline->op2.var));
}
+ if (IS_CV == IS_CONST &&
+ (value = CACHED_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)), ce)) != NULL) {
- if (IS_CV != IS_CONST && Z_TYPE(tmp) != IS_UNDEF) {
- zend_string_release(Z_STR(tmp));
- }
+ /* check if static properties were destoyed */
+ if (UNEXPECTED(CE_STATIC_MEMBERS(ce) == NULL)) {
+ value = NULL;
+ }
-is_var_return:
- if (opline->extended_value & ZEND_ISSET) {
- result = value && Z_TYPE_P(value) > IS_NULL &&
- (!Z_ISREF_P(value) || Z_TYPE_P(Z_REFVAL_P(value)) != IS_NULL);
- } else /* if (opline->extended_value & ZEND_ISEMPTY) */ {
- result = !value || !i_zend_is_true(value);
+ goto is_static_prop_return;
}
+ }
- ZEND_VM_SMART_BRANCH(result, 1);
- ZVAL_BOOL(EX_VAR(opline->result.var), result);
- ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
+ value = zend_std_get_static_property(ce, Z_STR_P(varname), 1);
+
+ if (IS_CV == IS_CONST && value) {
+ CACHE_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)), ce, value);
}
+
+ if (IS_CV != IS_CONST && Z_TYPE(tmp) != IS_UNDEF) {
+ zend_string_release(Z_STR(tmp));
+ }
+
+is_static_prop_return:
+ if (opline->extended_value & ZEND_ISSET) {
+ result = value && Z_TYPE_P(value) > IS_NULL &&
+ (!Z_ISREF_P(value) || Z_TYPE_P(Z_REFVAL_P(value)) != IS_NULL);
+ } else /* if (opline->extended_value & ZEND_ISEMPTY) */ {
+ result = !value || !i_zend_is_true(value);
+ }
+
+ ZEND_VM_SMART_BRANCH(result, 1);
+ ZVAL_BOOL(EX_VAR(opline->result.var), result);
+ ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INSTANCEOF_SPEC_CV_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@@ -33852,6 +40982,13 @@ try_instanceof:
}
CACHE_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)), ce);
}
+ } else if (IS_VAR == IS_UNUSED) {
+ ce = zend_fetch_class(NULL, opline->op2.num);
+ if (UNEXPECTED(ce == NULL)) {
+ ZEND_ASSERT(EG(exception));
+
+ HANDLE_EXCEPTION();
+ }
} else {
ce = Z_CE_P(EX_VAR(opline->op2.var));
}
@@ -33898,7 +41035,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_CV_VAR_HANDLER(ZEND
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) {
+ if (IS_CV & (IS_CONST|IS_TMP_VAR)) {
zval *value;
zend_error(E_NOTICE, "Only variable references should be yielded by reference");
@@ -33906,25 +41043,19 @@ 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 (IS_CV == IS_CONST) {
- if (UNEXPECTED(Z_OPT_COPYABLE(generator->value))) {
- zval_copy_ctor_func(&generator->value);
+ if (UNEXPECTED(Z_OPT_REFCOUNTED(generator->value))) {
+ Z_ADDREF(generator->value);
}
}
} else {
zval *value_ptr = _get_zval_ptr_cv_BP_VAR_W(execute_data, opline->op1.var);
- if (IS_CV == IS_VAR && UNEXPECTED(value_ptr == NULL)) {
- zend_throw_error(NULL, "Cannot yield string offsets by reference");
- zval_ptr_dtor_nogc(EX_VAR(opline->op2.var));
- HANDLE_EXCEPTION();
- }
-
/* If a function call result is yielded and the function did
* not return by reference we throw a notice. */
if (IS_CV == IS_VAR &&
(value_ptr == &EG(uninitialized_zval) ||
(opline->extended_value == ZEND_RETURNS_FUNCTION &&
- !(Z_VAR_FLAGS_P(value_ptr) & IS_VAR_RET_REF)))) {
+ !Z_ISREF_P(value_ptr)))) {
zend_error(E_NOTICE, "Only variable references should be yielded by reference");
} else {
ZVAL_MAKE_REF(value_ptr);
@@ -33938,8 +41069,8 @@ 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_COPY_VALUE(&generator->value, value);
- if (UNEXPECTED(Z_OPT_COPYABLE(generator->value))) {
- zval_copy_ctor_func(&generator->value);
+ if (UNEXPECTED(Z_OPT_REFCOUNTED(generator->value))) {
+ Z_ADDREF(generator->value);
}
} else if (IS_CV == IS_TMP_VAR) {
ZVAL_COPY_VALUE(&generator->value, value);
@@ -33966,8 +41097,8 @@ 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_COPY_VALUE(&generator->key, key);
- if (UNEXPECTED(Z_OPT_COPYABLE(generator->key))) {
- zval_copy_ctor_func(&generator->key);
+ if (UNEXPECTED(Z_OPT_REFCOUNTED(generator->key))) {
+ Z_ADDREF(generator->key);
}
} else if (IS_VAR == IS_TMP_VAR) {
ZVAL_COPY_VALUE(&generator->key, key);
@@ -34016,307 +41147,314 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_binary_assign_op_dim_helper_SP
{
USE_OPLINE
zend_free_op free_op_data1;
- zval *var_ptr, rv;
+ zval *var_ptr;
zval *value, *container, *dim;
SAVE_OPLINE();
- container = _get_zval_ptr_cv_BP_VAR_RW(execute_data, opline->op1.var);
- if (IS_CV == IS_UNUSED && UNEXPECTED(Z_OBJ_P(container) == NULL)) {
- zend_throw_error(NULL, "Using $this when not in object context");
- FREE_UNFETCHED_OP((opline+1)->op1_type, (opline+1)->op1.var);
-
- HANDLE_EXCEPTION();
- }
- if (IS_CV == IS_VAR && UNEXPECTED(container == NULL)) {
- zend_throw_error(NULL, "Cannot use string offset as an array");
- FREE_UNFETCHED_OP((opline+1)->op1_type, (opline+1)->op1.var);
+ container = _get_zval_ptr_cv_undef_BP_VAR_RW(execute_data, opline->op1.var);
- HANDLE_EXCEPTION();
- }
-
- dim = NULL;
+ if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) {
+assign_dim_op_array:
+ SEPARATE_ARRAY(container);
+assign_dim_op_new_array:
+ if (IS_UNUSED == IS_UNUSED) {
+ var_ptr = zend_hash_next_index_insert(Z_ARRVAL_P(container), &EG(uninitialized_zval));
+ if (UNEXPECTED(!var_ptr)) {
+ zend_error(E_WARNING, "Cannot add element to the array as the next element is already occupied");
+ goto assign_dim_op_ret_null;
+ }
+ } else {
+ dim = NULL;
- do {
- if (IS_CV == IS_UNUSED || UNEXPECTED(Z_TYPE_P(container) != IS_ARRAY)) {
- if (IS_CV != IS_UNUSED) {
- ZVAL_DEREF(container);
+ if (IS_UNUSED == IS_CONST) {
+ var_ptr = zend_fetch_dimension_address_inner_RW_CONST(Z_ARRVAL_P(container), dim);
+ } else {
+ var_ptr = zend_fetch_dimension_address_inner_RW(Z_ARRVAL_P(container), dim);
}
- if (IS_CV == IS_UNUSED || EXPECTED(Z_TYPE_P(container) == IS_OBJECT)) {
- value = get_zval_ptr_r((opline+1)->op1_type, (opline+1)->op1, execute_data, &free_op_data1);
- zend_binary_assign_op_obj_dim(container, dim, value, UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL, binary_op);
- break;
+ if (UNEXPECTED(!var_ptr)) {
+ goto assign_dim_op_ret_null;
}
+ ZVAL_DEREF(var_ptr);
+ SEPARATE_ZVAL_NOREF(var_ptr);
}
- zend_fetch_dimension_address_RW(&rv, container, dim, IS_UNUSED);
value = get_zval_ptr_r((opline+1)->op1_type, (opline+1)->op1, execute_data, &free_op_data1);
- ZEND_ASSERT(Z_TYPE(rv) == IS_INDIRECT);
- var_ptr = Z_INDIRECT(rv);
-
- if (UNEXPECTED(var_ptr == NULL)) {
- zend_throw_error(NULL, "Cannot use assign-op operators with overloaded objects nor string offsets");
- FREE_OP(free_op_data1);
+ binary_op(var_ptr, var_ptr, value);
- HANDLE_EXCEPTION();
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), var_ptr);
}
-
- if (UNEXPECTED(var_ptr == &EG(error_zval))) {
- if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
- ZVAL_NULL(EX_VAR(opline->result.var));
+ } else {
+ if (EXPECTED(Z_ISREF_P(container))) {
+ container = Z_REFVAL_P(container);
+ if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) {
+ goto assign_dim_op_array;
}
- } else {
- ZVAL_DEREF(var_ptr);
- SEPARATE_ZVAL_NOREF(var_ptr);
+ } else if (IS_CV == IS_CV && UNEXPECTED(Z_TYPE_INFO_P(container) == IS_UNDEF)) {
+ container = GET_OP1_UNDEF_CV(container, BP_VAR_RW);
+assign_dim_op_convert_to_array:
+ ZVAL_NEW_ARR(container);
+ zend_hash_init(Z_ARRVAL_P(container), 8, NULL, ZVAL_PTR_DTOR, 0);
+ goto assign_dim_op_new_array;
+ }
- binary_op(var_ptr, var_ptr, value);
+ dim = NULL;
- if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
- ZVAL_COPY(EX_VAR(opline->result.var), var_ptr);
+ if (EXPECTED(Z_TYPE_P(container) == IS_OBJECT)) {
+ value = get_zval_ptr_r((opline+1)->op1_type, (opline+1)->op1, execute_data, &free_op_data1);
+ zend_binary_assign_op_obj_dim(container, dim, value, UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL, binary_op);
+ } else {
+ if (UNEXPECTED(Z_TYPE_P(container) == IS_STRING)) {
+ if (IS_UNUSED == IS_UNUSED) {
+ zend_throw_error(NULL, "[] operator not supported for strings");
+ } else {
+ zend_check_string_offset(dim, BP_VAR_RW);
+ zend_wrong_string_offset();
+ }
+ } else if (EXPECTED(Z_TYPE_P(container) <= IS_FALSE)) {
+ goto assign_dim_op_convert_to_array;
+ } else {
+ if (UNEXPECTED(IS_CV != IS_VAR || EXPECTED(!Z_ISERROR_P(container)))) {
+ zend_error(E_WARNING, "Cannot use a scalar value as an array");
+ }
+assign_dim_op_ret_null:
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
}
+ value = get_zval_ptr_r((opline+1)->op1_type, (opline+1)->op1, execute_data, &free_op_data1);
}
- } while (0);
+ }
FREE_OP(free_op_data1);
ZEND_VM_NEXT_OPCODE_EX(1, 2);
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_ADD_SPEC_CV_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_ADD_SPEC_CV_UNUSED_DIM_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
#if 0 || (IS_UNUSED != IS_UNUSED)
USE_OPLINE
# if 0 || (IS_CV != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_CV_UNUSED(add_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
-# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
+ if (EXPECTED(1)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_UNUSED(add_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_UNUSED(add_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_UNUSED(add_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#else
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_UNUSED(add_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#endif
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_SUB_SPEC_CV_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_SUB_SPEC_CV_UNUSED_DIM_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
#if 0 || (IS_UNUSED != IS_UNUSED)
USE_OPLINE
# if 0 || (IS_CV != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_CV_UNUSED(sub_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
-# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
+ if (EXPECTED(1)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_UNUSED(sub_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_UNUSED(sub_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_UNUSED(sub_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#else
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_UNUSED(sub_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#endif
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_MUL_SPEC_CV_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_MUL_SPEC_CV_UNUSED_DIM_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
#if 0 || (IS_UNUSED != IS_UNUSED)
USE_OPLINE
# if 0 || (IS_CV != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_CV_UNUSED(mul_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
-# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
+ if (EXPECTED(1)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_UNUSED(mul_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_UNUSED(mul_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_UNUSED(mul_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#else
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_UNUSED(mul_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#endif
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIV_SPEC_CV_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIV_SPEC_CV_UNUSED_DIM_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
#if 0 || (IS_UNUSED != IS_UNUSED)
USE_OPLINE
# if 0 || (IS_CV != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_CV_UNUSED(div_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
-# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
+ if (EXPECTED(1)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_UNUSED(div_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_UNUSED(div_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_UNUSED(div_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#else
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_UNUSED(div_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#endif
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_MOD_SPEC_CV_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_MOD_SPEC_CV_UNUSED_DIM_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
#if 0 || (IS_UNUSED != IS_UNUSED)
USE_OPLINE
# if 0 || (IS_CV != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_CV_UNUSED(mod_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
-# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
+ if (EXPECTED(1)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_UNUSED(mod_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_UNUSED(mod_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_UNUSED(mod_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#else
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_UNUSED(mod_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#endif
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_SL_SPEC_CV_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_SL_SPEC_CV_UNUSED_DIM_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
#if 0 || (IS_UNUSED != IS_UNUSED)
USE_OPLINE
# if 0 || (IS_CV != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_CV_UNUSED(shift_left_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
-# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
+ if (EXPECTED(1)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_UNUSED(shift_left_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_UNUSED(shift_left_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_UNUSED(shift_left_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#else
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_UNUSED(shift_left_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#endif
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_SR_SPEC_CV_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_SR_SPEC_CV_UNUSED_DIM_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
#if 0 || (IS_UNUSED != IS_UNUSED)
USE_OPLINE
# if 0 || (IS_CV != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_CV_UNUSED(shift_right_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
-# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
+ if (EXPECTED(1)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_UNUSED(shift_right_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_UNUSED(shift_right_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_UNUSED(shift_right_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#else
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_UNUSED(shift_right_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#endif
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_CONCAT_SPEC_CV_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_CONCAT_SPEC_CV_UNUSED_DIM_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
#if 0 || (IS_UNUSED != IS_UNUSED)
USE_OPLINE
# if 0 || (IS_CV != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_CV_UNUSED(concat_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
-# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
+ if (EXPECTED(1)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_UNUSED(concat_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_UNUSED(concat_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_UNUSED(concat_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#else
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_UNUSED(concat_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#endif
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_BW_OR_SPEC_CV_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_BW_OR_SPEC_CV_UNUSED_DIM_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
#if 0 || (IS_UNUSED != IS_UNUSED)
USE_OPLINE
# if 0 || (IS_CV != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_CV_UNUSED(bitwise_or_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
-# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
+ if (EXPECTED(1)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_UNUSED(bitwise_or_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_UNUSED(bitwise_or_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_UNUSED(bitwise_or_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#else
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_UNUSED(bitwise_or_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#endif
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_BW_AND_SPEC_CV_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_BW_AND_SPEC_CV_UNUSED_DIM_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
#if 0 || (IS_UNUSED != IS_UNUSED)
USE_OPLINE
# if 0 || (IS_CV != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_CV_UNUSED(bitwise_and_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
-# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
+ if (EXPECTED(1)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_UNUSED(bitwise_and_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_UNUSED(bitwise_and_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_UNUSED(bitwise_and_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#else
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_UNUSED(bitwise_and_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#endif
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_BW_XOR_SPEC_CV_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_BW_XOR_SPEC_CV_UNUSED_DIM_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
#if 0 || (IS_UNUSED != IS_UNUSED)
USE_OPLINE
# if 0 || (IS_CV != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_CV_UNUSED(bitwise_xor_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
-# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
+ if (EXPECTED(1)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_UNUSED(bitwise_xor_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_UNUSED(bitwise_xor_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_UNUSED(bitwise_xor_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#else
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_UNUSED(bitwise_xor_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#endif
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_POW_SPEC_CV_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_POW_SPEC_CV_UNUSED_DIM_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
#if 0 || (IS_UNUSED != IS_UNUSED)
USE_OPLINE
# if 0 || (IS_CV != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_CV_UNUSED(pow_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
-# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
+ if (EXPECTED(1)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_UNUSED(pow_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_UNUSED(pow_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_UNUSED(pow_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#else
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_UNUSED(pow_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#endif
@@ -34346,63 +41484,70 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_fetch_var_address_helper_SPEC_
name = zval_get_string(varname);
}
- if (IS_UNUSED != IS_UNUSED) {
- zend_class_entry *ce;
-
- if (IS_UNUSED == IS_CONST) {
- if (IS_CV == IS_CONST && EXPECTED((ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)))) != NULL)) {
- retval = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)) + sizeof(void*));
-
- /* check if static properties were destoyed */
- if (UNEXPECTED(CE_STATIC_MEMBERS(ce) == NULL)) {
- zend_throw_error(NULL, "Access to undeclared static property: %s::$%s", ZSTR_VAL(ce->name), ZSTR_VAL(name));
-
- HANDLE_EXCEPTION();
- }
+ target_symbol_table = zend_get_target_symbol_table(execute_data, opline->extended_value & ZEND_FETCH_TYPE_MASK);
+ retval = zend_hash_find(target_symbol_table, name);
+ if (retval == NULL) {
+ if (UNEXPECTED(zend_string_equals(name, CG(known_strings)[ZEND_STR_THIS]))) {
+ zval *result;
- goto fetch_var_return;
- } else if (UNEXPECTED((ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)))) == NULL)) {
- ce = zend_fetch_class_by_name(Z_STR_P(EX_CONSTANT(opline->op2)), EX_CONSTANT(opline->op2) + 1, ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION);
- if (UNEXPECTED(ce == NULL)) {
- if (IS_CV != IS_CONST) {
- zend_string_release(name);
+fetch_this:
+ result = EX_VAR(opline->result.var);
+ switch (type) {
+ case BP_VAR_R:
+ if (EXPECTED(Z_TYPE(EX(This)) == IS_OBJECT)) {
+ ZVAL_OBJ(result, Z_OBJ(EX(This)));
+ Z_ADDREF_P(result);
+ } else {
+ ZVAL_NULL(result);
+ zend_error(E_NOTICE,"Undefined variable: this");
}
-
- ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
- }
- CACHE_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)), ce);
- }
- } else {
- ce = Z_CE_P(EX_VAR(opline->op2.var));
- if (IS_CV == IS_CONST &&
- (retval = CACHED_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)), ce)) != NULL) {
-
- /* check if static properties were destoyed */
- if (UNEXPECTED(CE_STATIC_MEMBERS(ce) == NULL)) {
- zend_throw_error(NULL, "Access to undeclared static property: %s::$%s", ZSTR_VAL(ce->name), ZSTR_VAL(name));
-
- HANDLE_EXCEPTION();
- }
-
- goto fetch_var_return;
+ break;
+ case BP_VAR_IS:
+ if (EXPECTED(Z_TYPE(EX(This)) == IS_OBJECT)) {
+ ZVAL_OBJ(result, Z_OBJ(EX(This)));
+ Z_ADDREF_P(result);
+ } else {
+ ZVAL_NULL(result);
+ }
+ break;
+ case BP_VAR_RW:
+ case BP_VAR_W:
+ zend_throw_error(NULL, "Cannot re-assign $this");
+ break;
+ case BP_VAR_UNSET:
+ zend_throw_error(NULL, "Cannot unset $this");
+ break;
+ EMPTY_SWITCH_DEFAULT_CASE()
}
- }
- retval = zend_std_get_static_property(ce, name, 0);
- if (UNEXPECTED(EG(exception))) {
if (IS_CV != IS_CONST) {
zend_string_release(name);
}
-
- HANDLE_EXCEPTION();
+ ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
- if (IS_CV == IS_CONST && retval) {
- CACHE_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)), ce, retval);
+ switch (type) {
+ case BP_VAR_R:
+ case BP_VAR_UNSET:
+ zend_error(E_NOTICE,"Undefined variable: %s", ZSTR_VAL(name));
+ /* break missing intentionally */
+ case BP_VAR_IS:
+ retval = &EG(uninitialized_zval);
+ break;
+ case BP_VAR_RW:
+ zend_error(E_NOTICE,"Undefined variable: %s", ZSTR_VAL(name));
+ retval = zend_hash_update(target_symbol_table, name, &EG(uninitialized_zval));
+ break;
+ case BP_VAR_W:
+ retval = zend_hash_add_new(target_symbol_table, name, &EG(uninitialized_zval));
+ break;
+ EMPTY_SWITCH_DEFAULT_CASE()
}
-
- } else {
- target_symbol_table = zend_get_target_symbol_table(execute_data, opline->extended_value & ZEND_FETCH_TYPE_MASK);
- retval = zend_hash_find(target_symbol_table, name);
- if (retval == NULL) {
+ /* GLOBAL or $$name variable may be an INDIRECT pointer to CV */
+ } else if (Z_TYPE_P(retval) == IS_INDIRECT) {
+ retval = Z_INDIRECT_P(retval);
+ if (Z_TYPE_P(retval) == IS_UNDEF) {
+ if (UNEXPECTED(zend_string_equals(name, CG(known_strings)[ZEND_STR_THIS]))) {
+ goto fetch_this;
+ }
switch (type) {
case BP_VAR_R:
case BP_VAR_UNSET:
@@ -34413,52 +41558,23 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_fetch_var_address_helper_SPEC_
break;
case BP_VAR_RW:
zend_error(E_NOTICE,"Undefined variable: %s", ZSTR_VAL(name));
- retval = zend_hash_update(target_symbol_table, name, &EG(uninitialized_zval));
- break;
+ /* break missing intentionally */
case BP_VAR_W:
- retval = zend_hash_add_new(target_symbol_table, name, &EG(uninitialized_zval));
+ ZVAL_NULL(retval);
break;
EMPTY_SWITCH_DEFAULT_CASE()
}
- /* GLOBAL or $$name variable may be an INDIRECT pointer to CV */
- } else if (Z_TYPE_P(retval) == IS_INDIRECT) {
- retval = Z_INDIRECT_P(retval);
- if (Z_TYPE_P(retval) == IS_UNDEF) {
- switch (type) {
- case BP_VAR_R:
- case BP_VAR_UNSET:
- zend_error(E_NOTICE,"Undefined variable: %s", ZSTR_VAL(name));
- /* break missing intentionally */
- case BP_VAR_IS:
- retval = &EG(uninitialized_zval);
- break;
- case BP_VAR_RW:
- zend_error(E_NOTICE,"Undefined variable: %s", ZSTR_VAL(name));
- /* break missing intentionally */
- case BP_VAR_W:
- ZVAL_NULL(retval);
- break;
- EMPTY_SWITCH_DEFAULT_CASE()
- }
- }
}
- if ((opline->extended_value & ZEND_FETCH_TYPE_MASK) == ZEND_FETCH_STATIC) {
- if (Z_CONSTANT_P(retval)) {
- if (UNEXPECTED(zval_update_constant_ex(retval, 1, NULL) != SUCCESS)) {
+ }
- HANDLE_EXCEPTION();
- }
- }
- } else if ((opline->extended_value & ZEND_FETCH_TYPE_MASK) != ZEND_FETCH_GLOBAL_LOCK) {
+ if ((opline->extended_value & ZEND_FETCH_TYPE_MASK) != ZEND_FETCH_GLOBAL_LOCK) {
- }
}
if (IS_CV != IS_CONST) {
zend_string_release(name);
}
-fetch_var_return:
ZEND_ASSERT(retval != NULL);
if (type == BP_VAR_R || type == BP_VAR_IS) {
if (/*type == BP_VAR_R &&*/ Z_ISREF_P(retval) && Z_REFCOUNT_P(retval) == 1) {
@@ -34507,6 +41623,145 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_IS_SPEC_CV_UNUSED_HANDLE
ZEND_VM_TAIL_CALL(zend_fetch_var_address_helper_SPEC_CV_UNUSED(BP_VAR_IS ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_fetch_static_prop_helper_SPEC_CV_UNUSED(int type ZEND_OPCODE_HANDLER_ARGS_DC)
+{
+ USE_OPLINE
+
+ zval *varname;
+ zval *retval;
+ zend_string *name;
+ zend_class_entry *ce;
+
+ SAVE_OPLINE();
+ varname = _get_zval_ptr_cv_undef(execute_data, opline->op1.var);
+
+ if (IS_CV == IS_CONST) {
+ name = Z_STR_P(varname);
+ } else if (EXPECTED(Z_TYPE_P(varname) == IS_STRING)) {
+ 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);
+ }
+
+ if (IS_UNUSED == IS_CONST) {
+ if (IS_CV == IS_CONST && EXPECTED((ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)))) != NULL)) {
+ retval = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)) + sizeof(void*));
+
+ /* check if static properties were destoyed */
+ if (UNEXPECTED(CE_STATIC_MEMBERS(ce) == NULL)) {
+ zend_throw_error(NULL, "Access to undeclared static property: %s::$%s", ZSTR_VAL(ce->name), ZSTR_VAL(name));
+
+ HANDLE_EXCEPTION();
+ }
+
+ goto fetch_static_prop_return;
+ } else if (UNEXPECTED((ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)))) == NULL)) {
+ ce = zend_fetch_class_by_name(Z_STR_P(EX_CONSTANT(opline->op2)), EX_CONSTANT(opline->op2) + 1, ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION);
+ if (UNEXPECTED(ce == NULL)) {
+ if (IS_CV != IS_CONST) {
+ zend_string_release(name);
+ }
+
+ ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
+ }
+ CACHE_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)), ce);
+ }
+ } else {
+ if (IS_UNUSED == IS_UNUSED) {
+ ce = zend_fetch_class(NULL, opline->op2.num);
+ if (UNEXPECTED(ce == NULL)) {
+ ZEND_ASSERT(EG(exception));
+ if (IS_CV != IS_CONST) {
+ zend_string_release(name);
+ }
+
+ HANDLE_EXCEPTION();
+ }
+ } else {
+ ce = Z_CE_P(EX_VAR(opline->op2.var));
+ }
+ if (IS_CV == IS_CONST &&
+ (retval = CACHED_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)), ce)) != NULL) {
+
+ /* check if static properties were destoyed */
+ if (UNEXPECTED(CE_STATIC_MEMBERS(ce) == NULL)) {
+ zend_throw_error(NULL, "Access to undeclared static property: %s::$%s", ZSTR_VAL(ce->name), ZSTR_VAL(name));
+
+ HANDLE_EXCEPTION();
+ }
+
+ goto fetch_static_prop_return;
+ }
+ }
+ retval = zend_std_get_static_property(ce, name, 0);
+ if (UNEXPECTED(EG(exception))) {
+ if (IS_CV != IS_CONST) {
+ zend_string_release(name);
+ }
+
+ HANDLE_EXCEPTION();
+ }
+ if (IS_CV == IS_CONST && retval) {
+ CACHE_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)), ce, retval);
+ }
+
+ if (IS_CV != IS_CONST) {
+ zend_string_release(name);
+ }
+
+fetch_static_prop_return:
+ ZEND_ASSERT(retval != NULL);
+ if (type == BP_VAR_R || type == BP_VAR_IS) {
+ if (/*type == BP_VAR_R &&*/ Z_ISREF_P(retval) && Z_REFCOUNT_P(retval) == 1) {
+ ZVAL_UNREF(retval);
+ }
+ ZVAL_COPY(EX_VAR(opline->result.var), retval);
+ } else {
+ ZVAL_INDIRECT(EX_VAR(opline->result.var), retval);
+ }
+ ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_STATIC_PROP_R_SPEC_CV_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ ZEND_VM_TAIL_CALL(zend_fetch_static_prop_helper_SPEC_CV_UNUSED(BP_VAR_R ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_STATIC_PROP_W_SPEC_CV_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ ZEND_VM_TAIL_CALL(zend_fetch_static_prop_helper_SPEC_CV_UNUSED(BP_VAR_W ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_STATIC_PROP_RW_SPEC_CV_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ ZEND_VM_TAIL_CALL(zend_fetch_static_prop_helper_SPEC_CV_UNUSED(BP_VAR_RW ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_STATIC_PROP_FUNC_ARG_SPEC_CV_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+
+ if (zend_is_by_ref_func_arg_fetch(opline, EX(call))) {
+ ZEND_VM_TAIL_CALL(zend_fetch_static_prop_helper_SPEC_CV_UNUSED(BP_VAR_W ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ } else {
+ ZEND_VM_TAIL_CALL(zend_fetch_static_prop_helper_SPEC_CV_UNUSED(BP_VAR_R ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_STATIC_PROP_UNSET_SPEC_CV_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ ZEND_VM_TAIL_CALL(zend_fetch_static_prop_helper_SPEC_CV_UNUSED(BP_VAR_UNSET ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_STATIC_PROP_IS_SPEC_CV_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ ZEND_VM_TAIL_CALL(zend_fetch_static_prop_helper_SPEC_CV_UNUSED(BP_VAR_IS ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+}
+
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_DIM_W_SPEC_CV_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
@@ -34516,14 +41771,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_DIM_W_SPEC_CV_UNUSED_HAN
SAVE_OPLINE();
container = _get_zval_ptr_cv_undef_BP_VAR_W(execute_data, opline->op1.var);
- if (IS_CV == IS_VAR && UNEXPECTED(container == NULL)) {
- zend_throw_error(NULL, "Cannot use string offset as an array");
- HANDLE_EXCEPTION();
- }
zend_fetch_dimension_address_W(EX_VAR(opline->result.var), container, NULL, IS_UNUSED);
if (IS_CV == IS_VAR && READY_TO_DESTROY(free_op1)) {
- EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var), 1);
+ EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var));
}
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
@@ -34536,16 +41787,12 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_DIM_RW_SPEC_CV_UNUSED_HA
zval *container;
SAVE_OPLINE();
- container = _get_zval_ptr_cv_BP_VAR_RW(execute_data, opline->op1.var);
+ container = _get_zval_ptr_cv_undef_BP_VAR_RW(execute_data, opline->op1.var);
- if (IS_CV == IS_VAR && UNEXPECTED(container == NULL)) {
- zend_throw_error(NULL, "Cannot use string offset as an array");
- HANDLE_EXCEPTION();
- }
zend_fetch_dimension_address_RW(EX_VAR(opline->result.var), container, NULL, IS_UNUSED);
if (IS_CV == IS_VAR && READY_TO_DESTROY(free_op1)) {
- EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var), 1);
+ EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var));
}
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
@@ -34560,21 +41807,16 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_DIM_FUNC_ARG_SPEC_CV_UNU
SAVE_OPLINE();
if (zend_is_by_ref_func_arg_fetch(opline, EX(call))) {
- if (IS_CV == IS_CONST || IS_CV == IS_TMP_VAR) {
+ if ((IS_CV & (IS_CONST|IS_TMP_VAR))) {
zend_throw_error(NULL, "Cannot use temporary expression in write context");
HANDLE_EXCEPTION();
}
container = _get_zval_ptr_cv_undef_BP_VAR_W(execute_data, opline->op1.var);
- if (IS_CV == IS_VAR && UNEXPECTED(container == NULL)) {
- zend_throw_error(NULL, "Cannot use string offset as an array");
-
- HANDLE_EXCEPTION();
- }
zend_fetch_dimension_address_W(EX_VAR(opline->result.var), container, NULL, IS_UNUSED);
if (IS_CV == IS_VAR && READY_TO_DESTROY(free_op1)) {
- EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var), 1);
+ EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var));
}
@@ -34585,7 +41827,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_DIM_FUNC_ARG_SPEC_CV_UNU
HANDLE_EXCEPTION();
}
- container = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var);
+ container = _get_zval_ptr_cv_undef(execute_data, opline->op1.var);
zend_fetch_dimension_address_read_R(EX_VAR(opline->result.var), container, NULL, IS_UNUSED);
@@ -34593,12 +41835,12 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_DIM_FUNC_ARG_SPEC_CV_UNU
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_CV_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_CV_UNUSED_OP_DATA_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
zval *object_ptr;
- zend_free_op free_op_data1;
+
zval *value;
zval *variable_ptr;
zval *dim;
@@ -34606,40 +41848,214 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_CV_UNUSED_HAND
SAVE_OPLINE();
object_ptr = _get_zval_ptr_cv_undef_BP_VAR_W(execute_data, opline->op1.var);
- if (IS_CV == IS_VAR && UNEXPECTED(object_ptr == NULL)) {
- zend_throw_error(NULL, "Cannot use string offset as an array");
- FREE_UNFETCHED_OP((opline+1)->op1_type, (opline+1)->op1.var);
+ if (EXPECTED(Z_TYPE_P(object_ptr) == IS_ARRAY)) {
+try_assign_dim_array:
+ SEPARATE_ARRAY(object_ptr);
+ if (IS_UNUSED == IS_UNUSED) {
+ variable_ptr = zend_hash_next_index_insert(Z_ARRVAL_P(object_ptr), &EG(uninitialized_zval));
+ if (UNEXPECTED(variable_ptr == NULL)) {
+ zend_error(E_WARNING, "Cannot add element to the array as the next element is already occupied");
+ goto assign_dim_error;
+ }
+ } else {
+ dim = NULL;
+ if (IS_UNUSED == IS_CONST) {
+ variable_ptr = zend_fetch_dimension_address_inner_W_CONST(Z_ARRVAL_P(object_ptr), dim);
+ } else {
+ variable_ptr = zend_fetch_dimension_address_inner_W(Z_ARRVAL_P(object_ptr), dim);
+ }
+ if (UNEXPECTED(variable_ptr == NULL)) {
+ goto assign_dim_error;
+ }
+ }
+ value = EX_CONSTANT((opline+1)->op1);
+ value = zend_assign_to_variable(variable_ptr, value, IS_CONST);
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+ } 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)) {
+ dim = NULL;
+ value = EX_CONSTANT((opline+1)->op1);
- HANDLE_EXCEPTION();
+ zend_assign_to_object_dim(object_ptr, dim, value);
+
+ if (UNEXPECTED(RETURN_VALUE_USED(opline)) && EXPECTED(!EG(exception))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+
+ } else if (EXPECTED(Z_TYPE_P(object_ptr) == IS_STRING)) {
+ if (IS_UNUSED == IS_UNUSED) {
+ zend_throw_error(NULL, "[] operator not supported for strings");
+
+
+ HANDLE_EXCEPTION();
+ } else {
+ dim = NULL;
+ value = EX_CONSTANT((opline+1)->op1);
+ zend_assign_to_string_offset(object_ptr, dim, value, (UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL));
+
+ }
+ } else if (EXPECTED(Z_TYPE_P(object_ptr) <= IS_FALSE)) {
+ 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 (IS_CV != IS_VAR || EXPECTED(!Z_ISERROR_P(object_ptr))) {
+ zend_error(E_WARNING, "Cannot use a scalar value as an array");
+ }
+ dim = NULL;
+assign_dim_error:
+
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+ }
}
+ if (IS_UNUSED != IS_UNUSED) {
+
+ }
+
+ /* assign_dim has two opcodes! */
+ ZEND_VM_NEXT_OPCODE_EX(1, 2);
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_CV_UNUSED_OP_DATA_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+
+ zval *object_ptr;
+ zend_free_op free_op_data;
+ zval *value;
+ zval *variable_ptr;
+ zval *dim;
+
+ SAVE_OPLINE();
+ object_ptr = _get_zval_ptr_cv_undef_BP_VAR_W(execute_data, opline->op1.var);
if (EXPECTED(Z_TYPE_P(object_ptr) == IS_ARRAY)) {
try_assign_dim_array:
+ SEPARATE_ARRAY(object_ptr);
if (IS_UNUSED == IS_UNUSED) {
- SEPARATE_ARRAY(object_ptr);
variable_ptr = zend_hash_next_index_insert(Z_ARRVAL_P(object_ptr), &EG(uninitialized_zval));
if (UNEXPECTED(variable_ptr == NULL)) {
zend_error(E_WARNING, "Cannot add element to the array as the next element is already occupied");
- variable_ptr = &EG(error_zval);
+ goto assign_dim_error;
}
} else {
dim = NULL;
- SEPARATE_ARRAY(object_ptr);
- variable_ptr = zend_fetch_dimension_address_inner(Z_ARRVAL_P(object_ptr), dim, IS_UNUSED, BP_VAR_W);
-
+ if (IS_UNUSED == IS_CONST) {
+ variable_ptr = zend_fetch_dimension_address_inner_W_CONST(Z_ARRVAL_P(object_ptr), dim);
+ } else {
+ variable_ptr = zend_fetch_dimension_address_inner_W(Z_ARRVAL_P(object_ptr), dim);
+ }
+ if (UNEXPECTED(variable_ptr == NULL)) {
+ goto assign_dim_error;
+ }
}
- value = get_zval_ptr_r((opline+1)->op1_type, (opline+1)->op1, execute_data, &free_op_data1);
- if (UNEXPECTED(variable_ptr == &EG(error_zval))) {
- FREE_OP(free_op_data1);
+ value = _get_zval_ptr_tmp((opline+1)->op1.var, execute_data, &free_op_data);
+ value = zend_assign_to_variable(variable_ptr, value, IS_TMP_VAR);
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+ } 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)) {
+ dim = NULL;
+ value = _get_zval_ptr_tmp((opline+1)->op1.var, execute_data, &free_op_data);
+
+ zend_assign_to_object_dim(object_ptr, dim, value);
+
+ if (UNEXPECTED(RETURN_VALUE_USED(opline)) && EXPECTED(!EG(exception))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+
+ zval_ptr_dtor_nogc(free_op_data);
+ } else if (EXPECTED(Z_TYPE_P(object_ptr) == IS_STRING)) {
+ if (IS_UNUSED == IS_UNUSED) {
+ zend_throw_error(NULL, "[] operator not supported for strings");
+ zval_ptr_dtor_nogc(EX_VAR((opline+1)->op1.var));
+
+ HANDLE_EXCEPTION();
+ } else {
+ dim = NULL;
+ value = _get_zval_ptr_tmp((opline+1)->op1.var, execute_data, &free_op_data);
+ zend_assign_to_string_offset(object_ptr, dim, value, (UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL));
+ zval_ptr_dtor_nogc(free_op_data);
+ }
+ } else if (EXPECTED(Z_TYPE_P(object_ptr) <= IS_FALSE)) {
+ 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 (IS_CV != IS_VAR || EXPECTED(!Z_ISERROR_P(object_ptr))) {
+ zend_error(E_WARNING, "Cannot use a scalar value as an array");
+ }
+ dim = NULL;
+assign_dim_error:
+ zval_ptr_dtor_nogc(EX_VAR((opline+1)->op1.var));
if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
ZVAL_NULL(EX_VAR(opline->result.var));
}
+ }
+ }
+ if (IS_UNUSED != IS_UNUSED) {
+
+ }
+
+ /* assign_dim has two opcodes! */
+ ZEND_VM_NEXT_OPCODE_EX(1, 2);
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_CV_UNUSED_OP_DATA_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+
+ zval *object_ptr;
+ zend_free_op free_op_data;
+ zval *value;
+ zval *variable_ptr;
+ zval *dim;
+
+ SAVE_OPLINE();
+ object_ptr = _get_zval_ptr_cv_undef_BP_VAR_W(execute_data, opline->op1.var);
+
+ if (EXPECTED(Z_TYPE_P(object_ptr) == IS_ARRAY)) {
+try_assign_dim_array:
+ SEPARATE_ARRAY(object_ptr);
+ if (IS_UNUSED == IS_UNUSED) {
+ variable_ptr = zend_hash_next_index_insert(Z_ARRVAL_P(object_ptr), &EG(uninitialized_zval));
+ if (UNEXPECTED(variable_ptr == NULL)) {
+ zend_error(E_WARNING, "Cannot add element to the array as the next element is already occupied");
+ goto assign_dim_error;
+ }
} else {
- value = zend_assign_to_variable(variable_ptr, value, (opline+1)->op1_type);
- if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
- ZVAL_COPY(EX_VAR(opline->result.var), value);
+ dim = NULL;
+ if (IS_UNUSED == IS_CONST) {
+ variable_ptr = zend_fetch_dimension_address_inner_W_CONST(Z_ARRVAL_P(object_ptr), dim);
+ } else {
+ variable_ptr = zend_fetch_dimension_address_inner_W(Z_ARRVAL_P(object_ptr), dim);
+ }
+ if (UNEXPECTED(variable_ptr == NULL)) {
+ goto assign_dim_error;
}
}
+ value = _get_zval_ptr_var((opline+1)->op1.var, execute_data, &free_op_data);
+ value = zend_assign_to_variable(variable_ptr, value, IS_VAR);
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
} else {
if (EXPECTED(Z_ISREF_P(object_ptr))) {
object_ptr = Z_REFVAL_P(object_ptr);
@@ -34648,52 +42064,138 @@ try_assign_dim_array:
}
}
if (EXPECTED(Z_TYPE_P(object_ptr) == IS_OBJECT)) {
+ dim = NULL;
+ value = _get_zval_ptr_var((opline+1)->op1.var, execute_data, &free_op_data);
- zval *property_name = NULL;
+ zend_assign_to_object_dim(object_ptr, dim, value);
- 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);
+ if (UNEXPECTED(RETURN_VALUE_USED(opline)) && EXPECTED(!EG(exception))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+ zval_ptr_dtor_nogc(free_op_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_throw_error(NULL, "[] operator not supported for strings");
- FREE_UNFETCHED_OP((opline+1)->op1_type, (opline+1)->op1.var);
+ if (IS_UNUSED == IS_UNUSED) {
+ zend_throw_error(NULL, "[] operator not supported for strings");
+ zval_ptr_dtor_nogc(EX_VAR((opline+1)->op1.var));
- HANDLE_EXCEPTION();
- } else {
- zend_long offset;
+ HANDLE_EXCEPTION();
+ } else {
+ dim = NULL;
+ value = _get_zval_ptr_var_deref((opline+1)->op1.var, execute_data, &free_op_data);
+ zend_assign_to_string_offset(object_ptr, dim, value, (UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL));
+ zval_ptr_dtor_nogc(free_op_data);
+ }
+ } else if (EXPECTED(Z_TYPE_P(object_ptr) <= IS_FALSE)) {
+ 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 (IS_CV != IS_VAR || EXPECTED(!Z_ISERROR_P(object_ptr))) {
+ zend_error(E_WARNING, "Cannot use a scalar value as an array");
+ }
+ dim = NULL;
+assign_dim_error:
+ zval_ptr_dtor_nogc(EX_VAR((opline+1)->op1.var));
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+ }
+ }
+ if (IS_UNUSED != IS_UNUSED) {
- dim = NULL;
- offset = zend_fetch_string_offset(object_ptr, dim, BP_VAR_W);
+ }
- value = get_zval_ptr_r_deref((opline+1)->op1_type, (opline+1)->op1, execute_data, &free_op_data1);
- 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);
- }
+ /* assign_dim has two opcodes! */
+ ZEND_VM_NEXT_OPCODE_EX(1, 2);
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_CV_UNUSED_OP_DATA_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+
+ zval *object_ptr;
+
+ zval *value;
+ zval *variable_ptr;
+ zval *dim;
+
+ SAVE_OPLINE();
+ object_ptr = _get_zval_ptr_cv_undef_BP_VAR_W(execute_data, opline->op1.var);
+
+ if (EXPECTED(Z_TYPE_P(object_ptr) == IS_ARRAY)) {
+try_assign_dim_array:
+ SEPARATE_ARRAY(object_ptr);
+ if (IS_UNUSED == IS_UNUSED) {
+ variable_ptr = zend_hash_next_index_insert(Z_ARRVAL_P(object_ptr), &EG(uninitialized_zval));
+ if (UNEXPECTED(variable_ptr == NULL)) {
+ zend_error(E_WARNING, "Cannot add element to the array as the next element is already occupied");
+ goto assign_dim_error;
+ }
+ } else {
+ dim = NULL;
+ if (IS_UNUSED == IS_CONST) {
+ variable_ptr = zend_fetch_dimension_address_inner_W_CONST(Z_ARRVAL_P(object_ptr), dim);
} 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);
+ variable_ptr = zend_fetch_dimension_address_inner_W(Z_ARRVAL_P(object_ptr), dim);
+ }
+ if (UNEXPECTED(variable_ptr == NULL)) {
+ goto assign_dim_error;
+ }
+ }
+ value = _get_zval_ptr_cv_BP_VAR_R(execute_data, (opline+1)->op1.var);
+ value = zend_assign_to_variable(variable_ptr, value, IS_CV);
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+ } 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;
}
- } 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;
+ }
+ if (EXPECTED(Z_TYPE_P(object_ptr) == IS_OBJECT)) {
+ dim = NULL;
+ value = _get_zval_ptr_cv_BP_VAR_R(execute_data, (opline+1)->op1.var);
+
+ zend_assign_to_object_dim(object_ptr, dim, value);
+
+ if (UNEXPECTED(RETURN_VALUE_USED(opline)) && EXPECTED(!EG(exception))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
}
- goto assign_dim_convert_to_array;
+
+ } else if (EXPECTED(Z_TYPE_P(object_ptr) == IS_STRING)) {
+ if (IS_UNUSED == IS_UNUSED) {
+ zend_throw_error(NULL, "[] operator not supported for strings");
+
+
+ HANDLE_EXCEPTION();
+ } else {
+ dim = NULL;
+ value = _get_zval_ptr_cv_deref_BP_VAR_R(execute_data, (opline+1)->op1.var);
+ zend_assign_to_string_offset(object_ptr, dim, value, (UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL));
+
+ }
+ } else if (EXPECTED(Z_TYPE_P(object_ptr) <= IS_FALSE)) {
+ 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 {
- zend_error(E_WARNING, "Cannot use a scalar value as an array");
-assign_dim_clean:
+ if (IS_CV != IS_VAR || EXPECTED(!Z_ISERROR_P(object_ptr))) {
+ zend_error(E_WARNING, "Cannot use a scalar value as an array");
+ }
dim = NULL;
+assign_dim_error:
- value = get_zval_ptr_r((opline+1)->op1_type, (opline+1)->op1, execute_data, &free_op_data1);
- FREE_OP(free_op_data1);
if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
ZVAL_NULL(EX_VAR(opline->result.var));
}
}
}
+ if (IS_UNUSED != IS_UNUSED) {
+
+ }
/* assign_dim has two opcodes! */
ZEND_VM_NEXT_OPCODE_EX(1, 2);
@@ -34747,8 +42249,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_VERIFY_RETURN_TYPE_SPEC_CV_UNU
if (UNEXPECTED(EG(exception) != NULL)) {
if (IS_CV == IS_CONST) {
zval_ptr_dtor_nogc(retval_ptr);
- } else {
-
}
}
#endif
@@ -34766,11 +42266,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ADD_ARRAY_ELEMENT_SPEC_CV_UNUS
if ((IS_CV == IS_VAR || IS_CV == IS_CV) &&
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_throw_error(NULL, "Cannot create references to/from string offsets");
- zend_array_destroy(Z_ARRVAL_P(EX_VAR(opline->result.var)));
- HANDLE_EXCEPTION();
- }
ZVAL_MAKE_REF(expr_ptr);
Z_ADDREF_P(expr_ptr);
@@ -34779,10 +42274,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ADD_ARRAY_ELEMENT_SPEC_CV_UNUS
if (IS_CV == IS_TMP_VAR) {
/* pass */
} else if (IS_CV == IS_CONST) {
- 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;
+ if (Z_REFCOUNTED_P(expr_ptr)) {
+ Z_ADDREF_P(expr_ptr);
}
} else if (IS_CV == IS_CV) {
ZVAL_DEREF(expr_ptr);
@@ -34895,7 +42388,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_UNSET_VAR_SPEC_CV_UNUSED_HANDL
SAVE_OPLINE();
if (IS_CV == IS_CV &&
- IS_UNUSED == IS_UNUSED &&
(opline->extended_value & ZEND_QUICK_SET)) {
zval *var = EX_VAR(opline->op1.var);
@@ -34904,7 +42396,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_UNSET_VAR_SPEC_CV_UNUSED_HANDL
if (!--GC_REFCOUNT(garbage)) {
ZVAL_UNDEF(var);
- zval_dtor_func_for_ptr(garbage);
+ zval_dtor_func(garbage);
} else {
zval *z = var;
ZVAL_DEREF(z);
@@ -34932,33 +42424,66 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_UNSET_VAR_SPEC_CV_UNUSED_HANDL
varname = &tmp;
}
- if (IS_UNUSED != IS_UNUSED) {
- zend_class_entry *ce;
+ target_symbol_table = zend_get_target_symbol_table(execute_data, opline->extended_value & ZEND_FETCH_TYPE_MASK);
+ zend_hash_del_ind(target_symbol_table, Z_STR_P(varname));
- if (IS_UNUSED == IS_CONST) {
- ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)));
- if (UNEXPECTED(ce == NULL)) {
- ce = zend_fetch_class_by_name(Z_STR_P(EX_CONSTANT(opline->op2)), EX_CONSTANT(opline->op2) + 1, ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION);
- if (UNEXPECTED(ce == NULL)) {
- if (EXPECTED(!EG(exception))) {
- zend_throw_error(NULL, "Class '%s' not found", Z_STRVAL_P(EX_CONSTANT(opline->op2)));
- }
- if (IS_CV != IS_CONST && Z_TYPE(tmp) != IS_UNDEF) {
- zend_string_release(Z_STR(tmp));
- }
+ if (IS_CV != IS_CONST && Z_TYPE(tmp) != IS_UNDEF) {
+ zend_string_release(Z_STR(tmp));
+ }
- HANDLE_EXCEPTION();
+ ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_UNSET_STATIC_PROP_SPEC_CV_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zval tmp, *varname;
+ zend_class_entry *ce;
+
+
+ SAVE_OPLINE();
+
+ 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;
+ }
+
+ if (IS_UNUSED == IS_CONST) {
+ ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)));
+ if (UNEXPECTED(ce == NULL)) {
+ ce = zend_fetch_class_by_name(Z_STR_P(EX_CONSTANT(opline->op2)), EX_CONSTANT(opline->op2) + 1, ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION);
+ if (UNEXPECTED(ce == NULL)) {
+ if (EXPECTED(!EG(exception))) {
+ zend_throw_error(NULL, "Class '%s' not found", Z_STRVAL_P(EX_CONSTANT(opline->op2)));
}
- CACHE_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)), ce);
+ if (IS_CV != IS_CONST && Z_TYPE(tmp) != IS_UNDEF) {
+ zend_string_release(Z_STR(tmp));
+ }
+
+ HANDLE_EXCEPTION();
}
- } else {
- ce = Z_CE_P(EX_VAR(opline->op2.var));
+ CACHE_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)), ce);
+ }
+ } else if (IS_UNUSED == IS_UNUSED) {
+ ce = zend_fetch_class(NULL, opline->op2.num);
+ if (UNEXPECTED(ce == NULL)) {
+ ZEND_ASSERT(EG(exception));
+ if (IS_CV != IS_CONST && Z_TYPE(tmp) != IS_UNDEF) {
+ zend_string_release(Z_STR(tmp));
+ }
+
+ HANDLE_EXCEPTION();
}
- zend_std_unset_static_property(ce, Z_STR_P(varname));
} else {
- target_symbol_table = zend_get_target_symbol_table(execute_data, opline->extended_value & ZEND_FETCH_TYPE_MASK);
- zend_hash_del_ind(target_symbol_table, Z_STR_P(varname));
+ ce = Z_CE_P(EX_VAR(opline->op2.var));
}
+ zend_std_unset_static_property(ce, Z_STR_P(varname));
if (IS_CV != IS_CONST && Z_TYPE(tmp) != IS_UNDEF) {
zend_string_release(Z_STR(tmp));
@@ -34974,7 +42499,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ISSET_ISEMPTY_VAR_SPEC_CV_UNUS
int result;
if (IS_CV == IS_CV &&
- IS_UNUSED == IS_UNUSED &&
(opline->extended_value & ZEND_QUICK_SET)) {
value = EX_VAR(opline->op1.var);
if (opline->extended_value & ZEND_ISSET) {
@@ -34995,6 +42519,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ISSET_ISEMPTY_VAR_SPEC_CV_UNUS
} else {
zval tmp, *varname;
+ HashTable *target_symbol_table;
SAVE_OPLINE();
varname = _get_zval_ptr_cv_BP_VAR_IS(execute_data, opline->op1.var);
@@ -35004,55 +42529,13 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ISSET_ISEMPTY_VAR_SPEC_CV_UNUS
varname = &tmp;
}
- if (IS_UNUSED != IS_UNUSED) {
- zend_class_entry *ce;
-
- if (IS_UNUSED == IS_CONST) {
- if (IS_CV == IS_CONST && EXPECTED((ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)))) != NULL)) {
- value = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)) + sizeof(void*));
-
- /* check if static properties were destoyed */
- if (UNEXPECTED(CE_STATIC_MEMBERS(ce) == NULL)) {
- value = NULL;
- }
-
- goto is_var_return;
- } else if (UNEXPECTED((ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)))) == NULL)) {
- ce = zend_fetch_class_by_name(Z_STR_P(EX_CONSTANT(opline->op2)), EX_CONSTANT(opline->op2) + 1, ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION);
- if (UNEXPECTED(ce == NULL)) {
- ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
- }
- CACHE_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)), ce);
- }
- } else {
- ce = Z_CE_P(EX_VAR(opline->op2.var));
- if (IS_CV == IS_CONST &&
- (value = CACHED_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)), ce)) != NULL) {
-
- /* check if static properties were destoyed */
- if (UNEXPECTED(CE_STATIC_MEMBERS(ce) == NULL)) {
- value = NULL;
- }
-
- goto is_var_return;
- }
- }
-
- value = zend_std_get_static_property(ce, Z_STR_P(varname), 1);
-
- if (IS_CV == IS_CONST && value) {
- CACHE_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)), ce, value);
- }
- } else {
- HashTable *target_symbol_table = zend_get_target_symbol_table(execute_data, opline->extended_value & ZEND_FETCH_TYPE_MASK);
- value = zend_hash_find_ind(target_symbol_table, Z_STR_P(varname));
- }
+ target_symbol_table = zend_get_target_symbol_table(execute_data, opline->extended_value & ZEND_FETCH_TYPE_MASK);
+ value = zend_hash_find_ind(target_symbol_table, Z_STR_P(varname));
if (IS_CV != IS_CONST && Z_TYPE(tmp) != IS_UNDEF) {
zend_string_release(Z_STR(tmp));
}
-is_var_return:
if (opline->extended_value & ZEND_ISSET) {
result = value && Z_TYPE_P(value) > IS_NULL &&
(!Z_ISREF_P(value) || Z_TYPE_P(Z_REFVAL_P(value)) != IS_NULL);
@@ -35066,6 +42549,140 @@ is_var_return:
}
}
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ISSET_ISEMPTY_STATIC_PROP_SPEC_CV_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zval *value;
+ int result;
+
+ zval tmp, *varname;
+ zend_class_entry *ce;
+
+ SAVE_OPLINE();
+ varname = _get_zval_ptr_cv_BP_VAR_IS(execute_data, opline->op1.var);
+ ZVAL_UNDEF(&tmp);
+ if (IS_CV != IS_CONST && Z_TYPE_P(varname) != IS_STRING) {
+ ZVAL_STR(&tmp, zval_get_string(varname));
+ varname = &tmp;
+ }
+
+ if (IS_UNUSED == IS_CONST) {
+ if (IS_CV == IS_CONST && EXPECTED((ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)))) != NULL)) {
+ value = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)) + sizeof(void*));
+
+ /* check if static properties were destoyed */
+ if (UNEXPECTED(CE_STATIC_MEMBERS(ce) == NULL)) {
+ value = NULL;
+ }
+
+ goto is_static_prop_return;
+ } else if (UNEXPECTED((ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)))) == NULL)) {
+ ce = zend_fetch_class_by_name(Z_STR_P(EX_CONSTANT(opline->op2)), EX_CONSTANT(opline->op2) + 1, ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION);
+ if (UNEXPECTED(ce == NULL)) {
+ ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
+ }
+ CACHE_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)), ce);
+ }
+ } else {
+ if (IS_UNUSED == IS_UNUSED) {
+ ce = zend_fetch_class(NULL, opline->op2.num);
+ if (UNEXPECTED(ce == NULL)) {
+ ZEND_ASSERT(EG(exception));
+ if (IS_CV != IS_CONST && Z_TYPE(tmp) != IS_UNDEF) {
+ zend_string_release(Z_STR(tmp));
+ }
+
+ HANDLE_EXCEPTION();
+ }
+ } else {
+ ce = Z_CE_P(EX_VAR(opline->op2.var));
+ }
+ if (IS_CV == IS_CONST &&
+ (value = CACHED_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)), ce)) != NULL) {
+
+ /* check if static properties were destoyed */
+ if (UNEXPECTED(CE_STATIC_MEMBERS(ce) == NULL)) {
+ value = NULL;
+ }
+
+ goto is_static_prop_return;
+ }
+ }
+
+ value = zend_std_get_static_property(ce, Z_STR_P(varname), 1);
+
+ if (IS_CV == IS_CONST && value) {
+ CACHE_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)), ce, value);
+ }
+
+ if (IS_CV != IS_CONST && Z_TYPE(tmp) != IS_UNDEF) {
+ zend_string_release(Z_STR(tmp));
+ }
+
+is_static_prop_return:
+ if (opline->extended_value & ZEND_ISSET) {
+ result = value && Z_TYPE_P(value) > IS_NULL &&
+ (!Z_ISREF_P(value) || Z_TYPE_P(Z_REFVAL_P(value)) != IS_NULL);
+ } else /* if (opline->extended_value & ZEND_ISEMPTY) */ {
+ result = !value || !i_zend_is_true(value);
+ }
+
+ ZEND_VM_SMART_BRANCH(result, 1);
+ ZVAL_BOOL(EX_VAR(opline->result.var), result);
+ ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INSTANCEOF_SPEC_CV_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+
+ zval *expr;
+ zend_bool result;
+
+ SAVE_OPLINE();
+ expr = _get_zval_ptr_cv_undef(execute_data, opline->op1.var);
+
+try_instanceof:
+ if (Z_TYPE_P(expr) == IS_OBJECT) {
+ zend_class_entry *ce;
+
+ if (IS_UNUSED == IS_CONST) {
+ ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)));
+ if (UNEXPECTED(ce == NULL)) {
+ ce = zend_fetch_class_by_name(Z_STR_P(EX_CONSTANT(opline->op2)), EX_CONSTANT(opline->op2) + 1, ZEND_FETCH_CLASS_NO_AUTOLOAD);
+ if (UNEXPECTED(ce == NULL)) {
+ ZVAL_FALSE(EX_VAR(opline->result.var));
+
+ ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
+ }
+ CACHE_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)), ce);
+ }
+ } else if (IS_UNUSED == IS_UNUSED) {
+ ce = zend_fetch_class(NULL, opline->op2.num);
+ if (UNEXPECTED(ce == NULL)) {
+ ZEND_ASSERT(EG(exception));
+
+ HANDLE_EXCEPTION();
+ }
+ } else {
+ ce = Z_CE_P(EX_VAR(opline->op2.var));
+ }
+ result = ce && instanceof_function(Z_OBJCE_P(expr), ce);
+ } else if ((IS_CV & (IS_VAR|IS_CV)) && Z_TYPE_P(expr) == IS_REFERENCE) {
+ 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;
+ }
+
+ ZEND_VM_SMART_BRANCH(result, 1);
+ ZVAL_BOOL(EX_VAR(opline->result.var), result);
+ ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
+}
+
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_CV_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
@@ -35093,7 +42710,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_CV_UNUSED_HANDLER(Z
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) {
+ if (IS_CV & (IS_CONST|IS_TMP_VAR)) {
zval *value;
zend_error(E_NOTICE, "Only variable references should be yielded by reference");
@@ -35101,25 +42718,19 @@ 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 (IS_CV == IS_CONST) {
- if (UNEXPECTED(Z_OPT_COPYABLE(generator->value))) {
- zval_copy_ctor_func(&generator->value);
+ if (UNEXPECTED(Z_OPT_REFCOUNTED(generator->value))) {
+ Z_ADDREF(generator->value);
}
}
} else {
zval *value_ptr = _get_zval_ptr_cv_BP_VAR_W(execute_data, opline->op1.var);
- if (IS_CV == IS_VAR && UNEXPECTED(value_ptr == NULL)) {
- zend_throw_error(NULL, "Cannot yield string offsets by reference");
-
- HANDLE_EXCEPTION();
- }
-
/* If a function call result is yielded and the function did
* not return by reference we throw a notice. */
if (IS_CV == IS_VAR &&
(value_ptr == &EG(uninitialized_zval) ||
(opline->extended_value == ZEND_RETURNS_FUNCTION &&
- !(Z_VAR_FLAGS_P(value_ptr) & IS_VAR_RET_REF)))) {
+ !Z_ISREF_P(value_ptr)))) {
zend_error(E_NOTICE, "Only variable references should be yielded by reference");
} else {
ZVAL_MAKE_REF(value_ptr);
@@ -35133,8 +42744,8 @@ 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_COPY_VALUE(&generator->value, value);
- if (UNEXPECTED(Z_OPT_COPYABLE(generator->value))) {
- zval_copy_ctor_func(&generator->value);
+ if (UNEXPECTED(Z_OPT_REFCOUNTED(generator->value))) {
+ Z_ADDREF(generator->value);
}
} else if (IS_CV == IS_TMP_VAR) {
ZVAL_COPY_VALUE(&generator->value, value);
@@ -35161,8 +42772,8 @@ 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_COPY_VALUE(&generator->key, key);
- if (UNEXPECTED(Z_OPT_COPYABLE(generator->key))) {
- zval_copy_ctor_func(&generator->key);
+ if (UNEXPECTED(Z_OPT_REFCOUNTED(generator->key))) {
+ Z_ADDREF(generator->key);
}
} else if (IS_UNUSED == IS_TMP_VAR) {
ZVAL_COPY_VALUE(&generator->key, key);
@@ -35472,7 +43083,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CONCAT_SPEC_CV_CV_HANDLER(ZEND
!ZSTR_IS_INTERNED(op1_str) && GC_REFCOUNT(op1_str) == 1) {
size_t len = ZSTR_LEN(op1_str);
- str = zend_string_realloc(op1_str, len + ZSTR_LEN(op2_str), 0);
+ str = zend_string_extend(op1_str, len + ZSTR_LEN(op2_str), 0);
memcpy(ZSTR_VAL(str) + len, ZSTR_VAL(op2_str), ZSTR_LEN(op2_str)+1);
ZVAL_NEW_STR(EX_VAR(opline->result.var), str);
break;
@@ -35856,7 +43467,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_binary_assign_op_obj_helper_SP
SAVE_OPLINE();
object = _get_zval_ptr_cv_BP_VAR_RW(execute_data, opline->op1.var);
- if (IS_CV == IS_UNUSED && UNEXPECTED(Z_OBJ_P(object) == NULL)) {
+ if (IS_CV == IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
FREE_UNFETCHED_OP((opline+1)->op1_type, (opline+1)->op1.var);
@@ -35865,13 +43476,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_binary_assign_op_obj_helper_SP
property = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var);
- if (IS_CV == IS_VAR && UNEXPECTED(object == NULL)) {
- zend_throw_error(NULL, "Cannot use string offset as an object");
- FREE_UNFETCHED_OP((opline+1)->op1_type, (opline+1)->op1.var);
-
- HANDLE_EXCEPTION();
- }
-
do {
value = get_zval_ptr_r((opline+1)->op1_type, (opline+1)->op1, execute_data, &free_op_data1);
@@ -35889,7 +43493,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_binary_assign_op_obj_helper_SP
/* here we are sure we are dealing with an object */
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)) {
- if (UNEXPECTED(zptr == &EG(error_zval))) {
+ if (UNEXPECTED(Z_ISERROR_P(zptr))) {
if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
ZVAL_NULL(EX_VAR(opline->result.var));
}
@@ -35918,66 +43522,85 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_binary_assign_op_dim_helper_SP
{
USE_OPLINE
zend_free_op free_op_data1;
- zval *var_ptr, rv;
+ zval *var_ptr;
zval *value, *container, *dim;
SAVE_OPLINE();
- container = _get_zval_ptr_cv_BP_VAR_RW(execute_data, opline->op1.var);
- if (IS_CV == IS_UNUSED && UNEXPECTED(Z_OBJ_P(container) == NULL)) {
- zend_throw_error(NULL, "Using $this when not in object context");
- FREE_UNFETCHED_OP((opline+1)->op1_type, (opline+1)->op1.var);
+ container = _get_zval_ptr_cv_undef_BP_VAR_RW(execute_data, opline->op1.var);
- HANDLE_EXCEPTION();
- }
- if (IS_CV == IS_VAR && UNEXPECTED(container == NULL)) {
- zend_throw_error(NULL, "Cannot use string offset as an array");
- FREE_UNFETCHED_OP((opline+1)->op1_type, (opline+1)->op1.var);
-
- HANDLE_EXCEPTION();
- }
-
- dim = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var);
+ if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) {
+assign_dim_op_array:
+ SEPARATE_ARRAY(container);
+assign_dim_op_new_array:
+ if (IS_CV == IS_UNUSED) {
+ var_ptr = zend_hash_next_index_insert(Z_ARRVAL_P(container), &EG(uninitialized_zval));
+ if (UNEXPECTED(!var_ptr)) {
+ zend_error(E_WARNING, "Cannot add element to the array as the next element is already occupied");
+ goto assign_dim_op_ret_null;
+ }
+ } else {
+ dim = _get_zval_ptr_cv_undef(execute_data, opline->op2.var);
- do {
- if (IS_CV == IS_UNUSED || UNEXPECTED(Z_TYPE_P(container) != IS_ARRAY)) {
- if (IS_CV != IS_UNUSED) {
- ZVAL_DEREF(container);
+ if (IS_CV == IS_CONST) {
+ var_ptr = zend_fetch_dimension_address_inner_RW_CONST(Z_ARRVAL_P(container), dim);
+ } else {
+ var_ptr = zend_fetch_dimension_address_inner_RW(Z_ARRVAL_P(container), dim);
}
- if (IS_CV == IS_UNUSED || EXPECTED(Z_TYPE_P(container) == IS_OBJECT)) {
- value = get_zval_ptr_r((opline+1)->op1_type, (opline+1)->op1, execute_data, &free_op_data1);
- zend_binary_assign_op_obj_dim(container, dim, value, UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL, binary_op);
- break;
+ if (UNEXPECTED(!var_ptr)) {
+ goto assign_dim_op_ret_null;
}
+ ZVAL_DEREF(var_ptr);
+ SEPARATE_ZVAL_NOREF(var_ptr);
}
- zend_fetch_dimension_address_RW(&rv, container, dim, IS_CV);
value = get_zval_ptr_r((opline+1)->op1_type, (opline+1)->op1, execute_data, &free_op_data1);
- ZEND_ASSERT(Z_TYPE(rv) == IS_INDIRECT);
- var_ptr = Z_INDIRECT(rv);
- if (UNEXPECTED(var_ptr == NULL)) {
- zend_throw_error(NULL, "Cannot use assign-op operators with overloaded objects nor string offsets");
-
- FREE_OP(free_op_data1);
+ binary_op(var_ptr, var_ptr, value);
- HANDLE_EXCEPTION();
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), var_ptr);
}
-
- if (UNEXPECTED(var_ptr == &EG(error_zval))) {
- if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
- ZVAL_NULL(EX_VAR(opline->result.var));
+ } else {
+ if (EXPECTED(Z_ISREF_P(container))) {
+ container = Z_REFVAL_P(container);
+ if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) {
+ goto assign_dim_op_array;
}
- } else {
- ZVAL_DEREF(var_ptr);
- SEPARATE_ZVAL_NOREF(var_ptr);
+ } else if (IS_CV == IS_CV && UNEXPECTED(Z_TYPE_INFO_P(container) == IS_UNDEF)) {
+ container = GET_OP1_UNDEF_CV(container, BP_VAR_RW);
+assign_dim_op_convert_to_array:
+ ZVAL_NEW_ARR(container);
+ zend_hash_init(Z_ARRVAL_P(container), 8, NULL, ZVAL_PTR_DTOR, 0);
+ goto assign_dim_op_new_array;
+ }
- binary_op(var_ptr, var_ptr, value);
+ dim = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var);
- if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
- ZVAL_COPY(EX_VAR(opline->result.var), var_ptr);
+ if (EXPECTED(Z_TYPE_P(container) == IS_OBJECT)) {
+ value = get_zval_ptr_r((opline+1)->op1_type, (opline+1)->op1, execute_data, &free_op_data1);
+ zend_binary_assign_op_obj_dim(container, dim, value, UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL, binary_op);
+ } else {
+ if (UNEXPECTED(Z_TYPE_P(container) == IS_STRING)) {
+ if (IS_CV == IS_UNUSED) {
+ zend_throw_error(NULL, "[] operator not supported for strings");
+ } else {
+ zend_check_string_offset(dim, BP_VAR_RW);
+ zend_wrong_string_offset();
+ }
+ } else if (EXPECTED(Z_TYPE_P(container) <= IS_FALSE)) {
+ goto assign_dim_op_convert_to_array;
+ } else {
+ if (UNEXPECTED(IS_CV != IS_VAR || EXPECTED(!Z_ISERROR_P(container)))) {
+ zend_error(E_WARNING, "Cannot use a scalar value as an array");
+ }
+assign_dim_op_ret_null:
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
}
+ value = get_zval_ptr_r((opline+1)->op1_type, (opline+1)->op1, execute_data, &free_op_data1);
}
- } while (0);
+ }
FREE_OP(free_op_data1);
@@ -35995,13 +43618,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_binary_assign_op_helper_SPEC_C
value = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var);
var_ptr = _get_zval_ptr_cv_BP_VAR_RW(execute_data, opline->op1.var);
- if (IS_CV == IS_VAR && UNEXPECTED(var_ptr == NULL)) {
- zend_throw_error(NULL, "Cannot use assign-op operators with overloaded objects nor string offsets");
-
- HANDLE_EXCEPTION();
- }
-
- if (IS_CV == IS_VAR && UNEXPECTED(var_ptr == &EG(error_zval))) {
+ if (IS_CV == IS_VAR && UNEXPECTED(Z_ISERROR_P(var_ptr))) {
if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
ZVAL_NULL(EX_VAR(opline->result.var));
}
@@ -36026,15 +43643,52 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_ADD_SPEC_CV_CV_HANDLER(
USE_OPLINE
# if 0 || (IS_CV != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
+ if (EXPECTED(1)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_CV_CV(add_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CV(add_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_CV(add_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CV(add_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_ADD_SPEC_CV_CV_DIM_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || (IS_CV != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_CV != IS_UNUSED)
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_CV_CV(add_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(1)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CV(add_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_CV(add_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_CV(add_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CV(add_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_ADD_SPEC_CV_CV_OBJ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || (IS_CV != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_CV != IS_UNUSED)
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_CV_CV(add_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CV(add_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_CV(add_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#else
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CV(add_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#endif
@@ -36046,15 +43700,52 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_SUB_SPEC_CV_CV_HANDLER(
USE_OPLINE
# if 0 || (IS_CV != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
+ if (EXPECTED(1)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_CV_CV(sub_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CV(sub_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_CV(sub_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CV(sub_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_SUB_SPEC_CV_CV_DIM_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || (IS_CV != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_CV != IS_UNUSED)
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_CV_CV(sub_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(1)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CV(sub_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_CV(sub_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CV(sub_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_SUB_SPEC_CV_CV_OBJ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || (IS_CV != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_CV != IS_UNUSED)
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_CV_CV(sub_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CV(sub_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_CV(sub_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_CV(sub_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#else
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CV(sub_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#endif
@@ -36066,15 +43757,52 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_MUL_SPEC_CV_CV_HANDLER(
USE_OPLINE
# if 0 || (IS_CV != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
+ if (EXPECTED(1)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_CV_CV(mul_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CV(mul_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_CV(mul_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CV(mul_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_MUL_SPEC_CV_CV_DIM_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || (IS_CV != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_CV != IS_UNUSED)
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_CV_CV(mul_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+ if (EXPECTED(1)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CV(mul_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_CV(mul_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CV(mul_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_MUL_SPEC_CV_CV_OBJ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || (IS_CV != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_CV != IS_UNUSED)
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_CV_CV(mul_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CV(mul_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_CV(mul_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_CV(mul_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#else
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CV(mul_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#endif
@@ -36086,15 +43814,52 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIV_SPEC_CV_CV_HANDLER(
USE_OPLINE
# if 0 || (IS_CV != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
+ if (EXPECTED(1)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_CV_CV(div_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CV(div_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_CV(div_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CV(div_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIV_SPEC_CV_CV_DIM_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || (IS_CV != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_CV != IS_UNUSED)
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_CV_CV(div_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(1)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CV(div_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_CV(div_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CV(div_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIV_SPEC_CV_CV_OBJ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || (IS_CV != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_CV != IS_UNUSED)
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_CV_CV(div_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CV(div_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_CV(div_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_CV(div_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#else
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CV(div_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#endif
@@ -36106,15 +43871,52 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_MOD_SPEC_CV_CV_HANDLER(
USE_OPLINE
# if 0 || (IS_CV != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
+ if (EXPECTED(1)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_CV_CV(mod_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CV(mod_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_CV(mod_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CV(mod_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_MOD_SPEC_CV_CV_DIM_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || (IS_CV != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_CV != IS_UNUSED)
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_CV_CV(mod_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+ if (EXPECTED(1)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CV(mod_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_CV(mod_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CV(mod_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_MOD_SPEC_CV_CV_OBJ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || (IS_CV != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_CV != IS_UNUSED)
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_CV_CV(mod_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CV(mod_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_CV(mod_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_CV(mod_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#else
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CV(mod_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#endif
@@ -36126,15 +43928,52 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_SL_SPEC_CV_CV_HANDLER(Z
USE_OPLINE
# if 0 || (IS_CV != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
+ if (EXPECTED(1)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_CV_CV(shift_left_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CV(shift_left_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_CV(shift_left_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CV(shift_left_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_SL_SPEC_CV_CV_DIM_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || (IS_CV != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_CV != IS_UNUSED)
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_CV_CV(shift_left_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(1)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CV(shift_left_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_CV(shift_left_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_CV(shift_left_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CV(shift_left_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_SL_SPEC_CV_CV_OBJ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || (IS_CV != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_CV != IS_UNUSED)
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_CV_CV(shift_left_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CV(shift_left_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_CV(shift_left_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#else
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CV(shift_left_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#endif
@@ -36146,15 +43985,52 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_SR_SPEC_CV_CV_HANDLER(Z
USE_OPLINE
# if 0 || (IS_CV != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
+ if (EXPECTED(1)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_CV_CV(shift_right_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CV(shift_right_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_CV(shift_right_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CV(shift_right_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_SR_SPEC_CV_CV_DIM_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || (IS_CV != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_CV != IS_UNUSED)
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_CV_CV(shift_right_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+ if (EXPECTED(1)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CV(shift_right_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_CV(shift_right_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CV(shift_right_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_SR_SPEC_CV_CV_OBJ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || (IS_CV != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_CV != IS_UNUSED)
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_CV_CV(shift_right_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CV(shift_right_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_CV(shift_right_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_CV(shift_right_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#else
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CV(shift_right_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#endif
@@ -36166,15 +44042,52 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_CONCAT_SPEC_CV_CV_HANDL
USE_OPLINE
# if 0 || (IS_CV != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
+ if (EXPECTED(1)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_CV_CV(concat_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CV(concat_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_CV(concat_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CV(concat_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_CONCAT_SPEC_CV_CV_DIM_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || (IS_CV != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_CV != IS_UNUSED)
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_CV_CV(concat_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(1)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CV(concat_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_CV(concat_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_CV(concat_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CV(concat_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_CONCAT_SPEC_CV_CV_OBJ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || (IS_CV != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_CV != IS_UNUSED)
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_CV_CV(concat_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CV(concat_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_CV(concat_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#else
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CV(concat_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#endif
@@ -36186,15 +44099,52 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_BW_OR_SPEC_CV_CV_HANDLE
USE_OPLINE
# if 0 || (IS_CV != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
+ if (EXPECTED(1)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_CV_CV(bitwise_or_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CV(bitwise_or_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_CV(bitwise_or_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CV(bitwise_or_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_BW_OR_SPEC_CV_CV_DIM_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || (IS_CV != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_CV != IS_UNUSED)
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_CV_CV(bitwise_or_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(1)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CV(bitwise_or_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_CV(bitwise_or_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_CV(bitwise_or_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CV(bitwise_or_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_BW_OR_SPEC_CV_CV_OBJ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || (IS_CV != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_CV != IS_UNUSED)
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_CV_CV(bitwise_or_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CV(bitwise_or_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_CV(bitwise_or_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#else
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CV(bitwise_or_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#endif
@@ -36206,15 +44156,52 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_BW_AND_SPEC_CV_CV_HANDL
USE_OPLINE
# if 0 || (IS_CV != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
+ if (EXPECTED(1)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_CV_CV(bitwise_and_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CV(bitwise_and_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_CV(bitwise_and_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CV(bitwise_and_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_BW_AND_SPEC_CV_CV_DIM_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || (IS_CV != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_CV != IS_UNUSED)
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_CV_CV(bitwise_and_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(1)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CV(bitwise_and_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_CV(bitwise_and_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_CV(bitwise_and_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CV(bitwise_and_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_BW_AND_SPEC_CV_CV_OBJ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || (IS_CV != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_CV != IS_UNUSED)
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_CV_CV(bitwise_and_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CV(bitwise_and_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_CV(bitwise_and_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#else
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CV(bitwise_and_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#endif
@@ -36226,15 +44213,52 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_BW_XOR_SPEC_CV_CV_HANDL
USE_OPLINE
# if 0 || (IS_CV != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
+ if (EXPECTED(1)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_CV_CV(bitwise_xor_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CV(bitwise_xor_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_CV(bitwise_xor_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CV(bitwise_xor_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_BW_XOR_SPEC_CV_CV_DIM_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || (IS_CV != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_CV != IS_UNUSED)
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_CV_CV(bitwise_xor_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+ if (EXPECTED(1)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CV(bitwise_xor_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_CV(bitwise_xor_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CV(bitwise_xor_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_BW_XOR_SPEC_CV_CV_OBJ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || (IS_CV != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_CV != IS_UNUSED)
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_CV_CV(bitwise_xor_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CV(bitwise_xor_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_CV(bitwise_xor_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_CV(bitwise_xor_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#else
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CV(bitwise_xor_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#endif
@@ -36246,15 +44270,52 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_POW_SPEC_CV_CV_HANDLER(
USE_OPLINE
# if 0 || (IS_CV != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
+ if (EXPECTED(1)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_CV_CV(pow_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CV(pow_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_CV(pow_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CV(pow_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_POW_SPEC_CV_CV_DIM_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || (IS_CV != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_CV != IS_UNUSED)
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_CV_CV(pow_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(1)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CV(pow_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_CV(pow_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_CV(pow_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CV(pow_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_POW_SPEC_CV_CV_OBJ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || (IS_CV != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_CV != IS_UNUSED)
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_CV_CV(pow_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CV(pow_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_CV(pow_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#else
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_CV(pow_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#endif
@@ -36271,7 +44332,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_pre_incdec_property_helper_SPE
SAVE_OPLINE();
object = _get_zval_ptr_cv_BP_VAR_RW(execute_data, opline->op1.var);
- if (IS_CV == IS_UNUSED && UNEXPECTED(Z_OBJ_P(object) == NULL)) {
+ if (IS_CV == IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
HANDLE_EXCEPTION();
@@ -36279,12 +44340,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_pre_incdec_property_helper_SPE
property = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var);
- if (IS_CV == IS_VAR && UNEXPECTED(object == NULL)) {
- zend_throw_error(NULL, "Cannot increment/decrement overloaded objects nor string offsets");
-
- HANDLE_EXCEPTION();
- }
-
do {
if (IS_CV != IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) != IS_OBJECT)) {
ZVAL_DEREF(object);
@@ -36300,7 +44355,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_pre_incdec_property_helper_SPE
/* here we are sure we are dealing with an object */
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)) {
- if (UNEXPECTED(zptr == &EG(error_zval))) {
+ if (UNEXPECTED(Z_ISERROR_P(zptr))) {
if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
ZVAL_NULL(EX_VAR(opline->result.var));
}
@@ -36355,7 +44410,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_post_incdec_property_helper_SP
SAVE_OPLINE();
object = _get_zval_ptr_cv_BP_VAR_RW(execute_data, opline->op1.var);
- if (IS_CV == IS_UNUSED && UNEXPECTED(Z_OBJ_P(object) == NULL)) {
+ if (IS_CV == IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
HANDLE_EXCEPTION();
@@ -36363,12 +44418,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_post_incdec_property_helper_SP
property = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var);
- if (IS_CV == IS_VAR && UNEXPECTED(object == NULL)) {
- zend_throw_error(NULL, "Cannot increment/decrement overloaded objects nor string offsets");
-
- HANDLE_EXCEPTION();
- }
-
do {
if (IS_CV != IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) != IS_OBJECT)) {
ZVAL_DEREF(object);
@@ -36383,7 +44432,7 @@ 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)) {
- if (UNEXPECTED(zptr == &EG(error_zval))) {
+ if (UNEXPECTED(Z_ISERROR_P(zptr))) {
ZVAL_NULL(EX_VAR(opline->result.var));
} else {
if (EXPECTED(Z_TYPE_P(zptr) == IS_LONG)) {
@@ -36427,11 +44476,33 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_DIM_R_SPEC_CV_CV_HANDLER
{
USE_OPLINE
- zval *container;
+ zval *container, *dim, *value, *result;
SAVE_OPLINE();
- container = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var);
- zend_fetch_dimension_address_read_R(EX_VAR(opline->result.var), container, _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var), IS_CV);
+ container = _get_zval_ptr_cv_undef(execute_data, opline->op1.var);
+ dim = _get_zval_ptr_cv_undef(execute_data, opline->op2.var);
+ if (IS_CV != IS_CONST) {
+ if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) {
+fetch_dim_r_array:
+ value = zend_fetch_dimension_address_inner(Z_ARRVAL_P(container), dim, IS_CV, BP_VAR_R);
+ result = EX_VAR(opline->result.var);
+ ZVAL_COPY(result, value);
+ } else if (EXPECTED(Z_TYPE_P(container) == IS_REFERENCE)) {
+ container = Z_REFVAL_P(container);
+ if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) {
+ goto fetch_dim_r_array;
+ } else {
+ goto fetch_dim_r_slow;
+ }
+ } else {
+fetch_dim_r_slow:
+ result = EX_VAR(opline->result.var);
+ zend_fetch_dimension_address_read_R_slow(result, container, dim);
+ }
+ } else {
+ result = EX_VAR(opline->result.var);
+ zend_fetch_dimension_address_read_R(result, container, dim, IS_CV);
+ }
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
@@ -36446,14 +44517,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_DIM_W_SPEC_CV_CV_HANDLER
SAVE_OPLINE();
container = _get_zval_ptr_cv_undef_BP_VAR_W(execute_data, opline->op1.var);
- if (IS_CV == IS_VAR && UNEXPECTED(container == NULL)) {
- zend_throw_error(NULL, "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);
+ zend_fetch_dimension_address_W(EX_VAR(opline->result.var), container, _get_zval_ptr_cv_undef(execute_data, opline->op2.var), IS_CV);
if (IS_CV == IS_VAR && READY_TO_DESTROY(free_op1)) {
- EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var), 1);
+ EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var));
}
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
@@ -36466,16 +44533,12 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_DIM_RW_SPEC_CV_CV_HANDLE
zval *container;
SAVE_OPLINE();
- container = _get_zval_ptr_cv_BP_VAR_RW(execute_data, opline->op1.var);
+ container = _get_zval_ptr_cv_undef_BP_VAR_RW(execute_data, opline->op1.var);
- if (IS_CV == IS_VAR && UNEXPECTED(container == NULL)) {
- zend_throw_error(NULL, "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);
+ zend_fetch_dimension_address_RW(EX_VAR(opline->result.var), container, _get_zval_ptr_cv_undef(execute_data, opline->op2.var), IS_CV);
if (IS_CV == IS_VAR && READY_TO_DESTROY(free_op1)) {
- EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var), 1);
+ EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var));
}
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
@@ -36488,8 +44551,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_DIM_IS_SPEC_CV_CV_HANDLE
zval *container;
SAVE_OPLINE();
- container = _get_zval_ptr_cv_BP_VAR_IS(execute_data, opline->op1.var);
- zend_fetch_dimension_address_read_IS(EX_VAR(opline->result.var), container, _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var), IS_CV);
+ container = _get_zval_ptr_cv_undef(execute_data, opline->op1.var);
+ zend_fetch_dimension_address_read_IS(EX_VAR(opline->result.var), container, _get_zval_ptr_cv_undef(execute_data, opline->op2.var), IS_CV);
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
@@ -36504,21 +44567,16 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_DIM_FUNC_ARG_SPEC_CV_CV_
SAVE_OPLINE();
if (zend_is_by_ref_func_arg_fetch(opline, EX(call))) {
- if (IS_CV == IS_CONST || IS_CV == IS_TMP_VAR) {
+ if ((IS_CV & (IS_CONST|IS_TMP_VAR))) {
zend_throw_error(NULL, "Cannot use temporary expression in write context");
HANDLE_EXCEPTION();
}
container = _get_zval_ptr_cv_undef_BP_VAR_W(execute_data, opline->op1.var);
- if (IS_CV == IS_VAR && UNEXPECTED(container == NULL)) {
- zend_throw_error(NULL, "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);
+ zend_fetch_dimension_address_W(EX_VAR(opline->result.var), container, _get_zval_ptr_cv_undef(execute_data, opline->op2.var), IS_CV);
if (IS_CV == IS_VAR && READY_TO_DESTROY(free_op1)) {
- EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var), 1);
+ EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var));
}
@@ -36529,8 +44587,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_DIM_FUNC_ARG_SPEC_CV_CV_
HANDLE_EXCEPTION();
}
- container = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var);
- zend_fetch_dimension_address_read_R(EX_VAR(opline->result.var), container, _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var), IS_CV);
+ container = _get_zval_ptr_cv_undef(execute_data, opline->op1.var);
+ zend_fetch_dimension_address_read_R(EX_VAR(opline->result.var), container, _get_zval_ptr_cv_undef(execute_data, opline->op2.var), IS_CV);
}
@@ -36544,17 +44602,12 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_DIM_UNSET_SPEC_CV_CV_HAN
zval *container;
SAVE_OPLINE();
- container = _get_zval_ptr_cv_BP_VAR_UNSET(execute_data, opline->op1.var);
+ container = _get_zval_ptr_cv_undef_BP_VAR_UNSET(execute_data, opline->op1.var);
- if (IS_CV == IS_VAR && UNEXPECTED(container == NULL)) {
- zend_throw_error(NULL, "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);
+ zend_fetch_dimension_address_UNSET(EX_VAR(opline->result.var), container, _get_zval_ptr_cv_undef(execute_data, opline->op2.var), IS_CV);
if (IS_CV == IS_VAR && READY_TO_DESTROY(free_op1)) {
- EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var), 1);
+ EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var));
}
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
@@ -36571,7 +44624,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_R_SPEC_CV_CV_HANDLER
SAVE_OPLINE();
container = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var);
- if (IS_CV == IS_UNUSED && UNEXPECTED(Z_OBJ_P(container) == NULL)) {
+ if (IS_CV == IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
HANDLE_EXCEPTION();
@@ -36643,21 +44696,16 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_W_SPEC_CV_CV_HANDLER
property = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var);
container = _get_zval_ptr_cv_undef_BP_VAR_W(execute_data, opline->op1.var);
- if (IS_CV == IS_UNUSED && UNEXPECTED(Z_OBJ_P(container) == NULL)) {
+ if (IS_CV == IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
HANDLE_EXCEPTION();
}
- if (IS_CV == IS_VAR && UNEXPECTED(container == NULL)) {
- zend_throw_error(NULL, "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);
if (IS_CV == IS_VAR && READY_TO_DESTROY(free_op1)) {
- EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var), 0);
+ EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var));
}
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
@@ -36674,20 +44722,15 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_RW_SPEC_CV_CV_HANDLE
property = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var);
container = _get_zval_ptr_cv_BP_VAR_RW(execute_data, opline->op1.var);
- if (IS_CV == IS_UNUSED && UNEXPECTED(Z_OBJ_P(container) == NULL)) {
+ if (IS_CV == IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
HANDLE_EXCEPTION();
}
- if (IS_CV == IS_VAR && UNEXPECTED(container == NULL)) {
- zend_throw_error(NULL, "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);
if (IS_CV == IS_VAR && READY_TO_DESTROY(free_op1)) {
- EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var), 0);
+ EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var));
}
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
@@ -36704,7 +44747,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_IS_SPEC_CV_CV_HANDLE
SAVE_OPLINE();
container = _get_zval_ptr_cv_BP_VAR_IS(execute_data, opline->op1.var);
- if (IS_CV == IS_UNUSED && UNEXPECTED(Z_OBJ_P(container) == NULL)) {
+ if (IS_CV == IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
HANDLE_EXCEPTION();
@@ -36779,26 +44822,21 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_FUNC_ARG_SPEC_CV_CV_
property = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var);
container = _get_zval_ptr_cv_undef_BP_VAR_W(execute_data, opline->op1.var);
- if (IS_CV == IS_UNUSED && UNEXPECTED(Z_OBJ_P(container) == NULL)) {
+ if (IS_CV == IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
HANDLE_EXCEPTION();
}
- if (IS_CV == IS_CONST || IS_CV == IS_TMP_VAR) {
+ if ((IS_CV & (IS_CONST|IS_TMP_VAR))) {
zend_throw_error(NULL, "Cannot use temporary expression in write context");
HANDLE_EXCEPTION();
}
- if (IS_CV == IS_VAR && UNEXPECTED(container == NULL)) {
- zend_throw_error(NULL, "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);
if (IS_CV == IS_VAR && READY_TO_DESTROY(free_op1)) {
- EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var), 0);
+ EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var));
}
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
@@ -36816,7 +44854,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_UNSET_SPEC_CV_CV_HAN
SAVE_OPLINE();
container = _get_zval_ptr_cv_BP_VAR_UNSET(execute_data, opline->op1.var);
- if (IS_CV == IS_UNUSED && UNEXPECTED(Z_OBJ_P(container) == NULL)) {
+ if (IS_CV == IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
HANDLE_EXCEPTION();
@@ -36824,56 +44862,658 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_UNSET_SPEC_CV_CV_HAN
property = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var);
- if (IS_CV == IS_VAR && UNEXPECTED(container == NULL)) {
- zend_throw_error(NULL, "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);
if (IS_CV == IS_VAR && READY_TO_DESTROY(free_op1)) {
- EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var), 0);
+ EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var));
}
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_LIST_SPEC_CV_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
- zval *object;
- zval *property_name;
+ zval *container;
+
+ SAVE_OPLINE();
+ container = _get_zval_ptr_cv_undef(execute_data, opline->op1.var);
+ zend_fetch_dimension_address_read_LIST(EX_VAR(opline->result.var), container, _get_zval_ptr_cv_undef(execute_data, opline->op2.var));
+
+ ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_CV_OP_DATA_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+
+ zval *object, *property_name, *value, tmp;
+
+ SAVE_OPLINE();
+ object = _get_zval_ptr_cv_undef_BP_VAR_W(execute_data, opline->op1.var);
+
+ if (IS_CV == IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) == IS_UNDEF)) {
+ zend_throw_error(NULL, "Using $this when not in object context");
+
+ HANDLE_EXCEPTION();
+ }
+
+ property_name = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var);
+ value = EX_CONSTANT((opline+1)->op1);
+
+ if (IS_CV != IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) != IS_OBJECT)) {
+ do {
+ if (Z_ISREF_P(object)) {
+ object = Z_REFVAL_P(object);
+ if (EXPECTED(Z_TYPE_P(object) == IS_OBJECT)) {
+ break;
+ }
+ }
+ if (EXPECTED(Z_TYPE_P(object) <= IS_FALSE ||
+ (Z_TYPE_P(object) == IS_STRING && Z_STRLEN_P(object) == 0))) {
+ zend_object *obj;
+
+ zval_ptr_dtor(object);
+ object_init(object);
+ Z_ADDREF_P(object);
+ obj = Z_OBJ_P(object);
+ zend_error(E_WARNING, "Creating default object from empty value");
+ if (GC_REFCOUNT(obj) == 1) {
+ /* the enclosing container was deleted, obj is unreferenced */
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+
+ OBJ_RELEASE(obj);
+ goto exit_assign_obj;
+ }
+ Z_DELREF_P(object);
+ } else {
+ if (IS_CV != IS_VAR || EXPECTED(!Z_ISERROR_P(object))) {
+ zend_error(E_WARNING, "Attempt to assign property of non-object");
+ }
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+
+ goto exit_assign_obj;
+ }
+ } while (0);
+ }
+
+ if (IS_CV == IS_CONST &&
+ EXPECTED(Z_OBJCE_P(object) == CACHED_PTR(Z_CACHE_SLOT_P(property_name)))) {
+ uint32_t prop_offset = (uint32_t)(intptr_t)CACHED_PTR(Z_CACHE_SLOT_P(property_name) + sizeof(void*));
+ zend_object *zobj = Z_OBJ_P(object);
+ zval *property;
+
+ if (EXPECTED(prop_offset != (uint32_t)ZEND_DYNAMIC_PROPERTY_OFFSET)) {
+ property = OBJ_PROP(zobj, prop_offset);
+ if (Z_TYPE_P(property) != IS_UNDEF) {
+fast_assign_obj:
+ value = zend_assign_to_variable(property, value, IS_CONST);
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+ goto exit_assign_obj;
+ }
+ } else {
+ if (EXPECTED(zobj->properties != NULL)) {
+ if (UNEXPECTED(GC_REFCOUNT(zobj->properties) > 1)) {
+ if (EXPECTED(!(GC_FLAGS(zobj->properties) & IS_ARRAY_IMMUTABLE))) {
+ GC_REFCOUNT(zobj->properties)--;
+ }
+ zobj->properties = zend_array_dup(zobj->properties);
+ }
+ property = zend_hash_find(zobj->properties, Z_STR_P(property_name));
+ if (property) {
+ goto fast_assign_obj;
+ }
+ }
+
+ if (!zobj->ce->__set) {
+
+ if (EXPECTED(zobj->properties == NULL)) {
+ rebuild_object_properties(zobj);
+ }
+ if (IS_CONST == IS_CONST) {
+ if (UNEXPECTED(Z_OPT_REFCOUNTED_P(value))) {
+ Z_ADDREF_P(value);
+ }
+ } else if (IS_CONST != IS_TMP_VAR) {
+ if (Z_ISREF_P(value)) {
+ if (IS_CONST == IS_VAR) {
+ zend_reference *ref = Z_REF_P(value);
+ if (--GC_REFCOUNT(ref) == 0) {
+ ZVAL_COPY_VALUE(&tmp, Z_REFVAL_P(value));
+ efree_size(ref, sizeof(zend_reference));
+ value = &tmp;
+ } else {
+ value = Z_REFVAL_P(value);
+ if (Z_REFCOUNTED_P(value)) {
+ Z_ADDREF_P(value);
+ }
+ }
+ } else {
+ value = Z_REFVAL_P(value);
+ if (Z_REFCOUNTED_P(value)) {
+ Z_ADDREF_P(value);
+ }
+ }
+ } else if (IS_CONST == IS_CV && Z_REFCOUNTED_P(value)) {
+ Z_ADDREF_P(value);
+ }
+ }
+ zend_hash_add_new(zobj->properties, Z_STR_P(property_name), value);
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+ goto exit_assign_obj;
+ }
+ }
+ }
+
+ if (!Z_OBJ_HT_P(object)->write_property) {
+ zend_error(E_WARNING, "Attempt to assign property of non-object");
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+
+ goto exit_assign_obj;
+ }
+
+ if (IS_CONST == IS_CV || IS_CONST == IS_VAR) {
+ ZVAL_DEREF(value);
+ }
+
+ Z_OBJ_HT_P(object)->write_property(object, property_name, value, (IS_CV == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property_name)) : NULL);
+
+ if (UNEXPECTED(RETURN_VALUE_USED(opline)) && EXPECTED(!EG(exception))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+
+exit_assign_obj:
+
+
+ /* assign_obj has two opcodes! */
+ ZEND_VM_NEXT_OPCODE_EX(1, 2);
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_CV_OP_DATA_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zend_free_op free_op_data;
+ zval *object, *property_name, *value, tmp;
+
+ SAVE_OPLINE();
+ object = _get_zval_ptr_cv_undef_BP_VAR_W(execute_data, opline->op1.var);
+
+ if (IS_CV == IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) == IS_UNDEF)) {
+ zend_throw_error(NULL, "Using $this when not in object context");
+
+ HANDLE_EXCEPTION();
+ }
+
+ property_name = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var);
+ value = _get_zval_ptr_tmp((opline+1)->op1.var, execute_data, &free_op_data);
+
+ if (IS_CV != IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) != IS_OBJECT)) {
+ do {
+ if (Z_ISREF_P(object)) {
+ object = Z_REFVAL_P(object);
+ if (EXPECTED(Z_TYPE_P(object) == IS_OBJECT)) {
+ break;
+ }
+ }
+ if (EXPECTED(Z_TYPE_P(object) <= IS_FALSE ||
+ (Z_TYPE_P(object) == IS_STRING && Z_STRLEN_P(object) == 0))) {
+ zend_object *obj;
+
+ zval_ptr_dtor(object);
+ object_init(object);
+ Z_ADDREF_P(object);
+ obj = Z_OBJ_P(object);
+ zend_error(E_WARNING, "Creating default object from empty value");
+ if (GC_REFCOUNT(obj) == 1) {
+ /* the enclosing container was deleted, obj is unreferenced */
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+ zval_ptr_dtor_nogc(free_op_data);
+ OBJ_RELEASE(obj);
+ goto exit_assign_obj;
+ }
+ Z_DELREF_P(object);
+ } else {
+ if (IS_CV != IS_VAR || EXPECTED(!Z_ISERROR_P(object))) {
+ zend_error(E_WARNING, "Attempt to assign property of non-object");
+ }
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+ zval_ptr_dtor_nogc(free_op_data);
+ goto exit_assign_obj;
+ }
+ } while (0);
+ }
+
+ if (IS_CV == IS_CONST &&
+ EXPECTED(Z_OBJCE_P(object) == CACHED_PTR(Z_CACHE_SLOT_P(property_name)))) {
+ uint32_t prop_offset = (uint32_t)(intptr_t)CACHED_PTR(Z_CACHE_SLOT_P(property_name) + sizeof(void*));
+ zend_object *zobj = Z_OBJ_P(object);
+ zval *property;
+
+ if (EXPECTED(prop_offset != (uint32_t)ZEND_DYNAMIC_PROPERTY_OFFSET)) {
+ property = OBJ_PROP(zobj, prop_offset);
+ if (Z_TYPE_P(property) != IS_UNDEF) {
+fast_assign_obj:
+ value = zend_assign_to_variable(property, value, IS_TMP_VAR);
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+ goto exit_assign_obj;
+ }
+ } else {
+ if (EXPECTED(zobj->properties != NULL)) {
+ if (UNEXPECTED(GC_REFCOUNT(zobj->properties) > 1)) {
+ if (EXPECTED(!(GC_FLAGS(zobj->properties) & IS_ARRAY_IMMUTABLE))) {
+ GC_REFCOUNT(zobj->properties)--;
+ }
+ zobj->properties = zend_array_dup(zobj->properties);
+ }
+ property = zend_hash_find(zobj->properties, Z_STR_P(property_name));
+ if (property) {
+ goto fast_assign_obj;
+ }
+ }
+
+ if (!zobj->ce->__set) {
+
+ if (EXPECTED(zobj->properties == NULL)) {
+ rebuild_object_properties(zobj);
+ }
+ if (IS_TMP_VAR == IS_CONST) {
+ if (UNEXPECTED(Z_OPT_REFCOUNTED_P(value))) {
+ Z_ADDREF_P(value);
+ }
+ } else if (IS_TMP_VAR != IS_TMP_VAR) {
+ if (Z_ISREF_P(value)) {
+ if (IS_TMP_VAR == IS_VAR) {
+ zend_reference *ref = Z_REF_P(value);
+ if (--GC_REFCOUNT(ref) == 0) {
+ ZVAL_COPY_VALUE(&tmp, Z_REFVAL_P(value));
+ efree_size(ref, sizeof(zend_reference));
+ value = &tmp;
+ } else {
+ value = Z_REFVAL_P(value);
+ if (Z_REFCOUNTED_P(value)) {
+ Z_ADDREF_P(value);
+ }
+ }
+ } else {
+ value = Z_REFVAL_P(value);
+ if (Z_REFCOUNTED_P(value)) {
+ Z_ADDREF_P(value);
+ }
+ }
+ } else if (IS_TMP_VAR == IS_CV && Z_REFCOUNTED_P(value)) {
+ Z_ADDREF_P(value);
+ }
+ }
+ zend_hash_add_new(zobj->properties, Z_STR_P(property_name), value);
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+ goto exit_assign_obj;
+ }
+ }
+ }
+
+ if (!Z_OBJ_HT_P(object)->write_property) {
+ zend_error(E_WARNING, "Attempt to assign property of non-object");
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+ zval_ptr_dtor_nogc(free_op_data);
+ goto exit_assign_obj;
+ }
+
+ if (IS_TMP_VAR == IS_CV || IS_TMP_VAR == IS_VAR) {
+ ZVAL_DEREF(value);
+ }
+
+ Z_OBJ_HT_P(object)->write_property(object, property_name, value, (IS_CV == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property_name)) : NULL);
+
+ if (UNEXPECTED(RETURN_VALUE_USED(opline)) && EXPECTED(!EG(exception))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+ zval_ptr_dtor_nogc(free_op_data);
+exit_assign_obj:
+
+
+ /* assign_obj has two opcodes! */
+ ZEND_VM_NEXT_OPCODE_EX(1, 2);
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_CV_OP_DATA_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zend_free_op free_op_data;
+ zval *object, *property_name, *value, tmp;
SAVE_OPLINE();
object = _get_zval_ptr_cv_undef_BP_VAR_W(execute_data, opline->op1.var);
- if (IS_CV == IS_UNUSED && UNEXPECTED(Z_OBJ_P(object) == NULL)) {
+ if (IS_CV == IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
HANDLE_EXCEPTION();
}
property_name = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var);
+ value = _get_zval_ptr_var((opline+1)->op1.var, execute_data, &free_op_data);
+
+ if (IS_CV != IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) != IS_OBJECT)) {
+ do {
+ if (Z_ISREF_P(object)) {
+ object = Z_REFVAL_P(object);
+ if (EXPECTED(Z_TYPE_P(object) == IS_OBJECT)) {
+ break;
+ }
+ }
+ if (EXPECTED(Z_TYPE_P(object) <= IS_FALSE ||
+ (Z_TYPE_P(object) == IS_STRING && Z_STRLEN_P(object) == 0))) {
+ zend_object *obj;
+
+ zval_ptr_dtor(object);
+ object_init(object);
+ Z_ADDREF_P(object);
+ obj = Z_OBJ_P(object);
+ zend_error(E_WARNING, "Creating default object from empty value");
+ if (GC_REFCOUNT(obj) == 1) {
+ /* the enclosing container was deleted, obj is unreferenced */
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+ zval_ptr_dtor_nogc(free_op_data);
+ OBJ_RELEASE(obj);
+ goto exit_assign_obj;
+ }
+ Z_DELREF_P(object);
+ } else {
+ if (IS_CV != IS_VAR || EXPECTED(!Z_ISERROR_P(object))) {
+ zend_error(E_WARNING, "Attempt to assign property of non-object");
+ }
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+ zval_ptr_dtor_nogc(free_op_data);
+ goto exit_assign_obj;
+ }
+ } while (0);
+ }
+
+ if (IS_CV == IS_CONST &&
+ EXPECTED(Z_OBJCE_P(object) == CACHED_PTR(Z_CACHE_SLOT_P(property_name)))) {
+ uint32_t prop_offset = (uint32_t)(intptr_t)CACHED_PTR(Z_CACHE_SLOT_P(property_name) + sizeof(void*));
+ zend_object *zobj = Z_OBJ_P(object);
+ zval *property;
+
+ if (EXPECTED(prop_offset != (uint32_t)ZEND_DYNAMIC_PROPERTY_OFFSET)) {
+ property = OBJ_PROP(zobj, prop_offset);
+ if (Z_TYPE_P(property) != IS_UNDEF) {
+fast_assign_obj:
+ value = zend_assign_to_variable(property, value, IS_VAR);
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+ goto exit_assign_obj;
+ }
+ } else {
+ if (EXPECTED(zobj->properties != NULL)) {
+ if (UNEXPECTED(GC_REFCOUNT(zobj->properties) > 1)) {
+ if (EXPECTED(!(GC_FLAGS(zobj->properties) & IS_ARRAY_IMMUTABLE))) {
+ GC_REFCOUNT(zobj->properties)--;
+ }
+ zobj->properties = zend_array_dup(zobj->properties);
+ }
+ property = zend_hash_find(zobj->properties, Z_STR_P(property_name));
+ if (property) {
+ goto fast_assign_obj;
+ }
+ }
+
+ if (!zobj->ce->__set) {
+
+ if (EXPECTED(zobj->properties == NULL)) {
+ rebuild_object_properties(zobj);
+ }
+ if (IS_VAR == IS_CONST) {
+ if (UNEXPECTED(Z_OPT_REFCOUNTED_P(value))) {
+ Z_ADDREF_P(value);
+ }
+ } else if (IS_VAR != IS_TMP_VAR) {
+ if (Z_ISREF_P(value)) {
+ if (IS_VAR == IS_VAR) {
+ zend_reference *ref = Z_REF_P(value);
+ if (--GC_REFCOUNT(ref) == 0) {
+ ZVAL_COPY_VALUE(&tmp, Z_REFVAL_P(value));
+ efree_size(ref, sizeof(zend_reference));
+ value = &tmp;
+ } else {
+ value = Z_REFVAL_P(value);
+ if (Z_REFCOUNTED_P(value)) {
+ Z_ADDREF_P(value);
+ }
+ }
+ } else {
+ value = Z_REFVAL_P(value);
+ if (Z_REFCOUNTED_P(value)) {
+ Z_ADDREF_P(value);
+ }
+ }
+ } else if (IS_VAR == IS_CV && Z_REFCOUNTED_P(value)) {
+ Z_ADDREF_P(value);
+ }
+ }
+ zend_hash_add_new(zobj->properties, Z_STR_P(property_name), value);
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+ goto exit_assign_obj;
+ }
+ }
+ }
+
+ if (!Z_OBJ_HT_P(object)->write_property) {
+ zend_error(E_WARNING, "Attempt to assign property of non-object");
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+ zval_ptr_dtor_nogc(free_op_data);
+ goto exit_assign_obj;
+ }
+
+ if (IS_VAR == IS_CV || IS_VAR == IS_VAR) {
+ ZVAL_DEREF(value);
+ }
+
+ Z_OBJ_HT_P(object)->write_property(object, property_name, value, (IS_CV == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property_name)) : NULL);
+
+ if (UNEXPECTED(RETURN_VALUE_USED(opline)) && EXPECTED(!EG(exception))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+ zval_ptr_dtor_nogc(free_op_data);
+exit_assign_obj:
- if (IS_CV == IS_VAR && UNEXPECTED(object == NULL)) {
- zend_throw_error(NULL, "Cannot use string offset as an array");
+
+ /* assign_obj has two opcodes! */
+ ZEND_VM_NEXT_OPCODE_EX(1, 2);
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_CV_OP_DATA_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+
+ zval *object, *property_name, *value, tmp;
+
+ SAVE_OPLINE();
+ object = _get_zval_ptr_cv_undef_BP_VAR_W(execute_data, opline->op1.var);
+
+ if (IS_CV == IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) == IS_UNDEF)) {
+ zend_throw_error(NULL, "Using $this when not in object context");
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));
+
+ property_name = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var);
+ value = _get_zval_ptr_cv_BP_VAR_R(execute_data, (opline+1)->op1.var);
+
+ if (IS_CV != IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) != IS_OBJECT)) {
+ do {
+ if (Z_ISREF_P(object)) {
+ object = Z_REFVAL_P(object);
+ if (EXPECTED(Z_TYPE_P(object) == IS_OBJECT)) {
+ break;
+ }
+ }
+ if (EXPECTED(Z_TYPE_P(object) <= IS_FALSE ||
+ (Z_TYPE_P(object) == IS_STRING && Z_STRLEN_P(object) == 0))) {
+ zend_object *obj;
+
+ zval_ptr_dtor(object);
+ object_init(object);
+ Z_ADDREF_P(object);
+ obj = Z_OBJ_P(object);
+ zend_error(E_WARNING, "Creating default object from empty value");
+ if (GC_REFCOUNT(obj) == 1) {
+ /* the enclosing container was deleted, obj is unreferenced */
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+
+ OBJ_RELEASE(obj);
+ goto exit_assign_obj;
+ }
+ Z_DELREF_P(object);
+ } else {
+ if (IS_CV != IS_VAR || EXPECTED(!Z_ISERROR_P(object))) {
+ zend_error(E_WARNING, "Attempt to assign property of non-object");
+ }
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+
+ goto exit_assign_obj;
+ }
+ } while (0);
+ }
+
+ if (IS_CV == IS_CONST &&
+ EXPECTED(Z_OBJCE_P(object) == CACHED_PTR(Z_CACHE_SLOT_P(property_name)))) {
+ uint32_t prop_offset = (uint32_t)(intptr_t)CACHED_PTR(Z_CACHE_SLOT_P(property_name) + sizeof(void*));
+ zend_object *zobj = Z_OBJ_P(object);
+ zval *property;
+
+ if (EXPECTED(prop_offset != (uint32_t)ZEND_DYNAMIC_PROPERTY_OFFSET)) {
+ property = OBJ_PROP(zobj, prop_offset);
+ if (Z_TYPE_P(property) != IS_UNDEF) {
+fast_assign_obj:
+ value = zend_assign_to_variable(property, value, IS_CV);
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+ goto exit_assign_obj;
+ }
+ } else {
+ if (EXPECTED(zobj->properties != NULL)) {
+ if (UNEXPECTED(GC_REFCOUNT(zobj->properties) > 1)) {
+ if (EXPECTED(!(GC_FLAGS(zobj->properties) & IS_ARRAY_IMMUTABLE))) {
+ GC_REFCOUNT(zobj->properties)--;
+ }
+ zobj->properties = zend_array_dup(zobj->properties);
+ }
+ property = zend_hash_find(zobj->properties, Z_STR_P(property_name));
+ if (property) {
+ goto fast_assign_obj;
+ }
+ }
+
+ if (!zobj->ce->__set) {
+
+ if (EXPECTED(zobj->properties == NULL)) {
+ rebuild_object_properties(zobj);
+ }
+ if (IS_CV == IS_CONST) {
+ if (UNEXPECTED(Z_OPT_REFCOUNTED_P(value))) {
+ Z_ADDREF_P(value);
+ }
+ } else if (IS_CV != IS_TMP_VAR) {
+ if (Z_ISREF_P(value)) {
+ if (IS_CV == IS_VAR) {
+ zend_reference *ref = Z_REF_P(value);
+ if (--GC_REFCOUNT(ref) == 0) {
+ ZVAL_COPY_VALUE(&tmp, Z_REFVAL_P(value));
+ efree_size(ref, sizeof(zend_reference));
+ value = &tmp;
+ } else {
+ value = Z_REFVAL_P(value);
+ if (Z_REFCOUNTED_P(value)) {
+ Z_ADDREF_P(value);
+ }
+ }
+ } else {
+ value = Z_REFVAL_P(value);
+ if (Z_REFCOUNTED_P(value)) {
+ Z_ADDREF_P(value);
+ }
+ }
+ } else if (IS_CV == IS_CV && Z_REFCOUNTED_P(value)) {
+ Z_ADDREF_P(value);
+ }
+ }
+ zend_hash_add_new(zobj->properties, Z_STR_P(property_name), value);
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+ goto exit_assign_obj;
+ }
+ }
+ }
+
+ if (!Z_OBJ_HT_P(object)->write_property) {
+ zend_error(E_WARNING, "Attempt to assign property of non-object");
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+
+ goto exit_assign_obj;
+ }
+
+ if (IS_CV == IS_CV || IS_CV == IS_VAR) {
+ ZVAL_DEREF(value);
+ }
+
+ Z_OBJ_HT_P(object)->write_property(object, property_name, value, (IS_CV == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property_name)) : NULL);
+
+ if (UNEXPECTED(RETURN_VALUE_USED(opline)) && EXPECTED(!EG(exception))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+
+exit_assign_obj:
/* assign_obj has two opcodes! */
ZEND_VM_NEXT_OPCODE_EX(1, 2);
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_CV_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_CV_CV_OP_DATA_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
zval *object_ptr;
- zend_free_op free_op_data1;
+
zval *value;
zval *variable_ptr;
zval *dim;
@@ -36881,40 +45521,214 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_CV_CV_HANDLER(
SAVE_OPLINE();
object_ptr = _get_zval_ptr_cv_undef_BP_VAR_W(execute_data, opline->op1.var);
- if (IS_CV == IS_VAR && UNEXPECTED(object_ptr == NULL)) {
- zend_throw_error(NULL, "Cannot use string offset as an array");
- FREE_UNFETCHED_OP((opline+1)->op1_type, (opline+1)->op1.var);
+ if (EXPECTED(Z_TYPE_P(object_ptr) == IS_ARRAY)) {
+try_assign_dim_array:
+ SEPARATE_ARRAY(object_ptr);
+ if (IS_CV == IS_UNUSED) {
+ variable_ptr = zend_hash_next_index_insert(Z_ARRVAL_P(object_ptr), &EG(uninitialized_zval));
+ if (UNEXPECTED(variable_ptr == NULL)) {
+ zend_error(E_WARNING, "Cannot add element to the array as the next element is already occupied");
+ goto assign_dim_error;
+ }
+ } else {
+ dim = _get_zval_ptr_cv_undef(execute_data, opline->op2.var);
+ if (IS_CV == IS_CONST) {
+ variable_ptr = zend_fetch_dimension_address_inner_W_CONST(Z_ARRVAL_P(object_ptr), dim);
+ } else {
+ variable_ptr = zend_fetch_dimension_address_inner_W(Z_ARRVAL_P(object_ptr), dim);
+ }
+ if (UNEXPECTED(variable_ptr == NULL)) {
+ goto assign_dim_error;
+ }
+ }
+ value = EX_CONSTANT((opline+1)->op1);
+ value = zend_assign_to_variable(variable_ptr, value, IS_CONST);
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+ } 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)) {
+ dim = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var);
+ value = EX_CONSTANT((opline+1)->op1);
- HANDLE_EXCEPTION();
+ zend_assign_to_object_dim(object_ptr, dim, value);
+
+ if (UNEXPECTED(RETURN_VALUE_USED(opline)) && EXPECTED(!EG(exception))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+
+ } else if (EXPECTED(Z_TYPE_P(object_ptr) == IS_STRING)) {
+ if (IS_CV == IS_UNUSED) {
+ zend_throw_error(NULL, "[] operator not supported for strings");
+
+
+ HANDLE_EXCEPTION();
+ } else {
+ dim = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var);
+ value = EX_CONSTANT((opline+1)->op1);
+ zend_assign_to_string_offset(object_ptr, dim, value, (UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL));
+
+ }
+ } else if (EXPECTED(Z_TYPE_P(object_ptr) <= IS_FALSE)) {
+ 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 (IS_CV != IS_VAR || EXPECTED(!Z_ISERROR_P(object_ptr))) {
+ zend_error(E_WARNING, "Cannot use a scalar value as an array");
+ }
+ dim = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var);
+assign_dim_error:
+
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+ }
}
+ if (IS_CV != IS_UNUSED) {
+
+ }
+
+ /* assign_dim has two opcodes! */
+ ZEND_VM_NEXT_OPCODE_EX(1, 2);
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_CV_CV_OP_DATA_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+
+ zval *object_ptr;
+ zend_free_op free_op_data;
+ zval *value;
+ zval *variable_ptr;
+ zval *dim;
+
+ SAVE_OPLINE();
+ object_ptr = _get_zval_ptr_cv_undef_BP_VAR_W(execute_data, opline->op1.var);
if (EXPECTED(Z_TYPE_P(object_ptr) == IS_ARRAY)) {
try_assign_dim_array:
+ SEPARATE_ARRAY(object_ptr);
if (IS_CV == IS_UNUSED) {
- SEPARATE_ARRAY(object_ptr);
variable_ptr = zend_hash_next_index_insert(Z_ARRVAL_P(object_ptr), &EG(uninitialized_zval));
if (UNEXPECTED(variable_ptr == NULL)) {
zend_error(E_WARNING, "Cannot add element to the array as the next element is already occupied");
- variable_ptr = &EG(error_zval);
+ goto assign_dim_error;
}
} else {
+ dim = _get_zval_ptr_cv_undef(execute_data, opline->op2.var);
+ if (IS_CV == IS_CONST) {
+ variable_ptr = zend_fetch_dimension_address_inner_W_CONST(Z_ARRVAL_P(object_ptr), dim);
+ } else {
+ variable_ptr = zend_fetch_dimension_address_inner_W(Z_ARRVAL_P(object_ptr), dim);
+ }
+ if (UNEXPECTED(variable_ptr == NULL)) {
+ goto assign_dim_error;
+ }
+ }
+ value = _get_zval_ptr_tmp((opline+1)->op1.var, execute_data, &free_op_data);
+ value = zend_assign_to_variable(variable_ptr, value, IS_TMP_VAR);
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+ } 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)) {
dim = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var);
- SEPARATE_ARRAY(object_ptr);
- variable_ptr = zend_fetch_dimension_address_inner(Z_ARRVAL_P(object_ptr), dim, IS_CV, BP_VAR_W);
+ value = _get_zval_ptr_tmp((opline+1)->op1.var, execute_data, &free_op_data);
- }
- value = get_zval_ptr_r((opline+1)->op1_type, (opline+1)->op1, execute_data, &free_op_data1);
- if (UNEXPECTED(variable_ptr == &EG(error_zval))) {
- FREE_OP(free_op_data1);
+ zend_assign_to_object_dim(object_ptr, dim, value);
+
+ if (UNEXPECTED(RETURN_VALUE_USED(opline)) && EXPECTED(!EG(exception))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+
+ zval_ptr_dtor_nogc(free_op_data);
+ } else if (EXPECTED(Z_TYPE_P(object_ptr) == IS_STRING)) {
+ if (IS_CV == IS_UNUSED) {
+ zend_throw_error(NULL, "[] operator not supported for strings");
+ zval_ptr_dtor_nogc(EX_VAR((opline+1)->op1.var));
+
+ HANDLE_EXCEPTION();
+ } else {
+ dim = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var);
+ value = _get_zval_ptr_tmp((opline+1)->op1.var, execute_data, &free_op_data);
+ zend_assign_to_string_offset(object_ptr, dim, value, (UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL));
+ zval_ptr_dtor_nogc(free_op_data);
+ }
+ } else if (EXPECTED(Z_TYPE_P(object_ptr) <= IS_FALSE)) {
+ 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 (IS_CV != IS_VAR || EXPECTED(!Z_ISERROR_P(object_ptr))) {
+ zend_error(E_WARNING, "Cannot use a scalar value as an array");
+ }
+ dim = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var);
+assign_dim_error:
+ zval_ptr_dtor_nogc(EX_VAR((opline+1)->op1.var));
if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
ZVAL_NULL(EX_VAR(opline->result.var));
}
+ }
+ }
+ if (IS_CV != IS_UNUSED) {
+
+ }
+
+ /* assign_dim has two opcodes! */
+ ZEND_VM_NEXT_OPCODE_EX(1, 2);
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_CV_CV_OP_DATA_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+
+ zval *object_ptr;
+ zend_free_op free_op_data;
+ zval *value;
+ zval *variable_ptr;
+ zval *dim;
+
+ SAVE_OPLINE();
+ object_ptr = _get_zval_ptr_cv_undef_BP_VAR_W(execute_data, opline->op1.var);
+
+ if (EXPECTED(Z_TYPE_P(object_ptr) == IS_ARRAY)) {
+try_assign_dim_array:
+ SEPARATE_ARRAY(object_ptr);
+ if (IS_CV == IS_UNUSED) {
+ variable_ptr = zend_hash_next_index_insert(Z_ARRVAL_P(object_ptr), &EG(uninitialized_zval));
+ if (UNEXPECTED(variable_ptr == NULL)) {
+ zend_error(E_WARNING, "Cannot add element to the array as the next element is already occupied");
+ goto assign_dim_error;
+ }
} else {
- value = zend_assign_to_variable(variable_ptr, value, (opline+1)->op1_type);
- if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
- ZVAL_COPY(EX_VAR(opline->result.var), value);
+ dim = _get_zval_ptr_cv_undef(execute_data, opline->op2.var);
+ if (IS_CV == IS_CONST) {
+ variable_ptr = zend_fetch_dimension_address_inner_W_CONST(Z_ARRVAL_P(object_ptr), dim);
+ } else {
+ variable_ptr = zend_fetch_dimension_address_inner_W(Z_ARRVAL_P(object_ptr), dim);
+ }
+ if (UNEXPECTED(variable_ptr == NULL)) {
+ goto assign_dim_error;
}
}
+ value = _get_zval_ptr_var((opline+1)->op1.var, execute_data, &free_op_data);
+ value = zend_assign_to_variable(variable_ptr, value, IS_VAR);
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
} else {
if (EXPECTED(Z_ISREF_P(object_ptr))) {
object_ptr = Z_REFVAL_P(object_ptr);
@@ -36923,58 +45737,144 @@ try_assign_dim_array:
}
}
if (EXPECTED(Z_TYPE_P(object_ptr) == IS_OBJECT)) {
+ dim = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var);
+ value = _get_zval_ptr_var((opline+1)->op1.var, execute_data, &free_op_data);
- zval *property_name = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var);
+ zend_assign_to_object_dim(object_ptr, dim, value);
- 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);
+ if (UNEXPECTED(RETURN_VALUE_USED(opline)) && EXPECTED(!EG(exception))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+ zval_ptr_dtor_nogc(free_op_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_throw_error(NULL, "[] operator not supported for strings");
- FREE_UNFETCHED_OP((opline+1)->op1_type, (opline+1)->op1.var);
+ if (IS_CV == IS_UNUSED) {
+ zend_throw_error(NULL, "[] operator not supported for strings");
+ zval_ptr_dtor_nogc(EX_VAR((opline+1)->op1.var));
- HANDLE_EXCEPTION();
- } else {
- zend_long offset;
+ HANDLE_EXCEPTION();
+ } else {
+ dim = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var);
+ value = _get_zval_ptr_var_deref((opline+1)->op1.var, execute_data, &free_op_data);
+ zend_assign_to_string_offset(object_ptr, dim, value, (UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL));
+ zval_ptr_dtor_nogc(free_op_data);
+ }
+ } else if (EXPECTED(Z_TYPE_P(object_ptr) <= IS_FALSE)) {
+ 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 (IS_CV != IS_VAR || EXPECTED(!Z_ISERROR_P(object_ptr))) {
+ zend_error(E_WARNING, "Cannot use a scalar value as an array");
+ }
+ dim = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var);
+assign_dim_error:
+ zval_ptr_dtor_nogc(EX_VAR((opline+1)->op1.var));
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+ }
+ }
+ if (IS_CV != IS_UNUSED) {
- 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_r_deref((opline+1)->op1_type, (opline+1)->op1, execute_data, &free_op_data1);
- 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);
- }
+ /* assign_dim has two opcodes! */
+ ZEND_VM_NEXT_OPCODE_EX(1, 2);
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_CV_CV_OP_DATA_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+
+ zval *object_ptr;
+
+ zval *value;
+ zval *variable_ptr;
+ zval *dim;
+
+ SAVE_OPLINE();
+ object_ptr = _get_zval_ptr_cv_undef_BP_VAR_W(execute_data, opline->op1.var);
+
+ if (EXPECTED(Z_TYPE_P(object_ptr) == IS_ARRAY)) {
+try_assign_dim_array:
+ SEPARATE_ARRAY(object_ptr);
+ if (IS_CV == IS_UNUSED) {
+ variable_ptr = zend_hash_next_index_insert(Z_ARRVAL_P(object_ptr), &EG(uninitialized_zval));
+ if (UNEXPECTED(variable_ptr == NULL)) {
+ zend_error(E_WARNING, "Cannot add element to the array as the next element is already occupied");
+ goto assign_dim_error;
+ }
+ } else {
+ dim = _get_zval_ptr_cv_undef(execute_data, opline->op2.var);
+ if (IS_CV == IS_CONST) {
+ variable_ptr = zend_fetch_dimension_address_inner_W_CONST(Z_ARRVAL_P(object_ptr), dim);
} 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);
+ variable_ptr = zend_fetch_dimension_address_inner_W(Z_ARRVAL_P(object_ptr), dim);
+ }
+ if (UNEXPECTED(variable_ptr == NULL)) {
+ goto assign_dim_error;
+ }
+ }
+ value = _get_zval_ptr_cv_BP_VAR_R(execute_data, (opline+1)->op1.var);
+ value = zend_assign_to_variable(variable_ptr, value, IS_CV);
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+ } 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;
}
- } 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;
+ }
+ if (EXPECTED(Z_TYPE_P(object_ptr) == IS_OBJECT)) {
+ dim = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var);
+ value = _get_zval_ptr_cv_BP_VAR_R(execute_data, (opline+1)->op1.var);
+
+ zend_assign_to_object_dim(object_ptr, dim, value);
+
+ if (UNEXPECTED(RETURN_VALUE_USED(opline)) && EXPECTED(!EG(exception))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
}
- goto assign_dim_convert_to_array;
+
+ } else if (EXPECTED(Z_TYPE_P(object_ptr) == IS_STRING)) {
+ if (IS_CV == IS_UNUSED) {
+ zend_throw_error(NULL, "[] operator not supported for strings");
+
+
+ HANDLE_EXCEPTION();
+ } else {
+ dim = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var);
+ value = _get_zval_ptr_cv_deref_BP_VAR_R(execute_data, (opline+1)->op1.var);
+ zend_assign_to_string_offset(object_ptr, dim, value, (UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL));
+
+ }
+ } else if (EXPECTED(Z_TYPE_P(object_ptr) <= IS_FALSE)) {
+ 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 {
- zend_error(E_WARNING, "Cannot use a scalar value as an array");
-assign_dim_clean:
+ if (IS_CV != IS_VAR || EXPECTED(!Z_ISERROR_P(object_ptr))) {
+ zend_error(E_WARNING, "Cannot use a scalar value as an array");
+ }
dim = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var);
+assign_dim_error:
- value = get_zval_ptr_r((opline+1)->op1_type, (opline+1)->op1, execute_data, &free_op_data1);
- FREE_OP(free_op_data1);
if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
ZVAL_NULL(EX_VAR(opline->result.var));
}
}
}
+ if (IS_CV != IS_UNUSED) {
+
+ }
/* assign_dim has two opcodes! */
ZEND_VM_NEXT_OPCODE_EX(1, 2);
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_SPEC_CV_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_SPEC_CV_CV_RETVAL_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
@@ -36985,14 +45885,42 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_SPEC_CV_CV_HANDLER(ZEND
value = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var);
variable_ptr = _get_zval_ptr_cv_undef_BP_VAR_W(execute_data, opline->op1.var);
- if (IS_CV == IS_VAR && UNEXPECTED(variable_ptr == &EG(error_zval))) {
+ if (IS_CV == IS_VAR && UNEXPECTED(Z_ISERROR_P(variable_ptr))) {
- if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ if (UNEXPECTED(0)) {
ZVAL_NULL(EX_VAR(opline->result.var));
}
} else {
value = zend_assign_to_variable(variable_ptr, value, IS_CV);
- if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ if (UNEXPECTED(0)) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+
+ /* zend_assign_to_variable() always takes care of op2, never free it! */
+ }
+
+ ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_SPEC_CV_CV_RETVAL_USED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+
+ zval *value;
+ zval *variable_ptr;
+
+ SAVE_OPLINE();
+ value = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var);
+ variable_ptr = _get_zval_ptr_cv_undef_BP_VAR_W(execute_data, opline->op1.var);
+
+ if (IS_CV == IS_VAR && UNEXPECTED(Z_ISERROR_P(variable_ptr))) {
+
+ if (UNEXPECTED(1)) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+ } else {
+ value = zend_assign_to_variable(variable_ptr, value, IS_CV);
+ if (UNEXPECTED(1)) {
ZVAL_COPY(EX_VAR(opline->result.var), value);
}
@@ -37011,51 +45939,46 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_REF_SPEC_CV_CV_HANDLER(
SAVE_OPLINE();
value_ptr = _get_zval_ptr_cv_BP_VAR_W(execute_data, opline->op2.var);
+ variable_ptr = _get_zval_ptr_cv_undef_BP_VAR_W(execute_data, opline->op1.var);
- if (IS_CV == IS_VAR && UNEXPECTED(value_ptr == NULL)) {
- zend_throw_error(NULL, "Cannot create references to/from string offsets nor overloaded objects");
-
- HANDLE_EXCEPTION();
- }
if (IS_CV == IS_VAR &&
UNEXPECTED(Z_TYPE_P(EX_VAR(opline->op1.var)) != IS_INDIRECT) &&
- UNEXPECTED(!Z_ISREF_P(EX_VAR(opline->op1.var)))) {
+ UNEXPECTED(!Z_ISREF_P(EX_VAR(opline->op1.var))) &&
+ UNEXPECTED(!Z_ISERROR_P(EX_VAR(opline->op1.var)))) {
+
zend_throw_error(NULL, "Cannot assign by reference to overloaded object");
HANDLE_EXCEPTION();
- }
- if (IS_CV == IS_VAR &&
- (value_ptr == &EG(uninitialized_zval) ||
- (opline->extended_value == ZEND_RETURNS_FUNCTION &&
- !(Z_VAR_FLAGS_P(value_ptr) & IS_VAR_RET_REF)))) {
- if (!0 && UNEXPECTED(Z_TYPE_P(EX_VAR(opline->op2.var)) != IS_INDIRECT)) { /* undo the effect of get_zval_ptr_ptr() */
- Z_TRY_ADDREF_P(value_ptr);
- }
+
+ } else if (IS_CV == IS_VAR &&
+ opline->extended_value == ZEND_RETURNS_FUNCTION &&
+ UNEXPECTED(!Z_ISREF_P(value_ptr))) {
zend_error(E_NOTICE, "Only variables should be assigned by reference");
if (UNEXPECTED(EG(exception) != NULL)) {
HANDLE_EXCEPTION();
}
- ZEND_VM_TAIL_CALL(ZEND_ASSIGN_SPEC_CV_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU));
- }
- variable_ptr = _get_zval_ptr_cv_undef_BP_VAR_W(execute_data, opline->op1.var);
- if (IS_CV == IS_VAR && UNEXPECTED(variable_ptr == NULL)) {
- zend_throw_error(NULL, "Cannot create references to/from string offsets nor overloaded objects");
+ value_ptr = zend_assign_to_variable(variable_ptr, value_ptr, IS_CV);
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value_ptr);
+ }
+ /* zend_assign_to_variable() always takes care of op2, never free it! */
- HANDLE_EXCEPTION();
- }
- if ((IS_CV == IS_VAR && UNEXPECTED(variable_ptr == &EG(error_zval))) ||
- (IS_CV == IS_VAR && UNEXPECTED(value_ptr == &EG(error_zval)))) {
- variable_ptr = &EG(uninitialized_zval);
} else {
- zend_assign_to_variable_reference(variable_ptr, value_ptr);
- }
- if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
- ZVAL_COPY(EX_VAR(opline->result.var), variable_ptr);
- }
+ if ((IS_CV == IS_VAR && UNEXPECTED(Z_ISERROR_P(variable_ptr))) ||
+ (IS_CV == IS_VAR && UNEXPECTED(Z_ISERROR_P(value_ptr)))) {
+ variable_ptr = &EG(uninitialized_zval);
+ } else {
+ zend_assign_to_variable_reference(variable_ptr, value_ptr);
+ }
+
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), variable_ptr);
+ }
+ }
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
@@ -37166,7 +46089,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_SPEC_CV_CV_HA
object = _get_zval_ptr_cv_undef(execute_data, opline->op1.var);
- if (IS_CV == IS_UNUSED && UNEXPECTED(Z_OBJ_P(object) == NULL)) {
+ if (IS_CV == IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
HANDLE_EXCEPTION();
@@ -37226,6 +46149,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_SPEC_CV_CV_HA
EXPECTED(obj == orig_obj)) {
CACHE_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(function_name), called_scope, fbc);
}
+ if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!fbc->op_array.run_time_cache)) {
+ init_func_run_time_cache(&fbc->op_array);
+ }
}
call_info = ZEND_CALL_NESTED_FUNCTION;
@@ -37330,11 +46256,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ADD_ARRAY_ELEMENT_SPEC_CV_CV_H
if ((IS_CV == IS_VAR || IS_CV == IS_CV) &&
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_throw_error(NULL, "Cannot create references to/from string offsets");
- zend_array_destroy(Z_ARRVAL_P(EX_VAR(opline->result.var)));
- HANDLE_EXCEPTION();
- }
ZVAL_MAKE_REF(expr_ptr);
Z_ADDREF_P(expr_ptr);
@@ -37343,10 +46264,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ADD_ARRAY_ELEMENT_SPEC_CV_CV_H
if (IS_CV == IS_TMP_VAR) {
/* pass */
} else if (IS_CV == IS_CONST) {
- 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;
+ if (Z_REFCOUNTED_P(expr_ptr)) {
+ Z_ADDREF_P(expr_ptr);
}
} else if (IS_CV == IS_CV) {
ZVAL_DEREF(expr_ptr);
@@ -37460,21 +46379,11 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_UNSET_DIM_SPEC_CV_CV_HANDLER(Z
zend_string *key;
SAVE_OPLINE();
- container = _get_zval_ptr_cv_BP_VAR_UNSET(execute_data, opline->op1.var);
- if (IS_CV == IS_UNUSED && UNEXPECTED(Z_OBJ_P(container) == NULL)) {
- zend_throw_error(NULL, "Using $this when not in object context");
-
- HANDLE_EXCEPTION();
- }
- if (IS_CV == IS_VAR && UNEXPECTED(container == NULL)) {
- zend_throw_error(NULL, "Cannot unset string offsets");
-
- HANDLE_EXCEPTION();
- }
+ container = _get_zval_ptr_cv_undef_BP_VAR_UNSET(execute_data, opline->op1.var);
offset = _get_zval_ptr_cv_undef(execute_data, opline->op2.var);
do {
- if (IS_CV != IS_UNUSED && EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) {
+ if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) {
HashTable *ht;
unset_dim_array:
@@ -37524,16 +46433,19 @@ num_index_dim:
zend_error(E_WARNING, "Illegal offset type in unset");
}
break;
- } else if (IS_CV != IS_UNUSED && Z_ISREF_P(container)) {
+ } else if (Z_ISREF_P(container)) {
container = Z_REFVAL_P(container);
if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) {
goto unset_dim_array;
}
}
+ if (IS_CV == IS_CV && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) {
+ container = GET_OP1_UNDEF_CV(container, BP_VAR_R);
+ }
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 || EXPECTED(Z_TYPE_P(container) == IS_OBJECT)) {
+ if (EXPECTED(Z_TYPE_P(container) == IS_OBJECT)) {
if (UNEXPECTED(Z_OBJ_HT_P(container)->unset_dimension == NULL)) {
zend_throw_error(NULL, "Cannot use object as array");
} else {
@@ -37557,16 +46469,11 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_UNSET_OBJ_SPEC_CV_CV_HANDLER(Z
SAVE_OPLINE();
container = _get_zval_ptr_cv_BP_VAR_UNSET(execute_data, opline->op1.var);
- if (IS_CV == IS_UNUSED && UNEXPECTED(Z_OBJ_P(container) == NULL)) {
+ if (IS_CV == IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
HANDLE_EXCEPTION();
}
- if (IS_CV == IS_VAR && UNEXPECTED(container == NULL)) {
- zend_throw_error(NULL, "Cannot unset string offsets");
-
- HANDLE_EXCEPTION();
- }
offset = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var);
do {
@@ -37601,17 +46508,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ISSET_ISEMPTY_DIM_OBJ_SPEC_CV_
zval *offset;
SAVE_OPLINE();
- container = _get_zval_ptr_cv_BP_VAR_IS(execute_data, opline->op1.var);
-
- if (IS_CV == IS_UNUSED && UNEXPECTED(Z_OBJ_P(container) == NULL)) {
- zend_throw_error(NULL, "Using $this when not in object context");
-
- HANDLE_EXCEPTION();
- }
-
+ container = _get_zval_ptr_cv_undef(execute_data, opline->op1.var);
offset = _get_zval_ptr_cv_undef(execute_data, opline->op2.var);
- if (IS_CV != IS_UNUSED && EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) {
+ if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) {
HashTable *ht;
zval *value;
zend_string *str;
@@ -37678,8 +46578,7 @@ num_index_prop:
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 ((IS_CV != IS_CONST && EXPECTED(Z_TYPE_P(container) == IS_OBJECT))) {
if (EXPECTED(Z_OBJ_HT_P(container)->has_dimension)) {
result =
((opline->extended_value & ZEND_ISSET) == 0) ^
@@ -37694,6 +46593,9 @@ num_index_prop:
if (EXPECTED(Z_TYPE_P(offset) == IS_LONG)) {
lval = Z_LVAL_P(offset);
isset_str_offset:
+ if (UNEXPECTED(lval < 0)) { /* Handle negative offset */
+ lval += (zend_long)Z_STRLEN_P(container);
+ }
if (EXPECTED(lval >= 0) && (size_t)lval < Z_STRLEN_P(container)) {
if (opline->extended_value & ZEND_ISSET) {
result = 1;
@@ -37739,7 +46641,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ISSET_ISEMPTY_PROP_OBJ_SPEC_CV
SAVE_OPLINE();
container = _get_zval_ptr_cv_BP_VAR_IS(execute_data, opline->op1.var);
- if (IS_CV == IS_UNUSED && UNEXPECTED(Z_OBJ_P(container) == NULL)) {
+ if (IS_CV == IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
HANDLE_EXCEPTION();
@@ -37801,7 +46703,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_CV_CV_HANDLER(ZEND_
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) {
+ if (IS_CV & (IS_CONST|IS_TMP_VAR)) {
zval *value;
zend_error(E_NOTICE, "Only variable references should be yielded by reference");
@@ -37809,25 +46711,19 @@ 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 (IS_CV == IS_CONST) {
- if (UNEXPECTED(Z_OPT_COPYABLE(generator->value))) {
- zval_copy_ctor_func(&generator->value);
+ if (UNEXPECTED(Z_OPT_REFCOUNTED(generator->value))) {
+ Z_ADDREF(generator->value);
}
}
} else {
zval *value_ptr = _get_zval_ptr_cv_BP_VAR_W(execute_data, opline->op1.var);
- if (IS_CV == IS_VAR && UNEXPECTED(value_ptr == NULL)) {
- zend_throw_error(NULL, "Cannot yield string offsets by reference");
-
- HANDLE_EXCEPTION();
- }
-
/* If a function call result is yielded and the function did
* not return by reference we throw a notice. */
if (IS_CV == IS_VAR &&
(value_ptr == &EG(uninitialized_zval) ||
(opline->extended_value == ZEND_RETURNS_FUNCTION &&
- !(Z_VAR_FLAGS_P(value_ptr) & IS_VAR_RET_REF)))) {
+ !Z_ISREF_P(value_ptr)))) {
zend_error(E_NOTICE, "Only variable references should be yielded by reference");
} else {
ZVAL_MAKE_REF(value_ptr);
@@ -37841,8 +46737,8 @@ 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_COPY_VALUE(&generator->value, value);
- if (UNEXPECTED(Z_OPT_COPYABLE(generator->value))) {
- zval_copy_ctor_func(&generator->value);
+ if (UNEXPECTED(Z_OPT_REFCOUNTED(generator->value))) {
+ Z_ADDREF(generator->value);
}
} else if (IS_CV == IS_TMP_VAR) {
ZVAL_COPY_VALUE(&generator->value, value);
@@ -37869,8 +46765,8 @@ 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_COPY_VALUE(&generator->key, key);
- if (UNEXPECTED(Z_OPT_COPYABLE(generator->key))) {
- zval_copy_ctor_func(&generator->key);
+ if (UNEXPECTED(Z_OPT_REFCOUNTED(generator->key))) {
+ Z_ADDREF(generator->key);
}
} else if (IS_CV == IS_TMP_VAR) {
ZVAL_COPY_VALUE(&generator->key, key);
@@ -37915,6 +46811,54 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_CV_CV_HANDLER(ZEND_
ZEND_VM_RETURN();
}
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_DIM_R_INDEX_SPEC_CV_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+
+ zval *container, *dim, *value;
+ zend_long offset;
+
+ container = _get_zval_ptr_cv_undef(execute_data, opline->op1.var);
+ dim = _get_zval_ptr_cv_undef(execute_data, opline->op2.var);
+ if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) {
+fetch_dim_r_index_array:
+ if (EXPECTED(Z_TYPE_P(dim) == IS_LONG)) {
+ offset = Z_LVAL_P(dim);
+ } else {
+ offset = zval_get_long(dim);
+ }
+ ZEND_HASH_INDEX_FIND(Z_ARRVAL_P(container), offset, value, fetch_dim_r_index_undef);
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ if (IS_CV & (IS_TMP_VAR|IS_VAR)) {
+ SAVE_OPLINE();
+
+ ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
+ } else {
+ ZEND_VM_NEXT_OPCODE();
+ }
+ } else if (IS_CV != IS_CONST && EXPECTED(Z_TYPE_P(container) == IS_REFERENCE)) {
+ container = Z_REFVAL_P(container);
+ if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) {
+ goto fetch_dim_r_index_array;
+ } else {
+ goto fetch_dim_r_index_slow;
+ }
+ } else {
+fetch_dim_r_index_slow:
+ SAVE_OPLINE();
+ zend_fetch_dimension_address_read_R_slow(EX_VAR(opline->result.var), container, dim);
+
+ ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
+ }
+
+fetch_dim_r_index_undef:
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ SAVE_OPLINE();
+ zend_error(E_NOTICE, "Undefined offset: " ZEND_LONG_FMT, offset);
+
+ ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
+}
+
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ADD_SPEC_CV_TMPVAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
@@ -38180,7 +47124,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CONCAT_SPEC_CV_TMPVAR_HANDLER(
!ZSTR_IS_INTERNED(op1_str) && GC_REFCOUNT(op1_str) == 1) {
size_t len = ZSTR_LEN(op1_str);
- str = zend_string_realloc(op1_str, len + ZSTR_LEN(op2_str), 0);
+ str = zend_string_extend(op1_str, len + ZSTR_LEN(op2_str), 0);
memcpy(ZSTR_VAL(str) + len, ZSTR_VAL(op2_str), ZSTR_LEN(op2_str)+1);
ZVAL_NEW_STR(EX_VAR(opline->result.var), str);
break;
@@ -38528,7 +47472,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_binary_assign_op_obj_helper_SP
SAVE_OPLINE();
object = _get_zval_ptr_cv_BP_VAR_RW(execute_data, opline->op1.var);
- if (IS_CV == IS_UNUSED && UNEXPECTED(Z_OBJ_P(object) == NULL)) {
+ if (IS_CV == IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
FREE_UNFETCHED_OP((opline+1)->op1_type, (opline+1)->op1.var);
zval_ptr_dtor_nogc(EX_VAR(opline->op2.var));
@@ -38537,13 +47481,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_binary_assign_op_obj_helper_SP
property = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2);
- if (IS_CV == IS_VAR && UNEXPECTED(object == NULL)) {
- zend_throw_error(NULL, "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();
- }
-
do {
value = get_zval_ptr_r((opline+1)->op1_type, (opline+1)->op1, execute_data, &free_op_data1);
@@ -38561,7 +47498,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_binary_assign_op_obj_helper_SP
/* here we are sure we are dealing with an object */
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)) {
- if (UNEXPECTED(zptr == &EG(error_zval))) {
+ if (UNEXPECTED(Z_ISERROR_P(zptr))) {
if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
ZVAL_NULL(EX_VAR(opline->result.var));
}
@@ -38590,66 +47527,85 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_binary_assign_op_dim_helper_SP
{
USE_OPLINE
zend_free_op free_op2, free_op_data1;
- zval *var_ptr, rv;
+ zval *var_ptr;
zval *value, *container, *dim;
SAVE_OPLINE();
- container = _get_zval_ptr_cv_BP_VAR_RW(execute_data, opline->op1.var);
- if (IS_CV == IS_UNUSED && UNEXPECTED(Z_OBJ_P(container) == NULL)) {
- zend_throw_error(NULL, "Using $this when not in object context");
- FREE_UNFETCHED_OP((opline+1)->op1_type, (opline+1)->op1.var);
- zval_ptr_dtor_nogc(EX_VAR(opline->op2.var));
- HANDLE_EXCEPTION();
- }
- if (IS_CV == IS_VAR && UNEXPECTED(container == NULL)) {
- zend_throw_error(NULL, "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();
- }
+ container = _get_zval_ptr_cv_undef_BP_VAR_RW(execute_data, opline->op1.var);
- dim = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2);
+ if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) {
+assign_dim_op_array:
+ SEPARATE_ARRAY(container);
+assign_dim_op_new_array:
+ if ((IS_TMP_VAR|IS_VAR) == IS_UNUSED) {
+ var_ptr = zend_hash_next_index_insert(Z_ARRVAL_P(container), &EG(uninitialized_zval));
+ if (UNEXPECTED(!var_ptr)) {
+ zend_error(E_WARNING, "Cannot add element to the array as the next element is already occupied");
+ goto assign_dim_op_ret_null;
+ }
+ } else {
+ dim = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2);
- do {
- if (IS_CV == IS_UNUSED || UNEXPECTED(Z_TYPE_P(container) != IS_ARRAY)) {
- if (IS_CV != IS_UNUSED) {
- ZVAL_DEREF(container);
+ if ((IS_TMP_VAR|IS_VAR) == IS_CONST) {
+ var_ptr = zend_fetch_dimension_address_inner_RW_CONST(Z_ARRVAL_P(container), dim);
+ } else {
+ var_ptr = zend_fetch_dimension_address_inner_RW(Z_ARRVAL_P(container), dim);
}
- if (IS_CV == IS_UNUSED || EXPECTED(Z_TYPE_P(container) == IS_OBJECT)) {
- value = get_zval_ptr_r((opline+1)->op1_type, (opline+1)->op1, execute_data, &free_op_data1);
- zend_binary_assign_op_obj_dim(container, dim, value, UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL, binary_op);
- break;
+ if (UNEXPECTED(!var_ptr)) {
+ goto assign_dim_op_ret_null;
}
+ ZVAL_DEREF(var_ptr);
+ SEPARATE_ZVAL_NOREF(var_ptr);
}
- zend_fetch_dimension_address_RW(&rv, container, dim, (IS_TMP_VAR|IS_VAR));
value = get_zval_ptr_r((opline+1)->op1_type, (opline+1)->op1, execute_data, &free_op_data1);
- ZEND_ASSERT(Z_TYPE(rv) == IS_INDIRECT);
- var_ptr = Z_INDIRECT(rv);
- if (UNEXPECTED(var_ptr == NULL)) {
- zend_throw_error(NULL, "Cannot use assign-op operators with overloaded objects nor string offsets");
- zval_ptr_dtor_nogc(free_op2);
- FREE_OP(free_op_data1);
+ binary_op(var_ptr, var_ptr, value);
- HANDLE_EXCEPTION();
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), var_ptr);
}
-
- if (UNEXPECTED(var_ptr == &EG(error_zval))) {
- if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
- ZVAL_NULL(EX_VAR(opline->result.var));
+ } else {
+ if (EXPECTED(Z_ISREF_P(container))) {
+ container = Z_REFVAL_P(container);
+ if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) {
+ goto assign_dim_op_array;
}
- } else {
- ZVAL_DEREF(var_ptr);
- SEPARATE_ZVAL_NOREF(var_ptr);
+ } else if (IS_CV == IS_CV && UNEXPECTED(Z_TYPE_INFO_P(container) == IS_UNDEF)) {
+ container = GET_OP1_UNDEF_CV(container, BP_VAR_RW);
+assign_dim_op_convert_to_array:
+ ZVAL_NEW_ARR(container);
+ zend_hash_init(Z_ARRVAL_P(container), 8, NULL, ZVAL_PTR_DTOR, 0);
+ goto assign_dim_op_new_array;
+ }
- binary_op(var_ptr, var_ptr, value);
+ dim = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2);
- if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
- ZVAL_COPY(EX_VAR(opline->result.var), var_ptr);
+ if (EXPECTED(Z_TYPE_P(container) == IS_OBJECT)) {
+ value = get_zval_ptr_r((opline+1)->op1_type, (opline+1)->op1, execute_data, &free_op_data1);
+ zend_binary_assign_op_obj_dim(container, dim, value, UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL, binary_op);
+ } else {
+ if (UNEXPECTED(Z_TYPE_P(container) == IS_STRING)) {
+ if ((IS_TMP_VAR|IS_VAR) == IS_UNUSED) {
+ zend_throw_error(NULL, "[] operator not supported for strings");
+ } else {
+ zend_check_string_offset(dim, BP_VAR_RW);
+ zend_wrong_string_offset();
+ }
+ } else if (EXPECTED(Z_TYPE_P(container) <= IS_FALSE)) {
+ goto assign_dim_op_convert_to_array;
+ } else {
+ if (UNEXPECTED(IS_CV != IS_VAR || EXPECTED(!Z_ISERROR_P(container)))) {
+ zend_error(E_WARNING, "Cannot use a scalar value as an array");
+ }
+assign_dim_op_ret_null:
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
}
+ value = get_zval_ptr_r((opline+1)->op1_type, (opline+1)->op1, execute_data, &free_op_data1);
}
- } while (0);
+ }
zval_ptr_dtor_nogc(free_op2);
FREE_OP(free_op_data1);
@@ -38668,13 +47624,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_binary_assign_op_helper_SPEC_C
value = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2);
var_ptr = _get_zval_ptr_cv_BP_VAR_RW(execute_data, opline->op1.var);
- if (IS_CV == IS_VAR && UNEXPECTED(var_ptr == NULL)) {
- zend_throw_error(NULL, "Cannot use assign-op operators with overloaded objects nor string offsets");
- zval_ptr_dtor_nogc(free_op2);
- HANDLE_EXCEPTION();
- }
-
- if (IS_CV == IS_VAR && UNEXPECTED(var_ptr == &EG(error_zval))) {
+ if (IS_CV == IS_VAR && UNEXPECTED(Z_ISERROR_P(var_ptr))) {
if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
ZVAL_NULL(EX_VAR(opline->result.var));
}
@@ -38700,15 +47650,52 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_ADD_SPEC_CV_TMPVAR_HAND
USE_OPLINE
# if 0 || (IS_CV != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
+ if (EXPECTED(1)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_CV_TMPVAR(add_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_TMPVAR(add_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_TMPVAR(add_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_TMPVAR(add_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_ADD_SPEC_CV_TMPVAR_DIM_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || ((IS_TMP_VAR|IS_VAR) != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_CV != IS_UNUSED)
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_CV_TMPVAR(add_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+ if (EXPECTED(1)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_TMPVAR(add_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_TMPVAR(add_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_TMPVAR(add_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_ADD_SPEC_CV_TMPVAR_OBJ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || ((IS_TMP_VAR|IS_VAR) != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_CV != IS_UNUSED)
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_CV_TMPVAR(add_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_TMPVAR(add_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_TMPVAR(add_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_TMPVAR(add_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#else
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_TMPVAR(add_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#endif
@@ -38720,15 +47707,52 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_SUB_SPEC_CV_TMPVAR_HAND
USE_OPLINE
# if 0 || (IS_CV != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
+ if (EXPECTED(1)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_CV_TMPVAR(sub_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_TMPVAR(sub_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_TMPVAR(sub_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_TMPVAR(sub_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_SUB_SPEC_CV_TMPVAR_DIM_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || ((IS_TMP_VAR|IS_VAR) != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_CV != IS_UNUSED)
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_CV_TMPVAR(sub_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(1)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_TMPVAR(sub_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_TMPVAR(sub_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_TMPVAR(sub_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_SUB_SPEC_CV_TMPVAR_OBJ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || ((IS_TMP_VAR|IS_VAR) != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_CV != IS_UNUSED)
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_CV_TMPVAR(sub_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_TMPVAR(sub_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_TMPVAR(sub_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_TMPVAR(sub_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#else
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_TMPVAR(sub_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#endif
@@ -38740,15 +47764,52 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_MUL_SPEC_CV_TMPVAR_HAND
USE_OPLINE
# if 0 || (IS_CV != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
+ if (EXPECTED(1)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_CV_TMPVAR(mul_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_TMPVAR(mul_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_TMPVAR(mul_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_TMPVAR(mul_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_MUL_SPEC_CV_TMPVAR_DIM_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || ((IS_TMP_VAR|IS_VAR) != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_CV != IS_UNUSED)
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_CV_TMPVAR(mul_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+ if (EXPECTED(1)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_TMPVAR(mul_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_TMPVAR(mul_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_TMPVAR(mul_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_MUL_SPEC_CV_TMPVAR_OBJ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || ((IS_TMP_VAR|IS_VAR) != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_CV != IS_UNUSED)
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_CV_TMPVAR(mul_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_TMPVAR(mul_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_TMPVAR(mul_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_TMPVAR(mul_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#else
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_TMPVAR(mul_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#endif
@@ -38760,15 +47821,52 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIV_SPEC_CV_TMPVAR_HAND
USE_OPLINE
# if 0 || (IS_CV != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
+ if (EXPECTED(1)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_CV_TMPVAR(div_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_TMPVAR(div_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_TMPVAR(div_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_TMPVAR(div_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIV_SPEC_CV_TMPVAR_DIM_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || ((IS_TMP_VAR|IS_VAR) != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_CV != IS_UNUSED)
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_CV_TMPVAR(div_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(1)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_TMPVAR(div_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_TMPVAR(div_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_TMPVAR(div_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIV_SPEC_CV_TMPVAR_OBJ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || ((IS_TMP_VAR|IS_VAR) != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_CV != IS_UNUSED)
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_CV_TMPVAR(div_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_TMPVAR(div_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_TMPVAR(div_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_TMPVAR(div_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#else
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_TMPVAR(div_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#endif
@@ -38780,15 +47878,52 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_MOD_SPEC_CV_TMPVAR_HAND
USE_OPLINE
# if 0 || (IS_CV != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
+ if (EXPECTED(1)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_CV_TMPVAR(mod_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_TMPVAR(mod_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_TMPVAR(mod_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_TMPVAR(mod_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_MOD_SPEC_CV_TMPVAR_DIM_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || ((IS_TMP_VAR|IS_VAR) != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_CV != IS_UNUSED)
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_CV_TMPVAR(mod_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(1)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_TMPVAR(mod_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_TMPVAR(mod_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_TMPVAR(mod_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_TMPVAR(mod_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_MOD_SPEC_CV_TMPVAR_OBJ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || ((IS_TMP_VAR|IS_VAR) != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_CV != IS_UNUSED)
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_CV_TMPVAR(mod_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_TMPVAR(mod_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_TMPVAR(mod_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#else
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_TMPVAR(mod_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#endif
@@ -38800,15 +47935,52 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_SL_SPEC_CV_TMPVAR_HANDL
USE_OPLINE
# if 0 || (IS_CV != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
+ if (EXPECTED(1)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_CV_TMPVAR(shift_left_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_TMPVAR(shift_left_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_TMPVAR(shift_left_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_TMPVAR(shift_left_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_SL_SPEC_CV_TMPVAR_DIM_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || ((IS_TMP_VAR|IS_VAR) != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_CV != IS_UNUSED)
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_CV_TMPVAR(shift_left_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(1)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_TMPVAR(shift_left_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_TMPVAR(shift_left_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_TMPVAR(shift_left_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_SL_SPEC_CV_TMPVAR_OBJ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || ((IS_TMP_VAR|IS_VAR) != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_CV != IS_UNUSED)
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_CV_TMPVAR(shift_left_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_TMPVAR(shift_left_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_TMPVAR(shift_left_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_TMPVAR(shift_left_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#else
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_TMPVAR(shift_left_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#endif
@@ -38820,15 +47992,52 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_SR_SPEC_CV_TMPVAR_HANDL
USE_OPLINE
# if 0 || (IS_CV != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
+ if (EXPECTED(1)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_CV_TMPVAR(shift_right_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_TMPVAR(shift_right_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_TMPVAR(shift_right_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_TMPVAR(shift_right_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_SR_SPEC_CV_TMPVAR_DIM_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || ((IS_TMP_VAR|IS_VAR) != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_CV != IS_UNUSED)
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_CV_TMPVAR(shift_right_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+ if (EXPECTED(1)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_TMPVAR(shift_right_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_TMPVAR(shift_right_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_TMPVAR(shift_right_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_SR_SPEC_CV_TMPVAR_OBJ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || ((IS_TMP_VAR|IS_VAR) != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_CV != IS_UNUSED)
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_CV_TMPVAR(shift_right_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_TMPVAR(shift_right_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_TMPVAR(shift_right_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_TMPVAR(shift_right_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#else
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_TMPVAR(shift_right_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#endif
@@ -38840,15 +48049,52 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_CONCAT_SPEC_CV_TMPVAR_H
USE_OPLINE
# if 0 || (IS_CV != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
+ if (EXPECTED(1)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_CV_TMPVAR(concat_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_TMPVAR(concat_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_TMPVAR(concat_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_TMPVAR(concat_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_CONCAT_SPEC_CV_TMPVAR_DIM_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || ((IS_TMP_VAR|IS_VAR) != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_CV != IS_UNUSED)
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_CV_TMPVAR(concat_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(1)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_TMPVAR(concat_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_TMPVAR(concat_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_TMPVAR(concat_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_CONCAT_SPEC_CV_TMPVAR_OBJ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || ((IS_TMP_VAR|IS_VAR) != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_CV != IS_UNUSED)
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_CV_TMPVAR(concat_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_TMPVAR(concat_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_TMPVAR(concat_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_TMPVAR(concat_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#else
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_TMPVAR(concat_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#endif
@@ -38860,15 +48106,52 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_BW_OR_SPEC_CV_TMPVAR_HA
USE_OPLINE
# if 0 || (IS_CV != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
+ if (EXPECTED(1)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_CV_TMPVAR(bitwise_or_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_TMPVAR(bitwise_or_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_TMPVAR(bitwise_or_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_TMPVAR(bitwise_or_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_BW_OR_SPEC_CV_TMPVAR_DIM_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || ((IS_TMP_VAR|IS_VAR) != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_CV != IS_UNUSED)
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_CV_TMPVAR(bitwise_or_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(1)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_TMPVAR(bitwise_or_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_TMPVAR(bitwise_or_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_TMPVAR(bitwise_or_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_TMPVAR(bitwise_or_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_BW_OR_SPEC_CV_TMPVAR_OBJ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || ((IS_TMP_VAR|IS_VAR) != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_CV != IS_UNUSED)
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_CV_TMPVAR(bitwise_or_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_TMPVAR(bitwise_or_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_TMPVAR(bitwise_or_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#else
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_TMPVAR(bitwise_or_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#endif
@@ -38880,15 +48163,52 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_BW_AND_SPEC_CV_TMPVAR_H
USE_OPLINE
# if 0 || (IS_CV != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
+ if (EXPECTED(1)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_CV_TMPVAR(bitwise_and_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_TMPVAR(bitwise_and_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_TMPVAR(bitwise_and_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_TMPVAR(bitwise_and_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_BW_AND_SPEC_CV_TMPVAR_DIM_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || ((IS_TMP_VAR|IS_VAR) != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_CV != IS_UNUSED)
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_CV_TMPVAR(bitwise_and_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(1)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_TMPVAR(bitwise_and_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_TMPVAR(bitwise_and_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_TMPVAR(bitwise_and_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_BW_AND_SPEC_CV_TMPVAR_OBJ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || ((IS_TMP_VAR|IS_VAR) != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_CV != IS_UNUSED)
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_CV_TMPVAR(bitwise_and_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_TMPVAR(bitwise_and_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_TMPVAR(bitwise_and_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_TMPVAR(bitwise_and_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#else
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_TMPVAR(bitwise_and_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#endif
@@ -38900,15 +48220,52 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_BW_XOR_SPEC_CV_TMPVAR_H
USE_OPLINE
# if 0 || (IS_CV != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
+ if (EXPECTED(1)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_CV_TMPVAR(bitwise_xor_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_TMPVAR(bitwise_xor_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_TMPVAR(bitwise_xor_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_TMPVAR(bitwise_xor_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_BW_XOR_SPEC_CV_TMPVAR_DIM_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || ((IS_TMP_VAR|IS_VAR) != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_CV != IS_UNUSED)
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_CV_TMPVAR(bitwise_xor_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(1)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_TMPVAR(bitwise_xor_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_TMPVAR(bitwise_xor_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_TMPVAR(bitwise_xor_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_BW_XOR_SPEC_CV_TMPVAR_OBJ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || ((IS_TMP_VAR|IS_VAR) != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_CV != IS_UNUSED)
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_CV_TMPVAR(bitwise_xor_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_TMPVAR(bitwise_xor_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_TMPVAR(bitwise_xor_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_TMPVAR(bitwise_xor_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#else
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_TMPVAR(bitwise_xor_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#endif
@@ -38920,15 +48277,52 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_POW_SPEC_CV_TMPVAR_HAND
USE_OPLINE
# if 0 || (IS_CV != IS_UNUSED)
- if (EXPECTED(opline->extended_value == 0)) {
+ if (EXPECTED(1)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_CV_TMPVAR(pow_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_TMPVAR(pow_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_TMPVAR(pow_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_TMPVAR(pow_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_POW_SPEC_CV_TMPVAR_DIM_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || ((IS_TMP_VAR|IS_VAR) != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_CV != IS_UNUSED)
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_CV_TMPVAR(pow_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+ if (EXPECTED(1)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_TMPVAR(pow_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
# endif
- if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_TMPVAR(pow_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#else
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_TMPVAR(pow_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+#endif
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_POW_SPEC_CV_TMPVAR_OBJ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+#if 0 || ((IS_TMP_VAR|IS_VAR) != IS_UNUSED)
+ USE_OPLINE
+
+# if 0 || (IS_CV != IS_UNUSED)
+ if (EXPECTED(0)) {
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_helper_SPEC_CV_TMPVAR(pow_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+ if (EXPECTED(0)) {
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_TMPVAR(pow_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
- } else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
- ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_TMPVAR(pow_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+# endif
+ ZEND_VM_TAIL_CALL(zend_binary_assign_op_obj_helper_SPEC_CV_TMPVAR(pow_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#else
ZEND_VM_TAIL_CALL(zend_binary_assign_op_dim_helper_SPEC_CV_TMPVAR(pow_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
#endif
@@ -38945,7 +48339,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_pre_incdec_property_helper_SPE
SAVE_OPLINE();
object = _get_zval_ptr_cv_BP_VAR_RW(execute_data, opline->op1.var);
- if (IS_CV == IS_UNUSED && UNEXPECTED(Z_OBJ_P(object) == NULL)) {
+ if (IS_CV == IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
zval_ptr_dtor_nogc(EX_VAR(opline->op2.var));
HANDLE_EXCEPTION();
@@ -38953,12 +48347,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_pre_incdec_property_helper_SPE
property = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2);
- if (IS_CV == IS_VAR && UNEXPECTED(object == NULL)) {
- zend_throw_error(NULL, "Cannot increment/decrement overloaded objects nor string offsets");
- zval_ptr_dtor_nogc(free_op2);
- HANDLE_EXCEPTION();
- }
-
do {
if (IS_CV != IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) != IS_OBJECT)) {
ZVAL_DEREF(object);
@@ -38974,7 +48362,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_pre_incdec_property_helper_SPE
/* here we are sure we are dealing with an object */
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)) {
- if (UNEXPECTED(zptr == &EG(error_zval))) {
+ if (UNEXPECTED(Z_ISERROR_P(zptr))) {
if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
ZVAL_NULL(EX_VAR(opline->result.var));
}
@@ -39030,7 +48418,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_post_incdec_property_helper_SP
SAVE_OPLINE();
object = _get_zval_ptr_cv_BP_VAR_RW(execute_data, opline->op1.var);
- if (IS_CV == IS_UNUSED && UNEXPECTED(Z_OBJ_P(object) == NULL)) {
+ if (IS_CV == IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
zval_ptr_dtor_nogc(EX_VAR(opline->op2.var));
HANDLE_EXCEPTION();
@@ -39038,12 +48426,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_post_incdec_property_helper_SP
property = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2);
- if (IS_CV == IS_VAR && UNEXPECTED(object == NULL)) {
- zend_throw_error(NULL, "Cannot increment/decrement overloaded objects nor string offsets");
- zval_ptr_dtor_nogc(free_op2);
- HANDLE_EXCEPTION();
- }
-
do {
if (IS_CV != IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) != IS_OBJECT)) {
ZVAL_DEREF(object);
@@ -39058,7 +48440,7 @@ 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)) {
- if (UNEXPECTED(zptr == &EG(error_zval))) {
+ if (UNEXPECTED(Z_ISERROR_P(zptr))) {
ZVAL_NULL(EX_VAR(opline->result.var));
} else {
if (EXPECTED(Z_TYPE_P(zptr) == IS_LONG)) {
@@ -39103,11 +48485,33 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_DIM_R_SPEC_CV_TMPVAR_HAN
{
USE_OPLINE
zend_free_op free_op2;
- zval *container;
+ zval *container, *dim, *value, *result;
SAVE_OPLINE();
- container = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var);
- zend_fetch_dimension_address_read_R(EX_VAR(opline->result.var), container, _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2), (IS_TMP_VAR|IS_VAR));
+ container = _get_zval_ptr_cv_undef(execute_data, opline->op1.var);
+ dim = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2);
+ if (IS_CV != IS_CONST) {
+ if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) {
+fetch_dim_r_array:
+ value = zend_fetch_dimension_address_inner(Z_ARRVAL_P(container), dim, (IS_TMP_VAR|IS_VAR), BP_VAR_R);
+ result = EX_VAR(opline->result.var);
+ ZVAL_COPY(result, value);
+ } else if (EXPECTED(Z_TYPE_P(container) == IS_REFERENCE)) {
+ container = Z_REFVAL_P(container);
+ if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) {
+ goto fetch_dim_r_array;
+ } else {
+ goto fetch_dim_r_slow;
+ }
+ } else {
+fetch_dim_r_slow:
+ result = EX_VAR(opline->result.var);
+ zend_fetch_dimension_address_read_R_slow(result, container, dim);
+ }
+ } else {
+ result = EX_VAR(opline->result.var);
+ zend_fetch_dimension_address_read_R(result, container, dim, (IS_TMP_VAR|IS_VAR));
+ }
zval_ptr_dtor_nogc(free_op2);
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
@@ -39122,14 +48526,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_DIM_W_SPEC_CV_TMPVAR_HAN
SAVE_OPLINE();
container = _get_zval_ptr_cv_undef_BP_VAR_W(execute_data, opline->op1.var);
- if (IS_CV == IS_VAR && UNEXPECTED(container == NULL)) {
- zend_throw_error(NULL, "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));
zval_ptr_dtor_nogc(free_op2);
if (IS_CV == IS_VAR && READY_TO_DESTROY(free_op1)) {
- EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var), 1);
+ EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var));
}
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
@@ -39142,16 +48542,12 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_DIM_RW_SPEC_CV_TMPVAR_HA
zval *container;
SAVE_OPLINE();
- container = _get_zval_ptr_cv_BP_VAR_RW(execute_data, opline->op1.var);
+ container = _get_zval_ptr_cv_undef_BP_VAR_RW(execute_data, opline->op1.var);
- if (IS_CV == IS_VAR && UNEXPECTED(container == NULL)) {
- zend_throw_error(NULL, "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));
zval_ptr_dtor_nogc(free_op2);
if (IS_CV == IS_VAR && READY_TO_DESTROY(free_op1)) {
- EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var), 1);
+ EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var));
}
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
@@ -39164,7 +48560,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_DIM_IS_SPEC_CV_TMPVAR_HA
zval *container;
SAVE_OPLINE();
- container = _get_zval_ptr_cv_BP_VAR_IS(execute_data, opline->op1.var);
+ container = _get_zval_ptr_cv_undef(execute_data, opline->op1.var);
zend_fetch_dimension_address_read_IS(EX_VAR(opline->result.var), container, _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2), (IS_TMP_VAR|IS_VAR));
zval_ptr_dtor_nogc(free_op2);
@@ -39180,21 +48576,16 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_DIM_FUNC_ARG_SPEC_CV_TMP
SAVE_OPLINE();
if (zend_is_by_ref_func_arg_fetch(opline, EX(call))) {
- if (IS_CV == IS_CONST || IS_CV == IS_TMP_VAR) {
+ if ((IS_CV & (IS_CONST|IS_TMP_VAR))) {
zend_throw_error(NULL, "Cannot use temporary expression in write context");
zval_ptr_dtor_nogc(EX_VAR(opline->op2.var));
HANDLE_EXCEPTION();
}
container = _get_zval_ptr_cv_undef_BP_VAR_W(execute_data, opline->op1.var);
- if (IS_CV == IS_VAR && UNEXPECTED(container == NULL)) {
- zend_throw_error(NULL, "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));
if (IS_CV == IS_VAR && READY_TO_DESTROY(free_op1)) {
- EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var), 1);
+ EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var));
}
zval_ptr_dtor_nogc(free_op2);
@@ -39205,7 +48596,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_DIM_FUNC_ARG_SPEC_CV_TMP
HANDLE_EXCEPTION();
}
- container = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var);
+ container = _get_zval_ptr_cv_undef(execute_data, opline->op1.var);
zend_fetch_dimension_address_read_R(EX_VAR(opline->result.var), container, _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2), (IS_TMP_VAR|IS_VAR));
zval_ptr_dtor_nogc(free_op2);
@@ -39220,17 +48611,12 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_DIM_UNSET_SPEC_CV_TMPVAR
zval *container;
SAVE_OPLINE();
- container = _get_zval_ptr_cv_BP_VAR_UNSET(execute_data, opline->op1.var);
+ container = _get_zval_ptr_cv_undef_BP_VAR_UNSET(execute_data, opline->op1.var);
- if (IS_CV == IS_VAR && UNEXPECTED(container == NULL)) {
- zend_throw_error(NULL, "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));
zval_ptr_dtor_nogc(free_op2);
if (IS_CV == IS_VAR && READY_TO_DESTROY(free_op1)) {
- EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var), 1);
+ EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var));
}
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
@@ -39247,7 +48633,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_R_SPEC_CV_TMPVAR_HAN
SAVE_OPLINE();
container = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var);
- if (IS_CV == IS_UNUSED && UNEXPECTED(Z_OBJ_P(container) == NULL)) {
+ if (IS_CV == IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
zval_ptr_dtor_nogc(EX_VAR(opline->op2.var));
HANDLE_EXCEPTION();
@@ -39320,21 +48706,16 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_W_SPEC_CV_TMPVAR_HAN
property = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2);
container = _get_zval_ptr_cv_undef_BP_VAR_W(execute_data, opline->op1.var);
- if (IS_CV == IS_UNUSED && UNEXPECTED(Z_OBJ_P(container) == NULL)) {
+ if (IS_CV == IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
zval_ptr_dtor_nogc(free_op2);
HANDLE_EXCEPTION();
}
- if (IS_CV == IS_VAR && UNEXPECTED(container == NULL)) {
- zend_throw_error(NULL, "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);
zval_ptr_dtor_nogc(free_op2);
if (IS_CV == IS_VAR && READY_TO_DESTROY(free_op1)) {
- EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var), 0);
+ EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var));
}
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
@@ -39351,20 +48732,15 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_RW_SPEC_CV_TMPVAR_HA
property = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2);
container = _get_zval_ptr_cv_BP_VAR_RW(execute_data, opline->op1.var);
- if (IS_CV == IS_UNUSED && UNEXPECTED(Z_OBJ_P(container) == NULL)) {
+ if (IS_CV == IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
zval_ptr_dtor_nogc(free_op2);
HANDLE_EXCEPTION();
}
- if (IS_CV == IS_VAR && UNEXPECTED(container == NULL)) {
- zend_throw_error(NULL, "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);
zval_ptr_dtor_nogc(free_op2);
if (IS_CV == IS_VAR && READY_TO_DESTROY(free_op1)) {
- EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var), 0);
+ EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var));
}
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
@@ -39381,7 +48757,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_IS_SPEC_CV_TMPVAR_HA
SAVE_OPLINE();
container = _get_zval_ptr_cv_BP_VAR_IS(execute_data, opline->op1.var);
- if (IS_CV == IS_UNUSED && UNEXPECTED(Z_OBJ_P(container) == NULL)) {
+ if (IS_CV == IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
zval_ptr_dtor_nogc(EX_VAR(opline->op2.var));
HANDLE_EXCEPTION();
@@ -39457,26 +48833,21 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_FUNC_ARG_SPEC_CV_TMP
property = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2);
container = _get_zval_ptr_cv_undef_BP_VAR_W(execute_data, opline->op1.var);
- if (IS_CV == IS_UNUSED && UNEXPECTED(Z_OBJ_P(container) == NULL)) {
+ if (IS_CV == IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
zval_ptr_dtor_nogc(free_op2);
HANDLE_EXCEPTION();
}
- if (IS_CV == IS_CONST || IS_CV == IS_TMP_VAR) {
+ if ((IS_CV & (IS_CONST|IS_TMP_VAR))) {
zend_throw_error(NULL, "Cannot use temporary expression in write context");
zval_ptr_dtor_nogc(free_op2);
HANDLE_EXCEPTION();
}
- if (IS_CV == IS_VAR && UNEXPECTED(container == NULL)) {
- zend_throw_error(NULL, "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);
zval_ptr_dtor_nogc(free_op2);
if (IS_CV == IS_VAR && READY_TO_DESTROY(free_op1)) {
- EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var), 0);
+ EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var));
}
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
@@ -39494,7 +48865,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_UNSET_SPEC_CV_TMPVAR
SAVE_OPLINE();
container = _get_zval_ptr_cv_BP_VAR_UNSET(execute_data, opline->op1.var);
- if (IS_CV == IS_UNUSED && UNEXPECTED(Z_OBJ_P(container) == NULL)) {
+ if (IS_CV == IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
zval_ptr_dtor_nogc(EX_VAR(opline->op2.var));
HANDLE_EXCEPTION();
@@ -39502,56 +48873,658 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_UNSET_SPEC_CV_TMPVAR
property = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2);
- if (IS_CV == IS_VAR && UNEXPECTED(container == NULL)) {
- zend_throw_error(NULL, "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);
zval_ptr_dtor_nogc(free_op2);
if (IS_CV == IS_VAR && READY_TO_DESTROY(free_op1)) {
- EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var), 0);
+ EXTRACT_ZVAL_PTR(EX_VAR(opline->result.var));
}
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_TMPVAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_LIST_SPEC_CV_TMPVAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
zend_free_op free_op2;
- zval *object;
- zval *property_name;
+ zval *container;
+
+ SAVE_OPLINE();
+ container = _get_zval_ptr_cv_undef(execute_data, opline->op1.var);
+ zend_fetch_dimension_address_read_LIST(EX_VAR(opline->result.var), container, _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2));
+ zval_ptr_dtor_nogc(free_op2);
+ ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_TMPVAR_OP_DATA_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zend_free_op free_op2;
+ zval *object, *property_name, *value, tmp;
SAVE_OPLINE();
object = _get_zval_ptr_cv_undef_BP_VAR_W(execute_data, opline->op1.var);
- if (IS_CV == IS_UNUSED && UNEXPECTED(Z_OBJ_P(object) == NULL)) {
+ if (IS_CV == IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
zval_ptr_dtor_nogc(EX_VAR(opline->op2.var));
HANDLE_EXCEPTION();
}
property_name = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2);
+ value = EX_CONSTANT((opline+1)->op1);
- if (IS_CV == IS_VAR && UNEXPECTED(object == NULL)) {
- zend_throw_error(NULL, "Cannot use string offset as an array");
- zval_ptr_dtor_nogc(free_op2);
+ if (IS_CV != IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) != IS_OBJECT)) {
+ do {
+ if (Z_ISREF_P(object)) {
+ object = Z_REFVAL_P(object);
+ if (EXPECTED(Z_TYPE_P(object) == IS_OBJECT)) {
+ break;
+ }
+ }
+ if (EXPECTED(Z_TYPE_P(object) <= IS_FALSE ||
+ (Z_TYPE_P(object) == IS_STRING && Z_STRLEN_P(object) == 0))) {
+ zend_object *obj;
+
+ zval_ptr_dtor(object);
+ object_init(object);
+ Z_ADDREF_P(object);
+ obj = Z_OBJ_P(object);
+ zend_error(E_WARNING, "Creating default object from empty value");
+ if (GC_REFCOUNT(obj) == 1) {
+ /* the enclosing container was deleted, obj is unreferenced */
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+
+ OBJ_RELEASE(obj);
+ goto exit_assign_obj;
+ }
+ Z_DELREF_P(object);
+ } else {
+ if (IS_CV != IS_VAR || EXPECTED(!Z_ISERROR_P(object))) {
+ zend_error(E_WARNING, "Attempt to assign property of non-object");
+ }
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+
+ goto exit_assign_obj;
+ }
+ } while (0);
+ }
+
+ if ((IS_TMP_VAR|IS_VAR) == IS_CONST &&
+ EXPECTED(Z_OBJCE_P(object) == CACHED_PTR(Z_CACHE_SLOT_P(property_name)))) {
+ uint32_t prop_offset = (uint32_t)(intptr_t)CACHED_PTR(Z_CACHE_SLOT_P(property_name) + sizeof(void*));
+ zend_object *zobj = Z_OBJ_P(object);
+ zval *property;
+
+ if (EXPECTED(prop_offset != (uint32_t)ZEND_DYNAMIC_PROPERTY_OFFSET)) {
+ property = OBJ_PROP(zobj, prop_offset);
+ if (Z_TYPE_P(property) != IS_UNDEF) {
+fast_assign_obj:
+ value = zend_assign_to_variable(property, value, IS_CONST);
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+ goto exit_assign_obj;
+ }
+ } else {
+ if (EXPECTED(zobj->properties != NULL)) {
+ if (UNEXPECTED(GC_REFCOUNT(zobj->properties) > 1)) {
+ if (EXPECTED(!(GC_FLAGS(zobj->properties) & IS_ARRAY_IMMUTABLE))) {
+ GC_REFCOUNT(zobj->properties)--;
+ }
+ zobj->properties = zend_array_dup(zobj->properties);
+ }
+ property = zend_hash_find(zobj->properties, Z_STR_P(property_name));
+ if (property) {
+ goto fast_assign_obj;
+ }
+ }
+
+ if (!zobj->ce->__set) {
+
+ if (EXPECTED(zobj->properties == NULL)) {
+ rebuild_object_properties(zobj);
+ }
+ if (IS_CONST == IS_CONST) {
+ if (UNEXPECTED(Z_OPT_REFCOUNTED_P(value))) {
+ Z_ADDREF_P(value);
+ }
+ } else if (IS_CONST != IS_TMP_VAR) {
+ if (Z_ISREF_P(value)) {
+ if (IS_CONST == IS_VAR) {
+ zend_reference *ref = Z_REF_P(value);
+ if (--GC_REFCOUNT(ref) == 0) {
+ ZVAL_COPY_VALUE(&tmp, Z_REFVAL_P(value));
+ efree_size(ref, sizeof(zend_reference));
+ value = &tmp;
+ } else {
+ value = Z_REFVAL_P(value);
+ if (Z_REFCOUNTED_P(value)) {
+ Z_ADDREF_P(value);
+ }
+ }
+ } else {
+ value = Z_REFVAL_P(value);
+ if (Z_REFCOUNTED_P(value)) {
+ Z_ADDREF_P(value);
+ }
+ }
+ } else if (IS_CONST == IS_CV && Z_REFCOUNTED_P(value)) {
+ Z_ADDREF_P(value);
+ }
+ }
+ zend_hash_add_new(zobj->properties, Z_STR_P(property_name), value);
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+ goto exit_assign_obj;
+ }
+ }
+ }
+
+ if (!Z_OBJ_HT_P(object)->write_property) {
+ zend_error(E_WARNING, "Attempt to assign property of non-object");
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+
+ goto exit_assign_obj;
+ }
+
+ if (IS_CONST == IS_CV || IS_CONST == IS_VAR) {
+ ZVAL_DEREF(value);
+ }
+
+ Z_OBJ_HT_P(object)->write_property(object, property_name, value, ((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property_name)) : NULL);
+
+ if (UNEXPECTED(RETURN_VALUE_USED(opline)) && EXPECTED(!EG(exception))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+
+exit_assign_obj:
+ zval_ptr_dtor_nogc(free_op2);
+
+ /* assign_obj has two opcodes! */
+ ZEND_VM_NEXT_OPCODE_EX(1, 2);
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_TMPVAR_OP_DATA_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zend_free_op free_op2, free_op_data;
+ zval *object, *property_name, *value, tmp;
+
+ SAVE_OPLINE();
+ object = _get_zval_ptr_cv_undef_BP_VAR_W(execute_data, opline->op1.var);
+
+ if (IS_CV == IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) == IS_UNDEF)) {
+ zend_throw_error(NULL, "Using $this when not in object context");
+ zval_ptr_dtor_nogc(EX_VAR(opline->op2.var));
+ HANDLE_EXCEPTION();
+ }
+
+ property_name = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2);
+ value = _get_zval_ptr_tmp((opline+1)->op1.var, execute_data, &free_op_data);
+
+ if (IS_CV != IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) != IS_OBJECT)) {
+ do {
+ if (Z_ISREF_P(object)) {
+ object = Z_REFVAL_P(object);
+ if (EXPECTED(Z_TYPE_P(object) == IS_OBJECT)) {
+ break;
+ }
+ }
+ if (EXPECTED(Z_TYPE_P(object) <= IS_FALSE ||
+ (Z_TYPE_P(object) == IS_STRING && Z_STRLEN_P(object) == 0))) {
+ zend_object *obj;
+
+ zval_ptr_dtor(object);
+ object_init(object);
+ Z_ADDREF_P(object);
+ obj = Z_OBJ_P(object);
+ zend_error(E_WARNING, "Creating default object from empty value");
+ if (GC_REFCOUNT(obj) == 1) {
+ /* the enclosing container was deleted, obj is unreferenced */
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+ zval_ptr_dtor_nogc(free_op_data);
+ OBJ_RELEASE(obj);
+ goto exit_assign_obj;
+ }
+ Z_DELREF_P(object);
+ } else {
+ if (IS_CV != IS_VAR || EXPECTED(!Z_ISERROR_P(object))) {
+ zend_error(E_WARNING, "Attempt to assign property of non-object");
+ }
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+ zval_ptr_dtor_nogc(free_op_data);
+ goto exit_assign_obj;
+ }
+ } while (0);
+ }
+
+ if ((IS_TMP_VAR|IS_VAR) == IS_CONST &&
+ EXPECTED(Z_OBJCE_P(object) == CACHED_PTR(Z_CACHE_SLOT_P(property_name)))) {
+ uint32_t prop_offset = (uint32_t)(intptr_t)CACHED_PTR(Z_CACHE_SLOT_P(property_name) + sizeof(void*));
+ zend_object *zobj = Z_OBJ_P(object);
+ zval *property;
+
+ if (EXPECTED(prop_offset != (uint32_t)ZEND_DYNAMIC_PROPERTY_OFFSET)) {
+ property = OBJ_PROP(zobj, prop_offset);
+ if (Z_TYPE_P(property) != IS_UNDEF) {
+fast_assign_obj:
+ value = zend_assign_to_variable(property, value, IS_TMP_VAR);
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+ goto exit_assign_obj;
+ }
+ } else {
+ if (EXPECTED(zobj->properties != NULL)) {
+ if (UNEXPECTED(GC_REFCOUNT(zobj->properties) > 1)) {
+ if (EXPECTED(!(GC_FLAGS(zobj->properties) & IS_ARRAY_IMMUTABLE))) {
+ GC_REFCOUNT(zobj->properties)--;
+ }
+ zobj->properties = zend_array_dup(zobj->properties);
+ }
+ property = zend_hash_find(zobj->properties, Z_STR_P(property_name));
+ if (property) {
+ goto fast_assign_obj;
+ }
+ }
+
+ if (!zobj->ce->__set) {
+
+ if (EXPECTED(zobj->properties == NULL)) {
+ rebuild_object_properties(zobj);
+ }
+ if (IS_TMP_VAR == IS_CONST) {
+ if (UNEXPECTED(Z_OPT_REFCOUNTED_P(value))) {
+ Z_ADDREF_P(value);
+ }
+ } else if (IS_TMP_VAR != IS_TMP_VAR) {
+ if (Z_ISREF_P(value)) {
+ if (IS_TMP_VAR == IS_VAR) {
+ zend_reference *ref = Z_REF_P(value);
+ if (--GC_REFCOUNT(ref) == 0) {
+ ZVAL_COPY_VALUE(&tmp, Z_REFVAL_P(value));
+ efree_size(ref, sizeof(zend_reference));
+ value = &tmp;
+ } else {
+ value = Z_REFVAL_P(value);
+ if (Z_REFCOUNTED_P(value)) {
+ Z_ADDREF_P(value);
+ }
+ }
+ } else {
+ value = Z_REFVAL_P(value);
+ if (Z_REFCOUNTED_P(value)) {
+ Z_ADDREF_P(value);
+ }
+ }
+ } else if (IS_TMP_VAR == IS_CV && Z_REFCOUNTED_P(value)) {
+ Z_ADDREF_P(value);
+ }
+ }
+ zend_hash_add_new(zobj->properties, Z_STR_P(property_name), value);
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+ goto exit_assign_obj;
+ }
+ }
+ }
+
+ if (!Z_OBJ_HT_P(object)->write_property) {
+ zend_error(E_WARNING, "Attempt to assign property of non-object");
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+ zval_ptr_dtor_nogc(free_op_data);
+ goto exit_assign_obj;
+ }
+
+ if (IS_TMP_VAR == IS_CV || IS_TMP_VAR == IS_VAR) {
+ ZVAL_DEREF(value);
+ }
+
+ Z_OBJ_HT_P(object)->write_property(object, property_name, value, ((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property_name)) : NULL);
+
+ if (UNEXPECTED(RETURN_VALUE_USED(opline)) && EXPECTED(!EG(exception))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+ zval_ptr_dtor_nogc(free_op_data);
+exit_assign_obj:
+ zval_ptr_dtor_nogc(free_op2);
+
+ /* assign_obj has two opcodes! */
+ ZEND_VM_NEXT_OPCODE_EX(1, 2);
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_TMPVAR_OP_DATA_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zend_free_op free_op2, free_op_data;
+ zval *object, *property_name, *value, tmp;
+
+ SAVE_OPLINE();
+ object = _get_zval_ptr_cv_undef_BP_VAR_W(execute_data, opline->op1.var);
+
+ if (IS_CV == IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) == IS_UNDEF)) {
+ zend_throw_error(NULL, "Using $this when not in object context");
+ zval_ptr_dtor_nogc(EX_VAR(opline->op2.var));
+ HANDLE_EXCEPTION();
+ }
+
+ property_name = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2);
+ value = _get_zval_ptr_var((opline+1)->op1.var, execute_data, &free_op_data);
+
+ if (IS_CV != IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) != IS_OBJECT)) {
+ do {
+ if (Z_ISREF_P(object)) {
+ object = Z_REFVAL_P(object);
+ if (EXPECTED(Z_TYPE_P(object) == IS_OBJECT)) {
+ break;
+ }
+ }
+ if (EXPECTED(Z_TYPE_P(object) <= IS_FALSE ||
+ (Z_TYPE_P(object) == IS_STRING && Z_STRLEN_P(object) == 0))) {
+ zend_object *obj;
+
+ zval_ptr_dtor(object);
+ object_init(object);
+ Z_ADDREF_P(object);
+ obj = Z_OBJ_P(object);
+ zend_error(E_WARNING, "Creating default object from empty value");
+ if (GC_REFCOUNT(obj) == 1) {
+ /* the enclosing container was deleted, obj is unreferenced */
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+ zval_ptr_dtor_nogc(free_op_data);
+ OBJ_RELEASE(obj);
+ goto exit_assign_obj;
+ }
+ Z_DELREF_P(object);
+ } else {
+ if (IS_CV != IS_VAR || EXPECTED(!Z_ISERROR_P(object))) {
+ zend_error(E_WARNING, "Attempt to assign property of non-object");
+ }
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+ zval_ptr_dtor_nogc(free_op_data);
+ goto exit_assign_obj;
+ }
+ } while (0);
+ }
+
+ if ((IS_TMP_VAR|IS_VAR) == IS_CONST &&
+ EXPECTED(Z_OBJCE_P(object) == CACHED_PTR(Z_CACHE_SLOT_P(property_name)))) {
+ uint32_t prop_offset = (uint32_t)(intptr_t)CACHED_PTR(Z_CACHE_SLOT_P(property_name) + sizeof(void*));
+ zend_object *zobj = Z_OBJ_P(object);
+ zval *property;
+
+ if (EXPECTED(prop_offset != (uint32_t)ZEND_DYNAMIC_PROPERTY_OFFSET)) {
+ property = OBJ_PROP(zobj, prop_offset);
+ if (Z_TYPE_P(property) != IS_UNDEF) {
+fast_assign_obj:
+ value = zend_assign_to_variable(property, value, IS_VAR);
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+ goto exit_assign_obj;
+ }
+ } else {
+ if (EXPECTED(zobj->properties != NULL)) {
+ if (UNEXPECTED(GC_REFCOUNT(zobj->properties) > 1)) {
+ if (EXPECTED(!(GC_FLAGS(zobj->properties) & IS_ARRAY_IMMUTABLE))) {
+ GC_REFCOUNT(zobj->properties)--;
+ }
+ zobj->properties = zend_array_dup(zobj->properties);
+ }
+ property = zend_hash_find(zobj->properties, Z_STR_P(property_name));
+ if (property) {
+ goto fast_assign_obj;
+ }
+ }
+
+ if (!zobj->ce->__set) {
+
+ if (EXPECTED(zobj->properties == NULL)) {
+ rebuild_object_properties(zobj);
+ }
+ if (IS_VAR == IS_CONST) {
+ if (UNEXPECTED(Z_OPT_REFCOUNTED_P(value))) {
+ Z_ADDREF_P(value);
+ }
+ } else if (IS_VAR != IS_TMP_VAR) {
+ if (Z_ISREF_P(value)) {
+ if (IS_VAR == IS_VAR) {
+ zend_reference *ref = Z_REF_P(value);
+ if (--GC_REFCOUNT(ref) == 0) {
+ ZVAL_COPY_VALUE(&tmp, Z_REFVAL_P(value));
+ efree_size(ref, sizeof(zend_reference));
+ value = &tmp;
+ } else {
+ value = Z_REFVAL_P(value);
+ if (Z_REFCOUNTED_P(value)) {
+ Z_ADDREF_P(value);
+ }
+ }
+ } else {
+ value = Z_REFVAL_P(value);
+ if (Z_REFCOUNTED_P(value)) {
+ Z_ADDREF_P(value);
+ }
+ }
+ } else if (IS_VAR == IS_CV && Z_REFCOUNTED_P(value)) {
+ Z_ADDREF_P(value);
+ }
+ }
+ zend_hash_add_new(zobj->properties, Z_STR_P(property_name), value);
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+ goto exit_assign_obj;
+ }
+ }
+ }
+
+ if (!Z_OBJ_HT_P(object)->write_property) {
+ zend_error(E_WARNING, "Attempt to assign property of non-object");
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+ zval_ptr_dtor_nogc(free_op_data);
+ goto exit_assign_obj;
+ }
+
+ if (IS_VAR == IS_CV || IS_VAR == IS_VAR) {
+ ZVAL_DEREF(value);
+ }
+
+ Z_OBJ_HT_P(object)->write_property(object, property_name, value, ((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property_name)) : NULL);
+
+ if (UNEXPECTED(RETURN_VALUE_USED(opline)) && EXPECTED(!EG(exception))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+ zval_ptr_dtor_nogc(free_op_data);
+exit_assign_obj:
+ zval_ptr_dtor_nogc(free_op2);
+
+ /* assign_obj has two opcodes! */
+ ZEND_VM_NEXT_OPCODE_EX(1, 2);
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_TMPVAR_OP_DATA_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zend_free_op free_op2;
+ zval *object, *property_name, *value, tmp;
+
+ SAVE_OPLINE();
+ object = _get_zval_ptr_cv_undef_BP_VAR_W(execute_data, opline->op1.var);
+
+ if (IS_CV == IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) == IS_UNDEF)) {
+ zend_throw_error(NULL, "Using $this when not in object context");
+ zval_ptr_dtor_nogc(EX_VAR(opline->op2.var));
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));
+
+ property_name = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2);
+ value = _get_zval_ptr_cv_BP_VAR_R(execute_data, (opline+1)->op1.var);
+
+ if (IS_CV != IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) != IS_OBJECT)) {
+ do {
+ if (Z_ISREF_P(object)) {
+ object = Z_REFVAL_P(object);
+ if (EXPECTED(Z_TYPE_P(object) == IS_OBJECT)) {
+ break;
+ }
+ }
+ if (EXPECTED(Z_TYPE_P(object) <= IS_FALSE ||
+ (Z_TYPE_P(object) == IS_STRING && Z_STRLEN_P(object) == 0))) {
+ zend_object *obj;
+
+ zval_ptr_dtor(object);
+ object_init(object);
+ Z_ADDREF_P(object);
+ obj = Z_OBJ_P(object);
+ zend_error(E_WARNING, "Creating default object from empty value");
+ if (GC_REFCOUNT(obj) == 1) {
+ /* the enclosing container was deleted, obj is unreferenced */
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+
+ OBJ_RELEASE(obj);
+ goto exit_assign_obj;
+ }
+ Z_DELREF_P(object);
+ } else {
+ if (IS_CV != IS_VAR || EXPECTED(!Z_ISERROR_P(object))) {
+ zend_error(E_WARNING, "Attempt to assign property of non-object");
+ }
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+
+ goto exit_assign_obj;
+ }
+ } while (0);
+ }
+
+ if ((IS_TMP_VAR|IS_VAR) == IS_CONST &&
+ EXPECTED(Z_OBJCE_P(object) == CACHED_PTR(Z_CACHE_SLOT_P(property_name)))) {
+ uint32_t prop_offset = (uint32_t)(intptr_t)CACHED_PTR(Z_CACHE_SLOT_P(property_name) + sizeof(void*));
+ zend_object *zobj = Z_OBJ_P(object);
+ zval *property;
+
+ if (EXPECTED(prop_offset != (uint32_t)ZEND_DYNAMIC_PROPERTY_OFFSET)) {
+ property = OBJ_PROP(zobj, prop_offset);
+ if (Z_TYPE_P(property) != IS_UNDEF) {
+fast_assign_obj:
+ value = zend_assign_to_variable(property, value, IS_CV);
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+ goto exit_assign_obj;
+ }
+ } else {
+ if (EXPECTED(zobj->properties != NULL)) {
+ if (UNEXPECTED(GC_REFCOUNT(zobj->properties) > 1)) {
+ if (EXPECTED(!(GC_FLAGS(zobj->properties) & IS_ARRAY_IMMUTABLE))) {
+ GC_REFCOUNT(zobj->properties)--;
+ }
+ zobj->properties = zend_array_dup(zobj->properties);
+ }
+ property = zend_hash_find(zobj->properties, Z_STR_P(property_name));
+ if (property) {
+ goto fast_assign_obj;
+ }
+ }
+
+ if (!zobj->ce->__set) {
+
+ if (EXPECTED(zobj->properties == NULL)) {
+ rebuild_object_properties(zobj);
+ }
+ if (IS_CV == IS_CONST) {
+ if (UNEXPECTED(Z_OPT_REFCOUNTED_P(value))) {
+ Z_ADDREF_P(value);
+ }
+ } else if (IS_CV != IS_TMP_VAR) {
+ if (Z_ISREF_P(value)) {
+ if (IS_CV == IS_VAR) {
+ zend_reference *ref = Z_REF_P(value);
+ if (--GC_REFCOUNT(ref) == 0) {
+ ZVAL_COPY_VALUE(&tmp, Z_REFVAL_P(value));
+ efree_size(ref, sizeof(zend_reference));
+ value = &tmp;
+ } else {
+ value = Z_REFVAL_P(value);
+ if (Z_REFCOUNTED_P(value)) {
+ Z_ADDREF_P(value);
+ }
+ }
+ } else {
+ value = Z_REFVAL_P(value);
+ if (Z_REFCOUNTED_P(value)) {
+ Z_ADDREF_P(value);
+ }
+ }
+ } else if (IS_CV == IS_CV && Z_REFCOUNTED_P(value)) {
+ Z_ADDREF_P(value);
+ }
+ }
+ zend_hash_add_new(zobj->properties, Z_STR_P(property_name), value);
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+ goto exit_assign_obj;
+ }
+ }
+ }
+
+ if (!Z_OBJ_HT_P(object)->write_property) {
+ zend_error(E_WARNING, "Attempt to assign property of non-object");
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+
+ goto exit_assign_obj;
+ }
+
+ if (IS_CV == IS_CV || IS_CV == IS_VAR) {
+ ZVAL_DEREF(value);
+ }
+
+ Z_OBJ_HT_P(object)->write_property(object, property_name, value, ((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property_name)) : NULL);
+
+ if (UNEXPECTED(RETURN_VALUE_USED(opline)) && EXPECTED(!EG(exception))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+
+exit_assign_obj:
zval_ptr_dtor_nogc(free_op2);
/* assign_obj has two opcodes! */
ZEND_VM_NEXT_OPCODE_EX(1, 2);
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_CV_TMPVAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_CV_TMPVAR_OP_DATA_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
zval *object_ptr;
- zend_free_op free_op2, free_op_data1;
+ zend_free_op free_op2;
zval *value;
zval *variable_ptr;
zval *dim;
@@ -39559,40 +49532,214 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_CV_TMPVAR_HAND
SAVE_OPLINE();
object_ptr = _get_zval_ptr_cv_undef_BP_VAR_W(execute_data, opline->op1.var);
- if (IS_CV == IS_VAR && UNEXPECTED(object_ptr == NULL)) {
- zend_throw_error(NULL, "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();
+ if (EXPECTED(Z_TYPE_P(object_ptr) == IS_ARRAY)) {
+try_assign_dim_array:
+ SEPARATE_ARRAY(object_ptr);
+ if ((IS_TMP_VAR|IS_VAR) == IS_UNUSED) {
+ variable_ptr = zend_hash_next_index_insert(Z_ARRVAL_P(object_ptr), &EG(uninitialized_zval));
+ if (UNEXPECTED(variable_ptr == NULL)) {
+ zend_error(E_WARNING, "Cannot add element to the array as the next element is already occupied");
+ goto assign_dim_error;
+ }
+ } else {
+ dim = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2);
+ if ((IS_TMP_VAR|IS_VAR) == IS_CONST) {
+ variable_ptr = zend_fetch_dimension_address_inner_W_CONST(Z_ARRVAL_P(object_ptr), dim);
+ } else {
+ variable_ptr = zend_fetch_dimension_address_inner_W(Z_ARRVAL_P(object_ptr), dim);
+ }
+ if (UNEXPECTED(variable_ptr == NULL)) {
+ goto assign_dim_error;
+ }
+ }
+ value = EX_CONSTANT((opline+1)->op1);
+ value = zend_assign_to_variable(variable_ptr, value, IS_CONST);
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+ } 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)) {
+ dim = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2);
+ value = EX_CONSTANT((opline+1)->op1);
+
+ zend_assign_to_object_dim(object_ptr, dim, value);
+
+ if (UNEXPECTED(RETURN_VALUE_USED(opline)) && EXPECTED(!EG(exception))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+
+ } else if (EXPECTED(Z_TYPE_P(object_ptr) == IS_STRING)) {
+ if ((IS_TMP_VAR|IS_VAR) == IS_UNUSED) {
+ zend_throw_error(NULL, "[] operator not supported for strings");
+
+
+ HANDLE_EXCEPTION();
+ } else {
+ dim = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2);
+ value = EX_CONSTANT((opline+1)->op1);
+ zend_assign_to_string_offset(object_ptr, dim, value, (UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL));
+
+ }
+ } else if (EXPECTED(Z_TYPE_P(object_ptr) <= IS_FALSE)) {
+ 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 (IS_CV != IS_VAR || EXPECTED(!Z_ISERROR_P(object_ptr))) {
+ zend_error(E_WARNING, "Cannot use a scalar value as an array");
+ }
+ dim = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2);
+assign_dim_error:
+
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+ }
}
+ if ((IS_TMP_VAR|IS_VAR) != IS_UNUSED) {
+ zval_ptr_dtor_nogc(free_op2);
+ }
+
+ /* assign_dim has two opcodes! */
+ ZEND_VM_NEXT_OPCODE_EX(1, 2);
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_CV_TMPVAR_OP_DATA_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+
+ zval *object_ptr;
+ zend_free_op free_op2, free_op_data;
+ zval *value;
+ zval *variable_ptr;
+ zval *dim;
+
+ SAVE_OPLINE();
+ object_ptr = _get_zval_ptr_cv_undef_BP_VAR_W(execute_data, opline->op1.var);
if (EXPECTED(Z_TYPE_P(object_ptr) == IS_ARRAY)) {
try_assign_dim_array:
+ SEPARATE_ARRAY(object_ptr);
if ((IS_TMP_VAR|IS_VAR) == IS_UNUSED) {
- SEPARATE_ARRAY(object_ptr);
variable_ptr = zend_hash_next_index_insert(Z_ARRVAL_P(object_ptr), &EG(uninitialized_zval));
if (UNEXPECTED(variable_ptr == NULL)) {
zend_error(E_WARNING, "Cannot add element to the array as the next element is already occupied");
- variable_ptr = &EG(error_zval);
+ goto assign_dim_error;
}
} else {
dim = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2);
- SEPARATE_ARRAY(object_ptr);
- variable_ptr = zend_fetch_dimension_address_inner(Z_ARRVAL_P(object_ptr), dim, (IS_TMP_VAR|IS_VAR), BP_VAR_W);
- zval_ptr_dtor_nogc(free_op2);
+ if ((IS_TMP_VAR|IS_VAR) == IS_CONST) {
+ variable_ptr = zend_fetch_dimension_address_inner_W_CONST(Z_ARRVAL_P(object_ptr), dim);
+ } else {
+ variable_ptr = zend_fetch_dimension_address_inner_W(Z_ARRVAL_P(object_ptr), dim);
+ }
+ if (UNEXPECTED(variable_ptr == NULL)) {
+ goto assign_dim_error;
+ }
}
- value = get_zval_ptr_r((opline+1)->op1_type, (opline+1)->op1, execute_data, &free_op_data1);
- if (UNEXPECTED(variable_ptr == &EG(error_zval))) {
- FREE_OP(free_op_data1);
+ value = _get_zval_ptr_tmp((opline+1)->op1.var, execute_data, &free_op_data);
+ value = zend_assign_to_variable(variable_ptr, value, IS_TMP_VAR);
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+ } 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)) {
+ dim = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2);
+ value = _get_zval_ptr_tmp((opline+1)->op1.var, execute_data, &free_op_data);
+
+ zend_assign_to_object_dim(object_ptr, dim, value);
+
+ if (UNEXPECTED(RETURN_VALUE_USED(opline)) && EXPECTED(!EG(exception))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+
+ zval_ptr_dtor_nogc(free_op_data);
+ } else if (EXPECTED(Z_TYPE_P(object_ptr) == IS_STRING)) {
+ if ((IS_TMP_VAR|IS_VAR) == IS_UNUSED) {
+ zend_throw_error(NULL, "[] operator not supported for strings");
+ zval_ptr_dtor_nogc(EX_VAR((opline+1)->op1.var));
+
+ HANDLE_EXCEPTION();
+ } else {
+ dim = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2);
+ value = _get_zval_ptr_tmp((opline+1)->op1.var, execute_data, &free_op_data);
+ zend_assign_to_string_offset(object_ptr, dim, value, (UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL));
+ zval_ptr_dtor_nogc(free_op_data);
+ }
+ } else if (EXPECTED(Z_TYPE_P(object_ptr) <= IS_FALSE)) {
+ 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 (IS_CV != IS_VAR || EXPECTED(!Z_ISERROR_P(object_ptr))) {
+ zend_error(E_WARNING, "Cannot use a scalar value as an array");
+ }
+ dim = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2);
+assign_dim_error:
+ zval_ptr_dtor_nogc(EX_VAR((opline+1)->op1.var));
if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
ZVAL_NULL(EX_VAR(opline->result.var));
}
+ }
+ }
+ if ((IS_TMP_VAR|IS_VAR) != IS_UNUSED) {
+ zval_ptr_dtor_nogc(free_op2);
+ }
+
+ /* assign_dim has two opcodes! */
+ ZEND_VM_NEXT_OPCODE_EX(1, 2);
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_CV_TMPVAR_OP_DATA_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+
+ zval *object_ptr;
+ zend_free_op free_op2, free_op_data;
+ zval *value;
+ zval *variable_ptr;
+ zval *dim;
+
+ SAVE_OPLINE();
+ object_ptr = _get_zval_ptr_cv_undef_BP_VAR_W(execute_data, opline->op1.var);
+
+ if (EXPECTED(Z_TYPE_P(object_ptr) == IS_ARRAY)) {
+try_assign_dim_array:
+ SEPARATE_ARRAY(object_ptr);
+ if ((IS_TMP_VAR|IS_VAR) == IS_UNUSED) {
+ variable_ptr = zend_hash_next_index_insert(Z_ARRVAL_P(object_ptr), &EG(uninitialized_zval));
+ if (UNEXPECTED(variable_ptr == NULL)) {
+ zend_error(E_WARNING, "Cannot add element to the array as the next element is already occupied");
+ goto assign_dim_error;
+ }
} else {
- value = zend_assign_to_variable(variable_ptr, value, (opline+1)->op1_type);
- if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
- ZVAL_COPY(EX_VAR(opline->result.var), value);
+ dim = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2);
+ if ((IS_TMP_VAR|IS_VAR) == IS_CONST) {
+ variable_ptr = zend_fetch_dimension_address_inner_W_CONST(Z_ARRVAL_P(object_ptr), dim);
+ } else {
+ variable_ptr = zend_fetch_dimension_address_inner_W(Z_ARRVAL_P(object_ptr), dim);
+ }
+ if (UNEXPECTED(variable_ptr == NULL)) {
+ goto assign_dim_error;
}
}
+ value = _get_zval_ptr_var((opline+1)->op1.var, execute_data, &free_op_data);
+ value = zend_assign_to_variable(variable_ptr, value, IS_VAR);
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
} else {
if (EXPECTED(Z_ISREF_P(object_ptr))) {
object_ptr = Z_REFVAL_P(object_ptr);
@@ -39601,52 +49748,138 @@ 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);
+ dim = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2);
+ value = _get_zval_ptr_var((opline+1)->op1.var, execute_data, &free_op_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);
- zval_ptr_dtor_nogc(free_op2);
+ zend_assign_to_object_dim(object_ptr, dim, value);
+
+ if (UNEXPECTED(RETURN_VALUE_USED(opline)) && EXPECTED(!EG(exception))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+
+ zval_ptr_dtor_nogc(free_op_data);
} 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_throw_error(NULL, "[] operator not supported for strings");
- FREE_UNFETCHED_OP((opline+1)->op1_type, (opline+1)->op1.var);
+ if ((IS_TMP_VAR|IS_VAR) == IS_UNUSED) {
+ zend_throw_error(NULL, "[] operator not supported for strings");
+ zval_ptr_dtor_nogc(EX_VAR((opline+1)->op1.var));
- HANDLE_EXCEPTION();
- } else {
- zend_long offset;
+ HANDLE_EXCEPTION();
+ } else {
+ dim = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2);
+ value = _get_zval_ptr_var_deref((opline+1)->op1.var, execute_data, &free_op_data);
+ zend_assign_to_string_offset(object_ptr, dim, value, (UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL));
+ zval_ptr_dtor_nogc(free_op_data);
+ }
+ } else if (EXPECTED(Z_TYPE_P(object_ptr) <= IS_FALSE)) {
+ 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 (IS_CV != IS_VAR || EXPECTED(!Z_ISERROR_P(object_ptr))) {
+ zend_error(E_WARNING, "Cannot use a scalar value as an array");
+ }
+ dim = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2);
+assign_dim_error:
+ zval_ptr_dtor_nogc(EX_VAR((opline+1)->op1.var));
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ }
+ }
+ }
+ if ((IS_TMP_VAR|IS_VAR) != IS_UNUSED) {
+ zval_ptr_dtor_nogc(free_op2);
+ }
- 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_r_deref((opline+1)->op1_type, (opline+1)->op1, execute_data, &free_op_data1);
- 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);
- }
+ /* assign_dim has two opcodes! */
+ ZEND_VM_NEXT_OPCODE_EX(1, 2);
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_CV_TMPVAR_OP_DATA_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+
+ zval *object_ptr;
+ zend_free_op free_op2;
+ zval *value;
+ zval *variable_ptr;
+ zval *dim;
+
+ SAVE_OPLINE();
+ object_ptr = _get_zval_ptr_cv_undef_BP_VAR_W(execute_data, opline->op1.var);
+
+ if (EXPECTED(Z_TYPE_P(object_ptr) == IS_ARRAY)) {
+try_assign_dim_array:
+ SEPARATE_ARRAY(object_ptr);
+ if ((IS_TMP_VAR|IS_VAR) == IS_UNUSED) {
+ variable_ptr = zend_hash_next_index_insert(Z_ARRVAL_P(object_ptr), &EG(uninitialized_zval));
+ if (UNEXPECTED(variable_ptr == NULL)) {
+ zend_error(E_WARNING, "Cannot add element to the array as the next element is already occupied");
+ goto assign_dim_error;
+ }
+ } else {
+ dim = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2);
+ if ((IS_TMP_VAR|IS_VAR) == IS_CONST) {
+ variable_ptr = zend_fetch_dimension_address_inner_W_CONST(Z_ARRVAL_P(object_ptr), dim);
} 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);
+ variable_ptr = zend_fetch_dimension_address_inner_W(Z_ARRVAL_P(object_ptr), dim);
+ }
+ if (UNEXPECTED(variable_ptr == NULL)) {
+ goto assign_dim_error;
+ }
+ }
+ value = _get_zval_ptr_cv_BP_VAR_R(execute_data, (opline+1)->op1.var);
+ value = zend_assign_to_variable(variable_ptr, value, IS_CV);
+ if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+ } 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;
}
- } 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;
+ }
+ if (EXPECTED(Z_TYPE_P(object_ptr) == IS_OBJECT)) {
+ dim = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2);
+ value = _get_zval_ptr_cv_BP_VAR_R(execute_data, (opline+1)->op1.var);
+
+ zend_assign_to_object_dim(object_ptr, dim, value);
+
+ if (UNEXPECTED(RETURN_VALUE_USED(opline)) && EXPECTED(!EG(exception))) {
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ }
+
+ } else if (EXPECTED(Z_TYPE_P(object_ptr) == IS_STRING)) {
+ if ((IS_TMP_VAR|IS_VAR) == IS_UNUSED) {
+ zend_throw_error(NULL, "[] operator not supported for strings");
+
+
+ HANDLE_EXCEPTION();
+ } else {
+ dim = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2);
+ value = _get_zval_ptr_cv_deref_BP_VAR_R(execute_data, (opline+1)->op1.var);
+ zend_assign_to_string_offset(object_ptr, dim, value, (UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL));
+
}
- goto assign_dim_convert_to_array;
+ } else if (EXPECTED(Z_TYPE_P(object_ptr) <= IS_FALSE)) {
+ 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 {
- zend_error(E_WARNING, "Cannot use a scalar value as an array");
-assign_dim_clean:
+ if (IS_CV != IS_VAR || EXPECTED(!Z_ISERROR_P(object_ptr))) {
+ zend_error(E_WARNING, "Cannot use a scalar value as an array");
+ }
dim = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2);
- zval_ptr_dtor_nogc(free_op2);
- value = get_zval_ptr_r((opline+1)->op1_type, (opline+1)->op1, execute_data, &free_op_data1);
- FREE_OP(free_op_data1);
+assign_dim_error:
+
if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
ZVAL_NULL(EX_VAR(opline->result.var));
}
}
}
+ if ((IS_TMP_VAR|IS_VAR) != IS_UNUSED) {
+ zval_ptr_dtor_nogc(free_op2);
+ }
/* assign_dim has two opcodes! */
ZEND_VM_NEXT_OPCODE_EX(1, 2);
@@ -39758,7 +49991,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_SPEC_CV_TMPVA
object = _get_zval_ptr_cv_undef(execute_data, opline->op1.var);
- if (IS_CV == IS_UNUSED && UNEXPECTED(Z_OBJ_P(object) == NULL)) {
+ if (IS_CV == IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
zval_ptr_dtor_nogc(free_op2);
HANDLE_EXCEPTION();
@@ -39818,6 +50051,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_SPEC_CV_TMPVA
EXPECTED(obj == orig_obj)) {
CACHE_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(function_name), called_scope, fbc);
}
+ if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!fbc->op_array.run_time_cache)) {
+ init_func_run_time_cache(&fbc->op_array);
+ }
}
call_info = ZEND_CALL_NESTED_FUNCTION;
@@ -39923,11 +50159,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ADD_ARRAY_ELEMENT_SPEC_CV_TMPV
if ((IS_CV == IS_VAR || IS_CV == IS_CV) &&
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_throw_error(NULL, "Cannot create references to/from string offsets");
- zend_array_destroy(Z_ARRVAL_P(EX_VAR(opline->result.var)));
- HANDLE_EXCEPTION();
- }
ZVAL_MAKE_REF(expr_ptr);
Z_ADDREF_P(expr_ptr);
@@ -39936,10 +50167,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ADD_ARRAY_ELEMENT_SPEC_CV_TMPV
if (IS_CV == IS_TMP_VAR) {
/* pass */
} else if (IS_CV == IS_CONST) {
- 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;
+ if (Z_REFCOUNTED_P(expr_ptr)) {
+ Z_ADDREF_P(expr_ptr);
}
} else if (IS_CV == IS_CV) {
ZVAL_DEREF(expr_ptr);
@@ -40053,21 +50282,11 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_UNSET_DIM_SPEC_CV_TMPVAR_HANDL
zend_string *key;
SAVE_OPLINE();
- container = _get_zval_ptr_cv_BP_VAR_UNSET(execute_data, opline->op1.var);
- if (IS_CV == IS_UNUSED && UNEXPECTED(Z_OBJ_P(container) == NULL)) {
- zend_throw_error(NULL, "Using $this when not in object context");
- zval_ptr_dtor_nogc(EX_VAR(opline->op2.var));
- HANDLE_EXCEPTION();
- }
- if (IS_CV == IS_VAR && UNEXPECTED(container == NULL)) {
- zend_throw_error(NULL, "Cannot unset string offsets");
- zval_ptr_dtor_nogc(EX_VAR(opline->op2.var));
- HANDLE_EXCEPTION();
- }
+ container = _get_zval_ptr_cv_undef_BP_VAR_UNSET(execute_data, opline->op1.var);
offset = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2);
do {
- if (IS_CV != IS_UNUSED && EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) {
+ if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) {
HashTable *ht;
unset_dim_array:
@@ -40117,16 +50336,19 @@ num_index_dim:
zend_error(E_WARNING, "Illegal offset type in unset");
}
break;
- } else if (IS_CV != IS_UNUSED && Z_ISREF_P(container)) {
+ } else if (Z_ISREF_P(container)) {
container = Z_REFVAL_P(container);
if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) {
goto unset_dim_array;
}
}
+ if (IS_CV == IS_CV && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) {
+ container = GET_OP1_UNDEF_CV(container, BP_VAR_R);
+ }
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 || EXPECTED(Z_TYPE_P(container) == IS_OBJECT)) {
+ if (EXPECTED(Z_TYPE_P(container) == IS_OBJECT)) {
if (UNEXPECTED(Z_OBJ_HT_P(container)->unset_dimension == NULL)) {
zend_throw_error(NULL, "Cannot use object as array");
} else {
@@ -40151,16 +50373,11 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_UNSET_OBJ_SPEC_CV_TMPVAR_HANDL
SAVE_OPLINE();
container = _get_zval_ptr_cv_BP_VAR_UNSET(execute_data, opline->op1.var);
- if (IS_CV == IS_UNUSED && UNEXPECTED(Z_OBJ_P(container) == NULL)) {
+ if (IS_CV == IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
zval_ptr_dtor_nogc(EX_VAR(opline->op2.var));
HANDLE_EXCEPTION();
}
- if (IS_CV == IS_VAR && UNEXPECTED(container == NULL)) {
- zend_throw_error(NULL, "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);
do {
@@ -40196,17 +50413,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ISSET_ISEMPTY_DIM_OBJ_SPEC_CV_
zval *offset;
SAVE_OPLINE();
- container = _get_zval_ptr_cv_BP_VAR_IS(execute_data, opline->op1.var);
-
- if (IS_CV == IS_UNUSED && UNEXPECTED(Z_OBJ_P(container) == NULL)) {
- zend_throw_error(NULL, "Using $this when not in object context");
- zval_ptr_dtor_nogc(EX_VAR(opline->op2.var));
- HANDLE_EXCEPTION();
- }
-
+ container = _get_zval_ptr_cv_undef(execute_data, opline->op1.var);
offset = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2);
- if (IS_CV != IS_UNUSED && EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) {
+ if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) {
HashTable *ht;
zval *value;
zend_string *str;
@@ -40273,8 +50483,7 @@ num_index_prop:
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 ((IS_CV != IS_CONST && EXPECTED(Z_TYPE_P(container) == IS_OBJECT))) {
if (EXPECTED(Z_OBJ_HT_P(container)->has_dimension)) {
result =
((opline->extended_value & ZEND_ISSET) == 0) ^
@@ -40289,6 +50498,9 @@ num_index_prop:
if (EXPECTED(Z_TYPE_P(offset) == IS_LONG)) {
lval = Z_LVAL_P(offset);
isset_str_offset:
+ if (UNEXPECTED(lval < 0)) { /* Handle negative offset */
+ lval += (zend_long)Z_STRLEN_P(container);
+ }
if (EXPECTED(lval >= 0) && (size_t)lval < Z_STRLEN_P(container)) {
if (opline->extended_value & ZEND_ISSET) {
result = 1;
@@ -40334,7 +50546,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ISSET_ISEMPTY_PROP_OBJ_SPEC_CV
SAVE_OPLINE();
container = _get_zval_ptr_cv_BP_VAR_IS(execute_data, opline->op1.var);
- if (IS_CV == IS_UNUSED && UNEXPECTED(Z_OBJ_P(container) == NULL)) {
+ if (IS_CV == IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
zval_ptr_dtor_nogc(EX_VAR(opline->op2.var));
HANDLE_EXCEPTION();
@@ -40370,6 +50582,54 @@ isset_no_object:
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_DIM_R_INDEX_SPEC_CV_TMPVAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zend_free_op free_op2;
+ zval *container, *dim, *value;
+ zend_long offset;
+
+ container = _get_zval_ptr_cv_undef(execute_data, opline->op1.var);
+ dim = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2);
+ if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) {
+fetch_dim_r_index_array:
+ if (EXPECTED(Z_TYPE_P(dim) == IS_LONG)) {
+ offset = Z_LVAL_P(dim);
+ } else {
+ offset = zval_get_long(dim);
+ }
+ ZEND_HASH_INDEX_FIND(Z_ARRVAL_P(container), offset, value, fetch_dim_r_index_undef);
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ if (IS_CV & (IS_TMP_VAR|IS_VAR)) {
+ SAVE_OPLINE();
+
+ ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
+ } else {
+ ZEND_VM_NEXT_OPCODE();
+ }
+ } else if (IS_CV != IS_CONST && EXPECTED(Z_TYPE_P(container) == IS_REFERENCE)) {
+ container = Z_REFVAL_P(container);
+ if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) {
+ goto fetch_dim_r_index_array;
+ } else {
+ goto fetch_dim_r_index_slow;
+ }
+ } else {
+fetch_dim_r_index_slow:
+ SAVE_OPLINE();
+ zend_fetch_dimension_address_read_R_slow(EX_VAR(opline->result.var), container, dim);
+
+ ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
+ }
+
+fetch_dim_r_index_undef:
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ SAVE_OPLINE();
+ zend_error(E_NOTICE, "Undefined offset: " ZEND_LONG_FMT, offset);
+
+ ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
+}
+
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_BW_NOT_SPEC_TMPVAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
@@ -40393,7 +50653,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_BOOL_NOT_SPEC_TMPVAR_HANDLER(Z
ZVAL_FALSE(EX_VAR(opline->result.var));
} else if (EXPECTED(Z_TYPE_INFO_P(val) <= IS_TRUE)) {
ZVAL_TRUE(EX_VAR(opline->result.var));
- if (UNEXPECTED(Z_TYPE_INFO_P(val) == IS_UNDEF)) {
+ if ((IS_TMP_VAR|IS_VAR) == IS_CV && UNEXPECTED(Z_TYPE_INFO_P(val) == IS_UNDEF)) {
SAVE_OPLINE();
GET_OP1_UNDEF_CV(val, BP_VAR_R);
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
@@ -40649,6 +50909,23 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FE_FREE_SPEC_TMPVAR_HANDLER(ZE
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SEND_VAL_SPEC_TMPVAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zval *value, *arg;
+ zend_free_op free_op1;
+
+ value = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1);
+ arg = ZEND_CALL_VAR(EX(call), opline->result.var);
+ ZVAL_COPY_VALUE(arg, value);
+ if ((IS_TMP_VAR|IS_VAR) == IS_CONST) {
+ if (UNEXPECTED(Z_OPT_REFCOUNTED_P(arg))) {
+ Z_ADDREF_P(arg);
+ }
+ }
+ ZEND_VM_NEXT_OPCODE();
+}
+
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_BOOL_SPEC_TMPVAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
@@ -40660,7 +50937,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_BOOL_SPEC_TMPVAR_HANDLER(ZEND_
ZVAL_TRUE(EX_VAR(opline->result.var));
} else if (EXPECTED(Z_TYPE_INFO_P(val) <= IS_TRUE)) {
ZVAL_FALSE(EX_VAR(opline->result.var));
- if (UNEXPECTED(Z_TYPE_INFO_P(val) == IS_UNDEF)) {
+ if ((IS_TMP_VAR|IS_VAR) == IS_CV && UNEXPECTED(Z_TYPE_INFO_P(val) == IS_UNDEF)) {
SAVE_OPLINE();
GET_OP1_UNDEF_CV(val, BP_VAR_R);
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
@@ -40679,14 +50956,14 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CLONE_SPEC_TMPVAR_HANDLER(ZEND
USE_OPLINE
zend_free_op free_op1;
zval *obj;
- zend_class_entry *ce;
+ zend_class_entry *ce, *scope;
zend_function *clone;
zend_object_clone_obj_t clone_call;
SAVE_OPLINE();
obj = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1);
- if ((IS_TMP_VAR|IS_VAR) == IS_UNUSED && UNEXPECTED(Z_OBJ_P(obj) == NULL)) {
+ if ((IS_TMP_VAR|IS_VAR) == IS_UNUSED && UNEXPECTED(Z_TYPE_P(obj) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
HANDLE_EXCEPTION();
}
@@ -40713,44 +50990,41 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CLONE_SPEC_TMPVAR_HANDLER(ZEND
} while (0);
ce = Z_OBJCE_P(obj);
- clone = ce ? ce->clone : NULL;
- clone_call = Z_OBJ_HT_P(obj)->clone_obj;
+ clone = ce->clone;
+ clone_call = Z_OBJ_HT_P(obj)->clone_obj;
if (UNEXPECTED(clone_call == NULL)) {
- if (ce) {
- zend_throw_error(NULL, "Trying to clone an uncloneable object of class %s", ZSTR_VAL(ce->name));
- } else {
- zend_throw_error(NULL, "Trying to clone an uncloneable object");
- }
+ zend_throw_error(NULL, "Trying to clone an uncloneable object of class %s", ZSTR_VAL(ce->name));
zval_ptr_dtor_nogc(free_op1);
HANDLE_EXCEPTION();
}
- if (ce && clone) {
+ if (clone) {
if (clone->op_array.fn_flags & ZEND_ACC_PRIVATE) {
/* Ensure that if we're calling a private function, we're allowed to do so.
*/
- if (UNEXPECTED(ce != EG(scope))) {
- zend_throw_error(NULL, "Call to private %s::__clone() from context '%s'", ZSTR_VAL(ce->name), EG(scope) ? ZSTR_VAL(EG(scope)->name) : "");
+ scope = EX(func)->op_array.scope;
+ if (UNEXPECTED(ce != scope)) {
+ zend_throw_error(NULL, "Call to private %s::__clone() from context '%s'", ZSTR_VAL(ce->name), scope ? ZSTR_VAL(scope->name) : "");
zval_ptr_dtor_nogc(free_op1);
HANDLE_EXCEPTION();
}
} else if ((clone->common.fn_flags & ZEND_ACC_PROTECTED)) {
/* Ensure that if we're calling a protected function, we're allowed to do so.
*/
- if (UNEXPECTED(!zend_check_protected(zend_get_function_root_class(clone), EG(scope)))) {
- zend_throw_error(NULL, "Call to protected %s::__clone() from context '%s'", ZSTR_VAL(ce->name), EG(scope) ? ZSTR_VAL(EG(scope)->name) : "");
+ scope = EX(func)->op_array.scope;
+ if (UNEXPECTED(!zend_check_protected(zend_get_function_root_class(clone), scope))) {
+ zend_throw_error(NULL, "Call to protected %s::__clone() from context '%s'", ZSTR_VAL(ce->name), scope ? ZSTR_VAL(scope->name) : "");
zval_ptr_dtor_nogc(free_op1);
HANDLE_EXCEPTION();
}
}
}
- if (EXPECTED(EG(exception) == NULL)) {
- ZVAL_OBJ(EX_VAR(opline->result.var), clone_call(obj));
- if (UNEXPECTED(!RETURN_VALUE_USED(opline)) || UNEXPECTED(EG(exception) != NULL)) {
- OBJ_RELEASE(Z_OBJ_P(EX_VAR(opline->result.var)));
- }
+ ZVAL_OBJ(EX_VAR(opline->result.var), clone_call(obj));
+ if (UNEXPECTED(EG(exception) != NULL)) {
+ OBJ_RELEASE(Z_OBJ_P(EX_VAR(opline->result.var)));
}
+
zval_ptr_dtor_nogc(free_op1);
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
@@ -40758,89 +51032,20 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CLONE_SPEC_TMPVAR_HANDLER(ZEND
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INCLUDE_OR_EVAL_SPEC_TMPVAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
- zend_op_array *new_op_array=NULL;
+ zend_op_array *new_op_array;
zend_free_op free_op1;
zval *inc_filename;
- zval tmp_inc_filename;
- zend_bool failure_retval=0;
SAVE_OPLINE();
inc_filename = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1);
-
- 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;
- }
-
- if (opline->extended_value != ZEND_EVAL && strlen(Z_STRVAL_P(inc_filename)) != Z_STRLEN_P(inc_filename)) {
- if (opline->extended_value == ZEND_INCLUDE_ONCE || opline->extended_value == ZEND_INCLUDE) {
- zend_message_dispatcher(ZMSG_FAILED_INCLUDE_FOPEN, Z_STRVAL_P(inc_filename));
- } else {
- zend_message_dispatcher(ZMSG_FAILED_REQUIRE_FOPEN, Z_STRVAL_P(inc_filename));
- }
- } else {
- switch (opline->extended_value) {
- case ZEND_INCLUDE_ONCE:
- case ZEND_REQUIRE_ONCE: {
- zend_file_handle file_handle;
- zend_string *resolved_path;
-
- resolved_path = zend_resolve_path(Z_STRVAL_P(inc_filename), (int)Z_STRLEN_P(inc_filename));
- if (resolved_path) {
- failure_retval = zend_hash_exists(&EG(included_files), resolved_path);
- } else {
- resolved_path = zend_string_copy(Z_STR_P(inc_filename));
- }
-
- if (failure_retval) {
- /* do nothing, file already included */
- } else if (SUCCESS == zend_stream_open(ZSTR_VAL(resolved_path), &file_handle)) {
-
- if (!file_handle.opened_path) {
- file_handle.opened_path = zend_string_copy(resolved_path);
- }
-
- if (zend_hash_add_empty_element(&EG(included_files), file_handle.opened_path)) {
- new_op_array = zend_compile_file(&file_handle, (opline->extended_value==ZEND_INCLUDE_ONCE?ZEND_INCLUDE:ZEND_REQUIRE));
- zend_destroy_file_handle(&file_handle);
- } else {
- zend_file_handle_dtor(&file_handle);
- failure_retval=1;
- }
- } else {
- if (opline->extended_value == ZEND_INCLUDE_ONCE) {
- zend_message_dispatcher(ZMSG_FAILED_INCLUDE_FOPEN, Z_STRVAL_P(inc_filename));
- } else {
- zend_message_dispatcher(ZMSG_FAILED_REQUIRE_FOPEN, Z_STRVAL_P(inc_filename));
- }
- }
- zend_string_release(resolved_path);
- }
- break;
- case ZEND_INCLUDE:
- case ZEND_REQUIRE:
- new_op_array = compile_filename(opline->extended_value, inc_filename);
- break;
- case ZEND_EVAL: {
- char *eval_desc = zend_make_compiled_string_description("eval()'d code");
-
- new_op_array = zend_compile_string(inc_filename, eval_desc);
- efree(eval_desc);
- }
- break;
- EMPTY_SWITCH_DEFAULT_CASE()
- }
- }
- if (Z_TYPE(tmp_inc_filename) != IS_UNDEF) {
- zend_string_release(Z_STR(tmp_inc_filename));
- }
+ new_op_array = zend_include_or_eval(inc_filename, opline->extended_value);
zval_ptr_dtor_nogc(free_op1);
if (UNEXPECTED(EG(exception) != NULL)) {
HANDLE_EXCEPTION();
+ } else if (new_op_array == ZEND_FAKE_OP_ARRAY) {
+ if (RETURN_VALUE_USED(opline)) {
+ ZVAL_TRUE(EX_VAR(opline->result.var));
+ }
} else if (EXPECTED(new_op_array != NULL)) {
zval *return_value = NULL;
zend_execute_data *call;
@@ -40849,19 +51054,21 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INCLUDE_OR_EVAL_SPEC_TMPVAR_HA
return_value = EX_VAR(opline->result.var);
}
- new_op_array->scope = EG(scope);
+ new_op_array->scope = EX(func)->op_array.scope;
- call = zend_vm_stack_push_call_frame(ZEND_CALL_NESTED_CODE,
- (zend_function*)new_op_array, 0, EX(called_scope), Z_OBJ(EX(This)));
+ call = zend_vm_stack_push_call_frame(ZEND_CALL_NESTED_CODE | ZEND_CALL_HAS_SYMBOL_TABLE,
+ (zend_function*)new_op_array, 0,
+ Z_TYPE(EX(This)) != IS_OBJECT ? Z_CE(EX(This)) : NULL,
+ Z_TYPE(EX(This)) == IS_OBJECT ? Z_OBJ(EX(This)) : NULL);
- if (EX(symbol_table)) {
+ if (EX_CALL_INFO() & ZEND_CALL_HAS_SYMBOL_TABLE) {
call->symbol_table = EX(symbol_table);
} else {
call->symbol_table = zend_rebuild_symbol_table();
}
call->prev_execute_data = execute_data;
- i_init_code_execute_data(call, new_op_array, return_value);
+ i_init_code_execute_data(call, new_op_array, return_value);
if (EXPECTED(zend_execute_ex == execute_ex)) {
ZEND_VM_ENTER();
} else {
@@ -40876,12 +51083,11 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INCLUDE_OR_EVAL_SPEC_TMPVAR_HA
zend_throw_exception_internal(NULL);
HANDLE_EXCEPTION();
}
-
} else if (RETURN_VALUE_USED(opline)) {
- ZVAL_BOOL(EX_VAR(opline->result.var), failure_retval);
+ ZVAL_FALSE(EX_VAR(opline->result.var));
}
- ZEND_VM_INTERRUPT_CHECK();
- ZEND_VM_NEXT_OPCODE();
+ ZEND_VM_SET_OPCODE(opline + 1);
+ ZEND_VM_CONTINUE();
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_EXIT_SPEC_TMPVAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@@ -41221,7 +51427,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CONCAT_SPEC_TMPVAR_CONST_HANDL
!ZSTR_IS_INTERNED(op1_str) && GC_REFCOUNT(op1_str) == 1) {
size_t len = ZSTR_LEN(op1_str);
- str = zend_string_realloc(op1_str, len + ZSTR_LEN(op2_str), 0);
+ str = zend_string_extend(op1_str, len + ZSTR_LEN(op2_str), 0);
memcpy(ZSTR_VAL(str) + len, ZSTR_VAL(op2_str), ZSTR_LEN(op2_str)+1);
ZVAL_NEW_STR(EX_VAR(opline->result.var), str);
break;
@@ -41557,14 +51763,14 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_BOOL_XOR_SPEC_TMPVAR_CONST_HAN
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_fetch_var_address_helper_SPEC_TMPVAR_CONST(int type ZEND_OPCODE_HANDLER_ARGS_DC)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_fetch_static_prop_helper_SPEC_TMPVAR_CONST(int type ZEND_OPCODE_HANDLER_ARGS_DC)
{
USE_OPLINE
zend_free_op free_op1;
zval *varname;
zval *retval;
zend_string *name;
- HashTable *target_symbol_table;
+ zend_class_entry *ce;
SAVE_OPLINE();
varname = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1);
@@ -41581,120 +51787,75 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_fetch_var_address_helper_SPEC_
name = zval_get_string(varname);
}
- if (IS_CONST != IS_UNUSED) {
- zend_class_entry *ce;
-
- if (IS_CONST == IS_CONST) {
- if ((IS_TMP_VAR|IS_VAR) == IS_CONST && EXPECTED((ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)))) != NULL)) {
- retval = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)) + sizeof(void*));
-
- /* check if static properties were destoyed */
- if (UNEXPECTED(CE_STATIC_MEMBERS(ce) == NULL)) {
- zend_throw_error(NULL, "Access to undeclared static property: %s::$%s", ZSTR_VAL(ce->name), ZSTR_VAL(name));
- zval_ptr_dtor_nogc(free_op1);
- HANDLE_EXCEPTION();
- }
+ if (IS_CONST == IS_CONST) {
+ if ((IS_TMP_VAR|IS_VAR) == IS_CONST && EXPECTED((ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)))) != NULL)) {
+ retval = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)) + sizeof(void*));
- goto fetch_var_return;
- } else if (UNEXPECTED((ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)))) == NULL)) {
- ce = zend_fetch_class_by_name(Z_STR_P(EX_CONSTANT(opline->op2)), EX_CONSTANT(opline->op2) + 1, ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION);
- if (UNEXPECTED(ce == NULL)) {
- if ((IS_TMP_VAR|IS_VAR) != IS_CONST) {
- zend_string_release(name);
- }
- zval_ptr_dtor_nogc(free_op1);
- ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
- }
- CACHE_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)), ce);
+ /* check if static properties were destoyed */
+ if (UNEXPECTED(CE_STATIC_MEMBERS(ce) == NULL)) {
+ zend_throw_error(NULL, "Access to undeclared static property: %s::$%s", ZSTR_VAL(ce->name), ZSTR_VAL(name));
+ zval_ptr_dtor_nogc(free_op1);
+ HANDLE_EXCEPTION();
}
- } else {
- ce = Z_CE_P(EX_VAR(opline->op2.var));
- if ((IS_TMP_VAR|IS_VAR) == IS_CONST &&
- (retval = CACHED_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)), ce)) != NULL) {
- /* check if static properties were destoyed */
- if (UNEXPECTED(CE_STATIC_MEMBERS(ce) == NULL)) {
- zend_throw_error(NULL, "Access to undeclared static property: %s::$%s", ZSTR_VAL(ce->name), ZSTR_VAL(name));
- zval_ptr_dtor_nogc(free_op1);
- HANDLE_EXCEPTION();
+ goto fetch_static_prop_return;
+ } else if (UNEXPECTED((ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)))) == NULL)) {
+ ce = zend_fetch_class_by_name(Z_STR_P(EX_CONSTANT(opline->op2)), EX_CONSTANT(opline->op2) + 1, ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION);
+ if (UNEXPECTED(ce == NULL)) {
+ if ((IS_TMP_VAR|IS_VAR) != IS_CONST) {
+ zend_string_release(name);
}
-
- goto fetch_var_return;
- }
- }
- retval = zend_std_get_static_property(ce, name, 0);
- if (UNEXPECTED(EG(exception))) {
- if ((IS_TMP_VAR|IS_VAR) != IS_CONST) {
- zend_string_release(name);
+ zval_ptr_dtor_nogc(free_op1);
+ ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
- zval_ptr_dtor_nogc(free_op1);
- HANDLE_EXCEPTION();
+ CACHE_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)), ce);
}
- if ((IS_TMP_VAR|IS_VAR) == IS_CONST && retval) {
- CACHE_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)), ce, retval);
- }
-
- zval_ptr_dtor_nogc(free_op1);
} else {
- target_symbol_table = zend_get_target_symbol_table(execute_data, opline->extended_value & ZEND_FETCH_TYPE_MASK);
- retval = zend_hash_find(target_symbol_table, name);
- if (retval == NULL) {
- switch (type) {
- case BP_VAR_R:
- case BP_VAR_UNSET:
- zend_error(E_NOTICE,"Undefined variable: %s", ZSTR_VAL(name));
- /* break missing intentionally */
- case BP_VAR_IS:
- retval = &EG(uninitialized_zval);
- break;
- case BP_VAR_RW:
- zend_error(E_NOTICE,"Undefined variable: %s", ZSTR_VAL(name));
- retval = zend_hash_update(target_symbol_table, name, &EG(uninitialized_zval));
- break;
- case BP_VAR_W:
- retval = zend_hash_add_new(target_symbol_table, name, &EG(uninitialized_zval));
- break;
- EMPTY_SWITCH_DEFAULT_CASE()
- }
- /* GLOBAL or $$name variable may be an INDIRECT pointer to CV */
- } else if (Z_TYPE_P(retval) == IS_INDIRECT) {
- retval = Z_INDIRECT_P(retval);
- if (Z_TYPE_P(retval) == IS_UNDEF) {
- switch (type) {
- case BP_VAR_R:
- case BP_VAR_UNSET:
- zend_error(E_NOTICE,"Undefined variable: %s", ZSTR_VAL(name));
- /* break missing intentionally */
- case BP_VAR_IS:
- retval = &EG(uninitialized_zval);
- break;
- case BP_VAR_RW:
- zend_error(E_NOTICE,"Undefined variable: %s", ZSTR_VAL(name));
- /* break missing intentionally */
- case BP_VAR_W:
- ZVAL_NULL(retval);
- break;
- EMPTY_SWITCH_DEFAULT_CASE()
+ if (IS_CONST == IS_UNUSED) {
+ ce = zend_fetch_class(NULL, opline->op2.num);
+ if (UNEXPECTED(ce == NULL)) {
+ ZEND_ASSERT(EG(exception));
+ if ((IS_TMP_VAR|IS_VAR) != IS_CONST) {
+ zend_string_release(name);
}
+ zval_ptr_dtor_nogc(free_op1);
+ HANDLE_EXCEPTION();
}
+ } else {
+ ce = Z_CE_P(EX_VAR(opline->op2.var));
}
- if ((opline->extended_value & ZEND_FETCH_TYPE_MASK) == ZEND_FETCH_STATIC) {
- if (Z_CONSTANT_P(retval)) {
- if (UNEXPECTED(zval_update_constant_ex(retval, 1, NULL) != SUCCESS)) {
- zval_ptr_dtor_nogc(free_op1);
- HANDLE_EXCEPTION();
- }
+ if ((IS_TMP_VAR|IS_VAR) == IS_CONST &&
+ (retval = CACHED_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)), ce)) != NULL) {
+
+ /* check if static properties were destoyed */
+ if (UNEXPECTED(CE_STATIC_MEMBERS(ce) == NULL)) {
+ zend_throw_error(NULL, "Access to undeclared static property: %s::$%s", ZSTR_VAL(ce->name), ZSTR_VAL(name));
+ zval_ptr_dtor_nogc(free_op1);
+ HANDLE_EXCEPTION();
}
- } else if ((opline->extended_value & ZEND_FETCH_TYPE_MASK) != ZEND_FETCH_GLOBAL_LOCK) {
- zval_ptr_dtor_nogc(free_op1);
+
+ goto fetch_static_prop_return;
}
}
+ retval = zend_std_get_static_property(ce, name, 0);
+ if (UNEXPECTED(EG(exception))) {
+ if ((IS_TMP_VAR|IS_VAR) != IS_CONST) {
+ zend_string_release(name);
+ }
+ zval_ptr_dtor_nogc(free_op1);
+ HANDLE_EXCEPTION();
+ }
+ if ((IS_TMP_VAR|IS_VAR) == IS_CONST && retval) {
+ CACHE_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)), ce, retval);
+ }
+
+ zval_ptr_dtor_nogc(free_op1);
if ((IS_TMP_VAR|IS_VAR) != IS_CONST) {
zend_string_release(name);
}
-fetch_var_return:
+fetch_static_prop_return:
ZEND_ASSERT(retval != NULL);
if (type == BP_VAR_R || type == BP_VAR_IS) {
if (/*type == BP_VAR_R &&*/ Z_ISREF_P(retval) && Z_REFCOUNT_P(retval) == 1) {
@@ -41707,51 +51868,73 @@ fetch_var_return:
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_R_SPEC_TMPVAR_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_STATIC_PROP_R_SPEC_TMPVAR_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
- ZEND_VM_TAIL_CALL(zend_fetch_var_address_helper_SPEC_TMPVAR_CONST(BP_VAR_R ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_fetch_static_prop_helper_SPEC_TMPVAR_CONST(BP_VAR_R ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_W_SPEC_TMPVAR_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_STATIC_PROP_W_SPEC_TMPVAR_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
- ZEND_VM_TAIL_CALL(zend_fetch_var_address_helper_SPEC_TMPVAR_CONST(BP_VAR_W ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_fetch_static_prop_helper_SPEC_TMPVAR_CONST(BP_VAR_W ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_RW_SPEC_TMPVAR_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_STATIC_PROP_RW_SPEC_TMPVAR_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
- ZEND_VM_TAIL_CALL(zend_fetch_var_address_helper_SPEC_TMPVAR_CONST(BP_VAR_RW ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_fetch_static_prop_helper_SPEC_TMPVAR_CONST(BP_VAR_RW ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_FUNC_ARG_SPEC_TMPVAR_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_STATIC_PROP_FUNC_ARG_SPEC_TMPVAR_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
if (zend_is_by_ref_func_arg_fetch(opline, EX(call))) {
- ZEND_VM_TAIL_CALL(zend_fetch_var_address_helper_SPEC_TMPVAR_CONST(BP_VAR_W ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_fetch_static_prop_helper_SPEC_TMPVAR_CONST(BP_VAR_W ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
} else {
- ZEND_VM_TAIL_CALL(zend_fetch_var_address_helper_SPEC_TMPVAR_CONST(BP_VAR_R ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_fetch_static_prop_helper_SPEC_TMPVAR_CONST(BP_VAR_R ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_UNSET_SPEC_TMPVAR_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_STATIC_PROP_UNSET_SPEC_TMPVAR_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
- ZEND_VM_TAIL_CALL(zend_fetch_var_address_helper_SPEC_TMPVAR_CONST(BP_VAR_UNSET ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_fetch_static_prop_helper_SPEC_TMPVAR_CONST(BP_VAR_UNSET ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_IS_SPEC_TMPVAR_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_STATIC_PROP_IS_SPEC_TMPVAR_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
- ZEND_VM_TAIL_CALL(zend_fetch_var_address_helper_SPEC_TMPVAR_CONST(BP_VAR_IS ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_fetch_static_prop_helper_SPEC_TMPVAR_CONST(BP_VAR_IS ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_DIM_R_SPEC_TMPVAR_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
zend_free_op free_op1;
- zval *container;
+ zval *container, *dim, *value, *result;
SAVE_OPLINE();
container = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1);
- zend_fetch_dimension_address_read_R(EX_VAR(opline->result.var), container, EX_CONSTANT(opline->op2), IS_CONST);
+ dim = EX_CONSTANT(opline->op2);
+ if ((IS_TMP_VAR|IS_VAR) != IS_CONST) {
+ if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) {
+fetch_dim_r_array:
+ value = zend_fetch_dimension_address_inner(Z_ARRVAL_P(container), dim, IS_CONST, BP_VAR_R);
+ result = EX_VAR(opline->result.var);
+ ZVAL_COPY(result, value);
+ } else if (EXPECTED(Z_TYPE_P(container) == IS_REFERENCE)) {
+ container = Z_REFVAL_P(container);
+ if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) {
+ goto fetch_dim_r_array;
+ } else {
+ goto fetch_dim_r_slow;
+ }
+ } else {
+fetch_dim_r_slow:
+ result = EX_VAR(opline->result.var);
+ zend_fetch_dimension_address_read_R_slow(result, container, dim);
+ }
+ } else {
+ result = EX_VAR(opline->result.var);
+ zend_fetch_dimension_address_read_R(result, container, dim, IS_CONST);
+ }
zval_ptr_dtor_nogc(free_op1);
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
@@ -41782,7 +51965,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_IS_SPEC_TMPVAR_CONST
SAVE_OPLINE();
container = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1);
- if ((IS_TMP_VAR|IS_VAR) == IS_UNUSED && UNEXPECTED(Z_OBJ_P(container) == NULL)) {
+ if ((IS_TMP_VAR|IS_VAR) == IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
HANDLE_EXCEPTION();
@@ -41851,39 +52034,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_LIST_SPEC_TMPVAR_CONST_H
SAVE_OPLINE();
container = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1);
+ zend_fetch_dimension_address_read_LIST(EX_VAR(opline->result.var), container, EX_CONSTANT(opline->op2));
-try_fetch_list:
- if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) {
- zval *value = zend_hash_index_find(Z_ARRVAL_P(container), Z_LVAL_P(EX_CONSTANT(opline->op2)));
-
- if (UNEXPECTED(value == NULL)) {
- zend_error(E_NOTICE,"Undefined offset: " ZEND_ULONG_FMT, Z_LVAL_P(EX_CONSTANT(opline->op2)));
- ZVAL_NULL(EX_VAR(opline->result.var));
- } else {
- ZVAL_COPY(EX_VAR(opline->result.var), value);
- }
- } 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);
-
- if (retval) {
- if (result != retval) {
- ZVAL_COPY(result, retval);
- }
- } else {
- ZVAL_NULL(result);
- }
- } 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));
- }
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
@@ -41993,7 +52145,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_SPEC_TMPVAR_C
object = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1);
- if ((IS_TMP_VAR|IS_VAR) == IS_UNUSED && UNEXPECTED(Z_OBJ_P(object) == NULL)) {
+ if ((IS_TMP_VAR|IS_VAR) == IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
HANDLE_EXCEPTION();
@@ -42053,6 +52205,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_SPEC_TMPVAR_C
EXPECTED(obj == orig_obj)) {
CACHE_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(function_name), called_scope, fbc);
}
+ if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!fbc->op_array.run_time_cache)) {
+ init_func_run_time_cache(&fbc->op_array);
+ }
}
call_info = ZEND_CALL_NESTED_FUNCTION;
@@ -42148,40 +52303,14 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CASE_SPEC_TMPVAR_CONST_HANDLER
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_UNSET_VAR_SPEC_TMPVAR_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_UNSET_STATIC_PROP_SPEC_TMPVAR_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
zval tmp, *varname;
- HashTable *target_symbol_table;
+ zend_class_entry *ce;
zend_free_op free_op1;
SAVE_OPLINE();
- if ((IS_TMP_VAR|IS_VAR) == IS_CV &&
- IS_CONST == IS_UNUSED &&
- (opline->extended_value & ZEND_QUICK_SET)) {
- zval *var = EX_VAR(opline->op1.var);
-
- if (Z_REFCOUNTED_P(var)) {
- zend_refcounted *garbage = Z_COUNTED_P(var);
-
- if (!--GC_REFCOUNT(garbage)) {
- ZVAL_UNDEF(var);
- zval_dtor_func_for_ptr(garbage);
- } else {
- zval *z = var;
- ZVAL_DEREF(z);
- if (Z_COLLECTABLE_P(z) && UNEXPECTED(!Z_GC_INFO_P(z))) {
- ZVAL_UNDEF(var);
- gc_possible_root(Z_COUNTED_P(z));
- } else {
- ZVAL_UNDEF(var);
- }
- }
- } else {
- ZVAL_UNDEF(var);
- }
- ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
- }
varname = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1);
@@ -42194,33 +52323,36 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_UNSET_VAR_SPEC_TMPVAR_CONST_HA
varname = &tmp;
}
- if (IS_CONST != IS_UNUSED) {
- zend_class_entry *ce;
-
- if (IS_CONST == IS_CONST) {
- ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)));
+ if (IS_CONST == IS_CONST) {
+ ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)));
+ if (UNEXPECTED(ce == NULL)) {
+ ce = zend_fetch_class_by_name(Z_STR_P(EX_CONSTANT(opline->op2)), EX_CONSTANT(opline->op2) + 1, ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION);
if (UNEXPECTED(ce == NULL)) {
- ce = zend_fetch_class_by_name(Z_STR_P(EX_CONSTANT(opline->op2)), EX_CONSTANT(opline->op2) + 1, ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION);
- if (UNEXPECTED(ce == NULL)) {
- if (EXPECTED(!EG(exception))) {
- zend_throw_error(NULL, "Class '%s' not found", Z_STRVAL_P(EX_CONSTANT(opline->op2)));
- }
- if ((IS_TMP_VAR|IS_VAR) != IS_CONST && Z_TYPE(tmp) != IS_UNDEF) {
- zend_string_release(Z_STR(tmp));
- }
- zval_ptr_dtor_nogc(free_op1);
- HANDLE_EXCEPTION();
+ if (EXPECTED(!EG(exception))) {
+ zend_throw_error(NULL, "Class '%s' not found", Z_STRVAL_P(EX_CONSTANT(opline->op2)));
}
- CACHE_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)), ce);
+ if ((IS_TMP_VAR|IS_VAR) != IS_CONST && Z_TYPE(tmp) != IS_UNDEF) {
+ zend_string_release(Z_STR(tmp));
+ }
+ zval_ptr_dtor_nogc(free_op1);
+ HANDLE_EXCEPTION();
}
- } else {
- ce = Z_CE_P(EX_VAR(opline->op2.var));
+ CACHE_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)), ce);
+ }
+ } else if (IS_CONST == IS_UNUSED) {
+ ce = zend_fetch_class(NULL, opline->op2.num);
+ if (UNEXPECTED(ce == NULL)) {
+ ZEND_ASSERT(EG(exception));
+ if ((IS_TMP_VAR|IS_VAR) != IS_CONST && Z_TYPE(tmp) != IS_UNDEF) {
+ zend_string_release(Z_STR(tmp));
+ }
+ zval_ptr_dtor_nogc(free_op1);
+ HANDLE_EXCEPTION();
}
- zend_std_unset_static_property(ce, Z_STR_P(varname));
} else {
- target_symbol_table = zend_get_target_symbol_table(execute_data, opline->extended_value & ZEND_FETCH_TYPE_MASK);
- zend_hash_del_ind(target_symbol_table, Z_STR_P(varname));
+ ce = Z_CE_P(EX_VAR(opline->op2.var));
}
+ zend_std_unset_static_property(ce, Z_STR_P(varname));
if ((IS_TMP_VAR|IS_VAR) != IS_CONST && Z_TYPE(tmp) != IS_UNDEF) {
zend_string_release(Z_STR(tmp));
@@ -42229,104 +52361,88 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_UNSET_VAR_SPEC_TMPVAR_CONST_HA
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ISSET_ISEMPTY_VAR_SPEC_TMPVAR_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ISSET_ISEMPTY_STATIC_PROP_SPEC_TMPVAR_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
zval *value;
int result;
+ zend_free_op free_op1;
+ zval tmp, *varname;
+ zend_class_entry *ce;
- if ((IS_TMP_VAR|IS_VAR) == IS_CV &&
- IS_CONST == IS_UNUSED &&
- (opline->extended_value & ZEND_QUICK_SET)) {
- value = EX_VAR(opline->op1.var);
- if (opline->extended_value & ZEND_ISSET) {
- result =
- Z_TYPE_P(value) > IS_NULL &&
- (!Z_ISREF_P(value) || Z_TYPE_P(Z_REFVAL_P(value)) != IS_NULL);
- } else /* if (opline->extended_value & ZEND_ISEMPTY) */ {
- SAVE_OPLINE();
- result = !i_zend_is_true(value);
- if (UNEXPECTED(EG(exception))) {
- HANDLE_EXCEPTION();
- }
- }
- ZEND_VM_SMART_BRANCH(result, 0);
- ZVAL_BOOL(EX_VAR(opline->result.var), result);
- ZEND_VM_SET_NEXT_OPCODE(opline + 1);
- ZEND_VM_CONTINUE();
- } else {
- zend_free_op free_op1;
- zval tmp, *varname;
-
- SAVE_OPLINE();
- varname = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1);
- ZVAL_UNDEF(&tmp);
- if ((IS_TMP_VAR|IS_VAR) != IS_CONST && Z_TYPE_P(varname) != IS_STRING) {
- ZVAL_STR(&tmp, zval_get_string(varname));
- varname = &tmp;
- }
-
- if (IS_CONST != IS_UNUSED) {
- zend_class_entry *ce;
-
- if (IS_CONST == IS_CONST) {
- if ((IS_TMP_VAR|IS_VAR) == IS_CONST && EXPECTED((ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)))) != NULL)) {
- value = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)) + sizeof(void*));
-
- /* check if static properties were destoyed */
- if (UNEXPECTED(CE_STATIC_MEMBERS(ce) == NULL)) {
- value = NULL;
- }
-
- goto is_var_return;
- } else if (UNEXPECTED((ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)))) == NULL)) {
- ce = zend_fetch_class_by_name(Z_STR_P(EX_CONSTANT(opline->op2)), EX_CONSTANT(opline->op2) + 1, ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION);
- if (UNEXPECTED(ce == NULL)) {
- ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
- }
- CACHE_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)), ce);
- }
- } else {
- ce = Z_CE_P(EX_VAR(opline->op2.var));
- if ((IS_TMP_VAR|IS_VAR) == IS_CONST &&
- (value = CACHED_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)), ce)) != NULL) {
+ SAVE_OPLINE();
+ varname = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1);
+ ZVAL_UNDEF(&tmp);
+ if ((IS_TMP_VAR|IS_VAR) != IS_CONST && Z_TYPE_P(varname) != IS_STRING) {
+ ZVAL_STR(&tmp, zval_get_string(varname));
+ varname = &tmp;
+ }
- /* check if static properties were destoyed */
- if (UNEXPECTED(CE_STATIC_MEMBERS(ce) == NULL)) {
- value = NULL;
- }
+ if (IS_CONST == IS_CONST) {
+ if ((IS_TMP_VAR|IS_VAR) == IS_CONST && EXPECTED((ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)))) != NULL)) {
+ value = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)) + sizeof(void*));
- goto is_var_return;
- }
+ /* check if static properties were destoyed */
+ if (UNEXPECTED(CE_STATIC_MEMBERS(ce) == NULL)) {
+ value = NULL;
}
- value = zend_std_get_static_property(ce, Z_STR_P(varname), 1);
-
- if ((IS_TMP_VAR|IS_VAR) == IS_CONST && value) {
- CACHE_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)), ce, value);
+ goto is_static_prop_return;
+ } else if (UNEXPECTED((ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)))) == NULL)) {
+ ce = zend_fetch_class_by_name(Z_STR_P(EX_CONSTANT(opline->op2)), EX_CONSTANT(opline->op2) + 1, ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION);
+ if (UNEXPECTED(ce == NULL)) {
+ ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
+ }
+ CACHE_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)), ce);
+ }
+ } else {
+ if (IS_CONST == IS_UNUSED) {
+ ce = zend_fetch_class(NULL, opline->op2.num);
+ if (UNEXPECTED(ce == NULL)) {
+ ZEND_ASSERT(EG(exception));
+ if ((IS_TMP_VAR|IS_VAR) != IS_CONST && Z_TYPE(tmp) != IS_UNDEF) {
+ zend_string_release(Z_STR(tmp));
+ }
+ zval_ptr_dtor_nogc(free_op1);
+ HANDLE_EXCEPTION();
}
} else {
- HashTable *target_symbol_table = zend_get_target_symbol_table(execute_data, opline->extended_value & ZEND_FETCH_TYPE_MASK);
- value = zend_hash_find_ind(target_symbol_table, Z_STR_P(varname));
+ ce = Z_CE_P(EX_VAR(opline->op2.var));
}
+ if ((IS_TMP_VAR|IS_VAR) == IS_CONST &&
+ (value = CACHED_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)), ce)) != NULL) {
- if ((IS_TMP_VAR|IS_VAR) != IS_CONST && Z_TYPE(tmp) != IS_UNDEF) {
- zend_string_release(Z_STR(tmp));
- }
- zval_ptr_dtor_nogc(free_op1);
+ /* check if static properties were destoyed */
+ if (UNEXPECTED(CE_STATIC_MEMBERS(ce) == NULL)) {
+ value = NULL;
+ }
-is_var_return:
- if (opline->extended_value & ZEND_ISSET) {
- result = value && Z_TYPE_P(value) > IS_NULL &&
- (!Z_ISREF_P(value) || Z_TYPE_P(Z_REFVAL_P(value)) != IS_NULL);
- } else /* if (opline->extended_value & ZEND_ISEMPTY) */ {
- result = !value || !i_zend_is_true(value);
+ goto is_static_prop_return;
}
+ }
- ZEND_VM_SMART_BRANCH(result, 1);
- ZVAL_BOOL(EX_VAR(opline->result.var), result);
- ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
+ value = zend_std_get_static_property(ce, Z_STR_P(varname), 1);
+
+ if ((IS_TMP_VAR|IS_VAR) == IS_CONST && value) {
+ CACHE_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)), ce, value);
+ }
+
+ if ((IS_TMP_VAR|IS_VAR) != IS_CONST && Z_TYPE(tmp) != IS_UNDEF) {
+ zend_string_release(Z_STR(tmp));
}
+ zval_ptr_dtor_nogc(free_op1);
+
+is_static_prop_return:
+ if (opline->extended_value & ZEND_ISSET) {
+ result = value && Z_TYPE_P(value) > IS_NULL &&
+ (!Z_ISREF_P(value) || Z_TYPE_P(Z_REFVAL_P(value)) != IS_NULL);
+ } else /* if (opline->extended_value & ZEND_ISEMPTY) */ {
+ result = !value || !i_zend_is_true(value);
+ }
+
+ ZEND_VM_SMART_BRANCH(result, 1);
+ ZVAL_BOOL(EX_VAR(opline->result.var), result);
+ ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ISSET_ISEMPTY_DIM_OBJ_SPEC_TMPVAR_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@@ -42340,16 +52456,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ISSET_ISEMPTY_DIM_OBJ_SPEC_TMP
SAVE_OPLINE();
container = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1);
-
- if ((IS_TMP_VAR|IS_VAR) == IS_UNUSED && UNEXPECTED(Z_OBJ_P(container) == NULL)) {
- zend_throw_error(NULL, "Using $this when not in object context");
-
- HANDLE_EXCEPTION();
- }
-
offset = EX_CONSTANT(opline->op2);
- if ((IS_TMP_VAR|IS_VAR) != IS_UNUSED && EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) {
+ if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) {
HashTable *ht;
zval *value;
zend_string *str;
@@ -42416,8 +52525,7 @@ num_index_prop:
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 (((IS_TMP_VAR|IS_VAR) != IS_CONST && EXPECTED(Z_TYPE_P(container) == IS_OBJECT))) {
if (EXPECTED(Z_OBJ_HT_P(container)->has_dimension)) {
result =
((opline->extended_value & ZEND_ISSET) == 0) ^
@@ -42432,6 +52540,9 @@ num_index_prop:
if (EXPECTED(Z_TYPE_P(offset) == IS_LONG)) {
lval = Z_LVAL_P(offset);
isset_str_offset:
+ if (UNEXPECTED(lval < 0)) { /* Handle negative offset */
+ lval += (zend_long)Z_STRLEN_P(container);
+ }
if (EXPECTED(lval >= 0) && (size_t)lval < Z_STRLEN_P(container)) {
if (opline->extended_value & ZEND_ISSET) {
result = 1;
@@ -42477,7 +52588,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ISSET_ISEMPTY_PROP_OBJ_SPEC_TM
SAVE_OPLINE();
container = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1);
- if ((IS_TMP_VAR|IS_VAR) == IS_UNUSED && UNEXPECTED(Z_OBJ_P(container) == NULL)) {
+ if ((IS_TMP_VAR|IS_VAR) == IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
HANDLE_EXCEPTION();
@@ -42537,6 +52648,13 @@ try_instanceof:
}
CACHE_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)), ce);
}
+ } else if (IS_CONST == IS_UNUSED) {
+ ce = zend_fetch_class(NULL, opline->op2.num);
+ if (UNEXPECTED(ce == NULL)) {
+ ZEND_ASSERT(EG(exception));
+ zval_ptr_dtor_nogc(free_op1);
+ HANDLE_EXCEPTION();
+ }
} else {
ce = Z_CE_P(EX_VAR(opline->op2.var));
}
@@ -42556,14 +52674,62 @@ try_instanceof:
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_fetch_var_address_helper_SPEC_TMPVAR_VAR(int type ZEND_OPCODE_HANDLER_ARGS_DC)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zend_free_op free_op1;
+ zval *container, *dim, *value;
+ zend_long offset;
+
+ container = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1);
+ dim = EX_CONSTANT(opline->op2);
+ if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) {
+fetch_dim_r_index_array:
+ if (EXPECTED(Z_TYPE_P(dim) == IS_LONG)) {
+ offset = Z_LVAL_P(dim);
+ } else {
+ offset = zval_get_long(dim);
+ }
+ ZEND_HASH_INDEX_FIND(Z_ARRVAL_P(container), offset, value, fetch_dim_r_index_undef);
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ if ((IS_TMP_VAR|IS_VAR) & (IS_TMP_VAR|IS_VAR)) {
+ SAVE_OPLINE();
+ zval_ptr_dtor_nogc(free_op1);
+ ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
+ } else {
+ ZEND_VM_NEXT_OPCODE();
+ }
+ } else if ((IS_TMP_VAR|IS_VAR) != IS_CONST && EXPECTED(Z_TYPE_P(container) == IS_REFERENCE)) {
+ container = Z_REFVAL_P(container);
+ if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) {
+ goto fetch_dim_r_index_array;
+ } else {
+ goto fetch_dim_r_index_slow;
+ }
+ } else {
+fetch_dim_r_index_slow:
+ SAVE_OPLINE();
+ zend_fetch_dimension_address_read_R_slow(EX_VAR(opline->result.var), container, dim);
+ zval_ptr_dtor_nogc(free_op1);
+ ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
+ }
+
+fetch_dim_r_index_undef:
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ SAVE_OPLINE();
+ zend_error(E_NOTICE, "Undefined offset: " ZEND_LONG_FMT, offset);
+ zval_ptr_dtor_nogc(free_op1);
+ ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_fetch_static_prop_helper_SPEC_TMPVAR_VAR(int type ZEND_OPCODE_HANDLER_ARGS_DC)
{
USE_OPLINE
zend_free_op free_op1;
zval *varname;
zval *retval;
zend_string *name;
- HashTable *target_symbol_table;
+ zend_class_entry *ce;
SAVE_OPLINE();
varname = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1);
@@ -42580,120 +52746,75 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_fetch_var_address_helper_SPEC_
name = zval_get_string(varname);
}
- if (IS_VAR != IS_UNUSED) {
- zend_class_entry *ce;
-
- if (IS_VAR == IS_CONST) {
- if ((IS_TMP_VAR|IS_VAR) == IS_CONST && EXPECTED((ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)))) != NULL)) {
- retval = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)) + sizeof(void*));
-
- /* check if static properties were destoyed */
- if (UNEXPECTED(CE_STATIC_MEMBERS(ce) == NULL)) {
- zend_throw_error(NULL, "Access to undeclared static property: %s::$%s", ZSTR_VAL(ce->name), ZSTR_VAL(name));
- zval_ptr_dtor_nogc(free_op1);
- HANDLE_EXCEPTION();
- }
+ if (IS_VAR == IS_CONST) {
+ if ((IS_TMP_VAR|IS_VAR) == IS_CONST && EXPECTED((ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)))) != NULL)) {
+ retval = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)) + sizeof(void*));
- goto fetch_var_return;
- } else if (UNEXPECTED((ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)))) == NULL)) {
- ce = zend_fetch_class_by_name(Z_STR_P(EX_CONSTANT(opline->op2)), EX_CONSTANT(opline->op2) + 1, ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION);
- if (UNEXPECTED(ce == NULL)) {
- if ((IS_TMP_VAR|IS_VAR) != IS_CONST) {
- zend_string_release(name);
- }
- zval_ptr_dtor_nogc(free_op1);
- ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
- }
- CACHE_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)), ce);
+ /* check if static properties were destoyed */
+ if (UNEXPECTED(CE_STATIC_MEMBERS(ce) == NULL)) {
+ zend_throw_error(NULL, "Access to undeclared static property: %s::$%s", ZSTR_VAL(ce->name), ZSTR_VAL(name));
+ zval_ptr_dtor_nogc(free_op1);
+ HANDLE_EXCEPTION();
}
- } else {
- ce = Z_CE_P(EX_VAR(opline->op2.var));
- if ((IS_TMP_VAR|IS_VAR) == IS_CONST &&
- (retval = CACHED_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)), ce)) != NULL) {
- /* check if static properties were destoyed */
- if (UNEXPECTED(CE_STATIC_MEMBERS(ce) == NULL)) {
- zend_throw_error(NULL, "Access to undeclared static property: %s::$%s", ZSTR_VAL(ce->name), ZSTR_VAL(name));
- zval_ptr_dtor_nogc(free_op1);
- HANDLE_EXCEPTION();
+ goto fetch_static_prop_return;
+ } else if (UNEXPECTED((ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)))) == NULL)) {
+ ce = zend_fetch_class_by_name(Z_STR_P(EX_CONSTANT(opline->op2)), EX_CONSTANT(opline->op2) + 1, ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION);
+ if (UNEXPECTED(ce == NULL)) {
+ if ((IS_TMP_VAR|IS_VAR) != IS_CONST) {
+ zend_string_release(name);
}
-
- goto fetch_var_return;
- }
- }
- retval = zend_std_get_static_property(ce, name, 0);
- if (UNEXPECTED(EG(exception))) {
- if ((IS_TMP_VAR|IS_VAR) != IS_CONST) {
- zend_string_release(name);
+ zval_ptr_dtor_nogc(free_op1);
+ ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
- zval_ptr_dtor_nogc(free_op1);
- HANDLE_EXCEPTION();
+ CACHE_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)), ce);
}
- if ((IS_TMP_VAR|IS_VAR) == IS_CONST && retval) {
- CACHE_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)), ce, retval);
- }
-
- zval_ptr_dtor_nogc(free_op1);
} else {
- target_symbol_table = zend_get_target_symbol_table(execute_data, opline->extended_value & ZEND_FETCH_TYPE_MASK);
- retval = zend_hash_find(target_symbol_table, name);
- if (retval == NULL) {
- switch (type) {
- case BP_VAR_R:
- case BP_VAR_UNSET:
- zend_error(E_NOTICE,"Undefined variable: %s", ZSTR_VAL(name));
- /* break missing intentionally */
- case BP_VAR_IS:
- retval = &EG(uninitialized_zval);
- break;
- case BP_VAR_RW:
- zend_error(E_NOTICE,"Undefined variable: %s", ZSTR_VAL(name));
- retval = zend_hash_update(target_symbol_table, name, &EG(uninitialized_zval));
- break;
- case BP_VAR_W:
- retval = zend_hash_add_new(target_symbol_table, name, &EG(uninitialized_zval));
- break;
- EMPTY_SWITCH_DEFAULT_CASE()
- }
- /* GLOBAL or $$name variable may be an INDIRECT pointer to CV */
- } else if (Z_TYPE_P(retval) == IS_INDIRECT) {
- retval = Z_INDIRECT_P(retval);
- if (Z_TYPE_P(retval) == IS_UNDEF) {
- switch (type) {
- case BP_VAR_R:
- case BP_VAR_UNSET:
- zend_error(E_NOTICE,"Undefined variable: %s", ZSTR_VAL(name));
- /* break missing intentionally */
- case BP_VAR_IS:
- retval = &EG(uninitialized_zval);
- break;
- case BP_VAR_RW:
- zend_error(E_NOTICE,"Undefined variable: %s", ZSTR_VAL(name));
- /* break missing intentionally */
- case BP_VAR_W:
- ZVAL_NULL(retval);
- break;
- EMPTY_SWITCH_DEFAULT_CASE()
+ if (IS_VAR == IS_UNUSED) {
+ ce = zend_fetch_class(NULL, opline->op2.num);
+ if (UNEXPECTED(ce == NULL)) {
+ ZEND_ASSERT(EG(exception));
+ if ((IS_TMP_VAR|IS_VAR) != IS_CONST) {
+ zend_string_release(name);
}
+ zval_ptr_dtor_nogc(free_op1);
+ HANDLE_EXCEPTION();
}
+ } else {
+ ce = Z_CE_P(EX_VAR(opline->op2.var));
}
- if ((opline->extended_value & ZEND_FETCH_TYPE_MASK) == ZEND_FETCH_STATIC) {
- if (Z_CONSTANT_P(retval)) {
- if (UNEXPECTED(zval_update_constant_ex(retval, 1, NULL) != SUCCESS)) {
- zval_ptr_dtor_nogc(free_op1);
- HANDLE_EXCEPTION();
- }
+ if ((IS_TMP_VAR|IS_VAR) == IS_CONST &&
+ (retval = CACHED_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)), ce)) != NULL) {
+
+ /* check if static properties were destoyed */
+ if (UNEXPECTED(CE_STATIC_MEMBERS(ce) == NULL)) {
+ zend_throw_error(NULL, "Access to undeclared static property: %s::$%s", ZSTR_VAL(ce->name), ZSTR_VAL(name));
+ zval_ptr_dtor_nogc(free_op1);
+ HANDLE_EXCEPTION();
}
- } else if ((opline->extended_value & ZEND_FETCH_TYPE_MASK) != ZEND_FETCH_GLOBAL_LOCK) {
- zval_ptr_dtor_nogc(free_op1);
+
+ goto fetch_static_prop_return;
}
}
+ retval = zend_std_get_static_property(ce, name, 0);
+ if (UNEXPECTED(EG(exception))) {
+ if ((IS_TMP_VAR|IS_VAR) != IS_CONST) {
+ zend_string_release(name);
+ }
+ zval_ptr_dtor_nogc(free_op1);
+ HANDLE_EXCEPTION();
+ }
+ if ((IS_TMP_VAR|IS_VAR) == IS_CONST && retval) {
+ CACHE_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)), ce, retval);
+ }
+
+ zval_ptr_dtor_nogc(free_op1);
if ((IS_TMP_VAR|IS_VAR) != IS_CONST) {
zend_string_release(name);
}
-fetch_var_return:
+fetch_static_prop_return:
ZEND_ASSERT(retval != NULL);
if (type == BP_VAR_R || type == BP_VAR_IS) {
if (/*type == BP_VAR_R &&*/ Z_ISREF_P(retval) && Z_REFCOUNT_P(retval) == 1) {
@@ -42706,76 +52827,50 @@ fetch_var_return:
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_R_SPEC_TMPVAR_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_STATIC_PROP_R_SPEC_TMPVAR_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
- ZEND_VM_TAIL_CALL(zend_fetch_var_address_helper_SPEC_TMPVAR_VAR(BP_VAR_R ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_fetch_static_prop_helper_SPEC_TMPVAR_VAR(BP_VAR_R ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_W_SPEC_TMPVAR_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_STATIC_PROP_W_SPEC_TMPVAR_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
- ZEND_VM_TAIL_CALL(zend_fetch_var_address_helper_SPEC_TMPVAR_VAR(BP_VAR_W ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_fetch_static_prop_helper_SPEC_TMPVAR_VAR(BP_VAR_W ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_RW_SPEC_TMPVAR_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_STATIC_PROP_RW_SPEC_TMPVAR_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
- ZEND_VM_TAIL_CALL(zend_fetch_var_address_helper_SPEC_TMPVAR_VAR(BP_VAR_RW ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_fetch_static_prop_helper_SPEC_TMPVAR_VAR(BP_VAR_RW ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_FUNC_ARG_SPEC_TMPVAR_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_STATIC_PROP_FUNC_ARG_SPEC_TMPVAR_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
if (zend_is_by_ref_func_arg_fetch(opline, EX(call))) {
- ZEND_VM_TAIL_CALL(zend_fetch_var_address_helper_SPEC_TMPVAR_VAR(BP_VAR_W ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_fetch_static_prop_helper_SPEC_TMPVAR_VAR(BP_VAR_W ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
} else {
- ZEND_VM_TAIL_CALL(zend_fetch_var_address_helper_SPEC_TMPVAR_VAR(BP_VAR_R ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_fetch_static_prop_helper_SPEC_TMPVAR_VAR(BP_VAR_R ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_UNSET_SPEC_TMPVAR_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_STATIC_PROP_UNSET_SPEC_TMPVAR_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
- ZEND_VM_TAIL_CALL(zend_fetch_var_address_helper_SPEC_TMPVAR_VAR(BP_VAR_UNSET ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_fetch_static_prop_helper_SPEC_TMPVAR_VAR(BP_VAR_UNSET ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_IS_SPEC_TMPVAR_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_STATIC_PROP_IS_SPEC_TMPVAR_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
- ZEND_VM_TAIL_CALL(zend_fetch_var_address_helper_SPEC_TMPVAR_VAR(BP_VAR_IS ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_fetch_static_prop_helper_SPEC_TMPVAR_VAR(BP_VAR_IS ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_UNSET_VAR_SPEC_TMPVAR_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_UNSET_STATIC_PROP_SPEC_TMPVAR_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
zval tmp, *varname;
- HashTable *target_symbol_table;
+ zend_class_entry *ce;
zend_free_op free_op1;
SAVE_OPLINE();
- if ((IS_TMP_VAR|IS_VAR) == IS_CV &&
- IS_VAR == IS_UNUSED &&
- (opline->extended_value & ZEND_QUICK_SET)) {
- zval *var = EX_VAR(opline->op1.var);
-
- if (Z_REFCOUNTED_P(var)) {
- zend_refcounted *garbage = Z_COUNTED_P(var);
-
- if (!--GC_REFCOUNT(garbage)) {
- ZVAL_UNDEF(var);
- zval_dtor_func_for_ptr(garbage);
- } else {
- zval *z = var;
- ZVAL_DEREF(z);
- if (Z_COLLECTABLE_P(z) && UNEXPECTED(!Z_GC_INFO_P(z))) {
- ZVAL_UNDEF(var);
- gc_possible_root(Z_COUNTED_P(z));
- } else {
- ZVAL_UNDEF(var);
- }
- }
- } else {
- ZVAL_UNDEF(var);
- }
- ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
- }
varname = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1);
@@ -42788,33 +52883,36 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_UNSET_VAR_SPEC_TMPVAR_VAR_HAND
varname = &tmp;
}
- if (IS_VAR != IS_UNUSED) {
- zend_class_entry *ce;
-
- if (IS_VAR == IS_CONST) {
- ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)));
+ if (IS_VAR == IS_CONST) {
+ ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)));
+ if (UNEXPECTED(ce == NULL)) {
+ ce = zend_fetch_class_by_name(Z_STR_P(EX_CONSTANT(opline->op2)), EX_CONSTANT(opline->op2) + 1, ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION);
if (UNEXPECTED(ce == NULL)) {
- ce = zend_fetch_class_by_name(Z_STR_P(EX_CONSTANT(opline->op2)), EX_CONSTANT(opline->op2) + 1, ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION);
- if (UNEXPECTED(ce == NULL)) {
- if (EXPECTED(!EG(exception))) {
- zend_throw_error(NULL, "Class '%s' not found", Z_STRVAL_P(EX_CONSTANT(opline->op2)));
- }
- if ((IS_TMP_VAR|IS_VAR) != IS_CONST && Z_TYPE(tmp) != IS_UNDEF) {
- zend_string_release(Z_STR(tmp));
- }
- zval_ptr_dtor_nogc(free_op1);
- HANDLE_EXCEPTION();
+ if (EXPECTED(!EG(exception))) {
+ zend_throw_error(NULL, "Class '%s' not found", Z_STRVAL_P(EX_CONSTANT(opline->op2)));
}
- CACHE_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)), ce);
+ if ((IS_TMP_VAR|IS_VAR) != IS_CONST && Z_TYPE(tmp) != IS_UNDEF) {
+ zend_string_release(Z_STR(tmp));
+ }
+ zval_ptr_dtor_nogc(free_op1);
+ HANDLE_EXCEPTION();
}
- } else {
- ce = Z_CE_P(EX_VAR(opline->op2.var));
+ CACHE_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)), ce);
+ }
+ } else if (IS_VAR == IS_UNUSED) {
+ ce = zend_fetch_class(NULL, opline->op2.num);
+ if (UNEXPECTED(ce == NULL)) {
+ ZEND_ASSERT(EG(exception));
+ if ((IS_TMP_VAR|IS_VAR) != IS_CONST && Z_TYPE(tmp) != IS_UNDEF) {
+ zend_string_release(Z_STR(tmp));
+ }
+ zval_ptr_dtor_nogc(free_op1);
+ HANDLE_EXCEPTION();
}
- zend_std_unset_static_property(ce, Z_STR_P(varname));
} else {
- target_symbol_table = zend_get_target_symbol_table(execute_data, opline->extended_value & ZEND_FETCH_TYPE_MASK);
- zend_hash_del_ind(target_symbol_table, Z_STR_P(varname));
+ ce = Z_CE_P(EX_VAR(opline->op2.var));
}
+ zend_std_unset_static_property(ce, Z_STR_P(varname));
if ((IS_TMP_VAR|IS_VAR) != IS_CONST && Z_TYPE(tmp) != IS_UNDEF) {
zend_string_release(Z_STR(tmp));
@@ -42823,104 +52921,88 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_UNSET_VAR_SPEC_TMPVAR_VAR_HAND
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ISSET_ISEMPTY_VAR_SPEC_TMPVAR_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ISSET_ISEMPTY_STATIC_PROP_SPEC_TMPVAR_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
zval *value;
int result;
+ zend_free_op free_op1;
+ zval tmp, *varname;
+ zend_class_entry *ce;
- if ((IS_TMP_VAR|IS_VAR) == IS_CV &&
- IS_VAR == IS_UNUSED &&
- (opline->extended_value & ZEND_QUICK_SET)) {
- value = EX_VAR(opline->op1.var);
- if (opline->extended_value & ZEND_ISSET) {
- result =
- Z_TYPE_P(value) > IS_NULL &&
- (!Z_ISREF_P(value) || Z_TYPE_P(Z_REFVAL_P(value)) != IS_NULL);
- } else /* if (opline->extended_value & ZEND_ISEMPTY) */ {
- SAVE_OPLINE();
- result = !i_zend_is_true(value);
- if (UNEXPECTED(EG(exception))) {
- HANDLE_EXCEPTION();
- }
- }
- ZEND_VM_SMART_BRANCH(result, 0);
- ZVAL_BOOL(EX_VAR(opline->result.var), result);
- ZEND_VM_SET_NEXT_OPCODE(opline + 1);
- ZEND_VM_CONTINUE();
- } else {
- zend_free_op free_op1;
- zval tmp, *varname;
-
- SAVE_OPLINE();
- varname = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1);
- ZVAL_UNDEF(&tmp);
- if ((IS_TMP_VAR|IS_VAR) != IS_CONST && Z_TYPE_P(varname) != IS_STRING) {
- ZVAL_STR(&tmp, zval_get_string(varname));
- varname = &tmp;
- }
-
- if (IS_VAR != IS_UNUSED) {
- zend_class_entry *ce;
-
- if (IS_VAR == IS_CONST) {
- if ((IS_TMP_VAR|IS_VAR) == IS_CONST && EXPECTED((ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)))) != NULL)) {
- value = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)) + sizeof(void*));
-
- /* check if static properties were destoyed */
- if (UNEXPECTED(CE_STATIC_MEMBERS(ce) == NULL)) {
- value = NULL;
- }
-
- goto is_var_return;
- } else if (UNEXPECTED((ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)))) == NULL)) {
- ce = zend_fetch_class_by_name(Z_STR_P(EX_CONSTANT(opline->op2)), EX_CONSTANT(opline->op2) + 1, ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION);
- if (UNEXPECTED(ce == NULL)) {
- ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
- }
- CACHE_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)), ce);
- }
- } else {
- ce = Z_CE_P(EX_VAR(opline->op2.var));
- if ((IS_TMP_VAR|IS_VAR) == IS_CONST &&
- (value = CACHED_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)), ce)) != NULL) {
+ SAVE_OPLINE();
+ varname = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1);
+ ZVAL_UNDEF(&tmp);
+ if ((IS_TMP_VAR|IS_VAR) != IS_CONST && Z_TYPE_P(varname) != IS_STRING) {
+ ZVAL_STR(&tmp, zval_get_string(varname));
+ varname = &tmp;
+ }
- /* check if static properties were destoyed */
- if (UNEXPECTED(CE_STATIC_MEMBERS(ce) == NULL)) {
- value = NULL;
- }
+ if (IS_VAR == IS_CONST) {
+ if ((IS_TMP_VAR|IS_VAR) == IS_CONST && EXPECTED((ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)))) != NULL)) {
+ value = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)) + sizeof(void*));
- goto is_var_return;
- }
+ /* check if static properties were destoyed */
+ if (UNEXPECTED(CE_STATIC_MEMBERS(ce) == NULL)) {
+ value = NULL;
}
- value = zend_std_get_static_property(ce, Z_STR_P(varname), 1);
-
- if ((IS_TMP_VAR|IS_VAR) == IS_CONST && value) {
- CACHE_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)), ce, value);
+ goto is_static_prop_return;
+ } else if (UNEXPECTED((ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)))) == NULL)) {
+ ce = zend_fetch_class_by_name(Z_STR_P(EX_CONSTANT(opline->op2)), EX_CONSTANT(opline->op2) + 1, ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION);
+ if (UNEXPECTED(ce == NULL)) {
+ ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
+ }
+ CACHE_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)), ce);
+ }
+ } else {
+ if (IS_VAR == IS_UNUSED) {
+ ce = zend_fetch_class(NULL, opline->op2.num);
+ if (UNEXPECTED(ce == NULL)) {
+ ZEND_ASSERT(EG(exception));
+ if ((IS_TMP_VAR|IS_VAR) != IS_CONST && Z_TYPE(tmp) != IS_UNDEF) {
+ zend_string_release(Z_STR(tmp));
+ }
+ zval_ptr_dtor_nogc(free_op1);
+ HANDLE_EXCEPTION();
}
} else {
- HashTable *target_symbol_table = zend_get_target_symbol_table(execute_data, opline->extended_value & ZEND_FETCH_TYPE_MASK);
- value = zend_hash_find_ind(target_symbol_table, Z_STR_P(varname));
+ ce = Z_CE_P(EX_VAR(opline->op2.var));
}
+ if ((IS_TMP_VAR|IS_VAR) == IS_CONST &&
+ (value = CACHED_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)), ce)) != NULL) {
- if ((IS_TMP_VAR|IS_VAR) != IS_CONST && Z_TYPE(tmp) != IS_UNDEF) {
- zend_string_release(Z_STR(tmp));
- }
- zval_ptr_dtor_nogc(free_op1);
+ /* check if static properties were destoyed */
+ if (UNEXPECTED(CE_STATIC_MEMBERS(ce) == NULL)) {
+ value = NULL;
+ }
-is_var_return:
- if (opline->extended_value & ZEND_ISSET) {
- result = value && Z_TYPE_P(value) > IS_NULL &&
- (!Z_ISREF_P(value) || Z_TYPE_P(Z_REFVAL_P(value)) != IS_NULL);
- } else /* if (opline->extended_value & ZEND_ISEMPTY) */ {
- result = !value || !i_zend_is_true(value);
+ goto is_static_prop_return;
}
+ }
- ZEND_VM_SMART_BRANCH(result, 1);
- ZVAL_BOOL(EX_VAR(opline->result.var), result);
- ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
+ value = zend_std_get_static_property(ce, Z_STR_P(varname), 1);
+
+ if ((IS_TMP_VAR|IS_VAR) == IS_CONST && value) {
+ CACHE_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)), ce, value);
+ }
+
+ if ((IS_TMP_VAR|IS_VAR) != IS_CONST && Z_TYPE(tmp) != IS_UNDEF) {
+ zend_string_release(Z_STR(tmp));
+ }
+ zval_ptr_dtor_nogc(free_op1);
+
+is_static_prop_return:
+ if (opline->extended_value & ZEND_ISSET) {
+ result = value && Z_TYPE_P(value) > IS_NULL &&
+ (!Z_ISREF_P(value) || Z_TYPE_P(Z_REFVAL_P(value)) != IS_NULL);
+ } else /* if (opline->extended_value & ZEND_ISEMPTY) */ {
+ result = !value || !i_zend_is_true(value);
}
+
+ ZEND_VM_SMART_BRANCH(result, 1);
+ ZVAL_BOOL(EX_VAR(opline->result.var), result);
+ ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INSTANCEOF_SPEC_TMPVAR_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@@ -42948,6 +53030,13 @@ try_instanceof:
}
CACHE_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)), ce);
}
+ } else if (IS_VAR == IS_UNUSED) {
+ ce = zend_fetch_class(NULL, opline->op2.num);
+ if (UNEXPECTED(ce == NULL)) {
+ ZEND_ASSERT(EG(exception));
+ zval_ptr_dtor_nogc(free_op1);
+ HANDLE_EXCEPTION();
+ }
} else {
ce = Z_CE_P(EX_VAR(opline->op2.var));
}
@@ -42991,64 +53080,70 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_fetch_var_address_helper_SPEC_
name = zval_get_string(varname);
}
- if (IS_UNUSED != IS_UNUSED) {
- zend_class_entry *ce;
-
- if (IS_UNUSED == IS_CONST) {
- if ((IS_TMP_VAR|IS_VAR) == IS_CONST && EXPECTED((ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)))) != NULL)) {
- retval = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)) + sizeof(void*));
+ target_symbol_table = zend_get_target_symbol_table(execute_data, opline->extended_value & ZEND_FETCH_TYPE_MASK);
+ retval = zend_hash_find(target_symbol_table, name);
+ if (retval == NULL) {
+ if (UNEXPECTED(zend_string_equals(name, CG(known_strings)[ZEND_STR_THIS]))) {
+ zval *result;
- /* check if static properties were destoyed */
- if (UNEXPECTED(CE_STATIC_MEMBERS(ce) == NULL)) {
- zend_throw_error(NULL, "Access to undeclared static property: %s::$%s", ZSTR_VAL(ce->name), ZSTR_VAL(name));
- zval_ptr_dtor_nogc(free_op1);
- HANDLE_EXCEPTION();
- }
-
- goto fetch_var_return;
- } else if (UNEXPECTED((ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)))) == NULL)) {
- ce = zend_fetch_class_by_name(Z_STR_P(EX_CONSTANT(opline->op2)), EX_CONSTANT(opline->op2) + 1, ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION);
- if (UNEXPECTED(ce == NULL)) {
- if ((IS_TMP_VAR|IS_VAR) != IS_CONST) {
- zend_string_release(name);
+fetch_this:
+ result = EX_VAR(opline->result.var);
+ switch (type) {
+ case BP_VAR_R:
+ if (EXPECTED(Z_TYPE(EX(This)) == IS_OBJECT)) {
+ ZVAL_OBJ(result, Z_OBJ(EX(This)));
+ Z_ADDREF_P(result);
+ } else {
+ ZVAL_NULL(result);
+ zend_error(E_NOTICE,"Undefined variable: this");
}
- zval_ptr_dtor_nogc(free_op1);
- ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
- }
- CACHE_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)), ce);
- }
- } else {
- ce = Z_CE_P(EX_VAR(opline->op2.var));
- if ((IS_TMP_VAR|IS_VAR) == IS_CONST &&
- (retval = CACHED_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)), ce)) != NULL) {
-
- /* check if static properties were destoyed */
- if (UNEXPECTED(CE_STATIC_MEMBERS(ce) == NULL)) {
- zend_throw_error(NULL, "Access to undeclared static property: %s::$%s", ZSTR_VAL(ce->name), ZSTR_VAL(name));
- zval_ptr_dtor_nogc(free_op1);
- HANDLE_EXCEPTION();
- }
-
- goto fetch_var_return;
+ break;
+ case BP_VAR_IS:
+ if (EXPECTED(Z_TYPE(EX(This)) == IS_OBJECT)) {
+ ZVAL_OBJ(result, Z_OBJ(EX(This)));
+ Z_ADDREF_P(result);
+ } else {
+ ZVAL_NULL(result);
+ }
+ break;
+ case BP_VAR_RW:
+ case BP_VAR_W:
+ zend_throw_error(NULL, "Cannot re-assign $this");
+ break;
+ case BP_VAR_UNSET:
+ zend_throw_error(NULL, "Cannot unset $this");
+ break;
+ EMPTY_SWITCH_DEFAULT_CASE()
}
- }
- retval = zend_std_get_static_property(ce, name, 0);
- if (UNEXPECTED(EG(exception))) {
if ((IS_TMP_VAR|IS_VAR) != IS_CONST) {
zend_string_release(name);
}
- zval_ptr_dtor_nogc(free_op1);
- HANDLE_EXCEPTION();
+ ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
- if ((IS_TMP_VAR|IS_VAR) == IS_CONST && retval) {
- CACHE_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)), ce, retval);
+ switch (type) {
+ case BP_VAR_R:
+ case BP_VAR_UNSET:
+ zend_error(E_NOTICE,"Undefined variable: %s", ZSTR_VAL(name));
+ /* break missing intentionally */
+ case BP_VAR_IS:
+ retval = &EG(uninitialized_zval);
+ break;
+ case BP_VAR_RW:
+ zend_error(E_NOTICE,"Undefined variable: %s", ZSTR_VAL(name));
+ retval = zend_hash_update(target_symbol_table, name, &EG(uninitialized_zval));
+ break;
+ case BP_VAR_W:
+ retval = zend_hash_add_new(target_symbol_table, name, &EG(uninitialized_zval));
+ break;
+ EMPTY_SWITCH_DEFAULT_CASE()
}
-
- zval_ptr_dtor_nogc(free_op1);
- } else {
- target_symbol_table = zend_get_target_symbol_table(execute_data, opline->extended_value & ZEND_FETCH_TYPE_MASK);
- retval = zend_hash_find(target_symbol_table, name);
- if (retval == NULL) {
+ /* GLOBAL or $$name variable may be an INDIRECT pointer to CV */
+ } else if (Z_TYPE_P(retval) == IS_INDIRECT) {
+ retval = Z_INDIRECT_P(retval);
+ if (Z_TYPE_P(retval) == IS_UNDEF) {
+ if (UNEXPECTED(zend_string_equals(name, CG(known_strings)[ZEND_STR_THIS]))) {
+ goto fetch_this;
+ }
switch (type) {
case BP_VAR_R:
case BP_VAR_UNSET:
@@ -43059,52 +53154,23 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_fetch_var_address_helper_SPEC_
break;
case BP_VAR_RW:
zend_error(E_NOTICE,"Undefined variable: %s", ZSTR_VAL(name));
- retval = zend_hash_update(target_symbol_table, name, &EG(uninitialized_zval));
- break;
+ /* break missing intentionally */
case BP_VAR_W:
- retval = zend_hash_add_new(target_symbol_table, name, &EG(uninitialized_zval));
+ ZVAL_NULL(retval);
break;
EMPTY_SWITCH_DEFAULT_CASE()
}
- /* GLOBAL or $$name variable may be an INDIRECT pointer to CV */
- } else if (Z_TYPE_P(retval) == IS_INDIRECT) {
- retval = Z_INDIRECT_P(retval);
- if (Z_TYPE_P(retval) == IS_UNDEF) {
- switch (type) {
- case BP_VAR_R:
- case BP_VAR_UNSET:
- zend_error(E_NOTICE,"Undefined variable: %s", ZSTR_VAL(name));
- /* break missing intentionally */
- case BP_VAR_IS:
- retval = &EG(uninitialized_zval);
- break;
- case BP_VAR_RW:
- zend_error(E_NOTICE,"Undefined variable: %s", ZSTR_VAL(name));
- /* break missing intentionally */
- case BP_VAR_W:
- ZVAL_NULL(retval);
- break;
- EMPTY_SWITCH_DEFAULT_CASE()
- }
- }
- }
- if ((opline->extended_value & ZEND_FETCH_TYPE_MASK) == ZEND_FETCH_STATIC) {
- if (Z_CONSTANT_P(retval)) {
- if (UNEXPECTED(zval_update_constant_ex(retval, 1, NULL) != SUCCESS)) {
- zval_ptr_dtor_nogc(free_op1);
- HANDLE_EXCEPTION();
- }
- }
- } else if ((opline->extended_value & ZEND_FETCH_TYPE_MASK) != ZEND_FETCH_GLOBAL_LOCK) {
- zval_ptr_dtor_nogc(free_op1);
}
}
+ if ((opline->extended_value & ZEND_FETCH_TYPE_MASK) != ZEND_FETCH_GLOBAL_LOCK) {
+ zval_ptr_dtor_nogc(free_op1);
+ }
+
if ((IS_TMP_VAR|IS_VAR) != IS_CONST) {
zend_string_release(name);
}
-fetch_var_return:
ZEND_ASSERT(retval != NULL);
if (type == BP_VAR_R || type == BP_VAR_IS) {
if (/*type == BP_VAR_R &&*/ Z_ISREF_P(retval) && Z_REFCOUNT_P(retval) == 1) {
@@ -43153,6 +53219,147 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_IS_SPEC_TMPVAR_UNUSED_HA
ZEND_VM_TAIL_CALL(zend_fetch_var_address_helper_SPEC_TMPVAR_UNUSED(BP_VAR_IS ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_fetch_static_prop_helper_SPEC_TMPVAR_UNUSED(int type ZEND_OPCODE_HANDLER_ARGS_DC)
+{
+ USE_OPLINE
+ zend_free_op free_op1;
+ zval *varname;
+ zval *retval;
+ zend_string *name;
+ zend_class_entry *ce;
+
+ SAVE_OPLINE();
+ varname = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1);
+
+ if ((IS_TMP_VAR|IS_VAR) == IS_CONST) {
+ name = Z_STR_P(varname);
+ } else if (EXPECTED(Z_TYPE_P(varname) == IS_STRING)) {
+ 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);
+ }
+
+ if (IS_UNUSED == IS_CONST) {
+ if ((IS_TMP_VAR|IS_VAR) == IS_CONST && EXPECTED((ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)))) != NULL)) {
+ retval = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)) + sizeof(void*));
+
+ /* check if static properties were destoyed */
+ if (UNEXPECTED(CE_STATIC_MEMBERS(ce) == NULL)) {
+ zend_throw_error(NULL, "Access to undeclared static property: %s::$%s", ZSTR_VAL(ce->name), ZSTR_VAL(name));
+ zval_ptr_dtor_nogc(free_op1);
+ HANDLE_EXCEPTION();
+ }
+
+ goto fetch_static_prop_return;
+ } else if (UNEXPECTED((ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)))) == NULL)) {
+ ce = zend_fetch_class_by_name(Z_STR_P(EX_CONSTANT(opline->op2)), EX_CONSTANT(opline->op2) + 1, ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION);
+ if (UNEXPECTED(ce == NULL)) {
+ if ((IS_TMP_VAR|IS_VAR) != IS_CONST) {
+ zend_string_release(name);
+ }
+ zval_ptr_dtor_nogc(free_op1);
+ ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
+ }
+ CACHE_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)), ce);
+ }
+ } else {
+ if (IS_UNUSED == IS_UNUSED) {
+ ce = zend_fetch_class(NULL, opline->op2.num);
+ if (UNEXPECTED(ce == NULL)) {
+ ZEND_ASSERT(EG(exception));
+ if ((IS_TMP_VAR|IS_VAR) != IS_CONST) {
+ zend_string_release(name);
+ }
+ zval_ptr_dtor_nogc(free_op1);
+ HANDLE_EXCEPTION();
+ }
+ } else {
+ ce = Z_CE_P(EX_VAR(opline->op2.var));
+ }
+ if ((IS_TMP_VAR|IS_VAR) == IS_CONST &&
+ (retval = CACHED_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)), ce)) != NULL) {
+
+ /* check if static properties were destoyed */
+ if (UNEXPECTED(CE_STATIC_MEMBERS(ce) == NULL)) {
+ zend_throw_error(NULL, "Access to undeclared static property: %s::$%s", ZSTR_VAL(ce->name), ZSTR_VAL(name));
+ zval_ptr_dtor_nogc(free_op1);
+ HANDLE_EXCEPTION();
+ }
+
+ goto fetch_static_prop_return;
+ }
+ }
+ retval = zend_std_get_static_property(ce, name, 0);
+ if (UNEXPECTED(EG(exception))) {
+ if ((IS_TMP_VAR|IS_VAR) != IS_CONST) {
+ zend_string_release(name);
+ }
+ zval_ptr_dtor_nogc(free_op1);
+ HANDLE_EXCEPTION();
+ }
+ if ((IS_TMP_VAR|IS_VAR) == IS_CONST && retval) {
+ CACHE_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)), ce, retval);
+ }
+
+ zval_ptr_dtor_nogc(free_op1);
+
+ if ((IS_TMP_VAR|IS_VAR) != IS_CONST) {
+ zend_string_release(name);
+ }
+
+fetch_static_prop_return:
+ ZEND_ASSERT(retval != NULL);
+ if (type == BP_VAR_R || type == BP_VAR_IS) {
+ if (/*type == BP_VAR_R &&*/ Z_ISREF_P(retval) && Z_REFCOUNT_P(retval) == 1) {
+ ZVAL_UNREF(retval);
+ }
+ ZVAL_COPY(EX_VAR(opline->result.var), retval);
+ } else {
+ ZVAL_INDIRECT(EX_VAR(opline->result.var), retval);
+ }
+ ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_STATIC_PROP_R_SPEC_TMPVAR_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ ZEND_VM_TAIL_CALL(zend_fetch_static_prop_helper_SPEC_TMPVAR_UNUSED(BP_VAR_R ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_STATIC_PROP_W_SPEC_TMPVAR_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ ZEND_VM_TAIL_CALL(zend_fetch_static_prop_helper_SPEC_TMPVAR_UNUSED(BP_VAR_W ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_STATIC_PROP_RW_SPEC_TMPVAR_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ ZEND_VM_TAIL_CALL(zend_fetch_static_prop_helper_SPEC_TMPVAR_UNUSED(BP_VAR_RW ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_STATIC_PROP_FUNC_ARG_SPEC_TMPVAR_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+
+ if (zend_is_by_ref_func_arg_fetch(opline, EX(call))) {
+ ZEND_VM_TAIL_CALL(zend_fetch_static_prop_helper_SPEC_TMPVAR_UNUSED(BP_VAR_W ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ } else {
+ ZEND_VM_TAIL_CALL(zend_fetch_static_prop_helper_SPEC_TMPVAR_UNUSED(BP_VAR_R ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ }
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_STATIC_PROP_UNSET_SPEC_TMPVAR_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ ZEND_VM_TAIL_CALL(zend_fetch_static_prop_helper_SPEC_TMPVAR_UNUSED(BP_VAR_UNSET ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_STATIC_PROP_IS_SPEC_TMPVAR_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ ZEND_VM_TAIL_CALL(zend_fetch_static_prop_helper_SPEC_TMPVAR_UNUSED(BP_VAR_IS ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+}
+
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_UNSET_VAR_SPEC_TMPVAR_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
@@ -43162,7 +53369,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_UNSET_VAR_SPEC_TMPVAR_UNUSED_H
SAVE_OPLINE();
if ((IS_TMP_VAR|IS_VAR) == IS_CV &&
- IS_UNUSED == IS_UNUSED &&
(opline->extended_value & ZEND_QUICK_SET)) {
zval *var = EX_VAR(opline->op1.var);
@@ -43171,7 +53377,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_UNSET_VAR_SPEC_TMPVAR_UNUSED_H
if (!--GC_REFCOUNT(garbage)) {
ZVAL_UNDEF(var);
- zval_dtor_func_for_ptr(garbage);
+ zval_dtor_func(garbage);
} else {
zval *z = var;
ZVAL_DEREF(z);
@@ -43199,33 +53405,66 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_UNSET_VAR_SPEC_TMPVAR_UNUSED_H
varname = &tmp;
}
- if (IS_UNUSED != IS_UNUSED) {
- zend_class_entry *ce;
+ target_symbol_table = zend_get_target_symbol_table(execute_data, opline->extended_value & ZEND_FETCH_TYPE_MASK);
+ zend_hash_del_ind(target_symbol_table, Z_STR_P(varname));
- if (IS_UNUSED == IS_CONST) {
- ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)));
+ if ((IS_TMP_VAR|IS_VAR) != IS_CONST && Z_TYPE(tmp) != IS_UNDEF) {
+ zend_string_release(Z_STR(tmp));
+ }
+ zval_ptr_dtor_nogc(free_op1);
+ ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_UNSET_STATIC_PROP_SPEC_TMPVAR_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zval tmp, *varname;
+ zend_class_entry *ce;
+ zend_free_op free_op1;
+
+ SAVE_OPLINE();
+
+ varname = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1);
+
+ 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;
+ }
+
+ if (IS_UNUSED == IS_CONST) {
+ ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)));
+ if (UNEXPECTED(ce == NULL)) {
+ ce = zend_fetch_class_by_name(Z_STR_P(EX_CONSTANT(opline->op2)), EX_CONSTANT(opline->op2) + 1, ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION);
if (UNEXPECTED(ce == NULL)) {
- ce = zend_fetch_class_by_name(Z_STR_P(EX_CONSTANT(opline->op2)), EX_CONSTANT(opline->op2) + 1, ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION);
- if (UNEXPECTED(ce == NULL)) {
- if (EXPECTED(!EG(exception))) {
- zend_throw_error(NULL, "Class '%s' not found", Z_STRVAL_P(EX_CONSTANT(opline->op2)));
- }
- if ((IS_TMP_VAR|IS_VAR) != IS_CONST && Z_TYPE(tmp) != IS_UNDEF) {
- zend_string_release(Z_STR(tmp));
- }
- zval_ptr_dtor_nogc(free_op1);
- HANDLE_EXCEPTION();
+ if (EXPECTED(!EG(exception))) {
+ zend_throw_error(NULL, "Class '%s' not found", Z_STRVAL_P(EX_CONSTANT(opline->op2)));
}
- CACHE_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)), ce);
+ if ((IS_TMP_VAR|IS_VAR) != IS_CONST && Z_TYPE(tmp) != IS_UNDEF) {
+ zend_string_release(Z_STR(tmp));
+ }
+ zval_ptr_dtor_nogc(free_op1);
+ HANDLE_EXCEPTION();
}
- } else {
- ce = Z_CE_P(EX_VAR(opline->op2.var));
+ CACHE_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)), ce);
+ }
+ } else if (IS_UNUSED == IS_UNUSED) {
+ ce = zend_fetch_class(NULL, opline->op2.num);
+ if (UNEXPECTED(ce == NULL)) {
+ ZEND_ASSERT(EG(exception));
+ if ((IS_TMP_VAR|IS_VAR) != IS_CONST && Z_TYPE(tmp) != IS_UNDEF) {
+ zend_string_release(Z_STR(tmp));
+ }
+ zval_ptr_dtor_nogc(free_op1);
+ HANDLE_EXCEPTION();
}
- zend_std_unset_static_property(ce, Z_STR_P(varname));
} else {
- target_symbol_table = zend_get_target_symbol_table(execute_data, opline->extended_value & ZEND_FETCH_TYPE_MASK);
- zend_hash_del_ind(target_symbol_table, Z_STR_P(varname));
+ ce = Z_CE_P(EX_VAR(opline->op2.var));
}
+ zend_std_unset_static_property(ce, Z_STR_P(varname));
if ((IS_TMP_VAR|IS_VAR) != IS_CONST && Z_TYPE(tmp) != IS_UNDEF) {
zend_string_release(Z_STR(tmp));
@@ -43241,7 +53480,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ISSET_ISEMPTY_VAR_SPEC_TMPVAR_
int result;
if ((IS_TMP_VAR|IS_VAR) == IS_CV &&
- IS_UNUSED == IS_UNUSED &&
(opline->extended_value & ZEND_QUICK_SET)) {
value = EX_VAR(opline->op1.var);
if (opline->extended_value & ZEND_ISSET) {
@@ -43262,6 +53500,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ISSET_ISEMPTY_VAR_SPEC_TMPVAR_
} else {
zend_free_op free_op1;
zval tmp, *varname;
+ HashTable *target_symbol_table;
SAVE_OPLINE();
varname = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1);
@@ -43271,56 +53510,14 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ISSET_ISEMPTY_VAR_SPEC_TMPVAR_
varname = &tmp;
}
- if (IS_UNUSED != IS_UNUSED) {
- zend_class_entry *ce;
-
- if (IS_UNUSED == IS_CONST) {
- if ((IS_TMP_VAR|IS_VAR) == IS_CONST && EXPECTED((ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)))) != NULL)) {
- value = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)) + sizeof(void*));
-
- /* check if static properties were destoyed */
- if (UNEXPECTED(CE_STATIC_MEMBERS(ce) == NULL)) {
- value = NULL;
- }
-
- goto is_var_return;
- } else if (UNEXPECTED((ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)))) == NULL)) {
- ce = zend_fetch_class_by_name(Z_STR_P(EX_CONSTANT(opline->op2)), EX_CONSTANT(opline->op2) + 1, ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION);
- if (UNEXPECTED(ce == NULL)) {
- ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
- }
- CACHE_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)), ce);
- }
- } else {
- ce = Z_CE_P(EX_VAR(opline->op2.var));
- if ((IS_TMP_VAR|IS_VAR) == IS_CONST &&
- (value = CACHED_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)), ce)) != NULL) {
-
- /* check if static properties were destoyed */
- if (UNEXPECTED(CE_STATIC_MEMBERS(ce) == NULL)) {
- value = NULL;
- }
-
- goto is_var_return;
- }
- }
-
- value = zend_std_get_static_property(ce, Z_STR_P(varname), 1);
-
- if ((IS_TMP_VAR|IS_VAR) == IS_CONST && value) {
- CACHE_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)), ce, value);
- }
- } else {
- HashTable *target_symbol_table = zend_get_target_symbol_table(execute_data, opline->extended_value & ZEND_FETCH_TYPE_MASK);
- value = zend_hash_find_ind(target_symbol_table, Z_STR_P(varname));
- }
+ target_symbol_table = zend_get_target_symbol_table(execute_data, opline->extended_value & ZEND_FETCH_TYPE_MASK);
+ value = zend_hash_find_ind(target_symbol_table, Z_STR_P(varname));
if ((IS_TMP_VAR|IS_VAR) != IS_CONST && Z_TYPE(tmp) != IS_UNDEF) {
zend_string_release(Z_STR(tmp));
}
zval_ptr_dtor_nogc(free_op1);
-is_var_return:
if (opline->extended_value & ZEND_ISSET) {
result = value && Z_TYPE_P(value) > IS_NULL &&
(!Z_ISREF_P(value) || Z_TYPE_P(Z_REFVAL_P(value)) != IS_NULL);
@@ -43334,6 +53531,141 @@ is_var_return:
}
}
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ISSET_ISEMPTY_STATIC_PROP_SPEC_TMPVAR_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zval *value;
+ int result;
+ zend_free_op free_op1;
+ zval tmp, *varname;
+ zend_class_entry *ce;
+
+ SAVE_OPLINE();
+ varname = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1);
+ ZVAL_UNDEF(&tmp);
+ if ((IS_TMP_VAR|IS_VAR) != IS_CONST && Z_TYPE_P(varname) != IS_STRING) {
+ ZVAL_STR(&tmp, zval_get_string(varname));
+ varname = &tmp;
+ }
+
+ if (IS_UNUSED == IS_CONST) {
+ if ((IS_TMP_VAR|IS_VAR) == IS_CONST && EXPECTED((ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)))) != NULL)) {
+ value = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)) + sizeof(void*));
+
+ /* check if static properties were destoyed */
+ if (UNEXPECTED(CE_STATIC_MEMBERS(ce) == NULL)) {
+ value = NULL;
+ }
+
+ goto is_static_prop_return;
+ } else if (UNEXPECTED((ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)))) == NULL)) {
+ ce = zend_fetch_class_by_name(Z_STR_P(EX_CONSTANT(opline->op2)), EX_CONSTANT(opline->op2) + 1, ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION);
+ if (UNEXPECTED(ce == NULL)) {
+ ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
+ }
+ CACHE_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)), ce);
+ }
+ } else {
+ if (IS_UNUSED == IS_UNUSED) {
+ ce = zend_fetch_class(NULL, opline->op2.num);
+ if (UNEXPECTED(ce == NULL)) {
+ ZEND_ASSERT(EG(exception));
+ if ((IS_TMP_VAR|IS_VAR) != IS_CONST && Z_TYPE(tmp) != IS_UNDEF) {
+ zend_string_release(Z_STR(tmp));
+ }
+ zval_ptr_dtor_nogc(free_op1);
+ HANDLE_EXCEPTION();
+ }
+ } else {
+ ce = Z_CE_P(EX_VAR(opline->op2.var));
+ }
+ if ((IS_TMP_VAR|IS_VAR) == IS_CONST &&
+ (value = CACHED_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)), ce)) != NULL) {
+
+ /* check if static properties were destoyed */
+ if (UNEXPECTED(CE_STATIC_MEMBERS(ce) == NULL)) {
+ value = NULL;
+ }
+
+ goto is_static_prop_return;
+ }
+ }
+
+ value = zend_std_get_static_property(ce, Z_STR_P(varname), 1);
+
+ if ((IS_TMP_VAR|IS_VAR) == IS_CONST && value) {
+ CACHE_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)), ce, value);
+ }
+
+ if ((IS_TMP_VAR|IS_VAR) != IS_CONST && Z_TYPE(tmp) != IS_UNDEF) {
+ zend_string_release(Z_STR(tmp));
+ }
+ zval_ptr_dtor_nogc(free_op1);
+
+is_static_prop_return:
+ if (opline->extended_value & ZEND_ISSET) {
+ result = value && Z_TYPE_P(value) > IS_NULL &&
+ (!Z_ISREF_P(value) || Z_TYPE_P(Z_REFVAL_P(value)) != IS_NULL);
+ } else /* if (opline->extended_value & ZEND_ISEMPTY) */ {
+ result = !value || !i_zend_is_true(value);
+ }
+
+ ZEND_VM_SMART_BRANCH(result, 1);
+ ZVAL_BOOL(EX_VAR(opline->result.var), result);
+ ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INSTANCEOF_SPEC_TMPVAR_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zend_free_op free_op1;
+ zval *expr;
+ zend_bool result;
+
+ SAVE_OPLINE();
+ expr = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1);
+
+try_instanceof:
+ if (Z_TYPE_P(expr) == IS_OBJECT) {
+ zend_class_entry *ce;
+
+ if (IS_UNUSED == IS_CONST) {
+ ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)));
+ if (UNEXPECTED(ce == NULL)) {
+ ce = zend_fetch_class_by_name(Z_STR_P(EX_CONSTANT(opline->op2)), EX_CONSTANT(opline->op2) + 1, ZEND_FETCH_CLASS_NO_AUTOLOAD);
+ if (UNEXPECTED(ce == NULL)) {
+ ZVAL_FALSE(EX_VAR(opline->result.var));
+ zval_ptr_dtor_nogc(free_op1);
+ ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
+ }
+ CACHE_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)), ce);
+ }
+ } else if (IS_UNUSED == IS_UNUSED) {
+ ce = zend_fetch_class(NULL, opline->op2.num);
+ if (UNEXPECTED(ce == NULL)) {
+ ZEND_ASSERT(EG(exception));
+ zval_ptr_dtor_nogc(free_op1);
+ HANDLE_EXCEPTION();
+ }
+ } else {
+ ce = Z_CE_P(EX_VAR(opline->op2.var));
+ }
+ result = ce && instanceof_function(Z_OBJCE_P(expr), ce);
+ } else if (((IS_TMP_VAR|IS_VAR) & (IS_VAR|IS_CV)) && Z_TYPE_P(expr) == IS_REFERENCE) {
+ 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);
+ ZEND_VM_SMART_BRANCH(result, 1);
+ ZVAL_BOOL(EX_VAR(opline->result.var), result);
+ ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
+}
+
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ADD_SPEC_TMPVAR_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
@@ -43599,7 +53931,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CONCAT_SPEC_TMPVAR_CV_HANDLER(
!ZSTR_IS_INTERNED(op1_str) && GC_REFCOUNT(op1_str) == 1) {
size_t len = ZSTR_LEN(op1_str);
- str = zend_string_realloc(op1_str, len + ZSTR_LEN(op2_str), 0);
+ str = zend_string_extend(op1_str, len + ZSTR_LEN(op2_str), 0);
memcpy(ZSTR_VAL(str) + len, ZSTR_VAL(op2_str), ZSTR_LEN(op2_str)+1);
ZVAL_NEW_STR(EX_VAR(opline->result.var), str);
break;
@@ -43939,11 +54271,33 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_DIM_R_SPEC_TMPVAR_CV_HAN
{
USE_OPLINE
zend_free_op free_op1;
- zval *container;
+ zval *container, *dim, *value, *result;
SAVE_OPLINE();
container = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1);
- zend_fetch_dimension_address_read_R(EX_VAR(opline->result.var), container, _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var), IS_CV);
+ dim = _get_zval_ptr_cv_undef(execute_data, opline->op2.var);
+ if ((IS_TMP_VAR|IS_VAR) != IS_CONST) {
+ if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) {
+fetch_dim_r_array:
+ value = zend_fetch_dimension_address_inner(Z_ARRVAL_P(container), dim, IS_CV, BP_VAR_R);
+ result = EX_VAR(opline->result.var);
+ ZVAL_COPY(result, value);
+ } else if (EXPECTED(Z_TYPE_P(container) == IS_REFERENCE)) {
+ container = Z_REFVAL_P(container);
+ if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) {
+ goto fetch_dim_r_array;
+ } else {
+ goto fetch_dim_r_slow;
+ }
+ } else {
+fetch_dim_r_slow:
+ result = EX_VAR(opline->result.var);
+ zend_fetch_dimension_address_read_R_slow(result, container, dim);
+ }
+ } else {
+ result = EX_VAR(opline->result.var);
+ zend_fetch_dimension_address_read_R(result, container, dim, IS_CV);
+ }
zval_ptr_dtor_nogc(free_op1);
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
@@ -43957,7 +54311,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_DIM_IS_SPEC_TMPVAR_CV_HA
SAVE_OPLINE();
container = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1);
- zend_fetch_dimension_address_read_IS(EX_VAR(opline->result.var), container, _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var), IS_CV);
+ zend_fetch_dimension_address_read_IS(EX_VAR(opline->result.var), container, _get_zval_ptr_cv_undef(execute_data, opline->op2.var), IS_CV);
zval_ptr_dtor_nogc(free_op1);
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
@@ -43974,7 +54328,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_IS_SPEC_TMPVAR_CV_HA
SAVE_OPLINE();
container = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1);
- if ((IS_TMP_VAR|IS_VAR) == IS_UNUSED && UNEXPECTED(Z_OBJ_P(container) == NULL)) {
+ if ((IS_TMP_VAR|IS_VAR) == IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
HANDLE_EXCEPTION();
@@ -44035,6 +54389,19 @@ fetch_obj_is_no_object:
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_LIST_SPEC_TMPVAR_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zend_free_op free_op1;
+ zval *container;
+
+ SAVE_OPLINE();
+ container = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1);
+ zend_fetch_dimension_address_read_LIST(EX_VAR(opline->result.var), container, _get_zval_ptr_cv_undef(execute_data, opline->op2.var));
+
+ ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
+}
+
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FAST_CONCAT_SPEC_TMPVAR_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
@@ -44141,7 +54508,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_SPEC_TMPVAR_C
object = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1);
- if ((IS_TMP_VAR|IS_VAR) == IS_UNUSED && UNEXPECTED(Z_OBJ_P(object) == NULL)) {
+ if ((IS_TMP_VAR|IS_VAR) == IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
HANDLE_EXCEPTION();
@@ -44201,6 +54568,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_SPEC_TMPVAR_C
EXPECTED(obj == orig_obj)) {
CACHE_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(function_name), called_scope, fbc);
}
+ if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!fbc->op_array.run_time_cache)) {
+ init_func_run_time_cache(&fbc->op_array);
+ }
}
call_info = ZEND_CALL_NESTED_FUNCTION;
@@ -44307,16 +54677,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ISSET_ISEMPTY_DIM_OBJ_SPEC_TMP
SAVE_OPLINE();
container = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1);
-
- if ((IS_TMP_VAR|IS_VAR) == IS_UNUSED && UNEXPECTED(Z_OBJ_P(container) == NULL)) {
- zend_throw_error(NULL, "Using $this when not in object context");
-
- HANDLE_EXCEPTION();
- }
-
offset = _get_zval_ptr_cv_undef(execute_data, opline->op2.var);
- if ((IS_TMP_VAR|IS_VAR) != IS_UNUSED && EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) {
+ if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) {
HashTable *ht;
zval *value;
zend_string *str;
@@ -44383,8 +54746,7 @@ num_index_prop:
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 (((IS_TMP_VAR|IS_VAR) != IS_CONST && EXPECTED(Z_TYPE_P(container) == IS_OBJECT))) {
if (EXPECTED(Z_OBJ_HT_P(container)->has_dimension)) {
result =
((opline->extended_value & ZEND_ISSET) == 0) ^
@@ -44399,6 +54761,9 @@ num_index_prop:
if (EXPECTED(Z_TYPE_P(offset) == IS_LONG)) {
lval = Z_LVAL_P(offset);
isset_str_offset:
+ if (UNEXPECTED(lval < 0)) { /* Handle negative offset */
+ lval += (zend_long)Z_STRLEN_P(container);
+ }
if (EXPECTED(lval >= 0) && (size_t)lval < Z_STRLEN_P(container)) {
if (opline->extended_value & ZEND_ISSET) {
result = 1;
@@ -44444,7 +54809,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ISSET_ISEMPTY_PROP_OBJ_SPEC_TM
SAVE_OPLINE();
container = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1);
- if ((IS_TMP_VAR|IS_VAR) == IS_UNUSED && UNEXPECTED(Z_OBJ_P(container) == NULL)) {
+ if ((IS_TMP_VAR|IS_VAR) == IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
HANDLE_EXCEPTION();
@@ -44479,6 +54844,54 @@ isset_no_object:
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zend_free_op free_op1;
+ zval *container, *dim, *value;
+ zend_long offset;
+
+ container = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1);
+ dim = _get_zval_ptr_cv_undef(execute_data, opline->op2.var);
+ if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) {
+fetch_dim_r_index_array:
+ if (EXPECTED(Z_TYPE_P(dim) == IS_LONG)) {
+ offset = Z_LVAL_P(dim);
+ } else {
+ offset = zval_get_long(dim);
+ }
+ ZEND_HASH_INDEX_FIND(Z_ARRVAL_P(container), offset, value, fetch_dim_r_index_undef);
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ if ((IS_TMP_VAR|IS_VAR) & (IS_TMP_VAR|IS_VAR)) {
+ SAVE_OPLINE();
+ zval_ptr_dtor_nogc(free_op1);
+ ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
+ } else {
+ ZEND_VM_NEXT_OPCODE();
+ }
+ } else if ((IS_TMP_VAR|IS_VAR) != IS_CONST && EXPECTED(Z_TYPE_P(container) == IS_REFERENCE)) {
+ container = Z_REFVAL_P(container);
+ if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) {
+ goto fetch_dim_r_index_array;
+ } else {
+ goto fetch_dim_r_index_slow;
+ }
+ } else {
+fetch_dim_r_index_slow:
+ SAVE_OPLINE();
+ zend_fetch_dimension_address_read_R_slow(EX_VAR(opline->result.var), container, dim);
+ zval_ptr_dtor_nogc(free_op1);
+ ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
+ }
+
+fetch_dim_r_index_undef:
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ SAVE_OPLINE();
+ zend_error(E_NOTICE, "Undefined offset: " ZEND_LONG_FMT, offset);
+ zval_ptr_dtor_nogc(free_op1);
+ ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
+}
+
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ADD_SPEC_TMPVAR_TMPVAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
@@ -44744,7 +55157,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CONCAT_SPEC_TMPVAR_TMPVAR_HAND
!ZSTR_IS_INTERNED(op1_str) && GC_REFCOUNT(op1_str) == 1) {
size_t len = ZSTR_LEN(op1_str);
- str = zend_string_realloc(op1_str, len + ZSTR_LEN(op2_str), 0);
+ str = zend_string_extend(op1_str, len + ZSTR_LEN(op2_str), 0);
memcpy(ZSTR_VAL(str) + len, ZSTR_VAL(op2_str), ZSTR_LEN(op2_str)+1);
ZVAL_NEW_STR(EX_VAR(opline->result.var), str);
break;
@@ -45084,11 +55497,33 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_DIM_R_SPEC_TMPVAR_TMPVAR
{
USE_OPLINE
zend_free_op free_op1, free_op2;
- zval *container;
+ zval *container, *dim, *value, *result;
SAVE_OPLINE();
container = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1);
- zend_fetch_dimension_address_read_R(EX_VAR(opline->result.var), container, _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2), (IS_TMP_VAR|IS_VAR));
+ dim = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2);
+ if ((IS_TMP_VAR|IS_VAR) != IS_CONST) {
+ if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) {
+fetch_dim_r_array:
+ value = zend_fetch_dimension_address_inner(Z_ARRVAL_P(container), dim, (IS_TMP_VAR|IS_VAR), BP_VAR_R);
+ result = EX_VAR(opline->result.var);
+ ZVAL_COPY(result, value);
+ } else if (EXPECTED(Z_TYPE_P(container) == IS_REFERENCE)) {
+ container = Z_REFVAL_P(container);
+ if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) {
+ goto fetch_dim_r_array;
+ } else {
+ goto fetch_dim_r_slow;
+ }
+ } else {
+fetch_dim_r_slow:
+ result = EX_VAR(opline->result.var);
+ zend_fetch_dimension_address_read_R_slow(result, container, dim);
+ }
+ } else {
+ result = EX_VAR(opline->result.var);
+ zend_fetch_dimension_address_read_R(result, container, dim, (IS_TMP_VAR|IS_VAR));
+ }
zval_ptr_dtor_nogc(free_op2);
zval_ptr_dtor_nogc(free_op1);
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
@@ -45119,7 +55554,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_IS_SPEC_TMPVAR_TMPVA
SAVE_OPLINE();
container = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1);
- if ((IS_TMP_VAR|IS_VAR) == IS_UNUSED && UNEXPECTED(Z_OBJ_P(container) == NULL)) {
+ if ((IS_TMP_VAR|IS_VAR) == IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
zval_ptr_dtor_nogc(EX_VAR(opline->op2.var));
HANDLE_EXCEPTION();
@@ -45181,6 +55616,19 @@ fetch_obj_is_no_object:
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_LIST_SPEC_TMPVAR_TMPVAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zend_free_op free_op1, free_op2;
+ zval *container;
+
+ SAVE_OPLINE();
+ container = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1);
+ zend_fetch_dimension_address_read_LIST(EX_VAR(opline->result.var), container, _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2));
+ zval_ptr_dtor_nogc(free_op2);
+ ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
+}
+
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FAST_CONCAT_SPEC_TMPVAR_TMPVAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
@@ -45287,7 +55735,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_SPEC_TMPVAR_T
object = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1);
- if ((IS_TMP_VAR|IS_VAR) == IS_UNUSED && UNEXPECTED(Z_OBJ_P(object) == NULL)) {
+ if ((IS_TMP_VAR|IS_VAR) == IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
zval_ptr_dtor_nogc(free_op2);
HANDLE_EXCEPTION();
@@ -45347,6 +55795,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_SPEC_TMPVAR_T
EXPECTED(obj == orig_obj)) {
CACHE_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(function_name), called_scope, fbc);
}
+ if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!fbc->op_array.run_time_cache)) {
+ init_func_run_time_cache(&fbc->op_array);
+ }
}
call_info = ZEND_CALL_NESTED_FUNCTION;
@@ -45454,16 +55905,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ISSET_ISEMPTY_DIM_OBJ_SPEC_TMP
SAVE_OPLINE();
container = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1);
-
- if ((IS_TMP_VAR|IS_VAR) == IS_UNUSED && UNEXPECTED(Z_OBJ_P(container) == NULL)) {
- zend_throw_error(NULL, "Using $this when not in object context");
- zval_ptr_dtor_nogc(EX_VAR(opline->op2.var));
- HANDLE_EXCEPTION();
- }
-
offset = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2);
- if ((IS_TMP_VAR|IS_VAR) != IS_UNUSED && EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) {
+ if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) {
HashTable *ht;
zval *value;
zend_string *str;
@@ -45530,8 +55974,7 @@ num_index_prop:
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 (((IS_TMP_VAR|IS_VAR) != IS_CONST && EXPECTED(Z_TYPE_P(container) == IS_OBJECT))) {
if (EXPECTED(Z_OBJ_HT_P(container)->has_dimension)) {
result =
((opline->extended_value & ZEND_ISSET) == 0) ^
@@ -45546,6 +55989,9 @@ num_index_prop:
if (EXPECTED(Z_TYPE_P(offset) == IS_LONG)) {
lval = Z_LVAL_P(offset);
isset_str_offset:
+ if (UNEXPECTED(lval < 0)) { /* Handle negative offset */
+ lval += (zend_long)Z_STRLEN_P(container);
+ }
if (EXPECTED(lval >= 0) && (size_t)lval < Z_STRLEN_P(container)) {
if (opline->extended_value & ZEND_ISSET) {
result = 1;
@@ -45591,7 +56037,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ISSET_ISEMPTY_PROP_OBJ_SPEC_TM
SAVE_OPLINE();
container = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1);
- if ((IS_TMP_VAR|IS_VAR) == IS_UNUSED && UNEXPECTED(Z_OBJ_P(container) == NULL)) {
+ if ((IS_TMP_VAR|IS_VAR) == IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
zval_ptr_dtor_nogc(EX_VAR(opline->op2.var));
HANDLE_EXCEPTION();
@@ -45627,6 +56073,972 @@ isset_no_object:
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_TMPVAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zend_free_op free_op1, free_op2;
+ zval *container, *dim, *value;
+ zend_long offset;
+
+ container = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1);
+ dim = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2);
+ if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) {
+fetch_dim_r_index_array:
+ if (EXPECTED(Z_TYPE_P(dim) == IS_LONG)) {
+ offset = Z_LVAL_P(dim);
+ } else {
+ offset = zval_get_long(dim);
+ }
+ ZEND_HASH_INDEX_FIND(Z_ARRVAL_P(container), offset, value, fetch_dim_r_index_undef);
+ ZVAL_COPY(EX_VAR(opline->result.var), value);
+ if ((IS_TMP_VAR|IS_VAR) & (IS_TMP_VAR|IS_VAR)) {
+ SAVE_OPLINE();
+ zval_ptr_dtor_nogc(free_op1);
+ ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
+ } else {
+ ZEND_VM_NEXT_OPCODE();
+ }
+ } else if ((IS_TMP_VAR|IS_VAR) != IS_CONST && EXPECTED(Z_TYPE_P(container) == IS_REFERENCE)) {
+ container = Z_REFVAL_P(container);
+ if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) {
+ goto fetch_dim_r_index_array;
+ } else {
+ goto fetch_dim_r_index_slow;
+ }
+ } else {
+fetch_dim_r_index_slow:
+ SAVE_OPLINE();
+ zend_fetch_dimension_address_read_R_slow(EX_VAR(opline->result.var), container, dim);
+ zval_ptr_dtor_nogc(free_op1);
+ ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
+ }
+
+fetch_dim_r_index_undef:
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ SAVE_OPLINE();
+ zend_error(E_NOTICE, "Undefined offset: " ZEND_LONG_FMT, offset);
+ zval_ptr_dtor_nogc(free_op1);
+ ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_PRE_INC_LONG_NO_OVERFLOW_SPEC_TMPVARCV_RETVAL_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zval *var_ptr;
+
+ var_ptr = EX_VAR(opline->op1.var);
+ Z_LVAL_P(var_ptr)++;
+ if (UNEXPECTED(0)) {
+ ZVAL_LONG(EX_VAR(opline->result.var), Z_LVAL_P(var_ptr));
+ }
+ ZEND_VM_NEXT_OPCODE();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_PRE_INC_LONG_NO_OVERFLOW_SPEC_TMPVARCV_RETVAL_USED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zval *var_ptr;
+
+ var_ptr = EX_VAR(opline->op1.var);
+ Z_LVAL_P(var_ptr)++;
+ if (UNEXPECTED(1)) {
+ ZVAL_LONG(EX_VAR(opline->result.var), Z_LVAL_P(var_ptr));
+ }
+ ZEND_VM_NEXT_OPCODE();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_PRE_INC_LONG_SPEC_TMPVARCV_RETVAL_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zval *var_ptr;
+
+ var_ptr = EX_VAR(opline->op1.var);
+ fast_long_increment_function(var_ptr);
+ if (UNEXPECTED(0)) {
+ ZVAL_COPY_VALUE(EX_VAR(opline->result.var), var_ptr);
+ }
+ ZEND_VM_NEXT_OPCODE();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_PRE_INC_LONG_SPEC_TMPVARCV_RETVAL_USED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zval *var_ptr;
+
+ var_ptr = EX_VAR(opline->op1.var);
+ fast_long_increment_function(var_ptr);
+ if (UNEXPECTED(1)) {
+ ZVAL_COPY_VALUE(EX_VAR(opline->result.var), var_ptr);
+ }
+ ZEND_VM_NEXT_OPCODE();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_PRE_INC_LONG_OR_DOUBLE_SPEC_TMPVARCV_RETVAL_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zval *var_ptr;
+
+ var_ptr = EX_VAR(opline->op1.var);
+ if (EXPECTED(Z_TYPE_P(var_ptr) == IS_LONG)) {
+ fast_long_increment_function(var_ptr);
+ } else {
+ Z_DVAL_P(var_ptr)++;
+ }
+ if (UNEXPECTED(0)) {
+ ZVAL_COPY_VALUE(EX_VAR(opline->result.var), var_ptr);
+ }
+ ZEND_VM_NEXT_OPCODE();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_PRE_INC_LONG_OR_DOUBLE_SPEC_TMPVARCV_RETVAL_USED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zval *var_ptr;
+
+ var_ptr = EX_VAR(opline->op1.var);
+ if (EXPECTED(Z_TYPE_P(var_ptr) == IS_LONG)) {
+ fast_long_increment_function(var_ptr);
+ } else {
+ Z_DVAL_P(var_ptr)++;
+ }
+ if (UNEXPECTED(1)) {
+ ZVAL_COPY_VALUE(EX_VAR(opline->result.var), var_ptr);
+ }
+ ZEND_VM_NEXT_OPCODE();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_PRE_DEC_LONG_NO_OVERFLOW_SPEC_TMPVARCV_RETVAL_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zval *var_ptr;
+
+ var_ptr = EX_VAR(opline->op1.var);
+ Z_LVAL_P(var_ptr)--;
+ if (UNEXPECTED(0)) {
+ ZVAL_LONG(EX_VAR(opline->result.var), Z_LVAL_P(var_ptr));
+ }
+ ZEND_VM_NEXT_OPCODE();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_PRE_DEC_LONG_NO_OVERFLOW_SPEC_TMPVARCV_RETVAL_USED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zval *var_ptr;
+
+ var_ptr = EX_VAR(opline->op1.var);
+ Z_LVAL_P(var_ptr)--;
+ if (UNEXPECTED(1)) {
+ ZVAL_LONG(EX_VAR(opline->result.var), Z_LVAL_P(var_ptr));
+ }
+ ZEND_VM_NEXT_OPCODE();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_PRE_DEC_LONG_SPEC_TMPVARCV_RETVAL_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zval *var_ptr;
+
+ var_ptr = EX_VAR(opline->op1.var);
+ fast_long_decrement_function(var_ptr);
+ if (UNEXPECTED(0)) {
+ ZVAL_COPY_VALUE(EX_VAR(opline->result.var), var_ptr);
+ }
+ ZEND_VM_NEXT_OPCODE();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_PRE_DEC_LONG_SPEC_TMPVARCV_RETVAL_USED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zval *var_ptr;
+
+ var_ptr = EX_VAR(opline->op1.var);
+ fast_long_decrement_function(var_ptr);
+ if (UNEXPECTED(1)) {
+ ZVAL_COPY_VALUE(EX_VAR(opline->result.var), var_ptr);
+ }
+ ZEND_VM_NEXT_OPCODE();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_PRE_DEC_LONG_OR_DOUBLE_SPEC_TMPVARCV_RETVAL_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zval *var_ptr;
+
+ var_ptr = EX_VAR(opline->op1.var);
+ if (EXPECTED(Z_TYPE_P(var_ptr) == IS_LONG)) {
+ fast_long_decrement_function(var_ptr);
+ } else {
+ Z_DVAL_P(var_ptr)--;
+ }
+ if (UNEXPECTED(0)) {
+ ZVAL_COPY_VALUE(EX_VAR(opline->result.var), var_ptr);
+ }
+ ZEND_VM_NEXT_OPCODE();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_PRE_DEC_LONG_OR_DOUBLE_SPEC_TMPVARCV_RETVAL_USED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zval *var_ptr;
+
+ var_ptr = EX_VAR(opline->op1.var);
+ if (EXPECTED(Z_TYPE_P(var_ptr) == IS_LONG)) {
+ fast_long_decrement_function(var_ptr);
+ } else {
+ Z_DVAL_P(var_ptr)--;
+ }
+ if (UNEXPECTED(1)) {
+ ZVAL_COPY_VALUE(EX_VAR(opline->result.var), var_ptr);
+ }
+ ZEND_VM_NEXT_OPCODE();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_POST_INC_LONG_NO_OVERFLOW_SPEC_TMPVARCV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zval *var_ptr;
+
+ var_ptr = EX_VAR(opline->op1.var);
+ ZVAL_LONG(EX_VAR(opline->result.var), Z_LVAL_P(var_ptr));
+ Z_LVAL_P(var_ptr)++;
+ ZEND_VM_NEXT_OPCODE();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_POST_INC_LONG_SPEC_TMPVARCV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zval *var_ptr;
+
+ var_ptr = EX_VAR(opline->op1.var);
+ ZVAL_LONG(EX_VAR(opline->result.var), Z_LVAL_P(var_ptr));
+ fast_long_increment_function(var_ptr);
+ ZEND_VM_NEXT_OPCODE();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_POST_INC_LONG_OR_DOUBLE_SPEC_TMPVARCV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zval *var_ptr;
+
+ var_ptr = EX_VAR(opline->op1.var);
+ ZVAL_COPY_VALUE(EX_VAR(opline->result.var), var_ptr);
+ if (EXPECTED(Z_TYPE_P(var_ptr) == IS_LONG)) {
+ fast_long_increment_function(var_ptr);
+ } else {
+ Z_DVAL_P(var_ptr)++;
+ }
+ ZEND_VM_NEXT_OPCODE();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_POST_DEC_LONG_NO_OVERFLOW_SPEC_TMPVARCV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zval *var_ptr;
+
+ var_ptr = EX_VAR(opline->op1.var);
+ ZVAL_LONG(EX_VAR(opline->result.var), Z_LVAL_P(var_ptr));
+ Z_LVAL_P(var_ptr)--;
+ ZEND_VM_NEXT_OPCODE();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_POST_DEC_LONG_SPEC_TMPVARCV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zval *var_ptr;
+
+ var_ptr = EX_VAR(opline->op1.var);
+ ZVAL_LONG(EX_VAR(opline->result.var), Z_LVAL_P(var_ptr));
+ fast_long_decrement_function(var_ptr);
+ ZEND_VM_NEXT_OPCODE();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_POST_DEC_LONG_OR_DOUBLE_SPEC_TMPVARCV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zval *var_ptr;
+
+ var_ptr = EX_VAR(opline->op1.var);
+ ZVAL_COPY_VALUE(EX_VAR(opline->result.var), var_ptr);
+ if (EXPECTED(Z_TYPE_P(var_ptr) == IS_LONG)) {
+ fast_long_decrement_function(var_ptr);
+ } else {
+ Z_DVAL_P(var_ptr)--;
+ }
+ ZEND_VM_NEXT_OPCODE();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_QM_ASSIGN_DOUBLE_SPEC_TMPVARCV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+
+ zval *value;
+
+ value = EX_VAR(opline->op1.var);
+ ZVAL_DOUBLE(EX_VAR(opline->result.var), Z_DVAL_P(value));
+ ZEND_VM_NEXT_OPCODE();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_QM_ASSIGN_NOREF_SPEC_TMPVARCV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+
+ zval *value;
+
+ value = EX_VAR(opline->op1.var);
+ ZVAL_COPY_VALUE(EX_VAR(opline->result.var), value);
+ ZEND_VM_NEXT_OPCODE();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zval *op1, *op2, *result;
+
+ op1 = EX_VAR(opline->op1.var);
+ op2 = EX_CONSTANT(opline->op2);
+ result = EX_VAR(opline->result.var);
+ ZVAL_LONG(result, Z_LVAL_P(op1) - Z_LVAL_P(op2));
+ ZEND_VM_NEXT_OPCODE();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SUB_LONG_SPEC_TMPVARCV_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zval *op1, *op2, *result;
+
+ op1 = EX_VAR(opline->op1.var);
+ op2 = EX_CONSTANT(opline->op2);
+ result = EX_VAR(opline->result.var);
+ fast_long_sub_function(result, op1, op2);
+ ZEND_VM_NEXT_OPCODE();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SUB_DOUBLE_SPEC_TMPVARCV_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zval *op1, *op2, *result;
+
+ op1 = EX_VAR(opline->op1.var);
+ op2 = EX_CONSTANT(opline->op2);
+ result = EX_VAR(opline->result.var);
+ ZVAL_DOUBLE(result, Z_DVAL_P(op1) - Z_DVAL_P(op2));
+ ZEND_VM_NEXT_OPCODE();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zval *op1, *op2;
+ int result;
+
+ op1 = EX_VAR(opline->op1.var);
+ op2 = EX_CONSTANT(opline->op2);
+ result = (Z_LVAL_P(op1) < Z_LVAL_P(op2));
+
+ ZVAL_BOOL(EX_VAR(opline->result.var), result);
+ ZEND_VM_NEXT_OPCODE();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST_JMPZ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zval *op1, *op2;
+ int result;
+
+ op1 = EX_VAR(opline->op1.var);
+ op2 = EX_CONSTANT(opline->op2);
+ result = (Z_LVAL_P(op1) < Z_LVAL_P(op2));
+ ZEND_VM_SMART_BRANCH_JMPZ(result, 0);
+ ZVAL_BOOL(EX_VAR(opline->result.var), result);
+ ZEND_VM_NEXT_OPCODE();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST_JMPNZ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zval *op1, *op2;
+ int result;
+
+ op1 = EX_VAR(opline->op1.var);
+ op2 = EX_CONSTANT(opline->op2);
+ result = (Z_LVAL_P(op1) < Z_LVAL_P(op2));
+ ZEND_VM_SMART_BRANCH_JMPNZ(result, 0);
+ ZVAL_BOOL(EX_VAR(opline->result.var), result);
+ ZEND_VM_NEXT_OPCODE();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zval *op1, *op2;
+ int result;
+
+ op1 = EX_VAR(opline->op1.var);
+ op2 = EX_CONSTANT(opline->op2);
+ result = (Z_DVAL_P(op1) < Z_DVAL_P(op2));
+
+ ZVAL_BOOL(EX_VAR(opline->result.var), result);
+ ZEND_VM_NEXT_OPCODE();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zval *op1, *op2;
+ int result;
+
+ op1 = EX_VAR(opline->op1.var);
+ op2 = EX_CONSTANT(opline->op2);
+ result = (Z_DVAL_P(op1) < Z_DVAL_P(op2));
+ ZEND_VM_SMART_BRANCH_JMPZ(result, 0);
+ ZVAL_BOOL(EX_VAR(opline->result.var), result);
+ ZEND_VM_NEXT_OPCODE();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zval *op1, *op2;
+ int result;
+
+ op1 = EX_VAR(opline->op1.var);
+ op2 = EX_CONSTANT(opline->op2);
+ result = (Z_DVAL_P(op1) < Z_DVAL_P(op2));
+ ZEND_VM_SMART_BRANCH_JMPNZ(result, 0);
+ ZVAL_BOOL(EX_VAR(opline->result.var), result);
+ ZEND_VM_NEXT_OPCODE();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zval *op1, *op2;
+ int result;
+
+ op1 = EX_VAR(opline->op1.var);
+ op2 = EX_CONSTANT(opline->op2);
+ result = (Z_LVAL_P(op1) <= Z_LVAL_P(op2));
+
+ ZVAL_BOOL(EX_VAR(opline->result.var), result);
+ ZEND_VM_NEXT_OPCODE();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zval *op1, *op2;
+ int result;
+
+ op1 = EX_VAR(opline->op1.var);
+ op2 = EX_CONSTANT(opline->op2);
+ result = (Z_LVAL_P(op1) <= Z_LVAL_P(op2));
+ ZEND_VM_SMART_BRANCH_JMPZ(result, 0);
+ ZVAL_BOOL(EX_VAR(opline->result.var), result);
+ ZEND_VM_NEXT_OPCODE();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zval *op1, *op2;
+ int result;
+
+ op1 = EX_VAR(opline->op1.var);
+ op2 = EX_CONSTANT(opline->op2);
+ result = (Z_LVAL_P(op1) <= Z_LVAL_P(op2));
+ ZEND_VM_SMART_BRANCH_JMPNZ(result, 0);
+ ZVAL_BOOL(EX_VAR(opline->result.var), result);
+ ZEND_VM_NEXT_OPCODE();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zval *op1, *op2;
+ int result;
+
+ op1 = EX_VAR(opline->op1.var);
+ op2 = EX_CONSTANT(opline->op2);
+ result = (Z_DVAL_P(op1) <= Z_DVAL_P(op2));
+
+ ZVAL_BOOL(EX_VAR(opline->result.var), result);
+ ZEND_VM_NEXT_OPCODE();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zval *op1, *op2;
+ int result;
+
+ op1 = EX_VAR(opline->op1.var);
+ op2 = EX_CONSTANT(opline->op2);
+ result = (Z_DVAL_P(op1) <= Z_DVAL_P(op2));
+ ZEND_VM_SMART_BRANCH_JMPZ(result, 0);
+ ZVAL_BOOL(EX_VAR(opline->result.var), result);
+ ZEND_VM_NEXT_OPCODE();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zval *op1, *op2;
+ int result;
+
+ op1 = EX_VAR(opline->op1.var);
+ op2 = EX_CONSTANT(opline->op2);
+ result = (Z_DVAL_P(op1) <= Z_DVAL_P(op2));
+ ZEND_VM_SMART_BRANCH_JMPNZ(result, 0);
+ ZVAL_BOOL(EX_VAR(opline->result.var), result);
+ ZEND_VM_NEXT_OPCODE();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zval *op1, *op2, *result;
+
+ op1 = EX_VAR(opline->op1.var);
+ op2 = EX_VAR(opline->op2.var);
+ result = EX_VAR(opline->result.var);
+ ZVAL_LONG(result, Z_LVAL_P(op1) + Z_LVAL_P(op2));
+ ZEND_VM_NEXT_OPCODE();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zval *op1, *op2, *result;
+
+ op1 = EX_VAR(opline->op1.var);
+ op2 = EX_VAR(opline->op2.var);
+ result = EX_VAR(opline->result.var);
+ fast_long_add_function(result, op1, op2);
+ ZEND_VM_NEXT_OPCODE();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zval *op1, *op2, *result;
+
+ op1 = EX_VAR(opline->op1.var);
+ op2 = EX_VAR(opline->op2.var);
+ result = EX_VAR(opline->result.var);
+ ZVAL_DOUBLE(result, Z_DVAL_P(op1) + Z_DVAL_P(op2));
+ ZEND_VM_NEXT_OPCODE();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zval *op1, *op2, *result;
+
+ op1 = EX_VAR(opline->op1.var);
+ op2 = EX_VAR(opline->op2.var);
+ result = EX_VAR(opline->result.var);
+ ZVAL_LONG(result, Z_LVAL_P(op1) - Z_LVAL_P(op2));
+ ZEND_VM_NEXT_OPCODE();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zval *op1, *op2, *result;
+
+ op1 = EX_VAR(opline->op1.var);
+ op2 = EX_VAR(opline->op2.var);
+ result = EX_VAR(opline->result.var);
+ fast_long_sub_function(result, op1, op2);
+ ZEND_VM_NEXT_OPCODE();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zval *op1, *op2, *result;
+
+ op1 = EX_VAR(opline->op1.var);
+ op2 = EX_VAR(opline->op2.var);
+ result = EX_VAR(opline->result.var);
+ ZVAL_DOUBLE(result, Z_DVAL_P(op1) - Z_DVAL_P(op2));
+ ZEND_VM_NEXT_OPCODE();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zval *op1, *op2, *result;
+
+ op1 = EX_VAR(opline->op1.var);
+ op2 = EX_VAR(opline->op2.var);
+ result = EX_VAR(opline->result.var);
+ ZVAL_LONG(result, Z_LVAL_P(op1) * Z_LVAL_P(op2));
+ ZEND_VM_NEXT_OPCODE();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zval *op1, *op2, *result;
+ zend_long overflow;
+
+ op1 = EX_VAR(opline->op1.var);
+ op2 = EX_VAR(opline->op2.var);
+ result = EX_VAR(opline->result.var);
+ ZEND_SIGNED_MULTIPLY_LONG(Z_LVAL_P(op1), Z_LVAL_P(op2), Z_LVAL_P(result), Z_DVAL_P(result), overflow);
+ Z_TYPE_INFO_P(result) = overflow ? IS_DOUBLE : IS_LONG;
+ ZEND_VM_NEXT_OPCODE();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zval *op1, *op2, *result;
+
+ op1 = EX_VAR(opline->op1.var);
+ op2 = EX_VAR(opline->op2.var);
+ result = EX_VAR(opline->result.var);
+ ZVAL_DOUBLE(result, Z_DVAL_P(op1) * Z_DVAL_P(op2));
+ ZEND_VM_NEXT_OPCODE();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zval *op1, *op2;
+ int result;
+
+ op1 = EX_VAR(opline->op1.var);
+ op2 = EX_VAR(opline->op2.var);
+ result = (Z_LVAL_P(op1) == Z_LVAL_P(op2));
+
+ ZVAL_BOOL(EX_VAR(opline->result.var), result);
+ ZEND_VM_NEXT_OPCODE();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zval *op1, *op2;
+ int result;
+
+ op1 = EX_VAR(opline->op1.var);
+ op2 = EX_VAR(opline->op2.var);
+ result = (Z_LVAL_P(op1) == Z_LVAL_P(op2));
+ ZEND_VM_SMART_BRANCH_JMPZ(result, 0);
+ ZVAL_BOOL(EX_VAR(opline->result.var), result);
+ ZEND_VM_NEXT_OPCODE();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zval *op1, *op2;
+ int result;
+
+ op1 = EX_VAR(opline->op1.var);
+ op2 = EX_VAR(opline->op2.var);
+ result = (Z_LVAL_P(op1) == Z_LVAL_P(op2));
+ ZEND_VM_SMART_BRANCH_JMPNZ(result, 0);
+ ZVAL_BOOL(EX_VAR(opline->result.var), result);
+ ZEND_VM_NEXT_OPCODE();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zval *op1, *op2;
+ int result;
+
+ op1 = EX_VAR(opline->op1.var);
+ op2 = EX_VAR(opline->op2.var);
+ result = (Z_DVAL_P(op1) == Z_DVAL_P(op2));
+
+ ZVAL_BOOL(EX_VAR(opline->result.var), result);
+ ZEND_VM_NEXT_OPCODE();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zval *op1, *op2;
+ int result;
+
+ op1 = EX_VAR(opline->op1.var);
+ op2 = EX_VAR(opline->op2.var);
+ result = (Z_DVAL_P(op1) == Z_DVAL_P(op2));
+ ZEND_VM_SMART_BRANCH_JMPZ(result, 0);
+ ZVAL_BOOL(EX_VAR(opline->result.var), result);
+ ZEND_VM_NEXT_OPCODE();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zval *op1, *op2;
+ int result;
+
+ op1 = EX_VAR(opline->op1.var);
+ op2 = EX_VAR(opline->op2.var);
+ result = (Z_DVAL_P(op1) == Z_DVAL_P(op2));
+ ZEND_VM_SMART_BRANCH_JMPNZ(result, 0);
+ ZVAL_BOOL(EX_VAR(opline->result.var), result);
+ ZEND_VM_NEXT_OPCODE();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zval *op1, *op2;
+ int result;
+
+ op1 = EX_VAR(opline->op1.var);
+ op2 = EX_VAR(opline->op2.var);
+ result = (Z_LVAL_P(op1) != Z_LVAL_P(op2));
+
+ ZVAL_BOOL(EX_VAR(opline->result.var), result);
+ ZEND_VM_NEXT_OPCODE();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zval *op1, *op2;
+ int result;
+
+ op1 = EX_VAR(opline->op1.var);
+ op2 = EX_VAR(opline->op2.var);
+ result = (Z_LVAL_P(op1) != Z_LVAL_P(op2));
+ ZEND_VM_SMART_BRANCH_JMPZ(result, 0);
+ ZVAL_BOOL(EX_VAR(opline->result.var), result);
+ ZEND_VM_NEXT_OPCODE();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zval *op1, *op2;
+ int result;
+
+ op1 = EX_VAR(opline->op1.var);
+ op2 = EX_VAR(opline->op2.var);
+ result = (Z_LVAL_P(op1) != Z_LVAL_P(op2));
+ ZEND_VM_SMART_BRANCH_JMPNZ(result, 0);
+ ZVAL_BOOL(EX_VAR(opline->result.var), result);
+ ZEND_VM_NEXT_OPCODE();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zval *op1, *op2;
+ int result;
+
+ op1 = EX_VAR(opline->op1.var);
+ op2 = EX_VAR(opline->op2.var);
+ result = (Z_DVAL_P(op1) != Z_DVAL_P(op2));
+
+ ZVAL_BOOL(EX_VAR(opline->result.var), result);
+ ZEND_VM_NEXT_OPCODE();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zval *op1, *op2;
+ int result;
+
+ op1 = EX_VAR(opline->op1.var);
+ op2 = EX_VAR(opline->op2.var);
+ result = (Z_DVAL_P(op1) != Z_DVAL_P(op2));
+ ZEND_VM_SMART_BRANCH_JMPZ(result, 0);
+ ZVAL_BOOL(EX_VAR(opline->result.var), result);
+ ZEND_VM_NEXT_OPCODE();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zval *op1, *op2;
+ int result;
+
+ op1 = EX_VAR(opline->op1.var);
+ op2 = EX_VAR(opline->op2.var);
+ result = (Z_DVAL_P(op1) != Z_DVAL_P(op2));
+ ZEND_VM_SMART_BRANCH_JMPNZ(result, 0);
+ ZVAL_BOOL(EX_VAR(opline->result.var), result);
+ ZEND_VM_NEXT_OPCODE();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zval *op1, *op2;
+ int result;
+
+ op1 = EX_VAR(opline->op1.var);
+ op2 = EX_VAR(opline->op2.var);
+ result = (Z_LVAL_P(op1) < Z_LVAL_P(op2));
+
+ ZVAL_BOOL(EX_VAR(opline->result.var), result);
+ ZEND_VM_NEXT_OPCODE();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zval *op1, *op2;
+ int result;
+
+ op1 = EX_VAR(opline->op1.var);
+ op2 = EX_VAR(opline->op2.var);
+ result = (Z_LVAL_P(op1) < Z_LVAL_P(op2));
+ ZEND_VM_SMART_BRANCH_JMPZ(result, 0);
+ ZVAL_BOOL(EX_VAR(opline->result.var), result);
+ ZEND_VM_NEXT_OPCODE();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zval *op1, *op2;
+ int result;
+
+ op1 = EX_VAR(opline->op1.var);
+ op2 = EX_VAR(opline->op2.var);
+ result = (Z_LVAL_P(op1) < Z_LVAL_P(op2));
+ ZEND_VM_SMART_BRANCH_JMPNZ(result, 0);
+ ZVAL_BOOL(EX_VAR(opline->result.var), result);
+ ZEND_VM_NEXT_OPCODE();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zval *op1, *op2;
+ int result;
+
+ op1 = EX_VAR(opline->op1.var);
+ op2 = EX_VAR(opline->op2.var);
+ result = (Z_DVAL_P(op1) < Z_DVAL_P(op2));
+
+ ZVAL_BOOL(EX_VAR(opline->result.var), result);
+ ZEND_VM_NEXT_OPCODE();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zval *op1, *op2;
+ int result;
+
+ op1 = EX_VAR(opline->op1.var);
+ op2 = EX_VAR(opline->op2.var);
+ result = (Z_DVAL_P(op1) < Z_DVAL_P(op2));
+ ZEND_VM_SMART_BRANCH_JMPZ(result, 0);
+ ZVAL_BOOL(EX_VAR(opline->result.var), result);
+ ZEND_VM_NEXT_OPCODE();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zval *op1, *op2;
+ int result;
+
+ op1 = EX_VAR(opline->op1.var);
+ op2 = EX_VAR(opline->op2.var);
+ result = (Z_DVAL_P(op1) < Z_DVAL_P(op2));
+ ZEND_VM_SMART_BRANCH_JMPNZ(result, 0);
+ ZVAL_BOOL(EX_VAR(opline->result.var), result);
+ ZEND_VM_NEXT_OPCODE();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zval *op1, *op2;
+ int result;
+
+ op1 = EX_VAR(opline->op1.var);
+ op2 = EX_VAR(opline->op2.var);
+ result = (Z_LVAL_P(op1) <= Z_LVAL_P(op2));
+
+ ZVAL_BOOL(EX_VAR(opline->result.var), result);
+ ZEND_VM_NEXT_OPCODE();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zval *op1, *op2;
+ int result;
+
+ op1 = EX_VAR(opline->op1.var);
+ op2 = EX_VAR(opline->op2.var);
+ result = (Z_LVAL_P(op1) <= Z_LVAL_P(op2));
+ ZEND_VM_SMART_BRANCH_JMPZ(result, 0);
+ ZVAL_BOOL(EX_VAR(opline->result.var), result);
+ ZEND_VM_NEXT_OPCODE();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zval *op1, *op2;
+ int result;
+
+ op1 = EX_VAR(opline->op1.var);
+ op2 = EX_VAR(opline->op2.var);
+ result = (Z_LVAL_P(op1) <= Z_LVAL_P(op2));
+ ZEND_VM_SMART_BRANCH_JMPNZ(result, 0);
+ ZVAL_BOOL(EX_VAR(opline->result.var), result);
+ ZEND_VM_NEXT_OPCODE();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zval *op1, *op2;
+ int result;
+
+ op1 = EX_VAR(opline->op1.var);
+ op2 = EX_VAR(opline->op2.var);
+ result = (Z_DVAL_P(op1) <= Z_DVAL_P(op2));
+
+ ZVAL_BOOL(EX_VAR(opline->result.var), result);
+ ZEND_VM_NEXT_OPCODE();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zval *op1, *op2;
+ int result;
+
+ op1 = EX_VAR(opline->op1.var);
+ op2 = EX_VAR(opline->op2.var);
+ result = (Z_DVAL_P(op1) <= Z_DVAL_P(op2));
+ ZEND_VM_SMART_BRANCH_JMPZ(result, 0);
+ ZVAL_BOOL(EX_VAR(opline->result.var), result);
+ ZEND_VM_NEXT_OPCODE();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zval *op1, *op2;
+ int result;
+
+ op1 = EX_VAR(opline->op1.var);
+ op2 = EX_VAR(opline->op2.var);
+ result = (Z_DVAL_P(op1) <= Z_DVAL_P(op2));
+ ZEND_VM_SMART_BRANCH_JMPNZ(result, 0);
+ ZVAL_BOOL(EX_VAR(opline->result.var), result);
+ ZEND_VM_NEXT_OPCODE();
+}
+
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_NULL_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
@@ -45638,4358 +57050,4833 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_NULL_HANDLER(ZEND_OPCODE_HANDL
void zend_init_opcodes_handlers(void)
{
- static const void *labels[] = {
- ZEND_NOP_SPEC_HANDLER,
- ZEND_NOP_SPEC_HANDLER,
- ZEND_NOP_SPEC_HANDLER,
- ZEND_NOP_SPEC_HANDLER,
- ZEND_NOP_SPEC_HANDLER,
- ZEND_NOP_SPEC_HANDLER,
- ZEND_NOP_SPEC_HANDLER,
- ZEND_NOP_SPEC_HANDLER,
- ZEND_NOP_SPEC_HANDLER,
- ZEND_NOP_SPEC_HANDLER,
- ZEND_NOP_SPEC_HANDLER,
- ZEND_NOP_SPEC_HANDLER,
- ZEND_NOP_SPEC_HANDLER,
- ZEND_NOP_SPEC_HANDLER,
- ZEND_NOP_SPEC_HANDLER,
- ZEND_NOP_SPEC_HANDLER,
- ZEND_NOP_SPEC_HANDLER,
- ZEND_NOP_SPEC_HANDLER,
- ZEND_NOP_SPEC_HANDLER,
- ZEND_NOP_SPEC_HANDLER,
- ZEND_NOP_SPEC_HANDLER,
- ZEND_NOP_SPEC_HANDLER,
- ZEND_NOP_SPEC_HANDLER,
- ZEND_NOP_SPEC_HANDLER,
- ZEND_NOP_SPEC_HANDLER,
- ZEND_ADD_SPEC_CONST_CONST_HANDLER,
- ZEND_ADD_SPEC_CONST_TMPVAR_HANDLER,
- ZEND_ADD_SPEC_CONST_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_ADD_SPEC_CONST_CV_HANDLER,
- ZEND_ADD_SPEC_TMPVAR_CONST_HANDLER,
- ZEND_ADD_SPEC_TMPVAR_TMPVAR_HANDLER,
- ZEND_ADD_SPEC_TMPVAR_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_ADD_SPEC_TMPVAR_CV_HANDLER,
- ZEND_ADD_SPEC_TMPVAR_CONST_HANDLER,
- ZEND_ADD_SPEC_TMPVAR_TMPVAR_HANDLER,
- ZEND_ADD_SPEC_TMPVAR_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_ADD_SPEC_TMPVAR_CV_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_ADD_SPEC_CV_CONST_HANDLER,
- ZEND_ADD_SPEC_CV_TMPVAR_HANDLER,
- ZEND_ADD_SPEC_CV_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_ADD_SPEC_CV_CV_HANDLER,
- ZEND_SUB_SPEC_CONST_CONST_HANDLER,
- ZEND_SUB_SPEC_CONST_TMPVAR_HANDLER,
- ZEND_SUB_SPEC_CONST_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_SUB_SPEC_CONST_CV_HANDLER,
- ZEND_SUB_SPEC_TMPVAR_CONST_HANDLER,
- ZEND_SUB_SPEC_TMPVAR_TMPVAR_HANDLER,
- ZEND_SUB_SPEC_TMPVAR_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_SUB_SPEC_TMPVAR_CV_HANDLER,
- ZEND_SUB_SPEC_TMPVAR_CONST_HANDLER,
- ZEND_SUB_SPEC_TMPVAR_TMPVAR_HANDLER,
- ZEND_SUB_SPEC_TMPVAR_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_SUB_SPEC_TMPVAR_CV_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_SUB_SPEC_CV_CONST_HANDLER,
- ZEND_SUB_SPEC_CV_TMPVAR_HANDLER,
- ZEND_SUB_SPEC_CV_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_SUB_SPEC_CV_CV_HANDLER,
- ZEND_MUL_SPEC_CONST_CONST_HANDLER,
- ZEND_MUL_SPEC_CONST_TMPVAR_HANDLER,
- ZEND_MUL_SPEC_CONST_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_MUL_SPEC_CONST_CV_HANDLER,
- ZEND_MUL_SPEC_TMPVAR_CONST_HANDLER,
- ZEND_MUL_SPEC_TMPVAR_TMPVAR_HANDLER,
- ZEND_MUL_SPEC_TMPVAR_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_MUL_SPEC_TMPVAR_CV_HANDLER,
- ZEND_MUL_SPEC_TMPVAR_CONST_HANDLER,
- ZEND_MUL_SPEC_TMPVAR_TMPVAR_HANDLER,
- ZEND_MUL_SPEC_TMPVAR_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_MUL_SPEC_TMPVAR_CV_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_MUL_SPEC_CV_CONST_HANDLER,
- ZEND_MUL_SPEC_CV_TMPVAR_HANDLER,
- ZEND_MUL_SPEC_CV_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_MUL_SPEC_CV_CV_HANDLER,
- ZEND_DIV_SPEC_CONST_CONST_HANDLER,
- ZEND_DIV_SPEC_CONST_TMPVAR_HANDLER,
- ZEND_DIV_SPEC_CONST_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_DIV_SPEC_CONST_CV_HANDLER,
- ZEND_DIV_SPEC_TMPVAR_CONST_HANDLER,
- ZEND_DIV_SPEC_TMPVAR_TMPVAR_HANDLER,
- ZEND_DIV_SPEC_TMPVAR_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_DIV_SPEC_TMPVAR_CV_HANDLER,
- ZEND_DIV_SPEC_TMPVAR_CONST_HANDLER,
- ZEND_DIV_SPEC_TMPVAR_TMPVAR_HANDLER,
- ZEND_DIV_SPEC_TMPVAR_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_DIV_SPEC_TMPVAR_CV_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_DIV_SPEC_CV_CONST_HANDLER,
- ZEND_DIV_SPEC_CV_TMPVAR_HANDLER,
- ZEND_DIV_SPEC_CV_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_DIV_SPEC_CV_CV_HANDLER,
- ZEND_MOD_SPEC_CONST_CONST_HANDLER,
- ZEND_MOD_SPEC_CONST_TMPVAR_HANDLER,
- ZEND_MOD_SPEC_CONST_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_MOD_SPEC_CONST_CV_HANDLER,
- ZEND_MOD_SPEC_TMPVAR_CONST_HANDLER,
- ZEND_MOD_SPEC_TMPVAR_TMPVAR_HANDLER,
- ZEND_MOD_SPEC_TMPVAR_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_MOD_SPEC_TMPVAR_CV_HANDLER,
- ZEND_MOD_SPEC_TMPVAR_CONST_HANDLER,
- ZEND_MOD_SPEC_TMPVAR_TMPVAR_HANDLER,
- ZEND_MOD_SPEC_TMPVAR_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_MOD_SPEC_TMPVAR_CV_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_MOD_SPEC_CV_CONST_HANDLER,
- ZEND_MOD_SPEC_CV_TMPVAR_HANDLER,
- ZEND_MOD_SPEC_CV_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_MOD_SPEC_CV_CV_HANDLER,
- ZEND_SL_SPEC_CONST_CONST_HANDLER,
- ZEND_SL_SPEC_CONST_TMPVAR_HANDLER,
- ZEND_SL_SPEC_CONST_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_SL_SPEC_CONST_CV_HANDLER,
- ZEND_SL_SPEC_TMPVAR_CONST_HANDLER,
- ZEND_SL_SPEC_TMPVAR_TMPVAR_HANDLER,
- ZEND_SL_SPEC_TMPVAR_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_SL_SPEC_TMPVAR_CV_HANDLER,
- ZEND_SL_SPEC_TMPVAR_CONST_HANDLER,
- ZEND_SL_SPEC_TMPVAR_TMPVAR_HANDLER,
- ZEND_SL_SPEC_TMPVAR_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_SL_SPEC_TMPVAR_CV_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_SL_SPEC_CV_CONST_HANDLER,
- ZEND_SL_SPEC_CV_TMPVAR_HANDLER,
- ZEND_SL_SPEC_CV_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_SL_SPEC_CV_CV_HANDLER,
- ZEND_SR_SPEC_CONST_CONST_HANDLER,
- ZEND_SR_SPEC_CONST_TMPVAR_HANDLER,
- ZEND_SR_SPEC_CONST_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_SR_SPEC_CONST_CV_HANDLER,
- ZEND_SR_SPEC_TMPVAR_CONST_HANDLER,
- ZEND_SR_SPEC_TMPVAR_TMPVAR_HANDLER,
- ZEND_SR_SPEC_TMPVAR_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_SR_SPEC_TMPVAR_CV_HANDLER,
- ZEND_SR_SPEC_TMPVAR_CONST_HANDLER,
- ZEND_SR_SPEC_TMPVAR_TMPVAR_HANDLER,
- ZEND_SR_SPEC_TMPVAR_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_SR_SPEC_TMPVAR_CV_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_SR_SPEC_CV_CONST_HANDLER,
- ZEND_SR_SPEC_CV_TMPVAR_HANDLER,
- ZEND_SR_SPEC_CV_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_SR_SPEC_CV_CV_HANDLER,
- ZEND_CONCAT_SPEC_CONST_CONST_HANDLER,
- ZEND_CONCAT_SPEC_CONST_TMPVAR_HANDLER,
- ZEND_CONCAT_SPEC_CONST_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_CONCAT_SPEC_CONST_CV_HANDLER,
- ZEND_CONCAT_SPEC_TMPVAR_CONST_HANDLER,
- ZEND_CONCAT_SPEC_TMPVAR_TMPVAR_HANDLER,
- ZEND_CONCAT_SPEC_TMPVAR_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_CONCAT_SPEC_TMPVAR_CV_HANDLER,
- ZEND_CONCAT_SPEC_TMPVAR_CONST_HANDLER,
- ZEND_CONCAT_SPEC_TMPVAR_TMPVAR_HANDLER,
- ZEND_CONCAT_SPEC_TMPVAR_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_CONCAT_SPEC_TMPVAR_CV_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_CONCAT_SPEC_CV_CONST_HANDLER,
- ZEND_CONCAT_SPEC_CV_TMPVAR_HANDLER,
- ZEND_CONCAT_SPEC_CV_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_CONCAT_SPEC_CV_CV_HANDLER,
- ZEND_BW_OR_SPEC_CONST_CONST_HANDLER,
- ZEND_BW_OR_SPEC_CONST_TMPVAR_HANDLER,
- ZEND_BW_OR_SPEC_CONST_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_BW_OR_SPEC_CONST_CV_HANDLER,
- ZEND_BW_OR_SPEC_TMPVAR_CONST_HANDLER,
- ZEND_BW_OR_SPEC_TMPVAR_TMPVAR_HANDLER,
- ZEND_BW_OR_SPEC_TMPVAR_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_BW_OR_SPEC_TMPVAR_CV_HANDLER,
- ZEND_BW_OR_SPEC_TMPVAR_CONST_HANDLER,
- ZEND_BW_OR_SPEC_TMPVAR_TMPVAR_HANDLER,
- ZEND_BW_OR_SPEC_TMPVAR_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_BW_OR_SPEC_TMPVAR_CV_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_BW_OR_SPEC_CV_CONST_HANDLER,
- ZEND_BW_OR_SPEC_CV_TMPVAR_HANDLER,
- ZEND_BW_OR_SPEC_CV_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_BW_OR_SPEC_CV_CV_HANDLER,
- ZEND_BW_AND_SPEC_CONST_CONST_HANDLER,
- ZEND_BW_AND_SPEC_CONST_TMPVAR_HANDLER,
- ZEND_BW_AND_SPEC_CONST_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_BW_AND_SPEC_CONST_CV_HANDLER,
- ZEND_BW_AND_SPEC_TMPVAR_CONST_HANDLER,
- ZEND_BW_AND_SPEC_TMPVAR_TMPVAR_HANDLER,
- ZEND_BW_AND_SPEC_TMPVAR_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_BW_AND_SPEC_TMPVAR_CV_HANDLER,
- ZEND_BW_AND_SPEC_TMPVAR_CONST_HANDLER,
- ZEND_BW_AND_SPEC_TMPVAR_TMPVAR_HANDLER,
- ZEND_BW_AND_SPEC_TMPVAR_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_BW_AND_SPEC_TMPVAR_CV_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_BW_AND_SPEC_CV_CONST_HANDLER,
- ZEND_BW_AND_SPEC_CV_TMPVAR_HANDLER,
- ZEND_BW_AND_SPEC_CV_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_BW_AND_SPEC_CV_CV_HANDLER,
- ZEND_BW_XOR_SPEC_CONST_CONST_HANDLER,
- ZEND_BW_XOR_SPEC_CONST_TMPVAR_HANDLER,
- ZEND_BW_XOR_SPEC_CONST_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_BW_XOR_SPEC_CONST_CV_HANDLER,
- ZEND_BW_XOR_SPEC_TMPVAR_CONST_HANDLER,
- ZEND_BW_XOR_SPEC_TMPVAR_TMPVAR_HANDLER,
- ZEND_BW_XOR_SPEC_TMPVAR_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_BW_XOR_SPEC_TMPVAR_CV_HANDLER,
- ZEND_BW_XOR_SPEC_TMPVAR_CONST_HANDLER,
- ZEND_BW_XOR_SPEC_TMPVAR_TMPVAR_HANDLER,
- ZEND_BW_XOR_SPEC_TMPVAR_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_BW_XOR_SPEC_TMPVAR_CV_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_BW_XOR_SPEC_CV_CONST_HANDLER,
- ZEND_BW_XOR_SPEC_CV_TMPVAR_HANDLER,
- ZEND_BW_XOR_SPEC_CV_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_BW_XOR_SPEC_CV_CV_HANDLER,
- ZEND_BW_NOT_SPEC_CONST_HANDLER,
- ZEND_BW_NOT_SPEC_CONST_HANDLER,
- ZEND_BW_NOT_SPEC_CONST_HANDLER,
- ZEND_BW_NOT_SPEC_CONST_HANDLER,
- ZEND_BW_NOT_SPEC_CONST_HANDLER,
- ZEND_BW_NOT_SPEC_TMPVAR_HANDLER,
- ZEND_BW_NOT_SPEC_TMPVAR_HANDLER,
- ZEND_BW_NOT_SPEC_TMPVAR_HANDLER,
- ZEND_BW_NOT_SPEC_TMPVAR_HANDLER,
- ZEND_BW_NOT_SPEC_TMPVAR_HANDLER,
- ZEND_BW_NOT_SPEC_TMPVAR_HANDLER,
- ZEND_BW_NOT_SPEC_TMPVAR_HANDLER,
- ZEND_BW_NOT_SPEC_TMPVAR_HANDLER,
- ZEND_BW_NOT_SPEC_TMPVAR_HANDLER,
- ZEND_BW_NOT_SPEC_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_BW_NOT_SPEC_CV_HANDLER,
- ZEND_BW_NOT_SPEC_CV_HANDLER,
- ZEND_BW_NOT_SPEC_CV_HANDLER,
- ZEND_BW_NOT_SPEC_CV_HANDLER,
- ZEND_BW_NOT_SPEC_CV_HANDLER,
- ZEND_BOOL_NOT_SPEC_CONST_HANDLER,
- ZEND_BOOL_NOT_SPEC_CONST_HANDLER,
- ZEND_BOOL_NOT_SPEC_CONST_HANDLER,
- ZEND_BOOL_NOT_SPEC_CONST_HANDLER,
- ZEND_BOOL_NOT_SPEC_CONST_HANDLER,
- ZEND_BOOL_NOT_SPEC_TMPVAR_HANDLER,
- ZEND_BOOL_NOT_SPEC_TMPVAR_HANDLER,
- ZEND_BOOL_NOT_SPEC_TMPVAR_HANDLER,
- ZEND_BOOL_NOT_SPEC_TMPVAR_HANDLER,
- ZEND_BOOL_NOT_SPEC_TMPVAR_HANDLER,
- ZEND_BOOL_NOT_SPEC_TMPVAR_HANDLER,
- ZEND_BOOL_NOT_SPEC_TMPVAR_HANDLER,
- ZEND_BOOL_NOT_SPEC_TMPVAR_HANDLER,
- ZEND_BOOL_NOT_SPEC_TMPVAR_HANDLER,
- ZEND_BOOL_NOT_SPEC_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_BOOL_NOT_SPEC_CV_HANDLER,
- ZEND_BOOL_NOT_SPEC_CV_HANDLER,
- ZEND_BOOL_NOT_SPEC_CV_HANDLER,
- ZEND_BOOL_NOT_SPEC_CV_HANDLER,
- ZEND_BOOL_NOT_SPEC_CV_HANDLER,
- ZEND_BOOL_XOR_SPEC_CONST_CONST_HANDLER,
- ZEND_BOOL_XOR_SPEC_CONST_TMPVAR_HANDLER,
- ZEND_BOOL_XOR_SPEC_CONST_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_BOOL_XOR_SPEC_CONST_CV_HANDLER,
- ZEND_BOOL_XOR_SPEC_TMPVAR_CONST_HANDLER,
- ZEND_BOOL_XOR_SPEC_TMPVAR_TMPVAR_HANDLER,
- ZEND_BOOL_XOR_SPEC_TMPVAR_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_BOOL_XOR_SPEC_TMPVAR_CV_HANDLER,
- ZEND_BOOL_XOR_SPEC_TMPVAR_CONST_HANDLER,
- ZEND_BOOL_XOR_SPEC_TMPVAR_TMPVAR_HANDLER,
- ZEND_BOOL_XOR_SPEC_TMPVAR_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_BOOL_XOR_SPEC_TMPVAR_CV_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_BOOL_XOR_SPEC_CV_CONST_HANDLER,
- ZEND_BOOL_XOR_SPEC_CV_TMPVAR_HANDLER,
- ZEND_BOOL_XOR_SPEC_CV_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_BOOL_XOR_SPEC_CV_CV_HANDLER,
- ZEND_IS_IDENTICAL_SPEC_CONST_CONST_HANDLER,
- ZEND_IS_IDENTICAL_SPEC_CONST_TMP_HANDLER,
- ZEND_IS_IDENTICAL_SPEC_CONST_VAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_IS_IDENTICAL_SPEC_CONST_CV_HANDLER,
- ZEND_IS_IDENTICAL_SPEC_TMP_CONST_HANDLER,
- ZEND_IS_IDENTICAL_SPEC_TMP_TMP_HANDLER,
- ZEND_IS_IDENTICAL_SPEC_TMP_VAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_IS_IDENTICAL_SPEC_TMP_CV_HANDLER,
- ZEND_IS_IDENTICAL_SPEC_VAR_CONST_HANDLER,
- ZEND_IS_IDENTICAL_SPEC_VAR_TMP_HANDLER,
- ZEND_IS_IDENTICAL_SPEC_VAR_VAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_IS_IDENTICAL_SPEC_VAR_CV_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_IS_IDENTICAL_SPEC_CV_CONST_HANDLER,
- ZEND_IS_IDENTICAL_SPEC_CV_TMP_HANDLER,
- ZEND_IS_IDENTICAL_SPEC_CV_VAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_IS_IDENTICAL_SPEC_CV_CV_HANDLER,
- ZEND_IS_NOT_IDENTICAL_SPEC_CONST_CONST_HANDLER,
- ZEND_IS_NOT_IDENTICAL_SPEC_CONST_TMP_HANDLER,
- ZEND_IS_NOT_IDENTICAL_SPEC_CONST_VAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_IS_NOT_IDENTICAL_SPEC_CONST_CV_HANDLER,
- ZEND_IS_NOT_IDENTICAL_SPEC_TMP_CONST_HANDLER,
- ZEND_IS_NOT_IDENTICAL_SPEC_TMP_TMP_HANDLER,
- ZEND_IS_NOT_IDENTICAL_SPEC_TMP_VAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_IS_NOT_IDENTICAL_SPEC_TMP_CV_HANDLER,
- ZEND_IS_NOT_IDENTICAL_SPEC_VAR_CONST_HANDLER,
- ZEND_IS_NOT_IDENTICAL_SPEC_VAR_TMP_HANDLER,
- ZEND_IS_NOT_IDENTICAL_SPEC_VAR_VAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_IS_NOT_IDENTICAL_SPEC_VAR_CV_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_IS_NOT_IDENTICAL_SPEC_CV_CONST_HANDLER,
- ZEND_IS_NOT_IDENTICAL_SPEC_CV_TMP_HANDLER,
- ZEND_IS_NOT_IDENTICAL_SPEC_CV_VAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_IS_NOT_IDENTICAL_SPEC_CV_CV_HANDLER,
- ZEND_IS_EQUAL_SPEC_CONST_CONST_HANDLER,
- ZEND_IS_EQUAL_SPEC_CONST_TMPVAR_HANDLER,
- ZEND_IS_EQUAL_SPEC_CONST_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_IS_EQUAL_SPEC_CONST_CV_HANDLER,
- ZEND_IS_EQUAL_SPEC_TMPVAR_CONST_HANDLER,
- ZEND_IS_EQUAL_SPEC_TMPVAR_TMPVAR_HANDLER,
- ZEND_IS_EQUAL_SPEC_TMPVAR_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_IS_EQUAL_SPEC_TMPVAR_CV_HANDLER,
- ZEND_IS_EQUAL_SPEC_TMPVAR_CONST_HANDLER,
- ZEND_IS_EQUAL_SPEC_TMPVAR_TMPVAR_HANDLER,
- ZEND_IS_EQUAL_SPEC_TMPVAR_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_IS_EQUAL_SPEC_TMPVAR_CV_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_IS_EQUAL_SPEC_CV_CONST_HANDLER,
- ZEND_IS_EQUAL_SPEC_CV_TMPVAR_HANDLER,
- ZEND_IS_EQUAL_SPEC_CV_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_IS_EQUAL_SPEC_CV_CV_HANDLER,
- ZEND_IS_NOT_EQUAL_SPEC_CONST_CONST_HANDLER,
- ZEND_IS_NOT_EQUAL_SPEC_CONST_TMPVAR_HANDLER,
- ZEND_IS_NOT_EQUAL_SPEC_CONST_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_IS_NOT_EQUAL_SPEC_CONST_CV_HANDLER,
- ZEND_IS_NOT_EQUAL_SPEC_TMPVAR_CONST_HANDLER,
- ZEND_IS_NOT_EQUAL_SPEC_TMPVAR_TMPVAR_HANDLER,
- ZEND_IS_NOT_EQUAL_SPEC_TMPVAR_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_IS_NOT_EQUAL_SPEC_TMPVAR_CV_HANDLER,
- ZEND_IS_NOT_EQUAL_SPEC_TMPVAR_CONST_HANDLER,
- ZEND_IS_NOT_EQUAL_SPEC_TMPVAR_TMPVAR_HANDLER,
- ZEND_IS_NOT_EQUAL_SPEC_TMPVAR_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_IS_NOT_EQUAL_SPEC_TMPVAR_CV_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_IS_NOT_EQUAL_SPEC_CV_CONST_HANDLER,
- ZEND_IS_NOT_EQUAL_SPEC_CV_TMPVAR_HANDLER,
- ZEND_IS_NOT_EQUAL_SPEC_CV_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_IS_NOT_EQUAL_SPEC_CV_CV_HANDLER,
- ZEND_IS_SMALLER_SPEC_CONST_CONST_HANDLER,
- ZEND_IS_SMALLER_SPEC_CONST_TMPVAR_HANDLER,
- ZEND_IS_SMALLER_SPEC_CONST_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_IS_SMALLER_SPEC_CONST_CV_HANDLER,
- ZEND_IS_SMALLER_SPEC_TMPVAR_CONST_HANDLER,
- ZEND_IS_SMALLER_SPEC_TMPVAR_TMPVAR_HANDLER,
- ZEND_IS_SMALLER_SPEC_TMPVAR_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_IS_SMALLER_SPEC_TMPVAR_CV_HANDLER,
- ZEND_IS_SMALLER_SPEC_TMPVAR_CONST_HANDLER,
- ZEND_IS_SMALLER_SPEC_TMPVAR_TMPVAR_HANDLER,
- ZEND_IS_SMALLER_SPEC_TMPVAR_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_IS_SMALLER_SPEC_TMPVAR_CV_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_IS_SMALLER_SPEC_CV_CONST_HANDLER,
- ZEND_IS_SMALLER_SPEC_CV_TMPVAR_HANDLER,
- ZEND_IS_SMALLER_SPEC_CV_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_IS_SMALLER_SPEC_CV_CV_HANDLER,
- ZEND_IS_SMALLER_OR_EQUAL_SPEC_CONST_CONST_HANDLER,
- ZEND_IS_SMALLER_OR_EQUAL_SPEC_CONST_TMPVAR_HANDLER,
- ZEND_IS_SMALLER_OR_EQUAL_SPEC_CONST_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_IS_SMALLER_OR_EQUAL_SPEC_CONST_CV_HANDLER,
- ZEND_IS_SMALLER_OR_EQUAL_SPEC_TMPVAR_CONST_HANDLER,
- ZEND_IS_SMALLER_OR_EQUAL_SPEC_TMPVAR_TMPVAR_HANDLER,
- ZEND_IS_SMALLER_OR_EQUAL_SPEC_TMPVAR_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_IS_SMALLER_OR_EQUAL_SPEC_TMPVAR_CV_HANDLER,
- ZEND_IS_SMALLER_OR_EQUAL_SPEC_TMPVAR_CONST_HANDLER,
- ZEND_IS_SMALLER_OR_EQUAL_SPEC_TMPVAR_TMPVAR_HANDLER,
- ZEND_IS_SMALLER_OR_EQUAL_SPEC_TMPVAR_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_IS_SMALLER_OR_EQUAL_SPEC_TMPVAR_CV_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_IS_SMALLER_OR_EQUAL_SPEC_CV_CONST_HANDLER,
- ZEND_IS_SMALLER_OR_EQUAL_SPEC_CV_TMPVAR_HANDLER,
- ZEND_IS_SMALLER_OR_EQUAL_SPEC_CV_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_IS_SMALLER_OR_EQUAL_SPEC_CV_CV_HANDLER,
- ZEND_CAST_SPEC_CONST_HANDLER,
- ZEND_CAST_SPEC_CONST_HANDLER,
- ZEND_CAST_SPEC_CONST_HANDLER,
- ZEND_CAST_SPEC_CONST_HANDLER,
- ZEND_CAST_SPEC_CONST_HANDLER,
- ZEND_CAST_SPEC_TMP_HANDLER,
- ZEND_CAST_SPEC_TMP_HANDLER,
- ZEND_CAST_SPEC_TMP_HANDLER,
- ZEND_CAST_SPEC_TMP_HANDLER,
- ZEND_CAST_SPEC_TMP_HANDLER,
- ZEND_CAST_SPEC_VAR_HANDLER,
- ZEND_CAST_SPEC_VAR_HANDLER,
- ZEND_CAST_SPEC_VAR_HANDLER,
- ZEND_CAST_SPEC_VAR_HANDLER,
- ZEND_CAST_SPEC_VAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_CAST_SPEC_CV_HANDLER,
- ZEND_CAST_SPEC_CV_HANDLER,
- ZEND_CAST_SPEC_CV_HANDLER,
- ZEND_CAST_SPEC_CV_HANDLER,
- ZEND_CAST_SPEC_CV_HANDLER,
- ZEND_QM_ASSIGN_SPEC_CONST_HANDLER,
- ZEND_QM_ASSIGN_SPEC_CONST_HANDLER,
- ZEND_QM_ASSIGN_SPEC_CONST_HANDLER,
- ZEND_QM_ASSIGN_SPEC_CONST_HANDLER,
- ZEND_QM_ASSIGN_SPEC_CONST_HANDLER,
- ZEND_QM_ASSIGN_SPEC_TMP_HANDLER,
- ZEND_QM_ASSIGN_SPEC_TMP_HANDLER,
- ZEND_QM_ASSIGN_SPEC_TMP_HANDLER,
- ZEND_QM_ASSIGN_SPEC_TMP_HANDLER,
- ZEND_QM_ASSIGN_SPEC_TMP_HANDLER,
- ZEND_QM_ASSIGN_SPEC_VAR_HANDLER,
- ZEND_QM_ASSIGN_SPEC_VAR_HANDLER,
- ZEND_QM_ASSIGN_SPEC_VAR_HANDLER,
- ZEND_QM_ASSIGN_SPEC_VAR_HANDLER,
- ZEND_QM_ASSIGN_SPEC_VAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_QM_ASSIGN_SPEC_CV_HANDLER,
- ZEND_QM_ASSIGN_SPEC_CV_HANDLER,
- ZEND_QM_ASSIGN_SPEC_CV_HANDLER,
- ZEND_QM_ASSIGN_SPEC_CV_HANDLER,
- ZEND_QM_ASSIGN_SPEC_CV_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_ASSIGN_ADD_SPEC_VAR_CONST_HANDLER,
- ZEND_ASSIGN_ADD_SPEC_VAR_TMPVAR_HANDLER,
- ZEND_ASSIGN_ADD_SPEC_VAR_TMPVAR_HANDLER,
- ZEND_ASSIGN_ADD_SPEC_VAR_UNUSED_HANDLER,
- ZEND_ASSIGN_ADD_SPEC_VAR_CV_HANDLER,
- ZEND_ASSIGN_ADD_SPEC_UNUSED_CONST_HANDLER,
- ZEND_ASSIGN_ADD_SPEC_UNUSED_TMPVAR_HANDLER,
- ZEND_ASSIGN_ADD_SPEC_UNUSED_TMPVAR_HANDLER,
- ZEND_ASSIGN_ADD_SPEC_UNUSED_UNUSED_HANDLER,
- ZEND_ASSIGN_ADD_SPEC_UNUSED_CV_HANDLER,
- ZEND_ASSIGN_ADD_SPEC_CV_CONST_HANDLER,
- ZEND_ASSIGN_ADD_SPEC_CV_TMPVAR_HANDLER,
- ZEND_ASSIGN_ADD_SPEC_CV_TMPVAR_HANDLER,
- ZEND_ASSIGN_ADD_SPEC_CV_UNUSED_HANDLER,
- ZEND_ASSIGN_ADD_SPEC_CV_CV_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_ASSIGN_SUB_SPEC_VAR_CONST_HANDLER,
- ZEND_ASSIGN_SUB_SPEC_VAR_TMPVAR_HANDLER,
- ZEND_ASSIGN_SUB_SPEC_VAR_TMPVAR_HANDLER,
- ZEND_ASSIGN_SUB_SPEC_VAR_UNUSED_HANDLER,
- ZEND_ASSIGN_SUB_SPEC_VAR_CV_HANDLER,
- ZEND_ASSIGN_SUB_SPEC_UNUSED_CONST_HANDLER,
- ZEND_ASSIGN_SUB_SPEC_UNUSED_TMPVAR_HANDLER,
- ZEND_ASSIGN_SUB_SPEC_UNUSED_TMPVAR_HANDLER,
- ZEND_ASSIGN_SUB_SPEC_UNUSED_UNUSED_HANDLER,
- ZEND_ASSIGN_SUB_SPEC_UNUSED_CV_HANDLER,
- ZEND_ASSIGN_SUB_SPEC_CV_CONST_HANDLER,
- ZEND_ASSIGN_SUB_SPEC_CV_TMPVAR_HANDLER,
- ZEND_ASSIGN_SUB_SPEC_CV_TMPVAR_HANDLER,
- ZEND_ASSIGN_SUB_SPEC_CV_UNUSED_HANDLER,
- ZEND_ASSIGN_SUB_SPEC_CV_CV_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_ASSIGN_MUL_SPEC_VAR_CONST_HANDLER,
- ZEND_ASSIGN_MUL_SPEC_VAR_TMPVAR_HANDLER,
- ZEND_ASSIGN_MUL_SPEC_VAR_TMPVAR_HANDLER,
- ZEND_ASSIGN_MUL_SPEC_VAR_UNUSED_HANDLER,
- ZEND_ASSIGN_MUL_SPEC_VAR_CV_HANDLER,
- ZEND_ASSIGN_MUL_SPEC_UNUSED_CONST_HANDLER,
- ZEND_ASSIGN_MUL_SPEC_UNUSED_TMPVAR_HANDLER,
- ZEND_ASSIGN_MUL_SPEC_UNUSED_TMPVAR_HANDLER,
- ZEND_ASSIGN_MUL_SPEC_UNUSED_UNUSED_HANDLER,
- ZEND_ASSIGN_MUL_SPEC_UNUSED_CV_HANDLER,
- ZEND_ASSIGN_MUL_SPEC_CV_CONST_HANDLER,
- ZEND_ASSIGN_MUL_SPEC_CV_TMPVAR_HANDLER,
- ZEND_ASSIGN_MUL_SPEC_CV_TMPVAR_HANDLER,
- ZEND_ASSIGN_MUL_SPEC_CV_UNUSED_HANDLER,
- ZEND_ASSIGN_MUL_SPEC_CV_CV_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_ASSIGN_DIV_SPEC_VAR_CONST_HANDLER,
- ZEND_ASSIGN_DIV_SPEC_VAR_TMPVAR_HANDLER,
- ZEND_ASSIGN_DIV_SPEC_VAR_TMPVAR_HANDLER,
- ZEND_ASSIGN_DIV_SPEC_VAR_UNUSED_HANDLER,
- ZEND_ASSIGN_DIV_SPEC_VAR_CV_HANDLER,
- ZEND_ASSIGN_DIV_SPEC_UNUSED_CONST_HANDLER,
- ZEND_ASSIGN_DIV_SPEC_UNUSED_TMPVAR_HANDLER,
- ZEND_ASSIGN_DIV_SPEC_UNUSED_TMPVAR_HANDLER,
- ZEND_ASSIGN_DIV_SPEC_UNUSED_UNUSED_HANDLER,
- ZEND_ASSIGN_DIV_SPEC_UNUSED_CV_HANDLER,
- ZEND_ASSIGN_DIV_SPEC_CV_CONST_HANDLER,
- ZEND_ASSIGN_DIV_SPEC_CV_TMPVAR_HANDLER,
- ZEND_ASSIGN_DIV_SPEC_CV_TMPVAR_HANDLER,
- ZEND_ASSIGN_DIV_SPEC_CV_UNUSED_HANDLER,
- ZEND_ASSIGN_DIV_SPEC_CV_CV_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_ASSIGN_MOD_SPEC_VAR_CONST_HANDLER,
- ZEND_ASSIGN_MOD_SPEC_VAR_TMPVAR_HANDLER,
- ZEND_ASSIGN_MOD_SPEC_VAR_TMPVAR_HANDLER,
- ZEND_ASSIGN_MOD_SPEC_VAR_UNUSED_HANDLER,
- ZEND_ASSIGN_MOD_SPEC_VAR_CV_HANDLER,
- ZEND_ASSIGN_MOD_SPEC_UNUSED_CONST_HANDLER,
- ZEND_ASSIGN_MOD_SPEC_UNUSED_TMPVAR_HANDLER,
- ZEND_ASSIGN_MOD_SPEC_UNUSED_TMPVAR_HANDLER,
- ZEND_ASSIGN_MOD_SPEC_UNUSED_UNUSED_HANDLER,
- ZEND_ASSIGN_MOD_SPEC_UNUSED_CV_HANDLER,
- ZEND_ASSIGN_MOD_SPEC_CV_CONST_HANDLER,
- ZEND_ASSIGN_MOD_SPEC_CV_TMPVAR_HANDLER,
- ZEND_ASSIGN_MOD_SPEC_CV_TMPVAR_HANDLER,
- ZEND_ASSIGN_MOD_SPEC_CV_UNUSED_HANDLER,
- ZEND_ASSIGN_MOD_SPEC_CV_CV_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_ASSIGN_SL_SPEC_VAR_CONST_HANDLER,
- ZEND_ASSIGN_SL_SPEC_VAR_TMPVAR_HANDLER,
- ZEND_ASSIGN_SL_SPEC_VAR_TMPVAR_HANDLER,
- ZEND_ASSIGN_SL_SPEC_VAR_UNUSED_HANDLER,
- ZEND_ASSIGN_SL_SPEC_VAR_CV_HANDLER,
- ZEND_ASSIGN_SL_SPEC_UNUSED_CONST_HANDLER,
- ZEND_ASSIGN_SL_SPEC_UNUSED_TMPVAR_HANDLER,
- ZEND_ASSIGN_SL_SPEC_UNUSED_TMPVAR_HANDLER,
- ZEND_ASSIGN_SL_SPEC_UNUSED_UNUSED_HANDLER,
- ZEND_ASSIGN_SL_SPEC_UNUSED_CV_HANDLER,
- ZEND_ASSIGN_SL_SPEC_CV_CONST_HANDLER,
- ZEND_ASSIGN_SL_SPEC_CV_TMPVAR_HANDLER,
- ZEND_ASSIGN_SL_SPEC_CV_TMPVAR_HANDLER,
- ZEND_ASSIGN_SL_SPEC_CV_UNUSED_HANDLER,
- ZEND_ASSIGN_SL_SPEC_CV_CV_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_ASSIGN_SR_SPEC_VAR_CONST_HANDLER,
- ZEND_ASSIGN_SR_SPEC_VAR_TMPVAR_HANDLER,
- ZEND_ASSIGN_SR_SPEC_VAR_TMPVAR_HANDLER,
- ZEND_ASSIGN_SR_SPEC_VAR_UNUSED_HANDLER,
- ZEND_ASSIGN_SR_SPEC_VAR_CV_HANDLER,
- ZEND_ASSIGN_SR_SPEC_UNUSED_CONST_HANDLER,
- ZEND_ASSIGN_SR_SPEC_UNUSED_TMPVAR_HANDLER,
- ZEND_ASSIGN_SR_SPEC_UNUSED_TMPVAR_HANDLER,
- ZEND_ASSIGN_SR_SPEC_UNUSED_UNUSED_HANDLER,
- ZEND_ASSIGN_SR_SPEC_UNUSED_CV_HANDLER,
- ZEND_ASSIGN_SR_SPEC_CV_CONST_HANDLER,
- ZEND_ASSIGN_SR_SPEC_CV_TMPVAR_HANDLER,
- ZEND_ASSIGN_SR_SPEC_CV_TMPVAR_HANDLER,
- ZEND_ASSIGN_SR_SPEC_CV_UNUSED_HANDLER,
- ZEND_ASSIGN_SR_SPEC_CV_CV_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_ASSIGN_CONCAT_SPEC_VAR_CONST_HANDLER,
- ZEND_ASSIGN_CONCAT_SPEC_VAR_TMPVAR_HANDLER,
- ZEND_ASSIGN_CONCAT_SPEC_VAR_TMPVAR_HANDLER,
- ZEND_ASSIGN_CONCAT_SPEC_VAR_UNUSED_HANDLER,
- ZEND_ASSIGN_CONCAT_SPEC_VAR_CV_HANDLER,
- ZEND_ASSIGN_CONCAT_SPEC_UNUSED_CONST_HANDLER,
- ZEND_ASSIGN_CONCAT_SPEC_UNUSED_TMPVAR_HANDLER,
- ZEND_ASSIGN_CONCAT_SPEC_UNUSED_TMPVAR_HANDLER,
- ZEND_ASSIGN_CONCAT_SPEC_UNUSED_UNUSED_HANDLER,
- ZEND_ASSIGN_CONCAT_SPEC_UNUSED_CV_HANDLER,
- ZEND_ASSIGN_CONCAT_SPEC_CV_CONST_HANDLER,
- ZEND_ASSIGN_CONCAT_SPEC_CV_TMPVAR_HANDLER,
- ZEND_ASSIGN_CONCAT_SPEC_CV_TMPVAR_HANDLER,
- ZEND_ASSIGN_CONCAT_SPEC_CV_UNUSED_HANDLER,
- ZEND_ASSIGN_CONCAT_SPEC_CV_CV_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_ASSIGN_BW_OR_SPEC_VAR_CONST_HANDLER,
- ZEND_ASSIGN_BW_OR_SPEC_VAR_TMPVAR_HANDLER,
- ZEND_ASSIGN_BW_OR_SPEC_VAR_TMPVAR_HANDLER,
- ZEND_ASSIGN_BW_OR_SPEC_VAR_UNUSED_HANDLER,
- ZEND_ASSIGN_BW_OR_SPEC_VAR_CV_HANDLER,
- ZEND_ASSIGN_BW_OR_SPEC_UNUSED_CONST_HANDLER,
- ZEND_ASSIGN_BW_OR_SPEC_UNUSED_TMPVAR_HANDLER,
- ZEND_ASSIGN_BW_OR_SPEC_UNUSED_TMPVAR_HANDLER,
- ZEND_ASSIGN_BW_OR_SPEC_UNUSED_UNUSED_HANDLER,
- ZEND_ASSIGN_BW_OR_SPEC_UNUSED_CV_HANDLER,
- ZEND_ASSIGN_BW_OR_SPEC_CV_CONST_HANDLER,
- ZEND_ASSIGN_BW_OR_SPEC_CV_TMPVAR_HANDLER,
- ZEND_ASSIGN_BW_OR_SPEC_CV_TMPVAR_HANDLER,
- ZEND_ASSIGN_BW_OR_SPEC_CV_UNUSED_HANDLER,
- ZEND_ASSIGN_BW_OR_SPEC_CV_CV_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_ASSIGN_BW_AND_SPEC_VAR_CONST_HANDLER,
- ZEND_ASSIGN_BW_AND_SPEC_VAR_TMPVAR_HANDLER,
- ZEND_ASSIGN_BW_AND_SPEC_VAR_TMPVAR_HANDLER,
- ZEND_ASSIGN_BW_AND_SPEC_VAR_UNUSED_HANDLER,
- ZEND_ASSIGN_BW_AND_SPEC_VAR_CV_HANDLER,
- ZEND_ASSIGN_BW_AND_SPEC_UNUSED_CONST_HANDLER,
- ZEND_ASSIGN_BW_AND_SPEC_UNUSED_TMPVAR_HANDLER,
- ZEND_ASSIGN_BW_AND_SPEC_UNUSED_TMPVAR_HANDLER,
- ZEND_ASSIGN_BW_AND_SPEC_UNUSED_UNUSED_HANDLER,
- ZEND_ASSIGN_BW_AND_SPEC_UNUSED_CV_HANDLER,
- ZEND_ASSIGN_BW_AND_SPEC_CV_CONST_HANDLER,
- ZEND_ASSIGN_BW_AND_SPEC_CV_TMPVAR_HANDLER,
- ZEND_ASSIGN_BW_AND_SPEC_CV_TMPVAR_HANDLER,
- ZEND_ASSIGN_BW_AND_SPEC_CV_UNUSED_HANDLER,
- ZEND_ASSIGN_BW_AND_SPEC_CV_CV_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_ASSIGN_BW_XOR_SPEC_VAR_CONST_HANDLER,
- ZEND_ASSIGN_BW_XOR_SPEC_VAR_TMPVAR_HANDLER,
- ZEND_ASSIGN_BW_XOR_SPEC_VAR_TMPVAR_HANDLER,
- ZEND_ASSIGN_BW_XOR_SPEC_VAR_UNUSED_HANDLER,
- ZEND_ASSIGN_BW_XOR_SPEC_VAR_CV_HANDLER,
- ZEND_ASSIGN_BW_XOR_SPEC_UNUSED_CONST_HANDLER,
- ZEND_ASSIGN_BW_XOR_SPEC_UNUSED_TMPVAR_HANDLER,
- ZEND_ASSIGN_BW_XOR_SPEC_UNUSED_TMPVAR_HANDLER,
- ZEND_ASSIGN_BW_XOR_SPEC_UNUSED_UNUSED_HANDLER,
- ZEND_ASSIGN_BW_XOR_SPEC_UNUSED_CV_HANDLER,
- ZEND_ASSIGN_BW_XOR_SPEC_CV_CONST_HANDLER,
- ZEND_ASSIGN_BW_XOR_SPEC_CV_TMPVAR_HANDLER,
- ZEND_ASSIGN_BW_XOR_SPEC_CV_TMPVAR_HANDLER,
- ZEND_ASSIGN_BW_XOR_SPEC_CV_UNUSED_HANDLER,
- ZEND_ASSIGN_BW_XOR_SPEC_CV_CV_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_PRE_INC_SPEC_VAR_HANDLER,
- ZEND_PRE_INC_SPEC_VAR_HANDLER,
- ZEND_PRE_INC_SPEC_VAR_HANDLER,
- ZEND_PRE_INC_SPEC_VAR_HANDLER,
- ZEND_PRE_INC_SPEC_VAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_PRE_INC_SPEC_CV_HANDLER,
- ZEND_PRE_INC_SPEC_CV_HANDLER,
- ZEND_PRE_INC_SPEC_CV_HANDLER,
- ZEND_PRE_INC_SPEC_CV_HANDLER,
- ZEND_PRE_INC_SPEC_CV_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_PRE_DEC_SPEC_VAR_HANDLER,
- ZEND_PRE_DEC_SPEC_VAR_HANDLER,
- ZEND_PRE_DEC_SPEC_VAR_HANDLER,
- ZEND_PRE_DEC_SPEC_VAR_HANDLER,
- ZEND_PRE_DEC_SPEC_VAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_PRE_DEC_SPEC_CV_HANDLER,
- ZEND_PRE_DEC_SPEC_CV_HANDLER,
- ZEND_PRE_DEC_SPEC_CV_HANDLER,
- ZEND_PRE_DEC_SPEC_CV_HANDLER,
- ZEND_PRE_DEC_SPEC_CV_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_POST_INC_SPEC_VAR_HANDLER,
- ZEND_POST_INC_SPEC_VAR_HANDLER,
- ZEND_POST_INC_SPEC_VAR_HANDLER,
- ZEND_POST_INC_SPEC_VAR_HANDLER,
- ZEND_POST_INC_SPEC_VAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_POST_INC_SPEC_CV_HANDLER,
- ZEND_POST_INC_SPEC_CV_HANDLER,
- ZEND_POST_INC_SPEC_CV_HANDLER,
- ZEND_POST_INC_SPEC_CV_HANDLER,
- ZEND_POST_INC_SPEC_CV_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_POST_DEC_SPEC_VAR_HANDLER,
- ZEND_POST_DEC_SPEC_VAR_HANDLER,
- ZEND_POST_DEC_SPEC_VAR_HANDLER,
- ZEND_POST_DEC_SPEC_VAR_HANDLER,
- ZEND_POST_DEC_SPEC_VAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_POST_DEC_SPEC_CV_HANDLER,
- ZEND_POST_DEC_SPEC_CV_HANDLER,
- ZEND_POST_DEC_SPEC_CV_HANDLER,
- ZEND_POST_DEC_SPEC_CV_HANDLER,
- ZEND_POST_DEC_SPEC_CV_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_ASSIGN_SPEC_VAR_CONST_HANDLER,
- ZEND_ASSIGN_SPEC_VAR_TMP_HANDLER,
- ZEND_ASSIGN_SPEC_VAR_VAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_ASSIGN_SPEC_VAR_CV_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_ASSIGN_SPEC_CV_CONST_HANDLER,
- ZEND_ASSIGN_SPEC_CV_TMP_HANDLER,
- ZEND_ASSIGN_SPEC_CV_VAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_ASSIGN_SPEC_CV_CV_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_ASSIGN_REF_SPEC_VAR_VAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_ASSIGN_REF_SPEC_VAR_CV_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_ASSIGN_REF_SPEC_CV_VAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_ASSIGN_REF_SPEC_CV_CV_HANDLER,
- ZEND_ECHO_SPEC_CONST_HANDLER,
- ZEND_ECHO_SPEC_CONST_HANDLER,
- ZEND_ECHO_SPEC_CONST_HANDLER,
- ZEND_ECHO_SPEC_CONST_HANDLER,
- ZEND_ECHO_SPEC_CONST_HANDLER,
- ZEND_ECHO_SPEC_TMPVAR_HANDLER,
- ZEND_ECHO_SPEC_TMPVAR_HANDLER,
- ZEND_ECHO_SPEC_TMPVAR_HANDLER,
- ZEND_ECHO_SPEC_TMPVAR_HANDLER,
- ZEND_ECHO_SPEC_TMPVAR_HANDLER,
- ZEND_ECHO_SPEC_TMPVAR_HANDLER,
- ZEND_ECHO_SPEC_TMPVAR_HANDLER,
- ZEND_ECHO_SPEC_TMPVAR_HANDLER,
- ZEND_ECHO_SPEC_TMPVAR_HANDLER,
- ZEND_ECHO_SPEC_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_ECHO_SPEC_CV_HANDLER,
- ZEND_ECHO_SPEC_CV_HANDLER,
- ZEND_ECHO_SPEC_CV_HANDLER,
- ZEND_ECHO_SPEC_CV_HANDLER,
- ZEND_ECHO_SPEC_CV_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,
- 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_JMP_SPEC_HANDLER,
- ZEND_JMP_SPEC_HANDLER,
- ZEND_JMP_SPEC_HANDLER,
- ZEND_JMP_SPEC_HANDLER,
- ZEND_JMP_SPEC_HANDLER,
- ZEND_JMP_SPEC_HANDLER,
- ZEND_JMP_SPEC_HANDLER,
- ZEND_JMP_SPEC_HANDLER,
- ZEND_JMP_SPEC_HANDLER,
- ZEND_JMP_SPEC_HANDLER,
- ZEND_JMP_SPEC_HANDLER,
- ZEND_JMP_SPEC_HANDLER,
- ZEND_JMP_SPEC_HANDLER,
- ZEND_JMP_SPEC_HANDLER,
- ZEND_JMP_SPEC_HANDLER,
- ZEND_JMP_SPEC_HANDLER,
- ZEND_JMP_SPEC_HANDLER,
- ZEND_JMP_SPEC_HANDLER,
- ZEND_JMP_SPEC_HANDLER,
- ZEND_JMP_SPEC_HANDLER,
- ZEND_JMP_SPEC_HANDLER,
- ZEND_JMP_SPEC_HANDLER,
- ZEND_JMP_SPEC_HANDLER,
- ZEND_JMP_SPEC_HANDLER,
- ZEND_JMP_SPEC_HANDLER,
- ZEND_JMPZ_SPEC_CONST_HANDLER,
- ZEND_JMPZ_SPEC_CONST_HANDLER,
- ZEND_JMPZ_SPEC_CONST_HANDLER,
- ZEND_JMPZ_SPEC_CONST_HANDLER,
- ZEND_JMPZ_SPEC_CONST_HANDLER,
- ZEND_JMPZ_SPEC_TMPVAR_HANDLER,
- ZEND_JMPZ_SPEC_TMPVAR_HANDLER,
- ZEND_JMPZ_SPEC_TMPVAR_HANDLER,
- ZEND_JMPZ_SPEC_TMPVAR_HANDLER,
- ZEND_JMPZ_SPEC_TMPVAR_HANDLER,
- ZEND_JMPZ_SPEC_TMPVAR_HANDLER,
- ZEND_JMPZ_SPEC_TMPVAR_HANDLER,
- ZEND_JMPZ_SPEC_TMPVAR_HANDLER,
- ZEND_JMPZ_SPEC_TMPVAR_HANDLER,
- ZEND_JMPZ_SPEC_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_JMPZ_SPEC_CV_HANDLER,
- ZEND_JMPZ_SPEC_CV_HANDLER,
- ZEND_JMPZ_SPEC_CV_HANDLER,
- ZEND_JMPZ_SPEC_CV_HANDLER,
- ZEND_JMPZ_SPEC_CV_HANDLER,
- ZEND_JMPNZ_SPEC_CONST_HANDLER,
- ZEND_JMPNZ_SPEC_CONST_HANDLER,
- ZEND_JMPNZ_SPEC_CONST_HANDLER,
- ZEND_JMPNZ_SPEC_CONST_HANDLER,
- ZEND_JMPNZ_SPEC_CONST_HANDLER,
- ZEND_JMPNZ_SPEC_TMPVAR_HANDLER,
- ZEND_JMPNZ_SPEC_TMPVAR_HANDLER,
- ZEND_JMPNZ_SPEC_TMPVAR_HANDLER,
- ZEND_JMPNZ_SPEC_TMPVAR_HANDLER,
- ZEND_JMPNZ_SPEC_TMPVAR_HANDLER,
- ZEND_JMPNZ_SPEC_TMPVAR_HANDLER,
- ZEND_JMPNZ_SPEC_TMPVAR_HANDLER,
- ZEND_JMPNZ_SPEC_TMPVAR_HANDLER,
- ZEND_JMPNZ_SPEC_TMPVAR_HANDLER,
- ZEND_JMPNZ_SPEC_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_JMPNZ_SPEC_CV_HANDLER,
- ZEND_JMPNZ_SPEC_CV_HANDLER,
- ZEND_JMPNZ_SPEC_CV_HANDLER,
- ZEND_JMPNZ_SPEC_CV_HANDLER,
- ZEND_JMPNZ_SPEC_CV_HANDLER,
- ZEND_JMPZNZ_SPEC_CONST_HANDLER,
- ZEND_JMPZNZ_SPEC_CONST_HANDLER,
- ZEND_JMPZNZ_SPEC_CONST_HANDLER,
- ZEND_JMPZNZ_SPEC_CONST_HANDLER,
- ZEND_JMPZNZ_SPEC_CONST_HANDLER,
- ZEND_JMPZNZ_SPEC_TMPVAR_HANDLER,
- ZEND_JMPZNZ_SPEC_TMPVAR_HANDLER,
- ZEND_JMPZNZ_SPEC_TMPVAR_HANDLER,
- ZEND_JMPZNZ_SPEC_TMPVAR_HANDLER,
- ZEND_JMPZNZ_SPEC_TMPVAR_HANDLER,
- ZEND_JMPZNZ_SPEC_TMPVAR_HANDLER,
- ZEND_JMPZNZ_SPEC_TMPVAR_HANDLER,
- ZEND_JMPZNZ_SPEC_TMPVAR_HANDLER,
- ZEND_JMPZNZ_SPEC_TMPVAR_HANDLER,
- ZEND_JMPZNZ_SPEC_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_JMPZNZ_SPEC_CV_HANDLER,
- ZEND_JMPZNZ_SPEC_CV_HANDLER,
- ZEND_JMPZNZ_SPEC_CV_HANDLER,
- ZEND_JMPZNZ_SPEC_CV_HANDLER,
- ZEND_JMPZNZ_SPEC_CV_HANDLER,
- ZEND_JMPZ_EX_SPEC_CONST_HANDLER,
- ZEND_JMPZ_EX_SPEC_CONST_HANDLER,
- ZEND_JMPZ_EX_SPEC_CONST_HANDLER,
- ZEND_JMPZ_EX_SPEC_CONST_HANDLER,
- ZEND_JMPZ_EX_SPEC_CONST_HANDLER,
- ZEND_JMPZ_EX_SPEC_TMPVAR_HANDLER,
- ZEND_JMPZ_EX_SPEC_TMPVAR_HANDLER,
- ZEND_JMPZ_EX_SPEC_TMPVAR_HANDLER,
- ZEND_JMPZ_EX_SPEC_TMPVAR_HANDLER,
- ZEND_JMPZ_EX_SPEC_TMPVAR_HANDLER,
- ZEND_JMPZ_EX_SPEC_TMPVAR_HANDLER,
- ZEND_JMPZ_EX_SPEC_TMPVAR_HANDLER,
- ZEND_JMPZ_EX_SPEC_TMPVAR_HANDLER,
- ZEND_JMPZ_EX_SPEC_TMPVAR_HANDLER,
- ZEND_JMPZ_EX_SPEC_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_JMPZ_EX_SPEC_CV_HANDLER,
- ZEND_JMPZ_EX_SPEC_CV_HANDLER,
- ZEND_JMPZ_EX_SPEC_CV_HANDLER,
- ZEND_JMPZ_EX_SPEC_CV_HANDLER,
- ZEND_JMPZ_EX_SPEC_CV_HANDLER,
- ZEND_JMPNZ_EX_SPEC_CONST_HANDLER,
- ZEND_JMPNZ_EX_SPEC_CONST_HANDLER,
- ZEND_JMPNZ_EX_SPEC_CONST_HANDLER,
- ZEND_JMPNZ_EX_SPEC_CONST_HANDLER,
- ZEND_JMPNZ_EX_SPEC_CONST_HANDLER,
- ZEND_JMPNZ_EX_SPEC_TMPVAR_HANDLER,
- ZEND_JMPNZ_EX_SPEC_TMPVAR_HANDLER,
- ZEND_JMPNZ_EX_SPEC_TMPVAR_HANDLER,
- ZEND_JMPNZ_EX_SPEC_TMPVAR_HANDLER,
- ZEND_JMPNZ_EX_SPEC_TMPVAR_HANDLER,
- ZEND_JMPNZ_EX_SPEC_TMPVAR_HANDLER,
- ZEND_JMPNZ_EX_SPEC_TMPVAR_HANDLER,
- ZEND_JMPNZ_EX_SPEC_TMPVAR_HANDLER,
- ZEND_JMPNZ_EX_SPEC_TMPVAR_HANDLER,
- ZEND_JMPNZ_EX_SPEC_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_JMPNZ_EX_SPEC_CV_HANDLER,
- ZEND_JMPNZ_EX_SPEC_CV_HANDLER,
- ZEND_JMPNZ_EX_SPEC_CV_HANDLER,
- ZEND_JMPNZ_EX_SPEC_CV_HANDLER,
- ZEND_JMPNZ_EX_SPEC_CV_HANDLER,
- ZEND_CASE_SPEC_CONST_CONST_HANDLER,
- ZEND_CASE_SPEC_CONST_TMPVAR_HANDLER,
- ZEND_CASE_SPEC_CONST_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_CASE_SPEC_CONST_CV_HANDLER,
- ZEND_CASE_SPEC_TMPVAR_CONST_HANDLER,
- ZEND_CASE_SPEC_TMPVAR_TMPVAR_HANDLER,
- ZEND_CASE_SPEC_TMPVAR_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_CASE_SPEC_TMPVAR_CV_HANDLER,
- ZEND_CASE_SPEC_TMPVAR_CONST_HANDLER,
- ZEND_CASE_SPEC_TMPVAR_TMPVAR_HANDLER,
- ZEND_CASE_SPEC_TMPVAR_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_CASE_SPEC_TMPVAR_CV_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_CASE_SPEC_CV_CONST_HANDLER,
- ZEND_CASE_SPEC_CV_TMPVAR_HANDLER,
- ZEND_CASE_SPEC_CV_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_CASE_SPEC_CV_CV_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,
- 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,
- 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,
- 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,
- 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,
- 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_BOOL_SPEC_CONST_HANDLER,
- ZEND_BOOL_SPEC_CONST_HANDLER,
- ZEND_BOOL_SPEC_CONST_HANDLER,
- ZEND_BOOL_SPEC_CONST_HANDLER,
- ZEND_BOOL_SPEC_CONST_HANDLER,
- ZEND_BOOL_SPEC_TMPVAR_HANDLER,
- ZEND_BOOL_SPEC_TMPVAR_HANDLER,
- ZEND_BOOL_SPEC_TMPVAR_HANDLER,
- ZEND_BOOL_SPEC_TMPVAR_HANDLER,
- ZEND_BOOL_SPEC_TMPVAR_HANDLER,
- ZEND_BOOL_SPEC_TMPVAR_HANDLER,
- ZEND_BOOL_SPEC_TMPVAR_HANDLER,
- ZEND_BOOL_SPEC_TMPVAR_HANDLER,
- ZEND_BOOL_SPEC_TMPVAR_HANDLER,
- ZEND_BOOL_SPEC_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_BOOL_SPEC_CV_HANDLER,
- ZEND_BOOL_SPEC_CV_HANDLER,
- ZEND_BOOL_SPEC_CV_HANDLER,
- ZEND_BOOL_SPEC_CV_HANDLER,
- ZEND_BOOL_SPEC_CV_HANDLER,
- ZEND_FAST_CONCAT_SPEC_CONST_CONST_HANDLER,
- ZEND_FAST_CONCAT_SPEC_CONST_TMPVAR_HANDLER,
- ZEND_FAST_CONCAT_SPEC_CONST_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_FAST_CONCAT_SPEC_CONST_CV_HANDLER,
- ZEND_FAST_CONCAT_SPEC_TMPVAR_CONST_HANDLER,
- ZEND_FAST_CONCAT_SPEC_TMPVAR_TMPVAR_HANDLER,
- ZEND_FAST_CONCAT_SPEC_TMPVAR_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_FAST_CONCAT_SPEC_TMPVAR_CV_HANDLER,
- ZEND_FAST_CONCAT_SPEC_TMPVAR_CONST_HANDLER,
- ZEND_FAST_CONCAT_SPEC_TMPVAR_TMPVAR_HANDLER,
- ZEND_FAST_CONCAT_SPEC_TMPVAR_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_FAST_CONCAT_SPEC_TMPVAR_CV_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_FAST_CONCAT_SPEC_CV_CONST_HANDLER,
- ZEND_FAST_CONCAT_SPEC_CV_TMPVAR_HANDLER,
- ZEND_FAST_CONCAT_SPEC_CV_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_FAST_CONCAT_SPEC_CV_CV_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,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_ROPE_INIT_SPEC_UNUSED_CONST_HANDLER,
- ZEND_ROPE_INIT_SPEC_UNUSED_TMPVAR_HANDLER,
- ZEND_ROPE_INIT_SPEC_UNUSED_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_ROPE_INIT_SPEC_UNUSED_CV_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_ROPE_ADD_SPEC_TMP_CONST_HANDLER,
- ZEND_ROPE_ADD_SPEC_TMP_TMPVAR_HANDLER,
- ZEND_ROPE_ADD_SPEC_TMP_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_ROPE_ADD_SPEC_TMP_CV_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,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_ROPE_END_SPEC_TMP_CONST_HANDLER,
- ZEND_ROPE_END_SPEC_TMP_TMPVAR_HANDLER,
- ZEND_ROPE_END_SPEC_TMP_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_ROPE_END_SPEC_TMP_CV_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,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_BEGIN_SILENCE_SPEC_HANDLER,
- ZEND_BEGIN_SILENCE_SPEC_HANDLER,
- ZEND_BEGIN_SILENCE_SPEC_HANDLER,
- ZEND_BEGIN_SILENCE_SPEC_HANDLER,
- ZEND_BEGIN_SILENCE_SPEC_HANDLER,
- ZEND_BEGIN_SILENCE_SPEC_HANDLER,
- ZEND_BEGIN_SILENCE_SPEC_HANDLER,
- ZEND_BEGIN_SILENCE_SPEC_HANDLER,
- ZEND_BEGIN_SILENCE_SPEC_HANDLER,
- ZEND_BEGIN_SILENCE_SPEC_HANDLER,
- ZEND_BEGIN_SILENCE_SPEC_HANDLER,
- ZEND_BEGIN_SILENCE_SPEC_HANDLER,
- ZEND_BEGIN_SILENCE_SPEC_HANDLER,
- ZEND_BEGIN_SILENCE_SPEC_HANDLER,
- ZEND_BEGIN_SILENCE_SPEC_HANDLER,
- ZEND_BEGIN_SILENCE_SPEC_HANDLER,
- ZEND_BEGIN_SILENCE_SPEC_HANDLER,
- ZEND_BEGIN_SILENCE_SPEC_HANDLER,
- ZEND_BEGIN_SILENCE_SPEC_HANDLER,
- ZEND_BEGIN_SILENCE_SPEC_HANDLER,
- ZEND_BEGIN_SILENCE_SPEC_HANDLER,
- ZEND_BEGIN_SILENCE_SPEC_HANDLER,
- ZEND_BEGIN_SILENCE_SPEC_HANDLER,
- ZEND_BEGIN_SILENCE_SPEC_HANDLER,
- ZEND_BEGIN_SILENCE_SPEC_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_END_SILENCE_SPEC_TMP_HANDLER,
- ZEND_END_SILENCE_SPEC_TMP_HANDLER,
- ZEND_END_SILENCE_SPEC_TMP_HANDLER,
- ZEND_END_SILENCE_SPEC_TMP_HANDLER,
- ZEND_END_SILENCE_SPEC_TMP_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,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_INIT_FCALL_BY_NAME_SPEC_CONST_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_INIT_FCALL_BY_NAME_SPEC_CONST_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_INIT_FCALL_BY_NAME_SPEC_CONST_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_INIT_FCALL_BY_NAME_SPEC_CONST_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_INIT_FCALL_BY_NAME_SPEC_CONST_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_DO_FCALL_SPEC_HANDLER,
- ZEND_DO_FCALL_SPEC_HANDLER,
- ZEND_DO_FCALL_SPEC_HANDLER,
- ZEND_DO_FCALL_SPEC_HANDLER,
- ZEND_DO_FCALL_SPEC_HANDLER,
- ZEND_DO_FCALL_SPEC_HANDLER,
- ZEND_DO_FCALL_SPEC_HANDLER,
- ZEND_DO_FCALL_SPEC_HANDLER,
- ZEND_DO_FCALL_SPEC_HANDLER,
- ZEND_DO_FCALL_SPEC_HANDLER,
- ZEND_DO_FCALL_SPEC_HANDLER,
- ZEND_DO_FCALL_SPEC_HANDLER,
- ZEND_DO_FCALL_SPEC_HANDLER,
- ZEND_DO_FCALL_SPEC_HANDLER,
- ZEND_DO_FCALL_SPEC_HANDLER,
- ZEND_DO_FCALL_SPEC_HANDLER,
- ZEND_DO_FCALL_SPEC_HANDLER,
- ZEND_DO_FCALL_SPEC_HANDLER,
- ZEND_DO_FCALL_SPEC_HANDLER,
- ZEND_DO_FCALL_SPEC_HANDLER,
- ZEND_DO_FCALL_SPEC_HANDLER,
- ZEND_DO_FCALL_SPEC_HANDLER,
- ZEND_DO_FCALL_SPEC_HANDLER,
- ZEND_DO_FCALL_SPEC_HANDLER,
- ZEND_DO_FCALL_SPEC_HANDLER,
- ZEND_INIT_FCALL_SPEC_CONST_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_INIT_FCALL_SPEC_CONST_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_INIT_FCALL_SPEC_CONST_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_INIT_FCALL_SPEC_CONST_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_INIT_FCALL_SPEC_CONST_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_RETURN_SPEC_CONST_HANDLER,
- ZEND_RETURN_SPEC_CONST_HANDLER,
- ZEND_RETURN_SPEC_CONST_HANDLER,
- ZEND_RETURN_SPEC_CONST_HANDLER,
- ZEND_RETURN_SPEC_CONST_HANDLER,
- ZEND_RETURN_SPEC_TMP_HANDLER,
- ZEND_RETURN_SPEC_TMP_HANDLER,
- ZEND_RETURN_SPEC_TMP_HANDLER,
- ZEND_RETURN_SPEC_TMP_HANDLER,
- ZEND_RETURN_SPEC_TMP_HANDLER,
- ZEND_RETURN_SPEC_VAR_HANDLER,
- ZEND_RETURN_SPEC_VAR_HANDLER,
- ZEND_RETURN_SPEC_VAR_HANDLER,
- ZEND_RETURN_SPEC_VAR_HANDLER,
- ZEND_RETURN_SPEC_VAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_RETURN_SPEC_CV_HANDLER,
- ZEND_RETURN_SPEC_CV_HANDLER,
- ZEND_RETURN_SPEC_CV_HANDLER,
- ZEND_RETURN_SPEC_CV_HANDLER,
- ZEND_RETURN_SPEC_CV_HANDLER,
- ZEND_RECV_SPEC_HANDLER,
- ZEND_RECV_SPEC_HANDLER,
- ZEND_RECV_SPEC_HANDLER,
- ZEND_RECV_SPEC_HANDLER,
- ZEND_RECV_SPEC_HANDLER,
- ZEND_RECV_SPEC_HANDLER,
- ZEND_RECV_SPEC_HANDLER,
- ZEND_RECV_SPEC_HANDLER,
- ZEND_RECV_SPEC_HANDLER,
- ZEND_RECV_SPEC_HANDLER,
- ZEND_RECV_SPEC_HANDLER,
- ZEND_RECV_SPEC_HANDLER,
- ZEND_RECV_SPEC_HANDLER,
- ZEND_RECV_SPEC_HANDLER,
- ZEND_RECV_SPEC_HANDLER,
- ZEND_RECV_SPEC_HANDLER,
- ZEND_RECV_SPEC_HANDLER,
- ZEND_RECV_SPEC_HANDLER,
- ZEND_RECV_SPEC_HANDLER,
- ZEND_RECV_SPEC_HANDLER,
- ZEND_RECV_SPEC_HANDLER,
- ZEND_RECV_SPEC_HANDLER,
- ZEND_RECV_SPEC_HANDLER,
- ZEND_RECV_SPEC_HANDLER,
- ZEND_RECV_SPEC_HANDLER,
- ZEND_RECV_INIT_SPEC_CONST_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_RECV_INIT_SPEC_CONST_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_RECV_INIT_SPEC_CONST_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_RECV_INIT_SPEC_CONST_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_RECV_INIT_SPEC_CONST_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_SEND_VAL_SPEC_CONST_HANDLER,
- ZEND_SEND_VAL_SPEC_CONST_HANDLER,
- ZEND_SEND_VAL_SPEC_CONST_HANDLER,
- ZEND_SEND_VAL_SPEC_CONST_HANDLER,
- ZEND_SEND_VAL_SPEC_CONST_HANDLER,
- ZEND_SEND_VAL_SPEC_TMP_HANDLER,
- ZEND_SEND_VAL_SPEC_TMP_HANDLER,
- ZEND_SEND_VAL_SPEC_TMP_HANDLER,
- ZEND_SEND_VAL_SPEC_TMP_HANDLER,
- ZEND_SEND_VAL_SPEC_TMP_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,
- 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_SEND_VAR_EX_SPEC_VAR_HANDLER,
- ZEND_SEND_VAR_EX_SPEC_VAR_HANDLER,
- ZEND_SEND_VAR_EX_SPEC_VAR_HANDLER,
- ZEND_SEND_VAR_EX_SPEC_VAR_HANDLER,
- ZEND_SEND_VAR_EX_SPEC_VAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_SEND_VAR_EX_SPEC_CV_HANDLER,
- ZEND_SEND_VAR_EX_SPEC_CV_HANDLER,
- ZEND_SEND_VAR_EX_SPEC_CV_HANDLER,
- ZEND_SEND_VAR_EX_SPEC_CV_HANDLER,
- ZEND_SEND_VAR_EX_SPEC_CV_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_SEND_REF_SPEC_VAR_HANDLER,
- ZEND_SEND_REF_SPEC_VAR_HANDLER,
- ZEND_SEND_REF_SPEC_VAR_HANDLER,
- ZEND_SEND_REF_SPEC_VAR_HANDLER,
- ZEND_SEND_REF_SPEC_VAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_SEND_REF_SPEC_CV_HANDLER,
- ZEND_SEND_REF_SPEC_CV_HANDLER,
- ZEND_SEND_REF_SPEC_CV_HANDLER,
- ZEND_SEND_REF_SPEC_CV_HANDLER,
- ZEND_SEND_REF_SPEC_CV_HANDLER,
- ZEND_NEW_SPEC_CONST_HANDLER,
- ZEND_NEW_SPEC_CONST_HANDLER,
- ZEND_NEW_SPEC_CONST_HANDLER,
- ZEND_NEW_SPEC_CONST_HANDLER,
- ZEND_NEW_SPEC_CONST_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NEW_SPEC_VAR_HANDLER,
- ZEND_NEW_SPEC_VAR_HANDLER,
- ZEND_NEW_SPEC_VAR_HANDLER,
- ZEND_NEW_SPEC_VAR_HANDLER,
- ZEND_NEW_SPEC_VAR_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_INIT_NS_FCALL_BY_NAME_SPEC_CONST_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_INIT_NS_FCALL_BY_NAME_SPEC_CONST_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_INIT_NS_FCALL_BY_NAME_SPEC_CONST_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_INIT_NS_FCALL_BY_NAME_SPEC_CONST_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_INIT_NS_FCALL_BY_NAME_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_FREE_SPEC_TMPVAR_HANDLER,
- ZEND_FREE_SPEC_TMPVAR_HANDLER,
- ZEND_FREE_SPEC_TMPVAR_HANDLER,
- ZEND_FREE_SPEC_TMPVAR_HANDLER,
- ZEND_FREE_SPEC_TMPVAR_HANDLER,
- ZEND_FREE_SPEC_TMPVAR_HANDLER,
- ZEND_FREE_SPEC_TMPVAR_HANDLER,
- ZEND_FREE_SPEC_TMPVAR_HANDLER,
- ZEND_FREE_SPEC_TMPVAR_HANDLER,
- ZEND_FREE_SPEC_TMPVAR_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_INIT_ARRAY_SPEC_CONST_CONST_HANDLER,
- ZEND_INIT_ARRAY_SPEC_CONST_TMPVAR_HANDLER,
- ZEND_INIT_ARRAY_SPEC_CONST_TMPVAR_HANDLER,
- ZEND_INIT_ARRAY_SPEC_CONST_UNUSED_HANDLER,
- ZEND_INIT_ARRAY_SPEC_CONST_CV_HANDLER,
- ZEND_INIT_ARRAY_SPEC_TMP_CONST_HANDLER,
- ZEND_INIT_ARRAY_SPEC_TMP_TMPVAR_HANDLER,
- ZEND_INIT_ARRAY_SPEC_TMP_TMPVAR_HANDLER,
- ZEND_INIT_ARRAY_SPEC_TMP_UNUSED_HANDLER,
- ZEND_INIT_ARRAY_SPEC_TMP_CV_HANDLER,
- ZEND_INIT_ARRAY_SPEC_VAR_CONST_HANDLER,
- ZEND_INIT_ARRAY_SPEC_VAR_TMPVAR_HANDLER,
- ZEND_INIT_ARRAY_SPEC_VAR_TMPVAR_HANDLER,
- ZEND_INIT_ARRAY_SPEC_VAR_UNUSED_HANDLER,
- ZEND_INIT_ARRAY_SPEC_VAR_CV_HANDLER,
- ZEND_INIT_ARRAY_SPEC_UNUSED_CONST_HANDLER,
- ZEND_INIT_ARRAY_SPEC_UNUSED_TMPVAR_HANDLER,
- ZEND_INIT_ARRAY_SPEC_UNUSED_TMPVAR_HANDLER,
- ZEND_INIT_ARRAY_SPEC_UNUSED_UNUSED_HANDLER,
- ZEND_INIT_ARRAY_SPEC_UNUSED_CV_HANDLER,
- ZEND_INIT_ARRAY_SPEC_CV_CONST_HANDLER,
- ZEND_INIT_ARRAY_SPEC_CV_TMPVAR_HANDLER,
- ZEND_INIT_ARRAY_SPEC_CV_TMPVAR_HANDLER,
- ZEND_INIT_ARRAY_SPEC_CV_UNUSED_HANDLER,
- ZEND_INIT_ARRAY_SPEC_CV_CV_HANDLER,
- ZEND_ADD_ARRAY_ELEMENT_SPEC_CONST_CONST_HANDLER,
- ZEND_ADD_ARRAY_ELEMENT_SPEC_CONST_TMPVAR_HANDLER,
- ZEND_ADD_ARRAY_ELEMENT_SPEC_CONST_TMPVAR_HANDLER,
- ZEND_ADD_ARRAY_ELEMENT_SPEC_CONST_UNUSED_HANDLER,
- ZEND_ADD_ARRAY_ELEMENT_SPEC_CONST_CV_HANDLER,
- ZEND_ADD_ARRAY_ELEMENT_SPEC_TMP_CONST_HANDLER,
- ZEND_ADD_ARRAY_ELEMENT_SPEC_TMP_TMPVAR_HANDLER,
- ZEND_ADD_ARRAY_ELEMENT_SPEC_TMP_TMPVAR_HANDLER,
- ZEND_ADD_ARRAY_ELEMENT_SPEC_TMP_UNUSED_HANDLER,
- ZEND_ADD_ARRAY_ELEMENT_SPEC_TMP_CV_HANDLER,
- ZEND_ADD_ARRAY_ELEMENT_SPEC_VAR_CONST_HANDLER,
- ZEND_ADD_ARRAY_ELEMENT_SPEC_VAR_TMPVAR_HANDLER,
- ZEND_ADD_ARRAY_ELEMENT_SPEC_VAR_TMPVAR_HANDLER,
- ZEND_ADD_ARRAY_ELEMENT_SPEC_VAR_UNUSED_HANDLER,
- ZEND_ADD_ARRAY_ELEMENT_SPEC_VAR_CV_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_ADD_ARRAY_ELEMENT_SPEC_CV_CONST_HANDLER,
- ZEND_ADD_ARRAY_ELEMENT_SPEC_CV_TMPVAR_HANDLER,
- ZEND_ADD_ARRAY_ELEMENT_SPEC_CV_TMPVAR_HANDLER,
- ZEND_ADD_ARRAY_ELEMENT_SPEC_CV_UNUSED_HANDLER,
- ZEND_ADD_ARRAY_ELEMENT_SPEC_CV_CV_HANDLER,
- ZEND_INCLUDE_OR_EVAL_SPEC_CONST_HANDLER,
- ZEND_INCLUDE_OR_EVAL_SPEC_CONST_HANDLER,
- ZEND_INCLUDE_OR_EVAL_SPEC_CONST_HANDLER,
- ZEND_INCLUDE_OR_EVAL_SPEC_CONST_HANDLER,
- ZEND_INCLUDE_OR_EVAL_SPEC_CONST_HANDLER,
- ZEND_INCLUDE_OR_EVAL_SPEC_TMPVAR_HANDLER,
- ZEND_INCLUDE_OR_EVAL_SPEC_TMPVAR_HANDLER,
- ZEND_INCLUDE_OR_EVAL_SPEC_TMPVAR_HANDLER,
- ZEND_INCLUDE_OR_EVAL_SPEC_TMPVAR_HANDLER,
- ZEND_INCLUDE_OR_EVAL_SPEC_TMPVAR_HANDLER,
- ZEND_INCLUDE_OR_EVAL_SPEC_TMPVAR_HANDLER,
- ZEND_INCLUDE_OR_EVAL_SPEC_TMPVAR_HANDLER,
- ZEND_INCLUDE_OR_EVAL_SPEC_TMPVAR_HANDLER,
- ZEND_INCLUDE_OR_EVAL_SPEC_TMPVAR_HANDLER,
- ZEND_INCLUDE_OR_EVAL_SPEC_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_INCLUDE_OR_EVAL_SPEC_CV_HANDLER,
- ZEND_INCLUDE_OR_EVAL_SPEC_CV_HANDLER,
- ZEND_INCLUDE_OR_EVAL_SPEC_CV_HANDLER,
- ZEND_INCLUDE_OR_EVAL_SPEC_CV_HANDLER,
- ZEND_INCLUDE_OR_EVAL_SPEC_CV_HANDLER,
- ZEND_UNSET_VAR_SPEC_CONST_CONST_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_UNSET_VAR_SPEC_CONST_VAR_HANDLER,
- ZEND_UNSET_VAR_SPEC_CONST_UNUSED_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_UNSET_VAR_SPEC_TMPVAR_CONST_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_UNSET_VAR_SPEC_TMPVAR_VAR_HANDLER,
- ZEND_UNSET_VAR_SPEC_TMPVAR_UNUSED_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_UNSET_VAR_SPEC_TMPVAR_CONST_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_UNSET_VAR_SPEC_TMPVAR_VAR_HANDLER,
- ZEND_UNSET_VAR_SPEC_TMPVAR_UNUSED_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_UNSET_VAR_SPEC_CV_CONST_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_UNSET_VAR_SPEC_CV_VAR_HANDLER,
- ZEND_UNSET_VAR_SPEC_CV_UNUSED_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_UNSET_DIM_SPEC_VAR_CONST_HANDLER,
- ZEND_UNSET_DIM_SPEC_VAR_TMPVAR_HANDLER,
- ZEND_UNSET_DIM_SPEC_VAR_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_UNSET_DIM_SPEC_VAR_CV_HANDLER,
- ZEND_UNSET_DIM_SPEC_UNUSED_CONST_HANDLER,
- ZEND_UNSET_DIM_SPEC_UNUSED_TMPVAR_HANDLER,
- ZEND_UNSET_DIM_SPEC_UNUSED_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_UNSET_DIM_SPEC_UNUSED_CV_HANDLER,
- ZEND_UNSET_DIM_SPEC_CV_CONST_HANDLER,
- ZEND_UNSET_DIM_SPEC_CV_TMPVAR_HANDLER,
- ZEND_UNSET_DIM_SPEC_CV_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_UNSET_DIM_SPEC_CV_CV_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_UNSET_OBJ_SPEC_VAR_CONST_HANDLER,
- ZEND_UNSET_OBJ_SPEC_VAR_TMPVAR_HANDLER,
- ZEND_UNSET_OBJ_SPEC_VAR_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_UNSET_OBJ_SPEC_VAR_CV_HANDLER,
- ZEND_UNSET_OBJ_SPEC_UNUSED_CONST_HANDLER,
- ZEND_UNSET_OBJ_SPEC_UNUSED_TMPVAR_HANDLER,
- ZEND_UNSET_OBJ_SPEC_UNUSED_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_UNSET_OBJ_SPEC_UNUSED_CV_HANDLER,
- ZEND_UNSET_OBJ_SPEC_CV_CONST_HANDLER,
- ZEND_UNSET_OBJ_SPEC_CV_TMPVAR_HANDLER,
- ZEND_UNSET_OBJ_SPEC_CV_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_UNSET_OBJ_SPEC_CV_CV_HANDLER,
- ZEND_FE_RESET_R_SPEC_CONST_HANDLER,
- ZEND_FE_RESET_R_SPEC_CONST_HANDLER,
- ZEND_FE_RESET_R_SPEC_CONST_HANDLER,
- ZEND_FE_RESET_R_SPEC_CONST_HANDLER,
- ZEND_FE_RESET_R_SPEC_CONST_HANDLER,
- ZEND_FE_RESET_R_SPEC_TMP_HANDLER,
- ZEND_FE_RESET_R_SPEC_TMP_HANDLER,
- ZEND_FE_RESET_R_SPEC_TMP_HANDLER,
- ZEND_FE_RESET_R_SPEC_TMP_HANDLER,
- ZEND_FE_RESET_R_SPEC_TMP_HANDLER,
- ZEND_FE_RESET_R_SPEC_VAR_HANDLER,
- ZEND_FE_RESET_R_SPEC_VAR_HANDLER,
- ZEND_FE_RESET_R_SPEC_VAR_HANDLER,
- ZEND_FE_RESET_R_SPEC_VAR_HANDLER,
- ZEND_FE_RESET_R_SPEC_VAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_FE_RESET_R_SPEC_CV_HANDLER,
- ZEND_FE_RESET_R_SPEC_CV_HANDLER,
- ZEND_FE_RESET_R_SPEC_CV_HANDLER,
- ZEND_FE_RESET_R_SPEC_CV_HANDLER,
- ZEND_FE_RESET_R_SPEC_CV_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_FE_FETCH_R_SPEC_VAR_HANDLER,
- ZEND_FE_FETCH_R_SPEC_VAR_HANDLER,
- ZEND_FE_FETCH_R_SPEC_VAR_HANDLER,
- ZEND_FE_FETCH_R_SPEC_VAR_HANDLER,
- ZEND_FE_FETCH_R_SPEC_VAR_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_EXIT_SPEC_CONST_HANDLER,
- ZEND_EXIT_SPEC_CONST_HANDLER,
- ZEND_EXIT_SPEC_CONST_HANDLER,
- ZEND_EXIT_SPEC_CONST_HANDLER,
- ZEND_EXIT_SPEC_CONST_HANDLER,
- ZEND_EXIT_SPEC_TMPVAR_HANDLER,
- ZEND_EXIT_SPEC_TMPVAR_HANDLER,
- ZEND_EXIT_SPEC_TMPVAR_HANDLER,
- ZEND_EXIT_SPEC_TMPVAR_HANDLER,
- ZEND_EXIT_SPEC_TMPVAR_HANDLER,
- ZEND_EXIT_SPEC_TMPVAR_HANDLER,
- ZEND_EXIT_SPEC_TMPVAR_HANDLER,
- ZEND_EXIT_SPEC_TMPVAR_HANDLER,
- ZEND_EXIT_SPEC_TMPVAR_HANDLER,
- ZEND_EXIT_SPEC_TMPVAR_HANDLER,
- ZEND_EXIT_SPEC_UNUSED_HANDLER,
- ZEND_EXIT_SPEC_UNUSED_HANDLER,
- ZEND_EXIT_SPEC_UNUSED_HANDLER,
- ZEND_EXIT_SPEC_UNUSED_HANDLER,
- ZEND_EXIT_SPEC_UNUSED_HANDLER,
- ZEND_EXIT_SPEC_CV_HANDLER,
- ZEND_EXIT_SPEC_CV_HANDLER,
- ZEND_EXIT_SPEC_CV_HANDLER,
- ZEND_EXIT_SPEC_CV_HANDLER,
- ZEND_EXIT_SPEC_CV_HANDLER,
- ZEND_FETCH_R_SPEC_CONST_CONST_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_FETCH_R_SPEC_CONST_VAR_HANDLER,
- ZEND_FETCH_R_SPEC_CONST_UNUSED_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_FETCH_R_SPEC_TMPVAR_CONST_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_FETCH_R_SPEC_TMPVAR_VAR_HANDLER,
- ZEND_FETCH_R_SPEC_TMPVAR_UNUSED_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_FETCH_R_SPEC_TMPVAR_CONST_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_FETCH_R_SPEC_TMPVAR_VAR_HANDLER,
- ZEND_FETCH_R_SPEC_TMPVAR_UNUSED_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_FETCH_R_SPEC_CV_CONST_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_FETCH_R_SPEC_CV_VAR_HANDLER,
- ZEND_FETCH_R_SPEC_CV_UNUSED_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_FETCH_DIM_R_SPEC_CONST_CONST_HANDLER,
- ZEND_FETCH_DIM_R_SPEC_CONST_TMPVAR_HANDLER,
- ZEND_FETCH_DIM_R_SPEC_CONST_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_FETCH_DIM_R_SPEC_CONST_CV_HANDLER,
- ZEND_FETCH_DIM_R_SPEC_TMPVAR_CONST_HANDLER,
- ZEND_FETCH_DIM_R_SPEC_TMPVAR_TMPVAR_HANDLER,
- ZEND_FETCH_DIM_R_SPEC_TMPVAR_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_FETCH_DIM_R_SPEC_TMPVAR_CV_HANDLER,
- ZEND_FETCH_DIM_R_SPEC_TMPVAR_CONST_HANDLER,
- ZEND_FETCH_DIM_R_SPEC_TMPVAR_TMPVAR_HANDLER,
- ZEND_FETCH_DIM_R_SPEC_TMPVAR_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_FETCH_DIM_R_SPEC_TMPVAR_CV_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_FETCH_DIM_R_SPEC_CV_CONST_HANDLER,
- ZEND_FETCH_DIM_R_SPEC_CV_TMPVAR_HANDLER,
- ZEND_FETCH_DIM_R_SPEC_CV_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_FETCH_DIM_R_SPEC_CV_CV_HANDLER,
- ZEND_FETCH_OBJ_R_SPEC_CONST_CONST_HANDLER,
- ZEND_FETCH_OBJ_R_SPEC_CONST_TMPVAR_HANDLER,
- ZEND_FETCH_OBJ_R_SPEC_CONST_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_FETCH_OBJ_R_SPEC_CONST_CV_HANDLER,
- ZEND_FETCH_OBJ_R_SPEC_TMP_CONST_HANDLER,
- ZEND_FETCH_OBJ_R_SPEC_TMP_TMPVAR_HANDLER,
- ZEND_FETCH_OBJ_R_SPEC_TMP_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_FETCH_OBJ_R_SPEC_TMP_CV_HANDLER,
- ZEND_FETCH_OBJ_R_SPEC_VAR_CONST_HANDLER,
- ZEND_FETCH_OBJ_R_SPEC_VAR_TMPVAR_HANDLER,
- ZEND_FETCH_OBJ_R_SPEC_VAR_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_FETCH_OBJ_R_SPEC_VAR_CV_HANDLER,
- ZEND_FETCH_OBJ_R_SPEC_UNUSED_CONST_HANDLER,
- ZEND_FETCH_OBJ_R_SPEC_UNUSED_TMPVAR_HANDLER,
- ZEND_FETCH_OBJ_R_SPEC_UNUSED_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_FETCH_OBJ_R_SPEC_UNUSED_CV_HANDLER,
- ZEND_FETCH_OBJ_R_SPEC_CV_CONST_HANDLER,
- ZEND_FETCH_OBJ_R_SPEC_CV_TMPVAR_HANDLER,
- ZEND_FETCH_OBJ_R_SPEC_CV_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_FETCH_OBJ_R_SPEC_CV_CV_HANDLER,
- ZEND_FETCH_W_SPEC_CONST_CONST_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_FETCH_W_SPEC_CONST_VAR_HANDLER,
- ZEND_FETCH_W_SPEC_CONST_UNUSED_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_FETCH_W_SPEC_TMPVAR_CONST_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_FETCH_W_SPEC_TMPVAR_VAR_HANDLER,
- ZEND_FETCH_W_SPEC_TMPVAR_UNUSED_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_FETCH_W_SPEC_TMPVAR_CONST_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_FETCH_W_SPEC_TMPVAR_VAR_HANDLER,
- ZEND_FETCH_W_SPEC_TMPVAR_UNUSED_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_FETCH_W_SPEC_CV_CONST_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_FETCH_W_SPEC_CV_VAR_HANDLER,
- ZEND_FETCH_W_SPEC_CV_UNUSED_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_FETCH_DIM_W_SPEC_VAR_CONST_HANDLER,
- ZEND_FETCH_DIM_W_SPEC_VAR_TMPVAR_HANDLER,
- ZEND_FETCH_DIM_W_SPEC_VAR_TMPVAR_HANDLER,
- ZEND_FETCH_DIM_W_SPEC_VAR_UNUSED_HANDLER,
- ZEND_FETCH_DIM_W_SPEC_VAR_CV_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_FETCH_DIM_W_SPEC_CV_CONST_HANDLER,
- ZEND_FETCH_DIM_W_SPEC_CV_TMPVAR_HANDLER,
- ZEND_FETCH_DIM_W_SPEC_CV_TMPVAR_HANDLER,
- ZEND_FETCH_DIM_W_SPEC_CV_UNUSED_HANDLER,
- ZEND_FETCH_DIM_W_SPEC_CV_CV_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_FETCH_OBJ_W_SPEC_VAR_CONST_HANDLER,
- ZEND_FETCH_OBJ_W_SPEC_VAR_TMPVAR_HANDLER,
- ZEND_FETCH_OBJ_W_SPEC_VAR_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_FETCH_OBJ_W_SPEC_VAR_CV_HANDLER,
- ZEND_FETCH_OBJ_W_SPEC_UNUSED_CONST_HANDLER,
- ZEND_FETCH_OBJ_W_SPEC_UNUSED_TMPVAR_HANDLER,
- ZEND_FETCH_OBJ_W_SPEC_UNUSED_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_FETCH_OBJ_W_SPEC_UNUSED_CV_HANDLER,
- ZEND_FETCH_OBJ_W_SPEC_CV_CONST_HANDLER,
- ZEND_FETCH_OBJ_W_SPEC_CV_TMPVAR_HANDLER,
- ZEND_FETCH_OBJ_W_SPEC_CV_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_FETCH_OBJ_W_SPEC_CV_CV_HANDLER,
- ZEND_FETCH_RW_SPEC_CONST_CONST_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_FETCH_RW_SPEC_CONST_VAR_HANDLER,
- ZEND_FETCH_RW_SPEC_CONST_UNUSED_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_FETCH_RW_SPEC_TMPVAR_CONST_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_FETCH_RW_SPEC_TMPVAR_VAR_HANDLER,
- ZEND_FETCH_RW_SPEC_TMPVAR_UNUSED_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_FETCH_RW_SPEC_TMPVAR_CONST_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_FETCH_RW_SPEC_TMPVAR_VAR_HANDLER,
- ZEND_FETCH_RW_SPEC_TMPVAR_UNUSED_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_FETCH_RW_SPEC_CV_CONST_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_FETCH_RW_SPEC_CV_VAR_HANDLER,
- ZEND_FETCH_RW_SPEC_CV_UNUSED_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_FETCH_DIM_RW_SPEC_VAR_CONST_HANDLER,
- ZEND_FETCH_DIM_RW_SPEC_VAR_TMPVAR_HANDLER,
- ZEND_FETCH_DIM_RW_SPEC_VAR_TMPVAR_HANDLER,
- ZEND_FETCH_DIM_RW_SPEC_VAR_UNUSED_HANDLER,
- ZEND_FETCH_DIM_RW_SPEC_VAR_CV_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_FETCH_DIM_RW_SPEC_CV_CONST_HANDLER,
- ZEND_FETCH_DIM_RW_SPEC_CV_TMPVAR_HANDLER,
- ZEND_FETCH_DIM_RW_SPEC_CV_TMPVAR_HANDLER,
- ZEND_FETCH_DIM_RW_SPEC_CV_UNUSED_HANDLER,
- ZEND_FETCH_DIM_RW_SPEC_CV_CV_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_FETCH_OBJ_RW_SPEC_VAR_CONST_HANDLER,
- ZEND_FETCH_OBJ_RW_SPEC_VAR_TMPVAR_HANDLER,
- ZEND_FETCH_OBJ_RW_SPEC_VAR_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_FETCH_OBJ_RW_SPEC_VAR_CV_HANDLER,
- ZEND_FETCH_OBJ_RW_SPEC_UNUSED_CONST_HANDLER,
- ZEND_FETCH_OBJ_RW_SPEC_UNUSED_TMPVAR_HANDLER,
- ZEND_FETCH_OBJ_RW_SPEC_UNUSED_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_FETCH_OBJ_RW_SPEC_UNUSED_CV_HANDLER,
- ZEND_FETCH_OBJ_RW_SPEC_CV_CONST_HANDLER,
- ZEND_FETCH_OBJ_RW_SPEC_CV_TMPVAR_HANDLER,
- ZEND_FETCH_OBJ_RW_SPEC_CV_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_FETCH_OBJ_RW_SPEC_CV_CV_HANDLER,
- ZEND_FETCH_IS_SPEC_CONST_CONST_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_FETCH_IS_SPEC_CONST_VAR_HANDLER,
- ZEND_FETCH_IS_SPEC_CONST_UNUSED_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_FETCH_IS_SPEC_TMPVAR_CONST_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_FETCH_IS_SPEC_TMPVAR_VAR_HANDLER,
- ZEND_FETCH_IS_SPEC_TMPVAR_UNUSED_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_FETCH_IS_SPEC_TMPVAR_CONST_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_FETCH_IS_SPEC_TMPVAR_VAR_HANDLER,
- ZEND_FETCH_IS_SPEC_TMPVAR_UNUSED_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_FETCH_IS_SPEC_CV_CONST_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_FETCH_IS_SPEC_CV_VAR_HANDLER,
- ZEND_FETCH_IS_SPEC_CV_UNUSED_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_FETCH_DIM_IS_SPEC_CONST_CONST_HANDLER,
- ZEND_FETCH_DIM_IS_SPEC_CONST_TMPVAR_HANDLER,
- ZEND_FETCH_DIM_IS_SPEC_CONST_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_FETCH_DIM_IS_SPEC_CONST_CV_HANDLER,
- ZEND_FETCH_DIM_IS_SPEC_TMPVAR_CONST_HANDLER,
- ZEND_FETCH_DIM_IS_SPEC_TMPVAR_TMPVAR_HANDLER,
- ZEND_FETCH_DIM_IS_SPEC_TMPVAR_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_FETCH_DIM_IS_SPEC_TMPVAR_CV_HANDLER,
- ZEND_FETCH_DIM_IS_SPEC_TMPVAR_CONST_HANDLER,
- ZEND_FETCH_DIM_IS_SPEC_TMPVAR_TMPVAR_HANDLER,
- ZEND_FETCH_DIM_IS_SPEC_TMPVAR_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_FETCH_DIM_IS_SPEC_TMPVAR_CV_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_FETCH_DIM_IS_SPEC_CV_CONST_HANDLER,
- ZEND_FETCH_DIM_IS_SPEC_CV_TMPVAR_HANDLER,
- ZEND_FETCH_DIM_IS_SPEC_CV_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_FETCH_DIM_IS_SPEC_CV_CV_HANDLER,
- ZEND_FETCH_OBJ_IS_SPEC_CONST_CONST_HANDLER,
- ZEND_FETCH_OBJ_IS_SPEC_CONST_TMPVAR_HANDLER,
- ZEND_FETCH_OBJ_IS_SPEC_CONST_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_FETCH_OBJ_IS_SPEC_CONST_CV_HANDLER,
- ZEND_FETCH_OBJ_IS_SPEC_TMPVAR_CONST_HANDLER,
- ZEND_FETCH_OBJ_IS_SPEC_TMPVAR_TMPVAR_HANDLER,
- ZEND_FETCH_OBJ_IS_SPEC_TMPVAR_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_FETCH_OBJ_IS_SPEC_TMPVAR_CV_HANDLER,
- ZEND_FETCH_OBJ_IS_SPEC_TMPVAR_CONST_HANDLER,
- ZEND_FETCH_OBJ_IS_SPEC_TMPVAR_TMPVAR_HANDLER,
- ZEND_FETCH_OBJ_IS_SPEC_TMPVAR_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_FETCH_OBJ_IS_SPEC_TMPVAR_CV_HANDLER,
- ZEND_FETCH_OBJ_IS_SPEC_UNUSED_CONST_HANDLER,
- ZEND_FETCH_OBJ_IS_SPEC_UNUSED_TMPVAR_HANDLER,
- ZEND_FETCH_OBJ_IS_SPEC_UNUSED_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_FETCH_OBJ_IS_SPEC_UNUSED_CV_HANDLER,
- ZEND_FETCH_OBJ_IS_SPEC_CV_CONST_HANDLER,
- ZEND_FETCH_OBJ_IS_SPEC_CV_TMPVAR_HANDLER,
- ZEND_FETCH_OBJ_IS_SPEC_CV_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_FETCH_OBJ_IS_SPEC_CV_CV_HANDLER,
- ZEND_FETCH_FUNC_ARG_SPEC_CONST_CONST_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_FETCH_FUNC_ARG_SPEC_CONST_VAR_HANDLER,
- ZEND_FETCH_FUNC_ARG_SPEC_CONST_UNUSED_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_FETCH_FUNC_ARG_SPEC_TMPVAR_CONST_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_FETCH_FUNC_ARG_SPEC_TMPVAR_VAR_HANDLER,
- ZEND_FETCH_FUNC_ARG_SPEC_TMPVAR_UNUSED_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_FETCH_FUNC_ARG_SPEC_TMPVAR_CONST_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_FETCH_FUNC_ARG_SPEC_TMPVAR_VAR_HANDLER,
- ZEND_FETCH_FUNC_ARG_SPEC_TMPVAR_UNUSED_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_FETCH_FUNC_ARG_SPEC_CV_CONST_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_FETCH_FUNC_ARG_SPEC_CV_VAR_HANDLER,
- ZEND_FETCH_FUNC_ARG_SPEC_CV_UNUSED_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_FETCH_DIM_FUNC_ARG_SPEC_CONST_CONST_HANDLER,
- ZEND_FETCH_DIM_FUNC_ARG_SPEC_CONST_TMPVAR_HANDLER,
- ZEND_FETCH_DIM_FUNC_ARG_SPEC_CONST_TMPVAR_HANDLER,
- ZEND_FETCH_DIM_FUNC_ARG_SPEC_CONST_UNUSED_HANDLER,
- ZEND_FETCH_DIM_FUNC_ARG_SPEC_CONST_CV_HANDLER,
- ZEND_FETCH_DIM_FUNC_ARG_SPEC_TMP_CONST_HANDLER,
- ZEND_FETCH_DIM_FUNC_ARG_SPEC_TMP_TMPVAR_HANDLER,
- ZEND_FETCH_DIM_FUNC_ARG_SPEC_TMP_TMPVAR_HANDLER,
- ZEND_FETCH_DIM_FUNC_ARG_SPEC_TMP_UNUSED_HANDLER,
- ZEND_FETCH_DIM_FUNC_ARG_SPEC_TMP_CV_HANDLER,
- ZEND_FETCH_DIM_FUNC_ARG_SPEC_VAR_CONST_HANDLER,
- ZEND_FETCH_DIM_FUNC_ARG_SPEC_VAR_TMPVAR_HANDLER,
- ZEND_FETCH_DIM_FUNC_ARG_SPEC_VAR_TMPVAR_HANDLER,
- ZEND_FETCH_DIM_FUNC_ARG_SPEC_VAR_UNUSED_HANDLER,
- ZEND_FETCH_DIM_FUNC_ARG_SPEC_VAR_CV_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_FETCH_DIM_FUNC_ARG_SPEC_CV_CONST_HANDLER,
- ZEND_FETCH_DIM_FUNC_ARG_SPEC_CV_TMPVAR_HANDLER,
- ZEND_FETCH_DIM_FUNC_ARG_SPEC_CV_TMPVAR_HANDLER,
- ZEND_FETCH_DIM_FUNC_ARG_SPEC_CV_UNUSED_HANDLER,
- ZEND_FETCH_DIM_FUNC_ARG_SPEC_CV_CV_HANDLER,
- ZEND_FETCH_OBJ_FUNC_ARG_SPEC_CONST_CONST_HANDLER,
- ZEND_FETCH_OBJ_FUNC_ARG_SPEC_CONST_TMPVAR_HANDLER,
- ZEND_FETCH_OBJ_FUNC_ARG_SPEC_CONST_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_FETCH_OBJ_FUNC_ARG_SPEC_CONST_CV_HANDLER,
- ZEND_FETCH_OBJ_FUNC_ARG_SPEC_TMP_CONST_HANDLER,
- ZEND_FETCH_OBJ_FUNC_ARG_SPEC_TMP_TMPVAR_HANDLER,
- ZEND_FETCH_OBJ_FUNC_ARG_SPEC_TMP_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_FETCH_OBJ_FUNC_ARG_SPEC_TMP_CV_HANDLER,
- ZEND_FETCH_OBJ_FUNC_ARG_SPEC_VAR_CONST_HANDLER,
- ZEND_FETCH_OBJ_FUNC_ARG_SPEC_VAR_TMPVAR_HANDLER,
- ZEND_FETCH_OBJ_FUNC_ARG_SPEC_VAR_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_FETCH_OBJ_FUNC_ARG_SPEC_VAR_CV_HANDLER,
- ZEND_FETCH_OBJ_FUNC_ARG_SPEC_UNUSED_CONST_HANDLER,
- ZEND_FETCH_OBJ_FUNC_ARG_SPEC_UNUSED_TMPVAR_HANDLER,
- ZEND_FETCH_OBJ_FUNC_ARG_SPEC_UNUSED_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_FETCH_OBJ_FUNC_ARG_SPEC_UNUSED_CV_HANDLER,
- ZEND_FETCH_OBJ_FUNC_ARG_SPEC_CV_CONST_HANDLER,
- ZEND_FETCH_OBJ_FUNC_ARG_SPEC_CV_TMPVAR_HANDLER,
- ZEND_FETCH_OBJ_FUNC_ARG_SPEC_CV_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_FETCH_OBJ_FUNC_ARG_SPEC_CV_CV_HANDLER,
- ZEND_FETCH_UNSET_SPEC_CONST_CONST_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_FETCH_UNSET_SPEC_CONST_VAR_HANDLER,
- ZEND_FETCH_UNSET_SPEC_CONST_UNUSED_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_FETCH_UNSET_SPEC_TMPVAR_CONST_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_FETCH_UNSET_SPEC_TMPVAR_VAR_HANDLER,
- ZEND_FETCH_UNSET_SPEC_TMPVAR_UNUSED_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_FETCH_UNSET_SPEC_TMPVAR_CONST_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_FETCH_UNSET_SPEC_TMPVAR_VAR_HANDLER,
- ZEND_FETCH_UNSET_SPEC_TMPVAR_UNUSED_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_FETCH_UNSET_SPEC_CV_CONST_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_FETCH_UNSET_SPEC_CV_VAR_HANDLER,
- ZEND_FETCH_UNSET_SPEC_CV_UNUSED_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_FETCH_DIM_UNSET_SPEC_VAR_CONST_HANDLER,
- ZEND_FETCH_DIM_UNSET_SPEC_VAR_TMPVAR_HANDLER,
- ZEND_FETCH_DIM_UNSET_SPEC_VAR_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_FETCH_DIM_UNSET_SPEC_VAR_CV_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_FETCH_DIM_UNSET_SPEC_CV_CONST_HANDLER,
- ZEND_FETCH_DIM_UNSET_SPEC_CV_TMPVAR_HANDLER,
- ZEND_FETCH_DIM_UNSET_SPEC_CV_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_FETCH_DIM_UNSET_SPEC_CV_CV_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_FETCH_OBJ_UNSET_SPEC_VAR_CONST_HANDLER,
- ZEND_FETCH_OBJ_UNSET_SPEC_VAR_TMPVAR_HANDLER,
- ZEND_FETCH_OBJ_UNSET_SPEC_VAR_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_FETCH_OBJ_UNSET_SPEC_VAR_CV_HANDLER,
- ZEND_FETCH_OBJ_UNSET_SPEC_UNUSED_CONST_HANDLER,
- ZEND_FETCH_OBJ_UNSET_SPEC_UNUSED_TMPVAR_HANDLER,
- ZEND_FETCH_OBJ_UNSET_SPEC_UNUSED_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_FETCH_OBJ_UNSET_SPEC_UNUSED_CV_HANDLER,
- ZEND_FETCH_OBJ_UNSET_SPEC_CV_CONST_HANDLER,
- ZEND_FETCH_OBJ_UNSET_SPEC_CV_TMPVAR_HANDLER,
- ZEND_FETCH_OBJ_UNSET_SPEC_CV_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_FETCH_OBJ_UNSET_SPEC_CV_CV_HANDLER,
- ZEND_FETCH_LIST_SPEC_CONST_CONST_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_FETCH_LIST_SPEC_TMPVAR_CONST_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_FETCH_LIST_SPEC_TMPVAR_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_FETCH_LIST_SPEC_CV_CONST_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_FETCH_CONSTANT_SPEC_CONST_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_FETCH_CONSTANT_SPEC_VAR_CONST_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_FETCH_CONSTANT_SPEC_UNUSED_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,
- 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,
- 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_EXT_STMT_SPEC_HANDLER,
- ZEND_EXT_STMT_SPEC_HANDLER,
- ZEND_EXT_STMT_SPEC_HANDLER,
- ZEND_EXT_STMT_SPEC_HANDLER,
- ZEND_EXT_STMT_SPEC_HANDLER,
- ZEND_EXT_STMT_SPEC_HANDLER,
- ZEND_EXT_STMT_SPEC_HANDLER,
- ZEND_EXT_STMT_SPEC_HANDLER,
- ZEND_EXT_STMT_SPEC_HANDLER,
- ZEND_EXT_STMT_SPEC_HANDLER,
- ZEND_EXT_STMT_SPEC_HANDLER,
- ZEND_EXT_STMT_SPEC_HANDLER,
- ZEND_EXT_STMT_SPEC_HANDLER,
- ZEND_EXT_STMT_SPEC_HANDLER,
- ZEND_EXT_STMT_SPEC_HANDLER,
- ZEND_EXT_STMT_SPEC_HANDLER,
- ZEND_EXT_STMT_SPEC_HANDLER,
- ZEND_EXT_STMT_SPEC_HANDLER,
- ZEND_EXT_STMT_SPEC_HANDLER,
- ZEND_EXT_STMT_SPEC_HANDLER,
- ZEND_EXT_STMT_SPEC_HANDLER,
- ZEND_EXT_STMT_SPEC_HANDLER,
- ZEND_EXT_STMT_SPEC_HANDLER,
- ZEND_EXT_STMT_SPEC_HANDLER,
- ZEND_EXT_STMT_SPEC_HANDLER,
- ZEND_EXT_FCALL_BEGIN_SPEC_HANDLER,
- ZEND_EXT_FCALL_BEGIN_SPEC_HANDLER,
- ZEND_EXT_FCALL_BEGIN_SPEC_HANDLER,
- ZEND_EXT_FCALL_BEGIN_SPEC_HANDLER,
- ZEND_EXT_FCALL_BEGIN_SPEC_HANDLER,
- ZEND_EXT_FCALL_BEGIN_SPEC_HANDLER,
- ZEND_EXT_FCALL_BEGIN_SPEC_HANDLER,
- ZEND_EXT_FCALL_BEGIN_SPEC_HANDLER,
- ZEND_EXT_FCALL_BEGIN_SPEC_HANDLER,
- ZEND_EXT_FCALL_BEGIN_SPEC_HANDLER,
- ZEND_EXT_FCALL_BEGIN_SPEC_HANDLER,
- ZEND_EXT_FCALL_BEGIN_SPEC_HANDLER,
- ZEND_EXT_FCALL_BEGIN_SPEC_HANDLER,
- ZEND_EXT_FCALL_BEGIN_SPEC_HANDLER,
- ZEND_EXT_FCALL_BEGIN_SPEC_HANDLER,
- ZEND_EXT_FCALL_BEGIN_SPEC_HANDLER,
- ZEND_EXT_FCALL_BEGIN_SPEC_HANDLER,
- ZEND_EXT_FCALL_BEGIN_SPEC_HANDLER,
- ZEND_EXT_FCALL_BEGIN_SPEC_HANDLER,
- ZEND_EXT_FCALL_BEGIN_SPEC_HANDLER,
- ZEND_EXT_FCALL_BEGIN_SPEC_HANDLER,
- ZEND_EXT_FCALL_BEGIN_SPEC_HANDLER,
- ZEND_EXT_FCALL_BEGIN_SPEC_HANDLER,
- ZEND_EXT_FCALL_BEGIN_SPEC_HANDLER,
- ZEND_EXT_FCALL_BEGIN_SPEC_HANDLER,
- ZEND_EXT_FCALL_END_SPEC_HANDLER,
- ZEND_EXT_FCALL_END_SPEC_HANDLER,
- ZEND_EXT_FCALL_END_SPEC_HANDLER,
- ZEND_EXT_FCALL_END_SPEC_HANDLER,
- ZEND_EXT_FCALL_END_SPEC_HANDLER,
- ZEND_EXT_FCALL_END_SPEC_HANDLER,
- ZEND_EXT_FCALL_END_SPEC_HANDLER,
- ZEND_EXT_FCALL_END_SPEC_HANDLER,
- ZEND_EXT_FCALL_END_SPEC_HANDLER,
- ZEND_EXT_FCALL_END_SPEC_HANDLER,
- ZEND_EXT_FCALL_END_SPEC_HANDLER,
- ZEND_EXT_FCALL_END_SPEC_HANDLER,
- ZEND_EXT_FCALL_END_SPEC_HANDLER,
- ZEND_EXT_FCALL_END_SPEC_HANDLER,
- ZEND_EXT_FCALL_END_SPEC_HANDLER,
- ZEND_EXT_FCALL_END_SPEC_HANDLER,
- ZEND_EXT_FCALL_END_SPEC_HANDLER,
- ZEND_EXT_FCALL_END_SPEC_HANDLER,
- ZEND_EXT_FCALL_END_SPEC_HANDLER,
- ZEND_EXT_FCALL_END_SPEC_HANDLER,
- ZEND_EXT_FCALL_END_SPEC_HANDLER,
- ZEND_EXT_FCALL_END_SPEC_HANDLER,
- ZEND_EXT_FCALL_END_SPEC_HANDLER,
- ZEND_EXT_FCALL_END_SPEC_HANDLER,
- ZEND_EXT_FCALL_END_SPEC_HANDLER,
- ZEND_EXT_NOP_SPEC_HANDLER,
- ZEND_EXT_NOP_SPEC_HANDLER,
- ZEND_EXT_NOP_SPEC_HANDLER,
- ZEND_EXT_NOP_SPEC_HANDLER,
- ZEND_EXT_NOP_SPEC_HANDLER,
- ZEND_EXT_NOP_SPEC_HANDLER,
- ZEND_EXT_NOP_SPEC_HANDLER,
- ZEND_EXT_NOP_SPEC_HANDLER,
- ZEND_EXT_NOP_SPEC_HANDLER,
- ZEND_EXT_NOP_SPEC_HANDLER,
- ZEND_EXT_NOP_SPEC_HANDLER,
- ZEND_EXT_NOP_SPEC_HANDLER,
- ZEND_EXT_NOP_SPEC_HANDLER,
- ZEND_EXT_NOP_SPEC_HANDLER,
- ZEND_EXT_NOP_SPEC_HANDLER,
- ZEND_EXT_NOP_SPEC_HANDLER,
- ZEND_EXT_NOP_SPEC_HANDLER,
- ZEND_EXT_NOP_SPEC_HANDLER,
- ZEND_EXT_NOP_SPEC_HANDLER,
- ZEND_EXT_NOP_SPEC_HANDLER,
- ZEND_EXT_NOP_SPEC_HANDLER,
- ZEND_EXT_NOP_SPEC_HANDLER,
- ZEND_EXT_NOP_SPEC_HANDLER,
- ZEND_EXT_NOP_SPEC_HANDLER,
- ZEND_EXT_NOP_SPEC_HANDLER,
- ZEND_TICKS_SPEC_HANDLER,
- ZEND_TICKS_SPEC_HANDLER,
- ZEND_TICKS_SPEC_HANDLER,
- ZEND_TICKS_SPEC_HANDLER,
- ZEND_TICKS_SPEC_HANDLER,
- ZEND_TICKS_SPEC_HANDLER,
- ZEND_TICKS_SPEC_HANDLER,
- ZEND_TICKS_SPEC_HANDLER,
- ZEND_TICKS_SPEC_HANDLER,
- ZEND_TICKS_SPEC_HANDLER,
- ZEND_TICKS_SPEC_HANDLER,
- ZEND_TICKS_SPEC_HANDLER,
- ZEND_TICKS_SPEC_HANDLER,
- ZEND_TICKS_SPEC_HANDLER,
- ZEND_TICKS_SPEC_HANDLER,
- ZEND_TICKS_SPEC_HANDLER,
- ZEND_TICKS_SPEC_HANDLER,
- ZEND_TICKS_SPEC_HANDLER,
- ZEND_TICKS_SPEC_HANDLER,
- ZEND_TICKS_SPEC_HANDLER,
- ZEND_TICKS_SPEC_HANDLER,
- ZEND_TICKS_SPEC_HANDLER,
- ZEND_TICKS_SPEC_HANDLER,
- ZEND_TICKS_SPEC_HANDLER,
- ZEND_TICKS_SPEC_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_SEND_VAR_NO_REF_SPEC_VAR_HANDLER,
- ZEND_SEND_VAR_NO_REF_SPEC_VAR_HANDLER,
- ZEND_SEND_VAR_NO_REF_SPEC_VAR_HANDLER,
- ZEND_SEND_VAR_NO_REF_SPEC_VAR_HANDLER,
- ZEND_SEND_VAR_NO_REF_SPEC_VAR_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,
- ZEND_NULL_HANDLER,
- ZEND_CATCH_SPEC_CONST_CV_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,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_THROW_SPEC_CONST_HANDLER,
- ZEND_THROW_SPEC_CONST_HANDLER,
- ZEND_THROW_SPEC_CONST_HANDLER,
- ZEND_THROW_SPEC_CONST_HANDLER,
- ZEND_THROW_SPEC_CONST_HANDLER,
- ZEND_THROW_SPEC_TMP_HANDLER,
- ZEND_THROW_SPEC_TMP_HANDLER,
- ZEND_THROW_SPEC_TMP_HANDLER,
- ZEND_THROW_SPEC_TMP_HANDLER,
- ZEND_THROW_SPEC_TMP_HANDLER,
- ZEND_THROW_SPEC_VAR_HANDLER,
- ZEND_THROW_SPEC_VAR_HANDLER,
- ZEND_THROW_SPEC_VAR_HANDLER,
- ZEND_THROW_SPEC_VAR_HANDLER,
- ZEND_THROW_SPEC_VAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_THROW_SPEC_CV_HANDLER,
- ZEND_THROW_SPEC_CV_HANDLER,
- ZEND_THROW_SPEC_CV_HANDLER,
- ZEND_THROW_SPEC_CV_HANDLER,
- ZEND_THROW_SPEC_CV_HANDLER,
- ZEND_FETCH_CLASS_SPEC_CONST_HANDLER,
- ZEND_FETCH_CLASS_SPEC_TMPVAR_HANDLER,
- ZEND_FETCH_CLASS_SPEC_TMPVAR_HANDLER,
- ZEND_FETCH_CLASS_SPEC_UNUSED_HANDLER,
- ZEND_FETCH_CLASS_SPEC_CV_HANDLER,
- ZEND_FETCH_CLASS_SPEC_CONST_HANDLER,
- ZEND_FETCH_CLASS_SPEC_TMPVAR_HANDLER,
- ZEND_FETCH_CLASS_SPEC_TMPVAR_HANDLER,
- ZEND_FETCH_CLASS_SPEC_UNUSED_HANDLER,
- ZEND_FETCH_CLASS_SPEC_CV_HANDLER,
- ZEND_FETCH_CLASS_SPEC_CONST_HANDLER,
- ZEND_FETCH_CLASS_SPEC_TMPVAR_HANDLER,
- ZEND_FETCH_CLASS_SPEC_TMPVAR_HANDLER,
- ZEND_FETCH_CLASS_SPEC_UNUSED_HANDLER,
- ZEND_FETCH_CLASS_SPEC_CV_HANDLER,
- ZEND_FETCH_CLASS_SPEC_CONST_HANDLER,
- ZEND_FETCH_CLASS_SPEC_TMPVAR_HANDLER,
- ZEND_FETCH_CLASS_SPEC_TMPVAR_HANDLER,
- ZEND_FETCH_CLASS_SPEC_UNUSED_HANDLER,
- ZEND_FETCH_CLASS_SPEC_CV_HANDLER,
- ZEND_FETCH_CLASS_SPEC_CONST_HANDLER,
- ZEND_FETCH_CLASS_SPEC_TMPVAR_HANDLER,
- ZEND_FETCH_CLASS_SPEC_TMPVAR_HANDLER,
- ZEND_FETCH_CLASS_SPEC_UNUSED_HANDLER,
- ZEND_FETCH_CLASS_SPEC_CV_HANDLER,
- ZEND_CLONE_SPEC_CONST_HANDLER,
- ZEND_CLONE_SPEC_CONST_HANDLER,
- ZEND_CLONE_SPEC_CONST_HANDLER,
- ZEND_CLONE_SPEC_CONST_HANDLER,
- ZEND_CLONE_SPEC_CONST_HANDLER,
- ZEND_CLONE_SPEC_TMPVAR_HANDLER,
- ZEND_CLONE_SPEC_TMPVAR_HANDLER,
- ZEND_CLONE_SPEC_TMPVAR_HANDLER,
- ZEND_CLONE_SPEC_TMPVAR_HANDLER,
- ZEND_CLONE_SPEC_TMPVAR_HANDLER,
- ZEND_CLONE_SPEC_TMPVAR_HANDLER,
- ZEND_CLONE_SPEC_TMPVAR_HANDLER,
- ZEND_CLONE_SPEC_TMPVAR_HANDLER,
- ZEND_CLONE_SPEC_TMPVAR_HANDLER,
- ZEND_CLONE_SPEC_TMPVAR_HANDLER,
- ZEND_CLONE_SPEC_UNUSED_HANDLER,
- ZEND_CLONE_SPEC_UNUSED_HANDLER,
- ZEND_CLONE_SPEC_UNUSED_HANDLER,
- ZEND_CLONE_SPEC_UNUSED_HANDLER,
- ZEND_CLONE_SPEC_UNUSED_HANDLER,
- ZEND_CLONE_SPEC_CV_HANDLER,
- ZEND_CLONE_SPEC_CV_HANDLER,
- ZEND_CLONE_SPEC_CV_HANDLER,
- ZEND_CLONE_SPEC_CV_HANDLER,
- ZEND_CLONE_SPEC_CV_HANDLER,
- ZEND_RETURN_BY_REF_SPEC_CONST_HANDLER,
- ZEND_RETURN_BY_REF_SPEC_CONST_HANDLER,
- ZEND_RETURN_BY_REF_SPEC_CONST_HANDLER,
- ZEND_RETURN_BY_REF_SPEC_CONST_HANDLER,
- ZEND_RETURN_BY_REF_SPEC_CONST_HANDLER,
- ZEND_RETURN_BY_REF_SPEC_TMP_HANDLER,
- ZEND_RETURN_BY_REF_SPEC_TMP_HANDLER,
- ZEND_RETURN_BY_REF_SPEC_TMP_HANDLER,
- ZEND_RETURN_BY_REF_SPEC_TMP_HANDLER,
- ZEND_RETURN_BY_REF_SPEC_TMP_HANDLER,
- ZEND_RETURN_BY_REF_SPEC_VAR_HANDLER,
- ZEND_RETURN_BY_REF_SPEC_VAR_HANDLER,
- ZEND_RETURN_BY_REF_SPEC_VAR_HANDLER,
- ZEND_RETURN_BY_REF_SPEC_VAR_HANDLER,
- ZEND_RETURN_BY_REF_SPEC_VAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_RETURN_BY_REF_SPEC_CV_HANDLER,
- ZEND_RETURN_BY_REF_SPEC_CV_HANDLER,
- ZEND_RETURN_BY_REF_SPEC_CV_HANDLER,
- ZEND_RETURN_BY_REF_SPEC_CV_HANDLER,
- ZEND_RETURN_BY_REF_SPEC_CV_HANDLER,
- ZEND_INIT_METHOD_CALL_SPEC_CONST_CONST_HANDLER,
- ZEND_INIT_METHOD_CALL_SPEC_CONST_TMPVAR_HANDLER,
- ZEND_INIT_METHOD_CALL_SPEC_CONST_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_INIT_METHOD_CALL_SPEC_CONST_CV_HANDLER,
- ZEND_INIT_METHOD_CALL_SPEC_TMPVAR_CONST_HANDLER,
- ZEND_INIT_METHOD_CALL_SPEC_TMPVAR_TMPVAR_HANDLER,
- ZEND_INIT_METHOD_CALL_SPEC_TMPVAR_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_INIT_METHOD_CALL_SPEC_TMPVAR_CV_HANDLER,
- ZEND_INIT_METHOD_CALL_SPEC_TMPVAR_CONST_HANDLER,
- ZEND_INIT_METHOD_CALL_SPEC_TMPVAR_TMPVAR_HANDLER,
- ZEND_INIT_METHOD_CALL_SPEC_TMPVAR_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_INIT_METHOD_CALL_SPEC_TMPVAR_CV_HANDLER,
- ZEND_INIT_METHOD_CALL_SPEC_UNUSED_CONST_HANDLER,
- ZEND_INIT_METHOD_CALL_SPEC_UNUSED_TMPVAR_HANDLER,
- ZEND_INIT_METHOD_CALL_SPEC_UNUSED_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_INIT_METHOD_CALL_SPEC_UNUSED_CV_HANDLER,
- ZEND_INIT_METHOD_CALL_SPEC_CV_CONST_HANDLER,
- ZEND_INIT_METHOD_CALL_SPEC_CV_TMPVAR_HANDLER,
- ZEND_INIT_METHOD_CALL_SPEC_CV_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_INIT_METHOD_CALL_SPEC_CV_CV_HANDLER,
- ZEND_INIT_STATIC_METHOD_CALL_SPEC_CONST_CONST_HANDLER,
- ZEND_INIT_STATIC_METHOD_CALL_SPEC_CONST_TMPVAR_HANDLER,
- ZEND_INIT_STATIC_METHOD_CALL_SPEC_CONST_TMPVAR_HANDLER,
- ZEND_INIT_STATIC_METHOD_CALL_SPEC_CONST_UNUSED_HANDLER,
- ZEND_INIT_STATIC_METHOD_CALL_SPEC_CONST_CV_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_INIT_STATIC_METHOD_CALL_SPEC_VAR_CONST_HANDLER,
- ZEND_INIT_STATIC_METHOD_CALL_SPEC_VAR_TMPVAR_HANDLER,
- ZEND_INIT_STATIC_METHOD_CALL_SPEC_VAR_TMPVAR_HANDLER,
- ZEND_INIT_STATIC_METHOD_CALL_SPEC_VAR_UNUSED_HANDLER,
- ZEND_INIT_STATIC_METHOD_CALL_SPEC_VAR_CV_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_ISSET_ISEMPTY_VAR_SPEC_CONST_CONST_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_ISSET_ISEMPTY_VAR_SPEC_CONST_VAR_HANDLER,
- ZEND_ISSET_ISEMPTY_VAR_SPEC_CONST_UNUSED_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_ISSET_ISEMPTY_VAR_SPEC_TMPVAR_CONST_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_ISSET_ISEMPTY_VAR_SPEC_TMPVAR_VAR_HANDLER,
- ZEND_ISSET_ISEMPTY_VAR_SPEC_TMPVAR_UNUSED_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_ISSET_ISEMPTY_VAR_SPEC_TMPVAR_CONST_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_ISSET_ISEMPTY_VAR_SPEC_TMPVAR_VAR_HANDLER,
- ZEND_ISSET_ISEMPTY_VAR_SPEC_TMPVAR_UNUSED_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_ISSET_ISEMPTY_VAR_SPEC_CV_CONST_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_ISSET_ISEMPTY_VAR_SPEC_CV_VAR_HANDLER,
- ZEND_ISSET_ISEMPTY_VAR_SPEC_CV_UNUSED_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_ISSET_ISEMPTY_DIM_OBJ_SPEC_CONST_CONST_HANDLER,
- ZEND_ISSET_ISEMPTY_DIM_OBJ_SPEC_CONST_TMPVAR_HANDLER,
- ZEND_ISSET_ISEMPTY_DIM_OBJ_SPEC_CONST_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_ISSET_ISEMPTY_DIM_OBJ_SPEC_CONST_CV_HANDLER,
- ZEND_ISSET_ISEMPTY_DIM_OBJ_SPEC_TMPVAR_CONST_HANDLER,
- ZEND_ISSET_ISEMPTY_DIM_OBJ_SPEC_TMPVAR_TMPVAR_HANDLER,
- ZEND_ISSET_ISEMPTY_DIM_OBJ_SPEC_TMPVAR_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_ISSET_ISEMPTY_DIM_OBJ_SPEC_TMPVAR_CV_HANDLER,
- ZEND_ISSET_ISEMPTY_DIM_OBJ_SPEC_TMPVAR_CONST_HANDLER,
- ZEND_ISSET_ISEMPTY_DIM_OBJ_SPEC_TMPVAR_TMPVAR_HANDLER,
- ZEND_ISSET_ISEMPTY_DIM_OBJ_SPEC_TMPVAR_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_ISSET_ISEMPTY_DIM_OBJ_SPEC_TMPVAR_CV_HANDLER,
- ZEND_ISSET_ISEMPTY_DIM_OBJ_SPEC_UNUSED_CONST_HANDLER,
- ZEND_ISSET_ISEMPTY_DIM_OBJ_SPEC_UNUSED_TMPVAR_HANDLER,
- ZEND_ISSET_ISEMPTY_DIM_OBJ_SPEC_UNUSED_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_ISSET_ISEMPTY_DIM_OBJ_SPEC_UNUSED_CV_HANDLER,
- ZEND_ISSET_ISEMPTY_DIM_OBJ_SPEC_CV_CONST_HANDLER,
- ZEND_ISSET_ISEMPTY_DIM_OBJ_SPEC_CV_TMPVAR_HANDLER,
- ZEND_ISSET_ISEMPTY_DIM_OBJ_SPEC_CV_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_ISSET_ISEMPTY_DIM_OBJ_SPEC_CV_CV_HANDLER,
- ZEND_SEND_VAL_EX_SPEC_CONST_HANDLER,
- ZEND_SEND_VAL_EX_SPEC_CONST_HANDLER,
- ZEND_SEND_VAL_EX_SPEC_CONST_HANDLER,
- ZEND_SEND_VAL_EX_SPEC_CONST_HANDLER,
- ZEND_SEND_VAL_EX_SPEC_CONST_HANDLER,
- ZEND_SEND_VAL_EX_SPEC_TMP_HANDLER,
- ZEND_SEND_VAL_EX_SPEC_TMP_HANDLER,
- ZEND_SEND_VAL_EX_SPEC_TMP_HANDLER,
- ZEND_SEND_VAL_EX_SPEC_TMP_HANDLER,
- ZEND_SEND_VAL_EX_SPEC_TMP_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,
- 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_SEND_VAR_SPEC_VAR_HANDLER,
- ZEND_SEND_VAR_SPEC_VAR_HANDLER,
- ZEND_SEND_VAR_SPEC_VAR_HANDLER,
- ZEND_SEND_VAR_SPEC_VAR_HANDLER,
- ZEND_SEND_VAR_SPEC_VAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_SEND_VAR_SPEC_CV_HANDLER,
- ZEND_SEND_VAR_SPEC_CV_HANDLER,
- ZEND_SEND_VAR_SPEC_CV_HANDLER,
- ZEND_SEND_VAR_SPEC_CV_HANDLER,
- ZEND_SEND_VAR_SPEC_CV_HANDLER,
- ZEND_INIT_USER_CALL_SPEC_CONST_CONST_HANDLER,
- ZEND_INIT_USER_CALL_SPEC_CONST_TMPVAR_HANDLER,
- ZEND_INIT_USER_CALL_SPEC_CONST_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_INIT_USER_CALL_SPEC_CONST_CV_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,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_SEND_ARRAY_SPEC_HANDLER,
- ZEND_SEND_ARRAY_SPEC_HANDLER,
- ZEND_SEND_ARRAY_SPEC_HANDLER,
- ZEND_SEND_ARRAY_SPEC_HANDLER,
- ZEND_SEND_ARRAY_SPEC_HANDLER,
- ZEND_SEND_ARRAY_SPEC_HANDLER,
- ZEND_SEND_ARRAY_SPEC_HANDLER,
- ZEND_SEND_ARRAY_SPEC_HANDLER,
- ZEND_SEND_ARRAY_SPEC_HANDLER,
- ZEND_SEND_ARRAY_SPEC_HANDLER,
- ZEND_SEND_ARRAY_SPEC_HANDLER,
- ZEND_SEND_ARRAY_SPEC_HANDLER,
- ZEND_SEND_ARRAY_SPEC_HANDLER,
- ZEND_SEND_ARRAY_SPEC_HANDLER,
- ZEND_SEND_ARRAY_SPEC_HANDLER,
- ZEND_SEND_ARRAY_SPEC_HANDLER,
- ZEND_SEND_ARRAY_SPEC_HANDLER,
- ZEND_SEND_ARRAY_SPEC_HANDLER,
- ZEND_SEND_ARRAY_SPEC_HANDLER,
- ZEND_SEND_ARRAY_SPEC_HANDLER,
- ZEND_SEND_ARRAY_SPEC_HANDLER,
- ZEND_SEND_ARRAY_SPEC_HANDLER,
- ZEND_SEND_ARRAY_SPEC_HANDLER,
- ZEND_SEND_ARRAY_SPEC_HANDLER,
- ZEND_SEND_ARRAY_SPEC_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_SEND_USER_SPEC_VAR_HANDLER,
- ZEND_SEND_USER_SPEC_VAR_HANDLER,
- ZEND_SEND_USER_SPEC_VAR_HANDLER,
- ZEND_SEND_USER_SPEC_VAR_HANDLER,
- ZEND_SEND_USER_SPEC_VAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_SEND_USER_SPEC_CV_HANDLER,
- ZEND_SEND_USER_SPEC_CV_HANDLER,
- ZEND_SEND_USER_SPEC_CV_HANDLER,
- ZEND_SEND_USER_SPEC_CV_HANDLER,
- ZEND_SEND_USER_SPEC_CV_HANDLER,
- ZEND_STRLEN_SPEC_CONST_HANDLER,
- ZEND_STRLEN_SPEC_CONST_HANDLER,
- ZEND_STRLEN_SPEC_CONST_HANDLER,
- ZEND_STRLEN_SPEC_CONST_HANDLER,
- ZEND_STRLEN_SPEC_CONST_HANDLER,
- ZEND_STRLEN_SPEC_TMPVAR_HANDLER,
- ZEND_STRLEN_SPEC_TMPVAR_HANDLER,
- ZEND_STRLEN_SPEC_TMPVAR_HANDLER,
- ZEND_STRLEN_SPEC_TMPVAR_HANDLER,
- ZEND_STRLEN_SPEC_TMPVAR_HANDLER,
- ZEND_STRLEN_SPEC_TMPVAR_HANDLER,
- ZEND_STRLEN_SPEC_TMPVAR_HANDLER,
- ZEND_STRLEN_SPEC_TMPVAR_HANDLER,
- ZEND_STRLEN_SPEC_TMPVAR_HANDLER,
- ZEND_STRLEN_SPEC_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_STRLEN_SPEC_CV_HANDLER,
- ZEND_STRLEN_SPEC_CV_HANDLER,
- ZEND_STRLEN_SPEC_CV_HANDLER,
- ZEND_STRLEN_SPEC_CV_HANDLER,
- ZEND_STRLEN_SPEC_CV_HANDLER,
- ZEND_DEFINED_SPEC_CONST_HANDLER,
- ZEND_DEFINED_SPEC_CONST_HANDLER,
- ZEND_DEFINED_SPEC_CONST_HANDLER,
- ZEND_DEFINED_SPEC_CONST_HANDLER,
- ZEND_DEFINED_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,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_TYPE_CHECK_SPEC_CONST_HANDLER,
- ZEND_TYPE_CHECK_SPEC_CONST_HANDLER,
- ZEND_TYPE_CHECK_SPEC_CONST_HANDLER,
- ZEND_TYPE_CHECK_SPEC_CONST_HANDLER,
- ZEND_TYPE_CHECK_SPEC_CONST_HANDLER,
- ZEND_TYPE_CHECK_SPEC_TMP_HANDLER,
- ZEND_TYPE_CHECK_SPEC_TMP_HANDLER,
- ZEND_TYPE_CHECK_SPEC_TMP_HANDLER,
- ZEND_TYPE_CHECK_SPEC_TMP_HANDLER,
- ZEND_TYPE_CHECK_SPEC_TMP_HANDLER,
- ZEND_TYPE_CHECK_SPEC_VAR_HANDLER,
- ZEND_TYPE_CHECK_SPEC_VAR_HANDLER,
- ZEND_TYPE_CHECK_SPEC_VAR_HANDLER,
- ZEND_TYPE_CHECK_SPEC_VAR_HANDLER,
- ZEND_TYPE_CHECK_SPEC_VAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_TYPE_CHECK_SPEC_CV_HANDLER,
- ZEND_TYPE_CHECK_SPEC_CV_HANDLER,
- ZEND_TYPE_CHECK_SPEC_CV_HANDLER,
- ZEND_TYPE_CHECK_SPEC_CV_HANDLER,
- ZEND_TYPE_CHECK_SPEC_CV_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_VERIFY_RETURN_TYPE_SPEC_CONST_UNUSED_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_VERIFY_RETURN_TYPE_SPEC_TMP_UNUSED_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_VERIFY_RETURN_TYPE_SPEC_VAR_UNUSED_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_VERIFY_RETURN_TYPE_SPEC_UNUSED_UNUSED_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_VERIFY_RETURN_TYPE_SPEC_CV_UNUSED_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_FE_RESET_RW_SPEC_CONST_HANDLER,
- ZEND_FE_RESET_RW_SPEC_CONST_HANDLER,
- ZEND_FE_RESET_RW_SPEC_CONST_HANDLER,
- ZEND_FE_RESET_RW_SPEC_CONST_HANDLER,
- ZEND_FE_RESET_RW_SPEC_CONST_HANDLER,
- ZEND_FE_RESET_RW_SPEC_TMP_HANDLER,
- ZEND_FE_RESET_RW_SPEC_TMP_HANDLER,
- ZEND_FE_RESET_RW_SPEC_TMP_HANDLER,
- ZEND_FE_RESET_RW_SPEC_TMP_HANDLER,
- ZEND_FE_RESET_RW_SPEC_TMP_HANDLER,
- ZEND_FE_RESET_RW_SPEC_VAR_HANDLER,
- ZEND_FE_RESET_RW_SPEC_VAR_HANDLER,
- ZEND_FE_RESET_RW_SPEC_VAR_HANDLER,
- ZEND_FE_RESET_RW_SPEC_VAR_HANDLER,
- ZEND_FE_RESET_RW_SPEC_VAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_FE_RESET_RW_SPEC_CV_HANDLER,
- ZEND_FE_RESET_RW_SPEC_CV_HANDLER,
- ZEND_FE_RESET_RW_SPEC_CV_HANDLER,
- ZEND_FE_RESET_RW_SPEC_CV_HANDLER,
- ZEND_FE_RESET_RW_SPEC_CV_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_FE_FETCH_RW_SPEC_VAR_HANDLER,
- ZEND_FE_FETCH_RW_SPEC_VAR_HANDLER,
- ZEND_FE_FETCH_RW_SPEC_VAR_HANDLER,
- ZEND_FE_FETCH_RW_SPEC_VAR_HANDLER,
- ZEND_FE_FETCH_RW_SPEC_VAR_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,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_FE_FREE_SPEC_TMPVAR_HANDLER,
- ZEND_FE_FREE_SPEC_TMPVAR_HANDLER,
- ZEND_FE_FREE_SPEC_TMPVAR_HANDLER,
- ZEND_FE_FREE_SPEC_TMPVAR_HANDLER,
- ZEND_FE_FREE_SPEC_TMPVAR_HANDLER,
- ZEND_FE_FREE_SPEC_TMPVAR_HANDLER,
- ZEND_FE_FREE_SPEC_TMPVAR_HANDLER,
- ZEND_FE_FREE_SPEC_TMPVAR_HANDLER,
- ZEND_FE_FREE_SPEC_TMPVAR_HANDLER,
- ZEND_FE_FREE_SPEC_TMPVAR_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_INIT_DYNAMIC_CALL_SPEC_CONST_HANDLER,
- ZEND_INIT_DYNAMIC_CALL_SPEC_TMPVAR_HANDLER,
- ZEND_INIT_DYNAMIC_CALL_SPEC_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_INIT_DYNAMIC_CALL_SPEC_CV_HANDLER,
- ZEND_INIT_DYNAMIC_CALL_SPEC_CONST_HANDLER,
- ZEND_INIT_DYNAMIC_CALL_SPEC_TMPVAR_HANDLER,
- ZEND_INIT_DYNAMIC_CALL_SPEC_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_INIT_DYNAMIC_CALL_SPEC_CV_HANDLER,
- ZEND_INIT_DYNAMIC_CALL_SPEC_CONST_HANDLER,
- ZEND_INIT_DYNAMIC_CALL_SPEC_TMPVAR_HANDLER,
- ZEND_INIT_DYNAMIC_CALL_SPEC_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_INIT_DYNAMIC_CALL_SPEC_CV_HANDLER,
- ZEND_INIT_DYNAMIC_CALL_SPEC_CONST_HANDLER,
- ZEND_INIT_DYNAMIC_CALL_SPEC_TMPVAR_HANDLER,
- ZEND_INIT_DYNAMIC_CALL_SPEC_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_INIT_DYNAMIC_CALL_SPEC_CV_HANDLER,
- ZEND_INIT_DYNAMIC_CALL_SPEC_CONST_HANDLER,
- ZEND_INIT_DYNAMIC_CALL_SPEC_TMPVAR_HANDLER,
- ZEND_INIT_DYNAMIC_CALL_SPEC_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_INIT_DYNAMIC_CALL_SPEC_CV_HANDLER,
- ZEND_DO_ICALL_SPEC_HANDLER,
- ZEND_DO_ICALL_SPEC_HANDLER,
- ZEND_DO_ICALL_SPEC_HANDLER,
- ZEND_DO_ICALL_SPEC_HANDLER,
- ZEND_DO_ICALL_SPEC_HANDLER,
- ZEND_DO_ICALL_SPEC_HANDLER,
- ZEND_DO_ICALL_SPEC_HANDLER,
- ZEND_DO_ICALL_SPEC_HANDLER,
- ZEND_DO_ICALL_SPEC_HANDLER,
- ZEND_DO_ICALL_SPEC_HANDLER,
- ZEND_DO_ICALL_SPEC_HANDLER,
- ZEND_DO_ICALL_SPEC_HANDLER,
- ZEND_DO_ICALL_SPEC_HANDLER,
- ZEND_DO_ICALL_SPEC_HANDLER,
- ZEND_DO_ICALL_SPEC_HANDLER,
- ZEND_DO_ICALL_SPEC_HANDLER,
- ZEND_DO_ICALL_SPEC_HANDLER,
- ZEND_DO_ICALL_SPEC_HANDLER,
- ZEND_DO_ICALL_SPEC_HANDLER,
- ZEND_DO_ICALL_SPEC_HANDLER,
- ZEND_DO_ICALL_SPEC_HANDLER,
- ZEND_DO_ICALL_SPEC_HANDLER,
- ZEND_DO_ICALL_SPEC_HANDLER,
- ZEND_DO_ICALL_SPEC_HANDLER,
- ZEND_DO_ICALL_SPEC_HANDLER,
- ZEND_DO_UCALL_SPEC_HANDLER,
- ZEND_DO_UCALL_SPEC_HANDLER,
- ZEND_DO_UCALL_SPEC_HANDLER,
- ZEND_DO_UCALL_SPEC_HANDLER,
- ZEND_DO_UCALL_SPEC_HANDLER,
- ZEND_DO_UCALL_SPEC_HANDLER,
- ZEND_DO_UCALL_SPEC_HANDLER,
- ZEND_DO_UCALL_SPEC_HANDLER,
- ZEND_DO_UCALL_SPEC_HANDLER,
- ZEND_DO_UCALL_SPEC_HANDLER,
- ZEND_DO_UCALL_SPEC_HANDLER,
- ZEND_DO_UCALL_SPEC_HANDLER,
- ZEND_DO_UCALL_SPEC_HANDLER,
- ZEND_DO_UCALL_SPEC_HANDLER,
- ZEND_DO_UCALL_SPEC_HANDLER,
- ZEND_DO_UCALL_SPEC_HANDLER,
- ZEND_DO_UCALL_SPEC_HANDLER,
- ZEND_DO_UCALL_SPEC_HANDLER,
- ZEND_DO_UCALL_SPEC_HANDLER,
- ZEND_DO_UCALL_SPEC_HANDLER,
- ZEND_DO_UCALL_SPEC_HANDLER,
- ZEND_DO_UCALL_SPEC_HANDLER,
- ZEND_DO_UCALL_SPEC_HANDLER,
- ZEND_DO_UCALL_SPEC_HANDLER,
- ZEND_DO_UCALL_SPEC_HANDLER,
- ZEND_DO_FCALL_BY_NAME_SPEC_HANDLER,
- ZEND_DO_FCALL_BY_NAME_SPEC_HANDLER,
- ZEND_DO_FCALL_BY_NAME_SPEC_HANDLER,
- ZEND_DO_FCALL_BY_NAME_SPEC_HANDLER,
- ZEND_DO_FCALL_BY_NAME_SPEC_HANDLER,
- ZEND_DO_FCALL_BY_NAME_SPEC_HANDLER,
- ZEND_DO_FCALL_BY_NAME_SPEC_HANDLER,
- ZEND_DO_FCALL_BY_NAME_SPEC_HANDLER,
- ZEND_DO_FCALL_BY_NAME_SPEC_HANDLER,
- ZEND_DO_FCALL_BY_NAME_SPEC_HANDLER,
- ZEND_DO_FCALL_BY_NAME_SPEC_HANDLER,
- ZEND_DO_FCALL_BY_NAME_SPEC_HANDLER,
- ZEND_DO_FCALL_BY_NAME_SPEC_HANDLER,
- ZEND_DO_FCALL_BY_NAME_SPEC_HANDLER,
- ZEND_DO_FCALL_BY_NAME_SPEC_HANDLER,
- ZEND_DO_FCALL_BY_NAME_SPEC_HANDLER,
- ZEND_DO_FCALL_BY_NAME_SPEC_HANDLER,
- ZEND_DO_FCALL_BY_NAME_SPEC_HANDLER,
- ZEND_DO_FCALL_BY_NAME_SPEC_HANDLER,
- ZEND_DO_FCALL_BY_NAME_SPEC_HANDLER,
- ZEND_DO_FCALL_BY_NAME_SPEC_HANDLER,
- ZEND_DO_FCALL_BY_NAME_SPEC_HANDLER,
- ZEND_DO_FCALL_BY_NAME_SPEC_HANDLER,
- ZEND_DO_FCALL_BY_NAME_SPEC_HANDLER,
- ZEND_DO_FCALL_BY_NAME_SPEC_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_PRE_INC_OBJ_SPEC_VAR_CONST_HANDLER,
- ZEND_PRE_INC_OBJ_SPEC_VAR_TMPVAR_HANDLER,
- ZEND_PRE_INC_OBJ_SPEC_VAR_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_PRE_INC_OBJ_SPEC_VAR_CV_HANDLER,
- ZEND_PRE_INC_OBJ_SPEC_UNUSED_CONST_HANDLER,
- ZEND_PRE_INC_OBJ_SPEC_UNUSED_TMPVAR_HANDLER,
- ZEND_PRE_INC_OBJ_SPEC_UNUSED_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_PRE_INC_OBJ_SPEC_UNUSED_CV_HANDLER,
- ZEND_PRE_INC_OBJ_SPEC_CV_CONST_HANDLER,
- ZEND_PRE_INC_OBJ_SPEC_CV_TMPVAR_HANDLER,
- ZEND_PRE_INC_OBJ_SPEC_CV_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_PRE_INC_OBJ_SPEC_CV_CV_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_PRE_DEC_OBJ_SPEC_VAR_CONST_HANDLER,
- ZEND_PRE_DEC_OBJ_SPEC_VAR_TMPVAR_HANDLER,
- ZEND_PRE_DEC_OBJ_SPEC_VAR_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_PRE_DEC_OBJ_SPEC_VAR_CV_HANDLER,
- ZEND_PRE_DEC_OBJ_SPEC_UNUSED_CONST_HANDLER,
- ZEND_PRE_DEC_OBJ_SPEC_UNUSED_TMPVAR_HANDLER,
- ZEND_PRE_DEC_OBJ_SPEC_UNUSED_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_PRE_DEC_OBJ_SPEC_UNUSED_CV_HANDLER,
- ZEND_PRE_DEC_OBJ_SPEC_CV_CONST_HANDLER,
- ZEND_PRE_DEC_OBJ_SPEC_CV_TMPVAR_HANDLER,
- ZEND_PRE_DEC_OBJ_SPEC_CV_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_PRE_DEC_OBJ_SPEC_CV_CV_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_POST_INC_OBJ_SPEC_VAR_CONST_HANDLER,
- ZEND_POST_INC_OBJ_SPEC_VAR_TMPVAR_HANDLER,
- ZEND_POST_INC_OBJ_SPEC_VAR_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_POST_INC_OBJ_SPEC_VAR_CV_HANDLER,
- ZEND_POST_INC_OBJ_SPEC_UNUSED_CONST_HANDLER,
- ZEND_POST_INC_OBJ_SPEC_UNUSED_TMPVAR_HANDLER,
- ZEND_POST_INC_OBJ_SPEC_UNUSED_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_POST_INC_OBJ_SPEC_UNUSED_CV_HANDLER,
- ZEND_POST_INC_OBJ_SPEC_CV_CONST_HANDLER,
- ZEND_POST_INC_OBJ_SPEC_CV_TMPVAR_HANDLER,
- ZEND_POST_INC_OBJ_SPEC_CV_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_POST_INC_OBJ_SPEC_CV_CV_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_POST_DEC_OBJ_SPEC_VAR_CONST_HANDLER,
- ZEND_POST_DEC_OBJ_SPEC_VAR_TMPVAR_HANDLER,
- ZEND_POST_DEC_OBJ_SPEC_VAR_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_POST_DEC_OBJ_SPEC_VAR_CV_HANDLER,
- ZEND_POST_DEC_OBJ_SPEC_UNUSED_CONST_HANDLER,
- ZEND_POST_DEC_OBJ_SPEC_UNUSED_TMPVAR_HANDLER,
- ZEND_POST_DEC_OBJ_SPEC_UNUSED_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_POST_DEC_OBJ_SPEC_UNUSED_CV_HANDLER,
- ZEND_POST_DEC_OBJ_SPEC_CV_CONST_HANDLER,
- ZEND_POST_DEC_OBJ_SPEC_CV_TMPVAR_HANDLER,
- ZEND_POST_DEC_OBJ_SPEC_CV_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_POST_DEC_OBJ_SPEC_CV_CV_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_ASSIGN_OBJ_SPEC_VAR_CONST_HANDLER,
- ZEND_ASSIGN_OBJ_SPEC_VAR_TMPVAR_HANDLER,
- ZEND_ASSIGN_OBJ_SPEC_VAR_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_ASSIGN_OBJ_SPEC_VAR_CV_HANDLER,
- ZEND_ASSIGN_OBJ_SPEC_UNUSED_CONST_HANDLER,
- ZEND_ASSIGN_OBJ_SPEC_UNUSED_TMPVAR_HANDLER,
- ZEND_ASSIGN_OBJ_SPEC_UNUSED_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_ASSIGN_OBJ_SPEC_UNUSED_CV_HANDLER,
- ZEND_ASSIGN_OBJ_SPEC_CV_CONST_HANDLER,
- ZEND_ASSIGN_OBJ_SPEC_CV_TMPVAR_HANDLER,
- ZEND_ASSIGN_OBJ_SPEC_CV_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_ASSIGN_OBJ_SPEC_CV_CV_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,
- 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,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_INSTANCEOF_SPEC_TMPVAR_CONST_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_INSTANCEOF_SPEC_TMPVAR_VAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_INSTANCEOF_SPEC_TMPVAR_CONST_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_INSTANCEOF_SPEC_TMPVAR_VAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_INSTANCEOF_SPEC_CV_CONST_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_INSTANCEOF_SPEC_CV_VAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_DECLARE_CLASS_SPEC_HANDLER,
- ZEND_DECLARE_CLASS_SPEC_HANDLER,
- ZEND_DECLARE_CLASS_SPEC_HANDLER,
- ZEND_DECLARE_CLASS_SPEC_HANDLER,
- ZEND_DECLARE_CLASS_SPEC_HANDLER,
- ZEND_DECLARE_CLASS_SPEC_HANDLER,
- ZEND_DECLARE_CLASS_SPEC_HANDLER,
- ZEND_DECLARE_CLASS_SPEC_HANDLER,
- ZEND_DECLARE_CLASS_SPEC_HANDLER,
- ZEND_DECLARE_CLASS_SPEC_HANDLER,
- ZEND_DECLARE_CLASS_SPEC_HANDLER,
- ZEND_DECLARE_CLASS_SPEC_HANDLER,
- ZEND_DECLARE_CLASS_SPEC_HANDLER,
- ZEND_DECLARE_CLASS_SPEC_HANDLER,
- ZEND_DECLARE_CLASS_SPEC_HANDLER,
- ZEND_DECLARE_CLASS_SPEC_HANDLER,
- ZEND_DECLARE_CLASS_SPEC_HANDLER,
- ZEND_DECLARE_CLASS_SPEC_HANDLER,
- ZEND_DECLARE_CLASS_SPEC_HANDLER,
- ZEND_DECLARE_CLASS_SPEC_HANDLER,
- ZEND_DECLARE_CLASS_SPEC_HANDLER,
- ZEND_DECLARE_CLASS_SPEC_HANDLER,
- ZEND_DECLARE_CLASS_SPEC_HANDLER,
- ZEND_DECLARE_CLASS_SPEC_HANDLER,
- ZEND_DECLARE_CLASS_SPEC_HANDLER,
- ZEND_DECLARE_INHERITED_CLASS_SPEC_HANDLER,
- ZEND_DECLARE_INHERITED_CLASS_SPEC_HANDLER,
- ZEND_DECLARE_INHERITED_CLASS_SPEC_HANDLER,
- ZEND_DECLARE_INHERITED_CLASS_SPEC_HANDLER,
- ZEND_DECLARE_INHERITED_CLASS_SPEC_HANDLER,
- ZEND_DECLARE_INHERITED_CLASS_SPEC_HANDLER,
- ZEND_DECLARE_INHERITED_CLASS_SPEC_HANDLER,
- ZEND_DECLARE_INHERITED_CLASS_SPEC_HANDLER,
- ZEND_DECLARE_INHERITED_CLASS_SPEC_HANDLER,
- ZEND_DECLARE_INHERITED_CLASS_SPEC_HANDLER,
- ZEND_DECLARE_INHERITED_CLASS_SPEC_HANDLER,
- ZEND_DECLARE_INHERITED_CLASS_SPEC_HANDLER,
- ZEND_DECLARE_INHERITED_CLASS_SPEC_HANDLER,
- ZEND_DECLARE_INHERITED_CLASS_SPEC_HANDLER,
- ZEND_DECLARE_INHERITED_CLASS_SPEC_HANDLER,
- ZEND_DECLARE_INHERITED_CLASS_SPEC_HANDLER,
- ZEND_DECLARE_INHERITED_CLASS_SPEC_HANDLER,
- ZEND_DECLARE_INHERITED_CLASS_SPEC_HANDLER,
- ZEND_DECLARE_INHERITED_CLASS_SPEC_HANDLER,
- ZEND_DECLARE_INHERITED_CLASS_SPEC_HANDLER,
- ZEND_DECLARE_INHERITED_CLASS_SPEC_HANDLER,
- ZEND_DECLARE_INHERITED_CLASS_SPEC_HANDLER,
- ZEND_DECLARE_INHERITED_CLASS_SPEC_HANDLER,
- ZEND_DECLARE_INHERITED_CLASS_SPEC_HANDLER,
- ZEND_DECLARE_INHERITED_CLASS_SPEC_HANDLER,
- ZEND_DECLARE_FUNCTION_SPEC_HANDLER,
- ZEND_DECLARE_FUNCTION_SPEC_HANDLER,
- ZEND_DECLARE_FUNCTION_SPEC_HANDLER,
- ZEND_DECLARE_FUNCTION_SPEC_HANDLER,
- ZEND_DECLARE_FUNCTION_SPEC_HANDLER,
- ZEND_DECLARE_FUNCTION_SPEC_HANDLER,
- ZEND_DECLARE_FUNCTION_SPEC_HANDLER,
- ZEND_DECLARE_FUNCTION_SPEC_HANDLER,
- ZEND_DECLARE_FUNCTION_SPEC_HANDLER,
- ZEND_DECLARE_FUNCTION_SPEC_HANDLER,
- ZEND_DECLARE_FUNCTION_SPEC_HANDLER,
- ZEND_DECLARE_FUNCTION_SPEC_HANDLER,
- ZEND_DECLARE_FUNCTION_SPEC_HANDLER,
- ZEND_DECLARE_FUNCTION_SPEC_HANDLER,
- ZEND_DECLARE_FUNCTION_SPEC_HANDLER,
- ZEND_DECLARE_FUNCTION_SPEC_HANDLER,
- ZEND_DECLARE_FUNCTION_SPEC_HANDLER,
- ZEND_DECLARE_FUNCTION_SPEC_HANDLER,
- ZEND_DECLARE_FUNCTION_SPEC_HANDLER,
- ZEND_DECLARE_FUNCTION_SPEC_HANDLER,
- ZEND_DECLARE_FUNCTION_SPEC_HANDLER,
- ZEND_DECLARE_FUNCTION_SPEC_HANDLER,
- ZEND_DECLARE_FUNCTION_SPEC_HANDLER,
- ZEND_DECLARE_FUNCTION_SPEC_HANDLER,
- ZEND_DECLARE_FUNCTION_SPEC_HANDLER,
- ZEND_YIELD_FROM_SPEC_CONST_HANDLER,
- ZEND_YIELD_FROM_SPEC_CONST_HANDLER,
- ZEND_YIELD_FROM_SPEC_CONST_HANDLER,
- ZEND_YIELD_FROM_SPEC_CONST_HANDLER,
- ZEND_YIELD_FROM_SPEC_CONST_HANDLER,
- ZEND_YIELD_FROM_SPEC_TMP_HANDLER,
- ZEND_YIELD_FROM_SPEC_TMP_HANDLER,
- ZEND_YIELD_FROM_SPEC_TMP_HANDLER,
- ZEND_YIELD_FROM_SPEC_TMP_HANDLER,
- ZEND_YIELD_FROM_SPEC_TMP_HANDLER,
- ZEND_YIELD_FROM_SPEC_VAR_HANDLER,
- ZEND_YIELD_FROM_SPEC_VAR_HANDLER,
- ZEND_YIELD_FROM_SPEC_VAR_HANDLER,
- ZEND_YIELD_FROM_SPEC_VAR_HANDLER,
- ZEND_YIELD_FROM_SPEC_VAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_YIELD_FROM_SPEC_CV_HANDLER,
- ZEND_YIELD_FROM_SPEC_CV_HANDLER,
- ZEND_YIELD_FROM_SPEC_CV_HANDLER,
- ZEND_YIELD_FROM_SPEC_CV_HANDLER,
- ZEND_YIELD_FROM_SPEC_CV_HANDLER,
- ZEND_DECLARE_CONST_SPEC_CONST_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,
- 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_ADD_INTERFACE_SPEC_CONST_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_ADD_INTERFACE_SPEC_CONST_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_ADD_INTERFACE_SPEC_CONST_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_ADD_INTERFACE_SPEC_CONST_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_ADD_INTERFACE_SPEC_CONST_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_DECLARE_INHERITED_CLASS_DELAYED_SPEC_HANDLER,
- ZEND_DECLARE_INHERITED_CLASS_DELAYED_SPEC_HANDLER,
- ZEND_DECLARE_INHERITED_CLASS_DELAYED_SPEC_HANDLER,
- ZEND_DECLARE_INHERITED_CLASS_DELAYED_SPEC_HANDLER,
- ZEND_DECLARE_INHERITED_CLASS_DELAYED_SPEC_HANDLER,
- ZEND_DECLARE_INHERITED_CLASS_DELAYED_SPEC_HANDLER,
- ZEND_DECLARE_INHERITED_CLASS_DELAYED_SPEC_HANDLER,
- ZEND_DECLARE_INHERITED_CLASS_DELAYED_SPEC_HANDLER,
- ZEND_DECLARE_INHERITED_CLASS_DELAYED_SPEC_HANDLER,
- ZEND_DECLARE_INHERITED_CLASS_DELAYED_SPEC_HANDLER,
- ZEND_DECLARE_INHERITED_CLASS_DELAYED_SPEC_HANDLER,
- ZEND_DECLARE_INHERITED_CLASS_DELAYED_SPEC_HANDLER,
- ZEND_DECLARE_INHERITED_CLASS_DELAYED_SPEC_HANDLER,
- ZEND_DECLARE_INHERITED_CLASS_DELAYED_SPEC_HANDLER,
- ZEND_DECLARE_INHERITED_CLASS_DELAYED_SPEC_HANDLER,
- ZEND_DECLARE_INHERITED_CLASS_DELAYED_SPEC_HANDLER,
- ZEND_DECLARE_INHERITED_CLASS_DELAYED_SPEC_HANDLER,
- ZEND_DECLARE_INHERITED_CLASS_DELAYED_SPEC_HANDLER,
- ZEND_DECLARE_INHERITED_CLASS_DELAYED_SPEC_HANDLER,
- ZEND_DECLARE_INHERITED_CLASS_DELAYED_SPEC_HANDLER,
- ZEND_DECLARE_INHERITED_CLASS_DELAYED_SPEC_HANDLER,
- ZEND_DECLARE_INHERITED_CLASS_DELAYED_SPEC_HANDLER,
- ZEND_DECLARE_INHERITED_CLASS_DELAYED_SPEC_HANDLER,
- ZEND_DECLARE_INHERITED_CLASS_DELAYED_SPEC_HANDLER,
- ZEND_DECLARE_INHERITED_CLASS_DELAYED_SPEC_HANDLER,
- ZEND_VERIFY_ABSTRACT_CLASS_SPEC_HANDLER,
- ZEND_VERIFY_ABSTRACT_CLASS_SPEC_HANDLER,
- ZEND_VERIFY_ABSTRACT_CLASS_SPEC_HANDLER,
- ZEND_VERIFY_ABSTRACT_CLASS_SPEC_HANDLER,
- ZEND_VERIFY_ABSTRACT_CLASS_SPEC_HANDLER,
- ZEND_VERIFY_ABSTRACT_CLASS_SPEC_HANDLER,
- ZEND_VERIFY_ABSTRACT_CLASS_SPEC_HANDLER,
- ZEND_VERIFY_ABSTRACT_CLASS_SPEC_HANDLER,
- ZEND_VERIFY_ABSTRACT_CLASS_SPEC_HANDLER,
- ZEND_VERIFY_ABSTRACT_CLASS_SPEC_HANDLER,
- ZEND_VERIFY_ABSTRACT_CLASS_SPEC_HANDLER,
- ZEND_VERIFY_ABSTRACT_CLASS_SPEC_HANDLER,
- ZEND_VERIFY_ABSTRACT_CLASS_SPEC_HANDLER,
- ZEND_VERIFY_ABSTRACT_CLASS_SPEC_HANDLER,
- ZEND_VERIFY_ABSTRACT_CLASS_SPEC_HANDLER,
- ZEND_VERIFY_ABSTRACT_CLASS_SPEC_HANDLER,
- ZEND_VERIFY_ABSTRACT_CLASS_SPEC_HANDLER,
- ZEND_VERIFY_ABSTRACT_CLASS_SPEC_HANDLER,
- ZEND_VERIFY_ABSTRACT_CLASS_SPEC_HANDLER,
- ZEND_VERIFY_ABSTRACT_CLASS_SPEC_HANDLER,
- ZEND_VERIFY_ABSTRACT_CLASS_SPEC_HANDLER,
- ZEND_VERIFY_ABSTRACT_CLASS_SPEC_HANDLER,
- ZEND_VERIFY_ABSTRACT_CLASS_SPEC_HANDLER,
- ZEND_VERIFY_ABSTRACT_CLASS_SPEC_HANDLER,
- ZEND_VERIFY_ABSTRACT_CLASS_SPEC_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_ASSIGN_DIM_SPEC_VAR_CONST_HANDLER,
- ZEND_ASSIGN_DIM_SPEC_VAR_TMPVAR_HANDLER,
- ZEND_ASSIGN_DIM_SPEC_VAR_TMPVAR_HANDLER,
- ZEND_ASSIGN_DIM_SPEC_VAR_UNUSED_HANDLER,
- ZEND_ASSIGN_DIM_SPEC_VAR_CV_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_ASSIGN_DIM_SPEC_CV_CONST_HANDLER,
- ZEND_ASSIGN_DIM_SPEC_CV_TMPVAR_HANDLER,
- ZEND_ASSIGN_DIM_SPEC_CV_TMPVAR_HANDLER,
- ZEND_ASSIGN_DIM_SPEC_CV_UNUSED_HANDLER,
- ZEND_ASSIGN_DIM_SPEC_CV_CV_HANDLER,
- ZEND_ISSET_ISEMPTY_PROP_OBJ_SPEC_CONST_CONST_HANDLER,
- ZEND_ISSET_ISEMPTY_PROP_OBJ_SPEC_CONST_TMPVAR_HANDLER,
- ZEND_ISSET_ISEMPTY_PROP_OBJ_SPEC_CONST_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_ISSET_ISEMPTY_PROP_OBJ_SPEC_CONST_CV_HANDLER,
- ZEND_ISSET_ISEMPTY_PROP_OBJ_SPEC_TMPVAR_CONST_HANDLER,
- ZEND_ISSET_ISEMPTY_PROP_OBJ_SPEC_TMPVAR_TMPVAR_HANDLER,
- ZEND_ISSET_ISEMPTY_PROP_OBJ_SPEC_TMPVAR_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_ISSET_ISEMPTY_PROP_OBJ_SPEC_TMPVAR_CV_HANDLER,
- ZEND_ISSET_ISEMPTY_PROP_OBJ_SPEC_TMPVAR_CONST_HANDLER,
- ZEND_ISSET_ISEMPTY_PROP_OBJ_SPEC_TMPVAR_TMPVAR_HANDLER,
- ZEND_ISSET_ISEMPTY_PROP_OBJ_SPEC_TMPVAR_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_ISSET_ISEMPTY_PROP_OBJ_SPEC_TMPVAR_CV_HANDLER,
- ZEND_ISSET_ISEMPTY_PROP_OBJ_SPEC_UNUSED_CONST_HANDLER,
- ZEND_ISSET_ISEMPTY_PROP_OBJ_SPEC_UNUSED_TMPVAR_HANDLER,
- ZEND_ISSET_ISEMPTY_PROP_OBJ_SPEC_UNUSED_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_ISSET_ISEMPTY_PROP_OBJ_SPEC_UNUSED_CV_HANDLER,
- ZEND_ISSET_ISEMPTY_PROP_OBJ_SPEC_CV_CONST_HANDLER,
- ZEND_ISSET_ISEMPTY_PROP_OBJ_SPEC_CV_TMPVAR_HANDLER,
- ZEND_ISSET_ISEMPTY_PROP_OBJ_SPEC_CV_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_ISSET_ISEMPTY_PROP_OBJ_SPEC_CV_CV_HANDLER,
- ZEND_HANDLE_EXCEPTION_SPEC_HANDLER,
- ZEND_HANDLE_EXCEPTION_SPEC_HANDLER,
- ZEND_HANDLE_EXCEPTION_SPEC_HANDLER,
- ZEND_HANDLE_EXCEPTION_SPEC_HANDLER,
- ZEND_HANDLE_EXCEPTION_SPEC_HANDLER,
- ZEND_HANDLE_EXCEPTION_SPEC_HANDLER,
- ZEND_HANDLE_EXCEPTION_SPEC_HANDLER,
- ZEND_HANDLE_EXCEPTION_SPEC_HANDLER,
- ZEND_HANDLE_EXCEPTION_SPEC_HANDLER,
- ZEND_HANDLE_EXCEPTION_SPEC_HANDLER,
- ZEND_HANDLE_EXCEPTION_SPEC_HANDLER,
- ZEND_HANDLE_EXCEPTION_SPEC_HANDLER,
- ZEND_HANDLE_EXCEPTION_SPEC_HANDLER,
- ZEND_HANDLE_EXCEPTION_SPEC_HANDLER,
- ZEND_HANDLE_EXCEPTION_SPEC_HANDLER,
- ZEND_HANDLE_EXCEPTION_SPEC_HANDLER,
- ZEND_HANDLE_EXCEPTION_SPEC_HANDLER,
- ZEND_HANDLE_EXCEPTION_SPEC_HANDLER,
- ZEND_HANDLE_EXCEPTION_SPEC_HANDLER,
- ZEND_HANDLE_EXCEPTION_SPEC_HANDLER,
- ZEND_HANDLE_EXCEPTION_SPEC_HANDLER,
- ZEND_HANDLE_EXCEPTION_SPEC_HANDLER,
- ZEND_HANDLE_EXCEPTION_SPEC_HANDLER,
- ZEND_HANDLE_EXCEPTION_SPEC_HANDLER,
- ZEND_HANDLE_EXCEPTION_SPEC_HANDLER,
- ZEND_USER_OPCODE_SPEC_HANDLER,
- ZEND_USER_OPCODE_SPEC_HANDLER,
- ZEND_USER_OPCODE_SPEC_HANDLER,
- ZEND_USER_OPCODE_SPEC_HANDLER,
- ZEND_USER_OPCODE_SPEC_HANDLER,
- ZEND_USER_OPCODE_SPEC_HANDLER,
- ZEND_USER_OPCODE_SPEC_HANDLER,
- ZEND_USER_OPCODE_SPEC_HANDLER,
- ZEND_USER_OPCODE_SPEC_HANDLER,
- ZEND_USER_OPCODE_SPEC_HANDLER,
- ZEND_USER_OPCODE_SPEC_HANDLER,
- ZEND_USER_OPCODE_SPEC_HANDLER,
- ZEND_USER_OPCODE_SPEC_HANDLER,
- ZEND_USER_OPCODE_SPEC_HANDLER,
- ZEND_USER_OPCODE_SPEC_HANDLER,
- ZEND_USER_OPCODE_SPEC_HANDLER,
- ZEND_USER_OPCODE_SPEC_HANDLER,
- ZEND_USER_OPCODE_SPEC_HANDLER,
- ZEND_USER_OPCODE_SPEC_HANDLER,
- ZEND_USER_OPCODE_SPEC_HANDLER,
- ZEND_USER_OPCODE_SPEC_HANDLER,
- ZEND_USER_OPCODE_SPEC_HANDLER,
- ZEND_USER_OPCODE_SPEC_HANDLER,
- ZEND_USER_OPCODE_SPEC_HANDLER,
- ZEND_USER_OPCODE_SPEC_HANDLER,
- ZEND_ASSERT_CHECK_SPEC_HANDLER,
- ZEND_ASSERT_CHECK_SPEC_HANDLER,
- ZEND_ASSERT_CHECK_SPEC_HANDLER,
- ZEND_ASSERT_CHECK_SPEC_HANDLER,
- ZEND_ASSERT_CHECK_SPEC_HANDLER,
- ZEND_ASSERT_CHECK_SPEC_HANDLER,
- ZEND_ASSERT_CHECK_SPEC_HANDLER,
- ZEND_ASSERT_CHECK_SPEC_HANDLER,
- ZEND_ASSERT_CHECK_SPEC_HANDLER,
- ZEND_ASSERT_CHECK_SPEC_HANDLER,
- ZEND_ASSERT_CHECK_SPEC_HANDLER,
- ZEND_ASSERT_CHECK_SPEC_HANDLER,
- ZEND_ASSERT_CHECK_SPEC_HANDLER,
- ZEND_ASSERT_CHECK_SPEC_HANDLER,
- ZEND_ASSERT_CHECK_SPEC_HANDLER,
- ZEND_ASSERT_CHECK_SPEC_HANDLER,
- ZEND_ASSERT_CHECK_SPEC_HANDLER,
- ZEND_ASSERT_CHECK_SPEC_HANDLER,
- ZEND_ASSERT_CHECK_SPEC_HANDLER,
- ZEND_ASSERT_CHECK_SPEC_HANDLER,
- ZEND_ASSERT_CHECK_SPEC_HANDLER,
- ZEND_ASSERT_CHECK_SPEC_HANDLER,
- ZEND_ASSERT_CHECK_SPEC_HANDLER,
- ZEND_ASSERT_CHECK_SPEC_HANDLER,
- ZEND_ASSERT_CHECK_SPEC_HANDLER,
- ZEND_JMP_SET_SPEC_CONST_HANDLER,
- ZEND_JMP_SET_SPEC_CONST_HANDLER,
- ZEND_JMP_SET_SPEC_CONST_HANDLER,
- ZEND_JMP_SET_SPEC_CONST_HANDLER,
- ZEND_JMP_SET_SPEC_CONST_HANDLER,
- ZEND_JMP_SET_SPEC_TMP_HANDLER,
- ZEND_JMP_SET_SPEC_TMP_HANDLER,
- ZEND_JMP_SET_SPEC_TMP_HANDLER,
- ZEND_JMP_SET_SPEC_TMP_HANDLER,
- ZEND_JMP_SET_SPEC_TMP_HANDLER,
- ZEND_JMP_SET_SPEC_VAR_HANDLER,
- ZEND_JMP_SET_SPEC_VAR_HANDLER,
- ZEND_JMP_SET_SPEC_VAR_HANDLER,
- ZEND_JMP_SET_SPEC_VAR_HANDLER,
- ZEND_JMP_SET_SPEC_VAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_JMP_SET_SPEC_CV_HANDLER,
- ZEND_JMP_SET_SPEC_CV_HANDLER,
- ZEND_JMP_SET_SPEC_CV_HANDLER,
- ZEND_JMP_SET_SPEC_CV_HANDLER,
- ZEND_JMP_SET_SPEC_CV_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_DECLARE_LAMBDA_FUNCTION_SPEC_CONST_UNUSED_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,
- 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_ADD_TRAIT_SPEC_HANDLER,
- ZEND_ADD_TRAIT_SPEC_HANDLER,
- ZEND_ADD_TRAIT_SPEC_HANDLER,
- ZEND_ADD_TRAIT_SPEC_HANDLER,
- ZEND_ADD_TRAIT_SPEC_HANDLER,
- ZEND_ADD_TRAIT_SPEC_HANDLER,
- ZEND_ADD_TRAIT_SPEC_HANDLER,
- ZEND_ADD_TRAIT_SPEC_HANDLER,
- ZEND_ADD_TRAIT_SPEC_HANDLER,
- ZEND_ADD_TRAIT_SPEC_HANDLER,
- ZEND_ADD_TRAIT_SPEC_HANDLER,
- ZEND_ADD_TRAIT_SPEC_HANDLER,
- ZEND_ADD_TRAIT_SPEC_HANDLER,
- ZEND_ADD_TRAIT_SPEC_HANDLER,
- ZEND_ADD_TRAIT_SPEC_HANDLER,
- ZEND_ADD_TRAIT_SPEC_HANDLER,
- ZEND_ADD_TRAIT_SPEC_HANDLER,
- ZEND_ADD_TRAIT_SPEC_HANDLER,
- ZEND_ADD_TRAIT_SPEC_HANDLER,
- ZEND_ADD_TRAIT_SPEC_HANDLER,
- ZEND_ADD_TRAIT_SPEC_HANDLER,
- ZEND_ADD_TRAIT_SPEC_HANDLER,
- ZEND_ADD_TRAIT_SPEC_HANDLER,
- ZEND_ADD_TRAIT_SPEC_HANDLER,
- ZEND_ADD_TRAIT_SPEC_HANDLER,
- ZEND_BIND_TRAITS_SPEC_HANDLER,
- ZEND_BIND_TRAITS_SPEC_HANDLER,
- ZEND_BIND_TRAITS_SPEC_HANDLER,
- ZEND_BIND_TRAITS_SPEC_HANDLER,
- ZEND_BIND_TRAITS_SPEC_HANDLER,
- ZEND_BIND_TRAITS_SPEC_HANDLER,
- ZEND_BIND_TRAITS_SPEC_HANDLER,
- ZEND_BIND_TRAITS_SPEC_HANDLER,
- ZEND_BIND_TRAITS_SPEC_HANDLER,
- ZEND_BIND_TRAITS_SPEC_HANDLER,
- ZEND_BIND_TRAITS_SPEC_HANDLER,
- ZEND_BIND_TRAITS_SPEC_HANDLER,
- ZEND_BIND_TRAITS_SPEC_HANDLER,
- ZEND_BIND_TRAITS_SPEC_HANDLER,
- ZEND_BIND_TRAITS_SPEC_HANDLER,
- ZEND_BIND_TRAITS_SPEC_HANDLER,
- ZEND_BIND_TRAITS_SPEC_HANDLER,
- ZEND_BIND_TRAITS_SPEC_HANDLER,
- ZEND_BIND_TRAITS_SPEC_HANDLER,
- ZEND_BIND_TRAITS_SPEC_HANDLER,
- ZEND_BIND_TRAITS_SPEC_HANDLER,
- ZEND_BIND_TRAITS_SPEC_HANDLER,
- ZEND_BIND_TRAITS_SPEC_HANDLER,
- ZEND_BIND_TRAITS_SPEC_HANDLER,
- ZEND_BIND_TRAITS_SPEC_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,
- ZEND_SEPARATE_SPEC_VAR_UNUSED_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_FETCH_CLASS_NAME_SPEC_HANDLER,
- ZEND_FETCH_CLASS_NAME_SPEC_HANDLER,
- ZEND_FETCH_CLASS_NAME_SPEC_HANDLER,
- ZEND_FETCH_CLASS_NAME_SPEC_HANDLER,
- ZEND_FETCH_CLASS_NAME_SPEC_HANDLER,
- ZEND_FETCH_CLASS_NAME_SPEC_HANDLER,
- ZEND_FETCH_CLASS_NAME_SPEC_HANDLER,
- ZEND_FETCH_CLASS_NAME_SPEC_HANDLER,
- ZEND_FETCH_CLASS_NAME_SPEC_HANDLER,
- ZEND_FETCH_CLASS_NAME_SPEC_HANDLER,
- ZEND_FETCH_CLASS_NAME_SPEC_HANDLER,
- ZEND_FETCH_CLASS_NAME_SPEC_HANDLER,
- ZEND_FETCH_CLASS_NAME_SPEC_HANDLER,
- ZEND_FETCH_CLASS_NAME_SPEC_HANDLER,
- ZEND_FETCH_CLASS_NAME_SPEC_HANDLER,
- ZEND_FETCH_CLASS_NAME_SPEC_HANDLER,
- ZEND_FETCH_CLASS_NAME_SPEC_HANDLER,
- ZEND_FETCH_CLASS_NAME_SPEC_HANDLER,
- ZEND_FETCH_CLASS_NAME_SPEC_HANDLER,
- ZEND_FETCH_CLASS_NAME_SPEC_HANDLER,
- ZEND_FETCH_CLASS_NAME_SPEC_HANDLER,
- ZEND_FETCH_CLASS_NAME_SPEC_HANDLER,
- ZEND_FETCH_CLASS_NAME_SPEC_HANDLER,
- ZEND_FETCH_CLASS_NAME_SPEC_HANDLER,
- ZEND_FETCH_CLASS_NAME_SPEC_HANDLER,
- ZEND_CALL_TRAMPOLINE_SPEC_HANDLER,
- ZEND_CALL_TRAMPOLINE_SPEC_HANDLER,
- ZEND_CALL_TRAMPOLINE_SPEC_HANDLER,
- ZEND_CALL_TRAMPOLINE_SPEC_HANDLER,
- ZEND_CALL_TRAMPOLINE_SPEC_HANDLER,
- ZEND_CALL_TRAMPOLINE_SPEC_HANDLER,
- ZEND_CALL_TRAMPOLINE_SPEC_HANDLER,
- ZEND_CALL_TRAMPOLINE_SPEC_HANDLER,
- ZEND_CALL_TRAMPOLINE_SPEC_HANDLER,
- ZEND_CALL_TRAMPOLINE_SPEC_HANDLER,
- ZEND_CALL_TRAMPOLINE_SPEC_HANDLER,
- ZEND_CALL_TRAMPOLINE_SPEC_HANDLER,
- ZEND_CALL_TRAMPOLINE_SPEC_HANDLER,
- ZEND_CALL_TRAMPOLINE_SPEC_HANDLER,
- ZEND_CALL_TRAMPOLINE_SPEC_HANDLER,
- ZEND_CALL_TRAMPOLINE_SPEC_HANDLER,
- ZEND_CALL_TRAMPOLINE_SPEC_HANDLER,
- ZEND_CALL_TRAMPOLINE_SPEC_HANDLER,
- ZEND_CALL_TRAMPOLINE_SPEC_HANDLER,
- ZEND_CALL_TRAMPOLINE_SPEC_HANDLER,
- ZEND_CALL_TRAMPOLINE_SPEC_HANDLER,
- ZEND_CALL_TRAMPOLINE_SPEC_HANDLER,
- ZEND_CALL_TRAMPOLINE_SPEC_HANDLER,
- ZEND_CALL_TRAMPOLINE_SPEC_HANDLER,
- ZEND_CALL_TRAMPOLINE_SPEC_HANDLER,
- ZEND_DISCARD_EXCEPTION_SPEC_HANDLER,
- ZEND_DISCARD_EXCEPTION_SPEC_HANDLER,
- ZEND_DISCARD_EXCEPTION_SPEC_HANDLER,
- ZEND_DISCARD_EXCEPTION_SPEC_HANDLER,
- ZEND_DISCARD_EXCEPTION_SPEC_HANDLER,
- ZEND_DISCARD_EXCEPTION_SPEC_HANDLER,
- ZEND_DISCARD_EXCEPTION_SPEC_HANDLER,
- ZEND_DISCARD_EXCEPTION_SPEC_HANDLER,
- ZEND_DISCARD_EXCEPTION_SPEC_HANDLER,
- ZEND_DISCARD_EXCEPTION_SPEC_HANDLER,
- ZEND_DISCARD_EXCEPTION_SPEC_HANDLER,
- ZEND_DISCARD_EXCEPTION_SPEC_HANDLER,
- ZEND_DISCARD_EXCEPTION_SPEC_HANDLER,
- ZEND_DISCARD_EXCEPTION_SPEC_HANDLER,
- ZEND_DISCARD_EXCEPTION_SPEC_HANDLER,
- ZEND_DISCARD_EXCEPTION_SPEC_HANDLER,
- ZEND_DISCARD_EXCEPTION_SPEC_HANDLER,
- ZEND_DISCARD_EXCEPTION_SPEC_HANDLER,
- ZEND_DISCARD_EXCEPTION_SPEC_HANDLER,
- ZEND_DISCARD_EXCEPTION_SPEC_HANDLER,
- ZEND_DISCARD_EXCEPTION_SPEC_HANDLER,
- ZEND_DISCARD_EXCEPTION_SPEC_HANDLER,
- ZEND_DISCARD_EXCEPTION_SPEC_HANDLER,
- ZEND_DISCARD_EXCEPTION_SPEC_HANDLER,
- ZEND_DISCARD_EXCEPTION_SPEC_HANDLER,
- ZEND_YIELD_SPEC_CONST_CONST_HANDLER,
- ZEND_YIELD_SPEC_CONST_TMP_HANDLER,
- ZEND_YIELD_SPEC_CONST_VAR_HANDLER,
- ZEND_YIELD_SPEC_CONST_UNUSED_HANDLER,
- ZEND_YIELD_SPEC_CONST_CV_HANDLER,
- ZEND_YIELD_SPEC_TMP_CONST_HANDLER,
- ZEND_YIELD_SPEC_TMP_TMP_HANDLER,
- ZEND_YIELD_SPEC_TMP_VAR_HANDLER,
- ZEND_YIELD_SPEC_TMP_UNUSED_HANDLER,
- ZEND_YIELD_SPEC_TMP_CV_HANDLER,
- ZEND_YIELD_SPEC_VAR_CONST_HANDLER,
- ZEND_YIELD_SPEC_VAR_TMP_HANDLER,
- ZEND_YIELD_SPEC_VAR_VAR_HANDLER,
- ZEND_YIELD_SPEC_VAR_UNUSED_HANDLER,
- ZEND_YIELD_SPEC_VAR_CV_HANDLER,
- ZEND_YIELD_SPEC_UNUSED_CONST_HANDLER,
- ZEND_YIELD_SPEC_UNUSED_TMP_HANDLER,
- ZEND_YIELD_SPEC_UNUSED_VAR_HANDLER,
- ZEND_YIELD_SPEC_UNUSED_UNUSED_HANDLER,
- ZEND_YIELD_SPEC_UNUSED_CV_HANDLER,
- ZEND_YIELD_SPEC_CV_CONST_HANDLER,
- ZEND_YIELD_SPEC_CV_TMP_HANDLER,
- ZEND_YIELD_SPEC_CV_VAR_HANDLER,
- ZEND_YIELD_SPEC_CV_UNUSED_HANDLER,
- ZEND_YIELD_SPEC_CV_CV_HANDLER,
- ZEND_GENERATOR_RETURN_SPEC_CONST_HANDLER,
- ZEND_GENERATOR_RETURN_SPEC_CONST_HANDLER,
- ZEND_GENERATOR_RETURN_SPEC_CONST_HANDLER,
- ZEND_GENERATOR_RETURN_SPEC_CONST_HANDLER,
- ZEND_GENERATOR_RETURN_SPEC_CONST_HANDLER,
- ZEND_GENERATOR_RETURN_SPEC_TMP_HANDLER,
- ZEND_GENERATOR_RETURN_SPEC_TMP_HANDLER,
- ZEND_GENERATOR_RETURN_SPEC_TMP_HANDLER,
- ZEND_GENERATOR_RETURN_SPEC_TMP_HANDLER,
- ZEND_GENERATOR_RETURN_SPEC_TMP_HANDLER,
- ZEND_GENERATOR_RETURN_SPEC_VAR_HANDLER,
- ZEND_GENERATOR_RETURN_SPEC_VAR_HANDLER,
- ZEND_GENERATOR_RETURN_SPEC_VAR_HANDLER,
- ZEND_GENERATOR_RETURN_SPEC_VAR_HANDLER,
- ZEND_GENERATOR_RETURN_SPEC_VAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_GENERATOR_RETURN_SPEC_CV_HANDLER,
- ZEND_GENERATOR_RETURN_SPEC_CV_HANDLER,
- ZEND_GENERATOR_RETURN_SPEC_CV_HANDLER,
- ZEND_GENERATOR_RETURN_SPEC_CV_HANDLER,
- ZEND_GENERATOR_RETURN_SPEC_CV_HANDLER,
- ZEND_FAST_CALL_SPEC_HANDLER,
- ZEND_FAST_CALL_SPEC_HANDLER,
- ZEND_FAST_CALL_SPEC_HANDLER,
- ZEND_FAST_CALL_SPEC_HANDLER,
- ZEND_FAST_CALL_SPEC_HANDLER,
- ZEND_FAST_CALL_SPEC_HANDLER,
- ZEND_FAST_CALL_SPEC_HANDLER,
- ZEND_FAST_CALL_SPEC_HANDLER,
- ZEND_FAST_CALL_SPEC_HANDLER,
- ZEND_FAST_CALL_SPEC_HANDLER,
- ZEND_FAST_CALL_SPEC_HANDLER,
- ZEND_FAST_CALL_SPEC_HANDLER,
- ZEND_FAST_CALL_SPEC_HANDLER,
- ZEND_FAST_CALL_SPEC_HANDLER,
- ZEND_FAST_CALL_SPEC_HANDLER,
- ZEND_FAST_CALL_SPEC_HANDLER,
- ZEND_FAST_CALL_SPEC_HANDLER,
- ZEND_FAST_CALL_SPEC_HANDLER,
- ZEND_FAST_CALL_SPEC_HANDLER,
- ZEND_FAST_CALL_SPEC_HANDLER,
- ZEND_FAST_CALL_SPEC_HANDLER,
- ZEND_FAST_CALL_SPEC_HANDLER,
- ZEND_FAST_CALL_SPEC_HANDLER,
- ZEND_FAST_CALL_SPEC_HANDLER,
- ZEND_FAST_CALL_SPEC_HANDLER,
- ZEND_FAST_RET_SPEC_HANDLER,
- ZEND_FAST_RET_SPEC_HANDLER,
- ZEND_FAST_RET_SPEC_HANDLER,
- ZEND_FAST_RET_SPEC_HANDLER,
- ZEND_FAST_RET_SPEC_HANDLER,
- ZEND_FAST_RET_SPEC_HANDLER,
- ZEND_FAST_RET_SPEC_HANDLER,
- ZEND_FAST_RET_SPEC_HANDLER,
- ZEND_FAST_RET_SPEC_HANDLER,
- ZEND_FAST_RET_SPEC_HANDLER,
- ZEND_FAST_RET_SPEC_HANDLER,
- ZEND_FAST_RET_SPEC_HANDLER,
- ZEND_FAST_RET_SPEC_HANDLER,
- ZEND_FAST_RET_SPEC_HANDLER,
- ZEND_FAST_RET_SPEC_HANDLER,
- ZEND_FAST_RET_SPEC_HANDLER,
- ZEND_FAST_RET_SPEC_HANDLER,
- ZEND_FAST_RET_SPEC_HANDLER,
- ZEND_FAST_RET_SPEC_HANDLER,
- ZEND_FAST_RET_SPEC_HANDLER,
- ZEND_FAST_RET_SPEC_HANDLER,
- ZEND_FAST_RET_SPEC_HANDLER,
- ZEND_FAST_RET_SPEC_HANDLER,
- ZEND_FAST_RET_SPEC_HANDLER,
- ZEND_FAST_RET_SPEC_HANDLER,
- ZEND_RECV_VARIADIC_SPEC_HANDLER,
- ZEND_RECV_VARIADIC_SPEC_HANDLER,
- ZEND_RECV_VARIADIC_SPEC_HANDLER,
- ZEND_RECV_VARIADIC_SPEC_HANDLER,
- ZEND_RECV_VARIADIC_SPEC_HANDLER,
- ZEND_RECV_VARIADIC_SPEC_HANDLER,
- ZEND_RECV_VARIADIC_SPEC_HANDLER,
- ZEND_RECV_VARIADIC_SPEC_HANDLER,
- ZEND_RECV_VARIADIC_SPEC_HANDLER,
- ZEND_RECV_VARIADIC_SPEC_HANDLER,
- ZEND_RECV_VARIADIC_SPEC_HANDLER,
- ZEND_RECV_VARIADIC_SPEC_HANDLER,
- ZEND_RECV_VARIADIC_SPEC_HANDLER,
- ZEND_RECV_VARIADIC_SPEC_HANDLER,
- ZEND_RECV_VARIADIC_SPEC_HANDLER,
- ZEND_RECV_VARIADIC_SPEC_HANDLER,
- ZEND_RECV_VARIADIC_SPEC_HANDLER,
- ZEND_RECV_VARIADIC_SPEC_HANDLER,
- ZEND_RECV_VARIADIC_SPEC_HANDLER,
- ZEND_RECV_VARIADIC_SPEC_HANDLER,
- ZEND_RECV_VARIADIC_SPEC_HANDLER,
- ZEND_RECV_VARIADIC_SPEC_HANDLER,
- ZEND_RECV_VARIADIC_SPEC_HANDLER,
- ZEND_RECV_VARIADIC_SPEC_HANDLER,
- ZEND_RECV_VARIADIC_SPEC_HANDLER,
- ZEND_SEND_UNPACK_SPEC_HANDLER,
- ZEND_SEND_UNPACK_SPEC_HANDLER,
- ZEND_SEND_UNPACK_SPEC_HANDLER,
- ZEND_SEND_UNPACK_SPEC_HANDLER,
- ZEND_SEND_UNPACK_SPEC_HANDLER,
- ZEND_SEND_UNPACK_SPEC_HANDLER,
- ZEND_SEND_UNPACK_SPEC_HANDLER,
- ZEND_SEND_UNPACK_SPEC_HANDLER,
- ZEND_SEND_UNPACK_SPEC_HANDLER,
- ZEND_SEND_UNPACK_SPEC_HANDLER,
- ZEND_SEND_UNPACK_SPEC_HANDLER,
- ZEND_SEND_UNPACK_SPEC_HANDLER,
- ZEND_SEND_UNPACK_SPEC_HANDLER,
- ZEND_SEND_UNPACK_SPEC_HANDLER,
- ZEND_SEND_UNPACK_SPEC_HANDLER,
- ZEND_SEND_UNPACK_SPEC_HANDLER,
- ZEND_SEND_UNPACK_SPEC_HANDLER,
- ZEND_SEND_UNPACK_SPEC_HANDLER,
- ZEND_SEND_UNPACK_SPEC_HANDLER,
- ZEND_SEND_UNPACK_SPEC_HANDLER,
- ZEND_SEND_UNPACK_SPEC_HANDLER,
- ZEND_SEND_UNPACK_SPEC_HANDLER,
- ZEND_SEND_UNPACK_SPEC_HANDLER,
- ZEND_SEND_UNPACK_SPEC_HANDLER,
- ZEND_SEND_UNPACK_SPEC_HANDLER,
- ZEND_POW_SPEC_CONST_CONST_HANDLER,
- ZEND_POW_SPEC_CONST_TMPVAR_HANDLER,
- ZEND_POW_SPEC_CONST_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_POW_SPEC_CONST_CV_HANDLER,
- ZEND_POW_SPEC_TMPVAR_CONST_HANDLER,
- ZEND_POW_SPEC_TMPVAR_TMPVAR_HANDLER,
- ZEND_POW_SPEC_TMPVAR_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_POW_SPEC_TMPVAR_CV_HANDLER,
- ZEND_POW_SPEC_TMPVAR_CONST_HANDLER,
- ZEND_POW_SPEC_TMPVAR_TMPVAR_HANDLER,
- ZEND_POW_SPEC_TMPVAR_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_POW_SPEC_TMPVAR_CV_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_POW_SPEC_CV_CONST_HANDLER,
- ZEND_POW_SPEC_CV_TMPVAR_HANDLER,
- ZEND_POW_SPEC_CV_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_POW_SPEC_CV_CV_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_ASSIGN_POW_SPEC_VAR_CONST_HANDLER,
- ZEND_ASSIGN_POW_SPEC_VAR_TMPVAR_HANDLER,
- ZEND_ASSIGN_POW_SPEC_VAR_TMPVAR_HANDLER,
- ZEND_ASSIGN_POW_SPEC_VAR_UNUSED_HANDLER,
- ZEND_ASSIGN_POW_SPEC_VAR_CV_HANDLER,
- ZEND_ASSIGN_POW_SPEC_UNUSED_CONST_HANDLER,
- ZEND_ASSIGN_POW_SPEC_UNUSED_TMPVAR_HANDLER,
- ZEND_ASSIGN_POW_SPEC_UNUSED_TMPVAR_HANDLER,
- ZEND_ASSIGN_POW_SPEC_UNUSED_UNUSED_HANDLER,
- ZEND_ASSIGN_POW_SPEC_UNUSED_CV_HANDLER,
- ZEND_ASSIGN_POW_SPEC_CV_CONST_HANDLER,
- ZEND_ASSIGN_POW_SPEC_CV_TMPVAR_HANDLER,
- ZEND_ASSIGN_POW_SPEC_CV_TMPVAR_HANDLER,
- ZEND_ASSIGN_POW_SPEC_CV_UNUSED_HANDLER,
- ZEND_ASSIGN_POW_SPEC_CV_CV_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,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_BIND_GLOBAL_SPEC_CV_CONST_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_COALESCE_SPEC_CONST_HANDLER,
- ZEND_COALESCE_SPEC_CONST_HANDLER,
- ZEND_COALESCE_SPEC_CONST_HANDLER,
- ZEND_COALESCE_SPEC_CONST_HANDLER,
- ZEND_COALESCE_SPEC_CONST_HANDLER,
- ZEND_COALESCE_SPEC_TMP_HANDLER,
- ZEND_COALESCE_SPEC_TMP_HANDLER,
- ZEND_COALESCE_SPEC_TMP_HANDLER,
- ZEND_COALESCE_SPEC_TMP_HANDLER,
- ZEND_COALESCE_SPEC_TMP_HANDLER,
- ZEND_COALESCE_SPEC_VAR_HANDLER,
- ZEND_COALESCE_SPEC_VAR_HANDLER,
- ZEND_COALESCE_SPEC_VAR_HANDLER,
- ZEND_COALESCE_SPEC_VAR_HANDLER,
- ZEND_COALESCE_SPEC_VAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_COALESCE_SPEC_CV_HANDLER,
- ZEND_COALESCE_SPEC_CV_HANDLER,
- ZEND_COALESCE_SPEC_CV_HANDLER,
- ZEND_COALESCE_SPEC_CV_HANDLER,
- ZEND_COALESCE_SPEC_CV_HANDLER,
- ZEND_SPACESHIP_SPEC_CONST_CONST_HANDLER,
- ZEND_SPACESHIP_SPEC_CONST_TMPVAR_HANDLER,
- ZEND_SPACESHIP_SPEC_CONST_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_SPACESHIP_SPEC_CONST_CV_HANDLER,
- ZEND_SPACESHIP_SPEC_TMPVAR_CONST_HANDLER,
- ZEND_SPACESHIP_SPEC_TMPVAR_TMPVAR_HANDLER,
- ZEND_SPACESHIP_SPEC_TMPVAR_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_SPACESHIP_SPEC_TMPVAR_CV_HANDLER,
- ZEND_SPACESHIP_SPEC_TMPVAR_CONST_HANDLER,
- ZEND_SPACESHIP_SPEC_TMPVAR_TMPVAR_HANDLER,
- ZEND_SPACESHIP_SPEC_TMPVAR_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_SPACESHIP_SPEC_TMPVAR_CV_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_SPACESHIP_SPEC_CV_CONST_HANDLER,
- ZEND_SPACESHIP_SPEC_CV_TMPVAR_HANDLER,
- ZEND_SPACESHIP_SPEC_CV_TMPVAR_HANDLER,
- ZEND_NULL_HANDLER,
- ZEND_SPACESHIP_SPEC_CV_CV_HANDLER,
- ZEND_DECLARE_ANON_CLASS_SPEC_HANDLER,
- ZEND_DECLARE_ANON_CLASS_SPEC_HANDLER,
- ZEND_DECLARE_ANON_CLASS_SPEC_HANDLER,
- ZEND_DECLARE_ANON_CLASS_SPEC_HANDLER,
- ZEND_DECLARE_ANON_CLASS_SPEC_HANDLER,
- ZEND_DECLARE_ANON_CLASS_SPEC_HANDLER,
- ZEND_DECLARE_ANON_CLASS_SPEC_HANDLER,
- ZEND_DECLARE_ANON_CLASS_SPEC_HANDLER,
- ZEND_DECLARE_ANON_CLASS_SPEC_HANDLER,
- ZEND_DECLARE_ANON_CLASS_SPEC_HANDLER,
- ZEND_DECLARE_ANON_CLASS_SPEC_HANDLER,
- ZEND_DECLARE_ANON_CLASS_SPEC_HANDLER,
- ZEND_DECLARE_ANON_CLASS_SPEC_HANDLER,
- ZEND_DECLARE_ANON_CLASS_SPEC_HANDLER,
- ZEND_DECLARE_ANON_CLASS_SPEC_HANDLER,
- ZEND_DECLARE_ANON_CLASS_SPEC_HANDLER,
- ZEND_DECLARE_ANON_CLASS_SPEC_HANDLER,
- ZEND_DECLARE_ANON_CLASS_SPEC_HANDLER,
- ZEND_DECLARE_ANON_CLASS_SPEC_HANDLER,
- ZEND_DECLARE_ANON_CLASS_SPEC_HANDLER,
- ZEND_DECLARE_ANON_CLASS_SPEC_HANDLER,
- ZEND_DECLARE_ANON_CLASS_SPEC_HANDLER,
- ZEND_DECLARE_ANON_CLASS_SPEC_HANDLER,
- ZEND_DECLARE_ANON_CLASS_SPEC_HANDLER,
- ZEND_DECLARE_ANON_CLASS_SPEC_HANDLER,
- ZEND_DECLARE_ANON_INHERITED_CLASS_SPEC_HANDLER,
- ZEND_DECLARE_ANON_INHERITED_CLASS_SPEC_HANDLER,
- ZEND_DECLARE_ANON_INHERITED_CLASS_SPEC_HANDLER,
- ZEND_DECLARE_ANON_INHERITED_CLASS_SPEC_HANDLER,
- ZEND_DECLARE_ANON_INHERITED_CLASS_SPEC_HANDLER,
- ZEND_DECLARE_ANON_INHERITED_CLASS_SPEC_HANDLER,
- ZEND_DECLARE_ANON_INHERITED_CLASS_SPEC_HANDLER,
- ZEND_DECLARE_ANON_INHERITED_CLASS_SPEC_HANDLER,
- ZEND_DECLARE_ANON_INHERITED_CLASS_SPEC_HANDLER,
- ZEND_DECLARE_ANON_INHERITED_CLASS_SPEC_HANDLER,
- ZEND_DECLARE_ANON_INHERITED_CLASS_SPEC_HANDLER,
- ZEND_DECLARE_ANON_INHERITED_CLASS_SPEC_HANDLER,
- ZEND_DECLARE_ANON_INHERITED_CLASS_SPEC_HANDLER,
- ZEND_DECLARE_ANON_INHERITED_CLASS_SPEC_HANDLER,
- ZEND_DECLARE_ANON_INHERITED_CLASS_SPEC_HANDLER,
- ZEND_DECLARE_ANON_INHERITED_CLASS_SPEC_HANDLER,
- ZEND_DECLARE_ANON_INHERITED_CLASS_SPEC_HANDLER,
- ZEND_DECLARE_ANON_INHERITED_CLASS_SPEC_HANDLER,
- ZEND_DECLARE_ANON_INHERITED_CLASS_SPEC_HANDLER,
- ZEND_DECLARE_ANON_INHERITED_CLASS_SPEC_HANDLER,
- ZEND_DECLARE_ANON_INHERITED_CLASS_SPEC_HANDLER,
- ZEND_DECLARE_ANON_INHERITED_CLASS_SPEC_HANDLER,
- ZEND_DECLARE_ANON_INHERITED_CLASS_SPEC_HANDLER,
- ZEND_DECLARE_ANON_INHERITED_CLASS_SPEC_HANDLER,
- ZEND_DECLARE_ANON_INHERITED_CLASS_SPEC_HANDLER,
- ZEND_NULL_HANDLER
- };
- zend_opcode_handlers = labels;
+ static const void *labels[] = {
+ ZEND_NOP_SPEC_HANDLER,
+ ZEND_ADD_SPEC_CONST_CONST_HANDLER,
+ ZEND_ADD_SPEC_CONST_TMPVAR_HANDLER,
+ ZEND_ADD_SPEC_CONST_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ADD_SPEC_CONST_CV_HANDLER,
+ ZEND_ADD_SPEC_TMPVAR_CONST_HANDLER,
+ ZEND_ADD_SPEC_TMPVAR_TMPVAR_HANDLER,
+ ZEND_ADD_SPEC_TMPVAR_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ADD_SPEC_TMPVAR_CV_HANDLER,
+ ZEND_ADD_SPEC_TMPVAR_CONST_HANDLER,
+ ZEND_ADD_SPEC_TMPVAR_TMPVAR_HANDLER,
+ ZEND_ADD_SPEC_TMPVAR_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ADD_SPEC_TMPVAR_CV_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ADD_SPEC_CV_CONST_HANDLER,
+ ZEND_ADD_SPEC_CV_TMPVAR_HANDLER,
+ ZEND_ADD_SPEC_CV_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ADD_SPEC_CV_CV_HANDLER,
+ ZEND_SUB_SPEC_CONST_CONST_HANDLER,
+ ZEND_SUB_SPEC_CONST_TMPVAR_HANDLER,
+ ZEND_SUB_SPEC_CONST_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_SUB_SPEC_CONST_CV_HANDLER,
+ ZEND_SUB_SPEC_TMPVAR_CONST_HANDLER,
+ ZEND_SUB_SPEC_TMPVAR_TMPVAR_HANDLER,
+ ZEND_SUB_SPEC_TMPVAR_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_SUB_SPEC_TMPVAR_CV_HANDLER,
+ ZEND_SUB_SPEC_TMPVAR_CONST_HANDLER,
+ ZEND_SUB_SPEC_TMPVAR_TMPVAR_HANDLER,
+ ZEND_SUB_SPEC_TMPVAR_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_SUB_SPEC_TMPVAR_CV_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_SUB_SPEC_CV_CONST_HANDLER,
+ ZEND_SUB_SPEC_CV_TMPVAR_HANDLER,
+ ZEND_SUB_SPEC_CV_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_SUB_SPEC_CV_CV_HANDLER,
+ ZEND_MUL_SPEC_CONST_CONST_HANDLER,
+ ZEND_MUL_SPEC_CONST_TMPVAR_HANDLER,
+ ZEND_MUL_SPEC_CONST_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_MUL_SPEC_CONST_CV_HANDLER,
+ ZEND_MUL_SPEC_TMPVAR_CONST_HANDLER,
+ ZEND_MUL_SPEC_TMPVAR_TMPVAR_HANDLER,
+ ZEND_MUL_SPEC_TMPVAR_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_MUL_SPEC_TMPVAR_CV_HANDLER,
+ ZEND_MUL_SPEC_TMPVAR_CONST_HANDLER,
+ ZEND_MUL_SPEC_TMPVAR_TMPVAR_HANDLER,
+ ZEND_MUL_SPEC_TMPVAR_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_MUL_SPEC_TMPVAR_CV_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_MUL_SPEC_CV_CONST_HANDLER,
+ ZEND_MUL_SPEC_CV_TMPVAR_HANDLER,
+ ZEND_MUL_SPEC_CV_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_MUL_SPEC_CV_CV_HANDLER,
+ ZEND_DIV_SPEC_CONST_CONST_HANDLER,
+ ZEND_DIV_SPEC_CONST_TMPVAR_HANDLER,
+ ZEND_DIV_SPEC_CONST_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_DIV_SPEC_CONST_CV_HANDLER,
+ ZEND_DIV_SPEC_TMPVAR_CONST_HANDLER,
+ ZEND_DIV_SPEC_TMPVAR_TMPVAR_HANDLER,
+ ZEND_DIV_SPEC_TMPVAR_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_DIV_SPEC_TMPVAR_CV_HANDLER,
+ ZEND_DIV_SPEC_TMPVAR_CONST_HANDLER,
+ ZEND_DIV_SPEC_TMPVAR_TMPVAR_HANDLER,
+ ZEND_DIV_SPEC_TMPVAR_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_DIV_SPEC_TMPVAR_CV_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_DIV_SPEC_CV_CONST_HANDLER,
+ ZEND_DIV_SPEC_CV_TMPVAR_HANDLER,
+ ZEND_DIV_SPEC_CV_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_DIV_SPEC_CV_CV_HANDLER,
+ ZEND_MOD_SPEC_CONST_CONST_HANDLER,
+ ZEND_MOD_SPEC_CONST_TMPVAR_HANDLER,
+ ZEND_MOD_SPEC_CONST_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_MOD_SPEC_CONST_CV_HANDLER,
+ ZEND_MOD_SPEC_TMPVAR_CONST_HANDLER,
+ ZEND_MOD_SPEC_TMPVAR_TMPVAR_HANDLER,
+ ZEND_MOD_SPEC_TMPVAR_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_MOD_SPEC_TMPVAR_CV_HANDLER,
+ ZEND_MOD_SPEC_TMPVAR_CONST_HANDLER,
+ ZEND_MOD_SPEC_TMPVAR_TMPVAR_HANDLER,
+ ZEND_MOD_SPEC_TMPVAR_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_MOD_SPEC_TMPVAR_CV_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_MOD_SPEC_CV_CONST_HANDLER,
+ ZEND_MOD_SPEC_CV_TMPVAR_HANDLER,
+ ZEND_MOD_SPEC_CV_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_MOD_SPEC_CV_CV_HANDLER,
+ ZEND_SL_SPEC_CONST_CONST_HANDLER,
+ ZEND_SL_SPEC_CONST_TMPVAR_HANDLER,
+ ZEND_SL_SPEC_CONST_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_SL_SPEC_CONST_CV_HANDLER,
+ ZEND_SL_SPEC_TMPVAR_CONST_HANDLER,
+ ZEND_SL_SPEC_TMPVAR_TMPVAR_HANDLER,
+ ZEND_SL_SPEC_TMPVAR_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_SL_SPEC_TMPVAR_CV_HANDLER,
+ ZEND_SL_SPEC_TMPVAR_CONST_HANDLER,
+ ZEND_SL_SPEC_TMPVAR_TMPVAR_HANDLER,
+ ZEND_SL_SPEC_TMPVAR_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_SL_SPEC_TMPVAR_CV_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_SL_SPEC_CV_CONST_HANDLER,
+ ZEND_SL_SPEC_CV_TMPVAR_HANDLER,
+ ZEND_SL_SPEC_CV_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_SL_SPEC_CV_CV_HANDLER,
+ ZEND_SR_SPEC_CONST_CONST_HANDLER,
+ ZEND_SR_SPEC_CONST_TMPVAR_HANDLER,
+ ZEND_SR_SPEC_CONST_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_SR_SPEC_CONST_CV_HANDLER,
+ ZEND_SR_SPEC_TMPVAR_CONST_HANDLER,
+ ZEND_SR_SPEC_TMPVAR_TMPVAR_HANDLER,
+ ZEND_SR_SPEC_TMPVAR_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_SR_SPEC_TMPVAR_CV_HANDLER,
+ ZEND_SR_SPEC_TMPVAR_CONST_HANDLER,
+ ZEND_SR_SPEC_TMPVAR_TMPVAR_HANDLER,
+ ZEND_SR_SPEC_TMPVAR_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_SR_SPEC_TMPVAR_CV_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_SR_SPEC_CV_CONST_HANDLER,
+ ZEND_SR_SPEC_CV_TMPVAR_HANDLER,
+ ZEND_SR_SPEC_CV_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_SR_SPEC_CV_CV_HANDLER,
+ ZEND_CONCAT_SPEC_CONST_CONST_HANDLER,
+ ZEND_CONCAT_SPEC_CONST_TMPVAR_HANDLER,
+ ZEND_CONCAT_SPEC_CONST_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_CONCAT_SPEC_CONST_CV_HANDLER,
+ ZEND_CONCAT_SPEC_TMPVAR_CONST_HANDLER,
+ ZEND_CONCAT_SPEC_TMPVAR_TMPVAR_HANDLER,
+ ZEND_CONCAT_SPEC_TMPVAR_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_CONCAT_SPEC_TMPVAR_CV_HANDLER,
+ ZEND_CONCAT_SPEC_TMPVAR_CONST_HANDLER,
+ ZEND_CONCAT_SPEC_TMPVAR_TMPVAR_HANDLER,
+ ZEND_CONCAT_SPEC_TMPVAR_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_CONCAT_SPEC_TMPVAR_CV_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_CONCAT_SPEC_CV_CONST_HANDLER,
+ ZEND_CONCAT_SPEC_CV_TMPVAR_HANDLER,
+ ZEND_CONCAT_SPEC_CV_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_CONCAT_SPEC_CV_CV_HANDLER,
+ ZEND_BW_OR_SPEC_CONST_CONST_HANDLER,
+ ZEND_BW_OR_SPEC_CONST_TMPVAR_HANDLER,
+ ZEND_BW_OR_SPEC_CONST_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_BW_OR_SPEC_CONST_CV_HANDLER,
+ ZEND_BW_OR_SPEC_TMPVAR_CONST_HANDLER,
+ ZEND_BW_OR_SPEC_TMPVAR_TMPVAR_HANDLER,
+ ZEND_BW_OR_SPEC_TMPVAR_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_BW_OR_SPEC_TMPVAR_CV_HANDLER,
+ ZEND_BW_OR_SPEC_TMPVAR_CONST_HANDLER,
+ ZEND_BW_OR_SPEC_TMPVAR_TMPVAR_HANDLER,
+ ZEND_BW_OR_SPEC_TMPVAR_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_BW_OR_SPEC_TMPVAR_CV_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_BW_OR_SPEC_CV_CONST_HANDLER,
+ ZEND_BW_OR_SPEC_CV_TMPVAR_HANDLER,
+ ZEND_BW_OR_SPEC_CV_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_BW_OR_SPEC_CV_CV_HANDLER,
+ ZEND_BW_AND_SPEC_CONST_CONST_HANDLER,
+ ZEND_BW_AND_SPEC_CONST_TMPVAR_HANDLER,
+ ZEND_BW_AND_SPEC_CONST_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_BW_AND_SPEC_CONST_CV_HANDLER,
+ ZEND_BW_AND_SPEC_TMPVAR_CONST_HANDLER,
+ ZEND_BW_AND_SPEC_TMPVAR_TMPVAR_HANDLER,
+ ZEND_BW_AND_SPEC_TMPVAR_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_BW_AND_SPEC_TMPVAR_CV_HANDLER,
+ ZEND_BW_AND_SPEC_TMPVAR_CONST_HANDLER,
+ ZEND_BW_AND_SPEC_TMPVAR_TMPVAR_HANDLER,
+ ZEND_BW_AND_SPEC_TMPVAR_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_BW_AND_SPEC_TMPVAR_CV_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_BW_AND_SPEC_CV_CONST_HANDLER,
+ ZEND_BW_AND_SPEC_CV_TMPVAR_HANDLER,
+ ZEND_BW_AND_SPEC_CV_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_BW_AND_SPEC_CV_CV_HANDLER,
+ ZEND_BW_XOR_SPEC_CONST_CONST_HANDLER,
+ ZEND_BW_XOR_SPEC_CONST_TMPVAR_HANDLER,
+ ZEND_BW_XOR_SPEC_CONST_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_BW_XOR_SPEC_CONST_CV_HANDLER,
+ ZEND_BW_XOR_SPEC_TMPVAR_CONST_HANDLER,
+ ZEND_BW_XOR_SPEC_TMPVAR_TMPVAR_HANDLER,
+ ZEND_BW_XOR_SPEC_TMPVAR_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_BW_XOR_SPEC_TMPVAR_CV_HANDLER,
+ ZEND_BW_XOR_SPEC_TMPVAR_CONST_HANDLER,
+ ZEND_BW_XOR_SPEC_TMPVAR_TMPVAR_HANDLER,
+ ZEND_BW_XOR_SPEC_TMPVAR_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_BW_XOR_SPEC_TMPVAR_CV_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_BW_XOR_SPEC_CV_CONST_HANDLER,
+ ZEND_BW_XOR_SPEC_CV_TMPVAR_HANDLER,
+ ZEND_BW_XOR_SPEC_CV_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_BW_XOR_SPEC_CV_CV_HANDLER,
+ ZEND_BW_NOT_SPEC_CONST_HANDLER,
+ ZEND_BW_NOT_SPEC_TMPVAR_HANDLER,
+ ZEND_BW_NOT_SPEC_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_BW_NOT_SPEC_CV_HANDLER,
+ ZEND_BOOL_NOT_SPEC_CONST_HANDLER,
+ ZEND_BOOL_NOT_SPEC_TMPVAR_HANDLER,
+ ZEND_BOOL_NOT_SPEC_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_BOOL_NOT_SPEC_CV_HANDLER,
+ ZEND_BOOL_XOR_SPEC_CONST_CONST_HANDLER,
+ ZEND_BOOL_XOR_SPEC_CONST_TMPVAR_HANDLER,
+ ZEND_BOOL_XOR_SPEC_CONST_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_BOOL_XOR_SPEC_CONST_CV_HANDLER,
+ ZEND_BOOL_XOR_SPEC_TMPVAR_CONST_HANDLER,
+ ZEND_BOOL_XOR_SPEC_TMPVAR_TMPVAR_HANDLER,
+ ZEND_BOOL_XOR_SPEC_TMPVAR_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_BOOL_XOR_SPEC_TMPVAR_CV_HANDLER,
+ ZEND_BOOL_XOR_SPEC_TMPVAR_CONST_HANDLER,
+ ZEND_BOOL_XOR_SPEC_TMPVAR_TMPVAR_HANDLER,
+ ZEND_BOOL_XOR_SPEC_TMPVAR_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_BOOL_XOR_SPEC_TMPVAR_CV_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_BOOL_XOR_SPEC_CV_CONST_HANDLER,
+ ZEND_BOOL_XOR_SPEC_CV_TMPVAR_HANDLER,
+ ZEND_BOOL_XOR_SPEC_CV_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_BOOL_XOR_SPEC_CV_CV_HANDLER,
+ ZEND_IS_IDENTICAL_SPEC_CONST_CONST_HANDLER,
+ ZEND_IS_IDENTICAL_SPEC_CONST_TMP_HANDLER,
+ ZEND_IS_IDENTICAL_SPEC_CONST_VAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_IS_IDENTICAL_SPEC_CONST_CV_HANDLER,
+ ZEND_IS_IDENTICAL_SPEC_TMP_CONST_HANDLER,
+ ZEND_IS_IDENTICAL_SPEC_TMP_TMP_HANDLER,
+ ZEND_IS_IDENTICAL_SPEC_TMP_VAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_IS_IDENTICAL_SPEC_TMP_CV_HANDLER,
+ ZEND_IS_IDENTICAL_SPEC_VAR_CONST_HANDLER,
+ ZEND_IS_IDENTICAL_SPEC_VAR_TMP_HANDLER,
+ ZEND_IS_IDENTICAL_SPEC_VAR_VAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_IS_IDENTICAL_SPEC_VAR_CV_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_IS_IDENTICAL_SPEC_CV_CONST_HANDLER,
+ ZEND_IS_IDENTICAL_SPEC_CV_TMP_HANDLER,
+ ZEND_IS_IDENTICAL_SPEC_CV_VAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_IS_IDENTICAL_SPEC_CV_CV_HANDLER,
+ ZEND_IS_NOT_IDENTICAL_SPEC_CONST_CONST_HANDLER,
+ ZEND_IS_NOT_IDENTICAL_SPEC_CONST_TMP_HANDLER,
+ ZEND_IS_NOT_IDENTICAL_SPEC_CONST_VAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_IS_NOT_IDENTICAL_SPEC_CONST_CV_HANDLER,
+ ZEND_IS_NOT_IDENTICAL_SPEC_TMP_CONST_HANDLER,
+ ZEND_IS_NOT_IDENTICAL_SPEC_TMP_TMP_HANDLER,
+ ZEND_IS_NOT_IDENTICAL_SPEC_TMP_VAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_IS_NOT_IDENTICAL_SPEC_TMP_CV_HANDLER,
+ ZEND_IS_NOT_IDENTICAL_SPEC_VAR_CONST_HANDLER,
+ ZEND_IS_NOT_IDENTICAL_SPEC_VAR_TMP_HANDLER,
+ ZEND_IS_NOT_IDENTICAL_SPEC_VAR_VAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_IS_NOT_IDENTICAL_SPEC_VAR_CV_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_IS_NOT_IDENTICAL_SPEC_CV_CONST_HANDLER,
+ ZEND_IS_NOT_IDENTICAL_SPEC_CV_TMP_HANDLER,
+ ZEND_IS_NOT_IDENTICAL_SPEC_CV_VAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_IS_NOT_IDENTICAL_SPEC_CV_CV_HANDLER,
+ ZEND_IS_EQUAL_SPEC_CONST_CONST_HANDLER,
+ ZEND_IS_EQUAL_SPEC_CONST_TMPVAR_HANDLER,
+ ZEND_IS_EQUAL_SPEC_CONST_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_IS_EQUAL_SPEC_CONST_CV_HANDLER,
+ ZEND_IS_EQUAL_SPEC_TMPVAR_CONST_HANDLER,
+ ZEND_IS_EQUAL_SPEC_TMPVAR_TMPVAR_HANDLER,
+ ZEND_IS_EQUAL_SPEC_TMPVAR_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_IS_EQUAL_SPEC_TMPVAR_CV_HANDLER,
+ ZEND_IS_EQUAL_SPEC_TMPVAR_CONST_HANDLER,
+ ZEND_IS_EQUAL_SPEC_TMPVAR_TMPVAR_HANDLER,
+ ZEND_IS_EQUAL_SPEC_TMPVAR_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_IS_EQUAL_SPEC_TMPVAR_CV_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_IS_EQUAL_SPEC_CV_CONST_HANDLER,
+ ZEND_IS_EQUAL_SPEC_CV_TMPVAR_HANDLER,
+ ZEND_IS_EQUAL_SPEC_CV_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_IS_EQUAL_SPEC_CV_CV_HANDLER,
+ ZEND_IS_NOT_EQUAL_SPEC_CONST_CONST_HANDLER,
+ ZEND_IS_NOT_EQUAL_SPEC_CONST_TMPVAR_HANDLER,
+ ZEND_IS_NOT_EQUAL_SPEC_CONST_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_IS_NOT_EQUAL_SPEC_CONST_CV_HANDLER,
+ ZEND_IS_NOT_EQUAL_SPEC_TMPVAR_CONST_HANDLER,
+ ZEND_IS_NOT_EQUAL_SPEC_TMPVAR_TMPVAR_HANDLER,
+ ZEND_IS_NOT_EQUAL_SPEC_TMPVAR_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_IS_NOT_EQUAL_SPEC_TMPVAR_CV_HANDLER,
+ ZEND_IS_NOT_EQUAL_SPEC_TMPVAR_CONST_HANDLER,
+ ZEND_IS_NOT_EQUAL_SPEC_TMPVAR_TMPVAR_HANDLER,
+ ZEND_IS_NOT_EQUAL_SPEC_TMPVAR_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_IS_NOT_EQUAL_SPEC_TMPVAR_CV_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_IS_NOT_EQUAL_SPEC_CV_CONST_HANDLER,
+ ZEND_IS_NOT_EQUAL_SPEC_CV_TMPVAR_HANDLER,
+ ZEND_IS_NOT_EQUAL_SPEC_CV_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_IS_NOT_EQUAL_SPEC_CV_CV_HANDLER,
+ ZEND_IS_SMALLER_SPEC_CONST_CONST_HANDLER,
+ ZEND_IS_SMALLER_SPEC_CONST_TMPVAR_HANDLER,
+ ZEND_IS_SMALLER_SPEC_CONST_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_IS_SMALLER_SPEC_CONST_CV_HANDLER,
+ ZEND_IS_SMALLER_SPEC_TMPVAR_CONST_HANDLER,
+ ZEND_IS_SMALLER_SPEC_TMPVAR_TMPVAR_HANDLER,
+ ZEND_IS_SMALLER_SPEC_TMPVAR_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_IS_SMALLER_SPEC_TMPVAR_CV_HANDLER,
+ ZEND_IS_SMALLER_SPEC_TMPVAR_CONST_HANDLER,
+ ZEND_IS_SMALLER_SPEC_TMPVAR_TMPVAR_HANDLER,
+ ZEND_IS_SMALLER_SPEC_TMPVAR_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_IS_SMALLER_SPEC_TMPVAR_CV_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_IS_SMALLER_SPEC_CV_CONST_HANDLER,
+ ZEND_IS_SMALLER_SPEC_CV_TMPVAR_HANDLER,
+ ZEND_IS_SMALLER_SPEC_CV_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_IS_SMALLER_SPEC_CV_CV_HANDLER,
+ ZEND_IS_SMALLER_OR_EQUAL_SPEC_CONST_CONST_HANDLER,
+ ZEND_IS_SMALLER_OR_EQUAL_SPEC_CONST_TMPVAR_HANDLER,
+ ZEND_IS_SMALLER_OR_EQUAL_SPEC_CONST_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_IS_SMALLER_OR_EQUAL_SPEC_CONST_CV_HANDLER,
+ ZEND_IS_SMALLER_OR_EQUAL_SPEC_TMPVAR_CONST_HANDLER,
+ ZEND_IS_SMALLER_OR_EQUAL_SPEC_TMPVAR_TMPVAR_HANDLER,
+ ZEND_IS_SMALLER_OR_EQUAL_SPEC_TMPVAR_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_IS_SMALLER_OR_EQUAL_SPEC_TMPVAR_CV_HANDLER,
+ ZEND_IS_SMALLER_OR_EQUAL_SPEC_TMPVAR_CONST_HANDLER,
+ ZEND_IS_SMALLER_OR_EQUAL_SPEC_TMPVAR_TMPVAR_HANDLER,
+ ZEND_IS_SMALLER_OR_EQUAL_SPEC_TMPVAR_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_IS_SMALLER_OR_EQUAL_SPEC_TMPVAR_CV_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_IS_SMALLER_OR_EQUAL_SPEC_CV_CONST_HANDLER,
+ ZEND_IS_SMALLER_OR_EQUAL_SPEC_CV_TMPVAR_HANDLER,
+ ZEND_IS_SMALLER_OR_EQUAL_SPEC_CV_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_IS_SMALLER_OR_EQUAL_SPEC_CV_CV_HANDLER,
+ ZEND_CAST_SPEC_CONST_HANDLER,
+ ZEND_CAST_SPEC_TMP_HANDLER,
+ ZEND_CAST_SPEC_VAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_CAST_SPEC_CV_HANDLER,
+ ZEND_QM_ASSIGN_SPEC_CONST_HANDLER,
+ ZEND_QM_ASSIGN_SPEC_TMP_HANDLER,
+ ZEND_QM_ASSIGN_SPEC_VAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_QM_ASSIGN_SPEC_CV_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,
+ 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,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_ADD_SPEC_VAR_CONST_HANDLER,
+ ZEND_ASSIGN_ADD_SPEC_VAR_CONST_DIM_HANDLER,
+ ZEND_ASSIGN_ADD_SPEC_VAR_CONST_OBJ_HANDLER,
+ ZEND_ASSIGN_ADD_SPEC_VAR_TMPVAR_HANDLER,
+ ZEND_ASSIGN_ADD_SPEC_VAR_TMPVAR_DIM_HANDLER,
+ ZEND_ASSIGN_ADD_SPEC_VAR_TMPVAR_OBJ_HANDLER,
+ ZEND_ASSIGN_ADD_SPEC_VAR_TMPVAR_HANDLER,
+ ZEND_ASSIGN_ADD_SPEC_VAR_TMPVAR_DIM_HANDLER,
+ ZEND_ASSIGN_ADD_SPEC_VAR_TMPVAR_OBJ_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_ADD_SPEC_VAR_UNUSED_DIM_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_ADD_SPEC_VAR_CV_HANDLER,
+ ZEND_ASSIGN_ADD_SPEC_VAR_CV_DIM_HANDLER,
+ ZEND_ASSIGN_ADD_SPEC_VAR_CV_OBJ_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_ADD_SPEC_UNUSED_CONST_OBJ_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_ADD_SPEC_UNUSED_TMPVAR_OBJ_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_ADD_SPEC_UNUSED_TMPVAR_OBJ_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_ADD_SPEC_UNUSED_CV_OBJ_HANDLER,
+ ZEND_ASSIGN_ADD_SPEC_CV_CONST_HANDLER,
+ ZEND_ASSIGN_ADD_SPEC_CV_CONST_DIM_HANDLER,
+ ZEND_ASSIGN_ADD_SPEC_CV_CONST_OBJ_HANDLER,
+ ZEND_ASSIGN_ADD_SPEC_CV_TMPVAR_HANDLER,
+ ZEND_ASSIGN_ADD_SPEC_CV_TMPVAR_DIM_HANDLER,
+ ZEND_ASSIGN_ADD_SPEC_CV_TMPVAR_OBJ_HANDLER,
+ ZEND_ASSIGN_ADD_SPEC_CV_TMPVAR_HANDLER,
+ ZEND_ASSIGN_ADD_SPEC_CV_TMPVAR_DIM_HANDLER,
+ ZEND_ASSIGN_ADD_SPEC_CV_TMPVAR_OBJ_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_ADD_SPEC_CV_UNUSED_DIM_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_ADD_SPEC_CV_CV_HANDLER,
+ ZEND_ASSIGN_ADD_SPEC_CV_CV_DIM_HANDLER,
+ ZEND_ASSIGN_ADD_SPEC_CV_CV_OBJ_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,
+ 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,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_SUB_SPEC_VAR_CONST_HANDLER,
+ ZEND_ASSIGN_SUB_SPEC_VAR_CONST_DIM_HANDLER,
+ ZEND_ASSIGN_SUB_SPEC_VAR_CONST_OBJ_HANDLER,
+ ZEND_ASSIGN_SUB_SPEC_VAR_TMPVAR_HANDLER,
+ ZEND_ASSIGN_SUB_SPEC_VAR_TMPVAR_DIM_HANDLER,
+ ZEND_ASSIGN_SUB_SPEC_VAR_TMPVAR_OBJ_HANDLER,
+ ZEND_ASSIGN_SUB_SPEC_VAR_TMPVAR_HANDLER,
+ ZEND_ASSIGN_SUB_SPEC_VAR_TMPVAR_DIM_HANDLER,
+ ZEND_ASSIGN_SUB_SPEC_VAR_TMPVAR_OBJ_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_SUB_SPEC_VAR_UNUSED_DIM_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_SUB_SPEC_VAR_CV_HANDLER,
+ ZEND_ASSIGN_SUB_SPEC_VAR_CV_DIM_HANDLER,
+ ZEND_ASSIGN_SUB_SPEC_VAR_CV_OBJ_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_SUB_SPEC_UNUSED_CONST_OBJ_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_SUB_SPEC_UNUSED_TMPVAR_OBJ_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_SUB_SPEC_UNUSED_TMPVAR_OBJ_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_SUB_SPEC_UNUSED_CV_OBJ_HANDLER,
+ ZEND_ASSIGN_SUB_SPEC_CV_CONST_HANDLER,
+ ZEND_ASSIGN_SUB_SPEC_CV_CONST_DIM_HANDLER,
+ ZEND_ASSIGN_SUB_SPEC_CV_CONST_OBJ_HANDLER,
+ ZEND_ASSIGN_SUB_SPEC_CV_TMPVAR_HANDLER,
+ ZEND_ASSIGN_SUB_SPEC_CV_TMPVAR_DIM_HANDLER,
+ ZEND_ASSIGN_SUB_SPEC_CV_TMPVAR_OBJ_HANDLER,
+ ZEND_ASSIGN_SUB_SPEC_CV_TMPVAR_HANDLER,
+ ZEND_ASSIGN_SUB_SPEC_CV_TMPVAR_DIM_HANDLER,
+ ZEND_ASSIGN_SUB_SPEC_CV_TMPVAR_OBJ_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_SUB_SPEC_CV_UNUSED_DIM_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_SUB_SPEC_CV_CV_HANDLER,
+ ZEND_ASSIGN_SUB_SPEC_CV_CV_DIM_HANDLER,
+ ZEND_ASSIGN_SUB_SPEC_CV_CV_OBJ_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,
+ 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,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_MUL_SPEC_VAR_CONST_HANDLER,
+ ZEND_ASSIGN_MUL_SPEC_VAR_CONST_DIM_HANDLER,
+ ZEND_ASSIGN_MUL_SPEC_VAR_CONST_OBJ_HANDLER,
+ ZEND_ASSIGN_MUL_SPEC_VAR_TMPVAR_HANDLER,
+ ZEND_ASSIGN_MUL_SPEC_VAR_TMPVAR_DIM_HANDLER,
+ ZEND_ASSIGN_MUL_SPEC_VAR_TMPVAR_OBJ_HANDLER,
+ ZEND_ASSIGN_MUL_SPEC_VAR_TMPVAR_HANDLER,
+ ZEND_ASSIGN_MUL_SPEC_VAR_TMPVAR_DIM_HANDLER,
+ ZEND_ASSIGN_MUL_SPEC_VAR_TMPVAR_OBJ_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_MUL_SPEC_VAR_UNUSED_DIM_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_MUL_SPEC_VAR_CV_HANDLER,
+ ZEND_ASSIGN_MUL_SPEC_VAR_CV_DIM_HANDLER,
+ ZEND_ASSIGN_MUL_SPEC_VAR_CV_OBJ_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_MUL_SPEC_UNUSED_CONST_OBJ_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_MUL_SPEC_UNUSED_TMPVAR_OBJ_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_MUL_SPEC_UNUSED_TMPVAR_OBJ_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_MUL_SPEC_UNUSED_CV_OBJ_HANDLER,
+ ZEND_ASSIGN_MUL_SPEC_CV_CONST_HANDLER,
+ ZEND_ASSIGN_MUL_SPEC_CV_CONST_DIM_HANDLER,
+ ZEND_ASSIGN_MUL_SPEC_CV_CONST_OBJ_HANDLER,
+ ZEND_ASSIGN_MUL_SPEC_CV_TMPVAR_HANDLER,
+ ZEND_ASSIGN_MUL_SPEC_CV_TMPVAR_DIM_HANDLER,
+ ZEND_ASSIGN_MUL_SPEC_CV_TMPVAR_OBJ_HANDLER,
+ ZEND_ASSIGN_MUL_SPEC_CV_TMPVAR_HANDLER,
+ ZEND_ASSIGN_MUL_SPEC_CV_TMPVAR_DIM_HANDLER,
+ ZEND_ASSIGN_MUL_SPEC_CV_TMPVAR_OBJ_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_MUL_SPEC_CV_UNUSED_DIM_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_MUL_SPEC_CV_CV_HANDLER,
+ ZEND_ASSIGN_MUL_SPEC_CV_CV_DIM_HANDLER,
+ ZEND_ASSIGN_MUL_SPEC_CV_CV_OBJ_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,
+ 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,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_DIV_SPEC_VAR_CONST_HANDLER,
+ ZEND_ASSIGN_DIV_SPEC_VAR_CONST_DIM_HANDLER,
+ ZEND_ASSIGN_DIV_SPEC_VAR_CONST_OBJ_HANDLER,
+ ZEND_ASSIGN_DIV_SPEC_VAR_TMPVAR_HANDLER,
+ ZEND_ASSIGN_DIV_SPEC_VAR_TMPVAR_DIM_HANDLER,
+ ZEND_ASSIGN_DIV_SPEC_VAR_TMPVAR_OBJ_HANDLER,
+ ZEND_ASSIGN_DIV_SPEC_VAR_TMPVAR_HANDLER,
+ ZEND_ASSIGN_DIV_SPEC_VAR_TMPVAR_DIM_HANDLER,
+ ZEND_ASSIGN_DIV_SPEC_VAR_TMPVAR_OBJ_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_DIV_SPEC_VAR_UNUSED_DIM_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_DIV_SPEC_VAR_CV_HANDLER,
+ ZEND_ASSIGN_DIV_SPEC_VAR_CV_DIM_HANDLER,
+ ZEND_ASSIGN_DIV_SPEC_VAR_CV_OBJ_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_DIV_SPEC_UNUSED_CONST_OBJ_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_DIV_SPEC_UNUSED_TMPVAR_OBJ_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_DIV_SPEC_UNUSED_TMPVAR_OBJ_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_DIV_SPEC_UNUSED_CV_OBJ_HANDLER,
+ ZEND_ASSIGN_DIV_SPEC_CV_CONST_HANDLER,
+ ZEND_ASSIGN_DIV_SPEC_CV_CONST_DIM_HANDLER,
+ ZEND_ASSIGN_DIV_SPEC_CV_CONST_OBJ_HANDLER,
+ ZEND_ASSIGN_DIV_SPEC_CV_TMPVAR_HANDLER,
+ ZEND_ASSIGN_DIV_SPEC_CV_TMPVAR_DIM_HANDLER,
+ ZEND_ASSIGN_DIV_SPEC_CV_TMPVAR_OBJ_HANDLER,
+ ZEND_ASSIGN_DIV_SPEC_CV_TMPVAR_HANDLER,
+ ZEND_ASSIGN_DIV_SPEC_CV_TMPVAR_DIM_HANDLER,
+ ZEND_ASSIGN_DIV_SPEC_CV_TMPVAR_OBJ_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_DIV_SPEC_CV_UNUSED_DIM_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_DIV_SPEC_CV_CV_HANDLER,
+ ZEND_ASSIGN_DIV_SPEC_CV_CV_DIM_HANDLER,
+ ZEND_ASSIGN_DIV_SPEC_CV_CV_OBJ_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,
+ 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,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_MOD_SPEC_VAR_CONST_HANDLER,
+ ZEND_ASSIGN_MOD_SPEC_VAR_CONST_DIM_HANDLER,
+ ZEND_ASSIGN_MOD_SPEC_VAR_CONST_OBJ_HANDLER,
+ ZEND_ASSIGN_MOD_SPEC_VAR_TMPVAR_HANDLER,
+ ZEND_ASSIGN_MOD_SPEC_VAR_TMPVAR_DIM_HANDLER,
+ ZEND_ASSIGN_MOD_SPEC_VAR_TMPVAR_OBJ_HANDLER,
+ ZEND_ASSIGN_MOD_SPEC_VAR_TMPVAR_HANDLER,
+ ZEND_ASSIGN_MOD_SPEC_VAR_TMPVAR_DIM_HANDLER,
+ ZEND_ASSIGN_MOD_SPEC_VAR_TMPVAR_OBJ_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_MOD_SPEC_VAR_UNUSED_DIM_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_MOD_SPEC_VAR_CV_HANDLER,
+ ZEND_ASSIGN_MOD_SPEC_VAR_CV_DIM_HANDLER,
+ ZEND_ASSIGN_MOD_SPEC_VAR_CV_OBJ_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_MOD_SPEC_UNUSED_CONST_OBJ_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_MOD_SPEC_UNUSED_TMPVAR_OBJ_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_MOD_SPEC_UNUSED_TMPVAR_OBJ_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_MOD_SPEC_UNUSED_CV_OBJ_HANDLER,
+ ZEND_ASSIGN_MOD_SPEC_CV_CONST_HANDLER,
+ ZEND_ASSIGN_MOD_SPEC_CV_CONST_DIM_HANDLER,
+ ZEND_ASSIGN_MOD_SPEC_CV_CONST_OBJ_HANDLER,
+ ZEND_ASSIGN_MOD_SPEC_CV_TMPVAR_HANDLER,
+ ZEND_ASSIGN_MOD_SPEC_CV_TMPVAR_DIM_HANDLER,
+ ZEND_ASSIGN_MOD_SPEC_CV_TMPVAR_OBJ_HANDLER,
+ ZEND_ASSIGN_MOD_SPEC_CV_TMPVAR_HANDLER,
+ ZEND_ASSIGN_MOD_SPEC_CV_TMPVAR_DIM_HANDLER,
+ ZEND_ASSIGN_MOD_SPEC_CV_TMPVAR_OBJ_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_MOD_SPEC_CV_UNUSED_DIM_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_MOD_SPEC_CV_CV_HANDLER,
+ ZEND_ASSIGN_MOD_SPEC_CV_CV_DIM_HANDLER,
+ ZEND_ASSIGN_MOD_SPEC_CV_CV_OBJ_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,
+ 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,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_SL_SPEC_VAR_CONST_HANDLER,
+ ZEND_ASSIGN_SL_SPEC_VAR_CONST_DIM_HANDLER,
+ ZEND_ASSIGN_SL_SPEC_VAR_CONST_OBJ_HANDLER,
+ ZEND_ASSIGN_SL_SPEC_VAR_TMPVAR_HANDLER,
+ ZEND_ASSIGN_SL_SPEC_VAR_TMPVAR_DIM_HANDLER,
+ ZEND_ASSIGN_SL_SPEC_VAR_TMPVAR_OBJ_HANDLER,
+ ZEND_ASSIGN_SL_SPEC_VAR_TMPVAR_HANDLER,
+ ZEND_ASSIGN_SL_SPEC_VAR_TMPVAR_DIM_HANDLER,
+ ZEND_ASSIGN_SL_SPEC_VAR_TMPVAR_OBJ_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_SL_SPEC_VAR_UNUSED_DIM_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_SL_SPEC_VAR_CV_HANDLER,
+ ZEND_ASSIGN_SL_SPEC_VAR_CV_DIM_HANDLER,
+ ZEND_ASSIGN_SL_SPEC_VAR_CV_OBJ_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_SL_SPEC_UNUSED_CONST_OBJ_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_SL_SPEC_UNUSED_TMPVAR_OBJ_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_SL_SPEC_UNUSED_TMPVAR_OBJ_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_SL_SPEC_UNUSED_CV_OBJ_HANDLER,
+ ZEND_ASSIGN_SL_SPEC_CV_CONST_HANDLER,
+ ZEND_ASSIGN_SL_SPEC_CV_CONST_DIM_HANDLER,
+ ZEND_ASSIGN_SL_SPEC_CV_CONST_OBJ_HANDLER,
+ ZEND_ASSIGN_SL_SPEC_CV_TMPVAR_HANDLER,
+ ZEND_ASSIGN_SL_SPEC_CV_TMPVAR_DIM_HANDLER,
+ ZEND_ASSIGN_SL_SPEC_CV_TMPVAR_OBJ_HANDLER,
+ ZEND_ASSIGN_SL_SPEC_CV_TMPVAR_HANDLER,
+ ZEND_ASSIGN_SL_SPEC_CV_TMPVAR_DIM_HANDLER,
+ ZEND_ASSIGN_SL_SPEC_CV_TMPVAR_OBJ_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_SL_SPEC_CV_UNUSED_DIM_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_SL_SPEC_CV_CV_HANDLER,
+ ZEND_ASSIGN_SL_SPEC_CV_CV_DIM_HANDLER,
+ ZEND_ASSIGN_SL_SPEC_CV_CV_OBJ_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,
+ 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,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_SR_SPEC_VAR_CONST_HANDLER,
+ ZEND_ASSIGN_SR_SPEC_VAR_CONST_DIM_HANDLER,
+ ZEND_ASSIGN_SR_SPEC_VAR_CONST_OBJ_HANDLER,
+ ZEND_ASSIGN_SR_SPEC_VAR_TMPVAR_HANDLER,
+ ZEND_ASSIGN_SR_SPEC_VAR_TMPVAR_DIM_HANDLER,
+ ZEND_ASSIGN_SR_SPEC_VAR_TMPVAR_OBJ_HANDLER,
+ ZEND_ASSIGN_SR_SPEC_VAR_TMPVAR_HANDLER,
+ ZEND_ASSIGN_SR_SPEC_VAR_TMPVAR_DIM_HANDLER,
+ ZEND_ASSIGN_SR_SPEC_VAR_TMPVAR_OBJ_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_SR_SPEC_VAR_UNUSED_DIM_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_SR_SPEC_VAR_CV_HANDLER,
+ ZEND_ASSIGN_SR_SPEC_VAR_CV_DIM_HANDLER,
+ ZEND_ASSIGN_SR_SPEC_VAR_CV_OBJ_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_SR_SPEC_UNUSED_CONST_OBJ_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_SR_SPEC_UNUSED_TMPVAR_OBJ_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_SR_SPEC_UNUSED_TMPVAR_OBJ_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_SR_SPEC_UNUSED_CV_OBJ_HANDLER,
+ ZEND_ASSIGN_SR_SPEC_CV_CONST_HANDLER,
+ ZEND_ASSIGN_SR_SPEC_CV_CONST_DIM_HANDLER,
+ ZEND_ASSIGN_SR_SPEC_CV_CONST_OBJ_HANDLER,
+ ZEND_ASSIGN_SR_SPEC_CV_TMPVAR_HANDLER,
+ ZEND_ASSIGN_SR_SPEC_CV_TMPVAR_DIM_HANDLER,
+ ZEND_ASSIGN_SR_SPEC_CV_TMPVAR_OBJ_HANDLER,
+ ZEND_ASSIGN_SR_SPEC_CV_TMPVAR_HANDLER,
+ ZEND_ASSIGN_SR_SPEC_CV_TMPVAR_DIM_HANDLER,
+ ZEND_ASSIGN_SR_SPEC_CV_TMPVAR_OBJ_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_SR_SPEC_CV_UNUSED_DIM_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_SR_SPEC_CV_CV_HANDLER,
+ ZEND_ASSIGN_SR_SPEC_CV_CV_DIM_HANDLER,
+ ZEND_ASSIGN_SR_SPEC_CV_CV_OBJ_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,
+ 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,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_CONCAT_SPEC_VAR_CONST_HANDLER,
+ ZEND_ASSIGN_CONCAT_SPEC_VAR_CONST_DIM_HANDLER,
+ ZEND_ASSIGN_CONCAT_SPEC_VAR_CONST_OBJ_HANDLER,
+ ZEND_ASSIGN_CONCAT_SPEC_VAR_TMPVAR_HANDLER,
+ ZEND_ASSIGN_CONCAT_SPEC_VAR_TMPVAR_DIM_HANDLER,
+ ZEND_ASSIGN_CONCAT_SPEC_VAR_TMPVAR_OBJ_HANDLER,
+ ZEND_ASSIGN_CONCAT_SPEC_VAR_TMPVAR_HANDLER,
+ ZEND_ASSIGN_CONCAT_SPEC_VAR_TMPVAR_DIM_HANDLER,
+ ZEND_ASSIGN_CONCAT_SPEC_VAR_TMPVAR_OBJ_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_CONCAT_SPEC_VAR_UNUSED_DIM_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_CONCAT_SPEC_VAR_CV_HANDLER,
+ ZEND_ASSIGN_CONCAT_SPEC_VAR_CV_DIM_HANDLER,
+ ZEND_ASSIGN_CONCAT_SPEC_VAR_CV_OBJ_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_CONCAT_SPEC_UNUSED_CONST_OBJ_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_CONCAT_SPEC_UNUSED_TMPVAR_OBJ_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_CONCAT_SPEC_UNUSED_TMPVAR_OBJ_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_CONCAT_SPEC_UNUSED_CV_OBJ_HANDLER,
+ ZEND_ASSIGN_CONCAT_SPEC_CV_CONST_HANDLER,
+ ZEND_ASSIGN_CONCAT_SPEC_CV_CONST_DIM_HANDLER,
+ ZEND_ASSIGN_CONCAT_SPEC_CV_CONST_OBJ_HANDLER,
+ ZEND_ASSIGN_CONCAT_SPEC_CV_TMPVAR_HANDLER,
+ ZEND_ASSIGN_CONCAT_SPEC_CV_TMPVAR_DIM_HANDLER,
+ ZEND_ASSIGN_CONCAT_SPEC_CV_TMPVAR_OBJ_HANDLER,
+ ZEND_ASSIGN_CONCAT_SPEC_CV_TMPVAR_HANDLER,
+ ZEND_ASSIGN_CONCAT_SPEC_CV_TMPVAR_DIM_HANDLER,
+ ZEND_ASSIGN_CONCAT_SPEC_CV_TMPVAR_OBJ_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_CONCAT_SPEC_CV_UNUSED_DIM_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_CONCAT_SPEC_CV_CV_HANDLER,
+ ZEND_ASSIGN_CONCAT_SPEC_CV_CV_DIM_HANDLER,
+ ZEND_ASSIGN_CONCAT_SPEC_CV_CV_OBJ_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,
+ 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,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_BW_OR_SPEC_VAR_CONST_HANDLER,
+ ZEND_ASSIGN_BW_OR_SPEC_VAR_CONST_DIM_HANDLER,
+ ZEND_ASSIGN_BW_OR_SPEC_VAR_CONST_OBJ_HANDLER,
+ ZEND_ASSIGN_BW_OR_SPEC_VAR_TMPVAR_HANDLER,
+ ZEND_ASSIGN_BW_OR_SPEC_VAR_TMPVAR_DIM_HANDLER,
+ ZEND_ASSIGN_BW_OR_SPEC_VAR_TMPVAR_OBJ_HANDLER,
+ ZEND_ASSIGN_BW_OR_SPEC_VAR_TMPVAR_HANDLER,
+ ZEND_ASSIGN_BW_OR_SPEC_VAR_TMPVAR_DIM_HANDLER,
+ ZEND_ASSIGN_BW_OR_SPEC_VAR_TMPVAR_OBJ_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_BW_OR_SPEC_VAR_UNUSED_DIM_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_BW_OR_SPEC_VAR_CV_HANDLER,
+ ZEND_ASSIGN_BW_OR_SPEC_VAR_CV_DIM_HANDLER,
+ ZEND_ASSIGN_BW_OR_SPEC_VAR_CV_OBJ_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_BW_OR_SPEC_UNUSED_CONST_OBJ_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_BW_OR_SPEC_UNUSED_TMPVAR_OBJ_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_BW_OR_SPEC_UNUSED_TMPVAR_OBJ_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_BW_OR_SPEC_UNUSED_CV_OBJ_HANDLER,
+ ZEND_ASSIGN_BW_OR_SPEC_CV_CONST_HANDLER,
+ ZEND_ASSIGN_BW_OR_SPEC_CV_CONST_DIM_HANDLER,
+ ZEND_ASSIGN_BW_OR_SPEC_CV_CONST_OBJ_HANDLER,
+ ZEND_ASSIGN_BW_OR_SPEC_CV_TMPVAR_HANDLER,
+ ZEND_ASSIGN_BW_OR_SPEC_CV_TMPVAR_DIM_HANDLER,
+ ZEND_ASSIGN_BW_OR_SPEC_CV_TMPVAR_OBJ_HANDLER,
+ ZEND_ASSIGN_BW_OR_SPEC_CV_TMPVAR_HANDLER,
+ ZEND_ASSIGN_BW_OR_SPEC_CV_TMPVAR_DIM_HANDLER,
+ ZEND_ASSIGN_BW_OR_SPEC_CV_TMPVAR_OBJ_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_BW_OR_SPEC_CV_UNUSED_DIM_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_BW_OR_SPEC_CV_CV_HANDLER,
+ ZEND_ASSIGN_BW_OR_SPEC_CV_CV_DIM_HANDLER,
+ ZEND_ASSIGN_BW_OR_SPEC_CV_CV_OBJ_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,
+ 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,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_BW_AND_SPEC_VAR_CONST_HANDLER,
+ ZEND_ASSIGN_BW_AND_SPEC_VAR_CONST_DIM_HANDLER,
+ ZEND_ASSIGN_BW_AND_SPEC_VAR_CONST_OBJ_HANDLER,
+ ZEND_ASSIGN_BW_AND_SPEC_VAR_TMPVAR_HANDLER,
+ ZEND_ASSIGN_BW_AND_SPEC_VAR_TMPVAR_DIM_HANDLER,
+ ZEND_ASSIGN_BW_AND_SPEC_VAR_TMPVAR_OBJ_HANDLER,
+ ZEND_ASSIGN_BW_AND_SPEC_VAR_TMPVAR_HANDLER,
+ ZEND_ASSIGN_BW_AND_SPEC_VAR_TMPVAR_DIM_HANDLER,
+ ZEND_ASSIGN_BW_AND_SPEC_VAR_TMPVAR_OBJ_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_BW_AND_SPEC_VAR_UNUSED_DIM_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_BW_AND_SPEC_VAR_CV_HANDLER,
+ ZEND_ASSIGN_BW_AND_SPEC_VAR_CV_DIM_HANDLER,
+ ZEND_ASSIGN_BW_AND_SPEC_VAR_CV_OBJ_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_BW_AND_SPEC_UNUSED_CONST_OBJ_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_BW_AND_SPEC_UNUSED_TMPVAR_OBJ_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_BW_AND_SPEC_UNUSED_TMPVAR_OBJ_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_BW_AND_SPEC_UNUSED_CV_OBJ_HANDLER,
+ ZEND_ASSIGN_BW_AND_SPEC_CV_CONST_HANDLER,
+ ZEND_ASSIGN_BW_AND_SPEC_CV_CONST_DIM_HANDLER,
+ ZEND_ASSIGN_BW_AND_SPEC_CV_CONST_OBJ_HANDLER,
+ ZEND_ASSIGN_BW_AND_SPEC_CV_TMPVAR_HANDLER,
+ ZEND_ASSIGN_BW_AND_SPEC_CV_TMPVAR_DIM_HANDLER,
+ ZEND_ASSIGN_BW_AND_SPEC_CV_TMPVAR_OBJ_HANDLER,
+ ZEND_ASSIGN_BW_AND_SPEC_CV_TMPVAR_HANDLER,
+ ZEND_ASSIGN_BW_AND_SPEC_CV_TMPVAR_DIM_HANDLER,
+ ZEND_ASSIGN_BW_AND_SPEC_CV_TMPVAR_OBJ_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_BW_AND_SPEC_CV_UNUSED_DIM_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_BW_AND_SPEC_CV_CV_HANDLER,
+ ZEND_ASSIGN_BW_AND_SPEC_CV_CV_DIM_HANDLER,
+ ZEND_ASSIGN_BW_AND_SPEC_CV_CV_OBJ_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,
+ 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,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_BW_XOR_SPEC_VAR_CONST_HANDLER,
+ ZEND_ASSIGN_BW_XOR_SPEC_VAR_CONST_DIM_HANDLER,
+ ZEND_ASSIGN_BW_XOR_SPEC_VAR_CONST_OBJ_HANDLER,
+ ZEND_ASSIGN_BW_XOR_SPEC_VAR_TMPVAR_HANDLER,
+ ZEND_ASSIGN_BW_XOR_SPEC_VAR_TMPVAR_DIM_HANDLER,
+ ZEND_ASSIGN_BW_XOR_SPEC_VAR_TMPVAR_OBJ_HANDLER,
+ ZEND_ASSIGN_BW_XOR_SPEC_VAR_TMPVAR_HANDLER,
+ ZEND_ASSIGN_BW_XOR_SPEC_VAR_TMPVAR_DIM_HANDLER,
+ ZEND_ASSIGN_BW_XOR_SPEC_VAR_TMPVAR_OBJ_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_BW_XOR_SPEC_VAR_UNUSED_DIM_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_BW_XOR_SPEC_VAR_CV_HANDLER,
+ ZEND_ASSIGN_BW_XOR_SPEC_VAR_CV_DIM_HANDLER,
+ ZEND_ASSIGN_BW_XOR_SPEC_VAR_CV_OBJ_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_BW_XOR_SPEC_UNUSED_CONST_OBJ_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_BW_XOR_SPEC_UNUSED_TMPVAR_OBJ_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_BW_XOR_SPEC_UNUSED_TMPVAR_OBJ_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_BW_XOR_SPEC_UNUSED_CV_OBJ_HANDLER,
+ ZEND_ASSIGN_BW_XOR_SPEC_CV_CONST_HANDLER,
+ ZEND_ASSIGN_BW_XOR_SPEC_CV_CONST_DIM_HANDLER,
+ ZEND_ASSIGN_BW_XOR_SPEC_CV_CONST_OBJ_HANDLER,
+ ZEND_ASSIGN_BW_XOR_SPEC_CV_TMPVAR_HANDLER,
+ ZEND_ASSIGN_BW_XOR_SPEC_CV_TMPVAR_DIM_HANDLER,
+ ZEND_ASSIGN_BW_XOR_SPEC_CV_TMPVAR_OBJ_HANDLER,
+ ZEND_ASSIGN_BW_XOR_SPEC_CV_TMPVAR_HANDLER,
+ ZEND_ASSIGN_BW_XOR_SPEC_CV_TMPVAR_DIM_HANDLER,
+ ZEND_ASSIGN_BW_XOR_SPEC_CV_TMPVAR_OBJ_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_BW_XOR_SPEC_CV_UNUSED_DIM_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_BW_XOR_SPEC_CV_CV_HANDLER,
+ ZEND_ASSIGN_BW_XOR_SPEC_CV_CV_DIM_HANDLER,
+ ZEND_ASSIGN_BW_XOR_SPEC_CV_CV_OBJ_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_PRE_INC_SPEC_VAR_RETVAL_UNUSED_HANDLER,
+ ZEND_PRE_INC_SPEC_VAR_RETVAL_USED_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_PRE_INC_SPEC_CV_RETVAL_UNUSED_HANDLER,
+ ZEND_PRE_INC_SPEC_CV_RETVAL_USED_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_PRE_DEC_SPEC_VAR_RETVAL_UNUSED_HANDLER,
+ ZEND_PRE_DEC_SPEC_VAR_RETVAL_USED_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_PRE_DEC_SPEC_CV_RETVAL_UNUSED_HANDLER,
+ ZEND_PRE_DEC_SPEC_CV_RETVAL_USED_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_POST_INC_SPEC_VAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_POST_INC_SPEC_CV_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_POST_DEC_SPEC_VAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_POST_DEC_SPEC_CV_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,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_SPEC_VAR_CONST_RETVAL_UNUSED_HANDLER,
+ ZEND_ASSIGN_SPEC_VAR_CONST_RETVAL_USED_HANDLER,
+ ZEND_ASSIGN_SPEC_VAR_TMP_RETVAL_UNUSED_HANDLER,
+ ZEND_ASSIGN_SPEC_VAR_TMP_RETVAL_USED_HANDLER,
+ ZEND_ASSIGN_SPEC_VAR_VAR_RETVAL_UNUSED_HANDLER,
+ ZEND_ASSIGN_SPEC_VAR_VAR_RETVAL_USED_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_SPEC_VAR_CV_RETVAL_UNUSED_HANDLER,
+ ZEND_ASSIGN_SPEC_VAR_CV_RETVAL_USED_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_ASSIGN_SPEC_CV_CONST_RETVAL_UNUSED_HANDLER,
+ ZEND_ASSIGN_SPEC_CV_CONST_RETVAL_USED_HANDLER,
+ ZEND_ASSIGN_SPEC_CV_TMP_RETVAL_UNUSED_HANDLER,
+ ZEND_ASSIGN_SPEC_CV_TMP_RETVAL_USED_HANDLER,
+ ZEND_ASSIGN_SPEC_CV_VAR_RETVAL_UNUSED_HANDLER,
+ ZEND_ASSIGN_SPEC_CV_VAR_RETVAL_USED_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_SPEC_CV_CV_RETVAL_UNUSED_HANDLER,
+ ZEND_ASSIGN_SPEC_CV_CV_RETVAL_USED_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_ASSIGN_REF_SPEC_VAR_VAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_REF_SPEC_VAR_CV_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_REF_SPEC_CV_VAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_REF_SPEC_CV_CV_HANDLER,
+ ZEND_ECHO_SPEC_CONST_HANDLER,
+ ZEND_ECHO_SPEC_TMPVAR_HANDLER,
+ ZEND_ECHO_SPEC_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ECHO_SPEC_CV_HANDLER,
+ ZEND_GENERATOR_CREATE_SPEC_HANDLER,
+ ZEND_JMP_SPEC_HANDLER,
+ ZEND_JMPZ_SPEC_CONST_HANDLER,
+ ZEND_JMPZ_SPEC_TMPVAR_HANDLER,
+ ZEND_JMPZ_SPEC_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_JMPZ_SPEC_CV_HANDLER,
+ ZEND_JMPNZ_SPEC_CONST_HANDLER,
+ ZEND_JMPNZ_SPEC_TMPVAR_HANDLER,
+ ZEND_JMPNZ_SPEC_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_JMPNZ_SPEC_CV_HANDLER,
+ ZEND_JMPZNZ_SPEC_CONST_HANDLER,
+ ZEND_JMPZNZ_SPEC_TMPVAR_HANDLER,
+ ZEND_JMPZNZ_SPEC_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_JMPZNZ_SPEC_CV_HANDLER,
+ ZEND_JMPZ_EX_SPEC_CONST_HANDLER,
+ ZEND_JMPZ_EX_SPEC_TMPVAR_HANDLER,
+ ZEND_JMPZ_EX_SPEC_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_JMPZ_EX_SPEC_CV_HANDLER,
+ ZEND_JMPNZ_EX_SPEC_CONST_HANDLER,
+ ZEND_JMPNZ_EX_SPEC_TMPVAR_HANDLER,
+ ZEND_JMPNZ_EX_SPEC_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_JMPNZ_EX_SPEC_CV_HANDLER,
+ ZEND_CASE_SPEC_CONST_CONST_HANDLER,
+ ZEND_CASE_SPEC_CONST_TMPVAR_HANDLER,
+ ZEND_CASE_SPEC_CONST_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_CASE_SPEC_CONST_CV_HANDLER,
+ ZEND_CASE_SPEC_TMPVAR_CONST_HANDLER,
+ ZEND_CASE_SPEC_TMPVAR_TMPVAR_HANDLER,
+ ZEND_CASE_SPEC_TMPVAR_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_CASE_SPEC_TMPVAR_CV_HANDLER,
+ ZEND_CASE_SPEC_TMPVAR_CONST_HANDLER,
+ ZEND_CASE_SPEC_TMPVAR_TMPVAR_HANDLER,
+ ZEND_CASE_SPEC_TMPVAR_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_CASE_SPEC_TMPVAR_CV_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_CASE_SPEC_CV_CONST_HANDLER,
+ ZEND_CASE_SPEC_CV_TMPVAR_HANDLER,
+ ZEND_CASE_SPEC_CV_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_CASE_SPEC_CV_CV_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_SEND_VAR_NO_REF_EX_SPEC_VAR_HANDLER,
+ ZEND_SEND_VAR_NO_REF_EX_SPEC_VAR_QUICK_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_BOOL_SPEC_CONST_HANDLER,
+ ZEND_BOOL_SPEC_TMPVAR_HANDLER,
+ ZEND_BOOL_SPEC_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_BOOL_SPEC_CV_HANDLER,
+ ZEND_FAST_CONCAT_SPEC_CONST_CONST_HANDLER,
+ ZEND_FAST_CONCAT_SPEC_CONST_TMPVAR_HANDLER,
+ ZEND_FAST_CONCAT_SPEC_CONST_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_FAST_CONCAT_SPEC_CONST_CV_HANDLER,
+ ZEND_FAST_CONCAT_SPEC_TMPVAR_CONST_HANDLER,
+ ZEND_FAST_CONCAT_SPEC_TMPVAR_TMPVAR_HANDLER,
+ ZEND_FAST_CONCAT_SPEC_TMPVAR_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_FAST_CONCAT_SPEC_TMPVAR_CV_HANDLER,
+ ZEND_FAST_CONCAT_SPEC_TMPVAR_CONST_HANDLER,
+ ZEND_FAST_CONCAT_SPEC_TMPVAR_TMPVAR_HANDLER,
+ ZEND_FAST_CONCAT_SPEC_TMPVAR_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_FAST_CONCAT_SPEC_TMPVAR_CV_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_FAST_CONCAT_SPEC_CV_CONST_HANDLER,
+ ZEND_FAST_CONCAT_SPEC_CV_TMPVAR_HANDLER,
+ ZEND_FAST_CONCAT_SPEC_CV_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_FAST_CONCAT_SPEC_CV_CV_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,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ROPE_INIT_SPEC_UNUSED_CONST_HANDLER,
+ ZEND_ROPE_INIT_SPEC_UNUSED_TMPVAR_HANDLER,
+ ZEND_ROPE_INIT_SPEC_UNUSED_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ROPE_INIT_SPEC_UNUSED_CV_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_ROPE_ADD_SPEC_TMP_CONST_HANDLER,
+ ZEND_ROPE_ADD_SPEC_TMP_TMPVAR_HANDLER,
+ ZEND_ROPE_ADD_SPEC_TMP_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ROPE_ADD_SPEC_TMP_CV_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,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ROPE_END_SPEC_TMP_CONST_HANDLER,
+ ZEND_ROPE_END_SPEC_TMP_TMPVAR_HANDLER,
+ ZEND_ROPE_END_SPEC_TMP_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ROPE_END_SPEC_TMP_CV_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,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_BEGIN_SILENCE_SPEC_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_END_SILENCE_SPEC_TMP_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_INIT_FCALL_BY_NAME_SPEC_CONST_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_DO_FCALL_SPEC_RETVAL_UNUSED_HANDLER,
+ ZEND_DO_FCALL_SPEC_RETVAL_USED_HANDLER,
+ ZEND_INIT_FCALL_SPEC_CONST_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_RETURN_SPEC_CONST_HANDLER,
+ ZEND_RETURN_SPEC_TMP_HANDLER,
+ ZEND_RETURN_SPEC_VAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_RETURN_SPEC_CV_HANDLER,
+ ZEND_RECV_SPEC_HANDLER,
+ ZEND_RECV_INIT_SPEC_CONST_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_SEND_VAL_SPEC_CONST_HANDLER,
+ ZEND_SEND_VAL_SPEC_TMPVAR_HANDLER,
+ ZEND_SEND_VAL_SPEC_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_SEND_VAR_EX_SPEC_VAR_HANDLER,
+ ZEND_SEND_VAR_EX_SPEC_VAR_QUICK_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_SEND_VAR_EX_SPEC_CV_HANDLER,
+ ZEND_SEND_VAR_EX_SPEC_CV_QUICK_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_SEND_REF_SPEC_VAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_SEND_REF_SPEC_CV_HANDLER,
+ ZEND_NEW_SPEC_CONST_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NEW_SPEC_VAR_HANDLER,
+ ZEND_NEW_SPEC_UNUSED_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_INIT_NS_FCALL_BY_NAME_SPEC_CONST_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_FREE_SPEC_TMPVAR_HANDLER,
+ ZEND_FREE_SPEC_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_INIT_ARRAY_SPEC_CONST_CONST_HANDLER,
+ ZEND_INIT_ARRAY_SPEC_CONST_TMPVAR_HANDLER,
+ ZEND_INIT_ARRAY_SPEC_CONST_TMPVAR_HANDLER,
+ ZEND_INIT_ARRAY_SPEC_CONST_UNUSED_HANDLER,
+ ZEND_INIT_ARRAY_SPEC_CONST_CV_HANDLER,
+ ZEND_INIT_ARRAY_SPEC_TMP_CONST_HANDLER,
+ ZEND_INIT_ARRAY_SPEC_TMP_TMPVAR_HANDLER,
+ ZEND_INIT_ARRAY_SPEC_TMP_TMPVAR_HANDLER,
+ ZEND_INIT_ARRAY_SPEC_TMP_UNUSED_HANDLER,
+ ZEND_INIT_ARRAY_SPEC_TMP_CV_HANDLER,
+ ZEND_INIT_ARRAY_SPEC_VAR_CONST_HANDLER,
+ ZEND_INIT_ARRAY_SPEC_VAR_TMPVAR_HANDLER,
+ ZEND_INIT_ARRAY_SPEC_VAR_TMPVAR_HANDLER,
+ ZEND_INIT_ARRAY_SPEC_VAR_UNUSED_HANDLER,
+ ZEND_INIT_ARRAY_SPEC_VAR_CV_HANDLER,
+ ZEND_INIT_ARRAY_SPEC_UNUSED_CONST_HANDLER,
+ ZEND_INIT_ARRAY_SPEC_UNUSED_TMPVAR_HANDLER,
+ ZEND_INIT_ARRAY_SPEC_UNUSED_TMPVAR_HANDLER,
+ ZEND_INIT_ARRAY_SPEC_UNUSED_UNUSED_HANDLER,
+ ZEND_INIT_ARRAY_SPEC_UNUSED_CV_HANDLER,
+ ZEND_INIT_ARRAY_SPEC_CV_CONST_HANDLER,
+ ZEND_INIT_ARRAY_SPEC_CV_TMPVAR_HANDLER,
+ ZEND_INIT_ARRAY_SPEC_CV_TMPVAR_HANDLER,
+ ZEND_INIT_ARRAY_SPEC_CV_UNUSED_HANDLER,
+ ZEND_INIT_ARRAY_SPEC_CV_CV_HANDLER,
+ ZEND_ADD_ARRAY_ELEMENT_SPEC_CONST_CONST_HANDLER,
+ ZEND_ADD_ARRAY_ELEMENT_SPEC_CONST_TMPVAR_HANDLER,
+ ZEND_ADD_ARRAY_ELEMENT_SPEC_CONST_TMPVAR_HANDLER,
+ ZEND_ADD_ARRAY_ELEMENT_SPEC_CONST_UNUSED_HANDLER,
+ ZEND_ADD_ARRAY_ELEMENT_SPEC_CONST_CV_HANDLER,
+ ZEND_ADD_ARRAY_ELEMENT_SPEC_TMP_CONST_HANDLER,
+ ZEND_ADD_ARRAY_ELEMENT_SPEC_TMP_TMPVAR_HANDLER,
+ ZEND_ADD_ARRAY_ELEMENT_SPEC_TMP_TMPVAR_HANDLER,
+ ZEND_ADD_ARRAY_ELEMENT_SPEC_TMP_UNUSED_HANDLER,
+ ZEND_ADD_ARRAY_ELEMENT_SPEC_TMP_CV_HANDLER,
+ ZEND_ADD_ARRAY_ELEMENT_SPEC_VAR_CONST_HANDLER,
+ ZEND_ADD_ARRAY_ELEMENT_SPEC_VAR_TMPVAR_HANDLER,
+ ZEND_ADD_ARRAY_ELEMENT_SPEC_VAR_TMPVAR_HANDLER,
+ ZEND_ADD_ARRAY_ELEMENT_SPEC_VAR_UNUSED_HANDLER,
+ ZEND_ADD_ARRAY_ELEMENT_SPEC_VAR_CV_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ADD_ARRAY_ELEMENT_SPEC_CV_CONST_HANDLER,
+ ZEND_ADD_ARRAY_ELEMENT_SPEC_CV_TMPVAR_HANDLER,
+ ZEND_ADD_ARRAY_ELEMENT_SPEC_CV_TMPVAR_HANDLER,
+ ZEND_ADD_ARRAY_ELEMENT_SPEC_CV_UNUSED_HANDLER,
+ ZEND_ADD_ARRAY_ELEMENT_SPEC_CV_CV_HANDLER,
+ ZEND_INCLUDE_OR_EVAL_SPEC_CONST_HANDLER,
+ ZEND_INCLUDE_OR_EVAL_SPEC_TMPVAR_HANDLER,
+ ZEND_INCLUDE_OR_EVAL_SPEC_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_INCLUDE_OR_EVAL_SPEC_CV_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_UNSET_VAR_SPEC_CONST_UNUSED_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_UNSET_VAR_SPEC_TMPVAR_UNUSED_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_UNSET_VAR_SPEC_TMPVAR_UNUSED_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_UNSET_VAR_SPEC_CV_UNUSED_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_UNSET_DIM_SPEC_VAR_CONST_HANDLER,
+ ZEND_UNSET_DIM_SPEC_VAR_TMPVAR_HANDLER,
+ ZEND_UNSET_DIM_SPEC_VAR_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_UNSET_DIM_SPEC_VAR_CV_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_UNSET_DIM_SPEC_CV_CONST_HANDLER,
+ ZEND_UNSET_DIM_SPEC_CV_TMPVAR_HANDLER,
+ ZEND_UNSET_DIM_SPEC_CV_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_UNSET_DIM_SPEC_CV_CV_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_UNSET_OBJ_SPEC_VAR_CONST_HANDLER,
+ ZEND_UNSET_OBJ_SPEC_VAR_TMPVAR_HANDLER,
+ ZEND_UNSET_OBJ_SPEC_VAR_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_UNSET_OBJ_SPEC_VAR_CV_HANDLER,
+ ZEND_UNSET_OBJ_SPEC_UNUSED_CONST_HANDLER,
+ ZEND_UNSET_OBJ_SPEC_UNUSED_TMPVAR_HANDLER,
+ ZEND_UNSET_OBJ_SPEC_UNUSED_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_UNSET_OBJ_SPEC_UNUSED_CV_HANDLER,
+ ZEND_UNSET_OBJ_SPEC_CV_CONST_HANDLER,
+ ZEND_UNSET_OBJ_SPEC_CV_TMPVAR_HANDLER,
+ ZEND_UNSET_OBJ_SPEC_CV_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_UNSET_OBJ_SPEC_CV_CV_HANDLER,
+ ZEND_FE_RESET_R_SPEC_CONST_HANDLER,
+ ZEND_FE_RESET_R_SPEC_TMP_HANDLER,
+ ZEND_FE_RESET_R_SPEC_VAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_FE_RESET_R_SPEC_CV_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_FE_FETCH_R_SPEC_VAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_EXIT_SPEC_CONST_HANDLER,
+ ZEND_EXIT_SPEC_TMPVAR_HANDLER,
+ ZEND_EXIT_SPEC_TMPVAR_HANDLER,
+ ZEND_EXIT_SPEC_UNUSED_HANDLER,
+ ZEND_EXIT_SPEC_CV_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_FETCH_R_SPEC_CONST_UNUSED_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_FETCH_R_SPEC_TMPVAR_UNUSED_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_FETCH_R_SPEC_TMPVAR_UNUSED_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_FETCH_R_SPEC_CV_UNUSED_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_FETCH_DIM_R_SPEC_CONST_CONST_HANDLER,
+ ZEND_FETCH_DIM_R_SPEC_CONST_TMPVAR_HANDLER,
+ ZEND_FETCH_DIM_R_SPEC_CONST_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_FETCH_DIM_R_SPEC_CONST_CV_HANDLER,
+ ZEND_FETCH_DIM_R_SPEC_TMPVAR_CONST_HANDLER,
+ ZEND_FETCH_DIM_R_SPEC_TMPVAR_TMPVAR_HANDLER,
+ ZEND_FETCH_DIM_R_SPEC_TMPVAR_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_FETCH_DIM_R_SPEC_TMPVAR_CV_HANDLER,
+ ZEND_FETCH_DIM_R_SPEC_TMPVAR_CONST_HANDLER,
+ ZEND_FETCH_DIM_R_SPEC_TMPVAR_TMPVAR_HANDLER,
+ ZEND_FETCH_DIM_R_SPEC_TMPVAR_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_FETCH_DIM_R_SPEC_TMPVAR_CV_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_FETCH_DIM_R_SPEC_CV_CONST_HANDLER,
+ ZEND_FETCH_DIM_R_SPEC_CV_TMPVAR_HANDLER,
+ ZEND_FETCH_DIM_R_SPEC_CV_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_FETCH_DIM_R_SPEC_CV_CV_HANDLER,
+ ZEND_FETCH_OBJ_R_SPEC_CONST_CONST_HANDLER,
+ ZEND_FETCH_OBJ_R_SPEC_CONST_TMPVAR_HANDLER,
+ ZEND_FETCH_OBJ_R_SPEC_CONST_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_FETCH_OBJ_R_SPEC_CONST_CV_HANDLER,
+ ZEND_FETCH_OBJ_R_SPEC_TMP_CONST_HANDLER,
+ ZEND_FETCH_OBJ_R_SPEC_TMP_TMPVAR_HANDLER,
+ ZEND_FETCH_OBJ_R_SPEC_TMP_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_FETCH_OBJ_R_SPEC_TMP_CV_HANDLER,
+ ZEND_FETCH_OBJ_R_SPEC_VAR_CONST_HANDLER,
+ ZEND_FETCH_OBJ_R_SPEC_VAR_TMPVAR_HANDLER,
+ ZEND_FETCH_OBJ_R_SPEC_VAR_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_FETCH_OBJ_R_SPEC_VAR_CV_HANDLER,
+ ZEND_FETCH_OBJ_R_SPEC_UNUSED_CONST_HANDLER,
+ ZEND_FETCH_OBJ_R_SPEC_UNUSED_TMPVAR_HANDLER,
+ ZEND_FETCH_OBJ_R_SPEC_UNUSED_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_FETCH_OBJ_R_SPEC_UNUSED_CV_HANDLER,
+ ZEND_FETCH_OBJ_R_SPEC_CV_CONST_HANDLER,
+ ZEND_FETCH_OBJ_R_SPEC_CV_TMPVAR_HANDLER,
+ ZEND_FETCH_OBJ_R_SPEC_CV_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_FETCH_OBJ_R_SPEC_CV_CV_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_FETCH_W_SPEC_CONST_UNUSED_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_FETCH_W_SPEC_TMPVAR_UNUSED_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_FETCH_W_SPEC_TMPVAR_UNUSED_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_FETCH_W_SPEC_CV_UNUSED_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_FETCH_DIM_W_SPEC_VAR_CONST_HANDLER,
+ ZEND_FETCH_DIM_W_SPEC_VAR_TMPVAR_HANDLER,
+ ZEND_FETCH_DIM_W_SPEC_VAR_TMPVAR_HANDLER,
+ ZEND_FETCH_DIM_W_SPEC_VAR_UNUSED_HANDLER,
+ ZEND_FETCH_DIM_W_SPEC_VAR_CV_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_FETCH_DIM_W_SPEC_CV_CONST_HANDLER,
+ ZEND_FETCH_DIM_W_SPEC_CV_TMPVAR_HANDLER,
+ ZEND_FETCH_DIM_W_SPEC_CV_TMPVAR_HANDLER,
+ ZEND_FETCH_DIM_W_SPEC_CV_UNUSED_HANDLER,
+ ZEND_FETCH_DIM_W_SPEC_CV_CV_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_FETCH_OBJ_W_SPEC_VAR_CONST_HANDLER,
+ ZEND_FETCH_OBJ_W_SPEC_VAR_TMPVAR_HANDLER,
+ ZEND_FETCH_OBJ_W_SPEC_VAR_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_FETCH_OBJ_W_SPEC_VAR_CV_HANDLER,
+ ZEND_FETCH_OBJ_W_SPEC_UNUSED_CONST_HANDLER,
+ ZEND_FETCH_OBJ_W_SPEC_UNUSED_TMPVAR_HANDLER,
+ ZEND_FETCH_OBJ_W_SPEC_UNUSED_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_FETCH_OBJ_W_SPEC_UNUSED_CV_HANDLER,
+ ZEND_FETCH_OBJ_W_SPEC_CV_CONST_HANDLER,
+ ZEND_FETCH_OBJ_W_SPEC_CV_TMPVAR_HANDLER,
+ ZEND_FETCH_OBJ_W_SPEC_CV_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_FETCH_OBJ_W_SPEC_CV_CV_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_FETCH_RW_SPEC_CONST_UNUSED_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_FETCH_RW_SPEC_TMPVAR_UNUSED_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_FETCH_RW_SPEC_TMPVAR_UNUSED_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_FETCH_RW_SPEC_CV_UNUSED_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_FETCH_DIM_RW_SPEC_VAR_CONST_HANDLER,
+ ZEND_FETCH_DIM_RW_SPEC_VAR_TMPVAR_HANDLER,
+ ZEND_FETCH_DIM_RW_SPEC_VAR_TMPVAR_HANDLER,
+ ZEND_FETCH_DIM_RW_SPEC_VAR_UNUSED_HANDLER,
+ ZEND_FETCH_DIM_RW_SPEC_VAR_CV_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_FETCH_DIM_RW_SPEC_CV_CONST_HANDLER,
+ ZEND_FETCH_DIM_RW_SPEC_CV_TMPVAR_HANDLER,
+ ZEND_FETCH_DIM_RW_SPEC_CV_TMPVAR_HANDLER,
+ ZEND_FETCH_DIM_RW_SPEC_CV_UNUSED_HANDLER,
+ ZEND_FETCH_DIM_RW_SPEC_CV_CV_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_FETCH_OBJ_RW_SPEC_VAR_CONST_HANDLER,
+ ZEND_FETCH_OBJ_RW_SPEC_VAR_TMPVAR_HANDLER,
+ ZEND_FETCH_OBJ_RW_SPEC_VAR_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_FETCH_OBJ_RW_SPEC_VAR_CV_HANDLER,
+ ZEND_FETCH_OBJ_RW_SPEC_UNUSED_CONST_HANDLER,
+ ZEND_FETCH_OBJ_RW_SPEC_UNUSED_TMPVAR_HANDLER,
+ ZEND_FETCH_OBJ_RW_SPEC_UNUSED_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_FETCH_OBJ_RW_SPEC_UNUSED_CV_HANDLER,
+ ZEND_FETCH_OBJ_RW_SPEC_CV_CONST_HANDLER,
+ ZEND_FETCH_OBJ_RW_SPEC_CV_TMPVAR_HANDLER,
+ ZEND_FETCH_OBJ_RW_SPEC_CV_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_FETCH_OBJ_RW_SPEC_CV_CV_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_FETCH_IS_SPEC_CONST_UNUSED_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_FETCH_IS_SPEC_TMPVAR_UNUSED_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_FETCH_IS_SPEC_TMPVAR_UNUSED_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_FETCH_IS_SPEC_CV_UNUSED_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_FETCH_DIM_IS_SPEC_CONST_CONST_HANDLER,
+ ZEND_FETCH_DIM_IS_SPEC_CONST_TMPVAR_HANDLER,
+ ZEND_FETCH_DIM_IS_SPEC_CONST_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_FETCH_DIM_IS_SPEC_CONST_CV_HANDLER,
+ ZEND_FETCH_DIM_IS_SPEC_TMPVAR_CONST_HANDLER,
+ ZEND_FETCH_DIM_IS_SPEC_TMPVAR_TMPVAR_HANDLER,
+ ZEND_FETCH_DIM_IS_SPEC_TMPVAR_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_FETCH_DIM_IS_SPEC_TMPVAR_CV_HANDLER,
+ ZEND_FETCH_DIM_IS_SPEC_TMPVAR_CONST_HANDLER,
+ ZEND_FETCH_DIM_IS_SPEC_TMPVAR_TMPVAR_HANDLER,
+ ZEND_FETCH_DIM_IS_SPEC_TMPVAR_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_FETCH_DIM_IS_SPEC_TMPVAR_CV_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_FETCH_DIM_IS_SPEC_CV_CONST_HANDLER,
+ ZEND_FETCH_DIM_IS_SPEC_CV_TMPVAR_HANDLER,
+ ZEND_FETCH_DIM_IS_SPEC_CV_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_FETCH_DIM_IS_SPEC_CV_CV_HANDLER,
+ ZEND_FETCH_OBJ_IS_SPEC_CONST_CONST_HANDLER,
+ ZEND_FETCH_OBJ_IS_SPEC_CONST_TMPVAR_HANDLER,
+ ZEND_FETCH_OBJ_IS_SPEC_CONST_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_FETCH_OBJ_IS_SPEC_CONST_CV_HANDLER,
+ ZEND_FETCH_OBJ_IS_SPEC_TMPVAR_CONST_HANDLER,
+ ZEND_FETCH_OBJ_IS_SPEC_TMPVAR_TMPVAR_HANDLER,
+ ZEND_FETCH_OBJ_IS_SPEC_TMPVAR_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_FETCH_OBJ_IS_SPEC_TMPVAR_CV_HANDLER,
+ ZEND_FETCH_OBJ_IS_SPEC_TMPVAR_CONST_HANDLER,
+ ZEND_FETCH_OBJ_IS_SPEC_TMPVAR_TMPVAR_HANDLER,
+ ZEND_FETCH_OBJ_IS_SPEC_TMPVAR_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_FETCH_OBJ_IS_SPEC_TMPVAR_CV_HANDLER,
+ ZEND_FETCH_OBJ_IS_SPEC_UNUSED_CONST_HANDLER,
+ ZEND_FETCH_OBJ_IS_SPEC_UNUSED_TMPVAR_HANDLER,
+ ZEND_FETCH_OBJ_IS_SPEC_UNUSED_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_FETCH_OBJ_IS_SPEC_UNUSED_CV_HANDLER,
+ ZEND_FETCH_OBJ_IS_SPEC_CV_CONST_HANDLER,
+ ZEND_FETCH_OBJ_IS_SPEC_CV_TMPVAR_HANDLER,
+ ZEND_FETCH_OBJ_IS_SPEC_CV_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_FETCH_OBJ_IS_SPEC_CV_CV_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_FETCH_FUNC_ARG_SPEC_CONST_UNUSED_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_FETCH_FUNC_ARG_SPEC_TMPVAR_UNUSED_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_FETCH_FUNC_ARG_SPEC_TMPVAR_UNUSED_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_FETCH_FUNC_ARG_SPEC_CV_UNUSED_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_FETCH_DIM_FUNC_ARG_SPEC_CONST_CONST_HANDLER,
+ ZEND_FETCH_DIM_FUNC_ARG_SPEC_CONST_TMPVAR_HANDLER,
+ ZEND_FETCH_DIM_FUNC_ARG_SPEC_CONST_TMPVAR_HANDLER,
+ ZEND_FETCH_DIM_FUNC_ARG_SPEC_CONST_UNUSED_HANDLER,
+ ZEND_FETCH_DIM_FUNC_ARG_SPEC_CONST_CV_HANDLER,
+ ZEND_FETCH_DIM_FUNC_ARG_SPEC_TMP_CONST_HANDLER,
+ ZEND_FETCH_DIM_FUNC_ARG_SPEC_TMP_TMPVAR_HANDLER,
+ ZEND_FETCH_DIM_FUNC_ARG_SPEC_TMP_TMPVAR_HANDLER,
+ ZEND_FETCH_DIM_FUNC_ARG_SPEC_TMP_UNUSED_HANDLER,
+ ZEND_FETCH_DIM_FUNC_ARG_SPEC_TMP_CV_HANDLER,
+ ZEND_FETCH_DIM_FUNC_ARG_SPEC_VAR_CONST_HANDLER,
+ ZEND_FETCH_DIM_FUNC_ARG_SPEC_VAR_TMPVAR_HANDLER,
+ ZEND_FETCH_DIM_FUNC_ARG_SPEC_VAR_TMPVAR_HANDLER,
+ ZEND_FETCH_DIM_FUNC_ARG_SPEC_VAR_UNUSED_HANDLER,
+ ZEND_FETCH_DIM_FUNC_ARG_SPEC_VAR_CV_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_FETCH_DIM_FUNC_ARG_SPEC_CV_CONST_HANDLER,
+ ZEND_FETCH_DIM_FUNC_ARG_SPEC_CV_TMPVAR_HANDLER,
+ ZEND_FETCH_DIM_FUNC_ARG_SPEC_CV_TMPVAR_HANDLER,
+ ZEND_FETCH_DIM_FUNC_ARG_SPEC_CV_UNUSED_HANDLER,
+ ZEND_FETCH_DIM_FUNC_ARG_SPEC_CV_CV_HANDLER,
+ ZEND_FETCH_OBJ_FUNC_ARG_SPEC_CONST_CONST_HANDLER,
+ ZEND_FETCH_OBJ_FUNC_ARG_SPEC_CONST_TMPVAR_HANDLER,
+ ZEND_FETCH_OBJ_FUNC_ARG_SPEC_CONST_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_FETCH_OBJ_FUNC_ARG_SPEC_CONST_CV_HANDLER,
+ ZEND_FETCH_OBJ_FUNC_ARG_SPEC_TMP_CONST_HANDLER,
+ ZEND_FETCH_OBJ_FUNC_ARG_SPEC_TMP_TMPVAR_HANDLER,
+ ZEND_FETCH_OBJ_FUNC_ARG_SPEC_TMP_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_FETCH_OBJ_FUNC_ARG_SPEC_TMP_CV_HANDLER,
+ ZEND_FETCH_OBJ_FUNC_ARG_SPEC_VAR_CONST_HANDLER,
+ ZEND_FETCH_OBJ_FUNC_ARG_SPEC_VAR_TMPVAR_HANDLER,
+ ZEND_FETCH_OBJ_FUNC_ARG_SPEC_VAR_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_FETCH_OBJ_FUNC_ARG_SPEC_VAR_CV_HANDLER,
+ ZEND_FETCH_OBJ_FUNC_ARG_SPEC_UNUSED_CONST_HANDLER,
+ ZEND_FETCH_OBJ_FUNC_ARG_SPEC_UNUSED_TMPVAR_HANDLER,
+ ZEND_FETCH_OBJ_FUNC_ARG_SPEC_UNUSED_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_FETCH_OBJ_FUNC_ARG_SPEC_UNUSED_CV_HANDLER,
+ ZEND_FETCH_OBJ_FUNC_ARG_SPEC_CV_CONST_HANDLER,
+ ZEND_FETCH_OBJ_FUNC_ARG_SPEC_CV_TMPVAR_HANDLER,
+ ZEND_FETCH_OBJ_FUNC_ARG_SPEC_CV_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_FETCH_OBJ_FUNC_ARG_SPEC_CV_CV_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_FETCH_UNSET_SPEC_CONST_UNUSED_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_FETCH_UNSET_SPEC_TMPVAR_UNUSED_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_FETCH_UNSET_SPEC_TMPVAR_UNUSED_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_FETCH_UNSET_SPEC_CV_UNUSED_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_FETCH_DIM_UNSET_SPEC_VAR_CONST_HANDLER,
+ ZEND_FETCH_DIM_UNSET_SPEC_VAR_TMPVAR_HANDLER,
+ ZEND_FETCH_DIM_UNSET_SPEC_VAR_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_FETCH_DIM_UNSET_SPEC_VAR_CV_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_FETCH_DIM_UNSET_SPEC_CV_CONST_HANDLER,
+ ZEND_FETCH_DIM_UNSET_SPEC_CV_TMPVAR_HANDLER,
+ ZEND_FETCH_DIM_UNSET_SPEC_CV_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_FETCH_DIM_UNSET_SPEC_CV_CV_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_FETCH_OBJ_UNSET_SPEC_VAR_CONST_HANDLER,
+ ZEND_FETCH_OBJ_UNSET_SPEC_VAR_TMPVAR_HANDLER,
+ ZEND_FETCH_OBJ_UNSET_SPEC_VAR_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_FETCH_OBJ_UNSET_SPEC_VAR_CV_HANDLER,
+ ZEND_FETCH_OBJ_UNSET_SPEC_UNUSED_CONST_HANDLER,
+ ZEND_FETCH_OBJ_UNSET_SPEC_UNUSED_TMPVAR_HANDLER,
+ ZEND_FETCH_OBJ_UNSET_SPEC_UNUSED_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_FETCH_OBJ_UNSET_SPEC_UNUSED_CV_HANDLER,
+ ZEND_FETCH_OBJ_UNSET_SPEC_CV_CONST_HANDLER,
+ ZEND_FETCH_OBJ_UNSET_SPEC_CV_TMPVAR_HANDLER,
+ ZEND_FETCH_OBJ_UNSET_SPEC_CV_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_FETCH_OBJ_UNSET_SPEC_CV_CV_HANDLER,
+ ZEND_FETCH_LIST_SPEC_CONST_CONST_HANDLER,
+ ZEND_FETCH_LIST_SPEC_CONST_TMPVAR_HANDLER,
+ ZEND_FETCH_LIST_SPEC_CONST_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_FETCH_LIST_SPEC_CONST_CV_HANDLER,
+ ZEND_FETCH_LIST_SPEC_TMPVAR_CONST_HANDLER,
+ ZEND_FETCH_LIST_SPEC_TMPVAR_TMPVAR_HANDLER,
+ ZEND_FETCH_LIST_SPEC_TMPVAR_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_FETCH_LIST_SPEC_TMPVAR_CV_HANDLER,
+ ZEND_FETCH_LIST_SPEC_TMPVAR_CONST_HANDLER,
+ ZEND_FETCH_LIST_SPEC_TMPVAR_TMPVAR_HANDLER,
+ ZEND_FETCH_LIST_SPEC_TMPVAR_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_FETCH_LIST_SPEC_TMPVAR_CV_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_FETCH_LIST_SPEC_CV_CONST_HANDLER,
+ ZEND_FETCH_LIST_SPEC_CV_TMPVAR_HANDLER,
+ ZEND_FETCH_LIST_SPEC_CV_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_FETCH_LIST_SPEC_CV_CV_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,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_FETCH_CONSTANT_SPEC_UNUSED_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_EXT_STMT_SPEC_HANDLER,
+ ZEND_EXT_FCALL_BEGIN_SPEC_HANDLER,
+ ZEND_EXT_FCALL_END_SPEC_HANDLER,
+ ZEND_EXT_NOP_SPEC_HANDLER,
+ ZEND_TICKS_SPEC_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_SEND_VAR_NO_REF_SPEC_VAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_CATCH_SPEC_CONST_CV_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,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_THROW_SPEC_CONST_HANDLER,
+ ZEND_THROW_SPEC_TMP_HANDLER,
+ ZEND_THROW_SPEC_VAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_THROW_SPEC_CV_HANDLER,
+ ZEND_FETCH_CLASS_SPEC_CONST_HANDLER,
+ ZEND_FETCH_CLASS_SPEC_TMPVAR_HANDLER,
+ ZEND_FETCH_CLASS_SPEC_TMPVAR_HANDLER,
+ ZEND_FETCH_CLASS_SPEC_UNUSED_HANDLER,
+ ZEND_FETCH_CLASS_SPEC_CV_HANDLER,
+ ZEND_CLONE_SPEC_CONST_HANDLER,
+ ZEND_CLONE_SPEC_TMPVAR_HANDLER,
+ ZEND_CLONE_SPEC_TMPVAR_HANDLER,
+ ZEND_CLONE_SPEC_UNUSED_HANDLER,
+ ZEND_CLONE_SPEC_CV_HANDLER,
+ ZEND_RETURN_BY_REF_SPEC_CONST_HANDLER,
+ ZEND_RETURN_BY_REF_SPEC_TMP_HANDLER,
+ ZEND_RETURN_BY_REF_SPEC_VAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_RETURN_BY_REF_SPEC_CV_HANDLER,
+ ZEND_INIT_METHOD_CALL_SPEC_CONST_CONST_HANDLER,
+ ZEND_INIT_METHOD_CALL_SPEC_CONST_TMPVAR_HANDLER,
+ ZEND_INIT_METHOD_CALL_SPEC_CONST_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_INIT_METHOD_CALL_SPEC_CONST_CV_HANDLER,
+ ZEND_INIT_METHOD_CALL_SPEC_TMPVAR_CONST_HANDLER,
+ ZEND_INIT_METHOD_CALL_SPEC_TMPVAR_TMPVAR_HANDLER,
+ ZEND_INIT_METHOD_CALL_SPEC_TMPVAR_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_INIT_METHOD_CALL_SPEC_TMPVAR_CV_HANDLER,
+ ZEND_INIT_METHOD_CALL_SPEC_TMPVAR_CONST_HANDLER,
+ ZEND_INIT_METHOD_CALL_SPEC_TMPVAR_TMPVAR_HANDLER,
+ ZEND_INIT_METHOD_CALL_SPEC_TMPVAR_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_INIT_METHOD_CALL_SPEC_TMPVAR_CV_HANDLER,
+ ZEND_INIT_METHOD_CALL_SPEC_UNUSED_CONST_HANDLER,
+ ZEND_INIT_METHOD_CALL_SPEC_UNUSED_TMPVAR_HANDLER,
+ ZEND_INIT_METHOD_CALL_SPEC_UNUSED_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_INIT_METHOD_CALL_SPEC_UNUSED_CV_HANDLER,
+ ZEND_INIT_METHOD_CALL_SPEC_CV_CONST_HANDLER,
+ ZEND_INIT_METHOD_CALL_SPEC_CV_TMPVAR_HANDLER,
+ ZEND_INIT_METHOD_CALL_SPEC_CV_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_INIT_METHOD_CALL_SPEC_CV_CV_HANDLER,
+ ZEND_INIT_STATIC_METHOD_CALL_SPEC_CONST_CONST_HANDLER,
+ ZEND_INIT_STATIC_METHOD_CALL_SPEC_CONST_TMPVAR_HANDLER,
+ ZEND_INIT_STATIC_METHOD_CALL_SPEC_CONST_TMPVAR_HANDLER,
+ ZEND_INIT_STATIC_METHOD_CALL_SPEC_CONST_UNUSED_HANDLER,
+ ZEND_INIT_STATIC_METHOD_CALL_SPEC_CONST_CV_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_INIT_STATIC_METHOD_CALL_SPEC_VAR_CONST_HANDLER,
+ ZEND_INIT_STATIC_METHOD_CALL_SPEC_VAR_TMPVAR_HANDLER,
+ ZEND_INIT_STATIC_METHOD_CALL_SPEC_VAR_TMPVAR_HANDLER,
+ ZEND_INIT_STATIC_METHOD_CALL_SPEC_VAR_UNUSED_HANDLER,
+ ZEND_INIT_STATIC_METHOD_CALL_SPEC_VAR_CV_HANDLER,
+ ZEND_INIT_STATIC_METHOD_CALL_SPEC_UNUSED_CONST_HANDLER,
+ ZEND_INIT_STATIC_METHOD_CALL_SPEC_UNUSED_TMPVAR_HANDLER,
+ ZEND_INIT_STATIC_METHOD_CALL_SPEC_UNUSED_TMPVAR_HANDLER,
+ ZEND_INIT_STATIC_METHOD_CALL_SPEC_UNUSED_UNUSED_HANDLER,
+ ZEND_INIT_STATIC_METHOD_CALL_SPEC_UNUSED_CV_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_ISSET_ISEMPTY_VAR_SPEC_CONST_UNUSED_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ISSET_ISEMPTY_VAR_SPEC_TMPVAR_UNUSED_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ISSET_ISEMPTY_VAR_SPEC_TMPVAR_UNUSED_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_ISSET_ISEMPTY_VAR_SPEC_CV_UNUSED_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ISSET_ISEMPTY_DIM_OBJ_SPEC_CONST_CONST_HANDLER,
+ ZEND_ISSET_ISEMPTY_DIM_OBJ_SPEC_CONST_TMPVAR_HANDLER,
+ ZEND_ISSET_ISEMPTY_DIM_OBJ_SPEC_CONST_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ISSET_ISEMPTY_DIM_OBJ_SPEC_CONST_CV_HANDLER,
+ ZEND_ISSET_ISEMPTY_DIM_OBJ_SPEC_TMPVAR_CONST_HANDLER,
+ ZEND_ISSET_ISEMPTY_DIM_OBJ_SPEC_TMPVAR_TMPVAR_HANDLER,
+ ZEND_ISSET_ISEMPTY_DIM_OBJ_SPEC_TMPVAR_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ISSET_ISEMPTY_DIM_OBJ_SPEC_TMPVAR_CV_HANDLER,
+ ZEND_ISSET_ISEMPTY_DIM_OBJ_SPEC_TMPVAR_CONST_HANDLER,
+ ZEND_ISSET_ISEMPTY_DIM_OBJ_SPEC_TMPVAR_TMPVAR_HANDLER,
+ ZEND_ISSET_ISEMPTY_DIM_OBJ_SPEC_TMPVAR_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ISSET_ISEMPTY_DIM_OBJ_SPEC_TMPVAR_CV_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ISSET_ISEMPTY_DIM_OBJ_SPEC_CV_CONST_HANDLER,
+ ZEND_ISSET_ISEMPTY_DIM_OBJ_SPEC_CV_TMPVAR_HANDLER,
+ ZEND_ISSET_ISEMPTY_DIM_OBJ_SPEC_CV_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ISSET_ISEMPTY_DIM_OBJ_SPEC_CV_CV_HANDLER,
+ ZEND_SEND_VAL_EX_SPEC_CONST_HANDLER,
+ ZEND_SEND_VAL_EX_SPEC_CONST_QUICK_HANDLER,
+ ZEND_SEND_VAL_EX_SPEC_TMP_HANDLER,
+ ZEND_SEND_VAL_EX_SPEC_TMP_QUICK_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_SEND_VAR_SPEC_VAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_SEND_VAR_SPEC_CV_HANDLER,
+ ZEND_INIT_USER_CALL_SPEC_CONST_CONST_HANDLER,
+ ZEND_INIT_USER_CALL_SPEC_CONST_TMPVAR_HANDLER,
+ ZEND_INIT_USER_CALL_SPEC_CONST_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_INIT_USER_CALL_SPEC_CONST_CV_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,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_SEND_ARRAY_SPEC_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_SEND_USER_SPEC_VAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_SEND_USER_SPEC_CV_HANDLER,
+ ZEND_STRLEN_SPEC_CONST_HANDLER,
+ ZEND_STRLEN_SPEC_TMPVAR_HANDLER,
+ ZEND_STRLEN_SPEC_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_STRLEN_SPEC_CV_HANDLER,
+ ZEND_DEFINED_SPEC_CONST_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_TYPE_CHECK_SPEC_CONST_HANDLER,
+ ZEND_TYPE_CHECK_SPEC_TMP_HANDLER,
+ ZEND_TYPE_CHECK_SPEC_VAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_TYPE_CHECK_SPEC_CV_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_VERIFY_RETURN_TYPE_SPEC_CONST_UNUSED_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_VERIFY_RETURN_TYPE_SPEC_TMP_UNUSED_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_VERIFY_RETURN_TYPE_SPEC_VAR_UNUSED_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_VERIFY_RETURN_TYPE_SPEC_UNUSED_UNUSED_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_VERIFY_RETURN_TYPE_SPEC_CV_UNUSED_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_FE_RESET_RW_SPEC_CONST_HANDLER,
+ ZEND_FE_RESET_RW_SPEC_TMP_HANDLER,
+ ZEND_FE_RESET_RW_SPEC_VAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_FE_RESET_RW_SPEC_CV_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_FE_FETCH_RW_SPEC_VAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_FE_FREE_SPEC_TMPVAR_HANDLER,
+ ZEND_FE_FREE_SPEC_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_INIT_DYNAMIC_CALL_SPEC_CONST_HANDLER,
+ ZEND_INIT_DYNAMIC_CALL_SPEC_TMPVAR_HANDLER,
+ ZEND_INIT_DYNAMIC_CALL_SPEC_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_INIT_DYNAMIC_CALL_SPEC_CV_HANDLER,
+ ZEND_DO_ICALL_SPEC_RETVAL_UNUSED_HANDLER,
+ ZEND_DO_ICALL_SPEC_RETVAL_USED_HANDLER,
+ ZEND_DO_UCALL_SPEC_RETVAL_UNUSED_HANDLER,
+ ZEND_DO_UCALL_SPEC_RETVAL_USED_HANDLER,
+ ZEND_DO_FCALL_BY_NAME_SPEC_RETVAL_UNUSED_HANDLER,
+ ZEND_DO_FCALL_BY_NAME_SPEC_RETVAL_USED_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_PRE_INC_OBJ_SPEC_VAR_CONST_HANDLER,
+ ZEND_PRE_INC_OBJ_SPEC_VAR_TMPVAR_HANDLER,
+ ZEND_PRE_INC_OBJ_SPEC_VAR_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_PRE_INC_OBJ_SPEC_VAR_CV_HANDLER,
+ ZEND_PRE_INC_OBJ_SPEC_UNUSED_CONST_HANDLER,
+ ZEND_PRE_INC_OBJ_SPEC_UNUSED_TMPVAR_HANDLER,
+ ZEND_PRE_INC_OBJ_SPEC_UNUSED_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_PRE_INC_OBJ_SPEC_UNUSED_CV_HANDLER,
+ ZEND_PRE_INC_OBJ_SPEC_CV_CONST_HANDLER,
+ ZEND_PRE_INC_OBJ_SPEC_CV_TMPVAR_HANDLER,
+ ZEND_PRE_INC_OBJ_SPEC_CV_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_PRE_INC_OBJ_SPEC_CV_CV_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_PRE_DEC_OBJ_SPEC_VAR_CONST_HANDLER,
+ ZEND_PRE_DEC_OBJ_SPEC_VAR_TMPVAR_HANDLER,
+ ZEND_PRE_DEC_OBJ_SPEC_VAR_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_PRE_DEC_OBJ_SPEC_VAR_CV_HANDLER,
+ ZEND_PRE_DEC_OBJ_SPEC_UNUSED_CONST_HANDLER,
+ ZEND_PRE_DEC_OBJ_SPEC_UNUSED_TMPVAR_HANDLER,
+ ZEND_PRE_DEC_OBJ_SPEC_UNUSED_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_PRE_DEC_OBJ_SPEC_UNUSED_CV_HANDLER,
+ ZEND_PRE_DEC_OBJ_SPEC_CV_CONST_HANDLER,
+ ZEND_PRE_DEC_OBJ_SPEC_CV_TMPVAR_HANDLER,
+ ZEND_PRE_DEC_OBJ_SPEC_CV_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_PRE_DEC_OBJ_SPEC_CV_CV_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_POST_INC_OBJ_SPEC_VAR_CONST_HANDLER,
+ ZEND_POST_INC_OBJ_SPEC_VAR_TMPVAR_HANDLER,
+ ZEND_POST_INC_OBJ_SPEC_VAR_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_POST_INC_OBJ_SPEC_VAR_CV_HANDLER,
+ ZEND_POST_INC_OBJ_SPEC_UNUSED_CONST_HANDLER,
+ ZEND_POST_INC_OBJ_SPEC_UNUSED_TMPVAR_HANDLER,
+ ZEND_POST_INC_OBJ_SPEC_UNUSED_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_POST_INC_OBJ_SPEC_UNUSED_CV_HANDLER,
+ ZEND_POST_INC_OBJ_SPEC_CV_CONST_HANDLER,
+ ZEND_POST_INC_OBJ_SPEC_CV_TMPVAR_HANDLER,
+ ZEND_POST_INC_OBJ_SPEC_CV_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_POST_INC_OBJ_SPEC_CV_CV_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_POST_DEC_OBJ_SPEC_VAR_CONST_HANDLER,
+ ZEND_POST_DEC_OBJ_SPEC_VAR_TMPVAR_HANDLER,
+ ZEND_POST_DEC_OBJ_SPEC_VAR_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_POST_DEC_OBJ_SPEC_VAR_CV_HANDLER,
+ ZEND_POST_DEC_OBJ_SPEC_UNUSED_CONST_HANDLER,
+ ZEND_POST_DEC_OBJ_SPEC_UNUSED_TMPVAR_HANDLER,
+ ZEND_POST_DEC_OBJ_SPEC_UNUSED_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_POST_DEC_OBJ_SPEC_UNUSED_CV_HANDLER,
+ ZEND_POST_DEC_OBJ_SPEC_CV_CONST_HANDLER,
+ ZEND_POST_DEC_OBJ_SPEC_CV_TMPVAR_HANDLER,
+ ZEND_POST_DEC_OBJ_SPEC_CV_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_POST_DEC_OBJ_SPEC_CV_CV_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,
+ 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,
+ 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,
+ 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_ASSIGN_OBJ_SPEC_VAR_CONST_OP_DATA_CONST_HANDLER,
+ ZEND_ASSIGN_OBJ_SPEC_VAR_CONST_OP_DATA_TMP_HANDLER,
+ ZEND_ASSIGN_OBJ_SPEC_VAR_CONST_OP_DATA_VAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_OBJ_SPEC_VAR_CONST_OP_DATA_CV_HANDLER,
+ ZEND_ASSIGN_OBJ_SPEC_VAR_TMPVAR_OP_DATA_CONST_HANDLER,
+ ZEND_ASSIGN_OBJ_SPEC_VAR_TMPVAR_OP_DATA_TMP_HANDLER,
+ ZEND_ASSIGN_OBJ_SPEC_VAR_TMPVAR_OP_DATA_VAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_OBJ_SPEC_VAR_TMPVAR_OP_DATA_CV_HANDLER,
+ ZEND_ASSIGN_OBJ_SPEC_VAR_TMPVAR_OP_DATA_CONST_HANDLER,
+ ZEND_ASSIGN_OBJ_SPEC_VAR_TMPVAR_OP_DATA_TMP_HANDLER,
+ ZEND_ASSIGN_OBJ_SPEC_VAR_TMPVAR_OP_DATA_VAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_OBJ_SPEC_VAR_TMPVAR_OP_DATA_CV_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_OBJ_SPEC_VAR_CV_OP_DATA_CONST_HANDLER,
+ ZEND_ASSIGN_OBJ_SPEC_VAR_CV_OP_DATA_TMP_HANDLER,
+ ZEND_ASSIGN_OBJ_SPEC_VAR_CV_OP_DATA_VAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_OBJ_SPEC_VAR_CV_OP_DATA_CV_HANDLER,
+ ZEND_ASSIGN_OBJ_SPEC_UNUSED_CONST_OP_DATA_CONST_HANDLER,
+ ZEND_ASSIGN_OBJ_SPEC_UNUSED_CONST_OP_DATA_TMP_HANDLER,
+ ZEND_ASSIGN_OBJ_SPEC_UNUSED_CONST_OP_DATA_VAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_OBJ_SPEC_UNUSED_CONST_OP_DATA_CV_HANDLER,
+ ZEND_ASSIGN_OBJ_SPEC_UNUSED_TMPVAR_OP_DATA_CONST_HANDLER,
+ ZEND_ASSIGN_OBJ_SPEC_UNUSED_TMPVAR_OP_DATA_TMP_HANDLER,
+ ZEND_ASSIGN_OBJ_SPEC_UNUSED_TMPVAR_OP_DATA_VAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_OBJ_SPEC_UNUSED_TMPVAR_OP_DATA_CV_HANDLER,
+ ZEND_ASSIGN_OBJ_SPEC_UNUSED_TMPVAR_OP_DATA_CONST_HANDLER,
+ ZEND_ASSIGN_OBJ_SPEC_UNUSED_TMPVAR_OP_DATA_TMP_HANDLER,
+ ZEND_ASSIGN_OBJ_SPEC_UNUSED_TMPVAR_OP_DATA_VAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_OBJ_SPEC_UNUSED_TMPVAR_OP_DATA_CV_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_OBJ_SPEC_UNUSED_CV_OP_DATA_CONST_HANDLER,
+ ZEND_ASSIGN_OBJ_SPEC_UNUSED_CV_OP_DATA_TMP_HANDLER,
+ ZEND_ASSIGN_OBJ_SPEC_UNUSED_CV_OP_DATA_VAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_OBJ_SPEC_UNUSED_CV_OP_DATA_CV_HANDLER,
+ ZEND_ASSIGN_OBJ_SPEC_CV_CONST_OP_DATA_CONST_HANDLER,
+ ZEND_ASSIGN_OBJ_SPEC_CV_CONST_OP_DATA_TMP_HANDLER,
+ ZEND_ASSIGN_OBJ_SPEC_CV_CONST_OP_DATA_VAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_OBJ_SPEC_CV_CONST_OP_DATA_CV_HANDLER,
+ ZEND_ASSIGN_OBJ_SPEC_CV_TMPVAR_OP_DATA_CONST_HANDLER,
+ ZEND_ASSIGN_OBJ_SPEC_CV_TMPVAR_OP_DATA_TMP_HANDLER,
+ ZEND_ASSIGN_OBJ_SPEC_CV_TMPVAR_OP_DATA_VAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_OBJ_SPEC_CV_TMPVAR_OP_DATA_CV_HANDLER,
+ ZEND_ASSIGN_OBJ_SPEC_CV_TMPVAR_OP_DATA_CONST_HANDLER,
+ ZEND_ASSIGN_OBJ_SPEC_CV_TMPVAR_OP_DATA_TMP_HANDLER,
+ ZEND_ASSIGN_OBJ_SPEC_CV_TMPVAR_OP_DATA_VAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_OBJ_SPEC_CV_TMPVAR_OP_DATA_CV_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_OBJ_SPEC_CV_CV_OP_DATA_CONST_HANDLER,
+ ZEND_ASSIGN_OBJ_SPEC_CV_CV_OP_DATA_TMP_HANDLER,
+ ZEND_ASSIGN_OBJ_SPEC_CV_CV_OP_DATA_VAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_OBJ_SPEC_CV_CV_OP_DATA_CV_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_INSTANCEOF_SPEC_TMPVAR_CONST_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_INSTANCEOF_SPEC_TMPVAR_VAR_HANDLER,
+ ZEND_INSTANCEOF_SPEC_TMPVAR_UNUSED_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_INSTANCEOF_SPEC_TMPVAR_CONST_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_INSTANCEOF_SPEC_TMPVAR_VAR_HANDLER,
+ ZEND_INSTANCEOF_SPEC_TMPVAR_UNUSED_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_INSTANCEOF_SPEC_CV_CONST_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_INSTANCEOF_SPEC_CV_VAR_HANDLER,
+ ZEND_INSTANCEOF_SPEC_CV_UNUSED_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_DECLARE_CLASS_SPEC_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_DECLARE_INHERITED_CLASS_SPEC_VAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_DECLARE_FUNCTION_SPEC_HANDLER,
+ ZEND_YIELD_FROM_SPEC_CONST_HANDLER,
+ ZEND_YIELD_FROM_SPEC_TMP_HANDLER,
+ ZEND_YIELD_FROM_SPEC_VAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_YIELD_FROM_SPEC_CV_HANDLER,
+ ZEND_DECLARE_CONST_SPEC_CONST_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,
+ 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_ADD_INTERFACE_SPEC_CONST_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_DECLARE_INHERITED_CLASS_DELAYED_SPEC_VAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_VERIFY_ABSTRACT_CLASS_SPEC_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,
+ 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,
+ 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,
+ 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_ASSIGN_DIM_SPEC_VAR_CONST_OP_DATA_CONST_HANDLER,
+ ZEND_ASSIGN_DIM_SPEC_VAR_CONST_OP_DATA_TMP_HANDLER,
+ ZEND_ASSIGN_DIM_SPEC_VAR_CONST_OP_DATA_VAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_DIM_SPEC_VAR_CONST_OP_DATA_CV_HANDLER,
+ ZEND_ASSIGN_DIM_SPEC_VAR_TMPVAR_OP_DATA_CONST_HANDLER,
+ ZEND_ASSIGN_DIM_SPEC_VAR_TMPVAR_OP_DATA_TMP_HANDLER,
+ ZEND_ASSIGN_DIM_SPEC_VAR_TMPVAR_OP_DATA_VAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_DIM_SPEC_VAR_TMPVAR_OP_DATA_CV_HANDLER,
+ ZEND_ASSIGN_DIM_SPEC_VAR_TMPVAR_OP_DATA_CONST_HANDLER,
+ ZEND_ASSIGN_DIM_SPEC_VAR_TMPVAR_OP_DATA_TMP_HANDLER,
+ ZEND_ASSIGN_DIM_SPEC_VAR_TMPVAR_OP_DATA_VAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_DIM_SPEC_VAR_TMPVAR_OP_DATA_CV_HANDLER,
+ ZEND_ASSIGN_DIM_SPEC_VAR_UNUSED_OP_DATA_CONST_HANDLER,
+ ZEND_ASSIGN_DIM_SPEC_VAR_UNUSED_OP_DATA_TMP_HANDLER,
+ ZEND_ASSIGN_DIM_SPEC_VAR_UNUSED_OP_DATA_VAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_DIM_SPEC_VAR_UNUSED_OP_DATA_CV_HANDLER,
+ ZEND_ASSIGN_DIM_SPEC_VAR_CV_OP_DATA_CONST_HANDLER,
+ ZEND_ASSIGN_DIM_SPEC_VAR_CV_OP_DATA_TMP_HANDLER,
+ ZEND_ASSIGN_DIM_SPEC_VAR_CV_OP_DATA_VAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_DIM_SPEC_VAR_CV_OP_DATA_CV_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,
+ 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_ASSIGN_DIM_SPEC_CV_CONST_OP_DATA_CONST_HANDLER,
+ ZEND_ASSIGN_DIM_SPEC_CV_CONST_OP_DATA_TMP_HANDLER,
+ ZEND_ASSIGN_DIM_SPEC_CV_CONST_OP_DATA_VAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_DIM_SPEC_CV_CONST_OP_DATA_CV_HANDLER,
+ ZEND_ASSIGN_DIM_SPEC_CV_TMPVAR_OP_DATA_CONST_HANDLER,
+ ZEND_ASSIGN_DIM_SPEC_CV_TMPVAR_OP_DATA_TMP_HANDLER,
+ ZEND_ASSIGN_DIM_SPEC_CV_TMPVAR_OP_DATA_VAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_DIM_SPEC_CV_TMPVAR_OP_DATA_CV_HANDLER,
+ ZEND_ASSIGN_DIM_SPEC_CV_TMPVAR_OP_DATA_CONST_HANDLER,
+ ZEND_ASSIGN_DIM_SPEC_CV_TMPVAR_OP_DATA_TMP_HANDLER,
+ ZEND_ASSIGN_DIM_SPEC_CV_TMPVAR_OP_DATA_VAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_DIM_SPEC_CV_TMPVAR_OP_DATA_CV_HANDLER,
+ ZEND_ASSIGN_DIM_SPEC_CV_UNUSED_OP_DATA_CONST_HANDLER,
+ ZEND_ASSIGN_DIM_SPEC_CV_UNUSED_OP_DATA_TMP_HANDLER,
+ ZEND_ASSIGN_DIM_SPEC_CV_UNUSED_OP_DATA_VAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_DIM_SPEC_CV_UNUSED_OP_DATA_CV_HANDLER,
+ ZEND_ASSIGN_DIM_SPEC_CV_CV_OP_DATA_CONST_HANDLER,
+ ZEND_ASSIGN_DIM_SPEC_CV_CV_OP_DATA_TMP_HANDLER,
+ ZEND_ASSIGN_DIM_SPEC_CV_CV_OP_DATA_VAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_DIM_SPEC_CV_CV_OP_DATA_CV_HANDLER,
+ ZEND_ISSET_ISEMPTY_PROP_OBJ_SPEC_CONST_CONST_HANDLER,
+ ZEND_ISSET_ISEMPTY_PROP_OBJ_SPEC_CONST_TMPVAR_HANDLER,
+ ZEND_ISSET_ISEMPTY_PROP_OBJ_SPEC_CONST_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ISSET_ISEMPTY_PROP_OBJ_SPEC_CONST_CV_HANDLER,
+ ZEND_ISSET_ISEMPTY_PROP_OBJ_SPEC_TMPVAR_CONST_HANDLER,
+ ZEND_ISSET_ISEMPTY_PROP_OBJ_SPEC_TMPVAR_TMPVAR_HANDLER,
+ ZEND_ISSET_ISEMPTY_PROP_OBJ_SPEC_TMPVAR_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ISSET_ISEMPTY_PROP_OBJ_SPEC_TMPVAR_CV_HANDLER,
+ ZEND_ISSET_ISEMPTY_PROP_OBJ_SPEC_TMPVAR_CONST_HANDLER,
+ ZEND_ISSET_ISEMPTY_PROP_OBJ_SPEC_TMPVAR_TMPVAR_HANDLER,
+ ZEND_ISSET_ISEMPTY_PROP_OBJ_SPEC_TMPVAR_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ISSET_ISEMPTY_PROP_OBJ_SPEC_TMPVAR_CV_HANDLER,
+ ZEND_ISSET_ISEMPTY_PROP_OBJ_SPEC_UNUSED_CONST_HANDLER,
+ ZEND_ISSET_ISEMPTY_PROP_OBJ_SPEC_UNUSED_TMPVAR_HANDLER,
+ ZEND_ISSET_ISEMPTY_PROP_OBJ_SPEC_UNUSED_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ISSET_ISEMPTY_PROP_OBJ_SPEC_UNUSED_CV_HANDLER,
+ ZEND_ISSET_ISEMPTY_PROP_OBJ_SPEC_CV_CONST_HANDLER,
+ ZEND_ISSET_ISEMPTY_PROP_OBJ_SPEC_CV_TMPVAR_HANDLER,
+ ZEND_ISSET_ISEMPTY_PROP_OBJ_SPEC_CV_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ISSET_ISEMPTY_PROP_OBJ_SPEC_CV_CV_HANDLER,
+ ZEND_HANDLE_EXCEPTION_SPEC_HANDLER,
+ ZEND_USER_OPCODE_SPEC_HANDLER,
+ ZEND_ASSERT_CHECK_SPEC_HANDLER,
+ ZEND_JMP_SET_SPEC_CONST_HANDLER,
+ ZEND_JMP_SET_SPEC_TMP_HANDLER,
+ ZEND_JMP_SET_SPEC_VAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_JMP_SET_SPEC_CV_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_DECLARE_LAMBDA_FUNCTION_SPEC_CONST_UNUSED_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,
+ 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_ADD_TRAIT_SPEC_HANDLER,
+ ZEND_BIND_TRAITS_SPEC_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,
+ ZEND_SEPARATE_SPEC_VAR_UNUSED_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_FETCH_CLASS_NAME_SPEC_HANDLER,
+ ZEND_CALL_TRAMPOLINE_SPEC_HANDLER,
+ ZEND_DISCARD_EXCEPTION_SPEC_HANDLER,
+ ZEND_YIELD_SPEC_CONST_CONST_HANDLER,
+ ZEND_YIELD_SPEC_CONST_TMP_HANDLER,
+ ZEND_YIELD_SPEC_CONST_VAR_HANDLER,
+ ZEND_YIELD_SPEC_CONST_UNUSED_HANDLER,
+ ZEND_YIELD_SPEC_CONST_CV_HANDLER,
+ ZEND_YIELD_SPEC_TMP_CONST_HANDLER,
+ ZEND_YIELD_SPEC_TMP_TMP_HANDLER,
+ ZEND_YIELD_SPEC_TMP_VAR_HANDLER,
+ ZEND_YIELD_SPEC_TMP_UNUSED_HANDLER,
+ ZEND_YIELD_SPEC_TMP_CV_HANDLER,
+ ZEND_YIELD_SPEC_VAR_CONST_HANDLER,
+ ZEND_YIELD_SPEC_VAR_TMP_HANDLER,
+ ZEND_YIELD_SPEC_VAR_VAR_HANDLER,
+ ZEND_YIELD_SPEC_VAR_UNUSED_HANDLER,
+ ZEND_YIELD_SPEC_VAR_CV_HANDLER,
+ ZEND_YIELD_SPEC_UNUSED_CONST_HANDLER,
+ ZEND_YIELD_SPEC_UNUSED_TMP_HANDLER,
+ ZEND_YIELD_SPEC_UNUSED_VAR_HANDLER,
+ ZEND_YIELD_SPEC_UNUSED_UNUSED_HANDLER,
+ ZEND_YIELD_SPEC_UNUSED_CV_HANDLER,
+ ZEND_YIELD_SPEC_CV_CONST_HANDLER,
+ ZEND_YIELD_SPEC_CV_TMP_HANDLER,
+ ZEND_YIELD_SPEC_CV_VAR_HANDLER,
+ ZEND_YIELD_SPEC_CV_UNUSED_HANDLER,
+ ZEND_YIELD_SPEC_CV_CV_HANDLER,
+ ZEND_GENERATOR_RETURN_SPEC_CONST_HANDLER,
+ ZEND_GENERATOR_RETURN_SPEC_TMP_HANDLER,
+ ZEND_GENERATOR_RETURN_SPEC_VAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_GENERATOR_RETURN_SPEC_CV_HANDLER,
+ ZEND_FAST_CALL_SPEC_HANDLER,
+ ZEND_FAST_RET_SPEC_HANDLER,
+ ZEND_RECV_VARIADIC_SPEC_HANDLER,
+ ZEND_SEND_UNPACK_SPEC_HANDLER,
+ ZEND_POW_SPEC_CONST_CONST_HANDLER,
+ ZEND_POW_SPEC_CONST_TMPVAR_HANDLER,
+ ZEND_POW_SPEC_CONST_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_POW_SPEC_CONST_CV_HANDLER,
+ ZEND_POW_SPEC_TMPVAR_CONST_HANDLER,
+ ZEND_POW_SPEC_TMPVAR_TMPVAR_HANDLER,
+ ZEND_POW_SPEC_TMPVAR_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_POW_SPEC_TMPVAR_CV_HANDLER,
+ ZEND_POW_SPEC_TMPVAR_CONST_HANDLER,
+ ZEND_POW_SPEC_TMPVAR_TMPVAR_HANDLER,
+ ZEND_POW_SPEC_TMPVAR_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_POW_SPEC_TMPVAR_CV_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_POW_SPEC_CV_CONST_HANDLER,
+ ZEND_POW_SPEC_CV_TMPVAR_HANDLER,
+ ZEND_POW_SPEC_CV_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_POW_SPEC_CV_CV_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,
+ 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,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_POW_SPEC_VAR_CONST_HANDLER,
+ ZEND_ASSIGN_POW_SPEC_VAR_CONST_DIM_HANDLER,
+ ZEND_ASSIGN_POW_SPEC_VAR_CONST_OBJ_HANDLER,
+ ZEND_ASSIGN_POW_SPEC_VAR_TMPVAR_HANDLER,
+ ZEND_ASSIGN_POW_SPEC_VAR_TMPVAR_DIM_HANDLER,
+ ZEND_ASSIGN_POW_SPEC_VAR_TMPVAR_OBJ_HANDLER,
+ ZEND_ASSIGN_POW_SPEC_VAR_TMPVAR_HANDLER,
+ ZEND_ASSIGN_POW_SPEC_VAR_TMPVAR_DIM_HANDLER,
+ ZEND_ASSIGN_POW_SPEC_VAR_TMPVAR_OBJ_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_POW_SPEC_VAR_UNUSED_DIM_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_POW_SPEC_VAR_CV_HANDLER,
+ ZEND_ASSIGN_POW_SPEC_VAR_CV_DIM_HANDLER,
+ ZEND_ASSIGN_POW_SPEC_VAR_CV_OBJ_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_POW_SPEC_UNUSED_CONST_OBJ_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_POW_SPEC_UNUSED_TMPVAR_OBJ_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_POW_SPEC_UNUSED_TMPVAR_OBJ_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_POW_SPEC_UNUSED_CV_OBJ_HANDLER,
+ ZEND_ASSIGN_POW_SPEC_CV_CONST_HANDLER,
+ ZEND_ASSIGN_POW_SPEC_CV_CONST_DIM_HANDLER,
+ ZEND_ASSIGN_POW_SPEC_CV_CONST_OBJ_HANDLER,
+ ZEND_ASSIGN_POW_SPEC_CV_TMPVAR_HANDLER,
+ ZEND_ASSIGN_POW_SPEC_CV_TMPVAR_DIM_HANDLER,
+ ZEND_ASSIGN_POW_SPEC_CV_TMPVAR_OBJ_HANDLER,
+ ZEND_ASSIGN_POW_SPEC_CV_TMPVAR_HANDLER,
+ ZEND_ASSIGN_POW_SPEC_CV_TMPVAR_DIM_HANDLER,
+ ZEND_ASSIGN_POW_SPEC_CV_TMPVAR_OBJ_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_POW_SPEC_CV_UNUSED_DIM_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ASSIGN_POW_SPEC_CV_CV_HANDLER,
+ ZEND_ASSIGN_POW_SPEC_CV_CV_DIM_HANDLER,
+ ZEND_ASSIGN_POW_SPEC_CV_CV_OBJ_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,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_BIND_GLOBAL_SPEC_CV_CONST_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_COALESCE_SPEC_CONST_HANDLER,
+ ZEND_COALESCE_SPEC_TMP_HANDLER,
+ ZEND_COALESCE_SPEC_VAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_COALESCE_SPEC_CV_HANDLER,
+ ZEND_SPACESHIP_SPEC_CONST_CONST_HANDLER,
+ ZEND_SPACESHIP_SPEC_CONST_TMPVAR_HANDLER,
+ ZEND_SPACESHIP_SPEC_CONST_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_SPACESHIP_SPEC_CONST_CV_HANDLER,
+ ZEND_SPACESHIP_SPEC_TMPVAR_CONST_HANDLER,
+ ZEND_SPACESHIP_SPEC_TMPVAR_TMPVAR_HANDLER,
+ ZEND_SPACESHIP_SPEC_TMPVAR_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_SPACESHIP_SPEC_TMPVAR_CV_HANDLER,
+ ZEND_SPACESHIP_SPEC_TMPVAR_CONST_HANDLER,
+ ZEND_SPACESHIP_SPEC_TMPVAR_TMPVAR_HANDLER,
+ ZEND_SPACESHIP_SPEC_TMPVAR_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_SPACESHIP_SPEC_TMPVAR_CV_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_SPACESHIP_SPEC_CV_CONST_HANDLER,
+ ZEND_SPACESHIP_SPEC_CV_TMPVAR_HANDLER,
+ ZEND_SPACESHIP_SPEC_CV_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_SPACESHIP_SPEC_CV_CV_HANDLER,
+ ZEND_DECLARE_ANON_CLASS_SPEC_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_DECLARE_ANON_INHERITED_CLASS_SPEC_VAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_FETCH_STATIC_PROP_R_SPEC_CONST_CONST_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_FETCH_STATIC_PROP_R_SPEC_CONST_VAR_HANDLER,
+ ZEND_FETCH_STATIC_PROP_R_SPEC_CONST_UNUSED_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_FETCH_STATIC_PROP_R_SPEC_TMPVAR_CONST_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_FETCH_STATIC_PROP_R_SPEC_TMPVAR_VAR_HANDLER,
+ ZEND_FETCH_STATIC_PROP_R_SPEC_TMPVAR_UNUSED_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_FETCH_STATIC_PROP_R_SPEC_TMPVAR_CONST_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_FETCH_STATIC_PROP_R_SPEC_TMPVAR_VAR_HANDLER,
+ ZEND_FETCH_STATIC_PROP_R_SPEC_TMPVAR_UNUSED_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_FETCH_STATIC_PROP_R_SPEC_CV_CONST_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_FETCH_STATIC_PROP_R_SPEC_CV_VAR_HANDLER,
+ ZEND_FETCH_STATIC_PROP_R_SPEC_CV_UNUSED_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_FETCH_STATIC_PROP_W_SPEC_CONST_CONST_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_FETCH_STATIC_PROP_W_SPEC_CONST_VAR_HANDLER,
+ ZEND_FETCH_STATIC_PROP_W_SPEC_CONST_UNUSED_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_FETCH_STATIC_PROP_W_SPEC_TMPVAR_CONST_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_FETCH_STATIC_PROP_W_SPEC_TMPVAR_VAR_HANDLER,
+ ZEND_FETCH_STATIC_PROP_W_SPEC_TMPVAR_UNUSED_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_FETCH_STATIC_PROP_W_SPEC_TMPVAR_CONST_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_FETCH_STATIC_PROP_W_SPEC_TMPVAR_VAR_HANDLER,
+ ZEND_FETCH_STATIC_PROP_W_SPEC_TMPVAR_UNUSED_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_FETCH_STATIC_PROP_W_SPEC_CV_CONST_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_FETCH_STATIC_PROP_W_SPEC_CV_VAR_HANDLER,
+ ZEND_FETCH_STATIC_PROP_W_SPEC_CV_UNUSED_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_FETCH_STATIC_PROP_RW_SPEC_CONST_CONST_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_FETCH_STATIC_PROP_RW_SPEC_CONST_VAR_HANDLER,
+ ZEND_FETCH_STATIC_PROP_RW_SPEC_CONST_UNUSED_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_FETCH_STATIC_PROP_RW_SPEC_TMPVAR_CONST_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_FETCH_STATIC_PROP_RW_SPEC_TMPVAR_VAR_HANDLER,
+ ZEND_FETCH_STATIC_PROP_RW_SPEC_TMPVAR_UNUSED_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_FETCH_STATIC_PROP_RW_SPEC_TMPVAR_CONST_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_FETCH_STATIC_PROP_RW_SPEC_TMPVAR_VAR_HANDLER,
+ ZEND_FETCH_STATIC_PROP_RW_SPEC_TMPVAR_UNUSED_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_FETCH_STATIC_PROP_RW_SPEC_CV_CONST_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_FETCH_STATIC_PROP_RW_SPEC_CV_VAR_HANDLER,
+ ZEND_FETCH_STATIC_PROP_RW_SPEC_CV_UNUSED_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_FETCH_STATIC_PROP_IS_SPEC_CONST_CONST_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_FETCH_STATIC_PROP_IS_SPEC_CONST_VAR_HANDLER,
+ ZEND_FETCH_STATIC_PROP_IS_SPEC_CONST_UNUSED_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_FETCH_STATIC_PROP_IS_SPEC_TMPVAR_CONST_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_FETCH_STATIC_PROP_IS_SPEC_TMPVAR_VAR_HANDLER,
+ ZEND_FETCH_STATIC_PROP_IS_SPEC_TMPVAR_UNUSED_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_FETCH_STATIC_PROP_IS_SPEC_TMPVAR_CONST_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_FETCH_STATIC_PROP_IS_SPEC_TMPVAR_VAR_HANDLER,
+ ZEND_FETCH_STATIC_PROP_IS_SPEC_TMPVAR_UNUSED_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_FETCH_STATIC_PROP_IS_SPEC_CV_CONST_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_FETCH_STATIC_PROP_IS_SPEC_CV_VAR_HANDLER,
+ ZEND_FETCH_STATIC_PROP_IS_SPEC_CV_UNUSED_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_FETCH_STATIC_PROP_FUNC_ARG_SPEC_CONST_CONST_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_FETCH_STATIC_PROP_FUNC_ARG_SPEC_CONST_VAR_HANDLER,
+ ZEND_FETCH_STATIC_PROP_FUNC_ARG_SPEC_CONST_UNUSED_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_FETCH_STATIC_PROP_FUNC_ARG_SPEC_TMPVAR_CONST_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_FETCH_STATIC_PROP_FUNC_ARG_SPEC_TMPVAR_VAR_HANDLER,
+ ZEND_FETCH_STATIC_PROP_FUNC_ARG_SPEC_TMPVAR_UNUSED_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_FETCH_STATIC_PROP_FUNC_ARG_SPEC_TMPVAR_CONST_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_FETCH_STATIC_PROP_FUNC_ARG_SPEC_TMPVAR_VAR_HANDLER,
+ ZEND_FETCH_STATIC_PROP_FUNC_ARG_SPEC_TMPVAR_UNUSED_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_FETCH_STATIC_PROP_FUNC_ARG_SPEC_CV_CONST_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_FETCH_STATIC_PROP_FUNC_ARG_SPEC_CV_VAR_HANDLER,
+ ZEND_FETCH_STATIC_PROP_FUNC_ARG_SPEC_CV_UNUSED_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_FETCH_STATIC_PROP_UNSET_SPEC_CONST_CONST_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_FETCH_STATIC_PROP_UNSET_SPEC_CONST_VAR_HANDLER,
+ ZEND_FETCH_STATIC_PROP_UNSET_SPEC_CONST_UNUSED_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_FETCH_STATIC_PROP_UNSET_SPEC_TMPVAR_CONST_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_FETCH_STATIC_PROP_UNSET_SPEC_TMPVAR_VAR_HANDLER,
+ ZEND_FETCH_STATIC_PROP_UNSET_SPEC_TMPVAR_UNUSED_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_FETCH_STATIC_PROP_UNSET_SPEC_TMPVAR_CONST_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_FETCH_STATIC_PROP_UNSET_SPEC_TMPVAR_VAR_HANDLER,
+ ZEND_FETCH_STATIC_PROP_UNSET_SPEC_TMPVAR_UNUSED_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_FETCH_STATIC_PROP_UNSET_SPEC_CV_CONST_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_FETCH_STATIC_PROP_UNSET_SPEC_CV_VAR_HANDLER,
+ ZEND_FETCH_STATIC_PROP_UNSET_SPEC_CV_UNUSED_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_UNSET_STATIC_PROP_SPEC_CONST_CONST_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_UNSET_STATIC_PROP_SPEC_CONST_VAR_HANDLER,
+ ZEND_UNSET_STATIC_PROP_SPEC_CONST_UNUSED_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_UNSET_STATIC_PROP_SPEC_TMPVAR_CONST_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_UNSET_STATIC_PROP_SPEC_TMPVAR_VAR_HANDLER,
+ ZEND_UNSET_STATIC_PROP_SPEC_TMPVAR_UNUSED_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_UNSET_STATIC_PROP_SPEC_TMPVAR_CONST_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_UNSET_STATIC_PROP_SPEC_TMPVAR_VAR_HANDLER,
+ ZEND_UNSET_STATIC_PROP_SPEC_TMPVAR_UNUSED_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_UNSET_STATIC_PROP_SPEC_CV_CONST_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_UNSET_STATIC_PROP_SPEC_CV_VAR_HANDLER,
+ ZEND_UNSET_STATIC_PROP_SPEC_CV_UNUSED_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ISSET_ISEMPTY_STATIC_PROP_SPEC_CONST_CONST_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ISSET_ISEMPTY_STATIC_PROP_SPEC_CONST_VAR_HANDLER,
+ ZEND_ISSET_ISEMPTY_STATIC_PROP_SPEC_CONST_UNUSED_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ISSET_ISEMPTY_STATIC_PROP_SPEC_TMPVAR_CONST_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ISSET_ISEMPTY_STATIC_PROP_SPEC_TMPVAR_VAR_HANDLER,
+ ZEND_ISSET_ISEMPTY_STATIC_PROP_SPEC_TMPVAR_UNUSED_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ISSET_ISEMPTY_STATIC_PROP_SPEC_TMPVAR_CONST_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ISSET_ISEMPTY_STATIC_PROP_SPEC_TMPVAR_VAR_HANDLER,
+ ZEND_ISSET_ISEMPTY_STATIC_PROP_SPEC_TMPVAR_UNUSED_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ISSET_ISEMPTY_STATIC_PROP_SPEC_CV_CONST_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ISSET_ISEMPTY_STATIC_PROP_SPEC_CV_VAR_HANDLER,
+ ZEND_ISSET_ISEMPTY_STATIC_PROP_SPEC_CV_UNUSED_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_FETCH_CLASS_CONSTANT_SPEC_CONST_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_FETCH_CLASS_CONSTANT_SPEC_VAR_CONST_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_FETCH_CLASS_CONSTANT_SPEC_UNUSED_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,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_BIND_LEXICAL_SPEC_TMP_CV_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,
+ 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,
+ 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_BIND_STATIC_SPEC_CV_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,
+ 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_FETCH_THIS_SPEC_UNUSED_UNUSED_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,
+ 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_ISSET_ISEMPTY_THIS_SPEC_UNUSED_UNUSED_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ADD_LONG_NO_OVERFLOW_SPEC_CONST_TMPVARCV_HANDLER,
+ ZEND_ADD_LONG_NO_OVERFLOW_SPEC_CONST_TMPVARCV_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ADD_LONG_NO_OVERFLOW_SPEC_CONST_TMPVARCV_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ADD_LONG_SPEC_CONST_TMPVARCV_HANDLER,
+ ZEND_ADD_LONG_SPEC_CONST_TMPVARCV_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ADD_LONG_SPEC_CONST_TMPVARCV_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ADD_DOUBLE_SPEC_CONST_TMPVARCV_HANDLER,
+ ZEND_ADD_DOUBLE_SPEC_CONST_TMPVARCV_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ADD_DOUBLE_SPEC_CONST_TMPVARCV_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_SUB_LONG_NO_OVERFLOW_SPEC_CONST_TMPVARCV_HANDLER,
+ ZEND_SUB_LONG_NO_OVERFLOW_SPEC_CONST_TMPVARCV_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_SUB_LONG_NO_OVERFLOW_SPEC_CONST_TMPVARCV_HANDLER,
+ ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST_HANDLER,
+ ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST_HANDLER,
+ ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST_HANDLER,
+ ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_SUB_LONG_SPEC_CONST_TMPVARCV_HANDLER,
+ ZEND_SUB_LONG_SPEC_CONST_TMPVARCV_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_SUB_LONG_SPEC_CONST_TMPVARCV_HANDLER,
+ ZEND_SUB_LONG_SPEC_TMPVARCV_CONST_HANDLER,
+ ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_SUB_LONG_SPEC_TMPVARCV_CONST_HANDLER,
+ ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_SUB_LONG_SPEC_TMPVARCV_CONST_HANDLER,
+ ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_SUB_DOUBLE_SPEC_CONST_TMPVARCV_HANDLER,
+ ZEND_SUB_DOUBLE_SPEC_CONST_TMPVARCV_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_SUB_DOUBLE_SPEC_CONST_TMPVARCV_HANDLER,
+ ZEND_SUB_DOUBLE_SPEC_TMPVARCV_CONST_HANDLER,
+ ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_SUB_DOUBLE_SPEC_TMPVARCV_CONST_HANDLER,
+ ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_SUB_DOUBLE_SPEC_TMPVARCV_CONST_HANDLER,
+ ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_MUL_LONG_NO_OVERFLOW_SPEC_CONST_TMPVARCV_HANDLER,
+ ZEND_MUL_LONG_NO_OVERFLOW_SPEC_CONST_TMPVARCV_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_MUL_LONG_NO_OVERFLOW_SPEC_CONST_TMPVARCV_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_MUL_LONG_SPEC_CONST_TMPVARCV_HANDLER,
+ ZEND_MUL_LONG_SPEC_CONST_TMPVARCV_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_MUL_LONG_SPEC_CONST_TMPVARCV_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_MUL_DOUBLE_SPEC_CONST_TMPVARCV_HANDLER,
+ ZEND_MUL_DOUBLE_SPEC_CONST_TMPVARCV_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_MUL_DOUBLE_SPEC_CONST_TMPVARCV_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_IS_EQUAL_LONG_SPEC_CONST_TMPVARCV_HANDLER,
+ ZEND_IS_EQUAL_LONG_SPEC_CONST_TMPVARCV_JMPZ_HANDLER,
+ ZEND_IS_EQUAL_LONG_SPEC_CONST_TMPVARCV_JMPNZ_HANDLER,
+ ZEND_IS_EQUAL_LONG_SPEC_CONST_TMPVARCV_HANDLER,
+ ZEND_IS_EQUAL_LONG_SPEC_CONST_TMPVARCV_JMPZ_HANDLER,
+ ZEND_IS_EQUAL_LONG_SPEC_CONST_TMPVARCV_JMPNZ_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_IS_EQUAL_LONG_SPEC_CONST_TMPVARCV_HANDLER,
+ ZEND_IS_EQUAL_LONG_SPEC_CONST_TMPVARCV_JMPZ_HANDLER,
+ ZEND_IS_EQUAL_LONG_SPEC_CONST_TMPVARCV_JMPNZ_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ_HANDLER,
+ ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ_HANDLER,
+ ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ_HANDLER,
+ ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ_HANDLER,
+ ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ_HANDLER,
+ ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ_HANDLER,
+ ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ_HANDLER,
+ ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ_HANDLER,
+ ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ_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,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ_HANDLER,
+ ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ_HANDLER,
+ ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ_HANDLER,
+ ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ_HANDLER,
+ ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_IS_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_HANDLER,
+ ZEND_IS_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_JMPZ_HANDLER,
+ ZEND_IS_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_JMPNZ_HANDLER,
+ ZEND_IS_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_HANDLER,
+ ZEND_IS_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_JMPZ_HANDLER,
+ ZEND_IS_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_JMPNZ_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_IS_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_HANDLER,
+ ZEND_IS_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_JMPZ_HANDLER,
+ ZEND_IS_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_JMPNZ_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ_HANDLER,
+ ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ_HANDLER,
+ ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ_HANDLER,
+ ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ_HANDLER,
+ ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ_HANDLER,
+ ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ_HANDLER,
+ ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ_HANDLER,
+ ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ_HANDLER,
+ ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ_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,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ_HANDLER,
+ ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ_HANDLER,
+ ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ_HANDLER,
+ ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ_HANDLER,
+ ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_IS_NOT_EQUAL_LONG_SPEC_CONST_TMPVARCV_HANDLER,
+ ZEND_IS_NOT_EQUAL_LONG_SPEC_CONST_TMPVARCV_JMPZ_HANDLER,
+ ZEND_IS_NOT_EQUAL_LONG_SPEC_CONST_TMPVARCV_JMPNZ_HANDLER,
+ ZEND_IS_NOT_EQUAL_LONG_SPEC_CONST_TMPVARCV_HANDLER,
+ ZEND_IS_NOT_EQUAL_LONG_SPEC_CONST_TMPVARCV_JMPZ_HANDLER,
+ ZEND_IS_NOT_EQUAL_LONG_SPEC_CONST_TMPVARCV_JMPNZ_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_IS_NOT_EQUAL_LONG_SPEC_CONST_TMPVARCV_HANDLER,
+ ZEND_IS_NOT_EQUAL_LONG_SPEC_CONST_TMPVARCV_JMPZ_HANDLER,
+ ZEND_IS_NOT_EQUAL_LONG_SPEC_CONST_TMPVARCV_JMPNZ_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ_HANDLER,
+ ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ_HANDLER,
+ ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ_HANDLER,
+ ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ_HANDLER,
+ ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ_HANDLER,
+ ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ_HANDLER,
+ ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ_HANDLER,
+ ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ_HANDLER,
+ ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ_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,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ_HANDLER,
+ ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ_HANDLER,
+ ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ_HANDLER,
+ ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ_HANDLER,
+ ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_HANDLER,
+ ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_JMPZ_HANDLER,
+ ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_JMPNZ_HANDLER,
+ ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_HANDLER,
+ ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_JMPZ_HANDLER,
+ ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_JMPNZ_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_HANDLER,
+ ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_JMPZ_HANDLER,
+ ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_JMPNZ_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ_HANDLER,
+ ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ_HANDLER,
+ ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ_HANDLER,
+ ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ_HANDLER,
+ ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ_HANDLER,
+ ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ_HANDLER,
+ ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ_HANDLER,
+ ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ_HANDLER,
+ ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ_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,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ_HANDLER,
+ ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ_HANDLER,
+ ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ_HANDLER,
+ ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ_HANDLER,
+ ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV_HANDLER,
+ ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV_JMPZ_HANDLER,
+ ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV_JMPNZ_HANDLER,
+ ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV_HANDLER,
+ ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV_JMPZ_HANDLER,
+ ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV_JMPNZ_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV_HANDLER,
+ ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV_JMPZ_HANDLER,
+ ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV_JMPNZ_HANDLER,
+ ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST_HANDLER,
+ ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST_JMPZ_HANDLER,
+ ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST_JMPNZ_HANDLER,
+ ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ_HANDLER,
+ ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ_HANDLER,
+ ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ_HANDLER,
+ ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ_HANDLER,
+ ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ_HANDLER,
+ ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST_HANDLER,
+ ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST_JMPZ_HANDLER,
+ ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST_JMPNZ_HANDLER,
+ ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ_HANDLER,
+ ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ_HANDLER,
+ ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ_HANDLER,
+ ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ_HANDLER,
+ ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ_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,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST_HANDLER,
+ ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST_JMPZ_HANDLER,
+ ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST_JMPNZ_HANDLER,
+ ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ_HANDLER,
+ ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ_HANDLER,
+ ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ_HANDLER,
+ ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ_HANDLER,
+ ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV_HANDLER,
+ ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV_JMPZ_HANDLER,
+ ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV_JMPNZ_HANDLER,
+ ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV_HANDLER,
+ ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV_JMPZ_HANDLER,
+ ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV_JMPNZ_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV_HANDLER,
+ ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV_JMPZ_HANDLER,
+ ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV_JMPNZ_HANDLER,
+ ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST_HANDLER,
+ ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ_HANDLER,
+ ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ_HANDLER,
+ ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ_HANDLER,
+ ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ_HANDLER,
+ ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ_HANDLER,
+ ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ_HANDLER,
+ ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ_HANDLER,
+ ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST_HANDLER,
+ ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ_HANDLER,
+ ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ_HANDLER,
+ ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ_HANDLER,
+ ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ_HANDLER,
+ ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ_HANDLER,
+ ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ_HANDLER,
+ ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ_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,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST_HANDLER,
+ ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ_HANDLER,
+ ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ_HANDLER,
+ ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ_HANDLER,
+ ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ_HANDLER,
+ ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ_HANDLER,
+ ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ_HANDLER,
+ ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV_HANDLER,
+ ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV_JMPZ_HANDLER,
+ ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV_JMPNZ_HANDLER,
+ ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV_HANDLER,
+ ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV_JMPZ_HANDLER,
+ ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV_JMPNZ_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV_HANDLER,
+ ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV_JMPZ_HANDLER,
+ ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV_JMPNZ_HANDLER,
+ ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST_HANDLER,
+ ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ_HANDLER,
+ ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ_HANDLER,
+ ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ_HANDLER,
+ ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ_HANDLER,
+ ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ_HANDLER,
+ ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ_HANDLER,
+ ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ_HANDLER,
+ ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST_HANDLER,
+ ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ_HANDLER,
+ ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ_HANDLER,
+ ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ_HANDLER,
+ ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ_HANDLER,
+ ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ_HANDLER,
+ ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ_HANDLER,
+ ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ_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,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST_HANDLER,
+ ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ_HANDLER,
+ ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ_HANDLER,
+ ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ_HANDLER,
+ ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ_HANDLER,
+ ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ_HANDLER,
+ ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ_HANDLER,
+ ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_HANDLER,
+ ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_JMPZ_HANDLER,
+ ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_JMPNZ_HANDLER,
+ ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_HANDLER,
+ ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_JMPZ_HANDLER,
+ ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_JMPNZ_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_HANDLER,
+ ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_JMPZ_HANDLER,
+ ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_JMPNZ_HANDLER,
+ ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_HANDLER,
+ ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ_HANDLER,
+ ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ_HANDLER,
+ ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ_HANDLER,
+ ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ_HANDLER,
+ ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ_HANDLER,
+ ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ_HANDLER,
+ ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ_HANDLER,
+ ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_HANDLER,
+ ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ_HANDLER,
+ ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ_HANDLER,
+ ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ_HANDLER,
+ ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ_HANDLER,
+ ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ_HANDLER,
+ ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ_HANDLER,
+ ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ_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,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_HANDLER,
+ ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ_HANDLER,
+ ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ_HANDLER,
+ ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ_HANDLER,
+ ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ_HANDLER,
+ ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ_HANDLER,
+ ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_HANDLER,
+ ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ_HANDLER,
+ ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_PRE_INC_LONG_NO_OVERFLOW_SPEC_TMPVARCV_RETVAL_UNUSED_HANDLER,
+ ZEND_PRE_INC_LONG_NO_OVERFLOW_SPEC_TMPVARCV_RETVAL_USED_HANDLER,
+ ZEND_PRE_INC_LONG_NO_OVERFLOW_SPEC_TMPVARCV_RETVAL_UNUSED_HANDLER,
+ ZEND_PRE_INC_LONG_NO_OVERFLOW_SPEC_TMPVARCV_RETVAL_USED_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_PRE_INC_LONG_NO_OVERFLOW_SPEC_TMPVARCV_RETVAL_UNUSED_HANDLER,
+ ZEND_PRE_INC_LONG_NO_OVERFLOW_SPEC_TMPVARCV_RETVAL_USED_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_PRE_INC_LONG_SPEC_TMPVARCV_RETVAL_UNUSED_HANDLER,
+ ZEND_PRE_INC_LONG_SPEC_TMPVARCV_RETVAL_USED_HANDLER,
+ ZEND_PRE_INC_LONG_SPEC_TMPVARCV_RETVAL_UNUSED_HANDLER,
+ ZEND_PRE_INC_LONG_SPEC_TMPVARCV_RETVAL_USED_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_PRE_INC_LONG_SPEC_TMPVARCV_RETVAL_UNUSED_HANDLER,
+ ZEND_PRE_INC_LONG_SPEC_TMPVARCV_RETVAL_USED_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_PRE_INC_LONG_OR_DOUBLE_SPEC_TMPVARCV_RETVAL_UNUSED_HANDLER,
+ ZEND_PRE_INC_LONG_OR_DOUBLE_SPEC_TMPVARCV_RETVAL_USED_HANDLER,
+ ZEND_PRE_INC_LONG_OR_DOUBLE_SPEC_TMPVARCV_RETVAL_UNUSED_HANDLER,
+ ZEND_PRE_INC_LONG_OR_DOUBLE_SPEC_TMPVARCV_RETVAL_USED_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_PRE_INC_LONG_OR_DOUBLE_SPEC_TMPVARCV_RETVAL_UNUSED_HANDLER,
+ ZEND_PRE_INC_LONG_OR_DOUBLE_SPEC_TMPVARCV_RETVAL_USED_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_PRE_DEC_LONG_NO_OVERFLOW_SPEC_TMPVARCV_RETVAL_UNUSED_HANDLER,
+ ZEND_PRE_DEC_LONG_NO_OVERFLOW_SPEC_TMPVARCV_RETVAL_USED_HANDLER,
+ ZEND_PRE_DEC_LONG_NO_OVERFLOW_SPEC_TMPVARCV_RETVAL_UNUSED_HANDLER,
+ ZEND_PRE_DEC_LONG_NO_OVERFLOW_SPEC_TMPVARCV_RETVAL_USED_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_PRE_DEC_LONG_NO_OVERFLOW_SPEC_TMPVARCV_RETVAL_UNUSED_HANDLER,
+ ZEND_PRE_DEC_LONG_NO_OVERFLOW_SPEC_TMPVARCV_RETVAL_USED_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_PRE_DEC_LONG_SPEC_TMPVARCV_RETVAL_UNUSED_HANDLER,
+ ZEND_PRE_DEC_LONG_SPEC_TMPVARCV_RETVAL_USED_HANDLER,
+ ZEND_PRE_DEC_LONG_SPEC_TMPVARCV_RETVAL_UNUSED_HANDLER,
+ ZEND_PRE_DEC_LONG_SPEC_TMPVARCV_RETVAL_USED_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_PRE_DEC_LONG_SPEC_TMPVARCV_RETVAL_UNUSED_HANDLER,
+ ZEND_PRE_DEC_LONG_SPEC_TMPVARCV_RETVAL_USED_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_PRE_DEC_LONG_OR_DOUBLE_SPEC_TMPVARCV_RETVAL_UNUSED_HANDLER,
+ ZEND_PRE_DEC_LONG_OR_DOUBLE_SPEC_TMPVARCV_RETVAL_USED_HANDLER,
+ ZEND_PRE_DEC_LONG_OR_DOUBLE_SPEC_TMPVARCV_RETVAL_UNUSED_HANDLER,
+ ZEND_PRE_DEC_LONG_OR_DOUBLE_SPEC_TMPVARCV_RETVAL_USED_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_PRE_DEC_LONG_OR_DOUBLE_SPEC_TMPVARCV_RETVAL_UNUSED_HANDLER,
+ ZEND_PRE_DEC_LONG_OR_DOUBLE_SPEC_TMPVARCV_RETVAL_USED_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_POST_INC_LONG_NO_OVERFLOW_SPEC_TMPVARCV_HANDLER,
+ ZEND_POST_INC_LONG_NO_OVERFLOW_SPEC_TMPVARCV_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_POST_INC_LONG_NO_OVERFLOW_SPEC_TMPVARCV_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_POST_INC_LONG_SPEC_TMPVARCV_HANDLER,
+ ZEND_POST_INC_LONG_SPEC_TMPVARCV_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_POST_INC_LONG_SPEC_TMPVARCV_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_POST_INC_LONG_OR_DOUBLE_SPEC_TMPVARCV_HANDLER,
+ ZEND_POST_INC_LONG_OR_DOUBLE_SPEC_TMPVARCV_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_POST_INC_LONG_OR_DOUBLE_SPEC_TMPVARCV_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_POST_DEC_LONG_NO_OVERFLOW_SPEC_TMPVARCV_HANDLER,
+ ZEND_POST_DEC_LONG_NO_OVERFLOW_SPEC_TMPVARCV_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_POST_DEC_LONG_NO_OVERFLOW_SPEC_TMPVARCV_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_POST_DEC_LONG_SPEC_TMPVARCV_HANDLER,
+ ZEND_POST_DEC_LONG_SPEC_TMPVARCV_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_POST_DEC_LONG_SPEC_TMPVARCV_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_POST_DEC_LONG_OR_DOUBLE_SPEC_TMPVARCV_HANDLER,
+ ZEND_POST_DEC_LONG_OR_DOUBLE_SPEC_TMPVARCV_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_POST_DEC_LONG_OR_DOUBLE_SPEC_TMPVARCV_HANDLER,
+ ZEND_QM_ASSIGN_DOUBLE_SPEC_CONST_HANDLER,
+ ZEND_QM_ASSIGN_DOUBLE_SPEC_TMPVARCV_HANDLER,
+ ZEND_QM_ASSIGN_DOUBLE_SPEC_TMPVARCV_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_QM_ASSIGN_DOUBLE_SPEC_TMPVARCV_HANDLER,
+ ZEND_QM_ASSIGN_NOREF_SPEC_CONST_HANDLER,
+ ZEND_QM_ASSIGN_NOREF_SPEC_TMPVARCV_HANDLER,
+ ZEND_QM_ASSIGN_NOREF_SPEC_TMPVARCV_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_QM_ASSIGN_NOREF_SPEC_TMPVARCV_HANDLER,
+ ZEND_FETCH_DIM_R_INDEX_SPEC_CONST_CONST_HANDLER,
+ ZEND_FETCH_DIM_R_INDEX_SPEC_CONST_TMPVAR_HANDLER,
+ ZEND_FETCH_DIM_R_INDEX_SPEC_CONST_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_FETCH_DIM_R_INDEX_SPEC_CONST_CV_HANDLER,
+ ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_CONST_HANDLER,
+ ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_TMPVAR_HANDLER,
+ ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_CV_HANDLER,
+ ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_CONST_HANDLER,
+ ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_TMPVAR_HANDLER,
+ ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_CV_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_FETCH_DIM_R_INDEX_SPEC_CV_CONST_HANDLER,
+ ZEND_FETCH_DIM_R_INDEX_SPEC_CV_TMPVAR_HANDLER,
+ ZEND_FETCH_DIM_R_INDEX_SPEC_CV_TMPVAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_FETCH_DIM_R_INDEX_SPEC_CV_CV_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_SEND_VAR_SIMPLE_SPEC_VAR_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_SEND_VAR_SIMPLE_SPEC_CV_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_SEND_VAR_EX_SIMPLE_SPEC_VAR_HANDLER,
+ ZEND_SEND_VAR_EX_SIMPLE_SPEC_VAR_QUICK_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_NULL_HANDLER,
+ ZEND_SEND_VAR_EX_SIMPLE_SPEC_CV_HANDLER,
+ ZEND_SEND_VAR_EX_SIMPLE_SPEC_CV_QUICK_HANDLER,
+ ZEND_NULL_HANDLER
+ };
+ static const uint32_t specs[] = {
+ 0,
+ 1 | SPEC_RULE_OP1 | SPEC_RULE_OP2,
+ 26 | SPEC_RULE_OP1 | SPEC_RULE_OP2,
+ 51 | SPEC_RULE_OP1 | SPEC_RULE_OP2,
+ 76 | SPEC_RULE_OP1 | SPEC_RULE_OP2,
+ 101 | SPEC_RULE_OP1 | SPEC_RULE_OP2,
+ 126 | SPEC_RULE_OP1 | SPEC_RULE_OP2,
+ 151 | SPEC_RULE_OP1 | SPEC_RULE_OP2,
+ 176 | SPEC_RULE_OP1 | SPEC_RULE_OP2,
+ 201 | SPEC_RULE_OP1 | SPEC_RULE_OP2,
+ 226 | SPEC_RULE_OP1 | SPEC_RULE_OP2,
+ 251 | SPEC_RULE_OP1 | SPEC_RULE_OP2,
+ 276 | SPEC_RULE_OP1,
+ 281 | SPEC_RULE_OP1,
+ 286 | SPEC_RULE_OP1 | SPEC_RULE_OP2,
+ 311 | SPEC_RULE_OP1 | SPEC_RULE_OP2,
+ 336 | SPEC_RULE_OP1 | SPEC_RULE_OP2,
+ 361 | SPEC_RULE_OP1 | SPEC_RULE_OP2,
+ 386 | SPEC_RULE_OP1 | SPEC_RULE_OP2,
+ 411 | SPEC_RULE_OP1 | SPEC_RULE_OP2,
+ 436 | SPEC_RULE_OP1 | SPEC_RULE_OP2,
+ 461 | SPEC_RULE_OP1,
+ 466 | SPEC_RULE_OP1,
+ 471 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_DIM_OBJ,
+ 546 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_DIM_OBJ,
+ 621 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_DIM_OBJ,
+ 696 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_DIM_OBJ,
+ 771 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_DIM_OBJ,
+ 846 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_DIM_OBJ,
+ 921 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_DIM_OBJ,
+ 996 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_DIM_OBJ,
+ 1071 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_DIM_OBJ,
+ 1146 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_DIM_OBJ,
+ 1221 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_DIM_OBJ,
+ 1296 | SPEC_RULE_OP1 | SPEC_RULE_RETVAL,
+ 1306 | SPEC_RULE_OP1 | SPEC_RULE_RETVAL,
+ 1316 | SPEC_RULE_OP1,
+ 1321 | SPEC_RULE_OP1,
+ 1326 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_RETVAL,
+ 1376 | SPEC_RULE_OP1 | SPEC_RULE_OP2,
+ 1401 | SPEC_RULE_OP1,
+ 1406,
+ 1407,
+ 1408 | SPEC_RULE_OP1,
+ 1413 | SPEC_RULE_OP1,
+ 1418 | SPEC_RULE_OP1,
+ 1423 | SPEC_RULE_OP1,
+ 1428 | SPEC_RULE_OP1,
+ 1433 | SPEC_RULE_OP1 | SPEC_RULE_OP2,
+ 4546,
+ 1458 | SPEC_RULE_OP1 | SPEC_RULE_QUICK_ARG,
+ 4546,
+ 1468 | SPEC_RULE_OP1,
+ 1473 | SPEC_RULE_OP1 | SPEC_RULE_OP2,
+ 1498 | SPEC_RULE_OP1 | SPEC_RULE_OP2,
+ 1523 | SPEC_RULE_OP1 | SPEC_RULE_OP2,
+ 1548 | SPEC_RULE_OP1 | SPEC_RULE_OP2,
+ 1573,
+ 1574 | SPEC_RULE_OP1,
+ 1579 | SPEC_RULE_OP2,
+ 1584 | SPEC_RULE_RETVAL,
+ 1586 | SPEC_RULE_OP2,
+ 1591 | SPEC_RULE_OP1,
+ 1596,
+ 1597 | SPEC_RULE_OP2,
+ 1602 | SPEC_RULE_OP1,
+ 1607 | SPEC_RULE_OP1 | SPEC_RULE_QUICK_ARG,
+ 1617 | SPEC_RULE_OP1,
+ 1622 | SPEC_RULE_OP1,
+ 1627 | SPEC_RULE_OP2,
+ 1632 | SPEC_RULE_OP1,
+ 1637 | SPEC_RULE_OP1 | SPEC_RULE_OP2,
+ 1662 | SPEC_RULE_OP1 | SPEC_RULE_OP2,
+ 1687 | SPEC_RULE_OP1,
+ 1692 | SPEC_RULE_OP1 | SPEC_RULE_OP2,
+ 1717 | SPEC_RULE_OP1 | SPEC_RULE_OP2,
+ 1742 | SPEC_RULE_OP1 | SPEC_RULE_OP2,
+ 1767 | SPEC_RULE_OP1,
+ 1772 | SPEC_RULE_OP1,
+ 1777 | SPEC_RULE_OP1,
+ 1782 | SPEC_RULE_OP1 | SPEC_RULE_OP2,
+ 1807 | SPEC_RULE_OP1 | SPEC_RULE_OP2,
+ 1832 | SPEC_RULE_OP1 | SPEC_RULE_OP2,
+ 1857 | SPEC_RULE_OP1 | SPEC_RULE_OP2,
+ 1882 | SPEC_RULE_OP1 | SPEC_RULE_OP2,
+ 1907 | SPEC_RULE_OP1 | SPEC_RULE_OP2,
+ 1932 | SPEC_RULE_OP1 | SPEC_RULE_OP2,
+ 1957 | SPEC_RULE_OP1 | SPEC_RULE_OP2,
+ 1982 | SPEC_RULE_OP1 | SPEC_RULE_OP2,
+ 2007 | SPEC_RULE_OP1 | SPEC_RULE_OP2,
+ 2032 | SPEC_RULE_OP1 | SPEC_RULE_OP2,
+ 2057 | SPEC_RULE_OP1 | SPEC_RULE_OP2,
+ 2082 | SPEC_RULE_OP1 | SPEC_RULE_OP2,
+ 2107 | SPEC_RULE_OP1 | SPEC_RULE_OP2,
+ 2132 | SPEC_RULE_OP1 | SPEC_RULE_OP2,
+ 2157 | SPEC_RULE_OP1 | SPEC_RULE_OP2,
+ 2182 | SPEC_RULE_OP1 | SPEC_RULE_OP2,
+ 2207 | SPEC_RULE_OP1 | SPEC_RULE_OP2,
+ 2232 | SPEC_RULE_OP1 | SPEC_RULE_OP2,
+ 2257 | SPEC_RULE_OP1 | SPEC_RULE_OP2,
+ 4546,
+ 2282,
+ 2283,
+ 2284,
+ 2285,
+ 2286,
+ 2287 | SPEC_RULE_OP1,
+ 2292 | SPEC_RULE_OP1 | SPEC_RULE_OP2,
+ 2317 | SPEC_RULE_OP1,
+ 2322 | SPEC_RULE_OP2,
+ 2327 | SPEC_RULE_OP1,
+ 2332 | SPEC_RULE_OP1,
+ 2337 | SPEC_RULE_OP1 | SPEC_RULE_OP2,
+ 2362 | SPEC_RULE_OP1 | SPEC_RULE_OP2,
+ 2387 | SPEC_RULE_OP1 | SPEC_RULE_OP2,
+ 2412 | SPEC_RULE_OP1 | SPEC_RULE_OP2,
+ 2437 | SPEC_RULE_OP1 | SPEC_RULE_QUICK_ARG,
+ 2447 | SPEC_RULE_OP1,
+ 2452 | SPEC_RULE_OP1 | SPEC_RULE_OP2,
+ 2477,
+ 2478 | SPEC_RULE_OP1,
+ 2483 | SPEC_RULE_OP1,
+ 2488 | SPEC_RULE_OP1,
+ 2493 | SPEC_RULE_OP1,
+ 2498 | SPEC_RULE_OP1 | SPEC_RULE_OP2,
+ 2523 | SPEC_RULE_OP1,
+ 2528 | SPEC_RULE_OP1,
+ 2533 | SPEC_RULE_OP1,
+ 2538 | SPEC_RULE_OP2,
+ 2543 | SPEC_RULE_RETVAL,
+ 2545 | SPEC_RULE_RETVAL,
+ 2547 | SPEC_RULE_RETVAL,
+ 2549 | SPEC_RULE_OP1 | SPEC_RULE_OP2,
+ 2574 | SPEC_RULE_OP1 | SPEC_RULE_OP2,
+ 2599 | SPEC_RULE_OP1 | SPEC_RULE_OP2,
+ 2624 | SPEC_RULE_OP1 | SPEC_RULE_OP2,
+ 2649 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_OP_DATA,
+ 2774,
+ 2775 | SPEC_RULE_OP1 | SPEC_RULE_OP2,
+ 2800,
+ 2801 | SPEC_RULE_OP2,
+ 2806,
+ 2807 | SPEC_RULE_OP1,
+ 2812 | SPEC_RULE_OP1 | SPEC_RULE_OP2,
+ 2837 | SPEC_RULE_OP2,
+ 2842 | SPEC_RULE_OP2,
+ 2847,
+ 2848 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_OP_DATA,
+ 2973 | SPEC_RULE_OP1 | SPEC_RULE_OP2,
+ 2998,
+ 2999,
+ 3000,
+ 3001 | SPEC_RULE_OP1,
+ 3006 | SPEC_RULE_OP1 | SPEC_RULE_OP2,
+ 3031,
+ 3032,
+ 3033 | SPEC_RULE_OP1 | SPEC_RULE_OP2,
+ 3058,
+ 3059,
+ 3060,
+ 3061 | SPEC_RULE_OP1 | SPEC_RULE_OP2,
+ 3086 | SPEC_RULE_OP1,
+ 3091,
+ 3092,
+ 3093,
+ 3094,
+ 3095 | SPEC_RULE_OP1 | SPEC_RULE_OP2,
+ 3120 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_DIM_OBJ,
+ 3195 | SPEC_RULE_OP1 | SPEC_RULE_OP2,
+ 3220 | SPEC_RULE_OP1,
+ 3225 | SPEC_RULE_OP1 | SPEC_RULE_OP2,
+ 3250,
+ 3251 | SPEC_RULE_OP2,
+ 3256 | SPEC_RULE_OP1 | SPEC_RULE_OP2,
+ 3281 | SPEC_RULE_OP1 | SPEC_RULE_OP2,
+ 3306 | SPEC_RULE_OP1 | SPEC_RULE_OP2,
+ 3331 | SPEC_RULE_OP1 | SPEC_RULE_OP2,
+ 3356 | SPEC_RULE_OP1 | SPEC_RULE_OP2,
+ 3381 | SPEC_RULE_OP1 | SPEC_RULE_OP2,
+ 3406 | SPEC_RULE_OP1 | SPEC_RULE_OP2,
+ 3431 | SPEC_RULE_OP1 | SPEC_RULE_OP2,
+ 3456 | SPEC_RULE_OP1 | SPEC_RULE_OP2,
+ 3481 | SPEC_RULE_OP1 | SPEC_RULE_OP2,
+ 3506 | SPEC_RULE_OP1 | SPEC_RULE_OP2,
+ 3531 | SPEC_RULE_OP1 | SPEC_RULE_OP2,
+ 4546,
+ 3556 | SPEC_RULE_OP1 | SPEC_RULE_OP2,
+ 4546
+ };
+ zend_opcode_handlers = labels;
+ zend_handlers_count = sizeof(labels) / sizeof(void*);
+ zend_spec_handlers = specs;
+}
+
+static HashTable *zend_handlers_table = NULL;
+
+static void init_opcode_serialiser(void)
+{
+ int i;
+ zval tmp;
+
+ zend_handlers_table = malloc(sizeof(HashTable));
+ zend_hash_init_ex(zend_handlers_table, zend_handlers_count, NULL, NULL, 1, 0);
+ zend_hash_real_init(zend_handlers_table, 0);
+ Z_TYPE_INFO(tmp) = IS_LONG;
+ for (i = 0; i < zend_handlers_count; i++) {
+ Z_LVAL(tmp) = i;
+ zend_hash_index_add(zend_handlers_table, (zend_long)(zend_uintptr_t)zend_opcode_handlers[i], &tmp);
+ }
}
+
+ZEND_API void zend_serialize_opcode_handler(zend_op *op)
+{
+ zval *zv;
+
+ if (!zend_handlers_table) {
+ init_opcode_serialiser();
+ }
+ zv = zend_hash_index_find(zend_handlers_table, (zend_long)(zend_uintptr_t)op->handler);
+ ZEND_ASSERT(zv != NULL);
+ op->handler = (const void *)(zend_uintptr_t)Z_LVAL_P(zv);
+}
+
+ZEND_API void zend_deserialize_opcode_handler(zend_op *op)
+{
+ op->handler = zend_opcode_handlers[(zend_uintptr_t)op->handler];
+}
+
+static const void *zend_vm_get_opcode_handler_ex(uint32_t spec, const zend_op* op)
+{
+ static const int zend_vm_decode[] = {
+ _UNUSED_CODE, /* 0 */
+ _CONST_CODE, /* 1 = IS_CONST */
+ _TMP_CODE, /* 2 = IS_TMP_VAR */
+ _UNUSED_CODE, /* 3 */
+ _VAR_CODE, /* 4 = IS_VAR */
+ _UNUSED_CODE, /* 5 */
+ _UNUSED_CODE, /* 6 */
+ _UNUSED_CODE, /* 7 */
+ _UNUSED_CODE, /* 8 = IS_UNUSED */
+ _UNUSED_CODE, /* 9 */
+ _UNUSED_CODE, /* 10 */
+ _UNUSED_CODE, /* 11 */
+ _UNUSED_CODE, /* 12 */
+ _UNUSED_CODE, /* 13 */
+ _UNUSED_CODE, /* 14 */
+ _UNUSED_CODE, /* 15 */
+ _CV_CODE /* 16 = IS_CV */
+ };
+ uint32_t offset = 0;
+ if (spec & SPEC_RULE_OP1) offset = offset * 5 + zend_vm_decode[op->op1_type];
+ if (spec & SPEC_RULE_OP2) offset = offset * 5 + zend_vm_decode[op->op2_type];
+ if (spec & SPEC_RULE_OP_DATA) offset = offset * 5 + zend_vm_decode[(op + 1)->op1_type];
+ if (spec & SPEC_RULE_RETVAL) offset = offset * 2 + (op->result_type != IS_UNUSED);
+ if (spec & SPEC_RULE_QUICK_ARG) offset = offset * 2 + (op->op2.num < MAX_ARG_FLAG_NUM);
+ if (spec & SPEC_RULE_SMART_BRANCH) {
+ offset = offset * 3;
+ if ((op+1)->opcode == ZEND_JMPZ) {
+ offset += 1;
+ } else if ((op+1)->opcode == ZEND_JMPNZ) {
+ offset += 2;
+ }
+ }
+ if (spec & SPEC_RULE_DIM_OBJ) {
+ offset = offset * 3;
+ if (op->extended_value == ZEND_ASSIGN_DIM) {
+ offset += 1;
+ } else if (op->extended_value == ZEND_ASSIGN_OBJ) {
+ offset += 2;
+ }
+ }
+ return zend_opcode_handlers[(spec & SPEC_START_MASK) + offset];
+}
+
static const void *zend_vm_get_opcode_handler(zend_uchar opcode, const zend_op* op)
{
- static const int zend_vm_decode[] = {
- _UNUSED_CODE, /* 0 */
- _CONST_CODE, /* 1 = IS_CONST */
- _TMP_CODE, /* 2 = IS_TMP_VAR */
- _UNUSED_CODE, /* 3 */
- _VAR_CODE, /* 4 = IS_VAR */
- _UNUSED_CODE, /* 5 */
- _UNUSED_CODE, /* 6 */
- _UNUSED_CODE, /* 7 */
- _UNUSED_CODE, /* 8 = IS_UNUSED */
- _UNUSED_CODE, /* 9 */
- _UNUSED_CODE, /* 10 */
- _UNUSED_CODE, /* 11 */
- _UNUSED_CODE, /* 12 */
- _UNUSED_CODE, /* 13 */
- _UNUSED_CODE, /* 14 */
- _UNUSED_CODE, /* 15 */
- _CV_CODE /* 16 = IS_CV */
- };
- return zend_opcode_handlers[opcode * 25 + zend_vm_decode[op->op1_type] * 5 + zend_vm_decode[op->op2_type]];
+ return zend_vm_get_opcode_handler_ex(zend_spec_handlers[opcode], op);
}
ZEND_API void zend_vm_set_opcode_handler(zend_op* op)
@@ -49997,6 +61884,211 @@ ZEND_API void zend_vm_set_opcode_handler(zend_op* op)
op->handler = zend_vm_get_opcode_handler(zend_user_opcodes[op->opcode], op);
}
+ZEND_API void zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t op1_info, uint32_t op2_info, uint32_t res_info)
+{
+ zend_uchar opcode = zend_user_opcodes[op->opcode];
+ uint32_t spec = zend_spec_handlers[opcode];
+ switch (opcode) {
+ case ZEND_ADD:
+ if ((res_info == MAY_BE_LONG && op1_info == MAY_BE_LONG && op2_info == MAY_BE_LONG)) {
+ if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
+ break;
+ }
+ spec = 3581 | SPEC_RULE_OP1 | SPEC_RULE_OP2;
+ if (op->op1_type > op->op2_type) {
+ zend_swap_operands(op);
+ }
+ } else if ((op1_info == MAY_BE_LONG && op2_info == MAY_BE_LONG)) {
+ if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
+ break;
+ }
+ spec = 3606 | SPEC_RULE_OP1 | SPEC_RULE_OP2;
+ if (op->op1_type > op->op2_type) {
+ zend_swap_operands(op);
+ }
+ } else if ((op1_info == MAY_BE_DOUBLE && op2_info == MAY_BE_DOUBLE)) {
+ if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
+ break;
+ }
+ spec = 3631 | SPEC_RULE_OP1 | SPEC_RULE_OP2;
+ if (op->op1_type > op->op2_type) {
+ zend_swap_operands(op);
+ }
+ }
+ break;
+ case ZEND_SUB:
+ if ((res_info == MAY_BE_LONG && op1_info == MAY_BE_LONG && op2_info == MAY_BE_LONG)) {
+ if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
+ break;
+ }
+ spec = 3656 | SPEC_RULE_OP1 | SPEC_RULE_OP2;
+ } else if ((op1_info == MAY_BE_LONG && op2_info == MAY_BE_LONG)) {
+ if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
+ break;
+ }
+ spec = 3681 | SPEC_RULE_OP1 | SPEC_RULE_OP2;
+ } else if ((op1_info == MAY_BE_DOUBLE && op2_info == MAY_BE_DOUBLE)) {
+ if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
+ break;
+ }
+ spec = 3706 | SPEC_RULE_OP1 | SPEC_RULE_OP2;
+ }
+ break;
+ case ZEND_MUL:
+ if ((res_info == MAY_BE_LONG && op1_info == MAY_BE_LONG && op2_info == MAY_BE_LONG)) {
+ if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
+ break;
+ }
+ spec = 3731 | SPEC_RULE_OP1 | SPEC_RULE_OP2;
+ if (op->op1_type > op->op2_type) {
+ zend_swap_operands(op);
+ }
+ } else if ((op1_info == MAY_BE_LONG && op2_info == MAY_BE_LONG)) {
+ if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
+ break;
+ }
+ spec = 3756 | SPEC_RULE_OP1 | SPEC_RULE_OP2;
+ if (op->op1_type > op->op2_type) {
+ zend_swap_operands(op);
+ }
+ } else if ((op1_info == MAY_BE_DOUBLE && op2_info == MAY_BE_DOUBLE)) {
+ if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
+ break;
+ }
+ spec = 3781 | SPEC_RULE_OP1 | SPEC_RULE_OP2;
+ if (op->op1_type > op->op2_type) {
+ zend_swap_operands(op);
+ }
+ }
+ break;
+ case ZEND_IS_EQUAL:
+ if ((op1_info == MAY_BE_LONG && op2_info == MAY_BE_LONG)) {
+ if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
+ break;
+ }
+ spec = 3806 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH;
+ if (op->op1_type > op->op2_type) {
+ zend_swap_operands(op);
+ }
+ } else if ((op1_info == MAY_BE_DOUBLE && op2_info == MAY_BE_DOUBLE)) {
+ if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
+ break;
+ }
+ spec = 3881 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH;
+ if (op->op1_type > op->op2_type) {
+ zend_swap_operands(op);
+ }
+ }
+ break;
+ case ZEND_IS_NOT_EQUAL:
+ if ((op1_info == MAY_BE_LONG && op2_info == MAY_BE_LONG)) {
+ if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
+ break;
+ }
+ spec = 3956 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH;
+ if (op->op1_type > op->op2_type) {
+ zend_swap_operands(op);
+ }
+ } else if ((op1_info == MAY_BE_DOUBLE && op2_info == MAY_BE_DOUBLE)) {
+ if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
+ break;
+ }
+ spec = 4031 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH;
+ if (op->op1_type > op->op2_type) {
+ zend_swap_operands(op);
+ }
+ }
+ break;
+ case ZEND_IS_SMALLER:
+ if ((op1_info == MAY_BE_LONG && op2_info == MAY_BE_LONG)) {
+ if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
+ break;
+ }
+ spec = 4106 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH;
+ } else if ((op1_info == MAY_BE_DOUBLE && op2_info == MAY_BE_DOUBLE)) {
+ if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
+ break;
+ }
+ spec = 4181 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH;
+ }
+ break;
+ case ZEND_IS_SMALLER_OR_EQUAL:
+ if ((op1_info == MAY_BE_LONG && op2_info == MAY_BE_LONG)) {
+ if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
+ break;
+ }
+ spec = 4256 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH;
+ } else if ((op1_info == MAY_BE_DOUBLE && op2_info == MAY_BE_DOUBLE)) {
+ if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
+ break;
+ }
+ spec = 4331 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH;
+ }
+ break;
+ case ZEND_QM_ASSIGN:
+ if ((op1_info == MAY_BE_DOUBLE)) {
+ spec = 4496 | SPEC_RULE_OP1;
+ } else if ((!(op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-(MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG|MAY_BE_DOUBLE))))) {
+ spec = 4501 | SPEC_RULE_OP1;
+ }
+ break;
+ case ZEND_PRE_INC:
+ if ((res_info == MAY_BE_LONG && op1_info == MAY_BE_LONG)) {
+ spec = 4406 | SPEC_RULE_OP1 | SPEC_RULE_RETVAL;
+ } else if ((op1_info == MAY_BE_LONG)) {
+ spec = 4416 | SPEC_RULE_OP1 | SPEC_RULE_RETVAL;
+ } else if ((op1_info == (MAY_BE_LONG|MAY_BE_DOUBLE))) {
+ spec = 4426 | SPEC_RULE_OP1 | SPEC_RULE_RETVAL;
+ }
+ break;
+ case ZEND_PRE_DEC:
+ if ((res_info == MAY_BE_LONG && op1_info == MAY_BE_LONG)) {
+ spec = 4436 | SPEC_RULE_OP1 | SPEC_RULE_RETVAL;
+ } else if ((op1_info == MAY_BE_LONG)) {
+ spec = 4446 | SPEC_RULE_OP1 | SPEC_RULE_RETVAL;
+ } else if ((op1_info == (MAY_BE_LONG|MAY_BE_DOUBLE))) {
+ spec = 4456 | SPEC_RULE_OP1 | SPEC_RULE_RETVAL;
+ }
+ break;
+ case ZEND_POST_INC:
+ if ((res_info == MAY_BE_LONG && op1_info == MAY_BE_LONG)) {
+ spec = 4466 | SPEC_RULE_OP1;
+ } else if ((op1_info == MAY_BE_LONG)) {
+ spec = 4471 | SPEC_RULE_OP1;
+ } else if ((op1_info == (MAY_BE_LONG|MAY_BE_DOUBLE))) {
+ spec = 4476 | SPEC_RULE_OP1;
+ }
+ break;
+ case ZEND_POST_DEC:
+ if ((res_info == MAY_BE_LONG && op1_info == MAY_BE_LONG)) {
+ spec = 4481 | SPEC_RULE_OP1;
+ } else if ((op1_info == MAY_BE_LONG)) {
+ spec = 4486 | SPEC_RULE_OP1;
+ } else if ((op1_info == (MAY_BE_LONG|MAY_BE_DOUBLE))) {
+ spec = 4491 | SPEC_RULE_OP1;
+ }
+ break;
+ case ZEND_SEND_VAR_EX:
+ if ((op1_info & (MAY_BE_UNDEF|MAY_BE_REF)) == 0) {
+ spec = 4536 | SPEC_RULE_OP1 | SPEC_RULE_QUICK_ARG;
+ }
+ break;
+ case ZEND_FETCH_DIM_R:
+ if ((!(op2_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF)))) {
+ spec = 4506 | SPEC_RULE_OP1 | SPEC_RULE_OP2;
+ }
+ break;
+ case ZEND_SEND_VAR:
+ if ((op1_info & (MAY_BE_UNDEF|MAY_BE_REF)) == 0) {
+ spec = 4531 | SPEC_RULE_OP1;
+ }
+ break;
+ default:
+ break;
+ }
+ op->handler = zend_vm_get_opcode_handler_ex(spec, op);
+}
+
ZEND_API int zend_vm_call_opcode_handler(zend_execute_data* ex)
{
int ret;
diff --git a/Zend/zend_vm_execute.skl b/Zend/zend_vm_execute.skl
index 7e223385ff..ebc6474ae5 100644
--- a/Zend/zend_vm_execute.skl
+++ b/Zend/zend_vm_execute.skl
@@ -9,9 +9,10 @@ ZEND_API void {%EXECUTOR_NAME%}_ex(zend_execute_data *ex)
{%INTERNAL_LABELS%}
LOAD_OPLINE();
+ ZEND_VM_INTERRUPT_CHECK();
while (1) {
- {%ZEND_VM_CONTINUE_LABEL%}
+ {%ZEND_VM_CONTINUE_LABEL%}
{%ZEND_VM_DISPATCH%} {
{%INTERNAL_EXECUTOR%}
}
@@ -28,7 +29,7 @@ ZEND_API void zend_{%EXECUTOR_NAME%}(zend_op_array *op_array, zval *return_value
return;
}
- execute_data = zend_vm_stack_push_call_frame(ZEND_CALL_TOP_CODE,
+ execute_data = zend_vm_stack_push_call_frame(ZEND_CALL_TOP_CODE | ZEND_CALL_HAS_SYMBOL_TABLE,
(zend_function*)op_array, 0, zend_get_called_scope(EG(current_execute_data)), zend_get_this_object(EG(current_execute_data)));
if (EG(current_execute_data)) {
execute_data->symbol_table = zend_rebuild_symbol_table();
@@ -45,5 +46,40 @@ ZEND_API void zend_{%EXECUTOR_NAME%}(zend_op_array *op_array, zval *return_value
void {%INITIALIZER_NAME%}(void)
{
- {%EXTERNAL_LABELS%}
+ {%EXTERNAL_LABELS%}
}
+
+static HashTable *zend_handlers_table = NULL;
+
+static void init_opcode_serialiser(void)
+{
+ int i;
+ zval tmp;
+
+ zend_handlers_table = malloc(sizeof(HashTable));
+ zend_hash_init_ex(zend_handlers_table, zend_handlers_count, NULL, NULL, 1, 0);
+ zend_hash_real_init(zend_handlers_table, 0);
+ Z_TYPE_INFO(tmp) = IS_LONG;
+ for (i = 0; i < zend_handlers_count; i++) {
+ Z_LVAL(tmp) = i;
+ zend_hash_index_add(zend_handlers_table, (zend_long)(zend_uintptr_t)zend_opcode_handlers[i], &tmp);
+ }
+}
+
+ZEND_API void zend_serialize_opcode_handler(zend_op *op)
+{
+ zval *zv;
+
+ if (!zend_handlers_table) {
+ init_opcode_serialiser();
+ }
+ zv = zend_hash_index_find(zend_handlers_table, (zend_long)(zend_uintptr_t)op->handler);
+ ZEND_ASSERT(zv != NULL);
+ op->handler = (const void *)(zend_uintptr_t)Z_LVAL_P(zv);
+}
+
+ZEND_API void zend_deserialize_opcode_handler(zend_op *op)
+{
+ op->handler = zend_opcode_handlers[(zend_uintptr_t)op->handler];
+}
+
diff --git a/Zend/zend_vm_gen.php b/Zend/zend_vm_gen.php
index c633124904..8775716753 100644
--- a/Zend/zend_vm_gen.php
+++ b/Zend/zend_vm_gen.php
@@ -54,6 +54,82 @@ define("ZEND_VM_KIND_CALL", 1);
define("ZEND_VM_KIND_SWITCH", 2);
define("ZEND_VM_KIND_GOTO", 3);
+$vm_op_flags = array(
+ "ZEND_VM_OP_SPEC" => 1<<0,
+ "ZEND_VM_OP_CONST" => 1<<1,
+ "ZEND_VM_OP_TMPVAR" => 1<<2,
+ "ZEND_VM_OP_TMPVARCV" => 1<<3,
+ "ZEND_VM_OP_MASK" => 0xf0,
+ "ZEND_VM_OP_NUM" => 0x10,
+ "ZEND_VM_OP_JMP_ADDR" => 0x20,
+ "ZEND_VM_OP_TRY_CATCH" => 0x30,
+ "ZEND_VM_OP_LIVE_RANGE" => 0x40,
+ "ZEND_VM_OP_THIS" => 0x50,
+ "ZEND_VM_OP_NEXT" => 0x60,
+ "ZEND_VM_OP_CLASS_FETCH" => 0x70,
+ "ZEND_VM_OP_CONSTRUCTOR" => 0x80,
+
+ "ZEND_VM_EXT_VAR_FETCH" => 1<<16,
+ "ZEND_VM_EXT_ISSET" => 1<<17,
+ "ZEND_VM_EXT_ARG_NUM" => 1<<18,
+ "ZEND_VM_EXT_ARRAY_INIT" => 1<<19,
+ "ZEND_VM_EXT_REF" => 1<<20,
+ "ZEND_VM_EXT_MASK" => 0x0f000000,
+ "ZEND_VM_EXT_NUM" => 0x01000000,
+ // unused 0x2000000
+ "ZEND_VM_EXT_JMP_ADDR" => 0x03000000,
+ "ZEND_VM_EXT_DIM_OBJ" => 0x04000000,
+ "ZEND_VM_EXT_CLASS_FETCH" => 0x05000000,
+ "ZEND_VM_EXT_CONST_FETCH" => 0x06000000,
+ "ZEND_VM_EXT_TYPE" => 0x07000000,
+ "ZEND_VM_EXT_EVAL" => 0x08000000,
+ // unused 0x09000000,
+ // unused 0x0a000000,
+ "ZEND_VM_EXT_SRC" => 0x0b000000,
+ // unused 0x0c000000,
+ "ZEND_VM_NO_CONST_CONST" => 0x40000000,
+ "ZEND_VM_COMMUTATIVE" => 0x80000000,
+);
+
+foreach ($vm_op_flags as $name => $val) {
+ define($name, $val);
+}
+
+$vm_op_decode = array(
+ "ANY" => 0,
+ "CONST" => ZEND_VM_OP_SPEC | ZEND_VM_OP_CONST,
+ "TMP" => ZEND_VM_OP_SPEC,
+ "VAR" => ZEND_VM_OP_SPEC,
+ "UNUSED" => ZEND_VM_OP_SPEC,
+ "CV" => ZEND_VM_OP_SPEC,
+ "TMPVAR" => ZEND_VM_OP_SPEC | ZEND_VM_OP_TMPVAR,
+ "TMPVARCV" => ZEND_VM_OP_SPEC | ZEND_VM_OP_TMPVARCV,
+ "NUM" => ZEND_VM_OP_NUM,
+ "JMP_ADDR" => ZEND_VM_OP_JMP_ADDR,
+ "TRY_CATCH" => ZEND_VM_OP_TRY_CATCH,
+ "LIVE_RANGE" => ZEND_VM_OP_LIVE_RANGE,
+ "THIS" => ZEND_VM_OP_THIS,
+ "NEXT" => ZEND_VM_OP_NEXT,
+ "CLASS_FETCH" => ZEND_VM_OP_CLASS_FETCH,
+ "CONSTRUCTOR" => ZEND_VM_OP_CONSTRUCTOR,
+);
+
+$vm_ext_decode = array(
+ "NUM" => ZEND_VM_EXT_NUM,
+ "JMP_ADDR" => ZEND_VM_EXT_JMP_ADDR,
+ "DIM_OBJ" => ZEND_VM_EXT_DIM_OBJ,
+ "CLASS_FETCH" => ZEND_VM_EXT_CLASS_FETCH,
+ "CONST_FETCH" => ZEND_VM_EXT_CONST_FETCH,
+ "VAR_FETCH" => ZEND_VM_EXT_VAR_FETCH,
+ "ARRAY_INIT" => ZEND_VM_EXT_ARRAY_INIT,
+ "TYPE" => ZEND_VM_EXT_TYPE,
+ "EVAL" => ZEND_VM_EXT_EVAL,
+ "ISSET" => ZEND_VM_EXT_ISSET,
+ "ARG_NUM" => ZEND_VM_EXT_ARG_NUM,
+ "REF" => ZEND_VM_EXT_REF,
+ "SRC" => ZEND_VM_EXT_SRC,
+);
+
$vm_kind_name = array(
ZEND_VM_KIND_CALL => "ZEND_VM_KIND_CALL",
ZEND_VM_KIND_SWITCH => "ZEND_VM_KIND_SWITCH",
@@ -77,346 +153,447 @@ $op_types_ex = array(
"UNUSED",
"CV",
"TMPVAR",
+ "TMPVARCV",
);
$prefix = array(
- "ANY" => "",
- "TMP" => "_TMP",
- "VAR" => "_VAR",
- "CONST" => "_CONST",
- "UNUSED" => "_UNUSED",
- "CV" => "_CV",
- "TMPVAR" => "_TMPVAR",
+ "ANY" => "",
+ "TMP" => "_TMP",
+ "VAR" => "_VAR",
+ "CONST" => "_CONST",
+ "UNUSED" => "_UNUSED",
+ "CV" => "_CV",
+ "TMPVAR" => "_TMPVAR",
+ "TMPVARCV" => "_TMPVARCV",
);
$typecode = array(
- "ANY" => 0,
- "TMP" => 1,
- "VAR" => 2,
- "CONST" => 0,
- "UNUSED" => 3,
- "CV" => 4,
- "TMPVAR" => 0,
+ "ANY" => 0,
+ "TMP" => 1,
+ "VAR" => 2,
+ "CONST" => 0,
+ "UNUSED" => 3,
+ "CV" => 4,
+ "TMPVAR" => 0,
+ "TMPVARCV" => 0,
+);
+
+$commutative_order = array(
+ "ANY" => 0,
+ "TMP" => 1,
+ "VAR" => 2,
+ "CONST" => 0,
+ "UNUSED" => 0,
+ "CV" => 4,
+ "TMPVAR" => 2,
+ "TMPVARCV" => 4,
);
$op1_type = array(
- "ANY" => "opline->op1_type",
- "TMP" => "IS_TMP_VAR",
- "VAR" => "IS_VAR",
- "CONST" => "IS_CONST",
- "UNUSED" => "IS_UNUSED",
- "CV" => "IS_CV",
- "TMPVAR" => "(IS_TMP_VAR|IS_VAR)",
+ "ANY" => "opline->op1_type",
+ "TMP" => "IS_TMP_VAR",
+ "VAR" => "IS_VAR",
+ "CONST" => "IS_CONST",
+ "UNUSED" => "IS_UNUSED",
+ "CV" => "IS_CV",
+ "TMPVAR" => "(IS_TMP_VAR|IS_VAR)",
+ "TMPVARCV" => "(IS_TMP_VAR|IS_VAR|IS_CV)",
);
$op2_type = array(
- "ANY" => "opline->op2_type",
- "TMP" => "IS_TMP_VAR",
- "VAR" => "IS_VAR",
- "CONST" => "IS_CONST",
- "UNUSED" => "IS_UNUSED",
- "CV" => "IS_CV",
- "TMPVAR" => "(IS_TMP_VAR|IS_VAR)",
+ "ANY" => "opline->op2_type",
+ "TMP" => "IS_TMP_VAR",
+ "VAR" => "IS_VAR",
+ "CONST" => "IS_CONST",
+ "UNUSED" => "IS_UNUSED",
+ "CV" => "IS_CV",
+ "TMPVAR" => "(IS_TMP_VAR|IS_VAR)",
+ "TMPVARCV" => "(IS_TMP_VAR|IS_VAR|IS_CV)",
);
$op1_free = array(
- "ANY" => "(free_op1 != NULL)",
- "TMP" => "1",
- "VAR" => "(free_op1 != NULL)",
- "CONST" => "0",
- "UNUSED" => "0",
- "CV" => "0",
- "TMPVAR" => "???",
+ "ANY" => "(free_op1 != NULL)",
+ "TMP" => "1",
+ "VAR" => "(free_op1 != NULL)",
+ "CONST" => "0",
+ "UNUSED" => "0",
+ "CV" => "0",
+ "TMPVAR" => "???",
+ "TMPVARCV" => "???",
);
$op2_free = array(
- "ANY" => "(free_op2 != NULL)",
- "TMP" => "1",
- "VAR" => "(free_op2 != NULL)",
- "CONST" => "0",
- "UNUSED" => "0",
- "CV" => "0",
- "TMPVAR" => "???",
+ "ANY" => "(free_op2 != NULL)",
+ "TMP" => "1",
+ "VAR" => "(free_op2 != NULL)",
+ "CONST" => "0",
+ "UNUSED" => "0",
+ "CV" => "0",
+ "TMPVAR" => "???",
+ "TMPVARCV" => "???",
);
$op1_get_zval_ptr = array(
- "ANY" => "get_zval_ptr(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" => "NULL",
- "CV" => "_get_zval_ptr_cv_\\1(execute_data, opline->op1.var)",
- "TMPVAR" => "_get_zval_ptr_var(opline->op1.var, execute_data, &free_op1)",
+ "ANY" => "get_zval_ptr(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" => "NULL",
+ "CV" => "_get_zval_ptr_cv_\\1(execute_data, opline->op1.var)",
+ "TMPVAR" => "_get_zval_ptr_var(opline->op1.var, execute_data, &free_op1)",
+ "TMPVARCV" => "???",
);
$op2_get_zval_ptr = array(
- "ANY" => "get_zval_ptr(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" => "NULL",
- "CV" => "_get_zval_ptr_cv_\\1(execute_data, opline->op2.var)",
- "TMPVAR" => "_get_zval_ptr_var(opline->op2.var, execute_data, &free_op2)",
+ "ANY" => "get_zval_ptr(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" => "NULL",
+ "CV" => "_get_zval_ptr_cv_\\1(execute_data, opline->op2.var)",
+ "TMPVAR" => "_get_zval_ptr_var(opline->op2.var, execute_data, &free_op2)",
+ "TMPVARCV" => "???",
);
$op1_get_zval_ptr_ptr = array(
- "ANY" => "get_zval_ptr_ptr(opline->op1_type, opline->op1, execute_data, &free_op1, \\1)",
- "TMP" => "NULL",
- "VAR" => "_get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1)",
- "CONST" => "NULL",
- "UNUSED" => "NULL",
- "CV" => "_get_zval_ptr_cv_\\1(execute_data, opline->op1.var)",
- "TMPVAR" => "???",
+ "ANY" => "get_zval_ptr_ptr(opline->op1_type, opline->op1, execute_data, &free_op1, \\1)",
+ "TMP" => "NULL",
+ "VAR" => "_get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1)",
+ "CONST" => "NULL",
+ "UNUSED" => "NULL",
+ "CV" => "_get_zval_ptr_cv_\\1(execute_data, opline->op1.var)",
+ "TMPVAR" => "???",
+ "TMPVARCV" => "???",
);
$op2_get_zval_ptr_ptr = array(
- "ANY" => "get_zval_ptr_ptr(opline->op2_type, opline->op2, execute_data, &free_op2, \\1)",
- "TMP" => "NULL",
- "VAR" => "_get_zval_ptr_ptr_var(opline->op2.var, execute_data, &free_op2)",
- "CONST" => "NULL",
- "UNUSED" => "NULL",
- "CV" => "_get_zval_ptr_cv_\\1(execute_data, opline->op2.var)",
- "TMPVAR" => "???",
+ "ANY" => "get_zval_ptr_ptr(opline->op2_type, opline->op2, execute_data, &free_op2, \\1)",
+ "TMP" => "NULL",
+ "VAR" => "_get_zval_ptr_ptr_var(opline->op2.var, execute_data, &free_op2)",
+ "CONST" => "NULL",
+ "UNUSED" => "NULL",
+ "CV" => "_get_zval_ptr_cv_\\1(execute_data, opline->op2.var)",
+ "TMPVAR" => "???",
+ "TMPVARCV" => "???",
);
$op1_get_zval_ptr_deref = array(
- "ANY" => "get_zval_ptr_deref(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_deref(opline->op1.var, execute_data, &free_op1)",
- "CONST" => "EX_CONSTANT(opline->op1)",
- "UNUSED" => "NULL",
- "CV" => "_get_zval_ptr_cv_deref_\\1(execute_data, opline->op1.var)",
- "TMPVAR" => "???",
+ "ANY" => "get_zval_ptr_deref(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_deref(opline->op1.var, execute_data, &free_op1)",
+ "CONST" => "EX_CONSTANT(opline->op1)",
+ "UNUSED" => "NULL",
+ "CV" => "_get_zval_ptr_cv_deref_\\1(execute_data, opline->op1.var)",
+ "TMPVAR" => "???",
+ "TMPVARCV" => "???",
);
$op2_get_zval_ptr_deref = array(
- "ANY" => "get_zval_ptr_deref(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_deref(opline->op2.var, execute_data, &free_op2)",
- "CONST" => "EX_CONSTANT(opline->op2)",
- "UNUSED" => "NULL",
- "CV" => "_get_zval_ptr_cv_deref_\\1(execute_data, opline->op2.var)",
- "TMPVAR" => "???",
+ "ANY" => "get_zval_ptr_deref(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_deref(opline->op2.var, execute_data, &free_op2)",
+ "CONST" => "EX_CONSTANT(opline->op2)",
+ "UNUSED" => "NULL",
+ "CV" => "_get_zval_ptr_cv_deref_\\1(execute_data, opline->op2.var)",
+ "TMPVAR" => "???",
+ "TMPVARCV" => "???",
);
$op1_get_zval_ptr_undef = array(
- "ANY" => "get_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" => "NULL",
- "CV" => "_get_zval_ptr_cv_undef(execute_data, opline->op1.var)",
- "TMPVAR" => "_get_zval_ptr_var(opline->op1.var, execute_data, &free_op1)",
+ "ANY" => "get_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" => "NULL",
+ "CV" => "_get_zval_ptr_cv_undef(execute_data, opline->op1.var)",
+ "TMPVAR" => "_get_zval_ptr_var(opline->op1.var, execute_data, &free_op1)",
+ "TMPVARCV" => "EX_VAR(opline->op1.var)",
);
$op2_get_zval_ptr_undef = array(
- "ANY" => "get_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" => "NULL",
- "CV" => "_get_zval_ptr_cv_undef(execute_data, opline->op2.var)",
- "TMPVAR" => "_get_zval_ptr_var(opline->op2.var, execute_data, &free_op2)",
+ "ANY" => "get_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" => "NULL",
+ "CV" => "_get_zval_ptr_cv_undef(execute_data, opline->op2.var)",
+ "TMPVAR" => "_get_zval_ptr_var(opline->op2.var, execute_data, &free_op2)",
+ "TMPVARCV" => "EX_VAR(opline->op2.var)",
);
$op1_get_zval_ptr_ptr_undef = array(
- "ANY" => "get_zval_ptr_ptr_undef(opline->op1_type, opline->op1, execute_data, &free_op1, \\1)",
- "TMP" => "NULL",
- "VAR" => "_get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1)",
- "CONST" => "NULL",
- "UNUSED" => "NULL",
- "CV" => "_get_zval_ptr_cv_undef_\\1(execute_data, opline->op1.var)",
- "TMPVAR" => "???",
+ "ANY" => "get_zval_ptr_ptr_undef(opline->op1_type, opline->op1, execute_data, &free_op1, \\1)",
+ "TMP" => "NULL",
+ "VAR" => "_get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1)",
+ "CONST" => "NULL",
+ "UNUSED" => "NULL",
+ "CV" => "_get_zval_ptr_cv_undef_\\1(execute_data, opline->op1.var)",
+ "TMPVAR" => "???",
+ "TMPVARCV" => "EX_VAR(opline->op1.var)",
);
$op2_get_zval_ptr_ptr_undef = array(
- "ANY" => "get_zval_ptr_ptr_undef(opline->op2_type, opline->op2, execute_data, &free_op2, \\1)",
- "TMP" => "NULL",
- "VAR" => "_get_zval_ptr_ptr_var(opline->op2.var, execute_data, &free_op2)",
- "CONST" => "NULL",
- "UNUSED" => "NULL",
- "CV" => "_get_zval_ptr_cv_undef_\\1(execute_data, opline->op2.var)",
- "TMPVAR" => "???",
+ "ANY" => "get_zval_ptr_ptr_undef(opline->op2_type, opline->op2, execute_data, &free_op2, \\1)",
+ "TMP" => "NULL",
+ "VAR" => "_get_zval_ptr_ptr_var(opline->op2.var, execute_data, &free_op2)",
+ "CONST" => "NULL",
+ "UNUSED" => "NULL",
+ "CV" => "_get_zval_ptr_cv_undef_\\1(execute_data, opline->op2.var)",
+ "TMPVAR" => "???",
+ "TMPVARCV" => "EX_VAR(opline->op2.var)",
);
$op1_get_obj_zval_ptr = 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)",
- "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_\\1(execute_data, opline->op1.var)",
- "TMPVAR" => "_get_zval_ptr_var(opline->op1.var, execute_data, &free_op1)",
+ "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)",
+ "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_\\1(execute_data, opline->op1.var)",
+ "TMPVAR" => "_get_zval_ptr_var(opline->op1.var, execute_data, &free_op1)",
+ "TMPVARCV" => "???",
);
$op2_get_obj_zval_ptr = array(
- "ANY" => "get_obj_zval_ptr(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_\\1(execute_data, opline->op2.var)",
- "TMPVAR" => "_get_zval_ptr_var(opline->op2.var, execute_data, &free_op2)",
+ "ANY" => "get_obj_zval_ptr(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_\\1(execute_data, opline->op2.var)",
+ "TMPVAR" => "_get_zval_ptr_var(opline->op2.var, execute_data, &free_op2)",
+ "TMPVARCV" => "???",
);
$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)",
+ "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)",
+ "TMPVARCV" => "EX_VAR(opline->op1.var)",
);
$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)",
+ "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)",
+ "TMPVARCV" => "EX_VAR(opline->op2.var)",
);
$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)",
- "VAR" => "_get_zval_ptr_var_deref(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_deref_\\1(execute_data, opline->op1.var)",
- "TMPVAR" => "???",
+ "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)",
+ "VAR" => "_get_zval_ptr_var_deref(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_deref_\\1(execute_data, opline->op1.var)",
+ "TMPVAR" => "???",
+ "TMPVARCV" => "???",
);
$op2_get_obj_zval_ptr_deref = array(
- "ANY" => "get_obj_zval_ptr(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_deref(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_deref_\\1(execute_data, opline->op2.var)",
- "TMPVAR" => "???",
+ "ANY" => "get_obj_zval_ptr(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_deref(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_deref_\\1(execute_data, opline->op2.var)",
+ "TMPVAR" => "???",
+ "TMPVARCV" => "???",
);
$op1_get_obj_zval_ptr_ptr = array(
- "ANY" => "get_obj_zval_ptr_ptr(opline->op1_type, opline->op1, execute_data, &free_op1, \\1)",
- "TMP" => "NULL",
- "VAR" => "_get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1)",
- "CONST" => "NULL",
- "UNUSED" => "_get_obj_zval_ptr_unused(execute_data)",
- "CV" => "_get_zval_ptr_cv_\\1(execute_data, opline->op1.var)",
- "TMPVAR" => "???",
+ "ANY" => "get_obj_zval_ptr_ptr(opline->op1_type, opline->op1, execute_data, &free_op1, \\1)",
+ "TMP" => "NULL",
+ "VAR" => "_get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1)",
+ "CONST" => "NULL",
+ "UNUSED" => "_get_obj_zval_ptr_unused(execute_data)",
+ "CV" => "_get_zval_ptr_cv_\\1(execute_data, opline->op1.var)",
+ "TMPVAR" => "???",
+ "TMPVARCV" => "???",
);
$op2_get_obj_zval_ptr_ptr = array(
- "ANY" => "get_obj_zval_ptr_ptr(opline->op2_type, opline->op2, execute_data, &free_op2, \\1)",
- "TMP" => "NULL",
- "VAR" => "_get_zval_ptr_ptr_var(opline->op2.var, execute_data, &free_op2)",
- "CONST" => "NULL",
- "UNUSED" => "_get_obj_zval_ptr_unused(execute_data)",
- "CV" => "_get_zval_ptr_cv_\\1(execute_data, opline->op2.var)",
- "TMPVAR" => "???",
+ "ANY" => "get_obj_zval_ptr_ptr(opline->op2_type, opline->op2, execute_data, &free_op2, \\1)",
+ "TMP" => "NULL",
+ "VAR" => "_get_zval_ptr_ptr_var(opline->op2.var, execute_data, &free_op2)",
+ "CONST" => "NULL",
+ "UNUSED" => "_get_obj_zval_ptr_unused(execute_data)",
+ "CV" => "_get_zval_ptr_cv_\\1(execute_data, opline->op2.var)",
+ "TMPVAR" => "???",
+ "TMPVARCV" => "???",
);
$op1_get_obj_zval_ptr_ptr_undef = array(
- "ANY" => "get_obj_zval_ptr_ptr(opline->op1_type, opline->op1, execute_data, &free_op1, \\1)",
- "TMP" => "NULL",
- "VAR" => "_get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1)",
- "CONST" => "NULL",
- "UNUSED" => "_get_obj_zval_ptr_unused(execute_data)",
- "CV" => "_get_zval_ptr_cv_undef_\\1(execute_data, opline->op1.var)",
- "TMPVAR" => "???",
+ "ANY" => "get_obj_zval_ptr_ptr(opline->op1_type, opline->op1, execute_data, &free_op1, \\1)",
+ "TMP" => "NULL",
+ "VAR" => "_get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1)",
+ "CONST" => "NULL",
+ "UNUSED" => "_get_obj_zval_ptr_unused(execute_data)",
+ "CV" => "_get_zval_ptr_cv_undef_\\1(execute_data, opline->op1.var)",
+ "TMPVAR" => "???",
+ "TMPVARCV" => "EX_VAR(opline->op1.var)",
);
$op2_get_obj_zval_ptr_ptr_undef = array(
- "ANY" => "get_obj_zval_ptr_ptr(opline->op2_type, opline->op2, execute_data, &free_op2, \\1)",
- "TMP" => "NULL",
- "VAR" => "_get_zval_ptr_ptr_var(opline->op2.var, execute_data, &free_op2)",
- "CONST" => "NULL",
- "UNUSED" => "_get_obj_zval_ptr_unused(execute_data)",
- "CV" => "_get_zval_ptr_cv_undef_\\1(execute_data, opline->op2.var)",
- "TMPVAR" => "???",
+ "ANY" => "get_obj_zval_ptr_ptr(opline->op2_type, opline->op2, execute_data, &free_op2, \\1)",
+ "TMP" => "NULL",
+ "VAR" => "_get_zval_ptr_ptr_var(opline->op2.var, execute_data, &free_op2)",
+ "CONST" => "NULL",
+ "UNUSED" => "_get_obj_zval_ptr_unused(execute_data)",
+ "CV" => "_get_zval_ptr_cv_undef_\\1(execute_data, opline->op2.var)",
+ "TMPVAR" => "???",
+ "TMPVARCV" => "EX_VAR(opline->op2.var)",
);
$op1_free_op = array(
- "ANY" => "FREE_OP(free_op1)",
- "TMP" => "zval_ptr_dtor_nogc(free_op1)",
- "VAR" => "zval_ptr_dtor_nogc(free_op1)",
- "CONST" => "",
- "UNUSED" => "",
- "CV" => "",
- "TMPVAR" => "zval_ptr_dtor_nogc(free_op1)",
+ "ANY" => "FREE_OP(free_op1)",
+ "TMP" => "zval_ptr_dtor_nogc(free_op1)",
+ "VAR" => "zval_ptr_dtor_nogc(free_op1)",
+ "CONST" => "",
+ "UNUSED" => "",
+ "CV" => "",
+ "TMPVAR" => "zval_ptr_dtor_nogc(free_op1)",
+ "TMPVARCV" => "???",
);
$op2_free_op = array(
- "ANY" => "FREE_OP(free_op2)",
- "TMP" => "zval_ptr_dtor_nogc(free_op2)",
- "VAR" => "zval_ptr_dtor_nogc(free_op2)",
- "CONST" => "",
- "UNUSED" => "",
- "CV" => "",
- "TMPVAR" => "zval_ptr_dtor_nogc(free_op2)",
+ "ANY" => "FREE_OP(free_op2)",
+ "TMP" => "zval_ptr_dtor_nogc(free_op2)",
+ "VAR" => "zval_ptr_dtor_nogc(free_op2)",
+ "CONST" => "",
+ "UNUSED" => "",
+ "CV" => "",
+ "TMPVAR" => "zval_ptr_dtor_nogc(free_op2)",
+ "TMPVARCV" => "???",
);
$op1_free_op_if_var = array(
- "ANY" => "if (opline->op1_type == IS_VAR) {zval_ptr_dtor_nogc(free_op1);}",
- "TMP" => "",
- "VAR" => "zval_ptr_dtor_nogc(free_op1)",
- "CONST" => "",
- "UNUSED" => "",
- "CV" => "",
- "TMPVAR" => "???",
+ "ANY" => "if (opline->op1_type == IS_VAR) {zval_ptr_dtor_nogc(free_op1);}",
+ "TMP" => "",
+ "VAR" => "zval_ptr_dtor_nogc(free_op1)",
+ "CONST" => "",
+ "UNUSED" => "",
+ "CV" => "",
+ "TMPVAR" => "???",
+ "TMPVARCV" => "???",
);
$op2_free_op_if_var = array(
- "ANY" => "if (opline->op2_type == IS_VAR) {zval_ptr_dtor_nogc(free_op2);}",
- "TMP" => "",
- "VAR" => "zval_ptr_dtor_nogc(free_op2)",
- "CONST" => "",
- "UNUSED" => "",
- "CV" => "",
- "TMPVAR" => "???",
+ "ANY" => "if (opline->op2_type == IS_VAR) {zval_ptr_dtor_nogc(free_op2);}",
+ "TMP" => "",
+ "VAR" => "zval_ptr_dtor_nogc(free_op2)",
+ "CONST" => "",
+ "UNUSED" => "",
+ "CV" => "",
+ "TMPVAR" => "???",
+ "TMPVARCV" => "???",
);
$op1_free_op_var_ptr = array(
- "ANY" => "if (free_op1) {zval_ptr_dtor_nogc(free_op1);}",
- "TMP" => "",
- "VAR" => "if (UNEXPECTED(free_op1)) {zval_ptr_dtor_nogc(free_op1);}",
- "CONST" => "",
- "UNUSED" => "",
- "CV" => "",
- "TMPVAR" => "???",
+ "ANY" => "if (free_op1) {zval_ptr_dtor_nogc(free_op1);}",
+ "TMP" => "",
+ "VAR" => "if (UNEXPECTED(free_op1)) {zval_ptr_dtor_nogc(free_op1);}",
+ "CONST" => "",
+ "UNUSED" => "",
+ "CV" => "",
+ "TMPVAR" => "???",
+ "TMPVARCV" => "???",
);
$op2_free_op_var_ptr = array(
- "ANY" => "if (free_op2) {zval_ptr_dtor_nogc(free_op2);}",
- "TMP" => "",
- "VAR" => "if (UNEXPECTED(free_op2)) {zval_ptr_dtor_nogc(free_op2);}",
- "CONST" => "",
- "UNUSED" => "",
- "CV" => "",
- "TMPVAR" => "???",
+ "ANY" => "if (free_op2) {zval_ptr_dtor_nogc(free_op2);}",
+ "TMP" => "",
+ "VAR" => "if (UNEXPECTED(free_op2)) {zval_ptr_dtor_nogc(free_op2);}",
+ "CONST" => "",
+ "UNUSED" => "",
+ "CV" => "",
+ "TMPVAR" => "???",
+ "TMPVARCV" => "???",
);
$op1_free_unfetched = array(
- "ANY" => "FREE_UNFETCHED_OP(opline->op1_type, opline->op1.var)",
- "TMP" => "zval_ptr_dtor_nogc(EX_VAR(opline->op1.var))",
- "VAR" => "zval_ptr_dtor_nogc(EX_VAR(opline->op1.var))",
- "CONST" => "",
- "UNUSED" => "",
- "CV" => "",
- "TMPVAR" => "zval_ptr_dtor_nogc(EX_VAR(opline->op1.var))",
+ "ANY" => "FREE_UNFETCHED_OP(opline->op1_type, opline->op1.var)",
+ "TMP" => "zval_ptr_dtor_nogc(EX_VAR(opline->op1.var))",
+ "VAR" => "zval_ptr_dtor_nogc(EX_VAR(opline->op1.var))",
+ "CONST" => "",
+ "UNUSED" => "",
+ "CV" => "",
+ "TMPVAR" => "zval_ptr_dtor_nogc(EX_VAR(opline->op1.var))",
+ "TMPVARCV" => "???",
);
$op2_free_unfetched = array(
- "ANY" => "FREE_UNFETCHED_OP(opline->op2_type, opline->op2.var)",
- "TMP" => "zval_ptr_dtor_nogc(EX_VAR(opline->op2.var))",
- "VAR" => "zval_ptr_dtor_nogc(EX_VAR(opline->op2.var))",
- "CONST" => "",
- "UNUSED" => "",
- "CV" => "",
- "TMPVAR" => "zval_ptr_dtor_nogc(EX_VAR(opline->op2.var))",
+ "ANY" => "FREE_UNFETCHED_OP(opline->op2_type, opline->op2.var)",
+ "TMP" => "zval_ptr_dtor_nogc(EX_VAR(opline->op2.var))",
+ "VAR" => "zval_ptr_dtor_nogc(EX_VAR(opline->op2.var))",
+ "CONST" => "",
+ "UNUSED" => "",
+ "CV" => "",
+ "TMPVAR" => "zval_ptr_dtor_nogc(EX_VAR(opline->op2.var))",
+ "TMPVARCV" => "???",
+);
+
+$op_data_type = array(
+ "ANY" => "(opline+1)->op1_type",
+ "TMP" => "IS_TMP_VAR",
+ "VAR" => "IS_VAR",
+ "CONST" => "IS_CONST",
+ "UNUSED" => "IS_UNUSED",
+ "CV" => "IS_CV",
+ "TMPVAR" => "(IS_TMP_VAR|IS_VAR)",
+ "TMPVARCV" => "(IS_TMP_VAR|IS_VAR|IS_CV)",
+);
+
+$op_data_get_zval_ptr = array(
+ "ANY" => "get_zval_ptr((opline+1)->op1_type, (opline+1)->op1, execute_data, &free_op_data, \\1)",
+ "TMP" => "_get_zval_ptr_tmp((opline+1)->op1.var, execute_data, &free_op_data)",
+ "VAR" => "_get_zval_ptr_var((opline+1)->op1.var, execute_data, &free_op_data)",
+ "CONST" => "EX_CONSTANT((opline+1)->op1)",
+ "UNUSED" => "NULL",
+ "CV" => "_get_zval_ptr_cv_\\1(execute_data, (opline+1)->op1.var)",
+ "TMPVAR" => "_get_zval_ptr_var((opline+1)->op1.var, execute_data, &free_op_data)",
+ "TMPVARCV" => "???",
+);
+
+$op_data_get_zval_ptr_deref = array(
+ "ANY" => "get_zval_ptr((opline+1)->op1_type, (opline+1)->op1, execute_data, &free_op_data, \\1)",
+ "TMP" => "_get_zval_ptr_tmp((opline+1)->op1.var, execute_data, &free_op_data)",
+ "VAR" => "_get_zval_ptr_var_deref((opline+1)->op1.var, execute_data, &free_op_data)",
+ "CONST" => "EX_CONSTANT((opline+1)->op1)",
+ "UNUSED" => "NULL",
+ "CV" => "_get_zval_ptr_cv_deref_\\1(execute_data, (opline+1)->op1.var)",
+ "TMPVAR" => "???",
+ "TMPVARCV" => "???",
+);
+
+$op_data_free_op = array(
+ "ANY" => "FREE_OP(free_op_data)",
+ "TMP" => "zval_ptr_dtor_nogc(free_op_data)",
+ "VAR" => "zval_ptr_dtor_nogc(free_op_data)",
+ "CONST" => "",
+ "UNUSED" => "",
+ "CV" => "",
+ "TMPVAR" => "zval_ptr_dtor_nogc(free_op_data)",
+ "TMPVARCV" => "???",
+);
+
+$op_data_free_unfetched = array(
+ "ANY" => "FREE_UNFETCHED_OP((opline+1)->op1_type, (opline+1)->op1.var)",
+ "TMP" => "zval_ptr_dtor_nogc(EX_VAR((opline+1)->op1.var))",
+ "VAR" => "zval_ptr_dtor_nogc(EX_VAR((opline+1)->op1.var))",
+ "CONST" => "",
+ "UNUSED" => "",
+ "CV" => "",
+ "TMPVAR" => "zval_ptr_dtor_nogc(EX_VAR((opline+1)->op1.var))",
+ "TMPVARCV" => "???",
);
$list = array(); // list of opcode handlers and helpers in original order
@@ -426,6 +603,8 @@ $params = array(); // parameters of helpers
$opnames = array(); // opcode name to code mapping
$line_no = 1;
+$used_extra_spec = array();
+
// Writes $s into resulting executor
function out($f, $s) {
global $line_no;
@@ -461,8 +640,27 @@ function helper_name($name, $spec, $op1, $op2) {
return $name.($spec?"_SPEC":"").$prefix[$op1].$prefix[$op2];
}
+function opcode_name($name, $spec, $op1, $op2) {
+ global $prefix, $opnames, $opcodes;
+
+ if (isset($opnames[$name])) {
+ $opcode = $opcodes[$opnames[$name]];
+ // If we haven't helper with specified spicialized operands then
+ // using unspecialized helper
+ if (!isset($opcode["op1"][$op1]) &&
+ isset($opcode["op1"]["ANY"])) {
+ $op1 = "ANY";
+ }
+ if (!isset($opcode["op2"][$op2]) &&
+ isset($opcode["op2"]["ANY"])) {
+ $op2 = "ANY";
+ }
+ }
+ return $name.($spec?"_SPEC":"").$prefix[$op1].$prefix[$op2];
+}
+
// Generates code for opcode handler or helper
-function gen_code($f, $spec, $kind, $export, $code, $op1, $op2, $name) {
+function gen_code($f, $spec, $kind, $export, $code, $op1, $op2, $name, $extra_spec=null) {
global $op1_type, $op2_type, $op1_get_zval_ptr, $op2_get_zval_ptr,
$op1_get_zval_ptr_deref, $op2_get_zval_ptr_deref,
$op1_get_zval_ptr_undef, $op2_get_zval_ptr_undef,
@@ -475,7 +673,9 @@ function gen_code($f, $spec, $kind, $export, $code, $op1, $op2, $name) {
$op1_get_obj_zval_ptr_ptr_undef, $op2_get_obj_zval_ptr_ptr_undef,
$op1_free, $op2_free, $op1_free_unfetched, $op2_free_unfetched,
$op1_free_op, $op2_free_op, $op1_free_op_if_var, $op2_free_op_if_var,
- $op1_free_op_var_ptr, $op2_free_op_var_ptr, $prefix;
+ $op1_free_op_var_ptr, $op2_free_op_var_ptr, $prefix,
+ $op_data_type, $op_data_get_zval_ptr, $op_data_get_zval_ptr_deref,
+ $op_data_free_op, $op_data_free_unfetched;
// Specializing
$code = preg_replace(
@@ -521,7 +721,18 @@ function gen_code($f, $spec, $kind, $export, $code, $op1, $op2, $name) {
"/^#(\s*)if\s+1\s*\\|\\|.*[^\\\\]$/m",
"/^#(\s*)if\s+0\s*&&.*[^\\\\]$/m",
"/^#(\s*)ifdef\s+ZEND_VM_EXPORT\s*\n/m",
- "/^#(\s*)ifndef\s+ZEND_VM_EXPORT\s*\n/m"
+ "/^#(\s*)ifndef\s+ZEND_VM_EXPORT\s*\n/m",
+ "/OP_DATA_TYPE/",
+ "/GET_OP_DATA_ZVAL_PTR\(([^)]*)\)/",
+ "/GET_OP_DATA_ZVAL_PTR_DEREF\(([^)]*)\)/",
+ "/FREE_OP_DATA\(\)/",
+ "/FREE_UNFETCHED_OP_DATA\(\)/",
+ "/RETURN_VALUE_USED\(opline\)/",
+ "/arg_num <= MAX_ARG_FLAG_NUM/",
+ "/ZEND_VM_SMART_BRANCH\(\s*([^,)]*)\s*,\s*([^)]*)\s*\)/",
+ "/opline->extended_value\s*==\s*0/",
+ "/opline->extended_value\s*==\s*ZEND_ASSIGN_DIM/",
+ "/opline->extended_value\s*==\s*ZEND_ASSIGN_OBJ/",
),
array(
$op1_type[$op1],
@@ -560,12 +771,34 @@ function gen_code($f, $spec, $kind, $export, $code, $op1, $op2, $name) {
($op1!="ANY"||$op2!="ANY")?"#\\1if 0\n":"#\\1if 1\n",
($op1!="ANY"||$op2!="ANY")?"0":"1",
($op1!="ANY"||$op2!="ANY")?"1":"0",
- "\\1".(($spec && $kind != ZEND_VM_KIND_CALL)?("_SPEC".$prefix[$op1].$prefix[$op2]):""),
- "goto \\1".(($spec && $kind != ZEND_VM_KIND_CALL)?("_SPEC".$prefix[$op1].$prefix[$op2]):""),
+ "\\1".(($spec && $kind != ZEND_VM_KIND_CALL)?("_SPEC".$prefix[$op1].$prefix[$op2].extra_spec_name($extra_spec)):""),
+ "goto \\1".(($spec && $kind != ZEND_VM_KIND_CALL)?("_SPEC".$prefix[$op1].$prefix[$op2].extra_spec_name($extra_spec)):""),
"#\\1if 1",
"#\\1if 0",
$export?"#\\1if 1\n":"#\\1if 0\n",
- $export?"#\\1if 0\n":"#\\1if 1\n"
+ $export?"#\\1if 0\n":"#\\1if 1\n",
+ $op_data_type[isset($extra_spec['OP_DATA']) ? $extra_spec['OP_DATA'] : "ANY"],
+ $op_data_get_zval_ptr[isset($extra_spec['OP_DATA']) ? $extra_spec['OP_DATA'] : "ANY"],
+ $op_data_get_zval_ptr_deref[isset($extra_spec['OP_DATA']) ? $extra_spec['OP_DATA'] : "ANY"],
+ $op_data_free_op[isset($extra_spec['OP_DATA']) ? $extra_spec['OP_DATA'] : "ANY"],
+ $op_data_free_unfetched[isset($extra_spec['OP_DATA']) ? $extra_spec['OP_DATA'] : "ANY"],
+ isset($extra_spec['RETVAL']) ? $extra_spec['RETVAL'] : "RETURN_VALUE_USED(opline)",
+ isset($extra_spec['QUICK_ARG']) ? $extra_spec['QUICK_ARG'] : "arg_num <= MAX_ARG_FLAG_NUM",
+ isset($extra_spec['SMART_BRANCH']) ?
+ ($extra_spec['SMART_BRANCH'] == 1 ?
+ "ZEND_VM_SMART_BRANCH_JMPZ(\\1, \\2)"
+ : ($extra_spec['SMART_BRANCH'] == 2 ?
+ "ZEND_VM_SMART_BRANCH_JMPNZ(\\1, \\2)" : ""))
+ : "ZEND_VM_SMART_BRANCH(\\1, \\2)",
+ isset($extra_spec['DIM_OBJ']) ?
+ ($extra_spec['DIM_OBJ'] == 0 ? "1" : "0")
+ : "\\0",
+ isset($extra_spec['DIM_OBJ']) ?
+ ($extra_spec['DIM_OBJ'] == 1 ? "1" : "0")
+ : "\\0",
+ isset($extra_spec['DIM_OBJ']) ?
+ ($extra_spec['DIM_OBJ'] == 2 ? "1" : "0")
+ : "\\0",
),
$code);
@@ -579,17 +812,20 @@ function gen_code($f, $spec, $kind, $export, $code, $op1, $op2, $name) {
array(
"/EXECUTE_DATA/m",
"/ZEND_VM_DISPATCH_TO_HANDLER\(\s*([A-Z_]*)\s*\)/m",
- "/ZEND_VM_DISPATCH_TO_HELPER\(\s*([A-Za-z_]*)\s*\)/m",
- "/ZEND_VM_DISPATCH_TO_HELPER_EX\(\s*([A-Za-z_]*)\s*,\s*[A-Za-z_]*\s*,\s*(.*)\s*\);/m",
+ "/ZEND_VM_DISPATCH_TO_HELPER\(\s*([A-Za-z_]*)\s*(,[^)]*)?\)/m",
),
function($matches) use ($spec, $prefix, $op1, $op2) {
if (strncasecmp($matches[0], "EXECUTE_DATA", strlen("EXECUTE_DATA")) == 0) {
return "execute_data";
} else if (strncasecmp($matches[0], "ZEND_VM_DISPATCH_TO_HANDLER", strlen("ZEND_VM_DISPATCH_TO_HANDLER")) == 0) {
- return "ZEND_VM_TAIL_CALL(" . $matches[1] . ($spec?"_SPEC":"") . $prefix[$op1] . $prefix[$op2] . "_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU))";
- } else if (strncasecmp($matches[0], "ZEND_VM_DISPATCH_TO_HELPER_EX", strlen("ZEND_VM_DISPATCH_TO_HELPER_EX")) == 0) {
- return "ZEND_VM_TAIL_CALL(" . helper_name($matches[1], $spec, $op1, $op2) . "(" . $matches[2]. " ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));";
+ return "ZEND_VM_TAIL_CALL(" . opcode_name($matches[1], $spec, $op1, $op2) . "_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU))";
} else {
+ // ZEND_VM_DISPATCH_TO_HELPER
+ if (isset($matches[2])) {
+ // extra args
+ $args = substr(preg_replace("/,\s*[A-Za-z_]*\s*,\s*([^,)\s]*)\s*/", ", $1", $matches[2]), 2);
+ return "ZEND_VM_TAIL_CALL(" . helper_name($matches[1], $spec, $op1, $op2) . "(" . $args. " ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC))";
+ }
return "ZEND_VM_TAIL_CALL(" . helper_name($matches[1], $spec, $op1, $op2) . "(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU))";
}
},
@@ -600,17 +836,20 @@ function gen_code($f, $spec, $kind, $export, $code, $op1, $op2, $name) {
array(
"/EXECUTE_DATA/m",
"/ZEND_VM_DISPATCH_TO_HANDLER\(\s*([A-Z_]*)\s*\)/m",
- "/ZEND_VM_DISPATCH_TO_HELPER\(\s*([A-Za-z_]*)\s*\)/m",
- "/ZEND_VM_DISPATCH_TO_HELPER_EX\(\s*([A-Za-z_]*)\s*,\s*([A-Za-z_]*)\s*,\s*(.*)\s*\);/m",
+ "/ZEND_VM_DISPATCH_TO_HELPER\(\s*([A-Za-z_]*)\s*(,[^)]*)?\)/m",
),
function($matches) use ($spec, $prefix, $op1, $op2) {
if (strncasecmp($matches[0], "EXECUTE_DATA", strlen("EXECUTE_DATA")) == 0) {
return "execute_data";
} else if (strncasecmp($matches[0], "ZEND_VM_DISPATCH_TO_HANDLER", strlen("ZEND_VM_DISPATCH_TO_HANDLER")) == 0) {
- return "goto " . $matches[1] . ($spec?"_SPEC":"") . $prefix[$op1] . $prefix[$op2] . "_LABEL";
- } else if (strncasecmp($matches[0], "ZEND_VM_DISPATCH_TO_HELPER_EX", strlen("ZEND_VM_DISPATCH_TO_HELPER_EX")) == 0) {
- return $matches[2] . " = " . $matches[3] . "; goto " . helper_name($matches[1], $spec, $op1, $op2) . ";";
+ return "goto " . opcode_name($matches[1], $spec, $op1, $op2) . "_LABEL";
} else {
+ // ZEND_VM_DISPATCH_TO_HELPER
+ if (isset($matches[2])) {
+ // extra args
+ $args = preg_replace("/,\s*([A-Za-z_]*)\s*,\s*([^,)\s]*)\s*/", "$1 = $2; ", $matches[2]);
+ return $args . "goto " . helper_name($matches[1], $spec, $op1, $op2);
+ }
return "goto " . helper_name($matches[1], $spec, $op1, $op2);
}
},
@@ -621,17 +860,20 @@ function gen_code($f, $spec, $kind, $export, $code, $op1, $op2, $name) {
array(
"/EXECUTE_DATA/m",
"/ZEND_VM_DISPATCH_TO_HANDLER\(\s*([A-Z_]*)\s*\)/m",
- "/ZEND_VM_DISPATCH_TO_HELPER\(\s*([A-Za-z_]*)\s*\)/m",
- "/ZEND_VM_DISPATCH_TO_HELPER_EX\(\s*([A-Za-z_]*)\s*,\s*([A-Za-z_]*)\s*,\s*(.*)\s*\);/m",
+ "/ZEND_VM_DISPATCH_TO_HELPER\(\s*([A-Za-z_]*)\s*(,[^)]*)?\)/m",
),
function($matches) use ($spec, $prefix, $op1, $op2) {
if (strncasecmp($matches[0], "EXECUTE_DATA", strlen("EXECUTE_DATA")) == 0) {
return "execute_data";
} else if (strncasecmp($matches[0], "ZEND_VM_DISPATCH_TO_HANDLER", strlen("ZEND_VM_DISPATCH_TO_HANDLER")) == 0) {
- return "goto " . $matches[1] . ($spec?"_SPEC":"") . $prefix[$op1] . $prefix[$op2] . "_HANDLER";
- } else if (strncasecmp($matches[0], "ZEND_VM_DISPATCH_TO_HELPER_EX", strlen("ZEND_VM_DISPATCH_TO_HELPER_EX")) == 0) {
- return $matches[2] . " = " . $matches[3] . "; goto " . helper_name($matches[1], $spec, $op1, $op2) . ";";
+ return "goto " . opcode_name($matches[1], $spec, $op1, $op2) . "_HANDLER";
} else {
+ // ZEND_VM_DISPATCH_TO_HELPER
+ if (isset($matches[2])) {
+ // extra args
+ $args = preg_replace("/,\s*([A-Za-z_]*)\s*,\s*([^,)\s]*)\s*/", "$1 = $2; ", $matches[2]);
+ return $args . "goto " . helper_name($matches[1], $spec, $op1, $op2);
+ }
return "goto " . helper_name($matches[1], $spec, $op1, $op2);
}
},
@@ -648,6 +890,7 @@ function gen_code($f, $spec, $kind, $export, $code, $op1, $op2, $name) {
}
$del_free_op1 = (strpos($code, "free_op1") === false);
$del_free_op2 = (strpos($code, "free_op2") === false);
+ $del_free_op_data = (strpos($code, "free_op_data") === false);
$n = 0;
foreach ($matches as $match) {
$dcl = $match[0];
@@ -662,6 +905,11 @@ function gen_code($f, $spec, $kind, $export, $code, $op1, $op2, $name) {
$dcl = preg_replace("/free_op2\s*;/", ";", $dcl);
$changed = 1;
}
+ if ($del_free_op_data && strpos($dcl, "free_op_data") !== false) {
+ $dcl = preg_replace("/free_op_data\s*,\s*/", "", $dcl);
+ $dcl = preg_replace("/free_op_data\s*;/", ";", $dcl);
+ $changed = 1;
+ }
if ($changed) {
$dcl = preg_replace("/,\s*;/", ";", $dcl);
$dcl = preg_replace("/zend_free_op\s*;/", "", $dcl);
@@ -681,42 +929,67 @@ function gen_code($f, $spec, $kind, $export, $code, $op1, $op2, $name) {
}
// Generates opcode handler
-function gen_handler($f, $spec, $kind, $name, $op1, $op2, $use, $code, $lineno) {
- global $definition_file, $prefix, $typecode, $opnames;
+function gen_handler($f, $spec, $kind, $name, $op1, $op2, $use, $code, $lineno, $extra_spec = null, &$switch_labels = array()) {
+ global $definition_file, $prefix, $typecode, $opnames, $commutative_order;
+
+ if ($spec &&
+ isset($extra_spec["NO_CONST_CONST"]) &&
+ $op1 == "CONST" && $op2 == "CONST") {
+ // Skip useless constant handlers
+ return;
+ }
+
+ if ($spec &&
+ isset($extra_spec["COMMUTATIVE"]) &&
+ $commutative_order[$op1] > $commutative_order[$op2]) {
+ // Skip duplicate commutative handlers
+ return;
+ }
+
+ if ($spec &&
+ isset($extra_spec["DIM_OBJ"]) &&
+ (($op2 == "UNUSED" && $extra_spec["DIM_OBJ"] != 1) ||
+ ($op1 == "UNUSED" && $extra_spec["DIM_OBJ"] != 2))) {
+ // Skip useless handlers
+ return;
+ }
if (ZEND_VM_LINES) {
out($f, "#line $lineno \"$definition_file\"\n");
}
// Generate opcode handler's entry point according to selected threading model
+ $spec_name = $name.($spec?"_SPEC":"").$prefix[$op1].$prefix[$op2].($spec?extra_spec_name($extra_spec):"");
switch($kind) {
case ZEND_VM_KIND_CALL:
- out($f,"static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ".$name.($spec?"_SPEC":"").$prefix[$op1].$prefix[$op2]."_HANDLER(ZEND_OPCODE_HANDLER_ARGS)\n");
+ out($f,"static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL {$spec_name}_HANDLER(ZEND_OPCODE_HANDLER_ARGS)\n");
break;
case ZEND_VM_KIND_SWITCH:
if ($spec) {
- out($f,"case ".((string)($opnames[$name]*25+($typecode[$op1=="TMPVAR"?"TMP":$op1]*5)+$typecode[$op2=="TMPVAR"?"TMP":$op2])).": /*".$name."_SPEC".$prefix[$op1].$prefix[$op2]."_HANDLER*/");
+ $cur = $switch_labels ? end($switch_labels) + 1 : 0;
+ out($f,"case $cur: /* $spec_name */");
+ $switch_labels[$spec_name] = $cur;
} else {
out($f,"case ".$name.":");
}
if ($use) {
// This handler is used by other handlers. We will add label to call it.
- out($f," ".$name.($spec?"_SPEC":"").$prefix[$op1].$prefix[$op2]."_LABEL:\n");
+ out($f," {$spec_name}_LABEL:\n");
} else {
out($f,"\n");
}
break;
case ZEND_VM_KIND_GOTO:
- out($f,$name.($spec?"_SPEC":"").$prefix[$op1].$prefix[$op2]."_HANDLER: ZEND_VM_GUARD(".$name.($spec?"_SPEC":"").$prefix[$op1].$prefix[$op2].");\n");
+ out($f,"{$spec_name}_HANDLER: ZEND_VM_GUARD($spec_name);\n");
break;
}
// Generate opcode handler's code
- gen_code($f, $spec, $kind, 0, $code, $op1, $op2, $name);
+ gen_code($f, $spec, $kind, 0, $code, $op1, $op2, $name, $extra_spec);
}
// Generates helper
-function gen_helper($f, $spec, $kind, $name, $op1, $op2, $param, $code, $lineno) {
+function gen_helper($f, $spec, $kind, $name, $op1, $op2, $param, $code, $lineno, $inline) {
global $definition_file, $prefix;
if (ZEND_VM_LINES) {
@@ -726,12 +999,19 @@ function gen_helper($f, $spec, $kind, $name, $op1, $op2, $param, $code, $lineno)
// Generate helper's entry point according to selected threading model
switch($kind) {
case ZEND_VM_KIND_CALL:
+ if ($inline) {
+ $zend_always_inline = " zend_always_inline";
+ $zend_fastcall = "";
+ } else {
+ $zend_always_inline = "";
+ $zend_fastcall = " ZEND_FASTCALL";
+ }
if ($param == null) {
// Helper without parameters
- out($f, "static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ".$name.($spec?"_SPEC":"").$prefix[$op1].$prefix[$op2]."(ZEND_OPCODE_HANDLER_ARGS)\n");
+ out($f, "static$zend_always_inline ZEND_OPCODE_HANDLER_RET$zend_fastcall ".$name.($spec?"_SPEC":"").$prefix[$op1].$prefix[$op2]."(ZEND_OPCODE_HANDLER_ARGS)\n");
} else {
// Helper with parameter
- out($f, "static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ".$name.($spec?"_SPEC":"").$prefix[$op1].$prefix[$op2]."(".$param." ZEND_OPCODE_HANDLER_ARGS_DC)\n");
+ out($f, "static$zend_always_inline ZEND_OPCODE_HANDLER_RET$zend_fastcall ".$name.($spec?"_SPEC":"").$prefix[$op1].$prefix[$op2]."(".$param." ZEND_OPCODE_HANDLER_ARGS_DC)\n");
}
break;
case ZEND_VM_KIND_SWITCH:
@@ -746,103 +1026,212 @@ function gen_helper($f, $spec, $kind, $name, $op1, $op2, $param, $code, $lineno)
gen_code($f, $spec, $kind, 0, $code, $op1, $op2, $name);
}
+
+function gen_null_label($f, $kind, $prolog) {
+ switch ($kind) {
+ case ZEND_VM_KIND_CALL:
+ out($f,$prolog."ZEND_NULL_HANDLER,\n");
+ break;
+ case ZEND_VM_KIND_SWITCH:
+ out($f,$prolog."(void*)(uintptr_t)-1,\n");
+ break;
+ case ZEND_VM_KIND_GOTO:
+ out($f,$prolog."(void*)&&ZEND_NULL_HANDLER,\n");
+ break;
+ }
+}
+
// Generates array of opcode handlers (specialized or unspecialized)
-function gen_labels($f, $spec, $kind, $prolog) {
- global $opcodes, $op_types, $prefix, $typecode;
+function gen_labels($f, $spec, $kind, $prolog, &$specs, $switch_labels = array()) {
+ global $opcodes, $op_types, $prefix;
$next = 0;
+ $label = 0;
if ($spec) {
// Emit labels for specialized executor
// For each opcode in opcode number order
foreach($opcodes as $num => $dsc) {
- while ($next != $num) {
- // If some opcode numbers are not used then fill hole with pointers
- // to handler of undefined opcode
- $op1t = $op_types;
- // For each op1.op_type except ANY
- foreach($op1t as $op1) {
- if ($op1 != "ANY") {
- $op2t = $op_types;
- // For each op2.op_type except ANY
- foreach($op2t as $op2) {
- if ($op2 != "ANY") {
- // Emit pointer to handler of undefined opcode
- switch ($kind) {
- case ZEND_VM_KIND_CALL:
- out($f,$prolog."ZEND_NULL_HANDLER,\n");
- break;
- case ZEND_VM_KIND_SWITCH:
- out($f,$prolog."(void*)(uintptr_t)-1,\n");
- break;
- case ZEND_VM_KIND_GOTO:
- out($f,$prolog."(void*)&&ZEND_NULL_HANDLER,\n");
- break;
+ $specs[$num] = "$label";
+ $spec_op1 = $spec_op2 = $spec_extra = false;
+ $next = $num + 1;
+ $diff = array_diff_key(array_flip($op_types), isset($dsc["op1"]) ? $dsc["op1"] : array());
+ if ((count($diff) == count($op_types) - 1 ? isset($diff["ANY"]) : count($diff) != count($op_types)) || isset($dsc["op1"]["TMPVAR"]) || isset($dsc["op1"]["TMPVARCV"])) {
+ $spec_op1 = true;
+ $specs[$num] .= " | SPEC_RULE_OP1";
+ }
+ $diff = array_diff_key(array_flip($op_types), isset($dsc["op2"]) ? $dsc["op2"] : array());
+ if ((count($diff) == count($op_types) - 1 ? isset($diff["ANY"]) : count($diff) != count($op_types)) || isset($dsc["op2"]["TMPVAR"]) || isset($dsc["op2"]["TMPVARCV"])) {
+ $spec_op2 = true;
+ $specs[$num] .= " | SPEC_RULE_OP2";
+ }
+ $spec_extra = call_user_func_array("array_merge", extra_spec_handler($dsc) ?: array(array()));
+ $flags = extra_spec_flags($spec_extra);
+ if ($flags) {
+ $specs[$num] .= " | ".implode("|", $flags);
+ }
+ if ($num >= 256) {
+ $opcodes[$num]['spec_code'] = $specs[$num];
+ unset($specs[$num]);
+ }
+
+ $foreach_op1 = function($do) use ($dsc, $op_types) {
+ return function() use ($do, $dsc, $op_types) {
+ // For each op1.op_type except ANY
+ foreach($op_types as $op1) {
+ if ($op1 != "ANY") {
+ if (!isset($dsc["op1"][$op1])) {
+ if ($op1 == "TMP" || $op1 == "VAR") {
+ if (isset($dsc["op1"]["TMPVAR"])) {
+ $op1 = "TMPVAR";
+ } else if (isset($dsc["op1"]["TMPVARCV"])) {
+ $op1 = "TMPVARCV";
+ } else {
+ $op1 = "ANY";
+ }
+ } else if ($op1 == "CV" && isset($dsc["op1"]["TMPVARCV"])) {
+ $op1 = "TMPVARCV";
+ } else {
+ // Try to use unspecialized handler
+ $op1 = "ANY";
}
}
+ $do($op1, "ANY");
}
}
- }
- $next++;
- }
- $next = $num + 1;
- $op1t = $op_types;
- // For each op1.op_type except ANY
- foreach($op1t as $op1) {
- if ($op1 != "ANY") {
- if (!isset($dsc["op1"][$op1])) {
- if (($op1 == "TMP" || $op1 == "VAR") && isset($dsc["op1"]["TMPVAR"])) {
- $op1 = "TMPVAR";
- } else {
- // Try to use unspecialized handler
- $op1 = "ANY";
- }
- }
- $op2t = $op_types;
+ };
+ };
+ $foreach_op2 = function($do) use ($dsc, $op_types) {
+ return function($op1) use ($do, $dsc, $op_types) {
// For each op2.op_type except ANY
- foreach($op2t as $op2) {
+ foreach($op_types as $op2) {
if ($op2 != "ANY") {
if (!isset($dsc["op2"][$op2])) {
- if (($op2 == "TMP" || $op2 == "VAR") && isset($dsc["op2"]["TMPVAR"])) {
- $op2 = "TMPVAR";
+ if ($op2 == "TMP" || $op2 == "VAR") {
+ if (isset($dsc["op2"]["TMPVAR"])) {
+ $op2 = "TMPVAR";
+ } else if (isset($dsc["op2"]["TMPVARCV"])) {
+ $op2 = "TMPVARCV";
+ } else {
+ $op2 = "ANY";
+ }
+ } else if ($op2 == "CV" && isset($dsc["op2"]["TMPVARCV"])) {
+ $op2 = "TMPVARCV";
} else {
// Try to use unspecialized handler
$op2 = "ANY";
}
}
- // Check if specialized handler is defined
- if (isset($dsc["op1"][$op1]) &&
- isset($dsc["op2"][$op2])) {
- // Emit pointer to specialized handler
- switch ($kind) {
- case ZEND_VM_KIND_CALL:
- out($f,$prolog.$dsc["op"]."_SPEC".$prefix[$op1].$prefix[$op2]."_HANDLER,\n");
- break;
- case ZEND_VM_KIND_SWITCH:
- out($f,$prolog."(void*)(uintptr_t)".((string)($num*25+$typecode[$op1=="TMPVAR"?"TMP":$op1]*5+$typecode[$op2=="TMPVAR"?"TMP":$op2])).",\n");
- break;
- case ZEND_VM_KIND_GOTO:
- out($f,$prolog."(void*)&&".$dsc["op"]."_SPEC".$prefix[$op1].$prefix[$op2]."_HANDLER,\n");
- break;
- }
- } else {
- // Emit pinter to handler of undefined opcode
- switch ($kind) {
- case ZEND_VM_KIND_CALL:
- out($f,$prolog."ZEND_NULL_HANDLER,\n");
- break;
- case ZEND_VM_KIND_SWITCH:
- out($f,$prolog."(void*)(uintptr_t)-1,\n");
- break;
- case ZEND_VM_KIND_GOTO:
- out($f,$prolog."(void*)&&ZEND_NULL_HANDLER,\n");
- break;
+ $do($op1, $op2);
+ }
+ }
+ };
+ };
+ $foreach_op_data = function($do) use ($dsc, $op_types) {
+ return function($op1, $op2, $extra_spec = array()) use ($do, $dsc, $op_types) {
+ // For each op_data.op_type except ANY
+ foreach($op_types as $op_data) {
+ if ($op_data != "ANY") {
+ if (!isset($dsc["spec"]["OP_DATA"][$op_data])) {
+ if ($op_data == "TMP" || $op_data == "VAR") {
+ if (isset($dsc["spec"]["OP_DATA"]["TMPVAR"])) {
+ $op_data = "TMPVAR";
+ } else if (isset($dsc["spec"]["OP_DATA"]["TMPVARCV"])) {
+ $op_data = "TMPVARCV";
+ } else {
+ // Try to use unspecialized handler
+ $op_data = "ANY";
+ }
+ } else if ($op_data == "CV" && isset($dsc["OP_DATA"]["TMPVARCV"])) {
+ $op_data = "TMPVARCV";
+ } else {
+ // Try to use unspecialized handler
+ $op_data = "ANY";
}
}
+ $do($op1, $op2, array("OP_DATA" => $op_data) + $extra_spec);
}
}
+ };
+ };
+ $foreach_extra_spec = function($do, $spec) use ($dsc) {
+ return function($op1, $op2, $extra_spec = array()) use ($do, $spec, $dsc) {
+ foreach ($dsc["spec"][$spec] as $val) {
+ $do($op1, $op2, array($spec => $val) + $extra_spec);
+ }
+ };
+ };
+ $generate = function ($op1, $op2, $extra_spec = array()) use ($f, $kind, $dsc, $prefix, $prolog, $num, $switch_labels, &$label) {
+ global $typecode, $commutative_order;
+
+ // Check if specialized handler is defined
+ /* TODO: figure out better way to signal "specialized and not defined" than an extra lookup */
+ if (isset($dsc["op1"][$op1]) &&
+ isset($dsc["op2"][$op2]) &&
+ (!isset($extra_spec["OP_DATA"]) || isset($dsc["spec"]["OP_DATA"][$extra_spec["OP_DATA"]]))) {
+
+ if (isset($extra_spec["NO_CONST_CONST"]) &&
+ $op1 == "CONST" && $op2 == "CONST") {
+ // Skip useless constant handlers
+ gen_null_label($f, $kind, $prolog);
+ $label++;
+ return;
+ } else if (isset($extra_spec["COMMUTATIVE"]) &&
+ $commutative_order[$op1] > $commutative_order[$op2]) {
+ // Skip duplicate commutative handlers
+ gen_null_label($f, $kind, $prolog);
+ $label++;
+ return;
+ } else if (isset($extra_spec["DIM_OBJ"]) &&
+ (($op2 == "UNUSED" && $extra_spec["DIM_OBJ"] != 1) ||
+ ($op1 == "UNUSED" && $extra_spec["DIM_OBJ"] != 2))) {
+ // Skip useless handlers
+ gen_null_label($f, $kind, $prolog);
+ $label++;
+ return;
+ }
+
+ // Emit pointer to specialized handler
+ $spec_name = $dsc["op"]."_SPEC".$prefix[$op1].$prefix[$op2].extra_spec_name($extra_spec);
+ switch ($kind) {
+ case ZEND_VM_KIND_CALL:
+ out($f,"$prolog{$spec_name}_HANDLER,\n");
+ $label++;
+ break;
+ case ZEND_VM_KIND_SWITCH:
+ out($f,$prolog."(void*)(uintptr_t)$switch_labels[$spec_name],\n");
+ $label++;
+ break;
+ case ZEND_VM_KIND_GOTO:
+ out($f,$prolog."(void*)&&{$spec_name}_HANDLER,\n");
+ $label++;
+ break;
+ }
+ } else {
+ // Emit pointer to handler of undefined opcode
+ gen_null_label($f, $kind, $prolog);
+ $label++;
+ }
+ };
+
+ $do = $generate;
+ if ($spec_extra) {
+ foreach ($spec_extra as $extra => $devnull) {
+ if ($extra == "OP_DATA") {
+ $do = $foreach_op_data($do);
+ } else {
+ $do = $foreach_extra_spec($do, $extra);
+ }
}
}
+ if ($spec_op2) {
+ $do = $foreach_op2($do);
+ }
+ if ($spec_op1) {
+ $do = $foreach_op1($do);
+ }
+
+ $do("ANY", "ANY");
}
} else {
// Emit labels for unspecialized executor
@@ -865,6 +1254,9 @@ function gen_labels($f, $spec, $kind, $prolog) {
}
$next++;
}
+ if ($num >= 256) {
+ continue;
+ }
$next = $num+1;
//ugly trick for ZEND_VM_DEFINE_OP
@@ -909,6 +1301,21 @@ function gen_labels($f, $spec, $kind, $prolog) {
out($f,$prolog."(void*)&&ZEND_NULL_HANDLER\n");
break;
}
+ $specs[$num + 1] = "$label";
+}
+
+// Generates specialized offsets
+function gen_specs($f, $spec, $kind, $prolog, $specs) {
+ $lastdef = array_pop($specs);
+ $last = 0;
+ foreach ($specs as $num => $def) {
+ while (++$last < $num) {
+ out($f, "$prolog$lastdef,\n");
+ }
+ $last = $num;
+ out($f, "$prolog$def,\n");
+ }
+ out($f, "$prolog$lastdef\n");
}
// Generates handler for undefined opcodes (CALL threading model)
@@ -929,8 +1336,97 @@ function gen_null_handler($f) {
}
}
+function extra_spec_name($extra_spec) {
+ global $prefix;
+
+ $s = "";
+ if (isset($extra_spec["OP_DATA"])) {
+ $s .= "_OP_DATA" . $prefix[$extra_spec["OP_DATA"]];
+ }
+ if (isset($extra_spec["RETVAL"])) {
+ $s .= "_RETVAL_".($extra_spec["RETVAL"] ? "USED" : "UNUSED");
+ }
+ if (isset($extra_spec["QUICK_ARG"])) {
+ if ($extra_spec["QUICK_ARG"]) {
+ $s .= "_QUICK";
+ }
+ }
+ if (isset($extra_spec["SMART_BRANCH"])) {
+ if ($extra_spec["SMART_BRANCH"] == 1) {
+ $s .= "_JMPZ";
+ } else if ($extra_spec["SMART_BRANCH"] == 2) {
+ $s .= "_JMPNZ";
+ }
+ }
+ if (isset($extra_spec["DIM_OBJ"])) {
+ if ($extra_spec["DIM_OBJ"] == 1) {
+ $s .= "_DIM";
+ } else if ($extra_spec["DIM_OBJ"] == 2) {
+ $s .= "_OBJ";
+ }
+ }
+ return $s;
+}
+
+function extra_spec_flags($extra_spec) {
+ $s = array();
+ if (isset($extra_spec["OP_DATA"])) {
+ $s[] = "SPEC_RULE_OP_DATA";
+ }
+ if (isset($extra_spec["RETVAL"])) {
+ $s[] = "SPEC_RULE_RETVAL";
+ }
+ if (isset($extra_spec["QUICK_ARG"])) {
+ $s[] = "SPEC_RULE_QUICK_ARG";
+ }
+ if (isset($extra_spec["SMART_BRANCH"])) {
+ $s[] = "SPEC_RULE_SMART_BRANCH";
+ }
+ if (isset($extra_spec["DIM_OBJ"])) {
+ $s[] = "SPEC_RULE_DIM_OBJ";
+ }
+ return $s;
+}
+
+function extra_spec_handler($dsc) {
+ global $op_types_ex;
+
+ if (!isset($dsc["spec"])) {
+ return array(array());
+ }
+ $specs = $dsc["spec"];
+
+ if (isset($specs["OP_DATA"])) {
+ $op_data_specs = $specs["OP_DATA"];
+ $specs["OP_DATA"] = array();
+ foreach($op_types_ex as $op_data) {
+ if (isset($dsc["spec"]["OP_DATA"][$op_data])) {
+ $specs["OP_DATA"][] = $op_data;
+ }
+ }
+ }
+
+ $f = function($specs) use (&$f) {
+ $spec = key($specs);
+ $top = array_shift($specs);
+ if ($specs) {
+ $next = $f($specs);
+ } else {
+ $next = array(array());
+ }
+ $ret = array();
+ foreach ($next as $existing) {
+ foreach ($top as $mode) {
+ $ret[] = array($spec => $mode) + $existing;
+ }
+ }
+ return $ret;
+ };
+ return $f($specs);
+}
+
// Generates all opcode handlers and helpers (specialized or unspecilaized)
-function gen_executor_code($f, $spec, $kind, $prolog) {
+function gen_executor_code($f, $spec, $kind, $prolog, &$switch_labels = array()) {
global $list, $opcodes, $helpers, $op_types_ex;
if ($spec) {
@@ -945,11 +1441,13 @@ function gen_executor_code($f, $spec, $kind, $prolog) {
foreach ($list as $lineno => $dsc) {
if (isset($dsc["handler"])) {
$num = $dsc["handler"];
- // Check if handler accepts such types of operands (op1 and op2)
- if (isset($opcodes[$num]["op1"][$op1]) &&
- isset($opcodes[$num]["op2"][$op2])) {
- // Generate handler code
- gen_handler($f, 1, $kind, $opcodes[$num]["op"], $op1, $op2, isset($opcodes[$num]["use"]), $opcodes[$num]["code"], $lineno);
+ foreach (extra_spec_handler($opcodes[$num]) as $extra_spec) {
+ // Check if handler accepts such types of operands (op1 and op2)
+ if (isset($opcodes[$num]["op1"][$op1]) &&
+ isset($opcodes[$num]["op2"][$op2])) {
+ // Generate handler code
+ gen_handler($f, 1, $kind, $opcodes[$num]["op"], $op1, $op2, isset($opcodes[$num]["use"]), $opcodes[$num]["code"], $lineno, $extra_spec, $switch_labels);
+ }
}
} else if (isset($dsc["helper"])) {
$num = $dsc["helper"];
@@ -957,7 +1455,7 @@ function gen_executor_code($f, $spec, $kind, $prolog) {
if (isset($helpers[$num]["op1"][$op1]) &&
isset($helpers[$num]["op2"][$op2])) {
// Generate helper code
- gen_helper($f, 1, $kind, $num, $op1, $op2, $helpers[$num]["param"], $helpers[$num]["code"], $lineno);
+ gen_helper($f, 1, $kind, $num, $op1, $op2, $helpers[$num]["param"], $helpers[$num]["code"], $lineno, $helpers[$num]["inline"]);
}
} else {
var_dump($dsc);
@@ -973,12 +1471,14 @@ function gen_executor_code($f, $spec, $kind, $prolog) {
foreach ($list as $lineno => $dsc) {
if (isset($dsc["handler"])) {
$num = $dsc["handler"];
- // Generate handler code
- gen_handler($f, 0, $kind, $opcodes[$num]["op"], "ANY", "ANY", isset($opcodes[$num]["use"]), $opcodes[$num]["code"], $lineno);
+ // Generate handler code
+ if ($num < 256) {
+ gen_handler($f, 0, $kind, $opcodes[$num]["op"], "ANY", "ANY", isset($opcodes[$num]["use"]), $opcodes[$num]["code"], $lineno);
+ }
} else if (isset($dsc["helper"])) {
$num = $dsc["helper"];
- // Generate helper code
- gen_helper($f, 0, $kind, $num, "ANY", "ANY", $helpers[$num]["param"], $helpers[$num]["code"], $lineno);
+ // Generate helper code
+ gen_helper($f, 0, $kind, $num, "ANY", "ANY", $helpers[$num]["param"], $helpers[$num]["code"], $lineno, $helpers[$num]["inline"]);
} else {
var_dump($dsc);
die("??? $kind:$num\n");
@@ -1019,14 +1519,26 @@ function skip_blanks($f, $prolog, $epilog) {
function gen_executor($f, $skl, $spec, $kind, $executor_name, $initializer_name) {
global $params, $skeleton_file, $line_no;
- $lineno = 0;
+ $switch_labels = array();
+ $lineno = 0;
foreach ($skl as $line) {
// Skeleton file contains special markers in form %NAME% those are
// substituted by custom code
if (preg_match("/(.*)[{][%]([A-Z_]*)[%][}](.*)/", $line, $m)) {
switch ($m[2]) {
case "DEFINES":
+ out($f,"#define SPEC_START_MASK 0x0000ffff\n");
+ out($f,"#define SPEC_RULE_OP1 0x00010000\n");
+ out($f,"#define SPEC_RULE_OP2 0x00020000\n");
+ out($f,"#define SPEC_RULE_OP_DATA 0x00040000\n");
+ out($f,"#define SPEC_RULE_RETVAL 0x00080000\n");
+ out($f,"#define SPEC_RULE_QUICK_ARG 0x00100000\n");
+ out($f,"#define SPEC_RULE_SMART_BRANCH 0x00200000\n");
+ out($f,"#define SPEC_RULE_DIM_OBJ 0x00400000\n");
+ out($f,"\n");
+ out($f,"static const uint32_t *zend_spec_handlers;\n");
out($f,"static const void **zend_opcode_handlers;\n");
+ out($f,"static int zend_handlers_count;\n");
out($f,"static const void *zend_vm_get_opcode_handler(zend_uchar opcode, const zend_op* op);\n\n");
switch ($kind) {
case ZEND_VM_KIND_CALL:
@@ -1098,7 +1610,7 @@ function gen_executor($f, $skl, $spec, $kind, $executor_name, $initializer_name)
out($f,"#define HANDLE_EXCEPTION() LOAD_OPLINE(); ZEND_VM_CONTINUE()\n");
out($f,"#define HANDLE_EXCEPTION_LEAVE() LOAD_OPLINE(); ZEND_VM_LEAVE()\n");
out($f,"#if defined(ZEND_VM_FP_GLOBAL_REG)\n");
- out($f,"# define ZEND_VM_ENTER() execute_data = EG(current_execute_data); LOAD_OPLINE(); ZEND_VM_CONTINUE()\n");
+ out($f,"# define ZEND_VM_ENTER() execute_data = EG(current_execute_data); LOAD_OPLINE(); ZEND_VM_INTERRUPT_CHECK(); ZEND_VM_CONTINUE()\n");
out($f,"# define ZEND_VM_LEAVE() ZEND_VM_CONTINUE()\n");
out($f,"#elif defined(ZEND_VM_IP_GLOBAL_REG)\n");
out($f,"# define ZEND_VM_ENTER() opline = EG(current_execute_data)->opline; return 1\n");
@@ -1134,7 +1646,7 @@ function gen_executor($f, $skl, $spec, $kind, $executor_name, $initializer_name)
out($f,"#define HANDLE_EXCEPTION_LEAVE() LOAD_OPLINE(); ZEND_VM_LEAVE()\n");
out($f,"#define ZEND_VM_CONTINUE() goto zend_vm_continue\n");
out($f,"#define ZEND_VM_RETURN() return\n");
- out($f,"#define ZEND_VM_ENTER() execute_data = EG(current_execute_data); LOAD_OPLINE(); ZEND_VM_CONTINUE()\n");
+ out($f,"#define ZEND_VM_ENTER() execute_data = EG(current_execute_data); LOAD_OPLINE(); ZEND_VM_INTERRUPT_CHECK(); ZEND_VM_CONTINUE()\n");
out($f,"#define ZEND_VM_LEAVE() ZEND_VM_CONTINUE()\n");
out($f,"#define ZEND_VM_DISPATCH(opcode, opline) dispatch_handler = zend_vm_get_opcode_handler(opcode, opline); goto zend_vm_dispatch;\n");
out($f,"\n");
@@ -1168,7 +1680,7 @@ function gen_executor($f, $skl, $spec, $kind, $executor_name, $initializer_name)
}
out($f,"#define ZEND_VM_CONTINUE() goto *(void**)(OPLINE->handler)\n");
out($f,"#define ZEND_VM_RETURN() return\n");
- out($f,"#define ZEND_VM_ENTER() execute_data = EG(current_execute_data); LOAD_OPLINE(); ZEND_VM_CONTINUE()\n");
+ out($f,"#define ZEND_VM_ENTER() execute_data = EG(current_execute_data); LOAD_OPLINE(); ZEND_VM_INTERRUPT_CHECK(); ZEND_VM_CONTINUE()\n");
out($f,"#define ZEND_VM_LEAVE() ZEND_VM_CONTINUE()\n");
out($f,"#define ZEND_VM_DISPATCH(opcode, opline) goto *(void**)(zend_vm_get_opcode_handler(opcode, opline));\n");
out($f,"\n");
@@ -1211,9 +1723,14 @@ function gen_executor($f, $skl, $spec, $kind, $executor_name, $initializer_name)
$prolog = $m[1];
out($f,$prolog."if (UNEXPECTED(execute_data == NULL)) {\n");
out($f,$prolog."\tstatic const void* labels[] = {\n");
- gen_labels($f, $spec, $kind, $prolog."\t\t");
+ gen_labels($f, $spec, $kind, $prolog."\t\t", $specs);
out($f,$prolog."\t};\n");
- out($f,$prolog."\tzend_opcode_handlers = (const void **)labels;\n");
+ out($f,$prolog."static const uint32_t specs[] = {\n");
+ gen_specs($f, $spec, $kind, $prolog."\t", $specs);
+ out($f,$prolog."};\n");
+ out($f,$prolog."\tzend_opcode_handlers = (const void **) labels;\n");
+ out($f,$prolog."\tzend_handlers_count = sizeof(labels) / sizeof(void*);\n");
+ out($f,$prolog."\tzend_spec_handlers = (const uint32_t *) specs;\n");
out($f,$prolog."\treturn;\n");
out($f,$prolog."}\n");
} else {
@@ -1265,6 +1782,7 @@ function gen_executor($f, $skl, $spec, $kind, $executor_name, $initializer_name)
"#else\n" .
$m[1]."if (EXPECTED(ret > 0)) {\n" .
$m[1]."\texecute_data = EG(current_execute_data);\n".
+ $m[1]."\tZEND_VM_INTERRUPT_CHECK();\n".
$m[1]."} else {\n" .
"# ifdef ZEND_VM_IP_GLOBAL_REG\n" .
$m[1]."\topline = orig_opline;\n" .
@@ -1274,7 +1792,7 @@ function gen_executor($f, $skl, $spec, $kind, $executor_name, $initializer_name)
"#endif\n");
} else {
// Emit executor code
- gen_executor_code($f, $spec, $kind, $m[1]);
+ gen_executor_code($f, $spec, $kind, $m[1], $switch_labels);
}
break;
case "EXTERNAL_EXECUTOR":
@@ -1295,9 +1813,14 @@ function gen_executor($f, $skl, $spec, $kind, $executor_name, $initializer_name)
out($f,$prolog.$executor_name."_ex(NULL);\n");
} else {
out($f,$prolog."static const void *labels[] = {\n");
- gen_labels($f, $spec, $kind, $prolog."\t");
+ gen_labels($f, $spec, $kind, $prolog."\t", $specs, $switch_labels);
+ out($f,$prolog."};\n");
+ out($f,$prolog."static const uint32_t specs[] = {\n");
+ gen_specs($f, $spec, $kind, $prolog."\t", $specs);
out($f,$prolog."};\n");
out($f,$prolog."zend_opcode_handlers = labels;\n");
+ out($f,$prolog."\tzend_handlers_count = sizeof(labels) / sizeof(void*);\n");
+ out($f,$prolog."zend_spec_handlers = specs;\n");
}
break;
default:
@@ -1310,9 +1833,93 @@ function gen_executor($f, $skl, $spec, $kind, $executor_name, $initializer_name)
}
}
+function parse_operand_spec($def, $lineno, $str, &$flags) {
+ global $vm_op_decode;
+
+ $flags = 0;
+ $a = explode("|",$str);
+ foreach($a as $val) {
+ if (isset($vm_op_decode[$val])) {
+ $flags |= $vm_op_decode[$val];
+ } else {
+ die("ERROR ($def:$lineno): Wrong operand type '$str'\n");
+ }
+ }
+ if (!($flags & ZEND_VM_OP_SPEC)) {
+ if (count($a) != 1) {
+ die("ERROR ($def:$lineno): Wrong operand type '$str'\n");
+ }
+ $a = array("ANY");
+ }
+ return array_flip($a);
+}
+
+function parse_ext_spec($def, $lineno, $str) {
+ global $vm_ext_decode;
+
+ $flags = 0;
+ $a = explode("|",$str);
+ foreach($a as $val) {
+ if (isset($vm_ext_decode[$val])) {
+ $flags |= $vm_ext_decode[$val];
+ } else {
+ die("ERROR ($def:$lineno): Wrong extended_value type '$str'\n");
+ }
+ }
+ return $flags;
+}
+
+function parse_spec_rules($def, $lineno, $str) {
+ global $used_extra_spec;
+
+ $ret = array();
+ $a = explode(",", $str);
+ foreach($a as $rule) {
+ $n = strpos($rule, "=");
+ if ($n !== false) {
+ $id = trim(substr($rule, 0, $n));
+ $val = trim(substr($rule, $n+1));
+ switch ($id) {
+ case "OP_DATA":
+ $ret["OP_DATA"] = parse_operand_spec($def, $lineno, $val, $devnull);
+ break;
+ default:
+ die("ERROR ($def:$lineno): Wrong specialization rules '$str'\n");
+ }
+ $used_extra_spec[$id] = 1;
+ } else {
+ switch ($rule) {
+ case "RETVAL":
+ $ret["RETVAL"] = array(0, 1);
+ break;
+ case "QUICK_ARG":
+ $ret["QUICK_ARG"] = array(0, 1);
+ break;
+ case "SMART_BRANCH":
+ $ret["SMART_BRANCH"] = array(0, 1, 2);
+ break;
+ case "DIM_OBJ":
+ $ret["DIM_OBJ"] = array(0, 1, 2);
+ break;
+ case "NO_CONST_CONST":
+ $ret["NO_CONST_CONST"] = array(1);
+ break;
+ case "COMMUTATIVE":
+ $ret["COMMUTATIVE"] = array(1);
+ break;
+ default:
+ die("ERROR ($def:$lineno): Wrong specialization rules '$str'\n");
+ }
+ $used_extra_spec[$rule] = 1;
+ }
+ }
+ return $ret;
+}
+
function gen_vm($def, $skel) {
global $definition_file, $skeleton_file, $executor_file,
- $op_types, $list, $opcodes, $helpers, $params, $opnames;
+ $op_types, $list, $opcodes, $helpers, $params, $opnames,
+ $vm_op_flags, $used_extra_spec;
// Load definition file
$in = @file($def);
@@ -1336,13 +1943,14 @@ function gen_vm($def, $skel) {
$helper = null;
$max_opcode_len = 0;
$max_opcode = 0;
+ $extra_num = 256;
$export = array();
foreach ($in as $line) {
++$lineno;
if (strpos($line,"ZEND_VM_HANDLER(") === 0) {
// Parsing opcode handler's definition
if (preg_match(
- "/^ZEND_VM_HANDLER\(\s*([0-9]+)\s*,\s*([A-Z_]+)\s*,\s*([A-Z|]+)\s*,\s*([A-Z|]+)\s*\)/",
+ "/^ZEND_VM_HANDLER\(\s*([0-9]+)\s*,\s*([A-Z_]+)\s*,\s*([A-Z_|]+)\s*,\s*([A-Z_|]+)\s*(,\s*([A-Z_|]+)\s*)?(,\s*SPEC\(([A-Z_|=,]+)\)\s*)?\)/",
$line,
$m) == 0) {
die("ERROR ($def:$lineno): Invalid ZEND_VM_HANDLER definition.\n");
@@ -1350,8 +1958,12 @@ function gen_vm($def, $skel) {
$code = (int)$m[1];
$op = $m[2];
$len = strlen($op);
- $op1 = array_flip(explode("|",$m[3]));
- $op2 = array_flip(explode("|",$m[4]));
+ $op1 = parse_operand_spec($def, $lineno, $m[3], $flags1);
+ $op2 = parse_operand_spec($def, $lineno, $m[4], $flags2);
+ $flags = $flags1 | ($flags2 << 8);
+ if (!empty($m[6])) {
+ $flags |= parse_ext_spec($def, $lineno, $m[6]);
+ }
if ($len > $max_opcode_len) {
$max_opcode_len = $len;
@@ -1365,48 +1977,88 @@ function gen_vm($def, $skel) {
if (isset($opnames[$op])) {
die("ERROR ($def:$lineno): Opcode with name '$op' is already defined.\n");
}
- $opcodes[$code] = array("op"=>$op,"op1"=>$op1,"op2"=>$op2,"code"=>"");
+ $opcodes[$code] = array("op"=>$op,"op1"=>$op1,"op2"=>$op2,"code"=>"","flags"=>$flags);
+ if (isset($m[8])) {
+ $opcodes[$code]["spec"] = parse_spec_rules($def, $lineno, $m[8]);
+ if (isset($opcodes[$code]["spec"]["NO_CONST_CONST"])) {
+ $opcodes[$code]["flags"] |= $vm_op_flags["ZEND_VM_NO_CONST_CONST"];
+ }
+ if (isset($opcodes[$code]["spec"]["COMMUTATIVE"])) {
+ $opcodes[$code]["flags"] |= $vm_op_flags["ZEND_VM_COMMUTATIVE"];
+ }
+ }
$opnames[$op] = $code;
$handler = $code;
$helper = null;
$list[$lineno] = array("handler"=>$handler);
- } else if (strpos($line,"ZEND_VM_HELPER(") === 0) {
- // Parsing helper's definition
+ } else if (strpos($line,"ZEND_VM_TYPE_SPEC_HANDLER(") === 0) {
+ // Parsing opcode handler's definition
if (preg_match(
- "/^ZEND_VM_HELPER\(\s*([A-Za-z_]+)\s*,\s*([A-Z|]+)\s*,\s*([A-Z|]+)\s*\)/",
+ "/^ZEND_VM_TYPE_SPEC_HANDLER\(\s*([A-Z_]+)\s*,\s*([^,]+),\s*([A-Za-z_]+)\s*,\s*([A-Z_|]+)\s*,\s*([A-Z_|]+)\s*(,\s*([A-Z_|]+)\s*)?(,\s*SPEC\(([A-Z_|=,]+)\)\s*)?\)/",
$line,
$m) == 0) {
- die("ERROR ($def:$lineno): Invalid ZEND_VM_HELPER definition.\n");
+ die("ERROR ($def:$lineno): Invalid ZEND_VM_TYPE_HANDLER_HANDLER definition.\n");
}
- $helper = $m[1];
- $op1 = array_flip(explode("|",$m[2]));
- $op2 = array_flip(explode("|",$m[3]));
- if (isset($helpers[$helper])) {
- die("ERROR ($def:$lineno): Helper with name '$helper' is already defined.\n");
+ $orig_op = $m[1];
+ if (!isset($opnames[$orig_op])) {
+ die("ERROR ($def:$lineno): Opcode with name '$orig_op' is not defined.\n");
}
- $helpers[$helper] = array("op1"=>$op1,"op2"=>$op2,"param"=>null,"code"=>"");
- $handler = null;
- $list[$lineno] = array("helper"=>$helper);
- } else if (strpos($line,"ZEND_VM_HELPER_EX(") === 0) {
- // Parsing helper with parameter definition
+ $orig_code = $opnames[$orig_op];
+ $condition = $m[2];
+ $code = $extra_num++;
+ $op = $m[3];
+ $op1 = parse_operand_spec($def, $lineno, $m[4], $flags1);
+ $op2 = parse_operand_spec($def, $lineno, $m[5], $flags2);
+ $flags = $flags1 | ($flags2 << 8);
+ if (!empty($m[7])) {
+ $flags |= parse_ext_spec($def, $lineno, $m[7]);
+ }
+
+ if (isset($opcodes[$code])) {
+ die("ERROR ($def:$lineno): Opcode with name '$code' is already defined.\n");
+ }
+ $opcodes[$orig_code]['type_spec'][$code] = $condition;
+ $used_extra_spec["TYPE"] = 1;
+ $opcodes[$code] = array("op"=>$op,"op1"=>$op1,"op2"=>$op2,"code"=>"","flags"=>$flags);
+ if (isset($m[9])) {
+ $opcodes[$code]["spec"] = parse_spec_rules($def, $lineno, $m[9]);
+ if (isset($opcodes[$code]["spec"]["NO_CONST_CONST"])) {
+ $opcodes[$code]["flags"] |= $vm_op_flags["ZEND_VM_NO_CONST_CONST"];
+ }
+ if (isset($opcodes[$code]["spec"]["COMMUTATIVE"])) {
+ $opcodes[$code]["flags"] |= $vm_op_flags["ZEND_VM_COMMUTATIVE"];
+ }
+ }
+ $opnames[$op] = $code;
+ $handler = $code;
+ $helper = null;
+ $list[$lineno] = array("handler"=>$handler);
+ } else if (strpos($line,"ZEND_VM_HELPER(") === 0 || strpos($line,"ZEND_VM_INLINE_HELPER(") === 0) {
+ // Parsing helper's definition
if (preg_match(
- "/^ZEND_VM_HELPER_EX\(\s*([A-Za-z_]+)\s*,\s*([A-Z|]+)\s*,\s*([A-Z|]+)\s*,\s*(.*)\s*\)/",
+ "/^ZEND_VM(_INLINE)?_HELPER\(\s*([A-Za-z_]+)\s*,\s*([A-Z_|]+)\s*,\s*([A-Z_|]+)\s*(?:,\s*([^)]*))?\s*\)/",
$line,
$m) == 0) {
die("ERROR ($def:$lineno): Invalid ZEND_VM_HELPER definition.\n");
}
- $helper = $m[1];
- $op1 = array_flip(explode("|",$m[2]));
- $op2 = array_flip(explode("|",$m[3]));
- $param = $m[4];
+ $inline = !empty($m[1]);
+ $helper = $m[2];
+ $op1 = parse_operand_spec($def, $lineno, $m[3], $flags1);
+ $op2 = parse_operand_spec($def, $lineno, $m[4], $flags2);
+ $param = isset($m[5]) ? $m[5] : null;
if (isset($helpers[$helper])) {
die("ERROR ($def:$lineno): Helper with name '$helper' is already defined.\n");
}
- // Store parameter
- $params[$param] = 1;
+ // Store parameters
+ foreach (explode(",", $param) as $p) {
+ $p = trim($p);
+ if ($p !== "") {
+ $params[$p] = 1;
+ }
+ }
- $helpers[$helper] = array("op1"=>$op1,"op2"=>$op2,"param"=>$param,"code"=>"");
+ $helpers[$helper] = array("op1"=>$op1,"op2"=>$op2,"param"=>$param,"code"=>"","inline"=>$inline);
$handler = null;
$list[$lineno] = array("helper"=>$helper);
} else if (strpos($line,"ZEND_VM_EXPORT_HANDLER(") === 0) {
@@ -1493,16 +2145,29 @@ function gen_vm($def, $skel) {
fputs($f, "#define ZEND_VM_KIND_GOTO\t" . ZEND_VM_KIND_GOTO . "\n");
fputs($f, "#define ZEND_VM_KIND\t\t" . $GLOBALS["vm_kind_name"][ZEND_VM_KIND] . "\n");
fputs($f, "\n");
+ foreach($vm_op_flags as $name => $val) {
+ fprintf($f, "#define %-24s 0x%08x\n", $name, $val);
+ }
+ fputs($f, "#define ZEND_VM_OP1_FLAGS(flags) (flags & 0xff)\n");
+ fputs($f, "#define ZEND_VM_OP2_FLAGS(flags) ((flags >> 8) & 0xff)\n");
+ fputs($f, "\n");
fputs($f, "BEGIN_EXTERN_C()\n\n");
- fputs($f, "ZEND_API const char *zend_get_opcode_name(zend_uchar opcode);\n\n");
+ fputs($f, "ZEND_API const char *zend_get_opcode_name(zend_uchar opcode);\n");
+ fputs($f, "ZEND_API uint32_t zend_get_opcode_flags(zend_uchar opcode);\n\n");
fputs($f, "END_EXTERN_C()\n\n");
foreach ($opcodes as $code => $dsc) {
$code = str_pad((string)$code,$code_len," ",STR_PAD_LEFT);
$op = str_pad($dsc["op"],$max_opcode_len);
- fputs($f,"#define $op $code\n");
+ if ($code <= $max_opcode) {
+ fputs($f,"#define $op $code\n");
+ }
}
+ $code = str_pad((string)$max_opcode,$code_len," ",STR_PAD_LEFT);
+ $op = str_pad("ZEND_VM_LAST_OPCODE",$max_opcode_len);
+ fputs($f,"\n#define $op $code\n");
+
fputs($f, "\n#endif\n");
fclose($f);
echo "zend_vm_opcodes.h generated successfully.\n";
@@ -1515,14 +2180,24 @@ function gen_vm($def, $skel) {
fputs($f,"#include <stdio.h>\n");
fputs($f,"#include <zend.h>\n\n");
- fputs($f,"const char *zend_vm_opcodes_map[".($max_opcode + 1)."] = {\n");
+ fputs($f,"static const char *zend_vm_opcodes_names[".($max_opcode + 1)."] = {\n");
for ($i = 0; $i <= $max_opcode; $i++) {
fputs($f,"\t".(isset($opcodes[$i]["op"])?'"'.$opcodes[$i]["op"].'"':"NULL").",\n");
}
fputs($f, "};\n\n");
+ fputs($f,"static uint32_t zend_vm_opcodes_flags[".($max_opcode + 1)."] = {\n");
+ for ($i = 0; $i <= $max_opcode; $i++) {
+ fprintf($f, "\t0x%08x,\n", isset($opcodes[$i]["flags"]) ? $opcodes[$i]["flags"] : 0);
+ }
+ fputs($f, "};\n\n");
+
fputs($f, "ZEND_API const char* zend_get_opcode_name(zend_uchar opcode) {\n");
- fputs($f, "\treturn zend_vm_opcodes_map[opcode];\n");
+ fputs($f, "\treturn zend_vm_opcodes_names[opcode];\n");
+ fputs($f, "}\n");
+
+ fputs($f, "ZEND_API uint32_t zend_get_opcode_flags(zend_uchar opcode) {\n");
+ fputs($f, "\treturn zend_vm_opcodes_flags[opcode];\n");
fputs($f, "}\n");
fclose($f);
@@ -1574,31 +2249,71 @@ function gen_vm($def, $skel) {
gen_executor($f, $skl, ZEND_VM_SPEC, ZEND_VM_KIND, "execute", "zend_init_opcodes_handlers");
// Generate zend_vm_get_opcode_handler() function
+ out($f, "static const void *zend_vm_get_opcode_handler_ex(uint32_t spec, const zend_op* op)\n");
+ out($f, "{\n");
+ if (!ZEND_VM_SPEC) {
+ out($f, "\treturn zend_opcode_handlers[spec];\n");
+ } else {
+ out($f, "\tstatic const int zend_vm_decode[] = {\n");
+ out($f, "\t\t_UNUSED_CODE, /* 0 */\n");
+ out($f, "\t\t_CONST_CODE, /* 1 = IS_CONST */\n");
+ out($f, "\t\t_TMP_CODE, /* 2 = IS_TMP_VAR */\n");
+ out($f, "\t\t_UNUSED_CODE, /* 3 */\n");
+ out($f, "\t\t_VAR_CODE, /* 4 = IS_VAR */\n");
+ out($f, "\t\t_UNUSED_CODE, /* 5 */\n");
+ out($f, "\t\t_UNUSED_CODE, /* 6 */\n");
+ out($f, "\t\t_UNUSED_CODE, /* 7 */\n");
+ out($f, "\t\t_UNUSED_CODE, /* 8 = IS_UNUSED */\n");
+ out($f, "\t\t_UNUSED_CODE, /* 9 */\n");
+ out($f, "\t\t_UNUSED_CODE, /* 10 */\n");
+ out($f, "\t\t_UNUSED_CODE, /* 11 */\n");
+ out($f, "\t\t_UNUSED_CODE, /* 12 */\n");
+ out($f, "\t\t_UNUSED_CODE, /* 13 */\n");
+ out($f, "\t\t_UNUSED_CODE, /* 14 */\n");
+ out($f, "\t\t_UNUSED_CODE, /* 15 */\n");
+ out($f, "\t\t_CV_CODE /* 16 = IS_CV */\n");
+ out($f, "\t};\n");
+ out($f, "\tuint32_t offset = 0;\n");
+ out($f, "\tif (spec & SPEC_RULE_OP1) offset = offset * 5 + zend_vm_decode[op->op1_type];\n");
+ out($f, "\tif (spec & SPEC_RULE_OP2) offset = offset * 5 + zend_vm_decode[op->op2_type];\n");
+ if (isset($used_extra_spec["OP_DATA"])) {
+ out($f, "\tif (spec & SPEC_RULE_OP_DATA) offset = offset * 5 + zend_vm_decode[(op + 1)->op1_type];\n");
+ }
+ if (isset($used_extra_spec["RETVAL"])) {
+ out($f, "\tif (spec & SPEC_RULE_RETVAL) offset = offset * 2 + (op->result_type != IS_UNUSED);\n");
+ }
+ if (isset($used_extra_spec["QUICK_ARG"])) {
+ out($f, "\tif (spec & SPEC_RULE_QUICK_ARG) offset = offset * 2 + (op->op2.num < MAX_ARG_FLAG_NUM);\n");
+ }
+ if (isset($used_extra_spec["SMART_BRANCH"])) {
+ out($f, "\tif (spec & SPEC_RULE_SMART_BRANCH) {\n");
+ out($f, "\t\toffset = offset * 3;\n");
+ out($f, "\t\tif ((op+1)->opcode == ZEND_JMPZ) {\n");
+ out($f, "\t\t\toffset += 1;\n");
+ out($f, "\t\t} else if ((op+1)->opcode == ZEND_JMPNZ) {\n");
+ out($f, "\t\t\toffset += 2;\n");
+ out($f, "\t\t}\n");
+ out($f, "\t}\n");
+ }
+ if (isset($used_extra_spec["DIM_OBJ"])) {
+ out($f, "\tif (spec & SPEC_RULE_DIM_OBJ) {\n");
+ out($f, "\t\toffset = offset * 3;\n");
+ out($f, "\t\tif (op->extended_value == ZEND_ASSIGN_DIM) {\n");
+ out($f, "\t\t\toffset += 1;\n");
+ out($f, "\t\t} else if (op->extended_value == ZEND_ASSIGN_OBJ) {\n");
+ out($f, "\t\t\toffset += 2;\n");
+ out($f, "\t\t}\n");
+ out($f, "\t}\n");
+ }
+ out($f, "\treturn zend_opcode_handlers[(spec & SPEC_START_MASK) + offset];\n");
+ }
+ out($f, "}\n\n");
out($f, "static const void *zend_vm_get_opcode_handler(zend_uchar opcode, const zend_op* op)\n");
out($f, "{\n");
if (!ZEND_VM_SPEC) {
- out($f, "\treturn zend_opcode_handlers[opcode];\n");
+ out($f, "\treturn zend_vm_get_opcode_handler_ex(opcode, op);\n");
} else {
- out($f, "\t\tstatic const int zend_vm_decode[] = {\n");
- out($f, "\t\t\t_UNUSED_CODE, /* 0 */\n");
- out($f, "\t\t\t_CONST_CODE, /* 1 = IS_CONST */\n");
- out($f, "\t\t\t_TMP_CODE, /* 2 = IS_TMP_VAR */\n");
- out($f, "\t\t\t_UNUSED_CODE, /* 3 */\n");
- out($f, "\t\t\t_VAR_CODE, /* 4 = IS_VAR */\n");
- out($f, "\t\t\t_UNUSED_CODE, /* 5 */\n");
- out($f, "\t\t\t_UNUSED_CODE, /* 6 */\n");
- out($f, "\t\t\t_UNUSED_CODE, /* 7 */\n");
- out($f, "\t\t\t_UNUSED_CODE, /* 8 = IS_UNUSED */\n");
- out($f, "\t\t\t_UNUSED_CODE, /* 9 */\n");
- out($f, "\t\t\t_UNUSED_CODE, /* 10 */\n");
- out($f, "\t\t\t_UNUSED_CODE, /* 11 */\n");
- out($f, "\t\t\t_UNUSED_CODE, /* 12 */\n");
- out($f, "\t\t\t_UNUSED_CODE, /* 13 */\n");
- out($f, "\t\t\t_UNUSED_CODE, /* 14 */\n");
- out($f, "\t\t\t_UNUSED_CODE, /* 15 */\n");
- out($f, "\t\t\t_CV_CODE /* 16 = IS_CV */\n");
- out($f, "\t\t};\n");
- out($f, "\t\treturn zend_opcode_handlers[opcode * 25 + zend_vm_decode[op->op1_type] * 5 + zend_vm_decode[op->op2_type]];\n");
+ out($f, "\treturn zend_vm_get_opcode_handler_ex(zend_spec_handlers[opcode], op);\n");
}
out($f, "}\n\n");
@@ -1608,6 +2323,55 @@ function gen_vm($def, $skel) {
out($f, "\top->handler = zend_vm_get_opcode_handler(zend_user_opcodes[op->opcode], op);\n");
out($f, "}\n\n");
+ // Generate zend_vm_set_opcode_handler_ex() function
+ out($f, "ZEND_API void zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t op1_info, uint32_t op2_info, uint32_t res_info)\n");
+ out($f, "{\n");
+ out($f, "\tzend_uchar opcode = zend_user_opcodes[op->opcode];\n");
+ if (!ZEND_VM_SPEC) {
+ out($f, "\top->handler = zend_vm_get_opcode_handler_ex(opcode, op);\n");
+ } else {
+ out($f, "\tuint32_t spec = zend_spec_handlers[opcode];\n");
+ if (isset($used_extra_spec["TYPE"])) {
+ out($f, "\tswitch (opcode) {\n");
+ foreach($opcodes as $code => $dsc) {
+ if (isset($dsc['type_spec'])) {
+ $orig_op = $dsc['op'];
+ out($f, "\t\tcase $orig_op:\n");
+ $first = true;
+ foreach($dsc['type_spec'] as $code => $condition) {
+ if ($first) {
+ out($f, "\t\t\tif ($condition) {\n");
+ $first = false;
+ } else {
+ out($f, "\t\t\t} else if ($condition) {\n");
+ }
+ $spec_dsc = $opcodes[$code];
+ if (isset($spec_dsc["spec"]["NO_CONST_CONST"])) {
+ out($f, "\t\t\t\tif (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {\n");
+ out($f, "\t\t\t\t\tbreak;\n");
+ out($f, "\t\t\t\t}\n");
+ }
+ out($f, "\t\t\t\tspec = ${spec_dsc['spec_code']};\n");
+ if (isset($spec_dsc["spec"]["COMMUTATIVE"])) {
+ out($f, "\t\t\t\tif (op->op1_type > op->op2_type) {\n");
+ out($f, "\t\t\t\t\tzend_swap_operands(op);\n");
+ out($f, "\t\t\t\t}\n");
+ }
+ }
+ if (!$first) {
+ out($f, "\t\t\t}\n");
+ }
+ out($f, "\t\t\tbreak;\n");
+ }
+ }
+ out($f, "\t\tdefault:\n");
+ out($f, "\t\t\tbreak;\n");
+ out($f, "\t}\n");
+ }
+ out($f, "\top->handler = zend_vm_get_opcode_handler_ex(spec, op);\n");
+ }
+ out($f, "}\n\n");
+
// Generate zend_vm_call_opcode_handler() function
if (ZEND_VM_KIND == ZEND_VM_KIND_CALL) {
out($f, "ZEND_API int zend_vm_call_opcode_handler(zend_execute_data* ex)\n");
diff --git a/Zend/zend_vm_opcodes.c b/Zend/zend_vm_opcodes.c
index e42c2e22f5..7e159d4100 100644
--- a/Zend/zend_vm_opcodes.c
+++ b/Zend/zend_vm_opcodes.c
@@ -21,7 +21,7 @@
#include <stdio.h>
#include <zend.h>
-const char *zend_vm_opcodes_map[173] = {
+static const char *zend_vm_opcodes_names[187] = {
"ZEND_NOP",
"ZEND_ADD",
"ZEND_SUB",
@@ -63,7 +63,7 @@ const char *zend_vm_opcodes_map[173] = {
"ZEND_ASSIGN",
"ZEND_ASSIGN_REF",
"ZEND_ECHO",
- NULL,
+ "ZEND_GENERATOR_CREATE",
"ZEND_JMP",
"ZEND_JMPZ",
"ZEND_JMPNZ",
@@ -72,7 +72,7 @@ const char *zend_vm_opcodes_map[173] = {
"ZEND_JMPNZ_EX",
"ZEND_CASE",
NULL,
- NULL,
+ "ZEND_SEND_VAR_NO_REF_EX",
NULL,
"ZEND_BOOL",
"ZEND_FAST_CONCAT",
@@ -195,8 +195,215 @@ const char *zend_vm_opcodes_map[173] = {
"ZEND_SPACESHIP",
"ZEND_DECLARE_ANON_CLASS",
"ZEND_DECLARE_ANON_INHERITED_CLASS",
+ "ZEND_FETCH_STATIC_PROP_R",
+ "ZEND_FETCH_STATIC_PROP_W",
+ "ZEND_FETCH_STATIC_PROP_RW",
+ "ZEND_FETCH_STATIC_PROP_IS",
+ "ZEND_FETCH_STATIC_PROP_FUNC_ARG",
+ "ZEND_FETCH_STATIC_PROP_UNSET",
+ "ZEND_UNSET_STATIC_PROP",
+ "ZEND_ISSET_ISEMPTY_STATIC_PROP",
+ "ZEND_FETCH_CLASS_CONSTANT",
+ "ZEND_BIND_LEXICAL",
+ "ZEND_BIND_STATIC",
+ "ZEND_FETCH_THIS",
+ NULL,
+ "ZEND_ISSET_ISEMPTY_THIS",
+};
+
+static uint32_t zend_vm_opcodes_flags[187] = {
+ 0x00000000,
+ 0x00000707,
+ 0x00000707,
+ 0x00000707,
+ 0x00000707,
+ 0x00000707,
+ 0x00000707,
+ 0x00000707,
+ 0x00000707,
+ 0x00000707,
+ 0x00000707,
+ 0x00000707,
+ 0x00000007,
+ 0x00000007,
+ 0x00000707,
+ 0x00000303,
+ 0x00000303,
+ 0x00000707,
+ 0x00000707,
+ 0x00000707,
+ 0x00000707,
+ 0x07000003,
+ 0x00000003,
+ 0x04006751,
+ 0x04006751,
+ 0x04006751,
+ 0x04006751,
+ 0x04006751,
+ 0x04006751,
+ 0x04006751,
+ 0x04006751,
+ 0x04006751,
+ 0x04006751,
+ 0x04006751,
+ 0x00000001,
+ 0x00000001,
+ 0x00000001,
+ 0x00000001,
+ 0x00000301,
+ 0x0b000101,
+ 0x00000007,
+ 0x00000000,
+ 0x00000020,
+ 0x00002007,
+ 0x00002007,
+ 0x03002007,
+ 0x00002007,
+ 0x00002007,
+ 0x00000707,
+ 0x00000000,
+ 0x00001001,
+ 0x00000000,
+ 0x00000007,
+ 0x00000707,
+ 0x01000701,
+ 0x01000701,
+ 0x01000701,
+ 0x00000000,
+ 0x00000001,
+ 0x01000300,
+ 0x00000000,
+ 0x01000310,
+ 0x00000003,
+ 0x00000010,
+ 0x00000310,
+ 0x00001007,
+ 0x00001001,
+ 0x00001001,
+ 0x01000073,
+ 0x01000300,
+ 0x00004005,
+ 0x00186703,
+ 0x00106703,
+ 0x08000007,
+ 0x00030107,
+ 0x00000701,
+ 0x00000751,
+ 0x00002003,
+ 0x03000001,
+ 0x00000007,
+ 0x00010107,
+ 0x00000707,
+ 0x00000753,
+ 0x00010107,
+ 0x00006701,
+ 0x00000751,
+ 0x00010107,
+ 0x00006701,
+ 0x00000751,
+ 0x00010107,
+ 0x00000707,
+ 0x00000757,
+ 0x00050107,
+ 0x01006703,
+ 0x01000753,
+ 0x00010107,
+ 0x00000701,
+ 0x00000751,
+ 0x00000707,
+ 0x06000301,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x01000000,
+ 0x00001001,
+ 0x03000103,
+ 0x00000003,
+ 0x05000700,
+ 0x00000057,
+ 0x0b000003,
+ 0x01000757,
+ 0x01008773,
+ 0x00030107,
+ 0x00020707,
+ 0x00001003,
+ 0x00001001,
+ 0x01000703,
+ 0x00000000,
+ 0x00001001,
+ 0x00000007,
+ 0x00000003,
+ 0x07000003,
+ 0x00000103,
+ 0x00002003,
+ 0x03000001,
+ 0x00004005,
+ 0x01000700,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000751,
+ 0x00000751,
+ 0x00000751,
+ 0x00000751,
+ 0x00000751,
+ 0x00000000,
+ 0x00007305,
+ 0x00000000,
+ 0x00000100,
+ 0x00000000,
+ 0x00000003,
+ 0x00000303,
+ 0x00000300,
+ 0x00000100,
+ 0x00000000,
+ 0x00006701,
+ 0x00020757,
+ 0x00000000,
+ 0x00000000,
+ 0x00002000,
+ 0x00002003,
+ 0x00000103,
+ 0x00000000,
+ 0x00000000,
+ 0x00000101,
+ 0x05000000,
+ 0x00000000,
+ 0x00000000,
+ 0x0b000303,
+ 0x00000003,
+ 0x00000020,
+ 0x00003000,
+ 0x00000010,
+ 0x00000000,
+ 0x00000707,
+ 0x04006751,
+ 0x00000301,
+ 0x00002003,
+ 0x00000707,
+ 0x03000000,
+ 0x03000100,
+ 0x00007307,
+ 0x00007307,
+ 0x00007307,
+ 0x00007307,
+ 0x01007307,
+ 0x00007307,
+ 0x00007307,
+ 0x00027307,
+ 0x00000373,
+ 0x00100101,
+ 0x00100301,
+ 0x00000101,
+ 0x00000000,
+ 0x00000101,
};
ZEND_API const char* zend_get_opcode_name(zend_uchar opcode) {
- return zend_vm_opcodes_map[opcode];
+ return zend_vm_opcodes_names[opcode];
+}
+ZEND_API uint32_t zend_get_opcode_flags(zend_uchar opcode) {
+ return zend_vm_opcodes_flags[opcode];
}
diff --git a/Zend/zend_vm_opcodes.h b/Zend/zend_vm_opcodes.h
index 9ba87e58be..b6d51b668c 100644
--- a/Zend/zend_vm_opcodes.h
+++ b/Zend/zend_vm_opcodes.h
@@ -28,9 +28,42 @@
#define ZEND_VM_KIND_GOTO 3
#define ZEND_VM_KIND ZEND_VM_KIND_CALL
+#define ZEND_VM_OP_SPEC 0x00000001
+#define ZEND_VM_OP_CONST 0x00000002
+#define ZEND_VM_OP_TMPVAR 0x00000004
+#define ZEND_VM_OP_TMPVARCV 0x00000008
+#define ZEND_VM_OP_MASK 0x000000f0
+#define ZEND_VM_OP_NUM 0x00000010
+#define ZEND_VM_OP_JMP_ADDR 0x00000020
+#define ZEND_VM_OP_TRY_CATCH 0x00000030
+#define ZEND_VM_OP_LIVE_RANGE 0x00000040
+#define ZEND_VM_OP_THIS 0x00000050
+#define ZEND_VM_OP_NEXT 0x00000060
+#define ZEND_VM_OP_CLASS_FETCH 0x00000070
+#define ZEND_VM_OP_CONSTRUCTOR 0x00000080
+#define ZEND_VM_EXT_VAR_FETCH 0x00010000
+#define ZEND_VM_EXT_ISSET 0x00020000
+#define ZEND_VM_EXT_ARG_NUM 0x00040000
+#define ZEND_VM_EXT_ARRAY_INIT 0x00080000
+#define ZEND_VM_EXT_REF 0x00100000
+#define ZEND_VM_EXT_MASK 0x0f000000
+#define ZEND_VM_EXT_NUM 0x01000000
+#define ZEND_VM_EXT_JMP_ADDR 0x03000000
+#define ZEND_VM_EXT_DIM_OBJ 0x04000000
+#define ZEND_VM_EXT_CLASS_FETCH 0x05000000
+#define ZEND_VM_EXT_CONST_FETCH 0x06000000
+#define ZEND_VM_EXT_TYPE 0x07000000
+#define ZEND_VM_EXT_EVAL 0x08000000
+#define ZEND_VM_EXT_SRC 0x0b000000
+#define ZEND_VM_NO_CONST_CONST 0x40000000
+#define ZEND_VM_COMMUTATIVE 0x80000000
+#define ZEND_VM_OP1_FLAGS(flags) (flags & 0xff)
+#define ZEND_VM_OP2_FLAGS(flags) ((flags >> 8) & 0xff)
+
BEGIN_EXTERN_C()
ZEND_API const char *zend_get_opcode_name(zend_uchar opcode);
+ZEND_API uint32_t zend_get_opcode_flags(zend_uchar opcode);
END_EXTERN_C()
@@ -75,6 +108,7 @@ END_EXTERN_C()
#define ZEND_ASSIGN 38
#define ZEND_ASSIGN_REF 39
#define ZEND_ECHO 40
+#define ZEND_GENERATOR_CREATE 41
#define ZEND_JMP 42
#define ZEND_JMPZ 43
#define ZEND_JMPNZ 44
@@ -82,6 +116,7 @@ END_EXTERN_C()
#define ZEND_JMPZ_EX 46
#define ZEND_JMPNZ_EX 47
#define ZEND_CASE 48
+#define ZEND_SEND_VAR_NO_REF_EX 50
#define ZEND_BOOL 52
#define ZEND_FAST_CONCAT 53
#define ZEND_ROPE_INIT 54
@@ -202,5 +237,20 @@ END_EXTERN_C()
#define ZEND_SPACESHIP 170
#define ZEND_DECLARE_ANON_CLASS 171
#define ZEND_DECLARE_ANON_INHERITED_CLASS 172
+#define ZEND_FETCH_STATIC_PROP_R 173
+#define ZEND_FETCH_STATIC_PROP_W 174
+#define ZEND_FETCH_STATIC_PROP_RW 175
+#define ZEND_FETCH_STATIC_PROP_IS 176
+#define ZEND_FETCH_STATIC_PROP_FUNC_ARG 177
+#define ZEND_FETCH_STATIC_PROP_UNSET 178
+#define ZEND_UNSET_STATIC_PROP 179
+#define ZEND_ISSET_ISEMPTY_STATIC_PROP 180
+#define ZEND_FETCH_CLASS_CONSTANT 181
+#define ZEND_BIND_LEXICAL 182
+#define ZEND_BIND_STATIC 183
+#define ZEND_FETCH_THIS 184
+#define ZEND_ISSET_ISEMPTY_THIS 186
+
+#define ZEND_VM_LAST_OPCODE 186
#endif
diff --git a/acinclude.m4 b/acinclude.m4
index 28506b6893..db146aaf2f 100644
--- a/acinclude.m4
+++ b/acinclude.m4
@@ -2672,14 +2672,10 @@ AC_DEFUN([PHP_CONFIG_NICE],[
EOF
- clean_configure_args=$ac_configure_args
for var in CFLAGS CXXFLAGS CPPFLAGS LDFLAGS EXTRA_LDFLAGS_PROGRAM LIBS CC CXX; do
eval val=\$$var
if test -n "$val"; then
echo "$var='$val' \\" >> $1
- if test `expr "X$ac_configure_args" : ".*${var}.*"` != 0; then
- clean_configure_args=$(echo $clean_configure_args | sed -e "s/'$var=$val'//")
- fi
fi
done
@@ -2689,21 +2685,27 @@ EOF
else
CONFIGURE_COMMAND="$CONFIGURE_COMMAND [$]0"
fi
-
- for arg in $clean_configure_args; do
- if test `expr -- $arg : "'.*"` = 0; then
- if test `expr -- $arg : "-.*"` = 0 && test `expr -- $arg : ".*=.*"` = 0; then
- continue;
- fi
- echo "'[$]arg' \\" >> $1
- CONFIGURE_OPTIONS="$CONFIGURE_OPTIONS '[$]arg'"
- else
- if test `expr -- $arg : "'-.*"` = 0 && test `expr -- $arg : "'.*=.*"` = 0; then
- continue;
- fi
- echo "[$]arg \\" >> $1
- CONFIGURE_OPTIONS="$CONFIGURE_OPTIONS [$]arg"
- fi
+ CONFIGURE_ARGS="$ac_configure_args"
+ while test "X$CONFIGURE_ARGS" != "X";
+ do
+ if CURRENT_ARG=`expr "X$CONFIGURE_ARGS" : "X *\('[[^']]*'\)"`
+ then
+ CONFIGURE_ARGS=`expr "X$CONFIGURE_ARGS" : "X *'[[^']]*' \(.*\)"`
+ elif CURRENT_ARG=`expr "X$CONFIGURE_ARGS" : "X *\([[^ ]]*\)"`
+ then
+ CONFIGURE_ARGS=`expr "X$CONFIGURE_ARGS" : "X *[[^ ]]* \(.*\)"`
+ CURRENT_ARG="'$CURRENT_ARG'"
+ else
+ break
+ fi
+ for var in CFLAGS CXXFLAGS CPPFLAGS LDFLAGS EXTRA_LDFLAGS_PROGRAM LIBS CC CXX; do
+ if test `expr "X$CURRENT_ARG" : "X.*${var}.*"` != 0;
+ then
+ continue 2
+ fi
+ done
+ $as_echo "$CURRENT_ARG \\" >>$1
+ CONFIGURE_OPTIONS="$CONFIGURE_OPTIONS $CURRENT_ARG"
done
echo '"[$]@"' >> $1
chmod +x $1
diff --git a/configure.in b/configure.in
index 142cf7f77b..299dbb55fb 100644
--- a/configure.in
+++ b/configure.in
@@ -118,8 +118,8 @@ int zend_sprintf(char *buffer, const char *format, ...);
])
PHP_MAJOR_VERSION=7
-PHP_MINOR_VERSION=0
-PHP_RELEASE_VERSION=9
+PHP_MINOR_VERSION=1
+PHP_RELEASE_VERSION=0
PHP_EXTRA_VERSION="-dev"
PHP_VERSION="$PHP_MAJOR_VERSION.$PHP_MINOR_VERSION.$PHP_RELEASE_VERSION$PHP_EXTRA_VERSION"
PHP_VERSION_ID=`expr [$]PHP_MAJOR_VERSION \* 10000 + [$]PHP_MINOR_VERSION \* 100 + [$]PHP_RELEASE_VERSION`
diff --git a/ext/bz2/tests/bug72447.phpt b/ext/bz2/tests/bug72447.phpt
new file mode 100644
index 0000000000..0df4cb177b
--- /dev/null
+++ b/ext/bz2/tests/bug72447.phpt
@@ -0,0 +1,15 @@
+--TEST--
+Bug #72447: Type Confusion in php_bz2_filter_create()
+--SKIPIF--
+<?php if (!extension_loaded("bz2")) print "skip"; ?>
+--FILE--
+<?php
+$input = "AAAAAAAA";
+$param = array('blocks' => $input);
+
+$fp = fopen('testfile', 'w');
+stream_filter_append($fp, 'bzip2.compress', STREAM_FILTER_WRITE, $param);
+fclose($fp);
+?>
+--EXPECTF--
+Warning: stream_filter_append(): Invalid parameter given for number of blocks to allocate. (0) in %s/bug72447.php on line %d
diff --git a/ext/curl/interface.c b/ext/curl/interface.c
index 6a616411ef..072c23e48d 100644
--- a/ext/curl/interface.c
+++ b/ext/curl/interface.c
@@ -1449,14 +1449,12 @@ static size_t curl_write(char *data, size_t size, size_t nmemb, void *ctx)
ZVAL_STRINGL(&argv[1], data, length);
fci.size = sizeof(fci);
- fci.function_table = EG(function_table);
fci.object = NULL;
ZVAL_COPY_VALUE(&fci.function_name, &t->func_name);
fci.retval = &retval;
fci.param_count = 2;
fci.params = argv;
fci.no_separation = 0;
- fci.symbol_table = NULL;
ch->in_callback = 1;
error = zend_call_function(&fci, &t->fci_cache);
@@ -1500,14 +1498,12 @@ static int curl_fnmatch(void *ctx, const char *pattern, const char *string)
ZVAL_STRING(&argv[2], string);
fci.size = sizeof(fci);
- fci.function_table = EG(function_table);
ZVAL_COPY_VALUE(&fci.function_name, &t->func_name);
fci.object = NULL;
fci.retval = &retval;
fci.param_count = 3;
fci.params = argv;
fci.no_separation = 0;
- fci.symbol_table = NULL;
ch->in_callback = 1;
error = zend_call_function(&fci, &t->fci_cache);
@@ -1557,14 +1553,12 @@ static size_t curl_progress(void *clientp, double dltotal, double dlnow, double
ZVAL_LONG(&argv[4], (zend_long)ulnow);
fci.size = sizeof(fci);
- fci.function_table = EG(function_table);
ZVAL_COPY_VALUE(&fci.function_name, &t->func_name);
fci.object = NULL;
fci.retval = &retval;
fci.param_count = 5;
fci.params = argv;
fci.no_separation = 0;
- fci.symbol_table = NULL;
ch->in_callback = 1;
error = zend_call_function(&fci, &t->fci_cache);
@@ -1620,14 +1614,12 @@ static size_t curl_read(char *data, size_t size, size_t nmemb, void *ctx)
ZVAL_LONG(&argv[2], (int)size * nmemb);
fci.size = sizeof(fci);
- fci.function_table = EG(function_table);
ZVAL_COPY_VALUE(&fci.function_name, &t->func_name);
fci.object = NULL;
fci.retval = &retval;
fci.param_count = 3;
fci.params = argv;
fci.no_separation = 0;
- fci.symbol_table = NULL;
ch->in_callback = 1;
error = zend_call_function(&fci, &t->fci_cache);
@@ -1688,9 +1680,7 @@ static size_t curl_write_header(char *data, size_t size, size_t nmemb, void *ctx
ZVAL_STRINGL(&argv[1], data, length);
fci.size = sizeof(fci);
- fci.function_table = EG(function_table);
ZVAL_COPY_VALUE(&fci.function_name, &t->func_name);
- fci.symbol_table = NULL;
fci.object = NULL;
fci.retval = &retval;
fci.param_count = 2;
@@ -3364,9 +3354,7 @@ PHP_FUNCTION(curl_close)
return;
}
- if (Z_REFCOUNT_P(zid) <= 2) {
- zend_list_close(Z_RES_P(zid));
- }
+ zend_list_close(Z_RES_P(zid));
}
/* }}} */
diff --git a/ext/curl/tests/bug72202.phpt b/ext/curl/tests/bug72202.phpt
new file mode 100644
index 0000000000..63138d9ba6
--- /dev/null
+++ b/ext/curl/tests/bug72202.phpt
@@ -0,0 +1,36 @@
+--TEST--
+Bug #72202 (curl_close doesn't close cURL handle)
+--SKIPIF--
+<?php
+if (!extension_loaded("curl")) {
+ exit("skip curl extension not loaded");
+}
+if (getenv("SKIP_ONLINE_TESTS")) {
+ die("skip online test");
+}
+?>
+--FILE--
+<?php
+$a = fopen(__FILE__, "r");
+$b = $a;
+var_dump($a, $b);
+fclose($a);
+var_dump($a, $b);
+unset($a, $b);
+
+$a = curl_init();
+$b = $a;
+var_dump($a, $b);
+curl_close($a);
+var_dump($a, $b);
+unset($a, $b);
+?>
+--EXPECTF--
+resource(%d) of type (stream)
+resource(%d) of type (stream)
+resource(%d) of type (Unknown)
+resource(%d) of type (Unknown)
+resource(%d) of type (curl)
+resource(%d) of type (curl)
+resource(%d) of type (Unknown)
+resource(%d) of type (Unknown)
diff --git a/ext/date/lib/timezonedb.h b/ext/date/lib/timezonedb.h
index 232053c4e7..bd8f4d7d9d 100644
--- a/ext/date/lib/timezonedb.h
+++ b/ext/date/lib/timezonedb.h
@@ -1,5 +1,5 @@
/* This is a generated file, do not modify */
-const timelib_tzdb_index_entry timezonedb_idx_builtin[587] = {
+const timelib_tzdb_index_entry timezonedb_idx_builtin[589] = {
#ifdef TIMELIB_SUPPORTS_V2DATA
# define FOR_V2(v2,v1) v2
#else
@@ -19,584 +19,586 @@ const timelib_tzdb_index_entry timezonedb_idx_builtin[587] = {
{ "Africa/Brazzaville" , FOR_V2(0x000E39, 0x00065A) },
{ "Africa/Bujumbura" , FOR_V2(0x000EF0, 0x0006B4) },
{ "Africa/Cairo" , FOR_V2(0x000FA7, 0x00070E) },
- { "Africa/Casablanca" , FOR_V2(0x001767, 0x0009EE) },
- { "Africa/Ceuta" , FOR_V2(0x001DD0, 0x000C53) },
- { "Africa/Conakry" , FOR_V2(0x002605, 0x000F6A) },
- { "Africa/Dakar" , FOR_V2(0x0026BB, 0x000FC4) },
- { "Africa/Dar_es_Salaam" , FOR_V2(0x002771, 0x00101E) },
- { "Africa/Djibouti" , FOR_V2(0x002898, 0x0010AA) },
- { "Africa/Douala" , FOR_V2(0x0029BF, 0x001136) },
- { "Africa/El_Aaiun" , FOR_V2(0x002A76, 0x001190) },
- { "Africa/Freetown" , FOR_V2(0x003035, 0x0013B6) },
- { "Africa/Gaborone" , FOR_V2(0x0030EB, 0x001410) },
- { "Africa/Harare" , FOR_V2(0x0031A2, 0x00146A) },
- { "Africa/Johannesburg" , FOR_V2(0x003259, 0x0014C4) },
- { "Africa/Juba" , FOR_V2(0x003374, 0x001543) },
- { "Africa/Kampala" , FOR_V2(0x00362B, 0x00165B) },
- { "Africa/Khartoum" , FOR_V2(0x003752, 0x0016E7) },
- { "Africa/Kigali" , FOR_V2(0x003A09, 0x0017FF) },
- { "Africa/Kinshasa" , FOR_V2(0x003AC0, 0x001859) },
- { "Africa/Lagos" , FOR_V2(0x003B90, 0x0018CC) },
- { "Africa/Libreville" , FOR_V2(0x003C47, 0x001926) },
- { "Africa/Lome" , FOR_V2(0x003CFE, 0x001980) },
- { "Africa/Luanda" , FOR_V2(0x003DB4, 0x0019DA) },
- { "Africa/Lubumbashi" , FOR_V2(0x003E6B, 0x001A34) },
- { "Africa/Lusaka" , FOR_V2(0x003F3B, 0x001AA7) },
- { "Africa/Malabo" , FOR_V2(0x003FF2, 0x001B01) },
- { "Africa/Maputo" , FOR_V2(0x0040A9, 0x001B5B) },
- { "Africa/Maseru" , FOR_V2(0x004160, 0x001BB5) },
- { "Africa/Mbabane" , FOR_V2(0x00427B, 0x001C34) },
- { "Africa/Mogadishu" , FOR_V2(0x004396, 0x001CB3) },
- { "Africa/Monrovia" , FOR_V2(0x0044BD, 0x001D3F) },
- { "Africa/Nairobi" , FOR_V2(0x0045BA, 0x001DB6) },
- { "Africa/Ndjamena" , FOR_V2(0x0046E1, 0x001E42) },
- { "Africa/Niamey" , FOR_V2(0x0047CE, 0x001EB3) },
- { "Africa/Nouakchott" , FOR_V2(0x004885, 0x001F0D) },
- { "Africa/Ouagadougou" , FOR_V2(0x00493B, 0x001F67) },
- { "Africa/Porto-Novo" , FOR_V2(0x0049F1, 0x001FC1) },
- { "Africa/Sao_Tome" , FOR_V2(0x004AA8, 0x00201B) },
- { "Africa/Timbuktu" , FOR_V2(0x004B5E, 0x002075) },
- { "Africa/Tripoli" , FOR_V2(0x004C14, 0x0020CF) },
- { "Africa/Tunis" , FOR_V2(0x004EAF, 0x0021DD) },
- { "Africa/Windhoek" , FOR_V2(0x005181, 0x002300) },
- { "America/Adak" , FOR_V2(0x0057BB, 0x002558) },
- { "America/Anchorage" , FOR_V2(0x006114, 0x0028D9) },
- { "America/Anguilla" , FOR_V2(0x006A83, 0x002C66) },
- { "America/Antigua" , FOR_V2(0x006B39, 0x002CC0) },
- { "America/Araguaina" , FOR_V2(0x006BEF, 0x002D1A) },
- { "America/Argentina/Buenos_Aires" , FOR_V2(0x006F84, 0x002E84) },
- { "America/Argentina/Catamarca" , FOR_V2(0x0073E4, 0x003043) },
- { "America/Argentina/ComodRivadavia" , FOR_V2(0x007874, 0x00321D) },
- { "America/Argentina/Cordoba" , FOR_V2(0x007CE9, 0x0033DC) },
- { "America/Argentina/Jujuy" , FOR_V2(0x008194, 0x0035D1) },
- { "America/Argentina/La_Rioja" , FOR_V2(0x008623, 0x0037A6) },
- { "America/Argentina/Mendoza" , FOR_V2(0x008AB3, 0x003977) },
- { "America/Argentina/Rio_Gallegos" , FOR_V2(0x008F60, 0x003B58) },
- { "America/Argentina/Salta" , FOR_V2(0x0093E4, 0x003D26) },
- { "America/Argentina/San_Juan" , FOR_V2(0x009853, 0x003EF1) },
- { "America/Argentina/San_Luis" , FOR_V2(0x009CE3, 0x0040C2) },
- { "America/Argentina/Tucuman" , FOR_V2(0x00A18F, 0x0042A1) },
- { "America/Argentina/Ushuaia" , FOR_V2(0x00A62C, 0x004476) },
- { "America/Aruba" , FOR_V2(0x00AAB6, 0x00464A) },
- { "America/Asuncion" , FOR_V2(0x00AB92, 0x0046B5) },
- { "America/Atikokan" , FOR_V2(0x00B3AC, 0x0049B3) },
- { "America/Atka" , FOR_V2(0x00B532, 0x004A77) },
- { "America/Bahia" , FOR_V2(0x00BE7B, 0x004DE8) },
- { "America/Bahia_Banderas" , FOR_V2(0x00C298, 0x004F80) },
- { "America/Barbados" , FOR_V2(0x00C8F8, 0x0051FE) },
- { "America/Belem" , FOR_V2(0x00CA5C, 0x00529D) },
- { "America/Belize" , FOR_V2(0x00CCC6, 0x0053A2) },
- { "America/Blanc-Sablon" , FOR_V2(0x00D0A2, 0x005523) },
- { "America/Boa_Vista" , FOR_V2(0x00D1FD, 0x0055D1) },
- { "America/Bogota" , FOR_V2(0x00D494, 0x0056DF) },
- { "America/Boise" , FOR_V2(0x00D5A1, 0x00575C) },
- { "America/Buenos_Aires" , FOR_V2(0x00DF30, 0x005AFB) },
- { "America/Cambridge_Bay" , FOR_V2(0x00E37B, 0x005CA5) },
- { "America/Campo_Grande" , FOR_V2(0x00EBCD, 0x005FDA) },
- { "America/Cancun" , FOR_V2(0x00F3CA, 0x0062CE) },
- { "America/Caracas" , FOR_V2(0x00F72A, 0x00643D) },
- { "America/Catamarca" , FOR_V2(0x00F840, 0x0064BD) },
- { "America/Cayenne" , FOR_V2(0x00FCB5, 0x00667C) },
- { "America/Cayman" , FOR_V2(0x00FD89, 0x0066E3) },
- { "America/Chicago" , FOR_V2(0x00FE60, 0x006749) },
- { "America/Chihuahua" , FOR_V2(0x010C81, 0x006C81) },
- { "America/Coral_Harbour" , FOR_V2(0x0112A5, 0x006EEA) },
- { "America/Cordoba" , FOR_V2(0x01140A, 0x006F8D) },
- { "America/Costa_Rica" , FOR_V2(0x01187F, 0x00714C) },
- { "America/Creston" , FOR_V2(0x0119E0, 0x0071E7) },
- { "America/Cuiaba" , FOR_V2(0x011AE7, 0x00726C) },
- { "America/Curacao" , FOR_V2(0x0122C1, 0x00754F) },
- { "America/Danmarkshavn" , FOR_V2(0x01239D, 0x0075BA) },
- { "America/Dawson" , FOR_V2(0x01268D, 0x0076FC) },
- { "America/Dawson_Creek" , FOR_V2(0x012EDD, 0x007A27) },
- { "America/Denver" , FOR_V2(0x01332C, 0x007BE9) },
- { "America/Detroit" , FOR_V2(0x013CE2, 0x007F88) },
- { "America/Dominica" , FOR_V2(0x0145AF, 0x0082DD) },
- { "America/Edmonton" , FOR_V2(0x014665, 0x008337) },
- { "America/Eirunepe" , FOR_V2(0x014FF0, 0x0086CF) },
- { "America/El_Salvador" , FOR_V2(0x0152B7, 0x0087F9) },
- { "America/Ensenada" , FOR_V2(0x0153BD, 0x008873) },
- { "America/Fort_Nelson" , FOR_V2(0x015CFD, 0x008BE1) },
- { "America/Fort_Wayne" , FOR_V2(0x0165E6, 0x008F40) },
- { "America/Fortaleza" , FOR_V2(0x016C7D, 0x0091BB) },
- { "America/Glace_Bay" , FOR_V2(0x016F87, 0x009306) },
- { "America/Godthab" , FOR_V2(0x01784C, 0x009656) },
- { "America/Goose_Bay" , FOR_V2(0x017FC3, 0x009927) },
- { "America/Grand_Turk" , FOR_V2(0x018C82, 0x009DF4) },
- { "America/Grenada" , FOR_V2(0x019195, 0x009FE4) },
- { "America/Guadeloupe" , FOR_V2(0x01924B, 0x00A03E) },
- { "America/Guatemala" , FOR_V2(0x019301, 0x00A098) },
- { "America/Guayaquil" , FOR_V2(0x01943F, 0x00A126) },
- { "America/Guyana" , FOR_V2(0x019528, 0x00A19E) },
- { "America/Halifax" , FOR_V2(0x019642, 0x00A224) },
- { "America/Havana" , FOR_V2(0x01A3DA, 0x00A72F) },
- { "America/Hermosillo" , FOR_V2(0x01AD6B, 0x00AAB3) },
- { "America/Indiana/Indianapolis" , FOR_V2(0x01AF5C, 0x00AB9E) },
- { "America/Indiana/Knox" , FOR_V2(0x01B60C, 0x00AE32) },
- { "America/Indiana/Marengo" , FOR_V2(0x01BFB2, 0x00B1D1) },
- { "America/Indiana/Petersburg" , FOR_V2(0x01C698, 0x00B477) },
- { "America/Indiana/Tell_City" , FOR_V2(0x01CE30, 0x00B75A) },
- { "America/Indiana/Vevay" , FOR_V2(0x01D517, 0x00BA03) },
- { "America/Indiana/Vincennes" , FOR_V2(0x01DACC, 0x00BC3E) },
- { "America/Indiana/Winamac" , FOR_V2(0x01E19B, 0x00BEDF) },
- { "America/Indianapolis" , FOR_V2(0x01E8B8, 0x00C198) },
- { "America/Inuvik" , FOR_V2(0x01EF4F, 0x00C413) },
- { "America/Iqaluit" , FOR_V2(0x01F6F7, 0x00C6F9) },
- { "America/Jamaica" , FOR_V2(0x01FF1F, 0x00CA22) },
- { "America/Jujuy" , FOR_V2(0x020126, 0x00CAF8) },
- { "America/Juneau" , FOR_V2(0x0205AB, 0x00CCC3) },
- { "America/Kentucky/Louisville" , FOR_V2(0x020F05, 0x00D048) },
- { "America/Kentucky/Monticello" , FOR_V2(0x021A0C, 0x00D46C) },
- { "America/Knox_IN" , FOR_V2(0x022365, 0x00D7F0) },
- { "America/Kralendijk" , FOR_V2(0x022CF6, 0x00DB7A) },
- { "America/La_Paz" , FOR_V2(0x022DD2, 0x00DBE5) },
- { "America/Lima" , FOR_V2(0x022ED1, 0x00DC5D) },
- { "America/Los_Angeles" , FOR_V2(0x02307E, 0x00DD12) },
- { "America/Louisville" , FOR_V2(0x023BAE, 0x00E12F) },
- { "America/Lower_Princes" , FOR_V2(0x024697, 0x00E535) },
- { "America/Maceio" , FOR_V2(0x024773, 0x00E5A0) },
- { "America/Managua" , FOR_V2(0x024A83, 0x00E6DF) },
- { "America/Manaus" , FOR_V2(0x024C5E, 0x00E7AB) },
- { "America/Marigot" , FOR_V2(0x024EE1, 0x00E8B7) },
- { "America/Martinique" , FOR_V2(0x024F97, 0x00E911) },
- { "America/Matamoros" , FOR_V2(0x0250A4, 0x00E98E) },
- { "America/Mazatlan" , FOR_V2(0x025676, 0x00EBE0) },
- { "America/Mendoza" , FOR_V2(0x025CD3, 0x00EE67) },
- { "America/Menominee" , FOR_V2(0x026174, 0x00F03C) },
- { "America/Merida" , FOR_V2(0x026A8A, 0x00F3AE) },
- { "America/Metlakatla" , FOR_V2(0x027066, 0x00F5F6) },
- { "America/Mexico_City" , FOR_V2(0x027613, 0x00F826) },
- { "America/Miquelon" , FOR_V2(0x027C7D, 0x00FA95) },
- { "America/Moncton" , FOR_V2(0x02831D, 0x00FD0C) },
- { "America/Monterrey" , FOR_V2(0x028F9C, 0x0101AF) },
- { "America/Montevideo" , FOR_V2(0x029575, 0x010408) },
- { "America/Montreal" , FOR_V2(0x029B68, 0x010652) },
- { "America/Montserrat" , FOR_V2(0x02A923, 0x010B53) },
- { "America/Nassau" , FOR_V2(0x02A9D9, 0x010BAD) },
- { "America/New_York" , FOR_V2(0x02B2D1, 0x010EF7) },
- { "America/Nipigon" , FOR_V2(0x02C0CA, 0x01141B) },
- { "America/Nome" , FOR_V2(0x02C94A, 0x011753) },
- { "America/Noronha" , FOR_V2(0x02D2AB, 0x011AD6) },
- { "America/North_Dakota/Beulah" , FOR_V2(0x02D59F, 0x011C0B) },
- { "America/North_Dakota/Center" , FOR_V2(0x02DF15, 0x011F9A) },
- { "America/North_Dakota/New_Salem" , FOR_V2(0x02E88B, 0x012329) },
- { "America/Ojinaga" , FOR_V2(0x02F207, 0x0126BE) },
- { "America/Panama" , FOR_V2(0x02F82D, 0x012929) },
- { "America/Pangnirtung" , FOR_V2(0x02F904, 0x01298F) },
- { "America/Paramaribo" , FOR_V2(0x030166, 0x012CD1) },
- { "America/Phoenix" , FOR_V2(0x0302A6, 0x012D68) },
- { "America/Port-au-Prince" , FOR_V2(0x030430, 0x012E24) },
- { "America/Port_of_Spain" , FOR_V2(0x03078D, 0x012F79) },
- { "America/Porto_Acre" , FOR_V2(0x030843, 0x012FD3) },
- { "America/Porto_Velho" , FOR_V2(0x030ADF, 0x0130E4) },
- { "America/Puerto_Rico" , FOR_V2(0x030D3F, 0x0131DF) },
- { "America/Rainy_River" , FOR_V2(0x030E4A, 0x01325B) },
- { "America/Rankin_Inlet" , FOR_V2(0x0316CB, 0x013594) },
- { "America/Recife" , FOR_V2(0x031E77, 0x01387F) },
- { "America/Regina" , FOR_V2(0x032165, 0x0139AE) },
- { "America/Resolute" , FOR_V2(0x032568, 0x013B51) },
- { "America/Rio_Branco" , FOR_V2(0x032D15, 0x013E3D) },
- { "America/Rosario" , FOR_V2(0x032FB5, 0x013F52) },
- { "America/Santa_Isabel" , FOR_V2(0x03342A, 0x014111) },
- { "America/Santarem" , FOR_V2(0x033D6A, 0x01447F) },
- { "America/Santiago" , FOR_V2(0x033FF3, 0x01458E) },
- { "America/Santo_Domingo" , FOR_V2(0x0349E4, 0x014944) },
- { "America/Sao_Paulo" , FOR_V2(0x034BD9, 0x014A1B) },
- { "America/Scoresbysund" , FOR_V2(0x0353FA, 0x014D33) },
- { "America/Shiprock" , FOR_V2(0x035BA8, 0x015024) },
- { "America/Sitka" , FOR_V2(0x036549, 0x0153AE) },
- { "America/St_Barthelemy" , FOR_V2(0x036E96, 0x015726) },
- { "America/St_Johns" , FOR_V2(0x036F4C, 0x015780) },
- { "America/St_Kitts" , FOR_V2(0x037DCA, 0x015CE6) },
- { "America/St_Lucia" , FOR_V2(0x037E80, 0x015D40) },
- { "America/St_Thomas" , FOR_V2(0x037F36, 0x015D9A) },
- { "America/St_Vincent" , FOR_V2(0x037FEC, 0x015DF4) },
- { "America/Swift_Current" , FOR_V2(0x0380A2, 0x015E4E) },
- { "America/Tegucigalpa" , FOR_V2(0x0382FE, 0x015F58) },
- { "America/Thule" , FOR_V2(0x038420, 0x015FDC) },
- { "America/Thunder_Bay" , FOR_V2(0x038A32, 0x016226) },
- { "America/Tijuana" , FOR_V2(0x0392FB, 0x016577) },
- { "America/Toronto" , FOR_V2(0x039C5C, 0x016906) },
- { "America/Tortola" , FOR_V2(0x03AA34, 0x016E24) },
- { "America/Vancouver" , FOR_V2(0x03AAEA, 0x016E7E) },
- { "America/Virgin" , FOR_V2(0x03B664, 0x0172C1) },
- { "America/Whitehorse" , FOR_V2(0x03B71A, 0x01731B) },
- { "America/Winnipeg" , FOR_V2(0x03BF6A, 0x017646) },
- { "America/Yakutat" , FOR_V2(0x03CADE, 0x017A8E) },
- { "America/Yellowknife" , FOR_V2(0x03D404, 0x017DF7) },
- { "Antarctica/Casey" , FOR_V2(0x03DBE3, 0x0180F6) },
- { "Antarctica/Davis" , FOR_V2(0x03DD04, 0x01817F) },
- { "Antarctica/DumontDUrville" , FOR_V2(0x03DE37, 0x01820D) },
- { "Antarctica/Macquarie" , FOR_V2(0x03DF36, 0x01828E) },
- { "Antarctica/Mawson" , FOR_V2(0x03E54C, 0x0184E8) },
- { "Antarctica/McMurdo" , FOR_V2(0x03E62A, 0x018556) },
- { "Antarctica/Palmer" , FOR_V2(0x03EFF8, 0x01890B) },
- { "Antarctica/Rothera" , FOR_V2(0x03F7F4, 0x018C0B) },
- { "Antarctica/South_Pole" , FOR_V2(0x03F8B4, 0x018C6D) },
- { "Antarctica/Syowa" , FOR_V2(0x04025C, 0x018FFC) },
- { "Antarctica/Troll" , FOR_V2(0x04031B, 0x01905C) },
- { "Antarctica/Vostok" , FOR_V2(0x0407B5, 0x01921A) },
- { "Arctic/Longyearbyen" , FOR_V2(0x040875, 0x01927B) },
- { "Asia/Aden" , FOR_V2(0x04114C, 0x0195BE) },
- { "Asia/Almaty" , FOR_V2(0x041203, 0x019618) },
- { "Asia/Amman" , FOR_V2(0x0415CE, 0x0197A5) },
- { "Asia/Anadyr" , FOR_V2(0x041D2F, 0x019A60) },
- { "Asia/Aqtau" , FOR_V2(0x0421FB, 0x019C4F) },
- { "Asia/Aqtobe" , FOR_V2(0x0426A9, 0x019E56) },
- { "Asia/Ashgabat" , FOR_V2(0x042ADE, 0x01A011) },
- { "Asia/Ashkhabad" , FOR_V2(0x042D89, 0x01A133) },
- { "Asia/Baghdad" , FOR_V2(0x043034, 0x01A255) },
- { "Asia/Bahrain" , FOR_V2(0x04341C, 0x01A3DB) },
- { "Asia/Baku" , FOR_V2(0x0434F9, 0x01A446) },
- { "Asia/Bangkok" , FOR_V2(0x043A2A, 0x01A657) },
- { "Asia/Barnaul" , FOR_V2(0x043B02, 0x01A6BD) },
- { "Asia/Beirut" , FOR_V2(0x043FF5, 0x01A8B7) },
- { "Asia/Bishkek" , FOR_V2(0x044880, 0x01ABD5) },
- { "Asia/Brunei" , FOR_V2(0x044CB1, 0x01AD86) },
- { "Asia/Calcutta" , FOR_V2(0x044D86, 0x01ADED) },
- { "Asia/Chita" , FOR_V2(0x044EB5, 0x01AE77) },
- { "Asia/Choibalsan" , FOR_V2(0x0453C7, 0x01B086) },
- { "Asia/Chongqing" , FOR_V2(0x045A16, 0x01B2F2) },
- { "Asia/Chungking" , FOR_V2(0x045BC0, 0x01B3A3) },
- { "Asia/Colombo" , FOR_V2(0x045D6A, 0x01B454) },
- { "Asia/Dacca" , FOR_V2(0x045EFB, 0x01B509) },
- { "Asia/Damascus" , FOR_V2(0x04608D, 0x01B5C0) },
- { "Asia/Dhaka" , FOR_V2(0x0469A9, 0x01B915) },
- { "Asia/Dili" , FOR_V2(0x046B3B, 0x01B9CC) },
- { "Asia/Dubai" , FOR_V2(0x046C7C, 0x01BA63) },
- { "Asia/Dushanbe" , FOR_V2(0x046D33, 0x01BABD) },
- { "Asia/Gaza" , FOR_V2(0x046FA2, 0x01BBC5) },
- { "Asia/Harbin" , FOR_V2(0x0478C1, 0x01BF29) },
- { "Asia/Hebron" , FOR_V2(0x047A6B, 0x01BFDA) },
- { "Asia/Ho_Chi_Minh" , FOR_V2(0x0483A5, 0x01C347) },
- { "Asia/Hong_Kong" , FOR_V2(0x048526, 0x01C3F6) },
- { "Asia/Hovd" , FOR_V2(0x0489D7, 0x01C5C5) },
- { "Asia/Irkutsk" , FOR_V2(0x048FF7, 0x01C828) },
- { "Asia/Istanbul" , FOR_V2(0x049508, 0x01CA37) },
- { "Asia/Jakarta" , FOR_V2(0x049FCF, 0x01CE35) },
- { "Asia/Jayapura" , FOR_V2(0x04A15A, 0x01CEEF) },
- { "Asia/Jerusalem" , FOR_V2(0x04A28D, 0x01CF9E) },
- { "Asia/Kabul" , FOR_V2(0x04AB72, 0x01D2E6) },
- { "Asia/Kamchatka" , FOR_V2(0x04AC45, 0x01D348) },
- { "Asia/Karachi" , FOR_V2(0x04B100, 0x01D52E) },
- { "Asia/Kashgar" , FOR_V2(0x04B29F, 0x01D5E8) },
- { "Asia/Kathmandu" , FOR_V2(0x04B356, 0x01D642) },
- { "Asia/Katmandu" , FOR_V2(0x04B436, 0x01D6AD) },
- { "Asia/Khandyga" , FOR_V2(0x04B516, 0x01D718) },
- { "Asia/Kolkata" , FOR_V2(0x04BA6C, 0x01D94C) },
- { "Asia/Krasnoyarsk" , FOR_V2(0x04BB9B, 0x01D9D6) },
- { "Asia/Kuala_Lumpur" , FOR_V2(0x04C08A, 0x01DBD8) },
- { "Asia/Kuching" , FOR_V2(0x04C238, 0x01DCA7) },
- { "Asia/Kuwait" , FOR_V2(0x04C459, 0x01DD99) },
- { "Asia/Macao" , FOR_V2(0x04C510, 0x01DDF3) },
- { "Asia/Macau" , FOR_V2(0x04C837, 0x01DF33) },
- { "Asia/Magadan" , FOR_V2(0x04CB5E, 0x01E073) },
- { "Asia/Makassar" , FOR_V2(0x04D045, 0x01E26C) },
- { "Asia/Manila" , FOR_V2(0x04D1B2, 0x01E33F) },
- { "Asia/Muscat" , FOR_V2(0x04D327, 0x01E3DD) },
- { "Asia/Nicosia" , FOR_V2(0x04D3DE, 0x01E437) },
- { "Asia/Novokuznetsk" , FOR_V2(0x04DBCA, 0x01E724) },
- { "Asia/Novosibirsk" , FOR_V2(0x04E0C7, 0x01E929) },
- { "Asia/Omsk" , FOR_V2(0x04E5A6, 0x01EB22) },
- { "Asia/Oral" , FOR_V2(0x04EA89, 0x01ED18) },
- { "Asia/Phnom_Penh" , FOR_V2(0x04EEF0, 0x01EEED) },
- { "Asia/Pontianak" , FOR_V2(0x04EFC8, 0x01EF53) },
- { "Asia/Pyongyang" , FOR_V2(0x04F161, 0x01F01B) },
- { "Asia/Qatar" , FOR_V2(0x04F2A5, 0x01F0B2) },
- { "Asia/Qyzylorda" , FOR_V2(0x04F382, 0x01F11D) },
- { "Asia/Rangoon" , FOR_V2(0x04F7E5, 0x01F2F5) },
- { "Asia/Riyadh" , FOR_V2(0x04F90E, 0x01F37E) },
- { "Asia/Saigon" , FOR_V2(0x04F9C5, 0x01F3D8) },
- { "Asia/Sakhalin" , FOR_V2(0x04FB46, 0x01F487) },
- { "Asia/Samarkand" , FOR_V2(0x050053, 0x01F693) },
- { "Asia/Seoul" , FOR_V2(0x050323, 0x01F7D0) },
- { "Asia/Shanghai" , FOR_V2(0x05056A, 0x01F8C8) },
- { "Asia/Singapore" , FOR_V2(0x050720, 0x01F985) },
- { "Asia/Srednekolymsk" , FOR_V2(0x0508D8, 0x01FA4D) },
- { "Asia/Taipei" , FOR_V2(0x050DDB, 0x01FC5D) },
- { "Asia/Tashkent" , FOR_V2(0x051107, 0x01FD9F) },
- { "Asia/Tbilisi" , FOR_V2(0x0513CD, 0x01FED7) },
- { "Asia/Tehran" , FOR_V2(0x05184F, 0x0200AA) },
- { "Asia/Tel_Aviv" , FOR_V2(0x051EE9, 0x020325) },
- { "Asia/Thimbu" , FOR_V2(0x0527CE, 0x02066D) },
- { "Asia/Thimphu" , FOR_V2(0x0528AB, 0x0206D8) },
- { "Asia/Tokyo" , FOR_V2(0x052988, 0x020743) },
- { "Asia/Ujung_Pandang" , FOR_V2(0x052AF7, 0x0207DA) },
- { "Asia/Ulaanbaatar" , FOR_V2(0x052C1B, 0x020864) },
- { "Asia/Ulan_Bator" , FOR_V2(0x053225, 0x020AB1) },
- { "Asia/Urumqi" , FOR_V2(0x05381A, 0x020CE9) },
- { "Asia/Ust-Nera" , FOR_V2(0x0538DE, 0x020D50) },
- { "Asia/Vientiane" , FOR_V2(0x053E0B, 0x020F6C) },
- { "Asia/Vladivostok" , FOR_V2(0x053EE3, 0x020FD2) },
- { "Asia/Yakutsk" , FOR_V2(0x0543CD, 0x0211CE) },
- { "Asia/Yekaterinburg" , FOR_V2(0x0548B6, 0x0213CA) },
- { "Asia/Yerevan" , FOR_V2(0x054E06, 0x0215F5) },
- { "Atlantic/Azores" , FOR_V2(0x05530F, 0x0217FA) },
- { "Atlantic/Bermuda" , FOR_V2(0x0560C1, 0x021D0E) },
- { "Atlantic/Canary" , FOR_V2(0x0568A1, 0x021FF4) },
- { "Atlantic/Cape_Verde" , FOR_V2(0x057034, 0x0222CF) },
- { "Atlantic/Faeroe" , FOR_V2(0x05713E, 0x02234D) },
- { "Atlantic/Faroe" , FOR_V2(0x05786F, 0x0225F6) },
- { "Atlantic/Jan_Mayen" , FOR_V2(0x057FA0, 0x02289F) },
- { "Atlantic/Madeira" , FOR_V2(0x058877, 0x022BE2) },
- { "Atlantic/Reykjavik" , FOR_V2(0x059628, 0x0230FC) },
- { "Atlantic/South_Georgia" , FOR_V2(0x059ADA, 0x0232CE) },
- { "Atlantic/St_Helena" , FOR_V2(0x059B7A, 0x023312) },
- { "Atlantic/Stanley" , FOR_V2(0x059C30, 0x02336C) },
- { "Australia/ACT" , FOR_V2(0x05A11A, 0x023553) },
- { "Australia/Adelaide" , FOR_V2(0x05A9D5, 0x023887) },
- { "Australia/Brisbane" , FOR_V2(0x05B2AE, 0x023BC6) },
- { "Australia/Broken_Hill" , FOR_V2(0x05B495, 0x023CA0) },
- { "Australia/Canberra" , FOR_V2(0x05BD9F, 0x023FF1) },
- { "Australia/Currie" , FOR_V2(0x05C65A, 0x024325) },
- { "Australia/Darwin" , FOR_V2(0x05CF2B, 0x02466F) },
- { "Australia/Eucla" , FOR_V2(0x05D08C, 0x024708) },
- { "Australia/Hobart" , FOR_V2(0x05D298, 0x0247F0) },
- { "Australia/LHI" , FOR_V2(0x05DBD8, 0x024B61) },
- { "Australia/Lindeman" , FOR_V2(0x05E327, 0x024E13) },
- { "Australia/Lord_Howe" , FOR_V2(0x05E55C, 0x024F0E) },
- { "Australia/Melbourne" , FOR_V2(0x05ECBB, 0x0251D0) },
- { "Australia/North" , FOR_V2(0x05F57E, 0x02550C) },
- { "Australia/NSW" , FOR_V2(0x05F6CD, 0x025593) },
- { "Australia/Perth" , FOR_V2(0x05FF88, 0x0258C7) },
- { "Australia/Queensland" , FOR_V2(0x060191, 0x0259B2) },
- { "Australia/South" , FOR_V2(0x060361, 0x025A75) },
- { "Australia/Sydney" , FOR_V2(0x060C2B, 0x025DA5) },
- { "Australia/Tasmania" , FOR_V2(0x061502, 0x0260F5) },
- { "Australia/Victoria" , FOR_V2(0x061E2D, 0x026451) },
- { "Australia/West" , FOR_V2(0x0626E8, 0x026785) },
- { "Australia/Yancowinna" , FOR_V2(0x0628D3, 0x026852) },
- { "Brazil/Acre" , FOR_V2(0x0631C1, 0x026B87) },
- { "Brazil/DeNoronha" , FOR_V2(0x06345D, 0x026C98) },
- { "Brazil/East" , FOR_V2(0x063741, 0x026DBD) },
- { "Brazil/West" , FOR_V2(0x063F2C, 0x02709F) },
- { "Canada/Atlantic" , FOR_V2(0x0641A0, 0x02719C) },
- { "Canada/Central" , FOR_V2(0x064F1A, 0x027689) },
- { "Canada/East-Saskatchewan" , FOR_V2(0x065A71, 0x027AB4) },
- { "Canada/Eastern" , FOR_V2(0x065E5F, 0x027C42) },
- { "Canada/Mountain" , FOR_V2(0x066C1A, 0x028143) },
- { "Canada/Newfoundland" , FOR_V2(0x067588, 0x0284BE) },
- { "Canada/Pacific" , FOR_V2(0x0683E4, 0x028A02) },
- { "Canada/Saskatchewan" , FOR_V2(0x068F45, 0x028E2C) },
- { "Canada/Yukon" , FOR_V2(0x069333, 0x028FBA) },
- { "CET" , FOR_V2(0x069B6C, 0x0292CE) },
- { "Chile/Continental" , FOR_V2(0x06A3AE, 0x0295D7) },
- { "Chile/EasterIsland" , FOR_V2(0x06AD8D, 0x02997B) },
- { "CST6CDT" , FOR_V2(0x06B64A, 0x029CB5) },
- { "Cuba" , FOR_V2(0x06BF4C, 0x02A006) },
- { "EET" , FOR_V2(0x06C8DD, 0x02A38A) },
- { "Egypt" , FOR_V2(0x06D03D, 0x02A63D) },
- { "Eire" , FOR_V2(0x06D7FD, 0x02A91D) },
- { "EST" , FOR_V2(0x06E5F0, 0x02AE3F) },
- { "EST5EDT" , FOR_V2(0x06E67B, 0x02AE83) },
- { "Etc/GMT" , FOR_V2(0x06EF7D, 0x02B1D4) },
- { "Etc/GMT+0" , FOR_V2(0x06F008, 0x02B218) },
- { "Etc/GMT+1" , FOR_V2(0x06F093, 0x02B25C) },
- { "Etc/GMT+10" , FOR_V2(0x06F126, 0x02B2A2) },
- { "Etc/GMT+11" , FOR_V2(0x06F1BD, 0x02B2E9) },
- { "Etc/GMT+12" , FOR_V2(0x06F254, 0x02B330) },
- { "Etc/GMT+2" , FOR_V2(0x06F2EB, 0x02B377) },
- { "Etc/GMT+3" , FOR_V2(0x06F37E, 0x02B3BD) },
- { "Etc/GMT+4" , FOR_V2(0x06F411, 0x02B403) },
- { "Etc/GMT+5" , FOR_V2(0x06F4A4, 0x02B449) },
- { "Etc/GMT+6" , FOR_V2(0x06F537, 0x02B48F) },
- { "Etc/GMT+7" , FOR_V2(0x06F5CA, 0x02B4D5) },
- { "Etc/GMT+8" , FOR_V2(0x06F65D, 0x02B51B) },
- { "Etc/GMT+9" , FOR_V2(0x06F6F0, 0x02B561) },
- { "Etc/GMT-0" , FOR_V2(0x06F783, 0x02B5A7) },
- { "Etc/GMT-1" , FOR_V2(0x06F80E, 0x02B5EB) },
- { "Etc/GMT-10" , FOR_V2(0x06F8A2, 0x02B631) },
- { "Etc/GMT-11" , FOR_V2(0x06F93A, 0x02B678) },
- { "Etc/GMT-12" , FOR_V2(0x06F9D2, 0x02B6BF) },
- { "Etc/GMT-13" , FOR_V2(0x06FA6A, 0x02B706) },
- { "Etc/GMT-14" , FOR_V2(0x06FB02, 0x02B74D) },
- { "Etc/GMT-2" , FOR_V2(0x06FB9A, 0x02B794) },
- { "Etc/GMT-3" , FOR_V2(0x06FC2E, 0x02B7DA) },
- { "Etc/GMT-4" , FOR_V2(0x06FCC2, 0x02B820) },
- { "Etc/GMT-5" , FOR_V2(0x06FD56, 0x02B866) },
- { "Etc/GMT-6" , FOR_V2(0x06FDEA, 0x02B8AC) },
- { "Etc/GMT-7" , FOR_V2(0x06FE7E, 0x02B8F2) },
- { "Etc/GMT-8" , FOR_V2(0x06FF12, 0x02B938) },
- { "Etc/GMT-9" , FOR_V2(0x06FFA6, 0x02B97E) },
- { "Etc/GMT0" , FOR_V2(0x07003A, 0x02B9C4) },
- { "Etc/Greenwich" , FOR_V2(0x0700C5, 0x02BA08) },
- { "Etc/UCT" , FOR_V2(0x070150, 0x02BA4C) },
- { "Etc/Universal" , FOR_V2(0x0701DB, 0x02BA90) },
- { "Etc/UTC" , FOR_V2(0x070266, 0x02BAD4) },
- { "Etc/Zulu" , FOR_V2(0x0702F1, 0x02BB18) },
- { "Europe/Amsterdam" , FOR_V2(0x07037C, 0x02BB5C) },
- { "Europe/Andorra" , FOR_V2(0x070F07, 0x02BFAB) },
- { "Europe/Astrakhan" , FOR_V2(0x0715EA, 0x02C238) },
- { "Europe/Athens" , FOR_V2(0x071AA7, 0x02C41F) },
- { "Europe/Belfast" , FOR_V2(0x072392, 0x02C773) },
- { "Europe/Belgrade" , FOR_V2(0x073205, 0x02CCBB) },
- { "Europe/Berlin" , FOR_V2(0x0739B6, 0x02CF95) },
- { "Europe/Bratislava" , FOR_V2(0x0742F5, 0x02D310) },
- { "Europe/Brussels" , FOR_V2(0x074BE1, 0x02D653) },
- { "Europe/Bucharest" , FOR_V2(0x075787, 0x02DA9B) },
- { "Europe/Budapest" , FOR_V2(0x076040, 0x02DDD6) },
- { "Europe/Busingen" , FOR_V2(0x0769B1, 0x02E150) },
- { "Europe/Chisinau" , FOR_V2(0x077143, 0x02E418) },
- { "Europe/Copenhagen" , FOR_V2(0x077ADC, 0x02E7BC) },
- { "Europe/Dublin" , FOR_V2(0x078358, 0x02EAD7) },
- { "Europe/Gibraltar" , FOR_V2(0x07914B, 0x02EFF9) },
- { "Europe/Guernsey" , FOR_V2(0x079D4C, 0x02F461) },
- { "Europe/Helsinki" , FOR_V2(0x07ABBF, 0x02F9A9) },
- { "Europe/Isle_of_Man" , FOR_V2(0x07B340, 0x02FC70) },
- { "Europe/Istanbul" , FOR_V2(0x07C1B3, 0x0301B8) },
- { "Europe/Jersey" , FOR_V2(0x07CC7A, 0x0305B6) },
- { "Europe/Kaliningrad" , FOR_V2(0x07DAED, 0x030AFE) },
- { "Europe/Kiev" , FOR_V2(0x07E11B, 0x030D7F) },
- { "Europe/Lisbon" , FOR_V2(0x07E96C, 0x0310B2) },
- { "Europe/Ljubljana" , FOR_V2(0x07F708, 0x0315C6) },
- { "Europe/London" , FOR_V2(0x07FEB9, 0x0318A0) },
- { "Europe/Luxembourg" , FOR_V2(0x080D2C, 0x031DE8) },
- { "Europe/Madrid" , FOR_V2(0x0818D6, 0x032243) },
- { "Europe/Malta" , FOR_V2(0x08232D, 0x032622) },
- { "Europe/Mariehamn" , FOR_V2(0x082D7E, 0x0329EC) },
- { "Europe/Minsk" , FOR_V2(0x0834FF, 0x032CB3) },
- { "Europe/Monaco" , FOR_V2(0x083A63, 0x032ED7) },
- { "Europe/Moscow" , FOR_V2(0x0845F8, 0x033323) },
- { "Europe/Nicosia" , FOR_V2(0x084C10, 0x03359B) },
- { "Europe/Oslo" , FOR_V2(0x0853FC, 0x033888) },
- { "Europe/Paris" , FOR_V2(0x085CD3, 0x033BCB) },
- { "Europe/Podgorica" , FOR_V2(0x08687A, 0x034022) },
- { "Europe/Prague" , FOR_V2(0x08702B, 0x0342FC) },
- { "Europe/Riga" , FOR_V2(0x087917, 0x03463F) },
- { "Europe/Rome" , FOR_V2(0x0881DE, 0x034995) },
- { "Europe/Samara" , FOR_V2(0x088C60, 0x034D69) },
- { "Europe/San_Marino" , FOR_V2(0x0891D7, 0x034FAF) },
- { "Europe/Sarajevo" , FOR_V2(0x089C59, 0x035383) },
- { "Europe/Simferopol" , FOR_V2(0x08A40A, 0x03565D) },
- { "Europe/Skopje" , FOR_V2(0x08A9F7, 0x0358BF) },
- { "Europe/Sofia" , FOR_V2(0x08B1A8, 0x035B99) },
- { "Europe/Stockholm" , FOR_V2(0x08BA06, 0x035EB2) },
- { "Europe/Tallinn" , FOR_V2(0x08C190, 0x036172) },
- { "Europe/Tirane" , FOR_V2(0x08CA27, 0x0364B8) },
- { "Europe/Tiraspol" , FOR_V2(0x08D265, 0x0367C3) },
- { "Europe/Ulyanovsk" , FOR_V2(0x08DBFE, 0x036B67) },
- { "Europe/Uzhgorod" , FOR_V2(0x08E10F, 0x036D74) },
- { "Europe/Vaduz" , FOR_V2(0x08E95A, 0x03709C) },
- { "Europe/Vatican" , FOR_V2(0x08F0E4, 0x03735C) },
- { "Europe/Vienna" , FOR_V2(0x08FB66, 0x037730) },
- { "Europe/Vilnius" , FOR_V2(0x09042F, 0x037A6E) },
- { "Europe/Volgograd" , FOR_V2(0x090CD2, 0x037DBE) },
- { "Europe/Warsaw" , FOR_V2(0x09122D, 0x037FFB) },
- { "Europe/Zagreb" , FOR_V2(0x091CCA, 0x0383ED) },
- { "Europe/Zaporozhye" , FOR_V2(0x09247B, 0x0386C7) },
- { "Europe/Zurich" , FOR_V2(0x092CF4, 0x038A19) },
- { "Factory" , FOR_V2(0x09347E, 0x038CD9) },
- { "GB" , FOR_V2(0x093592, 0x038D4A) },
- { "GB-Eire" , FOR_V2(0x094405, 0x039292) },
- { "GMT" , FOR_V2(0x095278, 0x0397DA) },
- { "GMT+0" , FOR_V2(0x095303, 0x03981E) },
- { "GMT-0" , FOR_V2(0x09538E, 0x039862) },
- { "GMT0" , FOR_V2(0x095419, 0x0398A6) },
- { "Greenwich" , FOR_V2(0x0954A4, 0x0398EA) },
- { "Hongkong" , FOR_V2(0x09552F, 0x03992E) },
- { "HST" , FOR_V2(0x0959E0, 0x039AFD) },
- { "Iceland" , FOR_V2(0x095A6C, 0x039B41) },
- { "Indian/Antananarivo" , FOR_V2(0x095F1E, 0x039D13) },
- { "Indian/Chagos" , FOR_V2(0x096045, 0x039D9F) },
- { "Indian/Christmas" , FOR_V2(0x09611A, 0x039E06) },
- { "Indian/Cocos" , FOR_V2(0x0961BB, 0x039E4A) },
- { "Indian/Comoro" , FOR_V2(0x09625F, 0x039E8E) },
- { "Indian/Kerguelen" , FOR_V2(0x096386, 0x039F1A) },
- { "Indian/Mahe" , FOR_V2(0x09643D, 0x039F74) },
- { "Indian/Maldives" , FOR_V2(0x0964F4, 0x039FCE) },
- { "Indian/Mauritius" , FOR_V2(0x0965CC, 0x03A034) },
- { "Indian/Mayotte" , FOR_V2(0x0966D5, 0x03A0AF) },
- { "Indian/Reunion" , FOR_V2(0x0967FC, 0x03A13B) },
- { "Iran" , FOR_V2(0x0968B3, 0x03A195) },
- { "Israel" , FOR_V2(0x096F4D, 0x03A410) },
- { "Jamaica" , FOR_V2(0x097832, 0x03A758) },
- { "Japan" , FOR_V2(0x097A39, 0x03A82E) },
- { "Kwajalein" , FOR_V2(0x097BA8, 0x03A8C5) },
- { "Libya" , FOR_V2(0x097CA1, 0x03A939) },
- { "MET" , FOR_V2(0x097F3C, 0x03AA47) },
- { "Mexico/BajaNorte" , FOR_V2(0x09877E, 0x03AD50) },
- { "Mexico/BajaSur" , FOR_V2(0x0990BE, 0x03B0BE) },
- { "Mexico/General" , FOR_V2(0x0996E6, 0x03B310) },
- { "MST" , FOR_V2(0x099D44, 0x03B573) },
- { "MST7MDT" , FOR_V2(0x099DCF, 0x03B5B7) },
- { "Navajo" , FOR_V2(0x09A6D1, 0x03B908) },
- { "NZ" , FOR_V2(0x09B072, 0x03BC92) },
- { "NZ-CHAT" , FOR_V2(0x09BA1A, 0x03C021) },
- { "Pacific/Apia" , FOR_V2(0x09C22F, 0x03C316) },
- { "Pacific/Auckland" , FOR_V2(0x09C689, 0x03C4BF) },
- { "Pacific/Bougainville" , FOR_V2(0x09D049, 0x03C866) },
- { "Pacific/Chatham" , FOR_V2(0x09D179, 0x03C8EF) },
- { "Pacific/Chuuk" , FOR_V2(0x09D99D, 0x03CBF3) },
- { "Pacific/Easter" , FOR_V2(0x09DA51, 0x03CC47) },
- { "Pacific/Efate" , FOR_V2(0x09E31B, 0x03CF8E) },
- { "Pacific/Enderbury" , FOR_V2(0x09E505, 0x03D059) },
- { "Pacific/Fakaofo" , FOR_V2(0x09E606, 0x03D0D8) },
- { "Pacific/Fiji" , FOR_V2(0x09E6D7, 0x03D13A) },
- { "Pacific/Funafuti" , FOR_V2(0x09EB14, 0x03D2D2) },
- { "Pacific/Galapagos" , FOR_V2(0x09EBB6, 0x03D316) },
- { "Pacific/Gambier" , FOR_V2(0x09ECA6, 0x03D393) },
- { "Pacific/Guadalcanal" , FOR_V2(0x09ED6E, 0x03D3FD) },
- { "Pacific/Guam" , FOR_V2(0x09EE26, 0x03D457) },
- { "Pacific/Honolulu" , FOR_V2(0x09EF13, 0x03D4BE) },
- { "Pacific/Johnston" , FOR_V2(0x09F039, 0x03D546) },
- { "Pacific/Kiritimati" , FOR_V2(0x09F167, 0x03D5D6) },
- { "Pacific/Kosrae" , FOR_V2(0x09F265, 0x03D652) },
- { "Pacific/Kwajalein" , FOR_V2(0x09F35D, 0x03D6C8) },
- { "Pacific/Majuro" , FOR_V2(0x09F45F, 0x03D745) },
- { "Pacific/Marquesas" , FOR_V2(0x09F54D, 0x03D7C4) },
- { "Pacific/Midway" , FOR_V2(0x09F61A, 0x03D830) },
- { "Pacific/Nauru" , FOR_V2(0x09F744, 0x03D8C2) },
- { "Pacific/Niue" , FOR_V2(0x09F84E, 0x03D93F) },
- { "Pacific/Norfolk" , FOR_V2(0x09F93C, 0x03D9AE) },
- { "Pacific/Noumea" , FOR_V2(0x09FA69, 0x03DA38) },
- { "Pacific/Pago_Pago" , FOR_V2(0x09FBAF, 0x03DACD) },
- { "Pacific/Palau" , FOR_V2(0x09FCCB, 0x03DB51) },
- { "Pacific/Pitcairn" , FOR_V2(0x09FD6C, 0x03DB95) },
- { "Pacific/Pohnpei" , FOR_V2(0x09FE43, 0x03DBFB) },
- { "Pacific/Ponape" , FOR_V2(0x09FEF6, 0x03DC4E) },
- { "Pacific/Port_Moresby" , FOR_V2(0x09FF9B, 0x03DC93) },
- { "Pacific/Rarotonga" , FOR_V2(0x0A0070, 0x03DCF4) },
- { "Pacific/Saipan" , FOR_V2(0x0A02BA, 0x03DDE1) },
- { "Pacific/Samoa" , FOR_V2(0x0A03A7, 0x03DE48) },
- { "Pacific/Tahiti" , FOR_V2(0x0A04C3, 0x03DECC) },
- { "Pacific/Tarawa" , FOR_V2(0x0A058C, 0x03DF36) },
- { "Pacific/Tongatapu" , FOR_V2(0x0A0640, 0x03DF8A) },
- { "Pacific/Truk" , FOR_V2(0x0A079F, 0x03E027) },
- { "Pacific/Wake" , FOR_V2(0x0A0844, 0x03E06C) },
- { "Pacific/Wallis" , FOR_V2(0x0A08F4, 0x03E0BC) },
- { "Pacific/Yap" , FOR_V2(0x0A0996, 0x03E100) },
- { "Poland" , FOR_V2(0x0A0A3B, 0x03E145) },
- { "Portugal" , FOR_V2(0x0A14D8, 0x03E537) },
- { "PRC" , FOR_V2(0x0A2261, 0x03EA38) },
- { "PST8PDT" , FOR_V2(0x0A240B, 0x03EAE9) },
- { "ROC" , FOR_V2(0x0A2D0D, 0x03EE3A) },
- { "ROK" , FOR_V2(0x0A3039, 0x03EF7C) },
- { "Singapore" , FOR_V2(0x0A3280, 0x03F074) },
- { "Turkey" , FOR_V2(0x0A3438, 0x03F13C) },
- { "UCT" , FOR_V2(0x0A3EFF, 0x03F53A) },
- { "Universal" , FOR_V2(0x0A3F8A, 0x03F57E) },
- { "US/Alaska" , FOR_V2(0x0A4015, 0x03F5C2) },
- { "US/Aleutian" , FOR_V2(0x0A4971, 0x03F93C) },
- { "US/Arizona" , FOR_V2(0x0A52BA, 0x03FCAD) },
- { "US/Central" , FOR_V2(0x0A5427, 0x03FD4C) },
- { "US/East-Indiana" , FOR_V2(0x0A6234, 0x040270) },
- { "US/Eastern" , FOR_V2(0x0A68CB, 0x0404EB) },
- { "US/Hawaii" , FOR_V2(0x0A76B0, 0x0409FB) },
- { "US/Indiana-Starke" , FOR_V2(0x0A77D0, 0x040A7D) },
- { "US/Michigan" , FOR_V2(0x0A8161, 0x040E07) },
- { "US/Mountain" , FOR_V2(0x0A8A15, 0x041143) },
- { "US/Pacific" , FOR_V2(0x0A93B6, 0x0414CD) },
- { "US/Pacific-New" , FOR_V2(0x0A9EDF, 0x0418E3) },
- { "US/Samoa" , FOR_V2(0x0AAA08, 0x041CF9) },
- { "UTC" , FOR_V2(0x0AAB24, 0x041D7D) },
- { "W-SU" , FOR_V2(0x0AABAF, 0x041DC1) },
- { "WET" , FOR_V2(0x0AB1B3, 0x042025) },
- { "Zulu" , FOR_V2(0x0AB910, 0x0422D8) },
+ { "Africa/Casablanca" , FOR_V2(0x001C6A, 0x000AE8) },
+ { "Africa/Ceuta" , FOR_V2(0x0022D3, 0x000D4D) },
+ { "Africa/Conakry" , FOR_V2(0x002B08, 0x001064) },
+ { "Africa/Dakar" , FOR_V2(0x002BBE, 0x0010BE) },
+ { "Africa/Dar_es_Salaam" , FOR_V2(0x002C74, 0x001118) },
+ { "Africa/Djibouti" , FOR_V2(0x002D9B, 0x0011A4) },
+ { "Africa/Douala" , FOR_V2(0x002EC2, 0x001230) },
+ { "Africa/El_Aaiun" , FOR_V2(0x002F79, 0x00128A) },
+ { "Africa/Freetown" , FOR_V2(0x003538, 0x0014B0) },
+ { "Africa/Gaborone" , FOR_V2(0x0035EE, 0x00150A) },
+ { "Africa/Harare" , FOR_V2(0x0036A5, 0x001564) },
+ { "Africa/Johannesburg" , FOR_V2(0x00375C, 0x0015BE) },
+ { "Africa/Juba" , FOR_V2(0x003877, 0x00163D) },
+ { "Africa/Kampala" , FOR_V2(0x003B2E, 0x001755) },
+ { "Africa/Khartoum" , FOR_V2(0x003C55, 0x0017E1) },
+ { "Africa/Kigali" , FOR_V2(0x003F0C, 0x0018F9) },
+ { "Africa/Kinshasa" , FOR_V2(0x003FC3, 0x001953) },
+ { "Africa/Lagos" , FOR_V2(0x004093, 0x0019C6) },
+ { "Africa/Libreville" , FOR_V2(0x00414A, 0x001A20) },
+ { "Africa/Lome" , FOR_V2(0x004201, 0x001A7A) },
+ { "Africa/Luanda" , FOR_V2(0x0042B7, 0x001AD4) },
+ { "Africa/Lubumbashi" , FOR_V2(0x00436E, 0x001B2E) },
+ { "Africa/Lusaka" , FOR_V2(0x00443E, 0x001BA1) },
+ { "Africa/Malabo" , FOR_V2(0x0044F5, 0x001BFB) },
+ { "Africa/Maputo" , FOR_V2(0x0045AC, 0x001C55) },
+ { "Africa/Maseru" , FOR_V2(0x004663, 0x001CAF) },
+ { "Africa/Mbabane" , FOR_V2(0x00477E, 0x001D2E) },
+ { "Africa/Mogadishu" , FOR_V2(0x004899, 0x001DAD) },
+ { "Africa/Monrovia" , FOR_V2(0x0049C0, 0x001E39) },
+ { "Africa/Nairobi" , FOR_V2(0x004ABD, 0x001EB0) },
+ { "Africa/Ndjamena" , FOR_V2(0x004BE4, 0x001F3C) },
+ { "Africa/Niamey" , FOR_V2(0x004CD1, 0x001FAD) },
+ { "Africa/Nouakchott" , FOR_V2(0x004D88, 0x002007) },
+ { "Africa/Ouagadougou" , FOR_V2(0x004E3E, 0x002061) },
+ { "Africa/Porto-Novo" , FOR_V2(0x004EF4, 0x0020BB) },
+ { "Africa/Sao_Tome" , FOR_V2(0x004FAB, 0x002115) },
+ { "Africa/Timbuktu" , FOR_V2(0x005061, 0x00216F) },
+ { "Africa/Tripoli" , FOR_V2(0x005117, 0x0021C9) },
+ { "Africa/Tunis" , FOR_V2(0x0053B2, 0x0022D7) },
+ { "Africa/Windhoek" , FOR_V2(0x005684, 0x0023FA) },
+ { "America/Adak" , FOR_V2(0x005CBE, 0x002652) },
+ { "America/Anchorage" , FOR_V2(0x006617, 0x0029D3) },
+ { "America/Anguilla" , FOR_V2(0x006F86, 0x002D60) },
+ { "America/Antigua" , FOR_V2(0x00703C, 0x002DBA) },
+ { "America/Araguaina" , FOR_V2(0x0070F2, 0x002E14) },
+ { "America/Argentina/Buenos_Aires" , FOR_V2(0x007487, 0x002F7E) },
+ { "America/Argentina/Catamarca" , FOR_V2(0x0078E7, 0x00313D) },
+ { "America/Argentina/ComodRivadavia" , FOR_V2(0x007D77, 0x003317) },
+ { "America/Argentina/Cordoba" , FOR_V2(0x0081EC, 0x0034D6) },
+ { "America/Argentina/Jujuy" , FOR_V2(0x008697, 0x0036CB) },
+ { "America/Argentina/La_Rioja" , FOR_V2(0x008B26, 0x0038A0) },
+ { "America/Argentina/Mendoza" , FOR_V2(0x008FB6, 0x003A71) },
+ { "America/Argentina/Rio_Gallegos" , FOR_V2(0x009463, 0x003C52) },
+ { "America/Argentina/Salta" , FOR_V2(0x0098E7, 0x003E20) },
+ { "America/Argentina/San_Juan" , FOR_V2(0x009D56, 0x003FEB) },
+ { "America/Argentina/San_Luis" , FOR_V2(0x00A1E6, 0x0041BC) },
+ { "America/Argentina/Tucuman" , FOR_V2(0x00A692, 0x00439B) },
+ { "America/Argentina/Ushuaia" , FOR_V2(0x00AB2F, 0x004570) },
+ { "America/Aruba" , FOR_V2(0x00AFB9, 0x004744) },
+ { "America/Asuncion" , FOR_V2(0x00B095, 0x0047AF) },
+ { "America/Atikokan" , FOR_V2(0x00B8AF, 0x004AAD) },
+ { "America/Atka" , FOR_V2(0x00BA35, 0x004B71) },
+ { "America/Bahia" , FOR_V2(0x00C37E, 0x004EE2) },
+ { "America/Bahia_Banderas" , FOR_V2(0x00C79B, 0x00507A) },
+ { "America/Barbados" , FOR_V2(0x00CDFB, 0x0052F8) },
+ { "America/Belem" , FOR_V2(0x00CF5F, 0x005397) },
+ { "America/Belize" , FOR_V2(0x00D1C9, 0x00549C) },
+ { "America/Blanc-Sablon" , FOR_V2(0x00D5A5, 0x00561D) },
+ { "America/Boa_Vista" , FOR_V2(0x00D700, 0x0056CB) },
+ { "America/Bogota" , FOR_V2(0x00D997, 0x0057D9) },
+ { "America/Boise" , FOR_V2(0x00DAA4, 0x005856) },
+ { "America/Buenos_Aires" , FOR_V2(0x00E433, 0x005BF5) },
+ { "America/Cambridge_Bay" , FOR_V2(0x00E87E, 0x005D9F) },
+ { "America/Campo_Grande" , FOR_V2(0x00F0D0, 0x0060D4) },
+ { "America/Cancun" , FOR_V2(0x00F8CD, 0x0063C8) },
+ { "America/Caracas" , FOR_V2(0x00FC2D, 0x006537) },
+ { "America/Catamarca" , FOR_V2(0x00FD3E, 0x0065B4) },
+ { "America/Cayenne" , FOR_V2(0x0101B3, 0x006773) },
+ { "America/Cayman" , FOR_V2(0x010287, 0x0067DA) },
+ { "America/Chicago" , FOR_V2(0x01035E, 0x006840) },
+ { "America/Chihuahua" , FOR_V2(0x01117F, 0x006D78) },
+ { "America/Coral_Harbour" , FOR_V2(0x0117A3, 0x006FE1) },
+ { "America/Cordoba" , FOR_V2(0x011908, 0x007084) },
+ { "America/Costa_Rica" , FOR_V2(0x011D7D, 0x007243) },
+ { "America/Creston" , FOR_V2(0x011EDE, 0x0072DE) },
+ { "America/Cuiaba" , FOR_V2(0x011FE5, 0x007363) },
+ { "America/Curacao" , FOR_V2(0x0127BF, 0x007646) },
+ { "America/Danmarkshavn" , FOR_V2(0x01289B, 0x0076B1) },
+ { "America/Dawson" , FOR_V2(0x012B8B, 0x0077F3) },
+ { "America/Dawson_Creek" , FOR_V2(0x0133DB, 0x007B1E) },
+ { "America/Denver" , FOR_V2(0x01382A, 0x007CE0) },
+ { "America/Detroit" , FOR_V2(0x0141E0, 0x00807F) },
+ { "America/Dominica" , FOR_V2(0x014AAD, 0x0083D4) },
+ { "America/Edmonton" , FOR_V2(0x014B63, 0x00842E) },
+ { "America/Eirunepe" , FOR_V2(0x0154EE, 0x0087C6) },
+ { "America/El_Salvador" , FOR_V2(0x0157B5, 0x0088F0) },
+ { "America/Ensenada" , FOR_V2(0x0158BB, 0x00896A) },
+ { "America/Fort_Nelson" , FOR_V2(0x0161FB, 0x008CD8) },
+ { "America/Fort_Wayne" , FOR_V2(0x016AE4, 0x009037) },
+ { "America/Fortaleza" , FOR_V2(0x01717B, 0x0092B2) },
+ { "America/Glace_Bay" , FOR_V2(0x017485, 0x0093FD) },
+ { "America/Godthab" , FOR_V2(0x017D4A, 0x00974D) },
+ { "America/Goose_Bay" , FOR_V2(0x0184C1, 0x009A1E) },
+ { "America/Grand_Turk" , FOR_V2(0x019180, 0x009EEB) },
+ { "America/Grenada" , FOR_V2(0x019693, 0x00A0DB) },
+ { "America/Guadeloupe" , FOR_V2(0x019749, 0x00A135) },
+ { "America/Guatemala" , FOR_V2(0x0197FF, 0x00A18F) },
+ { "America/Guayaquil" , FOR_V2(0x01993D, 0x00A21D) },
+ { "America/Guyana" , FOR_V2(0x019A26, 0x00A295) },
+ { "America/Halifax" , FOR_V2(0x019B40, 0x00A31B) },
+ { "America/Havana" , FOR_V2(0x01A8D8, 0x00A826) },
+ { "America/Hermosillo" , FOR_V2(0x01B269, 0x00ABAA) },
+ { "America/Indiana/Indianapolis" , FOR_V2(0x01B45A, 0x00AC95) },
+ { "America/Indiana/Knox" , FOR_V2(0x01BB0A, 0x00AF29) },
+ { "America/Indiana/Marengo" , FOR_V2(0x01C4B0, 0x00B2C8) },
+ { "America/Indiana/Petersburg" , FOR_V2(0x01CB96, 0x00B56E) },
+ { "America/Indiana/Tell_City" , FOR_V2(0x01D32E, 0x00B851) },
+ { "America/Indiana/Vevay" , FOR_V2(0x01DA15, 0x00BAFA) },
+ { "America/Indiana/Vincennes" , FOR_V2(0x01DFCA, 0x00BD35) },
+ { "America/Indiana/Winamac" , FOR_V2(0x01E699, 0x00BFD6) },
+ { "America/Indianapolis" , FOR_V2(0x01EDB6, 0x00C28F) },
+ { "America/Inuvik" , FOR_V2(0x01F44D, 0x00C50A) },
+ { "America/Iqaluit" , FOR_V2(0x01FBF5, 0x00C7F0) },
+ { "America/Jamaica" , FOR_V2(0x02041D, 0x00CB19) },
+ { "America/Jujuy" , FOR_V2(0x020624, 0x00CBEF) },
+ { "America/Juneau" , FOR_V2(0x020AA9, 0x00CDBA) },
+ { "America/Kentucky/Louisville" , FOR_V2(0x021403, 0x00D13F) },
+ { "America/Kentucky/Monticello" , FOR_V2(0x021F0A, 0x00D563) },
+ { "America/Knox_IN" , FOR_V2(0x022863, 0x00D8E7) },
+ { "America/Kralendijk" , FOR_V2(0x0231F4, 0x00DC71) },
+ { "America/La_Paz" , FOR_V2(0x0232D0, 0x00DCDC) },
+ { "America/Lima" , FOR_V2(0x0233CF, 0x00DD54) },
+ { "America/Los_Angeles" , FOR_V2(0x02357C, 0x00DE09) },
+ { "America/Louisville" , FOR_V2(0x0240AC, 0x00E226) },
+ { "America/Lower_Princes" , FOR_V2(0x024B95, 0x00E62C) },
+ { "America/Maceio" , FOR_V2(0x024C71, 0x00E697) },
+ { "America/Managua" , FOR_V2(0x024F81, 0x00E7D6) },
+ { "America/Manaus" , FOR_V2(0x02515C, 0x00E8A2) },
+ { "America/Marigot" , FOR_V2(0x0253DF, 0x00E9AE) },
+ { "America/Martinique" , FOR_V2(0x025495, 0x00EA08) },
+ { "America/Matamoros" , FOR_V2(0x0255A2, 0x00EA85) },
+ { "America/Mazatlan" , FOR_V2(0x025B74, 0x00ECD7) },
+ { "America/Mendoza" , FOR_V2(0x0261D1, 0x00EF5E) },
+ { "America/Menominee" , FOR_V2(0x026672, 0x00F133) },
+ { "America/Merida" , FOR_V2(0x026F88, 0x00F4A5) },
+ { "America/Metlakatla" , FOR_V2(0x027564, 0x00F6ED) },
+ { "America/Mexico_City" , FOR_V2(0x027B11, 0x00F91D) },
+ { "America/Miquelon" , FOR_V2(0x02817B, 0x00FB8C) },
+ { "America/Moncton" , FOR_V2(0x02881B, 0x00FE03) },
+ { "America/Monterrey" , FOR_V2(0x02949A, 0x0102A6) },
+ { "America/Montevideo" , FOR_V2(0x029A73, 0x0104FF) },
+ { "America/Montreal" , FOR_V2(0x02A066, 0x010749) },
+ { "America/Montserrat" , FOR_V2(0x02AE21, 0x010C4A) },
+ { "America/Nassau" , FOR_V2(0x02AED7, 0x010CA4) },
+ { "America/New_York" , FOR_V2(0x02B7CF, 0x010FEE) },
+ { "America/Nipigon" , FOR_V2(0x02C5C8, 0x011512) },
+ { "America/Nome" , FOR_V2(0x02CE48, 0x01184A) },
+ { "America/Noronha" , FOR_V2(0x02D7A9, 0x011BCD) },
+ { "America/North_Dakota/Beulah" , FOR_V2(0x02DA9D, 0x011D02) },
+ { "America/North_Dakota/Center" , FOR_V2(0x02E413, 0x012091) },
+ { "America/North_Dakota/New_Salem" , FOR_V2(0x02ED89, 0x012420) },
+ { "America/Ojinaga" , FOR_V2(0x02F705, 0x0127B5) },
+ { "America/Panama" , FOR_V2(0x02FD2B, 0x012A20) },
+ { "America/Pangnirtung" , FOR_V2(0x02FE02, 0x012A86) },
+ { "America/Paramaribo" , FOR_V2(0x030664, 0x012DC8) },
+ { "America/Phoenix" , FOR_V2(0x0307A4, 0x012E5F) },
+ { "America/Port-au-Prince" , FOR_V2(0x03092E, 0x012F1B) },
+ { "America/Port_of_Spain" , FOR_V2(0x030C8B, 0x013070) },
+ { "America/Porto_Acre" , FOR_V2(0x030D41, 0x0130CA) },
+ { "America/Porto_Velho" , FOR_V2(0x030FDD, 0x0131DB) },
+ { "America/Puerto_Rico" , FOR_V2(0x03123D, 0x0132D6) },
+ { "America/Rainy_River" , FOR_V2(0x031348, 0x013352) },
+ { "America/Rankin_Inlet" , FOR_V2(0x031BC9, 0x01368B) },
+ { "America/Recife" , FOR_V2(0x032375, 0x013976) },
+ { "America/Regina" , FOR_V2(0x032663, 0x013AA5) },
+ { "America/Resolute" , FOR_V2(0x032A66, 0x013C48) },
+ { "America/Rio_Branco" , FOR_V2(0x033213, 0x013F34) },
+ { "America/Rosario" , FOR_V2(0x0334B3, 0x014049) },
+ { "America/Santa_Isabel" , FOR_V2(0x033928, 0x014208) },
+ { "America/Santarem" , FOR_V2(0x034268, 0x014576) },
+ { "America/Santiago" , FOR_V2(0x0344F1, 0x014685) },
+ { "America/Santo_Domingo" , FOR_V2(0x034EE2, 0x014A3B) },
+ { "America/Sao_Paulo" , FOR_V2(0x0350D7, 0x014B12) },
+ { "America/Scoresbysund" , FOR_V2(0x0358F8, 0x014E2A) },
+ { "America/Shiprock" , FOR_V2(0x0360A6, 0x01511B) },
+ { "America/Sitka" , FOR_V2(0x036A47, 0x0154A5) },
+ { "America/St_Barthelemy" , FOR_V2(0x037394, 0x01581D) },
+ { "America/St_Johns" , FOR_V2(0x03744A, 0x015877) },
+ { "America/St_Kitts" , FOR_V2(0x0382C8, 0x015DDD) },
+ { "America/St_Lucia" , FOR_V2(0x03837E, 0x015E37) },
+ { "America/St_Thomas" , FOR_V2(0x038434, 0x015E91) },
+ { "America/St_Vincent" , FOR_V2(0x0384EA, 0x015EEB) },
+ { "America/Swift_Current" , FOR_V2(0x0385A0, 0x015F45) },
+ { "America/Tegucigalpa" , FOR_V2(0x0387FC, 0x01604F) },
+ { "America/Thule" , FOR_V2(0x03891E, 0x0160D3) },
+ { "America/Thunder_Bay" , FOR_V2(0x038F30, 0x01631D) },
+ { "America/Tijuana" , FOR_V2(0x0397F9, 0x01666E) },
+ { "America/Toronto" , FOR_V2(0x03A15A, 0x0169FD) },
+ { "America/Tortola" , FOR_V2(0x03AF32, 0x016F1B) },
+ { "America/Vancouver" , FOR_V2(0x03AFE8, 0x016F75) },
+ { "America/Virgin" , FOR_V2(0x03BB62, 0x0173B8) },
+ { "America/Whitehorse" , FOR_V2(0x03BC18, 0x017412) },
+ { "America/Winnipeg" , FOR_V2(0x03C468, 0x01773D) },
+ { "America/Yakutat" , FOR_V2(0x03CFDC, 0x017B85) },
+ { "America/Yellowknife" , FOR_V2(0x03D902, 0x017EEE) },
+ { "Antarctica/Casey" , FOR_V2(0x03E0E1, 0x0181ED) },
+ { "Antarctica/Davis" , FOR_V2(0x03E202, 0x018276) },
+ { "Antarctica/DumontDUrville" , FOR_V2(0x03E335, 0x018304) },
+ { "Antarctica/Macquarie" , FOR_V2(0x03E434, 0x018385) },
+ { "Antarctica/Mawson" , FOR_V2(0x03EA4A, 0x0185DF) },
+ { "Antarctica/McMurdo" , FOR_V2(0x03EB28, 0x01864D) },
+ { "Antarctica/Palmer" , FOR_V2(0x03F4F6, 0x018A02) },
+ { "Antarctica/Rothera" , FOR_V2(0x03FCF2, 0x018D02) },
+ { "Antarctica/South_Pole" , FOR_V2(0x03FDB2, 0x018D64) },
+ { "Antarctica/Syowa" , FOR_V2(0x04075A, 0x0190F3) },
+ { "Antarctica/Troll" , FOR_V2(0x040819, 0x019153) },
+ { "Antarctica/Vostok" , FOR_V2(0x040CB3, 0x019311) },
+ { "Arctic/Longyearbyen" , FOR_V2(0x040D73, 0x019372) },
+ { "Asia/Aden" , FOR_V2(0x04164A, 0x0196B5) },
+ { "Asia/Almaty" , FOR_V2(0x041701, 0x01970F) },
+ { "Asia/Amman" , FOR_V2(0x041B2B, 0x0198C7) },
+ { "Asia/Anadyr" , FOR_V2(0x04228C, 0x019B82) },
+ { "Asia/Aqtau" , FOR_V2(0x042758, 0x019D71) },
+ { "Asia/Aqtobe" , FOR_V2(0x042B89, 0x019F39) },
+ { "Asia/Ashgabat" , FOR_V2(0x042FB9, 0x01A0EF) },
+ { "Asia/Ashkhabad" , FOR_V2(0x043264, 0x01A211) },
+ { "Asia/Baghdad" , FOR_V2(0x04350F, 0x01A333) },
+ { "Asia/Bahrain" , FOR_V2(0x0438F7, 0x01A4B9) },
+ { "Asia/Baku" , FOR_V2(0x0439D4, 0x01A524) },
+ { "Asia/Bangkok" , FOR_V2(0x043F33, 0x01A74A) },
+ { "Asia/Barnaul" , FOR_V2(0x04400B, 0x01A7B0) },
+ { "Asia/Beirut" , FOR_V2(0x04450C, 0x01A9AF) },
+ { "Asia/Bishkek" , FOR_V2(0x044D97, 0x01ACCD) },
+ { "Asia/Brunei" , FOR_V2(0x0451C8, 0x01AE7E) },
+ { "Asia/Calcutta" , FOR_V2(0x04529D, 0x01AEE5) },
+ { "Asia/Chita" , FOR_V2(0x0453CC, 0x01AF6F) },
+ { "Asia/Choibalsan" , FOR_V2(0x0458DE, 0x01B17E) },
+ { "Asia/Chongqing" , FOR_V2(0x045F2D, 0x01B3EA) },
+ { "Asia/Chungking" , FOR_V2(0x0460D7, 0x01B49B) },
+ { "Asia/Colombo" , FOR_V2(0x046281, 0x01B54C) },
+ { "Asia/Dacca" , FOR_V2(0x046412, 0x01B601) },
+ { "Asia/Damascus" , FOR_V2(0x0465A4, 0x01B6B8) },
+ { "Asia/Dhaka" , FOR_V2(0x046EC0, 0x01BA0D) },
+ { "Asia/Dili" , FOR_V2(0x047052, 0x01BAC4) },
+ { "Asia/Dubai" , FOR_V2(0x047193, 0x01BB5B) },
+ { "Asia/Dushanbe" , FOR_V2(0x04724A, 0x01BBB5) },
+ { "Asia/Gaza" , FOR_V2(0x0474B9, 0x01BCBD) },
+ { "Asia/Harbin" , FOR_V2(0x047DD8, 0x01C021) },
+ { "Asia/Hebron" , FOR_V2(0x047F82, 0x01C0D2) },
+ { "Asia/Ho_Chi_Minh" , FOR_V2(0x0488BC, 0x01C43F) },
+ { "Asia/Hong_Kong" , FOR_V2(0x048A3D, 0x01C4EE) },
+ { "Asia/Hovd" , FOR_V2(0x048EEE, 0x01C6BD) },
+ { "Asia/Irkutsk" , FOR_V2(0x04950E, 0x01C920) },
+ { "Asia/Istanbul" , FOR_V2(0x049A1F, 0x01CB2F) },
+ { "Asia/Jakarta" , FOR_V2(0x04A4E6, 0x01CF2D) },
+ { "Asia/Jayapura" , FOR_V2(0x04A671, 0x01CFE7) },
+ { "Asia/Jerusalem" , FOR_V2(0x04A7A4, 0x01D096) },
+ { "Asia/Kabul" , FOR_V2(0x04B089, 0x01D3DE) },
+ { "Asia/Kamchatka" , FOR_V2(0x04B15C, 0x01D440) },
+ { "Asia/Karachi" , FOR_V2(0x04B617, 0x01D626) },
+ { "Asia/Kashgar" , FOR_V2(0x04B7B6, 0x01D6E0) },
+ { "Asia/Kathmandu" , FOR_V2(0x04B86D, 0x01D73A) },
+ { "Asia/Katmandu" , FOR_V2(0x04B94D, 0x01D7A5) },
+ { "Asia/Khandyga" , FOR_V2(0x04BA2D, 0x01D810) },
+ { "Asia/Kolkata" , FOR_V2(0x04BF83, 0x01DA44) },
+ { "Asia/Krasnoyarsk" , FOR_V2(0x04C0B2, 0x01DACE) },
+ { "Asia/Kuala_Lumpur" , FOR_V2(0x04C5A1, 0x01DCD0) },
+ { "Asia/Kuching" , FOR_V2(0x04C74F, 0x01DD9F) },
+ { "Asia/Kuwait" , FOR_V2(0x04C970, 0x01DE91) },
+ { "Asia/Macao" , FOR_V2(0x04CA27, 0x01DEEB) },
+ { "Asia/Macau" , FOR_V2(0x04CD4E, 0x01E02B) },
+ { "Asia/Magadan" , FOR_V2(0x04D075, 0x01E16B) },
+ { "Asia/Makassar" , FOR_V2(0x04D56A, 0x01E369) },
+ { "Asia/Manila" , FOR_V2(0x04D6D7, 0x01E43C) },
+ { "Asia/Muscat" , FOR_V2(0x04D84C, 0x01E4DA) },
+ { "Asia/Nicosia" , FOR_V2(0x04D903, 0x01E534) },
+ { "Asia/Novokuznetsk" , FOR_V2(0x04E0EF, 0x01E821) },
+ { "Asia/Novosibirsk" , FOR_V2(0x04E5EC, 0x01EA26) },
+ { "Asia/Omsk" , FOR_V2(0x04EAC4, 0x01EC18) },
+ { "Asia/Oral" , FOR_V2(0x04EFA7, 0x01EE0E) },
+ { "Asia/Phnom_Penh" , FOR_V2(0x04F3C9, 0x01EFBE) },
+ { "Asia/Pontianak" , FOR_V2(0x04F4A1, 0x01F024) },
+ { "Asia/Pyongyang" , FOR_V2(0x04F63A, 0x01F0EC) },
+ { "Asia/Qatar" , FOR_V2(0x04F77E, 0x01F183) },
+ { "Asia/Qyzylorda" , FOR_V2(0x04F85B, 0x01F1EE) },
+ { "Asia/Rangoon" , FOR_V2(0x04FC9B, 0x01F3B4) },
+ { "Asia/Riyadh" , FOR_V2(0x04FDC4, 0x01F43D) },
+ { "Asia/Saigon" , FOR_V2(0x04FE7B, 0x01F497) },
+ { "Asia/Sakhalin" , FOR_V2(0x04FFFC, 0x01F546) },
+ { "Asia/Samarkand" , FOR_V2(0x050509, 0x01F752) },
+ { "Asia/Seoul" , FOR_V2(0x0507D9, 0x01F88F) },
+ { "Asia/Shanghai" , FOR_V2(0x050A20, 0x01F987) },
+ { "Asia/Singapore" , FOR_V2(0x050BD6, 0x01FA44) },
+ { "Asia/Srednekolymsk" , FOR_V2(0x050D8E, 0x01FB0C) },
+ { "Asia/Taipei" , FOR_V2(0x051291, 0x01FD1C) },
+ { "Asia/Tashkent" , FOR_V2(0x0515BD, 0x01FE5E) },
+ { "Asia/Tbilisi" , FOR_V2(0x051883, 0x01FF96) },
+ { "Asia/Tehran" , FOR_V2(0x051D05, 0x020169) },
+ { "Asia/Tel_Aviv" , FOR_V2(0x05239F, 0x0203E4) },
+ { "Asia/Thimbu" , FOR_V2(0x052C84, 0x02072C) },
+ { "Asia/Thimphu" , FOR_V2(0x052D61, 0x020797) },
+ { "Asia/Tokyo" , FOR_V2(0x052E3E, 0x020802) },
+ { "Asia/Tomsk" , FOR_V2(0x052FAD, 0x020899) },
+ { "Asia/Ujung_Pandang" , FOR_V2(0x0534AE, 0x020A98) },
+ { "Asia/Ulaanbaatar" , FOR_V2(0x0535D2, 0x020B22) },
+ { "Asia/Ulan_Bator" , FOR_V2(0x053BDC, 0x020D6F) },
+ { "Asia/Urumqi" , FOR_V2(0x0541D1, 0x020FA7) },
+ { "Asia/Ust-Nera" , FOR_V2(0x054295, 0x02100E) },
+ { "Asia/Vientiane" , FOR_V2(0x0547C2, 0x02122A) },
+ { "Asia/Vladivostok" , FOR_V2(0x05489A, 0x021290) },
+ { "Asia/Yakutsk" , FOR_V2(0x054D84, 0x02148C) },
+ { "Asia/Yekaterinburg" , FOR_V2(0x05526D, 0x021688) },
+ { "Asia/Yerevan" , FOR_V2(0x05579D, 0x0218A3) },
+ { "Atlantic/Azores" , FOR_V2(0x055CA4, 0x021AA5) },
+ { "Atlantic/Bermuda" , FOR_V2(0x056A56, 0x021FB9) },
+ { "Atlantic/Canary" , FOR_V2(0x057236, 0x02229F) },
+ { "Atlantic/Cape_Verde" , FOR_V2(0x0579C9, 0x02257A) },
+ { "Atlantic/Faeroe" , FOR_V2(0x057AD3, 0x0225F8) },
+ { "Atlantic/Faroe" , FOR_V2(0x058204, 0x0228A1) },
+ { "Atlantic/Jan_Mayen" , FOR_V2(0x058935, 0x022B4A) },
+ { "Atlantic/Madeira" , FOR_V2(0x05920C, 0x022E8D) },
+ { "Atlantic/Reykjavik" , FOR_V2(0x059FBD, 0x0233A7) },
+ { "Atlantic/South_Georgia" , FOR_V2(0x05A46F, 0x023579) },
+ { "Atlantic/St_Helena" , FOR_V2(0x05A50F, 0x0235BD) },
+ { "Atlantic/Stanley" , FOR_V2(0x05A5C5, 0x023617) },
+ { "Australia/ACT" , FOR_V2(0x05AAAF, 0x0237FE) },
+ { "Australia/Adelaide" , FOR_V2(0x05B36A, 0x023B32) },
+ { "Australia/Brisbane" , FOR_V2(0x05BC43, 0x023E71) },
+ { "Australia/Broken_Hill" , FOR_V2(0x05BE2A, 0x023F4B) },
+ { "Australia/Canberra" , FOR_V2(0x05C734, 0x02429C) },
+ { "Australia/Currie" , FOR_V2(0x05CFEF, 0x0245D0) },
+ { "Australia/Darwin" , FOR_V2(0x05D8C0, 0x02491A) },
+ { "Australia/Eucla" , FOR_V2(0x05DA21, 0x0249B3) },
+ { "Australia/Hobart" , FOR_V2(0x05DC2D, 0x024A9B) },
+ { "Australia/LHI" , FOR_V2(0x05E56D, 0x024E0C) },
+ { "Australia/Lindeman" , FOR_V2(0x05ECBC, 0x0250BE) },
+ { "Australia/Lord_Howe" , FOR_V2(0x05EEF1, 0x0251B9) },
+ { "Australia/Melbourne" , FOR_V2(0x05F650, 0x02547B) },
+ { "Australia/North" , FOR_V2(0x05FF13, 0x0257B7) },
+ { "Australia/NSW" , FOR_V2(0x060062, 0x02583E) },
+ { "Australia/Perth" , FOR_V2(0x06091D, 0x025B72) },
+ { "Australia/Queensland" , FOR_V2(0x060B26, 0x025C5D) },
+ { "Australia/South" , FOR_V2(0x060CF6, 0x025D20) },
+ { "Australia/Sydney" , FOR_V2(0x0615C0, 0x026050) },
+ { "Australia/Tasmania" , FOR_V2(0x061E97, 0x0263A0) },
+ { "Australia/Victoria" , FOR_V2(0x0627C2, 0x0266FC) },
+ { "Australia/West" , FOR_V2(0x06307D, 0x026A30) },
+ { "Australia/Yancowinna" , FOR_V2(0x063268, 0x026AFD) },
+ { "Brazil/Acre" , FOR_V2(0x063B56, 0x026E32) },
+ { "Brazil/DeNoronha" , FOR_V2(0x063DF2, 0x026F43) },
+ { "Brazil/East" , FOR_V2(0x0640D6, 0x027068) },
+ { "Brazil/West" , FOR_V2(0x0648C1, 0x02734A) },
+ { "Canada/Atlantic" , FOR_V2(0x064B35, 0x027447) },
+ { "Canada/Central" , FOR_V2(0x0658AF, 0x027934) },
+ { "Canada/East-Saskatchewan" , FOR_V2(0x066406, 0x027D5F) },
+ { "Canada/Eastern" , FOR_V2(0x0667F4, 0x027EED) },
+ { "Canada/Mountain" , FOR_V2(0x0675AF, 0x0283EE) },
+ { "Canada/Newfoundland" , FOR_V2(0x067F1D, 0x028769) },
+ { "Canada/Pacific" , FOR_V2(0x068D79, 0x028CAD) },
+ { "Canada/Saskatchewan" , FOR_V2(0x0698DA, 0x0290D7) },
+ { "Canada/Yukon" , FOR_V2(0x069CC8, 0x029265) },
+ { "CET" , FOR_V2(0x06A501, 0x029579) },
+ { "Chile/Continental" , FOR_V2(0x06AD43, 0x029882) },
+ { "Chile/EasterIsland" , FOR_V2(0x06B722, 0x029C26) },
+ { "CST6CDT" , FOR_V2(0x06BFDF, 0x029F60) },
+ { "Cuba" , FOR_V2(0x06C8E1, 0x02A2B1) },
+ { "EET" , FOR_V2(0x06D272, 0x02A635) },
+ { "Egypt" , FOR_V2(0x06D9D2, 0x02A8E8) },
+ { "Eire" , FOR_V2(0x06E695, 0x02ACC2) },
+ { "EST" , FOR_V2(0x06F488, 0x02B1E4) },
+ { "EST5EDT" , FOR_V2(0x06F513, 0x02B228) },
+ { "Etc/GMT" , FOR_V2(0x06FE15, 0x02B579) },
+ { "Etc/GMT+0" , FOR_V2(0x06FEA0, 0x02B5BD) },
+ { "Etc/GMT+1" , FOR_V2(0x06FF2B, 0x02B601) },
+ { "Etc/GMT+10" , FOR_V2(0x06FFD1, 0x02B651) },
+ { "Etc/GMT+11" , FOR_V2(0x07007B, 0x02B6A2) },
+ { "Etc/GMT+12" , FOR_V2(0x070125, 0x02B6F3) },
+ { "Etc/GMT+2" , FOR_V2(0x0701CF, 0x02B744) },
+ { "Etc/GMT+3" , FOR_V2(0x070275, 0x02B794) },
+ { "Etc/GMT+4" , FOR_V2(0x07031B, 0x02B7E4) },
+ { "Etc/GMT+5" , FOR_V2(0x0703C1, 0x02B834) },
+ { "Etc/GMT+6" , FOR_V2(0x070467, 0x02B884) },
+ { "Etc/GMT+7" , FOR_V2(0x07050D, 0x02B8D4) },
+ { "Etc/GMT+8" , FOR_V2(0x0705B3, 0x02B924) },
+ { "Etc/GMT+9" , FOR_V2(0x070659, 0x02B974) },
+ { "Etc/GMT-0" , FOR_V2(0x0706FF, 0x02B9C4) },
+ { "Etc/GMT-1" , FOR_V2(0x07078A, 0x02BA08) },
+ { "Etc/GMT-10" , FOR_V2(0x070831, 0x02BA58) },
+ { "Etc/GMT-11" , FOR_V2(0x0708DC, 0x02BAA9) },
+ { "Etc/GMT-12" , FOR_V2(0x070987, 0x02BAFA) },
+ { "Etc/GMT-13" , FOR_V2(0x070A32, 0x02BB4B) },
+ { "Etc/GMT-14" , FOR_V2(0x070ADD, 0x02BB9C) },
+ { "Etc/GMT-2" , FOR_V2(0x070B88, 0x02BBED) },
+ { "Etc/GMT-3" , FOR_V2(0x070C2F, 0x02BC3D) },
+ { "Etc/GMT-4" , FOR_V2(0x070CD6, 0x02BC8D) },
+ { "Etc/GMT-5" , FOR_V2(0x070D7D, 0x02BCDD) },
+ { "Etc/GMT-6" , FOR_V2(0x070E24, 0x02BD2D) },
+ { "Etc/GMT-7" , FOR_V2(0x070ECB, 0x02BD7D) },
+ { "Etc/GMT-8" , FOR_V2(0x070F72, 0x02BDCD) },
+ { "Etc/GMT-9" , FOR_V2(0x071019, 0x02BE1D) },
+ { "Etc/GMT0" , FOR_V2(0x0710C0, 0x02BE6D) },
+ { "Etc/Greenwich" , FOR_V2(0x07114B, 0x02BEB1) },
+ { "Etc/UCT" , FOR_V2(0x0711D6, 0x02BEF5) },
+ { "Etc/Universal" , FOR_V2(0x071261, 0x02BF39) },
+ { "Etc/UTC" , FOR_V2(0x0712EC, 0x02BF7D) },
+ { "Etc/Zulu" , FOR_V2(0x071377, 0x02BFC1) },
+ { "Europe/Amsterdam" , FOR_V2(0x071402, 0x02C005) },
+ { "Europe/Andorra" , FOR_V2(0x071F8D, 0x02C454) },
+ { "Europe/Astrakhan" , FOR_V2(0x072670, 0x02C6E1) },
+ { "Europe/Athens" , FOR_V2(0x072B3B, 0x02C8CD) },
+ { "Europe/Belfast" , FOR_V2(0x073426, 0x02CC21) },
+ { "Europe/Belgrade" , FOR_V2(0x074299, 0x02D169) },
+ { "Europe/Berlin" , FOR_V2(0x074A4A, 0x02D443) },
+ { "Europe/Bratislava" , FOR_V2(0x075389, 0x02D7BE) },
+ { "Europe/Brussels" , FOR_V2(0x075C75, 0x02DB01) },
+ { "Europe/Bucharest" , FOR_V2(0x07681B, 0x02DF49) },
+ { "Europe/Budapest" , FOR_V2(0x0770D4, 0x02E284) },
+ { "Europe/Busingen" , FOR_V2(0x077A45, 0x02E5FE) },
+ { "Europe/Chisinau" , FOR_V2(0x0781D7, 0x02E8C6) },
+ { "Europe/Copenhagen" , FOR_V2(0x078B70, 0x02EC6A) },
+ { "Europe/Dublin" , FOR_V2(0x0793EC, 0x02EF85) },
+ { "Europe/Gibraltar" , FOR_V2(0x07A1DF, 0x02F4A7) },
+ { "Europe/Guernsey" , FOR_V2(0x07ADE0, 0x02F90F) },
+ { "Europe/Helsinki" , FOR_V2(0x07BC53, 0x02FE57) },
+ { "Europe/Isle_of_Man" , FOR_V2(0x07C3D4, 0x03011E) },
+ { "Europe/Istanbul" , FOR_V2(0x07D247, 0x030666) },
+ { "Europe/Jersey" , FOR_V2(0x07DD0E, 0x030A64) },
+ { "Europe/Kaliningrad" , FOR_V2(0x07EB81, 0x030FAC) },
+ { "Europe/Kiev" , FOR_V2(0x07F18F, 0x03121D) },
+ { "Europe/Kirov" , FOR_V2(0x07F9E0, 0x031550) },
+ { "Europe/Lisbon" , FOR_V2(0x07FE89, 0x03172B) },
+ { "Europe/Ljubljana" , FOR_V2(0x080C25, 0x031C3F) },
+ { "Europe/London" , FOR_V2(0x0813D6, 0x031F19) },
+ { "Europe/Luxembourg" , FOR_V2(0x082249, 0x032461) },
+ { "Europe/Madrid" , FOR_V2(0x082DF3, 0x0328BC) },
+ { "Europe/Malta" , FOR_V2(0x08384A, 0x032C9B) },
+ { "Europe/Mariehamn" , FOR_V2(0x08429B, 0x033065) },
+ { "Europe/Minsk" , FOR_V2(0x084A1C, 0x03332C) },
+ { "Europe/Monaco" , FOR_V2(0x084F80, 0x033550) },
+ { "Europe/Moscow" , FOR_V2(0x085B15, 0x03399C) },
+ { "Europe/Nicosia" , FOR_V2(0x08612D, 0x033C14) },
+ { "Europe/Oslo" , FOR_V2(0x086919, 0x033F01) },
+ { "Europe/Paris" , FOR_V2(0x0871F0, 0x034244) },
+ { "Europe/Podgorica" , FOR_V2(0x087D97, 0x03469B) },
+ { "Europe/Prague" , FOR_V2(0x088548, 0x034975) },
+ { "Europe/Riga" , FOR_V2(0x088E34, 0x034CB8) },
+ { "Europe/Rome" , FOR_V2(0x0896FB, 0x03500E) },
+ { "Europe/Samara" , FOR_V2(0x08A17D, 0x0353E2) },
+ { "Europe/San_Marino" , FOR_V2(0x08A6E4, 0x035620) },
+ { "Europe/Sarajevo" , FOR_V2(0x08B166, 0x0359F4) },
+ { "Europe/Simferopol" , FOR_V2(0x08B917, 0x035CCE) },
+ { "Europe/Skopje" , FOR_V2(0x08BF04, 0x035F30) },
+ { "Europe/Sofia" , FOR_V2(0x08C6B5, 0x03620A) },
+ { "Europe/Stockholm" , FOR_V2(0x08CF13, 0x036523) },
+ { "Europe/Tallinn" , FOR_V2(0x08D69D, 0x0367E3) },
+ { "Europe/Tirane" , FOR_V2(0x08DF34, 0x036B29) },
+ { "Europe/Tiraspol" , FOR_V2(0x08E772, 0x036E34) },
+ { "Europe/Ulyanovsk" , FOR_V2(0x08F10B, 0x0371D8) },
+ { "Europe/Uzhgorod" , FOR_V2(0x08F62A, 0x0373EA) },
+ { "Europe/Vaduz" , FOR_V2(0x08FE75, 0x037712) },
+ { "Europe/Vatican" , FOR_V2(0x0905FF, 0x0379D2) },
+ { "Europe/Vienna" , FOR_V2(0x091081, 0x037DA6) },
+ { "Europe/Vilnius" , FOR_V2(0x09194A, 0x0380E4) },
+ { "Europe/Volgograd" , FOR_V2(0x0921ED, 0x038434) },
+ { "Europe/Warsaw" , FOR_V2(0x092731, 0x038662) },
+ { "Europe/Zagreb" , FOR_V2(0x0931CE, 0x038A54) },
+ { "Europe/Zaporozhye" , FOR_V2(0x09397F, 0x038D2E) },
+ { "Europe/Zurich" , FOR_V2(0x0941F8, 0x039080) },
+ { "Factory" , FOR_V2(0x094982, 0x039340) },
+ { "GB" , FOR_V2(0x094AA9, 0x0393BB) },
+ { "GB-Eire" , FOR_V2(0x09591C, 0x039903) },
+ { "GMT" , FOR_V2(0x09678F, 0x039E4B) },
+ { "GMT+0" , FOR_V2(0x09681A, 0x039E8F) },
+ { "GMT-0" , FOR_V2(0x0968A5, 0x039ED3) },
+ { "GMT0" , FOR_V2(0x096930, 0x039F17) },
+ { "Greenwich" , FOR_V2(0x0969BB, 0x039F5B) },
+ { "Hongkong" , FOR_V2(0x096A46, 0x039F9F) },
+ { "HST" , FOR_V2(0x096EF7, 0x03A16E) },
+ { "Iceland" , FOR_V2(0x096F83, 0x03A1B2) },
+ { "Indian/Antananarivo" , FOR_V2(0x097435, 0x03A384) },
+ { "Indian/Chagos" , FOR_V2(0x09755C, 0x03A410) },
+ { "Indian/Christmas" , FOR_V2(0x097631, 0x03A477) },
+ { "Indian/Cocos" , FOR_V2(0x0976D2, 0x03A4BB) },
+ { "Indian/Comoro" , FOR_V2(0x097776, 0x03A4FF) },
+ { "Indian/Kerguelen" , FOR_V2(0x09789D, 0x03A58B) },
+ { "Indian/Mahe" , FOR_V2(0x097954, 0x03A5E5) },
+ { "Indian/Maldives" , FOR_V2(0x097A0B, 0x03A63F) },
+ { "Indian/Mauritius" , FOR_V2(0x097AE3, 0x03A6A5) },
+ { "Indian/Mayotte" , FOR_V2(0x097BEC, 0x03A720) },
+ { "Indian/Reunion" , FOR_V2(0x097D13, 0x03A7AC) },
+ { "Iran" , FOR_V2(0x097DCA, 0x03A806) },
+ { "Israel" , FOR_V2(0x098464, 0x03AA81) },
+ { "Jamaica" , FOR_V2(0x098D49, 0x03ADC9) },
+ { "Japan" , FOR_V2(0x098F50, 0x03AE9F) },
+ { "Kwajalein" , FOR_V2(0x0990BF, 0x03AF36) },
+ { "Libya" , FOR_V2(0x0991B8, 0x03AFAA) },
+ { "MET" , FOR_V2(0x099453, 0x03B0B8) },
+ { "Mexico/BajaNorte" , FOR_V2(0x099C95, 0x03B3C1) },
+ { "Mexico/BajaSur" , FOR_V2(0x09A5D5, 0x03B72F) },
+ { "Mexico/General" , FOR_V2(0x09ABFD, 0x03B981) },
+ { "MST" , FOR_V2(0x09B25B, 0x03BBE4) },
+ { "MST7MDT" , FOR_V2(0x09B2E6, 0x03BC28) },
+ { "Navajo" , FOR_V2(0x09BBE8, 0x03BF79) },
+ { "NZ" , FOR_V2(0x09C589, 0x03C303) },
+ { "NZ-CHAT" , FOR_V2(0x09CF31, 0x03C692) },
+ { "Pacific/Apia" , FOR_V2(0x09D746, 0x03C987) },
+ { "Pacific/Auckland" , FOR_V2(0x09DBA0, 0x03CB30) },
+ { "Pacific/Bougainville" , FOR_V2(0x09E560, 0x03CED7) },
+ { "Pacific/Chatham" , FOR_V2(0x09E690, 0x03CF60) },
+ { "Pacific/Chuuk" , FOR_V2(0x09EEB4, 0x03D264) },
+ { "Pacific/Easter" , FOR_V2(0x09EF68, 0x03D2B8) },
+ { "Pacific/Efate" , FOR_V2(0x09F832, 0x03D5FF) },
+ { "Pacific/Enderbury" , FOR_V2(0x09FA1C, 0x03D6CA) },
+ { "Pacific/Fakaofo" , FOR_V2(0x09FB1D, 0x03D749) },
+ { "Pacific/Fiji" , FOR_V2(0x09FBEE, 0x03D7AB) },
+ { "Pacific/Funafuti" , FOR_V2(0x0A002B, 0x03D943) },
+ { "Pacific/Galapagos" , FOR_V2(0x0A00CD, 0x03D987) },
+ { "Pacific/Gambier" , FOR_V2(0x0A01BD, 0x03DA04) },
+ { "Pacific/Guadalcanal" , FOR_V2(0x0A0285, 0x03DA6E) },
+ { "Pacific/Guam" , FOR_V2(0x0A033D, 0x03DAC8) },
+ { "Pacific/Honolulu" , FOR_V2(0x0A042A, 0x03DB2F) },
+ { "Pacific/Johnston" , FOR_V2(0x0A0550, 0x03DBB7) },
+ { "Pacific/Kiritimati" , FOR_V2(0x0A067E, 0x03DC47) },
+ { "Pacific/Kosrae" , FOR_V2(0x0A077C, 0x03DCC3) },
+ { "Pacific/Kwajalein" , FOR_V2(0x0A0874, 0x03DD39) },
+ { "Pacific/Majuro" , FOR_V2(0x0A0976, 0x03DDB6) },
+ { "Pacific/Marquesas" , FOR_V2(0x0A0A64, 0x03DE35) },
+ { "Pacific/Midway" , FOR_V2(0x0A0B31, 0x03DEA1) },
+ { "Pacific/Nauru" , FOR_V2(0x0A0C5B, 0x03DF33) },
+ { "Pacific/Niue" , FOR_V2(0x0A0D65, 0x03DFB0) },
+ { "Pacific/Norfolk" , FOR_V2(0x0A0E53, 0x03E01F) },
+ { "Pacific/Noumea" , FOR_V2(0x0A0F80, 0x03E0A9) },
+ { "Pacific/Pago_Pago" , FOR_V2(0x0A10C6, 0x03E13E) },
+ { "Pacific/Palau" , FOR_V2(0x0A11E2, 0x03E1C2) },
+ { "Pacific/Pitcairn" , FOR_V2(0x0A1283, 0x03E206) },
+ { "Pacific/Pohnpei" , FOR_V2(0x0A135A, 0x03E26C) },
+ { "Pacific/Ponape" , FOR_V2(0x0A140D, 0x03E2BF) },
+ { "Pacific/Port_Moresby" , FOR_V2(0x0A14B2, 0x03E304) },
+ { "Pacific/Rarotonga" , FOR_V2(0x0A1587, 0x03E365) },
+ { "Pacific/Saipan" , FOR_V2(0x0A17D1, 0x03E452) },
+ { "Pacific/Samoa" , FOR_V2(0x0A18BE, 0x03E4B9) },
+ { "Pacific/Tahiti" , FOR_V2(0x0A19DA, 0x03E53D) },
+ { "Pacific/Tarawa" , FOR_V2(0x0A1AA3, 0x03E5A7) },
+ { "Pacific/Tongatapu" , FOR_V2(0x0A1B57, 0x03E5FB) },
+ { "Pacific/Truk" , FOR_V2(0x0A1CB6, 0x03E698) },
+ { "Pacific/Wake" , FOR_V2(0x0A1D5B, 0x03E6DD) },
+ { "Pacific/Wallis" , FOR_V2(0x0A1E0B, 0x03E72D) },
+ { "Pacific/Yap" , FOR_V2(0x0A1EAD, 0x03E771) },
+ { "Poland" , FOR_V2(0x0A1F52, 0x03E7B6) },
+ { "Portugal" , FOR_V2(0x0A29EF, 0x03EBA8) },
+ { "PRC" , FOR_V2(0x0A3778, 0x03F0A9) },
+ { "PST8PDT" , FOR_V2(0x0A3922, 0x03F15A) },
+ { "ROC" , FOR_V2(0x0A4224, 0x03F4AB) },
+ { "ROK" , FOR_V2(0x0A4550, 0x03F5ED) },
+ { "Singapore" , FOR_V2(0x0A4797, 0x03F6E5) },
+ { "Turkey" , FOR_V2(0x0A494F, 0x03F7AD) },
+ { "UCT" , FOR_V2(0x0A5416, 0x03FBAB) },
+ { "Universal" , FOR_V2(0x0A54A1, 0x03FBEF) },
+ { "US/Alaska" , FOR_V2(0x0A552C, 0x03FC33) },
+ { "US/Aleutian" , FOR_V2(0x0A5E88, 0x03FFAD) },
+ { "US/Arizona" , FOR_V2(0x0A67D1, 0x04031E) },
+ { "US/Central" , FOR_V2(0x0A693E, 0x0403BD) },
+ { "US/East-Indiana" , FOR_V2(0x0A774B, 0x0408E1) },
+ { "US/Eastern" , FOR_V2(0x0A7DE2, 0x040B5C) },
+ { "US/Hawaii" , FOR_V2(0x0A8BC7, 0x04106C) },
+ { "US/Indiana-Starke" , FOR_V2(0x0A8CE7, 0x0410EE) },
+ { "US/Michigan" , FOR_V2(0x0A9678, 0x041478) },
+ { "US/Mountain" , FOR_V2(0x0A9F2C, 0x0417B4) },
+ { "US/Pacific" , FOR_V2(0x0AA8CD, 0x041B3E) },
+ { "US/Pacific-New" , FOR_V2(0x0AB3F6, 0x041F54) },
+ { "US/Samoa" , FOR_V2(0x0ABF1F, 0x04236A) },
+ { "UTC" , FOR_V2(0x0AC03B, 0x0423EE) },
+ { "W-SU" , FOR_V2(0x0AC0C6, 0x042432) },
+ { "WET" , FOR_V2(0x0AC6CA, 0x042696) },
+ { "Zulu" , FOR_V2(0x0ACE27, 0x042949) },
};
#ifdef TIMELIB_SUPPORTS_V2DATA
-const unsigned char timelib_timezone_db_data_builtin[702875] = {
+const unsigned char timelib_timezone_db_data_builtin[708274] = {
#else
-const unsigned char timelib_timezone_db_data_builtin[271132] = {
+const unsigned char timelib_timezone_db_data_builtin[272781] = {
#endif
@@ -915,7 +917,7 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
/* Africa/Cairo */
0x50, 0x48, 0x50, 0x32, 0x01, 0x45, 0x47, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0D, 0x80, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0xB1, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0D, 0x80, 0x00, 0x00, 0x00,
0xC8, 0x93, 0xB4, 0xE0, 0xC8, 0xFA, 0x7B, 0xD0, 0xC9, 0xFC, 0xEF, 0xE0, 0xCA, 0xC7, 0xE8, 0xD0,
0xCB, 0xCB, 0xAE, 0x60, 0xCC, 0xDF, 0x29, 0xD0, 0xCD, 0xAC, 0xE1, 0xE0, 0xCE, 0xC6, 0xF4, 0xD0,
0xCF, 0x8F, 0x66, 0xE0, 0xD0, 0xA9, 0x79, 0xD0, 0xD1, 0x84, 0x60, 0xE0, 0xD2, 0x8A, 0xAD, 0x50,
@@ -947,22 +949,37 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x46, 0x31, 0x20, 0xE0, 0x46, 0xE0, 0x6A, 0x50, 0x48, 0x11, 0x02, 0xE0, 0x48, 0xB7, 0x11, 0xD0,
0x49, 0xF0, 0xE4, 0xE0, 0x4A, 0x8D, 0xB9, 0x50, 0x4B, 0xDA, 0x01, 0x60, 0x4C, 0x61, 0xBD, 0xD0,
0x4C, 0x89, 0x58, 0xE0, 0x4C, 0xA4, 0xFA, 0x50, 0x53, 0x75, 0x38, 0xE0, 0x53, 0xAC, 0x89, 0xD0,
-0x53, 0xDA, 0xBC, 0x60, 0x54, 0x24, 0x82, 0x50, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01,
+0x53, 0xDA, 0xBC, 0x60, 0x54, 0x24, 0x82, 0x50, 0x57, 0x7E, 0xD0, 0xE0, 0x58, 0x12, 0x6A, 0xD0,
+0x59, 0x02, 0x69, 0xE0, 0x59, 0x27, 0x45, 0xD0, 0x59, 0x55, 0x78, 0x60, 0x59, 0xF2, 0x4C, 0xD0,
+0x5A, 0xE2, 0x4B, 0xE0, 0x5A, 0xF4, 0xB2, 0xD0, 0x5B, 0x22, 0xE5, 0x60, 0x5B, 0xD2, 0x2E, 0xD0,
+0x5C, 0xC2, 0x2D, 0xE0, 0x5C, 0xCB, 0x5A, 0x50, 0x5C, 0xF9, 0x8C, 0xE0, 0x5D, 0xBB, 0x4B, 0x50,
+0x5E, 0xD0, 0x34, 0x60, 0x5F, 0x9B, 0x2D, 0x50, 0x60, 0x9D, 0xA1, 0x60, 0x61, 0x7B, 0x0F, 0x50,
+0x62, 0x74, 0x48, 0xE0, 0x63, 0x5A, 0xF1, 0x50, 0x64, 0x4A, 0xF0, 0x60, 0x65, 0x3A, 0xD3, 0x50,
+0x66, 0x2A, 0xD2, 0x60, 0x67, 0x23, 0xEF, 0xD0, 0x68, 0x0A, 0xB4, 0x60, 0x69, 0x03, 0xD1, 0xD0,
+0x69, 0xF3, 0xD0, 0xE0, 0x6A, 0xE3, 0xB3, 0xD0, 0x6B, 0xD3, 0xB2, 0xE0, 0x6C, 0xC3, 0x95, 0xD0,
+0x6D, 0xB3, 0x94, 0xE0, 0x6E, 0xA3, 0x77, 0xD0, 0x6F, 0x93, 0x76, 0xE0, 0x70, 0x83, 0x59, 0xD0,
+0x71, 0x73, 0x58, 0xE0, 0x72, 0x6C, 0x76, 0x50, 0x73, 0x53, 0x3A, 0xE0, 0x74, 0x4C, 0x58, 0x50,
+0x75, 0x3C, 0x57, 0x60, 0x76, 0x2C, 0x3A, 0x50, 0x77, 0x1C, 0x39, 0x60, 0x78, 0x0C, 0x1C, 0x50,
+0x78, 0xFC, 0x1B, 0x60, 0x79, 0xEB, 0xFE, 0x50, 0x7A, 0xDB, 0xFD, 0x60, 0x7B, 0xCB, 0xE0, 0x50,
+0x7C, 0xBB, 0xDF, 0x60, 0x7D, 0xA2, 0x87, 0xD0, 0x7E, 0xA4, 0xFB, 0xE0, 0x7F, 0x79, 0x2F, 0x50,
0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01,
0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01,
0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01,
0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01,
0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01,
+0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03,
0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03,
-0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03,
-0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x00, 0x00, 0x1D, 0x55, 0x00, 0x00, 0x00, 0x00, 0x2A,
-0x30, 0x01, 0x04, 0x00, 0x00, 0x1C, 0x20, 0x00, 0x09, 0x00, 0x00, 0x2A, 0x30, 0x01, 0x04, 0x4C,
-0x4D, 0x54, 0x00, 0x45, 0x45, 0x53, 0x54, 0x00, 0x45, 0x45, 0x54, 0x00, 0x00, 0x00, 0x00, 0x01,
-0x00, 0x00, 0x00, 0x00,
+0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01,
+0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01,
+0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01,
+0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01,
+0x02, 0x00, 0x00, 0x1D, 0x55, 0x00, 0x00, 0x00, 0x00, 0x2A, 0x30, 0x01, 0x04, 0x00, 0x00, 0x1C,
+0x20, 0x00, 0x09, 0x00, 0x00, 0x2A, 0x30, 0x01, 0x04, 0x4C, 0x4D, 0x54, 0x00, 0x45, 0x45, 0x53,
+0x54, 0x00, 0x45, 0x45, 0x54, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
#ifdef TIMELIB_SUPPORTS_V2DATA
0x54, 0x5A, 0x69, 0x66, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0D, 0xF8, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0xF0, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0D, 0xF8, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x7D, 0xBD, 0x4D, 0xAB, 0xFF, 0xFF, 0xFF, 0xFF,
0xC8, 0x93, 0xB4, 0xE0, 0xFF, 0xFF, 0xFF, 0xFF, 0xC8, 0xFA, 0x7B, 0xD0, 0xFF, 0xFF, 0xFF, 0xFF,
0xC9, 0xFC, 0xEF, 0xE0, 0xFF, 0xFF, 0xFF, 0xFF, 0xCA, 0xC7, 0xE8, 0xD0, 0xFF, 0xFF, 0xFF, 0xFF,
@@ -1026,7 +1043,63 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x4B, 0xDA, 0x01, 0x60, 0x00, 0x00, 0x00, 0x00, 0x4C, 0x61, 0xBD, 0xD0, 0x00, 0x00, 0x00, 0x00,
0x4C, 0x89, 0x58, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x4C, 0xA4, 0xFA, 0x50, 0x00, 0x00, 0x00, 0x00,
0x53, 0x75, 0x38, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x53, 0xAC, 0x89, 0xD0, 0x00, 0x00, 0x00, 0x00,
-0x53, 0xDA, 0xBC, 0x60, 0x00, 0x00, 0x00, 0x00, 0x54, 0x24, 0x82, 0x50, 0x00, 0x02, 0x01, 0x02,
+0x53, 0xDA, 0xBC, 0x60, 0x00, 0x00, 0x00, 0x00, 0x54, 0x24, 0x82, 0x50, 0x00, 0x00, 0x00, 0x00,
+0x57, 0x7E, 0xD0, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x58, 0x12, 0x6A, 0xD0, 0x00, 0x00, 0x00, 0x00,
+0x59, 0x02, 0x69, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x59, 0x27, 0x45, 0xD0, 0x00, 0x00, 0x00, 0x00,
+0x59, 0x55, 0x78, 0x60, 0x00, 0x00, 0x00, 0x00, 0x59, 0xF2, 0x4C, 0xD0, 0x00, 0x00, 0x00, 0x00,
+0x5A, 0xE2, 0x4B, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x5A, 0xF4, 0xB2, 0xD0, 0x00, 0x00, 0x00, 0x00,
+0x5B, 0x22, 0xE5, 0x60, 0x00, 0x00, 0x00, 0x00, 0x5B, 0xD2, 0x2E, 0xD0, 0x00, 0x00, 0x00, 0x00,
+0x5C, 0xC2, 0x2D, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x5C, 0xCB, 0x5A, 0x50, 0x00, 0x00, 0x00, 0x00,
+0x5C, 0xF9, 0x8C, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x5D, 0xBB, 0x4B, 0x50, 0x00, 0x00, 0x00, 0x00,
+0x5E, 0xD0, 0x34, 0x60, 0x00, 0x00, 0x00, 0x00, 0x5F, 0x9B, 0x2D, 0x50, 0x00, 0x00, 0x00, 0x00,
+0x60, 0x9D, 0xA1, 0x60, 0x00, 0x00, 0x00, 0x00, 0x61, 0x7B, 0x0F, 0x50, 0x00, 0x00, 0x00, 0x00,
+0x62, 0x74, 0x48, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x63, 0x5A, 0xF1, 0x50, 0x00, 0x00, 0x00, 0x00,
+0x64, 0x4A, 0xF0, 0x60, 0x00, 0x00, 0x00, 0x00, 0x65, 0x3A, 0xD3, 0x50, 0x00, 0x00, 0x00, 0x00,
+0x66, 0x2A, 0xD2, 0x60, 0x00, 0x00, 0x00, 0x00, 0x67, 0x23, 0xEF, 0xD0, 0x00, 0x00, 0x00, 0x00,
+0x68, 0x0A, 0xB4, 0x60, 0x00, 0x00, 0x00, 0x00, 0x69, 0x03, 0xD1, 0xD0, 0x00, 0x00, 0x00, 0x00,
+0x69, 0xF3, 0xD0, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x6A, 0xE3, 0xB3, 0xD0, 0x00, 0x00, 0x00, 0x00,
+0x6B, 0xD3, 0xB2, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x6C, 0xC3, 0x95, 0xD0, 0x00, 0x00, 0x00, 0x00,
+0x6D, 0xB3, 0x94, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x6E, 0xA3, 0x77, 0xD0, 0x00, 0x00, 0x00, 0x00,
+0x6F, 0x93, 0x76, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x70, 0x83, 0x59, 0xD0, 0x00, 0x00, 0x00, 0x00,
+0x71, 0x73, 0x58, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x72, 0x6C, 0x76, 0x50, 0x00, 0x00, 0x00, 0x00,
+0x73, 0x53, 0x3A, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x74, 0x4C, 0x58, 0x50, 0x00, 0x00, 0x00, 0x00,
+0x75, 0x3C, 0x57, 0x60, 0x00, 0x00, 0x00, 0x00, 0x76, 0x2C, 0x3A, 0x50, 0x00, 0x00, 0x00, 0x00,
+0x77, 0x1C, 0x39, 0x60, 0x00, 0x00, 0x00, 0x00, 0x78, 0x0C, 0x1C, 0x50, 0x00, 0x00, 0x00, 0x00,
+0x78, 0xFC, 0x1B, 0x60, 0x00, 0x00, 0x00, 0x00, 0x79, 0xEB, 0xFE, 0x50, 0x00, 0x00, 0x00, 0x00,
+0x7A, 0xDB, 0xFD, 0x60, 0x00, 0x00, 0x00, 0x00, 0x7B, 0xCB, 0xE0, 0x50, 0x00, 0x00, 0x00, 0x00,
+0x7C, 0xBB, 0xDF, 0x60, 0x00, 0x00, 0x00, 0x00, 0x7D, 0xA2, 0x87, 0xD0, 0x00, 0x00, 0x00, 0x00,
+0x7E, 0xA4, 0xFB, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x79, 0x2F, 0x50, 0x00, 0x00, 0x00, 0x00,
+0x80, 0x84, 0xDD, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x81, 0x46, 0x9C, 0x50, 0x00, 0x00, 0x00, 0x00,
+0x82, 0x64, 0xBF, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x83, 0x1D, 0x43, 0xD0, 0x00, 0x00, 0x00, 0x00,
+0x83, 0x4B, 0x76, 0x60, 0x00, 0x00, 0x00, 0x00, 0x83, 0x54, 0xA2, 0xD0, 0x00, 0x00, 0x00, 0x00,
+0x84, 0x44, 0xA1, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x84, 0xF3, 0xEB, 0x50, 0x00, 0x00, 0x00, 0x00,
+0x85, 0x22, 0x1D, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x85, 0x34, 0x84, 0xD0, 0x00, 0x00, 0x00, 0x00,
+0x86, 0x24, 0x83, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x86, 0xC1, 0x58, 0x50, 0x00, 0x00, 0x00, 0x00,
+0x86, 0xEF, 0x8A, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x87, 0x1D, 0xA1, 0x50, 0x00, 0x00, 0x00, 0x00,
+0x88, 0x04, 0x65, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x88, 0x97, 0xFF, 0xD0, 0x00, 0x00, 0x00, 0x00,
+0x88, 0xC6, 0x32, 0x60, 0x00, 0x00, 0x00, 0x00, 0x88, 0xFD, 0x83, 0x50, 0x00, 0x00, 0x00, 0x00,
+0x89, 0xED, 0x82, 0x60, 0x00, 0x00, 0x00, 0x00, 0x8A, 0x6E, 0xA7, 0x50, 0x00, 0x00, 0x00, 0x00,
+0x8A, 0x9C, 0xD9, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x8A, 0xDD, 0x65, 0x50, 0x00, 0x00, 0x00, 0x00,
+0x8B, 0xCD, 0x64, 0x60, 0x00, 0x00, 0x00, 0x00, 0x8C, 0x3C, 0x14, 0x50, 0x00, 0x00, 0x00, 0x00,
+0x8C, 0x6A, 0x46, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x8C, 0xBD, 0x47, 0x50, 0x00, 0x00, 0x00, 0x00,
+0x8D, 0xAD, 0x46, 0x60, 0x00, 0x00, 0x00, 0x00, 0x8E, 0x12, 0xBB, 0xD0, 0x00, 0x00, 0x00, 0x00,
+0x8E, 0x40, 0xEE, 0x60, 0x00, 0x00, 0x00, 0x00, 0x8E, 0x9D, 0x29, 0x50, 0x00, 0x00, 0x00, 0x00,
+0x8F, 0x8D, 0x28, 0x60, 0x00, 0x00, 0x00, 0x00, 0x8F, 0xE0, 0x28, 0xD0, 0x00, 0x00, 0x00, 0x00,
+0x90, 0x17, 0x95, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x90, 0x7D, 0x0B, 0x50, 0x00, 0x00, 0x00, 0x00,
+0x91, 0x6D, 0x0A, 0x60, 0x00, 0x00, 0x00, 0x00, 0x91, 0xB6, 0xD0, 0x50, 0x00, 0x00, 0x00, 0x00,
+0x91, 0xE5, 0x02, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x92, 0x66, 0x27, 0xD0, 0x00, 0x00, 0x00, 0x00,
+0x93, 0x56, 0x26, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x93, 0x8D, 0x77, 0xD0, 0x00, 0x00, 0x00, 0x00,
+0x93, 0xBB, 0xAA, 0x60, 0x00, 0x00, 0x00, 0x00, 0x94, 0x46, 0x09, 0xD0, 0x00, 0x00, 0x00, 0x00,
+0x95, 0x36, 0x08, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x95, 0x5A, 0xE4, 0xD0, 0x00, 0x00, 0x00, 0x00,
+0x95, 0x89, 0x17, 0x60, 0x00, 0x00, 0x00, 0x00, 0x96, 0x25, 0xEB, 0xD0, 0x00, 0x00, 0x00, 0x00,
+0x97, 0x15, 0xEA, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x97, 0x31, 0x8C, 0x50, 0x00, 0x00, 0x00, 0x00,
+0x97, 0x5F, 0xBE, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x98, 0x05, 0xCD, 0xD0, 0x00, 0x00, 0x00, 0x00,
+0x98, 0xF5, 0xCC, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x99, 0x08, 0x33, 0xD0, 0x00, 0x00, 0x00, 0x00,
+0x99, 0x36, 0x66, 0x60, 0x00, 0x00, 0x00, 0x00, 0x99, 0xE5, 0xAF, 0xD0, 0x00, 0x00, 0x00, 0x00,
+0x9B, 0x03, 0xD3, 0x60, 0x00, 0x00, 0x00, 0x00, 0x9B, 0xCE, 0xCC, 0x50, 0x00, 0x00, 0x00, 0x00,
+0x9C, 0xDA, 0x7A, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x9D, 0xAE, 0xAE, 0x50, 0x00, 0x00, 0x00, 0x00,
+0x9E, 0xB1, 0x22, 0x60, 0x00, 0x00, 0x00, 0x00, 0x9F, 0x8E, 0x90, 0x50, 0x00, 0x00, 0x00, 0x00,
+0xA0, 0x7E, 0x8F, 0x60, 0x00, 0x00, 0x00, 0x00, 0xA1, 0x6E, 0x72, 0x50, 0x00, 0x02, 0x01, 0x02,
0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
@@ -1034,10 +1107,19 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02,
0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02,
-0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x00, 0x00, 0x1D, 0x55,
+0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
+0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
+0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
+0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
+0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
+0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
+0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
+0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x00, 0x00, 0x1D, 0x55,
0x00, 0x00, 0x00, 0x00, 0x2A, 0x30, 0x01, 0x04, 0x00, 0x00, 0x1C, 0x20, 0x00, 0x09, 0x00, 0x00,
0x2A, 0x30, 0x01, 0x04, 0x4C, 0x4D, 0x54, 0x00, 0x45, 0x45, 0x53, 0x54, 0x00, 0x45, 0x45, 0x54,
-0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x45, 0x45, 0x54, 0x2D, 0x32, 0x0A,
+0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x45, 0x45, 0x54, 0x2D, 0x32, 0x45,
+0x45, 0x53, 0x54, 0x2C, 0x4D, 0x34, 0x2E, 0x35, 0x2E, 0x34, 0x2F, 0x32, 0x34, 0x2C, 0x4D, 0x31,
+0x30, 0x2E, 0x35, 0x2E, 0x34, 0x2F, 0x32, 0x34, 0x0A,
#endif
0x00, 0xB7, 0x2E, 0x88, 0x01, 0x42, 0x57, 0x88, 0x00, 0x00, 0x00, 0x00,
@@ -4718,7 +4800,7 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0xAB, 0xA0, 0x01, 0x04, 0xFF, 0xFF, 0xAB, 0xA0, 0x01, 0x08, 0xFF, 0xFF, 0x9D, 0x90, 0x00, 0x0C,
0xFF, 0xFF, 0xB9, 0xB0, 0x01, 0x10, 0xFF, 0xFF, 0xAB, 0xA0, 0x01, 0x15, 0xFF, 0xFF, 0xB9, 0xB0,
0x01, 0x19, 0xFF, 0xFF, 0xAB, 0xA0, 0x00, 0x1D, 0xFF, 0xFF, 0xB9, 0xB0, 0x00, 0x21, 0xFF, 0xFF,
-0xAB, 0xA0, 0x01, 0x15, 0xFF, 0xFF, 0x9D, 0x90, 0x00, 0x0C, 0x7A, 0x7A, 0x7A, 0x00, 0x4D, 0x57,
+0xAB, 0xA0, 0x01, 0x15, 0xFF, 0xFF, 0x9D, 0x90, 0x00, 0x0C, 0x2D, 0x30, 0x30, 0x00, 0x4D, 0x57,
0x54, 0x00, 0x4D, 0x50, 0x54, 0x00, 0x4D, 0x53, 0x54, 0x00, 0x4D, 0x44, 0x44, 0x54, 0x00, 0x4D,
0x44, 0x54, 0x00, 0x43, 0x44, 0x54, 0x00, 0x43, 0x53, 0x54, 0x00, 0x45, 0x53, 0x54, 0x00, 0x00,
0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
@@ -4800,7 +4882,7 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0xAB, 0xA0, 0x01, 0x04, 0xFF, 0xFF, 0xAB, 0xA0, 0x01, 0x08, 0xFF, 0xFF, 0x9D, 0x90, 0x00, 0x0C,
0xFF, 0xFF, 0xB9, 0xB0, 0x01, 0x10, 0xFF, 0xFF, 0xAB, 0xA0, 0x01, 0x15, 0xFF, 0xFF, 0xB9, 0xB0,
0x01, 0x19, 0xFF, 0xFF, 0xAB, 0xA0, 0x00, 0x1D, 0xFF, 0xFF, 0xB9, 0xB0, 0x00, 0x21, 0xFF, 0xFF,
-0xAB, 0xA0, 0x01, 0x15, 0xFF, 0xFF, 0x9D, 0x90, 0x00, 0x0C, 0x7A, 0x7A, 0x7A, 0x00, 0x4D, 0x57,
+0xAB, 0xA0, 0x01, 0x15, 0xFF, 0xFF, 0x9D, 0x90, 0x00, 0x0C, 0x2D, 0x30, 0x30, 0x00, 0x4D, 0x57,
0x54, 0x00, 0x4D, 0x50, 0x54, 0x00, 0x4D, 0x53, 0x54, 0x00, 0x4D, 0x44, 0x44, 0x54, 0x00, 0x4D,
0x44, 0x54, 0x00, 0x43, 0x44, 0x54, 0x00, 0x43, 0x53, 0x54, 0x00, 0x45, 0x53, 0x54, 0x00, 0x00,
0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
@@ -5006,24 +5088,24 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
/* America/Caracas */
0x50, 0x48, 0x50, 0x32, 0x01, 0x56, 0x45, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x0C, 0x80, 0x00, 0x00, 0x00,
-0x93, 0x1E, 0x2C, 0x3C, 0xF6, 0x98, 0xEC, 0x48, 0x47, 0x5B, 0x92, 0x70, 0x01, 0x02, 0x03, 0x02,
-0xFF, 0xFF, 0xC1, 0x40, 0x00, 0x00, 0xFF, 0xFF, 0xC1, 0x44, 0x00, 0x04, 0xFF, 0xFF, 0xC0, 0xB8,
-0x00, 0x08, 0xFF, 0xFF, 0xC7, 0xC0, 0x00, 0x08, 0xFF, 0xFF, 0xC0, 0xB8, 0x00, 0x08, 0x4C, 0x4D,
-0x54, 0x00, 0x43, 0x4D, 0x54, 0x00, 0x56, 0x45, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0C, 0x80, 0x00, 0x00, 0x00,
+0x93, 0x1E, 0x2C, 0x3C, 0xF6, 0x98, 0xEC, 0x48, 0x47, 0x5B, 0x92, 0x70, 0x57, 0x25, 0xA9, 0x70,
+0x01, 0x02, 0x03, 0x02, 0x03, 0xFF, 0xFF, 0xC1, 0x40, 0x00, 0x00, 0xFF, 0xFF, 0xC1, 0x44, 0x00,
+0x04, 0xFF, 0xFF, 0xC0, 0xB8, 0x00, 0x08, 0xFF, 0xFF, 0xC7, 0xC0, 0x00, 0x08, 0x4C, 0x4D, 0x54,
+0x00, 0x43, 0x4D, 0x54, 0x00, 0x56, 0x45, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00,
#ifdef TIMELIB_SUPPORTS_V2DATA
0x54, 0x5A, 0x69, 0x66, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x0C, 0xF8, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0C, 0xF8, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x69, 0x87, 0x1A, 0x40, 0xFF, 0xFF, 0xFF, 0xFF,
0x93, 0x1E, 0x2C, 0x3C, 0xFF, 0xFF, 0xFF, 0xFF, 0xF6, 0x98, 0xEC, 0x48, 0x00, 0x00, 0x00, 0x00,
-0x47, 0x5B, 0x92, 0x70, 0x00, 0x01, 0x02, 0x03, 0x02, 0xFF, 0xFF, 0xC1, 0x40, 0x00, 0x00, 0xFF,
-0xFF, 0xC1, 0x44, 0x00, 0x04, 0xFF, 0xFF, 0xC0, 0xB8, 0x00, 0x08, 0xFF, 0xFF, 0xC7, 0xC0, 0x00,
-0x08, 0xFF, 0xFF, 0xC0, 0xB8, 0x00, 0x08, 0x4C, 0x4D, 0x54, 0x00, 0x43, 0x4D, 0x54, 0x00, 0x56,
-0x45, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x56, 0x45,
-0x54, 0x34, 0x3A, 0x33, 0x30, 0x0A,
+0x47, 0x5B, 0x92, 0x70, 0x00, 0x00, 0x00, 0x00, 0x57, 0x25, 0xA9, 0x70, 0x00, 0x01, 0x02, 0x03,
+0x02, 0x03, 0xFF, 0xFF, 0xC1, 0x40, 0x00, 0x00, 0xFF, 0xFF, 0xC1, 0x44, 0x00, 0x04, 0xFF, 0xFF,
+0xC0, 0xB8, 0x00, 0x08, 0xFF, 0xFF, 0xC7, 0xC0, 0x00, 0x08, 0x4C, 0x4D, 0x54, 0x00, 0x43, 0x4D,
+0x54, 0x00, 0x56, 0x45, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x56,
+0x45, 0x54, 0x34, 0x0A,
#endif
0x00, 0x99, 0x59, 0xD0, 0x00, 0xAC, 0x86, 0xAA, 0x00, 0x00, 0x00, 0x00,
@@ -9263,7 +9345,7 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04,
0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
0xFF, 0xAB, 0xA0, 0x01, 0x04, 0xFF, 0xFF, 0x8F, 0x80, 0x00, 0x09, 0xFF, 0xFF, 0x9D, 0x90, 0x00,
-0x0D, 0xFF, 0xFF, 0xAB, 0xA0, 0x01, 0x11, 0x7A, 0x7A, 0x7A, 0x00, 0x50, 0x44, 0x44, 0x54, 0x00,
+0x0D, 0xFF, 0xFF, 0xAB, 0xA0, 0x01, 0x11, 0x2D, 0x30, 0x30, 0x00, 0x50, 0x44, 0x44, 0x54, 0x00,
0x50, 0x53, 0x54, 0x00, 0x4D, 0x53, 0x54, 0x00, 0x4D, 0x44, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
#ifdef TIMELIB_SUPPORTS_V2DATA
@@ -9339,7 +9421,7 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04,
0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0xFF, 0xFF, 0xAB, 0xA0, 0x01, 0x04, 0xFF, 0xFF, 0x8F, 0x80, 0x00, 0x09, 0xFF,
-0xFF, 0x9D, 0x90, 0x00, 0x0D, 0xFF, 0xFF, 0xAB, 0xA0, 0x01, 0x11, 0x7A, 0x7A, 0x7A, 0x00, 0x50,
+0xFF, 0x9D, 0x90, 0x00, 0x0D, 0xFF, 0xFF, 0xAB, 0xA0, 0x01, 0x11, 0x2D, 0x30, 0x30, 0x00, 0x50,
0x44, 0x44, 0x54, 0x00, 0x50, 0x53, 0x54, 0x00, 0x4D, 0x53, 0x54, 0x00, 0x4D, 0x44, 0x54, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x4D, 0x53, 0x54, 0x37, 0x4D,
0x44, 0x54, 0x2C, 0x4D, 0x33, 0x2E, 0x32, 0x2E, 0x30, 0x2C, 0x4D, 0x31, 0x31, 0x2E, 0x31, 0x2E,
@@ -9394,7 +9476,7 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xC7, 0xC0, 0x01, 0x04, 0xFF, 0xFF, 0xB9, 0xB0, 0x00, 0x08,
0xFF, 0xFF, 0xD5, 0xD0, 0x01, 0x0C, 0xFF, 0xFF, 0xC7, 0xC0, 0x01, 0x11, 0xFF, 0xFF, 0xC7, 0xC0,
0x01, 0x15, 0xFF, 0xFF, 0xAB, 0xA0, 0x00, 0x19, 0xFF, 0xFF, 0xB9, 0xB0, 0x01, 0x1D, 0xFF, 0xFF,
-0xC7, 0xC0, 0x01, 0x11, 0xFF, 0xFF, 0xB9, 0xB0, 0x00, 0x08, 0x7A, 0x7A, 0x7A, 0x00, 0x45, 0x50,
+0xC7, 0xC0, 0x01, 0x11, 0xFF, 0xFF, 0xB9, 0xB0, 0x00, 0x08, 0x2D, 0x30, 0x30, 0x00, 0x45, 0x50,
0x54, 0x00, 0x45, 0x53, 0x54, 0x00, 0x45, 0x44, 0x44, 0x54, 0x00, 0x45, 0x44, 0x54, 0x00, 0x45,
0x57, 0x54, 0x00, 0x43, 0x53, 0x54, 0x00, 0x43, 0x44, 0x54, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -9474,7 +9556,7 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x01, 0x04, 0xFF, 0xFF, 0xB9, 0xB0, 0x00, 0x08, 0xFF, 0xFF, 0xD5, 0xD0, 0x01, 0x0C, 0xFF, 0xFF,
0xC7, 0xC0, 0x01, 0x11, 0xFF, 0xFF, 0xC7, 0xC0, 0x01, 0x15, 0xFF, 0xFF, 0xAB, 0xA0, 0x00, 0x19,
0xFF, 0xFF, 0xB9, 0xB0, 0x01, 0x1D, 0xFF, 0xFF, 0xC7, 0xC0, 0x01, 0x11, 0xFF, 0xFF, 0xB9, 0xB0,
-0x00, 0x08, 0x7A, 0x7A, 0x7A, 0x00, 0x45, 0x50, 0x54, 0x00, 0x45, 0x53, 0x54, 0x00, 0x45, 0x44,
+0x00, 0x08, 0x2D, 0x30, 0x30, 0x00, 0x45, 0x50, 0x54, 0x00, 0x45, 0x53, 0x54, 0x00, 0x45, 0x44,
0x44, 0x54, 0x00, 0x45, 0x44, 0x54, 0x00, 0x45, 0x57, 0x54, 0x00, 0x43, 0x53, 0x54, 0x00, 0x43,
0x44, 0x54, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x45, 0x53, 0x54, 0x35, 0x45, 0x44, 0x54, 0x2C,
@@ -13741,7 +13823,7 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0xFF, 0xD5, 0xD0, 0x01, 0x08, 0xFF, 0xFF, 0xC7, 0xC0, 0x00, 0x0C, 0xFF, 0xFF, 0xE3, 0xE0, 0x01,
0x10, 0xFF, 0xFF, 0xD5, 0xD0, 0x01, 0x15, 0xFF, 0xFF, 0xC7, 0xC0, 0x01, 0x19, 0xFF, 0xFF, 0xB9,
0xB0, 0x00, 0x1D, 0xFF, 0xFF, 0xAB, 0xA0, 0x00, 0x21, 0xFF, 0xFF, 0xB9, 0xB0, 0x01, 0x25, 0xFF,
-0xFF, 0xC7, 0xC0, 0x01, 0x19, 0xFF, 0xFF, 0xB9, 0xB0, 0x00, 0x1D, 0x7A, 0x7A, 0x7A, 0x00, 0x41,
+0xFF, 0xC7, 0xC0, 0x01, 0x19, 0xFF, 0xFF, 0xB9, 0xB0, 0x00, 0x1D, 0x2D, 0x30, 0x30, 0x00, 0x41,
0x57, 0x54, 0x00, 0x41, 0x50, 0x54, 0x00, 0x41, 0x53, 0x54, 0x00, 0x41, 0x44, 0x44, 0x54, 0x00,
0x41, 0x44, 0x54, 0x00, 0x45, 0x44, 0x54, 0x00, 0x45, 0x53, 0x54, 0x00, 0x43, 0x53, 0x54, 0x00,
0x43, 0x44, 0x54, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -13823,7 +13905,7 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x08, 0xFF, 0xFF, 0xC7, 0xC0, 0x00, 0x0C, 0xFF, 0xFF, 0xE3, 0xE0, 0x01, 0x10, 0xFF, 0xFF, 0xD5,
0xD0, 0x01, 0x15, 0xFF, 0xFF, 0xC7, 0xC0, 0x01, 0x19, 0xFF, 0xFF, 0xB9, 0xB0, 0x00, 0x1D, 0xFF,
0xFF, 0xAB, 0xA0, 0x00, 0x21, 0xFF, 0xFF, 0xB9, 0xB0, 0x01, 0x25, 0xFF, 0xFF, 0xC7, 0xC0, 0x01,
-0x19, 0xFF, 0xFF, 0xB9, 0xB0, 0x00, 0x1D, 0x7A, 0x7A, 0x7A, 0x00, 0x41, 0x57, 0x54, 0x00, 0x41,
+0x19, 0xFF, 0xFF, 0xB9, 0xB0, 0x00, 0x1D, 0x2D, 0x30, 0x30, 0x00, 0x41, 0x57, 0x54, 0x00, 0x41,
0x50, 0x54, 0x00, 0x41, 0x53, 0x54, 0x00, 0x41, 0x44, 0x44, 0x54, 0x00, 0x41, 0x44, 0x54, 0x00,
0x45, 0x44, 0x54, 0x00, 0x45, 0x53, 0x54, 0x00, 0x43, 0x53, 0x54, 0x00, 0x43, 0x44, 0x54, 0x00,
0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
@@ -14260,7 +14342,7 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02,
0x03, 0x02, 0x03, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xC7, 0xC0, 0x01, 0x04,
0xFF, 0xFF, 0xAB, 0xA0, 0x00, 0x09, 0xFF, 0xFF, 0xB9, 0xB0, 0x01, 0x0D, 0xFF, 0xFF, 0xB9, 0xB0,
-0x00, 0x11, 0xFF, 0xFF, 0xAB, 0xA0, 0x00, 0x09, 0x7A, 0x7A, 0x7A, 0x00, 0x43, 0x44, 0x44, 0x54,
+0x00, 0x11, 0xFF, 0xFF, 0xAB, 0xA0, 0x00, 0x09, 0x2D, 0x30, 0x30, 0x00, 0x43, 0x44, 0x44, 0x54,
0x00, 0x43, 0x53, 0x54, 0x00, 0x43, 0x44, 0x54, 0x00, 0x45, 0x53, 0x54, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
#ifdef TIMELIB_SUPPORTS_V2DATA
@@ -14336,7 +14418,7 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02,
0x03, 0x02, 0x03, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xC7, 0xC0, 0x01, 0x04,
0xFF, 0xFF, 0xAB, 0xA0, 0x00, 0x09, 0xFF, 0xFF, 0xB9, 0xB0, 0x01, 0x0D, 0xFF, 0xFF, 0xB9, 0xB0,
-0x00, 0x11, 0xFF, 0xFF, 0xAB, 0xA0, 0x00, 0x09, 0x7A, 0x7A, 0x7A, 0x00, 0x43, 0x44, 0x44, 0x54,
+0x00, 0x11, 0xFF, 0xFF, 0xAB, 0xA0, 0x00, 0x09, 0x2D, 0x30, 0x30, 0x00, 0x43, 0x44, 0x44, 0x54,
0x00, 0x43, 0x53, 0x54, 0x00, 0x43, 0x44, 0x54, 0x00, 0x45, 0x53, 0x54, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x43, 0x53, 0x54, 0x36, 0x43, 0x44,
0x54, 0x2C, 0x4D, 0x33, 0x2E, 0x32, 0x2E, 0x30, 0x2C, 0x4D, 0x31, 0x31, 0x2E, 0x31, 0x2E, 0x30,
@@ -14511,7 +14593,7 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02,
0x03, 0x02, 0x03, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xC7, 0xC0, 0x01, 0x04,
0xFF, 0xFF, 0xAB, 0xA0, 0x00, 0x09, 0xFF, 0xFF, 0xB9, 0xB0, 0x01, 0x0D, 0xFF, 0xFF, 0xB9, 0xB0,
-0x00, 0x11, 0xFF, 0xFF, 0xAB, 0xA0, 0x00, 0x09, 0x7A, 0x7A, 0x7A, 0x00, 0x43, 0x44, 0x44, 0x54,
+0x00, 0x11, 0xFF, 0xFF, 0xAB, 0xA0, 0x00, 0x09, 0x2D, 0x30, 0x30, 0x00, 0x43, 0x44, 0x44, 0x54,
0x00, 0x43, 0x53, 0x54, 0x00, 0x43, 0x44, 0x54, 0x00, 0x45, 0x53, 0x54, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
#ifdef TIMELIB_SUPPORTS_V2DATA
@@ -14587,7 +14669,7 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02,
0x03, 0x02, 0x03, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xC7, 0xC0, 0x01, 0x04,
0xFF, 0xFF, 0xAB, 0xA0, 0x00, 0x09, 0xFF, 0xFF, 0xB9, 0xB0, 0x01, 0x0D, 0xFF, 0xFF, 0xB9, 0xB0,
-0x00, 0x11, 0xFF, 0xFF, 0xAB, 0xA0, 0x00, 0x09, 0x7A, 0x7A, 0x7A, 0x00, 0x43, 0x44, 0x44, 0x54,
+0x00, 0x11, 0xFF, 0xFF, 0xAB, 0xA0, 0x00, 0x09, 0x2D, 0x30, 0x30, 0x00, 0x43, 0x44, 0x44, 0x54,
0x00, 0x43, 0x53, 0x54, 0x00, 0x43, 0x44, 0x54, 0x00, 0x45, 0x53, 0x54, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x43, 0x53, 0x54, 0x36, 0x43, 0x44,
0x54, 0x2C, 0x4D, 0x33, 0x2E, 0x32, 0x2E, 0x30, 0x2C, 0x4D, 0x31, 0x31, 0x2E, 0x31, 0x2E, 0x30,
@@ -17456,7 +17538,7 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x03, 0x05, 0x03, 0x05, 0x03, 0x05, 0x03, 0x05, 0x03, 0x05, 0x03, 0x05, 0x03, 0x05, 0x03, 0x05,
0x03, 0x05, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xAB, 0xA0, 0x01, 0x04, 0xFF,
0xFF, 0xAB, 0xA0, 0x01, 0x08, 0xFF, 0xFF, 0x9D, 0x90, 0x00, 0x0C, 0xFF, 0xFF, 0xB9, 0xB0, 0x01,
-0x10, 0xFF, 0xFF, 0xAB, 0xA0, 0x01, 0x15, 0x7A, 0x7A, 0x7A, 0x00, 0x4D, 0x57, 0x54, 0x00, 0x4D,
+0x10, 0xFF, 0xFF, 0xAB, 0xA0, 0x01, 0x15, 0x2D, 0x30, 0x30, 0x00, 0x4D, 0x57, 0x54, 0x00, 0x4D,
0x50, 0x54, 0x00, 0x4D, 0x53, 0x54, 0x00, 0x4D, 0x44, 0x44, 0x54, 0x00, 0x4D, 0x44, 0x54, 0x00,
0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
#ifdef TIMELIB_SUPPORTS_V2DATA
@@ -17534,7 +17616,7 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x03, 0x05, 0x03, 0x05, 0x03, 0x05, 0x03, 0x05, 0x03, 0x05, 0x03, 0x05, 0x03, 0x05, 0x03, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xAB, 0xA0, 0x01, 0x04, 0xFF, 0xFF, 0xAB, 0xA0, 0x01,
0x08, 0xFF, 0xFF, 0x9D, 0x90, 0x00, 0x0C, 0xFF, 0xFF, 0xB9, 0xB0, 0x01, 0x10, 0xFF, 0xFF, 0xAB,
-0xA0, 0x01, 0x15, 0x7A, 0x7A, 0x7A, 0x00, 0x4D, 0x57, 0x54, 0x00, 0x4D, 0x50, 0x54, 0x00, 0x4D,
+0xA0, 0x01, 0x15, 0x2D, 0x30, 0x30, 0x00, 0x4D, 0x57, 0x54, 0x00, 0x4D, 0x50, 0x54, 0x00, 0x4D,
0x53, 0x54, 0x00, 0x4D, 0x44, 0x44, 0x54, 0x00, 0x4D, 0x44, 0x54, 0x00, 0x00, 0x00, 0x01, 0x00,
0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0A, 0x4D, 0x53, 0x54, 0x37, 0x4D, 0x44, 0x54,
0x2C, 0x4D, 0x33, 0x2E, 0x32, 0x2E, 0x30, 0x2C, 0x4D, 0x31, 0x31, 0x2E, 0x31, 0x2E, 0x30, 0x0A,
@@ -17550,7 +17632,7 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0xFE, 0x1E, 0xCC, 0x80, 0x4A, 0xDA, 0x06, 0x20, 0x4B, 0x8F, 0xCA, 0xF0, 0x4E, 0xA9, 0x9C, 0x20,
0x4F, 0x43, 0xCD, 0x90, 0x00, 0x01, 0x02, 0x01, 0x02, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x70, 0x80, 0x00, 0x04, 0x00, 0x00, 0x9A, 0xB0, 0x00, 0x09, 0x00, 0x00, 0x70, 0x80,
-0x00, 0x04, 0x7A, 0x7A, 0x7A, 0x00, 0x41, 0x57, 0x53, 0x54, 0x00, 0x43, 0x41, 0x53, 0x54, 0x00,
+0x00, 0x04, 0x2D, 0x30, 0x30, 0x00, 0x41, 0x57, 0x53, 0x54, 0x00, 0x43, 0x41, 0x53, 0x54, 0x00,
0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01,
#ifdef TIMELIB_SUPPORTS_V2DATA
0x54, 0x5A, 0x69, 0x66, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -17560,7 +17642,7 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x4A, 0xDA, 0x06, 0x20, 0x00, 0x00, 0x00, 0x00, 0x4B, 0x8F, 0xCA, 0xF0, 0x00, 0x00, 0x00, 0x00,
0x4E, 0xA9, 0x9C, 0x20, 0x00, 0x00, 0x00, 0x00, 0x4F, 0x43, 0xCD, 0x90, 0x00, 0x01, 0x02, 0x01,
0x02, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x80, 0x00, 0x04, 0x00, 0x00,
-0x9A, 0xB0, 0x00, 0x09, 0x00, 0x00, 0x70, 0x80, 0x00, 0x04, 0x7A, 0x7A, 0x7A, 0x00, 0x41, 0x57,
+0x9A, 0xB0, 0x00, 0x09, 0x00, 0x00, 0x70, 0x80, 0x00, 0x04, 0x2D, 0x30, 0x30, 0x00, 0x41, 0x57,
0x53, 0x54, 0x00, 0x43, 0x41, 0x53, 0x54, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01,
0x0A, 0x41, 0x57, 0x53, 0x54, 0x2D, 0x38, 0x0A,
#endif
@@ -17574,7 +17656,7 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0xE7, 0x9C, 0x40, 0x00, 0xF6, 0x47, 0xDF, 0x10, 0xFE, 0x47, 0xAB, 0x00, 0x4A, 0xDA, 0x14, 0x30,
0x4B, 0x97, 0xFA, 0x40, 0x4E, 0xA9, 0xAA, 0x30, 0x4F, 0x43, 0xF7, 0xC0, 0x00, 0x01, 0x00, 0x01,
0x02, 0x03, 0x02, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x62, 0x70, 0x00, 0x04,
-0x00, 0x00, 0x46, 0x50, 0x00, 0x04, 0x00, 0x00, 0x62, 0x70, 0x00, 0x04, 0x7A, 0x7A, 0x7A, 0x00,
+0x00, 0x00, 0x46, 0x50, 0x00, 0x04, 0x00, 0x00, 0x62, 0x70, 0x00, 0x04, 0x2D, 0x30, 0x30, 0x00,
0x44, 0x41, 0x56, 0x54, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01,
#ifdef TIMELIB_SUPPORTS_V2DATA
0x54, 0x5A, 0x69, 0x66, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -17585,7 +17667,7 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x4A, 0xDA, 0x14, 0x30, 0x00, 0x00, 0x00, 0x00, 0x4B, 0x97, 0xFA, 0x40, 0x00, 0x00, 0x00, 0x00,
0x4E, 0xA9, 0xAA, 0x30, 0x00, 0x00, 0x00, 0x00, 0x4F, 0x43, 0xF7, 0xC0, 0x00, 0x01, 0x00, 0x01,
0x02, 0x03, 0x02, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x62, 0x70, 0x00, 0x04,
-0x00, 0x00, 0x46, 0x50, 0x00, 0x04, 0x00, 0x00, 0x62, 0x70, 0x00, 0x04, 0x7A, 0x7A, 0x7A, 0x00,
+0x00, 0x00, 0x46, 0x50, 0x00, 0x04, 0x00, 0x00, 0x62, 0x70, 0x00, 0x04, 0x2D, 0x30, 0x30, 0x00,
0x44, 0x41, 0x56, 0x54, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x0A, 0x44, 0x41,
0x56, 0x54, 0x2D, 0x37, 0x0A,
#endif
@@ -17598,7 +17680,7 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0D, 0x80, 0x00, 0x00, 0x00,
0xD4, 0xBC, 0x76, 0x80, 0xDE, 0x34, 0x60, 0x60, 0xE7, 0x3C, 0x02, 0x80, 0x00, 0x01, 0x00, 0x02,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8C, 0xA0, 0x00, 0x04, 0x00, 0x00, 0x8C, 0xA0,
-0x00, 0x08, 0x7A, 0x7A, 0x7A, 0x00, 0x50, 0x4D, 0x54, 0x00, 0x44, 0x44, 0x55, 0x54, 0x00, 0x00,
+0x00, 0x08, 0x2D, 0x30, 0x30, 0x00, 0x50, 0x4D, 0x54, 0x00, 0x44, 0x44, 0x55, 0x54, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00,
#ifdef TIMELIB_SUPPORTS_V2DATA
0x54, 0x5A, 0x69, 0x66, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -17607,7 +17689,7 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xD4, 0xBC, 0x76, 0x80, 0xFF, 0xFF, 0xFF, 0xFF,
0xDE, 0x34, 0x60, 0x60, 0xFF, 0xFF, 0xFF, 0xFF, 0xE7, 0x3C, 0x02, 0x80, 0x00, 0x01, 0x00, 0x02,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8C, 0xA0, 0x00, 0x04, 0x00, 0x00, 0x8C, 0xA0,
-0x00, 0x08, 0x7A, 0x7A, 0x7A, 0x00, 0x50, 0x4D, 0x54, 0x00, 0x44, 0x44, 0x55, 0x54, 0x00, 0x00,
+0x00, 0x08, 0x2D, 0x30, 0x30, 0x00, 0x50, 0x4D, 0x54, 0x00, 0x44, 0x44, 0x55, 0x54, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x44, 0x44, 0x55, 0x54, 0x2D, 0x31, 0x30, 0x0A,
#endif
0x00, 0x23, 0x9A, 0x95, 0x01, 0xE8, 0x4E, 0x82, 0x00, 0x00, 0x00, 0x10, 0x44, 0x75, 0x6D, 0x6F,
@@ -17647,7 +17729,7 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04,
0x05, 0x04, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8C, 0xA0, 0x00, 0x04, 0x00,
0x00, 0x9A, 0xB0, 0x01, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9A, 0xB0, 0x01,
-0x09, 0x00, 0x00, 0x8C, 0xA0, 0x00, 0x04, 0x00, 0x00, 0x9A, 0xB0, 0x00, 0x0E, 0x7A, 0x7A, 0x7A,
+0x09, 0x00, 0x00, 0x8C, 0xA0, 0x00, 0x04, 0x00, 0x00, 0x9A, 0xB0, 0x00, 0x0E, 0x2D, 0x30, 0x30,
0x00, 0x41, 0x45, 0x53, 0x54, 0x00, 0x41, 0x45, 0x44, 0x54, 0x00, 0x4D, 0x49, 0x53, 0x54, 0x00,
0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
#ifdef TIMELIB_SUPPORTS_V2DATA
@@ -17708,7 +17790,7 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x8C, 0xA0, 0x00, 0x04, 0x00, 0x00, 0x9A, 0xB0, 0x01, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x9A, 0xB0, 0x01, 0x09, 0x00, 0x00, 0x8C, 0xA0, 0x00, 0x04, 0x00, 0x00, 0x9A, 0xB0,
-0x00, 0x0E, 0x7A, 0x7A, 0x7A, 0x00, 0x41, 0x45, 0x53, 0x54, 0x00, 0x41, 0x45, 0x44, 0x54, 0x00,
+0x00, 0x0E, 0x2D, 0x30, 0x30, 0x00, 0x41, 0x45, 0x53, 0x54, 0x00, 0x41, 0x45, 0x44, 0x54, 0x00,
0x4D, 0x49, 0x53, 0x54, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x0A, 0x4D, 0x49, 0x53, 0x54, 0x2D, 0x31, 0x31, 0x0A,
#endif
@@ -17720,7 +17802,7 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x09, 0x80, 0x00, 0x00, 0x00,
0xE2, 0x20, 0x32, 0x80, 0x4A, 0xDA, 0x22, 0x40, 0x00, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x54, 0x60, 0x00, 0x04, 0x00, 0x00, 0x46, 0x50, 0x00, 0x04, 0x7A, 0x7A, 0x7A,
+0x00, 0x00, 0x00, 0x54, 0x60, 0x00, 0x04, 0x00, 0x00, 0x46, 0x50, 0x00, 0x04, 0x2D, 0x30, 0x30,
0x00, 0x4D, 0x41, 0x57, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
#ifdef TIMELIB_SUPPORTS_V2DATA
0x54, 0x5A, 0x69, 0x66, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -17728,7 +17810,7 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x09, 0xF8, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xE2, 0x20, 0x32, 0x80, 0x00, 0x00, 0x00, 0x00,
0x4A, 0xDA, 0x22, 0x40, 0x00, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x54,
-0x60, 0x00, 0x04, 0x00, 0x00, 0x46, 0x50, 0x00, 0x04, 0x7A, 0x7A, 0x7A, 0x00, 0x4D, 0x41, 0x57,
+0x60, 0x00, 0x04, 0x00, 0x00, 0x46, 0x50, 0x00, 0x04, 0x2D, 0x30, 0x30, 0x00, 0x4D, 0x41, 0x57,
0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x4D, 0x41, 0x57, 0x54, 0x2D, 0x35, 0x0A,
#endif
0x00, 0x22, 0x2E, 0x00, 0x01, 0x72, 0x9C, 0x4D, 0x00, 0x00, 0x00, 0x06, 0x4D, 0x61, 0x77, 0x73,
@@ -17942,7 +18024,7 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF,
0xC7, 0xC0, 0x00, 0x04, 0xFF, 0xFF, 0xD5, 0xD0, 0x01, 0x08, 0xFF, 0xFF, 0xE3, 0xE0, 0x01, 0x08,
0xFF, 0xFF, 0xD5, 0xD0, 0x00, 0x04, 0xFF, 0xFF, 0xD5, 0xD0, 0x01, 0x0D, 0xFF, 0xFF, 0xC7, 0xC0,
-0x00, 0x12, 0xFF, 0xFF, 0xC7, 0xC0, 0x00, 0x12, 0x7A, 0x7A, 0x7A, 0x00, 0x41, 0x52, 0x54, 0x00,
+0x00, 0x12, 0xFF, 0xFF, 0xC7, 0xC0, 0x00, 0x12, 0x2D, 0x30, 0x30, 0x00, 0x41, 0x52, 0x54, 0x00,
0x41, 0x52, 0x53, 0x54, 0x00, 0x43, 0x4C, 0x53, 0x54, 0x00, 0x43, 0x4C, 0x54, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00,
#ifdef TIMELIB_SUPPORTS_V2DATA
@@ -18021,7 +18103,7 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF,
0xC7, 0xC0, 0x00, 0x04, 0xFF, 0xFF, 0xD5, 0xD0, 0x01, 0x08, 0xFF, 0xFF, 0xE3, 0xE0, 0x01, 0x08,
0xFF, 0xFF, 0xD5, 0xD0, 0x00, 0x04, 0xFF, 0xFF, 0xD5, 0xD0, 0x01, 0x0D, 0xFF, 0xFF, 0xC7, 0xC0,
-0x00, 0x12, 0xFF, 0xFF, 0xC7, 0xC0, 0x00, 0x12, 0x7A, 0x7A, 0x7A, 0x00, 0x41, 0x52, 0x54, 0x00,
+0x00, 0x12, 0xFF, 0xFF, 0xC7, 0xC0, 0x00, 0x12, 0x2D, 0x30, 0x30, 0x00, 0x41, 0x52, 0x54, 0x00,
0x41, 0x52, 0x53, 0x54, 0x00, 0x43, 0x4C, 0x53, 0x54, 0x00, 0x43, 0x4C, 0x54, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x0A, 0x43,
0x4C, 0x54, 0x34, 0x43, 0x4C, 0x53, 0x54, 0x2C, 0x4D, 0x38, 0x2E, 0x32, 0x2E, 0x36, 0x2F, 0x32,
@@ -18035,13 +18117,13 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x09, 0x80, 0x00, 0x00, 0x00,
0x0D, 0x02, 0x2D, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xD5, 0xD0,
-0x00, 0x04, 0x7A, 0x7A, 0x7A, 0x00, 0x52, 0x4F, 0x54, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x04, 0x2D, 0x30, 0x30, 0x00, 0x52, 0x4F, 0x54, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00,
#ifdef TIMELIB_SUPPORTS_V2DATA
0x54, 0x5A, 0x69, 0x66, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x09, 0xF8, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0D, 0x02, 0x2D, 0x00, 0x00, 0x01, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xD5, 0xD0, 0x00, 0x04, 0x7A, 0x7A, 0x7A, 0x00, 0x52, 0x4F,
+0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xD5, 0xD0, 0x00, 0x04, 0x2D, 0x30, 0x30, 0x00, 0x52, 0x4F,
0x54, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x52, 0x4F, 0x54, 0x54, 0x33, 0x0A,
#endif
0x00, 0x22, 0x3B, 0x05, 0x00, 0xAA, 0xB1, 0xEA, 0x00, 0x00, 0x00, 0x07, 0x52, 0x6F, 0x74, 0x68,
@@ -18212,13 +18294,13 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x09, 0x80, 0x00, 0x00, 0x00,
0xE7, 0xB1, 0x58, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2A, 0x30,
-0x00, 0x04, 0x7A, 0x7A, 0x7A, 0x00, 0x53, 0x59, 0x4F, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x04, 0x2D, 0x30, 0x30, 0x00, 0x53, 0x59, 0x4F, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00,
#ifdef TIMELIB_SUPPORTS_V2DATA
0x54, 0x5A, 0x69, 0x66, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x09, 0xF8, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xE7, 0xB1, 0x58, 0x00, 0x00, 0x01, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2A, 0x30, 0x00, 0x04, 0x7A, 0x7A, 0x7A, 0x00, 0x53, 0x59,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2A, 0x30, 0x00, 0x04, 0x2D, 0x30, 0x30, 0x00, 0x53, 0x59,
0x4F, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x53, 0x59, 0x4F, 0x54, 0x2D, 0x33, 0x0A,
#endif
0x00, 0x20, 0x08, 0xBC, 0x01, 0x4F, 0x11, 0x58, 0x00, 0x00, 0x00, 0x05, 0x53, 0x79, 0x6F, 0x77,
@@ -18250,7 +18332,7 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x20, 0x01, 0x04, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x7A, 0x7A, 0x7A, 0x00, 0x43, 0x45, 0x53, 0x54,
+0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x2D, 0x30, 0x30, 0x00, 0x43, 0x45, 0x53, 0x54,
0x00, 0x55, 0x54, 0x43, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00,
#ifdef TIMELIB_SUPPORTS_V2DATA
0x54, 0x5A, 0x69, 0x66, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -18295,7 +18377,7 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x20, 0x01, 0x04, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x7A, 0x7A, 0x7A, 0x00, 0x43, 0x45, 0x53, 0x54,
+0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x2D, 0x30, 0x30, 0x00, 0x43, 0x45, 0x53, 0x54,
0x00, 0x55, 0x54, 0x43, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x0A, 0x55, 0x54,
0x43, 0x30, 0x43, 0x45, 0x53, 0x54, 0x2D, 0x32, 0x2C, 0x4D, 0x33, 0x2E, 0x35, 0x2E, 0x30, 0x2F,
0x31, 0x2C, 0x4D, 0x31, 0x30, 0x2E, 0x35, 0x2E, 0x30, 0x2F, 0x33, 0x0A,
@@ -18308,13 +18390,13 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x09, 0x80, 0x00, 0x00, 0x00,
0xE9, 0x58, 0x89, 0x80, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0x60,
-0x00, 0x04, 0x7A, 0x7A, 0x7A, 0x00, 0x56, 0x4F, 0x53, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x04, 0x2D, 0x30, 0x30, 0x00, 0x56, 0x4F, 0x53, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00,
#ifdef TIMELIB_SUPPORTS_V2DATA
0x54, 0x5A, 0x69, 0x66, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x09, 0xF8, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xE9, 0x58, 0x89, 0x80, 0x00, 0x01, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0x60, 0x00, 0x04, 0x7A, 0x7A, 0x7A, 0x00, 0x56, 0x4F,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0x60, 0x00, 0x04, 0x2D, 0x30, 0x30, 0x00, 0x56, 0x4F,
0x53, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x56, 0x4F, 0x53, 0x54, 0x2D, 0x36, 0x0A,
#endif
0x00, 0x11, 0xB3, 0x3F, 0x01, 0xB5, 0xC6, 0x4F, 0x00, 0x00, 0x00, 0x06, 0x56, 0x6F, 0x73, 0x74,
@@ -18485,32 +18567,35 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
/* Asia/Almaty */
0x50, 0x48, 0x50, 0x32, 0x01, 0x4B, 0x5A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0F, 0x80, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x10, 0x80, 0x00, 0x00, 0x00,
0xAA, 0x19, 0x7B, 0xDC, 0xB5, 0xA3, 0xEF, 0x30, 0x15, 0x27, 0x7D, 0xA0, 0x16, 0x18, 0xB2, 0x10,
0x17, 0x08, 0xB1, 0x20, 0x17, 0xF9, 0xE5, 0x90, 0x18, 0xE9, 0xE4, 0xA0, 0x19, 0xDB, 0x19, 0x10,
0x1A, 0xCC, 0x69, 0xA0, 0x1B, 0xBC, 0x76, 0xC0, 0x1C, 0xAC, 0x67, 0xC0, 0x1D, 0x9C, 0x58, 0xC0,
0x1E, 0x8C, 0x49, 0xC0, 0x1F, 0x7C, 0x3A, 0xC0, 0x20, 0x6C, 0x2B, 0xC0, 0x21, 0x5C, 0x1C, 0xC0,
0x22, 0x4C, 0x0D, 0xC0, 0x23, 0x3B, 0xFE, 0xC0, 0x24, 0x2B, 0xEF, 0xC0, 0x25, 0x1B, 0xE0, 0xC0,
-0x26, 0x0B, 0xD1, 0xC0, 0x27, 0x04, 0xFD, 0x40, 0x27, 0x7F, 0x7C, 0xA0, 0x29, 0xD4, 0xA6, 0x10,
-0x2A, 0xC4, 0x89, 0x00, 0x2B, 0xB4, 0xB2, 0x40, 0x2C, 0xA4, 0xA3, 0x40, 0x2D, 0x94, 0x94, 0x40,
-0x2E, 0x84, 0x85, 0x40, 0x2F, 0x74, 0x76, 0x40, 0x30, 0x64, 0x67, 0x40, 0x31, 0x5D, 0x92, 0xC0,
-0x32, 0x72, 0x6D, 0xC0, 0x33, 0x3D, 0x74, 0xC0, 0x34, 0x52, 0x4F, 0xC0, 0x35, 0x1D, 0x56, 0xC0,
-0x36, 0x32, 0x31, 0xC0, 0x36, 0xFD, 0x38, 0xC0, 0x38, 0x1B, 0x4E, 0x40, 0x38, 0xDD, 0x1A, 0xC0,
-0x39, 0xFB, 0x30, 0x40, 0x3A, 0xBC, 0xFC, 0xC0, 0x3B, 0xDB, 0x12, 0x40, 0x3C, 0xA6, 0x19, 0x40,
-0x3D, 0xBA, 0xF4, 0x40, 0x3E, 0x85, 0xFB, 0x40, 0x3F, 0x9A, 0xD6, 0x40, 0x40, 0x65, 0xDD, 0x40,
-0x41, 0x83, 0xF2, 0xC0, 0x42, 0x35, 0xD1, 0x20, 0x00, 0x01, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02,
-0x03, 0x02, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x03,
-0x02, 0x03, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04,
-0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x03, 0x00, 0x00, 0x48, 0x24, 0x00,
-0x00, 0x00, 0x00, 0x46, 0x50, 0x00, 0x04, 0x00, 0x00, 0x62, 0x70, 0x01, 0x09, 0x00, 0x00, 0x54,
-0x60, 0x00, 0x04, 0x00, 0x00, 0x54, 0x60, 0x00, 0x04, 0x00, 0x00, 0x62, 0x70, 0x01, 0x09, 0x4C,
-0x4D, 0x54, 0x00, 0x41, 0x4C, 0x4D, 0x54, 0x00, 0x41, 0x4C, 0x4D, 0x53, 0x54, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x26, 0x0B, 0xD1, 0xC0, 0x27, 0x04, 0xFD, 0x40, 0x27, 0xF4, 0xEE, 0x40, 0x28, 0xE4, 0xED, 0x50,
+0x29, 0x78, 0x95, 0x50, 0x29, 0xD4, 0xD0, 0x40, 0x2A, 0xC4, 0xC1, 0x40, 0x2B, 0xB4, 0xB2, 0x40,
+0x2C, 0xA4, 0xA3, 0x40, 0x2D, 0x94, 0x94, 0x40, 0x2E, 0x84, 0x85, 0x40, 0x2F, 0x74, 0x76, 0x40,
+0x30, 0x64, 0x67, 0x40, 0x31, 0x5D, 0x92, 0xC0, 0x32, 0x72, 0x6D, 0xC0, 0x33, 0x3D, 0x74, 0xC0,
+0x34, 0x52, 0x4F, 0xC0, 0x35, 0x1D, 0x56, 0xC0, 0x36, 0x32, 0x31, 0xC0, 0x36, 0xFD, 0x38, 0xC0,
+0x38, 0x1B, 0x4E, 0x40, 0x38, 0xDD, 0x1A, 0xC0, 0x39, 0xFB, 0x30, 0x40, 0x3A, 0xBC, 0xFC, 0xC0,
+0x3B, 0xDB, 0x12, 0x40, 0x3C, 0xA6, 0x19, 0x40, 0x3D, 0xBA, 0xF4, 0x40, 0x3E, 0x85, 0xFB, 0x40,
+0x3F, 0x9A, 0xD6, 0x40, 0x40, 0x65, 0xDD, 0x40, 0x41, 0x83, 0xF2, 0xC0, 0x7F, 0xFF, 0xFF, 0xFF,
+0x00, 0x01, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05,
+0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x06, 0x07, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04,
+0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04,
+0x05, 0x04, 0x05, 0x04, 0x04, 0x00, 0x00, 0x48, 0x24, 0x00, 0x00, 0x00, 0x00, 0x46, 0x50, 0x00,
+0x04, 0x00, 0x00, 0x62, 0x70, 0x01, 0x08, 0x00, 0x00, 0x54, 0x60, 0x00, 0x0C, 0x00, 0x00, 0x54,
+0x60, 0x00, 0x0C, 0x00, 0x00, 0x62, 0x70, 0x01, 0x08, 0x00, 0x00, 0x54, 0x60, 0x01, 0x0C, 0x00,
+0x00, 0x46, 0x50, 0x00, 0x04, 0x00, 0x00, 0x62, 0x70, 0x01, 0x08, 0x00, 0x00, 0x54, 0x60, 0x00,
+0x0C, 0x4C, 0x4D, 0x54, 0x00, 0x2B, 0x30, 0x35, 0x00, 0x2B, 0x30, 0x37, 0x00, 0x2B, 0x30, 0x36,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00,
#ifdef TIMELIB_SUPPORTS_V2DATA
0x54, 0x5A, 0x69, 0x66, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0F, 0xF8, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x10, 0xF8, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xAA, 0x19, 0x7B, 0xDC, 0xFF, 0xFF, 0xFF, 0xFF,
0xB5, 0xA3, 0xEF, 0x30, 0x00, 0x00, 0x00, 0x00, 0x15, 0x27, 0x7D, 0xA0, 0x00, 0x00, 0x00, 0x00,
0x16, 0x18, 0xB2, 0x10, 0x00, 0x00, 0x00, 0x00, 0x17, 0x08, 0xB1, 0x20, 0x00, 0x00, 0x00, 0x00,
@@ -18522,8 +18607,9 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x21, 0x5C, 0x1C, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x22, 0x4C, 0x0D, 0xC0, 0x00, 0x00, 0x00, 0x00,
0x23, 0x3B, 0xFE, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x24, 0x2B, 0xEF, 0xC0, 0x00, 0x00, 0x00, 0x00,
0x25, 0x1B, 0xE0, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x26, 0x0B, 0xD1, 0xC0, 0x00, 0x00, 0x00, 0x00,
-0x27, 0x04, 0xFD, 0x40, 0x00, 0x00, 0x00, 0x00, 0x27, 0x7F, 0x7C, 0xA0, 0x00, 0x00, 0x00, 0x00,
-0x29, 0xD4, 0xA6, 0x10, 0x00, 0x00, 0x00, 0x00, 0x2A, 0xC4, 0x89, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x27, 0x04, 0xFD, 0x40, 0x00, 0x00, 0x00, 0x00, 0x27, 0xF4, 0xEE, 0x40, 0x00, 0x00, 0x00, 0x00,
+0x28, 0xE4, 0xED, 0x50, 0x00, 0x00, 0x00, 0x00, 0x29, 0x78, 0x95, 0x50, 0x00, 0x00, 0x00, 0x00,
+0x29, 0xD4, 0xD0, 0x40, 0x00, 0x00, 0x00, 0x00, 0x2A, 0xC4, 0xC1, 0x40, 0x00, 0x00, 0x00, 0x00,
0x2B, 0xB4, 0xB2, 0x40, 0x00, 0x00, 0x00, 0x00, 0x2C, 0xA4, 0xA3, 0x40, 0x00, 0x00, 0x00, 0x00,
0x2D, 0x94, 0x94, 0x40, 0x00, 0x00, 0x00, 0x00, 0x2E, 0x84, 0x85, 0x40, 0x00, 0x00, 0x00, 0x00,
0x2F, 0x74, 0x76, 0x40, 0x00, 0x00, 0x00, 0x00, 0x30, 0x64, 0x67, 0x40, 0x00, 0x00, 0x00, 0x00,
@@ -18536,14 +18622,17 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x3C, 0xA6, 0x19, 0x40, 0x00, 0x00, 0x00, 0x00, 0x3D, 0xBA, 0xF4, 0x40, 0x00, 0x00, 0x00, 0x00,
0x3E, 0x85, 0xFB, 0x40, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x9A, 0xD6, 0x40, 0x00, 0x00, 0x00, 0x00,
0x40, 0x65, 0xDD, 0x40, 0x00, 0x00, 0x00, 0x00, 0x41, 0x83, 0xF2, 0xC0, 0x00, 0x00, 0x00, 0x00,
-0x42, 0x35, 0xD1, 0x20, 0x00, 0x01, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x04, 0x05,
-0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x03, 0x02, 0x03, 0x05, 0x04,
+0x7F, 0xFF, 0xFF, 0xFF, 0x00, 0x01, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x04, 0x05,
+0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x06, 0x07, 0x04, 0x05, 0x04,
0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04,
-0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x03, 0x00, 0x00, 0x48, 0x24, 0x00, 0x00, 0x00, 0x00, 0x46,
-0x50, 0x00, 0x04, 0x00, 0x00, 0x62, 0x70, 0x01, 0x09, 0x00, 0x00, 0x54, 0x60, 0x00, 0x04, 0x00,
-0x00, 0x54, 0x60, 0x00, 0x04, 0x00, 0x00, 0x62, 0x70, 0x01, 0x09, 0x4C, 0x4D, 0x54, 0x00, 0x41,
-0x4C, 0x4D, 0x54, 0x00, 0x41, 0x4C, 0x4D, 0x53, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x41, 0x4C, 0x4D, 0x54, 0x2D, 0x36, 0x0A,
+0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x04, 0x00, 0x00, 0x48, 0x24, 0x00, 0x00, 0x00,
+0x00, 0x46, 0x50, 0x00, 0x04, 0x00, 0x00, 0x62, 0x70, 0x01, 0x08, 0x00, 0x00, 0x54, 0x60, 0x00,
+0x0C, 0x00, 0x00, 0x54, 0x60, 0x00, 0x0C, 0x00, 0x00, 0x62, 0x70, 0x01, 0x08, 0x00, 0x00, 0x54,
+0x60, 0x01, 0x0C, 0x00, 0x00, 0x46, 0x50, 0x00, 0x04, 0x00, 0x00, 0x62, 0x70, 0x01, 0x08, 0x00,
+0x00, 0x54, 0x60, 0x00, 0x0C, 0x4C, 0x4D, 0x54, 0x00, 0x2B, 0x30, 0x35, 0x00, 0x2B, 0x30, 0x37,
+0x00, 0x2B, 0x30, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x3C, 0x2B, 0x30, 0x36, 0x3E, 0x2D,
+0x36, 0x0A,
#endif
0x00, 0xCB, 0x52, 0xC8, 0x01, 0x88, 0x13, 0x18, 0x00, 0x00, 0x00, 0x17, 0x4B, 0x61, 0x7A, 0x61,
0x6B, 0x68, 0x73, 0x74, 0x61, 0x6E, 0x20, 0x28, 0x6D, 0x6F, 0x73, 0x74, 0x20, 0x61, 0x72, 0x65,
@@ -18682,7 +18771,7 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x1E, 0x8B, 0xF5, 0x60, 0x1F, 0x7B, 0xE6, 0x60, 0x20, 0x6B, 0xD7, 0x60, 0x21, 0x5B, 0xC8, 0x60,
0x22, 0x4B, 0xB9, 0x60, 0x23, 0x3B, 0xAA, 0x60, 0x24, 0x2B, 0x9B, 0x60, 0x25, 0x1B, 0x8C, 0x60,
0x26, 0x0B, 0x7D, 0x60, 0x27, 0x04, 0xA8, 0xE0, 0x27, 0xF4, 0x99, 0xE0, 0x28, 0xE4, 0x98, 0xF0,
-0x29, 0x78, 0x40, 0xF0, 0x29, 0xD4, 0x51, 0xB0, 0x2A, 0xC4, 0x34, 0xA0, 0x2B, 0xB4, 0x5D, 0xE0,
+0x29, 0x78, 0x40, 0xF0, 0x29, 0xD4, 0x7B, 0xE0, 0x2A, 0xC4, 0x6C, 0xE0, 0x2B, 0xB4, 0x5D, 0xE0,
0x2C, 0xA4, 0x4E, 0xE0, 0x2D, 0x94, 0x3F, 0xE0, 0x2E, 0x84, 0x30, 0xE0, 0x2F, 0x74, 0x21, 0xE0,
0x30, 0x64, 0x12, 0xE0, 0x31, 0x5D, 0x3E, 0x60, 0x32, 0x72, 0x19, 0x60, 0x33, 0x3D, 0x20, 0x60,
0x34, 0x51, 0xFB, 0x60, 0x35, 0x1D, 0x02, 0x60, 0x36, 0x31, 0xDD, 0x60, 0x36, 0xFC, 0xE4, 0x60,
@@ -18693,7 +18782,7 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x47, 0x23, 0x44, 0x60, 0x47, 0xEE, 0x4B, 0x60, 0x49, 0x03, 0x26, 0x60, 0x49, 0xCE, 0x2D, 0x60,
0x4A, 0xE3, 0x08, 0x60, 0x4B, 0xAE, 0x0F, 0x60, 0x4C, 0xCC, 0x32, 0xF0, 0x4D, 0x8D, 0xFF, 0x70,
0x00, 0x01, 0x03, 0x02, 0x03, 0x04, 0x01, 0x04, 0x01, 0x04, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06,
-0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x07, 0x08, 0x05, 0x04, 0x01, 0x06, 0x05, 0x06, 0x05,
+0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x07, 0x08, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05,
0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05,
0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x07, 0x08,
0x05, 0x00, 0x00, 0xA6, 0x64, 0x00, 0x00, 0x00, 0x00, 0xA8, 0xC0, 0x00, 0x04, 0x00, 0x00, 0xC4,
@@ -18719,7 +18808,7 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x25, 0x1B, 0x8C, 0x60, 0x00, 0x00, 0x00, 0x00, 0x26, 0x0B, 0x7D, 0x60, 0x00, 0x00, 0x00, 0x00,
0x27, 0x04, 0xA8, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x27, 0xF4, 0x99, 0xE0, 0x00, 0x00, 0x00, 0x00,
0x28, 0xE4, 0x98, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x29, 0x78, 0x40, 0xF0, 0x00, 0x00, 0x00, 0x00,
-0x29, 0xD4, 0x51, 0xB0, 0x00, 0x00, 0x00, 0x00, 0x2A, 0xC4, 0x34, 0xA0, 0x00, 0x00, 0x00, 0x00,
+0x29, 0xD4, 0x7B, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x2A, 0xC4, 0x6C, 0xE0, 0x00, 0x00, 0x00, 0x00,
0x2B, 0xB4, 0x5D, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x2C, 0xA4, 0x4E, 0xE0, 0x00, 0x00, 0x00, 0x00,
0x2D, 0x94, 0x3F, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x2E, 0x84, 0x30, 0xE0, 0x00, 0x00, 0x00, 0x00,
0x2F, 0x74, 0x21, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x30, 0x64, 0x12, 0xE0, 0x00, 0x00, 0x00, 0x00,
@@ -18739,7 +18828,7 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x49, 0xCE, 0x2D, 0x60, 0x00, 0x00, 0x00, 0x00, 0x4A, 0xE3, 0x08, 0x60, 0x00, 0x00, 0x00, 0x00,
0x4B, 0xAE, 0x0F, 0x60, 0x00, 0x00, 0x00, 0x00, 0x4C, 0xCC, 0x32, 0xF0, 0x00, 0x00, 0x00, 0x00,
0x4D, 0x8D, 0xFF, 0x70, 0x00, 0x01, 0x03, 0x02, 0x03, 0x04, 0x01, 0x04, 0x01, 0x04, 0x05, 0x06,
-0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x07, 0x08, 0x05, 0x04, 0x01,
+0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x07, 0x08, 0x05, 0x06, 0x05,
0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05,
0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05,
0x06, 0x05, 0x07, 0x08, 0x05, 0x00, 0x00, 0xA6, 0x64, 0x00, 0x00, 0x00, 0x00, 0xA8, 0xC0, 0x00,
@@ -18755,54 +18844,50 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
/* Asia/Aqtau */
0x50, 0x48, 0x50, 0x32, 0x01, 0x4B, 0x5A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x1F, 0x80, 0x00, 0x00, 0x00,
-0xAA, 0x19, 0x94, 0xE0, 0xB5, 0xA3, 0xFD, 0x40, 0xF2, 0xD4, 0xAE, 0x30, 0x16, 0x18, 0xCE, 0x30,
-0x17, 0x08, 0xB1, 0x20, 0x17, 0xF9, 0xF3, 0xA0, 0x18, 0xE9, 0xF2, 0xB0, 0x19, 0xDB, 0x27, 0x20,
-0x1A, 0xCC, 0x77, 0xB0, 0x1B, 0xBC, 0x84, 0xD0, 0x1C, 0xAC, 0x75, 0xD0, 0x1D, 0x9C, 0x66, 0xD0,
-0x1E, 0x8C, 0x57, 0xD0, 0x1F, 0x7C, 0x48, 0xD0, 0x20, 0x6C, 0x39, 0xD0, 0x21, 0x5C, 0x2A, 0xD0,
-0x22, 0x4C, 0x1B, 0xD0, 0x23, 0x3C, 0x0C, 0xD0, 0x24, 0x2B, 0xFD, 0xD0, 0x25, 0x1B, 0xEE, 0xD0,
-0x26, 0x0B, 0xDF, 0xD0, 0x27, 0x05, 0x0B, 0x50, 0x27, 0x7F, 0x8A, 0xB0, 0x29, 0x4B, 0xA6, 0x30,
-0x29, 0xD4, 0xB4, 0x20, 0x2A, 0xC4, 0x97, 0x10, 0x2B, 0xB4, 0xC0, 0x50, 0x2C, 0xA4, 0xB1, 0x50,
-0x2D, 0x94, 0xA2, 0x50, 0x2E, 0x84, 0x93, 0x50, 0x2F, 0x74, 0x84, 0x50, 0x30, 0x64, 0x83, 0x60,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x10, 0x80, 0x00, 0x00, 0x00,
+0xAA, 0x19, 0x94, 0xE0, 0xB5, 0xA3, 0xFD, 0x40, 0x16, 0x18, 0xCE, 0x30, 0x17, 0x08, 0xB1, 0x20,
+0x17, 0xF9, 0xF3, 0xA0, 0x18, 0xE9, 0xF2, 0xB0, 0x19, 0xDB, 0x27, 0x20, 0x1A, 0xCC, 0x77, 0xB0,
+0x1B, 0xBC, 0x84, 0xD0, 0x1C, 0xAC, 0x75, 0xD0, 0x1D, 0x9C, 0x66, 0xD0, 0x1E, 0x8C, 0x57, 0xD0,
+0x1F, 0x7C, 0x48, 0xD0, 0x20, 0x6C, 0x39, 0xD0, 0x21, 0x5C, 0x2A, 0xD0, 0x22, 0x4C, 0x1B, 0xD0,
+0x23, 0x3C, 0x0C, 0xD0, 0x24, 0x2B, 0xFD, 0xD0, 0x25, 0x1B, 0xEE, 0xD0, 0x26, 0x0B, 0xDF, 0xD0,
+0x27, 0x05, 0x0B, 0x50, 0x27, 0xF4, 0xFC, 0x50, 0x28, 0xE4, 0xFB, 0x60, 0x29, 0x78, 0xA3, 0x60,
+0x29, 0xD4, 0xDE, 0x50, 0x2A, 0xC4, 0xCF, 0x50, 0x2B, 0xB4, 0xC0, 0x50, 0x2C, 0xA4, 0xB1, 0x50,
+0x2D, 0x94, 0xA2, 0x50, 0x2E, 0x84, 0x93, 0x50, 0x2F, 0x74, 0x92, 0x60, 0x30, 0x64, 0x83, 0x60,
0x31, 0x5D, 0xAE, 0xE0, 0x32, 0x72, 0x89, 0xE0, 0x33, 0x3D, 0x90, 0xE0, 0x34, 0x52, 0x6B, 0xE0,
0x35, 0x1D, 0x72, 0xE0, 0x36, 0x32, 0x4D, 0xE0, 0x36, 0xFD, 0x54, 0xE0, 0x38, 0x1B, 0x6A, 0x60,
0x38, 0xDD, 0x36, 0xE0, 0x39, 0xFB, 0x4C, 0x60, 0x3A, 0xBD, 0x18, 0xE0, 0x3B, 0xDB, 0x2E, 0x60,
0x3C, 0xA6, 0x35, 0x60, 0x3D, 0xBB, 0x10, 0x60, 0x3E, 0x86, 0x17, 0x60, 0x3F, 0x9A, 0xF2, 0x60,
-0x40, 0x65, 0xF9, 0x60, 0x41, 0x84, 0x0E, 0xE0, 0x42, 0x35, 0xED, 0x40, 0x00, 0x01, 0x02, 0x03,
-0x04, 0x05, 0x03, 0x05, 0x03, 0x05, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07,
-0x06, 0x07, 0x06, 0x03, 0x09, 0x08, 0x09, 0x0A, 0x0B, 0x0A, 0x0B, 0x0C, 0x0D, 0x0C, 0x0D, 0x0C,
-0x0D, 0x0C, 0x0D, 0x0C, 0x0D, 0x0C, 0x0D, 0x0C, 0x0D, 0x0C, 0x0D, 0x0C, 0x0D, 0x0C, 0x0D, 0x09,
+0x40, 0x65, 0xF9, 0x60, 0x41, 0x84, 0x0E, 0xE0, 0x7F, 0xFF, 0xFF, 0xFF, 0x00, 0x01, 0x02, 0x03,
+0x04, 0x02, 0x04, 0x02, 0x04, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05,
+0x06, 0x05, 0x07, 0x08, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x08, 0x07, 0x08, 0x07, 0x08, 0x07,
+0x08, 0x07, 0x08, 0x07, 0x08, 0x07, 0x08, 0x07, 0x08, 0x07, 0x08, 0x07, 0x08, 0x07, 0x05, 0x05,
0x00, 0x00, 0x2F, 0x20, 0x00, 0x00, 0x00, 0x00, 0x38, 0x40, 0x00, 0x04, 0x00, 0x00, 0x46, 0x50,
-0x00, 0x04, 0x00, 0x00, 0x46, 0x50, 0x00, 0x09, 0x00, 0x00, 0x54, 0x60, 0x00, 0x09, 0x00, 0x00,
-0x54, 0x60, 0x01, 0x0E, 0x00, 0x00, 0x46, 0x50, 0x00, 0x09, 0x00, 0x00, 0x54, 0x60, 0x01, 0x0E,
-0x00, 0x00, 0x54, 0x60, 0x01, 0x14, 0x00, 0x00, 0x46, 0x50, 0x00, 0x1A, 0x00, 0x00, 0x54, 0x60,
-0x01, 0x14, 0x00, 0x00, 0x46, 0x50, 0x00, 0x1A, 0x00, 0x00, 0x46, 0x50, 0x01, 0x14, 0x00, 0x00,
-0x38, 0x40, 0x00, 0x1A, 0x00, 0x00, 0x38, 0x40, 0x00, 0x1A, 0x00, 0x00, 0x46, 0x50, 0x00, 0x1A,
-0x4C, 0x4D, 0x54, 0x00, 0x46, 0x4F, 0x52, 0x54, 0x00, 0x53, 0x48, 0x45, 0x54, 0x00, 0x53, 0x48,
-0x45, 0x53, 0x54, 0x00, 0x41, 0x51, 0x54, 0x53, 0x54, 0x00, 0x41, 0x51, 0x54, 0x54, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x08, 0x00, 0x00, 0x54, 0x60, 0x00, 0x0C, 0x00, 0x00, 0x54, 0x60, 0x01, 0x0C, 0x00, 0x00,
+0x46, 0x50, 0x00, 0x08, 0x00, 0x00, 0x54, 0x60, 0x01, 0x0C, 0x00, 0x00, 0x46, 0x50, 0x01, 0x08,
+0x00, 0x00, 0x38, 0x40, 0x00, 0x04, 0x00, 0x00, 0x46, 0x50, 0x00, 0x08, 0x4C, 0x4D, 0x54, 0x00,
+0x2B, 0x30, 0x34, 0x00, 0x2B, 0x30, 0x35, 0x00, 0x2B, 0x30, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
#ifdef TIMELIB_SUPPORTS_V2DATA
0x54, 0x5A, 0x69, 0x66, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x1F, 0xF8, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x10, 0xF8, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xAA, 0x19, 0x94, 0xE0, 0xFF, 0xFF, 0xFF, 0xFF,
-0xB5, 0xA3, 0xFD, 0x40, 0xFF, 0xFF, 0xFF, 0xFF, 0xF2, 0xD4, 0xAE, 0x30, 0x00, 0x00, 0x00, 0x00,
-0x16, 0x18, 0xCE, 0x30, 0x00, 0x00, 0x00, 0x00, 0x17, 0x08, 0xB1, 0x20, 0x00, 0x00, 0x00, 0x00,
-0x17, 0xF9, 0xF3, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x18, 0xE9, 0xF2, 0xB0, 0x00, 0x00, 0x00, 0x00,
-0x19, 0xDB, 0x27, 0x20, 0x00, 0x00, 0x00, 0x00, 0x1A, 0xCC, 0x77, 0xB0, 0x00, 0x00, 0x00, 0x00,
-0x1B, 0xBC, 0x84, 0xD0, 0x00, 0x00, 0x00, 0x00, 0x1C, 0xAC, 0x75, 0xD0, 0x00, 0x00, 0x00, 0x00,
-0x1D, 0x9C, 0x66, 0xD0, 0x00, 0x00, 0x00, 0x00, 0x1E, 0x8C, 0x57, 0xD0, 0x00, 0x00, 0x00, 0x00,
-0x1F, 0x7C, 0x48, 0xD0, 0x00, 0x00, 0x00, 0x00, 0x20, 0x6C, 0x39, 0xD0, 0x00, 0x00, 0x00, 0x00,
-0x21, 0x5C, 0x2A, 0xD0, 0x00, 0x00, 0x00, 0x00, 0x22, 0x4C, 0x1B, 0xD0, 0x00, 0x00, 0x00, 0x00,
-0x23, 0x3C, 0x0C, 0xD0, 0x00, 0x00, 0x00, 0x00, 0x24, 0x2B, 0xFD, 0xD0, 0x00, 0x00, 0x00, 0x00,
-0x25, 0x1B, 0xEE, 0xD0, 0x00, 0x00, 0x00, 0x00, 0x26, 0x0B, 0xDF, 0xD0, 0x00, 0x00, 0x00, 0x00,
-0x27, 0x05, 0x0B, 0x50, 0x00, 0x00, 0x00, 0x00, 0x27, 0x7F, 0x8A, 0xB0, 0x00, 0x00, 0x00, 0x00,
-0x29, 0x4B, 0xA6, 0x30, 0x00, 0x00, 0x00, 0x00, 0x29, 0xD4, 0xB4, 0x20, 0x00, 0x00, 0x00, 0x00,
-0x2A, 0xC4, 0x97, 0x10, 0x00, 0x00, 0x00, 0x00, 0x2B, 0xB4, 0xC0, 0x50, 0x00, 0x00, 0x00, 0x00,
+0xB5, 0xA3, 0xFD, 0x40, 0x00, 0x00, 0x00, 0x00, 0x16, 0x18, 0xCE, 0x30, 0x00, 0x00, 0x00, 0x00,
+0x17, 0x08, 0xB1, 0x20, 0x00, 0x00, 0x00, 0x00, 0x17, 0xF9, 0xF3, 0xA0, 0x00, 0x00, 0x00, 0x00,
+0x18, 0xE9, 0xF2, 0xB0, 0x00, 0x00, 0x00, 0x00, 0x19, 0xDB, 0x27, 0x20, 0x00, 0x00, 0x00, 0x00,
+0x1A, 0xCC, 0x77, 0xB0, 0x00, 0x00, 0x00, 0x00, 0x1B, 0xBC, 0x84, 0xD0, 0x00, 0x00, 0x00, 0x00,
+0x1C, 0xAC, 0x75, 0xD0, 0x00, 0x00, 0x00, 0x00, 0x1D, 0x9C, 0x66, 0xD0, 0x00, 0x00, 0x00, 0x00,
+0x1E, 0x8C, 0x57, 0xD0, 0x00, 0x00, 0x00, 0x00, 0x1F, 0x7C, 0x48, 0xD0, 0x00, 0x00, 0x00, 0x00,
+0x20, 0x6C, 0x39, 0xD0, 0x00, 0x00, 0x00, 0x00, 0x21, 0x5C, 0x2A, 0xD0, 0x00, 0x00, 0x00, 0x00,
+0x22, 0x4C, 0x1B, 0xD0, 0x00, 0x00, 0x00, 0x00, 0x23, 0x3C, 0x0C, 0xD0, 0x00, 0x00, 0x00, 0x00,
+0x24, 0x2B, 0xFD, 0xD0, 0x00, 0x00, 0x00, 0x00, 0x25, 0x1B, 0xEE, 0xD0, 0x00, 0x00, 0x00, 0x00,
+0x26, 0x0B, 0xDF, 0xD0, 0x00, 0x00, 0x00, 0x00, 0x27, 0x05, 0x0B, 0x50, 0x00, 0x00, 0x00, 0x00,
+0x27, 0xF4, 0xFC, 0x50, 0x00, 0x00, 0x00, 0x00, 0x28, 0xE4, 0xFB, 0x60, 0x00, 0x00, 0x00, 0x00,
+0x29, 0x78, 0xA3, 0x60, 0x00, 0x00, 0x00, 0x00, 0x29, 0xD4, 0xDE, 0x50, 0x00, 0x00, 0x00, 0x00,
+0x2A, 0xC4, 0xCF, 0x50, 0x00, 0x00, 0x00, 0x00, 0x2B, 0xB4, 0xC0, 0x50, 0x00, 0x00, 0x00, 0x00,
0x2C, 0xA4, 0xB1, 0x50, 0x00, 0x00, 0x00, 0x00, 0x2D, 0x94, 0xA2, 0x50, 0x00, 0x00, 0x00, 0x00,
-0x2E, 0x84, 0x93, 0x50, 0x00, 0x00, 0x00, 0x00, 0x2F, 0x74, 0x84, 0x50, 0x00, 0x00, 0x00, 0x00,
+0x2E, 0x84, 0x93, 0x50, 0x00, 0x00, 0x00, 0x00, 0x2F, 0x74, 0x92, 0x60, 0x00, 0x00, 0x00, 0x00,
0x30, 0x64, 0x83, 0x60, 0x00, 0x00, 0x00, 0x00, 0x31, 0x5D, 0xAE, 0xE0, 0x00, 0x00, 0x00, 0x00,
0x32, 0x72, 0x89, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x33, 0x3D, 0x90, 0xE0, 0x00, 0x00, 0x00, 0x00,
0x34, 0x52, 0x6B, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x35, 0x1D, 0x72, 0xE0, 0x00, 0x00, 0x00, 0x00,
@@ -18812,21 +18897,17 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x3B, 0xDB, 0x2E, 0x60, 0x00, 0x00, 0x00, 0x00, 0x3C, 0xA6, 0x35, 0x60, 0x00, 0x00, 0x00, 0x00,
0x3D, 0xBB, 0x10, 0x60, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x86, 0x17, 0x60, 0x00, 0x00, 0x00, 0x00,
0x3F, 0x9A, 0xF2, 0x60, 0x00, 0x00, 0x00, 0x00, 0x40, 0x65, 0xF9, 0x60, 0x00, 0x00, 0x00, 0x00,
-0x41, 0x84, 0x0E, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x42, 0x35, 0xED, 0x40, 0x00, 0x01, 0x02, 0x03,
-0x04, 0x05, 0x03, 0x05, 0x03, 0x05, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07,
-0x06, 0x07, 0x06, 0x03, 0x09, 0x08, 0x09, 0x0A, 0x0B, 0x0A, 0x0B, 0x0C, 0x0D, 0x0C, 0x0D, 0x0C,
-0x0D, 0x0C, 0x0D, 0x0C, 0x0D, 0x0C, 0x0D, 0x0C, 0x0D, 0x0C, 0x0D, 0x0C, 0x0D, 0x0C, 0x0D, 0x09,
+0x41, 0x84, 0x0E, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0x00, 0x01, 0x02, 0x03,
+0x04, 0x02, 0x04, 0x02, 0x04, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05,
+0x06, 0x05, 0x07, 0x08, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x08, 0x07, 0x08, 0x07, 0x08, 0x07,
+0x08, 0x07, 0x08, 0x07, 0x08, 0x07, 0x08, 0x07, 0x08, 0x07, 0x08, 0x07, 0x08, 0x07, 0x05, 0x05,
0x00, 0x00, 0x2F, 0x20, 0x00, 0x00, 0x00, 0x00, 0x38, 0x40, 0x00, 0x04, 0x00, 0x00, 0x46, 0x50,
-0x00, 0x04, 0x00, 0x00, 0x46, 0x50, 0x00, 0x09, 0x00, 0x00, 0x54, 0x60, 0x00, 0x09, 0x00, 0x00,
-0x54, 0x60, 0x01, 0x0E, 0x00, 0x00, 0x46, 0x50, 0x00, 0x09, 0x00, 0x00, 0x54, 0x60, 0x01, 0x0E,
-0x00, 0x00, 0x54, 0x60, 0x01, 0x14, 0x00, 0x00, 0x46, 0x50, 0x00, 0x1A, 0x00, 0x00, 0x54, 0x60,
-0x01, 0x14, 0x00, 0x00, 0x46, 0x50, 0x00, 0x1A, 0x00, 0x00, 0x46, 0x50, 0x01, 0x14, 0x00, 0x00,
-0x38, 0x40, 0x00, 0x1A, 0x00, 0x00, 0x38, 0x40, 0x00, 0x1A, 0x00, 0x00, 0x46, 0x50, 0x00, 0x1A,
-0x4C, 0x4D, 0x54, 0x00, 0x46, 0x4F, 0x52, 0x54, 0x00, 0x53, 0x48, 0x45, 0x54, 0x00, 0x53, 0x48,
-0x45, 0x53, 0x54, 0x00, 0x41, 0x51, 0x54, 0x53, 0x54, 0x00, 0x41, 0x51, 0x54, 0x54, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A,
-0x41, 0x51, 0x54, 0x54, 0x2D, 0x35, 0x0A,
+0x00, 0x08, 0x00, 0x00, 0x54, 0x60, 0x00, 0x0C, 0x00, 0x00, 0x54, 0x60, 0x01, 0x0C, 0x00, 0x00,
+0x46, 0x50, 0x00, 0x08, 0x00, 0x00, 0x54, 0x60, 0x01, 0x0C, 0x00, 0x00, 0x46, 0x50, 0x01, 0x08,
+0x00, 0x00, 0x38, 0x40, 0x00, 0x04, 0x00, 0x00, 0x46, 0x50, 0x00, 0x08, 0x4C, 0x4D, 0x54, 0x00,
+0x2B, 0x30, 0x34, 0x00, 0x2B, 0x30, 0x35, 0x00, 0x2B, 0x30, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x0A, 0x3C, 0x2B, 0x30, 0x35, 0x3E, 0x2D, 0x35, 0x0A,
#endif
0x00, 0xCD, 0x41, 0x92, 0x01, 0x5F, 0x5B, 0xEA, 0x00, 0x00, 0x00, 0x2C, 0x41, 0x74, 0x79, 0x72,
0x61, 0x75, 0x2F, 0x41, 0x74, 0x69, 0x72, 0x61, 0x75, 0x2F, 0x47, 0x75, 0x72, 0x27, 0x79, 0x65,
@@ -18836,35 +18917,34 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
/* Asia/Aqtobe */
0x50, 0x48, 0x50, 0x32, 0x01, 0x4B, 0x5A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x1A, 0x80, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x10, 0x80, 0x00, 0x00, 0x00,
0xAA, 0x19, 0x8E, 0x68, 0xB5, 0xA3, 0xFD, 0x40, 0x15, 0x27, 0x8B, 0xB0, 0x16, 0x18, 0xC0, 0x20,
0x17, 0x08, 0xB1, 0x20, 0x17, 0xF9, 0xF3, 0xA0, 0x18, 0xE9, 0xF2, 0xB0, 0x19, 0xDB, 0x27, 0x20,
0x1A, 0xCC, 0x77, 0xB0, 0x1B, 0xBC, 0x84, 0xD0, 0x1C, 0xAC, 0x75, 0xD0, 0x1D, 0x9C, 0x66, 0xD0,
0x1E, 0x8C, 0x57, 0xD0, 0x1F, 0x7C, 0x48, 0xD0, 0x20, 0x6C, 0x39, 0xD0, 0x21, 0x5C, 0x2A, 0xD0,
0x22, 0x4C, 0x1B, 0xD0, 0x23, 0x3C, 0x0C, 0xD0, 0x24, 0x2B, 0xFD, 0xD0, 0x25, 0x1B, 0xEE, 0xD0,
-0x26, 0x0B, 0xDF, 0xD0, 0x27, 0x05, 0x0B, 0x50, 0x27, 0x7F, 0x8A, 0xB0, 0x29, 0x4B, 0xA6, 0x30,
-0x29, 0xD4, 0xB4, 0x20, 0x2A, 0xC4, 0x97, 0x10, 0x2B, 0xB4, 0xC0, 0x50, 0x2C, 0xA4, 0xB1, 0x50,
-0x2D, 0x94, 0xA2, 0x50, 0x2E, 0x84, 0x93, 0x50, 0x2F, 0x74, 0x84, 0x50, 0x30, 0x64, 0x75, 0x50,
-0x31, 0x5D, 0xA0, 0xD0, 0x32, 0x72, 0x7B, 0xD0, 0x33, 0x3D, 0x82, 0xD0, 0x34, 0x52, 0x5D, 0xD0,
-0x35, 0x1D, 0x64, 0xD0, 0x36, 0x32, 0x3F, 0xD0, 0x36, 0xFD, 0x46, 0xD0, 0x38, 0x1B, 0x5C, 0x50,
-0x38, 0xDD, 0x28, 0xD0, 0x39, 0xFB, 0x3E, 0x50, 0x3A, 0xBD, 0x0A, 0xD0, 0x3B, 0xDB, 0x20, 0x50,
-0x3C, 0xA6, 0x27, 0x50, 0x3D, 0xBB, 0x02, 0x50, 0x3E, 0x86, 0x09, 0x50, 0x3F, 0x9A, 0xE4, 0x50,
-0x40, 0x65, 0xEB, 0x50, 0x41, 0x84, 0x00, 0xD0, 0x42, 0x35, 0xDF, 0x30, 0x00, 0x01, 0x02, 0x03,
-0x04, 0x03, 0x02, 0x03, 0x02, 0x03, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06,
-0x05, 0x06, 0x05, 0x02, 0x08, 0x07, 0x08, 0x09, 0x0A, 0x09, 0x0A, 0x09, 0x0A, 0x09, 0x0A, 0x09,
-0x0A, 0x09, 0x0A, 0x09, 0x0A, 0x09, 0x0A, 0x09, 0x0A, 0x09, 0x0A, 0x09, 0x0A, 0x09, 0x0A, 0x08,
-0x00, 0x00, 0x35, 0x98, 0x00, 0x00, 0x00, 0x00, 0x38, 0x40, 0x00, 0x04, 0x00, 0x00, 0x46, 0x50,
-0x00, 0x04, 0x00, 0x00, 0x54, 0x60, 0x01, 0x09, 0x00, 0x00, 0x54, 0x60, 0x00, 0x04, 0x00, 0x00,
-0x46, 0x50, 0x00, 0x04, 0x00, 0x00, 0x54, 0x60, 0x01, 0x09, 0x00, 0x00, 0x54, 0x60, 0x01, 0x0F,
-0x00, 0x00, 0x46, 0x50, 0x00, 0x15, 0x00, 0x00, 0x54, 0x60, 0x01, 0x0F, 0x00, 0x00, 0x46, 0x50,
-0x00, 0x15, 0x4C, 0x4D, 0x54, 0x00, 0x41, 0x4B, 0x54, 0x54, 0x00, 0x41, 0x4B, 0x54, 0x53, 0x54,
-0x00, 0x41, 0x51, 0x54, 0x53, 0x54, 0x00, 0x41, 0x51, 0x54, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00,
+0x26, 0x0B, 0xDF, 0xD0, 0x27, 0x05, 0x0B, 0x50, 0x27, 0xF4, 0xFC, 0x50, 0x28, 0xE4, 0xFB, 0x60,
+0x29, 0x78, 0xA3, 0x60, 0x29, 0xD4, 0xDE, 0x50, 0x2A, 0xC4, 0xCF, 0x50, 0x2B, 0xB4, 0xC0, 0x50,
+0x2C, 0xA4, 0xB1, 0x50, 0x2D, 0x94, 0xA2, 0x50, 0x2E, 0x84, 0x93, 0x50, 0x2F, 0x74, 0x84, 0x50,
+0x30, 0x64, 0x75, 0x50, 0x31, 0x5D, 0xA0, 0xD0, 0x32, 0x72, 0x7B, 0xD0, 0x33, 0x3D, 0x82, 0xD0,
+0x34, 0x52, 0x5D, 0xD0, 0x35, 0x1D, 0x64, 0xD0, 0x36, 0x32, 0x3F, 0xD0, 0x36, 0xFD, 0x46, 0xD0,
+0x38, 0x1B, 0x5C, 0x50, 0x38, 0xDD, 0x28, 0xD0, 0x39, 0xFB, 0x3E, 0x50, 0x3A, 0xBD, 0x0A, 0xD0,
+0x3B, 0xDB, 0x20, 0x50, 0x3C, 0xA6, 0x27, 0x50, 0x3D, 0xBB, 0x02, 0x50, 0x3E, 0x86, 0x09, 0x50,
+0x3F, 0x9A, 0xE4, 0x50, 0x40, 0x65, 0xEB, 0x50, 0x41, 0x84, 0x00, 0xD0, 0x7F, 0xFF, 0xFF, 0xFF,
+0x00, 0x01, 0x02, 0x03, 0x04, 0x03, 0x02, 0x03, 0x02, 0x03, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06,
+0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x07, 0x08, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05,
+0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05,
+0x06, 0x05, 0x06, 0x05, 0x05, 0x00, 0x00, 0x35, 0x98, 0x00, 0x00, 0x00, 0x00, 0x38, 0x40, 0x00,
+0x04, 0x00, 0x00, 0x46, 0x50, 0x00, 0x08, 0x00, 0x00, 0x54, 0x60, 0x01, 0x0C, 0x00, 0x00, 0x54,
+0x60, 0x00, 0x0C, 0x00, 0x00, 0x46, 0x50, 0x00, 0x08, 0x00, 0x00, 0x54, 0x60, 0x01, 0x0C, 0x00,
+0x00, 0x46, 0x50, 0x01, 0x08, 0x00, 0x00, 0x38, 0x40, 0x00, 0x04, 0x00, 0x00, 0x54, 0x60, 0x01,
+0x0C, 0x00, 0x00, 0x46, 0x50, 0x00, 0x08, 0x4C, 0x4D, 0x54, 0x00, 0x2B, 0x30, 0x34, 0x00, 0x2B,
+0x30, 0x35, 0x00, 0x2B, 0x30, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
#ifdef TIMELIB_SUPPORTS_V2DATA
0x54, 0x5A, 0x69, 0x66, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x1A, 0xF8, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x10, 0xF8, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xAA, 0x19, 0x8E, 0x68, 0xFF, 0xFF, 0xFF, 0xFF,
0xB5, 0xA3, 0xFD, 0x40, 0x00, 0x00, 0x00, 0x00, 0x15, 0x27, 0x8B, 0xB0, 0x00, 0x00, 0x00, 0x00,
0x16, 0x18, 0xC0, 0x20, 0x00, 0x00, 0x00, 0x00, 0x17, 0x08, 0xB1, 0x20, 0x00, 0x00, 0x00, 0x00,
@@ -18876,32 +18956,32 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x21, 0x5C, 0x2A, 0xD0, 0x00, 0x00, 0x00, 0x00, 0x22, 0x4C, 0x1B, 0xD0, 0x00, 0x00, 0x00, 0x00,
0x23, 0x3C, 0x0C, 0xD0, 0x00, 0x00, 0x00, 0x00, 0x24, 0x2B, 0xFD, 0xD0, 0x00, 0x00, 0x00, 0x00,
0x25, 0x1B, 0xEE, 0xD0, 0x00, 0x00, 0x00, 0x00, 0x26, 0x0B, 0xDF, 0xD0, 0x00, 0x00, 0x00, 0x00,
-0x27, 0x05, 0x0B, 0x50, 0x00, 0x00, 0x00, 0x00, 0x27, 0x7F, 0x8A, 0xB0, 0x00, 0x00, 0x00, 0x00,
-0x29, 0x4B, 0xA6, 0x30, 0x00, 0x00, 0x00, 0x00, 0x29, 0xD4, 0xB4, 0x20, 0x00, 0x00, 0x00, 0x00,
-0x2A, 0xC4, 0x97, 0x10, 0x00, 0x00, 0x00, 0x00, 0x2B, 0xB4, 0xC0, 0x50, 0x00, 0x00, 0x00, 0x00,
-0x2C, 0xA4, 0xB1, 0x50, 0x00, 0x00, 0x00, 0x00, 0x2D, 0x94, 0xA2, 0x50, 0x00, 0x00, 0x00, 0x00,
-0x2E, 0x84, 0x93, 0x50, 0x00, 0x00, 0x00, 0x00, 0x2F, 0x74, 0x84, 0x50, 0x00, 0x00, 0x00, 0x00,
-0x30, 0x64, 0x75, 0x50, 0x00, 0x00, 0x00, 0x00, 0x31, 0x5D, 0xA0, 0xD0, 0x00, 0x00, 0x00, 0x00,
-0x32, 0x72, 0x7B, 0xD0, 0x00, 0x00, 0x00, 0x00, 0x33, 0x3D, 0x82, 0xD0, 0x00, 0x00, 0x00, 0x00,
-0x34, 0x52, 0x5D, 0xD0, 0x00, 0x00, 0x00, 0x00, 0x35, 0x1D, 0x64, 0xD0, 0x00, 0x00, 0x00, 0x00,
-0x36, 0x32, 0x3F, 0xD0, 0x00, 0x00, 0x00, 0x00, 0x36, 0xFD, 0x46, 0xD0, 0x00, 0x00, 0x00, 0x00,
-0x38, 0x1B, 0x5C, 0x50, 0x00, 0x00, 0x00, 0x00, 0x38, 0xDD, 0x28, 0xD0, 0x00, 0x00, 0x00, 0x00,
-0x39, 0xFB, 0x3E, 0x50, 0x00, 0x00, 0x00, 0x00, 0x3A, 0xBD, 0x0A, 0xD0, 0x00, 0x00, 0x00, 0x00,
-0x3B, 0xDB, 0x20, 0x50, 0x00, 0x00, 0x00, 0x00, 0x3C, 0xA6, 0x27, 0x50, 0x00, 0x00, 0x00, 0x00,
-0x3D, 0xBB, 0x02, 0x50, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x86, 0x09, 0x50, 0x00, 0x00, 0x00, 0x00,
-0x3F, 0x9A, 0xE4, 0x50, 0x00, 0x00, 0x00, 0x00, 0x40, 0x65, 0xEB, 0x50, 0x00, 0x00, 0x00, 0x00,
-0x41, 0x84, 0x00, 0xD0, 0x00, 0x00, 0x00, 0x00, 0x42, 0x35, 0xDF, 0x30, 0x00, 0x01, 0x02, 0x03,
-0x04, 0x03, 0x02, 0x03, 0x02, 0x03, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06,
-0x05, 0x06, 0x05, 0x02, 0x08, 0x07, 0x08, 0x09, 0x0A, 0x09, 0x0A, 0x09, 0x0A, 0x09, 0x0A, 0x09,
-0x0A, 0x09, 0x0A, 0x09, 0x0A, 0x09, 0x0A, 0x09, 0x0A, 0x09, 0x0A, 0x09, 0x0A, 0x09, 0x0A, 0x08,
-0x00, 0x00, 0x35, 0x98, 0x00, 0x00, 0x00, 0x00, 0x38, 0x40, 0x00, 0x04, 0x00, 0x00, 0x46, 0x50,
-0x00, 0x04, 0x00, 0x00, 0x54, 0x60, 0x01, 0x09, 0x00, 0x00, 0x54, 0x60, 0x00, 0x04, 0x00, 0x00,
-0x46, 0x50, 0x00, 0x04, 0x00, 0x00, 0x54, 0x60, 0x01, 0x09, 0x00, 0x00, 0x54, 0x60, 0x01, 0x0F,
-0x00, 0x00, 0x46, 0x50, 0x00, 0x15, 0x00, 0x00, 0x54, 0x60, 0x01, 0x0F, 0x00, 0x00, 0x46, 0x50,
-0x00, 0x15, 0x4C, 0x4D, 0x54, 0x00, 0x41, 0x4B, 0x54, 0x54, 0x00, 0x41, 0x4B, 0x54, 0x53, 0x54,
-0x00, 0x41, 0x51, 0x54, 0x53, 0x54, 0x00, 0x41, 0x51, 0x54, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x0A, 0x41, 0x51, 0x54, 0x54, 0x2D, 0x35, 0x0A,
+0x27, 0x05, 0x0B, 0x50, 0x00, 0x00, 0x00, 0x00, 0x27, 0xF4, 0xFC, 0x50, 0x00, 0x00, 0x00, 0x00,
+0x28, 0xE4, 0xFB, 0x60, 0x00, 0x00, 0x00, 0x00, 0x29, 0x78, 0xA3, 0x60, 0x00, 0x00, 0x00, 0x00,
+0x29, 0xD4, 0xDE, 0x50, 0x00, 0x00, 0x00, 0x00, 0x2A, 0xC4, 0xCF, 0x50, 0x00, 0x00, 0x00, 0x00,
+0x2B, 0xB4, 0xC0, 0x50, 0x00, 0x00, 0x00, 0x00, 0x2C, 0xA4, 0xB1, 0x50, 0x00, 0x00, 0x00, 0x00,
+0x2D, 0x94, 0xA2, 0x50, 0x00, 0x00, 0x00, 0x00, 0x2E, 0x84, 0x93, 0x50, 0x00, 0x00, 0x00, 0x00,
+0x2F, 0x74, 0x84, 0x50, 0x00, 0x00, 0x00, 0x00, 0x30, 0x64, 0x75, 0x50, 0x00, 0x00, 0x00, 0x00,
+0x31, 0x5D, 0xA0, 0xD0, 0x00, 0x00, 0x00, 0x00, 0x32, 0x72, 0x7B, 0xD0, 0x00, 0x00, 0x00, 0x00,
+0x33, 0x3D, 0x82, 0xD0, 0x00, 0x00, 0x00, 0x00, 0x34, 0x52, 0x5D, 0xD0, 0x00, 0x00, 0x00, 0x00,
+0x35, 0x1D, 0x64, 0xD0, 0x00, 0x00, 0x00, 0x00, 0x36, 0x32, 0x3F, 0xD0, 0x00, 0x00, 0x00, 0x00,
+0x36, 0xFD, 0x46, 0xD0, 0x00, 0x00, 0x00, 0x00, 0x38, 0x1B, 0x5C, 0x50, 0x00, 0x00, 0x00, 0x00,
+0x38, 0xDD, 0x28, 0xD0, 0x00, 0x00, 0x00, 0x00, 0x39, 0xFB, 0x3E, 0x50, 0x00, 0x00, 0x00, 0x00,
+0x3A, 0xBD, 0x0A, 0xD0, 0x00, 0x00, 0x00, 0x00, 0x3B, 0xDB, 0x20, 0x50, 0x00, 0x00, 0x00, 0x00,
+0x3C, 0xA6, 0x27, 0x50, 0x00, 0x00, 0x00, 0x00, 0x3D, 0xBB, 0x02, 0x50, 0x00, 0x00, 0x00, 0x00,
+0x3E, 0x86, 0x09, 0x50, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x9A, 0xE4, 0x50, 0x00, 0x00, 0x00, 0x00,
+0x40, 0x65, 0xEB, 0x50, 0x00, 0x00, 0x00, 0x00, 0x41, 0x84, 0x00, 0xD0, 0x00, 0x00, 0x00, 0x00,
+0x7F, 0xFF, 0xFF, 0xFF, 0x00, 0x01, 0x02, 0x03, 0x04, 0x03, 0x02, 0x03, 0x02, 0x03, 0x05, 0x06,
+0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x07, 0x08, 0x05, 0x06, 0x05,
+0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05,
+0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x05, 0x00, 0x00, 0x35, 0x98, 0x00, 0x00, 0x00,
+0x00, 0x38, 0x40, 0x00, 0x04, 0x00, 0x00, 0x46, 0x50, 0x00, 0x08, 0x00, 0x00, 0x54, 0x60, 0x01,
+0x0C, 0x00, 0x00, 0x54, 0x60, 0x00, 0x0C, 0x00, 0x00, 0x46, 0x50, 0x00, 0x08, 0x00, 0x00, 0x54,
+0x60, 0x01, 0x0C, 0x00, 0x00, 0x46, 0x50, 0x01, 0x08, 0x00, 0x00, 0x38, 0x40, 0x00, 0x04, 0x00,
+0x00, 0x54, 0x60, 0x01, 0x0C, 0x00, 0x00, 0x46, 0x50, 0x00, 0x08, 0x4C, 0x4D, 0x54, 0x00, 0x2B,
+0x30, 0x34, 0x00, 0x2B, 0x30, 0x35, 0x00, 0x2B, 0x30, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x0A, 0x3C, 0x2B, 0x30, 0x35, 0x3E, 0x2D, 0x35, 0x0A,
#endif
0x00, 0xD6, 0x0E, 0x2D, 0x01, 0x69, 0xE3, 0x3A, 0x00, 0x00, 0x00, 0x0D, 0x41, 0x71, 0x74, 0x6F,
0x62, 0x65, 0x2F, 0x41, 0x6B, 0x74, 0x6F, 0x62, 0x65,
@@ -19091,42 +19171,43 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
/* Asia/Baku */
0x50, 0x48, 0x50, 0x32, 0x01, 0x41, 0x5A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x18, 0x80, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x46, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x18, 0x80, 0x00, 0x00, 0x00,
0xAA, 0x19, 0x95, 0x44, 0xE7, 0xDA, 0x0C, 0x50, 0x15, 0x27, 0x99, 0xC0, 0x16, 0x18, 0xCE, 0x30,
0x17, 0x08, 0xCD, 0x40, 0x17, 0xFA, 0x01, 0xB0, 0x18, 0xEA, 0x00, 0xC0, 0x19, 0xDB, 0x35, 0x30,
0x1A, 0xCC, 0x85, 0xC0, 0x1B, 0xBC, 0x92, 0xE0, 0x1C, 0xAC, 0x83, 0xE0, 0x1D, 0x9C, 0x74, 0xE0,
0x1E, 0x8C, 0x65, 0xE0, 0x1F, 0x7C, 0x56, 0xE0, 0x20, 0x6C, 0x47, 0xE0, 0x21, 0x5C, 0x38, 0xE0,
0x22, 0x4C, 0x29, 0xE0, 0x23, 0x3C, 0x1A, 0xE0, 0x24, 0x2C, 0x0B, 0xE0, 0x25, 0x1B, 0xFC, 0xE0,
0x26, 0x0B, 0xED, 0xE0, 0x27, 0x05, 0x19, 0x60, 0x27, 0xF5, 0x0A, 0x60, 0x28, 0xBD, 0x52, 0x40,
-0x28, 0xE5, 0x09, 0x70, 0x29, 0xD4, 0xD0, 0x40, 0x2A, 0xC4, 0xB3, 0x30, 0x31, 0x5D, 0xD9, 0x10,
-0x32, 0x72, 0xB4, 0x10, 0x32, 0xC9, 0x70, 0xC0, 0x33, 0x3D, 0xAD, 0x00, 0x34, 0x52, 0x88, 0x00,
-0x35, 0x1D, 0x8F, 0x00, 0x36, 0x32, 0x6A, 0x00, 0x36, 0xFD, 0x71, 0x00, 0x38, 0x1B, 0x86, 0x80,
-0x38, 0xDD, 0x53, 0x00, 0x39, 0xFB, 0x68, 0x80, 0x3A, 0xBD, 0x35, 0x00, 0x3B, 0xDB, 0x4A, 0x80,
-0x3C, 0xA6, 0x51, 0x80, 0x3D, 0xBB, 0x2C, 0x80, 0x3E, 0x86, 0x33, 0x80, 0x3F, 0x9B, 0x0E, 0x80,
-0x40, 0x66, 0x15, 0x80, 0x41, 0x84, 0x2B, 0x00, 0x42, 0x45, 0xF7, 0x80, 0x43, 0x64, 0x0D, 0x00,
-0x44, 0x25, 0xD9, 0x80, 0x45, 0x43, 0xEF, 0x00, 0x46, 0x05, 0xBB, 0x80, 0x47, 0x23, 0xD1, 0x00,
-0x47, 0xEE, 0xD8, 0x00, 0x49, 0x03, 0xB3, 0x00, 0x49, 0xCE, 0xBA, 0x00, 0x4A, 0xE3, 0x95, 0x00,
-0x4B, 0xAE, 0x9C, 0x00, 0x4C, 0xCC, 0xB1, 0x80, 0x4D, 0x8E, 0x7E, 0x00, 0x4E, 0xAC, 0x93, 0x80,
-0x4F, 0x6E, 0x60, 0x00, 0x50, 0x8C, 0x75, 0x80, 0x51, 0x57, 0x7C, 0x80, 0x52, 0x6C, 0x57, 0x80,
-0x53, 0x37, 0x5E, 0x80, 0x54, 0x4C, 0x39, 0x80, 0x55, 0x17, 0x40, 0x80, 0x56, 0x2C, 0x1B, 0x80,
-0x00, 0x01, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05,
-0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x06, 0x08, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x09, 0x0C,
-0x09, 0x0C, 0x09, 0x0C, 0x09, 0x0C, 0x09, 0x0C, 0x09, 0x0C, 0x09, 0x0C, 0x09, 0x0C, 0x09, 0x0C,
-0x09, 0x0C, 0x09, 0x0C, 0x09, 0x0C, 0x09, 0x0C, 0x09, 0x0C, 0x09, 0x0C, 0x09, 0x0C, 0x09, 0x0C,
-0x09, 0x0C, 0x09, 0x0C, 0x09, 0x00, 0x00, 0x2E, 0xBC, 0x00, 0x00, 0x00, 0x00, 0x2A, 0x30, 0x00,
-0x04, 0x00, 0x00, 0x46, 0x50, 0x01, 0x09, 0x00, 0x00, 0x38, 0x40, 0x00, 0x04, 0x00, 0x00, 0x38,
-0x40, 0x00, 0x04, 0x00, 0x00, 0x46, 0x50, 0x01, 0x09, 0x00, 0x00, 0x38, 0x40, 0x01, 0x09, 0x00,
-0x00, 0x2A, 0x30, 0x00, 0x0F, 0x00, 0x00, 0x38, 0x40, 0x01, 0x13, 0x00, 0x00, 0x38, 0x40, 0x00,
-0x0F, 0x00, 0x00, 0x46, 0x50, 0x01, 0x13, 0x00, 0x00, 0x38, 0x40, 0x00, 0x0F, 0x00, 0x00, 0x46,
-0x50, 0x01, 0x13, 0x4C, 0x4D, 0x54, 0x00, 0x42, 0x41, 0x4B, 0x54, 0x00, 0x42, 0x41, 0x4B, 0x53,
-0x54, 0x00, 0x41, 0x5A, 0x54, 0x00, 0x41, 0x5A, 0x53, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
-0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x01, 0x01, 0x00,
+0x28, 0xE5, 0x09, 0x70, 0x29, 0xD4, 0xFA, 0x70, 0x2A, 0xC4, 0xEB, 0x70, 0x30, 0xE6, 0xEB, 0xC0,
+0x31, 0x5D, 0xD9, 0x10, 0x32, 0x72, 0xB4, 0x10, 0x32, 0xC9, 0x70, 0xC0, 0x33, 0x3D, 0xAD, 0x00,
+0x34, 0x52, 0x88, 0x00, 0x35, 0x1D, 0x8F, 0x00, 0x36, 0x32, 0x6A, 0x00, 0x36, 0xFD, 0x71, 0x00,
+0x38, 0x1B, 0x86, 0x80, 0x38, 0xDD, 0x53, 0x00, 0x39, 0xFB, 0x68, 0x80, 0x3A, 0xBD, 0x35, 0x00,
+0x3B, 0xDB, 0x4A, 0x80, 0x3C, 0xA6, 0x51, 0x80, 0x3D, 0xBB, 0x2C, 0x80, 0x3E, 0x86, 0x33, 0x80,
+0x3F, 0x9B, 0x0E, 0x80, 0x40, 0x66, 0x15, 0x80, 0x41, 0x84, 0x2B, 0x00, 0x42, 0x45, 0xF7, 0x80,
+0x43, 0x64, 0x0D, 0x00, 0x44, 0x25, 0xD9, 0x80, 0x45, 0x43, 0xEF, 0x00, 0x46, 0x05, 0xBB, 0x80,
+0x47, 0x23, 0xD1, 0x00, 0x47, 0xEE, 0xD8, 0x00, 0x49, 0x03, 0xB3, 0x00, 0x49, 0xCE, 0xBA, 0x00,
+0x4A, 0xE3, 0x95, 0x00, 0x4B, 0xAE, 0x9C, 0x00, 0x4C, 0xCC, 0xB1, 0x80, 0x4D, 0x8E, 0x7E, 0x00,
+0x4E, 0xAC, 0x93, 0x80, 0x4F, 0x6E, 0x60, 0x00, 0x50, 0x8C, 0x75, 0x80, 0x51, 0x57, 0x7C, 0x80,
+0x52, 0x6C, 0x57, 0x80, 0x53, 0x37, 0x5E, 0x80, 0x54, 0x4C, 0x39, 0x80, 0x55, 0x17, 0x40, 0x80,
+0x56, 0x2C, 0x1B, 0x80, 0x00, 0x01, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x04, 0x05,
+0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x06, 0x09, 0x07, 0x08, 0x0A,
+0x0D, 0x0B, 0x0C, 0x0D, 0x0E, 0x0D, 0x0E, 0x0D, 0x0E, 0x0D, 0x0E, 0x0D, 0x0E, 0x0D, 0x0E, 0x0D,
+0x0E, 0x0D, 0x0E, 0x0D, 0x0E, 0x0D, 0x0E, 0x0D, 0x0E, 0x0D, 0x0E, 0x0D, 0x0E, 0x0D, 0x0E, 0x0D,
+0x0E, 0x0D, 0x0E, 0x0D, 0x0E, 0x0D, 0x0E, 0x0D, 0x0E, 0x0D, 0x00, 0x00, 0x2E, 0xBC, 0x00, 0x00,
+0x00, 0x00, 0x2A, 0x30, 0x00, 0x04, 0x00, 0x00, 0x46, 0x50, 0x01, 0x09, 0x00, 0x00, 0x38, 0x40,
+0x00, 0x04, 0x00, 0x00, 0x38, 0x40, 0x00, 0x04, 0x00, 0x00, 0x46, 0x50, 0x01, 0x09, 0x00, 0x00,
+0x38, 0x40, 0x01, 0x09, 0x00, 0x00, 0x2A, 0x30, 0x00, 0x0F, 0x00, 0x00, 0x38, 0x40, 0x01, 0x13,
+0x00, 0x00, 0x38, 0x40, 0x01, 0x13, 0x00, 0x00, 0x38, 0x40, 0x00, 0x0F, 0x00, 0x00, 0x46, 0x50,
+0x01, 0x13, 0x00, 0x00, 0x38, 0x40, 0x00, 0x0F, 0x00, 0x00, 0x38, 0x40, 0x00, 0x0F, 0x00, 0x00,
+0x46, 0x50, 0x01, 0x13, 0x4C, 0x4D, 0x54, 0x00, 0x42, 0x41, 0x4B, 0x54, 0x00, 0x42, 0x41, 0x4B,
+0x53, 0x54, 0x00, 0x41, 0x5A, 0x54, 0x00, 0x41, 0x5A, 0x53, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00,
#ifdef TIMELIB_SUPPORTS_V2DATA
0x54, 0x5A, 0x69, 0x66, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x18, 0xF8, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x46, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x18, 0xF8, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xAA, 0x19, 0x95, 0x44, 0xFF, 0xFF, 0xFF, 0xFF,
0xE7, 0xDA, 0x0C, 0x50, 0x00, 0x00, 0x00, 0x00, 0x15, 0x27, 0x99, 0xC0, 0x00, 0x00, 0x00, 0x00,
0x16, 0x18, 0xCE, 0x30, 0x00, 0x00, 0x00, 0x00, 0x17, 0x08, 0xCD, 0x40, 0x00, 0x00, 0x00, 0x00,
@@ -19140,40 +19221,42 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x25, 0x1B, 0xFC, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x26, 0x0B, 0xED, 0xE0, 0x00, 0x00, 0x00, 0x00,
0x27, 0x05, 0x19, 0x60, 0x00, 0x00, 0x00, 0x00, 0x27, 0xF5, 0x0A, 0x60, 0x00, 0x00, 0x00, 0x00,
0x28, 0xBD, 0x52, 0x40, 0x00, 0x00, 0x00, 0x00, 0x28, 0xE5, 0x09, 0x70, 0x00, 0x00, 0x00, 0x00,
-0x29, 0xD4, 0xD0, 0x40, 0x00, 0x00, 0x00, 0x00, 0x2A, 0xC4, 0xB3, 0x30, 0x00, 0x00, 0x00, 0x00,
-0x31, 0x5D, 0xD9, 0x10, 0x00, 0x00, 0x00, 0x00, 0x32, 0x72, 0xB4, 0x10, 0x00, 0x00, 0x00, 0x00,
-0x32, 0xC9, 0x70, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x33, 0x3D, 0xAD, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x34, 0x52, 0x88, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x1D, 0x8F, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x36, 0x32, 0x6A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0xFD, 0x71, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x38, 0x1B, 0x86, 0x80, 0x00, 0x00, 0x00, 0x00, 0x38, 0xDD, 0x53, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x39, 0xFB, 0x68, 0x80, 0x00, 0x00, 0x00, 0x00, 0x3A, 0xBD, 0x35, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x3B, 0xDB, 0x4A, 0x80, 0x00, 0x00, 0x00, 0x00, 0x3C, 0xA6, 0x51, 0x80, 0x00, 0x00, 0x00, 0x00,
-0x3D, 0xBB, 0x2C, 0x80, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x86, 0x33, 0x80, 0x00, 0x00, 0x00, 0x00,
-0x3F, 0x9B, 0x0E, 0x80, 0x00, 0x00, 0x00, 0x00, 0x40, 0x66, 0x15, 0x80, 0x00, 0x00, 0x00, 0x00,
-0x41, 0x84, 0x2B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x45, 0xF7, 0x80, 0x00, 0x00, 0x00, 0x00,
-0x43, 0x64, 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x25, 0xD9, 0x80, 0x00, 0x00, 0x00, 0x00,
-0x45, 0x43, 0xEF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x05, 0xBB, 0x80, 0x00, 0x00, 0x00, 0x00,
-0x47, 0x23, 0xD1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0xEE, 0xD8, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x49, 0x03, 0xB3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x49, 0xCE, 0xBA, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x4A, 0xE3, 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4B, 0xAE, 0x9C, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x4C, 0xCC, 0xB1, 0x80, 0x00, 0x00, 0x00, 0x00, 0x4D, 0x8E, 0x7E, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x4E, 0xAC, 0x93, 0x80, 0x00, 0x00, 0x00, 0x00, 0x4F, 0x6E, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x50, 0x8C, 0x75, 0x80, 0x00, 0x00, 0x00, 0x00, 0x51, 0x57, 0x7C, 0x80, 0x00, 0x00, 0x00, 0x00,
-0x52, 0x6C, 0x57, 0x80, 0x00, 0x00, 0x00, 0x00, 0x53, 0x37, 0x5E, 0x80, 0x00, 0x00, 0x00, 0x00,
-0x54, 0x4C, 0x39, 0x80, 0x00, 0x00, 0x00, 0x00, 0x55, 0x17, 0x40, 0x80, 0x00, 0x00, 0x00, 0x00,
-0x56, 0x2C, 0x1B, 0x80, 0x00, 0x01, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x04, 0x05,
-0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x06, 0x08, 0x07, 0x08, 0x09,
-0x0A, 0x0B, 0x09, 0x0C, 0x09, 0x0C, 0x09, 0x0C, 0x09, 0x0C, 0x09, 0x0C, 0x09, 0x0C, 0x09, 0x0C,
-0x09, 0x0C, 0x09, 0x0C, 0x09, 0x0C, 0x09, 0x0C, 0x09, 0x0C, 0x09, 0x0C, 0x09, 0x0C, 0x09, 0x0C,
-0x09, 0x0C, 0x09, 0x0C, 0x09, 0x0C, 0x09, 0x0C, 0x09, 0x00, 0x00, 0x2E, 0xBC, 0x00, 0x00, 0x00,
-0x00, 0x2A, 0x30, 0x00, 0x04, 0x00, 0x00, 0x46, 0x50, 0x01, 0x09, 0x00, 0x00, 0x38, 0x40, 0x00,
-0x04, 0x00, 0x00, 0x38, 0x40, 0x00, 0x04, 0x00, 0x00, 0x46, 0x50, 0x01, 0x09, 0x00, 0x00, 0x38,
-0x40, 0x01, 0x09, 0x00, 0x00, 0x2A, 0x30, 0x00, 0x0F, 0x00, 0x00, 0x38, 0x40, 0x01, 0x13, 0x00,
-0x00, 0x38, 0x40, 0x00, 0x0F, 0x00, 0x00, 0x46, 0x50, 0x01, 0x13, 0x00, 0x00, 0x38, 0x40, 0x00,
-0x0F, 0x00, 0x00, 0x46, 0x50, 0x01, 0x13, 0x4C, 0x4D, 0x54, 0x00, 0x42, 0x41, 0x4B, 0x54, 0x00,
-0x42, 0x41, 0x4B, 0x53, 0x54, 0x00, 0x41, 0x5A, 0x54, 0x00, 0x41, 0x5A, 0x53, 0x54, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x0A, 0x41, 0x5A, 0x54, 0x2D, 0x34, 0x0A,
+0x29, 0xD4, 0xFA, 0x70, 0x00, 0x00, 0x00, 0x00, 0x2A, 0xC4, 0xEB, 0x70, 0x00, 0x00, 0x00, 0x00,
+0x30, 0xE6, 0xEB, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x31, 0x5D, 0xD9, 0x10, 0x00, 0x00, 0x00, 0x00,
+0x32, 0x72, 0xB4, 0x10, 0x00, 0x00, 0x00, 0x00, 0x32, 0xC9, 0x70, 0xC0, 0x00, 0x00, 0x00, 0x00,
+0x33, 0x3D, 0xAD, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x52, 0x88, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x35, 0x1D, 0x8F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x32, 0x6A, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x36, 0xFD, 0x71, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x1B, 0x86, 0x80, 0x00, 0x00, 0x00, 0x00,
+0x38, 0xDD, 0x53, 0x00, 0x00, 0x00, 0x00, 0x00, 0x39, 0xFB, 0x68, 0x80, 0x00, 0x00, 0x00, 0x00,
+0x3A, 0xBD, 0x35, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3B, 0xDB, 0x4A, 0x80, 0x00, 0x00, 0x00, 0x00,
+0x3C, 0xA6, 0x51, 0x80, 0x00, 0x00, 0x00, 0x00, 0x3D, 0xBB, 0x2C, 0x80, 0x00, 0x00, 0x00, 0x00,
+0x3E, 0x86, 0x33, 0x80, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x9B, 0x0E, 0x80, 0x00, 0x00, 0x00, 0x00,
+0x40, 0x66, 0x15, 0x80, 0x00, 0x00, 0x00, 0x00, 0x41, 0x84, 0x2B, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x42, 0x45, 0xF7, 0x80, 0x00, 0x00, 0x00, 0x00, 0x43, 0x64, 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x44, 0x25, 0xD9, 0x80, 0x00, 0x00, 0x00, 0x00, 0x45, 0x43, 0xEF, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x46, 0x05, 0xBB, 0x80, 0x00, 0x00, 0x00, 0x00, 0x47, 0x23, 0xD1, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x47, 0xEE, 0xD8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x49, 0x03, 0xB3, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x49, 0xCE, 0xBA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4A, 0xE3, 0x95, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x4B, 0xAE, 0x9C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4C, 0xCC, 0xB1, 0x80, 0x00, 0x00, 0x00, 0x00,
+0x4D, 0x8E, 0x7E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4E, 0xAC, 0x93, 0x80, 0x00, 0x00, 0x00, 0x00,
+0x4F, 0x6E, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x8C, 0x75, 0x80, 0x00, 0x00, 0x00, 0x00,
+0x51, 0x57, 0x7C, 0x80, 0x00, 0x00, 0x00, 0x00, 0x52, 0x6C, 0x57, 0x80, 0x00, 0x00, 0x00, 0x00,
+0x53, 0x37, 0x5E, 0x80, 0x00, 0x00, 0x00, 0x00, 0x54, 0x4C, 0x39, 0x80, 0x00, 0x00, 0x00, 0x00,
+0x55, 0x17, 0x40, 0x80, 0x00, 0x00, 0x00, 0x00, 0x56, 0x2C, 0x1B, 0x80, 0x00, 0x01, 0x03, 0x02,
+0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05,
+0x04, 0x05, 0x04, 0x06, 0x09, 0x07, 0x08, 0x0A, 0x0D, 0x0B, 0x0C, 0x0D, 0x0E, 0x0D, 0x0E, 0x0D,
+0x0E, 0x0D, 0x0E, 0x0D, 0x0E, 0x0D, 0x0E, 0x0D, 0x0E, 0x0D, 0x0E, 0x0D, 0x0E, 0x0D, 0x0E, 0x0D,
+0x0E, 0x0D, 0x0E, 0x0D, 0x0E, 0x0D, 0x0E, 0x0D, 0x0E, 0x0D, 0x0E, 0x0D, 0x0E, 0x0D, 0x0E, 0x0D,
+0x0E, 0x0D, 0x00, 0x00, 0x2E, 0xBC, 0x00, 0x00, 0x00, 0x00, 0x2A, 0x30, 0x00, 0x04, 0x00, 0x00,
+0x46, 0x50, 0x01, 0x09, 0x00, 0x00, 0x38, 0x40, 0x00, 0x04, 0x00, 0x00, 0x38, 0x40, 0x00, 0x04,
+0x00, 0x00, 0x46, 0x50, 0x01, 0x09, 0x00, 0x00, 0x38, 0x40, 0x01, 0x09, 0x00, 0x00, 0x2A, 0x30,
+0x00, 0x0F, 0x00, 0x00, 0x38, 0x40, 0x01, 0x13, 0x00, 0x00, 0x38, 0x40, 0x01, 0x13, 0x00, 0x00,
+0x38, 0x40, 0x00, 0x0F, 0x00, 0x00, 0x46, 0x50, 0x01, 0x13, 0x00, 0x00, 0x38, 0x40, 0x00, 0x0F,
+0x00, 0x00, 0x38, 0x40, 0x00, 0x0F, 0x00, 0x00, 0x46, 0x50, 0x01, 0x13, 0x4C, 0x4D, 0x54, 0x00,
+0x42, 0x41, 0x4B, 0x54, 0x00, 0x42, 0x41, 0x4B, 0x53, 0x54, 0x00, 0x41, 0x5A, 0x54, 0x00, 0x41,
+0x5A, 0x53, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x01, 0x01,
+0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01,
+0x00, 0x00, 0x0A, 0x41, 0x5A, 0x54, 0x2D, 0x34, 0x0A,
#endif
0x00, 0xC6, 0xF2, 0xFD, 0x01, 0x5E, 0xB9, 0x28, 0x00, 0x00, 0x00, 0x00,
@@ -19199,14 +19282,14 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
/* Asia/Barnaul */
0x50, 0x48, 0x50, 0x32, 0x01, 0x52, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x10, 0x80, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x10, 0x80, 0x00, 0x00, 0x00,
0xA1, 0xD5, 0x7D, 0xFC, 0xB5, 0xA3, 0xE1, 0x20, 0x15, 0x27, 0x6F, 0x90, 0x16, 0x18, 0xA4, 0x00,
0x17, 0x08, 0xA3, 0x10, 0x17, 0xF9, 0xD7, 0x80, 0x18, 0xE9, 0xD6, 0x90, 0x19, 0xDB, 0x0B, 0x00,
0x1A, 0xCC, 0x5B, 0x90, 0x1B, 0xBC, 0x68, 0xB0, 0x1C, 0xAC, 0x59, 0xB0, 0x1D, 0x9C, 0x4A, 0xB0,
0x1E, 0x8C, 0x3B, 0xB0, 0x1F, 0x7C, 0x2C, 0xB0, 0x20, 0x6C, 0x1D, 0xB0, 0x21, 0x5C, 0x0E, 0xB0,
0x22, 0x4B, 0xFF, 0xB0, 0x23, 0x3B, 0xF0, 0xB0, 0x24, 0x2B, 0xE1, 0xB0, 0x25, 0x1B, 0xD2, 0xB0,
0x26, 0x0B, 0xC3, 0xB0, 0x27, 0x04, 0xEF, 0x30, 0x27, 0xF4, 0xE0, 0x30, 0x28, 0xE4, 0xDF, 0x40,
-0x29, 0x78, 0x87, 0x40, 0x29, 0xD4, 0x98, 0x00, 0x2A, 0xC4, 0x7A, 0xF0, 0x2B, 0xB4, 0xA4, 0x30,
+0x29, 0x78, 0x87, 0x40, 0x29, 0xD4, 0xC2, 0x30, 0x2A, 0xC4, 0xB3, 0x30, 0x2B, 0xB4, 0xA4, 0x30,
0x2C, 0xA4, 0x95, 0x30, 0x2D, 0x94, 0x86, 0x30, 0x2E, 0x84, 0x77, 0x30, 0x2F, 0x74, 0x68, 0x30,
0x2F, 0xC7, 0x4C, 0x80, 0x30, 0x64, 0x67, 0x40, 0x31, 0x5D, 0x92, 0xC0, 0x32, 0x72, 0x6D, 0xC0,
0x33, 0x3D, 0x74, 0xC0, 0x34, 0x52, 0x4F, 0xC0, 0x35, 0x1D, 0x56, 0xC0, 0x36, 0x32, 0x31, 0xC0,
@@ -19216,21 +19299,22 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x42, 0x45, 0xBF, 0x40, 0x43, 0x63, 0xD4, 0xC0, 0x44, 0x25, 0xA1, 0x40, 0x45, 0x43, 0xB6, 0xC0,
0x46, 0x05, 0x83, 0x40, 0x47, 0x23, 0x98, 0xC0, 0x47, 0xEE, 0x9F, 0xC0, 0x49, 0x03, 0x7A, 0xC0,
0x49, 0xCE, 0x81, 0xC0, 0x4A, 0xE3, 0x5C, 0xC0, 0x4B, 0xAE, 0x63, 0xC0, 0x4C, 0xCC, 0x79, 0x40,
-0x4D, 0x8E, 0x45, 0xC0, 0x54, 0x4B, 0xF3, 0x30, 0x56, 0xF6, 0xEA, 0x40, 0x00, 0x01, 0x03, 0x02,
-0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05,
-0x04, 0x05, 0x04, 0x06, 0x07, 0x04, 0x02, 0x03, 0x05, 0x04, 0x05, 0x04, 0x05, 0x08, 0x07, 0x06,
+0x4D, 0x8E, 0x45, 0xC0, 0x54, 0x4B, 0xF3, 0x30, 0x56, 0xF6, 0xEA, 0x40, 0x7F, 0xFF, 0xFF, 0xFF,
+0x00, 0x01, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05,
+0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x06, 0x07, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04,
+0x05, 0x08, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06,
0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06,
-0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x04, 0x07, 0x04,
-0x00, 0x00, 0x4E, 0x84, 0x00, 0x00, 0x00, 0x00, 0x54, 0x60, 0x00, 0x04, 0x00, 0x00, 0x70, 0x80,
-0x01, 0x08, 0x00, 0x00, 0x62, 0x70, 0x00, 0x0C, 0x00, 0x00, 0x62, 0x70, 0x00, 0x0C, 0x00, 0x00,
-0x70, 0x80, 0x01, 0x08, 0x00, 0x00, 0x62, 0x70, 0x01, 0x0C, 0x00, 0x00, 0x54, 0x60, 0x00, 0x04,
-0x00, 0x00, 0x62, 0x70, 0x01, 0x0C, 0x00, 0x00, 0x62, 0x70, 0x00, 0x0C, 0x4C, 0x4D, 0x54, 0x00,
-0x2B, 0x30, 0x36, 0x00, 0x2B, 0x30, 0x38, 0x00, 0x2B, 0x30, 0x37, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x01, 0x01, 0x01, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x07, 0x04, 0x07, 0x04, 0x04, 0x00, 0x00, 0x4E, 0x84, 0x00, 0x00, 0x00, 0x00, 0x54, 0x60, 0x00,
+0x04, 0x00, 0x00, 0x70, 0x80, 0x01, 0x08, 0x00, 0x00, 0x62, 0x70, 0x00, 0x0C, 0x00, 0x00, 0x62,
+0x70, 0x00, 0x0C, 0x00, 0x00, 0x70, 0x80, 0x01, 0x08, 0x00, 0x00, 0x62, 0x70, 0x01, 0x0C, 0x00,
+0x00, 0x54, 0x60, 0x00, 0x04, 0x00, 0x00, 0x62, 0x70, 0x01, 0x0C, 0x00, 0x00, 0x62, 0x70, 0x00,
+0x0C, 0x4C, 0x4D, 0x54, 0x00, 0x2B, 0x30, 0x36, 0x00, 0x2B, 0x30, 0x38, 0x00, 0x2B, 0x30, 0x37,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00,
#ifdef TIMELIB_SUPPORTS_V2DATA
0x54, 0x5A, 0x69, 0x66, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x10, 0xF8, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x10, 0xF8, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xA1, 0xD5, 0x7D, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF,
0xB5, 0xA3, 0xE1, 0x20, 0x00, 0x00, 0x00, 0x00, 0x15, 0x27, 0x6F, 0x90, 0x00, 0x00, 0x00, 0x00,
0x16, 0x18, 0xA4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x08, 0xA3, 0x10, 0x00, 0x00, 0x00, 0x00,
@@ -19244,7 +19328,7 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x25, 0x1B, 0xD2, 0xB0, 0x00, 0x00, 0x00, 0x00, 0x26, 0x0B, 0xC3, 0xB0, 0x00, 0x00, 0x00, 0x00,
0x27, 0x04, 0xEF, 0x30, 0x00, 0x00, 0x00, 0x00, 0x27, 0xF4, 0xE0, 0x30, 0x00, 0x00, 0x00, 0x00,
0x28, 0xE4, 0xDF, 0x40, 0x00, 0x00, 0x00, 0x00, 0x29, 0x78, 0x87, 0x40, 0x00, 0x00, 0x00, 0x00,
-0x29, 0xD4, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2A, 0xC4, 0x7A, 0xF0, 0x00, 0x00, 0x00, 0x00,
+0x29, 0xD4, 0xC2, 0x30, 0x00, 0x00, 0x00, 0x00, 0x2A, 0xC4, 0xB3, 0x30, 0x00, 0x00, 0x00, 0x00,
0x2B, 0xB4, 0xA4, 0x30, 0x00, 0x00, 0x00, 0x00, 0x2C, 0xA4, 0x95, 0x30, 0x00, 0x00, 0x00, 0x00,
0x2D, 0x94, 0x86, 0x30, 0x00, 0x00, 0x00, 0x00, 0x2E, 0x84, 0x77, 0x30, 0x00, 0x00, 0x00, 0x00,
0x2F, 0x74, 0x68, 0x30, 0x00, 0x00, 0x00, 0x00, 0x2F, 0xC7, 0x4C, 0x80, 0x00, 0x00, 0x00, 0x00,
@@ -19264,18 +19348,19 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x49, 0x03, 0x7A, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x49, 0xCE, 0x81, 0xC0, 0x00, 0x00, 0x00, 0x00,
0x4A, 0xE3, 0x5C, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x4B, 0xAE, 0x63, 0xC0, 0x00, 0x00, 0x00, 0x00,
0x4C, 0xCC, 0x79, 0x40, 0x00, 0x00, 0x00, 0x00, 0x4D, 0x8E, 0x45, 0xC0, 0x00, 0x00, 0x00, 0x00,
-0x54, 0x4B, 0xF3, 0x30, 0x00, 0x00, 0x00, 0x00, 0x56, 0xF6, 0xEA, 0x40, 0x00, 0x01, 0x03, 0x02,
-0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05,
-0x04, 0x05, 0x04, 0x06, 0x07, 0x04, 0x02, 0x03, 0x05, 0x04, 0x05, 0x04, 0x05, 0x08, 0x07, 0x06,
+0x54, 0x4B, 0xF3, 0x30, 0x00, 0x00, 0x00, 0x00, 0x56, 0xF6, 0xEA, 0x40, 0x00, 0x00, 0x00, 0x00,
+0x7F, 0xFF, 0xFF, 0xFF, 0x00, 0x01, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x04, 0x05,
+0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x06, 0x07, 0x04, 0x05, 0x04,
+0x05, 0x04, 0x05, 0x04, 0x05, 0x08, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06,
0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06,
-0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x04, 0x07, 0x04,
-0x00, 0x00, 0x4E, 0x84, 0x00, 0x00, 0x00, 0x00, 0x54, 0x60, 0x00, 0x04, 0x00, 0x00, 0x70, 0x80,
-0x01, 0x08, 0x00, 0x00, 0x62, 0x70, 0x00, 0x0C, 0x00, 0x00, 0x62, 0x70, 0x00, 0x0C, 0x00, 0x00,
-0x70, 0x80, 0x01, 0x08, 0x00, 0x00, 0x62, 0x70, 0x01, 0x0C, 0x00, 0x00, 0x54, 0x60, 0x00, 0x04,
-0x00, 0x00, 0x62, 0x70, 0x01, 0x0C, 0x00, 0x00, 0x62, 0x70, 0x00, 0x0C, 0x4C, 0x4D, 0x54, 0x00,
-0x2B, 0x30, 0x36, 0x00, 0x2B, 0x30, 0x38, 0x00, 0x2B, 0x30, 0x37, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x01, 0x01, 0x01, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x0A, 0x3C, 0x2B, 0x30, 0x37, 0x3E, 0x2D, 0x37, 0x0A,
+0x07, 0x06, 0x07, 0x06, 0x07, 0x04, 0x07, 0x04, 0x04, 0x00, 0x00, 0x4E, 0x84, 0x00, 0x00, 0x00,
+0x00, 0x54, 0x60, 0x00, 0x04, 0x00, 0x00, 0x70, 0x80, 0x01, 0x08, 0x00, 0x00, 0x62, 0x70, 0x00,
+0x0C, 0x00, 0x00, 0x62, 0x70, 0x00, 0x0C, 0x00, 0x00, 0x70, 0x80, 0x01, 0x08, 0x00, 0x00, 0x62,
+0x70, 0x01, 0x0C, 0x00, 0x00, 0x54, 0x60, 0x00, 0x04, 0x00, 0x00, 0x62, 0x70, 0x01, 0x0C, 0x00,
+0x00, 0x62, 0x70, 0x00, 0x0C, 0x4C, 0x4D, 0x54, 0x00, 0x2B, 0x30, 0x36, 0x00, 0x2B, 0x30, 0x38,
+0x00, 0x2B, 0x30, 0x37, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x01, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x3C, 0x2B, 0x30, 0x37, 0x3E, 0x2D,
+0x37, 0x0A,
#endif
0x00, 0xDA, 0xC2, 0x9A, 0x01, 0x92, 0x73, 0x58, 0x00, 0x00, 0x00, 0x0E, 0x4D, 0x53, 0x4B, 0x2B,
0x30, 0x34, 0x20, 0x2D, 0x20, 0x41, 0x6C, 0x74, 0x61, 0x69,
@@ -19546,7 +19631,7 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x1E, 0x8C, 0x1F, 0x90, 0x1F, 0x7C, 0x10, 0x90, 0x20, 0x6C, 0x01, 0x90, 0x21, 0x5B, 0xF2, 0x90,
0x22, 0x4B, 0xE3, 0x90, 0x23, 0x3B, 0xD4, 0x90, 0x24, 0x2B, 0xC5, 0x90, 0x25, 0x1B, 0xB6, 0x90,
0x26, 0x0B, 0xA7, 0x90, 0x27, 0x04, 0xD3, 0x10, 0x27, 0xF4, 0xC4, 0x10, 0x28, 0xE4, 0xC3, 0x20,
-0x29, 0x78, 0x6B, 0x20, 0x29, 0xD4, 0x7B, 0xE0, 0x2A, 0xC4, 0x5E, 0xD0, 0x2B, 0xB4, 0x88, 0x10,
+0x29, 0x78, 0x6B, 0x20, 0x29, 0xD4, 0xA6, 0x10, 0x2A, 0xC4, 0x97, 0x10, 0x2B, 0xB4, 0x88, 0x10,
0x2C, 0xA4, 0x79, 0x10, 0x2D, 0x94, 0x6A, 0x10, 0x2E, 0x84, 0x5B, 0x10, 0x2F, 0x74, 0x4C, 0x10,
0x30, 0x64, 0x3D, 0x10, 0x31, 0x5D, 0x68, 0x90, 0x32, 0x72, 0x43, 0x90, 0x33, 0x3D, 0x4A, 0x90,
0x34, 0x52, 0x25, 0x90, 0x35, 0x1D, 0x2C, 0x90, 0x36, 0x32, 0x07, 0x90, 0x36, 0xFD, 0x0E, 0x90,
@@ -19558,7 +19643,7 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x4A, 0xE3, 0x32, 0x90, 0x4B, 0xAE, 0x39, 0x90, 0x4C, 0xCC, 0x4F, 0x10, 0x4D, 0x8E, 0x1B, 0x90,
0x54, 0x4B, 0xC9, 0x00, 0x56, 0xF6, 0xCE, 0x20, 0x00, 0x01, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02,
0x03, 0x02, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x06,
-0x07, 0x04, 0x02, 0x03, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04,
+0x07, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04,
0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04,
0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x08, 0x09, 0x03, 0x00, 0x00, 0x6A, 0x60, 0x00,
0x00, 0x00, 0x00, 0x70, 0x80, 0x00, 0x04, 0x00, 0x00, 0x8C, 0xA0, 0x01, 0x09, 0x00, 0x00, 0x7E,
@@ -19585,7 +19670,7 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x25, 0x1B, 0xB6, 0x90, 0x00, 0x00, 0x00, 0x00, 0x26, 0x0B, 0xA7, 0x90, 0x00, 0x00, 0x00, 0x00,
0x27, 0x04, 0xD3, 0x10, 0x00, 0x00, 0x00, 0x00, 0x27, 0xF4, 0xC4, 0x10, 0x00, 0x00, 0x00, 0x00,
0x28, 0xE4, 0xC3, 0x20, 0x00, 0x00, 0x00, 0x00, 0x29, 0x78, 0x6B, 0x20, 0x00, 0x00, 0x00, 0x00,
-0x29, 0xD4, 0x7B, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x2A, 0xC4, 0x5E, 0xD0, 0x00, 0x00, 0x00, 0x00,
+0x29, 0xD4, 0xA6, 0x10, 0x00, 0x00, 0x00, 0x00, 0x2A, 0xC4, 0x97, 0x10, 0x00, 0x00, 0x00, 0x00,
0x2B, 0xB4, 0x88, 0x10, 0x00, 0x00, 0x00, 0x00, 0x2C, 0xA4, 0x79, 0x10, 0x00, 0x00, 0x00, 0x00,
0x2D, 0x94, 0x6A, 0x10, 0x00, 0x00, 0x00, 0x00, 0x2E, 0x84, 0x5B, 0x10, 0x00, 0x00, 0x00, 0x00,
0x2F, 0x74, 0x4C, 0x10, 0x00, 0x00, 0x00, 0x00, 0x30, 0x64, 0x3D, 0x10, 0x00, 0x00, 0x00, 0x00,
@@ -19606,7 +19691,7 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x4B, 0xAE, 0x39, 0x90, 0x00, 0x00, 0x00, 0x00, 0x4C, 0xCC, 0x4F, 0x10, 0x00, 0x00, 0x00, 0x00,
0x4D, 0x8E, 0x1B, 0x90, 0x00, 0x00, 0x00, 0x00, 0x54, 0x4B, 0xC9, 0x00, 0x00, 0x00, 0x00, 0x00,
0x56, 0xF6, 0xCE, 0x20, 0x00, 0x01, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x04, 0x05,
-0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x06, 0x07, 0x04, 0x02, 0x03,
+0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x06, 0x07, 0x04, 0x05, 0x04,
0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04,
0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04,
0x05, 0x04, 0x05, 0x04, 0x08, 0x09, 0x03, 0x00, 0x00, 0x6A, 0x60, 0x00, 0x00, 0x00, 0x00, 0x70,
@@ -20679,7 +20764,7 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x1E, 0x8C, 0x2D, 0xA0, 0x1F, 0x7C, 0x1E, 0xA0, 0x20, 0x6C, 0x0F, 0xA0, 0x21, 0x5C, 0x00, 0xA0,
0x22, 0x4B, 0xF1, 0xA0, 0x23, 0x3B, 0xE2, 0xA0, 0x24, 0x2B, 0xD3, 0xA0, 0x25, 0x1B, 0xC4, 0xA0,
0x26, 0x0B, 0xB5, 0xA0, 0x27, 0x04, 0xE1, 0x20, 0x27, 0xF4, 0xD2, 0x20, 0x28, 0xE4, 0xD1, 0x30,
-0x29, 0x78, 0x79, 0x30, 0x29, 0xD4, 0x89, 0xF0, 0x2A, 0xC4, 0x6C, 0xE0, 0x2B, 0xB4, 0x96, 0x20,
+0x29, 0x78, 0x79, 0x30, 0x29, 0xD4, 0xB4, 0x20, 0x2A, 0xC4, 0xA5, 0x20, 0x2B, 0xB4, 0x96, 0x20,
0x2C, 0xA4, 0x87, 0x20, 0x2D, 0x94, 0x78, 0x20, 0x2E, 0x84, 0x69, 0x20, 0x2F, 0x74, 0x5A, 0x20,
0x30, 0x64, 0x4B, 0x20, 0x31, 0x5D, 0x76, 0xA0, 0x32, 0x72, 0x51, 0xA0, 0x33, 0x3D, 0x58, 0xA0,
0x34, 0x52, 0x33, 0xA0, 0x35, 0x1D, 0x3A, 0xA0, 0x36, 0x32, 0x15, 0xA0, 0x36, 0xFD, 0x1C, 0xA0,
@@ -20690,7 +20775,7 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x47, 0x23, 0x7C, 0xA0, 0x47, 0xEE, 0x83, 0xA0, 0x49, 0x03, 0x5E, 0xA0, 0x49, 0xCE, 0x65, 0xA0,
0x4A, 0xE3, 0x40, 0xA0, 0x4B, 0xAE, 0x47, 0xA0, 0x4C, 0xCC, 0x5D, 0x20, 0x4D, 0x8E, 0x29, 0xA0,
0x54, 0x4B, 0xD7, 0x10, 0x01, 0x02, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x05, 0x06,
-0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x07, 0x08, 0x05, 0x03, 0x04,
+0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x07, 0x08, 0x05, 0x06, 0x05,
0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05,
0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05,
0x06, 0x05, 0x06, 0x05, 0x09, 0x05, 0x00, 0x00, 0x61, 0xC1, 0x00, 0x00, 0x00, 0x00, 0x61, 0xC1,
@@ -20718,8 +20803,8 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x24, 0x2B, 0xD3, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x25, 0x1B, 0xC4, 0xA0, 0x00, 0x00, 0x00, 0x00,
0x26, 0x0B, 0xB5, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x27, 0x04, 0xE1, 0x20, 0x00, 0x00, 0x00, 0x00,
0x27, 0xF4, 0xD2, 0x20, 0x00, 0x00, 0x00, 0x00, 0x28, 0xE4, 0xD1, 0x30, 0x00, 0x00, 0x00, 0x00,
-0x29, 0x78, 0x79, 0x30, 0x00, 0x00, 0x00, 0x00, 0x29, 0xD4, 0x89, 0xF0, 0x00, 0x00, 0x00, 0x00,
-0x2A, 0xC4, 0x6C, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x2B, 0xB4, 0x96, 0x20, 0x00, 0x00, 0x00, 0x00,
+0x29, 0x78, 0x79, 0x30, 0x00, 0x00, 0x00, 0x00, 0x29, 0xD4, 0xB4, 0x20, 0x00, 0x00, 0x00, 0x00,
+0x2A, 0xC4, 0xA5, 0x20, 0x00, 0x00, 0x00, 0x00, 0x2B, 0xB4, 0x96, 0x20, 0x00, 0x00, 0x00, 0x00,
0x2C, 0xA4, 0x87, 0x20, 0x00, 0x00, 0x00, 0x00, 0x2D, 0x94, 0x78, 0x20, 0x00, 0x00, 0x00, 0x00,
0x2E, 0x84, 0x69, 0x20, 0x00, 0x00, 0x00, 0x00, 0x2F, 0x74, 0x5A, 0x20, 0x00, 0x00, 0x00, 0x00,
0x30, 0x64, 0x4B, 0x20, 0x00, 0x00, 0x00, 0x00, 0x31, 0x5D, 0x76, 0xA0, 0x00, 0x00, 0x00, 0x00,
@@ -20739,8 +20824,8 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x4A, 0xE3, 0x40, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x4B, 0xAE, 0x47, 0xA0, 0x00, 0x00, 0x00, 0x00,
0x4C, 0xCC, 0x5D, 0x20, 0x00, 0x00, 0x00, 0x00, 0x4D, 0x8E, 0x29, 0xA0, 0x00, 0x00, 0x00, 0x00,
0x54, 0x4B, 0xD7, 0x10, 0x00, 0x01, 0x02, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x05,
-0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x07, 0x08, 0x05, 0x03,
-0x04, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06,
+0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x07, 0x08, 0x05, 0x06,
+0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06,
0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06,
0x05, 0x06, 0x05, 0x06, 0x05, 0x09, 0x05, 0x00, 0x00, 0x61, 0xC1, 0x00, 0x00, 0x00, 0x00, 0x61,
0xC1, 0x00, 0x04, 0x00, 0x00, 0x62, 0x70, 0x00, 0x08, 0x00, 0x00, 0x7E, 0x90, 0x01, 0x0D, 0x00,
@@ -21166,7 +21251,7 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x1E, 0x8B, 0xF5, 0x60, 0x1F, 0x7B, 0xE6, 0x60, 0x20, 0x6B, 0xD7, 0x60, 0x21, 0x5B, 0xC8, 0x60,
0x22, 0x4B, 0xB9, 0x60, 0x23, 0x3B, 0xAA, 0x60, 0x24, 0x2B, 0x9B, 0x60, 0x25, 0x1B, 0x8C, 0x60,
0x26, 0x0B, 0x7D, 0x60, 0x27, 0x04, 0xA8, 0xE0, 0x27, 0xF4, 0x99, 0xE0, 0x28, 0xE4, 0x98, 0xF0,
-0x29, 0x78, 0x40, 0xF0, 0x29, 0xD4, 0x51, 0xB0, 0x2A, 0xC4, 0x34, 0xA0, 0x2B, 0xB4, 0x5D, 0xE0,
+0x29, 0x78, 0x40, 0xF0, 0x29, 0xD4, 0x7B, 0xE0, 0x2A, 0xC4, 0x6C, 0xE0, 0x2B, 0xB4, 0x5D, 0xE0,
0x2C, 0xA4, 0x4E, 0xE0, 0x2D, 0x94, 0x3F, 0xE0, 0x2E, 0x84, 0x30, 0xE0, 0x2F, 0x74, 0x21, 0xE0,
0x30, 0x64, 0x12, 0xE0, 0x31, 0x5D, 0x3E, 0x60, 0x32, 0x72, 0x19, 0x60, 0x33, 0x3D, 0x20, 0x60,
0x34, 0x51, 0xFB, 0x60, 0x35, 0x1D, 0x02, 0x60, 0x36, 0x31, 0xDD, 0x60, 0x36, 0xFC, 0xE4, 0x60,
@@ -21177,7 +21262,7 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x47, 0x23, 0x44, 0x60, 0x47, 0xEE, 0x4B, 0x60, 0x49, 0x03, 0x26, 0x60, 0x49, 0xCE, 0x2D, 0x60,
0x4A, 0xE3, 0x08, 0x60, 0x4B, 0xAE, 0x0F, 0x60, 0x4C, 0xCC, 0x32, 0xF0, 0x4D, 0x8D, 0xFF, 0x70,
0x00, 0x01, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05,
-0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x06, 0x07, 0x04, 0x02, 0x03, 0x05, 0x04, 0x05, 0x04,
+0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x06, 0x07, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04,
0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04,
0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x06, 0x07,
0x04, 0x00, 0x00, 0x94, 0xBC, 0x00, 0x00, 0x00, 0x00, 0x9A, 0xB0, 0x00, 0x04, 0x00, 0x00, 0xB6,
@@ -21203,7 +21288,7 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x25, 0x1B, 0x8C, 0x60, 0x00, 0x00, 0x00, 0x00, 0x26, 0x0B, 0x7D, 0x60, 0x00, 0x00, 0x00, 0x00,
0x27, 0x04, 0xA8, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x27, 0xF4, 0x99, 0xE0, 0x00, 0x00, 0x00, 0x00,
0x28, 0xE4, 0x98, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x29, 0x78, 0x40, 0xF0, 0x00, 0x00, 0x00, 0x00,
-0x29, 0xD4, 0x51, 0xB0, 0x00, 0x00, 0x00, 0x00, 0x2A, 0xC4, 0x34, 0xA0, 0x00, 0x00, 0x00, 0x00,
+0x29, 0xD4, 0x7B, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x2A, 0xC4, 0x6C, 0xE0, 0x00, 0x00, 0x00, 0x00,
0x2B, 0xB4, 0x5D, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x2C, 0xA4, 0x4E, 0xE0, 0x00, 0x00, 0x00, 0x00,
0x2D, 0x94, 0x3F, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x2E, 0x84, 0x30, 0xE0, 0x00, 0x00, 0x00, 0x00,
0x2F, 0x74, 0x21, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x30, 0x64, 0x12, 0xE0, 0x00, 0x00, 0x00, 0x00,
@@ -21223,7 +21308,7 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x49, 0xCE, 0x2D, 0x60, 0x00, 0x00, 0x00, 0x00, 0x4A, 0xE3, 0x08, 0x60, 0x00, 0x00, 0x00, 0x00,
0x4B, 0xAE, 0x0F, 0x60, 0x00, 0x00, 0x00, 0x00, 0x4C, 0xCC, 0x32, 0xF0, 0x00, 0x00, 0x00, 0x00,
0x4D, 0x8D, 0xFF, 0x70, 0x00, 0x01, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x04, 0x05,
-0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x06, 0x07, 0x04, 0x02, 0x03,
+0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x06, 0x07, 0x04, 0x05, 0x04,
0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04,
0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04,
0x05, 0x04, 0x06, 0x07, 0x04, 0x00, 0x00, 0x94, 0xBC, 0x00, 0x00, 0x00, 0x00, 0x9A, 0xB0, 0x00,
@@ -21332,7 +21417,7 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x1E, 0x8C, 0x1F, 0x90, 0x1F, 0x7C, 0x10, 0x90, 0x20, 0x6C, 0x01, 0x90, 0x21, 0x5B, 0xF2, 0x90,
0x22, 0x4B, 0xE3, 0x90, 0x23, 0x3B, 0xD4, 0x90, 0x24, 0x2B, 0xC5, 0x90, 0x25, 0x1B, 0xB6, 0x90,
0x26, 0x0B, 0xA7, 0x90, 0x27, 0x04, 0xD3, 0x10, 0x27, 0xF4, 0xC4, 0x10, 0x28, 0xE4, 0xC3, 0x20,
-0x29, 0x78, 0x6B, 0x20, 0x29, 0xD4, 0x7B, 0xE0, 0x2A, 0xC4, 0x5E, 0xD0, 0x2B, 0xB4, 0x88, 0x10,
+0x29, 0x78, 0x6B, 0x20, 0x29, 0xD4, 0xA6, 0x10, 0x2A, 0xC4, 0x97, 0x10, 0x2B, 0xB4, 0x88, 0x10,
0x2C, 0xA4, 0x79, 0x10, 0x2D, 0x94, 0x6A, 0x10, 0x2E, 0x84, 0x5B, 0x10, 0x2F, 0x74, 0x4C, 0x10,
0x30, 0x64, 0x3D, 0x10, 0x31, 0x5D, 0x68, 0x90, 0x32, 0x72, 0x43, 0x90, 0x33, 0x3D, 0x4A, 0x90,
0x34, 0x52, 0x25, 0x90, 0x35, 0x1D, 0x2C, 0x90, 0x36, 0x32, 0x07, 0x90, 0x36, 0xFD, 0x0E, 0x90,
@@ -21344,7 +21429,7 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x49, 0xCE, 0x49, 0x80, 0x4A, 0xE3, 0x24, 0x80, 0x4B, 0xAE, 0x2B, 0x80, 0x4C, 0xCC, 0x41, 0x00,
0x4D, 0x8E, 0x0D, 0x80, 0x4E, 0x6E, 0x02, 0x50, 0x54, 0x4B, 0xC9, 0x00, 0x00, 0x01, 0x03, 0x02,
0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05,
-0x04, 0x05, 0x04, 0x06, 0x07, 0x04, 0x02, 0x03, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04,
+0x04, 0x05, 0x04, 0x06, 0x07, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04,
0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x0A, 0x08,
0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x0B, 0x0C, 0x04,
0x00, 0x00, 0x7F, 0x15, 0x00, 0x00, 0x00, 0x00, 0x70, 0x80, 0x00, 0x04, 0x00, 0x00, 0x8C, 0xA0,
@@ -21373,7 +21458,7 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x25, 0x1B, 0xB6, 0x90, 0x00, 0x00, 0x00, 0x00, 0x26, 0x0B, 0xA7, 0x90, 0x00, 0x00, 0x00, 0x00,
0x27, 0x04, 0xD3, 0x10, 0x00, 0x00, 0x00, 0x00, 0x27, 0xF4, 0xC4, 0x10, 0x00, 0x00, 0x00, 0x00,
0x28, 0xE4, 0xC3, 0x20, 0x00, 0x00, 0x00, 0x00, 0x29, 0x78, 0x6B, 0x20, 0x00, 0x00, 0x00, 0x00,
-0x29, 0xD4, 0x7B, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x2A, 0xC4, 0x5E, 0xD0, 0x00, 0x00, 0x00, 0x00,
+0x29, 0xD4, 0xA6, 0x10, 0x00, 0x00, 0x00, 0x00, 0x2A, 0xC4, 0x97, 0x10, 0x00, 0x00, 0x00, 0x00,
0x2B, 0xB4, 0x88, 0x10, 0x00, 0x00, 0x00, 0x00, 0x2C, 0xA4, 0x79, 0x10, 0x00, 0x00, 0x00, 0x00,
0x2D, 0x94, 0x6A, 0x10, 0x00, 0x00, 0x00, 0x00, 0x2E, 0x84, 0x5B, 0x10, 0x00, 0x00, 0x00, 0x00,
0x2F, 0x74, 0x4C, 0x10, 0x00, 0x00, 0x00, 0x00, 0x30, 0x64, 0x3D, 0x10, 0x00, 0x00, 0x00, 0x00,
@@ -21395,7 +21480,7 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x4C, 0xCC, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4D, 0x8E, 0x0D, 0x80, 0x00, 0x00, 0x00, 0x00,
0x4E, 0x6E, 0x02, 0x50, 0x00, 0x00, 0x00, 0x00, 0x54, 0x4B, 0xC9, 0x00, 0x00, 0x01, 0x03, 0x02,
0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05,
-0x04, 0x05, 0x04, 0x06, 0x07, 0x04, 0x02, 0x03, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04,
+0x04, 0x05, 0x04, 0x06, 0x07, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04,
0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x0A, 0x08,
0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x0B, 0x0C, 0x04,
0x00, 0x00, 0x7F, 0x15, 0x00, 0x00, 0x00, 0x00, 0x70, 0x80, 0x00, 0x04, 0x00, 0x00, 0x8C, 0xA0,
@@ -21447,7 +21532,7 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x1E, 0x8C, 0x3B, 0xB0, 0x1F, 0x7C, 0x2C, 0xB0, 0x20, 0x6C, 0x1D, 0xB0, 0x21, 0x5C, 0x0E, 0xB0,
0x22, 0x4B, 0xFF, 0xB0, 0x23, 0x3B, 0xF0, 0xB0, 0x24, 0x2B, 0xE1, 0xB0, 0x25, 0x1B, 0xD2, 0xB0,
0x26, 0x0B, 0xC3, 0xB0, 0x27, 0x04, 0xEF, 0x30, 0x27, 0xF4, 0xE0, 0x30, 0x28, 0xE4, 0xDF, 0x40,
-0x29, 0x78, 0x87, 0x40, 0x29, 0xD4, 0x98, 0x00, 0x2A, 0xC4, 0x7A, 0xF0, 0x2B, 0xB4, 0xA4, 0x30,
+0x29, 0x78, 0x87, 0x40, 0x29, 0xD4, 0xC2, 0x30, 0x2A, 0xC4, 0xB3, 0x30, 0x2B, 0xB4, 0xA4, 0x30,
0x2C, 0xA4, 0x95, 0x30, 0x2D, 0x94, 0x86, 0x30, 0x2E, 0x84, 0x77, 0x30, 0x2F, 0x74, 0x68, 0x30,
0x30, 0x64, 0x59, 0x30, 0x31, 0x5D, 0x84, 0xB0, 0x32, 0x72, 0x5F, 0xB0, 0x33, 0x3D, 0x66, 0xB0,
0x34, 0x52, 0x41, 0xB0, 0x35, 0x1D, 0x48, 0xB0, 0x36, 0x32, 0x23, 0xB0, 0x36, 0xFD, 0x2A, 0xB0,
@@ -21458,7 +21543,7 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x47, 0x23, 0x8A, 0xB0, 0x47, 0xEE, 0x91, 0xB0, 0x49, 0x03, 0x6C, 0xB0, 0x49, 0xCE, 0x73, 0xB0,
0x4A, 0xE3, 0x4E, 0xB0, 0x4B, 0xAE, 0x55, 0xB0, 0x4C, 0xCC, 0x6B, 0x30, 0x4D, 0x8E, 0x37, 0xB0,
0x54, 0x4B, 0xE5, 0x20, 0x00, 0x01, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x04, 0x05,
-0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x06, 0x07, 0x04, 0x02, 0x03,
+0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x06, 0x07, 0x04, 0x05, 0x04,
0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04,
0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04,
0x05, 0x04, 0x05, 0x04, 0x08, 0x04, 0x00, 0x00, 0x57, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x54, 0x60,
@@ -21485,7 +21570,7 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x25, 0x1B, 0xD2, 0xB0, 0x00, 0x00, 0x00, 0x00, 0x26, 0x0B, 0xC3, 0xB0, 0x00, 0x00, 0x00, 0x00,
0x27, 0x04, 0xEF, 0x30, 0x00, 0x00, 0x00, 0x00, 0x27, 0xF4, 0xE0, 0x30, 0x00, 0x00, 0x00, 0x00,
0x28, 0xE4, 0xDF, 0x40, 0x00, 0x00, 0x00, 0x00, 0x29, 0x78, 0x87, 0x40, 0x00, 0x00, 0x00, 0x00,
-0x29, 0xD4, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2A, 0xC4, 0x7A, 0xF0, 0x00, 0x00, 0x00, 0x00,
+0x29, 0xD4, 0xC2, 0x30, 0x00, 0x00, 0x00, 0x00, 0x2A, 0xC4, 0xB3, 0x30, 0x00, 0x00, 0x00, 0x00,
0x2B, 0xB4, 0xA4, 0x30, 0x00, 0x00, 0x00, 0x00, 0x2C, 0xA4, 0x95, 0x30, 0x00, 0x00, 0x00, 0x00,
0x2D, 0x94, 0x86, 0x30, 0x00, 0x00, 0x00, 0x00, 0x2E, 0x84, 0x77, 0x30, 0x00, 0x00, 0x00, 0x00,
0x2F, 0x74, 0x68, 0x30, 0x00, 0x00, 0x00, 0x00, 0x30, 0x64, 0x59, 0x30, 0x00, 0x00, 0x00, 0x00,
@@ -21506,7 +21591,7 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x4B, 0xAE, 0x55, 0xB0, 0x00, 0x00, 0x00, 0x00, 0x4C, 0xCC, 0x6B, 0x30, 0x00, 0x00, 0x00, 0x00,
0x4D, 0x8E, 0x37, 0xB0, 0x00, 0x00, 0x00, 0x00, 0x54, 0x4B, 0xE5, 0x20, 0x00, 0x01, 0x03, 0x02,
0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05,
-0x04, 0x05, 0x04, 0x06, 0x07, 0x04, 0x02, 0x03, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04,
+0x04, 0x05, 0x04, 0x06, 0x07, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04,
0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04,
0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x08, 0x04, 0x00, 0x00,
0x57, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x54, 0x60, 0x00, 0x04, 0x00, 0x00, 0x70, 0x80, 0x01, 0x09,
@@ -21723,14 +21808,14 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
/* Asia/Magadan */
0x50, 0x48, 0x50, 0x32, 0x01, 0x52, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x0F, 0x80, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x43, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x0F, 0x80, 0x00, 0x00, 0x00,
0xAA, 0x19, 0x36, 0xA0, 0xB5, 0xA3, 0xA8, 0xE0, 0x15, 0x27, 0x37, 0x50, 0x16, 0x18, 0x6B, 0xC0,
0x17, 0x08, 0x6A, 0xD0, 0x17, 0xF9, 0x9F, 0x40, 0x18, 0xE9, 0x9E, 0x50, 0x19, 0xDA, 0xD2, 0xC0,
0x1A, 0xCC, 0x23, 0x50, 0x1B, 0xBC, 0x30, 0x70, 0x1C, 0xAC, 0x21, 0x70, 0x1D, 0x9C, 0x12, 0x70,
0x1E, 0x8C, 0x03, 0x70, 0x1F, 0x7B, 0xF4, 0x70, 0x20, 0x6B, 0xE5, 0x70, 0x21, 0x5B, 0xD6, 0x70,
0x22, 0x4B, 0xC7, 0x70, 0x23, 0x3B, 0xB8, 0x70, 0x24, 0x2B, 0xA9, 0x70, 0x25, 0x1B, 0x9A, 0x70,
0x26, 0x0B, 0x8B, 0x70, 0x27, 0x04, 0xB6, 0xF0, 0x27, 0xF4, 0xA7, 0xF0, 0x28, 0xE4, 0xA7, 0x00,
-0x29, 0x78, 0x4F, 0x00, 0x29, 0xD4, 0x5F, 0xC0, 0x2A, 0xC4, 0x42, 0xB0, 0x2B, 0xB4, 0x6B, 0xF0,
+0x29, 0x78, 0x4F, 0x00, 0x29, 0xD4, 0x89, 0xF0, 0x2A, 0xC4, 0x7A, 0xF0, 0x2B, 0xB4, 0x6B, 0xF0,
0x2C, 0xA4, 0x5C, 0xF0, 0x2D, 0x94, 0x4D, 0xF0, 0x2E, 0x84, 0x3E, 0xF0, 0x2F, 0x74, 0x2F, 0xF0,
0x30, 0x64, 0x20, 0xF0, 0x31, 0x5D, 0x4C, 0x70, 0x32, 0x72, 0x27, 0x70, 0x33, 0x3D, 0x2E, 0x70,
0x34, 0x52, 0x09, 0x70, 0x35, 0x1D, 0x10, 0x70, 0x36, 0x31, 0xEB, 0x70, 0x36, 0xFC, 0xF2, 0x70,
@@ -21740,21 +21825,22 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x43, 0x63, 0x8E, 0x70, 0x44, 0x25, 0x5A, 0xF0, 0x45, 0x43, 0x70, 0x70, 0x46, 0x05, 0x3C, 0xF0,
0x47, 0x23, 0x52, 0x70, 0x47, 0xEE, 0x59, 0x70, 0x49, 0x03, 0x34, 0x70, 0x49, 0xCE, 0x3B, 0x70,
0x4A, 0xE3, 0x16, 0x70, 0x4B, 0xAE, 0x1D, 0x70, 0x4C, 0xCC, 0x32, 0xF0, 0x4D, 0x8D, 0xFF, 0x70,
-0x54, 0x4B, 0xAC, 0xE0, 0x00, 0x01, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x04, 0x05,
-0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x06, 0x07, 0x04, 0x02, 0x03,
-0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04,
+0x54, 0x4B, 0xAC, 0xE0, 0x57, 0x1B, 0x9C, 0x00, 0x00, 0x01, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02,
+0x03, 0x02, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x06,
+0x07, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04,
0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04,
-0x05, 0x04, 0x05, 0x04, 0x08, 0x07, 0x00, 0x00, 0x8D, 0x60, 0x00, 0x00, 0x00, 0x00, 0x8C, 0xA0,
-0x00, 0x04, 0x00, 0x00, 0xA8, 0xC0, 0x01, 0x09, 0x00, 0x00, 0x9A, 0xB0, 0x00, 0x04, 0x00, 0x00,
-0x9A, 0xB0, 0x00, 0x04, 0x00, 0x00, 0xA8, 0xC0, 0x01, 0x09, 0x00, 0x00, 0x9A, 0xB0, 0x01, 0x09,
-0x00, 0x00, 0x8C, 0xA0, 0x00, 0x04, 0x00, 0x00, 0xA8, 0xC0, 0x00, 0x04, 0x00, 0x00, 0xA8, 0xC0,
-0x01, 0x09, 0x00, 0x00, 0x8C, 0xA0, 0x00, 0x04, 0x4C, 0x4D, 0x54, 0x00, 0x4D, 0x41, 0x47, 0x54,
-0x00, 0x4D, 0x41, 0x47, 0x53, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01,
-0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x08, 0x07, 0x04, 0x00, 0x00, 0x8D, 0x60, 0x00,
+0x00, 0x00, 0x00, 0x8C, 0xA0, 0x00, 0x04, 0x00, 0x00, 0xA8, 0xC0, 0x01, 0x09, 0x00, 0x00, 0x9A,
+0xB0, 0x00, 0x04, 0x00, 0x00, 0x9A, 0xB0, 0x00, 0x04, 0x00, 0x00, 0xA8, 0xC0, 0x01, 0x09, 0x00,
+0x00, 0x9A, 0xB0, 0x01, 0x09, 0x00, 0x00, 0x8C, 0xA0, 0x00, 0x04, 0x00, 0x00, 0xA8, 0xC0, 0x00,
+0x04, 0x00, 0x00, 0xA8, 0xC0, 0x01, 0x09, 0x00, 0x00, 0x9A, 0xB0, 0x00, 0x04, 0x4C, 0x4D, 0x54,
+0x00, 0x4D, 0x41, 0x47, 0x54, 0x00, 0x4D, 0x41, 0x47, 0x53, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00,
#ifdef TIMELIB_SUPPORTS_V2DATA
0x54, 0x5A, 0x69, 0x66, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x0F, 0xF8, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x43, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x0F, 0xF8, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xAA, 0x19, 0x36, 0xA0, 0xFF, 0xFF, 0xFF, 0xFF,
0xB5, 0xA3, 0xA8, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x15, 0x27, 0x37, 0x50, 0x00, 0x00, 0x00, 0x00,
0x16, 0x18, 0x6B, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x17, 0x08, 0x6A, 0xD0, 0x00, 0x00, 0x00, 0x00,
@@ -21768,7 +21854,7 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x25, 0x1B, 0x9A, 0x70, 0x00, 0x00, 0x00, 0x00, 0x26, 0x0B, 0x8B, 0x70, 0x00, 0x00, 0x00, 0x00,
0x27, 0x04, 0xB6, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x27, 0xF4, 0xA7, 0xF0, 0x00, 0x00, 0x00, 0x00,
0x28, 0xE4, 0xA7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x29, 0x78, 0x4F, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x29, 0xD4, 0x5F, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x2A, 0xC4, 0x42, 0xB0, 0x00, 0x00, 0x00, 0x00,
+0x29, 0xD4, 0x89, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x2A, 0xC4, 0x7A, 0xF0, 0x00, 0x00, 0x00, 0x00,
0x2B, 0xB4, 0x6B, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x2C, 0xA4, 0x5C, 0xF0, 0x00, 0x00, 0x00, 0x00,
0x2D, 0x94, 0x4D, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x2E, 0x84, 0x3E, 0xF0, 0x00, 0x00, 0x00, 0x00,
0x2F, 0x74, 0x2F, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x30, 0x64, 0x20, 0xF0, 0x00, 0x00, 0x00, 0x00,
@@ -21787,21 +21873,22 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x47, 0xEE, 0x59, 0x70, 0x00, 0x00, 0x00, 0x00, 0x49, 0x03, 0x34, 0x70, 0x00, 0x00, 0x00, 0x00,
0x49, 0xCE, 0x3B, 0x70, 0x00, 0x00, 0x00, 0x00, 0x4A, 0xE3, 0x16, 0x70, 0x00, 0x00, 0x00, 0x00,
0x4B, 0xAE, 0x1D, 0x70, 0x00, 0x00, 0x00, 0x00, 0x4C, 0xCC, 0x32, 0xF0, 0x00, 0x00, 0x00, 0x00,
-0x4D, 0x8D, 0xFF, 0x70, 0x00, 0x00, 0x00, 0x00, 0x54, 0x4B, 0xAC, 0xE0, 0x00, 0x01, 0x03, 0x02,
-0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05,
-0x04, 0x05, 0x04, 0x06, 0x07, 0x04, 0x02, 0x03, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04,
+0x4D, 0x8D, 0xFF, 0x70, 0x00, 0x00, 0x00, 0x00, 0x54, 0x4B, 0xAC, 0xE0, 0x00, 0x00, 0x00, 0x00,
+0x57, 0x1B, 0x9C, 0x00, 0x00, 0x01, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x04, 0x05,
+0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x06, 0x07, 0x04, 0x05, 0x04,
0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04,
-0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x08, 0x07, 0x00, 0x00,
-0x8D, 0x60, 0x00, 0x00, 0x00, 0x00, 0x8C, 0xA0, 0x00, 0x04, 0x00, 0x00, 0xA8, 0xC0, 0x01, 0x09,
-0x00, 0x00, 0x9A, 0xB0, 0x00, 0x04, 0x00, 0x00, 0x9A, 0xB0, 0x00, 0x04, 0x00, 0x00, 0xA8, 0xC0,
-0x01, 0x09, 0x00, 0x00, 0x9A, 0xB0, 0x01, 0x09, 0x00, 0x00, 0x8C, 0xA0, 0x00, 0x04, 0x00, 0x00,
-0xA8, 0xC0, 0x00, 0x04, 0x00, 0x00, 0xA8, 0xC0, 0x01, 0x09, 0x00, 0x00, 0x8C, 0xA0, 0x00, 0x04,
-0x4C, 0x4D, 0x54, 0x00, 0x4D, 0x41, 0x47, 0x54, 0x00, 0x4D, 0x41, 0x47, 0x53, 0x54, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x4D, 0x41, 0x47, 0x54, 0x2D, 0x31, 0x30, 0x0A,
+0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04,
+0x05, 0x04, 0x05, 0x04, 0x08, 0x07, 0x04, 0x00, 0x00, 0x8D, 0x60, 0x00, 0x00, 0x00, 0x00, 0x8C,
+0xA0, 0x00, 0x04, 0x00, 0x00, 0xA8, 0xC0, 0x01, 0x09, 0x00, 0x00, 0x9A, 0xB0, 0x00, 0x04, 0x00,
+0x00, 0x9A, 0xB0, 0x00, 0x04, 0x00, 0x00, 0xA8, 0xC0, 0x01, 0x09, 0x00, 0x00, 0x9A, 0xB0, 0x01,
+0x09, 0x00, 0x00, 0x8C, 0xA0, 0x00, 0x04, 0x00, 0x00, 0xA8, 0xC0, 0x00, 0x04, 0x00, 0x00, 0xA8,
+0xC0, 0x01, 0x09, 0x00, 0x00, 0x9A, 0xB0, 0x00, 0x04, 0x4C, 0x4D, 0x54, 0x00, 0x4D, 0x41, 0x47,
+0x54, 0x00, 0x4D, 0x41, 0x47, 0x53, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x4D,
+0x41, 0x47, 0x54, 0x2D, 0x31, 0x31, 0x0A,
#endif
0x00, 0xE4, 0x38, 0x7A, 0x01, 0xF8, 0xC2, 0xC0, 0x00, 0x00, 0x00, 0x10, 0x4D, 0x53, 0x4B, 0x2B,
-0x30, 0x37, 0x20, 0x2D, 0x20, 0x4D, 0x61, 0x67, 0x61, 0x64, 0x61, 0x6E,
+0x30, 0x38, 0x20, 0x2D, 0x20, 0x4D, 0x61, 0x67, 0x61, 0x64, 0x61, 0x6E,
/* Asia/Makassar */
0x50, 0x48, 0x50, 0x32, 0x01, 0x49, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -22018,7 +22105,7 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x1E, 0x8C, 0x3B, 0xB0, 0x1F, 0x7C, 0x2C, 0xB0, 0x20, 0x6C, 0x1D, 0xB0, 0x21, 0x5C, 0x0E, 0xB0,
0x22, 0x4B, 0xFF, 0xB0, 0x23, 0x3B, 0xF0, 0xB0, 0x24, 0x2B, 0xE1, 0xB0, 0x25, 0x1B, 0xD2, 0xB0,
0x26, 0x0B, 0xC3, 0xB0, 0x27, 0x04, 0xEF, 0x30, 0x27, 0xF4, 0xE0, 0x30, 0x28, 0xE4, 0xDF, 0x40,
-0x29, 0x78, 0x87, 0x40, 0x29, 0xD4, 0x98, 0x00, 0x2A, 0xC4, 0x7A, 0xF0, 0x2B, 0xB4, 0xA4, 0x30,
+0x29, 0x78, 0x87, 0x40, 0x29, 0xD4, 0xC2, 0x30, 0x2A, 0xC4, 0xB3, 0x30, 0x2B, 0xB4, 0xA4, 0x30,
0x2C, 0xA4, 0x95, 0x30, 0x2D, 0x94, 0x86, 0x30, 0x2E, 0x84, 0x77, 0x30, 0x2F, 0x74, 0x68, 0x30,
0x30, 0x64, 0x59, 0x30, 0x31, 0x5D, 0x84, 0xB0, 0x32, 0x72, 0x5F, 0xB0, 0x33, 0x3D, 0x66, 0xB0,
0x34, 0x52, 0x41, 0xB0, 0x35, 0x1D, 0x48, 0xB0, 0x36, 0x32, 0x23, 0xB0, 0x36, 0xFD, 0x2A, 0xB0,
@@ -22029,7 +22116,7 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x47, 0x23, 0x8A, 0xB0, 0x47, 0xEE, 0x91, 0xB0, 0x49, 0x03, 0x6C, 0xB0, 0x49, 0xCE, 0x73, 0xB0,
0x4A, 0xE3, 0x4E, 0xB0, 0x4B, 0xAE, 0x55, 0xB0, 0x4C, 0xCC, 0x79, 0x40, 0x4D, 0x8E, 0x45, 0xC0,
0x54, 0x4B, 0xF3, 0x30, 0x00, 0x01, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x04, 0x05,
-0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x06, 0x07, 0x04, 0x02, 0x03,
+0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x06, 0x07, 0x04, 0x05, 0x04,
0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04,
0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04,
0x05, 0x04, 0x08, 0x09, 0x0A, 0x04, 0x00, 0x00, 0x51, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x54, 0x60,
@@ -22057,7 +22144,7 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x25, 0x1B, 0xD2, 0xB0, 0x00, 0x00, 0x00, 0x00, 0x26, 0x0B, 0xC3, 0xB0, 0x00, 0x00, 0x00, 0x00,
0x27, 0x04, 0xEF, 0x30, 0x00, 0x00, 0x00, 0x00, 0x27, 0xF4, 0xE0, 0x30, 0x00, 0x00, 0x00, 0x00,
0x28, 0xE4, 0xDF, 0x40, 0x00, 0x00, 0x00, 0x00, 0x29, 0x78, 0x87, 0x40, 0x00, 0x00, 0x00, 0x00,
-0x29, 0xD4, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2A, 0xC4, 0x7A, 0xF0, 0x00, 0x00, 0x00, 0x00,
+0x29, 0xD4, 0xC2, 0x30, 0x00, 0x00, 0x00, 0x00, 0x2A, 0xC4, 0xB3, 0x30, 0x00, 0x00, 0x00, 0x00,
0x2B, 0xB4, 0xA4, 0x30, 0x00, 0x00, 0x00, 0x00, 0x2C, 0xA4, 0x95, 0x30, 0x00, 0x00, 0x00, 0x00,
0x2D, 0x94, 0x86, 0x30, 0x00, 0x00, 0x00, 0x00, 0x2E, 0x84, 0x77, 0x30, 0x00, 0x00, 0x00, 0x00,
0x2F, 0x74, 0x68, 0x30, 0x00, 0x00, 0x00, 0x00, 0x30, 0x64, 0x59, 0x30, 0x00, 0x00, 0x00, 0x00,
@@ -22078,7 +22165,7 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x4B, 0xAE, 0x55, 0xB0, 0x00, 0x00, 0x00, 0x00, 0x4C, 0xCC, 0x79, 0x40, 0x00, 0x00, 0x00, 0x00,
0x4D, 0x8E, 0x45, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x54, 0x4B, 0xF3, 0x30, 0x00, 0x01, 0x03, 0x02,
0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05,
-0x04, 0x05, 0x04, 0x06, 0x07, 0x04, 0x02, 0x03, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04,
+0x04, 0x05, 0x04, 0x06, 0x07, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04,
0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04,
0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x08, 0x09, 0x0A, 0x04, 0x00, 0x00,
0x51, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x54, 0x60, 0x00, 0x04, 0x00, 0x00, 0x70, 0x80, 0x01, 0x09,
@@ -22103,7 +22190,7 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x1E, 0x8C, 0x3B, 0xB0, 0x1F, 0x7C, 0x2C, 0xB0, 0x20, 0x6C, 0x1D, 0xB0, 0x21, 0x5C, 0x0E, 0xB0,
0x22, 0x4B, 0xFF, 0xB0, 0x23, 0x3B, 0xF0, 0xB0, 0x24, 0x2B, 0xE1, 0xB0, 0x25, 0x1B, 0xD2, 0xB0,
0x26, 0x0B, 0xC3, 0xB0, 0x27, 0x04, 0xEF, 0x30, 0x27, 0xF4, 0xE0, 0x30, 0x28, 0xE4, 0xDF, 0x40,
-0x29, 0x78, 0x87, 0x40, 0x29, 0xD4, 0x98, 0x00, 0x2A, 0xC4, 0x7A, 0xF0, 0x2B, 0xB4, 0xA4, 0x30,
+0x29, 0x78, 0x87, 0x40, 0x29, 0xD4, 0xC2, 0x30, 0x2A, 0xC4, 0xB3, 0x30, 0x2B, 0xB4, 0xA4, 0x30,
0x2B, 0xFE, 0x4E, 0x00, 0x2C, 0xA4, 0xA3, 0x40, 0x2D, 0x94, 0x94, 0x40, 0x2E, 0x84, 0x85, 0x40,
0x2F, 0x74, 0x76, 0x40, 0x30, 0x64, 0x67, 0x40, 0x31, 0x5D, 0x92, 0xC0, 0x32, 0x72, 0x6D, 0xC0,
0x33, 0x3D, 0x74, 0xC0, 0x34, 0x52, 0x4F, 0xC0, 0x35, 0x1D, 0x56, 0xC0, 0x36, 0x32, 0x31, 0xC0,
@@ -22115,7 +22202,7 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x49, 0xCE, 0x81, 0xC0, 0x4A, 0xE3, 0x5C, 0xC0, 0x4B, 0xAE, 0x63, 0xC0, 0x4C, 0xCC, 0x79, 0x40,
0x4D, 0x8E, 0x45, 0xC0, 0x54, 0x4B, 0xF3, 0x30, 0x00, 0x01, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02,
0x03, 0x02, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x06,
-0x07, 0x04, 0x02, 0x03, 0x05, 0x08, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06,
+0x07, 0x04, 0x05, 0x04, 0x05, 0x08, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06,
0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06,
0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x04, 0x07, 0x00, 0x00, 0x4D, 0xBC, 0x00,
0x00, 0x00, 0x00, 0x54, 0x60, 0x00, 0x04, 0x00, 0x00, 0x70, 0x80, 0x01, 0x09, 0x00, 0x00, 0x62,
@@ -22141,7 +22228,7 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x25, 0x1B, 0xD2, 0xB0, 0x00, 0x00, 0x00, 0x00, 0x26, 0x0B, 0xC3, 0xB0, 0x00, 0x00, 0x00, 0x00,
0x27, 0x04, 0xEF, 0x30, 0x00, 0x00, 0x00, 0x00, 0x27, 0xF4, 0xE0, 0x30, 0x00, 0x00, 0x00, 0x00,
0x28, 0xE4, 0xDF, 0x40, 0x00, 0x00, 0x00, 0x00, 0x29, 0x78, 0x87, 0x40, 0x00, 0x00, 0x00, 0x00,
-0x29, 0xD4, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2A, 0xC4, 0x7A, 0xF0, 0x00, 0x00, 0x00, 0x00,
+0x29, 0xD4, 0xC2, 0x30, 0x00, 0x00, 0x00, 0x00, 0x2A, 0xC4, 0xB3, 0x30, 0x00, 0x00, 0x00, 0x00,
0x2B, 0xB4, 0xA4, 0x30, 0x00, 0x00, 0x00, 0x00, 0x2B, 0xFE, 0x4E, 0x00, 0x00, 0x00, 0x00, 0x00,
0x2C, 0xA4, 0xA3, 0x40, 0x00, 0x00, 0x00, 0x00, 0x2D, 0x94, 0x94, 0x40, 0x00, 0x00, 0x00, 0x00,
0x2E, 0x84, 0x85, 0x40, 0x00, 0x00, 0x00, 0x00, 0x2F, 0x74, 0x76, 0x40, 0x00, 0x00, 0x00, 0x00,
@@ -22162,7 +22249,7 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x4A, 0xE3, 0x5C, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x4B, 0xAE, 0x63, 0xC0, 0x00, 0x00, 0x00, 0x00,
0x4C, 0xCC, 0x79, 0x40, 0x00, 0x00, 0x00, 0x00, 0x4D, 0x8E, 0x45, 0xC0, 0x00, 0x00, 0x00, 0x00,
0x54, 0x4B, 0xF3, 0x30, 0x00, 0x01, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x04, 0x05,
-0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x06, 0x07, 0x04, 0x02, 0x03,
+0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x06, 0x07, 0x04, 0x05, 0x04,
0x05, 0x08, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06,
0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06,
0x07, 0x06, 0x07, 0x06, 0x07, 0x04, 0x07, 0x00, 0x00, 0x4D, 0xBC, 0x00, 0x00, 0x00, 0x00, 0x54,
@@ -22173,9 +22260,9 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x4E,
0x4F, 0x56, 0x54, 0x2D, 0x36, 0x0A,
#endif
-0x00, 0xDD, 0x4D, 0xA5, 0x01, 0x91, 0x2D, 0xD2, 0x00, 0x00, 0x00, 0x1B, 0x4D, 0x53, 0x4B, 0x2B,
+0x00, 0xDD, 0x4D, 0xA5, 0x01, 0x91, 0x2D, 0xD2, 0x00, 0x00, 0x00, 0x14, 0x4D, 0x53, 0x4B, 0x2B,
0x30, 0x33, 0x20, 0x2D, 0x20, 0x4E, 0x6F, 0x76, 0x6F, 0x73, 0x69, 0x62, 0x69, 0x72, 0x73, 0x6B,
-0x2C, 0x20, 0x54, 0x6F, 0x6D, 0x73, 0x6B,
+
/* Asia/Omsk */
0x50, 0x48, 0x50, 0x32, 0x01, 0x52, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -22187,7 +22274,7 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x1E, 0x8C, 0x49, 0xC0, 0x1F, 0x7C, 0x3A, 0xC0, 0x20, 0x6C, 0x2B, 0xC0, 0x21, 0x5C, 0x1C, 0xC0,
0x22, 0x4C, 0x0D, 0xC0, 0x23, 0x3B, 0xFE, 0xC0, 0x24, 0x2B, 0xEF, 0xC0, 0x25, 0x1B, 0xE0, 0xC0,
0x26, 0x0B, 0xD1, 0xC0, 0x27, 0x04, 0xFD, 0x40, 0x27, 0xF4, 0xEE, 0x40, 0x28, 0xE4, 0xED, 0x50,
-0x29, 0x78, 0x95, 0x50, 0x29, 0xD4, 0xA6, 0x10, 0x2A, 0xC4, 0x89, 0x00, 0x2B, 0xB4, 0xB2, 0x40,
+0x29, 0x78, 0x95, 0x50, 0x29, 0xD4, 0xD0, 0x40, 0x2A, 0xC4, 0xC1, 0x40, 0x2B, 0xB4, 0xB2, 0x40,
0x2C, 0xA4, 0xA3, 0x40, 0x2D, 0x94, 0x94, 0x40, 0x2E, 0x84, 0x85, 0x40, 0x2F, 0x74, 0x76, 0x40,
0x30, 0x64, 0x67, 0x40, 0x31, 0x5D, 0x92, 0xC0, 0x32, 0x72, 0x6D, 0xC0, 0x33, 0x3D, 0x74, 0xC0,
0x34, 0x52, 0x4F, 0xC0, 0x35, 0x1D, 0x56, 0xC0, 0x36, 0x32, 0x31, 0xC0, 0x36, 0xFD, 0x38, 0xC0,
@@ -22198,7 +22285,7 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x47, 0x23, 0x98, 0xC0, 0x47, 0xEE, 0x9F, 0xC0, 0x49, 0x03, 0x7A, 0xC0, 0x49, 0xCE, 0x81, 0xC0,
0x4A, 0xE3, 0x5C, 0xC0, 0x4B, 0xAE, 0x63, 0xC0, 0x4C, 0xCC, 0x79, 0x40, 0x4D, 0x8E, 0x45, 0xC0,
0x54, 0x4B, 0xF3, 0x30, 0x00, 0x01, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x04, 0x05,
-0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x06, 0x07, 0x04, 0x02, 0x03,
+0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x06, 0x07, 0x04, 0x05, 0x04,
0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04,
0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04,
0x05, 0x04, 0x05, 0x04, 0x08, 0x04, 0x00, 0x00, 0x44, 0xCA, 0x00, 0x00, 0x00, 0x00, 0x46, 0x50,
@@ -22225,7 +22312,7 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x25, 0x1B, 0xE0, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x26, 0x0B, 0xD1, 0xC0, 0x00, 0x00, 0x00, 0x00,
0x27, 0x04, 0xFD, 0x40, 0x00, 0x00, 0x00, 0x00, 0x27, 0xF4, 0xEE, 0x40, 0x00, 0x00, 0x00, 0x00,
0x28, 0xE4, 0xED, 0x50, 0x00, 0x00, 0x00, 0x00, 0x29, 0x78, 0x95, 0x50, 0x00, 0x00, 0x00, 0x00,
-0x29, 0xD4, 0xA6, 0x10, 0x00, 0x00, 0x00, 0x00, 0x2A, 0xC4, 0x89, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x29, 0xD4, 0xD0, 0x40, 0x00, 0x00, 0x00, 0x00, 0x2A, 0xC4, 0xC1, 0x40, 0x00, 0x00, 0x00, 0x00,
0x2B, 0xB4, 0xB2, 0x40, 0x00, 0x00, 0x00, 0x00, 0x2C, 0xA4, 0xA3, 0x40, 0x00, 0x00, 0x00, 0x00,
0x2D, 0x94, 0x94, 0x40, 0x00, 0x00, 0x00, 0x00, 0x2E, 0x84, 0x85, 0x40, 0x00, 0x00, 0x00, 0x00,
0x2F, 0x74, 0x76, 0x40, 0x00, 0x00, 0x00, 0x00, 0x30, 0x64, 0x67, 0x40, 0x00, 0x00, 0x00, 0x00,
@@ -22246,7 +22333,7 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x4B, 0xAE, 0x63, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x4C, 0xCC, 0x79, 0x40, 0x00, 0x00, 0x00, 0x00,
0x4D, 0x8E, 0x45, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x54, 0x4B, 0xF3, 0x30, 0x00, 0x01, 0x03, 0x02,
0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05,
-0x04, 0x05, 0x04, 0x06, 0x07, 0x04, 0x02, 0x03, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04,
+0x04, 0x05, 0x04, 0x06, 0x07, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04,
0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04,
0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x08, 0x04, 0x00, 0x00,
0x44, 0xCA, 0x00, 0x00, 0x00, 0x00, 0x46, 0x50, 0x00, 0x04, 0x00, 0x00, 0x62, 0x70, 0x01, 0x09,
@@ -22262,37 +22349,35 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
/* Asia/Oral */
0x50, 0x48, 0x50, 0x32, 0x01, 0x4B, 0x5A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x1A, 0x80, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x10, 0x80, 0x00, 0x00, 0x00,
0xAA, 0x19, 0x93, 0xDC, 0xB5, 0xA3, 0xFD, 0x40, 0x15, 0x27, 0x8B, 0xB0, 0x16, 0x18, 0xC0, 0x20,
0x17, 0x08, 0xB1, 0x20, 0x17, 0xF9, 0xF3, 0xA0, 0x18, 0xE9, 0xF2, 0xB0, 0x19, 0xDB, 0x27, 0x20,
0x1A, 0xCC, 0x77, 0xB0, 0x1B, 0xBC, 0x84, 0xD0, 0x1C, 0xAC, 0x75, 0xD0, 0x1D, 0x9C, 0x66, 0xD0,
0x1E, 0x8C, 0x57, 0xD0, 0x1F, 0x7C, 0x48, 0xD0, 0x20, 0x6C, 0x39, 0xD0, 0x21, 0x5C, 0x2A, 0xD0,
0x22, 0x4C, 0x1B, 0xD0, 0x23, 0x3C, 0x0C, 0xD0, 0x24, 0x2B, 0xFD, 0xD0, 0x25, 0x1B, 0xFC, 0xE0,
-0x26, 0x0B, 0xED, 0xE0, 0x27, 0x05, 0x19, 0x60, 0x27, 0x7F, 0x98, 0xC0, 0x29, 0x4B, 0xB4, 0x40,
-0x29, 0xD4, 0xC2, 0x30, 0x2A, 0xC4, 0xA5, 0x20, 0x2B, 0xB4, 0xCE, 0x60, 0x2C, 0xA4, 0xBF, 0x60,
-0x2D, 0x94, 0xB0, 0x60, 0x2E, 0x84, 0xA1, 0x60, 0x2F, 0x74, 0x92, 0x60, 0x30, 0x64, 0x83, 0x60,
-0x31, 0x5D, 0xAE, 0xE0, 0x32, 0x72, 0x89, 0xE0, 0x33, 0x3D, 0x90, 0xE0, 0x34, 0x52, 0x6B, 0xE0,
-0x35, 0x1D, 0x72, 0xE0, 0x36, 0x32, 0x4D, 0xE0, 0x36, 0xFD, 0x54, 0xE0, 0x38, 0x1B, 0x6A, 0x60,
-0x38, 0xDD, 0x36, 0xE0, 0x39, 0xFB, 0x4C, 0x60, 0x3A, 0xBD, 0x18, 0xE0, 0x3B, 0xDB, 0x2E, 0x60,
-0x3C, 0xA6, 0x35, 0x60, 0x3D, 0xBB, 0x10, 0x60, 0x3E, 0x86, 0x17, 0x60, 0x3F, 0x9A, 0xF2, 0x60,
-0x40, 0x65, 0xF9, 0x60, 0x41, 0x84, 0x0E, 0xE0, 0x42, 0x35, 0xED, 0x40, 0x00, 0x01, 0x02, 0x03,
-0x04, 0x03, 0x02, 0x03, 0x02, 0x03, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x07,
-0x08, 0x07, 0x08, 0x01, 0x0A, 0x09, 0x0A, 0x0B, 0x0C, 0x0B, 0x0C, 0x0B, 0x0C, 0x0B, 0x0C, 0x0B,
-0x0C, 0x0B, 0x0C, 0x0B, 0x0C, 0x0B, 0x0C, 0x0B, 0x0C, 0x0B, 0x0C, 0x0B, 0x0C, 0x0B, 0x0C, 0x0D,
-0x00, 0x00, 0x30, 0x24, 0x00, 0x00, 0x00, 0x00, 0x38, 0x40, 0x00, 0x04, 0x00, 0x00, 0x46, 0x50,
-0x00, 0x04, 0x00, 0x00, 0x54, 0x60, 0x01, 0x09, 0x00, 0x00, 0x54, 0x60, 0x00, 0x04, 0x00, 0x00,
-0x46, 0x50, 0x00, 0x04, 0x00, 0x00, 0x54, 0x60, 0x01, 0x09, 0x00, 0x00, 0x46, 0x50, 0x01, 0x09,
-0x00, 0x00, 0x38, 0x40, 0x00, 0x04, 0x00, 0x00, 0x46, 0x50, 0x01, 0x0F, 0x00, 0x00, 0x38, 0x40,
-0x00, 0x15, 0x00, 0x00, 0x46, 0x50, 0x01, 0x0F, 0x00, 0x00, 0x38, 0x40, 0x00, 0x15, 0x00, 0x00,
-0x46, 0x50, 0x00, 0x15, 0x4C, 0x4D, 0x54, 0x00, 0x55, 0x52, 0x41, 0x54, 0x00, 0x55, 0x52, 0x41,
-0x53, 0x54, 0x00, 0x4F, 0x52, 0x41, 0x53, 0x54, 0x00, 0x4F, 0x52, 0x41, 0x54, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x26, 0x0B, 0xED, 0xE0, 0x27, 0x05, 0x19, 0x60, 0x27, 0xF5, 0x0A, 0x60, 0x28, 0xE4, 0xFB, 0x60,
+0x29, 0x78, 0xA3, 0x60, 0x29, 0xD4, 0xDE, 0x50, 0x2A, 0xC4, 0xDD, 0x60, 0x2B, 0xB4, 0xCE, 0x60,
+0x2C, 0xA4, 0xBF, 0x60, 0x2D, 0x94, 0xB0, 0x60, 0x2E, 0x84, 0xA1, 0x60, 0x2F, 0x74, 0x92, 0x60,
+0x30, 0x64, 0x83, 0x60, 0x31, 0x5D, 0xAE, 0xE0, 0x32, 0x72, 0x89, 0xE0, 0x33, 0x3D, 0x90, 0xE0,
+0x34, 0x52, 0x6B, 0xE0, 0x35, 0x1D, 0x72, 0xE0, 0x36, 0x32, 0x4D, 0xE0, 0x36, 0xFD, 0x54, 0xE0,
+0x38, 0x1B, 0x6A, 0x60, 0x38, 0xDD, 0x36, 0xE0, 0x39, 0xFB, 0x4C, 0x60, 0x3A, 0xBD, 0x18, 0xE0,
+0x3B, 0xDB, 0x2E, 0x60, 0x3C, 0xA6, 0x35, 0x60, 0x3D, 0xBB, 0x10, 0x60, 0x3E, 0x86, 0x17, 0x60,
+0x3F, 0x9A, 0xF2, 0x60, 0x40, 0x65, 0xF9, 0x60, 0x41, 0x84, 0x0E, 0xE0, 0x7F, 0xFF, 0xFF, 0xFF,
+0x00, 0x01, 0x02, 0x03, 0x04, 0x03, 0x02, 0x03, 0x02, 0x03, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06,
+0x05, 0x06, 0x05, 0x07, 0x08, 0x07, 0x08, 0x07, 0x08, 0x05, 0x07, 0x08, 0x07, 0x08, 0x07, 0x08,
+0x07, 0x08, 0x07, 0x08, 0x07, 0x08, 0x07, 0x08, 0x07, 0x08, 0x07, 0x08, 0x07, 0x08, 0x07, 0x08,
+0x07, 0x08, 0x07, 0x05, 0x05, 0x00, 0x00, 0x30, 0x24, 0x00, 0x00, 0x00, 0x00, 0x38, 0x40, 0x00,
+0x04, 0x00, 0x00, 0x46, 0x50, 0x00, 0x08, 0x00, 0x00, 0x54, 0x60, 0x01, 0x0C, 0x00, 0x00, 0x54,
+0x60, 0x00, 0x0C, 0x00, 0x00, 0x46, 0x50, 0x00, 0x08, 0x00, 0x00, 0x54, 0x60, 0x01, 0x0C, 0x00,
+0x00, 0x46, 0x50, 0x01, 0x08, 0x00, 0x00, 0x38, 0x40, 0x00, 0x04, 0x00, 0x00, 0x46, 0x50, 0x00,
+0x08, 0x4C, 0x4D, 0x54, 0x00, 0x2B, 0x30, 0x34, 0x00, 0x2B, 0x30, 0x35, 0x00, 0x2B, 0x30, 0x36,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00,
#ifdef TIMELIB_SUPPORTS_V2DATA
0x54, 0x5A, 0x69, 0x66, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x1A, 0xF8, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x10, 0xF8, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xAA, 0x19, 0x93, 0xDC, 0xFF, 0xFF, 0xFF, 0xFF,
0xB5, 0xA3, 0xFD, 0x40, 0x00, 0x00, 0x00, 0x00, 0x15, 0x27, 0x8B, 0xB0, 0x00, 0x00, 0x00, 0x00,
0x16, 0x18, 0xC0, 0x20, 0x00, 0x00, 0x00, 0x00, 0x17, 0x08, 0xB1, 0x20, 0x00, 0x00, 0x00, 0x00,
@@ -22304,33 +22389,31 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x21, 0x5C, 0x2A, 0xD0, 0x00, 0x00, 0x00, 0x00, 0x22, 0x4C, 0x1B, 0xD0, 0x00, 0x00, 0x00, 0x00,
0x23, 0x3C, 0x0C, 0xD0, 0x00, 0x00, 0x00, 0x00, 0x24, 0x2B, 0xFD, 0xD0, 0x00, 0x00, 0x00, 0x00,
0x25, 0x1B, 0xFC, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x26, 0x0B, 0xED, 0xE0, 0x00, 0x00, 0x00, 0x00,
-0x27, 0x05, 0x19, 0x60, 0x00, 0x00, 0x00, 0x00, 0x27, 0x7F, 0x98, 0xC0, 0x00, 0x00, 0x00, 0x00,
-0x29, 0x4B, 0xB4, 0x40, 0x00, 0x00, 0x00, 0x00, 0x29, 0xD4, 0xC2, 0x30, 0x00, 0x00, 0x00, 0x00,
-0x2A, 0xC4, 0xA5, 0x20, 0x00, 0x00, 0x00, 0x00, 0x2B, 0xB4, 0xCE, 0x60, 0x00, 0x00, 0x00, 0x00,
-0x2C, 0xA4, 0xBF, 0x60, 0x00, 0x00, 0x00, 0x00, 0x2D, 0x94, 0xB0, 0x60, 0x00, 0x00, 0x00, 0x00,
-0x2E, 0x84, 0xA1, 0x60, 0x00, 0x00, 0x00, 0x00, 0x2F, 0x74, 0x92, 0x60, 0x00, 0x00, 0x00, 0x00,
-0x30, 0x64, 0x83, 0x60, 0x00, 0x00, 0x00, 0x00, 0x31, 0x5D, 0xAE, 0xE0, 0x00, 0x00, 0x00, 0x00,
-0x32, 0x72, 0x89, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x33, 0x3D, 0x90, 0xE0, 0x00, 0x00, 0x00, 0x00,
-0x34, 0x52, 0x6B, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x35, 0x1D, 0x72, 0xE0, 0x00, 0x00, 0x00, 0x00,
-0x36, 0x32, 0x4D, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x36, 0xFD, 0x54, 0xE0, 0x00, 0x00, 0x00, 0x00,
-0x38, 0x1B, 0x6A, 0x60, 0x00, 0x00, 0x00, 0x00, 0x38, 0xDD, 0x36, 0xE0, 0x00, 0x00, 0x00, 0x00,
-0x39, 0xFB, 0x4C, 0x60, 0x00, 0x00, 0x00, 0x00, 0x3A, 0xBD, 0x18, 0xE0, 0x00, 0x00, 0x00, 0x00,
-0x3B, 0xDB, 0x2E, 0x60, 0x00, 0x00, 0x00, 0x00, 0x3C, 0xA6, 0x35, 0x60, 0x00, 0x00, 0x00, 0x00,
-0x3D, 0xBB, 0x10, 0x60, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x86, 0x17, 0x60, 0x00, 0x00, 0x00, 0x00,
-0x3F, 0x9A, 0xF2, 0x60, 0x00, 0x00, 0x00, 0x00, 0x40, 0x65, 0xF9, 0x60, 0x00, 0x00, 0x00, 0x00,
-0x41, 0x84, 0x0E, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x42, 0x35, 0xED, 0x40, 0x00, 0x01, 0x02, 0x03,
-0x04, 0x03, 0x02, 0x03, 0x02, 0x03, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x07,
-0x08, 0x07, 0x08, 0x01, 0x0A, 0x09, 0x0A, 0x0B, 0x0C, 0x0B, 0x0C, 0x0B, 0x0C, 0x0B, 0x0C, 0x0B,
-0x0C, 0x0B, 0x0C, 0x0B, 0x0C, 0x0B, 0x0C, 0x0B, 0x0C, 0x0B, 0x0C, 0x0B, 0x0C, 0x0B, 0x0C, 0x0D,
-0x00, 0x00, 0x30, 0x24, 0x00, 0x00, 0x00, 0x00, 0x38, 0x40, 0x00, 0x04, 0x00, 0x00, 0x46, 0x50,
-0x00, 0x04, 0x00, 0x00, 0x54, 0x60, 0x01, 0x09, 0x00, 0x00, 0x54, 0x60, 0x00, 0x04, 0x00, 0x00,
-0x46, 0x50, 0x00, 0x04, 0x00, 0x00, 0x54, 0x60, 0x01, 0x09, 0x00, 0x00, 0x46, 0x50, 0x01, 0x09,
-0x00, 0x00, 0x38, 0x40, 0x00, 0x04, 0x00, 0x00, 0x46, 0x50, 0x01, 0x0F, 0x00, 0x00, 0x38, 0x40,
-0x00, 0x15, 0x00, 0x00, 0x46, 0x50, 0x01, 0x0F, 0x00, 0x00, 0x38, 0x40, 0x00, 0x15, 0x00, 0x00,
-0x46, 0x50, 0x00, 0x15, 0x4C, 0x4D, 0x54, 0x00, 0x55, 0x52, 0x41, 0x54, 0x00, 0x55, 0x52, 0x41,
-0x53, 0x54, 0x00, 0x4F, 0x52, 0x41, 0x53, 0x54, 0x00, 0x4F, 0x52, 0x41, 0x54, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x4F, 0x52, 0x41, 0x54, 0x2D,
+0x27, 0x05, 0x19, 0x60, 0x00, 0x00, 0x00, 0x00, 0x27, 0xF5, 0x0A, 0x60, 0x00, 0x00, 0x00, 0x00,
+0x28, 0xE4, 0xFB, 0x60, 0x00, 0x00, 0x00, 0x00, 0x29, 0x78, 0xA3, 0x60, 0x00, 0x00, 0x00, 0x00,
+0x29, 0xD4, 0xDE, 0x50, 0x00, 0x00, 0x00, 0x00, 0x2A, 0xC4, 0xDD, 0x60, 0x00, 0x00, 0x00, 0x00,
+0x2B, 0xB4, 0xCE, 0x60, 0x00, 0x00, 0x00, 0x00, 0x2C, 0xA4, 0xBF, 0x60, 0x00, 0x00, 0x00, 0x00,
+0x2D, 0x94, 0xB0, 0x60, 0x00, 0x00, 0x00, 0x00, 0x2E, 0x84, 0xA1, 0x60, 0x00, 0x00, 0x00, 0x00,
+0x2F, 0x74, 0x92, 0x60, 0x00, 0x00, 0x00, 0x00, 0x30, 0x64, 0x83, 0x60, 0x00, 0x00, 0x00, 0x00,
+0x31, 0x5D, 0xAE, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x32, 0x72, 0x89, 0xE0, 0x00, 0x00, 0x00, 0x00,
+0x33, 0x3D, 0x90, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x34, 0x52, 0x6B, 0xE0, 0x00, 0x00, 0x00, 0x00,
+0x35, 0x1D, 0x72, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x36, 0x32, 0x4D, 0xE0, 0x00, 0x00, 0x00, 0x00,
+0x36, 0xFD, 0x54, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x38, 0x1B, 0x6A, 0x60, 0x00, 0x00, 0x00, 0x00,
+0x38, 0xDD, 0x36, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x39, 0xFB, 0x4C, 0x60, 0x00, 0x00, 0x00, 0x00,
+0x3A, 0xBD, 0x18, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x3B, 0xDB, 0x2E, 0x60, 0x00, 0x00, 0x00, 0x00,
+0x3C, 0xA6, 0x35, 0x60, 0x00, 0x00, 0x00, 0x00, 0x3D, 0xBB, 0x10, 0x60, 0x00, 0x00, 0x00, 0x00,
+0x3E, 0x86, 0x17, 0x60, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x9A, 0xF2, 0x60, 0x00, 0x00, 0x00, 0x00,
+0x40, 0x65, 0xF9, 0x60, 0x00, 0x00, 0x00, 0x00, 0x41, 0x84, 0x0E, 0xE0, 0x00, 0x00, 0x00, 0x00,
+0x7F, 0xFF, 0xFF, 0xFF, 0x00, 0x01, 0x02, 0x03, 0x04, 0x03, 0x02, 0x03, 0x02, 0x03, 0x05, 0x06,
+0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x07, 0x08, 0x07, 0x08, 0x07, 0x08, 0x05, 0x07, 0x08,
+0x07, 0x08, 0x07, 0x08, 0x07, 0x08, 0x07, 0x08, 0x07, 0x08, 0x07, 0x08, 0x07, 0x08, 0x07, 0x08,
+0x07, 0x08, 0x07, 0x08, 0x07, 0x08, 0x07, 0x05, 0x05, 0x00, 0x00, 0x30, 0x24, 0x00, 0x00, 0x00,
+0x00, 0x38, 0x40, 0x00, 0x04, 0x00, 0x00, 0x46, 0x50, 0x00, 0x08, 0x00, 0x00, 0x54, 0x60, 0x01,
+0x0C, 0x00, 0x00, 0x54, 0x60, 0x00, 0x0C, 0x00, 0x00, 0x46, 0x50, 0x00, 0x08, 0x00, 0x00, 0x54,
+0x60, 0x01, 0x0C, 0x00, 0x00, 0x46, 0x50, 0x01, 0x08, 0x00, 0x00, 0x38, 0x40, 0x00, 0x04, 0x00,
+0x00, 0x46, 0x50, 0x00, 0x08, 0x4C, 0x4D, 0x54, 0x00, 0x2B, 0x30, 0x34, 0x00, 0x2B, 0x30, 0x35,
+0x00, 0x2B, 0x30, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x3C, 0x2B, 0x30, 0x35, 0x3E, 0x2D,
0x35, 0x0A,
#endif
0x00, 0xD7, 0x7A, 0xC2, 0x01, 0x61, 0x03, 0x18, 0x00, 0x00, 0x00, 0x0F, 0x57, 0x65, 0x73, 0x74,
@@ -22433,36 +22516,35 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
/* Asia/Qyzylorda */
0x50, 0x48, 0x50, 0x32, 0x01, 0x4B, 0x5A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x1A, 0x80, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x10, 0x80, 0x00, 0x00, 0x00,
0xAA, 0x19, 0x86, 0xA0, 0xB5, 0xA3, 0xFD, 0x40, 0x15, 0x27, 0x8B, 0xB0, 0x16, 0x18, 0xC0, 0x20,
0x17, 0x08, 0xB1, 0x20, 0x17, 0xF9, 0xF3, 0xA0, 0x18, 0xE9, 0xF2, 0xB0, 0x19, 0xDB, 0x27, 0x20,
0x1A, 0xCC, 0x77, 0xB0, 0x1B, 0xBC, 0x84, 0xD0, 0x1C, 0xAC, 0x75, 0xD0, 0x1D, 0x9C, 0x66, 0xD0,
0x1E, 0x8C, 0x57, 0xD0, 0x1F, 0x7C, 0x48, 0xD0, 0x20, 0x6C, 0x39, 0xD0, 0x21, 0x5C, 0x2A, 0xD0,
0x22, 0x4C, 0x1B, 0xD0, 0x23, 0x3C, 0x0C, 0xD0, 0x24, 0x2B, 0xFD, 0xD0, 0x25, 0x1B, 0xEE, 0xD0,
-0x26, 0x0B, 0xDF, 0xD0, 0x27, 0x05, 0x0B, 0x50, 0x27, 0x7F, 0x8A, 0xB0, 0x29, 0x4B, 0xA6, 0x30,
-0x29, 0x78, 0x95, 0x50, 0x29, 0xD4, 0xA6, 0x10, 0x2A, 0xC4, 0x89, 0x00, 0x2B, 0xB4, 0xB2, 0x40,
-0x2C, 0xA4, 0xA3, 0x40, 0x2D, 0x94, 0x94, 0x40, 0x2E, 0x84, 0x85, 0x40, 0x2F, 0x74, 0x76, 0x40,
-0x30, 0x64, 0x67, 0x40, 0x31, 0x5D, 0x92, 0xC0, 0x32, 0x72, 0x6D, 0xC0, 0x33, 0x3D, 0x74, 0xC0,
-0x34, 0x52, 0x4F, 0xC0, 0x35, 0x1D, 0x56, 0xC0, 0x36, 0x32, 0x31, 0xC0, 0x36, 0xFD, 0x38, 0xC0,
-0x38, 0x1B, 0x4E, 0x40, 0x38, 0xDD, 0x1A, 0xC0, 0x39, 0xFB, 0x30, 0x40, 0x3A, 0xBC, 0xFC, 0xC0,
-0x3B, 0xDB, 0x12, 0x40, 0x3C, 0xA6, 0x19, 0x40, 0x3D, 0xBA, 0xF4, 0x40, 0x3E, 0x85, 0xFB, 0x40,
-0x3F, 0x9A, 0xD6, 0x40, 0x40, 0x65, 0xDD, 0x40, 0x41, 0x83, 0xF2, 0xC0, 0x42, 0x35, 0xD1, 0x20,
+0x26, 0x0B, 0xDF, 0xD0, 0x27, 0x05, 0x0B, 0x50, 0x27, 0xF4, 0xFC, 0x50, 0x28, 0xE4, 0xFB, 0x60,
+0x29, 0x78, 0x95, 0x50, 0x29, 0xD4, 0xD0, 0x40, 0x2A, 0xC4, 0xCF, 0x50, 0x2B, 0xB4, 0xC0, 0x50,
+0x2C, 0xA4, 0xB1, 0x50, 0x2D, 0x94, 0xA2, 0x50, 0x2E, 0x84, 0x93, 0x50, 0x2F, 0x74, 0x84, 0x50,
+0x30, 0x64, 0x75, 0x50, 0x31, 0x5D, 0xA0, 0xD0, 0x32, 0x72, 0x7B, 0xD0, 0x33, 0x3D, 0x82, 0xD0,
+0x34, 0x52, 0x5D, 0xD0, 0x35, 0x1D, 0x64, 0xD0, 0x36, 0x32, 0x3F, 0xD0, 0x36, 0xFD, 0x46, 0xD0,
+0x38, 0x1B, 0x5C, 0x50, 0x38, 0xDD, 0x28, 0xD0, 0x39, 0xFB, 0x3E, 0x50, 0x3A, 0xBD, 0x0A, 0xD0,
+0x3B, 0xDB, 0x20, 0x50, 0x3C, 0xA6, 0x27, 0x50, 0x3D, 0xBB, 0x02, 0x50, 0x3E, 0x86, 0x09, 0x50,
+0x3F, 0x9A, 0xE4, 0x50, 0x40, 0x65, 0xEB, 0x50, 0x41, 0x84, 0x00, 0xD0, 0x7F, 0xFF, 0xFF, 0xFF,
0x00, 0x01, 0x02, 0x03, 0x04, 0x03, 0x02, 0x03, 0x02, 0x03, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06,
-0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x02, 0x07, 0x09, 0x08, 0x09, 0x0A, 0x0B, 0x0A, 0x0B,
-0x0A, 0x0B, 0x0A, 0x0B, 0x0A, 0x0B, 0x0A, 0x0B, 0x0A, 0x0B, 0x0A, 0x0B, 0x0A, 0x0B, 0x0A, 0x0B,
-0x0A, 0x0B, 0x0A, 0x0B, 0x09, 0x00, 0x00, 0x3D, 0x60, 0x00, 0x00, 0x00, 0x00, 0x38, 0x40, 0x00,
-0x04, 0x00, 0x00, 0x46, 0x50, 0x00, 0x04, 0x00, 0x00, 0x54, 0x60, 0x01, 0x09, 0x00, 0x00, 0x54,
-0x60, 0x00, 0x04, 0x00, 0x00, 0x46, 0x50, 0x00, 0x04, 0x00, 0x00, 0x54, 0x60, 0x01, 0x09, 0x00,
-0x00, 0x46, 0x50, 0x00, 0x0F, 0x00, 0x00, 0x62, 0x70, 0x01, 0x14, 0x00, 0x00, 0x54, 0x60, 0x00,
-0x0F, 0x00, 0x00, 0x62, 0x70, 0x01, 0x14, 0x00, 0x00, 0x54, 0x60, 0x00, 0x0F, 0x4C, 0x4D, 0x54,
-0x00, 0x4B, 0x49, 0x5A, 0x54, 0x00, 0x4B, 0x49, 0x5A, 0x53, 0x54, 0x00, 0x51, 0x59, 0x5A, 0x54,
-0x00, 0x51, 0x59, 0x5A, 0x53, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00,
-0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x07, 0x05, 0x09, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05,
+0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05,
+0x06, 0x05, 0x06, 0x09, 0x09, 0x00, 0x00, 0x3D, 0x60, 0x00, 0x00, 0x00, 0x00, 0x38, 0x40, 0x00,
+0x04, 0x00, 0x00, 0x46, 0x50, 0x00, 0x08, 0x00, 0x00, 0x54, 0x60, 0x01, 0x0C, 0x00, 0x00, 0x54,
+0x60, 0x00, 0x0C, 0x00, 0x00, 0x46, 0x50, 0x00, 0x08, 0x00, 0x00, 0x54, 0x60, 0x01, 0x0C, 0x00,
+0x00, 0x46, 0x50, 0x01, 0x08, 0x00, 0x00, 0x38, 0x40, 0x00, 0x04, 0x00, 0x00, 0x54, 0x60, 0x00,
+0x0C, 0x00, 0x00, 0x54, 0x60, 0x01, 0x0C, 0x4C, 0x4D, 0x54, 0x00, 0x2B, 0x30, 0x34, 0x00, 0x2B,
+0x30, 0x35, 0x00, 0x2B, 0x30, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
#ifdef TIMELIB_SUPPORTS_V2DATA
0x54, 0x5A, 0x69, 0x66, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x1A, 0xF8, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x10, 0xF8, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xAA, 0x19, 0x86, 0xA0, 0xFF, 0xFF, 0xFF, 0xFF,
0xB5, 0xA3, 0xFD, 0x40, 0x00, 0x00, 0x00, 0x00, 0x15, 0x27, 0x8B, 0xB0, 0x00, 0x00, 0x00, 0x00,
0x16, 0x18, 0xC0, 0x20, 0x00, 0x00, 0x00, 0x00, 0x17, 0x08, 0xB1, 0x20, 0x00, 0x00, 0x00, 0x00,
@@ -22474,33 +22556,32 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x21, 0x5C, 0x2A, 0xD0, 0x00, 0x00, 0x00, 0x00, 0x22, 0x4C, 0x1B, 0xD0, 0x00, 0x00, 0x00, 0x00,
0x23, 0x3C, 0x0C, 0xD0, 0x00, 0x00, 0x00, 0x00, 0x24, 0x2B, 0xFD, 0xD0, 0x00, 0x00, 0x00, 0x00,
0x25, 0x1B, 0xEE, 0xD0, 0x00, 0x00, 0x00, 0x00, 0x26, 0x0B, 0xDF, 0xD0, 0x00, 0x00, 0x00, 0x00,
-0x27, 0x05, 0x0B, 0x50, 0x00, 0x00, 0x00, 0x00, 0x27, 0x7F, 0x8A, 0xB0, 0x00, 0x00, 0x00, 0x00,
-0x29, 0x4B, 0xA6, 0x30, 0x00, 0x00, 0x00, 0x00, 0x29, 0x78, 0x95, 0x50, 0x00, 0x00, 0x00, 0x00,
-0x29, 0xD4, 0xA6, 0x10, 0x00, 0x00, 0x00, 0x00, 0x2A, 0xC4, 0x89, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x2B, 0xB4, 0xB2, 0x40, 0x00, 0x00, 0x00, 0x00, 0x2C, 0xA4, 0xA3, 0x40, 0x00, 0x00, 0x00, 0x00,
-0x2D, 0x94, 0x94, 0x40, 0x00, 0x00, 0x00, 0x00, 0x2E, 0x84, 0x85, 0x40, 0x00, 0x00, 0x00, 0x00,
-0x2F, 0x74, 0x76, 0x40, 0x00, 0x00, 0x00, 0x00, 0x30, 0x64, 0x67, 0x40, 0x00, 0x00, 0x00, 0x00,
-0x31, 0x5D, 0x92, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x32, 0x72, 0x6D, 0xC0, 0x00, 0x00, 0x00, 0x00,
-0x33, 0x3D, 0x74, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x34, 0x52, 0x4F, 0xC0, 0x00, 0x00, 0x00, 0x00,
-0x35, 0x1D, 0x56, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x36, 0x32, 0x31, 0xC0, 0x00, 0x00, 0x00, 0x00,
-0x36, 0xFD, 0x38, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x38, 0x1B, 0x4E, 0x40, 0x00, 0x00, 0x00, 0x00,
-0x38, 0xDD, 0x1A, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x39, 0xFB, 0x30, 0x40, 0x00, 0x00, 0x00, 0x00,
-0x3A, 0xBC, 0xFC, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x3B, 0xDB, 0x12, 0x40, 0x00, 0x00, 0x00, 0x00,
-0x3C, 0xA6, 0x19, 0x40, 0x00, 0x00, 0x00, 0x00, 0x3D, 0xBA, 0xF4, 0x40, 0x00, 0x00, 0x00, 0x00,
-0x3E, 0x85, 0xFB, 0x40, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x9A, 0xD6, 0x40, 0x00, 0x00, 0x00, 0x00,
-0x40, 0x65, 0xDD, 0x40, 0x00, 0x00, 0x00, 0x00, 0x41, 0x83, 0xF2, 0xC0, 0x00, 0x00, 0x00, 0x00,
-0x42, 0x35, 0xD1, 0x20, 0x00, 0x01, 0x02, 0x03, 0x04, 0x03, 0x02, 0x03, 0x02, 0x03, 0x05, 0x06,
-0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x02, 0x07, 0x09, 0x08, 0x09,
-0x0A, 0x0B, 0x0A, 0x0B, 0x0A, 0x0B, 0x0A, 0x0B, 0x0A, 0x0B, 0x0A, 0x0B, 0x0A, 0x0B, 0x0A, 0x0B,
-0x0A, 0x0B, 0x0A, 0x0B, 0x0A, 0x0B, 0x0A, 0x0B, 0x09, 0x00, 0x00, 0x3D, 0x60, 0x00, 0x00, 0x00,
-0x00, 0x38, 0x40, 0x00, 0x04, 0x00, 0x00, 0x46, 0x50, 0x00, 0x04, 0x00, 0x00, 0x54, 0x60, 0x01,
-0x09, 0x00, 0x00, 0x54, 0x60, 0x00, 0x04, 0x00, 0x00, 0x46, 0x50, 0x00, 0x04, 0x00, 0x00, 0x54,
-0x60, 0x01, 0x09, 0x00, 0x00, 0x46, 0x50, 0x00, 0x0F, 0x00, 0x00, 0x62, 0x70, 0x01, 0x14, 0x00,
-0x00, 0x54, 0x60, 0x00, 0x0F, 0x00, 0x00, 0x62, 0x70, 0x01, 0x14, 0x00, 0x00, 0x54, 0x60, 0x00,
-0x0F, 0x4C, 0x4D, 0x54, 0x00, 0x4B, 0x49, 0x5A, 0x54, 0x00, 0x4B, 0x49, 0x5A, 0x53, 0x54, 0x00,
-0x51, 0x59, 0x5A, 0x54, 0x00, 0x51, 0x59, 0x5A, 0x53, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x0A, 0x51, 0x59, 0x5A, 0x54, 0x2D, 0x36, 0x0A,
+0x27, 0x05, 0x0B, 0x50, 0x00, 0x00, 0x00, 0x00, 0x27, 0xF4, 0xFC, 0x50, 0x00, 0x00, 0x00, 0x00,
+0x28, 0xE4, 0xFB, 0x60, 0x00, 0x00, 0x00, 0x00, 0x29, 0x78, 0x95, 0x50, 0x00, 0x00, 0x00, 0x00,
+0x29, 0xD4, 0xD0, 0x40, 0x00, 0x00, 0x00, 0x00, 0x2A, 0xC4, 0xCF, 0x50, 0x00, 0x00, 0x00, 0x00,
+0x2B, 0xB4, 0xC0, 0x50, 0x00, 0x00, 0x00, 0x00, 0x2C, 0xA4, 0xB1, 0x50, 0x00, 0x00, 0x00, 0x00,
+0x2D, 0x94, 0xA2, 0x50, 0x00, 0x00, 0x00, 0x00, 0x2E, 0x84, 0x93, 0x50, 0x00, 0x00, 0x00, 0x00,
+0x2F, 0x74, 0x84, 0x50, 0x00, 0x00, 0x00, 0x00, 0x30, 0x64, 0x75, 0x50, 0x00, 0x00, 0x00, 0x00,
+0x31, 0x5D, 0xA0, 0xD0, 0x00, 0x00, 0x00, 0x00, 0x32, 0x72, 0x7B, 0xD0, 0x00, 0x00, 0x00, 0x00,
+0x33, 0x3D, 0x82, 0xD0, 0x00, 0x00, 0x00, 0x00, 0x34, 0x52, 0x5D, 0xD0, 0x00, 0x00, 0x00, 0x00,
+0x35, 0x1D, 0x64, 0xD0, 0x00, 0x00, 0x00, 0x00, 0x36, 0x32, 0x3F, 0xD0, 0x00, 0x00, 0x00, 0x00,
+0x36, 0xFD, 0x46, 0xD0, 0x00, 0x00, 0x00, 0x00, 0x38, 0x1B, 0x5C, 0x50, 0x00, 0x00, 0x00, 0x00,
+0x38, 0xDD, 0x28, 0xD0, 0x00, 0x00, 0x00, 0x00, 0x39, 0xFB, 0x3E, 0x50, 0x00, 0x00, 0x00, 0x00,
+0x3A, 0xBD, 0x0A, 0xD0, 0x00, 0x00, 0x00, 0x00, 0x3B, 0xDB, 0x20, 0x50, 0x00, 0x00, 0x00, 0x00,
+0x3C, 0xA6, 0x27, 0x50, 0x00, 0x00, 0x00, 0x00, 0x3D, 0xBB, 0x02, 0x50, 0x00, 0x00, 0x00, 0x00,
+0x3E, 0x86, 0x09, 0x50, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x9A, 0xE4, 0x50, 0x00, 0x00, 0x00, 0x00,
+0x40, 0x65, 0xEB, 0x50, 0x00, 0x00, 0x00, 0x00, 0x41, 0x84, 0x00, 0xD0, 0x00, 0x00, 0x00, 0x00,
+0x7F, 0xFF, 0xFF, 0xFF, 0x00, 0x01, 0x02, 0x03, 0x04, 0x03, 0x02, 0x03, 0x02, 0x03, 0x05, 0x06,
+0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x07, 0x05, 0x09, 0x06, 0x05,
+0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05,
+0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x09, 0x09, 0x00, 0x00, 0x3D, 0x60, 0x00, 0x00, 0x00,
+0x00, 0x38, 0x40, 0x00, 0x04, 0x00, 0x00, 0x46, 0x50, 0x00, 0x08, 0x00, 0x00, 0x54, 0x60, 0x01,
+0x0C, 0x00, 0x00, 0x54, 0x60, 0x00, 0x0C, 0x00, 0x00, 0x46, 0x50, 0x00, 0x08, 0x00, 0x00, 0x54,
+0x60, 0x01, 0x0C, 0x00, 0x00, 0x46, 0x50, 0x01, 0x08, 0x00, 0x00, 0x38, 0x40, 0x00, 0x04, 0x00,
+0x00, 0x54, 0x60, 0x00, 0x0C, 0x00, 0x00, 0x54, 0x60, 0x01, 0x0C, 0x4C, 0x4D, 0x54, 0x00, 0x2B,
+0x30, 0x34, 0x00, 0x2B, 0x30, 0x35, 0x00, 0x2B, 0x30, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x0A, 0x3C, 0x2B, 0x30, 0x36, 0x3E, 0x2D, 0x36, 0x0A,
#endif
0x00, 0xCD, 0xB0, 0x40, 0x01, 0x76, 0x8D, 0x6A, 0x00, 0x00, 0x00, 0x1D, 0x51, 0x79, 0x7A, 0x79,
0x6C, 0x6F, 0x72, 0x64, 0x61, 0x2F, 0x4B, 0x79, 0x7A, 0x79, 0x6C, 0x6F, 0x72, 0x64, 0x61, 0x2F,
@@ -22585,7 +22666,7 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x1D, 0x9C, 0x12, 0x70, 0x1E, 0x8C, 0x03, 0x70, 0x1F, 0x7B, 0xF4, 0x70, 0x20, 0x6B, 0xE5, 0x70,
0x21, 0x5B, 0xD6, 0x70, 0x22, 0x4B, 0xC7, 0x70, 0x23, 0x3B, 0xB8, 0x70, 0x24, 0x2B, 0xA9, 0x70,
0x25, 0x1B, 0x9A, 0x70, 0x26, 0x0B, 0x8B, 0x70, 0x27, 0x04, 0xB6, 0xF0, 0x27, 0xF4, 0xA7, 0xF0,
-0x28, 0xE4, 0xA7, 0x00, 0x29, 0x78, 0x4F, 0x00, 0x29, 0xD4, 0x5F, 0xC0, 0x2A, 0xC4, 0x42, 0xB0,
+0x28, 0xE4, 0xA7, 0x00, 0x29, 0x78, 0x4F, 0x00, 0x29, 0xD4, 0x89, 0xF0, 0x2A, 0xC4, 0x7A, 0xF0,
0x2B, 0xB4, 0x6B, 0xF0, 0x2C, 0xA4, 0x5C, 0xF0, 0x2D, 0x94, 0x4D, 0xF0, 0x2E, 0x84, 0x3E, 0xF0,
0x2F, 0x74, 0x2F, 0xF0, 0x30, 0x64, 0x20, 0xF0, 0x31, 0x5D, 0x4C, 0x70, 0x32, 0x72, 0x27, 0x70,
0x33, 0x3D, 0x2E, 0x70, 0x34, 0x52, 0x17, 0x80, 0x35, 0x1D, 0x1E, 0x80, 0x36, 0x31, 0xF9, 0x80,
@@ -22597,7 +22678,7 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x49, 0xCE, 0x49, 0x80, 0x4A, 0xE3, 0x24, 0x80, 0x4B, 0xAE, 0x2B, 0x80, 0x4C, 0xCC, 0x41, 0x00,
0x4D, 0x8E, 0x0D, 0x80, 0x54, 0x4B, 0xBA, 0xF0, 0x56, 0xF6, 0xB2, 0x00, 0x00, 0x01, 0x02, 0x04,
0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05,
-0x06, 0x05, 0x06, 0x05, 0x07, 0x08, 0x05, 0x03, 0x04, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06,
+0x06, 0x05, 0x06, 0x05, 0x07, 0x08, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06,
0x05, 0x07, 0x08, 0x07, 0x08, 0x07, 0x08, 0x07, 0x08, 0x07, 0x08, 0x07, 0x08, 0x07, 0x08, 0x07,
0x08, 0x07, 0x08, 0x07, 0x08, 0x07, 0x08, 0x07, 0x08, 0x07, 0x08, 0x07, 0x08, 0x05, 0x08, 0x05,
0x00, 0x00, 0x85, 0xC8, 0x00, 0x00, 0x00, 0x00, 0x7E, 0x90, 0x00, 0x04, 0x00, 0x00, 0x7E, 0x90,
@@ -22624,8 +22705,8 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x24, 0x2B, 0xA9, 0x70, 0x00, 0x00, 0x00, 0x00, 0x25, 0x1B, 0x9A, 0x70, 0x00, 0x00, 0x00, 0x00,
0x26, 0x0B, 0x8B, 0x70, 0x00, 0x00, 0x00, 0x00, 0x27, 0x04, 0xB6, 0xF0, 0x00, 0x00, 0x00, 0x00,
0x27, 0xF4, 0xA7, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x28, 0xE4, 0xA7, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x29, 0x78, 0x4F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x29, 0xD4, 0x5F, 0xC0, 0x00, 0x00, 0x00, 0x00,
-0x2A, 0xC4, 0x42, 0xB0, 0x00, 0x00, 0x00, 0x00, 0x2B, 0xB4, 0x6B, 0xF0, 0x00, 0x00, 0x00, 0x00,
+0x29, 0x78, 0x4F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x29, 0xD4, 0x89, 0xF0, 0x00, 0x00, 0x00, 0x00,
+0x2A, 0xC4, 0x7A, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x2B, 0xB4, 0x6B, 0xF0, 0x00, 0x00, 0x00, 0x00,
0x2C, 0xA4, 0x5C, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x2D, 0x94, 0x4D, 0xF0, 0x00, 0x00, 0x00, 0x00,
0x2E, 0x84, 0x3E, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x2F, 0x74, 0x2F, 0xF0, 0x00, 0x00, 0x00, 0x00,
0x30, 0x64, 0x20, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x31, 0x5D, 0x4C, 0x70, 0x00, 0x00, 0x00, 0x00,
@@ -22646,7 +22727,7 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x4C, 0xCC, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4D, 0x8E, 0x0D, 0x80, 0x00, 0x00, 0x00, 0x00,
0x54, 0x4B, 0xBA, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x56, 0xF6, 0xB2, 0x00, 0x00, 0x01, 0x02, 0x04,
0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05,
-0x06, 0x05, 0x06, 0x05, 0x07, 0x08, 0x05, 0x03, 0x04, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06,
+0x06, 0x05, 0x06, 0x05, 0x07, 0x08, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06,
0x05, 0x07, 0x08, 0x07, 0x08, 0x07, 0x08, 0x07, 0x08, 0x07, 0x08, 0x07, 0x08, 0x07, 0x08, 0x07,
0x08, 0x07, 0x08, 0x07, 0x08, 0x07, 0x08, 0x07, 0x08, 0x07, 0x08, 0x07, 0x08, 0x05, 0x08, 0x05,
0x00, 0x00, 0x85, 0xC8, 0x00, 0x00, 0x00, 0x00, 0x7E, 0x90, 0x00, 0x04, 0x00, 0x00, 0x7E, 0x90,
@@ -22828,7 +22909,7 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x1E, 0x8C, 0x03, 0x70, 0x1F, 0x7B, 0xF4, 0x70, 0x20, 0x6B, 0xE5, 0x70, 0x21, 0x5B, 0xD6, 0x70,
0x22, 0x4B, 0xC7, 0x70, 0x23, 0x3B, 0xB8, 0x70, 0x24, 0x2B, 0xA9, 0x70, 0x25, 0x1B, 0x9A, 0x70,
0x26, 0x0B, 0x8B, 0x70, 0x27, 0x04, 0xB6, 0xF0, 0x27, 0xF4, 0xA7, 0xF0, 0x28, 0xE4, 0xA7, 0x00,
-0x29, 0x78, 0x4F, 0x00, 0x29, 0xD4, 0x5F, 0xC0, 0x2A, 0xC4, 0x42, 0xB0, 0x2B, 0xB4, 0x6B, 0xF0,
+0x29, 0x78, 0x4F, 0x00, 0x29, 0xD4, 0x89, 0xF0, 0x2A, 0xC4, 0x7A, 0xF0, 0x2B, 0xB4, 0x6B, 0xF0,
0x2C, 0xA4, 0x5C, 0xF0, 0x2D, 0x94, 0x4D, 0xF0, 0x2E, 0x84, 0x3E, 0xF0, 0x2F, 0x74, 0x2F, 0xF0,
0x30, 0x64, 0x20, 0xF0, 0x31, 0x5D, 0x4C, 0x70, 0x32, 0x72, 0x27, 0x70, 0x33, 0x3D, 0x2E, 0x70,
0x34, 0x52, 0x09, 0x70, 0x35, 0x1D, 0x10, 0x70, 0x36, 0x31, 0xEB, 0x70, 0x36, 0xFC, 0xF2, 0x70,
@@ -22839,7 +22920,7 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x47, 0x23, 0x52, 0x70, 0x47, 0xEE, 0x59, 0x70, 0x49, 0x03, 0x34, 0x70, 0x49, 0xCE, 0x3B, 0x70,
0x4A, 0xE3, 0x16, 0x70, 0x4B, 0xAE, 0x1D, 0x70, 0x4C, 0xCC, 0x32, 0xF0, 0x4D, 0x8D, 0xFF, 0x70,
0x54, 0x4B, 0xAC, 0xE0, 0x00, 0x01, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x04, 0x05,
-0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x06, 0x07, 0x04, 0x02, 0x03,
+0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x06, 0x07, 0x04, 0x05, 0x04,
0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04,
0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04,
0x05, 0x04, 0x05, 0x04, 0x08, 0x09, 0x00, 0x00, 0x90, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x8C, 0xA0,
@@ -22867,7 +22948,7 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x25, 0x1B, 0x9A, 0x70, 0x00, 0x00, 0x00, 0x00, 0x26, 0x0B, 0x8B, 0x70, 0x00, 0x00, 0x00, 0x00,
0x27, 0x04, 0xB6, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x27, 0xF4, 0xA7, 0xF0, 0x00, 0x00, 0x00, 0x00,
0x28, 0xE4, 0xA7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x29, 0x78, 0x4F, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x29, 0xD4, 0x5F, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x2A, 0xC4, 0x42, 0xB0, 0x00, 0x00, 0x00, 0x00,
+0x29, 0xD4, 0x89, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x2A, 0xC4, 0x7A, 0xF0, 0x00, 0x00, 0x00, 0x00,
0x2B, 0xB4, 0x6B, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x2C, 0xA4, 0x5C, 0xF0, 0x00, 0x00, 0x00, 0x00,
0x2D, 0x94, 0x4D, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x2E, 0x84, 0x3E, 0xF0, 0x00, 0x00, 0x00, 0x00,
0x2F, 0x74, 0x2F, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x30, 0x64, 0x20, 0xF0, 0x00, 0x00, 0x00, 0x00,
@@ -22888,7 +22969,7 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x4B, 0xAE, 0x1D, 0x70, 0x00, 0x00, 0x00, 0x00, 0x4C, 0xCC, 0x32, 0xF0, 0x00, 0x00, 0x00, 0x00,
0x4D, 0x8D, 0xFF, 0x70, 0x00, 0x00, 0x00, 0x00, 0x54, 0x4B, 0xAC, 0xE0, 0x00, 0x01, 0x03, 0x02,
0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05,
-0x04, 0x05, 0x04, 0x06, 0x07, 0x04, 0x02, 0x03, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04,
+0x04, 0x05, 0x04, 0x06, 0x07, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04,
0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04,
0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x08, 0x09, 0x00, 0x00,
0x90, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x8C, 0xA0, 0x00, 0x04, 0x00, 0x00, 0xA8, 0xC0, 0x01, 0x09,
@@ -23409,6 +23490,92 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
#endif
0x00, 0xBF, 0xBB, 0xC4, 0x01, 0xE7, 0xE4, 0x48, 0x00, 0x00, 0x00, 0x00,
+/* Asia/Tomsk */
+0x50, 0x48, 0x50, 0x32, 0x01, 0x52, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x10, 0x80, 0x00, 0x00, 0x00,
+0xA1, 0xE5, 0x4E, 0xD9, 0xB5, 0xA3, 0xE1, 0x20, 0x15, 0x27, 0x6F, 0x90, 0x16, 0x18, 0xA4, 0x00,
+0x17, 0x08, 0xA3, 0x10, 0x17, 0xF9, 0xD7, 0x80, 0x18, 0xE9, 0xD6, 0x90, 0x19, 0xDB, 0x0B, 0x00,
+0x1A, 0xCC, 0x5B, 0x90, 0x1B, 0xBC, 0x68, 0xB0, 0x1C, 0xAC, 0x59, 0xB0, 0x1D, 0x9C, 0x4A, 0xB0,
+0x1E, 0x8C, 0x3B, 0xB0, 0x1F, 0x7C, 0x2C, 0xB0, 0x20, 0x6C, 0x1D, 0xB0, 0x21, 0x5C, 0x0E, 0xB0,
+0x22, 0x4B, 0xFF, 0xB0, 0x23, 0x3B, 0xF0, 0xB0, 0x24, 0x2B, 0xE1, 0xB0, 0x25, 0x1B, 0xD2, 0xB0,
+0x26, 0x0B, 0xC3, 0xB0, 0x27, 0x04, 0xEF, 0x30, 0x27, 0xF4, 0xE0, 0x30, 0x28, 0xE4, 0xDF, 0x40,
+0x29, 0x78, 0x87, 0x40, 0x29, 0xD4, 0xC2, 0x30, 0x2A, 0xC4, 0xB3, 0x30, 0x2B, 0xB4, 0xA4, 0x30,
+0x2C, 0xA4, 0x95, 0x30, 0x2D, 0x94, 0x86, 0x30, 0x2E, 0x84, 0x77, 0x30, 0x2F, 0x74, 0x68, 0x30,
+0x30, 0x64, 0x59, 0x30, 0x31, 0x5D, 0x84, 0xB0, 0x32, 0x72, 0x5F, 0xB0, 0x33, 0x3D, 0x66, 0xB0,
+0x34, 0x52, 0x41, 0xB0, 0x35, 0x1D, 0x48, 0xB0, 0x36, 0x32, 0x23, 0xB0, 0x36, 0xFD, 0x2A, 0xB0,
+0x38, 0x1B, 0x40, 0x30, 0x38, 0xDD, 0x0C, 0xB0, 0x39, 0xFB, 0x22, 0x30, 0x3A, 0xBC, 0xEE, 0xB0,
+0x3B, 0xDB, 0x04, 0x30, 0x3C, 0xA6, 0x0B, 0x30, 0x3C, 0xCE, 0xE9, 0xB0, 0x3D, 0xBA, 0xF4, 0x40,
+0x3E, 0x85, 0xFB, 0x40, 0x3F, 0x9A, 0xD6, 0x40, 0x40, 0x65, 0xDD, 0x40, 0x41, 0x83, 0xF2, 0xC0,
+0x42, 0x45, 0xBF, 0x40, 0x43, 0x63, 0xD4, 0xC0, 0x44, 0x25, 0xA1, 0x40, 0x45, 0x43, 0xB6, 0xC0,
+0x46, 0x05, 0x83, 0x40, 0x47, 0x23, 0x98, 0xC0, 0x47, 0xEE, 0x9F, 0xC0, 0x49, 0x03, 0x7A, 0xC0,
+0x49, 0xCE, 0x81, 0xC0, 0x4A, 0xE3, 0x5C, 0xC0, 0x4B, 0xAE, 0x63, 0xC0, 0x4C, 0xCC, 0x79, 0x40,
+0x4D, 0x8E, 0x45, 0xC0, 0x54, 0x4B, 0xF3, 0x30, 0x57, 0x49, 0xF8, 0xC0, 0x7F, 0xFF, 0xFF, 0xFF,
+0x00, 0x01, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05,
+0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x06, 0x07, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04,
+0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x08,
+0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06,
+0x07, 0x04, 0x07, 0x04, 0x04, 0x00, 0x00, 0x4F, 0xA7, 0x00, 0x00, 0x00, 0x00, 0x54, 0x60, 0x00,
+0x04, 0x00, 0x00, 0x70, 0x80, 0x01, 0x08, 0x00, 0x00, 0x62, 0x70, 0x00, 0x0C, 0x00, 0x00, 0x62,
+0x70, 0x00, 0x0C, 0x00, 0x00, 0x70, 0x80, 0x01, 0x08, 0x00, 0x00, 0x62, 0x70, 0x01, 0x0C, 0x00,
+0x00, 0x54, 0x60, 0x00, 0x04, 0x00, 0x00, 0x62, 0x70, 0x01, 0x0C, 0x00, 0x00, 0x62, 0x70, 0x00,
+0x0C, 0x4C, 0x4D, 0x54, 0x00, 0x2B, 0x30, 0x36, 0x00, 0x2B, 0x30, 0x38, 0x00, 0x2B, 0x30, 0x37,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00,
+#ifdef TIMELIB_SUPPORTS_V2DATA
+0x54, 0x5A, 0x69, 0x66, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x10, 0xF8, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xA1, 0xE5, 0x4E, 0xD9, 0xFF, 0xFF, 0xFF, 0xFF,
+0xB5, 0xA3, 0xE1, 0x20, 0x00, 0x00, 0x00, 0x00, 0x15, 0x27, 0x6F, 0x90, 0x00, 0x00, 0x00, 0x00,
+0x16, 0x18, 0xA4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x08, 0xA3, 0x10, 0x00, 0x00, 0x00, 0x00,
+0x17, 0xF9, 0xD7, 0x80, 0x00, 0x00, 0x00, 0x00, 0x18, 0xE9, 0xD6, 0x90, 0x00, 0x00, 0x00, 0x00,
+0x19, 0xDB, 0x0B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1A, 0xCC, 0x5B, 0x90, 0x00, 0x00, 0x00, 0x00,
+0x1B, 0xBC, 0x68, 0xB0, 0x00, 0x00, 0x00, 0x00, 0x1C, 0xAC, 0x59, 0xB0, 0x00, 0x00, 0x00, 0x00,
+0x1D, 0x9C, 0x4A, 0xB0, 0x00, 0x00, 0x00, 0x00, 0x1E, 0x8C, 0x3B, 0xB0, 0x00, 0x00, 0x00, 0x00,
+0x1F, 0x7C, 0x2C, 0xB0, 0x00, 0x00, 0x00, 0x00, 0x20, 0x6C, 0x1D, 0xB0, 0x00, 0x00, 0x00, 0x00,
+0x21, 0x5C, 0x0E, 0xB0, 0x00, 0x00, 0x00, 0x00, 0x22, 0x4B, 0xFF, 0xB0, 0x00, 0x00, 0x00, 0x00,
+0x23, 0x3B, 0xF0, 0xB0, 0x00, 0x00, 0x00, 0x00, 0x24, 0x2B, 0xE1, 0xB0, 0x00, 0x00, 0x00, 0x00,
+0x25, 0x1B, 0xD2, 0xB0, 0x00, 0x00, 0x00, 0x00, 0x26, 0x0B, 0xC3, 0xB0, 0x00, 0x00, 0x00, 0x00,
+0x27, 0x04, 0xEF, 0x30, 0x00, 0x00, 0x00, 0x00, 0x27, 0xF4, 0xE0, 0x30, 0x00, 0x00, 0x00, 0x00,
+0x28, 0xE4, 0xDF, 0x40, 0x00, 0x00, 0x00, 0x00, 0x29, 0x78, 0x87, 0x40, 0x00, 0x00, 0x00, 0x00,
+0x29, 0xD4, 0xC2, 0x30, 0x00, 0x00, 0x00, 0x00, 0x2A, 0xC4, 0xB3, 0x30, 0x00, 0x00, 0x00, 0x00,
+0x2B, 0xB4, 0xA4, 0x30, 0x00, 0x00, 0x00, 0x00, 0x2C, 0xA4, 0x95, 0x30, 0x00, 0x00, 0x00, 0x00,
+0x2D, 0x94, 0x86, 0x30, 0x00, 0x00, 0x00, 0x00, 0x2E, 0x84, 0x77, 0x30, 0x00, 0x00, 0x00, 0x00,
+0x2F, 0x74, 0x68, 0x30, 0x00, 0x00, 0x00, 0x00, 0x30, 0x64, 0x59, 0x30, 0x00, 0x00, 0x00, 0x00,
+0x31, 0x5D, 0x84, 0xB0, 0x00, 0x00, 0x00, 0x00, 0x32, 0x72, 0x5F, 0xB0, 0x00, 0x00, 0x00, 0x00,
+0x33, 0x3D, 0x66, 0xB0, 0x00, 0x00, 0x00, 0x00, 0x34, 0x52, 0x41, 0xB0, 0x00, 0x00, 0x00, 0x00,
+0x35, 0x1D, 0x48, 0xB0, 0x00, 0x00, 0x00, 0x00, 0x36, 0x32, 0x23, 0xB0, 0x00, 0x00, 0x00, 0x00,
+0x36, 0xFD, 0x2A, 0xB0, 0x00, 0x00, 0x00, 0x00, 0x38, 0x1B, 0x40, 0x30, 0x00, 0x00, 0x00, 0x00,
+0x38, 0xDD, 0x0C, 0xB0, 0x00, 0x00, 0x00, 0x00, 0x39, 0xFB, 0x22, 0x30, 0x00, 0x00, 0x00, 0x00,
+0x3A, 0xBC, 0xEE, 0xB0, 0x00, 0x00, 0x00, 0x00, 0x3B, 0xDB, 0x04, 0x30, 0x00, 0x00, 0x00, 0x00,
+0x3C, 0xA6, 0x0B, 0x30, 0x00, 0x00, 0x00, 0x00, 0x3C, 0xCE, 0xE9, 0xB0, 0x00, 0x00, 0x00, 0x00,
+0x3D, 0xBA, 0xF4, 0x40, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x85, 0xFB, 0x40, 0x00, 0x00, 0x00, 0x00,
+0x3F, 0x9A, 0xD6, 0x40, 0x00, 0x00, 0x00, 0x00, 0x40, 0x65, 0xDD, 0x40, 0x00, 0x00, 0x00, 0x00,
+0x41, 0x83, 0xF2, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x42, 0x45, 0xBF, 0x40, 0x00, 0x00, 0x00, 0x00,
+0x43, 0x63, 0xD4, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x44, 0x25, 0xA1, 0x40, 0x00, 0x00, 0x00, 0x00,
+0x45, 0x43, 0xB6, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x46, 0x05, 0x83, 0x40, 0x00, 0x00, 0x00, 0x00,
+0x47, 0x23, 0x98, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x47, 0xEE, 0x9F, 0xC0, 0x00, 0x00, 0x00, 0x00,
+0x49, 0x03, 0x7A, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x49, 0xCE, 0x81, 0xC0, 0x00, 0x00, 0x00, 0x00,
+0x4A, 0xE3, 0x5C, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x4B, 0xAE, 0x63, 0xC0, 0x00, 0x00, 0x00, 0x00,
+0x4C, 0xCC, 0x79, 0x40, 0x00, 0x00, 0x00, 0x00, 0x4D, 0x8E, 0x45, 0xC0, 0x00, 0x00, 0x00, 0x00,
+0x54, 0x4B, 0xF3, 0x30, 0x00, 0x00, 0x00, 0x00, 0x57, 0x49, 0xF8, 0xC0, 0x00, 0x00, 0x00, 0x00,
+0x7F, 0xFF, 0xFF, 0xFF, 0x00, 0x01, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x04, 0x05,
+0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x06, 0x07, 0x04, 0x05, 0x04,
+0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04,
+0x05, 0x04, 0x05, 0x08, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06,
+0x07, 0x06, 0x07, 0x06, 0x07, 0x04, 0x07, 0x04, 0x04, 0x00, 0x00, 0x4F, 0xA7, 0x00, 0x00, 0x00,
+0x00, 0x54, 0x60, 0x00, 0x04, 0x00, 0x00, 0x70, 0x80, 0x01, 0x08, 0x00, 0x00, 0x62, 0x70, 0x00,
+0x0C, 0x00, 0x00, 0x62, 0x70, 0x00, 0x0C, 0x00, 0x00, 0x70, 0x80, 0x01, 0x08, 0x00, 0x00, 0x62,
+0x70, 0x01, 0x0C, 0x00, 0x00, 0x54, 0x60, 0x00, 0x04, 0x00, 0x00, 0x62, 0x70, 0x01, 0x0C, 0x00,
+0x00, 0x62, 0x70, 0x00, 0x0C, 0x4C, 0x4D, 0x54, 0x00, 0x2B, 0x30, 0x36, 0x00, 0x2B, 0x30, 0x38,
+0x00, 0x2B, 0x30, 0x37, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x01, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x3C, 0x2B, 0x30, 0x37, 0x3E, 0x2D,
+0x37, 0x0A,
+#endif
+0x00, 0xDF, 0x8A, 0x90, 0x01, 0x94, 0x4E, 0x9A, 0x00, 0x00, 0x00, 0x0E, 0x4D, 0x53, 0x4B, 0x2B,
+0x30, 0x34, 0x20, 0x2D, 0x20, 0x54, 0x6F, 0x6D, 0x73, 0x6B,
+
/* Asia/Ujung_Pandang */
0x50, 0x48, 0x50, 0x32, 0x00, 0x3F, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00,
@@ -23661,7 +23828,7 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x1E, 0x8C, 0x03, 0x70, 0x1F, 0x7B, 0xF4, 0x70, 0x20, 0x6B, 0xE5, 0x70, 0x21, 0x5B, 0xD6, 0x70,
0x22, 0x4B, 0xC7, 0x70, 0x23, 0x3B, 0xB8, 0x70, 0x24, 0x2B, 0xA9, 0x70, 0x25, 0x1B, 0x9A, 0x70,
0x26, 0x0B, 0x8B, 0x70, 0x27, 0x04, 0xB6, 0xF0, 0x27, 0xF4, 0xA7, 0xF0, 0x28, 0xE4, 0xA7, 0x00,
-0x29, 0x78, 0x4F, 0x00, 0x29, 0xD4, 0x5F, 0xC0, 0x2A, 0xC4, 0x42, 0xB0, 0x2B, 0xB4, 0x6B, 0xF0,
+0x29, 0x78, 0x4F, 0x00, 0x29, 0xD4, 0x89, 0xF0, 0x2A, 0xC4, 0x7A, 0xF0, 0x2B, 0xB4, 0x6B, 0xF0,
0x2C, 0xA4, 0x5C, 0xF0, 0x2D, 0x94, 0x4D, 0xF0, 0x2E, 0x84, 0x3E, 0xF0, 0x2F, 0x74, 0x2F, 0xF0,
0x30, 0x64, 0x20, 0xF0, 0x31, 0x5D, 0x4C, 0x70, 0x32, 0x72, 0x27, 0x70, 0x33, 0x3D, 0x2E, 0x70,
0x34, 0x52, 0x09, 0x70, 0x35, 0x1D, 0x10, 0x70, 0x36, 0x31, 0xEB, 0x70, 0x36, 0xFC, 0xF2, 0x70,
@@ -23673,7 +23840,7 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x4A, 0xE3, 0x16, 0x70, 0x4B, 0xAE, 0x1D, 0x70, 0x4C, 0xCC, 0x32, 0xF0, 0x4D, 0x8D, 0xFF, 0x70,
0x4E, 0x6D, 0xF4, 0x40, 0x54, 0x4B, 0xBA, 0xF0, 0x00, 0x01, 0x02, 0x04, 0x03, 0x04, 0x03, 0x04,
0x03, 0x04, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x07,
-0x08, 0x05, 0x04, 0x03, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05,
+0x08, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05,
0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05,
0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x09, 0x0A, 0x0B, 0x00, 0x00, 0x86, 0x46, 0x00,
0x00, 0x00, 0x00, 0x70, 0x80, 0x00, 0x04, 0x00, 0x00, 0x7E, 0x90, 0x00, 0x04, 0x00, 0x00, 0x9A,
@@ -23701,7 +23868,7 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x25, 0x1B, 0x9A, 0x70, 0x00, 0x00, 0x00, 0x00, 0x26, 0x0B, 0x8B, 0x70, 0x00, 0x00, 0x00, 0x00,
0x27, 0x04, 0xB6, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x27, 0xF4, 0xA7, 0xF0, 0x00, 0x00, 0x00, 0x00,
0x28, 0xE4, 0xA7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x29, 0x78, 0x4F, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x29, 0xD4, 0x5F, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x2A, 0xC4, 0x42, 0xB0, 0x00, 0x00, 0x00, 0x00,
+0x29, 0xD4, 0x89, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x2A, 0xC4, 0x7A, 0xF0, 0x00, 0x00, 0x00, 0x00,
0x2B, 0xB4, 0x6B, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x2C, 0xA4, 0x5C, 0xF0, 0x00, 0x00, 0x00, 0x00,
0x2D, 0x94, 0x4D, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x2E, 0x84, 0x3E, 0xF0, 0x00, 0x00, 0x00, 0x00,
0x2F, 0x74, 0x2F, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x30, 0x64, 0x20, 0xF0, 0x00, 0x00, 0x00, 0x00,
@@ -23722,7 +23889,7 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x4B, 0xAE, 0x1D, 0x70, 0x00, 0x00, 0x00, 0x00, 0x4C, 0xCC, 0x32, 0xF0, 0x00, 0x00, 0x00, 0x00,
0x4D, 0x8D, 0xFF, 0x70, 0x00, 0x00, 0x00, 0x00, 0x4E, 0x6D, 0xF4, 0x40, 0x00, 0x00, 0x00, 0x00,
0x54, 0x4B, 0xBA, 0xF0, 0x00, 0x01, 0x02, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x05, 0x06,
-0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x07, 0x08, 0x05, 0x04, 0x03,
+0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x07, 0x08, 0x05, 0x06, 0x05,
0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05,
0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05,
0x06, 0x05, 0x06, 0x05, 0x09, 0x0A, 0x0B, 0x00, 0x00, 0x86, 0x46, 0x00, 0x00, 0x00, 0x00, 0x70,
@@ -23769,7 +23936,7 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x1E, 0x8C, 0x11, 0x80, 0x1F, 0x7C, 0x02, 0x80, 0x20, 0x6B, 0xF3, 0x80, 0x21, 0x5B, 0xE4, 0x80,
0x22, 0x4B, 0xD5, 0x80, 0x23, 0x3B, 0xC6, 0x80, 0x24, 0x2B, 0xB7, 0x80, 0x25, 0x1B, 0xA8, 0x80,
0x26, 0x0B, 0x99, 0x80, 0x27, 0x04, 0xC5, 0x00, 0x27, 0xF4, 0xB6, 0x00, 0x28, 0xE4, 0xB5, 0x10,
-0x29, 0x78, 0x5D, 0x10, 0x29, 0xD4, 0x6D, 0xD0, 0x2A, 0xC4, 0x50, 0xC0, 0x2B, 0xB4, 0x7A, 0x00,
+0x29, 0x78, 0x5D, 0x10, 0x29, 0xD4, 0x98, 0x00, 0x2A, 0xC4, 0x89, 0x00, 0x2B, 0xB4, 0x7A, 0x00,
0x2C, 0xA4, 0x6B, 0x00, 0x2D, 0x94, 0x5C, 0x00, 0x2E, 0x84, 0x4D, 0x00, 0x2F, 0x74, 0x3E, 0x00,
0x30, 0x64, 0x2F, 0x00, 0x31, 0x5D, 0x5A, 0x80, 0x32, 0x72, 0x35, 0x80, 0x33, 0x3D, 0x3C, 0x80,
0x34, 0x52, 0x17, 0x80, 0x35, 0x1D, 0x1E, 0x80, 0x36, 0x31, 0xF9, 0x80, 0x36, 0xFD, 0x00, 0x80,
@@ -23780,7 +23947,7 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x47, 0x23, 0x60, 0x80, 0x47, 0xEE, 0x67, 0x80, 0x49, 0x03, 0x42, 0x80, 0x49, 0xCE, 0x49, 0x80,
0x4A, 0xE3, 0x24, 0x80, 0x4B, 0xAE, 0x2B, 0x80, 0x4C, 0xCC, 0x41, 0x00, 0x4D, 0x8E, 0x0D, 0x80,
0x54, 0x4B, 0xBA, 0xF0, 0x00, 0x01, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x04, 0x05,
-0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x06, 0x07, 0x04, 0x02, 0x03,
+0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x06, 0x07, 0x04, 0x05, 0x04,
0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04,
0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04,
0x05, 0x04, 0x05, 0x04, 0x08, 0x04, 0x00, 0x00, 0x7B, 0xA3, 0x00, 0x00, 0x00, 0x00, 0x7E, 0x90,
@@ -23807,7 +23974,7 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x25, 0x1B, 0xA8, 0x80, 0x00, 0x00, 0x00, 0x00, 0x26, 0x0B, 0x99, 0x80, 0x00, 0x00, 0x00, 0x00,
0x27, 0x04, 0xC5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x27, 0xF4, 0xB6, 0x00, 0x00, 0x00, 0x00, 0x00,
0x28, 0xE4, 0xB5, 0x10, 0x00, 0x00, 0x00, 0x00, 0x29, 0x78, 0x5D, 0x10, 0x00, 0x00, 0x00, 0x00,
-0x29, 0xD4, 0x6D, 0xD0, 0x00, 0x00, 0x00, 0x00, 0x2A, 0xC4, 0x50, 0xC0, 0x00, 0x00, 0x00, 0x00,
+0x29, 0xD4, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2A, 0xC4, 0x89, 0x00, 0x00, 0x00, 0x00, 0x00,
0x2B, 0xB4, 0x7A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2C, 0xA4, 0x6B, 0x00, 0x00, 0x00, 0x00, 0x00,
0x2D, 0x94, 0x5C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2E, 0x84, 0x4D, 0x00, 0x00, 0x00, 0x00, 0x00,
0x2F, 0x74, 0x3E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x64, 0x2F, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -23828,7 +23995,7 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x4B, 0xAE, 0x2B, 0x80, 0x00, 0x00, 0x00, 0x00, 0x4C, 0xCC, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00,
0x4D, 0x8E, 0x0D, 0x80, 0x00, 0x00, 0x00, 0x00, 0x54, 0x4B, 0xBA, 0xF0, 0x00, 0x01, 0x03, 0x02,
0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05,
-0x04, 0x05, 0x04, 0x06, 0x07, 0x04, 0x02, 0x03, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04,
+0x04, 0x05, 0x04, 0x06, 0x07, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04,
0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04,
0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x08, 0x04, 0x00, 0x00,
0x7B, 0xA3, 0x00, 0x00, 0x00, 0x00, 0x7E, 0x90, 0x00, 0x04, 0x00, 0x00, 0x9A, 0xB0, 0x01, 0x09,
@@ -23852,7 +24019,7 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x1E, 0x8C, 0x1F, 0x90, 0x1F, 0x7C, 0x10, 0x90, 0x20, 0x6C, 0x01, 0x90, 0x21, 0x5B, 0xF2, 0x90,
0x22, 0x4B, 0xE3, 0x90, 0x23, 0x3B, 0xD4, 0x90, 0x24, 0x2B, 0xC5, 0x90, 0x25, 0x1B, 0xB6, 0x90,
0x26, 0x0B, 0xA7, 0x90, 0x27, 0x04, 0xD3, 0x10, 0x27, 0xF4, 0xC4, 0x10, 0x28, 0xE4, 0xC3, 0x20,
-0x29, 0x78, 0x6B, 0x20, 0x29, 0xD4, 0x7B, 0xE0, 0x2A, 0xC4, 0x5E, 0xD0, 0x2B, 0xB4, 0x88, 0x10,
+0x29, 0x78, 0x6B, 0x20, 0x29, 0xD4, 0xA6, 0x10, 0x2A, 0xC4, 0x97, 0x10, 0x2B, 0xB4, 0x88, 0x10,
0x2C, 0xA4, 0x79, 0x10, 0x2D, 0x94, 0x6A, 0x10, 0x2E, 0x84, 0x5B, 0x10, 0x2F, 0x74, 0x4C, 0x10,
0x30, 0x64, 0x3D, 0x10, 0x31, 0x5D, 0x68, 0x90, 0x32, 0x72, 0x43, 0x90, 0x33, 0x3D, 0x4A, 0x90,
0x34, 0x52, 0x25, 0x90, 0x35, 0x1D, 0x2C, 0x90, 0x36, 0x32, 0x07, 0x90, 0x36, 0xFD, 0x0E, 0x90,
@@ -23863,7 +24030,7 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x47, 0x23, 0x6E, 0x90, 0x47, 0xEE, 0x75, 0x90, 0x49, 0x03, 0x50, 0x90, 0x49, 0xCE, 0x57, 0x90,
0x4A, 0xE3, 0x32, 0x90, 0x4B, 0xAE, 0x39, 0x90, 0x4C, 0xCC, 0x4F, 0x10, 0x4D, 0x8E, 0x1B, 0x90,
0x54, 0x4B, 0xC9, 0x00, 0x00, 0x01, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x04, 0x05,
-0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x06, 0x07, 0x04, 0x02, 0x03,
+0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x06, 0x07, 0x04, 0x05, 0x04,
0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04,
0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04,
0x05, 0x04, 0x05, 0x04, 0x08, 0x04, 0x00, 0x00, 0x79, 0xA2, 0x00, 0x00, 0x00, 0x00, 0x70, 0x80,
@@ -23890,7 +24057,7 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x25, 0x1B, 0xB6, 0x90, 0x00, 0x00, 0x00, 0x00, 0x26, 0x0B, 0xA7, 0x90, 0x00, 0x00, 0x00, 0x00,
0x27, 0x04, 0xD3, 0x10, 0x00, 0x00, 0x00, 0x00, 0x27, 0xF4, 0xC4, 0x10, 0x00, 0x00, 0x00, 0x00,
0x28, 0xE4, 0xC3, 0x20, 0x00, 0x00, 0x00, 0x00, 0x29, 0x78, 0x6B, 0x20, 0x00, 0x00, 0x00, 0x00,
-0x29, 0xD4, 0x7B, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x2A, 0xC4, 0x5E, 0xD0, 0x00, 0x00, 0x00, 0x00,
+0x29, 0xD4, 0xA6, 0x10, 0x00, 0x00, 0x00, 0x00, 0x2A, 0xC4, 0x97, 0x10, 0x00, 0x00, 0x00, 0x00,
0x2B, 0xB4, 0x88, 0x10, 0x00, 0x00, 0x00, 0x00, 0x2C, 0xA4, 0x79, 0x10, 0x00, 0x00, 0x00, 0x00,
0x2D, 0x94, 0x6A, 0x10, 0x00, 0x00, 0x00, 0x00, 0x2E, 0x84, 0x5B, 0x10, 0x00, 0x00, 0x00, 0x00,
0x2F, 0x74, 0x4C, 0x10, 0x00, 0x00, 0x00, 0x00, 0x30, 0x64, 0x3D, 0x10, 0x00, 0x00, 0x00, 0x00,
@@ -23911,7 +24078,7 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x4B, 0xAE, 0x39, 0x90, 0x00, 0x00, 0x00, 0x00, 0x4C, 0xCC, 0x4F, 0x10, 0x00, 0x00, 0x00, 0x00,
0x4D, 0x8E, 0x1B, 0x90, 0x00, 0x00, 0x00, 0x00, 0x54, 0x4B, 0xC9, 0x00, 0x00, 0x01, 0x03, 0x02,
0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05,
-0x04, 0x05, 0x04, 0x06, 0x07, 0x04, 0x02, 0x03, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04,
+0x04, 0x05, 0x04, 0x06, 0x07, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04,
0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04,
0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x08, 0x04, 0x00, 0x00,
0x79, 0xA2, 0x00, 0x00, 0x00, 0x00, 0x70, 0x80, 0x00, 0x04, 0x00, 0x00, 0x8C, 0xA0, 0x01, 0x09,
@@ -23927,15 +24094,15 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
/* Asia/Yekaterinburg */
0x50, 0x48, 0x50, 0x32, 0x01, 0x52, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x43, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x1E, 0x80, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x43, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x1E, 0x80, 0x00, 0x00, 0x00,
0x9B, 0x5F, 0x09, 0x27, 0xA1, 0x12, 0xB1, 0xFF, 0xB5, 0xA3, 0xFD, 0x40, 0x15, 0x27, 0x8B, 0xB0,
0x16, 0x18, 0xC0, 0x20, 0x17, 0x08, 0xBF, 0x30, 0x17, 0xF9, 0xF3, 0xA0, 0x18, 0xE9, 0xF2, 0xB0,
0x19, 0xDB, 0x27, 0x20, 0x1A, 0xCC, 0x77, 0xB0, 0x1B, 0xBC, 0x84, 0xD0, 0x1C, 0xAC, 0x75, 0xD0,
0x1D, 0x9C, 0x66, 0xD0, 0x1E, 0x8C, 0x57, 0xD0, 0x1F, 0x7C, 0x48, 0xD0, 0x20, 0x6C, 0x39, 0xD0,
0x21, 0x5C, 0x2A, 0xD0, 0x22, 0x4C, 0x1B, 0xD0, 0x23, 0x3C, 0x0C, 0xD0, 0x24, 0x2B, 0xFD, 0xD0,
0x25, 0x1B, 0xEE, 0xD0, 0x26, 0x0B, 0xDF, 0xD0, 0x27, 0x05, 0x0B, 0x50, 0x27, 0xF4, 0xFC, 0x50,
-0x28, 0xE4, 0xFB, 0x60, 0x29, 0x78, 0xA3, 0x60, 0x29, 0xD4, 0xB4, 0x20, 0x2A, 0xC4, 0x97, 0x10,
+0x28, 0xE4, 0xFB, 0x60, 0x29, 0x78, 0xA3, 0x60, 0x29, 0xD4, 0xDE, 0x50, 0x2A, 0xC4, 0xCF, 0x50,
0x2B, 0xB4, 0xC0, 0x50, 0x2C, 0xA4, 0xB1, 0x50, 0x2D, 0x94, 0xA2, 0x50, 0x2E, 0x84, 0x93, 0x50,
0x2F, 0x74, 0x84, 0x50, 0x30, 0x64, 0x75, 0x50, 0x31, 0x5D, 0xA0, 0xD0, 0x32, 0x72, 0x7B, 0xD0,
0x33, 0x3D, 0x82, 0xD0, 0x34, 0x52, 0x5D, 0xD0, 0x35, 0x1D, 0x64, 0xD0, 0x36, 0x32, 0x3F, 0xD0,
@@ -23947,23 +24114,22 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x49, 0xCE, 0x8F, 0xD0, 0x4A, 0xE3, 0x6A, 0xD0, 0x4B, 0xAE, 0x71, 0xD0, 0x4C, 0xCC, 0x87, 0x50,
0x4D, 0x8E, 0x53, 0xD0, 0x54, 0x4C, 0x01, 0x40, 0x00, 0x01, 0x02, 0x04, 0x03, 0x04, 0x03, 0x04,
0x03, 0x04, 0x03, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05,
-0x07, 0x08, 0x0C, 0x09, 0x0A, 0x0B, 0x0C, 0x0B, 0x0C, 0x0B, 0x0C, 0x0B, 0x0C, 0x0B, 0x0C, 0x0B,
-0x0C, 0x0B, 0x0C, 0x0B, 0x0C, 0x0B, 0x0C, 0x0B, 0x0C, 0x0B, 0x0C, 0x0B, 0x0C, 0x0B, 0x0C, 0x0B,
-0x0C, 0x0B, 0x0C, 0x0B, 0x0C, 0x0B, 0x0C, 0x0B, 0x0C, 0x0D, 0x0C, 0x00, 0x00, 0x38, 0xD9, 0x00,
+0x07, 0x08, 0x0A, 0x09, 0x0A, 0x09, 0x0A, 0x09, 0x0A, 0x09, 0x0A, 0x09, 0x0A, 0x09, 0x0A, 0x09,
+0x0A, 0x09, 0x0A, 0x09, 0x0A, 0x09, 0x0A, 0x09, 0x0A, 0x09, 0x0A, 0x09, 0x0A, 0x09, 0x0A, 0x09,
+0x0A, 0x09, 0x0A, 0x09, 0x0A, 0x09, 0x0A, 0x09, 0x0A, 0x0B, 0x0A, 0x00, 0x00, 0x38, 0xD9, 0x00,
0x00, 0x00, 0x00, 0x34, 0xC1, 0x00, 0x04, 0x00, 0x00, 0x38, 0x40, 0x00, 0x08, 0x00, 0x00, 0x54,
0x60, 0x01, 0x0D, 0x00, 0x00, 0x46, 0x50, 0x00, 0x08, 0x00, 0x00, 0x46, 0x50, 0x00, 0x08, 0x00,
0x00, 0x54, 0x60, 0x01, 0x0D, 0x00, 0x00, 0x46, 0x50, 0x01, 0x0D, 0x00, 0x00, 0x38, 0x40, 0x00,
0x08, 0x00, 0x00, 0x54, 0x60, 0x01, 0x13, 0x00, 0x00, 0x46, 0x50, 0x00, 0x19, 0x00, 0x00, 0x54,
-0x60, 0x01, 0x13, 0x00, 0x00, 0x46, 0x50, 0x00, 0x19, 0x00, 0x00, 0x54, 0x60, 0x00, 0x19, 0x00,
-0x00, 0x46, 0x50, 0x00, 0x19, 0x4C, 0x4D, 0x54, 0x00, 0x50, 0x4D, 0x54, 0x00, 0x53, 0x56, 0x45,
-0x54, 0x00, 0x53, 0x56, 0x45, 0x53, 0x54, 0x00, 0x59, 0x45, 0x4B, 0x53, 0x54, 0x00, 0x59, 0x45,
-0x4B, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01,
-0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x60, 0x00, 0x19, 0x00, 0x00, 0x46, 0x50, 0x00, 0x19, 0x4C, 0x4D, 0x54, 0x00, 0x50, 0x4D, 0x54,
+0x00, 0x53, 0x56, 0x45, 0x54, 0x00, 0x53, 0x56, 0x45, 0x53, 0x54, 0x00, 0x59, 0x45, 0x4B, 0x53,
+0x54, 0x00, 0x59, 0x45, 0x4B, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00,
#ifdef TIMELIB_SUPPORTS_V2DATA
0x54, 0x5A, 0x69, 0x66, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x43, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x1E, 0xF8, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x43, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x1E, 0xF8, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x9B, 0x5F, 0x09, 0x27, 0xFF, 0xFF, 0xFF, 0xFF,
0xA1, 0x12, 0xB1, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xB5, 0xA3, 0xFD, 0x40, 0x00, 0x00, 0x00, 0x00,
0x15, 0x27, 0x8B, 0xB0, 0x00, 0x00, 0x00, 0x00, 0x16, 0x18, 0xC0, 0x20, 0x00, 0x00, 0x00, 0x00,
@@ -23977,8 +24143,8 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x24, 0x2B, 0xFD, 0xD0, 0x00, 0x00, 0x00, 0x00, 0x25, 0x1B, 0xEE, 0xD0, 0x00, 0x00, 0x00, 0x00,
0x26, 0x0B, 0xDF, 0xD0, 0x00, 0x00, 0x00, 0x00, 0x27, 0x05, 0x0B, 0x50, 0x00, 0x00, 0x00, 0x00,
0x27, 0xF4, 0xFC, 0x50, 0x00, 0x00, 0x00, 0x00, 0x28, 0xE4, 0xFB, 0x60, 0x00, 0x00, 0x00, 0x00,
-0x29, 0x78, 0xA3, 0x60, 0x00, 0x00, 0x00, 0x00, 0x29, 0xD4, 0xB4, 0x20, 0x00, 0x00, 0x00, 0x00,
-0x2A, 0xC4, 0x97, 0x10, 0x00, 0x00, 0x00, 0x00, 0x2B, 0xB4, 0xC0, 0x50, 0x00, 0x00, 0x00, 0x00,
+0x29, 0x78, 0xA3, 0x60, 0x00, 0x00, 0x00, 0x00, 0x29, 0xD4, 0xDE, 0x50, 0x00, 0x00, 0x00, 0x00,
+0x2A, 0xC4, 0xCF, 0x50, 0x00, 0x00, 0x00, 0x00, 0x2B, 0xB4, 0xC0, 0x50, 0x00, 0x00, 0x00, 0x00,
0x2C, 0xA4, 0xB1, 0x50, 0x00, 0x00, 0x00, 0x00, 0x2D, 0x94, 0xA2, 0x50, 0x00, 0x00, 0x00, 0x00,
0x2E, 0x84, 0x93, 0x50, 0x00, 0x00, 0x00, 0x00, 0x2F, 0x74, 0x84, 0x50, 0x00, 0x00, 0x00, 0x00,
0x30, 0x64, 0x75, 0x50, 0x00, 0x00, 0x00, 0x00, 0x31, 0x5D, 0xA0, 0xD0, 0x00, 0x00, 0x00, 0x00,
@@ -23998,18 +24164,17 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x4A, 0xE3, 0x6A, 0xD0, 0x00, 0x00, 0x00, 0x00, 0x4B, 0xAE, 0x71, 0xD0, 0x00, 0x00, 0x00, 0x00,
0x4C, 0xCC, 0x87, 0x50, 0x00, 0x00, 0x00, 0x00, 0x4D, 0x8E, 0x53, 0xD0, 0x00, 0x00, 0x00, 0x00,
0x54, 0x4C, 0x01, 0x40, 0x00, 0x01, 0x02, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x05,
-0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x07, 0x08, 0x0C, 0x09,
-0x0A, 0x0B, 0x0C, 0x0B, 0x0C, 0x0B, 0x0C, 0x0B, 0x0C, 0x0B, 0x0C, 0x0B, 0x0C, 0x0B, 0x0C, 0x0B,
-0x0C, 0x0B, 0x0C, 0x0B, 0x0C, 0x0B, 0x0C, 0x0B, 0x0C, 0x0B, 0x0C, 0x0B, 0x0C, 0x0B, 0x0C, 0x0B,
-0x0C, 0x0B, 0x0C, 0x0B, 0x0C, 0x0D, 0x0C, 0x00, 0x00, 0x38, 0xD9, 0x00, 0x00, 0x00, 0x00, 0x34,
+0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x07, 0x08, 0x0A, 0x09,
+0x0A, 0x09, 0x0A, 0x09, 0x0A, 0x09, 0x0A, 0x09, 0x0A, 0x09, 0x0A, 0x09, 0x0A, 0x09, 0x0A, 0x09,
+0x0A, 0x09, 0x0A, 0x09, 0x0A, 0x09, 0x0A, 0x09, 0x0A, 0x09, 0x0A, 0x09, 0x0A, 0x09, 0x0A, 0x09,
+0x0A, 0x09, 0x0A, 0x09, 0x0A, 0x0B, 0x0A, 0x00, 0x00, 0x38, 0xD9, 0x00, 0x00, 0x00, 0x00, 0x34,
0xC1, 0x00, 0x04, 0x00, 0x00, 0x38, 0x40, 0x00, 0x08, 0x00, 0x00, 0x54, 0x60, 0x01, 0x0D, 0x00,
0x00, 0x46, 0x50, 0x00, 0x08, 0x00, 0x00, 0x46, 0x50, 0x00, 0x08, 0x00, 0x00, 0x54, 0x60, 0x01,
0x0D, 0x00, 0x00, 0x46, 0x50, 0x01, 0x0D, 0x00, 0x00, 0x38, 0x40, 0x00, 0x08, 0x00, 0x00, 0x54,
-0x60, 0x01, 0x13, 0x00, 0x00, 0x46, 0x50, 0x00, 0x19, 0x00, 0x00, 0x54, 0x60, 0x01, 0x13, 0x00,
-0x00, 0x46, 0x50, 0x00, 0x19, 0x00, 0x00, 0x54, 0x60, 0x00, 0x19, 0x00, 0x00, 0x46, 0x50, 0x00,
-0x19, 0x4C, 0x4D, 0x54, 0x00, 0x50, 0x4D, 0x54, 0x00, 0x53, 0x56, 0x45, 0x54, 0x00, 0x53, 0x56,
-0x45, 0x53, 0x54, 0x00, 0x59, 0x45, 0x4B, 0x53, 0x54, 0x00, 0x59, 0x45, 0x4B, 0x54, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00,
+0x60, 0x01, 0x13, 0x00, 0x00, 0x46, 0x50, 0x00, 0x19, 0x00, 0x00, 0x54, 0x60, 0x00, 0x19, 0x00,
+0x00, 0x46, 0x50, 0x00, 0x19, 0x4C, 0x4D, 0x54, 0x00, 0x50, 0x4D, 0x54, 0x00, 0x53, 0x56, 0x45,
+0x54, 0x00, 0x53, 0x56, 0x45, 0x53, 0x54, 0x00, 0x59, 0x45, 0x4B, 0x53, 0x54, 0x00, 0x59, 0x45,
+0x4B, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x59, 0x45,
0x4B, 0x54, 0x2D, 0x35, 0x0A,
#endif
@@ -24018,15 +24183,15 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
/* Asia/Yerevan */
0x50, 0x48, 0x50, 0x32, 0x01, 0x41, 0x4D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x18, 0x80, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x18, 0x80, 0x00, 0x00, 0x00,
0xAA, 0x19, 0x9A, 0x48, 0xE7, 0xDA, 0x0C, 0x50, 0x15, 0x27, 0x99, 0xC0, 0x16, 0x18, 0xCE, 0x30,
0x17, 0x08, 0xCD, 0x40, 0x17, 0xFA, 0x01, 0xB0, 0x18, 0xEA, 0x00, 0xC0, 0x19, 0xDB, 0x35, 0x30,
0x1A, 0xCC, 0x85, 0xC0, 0x1B, 0xBC, 0x92, 0xE0, 0x1C, 0xAC, 0x83, 0xE0, 0x1D, 0x9C, 0x74, 0xE0,
0x1E, 0x8C, 0x65, 0xE0, 0x1F, 0x7C, 0x56, 0xE0, 0x20, 0x6C, 0x47, 0xE0, 0x21, 0x5C, 0x38, 0xE0,
0x22, 0x4C, 0x29, 0xE0, 0x23, 0x3C, 0x1A, 0xE0, 0x24, 0x2C, 0x0B, 0xE0, 0x25, 0x1B, 0xFC, 0xE0,
0x26, 0x0B, 0xED, 0xE0, 0x27, 0x05, 0x19, 0x60, 0x27, 0xF5, 0x0A, 0x60, 0x28, 0xDC, 0xF6, 0x40,
-0x28, 0xE5, 0x09, 0x70, 0x29, 0xD4, 0xD0, 0x40, 0x2A, 0xC4, 0xB3, 0x30, 0x2B, 0xB4, 0xDC, 0x70,
+0x28, 0xE5, 0x09, 0x70, 0x29, 0xD4, 0xFA, 0x70, 0x2A, 0xC4, 0xEB, 0x70, 0x2B, 0xB4, 0xDC, 0x70,
0x2C, 0xA4, 0xCD, 0x70, 0x2D, 0x94, 0xBE, 0x70, 0x2E, 0x84, 0xAF, 0x70, 0x2F, 0x74, 0xA0, 0x70,
0x30, 0x64, 0x91, 0x70, 0x32, 0xC9, 0x70, 0xC0, 0x33, 0x3D, 0x90, 0xE0, 0x34, 0x52, 0x6B, 0xE0,
0x35, 0x1D, 0x72, 0xE0, 0x36, 0x32, 0x4D, 0xE0, 0x36, 0xFD, 0x54, 0xE0, 0x38, 0x1B, 0x6A, 0x60,
@@ -24036,23 +24201,23 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x44, 0x25, 0xBD, 0x60, 0x45, 0x43, 0xD2, 0xE0, 0x46, 0x05, 0x9F, 0x60, 0x47, 0x23, 0xB4, 0xE0,
0x47, 0xEE, 0xBB, 0xE0, 0x49, 0x03, 0x96, 0xE0, 0x49, 0xCE, 0x9D, 0xE0, 0x4A, 0xE3, 0x78, 0xE0,
0x4B, 0xAE, 0x7F, 0xE0, 0x4C, 0xCC, 0x95, 0x60, 0x4D, 0x8E, 0x61, 0xE0, 0x4E, 0xAC, 0x77, 0x60,
-0x00, 0x01, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05,
-0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x06, 0x08, 0x07, 0x08, 0x09, 0x0A, 0x07, 0x0A, 0x07,
-0x0A, 0x0B, 0x0D, 0x0C, 0x0B, 0x0C, 0x0B, 0x0C, 0x0B, 0x0C, 0x0B, 0x0C, 0x0B, 0x0C, 0x0B, 0x0C,
-0x0B, 0x0C, 0x0B, 0x0C, 0x0B, 0x0C, 0x0B, 0x0C, 0x0B, 0x0C, 0x0B, 0x0C, 0x0B, 0x0C, 0x0B, 0x0C,
-0x0B, 0x00, 0x00, 0x29, 0xB8, 0x00, 0x00, 0x00, 0x00, 0x2A, 0x30, 0x00, 0x04, 0x00, 0x00, 0x46,
-0x50, 0x01, 0x09, 0x00, 0x00, 0x38, 0x40, 0x00, 0x04, 0x00, 0x00, 0x38, 0x40, 0x00, 0x04, 0x00,
-0x00, 0x46, 0x50, 0x01, 0x09, 0x00, 0x00, 0x38, 0x40, 0x01, 0x09, 0x00, 0x00, 0x2A, 0x30, 0x00,
-0x0F, 0x00, 0x00, 0x38, 0x40, 0x01, 0x13, 0x00, 0x00, 0x2A, 0x30, 0x00, 0x0F, 0x00, 0x00, 0x38,
-0x40, 0x01, 0x13, 0x00, 0x00, 0x38, 0x40, 0x00, 0x0F, 0x00, 0x00, 0x46, 0x50, 0x01, 0x13, 0x00,
-0x00, 0x38, 0x40, 0x00, 0x0F, 0x4C, 0x4D, 0x54, 0x00, 0x59, 0x45, 0x52, 0x54, 0x00, 0x59, 0x45,
-0x52, 0x53, 0x54, 0x00, 0x41, 0x4D, 0x54, 0x00, 0x41, 0x4D, 0x53, 0x54, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x4F, 0x32, 0xD4, 0x40, 0x00, 0x01, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x04, 0x05,
+0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x06, 0x09, 0x07, 0x08, 0x07,
+0x08, 0x07, 0x08, 0x07, 0x08, 0x0A, 0x0C, 0x0B, 0x0A, 0x0B, 0x0A, 0x0B, 0x0A, 0x0B, 0x0A, 0x0B,
+0x0A, 0x0B, 0x0A, 0x0B, 0x0A, 0x0B, 0x0A, 0x0B, 0x0A, 0x0B, 0x0A, 0x0B, 0x0A, 0x0B, 0x0A, 0x0B,
+0x0A, 0x0B, 0x0A, 0x0B, 0x0A, 0x0C, 0x00, 0x00, 0x29, 0xB8, 0x00, 0x00, 0x00, 0x00, 0x2A, 0x30,
+0x00, 0x04, 0x00, 0x00, 0x46, 0x50, 0x01, 0x09, 0x00, 0x00, 0x38, 0x40, 0x00, 0x04, 0x00, 0x00,
+0x38, 0x40, 0x00, 0x04, 0x00, 0x00, 0x46, 0x50, 0x01, 0x09, 0x00, 0x00, 0x38, 0x40, 0x01, 0x09,
+0x00, 0x00, 0x2A, 0x30, 0x00, 0x0F, 0x00, 0x00, 0x38, 0x40, 0x01, 0x13, 0x00, 0x00, 0x38, 0x40,
+0x01, 0x13, 0x00, 0x00, 0x38, 0x40, 0x00, 0x0F, 0x00, 0x00, 0x46, 0x50, 0x01, 0x13, 0x00, 0x00,
+0x38, 0x40, 0x00, 0x0F, 0x4C, 0x4D, 0x54, 0x00, 0x59, 0x45, 0x52, 0x54, 0x00, 0x59, 0x45, 0x52,
+0x53, 0x54, 0x00, 0x41, 0x4D, 0x54, 0x00, 0x41, 0x4D, 0x53, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
#ifdef TIMELIB_SUPPORTS_V2DATA
0x54, 0x5A, 0x69, 0x66, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x18, 0xF8, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x18, 0xF8, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xAA, 0x19, 0x9A, 0x48, 0xFF, 0xFF, 0xFF, 0xFF,
0xE7, 0xDA, 0x0C, 0x50, 0x00, 0x00, 0x00, 0x00, 0x15, 0x27, 0x99, 0xC0, 0x00, 0x00, 0x00, 0x00,
0x16, 0x18, 0xCE, 0x30, 0x00, 0x00, 0x00, 0x00, 0x17, 0x08, 0xCD, 0x40, 0x00, 0x00, 0x00, 0x00,
@@ -24066,7 +24231,7 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x25, 0x1B, 0xFC, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x26, 0x0B, 0xED, 0xE0, 0x00, 0x00, 0x00, 0x00,
0x27, 0x05, 0x19, 0x60, 0x00, 0x00, 0x00, 0x00, 0x27, 0xF5, 0x0A, 0x60, 0x00, 0x00, 0x00, 0x00,
0x28, 0xDC, 0xF6, 0x40, 0x00, 0x00, 0x00, 0x00, 0x28, 0xE5, 0x09, 0x70, 0x00, 0x00, 0x00, 0x00,
-0x29, 0xD4, 0xD0, 0x40, 0x00, 0x00, 0x00, 0x00, 0x2A, 0xC4, 0xB3, 0x30, 0x00, 0x00, 0x00, 0x00,
+0x29, 0xD4, 0xFA, 0x70, 0x00, 0x00, 0x00, 0x00, 0x2A, 0xC4, 0xEB, 0x70, 0x00, 0x00, 0x00, 0x00,
0x2B, 0xB4, 0xDC, 0x70, 0x00, 0x00, 0x00, 0x00, 0x2C, 0xA4, 0xCD, 0x70, 0x00, 0x00, 0x00, 0x00,
0x2D, 0x94, 0xBE, 0x70, 0x00, 0x00, 0x00, 0x00, 0x2E, 0x84, 0xAF, 0x70, 0x00, 0x00, 0x00, 0x00,
0x2F, 0x74, 0xA0, 0x70, 0x00, 0x00, 0x00, 0x00, 0x30, 0x64, 0x91, 0x70, 0x00, 0x00, 0x00, 0x00,
@@ -24085,20 +24250,20 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x49, 0x03, 0x96, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x49, 0xCE, 0x9D, 0xE0, 0x00, 0x00, 0x00, 0x00,
0x4A, 0xE3, 0x78, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x4B, 0xAE, 0x7F, 0xE0, 0x00, 0x00, 0x00, 0x00,
0x4C, 0xCC, 0x95, 0x60, 0x00, 0x00, 0x00, 0x00, 0x4D, 0x8E, 0x61, 0xE0, 0x00, 0x00, 0x00, 0x00,
-0x4E, 0xAC, 0x77, 0x60, 0x00, 0x01, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x04, 0x05,
-0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x06, 0x08, 0x07, 0x08, 0x09,
-0x0A, 0x07, 0x0A, 0x07, 0x0A, 0x0B, 0x0D, 0x0C, 0x0B, 0x0C, 0x0B, 0x0C, 0x0B, 0x0C, 0x0B, 0x0C,
-0x0B, 0x0C, 0x0B, 0x0C, 0x0B, 0x0C, 0x0B, 0x0C, 0x0B, 0x0C, 0x0B, 0x0C, 0x0B, 0x0C, 0x0B, 0x0C,
-0x0B, 0x0C, 0x0B, 0x0C, 0x0B, 0x00, 0x00, 0x29, 0xB8, 0x00, 0x00, 0x00, 0x00, 0x2A, 0x30, 0x00,
-0x04, 0x00, 0x00, 0x46, 0x50, 0x01, 0x09, 0x00, 0x00, 0x38, 0x40, 0x00, 0x04, 0x00, 0x00, 0x38,
-0x40, 0x00, 0x04, 0x00, 0x00, 0x46, 0x50, 0x01, 0x09, 0x00, 0x00, 0x38, 0x40, 0x01, 0x09, 0x00,
-0x00, 0x2A, 0x30, 0x00, 0x0F, 0x00, 0x00, 0x38, 0x40, 0x01, 0x13, 0x00, 0x00, 0x2A, 0x30, 0x00,
-0x0F, 0x00, 0x00, 0x38, 0x40, 0x01, 0x13, 0x00, 0x00, 0x38, 0x40, 0x00, 0x0F, 0x00, 0x00, 0x46,
-0x50, 0x01, 0x13, 0x00, 0x00, 0x38, 0x40, 0x00, 0x0F, 0x4C, 0x4D, 0x54, 0x00, 0x59, 0x45, 0x52,
-0x54, 0x00, 0x59, 0x45, 0x52, 0x53, 0x54, 0x00, 0x41, 0x4D, 0x54, 0x00, 0x41, 0x4D, 0x53, 0x54,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x41, 0x4D,
-0x54, 0x2D, 0x34, 0x0A,
+0x4E, 0xAC, 0x77, 0x60, 0x00, 0x00, 0x00, 0x00, 0x4F, 0x32, 0xD4, 0x40, 0x00, 0x01, 0x03, 0x02,
+0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05,
+0x04, 0x05, 0x04, 0x06, 0x09, 0x07, 0x08, 0x07, 0x08, 0x07, 0x08, 0x07, 0x08, 0x0A, 0x0C, 0x0B,
+0x0A, 0x0B, 0x0A, 0x0B, 0x0A, 0x0B, 0x0A, 0x0B, 0x0A, 0x0B, 0x0A, 0x0B, 0x0A, 0x0B, 0x0A, 0x0B,
+0x0A, 0x0B, 0x0A, 0x0B, 0x0A, 0x0B, 0x0A, 0x0B, 0x0A, 0x0B, 0x0A, 0x0B, 0x0A, 0x0C, 0x00, 0x00,
+0x29, 0xB8, 0x00, 0x00, 0x00, 0x00, 0x2A, 0x30, 0x00, 0x04, 0x00, 0x00, 0x46, 0x50, 0x01, 0x09,
+0x00, 0x00, 0x38, 0x40, 0x00, 0x04, 0x00, 0x00, 0x38, 0x40, 0x00, 0x04, 0x00, 0x00, 0x46, 0x50,
+0x01, 0x09, 0x00, 0x00, 0x38, 0x40, 0x01, 0x09, 0x00, 0x00, 0x2A, 0x30, 0x00, 0x0F, 0x00, 0x00,
+0x38, 0x40, 0x01, 0x13, 0x00, 0x00, 0x38, 0x40, 0x01, 0x13, 0x00, 0x00, 0x38, 0x40, 0x00, 0x0F,
+0x00, 0x00, 0x46, 0x50, 0x01, 0x13, 0x00, 0x00, 0x38, 0x40, 0x00, 0x0F, 0x4C, 0x4D, 0x54, 0x00,
+0x59, 0x45, 0x52, 0x54, 0x00, 0x59, 0x45, 0x52, 0x53, 0x54, 0x00, 0x41, 0x4D, 0x54, 0x00, 0x41,
+0x4D, 0x53, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x01, 0x01,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x41,
+0x4D, 0x54, 0x2D, 0x34, 0x0A,
#endif
0x00, 0xC6, 0xA4, 0xDD, 0x01, 0x56, 0x8F, 0x50, 0x00, 0x00, 0x00, 0x00,
@@ -30486,7 +30651,7 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
/* Egypt */
0x50, 0x48, 0x50, 0x32, 0x00, 0x3F, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0D, 0x80, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0xB1, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0D, 0x80, 0x00, 0x00, 0x00,
0xC8, 0x93, 0xB4, 0xE0, 0xC8, 0xFA, 0x7B, 0xD0, 0xC9, 0xFC, 0xEF, 0xE0, 0xCA, 0xC7, 0xE8, 0xD0,
0xCB, 0xCB, 0xAE, 0x60, 0xCC, 0xDF, 0x29, 0xD0, 0xCD, 0xAC, 0xE1, 0xE0, 0xCE, 0xC6, 0xF4, 0xD0,
0xCF, 0x8F, 0x66, 0xE0, 0xD0, 0xA9, 0x79, 0xD0, 0xD1, 0x84, 0x60, 0xE0, 0xD2, 0x8A, 0xAD, 0x50,
@@ -30518,22 +30683,37 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x46, 0x31, 0x20, 0xE0, 0x46, 0xE0, 0x6A, 0x50, 0x48, 0x11, 0x02, 0xE0, 0x48, 0xB7, 0x11, 0xD0,
0x49, 0xF0, 0xE4, 0xE0, 0x4A, 0x8D, 0xB9, 0x50, 0x4B, 0xDA, 0x01, 0x60, 0x4C, 0x61, 0xBD, 0xD0,
0x4C, 0x89, 0x58, 0xE0, 0x4C, 0xA4, 0xFA, 0x50, 0x53, 0x75, 0x38, 0xE0, 0x53, 0xAC, 0x89, 0xD0,
-0x53, 0xDA, 0xBC, 0x60, 0x54, 0x24, 0x82, 0x50, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01,
+0x53, 0xDA, 0xBC, 0x60, 0x54, 0x24, 0x82, 0x50, 0x57, 0x7E, 0xD0, 0xE0, 0x58, 0x12, 0x6A, 0xD0,
+0x59, 0x02, 0x69, 0xE0, 0x59, 0x27, 0x45, 0xD0, 0x59, 0x55, 0x78, 0x60, 0x59, 0xF2, 0x4C, 0xD0,
+0x5A, 0xE2, 0x4B, 0xE0, 0x5A, 0xF4, 0xB2, 0xD0, 0x5B, 0x22, 0xE5, 0x60, 0x5B, 0xD2, 0x2E, 0xD0,
+0x5C, 0xC2, 0x2D, 0xE0, 0x5C, 0xCB, 0x5A, 0x50, 0x5C, 0xF9, 0x8C, 0xE0, 0x5D, 0xBB, 0x4B, 0x50,
+0x5E, 0xD0, 0x34, 0x60, 0x5F, 0x9B, 0x2D, 0x50, 0x60, 0x9D, 0xA1, 0x60, 0x61, 0x7B, 0x0F, 0x50,
+0x62, 0x74, 0x48, 0xE0, 0x63, 0x5A, 0xF1, 0x50, 0x64, 0x4A, 0xF0, 0x60, 0x65, 0x3A, 0xD3, 0x50,
+0x66, 0x2A, 0xD2, 0x60, 0x67, 0x23, 0xEF, 0xD0, 0x68, 0x0A, 0xB4, 0x60, 0x69, 0x03, 0xD1, 0xD0,
+0x69, 0xF3, 0xD0, 0xE0, 0x6A, 0xE3, 0xB3, 0xD0, 0x6B, 0xD3, 0xB2, 0xE0, 0x6C, 0xC3, 0x95, 0xD0,
+0x6D, 0xB3, 0x94, 0xE0, 0x6E, 0xA3, 0x77, 0xD0, 0x6F, 0x93, 0x76, 0xE0, 0x70, 0x83, 0x59, 0xD0,
+0x71, 0x73, 0x58, 0xE0, 0x72, 0x6C, 0x76, 0x50, 0x73, 0x53, 0x3A, 0xE0, 0x74, 0x4C, 0x58, 0x50,
+0x75, 0x3C, 0x57, 0x60, 0x76, 0x2C, 0x3A, 0x50, 0x77, 0x1C, 0x39, 0x60, 0x78, 0x0C, 0x1C, 0x50,
+0x78, 0xFC, 0x1B, 0x60, 0x79, 0xEB, 0xFE, 0x50, 0x7A, 0xDB, 0xFD, 0x60, 0x7B, 0xCB, 0xE0, 0x50,
+0x7C, 0xBB, 0xDF, 0x60, 0x7D, 0xA2, 0x87, 0xD0, 0x7E, 0xA4, 0xFB, 0xE0, 0x7F, 0x79, 0x2F, 0x50,
0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01,
0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01,
0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01,
0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01,
0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01,
+0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03,
0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03,
-0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03,
-0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x00, 0x00, 0x1D, 0x55, 0x00, 0x00, 0x00, 0x00, 0x2A,
-0x30, 0x01, 0x04, 0x00, 0x00, 0x1C, 0x20, 0x00, 0x09, 0x00, 0x00, 0x2A, 0x30, 0x01, 0x04, 0x4C,
-0x4D, 0x54, 0x00, 0x45, 0x45, 0x53, 0x54, 0x00, 0x45, 0x45, 0x54, 0x00, 0x00, 0x00, 0x00, 0x01,
-0x00, 0x00, 0x00, 0x00,
+0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01,
+0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01,
+0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01,
+0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01,
+0x02, 0x00, 0x00, 0x1D, 0x55, 0x00, 0x00, 0x00, 0x00, 0x2A, 0x30, 0x01, 0x04, 0x00, 0x00, 0x1C,
+0x20, 0x00, 0x09, 0x00, 0x00, 0x2A, 0x30, 0x01, 0x04, 0x4C, 0x4D, 0x54, 0x00, 0x45, 0x45, 0x53,
+0x54, 0x00, 0x45, 0x45, 0x54, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
#ifdef TIMELIB_SUPPORTS_V2DATA
0x54, 0x5A, 0x69, 0x66, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0D, 0xF8, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0xF0, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0D, 0xF8, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x7D, 0xBD, 0x4D, 0xAB, 0xFF, 0xFF, 0xFF, 0xFF,
0xC8, 0x93, 0xB4, 0xE0, 0xFF, 0xFF, 0xFF, 0xFF, 0xC8, 0xFA, 0x7B, 0xD0, 0xFF, 0xFF, 0xFF, 0xFF,
0xC9, 0xFC, 0xEF, 0xE0, 0xFF, 0xFF, 0xFF, 0xFF, 0xCA, 0xC7, 0xE8, 0xD0, 0xFF, 0xFF, 0xFF, 0xFF,
@@ -30597,7 +30777,63 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x4B, 0xDA, 0x01, 0x60, 0x00, 0x00, 0x00, 0x00, 0x4C, 0x61, 0xBD, 0xD0, 0x00, 0x00, 0x00, 0x00,
0x4C, 0x89, 0x58, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x4C, 0xA4, 0xFA, 0x50, 0x00, 0x00, 0x00, 0x00,
0x53, 0x75, 0x38, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x53, 0xAC, 0x89, 0xD0, 0x00, 0x00, 0x00, 0x00,
-0x53, 0xDA, 0xBC, 0x60, 0x00, 0x00, 0x00, 0x00, 0x54, 0x24, 0x82, 0x50, 0x00, 0x02, 0x01, 0x02,
+0x53, 0xDA, 0xBC, 0x60, 0x00, 0x00, 0x00, 0x00, 0x54, 0x24, 0x82, 0x50, 0x00, 0x00, 0x00, 0x00,
+0x57, 0x7E, 0xD0, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x58, 0x12, 0x6A, 0xD0, 0x00, 0x00, 0x00, 0x00,
+0x59, 0x02, 0x69, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x59, 0x27, 0x45, 0xD0, 0x00, 0x00, 0x00, 0x00,
+0x59, 0x55, 0x78, 0x60, 0x00, 0x00, 0x00, 0x00, 0x59, 0xF2, 0x4C, 0xD0, 0x00, 0x00, 0x00, 0x00,
+0x5A, 0xE2, 0x4B, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x5A, 0xF4, 0xB2, 0xD0, 0x00, 0x00, 0x00, 0x00,
+0x5B, 0x22, 0xE5, 0x60, 0x00, 0x00, 0x00, 0x00, 0x5B, 0xD2, 0x2E, 0xD0, 0x00, 0x00, 0x00, 0x00,
+0x5C, 0xC2, 0x2D, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x5C, 0xCB, 0x5A, 0x50, 0x00, 0x00, 0x00, 0x00,
+0x5C, 0xF9, 0x8C, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x5D, 0xBB, 0x4B, 0x50, 0x00, 0x00, 0x00, 0x00,
+0x5E, 0xD0, 0x34, 0x60, 0x00, 0x00, 0x00, 0x00, 0x5F, 0x9B, 0x2D, 0x50, 0x00, 0x00, 0x00, 0x00,
+0x60, 0x9D, 0xA1, 0x60, 0x00, 0x00, 0x00, 0x00, 0x61, 0x7B, 0x0F, 0x50, 0x00, 0x00, 0x00, 0x00,
+0x62, 0x74, 0x48, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x63, 0x5A, 0xF1, 0x50, 0x00, 0x00, 0x00, 0x00,
+0x64, 0x4A, 0xF0, 0x60, 0x00, 0x00, 0x00, 0x00, 0x65, 0x3A, 0xD3, 0x50, 0x00, 0x00, 0x00, 0x00,
+0x66, 0x2A, 0xD2, 0x60, 0x00, 0x00, 0x00, 0x00, 0x67, 0x23, 0xEF, 0xD0, 0x00, 0x00, 0x00, 0x00,
+0x68, 0x0A, 0xB4, 0x60, 0x00, 0x00, 0x00, 0x00, 0x69, 0x03, 0xD1, 0xD0, 0x00, 0x00, 0x00, 0x00,
+0x69, 0xF3, 0xD0, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x6A, 0xE3, 0xB3, 0xD0, 0x00, 0x00, 0x00, 0x00,
+0x6B, 0xD3, 0xB2, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x6C, 0xC3, 0x95, 0xD0, 0x00, 0x00, 0x00, 0x00,
+0x6D, 0xB3, 0x94, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x6E, 0xA3, 0x77, 0xD0, 0x00, 0x00, 0x00, 0x00,
+0x6F, 0x93, 0x76, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x70, 0x83, 0x59, 0xD0, 0x00, 0x00, 0x00, 0x00,
+0x71, 0x73, 0x58, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x72, 0x6C, 0x76, 0x50, 0x00, 0x00, 0x00, 0x00,
+0x73, 0x53, 0x3A, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x74, 0x4C, 0x58, 0x50, 0x00, 0x00, 0x00, 0x00,
+0x75, 0x3C, 0x57, 0x60, 0x00, 0x00, 0x00, 0x00, 0x76, 0x2C, 0x3A, 0x50, 0x00, 0x00, 0x00, 0x00,
+0x77, 0x1C, 0x39, 0x60, 0x00, 0x00, 0x00, 0x00, 0x78, 0x0C, 0x1C, 0x50, 0x00, 0x00, 0x00, 0x00,
+0x78, 0xFC, 0x1B, 0x60, 0x00, 0x00, 0x00, 0x00, 0x79, 0xEB, 0xFE, 0x50, 0x00, 0x00, 0x00, 0x00,
+0x7A, 0xDB, 0xFD, 0x60, 0x00, 0x00, 0x00, 0x00, 0x7B, 0xCB, 0xE0, 0x50, 0x00, 0x00, 0x00, 0x00,
+0x7C, 0xBB, 0xDF, 0x60, 0x00, 0x00, 0x00, 0x00, 0x7D, 0xA2, 0x87, 0xD0, 0x00, 0x00, 0x00, 0x00,
+0x7E, 0xA4, 0xFB, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x79, 0x2F, 0x50, 0x00, 0x00, 0x00, 0x00,
+0x80, 0x84, 0xDD, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x81, 0x46, 0x9C, 0x50, 0x00, 0x00, 0x00, 0x00,
+0x82, 0x64, 0xBF, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x83, 0x1D, 0x43, 0xD0, 0x00, 0x00, 0x00, 0x00,
+0x83, 0x4B, 0x76, 0x60, 0x00, 0x00, 0x00, 0x00, 0x83, 0x54, 0xA2, 0xD0, 0x00, 0x00, 0x00, 0x00,
+0x84, 0x44, 0xA1, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x84, 0xF3, 0xEB, 0x50, 0x00, 0x00, 0x00, 0x00,
+0x85, 0x22, 0x1D, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x85, 0x34, 0x84, 0xD0, 0x00, 0x00, 0x00, 0x00,
+0x86, 0x24, 0x83, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x86, 0xC1, 0x58, 0x50, 0x00, 0x00, 0x00, 0x00,
+0x86, 0xEF, 0x8A, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x87, 0x1D, 0xA1, 0x50, 0x00, 0x00, 0x00, 0x00,
+0x88, 0x04, 0x65, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x88, 0x97, 0xFF, 0xD0, 0x00, 0x00, 0x00, 0x00,
+0x88, 0xC6, 0x32, 0x60, 0x00, 0x00, 0x00, 0x00, 0x88, 0xFD, 0x83, 0x50, 0x00, 0x00, 0x00, 0x00,
+0x89, 0xED, 0x82, 0x60, 0x00, 0x00, 0x00, 0x00, 0x8A, 0x6E, 0xA7, 0x50, 0x00, 0x00, 0x00, 0x00,
+0x8A, 0x9C, 0xD9, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x8A, 0xDD, 0x65, 0x50, 0x00, 0x00, 0x00, 0x00,
+0x8B, 0xCD, 0x64, 0x60, 0x00, 0x00, 0x00, 0x00, 0x8C, 0x3C, 0x14, 0x50, 0x00, 0x00, 0x00, 0x00,
+0x8C, 0x6A, 0x46, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x8C, 0xBD, 0x47, 0x50, 0x00, 0x00, 0x00, 0x00,
+0x8D, 0xAD, 0x46, 0x60, 0x00, 0x00, 0x00, 0x00, 0x8E, 0x12, 0xBB, 0xD0, 0x00, 0x00, 0x00, 0x00,
+0x8E, 0x40, 0xEE, 0x60, 0x00, 0x00, 0x00, 0x00, 0x8E, 0x9D, 0x29, 0x50, 0x00, 0x00, 0x00, 0x00,
+0x8F, 0x8D, 0x28, 0x60, 0x00, 0x00, 0x00, 0x00, 0x8F, 0xE0, 0x28, 0xD0, 0x00, 0x00, 0x00, 0x00,
+0x90, 0x17, 0x95, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x90, 0x7D, 0x0B, 0x50, 0x00, 0x00, 0x00, 0x00,
+0x91, 0x6D, 0x0A, 0x60, 0x00, 0x00, 0x00, 0x00, 0x91, 0xB6, 0xD0, 0x50, 0x00, 0x00, 0x00, 0x00,
+0x91, 0xE5, 0x02, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x92, 0x66, 0x27, 0xD0, 0x00, 0x00, 0x00, 0x00,
+0x93, 0x56, 0x26, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x93, 0x8D, 0x77, 0xD0, 0x00, 0x00, 0x00, 0x00,
+0x93, 0xBB, 0xAA, 0x60, 0x00, 0x00, 0x00, 0x00, 0x94, 0x46, 0x09, 0xD0, 0x00, 0x00, 0x00, 0x00,
+0x95, 0x36, 0x08, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x95, 0x5A, 0xE4, 0xD0, 0x00, 0x00, 0x00, 0x00,
+0x95, 0x89, 0x17, 0x60, 0x00, 0x00, 0x00, 0x00, 0x96, 0x25, 0xEB, 0xD0, 0x00, 0x00, 0x00, 0x00,
+0x97, 0x15, 0xEA, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x97, 0x31, 0x8C, 0x50, 0x00, 0x00, 0x00, 0x00,
+0x97, 0x5F, 0xBE, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x98, 0x05, 0xCD, 0xD0, 0x00, 0x00, 0x00, 0x00,
+0x98, 0xF5, 0xCC, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x99, 0x08, 0x33, 0xD0, 0x00, 0x00, 0x00, 0x00,
+0x99, 0x36, 0x66, 0x60, 0x00, 0x00, 0x00, 0x00, 0x99, 0xE5, 0xAF, 0xD0, 0x00, 0x00, 0x00, 0x00,
+0x9B, 0x03, 0xD3, 0x60, 0x00, 0x00, 0x00, 0x00, 0x9B, 0xCE, 0xCC, 0x50, 0x00, 0x00, 0x00, 0x00,
+0x9C, 0xDA, 0x7A, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x9D, 0xAE, 0xAE, 0x50, 0x00, 0x00, 0x00, 0x00,
+0x9E, 0xB1, 0x22, 0x60, 0x00, 0x00, 0x00, 0x00, 0x9F, 0x8E, 0x90, 0x50, 0x00, 0x00, 0x00, 0x00,
+0xA0, 0x7E, 0x8F, 0x60, 0x00, 0x00, 0x00, 0x00, 0xA1, 0x6E, 0x72, 0x50, 0x00, 0x02, 0x01, 0x02,
0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
@@ -30605,10 +30841,19 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02,
0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02,
-0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x00, 0x00, 0x1D, 0x55,
+0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
+0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
+0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
+0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
+0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
+0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
+0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02,
+0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x00, 0x00, 0x1D, 0x55,
0x00, 0x00, 0x00, 0x00, 0x2A, 0x30, 0x01, 0x04, 0x00, 0x00, 0x1C, 0x20, 0x00, 0x09, 0x00, 0x00,
0x2A, 0x30, 0x01, 0x04, 0x4C, 0x4D, 0x54, 0x00, 0x45, 0x45, 0x53, 0x54, 0x00, 0x45, 0x45, 0x54,
-0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x45, 0x45, 0x54, 0x2D, 0x32, 0x0A,
+0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x45, 0x45, 0x54, 0x2D, 0x32, 0x45,
+0x45, 0x53, 0x54, 0x2C, 0x4D, 0x34, 0x2E, 0x35, 0x2E, 0x34, 0x2F, 0x32, 0x34, 0x2C, 0x4D, 0x31,
+0x30, 0x2E, 0x35, 0x2E, 0x34, 0x2F, 0x32, 0x34, 0x0A,
#endif
0x00, 0x89, 0x54, 0x40, 0x01, 0x12, 0xA8, 0x80, 0x00, 0x00, 0x00, 0x00,
@@ -31036,168 +31281,192 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
/* Etc/GMT+1 */
0x50, 0x48, 0x50, 0x32, 0x00, 0x3F, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0xFF, 0xFF, 0xF1, 0xF0,
-0x00, 0x00, 0x47, 0x4D, 0x54, 0x2B, 0x31, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0x80, 0x00, 0x00, 0x00,
+0x7F, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xF1, 0xF0, 0x00, 0x00, 0x47, 0x4D, 0x54, 0x2B,
+0x31, 0x00, 0x00, 0x00,
#ifdef TIMELIB_SUPPORTS_V2DATA
0x54, 0x5A, 0x69, 0x66, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0xF8, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xF1, 0xF0, 0x00, 0x00, 0x47, 0x4D, 0x54, 0x2B, 0x31,
-0x00, 0x00, 0x00, 0x0A, 0x3C, 0x47, 0x4D, 0x54, 0x2B, 0x31, 0x3E, 0x31, 0x0A,
+0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0xF8, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF,
+0xF1, 0xF0, 0x00, 0x00, 0x47, 0x4D, 0x54, 0x2B, 0x31, 0x00, 0x00, 0x00, 0x0A, 0x3C, 0x47, 0x4D,
+0x54, 0x2B, 0x31, 0x3E, 0x31, 0x0A,
#endif
0x00, 0x89, 0x54, 0x40, 0x01, 0x12, 0xA8, 0x80, 0x00, 0x00, 0x00, 0x00,
/* Etc/GMT+10 */
0x50, 0x48, 0x50, 0x32, 0x00, 0x3F, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x07, 0xFF, 0xFF, 0x73, 0x60,
-0x00, 0x00, 0x47, 0x4D, 0x54, 0x2B, 0x31, 0x30, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x07, 0x80, 0x00, 0x00, 0x00,
+0x7F, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x73, 0x60, 0x00, 0x00, 0x47, 0x4D, 0x54, 0x2B,
+0x31, 0x30, 0x00, 0x00, 0x00,
#ifdef TIMELIB_SUPPORTS_V2DATA
0x54, 0x5A, 0x69, 0x66, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x07, 0xF8, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x73, 0x60, 0x00, 0x00, 0x47, 0x4D, 0x54, 0x2B, 0x31,
-0x30, 0x00, 0x00, 0x00, 0x0A, 0x3C, 0x47, 0x4D, 0x54, 0x2B, 0x31, 0x30, 0x3E, 0x31, 0x30, 0x0A,
+0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x07, 0xF8, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF,
+0x73, 0x60, 0x00, 0x00, 0x47, 0x4D, 0x54, 0x2B, 0x31, 0x30, 0x00, 0x00, 0x00, 0x0A, 0x3C, 0x47,
+0x4D, 0x54, 0x2B, 0x31, 0x30, 0x3E, 0x31, 0x30, 0x0A,
#endif
0x00, 0x89, 0x54, 0x40, 0x01, 0x12, 0xA8, 0x80, 0x00, 0x00, 0x00, 0x00,
/* Etc/GMT+11 */
0x50, 0x48, 0x50, 0x32, 0x00, 0x3F, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x07, 0xFF, 0xFF, 0x65, 0x50,
-0x00, 0x00, 0x47, 0x4D, 0x54, 0x2B, 0x31, 0x31, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x07, 0x80, 0x00, 0x00, 0x00,
+0x7F, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x65, 0x50, 0x00, 0x00, 0x47, 0x4D, 0x54, 0x2B,
+0x31, 0x31, 0x00, 0x00, 0x00,
#ifdef TIMELIB_SUPPORTS_V2DATA
0x54, 0x5A, 0x69, 0x66, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x07, 0xF8, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x65, 0x50, 0x00, 0x00, 0x47, 0x4D, 0x54, 0x2B, 0x31,
-0x31, 0x00, 0x00, 0x00, 0x0A, 0x3C, 0x47, 0x4D, 0x54, 0x2B, 0x31, 0x31, 0x3E, 0x31, 0x31, 0x0A,
+0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x07, 0xF8, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF,
+0x65, 0x50, 0x00, 0x00, 0x47, 0x4D, 0x54, 0x2B, 0x31, 0x31, 0x00, 0x00, 0x00, 0x0A, 0x3C, 0x47,
+0x4D, 0x54, 0x2B, 0x31, 0x31, 0x3E, 0x31, 0x31, 0x0A,
#endif
0x00, 0x89, 0x54, 0x40, 0x01, 0x12, 0xA8, 0x80, 0x00, 0x00, 0x00, 0x00,
/* Etc/GMT+12 */
0x50, 0x48, 0x50, 0x32, 0x00, 0x3F, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x07, 0xFF, 0xFF, 0x57, 0x40,
-0x00, 0x00, 0x47, 0x4D, 0x54, 0x2B, 0x31, 0x32, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x07, 0x80, 0x00, 0x00, 0x00,
+0x7F, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x57, 0x40, 0x00, 0x00, 0x47, 0x4D, 0x54, 0x2B,
+0x31, 0x32, 0x00, 0x00, 0x00,
#ifdef TIMELIB_SUPPORTS_V2DATA
0x54, 0x5A, 0x69, 0x66, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x07, 0xF8, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x57, 0x40, 0x00, 0x00, 0x47, 0x4D, 0x54, 0x2B, 0x31,
-0x32, 0x00, 0x00, 0x00, 0x0A, 0x3C, 0x47, 0x4D, 0x54, 0x2B, 0x31, 0x32, 0x3E, 0x31, 0x32, 0x0A,
+0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x07, 0xF8, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF,
+0x57, 0x40, 0x00, 0x00, 0x47, 0x4D, 0x54, 0x2B, 0x31, 0x32, 0x00, 0x00, 0x00, 0x0A, 0x3C, 0x47,
+0x4D, 0x54, 0x2B, 0x31, 0x32, 0x3E, 0x31, 0x32, 0x0A,
#endif
0x00, 0x89, 0x54, 0x40, 0x01, 0x12, 0xA8, 0x80, 0x00, 0x00, 0x00, 0x00,
/* Etc/GMT+2 */
0x50, 0x48, 0x50, 0x32, 0x00, 0x3F, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0xFF, 0xFF, 0xE3, 0xE0,
-0x00, 0x00, 0x47, 0x4D, 0x54, 0x2B, 0x32, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0x80, 0x00, 0x00, 0x00,
+0x7F, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xE3, 0xE0, 0x00, 0x00, 0x47, 0x4D, 0x54, 0x2B,
+0x32, 0x00, 0x00, 0x00,
#ifdef TIMELIB_SUPPORTS_V2DATA
0x54, 0x5A, 0x69, 0x66, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0xF8, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xE3, 0xE0, 0x00, 0x00, 0x47, 0x4D, 0x54, 0x2B, 0x32,
-0x00, 0x00, 0x00, 0x0A, 0x3C, 0x47, 0x4D, 0x54, 0x2B, 0x32, 0x3E, 0x32, 0x0A,
+0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0xF8, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF,
+0xE3, 0xE0, 0x00, 0x00, 0x47, 0x4D, 0x54, 0x2B, 0x32, 0x00, 0x00, 0x00, 0x0A, 0x3C, 0x47, 0x4D,
+0x54, 0x2B, 0x32, 0x3E, 0x32, 0x0A,
#endif
0x00, 0x89, 0x54, 0x40, 0x01, 0x12, 0xA8, 0x80, 0x00, 0x00, 0x00, 0x00,
/* Etc/GMT+3 */
0x50, 0x48, 0x50, 0x32, 0x00, 0x3F, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0xFF, 0xFF, 0xD5, 0xD0,
-0x00, 0x00, 0x47, 0x4D, 0x54, 0x2B, 0x33, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0x80, 0x00, 0x00, 0x00,
+0x7F, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xD5, 0xD0, 0x00, 0x00, 0x47, 0x4D, 0x54, 0x2B,
+0x33, 0x00, 0x00, 0x00,
#ifdef TIMELIB_SUPPORTS_V2DATA
0x54, 0x5A, 0x69, 0x66, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0xF8, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xD5, 0xD0, 0x00, 0x00, 0x47, 0x4D, 0x54, 0x2B, 0x33,
-0x00, 0x00, 0x00, 0x0A, 0x3C, 0x47, 0x4D, 0x54, 0x2B, 0x33, 0x3E, 0x33, 0x0A,
+0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0xF8, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF,
+0xD5, 0xD0, 0x00, 0x00, 0x47, 0x4D, 0x54, 0x2B, 0x33, 0x00, 0x00, 0x00, 0x0A, 0x3C, 0x47, 0x4D,
+0x54, 0x2B, 0x33, 0x3E, 0x33, 0x0A,
#endif
0x00, 0x89, 0x54, 0x40, 0x01, 0x12, 0xA8, 0x80, 0x00, 0x00, 0x00, 0x00,
/* Etc/GMT+4 */
0x50, 0x48, 0x50, 0x32, 0x00, 0x3F, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0xFF, 0xFF, 0xC7, 0xC0,
-0x00, 0x00, 0x47, 0x4D, 0x54, 0x2B, 0x34, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0x80, 0x00, 0x00, 0x00,
+0x7F, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xC7, 0xC0, 0x00, 0x00, 0x47, 0x4D, 0x54, 0x2B,
+0x34, 0x00, 0x00, 0x00,
#ifdef TIMELIB_SUPPORTS_V2DATA
0x54, 0x5A, 0x69, 0x66, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0xF8, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xC7, 0xC0, 0x00, 0x00, 0x47, 0x4D, 0x54, 0x2B, 0x34,
-0x00, 0x00, 0x00, 0x0A, 0x3C, 0x47, 0x4D, 0x54, 0x2B, 0x34, 0x3E, 0x34, 0x0A,
+0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0xF8, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF,
+0xC7, 0xC0, 0x00, 0x00, 0x47, 0x4D, 0x54, 0x2B, 0x34, 0x00, 0x00, 0x00, 0x0A, 0x3C, 0x47, 0x4D,
+0x54, 0x2B, 0x34, 0x3E, 0x34, 0x0A,
#endif
0x00, 0x89, 0x54, 0x40, 0x01, 0x12, 0xA8, 0x80, 0x00, 0x00, 0x00, 0x00,
/* Etc/GMT+5 */
0x50, 0x48, 0x50, 0x32, 0x00, 0x3F, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0xFF, 0xFF, 0xB9, 0xB0,
-0x00, 0x00, 0x47, 0x4D, 0x54, 0x2B, 0x35, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0x80, 0x00, 0x00, 0x00,
+0x7F, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xB9, 0xB0, 0x00, 0x00, 0x47, 0x4D, 0x54, 0x2B,
+0x35, 0x00, 0x00, 0x00,
#ifdef TIMELIB_SUPPORTS_V2DATA
0x54, 0x5A, 0x69, 0x66, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0xF8, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xB9, 0xB0, 0x00, 0x00, 0x47, 0x4D, 0x54, 0x2B, 0x35,
-0x00, 0x00, 0x00, 0x0A, 0x3C, 0x47, 0x4D, 0x54, 0x2B, 0x35, 0x3E, 0x35, 0x0A,
+0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0xF8, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF,
+0xB9, 0xB0, 0x00, 0x00, 0x47, 0x4D, 0x54, 0x2B, 0x35, 0x00, 0x00, 0x00, 0x0A, 0x3C, 0x47, 0x4D,
+0x54, 0x2B, 0x35, 0x3E, 0x35, 0x0A,
#endif
0x00, 0x89, 0x54, 0x40, 0x01, 0x12, 0xA8, 0x80, 0x00, 0x00, 0x00, 0x00,
/* Etc/GMT+6 */
0x50, 0x48, 0x50, 0x32, 0x00, 0x3F, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0xFF, 0xFF, 0xAB, 0xA0,
-0x00, 0x00, 0x47, 0x4D, 0x54, 0x2B, 0x36, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0x80, 0x00, 0x00, 0x00,
+0x7F, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xAB, 0xA0, 0x00, 0x00, 0x47, 0x4D, 0x54, 0x2B,
+0x36, 0x00, 0x00, 0x00,
#ifdef TIMELIB_SUPPORTS_V2DATA
0x54, 0x5A, 0x69, 0x66, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0xF8, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xAB, 0xA0, 0x00, 0x00, 0x47, 0x4D, 0x54, 0x2B, 0x36,
-0x00, 0x00, 0x00, 0x0A, 0x3C, 0x47, 0x4D, 0x54, 0x2B, 0x36, 0x3E, 0x36, 0x0A,
+0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0xF8, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF,
+0xAB, 0xA0, 0x00, 0x00, 0x47, 0x4D, 0x54, 0x2B, 0x36, 0x00, 0x00, 0x00, 0x0A, 0x3C, 0x47, 0x4D,
+0x54, 0x2B, 0x36, 0x3E, 0x36, 0x0A,
#endif
0x00, 0x89, 0x54, 0x40, 0x01, 0x12, 0xA8, 0x80, 0x00, 0x00, 0x00, 0x00,
/* Etc/GMT+7 */
0x50, 0x48, 0x50, 0x32, 0x00, 0x3F, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0xFF, 0xFF, 0x9D, 0x90,
-0x00, 0x00, 0x47, 0x4D, 0x54, 0x2B, 0x37, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0x80, 0x00, 0x00, 0x00,
+0x7F, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x9D, 0x90, 0x00, 0x00, 0x47, 0x4D, 0x54, 0x2B,
+0x37, 0x00, 0x00, 0x00,
#ifdef TIMELIB_SUPPORTS_V2DATA
0x54, 0x5A, 0x69, 0x66, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0xF8, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x9D, 0x90, 0x00, 0x00, 0x47, 0x4D, 0x54, 0x2B, 0x37,
-0x00, 0x00, 0x00, 0x0A, 0x3C, 0x47, 0x4D, 0x54, 0x2B, 0x37, 0x3E, 0x37, 0x0A,
+0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0xF8, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF,
+0x9D, 0x90, 0x00, 0x00, 0x47, 0x4D, 0x54, 0x2B, 0x37, 0x00, 0x00, 0x00, 0x0A, 0x3C, 0x47, 0x4D,
+0x54, 0x2B, 0x37, 0x3E, 0x37, 0x0A,
#endif
0x00, 0x89, 0x54, 0x40, 0x01, 0x12, 0xA8, 0x80, 0x00, 0x00, 0x00, 0x00,
/* Etc/GMT+8 */
0x50, 0x48, 0x50, 0x32, 0x00, 0x3F, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0xFF, 0xFF, 0x8F, 0x80,
-0x00, 0x00, 0x47, 0x4D, 0x54, 0x2B, 0x38, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0x80, 0x00, 0x00, 0x00,
+0x7F, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x8F, 0x80, 0x00, 0x00, 0x47, 0x4D, 0x54, 0x2B,
+0x38, 0x00, 0x00, 0x00,
#ifdef TIMELIB_SUPPORTS_V2DATA
0x54, 0x5A, 0x69, 0x66, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0xF8, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x8F, 0x80, 0x00, 0x00, 0x47, 0x4D, 0x54, 0x2B, 0x38,
-0x00, 0x00, 0x00, 0x0A, 0x3C, 0x47, 0x4D, 0x54, 0x2B, 0x38, 0x3E, 0x38, 0x0A,
+0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0xF8, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF,
+0x8F, 0x80, 0x00, 0x00, 0x47, 0x4D, 0x54, 0x2B, 0x38, 0x00, 0x00, 0x00, 0x0A, 0x3C, 0x47, 0x4D,
+0x54, 0x2B, 0x38, 0x3E, 0x38, 0x0A,
#endif
0x00, 0x89, 0x54, 0x40, 0x01, 0x12, 0xA8, 0x80, 0x00, 0x00, 0x00, 0x00,
/* Etc/GMT+9 */
0x50, 0x48, 0x50, 0x32, 0x00, 0x3F, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0xFF, 0xFF, 0x81, 0x70,
-0x00, 0x00, 0x47, 0x4D, 0x54, 0x2B, 0x39, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0x80, 0x00, 0x00, 0x00,
+0x7F, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x81, 0x70, 0x00, 0x00, 0x47, 0x4D, 0x54, 0x2B,
+0x39, 0x00, 0x00, 0x00,
#ifdef TIMELIB_SUPPORTS_V2DATA
0x54, 0x5A, 0x69, 0x66, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0xF8, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x81, 0x70, 0x00, 0x00, 0x47, 0x4D, 0x54, 0x2B, 0x39,
-0x00, 0x00, 0x00, 0x0A, 0x3C, 0x47, 0x4D, 0x54, 0x2B, 0x39, 0x3E, 0x39, 0x0A,
+0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0xF8, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF,
+0x81, 0x70, 0x00, 0x00, 0x47, 0x4D, 0x54, 0x2B, 0x39, 0x00, 0x00, 0x00, 0x0A, 0x3C, 0x47, 0x4D,
+0x54, 0x2B, 0x39, 0x3E, 0x39, 0x0A,
#endif
0x00, 0x89, 0x54, 0x40, 0x01, 0x12, 0xA8, 0x80, 0x00, 0x00, 0x00, 0x00,
@@ -31218,201 +31487,224 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
/* Etc/GMT-1 */
0x50, 0x48, 0x50, 0x32, 0x00, 0x3F, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x0E, 0x10,
-0x00, 0x00, 0x47, 0x4D, 0x54, 0x2D, 0x31, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0x80, 0x00, 0x00, 0x00,
+0x7F, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x10, 0x00, 0x00, 0x47, 0x4D, 0x54, 0x2D,
+0x31, 0x00, 0x00, 0x00,
#ifdef TIMELIB_SUPPORTS_V2DATA
0x54, 0x5A, 0x69, 0x66, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0xF8, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x10, 0x00, 0x00, 0x47, 0x4D, 0x54, 0x2D, 0x31,
-0x00, 0x00, 0x00, 0x0A, 0x3C, 0x47, 0x4D, 0x54, 0x2D, 0x31, 0x3E, 0x2D, 0x31, 0x0A,
+0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0xF8, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
+0x0E, 0x10, 0x00, 0x00, 0x47, 0x4D, 0x54, 0x2D, 0x31, 0x00, 0x00, 0x00, 0x0A, 0x3C, 0x47, 0x4D,
+0x54, 0x2D, 0x31, 0x3E, 0x2D, 0x31, 0x0A,
#endif
0x00, 0x89, 0x54, 0x40, 0x01, 0x12, 0xA8, 0x80, 0x00, 0x00, 0x00, 0x00,
/* Etc/GMT-10 */
0x50, 0x48, 0x50, 0x32, 0x00, 0x3F, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x8C, 0xA0,
-0x00, 0x00, 0x47, 0x4D, 0x54, 0x2D, 0x31, 0x30, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x07, 0x80, 0x00, 0x00, 0x00,
+0x7F, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x8C, 0xA0, 0x00, 0x00, 0x47, 0x4D, 0x54, 0x2D,
+0x31, 0x30, 0x00, 0x00, 0x00,
#ifdef TIMELIB_SUPPORTS_V2DATA
0x54, 0x5A, 0x69, 0x66, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x07, 0xF8, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8C, 0xA0, 0x00, 0x00, 0x47, 0x4D, 0x54, 0x2D, 0x31,
-0x30, 0x00, 0x00, 0x00, 0x0A, 0x3C, 0x47, 0x4D, 0x54, 0x2D, 0x31, 0x30, 0x3E, 0x2D, 0x31, 0x30,
-0x0A,
+0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x07, 0xF8, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
+0x8C, 0xA0, 0x00, 0x00, 0x47, 0x4D, 0x54, 0x2D, 0x31, 0x30, 0x00, 0x00, 0x00, 0x0A, 0x3C, 0x47,
+0x4D, 0x54, 0x2D, 0x31, 0x30, 0x3E, 0x2D, 0x31, 0x30, 0x0A,
#endif
0x00, 0x89, 0x54, 0x40, 0x01, 0x12, 0xA8, 0x80, 0x00, 0x00, 0x00, 0x00,
/* Etc/GMT-11 */
0x50, 0x48, 0x50, 0x32, 0x00, 0x3F, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x9A, 0xB0,
-0x00, 0x00, 0x47, 0x4D, 0x54, 0x2D, 0x31, 0x31, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x07, 0x80, 0x00, 0x00, 0x00,
+0x7F, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x9A, 0xB0, 0x00, 0x00, 0x47, 0x4D, 0x54, 0x2D,
+0x31, 0x31, 0x00, 0x00, 0x00,
#ifdef TIMELIB_SUPPORTS_V2DATA
0x54, 0x5A, 0x69, 0x66, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x07, 0xF8, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9A, 0xB0, 0x00, 0x00, 0x47, 0x4D, 0x54, 0x2D, 0x31,
-0x31, 0x00, 0x00, 0x00, 0x0A, 0x3C, 0x47, 0x4D, 0x54, 0x2D, 0x31, 0x31, 0x3E, 0x2D, 0x31, 0x31,
-0x0A,
+0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x07, 0xF8, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
+0x9A, 0xB0, 0x00, 0x00, 0x47, 0x4D, 0x54, 0x2D, 0x31, 0x31, 0x00, 0x00, 0x00, 0x0A, 0x3C, 0x47,
+0x4D, 0x54, 0x2D, 0x31, 0x31, 0x3E, 0x2D, 0x31, 0x31, 0x0A,
#endif
0x00, 0x89, 0x54, 0x40, 0x01, 0x12, 0xA8, 0x80, 0x00, 0x00, 0x00, 0x00,
/* Etc/GMT-12 */
0x50, 0x48, 0x50, 0x32, 0x00, 0x3F, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0xA8, 0xC0,
-0x00, 0x00, 0x47, 0x4D, 0x54, 0x2D, 0x31, 0x32, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x07, 0x80, 0x00, 0x00, 0x00,
+0x7F, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xA8, 0xC0, 0x00, 0x00, 0x47, 0x4D, 0x54, 0x2D,
+0x31, 0x32, 0x00, 0x00, 0x00,
#ifdef TIMELIB_SUPPORTS_V2DATA
0x54, 0x5A, 0x69, 0x66, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x07, 0xF8, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA8, 0xC0, 0x00, 0x00, 0x47, 0x4D, 0x54, 0x2D, 0x31,
-0x32, 0x00, 0x00, 0x00, 0x0A, 0x3C, 0x47, 0x4D, 0x54, 0x2D, 0x31, 0x32, 0x3E, 0x2D, 0x31, 0x32,
-0x0A,
+0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x07, 0xF8, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
+0xA8, 0xC0, 0x00, 0x00, 0x47, 0x4D, 0x54, 0x2D, 0x31, 0x32, 0x00, 0x00, 0x00, 0x0A, 0x3C, 0x47,
+0x4D, 0x54, 0x2D, 0x31, 0x32, 0x3E, 0x2D, 0x31, 0x32, 0x0A,
#endif
0x00, 0x89, 0x54, 0x40, 0x01, 0x12, 0xA8, 0x80, 0x00, 0x00, 0x00, 0x00,
/* Etc/GMT-13 */
0x50, 0x48, 0x50, 0x32, 0x00, 0x3F, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0xB6, 0xD0,
-0x00, 0x00, 0x47, 0x4D, 0x54, 0x2D, 0x31, 0x33, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x07, 0x80, 0x00, 0x00, 0x00,
+0x7F, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xB6, 0xD0, 0x00, 0x00, 0x47, 0x4D, 0x54, 0x2D,
+0x31, 0x33, 0x00, 0x00, 0x00,
#ifdef TIMELIB_SUPPORTS_V2DATA
0x54, 0x5A, 0x69, 0x66, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x07, 0xF8, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xB6, 0xD0, 0x00, 0x00, 0x47, 0x4D, 0x54, 0x2D, 0x31,
-0x33, 0x00, 0x00, 0x00, 0x0A, 0x3C, 0x47, 0x4D, 0x54, 0x2D, 0x31, 0x33, 0x3E, 0x2D, 0x31, 0x33,
-0x0A,
+0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x07, 0xF8, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
+0xB6, 0xD0, 0x00, 0x00, 0x47, 0x4D, 0x54, 0x2D, 0x31, 0x33, 0x00, 0x00, 0x00, 0x0A, 0x3C, 0x47,
+0x4D, 0x54, 0x2D, 0x31, 0x33, 0x3E, 0x2D, 0x31, 0x33, 0x0A,
#endif
0x00, 0x89, 0x54, 0x40, 0x01, 0x12, 0xA8, 0x80, 0x00, 0x00, 0x00, 0x00,
/* Etc/GMT-14 */
0x50, 0x48, 0x50, 0x32, 0x00, 0x3F, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0xC4, 0xE0,
-0x00, 0x00, 0x47, 0x4D, 0x54, 0x2D, 0x31, 0x34, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x07, 0x80, 0x00, 0x00, 0x00,
+0x7F, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xC4, 0xE0, 0x00, 0x00, 0x47, 0x4D, 0x54, 0x2D,
+0x31, 0x34, 0x00, 0x00, 0x00,
#ifdef TIMELIB_SUPPORTS_V2DATA
0x54, 0x5A, 0x69, 0x66, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x07, 0xF8, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC4, 0xE0, 0x00, 0x00, 0x47, 0x4D, 0x54, 0x2D, 0x31,
-0x34, 0x00, 0x00, 0x00, 0x0A, 0x3C, 0x47, 0x4D, 0x54, 0x2D, 0x31, 0x34, 0x3E, 0x2D, 0x31, 0x34,
-0x0A,
+0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x07, 0xF8, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
+0xC4, 0xE0, 0x00, 0x00, 0x47, 0x4D, 0x54, 0x2D, 0x31, 0x34, 0x00, 0x00, 0x00, 0x0A, 0x3C, 0x47,
+0x4D, 0x54, 0x2D, 0x31, 0x34, 0x3E, 0x2D, 0x31, 0x34, 0x0A,
#endif
0x00, 0x89, 0x54, 0x40, 0x01, 0x12, 0xA8, 0x80, 0x00, 0x00, 0x00, 0x00,
/* Etc/GMT-2 */
0x50, 0x48, 0x50, 0x32, 0x00, 0x3F, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x1C, 0x20,
-0x00, 0x00, 0x47, 0x4D, 0x54, 0x2D, 0x32, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0x80, 0x00, 0x00, 0x00,
+0x7F, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x20, 0x00, 0x00, 0x47, 0x4D, 0x54, 0x2D,
+0x32, 0x00, 0x00, 0x00,
#ifdef TIMELIB_SUPPORTS_V2DATA
0x54, 0x5A, 0x69, 0x66, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0xF8, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x20, 0x00, 0x00, 0x47, 0x4D, 0x54, 0x2D, 0x32,
-0x00, 0x00, 0x00, 0x0A, 0x3C, 0x47, 0x4D, 0x54, 0x2D, 0x32, 0x3E, 0x2D, 0x32, 0x0A,
+0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0xF8, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
+0x1C, 0x20, 0x00, 0x00, 0x47, 0x4D, 0x54, 0x2D, 0x32, 0x00, 0x00, 0x00, 0x0A, 0x3C, 0x47, 0x4D,
+0x54, 0x2D, 0x32, 0x3E, 0x2D, 0x32, 0x0A,
#endif
0x00, 0x89, 0x54, 0x40, 0x01, 0x12, 0xA8, 0x80, 0x00, 0x00, 0x00, 0x00,
/* Etc/GMT-3 */
0x50, 0x48, 0x50, 0x32, 0x00, 0x3F, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x2A, 0x30,
-0x00, 0x00, 0x47, 0x4D, 0x54, 0x2D, 0x33, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0x80, 0x00, 0x00, 0x00,
+0x7F, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x2A, 0x30, 0x00, 0x00, 0x47, 0x4D, 0x54, 0x2D,
+0x33, 0x00, 0x00, 0x00,
#ifdef TIMELIB_SUPPORTS_V2DATA
0x54, 0x5A, 0x69, 0x66, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0xF8, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2A, 0x30, 0x00, 0x00, 0x47, 0x4D, 0x54, 0x2D, 0x33,
-0x00, 0x00, 0x00, 0x0A, 0x3C, 0x47, 0x4D, 0x54, 0x2D, 0x33, 0x3E, 0x2D, 0x33, 0x0A,
+0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0xF8, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
+0x2A, 0x30, 0x00, 0x00, 0x47, 0x4D, 0x54, 0x2D, 0x33, 0x00, 0x00, 0x00, 0x0A, 0x3C, 0x47, 0x4D,
+0x54, 0x2D, 0x33, 0x3E, 0x2D, 0x33, 0x0A,
#endif
0x00, 0x89, 0x54, 0x40, 0x01, 0x12, 0xA8, 0x80, 0x00, 0x00, 0x00, 0x00,
/* Etc/GMT-4 */
0x50, 0x48, 0x50, 0x32, 0x00, 0x3F, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x38, 0x40,
-0x00, 0x00, 0x47, 0x4D, 0x54, 0x2D, 0x34, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0x80, 0x00, 0x00, 0x00,
+0x7F, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x38, 0x40, 0x00, 0x00, 0x47, 0x4D, 0x54, 0x2D,
+0x34, 0x00, 0x00, 0x00,
#ifdef TIMELIB_SUPPORTS_V2DATA
0x54, 0x5A, 0x69, 0x66, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0xF8, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x40, 0x00, 0x00, 0x47, 0x4D, 0x54, 0x2D, 0x34,
-0x00, 0x00, 0x00, 0x0A, 0x3C, 0x47, 0x4D, 0x54, 0x2D, 0x34, 0x3E, 0x2D, 0x34, 0x0A,
+0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0xF8, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
+0x38, 0x40, 0x00, 0x00, 0x47, 0x4D, 0x54, 0x2D, 0x34, 0x00, 0x00, 0x00, 0x0A, 0x3C, 0x47, 0x4D,
+0x54, 0x2D, 0x34, 0x3E, 0x2D, 0x34, 0x0A,
#endif
0x00, 0x89, 0x54, 0x40, 0x01, 0x12, 0xA8, 0x80, 0x00, 0x00, 0x00, 0x00,
/* Etc/GMT-5 */
0x50, 0x48, 0x50, 0x32, 0x00, 0x3F, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x46, 0x50,
-0x00, 0x00, 0x47, 0x4D, 0x54, 0x2D, 0x35, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0x80, 0x00, 0x00, 0x00,
+0x7F, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x46, 0x50, 0x00, 0x00, 0x47, 0x4D, 0x54, 0x2D,
+0x35, 0x00, 0x00, 0x00,
#ifdef TIMELIB_SUPPORTS_V2DATA
0x54, 0x5A, 0x69, 0x66, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0xF8, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x50, 0x00, 0x00, 0x47, 0x4D, 0x54, 0x2D, 0x35,
-0x00, 0x00, 0x00, 0x0A, 0x3C, 0x47, 0x4D, 0x54, 0x2D, 0x35, 0x3E, 0x2D, 0x35, 0x0A,
+0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0xF8, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
+0x46, 0x50, 0x00, 0x00, 0x47, 0x4D, 0x54, 0x2D, 0x35, 0x00, 0x00, 0x00, 0x0A, 0x3C, 0x47, 0x4D,
+0x54, 0x2D, 0x35, 0x3E, 0x2D, 0x35, 0x0A,
#endif
0x00, 0x89, 0x54, 0x40, 0x01, 0x12, 0xA8, 0x80, 0x00, 0x00, 0x00, 0x00,
/* Etc/GMT-6 */
0x50, 0x48, 0x50, 0x32, 0x00, 0x3F, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x54, 0x60,
-0x00, 0x00, 0x47, 0x4D, 0x54, 0x2D, 0x36, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0x80, 0x00, 0x00, 0x00,
+0x7F, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x54, 0x60, 0x00, 0x00, 0x47, 0x4D, 0x54, 0x2D,
+0x36, 0x00, 0x00, 0x00,
#ifdef TIMELIB_SUPPORTS_V2DATA
0x54, 0x5A, 0x69, 0x66, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0xF8, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0x60, 0x00, 0x00, 0x47, 0x4D, 0x54, 0x2D, 0x36,
-0x00, 0x00, 0x00, 0x0A, 0x3C, 0x47, 0x4D, 0x54, 0x2D, 0x36, 0x3E, 0x2D, 0x36, 0x0A,
+0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0xF8, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
+0x54, 0x60, 0x00, 0x00, 0x47, 0x4D, 0x54, 0x2D, 0x36, 0x00, 0x00, 0x00, 0x0A, 0x3C, 0x47, 0x4D,
+0x54, 0x2D, 0x36, 0x3E, 0x2D, 0x36, 0x0A,
#endif
0x00, 0x89, 0x54, 0x40, 0x01, 0x12, 0xA8, 0x80, 0x00, 0x00, 0x00, 0x00,
/* Etc/GMT-7 */
0x50, 0x48, 0x50, 0x32, 0x00, 0x3F, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x62, 0x70,
-0x00, 0x00, 0x47, 0x4D, 0x54, 0x2D, 0x37, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0x80, 0x00, 0x00, 0x00,
+0x7F, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x62, 0x70, 0x00, 0x00, 0x47, 0x4D, 0x54, 0x2D,
+0x37, 0x00, 0x00, 0x00,
#ifdef TIMELIB_SUPPORTS_V2DATA
0x54, 0x5A, 0x69, 0x66, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0xF8, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x62, 0x70, 0x00, 0x00, 0x47, 0x4D, 0x54, 0x2D, 0x37,
-0x00, 0x00, 0x00, 0x0A, 0x3C, 0x47, 0x4D, 0x54, 0x2D, 0x37, 0x3E, 0x2D, 0x37, 0x0A,
+0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0xF8, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
+0x62, 0x70, 0x00, 0x00, 0x47, 0x4D, 0x54, 0x2D, 0x37, 0x00, 0x00, 0x00, 0x0A, 0x3C, 0x47, 0x4D,
+0x54, 0x2D, 0x37, 0x3E, 0x2D, 0x37, 0x0A,
#endif
0x00, 0x89, 0x54, 0x40, 0x01, 0x12, 0xA8, 0x80, 0x00, 0x00, 0x00, 0x00,
/* Etc/GMT-8 */
0x50, 0x48, 0x50, 0x32, 0x00, 0x3F, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x70, 0x80,
-0x00, 0x00, 0x47, 0x4D, 0x54, 0x2D, 0x38, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0x80, 0x00, 0x00, 0x00,
+0x7F, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x70, 0x80, 0x00, 0x00, 0x47, 0x4D, 0x54, 0x2D,
+0x38, 0x00, 0x00, 0x00,
#ifdef TIMELIB_SUPPORTS_V2DATA
0x54, 0x5A, 0x69, 0x66, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0xF8, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x80, 0x00, 0x00, 0x47, 0x4D, 0x54, 0x2D, 0x38,
-0x00, 0x00, 0x00, 0x0A, 0x3C, 0x47, 0x4D, 0x54, 0x2D, 0x38, 0x3E, 0x2D, 0x38, 0x0A,
+0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0xF8, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
+0x70, 0x80, 0x00, 0x00, 0x47, 0x4D, 0x54, 0x2D, 0x38, 0x00, 0x00, 0x00, 0x0A, 0x3C, 0x47, 0x4D,
+0x54, 0x2D, 0x38, 0x3E, 0x2D, 0x38, 0x0A,
#endif
0x00, 0x89, 0x54, 0x40, 0x01, 0x12, 0xA8, 0x80, 0x00, 0x00, 0x00, 0x00,
/* Etc/GMT-9 */
0x50, 0x48, 0x50, 0x32, 0x00, 0x3F, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x7E, 0x90,
-0x00, 0x00, 0x47, 0x4D, 0x54, 0x2D, 0x39, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0x80, 0x00, 0x00, 0x00,
+0x7F, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x7E, 0x90, 0x00, 0x00, 0x47, 0x4D, 0x54, 0x2D,
+0x39, 0x00, 0x00, 0x00,
#ifdef TIMELIB_SUPPORTS_V2DATA
0x54, 0x5A, 0x69, 0x66, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0xF8, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7E, 0x90, 0x00, 0x00, 0x47, 0x4D, 0x54, 0x2D, 0x39,
-0x00, 0x00, 0x00, 0x0A, 0x3C, 0x47, 0x4D, 0x54, 0x2D, 0x39, 0x3E, 0x2D, 0x39, 0x0A,
+0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0xF8, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
+0x7E, 0x90, 0x00, 0x00, 0x47, 0x4D, 0x54, 0x2D, 0x39, 0x00, 0x00, 0x00, 0x0A, 0x3C, 0x47, 0x4D,
+0x54, 0x2D, 0x39, 0x3E, 0x2D, 0x39, 0x0A,
#endif
0x00, 0x89, 0x54, 0x40, 0x01, 0x12, 0xA8, 0x80, 0x00, 0x00, 0x00, 0x00,
@@ -31809,14 +32101,14 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
/* Europe/Astrakhan */
0x50, 0x48, 0x50, 0x32, 0x01, 0x52, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x10, 0x80, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x10, 0x80, 0x00, 0x00, 0x00,
0xAA, 0x18, 0x45, 0x74, 0xB5, 0xA4, 0x0B, 0x50, 0x15, 0x27, 0x99, 0xC0, 0x16, 0x18, 0xCE, 0x30,
0x17, 0x08, 0xCD, 0x40, 0x17, 0xFA, 0x01, 0xB0, 0x18, 0xEA, 0x00, 0xC0, 0x19, 0xDB, 0x35, 0x30,
0x1A, 0xCC, 0x85, 0xC0, 0x1B, 0xBC, 0x92, 0xE0, 0x1C, 0xAC, 0x83, 0xE0, 0x1D, 0x9C, 0x74, 0xE0,
0x1E, 0x8C, 0x65, 0xE0, 0x1F, 0x7C, 0x56, 0xE0, 0x20, 0x6C, 0x47, 0xE0, 0x21, 0x5C, 0x38, 0xE0,
0x22, 0x4C, 0x29, 0xE0, 0x23, 0x3C, 0x1A, 0xE0, 0x24, 0x2C, 0x0B, 0xE0, 0x25, 0x1C, 0x0A, 0xF0,
0x26, 0x0B, 0xFB, 0xF0, 0x27, 0x05, 0x27, 0x70, 0x27, 0xF5, 0x18, 0x70, 0x29, 0xD4, 0xEC, 0x60,
-0x2A, 0xC4, 0xB3, 0x30, 0x2B, 0xB4, 0xDC, 0x70, 0x2C, 0xA4, 0xCD, 0x70, 0x2D, 0x94, 0xBE, 0x70,
+0x2A, 0xC4, 0xEB, 0x70, 0x2B, 0xB4, 0xDC, 0x70, 0x2C, 0xA4, 0xCD, 0x70, 0x2D, 0x94, 0xBE, 0x70,
0x2E, 0x84, 0xAF, 0x70, 0x2F, 0x74, 0xA0, 0x70, 0x30, 0x64, 0x91, 0x70, 0x31, 0x5D, 0xBC, 0xF0,
0x32, 0x72, 0x97, 0xF0, 0x33, 0x3D, 0x9E, 0xF0, 0x34, 0x52, 0x79, 0xF0, 0x35, 0x1D, 0x80, 0xF0,
0x36, 0x32, 0x5B, 0xF0, 0x36, 0xFD, 0x62, 0xF0, 0x38, 0x1B, 0x78, 0x70, 0x38, 0xDD, 0x44, 0xF0,
@@ -31826,20 +32118,20 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x45, 0x43, 0xE0, 0xF0, 0x46, 0x05, 0xAD, 0x70, 0x47, 0x23, 0xC2, 0xF0, 0x47, 0xEE, 0xC9, 0xF0,
0x49, 0x03, 0xA4, 0xF0, 0x49, 0xCE, 0xAB, 0xF0, 0x4A, 0xE3, 0x86, 0xF0, 0x4B, 0xAE, 0x8D, 0xF0,
0x4C, 0xCC, 0xA3, 0x70, 0x4D, 0x8E, 0x6F, 0xF0, 0x54, 0x4C, 0x1D, 0x60, 0x56, 0xF7, 0x14, 0x70,
-0x00, 0x01, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05,
-0x04, 0x05, 0x04, 0x06, 0x07, 0x06, 0x07, 0x04, 0x06, 0x01, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07,
+0x7F, 0xFF, 0xFF, 0xFF, 0x00, 0x01, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x04, 0x05,
+0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x06, 0x07, 0x06, 0x07, 0x04, 0x06, 0x07, 0x06, 0x07,
0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07,
-0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x04, 0x07,
-0x04, 0x00, 0x00, 0x2D, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x2A, 0x30, 0x00, 0x04, 0x00, 0x00, 0x46,
-0x50, 0x01, 0x08, 0x00, 0x00, 0x38, 0x40, 0x00, 0x0C, 0x00, 0x00, 0x38, 0x40, 0x00, 0x0C, 0x00,
-0x00, 0x46, 0x50, 0x01, 0x08, 0x00, 0x00, 0x38, 0x40, 0x01, 0x0C, 0x00, 0x00, 0x2A, 0x30, 0x00,
-0x04, 0x00, 0x00, 0x38, 0x40, 0x00, 0x0C, 0x4C, 0x4D, 0x54, 0x00, 0x2B, 0x30, 0x33, 0x00, 0x2B,
-0x30, 0x35, 0x00, 0x2B, 0x30, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07,
+0x06, 0x07, 0x04, 0x07, 0x04, 0x04, 0x00, 0x00, 0x2D, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x2A, 0x30,
+0x00, 0x04, 0x00, 0x00, 0x46, 0x50, 0x01, 0x08, 0x00, 0x00, 0x38, 0x40, 0x00, 0x0C, 0x00, 0x00,
+0x38, 0x40, 0x00, 0x0C, 0x00, 0x00, 0x46, 0x50, 0x01, 0x08, 0x00, 0x00, 0x38, 0x40, 0x01, 0x0C,
+0x00, 0x00, 0x2A, 0x30, 0x00, 0x04, 0x00, 0x00, 0x38, 0x40, 0x00, 0x0C, 0x4C, 0x4D, 0x54, 0x00,
+0x2B, 0x30, 0x33, 0x00, 0x2B, 0x30, 0x35, 0x00, 0x2B, 0x30, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
#ifdef TIMELIB_SUPPORTS_V2DATA
0x54, 0x5A, 0x69, 0x66, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x10, 0xF8, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x10, 0xF8, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xAA, 0x18, 0x45, 0x74, 0xFF, 0xFF, 0xFF, 0xFF,
0xB5, 0xA4, 0x0B, 0x50, 0x00, 0x00, 0x00, 0x00, 0x15, 0x27, 0x99, 0xC0, 0x00, 0x00, 0x00, 0x00,
0x16, 0x18, 0xCE, 0x30, 0x00, 0x00, 0x00, 0x00, 0x17, 0x08, 0xCD, 0x40, 0x00, 0x00, 0x00, 0x00,
@@ -31852,7 +32144,7 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x23, 0x3C, 0x1A, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x24, 0x2C, 0x0B, 0xE0, 0x00, 0x00, 0x00, 0x00,
0x25, 0x1C, 0x0A, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x26, 0x0B, 0xFB, 0xF0, 0x00, 0x00, 0x00, 0x00,
0x27, 0x05, 0x27, 0x70, 0x00, 0x00, 0x00, 0x00, 0x27, 0xF5, 0x18, 0x70, 0x00, 0x00, 0x00, 0x00,
-0x29, 0xD4, 0xEC, 0x60, 0x00, 0x00, 0x00, 0x00, 0x2A, 0xC4, 0xB3, 0x30, 0x00, 0x00, 0x00, 0x00,
+0x29, 0xD4, 0xEC, 0x60, 0x00, 0x00, 0x00, 0x00, 0x2A, 0xC4, 0xEB, 0x70, 0x00, 0x00, 0x00, 0x00,
0x2B, 0xB4, 0xDC, 0x70, 0x00, 0x00, 0x00, 0x00, 0x2C, 0xA4, 0xCD, 0x70, 0x00, 0x00, 0x00, 0x00,
0x2D, 0x94, 0xBE, 0x70, 0x00, 0x00, 0x00, 0x00, 0x2E, 0x84, 0xAF, 0x70, 0x00, 0x00, 0x00, 0x00,
0x2F, 0x74, 0xA0, 0x70, 0x00, 0x00, 0x00, 0x00, 0x30, 0x64, 0x91, 0x70, 0x00, 0x00, 0x00, 0x00,
@@ -31872,17 +32164,17 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x49, 0xCE, 0xAB, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x4A, 0xE3, 0x86, 0xF0, 0x00, 0x00, 0x00, 0x00,
0x4B, 0xAE, 0x8D, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x4C, 0xCC, 0xA3, 0x70, 0x00, 0x00, 0x00, 0x00,
0x4D, 0x8E, 0x6F, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x54, 0x4C, 0x1D, 0x60, 0x00, 0x00, 0x00, 0x00,
-0x56, 0xF7, 0x14, 0x70, 0x00, 0x01, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x04, 0x05,
-0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x06, 0x07, 0x06, 0x07, 0x04, 0x06, 0x01, 0x06, 0x07,
+0x56, 0xF7, 0x14, 0x70, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0x00, 0x01, 0x03, 0x02,
+0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x06,
+0x07, 0x06, 0x07, 0x04, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07,
0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07,
-0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07,
-0x06, 0x07, 0x04, 0x07, 0x04, 0x00, 0x00, 0x2D, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x2A, 0x30, 0x00,
-0x04, 0x00, 0x00, 0x46, 0x50, 0x01, 0x08, 0x00, 0x00, 0x38, 0x40, 0x00, 0x0C, 0x00, 0x00, 0x38,
-0x40, 0x00, 0x0C, 0x00, 0x00, 0x46, 0x50, 0x01, 0x08, 0x00, 0x00, 0x38, 0x40, 0x01, 0x0C, 0x00,
-0x00, 0x2A, 0x30, 0x00, 0x04, 0x00, 0x00, 0x38, 0x40, 0x00, 0x0C, 0x4C, 0x4D, 0x54, 0x00, 0x2B,
-0x30, 0x33, 0x00, 0x2B, 0x30, 0x35, 0x00, 0x2B, 0x30, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
-0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x3C, 0x2B,
-0x30, 0x34, 0x3E, 0x2D, 0x34, 0x0A,
+0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x04, 0x07, 0x04, 0x04, 0x00, 0x00,
+0x2D, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x2A, 0x30, 0x00, 0x04, 0x00, 0x00, 0x46, 0x50, 0x01, 0x08,
+0x00, 0x00, 0x38, 0x40, 0x00, 0x0C, 0x00, 0x00, 0x38, 0x40, 0x00, 0x0C, 0x00, 0x00, 0x46, 0x50,
+0x01, 0x08, 0x00, 0x00, 0x38, 0x40, 0x01, 0x0C, 0x00, 0x00, 0x2A, 0x30, 0x00, 0x04, 0x00, 0x00,
+0x38, 0x40, 0x00, 0x0C, 0x4C, 0x4D, 0x54, 0x00, 0x2B, 0x30, 0x33, 0x00, 0x2B, 0x30, 0x35, 0x00,
+0x2B, 0x30, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x3C, 0x2B, 0x30, 0x34, 0x3E, 0x2D, 0x34, 0x0A,
#endif
0x00, 0xD0, 0x0D, 0xB8, 0x01, 0x5B, 0xFA, 0x08, 0x00, 0x00, 0x00, 0x12, 0x4D, 0x53, 0x4B, 0x2B,
0x30, 0x31, 0x20, 0x2D, 0x20, 0x41, 0x73, 0x74, 0x72, 0x61, 0x6B, 0x68, 0x61, 0x6E,
@@ -35058,8 +35350,8 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
/* Europe/Kaliningrad */
0x50, 0x48, 0x50, 0x32, 0x01, 0x52, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x4F, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x22, 0x80, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x4F, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x22, 0x80, 0x00, 0x00, 0x00,
0x9B, 0x0C, 0x17, 0x60, 0x9B, 0xD5, 0xDA, 0xF0, 0x9C, 0xD9, 0xAE, 0x90, 0x9D, 0xA4, 0xB5, 0x90,
0x9E, 0xB9, 0x90, 0x90, 0x9F, 0x84, 0x97, 0x90, 0xC8, 0x09, 0x71, 0x90, 0xCC, 0xE7, 0x4B, 0x10,
0xCD, 0xA9, 0x17, 0x90, 0xCE, 0xA2, 0x43, 0x10, 0xCF, 0x92, 0x34, 0x10, 0xD0, 0x82, 0x25, 0x10,
@@ -35069,7 +35361,7 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x1C, 0xAC, 0x91, 0xF0, 0x1D, 0x9C, 0x82, 0xF0, 0x1E, 0x8C, 0x73, 0xF0, 0x1F, 0x7C, 0x64, 0xF0,
0x20, 0x6C, 0x55, 0xF0, 0x21, 0x5C, 0x46, 0xF0, 0x22, 0x4C, 0x37, 0xF0, 0x23, 0x3C, 0x28, 0xF0,
0x24, 0x2C, 0x19, 0xF0, 0x25, 0x1C, 0x19, 0x00, 0x26, 0x0C, 0x0A, 0x00, 0x27, 0x05, 0x35, 0x80,
-0x27, 0xF5, 0x26, 0x80, 0x28, 0xE5, 0x17, 0x80, 0x29, 0xD4, 0xDE, 0x50, 0x2A, 0xC4, 0xC1, 0x40,
+0x27, 0xF5, 0x26, 0x80, 0x28, 0xE5, 0x17, 0x80, 0x29, 0xD5, 0x08, 0x80, 0x2A, 0xC4, 0xF9, 0x80,
0x2B, 0xB4, 0xEA, 0x80, 0x2C, 0xA4, 0xDB, 0x80, 0x2D, 0x94, 0xCC, 0x80, 0x2E, 0x84, 0xBD, 0x80,
0x2F, 0x74, 0xAE, 0x80, 0x30, 0x64, 0x9F, 0x80, 0x31, 0x5D, 0xCB, 0x00, 0x32, 0x72, 0xA6, 0x00,
0x33, 0x3D, 0xAD, 0x00, 0x34, 0x52, 0x88, 0x00, 0x35, 0x1D, 0x8F, 0x00, 0x36, 0x32, 0x6A, 0x00,
@@ -35081,25 +35373,24 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x49, 0xCE, 0xBA, 0x00, 0x4A, 0xE3, 0x95, 0x00, 0x4B, 0xAE, 0x9C, 0x00, 0x4C, 0xCC, 0xB1, 0x80,
0x4D, 0x8E, 0x7E, 0x00, 0x54, 0x4C, 0x2B, 0x70, 0x02, 0x01, 0x02, 0x03, 0x04, 0x03, 0x04, 0x03,
0x04, 0x03, 0x04, 0x03, 0x04, 0x06, 0x05, 0x06, 0x08, 0x07, 0x08, 0x07, 0x08, 0x07, 0x08, 0x07,
-0x09, 0x0A, 0x09, 0x0A, 0x09, 0x0A, 0x09, 0x0A, 0x09, 0x0B, 0x0C, 0x0B, 0x0C, 0x0B, 0x0C, 0x0D,
-0x0E, 0x0B, 0x0C, 0x0B, 0x0C, 0x0B, 0x0C, 0x0B, 0x0C, 0x0B, 0x0C, 0x0B, 0x0C, 0x0B, 0x0C, 0x0B,
+0x09, 0x0A, 0x09, 0x0A, 0x09, 0x0A, 0x09, 0x0A, 0x09, 0x0B, 0x0C, 0x0B, 0x0C, 0x0B, 0x0C, 0x0B,
+0x0C, 0x0B, 0x0C, 0x0B, 0x0C, 0x0B, 0x0C, 0x0B, 0x0C, 0x0B, 0x0C, 0x0B, 0x0C, 0x0B, 0x0C, 0x0B,
0x0C, 0x0B, 0x0C, 0x0B, 0x0C, 0x0B, 0x0C, 0x0B, 0x0C, 0x0B, 0x0C, 0x0B, 0x0C, 0x0B, 0x0C, 0x0B,
-0x0C, 0x0B, 0x0C, 0x0B, 0x0C, 0x0F, 0x0C, 0x00, 0x00, 0x13, 0x38, 0x00, 0x00, 0x00, 0x00, 0x1C,
+0x0C, 0x0B, 0x0C, 0x0B, 0x0C, 0x0D, 0x0C, 0x00, 0x00, 0x13, 0x38, 0x00, 0x00, 0x00, 0x00, 0x1C,
0x20, 0x01, 0x04, 0x00, 0x00, 0x0E, 0x10, 0x00, 0x09, 0x00, 0x00, 0x1C, 0x20, 0x01, 0x04, 0x00,
0x00, 0x0E, 0x10, 0x00, 0x09, 0x00, 0x00, 0x2A, 0x30, 0x01, 0x04, 0x00, 0x00, 0x1C, 0x20, 0x00,
0x09, 0x00, 0x00, 0x38, 0x40, 0x01, 0x0D, 0x00, 0x00, 0x2A, 0x30, 0x00, 0x11, 0x00, 0x00, 0x2A,
0x30, 0x00, 0x11, 0x00, 0x00, 0x38, 0x40, 0x01, 0x0D, 0x00, 0x00, 0x2A, 0x30, 0x01, 0x15, 0x00,
-0x00, 0x1C, 0x20, 0x00, 0x1A, 0x00, 0x00, 0x2A, 0x30, 0x01, 0x15, 0x00, 0x00, 0x1C, 0x20, 0x00,
-0x1A, 0x00, 0x00, 0x2A, 0x30, 0x00, 0x1E, 0x00, 0x00, 0x1C, 0x20, 0x00, 0x1A, 0x4C, 0x4D, 0x54,
-0x00, 0x43, 0x45, 0x53, 0x54, 0x00, 0x43, 0x45, 0x54, 0x00, 0x4D, 0x53, 0x44, 0x00, 0x4D, 0x53,
-0x4B, 0x00, 0x45, 0x45, 0x53, 0x54, 0x00, 0x45, 0x45, 0x54, 0x00, 0x46, 0x45, 0x54, 0x00, 0x00,
-0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x1C, 0x20, 0x00, 0x1A, 0x00, 0x00, 0x2A, 0x30, 0x00, 0x1E, 0x00, 0x00, 0x1C, 0x20, 0x00,
+0x1A, 0x4C, 0x4D, 0x54, 0x00, 0x43, 0x45, 0x53, 0x54, 0x00, 0x43, 0x45, 0x54, 0x00, 0x4D, 0x53,
+0x44, 0x00, 0x4D, 0x53, 0x4B, 0x00, 0x45, 0x45, 0x53, 0x54, 0x00, 0x45, 0x45, 0x54, 0x00, 0x46,
+0x45, 0x54, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00,
#ifdef TIMELIB_SUPPORTS_V2DATA
0x54, 0x5A, 0x69, 0x66, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x22, 0xF8, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x22, 0xF8, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x6F, 0xA2, 0x5B, 0x48, 0xFF, 0xFF, 0xFF, 0xFF,
0x9B, 0x0C, 0x17, 0x60, 0xFF, 0xFF, 0xFF, 0xFF, 0x9B, 0xD5, 0xDA, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF,
0x9C, 0xD9, 0xAE, 0x90, 0xFF, 0xFF, 0xFF, 0xFF, 0x9D, 0xA4, 0xB5, 0x90, 0xFF, 0xFF, 0xFF, 0xFF,
@@ -35120,7 +35411,7 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x24, 0x2C, 0x19, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x25, 0x1C, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00,
0x26, 0x0C, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x27, 0x05, 0x35, 0x80, 0x00, 0x00, 0x00, 0x00,
0x27, 0xF5, 0x26, 0x80, 0x00, 0x00, 0x00, 0x00, 0x28, 0xE5, 0x17, 0x80, 0x00, 0x00, 0x00, 0x00,
-0x29, 0xD4, 0xDE, 0x50, 0x00, 0x00, 0x00, 0x00, 0x2A, 0xC4, 0xC1, 0x40, 0x00, 0x00, 0x00, 0x00,
+0x29, 0xD5, 0x08, 0x80, 0x00, 0x00, 0x00, 0x00, 0x2A, 0xC4, 0xF9, 0x80, 0x00, 0x00, 0x00, 0x00,
0x2B, 0xB4, 0xEA, 0x80, 0x00, 0x00, 0x00, 0x00, 0x2C, 0xA4, 0xDB, 0x80, 0x00, 0x00, 0x00, 0x00,
0x2D, 0x94, 0xCC, 0x80, 0x00, 0x00, 0x00, 0x00, 0x2E, 0x84, 0xBD, 0x80, 0x00, 0x00, 0x00, 0x00,
0x2F, 0x74, 0xAE, 0x80, 0x00, 0x00, 0x00, 0x00, 0x30, 0x64, 0x9F, 0x80, 0x00, 0x00, 0x00, 0x00,
@@ -35142,19 +35433,18 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x4D, 0x8E, 0x7E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0x4C, 0x2B, 0x70, 0x00, 0x02, 0x01, 0x02,
0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x06, 0x05, 0x06, 0x08, 0x07, 0x08,
0x07, 0x08, 0x07, 0x08, 0x07, 0x09, 0x0A, 0x09, 0x0A, 0x09, 0x0A, 0x09, 0x0A, 0x09, 0x0B, 0x0C,
-0x0B, 0x0C, 0x0B, 0x0C, 0x0D, 0x0E, 0x0B, 0x0C, 0x0B, 0x0C, 0x0B, 0x0C, 0x0B, 0x0C, 0x0B, 0x0C,
0x0B, 0x0C, 0x0B, 0x0C, 0x0B, 0x0C, 0x0B, 0x0C, 0x0B, 0x0C, 0x0B, 0x0C, 0x0B, 0x0C, 0x0B, 0x0C,
-0x0B, 0x0C, 0x0B, 0x0C, 0x0B, 0x0C, 0x0B, 0x0C, 0x0B, 0x0C, 0x0F, 0x0C, 0x00, 0x00, 0x13, 0x38,
+0x0B, 0x0C, 0x0B, 0x0C, 0x0B, 0x0C, 0x0B, 0x0C, 0x0B, 0x0C, 0x0B, 0x0C, 0x0B, 0x0C, 0x0B, 0x0C,
+0x0B, 0x0C, 0x0B, 0x0C, 0x0B, 0x0C, 0x0B, 0x0C, 0x0B, 0x0C, 0x0D, 0x0C, 0x00, 0x00, 0x13, 0x38,
0x00, 0x00, 0x00, 0x00, 0x1C, 0x20, 0x01, 0x04, 0x00, 0x00, 0x0E, 0x10, 0x00, 0x09, 0x00, 0x00,
0x1C, 0x20, 0x01, 0x04, 0x00, 0x00, 0x0E, 0x10, 0x00, 0x09, 0x00, 0x00, 0x2A, 0x30, 0x01, 0x04,
0x00, 0x00, 0x1C, 0x20, 0x00, 0x09, 0x00, 0x00, 0x38, 0x40, 0x01, 0x0D, 0x00, 0x00, 0x2A, 0x30,
0x00, 0x11, 0x00, 0x00, 0x2A, 0x30, 0x00, 0x11, 0x00, 0x00, 0x38, 0x40, 0x01, 0x0D, 0x00, 0x00,
-0x2A, 0x30, 0x01, 0x15, 0x00, 0x00, 0x1C, 0x20, 0x00, 0x1A, 0x00, 0x00, 0x2A, 0x30, 0x01, 0x15,
-0x00, 0x00, 0x1C, 0x20, 0x00, 0x1A, 0x00, 0x00, 0x2A, 0x30, 0x00, 0x1E, 0x00, 0x00, 0x1C, 0x20,
-0x00, 0x1A, 0x4C, 0x4D, 0x54, 0x00, 0x43, 0x45, 0x53, 0x54, 0x00, 0x43, 0x45, 0x54, 0x00, 0x4D,
-0x53, 0x44, 0x00, 0x4D, 0x53, 0x4B, 0x00, 0x45, 0x45, 0x53, 0x54, 0x00, 0x45, 0x45, 0x54, 0x00,
-0x46, 0x45, 0x54, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01,
-0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x2A, 0x30, 0x01, 0x15, 0x00, 0x00, 0x1C, 0x20, 0x00, 0x1A, 0x00, 0x00, 0x2A, 0x30, 0x00, 0x1E,
+0x00, 0x00, 0x1C, 0x20, 0x00, 0x1A, 0x4C, 0x4D, 0x54, 0x00, 0x43, 0x45, 0x53, 0x54, 0x00, 0x43,
+0x45, 0x54, 0x00, 0x4D, 0x53, 0x44, 0x00, 0x4D, 0x53, 0x4B, 0x00, 0x45, 0x45, 0x53, 0x54, 0x00,
+0x45, 0x45, 0x54, 0x00, 0x46, 0x45, 0x54, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00,
+0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x45, 0x45, 0x54, 0x2D, 0x32, 0x0A,
#endif
0x00, 0xDC, 0xD1, 0xF2, 0x01, 0x31, 0xF0, 0x50, 0x00, 0x00, 0x00, 0x14, 0x4D, 0x53, 0x4B, 0x2D,
@@ -35300,6 +35590,86 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x69, 0x6E, 0x65, 0x20, 0x28, 0x6D, 0x6F, 0x73, 0x74, 0x20, 0x61, 0x72, 0x65, 0x61, 0x73, 0x29,
+/* Europe/Kirov */
+0x50, 0x48, 0x50, 0x32, 0x01, 0x52, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x10, 0x80, 0x00, 0x00, 0x00,
+0xA1, 0x00, 0x27, 0x08, 0xB5, 0xA4, 0x0B, 0x50, 0x15, 0x27, 0x99, 0xC0, 0x16, 0x18, 0xCE, 0x30,
+0x17, 0x08, 0xCD, 0x40, 0x17, 0xFA, 0x01, 0xB0, 0x18, 0xEA, 0x00, 0xC0, 0x19, 0xDB, 0x35, 0x30,
+0x1A, 0xCC, 0x85, 0xC0, 0x1B, 0xBC, 0x92, 0xE0, 0x1C, 0xAC, 0x83, 0xE0, 0x1D, 0x9C, 0x74, 0xE0,
+0x1E, 0x8C, 0x65, 0xE0, 0x1F, 0x7C, 0x56, 0xE0, 0x20, 0x6C, 0x47, 0xE0, 0x21, 0x5C, 0x38, 0xE0,
+0x22, 0x4C, 0x29, 0xE0, 0x23, 0x3C, 0x1A, 0xE0, 0x24, 0x2C, 0x0B, 0xE0, 0x25, 0x1C, 0x0A, 0xF0,
+0x26, 0x0B, 0xFB, 0xF0, 0x27, 0x05, 0x27, 0x70, 0x27, 0xF5, 0x18, 0x70, 0x29, 0xD4, 0xEC, 0x60,
+0x2A, 0xC4, 0xEB, 0x70, 0x2B, 0xB4, 0xDC, 0x70, 0x2C, 0xA4, 0xCD, 0x70, 0x2D, 0x94, 0xBE, 0x70,
+0x2E, 0x84, 0xAF, 0x70, 0x2F, 0x74, 0xA0, 0x70, 0x30, 0x64, 0x91, 0x70, 0x31, 0x5D, 0xBC, 0xF0,
+0x32, 0x72, 0x97, 0xF0, 0x33, 0x3D, 0x9E, 0xF0, 0x34, 0x52, 0x79, 0xF0, 0x35, 0x1D, 0x80, 0xF0,
+0x36, 0x32, 0x5B, 0xF0, 0x36, 0xFD, 0x62, 0xF0, 0x38, 0x1B, 0x78, 0x70, 0x38, 0xDD, 0x44, 0xF0,
+0x39, 0xFB, 0x5A, 0x70, 0x3A, 0xBD, 0x26, 0xF0, 0x3B, 0xDB, 0x3C, 0x70, 0x3C, 0xA6, 0x43, 0x70,
+0x3D, 0xBB, 0x1E, 0x70, 0x3E, 0x86, 0x25, 0x70, 0x3F, 0x9B, 0x00, 0x70, 0x40, 0x66, 0x07, 0x70,
+0x41, 0x84, 0x1C, 0xF0, 0x42, 0x45, 0xE9, 0x70, 0x43, 0x63, 0xFE, 0xF0, 0x44, 0x25, 0xCB, 0x70,
+0x45, 0x43, 0xE0, 0xF0, 0x46, 0x05, 0xAD, 0x70, 0x47, 0x23, 0xC2, 0xF0, 0x47, 0xEE, 0xC9, 0xF0,
+0x49, 0x03, 0xA4, 0xF0, 0x49, 0xCE, 0xAB, 0xF0, 0x4A, 0xE3, 0x86, 0xF0, 0x4B, 0xAE, 0x8D, 0xF0,
+0x4C, 0xCC, 0xA3, 0x70, 0x4D, 0x8E, 0x6F, 0xF0, 0x54, 0x4C, 0x1D, 0x60, 0x7F, 0xFF, 0xFF, 0xFF,
+0x00, 0x01, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05,
+0x04, 0x05, 0x04, 0x06, 0x07, 0x06, 0x07, 0x04, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07,
+0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07,
+0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x04, 0x07,
+0x07, 0x00, 0x00, 0x2E, 0x98, 0x00, 0x00, 0x00, 0x00, 0x2A, 0x30, 0x00, 0x04, 0x00, 0x00, 0x46,
+0x50, 0x01, 0x08, 0x00, 0x00, 0x38, 0x40, 0x00, 0x0C, 0x00, 0x00, 0x38, 0x40, 0x00, 0x0C, 0x00,
+0x00, 0x46, 0x50, 0x01, 0x08, 0x00, 0x00, 0x38, 0x40, 0x01, 0x0C, 0x00, 0x00, 0x2A, 0x30, 0x00,
+0x04, 0x4C, 0x4D, 0x54, 0x00, 0x2B, 0x30, 0x33, 0x00, 0x2B, 0x30, 0x35, 0x00, 0x2B, 0x30, 0x34,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00,
+#ifdef TIMELIB_SUPPORTS_V2DATA
+0x54, 0x5A, 0x69, 0x66, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x10, 0xF8, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xA1, 0x00, 0x27, 0x08, 0xFF, 0xFF, 0xFF, 0xFF,
+0xB5, 0xA4, 0x0B, 0x50, 0x00, 0x00, 0x00, 0x00, 0x15, 0x27, 0x99, 0xC0, 0x00, 0x00, 0x00, 0x00,
+0x16, 0x18, 0xCE, 0x30, 0x00, 0x00, 0x00, 0x00, 0x17, 0x08, 0xCD, 0x40, 0x00, 0x00, 0x00, 0x00,
+0x17, 0xFA, 0x01, 0xB0, 0x00, 0x00, 0x00, 0x00, 0x18, 0xEA, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00,
+0x19, 0xDB, 0x35, 0x30, 0x00, 0x00, 0x00, 0x00, 0x1A, 0xCC, 0x85, 0xC0, 0x00, 0x00, 0x00, 0x00,
+0x1B, 0xBC, 0x92, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x1C, 0xAC, 0x83, 0xE0, 0x00, 0x00, 0x00, 0x00,
+0x1D, 0x9C, 0x74, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x1E, 0x8C, 0x65, 0xE0, 0x00, 0x00, 0x00, 0x00,
+0x1F, 0x7C, 0x56, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x20, 0x6C, 0x47, 0xE0, 0x00, 0x00, 0x00, 0x00,
+0x21, 0x5C, 0x38, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x22, 0x4C, 0x29, 0xE0, 0x00, 0x00, 0x00, 0x00,
+0x23, 0x3C, 0x1A, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x24, 0x2C, 0x0B, 0xE0, 0x00, 0x00, 0x00, 0x00,
+0x25, 0x1C, 0x0A, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x26, 0x0B, 0xFB, 0xF0, 0x00, 0x00, 0x00, 0x00,
+0x27, 0x05, 0x27, 0x70, 0x00, 0x00, 0x00, 0x00, 0x27, 0xF5, 0x18, 0x70, 0x00, 0x00, 0x00, 0x00,
+0x29, 0xD4, 0xEC, 0x60, 0x00, 0x00, 0x00, 0x00, 0x2A, 0xC4, 0xEB, 0x70, 0x00, 0x00, 0x00, 0x00,
+0x2B, 0xB4, 0xDC, 0x70, 0x00, 0x00, 0x00, 0x00, 0x2C, 0xA4, 0xCD, 0x70, 0x00, 0x00, 0x00, 0x00,
+0x2D, 0x94, 0xBE, 0x70, 0x00, 0x00, 0x00, 0x00, 0x2E, 0x84, 0xAF, 0x70, 0x00, 0x00, 0x00, 0x00,
+0x2F, 0x74, 0xA0, 0x70, 0x00, 0x00, 0x00, 0x00, 0x30, 0x64, 0x91, 0x70, 0x00, 0x00, 0x00, 0x00,
+0x31, 0x5D, 0xBC, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x32, 0x72, 0x97, 0xF0, 0x00, 0x00, 0x00, 0x00,
+0x33, 0x3D, 0x9E, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x34, 0x52, 0x79, 0xF0, 0x00, 0x00, 0x00, 0x00,
+0x35, 0x1D, 0x80, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x36, 0x32, 0x5B, 0xF0, 0x00, 0x00, 0x00, 0x00,
+0x36, 0xFD, 0x62, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x38, 0x1B, 0x78, 0x70, 0x00, 0x00, 0x00, 0x00,
+0x38, 0xDD, 0x44, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x39, 0xFB, 0x5A, 0x70, 0x00, 0x00, 0x00, 0x00,
+0x3A, 0xBD, 0x26, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x3B, 0xDB, 0x3C, 0x70, 0x00, 0x00, 0x00, 0x00,
+0x3C, 0xA6, 0x43, 0x70, 0x00, 0x00, 0x00, 0x00, 0x3D, 0xBB, 0x1E, 0x70, 0x00, 0x00, 0x00, 0x00,
+0x3E, 0x86, 0x25, 0x70, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x9B, 0x00, 0x70, 0x00, 0x00, 0x00, 0x00,
+0x40, 0x66, 0x07, 0x70, 0x00, 0x00, 0x00, 0x00, 0x41, 0x84, 0x1C, 0xF0, 0x00, 0x00, 0x00, 0x00,
+0x42, 0x45, 0xE9, 0x70, 0x00, 0x00, 0x00, 0x00, 0x43, 0x63, 0xFE, 0xF0, 0x00, 0x00, 0x00, 0x00,
+0x44, 0x25, 0xCB, 0x70, 0x00, 0x00, 0x00, 0x00, 0x45, 0x43, 0xE0, 0xF0, 0x00, 0x00, 0x00, 0x00,
+0x46, 0x05, 0xAD, 0x70, 0x00, 0x00, 0x00, 0x00, 0x47, 0x23, 0xC2, 0xF0, 0x00, 0x00, 0x00, 0x00,
+0x47, 0xEE, 0xC9, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x49, 0x03, 0xA4, 0xF0, 0x00, 0x00, 0x00, 0x00,
+0x49, 0xCE, 0xAB, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x4A, 0xE3, 0x86, 0xF0, 0x00, 0x00, 0x00, 0x00,
+0x4B, 0xAE, 0x8D, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x4C, 0xCC, 0xA3, 0x70, 0x00, 0x00, 0x00, 0x00,
+0x4D, 0x8E, 0x6F, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x54, 0x4C, 0x1D, 0x60, 0x00, 0x00, 0x00, 0x00,
+0x7F, 0xFF, 0xFF, 0xFF, 0x00, 0x01, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x04, 0x05,
+0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x06, 0x07, 0x06, 0x07, 0x04, 0x06, 0x07, 0x06, 0x07,
+0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07,
+0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07,
+0x06, 0x07, 0x04, 0x07, 0x07, 0x00, 0x00, 0x2E, 0x98, 0x00, 0x00, 0x00, 0x00, 0x2A, 0x30, 0x00,
+0x04, 0x00, 0x00, 0x46, 0x50, 0x01, 0x08, 0x00, 0x00, 0x38, 0x40, 0x00, 0x0C, 0x00, 0x00, 0x38,
+0x40, 0x00, 0x0C, 0x00, 0x00, 0x46, 0x50, 0x01, 0x08, 0x00, 0x00, 0x38, 0x40, 0x01, 0x0C, 0x00,
+0x00, 0x2A, 0x30, 0x00, 0x04, 0x4C, 0x4D, 0x54, 0x00, 0x2B, 0x30, 0x33, 0x00, 0x2B, 0x30, 0x35,
+0x00, 0x2B, 0x30, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x3C, 0x2B, 0x30, 0x33, 0x3E, 0x2D, 0x33, 0x0A,
+#endif
+0x00, 0xE2, 0xBE, 0xE0, 0x01, 0x5E, 0x6B, 0x08, 0x00, 0x00, 0x00, 0x0E, 0x4D, 0x53, 0x4B, 0x2B,
+0x30, 0x30, 0x20, 0x2D, 0x20, 0x4B, 0x69, 0x72, 0x6F, 0x76,
+
/* Europe/Lisbon */
0x50, 0x48, 0x50, 0x32, 0x01, 0x50, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x00,
@@ -36555,7 +36925,7 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x1C, 0xAC, 0x91, 0xF0, 0x1D, 0x9C, 0x82, 0xF0, 0x1E, 0x8C, 0x73, 0xF0, 0x1F, 0x7C, 0x64, 0xF0,
0x20, 0x6C, 0x55, 0xF0, 0x21, 0x5C, 0x46, 0xF0, 0x22, 0x4C, 0x37, 0xF0, 0x23, 0x3C, 0x28, 0xF0,
0x24, 0x2C, 0x19, 0xF0, 0x25, 0x1C, 0x0A, 0xF0, 0x25, 0x9E, 0x73, 0x50, 0x27, 0xF5, 0x18, 0x70,
-0x28, 0xE5, 0x17, 0x80, 0x29, 0xD4, 0xEC, 0x60, 0x2A, 0xC4, 0xDD, 0x60, 0x2B, 0xB4, 0xEA, 0x80,
+0x28, 0xE5, 0x17, 0x80, 0x29, 0xD4, 0xEC, 0x60, 0x2A, 0xC4, 0xF9, 0x80, 0x2B, 0xB4, 0xEA, 0x80,
0x2C, 0xA4, 0xDB, 0x80, 0x2D, 0x94, 0xCC, 0x80, 0x2E, 0x84, 0xBD, 0x80, 0x2F, 0x74, 0xAE, 0x80,
0x30, 0x64, 0x9F, 0x80, 0x31, 0x5D, 0xCB, 0x00, 0x32, 0x72, 0xA6, 0x00, 0x33, 0x3D, 0xAD, 0x00,
0x34, 0x52, 0x88, 0x00, 0x35, 0x1D, 0x8F, 0x00, 0x36, 0x32, 0x6A, 0x00, 0x36, 0xFD, 0x71, 0x00,
@@ -36599,7 +36969,7 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x24, 0x2C, 0x19, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x25, 0x1C, 0x0A, 0xF0, 0x00, 0x00, 0x00, 0x00,
0x25, 0x9E, 0x73, 0x50, 0x00, 0x00, 0x00, 0x00, 0x27, 0xF5, 0x18, 0x70, 0x00, 0x00, 0x00, 0x00,
0x28, 0xE5, 0x17, 0x80, 0x00, 0x00, 0x00, 0x00, 0x29, 0xD4, 0xEC, 0x60, 0x00, 0x00, 0x00, 0x00,
-0x2A, 0xC4, 0xDD, 0x60, 0x00, 0x00, 0x00, 0x00, 0x2B, 0xB4, 0xEA, 0x80, 0x00, 0x00, 0x00, 0x00,
+0x2A, 0xC4, 0xF9, 0x80, 0x00, 0x00, 0x00, 0x00, 0x2B, 0xB4, 0xEA, 0x80, 0x00, 0x00, 0x00, 0x00,
0x2C, 0xA4, 0xDB, 0x80, 0x00, 0x00, 0x00, 0x00, 0x2D, 0x94, 0xCC, 0x80, 0x00, 0x00, 0x00, 0x00,
0x2E, 0x84, 0xBD, 0x80, 0x00, 0x00, 0x00, 0x00, 0x2F, 0x74, 0xAE, 0x80, 0x00, 0x00, 0x00, 0x00,
0x30, 0x64, 0x9F, 0x80, 0x00, 0x00, 0x00, 0x00, 0x31, 0x5D, 0xCB, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -36838,7 +37208,7 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x1E, 0x8C, 0x73, 0xF0, 0x1F, 0x7C, 0x64, 0xF0, 0x20, 0x6C, 0x55, 0xF0, 0x21, 0x5C, 0x46, 0xF0,
0x22, 0x4C, 0x37, 0xF0, 0x23, 0x3C, 0x28, 0xF0, 0x24, 0x2C, 0x19, 0xF0, 0x25, 0x1C, 0x0A, 0xF0,
0x26, 0x0B, 0xFB, 0xF0, 0x27, 0x05, 0x27, 0x70, 0x27, 0xF5, 0x18, 0x70, 0x28, 0xE5, 0x17, 0x80,
-0x29, 0x78, 0xBF, 0x80, 0x29, 0xD4, 0xD0, 0x40, 0x2A, 0xC4, 0xB3, 0x30, 0x2B, 0xB4, 0xDC, 0x70,
+0x29, 0x78, 0xBF, 0x80, 0x29, 0xD4, 0xFA, 0x70, 0x2A, 0xC4, 0xEB, 0x70, 0x2B, 0xB4, 0xDC, 0x70,
0x2C, 0xA4, 0xCD, 0x70, 0x2D, 0x94, 0xBE, 0x70, 0x2E, 0x84, 0xAF, 0x70, 0x2F, 0x74, 0xA0, 0x70,
0x30, 0x64, 0x91, 0x70, 0x31, 0x5D, 0xBC, 0xF0, 0x32, 0x72, 0x97, 0xF0, 0x33, 0x3D, 0x9E, 0xF0,
0x34, 0x52, 0x79, 0xF0, 0x35, 0x1D, 0x80, 0xF0, 0x36, 0x32, 0x5B, 0xF0, 0x36, 0xFD, 0x62, 0xF0,
@@ -36850,7 +37220,7 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x4A, 0xE3, 0x86, 0xF0, 0x4B, 0xAE, 0x8D, 0xF0, 0x4C, 0xCC, 0xA3, 0x70, 0x4D, 0x8E, 0x6F, 0xF0,
0x54, 0x4C, 0x1D, 0x60, 0x01, 0x03, 0x02, 0x03, 0x04, 0x02, 0x04, 0x06, 0x05, 0x06, 0x07, 0x06,
0x05, 0x08, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x09, 0x0A, 0x09, 0x0A, 0x09, 0x0A,
-0x09, 0x0A, 0x09, 0x0A, 0x09, 0x0A, 0x09, 0x0B, 0x0C, 0x09, 0x06, 0x05, 0x0A, 0x09, 0x0A, 0x09,
+0x09, 0x0A, 0x09, 0x0A, 0x09, 0x0A, 0x09, 0x0B, 0x0C, 0x09, 0x0A, 0x09, 0x0A, 0x09, 0x0A, 0x09,
0x0A, 0x09, 0x0A, 0x09, 0x0A, 0x09, 0x0A, 0x09, 0x0A, 0x09, 0x0A, 0x09, 0x0A, 0x09, 0x0A, 0x09,
0x0A, 0x09, 0x0A, 0x09, 0x0A, 0x09, 0x0A, 0x09, 0x0A, 0x09, 0x0A, 0x09, 0x0A, 0x09, 0x0A, 0x09,
0x0D, 0x09, 0x00, 0x00, 0x23, 0x39, 0x00, 0x00, 0x00, 0x00, 0x23, 0x39, 0x00, 0x04, 0x00, 0x00,
@@ -36887,8 +37257,8 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x24, 0x2C, 0x19, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x25, 0x1C, 0x0A, 0xF0, 0x00, 0x00, 0x00, 0x00,
0x26, 0x0B, 0xFB, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x27, 0x05, 0x27, 0x70, 0x00, 0x00, 0x00, 0x00,
0x27, 0xF5, 0x18, 0x70, 0x00, 0x00, 0x00, 0x00, 0x28, 0xE5, 0x17, 0x80, 0x00, 0x00, 0x00, 0x00,
-0x29, 0x78, 0xBF, 0x80, 0x00, 0x00, 0x00, 0x00, 0x29, 0xD4, 0xD0, 0x40, 0x00, 0x00, 0x00, 0x00,
-0x2A, 0xC4, 0xB3, 0x30, 0x00, 0x00, 0x00, 0x00, 0x2B, 0xB4, 0xDC, 0x70, 0x00, 0x00, 0x00, 0x00,
+0x29, 0x78, 0xBF, 0x80, 0x00, 0x00, 0x00, 0x00, 0x29, 0xD4, 0xFA, 0x70, 0x00, 0x00, 0x00, 0x00,
+0x2A, 0xC4, 0xEB, 0x70, 0x00, 0x00, 0x00, 0x00, 0x2B, 0xB4, 0xDC, 0x70, 0x00, 0x00, 0x00, 0x00,
0x2C, 0xA4, 0xCD, 0x70, 0x00, 0x00, 0x00, 0x00, 0x2D, 0x94, 0xBE, 0x70, 0x00, 0x00, 0x00, 0x00,
0x2E, 0x84, 0xAF, 0x70, 0x00, 0x00, 0x00, 0x00, 0x2F, 0x74, 0xA0, 0x70, 0x00, 0x00, 0x00, 0x00,
0x30, 0x64, 0x91, 0x70, 0x00, 0x00, 0x00, 0x00, 0x31, 0x5D, 0xBC, 0xF0, 0x00, 0x00, 0x00, 0x00,
@@ -36909,7 +37279,7 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x4C, 0xCC, 0xA3, 0x70, 0x00, 0x00, 0x00, 0x00, 0x4D, 0x8E, 0x6F, 0xF0, 0x00, 0x00, 0x00, 0x00,
0x54, 0x4C, 0x1D, 0x60, 0x00, 0x01, 0x03, 0x02, 0x03, 0x04, 0x02, 0x04, 0x06, 0x05, 0x06, 0x07,
0x06, 0x05, 0x08, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x09, 0x0A, 0x09, 0x0A, 0x09,
-0x0A, 0x09, 0x0A, 0x09, 0x0A, 0x09, 0x0A, 0x09, 0x0B, 0x0C, 0x09, 0x06, 0x05, 0x0A, 0x09, 0x0A,
+0x0A, 0x09, 0x0A, 0x09, 0x0A, 0x09, 0x0A, 0x09, 0x0B, 0x0C, 0x09, 0x0A, 0x09, 0x0A, 0x09, 0x0A,
0x09, 0x0A, 0x09, 0x0A, 0x09, 0x0A, 0x09, 0x0A, 0x09, 0x0A, 0x09, 0x0A, 0x09, 0x0A, 0x09, 0x0A,
0x09, 0x0A, 0x09, 0x0A, 0x09, 0x0A, 0x09, 0x0A, 0x09, 0x0A, 0x09, 0x0A, 0x09, 0x0A, 0x09, 0x0A,
0x09, 0x0D, 0x09, 0x00, 0x00, 0x23, 0x39, 0x00, 0x00, 0x00, 0x00, 0x23, 0x39, 0x00, 0x04, 0x00,
@@ -37995,15 +38365,15 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
/* Europe/Samara */
0x50, 0x48, 0x50, 0x32, 0x01, 0x52, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x2B, 0x80, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x2B, 0x80, 0x00, 0x00, 0x00,
0xA1, 0x00, 0x26, 0xAC, 0xB5, 0xA4, 0x0B, 0x50, 0xBE, 0x4C, 0x26, 0xC0, 0x15, 0x27, 0x99, 0xC0,
0x16, 0x18, 0xCE, 0x30, 0x17, 0x08, 0xCD, 0x40, 0x17, 0xFA, 0x01, 0xB0, 0x18, 0xEA, 0x00, 0xC0,
0x19, 0xDB, 0x35, 0x30, 0x1A, 0xCC, 0x85, 0xC0, 0x1B, 0xBC, 0x92, 0xE0, 0x1C, 0xAC, 0x83, 0xE0,
0x1D, 0x9C, 0x74, 0xE0, 0x1E, 0x8C, 0x65, 0xE0, 0x1F, 0x7C, 0x56, 0xE0, 0x20, 0x6C, 0x47, 0xE0,
0x21, 0x5C, 0x38, 0xE0, 0x22, 0x4C, 0x29, 0xE0, 0x23, 0x3C, 0x1A, 0xE0, 0x24, 0x2C, 0x0B, 0xE0,
0x25, 0x1C, 0x0A, 0xF0, 0x26, 0x0B, 0xFB, 0xF0, 0x27, 0x05, 0x27, 0x70, 0x27, 0xF5, 0x18, 0x70,
-0x28, 0xE5, 0x17, 0x80, 0x29, 0x00, 0xC7, 0x00, 0x29, 0xD4, 0xC2, 0x30, 0x2A, 0xC4, 0xA5, 0x20,
+0x28, 0xE5, 0x17, 0x80, 0x29, 0x00, 0xC7, 0x00, 0x29, 0xD4, 0xEC, 0x60, 0x2A, 0xC4, 0xDD, 0x60,
0x2B, 0xB4, 0xCE, 0x60, 0x2C, 0xA4, 0xBF, 0x60, 0x2D, 0x94, 0xB0, 0x60, 0x2E, 0x84, 0xA1, 0x60,
0x2F, 0x74, 0x92, 0x60, 0x30, 0x64, 0x83, 0x60, 0x31, 0x5D, 0xAE, 0xE0, 0x32, 0x72, 0x89, 0xE0,
0x33, 0x3D, 0x90, 0xE0, 0x34, 0x52, 0x6B, 0xE0, 0x35, 0x1D, 0x72, 0xE0, 0x36, 0x32, 0x4D, 0xE0,
@@ -38015,24 +38385,23 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x49, 0xCE, 0x9D, 0xE0, 0x4A, 0xE3, 0x78, 0xE0, 0x4B, 0xAE, 0x7F, 0xE0, 0x4C, 0xCC, 0xA3, 0x70,
0x4D, 0x8E, 0x6F, 0xF0, 0x00, 0x01, 0x02, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x05,
0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x07, 0x08, 0x07, 0x08, 0x09, 0x0B, 0x02, 0x0C,
-0x02, 0x0D, 0x0E, 0x0D, 0x0E, 0x0D, 0x0E, 0x0D, 0x0E, 0x0D, 0x0E, 0x0D, 0x0E, 0x0D, 0x0E, 0x0D,
-0x0E, 0x0D, 0x0E, 0x0D, 0x0E, 0x0D, 0x0E, 0x0D, 0x0E, 0x0D, 0x0E, 0x0D, 0x0E, 0x0D, 0x0E, 0x0D,
-0x0E, 0x0D, 0x0E, 0x0F, 0x0B, 0x0E, 0x00, 0x00, 0x2E, 0xF4, 0x00, 0x00, 0x00, 0x00, 0x2A, 0x30,
+0x0D, 0x0C, 0x0D, 0x0C, 0x0D, 0x0C, 0x0D, 0x0C, 0x0D, 0x0C, 0x0D, 0x0C, 0x0D, 0x0C, 0x0D, 0x0C,
+0x0D, 0x0C, 0x0D, 0x0C, 0x0D, 0x0C, 0x0D, 0x0C, 0x0D, 0x0C, 0x0D, 0x0C, 0x0D, 0x0C, 0x0D, 0x0C,
+0x0D, 0x0C, 0x0D, 0x0E, 0x0B, 0x0D, 0x00, 0x00, 0x2E, 0xF4, 0x00, 0x00, 0x00, 0x00, 0x2A, 0x30,
0x00, 0x04, 0x00, 0x00, 0x38, 0x40, 0x00, 0x04, 0x00, 0x00, 0x46, 0x50, 0x01, 0x09, 0x00, 0x00,
0x38, 0x40, 0x00, 0x0F, 0x00, 0x00, 0x38, 0x40, 0x00, 0x0F, 0x00, 0x00, 0x46, 0x50, 0x01, 0x09,
0x00, 0x00, 0x38, 0x40, 0x01, 0x14, 0x00, 0x00, 0x2A, 0x30, 0x00, 0x18, 0x00, 0x00, 0x2A, 0x30,
0x01, 0x1C, 0x00, 0x00, 0x1C, 0x20, 0x00, 0x21, 0x00, 0x00, 0x2A, 0x30, 0x00, 0x04, 0x00, 0x00,
-0x46, 0x50, 0x01, 0x25, 0x00, 0x00, 0x46, 0x50, 0x01, 0x25, 0x00, 0x00, 0x38, 0x40, 0x00, 0x04,
-0x00, 0x00, 0x38, 0x40, 0x01, 0x25, 0x4C, 0x4D, 0x54, 0x00, 0x53, 0x41, 0x4D, 0x54, 0x00, 0x4B,
-0x55, 0x59, 0x53, 0x54, 0x00, 0x4B, 0x55, 0x59, 0x54, 0x00, 0x4D, 0x53, 0x44, 0x00, 0x4D, 0x53,
-0x4B, 0x00, 0x45, 0x45, 0x53, 0x54, 0x00, 0x45, 0x45, 0x54, 0x00, 0x53, 0x41, 0x4D, 0x53, 0x54,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x01, 0x01,
-0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00,
+0x46, 0x50, 0x01, 0x25, 0x00, 0x00, 0x38, 0x40, 0x00, 0x04, 0x00, 0x00, 0x38, 0x40, 0x01, 0x25,
+0x4C, 0x4D, 0x54, 0x00, 0x53, 0x41, 0x4D, 0x54, 0x00, 0x4B, 0x55, 0x59, 0x53, 0x54, 0x00, 0x4B,
+0x55, 0x59, 0x54, 0x00, 0x4D, 0x53, 0x44, 0x00, 0x4D, 0x53, 0x4B, 0x00, 0x45, 0x45, 0x53, 0x54,
+0x00, 0x45, 0x45, 0x54, 0x00, 0x53, 0x41, 0x4D, 0x53, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
#ifdef TIMELIB_SUPPORTS_V2DATA
0x54, 0x5A, 0x69, 0x66, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x2B, 0xF8, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x2B, 0xF8, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xA1, 0x00, 0x26, 0xAC, 0xFF, 0xFF, 0xFF, 0xFF,
0xB5, 0xA4, 0x0B, 0x50, 0xFF, 0xFF, 0xFF, 0xFF, 0xBE, 0x4C, 0x26, 0xC0, 0x00, 0x00, 0x00, 0x00,
0x15, 0x27, 0x99, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x16, 0x18, 0xCE, 0x30, 0x00, 0x00, 0x00, 0x00,
@@ -38046,8 +38415,8 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x24, 0x2C, 0x0B, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x25, 0x1C, 0x0A, 0xF0, 0x00, 0x00, 0x00, 0x00,
0x26, 0x0B, 0xFB, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x27, 0x05, 0x27, 0x70, 0x00, 0x00, 0x00, 0x00,
0x27, 0xF5, 0x18, 0x70, 0x00, 0x00, 0x00, 0x00, 0x28, 0xE5, 0x17, 0x80, 0x00, 0x00, 0x00, 0x00,
-0x29, 0x00, 0xC7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x29, 0xD4, 0xC2, 0x30, 0x00, 0x00, 0x00, 0x00,
-0x2A, 0xC4, 0xA5, 0x20, 0x00, 0x00, 0x00, 0x00, 0x2B, 0xB4, 0xCE, 0x60, 0x00, 0x00, 0x00, 0x00,
+0x29, 0x00, 0xC7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x29, 0xD4, 0xEC, 0x60, 0x00, 0x00, 0x00, 0x00,
+0x2A, 0xC4, 0xDD, 0x60, 0x00, 0x00, 0x00, 0x00, 0x2B, 0xB4, 0xCE, 0x60, 0x00, 0x00, 0x00, 0x00,
0x2C, 0xA4, 0xBF, 0x60, 0x00, 0x00, 0x00, 0x00, 0x2D, 0x94, 0xB0, 0x60, 0x00, 0x00, 0x00, 0x00,
0x2E, 0x84, 0xA1, 0x60, 0x00, 0x00, 0x00, 0x00, 0x2F, 0x74, 0x92, 0x60, 0x00, 0x00, 0x00, 0x00,
0x30, 0x64, 0x83, 0x60, 0x00, 0x00, 0x00, 0x00, 0x31, 0x5D, 0xAE, 0xE0, 0x00, 0x00, 0x00, 0x00,
@@ -38067,21 +38436,20 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x4A, 0xE3, 0x78, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x4B, 0xAE, 0x7F, 0xE0, 0x00, 0x00, 0x00, 0x00,
0x4C, 0xCC, 0xA3, 0x70, 0x00, 0x00, 0x00, 0x00, 0x4D, 0x8E, 0x6F, 0xF0, 0x00, 0x01, 0x02, 0x04,
0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05,
-0x07, 0x08, 0x07, 0x08, 0x09, 0x0B, 0x02, 0x0C, 0x02, 0x0D, 0x0E, 0x0D, 0x0E, 0x0D, 0x0E, 0x0D,
-0x0E, 0x0D, 0x0E, 0x0D, 0x0E, 0x0D, 0x0E, 0x0D, 0x0E, 0x0D, 0x0E, 0x0D, 0x0E, 0x0D, 0x0E, 0x0D,
-0x0E, 0x0D, 0x0E, 0x0D, 0x0E, 0x0D, 0x0E, 0x0D, 0x0E, 0x0D, 0x0E, 0x0F, 0x0B, 0x0E, 0x00, 0x00,
+0x07, 0x08, 0x07, 0x08, 0x09, 0x0B, 0x02, 0x0C, 0x0D, 0x0C, 0x0D, 0x0C, 0x0D, 0x0C, 0x0D, 0x0C,
+0x0D, 0x0C, 0x0D, 0x0C, 0x0D, 0x0C, 0x0D, 0x0C, 0x0D, 0x0C, 0x0D, 0x0C, 0x0D, 0x0C, 0x0D, 0x0C,
+0x0D, 0x0C, 0x0D, 0x0C, 0x0D, 0x0C, 0x0D, 0x0C, 0x0D, 0x0C, 0x0D, 0x0E, 0x0B, 0x0D, 0x00, 0x00,
0x2E, 0xF4, 0x00, 0x00, 0x00, 0x00, 0x2A, 0x30, 0x00, 0x04, 0x00, 0x00, 0x38, 0x40, 0x00, 0x04,
0x00, 0x00, 0x46, 0x50, 0x01, 0x09, 0x00, 0x00, 0x38, 0x40, 0x00, 0x0F, 0x00, 0x00, 0x38, 0x40,
0x00, 0x0F, 0x00, 0x00, 0x46, 0x50, 0x01, 0x09, 0x00, 0x00, 0x38, 0x40, 0x01, 0x14, 0x00, 0x00,
0x2A, 0x30, 0x00, 0x18, 0x00, 0x00, 0x2A, 0x30, 0x01, 0x1C, 0x00, 0x00, 0x1C, 0x20, 0x00, 0x21,
-0x00, 0x00, 0x2A, 0x30, 0x00, 0x04, 0x00, 0x00, 0x46, 0x50, 0x01, 0x25, 0x00, 0x00, 0x46, 0x50,
-0x01, 0x25, 0x00, 0x00, 0x38, 0x40, 0x00, 0x04, 0x00, 0x00, 0x38, 0x40, 0x01, 0x25, 0x4C, 0x4D,
-0x54, 0x00, 0x53, 0x41, 0x4D, 0x54, 0x00, 0x4B, 0x55, 0x59, 0x53, 0x54, 0x00, 0x4B, 0x55, 0x59,
-0x54, 0x00, 0x4D, 0x53, 0x44, 0x00, 0x4D, 0x53, 0x4B, 0x00, 0x45, 0x45, 0x53, 0x54, 0x00, 0x45,
-0x45, 0x54, 0x00, 0x53, 0x41, 0x4D, 0x53, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01,
-0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x53, 0x41, 0x4D, 0x54, 0x2D, 0x34,
-0x0A,
+0x00, 0x00, 0x2A, 0x30, 0x00, 0x04, 0x00, 0x00, 0x46, 0x50, 0x01, 0x25, 0x00, 0x00, 0x38, 0x40,
+0x00, 0x04, 0x00, 0x00, 0x38, 0x40, 0x01, 0x25, 0x4C, 0x4D, 0x54, 0x00, 0x53, 0x41, 0x4D, 0x54,
+0x00, 0x4B, 0x55, 0x59, 0x53, 0x54, 0x00, 0x4B, 0x55, 0x59, 0x54, 0x00, 0x4D, 0x53, 0x44, 0x00,
+0x4D, 0x53, 0x4B, 0x00, 0x45, 0x45, 0x53, 0x54, 0x00, 0x45, 0x45, 0x54, 0x00, 0x53, 0x41, 0x4D,
+0x53, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x0A, 0x53, 0x41, 0x4D, 0x54, 0x2D, 0x34, 0x0A,
#endif
0x00, 0xDA, 0x81, 0x7F, 0x01, 0x5F, 0x2E, 0x58, 0x00, 0x00, 0x00, 0x19, 0x4D, 0x53, 0x4B, 0x2B,
0x30, 0x31, 0x20, 0x2D, 0x20, 0x53, 0x61, 0x6D, 0x61, 0x72, 0x61, 0x2C, 0x20, 0x55, 0x64, 0x6D,
@@ -39323,14 +39691,14 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
/* Europe/Ulyanovsk */
0x50, 0x48, 0x50, 0x32, 0x01, 0x52, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x43, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x14, 0x80, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x14, 0x80, 0x00, 0x00, 0x00,
0xA1, 0x00, 0x28, 0x40, 0xB5, 0xA4, 0x0B, 0x50, 0x15, 0x27, 0x99, 0xC0, 0x16, 0x18, 0xCE, 0x30,
0x17, 0x08, 0xCD, 0x40, 0x17, 0xFA, 0x01, 0xB0, 0x18, 0xEA, 0x00, 0xC0, 0x19, 0xDB, 0x35, 0x30,
0x1A, 0xCC, 0x85, 0xC0, 0x1B, 0xBC, 0x92, 0xE0, 0x1C, 0xAC, 0x83, 0xE0, 0x1D, 0x9C, 0x74, 0xE0,
0x1E, 0x8C, 0x65, 0xE0, 0x1F, 0x7C, 0x56, 0xE0, 0x20, 0x6C, 0x47, 0xE0, 0x21, 0x5C, 0x38, 0xE0,
0x22, 0x4C, 0x29, 0xE0, 0x23, 0x3C, 0x1A, 0xE0, 0x24, 0x2C, 0x0B, 0xE0, 0x25, 0x1C, 0x0A, 0xF0,
0x26, 0x0B, 0xFB, 0xF0, 0x27, 0x05, 0x27, 0x70, 0x27, 0xF5, 0x18, 0x70, 0x28, 0xE5, 0x17, 0x80,
-0x29, 0x78, 0xBF, 0x80, 0x29, 0xD4, 0xD0, 0x40, 0x2A, 0xC4, 0xB3, 0x30, 0x2B, 0xB4, 0xDC, 0x70,
+0x29, 0x78, 0xBF, 0x80, 0x29, 0xD4, 0xFA, 0x70, 0x2A, 0xC4, 0xEB, 0x70, 0x2B, 0xB4, 0xDC, 0x70,
0x2C, 0xA4, 0xCD, 0x70, 0x2D, 0x94, 0xBE, 0x70, 0x2E, 0x84, 0xAF, 0x70, 0x2F, 0x74, 0xA0, 0x70,
0x30, 0x64, 0x91, 0x70, 0x31, 0x5D, 0xBC, 0xF0, 0x32, 0x72, 0x97, 0xF0, 0x33, 0x3D, 0x9E, 0xF0,
0x34, 0x52, 0x79, 0xF0, 0x35, 0x1D, 0x80, 0xF0, 0x36, 0x32, 0x5B, 0xF0, 0x36, 0xFD, 0x62, 0xF0,
@@ -39340,22 +39708,23 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x43, 0x63, 0xFE, 0xF0, 0x44, 0x25, 0xCB, 0x70, 0x45, 0x43, 0xE0, 0xF0, 0x46, 0x05, 0xAD, 0x70,
0x47, 0x23, 0xC2, 0xF0, 0x47, 0xEE, 0xC9, 0xF0, 0x49, 0x03, 0xA4, 0xF0, 0x49, 0xCE, 0xAB, 0xF0,
0x4A, 0xE3, 0x86, 0xF0, 0x4B, 0xAE, 0x8D, 0xF0, 0x4C, 0xCC, 0xA3, 0x70, 0x4D, 0x8E, 0x6F, 0xF0,
-0x54, 0x4C, 0x1D, 0x60, 0x56, 0xF7, 0x14, 0x70, 0x00, 0x01, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02,
-0x03, 0x02, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x06, 0x07, 0x06, 0x07, 0x08,
-0x09, 0x07, 0x0A, 0x01, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07,
+0x54, 0x4C, 0x1D, 0x60, 0x56, 0xF7, 0x14, 0x70, 0x7F, 0xFF, 0xFF, 0xFF, 0x00, 0x01, 0x03, 0x02,
+0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x06,
+0x07, 0x06, 0x07, 0x08, 0x09, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07,
0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07,
-0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x04, 0x07, 0x04, 0x00, 0x00, 0x2D, 0x60, 0x00,
-0x00, 0x00, 0x00, 0x2A, 0x30, 0x00, 0x04, 0x00, 0x00, 0x46, 0x50, 0x01, 0x08, 0x00, 0x00, 0x38,
-0x40, 0x00, 0x0C, 0x00, 0x00, 0x38, 0x40, 0x00, 0x0C, 0x00, 0x00, 0x46, 0x50, 0x01, 0x08, 0x00,
-0x00, 0x38, 0x40, 0x01, 0x0C, 0x00, 0x00, 0x2A, 0x30, 0x00, 0x04, 0x00, 0x00, 0x2A, 0x30, 0x01,
-0x04, 0x00, 0x00, 0x1C, 0x20, 0x00, 0x10, 0x00, 0x00, 0x38, 0x40, 0x01, 0x0C, 0x00, 0x00, 0x38,
-0x40, 0x00, 0x0C, 0x4C, 0x4D, 0x54, 0x00, 0x2B, 0x30, 0x33, 0x00, 0x2B, 0x30, 0x35, 0x00, 0x2B,
-0x30, 0x34, 0x00, 0x2B, 0x30, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01,
-0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x04, 0x07, 0x04, 0x04,
+0x00, 0x00, 0x2D, 0x60, 0x00, 0x00, 0x00, 0x00, 0x2A, 0x30, 0x00, 0x04, 0x00, 0x00, 0x46, 0x50,
+0x01, 0x08, 0x00, 0x00, 0x38, 0x40, 0x00, 0x0C, 0x00, 0x00, 0x38, 0x40, 0x00, 0x0C, 0x00, 0x00,
+0x46, 0x50, 0x01, 0x08, 0x00, 0x00, 0x38, 0x40, 0x01, 0x0C, 0x00, 0x00, 0x2A, 0x30, 0x00, 0x04,
+0x00, 0x00, 0x2A, 0x30, 0x01, 0x04, 0x00, 0x00, 0x1C, 0x20, 0x00, 0x10, 0x00, 0x00, 0x38, 0x40,
+0x01, 0x0C, 0x00, 0x00, 0x38, 0x40, 0x00, 0x0C, 0x4C, 0x4D, 0x54, 0x00, 0x2B, 0x30, 0x33, 0x00,
+0x2B, 0x30, 0x35, 0x00, 0x2B, 0x30, 0x34, 0x00, 0x2B, 0x30, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00,
#ifdef TIMELIB_SUPPORTS_V2DATA
0x54, 0x5A, 0x69, 0x66, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x43, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x14, 0xF8, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x14, 0xF8, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xA1, 0x00, 0x28, 0x40, 0xFF, 0xFF, 0xFF, 0xFF,
0xB5, 0xA4, 0x0B, 0x50, 0x00, 0x00, 0x00, 0x00, 0x15, 0x27, 0x99, 0xC0, 0x00, 0x00, 0x00, 0x00,
0x16, 0x18, 0xCE, 0x30, 0x00, 0x00, 0x00, 0x00, 0x17, 0x08, 0xCD, 0x40, 0x00, 0x00, 0x00, 0x00,
@@ -39369,7 +39738,7 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x25, 0x1C, 0x0A, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x26, 0x0B, 0xFB, 0xF0, 0x00, 0x00, 0x00, 0x00,
0x27, 0x05, 0x27, 0x70, 0x00, 0x00, 0x00, 0x00, 0x27, 0xF5, 0x18, 0x70, 0x00, 0x00, 0x00, 0x00,
0x28, 0xE5, 0x17, 0x80, 0x00, 0x00, 0x00, 0x00, 0x29, 0x78, 0xBF, 0x80, 0x00, 0x00, 0x00, 0x00,
-0x29, 0xD4, 0xD0, 0x40, 0x00, 0x00, 0x00, 0x00, 0x2A, 0xC4, 0xB3, 0x30, 0x00, 0x00, 0x00, 0x00,
+0x29, 0xD4, 0xFA, 0x70, 0x00, 0x00, 0x00, 0x00, 0x2A, 0xC4, 0xEB, 0x70, 0x00, 0x00, 0x00, 0x00,
0x2B, 0xB4, 0xDC, 0x70, 0x00, 0x00, 0x00, 0x00, 0x2C, 0xA4, 0xCD, 0x70, 0x00, 0x00, 0x00, 0x00,
0x2D, 0x94, 0xBE, 0x70, 0x00, 0x00, 0x00, 0x00, 0x2E, 0x84, 0xAF, 0x70, 0x00, 0x00, 0x00, 0x00,
0x2F, 0x74, 0xA0, 0x70, 0x00, 0x00, 0x00, 0x00, 0x30, 0x64, 0x91, 0x70, 0x00, 0x00, 0x00, 0x00,
@@ -39389,19 +39758,19 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x49, 0xCE, 0xAB, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x4A, 0xE3, 0x86, 0xF0, 0x00, 0x00, 0x00, 0x00,
0x4B, 0xAE, 0x8D, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x4C, 0xCC, 0xA3, 0x70, 0x00, 0x00, 0x00, 0x00,
0x4D, 0x8E, 0x6F, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x54, 0x4C, 0x1D, 0x60, 0x00, 0x00, 0x00, 0x00,
-0x56, 0xF7, 0x14, 0x70, 0x00, 0x01, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x04, 0x05,
-0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x06, 0x07, 0x06, 0x07, 0x08, 0x09, 0x07, 0x0A, 0x01,
+0x56, 0xF7, 0x14, 0x70, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0x00, 0x01, 0x03, 0x02,
+0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x06,
+0x07, 0x06, 0x07, 0x08, 0x09, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07,
0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07,
-0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07,
-0x06, 0x07, 0x06, 0x07, 0x04, 0x07, 0x04, 0x00, 0x00, 0x2D, 0x60, 0x00, 0x00, 0x00, 0x00, 0x2A,
-0x30, 0x00, 0x04, 0x00, 0x00, 0x46, 0x50, 0x01, 0x08, 0x00, 0x00, 0x38, 0x40, 0x00, 0x0C, 0x00,
-0x00, 0x38, 0x40, 0x00, 0x0C, 0x00, 0x00, 0x46, 0x50, 0x01, 0x08, 0x00, 0x00, 0x38, 0x40, 0x01,
-0x0C, 0x00, 0x00, 0x2A, 0x30, 0x00, 0x04, 0x00, 0x00, 0x2A, 0x30, 0x01, 0x04, 0x00, 0x00, 0x1C,
-0x20, 0x00, 0x10, 0x00, 0x00, 0x38, 0x40, 0x01, 0x0C, 0x00, 0x00, 0x38, 0x40, 0x00, 0x0C, 0x4C,
-0x4D, 0x54, 0x00, 0x2B, 0x30, 0x33, 0x00, 0x2B, 0x30, 0x35, 0x00, 0x2B, 0x30, 0x34, 0x00, 0x2B,
-0x30, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x01, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x3C, 0x2B, 0x30, 0x34,
-0x3E, 0x2D, 0x34, 0x0A,
+0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x04, 0x07, 0x04, 0x04,
+0x00, 0x00, 0x2D, 0x60, 0x00, 0x00, 0x00, 0x00, 0x2A, 0x30, 0x00, 0x04, 0x00, 0x00, 0x46, 0x50,
+0x01, 0x08, 0x00, 0x00, 0x38, 0x40, 0x00, 0x0C, 0x00, 0x00, 0x38, 0x40, 0x00, 0x0C, 0x00, 0x00,
+0x46, 0x50, 0x01, 0x08, 0x00, 0x00, 0x38, 0x40, 0x01, 0x0C, 0x00, 0x00, 0x2A, 0x30, 0x00, 0x04,
+0x00, 0x00, 0x2A, 0x30, 0x01, 0x04, 0x00, 0x00, 0x1C, 0x20, 0x00, 0x10, 0x00, 0x00, 0x38, 0x40,
+0x01, 0x0C, 0x00, 0x00, 0x38, 0x40, 0x00, 0x0C, 0x4C, 0x4D, 0x54, 0x00, 0x2B, 0x30, 0x33, 0x00,
+0x2B, 0x30, 0x35, 0x00, 0x2B, 0x30, 0x34, 0x00, 0x2B, 0x30, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x0A, 0x3C, 0x2B, 0x30, 0x34, 0x3E, 0x2D, 0x34, 0x0A,
#endif
0x00, 0xDC, 0x3C, 0x35, 0x01, 0x5C, 0x82, 0xC0, 0x00, 0x00, 0x00, 0x12, 0x4D, 0x53, 0x4B, 0x2B,
0x30, 0x31, 0x20, 0x2D, 0x20, 0x55, 0x6C, 0x79, 0x61, 0x6E, 0x6F, 0x76, 0x73, 0x6B,
@@ -40136,15 +40505,15 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
/* Europe/Volgograd */
0x50, 0x48, 0x50, 0x32, 0x01, 0x52, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x21, 0x80, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x21, 0x80, 0x00, 0x00, 0x00,
0xA1, 0xF5, 0x46, 0xDC, 0xAB, 0xD8, 0x86, 0x50, 0xB5, 0xA4, 0x0B, 0x50, 0xF0, 0xB0, 0x4C, 0x40,
0x15, 0x27, 0x99, 0xC0, 0x16, 0x18, 0xCE, 0x30, 0x17, 0x08, 0xCD, 0x40, 0x17, 0xFA, 0x01, 0xB0,
0x18, 0xEA, 0x00, 0xC0, 0x19, 0xDB, 0x35, 0x30, 0x1A, 0xCC, 0x85, 0xC0, 0x1B, 0xBC, 0x92, 0xE0,
0x1C, 0xAC, 0x83, 0xE0, 0x1D, 0x9C, 0x74, 0xE0, 0x1E, 0x8C, 0x65, 0xE0, 0x1F, 0x7C, 0x56, 0xE0,
0x20, 0x6C, 0x47, 0xE0, 0x21, 0x5C, 0x38, 0xE0, 0x22, 0x4C, 0x29, 0xE0, 0x23, 0x3C, 0x28, 0xF0,
0x24, 0x2C, 0x19, 0xF0, 0x25, 0x1C, 0x0A, 0xF0, 0x26, 0x0B, 0xFB, 0xF0, 0x27, 0x05, 0x27, 0x70,
-0x27, 0xF5, 0x18, 0x70, 0x29, 0xD4, 0xEC, 0x60, 0x2A, 0xC4, 0xB3, 0x30, 0x2B, 0xB4, 0xDC, 0x70,
+0x27, 0xF5, 0x18, 0x70, 0x29, 0xD4, 0xEC, 0x60, 0x2A, 0xC4, 0xEB, 0x70, 0x2B, 0xB4, 0xDC, 0x70,
0x2C, 0xA4, 0xCD, 0x70, 0x2D, 0x94, 0xBE, 0x70, 0x2E, 0x84, 0xAF, 0x70, 0x2F, 0x74, 0xA0, 0x70,
0x30, 0x64, 0x91, 0x70, 0x31, 0x5D, 0xBC, 0xF0, 0x32, 0x72, 0x97, 0xF0, 0x33, 0x3D, 0x9E, 0xF0,
0x34, 0x52, 0x79, 0xF0, 0x35, 0x1D, 0x80, 0xF0, 0x36, 0x32, 0x5B, 0xF0, 0x36, 0xFD, 0x62, 0xF0,
@@ -40155,23 +40524,23 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x47, 0x23, 0xC2, 0xF0, 0x47, 0xEE, 0xC9, 0xF0, 0x49, 0x03, 0xA4, 0xF0, 0x49, 0xCE, 0xAB, 0xF0,
0x4A, 0xE3, 0x86, 0xF0, 0x4B, 0xAE, 0x8D, 0xF0, 0x4C, 0xCC, 0xA3, 0x70, 0x4D, 0x8E, 0x6F, 0xF0,
0x54, 0x4C, 0x1D, 0x60, 0x00, 0x01, 0x02, 0x03, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04,
-0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x06, 0x0B, 0x0A,
-0x0B, 0x0C, 0x0B, 0x0C, 0x0B, 0x0C, 0x0B, 0x0C, 0x0B, 0x0C, 0x0B, 0x0C, 0x0B, 0x0C, 0x0B, 0x0C,
-0x0B, 0x0C, 0x0B, 0x0C, 0x0B, 0x0C, 0x0B, 0x0C, 0x0B, 0x0C, 0x0B, 0x0C, 0x0B, 0x0C, 0x0B, 0x0C,
-0x0B, 0x0C, 0x0B, 0x0C, 0x0D, 0x0C, 0x00, 0x00, 0x29, 0xA4, 0x00, 0x00, 0x00, 0x00, 0x2A, 0x30,
+0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x06, 0x0A, 0x0B,
+0x0A, 0x0B, 0x0A, 0x0B, 0x0A, 0x0B, 0x0A, 0x0B, 0x0A, 0x0B, 0x0A, 0x0B, 0x0A, 0x0B, 0x0A, 0x0B,
+0x0A, 0x0B, 0x0A, 0x0B, 0x0A, 0x0B, 0x0A, 0x0B, 0x0A, 0x0B, 0x0A, 0x0B, 0x0A, 0x0B, 0x0A, 0x0B,
+0x0A, 0x0B, 0x0A, 0x0B, 0x0C, 0x0B, 0x00, 0x00, 0x29, 0xA4, 0x00, 0x00, 0x00, 0x00, 0x2A, 0x30,
0x00, 0x04, 0x00, 0x00, 0x2A, 0x30, 0x00, 0x09, 0x00, 0x00, 0x38, 0x40, 0x00, 0x09, 0x00, 0x00,
0x46, 0x50, 0x01, 0x0E, 0x00, 0x00, 0x38, 0x40, 0x00, 0x14, 0x00, 0x00, 0x38, 0x40, 0x00, 0x14,
0x00, 0x00, 0x46, 0x50, 0x01, 0x0E, 0x00, 0x00, 0x38, 0x40, 0x01, 0x0E, 0x00, 0x00, 0x2A, 0x30,
-0x00, 0x14, 0x00, 0x00, 0x2A, 0x30, 0x00, 0x19, 0x00, 0x00, 0x38, 0x40, 0x01, 0x1D, 0x00, 0x00,
-0x2A, 0x30, 0x00, 0x19, 0x00, 0x00, 0x38, 0x40, 0x00, 0x19, 0x00, 0x00, 0x2A, 0x30, 0x00, 0x19,
-0x4C, 0x4D, 0x54, 0x00, 0x54, 0x53, 0x41, 0x54, 0x00, 0x53, 0x54, 0x41, 0x54, 0x00, 0x56, 0x4F,
-0x4C, 0x53, 0x54, 0x00, 0x56, 0x4F, 0x4C, 0x54, 0x00, 0x4D, 0x53, 0x4B, 0x00, 0x4D, 0x53, 0x44,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x14, 0x00, 0x00, 0x38, 0x40, 0x01, 0x19, 0x00, 0x00, 0x2A, 0x30, 0x00, 0x1D, 0x00, 0x00,
+0x38, 0x40, 0x00, 0x1D, 0x00, 0x00, 0x2A, 0x30, 0x00, 0x1D, 0x4C, 0x4D, 0x54, 0x00, 0x54, 0x53,
+0x41, 0x54, 0x00, 0x53, 0x54, 0x41, 0x54, 0x00, 0x56, 0x4F, 0x4C, 0x53, 0x54, 0x00, 0x56, 0x4F,
+0x4C, 0x54, 0x00, 0x4D, 0x53, 0x44, 0x00, 0x4D, 0x53, 0x4B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
#ifdef TIMELIB_SUPPORTS_V2DATA
0x54, 0x5A, 0x69, 0x66, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x21, 0xF8, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x21, 0xF8, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xA1, 0xF5, 0x46, 0xDC, 0xFF, 0xFF, 0xFF, 0xFF,
0xAB, 0xD8, 0x86, 0x50, 0xFF, 0xFF, 0xFF, 0xFF, 0xB5, 0xA4, 0x0B, 0x50, 0xFF, 0xFF, 0xFF, 0xFF,
0xF0, 0xB0, 0x4C, 0x40, 0x00, 0x00, 0x00, 0x00, 0x15, 0x27, 0x99, 0xC0, 0x00, 0x00, 0x00, 0x00,
@@ -40185,7 +40554,7 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x23, 0x3C, 0x28, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x24, 0x2C, 0x19, 0xF0, 0x00, 0x00, 0x00, 0x00,
0x25, 0x1C, 0x0A, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x26, 0x0B, 0xFB, 0xF0, 0x00, 0x00, 0x00, 0x00,
0x27, 0x05, 0x27, 0x70, 0x00, 0x00, 0x00, 0x00, 0x27, 0xF5, 0x18, 0x70, 0x00, 0x00, 0x00, 0x00,
-0x29, 0xD4, 0xEC, 0x60, 0x00, 0x00, 0x00, 0x00, 0x2A, 0xC4, 0xB3, 0x30, 0x00, 0x00, 0x00, 0x00,
+0x29, 0xD4, 0xEC, 0x60, 0x00, 0x00, 0x00, 0x00, 0x2A, 0xC4, 0xEB, 0x70, 0x00, 0x00, 0x00, 0x00,
0x2B, 0xB4, 0xDC, 0x70, 0x00, 0x00, 0x00, 0x00, 0x2C, 0xA4, 0xCD, 0x70, 0x00, 0x00, 0x00, 0x00,
0x2D, 0x94, 0xBE, 0x70, 0x00, 0x00, 0x00, 0x00, 0x2E, 0x84, 0xAF, 0x70, 0x00, 0x00, 0x00, 0x00,
0x2F, 0x74, 0xA0, 0x70, 0x00, 0x00, 0x00, 0x00, 0x30, 0x64, 0x91, 0x70, 0x00, 0x00, 0x00, 0x00,
@@ -40206,23 +40575,23 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x4B, 0xAE, 0x8D, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x4C, 0xCC, 0xA3, 0x70, 0x00, 0x00, 0x00, 0x00,
0x4D, 0x8E, 0x6F, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x54, 0x4C, 0x1D, 0x60, 0x00, 0x01, 0x02, 0x03,
0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x08,
-0x09, 0x08, 0x09, 0x08, 0x09, 0x06, 0x0B, 0x0A, 0x0B, 0x0C, 0x0B, 0x0C, 0x0B, 0x0C, 0x0B, 0x0C,
-0x0B, 0x0C, 0x0B, 0x0C, 0x0B, 0x0C, 0x0B, 0x0C, 0x0B, 0x0C, 0x0B, 0x0C, 0x0B, 0x0C, 0x0B, 0x0C,
-0x0B, 0x0C, 0x0B, 0x0C, 0x0B, 0x0C, 0x0B, 0x0C, 0x0B, 0x0C, 0x0B, 0x0C, 0x0D, 0x0C, 0x00, 0x00,
+0x09, 0x08, 0x09, 0x08, 0x09, 0x06, 0x0A, 0x0B, 0x0A, 0x0B, 0x0A, 0x0B, 0x0A, 0x0B, 0x0A, 0x0B,
+0x0A, 0x0B, 0x0A, 0x0B, 0x0A, 0x0B, 0x0A, 0x0B, 0x0A, 0x0B, 0x0A, 0x0B, 0x0A, 0x0B, 0x0A, 0x0B,
+0x0A, 0x0B, 0x0A, 0x0B, 0x0A, 0x0B, 0x0A, 0x0B, 0x0A, 0x0B, 0x0A, 0x0B, 0x0C, 0x0B, 0x00, 0x00,
0x29, 0xA4, 0x00, 0x00, 0x00, 0x00, 0x2A, 0x30, 0x00, 0x04, 0x00, 0x00, 0x2A, 0x30, 0x00, 0x09,
0x00, 0x00, 0x38, 0x40, 0x00, 0x09, 0x00, 0x00, 0x46, 0x50, 0x01, 0x0E, 0x00, 0x00, 0x38, 0x40,
0x00, 0x14, 0x00, 0x00, 0x38, 0x40, 0x00, 0x14, 0x00, 0x00, 0x46, 0x50, 0x01, 0x0E, 0x00, 0x00,
-0x38, 0x40, 0x01, 0x0E, 0x00, 0x00, 0x2A, 0x30, 0x00, 0x14, 0x00, 0x00, 0x2A, 0x30, 0x00, 0x19,
-0x00, 0x00, 0x38, 0x40, 0x01, 0x1D, 0x00, 0x00, 0x2A, 0x30, 0x00, 0x19, 0x00, 0x00, 0x38, 0x40,
-0x00, 0x19, 0x00, 0x00, 0x2A, 0x30, 0x00, 0x19, 0x4C, 0x4D, 0x54, 0x00, 0x54, 0x53, 0x41, 0x54,
-0x00, 0x53, 0x54, 0x41, 0x54, 0x00, 0x56, 0x4F, 0x4C, 0x53, 0x54, 0x00, 0x56, 0x4F, 0x4C, 0x54,
-0x00, 0x4D, 0x53, 0x4B, 0x00, 0x4D, 0x53, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
-0x01, 0x01, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x4D, 0x53, 0x4B, 0x2D, 0x33, 0x0A,
-#endif
-0x00, 0xD3, 0xB0, 0xB5, 0x01, 0x56, 0x6E, 0xC2, 0x00, 0x00, 0x00, 0x22, 0x4D, 0x53, 0x4B, 0x2B,
+0x38, 0x40, 0x01, 0x0E, 0x00, 0x00, 0x2A, 0x30, 0x00, 0x14, 0x00, 0x00, 0x38, 0x40, 0x01, 0x19,
+0x00, 0x00, 0x2A, 0x30, 0x00, 0x1D, 0x00, 0x00, 0x38, 0x40, 0x00, 0x1D, 0x00, 0x00, 0x2A, 0x30,
+0x00, 0x1D, 0x4C, 0x4D, 0x54, 0x00, 0x54, 0x53, 0x41, 0x54, 0x00, 0x53, 0x54, 0x41, 0x54, 0x00,
+0x56, 0x4F, 0x4C, 0x53, 0x54, 0x00, 0x56, 0x4F, 0x4C, 0x54, 0x00, 0x4D, 0x53, 0x44, 0x00, 0x4D,
+0x53, 0x4B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A,
+0x4D, 0x53, 0x4B, 0x2D, 0x33, 0x0A,
+#endif
+0x00, 0xD3, 0xB0, 0xB5, 0x01, 0x56, 0x6E, 0xC2, 0x00, 0x00, 0x00, 0x1B, 0x4D, 0x53, 0x4B, 0x2B,
0x30, 0x30, 0x20, 0x2D, 0x20, 0x56, 0x6F, 0x6C, 0x67, 0x6F, 0x67, 0x72, 0x61, 0x64, 0x2C, 0x20,
-0x4B, 0x69, 0x72, 0x6F, 0x76, 0x2C, 0x20, 0x53, 0x61, 0x72, 0x61, 0x74, 0x6F, 0x76,
+0x53, 0x61, 0x72, 0x61, 0x74, 0x6F, 0x76,
/* Europe/Warsaw */
0x50, 0x48, 0x50, 0x32, 0x01, 0x50, 0x4C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -40797,23 +41166,23 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
/* Factory */
0x50, 0x48, 0x50, 0x32, 0x00, 0x3F, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x4C, 0x6F, 0x63, 0x61, 0x6C, 0x20, 0x74, 0x69, 0x6D, 0x65, 0x20, 0x7A, 0x6F, 0x6E,
-0x65, 0x20, 0x6D, 0x75, 0x73, 0x74, 0x20, 0x62, 0x65, 0x20, 0x73, 0x65, 0x74, 0x2D, 0x2D, 0x73,
-0x65, 0x65, 0x20, 0x7A, 0x69, 0x63, 0x20, 0x6D, 0x61, 0x6E, 0x75, 0x61, 0x6C, 0x20, 0x70, 0x61,
-0x67, 0x65, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x31, 0x80, 0x00, 0x00, 0x00,
+0x7F, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4C, 0x6F, 0x63, 0x61,
+0x6C, 0x20, 0x74, 0x69, 0x6D, 0x65, 0x20, 0x7A, 0x6F, 0x6E, 0x65, 0x20, 0x6D, 0x75, 0x73, 0x74,
+0x20, 0x62, 0x65, 0x20, 0x73, 0x65, 0x74, 0x2D, 0x2D, 0x73, 0x65, 0x65, 0x20, 0x7A, 0x69, 0x63,
+0x20, 0x6D, 0x61, 0x6E, 0x75, 0x61, 0x6C, 0x20, 0x70, 0x61, 0x67, 0x65, 0x00, 0x00, 0x00,
#ifdef TIMELIB_SUPPORTS_V2DATA
0x54, 0x5A, 0x69, 0x66, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x31, 0xF8, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4C, 0x6F, 0x63, 0x61, 0x6C,
-0x20, 0x74, 0x69, 0x6D, 0x65, 0x20, 0x7A, 0x6F, 0x6E, 0x65, 0x20, 0x6D, 0x75, 0x73, 0x74, 0x20,
-0x62, 0x65, 0x20, 0x73, 0x65, 0x74, 0x2D, 0x2D, 0x73, 0x65, 0x65, 0x20, 0x7A, 0x69, 0x63, 0x20,
-0x6D, 0x61, 0x6E, 0x75, 0x61, 0x6C, 0x20, 0x70, 0x61, 0x67, 0x65, 0x00, 0x00, 0x00, 0x0A, 0x3C,
-0x4C, 0x6F, 0x63, 0x61, 0x6C, 0x20, 0x74, 0x69, 0x6D, 0x65, 0x20, 0x7A, 0x6F, 0x6E, 0x65, 0x20,
-0x6D, 0x75, 0x73, 0x74, 0x20, 0x62, 0x65, 0x20, 0x73, 0x65, 0x74, 0x2D, 0x2D, 0x73, 0x65, 0x65,
-0x20, 0x7A, 0x69, 0x63, 0x20, 0x6D, 0x61, 0x6E, 0x75, 0x61, 0x6C, 0x20, 0x70, 0x61, 0x67, 0x65,
-0x3E, 0x30, 0x0A,
+0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x31, 0xF8, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x4C, 0x6F, 0x63, 0x61, 0x6C, 0x20, 0x74, 0x69, 0x6D, 0x65, 0x20, 0x7A,
+0x6F, 0x6E, 0x65, 0x20, 0x6D, 0x75, 0x73, 0x74, 0x20, 0x62, 0x65, 0x20, 0x73, 0x65, 0x74, 0x2D,
+0x2D, 0x73, 0x65, 0x65, 0x20, 0x7A, 0x69, 0x63, 0x20, 0x6D, 0x61, 0x6E, 0x75, 0x61, 0x6C, 0x20,
+0x70, 0x61, 0x67, 0x65, 0x00, 0x00, 0x00, 0x0A, 0x3C, 0x4C, 0x6F, 0x63, 0x61, 0x6C, 0x20, 0x74,
+0x69, 0x6D, 0x65, 0x20, 0x7A, 0x6F, 0x6E, 0x65, 0x20, 0x6D, 0x75, 0x73, 0x74, 0x20, 0x62, 0x65,
+0x20, 0x73, 0x65, 0x74, 0x2D, 0x2D, 0x73, 0x65, 0x65, 0x20, 0x7A, 0x69, 0x63, 0x20, 0x6D, 0x61,
+0x6E, 0x75, 0x61, 0x6C, 0x20, 0x70, 0x61, 0x67, 0x65, 0x3E, 0x30, 0x0A,
#endif
0x00, 0x89, 0x54, 0x40, 0x01, 0x12, 0xA8, 0x80, 0x00, 0x00, 0x00, 0x00,
@@ -41633,13 +42002,13 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x08, 0x80, 0x00, 0x00, 0x00,
0xDA, 0x61, 0x62, 0x80, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x50,
-0x00, 0x04, 0x7A, 0x7A, 0x7A, 0x00, 0x54, 0x46, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x04, 0x2D, 0x30, 0x30, 0x00, 0x54, 0x46, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00,
#ifdef TIMELIB_SUPPORTS_V2DATA
0x54, 0x5A, 0x69, 0x66, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x08, 0xF8, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xDA, 0x61, 0x62, 0x80, 0x00, 0x01, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x50, 0x00, 0x04, 0x7A, 0x7A, 0x7A, 0x00, 0x54, 0x46,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x50, 0x00, 0x04, 0x2D, 0x30, 0x30, 0x00, 0x54, 0x46,
0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x54, 0x46, 0x54, 0x2D, 0x35, 0x0A,
#endif
0x00, 0x3E, 0x05, 0xD2, 0x01, 0x7D, 0xCD, 0x36, 0x00, 0x00, 0x00, 0x00,
@@ -47347,7 +47716,7 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x1E, 0x8C, 0x73, 0xF0, 0x1F, 0x7C, 0x64, 0xF0, 0x20, 0x6C, 0x55, 0xF0, 0x21, 0x5C, 0x46, 0xF0,
0x22, 0x4C, 0x37, 0xF0, 0x23, 0x3C, 0x28, 0xF0, 0x24, 0x2C, 0x19, 0xF0, 0x25, 0x1C, 0x0A, 0xF0,
0x26, 0x0B, 0xFB, 0xF0, 0x27, 0x05, 0x27, 0x70, 0x27, 0xF5, 0x18, 0x70, 0x28, 0xE5, 0x17, 0x80,
-0x29, 0x78, 0xBF, 0x80, 0x29, 0xD4, 0xD0, 0x40, 0x2A, 0xC4, 0xB3, 0x30, 0x2B, 0xB4, 0xDC, 0x70,
+0x29, 0x78, 0xBF, 0x80, 0x29, 0xD4, 0xFA, 0x70, 0x2A, 0xC4, 0xEB, 0x70, 0x2B, 0xB4, 0xDC, 0x70,
0x2C, 0xA4, 0xCD, 0x70, 0x2D, 0x94, 0xBE, 0x70, 0x2E, 0x84, 0xAF, 0x70, 0x2F, 0x74, 0xA0, 0x70,
0x30, 0x64, 0x91, 0x70, 0x31, 0x5D, 0xBC, 0xF0, 0x32, 0x72, 0x97, 0xF0, 0x33, 0x3D, 0x9E, 0xF0,
0x34, 0x52, 0x79, 0xF0, 0x35, 0x1D, 0x80, 0xF0, 0x36, 0x32, 0x5B, 0xF0, 0x36, 0xFD, 0x62, 0xF0,
@@ -47359,7 +47728,7 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x4A, 0xE3, 0x86, 0xF0, 0x4B, 0xAE, 0x8D, 0xF0, 0x4C, 0xCC, 0xA3, 0x70, 0x4D, 0x8E, 0x6F, 0xF0,
0x54, 0x4C, 0x1D, 0x60, 0x01, 0x03, 0x02, 0x03, 0x04, 0x02, 0x04, 0x06, 0x05, 0x06, 0x07, 0x06,
0x05, 0x08, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x09, 0x0A, 0x09, 0x0A, 0x09, 0x0A,
-0x09, 0x0A, 0x09, 0x0A, 0x09, 0x0A, 0x09, 0x0B, 0x0C, 0x09, 0x06, 0x05, 0x0A, 0x09, 0x0A, 0x09,
+0x09, 0x0A, 0x09, 0x0A, 0x09, 0x0A, 0x09, 0x0B, 0x0C, 0x09, 0x0A, 0x09, 0x0A, 0x09, 0x0A, 0x09,
0x0A, 0x09, 0x0A, 0x09, 0x0A, 0x09, 0x0A, 0x09, 0x0A, 0x09, 0x0A, 0x09, 0x0A, 0x09, 0x0A, 0x09,
0x0A, 0x09, 0x0A, 0x09, 0x0A, 0x09, 0x0A, 0x09, 0x0A, 0x09, 0x0A, 0x09, 0x0A, 0x09, 0x0A, 0x09,
0x0D, 0x09, 0x00, 0x00, 0x23, 0x39, 0x00, 0x00, 0x00, 0x00, 0x23, 0x39, 0x00, 0x04, 0x00, 0x00,
@@ -47396,8 +47765,8 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x24, 0x2C, 0x19, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x25, 0x1C, 0x0A, 0xF0, 0x00, 0x00, 0x00, 0x00,
0x26, 0x0B, 0xFB, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x27, 0x05, 0x27, 0x70, 0x00, 0x00, 0x00, 0x00,
0x27, 0xF5, 0x18, 0x70, 0x00, 0x00, 0x00, 0x00, 0x28, 0xE5, 0x17, 0x80, 0x00, 0x00, 0x00, 0x00,
-0x29, 0x78, 0xBF, 0x80, 0x00, 0x00, 0x00, 0x00, 0x29, 0xD4, 0xD0, 0x40, 0x00, 0x00, 0x00, 0x00,
-0x2A, 0xC4, 0xB3, 0x30, 0x00, 0x00, 0x00, 0x00, 0x2B, 0xB4, 0xDC, 0x70, 0x00, 0x00, 0x00, 0x00,
+0x29, 0x78, 0xBF, 0x80, 0x00, 0x00, 0x00, 0x00, 0x29, 0xD4, 0xFA, 0x70, 0x00, 0x00, 0x00, 0x00,
+0x2A, 0xC4, 0xEB, 0x70, 0x00, 0x00, 0x00, 0x00, 0x2B, 0xB4, 0xDC, 0x70, 0x00, 0x00, 0x00, 0x00,
0x2C, 0xA4, 0xCD, 0x70, 0x00, 0x00, 0x00, 0x00, 0x2D, 0x94, 0xBE, 0x70, 0x00, 0x00, 0x00, 0x00,
0x2E, 0x84, 0xAF, 0x70, 0x00, 0x00, 0x00, 0x00, 0x2F, 0x74, 0xA0, 0x70, 0x00, 0x00, 0x00, 0x00,
0x30, 0x64, 0x91, 0x70, 0x00, 0x00, 0x00, 0x00, 0x31, 0x5D, 0xBC, 0xF0, 0x00, 0x00, 0x00, 0x00,
@@ -47418,7 +47787,7 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
0x4C, 0xCC, 0xA3, 0x70, 0x00, 0x00, 0x00, 0x00, 0x4D, 0x8E, 0x6F, 0xF0, 0x00, 0x00, 0x00, 0x00,
0x54, 0x4C, 0x1D, 0x60, 0x00, 0x01, 0x03, 0x02, 0x03, 0x04, 0x02, 0x04, 0x06, 0x05, 0x06, 0x07,
0x06, 0x05, 0x08, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x09, 0x0A, 0x09, 0x0A, 0x09,
-0x0A, 0x09, 0x0A, 0x09, 0x0A, 0x09, 0x0A, 0x09, 0x0B, 0x0C, 0x09, 0x06, 0x05, 0x0A, 0x09, 0x0A,
+0x0A, 0x09, 0x0A, 0x09, 0x0A, 0x09, 0x0A, 0x09, 0x0B, 0x0C, 0x09, 0x0A, 0x09, 0x0A, 0x09, 0x0A,
0x09, 0x0A, 0x09, 0x0A, 0x09, 0x0A, 0x09, 0x0A, 0x09, 0x0A, 0x09, 0x0A, 0x09, 0x0A, 0x09, 0x0A,
0x09, 0x0A, 0x09, 0x0A, 0x09, 0x0A, 0x09, 0x0A, 0x09, 0x0A, 0x09, 0x0A, 0x09, 0x0A, 0x09, 0x0A,
0x09, 0x0D, 0x09, 0x00, 0x00, 0x23, 0x39, 0x00, 0x00, 0x00, 0x00, 0x23, 0x39, 0x00, 0x04, 0x00,
@@ -47572,4 +47941,4 @@ const unsigned char timelib_timezone_db_data_builtin[271132] = {
#endif
0x00, 0x89, 0x54, 0x40, 0x01, 0x12, 0xA8, 0x80, 0x00, 0x00, 0x00, 0x00,};
-const timelib_tzdb timezonedb_builtin = { "2016.3", 587, timezonedb_idx_builtin, timelib_timezone_db_data_builtin };
+const timelib_tzdb timezonedb_builtin = { "2016.5", 589, timezonedb_idx_builtin, timelib_timezone_db_data_builtin };
diff --git a/ext/date/php_date.c b/ext/date/php_date.c
index e780b2ee6b..f494d6cfaf 100644
--- a/ext/date/php_date.c
+++ b/ext/date/php_date.c
@@ -2013,6 +2013,7 @@ static void date_register_classes(void) /* {{{ */
date_object_handlers_immutable.clone_obj = date_object_clone_date;
date_object_handlers_immutable.compare_objects = date_object_compare_date;
date_object_handlers_immutable.get_properties = date_object_get_properties;
+ date_object_handlers_immutable.get_gc = date_object_get_gc;
zend_class_implements(date_ce_immutable, 1, date_ce_interface);
INIT_CLASS_ENTRY(ce_timezone, "DateTimeZone", date_funcs_timezone);
@@ -2170,7 +2171,7 @@ static HashTable *date_object_get_properties(zval *object) /* {{{ */
props = zend_std_get_properties(object);
- if (!dateobj->time || GC_G(gc_active)) {
+ if (!dateobj->time) {
return props;
}
@@ -4851,7 +4852,7 @@ static HashTable *date_object_get_properties_period(zval *object) /* {{{ */
props = zend_std_get_properties(object);
- if (!period_obj->start || GC_G(gc_active)) {
+ if (!period_obj->start) {
return props;
}
diff --git a/ext/date/tests/bug33414-1.phpt b/ext/date/tests/bug33414-1.phpt
index 03d9b4ca9f..bd6e4c39ac 100644
--- a/ext/date/tests/bug33414-1.phpt
+++ b/ext/date/tests/bug33414-1.phpt
@@ -49,9 +49,9 @@ print "TZ=Asia/Baku - wrong day.\n";
date_default_timezone_set("Asia/Baku");
$tStamp = mktime (17, 17, 17, 1, 8299, 1970);
print "tStamp=". date("l Y-m-d H:i:s T I", $tStamp). "\n";
-$strtotime_tstamp = strtotime("next Sunday", $tStamp);
+$strtotime_tstamp = strtotime("second Monday", $tStamp);
print "result=".date("l Y-m-d H:i:s T I", $strtotime_tstamp)."\n";
-print "wanted=Sunday 00:00:00\n\n";
+print "wanted=Monday 00:00:00\n\n";
print "TZ=America/Noronha - wrong day.\n";
date_default_timezone_set("America/Noronha");
@@ -227,8 +227,8 @@ wanted=Thursday 00:00:00
TZ=Asia/Baku - wrong day.
tStamp=Sunday 1992-09-20 17:17:17 AZST 1
-result=Sunday 1992-09-27 00:00:00 AZT 0
-wanted=Sunday 00:00:00
+result=Monday 1992-09-28 00:00:00 AZT 0
+wanted=Monday 00:00:00
TZ=America/Noronha - wrong day.
tStamp=Friday 1999-10-01 17:17:17 FNT 0
diff --git a/ext/date/tests/date_sun_info_003.phpt b/ext/date/tests/date_sun_info_003.phpt
new file mode 100644
index 0000000000..3ff7a06df1
--- /dev/null
+++ b/ext/date/tests/date_sun_info_003.phpt
@@ -0,0 +1,42 @@
+--TEST--
+Test basic date_sun_info()
+--CREDITS--
+edgarsandi - <edgar.r.sandi@gmail.com>
+--FILE--
+<?php
+date_default_timezone_set('America/Sao_Paulo');
+$sun_info = date_sun_info(strtotime("2015-01-12 00:00:00 UTC"), 89.00, 1.00);
+foreach ($sun_info as $key => $elem ) {
+ echo "$key: " . date("H:i:s", $elem) . "\n";
+}
+
+echo "\n";
+
+$sun_info = date_sun_info(strtotime("2015-09-12 00:00:00 UTC"), 89.00, 1.00);
+foreach ($sun_info as $key => $elem ) {
+ echo "$key: " . date("H:i:s", $elem) . "\n";
+}
+
+echo "Done\n";
+?>
+--EXPECTF--
+sunrise: 21:00:00
+sunset: 21:00:00
+transit: 10:04:02
+civil_twilight_begin: 21:00:00
+civil_twilight_end: 21:00:00
+nautical_twilight_begin: 21:00:00
+nautical_twilight_end: 21:00:00
+astronomical_twilight_begin: 21:00:00
+astronomical_twilight_end: 21:00:00
+
+sunrise: 21:00:01
+sunset: 21:00:01
+transit: 08:52:30
+civil_twilight_begin: 21:00:01
+civil_twilight_end: 21:00:01
+nautical_twilight_begin: 21:00:01
+nautical_twilight_end: 21:00:01
+astronomical_twilight_begin: 21:00:01
+astronomical_twilight_end: 21:00:01
+Done \ No newline at end of file
diff --git a/ext/dom/xpath.c b/ext/dom/xpath.c
index b883896726..e31dcefb78 100644
--- a/ext/dom/xpath.c
+++ b/ext/dom/xpath.c
@@ -171,7 +171,6 @@ static void dom_xpath_ext_function_php(xmlXPathParserContextPtr ctxt, int nargs,
}
fci.size = sizeof(fci);
- fci.function_table = EG(function_table);
obj = valuePop(ctxt);
if (obj->stringval == NULL) {
@@ -188,7 +187,6 @@ static void dom_xpath_ext_function_php(xmlXPathParserContextPtr ctxt, int nargs,
ZVAL_STRING(&fci.function_name, (char *) obj->stringval);
xmlXPathFreeObject(obj);
- fci.symbol_table = NULL;
fci.object = NULL;
fci.retval = &retval;
fci.no_separation = 0;
@@ -474,7 +472,7 @@ static void php_xpath_eval(INTERNAL_FUNCTION_PARAMETERS, int type) /* {{{ */
break;
case XPATH_NUMBER:
- RETVAL_DOUBLE(xpathobjp->floatval)
+ RETVAL_DOUBLE(xpathobjp->floatval);
break;
case XPATH_STRING:
diff --git a/ext/enchant/enchant.c b/ext/enchant/enchant.c
index e2428b7830..ef2634918f 100644
--- a/ext/enchant/enchant.c
+++ b/ext/enchant/enchant.c
@@ -587,11 +587,7 @@ PHP_FUNCTION(enchant_broker_request_pwl_dict)
RETURN_FALSE;
}
-#if PHP_API_VERSION < 20100412
- if ((PG(safe_mode) && (!php_checkuid(pwl, NULL, CHECKUID_CHECK_FILE_AND_DIR))) || php_check_open_basedir(pwl)) {
-#else
if (php_check_open_basedir(pwl)) {
-#endif
RETURN_FALSE;
}
diff --git a/ext/exif/exif.c b/ext/exif/exif.c
index 44b9fee0b1..777694538b 100644
--- a/ext/exif/exif.c
+++ b/ext/exif/exif.c
@@ -2869,11 +2869,11 @@ static int exif_process_IFD_TAG(image_info_type *ImageInfo, char *dir_entry, cha
}
fpos = php_stream_tell(ImageInfo->infile);
- php_stream_seek(ImageInfo->infile, offset_val, SEEK_SET);
+ php_stream_seek(ImageInfo->infile, displacement+offset_val, SEEK_SET);
fgot = php_stream_tell(ImageInfo->infile);
- if (fgot!=offset_val) {
+ if (fgot!=displacement+offset_val) {
EFREE_IF(outside);
- exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_WARNING, "Wrong file pointer: 0x%08X != 0x%08X", fgot, offset_val);
+ exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_WARNING, "Wrong file pointer: 0x%08X != 0x%08X", fgot, displacement+offset_val);
return FALSE;
}
fgot = php_stream_read(ImageInfo->infile, value_ptr, byte_count);
diff --git a/ext/exif/tests/bug50845.jpg b/ext/exif/tests/bug50845.jpg
new file mode 100644
index 0000000000..d30137b5e6
--- /dev/null
+++ b/ext/exif/tests/bug50845.jpg
Binary files differ
diff --git a/ext/exif/tests/bug50845.phpt b/ext/exif/tests/bug50845.phpt
new file mode 100644
index 0000000000..2c142236a4
--- /dev/null
+++ b/ext/exif/tests/bug50845.phpt
@@ -0,0 +1,140 @@
+--TEST--
+Bug #50845 (exif_read_data() returns corrupted exif headers)
+--SKIPIF--
+<?php if (!extension_loaded('exif')) print 'skip exif extension not available';?>
+--FILE--
+<?php
+$infile = dirname(__FILE__).'/bug50845.jpg';
+var_dump(exif_read_data($infile));
+--EXPECTF--
+array(44) {
+ ["FileName"]=>
+ string(12) "bug50845.jpg"
+ ["FileDateTime"]=>
+ int(%d)
+ ["FileSize"]=>
+ int(803603)
+ ["FileType"]=>
+ int(2)
+ ["MimeType"]=>
+ string(10) "image/jpeg"
+ ["SectionsFound"]=>
+ string(30) "ANY_TAG, IFD0, THUMBNAIL, EXIF"
+ ["COMPUTED"]=>
+ array(9) {
+ ["html"]=>
+ string(26) "width="5472" height="3648""
+ ["Height"]=>
+ int(3648)
+ ["Width"]=>
+ int(5472)
+ ["IsColor"]=>
+ int(1)
+ ["ByteOrderMotorola"]=>
+ int(0)
+ ["ApertureFNumber"]=>
+ string(5) "f/7.1"
+ ["Copyright"]=>
+ string(13) "Public Domain"
+ ["Thumbnail.FileType"]=>
+ int(2)
+ ["Thumbnail.MimeType"]=>
+ string(10) "image/jpeg"
+ }
+ ["ImageDescription"]=>
+ string(295) "A U.S. Marine Corps MV-22 Osprey lands on the USS Whidbey Island (LSD-41), May 5, 2016. The vehicles were loaded to support a theater security cooperation event as a part of a MEU readiness exercise. (U.S. Marine Corps photo by Lance Cpl. Koby I. Saunders/22 Marine Expeditionary Unit/ Released)"
+ ["Make"]=>
+ string(5) "Canon"
+ ["Model"]=>
+ string(22) "Canon EOS-1D X Mark II"
+ ["Orientation"]=>
+ int(1)
+ ["XResolution"]=>
+ string(5) "240/1"
+ ["YResolution"]=>
+ string(5) "240/1"
+ ["ResolutionUnit"]=>
+ int(2)
+ ["Artist"]=>
+ string(24) "Lance Cpl. Koby Saunders"
+ ["Copyright"]=>
+ string(13) "Public Domain"
+ ["Exif_IFD_Pointer"]=>
+ int(12572)
+ ["THUMBNAIL"]=>
+ array(6) {
+ ["Compression"]=>
+ int(6)
+ ["XResolution"]=>
+ string(5) "240/1"
+ ["YResolution"]=>
+ string(5) "240/1"
+ ["ResolutionUnit"]=>
+ int(2)
+ ["JPEGInterchangeFormat"]=>
+ int(860)
+ ["JPEGInterchangeFormatLength"]=>
+ int(11204)
+ }
+ ["ExposureTime"]=>
+ string(5) "1/200"
+ ["FNumber"]=>
+ string(5) "71/10"
+ ["ExposureProgram"]=>
+ int(1)
+ ["ISOSpeedRatings"]=>
+ int(100)
+ ["UndefinedTag:0x8830"]=>
+ int(2)
+ ["UndefinedTag:0x8832"]=>
+ int(100)
+ ["ExifVersion"]=>
+ string(4) "0230"
+ ["ShutterSpeedValue"]=>
+ string(15) "7643856/1000000"
+ ["ApertureValue"]=>
+ string(15) "5655638/1000000"
+ ["ExposureBiasValue"]=>
+ string(3) "0/1"
+ ["MaxApertureValue"]=>
+ string(3) "4/1"
+ ["MeteringMode"]=>
+ int(5)
+ ["Flash"]=>
+ int(16)
+ ["FocalLength"]=>
+ string(4) "24/1"
+ ["ColorSpace"]=>
+ int(65535)
+ ["FocalPlaneXResolution"]=>
+ string(12) "5472000/1438"
+ ["FocalPlaneYResolution"]=>
+ string(11) "3648000/958"
+ ["FocalPlaneResolutionUnit"]=>
+ int(2)
+ ["CustomRendered"]=>
+ int(0)
+ ["ExposureMode"]=>
+ int(1)
+ ["WhiteBalance"]=>
+ int(0)
+ ["SceneCaptureType"]=>
+ int(0)
+ ["UndefinedTag:0xA431"]=>
+ string(12) "002099000358"
+ ["UndefinedTag:0xA432"]=>
+ array(4) {
+ [0]=>
+ string(4) "24/1"
+ [1]=>
+ string(5) "105/1"
+ [2]=>
+ string(3) "0/0"
+ [3]=>
+ string(3) "0/0"
+ }
+ ["UndefinedTag:0xA434"]=>
+ string(22) "EF24-105mm f/4L IS USM"
+ ["UndefinedTag:0xA435"]=>
+ string(10) "000044bc4c"
+}
diff --git a/ext/ext_skel b/ext/ext_skel
index a1c64640ae..f5b9b2578d 100755
--- a/ext/ext_skel
+++ b/ext/ext_skel
@@ -299,12 +299,12 @@ if test -n "$proto"; then
warning="
NOTE! Because some arguments to functions were resources, the code generated
cannot yet be compiled without editing. Please consider this to be step 4.5
-in the instructions above.
+in the instructions above.
"
fi
fi
-find . -type f | xargs chmod 644
+find . -type f | xargs chmod 644
find . -type d | xargs chmod 755
fi
diff --git a/ext/fileinfo/fileinfo.c b/ext/fileinfo/fileinfo.c
index 0cad89bb7e..cc56e188fd 100644
--- a/ext/fileinfo/fileinfo.c
+++ b/ext/fileinfo/fileinfo.c
@@ -548,11 +548,7 @@ static void _php_finfo_get_type(INTERNAL_FUNCTION_PARAMETERS, int mode, int mime
}
#endif
-#if PHP_API_VERSION < 20100412
- stream = php_stream_open_wrapper_ex(buffer, "rb", ENFORCE_SAFE_MODE | REPORT_ERRORS, NULL, context);
-#else
stream = php_stream_open_wrapper_ex(buffer, "rb", REPORT_ERRORS, NULL, context);
-#endif
if (!stream) {
RETVAL_FALSE;
diff --git a/ext/gd/libgd/gd.c b/ext/gd/libgd/gd.c
index 0b0d933bfd..5c7b5ce1ba 100644
--- a/ext/gd/libgd/gd.c
+++ b/ext/gd/libgd/gd.c
@@ -1049,11 +1049,13 @@ void gdImageAABlend (gdImagePtr im)
}
}
+static void _gdImageFilledHRectangle (gdImagePtr im, int x1, int y1, int x2, int y2, int color);
+
static void gdImageHLine(gdImagePtr im, int y, int x1, int x2, int col)
{
if (im->thick > 1) {
int thickhalf = im->thick >> 1;
- gdImageFilledRectangle(im, x1, y - thickhalf, x2, y + im->thick - thickhalf - 1, col);
+ _gdImageFilledHRectangle(im, x1, y - thickhalf, x2, y + im->thick - thickhalf - 1, col);
} else {
if (x2 < x1) {
int t = x2;
@@ -2118,10 +2120,53 @@ void gdImageRectangle (gdImagePtr im, int x1, int y1, int x2, int y2, int color)
}
}
-void gdImageFilledRectangle (gdImagePtr im, int x1, int y1, int x2, int y2, int color)
+static void _gdImageFilledHRectangle (gdImagePtr im, int x1, int y1, int x2, int y2, int color)
{
int x, y;
+ if (x1 == x2 && y1 == y2) {
+ gdImageSetPixel(im, x1, y1, color);
+ return;
+ }
+
+ if (x1 > x2) {
+ x = x1;
+ x1 = x2;
+ x2 = x;
+ }
+
+ if (y1 > y2) {
+ y = y1;
+ y1 = y2;
+ y2 = y;
+ }
+
+ if (x1 < 0) {
+ x1 = 0;
+ }
+
+ if (x2 >= gdImageSX(im)) {
+ x2 = gdImageSX(im) - 1;
+ }
+
+ if (y1 < 0) {
+ y1 = 0;
+ }
+
+ if (y2 >= gdImageSY(im)) {
+ y2 = gdImageSY(im) - 1;
+ }
+
+ for (x = x1; (x <= x2); x++) {
+ for (y = y1; (y <= y2); y++) {
+ gdImageSetPixel (im, x, y, color);
+ }
+ }
+}
+
+static void _gdImageFilledVRectangle (gdImagePtr im, int x1, int y1, int x2, int y2, int color)
+{
+ int x, y;
if (x1 == x2 && y1 == y2) {
gdImageSetPixel(im, x1, y1, color);
@@ -2163,6 +2208,11 @@ void gdImageFilledRectangle (gdImagePtr im, int x1, int y1, int x2, int y2, int
}
}
+void gdImageFilledRectangle (gdImagePtr im, int x1, int y1, int x2, int y2, int color)
+{
+ _gdImageFilledVRectangle(im, x1, y1, x2, y2, color);
+}
+
void gdImageCopy (gdImagePtr dst, gdImagePtr src, int dstX, int dstY, int srcX, int srcY, int w, int h)
{
int c;
diff --git a/ext/gd/libgd/xbm.c b/ext/gd/libgd/xbm.c
index d127a1d4ff..b351814abb 100644
--- a/ext/gd/libgd/xbm.c
+++ b/ext/gd/libgd/xbm.c
@@ -210,7 +210,7 @@ void gdImageXbmCtx(gdImagePtr image, char* file_name, int fg, gdIOCtx * out)
if (gdImageGetPixel(image, x, y) == fg) {
c |= b;
}
- if ((b == 128) || (x == sx && y == sy)) {
+ if ((b == 128) || (x == sx - 1)) {
b = 1;
if (p) {
gdCtxPrintf(out, ", ");
diff --git a/ext/gd/tests/bug43475.phpt b/ext/gd/tests/bug43475.phpt
new file mode 100644
index 0000000000..b29b9800a5
--- /dev/null
+++ b/ext/gd/tests/bug43475.phpt
@@ -0,0 +1,59 @@
+--TEST--
+Bug #43475 (Thick styled lines have scrambled patterns)
+--SKIPIF--
+<?php
+ if (!extension_loaded('gd')) die("skip gd extension not available\n");
+?>
+--FILE--
+<?php
+require_once __DIR__ . '/similarity.inc';
+
+function setStyleAndThickness($im, $color, $thickness)
+{
+ $style = array();
+ $i = 0;
+ while ($i < 16 * $thickness) {
+ $style[$i++] = $color;
+ }
+ while ($i < 20 * $thickness) {
+ $style[$i++] = IMG_COLOR_TRANSPARENT;
+ }
+ while ($i < 28 * $thickness) {
+ $style[$i++] = $color;
+ }
+ while ($i < 32 * $thickness) {
+ $style[$i++] = IMG_COLOR_TRANSPARENT;
+ }
+ imagesetstyle($im, $style);
+ imagesetthickness($im, $thickness);
+}
+
+$im = imagecreate(800, 800);
+imagecolorallocate($im, 255, 255, 255);
+$black = imagecolorallocate($im, 0, 0, 0);
+
+setStyleAndThickness($im, $black, 1);
+imageline($im, 50, 250, 550, 250, IMG_COLOR_STYLED);
+imageline($im, 550, 250, 550, 750, IMG_COLOR_STYLED);
+imageline($im, 550, 750, 50, 250, IMG_COLOR_STYLED);
+
+setStyleAndThickness($im, $black, 2);
+imageline($im, 100, 200, 600, 200, IMG_COLOR_STYLED);
+imageline($im, 600, 200, 600, 700, IMG_COLOR_STYLED);
+imageline($im, 600, 700, 100, 200, IMG_COLOR_STYLED);
+
+setStyleAndThickness($im, $black, 4);
+imageline($im, 150, 150, 650, 150, IMG_COLOR_STYLED);
+imageline($im, 650, 150, 650, 650, IMG_COLOR_STYLED);
+imageline($im, 650, 650, 150, 150, IMG_COLOR_STYLED);
+
+setStyleAndThickness($im, $black, 6);
+imageline($im, 200, 100, 700, 100, IMG_COLOR_STYLED);
+imageline($im, 700, 100, 700, 600, IMG_COLOR_STYLED);
+imageline($im, 700, 600, 200, 100, IMG_COLOR_STYLED);
+
+$ex = imagecreatefrompng(__DIR__ . '/bug43475.png');
+var_dump(calc_image_dissimilarity($ex, $im) < 1e-5);
+?>
+--EXPECT--
+bool(true)
diff --git a/ext/gd/tests/bug43475.png b/ext/gd/tests/bug43475.png
new file mode 100644
index 0000000000..774270f63d
--- /dev/null
+++ b/ext/gd/tests/bug43475.png
Binary files differ
diff --git a/ext/gd/tests/bug53640.phpt b/ext/gd/tests/bug53640.phpt
index a16b7c24c0..ee875de19a 100644
--- a/ext/gd/tests/bug53640.phpt
+++ b/ext/gd/tests/bug53640.phpt
@@ -12,8 +12,6 @@ $white = imagecolorallocate($im, 255, 255, 255);
imagefilledrectangle($im, 2, 2, 6, 6, $white);
imagexbm($im, NULL);
?>
---XFAIL--
-Padding is not implemented yet
--EXPECT--
#define image_width 9
#define image_height 9
diff --git a/ext/hash/config.m4 b/ext/hash/config.m4
index 5174db3b71..703cf14a30 100644
--- a/ext/hash/config.m4
+++ b/ext/hash/config.m4
@@ -27,11 +27,11 @@ if test "$PHP_HASH" != "no"; then
EXT_HASH_SOURCES="hash.c hash_md.c hash_sha.c hash_ripemd.c hash_haval.c \
hash_tiger.c hash_gost.c hash_snefru.c hash_whirlpool.c hash_adler32.c \
- hash_crc32.c hash_fnv.c hash_joaat.c"
+ hash_crc32.c hash_fnv.c hash_joaat.c hash_sha3.c"
EXT_HASH_HEADERS="php_hash.h php_hash_md.h php_hash_sha.h php_hash_ripemd.h \
php_hash_haval.h php_hash_tiger.h php_hash_gost.h php_hash_snefru.h \
php_hash_whirlpool.h php_hash_adler32.h php_hash_crc32.h \
- php_hash_fnv.h php_hash_joaat.h"
+ php_hash_fnv.h php_hash_joaat.h php_hash_sha3.h"
PHP_NEW_EXTENSION(hash, $EXT_HASH_SOURCES, $ext_shared)
ifdef([PHP_INSTALL_HEADERS], [
diff --git a/ext/hash/config.w32 b/ext/hash/config.w32
index 8e9d4c3d48..17711facd8 100644
--- a/ext/hash/config.w32
+++ b/ext/hash/config.w32
@@ -15,10 +15,10 @@ if (PHP_HASH != "no") {
AC_DEFINE('HAVE_HASH_EXT', 1);
EXTENSION("hash", "hash.c hash_md.c hash_sha.c hash_ripemd.c hash_haval.c "
+ "hash_tiger.c hash_gost.c hash_snefru.c hash_whirlpool.c "
- + "hash_adler32.c hash_crc32.c hash_joaat.c hash_fnv.c");
+ + "hash_adler32.c hash_crc32.c hash_joaat.c hash_fnv.c hash_sha3.c");
PHP_INSTALL_HEADERS("ext/hash/", "php_hash.h php_hash_md.h php_hash_sha.h php_hash_ripemd.h " +
"php_hash_haval.h php_hash_tiger.h php_hash_gost.h php_hash_snefru.h " +
- "php_hash_whirlpool.h php_hash_adler32.h php_hash_crc32.h");
+ "php_hash_whirlpool.h php_hash_adler32.h php_hash_crc32.h php_hash_sha3.h");
}
diff --git a/ext/hash/hash.c b/ext/hash/hash.c
index dd19dabf84..87b11b6dad 100644
--- a/ext/hash/hash.c
+++ b/ext/hash/hash.c
@@ -31,12 +31,6 @@
static int php_hash_le_hash;
HashTable php_hash_hashtable;
-#if (PHP_MAJOR_VERSION >= 5)
-# define DEFAULT_CONTEXT FG(default_context)
-#else
-# define DEFAULT_CONTEXT NULL
-#endif
-
#ifdef PHP_MHASH_BC
struct mhash_bc_entry {
char *mhash_name;
@@ -140,7 +134,7 @@ static void php_hash_do_hash(INTERNAL_FUNCTION_PARAMETERS, int isfilename, zend_
php_error_docref(NULL, E_WARNING, "Invalid path");
RETURN_FALSE;
}
- stream = php_stream_open_wrapper_ex(data, "rb", REPORT_ERRORS, NULL, DEFAULT_CONTEXT);
+ stream = php_stream_open_wrapper_ex(data, "rb", REPORT_ERRORS, NULL, FG(default_context));
if (!stream) {
/* Stream will report errors opening file */
RETURN_FALSE;
@@ -259,7 +253,7 @@ static void php_hash_do_hash_hmac(INTERNAL_FUNCTION_PARAMETERS, int isfilename,
php_error_docref(NULL, E_WARNING, "Invalid path");
RETURN_FALSE;
}
- stream = php_stream_open_wrapper_ex(data, "rb", REPORT_ERRORS, NULL, DEFAULT_CONTEXT);
+ stream = php_stream_open_wrapper_ex(data, "rb", REPORT_ERRORS, NULL, FG(default_context));
if (!stream) {
/* Stream will report errors opening file */
RETURN_FALSE;
@@ -1013,7 +1007,13 @@ PHP_MINIT_FUNCTION(hash)
php_hash_register_algo("sha224", &php_hash_sha224_ops);
php_hash_register_algo("sha256", &php_hash_sha256_ops);
php_hash_register_algo("sha384", &php_hash_sha384_ops);
+ php_hash_register_algo("sha512/224", &php_hash_sha512_224_ops);
+ php_hash_register_algo("sha512/256", &php_hash_sha512_256_ops);
php_hash_register_algo("sha512", &php_hash_sha512_ops);
+ php_hash_register_algo("sha3-224", &php_hash_sha3_224_ops);
+ php_hash_register_algo("sha3-256", &php_hash_sha3_256_ops);
+ php_hash_register_algo("sha3-384", &php_hash_sha3_384_ops);
+ php_hash_register_algo("sha3-512", &php_hash_sha3_512_ops);
php_hash_register_algo("ripemd128", &php_hash_ripemd128_ops);
php_hash_register_algo("ripemd160", &php_hash_ripemd160_ops);
php_hash_register_algo("ripemd256", &php_hash_ripemd256_ops);
diff --git a/ext/hash/hash_adler32.c b/ext/hash/hash_adler32.c
index 07d71c3f33..eac389f896 100644
--- a/ext/hash/hash_adler32.c
+++ b/ext/hash/hash_adler32.c
@@ -29,7 +29,7 @@ PHP_HASH_API void PHP_ADLER32Init(PHP_ADLER32_CTX *context)
PHP_HASH_API void PHP_ADLER32Update(PHP_ADLER32_CTX *context, const unsigned char *input, size_t len)
{
- php_hash_uint32 i, s[2];
+ uint32_t i, s[2];
s[0] = context->state & 0xffff;
s[1] = (context->state >> 16) & 0xffff;
diff --git a/ext/hash/hash_fnv.c b/ext/hash/hash_fnv.c
index 9d365970c8..1e6f8d974d 100644
--- a/ext/hash/hash_fnv.c
+++ b/ext/hash/hash_fnv.c
@@ -147,8 +147,8 @@ PHP_HASH_API void PHP_FNV164Final(unsigned char digest[8], PHP_FNV164_CTX * cont
* returns:
* 32 bit hash as a static hash type
*/
-static php_hash_uint32
-fnv_32_buf(void *buf, size_t len, php_hash_uint32 hval, int alternate)
+static uint32_t
+fnv_32_buf(void *buf, size_t len, uint32_t hval, int alternate)
{
unsigned char *bp = (unsigned char *)buf; /* start of buffer */
unsigned char *be = bp + len; /* beyond end of buffer */
@@ -163,10 +163,10 @@ fnv_32_buf(void *buf, size_t len, php_hash_uint32 hval, int alternate)
hval *= PHP_FNV_32_PRIME;
/* xor the bottom with the current octet */
- hval ^= (php_hash_uint32)*bp++;
+ hval ^= (uint32_t)*bp++;
} else {
/* xor the bottom with the current octet */
- hval ^= (php_hash_uint32)*bp++;
+ hval ^= (uint32_t)*bp++;
/* multiply by the 32 bit FNV magic prime mod 2^32 */
hval *= PHP_FNV_32_PRIME;
@@ -189,8 +189,8 @@ fnv_32_buf(void *buf, size_t len, php_hash_uint32 hval, int alternate)
* returns:
* 64 bit hash as a static hash type
*/
-static php_hash_uint64
-fnv_64_buf(void *buf, size_t len, php_hash_uint64 hval, int alternate)
+static uint64_t
+fnv_64_buf(void *buf, size_t len, uint64_t hval, int alternate)
{
unsigned char *bp = (unsigned char *)buf; /* start of buffer */
unsigned char *be = bp + len; /* beyond end of buffer */
@@ -205,10 +205,10 @@ fnv_64_buf(void *buf, size_t len, php_hash_uint64 hval, int alternate)
hval *= PHP_FNV_64_PRIME;
/* xor the bottom with the current octet */
- hval ^= (php_hash_uint64)*bp++;
+ hval ^= (uint64_t)*bp++;
} else {
/* xor the bottom with the current octet */
- hval ^= (php_hash_uint64)*bp++;
+ hval ^= (uint64_t)*bp++;
/* multiply by the 64 bit FNV magic prime mod 2^64 */
hval *= PHP_FNV_64_PRIME;
diff --git a/ext/hash/hash_gost.c b/ext/hash/hash_gost.c
index 2bc78313fb..4e72117451 100644
--- a/ext/hash/hash_gost.c
+++ b/ext/hash/hash_gost.c
@@ -207,10 +207,10 @@
AA(v, l, r); \
}
-static inline void Gost(PHP_GOST_CTX *context, php_hash_uint32 data[8])
+static inline void Gost(PHP_GOST_CTX *context, uint32_t data[8])
{
int i;
- php_hash_uint32 l, r, t, key[8], u[8], v[8], w[8], s[8], *h = context->state, *m = data;
+ uint32_t l, r, t, key[8], u[8], v[8], w[8], s[8], *h = context->state, *m = data;
memcpy(u, context->state, sizeof(u));
memcpy(v, data, sizeof(v));
@@ -227,11 +227,11 @@ static inline void Gost(PHP_GOST_CTX *context, php_hash_uint32 data[8])
static inline void GostTransform(PHP_GOST_CTX *context, const unsigned char input[32])
{
int i, j;
- php_hash_uint32 data[8], temp = 0, save = 0;
+ uint32_t data[8], temp = 0, save = 0;
for (i = 0, j = 0; i < 8; ++i, j += 4) {
- data[i] = ((php_hash_uint32) input[j]) | (((php_hash_uint32) input[j + 1]) << 8) |
- (((php_hash_uint32) input[j + 2]) << 16) | (((php_hash_uint32) input[j + 3]) << 24);
+ data[i] = ((uint32_t) input[j]) | (((uint32_t) input[j + 1]) << 8) |
+ (((uint32_t) input[j + 2]) << 16) | (((uint32_t) input[j + 3]) << 24);
save = context->state[i + 8];
context->state[i + 8] += data[i] + temp;
temp = ((context->state[i + 8] < data[i]) || (context->state[i + 8] < save)) ? 1 : 0;
@@ -252,7 +252,7 @@ PHP_HASH_API void PHP_GOSTInitCrypto(PHP_GOST_CTX *context)
context->tables = &tables_crypto;
}
-static const php_hash_uint32 MAX32 = 0xffffffffLU;
+static const uint32_t MAX32 = 0xffffffffLU;
PHP_HASH_API void PHP_GOSTUpdate(PHP_GOST_CTX *context, const unsigned char *input, size_t len)
{
@@ -288,7 +288,7 @@ PHP_HASH_API void PHP_GOSTUpdate(PHP_GOST_CTX *context, const unsigned char *inp
PHP_HASH_API void PHP_GOSTFinal(unsigned char digest[32], PHP_GOST_CTX *context)
{
- php_hash_uint32 i, j, l[8] = {0};
+ uint32_t i, j, l[8] = {0};
if (context->length) {
GostTransform(context, context->buffer);
diff --git a/ext/hash/hash_haval.c b/ext/hash/hash_haval.c
index 2dbb15a34c..08168adbe1 100644
--- a/ext/hash/hash_haval.c
+++ b/ext/hash/hash_haval.c
@@ -31,28 +31,28 @@ static const unsigned char PADDING[128] ={
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
-static const php_hash_uint32 D0[8] = {
+static const uint32_t D0[8] = {
0x243F6A88, 0x85A308D3, 0x13198A2E, 0x03707344, 0xA4093822, 0x299F31D0, 0x082EFA98, 0xEC4E6C89 };
-static const php_hash_uint32 K2[32] = {
+static const uint32_t K2[32] = {
0x452821E6, 0x38D01377, 0xBE5466CF, 0x34E90C6C, 0xC0AC29B7, 0xC97C50DD, 0x3F84D5B5, 0xB5470917,
0x9216D5D9, 0x8979FB1B, 0xD1310BA6, 0x98DFB5AC, 0x2FFD72DB, 0xD01ADFB7, 0xB8E1AFED, 0x6A267E96,
0xBA7C9045, 0xF12C7F99, 0x24A19947, 0xB3916CF7, 0x0801F2E2, 0x858EFC16, 0x636920D8, 0x71574E69,
0xA458FEA3, 0xF4933D7E, 0x0D95748F, 0x728EB658, 0x718BCD58, 0x82154AEE, 0x7B54A41D, 0xC25A59B5 };
-static const php_hash_uint32 K3[32] = {
+static const uint32_t K3[32] = {
0x9C30D539, 0x2AF26013, 0xC5D1B023, 0x286085F0, 0xCA417918, 0xB8DB38EF, 0x8E79DCB0, 0x603A180E,
0x6C9E0E8B, 0xB01E8A3E, 0xD71577C1, 0xBD314B27, 0x78AF2FDA, 0x55605C60, 0xE65525F3, 0xAA55AB94,
0x57489862, 0x63E81440, 0x55CA396A, 0x2AAB10B6, 0xB4CC5C34, 0x1141E8CE, 0xA15486AF, 0x7C72E993,
0xB3EE1411, 0x636FBC2A, 0x2BA9C55D, 0x741831F6, 0xCE5C3E16, 0x9B87931E, 0xAFD6BA33, 0x6C24CF5C };
-static const php_hash_uint32 K4[32] = {
+static const uint32_t K4[32] = {
0x7A325381, 0x28958677, 0x3B8F4898, 0x6B4BB9AF, 0xC4BFE81B, 0x66282193, 0x61D809CC, 0xFB21A991,
0x487CAC60, 0x5DEC8032, 0xEF845D5D, 0xE98575B1, 0xDC262302, 0xEB651B88, 0x23893E81, 0xD396ACC5,
0x0F6D6FF3, 0x83F44239, 0x2E0B4482, 0xA4842004, 0x69C8F04A, 0x9E1F9B5E, 0x21C66842, 0xF6E96C9A,
0x670C9C61, 0xABD388F0, 0x6A51A0D2, 0xD8542F68, 0x960FA728, 0xAB5133A3, 0x6EEF0B6C, 0x137A3BE4 };
-static const php_hash_uint32 K5[32] = {
+static const uint32_t K5[32] = {
0xBA3BF050, 0x7EFB2A98, 0xA1F1651D, 0x39AF0176, 0x66CA593E, 0x82430E88, 0x8CEE8619, 0x456F9FB4,
0x7D84A5C3, 0x3B8B5EBE, 0xE06F75D8, 0x85C12073, 0x401A449F, 0x56C16AA6, 0x4ED3AA62, 0x363F7706,
0x1BFEDF72, 0x429B023D, 0x37D0D724, 0xD00A1248, 0xDB0FEAD3, 0x49F1C09B, 0x075372C9, 0x80991B7B,
@@ -95,10 +95,10 @@ static const short M7[32] = { 7, 6, 5, 4, 3, 2, 1, 0, 7, 6, 5, 4, 3, 2, 1, 0,
7, 6, 5, 4, 3, 2, 1, 0, 7, 6, 5, 4, 3, 2, 1, 0 };
/* {{{ Encode
- Encodes input (php_hash_uint32) into output (unsigned char). Assumes len is
+ Encodes input (uint32_t) into output (unsigned char). Assumes len is
a multiple of 4.
*/
-static void Encode(unsigned char *output, php_hash_uint32 *input, unsigned int len)
+static void Encode(unsigned char *output, uint32_t *input, unsigned int len)
{
unsigned int i, j;
@@ -112,16 +112,16 @@ static void Encode(unsigned char *output, php_hash_uint32 *input, unsigned int l
/* }}} */
/* {{{ Decode
- Decodes input (unsigned char) into output (php_hash_uint32). Assumes len is
+ Decodes input (unsigned char) into output (uint32_t). Assumes len is
a multiple of 4.
*/
-static void Decode(php_hash_uint32 *output, const unsigned char *input, unsigned int len)
+static void Decode(uint32_t *output, const unsigned char *input, unsigned int len)
{
unsigned int i, j;
for (i = 0, j = 0; j < len; i++, j += 4) {
- output[i] = ((php_hash_uint32) input[j]) | (((php_hash_uint32) input[j + 1]) << 8) |
- (((php_hash_uint32) input[j + 2]) << 16) | (((php_hash_uint32) input[j + 3]) << 24);
+ output[i] = ((uint32_t) input[j]) | (((uint32_t) input[j + 1]) << 8) |
+ (((uint32_t) input[j + 2]) << 16) | (((uint32_t) input[j + 3]) << 24);
}
}
/* }}} */
@@ -141,10 +141,10 @@ static void Decode(php_hash_uint32 *output, const unsigned char *input, unsigned
/* {{{ PHP_3HAVALTransform
*/
-static void PHP_3HAVALTransform(php_hash_uint32 state[8], const unsigned char block[128])
+static void PHP_3HAVALTransform(uint32_t state[8], const unsigned char block[128])
{
- php_hash_uint32 E[8];
- php_hash_uint32 x[32];
+ uint32_t E[8];
+ uint32_t x[32];
int i;
Decode(x, block, 128);
@@ -175,10 +175,10 @@ static void PHP_3HAVALTransform(php_hash_uint32 state[8], const unsigned char bl
/* {{{ PHP_4HAVALTransform
*/
-static void PHP_4HAVALTransform(php_hash_uint32 state[8], const unsigned char block[128])
+static void PHP_4HAVALTransform(uint32_t state[8], const unsigned char block[128])
{
- php_hash_uint32 E[8];
- php_hash_uint32 x[32];
+ uint32_t E[8];
+ uint32_t x[32];
int i;
Decode(x, block, 128);
@@ -212,10 +212,10 @@ static void PHP_4HAVALTransform(php_hash_uint32 state[8], const unsigned char bl
/* {{{ PHP_5HAVALTransform
*/
-static void PHP_5HAVALTransform(php_hash_uint32 state[8], const unsigned char block[128])
+static void PHP_5HAVALTransform(uint32_t state[8], const unsigned char block[128])
{
- php_hash_uint32 E[8];
- php_hash_uint32 x[32];
+ uint32_t E[8];
+ uint32_t x[32];
int i;
Decode(x, block, 128);
@@ -289,10 +289,10 @@ PHP_HASH_API void PHP_HAVALUpdate(PHP_HAVAL_CTX *context, const unsigned char *i
/* Compute number of bytes mod 128 */
index = (unsigned int) ((context->count[0] >> 3) & 0x7F);
/* Update number of bits */
- if ((context->count[0] += ((php_hash_uint32) inputLen << 3)) < ((php_hash_uint32) inputLen << 3)) {
+ if ((context->count[0] += ((uint32_t) inputLen << 3)) < ((uint32_t) inputLen << 3)) {
context->count[1]++;
}
- context->count[1] += ((php_hash_uint32) inputLen >> 29);
+ context->count[1] += ((uint32_t) inputLen >> 29);
partLen = 128 - index;
diff --git a/ext/hash/hash_joaat.c b/ext/hash/hash_joaat.c
index 545601fc90..98a11a08bc 100644
--- a/ext/hash/hash_joaat.c
+++ b/ext/hash/hash_joaat.c
@@ -70,8 +70,8 @@ PHP_HASH_API void PHP_JOAATFinal(unsigned char digest[4], PHP_JOAAT_CTX * contex
* returns:
* 32 bit hash as a static hash type
*/
-static php_hash_uint32
-joaat_buf(void *buf, size_t len, php_hash_uint32 hval)
+static uint32_t
+joaat_buf(void *buf, size_t len, uint32_t hval)
{
size_t i;
unsigned char *input = (unsigned char *)buf;
diff --git a/ext/hash/hash_md.c b/ext/hash/hash_md.c
index 80681c34db..711238ad82 100644
--- a/ext/hash/hash_md.c
+++ b/ext/hash/hash_md.c
@@ -61,10 +61,10 @@ static const unsigned char PADDING[64] =
};
/* {{{ Encode
- Encodes input (php_hash_uint32) into output (unsigned char). Assumes len is
+ Encodes input (uint32_t) into output (unsigned char). Assumes len is
a multiple of 4.
*/
-static void Encode(unsigned char *output, php_hash_uint32 *input, unsigned int len)
+static void Encode(unsigned char *output, uint32_t *input, unsigned int len)
{
unsigned int i, j;
@@ -78,16 +78,16 @@ static void Encode(unsigned char *output, php_hash_uint32 *input, unsigned int l
/* }}} */
/* {{{ Decode
- Decodes input (unsigned char) into output (php_hash_uint32). Assumes len is
+ Decodes input (unsigned char) into output (uint32_t). Assumes len is
a multiple of 4.
*/
-static void Decode(php_hash_uint32 *output, const unsigned char *input, unsigned int len)
+static void Decode(uint32_t *output, const unsigned char *input, unsigned int len)
{
unsigned int i, j;
for (i = 0, j = 0; j < len; i++, j += 4)
- output[i] = ((php_hash_uint32) input[j]) | (((php_hash_uint32) input[j + 1]) << 8) |
- (((php_hash_uint32) input[j + 2]) << 16) | (((php_hash_uint32) input[j + 3]) << 24);
+ output[i] = ((uint32_t) input[j]) | (((uint32_t) input[j + 1]) << 8) |
+ (((uint32_t) input[j + 2]) << 16) | (((uint32_t) input[j + 3]) << 24);
}
/* }}} */
@@ -224,7 +224,7 @@ PHP_NAMED_FUNCTION(php_if_md5_file)
#define S43 15
#define S44 21
-static void MD5Transform(php_hash_uint32[4], const unsigned char[64]);
+static void MD5Transform(uint32_t[4], const unsigned char[64]);
/* F, G, H and I are basic MD5 functions.
*/
@@ -241,22 +241,22 @@ static void MD5Transform(php_hash_uint32[4], const unsigned char[64]);
Rotation is separate from addition to prevent recomputation.
*/
#define FF(a, b, c, d, x, s, ac) { \
- (a) += F ((b), (c), (d)) + (x) + (php_hash_uint32)(ac); \
+ (a) += F ((b), (c), (d)) + (x) + (uint32_t)(ac); \
(a) = ROTATE_LEFT ((a), (s)); \
(a) += (b); \
}
#define GG(a, b, c, d, x, s, ac) { \
- (a) += G ((b), (c), (d)) + (x) + (php_hash_uint32)(ac); \
+ (a) += G ((b), (c), (d)) + (x) + (uint32_t)(ac); \
(a) = ROTATE_LEFT ((a), (s)); \
(a) += (b); \
}
#define HH(a, b, c, d, x, s, ac) { \
- (a) += H ((b), (c), (d)) + (x) + (php_hash_uint32)(ac); \
+ (a) += H ((b), (c), (d)) + (x) + (uint32_t)(ac); \
(a) = ROTATE_LEFT ((a), (s)); \
(a) += (b); \
}
#define II(a, b, c, d, x, s, ac) { \
- (a) += I ((b), (c), (d)) + (x) + (php_hash_uint32)(ac); \
+ (a) += I ((b), (c), (d)) + (x) + (uint32_t)(ac); \
(a) = ROTATE_LEFT ((a), (s)); \
(a) += (b); \
}
@@ -290,10 +290,10 @@ PHP_HASH_API void PHP_MD5Update(PHP_MD5_CTX * context, const unsigned char *inpu
index = (unsigned int) ((context->count[0] >> 3) & 0x3F);
/* Update number of bits */
- if ((context->count[0] += ((php_hash_uint32) inputLen << 3))
- < ((php_hash_uint32) inputLen << 3))
+ if ((context->count[0] += ((uint32_t) inputLen << 3))
+ < ((uint32_t) inputLen << 3))
context->count[1]++;
- context->count[1] += ((php_hash_uint32) inputLen >> 29);
+ context->count[1] += ((uint32_t) inputLen >> 29);
partLen = 64 - index;
@@ -352,10 +352,10 @@ PHP_HASH_API void PHP_MD5Final(unsigned char digest[16], PHP_MD5_CTX * context)
* MD5 basic transformation. Transforms state based on block.
*/
static void MD5Transform(state, block)
-php_hash_uint32 state[4];
+uint32_t state[4];
const unsigned char block[64];
{
- php_hash_uint32 a = state[0], b = state[1], c = state[2], d = state[3], x[16];
+ uint32_t a = state[0], b = state[1], c = state[2], d = state[3], x[16];
Decode(x, block, 64);
@@ -455,9 +455,9 @@ const unsigned char block[64];
#define MD4_R2(a,b,c,d,k,s) a = ROTL32(s, a + MD4_G(b,c,d) + x[k] + 0x5A827999)
#define MD4_R3(a,b,c,d,k,s) a = ROTL32(s, a + MD4_H(b,c,d) + x[k] + 0x6ED9EBA1)
-static void MD4Transform(php_hash_uint32 state[4], const unsigned char block[64])
+static void MD4Transform(uint32_t state[4], const unsigned char block[64])
{
- php_hash_uint32 a = state[0], b = state[1], c = state[2], d = state[3], x[16];
+ uint32_t a = state[0], b = state[1], c = state[2], d = state[3], x[16];
Decode(x, block, 64);
@@ -549,10 +549,10 @@ PHP_HASH_API void PHP_MD4Update(PHP_MD4_CTX * context, const unsigned char *inpu
index = (unsigned int) ((context->count[0] >> 3) & 0x3F);
/* Update number of bits */
- if ((context->count[0] += ((php_hash_uint32) inputLen << 3))
- < ((php_hash_uint32) inputLen << 3))
+ if ((context->count[0] += ((uint32_t) inputLen << 3))
+ < ((uint32_t) inputLen << 3))
context->count[1]++;
- context->count[1] += ((php_hash_uint32) inputLen >> 29);
+ context->count[1] += ((uint32_t) inputLen >> 29);
partLen = 64 - index;
diff --git a/ext/hash/hash_ripemd.c b/ext/hash/hash_ripemd.c
index 2f22a18d53..03d4af8349 100644
--- a/ext/hash/hash_ripemd.c
+++ b/ext/hash/hash_ripemd.c
@@ -143,9 +143,9 @@ PHP_HASH_API void PHP_RIPEMD320Init(PHP_RIPEMD320_CTX * context)
#define F3(x,y,z) (((x) & (z)) | ((y) & (~(z))))
#define F4(x,y,z) ((x) ^ ((y) | (~(z))))
-static const php_hash_uint32 K_values[5] = { 0x00000000, 0x5A827999, 0x6ED9EBA1, 0x8F1BBCDC, 0xA953FD4E }; /* 128, 256, 160, 320 */
-static const php_hash_uint32 KK_values[4] = { 0x50A28BE6, 0x5C4DD124, 0x6D703EF3, 0x00000000 }; /* 128 & 256 */
-static const php_hash_uint32 KK160_values[5] = { 0x50A28BE6, 0x5C4DD124, 0x6D703EF3, 0x7A6D76E9, 0x00000000 }; /* 160 & 320 */
+static const uint32_t K_values[5] = { 0x00000000, 0x5A827999, 0x6ED9EBA1, 0x8F1BBCDC, 0xA953FD4E }; /* 128, 256, 160, 320 */
+static const uint32_t KK_values[4] = { 0x50A28BE6, 0x5C4DD124, 0x6D703EF3, 0x00000000 }; /* 128 & 256 */
+static const uint32_t KK160_values[5] = { 0x50A28BE6, 0x5C4DD124, 0x6D703EF3, 0x7A6D76E9, 0x00000000 }; /* 160 & 320 */
#define K(n) K_values[ (n) >> 4]
#define KK(n) KK_values[(n) >> 4]
@@ -184,27 +184,27 @@ static const unsigned char SS[80] = {
#define ROL(n, x) (((x) << n) | ((x) >> (32 - n)))
/* {{{ RIPEMDDecode
- Decodes input (unsigned char) into output (php_hash_uint32). Assumes len is
+ Decodes input (unsigned char) into output (uint32_t). Assumes len is
a multiple of 4.
*/
-static void RIPEMDDecode(php_hash_uint32 *output, const unsigned char *input, unsigned int len)
+static void RIPEMDDecode(uint32_t *output, const unsigned char *input, unsigned int len)
{
unsigned int i, j;
for (i = 0, j = 0; j < len; i++, j += 4)
- output[i] = ((php_hash_uint32) input[j + 0]) | (((php_hash_uint32) input[j + 1]) << 8) |
- (((php_hash_uint32) input[j + 2]) << 16) | (((php_hash_uint32) input[j + 3]) << 24);
+ output[i] = ((uint32_t) input[j + 0]) | (((uint32_t) input[j + 1]) << 8) |
+ (((uint32_t) input[j + 2]) << 16) | (((uint32_t) input[j + 3]) << 24);
}
/* }}} */
/* {{{ RIPEMD128Transform
* ripemd128 basic transformation. Transforms state based on block.
*/
-static void RIPEMD128Transform(php_hash_uint32 state[4], const unsigned char block[64])
+static void RIPEMD128Transform(uint32_t state[4], const unsigned char block[64])
{
- php_hash_uint32 a = state[0], b = state[1], c = state[2], d = state[3];
- php_hash_uint32 aa = state[0], bb = state[1], cc = state[2], dd = state[3];
- php_hash_uint32 tmp, x[16];
+ uint32_t a = state[0], b = state[1], c = state[2], d = state[3];
+ uint32_t aa = state[0], bb = state[1], cc = state[2], dd = state[3];
+ uint32_t tmp, x[16];
int j;
RIPEMDDecode(x, block, 64);
@@ -261,10 +261,10 @@ PHP_HASH_API void PHP_RIPEMD128Update(PHP_RIPEMD128_CTX * context, const unsigne
index = (unsigned int) ((context->count[0] >> 3) & 0x3F);
/* Update number of bits */
- if ((context->count[0] += ((php_hash_uint32) inputLen << 3)) < ((php_hash_uint32) inputLen << 3)) {
+ if ((context->count[0] += ((uint32_t) inputLen << 3)) < ((uint32_t) inputLen << 3)) {
context->count[1]++;
}
- context->count[1] += ((php_hash_uint32) inputLen >> 29);
+ context->count[1] += ((uint32_t) inputLen >> 29);
partLen = 64 - index;
@@ -291,11 +291,11 @@ PHP_HASH_API void PHP_RIPEMD128Update(PHP_RIPEMD128_CTX * context, const unsigne
/* {{{ RIPEMD256Transform
* ripemd256 basic transformation. Transforms state based on block.
*/
-static void RIPEMD256Transform(php_hash_uint32 state[8], const unsigned char block[64])
+static void RIPEMD256Transform(uint32_t state[8], const unsigned char block[64])
{
- php_hash_uint32 a = state[0], b = state[1], c = state[2], d = state[3];
- php_hash_uint32 aa = state[4], bb = state[5], cc = state[6], dd = state[7];
- php_hash_uint32 tmp, x[16];
+ uint32_t a = state[0], b = state[1], c = state[2], d = state[3];
+ uint32_t aa = state[4], bb = state[5], cc = state[6], dd = state[7];
+ uint32_t tmp, x[16];
int j;
RIPEMDDecode(x, block, 64);
@@ -359,10 +359,10 @@ PHP_HASH_API void PHP_RIPEMD256Update(PHP_RIPEMD256_CTX * context, const unsigne
index = (unsigned int) ((context->count[0] >> 3) & 0x3F);
/* Update number of bits */
- if ((context->count[0] += ((php_hash_uint32) inputLen << 3)) < ((php_hash_uint32) inputLen << 3)) {
+ if ((context->count[0] += ((uint32_t) inputLen << 3)) < ((uint32_t) inputLen << 3)) {
context->count[1]++;
}
- context->count[1] += ((php_hash_uint32) inputLen >> 29);
+ context->count[1] += ((uint32_t) inputLen >> 29);
partLen = 64 - index;
@@ -389,11 +389,11 @@ PHP_HASH_API void PHP_RIPEMD256Update(PHP_RIPEMD256_CTX * context, const unsigne
/* {{{ RIPEMD160Transform
* ripemd160 basic transformation. Transforms state based on block.
*/
-static void RIPEMD160Transform(php_hash_uint32 state[5], const unsigned char block[64])
+static void RIPEMD160Transform(uint32_t state[5], const unsigned char block[64])
{
- php_hash_uint32 a = state[0], b = state[1], c = state[2], d = state[3], e = state[4];
- php_hash_uint32 aa = state[0], bb = state[1], cc = state[2], dd = state[3], ee = state[4];
- php_hash_uint32 tmp, x[16];
+ uint32_t a = state[0], b = state[1], c = state[2], d = state[3], e = state[4];
+ uint32_t aa = state[0], bb = state[1], cc = state[2], dd = state[3], ee = state[4];
+ uint32_t tmp, x[16];
int j;
RIPEMDDecode(x, block, 64);
@@ -458,10 +458,10 @@ PHP_HASH_API void PHP_RIPEMD160Update(PHP_RIPEMD160_CTX * context, const unsigne
index = (unsigned int) ((context->count[0] >> 3) & 0x3F);
/* Update number of bits */
- if ((context->count[0] += ((php_hash_uint32) inputLen << 3)) < ((php_hash_uint32) inputLen << 3)) {
+ if ((context->count[0] += ((uint32_t) inputLen << 3)) < ((uint32_t) inputLen << 3)) {
context->count[1]++;
}
- context->count[1] += ((php_hash_uint32) inputLen >> 29);
+ context->count[1] += ((uint32_t) inputLen >> 29);
partLen = 64 - index;
@@ -488,11 +488,11 @@ PHP_HASH_API void PHP_RIPEMD160Update(PHP_RIPEMD160_CTX * context, const unsigne
/* {{{ RIPEMD320Transform
* ripemd320 basic transformation. Transforms state based on block.
*/
-static void RIPEMD320Transform(php_hash_uint32 state[10], const unsigned char block[64])
+static void RIPEMD320Transform(uint32_t state[10], const unsigned char block[64])
{
- php_hash_uint32 a = state[0], b = state[1], c = state[2], d = state[3], e = state[4];
- php_hash_uint32 aa = state[5], bb = state[6], cc = state[7], dd = state[8], ee = state[9];
- php_hash_uint32 tmp, x[16];
+ uint32_t a = state[0], b = state[1], c = state[2], d = state[3], e = state[4];
+ uint32_t aa = state[5], bb = state[6], cc = state[7], dd = state[8], ee = state[9];
+ uint32_t tmp, x[16];
int j;
RIPEMDDecode(x, block, 64);
@@ -566,10 +566,10 @@ PHP_HASH_API void PHP_RIPEMD320Update(PHP_RIPEMD320_CTX * context, const unsigne
index = (unsigned int) ((context->count[0] >> 3) & 0x3F);
/* Update number of bits */
- if ((context->count[0] += ((php_hash_uint32) inputLen << 3)) < ((php_hash_uint32) inputLen << 3)) {
+ if ((context->count[0] += ((uint32_t) inputLen << 3)) < ((uint32_t) inputLen << 3)) {
context->count[1]++;
}
- context->count[1] += ((php_hash_uint32) inputLen >> 29);
+ context->count[1] += ((uint32_t) inputLen >> 29);
partLen = 64 - index;
@@ -601,10 +601,10 @@ static const unsigned char PADDING[64] =
};
/* {{{ RIPEMDEncode
- Encodes input (php_hash_uint32) into output (unsigned char). Assumes len is
+ Encodes input (uint32_t) into output (unsigned char). Assumes len is
a multiple of 4.
*/
-static void RIPEMDEncode(unsigned char *output, php_hash_uint32 *input, unsigned int len)
+static void RIPEMDEncode(unsigned char *output, uint32_t *input, unsigned int len)
{
unsigned int i, j;
diff --git a/ext/hash/hash_sha.c b/ext/hash/hash_sha.c
index 77c9bb7b4f..d158faaa64 100644
--- a/ext/hash/hash_sha.c
+++ b/ext/hash/hash_sha.c
@@ -35,10 +35,10 @@ static const unsigned char PADDING[128] =
};
/* {{{ SHAEncode32
- Encodes input (php_hash_uint32) into output (unsigned char). Assumes len is
+ Encodes input (uint32_t) into output (unsigned char). Assumes len is
a multiple of 4.
*/
-static void SHAEncode32(unsigned char *output, php_hash_uint32 *input, unsigned int len)
+static void SHAEncode32(unsigned char *output, uint32_t *input, unsigned int len)
{
unsigned int i, j;
@@ -53,16 +53,16 @@ static void SHAEncode32(unsigned char *output, php_hash_uint32 *input, unsigned
/* {{{ SHADecode32
- Decodes input (unsigned char) into output (php_hash_uint32). Assumes len is
+ Decodes input (unsigned char) into output (uint32_t). Assumes len is
a multiple of 4.
*/
-static void SHADecode32(php_hash_uint32 *output, const unsigned char *input, unsigned int len)
+static void SHADecode32(uint32_t *output, const unsigned char *input, unsigned int len)
{
unsigned int i, j;
for (i = 0, j = 0; j < len; i++, j += 4)
- output[i] = ((php_hash_uint32) input[j + 3]) | (((php_hash_uint32) input[j + 2]) << 8) |
- (((php_hash_uint32) input[j + 1]) << 16) | (((php_hash_uint32) input[j]) << 24);
+ output[i] = ((uint32_t) input[j + 3]) | (((uint32_t) input[j + 2]) << 8) |
+ (((uint32_t) input[j + 1]) << 16) | (((uint32_t) input[j]) << 24);
}
/* }}} */
@@ -179,22 +179,22 @@ PHP_FUNCTION(sha1_file)
/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4.
*/
#define FF(a, b, c, d, e, w) { \
- (e) += F ((b), (c), (d)) + (w) + (php_hash_uint32)(0x5A827999); \
+ (e) += F ((b), (c), (d)) + (w) + (uint32_t)(0x5A827999); \
(e) += ROTATE_LEFT ((a), 5); \
(b) = ROTATE_LEFT((b), 30); \
}
#define GG(a, b, c, d, e, w) { \
- (e) += G ((b), (c), (d)) + (w) + (php_hash_uint32)(0x6ED9EBA1); \
+ (e) += G ((b), (c), (d)) + (w) + (uint32_t)(0x6ED9EBA1); \
(e) += ROTATE_LEFT ((a), 5); \
(b) = ROTATE_LEFT((b), 30); \
}
#define HH(a, b, c, d, e, w) { \
- (e) += H ((b), (c), (d)) + (w) + (php_hash_uint32)(0x8F1BBCDC); \
+ (e) += H ((b), (c), (d)) + (w) + (uint32_t)(0x8F1BBCDC); \
(e) += ROTATE_LEFT ((a), 5); \
(b) = ROTATE_LEFT((b), 30); \
}
#define II(a, b, c, d, e, w) { \
- (e) += I ((b), (c), (d)) + (w) + (php_hash_uint32)(0xCA62C1D6); \
+ (e) += I ((b), (c), (d)) + (w) + (uint32_t)(0xCA62C1D6); \
(e) += ROTATE_LEFT ((a), 5); \
(b) = ROTATE_LEFT((b), 30); \
}
@@ -219,10 +219,10 @@ PHP_HASH_API void PHP_SHA1Init(PHP_SHA1_CTX * context)
/* {{{ SHA1Transform
* SHA1 basic transformation. Transforms state based on block.
*/
-static void SHA1Transform(php_hash_uint32 state[5], const unsigned char block[64])
+static void SHA1Transform(uint32_t state[5], const unsigned char block[64])
{
- php_hash_uint32 a = state[0], b = state[1], c = state[2];
- php_hash_uint32 d = state[3], e = state[4], x[16], tmp;
+ uint32_t a = state[0], b = state[1], c = state[2];
+ uint32_t d = state[3], e = state[4], x[16], tmp;
SHADecode32(x, block, 64);
@@ -339,10 +339,10 @@ PHP_HASH_API void PHP_SHA1Update(PHP_SHA1_CTX * context, const unsigned char *in
index = (unsigned int) ((context->count[0] >> 3) & 0x3F);
/* Update number of bits */
- if ((context->count[0] += ((php_hash_uint32) inputLen << 3))
- < ((php_hash_uint32) inputLen << 3))
+ if ((context->count[0] += ((uint32_t) inputLen << 3))
+ < ((uint32_t) inputLen << 3))
context->count[1]++;
- context->count[1] += ((php_hash_uint32) inputLen >> 29);
+ context->count[1] += ((uint32_t) inputLen >> 29);
partLen = 64 - index;
@@ -445,7 +445,7 @@ const php_hash_ops php_hash_sha224_ops = {
/* OM1 */
#define SHA256_F5(x) (ROTR32(17,(x)) ^ ROTR32(19,(x)) ^ SHR(10,(x)))
-static const php_hash_uint32 SHA256_K[64] = {
+static const uint32_t SHA256_K[64] = {
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
@@ -477,11 +477,11 @@ PHP_HASH_API void PHP_SHA256Init(PHP_SHA256_CTX * context)
/* {{{ SHA256Transform
* SHA256 basic transformation. Transforms state based on block.
*/
-static void SHA256Transform(php_hash_uint32 state[8], const unsigned char block[64])
+static void SHA256Transform(uint32_t state[8], const unsigned char block[64])
{
- php_hash_uint32 a = state[0], b = state[1], c = state[2], d = state[3];
- php_hash_uint32 e = state[4], f = state[5], g = state[6], h = state[7];
- php_hash_uint32 x[16], T1, T2, W[64];
+ uint32_t a = state[0], b = state[1], c = state[2], d = state[3];
+ uint32_t e = state[4], f = state[5], g = state[6], h = state[7];
+ uint32_t x[16], T1, T2, W[64];
int i;
SHADecode32(x, block, 64);
@@ -547,10 +547,10 @@ PHP_HASH_API void PHP_SHA224Update(PHP_SHA224_CTX * context, const unsigned char
index = (unsigned int) ((context->count[0] >> 3) & 0x3F);
/* Update number of bits */
- if ((context->count[0] += ((php_hash_uint32) inputLen << 3)) < ((php_hash_uint32) inputLen << 3)) {
+ if ((context->count[0] += ((uint32_t) inputLen << 3)) < ((uint32_t) inputLen << 3)) {
context->count[1]++;
}
- context->count[1] += ((php_hash_uint32) inputLen >> 29);
+ context->count[1] += ((uint32_t) inputLen >> 29);
partLen = 64 - index;
@@ -624,10 +624,10 @@ PHP_HASH_API void PHP_SHA256Update(PHP_SHA256_CTX * context, const unsigned char
index = (unsigned int) ((context->count[0] >> 3) & 0x3F);
/* Update number of bits */
- if ((context->count[0] += ((php_hash_uint32) inputLen << 3)) < ((php_hash_uint32) inputLen << 3)) {
+ if ((context->count[0] += ((uint32_t) inputLen << 3)) < ((uint32_t) inputLen << 3)) {
context->count[1]++;
}
- context->count[1] += ((php_hash_uint32) inputLen >> 29);
+ context->count[1] += ((uint32_t) inputLen >> 29);
partLen = 64 - index;
@@ -703,7 +703,7 @@ PHP_HASH_API void PHP_SHA256Final(unsigned char digest[32], PHP_SHA256_CTX * con
/* OM1 */
#define SHA512_F5(x) (ROTR64(19, x) ^ ROTR64(61, x) ^ SHR(6, x))
-static const php_hash_uint64 SHA512_K[128] = {
+static const uint64_t SHA512_K[128] = {
L64(0x428a2f98d728ae22), L64(0x7137449123ef65cd), L64(0xb5c0fbcfec4d3b2f), L64(0xe9b5dba58189dbbc),
L64(0x3956c25bf348b538), L64(0x59f111f1b605d019), L64(0x923f82a4af194f9b), L64(0xab1c5ed5da6d8118),
L64(0xd807aa98a3030242), L64(0x12835b0145706fbe), L64(0x243185be4ee4b28c), L64(0x550c7dc3d5ffb4e2),
@@ -726,10 +726,10 @@ static const php_hash_uint64 SHA512_K[128] = {
L64(0x4cc5d4becb3e42b6), L64(0x597f299cfc657e2a), L64(0x5fcb6fab3ad6faec), L64(0x6c44198c4a475817) };
/* {{{ SHAEncode64
- Encodes input (php_hash_uint64) into output (unsigned char). Assumes len is
+ Encodes input (uint64_t) into output (unsigned char). Assumes len is
a multiple of 8.
*/
-static void SHAEncode64(unsigned char *output, php_hash_uint64 *input, unsigned int len)
+static void SHAEncode64(unsigned char *output, uint64_t *input, unsigned int len)
{
unsigned int i, j;
@@ -748,19 +748,19 @@ static void SHAEncode64(unsigned char *output, php_hash_uint64 *input, unsigned
/* {{{ SHADecode64
- Decodes input (unsigned char) into output (php_hash_uint64). Assumes len is
+ Decodes input (unsigned char) into output (uint64_t). Assumes len is
a multiple of 8.
*/
-static void SHADecode64(php_hash_uint64 *output, const unsigned char *input, unsigned int len)
+static void SHADecode64(uint64_t *output, const unsigned char *input, unsigned int len)
{
unsigned int i, j;
for (i = 0, j = 0; j < len; i++, j += 8)
output[i] =
- ((php_hash_uint64) input[j + 7]) | (((php_hash_uint64) input[j + 6]) << 8) |
- (((php_hash_uint64) input[j + 5]) << 16) | (((php_hash_uint64) input[j + 4]) << 24) |
- (((php_hash_uint64) input[j + 3]) << 32) | (((php_hash_uint64) input[j + 2]) << 40) |
- (((php_hash_uint64) input[j + 1]) << 48) | (((php_hash_uint64) input[j]) << 56);
+ ((uint64_t) input[j + 7]) | (((uint64_t) input[j + 6]) << 8) |
+ (((uint64_t) input[j + 5]) << 16) | (((uint64_t) input[j + 4]) << 24) |
+ (((uint64_t) input[j + 3]) << 32) | (((uint64_t) input[j + 2]) << 40) |
+ (((uint64_t) input[j + 1]) << 48) | (((uint64_t) input[j]) << 56);
}
/* }}} */
@@ -787,11 +787,11 @@ PHP_HASH_API void PHP_SHA384Init(PHP_SHA384_CTX * context)
* SHA512 basic transformation. Transforms state based on block.
* SHA384 uses the exact same algorithm
*/
-static void SHA512Transform(php_hash_uint64 state[8], const unsigned char block[128])
+static void SHA512Transform(uint64_t state[8], const unsigned char block[128])
{
- php_hash_uint64 a = state[0], b = state[1], c = state[2], d = state[3];
- php_hash_uint64 e = state[4], f = state[5], g = state[6], h = state[7];
- php_hash_uint64 x[16], T1, T2, W[80];
+ uint64_t a = state[0], b = state[1], c = state[2], d = state[3];
+ uint64_t e = state[4], f = state[5], g = state[6], h = state[7];
+ uint64_t x[16], T1, T2, W[80];
int i;
SHADecode64(x, block, 128);
@@ -838,10 +838,10 @@ PHP_HASH_API void PHP_SHA384Update(PHP_SHA384_CTX * context, const unsigned char
index = (unsigned int) ((context->count[0] >> 3) & 0x7F);
/* Update number of bits */
- if ((context->count[0] += ((php_hash_uint64) inputLen << 3)) < ((php_hash_uint64) inputLen << 3)) {
+ if ((context->count[0] += ((uint64_t) inputLen << 3)) < ((uint64_t) inputLen << 3)) {
context->count[1]++;
}
- context->count[1] += ((php_hash_uint64) inputLen >> 61);
+ context->count[1] += ((uint64_t) inputLen >> 61);
partLen = 128 - index;
@@ -939,6 +939,42 @@ PHP_HASH_API void PHP_SHA512Init(PHP_SHA512_CTX * context)
}
/* }}} */
+/* {{{ PHP_SHA512_256Init
+ * SHA512/245 initialization. Identical algorithm to SHA512, using alternate initval and truncation
+ */
+PHP_HASH_API void PHP_SHA512_256Init(PHP_SHA512_CTX * context)
+{
+ context->count[0] = context->count[1] = 0;
+
+ context->state[0] = L64(0x22312194FC2BF72C);
+ context->state[1] = L64(0x9F555FA3C84C64C2);
+ context->state[2] = L64(0x2393B86B6F53B151);
+ context->state[3] = L64(0x963877195940EABD);
+ context->state[4] = L64(0x96283EE2A88EFFE3);
+ context->state[5] = L64(0xBE5E1E2553863992);
+ context->state[6] = L64(0x2B0199FC2C85B8AA);
+ context->state[7] = L64(0x0EB72DDC81C52CA2);
+}
+/* }}} */
+
+/* {{{ PHP_SHA512_224Init
+ * SHA512/224 initialization. Identical algorithm to SHA512, using alternate initval and truncation
+ */
+PHP_HASH_API void PHP_SHA512_224Init(PHP_SHA512_CTX * context)
+{
+ context->count[0] = context->count[1] = 0;
+
+ context->state[0] = L64(0x8C3D37C819544DA2);
+ context->state[1] = L64(0x73E1996689DCD4D6);
+ context->state[2] = L64(0x1DFAB7AE32FF9C82);
+ context->state[3] = L64(0x679DD514582F9FCF);
+ context->state[4] = L64(0x0F6D2B697BD44DA8);
+ context->state[5] = L64(0x77E36F7304C48942);
+ context->state[6] = L64(0x3F9D85A86A1D36C8);
+ context->state[7] = L64(0x1112E6AD91D692A1);
+}
+/* }}} */
+
/* {{{ PHP_SHA512Update
SHA512 block update operation. Continues an SHA512 message-digest
operation, processing another message block, and updating the
@@ -952,10 +988,10 @@ PHP_HASH_API void PHP_SHA512Update(PHP_SHA512_CTX * context, const unsigned char
index = (unsigned int) ((context->count[0] >> 3) & 0x7F);
/* Update number of bits */
- if ((context->count[0] += ((php_hash_uint64) inputLen << 3)) < ((php_hash_uint64) inputLen << 3)) {
+ if ((context->count[0] += ((uint64_t) inputLen << 3)) < ((uint64_t) inputLen << 3)) {
context->count[1]++;
}
- context->count[1] += ((php_hash_uint64) inputLen >> 61);
+ context->count[1] += ((uint64_t) inputLen >> 61);
partLen = 128 - index;
@@ -1024,6 +1060,28 @@ PHP_HASH_API void PHP_SHA512Final(unsigned char digest[64], PHP_SHA512_CTX * con
}
/* }}} */
+/* {{{ PHP_SHA512_256Final
+ SHA512/256 finalization. Identical to SHA512Final, but with truncation
+ */
+PHP_HASH_API void PHP_SHA512_256Final(unsigned char digest[32], PHP_SHA512_CTX * context)
+{
+ unsigned char full_digest[64];
+ PHP_SHA512Final(full_digest, context);
+ memcpy(digest, full_digest, 32);
+}
+/* }}} */
+
+/* {{{ PHP_SHA512_224Final
+ SHA512/224 finalization. Identical to SHA512Final, but with truncation
+ */
+PHP_HASH_API void PHP_SHA512_224Final(unsigned char digest[28], PHP_SHA512_CTX * context)
+{
+ unsigned char full_digest[64];
+ PHP_SHA512Final(full_digest, context);
+ memcpy(digest, full_digest, 28);
+}
+/* }}} */
+
const php_hash_ops php_hash_sha512_ops = {
(php_hash_init_func_t) PHP_SHA512Init,
(php_hash_update_func_t) PHP_SHA512Update,
@@ -1034,6 +1092,26 @@ const php_hash_ops php_hash_sha512_ops = {
sizeof(PHP_SHA512_CTX)
};
+const php_hash_ops php_hash_sha512_256_ops = {
+ (php_hash_init_func_t) PHP_SHA512_256Init,
+ (php_hash_update_func_t) PHP_SHA512_256Update,
+ (php_hash_final_func_t) PHP_SHA512_256Final,
+ (php_hash_copy_func_t) php_hash_copy,
+ 32,
+ 128,
+ sizeof(PHP_SHA512_CTX)
+};
+
+const php_hash_ops php_hash_sha512_224_ops = {
+ (php_hash_init_func_t) PHP_SHA512_224Init,
+ (php_hash_update_func_t) PHP_SHA512_224Update,
+ (php_hash_final_func_t) PHP_SHA512_224Final,
+ (php_hash_copy_func_t) php_hash_copy,
+ 28,
+ 128,
+ sizeof(PHP_SHA512_CTX)
+};
+
/*
* Local variables:
* tab-width: 4
diff --git a/ext/hash/hash_sha3.c b/ext/hash/hash_sha3.c
new file mode 100644
index 0000000000..aab17c1476
--- /dev/null
+++ b/ext/hash/hash_sha3.c
@@ -0,0 +1,238 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 7 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2016 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: Sara Golemon <pollita@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#include "php_hash_sha3.h"
+
+#if (defined(__APPLE__) || defined(__APPLE_CC__)) && \
+ (defined(__BIG_ENDIAN__) || defined(__LITTLE_ENDIAN__))
+# if defined(__LITTLE_ENDIAN__)
+# undef WORDS_BIGENDIAN
+# else
+# if defined(__BIG_ENDIAN__)
+# define WORDS_BIGENDIAN
+# endif
+# endif
+#endif
+
+static inline uint64_t rol64(uint64_t v, unsigned char b) {
+ return (v << b) | (v >> (64 - b));
+}
+static inline unsigned char idx(unsigned char x, unsigned char y) {
+ return x + (5 * y);
+}
+
+#ifdef WORDS_BIGENDIAN
+static inline uint64_t load64(const unsigned char* x) {
+ unsigned char i;
+ uint64_t ret = 0;
+ for (i = 7; i >= 0; --i) {
+ ret <<= 8;
+ ret |= x[i];
+ }
+ return ret;
+}
+static inline void store64(unsigned char* x, uint64_t val) {
+ unsigned char i;
+ for (i = 0; i < 8; ++i) {
+ x[i] = val & 0xFF;
+ val >>= 8;
+ }
+}
+static inline void xor64(unsigned char* x, uint64_t val) {
+ unsigned char i;
+ for (i = 0; i < 8; ++i) {
+ x[i] ^= val & 0xFF;
+ val >>= 8;
+ }
+}
+# define readLane(x, y) load64(ctx->state+sizeof(uint64_t)*idx(x, y))
+# define writeLane(x, y, v) store64(ctx->state+sizeof(uint64_t)*idx(x, y), v)
+# define XORLane(x, y, v) xor64(ctx->state+sizeof(uint64_t)*idx(x, y), v)
+#else
+# define readLane(x, y) (((uint64_t*)ctx->state)[idx(x,y)])
+# define writeLane(x, y, v) (((uint64_t*)ctx->state)[idx(x,y)] = v)
+# define XORLane(x, y, v) (((uint64_t*)ctx->state)[idx(x,y)] ^= v)
+#endif
+
+static inline char LFSR86540(unsigned char* pLFSR)
+{
+ unsigned char LFSR = *pLFSR;
+ char result = LFSR & 0x01;
+ if (LFSR & 0x80) {
+ // Primitive polynomial over GF(2): x^8+x^6+x^5+x^4+1
+ LFSR = (LFSR << 1) ^ 0x71;
+ } else {
+ LFSR <<= 1;
+ }
+ *pLFSR = LFSR;
+ return result;
+}
+
+static void permute(PHP_SHA3_CTX* ctx) {
+ unsigned char LFSRstate = 0x01;
+ unsigned char round;
+
+ for (round = 0; round < 24; ++round) {
+ { // Theta step (see [Keccak Reference, Section 2.3.2])
+ uint64_t C[5], D;
+ unsigned char x, y;
+ for (x = 0; x < 5; ++x) {
+ C[x] = readLane(x, 0) ^ readLane(x, 1) ^
+ readLane(x, 2) ^ readLane(x, 3) ^ readLane(x, 4);
+ }
+ for (x = 0; x < 5; ++x) {
+ D = C[(x+4)%5] ^ rol64(C[(x+1)%5], 1);
+ for (y = 0; y < 5; ++y) {
+ XORLane(x, y, D);
+ }
+ }
+ }
+
+ { // p and Pi steps (see [Keccak Reference, Sections 2.3.3 and 2.3.4])
+ unsigned char x = 1, y = 0, t;
+ uint64_t current = readLane(x, y);
+ for (t = 0; t < 24; ++t) {
+ unsigned char r = ((t + 1) * (t + 2) / 2) % 64;
+ unsigned char Y = (2*x + 3*y) % 5;
+ uint64_t temp;
+ x = y;
+ y = Y;
+ temp = readLane(x, y);
+ writeLane(x, y, rol64(current, r));
+ current = temp;
+ }
+ }
+
+ { // X step (see [Keccak Reference, Section 2.3.1])
+ unsigned char x, y;
+ for (y = 0; y < 5; ++y) {
+ uint64_t temp[5];
+ for (x = 0; x < 5; ++x) {
+ temp[x] = readLane(x, y);
+ }
+ for (x = 0; x < 5; ++x) {
+ writeLane(x, y, temp[x] ^((~temp[(x+1)%5]) & temp[(x+2)%5]));
+ }
+ }
+ }
+
+ { // i step (see [Keccak Reference, Section 2.3.5])
+ unsigned char j;
+ for (j = 0; j < 7; ++j) {
+ if (LFSR86540(&LFSRstate)) {
+ uint64_t bitPos = (1<<j) - 1;
+ XORLane(0, 0, (uint64_t)1 << bitPos);
+ }
+ }
+ }
+ }
+}
+
+// ==========================================================================
+
+static void PHP_SHA3_Init(PHP_SHA3_CTX* ctx,
+ int bits) {
+ memset(ctx, 0, sizeof(PHP_SHA3_CTX));
+}
+
+static void PHP_SHA3_Update(PHP_SHA3_CTX* ctx,
+ const unsigned char* buf,
+ unsigned int count,
+ int block_size) {
+ while (count > 0) {
+ int len = block_size - ctx->pos;
+ if (len > count) len = count;
+ count -= len;
+ while (len-- > 0) {
+ ctx->state[ctx->pos++] ^= *(buf++);
+ }
+ if (ctx->pos >= block_size) {
+ permute(ctx);
+ ctx->pos = 0;
+ }
+ }
+}
+
+static void PHP_SHA3_Final(unsigned char* digest,
+ PHP_SHA3_CTX* ctx,
+ int block_size,
+ int digest_size) {
+ int len = digest_size;
+
+ // Pad state to finalize
+ ctx->state[ctx->pos++] ^= 0x06;
+ ctx->state[block_size-1] ^= 0x80;
+ permute(ctx);
+
+ // Square output for digest
+ for(;;) {
+ int bs = (len < block_size) ? len : block_size;
+ memcpy(digest, ctx->state, bs);
+ digest += bs;
+ len -= bs;
+ if (!len) break;
+ permute(ctx);
+ }
+
+ // Zero out context
+ memset(ctx, 0, sizeof(PHP_SHA3_CTX));
+}
+
+// ==========================================================================
+
+#define DECLARE_SHA3_OPS(bits) \
+void PHP_SHA3##bits##Init(PHP_SHA3_##bits##_CTX* ctx) { \
+ PHP_SHA3_Init(ctx, bits); \
+} \
+void PHP_SHA3##bits##Update(PHP_SHA3_##bits##_CTX* ctx, \
+ const unsigned char* input, \
+ unsigned int inputLen) { \
+ PHP_SHA3_Update(ctx, input, inputLen, \
+ (1600 - (2 * bits)) >> 3); \
+} \
+void PHP_SHA3##bits##Final(unsigned char* digest, \
+ PHP_SHA3_##bits##_CTX* ctx) { \
+ PHP_SHA3_Final(digest, ctx, \
+ (1600 - (2 * bits)) >> 3, \
+ bits >> 3); \
+} \
+const php_hash_ops php_hash_sha3_##bits##_ops = { \
+ (php_hash_init_func_t) PHP_SHA3##bits##Init, \
+ (php_hash_update_func_t) PHP_SHA3##bits##Update, \
+ (php_hash_final_func_t) PHP_SHA3##bits##Final, \
+ php_hash_copy, \
+ bits >> 3, \
+ (1600 - (2 * bits)) >> 3, \
+ sizeof(PHP_SHA3_##bits##_CTX) \
+}
+
+DECLARE_SHA3_OPS(224);
+DECLARE_SHA3_OPS(256);
+DECLARE_SHA3_OPS(384);
+DECLARE_SHA3_OPS(512);
+
+#undef DECLARE_SHA3_OPS
+
+/*
+ * 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/ext/hash/hash_snefru.c b/ext/hash/hash_snefru.c
index 1d94343c07..c7251b0bb1 100644
--- a/ext/hash/hash_snefru.c
+++ b/ext/hash/hash_snefru.c
@@ -33,7 +33,7 @@
#endif
#if DBG_SNEFRU
-void ph(php_hash_uint32 h[16])
+void ph(uint32_t h[16])
{
int i;
for (i = 0; i < 16; i++)
@@ -41,12 +41,12 @@ void ph(php_hash_uint32 h[16])
}
#endif
-static inline void Snefru(php_hash_uint32 input[16])
+static inline void Snefru(uint32_t input[16])
{
static int shifts[4] = {16, 8, 16, 24};
int b, index, rshift, lshift;
- const php_hash_uint32 *t0,*t1;
- php_hash_uint32 SBE,B00,B01,B02,B03,B04,B05,B06,B07,B08,B09,B10,B11,B12,B13,B14,B15;
+ const uint32_t *t0,*t1;
+ uint32_t SBE,B00,B01,B02,B03,B04,B05,B06,B07,B08,B09,B10,B11,B12,B13,B14,B15;
B00 = input[0];
B01 = input[1];
@@ -129,7 +129,7 @@ static inline void SnefruTransform(PHP_SNEFRU_CTX *context, const unsigned char
((input[i+2] & 0xff) << 8) | (input[i+3] & 0xff);
}
Snefru(context->state);
- memset(&context->state[8], 0, sizeof(php_hash_uint32) * 8);
+ memset(&context->state[8], 0, sizeof(uint32_t) * 8);
}
PHP_HASH_API void PHP_SNEFRUInit(PHP_SNEFRU_CTX *context)
@@ -137,7 +137,7 @@ PHP_HASH_API void PHP_SNEFRUInit(PHP_SNEFRU_CTX *context)
memset(context, 0, sizeof(*context));
}
-static const php_hash_uint32 MAX32 = 0xffffffffLU;
+static const uint32_t MAX32 = 0xffffffffLU;
PHP_HASH_API void PHP_SNEFRUUpdate(PHP_SNEFRU_CTX *context, const unsigned char *input, size_t len)
{
@@ -173,7 +173,7 @@ PHP_HASH_API void PHP_SNEFRUUpdate(PHP_SNEFRU_CTX *context, const unsigned char
PHP_HASH_API void PHP_SNEFRUFinal(unsigned char digest[32], PHP_SNEFRU_CTX *context)
{
- php_hash_uint32 i, j;
+ uint32_t i, j;
if (context->length) {
SnefruTransform(context, context->buffer);
diff --git a/ext/hash/hash_tiger.c b/ext/hash/hash_tiger.c
index ac93d7513c..61244de773 100644
--- a/ext/hash/hash_tiger.c
+++ b/ext/hash/hash_tiger.c
@@ -42,13 +42,13 @@
#define round(a,b,c,x,mul) \
c ^= x; \
a -= t1[(unsigned char)(c)] ^ \
- t2[(unsigned char)(((php_hash_uint32)(c))>>(2*8))] ^ \
+ t2[(unsigned char)(((uint32_t)(c))>>(2*8))] ^ \
t3[(unsigned char)((c)>>(4*8))] ^ \
- t4[(unsigned char)(((php_hash_uint32)((c)>>(4*8)))>>(2*8))] ; \
- b += t4[(unsigned char)(((php_hash_uint32)(c))>>(1*8))] ^ \
- t3[(unsigned char)(((php_hash_uint32)(c))>>(3*8))] ^ \
- t2[(unsigned char)(((php_hash_uint32)((c)>>(4*8)))>>(1*8))] ^ \
- t1[(unsigned char)(((php_hash_uint32)((c)>>(4*8)))>>(3*8))]; \
+ t4[(unsigned char)(((uint32_t)((c)>>(4*8)))>>(2*8))] ; \
+ b += t4[(unsigned char)(((uint32_t)(c))>>(1*8))] ^ \
+ t3[(unsigned char)(((uint32_t)(c))>>(3*8))] ^ \
+ t2[(unsigned char)(((uint32_t)((c)>>(4*8)))>>(1*8))] ^ \
+ t1[(unsigned char)(((uint32_t)((c)>>(4*8)))>>(3*8))]; \
b *= mul;
#define pass(a,b,c,mul) \
@@ -105,7 +105,7 @@
# define split(str) \
{ \
int i; \
- php_hash_uint64 tmp[8]; \
+ uint64_t tmp[8]; \
\
for (i = 0; i < 64; ++i) { \
((unsigned char *) tmp)[i^7] = ((unsigned char *) str)[i]; \
@@ -118,8 +118,8 @@
#define tiger_compress(passes, str, state) \
{ \
- register php_hash_uint64 a, b, c, tmpa, x0, x1, x2, x3, x4, x5, x6, x7; \
- php_hash_uint64 aa, bb, cc; \
+ register uint64_t a, b, c, tmpa, x0, x1, x2, x3, x4, x5, x6, x7; \
+ uint64_t aa, bb, cc; \
unsigned int pass_no; \
\
a = state[0]; \
@@ -138,7 +138,7 @@
static inline void TigerFinalize(PHP_TIGER_CTX *context)
{
- context->passed += (php_hash_uint64) context->length << 3;
+ context->passed += (uint64_t) context->length << 3;
context->buffer[context->length++] = 0x1;
if (context->length % 8) {
@@ -148,14 +148,14 @@ static inline void TigerFinalize(PHP_TIGER_CTX *context)
if (context->length > 56) {
memset(&context->buffer[context->length], 0, 64 - context->length);
- tiger_compress(context->passes, ((php_hash_uint64 *) context->buffer), context->state);
+ tiger_compress(context->passes, ((uint64_t *) context->buffer), context->state);
memset(context->buffer, 0, 56);
} else {
memset(&context->buffer[context->length], 0, 56 - context->length);
}
#ifndef WORDS_BIGENDIAN
- memcpy(&context->buffer[56], &context->passed, sizeof(php_hash_uint64));
+ memcpy(&context->buffer[56], &context->passed, sizeof(uint64_t));
#else
context->buffer[56] = (unsigned char) (context->passed & 0xff);
context->buffer[57] = (unsigned char) ((context->passed >> 8) & 0xff);
@@ -166,7 +166,7 @@ static inline void TigerFinalize(PHP_TIGER_CTX *context)
context->buffer[62] = (unsigned char) ((context->passed >> 48) & 0xff);
context->buffer[63] = (unsigned char) ((context->passed >> 56) & 0xff);
#endif
- tiger_compress(context->passes, ((php_hash_uint64 *) context->buffer), context->state);
+ tiger_compress(context->passes, ((uint64_t *) context->buffer), context->state);
}
static inline void TigerDigest(unsigned char *digest_str, unsigned int digest_len, PHP_TIGER_CTX *context)
@@ -206,14 +206,14 @@ PHP_HASH_API void PHP_TIGERUpdate(PHP_TIGER_CTX *context, const unsigned char *i
if (context->length) {
i = 64 - context->length;
memcpy(&context->buffer[context->length], input, i);
- tiger_compress(context->passes, ((const php_hash_uint64 *) context->buffer), context->state);
+ tiger_compress(context->passes, ((const uint64_t *) context->buffer), context->state);
ZEND_SECURE_ZERO(context->buffer, 64);
context->passed += 512;
}
for (; i + 64 <= len; i += 64) {
memcpy(context->buffer, &input[i], 64);
- tiger_compress(context->passes, ((const php_hash_uint64 *) context->buffer), context->state);
+ tiger_compress(context->passes, ((const uint64_t *) context->buffer), context->state);
context->passed += 512;
}
ZEND_SECURE_ZERO(&context->buffer[r], 64-r);
diff --git a/ext/hash/hash_whirlpool.c b/ext/hash/hash_whirlpool.c
index 653b04ed0a..e856ffab0c 100644
--- a/ext/hash/hash_whirlpool.c
+++ b/ext/hash/hash_whirlpool.c
@@ -41,10 +41,10 @@
static void WhirlpoolTransform(PHP_WHIRLPOOL_CTX *context)
{
int i, r;
- php_hash_uint64 K[8]; /* the round key */
- php_hash_uint64 block[8]; /* mu(buffer) */
- php_hash_uint64 state[8]; /* the cipher state */
- php_hash_uint64 L[8];
+ uint64_t K[8]; /* the round key */
+ uint64_t block[8]; /* mu(buffer) */
+ uint64_t state[8]; /* the cipher state */
+ uint64_t L[8];
unsigned char *buffer = context->buffer.data;
/*
@@ -52,14 +52,14 @@ static void WhirlpoolTransform(PHP_WHIRLPOOL_CTX *context)
*/
for (i = 0; i < 8; i++, buffer += 8) {
block[i] =
- (((php_hash_uint64)buffer[0] ) << 56) ^
- (((php_hash_uint64)buffer[1] & 0xffL) << 48) ^
- (((php_hash_uint64)buffer[2] & 0xffL) << 40) ^
- (((php_hash_uint64)buffer[3] & 0xffL) << 32) ^
- (((php_hash_uint64)buffer[4] & 0xffL) << 24) ^
- (((php_hash_uint64)buffer[5] & 0xffL) << 16) ^
- (((php_hash_uint64)buffer[6] & 0xffL) << 8) ^
- (((php_hash_uint64)buffer[7] & 0xffL) );
+ (((uint64_t)buffer[0] ) << 56) ^
+ (((uint64_t)buffer[1] & 0xffL) << 48) ^
+ (((uint64_t)buffer[2] & 0xffL) << 40) ^
+ (((uint64_t)buffer[3] & 0xffL) << 32) ^
+ (((uint64_t)buffer[4] & 0xffL) << 24) ^
+ (((uint64_t)buffer[5] & 0xffL) << 16) ^
+ (((uint64_t)buffer[6] & 0xffL) << 8) ^
+ (((uint64_t)buffer[7] & 0xffL) );
}
/*
* compute and apply K^0 to the cipher state:
@@ -274,7 +274,7 @@ PHP_HASH_API void PHP_WHIRLPOOLInit(PHP_WHIRLPOOL_CTX *context)
PHP_HASH_API void PHP_WHIRLPOOLUpdate(PHP_WHIRLPOOL_CTX *context, const unsigned char *input, size_t len)
{
- php_hash_uint64 sourceBits = len * 8;
+ uint64_t sourceBits = len * 8;
int sourcePos = 0; /* index of leftmost source unsigned char containing data (1 to 8 bits). */
int sourceGap = (8 - ((int)sourceBits & 7)) & 7; /* space on source[sourcePos]. */
int bufferRem = context->buffer.bits & 7; /* occupied bits on buffer[bufferPos]. */
@@ -283,15 +283,15 @@ PHP_HASH_API void PHP_WHIRLPOOLUpdate(PHP_WHIRLPOOL_CTX *context, const unsigned
unsigned char *bitLength = context->bitlength;
int bufferBits = context->buffer.bits;
int bufferPos = context->buffer.pos;
- php_hash_uint32 b, carry;
+ uint32_t b, carry;
int i;
/*
* tally the length of the added data:
*/
- php_hash_uint64 value = sourceBits;
+ uint64_t value = sourceBits;
for (i = 31, carry = 0; i >= 0 && (carry != 0 || value != L64(0)); i--) {
- carry += bitLength[i] + ((php_hash_uint32)value & 0xff);
+ carry += bitLength[i] + ((uint32_t)value & 0xff);
bitLength[i] = (unsigned char)carry;
carry >>= 8;
value >>= 8;
diff --git a/ext/hash/php_hash.h b/ext/hash/php_hash.h
index afe53d72d8..7ec4dfb6c3 100644
--- a/ext/hash/php_hash.h
+++ b/ext/hash/php_hash.h
@@ -31,10 +31,6 @@
#define PHP_HASH_HMAC 0x0001
#define L64 INT64_C
-#define php_hash_int32 int32_t
-#define php_hash_uint32 uint32_t
-#define php_hash_int64 int64_t
-#define php_hash_uint64 uint64_t
typedef void (*php_hash_init_func_t)(void *context);
typedef void (*php_hash_update_func_t)(void *context, const unsigned char *buf, unsigned int count);
@@ -68,6 +64,12 @@ extern const php_hash_ops php_hash_sha224_ops;
extern const php_hash_ops php_hash_sha256_ops;
extern const php_hash_ops php_hash_sha384_ops;
extern const php_hash_ops php_hash_sha512_ops;
+extern const php_hash_ops php_hash_sha512_256_ops;
+extern const php_hash_ops php_hash_sha512_224_ops;
+extern const php_hash_ops php_hash_sha3_224_ops;
+extern const php_hash_ops php_hash_sha3_256_ops;
+extern const php_hash_ops php_hash_sha3_384_ops;
+extern const php_hash_ops php_hash_sha3_512_ops;
extern const php_hash_ops php_hash_ripemd128_ops;
extern const php_hash_ops php_hash_ripemd160_ops;
extern const php_hash_ops php_hash_ripemd256_ops;
diff --git a/ext/hash/php_hash_adler32.h b/ext/hash/php_hash_adler32.h
index 697498b260..dc2dfed9f5 100644
--- a/ext/hash/php_hash_adler32.h
+++ b/ext/hash/php_hash_adler32.h
@@ -24,7 +24,7 @@
#include "ext/standard/basic_functions.h"
typedef struct {
- php_hash_uint32 state;
+ uint32_t state;
} PHP_ADLER32_CTX;
PHP_HASH_API void PHP_ADLER32Init(PHP_ADLER32_CTX *context);
diff --git a/ext/hash/php_hash_crc32.h b/ext/hash/php_hash_crc32.h
index 0d3d13e0c8..377be020cb 100644
--- a/ext/hash/php_hash_crc32.h
+++ b/ext/hash/php_hash_crc32.h
@@ -24,7 +24,7 @@
#include "ext/standard/basic_functions.h"
typedef struct {
- php_hash_uint32 state;
+ uint32_t state;
} PHP_CRC32_CTX;
PHP_HASH_API void PHP_CRC32Init(PHP_CRC32_CTX *context);
diff --git a/ext/hash/php_hash_crc32_tables.h b/ext/hash/php_hash_crc32_tables.h
index 7e343442e4..6af878b3ac 100644
--- a/ext/hash/php_hash_crc32_tables.h
+++ b/ext/hash/php_hash_crc32_tables.h
@@ -18,7 +18,7 @@
/* $Id$ */
-static const php_hash_uint32 crc32_table[] = { 0x0,
+static const uint32_t crc32_table[] = { 0x0,
0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b,
0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6,
0x2b4bcb61, 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd,
@@ -72,7 +72,7 @@ static const php_hash_uint32 crc32_table[] = { 0x0,
0xa2f33668, 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4
};
-static const php_hash_uint32 crc32b_table[] = {
+static const uint32_t crc32b_table[] = {
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba,
0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
diff --git a/ext/hash/php_hash_fnv.h b/ext/hash/php_hash_fnv.h
index 1edbe8631e..f825ba8849 100644
--- a/ext/hash/php_hash_fnv.h
+++ b/ext/hash/php_hash_fnv.h
@@ -21,15 +21,15 @@
#ifndef PHP_HASH_FNV_H
#define PHP_HASH_FNV_H
-#define PHP_FNV1_32_INIT ((php_hash_uint32)0x811c9dc5)
+#define PHP_FNV1_32_INIT ((uint32_t)0x811c9dc5)
#define PHP_FNV1_32A_INIT PHP_FNV1_32_INIT
-#define PHP_FNV_32_PRIME ((php_hash_uint32)0x01000193)
+#define PHP_FNV_32_PRIME ((uint32_t)0x01000193)
-#define PHP_FNV1_64_INIT ((php_hash_uint64)0xcbf29ce484222325ULL)
+#define PHP_FNV1_64_INIT ((uint64_t)0xcbf29ce484222325ULL)
#define PHP_FNV1A_64_INIT FNV1_64_INIT
-#define PHP_FNV_64_PRIME ((php_hash_uint64)0x100000001b3ULL)
+#define PHP_FNV_64_PRIME ((uint64_t)0x100000001b3ULL)
/*
@@ -46,11 +46,11 @@ enum php_fnv_type {
};
typedef struct {
- php_hash_uint32 state;
+ uint32_t state;
} PHP_FNV132_CTX;
typedef struct {
- php_hash_uint64 state;
+ uint64_t state;
} PHP_FNV164_CTX;
@@ -64,8 +64,8 @@ PHP_HASH_API void PHP_FNV164Update(PHP_FNV164_CTX *context, const unsigned char
PHP_HASH_API void PHP_FNV1a64Update(PHP_FNV164_CTX *context, const unsigned char *input, unsigned int inputLen);
PHP_HASH_API void PHP_FNV164Final(unsigned char digest[16], PHP_FNV164_CTX * context);
-static php_hash_uint32 fnv_32_buf(void *buf, size_t len, php_hash_uint32 hval, int alternate);
-static php_hash_uint64 fnv_64_buf(void *buf, size_t len, php_hash_uint64 hval, int alternate);
+static uint32_t fnv_32_buf(void *buf, size_t len, uint32_t hval, int alternate);
+static uint64_t fnv_64_buf(void *buf, size_t len, uint64_t hval, int alternate);
#endif
diff --git a/ext/hash/php_hash_gost.h b/ext/hash/php_hash_gost.h
index 13a221edc9..a3d4a18a4c 100644
--- a/ext/hash/php_hash_gost.h
+++ b/ext/hash/php_hash_gost.h
@@ -25,11 +25,11 @@
/* GOST context */
typedef struct {
- php_hash_uint32 state[16];
- php_hash_uint32 count[2];
+ uint32_t state[16];
+ uint32_t count[2];
unsigned char length;
unsigned char buffer[32];
- const php_hash_uint32 (*tables)[4][256];
+ const uint32_t (*tables)[4][256];
} PHP_GOST_CTX;
PHP_HASH_API void PHP_GOSTInit(PHP_GOST_CTX *);
diff --git a/ext/hash/php_hash_gost_tables.h b/ext/hash/php_hash_gost_tables.h
index af6a78b10b..5a8f6e79fd 100644
--- a/ext/hash/php_hash_gost_tables.h
+++ b/ext/hash/php_hash_gost_tables.h
@@ -1,4 +1,4 @@
-static const php_hash_uint32 tables_test[4][256] = {
+static const uint32_t tables_test[4][256] = {
{ /* table 1 */
0x00072000LU, 0x00075000LU, 0x00074800LU, 0x00071000LU, 0x00076800LU, 0x00074000LU, 0x00070000LU, 0x00077000LU,
0x00073000LU, 0x00075800LU, 0x00070800LU, 0x00076000LU, 0x00073800LU, 0x00077800LU, 0x00072800LU, 0x00071800LU,
@@ -137,7 +137,7 @@ static const php_hash_uint32 tables_test[4][256] = {
},
};
-static const php_hash_uint32 tables_crypto[4][256] = {
+static const uint32_t tables_crypto[4][256] = {
{ /* table 1 */
0x0002d000LU, 0x0002a000LU, 0x0002a800LU, 0x0002b000LU, 0x0002c000LU, 0x00028800LU, 0x00029800LU, 0x0002b800LU,
0x0002e800LU, 0x0002e000LU, 0x0002f000LU, 0x00028000LU, 0x0002c800LU, 0x00029000LU, 0x0002d800LU, 0x0002f800LU,
diff --git a/ext/hash/php_hash_haval.h b/ext/hash/php_hash_haval.h
index 4d2504b037..8877797bfd 100644
--- a/ext/hash/php_hash_haval.h
+++ b/ext/hash/php_hash_haval.h
@@ -24,13 +24,13 @@
#include "ext/standard/basic_functions.h"
/* HAVAL context. */
typedef struct {
- php_hash_uint32 state[8];
- php_hash_uint32 count[2];
+ uint32_t state[8];
+ uint32_t count[2];
unsigned char buffer[128];
char passes;
short output;
- void (*Transform)(php_hash_uint32 state[8], const unsigned char block[128]);
+ void (*Transform)(uint32_t state[8], const unsigned char block[128]);
} PHP_HAVAL_CTX;
#define PHP_HASH_HAVAL_INIT_DECL(p,b) PHP_HASH_API void PHP_##p##HAVAL##b##Init(PHP_HAVAL_CTX *); \
diff --git a/ext/hash/php_hash_joaat.h b/ext/hash/php_hash_joaat.h
index 9d2c7be2db..97a76daff9 100644
--- a/ext/hash/php_hash_joaat.h
+++ b/ext/hash/php_hash_joaat.h
@@ -22,14 +22,14 @@
#define PHP_HASH_JOAAT_H
typedef struct {
- php_hash_uint32 state;
+ uint32_t state;
} PHP_JOAAT_CTX;
PHP_HASH_API void PHP_JOAATInit(PHP_JOAAT_CTX *context);
PHP_HASH_API void PHP_JOAATUpdate(PHP_JOAAT_CTX *context, const unsigned char *input, unsigned int inputLen);
PHP_HASH_API void PHP_JOAATFinal(unsigned char digest[16], PHP_JOAAT_CTX * context);
-static php_hash_uint32 joaat_buf(void *buf, size_t len, php_hash_uint32 hval);
+static uint32_t joaat_buf(void *buf, size_t len, uint32_t hval);
#endif
diff --git a/ext/hash/php_hash_md.h b/ext/hash/php_hash_md.h
index ea77eb5894..2e7121f37a 100644
--- a/ext/hash/php_hash_md.h
+++ b/ext/hash/php_hash_md.h
@@ -60,8 +60,8 @@
/* MD5 context. */
typedef struct {
- php_hash_uint32 state[4]; /* state (ABCD) */
- php_hash_uint32 count[2]; /* number of bits, modulo 2^64 (lsb first) */
+ uint32_t state[4]; /* state (ABCD) */
+ uint32_t count[2]; /* number of bits, modulo 2^64 (lsb first) */
unsigned char buffer[64]; /* input buffer */
} PHP_MD5_CTX;
@@ -76,8 +76,8 @@ PHP_NAMED_FUNCTION(php_if_md5_file);
/* MD4 context */
typedef struct {
- php_hash_uint32 state[4];
- php_hash_uint32 count[2];
+ uint32_t state[4];
+ uint32_t count[2];
unsigned char buffer[64];
} PHP_MD4_CTX;
diff --git a/ext/hash/php_hash_ripemd.h b/ext/hash/php_hash_ripemd.h
index e0aca994b6..ae29737756 100644
--- a/ext/hash/php_hash_ripemd.h
+++ b/ext/hash/php_hash_ripemd.h
@@ -24,26 +24,26 @@
/* RIPEMD context. */
typedef struct {
- php_hash_uint32 state[4]; /* state (ABCD) */
- php_hash_uint32 count[2]; /* number of bits, modulo 2^64 (lsb first) */
+ uint32_t state[4]; /* state (ABCD) */
+ uint32_t count[2]; /* number of bits, modulo 2^64 (lsb first) */
unsigned char buffer[64]; /* input buffer */
} PHP_RIPEMD128_CTX;
typedef struct {
- php_hash_uint32 state[5]; /* state (ABCD) */
- php_hash_uint32 count[2]; /* number of bits, modulo 2^64 (lsb first) */
+ uint32_t state[5]; /* state (ABCD) */
+ uint32_t count[2]; /* number of bits, modulo 2^64 (lsb first) */
unsigned char buffer[64]; /* input buffer */
} PHP_RIPEMD160_CTX;
typedef struct {
- php_hash_uint32 state[8]; /* state (ABCD) */
- php_hash_uint32 count[2]; /* number of bits, modulo 2^64 (lsb first) */
+ uint32_t state[8]; /* state (ABCD) */
+ uint32_t count[2]; /* number of bits, modulo 2^64 (lsb first) */
unsigned char buffer[64]; /* input buffer */
} PHP_RIPEMD256_CTX;
typedef struct {
- php_hash_uint32 state[10]; /* state (ABCD) */
- php_hash_uint32 count[2]; /* number of bits, modulo 2^64 (lsb first) */
+ uint32_t state[10]; /* state (ABCD) */
+ uint32_t count[2]; /* number of bits, modulo 2^64 (lsb first) */
unsigned char buffer[64]; /* input buffer */
} PHP_RIPEMD320_CTX;
diff --git a/ext/hash/php_hash_sha.h b/ext/hash/php_hash_sha.h
index 20564b25a2..98b33471b0 100644
--- a/ext/hash/php_hash_sha.h
+++ b/ext/hash/php_hash_sha.h
@@ -36,8 +36,8 @@
/* SHA1 context. */
typedef struct {
- php_hash_uint32 state[5]; /* state (ABCD) */
- php_hash_uint32 count[2]; /* number of bits, modulo 2^64 */
+ uint32_t state[5]; /* state (ABCD) */
+ uint32_t count[2]; /* number of bits, modulo 2^64 */
unsigned char buffer[64]; /* input buffer */
} PHP_SHA1_CTX;
@@ -52,8 +52,8 @@ PHP_FUNCTION(sha1_file);
/* SHA224 context. */
typedef struct {
- php_hash_uint32 state[8]; /* state */
- php_hash_uint32 count[2]; /* number of bits, modulo 2^64 */
+ uint32_t state[8]; /* state */
+ uint32_t count[2]; /* number of bits, modulo 2^64 */
unsigned char buffer[64]; /* input buffer */
} PHP_SHA224_CTX;
@@ -63,8 +63,8 @@ PHP_HASH_API void PHP_SHA224Final(unsigned char[28], PHP_SHA224_CTX *);
/* SHA256 context. */
typedef struct {
- php_hash_uint32 state[8]; /* state */
- php_hash_uint32 count[2]; /* number of bits, modulo 2^64 */
+ uint32_t state[8]; /* state */
+ uint32_t count[2]; /* number of bits, modulo 2^64 */
unsigned char buffer[64]; /* input buffer */
} PHP_SHA256_CTX;
@@ -74,8 +74,8 @@ PHP_HASH_API void PHP_SHA256Final(unsigned char[32], PHP_SHA256_CTX *);
/* SHA384 context */
typedef struct {
- php_hash_uint64 state[8]; /* state */
- php_hash_uint64 count[2]; /* number of bits, modulo 2^128 */
+ uint64_t state[8]; /* state */
+ uint64_t count[2]; /* number of bits, modulo 2^128 */
unsigned char buffer[128]; /* input buffer */
} PHP_SHA384_CTX;
@@ -85,8 +85,8 @@ PHP_HASH_API void PHP_SHA384Final(unsigned char[48], PHP_SHA384_CTX *);
/* SHA512 context */
typedef struct {
- php_hash_uint64 state[8]; /* state */
- php_hash_uint64 count[2]; /* number of bits, modulo 2^128 */
+ uint64_t state[8]; /* state */
+ uint64_t count[2]; /* number of bits, modulo 2^128 */
unsigned char buffer[128]; /* input buffer */
} PHP_SHA512_CTX;
@@ -94,4 +94,12 @@ PHP_HASH_API void PHP_SHA512Init(PHP_SHA512_CTX *);
PHP_HASH_API void PHP_SHA512Update(PHP_SHA512_CTX *, const unsigned char *, unsigned int);
PHP_HASH_API void PHP_SHA512Final(unsigned char[64], PHP_SHA512_CTX *);
+PHP_HASH_API void PHP_SHA512_256Init(PHP_SHA512_CTX *);
+#define PHP_SHA512_256Update PHP_SHA512Update
+PHP_HASH_API void PHP_SHA512_256Final(unsigned char[32], PHP_SHA512_CTX *);
+
+PHP_HASH_API void PHP_SHA512_224Init(PHP_SHA512_CTX *);
+#define PHP_SHA512_224Update PHP_SHA512Update
+PHP_HASH_API void PHP_SHA512_224Final(unsigned char[28], PHP_SHA512_CTX *);
+
#endif /* PHP_HASH_SHA_H */
diff --git a/ext/hash/php_hash_sha3.h b/ext/hash/php_hash_sha3.h
new file mode 100644
index 0000000000..da7c5f46d7
--- /dev/null
+++ b/ext/hash/php_hash_sha3.h
@@ -0,0 +1,59 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 7 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2016 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: Sara Golemon <pollita@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#ifndef PHP_HASH_SHA3_H
+#define PHP_HASH_SHA3_H
+
+#include "php.h"
+#include "ext/hash/php_hash.h"
+
+typedef struct {
+ unsigned char state[200]; // 5 * 5 * sizeof(uint64)
+ uint32_t pos;
+} PHP_SHA3_CTX;
+
+typedef PHP_SHA3_CTX PHP_SHA3_224_CTX;
+typedef PHP_SHA3_CTX PHP_SHA3_256_CTX;
+typedef PHP_SHA3_CTX PHP_SHA3_384_CTX;
+typedef PHP_SHA3_CTX PHP_SHA3_512_CTX;
+
+PHP_HASH_API void PHP_SHA3224Init(PHP_SHA3_224_CTX*);
+PHP_HASH_API void PHP_SHA3224Update(PHP_SHA3_224_CTX*, const unsigned char*, unsigned int);
+PHP_HASH_API void PHP_SAH3224Final(unsigned char[32], PHP_SHA3_224_CTX*);
+
+PHP_HASH_API void PHP_SHA3256Init(PHP_SHA3_256_CTX*);
+PHP_HASH_API void PHP_SHA3256Update(PHP_SHA3_256_CTX*, const unsigned char*, unsigned int);
+PHP_HASH_API void PHP_SAH3256Final(unsigned char[32], PHP_SHA3_256_CTX*);
+
+PHP_HASH_API void PHP_SHA3384Init(PHP_SHA3_384_CTX*);
+PHP_HASH_API void PHP_SHA3384Update(PHP_SHA3_384_CTX*, const unsigned char*, unsigned int);
+PHP_HASH_API void PHP_SAH3384Final(unsigned char[32], PHP_SHA3_384_CTX*);
+
+PHP_HASH_API void PHP_SHA3512Init(PHP_SHA3_512_CTX*);
+PHP_HASH_API void PHP_SHA3512Update(PHP_SHA3_512_CTX*, const unsigned char*, unsigned int);
+PHP_HASH_API void PHP_SAH3512Final(unsigned char[32], PHP_SHA3_512_CTX*);
+
+#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/ext/hash/php_hash_snefru.h b/ext/hash/php_hash_snefru.h
index cf6699a2bd..64c88fbab1 100644
--- a/ext/hash/php_hash_snefru.h
+++ b/ext/hash/php_hash_snefru.h
@@ -29,8 +29,8 @@
/* SNEFRU context */
typedef struct {
- php_hash_uint32 state[16];
- php_hash_uint32 count[2];
+ uint32_t state[16];
+ uint32_t count[2];
unsigned char length;
unsigned char buffer[32];
} PHP_SNEFRU_CTX;
diff --git a/ext/hash/php_hash_snefru_tables.h b/ext/hash/php_hash_snefru_tables.h
index 199f9f523d..ffd1d29eb5 100644
--- a/ext/hash/php_hash_snefru_tables.h
+++ b/ext/hash/php_hash_snefru_tables.h
@@ -18,7 +18,7 @@
/* $Id$ */
-static const php_hash_uint32 tables[16][256]= {
+static const uint32_t tables[16][256]= {
{ /* Start of S Box 0 */
diff --git a/ext/hash/php_hash_tiger.h b/ext/hash/php_hash_tiger.h
index 536da19a94..af52a95203 100644
--- a/ext/hash/php_hash_tiger.h
+++ b/ext/hash/php_hash_tiger.h
@@ -23,8 +23,8 @@
/* TIGER context */
typedef struct {
- php_hash_uint64 state[3];
- php_hash_uint64 passed;
+ uint64_t state[3];
+ uint64_t passed;
unsigned char buffer[64];
unsigned int passes:1;
unsigned int length:7;
diff --git a/ext/hash/php_hash_tiger_tables.h b/ext/hash/php_hash_tiger_tables.h
index 46197c7516..616213ae28 100644
--- a/ext/hash/php_hash_tiger_tables.h
+++ b/ext/hash/php_hash_tiger_tables.h
@@ -24,7 +24,7 @@
#define t3 (table+256*2)
#define t4 (table+256*3)
-static const php_hash_uint64 table[4*256] = {
+static const uint64_t table[4*256] = {
L64(0x02AAB17CF7E90C5E) /* 0 */, L64(0xAC424B03E243A8EC) /* 1 */,
L64(0x72CD5BE30DD5FCD3) /* 2 */, L64(0x6D019B93F6F97F3A) /* 3 */,
L64(0xCD9978FFD21F9193) /* 4 */, L64(0x7573A1C9708029E2) /* 5 */,
diff --git a/ext/hash/php_hash_whirlpool.h b/ext/hash/php_hash_whirlpool.h
index eb9205489d..dec337a390 100644
--- a/ext/hash/php_hash_whirlpool.h
+++ b/ext/hash/php_hash_whirlpool.h
@@ -23,7 +23,7 @@
/* WHIRLPOOL context */
typedef struct {
- php_hash_uint64 state[8];
+ uint64_t state[8];
unsigned char bitlength[32];
struct {
int pos;
diff --git a/ext/hash/php_hash_whirlpool_tables.h b/ext/hash/php_hash_whirlpool_tables.h
index 569b7530a1..286d180289 100644
--- a/ext/hash/php_hash_whirlpool_tables.h
+++ b/ext/hash/php_hash_whirlpool_tables.h
@@ -23,7 +23,7 @@
#define R 10
-static const php_hash_uint64 rc[R + 1] = {
+static const uint64_t rc[R + 1] = {
L64(0x0000000000000000),
L64(0x1823c6e887b8014f),
L64(0x36a6d2f5796f9152),
@@ -37,7 +37,7 @@ static const php_hash_uint64 rc[R + 1] = {
L64(0xca2dbf07ad5a8333),
};
-static const php_hash_uint64 C0[256] = {
+static const uint64_t C0[256] = {
L64(0x18186018c07830d8), L64(0x23238c2305af4626), L64(0xc6c63fc67ef991b8), L64(0xe8e887e8136fcdfb),
L64(0x878726874ca113cb), L64(0xb8b8dab8a9626d11), L64(0x0101040108050209), L64(0x4f4f214f426e9e0d),
L64(0x3636d836adee6c9b), L64(0xa6a6a2a6590451ff), L64(0xd2d26fd2debdb90c), L64(0xf5f5f3f5fb06f70e),
@@ -104,7 +104,7 @@ static const php_hash_uint64 C0[256] = {
L64(0x2828a0285d885075), L64(0x5c5c6d5cda31b886), L64(0xf8f8c7f8933fed6b), L64(0x8686228644a411c2),
};
-static const php_hash_uint64 C1[256] = {
+static const uint64_t C1[256] = {
L64(0xd818186018c07830), L64(0x2623238c2305af46), L64(0xb8c6c63fc67ef991), L64(0xfbe8e887e8136fcd),
L64(0xcb878726874ca113), L64(0x11b8b8dab8a9626d), L64(0x0901010401080502), L64(0x0d4f4f214f426e9e),
L64(0x9b3636d836adee6c), L64(0xffa6a6a2a6590451), L64(0x0cd2d26fd2debdb9), L64(0x0ef5f5f3f5fb06f7),
@@ -171,7 +171,7 @@ static const php_hash_uint64 C1[256] = {
L64(0x752828a0285d8850), L64(0x865c5c6d5cda31b8), L64(0x6bf8f8c7f8933fed), L64(0xc28686228644a411),
};
-static const php_hash_uint64 C2[256] = {
+static const uint64_t C2[256] = {
L64(0x30d818186018c078), L64(0x462623238c2305af), L64(0x91b8c6c63fc67ef9), L64(0xcdfbe8e887e8136f),
L64(0x13cb878726874ca1), L64(0x6d11b8b8dab8a962), L64(0x0209010104010805), L64(0x9e0d4f4f214f426e),
L64(0x6c9b3636d836adee), L64(0x51ffa6a6a2a65904), L64(0xb90cd2d26fd2debd), L64(0xf70ef5f5f3f5fb06),
@@ -238,7 +238,7 @@ static const php_hash_uint64 C2[256] = {
L64(0x50752828a0285d88), L64(0xb8865c5c6d5cda31), L64(0xed6bf8f8c7f8933f), L64(0x11c28686228644a4),
};
-static const php_hash_uint64 C3[256] = {
+static const uint64_t C3[256] = {
L64(0x7830d818186018c0), L64(0xaf462623238c2305), L64(0xf991b8c6c63fc67e), L64(0x6fcdfbe8e887e813),
L64(0xa113cb878726874c), L64(0x626d11b8b8dab8a9), L64(0x0502090101040108), L64(0x6e9e0d4f4f214f42),
L64(0xee6c9b3636d836ad), L64(0x0451ffa6a6a2a659), L64(0xbdb90cd2d26fd2de), L64(0x06f70ef5f5f3f5fb),
@@ -305,7 +305,7 @@ static const php_hash_uint64 C3[256] = {
L64(0x8850752828a0285d), L64(0x31b8865c5c6d5cda), L64(0x3fed6bf8f8c7f893), L64(0xa411c28686228644),
};
-static const php_hash_uint64 C4[256] = {
+static const uint64_t C4[256] = {
L64(0xc07830d818186018), L64(0x05af462623238c23), L64(0x7ef991b8c6c63fc6), L64(0x136fcdfbe8e887e8),
L64(0x4ca113cb87872687), L64(0xa9626d11b8b8dab8), L64(0x0805020901010401), L64(0x426e9e0d4f4f214f),
L64(0xadee6c9b3636d836), L64(0x590451ffa6a6a2a6), L64(0xdebdb90cd2d26fd2), L64(0xfb06f70ef5f5f3f5),
@@ -372,7 +372,7 @@ static const php_hash_uint64 C4[256] = {
L64(0x5d8850752828a028), L64(0xda31b8865c5c6d5c), L64(0x933fed6bf8f8c7f8), L64(0x44a411c286862286),
};
-static const php_hash_uint64 C5[256] = {
+static const uint64_t C5[256] = {
L64(0x18c07830d8181860), L64(0x2305af462623238c), L64(0xc67ef991b8c6c63f), L64(0xe8136fcdfbe8e887),
L64(0x874ca113cb878726), L64(0xb8a9626d11b8b8da), L64(0x0108050209010104), L64(0x4f426e9e0d4f4f21),
L64(0x36adee6c9b3636d8), L64(0xa6590451ffa6a6a2), L64(0xd2debdb90cd2d26f), L64(0xf5fb06f70ef5f5f3),
@@ -439,7 +439,7 @@ static const php_hash_uint64 C5[256] = {
L64(0x285d8850752828a0), L64(0x5cda31b8865c5c6d), L64(0xf8933fed6bf8f8c7), L64(0x8644a411c2868622),
};
-static const php_hash_uint64 C6[256] = {
+static const uint64_t C6[256] = {
L64(0x6018c07830d81818), L64(0x8c2305af46262323), L64(0x3fc67ef991b8c6c6), L64(0x87e8136fcdfbe8e8),
L64(0x26874ca113cb8787), L64(0xdab8a9626d11b8b8), L64(0x0401080502090101), L64(0x214f426e9e0d4f4f),
L64(0xd836adee6c9b3636), L64(0xa2a6590451ffa6a6), L64(0x6fd2debdb90cd2d2), L64(0xf3f5fb06f70ef5f5),
@@ -506,7 +506,7 @@ static const php_hash_uint64 C6[256] = {
L64(0xa0285d8850752828), L64(0x6d5cda31b8865c5c), L64(0xc7f8933fed6bf8f8), L64(0x228644a411c28686),
};
-static const php_hash_uint64 C7[256] = {
+static const uint64_t C7[256] = {
L64(0x186018c07830d818), L64(0x238c2305af462623), L64(0xc63fc67ef991b8c6), L64(0xe887e8136fcdfbe8),
L64(0x8726874ca113cb87), L64(0xb8dab8a9626d11b8), L64(0x0104010805020901), L64(0x4f214f426e9e0d4f),
L64(0x36d836adee6c9b36), L64(0xa6a2a6590451ffa6), L64(0xd26fd2debdb90cd2), L64(0xf5f3f5fb06f70ef5),
diff --git a/ext/hash/tests/hash_algos.phpt b/ext/hash/tests/hash_algos.phpt
index f2d6fc3ffb..0014f95b1d 100644
--- a/ext/hash/tests/hash_algos.phpt
+++ b/ext/hash/tests/hash_algos.phpt
@@ -18,7 +18,7 @@ var_dump(hash_algos());
===Done===
--EXPECTF--
*** Testing hash_algos() : basic functionality ***
-array(46) {
+array(52) {
[%d]=>
string(3) "md2"
[%d]=>
@@ -34,8 +34,20 @@ array(46) {
[%d]=>
string(6) "sha384"
[%d]=>
+ string(10) "sha512/224"
+ [%d]=>
+ string(10) "sha512/256"
+ [%d]=>
string(6) "sha512"
[%d]=>
+ string(8) "sha3-224"
+ [%d]=>
+ string(8) "sha3-256"
+ [%d]=>
+ string(8) "sha3-384"
+ [%d]=>
+ string(8) "sha3-512"
+ [%d]=>
string(9) "ripemd128"
[%d]=>
string(9) "ripemd160"
diff --git a/ext/hash/tests/hash_copy_001.phpt b/ext/hash/tests/hash_copy_001.phpt
index f9fe7cc552..b33d449762 100644
--- a/ext/hash/tests/hash_copy_001.phpt
+++ b/ext/hash/tests/hash_copy_001.phpt
@@ -52,9 +52,27 @@ string(64) "d3a13cf52af8e9390caed78b77b6b1e06e102204e3555d111dfd149bc5d54dba"
string(6) "sha384"
string(96) "6950d861ace4102b803ab8b3779d2f471968233010d2608974ab89804cef6f76162b4433d6e554e11e40a7cdcf510ea3"
string(96) "6950d861ace4102b803ab8b3779d2f471968233010d2608974ab89804cef6f76162b4433d6e554e11e40a7cdcf510ea3"
+string(10) "sha512/224"
+string(56) "a2573d0e3f6c3e2d174c935a35a8ea31032f04e9e83499ac3ceda568"
+string(56) "a2573d0e3f6c3e2d174c935a35a8ea31032f04e9e83499ac3ceda568"
+string(10) "sha512/256"
+string(64) "fddacab80b3a610ba024c9d75a5fe0cafe5ae7c789f829b3c5fbea8ef11ccc1a"
+string(64) "fddacab80b3a610ba024c9d75a5fe0cafe5ae7c789f829b3c5fbea8ef11ccc1a"
string(6) "sha512"
string(128) "caced3db8e9e3a5543d5b933bcbe9e7834e6667545c3f5d4087b58ec8d78b4c8a4a5500c9b88f65f7368810ba9905e51f1cff3b25a5dccf76634108fb4e7ce13"
string(128) "caced3db8e9e3a5543d5b933bcbe9e7834e6667545c3f5d4087b58ec8d78b4c8a4a5500c9b88f65f7368810ba9905e51f1cff3b25a5dccf76634108fb4e7ce13"
+string(8) "sha3-224"
+string(56) "7e1126cffee98e5c4b0e9dd5c6efabd5c9356d668e9a2d3cfab724d4"
+string(56) "7e1126cffee98e5c4b0e9dd5c6efabd5c9356d668e9a2d3cfab724d4"
+string(8) "sha3-256"
+string(64) "834abfed9197af09cbe66b7748c65a050a3755ef7a556d6764eb6eabc93b4c7a"
+string(64) "834abfed9197af09cbe66b7748c65a050a3755ef7a556d6764eb6eabc93b4c7a"
+string(8) "sha3-384"
+string(96) "c9016992586f7a8663c5379ed892349c1140ad258f7c44ee82f61f0b8cb75c675012ea94dc1314e06699be2d1465f67b"
+string(96) "c9016992586f7a8663c5379ed892349c1140ad258f7c44ee82f61f0b8cb75c675012ea94dc1314e06699be2d1465f67b"
+string(8) "sha3-512"
+string(128) "5f85341bc9c6621406bf1841c4ce01727ea8759fdf2927106c3e70a75ad9fffd095b87f995aeee844e1a2c287e1195ce809b9bdb1c31258f7fc098175b6de0b4"
+string(128) "5f85341bc9c6621406bf1841c4ce01727ea8759fdf2927106c3e70a75ad9fffd095b87f995aeee844e1a2c287e1195ce809b9bdb1c31258f7fc098175b6de0b4"
string(9) "ripemd128"
string(32) "5f1bc5f5aeaf747574dd34a6535cd94a"
string(32) "5f1bc5f5aeaf747574dd34a6535cd94a"
@@ -190,9 +208,27 @@ string(64) "268e7f4cf88504a53fd77136c4c4748169f46ff7150b376569ada9c374836944"
string(6) "sha384"
string(96) "6950d861ace4102b803ab8b3779d2f471968233010d2608974ab89804cef6f76162b4433d6e554e11e40a7cdcf510ea3"
string(96) "0d44981d04bb11b1ef75d5c2932bd0aa2785e7bc454daac954d77e2ca10047879b58997533fc99650b20049c6cb9a6cc"
+string(10) "sha512/224"
+string(56) "a2573d0e3f6c3e2d174c935a35a8ea31032f04e9e83499ac3ceda568"
+string(56) "cbc2bbf0028ed803af785b0f264962c84ec48d8ee0908322ef995ddb"
+string(10) "sha512/256"
+string(64) "fddacab80b3a610ba024c9d75a5fe0cafe5ae7c789f829b3c5fbea8ef11ccc1a"
+string(64) "2cec704878ffa7128e0c4a61eef87d1f3c823184d364dfa3fed73beb00499b00"
string(6) "sha512"
string(128) "caced3db8e9e3a5543d5b933bcbe9e7834e6667545c3f5d4087b58ec8d78b4c8a4a5500c9b88f65f7368810ba9905e51f1cff3b25a5dccf76634108fb4e7ce13"
string(128) "28d7c721433782a880f840af0c3f3ea2cad4ef55de2114dda9d504cedeb110e1cf2519c49e4b5da3da4484bb6ba4fd1621ceadc6408f4410b2ebe9d83a4202c2"
+string(8) "sha3-224"
+string(56) "7e1126cffee98e5c4b0e9dd5c6efabd5c9356d668e9a2d3cfab724d4"
+string(56) "9a21a5464794c2c9784df50cf89cf72234e11941bddaee93f912753e"
+string(8) "sha3-256"
+string(64) "834abfed9197af09cbe66b7748c65a050a3755ef7a556d6764eb6eabc93b4c7a"
+string(64) "57aa7a90f29b5ab66592760592780da247fd39b4c911773687450f9df8cc8ed0"
+string(8) "sha3-384"
+string(96) "c9016992586f7a8663c5379ed892349c1140ad258f7c44ee82f61f0b8cb75c675012ea94dc1314e06699be2d1465f67b"
+string(96) "5d6d7e42b241288bc707b74c50f90a37d69a4afa854ca72021a22cb379356e53b6233aea1be2f33d393d6effa9b5e36c"
+string(8) "sha3-512"
+string(128) "5f85341bc9c6621406bf1841c4ce01727ea8759fdf2927106c3e70a75ad9fffd095b87f995aeee844e1a2c287e1195ce809b9bdb1c31258f7fc098175b6de0b4"
+string(128) "9b88c689bc13a36e6983b32e8ee9464d63b619f246ca451d1fe2a6c9670f01e71d0c8eb245f3204d27d27c056f2a0fef76a1e3bc30fb74cccbc984dbd4883ae6"
string(9) "ripemd128"
string(32) "5f1bc5f5aeaf747574dd34a6535cd94a"
string(32) "f95f5e22b8875ee0c48219ae97f0674b"
diff --git a/ext/hash/tests/sha3.phpt b/ext/hash/tests/sha3.phpt
new file mode 100644
index 0000000000..67fb22f988
--- /dev/null
+++ b/ext/hash/tests/sha3.phpt
@@ -0,0 +1,56 @@
+--TEST--
+sha3 algorithm
+--SKIPIF--
+<?php if(!extension_loaded("hash")) print "skip"; ?>
+--FILE--
+<?php
+
+// Test vectors taken from a combination of NIST FIPS-202,
+// Wikipedia reference vectors,
+// and output from reference implementation
+$subjects = [
+ '',
+ 'a',
+ 'The quick brown fox jumps over the lazy dog',
+ 'The quick brown fox jumps over the lazy dog.',
+ str_repeat('a', 257),
+ str_repeat("\xA3", 200),
+];
+foreach ($subjects as $subject) {
+ echo '== ', urlencode($subject), " ==\n";
+ foreach ([224, 256, 384, 512] as $bits) {
+ echo $bits, ': ', hash("sha3-$bits", $subject), "\n";
+ }
+}
+--EXPECT--
+== ==
+224: 6b4e03423667dbb73b6e15454f0eb1abd4597f9a1b078e3f5b5a6bc7
+256: a7ffc6f8bf1ed76651c14756a061d662f580ff4de43b49fa82d80a4b80f8434a
+384: 0c63a75b845e4f7d01107d852e4c2485c51a50aaaa94fc61995e71bbee983a2ac3713831264adb47fb6bd1e058d5f004
+512: a69f73cca23a9ac5c8b567dc185a756e97c982164fe25859e0d1dcc1475c80a615b2123af1f5f94c11e3e9402c3ac558f500199d95b6d3e301758586281dcd26
+== a ==
+224: 9e86ff69557ca95f405f081269685b38e3a819b309ee942f482b6a8b
+256: 80084bf2fba02475726feb2cab2d8215eab14bc6bdd8bfb2c8151257032ecd8b
+384: 1815f774f320491b48569efec794d249eeb59aae46d22bf77dafe25c5edc28d7ea44f93ee1234aa88f61c91912a4ccd9
+512: 697f2d856172cb8309d6b8b97dac4de344b549d4dee61edfb4962d8698b7fa803f4f93ff24393586e28b5b957ac3d1d369420ce53332712f997bd336d09ab02a
+== The+quick+brown+fox+jumps+over+the+lazy+dog ==
+224: d15dadceaa4d5d7bb3b48f446421d542e08ad8887305e28d58335795
+256: 69070dda01975c8c120c3aada1b282394e7f032fa9cf32f4cb2259a0897dfc04
+384: 7063465e08a93bce31cd89d2e3ca8f602498696e253592ed26f07bf7e703cf328581e1471a7ba7ab119b1a9ebdf8be41
+512: 01dedd5de4ef14642445ba5f5b97c15e47b9ad931326e4b0727cd94cefc44fff23f07bf543139939b49128caf436dc1bdee54fcb24023a08d9403f9b4bf0d450
+== The+quick+brown+fox+jumps+over+the+lazy+dog. ==
+224: 2d0708903833afabdd232a20201176e8b58c5be8a6fe74265ac54db0
+256: a80f839cd4f83f6c3dafc87feae470045e4eb0d366397d5c6ce34ba1739f734d
+384: 1a34d81695b622df178bc74df7124fe12fac0f64ba5250b78b99c1273d4b080168e10652894ecad5f1f4d5b965437fb9
+512: 18f4f4bd419603f95538837003d9d254c26c23765565162247483f65c50303597bc9ce4d289f21d1c2f1f458828e33dc442100331b35e7eb031b5d38ba6460f8
+== aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa ==
+224: 10dd422d71c42ee102a0c4bd398b5b85470341a0794702c954b022ba
+256: 6d115e8744deef792419e8bdb8567d74844e0fa5c2d5e474a19de87ac001449f
+384: c9df41fa389101cde63447257835464d89fd3974e5813f3f58d30e0296e89486e2d4bfc2b4089cd3bb860a20263322b8
+512: 5008048b64c14975181175f157be4a780c3d443d2177edf323d57884bc7e3979b9b53bca1325e880df3da0d97c435693441cb5527fbe950f5585678dfbb37785
+== %A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3%A3 ==
+224: 9376816aba503f72f96ce7eb65ac095deee3be4bf9bbc2a1cb7e11e0
+256: 79f38adec5c20307a98ef76e8324afbfd46cfd81b22e3973c65fa1bd9de31787
+384: 1881de2ca7e41ef95dc4732b8f5f002b189cc1e42b74168ed1732649ce1dbcdd76197a31fd55ee989f2d7050dd473e8f
+512: e76dfad22084a8b1467fcf2ffa58361bec7628edf5f3fdc0e4805dc48caeeca81b7c13c30adf52a3659584739a2df46be589c51ca1a4a8416df6545a1ce8ba00
+
diff --git a/ext/hash/tests/sha512-224.phpt b/ext/hash/tests/sha512-224.phpt
new file mode 100644
index 0000000000..3769832ee6
--- /dev/null
+++ b/ext/hash/tests/sha512-224.phpt
@@ -0,0 +1,13 @@
+--TEST--
+sha512/224 algorithm
+--SKIPIF--
+<?php if(!extension_loaded("hash")) print "skip"; ?>
+--FILE--
+<?php
+echo hash('sha512/224', '') . "\n";
+echo hash('sha512/224', 'abc') . "\n";
+echo hash('sha512/224', 'abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu') . "\n";
+--EXPECT--
+6ed0dd02806fa89e25de060c19d3ac86cabb87d6a0ddd05c333b84f4
+4634270f707b6a54daae7530460842e20e37ed265ceee9a43e8924aa
+23fec5bb94d60b23308192640b0c453335d664734fe40e7268674af9
diff --git a/ext/hash/tests/sha512-256.phpt b/ext/hash/tests/sha512-256.phpt
new file mode 100644
index 0000000000..33ae5f1770
--- /dev/null
+++ b/ext/hash/tests/sha512-256.phpt
@@ -0,0 +1,13 @@
+--TEST--
+sha512/256 algorithm
+--SKIPIF--
+<?php if(!extension_loaded("hash")) print "skip"; ?>
+--FILE--
+<?php
+echo hash('sha512/256', '') . "\n";
+echo hash('sha512/256', 'abc') . "\n";
+echo hash('sha512/256', 'abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu') . "\n";
+--EXPECT--
+c672b8d1ef56ed28ab87c3622c5114069bdd3ad7b8f9737498d0c01ecef0967a
+53048e2681941ef99b2e29b76b4c7dabe4c2d0c634fc6d46e0e2f13107e7af23
+3928e184fb8690f840da3988121d31be65cb9d3ef83ee6146feac861e19b563a
diff --git a/ext/iconv/iconv.c b/ext/iconv/iconv.c
index d4a7f6e0af..0d44936ea6 100644
--- a/ext/iconv/iconv.c
+++ b/ext/iconv/iconv.c
@@ -2121,7 +2121,7 @@ PHP_FUNCTION(iconv_substr)
PHP_FUNCTION(iconv_strpos)
{
char *charset = get_internal_encoding();
- size_t charset_len = 0;
+ size_t charset_len = 0, haystk_len;
zend_string *haystk;
zend_string *ndl;
zend_long offset = 0;
@@ -2142,8 +2142,17 @@ PHP_FUNCTION(iconv_strpos)
}
if (offset < 0) {
- php_error_docref(NULL, E_WARNING, "Offset not contained in string.");
- RETURN_FALSE;
+ /* Convert negative offset (counted from the end of string) */
+ err = _php_iconv_strlen(&haystk_len, ZSTR_VAL(haystk), ZSTR_LEN(haystk), charset);
+ if (err != PHP_ICONV_ERR_SUCCESS) {
+ _php_iconv_show_error(err, GENERIC_SUPERSET_NAME, charset);
+ RETURN_FALSE;
+ }
+ offset += haystk_len;
+ if (offset < 0) { /* If offset before start */
+ php_error_docref(NULL, E_WARNING, "Offset not contained in string.");
+ RETURN_FALSE;
+ }
}
if (ZSTR_LEN(ndl) < 1) {
diff --git a/ext/iconv/tests/iconv_strpos.phpt b/ext/iconv/tests/iconv_strpos.phpt
index 6965f6fae1..28e3fe2320 100644
--- a/ext/iconv/tests/iconv_strpos.phpt
+++ b/ext/iconv/tests/iconv_strpos.phpt
@@ -24,6 +24,7 @@ function foo($haystk, $needle, $offset, $to_charset = false, $from_charset = fal
}
}
foo("abecdbcdabef", "bcd", -1);
+foo("abecdbcdabef", "bcd", -7);
foo("abecdbcdabef", "bcd", 100000);
foo("abcabcabcdabcababcdabc", "bcd", 0);
foo("abcabcabcdabcababcdabc", "bcd", 10);
@@ -37,10 +38,10 @@ var_dump(iconv_strpos("", "string"));
?>
--EXPECTF--
-2: %s
bool(false)
-2: %s
bool(false)
+int(5)
+int(5)
2: %s
bool(false)
bool(false)
diff --git a/ext/iconv/tests/iconv_strpos_variation3.phpt b/ext/iconv/tests/iconv_strpos_variation3.phpt
index aa9bc1ae1b..6f27b74a52 100644
--- a/ext/iconv/tests/iconv_strpos_variation3.phpt
+++ b/ext/iconv/tests/iconv_strpos_variation3.phpt
@@ -48,56 +48,58 @@ $fp = fopen(__FILE__, "r");
$inputs = array(
// int data
-/*1*/ 0,
+ 0,
1,
12345,
+ -5,
-2345,
// float data
-/*5*/ 10.5,
- -10.5,
+ 10.5,
+ -9.5,
+ -100.3,
12.3456789000e10,
12.3456789000E-10,
.5,
// null data
-/*10*/ NULL,
+ NULL,
null,
// boolean data
-/*12*/ true,
+ true,
false,
TRUE,
FALSE,
// empty data
-/*16*/ "",
+ "",
'',
// string data
-/*18*/ "string",
+ "string",
'string',
$heredoc,
// object data
-/*21*/ new classA(),
+ new classA(),
// undefined data
-/*22*/ @$undefined_var,
+ @$undefined_var,
// unset data
-/*23*/ @$unset_var,
+ @$unset_var,
// resource variable
-/*24*/ $fp
+ $fp
);
// loop through each element of $inputs to check the behavior of iconv_strpos()
-$iterator = 1;
+
foreach($inputs as $input) {
- echo "\n-- Iteration $iterator --\n";
+ echo "--\n";
+ var_dump($input);
var_dump( iconv_strpos($haystack, $needle, $input, $encoding));
- $iterator++;
};
fclose($fp);
@@ -106,96 +108,103 @@ echo "Done";
?>
--EXPECTF--
*** Testing iconv_strpos() : usage variations ***
-
--- Iteration 1 --
+--
+int(0)
int(8)
-
--- Iteration 2 --
+--
+int(1)
int(8)
-
--- Iteration 3 --
+--
+int(12345)
bool(false)
-
--- Iteration 4 --
+--
+int(-5)
+int(8)
+--
+int(-2345)
Warning: iconv_strpos(): Offset not contained in string. in %s on line %d
bool(false)
-
--- Iteration 5 --
+--
+float(10.5)
bool(false)
-
--- Iteration 6 --
+--
+float(-9.5)
+int(8)
+--
+float(-100.3)
Warning: iconv_strpos(): Offset not contained in string. in %s on line %d
bool(false)
-
--- Iteration 7 --
+--
+float(123456789000)
Warning: iconv_strpos() expects parameter 3 to be integer, float given in %s on line %d
bool(false)
-
--- Iteration 8 --
+--
+float(1.23456789E-9)
int(8)
-
--- Iteration 9 --
+--
+float(0.5)
int(8)
-
--- Iteration 10 --
+--
+NULL
int(8)
-
--- Iteration 11 --
+--
+NULL
int(8)
-
--- Iteration 12 --
+--
+bool(true)
int(8)
-
--- Iteration 13 --
+--
+bool(false)
int(8)
-
--- Iteration 14 --
+--
+bool(true)
int(8)
-
--- Iteration 15 --
+--
+bool(false)
int(8)
-
--- Iteration 16 --
+--
+string(0) ""
Warning: iconv_strpos() expects parameter 3 to be integer, string given in %s on line %d
bool(false)
-
--- Iteration 17 --
+--
+string(0) ""
Warning: iconv_strpos() expects parameter 3 to be integer, string given in %s on line %d
bool(false)
-
--- Iteration 18 --
+--
+string(6) "string"
Warning: iconv_strpos() expects parameter 3 to be integer, string given in %s on line %d
bool(false)
-
--- Iteration 19 --
+--
+string(6) "string"
Warning: iconv_strpos() expects parameter 3 to be integer, string given in %s on line %d
bool(false)
-
--- Iteration 20 --
+--
+string(11) "hello world"
Warning: iconv_strpos() expects parameter 3 to be integer, string given in %s on line %d
bool(false)
-
--- Iteration 21 --
+--
+object(classA)#%d (%d) {
+}
Warning: iconv_strpos() expects parameter 3 to be integer, object given in %s on line %d
bool(false)
-
--- Iteration 22 --
+--
+NULL
int(8)
-
--- Iteration 23 --
+--
+NULL
int(8)
-
--- Iteration 24 --
+--
+resource(%d) of type (stream)
Warning: iconv_strpos() expects parameter 3 to be integer, resource given in %s on line %d
bool(false)
-Done \ No newline at end of file
+Done
diff --git a/ext/iconv/tests/iconv_strpos_variation3_64bit.phpt b/ext/iconv/tests/iconv_strpos_variation3_64bit.phpt
index d915339275..2704493235 100644
--- a/ext/iconv/tests/iconv_strpos_variation3_64bit.phpt
+++ b/ext/iconv/tests/iconv_strpos_variation3_64bit.phpt
@@ -48,56 +48,58 @@ $fp = fopen(__FILE__, "r");
$inputs = array(
// int data
-/*1*/ 0,
+ 0,
1,
12345,
+ -5,
-2345,
// float data
-/*5*/ 10.5,
- -10.5,
+ 10.5,
+ -9.5,
+ -100.3,
12.3456789000e10,
12.3456789000E-10,
.5,
// null data
-/*10*/ NULL,
+ NULL,
null,
// boolean data
-/*12*/ true,
+ true,
false,
TRUE,
FALSE,
// empty data
-/*16*/ "",
+ "",
'',
// string data
-/*18*/ "string",
+ "string",
'string',
$heredoc,
// object data
-/*21*/ new classA(),
+ new classA(),
// undefined data
-/*22*/ @$undefined_var,
+ @$undefined_var,
// unset data
-/*23*/ @$unset_var,
+ @$unset_var,
// resource variable
-/*24*/ $fp
+ $fp
);
// loop through each element of $inputs to check the behavior of iconv_strpos()
-$iterator = 1;
+
foreach($inputs as $input) {
- echo "\n-- Iteration $iterator --\n";
+ echo "--\n";
+ var_dump($input);
var_dump( iconv_strpos($haystack, $needle, $input, $encoding));
- $iterator++;
};
fclose($fp);
@@ -106,93 +108,100 @@ echo "Done";
?>
--EXPECTF--
*** Testing iconv_strpos() : usage variations ***
-
--- Iteration 1 --
+--
+int(0)
int(8)
-
--- Iteration 2 --
+--
+int(1)
int(8)
-
--- Iteration 3 --
+--
+int(12345)
bool(false)
-
--- Iteration 4 --
+--
+int(-5)
+int(8)
+--
+int(-2345)
Warning: iconv_strpos(): Offset not contained in string. in %s on line %d
bool(false)
-
--- Iteration 5 --
+--
+float(10.5)
bool(false)
-
--- Iteration 6 --
+--
+float(-9.5)
+int(8)
+--
+float(-100.3)
Warning: iconv_strpos(): Offset not contained in string. in %s on line %d
bool(false)
-
--- Iteration 7 --
+--
+float(123456789000)
bool(false)
-
--- Iteration 8 --
+--
+float(1.23456789E-9)
int(8)
-
--- Iteration 9 --
+--
+float(0.5)
int(8)
-
--- Iteration 10 --
+--
+NULL
int(8)
-
--- Iteration 11 --
+--
+NULL
int(8)
-
--- Iteration 12 --
+--
+bool(true)
int(8)
-
--- Iteration 13 --
+--
+bool(false)
int(8)
-
--- Iteration 14 --
+--
+bool(true)
int(8)
-
--- Iteration 15 --
+--
+bool(false)
int(8)
-
--- Iteration 16 --
+--
+string(0) ""
Warning: iconv_strpos() expects parameter 3 to be integer, string given in %s on line %d
bool(false)
-
--- Iteration 17 --
+--
+string(0) ""
Warning: iconv_strpos() expects parameter 3 to be integer, string given in %s on line %d
bool(false)
-
--- Iteration 18 --
+--
+string(6) "string"
Warning: iconv_strpos() expects parameter 3 to be integer, string given in %s on line %d
bool(false)
-
--- Iteration 19 --
+--
+string(6) "string"
Warning: iconv_strpos() expects parameter 3 to be integer, string given in %s on line %d
bool(false)
-
--- Iteration 20 --
+--
+string(11) "hello world"
Warning: iconv_strpos() expects parameter 3 to be integer, string given in %s on line %d
bool(false)
-
--- Iteration 21 --
+--
+object(classA)#%d (%d) {
+}
Warning: iconv_strpos() expects parameter 3 to be integer, object given in %s on line %d
bool(false)
-
--- Iteration 22 --
+--
+NULL
int(8)
-
--- Iteration 23 --
+--
+NULL
int(8)
-
--- Iteration 24 --
+--
+resource(%d) of type (stream)
Warning: iconv_strpos() expects parameter 3 to be integer, resource given in %s on line %d
bool(false)
diff --git a/ext/iconv/tests/iconv_strpos_variation5.phpt b/ext/iconv/tests/iconv_strpos_variation5.phpt
index 3db0634215..fcd5aaecae 100644
--- a/ext/iconv/tests/iconv_strpos_variation5.phpt
+++ b/ext/iconv/tests/iconv_strpos_variation5.phpt
@@ -32,10 +32,9 @@ $needle_mb = base64_decode(b'44CC');
/*
* Loop through integers as multiples of ten for $offset argument
- * iconv_strpos should not be able to accept negative values as $offset.
* 60 is larger than *BYTE* count for $string_mb
*/
-for ($i = -10; $i <= 60; $i += 10) {
+for ($i = -30; $i <= 60; $i += 10) {
echo "\n**-- Offset is: $i --**\n";
echo "-- ASCII String --\n";
var_dump(iconv_strpos($string_ascii, $needle_ascii, $i));
@@ -49,7 +48,7 @@ echo "Done";
--EXPECTF--
*** Testing iconv_strpos() : usage variations ***
-**-- Offset is: -10 --**
+**-- Offset is: -30 --**
-- ASCII String --
Warning: iconv_strpos(): Offset not contained in string. in %s on line %d
@@ -59,6 +58,18 @@ bool(false)
Warning: iconv_strpos(): Offset not contained in string. in %s on line %d
bool(false)
+**-- Offset is: -20 --**
+-- ASCII String --
+int(9)
+--Multibyte String --
+int(9)
+
+**-- Offset is: -10 --**
+-- ASCII String --
+int(20)
+--Multibyte String --
+int(20)
+
**-- Offset is: 0 --**
-- ASCII String --
int(9)
diff --git a/ext/interbase/tests/interbase.inc b/ext/interbase/tests/interbase.inc
index fcf053213d..7e58ecca74 100644
--- a/ext/interbase/tests/interbase.inc
+++ b/ext/interbase/tests/interbase.inc
@@ -8,7 +8,7 @@ ini_set('ibase.default_user',$user);
ini_set('ibase.default_password',$password);
/* we need just the generated name, not the file itself */
-unlink($test_base = tempnam('/tmp',"php_ibase_test"));
+unlink($test_base = tempnam(sys_get_temp_dir(),"php_ibase_test"));
function init_db()
{
diff --git a/ext/intl/ERROR.CONVENTIONS b/ext/intl/ERROR.CONVENTIONS
index e053c8fbc7..a7ef53665e 100644
--- a/ext/intl/ERROR.CONVENTIONS
+++ b/ext/intl/ERROR.CONVENTIONS
@@ -80,7 +80,7 @@ ICU operates, where functions return immediately if an error is set.
Error resetting can be done with:
void intl_error_reset(NULL); /* reset global error */
-void intl_errors_reset(intl_error* err ); /* reset global and object error */
+void intl_errors_reset(intl_error* err); /* reset global and object error */
In practice, intl_errors_reset() is not used because most classes have also
plain functions mapped to the same internal functions as their instance methods.
diff --git a/ext/intl/calendar/calendar_class.cpp b/ext/intl/calendar/calendar_class.cpp
index 72cd7e1dd0..574f9a0fc3 100644
--- a/ext/intl/calendar/calendar_class.cpp
+++ b/ext/intl/calendar/calendar_class.cpp
@@ -505,8 +505,6 @@ void calendar_register_IntlCalendar_class(void)
CALENDAR_DECL_LONG_CONST("FIELD_AM_PM", UCAL_AM_PM);
CALENDAR_DECL_LONG_CONST("FIELD_HOUR", UCAL_HOUR);
CALENDAR_DECL_LONG_CONST("FIELD_HOUR_OF_DAY", UCAL_HOUR_OF_DAY);
- CALENDAR_DECL_LONG_CONST("FIELD_HOUR", UCAL_HOUR);
- CALENDAR_DECL_LONG_CONST("FIELD_HOUR_OF_DAY", UCAL_HOUR_OF_DAY);
CALENDAR_DECL_LONG_CONST("FIELD_MINUTE", UCAL_MINUTE);
CALENDAR_DECL_LONG_CONST("FIELD_SECOND", UCAL_SECOND);
CALENDAR_DECL_LONG_CONST("FIELD_MILLISECOND", UCAL_MILLISECOND);
diff --git a/ext/intl/collator/collator_convert.c b/ext/intl/collator/collator_convert.c
index 8f06c8f1ca..2d431a19d6 100644
--- a/ext/intl/collator/collator_convert.c
+++ b/ext/intl/collator/collator_convert.c
@@ -28,12 +28,6 @@
#include <unicode/ustring.h>
#include <php.h>
-#if PHP_VERSION_ID <= 50100
-#define CAST_OBJECT_SHOULD_FREE ,0
-#else
-#define CAST_OBJECT_SHOULD_FREE
-#endif
-
#define COLLATOR_CONVERT_RETURN_FAILED(retval) { \
Z_TRY_ADDREF_P(retval); \
return retval; \
@@ -258,7 +252,7 @@ zval* collator_convert_object_to_string( zval* obj, zval *rv )
{
zstr = rv;
- if( Z_OBJ_HT_P(obj)->cast_object( obj, zstr, IS_STRING CAST_OBJECT_SHOULD_FREE ) == FAILURE )
+ if( Z_OBJ_HT_P(obj)->cast_object( obj, zstr, IS_STRING ) == FAILURE )
{
/* cast_object failed => bail out. */
zval_ptr_dtor( zstr );
diff --git a/ext/intl/collator/collator_is_numeric.c b/ext/intl/collator/collator_is_numeric.c
index 6b0568dd64..4f6efbfa6d 100644
--- a/ext/intl/collator/collator_is_numeric.c
+++ b/ext/intl/collator/collator_is_numeric.c
@@ -17,17 +17,6 @@
#include "collator_is_numeric.h"
-#if ZEND_MODULE_API_NO < 20071006
-/* not 5.3 */
-#ifndef ALLOCA_FLAG
-#define ALLOCA_FLAG(use_heap)
-#endif
-#define _do_alloca(x, y) do_alloca((x))
-#define _free_alloca(x, y) free_alloca((x))
-#else
-#define _do_alloca do_alloca
-#define _free_alloca free_alloca
-#endif
/* {{{ collator_u_strtod
* Taken from PHP6:zend_u_strtod()
*/
@@ -87,7 +76,7 @@ static double collator_u_strtod(const UChar *nptr, UChar **endptr) /* {{{ */
if (length < sizeof(buf)) {
numbuf = buf;
} else {
- numbuf = (char *) _do_alloca(length + 1, use_heap);
+ numbuf = (char *) do_alloca(length + 1, use_heap);
}
bufpos = numbuf;
@@ -100,7 +89,7 @@ static double collator_u_strtod(const UChar *nptr, UChar **endptr) /* {{{ */
value = zend_strtod(numbuf, NULL);
if (numbuf != buf) {
- _free_alloca(numbuf, use_heap);
+ free_alloca(numbuf, use_heap);
}
if (endptr != NULL) {
diff --git a/ext/intl/dateformat/dateformat_format_object.cpp b/ext/intl/dateformat/dateformat_format_object.cpp
index 3be76332a8..e96ebe8243 100644
--- a/ext/intl/dateformat/dateformat_format_object.cpp
+++ b/ext/intl/dateformat/dateformat_format_object.cpp
@@ -146,7 +146,9 @@ U_CFUNC PHP_FUNCTION(datefmt_format_object)
}
//there's no support for relative time in ICU yet
- timeStyle = (DateFormat::EStyle)(timeStyle & ~DateFormat::kRelative);
+ if (timeStyle != DateFormat::NONE) {
+ timeStyle = (DateFormat::EStyle)(timeStyle & ~DateFormat::kRelative);
+ }
zend_class_entry *instance_ce = Z_OBJCE_P(object);
if (instanceof_function(instance_ce, Calendar_ce_ptr)) {
@@ -188,11 +190,11 @@ U_CFUNC PHP_FUNCTION(datefmt_format_object)
}
if (pattern) {
- df = new SimpleDateFormat(
- UnicodeString(Z_STRVAL_P(format), Z_STRLEN_P(format),
- UnicodeString::kInvariant),
- Locale::createFromName(locale_str),
- status);
+ StringPiece sp(Z_STRVAL_P(format));
+ df = new SimpleDateFormat(
+ UnicodeString::fromUTF8(sp),
+ Locale::createFromName(locale_str),
+ status);
if (U_FAILURE(status)) {
intl_error_set(NULL, status,
diff --git a/ext/intl/grapheme/grapheme_string.c b/ext/intl/grapheme/grapheme_string.c
index 5687e3e260..0735a7e822 100644
--- a/ext/intl/grapheme/grapheme_string.c
+++ b/ext/intl/grapheme/grapheme_string.c
@@ -135,20 +135,21 @@ PHP_FUNCTION(grapheme_strpos)
RETURN_FALSE;
}
+ if (offset >= 0) {
+ /* quick check to see if the string might be there
+ * I realize that 'offset' is 'grapheme count offset' but will work in spite of that
+ */
+ found = php_memnstr(haystack + noffset, needle, needle_len, haystack + haystack_len);
- /* quick check to see if the string might be there
- * I realize that 'offset' is 'grapheme count offset' but will work in spite of that
- */
- found = php_memnstr(haystack + noffset, needle, needle_len, haystack + haystack_len);
-
- /* if it isn't there the we are done */
- if (!found) {
- RETURN_FALSE;
- }
+ /* if it isn't there the we are done */
+ if (!found) {
+ RETURN_FALSE;
+ }
- /* if it is there, and if the haystack is ascii, we are all done */
- if ( grapheme_ascii_check((unsigned char *)haystack, haystack_len) >= 0 ) {
- RETURN_LONG(found - haystack);
+ /* if it is there, and if the haystack is ascii, we are all done */
+ if ( grapheme_ascii_check((unsigned char *)haystack, haystack_len) >= 0 ) {
+ RETURN_LONG(found - haystack);
+ }
}
/* do utf16 part of the strpos */
@@ -196,7 +197,6 @@ PHP_FUNCTION(grapheme_stripos)
RETURN_FALSE;
}
-
is_ascii = ( grapheme_ascii_check((unsigned char*)haystack, haystack_len) >= 0 );
if ( is_ascii ) {
@@ -804,6 +804,10 @@ PHP_FUNCTION(grapheme_extract)
RETURN_FALSE;
}
+ if (lstart < 0) {
+ lstart += str_len;
+ }
+
if ( NULL != next ) {
if ( !Z_ISREF_P(next) ) {
intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
diff --git a/ext/intl/msgformat/msgformat_class.h b/ext/intl/msgformat/msgformat_class.h
index a90042e58f..6823595f6d 100644
--- a/ext/intl/msgformat/msgformat_class.h
+++ b/ext/intl/msgformat/msgformat_class.h
@@ -24,6 +24,11 @@
#include "../intl_common.h"
#include "../intl_error.h"
#include "../intl_data.h"
+
+#if U_ICU_VERSION_MAJOR_NUM * 10 + U_ICU_VERSION_MINOR_NUM < 48
+# define MSG_FORMAT_QUOTE_APOS 1
+#endif
+
#include "msgformat_data.h"
typedef struct {
@@ -54,8 +59,4 @@ extern zend_class_entry *MessageFormatter_ce_ptr;
#define MSG_FORMAT_OBJECT(mfo) (mfo)->mf_data.umsgf
-#if U_ICU_VERSION_MAJOR_NUM * 10 + U_ICU_VERSION_MINOR_NUM < 48
-# define MSG_FORMAT_QUOTE_APOS 1
-#endif
-
#endif // #ifndef MSG_FORMAT_CLASS_H
diff --git a/ext/intl/tests/bug69374.phpt b/ext/intl/tests/bug69374.phpt
new file mode 100644
index 0000000000..4d9fffab7a
--- /dev/null
+++ b/ext/intl/tests/bug69374.phpt
@@ -0,0 +1,24 @@
+--TEST--
+IntlDateFormatter::formatObject(): returns wrong utf8 value when $format param is utf8 string pattern.
+--SKIPIF--
+<?php
+if (!extension_loaded('intl')) die('skip intl extension not enabled'); ?>
+<?php if (version_compare(INTL_ICU_VERSION, '50.1.2') < 0) die('skip for ICU >= 51.1.2'); ?>
+--FILE--
+<?php
+$millitimestamp = 1428133423941.0; // 14:43:43 April 04 2015
+$pattern1 = '\'tháng\' MM, y'; // yMM format for Vietnamese
+$pattern2 = 'yë…„ MMM'; // yMM format for Korean
+$date = IntlCalendar::createInstance('Asia/Ho_Chi_Minh');
+$date->setTime($millitimestamp);
+echo IntlDateFormatter::formatObject($date, $pattern1, 'vi_VN'), "\n";
+echo IntlDateFormatter::formatObject ($date, $pattern2, 'ko_KR'), "\n";
+?>
+==DONE==
+
+--EXPECTF--
+tháng 04, 2015
+2015ë…„ 4ì›”
+==DONE==
+
+
diff --git a/ext/intl/tests/bug69398.phpt b/ext/intl/tests/bug69398.phpt
new file mode 100644
index 0000000000..ea7dbd5098
--- /dev/null
+++ b/ext/intl/tests/bug69398.phpt
@@ -0,0 +1,22 @@
+--TEST--
+IntlDateFormatter::formatObject(): returns wrong value when time style is NONE.
+--SKIPIF--
+<?php
+if (!extension_loaded('intl')) die('skip intl extension not enabled'); ?>
+<?php if (version_compare(INTL_ICU_VERSION, '50.1.2') < 0) die('skip for ICU >= 51.1.2'); ?>
+--FILE--
+<?php
+$millitimestamp = 1428133423941.0; // 14:43:43 April 04 2015
+$date = IntlCalendar::createInstance('Asia/Ho_Chi_Minh');
+$date->setTime($millitimestamp);
+echo IntlDateFormatter::formatObject($date, array(IntlDateFormatter::SHORT, IntlDateFormatter::NONE), 'vi_VN'), "\n";
+echo IntlDateFormatter::formatObject ($date, array(IntlDateFormatter::SHORT, IntlDateFormatter::NONE), 'ko_KR'), "\n";
+?>
+==DONE==
+
+--EXPECTF--
+04/04/2015
+15. 4. 4.
+==DONE==
+
+
diff --git a/ext/intl/tests/grapheme.phpt b/ext/intl/tests/grapheme.phpt
index def9110d0d..251b1d142f 100644
--- a/ext/intl/tests/grapheme.phpt
+++ b/ext/intl/tests/grapheme.phpt
@@ -68,9 +68,13 @@ function ut_main()
array( "abc", "a", 0 ),
array( "abc", "a", 0, 0 ),
array( "abc", "a", 1, "false" ),
+ array( "abc", "a", -1, "false" ),
array( "ababc", "a", 1, 2 ),
array( "ao" . $char_a_ring_nfd . "bc" . $char_o_diaeresis_nfd . "o", "o", 2, 6 ),
+ array( "ao" . $char_a_ring_nfd . "bc" . $char_o_diaeresis_nfd . "o", "o", -1, 6 ),
+ array( "ao" . $char_a_ring_nfd . "bc" . $char_o_diaeresis_nfd . "o", "o", -5, 6 ),
array( $char_o_diaeresis_nfd . $char_a_ring_nfd . "a" . $char_a_ring_nfd . "bc", $char_a_ring_nfd, 2, 3 ),
+ array( $char_o_diaeresis_nfd . $char_a_ring_nfd . "a" . $char_a_ring_nfd . "bc", $char_a_ring_nfd, -4, 3 ),
array( "a" . $char_a_ring_nfd . "bc" . $char_o_diaeresis_nfd . "opq", "op", 5 ),
array( "a" . $char_a_ring_nfd . "bc" . $char_o_diaeresis_nfd . "opq", "opq", 5 ),
@@ -91,6 +95,7 @@ function ut_main()
array( "ababc", "ab", 1, 2 ),
array( "ababc", "abc", 1, 2 ),
array( "ao" . $char_a_ring_nfd . "bc" . $char_o_diaeresis_nfd . "o" . $char_a_ring_nfd . "bc", "o" . $char_a_ring_nfd . "bc", 2, 6 ),
+ array( "ao" . $char_a_ring_nfd . "bc" . $char_o_diaeresis_nfd . "o" . $char_a_ring_nfd . "bc", "o" . $char_a_ring_nfd . "bc", -8, 6 ),
array( $char_o_diaeresis_nfd . $char_a_ring_nfd . "a" . $char_a_ring_nfd . "bc" . $char_a_ring_nfd . "def", $char_a_ring_nfd . "bc" . $char_a_ring_nfd, 2, 3 ),
);
@@ -120,10 +125,12 @@ function ut_main()
$tests = array(
array( "ao" . $char_a_ring_nfd . "bc" . $char_o_diaeresis_nfd . "O", "o", 2, 6 ),
+ array( "ao" . $char_a_ring_nfd . "bc" . $char_o_diaeresis_nfd . "Oo", "o", -6, 6 ),
array( $char_o_diaeresis_nfd . $char_a_ring_nfd . "a" . $char_A_ring_nfd . "bc", $char_a_ring_nfd, 2, 3 ),
array( "a" . $char_a_ring_nfd . "bc" . $char_o_diaeresis_nfd . "O", "o", 5 ),
array( "a" . $char_a_ring_nfd . "bc" . $char_o_diaeresis_nfd, "O", "false" ),
array( "a" . $char_a_ring_nfd . "bc" . $char_O_diaeresis_nfd, $char_o_diaeresis_nfd, 4 ),
+ array( "a" . $char_a_ring_nfd . "bc" . $char_O_diaeresis_nfd, $char_o_diaeresis_nfd, -1, 4 ),
array( $char_o_diaeresis_nfd . "a" . $char_a_ring_nfd . "bc", $char_A_ring_nfd, 2 ),
array( "a" . $char_A_ring_nfd . "bc", $char_a_ring_nfd, 1 ),
array( "Abc", $char_a_ring_nfd, "false" ),
@@ -153,6 +160,7 @@ function ut_main()
array( "aBc", "abC", 0, 0 ),
array( "abc", "aBc", 1, "false" ),
array( "ABabc", "AB", 1, 2 ),
+ array( "ABabc", "AB", -4, 2 ),
array( "abaBc", "aBc", 1, 2 ),
array( "ao" . $char_a_ring_nfd . "bc" . $char_o_diaeresis_nfd . "o" . $char_A_ring_nfd . "bC", "O" . $char_a_ring_nfd . "bC", 2, 6 ),
array( $char_o_diaeresis_nfd . $char_a_ring_nfd . "a" . $char_A_ring_nfd . "bC" . $char_a_ring_nfd . "def", $char_a_ring_nfd . "Bc" . $char_a_ring_nfd, 2, 3 ),
@@ -559,6 +567,7 @@ function ut_main()
array( "abc", 1, 0, 1, "a" ),
array( "abc", 1, 1, 2, "b" ),
array( "abc", 1, 2, 3, "c" ),
+ array( "abc", 1, -2, 2, "b" ),
array( "abc", 0, 2, 2, "" ),
array( "http://news.bbc.co.uk/2/hi/middle_east/7831588.stm", 48, 48 , 50 , "tm" ),
@@ -569,8 +578,11 @@ function ut_main()
array( $char_a_ring_nfd . "bc", 2, 0, 4, $char_a_ring_nfd . "b" ),
array( $char_a_ring_nfd . "bc", 1, 0, 3, $char_a_ring_nfd . "" ),
array( $char_a_ring_nfd . "bcde", 2, 3, 5, "bc" ),
+ array( $char_a_ring_nfd . "bcde", 2, -4, 5, "bc" ),
array( $char_a_ring_nfd . "bcde", 2, 4, 6, "cd" ),
+ array( $char_a_ring_nfd . "bcde", 2, -7, 4, $char_a_ring_nfd . "b" ),
array( $char_a_ring_nfd . "bcde" . $char_a_ring_nfd . "f", 4, 5, 11, "de" . $char_a_ring_nfd . "f" ),
+ array( $char_a_ring_nfd . "bcde" . $char_a_ring_nfd . "f", 4, -6, 11, "de" . $char_a_ring_nfd . "f" ),
array( $char_a_ring_nfd . $char_o_diaeresis_nfd . $char_o_diaeresis_nfd, 3, $char_a_ring_nfd . $char_o_diaeresis_nfd . $char_o_diaeresis_nfd ),
array( $char_a_ring_nfd . $char_o_diaeresis_nfd . $char_o_diaeresis_nfd, 2, $char_a_ring_nfd . $char_o_diaeresis_nfd ),
@@ -794,9 +806,13 @@ find "b" in "abc" - grapheme_strpos = 1 == 1
find "a" in "abc" - grapheme_strpos = 0 == 0
find "a" in "abc" - grapheme_strpos from 0 = 0 == 0
find "a" in "abc" - grapheme_strpos from 1 = false == false
+find "a" in "abc" - grapheme_strpos from -1 = false == false
find "a" in "ababc" - grapheme_strpos from 1 = 2 == 2
find "o" in "aoa%CC%8Abco%CC%88o" - grapheme_strpos from 2 = 6 == 6
+find "o" in "aoa%CC%8Abco%CC%88o" - grapheme_strpos from -1 = 6 == 6
+find "o" in "aoa%CC%8Abco%CC%88o" - grapheme_strpos from -5 = 6 == 6
find "a%CC%8A" in "o%CC%88a%CC%8Aaa%CC%8Abc" - grapheme_strpos from 2 = 3 == 3
+find "a%CC%8A" in "o%CC%88a%CC%8Aaa%CC%8Abc" - grapheme_strpos from -4 = 3 == 3
find "op" in "aa%CC%8Abco%CC%88opq" - grapheme_strpos = 5 == 5
find "opq" in "aa%CC%8Abco%CC%88opq" - grapheme_strpos = 5 == 5
find "abc" in "aa%CC%8Abco%CC%88" - grapheme_strpos = false == false
@@ -816,15 +832,18 @@ find "abc" in "abc" - grapheme_strpos from 1 = false == false
find "ab" in "ababc" - grapheme_strpos from 1 = 2 == 2
find "abc" in "ababc" - grapheme_strpos from 1 = 2 == 2
find "oa%CC%8Abc" in "aoa%CC%8Abco%CC%88oa%CC%8Abc" - grapheme_strpos from 2 = 6 == 6
+find "oa%CC%8Abc" in "aoa%CC%8Abco%CC%88oa%CC%8Abc" - grapheme_strpos from -8 = 6 == 6
find "a%CC%8Abca%CC%8A" in "o%CC%88a%CC%8Aaa%CC%8Abca%CC%8Adef" - grapheme_strpos from 2 = 3 == 3
function grapheme_stripos($haystack, $needle, $offset = 0) {}
find "o" in "aoa%CC%8Abco%CC%88O" - grapheme_stripos from 2 = 6 == 6
+find "o" in "aoa%CC%8Abco%CC%88Oo" - grapheme_stripos from -6 = 6 == 6
find "a%CC%8A" in "o%CC%88a%CC%8AaA%CC%8Abc" - grapheme_stripos from 2 = 3 == 3
find "o" in "aa%CC%8Abco%CC%88O" - grapheme_stripos = 5 == 5
find "O" in "aa%CC%8Abco%CC%88" - grapheme_stripos = false == false
find "o%CC%88" in "aa%CC%8AbcO%CC%88" - grapheme_stripos = 4 == 4
+find "o%CC%88" in "aa%CC%8AbcO%CC%88" - grapheme_stripos from -1 = 4 == 4
find "A%CC%8A" in "o%CC%88aa%CC%8Abc" - grapheme_stripos = 2 == 2
find "a%CC%8A" in "aA%CC%8Abc" - grapheme_stripos = 1 == 1
find "a%CC%8A" in "Abc" - grapheme_stripos = false == false
@@ -853,6 +872,7 @@ find "ab" in "ABc" - grapheme_stripos from 0 = 0 == 0
find "abC" in "aBc" - grapheme_stripos from 0 = 0 == 0
find "aBc" in "abc" - grapheme_stripos from 1 = false == false
find "AB" in "ABabc" - grapheme_stripos from 1 = 2 == 2
+find "AB" in "ABabc" - grapheme_stripos from -4 = 2 == 2
find "aBc" in "abaBc" - grapheme_stripos from 1 = 2 == 2
find "Oa%CC%8AbC" in "aoa%CC%8Abco%CC%88oA%CC%8AbC" - grapheme_stripos from 2 = 6 == 6
find "a%CC%8ABca%CC%8A" in "o%CC%88a%CC%8AaA%CC%8AbCa%CC%8Adef" - grapheme_stripos from 2 = 3 == 3
@@ -1094,6 +1114,7 @@ extract from "abc" "0" graphemes - grapheme_extract starting at byte position 0
extract from "abc" "1" graphemes - grapheme_extract starting at byte position 0 with $next = a == a $next=1 == 1
extract from "abc" "1" graphemes - grapheme_extract starting at byte position 1 with $next = b == b $next=2 == 2
extract from "abc" "1" graphemes - grapheme_extract starting at byte position 2 with $next = c == c $next=3 == 3
+extract from "abc" "1" graphemes - grapheme_extract starting at byte position -2 with $next = b == b $next=2 == 2
extract from "abc" "0" graphemes - grapheme_extract starting at byte position 2 with $next = == $next=2 == 2
extract from "http%3A%2F%2Fnews.bbc.co.uk%2F2%2Fhi%2Fmiddle_east%2F7831588.stm" "48" graphemes - grapheme_extract starting at byte position 48 with $next = tm == tm $next=50 == 50
extract from "a%CC%8Abc" "3" graphemes - grapheme_extract = a%CC%8Abc == a%CC%8Abc
@@ -1103,8 +1124,11 @@ extract from "a%CC%8Abc" "3" graphemes - grapheme_extract starting at byte posit
extract from "a%CC%8Abc" "2" graphemes - grapheme_extract starting at byte position 0 with $next = a%CC%8Ab == a%CC%8Ab $next=4 == 4
extract from "a%CC%8Abc" "1" graphemes - grapheme_extract starting at byte position 0 with $next = a%CC%8A == a%CC%8A $next=3 == 3
extract from "a%CC%8Abcde" "2" graphemes - grapheme_extract starting at byte position 3 with $next = bc == bc $next=5 == 5
+extract from "a%CC%8Abcde" "2" graphemes - grapheme_extract starting at byte position -4 with $next = bc == bc $next=5 == 5
extract from "a%CC%8Abcde" "2" graphemes - grapheme_extract starting at byte position 4 with $next = cd == cd $next=6 == 6
+extract from "a%CC%8Abcde" "2" graphemes - grapheme_extract starting at byte position -7 with $next = a%CC%8Ab == a%CC%8Ab $next=4 == 4
extract from "a%CC%8Abcdea%CC%8Af" "4" graphemes - grapheme_extract starting at byte position 5 with $next = dea%CC%8Af == dea%CC%8Af $next=11 == 11
+extract from "a%CC%8Abcdea%CC%8Af" "4" graphemes - grapheme_extract starting at byte position -6 with $next = dea%CC%8Af == dea%CC%8Af $next=11 == 11
extract from "a%CC%8Ao%CC%88o%CC%88" "3" graphemes - grapheme_extract = a%CC%8Ao%CC%88o%CC%88 == a%CC%8Ao%CC%88o%CC%88
extract from "a%CC%8Ao%CC%88o%CC%88" "2" graphemes - grapheme_extract = a%CC%8Ao%CC%88 == a%CC%8Ao%CC%88
extract from "a%CC%8Ao%CC%88c" "1" graphemes - grapheme_extract = a%CC%8A == a%CC%8A
diff --git a/ext/intl/tests/timezone_IDforWindowsID_basic.phpt b/ext/intl/tests/timezone_IDforWindowsID_basic.phpt
new file mode 100644
index 0000000000..4127d8e31c
--- /dev/null
+++ b/ext/intl/tests/timezone_IDforWindowsID_basic.phpt
@@ -0,0 +1,46 @@
+--TEST--
+IntlTimeZone::getIDForWindowsID basic test
+--SKIPIF--
+<?php
+if (!extension_loaded('intl'))
+ die('skip intl extension not enabled');
+if (version_compare(INTL_ICU_VERSION, '52') < 0)
+ die('skip for ICU >= 52');
+--FILE--
+<?php
+
+$tzs = array(
+ 'Gnomeregan' => array(NULL),
+ 'India Standard Time' => array(NULL),
+ 'Pacific Standard Time' => array('001', 'CA', 'MX', 'US', 'ZZ'),
+ 'Romance Standard Time' => array('001', 'BE', 'DK', 'ES', 'FR'),
+);
+
+foreach ($tzs as $tz => $regions) {
+ echo "** $tz\n";
+ foreach ($regions as $region) {
+ var_dump(IntlTimeZone::getIDForWindowsID($tz, $region));
+ if (intl_get_error_code() != U_ZERO_ERROR) {
+ echo "Error: ", intl_get_error_message(), "\n";
+ }
+ }
+}
+
+--EXPECT--
+** Gnomeregan
+bool(false)
+Error: intltz_get_windows_id: Unknown windows timezone: U_ILLEGAL_ARGUMENT_ERROR
+** India Standard Time
+string(13) "Asia/Calcutta"
+** Pacific Standard Time
+string(19) "America/Los_Angeles"
+string(17) "America/Vancouver"
+string(15) "America/Tijuana"
+string(19) "America/Los_Angeles"
+string(7) "PST8PDT"
+** Romance Standard Time
+string(12) "Europe/Paris"
+string(15) "Europe/Brussels"
+string(17) "Europe/Copenhagen"
+string(13) "Europe/Madrid"
+string(12) "Europe/Paris"
diff --git a/ext/intl/tests/timezone_getCanonicalID_error.phpt b/ext/intl/tests/timezone_getCanonicalID_error.phpt
index e268e216a8..b29ca67701 100644
--- a/ext/intl/tests/timezone_getCanonicalID_error.phpt
+++ b/ext/intl/tests/timezone_getCanonicalID_error.phpt
@@ -11,7 +11,6 @@ ini_set("intl.error_level", E_WARNING);
var_dump(IntlTimeZone::getCanonicalID());
var_dump(IntlTimeZone::getCanonicalID(array()));
var_dump(IntlTimeZone::getCanonicalID("foo\x81"));
-var_dump(IntlTimeZone::getCanonicalID('foobar', null));
--EXPECTF--
@@ -28,8 +27,3 @@ bool(false)
Warning: IntlTimeZone::getCanonicalID(): intltz_get_canonical_id: could not convert time zone id to UTF-16 in %s on line %d
bool(false)
-
-Fatal error: Uncaught Error: Cannot pass parameter 2 by reference in %s:%d
-Stack trace:
-#0 {main}
- thrown in %s on line %d
diff --git a/ext/intl/tests/timezone_windowsID_basic.phpt b/ext/intl/tests/timezone_windowsID_basic.phpt
new file mode 100644
index 0000000000..dd48f016e2
--- /dev/null
+++ b/ext/intl/tests/timezone_windowsID_basic.phpt
@@ -0,0 +1,43 @@
+--TEST--
+IntlTimeZone::getWindowsID basic test
+--SKIPIF--
+<?php
+if (!extension_loaded('intl'))
+ die('skip intl extension not enabled');
+if (version_compare(INTL_ICU_VERSION, '52') < 0)
+ die('skip for ICU >= 52');
+--FILE--
+<?php
+
+$tzs = array(
+ 'America/Bogota',
+ 'America/Havana',
+ 'America/Indiana/Knox',
+ 'America/Los_Angeles',
+ 'Azeroth/Kalimdor/Durotar',
+ 'Africa/Casablanca',
+ 'Asia/Singapore',
+ 'Australia/Perth',
+ 'Europe/London',
+ 'Europe/Istanbul',
+);
+
+foreach ($tzs as $tz) {
+ var_dump(IntlTimeZone::getWindowsID($tz));
+ if (intl_get_error_code() != U_ZERO_ERROR) {
+ echo "Error: ", intl_get_error_message(), "\n";
+ }
+}
+
+--EXPECT--
+string(24) "SA Pacific Standard Time"
+string(21) "Eastern Standard Time"
+string(21) "Central Standard Time"
+string(21) "Pacific Standard Time"
+bool(false)
+Error: intltz_get_windows_id: Unknown system timezone: U_ILLEGAL_ARGUMENT_ERROR
+string(21) "Morocco Standard Time"
+string(23) "Singapore Standard Time"
+string(26) "W. Australia Standard Time"
+string(17) "GMT Standard Time"
+string(20) "Turkey Standard Time"
diff --git a/ext/intl/timezone/timezone_class.cpp b/ext/intl/timezone/timezone_class.cpp
index d1e8e2e0a6..f67e55ae4e 100644
--- a/ext/intl/timezone/timezone_class.cpp
+++ b/ext/intl/timezone/timezone_class.cpp
@@ -439,6 +439,17 @@ ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(ainfo_tz_void, 0, 0, 0)
ZEND_END_ARG_INFO()
+#if U_ICU_VERSION_MAJOR_NUM >= 52
+ZEND_BEGIN_ARG_INFO_EX(ainfo_tz_getWindowsID, 0, ZEND_RETURN_VALUE, 1)
+ ZEND_ARG_INFO(0, timezone)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(ainfo_tz_getIDForWindowsID, 0, ZEND_RETURN_VALUE, 1)
+ ZEND_ARG_INFO(0, timezone)
+ ZEND_ARG_INFO(0, region)
+ZEND_END_ARG_INFO()
+#endif
+
/* }}} */
/* {{{ TimeZone_class_functions
@@ -475,6 +486,10 @@ static zend_function_entry TimeZone_class_functions[] = {
PHP_ME_MAPPING(toDateTimeZone, intltz_to_date_time_zone, ainfo_tz_void, ZEND_ACC_PUBLIC)
PHP_ME_MAPPING(getErrorCode, intltz_get_error_code, ainfo_tz_void, ZEND_ACC_PUBLIC)
PHP_ME_MAPPING(getErrorMessage, intltz_get_error_message, ainfo_tz_void, ZEND_ACC_PUBLIC)
+#if U_ICU_VERSION_MAJOR_NUM >= 52
+ PHP_ME_MAPPING(getWindowsID, intltz_get_windows_id, ainfo_tz_getWindowsID, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
+ PHP_ME_MAPPING(getIDForWindowsID, intltz_get_id_for_windows_id, ainfo_tz_getIDForWindowsID, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
+#endif
PHP_FE_END
};
/* }}} */
diff --git a/ext/intl/timezone/timezone_methods.cpp b/ext/intl/timezone/timezone_methods.cpp
index a35174d3da..20c0e02480 100644
--- a/ext/intl/timezone/timezone_methods.cpp
+++ b/ext/intl/timezone/timezone_methods.cpp
@@ -647,3 +647,81 @@ U_CFUNC PHP_FUNCTION(intltz_get_error_message)
message = intl_error_get_message(TIMEZONE_ERROR_P(to));
RETURN_STR(message);
}
+
+#if U_ICU_VERSION_MAJOR_NUM >= 52
+/* {{{ proto string IntlTimeZone::getWindowsID(string $timezone)
+ proto string intltz_get_windows_id(string $timezone)
+Translate a system timezone (e.g. "America/Los_Angeles" into a
+Windows Timezone (e.g. "Pacific Standard Time")
+ */
+U_CFUNC PHP_FUNCTION(intltz_get_windows_id)
+{
+ zend_string *id, *winID;
+ UnicodeString uID, uWinID;
+ UErrorCode error;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &id) == FAILURE) {
+ return;
+ }
+
+ error = U_ZERO_ERROR;
+ if (intl_stringFromChar(uID, id->val, id->len, &error) == FAILURE) {
+ intl_error_set(NULL, error,
+ "intltz_get_windows_id: could not convert time zone id to UTF-16", 0);
+ RETURN_FALSE;
+ }
+
+ error = U_ZERO_ERROR;
+ TimeZone::getWindowsID(uID, uWinID, error);
+ INTL_CHECK_STATUS(error, "intltz_get_windows_id: Unable to get timezone from windows ID");
+ if (uWinID.length() == 0) {
+ intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "intltz_get_windows_id: Unknown system timezone", 0);
+ RETURN_FALSE;
+ }
+
+ error = U_ZERO_ERROR;
+ winID = intl_convert_utf16_to_utf8(uWinID.getBuffer(), uWinID.length(), &error);
+ INTL_CHECK_STATUS(error, "intltz_get_windows_id: could not convert time zone id to UTF-8");
+ RETURN_STR(winID);
+}
+/* }}} */
+
+/* {{{ proto string IntlTimeZone::getIDForWindowsID(string $timezone[, string $region = NULL])
+ proto string intltz_get_id_for_windows_id(string $timezone[, string $region = NULL])
+Translate a windows timezone (e.g. "Pacific Time Zone" into a
+System Timezone (e.g. "America/Los_Angeles")
+ */
+U_CFUNC PHP_FUNCTION(intltz_get_id_for_windows_id)
+{
+ zend_string *winID, *region = NULL, *id;
+ UnicodeString uWinID, uID;
+ UErrorCode error;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS(), "S|S", &winID, &region) == FAILURE) {
+ return;
+ }
+
+ error = U_ZERO_ERROR;
+ if (intl_stringFromChar(uWinID, winID->val, winID->len, &error) == FAILURE) {
+ intl_error_set(NULL, error,
+ "intltz_get_id_for_windows_id: could not convert time zone id to UTF-16", 0);
+ RETURN_FALSE;
+ }
+
+ error = U_ZERO_ERROR;
+ TimeZone::getIDForWindowsID(uWinID, region ? region->val : NULL, uID, error);
+ INTL_CHECK_STATUS(error, "intltz_get_id_for_windows_id: Unable to get windows ID for timezone");
+ if (uID.length() == 0) {
+ intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "intltz_get_windows_id: Unknown windows timezone", 0);
+ RETURN_FALSE;
+ }
+
+ error = U_ZERO_ERROR;
+ id = intl_convert_utf16_to_utf8(uID.getBuffer(), uID.length(), &error);
+ INTL_CHECK_STATUS(error, "intltz_get_id_for_windows_id: could not convert time zone id to UTF-8");
+ RETURN_STR(id);
+}
+/* }}} */
+#endif
diff --git a/ext/intl/timezone/timezone_methods.h b/ext/intl/timezone/timezone_methods.h
index 29d72913fd..6e6fa3f472 100644
--- a/ext/intl/timezone/timezone_methods.h
+++ b/ext/intl/timezone/timezone_methods.h
@@ -65,4 +65,9 @@ PHP_FUNCTION(intltz_get_error_code);
PHP_FUNCTION(intltz_get_error_message);
+#if U_ICU_VERSION_MAJOR_NUM >= 52
+PHP_FUNCTION(intltz_get_windows_id);
+PHP_FUNCTION(intltz_get_id_for_windows_id);
+#endif
+
#endif /* #ifndef TIMEZONE_METHODS_H */
diff --git a/ext/intl/transliterator/transliterator_class.c b/ext/intl/transliterator/transliterator_class.c
index ce8c7e6291..5c80b25af8 100644
--- a/ext/intl/transliterator/transliterator_class.c
+++ b/ext/intl/transliterator/transliterator_class.c
@@ -269,9 +269,15 @@ static zval *Transliterator_read_property( zval *object, zval *member, int type,
static void Transliterator_write_property( zval *object, zval *member, zval *value,
void **cache_slot )
{
+ zend_class_entry *scope;
TRANSLITERATOR_PROPERTY_HANDLER_PROLOG;
- if( ( EG( scope ) != Transliterator_ce_ptr ) &&
+ if (EG(fake_scope)) {
+ scope = EG(fake_scope);
+ } else {
+ scope = zend_get_executed_scope();
+ }
+ if( ( scope != Transliterator_ce_ptr ) &&
( zend_binary_strcmp( "id", sizeof( "id" ) - 1,
Z_STRVAL_P( member ), Z_STRLEN_P( member ) ) == 0 ) )
{
diff --git a/ext/json/json.c b/ext/json/json.c
index 6571bf99f0..d3c6111d4d 100644
--- a/ext/json/json.c
+++ b/ext/json/json.c
@@ -117,6 +117,7 @@ static PHP_MINIT_FUNCTION(json)
PHP_JSON_REGISTER_CONSTANT("JSON_UNESCAPED_UNICODE", PHP_JSON_UNESCAPED_UNICODE);
PHP_JSON_REGISTER_CONSTANT("JSON_PARTIAL_OUTPUT_ON_ERROR", PHP_JSON_PARTIAL_OUTPUT_ON_ERROR);
PHP_JSON_REGISTER_CONSTANT("JSON_PRESERVE_ZERO_FRACTION", PHP_JSON_PRESERVE_ZERO_FRACTION);
+ PHP_JSON_REGISTER_CONSTANT("JSON_UNESCAPED_LINE_TERMINATORS", PHP_JSON_UNESCAPED_LINE_TERMINATORS);
/* options for json_decode */
PHP_JSON_REGISTER_CONSTANT("JSON_OBJECT_AS_ARRAY", PHP_JSON_OBJECT_AS_ARRAY);
diff --git a/ext/json/json_encoder.c b/ext/json/json_encoder.c
index ae9e086fc0..62df102847 100644
--- a/ext/json/json_encoder.c
+++ b/ext/json/json_encoder.c
@@ -321,7 +321,7 @@ static void php_json_escape_string(smart_str *buf, char *s, size_t len, int opti
do {
us = (unsigned char)s[pos];
- if (us >= 0x80 && !(options & PHP_JSON_UNESCAPED_UNICODE)) {
+ if (us >= 0x80 && (!(options & PHP_JSON_UNESCAPED_UNICODE) || us == 0xE2)) {
/* UTF-8 character */
us = php_next_utf8_char((const unsigned char *)s, len, &pos, &status);
if (status != SUCCESS) {
@@ -332,6 +332,15 @@ static void php_json_escape_string(smart_str *buf, char *s, size_t len, int opti
smart_str_appendl(buf, "null", 4);
return;
}
+ /* Escape U+2028/U+2029 line terminators, UNLESS both
+ JSON_UNESCAPED_UNICODE and
+ JSON_UNESCAPED_LINE_TERMINATORS were provided */
+ if ((options & PHP_JSON_UNESCAPED_UNICODE)
+ && ((options & PHP_JSON_UNESCAPED_LINE_TERMINATORS)
+ || us < 0x2028 || us > 0x2029)) {
+ smart_str_appendl(buf, &s[pos - 3], 3);
+ continue;
+ }
/* From http://en.wikipedia.org/wiki/UTF16 */
if (us >= 0x10000) {
unsigned int next_us;
diff --git a/ext/json/json_parser.tab.c b/ext/json/json_parser.tab.c
index b01d031f14..02002d681b 100644
--- a/ext/json/json_parser.tab.c
+++ b/ext/json/json_parser.tab.c
@@ -98,6 +98,14 @@ int json_yydebug = 1;
#define PHP_JSON_USE_1(uvr, uv1) PHP_JSON_USE(uvr); PHP_JSON_USE(uv1)
#define PHP_JSON_USE_2(uvr, uv1, uv2) PHP_JSON_USE(uvr); PHP_JSON_USE(uv1); PHP_JSON_USE(uv2)
+#define PHP_JSON_DEPTH_DEC --parser->depth
+#define PHP_JSON_DEPTH_INC \
+ if (parser->max_depth && parser->depth >= parser->max_depth) { \
+ parser->scanner.errcode = PHP_JSON_ERROR_DEPTH; \
+ YYERROR; \
+ } \
+ ++parser->depth
+
@@ -201,20 +209,9 @@ int php_json_yyparse (php_json_parser *parser);
/* Unqualified %code blocks. */
-int php_json_yylex(union YYSTYPE *value, php_json_parser *parser);
-void php_json_yyerror(php_json_parser *parser, char const *msg);
-void php_json_parser_object_init(php_json_parser *parser, zval *object);
-int php_json_parser_object_update(php_json_parser *parser, zval *object, zend_string *key, zval *zvalue);
-void php_json_parser_array_init(zval *object);
-void php_json_parser_array_append(zval *array, zval *zvalue);
+static int php_json_yylex(union YYSTYPE *value, php_json_parser *parser);
+static void php_json_yyerror(php_json_parser *parser, char const *msg);
-#define PHP_JSON_DEPTH_DEC --parser->depth
-#define PHP_JSON_DEPTH_INC \
- if (parser->max_depth && parser->depth >= parser->max_depth) { \
- parser->scanner.errcode = PHP_JSON_ERROR_DEPTH; \
- YYERROR; \
- } \
- ++parser->depth
@@ -514,10 +511,10 @@ static const yytype_uint8 yytranslate[] =
/* YYRLINE[YYN] -- Source line where rule number YYN was defined. */
static const yytype_uint8 yyrline[] =
{
- 0, 92, 92, 98, 105, 105, 113, 114, 123, 126,
- 130, 136, 142, 149, 154, 161, 161, 169, 170, 179,
- 182, 186, 191, 196, 203, 204, 208, 209, 210, 211,
- 212, 213, 214, 215, 216, 217, 221
+ 0, 89, 89, 95, 103, 102, 120, 121, 130, 133,
+ 137, 144, 151, 158, 163, 171, 170, 188, 189, 198,
+ 201, 205, 210, 215, 222, 223, 227, 228, 229, 230,
+ 231, 232, 233, 234, 235, 236, 240
};
#endif
@@ -1465,15 +1462,23 @@ yyreduce:
case 4:
- { PHP_JSON_DEPTH_INC; }
+ {
+ PHP_JSON_DEPTH_INC;
+ if (parser->methods.object_start && FAILURE == parser->methods.object_start(parser)) {
+ YYERROR;
+ }
+ }
break;
case 5:
{
+ ZVAL_COPY_VALUE(&(yyval.value), &(yyvsp[-1].value));
PHP_JSON_DEPTH_DEC;
- (yyval.value) = (yyvsp[-1].value);
+ if (parser->methods.object_end && FAILURE == parser->methods.object_end(parser, &(yyval.value))) {
+ YYERROR;
+ }
}
break;
@@ -1490,7 +1495,7 @@ yyreduce:
case 8:
{
- php_json_parser_object_init(parser, &(yyval.value));
+ parser->methods.object_create(parser, &(yyval.value));
}
break;
@@ -1498,9 +1503,10 @@ yyreduce:
case 10:
{
- php_json_parser_object_init(parser, &(yyval.value));
- if (php_json_parser_object_update(parser, &(yyval.value), (yyvsp[0].pair).key, &(yyvsp[0].pair).val) == FAILURE)
+ parser->methods.object_create(parser, &(yyval.value));
+ if (parser->methods.object_update(parser, &(yyval.value), (yyvsp[0].pair).key, &(yyvsp[0].pair).val) == FAILURE) {
YYERROR;
+ }
}
break;
@@ -1508,8 +1514,9 @@ yyreduce:
case 11:
{
- if (php_json_parser_object_update(parser, &(yyvsp[-2].value), (yyvsp[0].pair).key, &(yyvsp[0].pair).val) == FAILURE)
+ if (parser->methods.object_update(parser, &(yyvsp[-2].value), (yyvsp[0].pair).key, &(yyvsp[0].pair).val) == FAILURE) {
YYERROR;
+ }
ZVAL_COPY_VALUE(&(yyval.value), &(yyvsp[-2].value));
}
@@ -1542,15 +1549,23 @@ yyreduce:
case 15:
- { PHP_JSON_DEPTH_INC; }
+ {
+ PHP_JSON_DEPTH_INC;
+ if (parser->methods.array_start && FAILURE == parser->methods.array_start(parser)) {
+ YYERROR;
+ }
+ }
break;
case 16:
{
- PHP_JSON_DEPTH_DEC;
ZVAL_COPY_VALUE(&(yyval.value), &(yyvsp[-1].value));
+ PHP_JSON_DEPTH_DEC;
+ if (parser->methods.array_end && FAILURE == parser->methods.array_end(parser, &(yyval.value))) {
+ YYERROR;
+ }
}
break;
@@ -1567,7 +1582,7 @@ yyreduce:
case 19:
{
- php_json_parser_array_init(&(yyval.value));
+ parser->methods.array_create(parser, &(yyval.value));
}
break;
@@ -1575,8 +1590,8 @@ yyreduce:
case 21:
{
- php_json_parser_array_init(&(yyval.value));
- php_json_parser_array_append(&(yyval.value), &(yyvsp[0].value));
+ parser->methods.array_create(parser, &(yyval.value));
+ parser->methods.array_append(parser, &(yyval.value), &(yyvsp[0].value));
}
break;
@@ -1584,7 +1599,7 @@ yyreduce:
case 22:
{
- php_json_parser_array_append(&(yyvsp[-2].value), &(yyvsp[0].value));
+ parser->methods.array_append(parser, &(yyvsp[-2].value), &(yyvsp[0].value));
ZVAL_COPY_VALUE(&(yyval.value), &(yyvsp[-2].value));
}
@@ -1839,30 +1854,27 @@ yyreturn:
/* Functions */
-void php_json_parser_init(php_json_parser *parser, zval *return_value, char *str, size_t str_len, int options, int max_depth)
+static int php_json_parser_array_create(php_json_parser *parser, zval *array)
{
- memset(parser, 0, sizeof(php_json_parser));
- php_json_scanner_init(&parser->scanner, str, str_len, options);
- parser->depth = 1;
- parser->max_depth = max_depth;
- parser->return_value = return_value;
+ return array_init(array);
}
-php_json_error_code php_json_parser_error_code(php_json_parser *parser)
+static int php_json_parser_array_append(php_json_parser *parser, zval *array, zval *zvalue)
{
- return parser->scanner.errcode;
+ zend_hash_next_index_insert(Z_ARRVAL_P(array), zvalue);
+ return SUCCESS;
}
-void php_json_parser_object_init(php_json_parser *parser, zval *object)
+static int php_json_parser_object_create(php_json_parser *parser, zval *object)
{
if (parser->scanner.options & PHP_JSON_OBJECT_AS_ARRAY) {
- array_init(object);
+ return array_init(object);
} else {
- object_init(object);
+ return object_init(object);
}
}
-int php_json_parser_object_update(php_json_parser *parser, zval *object, zend_string *key, zval *zvalue)
+static int php_json_parser_object_update(php_json_parser *parser, zval *object, zend_string *key, zval *zvalue)
{
/* if JSON_OBJECT_AS_ARRAY is set */
if (Z_TYPE_P(object) == IS_ARRAY) {
@@ -1880,7 +1892,7 @@ int php_json_parser_object_update(php_json_parser *parser, zval *object, zend_st
return FAILURE;
}
ZVAL_NEW_STR(&zkey, key);
- zend_std_write_property(object, &zkey, zvalue, NULL);
+ zend_std_write_property(object, &zkey, zvalue, NULL);
if (Z_REFCOUNTED_P(zvalue)) {
Z_DELREF_P(zvalue);
@@ -1891,26 +1903,71 @@ int php_json_parser_object_update(php_json_parser *parser, zval *object, zend_st
return SUCCESS;
}
-void php_json_parser_array_init(zval *array)
-{
- array_init(array);
-}
-
-void php_json_parser_array_append(zval *array, zval *zvalue)
-{
- zend_hash_next_index_insert(Z_ARRVAL_P(array), zvalue);
-}
-
-int php_json_yylex(union YYSTYPE *value, php_json_parser *parser)
+static int php_json_yylex(union YYSTYPE *value, php_json_parser *parser)
{
int token = php_json_scan(&parser->scanner);
value->value = parser->scanner.value;
return token;
}
-void php_json_yyerror(php_json_parser *parser, char const *msg)
+static void php_json_yyerror(php_json_parser *parser, char const *msg)
{
if (!parser->scanner.errcode) {
parser->scanner.errcode = PHP_JSON_ERROR_SYNTAX;
}
}
+
+PHP_JSON_API php_json_error_code php_json_parser_error_code(const php_json_parser *parser)
+{
+ return parser->scanner.errcode;
+}
+
+static const php_json_parser_methods default_parser_methods =
+{
+ php_json_parser_array_create,
+ php_json_parser_array_append,
+ NULL,
+ NULL,
+ php_json_parser_object_create,
+ php_json_parser_object_update,
+ NULL,
+ NULL,
+};
+
+PHP_JSON_API void php_json_parser_init_ex(php_json_parser *parser,
+ zval *return_value,
+ char *str,
+ size_t str_len,
+ int options,
+ int max_depth,
+ const php_json_parser_methods *parser_methods)
+{
+ memset(parser, 0, sizeof(php_json_parser));
+ php_json_scanner_init(&parser->scanner, str, str_len, options);
+ parser->depth = 1;
+ parser->max_depth = max_depth;
+ parser->return_value = return_value;
+ memcpy(&parser->methods, parser_methods, sizeof(php_json_parser_methods));
+}
+
+PHP_JSON_API void php_json_parser_init(php_json_parser *parser,
+ zval *return_value,
+ char *str,
+ size_t str_len,
+ int options,
+ int max_depth)
+{
+ php_json_parser_init_ex(
+ parser,
+ return_value,
+ str,
+ str_len,
+ options,
+ max_depth,
+ &default_parser_methods);
+}
+
+PHP_JSON_API int php_json_parse(php_json_parser *parser)
+{
+ return php_json_yyparse(parser);
+}
diff --git a/ext/json/json_parser.y b/ext/json/json_parser.y
index 2f37641c0c..fafe75cbf3 100644
--- a/ext/json/json_parser.y
+++ b/ext/json/json_parser.y
@@ -36,6 +36,14 @@ int json_yydebug = 1;
#define PHP_JSON_USE_1(uvr, uv1) PHP_JSON_USE(uvr); PHP_JSON_USE(uv1)
#define PHP_JSON_USE_2(uvr, uv1, uv2) PHP_JSON_USE(uvr); PHP_JSON_USE(uv1); PHP_JSON_USE(uv2)
+#define PHP_JSON_DEPTH_DEC --parser->depth
+#define PHP_JSON_DEPTH_INC \
+ if (parser->max_depth && parser->depth >= parser->max_depth) { \
+ parser->scanner.errcode = PHP_JSON_ERROR_DEPTH; \
+ YYERROR; \
+ } \
+ ++parser->depth
+
}
%pure-parser
@@ -70,20 +78,9 @@ int json_yydebug = 1;
%destructor { zend_string_release($$.key); zval_dtor(&$$.val); } <pair>
%code {
-int php_json_yylex(union YYSTYPE *value, php_json_parser *parser);
-void php_json_yyerror(php_json_parser *parser, char const *msg);
-void php_json_parser_object_init(php_json_parser *parser, zval *object);
-int php_json_parser_object_update(php_json_parser *parser, zval *object, zend_string *key, zval *zvalue);
-void php_json_parser_array_init(zval *object);
-void php_json_parser_array_append(zval *array, zval *zvalue);
+static int php_json_yylex(union YYSTYPE *value, php_json_parser *parser);
+static void php_json_yyerror(php_json_parser *parser, char const *msg);
-#define PHP_JSON_DEPTH_DEC --parser->depth
-#define PHP_JSON_DEPTH_INC \
- if (parser->max_depth && parser->depth >= parser->max_depth) { \
- parser->scanner.errcode = PHP_JSON_ERROR_DEPTH; \
- YYERROR; \
- } \
- ++parser->depth
}
%% /* Rules */
@@ -102,10 +99,20 @@ start:
;
object:
- '{' { PHP_JSON_DEPTH_INC; } members object_end
+ '{'
{
+ PHP_JSON_DEPTH_INC;
+ if (parser->methods.object_start && FAILURE == parser->methods.object_start(parser)) {
+ YYERROR;
+ }
+ }
+ members object_end
+ {
+ ZVAL_COPY_VALUE(&$$, &$3);
PHP_JSON_DEPTH_DEC;
- $$ = $3;
+ if (parser->methods.object_end && FAILURE == parser->methods.object_end(parser, &$$)) {
+ YYERROR;
+ }
}
;
@@ -121,7 +128,7 @@ object_end:
members:
/* empty */
{
- php_json_parser_object_init(parser, &$$);
+ parser->methods.object_create(parser, &$$);
}
| member
;
@@ -129,14 +136,16 @@ members:
member:
pair
{
- php_json_parser_object_init(parser, &$$);
- if (php_json_parser_object_update(parser, &$$, $1.key, &$1.val) == FAILURE)
+ parser->methods.object_create(parser, &$$);
+ if (parser->methods.object_update(parser, &$$, $1.key, &$1.val) == FAILURE) {
YYERROR;
+ }
}
| member ',' pair
{
- if (php_json_parser_object_update(parser, &$1, $3.key, &$3.val) == FAILURE)
+ if (parser->methods.object_update(parser, &$1, $3.key, &$3.val) == FAILURE) {
YYERROR;
+ }
ZVAL_COPY_VALUE(&$$, &$1);
}
| member errlex
@@ -158,10 +167,20 @@ pair:
;
array:
- '[' { PHP_JSON_DEPTH_INC; } elements array_end
+ '['
+ {
+ PHP_JSON_DEPTH_INC;
+ if (parser->methods.array_start && FAILURE == parser->methods.array_start(parser)) {
+ YYERROR;
+ }
+ }
+ elements array_end
{
- PHP_JSON_DEPTH_DEC;
ZVAL_COPY_VALUE(&$$, &$3);
+ PHP_JSON_DEPTH_DEC;
+ if (parser->methods.array_end && FAILURE == parser->methods.array_end(parser, &$$)) {
+ YYERROR;
+ }
}
;
@@ -177,7 +196,7 @@ array_end:
elements:
/* empty */
{
- php_json_parser_array_init(&$$);
+ parser->methods.array_create(parser, &$$);
}
| element
;
@@ -185,12 +204,12 @@ elements:
element:
value
{
- php_json_parser_array_init(&$$);
- php_json_parser_array_append(&$$, &$1);
+ parser->methods.array_create(parser, &$$);
+ parser->methods.array_append(parser, &$$, &$1);
}
| element ',' value
{
- php_json_parser_array_append(&$1, &$3);
+ parser->methods.array_append(parser, &$1, &$3);
ZVAL_COPY_VALUE(&$$, &$1);
}
| element errlex
@@ -224,33 +243,30 @@ errlex:
YYERROR;
}
;
-
+
%% /* Functions */
-void php_json_parser_init(php_json_parser *parser, zval *return_value, char *str, size_t str_len, int options, int max_depth)
+static int php_json_parser_array_create(php_json_parser *parser, zval *array)
{
- memset(parser, 0, sizeof(php_json_parser));
- php_json_scanner_init(&parser->scanner, str, str_len, options);
- parser->depth = 1;
- parser->max_depth = max_depth;
- parser->return_value = return_value;
+ return array_init(array);
}
-php_json_error_code php_json_parser_error_code(php_json_parser *parser)
+static int php_json_parser_array_append(php_json_parser *parser, zval *array, zval *zvalue)
{
- return parser->scanner.errcode;
+ zend_hash_next_index_insert(Z_ARRVAL_P(array), zvalue);
+ return SUCCESS;
}
-void php_json_parser_object_init(php_json_parser *parser, zval *object)
+static int php_json_parser_object_create(php_json_parser *parser, zval *object)
{
if (parser->scanner.options & PHP_JSON_OBJECT_AS_ARRAY) {
- array_init(object);
+ return array_init(object);
} else {
- object_init(object);
+ return object_init(object);
}
}
-int php_json_parser_object_update(php_json_parser *parser, zval *object, zend_string *key, zval *zvalue)
+static int php_json_parser_object_update(php_json_parser *parser, zval *object, zend_string *key, zval *zvalue)
{
/* if JSON_OBJECT_AS_ARRAY is set */
if (Z_TYPE_P(object) == IS_ARRAY) {
@@ -268,7 +284,7 @@ int php_json_parser_object_update(php_json_parser *parser, zval *object, zend_st
return FAILURE;
}
ZVAL_NEW_STR(&zkey, key);
- zend_std_write_property(object, &zkey, zvalue, NULL);
+ zend_std_write_property(object, &zkey, zvalue, NULL);
if (Z_REFCOUNTED_P(zvalue)) {
Z_DELREF_P(zvalue);
@@ -279,26 +295,71 @@ int php_json_parser_object_update(php_json_parser *parser, zval *object, zend_st
return SUCCESS;
}
-void php_json_parser_array_init(zval *array)
-{
- array_init(array);
-}
-
-void php_json_parser_array_append(zval *array, zval *zvalue)
-{
- zend_hash_next_index_insert(Z_ARRVAL_P(array), zvalue);
-}
-
-int php_json_yylex(union YYSTYPE *value, php_json_parser *parser)
+static int php_json_yylex(union YYSTYPE *value, php_json_parser *parser)
{
int token = php_json_scan(&parser->scanner);
value->value = parser->scanner.value;
return token;
}
-void php_json_yyerror(php_json_parser *parser, char const *msg)
+static void php_json_yyerror(php_json_parser *parser, char const *msg)
{
if (!parser->scanner.errcode) {
parser->scanner.errcode = PHP_JSON_ERROR_SYNTAX;
}
}
+
+PHP_JSON_API php_json_error_code php_json_parser_error_code(const php_json_parser *parser)
+{
+ return parser->scanner.errcode;
+}
+
+static const php_json_parser_methods default_parser_methods =
+{
+ php_json_parser_array_create,
+ php_json_parser_array_append,
+ NULL,
+ NULL,
+ php_json_parser_object_create,
+ php_json_parser_object_update,
+ NULL,
+ NULL,
+};
+
+PHP_JSON_API void php_json_parser_init_ex(php_json_parser *parser,
+ zval *return_value,
+ char *str,
+ size_t str_len,
+ int options,
+ int max_depth,
+ const php_json_parser_methods *parser_methods)
+{
+ memset(parser, 0, sizeof(php_json_parser));
+ php_json_scanner_init(&parser->scanner, str, str_len, options);
+ parser->depth = 1;
+ parser->max_depth = max_depth;
+ parser->return_value = return_value;
+ memcpy(&parser->methods, parser_methods, sizeof(php_json_parser_methods));
+}
+
+PHP_JSON_API void php_json_parser_init(php_json_parser *parser,
+ zval *return_value,
+ char *str,
+ size_t str_len,
+ int options,
+ int max_depth)
+{
+ php_json_parser_init_ex(
+ parser,
+ return_value,
+ str,
+ str_len,
+ options,
+ max_depth,
+ &default_parser_methods);
+}
+
+PHP_JSON_API int php_json_parse(php_json_parser *parser)
+{
+ return php_json_yyparse(parser);
+}
diff --git a/ext/json/php_json.h b/ext/json/php_json.h
index 7bcca27335..d1fabd1e6b 100644
--- a/ext/json/php_json.h
+++ b/ext/json/php_json.h
@@ -67,6 +67,7 @@ typedef enum {
#define PHP_JSON_UNESCAPED_UNICODE (1<<8)
#define PHP_JSON_PARTIAL_OUTPUT_ON_ERROR (1<<9)
#define PHP_JSON_PRESERVE_ZERO_FRACTION (1<<10)
+#define PHP_JSON_UNESCAPED_LINE_TERMINATORS (1<<11)
/* json_decode() options */
#define PHP_JSON_OBJECT_AS_ARRAY (1<<0)
diff --git a/ext/json/php_json_parser.h b/ext/json/php_json_parser.h
index 6964ef8e8e..f817a2da70 100644
--- a/ext/json/php_json_parser.h
+++ b/ext/json/php_json_parser.h
@@ -22,16 +22,64 @@
#include "php.h"
#include "php_json_scanner.h"
-typedef struct _php_json_parser {
+typedef struct _php_json_parser php_json_parser;
+
+typedef int (*php_json_parser_func_array_create_t)(
+ php_json_parser *parser, zval *array);
+typedef int (*php_json_parser_func_array_append_t)(
+ php_json_parser *parser, zval *array, zval *zvalue);
+typedef int (*php_json_parser_func_array_start_t)(
+ php_json_parser *parser);
+typedef int (*php_json_parser_func_array_end_t)(
+ php_json_parser *parser, zval *object);
+typedef int (*php_json_parser_func_object_create_t)(
+ php_json_parser *parser, zval *object);
+typedef int (*php_json_parser_func_object_update_t)(
+ php_json_parser *parser, zval *object, zend_string *key, zval *zvalue);
+typedef int (*php_json_parser_func_object_start_t)(
+ php_json_parser *parser);
+typedef int (*php_json_parser_func_object_end_t)(
+ php_json_parser *parser, zval *object);
+
+typedef struct _php_json_parser_methods {
+ php_json_parser_func_array_create_t array_create;
+ php_json_parser_func_array_append_t array_append;
+ php_json_parser_func_array_start_t array_start;
+ php_json_parser_func_array_end_t array_end;
+ php_json_parser_func_object_create_t object_create;
+ php_json_parser_func_object_update_t object_update;
+ php_json_parser_func_object_start_t object_start;
+ php_json_parser_func_object_end_t object_end;
+} php_json_parser_methods;
+
+struct _php_json_parser {
php_json_scanner scanner;
zval *return_value;
- int depth;
+ unsigned int depth;
int max_depth;
-} php_json_parser;
+ php_json_parser_methods methods;
+};
+
+PHP_JSON_API void php_json_parser_init_ex(
+ php_json_parser *parser,
+ zval *return_value,
+ char *str,
+ size_t str_len,
+ int options,
+ int max_depth,
+ const php_json_parser_methods *methods);
+
+PHP_JSON_API void php_json_parser_init(
+ php_json_parser *parser,
+ zval *return_value,
+ char *str,
+ size_t str_len,
+ int options,
+ int max_depth);
-void php_json_parser_init(php_json_parser *parser, zval *return_value, char *str, size_t str_len, int options, int max_depth);
+PHP_JSON_API php_json_error_code php_json_parser_error_code(const php_json_parser *parser);
-php_json_error_code php_json_parser_error_code(php_json_parser *parser);
+PHP_JSON_API int php_json_parse(php_json_parser *parser);
int php_json_yyparse(php_json_parser *parser);
diff --git a/ext/json/tests/json_encode_u2028_u2029.phpt b/ext/json/tests/json_encode_u2028_u2029.phpt
new file mode 100644
index 0000000000..4b87e9b307
--- /dev/null
+++ b/ext/json/tests/json_encode_u2028_u2029.phpt
@@ -0,0 +1,36 @@
+--TEST--
+json_encode() tests for U+2028, U+2029
+--SKIPIF--
+<?php if (!extension_loaded("json")) print "skip"; ?>
+--FILE--
+<?php
+var_dump(json_encode(array("a\xC3\xA1b")));
+var_dump(json_encode(array("a\xC3\xA1b"), JSON_UNESCAPED_UNICODE));
+var_dump(json_encode("a\xE2\x80\xA7b"));
+var_dump(json_encode("a\xE2\x80\xA7b", JSON_UNESCAPED_UNICODE));
+var_dump(json_encode("a\xE2\x80\xA8b"));
+var_dump(json_encode("a\xE2\x80\xA8b", JSON_UNESCAPED_UNICODE));
+var_dump(json_encode("a\xE2\x80\xA8b", JSON_UNESCAPED_LINE_TERMINATORS));
+var_dump(json_encode("a\xE2\x80\xA8b", JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_LINE_TERMINATORS));
+var_dump(json_encode("a\xE2\x80\xA9b"));
+var_dump(json_encode("a\xE2\x80\xA9b", JSON_UNESCAPED_UNICODE));
+var_dump(json_encode("a\xE2\x80\xA9b", JSON_UNESCAPED_LINE_TERMINATORS));
+var_dump(json_encode("a\xE2\x80\xA9b", JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_LINE_TERMINATORS));
+var_dump(json_encode("a\xE2\x80\xAAb"));
+var_dump(json_encode("a\xE2\x80\xAAb", JSON_UNESCAPED_UNICODE));
+?>
+--EXPECT--
+string(12) "["a\u00e1b"]"
+string(8) "["aáb"]"
+string(10) ""a\u2027b""
+string(7) ""a‧b""
+string(10) ""a\u2028b""
+string(10) ""a\u2028b""
+string(10) ""a\u2028b""
+string(7) ""a
b""
+string(10) ""a\u2029b""
+string(10) ""a\u2029b""
+string(10) ""a\u2029b""
+string(7) ""a
b""
+string(10) ""a\u202ab""
+string(7) ""a‪b""
diff --git a/ext/libxml/tests/libxml_set_external_entity_loader_error1.phpt b/ext/libxml/tests/libxml_set_external_entity_loader_error1.phpt
index 5ed079d8dd..7f1adde613 100644
--- a/ext/libxml/tests/libxml_set_external_entity_loader_error1.phpt
+++ b/ext/libxml/tests/libxml_set_external_entity_loader_error1.phpt
@@ -17,7 +17,11 @@ var_dump(libxml_set_external_entity_loader());
var_dump(libxml_set_external_entity_loader(function() {}, 2));
var_dump(libxml_set_external_entity_loader(function($a, $b, $c, $d) {}));
-var_dump($dd->validate());
+try {
+ var_dump($dd->validate());
+} catch (Throwable $e) {
+ echo "Exception: " . $e->getMessage() . "\n";
+}
echo "Done.\n";
@@ -32,8 +36,6 @@ Warning: libxml_set_external_entity_loader() expects exactly 1 parameter, 2 give
NULL
bool(true)
-Warning: Missing argument 4 for {closure}() in %s on line %d
-
Warning: DOMDocument::validate(): Could not load the external subset "http://example.com/foobar" in %s on line %d
-bool(false)
+Exception: Too few arguments to function {closure}(), 3 passed and exactly 4 expected
Done.
diff --git a/ext/mbstring/mbstring.c b/ext/mbstring/mbstring.c
index 9968a1dc51..423f357eae 100644
--- a/ext/mbstring/mbstring.c
+++ b/ext/mbstring/mbstring.c
@@ -2097,8 +2097,13 @@ PHP_FUNCTION(mb_parse_str)
detected = _php_mb_encoding_handler_ex(&info, track_vars_array, encstr);
} else {
zval tmp;
- zend_array *symbol_table = zend_rebuild_symbol_table();
+ zend_array *symbol_table;
+ if (zend_forbid_dynamic_call("mb_parse_str() with a single argument") == FAILURE) {
+ efree(encstr);
+ return;
+ }
+ symbol_table = zend_rebuild_symbol_table();
ZVAL_ARR(&tmp, symbol_table);
detected = _php_mb_encoding_handler_ex(&info, &tmp, encstr);
}
@@ -2262,7 +2267,7 @@ PHP_FUNCTION(mb_strlen)
PHP_FUNCTION(mb_strpos)
{
int n, reverse = 0;
- zend_long offset = 0;
+ zend_long offset = 0, slen;
mbfl_string haystack, needle;
char *enc_name = NULL;
size_t enc_name_len, haystack_len, needle_len;
@@ -2297,7 +2302,11 @@ PHP_FUNCTION(mb_strpos)
}
}
- if (offset < 0 || offset > mbfl_strlen(&haystack)) {
+ slen = mbfl_strlen(&haystack);
+ if (offset < 0) {
+ offset += slen;
+ }
+ if (offset < 0 || offset > slen) {
php_error_docref(NULL, E_WARNING, "Offset not contained in string");
RETURN_FALSE;
}
@@ -3053,7 +3062,7 @@ PHP_FUNCTION(mb_strwidth)
PHP_FUNCTION(mb_strimwidth)
{
char *str, *trimmarker = NULL, *encoding = NULL;
- zend_long from, width;
+ zend_long from, width, swidth;
size_t str_len, trimmarker_len, encoding_len;
mbfl_string string, result, marker, *ret;
@@ -3081,13 +3090,25 @@ PHP_FUNCTION(mb_strimwidth)
string.val = (unsigned char *)str;
string.len = str_len;
+ if ((from < 0) || (width < 0)) {
+ swidth = mbfl_strwidth(&string);
+ }
+
+ if (from < 0) {
+ from += swidth;
+ }
+
if (from < 0 || (size_t)from > str_len) {
php_error_docref(NULL, E_WARNING, "Start position is out of range");
RETURN_FALSE;
}
if (width < 0) {
- php_error_docref(NULL, E_WARNING, "Width is negative value");
+ width = swidth + width - from;
+ }
+
+ if (width < 0) {
+ php_error_docref(NULL, E_WARNING, "Width is out of range");
RETURN_FALSE;
}
@@ -3427,6 +3448,10 @@ PHP_FUNCTION(mb_list_encodings)
const mbfl_encoding *encoding;
int i;
+ if (zend_parse_parameters_none() == FAILURE) {
+ return;
+ }
+
array_init(return_value);
i = 0;
encodings = mbfl_get_supported_encodings();
@@ -4865,6 +4890,9 @@ MBSTRING_API int php_mb_stripos(int mode, const char *old_haystack, unsigned int
break;
}
} else {
+ if (offset < 0) {
+ offset += (long)haystack_char_len;
+ }
if (offset < 0 || offset > haystack_char_len) {
php_error_docref(NULL, E_WARNING, "Offset not contained in string");
break;
diff --git a/ext/mbstring/php_mbregex.c b/ext/mbstring/php_mbregex.c
index 73c94da5e9..0a3be4c179 100644
--- a/ext/mbstring/php_mbregex.c
+++ b/ext/mbstring/php_mbregex.c
@@ -459,8 +459,12 @@ static php_mb_regex_t *php_mbregex_compile_pattern(const char *pattern, int patl
retval = NULL;
goto out;
}
+ if (rc == MBREX(search_re)) {
+ /* reuse the new rc? see bug #72399 */
+ MBREX(search_re) = NULL;
+ }
zend_hash_str_update_ptr(&MBREX(ht_rc), (char *)pattern, patlen, retval);
- } else if (rc) {
+ } else {
retval = rc;
}
out:
@@ -807,7 +811,7 @@ static void _php_mb_regex_ereg_replace_exec(INTERNAL_FUNCTION_PARAMETERS, OnigOp
OnigUChar *pos;
OnigUChar *string_lim;
char *description = NULL;
- char pat_buf[4];
+ char pat_buf[6];
const mbfl_encoding *enc;
@@ -860,6 +864,8 @@ static void _php_mb_regex_ereg_replace_exec(INTERNAL_FUNCTION_PARAMETERS, OnigOp
pat_buf[1] = '\0';
pat_buf[2] = '\0';
pat_buf[3] = '\0';
+ pat_buf[4] = '\0';
+ pat_buf[5] = '\0';
arg_pattern = pat_buf;
arg_pattern_len = 1;
@@ -1406,6 +1412,11 @@ PHP_FUNCTION(mb_ereg_search_setpos)
return;
}
+ /* Accept negative position if length of search string can be determined */
+ if ((position < 0) && (!Z_ISUNDEF(MBREX(search_str))) && (Z_TYPE(MBREX(search_str)) == IS_STRING)) {
+ position += Z_STRLEN(MBREX(search_str));
+ }
+
if (position < 0 || (!Z_ISUNDEF(MBREX(search_str)) && Z_TYPE(MBREX(search_str)) == IS_STRING && (size_t)position >= Z_STRLEN(MBREX(search_str)))) {
php_error_docref(NULL, E_WARNING, "Position is out of range");
MBREX(search_pos) = 0;
diff --git a/ext/mbstring/tests/bug45923.phpt b/ext/mbstring/tests/bug45923.phpt
index 2d184ab019..41ffd70924 100644
--- a/ext/mbstring/tests/bug45923.phpt
+++ b/ext/mbstring/tests/bug45923.phpt
@@ -6,197 +6,241 @@ Bug #45923 (mb_st[r]ripos() offset not handled correctly)
mbstring.internal_encoding=UTF-8
--FILE--
<?php
-var_dump(strpos("abc abc abc", "abc", 0));
-var_dump(strpos("abc abc abc", "abc", 3));
-var_dump(strpos("abc abc abc", "abc", 6));
-var_dump(strpos("abc abc abc", "abc", 9));
-var_dump(strpos("abc abc abc", "abc", 11));
-var_dump(strpos("abc abc abc", "abc", 12));
-var_dump(strpos("abc abc abc", "abc", -1));
-var_dump(strpos("abc abc abc", "abc", -3));
-var_dump(strpos("abc abc abc", "abc", -6));
-
-var_dump(mb_strpos("â—â—‹â—† â—â—‹â—† â—â—‹â—†", "â—â—‹â—†", 0));
-var_dump(mb_strpos("â—â—‹â—† â—â—‹â—† â—â—‹â—†", "â—â—‹â—†", 3));
-var_dump(mb_strpos("â—â—‹â—† â—â—‹â—† â—â—‹â—†", "â—â—‹â—†", 6));
-var_dump(mb_strpos("â—â—‹â—† â—â—‹â—† â—â—‹â—†", "â—â—‹â—†", 9));
-var_dump(mb_strpos("â—â—‹â—† â—â—‹â—† â—â—‹â—†", "â—â—‹â—†", 11));
-var_dump(mb_strpos("â—â—‹â—† â—â—‹â—† â—â—‹â—†", "â—â—‹â—†", 12));
-var_dump(mb_strpos("â—â—‹â—† â—â—‹â—† â—â—‹â—†", "â—â—‹â—†", -1));
-var_dump(mb_strpos("â—â—‹â—† â—â—‹â—† â—â—‹â—†", "â—â—‹â—†", -3));
-var_dump(mb_strpos("â—â—‹â—† â—â—‹â—† â—â—‹â—†", "â—â—‹â—†", -6));
-
-var_dump(stripos("abc abc abc", "abc", 0));
-var_dump(stripos("abc abc abc", "abc", 3));
-var_dump(stripos("abc abc abc", "abc", 6));
-var_dump(stripos("abc abc abc", "abc", 9));
-var_dump(stripos("abc abc abc", "abc", 11));
-var_dump(stripos("abc abc abc", "abc", 12));
-var_dump(stripos("abc abc abc", "abc", -1));
-var_dump(stripos("abc abc abc", "abc", -3));
-var_dump(stripos("abc abc abc", "abc", -6));
-
-var_dump(mb_stripos("â—â—‹â—† â—â—‹â—† â—â—‹â—†", "â—â—‹â—†", 0));
-var_dump(mb_stripos("â—â—‹â—† â—â—‹â—† â—â—‹â—†", "â—â—‹â—†", 3));
-var_dump(mb_stripos("â—â—‹â—† â—â—‹â—† â—â—‹â—†", "â—â—‹â—†", 6));
-var_dump(mb_stripos("â—â—‹â—† â—â—‹â—† â—â—‹â—†", "â—â—‹â—†", 9));
-var_dump(mb_stripos("â—â—‹â—† â—â—‹â—† â—â—‹â—†", "â—â—‹â—†", 11));
-var_dump(mb_stripos("â—â—‹â—† â—â—‹â—† â—â—‹â—†", "â—â—‹â—†", 12));
-var_dump(mb_stripos("â—â—‹â—† â—â—‹â—† â—â—‹â—†", "â—â—‹â—†", -1));
-var_dump(mb_stripos("â—â—‹â—† â—â—‹â—† â—â—‹â—†", "â—â—‹â—†", -3));
-var_dump(mb_stripos("â—â—‹â—† â—â—‹â—† â—â—‹â—†", "â—â—‹â—†", -6));
-
-var_dump(strrpos("abc abc abc", "abc", 0));
-var_dump(strrpos("abc abc abc", "abc", 3));
-var_dump(strrpos("abc abc abc", "abc", 6));
-var_dump(strrpos("abc abc abc", "abc", 9));
-var_dump(strrpos("abc abc abc", "abc", 11));
-var_dump(strrpos("abc abc abc", "abc", 12));
-var_dump(strrpos("abc abc abc", "abc", -1));
-var_dump(strrpos("abc abc abc", "abc", -3));
-var_dump(strrpos("abc abc abc", "abc", -6));
-
-var_dump(mb_strrpos("â—â—‹â—† â—â—‹â—† â—â—‹â—†", "â—â—‹â—†", 0));
-var_dump(mb_strrpos("â—â—‹â—† â—â—‹â—† â—â—‹â—†", "â—â—‹â—†", 3));
-var_dump(mb_strrpos("â—â—‹â—† â—â—‹â—† â—â—‹â—†", "â—â—‹â—†", 6));
-var_dump(mb_strrpos("â—â—‹â—† â—â—‹â—† â—â—‹â—†", "â—â—‹â—†", 9));
-var_dump(mb_strrpos("â—â—‹â—† â—â—‹â—† â—â—‹â—†", "â—â—‹â—†", 11));
-var_dump(mb_strrpos("â—â—‹â—† â—â—‹â—† â—â—‹â—†", "â—â—‹â—†", 12));
-var_dump(mb_strrpos("â—â—‹â—† â—â—‹â—† â—â—‹â—†", "â—â—‹â—†", -1));
-var_dump(mb_strrpos("â—â—‹â—† â—â—‹â—† â—â—‹â—†", "â—â—‹â—†", -3));
-var_dump(mb_strrpos("â—â—‹â—† â—â—‹â—† â—â—‹â—†", "â—â—‹â—†", -6));
-
-var_dump(strripos("abc abc abc", "abc", 0));
-var_dump(strripos("abc abc abc", "abc", 3));
-var_dump(strripos("abc abc abc", "abc", 6));
-var_dump(strripos("abc abc abc", "abc", 9));
-var_dump(strripos("abc abc abc", "abc", 11));
-var_dump(strripos("abc abc abc", "abc", 12));
-var_dump(strripos("abc abc abc", "abc", -1));
-var_dump(strripos("abc abc abc", "abc", -3));
-var_dump(strripos("abc abc abc", "abc", -6));
-
-var_dump(mb_strripos("â—â—‹â—† â—â—‹â—† â—â—‹â—†", "â—â—‹â—†", 0));
-var_dump(mb_strripos("â—â—‹â—† â—â—‹â—† â—â—‹â—†", "â—â—‹â—†", 3));
-var_dump(mb_strripos("â—â—‹â—† â—â—‹â—† â—â—‹â—†", "â—â—‹â—†", 6));
-var_dump(mb_strripos("â—â—‹â—† â—â—‹â—† â—â—‹â—†", "â—â—‹â—†", 9));
-var_dump(mb_strripos("â—â—‹â—† â—â—‹â—† â—â—‹â—†", "â—â—‹â—†", 11));
-var_dump(mb_strripos("â—â—‹â—† â—â—‹â—† â—â—‹â—†", "â—â—‹â—†", 12));
-var_dump(mb_strripos("â—â—‹â—† â—â—‹â—† â—â—‹â—†", "â—â—‹â—†", -1));
-var_dump(mb_strripos("â—â—‹â—† â—â—‹â—† â—â—‹â—†", "â—â—‹â—†", -3));
-var_dump(mb_strripos("â—â—‹â—† â—â—‹â—† â—â—‹â—†", "â—â—‹â—†", -6));
+function section($func, $haystack, $needle)
+{
+ echo "\n------- $func -----------\n\n";
+ foreach(array(0, 3, 6, 9, 11, 12, -1, -3, -6, -20) as $offset) {
+ echo "> Offset: $offset\n";
+ var_dump($func($haystack,$needle,$offset));
+ }
+}
+
+section('strpos' , "abc abc abc" , "abc");
+section('mb_strpos' , "â—â—‹â—† â—â—‹â—† â—â—‹â—†", "â—â—‹â—†");
+
+section('stripos' , "abc abc abc" , "abc");
+section('mb_stripos' , "â—â—‹â—† â—â—‹â—† â—â—‹â—†", "â—â—‹â—†");
+
+section('strrpos' , "abc abc abc" , "abc");
+section('mb_strrpos' , "â—â—‹â—† â—â—‹â—† â—â—‹â—†", "â—â—‹â—†");
+
+section('strripos' , "abc abc abc" , "abc");
+section('mb_strripos', "â—â—‹â—† â—â—‹â—† â—â—‹â—†", "â—â—‹â—†");
?>
--EXPECTF--
+------- strpos -----------
+
+> Offset: 0
int(0)
+> Offset: 3
int(4)
+> Offset: 6
int(8)
+> Offset: 9
bool(false)
+> Offset: 11
bool(false)
+> Offset: 12
Warning: strpos(): Offset not contained in string in %s on line %d
bool(false)
-
-Warning: strpos(): Offset not contained in string in %s on line %d
+> Offset: -1
bool(false)
+> Offset: -3
+int(8)
+> Offset: -6
+int(8)
+> Offset: -20
Warning: strpos(): Offset not contained in string in %s on line %d
bool(false)
-Warning: strpos(): Offset not contained in string in %s on line %d
-bool(false)
+------- mb_strpos -----------
+
+> Offset: 0
int(0)
+> Offset: 3
int(4)
+> Offset: 6
int(8)
+> Offset: 9
bool(false)
+> Offset: 11
bool(false)
+> Offset: 12
Warning: mb_strpos(): Offset not contained in string in %s on line %d
bool(false)
-
-Warning: mb_strpos(): Offset not contained in string in %s on line %d
+> Offset: -1
bool(false)
+> Offset: -3
+int(8)
+> Offset: -6
+int(8)
+> Offset: -20
Warning: mb_strpos(): Offset not contained in string in %s on line %d
bool(false)
-Warning: mb_strpos(): Offset not contained in string in %s on line %d
-bool(false)
+------- stripos -----------
+
+> Offset: 0
int(0)
+> Offset: 3
int(4)
+> Offset: 6
int(8)
+> Offset: 9
bool(false)
+> Offset: 11
bool(false)
+> Offset: 12
Warning: stripos(): Offset not contained in string in %s on line %d
bool(false)
-
-Warning: stripos(): Offset not contained in string in %s on line %d
+> Offset: -1
bool(false)
+> Offset: -3
+int(8)
+> Offset: -6
+int(8)
+> Offset: -20
Warning: stripos(): Offset not contained in string in %s on line %d
bool(false)
-Warning: stripos(): Offset not contained in string in %s on line %d
-bool(false)
+------- mb_stripos -----------
+
+> Offset: 0
int(0)
+> Offset: 3
int(4)
+> Offset: 6
int(8)
+> Offset: 9
bool(false)
+> Offset: 11
bool(false)
+> Offset: 12
Warning: mb_stripos(): Offset not contained in string in %s on line %d
bool(false)
-
-Warning: mb_stripos(): Offset not contained in string in %s on line %d
+> Offset: -1
bool(false)
+> Offset: -3
+int(8)
+> Offset: -6
+int(8)
+> Offset: -20
Warning: mb_stripos(): Offset not contained in string in %s on line %d
bool(false)
-Warning: mb_stripos(): Offset not contained in string in %s on line %d
-bool(false)
+------- strrpos -----------
+
+> Offset: 0
int(8)
+> Offset: 3
int(8)
+> Offset: 6
int(8)
+> Offset: 9
bool(false)
+> Offset: 11
bool(false)
+> Offset: 12
Warning: strrpos(): Offset is greater than the length of haystack string in %s on line %d
bool(false)
+> Offset: -1
int(8)
+> Offset: -3
int(8)
+> Offset: -6
int(4)
+> Offset: -20
+
+Warning: strrpos(): Offset is greater than the length of haystack string in %s on line %d
+bool(false)
+
+------- mb_strrpos -----------
+
+> Offset: 0
int(8)
+> Offset: 3
int(8)
+> Offset: 6
int(8)
+> Offset: 9
bool(false)
+> Offset: 11
bool(false)
+> Offset: 12
Warning: mb_strrpos(): Offset is greater than the length of haystack string in %s on line %d
bool(false)
+> Offset: -1
int(8)
+> Offset: -3
int(8)
+> Offset: -6
int(4)
+> Offset: -20
+
+Warning: mb_strrpos(): Offset is greater than the length of haystack string in %s on line %d
+bool(false)
+
+------- strripos -----------
+
+> Offset: 0
int(8)
+> Offset: 3
int(8)
+> Offset: 6
int(8)
+> Offset: 9
bool(false)
+> Offset: 11
bool(false)
+> Offset: 12
Warning: strripos(): Offset is greater than the length of haystack string in %s on line %d
bool(false)
+> Offset: -1
int(8)
+> Offset: -3
int(8)
+> Offset: -6
int(4)
+> Offset: -20
+
+Warning: strripos(): Offset is greater than the length of haystack string in %s on line %d
+bool(false)
+
+------- mb_strripos -----------
+
+> Offset: 0
int(8)
+> Offset: 3
int(8)
+> Offset: 6
int(8)
+> Offset: 9
bool(false)
+> Offset: 11
bool(false)
+> Offset: 12
Warning: mb_strripos(): Offset is greater than the length of haystack string in %s on line %d
bool(false)
+> Offset: -1
int(8)
+> Offset: -3
int(8)
+> Offset: -6
int(4)
+> Offset: -20
+
+Warning: mb_strripos(): Offset is greater than the length of haystack string in %s on line %d
+bool(false) \ No newline at end of file
diff --git a/ext/mbstring/tests/bug72399.phpt b/ext/mbstring/tests/bug72399.phpt
new file mode 100644
index 0000000000..ba6ffb2cb1
--- /dev/null
+++ b/ext/mbstring/tests/bug72399.phpt
@@ -0,0 +1,10 @@
+--TEST--
+Bug #72399 (Use-After-Free in MBString (search_re))
+--FILE--
+<?php
+$var5 = mbereg_search_init("","2");
+$var6 = mb_eregi_replace("2","","");
+$var13 = mbereg_search_pos();
+?>
+--EXPECTF--
+Warning: mbereg_search_pos(): No regex given in %sbug72399.php on line %d
diff --git a/ext/mbstring/tests/mb_ereg_search_setpos.phpt b/ext/mbstring/tests/mb_ereg_search_setpos.phpt
new file mode 100644
index 0000000000..ec354309f3
--- /dev/null
+++ b/ext/mbstring/tests/mb_ereg_search_setpos.phpt
@@ -0,0 +1,70 @@
+--TEST--
+mb_ereg_search_setpos() function
+--SKIPIF--
+<?php
+if (!extension_loaded('mbstring')) die('skip mbstring not enabled');
+?>
+--FILE--
+<?php
+mb_regex_encoding('iso-8859-1');
+$test_str = 'Iñtërnâtiônàlizætiøn'; // Length = 20
+
+var_dump(mb_ereg_search_setpos(50)); // OK
+var_dump(mb_ereg_search_setpos(-1)); // Error
+
+mb_ereg_search_init($test_str);
+
+$positions = array( 5, 19, 20, 25, 0, -5, -20, -30);
+foreach($positions as $pos) {
+ echo("\n* Position: $pos :\n");
+ var_dump(mb_ereg_search_setpos($pos));
+ var_dump(mb_ereg_search_getpos());
+}
+?>
+==DONE==
+--EXPECTF--
+bool(true)
+
+Warning: mb_ereg_search_setpos(): Position is out of range in %s on line %d
+bool(false)
+
+* Position: 5 :
+bool(true)
+int(5)
+
+* Position: 19 :
+bool(true)
+int(19)
+
+* Position: 20 :
+
+Warning: mb_ereg_search_setpos(): Position is out of range in %s on line %d
+bool(false)
+int(0)
+
+* Position: 25 :
+
+Warning: mb_ereg_search_setpos(): Position is out of range in %s on line %d
+bool(false)
+int(0)
+
+* Position: 0 :
+bool(true)
+int(0)
+
+* Position: -5 :
+bool(true)
+int(15)
+
+* Position: -20 :
+bool(true)
+int(0)
+
+* Position: -30 :
+
+Warning: mb_ereg_search_setpos(): Position is out of range in %s on line %d
+bool(false)
+int(0)
+==DONE==
+
+
diff --git a/ext/mbstring/tests/mb_strimwidth.phpt b/ext/mbstring/tests/mb_strimwidth.phpt
index 82780d6756..f257604c6b 100644
--- a/ext/mbstring/tests/mb_strimwidth.phpt
+++ b/ext/mbstring/tests/mb_strimwidth.phpt
@@ -14,30 +14,44 @@ include_once('common.inc');
// EUC-JP
$euc_jp = '0123¤³¤Îʸ»úÎó¤ÏÆüËܸì¤Ç¤¹¡£EUC-JP¤ò»È¤Ã¤Æ¤¤¤Þ¤¹¡£ÆüËܸì¤ÏÌÌÅݽ­¤¤¡£';
+print "String width: ".mb_strwidth($euc_jp,'EUC-JP')."\n";
+
print "1: ". mb_strimwidth($euc_jp, 0, 15,'...','EUC-JP') . "\n";
print "2: ". mb_strimwidth($euc_jp, 0, 100,'...','EUC-JP') . "\n";
print "3: ". mb_strimwidth($euc_jp, 15, 100,'...','EUC-JP') . "\n";
-// Note: Did not start form -22 offset. Staring from 0.
-$str = mb_strimwidth($euc_jp,-22, 100,'...','EUC-JP');
-($str === FALSE) ? print "4 OK\n" : print "NG: $str\n";
+print "4: ". mb_strimwidth($euc_jp, -30, 5,'...','EUC-JP') . "\n";
+print "5: ". mb_strimwidth($euc_jp, 38, 5,'...','EUC-JP') . "\n";
+print "6: ". mb_strimwidth($euc_jp, 38, -25,'...','EUC-JP') . "\n";
+print "7: ". mb_strimwidth($euc_jp, -30, -25,'...','EUC-JP') . "\n";
+
+$str = mb_strimwidth($euc_jp, 0, -100,'...','EUC-JP');
+($str === FALSE) ? print "10 OK\n" : print "NG: $str\n";
-$str = mb_strimwidth($euc_jp, 100, -10,'...','EUC-JP');
-($str === FALSE) ? print "5 OK\n" : print "NG: $str\n";
+$str = mb_strimwidth($euc_jp, 100, 10,'...','EUC-JP');
+($str === FALSE) ? print "11 OK\n" : print "NG: $str\n";
$str = mb_strimwidth($euc_jp, -100, 10,'...','EUC-JP');
-($str === FALSE) ? print "6 OK\n" : print "NG: $str\n";
+($str === FALSE) ? print "12 OK\n" : print "NG: $str\n";
+
+$str = mb_strimwidth($euc_jp, -10, -12,'...','EUC-JP');
+($str === FALSE) ? print "13 OK\n" : print "NG: $str\n";
?>
--EXPECT--
+String width: 68
1: 0123¤³¤Îʸ»ú...
2: 0123¤³¤Îʸ»úÎó¤ÏÆüËܸì¤Ç¤¹¡£EUC-JP¤ò»È¤Ã¤Æ¤¤¤Þ¤¹¡£ÆüËܸì¤ÏÌÌÅݽ­¤¤¡£
3: ¡£EUC-JP¤ò»È¤Ã¤Æ¤¤¤Þ¤¹¡£ÆüËܸì¤ÏÌÌÅݽ­¤¤¡£
+4: ¡£
+5: ¡£
+6: ¡£
+7: ¡£
ERR: Warning
-4 OK
+10 OK
ERR: Warning
-5 OK
+11 OK
ERR: Warning
-6 OK
-
-
+12 OK
+ERR: Warning
+13 OK
diff --git a/ext/mbstring/tests/mb_stripos.phpt b/ext/mbstring/tests/mb_stripos.phpt
index 4ea8cfa6d6..8268872223 100644
--- a/ext/mbstring/tests/mb_stripos.phpt
+++ b/ext/mbstring/tests/mb_stripos.phpt
@@ -1,10 +1,7 @@
--TEST--
mb_stripos()
--SKIPIF--
-<?php
-extension_loaded('mbstring') or die('skip');
-function_exists('mb_stripos') or die("skip mb_stripos() is not available in this build");
-?>
+<?php extension_loaded('mbstring') or die('skip mbstring not available'); ?>
--FILE--
<?php
// TODO: Add more encodings
@@ -17,43 +14,62 @@ include_once('common.inc');
// Test string
$euc_jp = b'0123¤³¤Îʸ»úÎó¤ÏÆüËܸì¤Ç¤¹¡£EUC-JP¤ò»È¤Ã¤Æ¤¤¤Þ¤¹¡£0123ÆüËܸì¤ÏÌÌÅݽ­¤¤¡£';
+$slen = mb_strlen($euc_jp, 'EUC-JP');
+echo "String len: $slen\n";
+
// EUC-JP - With encoding parameter
mb_internal_encoding('UTF-8') or print("mb_internal_encoding() failed\n");
echo "== POSITIVE OFFSET ==\n";
-print mb_stripos($euc_jp,b'ÆüËܸì', 0, 'EUC-JP') . "\n";
+
+print mb_stripos($euc_jp, b'ÆüËܸì', 0, 'EUC-JP') . "\n";
print mb_stripos($euc_jp, b'0', 0, 'EUC-JP') . "\n";
print mb_stripos($euc_jp, 3, 0, 'EUC-JP') . "\n";
print mb_stripos($euc_jp, 0, 0, 'EUC-JP') . "\n";
-print mb_stripos($euc_jp,b'ÆüËܸì', 15, 'EUC-JP') . "\n";
+print mb_stripos($euc_jp, b'ÆüËܸì', 15, 'EUC-JP') . "\n";
print mb_stripos($euc_jp, b'0', 15, 'EUC-JP') . "\n";
print mb_stripos($euc_jp, 3, 15, 'EUC-JP') . "\n";
print mb_stripos($euc_jp, 0, 15, 'EUC-JP') . "\n";
+
// Negative offset
-// Note: PHP Warning - offset is negative.
-// Note: For offset(-15). It does not return position of latter string. (ie the same result as -50)
echo "== NEGATIVE OFFSET ==\n";
-$r = mb_stripos($euc_jp,b'ÆüËܸì', -15, 'EUC-JP');
-($r === FALSE) ? print "OK_NEGATIVE_OFFSET\n" : print "NG_NEGATIVE_OFFSET\n";
-$r = mb_stripos($euc_jp, b'0', -15, 'EUC-JP');
-($r === FALSE) ? print "OK_NEGATIVE_OFFSET\n" : print "NG_NEGATIVE_OFFSET\n";
-$r = mb_stripos($euc_jp, 3, -15, 'EUC-JP');
-($r === FALSE) ? print "OK_NEGATIVE_OFFSET\n" : print "NG_NEGATIVE_OFFSET\n";
-$r = mb_stripos($euc_jp, 0, -15, 'EUC-JP');
-($r === FALSE) ? print "OK_NEGATIVE_OFFSET\n" : print "NG_NEGATIVE_OFFSET\n";
-$r = mb_stripos($euc_jp,b'ÆüËܸì', -50, 'EUC-JP');
-($r === FALSE) ? print "OK_NEGATIVE_OFFSET\n" : print "NG_NEGATIVE_OFFSET\n";
+
+print mb_stripos($euc_jp, b'ÆüËܸì', -15, 'EUC-JP') . "\n";
+print mb_stripos($euc_jp, b'0', -15, 'EUC-JP') . "\n";
+print mb_stripos($euc_jp, 3, -15, 'EUC-JP') . "\n";
+print mb_stripos($euc_jp, 0, -15, 'EUC-JP') . "\n";
+print mb_stripos($euc_jp, 0, -43, 'EUC-JP') . "\n";
+
+
+// Invalid offset - should return false with warning
+print ("== INVALID OFFSET ==\n");
+
+$r = mb_stripos($euc_jp, b'ÆüËܸì', 44, 'EUC-JP');
+($r === FALSE) ? print "OK_INVALID_OFFSET\n" : print "NG_INVALID_OFFSET\n";
+$r = mb_stripos($euc_jp, b'ÆüËܸì', 50, 'EUC-JP');
+($r === FALSE) ? print "OK_INVALID_OFFSET\n" : print "NG_INVALID_OFFSET\n";
+$r = mb_stripos($euc_jp, b'0', 50, 'EUC-JP');
+($r === FALSE) ? print "OK_INVALID_OFFSET\n" : print "NG_INVALID_OFFSET\n";
+$r = mb_stripos($euc_jp, 3, 50, 'EUC-JP');
+($r === FALSE) ? print "OK_INVALID_OFFSET\n" : print "NG_INVALID_OFFSET\n";
+$r = mb_stripos($euc_jp, 0, 50, 'EUC-JP');
+($r === FALSE) ? print "OK_INVALID_OFFSET\n" : print "NG_INVALID_OFFSET\n";
+$r = mb_stripos($euc_jp, b'ÆüËܸì', -50, 'EUC-JP');
+($r === FALSE) ? print "OK_INVALID_OFFSET\n" : print "NG_INVALID_OFFSET\n";
$r = mb_stripos($euc_jp, b'0', -50, 'EUC-JP');
-($r === FALSE) ? print "OK_NEGATIVE_OFFSET\n" : print "NG_NEGATIVE_OFFSET\n";
+($r === FALSE) ? print "OK_INVALID_OFFSET\n" : print "NG_INVALID_OFFSET\n";
$r = mb_stripos($euc_jp, 3, -50, 'EUC-JP');
-($r === FALSE) ? print "OK_NEGATIVE_OFFSET\n" : print "NG_NEGATIVE_OFFSET\n";
+($r === FALSE) ? print "OK_INVALID_OFFSET\n" : print "NG_INVALID_OFFSET\n";
$r = mb_stripos($euc_jp, 0, -50, 'EUC-JP');
-($r === FALSE) ? print "OK_NEGATIVE_OFFSET\n" : print "NG_NEGATIVE_OFFSET\n";
+($r === FALSE) ? print "OK_INVALID_OFFSET\n" : print "NG_INVALID_OFFSET\n";
+$r = mb_stripos($euc_jp, 0, -44, 'EUC-JP');
+($r === FALSE) ? print "OK_INVALID_OFFSET\n" : print "NG_INVALID_OFFSET\n";
// Out of range - should return false
print ("== OUT OF RANGE ==\n");
-$r = mb_stripos($euc_jp,b'ÆüËܸì', 40, 'EUC-JP');
+
+$r = mb_stripos($euc_jp, b'ÆüËܸì', 40, 'EUC-JP');
($r === FALSE) ? print "OK_OUT_RANGE\n" : print "NG_OUT_RANGE\n";
$r = mb_stripos($euc_jp, b'0', 40, 'EUC-JP');
($r === FALSE) ? print "OK_OUT_RANGE\n" : print "NG_OUT_RANGE\n";
@@ -61,12 +77,19 @@ $r = mb_stripos($euc_jp, 3, 40, 'EUC-JP');
($r === FALSE) ? print "OK_OUT_RANGE\n" : print "NG_OUT_RANGE\n";
$r = mb_stripos($euc_jp, 0, 40, 'EUC-JP');
($r === FALSE) ? print "OK_OUT_RANGE\n" : print "NG_OUT_RANGE\n";
-// Note: Returned NULL string
-// echo gettype($r). ' val '. $r ."\n";
+$r = mb_stripos($euc_jp, b'ÆüËܸì', -3, 'EUC-JP');
+($r === FALSE) ? print "OK_OUT_RANGE\n" : print "NG_OUT_RANGE\n";
+$r = mb_stripos($euc_jp, b'0', -3, 'EUC-JP');
+($r === FALSE) ? print "OK_OUT_RANGE\n" : print "NG_OUT_RANGE\n";
+$r = mb_stripos($euc_jp, 3, -3, 'EUC-JP');
+($r === FALSE) ? print "OK_OUT_RANGE\n" : print "NG_OUT_RANGE\n";
+$r = mb_stripos($euc_jp, 0, -3, 'EUC-JP');
+($r === FALSE) ? print "OK_OUT_RANGE\n" : print "NG_OUT_RANGE\n";
// Non-existent
echo "== NON-EXISTENT ==\n";
+
$r = mb_stripos($euc_jp, b'´Ú¹ñ¸ì', 0, 'EUC-JP');
($r === FALSE) ? print "OK_STR\n" : print "NG_STR\n";
$r = mb_stripos($euc_jp, b"\n", 0, 'EUC-JP');
@@ -75,30 +98,32 @@ $r = mb_stripos($euc_jp, b"\n", 0, 'EUC-JP');
// EUC-JP - No encoding parameter
echo "== NO ENCODING PARAMETER ==\n";
+
mb_internal_encoding('EUC-JP') or print("mb_internal_encoding() failed\n");
-print mb_stripos($euc_jp,b'ÆüËܸì', 0) . "\n";
+print mb_stripos($euc_jp, b'ÆüËܸì', 0) . "\n";
print mb_stripos($euc_jp, b'0', 0) . "\n";
print mb_stripos($euc_jp, 3, 0) . "\n";
print mb_stripos($euc_jp, 0, 0) . "\n";
-$r = mb_stripos($euc_jp,b'´Ú¹ñ¸ì', 0);
+$r = mb_stripos($euc_jp, b'´Ú¹ñ¸ì', 0);
($r === FALSE) ? print "OK_STR\n" : print "NG_STR\n";
-$r = mb_stripos($euc_jp,b"\n", 0);
+$r = mb_stripos($euc_jp, b"\n", 0);
($r === FALSE) ? print "OK_NEWLINE\n" : print "NG_NEWLINE\n";
// EUC-JP - No offset and encoding parameter
echo "== NO OFFSET AND ENCODING PARAMETER ==\n";
+
mb_internal_encoding('EUC-JP') or print("mb_internal_encoding() failed\n");
-print mb_stripos($euc_jp,b'ÆüËܸì') . "\n";
+print mb_stripos($euc_jp, b'ÆüËܸì') . "\n";
print mb_stripos($euc_jp, b'0') . "\n";
print mb_stripos($euc_jp, 3) . "\n";
print mb_stripos($euc_jp, 0) . "\n";
-$r = mb_stripos($euc_jp,b'´Ú¹ñ¸ì');
+$r = mb_stripos($euc_jp, b'´Ú¹ñ¸ì');
($r === FALSE) ? print "OK_STR\n" : print "NG_STR\n";
-$r = mb_stripos($euc_jp,b"\n");
+$r = mb_stripos($euc_jp, b"\n");
($r === FALSE) ? print "OK_NEWLINE\n" : print "NG_NEWLINE\n";
@@ -113,11 +138,10 @@ $r = mb_stripos($euc_jp, $t_obj, 'EUC-JP');
($r === NULL) ? print("OK_OBJECT\n") : print("NG_OBJECT\n");
$r = mb_stripos($euc_jp, $t_obj, 'BAD_ENCODING');
($r === NULL) ? print("OK_BAD_ENCODING\n") : print("NG_BAD_ENCODING\n");
-
-
?>
-
+==DONE==
--EXPECT--
+String len: 43
== POSITIVE OFFSET ==
10
0
@@ -128,27 +152,41 @@ $r = mb_stripos($euc_jp, $t_obj, 'BAD_ENCODING');
33
30
== NEGATIVE OFFSET ==
+34
+30
+33
+30
+0
+== INVALID OFFSET ==
ERR: Warning
-OK_NEGATIVE_OFFSET
+OK_INVALID_OFFSET
ERR: Warning
-OK_NEGATIVE_OFFSET
+OK_INVALID_OFFSET
ERR: Warning
-OK_NEGATIVE_OFFSET
+OK_INVALID_OFFSET
ERR: Warning
-OK_NEGATIVE_OFFSET
+OK_INVALID_OFFSET
ERR: Warning
-OK_NEGATIVE_OFFSET
+OK_INVALID_OFFSET
ERR: Warning
-OK_NEGATIVE_OFFSET
+OK_INVALID_OFFSET
ERR: Warning
-OK_NEGATIVE_OFFSET
+OK_INVALID_OFFSET
ERR: Warning
-OK_NEGATIVE_OFFSET
+OK_INVALID_OFFSET
+ERR: Warning
+OK_INVALID_OFFSET
+ERR: Warning
+OK_INVALID_OFFSET
== OUT OF RANGE ==
OK_OUT_RANGE
OK_OUT_RANGE
OK_OUT_RANGE
OK_OUT_RANGE
+OK_OUT_RANGE
+OK_OUT_RANGE
+OK_OUT_RANGE
+OK_OUT_RANGE
== NON-EXISTENT ==
OK_STR
OK_NEWLINE
@@ -175,4 +213,4 @@ ERR: Warning
OK_OBJECT
ERR: Warning
OK_BAD_ENCODING
-
+==DONE==
diff --git a/ext/mbstring/tests/mb_stripos_variation3.phpt b/ext/mbstring/tests/mb_stripos_variation3.phpt
index 21a1293786..69c4a9e53b 100644
--- a/ext/mbstring/tests/mb_stripos_variation3.phpt
+++ b/ext/mbstring/tests/mb_stripos_variation3.phpt
@@ -8,10 +8,9 @@ if (PHP_INT_SIZE != 8) die('skip 64-bit only');
?>
--FILE--
<?php
-/* Prototype : int mb_stripos(string haystack, string needle [, int offset [, string encoding]])
- * Description: Finds position of first occurrence of a string within another, case insensitive
+/* Prototype : int mb_stripos(string $haystack, string $needle [, int $offset [, string $encoding]])
+ * Description: Find position of first occurrence of a string within another, case insensitive
* Source code: ext/mbstring/mbstring.c
- * Alias to functions:
*/
/*
@@ -52,45 +51,47 @@ $inputs = array(
/*1*/ 0,
1,
12345,
+ -5,
-2345,
// float data
-/*5*/ 10.5,
- -10.5,
+/*6*/ 10.5,
+ -5.5,
+ -100.5,
12.3456789000e10,
12.3456789000E-10,
.5,
// null data
-/*10*/ NULL,
+/*12*/ NULL,
null,
// boolean data
-/*12*/ true,
+/*14*/ true,
false,
TRUE,
FALSE,
// empty data
-/*16*/ "",
+/*18*/ "",
'',
// string data
-/*18*/ "string",
+/*20*/ "string",
'string',
$heredoc,
// object data
-/*21*/ new classA(),
+/*23*/ new classA(),
// undefined data
-/*22*/ @$undefined_var,
+/*24*/ @$undefined_var,
// unset data
-/*23*/ @$unset_var,
+/*25*/ @$unset_var,
// resource variable
-/*24*/ $fp
+/*26*/ $fp
);
// loop through each element of $inputs to check the behavior of mb_stripos()
@@ -120,28 +121,28 @@ Warning: mb_stripos(): Offset not contained in string in %s on line %d
bool(false)
-- Iteration 4 --
-
-Warning: mb_stripos(): Offset not contained in string in %s on line %d
-bool(false)
+int(8)
-- Iteration 5 --
+
+Warning: mb_stripos(): Offset not contained in string in %s on line %d
bool(false)
-- Iteration 6 --
-
-Warning: mb_stripos(): Offset not contained in string in %s on line %d
bool(false)
-- Iteration 7 --
+int(8)
+
+-- Iteration 8 --
Warning: mb_stripos(): Offset not contained in string in %s on line %d
bool(false)
--- Iteration 8 --
-int(8)
-
-- Iteration 9 --
-int(8)
+
+Warning: mb_stripos(): Offset not contained in string in %s on line %d
+bool(false)
-- Iteration 10 --
int(8)
@@ -162,42 +163,48 @@ int(8)
int(8)
-- Iteration 16 --
+int(8)
+
+-- Iteration 17 --
+int(8)
+
+-- Iteration 18 --
Warning: mb_stripos() expects parameter 3 to be integer, string given in %s on line %d
NULL
--- Iteration 17 --
+-- Iteration 19 --
Warning: mb_stripos() expects parameter 3 to be integer, string given in %s on line %d
NULL
--- Iteration 18 --
+-- Iteration 20 --
Warning: mb_stripos() expects parameter 3 to be integer, string given in %s on line %d
NULL
--- Iteration 19 --
+-- Iteration 21 --
Warning: mb_stripos() expects parameter 3 to be integer, string given in %s on line %d
NULL
--- Iteration 20 --
+-- Iteration 22 --
Warning: mb_stripos() expects parameter 3 to be integer, string given in %s on line %d
NULL
--- Iteration 21 --
+-- Iteration 23 --
Warning: mb_stripos() expects parameter 3 to be integer, object given in %s on line %d
NULL
--- Iteration 22 --
+-- Iteration 24 --
int(8)
--- Iteration 23 --
+-- Iteration 25 --
int(8)
--- Iteration 24 --
+-- Iteration 26 --
Warning: mb_stripos() expects parameter 3 to be integer, resource given in %s on line %d
NULL
diff --git a/ext/mbstring/tests/mb_stripos_variation5_Bug45923.phpt b/ext/mbstring/tests/mb_stripos_variation5_Bug45923.phpt
index fbe4937ac2..8ffcae5c0f 100644
--- a/ext/mbstring/tests/mb_stripos_variation5_Bug45923.phpt
+++ b/ext/mbstring/tests/mb_stripos_variation5_Bug45923.phpt
@@ -7,10 +7,9 @@ function_exists('mb_stripos') or die("skip mb_stripos() is not available in this
?>
--FILE--
<?php
-/* Prototype : int mb_stripos(string haystack, string needle [, int offset [, string encoding]])
- * Description: Finds position of first occurrence of a string within another, case insensitive
+/* Prototype : int mb_stripos(string $haystack, string $needle [, int $offset [, string $encoding]])
+ * Description: Find position of first occurrence of a string within another, case insensitive
* Source code: ext/mbstring/mbstring.c
- * Alias to functions:
*/
/*
@@ -34,7 +33,7 @@ $needle_mb = base64_decode('44CC');
* mb_stripos should not be able to accept negative values as $offset.
* 60 is larger than *BYTE* count for $string_mb
*/
-for ($i = -10; $i <= 60; $i += 10) {
+for ($i = -30; $i <= 60; $i += 10) {
echo "\n**-- Offset is: $i --**\n";
echo "-- ASCII String --\n";
var_dump(mb_stripos($string_ascii, $needle_ascii, $i));
@@ -48,7 +47,7 @@ echo "Done";
--EXPECTF--
*** Testing mb_stripos() : usage variations ***
-**-- Offset is: -10 --**
+**-- Offset is: -30 --**
-- ASCII String --
Warning: mb_stripos(): Offset not contained in string in %s on line %d
@@ -58,6 +57,18 @@ bool(false)
Warning: mb_stripos(): Offset not contained in string in %s on line %d
bool(false)
+**-- Offset is: -20 --**
+-- ASCII String --
+int(9)
+--Multibyte String --
+int(9)
+
+**-- Offset is: -10 --**
+-- ASCII String --
+int(20)
+--Multibyte String --
+int(20)
+
**-- Offset is: 0 --**
-- ASCII String --
int(9)
@@ -116,4 +127,3 @@ bool(false)
Warning: mb_stripos(): Offset not contained in string in %s on line %d
bool(false)
Done
-
diff --git a/ext/mbstring/tests/mb_strpos.phpt b/ext/mbstring/tests/mb_strpos.phpt
index e1222ca6dd..364bc7cc1a 100644
--- a/ext/mbstring/tests/mb_strpos.phpt
+++ b/ext/mbstring/tests/mb_strpos.phpt
@@ -14,10 +14,14 @@ include_once('common.inc');
// Test string
$euc_jp = b'0123¤³¤Îʸ»úÎó¤ÏÆüËܸì¤Ç¤¹¡£EUC-JP¤ò»È¤Ã¤Æ¤¤¤Þ¤¹¡£0123ÆüËܸì¤ÏÌÌÅݽ­¤¤¡£';
+$slen = mb_strlen($euc_jp, 'EUC-JP');
+echo "String len: $slen\n";
+
// EUC-JP - With encoding parameter
mb_internal_encoding('UTF-8') or print("mb_internal_encoding() failed\n");
echo "== POSITIVE OFFSET ==\n";
+
print mb_strpos($euc_jp, b'ÆüËܸì', 0, 'EUC-JP') . "\n";
print mb_strpos($euc_jp, b'0', 0, 'EUC-JP') . "\n";
print mb_strpos($euc_jp, 3, 0, 'EUC-JP') . "\n";
@@ -27,29 +31,44 @@ print mb_strpos($euc_jp, b'0', 15, 'EUC-JP') . "\n";
print mb_strpos($euc_jp, 3, 15, 'EUC-JP') . "\n";
print mb_strpos($euc_jp, 0, 15, 'EUC-JP') . "\n";
+
// Negative offset
-// Note: PHP Warning - offset is negative.
-// Note: For offset(-15). It does not return position of latter string. (ie the same result as -50)
echo "== NEGATIVE OFFSET ==\n";
-$r = mb_strpos($euc_jp, b'ÆüËܸì', -15, 'EUC-JP');
-($r === FALSE) ? print "OK_NEGATIVE_OFFSET\n" : print "NG_NEGATIVE_OFFSET\n";
-$r = mb_strpos($euc_jp, b'0', -15, 'EUC-JP');
-($r === FALSE) ? print "OK_NEGATIVE_OFFSET\n" : print "NG_NEGATIVE_OFFSET\n";
-$r = mb_strpos($euc_jp, 3, -15, 'EUC-JP');
-($r === FALSE) ? print "OK_NEGATIVE_OFFSET\n" : print "NG_NEGATIVE_OFFSET\n";
-$r = mb_strpos($euc_jp, 0, -15, 'EUC-JP');
-($r === FALSE) ? print "OK_NEGATIVE_OFFSET\n" : print "NG_NEGATIVE_OFFSET\n";
+
+print mb_strpos($euc_jp, b'ÆüËܸì', -15, 'EUC-JP') . "\n";
+print mb_strpos($euc_jp, b'0', -15, 'EUC-JP') . "\n";
+print mb_strpos($euc_jp, 3, -15, 'EUC-JP') . "\n";
+print mb_strpos($euc_jp, 0, -15, 'EUC-JP') . "\n";
+print mb_strpos($euc_jp, 0, -43, 'EUC-JP') . "\n";
+
+
+// Invalid offset - should return false with warning
+print ("== INVALID OFFSET ==\n");
+
+$r = mb_strpos($euc_jp, b'ÆüËܸì', 44, 'EUC-JP');
+($r === FALSE) ? print "OK_INVALID_OFFSET\n" : print "NG_INVALID_OFFSET\n";
+$r = mb_strpos($euc_jp, b'ÆüËܸì', 50, 'EUC-JP');
+($r === FALSE) ? print "OK_INVALID_OFFSET\n" : print "NG_INVALID_OFFSET\n";
+$r = mb_strpos($euc_jp, b'0', 50, 'EUC-JP');
+($r === FALSE) ? print "OK_INVALID_OFFSET\n" : print "NG_INVALID_OFFSET\n";
+$r = mb_strpos($euc_jp, 3, 50, 'EUC-JP');
+($r === FALSE) ? print "OK_INVALID_OFFSET\n" : print "NG_INVALID_OFFSET\n";
+$r = mb_strpos($euc_jp, 0, 50, 'EUC-JP');
+($r === FALSE) ? print "OK_INVALID_OFFSET\n" : print "NG_INVALID_OFFSET\n";
$r = mb_strpos($euc_jp, b'ÆüËܸì', -50, 'EUC-JP');
-($r === FALSE) ? print "OK_NEGATIVE_OFFSET\n" : print "NG_NEGATIVE_OFFSET\n";
+($r === FALSE) ? print "OK_INVALID_OFFSET\n" : print "NG_INVALID_OFFSET\n";
$r = mb_strpos($euc_jp, b'0', -50, 'EUC-JP');
-($r === FALSE) ? print "OK_NEGATIVE_OFFSET\n" : print "NG_NEGATIVE_OFFSET\n";
+($r === FALSE) ? print "OK_INVALID_OFFSET\n" : print "NG_INVALID_OFFSET\n";
$r = mb_strpos($euc_jp, 3, -50, 'EUC-JP');
-($r === FALSE) ? print "OK_NEGATIVE_OFFSET\n" : print "NG_NEGATIVE_OFFSET\n";
+($r === FALSE) ? print "OK_INVALID_OFFSET\n" : print "NG_INVALID_OFFSET\n";
$r = mb_strpos($euc_jp, 0, -50, 'EUC-JP');
-($r === FALSE) ? print "OK_NEGATIVE_OFFSET\n" : print "NG_NEGATIVE_OFFSET\n";
+($r === FALSE) ? print "OK_INVALID_OFFSET\n" : print "NG_INVALID_OFFSET\n";
+$r = mb_strpos($euc_jp, 0, -44, 'EUC-JP');
+($r === FALSE) ? print "OK_INVALID_OFFSET\n" : print "NG_INVALID_OFFSET\n";
// Out of range - should return false
print ("== OUT OF RANGE ==\n");
+
$r = mb_strpos($euc_jp, b'ÆüËܸì', 40, 'EUC-JP');
($r === FALSE) ? print "OK_OUT_RANGE\n" : print "NG_OUT_RANGE\n";
$r = mb_strpos($euc_jp, b'0', 40, 'EUC-JP');
@@ -58,12 +77,19 @@ $r = mb_strpos($euc_jp, 3, 40, 'EUC-JP');
($r === FALSE) ? print "OK_OUT_RANGE\n" : print "NG_OUT_RANGE\n";
$r = mb_strpos($euc_jp, 0, 40, 'EUC-JP');
($r === FALSE) ? print "OK_OUT_RANGE\n" : print "NG_OUT_RANGE\n";
-// Note: Returned NULL string
-// echo gettype($r). ' val '. $r ."\n";
+$r = mb_strpos($euc_jp, b'ÆüËܸì', -3, 'EUC-JP');
+($r === FALSE) ? print "OK_OUT_RANGE\n" : print "NG_OUT_RANGE\n";
+$r = mb_strpos($euc_jp, b'0', -3, 'EUC-JP');
+($r === FALSE) ? print "OK_OUT_RANGE\n" : print "NG_OUT_RANGE\n";
+$r = mb_strpos($euc_jp, 3, -3, 'EUC-JP');
+($r === FALSE) ? print "OK_OUT_RANGE\n" : print "NG_OUT_RANGE\n";
+$r = mb_strpos($euc_jp, 0, -3, 'EUC-JP');
+($r === FALSE) ? print "OK_OUT_RANGE\n" : print "NG_OUT_RANGE\n";
// Non-existent
echo "== NON-EXISTENT ==\n";
+
$r = mb_strpos($euc_jp, b'´Ú¹ñ¸ì', 0, 'EUC-JP');
($r === FALSE) ? print "OK_STR\n" : print "NG_STR\n";
$r = mb_strpos($euc_jp, b"\n", 0, 'EUC-JP');
@@ -72,6 +98,7 @@ $r = mb_strpos($euc_jp, b"\n", 0, 'EUC-JP');
// EUC-JP - No encoding parameter
echo "== NO ENCODING PARAMETER ==\n";
+
mb_internal_encoding('EUC-JP') or print("mb_internal_encoding() failed\n");
print mb_strpos($euc_jp, b'ÆüËܸì', 0) . "\n";
@@ -86,6 +113,7 @@ $r = mb_strpos($euc_jp, b"\n", 0);
// EUC-JP - No offset and encoding parameter
echo "== NO OFFSET AND ENCODING PARAMETER ==\n";
+
mb_internal_encoding('EUC-JP') or print("mb_internal_encoding() failed\n");
print mb_strpos($euc_jp, b'ÆüËܸì') . "\n";
@@ -110,11 +138,10 @@ $r = mb_strpos($euc_jp, $t_obj, 'EUC-JP');
($r === NULL) ? print("OK_OBJECT\n") : print("NG_OBJECT\n");
$r = mb_strpos($euc_jp, $t_obj, 'BAD_ENCODING');
($r === NULL) ? print("OK_BAD_ENCODING\n") : print("NG_BAD_ENCODING\n");
-
-
?>
-
+==DONE==
--EXPECT--
+String len: 43
== POSITIVE OFFSET ==
10
0
@@ -125,27 +152,41 @@ $r = mb_strpos($euc_jp, $t_obj, 'BAD_ENCODING');
33
30
== NEGATIVE OFFSET ==
+34
+30
+33
+30
+0
+== INVALID OFFSET ==
+ERR: Warning
+OK_INVALID_OFFSET
ERR: Warning
-OK_NEGATIVE_OFFSET
+OK_INVALID_OFFSET
ERR: Warning
-OK_NEGATIVE_OFFSET
+OK_INVALID_OFFSET
ERR: Warning
-OK_NEGATIVE_OFFSET
+OK_INVALID_OFFSET
ERR: Warning
-OK_NEGATIVE_OFFSET
+OK_INVALID_OFFSET
ERR: Warning
-OK_NEGATIVE_OFFSET
+OK_INVALID_OFFSET
ERR: Warning
-OK_NEGATIVE_OFFSET
+OK_INVALID_OFFSET
ERR: Warning
-OK_NEGATIVE_OFFSET
+OK_INVALID_OFFSET
ERR: Warning
-OK_NEGATIVE_OFFSET
+OK_INVALID_OFFSET
+ERR: Warning
+OK_INVALID_OFFSET
== OUT OF RANGE ==
OK_OUT_RANGE
OK_OUT_RANGE
OK_OUT_RANGE
OK_OUT_RANGE
+OK_OUT_RANGE
+OK_OUT_RANGE
+OK_OUT_RANGE
+OK_OUT_RANGE
== NON-EXISTENT ==
OK_STR
OK_NEWLINE
@@ -172,4 +213,4 @@ ERR: Warning
OK_OBJECT
ERR: Warning
OK_BAD_ENCODING
-
+==DONE==
diff --git a/ext/mbstring/tests/mb_strpos_variation3.phpt b/ext/mbstring/tests/mb_strpos_variation3.phpt
index 8079a19021..f30b708183 100644
--- a/ext/mbstring/tests/mb_strpos_variation3.phpt
+++ b/ext/mbstring/tests/mb_strpos_variation3.phpt
@@ -51,45 +51,47 @@ $inputs = array(
/*1*/ 0,
1,
12345,
+ -5,
-2345,
// float data
-/*5*/ 10.5,
- -10.5,
+/*6*/ 10.5,
+ -5.5,
+ -100.5,
12.3456789000e10,
12.3456789000E-10,
.5,
// null data
-/*10*/ NULL,
+/*12*/ NULL,
null,
// boolean data
-/*12*/ true,
+/*14*/ true,
false,
TRUE,
FALSE,
// empty data
-/*16*/ "",
+/*18*/ "",
'',
// string data
-/*18*/ "string",
+/*20*/ "string",
'string',
$heredoc,
// object data
-/*21*/ new classA(),
+/*23*/ new classA(),
// undefined data
-/*22*/ @$undefined_var,
+/*24*/ @$undefined_var,
// unset data
-/*23*/ @$unset_var,
+/*25*/ @$unset_var,
// resource variable
-/*24*/ $fp
+/*26*/ $fp
);
// loop through each element of $inputs to check the behavior of mb_strpos()
@@ -119,28 +121,28 @@ Warning: mb_strpos(): Offset not contained in string in %s on line %d
bool(false)
-- Iteration 4 --
-
-Warning: mb_strpos(): Offset not contained in string in %s on line %d
-bool(false)
+int(8)
-- Iteration 5 --
+
+Warning: mb_strpos(): Offset not contained in string in %s on line %d
bool(false)
-- Iteration 6 --
-
-Warning: mb_strpos(): Offset not contained in string in %s on line %d
bool(false)
-- Iteration 7 --
+int(8)
+
+-- Iteration 8 --
Warning: mb_strpos(): Offset not contained in string in %s on line %d
bool(false)
--- Iteration 8 --
-int(8)
-
-- Iteration 9 --
-int(8)
+
+Warning: mb_strpos(): Offset not contained in string in %s on line %d
+bool(false)
-- Iteration 10 --
int(8)
@@ -161,42 +163,48 @@ int(8)
int(8)
-- Iteration 16 --
+int(8)
+
+-- Iteration 17 --
+int(8)
+
+-- Iteration 18 --
Warning: mb_strpos() expects parameter 3 to be integer, string given in %s on line %d
NULL
--- Iteration 17 --
+-- Iteration 19 --
Warning: mb_strpos() expects parameter 3 to be integer, string given in %s on line %d
NULL
--- Iteration 18 --
+-- Iteration 20 --
Warning: mb_strpos() expects parameter 3 to be integer, string given in %s on line %d
NULL
--- Iteration 19 --
+-- Iteration 21 --
Warning: mb_strpos() expects parameter 3 to be integer, string given in %s on line %d
NULL
--- Iteration 20 --
+-- Iteration 22 --
Warning: mb_strpos() expects parameter 3 to be integer, string given in %s on line %d
NULL
--- Iteration 21 --
+-- Iteration 23 --
Warning: mb_strpos() expects parameter 3 to be integer, object given in %s on line %d
NULL
--- Iteration 22 --
+-- Iteration 24 --
int(8)
--- Iteration 23 --
+-- Iteration 25 --
int(8)
--- Iteration 24 --
+-- Iteration 26 --
Warning: mb_strpos() expects parameter 3 to be integer, resource given in %s on line %d
NULL
diff --git a/ext/mbstring/tests/mb_strpos_variation5.phpt b/ext/mbstring/tests/mb_strpos_variation5.phpt
index 7a9604abef..23bfa22b61 100644
--- a/ext/mbstring/tests/mb_strpos_variation5.phpt
+++ b/ext/mbstring/tests/mb_strpos_variation5.phpt
@@ -33,7 +33,7 @@ $needle_mb = base64_decode('44CC');
* mb_strpos should not be able to accept negative values as $offset.
* 60 is larger than *BYTE* count for $string_mb
*/
-for ($i = -10; $i <= 60; $i += 10) {
+for ($i = -30; $i <= 60; $i += 10) {
echo "\n**-- Offset is: $i --**\n";
echo "-- ASCII String --\n";
var_dump(mb_strpos($string_ascii, $needle_ascii, $i));
@@ -47,7 +47,7 @@ echo "Done";
--EXPECTF--
*** Testing mb_strpos() : usage variations ***
-**-- Offset is: -10 --**
+**-- Offset is: -30 --**
-- ASCII String --
Warning: mb_strpos(): Offset not contained in string in %s on line %d
@@ -57,6 +57,18 @@ bool(false)
Warning: mb_strpos(): Offset not contained in string in %s on line %d
bool(false)
+**-- Offset is: -20 --**
+-- ASCII String --
+int(9)
+--Multibyte String --
+int(9)
+
+**-- Offset is: -10 --**
+-- ASCII String --
+int(20)
+--Multibyte String --
+int(20)
+
**-- Offset is: 0 --**
-- ASCII String --
int(9)
diff --git a/ext/mcrypt/tests/mcrypt_module_get_algo_block_size.phpt b/ext/mcrypt/tests/mcrypt_module_get_algo_block_size.phpt
index c89a44ad8a..93db98241a 100644
--- a/ext/mcrypt/tests/mcrypt_module_get_algo_block_size.phpt
+++ b/ext/mcrypt/tests/mcrypt_module_get_algo_block_size.phpt
@@ -8,12 +8,13 @@ var_dump(mcrypt_module_get_algo_block_size(MCRYPT_RIJNDAEL_256));
var_dump(mcrypt_module_get_algo_block_size(MCRYPT_RIJNDAEL_192));
var_dump(mcrypt_module_get_algo_block_size(MCRYPT_RC2));
var_dump(mcrypt_module_get_algo_block_size(MCRYPT_XTEA));
-var_dump(mcrypt_module_get_algo_block_size(MCRYPT_CAST_128));
+var_dump(mcrypt_module_get_algo_block_size(MCRYPT_CAST_256));
var_dump(mcrypt_module_get_algo_block_size(MCRYPT_BLOWFISH));
+?>
--EXPECT--
int(32)
int(24)
int(8)
int(8)
-int(8)
+int(16)
int(8)
diff --git a/ext/mcrypt/tests/mcrypt_module_get_algo_key_size.phpt b/ext/mcrypt/tests/mcrypt_module_get_algo_key_size.phpt
index 7d3841f3e2..35e4721055 100644
--- a/ext/mcrypt/tests/mcrypt_module_get_algo_key_size.phpt
+++ b/ext/mcrypt/tests/mcrypt_module_get_algo_key_size.phpt
@@ -8,12 +8,13 @@ var_dump(mcrypt_module_get_algo_key_size(MCRYPT_RIJNDAEL_256));
var_dump(mcrypt_module_get_algo_key_size(MCRYPT_RIJNDAEL_192));
var_dump(mcrypt_module_get_algo_key_size(MCRYPT_RC2));
var_dump(mcrypt_module_get_algo_key_size(MCRYPT_XTEA));
-var_dump(mcrypt_module_get_algo_key_size(MCRYPT_CAST_128));
+var_dump(mcrypt_module_get_algo_key_size(MCRYPT_CAST_256));
var_dump(mcrypt_module_get_algo_key_size(MCRYPT_BLOWFISH));
+?>
--EXPECT--
int(32)
int(32)
int(128)
int(16)
-int(16)
+int(32)
int(56)
diff --git a/ext/mysqli/mysqli.c b/ext/mysqli/mysqli.c
index 594dd0da35..8653f99c99 100644
--- a/ext/mysqli/mysqli.c
+++ b/ext/mysqli/mysqli.c
@@ -34,6 +34,7 @@
#include "php_mysqli_structs.h"
#include "mysqli_priv.h"
#include "zend_exceptions.h"
+#include "ext/spl/spl_exceptions.h"
#include "zend_interfaces.h"
ZEND_DECLARE_MODULE_GLOBALS(mysqli)
@@ -582,9 +583,7 @@ PHP_MINIT_FUNCTION(mysqli)
mysqli_object_handlers.write_property = mysqli_write_property;
mysqli_object_handlers.get_property_ptr_ptr = std_hnd->get_property_ptr_ptr;
mysqli_object_handlers.has_property = mysqli_object_has_property;
-#if PHP_VERSION_ID >= 50300
mysqli_object_handlers.get_debug_info = mysqli_object_get_debug_info;
-#endif
memcpy(&mysqli_object_driver_handlers, &mysqli_object_handlers, sizeof(zend_object_handlers));
mysqli_object_driver_handlers.free_obj = mysqli_driver_free_storage;
memcpy(&mysqli_object_link_handlers, &mysqli_object_handlers, sizeof(zend_object_handlers));
@@ -603,11 +602,7 @@ PHP_MINIT_FUNCTION(mysqli)
"MySqli persistent connection", module_number);
INIT_CLASS_ENTRY(cex, "mysqli_sql_exception", mysqli_exception_methods);
-#ifdef HAVE_SPL
mysqli_exception_class_entry = zend_register_internal_class_ex(&cex, spl_ce_RuntimeException);
-#else
- mysqli_exception_class_entry = zend_register_internal_class_ex(&cex, zend_ce_exception);
-#endif
mysqli_exception_class_entry->ce_flags |= ZEND_ACC_FINAL;
zend_declare_property_long(mysqli_exception_class_entry, "code", sizeof("code")-1, 0, ZEND_ACC_PROTECTED);
zend_declare_property_string(mysqli_exception_class_entry, "sqlstate", sizeof("sqlstate")-1, "00000", ZEND_ACC_PROTECTED);
@@ -999,9 +994,7 @@ PHP_MINFO_FUNCTION(mysqli)
/* Dependancies */
static const zend_module_dep mysqli_deps[] = {
-#if defined(HAVE_SPL) && (PHP_VERSION_ID >= 50100)
ZEND_MOD_REQUIRED("spl")
-#endif
#if defined(MYSQLI_USE_MYSQLND)
ZEND_MOD_REQUIRED("mysqlnd")
#endif
@@ -1291,9 +1284,7 @@ void php_mysqli_fetch_into_hash(INTERNAL_FUNCTION_PARAMETERS, int override_flags
if (ce->constructor) {
fci.size = sizeof(fci);
- fci.function_table = &ce->function_table;
ZVAL_UNDEF(&fci.function_name);
- fci.symbol_table = NULL;
fci.object = Z_OBJ_P(return_value);
fci.retval = &retval;
fci.params = NULL;
@@ -1315,7 +1306,7 @@ void php_mysqli_fetch_into_hash(INTERNAL_FUNCTION_PARAMETERS, int override_flags
fcc.initialized = 1;
fcc.function_handler = ce->constructor;
- fcc.calling_scope = EG(scope);
+ fcc.calling_scope = zend_get_executed_scope();
fcc.called_scope = Z_OBJCE_P(return_value);
fcc.object = Z_OBJ_P(return_value);
diff --git a/ext/mysqli/mysqli_api.c b/ext/mysqli/mysqli_api.c
index 80cba4f85c..333e890f3b 100644
--- a/ext/mysqli/mysqli_api.c
+++ b/ext/mysqli/mysqli_api.c
@@ -1267,7 +1267,11 @@ PHP_FUNCTION(mysqli_fetch_lengths)
MYSQL_RES *result;
zval *mysql_result;
unsigned int i;
- zend_ulong *ret;
+#if defined(MYSQLI_USE_MYSQLND)
+ const size_t *ret;
+#else
+ const zend_ulong *ret;
+#endif
if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O", &mysql_result, mysqli_result_class_entry) == FAILURE) {
return;
@@ -1693,10 +1697,6 @@ static int mysqli_options_get_option_zval_type(int option)
{
switch (option) {
#ifdef MYSQLI_USE_MYSQLND
-#if PHP_MAJOR_VERSION == 6
- /* PHP-7 doesn't supprt unicode */
- case MYSQLND_OPT_NUMERIC_AND_DATETIME_AS_UNICODE:
-#endif
case MYSQLND_OPT_NET_CMD_BUFFER_SIZE:
case MYSQLND_OPT_NET_READ_BUFFER_SIZE:
#ifdef MYSQLND_STRING_TO_INT_CONVERSION
@@ -1777,11 +1777,7 @@ PHP_FUNCTION(mysqli_options)
MYSQLI_FETCH_RESOURCE_CONN(mysql, mysql_link, MYSQLI_STATUS_INITIALIZED);
#if !defined(MYSQLI_USE_MYSQLND)
-#if PHP_API_VERSION < 20100412
- if ((PG(open_basedir) && PG(open_basedir)[0] != '\0') || PG(safe_mode)) {
-#else
if (PG(open_basedir) && PG(open_basedir)[0] != '\0') {
-#endif
if(mysql_option == MYSQL_OPT_LOCAL_INFILE) {
RETURN_FALSE;
}
diff --git a/ext/mysqli/mysqli_fe.c b/ext/mysqli/mysqli_fe.c
index 817834dfc2..abc23f0699 100644
--- a/ext/mysqli/mysqli_fe.c
+++ b/ext/mysqli/mysqli_fe.c
@@ -243,18 +243,14 @@ ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_mysqli_fetch_object, 0, 0, 1)
-#if PHP_VERSION_ID > 50399
MYSQLI_ZEND_ARG_OBJ_INFO_RESULT()
ZEND_ARG_INFO(0, class_name)
ZEND_ARG_ARRAY_INFO(0, params, 0)
-#endif
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_class_mysqli_fetch_object, 0, 0, 0)
-#if PHP_VERSION_ID > 50399
ZEND_ARG_INFO(0, class_name)
ZEND_ARG_ARRAY_INFO(0, params, 0)
-#endif
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_mysqli_kill, 0, 0, 2)
diff --git a/ext/mysqli/mysqli_priv.h b/ext/mysqli/mysqli_priv.h
index 0a1122a836..c34049c89e 100644
--- a/ext/mysqli/mysqli_priv.h
+++ b/ext/mysqli/mysqli_priv.h
@@ -78,10 +78,6 @@ extern void php_mysqli_report_error(const char *sqlstate, int errorno, const cha
extern void php_mysqli_report_index(const char *query, unsigned int status);
extern void php_mysqli_throw_sql_exception(char *sqlstate, int errorno, char *format, ...);
-#ifdef HAVE_SPL
-extern PHPAPI zend_class_entry *spl_ce_RuntimeException;
-#endif
-
#define PHP_MYSQLI_EXPORT(__type) PHP_MYSQLI_API __type
PHP_MYSQLI_EXPORT(zend_object *) mysqli_objects_new(zend_class_entry *);
diff --git a/ext/mysqli/mysqli_prop.c b/ext/mysqli/mysqli_prop.c
index e679dcc48e..6b9e95ff7b 100644
--- a/ext/mysqli/mysqli_prop.c
+++ b/ext/mysqli/mysqli_prop.c
@@ -15,8 +15,6 @@
| Author: Georg Richter <georg@php.net> |
| Andrey Hristov <andrey@php.net> |
+----------------------------------------------------------------------+
-
- $Id$
*/
#ifdef HAVE_CONFIG_H
@@ -289,7 +287,11 @@ static zval *result_type_read(mysqli_object *obj, zval *retval)
static zval *result_lengths_read(mysqli_object *obj, zval *retval)
{
MYSQL_RES *p;
- zend_ulong *ret;
+#if defined(MYSQLI_USE_MYSQLND)
+ const size_t *ret;
+#else
+ const zend_ulong *ret;
+#endif
uint field_count;
CHECK_STATUS(MYSQLI_STATUS_VALID);
diff --git a/ext/mysqli/php_mysqli_structs.h b/ext/mysqli/php_mysqli_structs.h
index d231b774e1..c75023c3ad 100644
--- a/ext/mysqli/php_mysqli_structs.h
+++ b/ext/mysqli/php_mysqli_structs.h
@@ -216,10 +216,6 @@ extern zend_object_iterator *php_mysqli_result_get_iterator(zend_class_entry *ce
extern void php_mysqli_fetch_into_hash_aux(zval *return_value, MYSQL_RES * result, zend_long fetchtype);
-#ifdef HAVE_SPL
-extern PHPAPI zend_class_entry *spl_ce_RuntimeException;
-#endif
-
#define MYSQLI_DISABLE_MQ if (mysql->multi_query) { \
mysql_set_server_option(mysql->mysql, MYSQL_OPTION_MULTI_STATEMENTS_OFF); \
mysql->multi_query = 0; \
diff --git a/ext/mysqli/tests/mysqli_fetch_object.phpt b/ext/mysqli/tests/mysqli_fetch_object.phpt
index 9706ceac84..dff91531ce 100644
--- a/ext/mysqli/tests/mysqli_fetch_object.phpt
+++ b/ext/mysqli/tests/mysqli_fetch_object.phpt
@@ -59,18 +59,25 @@ require_once('skipifconnectfailure.inc');
}
- $obj = mysqli_fetch_object($res, 'mysqli_fetch_object_construct', array());
-
- if (($obj->ID !== "3") || ($obj->label !== "c") || ($obj->a !== NULL) || ($obj->b !== NULL) || (get_class($obj) != 'mysqli_fetch_object_construct')) {
- printf("[006] Object seems wrong. [%d] %s\n", mysqli_errno($link), mysqli_error($link));
- var_dump($obj);
- }
+ try {
+ $obj = mysqli_fetch_object($res, 'mysqli_fetch_object_construct', array());
+ if (($obj->ID !== "3") || ($obj->label !== "c") || ($obj->a !== NULL) || ($obj->b !== NULL) || (get_class($obj) != 'mysqli_fetch_object_construct')) {
+ printf("[006] Object seems wrong. [%d] %s\n", mysqli_errno($link), mysqli_error($link));
+ var_dump($obj);
+ }
+ } catch (Throwable $e) {
+ echo "Exception: " . $e->getMessage() . "\n";
+ }
- $obj = mysqli_fetch_object($res, 'mysqli_fetch_object_construct', array('a'));
- if (($obj->ID !== "4") || ($obj->label !== "d") || ($obj->a !== 'a') || ($obj->b !== NULL) || (get_class($obj) != 'mysqli_fetch_object_construct')) {
- printf("[007] Object seems wrong. [%d] %s\n", mysqli_errno($link), mysqli_error($link));
- var_dump($obj);
- }
+ try {
+ $obj = mysqli_fetch_object($res, 'mysqli_fetch_object_construct', array('a'));
+ if (($obj->ID !== "4") || ($obj->label !== "d") || ($obj->a !== 'a') || ($obj->b !== NULL) || (get_class($obj) != 'mysqli_fetch_object_construct')) {
+ printf("[007] Object seems wrong. [%d] %s\n", mysqli_errno($link), mysqli_error($link));
+ var_dump($obj);
+ }
+ } catch (Throwable $e) {
+ echo "Exception: " . $e->getMessage() . "\n";
+ }
$obj = mysqli_fetch_object($res, 'mysqli_fetch_object_construct', array('a', 'b'));
if (($obj->ID !== "5") || ($obj->label !== "e") || ($obj->a !== 'a') || ($obj->b !== 'b') || (get_class($obj) != 'mysqli_fetch_object_construct')) {
@@ -140,12 +147,8 @@ require_once('skipifconnectfailure.inc');
--EXPECTF--
[E_WARNING] mysqli_fetch_object() expects at least 1 parameter, 0 given in %s on line %d
[E_WARNING] mysqli_fetch_object() expects parameter 1 to be mysqli_result, null given in %s on line %d
-[E_WARNING] Missing argument 1 for mysqli_fetch_object_construct::__construct() in %s on line %d
-[E_WARNING] Missing argument 2 for mysqli_fetch_object_construct::__construct() in %s on line %d
-[E_NOTICE] Undefined variable: a in %s on line %d
-[E_NOTICE] Undefined variable: b in %s on line %d
-[E_WARNING] Missing argument 2 for mysqli_fetch_object_construct::__construct() in %s on line %d
-[E_NOTICE] Undefined variable: b in %s on line %d
+Exception: Too few arguments to function mysqli_fetch_object_construct::__construct(), 0 passed and exactly 2 expected
+Exception: Too few arguments to function mysqli_fetch_object_construct::__construct(), 1 passed and exactly 2 expected
NULL
NULL
[E_WARNING] mysqli_fetch_object(): Couldn't fetch mysqli_result in %s on line %d
diff --git a/ext/mysqli/tests/mysqli_fetch_object_oo.phpt b/ext/mysqli/tests/mysqli_fetch_object_oo.phpt
index 82e311cc72..8fac044139 100644
--- a/ext/mysqli/tests/mysqli_fetch_object_oo.phpt
+++ b/ext/mysqli/tests/mysqli_fetch_object_oo.phpt
@@ -89,10 +89,14 @@ require_once('skipifconnectfailure.inc');
mysqli_fetch_object($res);
}
- $obj = $res->fetch_object('mysqli_fetch_object_construct', array('a'));
- if (($obj->ID !== "4") || ($obj->label !== "d") || ($obj->a !== 'a') || ($obj->b !== NULL) || (get_class($obj) != 'mysqli_fetch_object_construct')) {
- printf("[010] Object seems wrong. [%d] %s\n", $mysqli->errno, $mysqli->error);
- var_dump($obj);
+ try {
+ $obj = $res->fetch_object('mysqli_fetch_object_construct', array('a'));
+ if (($obj->ID !== "4") || ($obj->label !== "d") || ($obj->a !== 'a') || ($obj->b !== NULL) || (get_class($obj) != 'mysqli_fetch_object_construct')) {
+ printf("[010] Object seems wrong. [%d] %s\n", $mysqli->errno, $mysqli->error);
+ var_dump($obj);
+ }
+ } catch (Throwable $e) {
+ echo "Exception: " . $e->getMessage() . "\n";
}
$obj = $res->fetch_object('mysqli_fetch_object_construct', array('a', 'b'));
@@ -132,8 +136,7 @@ require_once('skipifconnectfailure.inc');
[0] Argument 2 passed to mysqli_result::fetch_object() must be of the type array, object given in %s on line %d
[0] Argument 2 passed to mysqli_result::fetch_object() must be of the type array, object given in %s on line %d
[0] Argument 2 passed to mysqli_result::fetch_object() must be of the type array, null given in %s on line %d
-[E_WARNING] Missing argument 2 for mysqli_fetch_object_construct::__construct() in %s on line %d
-[E_NOTICE] Undefined variable: b in %s on line %d
+Exception: Too few arguments to function mysqli_fetch_object_construct::__construct(), 1 passed and exactly 2 expected
NULL
NULL
[E_WARNING] mysqli_fetch_object(): Couldn't fetch mysqli_result in %s on line %d
diff --git a/ext/mysqli/tests/mysqli_get_client_stats.phpt b/ext/mysqli/tests/mysqli_get_client_stats.phpt
index f0c4129dee..3e80c78e74 100644
--- a/ext/mysqli/tests/mysqli_get_client_stats.phpt
+++ b/ext/mysqli/tests/mysqli_get_client_stats.phpt
@@ -958,7 +958,7 @@ if (!mysqli_query($link, "DROP SERVER IF EXISTS myself"))
mysqli_close($link);
?>
--EXPECTF--
-array(161) {
+array(163) {
[%u|b%"bytes_sent"]=>
%unicode|string%(1) "0"
[%u|b%"bytes_received"]=>
@@ -1125,10 +1125,14 @@ array(161) {
%unicode|string%(1) "0"
[%u|b%"mem_strndup_count"]=>
%unicode|string%(1) "0"
- [%u|b%"mem_estndup_count"]=>
+ [%u|b%"mem_estrdup_count"]=>
%unicode|string%(1) "0"
[%u|b%"mem_strdup_count"]=>
%unicode|string%(1) "0"
+ [%u|b%"mem_edupl_count"]=>
+ %unicode|string%(1) "0"
+ [%u|b%"mem_dupl_count"]=>
+ %unicode|string%(1) "0"
[%u|b%"proto_text_fetched_null"]=>
%unicode|string%(1) "0"
[%u|b%"proto_text_fetched_bit"]=>
diff --git a/ext/mysqlnd/config.w32 b/ext/mysqlnd/config.w32
index ff102310dd..52be8f563c 100644
--- a/ext/mysqlnd/config.w32
+++ b/ext/mysqlnd/config.w32
@@ -6,23 +6,26 @@ if (PHP_MYSQLND != "no") {
if (CHECK_LIB("ws2_32.lib", "mysqlnd")) {
mysqlnd_source =
- "mysqlnd.c " +
"mysqlnd_alloc.c " +
"mysqlnd_auth.c " +
"mysqlnd_block_alloc.c " +
+ "mysqlnd_connection.c " +
"mysqlnd_charset.c " +
+ "mysqlnd_commands.c " +
"mysqlnd_debug.c " +
"mysqlnd_driver.c " +
"mysqlnd_ext_plugin.c " +
"mysqlnd_loaddata.c " +
"mysqlnd_reverse_api.c " +
- "mysqlnd_net.c " +
"mysqlnd_plugin.c " +
+ "mysqlnd_protocol_frame_codec.c " +
"mysqlnd_ps.c " +
"mysqlnd_ps_codec.c " +
+ "mysqlnd_read_buffer.c " +
"mysqlnd_result.c " +
"mysqlnd_result_meta.c " +
"mysqlnd_statistics.c " +
+ "mysqlnd_vio.c " +
"mysqlnd_wireprotocol.c " +
"php_mysqlnd.c ";
EXTENSION("mysqlnd", mysqlnd_source, false, "/DZEND_ENABLE_STATIC_TSRMLS_CACHE=1");
diff --git a/ext/mysqlnd/config9.m4 b/ext/mysqlnd/config9.m4
index 92cab94367..b5f20a9c8b 100644
--- a/ext/mysqlnd/config9.m4
+++ b/ext/mysqlnd/config9.m4
@@ -18,11 +18,11 @@ fi
dnl If some extension uses mysqlnd it will get compiled in PHP core
if test "$PHP_MYSQLND" != "no" || test "$PHP_MYSQLND_ENABLED" = "yes"; then
mysqlnd_ps_sources="mysqlnd_ps.c mysqlnd_ps_codec.c"
- mysqlnd_base_sources="mysqlnd.c mysqlnd_alloc.c mysqlnd_charset.c mysqlnd_wireprotocol.c \
- mysqlnd_loaddata.c mysqlnd_reverse_api.c mysqlnd_net.c \
+ mysqlnd_base_sources="mysqlnd_connection.c mysqlnd_alloc.c mysqlnd_charset.c mysqlnd_wireprotocol.c \
+ mysqlnd_loaddata.c mysqlnd_reverse_api.c mysqlnd_vio.c mysqlnd_protocol_frame_codec.c \
mysqlnd_statistics.c mysqlnd_driver.c mysqlnd_ext_plugin.c mysqlnd_auth.c \
- mysqlnd_result.c mysqlnd_result_meta.c mysqlnd_debug.c\
- mysqlnd_block_alloc.c mysqlnd_plugin.c php_mysqlnd.c"
+ mysqlnd_result.c mysqlnd_result_meta.c mysqlnd_debug.c mysqlnd_commands.c \
+ mysqlnd_block_alloc.c mysqlnd_read_buffer.c mysqlnd_plugin.c php_mysqlnd.c"
if test "$PHP_MYSQLND_COMPRESSION_SUPPORT" != "no"; then
diff --git a/ext/mysqlnd/mysqlnd.h b/ext/mysqlnd/mysqlnd.h
index 241ae00989..d8daadaf41 100644
--- a/ext/mysqlnd/mysqlnd.h
+++ b/ext/mysqlnd/mysqlnd.h
@@ -60,6 +60,7 @@
#include "mysqlnd_enum_n_def.h"
#include "mysqlnd_structs.h"
+#define MYSQLND_STR_W_LEN(str) str, (sizeof(str) - 1)
/* Library related */
PHPAPI void mysqlnd_library_init(void);
@@ -82,18 +83,23 @@ PHPAPI const MYSQLND_CHARSET * mysqlnd_find_charset_name(const char * const char
/* Connect */
-PHPAPI MYSQLND * mysqlnd_init(unsigned int client_flags, zend_bool persistent);
-PHPAPI MYSQLND * mysqlnd_connect(MYSQLND * conn,
- const char * host, const char * user,
- const char * passwd, unsigned int passwd_len,
- const char * db, unsigned int db_len,
- unsigned int port,
- const char * socket_or_pipe,
- unsigned int mysql_flags,
- unsigned int client_api_flags
- );
-
-#define mysqlnd_change_user(conn, user, passwd, db, silent) ((conn)->data)->m->change_user((conn)->data, (user), (passwd), (db), (silent), strlen((passwd)))
+#define mysqlnd_init(flags, persistent) mysqlnd_connection_init((flags), (persistent), NULL /*use default factory*/)
+#define mysqlnd_connect(conn, host, user, pass, pass_len, db, db_len, port, socket, mysql_flags, client_api_flags) \
+ mysqlnd_connection_connect((conn), (host), (user), (pass), (pass_len), (db), (db_len), (port), (socket), (mysql_flags), (client_api_flags))
+
+PHPAPI MYSQLND * mysqlnd_connection_init(const size_t client_flags, const zend_bool persistent, MYSQLND_CLASS_METHODS_TYPE(mysqlnd_object_factory) *object_factory);
+PHPAPI MYSQLND * mysqlnd_connection_connect(MYSQLND * conn,
+ const char * const host,
+ const char * const user,
+ const char * const passwd, unsigned int passwd_len,
+ const char * const db, unsigned int db_len,
+ unsigned int port,
+ const char * const socket_or_pipe,
+ unsigned int mysql_flags,
+ unsigned int client_api_flags
+ );
+
+#define mysqlnd_change_user(conn, user, passwd, db, silent) ((conn)->data)->m->change_user((conn)->data, (user), (passwd), (db), (silent), strlen((passwd)))
#define mysqlnd_change_user_ex(conn, user, passwd, db, silent, passwd_len) ((conn)->data)->m->change_user((conn)->data, (user), (passwd), (db), (silent), (passwd_len))
PHPAPI void mysqlnd_debug(const char *mode);
@@ -104,7 +110,7 @@ PHPAPI void mysqlnd_debug(const char *mode);
#define mysqlnd_fetch_all(result, flags, return_value) (result)->m.fetch_all((result), (flags), (return_value) ZEND_FILE_LINE_CC)
#define mysqlnd_result_fetch_field_data(res,offset,ret) (res)->m.fetch_field_data((res), (offset), (ret))
#define mysqlnd_get_connection_stats(conn, values) ((conn)->data)->m->get_statistics((conn)->data, (values) ZEND_FILE_LINE_CC)
-#define mysqlnd_get_client_stats(values) _mysqlnd_get_client_stats((values) ZEND_FILE_LINE_CC)
+#define mysqlnd_get_client_stats(values) _mysqlnd_get_client_stats(mysqlnd_global_stats, (values) ZEND_FILE_LINE_CC)
#define mysqlnd_close(conn,is_forced) (conn)->m->close((conn), (is_forced))
#define mysqlnd_query(conn, query_str, query_len) ((conn)->data)->m->query((conn)->data, (query_str), (query_len))
@@ -179,7 +185,7 @@ PHPAPI void mysqlnd_free_param_bind_dtor(MYSQLND_PARAM_BIND * param_bind);
PHPAPI void mysqlnd_free_result_bind_dtor(MYSQLND_RESULT_BIND * result_bind);
-PHPAPI const char * mysqlnd_field_type_name(enum mysqlnd_field_types field_type);
+PHPAPI const char * mysqlnd_field_type_name(const enum mysqlnd_field_types field_type);
/* LOAD DATA LOCAL */
void mysqlnd_local_infile_default(MYSQLND_CONN_DATA * conn);
@@ -192,7 +198,6 @@ void mysqlnd_local_infile_default(MYSQLND_CONN_DATA * conn);
#define mysqlnd_savepoint(conn, name) ((conn)->data)->m->tx_savepoint((conn)->data, (name))
#define mysqlnd_release_savepoint(conn, name) ((conn)->data)->m->tx_savepoint_release((conn)->data, (name))
#define mysqlnd_list_dbs(conn, wild) ((conn)->data)->m->list_method((conn)->data, wild? "SHOW DATABASES LIKE %s":"SHOW DATABASES", (wild), NULL)
-#define mysqlnd_list_fields(conn, tab,wild) ((conn)->data)->m->list_fields((conn)->data, (tab), (wild))
#define mysqlnd_list_processes(conn) ((conn)->data)->m->list_method((conn)->data, "SHOW PROCESSLIST", NULL, NULL)
#define mysqlnd_list_tables(conn, wild) ((conn)->data)->m->list_method((conn)->data, wild? "SHOW TABLES LIKE %s":"SHOW TABLES", (wild), NULL)
#define mysqlnd_dump_debug_info(conn) ((conn)->data)->m->server_dump_debug_information((conn)->data)
@@ -217,14 +222,14 @@ PHPAPI zend_ulong mysqlnd_old_escape_string(char * newstr, const char * escapest
/* PS */
-#define mysqlnd_stmt_init(conn) ((conn)->data)->m->stmt_init(((conn)->data))
-#define mysqlnd_stmt_store_result(stmt) (!mysqlnd_stmt_field_count((stmt)) ? PASS:((stmt)->m->store_result((stmt))? PASS:FAIL))
-#define mysqlnd_stmt_get_result(stmt) (stmt)->m->get_result((stmt))
-#define mysqlnd_stmt_more_results(stmt) (stmt)->m->more_results((stmt))
-#define mysqlnd_stmt_next_result(stmt) (stmt)->m->next_result((stmt))
-#define mysqlnd_stmt_data_seek(stmt, row) (stmt)->m->seek_data((stmt), (row))
-#define mysqlnd_stmt_prepare(stmt, q, qlen) (stmt)->m->prepare((stmt), (q), (qlen))
-#define mysqlnd_stmt_execute(stmt) (stmt)->m->execute((stmt))
+#define mysqlnd_stmt_init(conn) ((conn)->data)->m->stmt_init(((conn)->data))
+#define mysqlnd_stmt_store_result(stmt) (!mysqlnd_stmt_field_count((stmt)) ? PASS:((stmt)->m->store_result((stmt))? PASS:FAIL))
+#define mysqlnd_stmt_get_result(stmt) (stmt)->m->get_result((stmt))
+#define mysqlnd_stmt_more_results(stmt) (stmt)->m->more_results((stmt))
+#define mysqlnd_stmt_next_result(stmt) (stmt)->m->next_result((stmt))
+#define mysqlnd_stmt_data_seek(stmt, row) (stmt)->m->seek_data((stmt), (row))
+#define mysqlnd_stmt_prepare(stmt, q, qlen) (stmt)->m->prepare((stmt), (q), (qlen))
+#define mysqlnd_stmt_execute(stmt) (stmt)->m->execute((stmt))
#define mysqlnd_stmt_send_long_data(stmt,p,d,l) (stmt)->m->send_long_data((stmt), (p), (d), (l))
#define mysqlnd_stmt_alloc_param_bind(stmt) (stmt)->m->alloc_parameter_bind((stmt))
#define mysqlnd_stmt_free_param_bind(stmt,bind) (stmt)->m->free_parameter_bind((stmt), (bind))
@@ -238,10 +243,10 @@ PHPAPI zend_ulong mysqlnd_old_escape_string(char * newstr, const char * escapest
#define mysqlnd_stmt_param_metadata(stmt) (stmt)->m->get_parameter_metadata((stmt))
#define mysqlnd_stmt_result_metadata(stmt) (stmt)->m->get_result_metadata((stmt))
-#define mysqlnd_stmt_free_result(stmt) (stmt)->m->free_result((stmt))
-#define mysqlnd_stmt_close(stmt, implicit) (stmt)->m->dtor((stmt), (implicit))
-#define mysqlnd_stmt_reset(stmt) (stmt)->m->reset((stmt))
-#define mysqlnd_stmt_flush(stmt) (stmt)->m->flush((stmt))
+#define mysqlnd_stmt_free_result(stmt) (stmt)->m->free_result((stmt))
+#define mysqlnd_stmt_close(stmt, implicit) (stmt)->m->dtor((stmt), (implicit))
+#define mysqlnd_stmt_reset(stmt) (stmt)->m->reset((stmt))
+#define mysqlnd_stmt_flush(stmt) (stmt)->m->flush((stmt))
#define mysqlnd_stmt_attr_get(stmt, attr, value) (stmt)->m->get_attribute((stmt), (attr), (value))
@@ -251,11 +256,54 @@ PHPAPI zend_ulong mysqlnd_old_escape_string(char * newstr, const char * escapest
/* Performance statistics */
-PHPAPI void _mysqlnd_get_client_stats(zval *return_value ZEND_FILE_LINE_DC);
+PHPAPI extern MYSQLND_STATS * mysqlnd_global_stats;
+PHPAPI extern const MYSQLND_STRING mysqlnd_stats_values_names[];
+PHPAPI void _mysqlnd_get_client_stats(MYSQLND_STATS * stats, zval *return_value ZEND_FILE_LINE_DC);
+
+
+#ifndef MYSQLND_CORE_STATISTICS_DISABLED
+
+#define MYSQLND_INC_GLOBAL_STATISTIC(statistic) \
+ MYSQLND_INC_STATISTIC(MYSQLND_G(collect_statistics), mysqlnd_global_stats, (statistic))
+
+#define MYSQLND_DEC_GLOBAL_STATISTIC(statistic) \
+ MYSQLND_DEC_STATISTIC(MYSQLND_G(collect_statistics), mysqlnd_global_stats, (statistic))
+
+#define MYSQLND_INC_GLOBAL_STATISTIC_W_VALUE2(statistic1, value1, statistic2, value2) \
+ MYSQLND_INC_STATISTIC_W_VALUE2(MYSQLND_G(collect_statistics), mysqlnd_global_stats, (statistic1), (value1), (statistic2), (value2))
+
+#define MYSQLND_INC_CONN_STATISTIC(conn_stats, statistic) \
+ MYSQLND_INC_STATISTIC(MYSQLND_G(collect_statistics), mysqlnd_global_stats, (statistic)); \
+ MYSQLND_INC_STATISTIC(MYSQLND_G(collect_statistics), (conn_stats), (statistic));
+
+#define MYSQLND_INC_CONN_STATISTIC_W_VALUE(conn_stats, statistic, value) \
+ MYSQLND_INC_STATISTIC_W_VALUE(MYSQLND_G(collect_statistics), mysqlnd_global_stats, (statistic), (value)); \
+ MYSQLND_INC_STATISTIC_W_VALUE(MYSQLND_G(collect_statistics), (conn_stats), (statistic), (value));
+
+#define MYSQLND_INC_CONN_STATISTIC_W_VALUE2(conn_stats, statistic1, value1, statistic2, value2) \
+ MYSQLND_INC_STATISTIC_W_VALUE2(MYSQLND_G(collect_statistics), mysqlnd_global_stats, (statistic1), (value1), (statistic2), (value2)); \
+ MYSQLND_INC_STATISTIC_W_VALUE2(MYSQLND_G(collect_statistics), (conn_stats), (statistic1), (value1), (statistic2), (value2));
+
+#define MYSQLND_INC_CONN_STATISTIC_W_VALUE3(conn_stats, statistic1, value1, statistic2, value2, statistic3, value3) \
+ MYSQLND_INC_STATISTIC_W_VALUE3(MYSQLND_G(collect_statistics), mysqlnd_global_stats, (statistic1), (value1), (statistic2), (value2), (statistic3), (value3)); \
+ MYSQLND_INC_STATISTIC_W_VALUE3(MYSQLND_G(collect_statistics), (conn_stats), (statistic1), (value1), (statistic2), (value2), (statistic3), (value3));
+
+#else
+
+#define MYSQLND_INC_GLOBAL_STATISTIC(statistic)
+#define MYSQLND_DEC_GLOBAL_STATISTIC(statistic)
+#define MYSQLND_INC_GLOBAL_STATISTIC_W_VALUE2(statistic1, value1, statistic2, value2)
+#define MYSQLND_INC_CONN_STATISTIC(conn_stats, statistic)
+#define MYSQLND_INC_CONN_STATISTIC_W_VALUE(conn_stats, statistic, value)
+#define MYSQLND_INC_CONN_STATISTIC_W_VALUE2(conn_stats, statistic1, value1, statistic2, value2)
+#define MYSQLND_INC_CONN_STATISTIC_W_VALUE3(conn_stats, statistic1, value1, statistic2, value2, statistic3, value3)
+
+#endif /* MYSQLND_CORE_STATISTICS_DISABLED */
+
/* double check the class name to avoid naming conflicts when using these: */
-#define MYSQLND_METHOD(class, method) php_##class##_##method##_pub
-#define MYSQLND_METHOD_PRIVATE(class, method) php_##class##_##method##_priv
+#define MYSQLND_METHOD(class, method) mysqlnd_##class##_##method##_pub
+#define MYSQLND_METHOD_PRIVATE(class, method) mysqlnd_##class##_##method##_priv
ZEND_BEGIN_MODULE_GLOBALS(mysqlnd)
char * debug; /* The actual string */
diff --git a/ext/mysqlnd/mysqlnd_alloc.c b/ext/mysqlnd/mysqlnd_alloc.c
index fb62a7b138..6ee19a8630 100644
--- a/ext/mysqlnd/mysqlnd_alloc.c
+++ b/ext/mysqlnd/mysqlnd_alloc.c
@@ -14,11 +14,9 @@
+----------------------------------------------------------------------+
| Authors: Andrey Hristov <andrey@php.net> |
| Ulf Wendel <uw@php.net> |
- | Georg Richter <georg@php.net> |
+----------------------------------------------------------------------+
*/
-/* $Id: mysqlnd_debug.c 309303 2011-03-16 12:42:59Z andrey $ */
#include "php.h"
#include "mysqlnd.h"
#include "mysqlnd_priv.h"
@@ -26,6 +24,7 @@
#include "mysqlnd_wireprotocol.h"
#include "mysqlnd_statistics.h"
+#define MYSQLND_DEBUG_MEMORY 1
static const char mysqlnd_emalloc_name[] = "_mysqlnd_emalloc";
static const char mysqlnd_pemalloc_name[] = "_mysqlnd_pemalloc";
@@ -39,6 +38,7 @@ static const char mysqlnd_malloc_name[] = "_mysqlnd_malloc";
static const char mysqlnd_calloc_name[] = "_mysqlnd_calloc";
static const char mysqlnd_realloc_name[] = "_mysqlnd_realloc";
static const char mysqlnd_free_name[] = "_mysqlnd_free";
+static const char mysqlnd_pememdup_name[] = "_mysqlnd_pememdup";
static const char mysqlnd_pestrndup_name[] = "_mysqlnd_pestrndup";
static const char mysqlnd_pestrdup_name[] = "_mysqlnd_pestrdup";
@@ -62,6 +62,8 @@ PHPAPI const char * mysqlnd_debug_std_no_trace_funcs[] =
NULL /* must be always last */
};
+#if MYSQLND_DEBUG_MEMORY
+
#if ZEND_DEBUG
#else
@@ -74,7 +76,7 @@ PHPAPI const char * mysqlnd_debug_std_no_trace_funcs[] =
#define FAKE_PTR(p) (collect_memory_statistics && (p)? (((char *)(p)) + sizeof(size_t)) : (p))
/* {{{ _mysqlnd_emalloc */
-void * _mysqlnd_emalloc(size_t size MYSQLND_MEM_D)
+static void * _mysqlnd_emalloc(size_t size MYSQLND_MEM_D)
{
void *ret;
zend_bool collect_memory_statistics = MYSQLND_G(collect_memory_statistics);
@@ -85,8 +87,8 @@ void * _mysqlnd_emalloc(size_t size MYSQLND_MEM_D)
#if PHP_DEBUG
{
- char * fn = strrchr(__zend_orig_filename, PHP_DIR_SEPARATOR);
- TRACE_ALLOC_INF_FMT("file=%-15s line=%4d", fn? fn + 1:__zend_orig_filename, __zend_orig_lineno);
+ char * fn = strrchr(__zend_filename, PHP_DIR_SEPARATOR);
+ TRACE_ALLOC_INF_FMT("file=%-15s line=%4d", fn? fn + 1:__zend_filename, __zend_lineno);
}
#endif
@@ -94,7 +96,7 @@ void * _mysqlnd_emalloc(size_t size MYSQLND_MEM_D)
/* -1 is also "true" */
if (*threshold) {
#endif
- ret = _emalloc(REAL_SIZE(size) ZEND_FILE_LINE_CC ZEND_FILE_LINE_ORIG_RELAY_CC);
+ ret = emalloc_rel(REAL_SIZE(size));
#if PHP_DEBUG
--*threshold;
} else if (*threshold == 0) {
@@ -114,7 +116,7 @@ void * _mysqlnd_emalloc(size_t size MYSQLND_MEM_D)
/* {{{ _mysqlnd_pemalloc */
-void * _mysqlnd_pemalloc(size_t size, zend_bool persistent MYSQLND_MEM_D)
+static void * _mysqlnd_pemalloc(size_t size, zend_bool persistent MYSQLND_MEM_D)
{
void *ret;
zend_bool collect_memory_statistics = MYSQLND_G(collect_memory_statistics);
@@ -125,8 +127,8 @@ void * _mysqlnd_pemalloc(size_t size, zend_bool persistent MYSQLND_MEM_D)
#if PHP_DEBUG
{
- char * fn = strrchr(__zend_orig_filename, PHP_DIR_SEPARATOR);
- TRACE_ALLOC_INF_FMT("file=%-15s line=%4d", fn? fn + 1:__zend_orig_filename, __zend_orig_lineno);
+ char * fn = strrchr(__zend_filename, PHP_DIR_SEPARATOR);
+ TRACE_ALLOC_INF_FMT("file=%-15s line=%4d", fn? fn + 1:__zend_filename, __zend_lineno);
}
#endif
@@ -134,7 +136,7 @@ void * _mysqlnd_pemalloc(size_t size, zend_bool persistent MYSQLND_MEM_D)
/* -1 is also "true" */
if (*threshold) {
#endif
- ret = (persistent) ? __zend_malloc(REAL_SIZE(size)) : _emalloc(REAL_SIZE(size) ZEND_FILE_LINE_CC ZEND_FILE_LINE_ORIG_RELAY_CC);
+ ret = pemalloc_rel(REAL_SIZE(size), persistent);
#if PHP_DEBUG
--*threshold;
} else if (*threshold == 0) {
@@ -157,7 +159,7 @@ void * _mysqlnd_pemalloc(size_t size, zend_bool persistent MYSQLND_MEM_D)
/* {{{ _mysqlnd_ecalloc */
-void * _mysqlnd_ecalloc(unsigned int nmemb, size_t size MYSQLND_MEM_D)
+static void * _mysqlnd_ecalloc(unsigned int nmemb, size_t size MYSQLND_MEM_D)
{
void *ret;
zend_bool collect_memory_statistics = MYSQLND_G(collect_memory_statistics);
@@ -168,8 +170,8 @@ void * _mysqlnd_ecalloc(unsigned int nmemb, size_t size MYSQLND_MEM_D)
#if PHP_DEBUG
{
- char * fn = strrchr(__zend_orig_filename, PHP_DIR_SEPARATOR);
- TRACE_ALLOC_INF_FMT("file=%-15s line=%4d", fn? fn + 1:__zend_orig_filename, __zend_orig_lineno);
+ char * fn = strrchr(__zend_filename, PHP_DIR_SEPARATOR);
+ TRACE_ALLOC_INF_FMT("file=%-15s line=%4d", fn? fn + 1:__zend_filename, __zend_lineno);
}
#endif
TRACE_ALLOC_INF_FMT("before: %lu", zend_memory_usage(FALSE));
@@ -178,7 +180,7 @@ void * _mysqlnd_ecalloc(unsigned int nmemb, size_t size MYSQLND_MEM_D)
/* -1 is also "true" */
if (*threshold) {
#endif
- ret = _ecalloc(nmemb, REAL_SIZE(size) ZEND_FILE_LINE_CC ZEND_FILE_LINE_ORIG_RELAY_CC);
+ ret = ecalloc_rel(nmemb, REAL_SIZE(size));
#if PHP_DEBUG
--*threshold;
} else if (*threshold == 0) {
@@ -198,7 +200,7 @@ void * _mysqlnd_ecalloc(unsigned int nmemb, size_t size MYSQLND_MEM_D)
/* {{{ _mysqlnd_pecalloc */
-void * _mysqlnd_pecalloc(unsigned int nmemb, size_t size, zend_bool persistent MYSQLND_MEM_D)
+static void * _mysqlnd_pecalloc(unsigned int nmemb, size_t size, zend_bool persistent MYSQLND_MEM_D)
{
void *ret;
zend_bool collect_memory_statistics = MYSQLND_G(collect_memory_statistics);
@@ -208,8 +210,8 @@ void * _mysqlnd_pecalloc(unsigned int nmemb, size_t size, zend_bool persistent M
TRACE_ALLOC_ENTER(mysqlnd_pecalloc_name);
#if PHP_DEBUG
{
- char * fn = strrchr(__zend_orig_filename, PHP_DIR_SEPARATOR);
- TRACE_ALLOC_INF_FMT("file=%-15s line=%4d", fn? fn + 1:__zend_orig_filename, __zend_orig_lineno);
+ char * fn = strrchr(__zend_filename, PHP_DIR_SEPARATOR);
+ TRACE_ALLOC_INF_FMT("file=%-15s line=%4d", fn? fn + 1:__zend_filename, __zend_lineno);
}
#endif
@@ -217,7 +219,7 @@ void * _mysqlnd_pecalloc(unsigned int nmemb, size_t size, zend_bool persistent M
/* -1 is also "true" */
if (*threshold) {
#endif
- ret = (persistent) ? __zend_calloc(nmemb, REAL_SIZE(size)) : _ecalloc(nmemb, REAL_SIZE(size) ZEND_FILE_LINE_CC ZEND_FILE_LINE_ORIG_RELAY_CC);
+ ret = pecalloc_rel(nmemb, REAL_SIZE(size), persistent);
#if PHP_DEBUG
--*threshold;
} else if (*threshold == 0) {
@@ -240,7 +242,7 @@ void * _mysqlnd_pecalloc(unsigned int nmemb, size_t size, zend_bool persistent M
/* {{{ _mysqlnd_erealloc */
-void * _mysqlnd_erealloc(void *ptr, size_t new_size MYSQLND_MEM_D)
+static void * _mysqlnd_erealloc(void *ptr, size_t new_size MYSQLND_MEM_D)
{
void *ret;
zend_bool collect_memory_statistics = MYSQLND_G(collect_memory_statistics);
@@ -252,8 +254,8 @@ void * _mysqlnd_erealloc(void *ptr, size_t new_size MYSQLND_MEM_D)
#if PHP_DEBUG
{
- char * fn = strrchr(__zend_orig_filename, PHP_DIR_SEPARATOR);
- TRACE_ALLOC_INF_FMT("file=%-15s line=%4d", fn? fn + 1:__zend_orig_filename, __zend_orig_lineno);
+ char * fn = strrchr(__zend_filename, PHP_DIR_SEPARATOR);
+ TRACE_ALLOC_INF_FMT("file=%-15s line=%4d", fn? fn + 1:__zend_filename, __zend_lineno);
}
#endif
TRACE_ALLOC_INF_FMT("ptr=%p old_size=%lu, new_size=%lu", ptr, old_size, new_size);
@@ -262,7 +264,7 @@ void * _mysqlnd_erealloc(void *ptr, size_t new_size MYSQLND_MEM_D)
/* -1 is also "true" */
if (*threshold) {
#endif
- ret = _erealloc(REAL_PTR(ptr), REAL_SIZE(new_size) ZEND_FILE_LINE_CC ZEND_FILE_LINE_ORIG_RELAY_CC);
+ ret = erealloc_rel(REAL_PTR(ptr), REAL_SIZE(new_size));
#if PHP_DEBUG
--*threshold;
} else if (*threshold == 0) {
@@ -281,7 +283,7 @@ void * _mysqlnd_erealloc(void *ptr, size_t new_size MYSQLND_MEM_D)
/* {{{ _mysqlnd_perealloc */
-void * _mysqlnd_perealloc(void *ptr, size_t new_size, zend_bool persistent MYSQLND_MEM_D)
+static void * _mysqlnd_perealloc(void *ptr, size_t new_size, zend_bool persistent MYSQLND_MEM_D)
{
void *ret;
zend_bool collect_memory_statistics = MYSQLND_G(collect_memory_statistics);
@@ -293,8 +295,8 @@ void * _mysqlnd_perealloc(void *ptr, size_t new_size, zend_bool persistent MYSQL
#if PHP_DEBUG
{
- char * fn = strrchr(__zend_orig_filename, PHP_DIR_SEPARATOR);
- TRACE_ALLOC_INF_FMT("file=%-15s line=%4d", fn? fn + 1:__zend_orig_filename, __zend_orig_lineno);
+ char * fn = strrchr(__zend_filename, PHP_DIR_SEPARATOR);
+ TRACE_ALLOC_INF_FMT("file=%-15s line=%4d", fn? fn + 1:__zend_filename, __zend_lineno);
}
#endif
TRACE_ALLOC_INF_FMT("ptr=%p old_size=%lu new_size=%lu persistent=%u", ptr, old_size, new_size, persistent);
@@ -303,7 +305,7 @@ void * _mysqlnd_perealloc(void *ptr, size_t new_size, zend_bool persistent MYSQL
/* -1 is also "true" */
if (*threshold) {
#endif
- ret = perealloc(REAL_PTR(ptr), REAL_SIZE(new_size), persistent);
+ ret = perealloc_rel(REAL_PTR(ptr), REAL_SIZE(new_size), persistent);
#if PHP_DEBUG
--*threshold;
} else if (*threshold == 0) {
@@ -325,7 +327,7 @@ void * _mysqlnd_perealloc(void *ptr, size_t new_size, zend_bool persistent MYSQL
/* {{{ _mysqlnd_efree */
-void _mysqlnd_efree(void *ptr MYSQLND_MEM_D)
+static void _mysqlnd_efree(void *ptr MYSQLND_MEM_D)
{
size_t free_amount = 0;
zend_bool collect_memory_statistics = MYSQLND_G(collect_memory_statistics);
@@ -333,8 +335,8 @@ void _mysqlnd_efree(void *ptr MYSQLND_MEM_D)
#if PHP_DEBUG
{
- char * fn = strrchr(__zend_orig_filename, PHP_DIR_SEPARATOR);
- TRACE_ALLOC_INF_FMT("file=%-15s line=%4d", fn? fn + 1:__zend_orig_filename, __zend_orig_lineno);
+ char * fn = strrchr(__zend_filename, PHP_DIR_SEPARATOR);
+ TRACE_ALLOC_INF_FMT("file=%-15s line=%4d", fn? fn + 1:__zend_filename, __zend_lineno);
}
#endif
TRACE_ALLOC_INF_FMT("ptr=%p", ptr);
@@ -344,7 +346,7 @@ void _mysqlnd_efree(void *ptr MYSQLND_MEM_D)
free_amount = *(size_t *)(((char*)ptr) - sizeof(size_t));
TRACE_ALLOC_INF_FMT("ptr=%p size=%u", ((char*)ptr) - sizeof(size_t), (unsigned int) free_amount);
}
- _efree(REAL_PTR(ptr) ZEND_FILE_LINE_CC ZEND_FILE_LINE_ORIG_RELAY_CC);
+ efree_rel(REAL_PTR(ptr));
}
if (collect_memory_statistics) {
@@ -356,7 +358,7 @@ void _mysqlnd_efree(void *ptr MYSQLND_MEM_D)
/* {{{ _mysqlnd_pefree */
-void _mysqlnd_pefree(void *ptr, zend_bool persistent MYSQLND_MEM_D)
+static void _mysqlnd_pefree(void *ptr, zend_bool persistent MYSQLND_MEM_D)
{
size_t free_amount = 0;
zend_bool collect_memory_statistics = MYSQLND_G(collect_memory_statistics);
@@ -364,8 +366,8 @@ void _mysqlnd_pefree(void *ptr, zend_bool persistent MYSQLND_MEM_D)
#if PHP_DEBUG
{
- char * fn = strrchr(__zend_orig_filename, PHP_DIR_SEPARATOR);
- TRACE_ALLOC_INF_FMT("file=%-15s line=%4d", fn? fn + 1:__zend_orig_filename, __zend_orig_lineno);
+ char * fn = strrchr(__zend_filename, PHP_DIR_SEPARATOR);
+ TRACE_ALLOC_INF_FMT("file=%-15s line=%4d", fn? fn + 1:__zend_filename, __zend_lineno);
}
#endif
TRACE_ALLOC_INF_FMT("ptr=%p persistent=%u", ptr, persistent);
@@ -375,7 +377,7 @@ void _mysqlnd_pefree(void *ptr, zend_bool persistent MYSQLND_MEM_D)
free_amount = *(size_t *)(((char*)ptr) - sizeof(size_t));
TRACE_ALLOC_INF_FMT("ptr=%p size=%u", ((char*)ptr) - sizeof(size_t), (unsigned int) free_amount);
}
- (persistent) ? free(REAL_PTR(ptr)) : _efree(REAL_PTR(ptr) ZEND_FILE_LINE_CC ZEND_FILE_LINE_ORIG_RELAY_CC);
+ pefree_rel(REAL_PTR(ptr), persistent);
}
if (collect_memory_statistics) {
@@ -388,7 +390,7 @@ void _mysqlnd_pefree(void *ptr, zend_bool persistent MYSQLND_MEM_D)
/* {{{ _mysqlnd_malloc */
-void * _mysqlnd_malloc(size_t size MYSQLND_MEM_D)
+static void * _mysqlnd_malloc(size_t size MYSQLND_MEM_D)
{
void *ret;
zend_bool collect_memory_statistics = MYSQLND_G(collect_memory_statistics);
@@ -399,8 +401,8 @@ void * _mysqlnd_malloc(size_t size MYSQLND_MEM_D)
#if PHP_DEBUG
{
- char * fn = strrchr(__zend_orig_filename, PHP_DIR_SEPARATOR);
- TRACE_ALLOC_INF_FMT("file=%-15s line=%4d", fn? fn + 1:__zend_orig_filename, __zend_orig_lineno);
+ char * fn = strrchr(__zend_filename, PHP_DIR_SEPARATOR);
+ TRACE_ALLOC_INF_FMT("file=%-15s line=%4d", fn? fn + 1:__zend_filename, __zend_lineno);
}
#endif
@@ -427,7 +429,7 @@ void * _mysqlnd_malloc(size_t size MYSQLND_MEM_D)
/* {{{ _mysqlnd_calloc */
-void * _mysqlnd_calloc(unsigned int nmemb, size_t size MYSQLND_MEM_D)
+static void * _mysqlnd_calloc(unsigned int nmemb, size_t size MYSQLND_MEM_D)
{
void *ret;
zend_bool collect_memory_statistics = MYSQLND_G(collect_memory_statistics);
@@ -438,8 +440,8 @@ void * _mysqlnd_calloc(unsigned int nmemb, size_t size MYSQLND_MEM_D)
#if PHP_DEBUG
{
- char * fn = strrchr(__zend_orig_filename, PHP_DIR_SEPARATOR);
- TRACE_ALLOC_INF_FMT("file=%-15s line=%4d", fn? fn + 1:__zend_orig_filename, __zend_orig_lineno);
+ char * fn = strrchr(__zend_filename, PHP_DIR_SEPARATOR);
+ TRACE_ALLOC_INF_FMT("file=%-15s line=%4d", fn? fn + 1:__zend_filename, __zend_lineno);
}
#endif
@@ -466,7 +468,7 @@ void * _mysqlnd_calloc(unsigned int nmemb, size_t size MYSQLND_MEM_D)
/* {{{ _mysqlnd_realloc */
-void * _mysqlnd_realloc(void *ptr, size_t new_size MYSQLND_MEM_D)
+static void * _mysqlnd_realloc(void *ptr, size_t new_size MYSQLND_MEM_D)
{
void *ret;
zend_bool collect_memory_statistics = MYSQLND_G(collect_memory_statistics);
@@ -477,8 +479,8 @@ void * _mysqlnd_realloc(void *ptr, size_t new_size MYSQLND_MEM_D)
#if PHP_DEBUG
{
- char * fn = strrchr(__zend_orig_filename, PHP_DIR_SEPARATOR);
- TRACE_ALLOC_INF_FMT("file=%-15s line=%4d", fn? fn + 1:__zend_orig_filename, __zend_orig_lineno);
+ char * fn = strrchr(__zend_filename, PHP_DIR_SEPARATOR);
+ TRACE_ALLOC_INF_FMT("file=%-15s line=%4d", fn? fn + 1:__zend_filename, __zend_lineno);
}
#endif
TRACE_ALLOC_INF_FMT("ptr=%p new_size=%lu ", new_size, ptr);
@@ -508,7 +510,7 @@ void * _mysqlnd_realloc(void *ptr, size_t new_size MYSQLND_MEM_D)
/* {{{ _mysqlnd_free */
-void _mysqlnd_free(void *ptr MYSQLND_MEM_D)
+static void _mysqlnd_free(void *ptr MYSQLND_MEM_D)
{
size_t free_amount = 0;
zend_bool collect_memory_statistics = MYSQLND_G(collect_memory_statistics);
@@ -516,8 +518,8 @@ void _mysqlnd_free(void *ptr MYSQLND_MEM_D)
#if PHP_DEBUG
{
- char * fn = strrchr(__zend_orig_filename, PHP_DIR_SEPARATOR);
- TRACE_ALLOC_INF_FMT("file=%-15s line=%4d", fn? fn + 1:__zend_orig_filename, __zend_orig_lineno);
+ char * fn = strrchr(__zend_filename, PHP_DIR_SEPARATOR);
+ TRACE_ALLOC_INF_FMT("file=%-15s line=%4d", fn? fn + 1:__zend_filename, __zend_lineno);
}
#endif
TRACE_ALLOC_INF_FMT("ptr=%p", ptr);
@@ -537,13 +539,40 @@ void _mysqlnd_free(void *ptr MYSQLND_MEM_D)
}
/* }}} */
-#define SMART_STR_START_SIZE 2048
-#define SMART_STR_PREALLOC 512
-#include "zend_smart_str.h"
+
+/* {{{ _mysqlnd_pememdup */
+static char * _mysqlnd_pememdup(const char * const ptr, size_t length, zend_bool persistent MYSQLND_MEM_D)
+{
+ char * ret;
+ zend_bool collect_memory_statistics = MYSQLND_G(collect_memory_statistics);
+ TRACE_ALLOC_ENTER(mysqlnd_pememdup_name);
+
+#if PHP_DEBUG
+ {
+ char * fn = strrchr(__zend_filename, PHP_DIR_SEPARATOR);
+ TRACE_ALLOC_INF_FMT("file=%-15s line=%4d", fn? fn + 1:__zend_filename, __zend_lineno);
+ }
+#endif
+ TRACE_ALLOC_INF_FMT("ptr=%p", ptr);
+
+ ret = pemalloc_rel(REAL_SIZE(length + 1), persistent);
+ {
+ char * dest = (char *) FAKE_PTR(ret);
+ memcpy(dest, ptr, length);
+ }
+
+ if (collect_memory_statistics) {
+ *(size_t *) ret = length;
+ MYSQLND_INC_GLOBAL_STATISTIC(persistent? STAT_MEM_DUP_COUNT : STAT_MEM_EDUP_COUNT);
+ }
+
+ TRACE_ALLOC_RETURN(FAKE_PTR(ret));
+}
+/* }}} */
/* {{{ _mysqlnd_pestrndup */
-char * _mysqlnd_pestrndup(const char * const ptr, size_t length, zend_bool persistent MYSQLND_MEM_D)
+static char * _mysqlnd_pestrndup(const char * const ptr, size_t length, zend_bool persistent MYSQLND_MEM_D)
{
char * ret;
zend_bool collect_memory_statistics = MYSQLND_G(collect_memory_statistics);
@@ -551,13 +580,13 @@ char * _mysqlnd_pestrndup(const char * const ptr, size_t length, zend_bool persi
#if PHP_DEBUG
{
- char * fn = strrchr(__zend_orig_filename, PHP_DIR_SEPARATOR);
- TRACE_ALLOC_INF_FMT("file=%-15s line=%4d", fn? fn + 1:__zend_orig_filename, __zend_orig_lineno);
+ char * fn = strrchr(__zend_filename, PHP_DIR_SEPARATOR);
+ TRACE_ALLOC_INF_FMT("file=%-15s line=%4d", fn? fn + 1:__zend_filename, __zend_lineno);
}
#endif
TRACE_ALLOC_INF_FMT("ptr=%p", ptr);
- ret = (persistent) ? __zend_malloc(REAL_SIZE(length + 1)) : _emalloc(REAL_SIZE(length + 1) ZEND_FILE_LINE_CC ZEND_FILE_LINE_ORIG_RELAY_CC);
+ ret = pemalloc_rel(REAL_SIZE(length + 1), persistent);
{
size_t l = length;
char * p = (char *) ptr;
@@ -578,8 +607,13 @@ char * _mysqlnd_pestrndup(const char * const ptr, size_t length, zend_bool persi
/* }}} */
+#define SMART_STR_START_SIZE 2048
+#define SMART_STR_PREALLOC 512
+#include "zend_smart_str.h"
+
+
/* {{{ _mysqlnd_pestrdup */
-char * _mysqlnd_pestrdup(const char * const ptr, zend_bool persistent MYSQLND_MEM_D)
+static char * _mysqlnd_pestrdup(const char * const ptr, zend_bool persistent MYSQLND_MEM_D)
{
char * ret;
smart_str tmp_str = {0, 0};
@@ -588,8 +622,8 @@ char * _mysqlnd_pestrdup(const char * const ptr, zend_bool persistent MYSQLND_ME
TRACE_ALLOC_ENTER(mysqlnd_pestrdup_name);
#if PHP_DEBUG
{
- char * fn = strrchr(__zend_orig_filename, PHP_DIR_SEPARATOR);
- TRACE_ALLOC_INF_FMT("file=%-15s line=%4d", fn? fn + 1:__zend_orig_filename, __zend_orig_lineno);
+ char * fn = strrchr(__zend_filename, PHP_DIR_SEPARATOR);
+ TRACE_ALLOC_INF_FMT("file=%-15s line=%4d", fn? fn + 1:__zend_filename, __zend_lineno);
}
#endif
TRACE_ALLOC_INF_FMT("ptr=%p", ptr);
@@ -597,7 +631,7 @@ char * _mysqlnd_pestrdup(const char * const ptr, zend_bool persistent MYSQLND_ME
smart_str_appendc(&tmp_str, *p);
} while (*p++);
- ret = (persistent) ? __zend_malloc(ZSTR_LEN(tmp_str.s) + sizeof(size_t)) : _emalloc(REAL_SIZE(ZSTR_LEN(tmp_str.s) + sizeof(size_t)) ZEND_FILE_LINE_CC ZEND_FILE_LINE_ORIG_RELAY_CC);
+ ret = pemalloc_rel(ZSTR_LEN(tmp_str.s) + sizeof(size_t), persistent);
memcpy(FAKE_PTR(ret), ZSTR_VAL(tmp_str.s), ZSTR_LEN(tmp_str.s));
if (ret && collect_memory_statistics) {
@@ -611,8 +645,20 @@ char * _mysqlnd_pestrdup(const char * const ptr, zend_bool persistent MYSQLND_ME
/* }}} */
+
+
+#endif /* MYSQLND_DEBUG_MEMORY */
+
+/* {{{ _mysqlnd_sprintf_free */
+static void _mysqlnd_sprintf_free(char * p)
+{
+ efree(p);
+}
+/* }}} */
+
+
/* {{{ _mysqlnd_sprintf */
-PHPAPI int _mysqlnd_sprintf(char ** pbuf, size_t max_len, const char *format, ...)
+static int _mysqlnd_sprintf(char ** pbuf, size_t max_len, const char *format, ...)
{
int len;
va_list ap;
@@ -623,30 +669,21 @@ PHPAPI int _mysqlnd_sprintf(char ** pbuf, size_t max_len, const char *format, ..
}
/* }}} */
-
-/* {{{ _mysqlnd_sprintf_free */
-PHPAPI void _mysqlnd_sprintf_free(char * p)
-{
- efree(p);
-}
-/* }}} */
-
/* {{{ _mysqlnd_vsprintf */
-PHPAPI int _mysqlnd_vsprintf(char ** pbuf, size_t max_len, const char * format, va_list ap)
+static int _mysqlnd_vsprintf(char ** pbuf, size_t max_len, const char * format, va_list ap)
{
return vspprintf(pbuf, max_len, format, ap);
}
/* }}} */
-#define MYSQLND_DEBUG_MEMORY 1
#if MYSQLND_DEBUG_MEMORY == 0
/* {{{ mysqlnd_zend_mm_emalloc */
static void * mysqlnd_zend_mm_emalloc(size_t size MYSQLND_MEM_D)
{
- return emalloc(size);
+ return emalloc_rel(size);
}
/* }}} */
@@ -654,7 +691,7 @@ static void * mysqlnd_zend_mm_emalloc(size_t size MYSQLND_MEM_D)
/* {{{ mysqlnd_zend_mm_pemalloc */
static void * mysqlnd_zend_mm_pemalloc(size_t size, zend_bool persistent MYSQLND_MEM_D)
{
- return pemalloc(size, persistent);
+ return pemalloc_rel(size, persistent);
}
/* }}} */
@@ -662,7 +699,7 @@ static void * mysqlnd_zend_mm_pemalloc(size_t size, zend_bool persistent MYSQLND
/* {{{ mysqlnd_zend_mm_ecalloc */
static void * mysqlnd_zend_mm_ecalloc(unsigned int nmemb, size_t size MYSQLND_MEM_D)
{
- return ecalloc(nmemb, size);
+ return ecalloc_rel(nmemb, size);
}
/* }}} */
@@ -670,7 +707,7 @@ static void * mysqlnd_zend_mm_ecalloc(unsigned int nmemb, size_t size MYSQLND_ME
/* {{{ mysqlnd_zend_mm_pecalloc */
static void * mysqlnd_zend_mm_pecalloc(unsigned int nmemb, size_t size, zend_bool persistent MYSQLND_MEM_D)
{
- return pecalloc(nmemb, size, persistent);
+ return pecalloc_rel(nmemb, size, persistent);
}
/* }}} */
@@ -678,7 +715,7 @@ static void * mysqlnd_zend_mm_pecalloc(unsigned int nmemb, size_t size, zend_boo
/* {{{ mysqlnd_zend_mm_erealloc */
static void * mysqlnd_zend_mm_erealloc(void *ptr, size_t new_size MYSQLND_MEM_D)
{
- return erealloc(ptr, new_size);
+ return erealloc_rel(ptr, new_size);
}
/* }}} */
@@ -686,7 +723,7 @@ static void * mysqlnd_zend_mm_erealloc(void *ptr, size_t new_size MYSQLND_MEM_D)
/* {{{ mysqlnd_zend_mm_perealloc */
static void * mysqlnd_zend_mm_perealloc(void *ptr, size_t new_size, zend_bool persistent MYSQLND_MEM_D)
{
- return perealloc(ptr, new_size, persistent);
+ return perealloc_rel(ptr, new_size, persistent);
}
/* }}} */
@@ -694,7 +731,7 @@ static void * mysqlnd_zend_mm_perealloc(void *ptr, size_t new_size, zend_bool pe
/* {{{ mysqlnd_zend_mm_efree */
static void mysqlnd_zend_mm_efree(void * ptr MYSQLND_MEM_D)
{
- efree(ptr);
+ efree_rel(ptr);
}
/* }}} */
@@ -702,7 +739,7 @@ static void mysqlnd_zend_mm_efree(void * ptr MYSQLND_MEM_D)
/* {{{ mysqlnd_zend_mm_pefree */
static void mysqlnd_zend_mm_pefree(void * ptr, zend_bool persistent MYSQLND_MEM_D)
{
- pefree(ptr, persistent);
+ pefree_rel(ptr, persistent);
}
/* }}} */
@@ -739,10 +776,22 @@ static void mysqlnd_zend_mm_free(void * ptr MYSQLND_MEM_D)
/* }}} */
+/* {{{ mysqlnd_zend_mm_pememdup */
+static char * mysqlnd_zend_mm_pememdup(const char * const ptr, size_t length, zend_bool persistent MYSQLND_MEM_D)
+{
+ char * dest = pemalloc_rel(length, persistent);
+ if (dest) {
+ memcpy(dest, ptr, length);
+ }
+ return dest;
+}
+/* }}} */
+
+
/* {{{ mysqlnd_zend_mm_pestrndup */
static char * mysqlnd_zend_mm_pestrndup(const char * const ptr, size_t length, zend_bool persistent MYSQLND_MEM_D)
{
- return pestrndup(ptr, length, persistent);
+ return persistent? zend_strndup(ptr, length ) : estrndup_rel(ptr, length);
}
/* }}} */
@@ -750,7 +799,7 @@ static char * mysqlnd_zend_mm_pestrndup(const char * const ptr, size_t length, z
/* {{{ mysqlnd_zend_mm_pestrdup */
static char * mysqlnd_zend_mm_pestrdup(const char * const ptr, zend_bool persistent MYSQLND_MEM_D)
{
- return pestrdup(ptr, persistent);
+ return pestrdup_rel(ptr, persistent);
}
/* }}} */
@@ -759,7 +808,7 @@ static char * mysqlnd_zend_mm_pestrdup(const char * const ptr, zend_bool persist
PHPAPI struct st_mysqlnd_allocator_methods mysqlnd_allocator =
{
-#if MYSQLND_DEBUG_MEMORY
+#if MYSQLND_DEBUG_MEMORY == 1
_mysqlnd_emalloc,
_mysqlnd_pemalloc,
_mysqlnd_ecalloc,
@@ -772,6 +821,7 @@ PHPAPI struct st_mysqlnd_allocator_methods mysqlnd_allocator =
_mysqlnd_calloc,
_mysqlnd_realloc,
_mysqlnd_free,
+ _mysqlnd_pememdup,
_mysqlnd_pestrndup,
_mysqlnd_pestrdup,
_mysqlnd_sprintf,
@@ -790,10 +840,12 @@ PHPAPI struct st_mysqlnd_allocator_methods mysqlnd_allocator =
mysqlnd_zend_mm_calloc,
mysqlnd_zend_mm_realloc,
mysqlnd_zend_mm_free,
+ mysqlnd_zend_mm_pememdup,
mysqlnd_zend_mm_pestrndup,
- mysqlnd_zend_mm_pestrdup
- sprintf,
- mysqlnd_zend_mm_efree,
+ mysqlnd_zend_mm_pestrdup,
+ _mysqlnd_sprintf,
+ _mysqlnd_vsprintf,
+ _mysqlnd_sprintf_free,
#endif
};
diff --git a/ext/mysqlnd/mysqlnd_alloc.h b/ext/mysqlnd/mysqlnd_alloc.h
index 92d144954c..894da77823 100644
--- a/ext/mysqlnd/mysqlnd_alloc.h
+++ b/ext/mysqlnd/mysqlnd_alloc.h
@@ -14,7 +14,6 @@
+----------------------------------------------------------------------+
| Authors: Andrey Hristov <andrey@php.net> |
| Ulf Wendel <uw@php.net> |
- | Georg Richter <georg@php.net> |
+----------------------------------------------------------------------+
*/
@@ -23,7 +22,7 @@
PHPAPI extern const char * mysqlnd_debug_std_no_trace_funcs[];
-#define MYSQLND_MEM_D ZEND_FILE_LINE_ORIG_DC
+#define MYSQLND_MEM_D ZEND_FILE_LINE_DC
#define MYSQLND_MEM_C ZEND_FILE_LINE_CC
struct st_mysqlnd_allocator_methods
@@ -40,6 +39,7 @@ struct st_mysqlnd_allocator_methods
void * (*m_calloc)(unsigned int nmemb, size_t size MYSQLND_MEM_D);
void * (*m_realloc)(void *ptr, size_t new_size MYSQLND_MEM_D);
void (*m_free)(void *ptr MYSQLND_MEM_D);
+ char * (*m_pememdup)(const char * const ptr, size_t size, zend_bool persistent MYSQLND_MEM_D);
char * (*m_pestrndup)(const char * const ptr, size_t size, zend_bool persistent MYSQLND_MEM_D);
char * (*m_pestrdup)(const char * const ptr, zend_bool persistent MYSQLND_MEM_D);
int (*m_sprintf)(char **pbuf, size_t max_len, const char *format, ...);
@@ -49,24 +49,6 @@ struct st_mysqlnd_allocator_methods
PHPAPI extern struct st_mysqlnd_allocator_methods mysqlnd_allocator;
-PHPAPI void * _mysqlnd_emalloc(size_t size MYSQLND_MEM_D);
-PHPAPI void * _mysqlnd_pemalloc(size_t size, zend_bool persistent MYSQLND_MEM_D);
-PHPAPI void * _mysqlnd_ecalloc(unsigned int nmemb, size_t size MYSQLND_MEM_D);
-PHPAPI void * _mysqlnd_pecalloc(unsigned int nmemb, size_t size, zend_bool persistent MYSQLND_MEM_D);
-PHPAPI void * _mysqlnd_erealloc(void *ptr, size_t new_size MYSQLND_MEM_D);
-PHPAPI void * _mysqlnd_perealloc(void *ptr, size_t new_size, zend_bool persistent MYSQLND_MEM_D);
-PHPAPI void _mysqlnd_efree(void *ptr MYSQLND_MEM_D);
-PHPAPI void _mysqlnd_pefree(void *ptr, zend_bool persistent MYSQLND_MEM_D);
-PHPAPI void * _mysqlnd_malloc(size_t size MYSQLND_MEM_D);
-PHPAPI void * _mysqlnd_calloc(unsigned int nmemb, size_t size MYSQLND_MEM_D);
-PHPAPI void * _mysqlnd_realloc(void *ptr, size_t new_size MYSQLND_MEM_D);
-PHPAPI void _mysqlnd_free(void *ptr MYSQLND_MEM_D);
-PHPAPI char * _mysqlnd_pestrndup(const char * const ptr, size_t size, zend_bool persistent MYSQLND_MEM_D);
-PHPAPI char * _mysqlnd_pestrdup(const char * const ptr, zend_bool persistent MYSQLND_MEM_D);
-PHPAPI int _mysqlnd_sprintf(char **pbuf, size_t max_len, const char *format, ...);
-PHPAPI void _mysqlnd_sprintf_free(char * p);
-PHPAPI int _mysqlnd_vsprintf(char **pbuf, size_t max_len, const char *format, va_list ap);
-
#define mnd_emalloc(size) mysqlnd_allocator.m_emalloc((size) MYSQLND_MEM_C)
#define mnd_pemalloc(size, pers) mysqlnd_allocator.m_pemalloc((size), (pers) MYSQLND_MEM_C)
#define mnd_ecalloc(nmemb, size) mysqlnd_allocator.m_ecalloc((nmemb), (size) MYSQLND_MEM_C)
@@ -79,12 +61,29 @@ PHPAPI int _mysqlnd_vsprintf(char **pbuf, size_t max_len, const char *format, v
#define mnd_calloc(nmemb, size) mysqlnd_allocator.m_calloc((nmemb), (size) MYSQLND_MEM_C)
#define mnd_realloc(ptr, new_size) mysqlnd_allocator.m_realloc((ptr), (new_size) MYSQLND_MEM_C)
#define mnd_free(ptr) mysqlnd_allocator.m_free((ptr) MYSQLND_MEM_C)
+#define mnd_pememdup(ptr, size, pers) mysqlnd_allocator.m_pememdup((ptr), (size), (pers) MYSQLND_MEM_C)
#define mnd_pestrndup(ptr, size, pers) mysqlnd_allocator.m_pestrndup((ptr), (size), (pers) MYSQLND_MEM_C)
#define mnd_pestrdup(ptr, pers) mysqlnd_allocator.m_pestrdup((ptr), (pers) MYSQLND_MEM_C)
#define mnd_sprintf(p, mx_len, fmt,...) mysqlnd_allocator.m_sprintf((p), (mx_len), (fmt), __VA_ARGS__)
#define mnd_vsprintf(p, mx_len, fmt,ap) mysqlnd_allocator.m_vsprintf((p), (mx_len), (fmt), (ap))
#define mnd_sprintf_free(p) mysqlnd_allocator.m_sprintf_free((p))
+static inline MYSQLND_STRING mnd_dup_cstring(const MYSQLND_CSTRING str, const zend_bool persistent)
+{
+ const MYSQLND_STRING ret = {(char*) mnd_pemalloc(str.l + 1, persistent), str.l};
+ if (ret.s) {
+ memcpy(ret.s, str.s, str.l);
+ ret.s[str.l] = '\0';
+ }
+ return ret;
+}
+
+static inline MYSQLND_CSTRING mnd_str2c(const MYSQLND_STRING str)
+{
+ const MYSQLND_CSTRING ret = {str.s, str.l};
+ return ret;
+}
+
#endif /* MYSQLND_ALLOC_H */
/*
diff --git a/ext/mysqlnd/mysqlnd_auth.c b/ext/mysqlnd/mysqlnd_auth.c
index 7c9cead854..6cbd3e1149 100644
--- a/ext/mysqlnd/mysqlnd_auth.c
+++ b/ext/mysqlnd/mysqlnd_auth.c
@@ -14,20 +14,220 @@
+----------------------------------------------------------------------+
| Authors: Andrey Hristov <andrey@php.net> |
| Ulf Wendel <uw@php.net> |
- | Georg Richter <georg@php.net> |
+----------------------------------------------------------------------+
*/
-/* $Id: mysqlnd.c 307377 2011-01-11 13:02:57Z andrey $ */
#include "php.h"
#include "mysqlnd.h"
#include "mysqlnd_structs.h"
+#include "mysqlnd_auth.h"
#include "mysqlnd_wireprotocol.h"
+#include "mysqlnd_connection.h"
#include "mysqlnd_priv.h"
-#include "mysqlnd_result.h"
#include "mysqlnd_charset.h"
#include "mysqlnd_debug.h"
+static const char * const mysqlnd_old_passwd = "mysqlnd cannot connect to MySQL 4.1+ using the old insecure authentication. "
+"Please use an administration tool to reset your password with the command SET PASSWORD = PASSWORD('your_existing_password'). This will "
+"store a new, and more secure, hash value in mysql.user. If this user is used in other scripts executed by PHP 5.2 or earlier you might need to remove the old-passwords "
+"flag from your my.cnf file";
+
+
+/* {{{ mysqlnd_run_authentication */
+enum_func_status
+mysqlnd_run_authentication(
+ MYSQLND_CONN_DATA * conn,
+ const char * const user,
+ const char * const passwd,
+ const size_t passwd_len,
+ const char * const db,
+ const size_t db_len,
+ const MYSQLND_STRING auth_plugin_data,
+ const char * const auth_protocol,
+ unsigned int charset_no,
+ const MYSQLND_SESSION_OPTIONS * const session_options,
+ zend_ulong mysql_flags,
+ zend_bool silent,
+ zend_bool is_change_user
+ )
+{
+ enum_func_status ret = FAIL;
+ zend_bool first_call = TRUE;
+
+ char * switch_to_auth_protocol = NULL;
+ size_t switch_to_auth_protocol_len = 0;
+ char * requested_protocol = NULL;
+ zend_uchar * plugin_data;
+ size_t plugin_data_len;
+
+ DBG_ENTER("mysqlnd_run_authentication");
+
+ plugin_data_len = auth_plugin_data.l;
+ plugin_data = mnd_emalloc(plugin_data_len + 1);
+ if (!plugin_data) {
+ goto end;
+ }
+ memcpy(plugin_data, auth_plugin_data.s, plugin_data_len);
+ plugin_data[plugin_data_len] = '\0';
+
+ requested_protocol = mnd_pestrdup(auth_protocol? auth_protocol : MYSQLND_DEFAULT_AUTH_PROTOCOL, FALSE);
+ if (!requested_protocol) {
+ goto end;
+ }
+
+ do {
+ struct st_mysqlnd_authentication_plugin * auth_plugin = conn->m->fetch_auth_plugin_by_name(requested_protocol);
+
+ if (!auth_plugin) {
+ php_error_docref(NULL, E_WARNING, "The server requested authentication method unknown to the client [%s]", requested_protocol);
+ SET_CLIENT_ERROR(conn->error_info, CR_NOT_IMPLEMENTED, UNKNOWN_SQLSTATE, "The server requested authentication method unknown to the client");
+ goto end;
+ }
+ DBG_INF("plugin found");
+
+ {
+ zend_uchar * switch_to_auth_protocol_data = NULL;
+ size_t switch_to_auth_protocol_data_len = 0;
+ zend_uchar * scrambled_data = NULL;
+ size_t scrambled_data_len = 0;
+
+ switch_to_auth_protocol = NULL;
+ switch_to_auth_protocol_len = 0;
+
+ if (conn->authentication_plugin_data.s) {
+ mnd_pefree(conn->authentication_plugin_data.s, conn->persistent);
+ conn->authentication_plugin_data.s = NULL;
+ }
+ conn->authentication_plugin_data.l = plugin_data_len;
+ conn->authentication_plugin_data.s = mnd_pemalloc(conn->authentication_plugin_data.l, conn->persistent);
+ if (!conn->authentication_plugin_data.s) {
+ SET_OOM_ERROR(conn->error_info);
+ goto end;
+ }
+ memcpy(conn->authentication_plugin_data.s, plugin_data, plugin_data_len);
+
+ DBG_INF_FMT("salt(%d)=[%.*s]", plugin_data_len, plugin_data_len, plugin_data);
+ /* The data should be allocated with malloc() */
+ scrambled_data =
+ auth_plugin->methods.get_auth_data(NULL, &scrambled_data_len, conn, user, passwd, passwd_len,
+ plugin_data, plugin_data_len, session_options,
+ conn->protocol_frame_codec->data, mysql_flags);
+ if (conn->error_info->error_no) {
+ goto end;
+ }
+ if (FALSE == is_change_user) {
+ ret = mysqlnd_auth_handshake(conn, user, passwd, passwd_len, db, db_len, session_options, mysql_flags,
+ charset_no,
+ first_call,
+ requested_protocol,
+ scrambled_data, scrambled_data_len,
+ &switch_to_auth_protocol, &switch_to_auth_protocol_len,
+ &switch_to_auth_protocol_data, &switch_to_auth_protocol_data_len
+ );
+ } else {
+ ret = mysqlnd_auth_change_user(conn, user, strlen(user), passwd, passwd_len, db, db_len, silent,
+ first_call,
+ requested_protocol,
+ scrambled_data, scrambled_data_len,
+ &switch_to_auth_protocol, &switch_to_auth_protocol_len,
+ &switch_to_auth_protocol_data, &switch_to_auth_protocol_data_len
+ );
+ }
+ first_call = FALSE;
+ free(scrambled_data);
+
+ DBG_INF_FMT("switch_to_auth_protocol=%s", switch_to_auth_protocol? switch_to_auth_protocol:"n/a");
+ if (requested_protocol && switch_to_auth_protocol) {
+ mnd_efree(requested_protocol);
+ requested_protocol = switch_to_auth_protocol;
+ }
+
+ if (plugin_data) {
+ mnd_efree(plugin_data);
+ }
+ plugin_data_len = switch_to_auth_protocol_data_len;
+ plugin_data = switch_to_auth_protocol_data;
+ }
+ DBG_INF_FMT("conn->error_info->error_no = %d", conn->error_info->error_no);
+ } while (ret == FAIL && conn->error_info->error_no == 0 && switch_to_auth_protocol != NULL);
+
+ if (ret == PASS) {
+ DBG_INF_FMT("saving requested_protocol=%s", requested_protocol);
+ conn->m->set_client_option(conn, MYSQLND_OPT_AUTH_PROTOCOL, requested_protocol);
+ }
+end:
+ if (plugin_data) {
+ mnd_efree(plugin_data);
+ }
+ if (requested_protocol) {
+ mnd_efree(requested_protocol);
+ }
+
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_switch_to_ssl_if_needed */
+static enum_func_status
+mysqlnd_switch_to_ssl_if_needed(MYSQLND_CONN_DATA * conn,
+ unsigned int charset_no,
+ size_t server_capabilities,
+ const MYSQLND_SESSION_OPTIONS * const session_options,
+ zend_ulong mysql_flags)
+{
+ enum_func_status ret = FAIL;
+ const MYSQLND_CHARSET * charset;
+ DBG_ENTER("mysqlnd_switch_to_ssl_if_needed");
+
+ if (session_options->charset_name && (charset = mysqlnd_find_charset_name(session_options->charset_name))) {
+ charset_no = charset->nr;
+ }
+
+ {
+ size_t client_capabilities = mysql_flags;
+ struct st_mysqlnd_protocol_command * command = conn->command_factory(COM_ENABLE_SSL, conn, client_capabilities, server_capabilities, charset_no);
+ if (command) {
+ ret = command->run(command);
+ command->free_command(command);
+ }
+ }
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_connect_run_authentication */
+enum_func_status
+mysqlnd_connect_run_authentication(
+ MYSQLND_CONN_DATA * conn,
+ const char * const user,
+ const char * const passwd,
+ const char * const db,
+ size_t db_len,
+ size_t passwd_len,
+ MYSQLND_STRING authentication_plugin_data,
+ const char * const authentication_protocol,
+ const unsigned int charset_no,
+ size_t server_capabilities,
+ const MYSQLND_SESSION_OPTIONS * const session_options,
+ zend_ulong mysql_flags
+ )
+{
+ enum_func_status ret = FAIL;
+ DBG_ENTER("mysqlnd_connect_run_authentication");
+
+ ret = mysqlnd_switch_to_ssl_if_needed(conn, charset_no, server_capabilities, session_options, mysql_flags);
+ if (PASS == ret) {
+ ret = mysqlnd_run_authentication(conn, user, passwd, passwd_len, db, db_len,
+ authentication_plugin_data, authentication_protocol,
+ charset_no, session_options, mysql_flags, FALSE /*silent*/, FALSE/*is_change*/);
+ }
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
/* {{{ mysqlnd_auth_handshake */
enum_func_status
mysqlnd_auth_handshake(MYSQLND_CONN_DATA * conn,
@@ -36,7 +236,7 @@ mysqlnd_auth_handshake(MYSQLND_CONN_DATA * conn,
const size_t passwd_len,
const char * const db,
const size_t db_len,
- const MYSQLND_OPTIONS * const options,
+ const MYSQLND_SESSION_OPTIONS * const session_options,
zend_ulong mysql_flags,
unsigned int server_charset_no,
zend_bool use_full_blown_auth_packet,
@@ -57,34 +257,34 @@ mysqlnd_auth_handshake(MYSQLND_CONN_DATA * conn,
DBG_ENTER("mysqlnd_auth_handshake");
- auth_resp_packet = conn->protocol->m.get_auth_response_packet(conn->protocol, FALSE);
+ auth_resp_packet = conn->payload_decoder_factory->m.get_auth_response_packet(conn->payload_decoder_factory, FALSE);
if (!auth_resp_packet) {
- SET_OOM_ERROR(*conn->error_info);
+ SET_OOM_ERROR(conn->error_info);
goto end;
}
if (use_full_blown_auth_packet != TRUE) {
- change_auth_resp_packet = conn->protocol->m.get_change_auth_response_packet(conn->protocol, FALSE);
+ change_auth_resp_packet = conn->payload_decoder_factory->m.get_change_auth_response_packet(conn->payload_decoder_factory, FALSE);
if (!change_auth_resp_packet) {
- SET_OOM_ERROR(*conn->error_info);
+ SET_OOM_ERROR(conn->error_info);
goto end;
}
change_auth_resp_packet->auth_data = auth_plugin_data;
change_auth_resp_packet->auth_data_len = auth_plugin_data_len;
- if (!PACKET_WRITE(change_auth_resp_packet, conn)) {
- CONN_SET_STATE(conn, CONN_QUIT_SENT);
- SET_CLIENT_ERROR(*conn->error_info, CR_SERVER_GONE_ERROR, UNKNOWN_SQLSTATE, mysqlnd_server_gone);
+ if (!PACKET_WRITE(change_auth_resp_packet)) {
+ SET_CONNECTION_STATE(&conn->state, CONN_QUIT_SENT);
+ SET_CLIENT_ERROR(conn->error_info, CR_SERVER_GONE_ERROR, UNKNOWN_SQLSTATE, mysqlnd_server_gone);
goto end;
}
} else {
- auth_packet = conn->protocol->m.get_auth_packet(conn->protocol, FALSE);
+ auth_packet = conn->payload_decoder_factory->m.get_auth_packet(conn->payload_decoder_factory, FALSE);
auth_packet->client_flags = mysql_flags;
- auth_packet->max_packet_size = options->max_allowed_packet;
- if (options->charset_name && (charset = mysqlnd_find_charset_name(options->charset_name))) {
+ auth_packet->max_packet_size = session_options->max_allowed_packet;
+ if (session_options->charset_name && (charset = mysqlnd_find_charset_name(session_options->charset_name))) {
auth_packet->charset_no = charset->nr;
} else {
auth_packet->charset_no = server_charset_no;
@@ -103,7 +303,7 @@ mysqlnd_auth_handshake(MYSQLND_CONN_DATA * conn,
auth_packet->connect_attr = conn->options->connect_attr;
}
- if (!PACKET_WRITE(auth_packet, conn)) {
+ if (!PACKET_WRITE(auth_packet)) {
goto end;
}
}
@@ -111,12 +311,12 @@ mysqlnd_auth_handshake(MYSQLND_CONN_DATA * conn,
conn->charset = mysqlnd_find_charset_nr(auth_packet->charset_no);
}
- if (FAIL == PACKET_READ(auth_resp_packet, conn) || auth_resp_packet->response_code >= 0xFE) {
+ if (FAIL == PACKET_READ(auth_resp_packet) || auth_resp_packet->response_code >= 0xFE) {
if (auth_resp_packet->response_code == 0xFE) {
/* old authentication with new server !*/
if (!auth_resp_packet->new_auth_protocol) {
DBG_ERR(mysqlnd_old_passwd);
- SET_CLIENT_ERROR(*conn->error_info, CR_UNKNOWN_ERROR, UNKNOWN_SQLSTATE, mysqlnd_old_passwd);
+ SET_CLIENT_ERROR(conn->error_info, CR_UNKNOWN_ERROR, UNKNOWN_SQLSTATE, mysqlnd_old_passwd);
} else {
*switch_to_auth_protocol = mnd_pestrndup(auth_resp_packet->new_auth_protocol, auth_resp_packet->new_auth_protocol_len, FALSE);
*switch_to_auth_protocol_len = auth_resp_packet->new_auth_protocol_len;
@@ -134,12 +334,12 @@ mysqlnd_auth_handshake(MYSQLND_CONN_DATA * conn,
strlcpy(conn->error_info->sqlstate, auth_resp_packet->sqlstate, sizeof(conn->error_info->sqlstate));
DBG_ERR_FMT("ERROR:%u [SQLSTATE:%s] %s", auth_resp_packet->error_no, auth_resp_packet->sqlstate, auth_resp_packet->error);
}
- SET_CLIENT_ERROR(*conn->error_info, auth_resp_packet->error_no, UNKNOWN_SQLSTATE, auth_resp_packet->error);
+ SET_CLIENT_ERROR(conn->error_info, auth_resp_packet->error_no, UNKNOWN_SQLSTATE, auth_resp_packet->error);
}
goto end;
}
- SET_NEW_MESSAGE(conn->last_message, conn->last_message_len, auth_resp_packet->message, auth_resp_packet->message_len, conn->persistent);
+ SET_NEW_MESSAGE(conn->last_message.s, conn->last_message.l, auth_resp_packet->message, auth_resp_packet->message_len, conn->persistent);
ret = PASS;
end:
PACKET_FREE(change_auth_resp_packet);
@@ -178,33 +378,33 @@ mysqlnd_auth_change_user(MYSQLND_CONN_DATA * const conn,
DBG_ENTER("mysqlnd_auth_change_user");
- chg_user_resp = conn->protocol->m.get_change_user_response_packet(conn->protocol, FALSE);
+ chg_user_resp = conn->payload_decoder_factory->m.get_change_user_response_packet(conn->payload_decoder_factory, FALSE);
if (!chg_user_resp) {
- SET_OOM_ERROR(*conn->error_info);
+ SET_OOM_ERROR(conn->error_info);
goto end;
}
if (use_full_blown_auth_packet != TRUE) {
- change_auth_resp_packet = conn->protocol->m.get_change_auth_response_packet(conn->protocol, FALSE);
+ change_auth_resp_packet = conn->payload_decoder_factory->m.get_change_auth_response_packet(conn->payload_decoder_factory, FALSE);
if (!change_auth_resp_packet) {
- SET_OOM_ERROR(*conn->error_info);
+ SET_OOM_ERROR(conn->error_info);
goto end;
}
change_auth_resp_packet->auth_data = auth_plugin_data;
change_auth_resp_packet->auth_data_len = auth_plugin_data_len;
- if (!PACKET_WRITE(change_auth_resp_packet, conn)) {
- CONN_SET_STATE(conn, CONN_QUIT_SENT);
- SET_CLIENT_ERROR(*conn->error_info, CR_SERVER_GONE_ERROR, UNKNOWN_SQLSTATE, mysqlnd_server_gone);
+ if (!PACKET_WRITE(change_auth_resp_packet)) {
+ SET_CONNECTION_STATE(&conn->state, CONN_QUIT_SENT);
+ SET_CLIENT_ERROR(conn->error_info, CR_SERVER_GONE_ERROR, UNKNOWN_SQLSTATE, mysqlnd_server_gone);
goto end;
}
} else {
- auth_packet = conn->protocol->m.get_auth_packet(conn->protocol, FALSE);
+ auth_packet = conn->payload_decoder_factory->m.get_auth_packet(conn->payload_decoder_factory, FALSE);
if (!auth_packet) {
- SET_OOM_ERROR(*conn->error_info);
+ SET_OOM_ERROR(conn->error_info);
goto end;
}
@@ -223,21 +423,21 @@ mysqlnd_auth_change_user(MYSQLND_CONN_DATA * const conn,
auth_packet->charset_no = conn->charset->nr;
}
- if (!PACKET_WRITE(auth_packet, conn)) {
- CONN_SET_STATE(conn, CONN_QUIT_SENT);
- SET_CLIENT_ERROR(*conn->error_info, CR_SERVER_GONE_ERROR, UNKNOWN_SQLSTATE, mysqlnd_server_gone);
+ if (!PACKET_WRITE(auth_packet)) {
+ SET_CONNECTION_STATE(&conn->state, CONN_QUIT_SENT);
+ SET_CLIENT_ERROR(conn->error_info, CR_SERVER_GONE_ERROR, UNKNOWN_SQLSTATE, mysqlnd_server_gone);
goto end;
}
}
- ret = PACKET_READ(chg_user_resp, conn);
- COPY_CLIENT_ERROR(*conn->error_info, chg_user_resp->error_info);
+ ret = PACKET_READ(chg_user_resp);
+ COPY_CLIENT_ERROR(conn->error_info, chg_user_resp->error_info);
if (0xFE == chg_user_resp->response_code) {
ret = FAIL;
if (!chg_user_resp->new_auth_protocol) {
DBG_ERR(mysqlnd_old_passwd);
- SET_CLIENT_ERROR(*conn->error_info, CR_UNKNOWN_ERROR, UNKNOWN_SQLSTATE, mysqlnd_old_passwd);
+ SET_CLIENT_ERROR(conn->error_info, CR_UNKNOWN_ERROR, UNKNOWN_SQLSTATE, mysqlnd_old_passwd);
} else {
*switch_to_auth_protocol = mnd_pestrndup(chg_user_resp->new_auth_protocol, chg_user_resp->new_auth_protocol_len, FALSE);
*switch_to_auth_protocol_len = chg_user_resp->new_auth_protocol_len;
@@ -260,36 +460,36 @@ mysqlnd_auth_change_user(MYSQLND_CONN_DATA * const conn,
When it gets fixed, there should be one more check here
*/
if (conn->m->get_server_version(conn) > 50113L &&conn->m->get_server_version(conn) < 50118L) {
- MYSQLND_PACKET_OK * redundant_error_packet = conn->protocol->m.get_ok_packet(conn->protocol, FALSE);
+ MYSQLND_PACKET_OK * redundant_error_packet = conn->payload_decoder_factory->m.get_ok_packet(conn->payload_decoder_factory, FALSE);
if (redundant_error_packet) {
- PACKET_READ(redundant_error_packet, conn);
+ PACKET_READ(redundant_error_packet);
PACKET_FREE(redundant_error_packet);
DBG_INF_FMT("Server is %u, buggy, sends two ERR messages", conn->m->get_server_version(conn));
} else {
- SET_OOM_ERROR(*conn->error_info);
+ SET_OOM_ERROR(conn->error_info);
}
}
}
if (ret == PASS) {
char * tmp = NULL;
- /* if we get conn->user as parameter and then we first free it, then estrndup it, we will crash */
+ /* if we get conn->username as parameter and then we first free it, then estrndup it, we will crash */
tmp = mnd_pestrndup(user, user_len, conn->persistent);
- if (conn->user) {
- mnd_pefree(conn->user, conn->persistent);
+ if (conn->username.s) {
+ mnd_pefree(conn->username.s, conn->persistent);
}
- conn->user = tmp;
+ conn->username.s = tmp;
tmp = mnd_pestrdup(passwd, conn->persistent);
- if (conn->passwd) {
- mnd_pefree(conn->passwd, conn->persistent);
+ if (conn->password.s) {
+ mnd_pefree(conn->password.s, conn->persistent);
}
- conn->passwd = tmp;
+ conn->password.s = tmp;
- if (conn->last_message) {
- mnd_pefree(conn->last_message, conn->persistent);
- conn->last_message = NULL;
+ if (conn->last_message.s) {
+ mnd_pefree(conn->last_message.s, conn->persistent);
+ conn->last_message.s = NULL;
}
- memset(conn->upsert_status, 0, sizeof(*conn->upsert_status));
+ UPSERT_STATUS_RESET(conn->upsert_status);
/* set charset for old servers */
if (conn->m->get_server_version(conn) < 50123) {
ret = conn->m->set_charset(conn, old_cs->name);
@@ -297,7 +497,7 @@ mysqlnd_auth_change_user(MYSQLND_CONN_DATA * const conn,
} else if (ret == FAIL && chg_user_resp->server_asked_323_auth == TRUE) {
/* old authentication with new server !*/
DBG_ERR(mysqlnd_old_passwd);
- SET_CLIENT_ERROR(*conn->error_info, CR_UNKNOWN_ERROR, UNKNOWN_SQLSTATE, mysqlnd_old_passwd);
+ SET_CLIENT_ERROR(conn->error_info, CR_UNKNOWN_ERROR, UNKNOWN_SQLSTATE, mysqlnd_old_passwd);
}
end:
PACKET_FREE(change_auth_resp_packet);
@@ -325,7 +525,7 @@ php_mysqlnd_crypt(zend_uchar *buffer, const zend_uchar *s1, const zend_uchar *s2
/* {{{ php_mysqlnd_scramble */
-void php_mysqlnd_scramble(zend_uchar * const buffer, const zend_uchar * const scramble, const zend_uchar * const password, size_t password_len)
+void php_mysqlnd_scramble(zend_uchar * const buffer, const zend_uchar * const scramble, const zend_uchar * const password, const size_t password_len)
{
PHP_SHA1_CTX context;
zend_uchar sha1[SHA1_MAX_LENGTH];
@@ -359,8 +559,8 @@ mysqlnd_native_auth_get_auth_data(struct st_mysqlnd_authentication_plugin * self
size_t * auth_data_len,
MYSQLND_CONN_DATA * conn, const char * const user, const char * const passwd,
const size_t passwd_len, zend_uchar * auth_plugin_data, size_t auth_plugin_data_len,
- const MYSQLND_OPTIONS * const options,
- const MYSQLND_NET_OPTIONS * const net_options,
+ const MYSQLND_SESSION_OPTIONS * const session_options,
+ const MYSQLND_PFC_DATA * const pfc_data,
zend_ulong mysql_flags
)
{
@@ -371,7 +571,7 @@ mysqlnd_native_auth_get_auth_data(struct st_mysqlnd_authentication_plugin * self
/* 5.5.x reports 21 as scramble length because it needs to show the length of the data before the plugin name */
if (auth_plugin_data_len < SCRAMBLE_LENGTH) {
/* mysql_native_password only works with SCRAMBLE_LENGTH scramble */
- SET_CLIENT_ERROR(*conn->error_info, CR_MALFORMED_PACKET, UNKNOWN_SQLSTATE, "The server sent wrong length for scramble");
+ SET_CLIENT_ERROR(conn->error_info, CR_MALFORMED_PACKET, UNKNOWN_SQLSTATE, "The server sent wrong length for scramble");
DBG_ERR_FMT("The server sent wrong length for scramble %u. Expected %u", auth_plugin_data_len, SCRAMBLE_LENGTH);
DBG_RETURN(NULL);
}
@@ -419,8 +619,8 @@ mysqlnd_pam_auth_get_auth_data(struct st_mysqlnd_authentication_plugin * self,
size_t * auth_data_len,
MYSQLND_CONN_DATA * conn, const char * const user, const char * const passwd,
const size_t passwd_len, zend_uchar * auth_plugin_data, size_t auth_plugin_data_len,
- const MYSQLND_OPTIONS * const options,
- const MYSQLND_NET_OPTIONS * const net_options,
+ const MYSQLND_SESSION_OPTIONS * const session_options,
+ const MYSQLND_PFC_DATA * const pfc_data,
zend_ulong mysql_flags
)
{
@@ -480,18 +680,18 @@ mysqlnd_xor_string(char * dst, const size_t dst_len, const char * xor_str, const
/* {{{ mysqlnd_sha256_get_rsa_key */
static RSA *
mysqlnd_sha256_get_rsa_key(MYSQLND_CONN_DATA * conn,
- const MYSQLND_OPTIONS * const options,
- const MYSQLND_NET_OPTIONS * const net_options
+ const MYSQLND_SESSION_OPTIONS * const session_options,
+ const MYSQLND_PFC_DATA * const pfc_data
)
{
RSA * ret = NULL;
- const char * fname = (net_options->sha256_server_public_key && net_options->sha256_server_public_key[0] != '\0')?
- net_options->sha256_server_public_key:
+ const char * fname = (pfc_data->sha256_server_public_key && pfc_data->sha256_server_public_key[0] != '\0')?
+ pfc_data->sha256_server_public_key:
MYSQLND_G(sha256_server_public_key);
php_stream * stream;
DBG_ENTER("mysqlnd_sha256_get_rsa_key");
DBG_INF_FMT("options_s256_pk=[%s] MYSQLND_G(sha256_server_public_key)=[%s]",
- net_options->sha256_server_public_key? net_options->sha256_server_public_key:"n/a",
+ pfc_data->sha256_server_public_key? pfc_data->sha256_server_public_key:"n/a",
MYSQLND_G(sha256_server_public_key)? MYSQLND_G(sha256_server_public_key):"n/a");
if (!fname || fname[0] == '\0') {
MYSQLND_PACKET_SHA256_PK_REQUEST * pk_req_packet = NULL;
@@ -499,28 +699,28 @@ mysqlnd_sha256_get_rsa_key(MYSQLND_CONN_DATA * conn,
do {
DBG_INF("requesting the public key from the server");
- pk_req_packet = conn->protocol->m.get_sha256_pk_request_packet(conn->protocol, FALSE);
+ pk_req_packet = conn->payload_decoder_factory->m.get_sha256_pk_request_packet(conn->payload_decoder_factory, FALSE);
if (!pk_req_packet) {
- SET_OOM_ERROR(*conn->error_info);
+ SET_OOM_ERROR(conn->error_info);
break;
}
- pk_resp_packet = conn->protocol->m.get_sha256_pk_request_response_packet(conn->protocol, FALSE);
+ pk_resp_packet = conn->payload_decoder_factory->m.get_sha256_pk_request_response_packet(conn->payload_decoder_factory, FALSE);
if (!pk_resp_packet) {
- SET_OOM_ERROR(*conn->error_info);
+ SET_OOM_ERROR(conn->error_info);
PACKET_FREE(pk_req_packet);
break;
}
- if (! PACKET_WRITE(pk_req_packet, conn)) {
+ if (! PACKET_WRITE(pk_req_packet)) {
DBG_ERR_FMT("Error while sending public key request packet");
php_error(E_WARNING, "Error while sending public key request packet. PID=%d", getpid());
- CONN_SET_STATE(conn, CONN_QUIT_SENT);
+ SET_CONNECTION_STATE(&conn->state, CONN_QUIT_SENT);
break;
}
- if (FAIL == PACKET_READ(pk_resp_packet, conn) || NULL == pk_resp_packet->public_key) {
+ if (FAIL == PACKET_READ(pk_resp_packet) || NULL == pk_resp_packet->public_key) {
DBG_ERR_FMT("Error while receiving public key");
php_error(E_WARNING, "Error while receiving public key. PID=%d", getpid());
- CONN_SET_STATE(conn, CONN_QUIT_SENT);
+ SET_CONNECTION_STATE(&conn->state, CONN_QUIT_SENT);
break;
}
DBG_INF_FMT("Public key(%d):\n%s", pk_resp_packet->public_key_len, pk_resp_packet->public_key);
@@ -537,7 +737,7 @@ mysqlnd_sha256_get_rsa_key(MYSQLND_CONN_DATA * conn,
DBG_INF_FMT("ret=%p", ret);
DBG_RETURN(ret);
- SET_CLIENT_ERROR(*conn->error_info, CR_UNKNOWN_ERROR, UNKNOWN_SQLSTATE,
+ SET_CLIENT_ERROR(conn->error_info, CR_UNKNOWN_ERROR, UNKNOWN_SQLSTATE,
"sha256_server_public_key is not set for the connection or as mysqlnd.sha256_server_public_key");
DBG_ERR("server_public_key is not set");
DBG_RETURN(NULL);
@@ -569,8 +769,8 @@ mysqlnd_sha256_auth_get_auth_data(struct st_mysqlnd_authentication_plugin * self
size_t * auth_data_len,
MYSQLND_CONN_DATA * conn, const char * const user, const char * const passwd,
const size_t passwd_len, zend_uchar * auth_plugin_data, size_t auth_plugin_data_len,
- const MYSQLND_OPTIONS * const options,
- const MYSQLND_NET_OPTIONS * const net_options,
+ const MYSQLND_SESSION_OPTIONS * const session_options,
+ const MYSQLND_PFC_DATA * const pfc_data,
zend_ulong mysql_flags
)
{
@@ -580,7 +780,7 @@ mysqlnd_sha256_auth_get_auth_data(struct st_mysqlnd_authentication_plugin * self
DBG_INF_FMT("salt(%d)=[%.*s]", auth_plugin_data_len, auth_plugin_data_len, auth_plugin_data);
- if (conn->net->data->ssl) {
+ if (conn->protocol_frame_codec->data->ssl) {
DBG_INF("simple clear text under SSL");
/* clear text under SSL */
*auth_data_len = passwd_len;
@@ -588,7 +788,7 @@ mysqlnd_sha256_auth_get_auth_data(struct st_mysqlnd_authentication_plugin * self
memcpy(ret, passwd, passwd_len);
} else {
*auth_data_len = 0;
- server_public_key = mysqlnd_sha256_get_rsa_key(conn, options, net_options);
+ server_public_key = mysqlnd_sha256_get_rsa_key(conn, session_options, pfc_data);
if (server_public_key) {
int server_public_key_len;
@@ -605,7 +805,7 @@ mysqlnd_sha256_auth_get_auth_data(struct st_mysqlnd_authentication_plugin * self
*/
if ((size_t) server_public_key_len - 41 <= passwd_len) {
/* password message is to long */
- SET_CLIENT_ERROR(*conn->error_info, CR_UNKNOWN_ERROR, UNKNOWN_SQLSTATE, "password is too long");
+ SET_CLIENT_ERROR(conn->error_info, CR_UNKNOWN_ERROR, UNKNOWN_SQLSTATE, "password is too long");
DBG_ERR("password is too long");
DBG_RETURN(NULL);
}
diff --git a/ext/mysqlnd/mysqlnd_auth.h b/ext/mysqlnd/mysqlnd_auth.h
new file mode 100644
index 0000000000..db9460df54
--- /dev/null
+++ b/ext/mysqlnd/mysqlnd_auth.h
@@ -0,0 +1,126 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 7 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 2006-2016 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: Andrey Hristov <andrey@php.net> |
+ | Ulf Wendel <uw@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#ifndef MYSQLND_AUTH_H
+#define MYSQLND_AUTH_H
+enum_func_status
+mysqlnd_auth_handshake(MYSQLND_CONN_DATA * conn,
+ const char * const user,
+ const char * const passwd,
+ const size_t passwd_len,
+ const char * const db,
+ const size_t db_len,
+ const MYSQLND_SESSION_OPTIONS * const session_options,
+ zend_ulong mysql_flags,
+ unsigned int server_charset_no,
+ zend_bool use_full_blown_auth_packet,
+ const char * const auth_protocol,
+ const zend_uchar * const auth_plugin_data,
+ const size_t auth_plugin_data_len,
+ char ** switch_to_auth_protocol,
+ size_t * switch_to_auth_protocol_len,
+ zend_uchar ** switch_to_auth_protocol_data,
+ size_t * switch_to_auth_protocol_data_len
+ );
+
+enum_func_status
+mysqlnd_auth_handshake(MYSQLND_CONN_DATA * conn,
+ const char * const user,
+ const char * const passwd,
+ const size_t passwd_len,
+ const char * const db,
+ const size_t db_len,
+ const MYSQLND_SESSION_OPTIONS * const session_options,
+ zend_ulong mysql_flags,
+ unsigned int server_charset_no,
+ zend_bool use_full_blown_auth_packet,
+ const char * const auth_protocol,
+ const zend_uchar * const auth_plugin_data,
+ const size_t auth_plugin_data_len,
+ char ** switch_to_auth_protocol,
+ size_t * switch_to_auth_protocol_len,
+ zend_uchar ** switch_to_auth_protocol_data,
+ size_t * switch_to_auth_protocol_data_len
+ );
+
+enum_func_status
+mysqlnd_auth_change_user(MYSQLND_CONN_DATA * const conn,
+ const char * const user,
+ const size_t user_len,
+ const char * const passwd,
+ const size_t passwd_len,
+ const char * const db,
+ const size_t db_len,
+ const zend_bool silent,
+ zend_bool use_full_blown_auth_packet,
+ const char * const auth_protocol,
+ zend_uchar * auth_plugin_data,
+ size_t auth_plugin_data_len,
+ char ** switch_to_auth_protocol,
+ size_t * switch_to_auth_protocol_len,
+ zend_uchar ** switch_to_auth_protocol_data,
+ size_t * switch_to_auth_protocol_data_len
+ );
+
+
+enum_func_status
+mysqlnd_connect_run_authentication(
+ MYSQLND_CONN_DATA * conn,
+ const char * const user,
+ const char * const passwd,
+ const char * const db,
+ size_t db_len,
+ size_t passwd_len,
+ MYSQLND_STRING authentication_plugin_data,
+ const char * const authentication_protocol,
+ const unsigned int charset_no,
+ size_t server_capabilities,
+ const MYSQLND_SESSION_OPTIONS * const session_options,
+ zend_ulong mysql_flags
+ );
+
+enum_func_status
+mysqlnd_run_authentication(
+ MYSQLND_CONN_DATA * conn,
+ const char * const user,
+ const char * const passwd,
+ const size_t passwd_len,
+ const char * const db,
+ const size_t db_len,
+ const MYSQLND_STRING auth_plugin_data,
+ const char * const auth_protocol,
+ unsigned int charset_no,
+ const MYSQLND_SESSION_OPTIONS * const session_options,
+ zend_ulong mysql_flags,
+ zend_bool silent,
+ zend_bool is_change_user
+ );
+
+PHPAPI void php_mysqlnd_scramble(zend_uchar * const buffer, const zend_uchar * const scramble, const zend_uchar * const pass, const size_t pass_len);
+
+#endif /* MYSQLND_AUTH_H */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: noet sw=4 ts=4 fdm=marker
+ * vim<600: noet sw=4 ts=4
+ */
diff --git a/ext/mysqlnd/mysqlnd_block_alloc.c b/ext/mysqlnd/mysqlnd_block_alloc.c
index 9c5f12130b..60e6f938ef 100644
--- a/ext/mysqlnd/mysqlnd_block_alloc.c
+++ b/ext/mysqlnd/mysqlnd_block_alloc.c
@@ -14,12 +14,9 @@
+----------------------------------------------------------------------+
| Authors: Andrey Hristov <andrey@php.net> |
| Ulf Wendel <uw@php.net> |
- | Georg Richter <georg@php.net> |
+----------------------------------------------------------------------+
*/
-/* $Id$ */
-
#include "php.h"
#include "mysqlnd.h"
#include "mysqlnd_block_alloc.h"
@@ -29,9 +26,8 @@
/* {{{ mysqlnd_mempool_free_chunk */
static void
-mysqlnd_mempool_free_chunk(MYSQLND_MEMORY_POOL_CHUNK * chunk)
+mysqlnd_mempool_free_chunk(MYSQLND_MEMORY_POOL * pool, MYSQLND_MEMORY_POOL_CHUNK * chunk)
{
- MYSQLND_MEMORY_POOL * pool = chunk->pool;
DBG_ENTER("mysqlnd_mempool_free_chunk");
if (chunk->from_pool) {
/* Try to back-off and guess if this is the last block allocated */
@@ -42,7 +38,6 @@ mysqlnd_mempool_free_chunk(MYSQLND_MEMORY_POOL_CHUNK * chunk)
*/
pool->free_size += chunk->size;
}
- pool->refcount--;
} else {
mnd_efree(chunk->ptr);
}
@@ -54,11 +49,10 @@ mysqlnd_mempool_free_chunk(MYSQLND_MEMORY_POOL_CHUNK * chunk)
/* {{{ mysqlnd_mempool_resize_chunk */
static enum_func_status
-mysqlnd_mempool_resize_chunk(MYSQLND_MEMORY_POOL_CHUNK * chunk, unsigned int size)
+mysqlnd_mempool_resize_chunk(MYSQLND_MEMORY_POOL * pool, MYSQLND_MEMORY_POOL_CHUNK * chunk, unsigned int size)
{
DBG_ENTER("mysqlnd_mempool_resize_chunk");
if (chunk->from_pool) {
- MYSQLND_MEMORY_POOL * pool = chunk->pool;
/* Try to back-off and guess if this is the last block allocated */
if (chunk->ptr == (pool->arena + (pool->arena_size - pool->free_size - chunk->size))) {
/*
@@ -75,8 +69,7 @@ mysqlnd_mempool_resize_chunk(MYSQLND_MEMORY_POOL_CHUNK * chunk, unsigned int siz
chunk->ptr = new_ptr;
pool->free_size += chunk->size;
chunk->size = size;
- chunk->pool = NULL; /* now we have no pool memory */
- pool->refcount--;
+ chunk->from_pool = FALSE; /* now we have no pool memory */
} else {
/* If the chunk is > than asked size then free_memory increases, otherwise decreases*/
pool->free_size += (chunk->size - size);
@@ -94,8 +87,7 @@ mysqlnd_mempool_resize_chunk(MYSQLND_MEMORY_POOL_CHUNK * chunk, unsigned int siz
memcpy(new_ptr, chunk->ptr, chunk->size);
chunk->ptr = new_ptr;
chunk->size = size;
- chunk->pool = NULL; /* now we have non-pool memory */
- pool->refcount--;
+ chunk->from_pool = FALSE; /* now we have non-pool memory */
}
}
} else {
@@ -119,25 +111,21 @@ MYSQLND_MEMORY_POOL_CHUNK * mysqlnd_mempool_get_chunk(MYSQLND_MEMORY_POOL * pool
chunk = mnd_emalloc(sizeof(MYSQLND_MEMORY_POOL_CHUNK));
if (chunk) {
- chunk->free_chunk = mysqlnd_mempool_free_chunk;
- chunk->resize_chunk = mysqlnd_mempool_resize_chunk;
chunk->size = size;
/*
Should not go over MYSQLND_MAX_PACKET_SIZE, since we
expect non-arena memory in mysqlnd_wireprotocol.c . We
realloc the non-arena memory.
*/
- chunk->pool = pool;
if (size > pool->free_size) {
chunk->from_pool = FALSE;
chunk->ptr = mnd_emalloc(size);
if (!chunk->ptr) {
- chunk->free_chunk(chunk);
+ pool->free_chunk(pool, chunk);
chunk = NULL;
}
} else {
chunk->from_pool = TRUE;
- ++pool->refcount;
chunk->ptr = pool->arena + (pool->arena_size - pool->free_size);
/* Last step, update free_size */
pool->free_size -= size;
@@ -157,8 +145,9 @@ mysqlnd_mempool_create(size_t arena_size)
DBG_ENTER("mysqlnd_mempool_create");
if (ret) {
ret->get_chunk = mysqlnd_mempool_get_chunk;
+ ret->free_chunk = mysqlnd_mempool_free_chunk;
+ ret->resize_chunk = mysqlnd_mempool_resize_chunk;
ret->free_size = ret->arena_size = arena_size ? arena_size : 0;
- ret->refcount = 0;
/* OOM ? */
ret->arena = mnd_emalloc(ret->arena_size);
if (!ret->arena) {
diff --git a/ext/mysqlnd/mysqlnd_block_alloc.h b/ext/mysqlnd/mysqlnd_block_alloc.h
index 2e6a3a2446..54529a0aca 100644
--- a/ext/mysqlnd/mysqlnd_block_alloc.h
+++ b/ext/mysqlnd/mysqlnd_block_alloc.h
@@ -14,7 +14,6 @@
+----------------------------------------------------------------------+
| Authors: Andrey Hristov <andrey@php.net> |
| Ulf Wendel <uw@php.net> |
- | Georg Richter <georg@php.net> |
+----------------------------------------------------------------------+
*/
diff --git a/ext/mysqlnd/mysqlnd_charset.c b/ext/mysqlnd/mysqlnd_charset.c
index f47942a493..2b3ddd0831 100644
--- a/ext/mysqlnd/mysqlnd_charset.c
+++ b/ext/mysqlnd/mysqlnd_charset.c
@@ -19,7 +19,6 @@
*/
#include "php.h"
-#include "php_globals.h"
#include "mysqlnd.h"
#include "mysqlnd_priv.h"
#include "mysqlnd_debug.h"
diff --git a/ext/mysqlnd/mysqlnd_commands.c b/ext/mysqlnd/mysqlnd_commands.c
new file mode 100644
index 0000000000..d276a0f10a
--- /dev/null
+++ b/ext/mysqlnd/mysqlnd_commands.c
@@ -0,0 +1,1470 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 7 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 2006-2016 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: Andrey Hristov <andrey@php.net> |
+ | Ulf Wendel <uw@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#include "php.h"
+#include "mysqlnd.h"
+#include "mysqlnd_connection.h"
+#include "mysqlnd_priv.h"
+#include "mysqlnd_auth.h"
+#include "mysqlnd_wireprotocol.h"
+#include "mysqlnd_statistics.h"
+#include "mysqlnd_debug.h"
+
+
+struct st_mysqlnd_protocol_no_params_command
+{
+ struct st_mysqlnd_protocol_command parent;
+ struct st_mysqlnd_protocol_no_params_command_context
+ {
+ MYSQLND_CONN_DATA * conn;
+ } context;
+};
+
+/* {{{ mysqlnd_com_no_params_free_command */
+static void
+mysqlnd_com_no_params_free_command(void * command)
+{
+ DBG_ENTER("mysqlnd_com_no_params_free_command");
+ mnd_efree(command);
+ DBG_VOID_RETURN;
+}
+/* }}} */
+
+
+/************************** COM_SET_OPTION ******************************************/
+struct st_mysqlnd_protocol_com_set_option_command
+{
+ struct st_mysqlnd_protocol_command parent;
+ struct st_mysqlnd_com_set_option_context
+ {
+ MYSQLND_CONN_DATA * conn;
+ enum_mysqlnd_server_option option;
+ } context;
+};
+
+
+/* {{{ mysqlnd_com_set_option_run */
+enum_func_status
+mysqlnd_com_set_option_run(void *cmd)
+{
+ struct st_mysqlnd_protocol_com_set_option_command * command = (struct st_mysqlnd_protocol_com_set_option_command *) cmd;
+ zend_uchar buffer[2];
+ enum_func_status ret = FAIL;
+ MYSQLND_CONN_DATA * conn = command->context.conn;
+ enum_mysqlnd_server_option option = command->context.option;
+ func_mysqlnd_protocol_payload_decoder_factory__send_command send_command = conn->payload_decoder_factory->m.send_command;
+ func_mysqlnd_protocol_payload_decoder_factory__send_command_handle_response send_command_handle_response = conn->payload_decoder_factory->m.send_command_handle_response;
+
+ DBG_ENTER("mysqlnd_com_set_option_run");
+ int2store(buffer, (unsigned int) option);
+
+ ret = send_command(conn->payload_decoder_factory, COM_SET_OPTION, buffer, sizeof(buffer), FALSE,
+ &conn->state,
+ conn->error_info,
+ conn->upsert_status,
+ conn->stats,
+ conn->m->send_close,
+ conn);
+ if (PASS == ret) {
+ ret = send_command_handle_response(conn->payload_decoder_factory, PROT_EOF_PACKET, FALSE, COM_SET_OPTION, TRUE,
+ conn->error_info, conn->upsert_status, &conn->last_message, conn->persistent);
+ }
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_com_set_option_create_command */
+static struct st_mysqlnd_protocol_command *
+mysqlnd_com_set_option_create_command(va_list args)
+{
+ struct st_mysqlnd_protocol_com_set_option_command * command;
+ DBG_ENTER("mysqlnd_com_set_option_create_command");
+ command = mnd_ecalloc(1, sizeof(struct st_mysqlnd_protocol_com_set_option_command));
+ if (command) {
+ command->context.conn = va_arg(args, MYSQLND_CONN_DATA *);
+ command->context.option = va_arg(args, enum_mysqlnd_server_option);
+
+ command->parent.free_command = mysqlnd_com_no_params_free_command;
+ command->parent.run = mysqlnd_com_set_option_run;
+ }
+
+ DBG_RETURN((struct st_mysqlnd_protocol_command *) command);
+}
+/* }}} */
+
+
+/************************** COM_DEBUG ******************************************/
+/* {{{ mysqlnd_com_debug_run */
+static enum_func_status
+mysqlnd_com_debug_run(void *cmd)
+{
+ struct st_mysqlnd_protocol_no_params_command * command = (struct st_mysqlnd_protocol_no_params_command *) cmd;
+ enum_func_status ret = FAIL;
+ MYSQLND_CONN_DATA * conn = command->context.conn;
+ func_mysqlnd_protocol_payload_decoder_factory__send_command send_command = conn->payload_decoder_factory->m.send_command;
+ func_mysqlnd_protocol_payload_decoder_factory__send_command_handle_response send_command_handle_response = conn->payload_decoder_factory->m.send_command_handle_response;
+
+ DBG_ENTER("mysqlnd_com_debug_run");
+
+ ret = send_command(conn->payload_decoder_factory, COM_DEBUG, NULL, 0, FALSE,
+ &conn->state,
+ conn->error_info,
+ conn->upsert_status,
+ conn->stats,
+ conn->m->send_close,
+ conn);
+ if (PASS == ret) {
+ ret = send_command_handle_response(conn->payload_decoder_factory, PROT_EOF_PACKET, FALSE, COM_DEBUG, TRUE,
+ conn->error_info, conn->upsert_status, &conn->last_message, conn->persistent);
+ }
+
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_com_debug_create_command */
+static struct st_mysqlnd_protocol_command *
+mysqlnd_com_debug_create_command(va_list args)
+{
+ struct st_mysqlnd_protocol_no_params_command * command;
+ DBG_ENTER("mysqlnd_com_debug_create_command");
+ command = mnd_ecalloc(1, sizeof(struct st_mysqlnd_protocol_no_params_command));
+ if (command) {
+ command->context.conn = va_arg(args, MYSQLND_CONN_DATA *);
+ command->parent.free_command = mysqlnd_com_no_params_free_command;
+
+ command->parent.run = mysqlnd_com_debug_run;
+ }
+
+ DBG_RETURN((struct st_mysqlnd_protocol_command *) command);
+}
+/* }}} */
+
+
+/************************** COM_INIT_DB ******************************************/
+struct st_mysqlnd_protocol_com_init_db_command
+{
+ struct st_mysqlnd_protocol_command parent;
+ struct st_mysqlnd_com_init_db_context
+ {
+ MYSQLND_CONN_DATA * conn;
+ MYSQLND_CSTRING db;
+ } context;
+};
+
+
+/* {{{ mysqlnd_com_init_db_run */
+static enum_func_status
+mysqlnd_com_init_db_run(void *cmd)
+{
+ struct st_mysqlnd_protocol_com_init_db_command * command = (struct st_mysqlnd_protocol_com_init_db_command *) cmd;
+ enum_func_status ret = FAIL;
+ MYSQLND_CONN_DATA * conn = command->context.conn;
+ const MYSQLND_CSTRING db = command->context.db;
+ func_mysqlnd_protocol_payload_decoder_factory__send_command send_command = conn->payload_decoder_factory->m.send_command;
+ func_mysqlnd_protocol_payload_decoder_factory__send_command_handle_response send_command_handle_response = conn->payload_decoder_factory->m.send_command_handle_response;
+
+ DBG_ENTER("mysqlnd_com_init_db_run");
+
+ ret = send_command(conn->payload_decoder_factory, COM_INIT_DB, (zend_uchar*) command->context.db.s, command->context.db.l, FALSE,
+ &conn->state,
+ conn->error_info,
+ conn->upsert_status,
+ conn->stats,
+ conn->m->send_close,
+ conn);
+ if (PASS == ret) {
+ ret = send_command_handle_response(conn->payload_decoder_factory, PROT_OK_PACKET, FALSE, COM_INIT_DB, TRUE,
+ conn->error_info, conn->upsert_status, &conn->last_message, conn->persistent);
+ }
+
+ /*
+ The server sends 0 but libmysql doesn't read it and has established
+ a protocol of giving back -1. Thus we have to follow it :(
+ */
+ UPSERT_STATUS_SET_AFFECTED_ROWS_TO_ERROR(conn->upsert_status);
+ if (ret == PASS) {
+ if (conn->connect_or_select_db.s) {
+ mnd_pefree(conn->connect_or_select_db.s, conn->persistent);
+ }
+ conn->connect_or_select_db.s = mnd_pestrndup(db.s, db.l, conn->persistent);
+ conn->connect_or_select_db.l = db.l;
+ if (!conn->connect_or_select_db.s) {
+ /* OOM */
+ SET_OOM_ERROR(conn->error_info);
+ ret = FAIL;
+ }
+ }
+
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_com_init_db_create_command */
+static struct st_mysqlnd_protocol_command *
+mysqlnd_com_init_db_create_command(va_list args)
+{
+ struct st_mysqlnd_protocol_com_init_db_command * command;
+ DBG_ENTER("mysqlnd_com_init_db_create_command");
+ command = mnd_ecalloc(1, sizeof(struct st_mysqlnd_protocol_com_init_db_command));
+ if (command) {
+ command->context.conn = va_arg(args, MYSQLND_CONN_DATA *);
+ command->context.db = va_arg(args, MYSQLND_CSTRING);
+
+ command->parent.free_command = mysqlnd_com_no_params_free_command;
+ command->parent.run = mysqlnd_com_init_db_run;
+ }
+
+ DBG_RETURN((struct st_mysqlnd_protocol_command *) command);
+}
+/* }}} */
+
+
+/************************** COM_PING ******************************************/
+/* {{{ mysqlnd_com_ping_run */
+static enum_func_status
+mysqlnd_com_ping_run(void *cmd)
+{
+ struct st_mysqlnd_protocol_no_params_command * command = (struct st_mysqlnd_protocol_no_params_command *) cmd;
+ enum_func_status ret = FAIL;
+ MYSQLND_CONN_DATA * conn = command->context.conn;
+ func_mysqlnd_protocol_payload_decoder_factory__send_command send_command = conn->payload_decoder_factory->m.send_command;
+ func_mysqlnd_protocol_payload_decoder_factory__send_command_handle_response send_command_handle_response = conn->payload_decoder_factory->m.send_command_handle_response;
+
+ DBG_ENTER("mysqlnd_com_ping_run");
+
+ ret = send_command(conn->payload_decoder_factory, COM_PING, NULL, 0, TRUE,
+ &conn->state,
+ conn->error_info,
+ conn->upsert_status,
+ conn->stats,
+ conn->m->send_close,
+ conn);
+ if (PASS == ret) {
+ ret = send_command_handle_response(conn->payload_decoder_factory, PROT_OK_PACKET, TRUE, COM_PING, TRUE,
+ conn->error_info, conn->upsert_status, &conn->last_message, conn->persistent);
+ }
+ /*
+ The server sends 0 but libmysql doesn't read it and has established
+ a protocol of giving back -1. Thus we have to follow it :(
+ */
+ UPSERT_STATUS_SET_AFFECTED_ROWS_TO_ERROR(conn->upsert_status);
+
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_com_ping_create_command */
+static struct st_mysqlnd_protocol_command *
+mysqlnd_com_ping_create_command(va_list args)
+{
+ struct st_mysqlnd_protocol_no_params_command * command;
+ DBG_ENTER("mysqlnd_com_ping_create_command");
+ command = mnd_ecalloc(1, sizeof(struct st_mysqlnd_protocol_no_params_command));
+ if (command) {
+ command->context.conn = va_arg(args, MYSQLND_CONN_DATA *);
+ command->parent.free_command = mysqlnd_com_no_params_free_command;
+
+ command->parent.run = mysqlnd_com_ping_run;
+ }
+
+ DBG_RETURN((struct st_mysqlnd_protocol_command *) command);
+}
+/* }}} */
+
+
+/************************** COM_STATISTICS ******************************************/
+struct st_mysqlnd_protocol_com_statistics_command
+{
+ struct st_mysqlnd_protocol_command parent;
+ struct st_mysqlnd_com_statistics_context
+ {
+ MYSQLND_CONN_DATA * conn;
+ zend_string ** message;
+ } context;
+};
+
+
+/* {{{ mysqlnd_com_statistics_run */
+static enum_func_status
+mysqlnd_com_statistics_run(void *cmd)
+{
+ struct st_mysqlnd_protocol_com_statistics_command * command = (struct st_mysqlnd_protocol_com_statistics_command *) cmd;
+ enum_func_status ret = FAIL;
+ MYSQLND_CONN_DATA * conn = command->context.conn;
+ zend_string **message = command->context.message;
+ func_mysqlnd_protocol_payload_decoder_factory__send_command send_command = conn->payload_decoder_factory->m.send_command;
+
+ DBG_ENTER("mysqlnd_com_statistics_run");
+
+ ret = send_command(conn->payload_decoder_factory, COM_STATISTICS, NULL, 0, FALSE,
+ &conn->state,
+ conn->error_info,
+ conn->upsert_status,
+ conn->stats,
+ conn->m->send_close,
+ conn);
+
+ if (PASS == ret) {
+ MYSQLND_PACKET_STATS * stats_header = conn->payload_decoder_factory->m.get_stats_packet(conn->payload_decoder_factory, FALSE);
+ if (!stats_header) {
+ SET_OOM_ERROR(conn->error_info);
+ } else {
+ if (PASS == (ret = PACKET_READ(stats_header))) {
+ /* will be freed by Zend, thus don't use the mnd_ allocator */
+ *message = zend_string_init(stats_header->message.s, stats_header->message.l, 0);
+ DBG_INF(ZSTR_VAL(*message));
+ }
+ PACKET_FREE(stats_header);
+ }
+ }
+
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_com_statistics_create_command */
+static struct st_mysqlnd_protocol_command *
+mysqlnd_com_statistics_create_command(va_list args)
+{
+ struct st_mysqlnd_protocol_com_statistics_command * command;
+ DBG_ENTER("mysqlnd_com_statistics_create_command");
+ command = mnd_ecalloc(1, sizeof(struct st_mysqlnd_protocol_com_statistics_command));
+ if (command) {
+ command->context.conn = va_arg(args, MYSQLND_CONN_DATA *);
+ command->context.message = va_arg(args, zend_string **);
+
+ command->parent.free_command = mysqlnd_com_no_params_free_command;
+ command->parent.run = mysqlnd_com_statistics_run;
+ }
+
+ DBG_RETURN((struct st_mysqlnd_protocol_command *) command);
+}
+/* }}} */
+
+/************************** COM_PROCESS_KILL ******************************************/
+struct st_mysqlnd_protocol_com_process_kill_command
+{
+ struct st_mysqlnd_protocol_command parent;
+ struct st_mysqlnd_com_process_kill_context
+ {
+ MYSQLND_CONN_DATA * conn;
+ unsigned int process_id;
+ zend_bool read_response;
+ } context;
+};
+
+
+/* {{{ mysqlnd_com_process_kill_run */
+enum_func_status
+mysqlnd_com_process_kill_run(void *cmd)
+{
+ struct st_mysqlnd_protocol_com_process_kill_command * command = (struct st_mysqlnd_protocol_com_process_kill_command *) cmd;
+ zend_uchar buff[4];
+ enum_func_status ret = FAIL;
+ MYSQLND_CONN_DATA * conn = command->context.conn;
+ zend_bool read_response = command->context.read_response;
+ func_mysqlnd_protocol_payload_decoder_factory__send_command send_command = conn->payload_decoder_factory->m.send_command;
+ func_mysqlnd_protocol_payload_decoder_factory__send_command_handle_response send_command_handle_response = conn->payload_decoder_factory->m.send_command_handle_response;
+
+ DBG_ENTER("mysqlnd_com_process_kill_run");
+ int4store(buff, command->context.process_id);
+
+ ret = send_command(conn->payload_decoder_factory, COM_PROCESS_KILL, buff, 4, FALSE,
+ &conn->state,
+ conn->error_info,
+ conn->upsert_status,
+ conn->stats,
+ conn->m->send_close,
+ conn);
+ if (PASS == ret && read_response) {
+ ret = send_command_handle_response(conn->payload_decoder_factory, PROT_OK_PACKET, FALSE, COM_PROCESS_KILL, TRUE,
+ conn->error_info, conn->upsert_status, &conn->last_message, conn->persistent);
+ }
+
+ if (read_response) {
+ /*
+ The server sends 0 but libmysql doesn't read it and has established
+ a protocol of giving back -1. Thus we have to follow it :(
+ */
+ UPSERT_STATUS_SET_AFFECTED_ROWS_TO_ERROR(conn->upsert_status);
+ } else if (PASS == ret) {
+ SET_CONNECTION_STATE(&conn->state, CONN_QUIT_SENT);
+ conn->m->send_close(conn);
+ }
+
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_com_process_kill_create_command */
+static struct st_mysqlnd_protocol_command *
+mysqlnd_com_process_kill_create_command(va_list args)
+{
+ struct st_mysqlnd_protocol_com_process_kill_command * command;
+ DBG_ENTER("mysqlnd_com_process_kill_create_command");
+ command = mnd_ecalloc(1, sizeof(struct st_mysqlnd_protocol_com_process_kill_command));
+ if (command) {
+ command->context.conn = va_arg(args, MYSQLND_CONN_DATA *);
+ command->context.process_id = va_arg(args, unsigned int);
+ command->context.read_response = va_arg(args, unsigned int)? TRUE:FALSE;
+
+ command->parent.free_command = mysqlnd_com_no_params_free_command;
+ command->parent.run = mysqlnd_com_process_kill_run;
+ }
+
+ DBG_RETURN((struct st_mysqlnd_protocol_command *) command);
+}
+/* }}} */
+
+/************************** COM_REFRESH ******************************************/
+struct st_mysqlnd_protocol_com_refresh_command
+{
+ struct st_mysqlnd_protocol_command parent;
+ struct st_mysqlnd_com_refresh_context
+ {
+ MYSQLND_CONN_DATA * conn;
+ uint8_t options;
+ } context;
+};
+
+
+/* {{{ mysqlnd_com_refresh_run */
+enum_func_status
+mysqlnd_com_refresh_run(void *cmd)
+{
+ struct st_mysqlnd_protocol_com_refresh_command * command = (struct st_mysqlnd_protocol_com_refresh_command *) cmd;
+ zend_uchar bits[1];
+ enum_func_status ret = FAIL;
+ MYSQLND_CONN_DATA * conn = command->context.conn;
+ func_mysqlnd_protocol_payload_decoder_factory__send_command send_command = conn->payload_decoder_factory->m.send_command;
+ func_mysqlnd_protocol_payload_decoder_factory__send_command_handle_response send_command_handle_response = conn->payload_decoder_factory->m.send_command_handle_response;
+
+ DBG_ENTER("mysqlnd_com_refresh_run");
+ int1store(bits, command->context.options);
+
+ ret = send_command(conn->payload_decoder_factory, COM_REFRESH, bits, 1, FALSE,
+ &conn->state,
+ conn->error_info,
+ conn->upsert_status,
+ conn->stats,
+ conn->m->send_close,
+ conn);
+ if (PASS == ret) {
+ ret = send_command_handle_response(conn->payload_decoder_factory, PROT_OK_PACKET, FALSE, COM_REFRESH, TRUE,
+ conn->error_info, conn->upsert_status, &conn->last_message, conn->persistent);
+ }
+
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_com_refresh_create_command */
+static struct st_mysqlnd_protocol_command *
+mysqlnd_com_refresh_create_command(va_list args)
+{
+ struct st_mysqlnd_protocol_com_refresh_command * command;
+ DBG_ENTER("mysqlnd_com_refresh_create_command");
+ command = mnd_ecalloc(1, sizeof(struct st_mysqlnd_protocol_com_refresh_command));
+ if (command) {
+ command->context.conn = va_arg(args, MYSQLND_CONN_DATA *);
+ command->context.options = va_arg(args, unsigned int);
+
+ command->parent.free_command = mysqlnd_com_no_params_free_command;
+ command->parent.run = mysqlnd_com_refresh_run;
+ }
+
+ DBG_RETURN((struct st_mysqlnd_protocol_command *) command);
+}
+/* }}} */
+
+
+/************************** COM_SHUTDOWN ******************************************/
+struct st_mysqlnd_protocol_com_shutdown_command
+{
+ struct st_mysqlnd_protocol_command parent;
+ struct st_mysqlnd_com_shutdown_context
+ {
+ MYSQLND_CONN_DATA * conn;
+ uint8_t level;
+ } context;
+};
+
+
+/* {{{ mysqlnd_com_shutdown_run */
+enum_func_status
+mysqlnd_com_shutdown_run(void *cmd)
+{
+ struct st_mysqlnd_protocol_com_shutdown_command * command = (struct st_mysqlnd_protocol_com_shutdown_command *) cmd;
+ zend_uchar bits[1];
+ enum_func_status ret = FAIL;
+ MYSQLND_CONN_DATA * conn = command->context.conn;
+ func_mysqlnd_protocol_payload_decoder_factory__send_command send_command = conn->payload_decoder_factory->m.send_command;
+ func_mysqlnd_protocol_payload_decoder_factory__send_command_handle_response send_command_handle_response = conn->payload_decoder_factory->m.send_command_handle_response;
+
+ DBG_ENTER("mysqlnd_com_shutdown_run");
+ int1store(bits, command->context.level);
+
+ ret = send_command(conn->payload_decoder_factory, COM_SHUTDOWN, bits, 1, FALSE,
+ &conn->state,
+ conn->error_info,
+ conn->upsert_status,
+ conn->stats,
+ conn->m->send_close,
+ conn);
+ if (PASS == ret) {
+ ret = send_command_handle_response(conn->payload_decoder_factory, PROT_OK_PACKET, FALSE, COM_SHUTDOWN, TRUE,
+ conn->error_info, conn->upsert_status, &conn->last_message, conn->persistent);
+ }
+
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_com_shutdown_create_command */
+static struct st_mysqlnd_protocol_command *
+mysqlnd_com_shutdown_create_command(va_list args)
+{
+ struct st_mysqlnd_protocol_com_shutdown_command * command;
+ DBG_ENTER("mysqlnd_com_shutdown_create_command");
+ command = mnd_ecalloc(1, sizeof(struct st_mysqlnd_protocol_com_shutdown_command));
+ if (command) {
+ command->context.conn = va_arg(args, MYSQLND_CONN_DATA *);
+ command->context.level = va_arg(args, unsigned int);
+
+ command->parent.free_command = mysqlnd_com_no_params_free_command;
+ command->parent.run = mysqlnd_com_shutdown_run;
+ }
+
+ DBG_RETURN((struct st_mysqlnd_protocol_command *) command);
+}
+/* }}} */
+
+
+/************************** COM_QUIT ******************************************/
+struct st_mysqlnd_protocol_com_quit_command
+{
+ struct st_mysqlnd_protocol_command parent;
+ struct st_mysqlnd_com_quit_context
+ {
+ MYSQLND_CONN_DATA * conn;
+ } context;
+};
+
+
+/* {{{ mysqlnd_com_quit_run */
+enum_func_status
+mysqlnd_com_quit_run(void *cmd)
+{
+ struct st_mysqlnd_protocol_com_quit_command * command = (struct st_mysqlnd_protocol_com_quit_command *) cmd;
+ enum_func_status ret = FAIL;
+ MYSQLND_CONN_DATA * conn = command->context.conn;
+ func_mysqlnd_protocol_payload_decoder_factory__send_command send_command = conn->payload_decoder_factory->m.send_command;
+
+ DBG_ENTER("mysqlnd_com_quit_run");
+
+ ret = send_command(conn->payload_decoder_factory, COM_QUIT, NULL, 0, TRUE,
+ &conn->state,
+ conn->error_info,
+ conn->upsert_status,
+ conn->stats,
+ conn->m->send_close,
+ conn);
+
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_com_quit_create_command */
+static struct st_mysqlnd_protocol_command *
+mysqlnd_com_quit_create_command(va_list args)
+{
+ struct st_mysqlnd_protocol_com_quit_command * command;
+ DBG_ENTER("mysqlnd_com_quit_create_command");
+ command = mnd_ecalloc(1, sizeof(struct st_mysqlnd_protocol_com_quit_command));
+ if (command) {
+ command->context.conn = va_arg(args, MYSQLND_CONN_DATA *);
+
+ command->parent.free_command = mysqlnd_com_no_params_free_command;
+ command->parent.run = mysqlnd_com_quit_run;
+ }
+
+ DBG_RETURN((struct st_mysqlnd_protocol_command *) command);
+}
+/* }}} */
+
+/************************** COM_QUERY ******************************************/
+struct st_mysqlnd_protocol_com_query_command
+{
+ struct st_mysqlnd_protocol_command parent;
+ struct st_mysqlnd_com_query_context
+ {
+ MYSQLND_CONN_DATA * conn;
+ MYSQLND_CSTRING query;
+ } context;
+};
+
+
+/* {{{ mysqlnd_com_query_run */
+static enum_func_status
+mysqlnd_com_query_run(void *cmd)
+{
+ struct st_mysqlnd_protocol_com_query_command * command = (struct st_mysqlnd_protocol_com_query_command *) cmd;
+ enum_func_status ret = FAIL;
+ MYSQLND_CONN_DATA * conn = command->context.conn;
+ func_mysqlnd_protocol_payload_decoder_factory__send_command send_command = conn->payload_decoder_factory->m.send_command;
+
+ DBG_ENTER("mysqlnd_com_query_run");
+
+ ret = send_command(conn->payload_decoder_factory, COM_QUERY, (zend_uchar*) command->context.query.s, command->context.query.l, FALSE,
+ &conn->state,
+ conn->error_info,
+ conn->upsert_status,
+ conn->stats,
+ conn->m->send_close,
+ conn);
+
+ if (PASS == ret) {
+ SET_CONNECTION_STATE(&conn->state, CONN_QUERY_SENT);
+ }
+
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_com_query_create_command */
+static struct st_mysqlnd_protocol_command *
+mysqlnd_com_query_create_command(va_list args)
+{
+ struct st_mysqlnd_protocol_com_query_command * command;
+ DBG_ENTER("mysqlnd_com_query_create_command");
+ command = mnd_ecalloc(1, sizeof(struct st_mysqlnd_protocol_com_query_command));
+ if (command) {
+ command->context.conn = va_arg(args, MYSQLND_CONN_DATA *);
+ command->context.query = va_arg(args, MYSQLND_CSTRING);
+
+ command->parent.free_command = mysqlnd_com_no_params_free_command;
+ command->parent.run = mysqlnd_com_query_run;
+ }
+
+ DBG_RETURN((struct st_mysqlnd_protocol_command *) command);
+}
+/* }}} */
+
+/************************** COM_CHANGE_USER ******************************************/
+struct st_mysqlnd_protocol_com_change_user_command
+{
+ struct st_mysqlnd_protocol_command parent;
+ struct st_mysqlnd_com_change_user_context
+ {
+ MYSQLND_CONN_DATA * conn;
+ MYSQLND_CSTRING payload;
+ zend_bool silent;
+ } context;
+};
+
+
+/* {{{ mysqlnd_com_change_user_run */
+static enum_func_status
+mysqlnd_com_change_user_run(void *cmd)
+{
+ struct st_mysqlnd_protocol_com_change_user_command * command = (struct st_mysqlnd_protocol_com_change_user_command *) cmd;
+ enum_func_status ret = FAIL;
+ MYSQLND_CONN_DATA * conn = command->context.conn;
+ func_mysqlnd_protocol_payload_decoder_factory__send_command send_command = conn->payload_decoder_factory->m.send_command;
+
+ DBG_ENTER("mysqlnd_com_change_user_run");
+
+ ret = send_command(conn->payload_decoder_factory, COM_CHANGE_USER, (zend_uchar*) command->context.payload.s, command->context.payload.l, command->context.silent,
+ &conn->state,
+ conn->error_info,
+ conn->upsert_status,
+ conn->stats,
+ conn->m->send_close,
+ conn);
+
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_com_change_user_create_command */
+static struct st_mysqlnd_protocol_command *
+mysqlnd_com_change_user_create_command(va_list args)
+{
+ struct st_mysqlnd_protocol_com_change_user_command * command;
+ DBG_ENTER("mysqlnd_com_change_user_create_command");
+ command = mnd_ecalloc(1, sizeof(struct st_mysqlnd_protocol_com_change_user_command));
+ if (command) {
+ command->context.conn = va_arg(args, MYSQLND_CONN_DATA *);
+ command->context.payload = va_arg(args, MYSQLND_CSTRING);
+ command->context.silent = va_arg(args, unsigned int);
+
+ command->parent.free_command = mysqlnd_com_no_params_free_command;
+ command->parent.run = mysqlnd_com_change_user_run;
+ }
+
+ DBG_RETURN((struct st_mysqlnd_protocol_command *) command);
+}
+/* }}} */
+
+
+/************************** COM_REAP_RESULT ******************************************/
+struct st_mysqlnd_protocol_com_reap_result_command
+{
+ struct st_mysqlnd_protocol_command parent;
+ struct st_mysqlnd_com_reap_result_context
+ {
+ MYSQLND_CONN_DATA * conn;
+ } context;
+};
+
+
+/* {{{ mysqlnd_com_reap_result_run */
+static enum_func_status
+mysqlnd_com_reap_result_run(void *cmd)
+{
+ struct st_mysqlnd_protocol_com_reap_result_command * command = (struct st_mysqlnd_protocol_com_reap_result_command *) cmd;
+ enum_func_status ret = FAIL;
+ MYSQLND_CONN_DATA * conn = command->context.conn;
+ const enum_mysqlnd_connection_state state = GET_CONNECTION_STATE(&conn->state);
+
+ DBG_ENTER("mysqlnd_com_reap_result_run");
+ if (state <= CONN_READY || state == CONN_QUIT_SENT) {
+ php_error_docref(NULL, E_WARNING, "Connection not opened, clear or has been closed");
+ DBG_ERR_FMT("Connection not opened, clear or has been closed. State=%u", state);
+ DBG_RETURN(ret);
+ }
+ ret = conn->m->query_read_result_set_header(conn, NULL);
+
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_com_reap_result_create_command */
+static struct st_mysqlnd_protocol_command *
+mysqlnd_com_reap_result_create_command(va_list args)
+{
+ struct st_mysqlnd_protocol_com_reap_result_command * command;
+ DBG_ENTER("mysqlnd_com_reap_result_create_command");
+ command = mnd_ecalloc(1, sizeof(struct st_mysqlnd_protocol_com_reap_result_command));
+ if (command) {
+ command->context.conn = va_arg(args, MYSQLND_CONN_DATA *);
+
+ command->parent.free_command = mysqlnd_com_no_params_free_command;
+ command->parent.run = mysqlnd_com_reap_result_run;
+ }
+
+ DBG_RETURN((struct st_mysqlnd_protocol_command *) command);
+}
+/* }}} */
+
+
+/************************** COM_STMT_PREPARE ******************************************/
+struct st_mysqlnd_protocol_com_stmt_prepare_command
+{
+ struct st_mysqlnd_protocol_command parent;
+ struct st_mysqlnd_com_stmt_prepare_context
+ {
+ MYSQLND_CONN_DATA * conn;
+ MYSQLND_CSTRING query;
+ } context;
+};
+
+
+/* {{{ mysqlnd_com_stmt_prepare_run */
+static enum_func_status
+mysqlnd_com_stmt_prepare_run(void *cmd)
+{
+ struct st_mysqlnd_protocol_com_stmt_prepare_command * command = (struct st_mysqlnd_protocol_com_stmt_prepare_command *) cmd;
+ enum_func_status ret = FAIL;
+ MYSQLND_CONN_DATA * conn = command->context.conn;
+ func_mysqlnd_protocol_payload_decoder_factory__send_command send_command = conn->payload_decoder_factory->m.send_command;
+
+ DBG_ENTER("mysqlnd_com_stmt_prepare_run");
+
+ ret = send_command(conn->payload_decoder_factory, COM_STMT_PREPARE, (zend_uchar*) command->context.query.s, command->context.query.l, FALSE,
+ &conn->state,
+ conn->error_info,
+ conn->upsert_status,
+ conn->stats,
+ conn->m->send_close,
+ conn);
+
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_com_stmt_prepare_create_command */
+static struct st_mysqlnd_protocol_command *
+mysqlnd_com_stmt_prepare_create_command(va_list args)
+{
+ struct st_mysqlnd_protocol_com_stmt_prepare_command * command;
+ DBG_ENTER("mysqlnd_com_stmt_prepare_create_command");
+ command = mnd_ecalloc(1, sizeof(struct st_mysqlnd_protocol_com_stmt_prepare_command));
+ if (command) {
+ command->context.conn = va_arg(args, MYSQLND_CONN_DATA *);
+ command->context.query = va_arg(args, MYSQLND_CSTRING);
+
+ command->parent.free_command = mysqlnd_com_no_params_free_command;
+ command->parent.run = mysqlnd_com_stmt_prepare_run;
+ }
+
+ DBG_RETURN((struct st_mysqlnd_protocol_command *) command);
+}
+/* }}} */
+
+
+/************************** COM_STMT_EXECUTE ******************************************/
+struct st_mysqlnd_protocol_com_stmt_execute_command
+{
+ struct st_mysqlnd_protocol_command parent;
+ struct st_mysqlnd_com_stmt_execute_context
+ {
+ MYSQLND_CONN_DATA * conn;
+ MYSQLND_CSTRING payload;
+ } context;
+};
+
+
+/* {{{ mysqlnd_com_stmt_execute_run */
+static enum_func_status
+mysqlnd_com_stmt_execute_run(void *cmd)
+{
+ struct st_mysqlnd_protocol_com_stmt_execute_command * command = (struct st_mysqlnd_protocol_com_stmt_execute_command *) cmd;
+ enum_func_status ret = FAIL;
+ MYSQLND_CONN_DATA * conn = command->context.conn;
+ func_mysqlnd_protocol_payload_decoder_factory__send_command send_command = conn->payload_decoder_factory->m.send_command;
+
+ DBG_ENTER("mysqlnd_com_stmt_execute_run");
+
+ ret = send_command(conn->payload_decoder_factory, COM_STMT_EXECUTE, (zend_uchar*) command->context.payload.s, command->context.payload.l, FALSE,
+ &conn->state,
+ conn->error_info,
+ conn->upsert_status,
+ conn->stats,
+ conn->m->send_close,
+ conn);
+
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_com_stmt_execute_create_command */
+static struct st_mysqlnd_protocol_command *
+mysqlnd_com_stmt_execute_create_command(va_list args)
+{
+ struct st_mysqlnd_protocol_com_stmt_execute_command * command;
+ DBG_ENTER("mysqlnd_com_stmt_execute_create_command");
+ command = mnd_ecalloc(1, sizeof(struct st_mysqlnd_protocol_com_stmt_execute_command));
+ if (command) {
+ command->context.conn = va_arg(args, MYSQLND_CONN_DATA *);
+ command->context.payload = va_arg(args, MYSQLND_CSTRING);
+
+ command->parent.free_command = mysqlnd_com_no_params_free_command;
+ command->parent.run = mysqlnd_com_stmt_execute_run;
+ }
+
+ DBG_RETURN((struct st_mysqlnd_protocol_command *) command);
+}
+/* }}} */
+
+
+/************************** COM_STMT_FETCH ******************************************/
+struct st_mysqlnd_protocol_com_stmt_fetch_command
+{
+ struct st_mysqlnd_protocol_command parent;
+ struct st_mysqlnd_com_stmt_fetch_context
+ {
+ MYSQLND_CONN_DATA * conn;
+ MYSQLND_CSTRING payload;
+ } context;
+};
+
+
+/* {{{ mysqlnd_com_stmt_fetch_run */
+static enum_func_status
+mysqlnd_com_stmt_fetch_run(void *cmd)
+{
+ struct st_mysqlnd_protocol_com_stmt_fetch_command * command = (struct st_mysqlnd_protocol_com_stmt_fetch_command *) cmd;
+ enum_func_status ret = FAIL;
+ MYSQLND_CONN_DATA * conn = command->context.conn;
+ func_mysqlnd_protocol_payload_decoder_factory__send_command send_command = conn->payload_decoder_factory->m.send_command;
+
+ DBG_ENTER("mysqlnd_com_stmt_fetch_run");
+
+ ret = send_command(conn->payload_decoder_factory, COM_STMT_FETCH, (zend_uchar*) command->context.payload.s, command->context.payload.l, FALSE,
+ &conn->state,
+ conn->error_info,
+ conn->upsert_status,
+ conn->stats,
+ conn->m->send_close,
+ conn);
+
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_com_stmt_fetch_create_command */
+static struct st_mysqlnd_protocol_command *
+mysqlnd_com_stmt_fetch_create_command(va_list args)
+{
+ struct st_mysqlnd_protocol_com_stmt_fetch_command * command;
+ DBG_ENTER("mysqlnd_com_stmt_fetch_create_command");
+ command = mnd_ecalloc(1, sizeof(struct st_mysqlnd_protocol_com_stmt_fetch_command));
+ if (command) {
+ command->context.conn = va_arg(args, MYSQLND_CONN_DATA *);
+ command->context.payload = va_arg(args, MYSQLND_CSTRING);
+
+ command->parent.free_command = mysqlnd_com_no_params_free_command;
+ command->parent.run = mysqlnd_com_stmt_fetch_run;
+ }
+
+ DBG_RETURN((struct st_mysqlnd_protocol_command *) command);
+}
+/* }}} */
+
+
+/************************** COM_STMT_RESET ******************************************/
+struct st_mysqlnd_protocol_com_stmt_reset_command
+{
+ struct st_mysqlnd_protocol_command parent;
+ struct st_mysqlnd_com_stmt_reset_context
+ {
+ MYSQLND_CONN_DATA * conn;
+ zend_ulong stmt_id;
+ } context;
+};
+
+
+/* {{{ mysqlnd_com_stmt_reset_run */
+static enum_func_status
+mysqlnd_com_stmt_reset_run(void *cmd)
+{
+ zend_uchar cmd_buf[MYSQLND_STMT_ID_LENGTH /* statement id */];
+ struct st_mysqlnd_protocol_com_stmt_reset_command * command = (struct st_mysqlnd_protocol_com_stmt_reset_command *) cmd;
+ enum_func_status ret = FAIL;
+ MYSQLND_CONN_DATA * conn = command->context.conn;
+ func_mysqlnd_protocol_payload_decoder_factory__send_command send_command = conn->payload_decoder_factory->m.send_command;
+ func_mysqlnd_protocol_payload_decoder_factory__send_command_handle_response send_command_handle_response = conn->payload_decoder_factory->m.send_command_handle_response;
+
+ DBG_ENTER("mysqlnd_com_stmt_reset_run");
+
+ int4store(cmd_buf, command->context.stmt_id);
+ ret = send_command(conn->payload_decoder_factory, COM_STMT_RESET, cmd_buf, sizeof(cmd_buf), FALSE,
+ &conn->state,
+ conn->error_info,
+ conn->upsert_status,
+ conn->stats,
+ conn->m->send_close,
+ conn);
+ if (PASS == ret) {
+ ret = send_command_handle_response(conn->payload_decoder_factory, PROT_OK_PACKET, FALSE, COM_STMT_RESET, TRUE,
+ conn->error_info, conn->upsert_status, &conn->last_message, conn->persistent);
+ }
+
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_com_stmt_reset_create_command */
+static struct st_mysqlnd_protocol_command *
+mysqlnd_com_stmt_reset_create_command(va_list args)
+{
+ struct st_mysqlnd_protocol_com_stmt_reset_command * command;
+ DBG_ENTER("mysqlnd_com_stmt_reset_create_command");
+ command = mnd_ecalloc(1, sizeof(struct st_mysqlnd_protocol_com_stmt_reset_command));
+ if (command) {
+ command->context.conn = va_arg(args, MYSQLND_CONN_DATA *);
+ command->context.stmt_id = va_arg(args, size_t);
+
+ command->parent.free_command = mysqlnd_com_no_params_free_command;
+ command->parent.run = mysqlnd_com_stmt_reset_run;
+ }
+
+ DBG_RETURN((struct st_mysqlnd_protocol_command *) command);
+}
+/* }}} */
+
+
+/************************** COM_STMT_SEND_LONG_DATA ******************************************/
+struct st_mysqlnd_protocol_com_stmt_send_long_data_command
+{
+ struct st_mysqlnd_protocol_command parent;
+ struct st_mysqlnd_com_stmt_send_long_data_context
+ {
+ MYSQLND_CONN_DATA * conn;
+ MYSQLND_CSTRING payload;
+ } context;
+};
+
+
+/* {{{ mysqlnd_com_stmt_send_long_data_run */
+static enum_func_status
+mysqlnd_com_stmt_send_long_data_run(void *cmd)
+{
+ struct st_mysqlnd_protocol_com_stmt_send_long_data_command * command = (struct st_mysqlnd_protocol_com_stmt_send_long_data_command *) cmd;
+ enum_func_status ret = FAIL;
+ MYSQLND_CONN_DATA * conn = command->context.conn;
+ func_mysqlnd_protocol_payload_decoder_factory__send_command send_command = conn->payload_decoder_factory->m.send_command;
+
+ DBG_ENTER("mysqlnd_com_stmt_send_long_data_run");
+
+ ret = send_command(conn->payload_decoder_factory, COM_STMT_SEND_LONG_DATA, (zend_uchar*) command->context.payload.s, command->context.payload.l, FALSE,
+ &conn->state,
+ conn->error_info,
+ conn->upsert_status,
+ conn->stats,
+ conn->m->send_close,
+ conn);
+
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_com_stmt_send_long_data_create_command */
+static struct st_mysqlnd_protocol_command *
+mysqlnd_com_stmt_send_long_data_create_command(va_list args)
+{
+ struct st_mysqlnd_protocol_com_stmt_send_long_data_command * command;
+ DBG_ENTER("mysqlnd_com_stmt_send_long_data_create_command");
+ command = mnd_ecalloc(1, sizeof(struct st_mysqlnd_protocol_com_stmt_send_long_data_command));
+ if (command) {
+ command->context.conn = va_arg(args, MYSQLND_CONN_DATA *);
+ command->context.payload = va_arg(args, MYSQLND_CSTRING);
+
+ command->parent.free_command = mysqlnd_com_no_params_free_command;
+ command->parent.run = mysqlnd_com_stmt_send_long_data_run;
+ }
+
+ DBG_RETURN((struct st_mysqlnd_protocol_command *) command);
+}
+/* }}} */
+
+
+/************************** COM_STMT_CLOSE ******************************************/
+struct st_mysqlnd_protocol_com_stmt_close_command
+{
+ struct st_mysqlnd_protocol_command parent;
+ struct st_mysqlnd_com_stmt_close_context
+ {
+ MYSQLND_CONN_DATA * conn;
+ zend_ulong stmt_id;
+ } context;
+};
+
+
+/* {{{ mysqlnd_com_stmt_close_run */
+static enum_func_status
+mysqlnd_com_stmt_close_run(void *cmd)
+{
+ zend_uchar cmd_buf[MYSQLND_STMT_ID_LENGTH /* statement id */];
+ struct st_mysqlnd_protocol_com_stmt_close_command * command = (struct st_mysqlnd_protocol_com_stmt_close_command *) cmd;
+ enum_func_status ret = FAIL;
+ MYSQLND_CONN_DATA * conn = command->context.conn;
+ func_mysqlnd_protocol_payload_decoder_factory__send_command send_command = conn->payload_decoder_factory->m.send_command;
+
+ DBG_ENTER("mysqlnd_com_stmt_close_run");
+
+ int4store(cmd_buf, command->context.stmt_id);
+ ret = send_command(conn->payload_decoder_factory, COM_STMT_CLOSE, cmd_buf, sizeof(cmd_buf), FALSE,
+ &conn->state,
+ conn->error_info,
+ conn->upsert_status,
+ conn->stats,
+ conn->m->send_close,
+ conn);
+
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_com_stmt_close_create_command */
+static struct st_mysqlnd_protocol_command *
+mysqlnd_com_stmt_close_create_command(va_list args)
+{
+ struct st_mysqlnd_protocol_com_stmt_close_command * command;
+ DBG_ENTER("mysqlnd_com_stmt_close_create_command");
+ command = mnd_ecalloc(1, sizeof(struct st_mysqlnd_protocol_com_stmt_close_command));
+ if (command) {
+ command->context.conn = va_arg(args, MYSQLND_CONN_DATA *);
+ command->context.stmt_id = va_arg(args, size_t);
+
+ command->parent.free_command = mysqlnd_com_no_params_free_command;
+ command->parent.run = mysqlnd_com_stmt_close_run;
+ }
+
+ DBG_RETURN((struct st_mysqlnd_protocol_command *) command);
+}
+/* }}} */
+
+
+
+/************************** COM_ENABLE_SSL ******************************************/
+struct st_mysqlnd_protocol_com_enable_ssl_command
+{
+ struct st_mysqlnd_protocol_command parent;
+ struct st_mysqlnd_com_enable_ssl_context
+ {
+ MYSQLND_CONN_DATA * conn;
+ size_t client_capabilities;
+ size_t server_capabilities;
+ unsigned int charset_no;
+ } context;
+};
+
+
+/* {{{ mysqlnd_com_enable_ssl_run */
+static enum_func_status
+mysqlnd_com_enable_ssl_run(void *cmd)
+{
+ struct st_mysqlnd_protocol_com_enable_ssl_command * command = (struct st_mysqlnd_protocol_com_enable_ssl_command *) cmd;
+ enum_func_status ret = FAIL;
+ MYSQLND_CONN_DATA * conn = command->context.conn;
+ MYSQLND_PACKET_AUTH * auth_packet;
+ size_t client_capabilities = command->context.client_capabilities;
+ size_t server_capabilities = command->context.server_capabilities;
+
+ DBG_ENTER("mysqlnd_com_enable_ssl_run");
+ DBG_INF_FMT("client_capability_flags=%lu", client_capabilities);
+ DBG_INF_FMT("CLIENT_LONG_PASSWORD= %d", client_capabilities & CLIENT_LONG_PASSWORD? 1:0);
+ DBG_INF_FMT("CLIENT_FOUND_ROWS= %d", client_capabilities & CLIENT_FOUND_ROWS? 1:0);
+ DBG_INF_FMT("CLIENT_LONG_FLAG= %d", client_capabilities & CLIENT_LONG_FLAG? 1:0);
+ DBG_INF_FMT("CLIENT_NO_SCHEMA= %d", client_capabilities & CLIENT_NO_SCHEMA? 1:0);
+ DBG_INF_FMT("CLIENT_COMPRESS= %d", client_capabilities & CLIENT_COMPRESS? 1:0);
+ DBG_INF_FMT("CLIENT_ODBC= %d", client_capabilities & CLIENT_ODBC? 1:0);
+ DBG_INF_FMT("CLIENT_LOCAL_FILES= %d", client_capabilities & CLIENT_LOCAL_FILES? 1:0);
+ DBG_INF_FMT("CLIENT_IGNORE_SPACE= %d", client_capabilities & CLIENT_IGNORE_SPACE? 1:0);
+ DBG_INF_FMT("CLIENT_PROTOCOL_41= %d", client_capabilities & CLIENT_PROTOCOL_41? 1:0);
+ DBG_INF_FMT("CLIENT_INTERACTIVE= %d", client_capabilities & CLIENT_INTERACTIVE? 1:0);
+ DBG_INF_FMT("CLIENT_SSL= %d", client_capabilities & CLIENT_SSL? 1:0);
+ DBG_INF_FMT("CLIENT_IGNORE_SIGPIPE= %d", client_capabilities & CLIENT_IGNORE_SIGPIPE? 1:0);
+ DBG_INF_FMT("CLIENT_TRANSACTIONS= %d", client_capabilities & CLIENT_TRANSACTIONS? 1:0);
+ DBG_INF_FMT("CLIENT_RESERVED= %d", client_capabilities & CLIENT_RESERVED? 1:0);
+ DBG_INF_FMT("CLIENT_SECURE_CONNECTION=%d", client_capabilities & CLIENT_SECURE_CONNECTION? 1:0);
+ DBG_INF_FMT("CLIENT_MULTI_STATEMENTS=%d", client_capabilities & CLIENT_MULTI_STATEMENTS? 1:0);
+ DBG_INF_FMT("CLIENT_MULTI_RESULTS= %d", client_capabilities & CLIENT_MULTI_RESULTS? 1:0);
+ DBG_INF_FMT("CLIENT_PS_MULTI_RESULTS=%d", client_capabilities & CLIENT_PS_MULTI_RESULTS? 1:0);
+ DBG_INF_FMT("CLIENT_CONNECT_ATTRS= %d", client_capabilities & CLIENT_PLUGIN_AUTH? 1:0);
+ DBG_INF_FMT("CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA= %d", client_capabilities & CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA? 1:0);
+ DBG_INF_FMT("CLIENT_CAN_HANDLE_EXPIRED_PASSWORDS= %d", client_capabilities & CLIENT_CAN_HANDLE_EXPIRED_PASSWORDS? 1:0);
+ DBG_INF_FMT("CLIENT_SESSION_TRACK= %d", client_capabilities & CLIENT_SESSION_TRACK? 1:0);
+ DBG_INF_FMT("CLIENT_SSL_VERIFY_SERVER_CERT= %d", client_capabilities & CLIENT_SSL_VERIFY_SERVER_CERT? 1:0);
+ DBG_INF_FMT("CLIENT_REMEMBER_OPTIONS= %d", client_capabilities & CLIENT_REMEMBER_OPTIONS? 1:0);
+
+ auth_packet = conn->payload_decoder_factory->m.get_auth_packet(conn->payload_decoder_factory, FALSE);
+ if (!auth_packet) {
+ SET_OOM_ERROR(conn->error_info);
+ goto end;
+ }
+ auth_packet->client_flags = client_capabilities;
+ auth_packet->max_packet_size = MYSQLND_ASSEMBLED_PACKET_MAX_SIZE;
+
+ auth_packet->charset_no = command->context.charset_no;
+
+#ifdef MYSQLND_SSL_SUPPORTED
+ if (client_capabilities & CLIENT_SSL) {
+ const zend_bool server_has_ssl = (server_capabilities & CLIENT_SSL)? TRUE:FALSE;
+ if (server_has_ssl == FALSE) {
+ goto close_conn;
+ } else {
+ enum mysqlnd_ssl_peer verify = client_capabilities & CLIENT_SSL_VERIFY_SERVER_CERT?
+ MYSQLND_SSL_PEER_VERIFY:
+ (client_capabilities & CLIENT_SSL_DONT_VERIFY_SERVER_CERT?
+ MYSQLND_SSL_PEER_DONT_VERIFY:
+ MYSQLND_SSL_PEER_DEFAULT);
+ DBG_INF("Switching to SSL");
+ if (!PACKET_WRITE(auth_packet)) {
+ goto close_conn;
+ }
+
+ conn->vio->data->m.set_client_option(conn->vio, MYSQL_OPT_SSL_VERIFY_SERVER_CERT, (const char *) &verify);
+
+ if (FAIL == conn->vio->data->m.enable_ssl(conn->vio)) {
+ goto end;
+ }
+ }
+ }
+#else
+ auth_packet->client_flags &= ~CLIENT_SSL;
+ if (!PACKET_WRITE(auth_packet)) {
+ goto close_conn;
+ }
+#endif
+ ret = PASS;
+end:
+ PACKET_FREE(auth_packet);
+ DBG_RETURN(ret);
+
+close_conn:
+ SET_CONNECTION_STATE(&conn->state, CONN_QUIT_SENT);
+ conn->m->send_close(conn);
+ SET_CLIENT_ERROR(conn->error_info, CR_SERVER_GONE_ERROR, UNKNOWN_SQLSTATE, mysqlnd_server_gone);
+ PACKET_FREE(auth_packet);
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_com_enable_ssl_create_command */
+static struct st_mysqlnd_protocol_command *
+mysqlnd_com_enable_ssl_create_command(va_list args)
+{
+ struct st_mysqlnd_protocol_com_enable_ssl_command * command;
+ DBG_ENTER("mysqlnd_com_enable_ssl_create_command");
+ command = mnd_ecalloc(1, sizeof(struct st_mysqlnd_protocol_com_enable_ssl_command));
+ if (command) {
+ command->context.conn = va_arg(args, MYSQLND_CONN_DATA *);
+ command->context.client_capabilities = va_arg(args, size_t);
+ command->context.server_capabilities = va_arg(args, size_t);
+ command->context.charset_no = va_arg(args, unsigned int);
+
+ command->parent.free_command = mysqlnd_com_no_params_free_command;
+ command->parent.run = mysqlnd_com_enable_ssl_run;
+ }
+
+ DBG_RETURN((struct st_mysqlnd_protocol_command *) command);
+}
+/* }}} */
+
+/************************** COM_READ_HANDSHAKE ******************************************/
+struct st_mysqlnd_protocol_com_handshake_command
+{
+ struct st_mysqlnd_protocol_command parent;
+ struct st_mysqlnd_com_handshake_context
+ {
+ MYSQLND_CONN_DATA * conn;
+ MYSQLND_CSTRING user;
+ MYSQLND_CSTRING passwd;
+ MYSQLND_CSTRING database;
+ size_t client_flags;
+ } context;
+};
+
+
+/* {{{ mysqlnd_com_handshake_run */
+static enum_func_status
+mysqlnd_com_handshake_run(void *cmd)
+{
+ struct st_mysqlnd_protocol_com_handshake_command * command = (struct st_mysqlnd_protocol_com_handshake_command *) cmd;
+ const char * user = command->context.user.s;
+
+ const char * passwd = command->context.passwd.s;
+ size_t passwd_len = command->context.passwd.l;
+
+ const char * db = command->context.database.s;
+ size_t db_len = command->context.database.l;
+
+ size_t mysql_flags = command->context.client_flags;
+
+ MYSQLND_CONN_DATA * conn = command->context.conn;
+ MYSQLND_PACKET_GREET * greet_packet;
+
+ DBG_ENTER("mysqlnd_conn_data::connect_handshake");
+ DBG_INF_FMT("stream=%p", conn->vio->data->m.get_stream(conn->vio));
+ DBG_INF_FMT("[user=%s] [db=%s:%d] [flags=%llu]", user, db, db_len, mysql_flags);
+
+ greet_packet = conn->payload_decoder_factory->m.get_greet_packet(conn->payload_decoder_factory, FALSE);
+ if (!greet_packet) {
+ SET_OOM_ERROR(conn->error_info);
+ DBG_RETURN(FAIL); /* OOM */
+ }
+
+ if (FAIL == PACKET_READ(greet_packet)) {
+ DBG_ERR("Error while reading greeting packet");
+ php_error_docref(NULL, E_WARNING, "Error while reading greeting packet. PID=%d", getpid());
+ goto err;
+ } else if (greet_packet->error_no) {
+ DBG_ERR_FMT("errorno=%u error=%s", greet_packet->error_no, greet_packet->error);
+ SET_CLIENT_ERROR(conn->error_info, greet_packet->error_no, greet_packet->sqlstate, greet_packet->error);
+ goto err;
+ } else if (greet_packet->pre41) {
+ DBG_ERR_FMT("Connecting to 3.22, 3.23 & 4.0 is not supported. Server is %-.32s", greet_packet->server_version);
+ php_error_docref(NULL, E_WARNING, "Connecting to 3.22, 3.23 & 4.0 "
+ " is not supported. Server is %-.32s", greet_packet->server_version);
+ SET_CLIENT_ERROR(conn->error_info, CR_NOT_IMPLEMENTED, UNKNOWN_SQLSTATE,
+ "Connecting to 3.22, 3.23 & 4.0 servers is not supported");
+ goto err;
+ }
+
+ conn->thread_id = greet_packet->thread_id;
+ conn->protocol_version = greet_packet->protocol_version;
+ conn->server_version = mnd_pestrdup(greet_packet->server_version, conn->persistent);
+
+ conn->greet_charset = mysqlnd_find_charset_nr(greet_packet->charset_no);
+ if (!conn->greet_charset) {
+ php_error_docref(NULL, E_WARNING,
+ "Server sent charset (%d) unknown to the client. Please, report to the developers", greet_packet->charset_no);
+ SET_CLIENT_ERROR(conn->error_info, CR_NOT_IMPLEMENTED, UNKNOWN_SQLSTATE,
+ "Server sent charset unknown to the client. Please, report to the developers");
+ goto err;
+ }
+
+ conn->server_capabilities = greet_packet->server_capabilities;
+
+ if (FAIL == mysqlnd_connect_run_authentication(conn, user, passwd, db, db_len, (size_t) passwd_len,
+ greet_packet->authentication_plugin_data, greet_packet->auth_protocol,
+ greet_packet->charset_no, greet_packet->server_capabilities,
+ conn->options, mysql_flags))
+ {
+ goto err;
+ }
+
+ UPSERT_STATUS_RESET(conn->upsert_status);
+ UPSERT_STATUS_SET_SERVER_STATUS(conn->upsert_status, greet_packet->server_status);
+
+ PACKET_FREE(greet_packet);
+ DBG_RETURN(PASS);
+err:
+ conn->server_capabilities = 0;
+ PACKET_FREE(greet_packet);
+ DBG_RETURN(FAIL);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_com_handshake_create_command */
+static struct st_mysqlnd_protocol_command *
+mysqlnd_com_handshake_create_command(va_list args)
+{
+ struct st_mysqlnd_protocol_com_handshake_command * command;
+ DBG_ENTER("mysqlnd_com_handshake_create_command");
+ command = mnd_ecalloc(1, sizeof(struct st_mysqlnd_protocol_com_handshake_command));
+ if (command) {
+ command->context.conn = va_arg(args, MYSQLND_CONN_DATA *);
+ command->context.user = *va_arg(args, const MYSQLND_CSTRING *);
+ command->context.passwd = *va_arg(args, const MYSQLND_CSTRING *);
+ command->context.database = *va_arg(args, const MYSQLND_CSTRING *);
+ command->context.client_flags = va_arg(args, size_t);
+
+ command->parent.free_command = mysqlnd_com_no_params_free_command;
+ command->parent.run = mysqlnd_com_handshake_run;
+ }
+
+ DBG_RETURN((struct st_mysqlnd_protocol_command *) command);
+}
+/* }}} */
+
+
+
+/* {{{ mysqlnd_get_command */
+static struct st_mysqlnd_protocol_command *
+mysqlnd_get_command(enum php_mysqlnd_server_command command, ...)
+{
+ struct st_mysqlnd_protocol_command * ret;
+ va_list args;
+ DBG_ENTER("mysqlnd_get_command");
+
+ va_start(args, command);
+ switch (command) {
+ case COM_SET_OPTION:
+ ret = mysqlnd_com_set_option_create_command(args);
+ break;
+ case COM_DEBUG:
+ ret = mysqlnd_com_debug_create_command(args);
+ break;
+ case COM_INIT_DB:
+ ret = mysqlnd_com_init_db_create_command(args);
+ break;
+ case COM_PING:
+ ret = mysqlnd_com_ping_create_command(args);
+ break;
+ case COM_STATISTICS:
+ ret = mysqlnd_com_statistics_create_command(args);
+ break;
+ case COM_PROCESS_KILL:
+ ret = mysqlnd_com_process_kill_create_command(args);
+ break;
+ case COM_REFRESH:
+ ret = mysqlnd_com_refresh_create_command(args);
+ break;
+ case COM_SHUTDOWN:
+ ret = mysqlnd_com_shutdown_create_command(args);
+ break;
+ case COM_QUIT:
+ ret = mysqlnd_com_quit_create_command(args);
+ break;
+ case COM_QUERY:
+ ret = mysqlnd_com_query_create_command(args);
+ break;
+ case COM_REAP_RESULT:
+ ret = mysqlnd_com_reap_result_create_command(args);
+ break;
+ case COM_CHANGE_USER:
+ ret = mysqlnd_com_change_user_create_command(args);
+ break;
+ case COM_STMT_PREPARE:
+ ret = mysqlnd_com_stmt_prepare_create_command(args);
+ break;
+ case COM_STMT_EXECUTE:
+ ret = mysqlnd_com_stmt_execute_create_command(args);
+ break;
+ case COM_STMT_FETCH:
+ ret = mysqlnd_com_stmt_fetch_create_command(args);
+ break;
+ case COM_STMT_RESET:
+ ret = mysqlnd_com_stmt_reset_create_command(args);
+ break;
+ case COM_STMT_SEND_LONG_DATA:
+ ret = mysqlnd_com_stmt_send_long_data_create_command(args);
+ break;
+ case COM_STMT_CLOSE:
+ ret = mysqlnd_com_stmt_close_create_command(args);
+ break;
+ case COM_ENABLE_SSL:
+ ret = mysqlnd_com_enable_ssl_create_command(args);
+ break;
+ case COM_HANDSHAKE:
+ ret = mysqlnd_com_handshake_create_command(args);
+ break;
+ default:
+ break;
+ }
+ va_end(args);
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+func_mysqlnd__command_factory mysqlnd_command_factory = mysqlnd_get_command;
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: noet sw=4 ts=4 fdm=marker
+ * vim<600: noet sw=4 ts=4
+ */
diff --git a/ext/mysqlnd/mysqlnd_commands.h b/ext/mysqlnd/mysqlnd_commands.h
new file mode 100644
index 0000000000..0df83a91a7
--- /dev/null
+++ b/ext/mysqlnd/mysqlnd_commands.h
@@ -0,0 +1,34 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 7 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 2006-2016 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: Andrey Hristov <andrey@php.net> |
+ | Ulf Wendel <uw@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#ifndef MYSQLND_COMMANDS_H
+#define MYSQLND_COMMANDS_H
+
+extern func_mysqlnd__command_factory mysqlnd_command_factory;
+
+#endif /* MYSQLND_COMMANDS_H */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: noet sw=4 ts=4 fdm=marker
+ * vim<600: noet sw=4 ts=4
+ */
diff --git a/ext/mysqlnd/mysqlnd.c b/ext/mysqlnd/mysqlnd_connection.c
index cc9da2f088..bb582dbf6a 100644
--- a/ext/mysqlnd/mysqlnd.c
+++ b/ext/mysqlnd/mysqlnd_connection.c
@@ -14,55 +14,218 @@
+----------------------------------------------------------------------+
| Authors: Andrey Hristov <andrey@php.net> |
| Ulf Wendel <uw@php.net> |
- | Georg Richter <georg@php.net> |
+----------------------------------------------------------------------+
*/
-/* $Id$ */
#include "php.h"
#include "mysqlnd.h"
+#include "mysqlnd_connection.h"
+#include "mysqlnd_vio.h"
+#include "mysqlnd_protocol_frame_codec.h"
+#include "mysqlnd_auth.h"
#include "mysqlnd_wireprotocol.h"
#include "mysqlnd_priv.h"
#include "mysqlnd_result.h"
#include "mysqlnd_statistics.h"
#include "mysqlnd_charset.h"
#include "mysqlnd_debug.h"
+#include "mysqlnd_ext_plugin.h"
#include "zend_smart_str.h"
-/*
- TODO :
- - Don't bind so tightly the metadata with the result set. This means
- that the metadata reading should not expect a MYSQLND_RES pointer, it
- does not need it, but return a pointer to the metadata (MYSQLND_FIELD *).
- For normal statements we will then just assign it to a member of
- MYSQLND_RES. For PS statements, it will stay as part of the statement
- (MYSQLND_STMT) between prepare and execute. At execute the new metadata
- will be sent by the server, so we will discard the old one and then
- finally attach it to the result set. This will make the code more clean,
- as a prepared statement won't have anymore stmt->result != NULL, as it
- is now, just to have where to store the metadata.
-
- - Change mysqlnd_simple_command to accept a heap dynamic array of MYSQLND_STRING
- terminated by a string with ptr being NULL. Thus, multi-part messages can be
- sent to the network like writev() and this can save at least for
- mysqlnd_stmt_send_long_data() new malloc. This change will probably make the
- code in few other places cleaner.
-*/
extern MYSQLND_CHARSET *mysqlnd_charsets;
-
-
-PHPAPI const char * const mysqlnd_old_passwd = "mysqlnd cannot connect to MySQL 4.1+ using the old insecure authentication. "
-"Please use an administration tool to reset your password with the command SET PASSWORD = PASSWORD('your_existing_password'). This will "
-"store a new, and more secure, hash value in mysql.user. If this user is used in other scripts executed by PHP 5.2 or earlier you might need to remove the old-passwords "
-"flag from your my.cnf file";
-
PHPAPI const char * const mysqlnd_server_gone = "MySQL server has gone away";
PHPAPI const char * const mysqlnd_out_of_sync = "Commands out of sync; you can't run this command now";
PHPAPI const char * const mysqlnd_out_of_memory = "Out of memory";
-PHPAPI MYSQLND_STATS *mysqlnd_global_stats = NULL;
+PHPAPI MYSQLND_STATS * mysqlnd_global_stats = NULL;
+
+
+/* {{{ mysqlnd_upsert_status::reset */
+void
+MYSQLND_METHOD(mysqlnd_upsert_status, reset)(MYSQLND_UPSERT_STATUS * const upsert_status)
+{
+ upsert_status->warning_count = 0;
+ upsert_status->server_status = 0;
+ upsert_status->affected_rows = 0;
+ upsert_status->last_insert_id = 0;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_upsert_status::set_affected_rows_to_error */
+void
+MYSQLND_METHOD(mysqlnd_upsert_status, set_affected_rows_to_error)(MYSQLND_UPSERT_STATUS * upsert_status)
+{
+ upsert_status->affected_rows = (uint64_t) ~0;
+}
+/* }}} */
+
+
+MYSQLND_CLASS_METHODS_START(mysqlnd_upsert_status)
+ MYSQLND_METHOD(mysqlnd_upsert_status, reset),
+ MYSQLND_METHOD(mysqlnd_upsert_status, set_affected_rows_to_error),
+MYSQLND_CLASS_METHODS_END;
+
+
+/* {{{ mysqlnd_upsert_status_init */
+void
+mysqlnd_upsert_status_init(MYSQLND_UPSERT_STATUS * const upsert_status)
+{
+ upsert_status->m = &MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_upsert_status);
+ upsert_status->m->reset(upsert_status);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_error_list_pdtor */
+static void
+mysqlnd_error_list_pdtor(void * pDest)
+{
+ MYSQLND_ERROR_LIST_ELEMENT * element = (MYSQLND_ERROR_LIST_ELEMENT *) pDest;
+
+ DBG_ENTER("mysqlnd_error_list_pdtor");
+ if (element->error) {
+ mnd_pefree(element->error, TRUE);
+ }
+ DBG_VOID_RETURN;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_error_info::reset */
+static void
+MYSQLND_METHOD(mysqlnd_error_info, reset)(MYSQLND_ERROR_INFO * const info)
+{
+ DBG_ENTER("mysqlnd_error_info::reset");
+
+ info->error_no = 0;
+ info->error[0] = '\0';
+ memset(info->sqlstate, 0, sizeof(info->sqlstate));
+ if (info->error_list) {
+ zend_llist_clean(info->error_list);
+ }
+
+ DBG_VOID_RETURN;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_error_info::set_client_error */
+static void
+MYSQLND_METHOD(mysqlnd_error_info, set_client_error)(MYSQLND_ERROR_INFO * const info,
+ const unsigned int err_no,
+ const char * const sqlstate,
+ const char * const error)
+{
+ DBG_ENTER("mysqlnd_error_info::set_client_error");
+ if (err_no) {
+ info->error_no = err_no;
+ strlcpy(info->sqlstate, sqlstate, sizeof(info->sqlstate));
+ strlcpy(info->error, error, sizeof(info->error));
+ if (info->error_list) {
+ MYSQLND_ERROR_LIST_ELEMENT error_for_the_list = {0};
+
+ error_for_the_list.error_no = err_no;
+ strlcpy(error_for_the_list.sqlstate, sqlstate, sizeof(error_for_the_list.sqlstate));
+ error_for_the_list.error = mnd_pestrdup(error, TRUE);
+ if (error_for_the_list.error) {
+ DBG_INF_FMT("adding error [%s] to the list", error_for_the_list.error);
+ zend_llist_add_element(info->error_list, &error_for_the_list);
+ }
+ }
+ } else {
+ info->m->reset(info);
+ }
+ DBG_VOID_RETURN;
+}
+/* }}} */
+
+
+MYSQLND_CLASS_METHODS_START(mysqlnd_error_info)
+ MYSQLND_METHOD(mysqlnd_error_info, reset),
+ MYSQLND_METHOD(mysqlnd_error_info, set_client_error),
+MYSQLND_CLASS_METHODS_END;
+
+
+
+/* {{{ mysqlnd_error_info_init */
+PHPAPI enum_func_status
+mysqlnd_error_info_init(MYSQLND_ERROR_INFO * const info, const zend_bool persistent)
+{
+ DBG_ENTER("mysqlnd_error_info_init");
+ info->m = mysqlnd_error_info_get_methods();
+ info->m->reset(info);
+
+ info->error_list = mnd_pecalloc(1, sizeof(zend_llist), persistent);
+ if (info->error_list) {
+ zend_llist_init(info->error_list, sizeof(MYSQLND_ERROR_LIST_ELEMENT), (llist_dtor_func_t) mysqlnd_error_list_pdtor, persistent);
+ }
+ info->persistent = persistent;
+ DBG_RETURN(info->error_list? PASS:FAIL);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_error_info_free_contents */
+PHPAPI void
+mysqlnd_error_info_free_contents(MYSQLND_ERROR_INFO * const info)
+{
+ DBG_ENTER("mysqlnd_error_info_free_contents");
+ info->m->reset(info);
+ if (info->error_list) {
+ mnd_pefree(info->error_list, info->persistent);
+ info->error_list = NULL;
+ }
+
+ DBG_VOID_RETURN;
+}
+/* }}} */
+
+
+
+
+/* {{{ mysqlnd_connection_state::get */
+static enum mysqlnd_connection_state
+MYSQLND_METHOD(mysqlnd_connection_state, get)(const struct st_mysqlnd_connection_state * const state_struct)
+{
+ DBG_ENTER("mysqlnd_connection_state::get");
+ DBG_INF_FMT("State=%u", state_struct->state);
+ DBG_RETURN(state_struct->state);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_connection_state::set */
+static void
+MYSQLND_METHOD(mysqlnd_connection_state, set)(struct st_mysqlnd_connection_state * const state_struct, const enum mysqlnd_connection_state state)
+{
+ DBG_ENTER("mysqlnd_connection_state::set");
+ DBG_INF_FMT("New state=%u", state);
+ state_struct->state = state;
+ DBG_VOID_RETURN;
+}
+/* }}} */
+
+
+MYSQLND_CLASS_METHODS_START(mysqlnd_connection_state)
+ MYSQLND_METHOD(mysqlnd_connection_state, get),
+ MYSQLND_METHOD(mysqlnd_connection_state, set),
+MYSQLND_CLASS_METHODS_END;
+
+
+/* {{{ mysqlnd_upsert_status_init */
+PHPAPI void
+mysqlnd_connection_state_init(struct st_mysqlnd_connection_state * const state)
+{
+ DBG_ENTER("mysqlnd_error_info_init");
+ state->m = &MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_connection_state);
+ state->state = CONN_ALLOCED;
+ DBG_VOID_RETURN;
+}
+/* }}} */
+
/* {{{ mysqlnd_conn_data::free_options */
@@ -118,36 +281,40 @@ MYSQLND_METHOD(mysqlnd_conn_data, free_contents)(MYSQLND_CONN_DATA * conn)
conn->current_result = NULL;
}
- if (conn->net) {
- conn->net->data->m.free_contents(conn->net);
+ if (conn->protocol_frame_codec) {
+ conn->protocol_frame_codec->data->m.free_contents(conn->protocol_frame_codec);
+ }
+
+ if (conn->vio) {
+ conn->vio->data->m.free_contents(conn->vio);
}
DBG_INF("Freeing memory of members");
- if (conn->host) {
- mnd_pefree(conn->host, pers);
- conn->host = NULL;
+ if (conn->hostname.s) {
+ mnd_pefree(conn->hostname.s, pers);
+ conn->hostname.s = NULL;
}
- if (conn->user) {
- mnd_pefree(conn->user, pers);
- conn->user = NULL;
+ if (conn->username.s) {
+ mnd_pefree(conn->username.s, pers);
+ conn->username.s = NULL;
}
- if (conn->passwd) {
- mnd_pefree(conn->passwd, pers);
- conn->passwd = NULL;
+ if (conn->password.s) {
+ mnd_pefree(conn->password.s, pers);
+ conn->password.s = NULL;
}
- if (conn->connect_or_select_db) {
- mnd_pefree(conn->connect_or_select_db, pers);
- conn->connect_or_select_db = NULL;
+ if (conn->connect_or_select_db.s) {
+ mnd_pefree(conn->connect_or_select_db.s, pers);
+ conn->connect_or_select_db.s = NULL;
}
- if (conn->unix_socket) {
- mnd_pefree(conn->unix_socket, pers);
- conn->unix_socket = NULL;
+ if (conn->unix_socket.s) {
+ mnd_pefree(conn->unix_socket.s, pers);
+ conn->unix_socket.s = NULL;
}
- DBG_INF_FMT("scheme=%s", conn->scheme);
- if (conn->scheme) {
- mnd_pefree(conn->scheme, pers);
- conn->scheme = NULL;
+ DBG_INF_FMT("scheme=%s", conn->scheme.s);
+ if (conn->scheme.s) {
+ mnd_pefree(conn->scheme.s, pers);
+ conn->scheme.s = NULL;
}
if (conn->server_version) {
mnd_pefree(conn->server_version, pers);
@@ -157,19 +324,15 @@ MYSQLND_METHOD(mysqlnd_conn_data, free_contents)(MYSQLND_CONN_DATA * conn)
mnd_pefree(conn->host_info, pers);
conn->host_info = NULL;
}
- if (conn->auth_plugin_data) {
- mnd_pefree(conn->auth_plugin_data, pers);
- conn->auth_plugin_data = NULL;
- }
- if (conn->last_message) {
- mnd_pefree(conn->last_message, pers);
- conn->last_message = NULL;
+ if (conn->authentication_plugin_data.s) {
+ mnd_pefree(conn->authentication_plugin_data.s, pers);
+ conn->authentication_plugin_data.s = NULL;
}
- if (conn->error_info->error_list) {
- zend_llist_clean(conn->error_info->error_list);
- mnd_pefree(conn->error_info->error_list, pers);
- conn->error_info->error_list = NULL;
+ if (conn->last_message.s) {
+ mnd_pefree(conn->last_message.s, pers);
+ conn->last_message.s = NULL;
}
+
conn->charset = NULL;
conn->greet_charset = NULL;
@@ -188,202 +351,33 @@ MYSQLND_METHOD_PRIVATE(mysqlnd_conn_data, dtor)(MYSQLND_CONN_DATA * conn)
conn->m->free_contents(conn);
conn->m->free_options(conn);
- if (conn->net) {
- mysqlnd_net_free(conn->net, conn->stats, conn->error_info);
- conn->net = NULL;
- }
-
- if (conn->protocol) {
- mysqlnd_protocol_free(conn->protocol);
- conn->protocol = NULL;
- }
-
- if (conn->stats) {
- mysqlnd_stats_end(conn->stats, conn->persistent);
- }
-
- mnd_pefree(conn, conn->persistent);
-
- DBG_VOID_RETURN;
-}
-/* }}} */
-
-
-/* {{{ mysqlnd_conn_data::simple_command_handle_response */
-static enum_func_status
-MYSQLND_METHOD(mysqlnd_conn_data, simple_command_handle_response)(MYSQLND_CONN_DATA * conn, enum mysqlnd_packet_type ok_packet,
- zend_bool silent, enum php_mysqlnd_server_command command,
- zend_bool ignore_upsert_status)
-{
- enum_func_status ret = FAIL;
-
- DBG_ENTER("mysqlnd_conn_data::simple_command_handle_response");
- DBG_INF_FMT("silent=%u packet=%u command=%s", silent, ok_packet, mysqlnd_command_to_text[command]);
-
- switch (ok_packet) {
- case PROT_OK_PACKET:{
- MYSQLND_PACKET_OK * ok_response = conn->protocol->m.get_ok_packet(conn->protocol, FALSE);
- if (!ok_response) {
- SET_OOM_ERROR(*conn->error_info);
- break;
- }
- if (FAIL == (ret = PACKET_READ(ok_response, conn))) {
- if (!silent) {
- DBG_ERR_FMT("Error while reading %s's OK packet", mysqlnd_command_to_text[command]);
- php_error_docref(NULL, E_WARNING, "Error while reading %s's OK packet. PID=%u",
- mysqlnd_command_to_text[command], getpid());
- }
- } else {
- DBG_INF_FMT("OK from server");
- if (0xFF == ok_response->field_count) {
- /* The server signalled error. Set the error */
- SET_CLIENT_ERROR(*conn->error_info, ok_response->error_no, ok_response->sqlstate, ok_response->error);
- ret = FAIL;
- /*
- Cover a protocol design error: error packet does not
- contain the server status. Therefore, the client has no way
- to find out whether there are more result sets of
- a multiple-result-set statement pending. Luckily, in 5.0 an
- error always aborts execution of a statement, wherever it is
- a multi-statement or a stored procedure, so it should be
- safe to unconditionally turn off the flag here.
- */
- conn->upsert_status->server_status &= ~SERVER_MORE_RESULTS_EXISTS;
- SET_ERROR_AFF_ROWS(conn);
- } else {
- SET_NEW_MESSAGE(conn->last_message, conn->last_message_len,
- ok_response->message, ok_response->message_len,
- conn->persistent);
-
- if (!ignore_upsert_status) {
- memset(conn->upsert_status, 0, sizeof(*conn->upsert_status));
- conn->upsert_status->warning_count = ok_response->warning_count;
- conn->upsert_status->server_status = ok_response->server_status;
- conn->upsert_status->affected_rows = ok_response->affected_rows;
- conn->upsert_status->last_insert_id = ok_response->last_insert_id;
- }
- }
- }
- PACKET_FREE(ok_response);
- break;
- }
- case PROT_EOF_PACKET:{
- MYSQLND_PACKET_EOF * ok_response = conn->protocol->m.get_eof_packet(conn->protocol, FALSE);
- if (!ok_response) {
- SET_OOM_ERROR(*conn->error_info);
- break;
- }
- if (FAIL == (ret = PACKET_READ(ok_response, conn))) {
- SET_CLIENT_ERROR(*conn->error_info, CR_MALFORMED_PACKET, UNKNOWN_SQLSTATE,
- "Malformed packet");
- if (!silent) {
- DBG_ERR_FMT("Error while reading %s's EOF packet", mysqlnd_command_to_text[command]);
- php_error_docref(NULL, E_WARNING, "Error while reading %s's EOF packet. PID=%d",
- mysqlnd_command_to_text[command], getpid());
- }
- } else if (0xFF == ok_response->field_count) {
- /* The server signalled error. Set the error */
- SET_CLIENT_ERROR(*conn->error_info, ok_response->error_no, ok_response->sqlstate, ok_response->error);
- SET_ERROR_AFF_ROWS(conn);
- } else if (0xFE != ok_response->field_count) {
- SET_CLIENT_ERROR(*conn->error_info, CR_MALFORMED_PACKET, UNKNOWN_SQLSTATE, "Malformed packet");
- if (!silent) {
- DBG_ERR_FMT("EOF packet expected, field count wasn't 0xFE but 0x%2X", ok_response->field_count);
- php_error_docref(NULL, E_WARNING, "EOF packet expected, field count wasn't 0xFE but 0x%2X",
- ok_response->field_count);
- }
- } else {
- DBG_INF_FMT("OK from server");
- }
- PACKET_FREE(ok_response);
- break;
- }
- default:
- SET_CLIENT_ERROR(*conn->error_info, CR_MALFORMED_PACKET, UNKNOWN_SQLSTATE, "Malformed packet");
- php_error_docref(NULL, E_ERROR, "Wrong response packet %u passed to the function", ok_packet);
- break;
+ if (conn->error_info) {
+ mysqlnd_error_info_free_contents(conn->error_info);
+ conn->error_info = NULL;
}
- DBG_INF(ret == PASS ? "PASS":"FAIL");
- DBG_RETURN(ret);
-}
-/* }}} */
-
-/* {{{ mysqlnd_conn_data::simple_command_send_request */
-static enum_func_status
-MYSQLND_METHOD(mysqlnd_conn_data, simple_command_send_request)(MYSQLND_CONN_DATA * conn, enum php_mysqlnd_server_command command,
- const zend_uchar * const arg, size_t arg_len, zend_bool silent, zend_bool ignore_upsert_status)
-{
- enum_func_status ret = PASS;
- MYSQLND_PACKET_COMMAND * cmd_packet;
-
- DBG_ENTER("mysqlnd_conn_data::simple_command_send_request");
- DBG_INF_FMT("command=%s silent=%u", mysqlnd_command_to_text[command], silent);
- DBG_INF_FMT("conn->server_status=%u", conn->upsert_status->server_status);
- DBG_INF_FMT("sending %u bytes", arg_len + 1); /* + 1 is for the command */
-
- switch (CONN_GET_STATE(conn)) {
- case CONN_READY:
- break;
- case CONN_QUIT_SENT:
- SET_CLIENT_ERROR(*conn->error_info, CR_SERVER_GONE_ERROR, UNKNOWN_SQLSTATE, mysqlnd_server_gone);
- DBG_ERR("Server is gone");
- DBG_RETURN(FAIL);
- default:
- SET_CLIENT_ERROR(*conn->error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, mysqlnd_out_of_sync);
- DBG_ERR_FMT("Command out of sync. State=%u", CONN_GET_STATE(conn));
- DBG_RETURN(FAIL);
+ if (conn->protocol_frame_codec) {
+ mysqlnd_pfc_free(conn->protocol_frame_codec, conn->stats, conn->error_info);
+ conn->protocol_frame_codec = NULL;
}
- SET_ERROR_AFF_ROWS(conn);
- SET_EMPTY_ERROR(*conn->error_info);
-
- cmd_packet = conn->protocol->m.get_command_packet(conn->protocol, FALSE);
- if (!cmd_packet) {
- SET_OOM_ERROR(*conn->error_info);
- DBG_RETURN(FAIL);
+ if (conn->vio) {
+ mysqlnd_vio_free(conn->vio, conn->stats, conn->error_info);
+ conn->vio = NULL;
}
- cmd_packet->command = command;
- if (arg && arg_len) {
- cmd_packet->argument = arg;
- cmd_packet->arg_len = arg_len;
+ if (conn->payload_decoder_factory) {
+ mysqlnd_protocol_payload_decoder_factory_free(conn->payload_decoder_factory);
+ conn->payload_decoder_factory = NULL;
}
- MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_COM_QUIT + command - 1 /* because of COM_SLEEP */ );
-
- if (! PACKET_WRITE(cmd_packet, conn)) {
- if (!silent) {
- DBG_ERR_FMT("Error while sending %s packet", mysqlnd_command_to_text[command]);
- php_error(E_WARNING, "Error while sending %s packet. PID=%d", mysqlnd_command_to_text[command], getpid());
- }
- CONN_SET_STATE(conn, CONN_QUIT_SENT);
- conn->m->send_close(conn);
- DBG_ERR("Server is gone");
- ret = FAIL;
+ if (conn->stats) {
+ mysqlnd_stats_end(conn->stats, conn->persistent);
}
- PACKET_FREE(cmd_packet);
- DBG_RETURN(ret);
-}
-/* }}} */
-
-
-/* {{{ mysqlnd_conn_data::simple_command */
-static enum_func_status
-MYSQLND_METHOD(mysqlnd_conn_data, simple_command)(MYSQLND_CONN_DATA * conn, enum php_mysqlnd_server_command command,
- const zend_uchar * const arg, size_t arg_len, enum mysqlnd_packet_type ok_packet, zend_bool silent,
- zend_bool ignore_upsert_status)
-{
- enum_func_status ret;
- DBG_ENTER("mysqlnd_conn_data::simple_command");
- ret = conn->m->simple_command_send_request(conn, command, arg, arg_len, silent, ignore_upsert_status);
- if (PASS == ret && ok_packet != PROT_LAST) {
- ret = conn->m->simple_command_handle_response(conn, ok_packet, silent, command, ignore_upsert_status);
- }
+ mnd_pefree(conn, conn->persistent);
- DBG_INF(ret == PASS ? "PASS":"FAIL");
- DBG_RETURN(ret);
+ DBG_VOID_RETURN;
}
/* }}} */
@@ -392,14 +386,15 @@ MYSQLND_METHOD(mysqlnd_conn_data, simple_command)(MYSQLND_CONN_DATA * conn, enum
static enum_func_status
MYSQLND_METHOD(mysqlnd_conn_data, set_server_option)(MYSQLND_CONN_DATA * const conn, enum_mysqlnd_server_option option)
{
- size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, set_server_option);
- zend_uchar buffer[2];
+ const size_t this_func = STRUCT_OFFSET(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn_data), set_server_option);
enum_func_status ret = FAIL;
DBG_ENTER("mysqlnd_conn_data::set_server_option");
if (PASS == conn->m->local_tx_start(conn, this_func)) {
-
- int2store(buffer, (unsigned int) option);
- ret = conn->m->simple_command(conn, COM_SET_OPTION, buffer, sizeof(buffer), PROT_EOF_PACKET, FALSE, TRUE);
+ struct st_mysqlnd_protocol_command * command = conn->command_factory(COM_SET_OPTION, conn, option);
+ if (command) {
+ ret = command->run(command);
+ command->free_command(command);
+ }
conn->m->local_tx_end(conn, this_func, ret);
}
@@ -415,9 +410,9 @@ MYSQLND_METHOD(mysqlnd_conn_data, restart_psession)(MYSQLND_CONN_DATA * conn)
DBG_ENTER("mysqlnd_conn_data::restart_psession");
MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_CONNECT_REUSED);
/* Free here what should not be seen by the next script */
- if (conn->last_message) {
- mnd_pefree(conn->last_message, conn->persistent);
- conn->last_message = NULL;
+ if (conn->last_message.s) {
+ mnd_pefree(conn->last_message.s, conn->persistent);
+ conn->last_message.s = NULL;
}
DBG_RETURN(PASS);
}
@@ -434,103 +429,6 @@ MYSQLND_METHOD(mysqlnd_conn_data, end_psession)(MYSQLND_CONN_DATA * conn)
/* }}} */
-/* {{{ mysqlnd_switch_to_ssl_if_needed */
-static enum_func_status
-mysqlnd_switch_to_ssl_if_needed(
- MYSQLND_CONN_DATA * conn,
- const MYSQLND_PACKET_GREET * const greet_packet,
- const MYSQLND_OPTIONS * const options,
- zend_ulong mysql_flags)
-{
- enum_func_status ret = FAIL;
- const MYSQLND_CHARSET * charset;
- MYSQLND_PACKET_AUTH * auth_packet;
- DBG_ENTER("mysqlnd_switch_to_ssl_if_needed");
- DBG_INF_FMT("client_capability_flags=%lu", mysql_flags);
- DBG_INF_FMT("CLIENT_LONG_PASSWORD= %d", mysql_flags & CLIENT_LONG_PASSWORD? 1:0);
- DBG_INF_FMT("CLIENT_FOUND_ROWS= %d", mysql_flags & CLIENT_FOUND_ROWS? 1:0);
- DBG_INF_FMT("CLIENT_LONG_FLAG= %d", mysql_flags & CLIENT_LONG_FLAG? 1:0);
- DBG_INF_FMT("CLIENT_NO_SCHEMA= %d", mysql_flags & CLIENT_NO_SCHEMA? 1:0);
- DBG_INF_FMT("CLIENT_COMPRESS= %d", mysql_flags & CLIENT_COMPRESS? 1:0);
- DBG_INF_FMT("CLIENT_ODBC= %d", mysql_flags & CLIENT_ODBC? 1:0);
- DBG_INF_FMT("CLIENT_LOCAL_FILES= %d", mysql_flags & CLIENT_LOCAL_FILES? 1:0);
- DBG_INF_FMT("CLIENT_IGNORE_SPACE= %d", mysql_flags & CLIENT_IGNORE_SPACE? 1:0);
- DBG_INF_FMT("CLIENT_PROTOCOL_41= %d", mysql_flags & CLIENT_PROTOCOL_41? 1:0);
- DBG_INF_FMT("CLIENT_INTERACTIVE= %d", mysql_flags & CLIENT_INTERACTIVE? 1:0);
- DBG_INF_FMT("CLIENT_SSL= %d", mysql_flags & CLIENT_SSL? 1:0);
- DBG_INF_FMT("CLIENT_IGNORE_SIGPIPE= %d", mysql_flags & CLIENT_IGNORE_SIGPIPE? 1:0);
- DBG_INF_FMT("CLIENT_TRANSACTIONS= %d", mysql_flags & CLIENT_TRANSACTIONS? 1:0);
- DBG_INF_FMT("CLIENT_RESERVED= %d", mysql_flags & CLIENT_RESERVED? 1:0);
- DBG_INF_FMT("CLIENT_SECURE_CONNECTION=%d", mysql_flags & CLIENT_SECURE_CONNECTION? 1:0);
- DBG_INF_FMT("CLIENT_MULTI_STATEMENTS=%d", mysql_flags & CLIENT_MULTI_STATEMENTS? 1:0);
- DBG_INF_FMT("CLIENT_MULTI_RESULTS= %d", mysql_flags & CLIENT_MULTI_RESULTS? 1:0);
- DBG_INF_FMT("CLIENT_PS_MULTI_RESULTS=%d", mysql_flags & CLIENT_PS_MULTI_RESULTS? 1:0);
- DBG_INF_FMT("CLIENT_CONNECT_ATTRS= %d", mysql_flags & CLIENT_PLUGIN_AUTH? 1:0);
- DBG_INF_FMT("CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA= %d", mysql_flags & CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA? 1:0);
- DBG_INF_FMT("CLIENT_CAN_HANDLE_EXPIRED_PASSWORDS= %d", mysql_flags & CLIENT_CAN_HANDLE_EXPIRED_PASSWORDS? 1:0);
- DBG_INF_FMT("CLIENT_SESSION_TRACK= %d", mysql_flags & CLIENT_SESSION_TRACK? 1:0);
- DBG_INF_FMT("CLIENT_SSL_DONT_VERIFY_SERVER_CERT= %d", mysql_flags & CLIENT_SSL_DONT_VERIFY_SERVER_CERT? 1:0);
- DBG_INF_FMT("CLIENT_SSL_VERIFY_SERVER_CERT= %d", mysql_flags & CLIENT_SSL_VERIFY_SERVER_CERT? 1:0);
- DBG_INF_FMT("CLIENT_REMEMBER_OPTIONS= %d", mysql_flags & CLIENT_REMEMBER_OPTIONS? 1:0);
-
- auth_packet = conn->protocol->m.get_auth_packet(conn->protocol, FALSE);
- if (!auth_packet) {
- SET_OOM_ERROR(*conn->error_info);
- goto end;
- }
- auth_packet->client_flags = mysql_flags;
- auth_packet->max_packet_size = MYSQLND_ASSEMBLED_PACKET_MAX_SIZE;
-
- if (options->charset_name && (charset = mysqlnd_find_charset_name(options->charset_name))) {
- auth_packet->charset_no = charset->nr;
- } else {
- auth_packet->charset_no = greet_packet->charset_no;
- }
-
-#ifdef MYSQLND_SSL_SUPPORTED
- if (mysql_flags & CLIENT_SSL) {
- zend_bool server_has_ssl = (greet_packet->server_capabilities & CLIENT_SSL)? TRUE:FALSE;
- if (server_has_ssl == FALSE) {
- goto close_conn;
- } else {
- enum mysqlnd_ssl_peer verify = mysql_flags & CLIENT_SSL_VERIFY_SERVER_CERT?
- MYSQLND_SSL_PEER_VERIFY:
- (mysql_flags & CLIENT_SSL_DONT_VERIFY_SERVER_CERT?
- MYSQLND_SSL_PEER_DONT_VERIFY:
- MYSQLND_SSL_PEER_DEFAULT);
- DBG_INF("Switching to SSL");
- if (!PACKET_WRITE(auth_packet, conn)) {
- goto close_conn;
- }
-
- conn->net->data->m.set_client_option(conn->net, MYSQL_OPT_SSL_VERIFY_SERVER_CERT, (const char *) &verify);
-
- if (FAIL == conn->net->data->m.enable_ssl(conn->net)) {
- goto end;
- }
- }
- }
-#else
- auth_packet->client_flags &= ~CLIENT_SSL;
- if (!PACKET_WRITE(auth_packet, conn)) {
- goto close_conn;
- }
-#endif
- ret = PASS;
-end:
- PACKET_FREE(auth_packet);
- DBG_RETURN(ret);
-
-close_conn:
- CONN_SET_STATE(conn, CONN_QUIT_SENT);
- conn->m->send_close(conn);
- SET_CLIENT_ERROR(*conn->error_info, CR_SERVER_GONE_ERROR, UNKNOWN_SQLSTATE, mysqlnd_server_gone);
- PACKET_FREE(auth_packet);
- DBG_RETURN(ret);
-}
-/* }}} */
-
-
/* {{{ mysqlnd_conn_data::fetch_auth_plugin_by_name */
static struct st_mysqlnd_authentication_plugin *
MYSQLND_METHOD(mysqlnd_conn_data, fetch_auth_plugin_by_name)(const char * const requested_protocol)
@@ -549,169 +447,6 @@ MYSQLND_METHOD(mysqlnd_conn_data, fetch_auth_plugin_by_name)(const char * const
/* }}} */
-/* {{{ mysqlnd_run_authentication */
-static enum_func_status
-mysqlnd_run_authentication(
- MYSQLND_CONN_DATA * conn,
- const char * const user,
- const char * const passwd,
- const size_t passwd_len,
- const char * const db,
- const size_t db_len,
- const zend_uchar * const auth_plugin_data,
- const size_t auth_plugin_data_len,
- const char * const auth_protocol,
- unsigned int charset_no,
- const MYSQLND_OPTIONS * const options,
- zend_ulong mysql_flags,
- zend_bool silent,
- zend_bool is_change_user
- )
-{
- enum_func_status ret = FAIL;
- zend_bool first_call = TRUE;
-
- char * switch_to_auth_protocol = NULL;
- size_t switch_to_auth_protocol_len = 0;
- char * requested_protocol = NULL;
- zend_uchar * plugin_data;
- size_t plugin_data_len;
-
- DBG_ENTER("mysqlnd_run_authentication");
-
- plugin_data_len = auth_plugin_data_len;
- plugin_data = mnd_emalloc(plugin_data_len + 1);
- if (!plugin_data) {
- goto end;
- }
- memcpy(plugin_data, auth_plugin_data, plugin_data_len);
- plugin_data[plugin_data_len] = '\0';
-
- requested_protocol = mnd_pestrdup(auth_protocol? auth_protocol : MYSQLND_DEFAULT_AUTH_PROTOCOL, FALSE);
- if (!requested_protocol) {
- goto end;
- }
-
- do {
- struct st_mysqlnd_authentication_plugin * auth_plugin = conn->m->fetch_auth_plugin_by_name(requested_protocol);
-
- if (!auth_plugin) {
- php_error_docref(NULL, E_WARNING, "The server requested authentication method unknown to the client [%s]", requested_protocol);
- SET_CLIENT_ERROR(*conn->error_info, CR_NOT_IMPLEMENTED, UNKNOWN_SQLSTATE, "The server requested authentication method unknown to the client");
- goto end;
- }
- DBG_INF("plugin found");
-
- {
- zend_uchar * switch_to_auth_protocol_data = NULL;
- size_t switch_to_auth_protocol_data_len = 0;
- zend_uchar * scrambled_data = NULL;
- size_t scrambled_data_len = 0;
-
- switch_to_auth_protocol = NULL;
- switch_to_auth_protocol_len = 0;
-
- if (conn->auth_plugin_data) {
- mnd_pefree(conn->auth_plugin_data, conn->persistent);
- conn->auth_plugin_data = NULL;
- }
- conn->auth_plugin_data_len = plugin_data_len;
- conn->auth_plugin_data = mnd_pemalloc(conn->auth_plugin_data_len, conn->persistent);
- if (!conn->auth_plugin_data) {
- SET_OOM_ERROR(*conn->error_info);
- goto end;
- }
- memcpy(conn->auth_plugin_data, plugin_data, plugin_data_len);
-
- DBG_INF_FMT("salt(%d)=[%.*s]", plugin_data_len, plugin_data_len, plugin_data);
- /* The data should be allocated with malloc() */
- scrambled_data =
- auth_plugin->methods.get_auth_data(NULL, &scrambled_data_len, conn, user, passwd, passwd_len,
- plugin_data, plugin_data_len, options, &conn->net->data->options, mysql_flags);
- if (conn->error_info->error_no) {
- goto end;
- }
- if (FALSE == is_change_user) {
- ret = mysqlnd_auth_handshake(conn, user, passwd, passwd_len, db, db_len, options, mysql_flags,
- charset_no,
- first_call,
- requested_protocol,
- scrambled_data, scrambled_data_len,
- &switch_to_auth_protocol, &switch_to_auth_protocol_len,
- &switch_to_auth_protocol_data, &switch_to_auth_protocol_data_len
- );
- } else {
- ret = mysqlnd_auth_change_user(conn, user, strlen(user), passwd, passwd_len, db, db_len, silent,
- first_call,
- requested_protocol,
- scrambled_data, scrambled_data_len,
- &switch_to_auth_protocol, &switch_to_auth_protocol_len,
- &switch_to_auth_protocol_data, &switch_to_auth_protocol_data_len
- );
- }
- first_call = FALSE;
- free(scrambled_data);
-
- DBG_INF_FMT("switch_to_auth_protocol=%s", switch_to_auth_protocol? switch_to_auth_protocol:"n/a");
- if (requested_protocol && switch_to_auth_protocol) {
- mnd_efree(requested_protocol);
- requested_protocol = switch_to_auth_protocol;
- }
-
- if (plugin_data) {
- mnd_efree(plugin_data);
- }
- plugin_data_len = switch_to_auth_protocol_data_len;
- plugin_data = switch_to_auth_protocol_data;
- }
- DBG_INF_FMT("conn->error_info->error_no = %d", conn->error_info->error_no);
- } while (ret == FAIL && conn->error_info->error_no == 0 && switch_to_auth_protocol != NULL);
-
- if (ret == PASS) {
- DBG_INF_FMT("saving requested_protocol=%s", requested_protocol);
- conn->m->set_client_option(conn, MYSQLND_OPT_AUTH_PROTOCOL, requested_protocol);
- }
-end:
- if (plugin_data) {
- mnd_efree(plugin_data);
- }
- if (requested_protocol) {
- mnd_efree(requested_protocol);
- }
-
- DBG_RETURN(ret);
-}
-/* }}} */
-
-
-/* {{{ mysqlnd_connect_run_authentication */
-static enum_func_status
-mysqlnd_connect_run_authentication(
- MYSQLND_CONN_DATA * conn,
- const char * const user,
- const char * const passwd,
- const char * const db,
- size_t db_len,
- size_t passwd_len,
- const MYSQLND_PACKET_GREET * const greet_packet,
- const MYSQLND_OPTIONS * const options,
- zend_ulong mysql_flags
- )
-{
- enum_func_status ret = FAIL;
- DBG_ENTER("mysqlnd_connect_run_authentication");
-
- ret = mysqlnd_switch_to_ssl_if_needed(conn, greet_packet, options, mysql_flags);
- if (PASS == ret) {
- ret = mysqlnd_run_authentication(conn, user, passwd, passwd_len, db, db_len,
- greet_packet->auth_plugin_data, greet_packet->auth_plugin_data_len, greet_packet->auth_protocol,
- greet_packet->charset_no, options, mysql_flags, FALSE /*silent*/, FALSE/*is_change*/);
- }
- DBG_RETURN(ret);
-}
-/* }}} */
-
-
/* {{{ mysqlnd_conn_data::execute_init_commands */
static enum_func_status
MYSQLND_METHOD(mysqlnd_conn_data, execute_init_commands)(MYSQLND_CONN_DATA * conn)
@@ -748,7 +483,10 @@ MYSQLND_METHOD(mysqlnd_conn_data, execute_init_commands)(MYSQLND_CONN_DATA * con
static unsigned int
MYSQLND_METHOD(mysqlnd_conn_data, get_updated_connect_flags)(MYSQLND_CONN_DATA * conn, unsigned int mysql_flags)
{
- MYSQLND_NET * net = conn->net;
+#ifdef MYSQLND_COMPRESSION_ENABLED
+ MYSQLND_PFC * pfc = conn->protocol_frame_codec;
+#endif
+ MYSQLND_VIO * vio = conn->vio;
DBG_ENTER("mysqlnd_conn_data::get_updated_connect_flags");
/* we allow load data local infile by default */
@@ -761,7 +499,7 @@ MYSQLND_METHOD(mysqlnd_conn_data, get_updated_connect_flags)(MYSQLND_CONN_DATA *
mysql_flags &= ~CLIENT_COMPRESS;
}
#else
- if (net && net->data->options.flags & MYSQLND_NET_FLAG_USE_COMPRESSION) {
+ if (pfc && pfc->data->flags & MYSQLND_PROTOCOL_FLAG_USE_COMPRESSION) {
mysql_flags |= CLIENT_COMPRESS;
}
#endif
@@ -770,8 +508,11 @@ MYSQLND_METHOD(mysqlnd_conn_data, get_updated_connect_flags)(MYSQLND_CONN_DATA *
mysql_flags &= ~CLIENT_SSL;
}
#else
- if (net && (net->data->options.ssl_key || net->data->options.ssl_cert ||
- net->data->options.ssl_ca || net->data->options.ssl_capath || net->data->options.ssl_cipher))
+ if (vio && (vio->data->options.ssl_key ||
+ vio->data->options.ssl_cert ||
+ vio->data->options.ssl_ca ||
+ vio->data->options.ssl_capath ||
+ vio->data->options.ssl_cipher))
{
mysql_flags |= CLIENT_SSL;
}
@@ -785,80 +526,62 @@ MYSQLND_METHOD(mysqlnd_conn_data, get_updated_connect_flags)(MYSQLND_CONN_DATA *
/* {{{ mysqlnd_conn_data::connect_handshake */
static enum_func_status
MYSQLND_METHOD(mysqlnd_conn_data, connect_handshake)(MYSQLND_CONN_DATA * conn,
- const char * const host, const char * const user,
- const char * const passwd, const unsigned int passwd_len,
- const char * const db, const unsigned int db_len,
+ const MYSQLND_CSTRING * const scheme,
+ const MYSQLND_CSTRING * const username,
+ const MYSQLND_CSTRING * const password,
+ const MYSQLND_CSTRING * const database,
const unsigned int mysql_flags)
{
- MYSQLND_PACKET_GREET * greet_packet;
- MYSQLND_NET * net = conn->net;
-
+ enum_func_status ret = FAIL;
DBG_ENTER("mysqlnd_conn_data::connect_handshake");
- greet_packet = conn->protocol->m.get_greet_packet(conn->protocol, FALSE);
- if (!greet_packet) {
- SET_OOM_ERROR(*conn->error_info);
- DBG_RETURN(FAIL); /* OOM */
- }
-
- if (FAIL == net->data->m.connect_ex(conn->net, conn->scheme, conn->scheme_len, conn->persistent,
- conn->stats, conn->error_info))
+ if (PASS == conn->vio->data->m.connect(conn->vio, *scheme, conn->persistent, conn->stats, conn->error_info) &&
+ PASS == conn->protocol_frame_codec->data->m.reset(conn->protocol_frame_codec, conn->stats, conn->error_info))
{
- goto err;
- }
-
- DBG_INF_FMT("stream=%p", net->data->m.get_stream(net));
-
- if (FAIL == PACKET_READ(greet_packet, conn)) {
- DBG_ERR("Error while reading greeting packet");
- php_error_docref(NULL, E_WARNING, "Error while reading greeting packet. PID=%d", getpid());
- goto err;
- } else if (greet_packet->error_no) {
- DBG_ERR_FMT("errorno=%u error=%s", greet_packet->error_no, greet_packet->error);
- SET_CLIENT_ERROR(*conn->error_info, greet_packet->error_no, greet_packet->sqlstate, greet_packet->error);
- goto err;
- } else if (greet_packet->pre41) {
- DBG_ERR_FMT("Connecting to 3.22, 3.23 & 4.0 is not supported. Server is %-.32s", greet_packet->server_version);
- php_error_docref(NULL, E_WARNING, "Connecting to 3.22, 3.23 & 4.0 "
- " is not supported. Server is %-.32s", greet_packet->server_version);
- SET_CLIENT_ERROR(*conn->error_info, CR_NOT_IMPLEMENTED, UNKNOWN_SQLSTATE,
- "Connecting to 3.22, 3.23 & 4.0 servers is not supported");
- goto err;
- }
-
- conn->thread_id = greet_packet->thread_id;
- conn->protocol_version = greet_packet->protocol_version;
- conn->server_version = mnd_pestrdup(greet_packet->server_version, conn->persistent);
-
- conn->greet_charset = mysqlnd_find_charset_nr(greet_packet->charset_no);
- if (!conn->greet_charset) {
- php_error_docref(NULL, E_WARNING,
- "Server sent charset (%d) unknown to the client. Please, report to the developers", greet_packet->charset_no);
- SET_CLIENT_ERROR(*conn->error_info, CR_NOT_IMPLEMENTED, UNKNOWN_SQLSTATE,
- "Server sent charset unknown to the client. Please, report to the developers");
- goto err;
+ size_t client_flags = mysql_flags;
+ struct st_mysqlnd_protocol_command * command = conn->command_factory(COM_HANDSHAKE, conn, username, password, database, client_flags);
+ if (command) {
+ ret = command->run(command);
+ command->free_command(command);
+ }
}
+ DBG_RETURN(ret);
+}
+/* }}} */
- conn->client_flag = mysql_flags;
- conn->server_capabilities = greet_packet->server_capabilities;
-
- if (FAIL == mysqlnd_connect_run_authentication(conn, user, passwd, db, db_len, (size_t) passwd_len,
- greet_packet, conn->options, mysql_flags))
- {
- goto err;
+/* {{{ mysqlnd_conn_data::get_scheme */
+static MYSQLND_STRING
+MYSQLND_METHOD(mysqlnd_conn_data, get_scheme)(MYSQLND_CONN_DATA * conn, MYSQLND_CSTRING hostname, MYSQLND_CSTRING *socket_or_pipe, unsigned int port, zend_bool * unix_socket, zend_bool * named_pipe)
+{
+ MYSQLND_STRING transport;
+ DBG_ENTER("mysqlnd_conn_data::get_scheme");
+#ifndef PHP_WIN32
+ if (hostname.l == sizeof("localhost") - 1 && !strncasecmp(hostname.s, "localhost", hostname.l)) {
+ DBG_INF_FMT("socket=%s", socket_or_pipe->s? socket_or_pipe->s:"n/a");
+ if (!socket_or_pipe->s) {
+ socket_or_pipe->s = "/tmp/mysql.sock";
+ socket_or_pipe->l = strlen(socket_or_pipe->s);
+ }
+ transport.l = mnd_sprintf(&transport.s, 0, "unix://%s", socket_or_pipe->s);
+ *unix_socket = TRUE;
+#else
+ if (hostname.l == sizeof(".") - 1 && hostname.s[0] == '.') {
+ /* named pipe in socket */
+ if (!socket_or_pipe->s) {
+ socket_or_pipe->s = "\\\\.\\pipe\\MySQL";
+ socket_or_pipe->l = strlen(socket_or_pipe->s);
+ }
+ transport.l = mnd_sprintf(&transport.s, 0, "pipe://%s", socket_or_pipe->s);
+ *named_pipe = TRUE;
+#endif
+ } else {
+ if (!port) {
+ port = 3306;
+ }
+ transport.l = mnd_sprintf(&transport.s, 0, "tcp://%s:%u", hostname.s, port);
}
- memset(conn->upsert_status, 0, sizeof(*conn->upsert_status));
- conn->upsert_status->warning_count = 0;
- conn->upsert_status->server_status = greet_packet->server_status;
- conn->upsert_status->affected_rows = 0;
-
- PACKET_FREE(greet_packet);
- DBG_RETURN(PASS);
-err:
- conn->client_flag = 0;
- conn->server_capabilities = 0;
- PACKET_FREE(greet_packet);
- DBG_RETURN(FAIL);
+ DBG_INF_FMT("transport=%s", transport.s? transport.s:"OOM");
+ DBG_RETURN(transport);
}
/* }}} */
@@ -866,22 +589,23 @@ err:
/* {{{ mysqlnd_conn_data::connect */
static enum_func_status
MYSQLND_METHOD(mysqlnd_conn_data, connect)(MYSQLND_CONN_DATA * conn,
- const char *host, const char *user,
- const char *passwd, unsigned int passwd_len,
- const char *db, unsigned int db_len,
- unsigned int port,
- const char *socket_or_pipe,
- unsigned int mysql_flags
- )
-{
- size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, connect);
- size_t host_len;
+ MYSQLND_CSTRING hostname,
+ MYSQLND_CSTRING username,
+ MYSQLND_CSTRING password,
+ MYSQLND_CSTRING database,
+ unsigned int port,
+ MYSQLND_CSTRING socket_or_pipe,
+ unsigned int mysql_flags
+ )
+{
+ const size_t this_func = STRUCT_OFFSET(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn_data), connect);
zend_bool unix_socket = FALSE;
zend_bool named_pipe = FALSE;
zend_bool reconnect = FALSE;
zend_bool saved_compression = FALSE;
zend_bool local_tx_started = FALSE;
- MYSQLND_NET * net = conn->net;
+ MYSQLND_PFC * pfc = conn->protocol_frame_codec;
+ MYSQLND_STRING transport = { NULL, 0 };
DBG_ENTER("mysqlnd_conn_data::connect");
DBG_INF_FMT("conn=%p", conn);
@@ -891,189 +615,161 @@ MYSQLND_METHOD(mysqlnd_conn_data, connect)(MYSQLND_CONN_DATA * conn,
}
local_tx_started = TRUE;
- SET_EMPTY_ERROR(*conn->error_info);
- SET_ERROR_AFF_ROWS(conn);
+ SET_EMPTY_ERROR(conn->error_info);
+ UPSERT_STATUS_SET_AFFECTED_ROWS_TO_ERROR(conn->upsert_status);
DBG_INF_FMT("host=%s user=%s db=%s port=%u flags=%u persistent=%u state=%u",
- host?host:"", user?user:"", db?db:"", port, mysql_flags,
- conn? conn->persistent:0, conn? CONN_GET_STATE(conn):-1);
+ hostname.s?hostname.s:"", username.s?username.s:"", database.s?database.s:"", port, mysql_flags,
+ conn? conn->persistent:0, conn? GET_CONNECTION_STATE(&conn->state):-1);
- if (CONN_GET_STATE(conn) > CONN_ALLOCED && CONN_GET_STATE(conn) ) {
+ if (GET_CONNECTION_STATE(&conn->state) > CONN_ALLOCED) {
DBG_INF("Connecting on a connected handle.");
- if (CONN_GET_STATE(conn) < CONN_QUIT_SENT) {
+ if (GET_CONNECTION_STATE(&conn->state) < CONN_QUIT_SENT) {
MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_CLOSE_IMPLICIT);
reconnect = TRUE;
conn->m->send_close(conn);
}
conn->m->free_contents(conn);
- MYSQLND_DEC_CONN_STATISTIC(conn->stats, STAT_OPENED_CONNECTIONS);
- if (conn->persistent) {
- MYSQLND_DEC_CONN_STATISTIC(conn->stats, STAT_OPENED_PERSISTENT_CONNECTIONS);
- }
/* Now reconnect using the same handle */
- if (net->data->compressed) {
+ if (pfc->data->compressed) {
/*
- we need to save the state. As we will re-connect, net->compressed should be off, or
+ we need to save the state. As we will re-connect, pfc->compressed should be off, or
we will look for a compression header as part of the greet message, but there will
be none.
*/
saved_compression = TRUE;
- net->data->compressed = FALSE;
+ pfc->data->compressed = FALSE;
}
- if (net->data->ssl) {
- net->data->ssl = FALSE;
+ if (pfc->data->ssl) {
+ pfc->data->ssl = FALSE;
}
} else {
unsigned int max_allowed_size = MYSQLND_ASSEMBLED_PACKET_MAX_SIZE;
conn->m->set_client_option(conn, MYSQLND_OPT_MAX_ALLOWED_PACKET, (char *)&max_allowed_size);
}
- if (!host || !host[0]) {
- host = "localhost";
+ if (!hostname.s || !hostname.s[0]) {
+ hostname.s = "localhost";
+ hostname.l = strlen(hostname.s);
}
- if (!user) {
+ if (!username.s) {
DBG_INF_FMT("no user given, using empty string");
- user = "";
+ username.s = "";
+ username.l = 0;
}
- if (!passwd) {
+ if (!password.s) {
DBG_INF_FMT("no password given, using empty string");
- passwd = "";
- passwd_len = 0;
+ password.s = "";
+ password.l = 0;
}
- if (!db) {
+ if (!database.s) {
DBG_INF_FMT("no db given, using empty string");
- db = "";
- db_len = 0;
+ database.s = "";
+ database.l = 0;
} else {
mysql_flags |= CLIENT_CONNECT_WITH_DB;
}
- host_len = strlen(host);
- {
- char * transport = NULL;
- int transport_len;
-#ifndef PHP_WIN32
- if (host_len == sizeof("localhost") - 1 && !strncasecmp(host, "localhost", host_len)) {
- DBG_INF_FMT("socket=%s", socket_or_pipe? socket_or_pipe:"n/a");
- if (!socket_or_pipe) {
- socket_or_pipe = "/tmp/mysql.sock";
- }
- transport_len = mnd_sprintf(&transport, 0, "unix://%s", socket_or_pipe);
- unix_socket = TRUE;
-#else
- if (host_len == sizeof(".") - 1 && host[0] == '.') {
- /* named pipe in socket */
- if (!socket_or_pipe) {
- socket_or_pipe = "\\\\.\\pipe\\MySQL";
- }
- transport_len = mnd_sprintf(&transport, 0, "pipe://%s", socket_or_pipe);
- named_pipe = TRUE;
-#endif
- } else {
- if (!port) {
- port = 3306;
- }
- transport_len = mnd_sprintf(&transport, 0, "tcp://%s:%u", host, port);
- }
- if (!transport) {
- SET_OOM_ERROR(*conn->error_info);
- goto err; /* OOM */
- }
- DBG_INF_FMT("transport=%s conn->scheme=%s", transport, conn->scheme);
- conn->scheme = mnd_pestrndup(transport, transport_len, conn->persistent);
- conn->scheme_len = transport_len;
- mnd_sprintf_free(transport);
- transport = NULL;
- if (!conn->scheme) {
- goto err; /* OOM */
- }
- }
+ transport = conn->m->get_scheme(conn, hostname, &socket_or_pipe, port, &unix_socket, &named_pipe);
mysql_flags = conn->m->get_updated_connect_flags(conn, mysql_flags);
- if (FAIL == conn->m->connect_handshake(conn, host, user, passwd, passwd_len, db, db_len, mysql_flags)) {
- goto err;
+ {
+ const MYSQLND_CSTRING scheme = { transport.s, transport.l };
+ if (FAIL == conn->m->connect_handshake(conn, &scheme, &username, &password, &database, mysql_flags)) {
+ goto err;
+ }
}
{
- CONN_SET_STATE(conn, CONN_READY);
+ SET_CONNECTION_STATE(&conn->state, CONN_READY);
if (saved_compression) {
- net->data->compressed = TRUE;
+ pfc->data->compressed = TRUE;
}
/*
If a connect on a existing handle is performed and mysql_flags is
passed which doesn't CLIENT_COMPRESS, then we need to overwrite the value
which we set based on saved_compression.
*/
- net->data->compressed = mysql_flags & CLIENT_COMPRESS? TRUE:FALSE;
+ pfc->data->compressed = mysql_flags & CLIENT_COMPRESS? TRUE:FALSE;
+
+
+ conn->scheme.s = mnd_pestrndup(transport.s, transport.l, conn->persistent);
+ conn->scheme.l = transport.l;
+ if (transport.s) {
+ mnd_sprintf_free(transport.s);
+ transport.s = NULL;
+ }
+
+ if (!conn->scheme.s) {
+ goto err; /* OOM */
+ }
- conn->user_len = strlen(user);
- conn->user = mnd_pestrndup(user, conn->user_len, conn->persistent);
- conn->passwd = mnd_pestrndup(passwd, passwd_len, conn->persistent);
- conn->passwd_len = passwd_len;
+ conn->username.l = username.l;
+ conn->username.s = mnd_pestrndup(username.s, conn->username.l, conn->persistent);
+ conn->password.l = password.l;
+ conn->password.s = mnd_pestrndup(password.s, conn->password.l, conn->persistent);
conn->port = port;
- conn->connect_or_select_db = mnd_pestrndup(db, db_len, conn->persistent);
- conn->connect_or_select_db_len = db_len;
+ conn->connect_or_select_db.l = database.l;
+ conn->connect_or_select_db.s = mnd_pestrndup(database.s, conn->connect_or_select_db.l, conn->persistent);
- if (!conn->user || !conn->passwd || !conn->connect_or_select_db) {
- SET_OOM_ERROR(*conn->error_info);
+ if (!conn->username.s || !conn->password.s|| !conn->connect_or_select_db.s) {
+ SET_OOM_ERROR(conn->error_info);
goto err; /* OOM */
}
if (!unix_socket && !named_pipe) {
- conn->host = mnd_pestrndup(host, host_len, conn->persistent);
- if (!conn->host) {
- SET_OOM_ERROR(*conn->error_info);
+ conn->hostname.s = mnd_pestrndup(hostname.s, hostname.l, conn->persistent);
+ if (!conn->hostname.s) {
+ SET_OOM_ERROR(conn->error_info);
goto err; /* OOM */
}
- conn->host_len = host_len;
+ conn->hostname.l = hostname.l;
{
char *p;
- mnd_sprintf(&p, 0, "%s via TCP/IP", conn->host);
+ mnd_sprintf(&p, 0, "%s via TCP/IP", conn->hostname);
if (!p) {
- SET_OOM_ERROR(*conn->error_info);
+ SET_OOM_ERROR(conn->error_info);
goto err; /* OOM */
}
conn->host_info = mnd_pestrdup(p, conn->persistent);
mnd_sprintf_free(p);
if (!conn->host_info) {
- SET_OOM_ERROR(*conn->error_info);
+ SET_OOM_ERROR(conn->error_info);
goto err; /* OOM */
}
}
} else {
- conn->unix_socket = mnd_pestrdup(socket_or_pipe, conn->persistent);
+ conn->unix_socket.s = mnd_pestrdup(socket_or_pipe.s, conn->persistent);
if (unix_socket) {
conn->host_info = mnd_pestrdup("Localhost via UNIX socket", conn->persistent);
} else if (named_pipe) {
char *p;
- mnd_sprintf(&p, 0, "%s via named pipe", conn->unix_socket);
+ mnd_sprintf(&p, 0, "%s via named pipe", conn->unix_socket.s);
if (!p) {
- SET_OOM_ERROR(*conn->error_info);
+ SET_OOM_ERROR(conn->error_info);
goto err; /* OOM */
}
conn->host_info = mnd_pestrdup(p, conn->persistent);
mnd_sprintf_free(p);
if (!conn->host_info) {
- SET_OOM_ERROR(*conn->error_info);
+ SET_OOM_ERROR(conn->error_info);
goto err; /* OOM */
}
} else {
php_error_docref(NULL, E_WARNING, "Impossible. Should be either socket or a pipe. Report a bug!");
}
- if (!conn->unix_socket || !conn->host_info) {
- SET_OOM_ERROR(*conn->error_info);
+ if (!conn->unix_socket.s || !conn->host_info) {
+ SET_OOM_ERROR(conn->error_info);
goto err; /* OOM */
}
- conn->unix_socket_len = strlen(conn->unix_socket);
+ conn->unix_socket.l = strlen(conn->unix_socket.s);
}
- conn->max_packet_size = MYSQLND_ASSEMBLED_PACKET_MAX_SIZE;
- /* todo: check if charset is available */
- SET_EMPTY_ERROR(*conn->error_info);
+ SET_EMPTY_ERROR(conn->error_info);
mysqlnd_local_infile_default(conn);
@@ -1095,12 +791,15 @@ MYSQLND_METHOD(mysqlnd_conn_data, connect)(MYSQLND_CONN_DATA * conn,
DBG_RETURN(PASS);
}
err:
+ if (transport.s) {
+ mnd_sprintf_free(transport.s);
+ transport.s = NULL;
+ }
- DBG_ERR_FMT("[%u] %.128s (trying to connect via %s)", conn->error_info->error_no, conn->error_info->error, conn->scheme);
+ DBG_ERR_FMT("[%u] %.128s (trying to connect via %s)", conn->error_info->error_no, conn->error_info->error, conn->scheme.s);
if (!conn->error_info->error_no) {
- SET_CLIENT_ERROR(*conn->error_info, CR_CONNECTION_ERROR, UNKNOWN_SQLSTATE, conn->error_info->error? conn->error_info->error:"Unknown error");
- php_error_docref(NULL, E_WARNING, "[%u] %.128s (trying to connect via %s)",
- conn->error_info->error_no, conn->error_info->error, conn->scheme);
+ SET_CLIENT_ERROR(conn->error_info, CR_CONNECTION_ERROR, UNKNOWN_SQLSTATE, conn->error_info->error? conn->error_info->error:"Unknown error");
+ php_error_docref(NULL, E_WARNING, "[%u] %.128s (trying to connect via %s)", conn->error_info->error_no, conn->error_info->error, conn->scheme.s);
}
conn->m->free_contents(conn);
@@ -1117,15 +816,15 @@ err:
/* {{{ mysqlnd_conn::connect */
static enum_func_status
MYSQLND_METHOD(mysqlnd_conn, connect)(MYSQLND * conn_handle,
- const char * host, const char * user,
- const char * passwd, unsigned int passwd_len,
- const char * db, unsigned int db_len,
- unsigned int port,
- const char * socket_or_pipe,
- unsigned int mysql_flags
- )
-{
- size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, connect);
+ const MYSQLND_CSTRING hostname,
+ const MYSQLND_CSTRING username,
+ const MYSQLND_CSTRING password,
+ const MYSQLND_CSTRING database,
+ unsigned int port,
+ const MYSQLND_CSTRING socket_or_pipe,
+ unsigned int mysql_flags)
+{
+ const size_t this_func = STRUCT_OFFSET(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn_data), connect);
enum_func_status ret = FAIL;
MYSQLND_CONN_DATA * conn = conn_handle->data;
@@ -1133,7 +832,7 @@ MYSQLND_METHOD(mysqlnd_conn, connect)(MYSQLND * conn_handle,
if (PASS == conn->m->local_tx_start(conn, this_func)) {
mysqlnd_options4(conn_handle, MYSQL_OPT_CONNECT_ATTR_ADD, "_client_name", "mysqlnd");
- ret = conn->m->connect(conn, host, user, passwd, passwd_len, db, db_len, port, socket_or_pipe, mysql_flags);
+ ret = conn->m->connect(conn, hostname, username, password, database, port, socket_or_pipe, mysql_flags);
conn->m->local_tx_end(conn, this_func, FAIL);
}
@@ -1142,57 +841,15 @@ MYSQLND_METHOD(mysqlnd_conn, connect)(MYSQLND * conn_handle,
/* }}} */
-/* {{{ mysqlnd_connect */
-PHPAPI MYSQLND * mysqlnd_connect(MYSQLND * conn_handle,
- const char * host, const char * user,
- const char * passwd, unsigned int passwd_len,
- const char * db, unsigned int db_len,
- unsigned int port,
- const char * socket_or_pipe,
- unsigned int mysql_flags,
- unsigned int client_api_flags
- )
-{
- enum_func_status ret = FAIL;
- zend_bool self_alloced = FALSE;
-
- DBG_ENTER("mysqlnd_connect");
- DBG_INF_FMT("host=%s user=%s db=%s port=%u flags=%u", host?host:"", user?user:"", db?db:"", port, mysql_flags);
-
- if (!conn_handle) {
- self_alloced = TRUE;
- if (!(conn_handle = mysqlnd_init(client_api_flags, FALSE))) {
- /* OOM */
- DBG_RETURN(NULL);
- }
- }
-
- ret = conn_handle->m->connect(conn_handle, host, user, passwd, passwd_len, db, db_len, port, socket_or_pipe, mysql_flags);
-
- if (ret == FAIL) {
- if (self_alloced) {
- /*
- We have alloced, thus there are no references to this
- object - we are free to kill it!
- */
- conn_handle->m->dtor(conn_handle);
- }
- DBG_RETURN(NULL);
- }
- DBG_RETURN(conn_handle);
-}
-/* }}} */
-
-
/* {{{ mysqlnd_conn_data::query */
/*
If conn->error_info->error_no is not zero, then we had an error.
Still the result from the query is PASS
*/
static enum_func_status
-MYSQLND_METHOD(mysqlnd_conn_data, query)(MYSQLND_CONN_DATA * conn, const char * query, unsigned int query_len)
+MYSQLND_METHOD(mysqlnd_conn_data, query)(MYSQLND_CONN_DATA * conn, const char * const query, const size_t query_len)
{
- size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, query);
+ const size_t this_func = STRUCT_OFFSET(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn_data), query);
enum_func_status ret = FAIL;
DBG_ENTER("mysqlnd_conn_data::query");
DBG_INF_FMT("conn=%p conn=%llu query=%s", conn, conn->thread_id, query);
@@ -1202,8 +859,8 @@ MYSQLND_METHOD(mysqlnd_conn_data, query)(MYSQLND_CONN_DATA * conn, const char *
PASS == conn->m->reap_query(conn, MYSQLND_REAP_RESULT_IMPLICIT))
{
ret = PASS;
- if (conn->last_query_type == QUERY_UPSERT && conn->upsert_status->affected_rows) {
- MYSQLND_INC_CONN_STATISTIC_W_VALUE(conn->stats, STAT_ROWS_AFFECTED_NORMAL, conn->upsert_status->affected_rows);
+ if (conn->last_query_type == QUERY_UPSERT && UPSERT_STATUS_GET_AFFECTED_ROWS(conn->upsert_status)) {
+ MYSQLND_INC_CONN_STATISTIC_W_VALUE(conn->stats, STAT_ROWS_AFFECTED_NORMAL, UPSERT_STATUS_GET_AFFECTED_ROWS(conn->upsert_status));
}
}
conn->m->local_tx_end(conn, this_func, ret);
@@ -1215,25 +872,29 @@ MYSQLND_METHOD(mysqlnd_conn_data, query)(MYSQLND_CONN_DATA * conn, const char *
/* {{{ mysqlnd_conn_data::send_query */
static enum_func_status
-MYSQLND_METHOD(mysqlnd_conn_data, send_query)(MYSQLND_CONN_DATA * conn, const char * query, unsigned int query_len,
+MYSQLND_METHOD(mysqlnd_conn_data, send_query)(MYSQLND_CONN_DATA * conn, const char * const query, const size_t query_len,
enum_mysqlnd_send_query_type type, zval *read_cb, zval *err_cb)
{
- size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, send_query);
+ const size_t this_func = STRUCT_OFFSET(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn_data), send_query);
enum_func_status ret = FAIL;
DBG_ENTER("mysqlnd_conn_data::send_query");
DBG_INF_FMT("conn=%llu query=%s", conn->thread_id, query);
- DBG_INF_FMT("conn->server_status=%u", conn->upsert_status->server_status);
+ DBG_INF_FMT("conn->server_status=%u", UPSERT_STATUS_GET_SERVER_STATUS(conn->upsert_status));
- if (PASS == conn->m->local_tx_start(conn, this_func)) {
- ret = conn->m->simple_command(conn, COM_QUERY, (zend_uchar *) query, query_len,
- PROT_LAST /* we will handle the OK packet*/,
- FALSE, FALSE);
- if (PASS == ret) {
- CONN_SET_STATE(conn, CONN_QUERY_SENT);
+ if (type == MYSQLND_SEND_QUERY_IMPLICIT || PASS == conn->m->local_tx_start(conn, this_func))
+ {
+ const MYSQLND_CSTRING query_string = {query, query_len};
+ struct st_mysqlnd_protocol_command * command = conn->command_factory(COM_QUERY, conn, query_string);
+ if (command) {
+ ret = command->run(command);
+ command->free_command(command);
+ }
+
+ if (type == MYSQLND_SEND_QUERY_EXPLICIT) {
+ conn->m->local_tx_end(conn, this_func, ret);
}
- conn->m->local_tx_end(conn, this_func, ret);
}
- DBG_INF_FMT("conn->server_status=%u", conn->upsert_status->server_status);
+ DBG_INF_FMT("conn->server_status=%u", UPSERT_STATUS_GET_SERVER_STATUS(conn->upsert_status));
DBG_RETURN(ret);
}
/* }}} */
@@ -1243,302 +904,35 @@ MYSQLND_METHOD(mysqlnd_conn_data, send_query)(MYSQLND_CONN_DATA * conn, const ch
static enum_func_status
MYSQLND_METHOD(mysqlnd_conn_data, reap_query)(MYSQLND_CONN_DATA * conn, enum_mysqlnd_reap_result_type type)
{
- size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, reap_query);
- enum_mysqlnd_connection_state state = CONN_GET_STATE(conn);
+ const size_t this_func = STRUCT_OFFSET(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn_data), reap_query);
enum_func_status ret = FAIL;
DBG_ENTER("mysqlnd_conn_data::reap_query");
DBG_INF_FMT("conn=%llu", conn->thread_id);
- DBG_INF_FMT("conn->server_status=%u", conn->upsert_status->server_status);
- if (PASS == conn->m->local_tx_start(conn, this_func)) {
- if (state <= CONN_READY || state == CONN_QUIT_SENT) {
- php_error_docref(NULL, E_WARNING, "Connection not opened, clear or has been closed");
- DBG_ERR_FMT("Connection not opened, clear or has been closed. State=%u", state);
- DBG_RETURN(ret);
- }
- ret = conn->m->query_read_result_set_header(conn, NULL);
-
- conn->m->local_tx_end(conn, this_func, ret);
- }
- DBG_INF_FMT("conn->server_status=%u", conn->upsert_status->server_status);
- DBG_RETURN(ret);
-}
-/* }}} */
-
-
-#include "php_network.h"
-
-/* {{{ mysqlnd_stream_array_to_fd_set */
-MYSQLND ** mysqlnd_stream_array_check_for_readiness(MYSQLND ** conn_array)
-{
- int cnt = 0;
- MYSQLND **p = conn_array, **p_p;
- MYSQLND **ret = NULL;
-
- while (*p) {
- if (CONN_GET_STATE((*p)->data) <= CONN_READY || CONN_GET_STATE((*p)->data) == CONN_QUIT_SENT) {
- cnt++;
- }
- p++;
- }
- if (cnt) {
- MYSQLND **ret_p = ret = ecalloc(cnt + 1, sizeof(MYSQLND *));
- p_p = p = conn_array;
- while (*p) {
- if (CONN_GET_STATE((*p)->data) <= CONN_READY || CONN_GET_STATE((*p)->data) == CONN_QUIT_SENT) {
- *ret_p = *p;
- *p = NULL;
- ret_p++;
- } else {
- *p_p = *p;
- p_p++;
- }
- p++;
- }
- *ret_p = NULL;
- }
- return ret;
-}
-/* }}} */
-
-
-/* {{{ mysqlnd_stream_array_to_fd_set */
-static int mysqlnd_stream_array_to_fd_set(MYSQLND ** conn_array, fd_set * fds, php_socket_t * max_fd)
-{
- php_socket_t this_fd;
- php_stream *stream = NULL;
- unsigned int cnt = 0;
- MYSQLND **p = conn_array;
- DBG_ENTER("mysqlnd_stream_array_to_fd_set");
-
- while (*p) {
- /* get the fd.
- * NB: Most other code will NOT use the PHP_STREAM_CAST_INTERNAL flag
- * when casting. It is only used here so that the buffered data warning
- * is not displayed.
- * */
- stream = (*p)->data->net->data->m.get_stream((*p)->data->net);
- DBG_INF_FMT("conn=%llu stream=%p", (*p)->data->thread_id, stream);
- if (stream != NULL && SUCCESS == php_stream_cast(stream, PHP_STREAM_AS_FD_FOR_SELECT | PHP_STREAM_CAST_INTERNAL,
- (void*)&this_fd, 1) && ZEND_VALID_SOCKET(this_fd)) {
-
- PHP_SAFE_FD_SET(this_fd, fds);
-
- if (this_fd > *max_fd) {
- *max_fd = this_fd;
- }
- cnt++;
+ DBG_INF_FMT("conn->server_status=%u", UPSERT_STATUS_GET_SERVER_STATUS(conn->upsert_status));
+ if (type == MYSQLND_REAP_RESULT_IMPLICIT || PASS == conn->m->local_tx_start(conn, this_func))
+ {
+ struct st_mysqlnd_protocol_command * command = conn->command_factory(COM_REAP_RESULT, conn);
+ if (command) {
+ ret = command->run(command);
+ command->free_command(command);
}
- p++;
- }
- DBG_RETURN(cnt ? 1 : 0);
-}
-/* }}} */
-
-/* {{{ mysqlnd_stream_array_from_fd_set */
-static int mysqlnd_stream_array_from_fd_set(MYSQLND ** conn_array, fd_set * fds)
-{
- php_socket_t this_fd;
- php_stream *stream = NULL;
- int ret = 0;
- zend_bool disproportion = FALSE;
- MYSQLND **fwd = conn_array, **bckwd = conn_array;
- DBG_ENTER("mysqlnd_stream_array_from_fd_set");
-
- while (*fwd) {
- stream = (*fwd)->data->net->data->m.get_stream((*fwd)->data->net);
- DBG_INF_FMT("conn=%llu stream=%p", (*fwd)->data->thread_id, stream);
- if (stream != NULL && SUCCESS == php_stream_cast(stream, PHP_STREAM_AS_FD_FOR_SELECT | PHP_STREAM_CAST_INTERNAL,
- (void*)&this_fd, 1) && ZEND_VALID_SOCKET(this_fd)) {
- if (PHP_SAFE_FD_ISSET(this_fd, fds)) {
- if (disproportion) {
- *bckwd = *fwd;
- }
- bckwd++;
- fwd++;
- ret++;
- continue;
- }
+ if (type == MYSQLND_REAP_RESULT_EXPLICIT) {
+ conn->m->local_tx_end(conn, this_func, ret);
}
- disproportion = TRUE;
- fwd++;
}
- *bckwd = NULL;/* NULL-terminate the list */
-
+ DBG_INF_FMT("conn->server_status=%u", UPSERT_STATUS_GET_SERVER_STATUS(conn->upsert_status));
DBG_RETURN(ret);
}
/* }}} */
-#ifndef PHP_WIN32
-#define php_select(m, r, w, e, t) select(m, r, w, e, t)
-#else
-#include "win32/select.h"
-#endif
-
-
-/* {{{ mysqlnd_poll */
-PHPAPI enum_func_status
-mysqlnd_poll(MYSQLND **r_array, MYSQLND **e_array, MYSQLND ***dont_poll, long sec, long usec, int * desc_num)
-{
- struct timeval tv;
- struct timeval *tv_p = NULL;
- fd_set rfds, wfds, efds;
- php_socket_t max_fd = 0;
- int retval, sets = 0;
- int set_count, max_set_count = 0;
-
- DBG_ENTER("_mysqlnd_poll");
- if (sec < 0 || usec < 0) {
- php_error_docref(NULL, E_WARNING, "Negative values passed for sec and/or usec");
- DBG_RETURN(FAIL);
- }
-
- FD_ZERO(&rfds);
- FD_ZERO(&wfds);
- FD_ZERO(&efds);
-
- if (r_array != NULL) {
- *dont_poll = mysqlnd_stream_array_check_for_readiness(r_array);
- set_count = mysqlnd_stream_array_to_fd_set(r_array, &rfds, &max_fd);
- if (set_count > max_set_count) {
- max_set_count = set_count;
- }
- sets += set_count;
- }
-
- if (e_array != NULL) {
- set_count = mysqlnd_stream_array_to_fd_set(e_array, &efds, &max_fd);
- if (set_count > max_set_count) {
- max_set_count = set_count;
- }
- sets += set_count;
- }
-
- if (!sets) {
- php_error_docref(NULL, E_WARNING, *dont_poll ? "All arrays passed are clear":"No stream arrays were passed");
- DBG_ERR_FMT(*dont_poll ? "All arrays passed are clear":"No stream arrays were passed");
- DBG_RETURN(FAIL);
- }
-
- PHP_SAFE_MAX_FD(max_fd, max_set_count);
-
- /* Solaris + BSD do not like microsecond values which are >= 1 sec */
- if (usec > 999999) {
- tv.tv_sec = sec + (usec / 1000000);
- tv.tv_usec = usec % 1000000;
- } else {
- tv.tv_sec = sec;
- tv.tv_usec = usec;
- }
-
- tv_p = &tv;
-
- retval = php_select(max_fd + 1, &rfds, &wfds, &efds, tv_p);
-
- if (retval == -1) {
- php_error_docref(NULL, E_WARNING, "unable to select [%d]: %s (max_fd=%d)",
- errno, strerror(errno), max_fd);
- DBG_RETURN(FAIL);
- }
-
- if (r_array != NULL) {
- mysqlnd_stream_array_from_fd_set(r_array, &rfds);
- }
- if (e_array != NULL) {
- mysqlnd_stream_array_from_fd_set(e_array, &efds);
- }
-
- *desc_num = retval;
- DBG_RETURN(PASS);
-}
-/* }}} */
-
-
-/*
- COM_FIELD_LIST is special, different from a SHOW FIELDS FROM :
- - There is no result set header - status from the command, which
- impacts us to allocate big chunk of memory for reading the metadata.
- - The EOF packet is consumed by the metadata packet reader.
-*/
-
-/* {{{ mysqlnd_conn_data::list_fields */
-MYSQLND_RES *
-MYSQLND_METHOD(mysqlnd_conn_data, list_fields)(MYSQLND_CONN_DATA * conn, const char *table, const char *achtung_wild)
-{
- size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, list_fields);
- /* db + \0 + wild + \0 (for wild) */
- zend_uchar buff[MYSQLND_MAX_ALLOWED_DB_LEN * 2 + 1 + 1], *p;
- size_t table_len, wild_len;
- MYSQLND_RES * result = NULL;
- DBG_ENTER("mysqlnd_conn_data::list_fields");
- DBG_INF_FMT("conn=%llu table=%s wild=%s", conn->thread_id, table? table:"",achtung_wild? achtung_wild:"");
-
- if (PASS == conn->m->local_tx_start(conn, this_func)) {
- do {
- p = buff;
- if (table && (table_len = strlen(table))) {
- size_t to_copy = MIN(table_len, MYSQLND_MAX_ALLOWED_DB_LEN);
- memcpy(p, table, to_copy);
- p += to_copy;
- *p++ = '\0';
- }
-
- if (achtung_wild && (wild_len = strlen(achtung_wild))) {
- size_t to_copy = MIN(wild_len, MYSQLND_MAX_ALLOWED_DB_LEN);
- memcpy(p, achtung_wild, to_copy);
- p += to_copy;
- *p++ = '\0';
- }
-
- if (PASS != conn->m->simple_command(conn, COM_FIELD_LIST, buff, p - buff,
- PROT_LAST /* we will handle the OK packet*/,
- FALSE, TRUE)) {
- conn->m->local_tx_end(conn, 0, FAIL);
- break;
- }
-
- /*
- Prepare for the worst case.
- MyISAM goes to 2500 BIT columns, double it for safety.
- */
- result = conn->m->result_init(5000, conn->persistent);
- if (!result) {
- break;
- }
-
- if (FAIL == result->m.read_result_metadata(result, conn)) {
- DBG_ERR("Error occurred while reading metadata");
- result->m.free_result(result, TRUE);
- result = NULL;
- break;
- }
-
- result->type = MYSQLND_RES_NORMAL;
- result->unbuf = mysqlnd_result_unbuffered_init(result->field_count, FALSE, result->persistent);
- if (!result->unbuf) {
- /* OOM */
- SET_OOM_ERROR(*conn->error_info);
- result->m.free_result(result, TRUE);
- result = NULL;
- break;
- }
- result->unbuf->eof_reached = TRUE;
- } while (0);
- conn->m->local_tx_end(conn, this_func, result == NULL? FAIL:PASS);
- }
-
- DBG_RETURN(result);
-}
-/* }}} */
-
-
/* {{{ mysqlnd_conn_data::list_method */
MYSQLND_RES *
-MYSQLND_METHOD(mysqlnd_conn_data, list_method)(MYSQLND_CONN_DATA * conn, const char * query, const char *achtung_wild, char *par1)
+MYSQLND_METHOD(mysqlnd_conn_data, list_method)(MYSQLND_CONN_DATA * conn, const char * const query, const char * const achtung_wild, const char * const par1)
{
- size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, list_method);
+ const size_t this_func = STRUCT_OFFSET(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn_data), list_method);
char * show_query = NULL;
size_t show_query_len;
MYSQLND_RES * result = NULL;
@@ -1574,9 +968,9 @@ MYSQLND_METHOD(mysqlnd_conn_data, list_method)(MYSQLND_CONN_DATA * conn, const c
/* }}} */
-/* {{{ mysqlnd_conn_data::errno */
+/* {{{ mysqlnd_conn_data::err_no */
static unsigned int
-MYSQLND_METHOD(mysqlnd_conn_data, errno)(const MYSQLND_CONN_DATA * const conn)
+MYSQLND_METHOD(mysqlnd_conn_data, err_no)(const MYSQLND_CONN_DATA * const conn)
{
return conn->error_info->error_no;
}
@@ -1616,17 +1010,17 @@ static enum_func_status
MYSQLND_METHOD(mysqlnd_conn_data, ssl_set)(MYSQLND_CONN_DATA * const conn, const char * key, const char * const cert,
const char * const ca, const char * const capath, const char * const cipher)
{
- size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, ssl_set);
+ const size_t this_func = STRUCT_OFFSET(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn_data), ssl_set);
enum_func_status ret = FAIL;
- MYSQLND_NET * net = conn->net;
+ MYSQLND_VIO * vio = conn->vio;
DBG_ENTER("mysqlnd_conn_data::ssl_set");
if (PASS == conn->m->local_tx_start(conn, this_func)) {
- ret = (PASS == net->data->m.set_client_option(net, MYSQLND_OPT_SSL_KEY, key) &&
- PASS == net->data->m.set_client_option(net, MYSQLND_OPT_SSL_CERT, cert) &&
- PASS == net->data->m.set_client_option(net, MYSQLND_OPT_SSL_CA, ca) &&
- PASS == net->data->m.set_client_option(net, MYSQLND_OPT_SSL_CAPATH, capath) &&
- PASS == net->data->m.set_client_option(net, MYSQLND_OPT_SSL_CIPHER, cipher)) ? PASS : FAIL;
+ ret = (PASS == vio->data->m.set_client_option(vio, MYSQLND_OPT_SSL_KEY, key) &&
+ PASS == vio->data->m.set_client_option(vio, MYSQLND_OPT_SSL_CERT, cert) &&
+ PASS == vio->data->m.set_client_option(vio, MYSQLND_OPT_SSL_CA, ca) &&
+ PASS == vio->data->m.set_client_option(vio, MYSQLND_OPT_SSL_CAPATH, capath) &&
+ PASS == vio->data->m.set_client_option(vio, MYSQLND_OPT_SSL_CIPHER, cipher)) ? PASS : FAIL;
conn->m->local_tx_end(conn, this_func, ret);
}
@@ -1639,14 +1033,14 @@ MYSQLND_METHOD(mysqlnd_conn_data, ssl_set)(MYSQLND_CONN_DATA * const conn, const
static zend_ulong
MYSQLND_METHOD(mysqlnd_conn_data, escape_string)(MYSQLND_CONN_DATA * const conn, char * newstr, const char * escapestr, size_t escapestr_len)
{
- size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, escape_string);
+ const size_t this_func = STRUCT_OFFSET(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn_data), escape_string);
zend_ulong ret = FAIL;
DBG_ENTER("mysqlnd_conn_data::escape_string");
DBG_INF_FMT("conn=%llu", conn->thread_id);
if (PASS == conn->m->local_tx_start(conn, this_func)) {
- DBG_INF_FMT("server_status=%u", conn->upsert_status->server_status);
- if (conn->upsert_status->server_status & SERVER_STATUS_NO_BACKSLASH_ESCAPES) {
+ DBG_INF_FMT("server_status=%u", UPSERT_STATUS_GET_SERVER_STATUS(conn->upsert_status));
+ if (UPSERT_STATUS_GET_SERVER_STATUS(conn->upsert_status) & SERVER_STATUS_NO_BACKSLASH_ESCAPES) {
ret = mysqlnd_cset_escape_quotes(conn->charset, newstr, escapestr, escapestr_len);
} else {
ret = mysqlnd_cset_escape_slashes(conn->charset, newstr, escapestr, escapestr_len);
@@ -1662,12 +1056,16 @@ MYSQLND_METHOD(mysqlnd_conn_data, escape_string)(MYSQLND_CONN_DATA * const conn,
static enum_func_status
MYSQLND_METHOD(mysqlnd_conn_data, dump_debug_info)(MYSQLND_CONN_DATA * const conn)
{
- size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, server_dump_debug_information);
+ const size_t this_func = STRUCT_OFFSET(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn_data), server_dump_debug_information);
enum_func_status ret = FAIL;
DBG_ENTER("mysqlnd_conn_data::dump_debug_info");
DBG_INF_FMT("conn=%llu", conn->thread_id);
if (PASS == conn->m->local_tx_start(conn, this_func)) {
- ret = conn->m->simple_command(conn, COM_DEBUG, NULL, 0, PROT_EOF_PACKET, FALSE, TRUE);
+ struct st_mysqlnd_protocol_command * command = conn->command_factory(COM_DEBUG, conn);
+ if (command) {
+ ret = command->run(command);
+ command->free_command(command);
+ }
conn->m->local_tx_end(conn, this_func, ret);
}
@@ -1679,33 +1077,22 @@ MYSQLND_METHOD(mysqlnd_conn_data, dump_debug_info)(MYSQLND_CONN_DATA * const con
/* {{{ mysqlnd_conn_data::select_db */
static enum_func_status
-MYSQLND_METHOD(mysqlnd_conn_data, select_db)(MYSQLND_CONN_DATA * const conn, const char * const db, unsigned int db_len)
+MYSQLND_METHOD(mysqlnd_conn_data, select_db)(MYSQLND_CONN_DATA * const conn, const char * const db, const size_t db_len)
{
- size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, select_db);
+ const size_t this_func = STRUCT_OFFSET(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn_data), select_db);
enum_func_status ret = FAIL;
DBG_ENTER("mysqlnd_conn_data::select_db");
DBG_INF_FMT("conn=%llu db=%s", conn->thread_id, db);
if (PASS == conn->m->local_tx_start(conn, this_func)) {
- ret = conn->m->simple_command(conn, COM_INIT_DB, (zend_uchar*) db, db_len, PROT_OK_PACKET, FALSE, TRUE);
- /*
- The server sends 0 but libmysql doesn't read it and has established
- a protocol of giving back -1. Thus we have to follow it :(
- */
- SET_ERROR_AFF_ROWS(conn);
- if (ret == PASS) {
- if (conn->connect_or_select_db) {
- mnd_pefree(conn->connect_or_select_db, conn->persistent);
- }
- conn->connect_or_select_db = mnd_pestrndup(db, db_len, conn->persistent);
- conn->connect_or_select_db_len = db_len;
- if (!conn->connect_or_select_db) {
- /* OOM */
- SET_OOM_ERROR(*conn->error_info);
- ret = FAIL;
- }
+ const MYSQLND_CSTRING database = {db, db_len};
+ struct st_mysqlnd_protocol_command * command = conn->command_factory(COM_INIT_DB, conn, database);
+ if (command) {
+ ret = command->run(command);
+ command->free_command(command);
}
+
conn->m->local_tx_end(conn, this_func, ret);
}
DBG_RETURN(ret);
@@ -1717,20 +1104,18 @@ MYSQLND_METHOD(mysqlnd_conn_data, select_db)(MYSQLND_CONN_DATA * const conn, con
static enum_func_status
MYSQLND_METHOD(mysqlnd_conn_data, ping)(MYSQLND_CONN_DATA * const conn)
{
- size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, ping);
+ const size_t this_func = STRUCT_OFFSET(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn_data), ping);
enum_func_status ret = FAIL;
DBG_ENTER("mysqlnd_conn_data::ping");
DBG_INF_FMT("conn=%llu", conn->thread_id);
if (PASS == conn->m->local_tx_start(conn, this_func)) {
- ret = conn->m->simple_command(conn, COM_PING, NULL, 0, PROT_OK_PACKET, TRUE, TRUE);
- /*
- The server sends 0 but libmysql doesn't read it and has established
- a protocol of giving back -1. Thus we have to follow it :(
- */
- SET_ERROR_AFF_ROWS(conn);
-
+ struct st_mysqlnd_protocol_command * command = conn->command_factory(COM_PING, conn);
+ if (command) {
+ ret = command->run(command);
+ command->free_command(command);
+ }
conn->m->local_tx_end(conn, this_func, ret);
}
DBG_INF_FMT("ret=%u", ret);
@@ -1743,33 +1128,18 @@ MYSQLND_METHOD(mysqlnd_conn_data, ping)(MYSQLND_CONN_DATA * const conn)
static enum_func_status
MYSQLND_METHOD(mysqlnd_conn_data, statistic)(MYSQLND_CONN_DATA * conn, zend_string **message)
{
- size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, get_server_statistics);
+ const size_t this_func = STRUCT_OFFSET(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn_data), get_server_statistics);
enum_func_status ret = FAIL;
- MYSQLND_PACKET_STATS * stats_header;
DBG_ENTER("mysqlnd_conn_data::statistic");
DBG_INF_FMT("conn=%llu", conn->thread_id);
if (PASS == conn->m->local_tx_start(conn, this_func)) {
- do {
- ret = conn->m->simple_command(conn, COM_STATISTICS, NULL, 0, PROT_LAST, FALSE, TRUE);
- if (FAIL == ret) {
- break;
- }
- stats_header = conn->protocol->m.get_stats_packet(conn->protocol, FALSE);
- if (!stats_header) {
- SET_OOM_ERROR(*conn->error_info);
- break;
- }
-
- if (PASS == (ret = PACKET_READ(stats_header, conn))) {
- /* will be freed by Zend, thus don't use the mnd_ allocator */
- *message = zend_string_init(stats_header->message, stats_header->message_len, 0);
- DBG_INF(ZSTR_VAL(*message));
- }
- PACKET_FREE(stats_header);
- } while (0);
-
+ struct st_mysqlnd_protocol_command * command = conn->command_factory(COM_STATISTICS, conn, message);
+ if (command) {
+ ret = command->run(command);
+ command->free_command(command);
+ }
conn->m->local_tx_end(conn, this_func, ret);
}
DBG_RETURN(ret);
@@ -1781,29 +1151,21 @@ MYSQLND_METHOD(mysqlnd_conn_data, statistic)(MYSQLND_CONN_DATA * conn, zend_stri
static enum_func_status
MYSQLND_METHOD(mysqlnd_conn_data, kill)(MYSQLND_CONN_DATA * conn, unsigned int pid)
{
- size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, kill_connection);
+ const size_t this_func = STRUCT_OFFSET(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn_data), kill_connection);
enum_func_status ret = FAIL;
- zend_uchar buff[4];
DBG_ENTER("mysqlnd_conn_data::kill");
DBG_INF_FMT("conn=%llu pid=%u", conn->thread_id, pid);
if (PASS == conn->m->local_tx_start(conn, this_func)) {
- int4store(buff, pid);
-
- /* If we kill ourselves don't expect OK packet, PROT_LAST will skip it */
- if (pid != conn->thread_id) {
- ret = conn->m->simple_command(conn, COM_PROCESS_KILL, buff, 4, PROT_OK_PACKET, FALSE, TRUE);
- /*
- The server sends 0 but libmysql doesn't read it and has established
- a protocol of giving back -1. Thus we have to follow it :(
- */
- SET_ERROR_AFF_ROWS(conn);
- } else if (PASS == (ret = conn->m->simple_command(conn, COM_PROCESS_KILL, buff, 4, PROT_LAST, FALSE, TRUE))) {
- CONN_SET_STATE(conn, CONN_QUIT_SENT);
- conn->m->send_close(conn);
+ unsigned int process_id = pid;
+ /* 'unsigned char' is promoted to 'int' when passed through '...' */
+ unsigned int read_response = (pid != conn->thread_id);
+ struct st_mysqlnd_protocol_command * command = conn->command_factory(COM_PROCESS_KILL, conn, process_id, read_response);
+ if (command) {
+ ret = command->run(command);
+ command->free_command(command);
}
-
conn->m->local_tx_end(conn, this_func, ret);
}
DBG_RETURN(ret);
@@ -1815,7 +1177,7 @@ MYSQLND_METHOD(mysqlnd_conn_data, kill)(MYSQLND_CONN_DATA * conn, unsigned int p
static enum_func_status
MYSQLND_METHOD(mysqlnd_conn_data, set_charset)(MYSQLND_CONN_DATA * const conn, const char * const csname)
{
- size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, set_charset);
+ const size_t this_func = STRUCT_OFFSET(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn_data), set_charset);
enum_func_status ret = FAIL;
const MYSQLND_CHARSET * const charset = mysqlnd_find_charset_name(csname);
@@ -1823,7 +1185,7 @@ MYSQLND_METHOD(mysqlnd_conn_data, set_charset)(MYSQLND_CONN_DATA * const conn, c
DBG_INF_FMT("conn=%llu cs=%s", conn->thread_id, csname);
if (!charset) {
- SET_CLIENT_ERROR(*conn->error_info, CR_CANT_FIND_CHARSET, UNKNOWN_SQLSTATE,
+ SET_CLIENT_ERROR(conn->error_info, CR_CANT_FIND_CHARSET, UNKNOWN_SQLSTATE,
"Invalid characterset or character set not supported");
DBG_RETURN(ret);
}
@@ -1854,17 +1216,18 @@ MYSQLND_METHOD(mysqlnd_conn_data, set_charset)(MYSQLND_CONN_DATA * const conn, c
static enum_func_status
MYSQLND_METHOD(mysqlnd_conn_data, refresh)(MYSQLND_CONN_DATA * const conn, uint8_t options)
{
- size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, refresh_server);
+ const size_t this_func = STRUCT_OFFSET(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn_data), refresh_server);
enum_func_status ret = FAIL;
- zend_uchar bits[1];
DBG_ENTER("mysqlnd_conn_data::refresh");
DBG_INF_FMT("conn=%llu options=%lu", conn->thread_id, options);
if (PASS == conn->m->local_tx_start(conn, this_func)) {
- int1store(bits, options);
-
- ret = conn->m->simple_command(conn, COM_REFRESH, bits, 1, PROT_OK_PACKET, FALSE, TRUE);
-
+ unsigned int options_param = (unsigned int) options;
+ struct st_mysqlnd_protocol_command * command = conn->command_factory(COM_REFRESH, conn, options_param);
+ if (command) {
+ ret = command->run(command);
+ command->free_command(command);
+ }
conn->m->local_tx_end(conn, this_func, ret);
}
DBG_RETURN(ret);
@@ -1876,17 +1239,18 @@ MYSQLND_METHOD(mysqlnd_conn_data, refresh)(MYSQLND_CONN_DATA * const conn, uint8
static enum_func_status
MYSQLND_METHOD(mysqlnd_conn_data, shutdown)(MYSQLND_CONN_DATA * const conn, uint8_t level)
{
- size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, shutdown_server);
+ const size_t this_func = STRUCT_OFFSET(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn_data), shutdown_server);
enum_func_status ret = FAIL;
- zend_uchar bits[1];
DBG_ENTER("mysqlnd_conn_data::shutdown");
DBG_INF_FMT("conn=%llu level=%lu", conn->thread_id, level);
if (PASS == conn->m->local_tx_start(conn, this_func)) {
- int1store(bits, level);
-
- ret = conn->m->simple_command(conn, COM_SHUTDOWN, bits, 1, PROT_OK_PACKET, FALSE, TRUE);
-
+ unsigned int level_param = (unsigned int) level;
+ struct st_mysqlnd_protocol_command * command = conn->command_factory(COM_SHUTDOWN, conn, level_param);
+ if (command) {
+ ret = command->run(command);
+ command->free_command(command);
+ }
conn->m->local_tx_end(conn, this_func, ret);
}
DBG_RETURN(ret);
@@ -1899,29 +1263,32 @@ static enum_func_status
MYSQLND_METHOD(mysqlnd_conn_data, send_close)(MYSQLND_CONN_DATA * const conn)
{
enum_func_status ret = PASS;
- MYSQLND_NET * net = conn->net;
- php_stream * net_stream = net->data->m.get_stream(net);
- enum mysqlnd_connection_state state;
+ MYSQLND_VIO * vio = conn->vio;
+ php_stream * net_stream = vio->data->m.get_stream(vio);
+ enum mysqlnd_connection_state state = GET_CONNECTION_STATE(&conn->state);
DBG_ENTER("mysqlnd_send_close");
- DBG_INF_FMT("conn=%llu net->data->stream->abstract=%p", conn->thread_id, net_stream? net_stream->abstract:NULL);
+ DBG_INF_FMT("conn=%llu vio->data->stream->abstract=%p", conn->thread_id, net_stream? net_stream->abstract:NULL);
+ DBG_INF_FMT("state=%u", state);
- if (CONN_GET_STATE(conn) >= CONN_READY) {
- MYSQLND_DEC_CONN_STATISTIC(conn->stats, STAT_OPENED_CONNECTIONS);
+ if (state >= CONN_READY) {
+ MYSQLND_DEC_GLOBAL_STATISTIC(STAT_OPENED_CONNECTIONS);
if (conn->persistent) {
- MYSQLND_DEC_CONN_STATISTIC(conn->stats, STAT_OPENED_PERSISTENT_CONNECTIONS);
+ MYSQLND_DEC_GLOBAL_STATISTIC(STAT_OPENED_PERSISTENT_CONNECTIONS);
}
}
- state = CONN_GET_STATE(conn);
- DBG_INF_FMT("state=%u", state);
switch (state) {
case CONN_READY:
DBG_INF("Connection clean, sending COM_QUIT");
if (net_stream) {
- ret = conn->m->simple_command(conn, COM_QUIT, NULL, 0, PROT_LAST, TRUE, TRUE);
- net->data->m.close_stream(net, conn->stats, conn->error_info);
+ struct st_mysqlnd_protocol_command * command = conn->command_factory(COM_QUIT, conn);
+ if (command) {
+ ret = command->run(command);
+ command->free_command(command);
+ }
+ vio->data->m.close_stream(vio, conn->stats, conn->error_info);
}
- CONN_SET_STATE(conn, CONN_QUIT_SENT);
+ SET_CONNECTION_STATE(&conn->state, CONN_QUIT_SENT);
break;
case CONN_SENDING_LOAD_DATA:
/*
@@ -1932,7 +1299,7 @@ MYSQLND_METHOD(mysqlnd_conn_data, send_close)(MYSQLND_CONN_DATA * const conn)
case CONN_QUERY_SENT:
case CONN_FETCHING_DATA:
MYSQLND_INC_GLOBAL_STATISTIC(STAT_CLOSE_IN_MIDDLE);
- DBG_ERR_FMT("Brutally closing connection [%p][%s]", conn, conn->scheme);
+ DBG_ERR_FMT("Brutally closing connection [%p][%s]", conn, conn->scheme.s);
/*
Do nothing, the connection will be brutally closed
and the server will catch it and free close from its side.
@@ -1945,11 +1312,11 @@ MYSQLND_METHOD(mysqlnd_conn_data, send_close)(MYSQLND_CONN_DATA * const conn)
Fall-through
*/
- CONN_SET_STATE(conn, CONN_QUIT_SENT);
+ SET_CONNECTION_STATE(&conn->state, CONN_QUIT_SENT);
/* Fall-through */
case CONN_QUIT_SENT:
/* The user has killed its own connection */
- net->data->m.close_stream(net, conn->stats, conn->error_info);
+ vio->data->m.close_stream(vio, conn->stats, conn->error_info);
break;
}
@@ -1991,28 +1358,6 @@ MYSQLND_METHOD_PRIVATE(mysqlnd_conn_data, free_reference)(MYSQLND_CONN_DATA * co
/* }}} */
-/* {{{ mysqlnd_conn_data::get_state */
-static enum mysqlnd_connection_state
-MYSQLND_METHOD_PRIVATE(mysqlnd_conn_data, get_state)(const MYSQLND_CONN_DATA * const conn)
-{
- DBG_ENTER("mysqlnd_conn_data::get_state");
- DBG_RETURN(conn->state);
-}
-/* }}} */
-
-
-/* {{{ mysqlnd_conn_data::set_state */
-static void
-MYSQLND_METHOD_PRIVATE(mysqlnd_conn_data, set_state)(MYSQLND_CONN_DATA * const conn, enum mysqlnd_connection_state new_state)
-{
- DBG_ENTER("mysqlnd_conn_data::set_state");
- DBG_INF_FMT("New state=%u", new_state);
- conn->state = new_state;
- DBG_VOID_RETURN;
-}
-/* }}} */
-
-
/* {{{ mysqlnd_conn_data::field_count */
static unsigned int
MYSQLND_METHOD(mysqlnd_conn_data, field_count)(const MYSQLND_CONN_DATA * const conn)
@@ -2026,7 +1371,7 @@ MYSQLND_METHOD(mysqlnd_conn_data, field_count)(const MYSQLND_CONN_DATA * const c
static unsigned int
MYSQLND_METHOD(mysqlnd_conn_data, server_status)(const MYSQLND_CONN_DATA * const conn)
{
- return conn->upsert_status->server_status;
+ return UPSERT_STATUS_GET_SERVER_STATUS(conn->upsert_status);
}
/* }}} */
@@ -2035,7 +1380,7 @@ MYSQLND_METHOD(mysqlnd_conn_data, server_status)(const MYSQLND_CONN_DATA * const
static uint64_t
MYSQLND_METHOD(mysqlnd_conn_data, insert_id)(const MYSQLND_CONN_DATA * const conn)
{
- return conn->upsert_status->last_insert_id;
+ return UPSERT_STATUS_GET_LAST_INSERT_ID(conn->upsert_status);
}
/* }}} */
@@ -2044,7 +1389,7 @@ MYSQLND_METHOD(mysqlnd_conn_data, insert_id)(const MYSQLND_CONN_DATA * const con
static uint64_t
MYSQLND_METHOD(mysqlnd_conn_data, affected_rows)(const MYSQLND_CONN_DATA * const conn)
{
- return conn->upsert_status->affected_rows;
+ return UPSERT_STATUS_GET_AFFECTED_ROWS(conn->upsert_status);
}
/* }}} */
@@ -2053,7 +1398,7 @@ MYSQLND_METHOD(mysqlnd_conn_data, affected_rows)(const MYSQLND_CONN_DATA * const
static unsigned int
MYSQLND_METHOD(mysqlnd_conn_data, warning_count)(const MYSQLND_CONN_DATA * const conn)
{
- return conn->upsert_status->warning_count;
+ return UPSERT_STATUS_GET_WARNINGS(conn->upsert_status);
}
/* }}} */
@@ -2062,10 +1407,11 @@ MYSQLND_METHOD(mysqlnd_conn_data, warning_count)(const MYSQLND_CONN_DATA * const
static const char *
MYSQLND_METHOD(mysqlnd_conn_data, info)(const MYSQLND_CONN_DATA * const conn)
{
- return conn->last_message;
+ return conn->last_message.s;
}
/* }}} */
+
/* {{{ mysqlnd_get_client_info */
PHPAPI const char * mysqlnd_get_client_info()
{
@@ -2155,7 +1501,7 @@ MYSQLND_METHOD(mysqlnd_conn_data, more_results)(const MYSQLND_CONN_DATA * const
{
DBG_ENTER("mysqlnd_conn_data::more_results");
/* (conn->state == CONN_NEXT_RESULT_PENDING) too */
- DBG_RETURN(conn->upsert_status->server_status & SERVER_MORE_RESULTS_EXISTS? TRUE:FALSE);
+ DBG_RETURN(UPSERT_STATUS_GET_SERVER_STATUS(conn->upsert_status) & SERVER_MORE_RESULTS_EXISTS? TRUE:FALSE);
}
/* }}} */
@@ -2164,7 +1510,7 @@ MYSQLND_METHOD(mysqlnd_conn_data, more_results)(const MYSQLND_CONN_DATA * const
static enum_func_status
MYSQLND_METHOD(mysqlnd_conn_data, next_result)(MYSQLND_CONN_DATA * const conn)
{
- size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, next_result);
+ const size_t this_func = STRUCT_OFFSET(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn_data), next_result);
enum_func_status ret = FAIL;
DBG_ENTER("mysqlnd_conn_data::next_result");
@@ -2172,12 +1518,12 @@ MYSQLND_METHOD(mysqlnd_conn_data, next_result)(MYSQLND_CONN_DATA * const conn)
if (PASS == conn->m->local_tx_start(conn, this_func)) {
do {
- if (CONN_GET_STATE(conn) != CONN_NEXT_RESULT_PENDING) {
+ if (GET_CONNECTION_STATE(&conn->state) != CONN_NEXT_RESULT_PENDING) {
break;
}
- SET_EMPTY_ERROR(*conn->error_info);
- SET_ERROR_AFF_ROWS(conn);
+ SET_EMPTY_ERROR(conn->error_info);
+ UPSERT_STATUS_SET_AFFECTED_ROWS_TO_ERROR(conn->upsert_status);
/*
We are sure that there is a result set, since conn->state is set accordingly
in mysqlnd_store_result() or mysqlnd_fetch_row_unbuffered()
@@ -2190,15 +1536,15 @@ MYSQLND_METHOD(mysqlnd_conn_data, next_result)(MYSQLND_CONN_DATA * const conn)
if (!conn->error_info->error_no) {
DBG_ERR_FMT("Serious error. %s::%u", __FILE__, __LINE__);
php_error_docref(NULL, E_WARNING, "Serious error. PID=%d", getpid());
- CONN_SET_STATE(conn, CONN_QUIT_SENT);
+ SET_CONNECTION_STATE(&conn->state, CONN_QUIT_SENT);
conn->m->send_close(conn);
} else {
DBG_INF_FMT("Error from the server : (%u) %s", conn->error_info->error_no, conn->error_info->error);
}
break;
}
- if (conn->last_query_type == QUERY_UPSERT && conn->upsert_status->affected_rows) {
- MYSQLND_INC_CONN_STATISTIC_W_VALUE(conn->stats, STAT_ROWS_AFFECTED_NORMAL, conn->upsert_status->affected_rows);
+ if (conn->last_query_type == QUERY_UPSERT && UPSERT_STATUS_GET_AFFECTED_ROWS(conn->upsert_status)) {
+ MYSQLND_INC_CONN_STATISTIC_W_VALUE(conn->stats, STAT_ROWS_AFFECTED_NORMAL, UPSERT_STATUS_GET_AFFECTED_ROWS(conn->upsert_status));
}
} while (0);
conn->m->local_tx_end(conn, this_func, ret);
@@ -2210,7 +1556,7 @@ MYSQLND_METHOD(mysqlnd_conn_data, next_result)(MYSQLND_CONN_DATA * const conn)
/* {{{ mysqlnd_field_type_name */
-PHPAPI const char *mysqlnd_field_type_name(enum mysqlnd_field_types field_type)
+PHPAPI const char * mysqlnd_field_type_name(const enum mysqlnd_field_types field_type)
{
switch(field_type) {
case FIELD_TYPE_JSON:
@@ -2265,14 +1611,14 @@ PHPAPI const char *mysqlnd_field_type_name(enum mysqlnd_field_types field_type)
/* {{{ mysqlnd_conn_data::change_user */
static enum_func_status
MYSQLND_METHOD(mysqlnd_conn_data, change_user)(MYSQLND_CONN_DATA * const conn,
- const char * user,
- const char * passwd,
- const char * db,
- zend_bool silent,
- size_t passwd_len
- )
-{
- size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, change_user);
+ const char * user,
+ const char * passwd,
+ const char * db,
+ zend_bool silent,
+ size_t passwd_len
+ )
+{
+ const size_t this_func = STRUCT_OFFSET(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn_data), change_user);
enum_func_status ret = FAIL;
DBG_ENTER("mysqlnd_conn_data::change_user");
@@ -2283,14 +1629,15 @@ MYSQLND_METHOD(mysqlnd_conn_data, change_user)(MYSQLND_CONN_DATA * const conn,
goto end;
}
- SET_EMPTY_ERROR(*conn->error_info);
- SET_ERROR_AFF_ROWS(conn);
+ SET_EMPTY_ERROR(conn->error_info);
+ UPSERT_STATUS_SET_AFFECTED_ROWS_TO_ERROR(conn->upsert_status);
if (!user) {
user = "";
}
if (!passwd) {
passwd = "";
+ passwd_len = 0;
}
if (!db) {
db = "";
@@ -2298,7 +1645,7 @@ MYSQLND_METHOD(mysqlnd_conn_data, change_user)(MYSQLND_CONN_DATA * const conn,
/* XXX: passwords that have \0 inside work during auth, but in this case won't work with change user */
ret = mysqlnd_run_authentication(conn, user, passwd, passwd_len, db, strlen(db),
- conn->auth_plugin_data, conn->auth_plugin_data_len, conn->options->auth_protocol,
+ conn->authentication_plugin_data, conn->options->auth_protocol,
0 /*charset not used*/, conn->options, conn->server_capabilities, silent, TRUE/*is_change*/);
/*
@@ -2316,11 +1663,11 @@ end:
/* {{{ mysqlnd_conn_data::set_client_option */
static enum_func_status
MYSQLND_METHOD(mysqlnd_conn_data, set_client_option)(MYSQLND_CONN_DATA * const conn,
- enum mysqlnd_option option,
+ enum_mysqlnd_client_option option,
const char * const value
)
{
- size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, set_client_option);
+ const size_t this_func = STRUCT_OFFSET(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn_data), set_client_option);
enum_func_status ret = PASS;
DBG_ENTER("mysqlnd_conn_data::set_client_option");
DBG_INF_FMT("conn=%llu option=%u", conn->thread_id, option);
@@ -2329,7 +1676,6 @@ MYSQLND_METHOD(mysqlnd_conn_data, set_client_option)(MYSQLND_CONN_DATA * const c
goto end;
}
switch (option) {
- case MYSQL_OPT_COMPRESS:
#ifdef WHEN_SUPPORTED_BY_MYSQLI
case MYSQL_OPT_READ_TIMEOUT:
case MYSQL_OPT_WRITE_TIMEOUT:
@@ -2341,10 +1687,13 @@ MYSQLND_METHOD(mysqlnd_conn_data, set_client_option)(MYSQLND_CONN_DATA * const c
case MYSQLND_OPT_SSL_CIPHER:
case MYSQL_OPT_SSL_VERIFY_SERVER_CERT:
case MYSQL_OPT_CONNECT_TIMEOUT:
- case MYSQLND_OPT_NET_CMD_BUFFER_SIZE:
case MYSQLND_OPT_NET_READ_BUFFER_SIZE:
+ ret = conn->vio->data->m.set_client_option(conn->vio, option, value);
+ break;
+ case MYSQLND_OPT_NET_CMD_BUFFER_SIZE:
+ case MYSQL_OPT_COMPRESS:
case MYSQL_SERVER_PUBLIC_KEY:
- ret = conn->net->data->m.set_client_option(conn->net, option, value);
+ ret = conn->protocol_frame_codec->data->m.set_client_option(conn->protocol_frame_codec, option, value);
break;
#ifdef MYSQLND_STRING_TO_INT_CONVERSION
case MYSQLND_OPT_INT_AND_FLOAT_NATIVE:
@@ -2389,7 +1738,7 @@ MYSQLND_METHOD(mysqlnd_conn_data, set_client_option)(MYSQLND_CONN_DATA * const c
{
char * new_charset_name;
if (!mysqlnd_find_charset_name(value)) {
- SET_CLIENT_ERROR(*conn->error_info, CR_CANT_FIND_CHARSET, UNKNOWN_SQLSTATE, "Unknown character set");
+ SET_CLIENT_ERROR(conn->error_info, CR_CANT_FIND_CHARSET, UNKNOWN_SQLSTATE, "Unknown character set");
ret = FAIL;
break;
}
@@ -2475,7 +1824,7 @@ MYSQLND_METHOD(mysqlnd_conn_data, set_client_option)(MYSQLND_CONN_DATA * const c
conn->m->local_tx_end(conn, this_func, ret);
DBG_RETURN(ret);
oom:
- SET_OOM_ERROR(*conn->error_info);
+ SET_OOM_ERROR(conn->error_info);
conn->m->local_tx_end(conn, this_func, FAIL);
end:
DBG_RETURN(FAIL);
@@ -2486,12 +1835,12 @@ end:
/* {{{ mysqlnd_conn_data::set_client_option_2d */
static enum_func_status
MYSQLND_METHOD(mysqlnd_conn_data, set_client_option_2d)(MYSQLND_CONN_DATA * const conn,
- enum mysqlnd_option option,
+ const enum_mysqlnd_client_option option,
const char * const key,
const char * const value
)
{
- size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, set_client_option_2d);
+ const size_t this_func = STRUCT_OFFSET(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn_data), set_client_option_2d);
enum_func_status ret = PASS;
DBG_ENTER("mysqlnd_conn_data::set_client_option_2d");
DBG_INF_FMT("conn=%llu option=%u", conn->thread_id, option);
@@ -2522,7 +1871,7 @@ MYSQLND_METHOD(mysqlnd_conn_data, set_client_option_2d)(MYSQLND_CONN_DATA * cons
conn->m->local_tx_end(conn, this_func, ret);
DBG_RETURN(ret);
oom:
- SET_OOM_ERROR(*conn->error_info);
+ SET_OOM_ERROR(conn->error_info);
conn->m->local_tx_end(conn, this_func, FAIL);
end:
DBG_RETURN(FAIL);
@@ -2534,7 +1883,7 @@ end:
static MYSQLND_RES *
MYSQLND_METHOD(mysqlnd_conn_data, use_result)(MYSQLND_CONN_DATA * const conn, const unsigned int flags)
{
- size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, use_result);
+ const size_t this_func = STRUCT_OFFSET(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn_data), use_result);
MYSQLND_RES * result = NULL;
DBG_ENTER("mysqlnd_conn_data::use_result");
@@ -2547,8 +1896,8 @@ MYSQLND_METHOD(mysqlnd_conn_data, use_result)(MYSQLND_CONN_DATA * const conn, co
}
/* Nothing to store for UPSERT/LOAD DATA */
- if (conn->last_query_type != QUERY_SELECT || CONN_GET_STATE(conn) != CONN_FETCHING_DATA) {
- SET_CLIENT_ERROR(*conn->error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, mysqlnd_out_of_sync);
+ if (conn->last_query_type != QUERY_SELECT || GET_CONNECTION_STATE(&conn->state) != CONN_FETCHING_DATA) {
+ SET_CLIENT_ERROR(conn->error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, mysqlnd_out_of_sync);
DBG_ERR("Command out of sync");
break;
}
@@ -2576,7 +1925,7 @@ MYSQLND_METHOD(mysqlnd_conn_data, use_result)(MYSQLND_CONN_DATA * const conn, co
static MYSQLND_RES *
MYSQLND_METHOD(mysqlnd_conn_data, store_result)(MYSQLND_CONN_DATA * const conn, const unsigned int flags)
{
- size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, store_result);
+ const size_t this_func = STRUCT_OFFSET(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn_data), store_result);
MYSQLND_RES * result = NULL;
DBG_ENTER("mysqlnd_conn_data::store_result");
@@ -2590,8 +1939,8 @@ MYSQLND_METHOD(mysqlnd_conn_data, store_result)(MYSQLND_CONN_DATA * const conn,
}
/* Nothing to store for UPSERT/LOAD DATA*/
- if (conn->last_query_type != QUERY_SELECT || CONN_GET_STATE(conn) != CONN_FETCHING_DATA) {
- SET_CLIENT_ERROR(*conn->error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, mysqlnd_out_of_sync);
+ if (conn->last_query_type != QUERY_SELECT || GET_CONNECTION_STATE(&conn->state) != CONN_FETCHING_DATA) {
+ SET_CLIENT_ERROR(conn->error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, mysqlnd_out_of_sync);
DBG_ERR("Command out of sync");
break;
}
@@ -2611,7 +1960,7 @@ MYSQLND_METHOD(mysqlnd_conn_data, store_result)(MYSQLND_CONN_DATA * const conn,
}
}
if (!(f & (MYSQLND_STORE_NO_COPY | MYSQLND_STORE_COPY))) {
- SET_CLIENT_ERROR(*conn->error_info, CR_UNKNOWN_ERROR, UNKNOWN_SQLSTATE, "Unknown fetch mode");
+ SET_CLIENT_ERROR(conn->error_info, CR_UNKNOWN_ERROR, UNKNOWN_SQLSTATE, "Unknown fetch mode");
DBG_ERR("Unknown fetch mode");
break;
}
@@ -2632,7 +1981,7 @@ MYSQLND_METHOD(mysqlnd_conn_data, store_result)(MYSQLND_CONN_DATA * const conn,
/* {{{ mysqlnd_conn_data::get_connection_stats */
static void
MYSQLND_METHOD(mysqlnd_conn_data, get_connection_stats)(const MYSQLND_CONN_DATA * const conn,
- zval * return_value ZEND_FILE_LINE_DC)
+ zval * return_value ZEND_FILE_LINE_DC)
{
DBG_ENTER("mysqlnd_conn_data::get_connection_stats");
mysqlnd_fill_stats_hash(conn->stats, mysqlnd_stats_values_names, return_value ZEND_FILE_LINE_CC);
@@ -2645,7 +1994,7 @@ MYSQLND_METHOD(mysqlnd_conn_data, get_connection_stats)(const MYSQLND_CONN_DATA
static enum_func_status
MYSQLND_METHOD(mysqlnd_conn_data, set_autocommit)(MYSQLND_CONN_DATA * conn, unsigned int mode)
{
- size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, set_autocommit);
+ const size_t this_func = STRUCT_OFFSET(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn_data), set_autocommit);
enum_func_status ret = FAIL;
DBG_ENTER("mysqlnd_conn_data::set_autocommit");
@@ -2756,7 +2105,7 @@ mysqlnd_escape_string_for_tx_name_in_comment(const char * const name)
static enum_func_status
MYSQLND_METHOD(mysqlnd_conn_data, tx_commit_or_rollback)(MYSQLND_CONN_DATA * conn, const zend_bool commit, const unsigned int flags, const char * const name)
{
- size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, tx_commit_or_rollback);
+ const size_t this_func = STRUCT_OFFSET(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn_data), tx_commit_or_rollback);
enum_func_status ret = FAIL;
DBG_ENTER("mysqlnd_conn_data::tx_commit_or_rollback");
@@ -2780,7 +2129,7 @@ MYSQLND_METHOD(mysqlnd_conn_data, tx_commit_or_rollback)(MYSQLND_CONN_DATA * con
name_esc = NULL;
}
if (!query) {
- SET_OOM_ERROR(*conn->error_info);
+ SET_OOM_ERROR(conn->error_info);
break;
}
@@ -2800,7 +2149,7 @@ MYSQLND_METHOD(mysqlnd_conn_data, tx_commit_or_rollback)(MYSQLND_CONN_DATA * con
static enum_func_status
MYSQLND_METHOD(mysqlnd_conn_data, tx_begin)(MYSQLND_CONN_DATA * conn, const unsigned int mode, const char * const name)
{
- size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, tx_begin);
+ const size_t this_func = STRUCT_OFFSET(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn_data), tx_begin);
enum_func_status ret = FAIL;
DBG_ENTER("mysqlnd_conn_data::tx_begin");
@@ -2843,7 +2192,7 @@ MYSQLND_METHOD(mysqlnd_conn_data, tx_begin)(MYSQLND_CONN_DATA * conn, const unsi
name_esc = NULL;
}
if (!query) {
- SET_OOM_ERROR(*conn->error_info);
+ SET_OOM_ERROR(conn->error_info);
break;
}
ret = conn->m->query(conn, query, query_len);
@@ -2862,7 +2211,7 @@ MYSQLND_METHOD(mysqlnd_conn_data, tx_begin)(MYSQLND_CONN_DATA * conn, const unsi
static enum_func_status
MYSQLND_METHOD(mysqlnd_conn_data, tx_savepoint)(MYSQLND_CONN_DATA * conn, const char * const name)
{
- size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, tx_savepoint);
+ const size_t this_func = STRUCT_OFFSET(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn_data), tx_savepoint);
enum_func_status ret = FAIL;
DBG_ENTER("mysqlnd_conn_data::tx_savepoint");
@@ -2871,12 +2220,12 @@ MYSQLND_METHOD(mysqlnd_conn_data, tx_savepoint)(MYSQLND_CONN_DATA * conn, const
char * query;
unsigned int query_len;
if (!name) {
- SET_CLIENT_ERROR(*conn->error_info, CR_UNKNOWN_ERROR, UNKNOWN_SQLSTATE, "Savepoint name not provided");
+ SET_CLIENT_ERROR(conn->error_info, CR_UNKNOWN_ERROR, UNKNOWN_SQLSTATE, "Savepoint name not provided");
break;
}
query_len = mnd_sprintf(&query, 0, "SAVEPOINT `%s`", name);
if (!query) {
- SET_OOM_ERROR(*conn->error_info);
+ SET_OOM_ERROR(conn->error_info);
break;
}
ret = conn->m->query(conn, query, query_len);
@@ -2894,7 +2243,7 @@ MYSQLND_METHOD(mysqlnd_conn_data, tx_savepoint)(MYSQLND_CONN_DATA * conn, const
static enum_func_status
MYSQLND_METHOD(mysqlnd_conn_data, tx_savepoint_release)(MYSQLND_CONN_DATA * conn, const char * const name)
{
- size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, tx_savepoint_release);
+ const size_t this_func = STRUCT_OFFSET(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn_data), tx_savepoint_release);
enum_func_status ret = FAIL;
DBG_ENTER("mysqlnd_conn_data::tx_savepoint_release");
@@ -2903,12 +2252,12 @@ MYSQLND_METHOD(mysqlnd_conn_data, tx_savepoint_release)(MYSQLND_CONN_DATA * conn
char * query;
unsigned int query_len;
if (!name) {
- SET_CLIENT_ERROR(*conn->error_info, CR_UNKNOWN_ERROR, UNKNOWN_SQLSTATE, "Savepoint name not provided");
+ SET_CLIENT_ERROR(conn->error_info, CR_UNKNOWN_ERROR, UNKNOWN_SQLSTATE, "Savepoint name not provided");
break;
}
query_len = mnd_sprintf(&query, 0, "RELEASE SAVEPOINT `%s`", name);
if (!query) {
- SET_OOM_ERROR(*conn->error_info);
+ SET_OOM_ERROR(conn->error_info);
break;
}
ret = conn->m->query(conn, query, query_len);
@@ -2923,8 +2272,8 @@ MYSQLND_METHOD(mysqlnd_conn_data, tx_savepoint_release)(MYSQLND_CONN_DATA * conn
/* {{{ mysqlnd_conn_data::negotiate_client_api_capabilities */
-static unsigned int
-MYSQLND_METHOD(mysqlnd_conn_data, negotiate_client_api_capabilities)(MYSQLND_CONN_DATA * const conn, const unsigned int flags)
+static size_t
+MYSQLND_METHOD(mysqlnd_conn_data, negotiate_client_api_capabilities)(MYSQLND_CONN_DATA * const conn, const size_t flags)
{
unsigned int ret = 0;
DBG_ENTER("mysqlnd_conn_data::negotiate_client_api_capabilities");
@@ -2939,7 +2288,7 @@ MYSQLND_METHOD(mysqlnd_conn_data, negotiate_client_api_capabilities)(MYSQLND_CON
/* {{{ mysqlnd_conn_data::get_client_api_capabilities */
-static unsigned int
+static size_t
MYSQLND_METHOD(mysqlnd_conn_data, get_client_api_capabilities)(const MYSQLND_CONN_DATA * const conn)
{
DBG_ENTER("mysqlnd_conn_data::get_client_api_capabilities");
@@ -2950,18 +2299,17 @@ MYSQLND_METHOD(mysqlnd_conn_data, get_client_api_capabilities)(const MYSQLND_CON
/* {{{ mysqlnd_conn_data::local_tx_start */
static enum_func_status
-MYSQLND_METHOD(mysqlnd_conn_data, local_tx_start)(MYSQLND_CONN_DATA * conn, size_t this_func)
+MYSQLND_METHOD(mysqlnd_conn_data, local_tx_start)(MYSQLND_CONN_DATA * conn, const size_t this_func)
{
- enum_func_status ret = PASS;
DBG_ENTER("mysqlnd_conn_data::local_tx_start");
- DBG_RETURN(ret);
+ DBG_RETURN(PASS);
}
/* }}} */
/* {{{ mysqlnd_conn_data::local_tx_end */
static enum_func_status
-MYSQLND_METHOD(mysqlnd_conn_data, local_tx_end)(MYSQLND_CONN_DATA * conn, size_t this_func, enum_func_status status)
+MYSQLND_METHOD(mysqlnd_conn_data, local_tx_end)(MYSQLND_CONN_DATA * conn, const size_t this_func, const enum_func_status status)
{
DBG_ENTER("mysqlnd_conn_data::local_tx_end");
DBG_RETURN(status);
@@ -2969,27 +2317,19 @@ MYSQLND_METHOD(mysqlnd_conn_data, local_tx_end)(MYSQLND_CONN_DATA * conn, size_t
/* }}} */
-/* {{{ mysqlnd_conn_data::init */
-static enum_func_status
-MYSQLND_METHOD(mysqlnd_conn_data, init)(MYSQLND_CONN_DATA * conn)
+/* {{{ _mysqlnd_stmt_init */
+MYSQLND_STMT *
+MYSQLND_METHOD(mysqlnd_conn_data, stmt_init)(MYSQLND_CONN_DATA * const conn)
{
- DBG_ENTER("mysqlnd_conn_data::init");
- mysqlnd_stats_init(&conn->stats, STAT_LAST, conn->persistent);
- SET_ERROR_AFF_ROWS(conn);
-
- conn->net = mysqlnd_net_init(conn->persistent, conn->stats, conn->error_info);
- conn->protocol = mysqlnd_protocol_init(conn->persistent);
-
- DBG_RETURN(conn->stats && conn->net && conn->protocol? PASS:FAIL);
+ MYSQLND_STMT * ret;
+ DBG_ENTER("mysqlnd_conn_data::stmt_init");
+ ret = conn->object_factory.get_prepared_statement(conn, conn->persistent);
+ DBG_RETURN(ret);
}
/* }}} */
-MYSQLND_STMT * _mysqlnd_stmt_init(MYSQLND_CONN_DATA * const conn);
-
-
MYSQLND_CLASS_METHODS_START(mysqlnd_conn_data)
- MYSQLND_METHOD(mysqlnd_conn_data, init),
MYSQLND_METHOD(mysqlnd_conn_data, connect),
MYSQLND_METHOD(mysqlnd_conn_data, escape_string),
@@ -3002,7 +2342,7 @@ MYSQLND_CLASS_METHODS_START(mysqlnd_conn_data)
MYSQLND_METHOD(mysqlnd_conn_data, next_result),
MYSQLND_METHOD(mysqlnd_conn_data, more_results),
- _mysqlnd_stmt_init,
+ MYSQLND_METHOD(mysqlnd_conn_data, stmt_init),
MYSQLND_METHOD(mysqlnd_conn_data, shutdown),
MYSQLND_METHOD(mysqlnd_conn_data, refresh),
@@ -3013,7 +2353,7 @@ MYSQLND_CLASS_METHODS_START(mysqlnd_conn_data)
MYSQLND_METHOD(mysqlnd_conn_data, dump_debug_info),
MYSQLND_METHOD(mysqlnd_conn_data, change_user),
- MYSQLND_METHOD(mysqlnd_conn_data, errno),
+ MYSQLND_METHOD(mysqlnd_conn_data, err_no),
MYSQLND_METHOD(mysqlnd_conn_data, error),
MYSQLND_METHOD(mysqlnd_conn_data, sqlstate),
MYSQLND_METHOD(mysqlnd_conn_data, thread_id),
@@ -3027,7 +2367,6 @@ MYSQLND_CLASS_METHODS_START(mysqlnd_conn_data)
MYSQLND_METHOD(mysqlnd_conn_data, get_proto_info),
MYSQLND_METHOD(mysqlnd_conn_data, info),
MYSQLND_METHOD(mysqlnd_conn_data, charset_name),
- MYSQLND_METHOD(mysqlnd_conn_data, list_fields),
MYSQLND_METHOD(mysqlnd_conn_data, list_method),
MYSQLND_METHOD(mysqlnd_conn_data, insert_id),
@@ -3048,11 +2387,7 @@ MYSQLND_CLASS_METHODS_START(mysqlnd_conn_data)
MYSQLND_METHOD_PRIVATE(mysqlnd_conn_data, get_reference),
MYSQLND_METHOD_PRIVATE(mysqlnd_conn_data, free_reference),
- MYSQLND_METHOD_PRIVATE(mysqlnd_conn_data, get_state),
- MYSQLND_METHOD_PRIVATE(mysqlnd_conn_data, set_state),
- MYSQLND_METHOD(mysqlnd_conn_data, simple_command),
- MYSQLND_METHOD(mysqlnd_conn_data, simple_command_handle_response),
MYSQLND_METHOD(mysqlnd_conn_data, restart_psession),
MYSQLND_METHOD(mysqlnd_conn_data, end_psession),
MYSQLND_METHOD(mysqlnd_conn_data, send_close),
@@ -3073,13 +2408,14 @@ MYSQLND_CLASS_METHODS_START(mysqlnd_conn_data)
MYSQLND_METHOD(mysqlnd_conn_data, execute_init_commands),
MYSQLND_METHOD(mysqlnd_conn_data, get_updated_connect_flags),
MYSQLND_METHOD(mysqlnd_conn_data, connect_handshake),
- MYSQLND_METHOD(mysqlnd_conn_data, simple_command_send_request),
MYSQLND_METHOD(mysqlnd_conn_data, fetch_auth_plugin_by_name),
MYSQLND_METHOD(mysqlnd_conn_data, set_client_option_2d),
MYSQLND_METHOD(mysqlnd_conn_data, negotiate_client_api_capabilities),
- MYSQLND_METHOD(mysqlnd_conn_data, get_client_api_capabilities)
+ MYSQLND_METHOD(mysqlnd_conn_data, get_client_api_capabilities),
+
+ MYSQLND_METHOD(mysqlnd_conn_data, get_scheme)
MYSQLND_CLASS_METHODS_END;
@@ -3089,7 +2425,7 @@ MYSQLND_METHOD(mysqlnd_conn, clone_object)(MYSQLND * const conn)
{
MYSQLND * ret;
DBG_ENTER("mysqlnd_conn::get_reference");
- ret = MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_object_factory).clone_connection_object(conn);
+ ret = conn->data->object_factory.clone_connection_object(conn);
DBG_RETURN(ret);
}
/* }}} */
@@ -3113,9 +2449,9 @@ MYSQLND_METHOD_PRIVATE(mysqlnd_conn, dtor)(MYSQLND * conn)
/* {{{ mysqlnd_conn_data::close */
static enum_func_status
-MYSQLND_METHOD(mysqlnd_conn, close)(MYSQLND * conn_handle, enum_connection_close_type close_type)
+MYSQLND_METHOD(mysqlnd_conn, close)(MYSQLND * conn_handle, const enum_connection_close_type close_type)
{
- size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_methods, close);
+ const size_t this_func = STRUCT_OFFSET(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn), close);
MYSQLND_CONN_DATA * conn = conn_handle->data;
enum_func_status ret = FAIL;
@@ -3123,7 +2459,7 @@ MYSQLND_METHOD(mysqlnd_conn, close)(MYSQLND * conn_handle, enum_connection_close
DBG_INF_FMT("conn=%llu", conn->thread_id);
if (PASS == conn->m->local_tx_start(conn, this_func)) {
- if (CONN_GET_STATE(conn) >= CONN_READY) {
+ if (GET_CONNECTION_STATE(&conn->state) >= CONN_READY) {
static enum_mysqlnd_collected_stats close_type_to_stat_map[MYSQLND_CLOSE_LAST] = {
STAT_CLOSE_EXPLICIT,
STAT_CLOSE_IMPLICIT,
@@ -3138,7 +2474,7 @@ MYSQLND_METHOD(mysqlnd_conn, close)(MYSQLND * conn_handle, enum_connection_close
*/
ret = conn->m->send_close(conn);
- /* do it after free_reference/dtor and we might crash */
+ /* If we do it after free_reference/dtor then we might crash */
conn->m->local_tx_end(conn, this_func, ret);
conn_handle->m->dtor(conn_handle);
@@ -3156,20 +2492,267 @@ MYSQLND_CLASS_METHODS_START(mysqlnd_conn)
MYSQLND_CLASS_METHODS_END;
-/* {{{ mysqlnd_init */
+#include "php_network.h"
+
+/* {{{ mysqlnd_stream_array_to_fd_set */
+MYSQLND **
+mysqlnd_stream_array_check_for_readiness(MYSQLND ** conn_array)
+{
+ unsigned int cnt = 0;
+ MYSQLND **p = conn_array, **p_p;
+ MYSQLND **ret = NULL;
+
+ while (*p) {
+ const enum mysqlnd_connection_state conn_state = GET_CONNECTION_STATE(&((*p)->data->state));
+ if (conn_state <= CONN_READY || conn_state == CONN_QUIT_SENT) {
+ cnt++;
+ }
+ p++;
+ }
+ if (cnt) {
+ MYSQLND **ret_p = ret = ecalloc(cnt + 1, sizeof(MYSQLND *));
+ p_p = p = conn_array;
+ while (*p) {
+ const enum mysqlnd_connection_state conn_state = GET_CONNECTION_STATE(&((*p)->data->state));
+ if (conn_state <= CONN_READY || conn_state == CONN_QUIT_SENT) {
+ *ret_p = *p;
+ *p = NULL;
+ ret_p++;
+ } else {
+ *p_p = *p;
+ p_p++;
+ }
+ p++;
+ }
+ *ret_p = NULL;
+ }
+ return ret;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_stream_array_to_fd_set */
+static unsigned int
+mysqlnd_stream_array_to_fd_set(MYSQLND ** conn_array, fd_set * fds, php_socket_t * max_fd)
+{
+ php_socket_t this_fd;
+ php_stream *stream = NULL;
+ unsigned int cnt = 0;
+ MYSQLND **p = conn_array;
+ DBG_ENTER("mysqlnd_stream_array_to_fd_set");
+
+ while (*p) {
+ /* get the fd.
+ * NB: Most other code will NOT use the PHP_STREAM_CAST_INTERNAL flag
+ * when casting. It is only used here so that the buffered data warning
+ * is not displayed.
+ * */
+ stream = (*p)->data->vio->data->m.get_stream((*p)->data->vio);
+ DBG_INF_FMT("conn=%llu stream=%p", (*p)->data->thread_id, stream);
+ if (stream != NULL &&
+ SUCCESS == php_stream_cast(stream, PHP_STREAM_AS_FD_FOR_SELECT | PHP_STREAM_CAST_INTERNAL, (void*)&this_fd, 1) &&
+ ZEND_VALID_SOCKET(this_fd))
+ {
+
+ PHP_SAFE_FD_SET(this_fd, fds);
+
+ if (this_fd > *max_fd) {
+ *max_fd = this_fd;
+ }
+ ++cnt;
+ }
+ ++p;
+ }
+ DBG_RETURN(cnt ? 1 : 0);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_stream_array_from_fd_set */
+static unsigned int
+mysqlnd_stream_array_from_fd_set(MYSQLND ** conn_array, fd_set * fds)
+{
+ php_socket_t this_fd;
+ php_stream *stream = NULL;
+ unsigned int ret = 0;
+ zend_bool disproportion = FALSE;
+ MYSQLND **fwd = conn_array, **bckwd = conn_array;
+ DBG_ENTER("mysqlnd_stream_array_from_fd_set");
+
+ while (*fwd) {
+ stream = (*fwd)->data->vio->data->m.get_stream((*fwd)->data->vio);
+ DBG_INF_FMT("conn=%llu stream=%p", (*fwd)->data->thread_id, stream);
+ if (stream != NULL && SUCCESS == php_stream_cast(stream, PHP_STREAM_AS_FD_FOR_SELECT | PHP_STREAM_CAST_INTERNAL,
+ (void*)&this_fd, 1) && ZEND_VALID_SOCKET(this_fd)) {
+ if (PHP_SAFE_FD_ISSET(this_fd, fds)) {
+ if (disproportion) {
+ *bckwd = *fwd;
+ }
+ ++bckwd;
+ ++fwd;
+ ++ret;
+ continue;
+ }
+ }
+ disproportion = TRUE;
+ ++fwd;
+ }
+ *bckwd = NULL;/* NULL-terminate the list */
+
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+#ifndef PHP_WIN32
+#define php_select(m, r, w, e, t) select(m, r, w, e, t)
+#else
+#include "win32/select.h"
+#endif
+
+
+/* {{{ mysqlnd_poll */
+PHPAPI enum_func_status
+mysqlnd_poll(MYSQLND **r_array, MYSQLND **e_array, MYSQLND ***dont_poll, long sec, long usec, int * desc_num)
+{
+ struct timeval tv;
+ struct timeval *tv_p = NULL;
+ fd_set rfds, wfds, efds;
+ php_socket_t max_fd = 0;
+ int retval, sets = 0;
+ int set_count, max_set_count = 0;
+
+ DBG_ENTER("_mysqlnd_poll");
+ if (sec < 0 || usec < 0) {
+ php_error_docref(NULL, E_WARNING, "Negative values passed for sec and/or usec");
+ DBG_RETURN(FAIL);
+ }
+
+ FD_ZERO(&rfds);
+ FD_ZERO(&wfds);
+ FD_ZERO(&efds);
+
+ if (r_array != NULL) {
+ *dont_poll = mysqlnd_stream_array_check_for_readiness(r_array);
+ set_count = mysqlnd_stream_array_to_fd_set(r_array, &rfds, &max_fd);
+ if (set_count > max_set_count) {
+ max_set_count = set_count;
+ }
+ sets += set_count;
+ }
+
+ if (e_array != NULL) {
+ set_count = mysqlnd_stream_array_to_fd_set(e_array, &efds, &max_fd);
+ if (set_count > max_set_count) {
+ max_set_count = set_count;
+ }
+ sets += set_count;
+ }
+
+ if (!sets) {
+ php_error_docref(NULL, E_WARNING, *dont_poll ? "All arrays passed are clear":"No stream arrays were passed");
+ DBG_ERR_FMT(*dont_poll ? "All arrays passed are clear":"No stream arrays were passed");
+ DBG_RETURN(FAIL);
+ }
+
+ PHP_SAFE_MAX_FD(max_fd, max_set_count);
+
+ /* Solaris + BSD do not like microsecond values which are >= 1 sec */
+ if (usec > 999999) {
+ tv.tv_sec = sec + (usec / 1000000);
+ tv.tv_usec = usec % 1000000;
+ } else {
+ tv.tv_sec = sec;
+ tv.tv_usec = usec;
+ }
+
+ tv_p = &tv;
+
+ retval = php_select(max_fd + 1, &rfds, &wfds, &efds, tv_p);
+
+ if (retval == -1) {
+ php_error_docref(NULL, E_WARNING, "unable to select [%d]: %s (max_fd=%d)",
+ errno, strerror(errno), max_fd);
+ DBG_RETURN(FAIL);
+ }
+
+ if (r_array != NULL) {
+ mysqlnd_stream_array_from_fd_set(r_array, &rfds);
+ }
+ if (e_array != NULL) {
+ mysqlnd_stream_array_from_fd_set(e_array, &efds);
+ }
+
+ *desc_num = retval;
+ DBG_RETURN(PASS);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_connect */
+PHPAPI MYSQLND * mysqlnd_connection_connect(MYSQLND * conn_handle,
+ const char * const host,
+ const char * const user,
+ const char * const passwd, unsigned int passwd_len,
+ const char * const db, unsigned int db_len,
+ unsigned int port,
+ const char * const sock_or_pipe,
+ unsigned int mysql_flags,
+ unsigned int client_api_flags
+ )
+{
+ enum_func_status ret = FAIL;
+ zend_bool self_alloced = FALSE;
+ MYSQLND_CSTRING hostname = { host, host? strlen(host) : 0 };
+ MYSQLND_CSTRING username = { user, user? strlen(user) : 0 };
+ MYSQLND_CSTRING password = { passwd, passwd_len };
+ MYSQLND_CSTRING database = { db, db_len };
+ MYSQLND_CSTRING socket_or_pipe = { sock_or_pipe, sock_or_pipe? strlen(sock_or_pipe) : 0 };
+
+ DBG_ENTER("mysqlnd_connect");
+ DBG_INF_FMT("host=%s user=%s db=%s port=%u flags=%u", host? host:"", user? user:"", db? db:"", port, mysql_flags);
+
+ if (!conn_handle) {
+ self_alloced = TRUE;
+ if (!(conn_handle = mysqlnd_connection_init(client_api_flags, FALSE, NULL))) {
+ /* OOM */
+ DBG_RETURN(NULL);
+ }
+ }
+
+ ret = conn_handle->m->connect(conn_handle, hostname, username, password, database, port, socket_or_pipe, mysql_flags);
+
+ if (ret == FAIL) {
+ if (self_alloced) {
+ /*
+ We have alloced, thus there are no references to this
+ object - we are free to kill it!
+ */
+ conn_handle->m->dtor(conn_handle);
+ }
+ DBG_RETURN(NULL);
+ }
+ DBG_RETURN(conn_handle);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_connection_init */
PHPAPI MYSQLND *
-mysqlnd_init(unsigned int flags, zend_bool persistent)
+mysqlnd_connection_init(const size_t client_flags, const zend_bool persistent, MYSQLND_CLASS_METHODS_TYPE(mysqlnd_object_factory) *object_factory)
{
+ MYSQLND_CLASS_METHODS_TYPE(mysqlnd_object_factory) *factory = object_factory? object_factory : &MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_object_factory);
MYSQLND * ret;
- DBG_ENTER("mysqlnd_init");
- ret = MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_object_factory).get_connection(persistent);
+ DBG_ENTER("mysqlnd_connection_init");
+ ret = factory->get_connection(factory, persistent);
if (ret && ret->data) {
- ret->data->m->negotiate_client_api_capabilities(ret->data, flags);
+ ret->data->m->negotiate_client_api_capabilities(ret->data, client_flags);
}
DBG_RETURN(ret);
}
/* }}} */
+
/*
* Local variables:
* tab-width: 4
diff --git a/ext/mysqlnd/mysqlnd_connection.h b/ext/mysqlnd/mysqlnd_connection.h
new file mode 100644
index 0000000000..71fca55ceb
--- /dev/null
+++ b/ext/mysqlnd/mysqlnd_connection.h
@@ -0,0 +1,87 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 7 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 2006-2016 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: Andrey Hristov <andrey@php.net> |
+ | Ulf Wendel <uw@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#ifndef MYSQLND_CONNECTION_H
+#define MYSQLND_CONNECTION_H
+
+PHPAPI extern const char * const mysqlnd_out_of_sync;
+PHPAPI extern const char * const mysqlnd_server_gone;
+PHPAPI extern const char * const mysqlnd_out_of_memory;
+
+
+void mysqlnd_upsert_status_init(MYSQLND_UPSERT_STATUS * const upsert_status);
+
+#define UPSERT_STATUS_RESET(status) (status)->m->reset((status))
+
+#define UPSERT_STATUS_GET_SERVER_STATUS(status) (status)->server_status
+#define UPSERT_STATUS_SET_SERVER_STATUS(status, server_st) (status)->server_status = (server_st)
+
+#define UPSERT_STATUS_GET_WARNINGS(status) (status)->warning_count
+#define UPSERT_STATUS_SET_WARNINGS(status, warnings) (status)->warning_count = (warnings)
+
+#define UPSERT_STATUS_GET_AFFECTED_ROWS(status) (status)->affected_rows
+#define UPSERT_STATUS_SET_AFFECTED_ROWS(status, rows) (status)->affected_rows = (rows)
+#define UPSERT_STATUS_SET_AFFECTED_ROWS_TO_ERROR(status) (status)->m->set_affected_rows_to_error((status))
+
+#define UPSERT_STATUS_GET_LAST_INSERT_ID(status) (status)->last_insert_id
+#define UPSERT_STATUS_SET_LAST_INSERT_ID(status, id) (status)->last_insert_id = (id)
+
+
+/* Error handling */
+#define SET_NEW_MESSAGE(buf, buf_len, message, len, persistent) \
+ {\
+ if ((buf)) { \
+ mnd_pefree((buf), (persistent)); \
+ } \
+ if ((message)) { \
+ (buf) = mnd_pestrndup((message), (len), (persistent)); \
+ } else { \
+ (buf) = NULL; \
+ } \
+ (buf_len) = (len); \
+ }
+
+#define SET_EMPTY_MESSAGE(buf, buf_len, persistent) \
+ {\
+ if ((buf)) { \
+ mnd_pefree((buf), (persistent)); \
+ (buf) = NULL; \
+ } \
+ (buf_len) = 0; \
+ }
+
+
+PHPAPI enum_func_status mysqlnd_error_info_init(MYSQLND_ERROR_INFO * const info, const zend_bool persistent);
+PHPAPI void mysqlnd_error_info_free_contents(MYSQLND_ERROR_INFO * const info);
+
+#define GET_CONNECTION_STATE(state_struct) (state_struct)->m->get((state_struct))
+#define SET_CONNECTION_STATE(state_struct, s) (state_struct)->m->set((state_struct), (s))
+
+PHPAPI void mysqlnd_connection_state_init(struct st_mysqlnd_connection_state * const state);
+
+#endif /* MYSQLND_CONNECTION_H */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: noet sw=4 ts=4 fdm=marker
+ * vim<600: noet sw=4 ts=4
+ */
diff --git a/ext/mysqlnd/mysqlnd_debug.c b/ext/mysqlnd/mysqlnd_debug.c
index 989339854d..3a03d56975 100644
--- a/ext/mysqlnd/mysqlnd_debug.c
+++ b/ext/mysqlnd/mysqlnd_debug.c
@@ -14,7 +14,6 @@
+----------------------------------------------------------------------+
| Authors: Andrey Hristov <andrey@php.net> |
| Ulf Wendel <uw@php.net> |
- | Georg Richter <georg@php.net> |
+----------------------------------------------------------------------+
*/
diff --git a/ext/mysqlnd/mysqlnd_debug.h b/ext/mysqlnd/mysqlnd_debug.h
index 854ab4cb22..c9eced8af3 100644
--- a/ext/mysqlnd/mysqlnd_debug.h
+++ b/ext/mysqlnd/mysqlnd_debug.h
@@ -14,7 +14,6 @@
+----------------------------------------------------------------------+
| Authors: Andrey Hristov <andrey@php.net> |
| Ulf Wendel <uw@php.net> |
- | Georg Richter <georg@php.net> |
+----------------------------------------------------------------------+
*/
@@ -68,6 +67,17 @@ void mysqlnd_debug_trace_plugin_register(void);
PHPAPI MYSQLND_DEBUG * mysqlnd_debug_init(const char * skip_functions[]);
+#define MYSQLND_DEBUG_DUMP_TIME 1
+#define MYSQLND_DEBUG_DUMP_TRACE 2
+#define MYSQLND_DEBUG_DUMP_PID 4
+#define MYSQLND_DEBUG_DUMP_LINE 8
+#define MYSQLND_DEBUG_DUMP_FILE 16
+#define MYSQLND_DEBUG_DUMP_LEVEL 32
+#define MYSQLND_DEBUG_APPEND 64
+#define MYSQLND_DEBUG_FLUSH 128
+#define MYSQLND_DEBUG_TRACE_MEMORY_CALLS 256
+#define MYSQLND_DEBUG_PROFILE_CALLS 512
+
#if defined(__GNUC__) || defined(PHP_WIN32)
#ifdef PHP_WIN32
diff --git a/ext/mysqlnd/mysqlnd_driver.c b/ext/mysqlnd/mysqlnd_driver.c
index f437a399e2..ced03cf3b2 100644
--- a/ext/mysqlnd/mysqlnd_driver.c
+++ b/ext/mysqlnd/mysqlnd_driver.c
@@ -14,17 +14,19 @@
+----------------------------------------------------------------------+
| Authors: Andrey Hristov <andrey@php.net> |
| Ulf Wendel <uw@php.net> |
- | Georg Richter <georg@php.net> |
+----------------------------------------------------------------------+
*/
#include "php.h"
#include "mysqlnd.h"
+#include "mysqlnd_vio.h"
+#include "mysqlnd_protocol_frame_codec.h"
#include "mysqlnd_wireprotocol.h"
+#include "mysqlnd_connection.h"
+#include "mysqlnd_ps.h"
+#include "mysqlnd_plugin.h"
#include "mysqlnd_priv.h"
-#include "mysqlnd_result.h"
#include "mysqlnd_statistics.h"
-#include "mysqlnd_charset.h"
#include "mysqlnd_debug.h"
#include "mysqlnd_reverse_api.h"
#include "mysqlnd_ext_plugin.h"
@@ -92,25 +94,9 @@ PHPAPI void mysqlnd_library_init(void)
/* }}} */
-
-/* {{{ mysqlnd_error_list_pdtor */
-static void
-mysqlnd_error_list_pdtor(void * pDest)
-{
- MYSQLND_ERROR_LIST_ELEMENT * element = (MYSQLND_ERROR_LIST_ELEMENT *) pDest;
-
- DBG_ENTER("mysqlnd_error_list_pdtor");
- if (element->error) {
- mnd_pefree(element->error, TRUE);
- }
- DBG_VOID_RETURN;
-}
-/* }}} */
-
-
/* {{{ mysqlnd_object_factory::get_connection */
static MYSQLND *
-MYSQLND_METHOD(mysqlnd_object_factory, get_connection)(zend_bool persistent)
+MYSQLND_METHOD(mysqlnd_object_factory, get_connection)(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_object_factory) *factory, const zend_bool persistent)
{
size_t alloc_size_ret = sizeof(MYSQLND) + mysqlnd_plugin_count() * sizeof(void *);
size_t alloc_size_ret_data = sizeof(MYSQLND_CONN_DATA) + mysqlnd_plugin_count() * sizeof(void *);
@@ -132,26 +118,36 @@ MYSQLND_METHOD(mysqlnd_object_factory, get_connection)(zend_bool persistent)
new_object->m = mysqlnd_conn_get_methods();
data = new_object->data;
- data->error_info = &(data->error_info_impl);
+ if (FAIL == mysqlnd_error_info_init(&data->error_info_impl, persistent)) {
+ new_object->m->dtor(new_object);
+ DBG_RETURN(NULL);
+ }
+ data->error_info = &data->error_info_impl;
+
data->options = &(data->options_impl);
+
+ mysqlnd_upsert_status_init(&data->upsert_status_impl);
data->upsert_status = &(data->upsert_status_impl);
+ UPSERT_STATUS_SET_AFFECTED_ROWS_TO_ERROR(data->upsert_status);
data->persistent = persistent;
data->m = mysqlnd_conn_data_get_methods();
- CONN_SET_STATE(data, CONN_ALLOCED);
+ data->object_factory = *factory;
+
+ mysqlnd_connection_state_init(&data->state);
+
data->m->get_reference(data);
- if (PASS != data->m->init(data)) {
- new_object->m->dtor(new_object);
- DBG_RETURN(NULL);
- }
+ mysqlnd_stats_init(&data->stats, STAT_LAST, persistent);
- data->error_info->error_list = mnd_pecalloc(1, sizeof(zend_llist), persistent);
- if (!data->error_info->error_list) {
+ data->protocol_frame_codec = mysqlnd_pfc_init(persistent, factory, data->stats, data->error_info);
+ data->vio = mysqlnd_vio_init(persistent, factory, data->stats, data->error_info);
+ data->payload_decoder_factory = mysqlnd_protocol_payload_decoder_factory_init(data, persistent);
+ data->command_factory = mysqlnd_command_factory_get();
+
+ if (!data->protocol_frame_codec || !data->vio || !data->payload_decoder_factory || !data->command_factory) {
new_object->m->dtor(new_object);
DBG_RETURN(NULL);
- } else {
- zend_llist_init(data->error_info->error_list, sizeof(MYSQLND_ERROR_LIST_ELEMENT), (llist_dtor_func_t)mysqlnd_error_list_pdtor, persistent);
}
DBG_RETURN(new_object);
@@ -190,7 +186,7 @@ MYSQLND_METHOD(mysqlnd_object_factory, clone_connection_object)(MYSQLND * to_be_
/* {{{ mysqlnd_object_factory::get_prepared_statement */
static MYSQLND_STMT *
-MYSQLND_METHOD(mysqlnd_object_factory, get_prepared_statement)(MYSQLND_CONN_DATA * const conn)
+MYSQLND_METHOD(mysqlnd_object_factory, get_prepared_statement)(MYSQLND_CONN_DATA * const conn, const zend_bool persistent)
{
size_t alloc_size = sizeof(MYSQLND_STMT) + mysqlnd_plugin_count() * sizeof(void *);
MYSQLND_STMT * ret = mnd_pecalloc(1, alloc_size, conn->persistent);
@@ -204,13 +200,19 @@ MYSQLND_METHOD(mysqlnd_object_factory, get_prepared_statement)(MYSQLND_CONN_DATA
ret->m = mysqlnd_stmt_get_methods();
ret->persistent = conn->persistent;
- stmt = ret->data = mnd_pecalloc(1, sizeof(MYSQLND_STMT_DATA), conn->persistent);
+ stmt = ret->data = mnd_pecalloc(1, sizeof(MYSQLND_STMT_DATA), persistent);
DBG_INF_FMT("stmt=%p", stmt);
if (!stmt) {
break;
}
- stmt->persistent = conn->persistent;
- stmt->error_info = &(stmt->error_info_impl);
+ stmt->persistent = persistent;
+
+ if (FAIL == mysqlnd_error_info_init(&stmt->error_info_impl, persistent)) {
+ break;
+ }
+ stmt->error_info = &stmt->error_info_impl;
+
+ mysqlnd_upsert_status_init(&stmt->upsert_status_impl);
stmt->upsert_status = &(stmt->upsert_status_impl);
stmt->state = MYSQLND_STMT_INITTED;
stmt->execute_cmd_buffer.length = 4096;
@@ -220,23 +222,18 @@ MYSQLND_METHOD(mysqlnd_object_factory, get_prepared_statement)(MYSQLND_CONN_DATA
}
stmt->prefetch_rows = MYSQLND_DEFAULT_PREFETCH_ROWS;
+
/*
Mark that we reference the connection, thus it won't be
be destructed till there is open statements. The last statement
or normal query result will close it then.
*/
stmt->conn = conn->m->get_reference(conn);
- stmt->error_info->error_list = mnd_pecalloc(1, sizeof(zend_llist), ret->persistent);
- if (!stmt->error_info->error_list) {
- break;
- }
-
- zend_llist_init(stmt->error_info->error_list, sizeof(MYSQLND_ERROR_LIST_ELEMENT), (llist_dtor_func_t) mysqlnd_error_list_pdtor, conn->persistent);
DBG_RETURN(ret);
} while (0);
- SET_OOM_ERROR(*conn->error_info);
+ SET_OOM_ERROR(conn->error_info);
if (ret) {
ret->m->dtor(ret, TRUE);
ret = NULL;
@@ -246,53 +243,89 @@ MYSQLND_METHOD(mysqlnd_object_factory, get_prepared_statement)(MYSQLND_CONN_DATA
/* }}} */
-/* {{{ mysqlnd_object_factory::get_io_channel */
-PHPAPI MYSQLND_NET *
-MYSQLND_METHOD(mysqlnd_object_factory, get_io_channel)(zend_bool persistent, MYSQLND_STATS * stats, MYSQLND_ERROR_INFO * error_info)
+/* {{{ mysqlnd_object_factory::get_pfc */
+static MYSQLND_PFC *
+MYSQLND_METHOD(mysqlnd_object_factory, get_pfc)(const zend_bool persistent, MYSQLND_STATS * stats, MYSQLND_ERROR_INFO * error_info)
+{
+ size_t pfc_alloc_size = sizeof(MYSQLND_PFC) + mysqlnd_plugin_count() * sizeof(void *);
+ size_t pfc_data_alloc_size = sizeof(MYSQLND_PFC_DATA) + mysqlnd_plugin_count() * sizeof(void *);
+ MYSQLND_PFC * pfc = mnd_pecalloc(1, pfc_alloc_size, persistent);
+ MYSQLND_PFC_DATA * pfc_data = mnd_pecalloc(1, pfc_data_alloc_size, persistent);
+
+ DBG_ENTER("mysqlnd_object_factory::get_pfc");
+ DBG_INF_FMT("persistent=%u", persistent);
+ if (pfc && pfc_data) {
+ pfc->data = pfc_data;
+ pfc->persistent = pfc->data->persistent = persistent;
+ pfc->data->m = *mysqlnd_pfc_get_methods();
+
+ if (PASS != pfc->data->m.init(pfc, stats, error_info)) {
+ pfc->data->m.dtor(pfc, stats, error_info);
+ pfc = NULL;
+ }
+ } else {
+ if (pfc_data) {
+ mnd_pefree(pfc_data, persistent);
+ pfc_data = NULL;
+ }
+ if (pfc) {
+ mnd_pefree(pfc, persistent);
+ pfc = NULL;
+ }
+ }
+ DBG_RETURN(pfc);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_object_factory::get_vio */
+static MYSQLND_VIO *
+MYSQLND_METHOD(mysqlnd_object_factory, get_vio)(const zend_bool persistent, MYSQLND_STATS * stats, MYSQLND_ERROR_INFO * error_info)
{
- size_t net_alloc_size = sizeof(MYSQLND_NET) + mysqlnd_plugin_count() * sizeof(void *);
- size_t net_data_alloc_size = sizeof(MYSQLND_NET_DATA) + mysqlnd_plugin_count() * sizeof(void *);
- MYSQLND_NET * net = mnd_pecalloc(1, net_alloc_size, persistent);
- MYSQLND_NET_DATA * net_data = mnd_pecalloc(1, net_data_alloc_size, persistent);
+ size_t vio_alloc_size = sizeof(MYSQLND_VIO) + mysqlnd_plugin_count() * sizeof(void *);
+ size_t vio_data_alloc_size = sizeof(MYSQLND_VIO_DATA) + mysqlnd_plugin_count() * sizeof(void *);
+ MYSQLND_VIO * vio = mnd_pecalloc(1, vio_alloc_size, persistent);
+ MYSQLND_VIO_DATA * vio_data = mnd_pecalloc(1, vio_data_alloc_size, persistent);
- DBG_ENTER("mysqlnd_object_factory::get_io_channel");
+ DBG_ENTER("mysqlnd_object_factory::get_vio");
DBG_INF_FMT("persistent=%u", persistent);
- if (net && net_data) {
- net->data = net_data;
- net->persistent = net->data->persistent = persistent;
- net->data->m = *mysqlnd_net_get_methods();
-
- if (PASS != net->data->m.init(net, stats, error_info)) {
- net->data->m.dtor(net, stats, error_info);
- net = NULL;
+ if (vio && vio_data) {
+ vio->data = vio_data;
+ vio->persistent = vio->data->persistent = persistent;
+ vio->data->m = *mysqlnd_vio_get_methods();
+
+ if (PASS != vio->data->m.init(vio, stats, error_info)) {
+ vio->data->m.dtor(vio, stats, error_info);
+ vio = NULL;
}
} else {
- if (net_data) {
- mnd_pefree(net_data, persistent);
- net_data = NULL;
+ if (vio_data) {
+ mnd_pefree(vio_data, persistent);
+ vio_data = NULL;
}
- if (net) {
- mnd_pefree(net, persistent);
- net = NULL;
+ if (vio) {
+ mnd_pefree(vio, persistent);
+ vio = NULL;
}
}
- DBG_RETURN(net);
+ DBG_RETURN(vio);
}
/* }}} */
-/* {{{ mysqlnd_object_factory::get_protocol_decoder */
-static MYSQLND_PROTOCOL *
-MYSQLND_METHOD(mysqlnd_object_factory, get_protocol_decoder)(zend_bool persistent)
+/* {{{ mysqlnd_object_factory::get_protocol_payload_decoder_factory */
+static MYSQLND_PROTOCOL_PAYLOAD_DECODER_FACTORY *
+MYSQLND_METHOD(mysqlnd_object_factory, get_protocol_payload_decoder_factory)(MYSQLND_CONN_DATA * conn, const zend_bool persistent)
{
- size_t alloc_size = sizeof(MYSQLND_PROTOCOL) + mysqlnd_plugin_count() * sizeof(void *);
- MYSQLND_PROTOCOL *ret = mnd_pecalloc(1, alloc_size, persistent);
+ size_t alloc_size = sizeof(MYSQLND_PROTOCOL_PAYLOAD_DECODER_FACTORY) + mysqlnd_plugin_count() * sizeof(void *);
+ MYSQLND_PROTOCOL_PAYLOAD_DECODER_FACTORY *ret = mnd_pecalloc(1, alloc_size, persistent);
- DBG_ENTER("mysqlnd_object_factory::get_protocol_decoder");
+ DBG_ENTER("mysqlnd_object_factory::get_protocol_payload_decoder_factory");
DBG_INF_FMT("persistent=%u", persistent);
if (ret) {
ret->persistent = persistent;
- ret->m = mysqlnd_mysqlnd_protocol_methods;
+ ret->conn = conn;
+ ret->m = MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_protocol_payload_decoder_factory);
}
DBG_RETURN(ret);
@@ -304,8 +337,9 @@ PHPAPI MYSQLND_CLASS_METHODS_START(mysqlnd_object_factory)
MYSQLND_METHOD(mysqlnd_object_factory, get_connection),
MYSQLND_METHOD(mysqlnd_object_factory, clone_connection_object),
MYSQLND_METHOD(mysqlnd_object_factory, get_prepared_statement),
- MYSQLND_METHOD(mysqlnd_object_factory, get_io_channel),
- MYSQLND_METHOD(mysqlnd_object_factory, get_protocol_decoder)
+ MYSQLND_METHOD(mysqlnd_object_factory, get_pfc),
+ MYSQLND_METHOD(mysqlnd_object_factory, get_vio),
+ MYSQLND_METHOD(mysqlnd_object_factory, get_protocol_payload_decoder_factory)
MYSQLND_CLASS_METHODS_END;
/*
diff --git a/ext/mysqlnd/mysqlnd_enum_n_def.h b/ext/mysqlnd/mysqlnd_enum_n_def.h
index 21e67b4a14..50fda41208 100644
--- a/ext/mysqlnd/mysqlnd_enum_n_def.h
+++ b/ext/mysqlnd/mysqlnd_enum_n_def.h
@@ -48,6 +48,9 @@
#define MYSQLND_NET_CMD_BUFFER_MIN_SIZE 4096
#define MYSQLND_NET_CMD_BUFFER_MIN_SIZE_STR "4096"
+#define MYSQLND_STMT_ID_LENGTH 4
+
+
#define SERVER_STATUS_IN_TRANS 1 /* Transaction has started */
#define SERVER_STATUS_AUTOCOMMIT 2 /* Server in auto_commit mode */
#define SERVER_MORE_RESULTS_EXISTS 8 /* Multi query - next query exists */
@@ -111,7 +114,29 @@
CLIENT_PROTOCOL_41 | CLIENT_SECURE_CONNECTION | \
CLIENT_MULTI_RESULTS | CLIENT_LOCAL_FILES | CLIENT_PLUGIN_AUTH)
-#define MYSQLND_NET_FLAG_USE_COMPRESSION 1
+#define MYSQLND_PROTOCOL_FLAG_USE_COMPRESSION 1
+
+
+/* Client Error codes */
+#define CR_UNKNOWN_ERROR 2000
+#define CR_CONNECTION_ERROR 2002
+#define CR_SERVER_GONE_ERROR 2006
+#define CR_OUT_OF_MEMORY 2008
+#define CR_SERVER_LOST 2013
+#define CR_COMMANDS_OUT_OF_SYNC 2014
+#define CR_CANT_FIND_CHARSET 2019
+#define CR_MALFORMED_PACKET 2027
+#define CR_NOT_IMPLEMENTED 2054
+#define CR_NO_PREPARE_STMT 2030
+#define CR_PARAMS_NOT_BOUND 2031
+#define CR_INVALID_PARAMETER_NO 2034
+#define CR_INVALID_BUFFER_USE 2035
+
+#define MYSQLND_EE_FILENOTFOUND 7890
+
+#define UNKNOWN_SQLSTATE "HY000"
+
+#define MAX_CHARSET_LEN 32
#define TRANS_START_NO_OPT 0
@@ -185,7 +210,7 @@ typedef enum mysqlnd_parse_exec_response_type
MYSQLND_PARSE_EXEC_RESPONSE_EXPLICIT,
} enum_mysqlnd_parse_exec_response_type;
-typedef enum mysqlnd_option
+typedef enum mysqlnd_client_option
{
MYSQL_OPT_CONNECT_TIMEOUT,
MYSQL_OPT_COMPRESS,
@@ -231,9 +256,9 @@ typedef enum mysqlnd_option
MYSQLND_OPT_SSL_PASSPHRASE = 209,
MYSQLND_OPT_MAX_ALLOWED_PACKET = 210,
MYSQLND_OPT_AUTH_PROTOCOL = 211
-} enum_mysqlnd_option;
+} enum_mysqlnd_client_option;
-typedef enum mysqlnd_protocol_type
+typedef enum mysqlnd_session_protocol_type
{
MYSQL_PROTOCOL_DEFAULT = 0,
MYSQL_PROTOCOL_TCP, /* all, supported */
@@ -241,7 +266,7 @@ typedef enum mysqlnd_protocol_type
MYSQL_PROTOCOL_PIPE, /* win32, not-supported */
MYSQL_PROTOCOL_MEMORY, /* win32, not-supported */
MYSQL_PROTOCOL_LAST
-} enum_mysqlnd_protocol_type;
+} enum_mysqlnd_session_protocol_type;
typedef enum mysqlnd_field_types
{
@@ -493,6 +518,8 @@ typedef enum mysqlnd_collected_stats
STAT_MEM_STRNDUP_COUNT,
STAT_MEM_ESTRDUP_COUNT,
STAT_MEM_STRDUP_COUNT,
+ STAT_MEM_EDUP_COUNT,
+ STAT_MEM_DUP_COUNT,
STAT_TEXT_TYPE_FETCHED_NULL,
STAT_TEXT_TYPE_FETCHED_BIT,
STAT_TEXT_TYPE_FETCHED_INT8,
@@ -634,7 +661,11 @@ enum php_mysqlnd_server_command
COM_BINLOG_DUMP_GTID = 30,
COM_RESET_CONNECTION = 31,
COM_STMT_EXECUTE_BATCH = 32,
- COM_END
+ COM_END,
+ /* Here follow own, non-protocol, commands */
+ COM_REAP_RESULT=240, /* own command */
+ COM_ENABLE_SSL, /* own command */
+ COM_HANDSHAKE, /* own command */
};
diff --git a/ext/mysqlnd/mysqlnd_ext_plugin.c b/ext/mysqlnd/mysqlnd_ext_plugin.c
index 18873a08b4..4da47f6921 100644
--- a/ext/mysqlnd/mysqlnd_ext_plugin.c
+++ b/ext/mysqlnd/mysqlnd_ext_plugin.c
@@ -13,8 +13,8 @@
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
| Authors: Andrey Hristov <andrey@php.net> |
+ | Johannes Schlüter <johannes@php.net> |
| Ulf Wendel <uw@php.net> |
- | Georg Richter <georg@php.net> |
+----------------------------------------------------------------------+
*/
@@ -23,16 +23,18 @@
#include "mysqlnd_priv.h"
#include "mysqlnd_result.h"
#include "mysqlnd_debug.h"
+#include "mysqlnd_commands.h"
+#include "mysqlnd_ext_plugin.h"
static struct st_mysqlnd_conn_methods * mysqlnd_conn_methods;
static struct st_mysqlnd_conn_data_methods * mysqlnd_conn_data_methods;
static struct st_mysqlnd_stmt_methods * mysqlnd_stmt_methods;
-/* {{{ _mysqlnd_plugin_get_plugin_connection_data */
-PHPAPI void **
-_mysqlnd_plugin_get_plugin_connection_data(const MYSQLND * conn, unsigned int plugin_id)
+/* {{{ mysqlnd_plugin__get_plugin_connection_data */
+static void **
+mysqlnd_plugin__get_plugin_connection_data(const MYSQLND * conn, const unsigned int plugin_id)
{
- DBG_ENTER("_mysqlnd_plugin_get_plugin_connection_data");
+ DBG_ENTER("mysqlnd_plugin__get_plugin_connection_data");
DBG_INF_FMT("plugin_id=%u", plugin_id);
if (!conn || plugin_id >= mysqlnd_plugin_count()) {
return NULL;
@@ -42,11 +44,11 @@ _mysqlnd_plugin_get_plugin_connection_data(const MYSQLND * conn, unsigned int pl
/* }}} */
-/* {{{ _mysqlnd_plugin_get_plugin_connection_data_data */
-PHPAPI void **
-_mysqlnd_plugin_get_plugin_connection_data_data(const MYSQLND_CONN_DATA * conn, unsigned int plugin_id)
+/* {{{ mysqlnd_plugin__get_plugin_connection_data_data */
+static void **
+mysqlnd_plugin__get_plugin_connection_data_data(const MYSQLND_CONN_DATA * conn, const unsigned int plugin_id)
{
- DBG_ENTER("_mysqlnd_plugin_get_plugin_connection_data_data");
+ DBG_ENTER("mysqlnd_plugin__get_plugin_connection_data_data");
DBG_INF_FMT("plugin_id=%u", plugin_id);
if (!conn || plugin_id >= mysqlnd_plugin_count()) {
return NULL;
@@ -56,10 +58,11 @@ _mysqlnd_plugin_get_plugin_connection_data_data(const MYSQLND_CONN_DATA * conn,
/* }}} */
-/* {{{ _mysqlnd_plugin_get_plugin_result_data */
-PHPAPI void ** _mysqlnd_plugin_get_plugin_result_data(const MYSQLND_RES * result, unsigned int plugin_id)
+/* {{{ mysqlnd_plugin__get_plugin_result_data */
+static void **
+mysqlnd_plugin__get_plugin_result_data(const MYSQLND_RES * result, const unsigned int plugin_id)
{
- DBG_ENTER("_mysqlnd_plugin_get_plugin_result_data");
+ DBG_ENTER("mysqlnd_plugin__get_plugin_result_data");
DBG_INF_FMT("plugin_id=%u", plugin_id);
if (!result || plugin_id >= mysqlnd_plugin_count()) {
return NULL;
@@ -69,10 +72,11 @@ PHPAPI void ** _mysqlnd_plugin_get_plugin_result_data(const MYSQLND_RES * result
/* }}} */
-/* {{{ _mysqlnd_plugin_get_plugin_result_unbuffered_data */
-PHPAPI void ** _mysqlnd_plugin_get_plugin_result_unbuffered_data(const MYSQLND_RES_UNBUFFERED * result, unsigned int plugin_id)
+/* {{{ _mysqlnd_plugin__get_plugin_result_unbuffered_data */
+static void **
+mysqlnd_plugin__get_plugin_result_unbuffered_data(const MYSQLND_RES_UNBUFFERED * result, const unsigned int plugin_id)
{
- DBG_ENTER("_mysqlnd_plugin_get_plugin_result_data");
+ DBG_ENTER("mysqlnd_plugin__get_plugin_result_data");
DBG_INF_FMT("plugin_id=%u", plugin_id);
if (!result || plugin_id >= mysqlnd_plugin_count()) {
return NULL;
@@ -82,10 +86,11 @@ PHPAPI void ** _mysqlnd_plugin_get_plugin_result_unbuffered_data(const MYSQLND_R
/* }}} */
-/* {{{ _mysqlnd_plugin_get_plugin_result_buffered_data */
-PHPAPI void ** _mysqlnd_plugin_get_plugin_result_buffered_data_zval(const MYSQLND_RES_BUFFERED_ZVAL * result, unsigned int plugin_id)
+/* {{{ _mysqlnd_plugin__get_plugin_result_buffered_data */
+static void **
+mysqlnd_plugin__get_plugin_result_buffered_data_zval(const MYSQLND_RES_BUFFERED_ZVAL * result, const unsigned int plugin_id)
{
- DBG_ENTER("_mysqlnd_plugin_get_plugin_result_data");
+ DBG_ENTER("_mysqlnd_plugin__get_plugin_result_data");
DBG_INF_FMT("plugin_id=%u", plugin_id);
if (!result || plugin_id >= mysqlnd_plugin_count()) {
return NULL;
@@ -94,10 +99,11 @@ PHPAPI void ** _mysqlnd_plugin_get_plugin_result_buffered_data_zval(const MYSQLN
}
/* }}} */
-/* {{{ _mysqlnd_plugin_get_plugin_result_buffered_data */
-PHPAPI void ** _mysqlnd_plugin_get_plugin_result_buffered_data_c(const MYSQLND_RES_BUFFERED_C * result, unsigned int plugin_id)
+/* {{{ mysqlnd_plugin__get_plugin_result_buffered_data */
+static void **
+mysqlnd_plugin__get_plugin_result_buffered_data_c(const MYSQLND_RES_BUFFERED_C * result, const unsigned int plugin_id)
{
- DBG_ENTER("_mysqlnd_plugin_get_plugin_result_data");
+ DBG_ENTER("mysqlnd_plugin__get_plugin_result_data");
DBG_INF_FMT("plugin_id=%u", plugin_id);
if (!result || plugin_id >= mysqlnd_plugin_count()) {
return NULL;
@@ -107,24 +113,25 @@ PHPAPI void ** _mysqlnd_plugin_get_plugin_result_buffered_data_c(const MYSQLND_R
/* }}} */
-/* {{{ _mysqlnd_plugin_get_plugin_protocol_data */
-PHPAPI void **
-_mysqlnd_plugin_get_plugin_protocol_data(const MYSQLND_PROTOCOL * protocol, unsigned int plugin_id)
+/* {{{ mysqlnd_plugin__get_plugin_protocol_data */
+static void **
+mysqlnd_plugin__get_plugin_protocol_data(const MYSQLND_PROTOCOL_PAYLOAD_DECODER_FACTORY * factory, const unsigned int plugin_id)
{
- DBG_ENTER("_mysqlnd_plugin_get_plugin_protocol_data");
+ DBG_ENTER("mysqlnd_plugin__get_plugin_protocol_data");
DBG_INF_FMT("plugin_id=%u", plugin_id);
- if (!protocol || plugin_id >= mysqlnd_plugin_count()) {
+ if (!factory || plugin_id >= mysqlnd_plugin_count()) {
return NULL;
}
- DBG_RETURN((void *)((char *)protocol + sizeof(MYSQLND_PROTOCOL) + plugin_id * sizeof(void *)));
+ DBG_RETURN((void *)((char *)factory + sizeof(MYSQLND_PROTOCOL_PAYLOAD_DECODER_FACTORY) + plugin_id * sizeof(void *)));
}
/* }}} */
-/* {{{ _mysqlnd_plugin_get_plugin_stmt_data */
-PHPAPI void ** _mysqlnd_plugin_get_plugin_stmt_data(const MYSQLND_STMT * stmt, unsigned int plugin_id)
+/* {{{ mysqlnd_plugin__get_plugin_stmt_data */
+static void **
+mysqlnd_plugin__get_plugin_stmt_data(const MYSQLND_STMT * stmt, const unsigned int plugin_id)
{
- DBG_ENTER("_mysqlnd_plugin_get_plugin_stmt_data");
+ DBG_ENTER("mysqlnd_plugin__get_plugin_stmt_data");
DBG_INF_FMT("plugin_id=%u", plugin_id);
if (!stmt || plugin_id >= mysqlnd_plugin_count()) {
return NULL;
@@ -134,160 +141,314 @@ PHPAPI void ** _mysqlnd_plugin_get_plugin_stmt_data(const MYSQLND_STMT * stmt, u
/* }}} */
-/* {{{ _mysqlnd_plugin_get_plugin_net_data */
-PHPAPI void ** _mysqlnd_plugin_get_plugin_net_data(const MYSQLND_NET * net, unsigned int plugin_id)
+/* {{{ mysqlnd_plugin__get_plugin_pfc_data */
+static void **
+mysqlnd_plugin__get_plugin_pfc_data(const MYSQLND_PFC * pfc, const unsigned int plugin_id)
{
- DBG_ENTER("_mysqlnd_plugin_get_plugin_net_data");
+ DBG_ENTER("mysqlnd_plugin__get_plugin_pfc_data");
DBG_INF_FMT("plugin_id=%u", plugin_id);
- if (!net || plugin_id >= mysqlnd_plugin_count()) {
+ if (!pfc || plugin_id >= mysqlnd_plugin_count()) {
return NULL;
}
- DBG_RETURN((void *)((char *)net + sizeof(MYSQLND_NET) + plugin_id * sizeof(void *)));
+ DBG_RETURN((void *)((char *)pfc + sizeof(MYSQLND_PFC) + plugin_id * sizeof(void *)));
}
/* }}} */
+/* {{{ _mysqlnd_plugin__get_plugin_vio_data */
+static void **
+mysqlnd_plugin__get_plugin_vio_data(const MYSQLND_VIO * vio, const unsigned int plugin_id)
+{
+ DBG_ENTER("_mysqlnd_plugin__get_plugin_vio_data");
+ DBG_INF_FMT("plugin_id=%u", plugin_id);
+ if (!vio || plugin_id >= mysqlnd_plugin_count()) {
+ return NULL;
+ }
+ DBG_RETURN((void *)((char *)vio + sizeof(MYSQLND_VIO) + plugin_id * sizeof(void *)));
+}
+/* }}} */
-/* {{{ mysqlnd_conn_get_methods */
-PHPAPI struct st_mysqlnd_conn_methods *
-mysqlnd_conn_get_methods()
+struct st_mysqlnd_plugin__plugin_area_getters mysqlnd_plugin_area_getters =
{
- return mysqlnd_conn_methods;
+ mysqlnd_plugin__get_plugin_connection_data,
+ mysqlnd_plugin__get_plugin_connection_data_data,
+ mysqlnd_plugin__get_plugin_result_data,
+ mysqlnd_plugin__get_plugin_result_unbuffered_data,
+ mysqlnd_plugin__get_plugin_result_buffered_data_zval,
+ mysqlnd_plugin__get_plugin_result_buffered_data_c,
+ mysqlnd_plugin__get_plugin_stmt_data,
+ mysqlnd_plugin__get_plugin_protocol_data,
+ mysqlnd_plugin__get_plugin_pfc_data,
+ mysqlnd_plugin__get_plugin_vio_data,
+};
+
+
+
+/* {{{ _mysqlnd_object_factory_get_methods */
+static MYSQLND_CLASS_METHODS_TYPE(mysqlnd_object_factory) *
+_mysqlnd_object_factory_get_methods()
+{
+ return &MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_object_factory);
}
/* }}} */
/* {{{ mysqlnd_conn_set_methods */
-PHPAPI void mysqlnd_conn_set_methods(struct st_mysqlnd_conn_methods *methods)
+static void
+_mysqlnd_object_factory_set_methods(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_object_factory) *methods)
+{
+ MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_object_factory) = *methods;
+}
+/* }}} */
+
+
+/* {{{ _mysqlnd_conn_get_methods */
+static MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn) *
+_mysqlnd_conn_get_methods()
+{
+ return mysqlnd_conn_methods;
+}
+/* }}} */
+
+/* {{{ _mysqlnd_conn_set_methods */
+static void
+_mysqlnd_conn_set_methods(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn) *methods)
{
mysqlnd_conn_methods = methods;
}
/* }}} */
-/* {{{ mysqlnd_conn_get_methods */
-PHPAPI struct st_mysqlnd_conn_data_methods *
-mysqlnd_conn_data_get_methods()
+/* {{{ _mysqlnd_conn_data_get_methods */
+static MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn_data) *
+_mysqlnd_conn_data_get_methods()
{
return mysqlnd_conn_data_methods;
}
/* }}} */
-/* {{{ mysqlnd_conn_set_methods */
-PHPAPI void mysqlnd_conn_data_set_methods(struct st_mysqlnd_conn_data_methods * methods)
+/* {{{ _mysqlnd_conn_data_set_methods */
+static void
+_mysqlnd_conn_data_set_methods(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn_data) * methods)
{
mysqlnd_conn_data_methods = methods;
}
/* }}} */
-/* {{{ mysqlnd_result_get_methods */
-PHPAPI struct st_mysqlnd_res_methods *
-mysqlnd_result_get_methods()
+/* {{{ _mysqlnd_result_get_methods */
+static MYSQLND_CLASS_METHODS_TYPE(mysqlnd_res) *
+_mysqlnd_result_get_methods()
{
return &MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_res);
}
/* }}} */
-/* {{{ mysqlnd_result_set_methods */
-PHPAPI void
-mysqlnd_result_set_methods(struct st_mysqlnd_res_methods * methods)
+/* {{{ _mysqlnd_result_set_methods */
+static void
+_mysqlnd_result_set_methods(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_res) * methods)
{
MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_res) = *methods;
}
/* }}} */
-/* {{{ mysqlnd_result_unbuffered_get_methods */
-PHPAPI struct st_mysqlnd_result_unbuffered_methods *
-mysqlnd_result_unbuffered_get_methods()
+/* {{{ _mysqlnd_result_unbuffered_get_methods */
+static MYSQLND_CLASS_METHODS_TYPE(mysqlnd_result_unbuffered) *
+_mysqlnd_result_unbuffered_get_methods()
{
return &MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_result_unbuffered);
}
/* }}} */
-/* {{{ mysqlnd_result_unbuffered_set_methods */
-PHPAPI void
-mysqlnd_result_unbuffered_set_methods(struct st_mysqlnd_result_unbuffered_methods * methods)
+/* {{{ _mysqlnd_result_unbuffered_set_methods */
+static void
+_mysqlnd_result_unbuffered_set_methods(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_result_unbuffered) * methods)
{
MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_result_unbuffered) = *methods;
}
/* }}} */
-/* {{{ mysqlnd_result_buffered_get_methods */
-PHPAPI struct st_mysqlnd_result_buffered_methods *
-mysqlnd_result_buffered_get_methods()
+/* {{{ _mysqlnd_result_buffered_get_methods */
+static MYSQLND_CLASS_METHODS_TYPE(mysqlnd_result_buffered) *
+_mysqlnd_result_buffered_get_methods()
{
return &MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_result_buffered);
}
/* }}} */
-/* {{{ mysqlnd_result_buffered_set_methods */
-PHPAPI void
-mysqlnd_result_buffered_set_methods(struct st_mysqlnd_result_buffered_methods * methods)
+/* {{{ _mysqlnd_result_buffered_set_methods */
+static void
+_mysqlnd_result_buffered_set_methods(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_result_buffered) * methods)
{
MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_result_buffered) = *methods;
}
/* }}} */
-/* {{{ mysqlnd_stmt_get_methods */
-PHPAPI struct st_mysqlnd_stmt_methods *
-mysqlnd_stmt_get_methods()
+/* {{{ _mysqlnd_stmt_get_methods */
+static MYSQLND_CLASS_METHODS_TYPE(mysqlnd_stmt) *
+_mysqlnd_stmt_get_methods()
{
return mysqlnd_stmt_methods;
}
/* }}} */
-/* {{{ mysqlnd_stmt_set_methods */
-PHPAPI void
-mysqlnd_stmt_set_methods(struct st_mysqlnd_stmt_methods *methods)
+/* {{{ _mysqlnd_stmt_set_methods */
+static void
+_mysqlnd_stmt_set_methods(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_stmt) *methods)
{
mysqlnd_stmt_methods = methods;
}
/* }}} */
-/* {{{ mysqlnd_protocol_get_methods */
-PHPAPI struct st_mysqlnd_protocol_methods *
-mysqlnd_protocol_get_methods()
+/* {{{ _mysqlnd_protocol_payload_decoder_factory_get_methods */
+static MYSQLND_CLASS_METHODS_TYPE(mysqlnd_protocol_payload_decoder_factory) *
+_mysqlnd_protocol_payload_decoder_factory_get_methods()
{
- return &MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_protocol);
+ return &MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_protocol_payload_decoder_factory);
}
/* }}} */
-/* {{{ mysqlnd_protocol_set_methods */
-PHPAPI void
-mysqlnd_protocol_set_methods(struct st_mysqlnd_protocol_methods * methods)
+/* {{{ _mysqlnd_protocol_payload_decoder_factory_set_methods */
+static void
+_mysqlnd_protocol_payload_decoder_factory_set_methods(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_protocol_payload_decoder_factory) * methods)
{
- MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_protocol) = *methods;
+ MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_protocol_payload_decoder_factory) = *methods;
}
/* }}} */
-/* {{{ mysqlnd_net_get_methods */
-PHPAPI struct st_mysqlnd_net_methods *
-mysqlnd_net_get_methods()
+/* {{{ _mysqlnd_pfc_get_methods */
+static MYSQLND_CLASS_METHODS_TYPE(mysqlnd_protocol_packet_frame_codec) *
+_mysqlnd_pfc_get_methods()
{
- return &MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_net);
+ return &MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_protocol_packet_frame_codec);
}
/* }}} */
-/* {{{ mysqlnd_net_set_methods */
-PHPAPI void
-mysqlnd_net_set_methods(struct st_mysqlnd_net_methods * methods)
+/* {{{ _mysqlnd_pfc_set_methods */
+static void
+_mysqlnd_pfc_set_methods(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_protocol_packet_frame_codec) * methods)
{
- MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_net) = *methods;
+ MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_protocol_packet_frame_codec) = *methods;
}
/* }}} */
+/* {{{ _mysqlnd_vio_get_methods */
+static MYSQLND_CLASS_METHODS_TYPE(mysqlnd_vio) *
+_mysqlnd_vio_get_methods()
+{
+ return &MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_vio);
+}
+/* }}} */
+
+
+/* {{{ _mysqlnd_vio_set_methods */
+static void
+_mysqlnd_vio_set_methods(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_vio) * methods)
+{
+ MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_vio) = *methods;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_command_factory_get */
+static func_mysqlnd__command_factory
+_mysqlnd_command_factory_get()
+{
+ return mysqlnd_command_factory;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_command_factory_set */
+static void
+_mysqlnd_command_factory_set(func_mysqlnd__command_factory factory)
+{
+ mysqlnd_command_factory = factory;
+}
+/* }}} */
+
+
+/* {{{ _mysqlnd_error_info_get_methods */
+static MYSQLND_CLASS_METHODS_TYPE(mysqlnd_error_info) *
+_mysqlnd_error_info_get_methods()
+{
+ return &MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_error_info);
+}
+/* }}} */
+
+
+/* {{{ _mysqlnd_error_info_set_methods */
+static void
+_mysqlnd_error_info_set_methods(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_error_info) *methods)
+{
+ MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_error_info) = *methods;
+}
+/* }}} */
+
+
+struct st_mysqlnd_plugin_methods_xetters mysqlnd_plugin_methods_xetters =
+{
+ {
+ _mysqlnd_object_factory_get_methods,
+ _mysqlnd_object_factory_set_methods
+ },
+ {
+ _mysqlnd_conn_get_methods,
+ _mysqlnd_conn_set_methods,
+ },
+ {
+ _mysqlnd_conn_data_get_methods,
+ _mysqlnd_conn_data_set_methods,
+ },
+ {
+ _mysqlnd_result_get_methods,
+ _mysqlnd_result_set_methods,
+ },
+ {
+ _mysqlnd_result_unbuffered_get_methods,
+ _mysqlnd_result_unbuffered_set_methods,
+ },
+ {
+ _mysqlnd_result_buffered_get_methods,
+ _mysqlnd_result_buffered_set_methods,
+ },
+ {
+ _mysqlnd_stmt_get_methods,
+ _mysqlnd_stmt_set_methods,
+ },
+ {
+ _mysqlnd_protocol_payload_decoder_factory_get_methods,
+ _mysqlnd_protocol_payload_decoder_factory_set_methods,
+ },
+ {
+ _mysqlnd_pfc_get_methods,
+ _mysqlnd_pfc_set_methods,
+ },
+ {
+ _mysqlnd_vio_get_methods,
+ _mysqlnd_vio_set_methods,
+ },
+ {
+ _mysqlnd_error_info_get_methods,
+ _mysqlnd_error_info_set_methods,
+ },
+ {
+ _mysqlnd_command_factory_get,
+ _mysqlnd_command_factory_set,
+ },
+};
+
/*
* Local variables:
* tab-width: 4
diff --git a/ext/mysqlnd/mysqlnd_ext_plugin.h b/ext/mysqlnd/mysqlnd_ext_plugin.h
index d20868a4a1..ecab5e8fc6 100644
--- a/ext/mysqlnd/mysqlnd_ext_plugin.h
+++ b/ext/mysqlnd/mysqlnd_ext_plugin.h
@@ -13,66 +13,155 @@
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
| Authors: Andrey Hristov <andrey@php.net> |
+ | Johannes Schlüter <johannes@php.net> |
| Ulf Wendel <uw@php.net> |
- | Georg Richter <georg@php.net> |
+----------------------------------------------------------------------+
*/
#ifndef MYSQLND_EXT_PLUGIN_H
#define MYSQLND_EXT_PLUGIN_H
-PHPAPI void ** _mysqlnd_plugin_get_plugin_connection_data(const MYSQLND * conn, unsigned int plugin_id);
-#define mysqlnd_plugin_get_plugin_connection_data(c, p_id) _mysqlnd_plugin_get_plugin_connection_data((c), (p_id))
-
-PHPAPI void ** _mysqlnd_plugin_get_plugin_connection_data_data(const MYSQLND_CONN_DATA * conn, unsigned int plugin_id);
-#define mysqlnd_plugin_get_plugin_connection_data_data(c, p_id) _mysqlnd_plugin_get_plugin_connection_data_data((c), (p_id))
-
-PHPAPI void ** _mysqlnd_plugin_get_plugin_result_data(const MYSQLND_RES * result, unsigned int plugin_id);
-#define mysqlnd_plugin_get_plugin_result_data(r, p_id) _mysqlnd_plugin_get_plugin_result_data((r), (p_id))
-
-PHPAPI void ** _mysqlnd_plugin_get_plugin_result_unbuffered_data(const MYSQLND_RES_UNBUFFERED * result, unsigned int plugin_id);
-#define mysqlnd_plugin_get_plugin_result_unbuffered_data(r, p_id) _mysqlnd_plugin_get_plugin_result_unbuffered_data((r), (p_id))
-
-PHPAPI void ** _mysqlnd_plugin_get_plugin_result_buffered_data_zval(const MYSQLND_RES_BUFFERED_ZVAL * result, unsigned int plugin_id);
-#define mysqlnd_plugin_get_plugin_result_buffered_data_zval(r, p_id) _mysqlnd_plugin_get_plugin_result_buffered_data_zval((r), (p_id))
-
-PHPAPI void ** _mysqlnd_plugin_get_plugin_result_buffered_data_c(const MYSQLND_RES_BUFFERED_C * result, unsigned int plugin_id);
-#define mysqlnd_plugin_get_plugin_result_buffered_data_c(r, p_id) _mysqlnd_plugin_get_plugin_result_buffered_data_c((r), (p_id))
-
-PHPAPI void ** _mysqlnd_plugin_get_plugin_stmt_data(const MYSQLND_STMT * stmt, unsigned int plugin_id);
-#define mysqlnd_plugin_get_plugin_stmt_data(s, p_id) _mysqlnd_plugin_get_plugin_stmt_data((s), (p_id))
-
-PHPAPI void ** _mysqlnd_plugin_get_plugin_protocol_data(const MYSQLND_PROTOCOL * protocol, unsigned int plugin_id);
-#define mysqlnd_plugin_get_plugin_protocol_data(p, p_id) _mysqlnd_plugin_get_plugin_protocol_data((p), (p_id))
-
-PHPAPI void ** _mysqlnd_plugin_get_plugin_net_data(const MYSQLND_NET * net, unsigned int plugin_id);
-#define mysqlnd_plugin_get_plugin_net_data(n, p_id) _mysqlnd_plugin_get_plugin_net_data((n), (p_id))
-
-
-PHPAPI struct st_mysqlnd_conn_methods * mysqlnd_conn_get_methods();
-PHPAPI void mysqlnd_conn_set_methods(struct st_mysqlnd_conn_methods * methods);
-
-PHPAPI struct st_mysqlnd_conn_data_methods * mysqlnd_conn_data_get_methods();
-PHPAPI void mysqlnd_conn_data_set_methods(struct st_mysqlnd_conn_data_methods * methods);
-
-PHPAPI struct st_mysqlnd_res_methods * mysqlnd_result_get_methods();
-PHPAPI void mysqlnd_result_set_methods(struct st_mysqlnd_res_methods * methods);
-
-PHPAPI struct st_mysqlnd_result_unbuffered_methods * mysqlnd_result_unbuffered_get_methods();
-PHPAPI void mysqlnd_result_unbuffered_set_methods(struct st_mysqlnd_result_unbuffered_methods * methods);
-
-PHPAPI struct st_mysqlnd_result_buffered_methods * mysqlnd_result_buffered_get_methods();
-PHPAPI void mysqlnd_result_buffered_set_methods(struct st_mysqlnd_result_buffered_methods * methods);
-
-PHPAPI struct st_mysqlnd_stmt_methods * mysqlnd_stmt_get_methods();
-PHPAPI void mysqlnd_stmt_set_methods(struct st_mysqlnd_stmt_methods * methods);
-
-PHPAPI struct st_mysqlnd_protocol_methods * mysqlnd_protocol_get_methods();
-PHPAPI void mysqlnd_protocol_set_methods(struct st_mysqlnd_protocol_methods * methods);
-
-PHPAPI struct st_mysqlnd_net_methods * mysqlnd_net_get_methods();
-PHPAPI void mysqlnd_net_set_methods(struct st_mysqlnd_net_methods * methods);
-
+struct st_mysqlnd_plugin__plugin_area_getters
+{
+ void ** (*get_connection_area)(const MYSQLND * conn, const unsigned int plugin_id);
+ void ** (*get_connection_data_area)(const MYSQLND_CONN_DATA * conn, const unsigned int plugin_id);
+ void ** (*get_result_area)(const MYSQLND_RES * result, const unsigned int plugin_id);
+ void ** (*get_unbuffered_area)(const MYSQLND_RES_UNBUFFERED * result, const unsigned int plugin_id);
+ void ** (*get_result_buffered_area)(const MYSQLND_RES_BUFFERED_ZVAL * result, const unsigned int plugin_id);
+ void ** (*get_result_buffered_aread_c)(const MYSQLND_RES_BUFFERED_C * result, const unsigned int plugin_id);
+ void ** (*get_stmt_area)(const MYSQLND_STMT * stmt, const unsigned int plugin_id);
+ void ** (*get_protocol_decoder_area)(const MYSQLND_PROTOCOL_PAYLOAD_DECODER_FACTORY * factory, const unsigned int plugin_id);
+ void ** (*get_pfc_area)(const MYSQLND_PFC * pfc, const unsigned int plugin_id);
+ void ** (*get_vio_area)(const MYSQLND_VIO * vio, const unsigned int plugin_id);
+};
+
+extern struct st_mysqlnd_plugin__plugin_area_getters mysqlnd_plugin_area_getters;
+
+#define mysqlnd_plugin_get_plugin_connection_data(c, p_id) mysqlnd_plugin_area_getters.get_connection_area((c), (p_id))
+#define mysqlnd_plugin_get_plugin_connection_data_data(c, p_id) mysqlnd_plugin_area_getters.get_connection_data_area((c), (p_id))
+#define mysqlnd_plugin_get_plugin_result_data(res, p_id) mysqlnd_plugin_area_getters.get_result_area((res), (p_id))
+#define mysqlnd_plugin_get_plugin_result_unbuffered_data(res, p_id) mysqlnd_plugin_area_getters.get_unbuffered_area((res), (p_id))
+#define mysqlnd_plugin_get_plugin_result_buffered_data_zval(res, p_id) mysqlnd_plugin_area_getters.get_result_buffered_area((res), (p_id))
+#define mysqlnd_plugin_get_plugin_result_buffered_data_c(res, p_id) mysqlnd_plugin_area_getters.get_result_buffered_aread_c((res), (p_id))
+#define mysqlnd_plugin_get_plugin_stmt_data(stmt, p_id) mysqlnd_plugin_area_getters.get_stmt_area((stmt), (p_id))
+#define mysqlnd_plugin_get_plugin_protocol_data(proto, p_id) mysqlnd_plugin_area_getters.get_protocol_decoder_area((proto), (p_id))
+#define mysqlnd_plugin_get_plugin_pfc_data(pfc, p_id) mysqlnd_plugin_area_getters.get_pfc_area((pfc), (p_id))
+#define mysqlnd_plugin_get_plugin_vio_data(vio, p_id) mysqlnd_plugin_area_getters.get_pfc_area((vio), (p_id))
+
+
+struct st_mysqlnd_plugin_methods_xetters
+{
+ struct st_mnd_object_factory_xetters
+ {
+ MYSQLND_CLASS_METHODS_TYPE(mysqlnd_object_factory) * (*get)();
+ void (*set)(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_object_factory) *methods);
+ } object_factory;
+
+ struct st_mnd_connection_xetters
+ {
+ MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn) * (*get)();
+ void (*set)(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn) *methods);
+ } connection;
+
+ struct st_mnd_connection_data_xetters
+ {
+ MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn_data) * (*get)();
+ void (*set)(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn_data) *methods);
+ } connection_data;
+
+ struct st_mnd_result_xetters
+ {
+ MYSQLND_CLASS_METHODS_TYPE(mysqlnd_res) * (*get)();
+ void (*set)(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_res) *methods);
+ } result;
+
+ struct st_mnd_unbuffered_result_xetters
+ {
+ MYSQLND_CLASS_METHODS_TYPE(mysqlnd_result_unbuffered) * (*get)();
+ void (*set)(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_result_unbuffered) *methods);
+ } unbuffered_result;
+
+ struct st_mnd_buffered_result_xetters
+ {
+ MYSQLND_CLASS_METHODS_TYPE(mysqlnd_result_buffered)* (*get)();
+ void (*set)(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_result_buffered) *methods);
+ } buffered_result;
+
+ struct st_mnd_stmt_xetters
+ {
+ MYSQLND_CLASS_METHODS_TYPE(mysqlnd_stmt) * (*get)();
+ void (*set)(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_stmt) * methods);
+ } statement;
+
+ struct st_mnd_protocol_xetters
+ {
+ MYSQLND_CLASS_METHODS_TYPE(mysqlnd_protocol_payload_decoder_factory)* (*get)();
+ void (*set)(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_protocol_payload_decoder_factory) *methods);
+ } protocol;
+
+ struct st_mnd_pfc_xetters
+ {
+ MYSQLND_CLASS_METHODS_TYPE(mysqlnd_protocol_packet_frame_codec) * (*get)();
+ void (*set)(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_protocol_packet_frame_codec) * methods);
+ } pfc;
+
+ struct st_mnd_vio_xetters
+ {
+ MYSQLND_CLASS_METHODS_TYPE(mysqlnd_vio) * (*get)();
+ void (*set)(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_vio) * methods);
+ } vio;
+
+ struct st_mnd_error_info_xetters
+ {
+ MYSQLND_CLASS_METHODS_TYPE(mysqlnd_error_info) * (*get)();
+ void (*set)(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_error_info) * methods);
+ } error_info;
+
+ struct st_mnd_command_factory_xetters
+ {
+ func_mysqlnd__command_factory (*get)();
+ void (*set)(func_mysqlnd__command_factory factory);
+ } command_factory;
+};
+
+extern struct st_mysqlnd_plugin_methods_xetters mysqlnd_plugin_methods_xetters;
+
+
+#define mysqlnd_object_factory_get_methods() mysqlnd_plugin_methods_xetters.object_factory.get()
+#define mysqlnd_object_factory_set_methods(m) mysqlnd_plugin_methods_xetters.object_factory.set((m))
+
+#define mysqlnd_conn_get_methods() mysqlnd_plugin_methods_xetters.connection.get()
+#define mysqlnd_conn_set_methods(m) mysqlnd_plugin_methods_xetters.connection.set((m))
+
+#define mysqlnd_conn_data_get_methods() mysqlnd_plugin_methods_xetters.connection_data.get()
+#define mysqlnd_conn_data_set_methods(m) mysqlnd_plugin_methods_xetters.connection_data.set((m))
+
+#define mysqlnd_result_get_methods() mysqlnd_plugin_methods_xetters.result.get()
+#define mysqlnd_result_set_methods(m) mysqlnd_plugin_methods_xetters.result.set((m))
+
+#define mysqlnd_result_unbuffered_get_methods() mysqlnd_plugin_methods_xetters.unbuffered_result.get()
+#define mysqlnd_result_unbuffered_set_methods(m) mysqlnd_plugin_methods_xetters.unbuffered_result.set((m))
+
+#define mysqlnd_result_buffered_get_methods() mysqlnd_plugin_methods_xetters.buffered_result.get()
+#define mysqlnd_result_buffered_set_methods(m) mysqlnd_plugin_methods_xetters.buffered_result.set((m))
+
+#define mysqlnd_stmt_get_methods() mysqlnd_plugin_methods_xetters.statement.get()
+#define mysqlnd_stmt_set_methods(m) mysqlnd_plugin_methods_xetters.statement.set((m))
+
+#define mysqlnd_protocol_get_methods() mysqlnd_plugin_methods_xetters.protocol.get()
+#define mysqlnd_protocol_set_methods(m) mysqlnd_plugin_methods_xetters.protocol.set((m))
+
+#define mysqlnd_pfc_get_methods() mysqlnd_plugin_methods_xetters.pfc.get()
+#define mysqlnd_pfc_set_methods(m) mysqlnd_plugin_methods_xetters.pfc.set((m))
+
+#define mysqlnd_vio_get_methods() mysqlnd_plugin_methods_xetters.vio.get()
+#define mysqlnd_vio_set_methods(m) mysqlnd_plugin_methods_xetters.vio.set((m))
+
+#define mysqlnd_command_factory_get() mysqlnd_plugin_methods_xetters.command_factory.get()
+#define mysqlnd_command_factory_set(m) mysqlnd_plugin_methods_xetters.command_factory.set((m))
+
+#define mysqlnd_error_info_get_methods() mysqlnd_plugin_methods_xetters.error_info.get()
+#define mysqlnd_error_info_set_methods(m) mysqlnd_plugin_methods_xetters.error_info.set((m))
#endif /* MYSQLND_EXT_PLUGIN_H */
diff --git a/ext/mysqlnd/mysqlnd_libmysql_compat.h b/ext/mysqlnd/mysqlnd_libmysql_compat.h
index 6be504f70f..0556ef1d38 100644
--- a/ext/mysqlnd/mysqlnd_libmysql_compat.h
+++ b/ext/mysqlnd/mysqlnd_libmysql_compat.h
@@ -17,7 +17,6 @@
| Georg Richter <georg@php.net> |
+----------------------------------------------------------------------+
*/
-
#ifndef MYSQLND_LIBMYSQL_COMPAT_H
#define MYSQLND_LIBMYSQL_COMPAT_H
@@ -58,11 +57,10 @@
#define mysql_field_count(r) mysqlnd_field_count((r))
#define mysql_field_seek(r,o) mysqlnd_field_seek((r), (o))
#define mysql_field_tell(r) mysqlnd_field_tell((r))
-#define mysql_init(a) mysqlnd_init((a))
+#define mysql_init(a) mysqlnd_connection_init((a), false)
#define mysql_insert_id(r) mysqlnd_insert_id((r))
#define mysql_kill(r,n) mysqlnd_kill((r), (n))
#define mysql_list_dbs(c, wild) mysqlnd_list_dbs((c), (wild))
-#define mysql_list_fields(c, tab, wild) mysqlnd_list_fields((c), (tab), (wild))
#define mysql_list_processes(c) mysqlnd_list_processes((c))
#define mysql_list_tables(c, wild) mysqlnd_list_tables((c), (wild))
#define mysql_more_results(r) mysqlnd_more_results((r))
diff --git a/ext/mysqlnd/mysqlnd_loaddata.c b/ext/mysqlnd/mysqlnd_loaddata.c
index 0edffdefaf..14dee737bf 100644
--- a/ext/mysqlnd/mysqlnd_loaddata.c
+++ b/ext/mysqlnd/mysqlnd_loaddata.c
@@ -17,9 +17,7 @@
| Georg Richter <georg@php.net> |
+----------------------------------------------------------------------+
*/
-
#include "php.h"
-#include "php_globals.h"
#include "mysqlnd.h"
#include "mysqlnd_wireprotocol.h"
#include "mysqlnd_priv.h"
@@ -136,12 +134,12 @@ mysqlnd_local_infile_default(MYSQLND_CONN_DATA * conn)
/* }}} */
-static const char *lost_conn = "Lost connection to MySQL server during LOAD DATA of local file";
+static const char *lost_conn = "Lost connection to MySQL server during LOAD DATA of a local file";
/* {{{ mysqlnd_handle_local_infile */
enum_func_status
-mysqlnd_handle_local_infile(MYSQLND_CONN_DATA * conn, const char * filename, zend_bool * is_warning)
+mysqlnd_handle_local_infile(MYSQLND_CONN_DATA * conn, const char * const filename, zend_bool * is_warning)
{
zend_uchar *buf = NULL;
zend_uchar empty_packet[MYSQLND_HEADER_SIZE];
@@ -151,14 +149,15 @@ mysqlnd_handle_local_infile(MYSQLND_CONN_DATA * conn, const char * filename, zen
int bufsize;
size_t ret;
MYSQLND_INFILE infile;
- MYSQLND_NET * net = conn->net;
+ MYSQLND_PFC * net = conn->protocol_frame_codec;
+ MYSQLND_VIO * vio = conn->vio;
DBG_ENTER("mysqlnd_handle_local_infile");
if (!(conn->options->flags & CLIENT_LOCAL_FILES)) {
php_error_docref(NULL, E_WARNING, "LOAD DATA LOCAL INFILE forbidden");
/* write empty packet to server */
- ret = net->data->m.send_ex(net, empty_packet, 0, conn->stats, conn->error_info);
+ ret = net->data->m.send(net, vio, empty_packet, 0, conn->stats, conn->error_info);
*is_warning = TRUE;
goto infile_error;
}
@@ -176,24 +175,24 @@ mysqlnd_handle_local_infile(MYSQLND_CONN_DATA * conn, const char * filename, zen
*is_warning = TRUE;
/* error occurred */
tmp_error_no = infile.local_infile_error(info, tmp_buf, sizeof(tmp_buf));
- SET_CLIENT_ERROR(*conn->error_info, tmp_error_no, UNKNOWN_SQLSTATE, tmp_buf);
+ SET_CLIENT_ERROR(conn->error_info, tmp_error_no, UNKNOWN_SQLSTATE, tmp_buf);
/* write empty packet to server */
- ret = net->data->m.send_ex(net, empty_packet, 0, conn->stats, conn->error_info);
+ ret = net->data->m.send(net, vio, empty_packet, 0, conn->stats, conn->error_info);
goto infile_error;
}
/* read data */
while ((bufsize = infile.local_infile_read (info, buf + MYSQLND_HEADER_SIZE, buflen - MYSQLND_HEADER_SIZE)) > 0) {
- if ((ret = net->data->m.send_ex(net, buf, bufsize, conn->stats, conn->error_info)) == 0) {
+ if ((ret = net->data->m.send(net, vio, buf, bufsize, conn->stats, conn->error_info)) == 0) {
DBG_ERR_FMT("Error during read : %d %s %s", CR_SERVER_LOST, UNKNOWN_SQLSTATE, lost_conn);
- SET_CLIENT_ERROR(*conn->error_info, CR_SERVER_LOST, UNKNOWN_SQLSTATE, lost_conn);
+ SET_CLIENT_ERROR(conn->error_info, CR_SERVER_LOST, UNKNOWN_SQLSTATE, lost_conn);
goto infile_error;
}
}
/* send empty packet for eof */
- if ((ret = net->data->m.send_ex(net, empty_packet, 0, conn->stats, conn->error_info)) == 0) {
- SET_CLIENT_ERROR(*conn->error_info, CR_SERVER_LOST, UNKNOWN_SQLSTATE, lost_conn);
+ if ((ret = net->data->m.send(net, vio, empty_packet, 0, conn->stats, conn->error_info)) == 0) {
+ SET_CLIENT_ERROR(conn->error_info, CR_SERVER_LOST, UNKNOWN_SQLSTATE, lost_conn);
goto infile_error;
}
@@ -204,7 +203,7 @@ mysqlnd_handle_local_infile(MYSQLND_CONN_DATA * conn, const char * filename, zen
*is_warning = TRUE;
DBG_ERR_FMT("Bufsize < 0, warning, %d %s %s", CR_SERVER_LOST, UNKNOWN_SQLSTATE, lost_conn);
tmp_error_no = infile.local_infile_error(info, tmp_buf, sizeof(tmp_buf));
- SET_CLIENT_ERROR(*conn->error_info, tmp_error_no, UNKNOWN_SQLSTATE, tmp_buf);
+ SET_CLIENT_ERROR(conn->error_info, tmp_error_no, UNKNOWN_SQLSTATE, tmp_buf);
goto infile_error;
}
@@ -212,7 +211,13 @@ mysqlnd_handle_local_infile(MYSQLND_CONN_DATA * conn, const char * filename, zen
infile_error:
/* get response from server and update upsert values */
- if (FAIL == conn->m->simple_command_handle_response(conn, PROT_OK_PACKET, FALSE, COM_QUERY, FALSE)) {
+ if (FAIL == conn->payload_decoder_factory->m.send_command_handle_response(
+ conn->payload_decoder_factory,
+ PROT_OK_PACKET, FALSE, COM_QUERY, FALSE,
+ conn->error_info,
+ conn->upsert_status,
+ &conn->last_message,
+ conn->persistent)) {
result = FAIL;
}
diff --git a/ext/mysqlnd/mysqlnd_net.c b/ext/mysqlnd/mysqlnd_net.c
index 56c9d30df1..111a4898d0 100644
--- a/ext/mysqlnd/mysqlnd_net.c
+++ b/ext/mysqlnd/mysqlnd_net.c
@@ -14,7 +14,6 @@
+----------------------------------------------------------------------+
| Authors: Andrey Hristov <andrey@php.net> |
| Ulf Wendel <uw@php.net> |
- | Georg Richter <georg@php.net> |
+----------------------------------------------------------------------+
*/
@@ -132,11 +131,7 @@ MYSQLND_METHOD(mysqlnd_net, open_pipe)(MYSQLND_NET * const net, const char * con
const zend_bool persistent,
MYSQLND_STATS * const conn_stats, MYSQLND_ERROR_INFO * const error_info)
{
-#if PHP_API_VERSION < 20100412
- unsigned int streams_options = ENFORCE_SAFE_MODE;
-#else
unsigned int streams_options = 0;
-#endif
dtor_func_t origin_dtor;
php_stream * net_stream = NULL;
@@ -172,11 +167,7 @@ MYSQLND_METHOD(mysqlnd_net, open_tcp_or_unix)(MYSQLND_NET * const net, const cha
const zend_bool persistent,
MYSQLND_STATS * const conn_stats, MYSQLND_ERROR_INFO * const error_info)
{
-#if PHP_API_VERSION < 20100412
- unsigned int streams_options = ENFORCE_SAFE_MODE;
-#else
unsigned int streams_options = 0;
-#endif
unsigned int streams_flags = STREAM_XPORT_CLIENT | STREAM_XPORT_CONNECT;
char * hashed_details = NULL;
int hashed_details_len = 0;
@@ -982,11 +973,7 @@ MYSQLND_METHOD(mysqlnd_net, enable_ssl)(MYSQLND_NET * const net)
php_stream_context_set_option(context, "ssl", "allow_self_signed", &verify_peer_zval);
}
}
-#if PHP_API_VERSION >= 20131106
- php_stream_context_set(net_stream, context);
-#else
php_stream_context_set(net_stream, context);
-#endif
if (php_stream_xport_crypto_setup(net_stream, STREAM_CRYPTO_METHOD_TLS_CLIENT, NULL) < 0 ||
php_stream_xport_crypto_enable(net_stream, 1) < 0)
{
@@ -1002,11 +989,7 @@ MYSQLND_METHOD(mysqlnd_net, enable_ssl)(MYSQLND_NET * const net)
of the context, which means usage of already freed memory, bad. Actually we don't need this
context anymore after we have enabled SSL on the connection. Thus it is very simple, we remove it.
*/
-#if PHP_API_VERSION >= 20131106
php_stream_context_set(net_stream, NULL);
-#else
- php_stream_context_set(net_stream, NULL);
-#endif
if (net->data->options.timeout_read) {
struct timeval tv;
diff --git a/ext/mysqlnd/mysqlnd_plugin.c b/ext/mysqlnd/mysqlnd_plugin.c
index 487bd8485c..b90081a0ef 100644
--- a/ext/mysqlnd/mysqlnd_plugin.c
+++ b/ext/mysqlnd/mysqlnd_plugin.c
@@ -14,7 +14,6 @@
+----------------------------------------------------------------------+
| Authors: Andrey Hristov <andrey@php.net> |
| Ulf Wendel <uw@php.net> |
- | Georg Richter <georg@php.net> |
+----------------------------------------------------------------------+
*/
diff --git a/ext/mysqlnd/mysqlnd_plugin.h b/ext/mysqlnd/mysqlnd_plugin.h
new file mode 100644
index 0000000000..f01c99579a
--- /dev/null
+++ b/ext/mysqlnd/mysqlnd_plugin.h
@@ -0,0 +1,41 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 7 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 2006-2016 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: Andrey Hristov <andrey@php.net> |
+ | Ulf Wendel <uw@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#ifndef MYSQLND_PLUGIN_H
+#define MYSQLND_PLUGIN_H
+
+
+void mysqlnd_plugin_subsystem_init(void);
+void mysqlnd_plugin_subsystem_end(void);
+
+void mysqlnd_register_builtin_authentication_plugins(void);
+
+void mysqlnd_example_plugin_register(void);
+
+#endif /* MYSQLND_PLUGIN_H */
+
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: noet sw=4 ts=4 fdm=marker
+ * vim<600: noet sw=4 ts=4
+ */
diff --git a/ext/mysqlnd/mysqlnd_priv.h b/ext/mysqlnd/mysqlnd_priv.h
index 82260c66c1..cea1bc68c7 100644
--- a/ext/mysqlnd/mysqlnd_priv.h
+++ b/ext/mysqlnd/mysqlnd_priv.h
@@ -14,219 +14,26 @@
+----------------------------------------------------------------------+
| Authors: Andrey Hristov <andrey@php.net> |
| Ulf Wendel <uw@php.net> |
- | Georg Richter <georg@php.net> |
+----------------------------------------------------------------------+
*/
#ifndef MYSQLND_PRIV_H
#define MYSQLND_PRIV_H
-
-#ifdef ZTS
-#include "TSRM.h"
-#endif
-
-#define MYSQLND_STR_W_LEN(str) str, (sizeof(str) - 1)
-
-#define MYSQLND_DEBUG_DUMP_TIME 1
-#define MYSQLND_DEBUG_DUMP_TRACE 2
-#define MYSQLND_DEBUG_DUMP_PID 4
-#define MYSQLND_DEBUG_DUMP_LINE 8
-#define MYSQLND_DEBUG_DUMP_FILE 16
-#define MYSQLND_DEBUG_DUMP_LEVEL 32
-#define MYSQLND_DEBUG_APPEND 64
-#define MYSQLND_DEBUG_FLUSH 128
-#define MYSQLND_DEBUG_TRACE_MEMORY_CALLS 256
-#define MYSQLND_DEBUG_PROFILE_CALLS 512
-
-
-/* Client Error codes */
-#define CR_UNKNOWN_ERROR 2000
-#define CR_CONNECTION_ERROR 2002
-#define CR_SERVER_GONE_ERROR 2006
-#define CR_OUT_OF_MEMORY 2008
-#define CR_SERVER_LOST 2013
-#define CR_COMMANDS_OUT_OF_SYNC 2014
-#define CR_CANT_FIND_CHARSET 2019
-#define CR_MALFORMED_PACKET 2027
-#define CR_NOT_IMPLEMENTED 2054
-#define CR_NO_PREPARE_STMT 2030
-#define CR_PARAMS_NOT_BOUND 2031
-#define CR_INVALID_PARAMETER_NO 2034
-#define CR_INVALID_BUFFER_USE 2035
-
-#define MYSQLND_EE_FILENOTFOUND 7890
-
-#define UNKNOWN_SQLSTATE "HY000"
-
-#define MAX_CHARSET_LEN 32
-
-
-#define SET_ERROR_AFF_ROWS(s) (s)->upsert_status->affected_rows = (uint64_t) ~0
-
-/* Error handling */
-#define SET_NEW_MESSAGE(buf, buf_len, message, len, persistent) \
- {\
- if ((buf)) { \
- mnd_pefree((buf), (persistent)); \
- } \
- if ((message)) { \
- (buf) = mnd_pestrndup((message), (len), (persistent)); \
- } else { \
- (buf) = NULL; \
- } \
- (buf_len) = (len); \
- }
-
-#define SET_EMPTY_MESSAGE(buf, buf_len, persistent) \
- {\
- if ((buf)) { \
- mnd_pefree((buf), (persistent)); \
- (buf) = NULL; \
- } \
- (buf_len) = 0; \
- }
-
-
-#define SET_EMPTY_ERROR(error_info) \
- { \
- (error_info).error_no = 0; \
- (error_info).error[0] = '\0'; \
- strlcpy((error_info).sqlstate, "00000", sizeof((error_info).sqlstate)); \
- if ((error_info).error_list) { \
- zend_llist_clean((error_info).error_list); \
- } \
- }
-
-
-#define SET_CLIENT_ERROR(error_info, a, b, c) \
-{ \
- if (0 == (a)) { \
- SET_EMPTY_ERROR((error_info)); \
- } else { \
- (error_info).error_no = (a); \
- strlcpy((error_info).sqlstate, (b), sizeof((error_info).sqlstate)); \
- strlcpy((error_info).error, (c), sizeof((error_info).error)); \
- if ((error_info).error_list) {\
- MYSQLND_ERROR_LIST_ELEMENT error_for_the_list = {0}; \
- \
- error_for_the_list.error_no = (a); \
- strlcpy(error_for_the_list.sqlstate, (b), sizeof(error_for_the_list.sqlstate)); \
- error_for_the_list.error = mnd_pestrdup((c), TRUE); \
- if (error_for_the_list.error) { \
- DBG_INF_FMT("adding error [%s] to the list", error_for_the_list.error); \
- zend_llist_add_element((error_info).error_list, &error_for_the_list); \
- } \
- } \
- } \
-}
-
-
-#define COPY_CLIENT_ERROR(error_info_to, error_info_from) \
- { \
- SET_CLIENT_ERROR((error_info_to), (error_info_from).error_no, (error_info_from).sqlstate, (error_info_from).error); \
- }
-
-
-#define SET_OOM_ERROR(error_info) SET_CLIENT_ERROR((error_info), CR_OUT_OF_MEMORY, UNKNOWN_SQLSTATE, mysqlnd_out_of_memory)
-
-
-#define SET_STMT_ERROR(stmt, a, b, c) SET_CLIENT_ERROR(*(stmt)->error_info, a, b, c)
-
-#define CONN_GET_STATE(c) (c)->m->get_state((c))
-#define CONN_SET_STATE(c, s) (c)->m->set_state((c), (s))
-
-/* PS stuff */
-typedef void (*ps_field_fetch_func)(zval * zv, const MYSQLND_FIELD * const field, unsigned int pack_len, zend_uchar ** row);
-struct st_mysqlnd_perm_bind {
- ps_field_fetch_func func;
- /* should be signed int */
- int pack_len;
- unsigned int php_type;
- zend_bool is_possibly_blob;
- zend_bool can_ret_as_str_in_uni;
-};
-
-extern struct st_mysqlnd_perm_bind mysqlnd_ps_fetch_functions[MYSQL_TYPE_LAST + 1];
-
-enum_func_status mysqlnd_stmt_fetch_row_buffered(MYSQLND_RES * result, void * param, unsigned int flags, zend_bool * fetched_anything);
-enum_func_status mysqlnd_fetch_stmt_row_cursor(MYSQLND_RES * result, void * param, unsigned int flags, zend_bool * fetched_anything);
-
-
-PHPAPI extern const char * const mysqlnd_old_passwd;
-PHPAPI extern const char * const mysqlnd_out_of_sync;
-PHPAPI extern const char * const mysqlnd_server_gone;
-PHPAPI extern const char * const mysqlnd_out_of_memory;
-
PHPAPI extern MYSQLND_CLASS_METHOD_TABLE_NAME_FORWARD(mysqlnd_object_factory);
PHPAPI extern MYSQLND_CLASS_METHOD_TABLE_NAME_FORWARD(mysqlnd_conn);
PHPAPI extern MYSQLND_CLASS_METHOD_TABLE_NAME_FORWARD(mysqlnd_conn_data);
PHPAPI extern MYSQLND_CLASS_METHOD_TABLE_NAME_FORWARD(mysqlnd_res);
PHPAPI extern MYSQLND_CLASS_METHOD_TABLE_NAME_FORWARD(mysqlnd_result_unbuffered);
PHPAPI extern MYSQLND_CLASS_METHOD_TABLE_NAME_FORWARD(mysqlnd_result_buffered);
-PHPAPI extern MYSQLND_CLASS_METHOD_TABLE_NAME_FORWARD(mysqlnd_protocol);
-PHPAPI extern MYSQLND_CLASS_METHOD_TABLE_NAME_FORWARD(mysqlnd_net);
-
-enum_func_status mysqlnd_handle_local_infile(MYSQLND_CONN_DATA * conn, const char * filename, zend_bool * is_warning);
-
-
-
-void _mysqlnd_init_ps_subsystem();/* This one is private, mysqlnd_library_init() will call it */
-void _mysqlnd_init_ps_fetch_subsystem();
-
-void ps_fetch_from_1_to_8_bytes(zval * zv, const MYSQLND_FIELD * const field, unsigned int pack_len, zend_uchar ** row, unsigned int byte_count);
-
-void mysqlnd_plugin_subsystem_init(void);
-void mysqlnd_plugin_subsystem_end(void);
-
-void mysqlnd_register_builtin_authentication_plugins(void);
-
-void mysqlnd_example_plugin_register(void);
-
-struct st_mysqlnd_packet_greet;
-struct st_mysqlnd_authentication_plugin;
-
-enum_func_status
-mysqlnd_auth_handshake(MYSQLND_CONN_DATA * conn,
- const char * const user,
- const char * const passwd,
- const size_t passwd_len,
- const char * const db,
- const size_t db_len,
- const MYSQLND_OPTIONS * const options,
- zend_ulong mysql_flags,
- unsigned int server_charset_no,
- zend_bool use_full_blown_auth_packet,
- const char * const auth_protocol,
- const zend_uchar * const auth_plugin_data,
- const size_t auth_plugin_data_len,
- char ** switch_to_auth_protocol,
- size_t * switch_to_auth_protocol_len,
- zend_uchar ** switch_to_auth_protocol_data,
- size_t * switch_to_auth_protocol_data_len
- );
-
-enum_func_status
-mysqlnd_auth_change_user(MYSQLND_CONN_DATA * const conn,
- const char * const user,
- const size_t user_len,
- const char * const passwd,
- const size_t passwd_len,
- const char * const db,
- const size_t db_len,
- const zend_bool silent,
- zend_bool use_full_blown_auth_packet,
- const char * const auth_protocol,
- zend_uchar * auth_plugin_data,
- size_t auth_plugin_data_len,
- char ** switch_to_auth_protocol,
- size_t * switch_to_auth_protocol_len,
- zend_uchar ** switch_to_auth_protocol_data,
- size_t * switch_to_auth_protocol_data_len
- );
+PHPAPI extern MYSQLND_CLASS_METHOD_TABLE_NAME_FORWARD(mysqlnd_protocol_payload_decoder_factory);
+PHPAPI extern MYSQLND_CLASS_METHOD_TABLE_NAME_FORWARD(mysqlnd_protocol_packet_frame_codec);
+PHPAPI extern MYSQLND_CLASS_METHOD_TABLE_NAME_FORWARD(mysqlnd_vio);
+PHPAPI extern MYSQLND_CLASS_METHOD_TABLE_NAME_FORWARD(mysqlnd_upsert_status);
+PHPAPI extern MYSQLND_CLASS_METHOD_TABLE_NAME_FORWARD(mysqlnd_error_info);
+enum_func_status mysqlnd_handle_local_infile(MYSQLND_CONN_DATA * conn, const char * const filename, zend_bool * is_warning);
#endif /* MYSQLND_PRIV_H */
-
/*
* Local variables:
* tab-width: 4
diff --git a/ext/mysqlnd/mysqlnd_protocol_frame_codec.c b/ext/mysqlnd/mysqlnd_protocol_frame_codec.c
new file mode 100644
index 0000000000..36b9d77958
--- /dev/null
+++ b/ext/mysqlnd/mysqlnd_protocol_frame_codec.c
@@ -0,0 +1,508 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 7 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 2006-2016 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: Andrey Hristov <andrey@php.net> |
+ | Ulf Wendel <uw@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#include "php.h"
+#include "mysqlnd.h"
+#include "mysqlnd_connection.h"
+#include "mysqlnd_priv.h"
+#include "mysqlnd_read_buffer.h"
+#include "mysqlnd_wireprotocol.h"
+#include "mysqlnd_statistics.h"
+#include "mysqlnd_debug.h"
+#ifdef MYSQLND_COMPRESSION_ENABLED
+#include <zlib.h>
+#endif
+
+
+/* {{{ mysqlnd_pfc::reset */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_pfc, reset)(MYSQLND_PFC * const pfc, MYSQLND_STATS * const conn_stats, MYSQLND_ERROR_INFO * const error_info)
+{
+ DBG_ENTER("mysqlnd_pfc::reset");
+ pfc->data->packet_no = pfc->data->compressed_envelope_packet_no = 0;
+ DBG_RETURN(PASS);
+}
+/* }}} */
+
+
+/* We assume that MYSQLND_HEADER_SIZE is 4 bytes !! */
+#define COPY_HEADER(T,A) do { \
+ *(((char *)(T))) = *(((char *)(A)));\
+ *(((char *)(T))+1) = *(((char *)(A))+1);\
+ *(((char *)(T))+2) = *(((char *)(A))+2);\
+ *(((char *)(T))+3) = *(((char *)(A))+3); } while (0)
+#define STORE_HEADER_SIZE(safe_storage, buffer) COPY_HEADER((safe_storage), (buffer))
+#define RESTORE_HEADER_SIZE(buffer, safe_storage) STORE_HEADER_SIZE((safe_storage), (buffer))
+
+
+/* {{{ mysqlnd_pfc::send */
+/*
+ IMPORTANT : It's expected that buffer has place in the beginning for MYSQLND_HEADER_SIZE !!!!
+ This is done for performance reasons in the caller of this function.
+ Otherwise we will have to do send two TCP packets, or do new alloc and memcpy.
+ Neither are quick, thus the clients of this function are obligated to do
+ what they are asked for.
+
+ `count` is actually the length of the payload data. Thus :
+ count + MYSQLND_HEADER_SIZE = sizeof(buffer) (not the pointer but the actual buffer)
+*/
+static size_t
+MYSQLND_METHOD(mysqlnd_pfc, send)(MYSQLND_PFC * const pfc, MYSQLND_VIO * const vio, zend_uchar * const buffer, const size_t count,
+ MYSQLND_STATS * const conn_stats, MYSQLND_ERROR_INFO * const error_info)
+{
+ zend_uchar safe_buf[((MYSQLND_HEADER_SIZE) + (sizeof(zend_uchar)) - 1) / (sizeof(zend_uchar))];
+ zend_uchar * safe_storage = safe_buf;
+ size_t bytes_sent, packets_sent = 1;
+ size_t left = count;
+ zend_uchar * p = (zend_uchar *) buffer;
+ zend_uchar * compress_buf = NULL;
+ size_t to_be_sent;
+
+ DBG_ENTER("mysqlnd_pfc::send");
+ DBG_INF_FMT("count=" MYSQLND_SZ_T_SPEC " compression=%u", count, pfc->data->compressed);
+
+ if (pfc->data->compressed == TRUE) {
+ size_t comp_buf_size = MYSQLND_HEADER_SIZE + COMPRESSED_HEADER_SIZE + MYSQLND_HEADER_SIZE + MIN(left, MYSQLND_MAX_PACKET_SIZE);
+ DBG_INF_FMT("compress_buf_size="MYSQLND_SZ_T_SPEC, comp_buf_size);
+ compress_buf = mnd_emalloc(comp_buf_size);
+ }
+
+ do {
+ to_be_sent = MIN(left, MYSQLND_MAX_PACKET_SIZE);
+ DBG_INF_FMT("to_be_sent=%u", to_be_sent);
+ DBG_INF_FMT("packets_sent=%u", packets_sent);
+ DBG_INF_FMT("compressed_envelope_packet_no=%u", pfc->data->compressed_envelope_packet_no);
+ DBG_INF_FMT("packet_no=%u", pfc->data->packet_no);
+#ifdef MYSQLND_COMPRESSION_ENABLED
+ if (pfc->data->compressed == TRUE) {
+ /* here we need to compress the data and then write it, first comes the compressed header */
+ size_t tmp_complen = to_be_sent;
+ size_t payload_size;
+ zend_uchar * uncompressed_payload = p; /* should include the header */
+
+ STORE_HEADER_SIZE(safe_storage, uncompressed_payload);
+ int3store(uncompressed_payload, to_be_sent);
+ int1store(uncompressed_payload + 3, pfc->data->packet_no);
+ if (PASS == pfc->data->m.encode((compress_buf + COMPRESSED_HEADER_SIZE + MYSQLND_HEADER_SIZE), &tmp_complen,
+ uncompressed_payload, to_be_sent + MYSQLND_HEADER_SIZE))
+ {
+ int3store(compress_buf + MYSQLND_HEADER_SIZE, to_be_sent + MYSQLND_HEADER_SIZE);
+ payload_size = tmp_complen;
+ } else {
+ int3store(compress_buf + MYSQLND_HEADER_SIZE, 0);
+ memcpy(compress_buf + MYSQLND_HEADER_SIZE + COMPRESSED_HEADER_SIZE, uncompressed_payload, to_be_sent + MYSQLND_HEADER_SIZE);
+ payload_size = to_be_sent + MYSQLND_HEADER_SIZE;
+ }
+ RESTORE_HEADER_SIZE(uncompressed_payload, safe_storage);
+
+ int3store(compress_buf, payload_size);
+ int1store(compress_buf + 3, pfc->data->packet_no);
+ DBG_INF_FMT("writing "MYSQLND_SZ_T_SPEC" bytes to the network", payload_size + MYSQLND_HEADER_SIZE + COMPRESSED_HEADER_SIZE);
+ bytes_sent = vio->data->m.network_write(vio, compress_buf, payload_size + MYSQLND_HEADER_SIZE + COMPRESSED_HEADER_SIZE, conn_stats, error_info);
+ pfc->data->compressed_envelope_packet_no++;
+ #if WHEN_WE_NEED_TO_CHECK_WHETHER_COMPRESSION_WORKS_CORRECTLY
+ if (res == Z_OK) {
+ size_t decompressed_size = left + MYSQLND_HEADER_SIZE;
+ zend_uchar * decompressed_data = mnd_malloc(decompressed_size);
+ int error = pfc->data->m.decode(decompressed_data, decompressed_size,
+ compress_buf + MYSQLND_HEADER_SIZE + COMPRESSED_HEADER_SIZE, payload_size);
+ if (error == Z_OK) {
+ int i;
+ DBG_INF("success decompressing");
+ for (i = 0 ; i < decompressed_size; i++) {
+ if (i && (i % 30 == 0)) {
+ printf("\n\t\t");
+ }
+ printf("%.2X ", (int)*((char*)&(decompressed_data[i])));
+ DBG_INF_FMT("%.2X ", (int)*((char*)&(decompressed_data[i])));
+ }
+ } else {
+ DBG_INF("error decompressing");
+ }
+ mnd_free(decompressed_data);
+ }
+ #endif /* WHEN_WE_NEED_TO_CHECK_WHETHER_COMPRESSION_WORKS_CORRECTLY */
+ } else
+#endif /* MYSQLND_COMPRESSION_ENABLED */
+ {
+ DBG_INF("no compression");
+ STORE_HEADER_SIZE(safe_storage, p);
+ int3store(p, to_be_sent);
+ int1store(p + 3, pfc->data->packet_no);
+ bytes_sent = vio->data->m.network_write(vio, p, to_be_sent + MYSQLND_HEADER_SIZE, conn_stats, error_info);
+ RESTORE_HEADER_SIZE(p, safe_storage);
+ pfc->data->compressed_envelope_packet_no++;
+ }
+ pfc->data->packet_no++;
+
+ p += to_be_sent;
+ left -= to_be_sent;
+ packets_sent++;
+ /*
+ if left is 0 then there is nothing more to send, but if the last packet was exactly
+ with the size MYSQLND_MAX_PACKET_SIZE we need to send additional packet, which has
+ empty payload. Thus if left == 0 we check for to_be_sent being the max size. If it is
+ indeed it then loop once more, then to_be_sent will become 0, left will stay 0. Empty
+ packet will be sent and this loop will end.
+ */
+ } while (bytes_sent && (left > 0 || to_be_sent == MYSQLND_MAX_PACKET_SIZE));
+
+ DBG_INF_FMT("packet_size="MYSQLND_SZ_T_SPEC" packet_no=%u", left, pfc->data->packet_no);
+
+ MYSQLND_INC_CONN_STATISTIC_W_VALUE3(conn_stats,
+ STAT_BYTES_SENT, count + packets_sent * MYSQLND_HEADER_SIZE,
+ STAT_PROTOCOL_OVERHEAD_OUT, packets_sent * MYSQLND_HEADER_SIZE,
+ STAT_PACKETS_SENT, packets_sent);
+
+ if (compress_buf) {
+ mnd_efree(compress_buf);
+ }
+
+ /* Even for zero size payload we have to send a packet */
+ if (!bytes_sent) {
+ DBG_ERR_FMT("Can't %u send bytes", count);
+ SET_CLIENT_ERROR(error_info, CR_SERVER_GONE_ERROR, UNKNOWN_SQLSTATE, mysqlnd_server_gone);
+ }
+ DBG_RETURN(bytes_sent);
+}
+/* }}} */
+
+
+#ifdef MYSQLND_COMPRESSION_ENABLED
+
+/* {{{ mysqlnd_pfc::read_compressed_packet_from_stream_and_fill_read_buffer */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_pfc, read_compressed_packet_from_stream_and_fill_read_buffer)
+ (MYSQLND_PFC * pfc, MYSQLND_VIO * vio, size_t net_payload_size, MYSQLND_STATS * conn_stats, MYSQLND_ERROR_INFO * error_info)
+{
+ size_t decompressed_size;
+ enum_func_status retval = PASS;
+ zend_uchar * compressed_data = NULL;
+ zend_uchar comp_header[COMPRESSED_HEADER_SIZE];
+ DBG_ENTER("mysqlnd_pfc::read_compressed_packet_from_stream_and_fill_read_buffer");
+
+ /* Read the compressed header */
+ if (FAIL == vio->data->m.network_read(vio, comp_header, COMPRESSED_HEADER_SIZE, conn_stats, error_info)) {
+ DBG_RETURN(FAIL);
+ }
+ decompressed_size = uint3korr(comp_header);
+
+ /* When decompressed_size is 0, then the data is not compressed, and we have wasted 3 bytes */
+ /* we need to decompress the data */
+
+ if (decompressed_size) {
+ compressed_data = mnd_emalloc(net_payload_size);
+ if (FAIL == vio->data->m.network_read(vio, compressed_data, net_payload_size, conn_stats, error_info)) {
+ retval = FAIL;
+ goto end;
+ }
+ pfc->data->uncompressed_data = mysqlnd_create_read_buffer(decompressed_size);
+ retval = pfc->data->m.decode(pfc->data->uncompressed_data->data, decompressed_size, compressed_data, net_payload_size);
+ if (FAIL == retval) {
+ goto end;
+ }
+ } else {
+ DBG_INF_FMT("The server decided not to compress the data. Our job is easy. Copying %u bytes", net_payload_size);
+ pfc->data->uncompressed_data = mysqlnd_create_read_buffer(net_payload_size);
+ if (FAIL == vio->data->m.network_read(vio, pfc->data->uncompressed_data->data, net_payload_size, conn_stats, error_info)) {
+ retval = FAIL;
+ goto end;
+ }
+ }
+end:
+ if (compressed_data) {
+ mnd_efree(compressed_data);
+ }
+ DBG_RETURN(retval);
+}
+/* }}} */
+#endif /* MYSQLND_COMPRESSION_ENABLED */
+
+
+/* {{{ mysqlnd_pfc::decode */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_pfc, decode)(zend_uchar * uncompressed_data, const size_t uncompressed_data_len,
+ const zend_uchar * const compressed_data, const size_t compressed_data_len)
+{
+#ifdef MYSQLND_COMPRESSION_ENABLED
+ int error;
+ uLongf tmp_complen = uncompressed_data_len;
+ DBG_ENTER("mysqlnd_pfc::decode");
+ error = uncompress(uncompressed_data, &tmp_complen, compressed_data, compressed_data_len);
+
+ DBG_INF_FMT("compressed data: decomp_len=%lu compressed_size="MYSQLND_SZ_T_SPEC, tmp_complen, compressed_data_len);
+ if (error != Z_OK) {
+ DBG_INF_FMT("decompression NOT successful. error=%d Z_OK=%d Z_BUF_ERROR=%d Z_MEM_ERROR=%d", error, Z_OK, Z_BUF_ERROR, Z_MEM_ERROR);
+ }
+ DBG_RETURN(error == Z_OK? PASS:FAIL);
+#else
+ DBG_ENTER("mysqlnd_pfc::decode");
+ DBG_RETURN(FAIL);
+#endif
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_pfc::encode */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_pfc, encode)(zend_uchar * compress_buffer, size_t * compress_buffer_len,
+ const zend_uchar * const uncompressed_data, const size_t uncompressed_data_len)
+{
+#ifdef MYSQLND_COMPRESSION_ENABLED
+ int error;
+ uLongf tmp_complen = *compress_buffer_len;
+ DBG_ENTER("mysqlnd_pfc::encode");
+ error = compress(compress_buffer, &tmp_complen, uncompressed_data, uncompressed_data_len);
+
+ if (error != Z_OK) {
+ DBG_INF_FMT("compression NOT successful. error=%d Z_OK=%d Z_BUF_ERROR=%d Z_MEM_ERROR=%d", error, Z_OK, Z_BUF_ERROR, Z_MEM_ERROR);
+ } else {
+ *compress_buffer_len = tmp_complen;
+ DBG_INF_FMT("compression successful. compressed size=%lu", tmp_complen);
+ }
+
+ DBG_RETURN(error == Z_OK? PASS:FAIL);
+#else
+ DBG_ENTER("mysqlnd_pfc::encode");
+ DBG_RETURN(FAIL);
+#endif
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_pfc::receive */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_pfc, receive)(MYSQLND_PFC * const pfc, MYSQLND_VIO * const vio, zend_uchar * const buffer, const size_t count,
+ MYSQLND_STATS * const conn_stats, MYSQLND_ERROR_INFO * const error_info)
+{
+ size_t to_read = count;
+ zend_uchar * p = buffer;
+
+ DBG_ENTER("mysqlnd_pfc::receive");
+#ifdef MYSQLND_COMPRESSION_ENABLED
+ if (pfc->data->compressed) {
+ if (pfc->data->uncompressed_data) {
+ size_t to_read_from_buffer = MIN(pfc->data->uncompressed_data->bytes_left(pfc->data->uncompressed_data), to_read);
+ DBG_INF_FMT("reading "MYSQLND_SZ_T_SPEC" from uncompressed_data buffer", to_read_from_buffer);
+ if (to_read_from_buffer) {
+ pfc->data->uncompressed_data->read(pfc->data->uncompressed_data, to_read_from_buffer, (zend_uchar *) p);
+ p += to_read_from_buffer;
+ to_read -= to_read_from_buffer;
+ }
+ DBG_INF_FMT("left "MYSQLND_SZ_T_SPEC" to read", to_read);
+ if (TRUE == pfc->data->uncompressed_data->is_empty(pfc->data->uncompressed_data)) {
+ /* Everything was consumed. This should never happen here, but for security */
+ pfc->data->uncompressed_data->free_buffer(&pfc->data->uncompressed_data);
+ }
+ }
+ if (to_read) {
+ zend_uchar net_header[MYSQLND_HEADER_SIZE];
+ size_t net_payload_size;
+ zend_uchar packet_no;
+
+ if (FAIL == vio->data->m.network_read(vio, net_header, MYSQLND_HEADER_SIZE, conn_stats, error_info)) {
+ DBG_RETURN(FAIL);
+ }
+ net_payload_size = uint3korr(net_header);
+ packet_no = uint1korr(net_header + 3);
+ if (pfc->data->compressed_envelope_packet_no != packet_no) {
+ DBG_ERR_FMT("Transport level: packets out of order. Expected %u received %u. Packet size="MYSQLND_SZ_T_SPEC,
+ pfc->data->compressed_envelope_packet_no, packet_no, net_payload_size);
+
+ php_error(E_WARNING, "Packets out of order. Expected %u received %u. Packet size="MYSQLND_SZ_T_SPEC,
+ pfc->data->compressed_envelope_packet_no, packet_no, net_payload_size);
+ DBG_RETURN(FAIL);
+ }
+ pfc->data->compressed_envelope_packet_no++;
+#ifdef MYSQLND_DUMP_HEADER_N_BODY
+ DBG_INF_FMT("HEADER: hwd_packet_no=%u size=%3u", packet_no, (zend_ulong) net_payload_size);
+#endif
+ /* Now let's read from the wire, decompress it and fill the read buffer */
+ pfc->data->m.read_compressed_packet_from_stream_and_fill_read_buffer(pfc, vio, net_payload_size, conn_stats, error_info);
+
+ /*
+ Now a bit of recursion - read from the read buffer,
+ if the data which we have just read from the wire
+ is not enough, then the recursive call will try to
+ satisfy it until it is satisfied.
+ */
+ DBG_RETURN(pfc->data->m.receive(pfc, vio, p, to_read, conn_stats, error_info));
+ }
+ DBG_RETURN(PASS);
+ }
+#endif /* MYSQLND_COMPRESSION_ENABLED */
+ DBG_RETURN(vio->data->m.network_read(vio, p, to_read, conn_stats, error_info));
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_pfc::set_client_option */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_pfc, set_client_option)(MYSQLND_PFC * const pfc, enum_mysqlnd_client_option option, const char * const value)
+{
+ DBG_ENTER("mysqlnd_pfc::set_client_option");
+ DBG_INF_FMT("option=%u", option);
+ switch (option) {
+ case MYSQL_OPT_COMPRESS:
+ pfc->data->flags |= MYSQLND_PROTOCOL_FLAG_USE_COMPRESSION;
+ break;
+ case MYSQL_SERVER_PUBLIC_KEY: {
+ const zend_bool pers = pfc->persistent;
+ if (pfc->data->sha256_server_public_key) {
+ mnd_pefree(pfc->data->sha256_server_public_key, pers);
+ }
+ pfc->data->sha256_server_public_key = value? mnd_pestrdup(value, pers) : NULL;
+ break;
+ case MYSQLND_OPT_NET_CMD_BUFFER_SIZE:
+ DBG_INF("MYSQLND_OPT_NET_CMD_BUFFER_SIZE");
+ if (*(unsigned int*) value < MYSQLND_NET_CMD_BUFFER_MIN_SIZE) {
+ DBG_RETURN(FAIL);
+ }
+ pfc->cmd_buffer.length = *(unsigned int*) value;
+ DBG_INF_FMT("new_length="MYSQLND_SZ_T_SPEC, pfc->cmd_buffer.length);
+ if (!pfc->cmd_buffer.buffer) {
+ pfc->cmd_buffer.buffer = mnd_pemalloc(pfc->cmd_buffer.length, pfc->persistent);
+ } else {
+ pfc->cmd_buffer.buffer = mnd_perealloc(pfc->cmd_buffer.buffer, pfc->cmd_buffer.length, pfc->persistent);
+ }
+ break;
+ }
+ default:
+ DBG_RETURN(FAIL);
+ }
+ DBG_RETURN(PASS);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_pfc::free_contents */
+static void
+MYSQLND_METHOD(mysqlnd_pfc, free_contents)(MYSQLND_PFC * pfc)
+{
+ DBG_ENTER("mysqlnd_pfc::free_contents");
+
+#ifdef MYSQLND_COMPRESSION_ENABLED
+ if (pfc->data->uncompressed_data) {
+ pfc->data->uncompressed_data->free_buffer(&pfc->data->uncompressed_data);
+ }
+#endif
+ if (pfc->data->sha256_server_public_key) {
+ mnd_pefree(pfc->data->sha256_server_public_key, pfc->persistent);
+ pfc->data->sha256_server_public_key = NULL;
+ }
+
+ DBG_VOID_RETURN;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_pfc::init */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_pfc, init)(MYSQLND_PFC * const pfc, MYSQLND_STATS * const stats, MYSQLND_ERROR_INFO * const error_info)
+{
+ unsigned int buf_size;
+ DBG_ENTER("mysqlnd_pfc::init");
+
+ buf_size = MYSQLND_G(net_cmd_buffer_size); /* this is long, cast to unsigned int*/
+ pfc->data->m.set_client_option(pfc, MYSQLND_OPT_NET_CMD_BUFFER_SIZE, (char *) &buf_size);
+
+ DBG_RETURN(PASS);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_pfc::dtor */
+static void
+MYSQLND_METHOD(mysqlnd_pfc, dtor)(MYSQLND_PFC * const pfc, MYSQLND_STATS * const stats, MYSQLND_ERROR_INFO * const error_info)
+{
+ DBG_ENTER("mysqlnd_pfc::dtor");
+ if (pfc) {
+ pfc->data->m.free_contents(pfc);
+
+ if (pfc->cmd_buffer.buffer) {
+ DBG_INF("Freeing cmd buffer");
+ mnd_pefree(pfc->cmd_buffer.buffer, pfc->persistent);
+ pfc->cmd_buffer.buffer = NULL;
+ }
+
+ mnd_pefree(pfc->data, pfc->data->persistent);
+ mnd_pefree(pfc, pfc->persistent);
+ }
+ DBG_VOID_RETURN;
+}
+/* }}} */
+
+
+MYSQLND_CLASS_METHODS_START(mysqlnd_protocol_packet_frame_codec)
+ MYSQLND_METHOD(mysqlnd_pfc, init),
+ MYSQLND_METHOD(mysqlnd_pfc, dtor),
+ MYSQLND_METHOD(mysqlnd_pfc, reset),
+
+ MYSQLND_METHOD(mysqlnd_pfc, set_client_option),
+
+ MYSQLND_METHOD(mysqlnd_pfc, decode),
+ MYSQLND_METHOD(mysqlnd_pfc, encode),
+
+ MYSQLND_METHOD(mysqlnd_pfc, send),
+ MYSQLND_METHOD(mysqlnd_pfc, receive),
+
+#ifdef MYSQLND_COMPRESSION_ENABLED
+ MYSQLND_METHOD(mysqlnd_pfc, read_compressed_packet_from_stream_and_fill_read_buffer),
+#else
+ NULL,
+#endif
+
+ MYSQLND_METHOD(mysqlnd_pfc, free_contents),
+MYSQLND_CLASS_METHODS_END;
+
+
+/* {{{ mysqlnd_pfc_init */
+PHPAPI MYSQLND_PFC *
+mysqlnd_pfc_init(const zend_bool persistent, MYSQLND_CLASS_METHODS_TYPE(mysqlnd_object_factory) *object_factory, MYSQLND_STATS * stats, MYSQLND_ERROR_INFO * error_info)
+{
+ MYSQLND_CLASS_METHODS_TYPE(mysqlnd_object_factory) *factory = object_factory? object_factory : &MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_object_factory);
+ MYSQLND_PFC * pfc;
+ DBG_ENTER("mysqlnd_pfc_init");
+ pfc = factory->get_protocol_frame_codec(persistent, stats, error_info);
+ DBG_RETURN(pfc);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_pfc_free */
+PHPAPI void
+mysqlnd_pfc_free(MYSQLND_PFC * const pfc, MYSQLND_STATS * stats, MYSQLND_ERROR_INFO * error_info)
+{
+ DBG_ENTER("mysqlnd_pfc_free");
+ if (pfc) {
+ pfc->data->m.dtor(pfc, stats, error_info);
+ }
+ DBG_VOID_RETURN;
+}
+/* }}} */
+
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: noet sw=4 ts=4 fdm=marker
+ * vim<600: noet sw=4 ts=4
+ */
diff --git a/ext/mysqlnd/mysqlnd_protocol_frame_codec.h b/ext/mysqlnd/mysqlnd_protocol_frame_codec.h
new file mode 100644
index 0000000000..b00d0c5b08
--- /dev/null
+++ b/ext/mysqlnd/mysqlnd_protocol_frame_codec.h
@@ -0,0 +1,35 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 7 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 2006-2016 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: Andrey Hristov <andrey@php.net> |
+ | Ulf Wendel <uw@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#ifndef MYSQLND_PROTOCOL_FRAME_CODEC_H
+#define MYSQLND_PROTOCOL_FRAME_CODEC_H
+
+PHPAPI MYSQLND_PFC * mysqlnd_pfc_init(const zend_bool persistent, MYSQLND_CLASS_METHODS_TYPE(mysqlnd_object_factory) *object_factory, MYSQLND_STATS * stats, MYSQLND_ERROR_INFO * error_info);
+PHPAPI void mysqlnd_pfc_free(MYSQLND_PFC * const pfc, MYSQLND_STATS * stats, MYSQLND_ERROR_INFO * error_info);
+
+#endif /* MYSQLND_PROTOCOL_FRAME_CODEC_H */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: noet sw=4 ts=4 fdm=marker
+ * vim<600: noet sw=4 ts=4
+ */
diff --git a/ext/mysqlnd/mysqlnd_ps.c b/ext/mysqlnd/mysqlnd_ps.c
index 7053ebe8e7..432bc78ade 100644
--- a/ext/mysqlnd/mysqlnd_ps.c
+++ b/ext/mysqlnd/mysqlnd_ps.c
@@ -14,14 +14,15 @@
+----------------------------------------------------------------------+
| Authors: Andrey Hristov <andrey@php.net> |
| Ulf Wendel <uw@php.net> |
- | Georg Richter <georg@php.net> |
+----------------------------------------------------------------------+
*/
#include "php.h"
#include "mysqlnd.h"
#include "mysqlnd_wireprotocol.h"
+#include "mysqlnd_connection.h"
#include "mysqlnd_priv.h"
+#include "mysqlnd_ps.h"
#include "mysqlnd_result.h"
#include "mysqlnd_result_meta.h"
#include "mysqlnd_statistics.h"
@@ -29,9 +30,6 @@
#include "mysqlnd_block_alloc.h"
#include "mysqlnd_ext_plugin.h"
-#define MYSQLND_SILENT
-
-
const char * const mysqlnd_not_bound_as_blob = "Can't send long data for non-string/non-binary data types";
const char * const mysqlnd_stmt_not_prepared = "Statement not prepared";
@@ -40,25 +38,23 @@ enum_func_status mysqlnd_stmt_execute_generate_request(MYSQLND_STMT * const s, z
enum_func_status mysqlnd_stmt_execute_batch_generate_request(MYSQLND_STMT * const s, zend_uchar ** request, size_t *request_len, zend_bool * free_buffer);
static void mysqlnd_stmt_separate_result_bind(MYSQLND_STMT * const stmt);
-static void mysqlnd_stmt_separate_one_result_bind(MYSQLND_STMT * const stmt, unsigned int param_no);
+static void mysqlnd_stmt_separate_one_result_bind(MYSQLND_STMT * const stmt, const unsigned int param_no);
/* {{{ mysqlnd_stmt::store_result */
static MYSQLND_RES *
MYSQLND_METHOD(mysqlnd_stmt, store_result)(MYSQLND_STMT * const s)
{
- MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
enum_func_status ret;
- MYSQLND_CONN_DATA * conn;
+ MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
+ MYSQLND_CONN_DATA * conn = stmt? stmt->conn : NULL;
MYSQLND_RES * result;
DBG_ENTER("mysqlnd_stmt::store_result");
- if (!stmt || !stmt->conn || !stmt->result) {
+ if (!stmt || !conn || !stmt->result) {
DBG_RETURN(NULL);
}
DBG_INF_FMT("stmt=%lu", stmt->stmt_id);
- conn = stmt->conn;
-
/* be compliant with libmysql - NULL will turn */
if (!stmt->field_count) {
DBG_RETURN(NULL);
@@ -70,18 +66,16 @@ MYSQLND_METHOD(mysqlnd_stmt, store_result)(MYSQLND_STMT * const s)
}
/* Nothing to store for UPSERT/LOAD DATA*/
- if (CONN_GET_STATE(conn) != CONN_FETCHING_DATA ||
- stmt->state != MYSQLND_STMT_WAITING_USE_OR_STORE)
+ if (GET_CONNECTION_STATE(&conn->state) != CONN_FETCHING_DATA || stmt->state != MYSQLND_STMT_WAITING_USE_OR_STORE)
{
- SET_CLIENT_ERROR(*conn->error_info, CR_COMMANDS_OUT_OF_SYNC,
- UNKNOWN_SQLSTATE, mysqlnd_out_of_sync);
+ SET_CLIENT_ERROR(conn->error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, mysqlnd_out_of_sync);
DBG_RETURN(NULL);
}
stmt->default_rset_handler = s->m->store_result;
- SET_EMPTY_ERROR(*stmt->error_info);
- SET_EMPTY_ERROR(*conn->error_info);
+ SET_EMPTY_ERROR(stmt->error_info);
+ SET_EMPTY_ERROR(conn->error_info);
MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_PS_BUFFERED_SETS);
result = stmt->result;
@@ -90,7 +84,7 @@ MYSQLND_METHOD(mysqlnd_stmt, store_result)(MYSQLND_STMT * const s)
result->stored_data = (MYSQLND_RES_BUFFERED *) mysqlnd_result_buffered_zval_init(result->field_count, TRUE, result->persistent);
if (!result->stored_data) {
- SET_OOM_ERROR(*conn->error_info);
+ SET_OOM_ERROR(conn->error_info);
DBG_RETURN(NULL);
}
@@ -99,19 +93,18 @@ MYSQLND_METHOD(mysqlnd_stmt, store_result)(MYSQLND_STMT * const s)
result->stored_data->m.fetch_row = mysqlnd_stmt_fetch_row_buffered;
if (PASS == ret) {
- /* Overflow ? */
if (result->stored_data->type == MYSQLND_BUFFERED_TYPE_ZVAL) {
MYSQLND_RES_BUFFERED_ZVAL * set = (MYSQLND_RES_BUFFERED_ZVAL *) result->stored_data;
if (result->stored_data->row_count) {
/* don't try to allocate more than possible - mnd_XXalloc expects size_t, and it can have narrower range than uint64_t */
if (result->stored_data->row_count * result->meta->field_count * sizeof(zval *) > SIZE_MAX) {
- SET_OOM_ERROR(*conn->error_info);
+ SET_OOM_ERROR(conn->error_info);
DBG_RETURN(NULL);
}
/* if pecalloc is used valgrind barks gcc version 4.3.1 20080507 (prerelease) [gcc-4_3-branch revision 135036] (SUSE Linux) */
set->data = mnd_emalloc((size_t)(result->stored_data->row_count * result->meta->field_count * sizeof(zval)));
if (!set->data) {
- SET_OOM_ERROR(*conn->error_info);
+ SET_OOM_ERROR(conn->error_info);
DBG_RETURN(NULL);
}
memset(set->data, 0, (size_t)(result->stored_data->row_count * result->meta->field_count * sizeof(zval)));
@@ -123,11 +116,11 @@ MYSQLND_METHOD(mysqlnd_stmt, store_result)(MYSQLND_STMT * const s)
}
/* libmysql API docs say it should be so for SELECT statements */
- stmt->upsert_status->affected_rows = stmt->result->stored_data->row_count;
+ UPSERT_STATUS_SET_AFFECTED_ROWS(stmt->upsert_status, stmt->result->stored_data->row_count);
stmt->state = MYSQLND_STMT_USE_OR_STORE_CALLED;
} else {
- COPY_CLIENT_ERROR(*conn->error_info, result->stored_data->error_info);
+ COPY_CLIENT_ERROR(conn->error_info, result->stored_data->error_info);
stmt->result->m.free_result_contents(stmt->result);
mnd_efree(stmt->result);
stmt->result = NULL;
@@ -143,18 +136,16 @@ MYSQLND_METHOD(mysqlnd_stmt, store_result)(MYSQLND_STMT * const s)
static MYSQLND_RES *
MYSQLND_METHOD(mysqlnd_stmt, get_result)(MYSQLND_STMT * const s)
{
- MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
- MYSQLND_CONN_DATA * conn;
- MYSQLND_RES *result;
+ MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
+ MYSQLND_CONN_DATA * conn = stmt? stmt->conn : NULL;
+ MYSQLND_RES * result;
DBG_ENTER("mysqlnd_stmt::get_result");
- if (!stmt || !stmt->conn || !stmt->result) {
+ if (!stmt || !conn || !stmt->result) {
DBG_RETURN(NULL);
}
DBG_INF_FMT("stmt=%lu", stmt->stmt_id);
- conn = stmt->conn;
-
/* be compliant with libmysql - NULL will turn */
if (!stmt->field_count) {
DBG_RETURN(NULL);
@@ -166,35 +157,34 @@ MYSQLND_METHOD(mysqlnd_stmt, get_result)(MYSQLND_STMT * const s)
}
/* Nothing to store for UPSERT/LOAD DATA*/
- if (CONN_GET_STATE(conn) != CONN_FETCHING_DATA || stmt->state != MYSQLND_STMT_WAITING_USE_OR_STORE) {
- SET_CLIENT_ERROR(*conn->error_info, CR_COMMANDS_OUT_OF_SYNC,
- UNKNOWN_SQLSTATE, mysqlnd_out_of_sync);
+ if (GET_CONNECTION_STATE(&conn->state) != CONN_FETCHING_DATA || stmt->state != MYSQLND_STMT_WAITING_USE_OR_STORE) {
+ SET_CLIENT_ERROR(conn->error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, mysqlnd_out_of_sync);
DBG_RETURN(NULL);
}
- SET_EMPTY_ERROR(*stmt->error_info);
- SET_EMPTY_ERROR(*conn->error_info);
+ SET_EMPTY_ERROR(stmt->error_info);
+ SET_EMPTY_ERROR(conn->error_info);
MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_BUFFERED_SETS);
do {
result = conn->m->result_init(stmt->result->field_count, stmt->persistent);
if (!result) {
- SET_OOM_ERROR(*conn->error_info);
+ SET_OOM_ERROR(conn->error_info);
break;
}
result->meta = stmt->result->meta->m->clone_metadata(stmt->result->meta, FALSE);
if (!result->meta) {
- SET_OOM_ERROR(*conn->error_info);
+ SET_OOM_ERROR(conn->error_info);
break;
}
if ((result = result->m.store_result(result, conn, MYSQLND_STORE_PS | MYSQLND_STORE_NO_COPY))) {
- stmt->upsert_status->affected_rows = result->stored_data->row_count;
+ UPSERT_STATUS_SET_AFFECTED_ROWS(stmt->upsert_status, result->stored_data->row_count);
stmt->state = MYSQLND_STMT_PREPARED;
result->type = MYSQLND_RES_PS_BUF;
} else {
- COPY_CLIENT_ERROR(*stmt->error_info, *conn->error_info);
+ COPY_CLIENT_ERROR(stmt->error_info, *conn->error_info);
stmt->state = MYSQLND_STMT_PREPARED;
break;
}
@@ -213,12 +203,11 @@ MYSQLND_METHOD(mysqlnd_stmt, get_result)(MYSQLND_STMT * const s)
static zend_bool
MYSQLND_METHOD(mysqlnd_stmt, more_results)(const MYSQLND_STMT * s)
{
- MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
+ MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
+ MYSQLND_CONN_DATA * conn = stmt? stmt->conn : NULL;
DBG_ENTER("mysqlnd_stmt::more_results");
/* (conn->state == CONN_NEXT_RESULT_PENDING) too */
- DBG_RETURN((stmt && stmt->conn && (stmt->conn->m->get_server_status(stmt->conn) & SERVER_MORE_RESULTS_EXISTS))?
- TRUE:
- FALSE);
+ DBG_RETURN((stmt && conn && (conn->m->get_server_status(conn) & SERVER_MORE_RESULTS_EXISTS))? TRUE: FALSE);
}
/* }}} */
@@ -227,21 +216,20 @@ MYSQLND_METHOD(mysqlnd_stmt, more_results)(const MYSQLND_STMT * s)
static enum_func_status
MYSQLND_METHOD(mysqlnd_stmt, next_result)(MYSQLND_STMT * s)
{
- MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
- MYSQLND_CONN_DATA * conn;
+ MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
+ MYSQLND_CONN_DATA * conn = stmt? stmt->conn : NULL;
DBG_ENTER("mysqlnd_stmt::next_result");
- if (!stmt || !stmt->conn || !stmt->result) {
+ if (!stmt || !conn || !stmt->result) {
DBG_RETURN(FAIL);
}
- conn = stmt->conn;
DBG_INF_FMT("stmt=%lu", stmt->stmt_id);
- if (CONN_GET_STATE(conn) != CONN_NEXT_RESULT_PENDING || !(conn->upsert_status->server_status & SERVER_MORE_RESULTS_EXISTS)) {
+ if (GET_CONNECTION_STATE(&conn->state) != CONN_NEXT_RESULT_PENDING || !(UPSERT_STATUS_GET_SERVER_STATUS(conn->upsert_status) & SERVER_MORE_RESULTS_EXISTS)) {
DBG_RETURN(FAIL);
}
- DBG_INF_FMT("server_status=%u cursor=%u", stmt->upsert_status->server_status, stmt->upsert_status->server_status & SERVER_STATUS_CURSOR_EXISTS);
+ DBG_INF_FMT("server_status=%u cursor=%u", UPSERT_STATUS_GET_SERVER_STATUS(conn->upsert_status), UPSERT_STATUS_GET_SERVER_STATUS(conn->upsert_status) & SERVER_STATUS_CURSOR_EXISTS);
/* Free space for next result */
s->m->free_stmt_result(s);
@@ -257,27 +245,28 @@ MYSQLND_METHOD(mysqlnd_stmt, next_result)(MYSQLND_STMT * s)
static enum_func_status
mysqlnd_stmt_skip_metadata(MYSQLND_STMT * s)
{
- MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
+ MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
+ MYSQLND_CONN_DATA * conn = stmt? stmt->conn : NULL;
/* Follows parameter metadata, we have just to skip it, as libmysql does */
unsigned int i = 0;
enum_func_status ret = FAIL;
MYSQLND_PACKET_RES_FIELD * field_packet;
DBG_ENTER("mysqlnd_stmt_skip_metadata");
- if (!stmt || !stmt->conn || !stmt->conn->protocol) {
+ if (!stmt || !conn) {
DBG_RETURN(FAIL);
}
DBG_INF_FMT("stmt=%lu", stmt->stmt_id);
- field_packet = stmt->conn->protocol->m.get_result_field_packet(stmt->conn->protocol, FALSE);
+ field_packet = conn->payload_decoder_factory->m.get_result_field_packet(conn->payload_decoder_factory, FALSE);
if (!field_packet) {
- SET_OOM_ERROR(*stmt->error_info);
- SET_OOM_ERROR(*stmt->conn->error_info);
+ SET_OOM_ERROR(stmt->error_info);
+ SET_OOM_ERROR(conn->error_info);
} else {
ret = PASS;
field_packet->skip_parsing = TRUE;
for (;i < stmt->param_count; i++) {
- if (FAIL == PACKET_READ(field_packet, stmt->conn)) {
+ if (FAIL == PACKET_READ(field_packet)) {
ret = FAIL;
break;
}
@@ -294,38 +283,39 @@ mysqlnd_stmt_skip_metadata(MYSQLND_STMT * s)
static enum_func_status
mysqlnd_stmt_read_prepare_response(MYSQLND_STMT * s)
{
- MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
+ MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
+ MYSQLND_CONN_DATA * conn = stmt? stmt->conn : NULL;
MYSQLND_PACKET_PREPARE_RESPONSE * prepare_resp;
enum_func_status ret = FAIL;
DBG_ENTER("mysqlnd_stmt_read_prepare_response");
- if (!stmt || !stmt->conn || !stmt->conn->protocol) {
+ if (!stmt || !conn) {
DBG_RETURN(FAIL);
}
DBG_INF_FMT("stmt=%lu", stmt->stmt_id);
- prepare_resp = stmt->conn->protocol->m.get_prepare_response_packet(stmt->conn->protocol, FALSE);
+ prepare_resp = conn->payload_decoder_factory->m.get_prepare_response_packet(conn->payload_decoder_factory, FALSE);
if (!prepare_resp) {
- SET_OOM_ERROR(*stmt->error_info);
- SET_OOM_ERROR(*stmt->conn->error_info);
+ SET_OOM_ERROR(stmt->error_info);
+ SET_OOM_ERROR(conn->error_info);
goto done;
}
- if (FAIL == PACKET_READ(prepare_resp, stmt->conn)) {
+ if (FAIL == PACKET_READ(prepare_resp)) {
goto done;
}
if (0xFF == prepare_resp->error_code) {
- COPY_CLIENT_ERROR(*stmt->error_info, prepare_resp->error_info);
- COPY_CLIENT_ERROR(*stmt->conn->error_info, prepare_resp->error_info);
+ COPY_CLIENT_ERROR(stmt->error_info, prepare_resp->error_info);
+ COPY_CLIENT_ERROR(conn->error_info, prepare_resp->error_info);
goto done;
}
ret = PASS;
stmt->stmt_id = prepare_resp->stmt_id;
- stmt->warning_count = stmt->conn->upsert_status->warning_count = prepare_resp->warning_count;
- stmt->field_count = stmt->conn->field_count = prepare_resp->field_count;
+ UPSERT_STATUS_SET_WARNINGS(conn->upsert_status, prepare_resp->warning_count);
+ UPSERT_STATUS_SET_AFFECTED_ROWS(stmt->upsert_status, 0); /* be like libmysql */
+ stmt->field_count = conn->field_count = prepare_resp->field_count;
stmt->param_count = prepare_resp->param_count;
- stmt->upsert_status->affected_rows = 0; /* be like libmysql */
done:
PACKET_FREE(prepare_resp);
@@ -338,31 +328,37 @@ done:
static enum_func_status
mysqlnd_stmt_prepare_read_eof(MYSQLND_STMT * s)
{
- MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
+ MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
+ MYSQLND_CONN_DATA * conn = stmt? stmt->conn : NULL;
MYSQLND_PACKET_EOF * fields_eof;
enum_func_status ret = FAIL;
DBG_ENTER("mysqlnd_stmt_prepare_read_eof");
- if (!stmt || !stmt->conn || !stmt->conn->protocol) {
+ if (!stmt || !conn) {
DBG_RETURN(FAIL);
}
DBG_INF_FMT("stmt=%lu", stmt->stmt_id);
- fields_eof = stmt->conn->protocol->m.get_eof_packet(stmt->conn->protocol, FALSE);
+ fields_eof = conn->payload_decoder_factory->m.get_eof_packet(conn->payload_decoder_factory, FALSE);
if (!fields_eof) {
- SET_OOM_ERROR(*stmt->error_info);
- SET_OOM_ERROR(*stmt->conn->error_info);
+ SET_OOM_ERROR(stmt->error_info);
+ SET_OOM_ERROR(conn->error_info);
} else {
- if (FAIL == (ret = PACKET_READ(fields_eof, stmt->conn))) {
+ if (FAIL == (ret = PACKET_READ(fields_eof))) {
if (stmt->result) {
stmt->result->m.free_result_contents(stmt->result);
mnd_efree(stmt->result);
+ /* XXX: This will crash, because we will null also the methods.
+ But seems it happens in extreme cases or doesn't. Should be fixed by exporting a function
+ (from mysqlnd_driver.c?) to do the reset.
+ This bad handling is also in mysqlnd_result.c
+ */
memset(stmt, 0, sizeof(MYSQLND_STMT_DATA));
stmt->state = MYSQLND_STMT_INITTED;
}
} else {
- stmt->upsert_status->server_status = fields_eof->server_status;
- stmt->upsert_status->warning_count = fields_eof->warning_count;
+ UPSERT_STATUS_SET_SERVER_STATUS(stmt->upsert_status, fields_eof->server_status);
+ UPSERT_STATUS_SET_WARNINGS(stmt->upsert_status, fields_eof->warning_count);
stmt->state = MYSQLND_STMT_PREPARED;
}
PACKET_FREE(fields_eof);
@@ -375,24 +371,25 @@ mysqlnd_stmt_prepare_read_eof(MYSQLND_STMT * s)
/* {{{ mysqlnd_stmt::prepare */
static enum_func_status
-MYSQLND_METHOD(mysqlnd_stmt, prepare)(MYSQLND_STMT * const s, const char * const query, unsigned int query_len)
+MYSQLND_METHOD(mysqlnd_stmt, prepare)(MYSQLND_STMT * const s, const char * const query, const size_t query_len)
{
- MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
+ MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
+ MYSQLND_CONN_DATA * conn = stmt? stmt->conn : NULL;
MYSQLND_STMT * s_to_prepare = s;
MYSQLND_STMT_DATA * stmt_to_prepare = stmt;
DBG_ENTER("mysqlnd_stmt::prepare");
- if (!stmt || !stmt->conn) {
+ if (!stmt || !conn) {
DBG_RETURN(FAIL);
}
DBG_INF_FMT("stmt=%lu", stmt->stmt_id);
DBG_INF_FMT("query=%s", query);
- SET_ERROR_AFF_ROWS(stmt);
- SET_ERROR_AFF_ROWS(stmt->conn);
+ UPSERT_STATUS_SET_AFFECTED_ROWS_TO_ERROR(stmt->upsert_status);
+ UPSERT_STATUS_SET_AFFECTED_ROWS_TO_ERROR(conn->upsert_status);
- SET_EMPTY_ERROR(*stmt->error_info);
- SET_EMPTY_ERROR(*stmt->conn->error_info);
+ SET_EMPTY_ERROR(stmt->error_info);
+ SET_EMPTY_ERROR(conn->error_info);
if (stmt->state > MYSQLND_STMT_INITTED) {
/* See if we have to clean the wire */
@@ -409,16 +406,27 @@ MYSQLND_METHOD(mysqlnd_stmt, prepare)(MYSQLND_STMT * const s, const char * const
Create a new test statement, which we will prepare, but if anything
fails, we will scrap it.
*/
- s_to_prepare = stmt->conn->m->stmt_init(stmt->conn);
+ s_to_prepare = conn->m->stmt_init(conn);
if (!s_to_prepare) {
goto fail;
}
stmt_to_prepare = s_to_prepare->data;
}
- if (FAIL == stmt_to_prepare->conn->m->simple_command(stmt_to_prepare->conn, COM_STMT_PREPARE, (const zend_uchar *) query, query_len, PROT_LAST, FALSE, TRUE) ||
- FAIL == mysqlnd_stmt_read_prepare_response(s_to_prepare))
{
+ enum_func_status ret = FAIL;
+ const MYSQLND_CSTRING query_string = {query, query_len};
+ struct st_mysqlnd_protocol_command * command = conn->command_factory(COM_STMT_PREPARE, conn, query_string);
+ if (command) {
+ ret = command->run(command);
+ command->free_command(command);
+ }
+ if (FAIL == ret) {
+ goto fail;
+ }
+ }
+
+ if (FAIL == mysqlnd_stmt_read_prepare_response(s_to_prepare)) {
goto fail;
}
@@ -436,19 +444,19 @@ MYSQLND_METHOD(mysqlnd_stmt, prepare)(MYSQLND_STMT * const s, const char * const
no metadata at prepare.
*/
if (stmt_to_prepare->field_count) {
- MYSQLND_RES * result = stmt->conn->m->result_init(stmt_to_prepare->field_count, stmt_to_prepare->persistent);
+ MYSQLND_RES * result = conn->m->result_init(stmt_to_prepare->field_count, stmt_to_prepare->persistent);
if (!result) {
- SET_OOM_ERROR(*stmt->conn->error_info);
+ SET_OOM_ERROR(conn->error_info);
goto fail;
}
/* Allocate the result now as it is needed for the reading of metadata */
stmt_to_prepare->result = result;
- result->conn = stmt_to_prepare->conn->m->get_reference(stmt_to_prepare->conn);
+ result->conn = conn->m->get_reference(conn);
result->type = MYSQLND_RES_PS_BUF;
- if (FAIL == result->m.read_result_metadata(result, stmt_to_prepare->conn) ||
+ if (FAIL == result->m.read_result_metadata(result, conn) ||
FAIL == mysqlnd_stmt_prepare_read_eof(s_to_prepare))
{
goto fail;
@@ -490,23 +498,22 @@ fail:
static enum_func_status
mysqlnd_stmt_execute_parse_response(MYSQLND_STMT * const s, enum_mysqlnd_parse_exec_response_type type)
{
- MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
+ MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
+ MYSQLND_CONN_DATA * conn = stmt? stmt->conn : NULL;
enum_func_status ret;
- MYSQLND_CONN_DATA * conn;
DBG_ENTER("mysqlnd_stmt_execute_parse_response");
- if (!stmt || !stmt->conn) {
+ if (!stmt || !conn) {
DBG_RETURN(FAIL);
}
- conn = stmt->conn;
- CONN_SET_STATE(conn, CONN_QUERY_SENT);
+ SET_CONNECTION_STATE(&conn->state, CONN_QUERY_SENT);
- ret = mysqlnd_query_read_result_set_header(stmt->conn, s);
+ ret = conn->m->query_read_result_set_header(conn, s);
if (ret == FAIL) {
- COPY_CLIENT_ERROR(*stmt->error_info, *conn->error_info);
- memset(stmt->upsert_status, 0, sizeof(*stmt->upsert_status));
- stmt->upsert_status->affected_rows = conn->upsert_status->affected_rows;
- if (CONN_GET_STATE(conn) == CONN_QUIT_SENT) {
+ COPY_CLIENT_ERROR(stmt->error_info, *conn->error_info);
+ UPSERT_STATUS_RESET(stmt->upsert_status);
+ UPSERT_STATUS_SET_AFFECTED_ROWS(stmt->upsert_status, UPSERT_STATUS_GET_AFFECTED_ROWS(conn->upsert_status));
+ if (GET_CONNECTION_STATE(&conn->state) == CONN_QUIT_SENT) {
/* close the statement here, the connection has been closed */
}
stmt->state = MYSQLND_STMT_PREPARED;
@@ -519,9 +526,13 @@ mysqlnd_stmt_execute_parse_response(MYSQLND_STMT * const s, enum_mysqlnd_parse_e
value is > LONG_MAX or < LONG_MIN, there is string conversion and we have
to resend the types. Next execution will also need to resend the type.
*/
- SET_EMPTY_ERROR(*stmt->error_info);
- SET_EMPTY_ERROR(*stmt->conn->error_info);
- *stmt->upsert_status = *conn->upsert_status; /* copy status */
+ SET_EMPTY_ERROR(stmt->error_info);
+ SET_EMPTY_ERROR(conn->error_info);
+ UPSERT_STATUS_SET_WARNINGS(stmt->upsert_status, UPSERT_STATUS_GET_WARNINGS(conn->upsert_status));
+ UPSERT_STATUS_SET_AFFECTED_ROWS(stmt->upsert_status, UPSERT_STATUS_GET_AFFECTED_ROWS(conn->upsert_status));
+ UPSERT_STATUS_SET_SERVER_STATUS(stmt->upsert_status, UPSERT_STATUS_GET_SERVER_STATUS(conn->upsert_status));
+ UPSERT_STATUS_SET_LAST_INSERT_ID(stmt->upsert_status, UPSERT_STATUS_GET_LAST_INSERT_ID(conn->upsert_status));
+
stmt->state = MYSQLND_STMT_EXECUTED;
if (conn->last_query_type == QUERY_UPSERT || conn->last_query_type == QUERY_LOAD_LOCAL) {
DBG_INF("PASS");
@@ -534,7 +545,7 @@ mysqlnd_stmt_execute_parse_response(MYSQLND_STMT * const s, enum_mysqlnd_parse_e
For SHOW we don't create (bypasses PS in server)
a result set at prepare and thus a connection was missing
*/
- stmt->result->conn = stmt->conn->m->get_reference(stmt->conn);
+ stmt->result->conn = conn->m->get_reference(conn);
}
/* Update stmt->field_count as SHOW sets it to 0 at prepare */
@@ -551,13 +562,13 @@ mysqlnd_stmt_execute_parse_response(MYSQLND_STMT * const s, enum_mysqlnd_parse_e
use_result() or store_result() and we should be able to scrap the
data on the line, if he just decides to close the statement.
*/
- DBG_INF_FMT("server_status=%u cursor=%u", stmt->upsert_status->server_status,
- stmt->upsert_status->server_status & SERVER_STATUS_CURSOR_EXISTS);
+ DBG_INF_FMT("server_status=%u cursor=%u", UPSERT_STATUS_GET_SERVER_STATUS(stmt->upsert_status),
+ UPSERT_STATUS_GET_SERVER_STATUS(stmt->upsert_status) & SERVER_STATUS_CURSOR_EXISTS);
- if (stmt->upsert_status->server_status & SERVER_STATUS_CURSOR_EXISTS) {
+ if (UPSERT_STATUS_GET_SERVER_STATUS(stmt->upsert_status) & SERVER_STATUS_CURSOR_EXISTS) {
DBG_INF("cursor exists");
stmt->cursor_exists = TRUE;
- CONN_SET_STATE(conn, CONN_READY);
+ SET_CONNECTION_STATE(&conn->state, CONN_READY);
/* Only cursor read */
stmt->default_rset_handler = s->m->use_result;
DBG_INF("use_result");
@@ -585,7 +596,7 @@ mysqlnd_stmt_execute_parse_response(MYSQLND_STMT * const s, enum_mysqlnd_parse_e
}
}
#ifndef MYSQLND_DONT_SKIP_OUT_PARAMS_RESULTSET
- if (stmt->upsert_status->server_status & SERVER_PS_OUT_PARAMS) {
+ if (UPSERT_STATUS_GET_SERVER_STATUS(stmt->upsert_status) & SERVER_PS_OUT_PARAMS) {
s->m->free_stmt_content(s);
DBG_INF("PS OUT Variable RSet, skipping");
/* OUT params result set. Skip for now to retain compatibility */
@@ -593,10 +604,10 @@ mysqlnd_stmt_execute_parse_response(MYSQLND_STMT * const s, enum_mysqlnd_parse_e
}
#endif
- DBG_INF_FMT("server_status=%u cursor=%u", stmt->upsert_status->server_status, stmt->upsert_status->server_status & SERVER_STATUS_CURSOR_EXISTS);
+ DBG_INF_FMT("server_status=%u cursor=%u", UPSERT_STATUS_GET_SERVER_STATUS(stmt->upsert_status), UPSERT_STATUS_GET_SERVER_STATUS(stmt->upsert_status) & SERVER_STATUS_CURSOR_EXISTS);
- if (ret == PASS && conn->last_query_type == QUERY_UPSERT && stmt->upsert_status->affected_rows) {
- MYSQLND_INC_CONN_STATISTIC_W_VALUE(conn->stats, STAT_ROWS_AFFECTED_PS, stmt->upsert_status->affected_rows);
+ if (ret == PASS && conn->last_query_type == QUERY_UPSERT && UPSERT_STATUS_GET_AFFECTED_ROWS(stmt->upsert_status)) {
+ MYSQLND_INC_CONN_STATISTIC_W_VALUE(conn->stats, STAT_ROWS_AFFECTED_PS, UPSERT_STATUS_GET_AFFECTED_ROWS(stmt->upsert_status));
}
DBG_INF(ret == PASS? "PASS":"FAIL");
@@ -622,24 +633,23 @@ MYSQLND_METHOD(mysqlnd_stmt, execute)(MYSQLND_STMT * const s)
/* {{{ mysqlnd_stmt::send_execute */
static enum_func_status
-MYSQLND_METHOD(mysqlnd_stmt, send_execute)(MYSQLND_STMT * const s, enum_mysqlnd_send_execute_type type, zval * read_cb, zval * err_cb)
+MYSQLND_METHOD(mysqlnd_stmt, send_execute)(MYSQLND_STMT * const s, const enum_mysqlnd_send_execute_type type, zval * read_cb, zval * err_cb)
{
- MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
+ MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
+ MYSQLND_CONN_DATA * conn = stmt? stmt->conn : NULL;
enum_func_status ret;
- MYSQLND_CONN_DATA * conn;
zend_uchar *request = NULL;
size_t request_len;
zend_bool free_request;
DBG_ENTER("mysqlnd_stmt::send_execute");
- if (!stmt || !stmt->conn) {
+ if (!stmt || !conn) {
DBG_RETURN(FAIL);
}
- conn = stmt->conn;
DBG_INF_FMT("stmt=%lu", stmt->stmt_id);
- SET_ERROR_AFF_ROWS(stmt);
- SET_ERROR_AFF_ROWS(stmt->conn);
+ UPSERT_STATUS_SET_AFFECTED_ROWS_TO_ERROR(stmt->upsert_status);
+ UPSERT_STATUS_SET_AFFECTED_ROWS_TO_ERROR(conn->upsert_status);
if (stmt->result && stmt->state >= MYSQLND_STMT_PREPARED && stmt->field_count) {
/*
@@ -687,9 +697,7 @@ MYSQLND_METHOD(mysqlnd_stmt, send_execute)(MYSQLND_STMT * const s, enum_mysqlnd_
stmt->state = MYSQLND_STMT_PREPARED;
} else if (stmt->state < MYSQLND_STMT_PREPARED) {
/* Only initted - error */
- SET_CLIENT_ERROR(*conn->error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE,
- mysqlnd_out_of_sync);
- SET_STMT_ERROR(stmt, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, mysqlnd_out_of_sync);
+ SET_CLIENT_ERROR(stmt->error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, mysqlnd_out_of_sync);
DBG_INF("FAIL");
DBG_RETURN(FAIL);
}
@@ -697,8 +705,7 @@ MYSQLND_METHOD(mysqlnd_stmt, send_execute)(MYSQLND_STMT * const s, enum_mysqlnd_
if (stmt->param_count) {
unsigned int i, not_bound = 0;
if (!stmt->param_bind) {
- SET_STMT_ERROR(stmt, CR_PARAMS_NOT_BOUND, UNKNOWN_SQLSTATE,
- "No data supplied for parameters in prepared statement");
+ SET_CLIENT_ERROR(stmt->error_info, CR_PARAMS_NOT_BOUND, UNKNOWN_SQLSTATE, "No data supplied for parameters in prepared statement");
DBG_INF("FAIL");
DBG_RETURN(FAIL);
}
@@ -711,7 +718,7 @@ MYSQLND_METHOD(mysqlnd_stmt, send_execute)(MYSQLND_STMT * const s, enum_mysqlnd_
char * msg;
mnd_sprintf(&msg, 0, "No data supplied for %u parameter%s in prepared statement",
not_bound, not_bound>1 ?"s":"");
- SET_STMT_ERROR(stmt, CR_PARAMS_NOT_BOUND, UNKNOWN_SQLSTATE, msg);
+ SET_CLIENT_ERROR(stmt->error_info, CR_PARAMS_NOT_BOUND, UNKNOWN_SQLSTATE, msg);
if (msg) {
mnd_sprintf_free(msg);
}
@@ -721,12 +728,15 @@ MYSQLND_METHOD(mysqlnd_stmt, send_execute)(MYSQLND_STMT * const s, enum_mysqlnd_
}
ret = s->m->generate_execute_request(s, &request, &request_len, &free_request);
if (ret == PASS) {
- /* support for buffer types should be added here ! */
- ret = stmt->conn->m->simple_command(stmt->conn, COM_STMT_EXECUTE, request, request_len,
- PROT_LAST /* we will handle the response packet*/,
- FALSE, FALSE);
+ const MYSQLND_CSTRING payload = {(const char*) request, request_len};
+ struct st_mysqlnd_protocol_command * command = conn->command_factory(COM_STMT_EXECUTE, conn, payload);
+ ret = FAIL;
+ if (command) {
+ ret = command->run(command);
+ command->free_command(command);
+ }
} else {
- SET_STMT_ERROR(stmt, CR_UNKNOWN_ERROR, UNKNOWN_SQLSTATE, "Couldn't generate the request. Possibly OOM.");
+ SET_CLIENT_ERROR(stmt->error_info, CR_UNKNOWN_ERROR, UNKNOWN_SQLSTATE, "Couldn't generate the request. Possibly OOM.");
}
if (free_request) {
@@ -734,7 +744,7 @@ MYSQLND_METHOD(mysqlnd_stmt, send_execute)(MYSQLND_STMT * const s, enum_mysqlnd_
}
if (ret == FAIL) {
- COPY_CLIENT_ERROR(*stmt->error_info, *conn->error_info);
+ COPY_CLIENT_ERROR(stmt->error_info, *conn->error_info);
DBG_INF("FAIL");
DBG_RETURN(FAIL);
}
@@ -747,10 +757,10 @@ MYSQLND_METHOD(mysqlnd_stmt, send_execute)(MYSQLND_STMT * const s, enum_mysqlnd_
/* {{{ mysqlnd_stmt_fetch_row_buffered */
enum_func_status
-mysqlnd_stmt_fetch_row_buffered(MYSQLND_RES * result, void * param, unsigned int flags, zend_bool * fetched_anything)
+mysqlnd_stmt_fetch_row_buffered(MYSQLND_RES * result, void * param, const unsigned int flags, zend_bool * fetched_anything)
{
MYSQLND_STMT * s = (MYSQLND_STMT *) param;
- MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
+ MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
const MYSQLND_RES_METADATA * const meta = result->meta;
unsigned int field_count = meta->field_count;
@@ -848,12 +858,13 @@ mysqlnd_stmt_fetch_row_buffered(MYSQLND_RES * result, void * param, unsigned int
/* {{{ mysqlnd_stmt_fetch_row_unbuffered */
enum_func_status
-mysqlnd_stmt_fetch_row_unbuffered(MYSQLND_RES * result, void * param, unsigned int flags, zend_bool * fetched_anything)
+mysqlnd_stmt_fetch_row_unbuffered(MYSQLND_RES * result, void * param, const unsigned int flags, zend_bool * fetched_anything)
{
enum_func_status ret;
MYSQLND_STMT * s = (MYSQLND_STMT *) param;
- MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
+ MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
MYSQLND_PACKET_ROW * row_packet;
+ MYSQLND_CONN_DATA * conn = result->conn;
const MYSQLND_RES_METADATA * const meta = result->meta;
DBG_ENTER("mysqlnd_stmt_fetch_row_unbuffered");
@@ -865,9 +876,8 @@ mysqlnd_stmt_fetch_row_unbuffered(MYSQLND_RES * result, void * param, unsigned i
DBG_INF("EOF already reached");
DBG_RETURN(PASS);
}
- if (CONN_GET_STATE(result->conn) != CONN_FETCHING_DATA) {
- SET_CLIENT_ERROR(*result->conn->error_info, CR_COMMANDS_OUT_OF_SYNC,
- UNKNOWN_SQLSTATE, mysqlnd_out_of_sync);
+ if (GET_CONNECTION_STATE(&conn->state) != CONN_FETCHING_DATA) {
+ SET_CLIENT_ERROR(conn->error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, mysqlnd_out_of_sync);
DBG_ERR("command out of sync");
DBG_RETURN(FAIL);
}
@@ -882,11 +892,11 @@ mysqlnd_stmt_fetch_row_unbuffered(MYSQLND_RES * result, void * param, unsigned i
If we skip rows (stmt == NULL || stmt->result_bind == NULL) we have to
result->unbuf->m.free_last_data() before it. The function returns always true.
*/
- if (PASS == (ret = PACKET_READ(row_packet, result->conn)) && !row_packet->eof) {
+ if (PASS == (ret = PACKET_READ(row_packet)) && !row_packet->eof) {
unsigned int i, field_count = result->field_count;
if (!row_packet->skip_extraction) {
- result->unbuf->m.free_last_data(result->unbuf, result->conn? result->conn->stats : NULL);
+ result->unbuf->m.free_last_data(result->unbuf, conn->stats);
result->unbuf->last_row_data = row_packet->fields;
result->unbuf->last_row_buffer = row_packet->row_buffer;
@@ -897,8 +907,8 @@ mysqlnd_stmt_fetch_row_unbuffered(MYSQLND_RES * result, void * param, unsigned i
result->unbuf->last_row_data,
row_packet->field_count,
row_packet->fields_metadata,
- result->conn->options->int_and_float_native,
- result->conn->stats))
+ conn->options->int_and_float_native,
+ conn->stats))
{
DBG_RETURN(FAIL);
}
@@ -917,8 +927,7 @@ mysqlnd_stmt_fetch_row_unbuffered(MYSQLND_RES * result, void * param, unsigned i
zval_dtor(result);
#endif
if (!Z_ISNULL_P(data)) {
- if ((Z_TYPE_P(data) == IS_STRING) &&
- (meta->fields[i].max_length < (zend_ulong) Z_STRLEN_P(data))) {
+ if ((Z_TYPE_P(data) == IS_STRING) && (meta->fields[i].max_length < (zend_ulong) Z_STRLEN_P(data))){
meta->fields[i].max_length = Z_STRLEN_P(data);
}
ZVAL_COPY_VALUE(result, data);
@@ -929,7 +938,7 @@ mysqlnd_stmt_fetch_row_unbuffered(MYSQLND_RES * result, void * param, unsigned i
}
}
}
- MYSQLND_INC_CONN_STATISTIC(stmt->conn->stats, STAT_ROWS_FETCHED_FROM_CLIENT_PS_UNBUF);
+ MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_ROWS_FETCHED_FROM_CLIENT_PS_UNBUF);
} else {
DBG_INF("skipping extraction");
/*
@@ -938,7 +947,8 @@ mysqlnd_stmt_fetch_row_unbuffered(MYSQLND_RES * result, void * param, unsigned i
the bound variables. Thus we need to do part of what it does or Zend will
report leaks.
*/
- row_packet->row_buffer->free_chunk(row_packet->row_buffer);
+ row_packet->result_set_memory_pool->free_chunk(
+ row_packet->result_set_memory_pool, row_packet->row_buffer);
row_packet->row_buffer = NULL;
}
@@ -946,26 +956,27 @@ mysqlnd_stmt_fetch_row_unbuffered(MYSQLND_RES * result, void * param, unsigned i
*fetched_anything = TRUE;
} else if (ret == FAIL) {
if (row_packet->error_info.error_no) {
- COPY_CLIENT_ERROR(*stmt->conn->error_info, row_packet->error_info);
- COPY_CLIENT_ERROR(*stmt->error_info, row_packet->error_info);
+ COPY_CLIENT_ERROR(conn->error_info, row_packet->error_info);
+ COPY_CLIENT_ERROR(stmt->error_info, row_packet->error_info);
}
- CONN_SET_STATE(result->conn, CONN_READY);
+ SET_CONNECTION_STATE(&conn->state, CONN_READY);
result->unbuf->eof_reached = TRUE; /* so next time we won't get an error */
} else if (row_packet->eof) {
DBG_INF("EOF");
/* Mark the connection as usable again */
result->unbuf->eof_reached = TRUE;
- memset(result->conn->upsert_status, 0, sizeof(*result->conn->upsert_status));
- result->conn->upsert_status->warning_count = row_packet->warning_count;
- result->conn->upsert_status->server_status = row_packet->server_status;
+ UPSERT_STATUS_RESET(conn->upsert_status);
+ UPSERT_STATUS_SET_WARNINGS(conn->upsert_status, row_packet->warning_count);
+ UPSERT_STATUS_SET_SERVER_STATUS(conn->upsert_status, row_packet->server_status);
+
/*
result->row_packet will be cleaned when
destroying the result object
*/
- if (result->conn->upsert_status->server_status & SERVER_MORE_RESULTS_EXISTS) {
- CONN_SET_STATE(result->conn, CONN_NEXT_RESULT_PENDING);
+ if (UPSERT_STATUS_GET_SERVER_STATUS(conn->upsert_status) & SERVER_MORE_RESULTS_EXISTS) {
+ SET_CONNECTION_STATE(&conn->state, CONN_NEXT_RESULT_PENDING);
} else {
- CONN_SET_STATE(result->conn, CONN_READY);
+ SET_CONNECTION_STATE(&conn->state, CONN_READY);
}
}
@@ -979,32 +990,29 @@ mysqlnd_stmt_fetch_row_unbuffered(MYSQLND_RES * result, void * param, unsigned i
static MYSQLND_RES *
MYSQLND_METHOD(mysqlnd_stmt, use_result)(MYSQLND_STMT * s)
{
- MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
+ MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
+ MYSQLND_CONN_DATA * conn = stmt? stmt->conn : NULL;
MYSQLND_RES * result;
- MYSQLND_CONN_DATA * conn;
DBG_ENTER("mysqlnd_stmt::use_result");
- if (!stmt || !stmt->conn || !stmt->result) {
+ if (!stmt || !conn || !stmt->result) {
DBG_RETURN(NULL);
}
DBG_INF_FMT("stmt=%lu", stmt->stmt_id);
- conn = stmt->conn;
-
if (!stmt->field_count ||
- (!stmt->cursor_exists && CONN_GET_STATE(conn) != CONN_FETCHING_DATA) ||
- (stmt->cursor_exists && CONN_GET_STATE(conn) != CONN_READY) ||
+ (!stmt->cursor_exists && GET_CONNECTION_STATE(&conn->state) != CONN_FETCHING_DATA) ||
+ (stmt->cursor_exists && GET_CONNECTION_STATE(&conn->state) != CONN_READY) ||
(stmt->state != MYSQLND_STMT_WAITING_USE_OR_STORE))
{
- SET_CLIENT_ERROR(*conn->error_info, CR_COMMANDS_OUT_OF_SYNC,
- UNKNOWN_SQLSTATE, mysqlnd_out_of_sync);
+ SET_CLIENT_ERROR(conn->error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, mysqlnd_out_of_sync);
DBG_ERR("command out of sync");
DBG_RETURN(NULL);
}
- SET_EMPTY_ERROR(*stmt->error_info);
+ SET_EMPTY_ERROR(stmt->error_info);
- MYSQLND_INC_CONN_STATISTIC(stmt->conn->stats, STAT_PS_UNBUFFERED_SETS);
+ MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_PS_UNBUFFERED_SETS);
result = stmt->result;
result->m.use_result(stmt->result, TRUE);
@@ -1018,16 +1026,15 @@ MYSQLND_METHOD(mysqlnd_stmt, use_result)(MYSQLND_STMT * s)
/* }}} */
-#define STMT_ID_LENGTH 4
-
/* {{{ mysqlnd_fetch_row_cursor */
enum_func_status
-mysqlnd_fetch_stmt_row_cursor(MYSQLND_RES * result, void * param, unsigned int flags, zend_bool * fetched_anything)
+mysqlnd_fetch_stmt_row_cursor(MYSQLND_RES * result, void * param, const unsigned int flags, zend_bool * fetched_anything)
{
enum_func_status ret;
MYSQLND_STMT * s = (MYSQLND_STMT *) param;
- MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
- zend_uchar buf[STMT_ID_LENGTH /* statement id */ + 4 /* number of rows to fetch */];
+ MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
+ MYSQLND_CONN_DATA * conn = stmt? stmt->conn : NULL;
+ zend_uchar buf[MYSQLND_STMT_ID_LENGTH /* statement id */ + 4 /* number of rows to fetch */];
MYSQLND_PACKET_ROW * row_packet;
DBG_ENTER("mysqlnd_fetch_stmt_row_cursor");
@@ -1036,13 +1043,11 @@ mysqlnd_fetch_stmt_row_cursor(MYSQLND_RES * result, void * param, unsigned int f
DBG_ERR("no statement");
DBG_RETURN(FAIL);
}
-
DBG_INF_FMT("stmt=%lu flags=%u", stmt->stmt_id, flags);
if (stmt->state < MYSQLND_STMT_USER_FETCHING) {
/* Only initted - error */
- SET_CLIENT_ERROR(*stmt->conn->error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE,
- mysqlnd_out_of_sync);
+ SET_CLIENT_ERROR(conn->error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, mysqlnd_out_of_sync);
DBG_ERR("command out of sync");
DBG_RETURN(FAIL);
}
@@ -1050,28 +1055,38 @@ mysqlnd_fetch_stmt_row_cursor(MYSQLND_RES * result, void * param, unsigned int f
DBG_RETURN(FAIL);
}
- SET_EMPTY_ERROR(*stmt->error_info);
- SET_EMPTY_ERROR(*stmt->conn->error_info);
+ SET_EMPTY_ERROR(stmt->error_info);
+ SET_EMPTY_ERROR(conn->error_info);
int4store(buf, stmt->stmt_id);
- int4store(buf + STMT_ID_LENGTH, 1); /* for now fetch only one row */
+ int4store(buf + MYSQLND_STMT_ID_LENGTH, 1); /* for now fetch only one row */
+
+ {
+ const MYSQLND_CSTRING payload = {(const char*) buf, sizeof(buf)};
+ struct st_mysqlnd_protocol_command * command = conn->command_factory(COM_STMT_FETCH, conn, payload);
+ ret = FAIL;
+ if (command) {
+ ret = command->run(command);
+ command->free_command(command);
+ if (ret == FAIL) {
+ COPY_CLIENT_ERROR(stmt->error_info, *conn->error_info);
+ }
+ }
+ if (FAIL == ret) {
+ DBG_RETURN(FAIL);
+ }
- if (FAIL == stmt->conn->m->simple_command(stmt->conn, COM_STMT_FETCH, buf, sizeof(buf),
- PROT_LAST /* we will handle the response packet*/,
- FALSE, TRUE)) {
- COPY_CLIENT_ERROR(*stmt->error_info, *stmt->conn->error_info);
- DBG_RETURN(FAIL);
}
row_packet->skip_extraction = stmt->result_bind? FALSE:TRUE;
- memset(stmt->upsert_status, 0, sizeof(*stmt->upsert_status));
- if (PASS == (ret = PACKET_READ(row_packet, result->conn)) && !row_packet->eof) {
+ UPSERT_STATUS_RESET(stmt->upsert_status);
+ if (PASS == (ret = PACKET_READ(row_packet)) && !row_packet->eof) {
const MYSQLND_RES_METADATA * const meta = result->meta;
unsigned int i, field_count = result->field_count;
if (!row_packet->skip_extraction) {
- result->unbuf->m.free_last_data(result->unbuf, result->conn? result->conn->stats : NULL);
+ result->unbuf->m.free_last_data(result->unbuf, conn->stats);
result->unbuf->last_row_data = row_packet->fields;
result->unbuf->last_row_buffer = row_packet->row_buffer;
@@ -1082,8 +1097,8 @@ mysqlnd_fetch_stmt_row_cursor(MYSQLND_RES * result, void * param, unsigned int f
result->unbuf->last_row_data,
row_packet->field_count,
row_packet->fields_metadata,
- result->conn->options->int_and_float_native,
- result->conn->stats))
+ conn->options->int_and_float_native,
+ conn->stats))
{
DBG_RETURN(FAIL);
}
@@ -1127,38 +1142,36 @@ mysqlnd_fetch_stmt_row_cursor(MYSQLND_RES * result, void * param, unsigned int f
the bound variables. Thus we need to do part of what it does or Zend will
report leaks.
*/
- row_packet->row_buffer->free_chunk(row_packet->row_buffer);
+ row_packet->result_set_memory_pool->free_chunk(
+ row_packet->result_set_memory_pool, row_packet->row_buffer);
row_packet->row_buffer = NULL;
}
/* We asked for one row, the next one should be EOF, eat it */
- ret = PACKET_READ(row_packet, result->conn);
+ ret = PACKET_READ(row_packet);
if (row_packet->row_buffer) {
- row_packet->row_buffer->free_chunk(row_packet->row_buffer);
+ row_packet->result_set_memory_pool->free_chunk(
+ row_packet->result_set_memory_pool, row_packet->row_buffer);
row_packet->row_buffer = NULL;
}
- MYSQLND_INC_CONN_STATISTIC(stmt->conn->stats, STAT_ROWS_FETCHED_FROM_CLIENT_PS_CURSOR);
+ MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_ROWS_FETCHED_FROM_CLIENT_PS_CURSOR);
result->unbuf->row_count++;
*fetched_anything = TRUE;
} else {
*fetched_anything = FALSE;
+ UPSERT_STATUS_SET_WARNINGS(stmt->upsert_status, row_packet->warning_count);
+ UPSERT_STATUS_SET_WARNINGS(conn->upsert_status, row_packet->warning_count);
- stmt->upsert_status->warning_count =
- stmt->conn->upsert_status->warning_count =
- row_packet->warning_count;
-
- stmt->upsert_status->server_status =
- stmt->conn->upsert_status->server_status =
- row_packet->server_status;
+ UPSERT_STATUS_SET_SERVER_STATUS(stmt->upsert_status, row_packet->server_status);
+ UPSERT_STATUS_SET_SERVER_STATUS(conn->upsert_status, row_packet->server_status);
result->unbuf->eof_reached = row_packet->eof;
}
- stmt->upsert_status->warning_count =
- stmt->conn->upsert_status->warning_count =
- row_packet->warning_count;
- stmt->upsert_status->server_status =
- stmt->conn->upsert_status->server_status =
- row_packet->server_status;
+ UPSERT_STATUS_SET_WARNINGS(stmt->upsert_status, row_packet->warning_count);
+ UPSERT_STATUS_SET_WARNINGS(conn->upsert_status, row_packet->warning_count);
+
+ UPSERT_STATUS_SET_SERVER_STATUS(stmt->upsert_status, row_packet->server_status);
+ UPSERT_STATUS_SET_SERVER_STATUS(conn->upsert_status, row_packet->server_status);
DBG_INF_FMT("ret=%s fetched=%u server_status=%u warnings=%u eof=%u",
ret == PASS? "PASS":"FAIL", *fetched_anything,
@@ -1173,7 +1186,8 @@ mysqlnd_fetch_stmt_row_cursor(MYSQLND_RES * result, void * param, unsigned int f
static enum_func_status
MYSQLND_METHOD(mysqlnd_stmt, fetch)(MYSQLND_STMT * const s, zend_bool * const fetched_anything)
{
- MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
+ MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
+ MYSQLND_CONN_DATA * conn = stmt? stmt->conn : NULL;
enum_func_status ret;
DBG_ENTER("mysqlnd_stmt::fetch");
if (!stmt || !stmt->conn) {
@@ -1181,10 +1195,8 @@ MYSQLND_METHOD(mysqlnd_stmt, fetch)(MYSQLND_STMT * const s, zend_bool * const fe
}
DBG_INF_FMT("stmt=%lu", stmt->stmt_id);
- if (!stmt->result ||
- stmt->state < MYSQLND_STMT_WAITING_USE_OR_STORE) {
- SET_STMT_ERROR(stmt, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, mysqlnd_out_of_sync);
-
+ if (!stmt->result || stmt->state < MYSQLND_STMT_WAITING_USE_OR_STORE) {
+ SET_CLIENT_ERROR(stmt->error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, mysqlnd_out_of_sync);
DBG_ERR("command out of sync");
DBG_RETURN(FAIL);
} else if (stmt->state == MYSQLND_STMT_WAITING_USE_OR_STORE) {
@@ -1194,8 +1206,8 @@ MYSQLND_METHOD(mysqlnd_stmt, fetch)(MYSQLND_STMT * const s, zend_bool * const fe
}
stmt->state = MYSQLND_STMT_USER_FETCHING;
- SET_EMPTY_ERROR(*stmt->error_info);
- SET_EMPTY_ERROR(*stmt->conn->error_info);
+ SET_EMPTY_ERROR(stmt->error_info);
+ SET_EMPTY_ERROR(conn->error_info);
DBG_INF_FMT("result_bind=%p separated_once=%u", &stmt->result_bind, stmt->result_zvals_separated_once);
/*
@@ -1229,18 +1241,18 @@ MYSQLND_METHOD(mysqlnd_stmt, fetch)(MYSQLND_STMT * const s, zend_bool * const fe
static enum_func_status
MYSQLND_METHOD(mysqlnd_stmt, reset)(MYSQLND_STMT * const s)
{
- MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
enum_func_status ret = PASS;
- zend_uchar cmd_buf[STMT_ID_LENGTH /* statement id */];
+ MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
+ MYSQLND_CONN_DATA * conn = stmt? stmt->conn : NULL;
DBG_ENTER("mysqlnd_stmt::reset");
- if (!stmt || !stmt->conn) {
+ if (!stmt || !conn) {
DBG_RETURN(FAIL);
}
DBG_INF_FMT("stmt=%lu", stmt->stmt_id);
- SET_EMPTY_ERROR(*stmt->error_info);
- SET_EMPTY_ERROR(*stmt->conn->error_info);
+ SET_EMPTY_ERROR(stmt->error_info);
+ SET_EMPTY_ERROR(conn->error_info);
if (stmt->stmt_id) {
MYSQLND_CONN_DATA * conn = stmt->conn;
@@ -1263,12 +1275,18 @@ MYSQLND_METHOD(mysqlnd_stmt, reset)(MYSQLND_STMT * const s)
be separated before that.
*/
- int4store(cmd_buf, stmt->stmt_id);
- if (CONN_GET_STATE(conn) == CONN_READY &&
- FAIL == (ret = conn->m->simple_command(conn, COM_STMT_RESET, cmd_buf,
- sizeof(cmd_buf), PROT_OK_PACKET,
- FALSE, TRUE))) {
- COPY_CLIENT_ERROR(*stmt->error_info, *conn->error_info);
+ if (GET_CONNECTION_STATE(&conn->state) == CONN_READY) {
+ size_t stmt_id = stmt->stmt_id;
+ struct st_mysqlnd_protocol_command * command = stmt->conn->command_factory(COM_STMT_RESET, stmt->conn, stmt_id);
+ ret = FAIL;
+ if (command) {
+ ret = command->run(command);
+ command->free_command(command);
+
+ if (ret == FAIL) {
+ COPY_CLIENT_ERROR(stmt->error_info, *conn->error_info);
+ }
+ }
}
*stmt->upsert_status = *conn->upsert_status;
}
@@ -1282,11 +1300,12 @@ MYSQLND_METHOD(mysqlnd_stmt, reset)(MYSQLND_STMT * const s)
static enum_func_status
MYSQLND_METHOD(mysqlnd_stmt, flush)(MYSQLND_STMT * const s)
{
- MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
enum_func_status ret = PASS;
+ MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
+ MYSQLND_CONN_DATA * conn = stmt? stmt->conn : NULL;
DBG_ENTER("mysqlnd_stmt::flush");
- if (!stmt || !stmt->conn) {
+ if (!stmt || !conn) {
DBG_RETURN(FAIL);
}
DBG_INF_FMT("stmt=%lu", stmt->stmt_id);
@@ -1319,76 +1338,72 @@ MYSQLND_METHOD(mysqlnd_stmt, flush)(MYSQLND_STMT * const s)
/* {{{ mysqlnd_stmt::send_long_data */
static enum_func_status
MYSQLND_METHOD(mysqlnd_stmt, send_long_data)(MYSQLND_STMT * const s, unsigned int param_no,
- const char * const data, zend_ulong length)
+ const char * const data, zend_ulong data_length)
{
- MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
enum_func_status ret = FAIL;
- MYSQLND_CONN_DATA * conn;
+ MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
+ MYSQLND_CONN_DATA * conn = stmt? stmt->conn : NULL;
zend_uchar * cmd_buf;
- enum php_mysqlnd_server_command cmd = COM_STMT_SEND_LONG_DATA;
DBG_ENTER("mysqlnd_stmt::send_long_data");
- if (!stmt || !stmt->conn) {
+ if (!stmt || !conn) {
DBG_RETURN(FAIL);
}
- DBG_INF_FMT("stmt=%lu param_no=%u data_len=%lu", stmt->stmt_id, param_no, length);
+ DBG_INF_FMT("stmt=%lu param_no=%u data_len=%lu", stmt->stmt_id, param_no, data_length);
- conn = stmt->conn;
-
- SET_EMPTY_ERROR(*stmt->error_info);
- SET_EMPTY_ERROR(*stmt->conn->error_info);
+ SET_EMPTY_ERROR(stmt->error_info);
+ SET_EMPTY_ERROR(conn->error_info);
if (stmt->state < MYSQLND_STMT_PREPARED) {
- SET_STMT_ERROR(stmt, CR_NO_PREPARE_STMT, UNKNOWN_SQLSTATE, mysqlnd_stmt_not_prepared);
+ SET_CLIENT_ERROR(stmt->error_info, CR_NO_PREPARE_STMT, UNKNOWN_SQLSTATE, mysqlnd_stmt_not_prepared);
DBG_ERR("not prepared");
DBG_RETURN(FAIL);
}
if (!stmt->param_bind) {
- SET_STMT_ERROR(stmt, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, mysqlnd_out_of_sync);
+ SET_CLIENT_ERROR(stmt->error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, mysqlnd_out_of_sync);
DBG_ERR("command out of sync");
DBG_RETURN(FAIL);
}
if (param_no >= stmt->param_count) {
- SET_STMT_ERROR(stmt, CR_INVALID_PARAMETER_NO, UNKNOWN_SQLSTATE, "Invalid parameter number");
+ SET_CLIENT_ERROR(stmt->error_info, CR_INVALID_PARAMETER_NO, UNKNOWN_SQLSTATE, "Invalid parameter number");
DBG_ERR("invalid param_no");
DBG_RETURN(FAIL);
}
if (stmt->param_bind[param_no].type != MYSQL_TYPE_LONG_BLOB) {
- SET_STMT_ERROR(stmt, CR_INVALID_BUFFER_USE, UNKNOWN_SQLSTATE, mysqlnd_not_bound_as_blob);
+ SET_CLIENT_ERROR(stmt->error_info, CR_INVALID_BUFFER_USE, UNKNOWN_SQLSTATE, mysqlnd_not_bound_as_blob);
DBG_ERR("param_no is not of a blob type");
DBG_RETURN(FAIL);
}
- /*
- XXX: Unfortunately we have to allocate additional buffer to be able the
- additional data, which is like a header inside the payload.
- This should be optimised, but it will be a pervasive change, so
- conn->m->simple_command() will accept not a buffer, but actually MYSQLND_STRING*
- terminated by NULL, to send. If the strings are not big, we can collapse them
- on the buffer every connection has, but otherwise we will just send them
- one by one to the wire.
- */
-
- if (CONN_GET_STATE(conn) == CONN_READY) {
- size_t packet_len;
- cmd_buf = mnd_emalloc(packet_len = STMT_ID_LENGTH + 2 + length);
+ if (GET_CONNECTION_STATE(&conn->state) == CONN_READY) {
+ const size_t packet_len = MYSQLND_STMT_ID_LENGTH + 2 + data_length;
+ cmd_buf = mnd_emalloc(packet_len);
if (cmd_buf) {
stmt->param_bind[param_no].flags |= MYSQLND_PARAM_BIND_BLOB_USED;
int4store(cmd_buf, stmt->stmt_id);
- int2store(cmd_buf + STMT_ID_LENGTH, param_no);
- memcpy(cmd_buf + STMT_ID_LENGTH + 2, data, length);
+ int2store(cmd_buf + MYSQLND_STMT_ID_LENGTH, param_no);
+ memcpy(cmd_buf + MYSQLND_STMT_ID_LENGTH + 2, data, data_length);
- /* COM_STMT_SEND_LONG_DATA doesn't send an OK packet*/
- ret = conn->m->simple_command(conn, cmd, cmd_buf, packet_len, PROT_LAST , FALSE, TRUE);
- mnd_efree(cmd_buf);
- if (FAIL == ret) {
- COPY_CLIENT_ERROR(*stmt->error_info, *conn->error_info);
+ /* COM_STMT_SEND_LONG_DATA doesn't acknowledge with an OK packet */
+ {
+ const MYSQLND_CSTRING payload = {(const char *) cmd_buf, packet_len};
+ struct st_mysqlnd_protocol_command * command = conn->command_factory(COM_STMT_SEND_LONG_DATA, conn, payload);
+ ret = FAIL;
+ if (command) {
+ ret = command->run(command);
+ command->free_command(command);
+ if (ret == FAIL) {
+ COPY_CLIENT_ERROR(stmt->error_info, *conn->error_info);
+ }
+ }
}
+
+ mnd_efree(cmd_buf);
} else {
ret = FAIL;
- SET_OOM_ERROR(*stmt->error_info);
- SET_OOM_ERROR(*conn->error_info);
+ SET_OOM_ERROR(stmt->error_info);
+ SET_OOM_ERROR(conn->error_info);
}
/*
Cover protocol error: COM_STMT_SEND_LONG_DATA was designed to be quick and not
@@ -1410,12 +1425,12 @@ MYSQLND_METHOD(mysqlnd_stmt, send_long_data)(MYSQLND_STMT * const s, unsigned in
#if HAVE_USLEEP && !defined(PHP_WIN32)
usleep(120000);
#endif
- if ((packet_len = conn->net->m.consume_uneaten_data(conn->net, cmd))) {
+ if ((packet_len = conn->protocol_frame_codec->m.consume_uneaten_data(conn->protocol_frame_codec, COM_STMT_SEND_LONG_DATA))) {
php_error_docref(NULL, E_WARNING, "There was an error "
"while sending long data. Probably max_allowed_packet_size "
"is smaller than the data. You have to increase it or send "
"smaller chunks of data. Answer was "MYSQLND_SZ_T_SPEC" bytes long.", packet_len);
- SET_STMT_ERROR(stmt, CR_CONNECTION_ERROR, UNKNOWN_SQLSTATE,
+ SET_CLIENT_ERROR(stmt->error_info, CR_CONNECTION_ERROR, UNKNOWN_SQLSTATE,
"Server responded to COM_STMT_SEND_LONG_DATA.");
ret = FAIL;
}
@@ -1432,15 +1447,17 @@ MYSQLND_METHOD(mysqlnd_stmt, send_long_data)(MYSQLND_STMT * const s, unsigned in
static enum_func_status
MYSQLND_METHOD(mysqlnd_stmt, bind_parameters)(MYSQLND_STMT * const s, MYSQLND_PARAM_BIND * const param_bind)
{
- MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
+ MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
+ MYSQLND_CONN_DATA * conn = stmt? stmt->conn : NULL;
+
DBG_ENTER("mysqlnd_stmt::bind_param");
- if (!stmt || !stmt->conn) {
+ if (!stmt || !conn) {
DBG_RETURN(FAIL);
}
DBG_INF_FMT("stmt=%lu param_count=%u", stmt->stmt_id, stmt->param_count);
if (stmt->state < MYSQLND_STMT_PREPARED) {
- SET_STMT_ERROR(stmt, CR_NO_PREPARE_STMT, UNKNOWN_SQLSTATE, mysqlnd_stmt_not_prepared);
+ SET_CLIENT_ERROR(stmt->error_info, CR_NO_PREPARE_STMT, UNKNOWN_SQLSTATE, mysqlnd_stmt_not_prepared);
DBG_ERR("not prepared");
if (param_bind) {
s->m->free_parameter_bind(s, param_bind);
@@ -1448,14 +1465,14 @@ MYSQLND_METHOD(mysqlnd_stmt, bind_parameters)(MYSQLND_STMT * const s, MYSQLND_PA
DBG_RETURN(FAIL);
}
- SET_EMPTY_ERROR(*stmt->error_info);
- SET_EMPTY_ERROR(*stmt->conn->error_info);
+ SET_EMPTY_ERROR(stmt->error_info);
+ SET_EMPTY_ERROR(conn->error_info);
if (stmt->param_count) {
unsigned int i = 0;
if (!param_bind) {
- SET_STMT_ERROR(stmt, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, "Re-binding (still) not supported");
+ SET_CLIENT_ERROR(stmt->error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, "Re-binding (still) not supported");
DBG_ERR("Re-binding (still) not supported");
DBG_RETURN(FAIL);
} else if (stmt->param_bind) {
@@ -1501,26 +1518,28 @@ static enum_func_status
MYSQLND_METHOD(mysqlnd_stmt, bind_one_parameter)(MYSQLND_STMT * const s, unsigned int param_no,
zval * const zv, zend_uchar type)
{
- MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
+ MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
+ MYSQLND_CONN_DATA * conn = stmt? stmt->conn : NULL;
+
DBG_ENTER("mysqlnd_stmt::bind_one_parameter");
- if (!stmt || !stmt->conn) {
+ if (!stmt || !conn) {
DBG_RETURN(FAIL);
}
DBG_INF_FMT("stmt=%lu param_no=%u param_count=%u type=%u", stmt->stmt_id, param_no, stmt->param_count, type);
if (stmt->state < MYSQLND_STMT_PREPARED) {
- SET_STMT_ERROR(stmt, CR_NO_PREPARE_STMT, UNKNOWN_SQLSTATE, mysqlnd_stmt_not_prepared);
+ SET_CLIENT_ERROR(stmt->error_info, CR_NO_PREPARE_STMT, UNKNOWN_SQLSTATE, mysqlnd_stmt_not_prepared);
DBG_ERR("not prepared");
DBG_RETURN(FAIL);
}
if (param_no >= stmt->param_count) {
- SET_STMT_ERROR(stmt, CR_INVALID_PARAMETER_NO, UNKNOWN_SQLSTATE, "Invalid parameter number");
+ SET_CLIENT_ERROR(stmt->error_info, CR_INVALID_PARAMETER_NO, UNKNOWN_SQLSTATE, "Invalid parameter number");
DBG_ERR("invalid param_no");
DBG_RETURN(FAIL);
}
- SET_EMPTY_ERROR(*stmt->error_info);
- SET_EMPTY_ERROR(*stmt->conn->error_info);
+ SET_EMPTY_ERROR(stmt->error_info);
+ SET_EMPTY_ERROR(conn->error_info);
if (stmt->param_count) {
if (!stmt->param_bind) {
@@ -1555,21 +1574,23 @@ MYSQLND_METHOD(mysqlnd_stmt, bind_one_parameter)(MYSQLND_STMT * const s, unsigne
static enum_func_status
MYSQLND_METHOD(mysqlnd_stmt, refresh_bind_param)(MYSQLND_STMT * const s)
{
- MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
+ MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
+ MYSQLND_CONN_DATA * conn = stmt? stmt->conn : NULL;
+
DBG_ENTER("mysqlnd_stmt::refresh_bind_param");
- if (!stmt || !stmt->conn) {
+ if (!stmt || !conn) {
DBG_RETURN(FAIL);
}
DBG_INF_FMT("stmt=%lu param_count=%u", stmt->stmt_id, stmt->param_count);
if (stmt->state < MYSQLND_STMT_PREPARED) {
- SET_STMT_ERROR(stmt, CR_NO_PREPARE_STMT, UNKNOWN_SQLSTATE, mysqlnd_stmt_not_prepared);
+ SET_CLIENT_ERROR(stmt->error_info, CR_NO_PREPARE_STMT, UNKNOWN_SQLSTATE, mysqlnd_stmt_not_prepared);
DBG_ERR("not prepared");
DBG_RETURN(FAIL);
}
- SET_EMPTY_ERROR(*stmt->error_info);
- SET_EMPTY_ERROR(*stmt->conn->error_info);
+ SET_EMPTY_ERROR(stmt->error_info);
+ SET_EMPTY_ERROR(conn->error_info);
if (stmt->param_count) {
stmt->send_types_to_server = 1;
@@ -1584,15 +1605,17 @@ static enum_func_status
MYSQLND_METHOD(mysqlnd_stmt, bind_result)(MYSQLND_STMT * const s,
MYSQLND_RESULT_BIND * const result_bind)
{
- MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
+ MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
+ MYSQLND_CONN_DATA * conn = stmt? stmt->conn : NULL;
+
DBG_ENTER("mysqlnd_stmt::bind_result");
- if (!stmt || !stmt->conn) {
+ if (!stmt || !conn) {
DBG_RETURN(FAIL);
}
DBG_INF_FMT("stmt=%lu field_count=%u", stmt->stmt_id, stmt->field_count);
if (stmt->state < MYSQLND_STMT_PREPARED) {
- SET_STMT_ERROR(stmt, CR_NO_PREPARE_STMT, UNKNOWN_SQLSTATE, mysqlnd_stmt_not_prepared);
+ SET_CLIENT_ERROR(stmt->error_info, CR_NO_PREPARE_STMT, UNKNOWN_SQLSTATE, mysqlnd_stmt_not_prepared);
if (result_bind) {
s->m->free_result_bind(s, result_bind);
}
@@ -1600,8 +1623,8 @@ MYSQLND_METHOD(mysqlnd_stmt, bind_result)(MYSQLND_STMT * const s,
DBG_RETURN(FAIL);
}
- SET_EMPTY_ERROR(*stmt->error_info);
- SET_EMPTY_ERROR(*stmt->conn->error_info);
+ SET_EMPTY_ERROR(stmt->error_info);
+ SET_EMPTY_ERROR(conn->error_info);
if (stmt->field_count) {
unsigned int i = 0;
@@ -1640,27 +1663,29 @@ MYSQLND_METHOD(mysqlnd_stmt, bind_result)(MYSQLND_STMT * const s,
static enum_func_status
MYSQLND_METHOD(mysqlnd_stmt, bind_one_result)(MYSQLND_STMT * const s, unsigned int param_no)
{
- MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
+ MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
+ MYSQLND_CONN_DATA * conn = stmt? stmt->conn : NULL;
+
DBG_ENTER("mysqlnd_stmt::bind_result");
- if (!stmt || !stmt->conn) {
+ if (!stmt || !conn) {
DBG_RETURN(FAIL);
}
DBG_INF_FMT("stmt=%lu field_count=%u", stmt->stmt_id, stmt->field_count);
if (stmt->state < MYSQLND_STMT_PREPARED) {
- SET_STMT_ERROR(stmt, CR_NO_PREPARE_STMT, UNKNOWN_SQLSTATE, mysqlnd_stmt_not_prepared);
+ SET_CLIENT_ERROR(stmt->error_info, CR_NO_PREPARE_STMT, UNKNOWN_SQLSTATE, mysqlnd_stmt_not_prepared);
DBG_ERR("not prepared");
DBG_RETURN(FAIL);
}
if (param_no >= stmt->field_count) {
- SET_STMT_ERROR(stmt, CR_INVALID_PARAMETER_NO, UNKNOWN_SQLSTATE, "Invalid parameter number");
+ SET_CLIENT_ERROR(stmt->error_info, CR_INVALID_PARAMETER_NO, UNKNOWN_SQLSTATE, "Invalid parameter number");
DBG_ERR("invalid param_no");
DBG_RETURN(FAIL);
}
- SET_EMPTY_ERROR(*stmt->error_info);
- SET_EMPTY_ERROR(*stmt->conn->error_info);
+ SET_EMPTY_ERROR(stmt->error_info);
+ SET_EMPTY_ERROR(conn->error_info);
if (stmt->field_count) {
mysqlnd_stmt_separate_one_result_bind(s, param_no);
@@ -1691,8 +1716,8 @@ MYSQLND_METHOD(mysqlnd_stmt, bind_one_result)(MYSQLND_STMT * const s, unsigned i
static uint64_t
MYSQLND_METHOD(mysqlnd_stmt, insert_id)(const MYSQLND_STMT * const s)
{
- MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
- return stmt? stmt->upsert_status->last_insert_id : 0;
+ MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
+ return stmt? UPSERT_STATUS_GET_LAST_INSERT_ID(stmt->upsert_status) : 0;
}
/* }}} */
@@ -1701,8 +1726,8 @@ MYSQLND_METHOD(mysqlnd_stmt, insert_id)(const MYSQLND_STMT * const s)
static uint64_t
MYSQLND_METHOD(mysqlnd_stmt, affected_rows)(const MYSQLND_STMT * const s)
{
- MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
- return stmt? stmt->upsert_status->affected_rows : 0;
+ MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
+ return stmt? UPSERT_STATUS_GET_AFFECTED_ROWS(stmt->upsert_status) : 0;
}
/* }}} */
@@ -1711,7 +1736,7 @@ MYSQLND_METHOD(mysqlnd_stmt, affected_rows)(const MYSQLND_STMT * const s)
static uint64_t
MYSQLND_METHOD(mysqlnd_stmt, num_rows)(const MYSQLND_STMT * const s)
{
- MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
+ MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
return stmt && stmt->result? mysqlnd_num_rows(stmt->result):0;
}
/* }}} */
@@ -1721,8 +1746,8 @@ MYSQLND_METHOD(mysqlnd_stmt, num_rows)(const MYSQLND_STMT * const s)
static unsigned int
MYSQLND_METHOD(mysqlnd_stmt, warning_count)(const MYSQLND_STMT * const s)
{
- MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
- return stmt? stmt->upsert_status->warning_count : 0;
+ MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
+ return stmt? UPSERT_STATUS_GET_WARNINGS(stmt->upsert_status) : 0;
}
/* }}} */
@@ -1731,8 +1756,8 @@ MYSQLND_METHOD(mysqlnd_stmt, warning_count)(const MYSQLND_STMT * const s)
static unsigned int
MYSQLND_METHOD(mysqlnd_stmt, server_status)(const MYSQLND_STMT * const s)
{
- MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
- return stmt? stmt->upsert_status->server_status : 0;
+ MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
+ return stmt? UPSERT_STATUS_GET_SERVER_STATUS(stmt->upsert_status) : 0;
}
/* }}} */
@@ -1741,7 +1766,7 @@ MYSQLND_METHOD(mysqlnd_stmt, server_status)(const MYSQLND_STMT * const s)
static unsigned int
MYSQLND_METHOD(mysqlnd_stmt, field_count)(const MYSQLND_STMT * const s)
{
- MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
+ MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
return stmt? stmt->field_count : 0;
}
/* }}} */
@@ -1751,7 +1776,7 @@ MYSQLND_METHOD(mysqlnd_stmt, field_count)(const MYSQLND_STMT * const s)
static unsigned int
MYSQLND_METHOD(mysqlnd_stmt, param_count)(const MYSQLND_STMT * const s)
{
- MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
+ MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
return stmt? stmt->param_count : 0;
}
/* }}} */
@@ -1761,7 +1786,7 @@ MYSQLND_METHOD(mysqlnd_stmt, param_count)(const MYSQLND_STMT * const s)
static unsigned int
MYSQLND_METHOD(mysqlnd_stmt, errno)(const MYSQLND_STMT * const s)
{
- MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
+ MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
return stmt? stmt->error_info->error_no : 0;
}
/* }}} */
@@ -1771,7 +1796,7 @@ MYSQLND_METHOD(mysqlnd_stmt, errno)(const MYSQLND_STMT * const s)
static const char *
MYSQLND_METHOD(mysqlnd_stmt, error)(const MYSQLND_STMT * const s)
{
- MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
+ MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
return stmt? stmt->error_info->error : 0;
}
/* }}} */
@@ -1781,7 +1806,7 @@ MYSQLND_METHOD(mysqlnd_stmt, error)(const MYSQLND_STMT * const s)
static const char *
MYSQLND_METHOD(mysqlnd_stmt, sqlstate)(const MYSQLND_STMT * const s)
{
- MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
+ MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
return stmt && stmt->error_info->sqlstate[0] ? stmt->error_info->sqlstate:MYSQLND_SQLSTATE_NULL;
}
/* }}} */
@@ -1791,7 +1816,7 @@ MYSQLND_METHOD(mysqlnd_stmt, sqlstate)(const MYSQLND_STMT * const s)
static enum_func_status
MYSQLND_METHOD(mysqlnd_stmt, data_seek)(const MYSQLND_STMT * const s, uint64_t row)
{
- MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
+ MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
return stmt && stmt->result? stmt->result->m.seek_data(stmt->result, row) : FAIL;
}
/* }}} */
@@ -1801,7 +1826,7 @@ MYSQLND_METHOD(mysqlnd_stmt, data_seek)(const MYSQLND_STMT * const s, uint64_t r
static MYSQLND_RES *
MYSQLND_METHOD(mysqlnd_stmt, param_metadata)(MYSQLND_STMT * const s)
{
- MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
+ MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
if (!stmt || !stmt->param_count) {
return NULL;
}
@@ -1814,57 +1839,57 @@ MYSQLND_METHOD(mysqlnd_stmt, param_metadata)(MYSQLND_STMT * const s)
static MYSQLND_RES *
MYSQLND_METHOD(mysqlnd_stmt, result_metadata)(MYSQLND_STMT * const s)
{
- MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
- MYSQLND_RES *result;
+ MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
+ MYSQLND_CONN_DATA * conn = stmt? stmt->conn : NULL;
+ MYSQLND_RES * result_meta = NULL;
DBG_ENTER("mysqlnd_stmt::result_metadata");
- if (!stmt) {
+ if (!stmt || ! conn) {
DBG_RETURN(NULL);
}
DBG_INF_FMT("stmt=%u field_count=%u", stmt->stmt_id, stmt->field_count);
- if (!stmt->field_count || !stmt->conn || !stmt->result || !stmt->result->meta) {
+ if (!stmt->field_count || !stmt->result || !stmt->result->meta) {
DBG_INF("NULL");
DBG_RETURN(NULL);
}
if (stmt->update_max_length && stmt->result->stored_data) {
/* stored result, we have to update the max_length before we clone the meta data :( */
- stmt->result->stored_data->m.initialize_result_set_rest(stmt->result->stored_data, stmt->result->meta, stmt->conn->stats,
- stmt->conn->options->int_and_float_native);
+ stmt->result->stored_data->m.initialize_result_set_rest(stmt->result->stored_data,
+ stmt->result->meta,
+ conn->stats,
+ conn->options->int_and_float_native);
}
/*
TODO: This implementation is kind of a hack,
find a better way to do it. In different functions I have put
fuses to check for result->m.fetch_row() being NULL. This should
be handled in a better way.
-
- In the meantime we don't need a zval cache reference for this fake
- result set, so we don't get one.
*/
do {
- result = stmt->conn->m->result_init(stmt->field_count, stmt->persistent);
- if (!result) {
+ result_meta = conn->m->result_init(stmt->field_count, stmt->persistent);
+ if (!result_meta) {
break;
}
- result->type = MYSQLND_RES_NORMAL;
- result->unbuf = mysqlnd_result_unbuffered_init(stmt->field_count, TRUE, result->persistent);
- if (!result->unbuf) {
+ result_meta->type = MYSQLND_RES_NORMAL;
+ result_meta->unbuf = mysqlnd_result_unbuffered_init(stmt->field_count, TRUE, result_meta->persistent);
+ if (!result_meta->unbuf) {
break;
}
- result->unbuf->eof_reached = TRUE;
- result->meta = stmt->result->meta->m->clone_metadata(stmt->result->meta, FALSE);
- if (!result->meta) {
+ result_meta->unbuf->eof_reached = TRUE;
+ result_meta->meta = stmt->result->meta->m->clone_metadata(stmt->result->meta, FALSE);
+ if (!result_meta->meta) {
break;
}
- DBG_INF_FMT("result=%p", result);
- DBG_RETURN(result);
+ DBG_INF_FMT("result_meta=%p", result_meta);
+ DBG_RETURN(result_meta);
} while (0);
- SET_OOM_ERROR(*stmt->conn->error_info);
- if (result) {
- result->m.free_result(result, TRUE);
+ SET_OOM_ERROR(conn->error_info);
+ if (result_meta) {
+ result_meta->m.free_result(result_meta, TRUE);
}
DBG_RETURN(NULL);
}
@@ -1877,7 +1902,7 @@ MYSQLND_METHOD(mysqlnd_stmt, attr_set)(MYSQLND_STMT * const s,
enum mysqlnd_stmt_attr attr_type,
const void * const value)
{
- MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
+ MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
DBG_ENTER("mysqlnd_stmt::attr_set");
if (!stmt) {
DBG_RETURN(FAIL);
@@ -1897,7 +1922,7 @@ MYSQLND_METHOD(mysqlnd_stmt, attr_set)(MYSQLND_STMT * const s,
case STMT_ATTR_CURSOR_TYPE: {
unsigned int ival = *(unsigned int *) value;
if (ival > (zend_ulong) CURSOR_TYPE_READ_ONLY) {
- SET_STMT_ERROR(stmt, CR_NOT_IMPLEMENTED, UNKNOWN_SQLSTATE, "Not implemented");
+ SET_CLIENT_ERROR(stmt->error_info, CR_NOT_IMPLEMENTED, UNKNOWN_SQLSTATE, "Not implemented");
DBG_INF("FAIL");
DBG_RETURN(FAIL);
}
@@ -1909,7 +1934,7 @@ MYSQLND_METHOD(mysqlnd_stmt, attr_set)(MYSQLND_STMT * const s,
if (ival == 0) {
ival = MYSQLND_DEFAULT_PREFETCH_ROWS;
} else if (ival > 1) {
- SET_STMT_ERROR(stmt, CR_NOT_IMPLEMENTED, UNKNOWN_SQLSTATE, "Not implemented");
+ SET_CLIENT_ERROR(stmt->error_info, CR_NOT_IMPLEMENTED, UNKNOWN_SQLSTATE, "Not implemented");
DBG_INF("FAIL");
DBG_RETURN(FAIL);
}
@@ -1917,7 +1942,7 @@ MYSQLND_METHOD(mysqlnd_stmt, attr_set)(MYSQLND_STMT * const s,
break;
}
default:
- SET_STMT_ERROR(stmt, CR_NOT_IMPLEMENTED, UNKNOWN_SQLSTATE, "Not implemented");
+ SET_CLIENT_ERROR(stmt->error_info, CR_NOT_IMPLEMENTED, UNKNOWN_SQLSTATE, "Not implemented");
DBG_RETURN(FAIL);
}
DBG_INF("PASS");
@@ -1932,7 +1957,7 @@ MYSQLND_METHOD(mysqlnd_stmt, attr_get)(const MYSQLND_STMT * const s,
enum mysqlnd_stmt_attr attr_type,
void * const value)
{
- MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
+ MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
DBG_ENTER("mysqlnd_stmt::attr_set");
if (!stmt) {
DBG_RETURN(FAIL);
@@ -1963,9 +1988,11 @@ MYSQLND_METHOD(mysqlnd_stmt, attr_get)(const MYSQLND_STMT * const s,
static enum_func_status
MYSQLND_METHOD(mysqlnd_stmt, free_result)(MYSQLND_STMT * const s)
{
- MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
+ MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
+ MYSQLND_CONN_DATA * conn = stmt? stmt->conn : NULL;
+
DBG_ENTER("mysqlnd_stmt::free_result");
- if (!stmt || !stmt->conn) {
+ if (!stmt || !conn) {
DBG_RETURN(FAIL);
}
DBG_INF_FMT("stmt=%lu", stmt->stmt_id);
@@ -2006,7 +2033,7 @@ MYSQLND_METHOD(mysqlnd_stmt, free_result)(MYSQLND_STMT * const s)
}
/* Line is free! */
- CONN_SET_STATE(stmt->conn, CONN_READY);
+ SET_CONNECTION_STATE(&conn->state, CONN_READY);
DBG_RETURN(PASS);
}
@@ -2017,7 +2044,7 @@ MYSQLND_METHOD(mysqlnd_stmt, free_result)(MYSQLND_STMT * const s)
static void
mysqlnd_stmt_separate_result_bind(MYSQLND_STMT * const s)
{
- MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
+ MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
unsigned int i;
DBG_ENTER("mysqlnd_stmt_separate_result_bind");
@@ -2038,8 +2065,7 @@ mysqlnd_stmt_separate_result_bind(MYSQLND_STMT * const s)
for (i = 0; i < stmt->field_count; i++) {
/* Let's try with no cache */
if (stmt->result_bind[i].bound == TRUE) {
- DBG_INF_FMT("%u has refcount=%u", i,
- Z_REFCOUNTED(stmt->result_bind[i].zv)? Z_REFCOUNT(stmt->result_bind[i].zv) : 0);
+ DBG_INF_FMT("%u has refcount=%u", i, Z_REFCOUNTED(stmt->result_bind[i].zv)? Z_REFCOUNT(stmt->result_bind[i].zv) : 0);
zval_ptr_dtor(&stmt->result_bind[i].zv);
}
}
@@ -2054,9 +2080,9 @@ mysqlnd_stmt_separate_result_bind(MYSQLND_STMT * const s)
/* {{{ mysqlnd_stmt_separate_one_result_bind */
static void
-mysqlnd_stmt_separate_one_result_bind(MYSQLND_STMT * const s, unsigned int param_no)
+mysqlnd_stmt_separate_one_result_bind(MYSQLND_STMT * const s, const unsigned int param_no)
{
- MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
+ MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
DBG_ENTER("mysqlnd_stmt_separate_one_result_bind");
if (!stmt) {
DBG_VOID_RETURN;
@@ -2074,9 +2100,7 @@ mysqlnd_stmt_separate_one_result_bind(MYSQLND_STMT * const s, unsigned int param
*/
/* Let's try with no cache */
if (stmt->result_bind[param_no].bound == TRUE) {
- DBG_INF_FMT("%u has refcount=%u", param_no,
- Z_REFCOUNTED(stmt->result_bind[param_no].zv)?
- Z_REFCOUNT(stmt->result_bind[param_no].zv) : 0);
+ DBG_INF_FMT("%u has refcount=%u", param_no, Z_REFCOUNTED(stmt->result_bind[param_no].zv)? Z_REFCOUNT(stmt->result_bind[param_no].zv) : 0);
zval_ptr_dtor(&stmt->result_bind[param_no].zv);
}
@@ -2089,7 +2113,7 @@ mysqlnd_stmt_separate_one_result_bind(MYSQLND_STMT * const s, unsigned int param
static void
MYSQLND_METHOD(mysqlnd_stmt, free_stmt_result)(MYSQLND_STMT * const s)
{
- MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
+ MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
DBG_ENTER("mysqlnd_stmt::free_stmt_result");
if (!stmt) {
DBG_VOID_RETURN;
@@ -2120,7 +2144,7 @@ MYSQLND_METHOD(mysqlnd_stmt, free_stmt_result)(MYSQLND_STMT * const s)
static void
MYSQLND_METHOD(mysqlnd_stmt, free_stmt_content)(MYSQLND_STMT * const s)
{
- MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
+ MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
DBG_ENTER("mysqlnd_stmt::free_stmt_content");
if (!stmt) {
DBG_VOID_RETURN;
@@ -2152,25 +2176,22 @@ MYSQLND_METHOD(mysqlnd_stmt, free_stmt_content)(MYSQLND_STMT * const s)
/* }}} */
-/* {{{ mysqlnd_stmt::net_close */
+/* {{{ mysqlnd_stmt::close_on_server */
static enum_func_status
-MYSQLND_METHOD_PRIVATE(mysqlnd_stmt, net_close)(MYSQLND_STMT * const s, zend_bool implicit)
+MYSQLND_METHOD_PRIVATE(mysqlnd_stmt, close_on_server)(MYSQLND_STMT * const s, zend_bool implicit)
{
- MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
- MYSQLND_CONN_DATA * conn;
- zend_uchar cmd_buf[STMT_ID_LENGTH /* statement id */];
+ MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
+ MYSQLND_CONN_DATA * conn = stmt? stmt->conn : NULL;
enum_mysqlnd_collected_stats statistic = STAT_LAST;
- DBG_ENTER("mysqlnd_stmt::net_close");
- if (!stmt || !stmt->conn) {
+ DBG_ENTER("mysqlnd_stmt::close_on_server");
+ if (!stmt || !conn) {
DBG_RETURN(FAIL);
}
DBG_INF_FMT("stmt=%lu", stmt->stmt_id);
- conn = stmt->conn;
-
- SET_EMPTY_ERROR(*stmt->error_info);
- SET_EMPTY_ERROR(*stmt->conn->error_info);
+ SET_EMPTY_ERROR(stmt->error_info);
+ SET_EMPTY_ERROR(conn->error_info);
/*
If the user decided to close the statement right after execute()
@@ -2198,13 +2219,21 @@ MYSQLND_METHOD_PRIVATE(mysqlnd_stmt, net_close)(MYSQLND_STMT * const s, zend_boo
MYSQLND_INC_GLOBAL_STATISTIC(implicit == TRUE? STAT_FREE_RESULT_IMPLICIT:
STAT_FREE_RESULT_EXPLICIT);
- int4store(cmd_buf, stmt->stmt_id);
- if (CONN_GET_STATE(conn) == CONN_READY &&
- FAIL == conn->m->simple_command(conn, COM_STMT_CLOSE, cmd_buf, sizeof(cmd_buf),
- PROT_LAST /* COM_STMT_CLOSE doesn't send an OK packet*/,
- FALSE, TRUE)) {
- COPY_CLIENT_ERROR(*stmt->error_info, *conn->error_info);
- DBG_RETURN(FAIL);
+ if (GET_CONNECTION_STATE(&conn->state) == CONN_READY) {
+ enum_func_status ret = FAIL;
+ size_t stmt_id = stmt->stmt_id;
+ struct st_mysqlnd_protocol_command * command = conn->command_factory(COM_STMT_CLOSE, conn, stmt_id);
+ if (command) {
+ ret = command->run(command);
+ command->free_command(command);
+
+ if (ret == FAIL) {
+ COPY_CLIENT_ERROR(stmt->error_info, *conn->error_info);
+ }
+ }
+ if (ret == FAIL) {
+ DBG_RETURN(FAIL);
+ }
}
}
switch (stmt->execute_count) {
@@ -2228,8 +2257,8 @@ MYSQLND_METHOD_PRIVATE(mysqlnd_stmt, net_close)(MYSQLND_STMT * const s, zend_boo
s->m->free_stmt_content(s);
- if (stmt->conn) {
- stmt->conn->m->free_reference(stmt->conn);
+ if (conn) {
+ conn->m->free_reference(conn);
stmt->conn = NULL;
}
@@ -2252,7 +2281,7 @@ MYSQLND_METHOD(mysqlnd_stmt, dtor)(MYSQLND_STMT * const s, zend_bool implicit)
MYSQLND_INC_GLOBAL_STATISTIC(implicit == TRUE? STAT_STMT_CLOSE_IMPLICIT:
STAT_STMT_CLOSE_EXPLICIT);
- ret = s->m->net_close(s, implicit);
+ ret = s->m->close_on_server(s, implicit);
mnd_pefree(stmt, persistent);
}
mnd_pefree(s, persistent);
@@ -2267,7 +2296,7 @@ MYSQLND_METHOD(mysqlnd_stmt, dtor)(MYSQLND_STMT * const s, zend_bool implicit)
static MYSQLND_PARAM_BIND *
MYSQLND_METHOD(mysqlnd_stmt, alloc_param_bind)(MYSQLND_STMT * const s)
{
- MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
+ MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
DBG_ENTER("mysqlnd_stmt::alloc_param_bind");
if (!stmt) {
DBG_RETURN(NULL);
@@ -2281,7 +2310,7 @@ MYSQLND_METHOD(mysqlnd_stmt, alloc_param_bind)(MYSQLND_STMT * const s)
static MYSQLND_RESULT_BIND *
MYSQLND_METHOD(mysqlnd_stmt, alloc_result_bind)(MYSQLND_STMT * const s)
{
- MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
+ MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
DBG_ENTER("mysqlnd_stmt::alloc_result_bind");
if (!stmt) {
DBG_RETURN(NULL);
@@ -2295,7 +2324,7 @@ MYSQLND_METHOD(mysqlnd_stmt, alloc_result_bind)(MYSQLND_STMT * const s)
PHPAPI void
MYSQLND_METHOD(mysqlnd_stmt, free_parameter_bind)(MYSQLND_STMT * const s, MYSQLND_PARAM_BIND * param_bind)
{
- MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
+ MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
if (stmt) {
mnd_pefree(param_bind, stmt->persistent);
}
@@ -2307,7 +2336,7 @@ MYSQLND_METHOD(mysqlnd_stmt, free_parameter_bind)(MYSQLND_STMT * const s, MYSQLN
PHPAPI void
MYSQLND_METHOD(mysqlnd_stmt, free_result_bind)(MYSQLND_STMT * const s, MYSQLND_RESULT_BIND * result_bind)
{
- MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
+ MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
if (stmt) {
mnd_pefree(result_bind, stmt->persistent);
}
@@ -2328,7 +2357,7 @@ MYSQLND_CLASS_METHODS_START(mysqlnd_stmt)
MYSQLND_METHOD(mysqlnd_stmt, free_result),
MYSQLND_METHOD(mysqlnd_stmt, data_seek),
MYSQLND_METHOD(mysqlnd_stmt, reset),
- MYSQLND_METHOD_PRIVATE(mysqlnd_stmt, net_close),
+ MYSQLND_METHOD_PRIVATE(mysqlnd_stmt, close_on_server),
MYSQLND_METHOD(mysqlnd_stmt, dtor),
MYSQLND_METHOD(mysqlnd_stmt, fetch),
@@ -2371,18 +2400,6 @@ MYSQLND_CLASS_METHODS_START(mysqlnd_stmt)
MYSQLND_CLASS_METHODS_END;
-/* {{{ _mysqlnd_stmt_init */
-MYSQLND_STMT *
-_mysqlnd_stmt_init(MYSQLND_CONN_DATA * const conn)
-{
- MYSQLND_STMT * ret;
- DBG_ENTER("_mysqlnd_stmt_init");
- ret = MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_object_factory).get_prepared_statement(conn);
- DBG_RETURN(ret);
-}
-/* }}} */
-
-
/* {{{ _mysqlnd_init_ps_subsystem */
void _mysqlnd_init_ps_subsystem()
{
diff --git a/ext/mysqlnd/mysqlnd_ps.h b/ext/mysqlnd/mysqlnd_ps.h
new file mode 100644
index 0000000000..42361d0a7d
--- /dev/null
+++ b/ext/mysqlnd/mysqlnd_ps.h
@@ -0,0 +1,54 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 7 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 2006-2016 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: Andrey Hristov <andrey@php.net> |
+ | Ulf Wendel <uw@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#ifndef MYSQLND_PS_H
+#define MYSQLND_PS_H
+
+/* PS stuff */
+typedef void (*ps_field_fetch_func)(zval * zv, const MYSQLND_FIELD * const field, const unsigned int pack_len, const zend_uchar ** row);
+
+struct st_mysqlnd_perm_bind {
+ ps_field_fetch_func func;
+ /* should be signed int */
+ int pack_len;
+ unsigned int php_type;
+ zend_bool is_possibly_blob;
+ zend_bool can_ret_as_str_in_uni;
+};
+
+extern struct st_mysqlnd_perm_bind mysqlnd_ps_fetch_functions[MYSQL_TYPE_LAST + 1];
+
+enum_func_status mysqlnd_stmt_fetch_row_buffered(MYSQLND_RES * result, void * param, const unsigned int flags, zend_bool * fetched_anything);
+enum_func_status mysqlnd_fetch_stmt_row_cursor(MYSQLND_RES * result, void * param, const unsigned int flags, zend_bool * fetched_anything);
+
+void _mysqlnd_init_ps_subsystem();/* This one is private, mysqlnd_library_init() will call it */
+void _mysqlnd_init_ps_fetch_subsystem();
+
+void ps_fetch_from_1_to_8_bytes(zval * zv, const MYSQLND_FIELD * const field, const unsigned int pack_len, const zend_uchar ** row, unsigned int byte_count);
+
+#endif /* MYSQLND_PS_H */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: noet sw=4 ts=4 fdm=marker
+ * vim<600: noet sw=4 ts=4
+ */
diff --git a/ext/mysqlnd/mysqlnd_ps_codec.c b/ext/mysqlnd/mysqlnd_ps_codec.c
index e0b6c5630f..d989101060 100644
--- a/ext/mysqlnd/mysqlnd_ps_codec.c
+++ b/ext/mysqlnd/mysqlnd_ps_codec.c
@@ -14,18 +14,17 @@
+----------------------------------------------------------------------+
| Authors: Andrey Hristov <andrey@php.net> |
| Ulf Wendel <uw@php.net> |
- | Georg Richter <georg@php.net> |
+----------------------------------------------------------------------+
*/
#include "php.h"
#include "mysqlnd.h"
#include "mysqlnd_wireprotocol.h"
+#include "mysqlnd_connection.h"
+#include "mysqlnd_ps.h"
#include "mysqlnd_priv.h"
#include "mysqlnd_debug.h"
-#include "ext/mysqlnd/mysql_float_to_double.h"
-
-#define MYSQLND_SILENT
+#include "mysql_float_to_double.h"
enum mysqlnd_timestamp_type
@@ -54,8 +53,8 @@ struct st_mysqlnd_perm_bind mysqlnd_ps_fetch_functions[MYSQL_TYPE_LAST + 1];
/* {{{ ps_fetch_from_1_to_8_bytes */
void
-ps_fetch_from_1_to_8_bytes(zval * zv, const MYSQLND_FIELD * const field, unsigned int pack_len,
- zend_uchar ** row, unsigned int byte_count)
+ps_fetch_from_1_to_8_bytes(zval * zv, const MYSQLND_FIELD * const field, const unsigned int pack_len,
+ const zend_uchar ** row, unsigned int byte_count)
{
char tmp[22];
size_t tmp_len = 0;
@@ -127,7 +126,7 @@ ps_fetch_from_1_to_8_bytes(zval * zv, const MYSQLND_FIELD * const field, unsigne
/* {{{ ps_fetch_null */
static void
-ps_fetch_null(zval *zv, const MYSQLND_FIELD * const field, unsigned int pack_len, zend_uchar ** row)
+ps_fetch_null(zval *zv, const MYSQLND_FIELD * const field, const unsigned int pack_len, const zend_uchar ** row)
{
ZVAL_NULL(zv);
}
@@ -136,7 +135,7 @@ ps_fetch_null(zval *zv, const MYSQLND_FIELD * const field, unsigned int pack_len
/* {{{ ps_fetch_int8 */
static void
-ps_fetch_int8(zval * zv, const MYSQLND_FIELD * const field, unsigned int pack_len, zend_uchar ** row)
+ps_fetch_int8(zval * zv, const MYSQLND_FIELD * const field, const unsigned int pack_len, const zend_uchar ** row)
{
ps_fetch_from_1_to_8_bytes(zv, field, pack_len, row, 1);
}
@@ -145,7 +144,7 @@ ps_fetch_int8(zval * zv, const MYSQLND_FIELD * const field, unsigned int pack_le
/* {{{ ps_fetch_int16 */
static void
-ps_fetch_int16(zval * zv, const MYSQLND_FIELD * const field, unsigned int pack_len, zend_uchar ** row)
+ps_fetch_int16(zval * zv, const MYSQLND_FIELD * const field, const unsigned int pack_len, const zend_uchar ** row)
{
ps_fetch_from_1_to_8_bytes(zv, field, pack_len, row, 2);
}
@@ -154,7 +153,7 @@ ps_fetch_int16(zval * zv, const MYSQLND_FIELD * const field, unsigned int pack_l
/* {{{ ps_fetch_int32 */
static void
-ps_fetch_int32(zval * zv, const MYSQLND_FIELD * const field, unsigned int pack_len, zend_uchar ** row)
+ps_fetch_int32(zval * zv, const MYSQLND_FIELD * const field, const unsigned int pack_len, const zend_uchar ** row)
{
ps_fetch_from_1_to_8_bytes(zv, field, pack_len, row, 4);
}
@@ -163,7 +162,7 @@ ps_fetch_int32(zval * zv, const MYSQLND_FIELD * const field, unsigned int pack_l
/* {{{ ps_fetch_int64 */
static void
-ps_fetch_int64(zval * zv, const MYSQLND_FIELD * const field, unsigned int pack_len, zend_uchar ** row)
+ps_fetch_int64(zval * zv, const MYSQLND_FIELD * const field, const unsigned int pack_len, const zend_uchar ** row)
{
ps_fetch_from_1_to_8_bytes(zv, field, pack_len, row, 8);
}
@@ -172,7 +171,7 @@ ps_fetch_int64(zval * zv, const MYSQLND_FIELD * const field, unsigned int pack_l
/* {{{ ps_fetch_float */
static void
-ps_fetch_float(zval * zv, const MYSQLND_FIELD * const field, unsigned int pack_len, zend_uchar ** row)
+ps_fetch_float(zval * zv, const MYSQLND_FIELD * const field, const unsigned int pack_len, const zend_uchar ** row)
{
float fval;
double dval;
@@ -195,7 +194,7 @@ ps_fetch_float(zval * zv, const MYSQLND_FIELD * const field, unsigned int pack_l
/* {{{ ps_fetch_double */
static void
-ps_fetch_double(zval * zv, const MYSQLND_FIELD * const field, unsigned int pack_len, zend_uchar ** row)
+ps_fetch_double(zval * zv, const MYSQLND_FIELD * const field, const unsigned int pack_len, const zend_uchar ** row)
{
double value;
DBG_ENTER("ps_fetch_double");
@@ -210,7 +209,7 @@ ps_fetch_double(zval * zv, const MYSQLND_FIELD * const field, unsigned int pack_
/* {{{ ps_fetch_time */
static void
-ps_fetch_time(zval * zv, const MYSQLND_FIELD * const field, unsigned int pack_len, zend_uchar ** row)
+ps_fetch_time(zval * zv, const MYSQLND_FIELD * const field, const unsigned int pack_len, const zend_uchar ** row)
{
struct st_mysqlnd_time t;
zend_ulong length; /* First byte encodes the length*/
@@ -218,7 +217,7 @@ ps_fetch_time(zval * zv, const MYSQLND_FIELD * const field, unsigned int pack_le
DBG_ENTER("ps_fetch_time");
if ((length = php_mysqlnd_net_field_length(row))) {
- zend_uchar * to= *row;
+ const zend_uchar * to = *row;
t.time_type = MYSQLND_TIMESTAMP_TIME;
t.neg = (zend_bool) to[0];
@@ -253,7 +252,7 @@ ps_fetch_time(zval * zv, const MYSQLND_FIELD * const field, unsigned int pack_le
/* {{{ ps_fetch_date */
static void
-ps_fetch_date(zval * zv, const MYSQLND_FIELD * const field, unsigned int pack_len, zend_uchar ** row)
+ps_fetch_date(zval * zv, const MYSQLND_FIELD * const field, const unsigned int pack_len, const zend_uchar ** row)
{
struct st_mysqlnd_time t = {0};
zend_ulong length; /* First byte encodes the length*/
@@ -261,10 +260,10 @@ ps_fetch_date(zval * zv, const MYSQLND_FIELD * const field, unsigned int pack_le
DBG_ENTER("ps_fetch_date");
if ((length = php_mysqlnd_net_field_length(row))) {
- zend_uchar *to= *row;
+ const zend_uchar * to = *row;
- t.time_type= MYSQLND_TIMESTAMP_DATE;
- t.neg= 0;
+ t.time_type = MYSQLND_TIMESTAMP_DATE;
+ t.neg = 0;
t.second_part = t.hour = t.minute = t.second = 0;
@@ -290,7 +289,7 @@ ps_fetch_date(zval * zv, const MYSQLND_FIELD * const field, unsigned int pack_le
/* {{{ ps_fetch_datetime */
static void
-ps_fetch_datetime(zval * zv, const MYSQLND_FIELD * const field, unsigned int pack_len, zend_uchar ** row)
+ps_fetch_datetime(zval * zv, const MYSQLND_FIELD * const field, const unsigned int pack_len, const zend_uchar ** row)
{
struct st_mysqlnd_time t;
zend_ulong length; /* First byte encodes the length*/
@@ -298,10 +297,10 @@ ps_fetch_datetime(zval * zv, const MYSQLND_FIELD * const field, unsigned int pac
DBG_ENTER("ps_fetch_datetime");
if ((length = php_mysqlnd_net_field_length(row))) {
- zend_uchar * to = *row;
+ const zend_uchar * to = *row;
t.time_type = MYSQLND_TIMESTAMP_DATETIME;
- t.neg = 0;
+ t.neg = 0;
t.year = (unsigned int) sint2korr(to);
t.month = (unsigned int) to[2];
@@ -334,7 +333,7 @@ ps_fetch_datetime(zval * zv, const MYSQLND_FIELD * const field, unsigned int pac
/* {{{ ps_fetch_string */
static void
-ps_fetch_string(zval * zv, const MYSQLND_FIELD * const field, unsigned int pack_len, zend_uchar ** row)
+ps_fetch_string(zval * zv, const MYSQLND_FIELD * const field, const unsigned int pack_len, const zend_uchar ** row)
{
/*
For now just copy, before we make it possible
@@ -354,9 +353,9 @@ ps_fetch_string(zval * zv, const MYSQLND_FIELD * const field, unsigned int pack_
/* {{{ ps_fetch_bit */
static void
-ps_fetch_bit(zval * zv, const MYSQLND_FIELD * const field, unsigned int pack_len, zend_uchar ** row)
+ps_fetch_bit(zval * zv, const MYSQLND_FIELD * const field, const unsigned int pack_len, const zend_uchar ** row)
{
- zend_ulong length = php_mysqlnd_net_field_length(row);
+ const zend_ulong length = php_mysqlnd_net_field_length(row);
ps_fetch_from_1_to_8_bytes(zv, field, pack_len, row, length);
}
/* }}} */
@@ -550,7 +549,7 @@ mysqlnd_stmt_execute_check_n_enlarge_buffer(zend_uchar **buf, zend_uchar **p, si
size_t left = (*buf_len - (*p - *buf));
if (left < (needed_bytes + overalloc)) {
- size_t offset = *p - *buf;
+ const size_t offset = *p - *buf;
zend_uchar *tmp_buf;
*buf_len = offset + needed_bytes + overalloc;
tmp_buf = mnd_emalloc(*buf_len);
@@ -577,7 +576,7 @@ mysqlnd_stmt_execute_prepare_param_types(MYSQLND_STMT_DATA * stmt, zval ** copie
unsigned int i;
DBG_ENTER("mysqlnd_stmt_execute_prepare_param_types");
for (i = 0; i < stmt->param_count; i++) {
- short current_type = stmt->param_bind[i].type;
+ const short current_type = stmt->param_bind[i].type;
zval *parameter = &stmt->param_bind[i].zv;
ZVAL_DEREF(parameter);
@@ -586,7 +585,7 @@ mysqlnd_stmt_execute_prepare_param_types(MYSQLND_STMT_DATA * stmt, zval ** copie
if (Z_TYPE_P(parameter) != IS_LONG &&
PASS != mysqlnd_stmt_copy_it(copies_param, parameter, stmt->param_count, i))
{
- SET_OOM_ERROR(*stmt->error_info);
+ SET_OOM_ERROR(stmt->error_info);
goto end;
}
/*
@@ -694,7 +693,7 @@ mysqlnd_stmt_execute_calculate_param_values_size(MYSQLND_STMT_DATA * stmt, zval
/* Double binding of the same zval, make a copy */
if (!*copies_param || Z_ISUNDEF((*copies_param)[i])) {
if (PASS != mysqlnd_stmt_copy_it(copies_param, the_var, stmt->param_count, i)) {
- SET_OOM_ERROR(*stmt->error_info);
+ SET_OOM_ERROR(stmt->error_info);
goto end;
}
}
@@ -709,7 +708,7 @@ mysqlnd_stmt_execute_calculate_param_values_size(MYSQLND_STMT_DATA * stmt, zval
if (Z_TYPE_P(the_var) != IS_DOUBLE) {
if (!*copies_param || Z_ISUNDEF((*copies_param)[i])) {
if (PASS != mysqlnd_stmt_copy_it(copies_param, the_var, stmt->param_count, i)) {
- SET_OOM_ERROR(*stmt->error_info);
+ SET_OOM_ERROR(stmt->error_info);
goto end;
}
}
@@ -744,7 +743,7 @@ use_string:
if (Z_TYPE_P(the_var) != IS_STRING) {
if (!*copies_param || Z_ISUNDEF((*copies_param)[i])) {
if (PASS != mysqlnd_stmt_copy_it(copies_param, the_var, stmt->param_count, i)) {
- SET_OOM_ERROR(*stmt->error_info);
+ SET_OOM_ERROR(stmt->error_info);
goto end;
}
}
@@ -809,7 +808,7 @@ mysqlnd_stmt_execute_store_param_values(MYSQLND_STMT_DATA * stmt, zval * copies,
case MYSQL_TYPE_VAR_STRING:
send_string:
{
- size_t len = Z_STRLEN_P(data);
+ const size_t len = Z_STRLEN_P(data);
/* to is after p. The latter hasn't been moved */
*p = php_mysqlnd_net_store_length(*p, len);
memcpy(*p, Z_STRVAL_P(data), len);
@@ -844,7 +843,7 @@ mysqlnd_stmt_execute_store_params(MYSQLND_STMT * s, zend_uchar **buf, zend_uchar
{
unsigned int null_count = (stmt->param_count + 7) / 8;
if (FAIL == mysqlnd_stmt_execute_check_n_enlarge_buffer(buf, p, buf_len, provided_buffer, null_count)) {
- SET_OOM_ERROR(*stmt->error_info);
+ SET_OOM_ERROR(stmt->error_info);
goto end;
}
/* put `null` bytes */
@@ -870,7 +869,7 @@ mysqlnd_stmt_execute_store_params(MYSQLND_STMT * s, zend_uchar **buf, zend_uchar
if (stmt->send_types_to_server) {
if (FAIL == mysqlnd_stmt_execute_check_n_enlarge_buffer(buf, p, buf_len, provided_buffer, stmt->param_count * 2)) {
- SET_OOM_ERROR(*stmt->error_info);
+ SET_OOM_ERROR(stmt->error_info);
goto end;
}
mysqlnd_stmt_execute_store_types(stmt, copies, p);
@@ -886,7 +885,7 @@ mysqlnd_stmt_execute_store_params(MYSQLND_STMT * s, zend_uchar **buf, zend_uchar
/* 2.2 Enlarge the buffer, if needed */
if (FAIL == mysqlnd_stmt_execute_check_n_enlarge_buffer(buf, p, buf_len, provided_buffer, data_size)) {
- SET_OOM_ERROR(*stmt->error_info);
+ SET_OOM_ERROR(stmt->error_info);
goto end;
}
diff --git a/ext/mysqlnd/mysqlnd_read_buffer.c b/ext/mysqlnd/mysqlnd_read_buffer.c
new file mode 100644
index 0000000000..1d214c21db
--- /dev/null
+++ b/ext/mysqlnd/mysqlnd_read_buffer.c
@@ -0,0 +1,96 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 7 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 2006-2016 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: Andrey Hristov <andrey@php.net> |
+ | Ulf Wendel <uw@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#include "php.h"
+#include "mysqlnd.h"
+#include "mysqlnd_debug.h"
+#include "mysqlnd_read_buffer.h"
+
+
+/* {{{ mysqlnd_read_buffer_is_empty */
+static zend_bool
+mysqlnd_read_buffer_is_empty(const MYSQLND_READ_BUFFER * const buffer)
+{
+ return buffer->len? FALSE:TRUE;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_read_buffer_read */
+static void
+mysqlnd_read_buffer_read(MYSQLND_READ_BUFFER * buffer, const size_t count, zend_uchar * dest)
+{
+ if (buffer->len >= count) {
+ memcpy(dest, buffer->data + buffer->offset, count);
+ buffer->offset += count;
+ buffer->len -= count;
+ }
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_read_buffer_bytes_left */
+static size_t
+mysqlnd_read_buffer_bytes_left(const MYSQLND_READ_BUFFER * const buffer)
+{
+ return buffer->len;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_read_buffer_free */
+static void
+mysqlnd_read_buffer_free(MYSQLND_READ_BUFFER ** buffer)
+{
+ DBG_ENTER("mysqlnd_read_buffer_free");
+ if (*buffer) {
+ mnd_efree((*buffer)->data);
+ mnd_efree(*buffer);
+ *buffer = NULL;
+ }
+ DBG_VOID_RETURN;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_create_read_buffer */
+PHPAPI MYSQLND_READ_BUFFER *
+mysqlnd_create_read_buffer(const size_t count)
+{
+ MYSQLND_READ_BUFFER * ret = mnd_emalloc(sizeof(MYSQLND_READ_BUFFER));
+ DBG_ENTER("mysqlnd_create_read_buffer");
+ ret->is_empty = mysqlnd_read_buffer_is_empty;
+ ret->read = mysqlnd_read_buffer_read;
+ ret->bytes_left = mysqlnd_read_buffer_bytes_left;
+ ret->free_buffer = mysqlnd_read_buffer_free;
+ ret->data = mnd_emalloc(count);
+ ret->size = ret->len = count;
+ ret->offset = 0;
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: noet sw=4 ts=4 fdm=marker
+ * vim<600: noet sw=4 ts=4
+ */
diff --git a/ext/mysqlnd/mysqlnd_read_buffer.h b/ext/mysqlnd/mysqlnd_read_buffer.h
new file mode 100644
index 0000000000..f353f8557a
--- /dev/null
+++ b/ext/mysqlnd/mysqlnd_read_buffer.h
@@ -0,0 +1,25 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 7 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 2006-2016 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: Andrey Hristov <andrey@php.net> |
+ | Ulf Wendel <uw@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#ifndef MYSQLND_READ_BUFFER_H
+#define MYSQLND_READ_BUFFER_H
+
+PHPAPI MYSQLND_READ_BUFFER * mysqlnd_create_read_buffer(const size_t count);
+
+#endif /* MYSQLND_READ_BUFFER_H */
diff --git a/ext/mysqlnd/mysqlnd_result.c b/ext/mysqlnd/mysqlnd_result.c
index 84985f1992..c4aa537eb0 100644
--- a/ext/mysqlnd/mysqlnd_result.c
+++ b/ext/mysqlnd/mysqlnd_result.c
@@ -14,7 +14,6 @@
+----------------------------------------------------------------------+
| Authors: Andrey Hristov <andrey@php.net> |
| Ulf Wendel <uw@php.net> |
- | Georg Richter <georg@php.net> |
+----------------------------------------------------------------------+
*/
@@ -22,6 +21,7 @@
#include "mysqlnd.h"
#include "mysqlnd_wireprotocol.h"
#include "mysqlnd_block_alloc.h"
+#include "mysqlnd_connection.h"
#include "mysqlnd_priv.h"
#include "mysqlnd_result.h"
#include "mysqlnd_result_meta.h"
@@ -29,18 +29,16 @@
#include "mysqlnd_debug.h"
#include "mysqlnd_ext_plugin.h"
-#define MYSQLND_SILENT
-
/* {{{ mysqlnd_result_buffered_zval::initialize_result_set_rest */
static enum_func_status
-MYSQLND_METHOD(mysqlnd_result_buffered_zval, initialize_result_set_rest)(MYSQLND_RES_BUFFERED * const result, MYSQLND_RES_METADATA * const meta,
- MYSQLND_STATS * stats, zend_bool int_and_float_native)
+MYSQLND_METHOD(mysqlnd_result_buffered_zval, initialize_result_set_rest)(MYSQLND_RES_BUFFERED * const result,
+ MYSQLND_RES_METADATA * const meta,
+ MYSQLND_STATS * stats,
+ zend_bool int_and_float_native)
{
- unsigned int i;
enum_func_status ret = PASS;
const unsigned int field_count = meta->field_count;
const uint64_t row_count = result->row_count;
- enum_func_status rc;
zval *data_begin = ((MYSQLND_RES_BUFFERED_ZVAL *) result)->data;
zval *data_cursor = data_begin;
@@ -52,25 +50,27 @@ MYSQLND_METHOD(mysqlnd_result_buffered_zval, initialize_result_set_rest)(MYSQLND
}
while ((data_cursor - data_begin) < (int)(row_count * field_count)) {
if (Z_ISUNDEF(data_cursor[0])) {
- rc = result->m.row_decoder(result->row_buffers[(data_cursor - data_begin) / field_count],
- data_cursor,
- field_count,
- meta->fields,
- int_and_float_native,
- stats);
+ unsigned int i;
+ const size_t current_row_num = (data_cursor - data_begin) / field_count;
+ enum_func_status rc = result->m.row_decoder(result->row_buffers[current_row_num],
+ data_cursor,
+ field_count,
+ meta->fields,
+ int_and_float_native,
+ stats);
if (rc != PASS) {
ret = FAIL;
break;
}
- result->initialized_rows++;
- for (i = 0; i < field_count; i++) {
+ ++result->initialized_rows;
+ for (i = 0; i < field_count; ++i) {
/*
NULL fields are 0 length, 0 is not more than 0
String of zero size, definitely can't be the next max_length.
Thus for NULL and zero-length we are quite efficient.
*/
if (Z_TYPE(data_cursor[i]) == IS_STRING) {
- zend_ulong len = Z_STRLEN(data_cursor[i]);
+ const size_t len = Z_STRLEN(data_cursor[i]);
if (meta->fields[i].max_length < len) {
meta->fields[i].max_length = len;
}
@@ -86,8 +86,10 @@ MYSQLND_METHOD(mysqlnd_result_buffered_zval, initialize_result_set_rest)(MYSQLND
/* {{{ mysqlnd_result_buffered_c::initialize_result_set_rest */
static enum_func_status
-MYSQLND_METHOD(mysqlnd_result_buffered_c, initialize_result_set_rest)(MYSQLND_RES_BUFFERED * const result, MYSQLND_RES_METADATA * const meta,
- MYSQLND_STATS * stats, zend_bool int_and_float_native)
+MYSQLND_METHOD(mysqlnd_result_buffered_c, initialize_result_set_rest)(MYSQLND_RES_BUFFERED * const result,
+ MYSQLND_RES_METADATA * const meta,
+ MYSQLND_STATS * stats,
+ zend_bool int_and_float_native)
{
unsigned int i;
enum_func_status ret = PASS;
@@ -125,7 +127,7 @@ MYSQLND_METHOD(mysqlnd_result_buffered_c, initialize_result_set_rest)(MYSQLND_RE
Thus for NULL and zero-length we are quite efficient.
*/
if (Z_TYPE(current_row[i]) == IS_STRING) {
- zend_ulong len = Z_STRLEN(current_row[i]);
+ const size_t len = Z_STRLEN(current_row[i]);
if (meta->fields[i].max_length < len) {
meta->fields[i].max_length = len;
}
@@ -164,7 +166,8 @@ MYSQLND_METHOD(mysqlnd_result_unbuffered, free_last_data)(MYSQLND_RES_UNBUFFERED
if (unbuf->last_row_buffer) {
DBG_INF("Freeing last row buffer");
/* Nothing points to this buffer now, free it */
- unbuf->last_row_buffer->free_chunk(unbuf->last_row_buffer);
+ unbuf->result_set_memory_pool->free_chunk(
+ unbuf->result_set_memory_pool, unbuf->last_row_buffer);
unbuf->last_row_buffer = NULL;
}
@@ -213,7 +216,7 @@ MYSQLND_METHOD(mysqlnd_result_buffered_zval, free_result)(MYSQLND_RES_BUFFERED_Z
set->data = NULL; /* prevent double free if following loop is interrupted */
if (data) {
- unsigned int field_count = set->field_count;
+ const unsigned int field_count = set->field_count;
int64_t row;
for (row = set->row_count - 1; row >= 0; row--) {
@@ -251,6 +254,7 @@ static void
MYSQLND_METHOD(mysqlnd_result_buffered, free_result)(MYSQLND_RES_BUFFERED * const set)
{
int64_t row;
+ MYSQLND_MEMORY_POOL * pool;
DBG_ENTER("mysqlnd_result_buffered::free_result");
DBG_INF_FMT("Freeing "MYSQLND_LLU_SPEC" row(s)", set->row_count);
@@ -261,9 +265,10 @@ MYSQLND_METHOD(mysqlnd_result_buffered, free_result)(MYSQLND_RES_BUFFERED * cons
MYSQLND_METHOD(mysqlnd_result_buffered_c, free_result)((MYSQLND_RES_BUFFERED_C *) set);
}
+ pool = set->result_set_memory_pool;
for (row = set->row_count - 1; row >= 0; row--) {
MYSQLND_MEMORY_POOL_CHUNK *current_buffer = set->row_buffers[row];
- current_buffer->free_chunk(current_buffer);
+ pool->free_chunk(pool, current_buffer);
}
if (set->lengths) {
@@ -281,7 +286,6 @@ MYSQLND_METHOD(mysqlnd_result_buffered, free_result)(MYSQLND_RES_BUFFERED * cons
set->result_set_memory_pool = NULL;
}
-
set->row_count = 0;
mnd_pefree(set, set->persistent);
@@ -306,7 +310,6 @@ MYSQLND_METHOD(mysqlnd_res, free_result_buffers)(MYSQLND_RES * result)
result->stored_data = NULL;
}
-
DBG_VOID_RETURN;
}
/* }}} */
@@ -370,7 +373,7 @@ MYSQLND_METHOD(mysqlnd_res, read_result_metadata)(MYSQLND_RES * result, MYSQLND_
result->meta = result->m.result_meta_init(result->field_count, result->persistent);
if (!result->meta) {
- SET_OOM_ERROR(*conn->error_info);
+ SET_OOM_ERROR(conn->error_info);
DBG_RETURN(FAIL);
}
@@ -400,26 +403,27 @@ MYSQLND_METHOD(mysqlnd_res, read_result_metadata)(MYSQLND_RES * result, MYSQLND_
enum_func_status
mysqlnd_query_read_result_set_header(MYSQLND_CONN_DATA * conn, MYSQLND_STMT * s)
{
- MYSQLND_STMT_DATA * stmt = s ? s->data:NULL;
enum_func_status ret;
+ MYSQLND_STMT_DATA * stmt = s ? s->data : NULL;
MYSQLND_PACKET_RSET_HEADER * rset_header = NULL;
MYSQLND_PACKET_EOF * fields_eof = NULL;
+ const zend_bool persistent = conn->persistent;
DBG_ENTER("mysqlnd_query_read_result_set_header");
DBG_INF_FMT("stmt=%lu", stmt? stmt->stmt_id:0);
ret = FAIL;
do {
- rset_header = conn->protocol->m.get_rset_header_packet(conn->protocol, FALSE);
+ rset_header = conn->payload_decoder_factory->m.get_rset_header_packet(conn->payload_decoder_factory, FALSE);
if (!rset_header) {
- SET_OOM_ERROR(*conn->error_info);
+ SET_OOM_ERROR(conn->error_info);
ret = FAIL;
break;
}
- SET_ERROR_AFF_ROWS(conn);
+ UPSERT_STATUS_SET_AFFECTED_ROWS_TO_ERROR(conn->upsert_status);
- if (FAIL == (ret = PACKET_READ(rset_header, conn))) {
+ if (FAIL == (ret = PACKET_READ(rset_header))) {
php_error_docref(NULL, E_WARNING, "Error reading result set's header");
break;
}
@@ -434,16 +438,16 @@ mysqlnd_query_read_result_set_header(MYSQLND_CONN_DATA * conn, MYSQLND_STMT * s)
a multi-statement or a stored procedure, so it should be
safe to unconditionally turn off the flag here.
*/
- conn->upsert_status->server_status &= ~SERVER_MORE_RESULTS_EXISTS;
+ UPSERT_STATUS_SET_SERVER_STATUS(conn->upsert_status, UPSERT_STATUS_GET_SERVER_STATUS(conn->upsert_status) & ~SERVER_MORE_RESULTS_EXISTS);
/*
This will copy the error code and the messages, as they
are buffers in the struct
*/
- COPY_CLIENT_ERROR(*conn->error_info, rset_header->error_info);
+ COPY_CLIENT_ERROR(conn->error_info, rset_header->error_info);
ret = FAIL;
DBG_ERR_FMT("error=%s", rset_header->error_info.error);
/* Return back from CONN_QUERY_SENT */
- CONN_SET_STATE(conn, CONN_READY);
+ SET_CONNECTION_STATE(&conn->state, CONN_READY);
break;
}
conn->error_info->error_no = 0;
@@ -454,9 +458,9 @@ mysqlnd_query_read_result_set_header(MYSQLND_CONN_DATA * conn, MYSQLND_STMT * s)
DBG_INF("LOAD DATA");
conn->last_query_type = QUERY_LOAD_LOCAL;
conn->field_count = 0; /* overwrite previous value, or the last value could be used and lead to bug#53503 */
- CONN_SET_STATE(conn, CONN_SENDING_LOAD_DATA);
- ret = mysqlnd_handle_local_infile(conn, rset_header->info_or_local_file, &is_warning);
- CONN_SET_STATE(conn, (ret == PASS || is_warning == TRUE)? CONN_READY:CONN_QUIT_SENT);
+ SET_CONNECTION_STATE(&conn->state, CONN_SENDING_LOAD_DATA);
+ ret = mysqlnd_handle_local_infile(conn, rset_header->info_or_local_file.s, &is_warning);
+ SET_CONNECTION_STATE(&conn->state, (ret == PASS || is_warning == TRUE)? CONN_READY:CONN_QUIT_SENT);
MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_NON_RSET_QUERY);
break;
}
@@ -464,19 +468,19 @@ mysqlnd_query_read_result_set_header(MYSQLND_CONN_DATA * conn, MYSQLND_STMT * s)
DBG_INF("UPSERT");
conn->last_query_type = QUERY_UPSERT;
conn->field_count = rset_header->field_count;
- memset(conn->upsert_status, 0, sizeof(*conn->upsert_status));
- conn->upsert_status->warning_count = rset_header->warning_count;
- conn->upsert_status->server_status = rset_header->server_status;
- conn->upsert_status->affected_rows = rset_header->affected_rows;
- conn->upsert_status->last_insert_id = rset_header->last_insert_id;
- SET_NEW_MESSAGE(conn->last_message, conn->last_message_len,
- rset_header->info_or_local_file, rset_header->info_or_local_file_len,
- conn->persistent);
+ UPSERT_STATUS_RESET(conn->upsert_status);
+ UPSERT_STATUS_SET_WARNINGS(conn->upsert_status, rset_header->warning_count);
+ UPSERT_STATUS_SET_SERVER_STATUS(conn->upsert_status, rset_header->server_status);
+ UPSERT_STATUS_SET_AFFECTED_ROWS(conn->upsert_status, rset_header->affected_rows);
+ UPSERT_STATUS_SET_LAST_INSERT_ID(conn->upsert_status, rset_header->last_insert_id);
+ SET_NEW_MESSAGE(conn->last_message.s, conn->last_message.l,
+ rset_header->info_or_local_file.s, rset_header->info_or_local_file.l,
+ persistent);
/* Result set can follow UPSERT statement, check server_status */
- if (conn->upsert_status->server_status & SERVER_MORE_RESULTS_EXISTS) {
- CONN_SET_STATE(conn, CONN_NEXT_RESULT_PENDING);
+ if (UPSERT_STATUS_GET_SERVER_STATUS(conn->upsert_status) & SERVER_MORE_RESULTS_EXISTS) {
+ SET_CONNECTION_STATE(&conn->state, CONN_NEXT_RESULT_PENDING);
} else {
- CONN_SET_STATE(conn, CONN_READY);
+ SET_CONNECTION_STATE(&conn->state, CONN_READY);
}
ret = PASS;
MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_NON_RSET_QUERY);
@@ -486,19 +490,19 @@ mysqlnd_query_read_result_set_header(MYSQLND_CONN_DATA * conn, MYSQLND_STMT * s)
enum_mysqlnd_collected_stats statistic = STAT_LAST;
DBG_INF("Result set pending");
- SET_EMPTY_MESSAGE(conn->last_message, conn->last_message_len, conn->persistent);
+ SET_EMPTY_MESSAGE(conn->last_message.s, conn->last_message.l, persistent);
MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_RSET_QUERY);
- memset(conn->upsert_status, 0, sizeof(*conn->upsert_status));
+ UPSERT_STATUS_RESET(conn->upsert_status);
/* restore after zeroing */
- SET_ERROR_AFF_ROWS(conn);
+ UPSERT_STATUS_SET_AFFECTED_ROWS_TO_ERROR(conn->upsert_status);
conn->last_query_type = QUERY_SELECT;
- CONN_SET_STATE(conn, CONN_FETCHING_DATA);
+ SET_CONNECTION_STATE(&conn->state, CONN_FETCHING_DATA);
/* PS has already allocated it */
conn->field_count = rset_header->field_count;
if (!stmt) {
- result = conn->current_result = conn->m->result_init(rset_header->field_count, conn->persistent);
+ result = conn->current_result = conn->m->result_init(rset_header->field_count, persistent);
} else {
if (!stmt->result) {
DBG_INF("This is 'SHOW'/'EXPLAIN'-like query.");
@@ -525,7 +529,7 @@ mysqlnd_query_read_result_set_header(MYSQLND_CONN_DATA * conn, MYSQLND_STMT * s)
result = stmt->result;
}
if (!result) {
- SET_OOM_ERROR(*conn->error_info);
+ SET_OOM_ERROR(conn->error_info);
ret = FAIL;
break;
}
@@ -541,13 +545,13 @@ mysqlnd_query_read_result_set_header(MYSQLND_CONN_DATA * conn, MYSQLND_STMT * s)
}
/* Check for SERVER_STATUS_MORE_RESULTS if needed */
- fields_eof = conn->protocol->m.get_eof_packet(conn->protocol, FALSE);
+ fields_eof = conn->payload_decoder_factory->m.get_eof_packet(conn->payload_decoder_factory, FALSE);
if (!fields_eof) {
- SET_OOM_ERROR(*conn->error_info);
+ SET_OOM_ERROR(conn->error_info);
ret = FAIL;
break;
}
- if (FAIL == (ret = PACKET_READ(fields_eof, conn))) {
+ if (FAIL == (ret = PACKET_READ(fields_eof))) {
DBG_ERR("Error occurred while reading the EOF packet");
result->m.free_result_contents(result);
mnd_efree(result);
@@ -555,12 +559,17 @@ mysqlnd_query_read_result_set_header(MYSQLND_CONN_DATA * conn, MYSQLND_STMT * s)
conn->current_result = NULL;
} else {
stmt->result = NULL;
+ /* XXX: This will crash, because we will null also the methods.
+ But seems it happens in extreme cases or doesn't. Should be fixed by exporting a function
+ (from mysqlnd_driver.c?) to do the reset.
+ This is done also in mysqlnd_ps.c
+ */
memset(stmt, 0, sizeof(*stmt));
stmt->state = MYSQLND_STMT_INITTED;
}
} else {
DBG_INF_FMT("warnings=%u server_status=%u", fields_eof->warning_count, fields_eof->server_status);
- conn->upsert_status->warning_count = fields_eof->warning_count;
+ UPSERT_STATUS_SET_WARNINGS(conn->upsert_status, fields_eof->warning_count);
/*
If SERVER_MORE_RESULTS_EXISTS is set then this is either MULTI_QUERY or a CALL()
The first packet after sending the query/com_execute has the bit set only
@@ -568,7 +577,7 @@ mysqlnd_query_read_result_set_header(MYSQLND_CONN_DATA * conn, MYSQLND_STMT * s)
will include many result sets. What actually matters are the bits set at the end
of every result set (the EOF packet).
*/
- conn->upsert_status->server_status = fields_eof->server_status;
+ UPSERT_STATUS_SET_SERVER_STATUS(conn->upsert_status, fields_eof->server_status);
if (fields_eof->server_status & SERVER_QUERY_NO_GOOD_INDEX_USED) {
statistic = STAT_BAD_INDEX_USED;
} else if (fields_eof->server_status & SERVER_QUERY_NO_INDEX_USED) {
@@ -598,7 +607,7 @@ mysqlnd_query_read_result_set_header(MYSQLND_CONN_DATA * conn, MYSQLND_STMT * s)
of PHP, to be called as separate function. But let's have it for
completeness.
*/
-static zend_ulong *
+static const size_t *
MYSQLND_METHOD(mysqlnd_result_buffered_zval, fetch_lengths)(MYSQLND_RES_BUFFERED * const result)
{
const MYSQLND_RES_BUFFERED_ZVAL * set = (MYSQLND_RES_BUFFERED_ZVAL *) result;
@@ -630,7 +639,7 @@ MYSQLND_METHOD(mysqlnd_result_buffered_zval, fetch_lengths)(MYSQLND_RES_BUFFERED
of PHP, to be called as separate function. But let's have it for
completeness.
*/
-static zend_ulong *
+static const size_t *
MYSQLND_METHOD(mysqlnd_result_buffered_c, fetch_lengths)(MYSQLND_RES_BUFFERED * const result)
{
const MYSQLND_RES_BUFFERED_C * set = (MYSQLND_RES_BUFFERED_C *) result;
@@ -647,7 +656,7 @@ MYSQLND_METHOD(mysqlnd_result_buffered_c, fetch_lengths)(MYSQLND_RES_BUFFERED *
/* {{{ mysqlnd_result_unbuffered::fetch_lengths */
-static zend_ulong *
+static const size_t *
MYSQLND_METHOD(mysqlnd_result_unbuffered, fetch_lengths)(MYSQLND_RES_UNBUFFERED * const result)
{
/* simulate output of libmysql */
@@ -657,10 +666,10 @@ MYSQLND_METHOD(mysqlnd_result_unbuffered, fetch_lengths)(MYSQLND_RES_UNBUFFERED
/* {{{ mysqlnd_res::fetch_lengths */
-static zend_ulong *
+static const size_t *
MYSQLND_METHOD(mysqlnd_res, fetch_lengths)(MYSQLND_RES * const result)
{
- zend_ulong * ret;
+ const size_t * ret;
DBG_ENTER("mysqlnd_res::fetch_lengths");
ret = result->stored_data && result->stored_data->m.fetch_lengths ?
result->stored_data->m.fetch_lengths(result->stored_data) :
@@ -680,7 +689,8 @@ MYSQLND_METHOD(mysqlnd_result_unbuffered, fetch_row_c)(MYSQLND_RES * result, voi
enum_func_status ret;
MYSQLND_ROW_C *row = (MYSQLND_ROW_C *) param;
MYSQLND_PACKET_ROW *row_packet = result->unbuf->row_packet;
- const MYSQLND_RES_METADATA * const meta = result->meta;
+ MYSQLND_RES_METADATA * const meta = result->meta;
+ MYSQLND_CONN_DATA * const conn = result->conn;
DBG_ENTER("mysqlnd_result_unbuffered::fetch_row_c");
@@ -689,8 +699,8 @@ MYSQLND_METHOD(mysqlnd_result_unbuffered, fetch_row_c)(MYSQLND_RES * result, voi
/* No more rows obviously */
DBG_RETURN(PASS);
}
- if (CONN_GET_STATE(result->conn) != CONN_FETCHING_DATA) {
- SET_CLIENT_ERROR(*result->conn->error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, mysqlnd_out_of_sync);
+ if (!conn || GET_CONNECTION_STATE(&conn->state) != CONN_FETCHING_DATA) {
+ SET_CLIENT_ERROR(conn->error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, mysqlnd_out_of_sync);
DBG_RETURN(FAIL);
}
if (!row_packet) {
@@ -704,15 +714,15 @@ MYSQLND_METHOD(mysqlnd_result_unbuffered, fetch_row_c)(MYSQLND_RES * result, voi
If we skip rows (row == NULL) we have to
result->m.unbuffered_free_last_data() before it. The function returns always true.
*/
- if (PASS == (ret = PACKET_READ(row_packet, result->conn)) && !row_packet->eof) {
- result->unbuf->m.free_last_data(result->unbuf, result->conn? result->conn->stats : NULL);
+ if (PASS == (ret = PACKET_READ(row_packet)) && !row_packet->eof) {
+ result->unbuf->m.free_last_data(result->unbuf, conn->stats);
result->unbuf->last_row_data = row_packet->fields;
result->unbuf->last_row_buffer = row_packet->row_buffer;
row_packet->fields = NULL;
row_packet->row_buffer = NULL;
- MYSQLND_INC_CONN_STATISTIC(result->conn->stats, STAT_ROWS_FETCHED_FROM_CLIENT_NORMAL_UNBUF);
+ MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_ROWS_FETCHED_FROM_CLIENT_NORMAL_UNBUF);
if (!row_packet->skip_extraction) {
unsigned int i, field_count = meta->field_count;
@@ -721,8 +731,8 @@ MYSQLND_METHOD(mysqlnd_result_unbuffered, fetch_row_c)(MYSQLND_RES * result, voi
result->unbuf->last_row_data,
field_count,
row_packet->fields_metadata,
- result->conn->options->int_and_float_native,
- result->conn->stats);
+ conn->options->int_and_float_native,
+ conn->stats);
if (PASS != rc) {
DBG_RETURN(FAIL);
}
@@ -730,11 +740,11 @@ MYSQLND_METHOD(mysqlnd_result_unbuffered, fetch_row_c)(MYSQLND_RES * result, voi
*row = mnd_malloc(field_count * sizeof(char *));
if (*row) {
MYSQLND_FIELD * field = meta->fields;
- zend_ulong * lengths = result->unbuf->lengths;
+ size_t * lengths = result->unbuf->lengths;
for (i = 0; i < field_count; i++, field++) {
zval * data = &result->unbuf->last_row_data[i];
- unsigned int len = (Z_TYPE_P(data) == IS_STRING)? Z_STRLEN_P(data) : 0;
+ const size_t len = (Z_TYPE_P(data) == IS_STRING)? Z_STRLEN_P(data) : 0;
/* BEGIN difference between normal normal fetch and _c */
if (Z_TYPE_P(data) != IS_NULL) {
@@ -754,7 +764,7 @@ MYSQLND_METHOD(mysqlnd_result_unbuffered, fetch_row_c)(MYSQLND_RES * result, voi
}
}
} else {
- SET_OOM_ERROR(*result->conn->error_info);
+ SET_OOM_ERROR(conn->error_info);
}
}
}
@@ -762,28 +772,29 @@ MYSQLND_METHOD(mysqlnd_result_unbuffered, fetch_row_c)(MYSQLND_RES * result, voi
*fetched_anything = TRUE;
} else if (ret == FAIL) {
if (row_packet->error_info.error_no) {
- COPY_CLIENT_ERROR(*result->conn->error_info, row_packet->error_info);
+ COPY_CLIENT_ERROR(conn->error_info, row_packet->error_info);
DBG_ERR_FMT("errorno=%u error=%s", row_packet->error_info.error_no, row_packet->error_info.error);
}
- CONN_SET_STATE(result->conn, CONN_READY);
+ SET_CONNECTION_STATE(&conn->state, CONN_READY);
result->unbuf->eof_reached = TRUE; /* so next time we won't get an error */
} else if (row_packet->eof) {
/* Mark the connection as usable again */
DBG_INF_FMT("warnings=%u server_status=%u", row_packet->warning_count, row_packet->server_status);
result->unbuf->eof_reached = TRUE;
- memset(result->conn->upsert_status, 0, sizeof(*result->conn->upsert_status));
- result->conn->upsert_status->warning_count = row_packet->warning_count;
- result->conn->upsert_status->server_status = row_packet->server_status;
+
+ UPSERT_STATUS_RESET(conn->upsert_status);
+ UPSERT_STATUS_SET_WARNINGS(conn->upsert_status, row_packet->warning_count);
+ UPSERT_STATUS_SET_SERVER_STATUS(conn->upsert_status, row_packet->server_status);
/*
result->row_packet will be cleaned when
destroying the result object
*/
- if (result->conn->upsert_status->server_status & SERVER_MORE_RESULTS_EXISTS) {
- CONN_SET_STATE(result->conn, CONN_NEXT_RESULT_PENDING);
+ if (UPSERT_STATUS_GET_SERVER_STATUS(conn->upsert_status) & SERVER_MORE_RESULTS_EXISTS) {
+ SET_CONNECTION_STATE(&conn->state, CONN_NEXT_RESULT_PENDING);
} else {
- CONN_SET_STATE(result->conn, CONN_READY);
+ SET_CONNECTION_STATE(&conn->state, CONN_READY);
}
- result->unbuf->m.free_last_data(result->unbuf, result->conn? result->conn->stats : NULL);
+ result->unbuf->m.free_last_data(result->unbuf, conn->stats);
}
DBG_INF_FMT("ret=%s fetched=%u", ret == PASS? "PASS":"FAIL", *fetched_anything);
@@ -800,6 +811,7 @@ MYSQLND_METHOD(mysqlnd_result_unbuffered, fetch_row)(MYSQLND_RES * result, void
zval *row = (zval *) param;
MYSQLND_PACKET_ROW *row_packet = result->unbuf->row_packet;
const MYSQLND_RES_METADATA * const meta = result->meta;
+ MYSQLND_CONN_DATA * const conn = result->conn;
DBG_ENTER("mysqlnd_result_unbuffered::fetch_row");
@@ -808,8 +820,8 @@ MYSQLND_METHOD(mysqlnd_result_unbuffered, fetch_row)(MYSQLND_RES * result, void
/* No more rows obviously */
DBG_RETURN(PASS);
}
- if (CONN_GET_STATE(result->conn) != CONN_FETCHING_DATA) {
- SET_CLIENT_ERROR(*result->conn->error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, mysqlnd_out_of_sync);
+ if (GET_CONNECTION_STATE(&conn->state) != CONN_FETCHING_DATA) {
+ SET_CLIENT_ERROR(conn->error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, mysqlnd_out_of_sync);
DBG_RETURN(FAIL);
}
if (!row_packet) {
@@ -823,36 +835,36 @@ MYSQLND_METHOD(mysqlnd_result_unbuffered, fetch_row)(MYSQLND_RES * result, void
If we skip rows (row == NULL) we have to
result->m.unbuffered_free_last_data() before it. The function returns always true.
*/
- if (PASS == (ret = PACKET_READ(row_packet, result->conn)) && !row_packet->eof) {
- result->unbuf->m.free_last_data(result->unbuf, result->conn? result->conn->stats : NULL);
+ if (PASS == (ret = PACKET_READ(row_packet)) && !row_packet->eof) {
+ result->unbuf->m.free_last_data(result->unbuf, conn->stats);
result->unbuf->last_row_data = row_packet->fields;
result->unbuf->last_row_buffer = row_packet->row_buffer;
row_packet->fields = NULL;
row_packet->row_buffer = NULL;
- MYSQLND_INC_CONN_STATISTIC(result->conn->stats, STAT_ROWS_FETCHED_FROM_CLIENT_NORMAL_UNBUF);
+ MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_ROWS_FETCHED_FROM_CLIENT_NORMAL_UNBUF);
if (!row_packet->skip_extraction) {
unsigned int i, field_count = meta->field_count;
enum_func_status rc = result->unbuf->m.row_decoder(result->unbuf->last_row_buffer,
- result->unbuf->last_row_data,
- field_count,
- row_packet->fields_metadata,
- result->conn->options->int_and_float_native,
- result->conn->stats);
+ result->unbuf->last_row_data,
+ field_count,
+ row_packet->fields_metadata,
+ conn->options->int_and_float_native,
+ conn->stats);
if (PASS != rc) {
DBG_RETURN(FAIL);
}
{
HashTable * row_ht = Z_ARRVAL_P(row);
MYSQLND_FIELD * field = meta->fields;
- zend_ulong * lengths = result->unbuf->lengths;
+ size_t * lengths = result->unbuf->lengths;
for (i = 0; i < field_count; i++, field++) {
zval * data = &result->unbuf->last_row_data[i];
- unsigned int len = (Z_TYPE_P(data) == IS_STRING)? Z_STRLEN_P(data) : 0;
+ const size_t len = (Z_TYPE_P(data) == IS_STRING)? Z_STRLEN_P(data) : 0;
if (flags & MYSQLND_FETCH_NUM) {
Z_TRY_ADDREF_P(data);
@@ -868,9 +880,9 @@ MYSQLND_METHOD(mysqlnd_result_unbuffered, fetch_row)(MYSQLND_RES * result, void
*/
Z_TRY_ADDREF_P(data);
if (meta->zend_hash_keys[i].is_numeric == FALSE) {
- zend_hash_update(Z_ARRVAL_P(row), meta->fields[i].sname, data);
+ zend_hash_update(row_ht, meta->fields[i].sname, data);
} else {
- zend_hash_index_update(Z_ARRVAL_P(row), meta->zend_hash_keys[i].key, data);
+ zend_hash_index_update(row_ht, meta->zend_hash_keys[i].key, data);
}
}
@@ -888,28 +900,29 @@ MYSQLND_METHOD(mysqlnd_result_unbuffered, fetch_row)(MYSQLND_RES * result, void
*fetched_anything = TRUE;
} else if (ret == FAIL) {
if (row_packet->error_info.error_no) {
- COPY_CLIENT_ERROR(*result->conn->error_info, row_packet->error_info);
+ COPY_CLIENT_ERROR(conn->error_info, row_packet->error_info);
DBG_ERR_FMT("errorno=%u error=%s", row_packet->error_info.error_no, row_packet->error_info.error);
}
- CONN_SET_STATE(result->conn, CONN_READY);
+ SET_CONNECTION_STATE(&conn->state, CONN_READY);
result->unbuf->eof_reached = TRUE; /* so next time we won't get an error */
} else if (row_packet->eof) {
/* Mark the connection as usable again */
DBG_INF_FMT("warnings=%u server_status=%u", row_packet->warning_count, row_packet->server_status);
result->unbuf->eof_reached = TRUE;
- memset(result->conn->upsert_status, 0, sizeof(*result->conn->upsert_status));
- result->conn->upsert_status->warning_count = row_packet->warning_count;
- result->conn->upsert_status->server_status = row_packet->server_status;
+
+ UPSERT_STATUS_RESET(conn->upsert_status);
+ UPSERT_STATUS_SET_WARNINGS(conn->upsert_status, row_packet->warning_count);
+ UPSERT_STATUS_SET_SERVER_STATUS(conn->upsert_status, row_packet->server_status);
/*
result->row_packet will be cleaned when
destroying the result object
*/
- if (result->conn->upsert_status->server_status & SERVER_MORE_RESULTS_EXISTS) {
- CONN_SET_STATE(result->conn, CONN_NEXT_RESULT_PENDING);
+ if (UPSERT_STATUS_GET_SERVER_STATUS(conn->upsert_status) & SERVER_MORE_RESULTS_EXISTS) {
+ SET_CONNECTION_STATE(&conn->state, CONN_NEXT_RESULT_PENDING);
} else {
- CONN_SET_STATE(result->conn, CONN_READY);
+ SET_CONNECTION_STATE(&conn->state, CONN_READY);
}
- result->unbuf->m.free_last_data(result->unbuf, result->conn? result->conn->stats : NULL);
+ result->unbuf->m.free_last_data(result->unbuf, conn->stats);
}
DBG_INF_FMT("ret=%s fetched=%u", ret == PASS? "PASS":"FAIL", *fetched_anything);
@@ -920,11 +933,12 @@ MYSQLND_METHOD(mysqlnd_result_unbuffered, fetch_row)(MYSQLND_RES * result, void
/* {{{ mysqlnd_res::use_result */
static MYSQLND_RES *
-MYSQLND_METHOD(mysqlnd_res, use_result)(MYSQLND_RES * const result, zend_bool ps)
+MYSQLND_METHOD(mysqlnd_res, use_result)(MYSQLND_RES * const result, const zend_bool ps)
{
+ MYSQLND_CONN_DATA * const conn = result->conn;
DBG_ENTER("mysqlnd_res::use_result");
- SET_EMPTY_ERROR(*result->conn->error_info);
+ SET_EMPTY_ERROR(conn->error_info);
if (ps == FALSE) {
result->type = MYSQLND_RES_NORMAL;
@@ -943,20 +957,24 @@ MYSQLND_METHOD(mysqlnd_res, use_result)(MYSQLND_RES * const result, zend_bool ps
this to be not NULL.
*/
/* FALSE = non-persistent */
- result->unbuf->row_packet = result->conn->protocol->m.get_row_packet(result->conn->protocol, FALSE);
- if (!result->unbuf->row_packet) {
- goto oom;
+ {
+ struct st_mysqlnd_packet_row * row_packet = conn->payload_decoder_factory->m.get_row_packet(conn->payload_decoder_factory, FALSE);
+ if (!row_packet) {
+ goto oom;
+ }
+ row_packet->result_set_memory_pool = result->unbuf->result_set_memory_pool;
+ row_packet->field_count = result->field_count;
+ row_packet->binary_protocol = ps;
+ row_packet->fields_metadata = result->meta->fields;
+ row_packet->bit_fields_count = result->meta->bit_fields_count;
+ row_packet->bit_fields_total_len = result->meta->bit_fields_total_len;
+
+ result->unbuf->row_packet = row_packet;
}
- result->unbuf->row_packet->result_set_memory_pool = result->unbuf->result_set_memory_pool;
- result->unbuf->row_packet->field_count = result->field_count;
- result->unbuf->row_packet->binary_protocol = ps;
- result->unbuf->row_packet->fields_metadata = result->meta->fields;
- result->unbuf->row_packet->bit_fields_count = result->meta->bit_fields_count;
- result->unbuf->row_packet->bit_fields_total_len = result->meta->bit_fields_total_len;
DBG_RETURN(result);
oom:
- SET_OOM_ERROR(*result->conn->error_info);
+ SET_OOM_ERROR(conn->error_info);
DBG_RETURN(NULL);
}
/* }}} */
@@ -966,10 +984,11 @@ oom:
static enum_func_status
MYSQLND_METHOD(mysqlnd_result_buffered, fetch_row_c)(MYSQLND_RES * result, void * param, unsigned int flags, zend_bool * fetched_anything)
{
+ enum_func_status ret = FAIL;
MYSQLND_ROW_C * row = (MYSQLND_ROW_C *) param;
const MYSQLND_RES_METADATA * const meta = result->meta;
unsigned int field_count = meta->field_count;
- enum_func_status ret = FAIL;
+ MYSQLND_CONN_DATA * const conn = result->conn;
DBG_ENTER("mysqlnd_result_buffered::fetch_row_c");
if (result->stored_data->type == MYSQLND_BUFFERED_TYPE_ZVAL) {
@@ -988,20 +1007,20 @@ MYSQLND_METHOD(mysqlnd_result_buffered, fetch_row_c)(MYSQLND_RES * result, void
current_row,
field_count,
meta->fields,
- result->conn->options->int_and_float_native,
- result->conn->stats);
+ conn->options->int_and_float_native,
+ conn->stats);
if (rc != PASS) {
DBG_RETURN(FAIL);
}
- set->initialized_rows++;
- for (i = 0; i < field_count; i++) {
+ ++set->initialized_rows;
+ for (i = 0; i < field_count; ++i) {
/*
NULL fields are 0 length, 0 is not more than 0
String of zero size, definitely can't be the next max_length.
Thus for NULL and zero-length we are quite efficient.
*/
if (Z_TYPE(current_row[i]) == IS_STRING) {
- zend_ulong len = Z_STRLEN(current_row[i]);
+ const size_t len = Z_STRLEN(current_row[i]);
if (meta->fields[i].max_length < len) {
meta->fields[i].max_length = len;
}
@@ -1013,7 +1032,7 @@ MYSQLND_METHOD(mysqlnd_result_buffered, fetch_row_c)(MYSQLND_RES * result, void
/* there is no conn handle in this function thus we can't set OOM in error_info */
*row = mnd_malloc(field_count * sizeof(char *));
if (*row) {
- for (i = 0; i < field_count; i++) {
+ for (i = 0; i < field_count; ++i) {
zval * data = &current_row[i];
set->lengths[i] = (Z_TYPE_P(data) == IS_STRING)? Z_STRLEN_P(data) : 0;
@@ -1028,7 +1047,7 @@ MYSQLND_METHOD(mysqlnd_result_buffered, fetch_row_c)(MYSQLND_RES * result, void
set->data_cursor += field_count;
MYSQLND_INC_GLOBAL_STATISTIC(STAT_ROWS_FETCHED_FROM_CLIENT_NORMAL_BUF);
} else {
- SET_OOM_ERROR(*result->conn->error_info);
+ SET_OOM_ERROR(conn->error_info);
}
/* END difference between normal normal fetch and _c */
@@ -1058,41 +1077,40 @@ MYSQLND_METHOD(mysqlnd_result_buffered, fetch_row_c)(MYSQLND_RES * result, void
static enum_func_status
MYSQLND_METHOD(mysqlnd_result_buffered_zval, fetch_row)(MYSQLND_RES * result, void * param, const unsigned int flags, zend_bool * fetched_anything)
{
+ enum_func_status ret = FAIL;
zval * row = (zval *) param;
const MYSQLND_RES_METADATA * const meta = result->meta;
- unsigned int field_count = meta->field_count;
- enum_func_status ret = FAIL;
+ const unsigned int field_count = meta->field_count;
MYSQLND_RES_BUFFERED_ZVAL * set = (MYSQLND_RES_BUFFERED_ZVAL *) result->stored_data;
+ MYSQLND_CONN_DATA * const conn = result->conn;
DBG_ENTER("mysqlnd_result_buffered_zval::fetch_row");
/* If we haven't read everything */
- if (set->data_cursor &&
- (set->data_cursor - set->data) < (set->row_count * field_count))
- {
+ if (set->data_cursor && (set->data_cursor - set->data) < (set->row_count * field_count)) {
unsigned int i;
zval *current_row = set->data_cursor;
if (Z_ISUNDEF(current_row[0])) {
- uint64_t row_num = (set->data_cursor - set->data) / field_count;
+ const size_t row_num = (set->data_cursor - set->data) / field_count;
enum_func_status rc = set->m.row_decoder(set->row_buffers[row_num],
- current_row,
- field_count,
- meta->fields,
- result->conn->options->int_and_float_native,
- result->conn->stats);
+ current_row,
+ field_count,
+ meta->fields,
+ conn->options->int_and_float_native,
+ conn->stats);
if (rc != PASS) {
DBG_RETURN(FAIL);
}
- set->initialized_rows++;
- for (i = 0; i < field_count; i++) {
+ ++set->initialized_rows;
+ for (i = 0; i < field_count; ++i) {
/*
NULL fields are 0 length, 0 is not more than 0
String of zero size, definitely can't be the next max_length.
Thus for NULL and zero-length we are quite efficient.
*/
if (Z_TYPE(current_row[i]) == IS_STRING) {
- zend_ulong len = Z_STRLEN(current_row[i]);
+ const size_t len = Z_STRLEN(current_row[i]);
if (meta->fields[i].max_length < len) {
meta->fields[i].max_length = len;
}
@@ -1100,7 +1118,7 @@ MYSQLND_METHOD(mysqlnd_result_buffered_zval, fetch_row)(MYSQLND_RES * result, vo
}
}
- for (i = 0; i < field_count; i++) {
+ for (i = 0; i < field_count; ++i) {
zval * data = &current_row[i];
set->lengths[i] = (Z_TYPE_P(data) == IS_STRING)? Z_STRLEN_P(data) : 0;
@@ -1145,10 +1163,11 @@ MYSQLND_METHOD(mysqlnd_result_buffered_zval, fetch_row)(MYSQLND_RES * result, vo
static enum_func_status
MYSQLND_METHOD(mysqlnd_result_buffered_c, fetch_row)(MYSQLND_RES * result, void * param, const unsigned int flags, zend_bool * fetched_anything)
{
+ enum_func_status ret = FAIL;
zval * row = (zval *) param;
const MYSQLND_RES_METADATA * const meta = result->meta;
- unsigned int field_count = meta->field_count;
- enum_func_status ret = FAIL;
+ const unsigned int field_count = meta->field_count;
+ MYSQLND_CONN_DATA * const conn = result->conn;
MYSQLND_RES_BUFFERED_C * set = (MYSQLND_RES_BUFFERED_C *) result->stored_data;
@@ -1156,38 +1175,38 @@ MYSQLND_METHOD(mysqlnd_result_buffered_c, fetch_row)(MYSQLND_RES * result, void
/* If we haven't read everything */
if (set->current_row < set->row_count) {
- zval *current_row;
enum_func_status rc;
+ zval * current_row;
unsigned int i;
current_row = mnd_emalloc(field_count * sizeof(zval));
if (!current_row) {
- SET_OOM_ERROR(*result->conn->error_info);
+ SET_OOM_ERROR(conn->error_info);
DBG_RETURN(FAIL);
}
rc = result->stored_data->m.row_decoder(result->stored_data->row_buffers[set->current_row],
- current_row,
- field_count,
- meta->fields,
- result->conn->options->int_and_float_native,
- result->conn->stats);
+ current_row,
+ field_count,
+ meta->fields,
+ conn->options->int_and_float_native,
+ conn->stats);
if (rc != PASS) {
DBG_RETURN(FAIL);
}
if (!(set->initialized[set->current_row >> 3] & (1 << (set->current_row & 7)))) {
set->initialized[set->current_row >> 3] |= (1 << (set->current_row & 7)); /* mark initialized */
- set->initialized_rows++;
+ ++set->initialized_rows;
- for (i = 0; i < field_count; i++) {
+ for (i = 0; i < field_count; ++i) {
/*
NULL fields are 0 length, 0 is not more than 0
String of zero size, definitely can't be the next max_length.
Thus for NULL and zero-length we are quite efficient.
*/
if (Z_TYPE(current_row[i]) == IS_STRING) {
- zend_ulong len = Z_STRLEN(current_row[i]);
+ const size_t len = Z_STRLEN(current_row[i]);
if (meta->fields[i].max_length < len) {
meta->fields[i].max_length = len;
}
@@ -1195,7 +1214,7 @@ MYSQLND_METHOD(mysqlnd_result_buffered_c, fetch_row)(MYSQLND_RES * result, void
}
}
- for (i = 0; i < field_count; i++) {
+ for (i = 0; i < field_count; ++i) {
zval * data = &current_row[i];
set->lengths[i] = (Z_TYPE_P(data) == IS_STRING)? Z_STRLEN_P(data) : 0;
@@ -1228,7 +1247,7 @@ MYSQLND_METHOD(mysqlnd_result_buffered_c, fetch_row)(MYSQLND_RES * result, void
zval_ptr_dtor(data);
}
mnd_efree(current_row);
- set->current_row++;
+ ++set->current_row;
MYSQLND_INC_GLOBAL_STATISTIC(STAT_ROWS_FETCHED_FROM_CLIENT_NORMAL_BUF);
*fetched_anything = TRUE;
ret = PASS;
@@ -1271,14 +1290,11 @@ MYSQLND_METHOD(mysqlnd_res, store_result_fetch_data)(MYSQLND_CONN_DATA * const c
zend_bool binary_protocol)
{
enum_func_status ret;
- MYSQLND_PACKET_ROW * row_packet = NULL;
unsigned int next_extend = STORE_RESULT_PREALLOCATED_SET_IF_NOT_EMPTY, free_rows = 1;
- MYSQLND_RES_BUFFERED *set;
+ MYSQLND_RES_BUFFERED * set = result->stored_data;
+ MYSQLND_PACKET_ROW * row_packet = NULL;
DBG_ENTER("mysqlnd_res::store_result_fetch_data");
-
- set = result->stored_data;
-
if (!set || !row_buffers) {
ret = FAIL;
goto end;
@@ -1286,20 +1302,20 @@ MYSQLND_METHOD(mysqlnd_res, store_result_fetch_data)(MYSQLND_CONN_DATA * const c
if (free_rows) {
*row_buffers = mnd_pemalloc((size_t)(free_rows * sizeof(MYSQLND_MEMORY_POOL_CHUNK *)), 0);
if (!*row_buffers) {
- SET_OOM_ERROR(*conn->error_info);
+ SET_OOM_ERROR(conn->error_info);
ret = FAIL;
goto end;
}
}
- set->references = 1;
-
/* non-persistent */
- row_packet = conn->protocol->m.get_row_packet(conn->protocol, FALSE);
+ row_packet = conn->payload_decoder_factory->m.get_row_packet(conn->payload_decoder_factory, FALSE);
if (!row_packet) {
- SET_OOM_ERROR(*conn->error_info);
+ SET_OOM_ERROR(conn->error_info);
ret = FAIL;
goto end;
}
+ set->references = 1;
+
row_packet->result_set_memory_pool = result->stored_data->result_set_memory_pool;
row_packet->field_count = meta->field_count;
row_packet->binary_protocol = binary_protocol;
@@ -1309,7 +1325,7 @@ MYSQLND_METHOD(mysqlnd_res, store_result_fetch_data)(MYSQLND_CONN_DATA * const c
row_packet->skip_extraction = TRUE; /* let php_mysqlnd_rowp_read() not allocate row_packet->fields, we will do it */
- while (FAIL != (ret = PACKET_READ(row_packet, conn)) && !row_packet->eof) {
+ while (FAIL != (ret = PACKET_READ(row_packet)) && !row_packet->eof) {
if (!free_rows) {
uint64_t total_allocated_rows = free_rows = next_extend = next_extend * 11 / 10; /* extend with 10% */
MYSQLND_MEMORY_POOL_CHUNK ** new_row_buffers;
@@ -1317,13 +1333,13 @@ MYSQLND_METHOD(mysqlnd_res, store_result_fetch_data)(MYSQLND_CONN_DATA * const c
/* don't try to allocate more than possible - mnd_XXalloc expects size_t, and it can have narrower range than uint64_t */
if (total_allocated_rows * sizeof(MYSQLND_MEMORY_POOL_CHUNK *) > SIZE_MAX) {
- SET_OOM_ERROR(*conn->error_info);
+ SET_OOM_ERROR(conn->error_info);
ret = FAIL;
goto end;
}
new_row_buffers = mnd_perealloc(*row_buffers, (size_t)(total_allocated_rows * sizeof(MYSQLND_MEMORY_POOL_CHUNK *)), 0);
if (!new_row_buffers) {
- SET_OOM_ERROR(*conn->error_info);
+ SET_OOM_ERROR(conn->error_info);
ret = FAIL;
goto end;
}
@@ -1353,35 +1369,38 @@ MYSQLND_METHOD(mysqlnd_res, store_result_fetch_data)(MYSQLND_CONN_DATA * const c
/* Finally clean */
if (row_packet->eof) {
- memset(conn->upsert_status, 0, sizeof(*conn->upsert_status));
- conn->upsert_status->warning_count = row_packet->warning_count;
- conn->upsert_status->server_status = row_packet->server_status;
+ UPSERT_STATUS_RESET(conn->upsert_status);
+ UPSERT_STATUS_SET_WARNINGS(conn->upsert_status, row_packet->warning_count);
+ UPSERT_STATUS_SET_SERVER_STATUS(conn->upsert_status, row_packet->server_status);
}
/* save some memory */
if (free_rows) {
/* don't try to allocate more than possible - mnd_XXalloc expects size_t, and it can have narrower range than uint64_t */
if (set->row_count * sizeof(MYSQLND_MEMORY_POOL_CHUNK *) > SIZE_MAX) {
- SET_OOM_ERROR(*conn->error_info);
+ SET_OOM_ERROR(conn->error_info);
ret = FAIL;
goto end;
}
*row_buffers = mnd_perealloc(*row_buffers, (size_t) (set->row_count * sizeof(MYSQLND_MEMORY_POOL_CHUNK *)), 0);
}
- if (conn->upsert_status->server_status & SERVER_MORE_RESULTS_EXISTS) {
- CONN_SET_STATE(conn, CONN_NEXT_RESULT_PENDING);
+ if (UPSERT_STATUS_GET_SERVER_STATUS(conn->upsert_status) & SERVER_MORE_RESULTS_EXISTS) {
+ SET_CONNECTION_STATE(&conn->state, CONN_NEXT_RESULT_PENDING);
} else {
- CONN_SET_STATE(conn, CONN_READY);
+ SET_CONNECTION_STATE(&conn->state, CONN_READY);
}
if (ret == FAIL) {
- COPY_CLIENT_ERROR(set->error_info, row_packet->error_info);
+ COPY_CLIENT_ERROR(&set->error_info, row_packet->error_info);
} else {
/* libmysql's documentation says it should be so for SELECT statements */
- conn->upsert_status->affected_rows = set->row_count;
+ UPSERT_STATUS_SET_AFFECTED_ROWS(conn->upsert_status, set->row_count);
}
DBG_INF_FMT("ret=%s row_count=%u warnings=%u server_status=%u",
- ret == PASS? "PASS":"FAIL", (uint) set->row_count, conn->upsert_status->warning_count, conn->upsert_status->server_status);
+ ret == PASS? "PASS":"FAIL",
+ (uint) set->row_count,
+ UPSERT_STATUS_GET_WARNINGS(conn->upsert_status),
+ UPSERT_STATUS_GET_SERVER_STATUS(conn->upsert_status));
end:
PACKET_FREE(row_packet);
DBG_INF_FMT("rows=%llu", (unsigned long long)result->stored_data->row_count);
@@ -1406,19 +1425,19 @@ MYSQLND_METHOD(mysqlnd_res, store_result)(MYSQLND_RES * result,
result->conn = conn->m->get_reference(conn);
result->type = MYSQLND_RES_NORMAL;
- CONN_SET_STATE(conn, CONN_FETCHING_DATA);
+ SET_CONNECTION_STATE(&conn->state, CONN_FETCHING_DATA);
if (flags & MYSQLND_STORE_NO_COPY) {
result->stored_data = (MYSQLND_RES_BUFFERED *) mysqlnd_result_buffered_zval_init(result->field_count, flags & MYSQLND_STORE_PS, result->persistent);
if (!result->stored_data) {
- SET_OOM_ERROR(*conn->error_info);
+ SET_OOM_ERROR(conn->error_info);
DBG_RETURN(NULL);
}
row_buffers = &result->stored_data->row_buffers;
} else if (flags & MYSQLND_STORE_COPY) {
result->stored_data = (MYSQLND_RES_BUFFERED *) mysqlnd_result_buffered_c_init(result->field_count, flags & MYSQLND_STORE_PS, result->persistent);
if (!result->stored_data) {
- SET_OOM_ERROR(*conn->error_info);
+ SET_OOM_ERROR(conn->error_info);
DBG_RETURN(NULL);
}
row_buffers = &result->stored_data->row_buffers;
@@ -1427,26 +1446,26 @@ MYSQLND_METHOD(mysqlnd_res, store_result)(MYSQLND_RES * result,
if (FAIL == ret) {
if (result->stored_data) {
- COPY_CLIENT_ERROR(*conn->error_info, result->stored_data->error_info);
+ COPY_CLIENT_ERROR(conn->error_info, result->stored_data->error_info);
} else {
- SET_OOM_ERROR(*conn->error_info);
+ SET_OOM_ERROR(conn->error_info);
}
DBG_RETURN(NULL);
} else {
- /* Overflow ? */
if (flags & MYSQLND_STORE_NO_COPY) {
- MYSQLND_RES_METADATA * meta = result->meta;
+ const MYSQLND_RES_METADATA * const meta = result->meta;
MYSQLND_RES_BUFFERED_ZVAL * set = (MYSQLND_RES_BUFFERED_ZVAL *) result->stored_data;
+
if (set->row_count) {
/* don't try to allocate more than possible - mnd_XXalloc expects size_t, and it can have narrower range than uint64_t */
if (set->row_count * meta->field_count * sizeof(zval *) > SIZE_MAX) {
- SET_OOM_ERROR(*conn->error_info);
+ SET_OOM_ERROR(conn->error_info);
DBG_RETURN(NULL);
}
/* if pecalloc is used valgrind barks gcc version 4.3.1 20080507 (prerelease) [gcc-4_3-branch revision 135036] (SUSE Linux) */
set->data = mnd_emalloc((size_t)(set->row_count * meta->field_count * sizeof(zval)));
if (!set->data) {
- SET_OOM_ERROR(*conn->error_info);
+ SET_OOM_ERROR(conn->error_info);
DBG_RETURN(NULL);
}
memset(set->data, 0, (size_t)(set->row_count * meta->field_count * sizeof(zval)));
@@ -1456,12 +1475,12 @@ MYSQLND_METHOD(mysqlnd_res, store_result)(MYSQLND_RES * result,
} else if (flags & MYSQLND_STORE_COPY) {
MYSQLND_RES_BUFFERED_C * set = (MYSQLND_RES_BUFFERED_C *) result->stored_data;
set->current_row = 0;
- set->initialized = mnd_pecalloc((set->row_count / 8) + 1, sizeof(zend_uchar), set->persistent); /* +1 for safety */
+ set->initialized = mnd_pecalloc((unsigned int) ((set->row_count / 8) + 1), sizeof(zend_uchar), set->persistent); /* +1 for safety */
}
}
/* libmysql's documentation says it should be so for SELECT statements */
- conn->upsert_status->affected_rows = result->stored_data->row_count;
+ UPSERT_STATUS_SET_AFFECTED_ROWS(conn->upsert_status, result->stored_data->row_count);
DBG_RETURN(result);
}
@@ -1481,9 +1500,10 @@ MYSQLND_METHOD(mysqlnd_res, skip_result)(MYSQLND_RES * const result)
fetch_row function isn't actually set (NULL), thus we have to skip these.
*/
if (result->unbuf && !result->unbuf->eof_reached) {
+ MYSQLND_CONN_DATA * const conn = result->conn;
DBG_INF("skipping result");
/* We have to fetch all data to clean the line */
- MYSQLND_INC_CONN_STATISTIC(result->conn->stats,
+ MYSQLND_INC_CONN_STATISTIC(conn->stats,
result->type == MYSQLND_RES_NORMAL? STAT_FLUSHED_NORMAL_SETS:
STAT_FLUSHED_PS_SETS);
@@ -1498,7 +1518,7 @@ MYSQLND_METHOD(mysqlnd_res, skip_result)(MYSQLND_RES * const result)
/* {{{ mysqlnd_res::free_result */
static enum_func_status
-MYSQLND_METHOD(mysqlnd_res, free_result)(MYSQLND_RES * result, zend_bool implicit)
+MYSQLND_METHOD(mysqlnd_res, free_result)(MYSQLND_RES * result, const zend_bool implicit)
{
DBG_ENTER("mysqlnd_res::free_result");
@@ -1565,7 +1585,7 @@ static uint64_t
MYSQLND_METHOD(mysqlnd_result_unbuffered, num_rows)(const MYSQLND_RES_UNBUFFERED * const result)
{
/* Be compatible with libmysql. We count row_count, but will return 0 */
- return result->eof_reached? result->row_count:0;
+ return result->eof_reached? result->row_count : 0;
}
/* }}} */
@@ -1617,10 +1637,13 @@ MYSQLND_METHOD(mysqlnd_res, fetch_field)(MYSQLND_RES * const result)
not during mysqli_fetch_field() time.
*/
if (result->stored_data && (result->stored_data->initialized_rows < result->stored_data->row_count)) {
+ const MYSQLND_CONN_DATA * const conn = result->conn;
DBG_INF_FMT("We have decode the whole result set to be able to satisfy this meta request");
/* we have to initialize the rest to get the updated max length */
- if (PASS != result->stored_data->m.initialize_result_set_rest(result->stored_data, result->meta, result->conn->stats,
- result->conn->options->int_and_float_native))
+ if (PASS != result->stored_data->m.initialize_result_set_rest(result->stored_data,
+ result->meta,
+ conn->stats,
+ conn->options->int_and_float_native))
{
break;
}
@@ -1651,10 +1674,13 @@ MYSQLND_METHOD(mysqlnd_res, fetch_field_direct)(MYSQLND_RES * const result, cons
not during mysqli_fetch_field_direct() time.
*/
if (result->stored_data && (result->stored_data->initialized_rows < result->stored_data->row_count)) {
+ const MYSQLND_CONN_DATA * const conn = result->conn;
DBG_INF_FMT("We have decode the whole result set to be able to satisfy this meta request");
/* we have to initialized the rest to get the updated max length */
- if (PASS != result->stored_data->m.initialize_result_set_rest(result->stored_data, result->meta, result->conn->stats,
- result->conn->options->int_and_float_native))
+ if (PASS != result->stored_data->m.initialize_result_set_rest(result->stored_data,
+ result->meta,
+ conn->stats,
+ conn->options->int_and_float_native))
{
break;
}
@@ -1676,9 +1702,12 @@ MYSQLND_METHOD(mysqlnd_res, fetch_fields)(MYSQLND_RES * const result)
do {
if (result->meta) {
if (result->stored_data && (result->stored_data->initialized_rows < result->stored_data->row_count)) {
+ const MYSQLND_CONN_DATA * const conn = result->conn;
/* we have to initialize the rest to get the updated max length */
- if (PASS != result->stored_data->m.initialize_result_set_rest(result->stored_data, result->meta, result->conn->stats,
- result->conn->options->int_and_float_native))
+ if (PASS != result->stored_data->m.initialize_result_set_rest(result->stored_data,
+ result->meta,
+ conn->stats,
+ conn->options->int_and_float_native))
{
break;
}
@@ -1783,7 +1812,7 @@ MYSQLND_METHOD(mysqlnd_res, fetch_all)(MYSQLND_RES * result, const unsigned int
if ((!result->unbuf && !set)) {
php_error_docref(NULL, E_WARNING, "fetch_all can be used only with buffered sets");
if (result->conn) {
- SET_CLIENT_ERROR(*result->conn->error_info, CR_NOT_IMPLEMENTED, UNKNOWN_SQLSTATE, "fetch_all can be used only with buffered sets");
+ SET_CLIENT_ERROR(result->conn->error_info, CR_NOT_IMPLEMENTED, UNKNOWN_SQLSTATE, "fetch_all can be used only with buffered sets");
}
RETVAL_NULL();
DBG_VOID_RETURN;
@@ -1893,9 +1922,9 @@ MYSQLND_CLASS_METHODS_END;
/* {{{ mysqlnd_result_init */
PHPAPI MYSQLND_RES *
-mysqlnd_result_init(unsigned int field_count, zend_bool persistent)
+mysqlnd_result_init(const unsigned int field_count, const zend_bool persistent)
{
- size_t alloc_size = sizeof(MYSQLND_RES) + mysqlnd_plugin_count() * sizeof(void *);
+ const size_t alloc_size = sizeof(MYSQLND_RES) + mysqlnd_plugin_count() * sizeof(void *);
MYSQLND_RES * ret = mnd_pecalloc(1, alloc_size, persistent);
DBG_ENTER("mysqlnd_result_init");
@@ -1915,9 +1944,9 @@ mysqlnd_result_init(unsigned int field_count, zend_bool persistent)
/* {{{ mysqlnd_result_unbuffered_init */
PHPAPI MYSQLND_RES_UNBUFFERED *
-mysqlnd_result_unbuffered_init(unsigned int field_count, zend_bool ps, zend_bool persistent)
+mysqlnd_result_unbuffered_init(const unsigned int field_count, const zend_bool ps, const zend_bool persistent)
{
- size_t alloc_size = sizeof(MYSQLND_RES_UNBUFFERED) + mysqlnd_plugin_count() * sizeof(void *);
+ const size_t alloc_size = sizeof(MYSQLND_RES_UNBUFFERED) + mysqlnd_plugin_count() * sizeof(void *);
MYSQLND_RES_UNBUFFERED * ret = mnd_pecalloc(1, alloc_size, persistent);
DBG_ENTER("mysqlnd_result_unbuffered_init");
@@ -1926,7 +1955,7 @@ mysqlnd_result_unbuffered_init(unsigned int field_count, zend_bool ps, zend_bool
DBG_RETURN(NULL);
}
- if (!(ret->lengths = mnd_pecalloc(field_count, sizeof(zend_ulong), persistent))) {
+ if (!(ret->lengths = mnd_pecalloc(field_count, sizeof(size_t), persistent))) {
mnd_pefree(ret, persistent);
DBG_RETURN(NULL);
}
@@ -1956,9 +1985,9 @@ mysqlnd_result_unbuffered_init(unsigned int field_count, zend_bool ps, zend_bool
/* {{{ mysqlnd_result_buffered_zval_init */
PHPAPI MYSQLND_RES_BUFFERED_ZVAL *
-mysqlnd_result_buffered_zval_init(unsigned int field_count, zend_bool ps, zend_bool persistent)
+mysqlnd_result_buffered_zval_init(const unsigned int field_count, const zend_bool ps, const zend_bool persistent)
{
- size_t alloc_size = sizeof(MYSQLND_RES_BUFFERED_ZVAL) + mysqlnd_plugin_count() * sizeof(void *);
+ const size_t alloc_size = sizeof(MYSQLND_RES_BUFFERED_ZVAL) + mysqlnd_plugin_count() * sizeof(void *);
MYSQLND_RES_BUFFERED_ZVAL * ret = mnd_pecalloc(1, alloc_size, persistent);
DBG_ENTER("mysqlnd_result_buffered_zval_init");
@@ -1966,7 +1995,7 @@ mysqlnd_result_buffered_zval_init(unsigned int field_count, zend_bool ps, zend_b
if (!ret) {
DBG_RETURN(NULL);
}
- if (!(ret->lengths = mnd_pecalloc(field_count, sizeof(zend_ulong), persistent))) {
+ if (!(ret->lengths = mnd_pecalloc(field_count, sizeof(size_t), persistent))) {
mnd_pefree(ret, persistent);
DBG_RETURN(NULL);
}
@@ -1999,9 +2028,9 @@ mysqlnd_result_buffered_zval_init(unsigned int field_count, zend_bool ps, zend_b
/* {{{ mysqlnd_result_buffered_c_init */
PHPAPI MYSQLND_RES_BUFFERED_C *
-mysqlnd_result_buffered_c_init(unsigned int field_count, zend_bool ps, zend_bool persistent)
+mysqlnd_result_buffered_c_init(const unsigned int field_count, const zend_bool ps, const zend_bool persistent)
{
- size_t alloc_size = sizeof(MYSQLND_RES_BUFFERED_C) + mysqlnd_plugin_count() * sizeof(void *);
+ const size_t alloc_size = sizeof(MYSQLND_RES_BUFFERED_C) + mysqlnd_plugin_count() * sizeof(void *);
MYSQLND_RES_BUFFERED_C * ret = mnd_pecalloc(1, alloc_size, persistent);
DBG_ENTER("mysqlnd_result_buffered_c_init");
@@ -2009,7 +2038,7 @@ mysqlnd_result_buffered_c_init(unsigned int field_count, zend_bool ps, zend_bool
if (!ret) {
DBG_RETURN(NULL);
}
- if (!(ret->lengths = mnd_pecalloc(field_count, sizeof(zend_ulong), persistent))) {
+ if (!(ret->lengths = mnd_pecalloc(field_count, sizeof(size_t), persistent))) {
mnd_pefree(ret, persistent);
DBG_RETURN(NULL);
}
diff --git a/ext/mysqlnd/mysqlnd_result.h b/ext/mysqlnd/mysqlnd_result.h
index 3351b9eae0..3f91195b32 100644
--- a/ext/mysqlnd/mysqlnd_result.h
+++ b/ext/mysqlnd/mysqlnd_result.h
@@ -14,17 +14,16 @@
+----------------------------------------------------------------------+
| Authors: Andrey Hristov <andrey@php.net> |
| Ulf Wendel <uw@php.net> |
- | Georg Richter <georg@php.net> |
+----------------------------------------------------------------------+
*/
#ifndef MYSQLND_RESULT_H
#define MYSQLND_RESULT_H
-PHPAPI MYSQLND_RES * mysqlnd_result_init(unsigned int field_count, zend_bool persistent);
-PHPAPI MYSQLND_RES_UNBUFFERED * mysqlnd_result_unbuffered_init(unsigned int field_count, zend_bool ps, zend_bool persistent);
-PHPAPI MYSQLND_RES_BUFFERED_ZVAL * mysqlnd_result_buffered_zval_init(unsigned int field_count, zend_bool ps, zend_bool persistent);
-PHPAPI MYSQLND_RES_BUFFERED_C * mysqlnd_result_buffered_c_init(unsigned int field_count, zend_bool ps, zend_bool persistent);
+PHPAPI MYSQLND_RES * mysqlnd_result_init(const unsigned int field_count, const zend_bool persistent);
+PHPAPI MYSQLND_RES_UNBUFFERED * mysqlnd_result_unbuffered_init(const unsigned int field_count, const zend_bool ps, const zend_bool persistent);
+PHPAPI MYSQLND_RES_BUFFERED_ZVAL * mysqlnd_result_buffered_zval_init(const unsigned int field_count, const zend_bool ps, const zend_bool persistent);
+PHPAPI MYSQLND_RES_BUFFERED_C * mysqlnd_result_buffered_c_init(const unsigned int field_count, const zend_bool ps, const zend_bool persistent);
enum_func_status mysqlnd_query_read_result_set_header(MYSQLND_CONN_DATA * conn, MYSQLND_STMT * stmt);
diff --git a/ext/mysqlnd/mysqlnd_result_meta.c b/ext/mysqlnd/mysqlnd_result_meta.c
index 49954ead86..3934edd03e 100644
--- a/ext/mysqlnd/mysqlnd_result_meta.c
+++ b/ext/mysqlnd/mysqlnd_result_meta.c
@@ -14,12 +14,13 @@
+----------------------------------------------------------------------+
| Authors: Andrey Hristov <andrey@php.net> |
| Ulf Wendel <uw@php.net> |
- | Georg Richter <georg@php.net> |
+----------------------------------------------------------------------+
*/
#include "php.h"
#include "mysqlnd.h"
+#include "mysqlnd_connection.h"
+#include "mysqlnd_ps.h"
#include "mysqlnd_priv.h"
#include "mysqlnd_result.h"
#include "mysqlnd_wireprotocol.h"
@@ -56,9 +57,9 @@ MYSQLND_METHOD(mysqlnd_res_meta, read_metadata)(MYSQLND_RES_METADATA * const met
DBG_ENTER("mysqlnd_res_meta::read_metadata");
- field_packet = conn->protocol->m.get_result_field_packet(conn->protocol, FALSE);
+ field_packet = conn->payload_decoder_factory->m.get_result_field_packet(conn->payload_decoder_factory, FALSE);
if (!field_packet) {
- SET_OOM_ERROR(*conn->error_info);
+ SET_OOM_ERROR(conn->error_info);
DBG_RETURN(FAIL);
}
field_packet->persistent_alloc = meta->persistent;
@@ -72,29 +73,20 @@ MYSQLND_METHOD(mysqlnd_res_meta, read_metadata)(MYSQLND_RES_METADATA * const met
}
field_packet->metadata = &(meta->fields[i]);
- if (FAIL == PACKET_READ(field_packet, conn)) {
+ if (FAIL == PACKET_READ(field_packet)) {
PACKET_FREE(field_packet);
DBG_RETURN(FAIL);
}
if (field_packet->error_info.error_no) {
- COPY_CLIENT_ERROR(*conn->error_info, field_packet->error_info);
+ COPY_CLIENT_ERROR(conn->error_info, field_packet->error_info);
/* Return back from CONN_QUERY_SENT */
PACKET_FREE(field_packet);
DBG_RETURN(FAIL);
}
- if (field_packet->stupid_list_fields_eof == TRUE) {
- meta->field_count = i;
- break;
- }
-
if (mysqlnd_ps_fetch_functions[meta->fields[i].type].func == NULL) {
- DBG_ERR_FMT("Unknown type %u sent by the server. Please send a report to the developers",
- meta->fields[i].type);
- php_error_docref(NULL, E_WARNING,
- "Unknown type %u sent by the server. "
- "Please send a report to the developers",
- meta->fields[i].type);
+ DBG_ERR_FMT("Unknown type %u sent by the server. Please send a report to the developers", meta->fields[i].type);
+ php_error_docref(NULL, E_WARNING, "Unknown type %u sent by the server. Please send a report to the developers", meta->fields[i].type);
PACKET_FREE(field_packet);
DBG_RETURN(FAIL);
}
@@ -179,7 +171,7 @@ MYSQLND_METHOD(mysqlnd_res_meta, free)(MYSQLND_RES_METADATA * meta)
/* {{{ mysqlnd_res::clone_metadata */
static MYSQLND_RES_METADATA *
-MYSQLND_METHOD(mysqlnd_res_meta, clone_metadata)(const MYSQLND_RES_METADATA * const meta, zend_bool persistent)
+MYSQLND_METHOD(mysqlnd_res_meta, clone_metadata)(const MYSQLND_RES_METADATA * const meta, const zend_bool persistent)
{
unsigned int i;
/* +1 is to have empty marker at the end */
diff --git a/ext/mysqlnd/mysqlnd_result_meta.h b/ext/mysqlnd/mysqlnd_result_meta.h
index d877d086f0..4f22dc8a8f 100644
--- a/ext/mysqlnd/mysqlnd_result_meta.h
+++ b/ext/mysqlnd/mysqlnd_result_meta.h
@@ -13,8 +13,8 @@
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
| Authors: Andrey Hristov <andrey@php.net> |
+ | Johannes Schlüter <johannes@php.net> |
| Ulf Wendel <uw@php.net> |
- | Georg Richter <georg@php.net> |
+----------------------------------------------------------------------+
*/
diff --git a/ext/mysqlnd/mysqlnd_reverse_api.c b/ext/mysqlnd/mysqlnd_reverse_api.c
index 753cbf9eff..3ac3ff8ff2 100644
--- a/ext/mysqlnd/mysqlnd_reverse_api.c
+++ b/ext/mysqlnd/mysqlnd_reverse_api.c
@@ -13,8 +13,8 @@
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
| Authors: Andrey Hristov <andrey@php.net> |
+ | Johannes Schlüter <johannes@php.net> |
| Ulf Wendel <uw@php.net> |
- | Georg Richter <georg@php.net> |
+----------------------------------------------------------------------+
*/
diff --git a/ext/mysqlnd/mysqlnd_statistics.c b/ext/mysqlnd/mysqlnd_statistics.c
index 8acc056093..7f12a72511 100644
--- a/ext/mysqlnd/mysqlnd_statistics.c
+++ b/ext/mysqlnd/mysqlnd_statistics.c
@@ -25,9 +25,7 @@
#include "mysqlnd_debug.h"
-/* {{{ mysqlnd_stats_values_names
- */
-
+/* {{{ mysqlnd_stats_values_names */
const MYSQLND_STRING mysqlnd_stats_values_names[STAT_LAST] =
{
{ MYSQLND_STR_W_LEN("bytes_sent") },
@@ -113,8 +111,10 @@ const MYSQLND_STRING mysqlnd_stats_values_names[STAT_LAST] =
{ MYSQLND_STR_W_LEN("mem_free_amount") },
{ MYSQLND_STR_W_LEN("mem_estrndup_count") },
{ MYSQLND_STR_W_LEN("mem_strndup_count") },
- { MYSQLND_STR_W_LEN("mem_estndup_count") },
+ { MYSQLND_STR_W_LEN("mem_estrdup_count") },
{ MYSQLND_STR_W_LEN("mem_strdup_count") },
+ { MYSQLND_STR_W_LEN("mem_edupl_count") },
+ { MYSQLND_STR_W_LEN("mem_dupl_count") },
{ MYSQLND_STR_W_LEN("proto_text_fetched_null") },
{ MYSQLND_STR_W_LEN("proto_text_fetched_bit") },
{ MYSQLND_STR_W_LEN("proto_text_fetched_tinyint") },
@@ -212,25 +212,9 @@ mysqlnd_fill_stats_hash(const MYSQLND_STATS * const stats, const MYSQLND_STRING
/* }}} */
-/* {{{ _mysqlnd_get_client_stats */
-PHPAPI void
-_mysqlnd_get_client_stats(zval *return_value ZEND_FILE_LINE_DC)
-{
- MYSQLND_STATS stats, *stats_ptr = mysqlnd_global_stats;
- DBG_ENTER("_mysqlnd_get_client_stats");
- if (!stats_ptr) {
- memset(&stats, 0, sizeof(stats));
- stats_ptr = &stats;
- }
- mysqlnd_fill_stats_hash(stats_ptr, mysqlnd_stats_values_names, return_value ZEND_FILE_LINE_CC);
- DBG_VOID_RETURN;
-}
-/* }}} */
-
-
/* {{{ mysqlnd_stats_init */
PHPAPI void
-mysqlnd_stats_init(MYSQLND_STATS ** stats, size_t statistic_count, int persistent)
+mysqlnd_stats_init(MYSQLND_STATS ** stats, const size_t statistic_count, const zend_bool persistent)
{
*stats = pecalloc(1, sizeof(MYSQLND_STATS), persistent);
if (*stats == NULL) {
@@ -249,7 +233,7 @@ mysqlnd_stats_init(MYSQLND_STATS ** stats, size_t statistic_count, int persisten
/* {{{ mysqlnd_stats_end */
PHPAPI void
-mysqlnd_stats_end(MYSQLND_STATS * stats, int persistent)
+mysqlnd_stats_end(MYSQLND_STATS * stats, const zend_bool persistent)
{
#ifdef ZTS
tsrm_mutex_free(stats->LOCK_access);
@@ -295,6 +279,25 @@ mysqlnd_stats_reset_triggers(MYSQLND_STATS * const stats)
/* }}} */
+/************ MYSQLND specific code **********/
+
+/* {{{ _mysqlnd_get_client_stats */
+PHPAPI void
+_mysqlnd_get_client_stats(MYSQLND_STATS * stats_ptr, zval *return_value ZEND_FILE_LINE_DC)
+{
+ MYSQLND_STATS stats;
+ DBG_ENTER("_mysqlnd_get_client_stats");
+ if (!stats_ptr) {
+ memset(&stats, 0, sizeof(stats));
+ stats_ptr = &stats;
+ }
+ mysqlnd_fill_stats_hash(stats_ptr, mysqlnd_stats_values_names, return_value ZEND_FILE_LINE_CC);
+ DBG_VOID_RETURN;
+}
+/* }}} */
+
+
+
/*
* Local variables:
* tab-width: 4
diff --git a/ext/mysqlnd/mysqlnd_statistics.h b/ext/mysqlnd/mysqlnd_statistics.h
index 2dde6e94c2..ea909fcfcb 100644
--- a/ext/mysqlnd/mysqlnd_statistics.h
+++ b/ext/mysqlnd/mysqlnd_statistics.h
@@ -14,18 +14,12 @@
+----------------------------------------------------------------------+
| Authors: Andrey Hristov <andrey@php.net> |
| Ulf Wendel <uw@php.net> |
- | Georg Richter <georg@php.net> |
+----------------------------------------------------------------------+
*/
#ifndef MYSQLND_STATISTICS_H
#define MYSQLND_STATISTICS_H
-
-PHPAPI extern MYSQLND_STATS * mysqlnd_global_stats;
-
-extern const MYSQLND_STRING mysqlnd_stats_values_names[];
-
#ifdef ZTS
#define MYSQLND_STATS_LOCK(stats) tsrm_mutex_lock((stats)->LOCK_access)
#define MYSQLND_STATS_UNLOCK(stats) tsrm_mutex_unlock((stats)->LOCK_access)
@@ -116,50 +110,11 @@ extern const MYSQLND_STRING mysqlnd_stats_values_names[];
-#ifndef MYSQLND_CORE_STATISTICS_DISABLED
-
-#define MYSQLND_INC_GLOBAL_STATISTIC(statistic) \
- MYSQLND_INC_STATISTIC(MYSQLND_G(collect_statistics), mysqlnd_global_stats, (statistic))
-
-#define MYSQLND_DEC_CONN_STATISTIC(conn_stats, statistic) \
- MYSQLND_DEC_STATISTIC(MYSQLND_G(collect_statistics), mysqlnd_global_stats, (statistic))
-
-#define MYSQLND_INC_GLOBAL_STATISTIC_W_VALUE2(statistic1, value1, statistic2, value2) \
- MYSQLND_INC_STATISTIC_W_VALUE2(MYSQLND_G(collect_statistics), mysqlnd_global_stats, (statistic1), (value1), (statistic2), (value2))
-
-#define MYSQLND_INC_CONN_STATISTIC(conn_stats, statistic) \
- MYSQLND_INC_STATISTIC(MYSQLND_G(collect_statistics), mysqlnd_global_stats, (statistic)); \
- MYSQLND_INC_STATISTIC(MYSQLND_G(collect_statistics), (conn_stats), (statistic));
-
-#define MYSQLND_INC_CONN_STATISTIC_W_VALUE(conn_stats, statistic, value) \
- MYSQLND_INC_STATISTIC_W_VALUE(MYSQLND_G(collect_statistics), mysqlnd_global_stats, (statistic), (value)); \
- MYSQLND_INC_STATISTIC_W_VALUE(MYSQLND_G(collect_statistics), (conn_stats), (statistic), (value));
-
-#define MYSQLND_INC_CONN_STATISTIC_W_VALUE2(conn_stats, statistic1, value1, statistic2, value2) \
- MYSQLND_INC_STATISTIC_W_VALUE2(MYSQLND_G(collect_statistics), mysqlnd_global_stats, (statistic1), (value1), (statistic2), (value2)); \
- MYSQLND_INC_STATISTIC_W_VALUE2(MYSQLND_G(collect_statistics), (conn_stats), (statistic1), (value1), (statistic2), (value2));
-
-#define MYSQLND_INC_CONN_STATISTIC_W_VALUE3(conn_stats, statistic1, value1, statistic2, value2, statistic3, value3) \
- MYSQLND_INC_STATISTIC_W_VALUE3(MYSQLND_G(collect_statistics), mysqlnd_global_stats, (statistic1), (value1), (statistic2), (value2), (statistic3), (value3)); \
- MYSQLND_INC_STATISTIC_W_VALUE3(MYSQLND_G(collect_statistics), (conn_stats), (statistic1), (value1), (statistic2), (value2), (statistic3), (value3));
-
-#else
-
-#define MYSQLND_INC_GLOBAL_STATISTIC(statistic)
-#define MYSQLND_DEC_CONN_STATISTIC(conn_stats, statistic)
-#define MYSQLND_INC_GLOBAL_STATISTIC_W_VALUE2(statistic1, value1, statistic2, value2)
-#define MYSQLND_INC_CONN_STATISTIC(conn_stats, statistic)
-#define MYSQLND_INC_CONN_STATISTIC_W_VALUE(conn_stats, statistic, value)
-#define MYSQLND_INC_CONN_STATISTIC_W_VALUE2(conn_stats, statistic1, value1, statistic2, value2)
-#define MYSQLND_INC_CONN_STATISTIC_W_VALUE3(conn_stats, statistic1, value1, statistic2, value2, statistic3, value3)
-
-#endif /* MYSQLND_CORE_STATISTICS_DISABLED */
+PHPAPI void mysqlnd_stats_init(MYSQLND_STATS ** stats, const size_t statistic_count, const zend_bool persistent);
+PHPAPI void mysqlnd_stats_end(MYSQLND_STATS * stats, const zend_bool persistent);
PHPAPI void mysqlnd_fill_stats_hash(const MYSQLND_STATS * const stats, const MYSQLND_STRING * names, zval *return_value ZEND_FILE_LINE_DC);
-PHPAPI void mysqlnd_stats_init(MYSQLND_STATS ** stats, size_t statistic_count, int persistent);
-PHPAPI void mysqlnd_stats_end(MYSQLND_STATS * stats, int persistent);
-
PHPAPI mysqlnd_stat_trigger mysqlnd_stats_set_trigger(MYSQLND_STATS * const stats, enum_mysqlnd_collected_stats stat, mysqlnd_stat_trigger trigger);
PHPAPI mysqlnd_stat_trigger mysqlnd_stats_reset_triggers(MYSQLND_STATS * const stats);
diff --git a/ext/mysqlnd/mysqlnd_structs.h b/ext/mysqlnd/mysqlnd_structs.h
index c319784481..3f26425604 100644
--- a/ext/mysqlnd/mysqlnd_structs.h
+++ b/ext/mysqlnd/mysqlnd_structs.h
@@ -14,7 +14,6 @@
+----------------------------------------------------------------------+
| Authors: Andrey Hristov <andrey@php.net> |
| Ulf Wendel <uw@php.net> |
- | Georg Richter <georg@php.net> |
+----------------------------------------------------------------------+
*/
@@ -26,11 +25,28 @@
#define MYSQLND_TYPEDEFED_METHODS
#define MYSQLND_CLASS_METHOD_TABLE_NAME(class) mysqlnd_##class##_methods
-#define MYSQLND_CLASS_METHOD_TABLE_NAME_FORWARD(class) struct st_##class##_methods MYSQLND_CLASS_METHOD_TABLE_NAME(class)
+#define MYSQLND_CLASS_METHODS_TYPE(class) struct st_##class##_methods
+#define MYSQLND_CLASS_METHOD_TABLE_NAME_FORWARD(class) MYSQLND_CLASS_METHODS_TYPE(class) MYSQLND_CLASS_METHOD_TABLE_NAME(class)
#define MYSQLND_CLASS_METHODS_START(class) MYSQLND_CLASS_METHOD_TABLE_NAME_FORWARD(class) = {
#define MYSQLND_CLASS_METHODS_END }
+#define MYSQLND_CLASS_METHODS_INSTANCE_NAME(class) mysqlnd_##class##_methods_ptr
+#define MYSQLND_CLASS_METHODS_INSTANCE_DECLARE(class) extern const MYSQLND_CLASS_METHODS_TYPE(class) * MYSQLND_CLASS_METHODS_INSTANCE_NAME(class)
+#define MYSQLND_CLASS_METHODS_INSTANCE_DEFINE(class) const MYSQLND_CLASS_METHODS_TYPE(class) * MYSQLND_CLASS_METHODS_INSTANCE_NAME(class) = & MYSQLND_CLASS_METHOD_TABLE_NAME(class)
+
+typedef struct st_mysqlnd_string
+{
+ char *s;
+ size_t l;
+} MYSQLND_STRING;
+
+typedef struct st_mysqlnd_const_string
+{
+ const char *s;
+ size_t l;
+} MYSQLND_CSTRING;
+
typedef struct st_mysqlnd_memory_pool MYSQLND_MEMORY_POOL;
typedef struct st_mysqlnd_memory_pool_chunk MYSQLND_MEMORY_POOL_CHUNK;
@@ -42,20 +58,18 @@ typedef struct st_mysqlnd_memory_pool_chunk_llist MYSQLND_MEMORY_POOL_CHUNK_LLIS
struct st_mysqlnd_memory_pool
{
zend_uchar *arena;
- unsigned int refcount;
unsigned int arena_size;
unsigned int free_size;
MYSQLND_MEMORY_POOL_CHUNK* (*get_chunk)(MYSQLND_MEMORY_POOL * pool, unsigned int size);
+ enum_func_status (*resize_chunk)(MYSQLND_MEMORY_POOL * pool, MYSQLND_MEMORY_POOL_CHUNK * chunk, unsigned int size);
+ void (*free_chunk)(MYSQLND_MEMORY_POOL * pool, MYSQLND_MEMORY_POOL_CHUNK * chunk);
};
struct st_mysqlnd_memory_pool_chunk
{
size_t app;
- MYSQLND_MEMORY_POOL *pool;
zend_uchar *ptr;
- enum_func_status (*resize_chunk)(MYSQLND_MEMORY_POOL_CHUNK * chunk, unsigned int size);
- void (*free_chunk)(MYSQLND_MEMORY_POOL_CHUNK * chunk);
unsigned int size;
zend_bool from_pool;
};
@@ -77,7 +91,7 @@ typedef struct st_mysqlnd_field
const char *org_table; /* Org table name, if table was an alias */
const char *db; /* Database for table */
const char *catalog; /* Catalog for table */
- char *def; /* Default value (set by mysql_list_fields) */
+ char *def; /* Default value */
zend_ulong length; /* Width of column (create length) */
zend_ulong max_length; /* Max width for selected set */
unsigned int name_length;
@@ -96,22 +110,53 @@ typedef struct st_mysqlnd_field
} MYSQLND_FIELD;
-typedef struct st_mysqlnd_upsert_result
+typedef struct st_mysqlnd_upsert_status MYSQLND_UPSERT_STATUS;
+typedef void (*func_mysqlnd_upsert_status__reset)(MYSQLND_UPSERT_STATUS * const upsert_status);
+typedef void (*func_mysqlnd_upsert_status__set_affected_rows_to_error)(MYSQLND_UPSERT_STATUS * const upsert_status);
+
+MYSQLND_CLASS_METHODS_TYPE(mysqlnd_upsert_status)
+{
+ func_mysqlnd_upsert_status__reset reset;
+ func_mysqlnd_upsert_status__set_affected_rows_to_error set_affected_rows_to_error;
+};
+
+struct st_mysqlnd_upsert_status
{
unsigned int warning_count;
unsigned int server_status;
uint64_t affected_rows;
uint64_t last_insert_id;
-} MYSQLND_UPSERT_STATUS;
+ MYSQLND_CLASS_METHODS_TYPE(mysqlnd_upsert_status) *m;
+};
+
+#define SET_EMPTY_ERROR(info) (info)->m->reset((info))
+#define SET_CLIENT_ERROR(info, err_no, sqlstate, error) (err_no)? (info)->m->set_client_error((info), (err_no), (sqlstate), (error)) : (info)->m->reset((info))
+#define SET_OOM_ERROR(info) SET_CLIENT_ERROR((info), CR_OUT_OF_MEMORY, UNKNOWN_SQLSTATE, mysqlnd_out_of_memory)
+#define COPY_CLIENT_ERROR(dest, source) SET_CLIENT_ERROR((dest), (source).error_no, (source).sqlstate, (source).error)
+
+
+typedef struct st_mysqlnd_error_info MYSQLND_ERROR_INFO;
+typedef void (*func_mysqlnd_error_info__reset)(MYSQLND_ERROR_INFO * const info);
+typedef void (*func_mysqlnd_error_info__set_client_error)(MYSQLND_ERROR_INFO * const info, const unsigned int err_no, const char * const sqlstate, const char * const error);
+
+
+MYSQLND_CLASS_METHODS_TYPE(mysqlnd_error_info)
+{
+ func_mysqlnd_error_info__reset reset;
+ func_mysqlnd_error_info__set_client_error set_client_error;
+};
-typedef struct st_mysqlnd_error_info
+struct st_mysqlnd_error_info
{
char error[MYSQLND_ERRMSG_SIZE+1];
char sqlstate[MYSQLND_SQLSTATE_LENGTH + 1];
unsigned int error_no;
zend_llist * error_list;
-} MYSQLND_ERROR_INFO;
+
+ zend_bool persistent;
+ MYSQLND_CLASS_METHODS_TYPE(mysqlnd_error_info) *m;
+};
typedef struct st_mysqlnd_error_list_element
@@ -154,7 +199,7 @@ typedef struct st_mysqlnd_infile
void (*local_infile_end)(void *ptr);
} MYSQLND_INFILE;
-typedef struct st_mysqlnd_options
+typedef struct st_mysqlnd_session_options
{
ulong flags;
@@ -173,11 +218,11 @@ typedef struct st_mysqlnd_options
in the memory which can crash external code. Feel free to reuse these.
*/
HashTable * connect_attr;
+ char * unused1;
+ char * unused2;
char * unused3;
- char * unused4;
- char * unused5;
- enum_mysqlnd_protocol_type protocol;
+ enum_mysqlnd_session_protocol_type protocol;
char *charset_name;
/* maximum allowed packet size for communication */
@@ -186,9 +231,10 @@ typedef struct st_mysqlnd_options
#ifdef MYSQLND_STRING_TO_INT_CONVERSION
zend_bool int_and_float_native;
#endif
-} MYSQLND_OPTIONS;
+} MYSQLND_SESSION_OPTIONS;
-typedef struct st_mysqlnd_net_options
+
+typedef struct st_mysqlnd_vio_options
{
/* timeouts */
unsigned int timeout_connect;
@@ -211,23 +257,17 @@ typedef struct st_mysqlnd_net_options
#define MYSQLND_SSL_PEER_DEFAULT_ACTION MYSQLND_SSL_PEER_VERIFY
} ssl_verify_peer;
- uint64_t flags;
-
- char * sha256_server_public_key;
-
- char * unused1;
- char * unused2;
- char * unused3;
- char * unused4;
-} MYSQLND_NET_OPTIONS;
+} MYSQLND_VIO_OPTIONS;
typedef struct st_mysqlnd_connection MYSQLND;
typedef struct st_mysqlnd_connection_data MYSQLND_CONN_DATA;
-typedef struct st_mysqlnd_net MYSQLND_NET;
-typedef struct st_mysqlnd_net_data MYSQLND_NET_DATA;
-typedef struct st_mysqlnd_protocol MYSQLND_PROTOCOL;
+typedef struct st_mysqlnd_protocol_frame_codec MYSQLND_PFC;
+typedef struct st_mysqlnd_protocol_frame_codec_data MYSQLND_PFC_DATA;
+typedef struct st_mysqlnd_vio MYSQLND_VIO;
+typedef struct st_mysqlnd_vio_data MYSQLND_VIO_DATA;
+typedef struct st_mysqlnd_protocol_payload_decoder_factory MYSQLND_PROTOCOL_PAYLOAD_DECODER_FACTORY;
typedef struct st_mysqlnd_res MYSQLND_RES;
typedef char** MYSQLND_ROW_C; /* return data as array of strings */
typedef struct st_mysqlnd_stmt_data MYSQLND_STMT_DATA;
@@ -271,158 +311,87 @@ struct st_mysqlnd_stats
};
-typedef struct st_mysqlnd_read_buffer {
- zend_uchar * data;
- size_t offset;
- size_t size;
- size_t len;
- zend_bool (*is_empty)(struct st_mysqlnd_read_buffer *);
- void (*read)(struct st_mysqlnd_read_buffer *, size_t count, zend_uchar * dest);
- size_t (*bytes_left)(struct st_mysqlnd_read_buffer *);
- void (*free_buffer)(struct st_mysqlnd_read_buffer **);
-} MYSQLND_READ_BUFFER;
+typedef enum_func_status (*func_mysqlnd_vio__init)(MYSQLND_VIO * const vio, MYSQLND_STATS * const stats, MYSQLND_ERROR_INFO * const error_info);
+typedef void (*func_mysqlnd_vio__dtor)(MYSQLND_VIO * const vio, MYSQLND_STATS * const conn_stats, MYSQLND_ERROR_INFO * const error_info);
+typedef enum_func_status (*func_mysqlnd_vio__connect)(MYSQLND_VIO * const vio, const MYSQLND_CSTRING scheme, const zend_bool persistent, MYSQLND_STATS * const conn_stats, MYSQLND_ERROR_INFO * const error_info);
+typedef void (*func_mysqlnd_vio__close_stream)(MYSQLND_VIO * const vio, MYSQLND_STATS * const conn_stats, MYSQLND_ERROR_INFO * const error_info);
+typedef php_stream * (*func_mysqlnd_vio__open_stream)(MYSQLND_VIO * const vio, const MYSQLND_CSTRING scheme, const zend_bool persistent, MYSQLND_STATS * const conn_stats, MYSQLND_ERROR_INFO * const error_info);
+typedef php_stream * (*func_mysqlnd_vio__get_stream)(const MYSQLND_VIO * const vio);
+typedef enum_func_status (*func_mysqlnd_vio__set_stream)(MYSQLND_VIO * const vio, php_stream * vio_stream);
+typedef zend_bool (*func_mysqlnd_vio__has_valid_stream)(const MYSQLND_VIO * const vio);
+typedef func_mysqlnd_vio__open_stream (*func_mysqlnd_vio__get_open_stream)(MYSQLND_VIO * const vio, const MYSQLND_CSTRING scheme, MYSQLND_ERROR_INFO * const error_info);
-typedef enum_func_status (*func_mysqlnd_net__set_client_option)(MYSQLND_NET * const net, enum_mysqlnd_option option, const char * const value);
-typedef enum_func_status (*func_mysqlnd_net__decode)(zend_uchar * uncompressed_data, const size_t uncompressed_data_len, const zend_uchar * const compressed_data, const size_t compressed_data_len);
-typedef enum_func_status (*func_mysqlnd_net__encode)(zend_uchar * compress_buffer, size_t * compress_buffer_len, const zend_uchar * const uncompressed_data, const size_t uncompressed_data_len);
-typedef size_t (*func_mysqlnd_net__consume_uneaten_data)(MYSQLND_NET * const net, enum php_mysqlnd_server_command cmd);
-typedef void (*func_mysqlnd_net__free_contents)(MYSQLND_NET * net);
-typedef enum_func_status (*func_mysqlnd_net__enable_ssl)(MYSQLND_NET * const net);
-typedef enum_func_status (*func_mysqlnd_net__disable_ssl)(MYSQLND_NET * const net);
-typedef enum_func_status (*func_mysqlnd_net__network_read_ex)(MYSQLND_NET * const net, zend_uchar * const buffer, const size_t count, MYSQLND_STATS * const stats, MYSQLND_ERROR_INFO * const error_info);
-typedef size_t (*func_mysqlnd_net__network_write_ex)(MYSQLND_NET * const net, const zend_uchar * const buf, const size_t count, MYSQLND_STATS * const stats, MYSQLND_ERROR_INFO * const error_info);
-typedef size_t (*func_mysqlnd_net__send_ex)(MYSQLND_NET * const net, zend_uchar * const buffer, const size_t count, MYSQLND_STATS * const conn_stats, MYSQLND_ERROR_INFO * const error_info);
-typedef enum_func_status (*func_mysqlnd_net__receive_ex)(MYSQLND_NET * const net, zend_uchar * const buffer, const size_t count, MYSQLND_STATS * const conn_stats, MYSQLND_ERROR_INFO * const error_info);
-typedef enum_func_status (*func_mysqlnd_net__init)(MYSQLND_NET * const net, MYSQLND_STATS * const stats, MYSQLND_ERROR_INFO * const error_info);
-typedef void (*func_mysqlnd_net__dtor)(MYSQLND_NET * const net, MYSQLND_STATS * const conn_stats, MYSQLND_ERROR_INFO * const error_info);
-typedef enum_func_status (*func_mysqlnd_net__connect_ex)(MYSQLND_NET * const net, const char * const scheme, const size_t scheme_len, const zend_bool persistent, MYSQLND_STATS * const conn_stats, MYSQLND_ERROR_INFO * const error_info);
-typedef void (*func_mysqlnd_net__close_stream)(MYSQLND_NET * const net, MYSQLND_STATS * const conn_stats, MYSQLND_ERROR_INFO * const error_info);
-typedef php_stream * (*func_mysqlnd_net__open_stream)(MYSQLND_NET * const net, const char * const scheme, const size_t scheme_len, const zend_bool persistent, MYSQLND_STATS * const conn_stats, MYSQLND_ERROR_INFO * const error_info);
-typedef php_stream * (*func_mysqlnd_net__get_stream)(const MYSQLND_NET * const net);
-typedef php_stream * (*func_mysqlnd_net__set_stream)(MYSQLND_NET * const net, php_stream * net_stream);
-typedef func_mysqlnd_net__open_stream (*func_mysqlnd_net__get_open_stream)(MYSQLND_NET * const net, const char * const scheme, const size_t scheme_len, MYSQLND_ERROR_INFO * const error_info);
-typedef void (*func_mysqlnd_net__post_connect_set_opt)(MYSQLND_NET * const net, const char * const scheme, const size_t scheme_len, MYSQLND_STATS * const conn_stats, MYSQLND_ERROR_INFO * const error_info);
-typedef enum_func_status (*func_mysqlnd_net__read_compressed_packet_from_stream_and_fill_read_buffer)(MYSQLND_NET * net, size_t net_payload_size, MYSQLND_STATS * conn_stats, MYSQLND_ERROR_INFO * error_info);
-
-struct st_mysqlnd_net_methods
-{
- func_mysqlnd_net__init init;
- func_mysqlnd_net__dtor dtor;
- func_mysqlnd_net__connect_ex connect_ex;
- func_mysqlnd_net__close_stream close_stream;
- func_mysqlnd_net__open_stream open_pipe;
- func_mysqlnd_net__open_stream open_tcp_or_unix;
-
- func_mysqlnd_net__get_stream get_stream;
- func_mysqlnd_net__set_stream set_stream;
- func_mysqlnd_net__get_open_stream get_open_stream;
-
- func_mysqlnd_net__post_connect_set_opt post_connect_set_opt;
-
- func_mysqlnd_net__set_client_option set_client_option;
- func_mysqlnd_net__decode decode;
- func_mysqlnd_net__encode encode;
- func_mysqlnd_net__consume_uneaten_data consume_uneaten_data;
- func_mysqlnd_net__free_contents free_contents;
- func_mysqlnd_net__enable_ssl enable_ssl;
- func_mysqlnd_net__disable_ssl disable_ssl;
-
- func_mysqlnd_net__network_read_ex network_read_ex;
- func_mysqlnd_net__network_write_ex network_write_ex;
- func_mysqlnd_net__send_ex send_ex;
- func_mysqlnd_net__receive_ex receive_ex;
-
- func_mysqlnd_net__read_compressed_packet_from_stream_and_fill_read_buffer read_compressed_packet_from_stream_and_fill_read_buffer;
+typedef enum_func_status (*func_mysqlnd_vio__set_client_option)(MYSQLND_VIO * const vio, enum_mysqlnd_client_option option, const char * const value);
+typedef void (*func_mysqlnd_vio__post_connect_set_opt)(MYSQLND_VIO * const vio, const MYSQLND_CSTRING scheme, MYSQLND_STATS * const conn_stats, MYSQLND_ERROR_INFO * const error_info);
- void * unused1;
- void * unused2;
- void * unused3;
- void * unused4;
- void * unused5;
-};
+typedef enum_func_status (*func_mysqlnd_vio__enable_ssl)(MYSQLND_VIO * const vio);
+typedef enum_func_status (*func_mysqlnd_vio__disable_ssl)(MYSQLND_VIO * const vio);
+typedef enum_func_status (*func_mysqlnd_vio__network_read)(MYSQLND_VIO * const vio, zend_uchar * const buffer, const size_t count, MYSQLND_STATS * const stats, MYSQLND_ERROR_INFO * const error_info);
+typedef size_t (*func_mysqlnd_vio__network_write)(MYSQLND_VIO * const vio, const zend_uchar * const buf, const size_t count, MYSQLND_STATS * const stats, MYSQLND_ERROR_INFO * const error_info);
+typedef size_t (*func_mysqlnd_vio__consume_uneaten_data)(MYSQLND_VIO * const vio, enum php_mysqlnd_server_command cmd);
-struct st_mysqlnd_packet_greet;
-struct st_mysqlnd_packet_greet;
-struct st_mysqlnd_packet_auth;
-struct st_mysqlnd_packet_ok;
-struct st_mysqlnd_packet_command;
-struct st_mysqlnd_packet_eof;
-struct st_mysqlnd_packet_rset_header;
-struct st_mysqlnd_packet_res_field;
-struct st_mysqlnd_packet_row;
-struct st_mysqlnd_packet_stats;
-struct st_mysqlnd_packet_prepare_response;
-struct st_mysqlnd_packet_chg_user_resp;
-struct st_mysqlnd_packet_auth_pam;
-struct st_mysqlnd_packet_sha256_pk_request;
-struct st_mysqlnd_packet_sha256_pk_request_response;
+typedef void (*func_mysqlnd_vio__free_contents)(MYSQLND_VIO * vio);
-typedef struct st_mysqlnd_packet_greet * (*func_mysqlnd_protocol__get_greet_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent);
-typedef struct st_mysqlnd_packet_auth * (*func_mysqlnd_protocol__get_auth_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent);
-typedef struct st_mysqlnd_packet_auth_response *(*func_mysqlnd_protocol__get_auth_response_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent);
-typedef struct st_mysqlnd_packet_change_auth_response * (*func_mysqlnd_protocol__get_change_auth_response_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent);
-typedef struct st_mysqlnd_packet_ok * (*func_mysqlnd_protocol__get_ok_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent);
-typedef struct st_mysqlnd_packet_command * (*func_mysqlnd_protocol__get_command_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent);
-typedef struct st_mysqlnd_packet_eof * (*func_mysqlnd_protocol__get_eof_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent);
-typedef struct st_mysqlnd_packet_rset_header * (*func_mysqlnd_protocol__get_rset_header_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent);
-typedef struct st_mysqlnd_packet_res_field * (*func_mysqlnd_protocol__get_result_field_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent);
-typedef struct st_mysqlnd_packet_row * (*func_mysqlnd_protocol__get_row_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent);
-typedef struct st_mysqlnd_packet_stats * (*func_mysqlnd_protocol__get_stats_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent);
-typedef struct st_mysqlnd_packet_prepare_response *(*func_mysqlnd_protocol__get_prepare_response_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent);
-typedef struct st_mysqlnd_packet_chg_user_resp*(*func_mysqlnd_protocol__get_change_user_response_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent);
-typedef struct st_mysqlnd_packet_sha256_pk_request *(*func_mysqlnd_protocol__get_sha256_pk_request_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent);
-typedef struct st_mysqlnd_packet_sha256_pk_request_response *(*func_mysqlnd_protocol__get_sha256_pk_request_response_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent);
-
-struct st_mysqlnd_protocol_methods
+
+MYSQLND_CLASS_METHODS_TYPE(mysqlnd_vio)
{
- func_mysqlnd_protocol__get_greet_packet get_greet_packet;
- func_mysqlnd_protocol__get_auth_packet get_auth_packet;
- func_mysqlnd_protocol__get_auth_response_packet get_auth_response_packet;
- func_mysqlnd_protocol__get_change_auth_response_packet get_change_auth_response_packet;
- func_mysqlnd_protocol__get_ok_packet get_ok_packet;
- func_mysqlnd_protocol__get_command_packet get_command_packet;
- func_mysqlnd_protocol__get_eof_packet get_eof_packet;
- func_mysqlnd_protocol__get_rset_header_packet get_rset_header_packet;
- func_mysqlnd_protocol__get_result_field_packet get_result_field_packet;
- func_mysqlnd_protocol__get_row_packet get_row_packet;
- func_mysqlnd_protocol__get_stats_packet get_stats_packet;
- func_mysqlnd_protocol__get_prepare_response_packet get_prepare_response_packet;
- func_mysqlnd_protocol__get_change_user_response_packet get_change_user_response_packet;
- func_mysqlnd_protocol__get_sha256_pk_request_packet get_sha256_pk_request_packet;
- func_mysqlnd_protocol__get_sha256_pk_request_response_packet get_sha256_pk_request_response_packet;
+ func_mysqlnd_vio__init init;
+ func_mysqlnd_vio__dtor dtor;
+ func_mysqlnd_vio__connect connect;
- void * unused1;
- void * unused2;
- void * unused3;
+ func_mysqlnd_vio__close_stream close_stream;
+ func_mysqlnd_vio__open_stream open_pipe;
+ func_mysqlnd_vio__open_stream open_tcp_or_unix;
+
+ func_mysqlnd_vio__get_stream get_stream;
+ func_mysqlnd_vio__set_stream set_stream;
+ func_mysqlnd_vio__has_valid_stream has_valid_stream;
+ func_mysqlnd_vio__get_open_stream get_open_stream;
+
+ func_mysqlnd_vio__set_client_option set_client_option;
+ func_mysqlnd_vio__post_connect_set_opt post_connect_set_opt;
+
+ func_mysqlnd_vio__enable_ssl enable_ssl;
+ func_mysqlnd_vio__disable_ssl disable_ssl;
+
+ func_mysqlnd_vio__network_read network_read;
+ func_mysqlnd_vio__network_write network_write;
+
+ func_mysqlnd_vio__consume_uneaten_data consume_uneaten_data;
+
+ func_mysqlnd_vio__free_contents free_contents;
};
-typedef MYSQLND * (*func_mysqlnd_object_factory__get_connection)(zend_bool persistent);
+MYSQLND_CLASS_METHODS_TYPE(mysqlnd_object_factory);
+
+typedef MYSQLND * (*func_mysqlnd_object_factory__get_connection)(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_object_factory) * factory, const zend_bool persistent);
typedef MYSQLND * (*func_mysqlnd_object_factory__clone_connection_object)(MYSQLND * conn);
-typedef MYSQLND_STMT * (*func_mysqlnd_object_factory__get_prepared_statement)(MYSQLND_CONN_DATA * conn);
-typedef MYSQLND_NET * (*func_mysqlnd_object_factory__get_io_channel)(zend_bool persistent, MYSQLND_STATS * stats, MYSQLND_ERROR_INFO * error_info);
-typedef MYSQLND_PROTOCOL * (*func_mysqlnd_object_factory__get_protocol_decoder)(zend_bool persistent);
+typedef MYSQLND_STMT * (*func_mysqlnd_object_factory__get_prepared_statement)(MYSQLND_CONN_DATA * conn, const zend_bool persistent);
+typedef MYSQLND_PFC * (*func_mysqlnd_object_factory__get_pfc)(const zend_bool persistent, MYSQLND_STATS * stats, MYSQLND_ERROR_INFO * error_info);
+typedef MYSQLND_VIO * (*func_mysqlnd_object_factory__get_vio)(const zend_bool persistent, MYSQLND_STATS * stats, MYSQLND_ERROR_INFO * error_info);
+typedef MYSQLND_PROTOCOL_PAYLOAD_DECODER_FACTORY * (*func_mysqlnd_object_factory__get_protocol_payload_decoder_factory)(MYSQLND_CONN_DATA * conn, const zend_bool persistent);
-struct st_mysqlnd_object_factory_methods
+MYSQLND_CLASS_METHODS_TYPE(mysqlnd_object_factory)
{
func_mysqlnd_object_factory__get_connection get_connection;
func_mysqlnd_object_factory__clone_connection_object clone_connection_object;
func_mysqlnd_object_factory__get_prepared_statement get_prepared_statement;
- func_mysqlnd_object_factory__get_io_channel get_io_channel;
- func_mysqlnd_object_factory__get_protocol_decoder get_protocol_decoder;
+ func_mysqlnd_object_factory__get_pfc get_protocol_frame_codec;
+ func_mysqlnd_object_factory__get_vio get_vio;
+ func_mysqlnd_object_factory__get_protocol_payload_decoder_factory get_protocol_payload_decoder_factory;
};
-typedef enum_func_status (*func_mysqlnd_conn_data__init)(MYSQLND_CONN_DATA * conn);
-typedef enum_func_status (*func_mysqlnd_conn_data__connect)(MYSQLND_CONN_DATA * conn, const char * host, const char * user, const char * passwd, unsigned int passwd_len, const char * db, unsigned int db_len, unsigned int port, const char * socket_or_pipe, unsigned int mysql_flags);
-typedef zend_ulong (*func_mysqlnd_conn_data__escape_string)(MYSQLND_CONN_DATA * const conn, char *newstr, const char *escapestr, size_t escapestr_len);
+typedef enum_func_status (*func_mysqlnd_conn_data__connect)(MYSQLND_CONN_DATA * conn, MYSQLND_CSTRING hostname, MYSQLND_CSTRING username, MYSQLND_CSTRING password, MYSQLND_CSTRING database, unsigned int port, MYSQLND_CSTRING socket_or_pipe, unsigned int mysql_flags);
+typedef zend_ulong (*func_mysqlnd_conn_data__escape_string)(MYSQLND_CONN_DATA * const conn, char *newstr, const char *escapestr, size_t escapestr_len);
typedef enum_func_status (*func_mysqlnd_conn_data__set_charset)(MYSQLND_CONN_DATA * const conn, const char * const charset);
-typedef enum_func_status (*func_mysqlnd_conn_data__query)(MYSQLND_CONN_DATA * conn, const char * query, unsigned int query_len);
-typedef enum_func_status (*func_mysqlnd_conn_data__send_query)(MYSQLND_CONN_DATA * conn, const char *query, unsigned int query_len, enum_mysqlnd_send_query_type type, zval *read_cb, zval *err_cb);
+typedef enum_func_status (*func_mysqlnd_conn_data__query)(MYSQLND_CONN_DATA * conn, const char * const query, const size_t query_len);
+typedef enum_func_status (*func_mysqlnd_conn_data__send_query)(MYSQLND_CONN_DATA * conn, const char * const query, const size_t query_len, enum_mysqlnd_send_query_type type, zval *read_cb, zval *err_cb);
typedef enum_func_status (*func_mysqlnd_conn_data__reap_query)(MYSQLND_CONN_DATA * conn, enum_mysqlnd_reap_result_type type);
typedef MYSQLND_RES * (*func_mysqlnd_conn_data__use_result)(MYSQLND_CONN_DATA * const conn, const unsigned int flags);
typedef MYSQLND_RES * (*func_mysqlnd_conn_data__store_result)(MYSQLND_CONN_DATA * const conn, const unsigned int flags);
@@ -436,7 +405,7 @@ typedef enum_func_status (*func_mysqlnd_conn_data__refresh_server)(MYSQLND_CONN_
typedef enum_func_status (*func_mysqlnd_conn_data__ping)(MYSQLND_CONN_DATA * const conn);
typedef enum_func_status (*func_mysqlnd_conn_data__kill_connection)(MYSQLND_CONN_DATA * conn, unsigned int pid);
-typedef enum_func_status (*func_mysqlnd_conn_data__select_db)(MYSQLND_CONN_DATA * const conn, const char * const db, unsigned int db_len);
+typedef enum_func_status (*func_mysqlnd_conn_data__select_db)(MYSQLND_CONN_DATA * const conn, const char * const db, const size_t db_len);
typedef enum_func_status (*func_mysqlnd_conn_data__server_dump_debug_information)(MYSQLND_CONN_DATA * const conn);
typedef enum_func_status (*func_mysqlnd_conn_data__change_user)(MYSQLND_CONN_DATA * const conn, const char * user, const char * passwd, const char * db, zend_bool silent, size_t passwd_len);
@@ -446,15 +415,14 @@ typedef const char * (*func_mysqlnd_conn_data__get_sqlstate)(const MYSQLND_CONN
typedef uint64_t (*func_mysqlnd_conn_data__get_thread_id)(const MYSQLND_CONN_DATA * const conn);
typedef void (*func_mysqlnd_conn_data__get_statistics)(const MYSQLND_CONN_DATA * const conn, zval *return_value ZEND_FILE_LINE_DC);
-typedef zend_ulong (*func_mysqlnd_conn_data__get_server_version)(const MYSQLND_CONN_DATA * const conn);
+typedef zend_ulong (*func_mysqlnd_conn_data__get_server_version)(const MYSQLND_CONN_DATA * const conn);
typedef const char * (*func_mysqlnd_conn_data__get_server_information)(const MYSQLND_CONN_DATA * const conn);
typedef enum_func_status (*func_mysqlnd_conn_data__get_server_statistics)(MYSQLND_CONN_DATA * conn, zend_string **message);
typedef const char * (*func_mysqlnd_conn_data__get_host_information)(const MYSQLND_CONN_DATA * const conn);
typedef unsigned int (*func_mysqlnd_conn_data__get_protocol_information)(const MYSQLND_CONN_DATA * const conn);
typedef const char * (*func_mysqlnd_conn_data__get_last_message)(const MYSQLND_CONN_DATA * const conn);
typedef const char * (*func_mysqlnd_conn_data__charset_name)(const MYSQLND_CONN_DATA * const conn);
-typedef MYSQLND_RES * (*func_mysqlnd_conn_data__list_fields)(MYSQLND_CONN_DATA * conn, const char * table, const char * achtung_wild);
-typedef MYSQLND_RES * (*func_mysqlnd_conn_data__list_method)(MYSQLND_CONN_DATA * conn, const char * query, const char * achtung_wild, char *par1);
+typedef MYSQLND_RES * (*func_mysqlnd_conn_data__list_method)(MYSQLND_CONN_DATA * conn, const char * const query, const char * const achtung_wild, const char * const par1);
typedef uint64_t (*func_mysqlnd_conn_data__get_last_insert_id)(const MYSQLND_CONN_DATA * const conn);
typedef uint64_t (*func_mysqlnd_conn_data__get_affected_rows)(const MYSQLND_CONN_DATA * const conn);
@@ -464,7 +432,7 @@ typedef unsigned int (*func_mysqlnd_conn_data__get_field_count)(const MYSQLND_C
typedef unsigned int (*func_mysqlnd_conn_data__get_server_status)(const MYSQLND_CONN_DATA * const conn);
typedef enum_func_status (*func_mysqlnd_conn_data__set_server_option)(MYSQLND_CONN_DATA * const conn, enum_mysqlnd_server_option option);
-typedef enum_func_status (*func_mysqlnd_conn_data__set_client_option)(MYSQLND_CONN_DATA * const conn, enum_mysqlnd_option option, const char * const value);
+typedef enum_func_status (*func_mysqlnd_conn_data__set_client_option)(MYSQLND_CONN_DATA * const conn, enum_mysqlnd_client_option option, const char * const value);
typedef void (*func_mysqlnd_conn_data__free_contents)(MYSQLND_CONN_DATA * conn);/* private */
typedef void (*func_mysqlnd_conn_data__free_options)(MYSQLND_CONN_DATA * conn); /* private */
typedef void (*func_mysqlnd_conn_data__dtor)(MYSQLND_CONN_DATA * conn); /* private */
@@ -473,11 +441,9 @@ typedef enum_func_status (*func_mysqlnd_conn_data__query_read_result_set_header)
typedef MYSQLND_CONN_DATA * (*func_mysqlnd_conn_data__get_reference)(MYSQLND_CONN_DATA * const conn);
typedef enum_func_status (*func_mysqlnd_conn_data__free_reference)(MYSQLND_CONN_DATA * const conn);
-typedef enum mysqlnd_connection_state (*func_mysqlnd_conn_data__get_state)(const MYSQLND_CONN_DATA * const conn);
-typedef void (*func_mysqlnd_conn_data__set_state)(MYSQLND_CONN_DATA * const conn, enum mysqlnd_connection_state new_state);
-typedef enum_func_status (*func_mysqlnd_conn_data__simple_command)(MYSQLND_CONN_DATA * conn, enum php_mysqlnd_server_command command, const zend_uchar * const arg, size_t arg_len, enum mysqlnd_packet_type ok_packet, zend_bool silent, zend_bool ignore_upsert_status);
-typedef enum_func_status (*func_mysqlnd_conn_data__simple_command_handle_response)(MYSQLND_CONN_DATA * conn, enum mysqlnd_packet_type ok_packet, zend_bool silent, enum php_mysqlnd_server_command command, zend_bool ignore_upsert_status);
+typedef enum_func_status (*func_mysqlnd_conn_data__send_command_do_request)(MYSQLND_CONN_DATA * const conn, const enum php_mysqlnd_server_command command, const zend_uchar * const arg, const size_t arg_len, const zend_bool silent, const zend_bool ignore_upsert_status);
+typedef enum_func_status (*func_mysqlnd_conn_data__send_command_handle_response)(MYSQLND_CONN_DATA * const conn, const enum mysqlnd_packet_type ok_packet, const zend_bool silent, const enum php_mysqlnd_server_command command, const zend_bool ignore_upsert_status);
typedef enum_func_status (*func_mysqlnd_conn_data__restart_psession)(MYSQLND_CONN_DATA * conn);
typedef enum_func_status (*func_mysqlnd_conn_data__end_psession)(MYSQLND_CONN_DATA * conn);
@@ -496,24 +462,25 @@ typedef void (*func_mysqlnd_conn_data__tx_cor_options_to_string)(const MYSQLN
typedef enum_func_status (*func_mysqlnd_conn_data__tx_savepoint)(MYSQLND_CONN_DATA * conn, const char * const name);
typedef enum_func_status (*func_mysqlnd_conn_data__tx_savepoint_release)(MYSQLND_CONN_DATA * conn, const char * const name);
-typedef enum_func_status (*func_mysqlnd_conn_data__local_tx_start)(MYSQLND_CONN_DATA * conn, size_t this_func);
-typedef enum_func_status (*func_mysqlnd_conn_data__local_tx_end)(MYSQLND_CONN_DATA * conn, size_t this_func, enum_func_status status);
+typedef enum_func_status (*func_mysqlnd_conn_data__local_tx_start)(MYSQLND_CONN_DATA * conn, const size_t this_func);
+typedef enum_func_status (*func_mysqlnd_conn_data__local_tx_end)(MYSQLND_CONN_DATA * conn, const size_t this_func, const enum_func_status status);
typedef enum_func_status (*func_mysqlnd_conn_data__execute_init_commands)(MYSQLND_CONN_DATA * conn);
typedef unsigned int (*func_mysqlnd_conn_data__get_updated_connect_flags)(MYSQLND_CONN_DATA * conn, unsigned int mysql_flags);
-typedef enum_func_status (*func_mysqlnd_conn_data__connect_handshake)(MYSQLND_CONN_DATA * conn, const char * const host, const char * const user, const char * const passwd, const unsigned int passwd_len, const char * const db, const unsigned int db_len, const unsigned int mysql_flags);
-typedef enum_func_status (*func_mysqlnd_conn_data__simple_command_send_request)(MYSQLND_CONN_DATA * conn, enum php_mysqlnd_server_command command, const zend_uchar * const arg, size_t arg_len, zend_bool silent, zend_bool ignore_upsert_status);
+typedef enum_func_status (*func_mysqlnd_conn_data__connect_handshake)(MYSQLND_CONN_DATA * conn, const MYSQLND_CSTRING * const scheme, const MYSQLND_CSTRING * const username, const MYSQLND_CSTRING * const password, const MYSQLND_CSTRING * const database, const unsigned int mysql_flags);
typedef struct st_mysqlnd_authentication_plugin * (*func_mysqlnd_conn_data__fetch_auth_plugin_by_name)(const char * const requested_protocol);
-typedef enum_func_status (*func_mysqlnd_conn_data__set_client_option_2d)(MYSQLND_CONN_DATA * const conn, enum mysqlnd_option option, const char * const key, const char * const value);
+typedef enum_func_status (*func_mysqlnd_conn_data__set_client_option_2d)(MYSQLND_CONN_DATA * const conn, const enum_mysqlnd_client_option option, const char * const key, const char * const value);
-typedef unsigned int (*func_mysqlnd_conn_data__negotiate_client_api_capabilities)(MYSQLND_CONN_DATA * const conn, const unsigned int flags);
-typedef unsigned int (*func_mysqlnd_conn_data__get_client_api_capabilities)(const MYSQLND_CONN_DATA * const conn);
+typedef size_t (*func_mysqlnd_conn_data__negotiate_client_api_capabilities)(MYSQLND_CONN_DATA * const conn, const size_t flags);
+typedef size_t (*func_mysqlnd_conn_data__get_client_api_capabilities)(const MYSQLND_CONN_DATA * const conn);
+typedef MYSQLND_STRING (*func_mysqlnd_conn_data__get_scheme)(MYSQLND_CONN_DATA * conn, MYSQLND_CSTRING hostname, MYSQLND_CSTRING *socket_or_pipe, unsigned int port, zend_bool * unix_socket, zend_bool * named_pipe);
-struct st_mysqlnd_conn_data_methods
+
+
+MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn_data)
{
- func_mysqlnd_conn_data__init init;
func_mysqlnd_conn_data__connect connect;
func_mysqlnd_conn_data__escape_string escape_string;
func_mysqlnd_conn_data__set_charset set_charset;
@@ -549,7 +516,6 @@ struct st_mysqlnd_conn_data_methods
func_mysqlnd_conn_data__get_protocol_information get_protocol_information;
func_mysqlnd_conn_data__get_last_message get_last_message;
func_mysqlnd_conn_data__charset_name charset_name;
- func_mysqlnd_conn_data__list_fields list_fields;
func_mysqlnd_conn_data__list_method list_method;
func_mysqlnd_conn_data__get_last_insert_id get_last_insert_id;
@@ -570,11 +536,6 @@ struct st_mysqlnd_conn_data_methods
func_mysqlnd_conn_data__get_reference get_reference;
func_mysqlnd_conn_data__free_reference free_reference;
- func_mysqlnd_conn_data__get_state get_state;
- func_mysqlnd_conn_data__set_state set_state;
-
- func_mysqlnd_conn_data__simple_command simple_command;
- func_mysqlnd_conn_data__simple_command_handle_response simple_command_handle_response;
func_mysqlnd_conn_data__restart_psession restart_psession;
func_mysqlnd_conn_data__end_psession end_psession;
@@ -598,22 +559,23 @@ struct st_mysqlnd_conn_data_methods
func_mysqlnd_conn_data__execute_init_commands execute_init_commands;
func_mysqlnd_conn_data__get_updated_connect_flags get_updated_connect_flags;
func_mysqlnd_conn_data__connect_handshake connect_handshake;
- func_mysqlnd_conn_data__simple_command_send_request simple_command_send_request;
func_mysqlnd_conn_data__fetch_auth_plugin_by_name fetch_auth_plugin_by_name;
func_mysqlnd_conn_data__set_client_option_2d set_client_option_2d;
func_mysqlnd_conn_data__negotiate_client_api_capabilities negotiate_client_api_capabilities;
func_mysqlnd_conn_data__get_client_api_capabilities get_client_api_capabilities;
+
+ func_mysqlnd_conn_data__get_scheme get_scheme;
};
-typedef enum_func_status (*func_mysqlnd_data__connect)(MYSQLND * conn, const char * host, const char * user, const char * passwd, unsigned int passwd_len, const char * db, unsigned int db_len, unsigned int port, const char * socket_or_pipe, unsigned int mysql_flags);
+typedef enum_func_status (*func_mysqlnd_data__connect)(MYSQLND * conn, const MYSQLND_CSTRING hostname, const MYSQLND_CSTRING username, const MYSQLND_CSTRING password, const MYSQLND_CSTRING database, unsigned int port, const MYSQLND_CSTRING socket_or_pipe, unsigned int mysql_flags);
typedef MYSQLND * (*func_mysqlnd_conn__clone_object)(MYSQLND * const conn);
typedef void (*func_mysqlnd_conn__dtor)(MYSQLND * conn);
-typedef enum_func_status (*func_mysqlnd_conn__close)(MYSQLND * conn, enum_connection_close_type close_type);
+typedef enum_func_status (*func_mysqlnd_conn__close)(MYSQLND * conn, const enum_connection_close_type close_type);
-struct st_mysqlnd_conn_methods
+MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn)
{
func_mysqlnd_data__connect connect;
func_mysqlnd_conn__clone_object clone_object;
@@ -621,13 +583,14 @@ struct st_mysqlnd_conn_methods
func_mysqlnd_conn__close close;
};
+
/* for decoding - binary or text protocol */
typedef enum_func_status (*func_mysqlnd_res__row_decoder)(MYSQLND_MEMORY_POOL_CHUNK * row_buffer, zval * fields,
unsigned int field_count, const MYSQLND_FIELD * fields_metadata,
zend_bool as_int_or_float, MYSQLND_STATS * stats);
-typedef MYSQLND_RES * (*func_mysqlnd_res__use_result)(MYSQLND_RES * const result, zend_bool ps_protocol);
+typedef MYSQLND_RES * (*func_mysqlnd_res__use_result)(MYSQLND_RES * const result, const zend_bool ps_protocol);
typedef MYSQLND_RES * (*func_mysqlnd_res__store_result)(MYSQLND_RES * result, MYSQLND_CONN_DATA * const conn, const unsigned int flags);
typedef void (*func_mysqlnd_res__fetch_into)(MYSQLND_RES *result, const unsigned int flags, zval *return_value, enum_mysqlnd_extension ext ZEND_FILE_LINE_DC);
typedef MYSQLND_ROW_C (*func_mysqlnd_res__fetch_row_c)(MYSQLND_RES *result);
@@ -644,11 +607,11 @@ typedef const MYSQLND_FIELD *(*func_mysqlnd_res__fetch_field_direct)(MYSQLND_RES
typedef const MYSQLND_FIELD *(*func_mysqlnd_res__fetch_fields)(MYSQLND_RES * const result);
typedef enum_func_status (*func_mysqlnd_res__read_result_metadata)(MYSQLND_RES * result, MYSQLND_CONN_DATA * conn);
-typedef zend_ulong * (*func_mysqlnd_res__fetch_lengths)(MYSQLND_RES * const result);
+typedef const size_t * (*func_mysqlnd_res__fetch_lengths)(MYSQLND_RES * const result);
typedef enum_func_status (*func_mysqlnd_res__store_result_fetch_data)(MYSQLND_CONN_DATA * const conn, MYSQLND_RES * result, MYSQLND_RES_METADATA * meta, MYSQLND_MEMORY_POOL_CHUNK *** row_buffers, zend_bool binary_protocol);
typedef void (*func_mysqlnd_res__free_result_buffers)(MYSQLND_RES * result); /* private */
-typedef enum_func_status (*func_mysqlnd_res__free_result)(MYSQLND_RES * result, zend_bool implicit);
+typedef enum_func_status (*func_mysqlnd_res__free_result)(MYSQLND_RES * result, const zend_bool implicit);
typedef void (*func_mysqlnd_res__free_result_internal)(MYSQLND_RES *result);
typedef void (*func_mysqlnd_res__free_result_contents)(MYSQLND_RES *result);
typedef void (*func_mysqlnd_res__free_buffered_data)(MYSQLND_RES *result);
@@ -657,7 +620,7 @@ typedef void (*func_mysqlnd_res__unbuffered_free_last_data)(MYSQLND_RES *resu
typedef MYSQLND_RES_METADATA * (*func_mysqlnd_res__result_meta_init)(unsigned int field_count, zend_bool persistent);
-struct st_mysqlnd_res_methods
+MYSQLND_CLASS_METHODS_TYPE(mysqlnd_res)
{
mysqlnd_fetch_row_func fetch_row;
@@ -694,12 +657,12 @@ struct st_mysqlnd_res_methods
};
-typedef uint64_t (*func_mysqlnd_result_unbuffered__num_rows)(const MYSQLND_RES_UNBUFFERED * const result);
-typedef zend_ulong * (*func_mysqlnd_result_unbuffered__fetch_lengths)(MYSQLND_RES_UNBUFFERED * const result);
-typedef void (*func_mysqlnd_result_unbuffered__free_last_data)(MYSQLND_RES_UNBUFFERED * result, MYSQLND_STATS * const global_stats);
-typedef void (*func_mysqlnd_result_unbuffered__free_result)(MYSQLND_RES_UNBUFFERED * const result, MYSQLND_STATS * const global_stats);
+typedef uint64_t (*func_mysqlnd_result_unbuffered__num_rows)(const MYSQLND_RES_UNBUFFERED * const result);
+typedef const size_t * (*func_mysqlnd_result_unbuffered__fetch_lengths)(MYSQLND_RES_UNBUFFERED * const result);
+typedef void (*func_mysqlnd_result_unbuffered__free_last_data)(MYSQLND_RES_UNBUFFERED * result, MYSQLND_STATS * const global_stats);
+typedef void (*func_mysqlnd_result_unbuffered__free_result)(MYSQLND_RES_UNBUFFERED * const result, MYSQLND_STATS * const global_stats);
-struct st_mysqlnd_result_unbuffered_methods
+MYSQLND_CLASS_METHODS_TYPE(mysqlnd_result_unbuffered)
{
mysqlnd_fetch_row_func fetch_row;
func_mysqlnd_res__row_decoder row_decoder;
@@ -712,11 +675,11 @@ struct st_mysqlnd_result_unbuffered_methods
typedef uint64_t (*func_mysqlnd_result_buffered__num_rows)(const MYSQLND_RES_BUFFERED * const result);
typedef enum_func_status (*func_mysqlnd_result_buffered__initialize_result_set_rest)(MYSQLND_RES_BUFFERED * const result, MYSQLND_RES_METADATA * const meta,
MYSQLND_STATS * stats, zend_bool int_and_float_native);
-typedef zend_ulong * (*func_mysqlnd_result_buffered__fetch_lengths)(MYSQLND_RES_BUFFERED * const result);
+typedef const size_t * (*func_mysqlnd_result_buffered__fetch_lengths)(MYSQLND_RES_BUFFERED * const result);
typedef enum_func_status (*func_mysqlnd_result_buffered__data_seek)(MYSQLND_RES_BUFFERED * const result, const uint64_t row);
typedef void (*func_mysqlnd_result_buffered__free_result)(MYSQLND_RES_BUFFERED * const result);
-struct st_mysqlnd_result_buffered_methods
+MYSQLND_CLASS_METHODS_TYPE(mysqlnd_result_buffered)
{
mysqlnd_fetch_row_func fetch_row;
func_mysqlnd_res__row_decoder row_decoder;
@@ -734,10 +697,10 @@ typedef const MYSQLND_FIELD * (*func_mysqlnd_res_meta__fetch_fields)(MYSQLND_RES
typedef MYSQLND_FIELD_OFFSET (*func_mysqlnd_res_meta__field_tell)(const MYSQLND_RES_METADATA * const meta);
typedef MYSQLND_FIELD_OFFSET (*func_mysqlnd_res_meta__field_seek)(MYSQLND_RES_METADATA * const meta, const MYSQLND_FIELD_OFFSET field_offset);
typedef enum_func_status (*func_mysqlnd_res_meta__read_metadata)(MYSQLND_RES_METADATA * const meta, MYSQLND_CONN_DATA * conn);
-typedef MYSQLND_RES_METADATA * (*func_mysqlnd_res_meta__clone_metadata)(const MYSQLND_RES_METADATA * const meta, zend_bool persistent);
+typedef MYSQLND_RES_METADATA * (*func_mysqlnd_res_meta__clone_metadata)(const MYSQLND_RES_METADATA * const meta, const zend_bool persistent);
typedef void (*func_mysqlnd_res_meta__free_metadata)(MYSQLND_RES_METADATA * meta);
-struct st_mysqlnd_res_meta_methods
+MYSQLND_CLASS_METHODS_TYPE(mysqlnd_res_meta)
{
func_mysqlnd_res_meta__fetch_field fetch_field;
func_mysqlnd_res_meta__fetch_field_direct fetch_field_direct;
@@ -750,8 +713,8 @@ struct st_mysqlnd_res_meta_methods
};
-typedef enum_func_status (*func_mysqlnd_stmt__prepare)(MYSQLND_STMT * const stmt, const char * const query, unsigned int query_len);
-typedef enum_func_status (*func_mysqlnd_stmt__send_execute)(MYSQLND_STMT * const s, enum_mysqlnd_send_execute_type type, zval * read_cb, zval * err_cb);
+typedef enum_func_status (*func_mysqlnd_stmt__prepare)(MYSQLND_STMT * const stmt, const char * const query, const size_t query_len);
+typedef enum_func_status (*func_mysqlnd_stmt__send_execute)(MYSQLND_STMT * const s, const enum_mysqlnd_send_execute_type type, zval * read_cb, zval * err_cb);
typedef enum_func_status (*func_mysqlnd_stmt__execute)(MYSQLND_STMT * const stmt);
typedef MYSQLND_RES * (*func_mysqlnd_stmt__use_result)(MYSQLND_STMT * const stmt);
typedef MYSQLND_RES * (*func_mysqlnd_stmt__store_result)(MYSQLND_STMT * const stmt);
@@ -761,7 +724,7 @@ typedef enum_func_status (*func_mysqlnd_stmt__next_result)(MYSQLND_STMT * const
typedef enum_func_status (*func_mysqlnd_stmt__free_result)(MYSQLND_STMT * const stmt);
typedef enum_func_status (*func_mysqlnd_stmt__seek_data)(const MYSQLND_STMT * const stmt, uint64_t row);
typedef enum_func_status (*func_mysqlnd_stmt__reset)(MYSQLND_STMT * const stmt);
-typedef enum_func_status (*func_mysqlnd_stmt__net_close)(MYSQLND_STMT * const stmt, zend_bool implicit); /* private */
+typedef enum_func_status (*func_mysqlnd_stmt__close_on_server)(MYSQLND_STMT * const stmt, zend_bool implicit); /* private */
typedef enum_func_status (*func_mysqlnd_stmt__dtor)(MYSQLND_STMT * const stmt, zend_bool implicit); /* use this for mysqlnd_stmt_close */
typedef enum_func_status (*func_mysqlnd_stmt__fetch)(MYSQLND_STMT * const stmt, zend_bool * const fetched_anything);
typedef enum_func_status (*func_mysqlnd_stmt__bind_parameters)(MYSQLND_STMT * const stmt, MYSQLND_PARAM_BIND * const param_bind);
@@ -794,7 +757,7 @@ typedef void (*func_mysqlnd_stmt__free_stmt_content)(MYSQLND_STMT * const s)
typedef enum_func_status (*func_mysqlnd_stmt__flush)(MYSQLND_STMT * const stmt);
typedef void (*func_mysqlnd_stmt__free_stmt_result)(MYSQLND_STMT * const s);
-struct st_mysqlnd_stmt_methods
+MYSQLND_CLASS_METHODS_TYPE(mysqlnd_stmt)
{
func_mysqlnd_stmt__prepare prepare;
func_mysqlnd_stmt__send_execute send_execute;
@@ -807,7 +770,7 @@ struct st_mysqlnd_stmt_methods
func_mysqlnd_stmt__free_result free_result;
func_mysqlnd_stmt__seek_data seek_data;
func_mysqlnd_stmt__reset reset;
- func_mysqlnd_stmt__net_close net_close;
+ func_mysqlnd_stmt__close_on_server close_on_server;
func_mysqlnd_stmt__dtor dtor;
func_mysqlnd_stmt__fetch fetch;
@@ -854,103 +817,96 @@ struct st_mysqlnd_stmt_methods
};
-struct st_mysqlnd_net_data
+struct st_mysqlnd_vio_data
{
php_stream *stream;
- zend_bool compressed;
zend_bool ssl;
+ MYSQLND_VIO_OPTIONS options;
#ifdef MYSQLND_DO_WIRE_CHECK_BEFORE_COMMAND
zend_uchar last_command;
#else
zend_uchar unused_pad1;
#endif
- MYSQLND_NET_OPTIONS options;
-
- unsigned int refcount;
zend_bool persistent;
- struct st_mysqlnd_net_methods m;
+ MYSQLND_CLASS_METHODS_TYPE(mysqlnd_vio) m;
};
-struct st_mysqlnd_net
+struct st_mysqlnd_vio
{
- /* cmd buffer */
- MYSQLND_CMD_BUFFER cmd_buffer;
+ struct st_mysqlnd_vio_data * data;
- struct st_mysqlnd_net_data * data;
+ zend_bool persistent;
+};
-#ifdef MYSQLND_COMPRESSION_ENABLED
- MYSQLND_READ_BUFFER * uncompressed_data;
-#else
- void * unused_pad1;
-#endif
- zend_bool persistent;
- /* sequence for simple checking of correct packets */
- zend_uchar packet_no;
- zend_uchar compressed_envelope_packet_no;
+struct st_mysqlnd_protocol_command
+{
+ enum_func_status (*run)(void *cmd);
+ void (*free_command)(void * cmd);
};
+typedef struct st_mysqlnd_protocol_command * (*func_mysqlnd__command_factory)(enum php_mysqlnd_server_command command, ...);
+
-struct st_mysqlnd_protocol
+
+typedef struct st_mysqlnd_connection_state MYSQLND_CONNECTION_STATE;
+typedef enum mysqlnd_connection_state (*func_mysqlnd_connection_state__get)(const MYSQLND_CONNECTION_STATE * const state_struct);
+typedef void (*func_mysqlnd_connection_state__set)(MYSQLND_CONNECTION_STATE * const state_struct, const enum mysqlnd_connection_state state);
+
+
+MYSQLND_CLASS_METHODS_TYPE(mysqlnd_connection_state)
{
- zend_bool persistent;
- struct st_mysqlnd_protocol_methods m;
+ func_mysqlnd_connection_state__get get;
+ func_mysqlnd_connection_state__set set;
};
+struct st_mysqlnd_connection_state
+{
+ enum mysqlnd_connection_state state;
+
+ MYSQLND_CLASS_METHODS_TYPE(mysqlnd_connection_state) *m;
+};
struct st_mysqlnd_connection_data
{
/* Operation related */
- MYSQLND_NET * net;
- MYSQLND_PROTOCOL * protocol;
+ MYSQLND_PFC * protocol_frame_codec;
+ MYSQLND_VIO * vio;
+ MYSQLND_PROTOCOL_PAYLOAD_DECODER_FACTORY * payload_decoder_factory;
/* Information related */
- char *host;
- unsigned int host_len;
- char *unix_socket;
- unsigned int unix_socket_len;
- char *user;
- unsigned int user_len;
- char *passwd;
- unsigned int passwd_len;
- char *scheme;
- unsigned int scheme_len;
+ MYSQLND_STRING hostname;
+ MYSQLND_STRING unix_socket;
+ MYSQLND_STRING username;
+ MYSQLND_STRING password;
+ MYSQLND_STRING scheme;
uint64_t thread_id;
char *server_version;
char *host_info;
- zend_uchar *auth_plugin_data;
- size_t auth_plugin_data_len;
+ MYSQLND_STRING authentication_plugin_data;
const MYSQLND_CHARSET *charset;
const MYSQLND_CHARSET *greet_charset;
- char *connect_or_select_db;
- unsigned int connect_or_select_db_len;
+ MYSQLND_STRING connect_or_select_db;
MYSQLND_INFILE infile;
unsigned int protocol_version;
- zend_ulong max_packet_size;
unsigned int port;
- zend_ulong client_flag;
- zend_ulong server_capabilities;
+ zend_ulong server_capabilities;
/* For UPSERT queries */
MYSQLND_UPSERT_STATUS * upsert_status;
MYSQLND_UPSERT_STATUS upsert_status_impl;
- char *last_message;
- unsigned int last_message_len;
+ MYSQLND_STRING last_message;
/* If error packet, we use these */
MYSQLND_ERROR_INFO * error_info;
MYSQLND_ERROR_INFO error_info_impl;
- /*
- To prevent queries during unbuffered fetches. Also to
- mark the connection as destroyed for garbage collection.
- */
- enum mysqlnd_connection_state state;
- enum_mysqlnd_query_type last_query_type;
+ MYSQLND_CONNECTION_STATE state;
+ enum_mysqlnd_query_type last_query_type;
/* Temporary storage between query and (use|store)_result() call */
MYSQLND_RES *current_result;
@@ -967,20 +923,23 @@ struct st_mysqlnd_connection_data
unsigned int field_count;
/* options */
- MYSQLND_OPTIONS * options;
- MYSQLND_OPTIONS options_impl;
+ MYSQLND_SESSION_OPTIONS * options;
+ MYSQLND_SESSION_OPTIONS options_impl;
/* stats */
MYSQLND_STATS * stats;
- unsigned int client_api_capabilities;
+ size_t client_api_capabilities;
zval async_read_cb;
zval async_err_cb;
zend_bool in_async_read_cb;
zend_bool in_async_err_cb;
- struct st_mysqlnd_conn_data_methods * m;
+ MYSQLND_CLASS_METHODS_TYPE(mysqlnd_object_factory) object_factory;
+ func_mysqlnd__command_factory command_factory;
+
+ MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn_data) * m;
/* persistent connection */
zend_bool persistent;
@@ -991,10 +950,192 @@ struct st_mysqlnd_connection
{
MYSQLND_CONN_DATA * data;
zend_bool persistent;
- struct st_mysqlnd_conn_methods * m;
+ MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn) * m;
+};
+
+
+
+struct st_mysqlnd_packet_greet;
+struct st_mysqlnd_packet_greet;
+struct st_mysqlnd_packet_auth;
+struct st_mysqlnd_packet_ok;
+struct st_mysqlnd_packet_command;
+struct st_mysqlnd_packet_eof;
+struct st_mysqlnd_packet_rset_header;
+struct st_mysqlnd_packet_res_field;
+struct st_mysqlnd_packet_row;
+struct st_mysqlnd_packet_stats;
+struct st_mysqlnd_packet_prepare_response;
+struct st_mysqlnd_packet_chg_user_resp;
+struct st_mysqlnd_packet_auth_pam;
+struct st_mysqlnd_packet_sha256_pk_request;
+struct st_mysqlnd_packet_sha256_pk_request_response;
+
+typedef struct st_mysqlnd_packet_greet * (*func_mysqlnd_protocol_payload_decoder_factory__get_greet_packet)(MYSQLND_PROTOCOL_PAYLOAD_DECODER_FACTORY * const factory, const zend_bool persistent);
+typedef struct st_mysqlnd_packet_auth * (*func_mysqlnd_protocol_payload_decoder_factory__get_auth_packet)(MYSQLND_PROTOCOL_PAYLOAD_DECODER_FACTORY * const factory, const zend_bool persistent);
+typedef struct st_mysqlnd_packet_auth_response *(*func_mysqlnd_protocol_payload_decoder_factory__get_auth_response_packet)(MYSQLND_PROTOCOL_PAYLOAD_DECODER_FACTORY * const factory, const zend_bool persistent);
+typedef struct st_mysqlnd_packet_change_auth_response * (*func_mysqlnd_protocol_payload_decoder_factory__get_change_auth_response_packet)(MYSQLND_PROTOCOL_PAYLOAD_DECODER_FACTORY * const factory, const zend_bool persistent);
+typedef struct st_mysqlnd_packet_ok * (*func_mysqlnd_protocol_payload_decoder_factory__get_ok_packet)(MYSQLND_PROTOCOL_PAYLOAD_DECODER_FACTORY * const factory, const zend_bool persistent);
+typedef struct st_mysqlnd_packet_command * (*func_mysqlnd_protocol_payload_decoder_factory__get_command_packet)(MYSQLND_PROTOCOL_PAYLOAD_DECODER_FACTORY * const factory, const zend_bool persistent);
+typedef struct st_mysqlnd_packet_eof * (*func_mysqlnd_protocol_payload_decoder_factory__get_eof_packet)(MYSQLND_PROTOCOL_PAYLOAD_DECODER_FACTORY * const factory, const zend_bool persistent);
+typedef struct st_mysqlnd_packet_rset_header * (*func_mysqlnd_protocol_payload_decoder_factory__get_rset_header_packet)(MYSQLND_PROTOCOL_PAYLOAD_DECODER_FACTORY * const factory, const zend_bool persistent);
+typedef struct st_mysqlnd_packet_res_field * (*func_mysqlnd_protocol_payload_decoder_factory__get_result_field_packet)(MYSQLND_PROTOCOL_PAYLOAD_DECODER_FACTORY * const factory, const zend_bool persistent);
+typedef struct st_mysqlnd_packet_row * (*func_mysqlnd_protocol_payload_decoder_factory__get_row_packet)(MYSQLND_PROTOCOL_PAYLOAD_DECODER_FACTORY * const factory, const zend_bool persistent);
+typedef struct st_mysqlnd_packet_stats * (*func_mysqlnd_protocol_payload_decoder_factory__get_stats_packet)(MYSQLND_PROTOCOL_PAYLOAD_DECODER_FACTORY * const factory, const zend_bool persistent);
+typedef struct st_mysqlnd_packet_prepare_response *(*func_mysqlnd_protocol_payload_decoder_factory__get_prepare_response_packet)(MYSQLND_PROTOCOL_PAYLOAD_DECODER_FACTORY * const factory, const zend_bool persistent);
+typedef struct st_mysqlnd_packet_chg_user_resp*(*func_mysqlnd_protocol_payload_decoder_factory__get_change_user_response_packet)(MYSQLND_PROTOCOL_PAYLOAD_DECODER_FACTORY * const factory, const zend_bool persistent);
+typedef struct st_mysqlnd_packet_sha256_pk_request *(*func_mysqlnd_protocol_payload_decoder_factory__get_sha256_pk_request_packet)(MYSQLND_PROTOCOL_PAYLOAD_DECODER_FACTORY * const factory, const zend_bool persistent);
+typedef struct st_mysqlnd_packet_sha256_pk_request_response *(*func_mysqlnd_protocol_payload_decoder_factory__get_sha256_pk_request_response_packet)(MYSQLND_PROTOCOL_PAYLOAD_DECODER_FACTORY * const factory, const zend_bool persistent);
+
+typedef enum_func_status (*func_mysqlnd_protocol_payload_decoder_factory__send_command)(
+ MYSQLND_PROTOCOL_PAYLOAD_DECODER_FACTORY * payload_decoder_factory,
+ const enum php_mysqlnd_server_command command,
+ const zend_uchar * const arg, const size_t arg_len,
+ const zend_bool silent,
+
+ MYSQLND_CONNECTION_STATE * connection_state,
+ MYSQLND_ERROR_INFO * error_info,
+ MYSQLND_UPSERT_STATUS * upsert_status,
+ MYSQLND_STATS * stats,
+ func_mysqlnd_conn_data__send_close send_close,
+ void * send_close_ctx);
+
+typedef enum_func_status (*func_mysqlnd_protocol_payload_decoder_factory__send_command_handle_OK)(
+ MYSQLND_PROTOCOL_PAYLOAD_DECODER_FACTORY * const payload_decoder_factory,
+ MYSQLND_ERROR_INFO * const error_info,
+ MYSQLND_UPSERT_STATUS * const upsert_status,
+ const zend_bool ignore_upsert_status, /* actually used only by LOAD DATA. COM_QUERY and COM_EXECUTE handle the responses themselves */
+ MYSQLND_STRING * const last_message,
+ const zend_bool last_message_persistent);
+
+typedef enum_func_status (*func_mysqlnd_protocol_payload_decoder_factory__send_command_handle_EOF)(
+ MYSQLND_PROTOCOL_PAYLOAD_DECODER_FACTORY * const payload_decoder_factory,
+ MYSQLND_ERROR_INFO * const error_info,
+ MYSQLND_UPSERT_STATUS * const upsert_status);
+
+typedef enum_func_status (*func_mysqlnd_protocol_payload_decoder_factory__send_command_handle_response)(
+ MYSQLND_PROTOCOL_PAYLOAD_DECODER_FACTORY * payload_decoder_factory,
+ const enum mysqlnd_packet_type ok_packet,
+ const zend_bool silent,
+ const enum php_mysqlnd_server_command command,
+ const zend_bool ignore_upsert_status, /* actually used only by LOAD DATA. COM_QUERY and COM_EXECUTE handle the responses themselves */
+
+ MYSQLND_ERROR_INFO * error_info,
+ MYSQLND_UPSERT_STATUS * upsert_status,
+ MYSQLND_STRING * last_message,
+ zend_bool last_message_persistent);
+
+
+MYSQLND_CLASS_METHODS_TYPE(mysqlnd_protocol_payload_decoder_factory)
+{
+ func_mysqlnd_protocol_payload_decoder_factory__get_greet_packet get_greet_packet;
+ func_mysqlnd_protocol_payload_decoder_factory__get_auth_packet get_auth_packet;
+ func_mysqlnd_protocol_payload_decoder_factory__get_auth_response_packet get_auth_response_packet;
+ func_mysqlnd_protocol_payload_decoder_factory__get_change_auth_response_packet get_change_auth_response_packet;
+ func_mysqlnd_protocol_payload_decoder_factory__get_ok_packet get_ok_packet;
+ func_mysqlnd_protocol_payload_decoder_factory__get_command_packet get_command_packet;
+ func_mysqlnd_protocol_payload_decoder_factory__get_eof_packet get_eof_packet;
+ func_mysqlnd_protocol_payload_decoder_factory__get_rset_header_packet get_rset_header_packet;
+ func_mysqlnd_protocol_payload_decoder_factory__get_result_field_packet get_result_field_packet;
+ func_mysqlnd_protocol_payload_decoder_factory__get_row_packet get_row_packet;
+ func_mysqlnd_protocol_payload_decoder_factory__get_stats_packet get_stats_packet;
+ func_mysqlnd_protocol_payload_decoder_factory__get_prepare_response_packet get_prepare_response_packet;
+ func_mysqlnd_protocol_payload_decoder_factory__get_change_user_response_packet get_change_user_response_packet;
+ func_mysqlnd_protocol_payload_decoder_factory__get_sha256_pk_request_packet get_sha256_pk_request_packet;
+ func_mysqlnd_protocol_payload_decoder_factory__get_sha256_pk_request_response_packet get_sha256_pk_request_response_packet;
+
+ func_mysqlnd_protocol_payload_decoder_factory__send_command send_command;
+ func_mysqlnd_protocol_payload_decoder_factory__send_command_handle_response send_command_handle_response;
+ func_mysqlnd_protocol_payload_decoder_factory__send_command_handle_OK send_command_handle_OK;
+ func_mysqlnd_protocol_payload_decoder_factory__send_command_handle_EOF send_command_handle_EOF;
+};
+
+struct st_mysqlnd_protocol_payload_decoder_factory
+{
+ MYSQLND_CONN_DATA * conn;
+ zend_bool persistent;
+ MYSQLND_CLASS_METHODS_TYPE(mysqlnd_protocol_payload_decoder_factory) m;
};
+typedef struct st_mysqlnd_read_buffer {
+ zend_uchar * data;
+ size_t offset;
+ size_t size;
+ size_t len;
+ zend_bool (*is_empty)(const struct st_mysqlnd_read_buffer *);
+ void (*read)(struct st_mysqlnd_read_buffer *, size_t count, zend_uchar * dest);
+ size_t (*bytes_left)(const struct st_mysqlnd_read_buffer *);
+ void (*free_buffer)(struct st_mysqlnd_read_buffer **);
+} MYSQLND_READ_BUFFER;
+
+
+
+typedef enum_func_status (*func_mysqlnd_pfc__init)(MYSQLND_PFC * const pfc, MYSQLND_STATS * const stats, MYSQLND_ERROR_INFO * const error_info);
+typedef void (*func_mysqlnd_pfc__dtor)(MYSQLND_PFC * const pfc, MYSQLND_STATS * const conn_stats, MYSQLND_ERROR_INFO * const error_info);
+typedef enum_func_status (*func_mysqlnd_pfc__reset)(MYSQLND_PFC * const pfc, MYSQLND_STATS * const conn_stats, MYSQLND_ERROR_INFO * const error_info);
+typedef enum_func_status (*func_mysqlnd_pfc__set_client_option)(MYSQLND_PFC * const pfc, enum_mysqlnd_client_option option, const char * const value);
+typedef enum_func_status (*func_mysqlnd_pfc__decode)(zend_uchar * uncompressed_data, const size_t uncompressed_data_len, const zend_uchar * const compressed_data, const size_t compressed_data_len);
+typedef enum_func_status (*func_mysqlnd_pfc__encode)(zend_uchar * compress_buffer, size_t * compress_buffer_len, const zend_uchar * const uncompressed_data, const size_t uncompressed_data_len);
+typedef size_t (*func_mysqlnd_pfc__send)(MYSQLND_PFC * const pfc, MYSQLND_VIO * const vio, zend_uchar * const buffer, const size_t count, MYSQLND_STATS * const conn_stats, MYSQLND_ERROR_INFO * const error_info);
+typedef enum_func_status (*func_mysqlnd_pfc__receive)(MYSQLND_PFC * const pfc, MYSQLND_VIO * const vio, zend_uchar * const buffer, const size_t count, MYSQLND_STATS * const conn_stats, MYSQLND_ERROR_INFO * const error_info);
+typedef enum_func_status (*func_mysqlnd_pfc__read_compressed_packet_from_stream_and_fill_read_buffer)(MYSQLND_PFC * pfc, MYSQLND_VIO * const vio, size_t net_payload_size, MYSQLND_STATS * conn_stats, MYSQLND_ERROR_INFO * error_info);
+typedef void (*func_mysqlnd_pfc__free_contents)(MYSQLND_PFC * pfc);
+
+MYSQLND_CLASS_METHODS_TYPE(mysqlnd_protocol_packet_frame_codec)
+{
+ func_mysqlnd_pfc__init init;
+ func_mysqlnd_pfc__dtor dtor;
+ func_mysqlnd_pfc__reset reset;
+ func_mysqlnd_pfc__set_client_option set_client_option;
+
+ func_mysqlnd_pfc__decode decode;
+ func_mysqlnd_pfc__encode encode;
+
+ func_mysqlnd_pfc__send send;
+ func_mysqlnd_pfc__receive receive;
+
+ func_mysqlnd_pfc__read_compressed_packet_from_stream_and_fill_read_buffer read_compressed_packet_from_stream_and_fill_read_buffer;
+
+ func_mysqlnd_pfc__free_contents free_contents;
+};
+
+
+struct st_mysqlnd_protocol_frame_codec_data
+{
+ php_stream *stream;
+ zend_bool compressed;
+ zend_bool ssl;
+ uint64_t flags;
+ char * sha256_server_public_key;
+
+#ifdef MYSQLND_COMPRESSION_ENABLED
+ MYSQLND_READ_BUFFER * uncompressed_data;
+#else
+ void * unused_pad1;
+#endif
+
+ /* sequence for simple checking of correct packets */
+ zend_uchar packet_no;
+ zend_uchar compressed_envelope_packet_no;
+
+ zend_bool persistent;
+
+ MYSQLND_CLASS_METHODS_TYPE(mysqlnd_protocol_packet_frame_codec) m;
+};
+
+
+struct st_mysqlnd_protocol_frame_codec
+{
+ MYSQLND_CMD_BUFFER cmd_buffer;
+
+ struct st_mysqlnd_protocol_frame_codec_data * data;
+
+ zend_bool persistent;
+};
+
+
+
struct mysqlnd_field_hash_key
{
zend_bool is_numeric;
@@ -1007,7 +1148,7 @@ struct st_mysqlnd_result_metadata
MYSQLND_FIELD *fields;
struct mysqlnd_field_hash_key *zend_hash_keys;
- struct st_mysqlnd_res_meta_methods * m;
+ MYSQLND_CLASS_METHODS_TYPE(mysqlnd_res_meta) * m;
size_t bit_fields_total_len; /* trailing \0 not counted */
/* We need this to make fast allocs in rowp_read */
@@ -1026,7 +1167,7 @@ struct st_mysqlnd_result_metadata
uint64_t initialized_rows; \
\
/* Column lengths of current row - both buffered and unbuffered. For buffered results it duplicates the data found in **data */ \
- zend_ulong *lengths; \
+ size_t *lengths; \
\
MYSQLND_MEMORY_POOL *result_set_memory_pool; \
\
@@ -1037,7 +1178,7 @@ struct st_mysqlnd_result_metadata
unsigned int field_count; \
zend_bool ps; \
zend_bool persistent; \
- struct st_mysqlnd_result_buffered_methods m; \
+ MYSQLND_CLASS_METHODS_TYPE(mysqlnd_result_buffered) m; \
enum mysqlnd_buffered_type type; \
void * unused1; \
void * unused2; \
@@ -1070,7 +1211,7 @@ struct st_mysqlnd_buffered_result_c
struct st_mysqlnd_unbuffered_result
{
- struct st_mysqlnd_result_unbuffered_methods m;
+ MYSQLND_CLASS_METHODS_TYPE(mysqlnd_result_unbuffered) m;
uint64_t row_count;
/* For unbuffered (both normal and PS) */
@@ -1081,7 +1222,7 @@ struct st_mysqlnd_unbuffered_result
Column lengths of current row - both buffered and unbuffered.
For buffered results it duplicates the data found in **data
*/
- zend_ulong *lengths;
+ size_t *lengths;
MYSQLND_MEMORY_POOL *result_set_memory_pool;
@@ -1111,8 +1252,7 @@ struct st_mysqlnd_res
MYSQLND_RES_UNBUFFERED *unbuf;
zend_bool persistent;
-
- struct st_mysqlnd_res_methods m;
+ MYSQLND_CLASS_METHODS_TYPE(mysqlnd_res) m;
};
@@ -1136,7 +1276,6 @@ struct st_mysqlnd_stmt_data
zend_ulong stmt_id;
zend_ulong flags;/* cursor is set here */
enum_mysqlnd_stmt_state state;
- unsigned int warning_count;
MYSQLND_RES *result;
unsigned int field_count;
unsigned int param_count;
@@ -1171,18 +1310,11 @@ struct st_mysqlnd_stmt_data
struct st_mysqlnd_stmt
{
MYSQLND_STMT_DATA * data;
- struct st_mysqlnd_stmt_methods *m;
+ MYSQLND_CLASS_METHODS_TYPE(mysqlnd_stmt) * m;
zend_bool persistent;
};
-typedef struct st_mysqlnd_string
-{
- char *s;
- size_t l;
-} MYSQLND_STRING;
-
-
struct st_mysqlnd_plugin_header
{
unsigned int plugin_api_version;
@@ -1223,8 +1355,8 @@ typedef zend_uchar * (*func_auth_plugin__get_auth_data)(struct st_mysqlnd_authen
size_t * auth_data_len,
MYSQLND_CONN_DATA * conn, const char * const user, const char * const passwd,
const size_t passwd_len, zend_uchar * auth_plugin_data, size_t auth_plugin_data_len,
- const MYSQLND_OPTIONS * const options,
- const MYSQLND_NET_OPTIONS * const net_options, zend_ulong mysql_flags
+ const MYSQLND_SESSION_OPTIONS * const session_options,
+ const MYSQLND_PFC_DATA * const pfc_data, zend_ulong mysql_flags
);
struct st_mysqlnd_authentication_plugin
@@ -1235,5 +1367,4 @@ struct st_mysqlnd_authentication_plugin
} methods;
};
-
#endif /* MYSQLND_STRUCTS_H */
diff --git a/ext/mysqlnd/mysqlnd_vio.c b/ext/mysqlnd/mysqlnd_vio.c
new file mode 100644
index 0000000000..6bff5ae4c6
--- /dev/null
+++ b/ext/mysqlnd/mysqlnd_vio.c
@@ -0,0 +1,805 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 7 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 2006-2016 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: Andrey Hristov <andrey@php.net> |
+ | Ulf Wendel <uw@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#include "php.h"
+#include "mysqlnd.h"
+#include "mysqlnd_priv.h"
+#include "mysqlnd_statistics.h"
+#include "mysqlnd_debug.h"
+#include "mysqlnd_ext_plugin.h"
+#include "php_network.h"
+
+#ifndef PHP_WIN32
+#include <netinet/tcp.h>
+#else
+#include <winsock.h>
+#endif
+
+
+/* {{{ mysqlnd_set_sock_no_delay */
+static int
+mysqlnd_set_sock_no_delay(php_stream * stream)
+{
+ int socketd = ((php_netstream_data_t*)stream->abstract)->socket;
+ int ret = SUCCESS;
+ int flag = 1;
+ int result = setsockopt(socketd, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(int));
+
+ DBG_ENTER("mysqlnd_set_sock_no_delay");
+
+ if (result == -1) {
+ ret = FAILURE;
+ }
+
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_set_sock_keepalive */
+static int
+mysqlnd_set_sock_keepalive(php_stream * stream)
+{
+ int socketd = ((php_netstream_data_t*)stream->abstract)->socket;
+ int ret = SUCCESS;
+ int flag = 1;
+ int result = setsockopt(socketd, SOL_SOCKET, SO_KEEPALIVE, (char *) &flag, sizeof(int));
+
+ DBG_ENTER("mysqlnd_set_sock_keepalive");
+
+ if (result == -1) {
+ ret = FAILURE;
+ }
+
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_vio::network_read */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_vio, network_read)(MYSQLND_VIO * const vio, zend_uchar * const buffer, const size_t count,
+ MYSQLND_STATS * const stats, MYSQLND_ERROR_INFO * const error_info)
+{
+ enum_func_status return_value = PASS;
+ php_stream * net_stream = vio->data->m.get_stream(vio);
+ size_t old_chunk_size = net_stream->chunk_size;
+ size_t to_read = count, ret;
+ zend_uchar * p = buffer;
+
+ DBG_ENTER("mysqlnd_vio::network_read");
+ DBG_INF_FMT("count="MYSQLND_SZ_T_SPEC, count);
+
+ net_stream->chunk_size = MIN(to_read, vio->data->options.net_read_buffer_size);
+ while (to_read) {
+ if (!(ret = php_stream_read(net_stream, (char *) p, to_read))) {
+ DBG_ERR_FMT("Error while reading header from socket");
+ return_value = FAIL;
+ break;
+ }
+ p += ret;
+ to_read -= ret;
+ }
+ MYSQLND_INC_CONN_STATISTIC_W_VALUE(stats, STAT_BYTES_RECEIVED, count - to_read);
+ net_stream->chunk_size = old_chunk_size;
+ DBG_RETURN(return_value);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_vio::network_write */
+static size_t
+MYSQLND_METHOD(mysqlnd_vio, network_write)(MYSQLND_VIO * const vio, const zend_uchar * const buffer, const size_t count,
+ MYSQLND_STATS * const stats, MYSQLND_ERROR_INFO * const error_info)
+{
+ size_t ret;
+ DBG_ENTER("mysqlnd_vio::network_write");
+ DBG_INF_FMT("sending %u bytes", count);
+ ret = php_stream_write(vio->data->m.get_stream(vio), (char *)buffer, count);
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_vio::open_pipe */
+static php_stream *
+MYSQLND_METHOD(mysqlnd_vio, open_pipe)(MYSQLND_VIO * const vio, const MYSQLND_CSTRING scheme, const zend_bool persistent,
+ MYSQLND_STATS * const conn_stats, MYSQLND_ERROR_INFO * const error_info)
+{
+ unsigned int streams_options = 0;
+ dtor_func_t origin_dtor;
+ php_stream * net_stream = NULL;
+
+ DBG_ENTER("mysqlnd_vio::open_pipe");
+ if (persistent) {
+ streams_options |= STREAM_OPEN_PERSISTENT;
+ }
+ streams_options |= IGNORE_URL;
+ net_stream = php_stream_open_wrapper(scheme.s + sizeof("pipe://") - 1, "r+", streams_options, NULL);
+ if (!net_stream) {
+ SET_CLIENT_ERROR(error_info, CR_CONNECTION_ERROR, UNKNOWN_SQLSTATE, "Unknown errror while connecting");
+ DBG_RETURN(NULL);
+ }
+ /*
+ Streams are not meant for C extensions! Thus we need a hack. Every connected stream will
+ be registered as resource (in EG(regular_list). So far, so good. However, it won't be
+ unregistered until the script ends. So, we need to take care of that.
+ */
+ origin_dtor = EG(regular_list).pDestructor;
+ EG(regular_list).pDestructor = NULL;
+ zend_hash_index_del(&EG(regular_list), net_stream->res->handle); /* ToDO: should it be res->handle, do streams register with addref ?*/
+ EG(regular_list).pDestructor = origin_dtor;
+ net_stream->res = NULL;
+
+ DBG_RETURN(net_stream);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_vio::open_tcp_or_unix */
+static php_stream *
+MYSQLND_METHOD(mysqlnd_vio, open_tcp_or_unix)(MYSQLND_VIO * const vio, const MYSQLND_CSTRING scheme, const zend_bool persistent,
+ MYSQLND_STATS * const conn_stats, MYSQLND_ERROR_INFO * const error_info)
+{
+ unsigned int streams_options = 0;
+ unsigned int streams_flags = STREAM_XPORT_CLIENT | STREAM_XPORT_CONNECT;
+ char * hashed_details = NULL;
+ int hashed_details_len = 0;
+ zend_string *errstr = NULL;
+ int errcode = 0;
+ struct timeval tv;
+ dtor_func_t origin_dtor;
+ php_stream * net_stream = NULL;
+
+ DBG_ENTER("mysqlnd_vio::open_tcp_or_unix");
+
+ vio->data->stream = NULL;
+
+ if (persistent) {
+ hashed_details_len = mnd_sprintf(&hashed_details, 0, "%p", vio);
+ DBG_INF_FMT("hashed_details=%s", hashed_details);
+ }
+
+ if (vio->data->options.timeout_connect) {
+ tv.tv_sec = vio->data->options.timeout_connect;
+ tv.tv_usec = 0;
+ }
+
+ DBG_INF_FMT("calling php_stream_xport_create");
+ net_stream = php_stream_xport_create(scheme.s, scheme.l, streams_options, streams_flags,
+ hashed_details, (vio->data->options.timeout_connect) ? &tv : NULL,
+ NULL /*ctx*/, &errstr, &errcode);
+ if (errstr || !net_stream) {
+ DBG_ERR("Error");
+ if (hashed_details) {
+ mnd_sprintf_free(hashed_details);
+ }
+ errcode = CR_CONNECTION_ERROR;
+ SET_CLIENT_ERROR(error_info,
+ CR_CONNECTION_ERROR,
+ UNKNOWN_SQLSTATE,
+ errstr? ZSTR_VAL(errstr):"Unknown error while connecting");
+ if (errstr) {
+ zend_string_release(errstr);
+ }
+ DBG_RETURN(NULL);
+ }
+ if (hashed_details) {
+ /*
+ If persistent, the streams register it in EG(persistent_list).
+ This is unwanted. ext/mysql or ext/mysqli are responsible to clean,
+ whatever they have to.
+ */
+ zend_resource *le;
+
+ if ((le = zend_hash_str_find_ptr(&EG(persistent_list), hashed_details, hashed_details_len))) {
+ origin_dtor = EG(persistent_list).pDestructor;
+ /*
+ in_free will let streams code skip destructing - big HACK,
+ but STREAMS suck big time regarding persistent streams.
+ Just not compatible for extensions that need persistency.
+ */
+ EG(persistent_list).pDestructor = NULL;
+ zend_hash_str_del(&EG(persistent_list), hashed_details, hashed_details_len);
+ EG(persistent_list).pDestructor = origin_dtor;
+ pefree(le, 1);
+ }
+#if ZEND_DEBUG
+ /* Shut-up the streams, they don't know what they are doing */
+ net_stream->__exposed = 1;
+#endif
+ mnd_sprintf_free(hashed_details);
+ }
+
+ /*
+ Streams are not meant for C extensions! Thus we need a hack. Every connected stream will
+ be registered as resource (in EG(regular_list). So far, so good. However, it won't be
+ unregistered until the script ends. So, we need to take care of that.
+ */
+ origin_dtor = EG(regular_list).pDestructor;
+ EG(regular_list).pDestructor = NULL;
+ zend_hash_index_del(&EG(regular_list), net_stream->res->handle); /* ToDO: should it be res->handle, do streams register with addref ?*/
+ efree(net_stream->res);
+ net_stream->res = NULL;
+ EG(regular_list).pDestructor = origin_dtor;
+ DBG_RETURN(net_stream);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_vio::post_connect_set_opt */
+static void
+MYSQLND_METHOD(mysqlnd_vio, post_connect_set_opt)(MYSQLND_VIO * const vio, const MYSQLND_CSTRING scheme,
+ MYSQLND_STATS * const conn_stats, MYSQLND_ERROR_INFO * const error_info)
+{
+ php_stream * net_stream = vio->data->m.get_stream(vio);
+ DBG_ENTER("mysqlnd_vio::post_connect_set_opt");
+ if (net_stream) {
+ if (vio->data->options.timeout_read) {
+ struct timeval tv;
+ DBG_INF_FMT("setting %u as PHP_STREAM_OPTION_READ_TIMEOUT", vio->data->options.timeout_read);
+ tv.tv_sec = vio->data->options.timeout_read;
+ tv.tv_usec = 0;
+ php_stream_set_option(net_stream, PHP_STREAM_OPTION_READ_TIMEOUT, 0, &tv);
+ }
+
+ if (!memcmp(scheme.s, "tcp://", sizeof("tcp://") - 1)) {
+ /* TCP -> Set TCP_NODELAY */
+ mysqlnd_set_sock_no_delay(net_stream);
+ /* TCP -> Set SO_KEEPALIVE */
+ mysqlnd_set_sock_keepalive(net_stream);
+ }
+ }
+
+ DBG_VOID_RETURN;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_vio::get_open_stream */
+static func_mysqlnd_vio__open_stream
+MYSQLND_METHOD(mysqlnd_vio, get_open_stream)(MYSQLND_VIO * const vio, const MYSQLND_CSTRING scheme,
+ MYSQLND_ERROR_INFO * const error_info)
+{
+ func_mysqlnd_vio__open_stream ret = NULL;
+ DBG_ENTER("mysqlnd_vio::get_open_stream");
+ if (scheme.l > (sizeof("pipe://") - 1) && !memcmp(scheme.s, "pipe://", sizeof("pipe://") - 1)) {
+ ret = vio->data->m.open_pipe;
+ } else if ((scheme.l > (sizeof("tcp://") - 1) && !memcmp(scheme.s, "tcp://", sizeof("tcp://") - 1))
+ ||
+ (scheme.l > (sizeof("unix://") - 1) && !memcmp(scheme.s, "unix://", sizeof("unix://") - 1)))
+ {
+ ret = vio->data->m.open_tcp_or_unix;
+ }
+
+ if (!ret) {
+ SET_CLIENT_ERROR(error_info, CR_CONNECTION_ERROR, UNKNOWN_SQLSTATE, "No handler for this scheme");
+ }
+
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_vio::connect */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_vio, connect)(MYSQLND_VIO * const vio, const MYSQLND_CSTRING scheme, const zend_bool persistent,
+ MYSQLND_STATS * const conn_stats, MYSQLND_ERROR_INFO * const error_info)
+{
+ enum_func_status ret = FAIL;
+ func_mysqlnd_vio__open_stream open_stream = NULL;
+ DBG_ENTER("mysqlnd_vio::connect");
+
+ vio->data->m.close_stream(vio, conn_stats, error_info);
+
+ open_stream = vio->data->m.get_open_stream(vio, scheme, error_info);
+ if (open_stream) {
+ php_stream * net_stream = open_stream(vio, scheme, persistent, conn_stats, error_info);
+ if (net_stream && PASS == vio->data->m.set_stream(vio, net_stream)) {
+ vio->data->m.post_connect_set_opt(vio, scheme, conn_stats, error_info);
+ ret = PASS;
+ }
+ }
+
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_vio::set_client_option */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_vio, set_client_option)(MYSQLND_VIO * const net, enum_mysqlnd_client_option option, const char * const value)
+{
+ DBG_ENTER("mysqlnd_vio::set_client_option");
+ DBG_INF_FMT("option=%u", option);
+ switch (option) {
+ case MYSQLND_OPT_NET_READ_BUFFER_SIZE:
+ DBG_INF("MYSQLND_OPT_NET_READ_BUFFER_SIZE");
+ net->data->options.net_read_buffer_size = *(unsigned int*) value;
+ DBG_INF_FMT("new_length="MYSQLND_SZ_T_SPEC, net->data->options.net_read_buffer_size);
+ break;
+ case MYSQL_OPT_CONNECT_TIMEOUT:
+ DBG_INF("MYSQL_OPT_CONNECT_TIMEOUT");
+ net->data->options.timeout_connect = *(unsigned int*) value;
+ break;
+ case MYSQLND_OPT_SSL_KEY:
+ {
+ zend_bool pers = net->persistent;
+ if (net->data->options.ssl_key) {
+ mnd_pefree(net->data->options.ssl_key, pers);
+ }
+ net->data->options.ssl_key = value? mnd_pestrdup(value, pers) : NULL;
+ break;
+ }
+ case MYSQLND_OPT_SSL_CERT:
+ {
+ zend_bool pers = net->persistent;
+ if (net->data->options.ssl_cert) {
+ mnd_pefree(net->data->options.ssl_cert, pers);
+ }
+ net->data->options.ssl_cert = value? mnd_pestrdup(value, pers) : NULL;
+ break;
+ }
+ case MYSQLND_OPT_SSL_CA:
+ {
+ zend_bool pers = net->persistent;
+ if (net->data->options.ssl_ca) {
+ mnd_pefree(net->data->options.ssl_ca, pers);
+ }
+ net->data->options.ssl_ca = value? mnd_pestrdup(value, pers) : NULL;
+ break;
+ }
+ case MYSQLND_OPT_SSL_CAPATH:
+ {
+ zend_bool pers = net->persistent;
+ if (net->data->options.ssl_capath) {
+ mnd_pefree(net->data->options.ssl_capath, pers);
+ }
+ net->data->options.ssl_capath = value? mnd_pestrdup(value, pers) : NULL;
+ break;
+ }
+ case MYSQLND_OPT_SSL_CIPHER:
+ {
+ zend_bool pers = net->persistent;
+ if (net->data->options.ssl_cipher) {
+ mnd_pefree(net->data->options.ssl_cipher, pers);
+ }
+ net->data->options.ssl_cipher = value? mnd_pestrdup(value, pers) : NULL;
+ break;
+ }
+ case MYSQLND_OPT_SSL_PASSPHRASE:
+ {
+ zend_bool pers = net->persistent;
+ if (net->data->options.ssl_passphrase) {
+ mnd_pefree(net->data->options.ssl_passphrase, pers);
+ }
+ net->data->options.ssl_passphrase = value? mnd_pestrdup(value, pers) : NULL;
+ break;
+ }
+ case MYSQL_OPT_SSL_VERIFY_SERVER_CERT:
+ {
+ enum mysqlnd_ssl_peer val = *((enum mysqlnd_ssl_peer *)value);
+ switch (val) {
+ case MYSQLND_SSL_PEER_VERIFY:
+ DBG_INF("MYSQLND_SSL_PEER_VERIFY");
+ break;
+ case MYSQLND_SSL_PEER_DONT_VERIFY:
+ DBG_INF("MYSQLND_SSL_PEER_DONT_VERIFY");
+ break;
+ case MYSQLND_SSL_PEER_DEFAULT:
+ DBG_INF("MYSQLND_SSL_PEER_DEFAULT");
+ val = MYSQLND_SSL_PEER_DEFAULT;
+ break;
+ default:
+ DBG_INF("default = MYSQLND_SSL_PEER_DEFAULT_ACTION");
+ val = MYSQLND_SSL_PEER_DEFAULT;
+ break;
+ }
+ net->data->options.ssl_verify_peer = val;
+ break;
+ }
+ case MYSQL_OPT_READ_TIMEOUT:
+ net->data->options.timeout_read = *(unsigned int*) value;
+ break;
+#ifdef WHEN_SUPPORTED_BY_MYSQLI
+ case MYSQL_OPT_WRITE_TIMEOUT:
+ net->data->options.timeout_write = *(unsigned int*) value;
+ break;
+#endif
+ default:
+ DBG_RETURN(FAIL);
+ }
+ DBG_RETURN(PASS);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_vio::consume_uneaten_data */
+size_t
+MYSQLND_METHOD(mysqlnd_vio, consume_uneaten_data)(MYSQLND_VIO * const net, enum php_mysqlnd_server_command cmd)
+{
+#ifdef MYSQLND_DO_WIRE_CHECK_BEFORE_COMMAND
+ /*
+ Switch to non-blocking mode and try to consume something from
+ the line, if possible, then continue. This saves us from looking for
+ the actual place where out-of-order packets have been sent.
+ If someone is completely sure that everything is fine, he can switch it
+ off.
+ */
+ char tmp_buf[256];
+ size_t skipped_bytes = 0;
+ int opt = PHP_STREAM_OPTION_BLOCKING;
+ php_stream * net_stream = net->data->get_stream(net);
+ int was_blocked = net_stream->ops->set_option(net_stream, opt, 0, NULL);
+
+ DBG_ENTER("mysqlnd_vio::consume_uneaten_data");
+
+ if (PHP_STREAM_OPTION_RETURN_ERR != was_blocked) {
+ /* Do a read of 1 byte */
+ int bytes_consumed;
+
+ do {
+ skipped_bytes += (bytes_consumed = php_stream_read(net_stream, tmp_buf, sizeof(tmp_buf)));
+ } while (bytes_consumed == sizeof(tmp_buf));
+
+ if (was_blocked) {
+ net_stream->ops->set_option(net_stream, opt, 1, NULL);
+ }
+
+ if (bytes_consumed) {
+ DBG_ERR_FMT("Skipped %u bytes. Last command hasn't consumed all the output from the server",
+ bytes_consumed, mysqlnd_command_to_text[net->last_command]);
+ php_error_docref(NULL, E_WARNING, "Skipped %u bytes. Last command %s hasn't "
+ "consumed all the output from the server",
+ bytes_consumed, mysqlnd_command_to_text[net->last_command]);
+ }
+ }
+ net->last_command = cmd;
+
+ DBG_RETURN(skipped_bytes);
+#else
+ return 0;
+#endif
+}
+/* }}} */
+
+/*
+ in libmyusql, if cert and !key then key=cert
+*/
+/* {{{ mysqlnd_vio::enable_ssl */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_vio, enable_ssl)(MYSQLND_VIO * const net)
+{
+#ifdef MYSQLND_SSL_SUPPORTED
+ php_stream_context * context = php_stream_context_alloc();
+ php_stream * net_stream = net->data->m.get_stream(net);
+ zend_bool any_flag = FALSE;
+
+ DBG_ENTER("mysqlnd_vio::enable_ssl");
+ if (!context) {
+ DBG_RETURN(FAIL);
+ }
+
+ if (net->data->options.ssl_key) {
+ zval key_zval;
+ ZVAL_STRING(&key_zval, net->data->options.ssl_key);
+ php_stream_context_set_option(context, "ssl", "local_pk", &key_zval);
+ zval_ptr_dtor(&key_zval);
+ any_flag = TRUE;
+ }
+ if (net->data->options.ssl_cert) {
+ zval cert_zval;
+ ZVAL_STRING(&cert_zval, net->data->options.ssl_cert);
+ php_stream_context_set_option(context, "ssl", "local_cert", &cert_zval);
+ if (!net->data->options.ssl_key) {
+ php_stream_context_set_option(context, "ssl", "local_pk", &cert_zval);
+ }
+ zval_ptr_dtor(&cert_zval);
+ any_flag = TRUE;
+ }
+ if (net->data->options.ssl_ca) {
+ zval cafile_zval;
+ ZVAL_STRING(&cafile_zval, net->data->options.ssl_ca);
+ php_stream_context_set_option(context, "ssl", "cafile", &cafile_zval);
+ any_flag = TRUE;
+ }
+ if (net->data->options.ssl_capath) {
+ zval capath_zval;
+ ZVAL_STRING(&capath_zval, net->data->options.ssl_capath);
+ php_stream_context_set_option(context, "ssl", "capath", &capath_zval);
+ zval_ptr_dtor(&capath_zval);
+ any_flag = TRUE;
+ }
+ if (net->data->options.ssl_passphrase) {
+ zval passphrase_zval;
+ ZVAL_STRING(&passphrase_zval, net->data->options.ssl_passphrase);
+ php_stream_context_set_option(context, "ssl", "passphrase", &passphrase_zval);
+ zval_ptr_dtor(&passphrase_zval);
+ any_flag = TRUE;
+ }
+ if (net->data->options.ssl_cipher) {
+ zval cipher_zval;
+ ZVAL_STRING(&cipher_zval, net->data->options.ssl_cipher);
+ php_stream_context_set_option(context, "ssl", "ciphers", &cipher_zval);
+ zval_ptr_dtor(&cipher_zval);
+ any_flag = TRUE;
+ }
+ {
+ zval verify_peer_zval;
+ zend_bool verify;
+
+ if (net->data->options.ssl_verify_peer == MYSQLND_SSL_PEER_DEFAULT) {
+ net->data->options.ssl_verify_peer = any_flag? MYSQLND_SSL_PEER_DEFAULT_ACTION:MYSQLND_SSL_PEER_DONT_VERIFY;
+ }
+
+ verify = net->data->options.ssl_verify_peer == MYSQLND_SSL_PEER_VERIFY? TRUE:FALSE;
+
+ DBG_INF_FMT("VERIFY=%d", verify);
+ ZVAL_BOOL(&verify_peer_zval, verify);
+ php_stream_context_set_option(context, "ssl", "verify_peer", &verify_peer_zval);
+ php_stream_context_set_option(context, "ssl", "verify_peer_name", &verify_peer_zval);
+ if (net->data->options.ssl_verify_peer == MYSQLND_SSL_PEER_DONT_VERIFY) {
+ ZVAL_TRUE(&verify_peer_zval);
+ php_stream_context_set_option(context, "ssl", "allow_self_signed", &verify_peer_zval);
+ }
+ }
+ php_stream_context_set(net_stream, context);
+ if (php_stream_xport_crypto_setup(net_stream, STREAM_CRYPTO_METHOD_TLS_CLIENT, NULL) < 0 ||
+ php_stream_xport_crypto_enable(net_stream, 1) < 0)
+ {
+ DBG_ERR("Cannot connect to MySQL by using SSL");
+ php_error_docref(NULL, E_WARNING, "Cannot connect to MySQL by using SSL");
+ DBG_RETURN(FAIL);
+ }
+ net->data->ssl = TRUE;
+ /*
+ get rid of the context. we are persistent and if this is a real pconn used by mysql/mysqli,
+ then the context would not survive cleaning of EG(regular_list), where it is registered, as a
+ resource. What happens is that after this destruction any use of the network will mean usage
+ of the context, which means usage of already freed memory, bad. Actually we don't need this
+ context anymore after we have enabled SSL on the connection. Thus it is very simple, we remove it.
+ */
+ php_stream_context_set(net_stream, NULL);
+
+ if (net->data->options.timeout_read) {
+ struct timeval tv;
+ DBG_INF_FMT("setting %u as PHP_STREAM_OPTION_READ_TIMEOUT", net->data->options.timeout_read);
+ tv.tv_sec = net->data->options.timeout_read;
+ tv.tv_usec = 0;
+ php_stream_set_option(net_stream, PHP_STREAM_OPTION_READ_TIMEOUT, 0, &tv);
+ }
+
+ DBG_RETURN(PASS);
+#else
+ DBG_ENTER("mysqlnd_vio::enable_ssl");
+ DBG_INF("MYSQLND_SSL_SUPPORTED is not defined");
+ DBG_RETURN(PASS);
+#endif
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_vio::disable_ssl */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_vio, disable_ssl)(MYSQLND_VIO * const vio)
+{
+ DBG_ENTER("mysqlnd_vio::disable_ssl");
+ DBG_RETURN(PASS);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_vio::free_contents */
+static void
+MYSQLND_METHOD(mysqlnd_vio, free_contents)(MYSQLND_VIO * net)
+{
+ zend_bool pers = net->persistent;
+ DBG_ENTER("mysqlnd_vio::free_contents");
+
+ if (net->data->options.ssl_key) {
+ mnd_pefree(net->data->options.ssl_key, pers);
+ net->data->options.ssl_key = NULL;
+ }
+ if (net->data->options.ssl_cert) {
+ mnd_pefree(net->data->options.ssl_cert, pers);
+ net->data->options.ssl_cert = NULL;
+ }
+ if (net->data->options.ssl_ca) {
+ mnd_pefree(net->data->options.ssl_ca, pers);
+ net->data->options.ssl_ca = NULL;
+ }
+ if (net->data->options.ssl_capath) {
+ mnd_pefree(net->data->options.ssl_capath, pers);
+ net->data->options.ssl_capath = NULL;
+ }
+ if (net->data->options.ssl_cipher) {
+ mnd_pefree(net->data->options.ssl_cipher, pers);
+ net->data->options.ssl_cipher = NULL;
+ }
+
+ DBG_VOID_RETURN;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_vio::close_stream */
+static void
+MYSQLND_METHOD(mysqlnd_vio, close_stream)(MYSQLND_VIO * const net, MYSQLND_STATS * const stats, MYSQLND_ERROR_INFO * const error_info)
+{
+ php_stream * net_stream;
+ DBG_ENTER("mysqlnd_vio::close_stream");
+ if (net && (net_stream = net->data->m.get_stream(net))) {
+ zend_bool pers = net->persistent;
+ DBG_INF_FMT("Freeing stream. abstract=%p", net_stream->abstract);
+ if (pers) {
+ if (EG(active)) {
+ php_stream_free(net_stream, PHP_STREAM_FREE_CLOSE_PERSISTENT | PHP_STREAM_FREE_RSRC_DTOR);
+ } else {
+ /*
+ otherwise we will crash because the EG(persistent_list) has been freed already,
+ before the modules are shut down
+ */
+ php_stream_free(net_stream, PHP_STREAM_FREE_CLOSE | PHP_STREAM_FREE_RSRC_DTOR);
+ }
+ } else {
+ php_stream_free(net_stream, PHP_STREAM_FREE_CLOSE);
+ }
+ net->data->m.set_stream(net, NULL);
+ }
+
+ DBG_VOID_RETURN;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_vio::init */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_vio, init)(MYSQLND_VIO * const net, MYSQLND_STATS * const stats, MYSQLND_ERROR_INFO * const error_info)
+{
+ unsigned int buf_size;
+ DBG_ENTER("mysqlnd_vio::init");
+
+ buf_size = MYSQLND_G(net_read_buffer_size); /* this is long, cast to unsigned int*/
+ net->data->m.set_client_option(net, MYSQLND_OPT_NET_READ_BUFFER_SIZE, (char *)&buf_size);
+
+ buf_size = MYSQLND_G(net_read_timeout); /* this is long, cast to unsigned int*/
+ net->data->m.set_client_option(net, MYSQL_OPT_READ_TIMEOUT, (char *)&buf_size);
+
+ DBG_RETURN(PASS);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_vio::dtor */
+static void
+MYSQLND_METHOD(mysqlnd_vio, dtor)(MYSQLND_VIO * const vio, MYSQLND_STATS * const stats, MYSQLND_ERROR_INFO * const error_info)
+{
+ DBG_ENTER("mysqlnd_vio::dtor");
+ if (vio) {
+ vio->data->m.free_contents(vio);
+ vio->data->m.close_stream(vio, stats, error_info);
+
+ mnd_pefree(vio->data, vio->data->persistent);
+ mnd_pefree(vio, vio->persistent);
+ }
+ DBG_VOID_RETURN;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_vio::get_stream */
+static php_stream *
+MYSQLND_METHOD(mysqlnd_vio, get_stream)(const MYSQLND_VIO * const net)
+{
+ DBG_ENTER("mysqlnd_vio::get_stream");
+ DBG_INF_FMT("%p", net? net->data->stream:NULL);
+ DBG_RETURN(net? net->data->stream:NULL);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_vio::set_stream */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_vio, set_stream)(MYSQLND_VIO * const vio, php_stream * net_stream)
+{
+ DBG_ENTER("mysqlnd_vio::set_stream");
+ if (vio) {
+ vio->data->stream = net_stream;
+ DBG_RETURN(PASS);
+ }
+ DBG_RETURN(FAIL);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_vio::has_valid_stream */
+static zend_bool
+MYSQLND_METHOD(mysqlnd_vio, has_valid_stream)(const MYSQLND_VIO * const vio)
+{
+ DBG_ENTER("mysqlnd_vio::has_valid_stream");
+ DBG_INF_FMT("%p %p", vio, vio? vio->data->stream:NULL);
+ DBG_RETURN((vio && vio->data->stream)? TRUE: FALSE);
+}
+/* }}} */
+
+
+MYSQLND_CLASS_METHODS_START(mysqlnd_vio)
+ MYSQLND_METHOD(mysqlnd_vio, init),
+ MYSQLND_METHOD(mysqlnd_vio, dtor),
+
+ MYSQLND_METHOD(mysqlnd_vio, connect),
+
+ MYSQLND_METHOD(mysqlnd_vio, close_stream),
+ MYSQLND_METHOD(mysqlnd_vio, open_pipe),
+ MYSQLND_METHOD(mysqlnd_vio, open_tcp_or_unix),
+
+ MYSQLND_METHOD(mysqlnd_vio, get_stream),
+ MYSQLND_METHOD(mysqlnd_vio, set_stream),
+ MYSQLND_METHOD(mysqlnd_vio, has_valid_stream),
+ MYSQLND_METHOD(mysqlnd_vio, get_open_stream),
+
+ MYSQLND_METHOD(mysqlnd_vio, set_client_option),
+ MYSQLND_METHOD(mysqlnd_vio, post_connect_set_opt),
+
+ MYSQLND_METHOD(mysqlnd_vio, enable_ssl),
+ MYSQLND_METHOD(mysqlnd_vio, disable_ssl),
+
+ MYSQLND_METHOD(mysqlnd_vio, network_read),
+ MYSQLND_METHOD(mysqlnd_vio, network_write),
+
+ MYSQLND_METHOD(mysqlnd_vio, consume_uneaten_data),
+
+ MYSQLND_METHOD(mysqlnd_vio, free_contents),
+MYSQLND_CLASS_METHODS_END;
+
+
+/* {{{ mysqlnd_vio_init */
+PHPAPI MYSQLND_VIO *
+mysqlnd_vio_init(zend_bool persistent, MYSQLND_CLASS_METHODS_TYPE(mysqlnd_object_factory) *object_factory, MYSQLND_STATS * stats, MYSQLND_ERROR_INFO * error_info)
+{
+ MYSQLND_CLASS_METHODS_TYPE(mysqlnd_object_factory) *factory = object_factory? object_factory : &MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_object_factory);
+ MYSQLND_VIO * vio;
+ DBG_ENTER("mysqlnd_vio_init");
+ vio = factory->get_vio(persistent, stats, error_info);
+ DBG_RETURN(vio);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_vio_free */
+PHPAPI void
+mysqlnd_vio_free(MYSQLND_VIO * const vio, MYSQLND_STATS * stats, MYSQLND_ERROR_INFO * error_info)
+{
+ DBG_ENTER("mysqlnd_vio_free");
+ if (vio) {
+ vio->data->m.dtor(vio, stats, error_info);
+ }
+ DBG_VOID_RETURN;
+}
+/* }}} */
+
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: noet sw=4 ts=4 fdm=marker
+ * vim<600: noet sw=4 ts=4
+ */
diff --git a/ext/mysqlnd/mysqlnd_net.h b/ext/mysqlnd/mysqlnd_vio.h
index a137b2a327..bb773516b8 100644
--- a/ext/mysqlnd/mysqlnd_net.h
+++ b/ext/mysqlnd/mysqlnd_vio.h
@@ -14,17 +14,16 @@
+----------------------------------------------------------------------+
| Authors: Andrey Hristov <andrey@php.net> |
| Ulf Wendel <uw@php.net> |
- | Georg Richter <georg@php.net> |
+----------------------------------------------------------------------+
*/
-#ifndef MYSQLND_NET_H
-#define MYSQLND_NET_H
+#ifndef MYSQLND_VIO_H
+#define MYSQLND_VIO_H
-PHPAPI MYSQLND_NET * mysqlnd_net_init(zend_bool persistent, MYSQLND_STATS * stats, MYSQLND_ERROR_INFO * error_info);
-PHPAPI void mysqlnd_net_free(MYSQLND_NET * const net, MYSQLND_STATS * stats, MYSQLND_ERROR_INFO * error_info);
+PHPAPI MYSQLND_VIO * mysqlnd_vio_init(zend_bool persistent, MYSQLND_CLASS_METHODS_TYPE(mysqlnd_object_factory) *object_factory, MYSQLND_STATS * stats, MYSQLND_ERROR_INFO * error_info);
+PHPAPI void mysqlnd_vio_free(MYSQLND_VIO * const vio, MYSQLND_STATS * stats, MYSQLND_ERROR_INFO * error_info);
-#endif /* MYSQLND_NET_H */
+#endif /* MYSQLND_VIO_H */
/*
* Local variables:
diff --git a/ext/mysqlnd/mysqlnd_wireprotocol.c b/ext/mysqlnd/mysqlnd_wireprotocol.c
index 8f80bbaada..825c56feae 100644
--- a/ext/mysqlnd/mysqlnd_wireprotocol.c
+++ b/ext/mysqlnd/mysqlnd_wireprotocol.c
@@ -14,51 +14,17 @@
+----------------------------------------------------------------------+
| Authors: Andrey Hristov <andrey@php.net> |
| Ulf Wendel <uw@php.net> |
- | Georg Richter <georg@php.net> |
+----------------------------------------------------------------------+
*/
#include "php.h"
-#include "php_globals.h"
#include "mysqlnd.h"
+#include "mysqlnd_connection.h"
+#include "mysqlnd_ps.h"
#include "mysqlnd_priv.h"
#include "mysqlnd_wireprotocol.h"
#include "mysqlnd_statistics.h"
#include "mysqlnd_debug.h"
-#include "zend_ini.h"
-
-#define MYSQLND_SILENT 1
-
-#define MYSQLND_DUMP_HEADER_N_BODY
-
-#define PACKET_READ_HEADER_AND_BODY(packet, conn, buf, buf_size, packet_type_as_text, packet_type) \
- { \
- DBG_INF_FMT("buf=%p size=%u", (buf), (buf_size)); \
- if (FAIL == mysqlnd_read_header((conn)->net, &((packet)->header), (conn)->stats, ((conn)->error_info))) {\
- CONN_SET_STATE(conn, CONN_QUIT_SENT); \
- SET_CLIENT_ERROR(*conn->error_info, CR_SERVER_GONE_ERROR, UNKNOWN_SQLSTATE, mysqlnd_server_gone);\
- php_error_docref(NULL, E_WARNING, "%s", mysqlnd_server_gone); \
- DBG_ERR_FMT("Can't read %s's header", (packet_type_as_text)); \
- DBG_RETURN(FAIL);\
- }\
- if ((buf_size) < (packet)->header.size) { \
- DBG_ERR_FMT("Packet buffer %u wasn't big enough %u, %u bytes will be unread", \
- (buf_size), (packet)->header.size, (packet)->header.size - (buf_size)); \
- DBG_RETURN(FAIL); \
- }\
- if (FAIL == conn->net->data->m.receive_ex((conn)->net, (buf), (packet)->header.size, (conn)->stats, ((conn)->error_info))) { \
- CONN_SET_STATE(conn, CONN_QUIT_SENT); \
- SET_CLIENT_ERROR(*conn->error_info, CR_SERVER_GONE_ERROR, UNKNOWN_SQLSTATE, mysqlnd_server_gone);\
- php_error_docref(NULL, E_WARNING, "%s", mysqlnd_server_gone); \
- DBG_ERR_FMT("Empty '%s' packet body", (packet_type_as_text)); \
- DBG_RETURN(FAIL);\
- } \
- MYSQLND_INC_CONN_STATISTIC_W_VALUE2(conn->stats, packet_type_to_statistic_byte_count[packet_type], \
- MYSQLND_HEADER_SIZE + (packet)->header.size, \
- packet_type_to_statistic_packet_count[packet_type], \
- 1); \
- }
-
#define BAIL_IF_NO_MORE_DATA \
if ((size_t)(p - begin) > packet->header.size) { \
@@ -126,9 +92,9 @@ static enum_mysqlnd_collected_stats packet_type_to_statistic_packet_count[PROT_L
/* {{{ php_mysqlnd_net_field_length
Get next field's length */
zend_ulong
-php_mysqlnd_net_field_length(zend_uchar **packet)
+php_mysqlnd_net_field_length(const zend_uchar **packet)
{
- register zend_uchar *p= (zend_uchar *)*packet;
+ register const zend_uchar *p= (const zend_uchar *)*packet;
if (*p < 251) {
(*packet)++;
@@ -156,9 +122,9 @@ php_mysqlnd_net_field_length(zend_uchar **packet)
/* {{{ php_mysqlnd_net_field_length_ll
Get next field's length */
uint64_t
-php_mysqlnd_net_field_length_ll(zend_uchar **packet)
+php_mysqlnd_net_field_length_ll(const zend_uchar **packet)
{
- register zend_uchar *p = (zend_uchar *)*packet;
+ register const zend_uchar *p = (zend_uchar *)*packet;
if (*p < 251) {
(*packet)++;
@@ -185,7 +151,7 @@ php_mysqlnd_net_field_length_ll(zend_uchar **packet)
/* {{{ php_mysqlnd_net_store_length */
zend_uchar *
-php_mysqlnd_net_store_length(zend_uchar *packet, uint64_t length)
+php_mysqlnd_net_store_length(zend_uchar *packet, const uint64_t length)
{
if (length < (uint64_t) L64(251)) {
*packet = (zend_uchar) length;
@@ -230,12 +196,12 @@ php_mysqlnd_net_store_length_size(uint64_t length)
/* {{{ php_mysqlnd_read_error_from_line */
static enum_func_status
-php_mysqlnd_read_error_from_line(zend_uchar *buf, size_t buf_len,
- char *error, int error_buf_len,
- unsigned int *error_no, char *sqlstate)
+php_mysqlnd_read_error_from_line(const zend_uchar * const buf, const size_t buf_len,
+ char *error, const size_t error_buf_len,
+ unsigned int *error_no, char *sqlstate)
{
- zend_uchar *p = buf;
- int error_msg_len= 0;
+ const zend_uchar *p = buf;
+ size_t error_msg_len = 0;
DBG_ENTER("php_mysqlnd_read_error_from_line");
@@ -274,64 +240,110 @@ end:
/* {{{ mysqlnd_read_header */
static enum_func_status
-mysqlnd_read_header(MYSQLND_NET * net, MYSQLND_PACKET_HEADER * header,
+mysqlnd_read_header(MYSQLND_PFC * pfc, MYSQLND_VIO * vio, MYSQLND_PACKET_HEADER * header,
MYSQLND_STATS * conn_stats, MYSQLND_ERROR_INFO * error_info)
{
zend_uchar buffer[MYSQLND_HEADER_SIZE];
DBG_ENTER(mysqlnd_read_header_name);
- DBG_INF_FMT("compressed=%u", net->data->compressed);
- if (FAIL == net->data->m.receive_ex(net, buffer, MYSQLND_HEADER_SIZE, conn_stats, error_info)) {
+ DBG_INF_FMT("compressed=%u", pfc->data->compressed);
+ if (FAIL == pfc->data->m.receive(pfc, vio, buffer, MYSQLND_HEADER_SIZE, conn_stats, error_info)) {
DBG_RETURN(FAIL);
}
header->size = uint3korr(buffer);
header->packet_no = uint1korr(buffer + 3);
-#ifdef MYSQLND_DUMP_HEADER_N_BODY
DBG_INF_FMT("HEADER: prot_packet_no=%u size=%3u", header->packet_no, header->size);
-#endif
MYSQLND_INC_CONN_STATISTIC_W_VALUE2(conn_stats,
STAT_PROTOCOL_OVERHEAD_IN, MYSQLND_HEADER_SIZE,
STAT_PACKETS_RECEIVED, 1);
- if (net->data->compressed || net->packet_no == header->packet_no) {
+ if (pfc->data->compressed || pfc->data->packet_no == header->packet_no) {
/*
Have to increase the number, so we can send correct number back. It will
round at 255 as this is unsigned char. The server needs this for simple
flow control checking.
*/
- net->packet_no++;
+ pfc->data->packet_no++;
DBG_RETURN(PASS);
}
DBG_ERR_FMT("Logical link: packets out of order. Expected %u received %u. Packet size="MYSQLND_SZ_T_SPEC,
- net->packet_no, header->packet_no, header->size);
+ pfc->data->packet_no, header->packet_no, header->size);
php_error(E_WARNING, "Packets out of order. Expected %u received %u. Packet size="MYSQLND_SZ_T_SPEC,
- net->packet_no, header->packet_no, header->size);
+ pfc->data->packet_no, header->packet_no, header->size);
DBG_RETURN(FAIL);
}
/* }}} */
+/* {{{ mysqlnd_read_packet_header_and_body */
+static enum_func_status
+mysqlnd_read_packet_header_and_body(MYSQLND_PACKET_HEADER * packet_header,
+ MYSQLND_PFC * pfc,
+ MYSQLND_VIO * vio,
+ MYSQLND_STATS * stats,
+ MYSQLND_ERROR_INFO * error_info,
+ MYSQLND_CONNECTION_STATE * connection_state,
+ zend_uchar * buf, size_t buf_size, const char * const packet_type_as_text,
+ enum mysqlnd_packet_type packet_type)
+{
+ DBG_ENTER("mysqlnd_read_packet_header_and_body");
+ DBG_INF_FMT("buf=%p size=%u", buf, buf_size);
+ if (FAIL == mysqlnd_read_header(pfc, vio, packet_header, stats, error_info)) {
+ SET_CONNECTION_STATE(connection_state, CONN_QUIT_SENT);
+ SET_CLIENT_ERROR(error_info, CR_SERVER_GONE_ERROR, UNKNOWN_SQLSTATE, mysqlnd_server_gone);
+ php_error_docref(NULL, E_WARNING, "%s", mysqlnd_server_gone);
+ DBG_ERR_FMT("Can't read %s's header", packet_type_as_text);
+ DBG_RETURN(FAIL);
+ }
+ if (buf_size < packet_header->size) {
+ DBG_ERR_FMT("Packet buffer %u wasn't big enough %u, %u bytes will be unread",
+ buf_size, packet_header->size, packet_header->size - buf_size);
+ DBG_RETURN(FAIL);
+ }
+ if (FAIL == pfc->data->m.receive(pfc, vio, buf, packet_header->size, stats, error_info)) {
+ SET_CONNECTION_STATE(connection_state, CONN_QUIT_SENT);
+ SET_CLIENT_ERROR(error_info, CR_SERVER_GONE_ERROR, UNKNOWN_SQLSTATE, mysqlnd_server_gone);
+ php_error_docref(NULL, E_WARNING, "%s", mysqlnd_server_gone);
+ DBG_ERR_FMT("Empty '%s' packet body", packet_type_as_text);
+ DBG_RETURN(FAIL);
+ }
+ MYSQLND_INC_CONN_STATISTIC_W_VALUE2(stats, packet_type_to_statistic_byte_count[packet_type],
+ MYSQLND_HEADER_SIZE + packet_header->size,
+ packet_type_to_statistic_packet_count[packet_type],
+ 1);
+ DBG_RETURN(PASS);
+}
+/* }}} */
+
+
/* {{{ php_mysqlnd_greet_read */
static enum_func_status
-php_mysqlnd_greet_read(void * _packet, MYSQLND_CONN_DATA * conn)
+php_mysqlnd_greet_read(void * _packet)
{
zend_uchar buf[2048];
- zend_uchar *p = buf;
- zend_uchar *begin = buf;
- zend_uchar *pad_start = NULL;
+ const zend_uchar * p = buf;
+ const zend_uchar * const begin = buf;
+ const zend_uchar * pad_start = NULL;
MYSQLND_PACKET_GREET *packet= (MYSQLND_PACKET_GREET *) _packet;
+ MYSQLND_ERROR_INFO * error_info = packet->header.error_info;
+ MYSQLND_PFC * pfc = packet->header.protocol_frame_codec;
+ MYSQLND_VIO * vio = packet->header.vio;
+ MYSQLND_STATS * stats = packet->header.stats;
+ MYSQLND_CONNECTION_STATE * connection_state = packet->header.connection_state;
DBG_ENTER("php_mysqlnd_greet_read");
- PACKET_READ_HEADER_AND_BODY(packet, conn, buf, sizeof(buf), "greeting", PROT_GREET_PACKET);
+ if (FAIL == mysqlnd_read_packet_header_and_body(&(packet->header), pfc, vio, stats, error_info, connection_state, buf, sizeof(buf), "greeting", PROT_GREET_PACKET)) {
+ DBG_RETURN(FAIL);
+ }
BAIL_IF_NO_MORE_DATA;
- packet->auth_plugin_data = packet->intern_auth_plugin_data;
- packet->auth_plugin_data_len = sizeof(packet->intern_auth_plugin_data);
+ packet->authentication_plugin_data.s = packet->intern_auth_plugin_data;
+ packet->authentication_plugin_data.l = sizeof(packet->intern_auth_plugin_data);
if (packet->header.size < sizeof(buf)) {
/*
@@ -369,7 +381,7 @@ php_mysqlnd_greet_read(void * _packet, MYSQLND_CONN_DATA * conn)
p+=4;
BAIL_IF_NO_MORE_DATA;
- memcpy(packet->auth_plugin_data, p, SCRAMBLE_LENGTH_323);
+ memcpy(packet->authentication_plugin_data.s, p, SCRAMBLE_LENGTH_323);
p+= SCRAMBLE_LENGTH_323;
BAIL_IF_NO_MORE_DATA;
@@ -396,7 +408,7 @@ php_mysqlnd_greet_read(void * _packet, MYSQLND_CONN_DATA * conn)
if ((size_t) (p - buf) < packet->header.size) {
/* auth_plugin_data is split into two parts */
- memcpy(packet->auth_plugin_data + SCRAMBLE_LENGTH_323, p, SCRAMBLE_LENGTH - SCRAMBLE_LENGTH_323);
+ memcpy(packet->authentication_plugin_data.s + SCRAMBLE_LENGTH_323, p, SCRAMBLE_LENGTH - SCRAMBLE_LENGTH_323);
p+= SCRAMBLE_LENGTH - SCRAMBLE_LENGTH_323;
p++; /* 0x0 at the end of the scramble and thus last byte in the packet in 5.1 and previous */
} else {
@@ -411,19 +423,19 @@ php_mysqlnd_greet_read(void * _packet, MYSQLND_CONN_DATA * conn)
/* Additional 16 bits for server capabilities */
packet->server_capabilities |= uint2korr(pad_start) << 16;
/* And a length of the server scramble in one byte */
- packet->auth_plugin_data_len = uint1korr(pad_start + 2);
- if (packet->auth_plugin_data_len > SCRAMBLE_LENGTH) {
+ packet->authentication_plugin_data.l = uint1korr(pad_start + 2);
+ if (packet->authentication_plugin_data.l > SCRAMBLE_LENGTH) {
/* more data*/
- zend_uchar * new_auth_plugin_data = emalloc(packet->auth_plugin_data_len);
+ char * new_auth_plugin_data = emalloc(packet->authentication_plugin_data.l);
if (!new_auth_plugin_data) {
goto premature_end;
}
/* copy what we already have */
- memcpy(new_auth_plugin_data, packet->auth_plugin_data, SCRAMBLE_LENGTH);
+ memcpy(new_auth_plugin_data, packet->authentication_plugin_data.s, SCRAMBLE_LENGTH);
/* add additional scramble data 5.5+ sent us */
- memcpy(new_auth_plugin_data + SCRAMBLE_LENGTH, p, packet->auth_plugin_data_len - SCRAMBLE_LENGTH);
- p+= (packet->auth_plugin_data_len - SCRAMBLE_LENGTH);
- packet->auth_plugin_data = new_auth_plugin_data;
+ memcpy(new_auth_plugin_data + SCRAMBLE_LENGTH, p, packet->authentication_plugin_data.l - SCRAMBLE_LENGTH);
+ p+= (packet->authentication_plugin_data.l - SCRAMBLE_LENGTH);
+ packet->authentication_plugin_data.s = new_auth_plugin_data;
}
}
@@ -439,7 +451,7 @@ php_mysqlnd_greet_read(void * _packet, MYSQLND_CONN_DATA * conn)
DBG_INF_FMT("server_capabilities=%u charset_no=%u server_status=%i auth_protocol=%s scramble_length=%u",
packet->server_capabilities, packet->charset_no, packet->server_status,
- packet->auth_protocol? packet->auth_protocol:"n/a", packet->auth_plugin_data_len);
+ packet->auth_protocol? packet->auth_protocol:"n/a", packet->authentication_plugin_data.l);
DBG_RETURN(PASS);
premature_end:
@@ -460,9 +472,9 @@ void php_mysqlnd_greet_free_mem(void * _packet, zend_bool stack_allocation)
efree(p->server_version);
p->server_version = NULL;
}
- if (p->auth_plugin_data && p->auth_plugin_data != p->intern_auth_plugin_data) {
- efree(p->auth_plugin_data);
- p->auth_plugin_data = NULL;
+ if (p->authentication_plugin_data.s && p->authentication_plugin_data.s != p->intern_auth_plugin_data) {
+ efree(p->authentication_plugin_data.s);
+ p->authentication_plugin_data.s = NULL;
}
if (p->auth_protocol) {
efree(p->auth_protocol);
@@ -479,12 +491,18 @@ void php_mysqlnd_greet_free_mem(void * _packet, zend_bool stack_allocation)
/* {{{ php_mysqlnd_auth_write */
static
-size_t php_mysqlnd_auth_write(void * _packet, MYSQLND_CONN_DATA * conn)
+size_t php_mysqlnd_auth_write(void * _packet)
{
zend_uchar buffer[AUTH_WRITE_BUFFER_LEN];
zend_uchar *p = buffer + MYSQLND_HEADER_SIZE; /* start after the header */
- int len;
+ size_t len;
MYSQLND_PACKET_AUTH * packet= (MYSQLND_PACKET_AUTH *) _packet;
+ MYSQLND_CONN_DATA * conn = packet->header.conn;
+ MYSQLND_ERROR_INFO * error_info = packet->header.error_info;
+ MYSQLND_PFC * pfc = packet->header.protocol_frame_codec;
+ MYSQLND_VIO * vio = packet->header.vio;
+ MYSQLND_STATS * stats = packet->header.stats;
+ MYSQLND_CONNECTION_STATE * connection_state = packet->header.connection_state;
DBG_ENTER("php_mysqlnd_auth_write");
@@ -515,7 +533,7 @@ size_t php_mysqlnd_auth_write(void * _packet, MYSQLND_CONN_DATA * conn)
if (packet->auth_data_len > 0xFF) {
const char * const msg = "Authentication data too long. "
"Won't fit into the buffer and will be truncated. Authentication will thus fail";
- SET_CLIENT_ERROR(*conn->error_info, CR_UNKNOWN_ERROR, UNKNOWN_SQLSTATE, msg);
+ SET_CLIENT_ERROR(error_info, CR_UNKNOWN_ERROR, UNKNOWN_SQLSTATE, msg);
php_error_docref(NULL, E_WARNING, "%s", msg);
DBG_RETURN(0);
}
@@ -551,7 +569,7 @@ size_t php_mysqlnd_auth_write(void * _packet, MYSQLND_CONN_DATA * conn)
}
if (packet->auth_plugin_name) {
- size_t len = MIN(strlen(packet->auth_plugin_name), sizeof(buffer) - (p - buffer) - 1);
+ len = MIN(strlen(packet->auth_plugin_name), sizeof(buffer) - (p - buffer) - 1);
memcpy(p, packet->auth_plugin_name, len);
p+= len;
*p++= '\0';
@@ -559,25 +577,6 @@ size_t php_mysqlnd_auth_write(void * _packet, MYSQLND_CONN_DATA * conn)
if (packet->connect_attr && zend_hash_num_elements(packet->connect_attr)) {
size_t ca_payload_len = 0;
-#ifdef OLD_CODE
- HashPosition pos_value;
- const char ** entry_value;
- zend_hash_internal_pointer_reset_ex(packet->connect_attr, &pos_value);
- while (SUCCESS == zend_hash_get_current_data_ex(packet->connect_attr, (void **)&entry_value, &pos_value)) {
- char *s_key;
- unsigned int s_len;
- zend_ulong num_key;
- size_t value_len = strlen(*entry_value);
-
- if (HASH_KEY_IS_STRING == zend_hash_get_current_key_ex(packet->connect_attr, &s_key, &s_len, &num_key, &pos_value)) {
- ca_payload_len += php_mysqlnd_net_store_length_size(s_len);
- ca_payload_len += s_len;
- ca_payload_len += php_mysqlnd_net_store_length_size(value_len);
- ca_payload_len += value_len;
- }
- zend_hash_move_forward_ex(conn->options->connect_attr, &pos_value);
- }
-#else
{
zend_string * key;
@@ -593,30 +592,10 @@ size_t php_mysqlnd_auth_write(void * _packet, MYSQLND_CONN_DATA * conn)
}
} ZEND_HASH_FOREACH_END();
}
-#endif
+
if (sizeof(buffer) >= (ca_payload_len + php_mysqlnd_net_store_length_size(ca_payload_len) + (p - buffer))) {
p = php_mysqlnd_net_store_length(p, ca_payload_len);
-#ifdef OLD_CODE
- zend_hash_internal_pointer_reset_ex(packet->connect_attr, &pos_value);
- while (SUCCESS == zend_hash_get_current_data_ex(packet->connect_attr, (void **)&entry_value, &pos_value)) {
- char *s_key;
- unsigned int s_len;
- zend_ulong num_key;
- size_t value_len = strlen(*entry_value);
- if (HASH_KEY_IS_STRING == zend_hash_get_current_key_ex(packet->connect_attr, &s_key, &s_len, &num_key, &pos_value)) {
- /* copy key */
- p = php_mysqlnd_net_store_length(p, s_len);
- memcpy(p, s_key, s_len);
- p+= s_len;
- /* copy value */
- p = php_mysqlnd_net_store_length(p, value_len);
- memcpy(p, *entry_value, value_len);
- p+= value_len;
- }
- zend_hash_move_forward_ex(conn->options->connect_attr, &pos_value);
- }
-#else
{
zend_string * key;
zval * entry_value;
@@ -635,23 +614,26 @@ size_t php_mysqlnd_auth_write(void * _packet, MYSQLND_CONN_DATA * conn)
}
} ZEND_HASH_FOREACH_END();
}
-#endif
} else {
/* cannot put the data - skip */
}
}
}
if (packet->is_change_user_packet) {
- if (PASS != conn->m->simple_command(conn, COM_CHANGE_USER, buffer + MYSQLND_HEADER_SIZE, p - buffer - MYSQLND_HEADER_SIZE,
- PROT_LAST /* the caller will handle the OK packet */,
- packet->silent, TRUE)) {
- DBG_RETURN(0);
+ enum_func_status ret = FAIL;
+ const MYSQLND_CSTRING payload = {(char*) buffer + MYSQLND_HEADER_SIZE, p - (buffer + MYSQLND_HEADER_SIZE)};
+ const unsigned int silent = packet->silent;
+ struct st_mysqlnd_protocol_command * command = conn->command_factory(COM_CHANGE_USER, conn, payload, silent);
+ if (command) {
+ ret = command->run(command);
+ command->free_command(command);
}
- DBG_RETURN(p - buffer - MYSQLND_HEADER_SIZE);
+
+ DBG_RETURN(ret == PASS? (p - buffer - MYSQLND_HEADER_SIZE) : 0);
} else {
- size_t sent = conn->net->data->m.send_ex(conn->net, buffer, p - buffer - MYSQLND_HEADER_SIZE, conn->stats, conn->error_info);
+ size_t sent = pfc->data->m.send(pfc, vio, buffer, p - buffer - MYSQLND_HEADER_SIZE, stats, error_info);
if (!sent) {
- CONN_SET_STATE(conn, CONN_QUIT_SENT);
+ SET_CONNECTION_STATE(connection_state, CONN_QUIT_SENT);
}
DBG_RETURN(sent);
}
@@ -675,21 +657,27 @@ void php_mysqlnd_auth_free_mem(void * _packet, zend_bool stack_allocation)
/* {{{ php_mysqlnd_auth_response_read */
static enum_func_status
-php_mysqlnd_auth_response_read(void * _packet, MYSQLND_CONN_DATA * conn)
+php_mysqlnd_auth_response_read(void * _packet)
{
- zend_uchar local_buf[AUTH_RESP_BUFFER_SIZE];
- size_t buf_len = conn->net->cmd_buffer.buffer? conn->net->cmd_buffer.length: AUTH_RESP_BUFFER_SIZE;
- zend_uchar *buf = conn->net->cmd_buffer.buffer? (zend_uchar *) conn->net->cmd_buffer.buffer : local_buf;
- zend_uchar *p = buf;
- zend_uchar *begin = buf;
- zend_ulong i;
register MYSQLND_PACKET_AUTH_RESPONSE * packet= (MYSQLND_PACKET_AUTH_RESPONSE *) _packet;
+ MYSQLND_ERROR_INFO * error_info = packet->header.error_info;
+ MYSQLND_PFC * pfc = packet->header.protocol_frame_codec;
+ MYSQLND_VIO * vio = packet->header.vio;
+ MYSQLND_STATS * stats = packet->header.stats;
+ MYSQLND_CONNECTION_STATE * connection_state = packet->header.connection_state;
+ zend_uchar local_buf[AUTH_RESP_BUFFER_SIZE];
+ size_t buf_len = pfc->cmd_buffer.buffer? pfc->cmd_buffer.length: AUTH_RESP_BUFFER_SIZE;
+ zend_uchar *buf = pfc->cmd_buffer.buffer? (zend_uchar *) pfc->cmd_buffer.buffer : local_buf;
+ const zend_uchar * p = buf;
+ const zend_uchar * const begin = buf;
DBG_ENTER("php_mysqlnd_auth_response_read");
/* leave space for terminating safety \0 */
buf_len--;
- PACKET_READ_HEADER_AND_BODY(packet, conn, buf, buf_len, "OK", PROT_OK_PACKET);
+ if (FAIL == mysqlnd_read_packet_header_and_body(&(packet->header), pfc, vio, stats, error_info, connection_state, buf, buf_len, "OK", PROT_OK_PACKET)) {
+ DBG_RETURN(FAIL);
+ }
BAIL_IF_NO_MORE_DATA;
/*
@@ -726,6 +714,7 @@ php_mysqlnd_auth_response_read(void * _packet, MYSQLND_CONN_DATA * conn)
DBG_INF_FMT("Server salt : [%d][%.*s]", packet->new_auth_protocol_data_len, packet->new_auth_protocol_data_len, packet->new_auth_protocol_data);
}
} else {
+ zend_ulong net_len;
/* Everything was fine! */
packet->affected_rows = php_mysqlnd_net_field_length_ll(&p);
BAIL_IF_NO_MORE_DATA;
@@ -742,8 +731,8 @@ php_mysqlnd_auth_response_read(void * _packet, MYSQLND_CONN_DATA * conn)
BAIL_IF_NO_MORE_DATA;
/* There is a message */
- if (packet->header.size > (size_t) (p - buf) && (i = php_mysqlnd_net_field_length(&p))) {
- packet->message_len = MIN(i, buf_len - (p - begin));
+ if (packet->header.size > (size_t) (p - buf) && (net_len = php_mysqlnd_net_field_length(&p))) {
+ packet->message_len = MIN(net_len, buf_len - (p - begin));
packet->message = mnd_pestrndup((char *)p, packet->message_len, FALSE);
} else {
packet->message = NULL;
@@ -795,11 +784,16 @@ php_mysqlnd_auth_response_free_mem(void * _packet, zend_bool stack_allocation)
/* {{{ php_mysqlnd_change_auth_response_write */
static size_t
-php_mysqlnd_change_auth_response_write(void * _packet, MYSQLND_CONN_DATA * conn)
+php_mysqlnd_change_auth_response_write(void * _packet)
{
MYSQLND_PACKET_CHANGE_AUTH_RESPONSE *packet= (MYSQLND_PACKET_CHANGE_AUTH_RESPONSE *) _packet;
- zend_uchar * buffer = conn->net->cmd_buffer.length >= packet->auth_data_len? conn->net->cmd_buffer.buffer : mnd_emalloc(packet->auth_data_len);
- zend_uchar *p = buffer + MYSQLND_HEADER_SIZE; /* start after the header */
+ MYSQLND_ERROR_INFO * error_info = packet->header.error_info;
+ MYSQLND_PFC * pfc = packet->header.protocol_frame_codec;
+ MYSQLND_VIO * vio = packet->header.vio;
+ MYSQLND_STATS * stats = packet->header.stats;
+ MYSQLND_CONNECTION_STATE * connection_state = packet->header.connection_state;
+ zend_uchar * buffer = pfc->cmd_buffer.length >= packet->auth_data_len? pfc->cmd_buffer.buffer : mnd_emalloc(packet->auth_data_len);
+ zend_uchar * p = buffer + MYSQLND_HEADER_SIZE; /* start after the header */
DBG_ENTER("php_mysqlnd_change_auth_response_write");
@@ -809,12 +803,12 @@ php_mysqlnd_change_auth_response_write(void * _packet, MYSQLND_CONN_DATA * conn)
}
{
- size_t sent = conn->net->data->m.send_ex(conn->net, buffer, p - buffer - MYSQLND_HEADER_SIZE, conn->stats, conn->error_info);
- if (buffer != conn->net->cmd_buffer.buffer) {
+ size_t sent = pfc->data->m.send(pfc, vio, buffer, p - buffer - MYSQLND_HEADER_SIZE, stats, error_info);
+ if (buffer != pfc->cmd_buffer.buffer) {
mnd_efree(buffer);
}
if (!sent) {
- CONN_SET_STATE(conn, CONN_QUIT_SENT);
+ SET_CONNECTION_STATE(connection_state, CONN_QUIT_SENT);
}
DBG_RETURN(sent);
}
@@ -838,19 +832,26 @@ php_mysqlnd_change_auth_response_free_mem(void * _packet, zend_bool stack_alloca
/* {{{ php_mysqlnd_ok_read */
static enum_func_status
-php_mysqlnd_ok_read(void * _packet, MYSQLND_CONN_DATA * conn)
+php_mysqlnd_ok_read(void * _packet)
{
- zend_uchar local_buf[OK_BUFFER_SIZE];
- size_t buf_len = conn->net->cmd_buffer.buffer? conn->net->cmd_buffer.length : OK_BUFFER_SIZE;
- zend_uchar *buf = conn->net->cmd_buffer.buffer? (zend_uchar *) conn->net->cmd_buffer.buffer : local_buf;
- zend_uchar *p = buf;
- zend_uchar *begin = buf;
- zend_ulong i;
register MYSQLND_PACKET_OK *packet= (MYSQLND_PACKET_OK *) _packet;
+ MYSQLND_ERROR_INFO * error_info = packet->header.error_info;
+ MYSQLND_PFC * pfc = packet->header.protocol_frame_codec;
+ MYSQLND_VIO * vio = packet->header.vio;
+ MYSQLND_STATS * stats = packet->header.stats;
+ MYSQLND_CONNECTION_STATE * connection_state = packet->header.connection_state;
+ zend_uchar local_buf[OK_BUFFER_SIZE];
+ size_t buf_len = pfc->cmd_buffer.buffer? pfc->cmd_buffer.length : OK_BUFFER_SIZE;
+ zend_uchar * buf = pfc->cmd_buffer.buffer? (zend_uchar *) pfc->cmd_buffer.buffer : local_buf;
+ const zend_uchar * p = buf;
+ const zend_uchar * const begin = buf;
+ zend_ulong net_len;
DBG_ENTER("php_mysqlnd_ok_read");
- PACKET_READ_HEADER_AND_BODY(packet, conn, buf, buf_len, "OK", PROT_OK_PACKET);
+ if (FAIL == mysqlnd_read_packet_header_and_body(&(packet->header), pfc, vio, stats, error_info, connection_state, buf, buf_len, "OK", PROT_OK_PACKET)) {
+ DBG_RETURN(FAIL);
+ }
BAIL_IF_NO_MORE_DATA;
/* Should be always 0x0 or ERROR_MARKER for error */
@@ -863,7 +864,6 @@ php_mysqlnd_ok_read(void * _packet, MYSQLND_CONN_DATA * conn)
packet->error, sizeof(packet->error),
&packet->error_no, packet->sqlstate
);
- DBG_INF_FMT("conn->server_status=%u", conn->upsert_status->server_status);
DBG_RETURN(PASS);
}
/* Everything was fine! */
@@ -882,8 +882,8 @@ php_mysqlnd_ok_read(void * _packet, MYSQLND_CONN_DATA * conn)
BAIL_IF_NO_MORE_DATA;
/* There is a message */
- if (packet->header.size > (size_t) (p - buf) && (i = php_mysqlnd_net_field_length(&p))) {
- packet->message_len = MIN(i, buf_len - (p - begin));
+ if (packet->header.size > (size_t) (p - buf) && (net_len = php_mysqlnd_net_field_length(&p))) {
+ packet->message_len = MIN(net_len, buf_len - (p - begin));
packet->message = mnd_pestrndup((char *)p, packet->message_len, FALSE);
} else {
packet->message = NULL;
@@ -924,7 +924,7 @@ php_mysqlnd_ok_free_mem(void * _packet, zend_bool stack_allocation)
/* {{{ php_mysqlnd_eof_read */
static enum_func_status
-php_mysqlnd_eof_read(void * _packet, MYSQLND_CONN_DATA * conn)
+php_mysqlnd_eof_read(void * _packet)
{
/*
EOF packet is since 4.1 five bytes long,
@@ -933,14 +933,21 @@ php_mysqlnd_eof_read(void * _packet, MYSQLND_CONN_DATA * conn)
Error : error_code + '#' + sqlstate + MYSQLND_ERRMSG_SIZE
*/
MYSQLND_PACKET_EOF *packet= (MYSQLND_PACKET_EOF *) _packet;
- size_t buf_len = conn->net->cmd_buffer.length;
- zend_uchar *buf = (zend_uchar *) conn->net->cmd_buffer.buffer;
- zend_uchar *p = buf;
- zend_uchar *begin = buf;
+ MYSQLND_ERROR_INFO * error_info = packet->header.error_info;
+ MYSQLND_PFC * pfc = packet->header.protocol_frame_codec;
+ MYSQLND_VIO * vio = packet->header.vio;
+ MYSQLND_STATS * stats = packet->header.stats;
+ MYSQLND_CONNECTION_STATE * connection_state = packet->header.connection_state;
+ size_t buf_len = pfc->cmd_buffer.length;
+ zend_uchar * buf = (zend_uchar *) pfc->cmd_buffer.buffer;
+ const zend_uchar * p = buf;
+ const zend_uchar * const begin = buf;
DBG_ENTER("php_mysqlnd_eof_read");
- PACKET_READ_HEADER_AND_BODY(packet, conn, buf, buf_len, "EOF", PROT_EOF_PACKET);
+ if (FAIL == mysqlnd_read_packet_header_and_body(&(packet->header), pfc, vio, stats, error_info, connection_state, buf, buf_len, "EOF", PROT_EOF_PACKET)) {
+ DBG_RETURN(FAIL);
+ }
BAIL_IF_NO_MORE_DATA;
/* Should be always EODATA_MARKER */
@@ -1001,11 +1008,15 @@ void php_mysqlnd_eof_free_mem(void * _packet, zend_bool stack_allocation)
/* {{{ php_mysqlnd_cmd_write */
-size_t php_mysqlnd_cmd_write(void * _packet, MYSQLND_CONN_DATA * conn)
+size_t php_mysqlnd_cmd_write(void * _packet)
{
/* Let's have some space, which we can use, if not enough, we will allocate new buffer */
MYSQLND_PACKET_COMMAND * packet= (MYSQLND_PACKET_COMMAND *) _packet;
- MYSQLND_NET * net = conn->net;
+ MYSQLND_ERROR_INFO * error_info = packet->header.error_info;
+ MYSQLND_PFC * pfc = packet->header.protocol_frame_codec;
+ MYSQLND_VIO * vio = packet->header.vio;
+ MYSQLND_STATS * stats = packet->header.stats;
+ MYSQLND_CONNECTION_STATE * connection_state = packet->header.connection_state;
unsigned int error_reporting = EG(error_reporting);
size_t sent = 0;
@@ -1014,28 +1025,27 @@ size_t php_mysqlnd_cmd_write(void * _packet, MYSQLND_CONN_DATA * conn)
Reset packet_no, or we will get bad handshake!
Every command starts a new TX and packet numbers are reset to 0.
*/
- net->packet_no = 0;
- net->compressed_envelope_packet_no = 0; /* this is for the response */
+ pfc->data->m.reset(pfc, stats, error_info);
if (error_reporting) {
EG(error_reporting) = 0;
}
- MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_PACKETS_SENT_CMD);
+ MYSQLND_INC_CONN_STATISTIC(stats, STAT_PACKETS_SENT_CMD);
#ifdef MYSQLND_DO_WIRE_CHECK_BEFORE_COMMAND
- net->data->m.consume_uneaten_data(net, packet->command);
+ vio->data->m.consume_uneaten_data(vio, packet->command);
#endif
- if (!packet->argument || !packet->arg_len) {
+ if (!packet->argument.s || !packet->argument.l) {
zend_uchar buffer[MYSQLND_HEADER_SIZE + 1];
int1store(buffer + MYSQLND_HEADER_SIZE, packet->command);
- sent = net->data->m.send_ex(net, buffer, 1, conn->stats, conn->error_info);
+ sent = pfc->data->m.send(pfc, vio, buffer, 1, stats, error_info);
} else {
- size_t tmp_len = packet->arg_len + 1 + MYSQLND_HEADER_SIZE;
+ size_t tmp_len = packet->argument.l + 1 + MYSQLND_HEADER_SIZE;
zend_uchar *tmp, *p;
- tmp = (tmp_len > net->cmd_buffer.length)? mnd_emalloc(tmp_len):net->cmd_buffer.buffer;
+ tmp = (tmp_len > pfc->cmd_buffer.length)? mnd_emalloc(tmp_len):pfc->cmd_buffer.buffer;
if (!tmp) {
goto end;
}
@@ -1044,11 +1054,11 @@ size_t php_mysqlnd_cmd_write(void * _packet, MYSQLND_CONN_DATA * conn)
int1store(p, packet->command);
p++;
- memcpy(p, packet->argument, packet->arg_len);
+ memcpy(p, packet->argument.s, packet->argument.l);
- sent = net->data->m.send_ex(net, tmp, tmp_len - MYSQLND_HEADER_SIZE, conn->stats, conn->error_info);
- if (tmp != net->cmd_buffer.buffer) {
- MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_CMD_BUFFER_TOO_SMALL);
+ sent = pfc->data->m.send(pfc, vio, tmp, tmp_len - MYSQLND_HEADER_SIZE, stats, error_info);
+ if (tmp != pfc->cmd_buffer.buffer) {
+ MYSQLND_INC_CONN_STATISTIC(stats, STAT_CMD_BUFFER_TOO_SMALL);
mnd_efree(tmp);
}
}
@@ -1058,7 +1068,7 @@ end:
EG(error_reporting) = error_reporting;
}
if (!sent) {
- CONN_SET_STATE(conn, CONN_QUIT_SENT);
+ SET_CONNECTION_STATE(connection_state, CONN_QUIT_SENT);
}
DBG_RETURN(sent);
}
@@ -1079,19 +1089,26 @@ void php_mysqlnd_cmd_free_mem(void * _packet, zend_bool stack_allocation)
/* {{{ php_mysqlnd_rset_header_read */
static enum_func_status
-php_mysqlnd_rset_header_read(void * _packet, MYSQLND_CONN_DATA * conn)
+php_mysqlnd_rset_header_read(void * _packet)
{
+ MYSQLND_PACKET_RSET_HEADER * packet= (MYSQLND_PACKET_RSET_HEADER *) _packet;
+ MYSQLND_ERROR_INFO * error_info = packet->header.error_info;
+ MYSQLND_PFC * pfc = packet->header.protocol_frame_codec;
+ MYSQLND_VIO * vio = packet->header.vio;
+ MYSQLND_STATS * stats = packet->header.stats;
+ MYSQLND_CONNECTION_STATE * connection_state = packet->header.connection_state;
enum_func_status ret = PASS;
- size_t buf_len = conn->net->cmd_buffer.length;
- zend_uchar *buf = (zend_uchar *) conn->net->cmd_buffer.buffer;
- zend_uchar *p = buf;
- zend_uchar *begin = buf;
+ size_t buf_len = pfc->cmd_buffer.length;
+ zend_uchar * buf = (zend_uchar *) pfc->cmd_buffer.buffer;
+ const zend_uchar * p = buf;
+ const zend_uchar * const begin = buf;
size_t len;
- MYSQLND_PACKET_RSET_HEADER *packet= (MYSQLND_PACKET_RSET_HEADER *) _packet;
DBG_ENTER("php_mysqlnd_rset_header_read");
- PACKET_READ_HEADER_AND_BODY(packet, conn, buf, buf_len, "resultset header", PROT_RSET_HEADER_PACKET);
+ if (FAIL == mysqlnd_read_packet_header_and_body(&(packet->header), pfc, vio, stats, error_info, connection_state, buf, buf_len, "resultset header", PROT_RSET_HEADER_PACKET)) {
+ DBG_RETURN(FAIL);
+ }
BAIL_IF_NO_MORE_DATA;
/*
@@ -1106,7 +1123,6 @@ php_mysqlnd_rset_header_read(void * _packet, MYSQLND_CONN_DATA * conn)
packet->error_info.error, sizeof(packet->error_info.error),
&packet->error_info.error_no, packet->error_info.sqlstate
);
- DBG_INF_FMT("conn->server_status=%u", conn->upsert_status->server_status);
DBG_RETURN(PASS);
}
@@ -1124,13 +1140,13 @@ php_mysqlnd_rset_header_read(void * _packet, MYSQLND_CONN_DATA * conn)
would lead to 0 byte allocation but 2^32 or 2^64 bytes copied.
*/
len = packet->header.size - 1;
- packet->info_or_local_file = mnd_emalloc(len + 1);
- if (packet->info_or_local_file) {
- memcpy(packet->info_or_local_file, p, len);
- packet->info_or_local_file[len] = '\0';
- packet->info_or_local_file_len = len;
+ packet->info_or_local_file.s = mnd_emalloc(len + 1);
+ if (packet->info_or_local_file.s) {
+ memcpy(packet->info_or_local_file.s, p, len);
+ packet->info_or_local_file.s[len] = '\0';
+ packet->info_or_local_file.l = len;
} else {
- SET_OOM_ERROR(*conn->error_info);
+ SET_OOM_ERROR(error_info);
ret = FAIL;
}
break;
@@ -1151,13 +1167,13 @@ php_mysqlnd_rset_header_read(void * _packet, MYSQLND_CONN_DATA * conn)
BAIL_IF_NO_MORE_DATA;
/* Check for additional textual data */
if (packet->header.size > (size_t) (p - buf) && (len = php_mysqlnd_net_field_length(&p))) {
- packet->info_or_local_file = mnd_emalloc(len + 1);
- if (packet->info_or_local_file) {
- memcpy(packet->info_or_local_file, p, len);
- packet->info_or_local_file[len] = '\0';
- packet->info_or_local_file_len = len;
+ packet->info_or_local_file.s = mnd_emalloc(len + 1);
+ if (packet->info_or_local_file.s) {
+ memcpy(packet->info_or_local_file.s, p, len);
+ packet->info_or_local_file.s[len] = '\0';
+ packet->info_or_local_file.l = len;
} else {
- SET_OOM_ERROR(*conn->error_info);
+ SET_OOM_ERROR(error_info);
ret = FAIL;
}
}
@@ -1188,9 +1204,9 @@ void php_mysqlnd_rset_header_free_mem(void * _packet, zend_bool stack_allocation
{
MYSQLND_PACKET_RSET_HEADER *p= (MYSQLND_PACKET_RSET_HEADER *) _packet;
DBG_ENTER("php_mysqlnd_rset_header_free_mem");
- if (p->info_or_local_file) {
- mnd_efree(p->info_or_local_file);
- p->info_or_local_file = NULL;
+ if (p->info_or_local_file.s) {
+ mnd_efree(p->info_or_local_file.s);
+ p->info_or_local_file.s = NULL;
}
if (!stack_allocation) {
mnd_pefree(p, p->header.persistent);
@@ -1218,14 +1234,19 @@ static size_t rset_field_offsets[] =
/* {{{ php_mysqlnd_rset_field_read */
static enum_func_status
-php_mysqlnd_rset_field_read(void * _packet, MYSQLND_CONN_DATA * conn)
+php_mysqlnd_rset_field_read(void * _packet)
{
/* Should be enough for the metadata of a single row */
MYSQLND_PACKET_RES_FIELD *packet = (MYSQLND_PACKET_RES_FIELD *) _packet;
- size_t buf_len = conn->net->cmd_buffer.length, total_len = 0;
- zend_uchar *buf = (zend_uchar *) conn->net->cmd_buffer.buffer;
- zend_uchar *p = buf;
- zend_uchar *begin = buf;
+ MYSQLND_ERROR_INFO * error_info = packet->header.error_info;
+ MYSQLND_PFC * pfc = packet->header.protocol_frame_codec;
+ MYSQLND_VIO * vio = packet->header.vio;
+ MYSQLND_STATS * stats = packet->header.stats;
+ MYSQLND_CONNECTION_STATE * connection_state = packet->header.connection_state;
+ size_t buf_len = pfc->cmd_buffer.length, total_len = 0;
+ zend_uchar * buf = (zend_uchar *) pfc->cmd_buffer.buffer;
+ const zend_uchar * p = buf;
+ const zend_uchar * const begin = buf;
char *root_ptr;
zend_ulong len;
MYSQLND_FIELD *meta;
@@ -1233,7 +1254,9 @@ php_mysqlnd_rset_field_read(void * _packet, MYSQLND_CONN_DATA * conn)
DBG_ENTER("php_mysqlnd_rset_field_read");
- PACKET_READ_HEADER_AND_BODY(packet, conn, buf, buf_len, "field", PROT_RSET_FLD_PACKET);
+ if (FAIL == mysqlnd_read_packet_header_and_body(&(packet->header), pfc, vio, stats, error_info, connection_state, buf, buf_len, "field", PROT_RSET_FLD_PACKET)) {
+ DBG_RETURN(FAIL);
+ }
if (packet->skip_parsing) {
DBG_RETURN(PASS);
@@ -1251,9 +1274,8 @@ php_mysqlnd_rset_field_read(void * _packet, MYSQLND_CONN_DATA * conn)
DBG_ERR_FMT("Server error : (%u) %s", packet->error_info.error_no, packet->error_info.error);
DBG_RETURN(PASS);
} else if (EODATA_MARKER == *p && packet->header.size < 8) {
- /* Premature EOF. That should be COM_FIELD_LIST */
+ /* Premature EOF. That should be COM_FIELD_LIST. But we don't support COM_FIELD_LIST anymore, thus this should not happen */
DBG_INF("Premature EOF. That should be COM_FIELD_LIST");
- packet->stupid_list_fields_eof = TRUE;
DBG_RETURN(PASS);
}
@@ -1335,7 +1357,7 @@ php_mysqlnd_rset_field_read(void * _packet, MYSQLND_CONN_DATA * conn)
DBG_INF_FMT("Def found, length %lu, persistent=%u", len, packet->persistent_alloc);
meta->def = mnd_pemalloc(len + 1, packet->persistent_alloc);
if (!meta->def) {
- SET_OOM_ERROR(*conn->error_info);
+ SET_OOM_ERROR(error_info);
DBG_RETURN(FAIL);
}
memcpy(meta->def, p, len);
@@ -1346,7 +1368,7 @@ php_mysqlnd_rset_field_read(void * _packet, MYSQLND_CONN_DATA * conn)
root_ptr = meta->root = mnd_pemalloc(total_len, packet->persistent_alloc);
if (!root_ptr) {
- SET_OOM_ERROR(*conn->error_info);
+ SET_OOM_ERROR(error_info);
DBG_RETURN(FAIL);
}
@@ -1432,7 +1454,11 @@ void php_mysqlnd_rset_field_free_mem(void * _packet, zend_bool stack_allocation)
/* {{{ php_mysqlnd_read_row_ex */
static enum_func_status
-php_mysqlnd_read_row_ex(MYSQLND_CONN_DATA * conn, MYSQLND_MEMORY_POOL * result_set_memory_pool,
+php_mysqlnd_read_row_ex(MYSQLND_PFC * pfc,
+ MYSQLND_VIO * vio,
+ MYSQLND_STATS * stats,
+ MYSQLND_ERROR_INFO * error_info,
+ MYSQLND_MEMORY_POOL * pool,
MYSQLND_MEMORY_POOL_CHUNK ** buffer,
size_t * data_size, zend_bool persistent_alloc,
unsigned int prealloc_more_bytes)
@@ -1454,7 +1480,7 @@ php_mysqlnd_read_row_ex(MYSQLND_CONN_DATA * conn, MYSQLND_MEMORY_POOL * result_s
*data_size = prealloc_more_bytes;
while (1) {
- if (FAIL == mysqlnd_read_header(conn->net, &header, conn->stats, conn->error_info)) {
+ if (FAIL == mysqlnd_read_header(pfc, vio, &header, stats, error_info)) {
ret = FAIL;
break;
}
@@ -1463,7 +1489,7 @@ php_mysqlnd_read_row_ex(MYSQLND_CONN_DATA * conn, MYSQLND_MEMORY_POOL * result_s
if (first_iteration) {
first_iteration = FALSE;
- *buffer = result_set_memory_pool->get_chunk(result_set_memory_pool, *data_size);
+ *buffer = pool->get_chunk(pool, *data_size);
if (!*buffer) {
ret = FAIL;
break;
@@ -1478,8 +1504,8 @@ php_mysqlnd_read_row_ex(MYSQLND_CONN_DATA * conn, MYSQLND_MEMORY_POOL * result_s
/*
We have to realloc the buffer.
*/
- if (FAIL == (*buffer)->resize_chunk((*buffer), *data_size)) {
- SET_OOM_ERROR(*conn->error_info);
+ if (FAIL == pool->resize_chunk(pool, *buffer, *data_size)) {
+ SET_OOM_ERROR(error_info);
ret = FAIL;
break;
}
@@ -1487,7 +1513,7 @@ php_mysqlnd_read_row_ex(MYSQLND_CONN_DATA * conn, MYSQLND_MEMORY_POOL * result_s
p = (*buffer)->ptr + (*data_size - header.size);
}
- if (PASS != (ret = conn->net->data->m.receive_ex(conn->net, p, header.size, conn->stats, conn->error_info))) {
+ if (PASS != (ret = pfc->data->m.receive(pfc, vio, p, header.size, stats, error_info))) {
DBG_ERR("Empty row packet body");
php_error(E_WARNING, "Empty row packet body");
break;
@@ -1498,7 +1524,7 @@ php_mysqlnd_read_row_ex(MYSQLND_CONN_DATA * conn, MYSQLND_MEMORY_POOL * result_s
}
}
if (ret == FAIL && *buffer) {
- (*buffer)->free_chunk((*buffer));
+ pool->free_chunk(pool, *buffer);
*buffer = NULL;
}
*data_size -= prealloc_more_bytes;
@@ -1514,8 +1540,9 @@ php_mysqlnd_rowp_read_binary_protocol(MYSQLND_MEMORY_POOL_CHUNK * row_buffer, zv
zend_bool as_int_or_float, MYSQLND_STATS * stats)
{
unsigned int i;
- zend_uchar *p = row_buffer->ptr;
- zend_uchar *null_ptr, bit;
+ const zend_uchar * p = row_buffer->ptr;
+ const zend_uchar * null_ptr;
+ zend_uchar bit;
zval *current_field, *end_field, *start_field;
DBG_ENTER("php_mysqlnd_rowp_read_binary_protocol");
@@ -1534,7 +1561,7 @@ php_mysqlnd_rowp_read_binary_protocol(MYSQLND_MEMORY_POOL_CHUNK * row_buffer, zv
for (i = 0, current_field = start_field; current_field < end_field; current_field++, i++) {
enum_mysqlnd_collected_stats statistic;
- zend_uchar * orig_p = p;
+ const zend_uchar * orig_p = p;
DBG_INF_FMT("Into zval=%p decoding column %u [%s.%s.%s] type=%u field->flags&unsigned=%u flags=%u is_bit=%u",
current_field, i,
@@ -1619,7 +1646,7 @@ php_mysqlnd_rowp_read_text_protocol_aux(MYSQLND_MEMORY_POOL_CHUNK * row_buffer,
for (i = 0, current_field = start_field; current_field < end_field; current_field++, i++) {
/* php_mysqlnd_net_field_length() call should be after *this_field_len_pos = p; */
- zend_ulong len = php_mysqlnd_net_field_length(&p);
+ const zend_ulong len = php_mysqlnd_net_field_length((const zend_uchar **) &p);
/* NULL or NOT NULL, this is the question! */
if (len == MYSQLND_NULL_LENGTH) {
@@ -1722,7 +1749,7 @@ php_mysqlnd_rowp_read_text_protocol_aux(MYSQLND_MEMORY_POOL_CHUNK * row_buffer,
Definitely not nice, _hackish_ :(, but works.
*/
zend_uchar *start = bit_area;
- ps_fetch_from_1_to_8_bytes(current_field, &(fields_metadata[i]), 0, &p, len);
+ ps_fetch_from_1_to_8_bytes(current_field, &(fields_metadata[i]), 0, (const zend_uchar **) &p, len);
/*
We have advanced in ps_fetch_from_1_to_8_bytes. We should go back because
later in this function there will be an advancement.
@@ -1784,11 +1811,15 @@ php_mysqlnd_rowp_read_text_protocol_c(MYSQLND_MEMORY_POOL_CHUNK * row_buffer, zv
if PS => packet->fields is passed from outside
*/
static enum_func_status
-php_mysqlnd_rowp_read(void * _packet, MYSQLND_CONN_DATA * conn)
+php_mysqlnd_rowp_read(void * _packet)
{
+ MYSQLND_PACKET_ROW *packet= (MYSQLND_PACKET_ROW *) _packet;
+ MYSQLND_ERROR_INFO * error_info = packet->header.error_info;
+ MYSQLND_PFC * pfc = packet->header.protocol_frame_codec;
+ MYSQLND_VIO * vio = packet->header.vio;
+ MYSQLND_STATS * stats = packet->header.stats;
zend_uchar *p;
enum_func_status ret = PASS;
- MYSQLND_PACKET_ROW *packet= (MYSQLND_PACKET_ROW *) _packet;
size_t post_alloc_for_bit_fields = 0;
size_t data_size = 0;
@@ -1799,13 +1830,14 @@ php_mysqlnd_rowp_read(void * _packet, MYSQLND_CONN_DATA * conn)
post_alloc_for_bit_fields = packet->bit_fields_total_len + packet->bit_fields_count;
}
- ret = php_mysqlnd_read_row_ex(conn, packet->result_set_memory_pool, &packet->row_buffer, &data_size,
+ ret = php_mysqlnd_read_row_ex(pfc, vio, stats, error_info,
+ packet->result_set_memory_pool, &packet->row_buffer, &data_size,
packet->persistent_alloc, post_alloc_for_bit_fields
);
if (FAIL == ret) {
goto end;
}
- MYSQLND_INC_CONN_STATISTIC_W_VALUE2(conn->stats, packet_type_to_statistic_byte_count[PROT_ROW_PACKET],
+ MYSQLND_INC_CONN_STATISTIC_W_VALUE2(stats, packet_type_to_statistic_byte_count[PROT_ROW_PACKET],
MYSQLND_HEADER_SIZE + packet->header.size,
packet_type_to_statistic_packet_count[PROT_ROW_PACKET],
1);
@@ -1838,7 +1870,7 @@ php_mysqlnd_rowp_read(void * _packet, MYSQLND_CONN_DATA * conn)
DBG_INF_FMT("server_status=%u warning_count=%u", packet->server_status, packet->warning_count);
}
} else {
- MYSQLND_INC_CONN_STATISTIC(conn->stats,
+ MYSQLND_INC_CONN_STATISTIC(stats,
packet->binary_protocol? STAT_ROWS_FETCHED_FROM_SERVER_PS:
STAT_ROWS_FETCHED_FROM_SERVER_NORMAL);
@@ -1862,7 +1894,7 @@ php_mysqlnd_rowp_read(void * _packet, MYSQLND_CONN_DATA * conn)
packet->persistent_alloc);
}
} else {
- MYSQLND_INC_CONN_STATISTIC(conn->stats,
+ MYSQLND_INC_CONN_STATISTIC(stats,
packet->binary_protocol? STAT_ROWS_SKIPPED_PS:
STAT_ROWS_SKIPPED_NORMAL);
}
@@ -1883,7 +1915,7 @@ php_mysqlnd_rowp_free_mem(void * _packet, zend_bool stack_allocation)
DBG_ENTER("php_mysqlnd_rowp_free_mem");
p = (MYSQLND_PACKET_ROW *) _packet;
if (p->row_buffer) {
- p->row_buffer->free_chunk(p->row_buffer);
+ p->result_set_memory_pool->free_chunk(p->result_set_memory_pool, p->row_buffer);
p->row_buffer = NULL;
}
DBG_INF_FMT("stack_allocation=%u persistent=%u", (int)stack_allocation, (int)p->header.persistent);
@@ -1904,20 +1936,27 @@ php_mysqlnd_rowp_free_mem(void * _packet, zend_bool stack_allocation)
/* {{{ php_mysqlnd_stats_read */
static enum_func_status
-php_mysqlnd_stats_read(void * _packet, MYSQLND_CONN_DATA * conn)
+php_mysqlnd_stats_read(void * _packet)
{
MYSQLND_PACKET_STATS *packet= (MYSQLND_PACKET_STATS *) _packet;
- size_t buf_len = conn->net->cmd_buffer.length;
- zend_uchar *buf = (zend_uchar *) conn->net->cmd_buffer.buffer;
+ MYSQLND_ERROR_INFO * error_info = packet->header.error_info;
+ MYSQLND_PFC * pfc = packet->header.protocol_frame_codec;
+ MYSQLND_VIO * vio = packet->header.vio;
+ MYSQLND_STATS * stats = packet->header.stats;
+ MYSQLND_CONNECTION_STATE * connection_state = packet->header.connection_state;
+ size_t buf_len = pfc->cmd_buffer.length;
+ zend_uchar *buf = (zend_uchar *) pfc->cmd_buffer.buffer;
DBG_ENTER("php_mysqlnd_stats_read");
- PACKET_READ_HEADER_AND_BODY(packet, conn, buf, buf_len, "statistics", PROT_STATS_PACKET);
+ if (FAIL == mysqlnd_read_packet_header_and_body(&(packet->header), pfc, vio, stats, error_info, connection_state, buf, buf_len, "statistics", PROT_STATS_PACKET)) {
+ DBG_RETURN(FAIL);
+ }
- packet->message = mnd_emalloc(packet->header.size + 1);
- memcpy(packet->message, buf, packet->header.size);
- packet->message[packet->header.size] = '\0';
- packet->message_len = packet->header.size;
+ packet->message.s = mnd_emalloc(packet->header.size + 1);
+ memcpy(packet->message.s, buf, packet->header.size);
+ packet->message.s[packet->header.size] = '\0';
+ packet->message.l = packet->header.size;
DBG_RETURN(PASS);
}
@@ -1929,9 +1968,9 @@ static
void php_mysqlnd_stats_free_mem(void * _packet, zend_bool stack_allocation)
{
MYSQLND_PACKET_STATS *p= (MYSQLND_PACKET_STATS *) _packet;
- if (p->message) {
- mnd_efree(p->message);
- p->message = NULL;
+ if (p->message.s) {
+ mnd_efree(p->message.s);
+ p->message.s = NULL;
}
if (!stack_allocation) {
mnd_pefree(p, p->header.persistent);
@@ -1946,19 +1985,26 @@ void php_mysqlnd_stats_free_mem(void * _packet, zend_bool stack_allocation)
/* {{{ php_mysqlnd_prepare_read */
static enum_func_status
-php_mysqlnd_prepare_read(void * _packet, MYSQLND_CONN_DATA * conn)
+php_mysqlnd_prepare_read(void * _packet)
{
+ MYSQLND_PACKET_PREPARE_RESPONSE *packet= (MYSQLND_PACKET_PREPARE_RESPONSE *) _packet;
+ MYSQLND_ERROR_INFO * error_info = packet->header.error_info;
+ MYSQLND_PFC * pfc = packet->header.protocol_frame_codec;
+ MYSQLND_VIO * vio = packet->header.vio;
+ MYSQLND_STATS * stats = packet->header.stats;
+ MYSQLND_CONNECTION_STATE * connection_state = packet->header.connection_state;
/* In case of an error, we should have place to put it */
- size_t buf_len = conn->net->cmd_buffer.length;
- zend_uchar *buf = (zend_uchar *) conn->net->cmd_buffer.buffer;
+ size_t buf_len = pfc->cmd_buffer.length;
+ zend_uchar *buf = (zend_uchar *) pfc->cmd_buffer.buffer;
zend_uchar *p = buf;
- zend_uchar *begin = buf;
+ const zend_uchar * const begin = buf;
unsigned int data_size;
- MYSQLND_PACKET_PREPARE_RESPONSE *packet= (MYSQLND_PACKET_PREPARE_RESPONSE *) _packet;
DBG_ENTER("php_mysqlnd_prepare_read");
- PACKET_READ_HEADER_AND_BODY(packet, conn, buf, buf_len, "prepare", PROT_PREPARE_RESP_PACKET);
+ if (FAIL == mysqlnd_read_packet_header_and_body(&(packet->header), pfc, vio, stats, error_info, connection_state, buf, buf_len, "prepare", PROT_PREPARE_RESP_PACKET)) {
+ DBG_RETURN(FAIL);
+ }
BAIL_IF_NO_MORE_DATA;
data_size = packet->header.size;
@@ -2034,18 +2080,25 @@ php_mysqlnd_prepare_free_mem(void * _packet, zend_bool stack_allocation)
/* {{{ php_mysqlnd_chg_user_read */
static enum_func_status
-php_mysqlnd_chg_user_read(void * _packet, MYSQLND_CONN_DATA * conn)
+php_mysqlnd_chg_user_read(void * _packet)
{
+ MYSQLND_PACKET_CHG_USER_RESPONSE *packet= (MYSQLND_PACKET_CHG_USER_RESPONSE *) _packet;
+ MYSQLND_ERROR_INFO * error_info = packet->header.error_info;
+ MYSQLND_PFC * pfc = packet->header.protocol_frame_codec;
+ MYSQLND_VIO * vio = packet->header.vio;
+ MYSQLND_STATS * stats = packet->header.stats;
+ MYSQLND_CONNECTION_STATE * connection_state = packet->header.connection_state;
/* There could be an error message */
- size_t buf_len = conn->net->cmd_buffer.length;
- zend_uchar *buf = (zend_uchar *) conn->net->cmd_buffer.buffer;
+ size_t buf_len = pfc->cmd_buffer.length;
+ zend_uchar *buf = (zend_uchar *) pfc->cmd_buffer.buffer;
zend_uchar *p = buf;
- zend_uchar *begin = buf;
- MYSQLND_PACKET_CHG_USER_RESPONSE *packet= (MYSQLND_PACKET_CHG_USER_RESPONSE *) _packet;
+ const zend_uchar * const begin = buf;
DBG_ENTER("php_mysqlnd_chg_user_read");
- PACKET_READ_HEADER_AND_BODY(packet, conn, buf, buf_len, "change user response", PROT_CHG_USER_RESP_PACKET);
+ if (FAIL == mysqlnd_read_packet_header_and_body(&(packet->header), pfc, vio, stats, error_info, connection_state, buf, buf_len, "change user response", PROT_CHG_USER_RESP_PACKET)) {
+ DBG_RETURN(FAIL);
+ }
BAIL_IF_NO_MORE_DATA;
/*
@@ -2122,15 +2175,20 @@ php_mysqlnd_chg_user_free_mem(void * _packet, zend_bool stack_allocation)
/* {{{ php_mysqlnd_sha256_pk_request_write */
static
-size_t php_mysqlnd_sha256_pk_request_write(void * _packet, MYSQLND_CONN_DATA * conn)
+size_t php_mysqlnd_sha256_pk_request_write(void * _packet)
{
+ MYSQLND_PACKET_SHA256_PK_REQUEST * packet = (MYSQLND_PACKET_SHA256_PK_REQUEST *) _packet;
+ MYSQLND_ERROR_INFO * error_info = packet->header.error_info;
+ MYSQLND_PFC * pfc = packet->header.protocol_frame_codec;
+ MYSQLND_VIO * vio = packet->header.vio;
+ MYSQLND_STATS * stats = packet->header.stats;
zend_uchar buffer[MYSQLND_HEADER_SIZE + 1];
size_t sent;
DBG_ENTER("php_mysqlnd_sha256_pk_request_write");
int1store(buffer + MYSQLND_HEADER_SIZE, '\1');
- sent = conn->net->data->m.send_ex(conn->net, buffer, 1, conn->stats, conn->error_info);
+ sent = pfc->data->m.send(pfc, vio, buffer, 1, stats, error_info);
DBG_RETURN(sent);
}
@@ -2153,17 +2211,24 @@ void php_mysqlnd_sha256_pk_request_free_mem(void * _packet, zend_bool stack_allo
/* {{{ php_mysqlnd_sha256_pk_request_response_read */
static enum_func_status
-php_mysqlnd_sha256_pk_request_response_read(void * _packet, MYSQLND_CONN_DATA * conn)
+php_mysqlnd_sha256_pk_request_response_read(void * _packet)
{
+ MYSQLND_PACKET_SHA256_PK_REQUEST_RESPONSE * packet= (MYSQLND_PACKET_SHA256_PK_REQUEST_RESPONSE *) _packet;
+ MYSQLND_ERROR_INFO * error_info = packet->header.error_info;
+ MYSQLND_PFC * pfc = packet->header.protocol_frame_codec;
+ MYSQLND_VIO * vio = packet->header.vio;
+ MYSQLND_STATS * stats = packet->header.stats;
+ MYSQLND_CONNECTION_STATE * connection_state = packet->header.connection_state;
zend_uchar buf[SHA256_PK_REQUEST_RESP_BUFFER_SIZE];
zend_uchar *p = buf;
- zend_uchar *begin = buf;
- MYSQLND_PACKET_SHA256_PK_REQUEST_RESPONSE * packet= (MYSQLND_PACKET_SHA256_PK_REQUEST_RESPONSE *) _packet;
+ const zend_uchar * const begin = buf;
DBG_ENTER("php_mysqlnd_sha256_pk_request_response_read");
/* leave space for terminating safety \0 */
- PACKET_READ_HEADER_AND_BODY(packet, conn, buf, sizeof(buf), "SHA256_PK_REQUEST_RESPONSE", PROT_SHA256_PK_REQUEST_RESPONSE_PACKET);
+ if (FAIL == mysqlnd_read_packet_header_and_body(&(packet->header), pfc, vio, stats, error_info, connection_state, buf, sizeof(buf), "SHA256_PK_REQUEST_RESPONSE", PROT_SHA256_PK_REQUEST_RESPONSE_PACKET)) {
+ DBG_RETURN(FAIL);
+ }
BAIL_IF_NO_MORE_DATA;
p++;
@@ -2303,12 +2368,20 @@ mysqlnd_packet_methods packet_methods[PROT_LAST] =
/* {{{ mysqlnd_protocol::get_greet_packet */
static struct st_mysqlnd_packet_greet *
-MYSQLND_METHOD(mysqlnd_protocol, get_greet_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent)
+MYSQLND_METHOD(mysqlnd_protocol, get_greet_packet)(MYSQLND_PROTOCOL_PAYLOAD_DECODER_FACTORY * const factory, const zend_bool persistent)
{
struct st_mysqlnd_packet_greet * packet = mnd_pecalloc(1, packet_methods[PROT_GREET_PACKET].struct_size, persistent);
DBG_ENTER("mysqlnd_protocol::get_greet_packet");
if (packet) {
packet->header.m = &packet_methods[PROT_GREET_PACKET];
+ packet->header.factory = factory;
+
+ packet->header.protocol_frame_codec = factory->conn->protocol_frame_codec;
+ packet->header.vio = factory->conn->vio;
+ packet->header.stats = factory->conn->stats;
+ packet->header.error_info = factory->conn->error_info;
+ packet->header.connection_state = &factory->conn->state;
+
packet->header.persistent = persistent;
}
DBG_RETURN(packet);
@@ -2318,12 +2391,21 @@ MYSQLND_METHOD(mysqlnd_protocol, get_greet_packet)(MYSQLND_PROTOCOL * const prot
/* {{{ mysqlnd_protocol::get_auth_packet */
static struct st_mysqlnd_packet_auth *
-MYSQLND_METHOD(mysqlnd_protocol, get_auth_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent)
+MYSQLND_METHOD(mysqlnd_protocol, get_auth_packet)(MYSQLND_PROTOCOL_PAYLOAD_DECODER_FACTORY * const factory, const zend_bool persistent)
{
struct st_mysqlnd_packet_auth * packet = mnd_pecalloc(1, packet_methods[PROT_AUTH_PACKET].struct_size, persistent);
DBG_ENTER("mysqlnd_protocol::get_auth_packet");
if (packet) {
packet->header.m = &packet_methods[PROT_AUTH_PACKET];
+ packet->header.factory = factory;
+
+ packet->header.conn = factory->conn;
+ packet->header.protocol_frame_codec = factory->conn->protocol_frame_codec;
+ packet->header.vio = factory->conn->vio;
+ packet->header.stats = factory->conn->stats;
+ packet->header.error_info = factory->conn->error_info;
+ packet->header.connection_state = &factory->conn->state;
+
packet->header.persistent = persistent;
}
DBG_RETURN(packet);
@@ -2333,12 +2415,20 @@ MYSQLND_METHOD(mysqlnd_protocol, get_auth_packet)(MYSQLND_PROTOCOL * const proto
/* {{{ mysqlnd_protocol::get_auth_response_packet */
static struct st_mysqlnd_packet_auth_response *
-MYSQLND_METHOD(mysqlnd_protocol, get_auth_response_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent)
+MYSQLND_METHOD(mysqlnd_protocol, get_auth_response_packet)(MYSQLND_PROTOCOL_PAYLOAD_DECODER_FACTORY * const factory, const zend_bool persistent)
{
struct st_mysqlnd_packet_auth_response * packet = mnd_pecalloc(1, packet_methods[PROT_AUTH_RESP_PACKET].struct_size, persistent);
DBG_ENTER("mysqlnd_protocol::get_auth_response_packet");
if (packet) {
packet->header.m = &packet_methods[PROT_AUTH_RESP_PACKET];
+ packet->header.factory = factory;
+
+ packet->header.protocol_frame_codec = factory->conn->protocol_frame_codec;
+ packet->header.vio = factory->conn->vio;
+ packet->header.stats = factory->conn->stats;
+ packet->header.error_info = factory->conn->error_info;
+ packet->header.connection_state = &factory->conn->state;
+
packet->header.persistent = persistent;
}
DBG_RETURN(packet);
@@ -2348,12 +2438,20 @@ MYSQLND_METHOD(mysqlnd_protocol, get_auth_response_packet)(MYSQLND_PROTOCOL * co
/* {{{ mysqlnd_protocol::get_change_auth_response_packet */
static struct st_mysqlnd_packet_change_auth_response *
-MYSQLND_METHOD(mysqlnd_protocol, get_change_auth_response_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent)
+MYSQLND_METHOD(mysqlnd_protocol, get_change_auth_response_packet)(MYSQLND_PROTOCOL_PAYLOAD_DECODER_FACTORY * const factory, const zend_bool persistent)
{
struct st_mysqlnd_packet_change_auth_response * packet = mnd_pecalloc(1, packet_methods[PROT_CHANGE_AUTH_RESP_PACKET].struct_size, persistent);
DBG_ENTER("mysqlnd_protocol::get_change_auth_response_packet");
if (packet) {
packet->header.m = &packet_methods[PROT_CHANGE_AUTH_RESP_PACKET];
+ packet->header.factory = factory;
+
+ packet->header.protocol_frame_codec = factory->conn->protocol_frame_codec;
+ packet->header.vio = factory->conn->vio;
+ packet->header.stats = factory->conn->stats;
+ packet->header.error_info = factory->conn->error_info;
+ packet->header.connection_state = &factory->conn->state;
+
packet->header.persistent = persistent;
}
DBG_RETURN(packet);
@@ -2363,12 +2461,20 @@ MYSQLND_METHOD(mysqlnd_protocol, get_change_auth_response_packet)(MYSQLND_PROTOC
/* {{{ mysqlnd_protocol::get_ok_packet */
static struct st_mysqlnd_packet_ok *
-MYSQLND_METHOD(mysqlnd_protocol, get_ok_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent)
+MYSQLND_METHOD(mysqlnd_protocol, get_ok_packet)(MYSQLND_PROTOCOL_PAYLOAD_DECODER_FACTORY * const factory, const zend_bool persistent)
{
struct st_mysqlnd_packet_ok * packet = mnd_pecalloc(1, packet_methods[PROT_OK_PACKET].struct_size, persistent);
DBG_ENTER("mysqlnd_protocol::get_ok_packet");
if (packet) {
packet->header.m = &packet_methods[PROT_OK_PACKET];
+ packet->header.factory = factory;
+
+ packet->header.protocol_frame_codec = factory->conn->protocol_frame_codec;
+ packet->header.vio = factory->conn->vio;
+ packet->header.stats = factory->conn->stats;
+ packet->header.error_info = factory->conn->error_info;
+ packet->header.connection_state = &factory->conn->state;
+
packet->header.persistent = persistent;
}
DBG_RETURN(packet);
@@ -2378,12 +2484,20 @@ MYSQLND_METHOD(mysqlnd_protocol, get_ok_packet)(MYSQLND_PROTOCOL * const protoco
/* {{{ mysqlnd_protocol::get_eof_packet */
static struct st_mysqlnd_packet_eof *
-MYSQLND_METHOD(mysqlnd_protocol, get_eof_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent)
+MYSQLND_METHOD(mysqlnd_protocol, get_eof_packet)(MYSQLND_PROTOCOL_PAYLOAD_DECODER_FACTORY * const factory, const zend_bool persistent)
{
struct st_mysqlnd_packet_eof * packet = mnd_pecalloc(1, packet_methods[PROT_EOF_PACKET].struct_size, persistent);
DBG_ENTER("mysqlnd_protocol::get_eof_packet");
if (packet) {
packet->header.m = &packet_methods[PROT_EOF_PACKET];
+ packet->header.factory = factory;
+
+ packet->header.protocol_frame_codec = factory->conn->protocol_frame_codec;
+ packet->header.vio = factory->conn->vio;
+ packet->header.stats = factory->conn->stats;
+ packet->header.error_info = factory->conn->error_info;
+ packet->header.connection_state = &factory->conn->state;
+
packet->header.persistent = persistent;
}
DBG_RETURN(packet);
@@ -2393,12 +2507,20 @@ MYSQLND_METHOD(mysqlnd_protocol, get_eof_packet)(MYSQLND_PROTOCOL * const protoc
/* {{{ mysqlnd_protocol::get_command_packet */
static struct st_mysqlnd_packet_command *
-MYSQLND_METHOD(mysqlnd_protocol, get_command_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent)
+MYSQLND_METHOD(mysqlnd_protocol, get_command_packet)(MYSQLND_PROTOCOL_PAYLOAD_DECODER_FACTORY * const factory, const zend_bool persistent)
{
struct st_mysqlnd_packet_command * packet = mnd_pecalloc(1, packet_methods[PROT_CMD_PACKET].struct_size, persistent);
DBG_ENTER("mysqlnd_protocol::get_command_packet");
if (packet) {
packet->header.m = &packet_methods[PROT_CMD_PACKET];
+ packet->header.factory = factory;
+
+ packet->header.protocol_frame_codec = factory->conn->protocol_frame_codec;
+ packet->header.vio = factory->conn->vio;
+ packet->header.stats = factory->conn->stats;
+ packet->header.error_info = factory->conn->error_info;
+ packet->header.connection_state = &factory->conn->state;
+
packet->header.persistent = persistent;
}
DBG_RETURN(packet);
@@ -2408,12 +2530,20 @@ MYSQLND_METHOD(mysqlnd_protocol, get_command_packet)(MYSQLND_PROTOCOL * const pr
/* {{{ mysqlnd_protocol::get_rset_packet */
static struct st_mysqlnd_packet_rset_header *
-MYSQLND_METHOD(mysqlnd_protocol, get_rset_header_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent)
+MYSQLND_METHOD(mysqlnd_protocol, get_rset_header_packet)(MYSQLND_PROTOCOL_PAYLOAD_DECODER_FACTORY * const factory, const zend_bool persistent)
{
struct st_mysqlnd_packet_rset_header * packet = mnd_pecalloc(1, packet_methods[PROT_RSET_HEADER_PACKET].struct_size, persistent);
DBG_ENTER("mysqlnd_protocol::get_rset_header_packet");
if (packet) {
packet->header.m = &packet_methods[PROT_RSET_HEADER_PACKET];
+ packet->header.factory = factory;
+
+ packet->header.protocol_frame_codec = factory->conn->protocol_frame_codec;
+ packet->header.vio = factory->conn->vio;
+ packet->header.stats = factory->conn->stats;
+ packet->header.error_info = factory->conn->error_info;
+ packet->header.connection_state = &factory->conn->state;
+
packet->header.persistent = persistent;
}
DBG_RETURN(packet);
@@ -2423,12 +2553,20 @@ MYSQLND_METHOD(mysqlnd_protocol, get_rset_header_packet)(MYSQLND_PROTOCOL * cons
/* {{{ mysqlnd_protocol::get_result_field_packet */
static struct st_mysqlnd_packet_res_field *
-MYSQLND_METHOD(mysqlnd_protocol, get_result_field_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent)
+MYSQLND_METHOD(mysqlnd_protocol, get_result_field_packet)(MYSQLND_PROTOCOL_PAYLOAD_DECODER_FACTORY * const factory, const zend_bool persistent)
{
struct st_mysqlnd_packet_res_field * packet = mnd_pecalloc(1, packet_methods[PROT_RSET_FLD_PACKET].struct_size, persistent);
DBG_ENTER("mysqlnd_protocol::get_result_field_packet");
if (packet) {
packet->header.m = &packet_methods[PROT_RSET_FLD_PACKET];
+ packet->header.factory = factory;
+
+ packet->header.protocol_frame_codec = factory->conn->protocol_frame_codec;
+ packet->header.vio = factory->conn->vio;
+ packet->header.stats = factory->conn->stats;
+ packet->header.error_info = factory->conn->error_info;
+ packet->header.connection_state = &factory->conn->state;
+
packet->header.persistent = persistent;
}
DBG_RETURN(packet);
@@ -2438,12 +2576,21 @@ MYSQLND_METHOD(mysqlnd_protocol, get_result_field_packet)(MYSQLND_PROTOCOL * con
/* {{{ mysqlnd_protocol::get_row_packet */
static struct st_mysqlnd_packet_row *
-MYSQLND_METHOD(mysqlnd_protocol, get_row_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent)
+MYSQLND_METHOD(mysqlnd_protocol, get_row_packet)(MYSQLND_PROTOCOL_PAYLOAD_DECODER_FACTORY * const factory, const zend_bool persistent)
{
struct st_mysqlnd_packet_row * packet = mnd_pecalloc(1, packet_methods[PROT_ROW_PACKET].struct_size, persistent);
DBG_ENTER("mysqlnd_protocol::get_row_packet");
if (packet) {
packet->header.m = &packet_methods[PROT_ROW_PACKET];
+ packet->header.factory = factory;
+
+ packet->header.conn = factory->conn;
+ packet->header.protocol_frame_codec = factory->conn->protocol_frame_codec;
+ packet->header.vio = factory->conn->vio;
+ packet->header.stats = factory->conn->stats;
+ packet->header.error_info = factory->conn->error_info;
+ packet->header.connection_state = &factory->conn->state;
+
packet->header.persistent = persistent;
}
DBG_RETURN(packet);
@@ -2453,12 +2600,20 @@ MYSQLND_METHOD(mysqlnd_protocol, get_row_packet)(MYSQLND_PROTOCOL * const protoc
/* {{{ mysqlnd_protocol::get_stats_packet */
static struct st_mysqlnd_packet_stats *
-MYSQLND_METHOD(mysqlnd_protocol, get_stats_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent)
+MYSQLND_METHOD(mysqlnd_protocol, get_stats_packet)(MYSQLND_PROTOCOL_PAYLOAD_DECODER_FACTORY * const factory, const zend_bool persistent)
{
struct st_mysqlnd_packet_stats * packet = mnd_pecalloc(1, packet_methods[PROT_STATS_PACKET].struct_size, persistent);
DBG_ENTER("mysqlnd_protocol::get_stats_packet");
if (packet) {
packet->header.m = &packet_methods[PROT_STATS_PACKET];
+ packet->header.factory = factory;
+
+ packet->header.protocol_frame_codec = factory->conn->protocol_frame_codec;
+ packet->header.vio = factory->conn->vio;
+ packet->header.stats = factory->conn->stats;
+ packet->header.error_info = factory->conn->error_info;
+ packet->header.connection_state = &factory->conn->state;
+
packet->header.persistent = persistent;
}
DBG_RETURN(packet);
@@ -2468,12 +2623,20 @@ MYSQLND_METHOD(mysqlnd_protocol, get_stats_packet)(MYSQLND_PROTOCOL * const prot
/* {{{ mysqlnd_protocol::get_prepare_response_packet */
static struct st_mysqlnd_packet_prepare_response *
-MYSQLND_METHOD(mysqlnd_protocol, get_prepare_response_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent)
+MYSQLND_METHOD(mysqlnd_protocol, get_prepare_response_packet)(MYSQLND_PROTOCOL_PAYLOAD_DECODER_FACTORY * const factory, const zend_bool persistent)
{
struct st_mysqlnd_packet_prepare_response * packet = mnd_pecalloc(1, packet_methods[PROT_PREPARE_RESP_PACKET].struct_size, persistent);
DBG_ENTER("mysqlnd_protocol::get_prepare_response_packet");
if (packet) {
packet->header.m = &packet_methods[PROT_PREPARE_RESP_PACKET];
+ packet->header.factory = factory;
+
+ packet->header.protocol_frame_codec = factory->conn->protocol_frame_codec;
+ packet->header.vio = factory->conn->vio;
+ packet->header.stats = factory->conn->stats;
+ packet->header.error_info = factory->conn->error_info;
+ packet->header.connection_state = &factory->conn->state;
+
packet->header.persistent = persistent;
}
DBG_RETURN(packet);
@@ -2483,12 +2646,20 @@ MYSQLND_METHOD(mysqlnd_protocol, get_prepare_response_packet)(MYSQLND_PROTOCOL *
/* {{{ mysqlnd_protocol::get_change_user_response_packet */
static struct st_mysqlnd_packet_chg_user_resp*
-MYSQLND_METHOD(mysqlnd_protocol, get_change_user_response_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent)
+MYSQLND_METHOD(mysqlnd_protocol, get_change_user_response_packet)(MYSQLND_PROTOCOL_PAYLOAD_DECODER_FACTORY * const factory, const zend_bool persistent)
{
struct st_mysqlnd_packet_chg_user_resp * packet = mnd_pecalloc(1, packet_methods[PROT_CHG_USER_RESP_PACKET].struct_size, persistent);
DBG_ENTER("mysqlnd_protocol::get_change_user_response_packet");
if (packet) {
packet->header.m = &packet_methods[PROT_CHG_USER_RESP_PACKET];
+ packet->header.factory = factory;
+
+ packet->header.protocol_frame_codec = factory->conn->protocol_frame_codec;
+ packet->header.vio = factory->conn->vio;
+ packet->header.stats = factory->conn->stats;
+ packet->header.error_info = factory->conn->error_info;
+ packet->header.connection_state = &factory->conn->state;
+
packet->header.persistent = persistent;
}
DBG_RETURN(packet);
@@ -2498,12 +2669,20 @@ MYSQLND_METHOD(mysqlnd_protocol, get_change_user_response_packet)(MYSQLND_PROTOC
/* {{{ mysqlnd_protocol::get_sha256_pk_request_packet */
static struct st_mysqlnd_packet_sha256_pk_request *
-MYSQLND_METHOD(mysqlnd_protocol, get_sha256_pk_request_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent)
+MYSQLND_METHOD(mysqlnd_protocol, get_sha256_pk_request_packet)(MYSQLND_PROTOCOL_PAYLOAD_DECODER_FACTORY * const factory, const zend_bool persistent)
{
struct st_mysqlnd_packet_sha256_pk_request * packet = mnd_pecalloc(1, packet_methods[PROT_SHA256_PK_REQUEST_PACKET].struct_size, persistent);
DBG_ENTER("mysqlnd_protocol::get_sha256_pk_request_packet");
if (packet) {
packet->header.m = &packet_methods[PROT_SHA256_PK_REQUEST_PACKET];
+ packet->header.factory = factory;
+
+ packet->header.protocol_frame_codec = factory->conn->protocol_frame_codec;
+ packet->header.vio = factory->conn->vio;
+ packet->header.stats = factory->conn->stats;
+ packet->header.error_info = factory->conn->error_info;
+ packet->header.connection_state = &factory->conn->state;
+
packet->header.persistent = persistent;
}
DBG_RETURN(packet);
@@ -2513,12 +2692,20 @@ MYSQLND_METHOD(mysqlnd_protocol, get_sha256_pk_request_packet)(MYSQLND_PROTOCOL
/* {{{ mysqlnd_protocol::get_sha256_pk_request_response_packet */
static struct st_mysqlnd_packet_sha256_pk_request_response *
-MYSQLND_METHOD(mysqlnd_protocol, get_sha256_pk_request_response_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent)
+MYSQLND_METHOD(mysqlnd_protocol, get_sha256_pk_request_response_packet)(MYSQLND_PROTOCOL_PAYLOAD_DECODER_FACTORY * const factory, const zend_bool persistent)
{
struct st_mysqlnd_packet_sha256_pk_request_response * packet = mnd_pecalloc(1, packet_methods[PROT_SHA256_PK_REQUEST_RESPONSE_PACKET].struct_size, persistent);
DBG_ENTER("mysqlnd_protocol::get_sha256_pk_request_response_packet");
if (packet) {
packet->header.m = &packet_methods[PROT_SHA256_PK_REQUEST_RESPONSE_PACKET];
+ packet->header.factory = factory;
+
+ packet->header.protocol_frame_codec = factory->conn->protocol_frame_codec;
+ packet->header.vio = factory->conn->vio;
+ packet->header.stats = factory->conn->stats;
+ packet->header.error_info = factory->conn->error_info;
+ packet->header.connection_state = &factory->conn->state;
+
packet->header.persistent = persistent;
}
DBG_RETURN(packet);
@@ -2526,8 +2713,220 @@ MYSQLND_METHOD(mysqlnd_protocol, get_sha256_pk_request_response_packet)(MYSQLND_
/* }}} */
+/* {{{ mysqlnd_protocol::send_command */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_protocol, send_command)(
+ MYSQLND_PROTOCOL_PAYLOAD_DECODER_FACTORY * payload_decoder_factory,
+ const enum php_mysqlnd_server_command command,
+ const zend_uchar * const arg, const size_t arg_len,
+ const zend_bool silent,
+
+ struct st_mysqlnd_connection_state * connection_state,
+ MYSQLND_ERROR_INFO * error_info,
+ MYSQLND_UPSERT_STATUS * upsert_status,
+ MYSQLND_STATS * stats,
+ func_mysqlnd_conn_data__send_close send_close,
+ void * send_close_ctx)
+{
+ enum_func_status ret = PASS;
+ MYSQLND_PACKET_COMMAND * cmd_packet = NULL;
+ enum mysqlnd_connection_state state;
+ DBG_ENTER("mysqlnd_protocol::send_command");
+ DBG_INF_FMT("command=%s silent=%u", mysqlnd_command_to_text[command], silent);
+ DBG_INF_FMT("server_status=%u", UPSERT_STATUS_GET_SERVER_STATUS(upsert_status));
+ DBG_INF_FMT("sending %u bytes", arg_len + 1); /* + 1 is for the command */
+ state = connection_state->m->get(connection_state);
+
+ switch (state) {
+ case CONN_READY:
+ break;
+ case CONN_QUIT_SENT:
+ SET_CLIENT_ERROR(error_info, CR_SERVER_GONE_ERROR, UNKNOWN_SQLSTATE, mysqlnd_server_gone);
+ DBG_ERR("Server is gone");
+ DBG_RETURN(FAIL);
+ default:
+ SET_CLIENT_ERROR(error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, mysqlnd_out_of_sync);
+ DBG_ERR_FMT("Command out of sync. State=%u", state);
+ DBG_RETURN(FAIL);
+ }
+
+ UPSERT_STATUS_SET_AFFECTED_ROWS_TO_ERROR(upsert_status);
+ SET_EMPTY_ERROR(error_info);
+
+ cmd_packet = payload_decoder_factory->m.get_command_packet(payload_decoder_factory, FALSE);
+ if (!cmd_packet) {
+ SET_OOM_ERROR(error_info);
+ DBG_RETURN(FAIL);
+ }
+
+ cmd_packet->command = command;
+ if (arg && arg_len) {
+ cmd_packet->argument.s = (char *) arg;
+ cmd_packet->argument.l = arg_len;
+ }
+
+ MYSQLND_INC_CONN_STATISTIC(stats, STAT_COM_QUIT + command - 1 /* because of COM_SLEEP */ );
+
+ if (! PACKET_WRITE(cmd_packet)) {
+ if (!silent) {
+ DBG_ERR_FMT("Error while sending %s packet", mysqlnd_command_to_text[command]);
+ php_error(E_WARNING, "Error while sending %s packet. PID=%d", mysqlnd_command_to_text[command], getpid());
+ }
+ connection_state->m->set(connection_state, CONN_QUIT_SENT);
+ send_close(send_close_ctx);
+ DBG_ERR("Server is gone");
+ ret = FAIL;
+ }
+ PACKET_FREE(cmd_packet);
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_protocol::send_command_handle_OK */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_protocol, send_command_handle_OK)(
+ MYSQLND_PROTOCOL_PAYLOAD_DECODER_FACTORY * const payload_decoder_factory,
+ MYSQLND_ERROR_INFO * const error_info,
+ MYSQLND_UPSERT_STATUS * const upsert_status,
+ const zend_bool ignore_upsert_status, /* actually used only by LOAD DATA. COM_QUERY and COM_EXECUTE handle the responses themselves */
+ MYSQLND_STRING * const last_message,
+ const zend_bool last_message_persistent)
+{
+ enum_func_status ret = FAIL;
+ MYSQLND_PACKET_OK * ok_response = payload_decoder_factory->m.get_ok_packet(payload_decoder_factory, FALSE);
+
+ DBG_ENTER("mysqlnd_protocol::send_command_handle_OK");
+ if (!ok_response) {
+ SET_OOM_ERROR(error_info);
+ DBG_RETURN(FAIL);
+ }
+ if (FAIL == (ret = PACKET_READ(ok_response))) {
+ DBG_INF("Error while reading OK packet");
+ SET_CLIENT_ERROR(error_info, CR_MALFORMED_PACKET, UNKNOWN_SQLSTATE, "Malformed packet");
+ goto end;
+ }
+ DBG_INF_FMT("OK from server");
+ if (0xFF == ok_response->field_count) {
+ /* The server signalled error. Set the error */
+ SET_CLIENT_ERROR(error_info, ok_response->error_no, ok_response->sqlstate, ok_response->error);
+ ret = FAIL;
+ /*
+ Cover a protocol design error: error packet does not
+ contain the server status. Therefore, the client has no way
+ to find out whether there are more result sets of
+ a multiple-result-set statement pending. Luckily, in 5.0 an
+ error always aborts execution of a statement, wherever it is
+ a multi-statement or a stored procedure, so it should be
+ safe to unconditionally turn off the flag here.
+ */
+ upsert_status->server_status &= ~SERVER_MORE_RESULTS_EXISTS;
+ UPSERT_STATUS_SET_AFFECTED_ROWS_TO_ERROR(upsert_status);
+ } else {
+ SET_NEW_MESSAGE(last_message->s, last_message->l,
+ ok_response->message, ok_response->message_len,
+ last_message_persistent);
+ if (!ignore_upsert_status) {
+ UPSERT_STATUS_RESET(upsert_status);
+ UPSERT_STATUS_SET_WARNINGS(upsert_status, ok_response->warning_count);
+ UPSERT_STATUS_SET_SERVER_STATUS(upsert_status, ok_response->server_status);
+ UPSERT_STATUS_SET_AFFECTED_ROWS(upsert_status, ok_response->affected_rows);
+ UPSERT_STATUS_SET_LAST_INSERT_ID(upsert_status, ok_response->last_insert_id);
+ } else {
+ /* LOAD DATA */
+ }
+ }
+end:
+ PACKET_FREE(ok_response);
+ DBG_INF(ret == PASS ? "PASS":"FAIL");
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_protocol::send_command_handle_EOF */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_protocol, send_command_handle_EOF)(
+ MYSQLND_PROTOCOL_PAYLOAD_DECODER_FACTORY * const payload_decoder_factory,
+ MYSQLND_ERROR_INFO * const error_info,
+ MYSQLND_UPSERT_STATUS * const upsert_status)
+{
+ enum_func_status ret = FAIL;
+ MYSQLND_PACKET_EOF * response = payload_decoder_factory->m.get_eof_packet(payload_decoder_factory, FALSE);
+
+ DBG_ENTER("mysqlnd_protocol::send_command_handle_EOF");
+
+ if (!response) {
+ SET_OOM_ERROR(error_info);
+ DBG_RETURN(FAIL);
+ }
+ if (FAIL == (ret = PACKET_READ(response))) {
+ DBG_INF("Error while reading EOF packet");
+ SET_CLIENT_ERROR(error_info, CR_MALFORMED_PACKET, UNKNOWN_SQLSTATE, "Malformed packet");
+ } else if (0xFF == response->field_count) {
+ /* The server signalled error. Set the error */
+ DBG_INF_FMT("Error_no=%d SQLstate=%s Error=%s", response->error_no, response->sqlstate, response->error);
+
+ SET_CLIENT_ERROR(error_info, response->error_no, response->sqlstate, response->error);
+
+ UPSERT_STATUS_SET_AFFECTED_ROWS_TO_ERROR(upsert_status);
+ } else if (0xFE != response->field_count) {
+ SET_CLIENT_ERROR(error_info, CR_MALFORMED_PACKET, UNKNOWN_SQLSTATE, "Malformed packet");
+ DBG_ERR_FMT("EOF packet expected, field count wasn't 0xFE but 0x%2X", response->field_count);
+ php_error_docref(NULL, E_WARNING, "EOF packet expected, field count wasn't 0xFE but 0x%2X", response->field_count);
+ } else {
+ DBG_INF_FMT("EOF from server");
+ }
+ PACKET_FREE(response);
+
+ DBG_INF(ret == PASS ? "PASS":"FAIL");
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ send_command_handle_response */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_protocol, send_command_handle_response)(
+ MYSQLND_PROTOCOL_PAYLOAD_DECODER_FACTORY * payload_decoder_factory,
+ const enum mysqlnd_packet_type ok_packet,
+ const zend_bool silent,
+ const enum php_mysqlnd_server_command command,
+ const zend_bool ignore_upsert_status, /* actually used only by LOAD DATA. COM_QUERY and COM_EXECUTE handle the responses themselves */
+
+ MYSQLND_ERROR_INFO * error_info,
+ MYSQLND_UPSERT_STATUS * upsert_status,
+ MYSQLND_STRING * last_message,
+ zend_bool last_message_persistent
+ )
+{
+ enum_func_status ret = FAIL;
+
+ DBG_ENTER("mysqlnd_protocol::send_command_handle_response");
+ DBG_INF_FMT("silent=%u packet=%u command=%s", silent, ok_packet, mysqlnd_command_to_text[command]);
+
+ switch (ok_packet) {
+ case PROT_OK_PACKET:
+ ret = payload_decoder_factory->m.send_command_handle_OK(payload_decoder_factory, error_info, upsert_status, ignore_upsert_status, last_message, last_message_persistent);
+ break;
+ case PROT_EOF_PACKET:
+ ret = payload_decoder_factory->m.send_command_handle_EOF(payload_decoder_factory, error_info, upsert_status);
+ break;
+ default:
+ SET_CLIENT_ERROR(error_info, CR_MALFORMED_PACKET, UNKNOWN_SQLSTATE, "Malformed packet");
+ php_error_docref(NULL, E_ERROR, "Wrong response packet %u passed to the function", ok_packet);
+ break;
+ }
+ if (!silent && error_info->error_no == CR_MALFORMED_PACKET) {
+ php_error_docref(NULL, E_WARNING, "Error while reading %s's response packet. PID=%d", mysqlnd_command_to_text[command], getpid());
+ }
+ DBG_INF(ret == PASS ? "PASS":"FAIL");
+ DBG_RETURN(ret);
+}
+/* }}} */
-MYSQLND_CLASS_METHODS_START(mysqlnd_protocol)
+
+MYSQLND_CLASS_METHODS_START(mysqlnd_protocol_payload_decoder_factory)
MYSQLND_METHOD(mysqlnd_protocol, get_greet_packet),
MYSQLND_METHOD(mysqlnd_protocol, get_auth_packet),
MYSQLND_METHOD(mysqlnd_protocol, get_auth_response_packet),
@@ -2542,31 +2941,36 @@ MYSQLND_CLASS_METHODS_START(mysqlnd_protocol)
MYSQLND_METHOD(mysqlnd_protocol, get_prepare_response_packet),
MYSQLND_METHOD(mysqlnd_protocol, get_change_user_response_packet),
MYSQLND_METHOD(mysqlnd_protocol, get_sha256_pk_request_packet),
- MYSQLND_METHOD(mysqlnd_protocol, get_sha256_pk_request_response_packet)
+ MYSQLND_METHOD(mysqlnd_protocol, get_sha256_pk_request_response_packet),
+
+ MYSQLND_METHOD(mysqlnd_protocol, send_command),
+ MYSQLND_METHOD(mysqlnd_protocol, send_command_handle_response),
+ MYSQLND_METHOD(mysqlnd_protocol, send_command_handle_OK),
+ MYSQLND_METHOD(mysqlnd_protocol, send_command_handle_EOF),
MYSQLND_CLASS_METHODS_END;
-/* {{{ mysqlnd_protocol_init */
-PHPAPI MYSQLND_PROTOCOL *
-mysqlnd_protocol_init(zend_bool persistent)
+/* {{{ mysqlnd_protocol_payload_decoder_factory_init */
+PHPAPI MYSQLND_PROTOCOL_PAYLOAD_DECODER_FACTORY *
+mysqlnd_protocol_payload_decoder_factory_init(MYSQLND_CONN_DATA * conn, const zend_bool persistent)
{
- MYSQLND_PROTOCOL * ret;
- DBG_ENTER("mysqlnd_protocol_init");
- ret = MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_object_factory).get_protocol_decoder(persistent);
+ MYSQLND_PROTOCOL_PAYLOAD_DECODER_FACTORY * ret;
+ DBG_ENTER("mysqlnd_protocol_payload_decoder_factory_init");
+ ret = MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_object_factory).get_protocol_payload_decoder_factory(conn, persistent);
DBG_RETURN(ret);
}
/* }}} */
-/* {{{ mysqlnd_protocol_free */
+/* {{{ mysqlnd_protocol_payload_decoder_factory_free */
PHPAPI void
-mysqlnd_protocol_free(MYSQLND_PROTOCOL * const protocol)
+mysqlnd_protocol_payload_decoder_factory_free(MYSQLND_PROTOCOL_PAYLOAD_DECODER_FACTORY * const factory)
{
- DBG_ENTER("mysqlnd_protocol_free");
+ DBG_ENTER("mysqlnd_protocol_payload_decoder_factory_free");
- if (protocol) {
- zend_bool pers = protocol->persistent;
- mnd_pefree(protocol, pers);
+ if (factory) {
+ zend_bool pers = factory->persistent;
+ mnd_pefree(factory, pers);
}
DBG_VOID_RETURN;
}
diff --git a/ext/mysqlnd/mysqlnd_wireprotocol.h b/ext/mysqlnd/mysqlnd_wireprotocol.h
index 9164a64018..337143fb9f 100644
--- a/ext/mysqlnd/mysqlnd_wireprotocol.h
+++ b/ext/mysqlnd/mysqlnd_wireprotocol.h
@@ -14,15 +14,12 @@
+----------------------------------------------------------------------+
| Authors: Andrey Hristov <andrey@php.net> |
| Ulf Wendel <uw@php.net> |
- | Georg Richter <georg@php.net> |
+----------------------------------------------------------------------+
*/
#ifndef MYSQLND_WIREPROTOCOL_H
#define MYSQLND_WIREPROTOCOL_H
-#include "mysqlnd_net.h"
-
#define MYSQLND_HEADER_SIZE 4
#define COMPRESSED_HEADER_SIZE 3
@@ -34,8 +31,8 @@ PHPAPI extern const char mysqlnd_read_body_name[];
/* Packet handling */
-#define PACKET_WRITE(packet, conn) ((packet)->header.m->write_to_net((packet), (conn)))
-#define PACKET_READ(packet, conn) ((packet)->header.m->read_from_net((packet), (conn)))
+#define PACKET_WRITE(packet) ((packet)->header.m->write_to_net((packet)))
+#define PACKET_READ(packet) ((packet)->header.m->read_from_net((packet)))
#define PACKET_FREE(packet) \
do { \
DBG_INF_FMT("PACKET_FREE(%p)", packet); \
@@ -49,17 +46,26 @@ PHPAPI extern const char * const mysqlnd_command_to_text[COM_END];
/* Low-level extraction functionality */
typedef struct st_mysqlnd_packet_methods {
size_t struct_size;
- enum_func_status (*read_from_net)(void * packet, MYSQLND_CONN_DATA * conn);
- size_t (*write_to_net)(void * packet, MYSQLND_CONN_DATA * conn);
+ enum_func_status (*read_from_net)(void * packet);
+ size_t (*write_to_net)(void * packet);
void (*free_mem)(void *packet, zend_bool stack_allocation);
} mysqlnd_packet_methods;
typedef struct st_mysqlnd_packet_header {
size_t size;
- mysqlnd_packet_methods *m;
zend_uchar packet_no;
zend_bool persistent;
+
+ mysqlnd_packet_methods *m;
+
+ MYSQLND_CONN_DATA * conn;
+ MYSQLND_PFC * protocol_frame_codec;
+ MYSQLND_VIO * vio;
+ MYSQLND_ERROR_INFO * error_info;
+ MYSQLND_STATS * stats;
+ MYSQLND_PROTOCOL_PAYLOAD_DECODER_FACTORY * factory;
+ MYSQLND_CONNECTION_STATE * connection_state;
} MYSQLND_PACKET_HEADER;
/* Server greets the client */
@@ -68,9 +74,8 @@ typedef struct st_mysqlnd_packet_greet {
uint8_t protocol_version;
char *server_version;
uint32_t thread_id;
- zend_uchar intern_auth_plugin_data[SCRAMBLE_LENGTH];
- zend_uchar * auth_plugin_data;
- size_t auth_plugin_data_len;
+ char intern_auth_plugin_data[SCRAMBLE_LENGTH];
+ MYSQLND_STRING authentication_plugin_data;
/* 1 byte pad */
uint32_t server_capabilities;
uint8_t charset_no;
@@ -155,8 +160,7 @@ typedef struct st_mysqlnd_packet_ok {
typedef struct st_mysqlnd_packet_command {
MYSQLND_PACKET_HEADER header;
enum php_mysqlnd_server_command command;
- const zend_uchar *argument;
- size_t arg_len;
+ MYSQLND_CSTRING argument;
} MYSQLND_PACKET_COMMAND;
@@ -176,7 +180,7 @@ typedef struct st_mysqlnd_packet_eof {
/* Result Set header*/
typedef struct st_mysqlnd_packet_rset_header {
- MYSQLND_PACKET_HEADER header;
+ MYSQLND_PACKET_HEADER header;
/*
0x00 => ok
~0 => LOAD DATA LOCAL
@@ -193,8 +197,7 @@ typedef struct st_mysqlnd_packet_rset_header {
uint64_t affected_rows;
uint64_t last_insert_id;
/* This is for both LOAD DATA or info, when no result set */
- char *info_or_local_file;
- size_t info_or_local_file_len;
+ MYSQLND_STRING info_or_local_file;
/* If error packet, we use these */
MYSQLND_ERROR_INFO error_info;
} MYSQLND_PACKET_RSET_HEADER;
@@ -206,7 +209,6 @@ typedef struct st_mysqlnd_packet_res_field {
MYSQLND_FIELD *metadata;
/* For table definitions, empty for result sets */
zend_bool skip_parsing;
- zend_bool stupid_list_fields_eof;
zend_bool persistent_alloc;
MYSQLND_ERROR_INFO error_info;
@@ -245,9 +247,7 @@ typedef struct st_mysqlnd_packet_row {
/* Statistics packet */
typedef struct st_mysqlnd_packet_stats {
MYSQLND_PACKET_HEADER header;
- char *message;
- /* message_len is not part of the packet*/
- size_t message_len;
+ MYSQLND_STRING message;
} MYSQLND_PACKET_STATS;
@@ -296,10 +296,8 @@ typedef struct st_mysqlnd_packet_sha256_pk_request_response {
} MYSQLND_PACKET_SHA256_PK_REQUEST_RESPONSE;
-PHPAPI void php_mysqlnd_scramble(zend_uchar * const buffer, const zend_uchar * const scramble, const zend_uchar * const pass, size_t pass_len);
-
-zend_ulong php_mysqlnd_net_field_length(zend_uchar **packet);
-zend_uchar * php_mysqlnd_net_store_length(zend_uchar *packet, uint64_t length);
+zend_ulong php_mysqlnd_net_field_length(const zend_uchar **packet);
+zend_uchar * php_mysqlnd_net_store_length(zend_uchar *packet, const uint64_t length);
size_t php_mysqlnd_net_store_length_size(uint64_t length);
PHPAPI const extern char * const mysqlnd_empty_string;
@@ -318,8 +316,8 @@ enum_func_status php_mysqlnd_rowp_read_text_protocol_c(MYSQLND_MEMORY_POOL_CHUNK
zend_bool as_int_or_float, MYSQLND_STATS * stats);
-PHPAPI MYSQLND_PROTOCOL * mysqlnd_protocol_init(zend_bool persistent);
-PHPAPI void mysqlnd_protocol_free(MYSQLND_PROTOCOL * const protocol);
+PHPAPI MYSQLND_PROTOCOL_PAYLOAD_DECODER_FACTORY * mysqlnd_protocol_payload_decoder_factory_init(MYSQLND_CONN_DATA * conn, const zend_bool persistent);
+PHPAPI void mysqlnd_protocol_payload_decoder_factory_free(MYSQLND_PROTOCOL_PAYLOAD_DECODER_FACTORY * const payload_decoder_factory);
#endif /* MYSQLND_WIREPROTOCOL_H */
diff --git a/ext/mysqlnd/php_mysqlnd.c b/ext/mysqlnd/php_mysqlnd.c
index 5c8a7d81e9..87e5009183 100644
--- a/ext/mysqlnd/php_mysqlnd.c
+++ b/ext/mysqlnd/php_mysqlnd.c
@@ -14,12 +14,10 @@
+----------------------------------------------------------------------+
| Authors: Andrey Hristov <andrey@php.net> |
| Ulf Wendel <uw@php.net> |
- | Georg Richter <georg@php.net> |
+----------------------------------------------------------------------+
*/
#include "php.h"
-#include "php_ini.h"
#include "mysqlnd.h"
#include "mysqlnd_priv.h"
#include "mysqlnd_debug.h"
diff --git a/ext/mysqlnd/php_mysqlnd.h b/ext/mysqlnd/php_mysqlnd.h
index 6e1f285abc..87e6a75b07 100644
--- a/ext/mysqlnd/php_mysqlnd.h
+++ b/ext/mysqlnd/php_mysqlnd.h
@@ -14,7 +14,6 @@
+----------------------------------------------------------------------+
| Authors: Andrey Hristov <andrey@php.net> |
| Ulf Wendel <uw@php.net> |
- | Georg Richter <georg@php.net> |
+----------------------------------------------------------------------+
*/
diff --git a/ext/oci8/oci8.c b/ext/oci8/oci8.c
index 86f22a208f..120f055a3a 100644
--- a/ext/oci8/oci8.c
+++ b/ext/oci8/oci8.c
@@ -103,12 +103,6 @@ zend_class_entry *oci_coll_class_entry_ptr;
#define SQLT_CFILEE 115
#endif
-#if ZEND_MODULE_API_NO > 20020429
-#define ONUPDATELONGFUNC OnUpdateLong
-#else
-#define ONUPDATELONGFUNC OnUpdateInt
-#endif
-
#ifdef ZTS
#define PHP_OCI_INIT_MODE (OCI_DEFAULT | OCI_OBJECT | OCI_THREADED | OCI_NO_MUTEX)
#else
@@ -144,8 +138,6 @@ ZEND_GET_MODULE(oci8)
#endif /* COMPILE_DL */
/* }}} */
-#if defined(ZEND_ENGINE_2) || defined(ZEND_ENGINE_3)
-
/* {{{ Function arginfo */
ZEND_BEGIN_ARG_INFO_EX(arginfo_oci_define_by_name, 0, 0, 3)
ZEND_ARG_INFO(0, statement_resource)
@@ -641,118 +633,6 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_oci_collection_trim_method, 0, 0, 1)
ZEND_END_ARG_INFO()
/* }}} */
-#else /* defined(ZEND_ENGINE_2) || defined(ZEND_ENGINE_3) */
-/* {{{ Keep the old arginfo behavior when building with PHP 4 */
-
-static unsigned char arginfo_ocifetchinto[] = { 2, BYREF_NONE, BYREF_FORCE };
-static unsigned char arginfo_oci_fetch_all[] = { 2, BYREF_NONE, BYREF_FORCE };
-static unsigned char arginfo_oci_define_by_name[] = { 3, BYREF_NONE, BYREF_NONE, BYREF_FORCE };
-static unsigned char arginfo_oci_bind_by_name[] = { 3, BYREF_NONE, BYREF_NONE, BYREF_FORCE };
-static unsigned char arginfo_oci_bind_array_by_name[] = { 3, BYREF_NONE, BYREF_NONE, BYREF_FORCE };
-
-#define arginfo_oci_free_descriptor NULL
-#define arginfo_oci_lob_save NULL
-#define arginfo_oci_lob_import NULL
-#define arginfo_oci_lob_load NULL
-#define arginfo_oci_lob_read NULL
-#define arginfo_oci_lob_eof NULL
-#define arginfo_oci_lob_tell NULL
-#define arginfo_oci_lob_rewind NULL
-#define arginfo_oci_lob_seek NULL
-#define arginfo_oci_lob_size NULL
-#define arginfo_oci_lob_write NULL
-#define arginfo_oci_lob_append NULL
-#define arginfo_oci_lob_truncate NULL
-#define arginfo_oci_lob_erase NULL
-#define arginfo_oci_lob_flush NULL
-#define arginfo_ocisetbufferinglob NULL
-#define arginfo_ocigetbufferinglob NULL
-#define arginfo_oci_lob_copy NULL
-#define arginfo_oci_lob_is_equal NULL
-#define arginfo_oci_lob_export NULL
-#define arginfo_oci_new_descriptor NULL
-#define arginfo_oci_rollback NULL
-#define arginfo_oci_commit NULL
-#define arginfo_oci_field_name NULL
-#define arginfo_oci_field_size NULL
-#define arginfo_oci_field_scale NULL
-#define arginfo_oci_field_precision NULL
-#define arginfo_oci_field_type NULL
-#define arginfo_oci_field_type_raw NULL
-#define arginfo_oci_field_is_null NULL
-#define arginfo_oci_internal_debug NULL
-#define arginfo_oci_execute NULL
-#define arginfo_oci_cancel NULL
-#define arginfo_oci_fetch NULL
-#define arginfo_oci_fetch_object NULL
-#define arginfo_oci_fetch_row NULL
-#define arginfo_oci_fetch_assoc NULL
-#define arginfo_oci_fetch_array NULL
-#define arginfo_oci_free_statement NULL
-#define arginfo_oci_close NULL
-#define arginfo_oci_new_connect NULL
-#define arginfo_oci_connect NULL
-#define arginfo_oci_pconnect NULL
-#define arginfo_oci_error NULL
-#define arginfo_oci_num_fields NULL
-#define arginfo_oci_parse NULL
-#define arginfo_oci_get_implicit_resultset NULL
-#define arginfo_oci_set_prefetch NULL
-#define arginfo_oci_set_client_identifier NULL
-#define arginfo_oci_set_edition NULL
-#define arginfo_oci_set_module_name NULL
-#define arginfo_oci_set_action NULL
-#define arginfo_oci_set_client_info NULL
-#ifdef WAITIING_ORACLE_BUG_16695981_FIX
-#define arginfo_oci_set_db_operation NULL
-#endif
-#define arginfo_oci_password_change NULL
-#define arginfo_oci_new_cursor NULL
-#define arginfo_oci_result NULL
-#define arginfo_oci_client_version NULL
-#define arginfo_oci_server_version NULL
-#define arginfo_oci_statement_type NULL
-#define arginfo_oci_num_rows NULL
-#define arginfo_oci_free_collection NULL
-#define arginfo_oci_collection_append NULL
-#define arginfo_oci_collection_element_get NULL
-#define arginfo_oci_collection_assign NULL
-#define arginfo_oci_collection_element_assign NULL
-#define arginfo_oci_collection_size NULL
-#define arginfo_oci_collection_max NULL
-#define arginfo_oci_collection_trim NULL
-#define arginfo_oci_new_collection NULL
-#define arginfo_oci_lob_size_method NULL
-#define arginfo_oci_lob_getbuffering_method NULL
-#define arginfo_oci_lob_close_method NULL
-#define arginfo_oci_lob_save_method NULL
-#define arginfo_oci_lob_import_method NULL
-#define arginfo_oci_lob_read_method NULL
-#define arginfo_oci_lob_seek_method NULL
-#define arginfo_oci_lob_write_method NULL
-#define arginfo_oci_lob_append_method NULL
-#define arginfo_oci_lob_truncate_method NULL
-#define arginfo_oci_lob_erase_method NULL
-#define arginfo_oci_lob_flush_method NULL
-#define arginfo_oci_lob_setbuffering_method NULL
-#define arginfo_oci_lob_export_method NULL
-#define arginfo_oci_lob_write_temporary_method NULL
-#define arginfo_oci_lob_load_method NULL
-#define arginfo_oci_lob_tell_method NULL
-#define arginfo_oci_lob_rewind_method NULL
-#define arginfo_oci_lob_eof_method NULL
-#define arginfo_oci_free_descriptor_method NULL
-#define arginfo_oci_collection_append_method NULL
-#define arginfo_oci_collection_element_get_method NULL
-#define arginfo_oci_collection_assign_method NULL
-#define arginfo_oci_collection_size_method NULL
-#define arginfo_oci_collection_element_assign_method NULL
-#define arginfo_oci_collection_max_method NULL
-#define arginfo_oci_collection_trim_method NULL
-#define arginfo_oci_collection_free_method NULL
-/* }}} */
-#endif /* defined(ZEND_ENGINE_2) || defined(ZEND_ENGINE_3) */
-
/* {{{ extension function prototypes
*/
PHP_FUNCTION(oci_bind_by_name);
@@ -839,12 +719,7 @@ PHP_FUNCTION(oci_collection_trim);
/* {{{ extension definition structures
*/
-static
-#if (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION > 2) || (PHP_MAJOR_VERSION > 5)
-/* This "if" allows PECL builds from this file to be portable to older PHP releases */
-const
-#endif
-zend_function_entry php_oci_functions[] = {
+static const zend_function_entry php_oci_functions[] = {
PHP_FE(oci_define_by_name, arginfo_oci_define_by_name)
PHP_FE(oci_bind_by_name, arginfo_oci_bind_by_name)
PHP_FE(oci_bind_array_by_name, arginfo_oci_bind_array_by_name)
@@ -970,19 +845,10 @@ zend_function_entry php_oci_functions[] = {
PHP_FALIAS(ocicollsize, oci_collection_size, arginfo_oci_collection_size)
PHP_FALIAS(ocicollmax, oci_collection_max, arginfo_oci_collection_max)
PHP_FALIAS(ocicolltrim, oci_collection_trim, arginfo_oci_collection_trim)
-#if (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION == 3 && PHP_RELEASE_VERSION >= 7) || (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION >= 4) || (PHP_MAJOR_VERSION > 5)
PHP_FE_END
-#else
- {NULL,NULL,NULL}
-#endif
};
-static
-#if (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION > 2) || (PHP_MAJOR_VERSION > 5)
-/* This "if" allows PECL builds from this file to be portable to older PHP releases */
-const
-#endif
-zend_function_entry php_oci_lob_class_functions[] = {
+static const zend_function_entry php_oci_lob_class_functions[] = {
PHP_FALIAS(load, oci_lob_load, arginfo_oci_lob_load_method)
PHP_FALIAS(tell, oci_lob_tell, arginfo_oci_lob_tell_method)
PHP_FALIAS(truncate, oci_lob_truncate, arginfo_oci_lob_truncate_method)
@@ -1005,19 +871,10 @@ zend_function_entry php_oci_lob_class_functions[] = {
PHP_FALIAS(save, oci_lob_save, arginfo_oci_lob_save_method)
PHP_FALIAS(savefile, oci_lob_import, arginfo_oci_lob_import_method)
PHP_FALIAS(free, oci_free_descriptor, arginfo_oci_free_descriptor_method)
-#if (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION == 3 && PHP_RELEASE_VERSION >= 7) || (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION >= 4) || (PHP_MAJOR_VERSION > 5)
PHP_FE_END
-#else
- {NULL,NULL,NULL}
-#endif
};
-static
-#if (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION > 2) || (PHP_MAJOR_VERSION > 5)
-/* This "if" allows PECL builds from this file to be portable to older PHP releases */
-const
-#endif
-zend_function_entry php_oci_coll_class_functions[] = {
+static const zend_function_entry php_oci_coll_class_functions[] = {
PHP_FALIAS(append, oci_collection_append, arginfo_oci_collection_append_method)
PHP_FALIAS(getelem, oci_collection_element_get, arginfo_oci_collection_element_get_method)
PHP_FALIAS(assignelem, oci_collection_element_assign, arginfo_oci_collection_element_assign_method)
@@ -1026,11 +883,7 @@ zend_function_entry php_oci_coll_class_functions[] = {
PHP_FALIAS(max, oci_collection_max, arginfo_oci_collection_max_method)
PHP_FALIAS(trim, oci_collection_trim, arginfo_oci_collection_trim_method)
PHP_FALIAS(free, oci_free_collection, arginfo_oci_collection_free_method)
-#if (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION == 3 && PHP_RELEASE_VERSION >= 7) || (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION >= 4) || (PHP_MAJOR_VERSION > 5)
PHP_FE_END
-#else
- {NULL,NULL,NULL}
-#endif
};
zend_module_entry oci8_module_entry = {
@@ -1053,12 +906,12 @@ zend_module_entry oci8_module_entry = {
/* {{{ PHP_INI */
PHP_INI_BEGIN()
- STD_PHP_INI_ENTRY( "oci8.max_persistent", "-1", PHP_INI_SYSTEM, ONUPDATELONGFUNC, max_persistent, zend_oci_globals, oci_globals)
- STD_PHP_INI_ENTRY( "oci8.persistent_timeout", "-1", PHP_INI_SYSTEM, ONUPDATELONGFUNC, persistent_timeout, zend_oci_globals, oci_globals)
- STD_PHP_INI_ENTRY( "oci8.ping_interval", "60", PHP_INI_SYSTEM, ONUPDATELONGFUNC, ping_interval, zend_oci_globals, oci_globals)
+ STD_PHP_INI_ENTRY( "oci8.max_persistent", "-1", PHP_INI_SYSTEM, OnUpdateLong, max_persistent, zend_oci_globals, oci_globals)
+ STD_PHP_INI_ENTRY( "oci8.persistent_timeout", "-1", PHP_INI_SYSTEM, OnUpdateLong, persistent_timeout, zend_oci_globals, oci_globals)
+ STD_PHP_INI_ENTRY( "oci8.ping_interval", "60", PHP_INI_SYSTEM, OnUpdateLong, ping_interval, zend_oci_globals, oci_globals)
STD_PHP_INI_BOOLEAN("oci8.privileged_connect", "0", PHP_INI_SYSTEM, OnUpdateBool, privileged_connect, zend_oci_globals, oci_globals)
- STD_PHP_INI_ENTRY( "oci8.statement_cache_size", "20", PHP_INI_SYSTEM, ONUPDATELONGFUNC, statement_cache_size, zend_oci_globals, oci_globals)
- STD_PHP_INI_ENTRY( "oci8.default_prefetch", "100", PHP_INI_SYSTEM, ONUPDATELONGFUNC, default_prefetch, zend_oci_globals, oci_globals)
+ STD_PHP_INI_ENTRY( "oci8.statement_cache_size", "20", PHP_INI_SYSTEM, OnUpdateLong, statement_cache_size, zend_oci_globals, oci_globals)
+ STD_PHP_INI_ENTRY( "oci8.default_prefetch", "100", PHP_INI_SYSTEM, OnUpdateLong, default_prefetch, zend_oci_globals, oci_globals)
STD_PHP_INI_BOOLEAN("oci8.old_oci_close_semantics", "0", PHP_INI_SYSTEM, OnUpdateBool, old_oci_close_semantics,zend_oci_globals, oci_globals)
#if (OCI_MAJOR_VERSION >= 11)
STD_PHP_INI_ENTRY( "oci8.connection_class", "", PHP_INI_ALL, OnUpdateString, connection_class, zend_oci_globals, oci_globals)
@@ -1839,13 +1692,6 @@ php_oci_connection *php_oci_do_connect_ex(char *username, int username_len, char
php_error_docref(NULL, E_WARNING, "Privileged connect is disabled. Enable oci8.privileged_connect to be able to connect as SYSOPER or SYSDBA");
return NULL;
}
-#if (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION < 4) || (PHP_MAJOR_VERSION < 5)
- /* Safe mode has been removed in PHP 5.4 */
- if (PG(safe_mode)) {
- php_error_docref(NULL, E_WARNING, "Privileged connect is disabled in Safe Mode");
- return NULL;
- }
-#endif
}
}
diff --git a/ext/oci8/oci8_interface.c b/ext/oci8/oci8_interface.c
index f78f03727d..bb8828b746 100644
--- a/ext/oci8/oci8_interface.c
+++ b/ext/oci8/oci8_interface.c
@@ -240,32 +240,16 @@ PHP_FUNCTION(oci_lob_import)
size_t filename_len;
if (getThis()) {
-#if (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION > 3) || (PHP_MAJOR_VERSION > 5)
if (zend_parse_parameters(ZEND_NUM_ARGS(), "p", &filename, &filename_len) == FAILURE) {
-#else
- if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &filename, &filename_len) == FAILURE) {
-#endif
return;
}
}
else {
-#if (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION > 3) || (PHP_MAJOR_VERSION > 5)
if (zend_parse_parameters(ZEND_NUM_ARGS(), "Op", &z_descriptor, oci_lob_class_entry_ptr, &filename, &filename_len) == FAILURE) {
-#else
- if (zend_parse_parameters(ZEND_NUM_ARGS(), "Os", &z_descriptor, oci_lob_class_entry_ptr, &filename, &filename_len) == FAILURE) {
-#endif
return;
}
}
-#if (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION < 4) || (PHP_MAJOR_VERSION < 5)
- /* The "p" parsing parameter handles this case in PHP 5.4+ */
- if (strlen(filename) != filename_len) {
- php_error_docref(NULL, E_WARNING, "Filename cannot contain null bytes");
- RETURN_FALSE;
- }
-#endif
-
if ((tmp = zend_hash_str_find(Z_OBJPROP_P(z_descriptor), "descriptor", sizeof("descriptor")-1)) == NULL) {
php_error_docref(NULL, E_WARNING, "Unable to find descriptor property");
RETURN_FALSE;
@@ -899,11 +883,7 @@ PHP_FUNCTION(oci_lob_export)
ub4 lob_length;
if (getThis()) {
-#if (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION > 3) || (PHP_MAJOR_VERSION > 5)
if (zend_parse_parameters(ZEND_NUM_ARGS(), "p|ll", &filename, &filename_len, &start, &length) == FAILURE) {
-#else
- if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|ll", &filename, &filename_len, &start, &length) == FAILURE) {
-#endif
return;
}
@@ -917,11 +897,7 @@ PHP_FUNCTION(oci_lob_export)
}
}
else {
-#if (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION > 3) || (PHP_MAJOR_VERSION > 5)
if (zend_parse_parameters(ZEND_NUM_ARGS(), "Op|ll", &z_descriptor, oci_lob_class_entry_ptr, &filename, &filename_len, &start, &length) == FAILURE) {
-#else
- if (zend_parse_parameters(ZEND_NUM_ARGS(), "Os|ll", &z_descriptor, oci_lob_class_entry_ptr, &filename, &filename_len, &start, &length) == FAILURE) {
-#endif
return;
}
@@ -935,14 +911,6 @@ PHP_FUNCTION(oci_lob_export)
}
}
-#if (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION < 4) || (PHP_MAJOR_VERSION < 5)
- /* The "p" parsing parameter handles this case in PHP 5.4+ */
- if (strlen(filename) != filename_len) {
- php_error_docref(NULL, E_WARNING, "Filename cannot contain null bytes");
- RETURN_FALSE;
- }
-#endif
-
if ((tmp = zend_hash_str_find(Z_OBJPROP_P(z_descriptor), "descriptor", sizeof("descriptor")-1)) == NULL) {
php_error_docref(NULL, E_WARNING, "Unable to find descriptor property");
RETURN_FALSE;
@@ -971,22 +939,11 @@ PHP_FUNCTION(oci_lob_export)
RETURN_FALSE;
}
-#if (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION < 4) || (PHP_MAJOR_VERSION < 5)
- /* Safe mode has been removed in PHP 5.4 */
- if (PG(safe_mode) && (!php_checkuid(filename, NULL, CHECKUID_CHECK_FILE_AND_DIR))) {
- RETURN_FALSE;
- }
-#endif
-
if (php_check_open_basedir(filename)) {
RETURN_FALSE;
}
-#if (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION > 3) || (PHP_MAJOR_VERSION > 5)
stream = php_stream_open_wrapper_ex(filename, "w", REPORT_ERRORS, NULL, NULL);
-#else
- stream = php_stream_open_wrapper_ex(filename, "w", ENFORCE_SAFE_MODE | REPORT_ERRORS, NULL, NULL);
-#endif
block_length = PHP_OCI_LOB_BUFFER_SIZE;
if (block_length > length) {
@@ -1989,14 +1946,6 @@ PHP_FUNCTION(oci_password_change)
size_t user_len, pass_old_len, pass_new_len, dbname_len;
php_oci_connection *connection;
-#if (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION < 4) || (PHP_MAJOR_VERSION < 5)
- /* Safe mode has been removed in PHP 5.4 */
- if (PG(safe_mode)) {
- php_error_docref(NULL, E_WARNING, "is disabled in Safe Mode");
- RETURN_FALSE;
- }
-#endif
-
if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "rsss", &z_connection, &user, &user_len, &pass_old, &pass_old_len, &pass_new, &pass_new_len) == SUCCESS) {
PHP_OCI_ZVAL_TO_CONNECTION(z_connection, connection);
diff --git a/ext/oci8/oci8_lob.c b/ext/oci8/oci8_lob.c
index d7b949ae13..78ba8f11fa 100644
--- a/ext/oci8/oci8_lob.c
+++ b/ext/oci8/oci8_lob.c
@@ -716,12 +716,7 @@ int php_oci_lob_import (php_oci_descriptor *descriptor, char *filename)
ub4 offset = 1;
sword errstatus;
-#if (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION > 3) || (PHP_MAJOR_VERSION > 5)
- /* Safe mode has been removed in PHP 5.4 */
if (php_check_open_basedir(filename)) {
-#else
- if ((PG(safe_mode) && (!php_checkuid(filename, NULL, CHECKUID_CHECK_FILE_AND_DIR))) || php_check_open_basedir(filename)) {
-#endif
return 1;
}
diff --git a/ext/opcache/Optimizer/block_pass.c b/ext/opcache/Optimizer/block_pass.c
index 5e9b20d191..02fb8192a6 100644
--- a/ext/opcache/Optimizer/block_pass.c
+++ b/ext/opcache/Optimizer/block_pass.c
@@ -27,8 +27,8 @@
#include "zend_execute.h"
#include "zend_vm.h"
#include "zend_bitset.h"
-
-#define DEBUG_BLOCKPASS 0
+#include "zend_cfg.h"
+#include "zend_dump.h"
/* Checks if a constant (like "true") may be replaced by its value */
int zend_optimizer_get_persistent_constant(zend_string *name, zval *result, int copy)
@@ -39,7 +39,7 @@ int zend_optimizer_get_persistent_constant(zend_string *name, zval *result, int
ALLOCA_FLAG(use_heap);
if ((c = zend_hash_find_ptr(EG(zend_constants), name)) == NULL) {
- lookup_name = DO_ALLOCA(ZSTR_LEN(name) + 1);
+ lookup_name = do_alloca(ZSTR_LEN(name) + 1, use_heap);
memcpy(lookup_name, ZSTR_VAL(name), ZSTR_LEN(name) + 1);
zend_str_tolower(lookup_name, ZSTR_LEN(name));
@@ -50,7 +50,7 @@ int zend_optimizer_get_persistent_constant(zend_string *name, zval *result, int
} else {
retval = 0;
}
- FREE_ALLOCA(lookup_name);
+ free_alloca(lookup_name, use_heap);
}
if (retval) {
@@ -67,483 +67,10 @@ int zend_optimizer_get_persistent_constant(zend_string *name, zval *result, int
return retval;
}
-#if DEBUG_BLOCKPASS
-# define BLOCK_REF(b) b?op_array->opcodes-b->start_opline:-1
-
-static inline void print_block(zend_code_block *block, zend_op *opcodes, char *txt)
-{
- fprintf(stderr, "%sBlock: %d-%d (%d)", txt, block->start_opline - opcodes, block->start_opline - opcodes + block->len - 1, block->len);
- if (!block->access) {
- fprintf(stderr, " unused");
- }
- if (block->op1_to) {
- fprintf(stderr, " 1: %d", block->op1_to->start_opline - opcodes);
- }
- if (block->op2_to) {
- fprintf(stderr, " 2: %d", block->op2_to->start_opline - opcodes);
- }
- if (block->ext_to) {
- fprintf(stderr, " e: %d", block->ext_to->start_opline - opcodes);
- }
- if (block->follow_to) {
- fprintf(stderr, " f: %d", block->follow_to->start_opline - opcodes);
- }
-
- if (block->sources) {
- zend_block_source *bs = block->sources;
- fprintf(stderr, " s:");
- while (bs) {
- fprintf(stderr, " %d", bs->from->start_opline - opcodes);
- bs = bs->next;
- }
- }
-
- fprintf(stderr, "\n");
- fflush(stderr);
-}
-#else
-#define print_block(a,b,c)
-#endif
-
-#define START_BLOCK_OP(opno) blocks[opno].start_opline = &op_array->opcodes[opno]; blocks[opno].start_opline_no = opno; blocks[opno].access = 1
-
-/* find code blocks in op_array
- code block is a set of opcodes with single flow of control, i.e. without jmps,
- branches, etc. */
-static int find_code_blocks(zend_op_array *op_array, zend_cfg *cfg, zend_optimizer_ctx *ctx)
-{
- zend_op *opline;
- zend_op *end = op_array->opcodes + op_array->last;
- zend_code_block *blocks, *cur_block;
- uint32_t opno = 0;
-
- memset(cfg, 0, sizeof(zend_cfg));
- blocks = cfg->blocks = zend_arena_calloc(&ctx->arena, op_array->last + 2, sizeof(zend_code_block));
- opline = op_array->opcodes;
- blocks[0].start_opline = opline;
- blocks[0].start_opline_no = 0;
- while (opline < end) {
- switch((unsigned)opline->opcode) {
- case ZEND_FAST_CALL:
- START_BLOCK_OP(ZEND_OP1(opline).opline_num);
- if (opline->extended_value) {
- START_BLOCK_OP(ZEND_OP2(opline).opline_num);
- }
- START_BLOCK_OP(opno + 1);
- break;
- case ZEND_FAST_RET:
- if (opline->extended_value) {
- START_BLOCK_OP(ZEND_OP2(opline).opline_num);
- }
- START_BLOCK_OP(opno + 1);
- break;
- case ZEND_JMP:
- case ZEND_DECLARE_ANON_CLASS:
- case ZEND_DECLARE_ANON_INHERITED_CLASS:
- START_BLOCK_OP(ZEND_OP1(opline).opline_num);
- /* break missing intentionally */
- case ZEND_RETURN:
- case ZEND_RETURN_BY_REF:
- case ZEND_GENERATOR_RETURN:
- case ZEND_EXIT:
- case ZEND_THROW:
- /* start new block from this+1 */
- START_BLOCK_OP(opno + 1);
- break;
- /* TODO: if conditional jmp depends on constant,
- don't start block that won't be executed */
- case ZEND_CATCH:
- START_BLOCK_OP(opline->extended_value);
- START_BLOCK_OP(opno + 1);
- break;
- case ZEND_JMPZNZ:
- START_BLOCK_OP(opline->extended_value);
- case ZEND_JMPZ:
- case ZEND_JMPNZ:
- case ZEND_JMPZ_EX:
- case ZEND_JMPNZ_EX:
- case ZEND_FE_RESET_R:
- case ZEND_FE_RESET_RW:
- case ZEND_NEW:
- case ZEND_JMP_SET:
- case ZEND_COALESCE:
- case ZEND_ASSERT_CHECK:
- START_BLOCK_OP(ZEND_OP2(opline).opline_num);
- START_BLOCK_OP(opno + 1);
- break;
- case ZEND_FE_FETCH_R:
- case ZEND_FE_FETCH_RW:
- START_BLOCK_OP(opline->extended_value);
- START_BLOCK_OP(opno + 1);
- break;
- }
- opno++;
- opline++;
- }
-
- /* first find block start points */
- if (op_array->last_try_catch) {
- int i;
- cfg->try = zend_arena_calloc(&ctx->arena, op_array->last_try_catch, sizeof(zend_code_block *));
- cfg->catch = zend_arena_calloc(&ctx->arena, op_array->last_try_catch, sizeof(zend_code_block *));
- for (i = 0; i< op_array->last_try_catch; i++) {
- cfg->try[i] = &blocks[op_array->try_catch_array[i].try_op];
- cfg->catch[i] = &blocks[op_array->try_catch_array[i].catch_op];
- START_BLOCK_OP(op_array->try_catch_array[i].try_op);
- START_BLOCK_OP(op_array->try_catch_array[i].catch_op);
- blocks[op_array->try_catch_array[i].try_op].protected = 1;
- }
- }
- /* Currently, we don't optimize op_arrays with BRK/CONT/GOTO opcodes,
- * but, we have to keep brk_cont_array to avoid memory leaks during
- * exception handling */
- if (op_array->last_brk_cont) {
- int i, j;
-
- j = 0;
- for (i = 0; i< op_array->last_brk_cont; i++) {
- if (op_array->brk_cont_array[i].start >= 0 &&
- (op_array->opcodes[op_array->brk_cont_array[i].brk].opcode == ZEND_FREE ||
- op_array->opcodes[op_array->brk_cont_array[i].brk].opcode == ZEND_FE_FREE ||
- op_array->opcodes[op_array->brk_cont_array[i].brk].opcode == ZEND_ROPE_END ||
- op_array->opcodes[op_array->brk_cont_array[i].brk].opcode == ZEND_END_SILENCE)) {
- int parent = op_array->brk_cont_array[i].parent;
-
- while (parent >= 0 &&
- op_array->brk_cont_array[parent].start < 0 &&
- (op_array->opcodes[op_array->brk_cont_array[parent].brk].opcode != ZEND_FREE ||
- op_array->opcodes[op_array->brk_cont_array[parent].brk].opcode != ZEND_FE_FREE ||
- op_array->opcodes[op_array->brk_cont_array[i].brk].opcode != ZEND_ROPE_END ||
- op_array->opcodes[op_array->brk_cont_array[parent].brk].opcode != ZEND_END_SILENCE)) {
- parent = op_array->brk_cont_array[parent].parent;
- }
- op_array->brk_cont_array[i].parent = parent;
- j++;
- }
- }
- if (j) {
- cfg->loop_start = zend_arena_calloc(&ctx->arena, op_array->last_brk_cont, sizeof(zend_code_block *));
- cfg->loop_cont = zend_arena_calloc(&ctx->arena, op_array->last_brk_cont, sizeof(zend_code_block *));
- cfg->loop_brk = zend_arena_calloc(&ctx->arena, op_array->last_brk_cont, sizeof(zend_code_block *));
- j = 0;
- for (i = 0; i< op_array->last_brk_cont; i++) {
- if (op_array->brk_cont_array[i].start >= 0 &&
- (op_array->opcodes[op_array->brk_cont_array[i].brk].opcode == ZEND_FREE ||
- op_array->opcodes[op_array->brk_cont_array[i].brk].opcode == ZEND_FE_FREE ||
- op_array->opcodes[op_array->brk_cont_array[i].brk].opcode == ZEND_ROPE_END ||
- op_array->opcodes[op_array->brk_cont_array[i].brk].opcode == ZEND_END_SILENCE)) {
- if (i != j) {
- op_array->brk_cont_array[j] = op_array->brk_cont_array[i];
- }
- cfg->loop_start[j] = &blocks[op_array->brk_cont_array[j].start];
- cfg->loop_cont[j] = &blocks[op_array->brk_cont_array[j].cont];
- cfg->loop_brk[j] = &blocks[op_array->brk_cont_array[j].brk];
- START_BLOCK_OP(op_array->brk_cont_array[j].start);
- START_BLOCK_OP(op_array->brk_cont_array[j].cont);
- START_BLOCK_OP(op_array->brk_cont_array[j].brk);
- blocks[op_array->brk_cont_array[j].start].protected = 1;
- blocks[op_array->brk_cont_array[j].brk].protected = 1;
- j++;
- }
- }
- op_array->last_brk_cont = j;
- } else {
- efree(op_array->brk_cont_array);
- op_array->brk_cont_array = NULL;
- op_array->last_brk_cont = 0;
- }
- }
-
- /* Build CFG (Control Flow Graph) */
- cur_block = blocks;
- for (opno = 1; opno < op_array->last; opno++) {
- if (blocks[opno].start_opline) {
- /* found new block start */
- cur_block->len = blocks[opno].start_opline - cur_block->start_opline;
- cur_block->next = &blocks[opno];
- /* what is the last OP of previous block? */
- opline = blocks[opno].start_opline - 1;
- if (opline->opcode == ZEND_OP_DATA) {
- opline--;
- }
- switch((unsigned)opline->opcode) {
- case ZEND_RETURN:
- case ZEND_RETURN_BY_REF:
- case ZEND_GENERATOR_RETURN:
- case ZEND_EXIT:
- case ZEND_THROW:
- break;
- case ZEND_FAST_CALL:
- if (opline->extended_value) {
- cur_block->op2_to = &blocks[ZEND_OP2(opline).opline_num];
- }
- cur_block->op1_to = &blocks[ZEND_OP1(opline).opline_num];
- break;
- case ZEND_FAST_RET:
- if (opline->extended_value) {
- cur_block->op2_to = &blocks[ZEND_OP2(opline).opline_num];
- }
- break;
- case ZEND_JMP:
- cur_block->op1_to = &blocks[ZEND_OP1(opline).opline_num];
- break;
- case ZEND_DECLARE_ANON_CLASS:
- case ZEND_DECLARE_ANON_INHERITED_CLASS:
- cur_block->op1_to = &blocks[ZEND_OP1(opline).opline_num];
- cur_block->follow_to = &blocks[opno];
- break;
- case ZEND_JMPZNZ:
- cur_block->op2_to = &blocks[ZEND_OP2(opline).opline_num];
- cur_block->ext_to = &blocks[opline->extended_value];
- break;
- case ZEND_CATCH:
- cur_block->ext_to = &blocks[opline->extended_value];
- cur_block->follow_to = &blocks[opno];
- break;
- case ZEND_FE_FETCH_R:
- case ZEND_FE_FETCH_RW:
- cur_block->ext_to = &blocks[opline->extended_value];
- cur_block->follow_to = &blocks[opno];
- break;
- case ZEND_JMPZ:
- case ZEND_JMPNZ:
- case ZEND_JMPZ_EX:
- case ZEND_JMPNZ_EX:
- case ZEND_FE_RESET_R:
- case ZEND_FE_RESET_RW:
- case ZEND_NEW:
- case ZEND_JMP_SET:
- case ZEND_COALESCE:
- case ZEND_ASSERT_CHECK:
- cur_block->op2_to = &blocks[ZEND_OP2(opline).opline_num];
- /* break missing intentionally */
- default:
- /* next block follows this */
- cur_block->follow_to = &blocks[opno];
- break;
- }
- print_block(cur_block, op_array->opcodes, "");
- cur_block = cur_block->next;
- }
- }
- cur_block->len = end - cur_block->start_opline;
- cur_block->next = &blocks[op_array->last + 1];
- print_block(cur_block, op_array->opcodes, "");
-
- return 1;
-}
-
/* CFG back references management */
-#define ADD_SOURCE(fromb, tob) { \
- zend_block_source *__s = tob->sources; \
- while (__s && __s->from != fromb) __s = __s->next; \
- if (__s == NULL) { \
- zend_block_source *__t = zend_arena_alloc(&ctx->arena, sizeof(zend_block_source)); \
- __t->next = tob->sources; \
- tob->sources = __t; \
- __t->from = fromb; \
- } \
-}
-
-#define DEL_SOURCE(cs) do { \
- *(cs) = (*(cs))->next; \
- } while (0)
-
-
-static inline void replace_source(zend_block_source *list, zend_code_block *old, zend_code_block *new)
-{
- /* replace all references to 'old' in 'list' with 'new' */
- zend_block_source **cs;
- int found = 0;
-
- for (cs = &list; *cs; cs = &((*cs)->next)) {
- if ((*cs)->from == new) {
- if (found) {
- DEL_SOURCE(cs);
- } else {
- found = 1;
- }
- }
-
- if ((*cs)->from == old) {
- if (found) {
- DEL_SOURCE(cs);
- } else {
- (*cs)->from = new;
- found = 1;
- }
- }
- }
-}
-
-static inline void del_source(zend_code_block *from, zend_code_block *to)
-{
- /* delete source 'from' from 'to'-s sources list */
- zend_block_source **cs = &to->sources;
-
- if (to->sources == NULL) {
- to->access = 0;
- return;
- }
-
- if (from == to) {
- return;
- }
-
- while (*cs) {
- if ((*cs)->from == from) {
- DEL_SOURCE(cs);
- break;
- }
- cs = &((*cs)->next);
- }
-
- if (to->sources == NULL) {
- /* 'to' has no more sources - it's unused, will be stripped */
- to->access = 0;
- return;
- }
-
- if (!to->protected && to->sources->next == NULL) {
- /* source to only one block */
- zend_code_block *from_block = to->sources->from;
-
- if (from_block->access && from_block->follow_to == to &&
- from_block->op1_to == NULL &&
- from_block->op2_to == NULL &&
- from_block->ext_to == NULL) {
- /* this block follows it's only predecessor - we can join them */
- zend_op *new_to = from_block->start_opline + from_block->len;
- if (new_to != to->start_opline) {
- /* move block to new location */
- memmove(new_to, to->start_opline, sizeof(zend_op)*to->len);
- }
- /* join blocks' lengths */
- from_block->len += to->len;
- /* move 'to'`s references to 'from' */
- to->start_opline = NULL;
- to->access = 0;
- to->sources = NULL;
- from_block->follow_to = to->follow_to;
- if (to->op1_to) {
- from_block->op1_to = to->op1_to;
- replace_source(to->op1_to->sources, to, from_block);
- }
- if (to->op2_to) {
- from_block->op2_to = to->op2_to;
- replace_source(to->op2_to->sources, to, from_block);
- }
- if (to->ext_to) {
- from_block->ext_to = to->ext_to;
- replace_source(to->ext_to->sources, to, from_block);
- }
- if (to->follow_to) {
- replace_source(to->follow_to->sources, to, from_block);
- }
- /* remove "to" from list */
- }
- }
-}
-
-static void delete_code_block(zend_code_block *block, zend_optimizer_ctx *ctx)
-{
- if (block->protected) {
- return;
- }
- if (block->follow_to) {
- zend_block_source *bs = block->sources;
- while (bs) {
- zend_code_block *from_block = bs->from;
- zend_code_block *to = block->follow_to;
- if (from_block->op1_to == block) {
- from_block->op1_to = to;
- ADD_SOURCE(from_block, to);
- }
- if (from_block->op2_to == block) {
- from_block->op2_to = to;
- ADD_SOURCE(from_block, to);
- }
- if (from_block->ext_to == block) {
- from_block->ext_to = to;
- ADD_SOURCE(from_block, to);
- }
- if (from_block->follow_to == block) {
- from_block->follow_to = to;
- ADD_SOURCE(from_block, to);
- }
- bs = bs->next;
- }
- }
- block->access = 0;
-}
-
-static void zend_access_path(zend_code_block *block, zend_optimizer_ctx *ctx)
-{
- if (block->access) {
- return;
- }
-
- block->access = 1;
- if (block->op1_to) {
- zend_access_path(block->op1_to, ctx);
- ADD_SOURCE(block, block->op1_to);
- }
- if (block->op2_to) {
- zend_access_path(block->op2_to, ctx);
- ADD_SOURCE(block, block->op2_to);
- }
- if (block->ext_to) {
- zend_access_path(block->ext_to, ctx);
- ADD_SOURCE(block, block->ext_to);
- }
- if (block->follow_to) {
- zend_access_path(block->follow_to, ctx);
- ADD_SOURCE(block, block->follow_to);
- }
-}
-
-/* Traverse CFG, mark reachable basic blocks and build back references */
-static void zend_rebuild_access_path(zend_cfg *cfg, zend_op_array *op_array, int find_start, zend_optimizer_ctx *ctx)
-{
- zend_code_block *blocks = cfg->blocks;
- zend_code_block *start = find_start? NULL : blocks;
- zend_code_block *b;
-
- /* Mark all blocks as unaccessible and destroy back references */
- b = blocks;
- while (b != NULL) {
- if (!start && b->access) {
- start = b;
- }
- b->access = 0;
- b->sources = NULL;
- b = b->next;
- }
-
- /* Walk thorough all paths */
- zend_access_path(start, ctx);
-
- /* Add brk/cont paths */
- if (op_array->last_brk_cont) {
- int i;
- for (i=0; i< op_array->last_brk_cont; i++) {
- zend_access_path(cfg->loop_start[i], ctx);
- zend_access_path(cfg->loop_cont[i], ctx);
- zend_access_path(cfg->loop_brk[i], ctx);
- }
- }
-
- /* Add exception paths */
- if (op_array->last_try_catch) {
- int i;
- for (i=0; i< op_array->last_try_catch; i++) {
- if (!cfg->catch[i]->access) {
- zend_access_path(cfg->catch[i], ctx);
- }
- }
- }
-}
+#define DEL_SOURCE(from, to)
+#define ADD_SOURCE(from, to)
/* Data dependencies macros */
@@ -552,8 +79,6 @@ static void zend_rebuild_access_path(zend_cfg *cfg, zend_op_array *op_array, int
#define VAR_SOURCE(op) Tsource[VAR_NUM(op.var)]
#define SET_VAR_SOURCE(opline) Tsource[VAR_NUM(opline->result.var)] = opline
-#define VAR_UNSET(op) do { if (op ## _type & (IS_TMP_VAR|IS_VAR)) {VAR_SOURCE(op) = NULL;}} while (0)
-
#define convert_to_string_safe(v) \
if (Z_TYPE_P((v)) == IS_NULL) { \
ZVAL_STRINGL((v), "", 0); \
@@ -561,146 +86,185 @@ static void zend_rebuild_access_path(zend_cfg *cfg, zend_op_array *op_array, int
convert_to_string((v)); \
}
-static void strip_nop(zend_code_block *block, zend_optimizer_ctx *ctx)
+static void strip_leading_nops(zend_op_array *op_array, zend_basic_block *b)
{
- zend_op *opline = block->start_opline;
- zend_op *end, *new_end;
+ zend_op *opcodes = op_array->opcodes;
- /* remove leading NOPs */
- while (block->len > 0 && block->start_opline->opcode == ZEND_NOP) {
- if (block->len == 1) {
- /* this block is all NOPs, join with following block */
- if (block->follow_to) {
- delete_code_block(block, ctx);
- }
- return;
- }
- block->start_opline++;
- block->start_opline_no++;
- block->len--;
+ while (b->len > 0 && opcodes[b->start].opcode == ZEND_NOP) {
+ b->start++;
+ b->len--;
}
+}
- /* strip the inside NOPs */
- opline = new_end = block->start_opline;
- end = opline + block->len;
-
- while (opline < end) {
- zend_op *src;
- int len = 0;
+static void strip_nops(zend_op_array *op_array, zend_basic_block *b)
+{
+ uint32_t i, j;
- while (opline < end && opline->opcode == ZEND_NOP) {
- opline++;
- }
- src = opline;
+ strip_leading_nops(op_array, b);
+ if (b->len == 0) {
+ return;
+ }
- while (opline < end && opline->opcode != ZEND_NOP) {
- opline++;
+ /* strip the inside NOPs */
+ i = j = b->start + 1;
+ while (i < b->start + b->len) {
+ if (op_array->opcodes[i].opcode != ZEND_NOP) {
+ if (i != j) {
+ op_array->opcodes[j] = op_array->opcodes[i];
+ }
+ j++;
}
- len = opline - src;
-
- /* move up non-NOP opcodes */
- memmove(new_end, src, len*sizeof(zend_op));
-
- new_end += len;
+ i++;
+ }
+ b->len = j - b->start;
+ while (j < i) {
+ MAKE_NOP(op_array->opcodes + j);
+ j++;
}
- block->len = new_end - block->start_opline;
}
-static void zend_optimize_block(zend_code_block *block, zend_op_array *op_array, zend_bitset used_ext, zend_cfg *cfg, zend_optimizer_ctx *ctx)
+static void zend_optimize_block(zend_basic_block *block, zend_op_array *op_array, zend_bitset used_ext, zend_cfg *cfg, zend_op **Tsource)
{
- zend_op *opline = block->start_opline;
+ zend_op *opline, *src;
zend_op *end, *last_op = NULL;
- zend_op **Tsource = cfg->Tsource;
-
- print_block(block, op_array->opcodes, "Opt ");
/* remove leading NOPs */
- while (block->len > 0 && block->start_opline->opcode == ZEND_NOP) {
- if (block->len == 1) {
- /* this block is all NOPs, join with following block */
- if (block->follow_to) {
- delete_code_block(block, ctx);
- }
- return;
- }
- block->start_opline++;
- block->start_opline_no++;
- block->len--;
- }
+ strip_leading_nops(op_array, block);
- /* we track data dependencies only insight a single basic block */
- memset(Tsource, 0, (op_array->last_var + op_array->T) * sizeof(zend_op *));
- opline = block->start_opline;
+ opline = op_array->opcodes + block->start;
end = opline + block->len;
- while ((op_array->T) && (opline < end)) {
- /* strip X = QM_ASSIGN(const) */
- if ((ZEND_OP1_TYPE(opline) & (IS_TMP_VAR|IS_VAR)) &&
- VAR_SOURCE(opline->op1) &&
- VAR_SOURCE(opline->op1)->opcode == ZEND_QM_ASSIGN &&
- ZEND_OP1_TYPE(VAR_SOURCE(opline->op1)) == IS_CONST &&
- opline->opcode != ZEND_CASE && /* CASE _always_ expects variable */
- opline->opcode != ZEND_FETCH_LIST &&
- (opline->opcode != ZEND_FE_RESET_R || opline->opcode != ZEND_FE_RESET_RW) &&
- opline->opcode != ZEND_FREE
- ) {
- znode_op op1 = opline->op1;
- zend_op *src = VAR_SOURCE(op1);
- zval c = ZEND_OP1_LITERAL(src);
- zval_copy_ctor(&c);
- if (zend_optimizer_update_op1_const(op_array, opline, &c)) {
- VAR_SOURCE(op1) = NULL;
- literal_dtor(&ZEND_OP1_LITERAL(src));
- MAKE_NOP(src);
+ while (opline < end) {
+ /* Constant Propagation: strip X = QM_ASSIGN(const) */
+ if ((opline->op1_type & (IS_TMP_VAR|IS_VAR)) &&
+ opline->opcode != ZEND_FREE) {
+ src = VAR_SOURCE(opline->op1);
+ if (src &&
+ src->opcode == ZEND_QM_ASSIGN &&
+ src->op1_type == IS_CONST) {
+
+ znode_op op1 = opline->op1;
+ zval c = ZEND_OP1_LITERAL(src);
+
+ zval_copy_ctor(&c);
+ if (zend_optimizer_update_op1_const(op_array, opline, &c)) {
+ zend_optimizer_remove_live_range(op_array, op1.var);
+ VAR_SOURCE(op1) = NULL;
+ literal_dtor(&ZEND_OP1_LITERAL(src));
+ MAKE_NOP(src);
+ }
}
}
- /* T = QM_ASSIGN(C), F(T) => NOP, F(C) */
- if ((ZEND_OP2_TYPE(opline) & (IS_TMP_VAR|IS_VAR)) &&
- VAR_SOURCE(opline->op2) &&
- VAR_SOURCE(opline->op2)->opcode == ZEND_QM_ASSIGN &&
- ZEND_OP1_TYPE(VAR_SOURCE(opline->op2)) == IS_CONST) {
- znode_op op2 = opline->op2;
- zend_op *src = VAR_SOURCE(op2);
- zval c = ZEND_OP1_LITERAL(src);
- zval_copy_ctor(&c);
- if (zend_optimizer_update_op2_const(op_array, opline, &c)) {
- VAR_SOURCE(op2) = NULL;
- literal_dtor(&ZEND_OP1_LITERAL(src));
- MAKE_NOP(src);
- }
- }
+ /* Constant Propagation: strip X = QM_ASSIGN(const) */
+ if (opline->op2_type & (IS_TMP_VAR|IS_VAR)) {
+ src = VAR_SOURCE(opline->op2);
+ if (src &&
+ src->opcode == ZEND_QM_ASSIGN &&
+ src->op1_type == IS_CONST) {
- /* T = CAST(X, String), ECHO(T) => NOP, ECHO(X) */
- if (opline->opcode == ZEND_ECHO &&
- ZEND_OP1_TYPE(opline) & (IS_TMP_VAR|IS_VAR) &&
- VAR_SOURCE(opline->op1) &&
- VAR_SOURCE(opline->op1)->opcode == ZEND_CAST &&
- VAR_SOURCE(opline->op1)->extended_value == IS_STRING) {
- zend_op *src = VAR_SOURCE(opline->op1);
- COPY_NODE(opline->op1, src->op1);
- MAKE_NOP(src);
- }
+ znode_op op2 = opline->op2;
+ zval c = ZEND_OP1_LITERAL(src);
-#if 0
- /* This pattern is unnecessary for PHP7,
- * since compiler won't generate ZEND_FREE for ZEND_BOOL anymore */
- /* T = BOOL(X), FREE(T) => NOP */
- if (opline->opcode == ZEND_FREE &&
- ZEND_OP1_TYPE(opline) == IS_TMP_VAR &&
- VAR_SOURCE(opline->op1)) {
- zend_op *src = VAR_SOURCE(opline->op1);
- if (src->opcode == ZEND_BOOL) {
- if (ZEND_OP1_TYPE(src) == IS_CONST) {
+ zval_copy_ctor(&c);
+ if (zend_optimizer_update_op2_const(op_array, opline, &c)) {
+ zend_optimizer_remove_live_range(op_array, op2.var);
+ VAR_SOURCE(op2) = NULL;
literal_dtor(&ZEND_OP1_LITERAL(src));
- } else if (ZEND_OP1_TYPE(src) == IS_TMP_VAR) {
- src->opcode = ZEND_FREE;
- } else {
MAKE_NOP(src);
}
- MAKE_NOP(opline);
}
}
+ if (opline->opcode == ZEND_ECHO) {
+ if (opline->op1_type & (IS_TMP_VAR|IS_VAR)) {
+ src = VAR_SOURCE(opline->op1);
+ if (src &&
+ src->opcode == ZEND_CAST &&
+ src->extended_value == IS_STRING) {
+ /* T = CAST(X, String), ECHO(T) => NOP, ECHO(X) */
+ VAR_SOURCE(opline->op1) = NULL;
+ COPY_NODE(opline->op1, src->op1);
+ MAKE_NOP(src);
+ }
+ }
+
+ if (opline->op1_type == IS_CONST) {
+ if (last_op && last_op->opcode == ZEND_ECHO &&
+ last_op->op1_type == IS_CONST &&
+ Z_TYPE(ZEND_OP1_LITERAL(opline)) != IS_DOUBLE &&
+ Z_TYPE(ZEND_OP1_LITERAL(last_op)) != IS_DOUBLE) {
+ /* compress consecutive ECHO's.
+ * Float to string conversion may be affected by current
+ * locale setting.
+ */
+ int l, old_len;
+
+ if (Z_TYPE(ZEND_OP1_LITERAL(opline)) != IS_STRING) {
+ convert_to_string_safe(&ZEND_OP1_LITERAL(opline));
+ }
+ if (Z_TYPE(ZEND_OP1_LITERAL(last_op)) != IS_STRING) {
+ convert_to_string_safe(&ZEND_OP1_LITERAL(last_op));
+ }
+ old_len = Z_STRLEN(ZEND_OP1_LITERAL(last_op));
+ l = old_len + Z_STRLEN(ZEND_OP1_LITERAL(opline));
+ if (!Z_REFCOUNTED(ZEND_OP1_LITERAL(last_op))) {
+ zend_string *tmp = zend_string_alloc(l, 0);
+ memcpy(ZSTR_VAL(tmp), Z_STRVAL(ZEND_OP1_LITERAL(last_op)), old_len);
+ Z_STR(ZEND_OP1_LITERAL(last_op)) = tmp;
+ } else {
+ Z_STR(ZEND_OP1_LITERAL(last_op)) = zend_string_extend(Z_STR(ZEND_OP1_LITERAL(last_op)), l, 0);
+ }
+ Z_TYPE_INFO(ZEND_OP1_LITERAL(last_op)) = IS_STRING_EX;
+ memcpy(Z_STRVAL(ZEND_OP1_LITERAL(last_op)) + old_len, Z_STRVAL(ZEND_OP1_LITERAL(opline)), Z_STRLEN(ZEND_OP1_LITERAL(opline)));
+ Z_STRVAL(ZEND_OP1_LITERAL(last_op))[l] = '\0';
+ zval_dtor(&ZEND_OP1_LITERAL(opline));
+ ZVAL_STR(&ZEND_OP1_LITERAL(opline), zend_new_interned_string(Z_STR(ZEND_OP1_LITERAL(last_op))));
+ ZVAL_NULL(&ZEND_OP1_LITERAL(last_op));
+ MAKE_NOP(last_op);
+ }
+ last_op = opline;
+ } else {
+ last_op = NULL;
+ }
+ } else {
+ last_op = NULL;
+ }
+
+ switch (opline->opcode) {
+
+ case ZEND_FREE:
+ if (opline->op1_type == IS_TMP_VAR) {
+ src = VAR_SOURCE(opline->op1);
+ if (src &&
+ (src->opcode == ZEND_BOOL || src->opcode == ZEND_BOOL_NOT)) {
+ /* T = BOOL(X), FREE(T) => NOP */
+ if (ZEND_OP1_TYPE(src) == IS_CONST) {
+ literal_dtor(&ZEND_OP1_LITERAL(src));
+ }
+ VAR_SOURCE(opline->op1) = NULL;
+ MAKE_NOP(src);
+ MAKE_NOP(opline);
+ }
+ } else if (opline->op1_type == IS_VAR) {
+ src = VAR_SOURCE(opline->op1);
+ /* V = OP, FREE(V) => OP. NOP */
+ if (src &&
+ src->opcode != ZEND_FETCH_R &&
+ src->opcode != ZEND_FETCH_STATIC_PROP_R &&
+ src->opcode != ZEND_FETCH_DIM_R &&
+ src->opcode != ZEND_FETCH_OBJ_R &&
+ src->opcode != ZEND_NEW) {
+ if (opline->extended_value & ZEND_FREE_ON_RETURN) {
+ /* mark as removed (empty live range) */
+ op_array->live_range[opline->op2.num].var = (uint32_t)-1;
+ }
+ ZEND_RESULT_TYPE(src) = IS_UNUSED;
+ MAKE_NOP(opline);
+ }
+ }
+ break;
+
+#if 0
/* pre-evaluate functions:
constant(x)
defined(x)
@@ -763,475 +327,641 @@ static void zend_optimize_block(zend_code_block *block, zend_op_array *op_array,
}
#endif
- /* IS_EQ(TRUE, X) => BOOL(X)
- * IS_EQ(FALSE, X) => BOOL_NOT(X)
- * IS_NOT_EQ(TRUE, X) => BOOL_NOT(X)
- * IS_NOT_EQ(FALSE, X) => BOOL(X)
- * CASE(TRUE, X) => BOOL(X)
- * CASE(FALSE, X) => BOOL_NOT(X)
- */
- if (opline->opcode == ZEND_IS_EQUAL ||
- opline->opcode == ZEND_IS_NOT_EQUAL ||
- /* CASE variable will be deleted later by FREE, so we can't optimize it */
- (opline->opcode == ZEND_CASE && (ZEND_OP1_TYPE(opline) & (IS_CONST|IS_CV)))) {
- if (ZEND_OP1_TYPE(opline) == IS_CONST &&
- (Z_TYPE(ZEND_OP1_LITERAL(opline)) == IS_FALSE ||
- Z_TYPE(ZEND_OP1_LITERAL(opline)) == IS_TRUE)) {
- /* T = IS_EQUAL(TRUE, X) => T = BOOL(X) */
- /* T = IS_EQUAL(FALSE, X) => T = BOOL_NOT(X) */
- /* T = IS_NOT_EQUAL(TRUE, X) => T = BOOL_NOT(X) */
- /* T = IS_NOT_EQUAL(FALSE, X) => T = BOOL(X) */
- /* Optimization of comparison with "null" is not safe,
- * because ("0" == null) is not equal to !("0")
- */
- opline->opcode =
- ((opline->opcode != ZEND_IS_NOT_EQUAL) == ((Z_TYPE(ZEND_OP1_LITERAL(opline))) == IS_TRUE)) ?
- ZEND_BOOL : ZEND_BOOL_NOT;
- COPY_NODE(opline->op1, opline->op2);
- SET_UNUSED(opline->op2);
- } else if (ZEND_OP2_TYPE(opline) == IS_CONST &&
- (Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_FALSE ||
- Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_TRUE)) {
- /* T = IS_EQUAL(X, TRUE) => T = BOOL(X) */
- /* T = IS_EQUAL(X, FALSE) => T = BOOL_NOT(X) */
- /* T = IS_NOT_EQUAL(X, TRUE) => T = BOOL_NOT(X) */
- /* T = IS_NOT_EQUAL(X, FALSE) => T = BOOL(X) */
- /* Optimization of comparison with "null" is not safe,
- * because ("0" == null) is not equal to !("0")
- */
- opline->opcode =
- ((opline->opcode != ZEND_IS_NOT_EQUAL) == ((Z_TYPE(ZEND_OP2_LITERAL(opline))) == IS_TRUE)) ?
- ZEND_BOOL : ZEND_BOOL_NOT;
- SET_UNUSED(opline->op2);
- }
- }
+ case ZEND_FETCH_LIST:
+ if (opline->op1_type & (IS_TMP_VAR|IS_VAR)) {
+ /* LIST variable will be deleted later by FREE */
+ Tsource[VAR_NUM(opline->op1.var)] = NULL;
+ }
+ break;
- if ((opline->opcode == ZEND_BOOL ||
- opline->opcode == ZEND_BOOL_NOT ||
- opline->opcode == ZEND_JMPZ ||
- opline->opcode == ZEND_JMPNZ ||
- opline->opcode == ZEND_JMPZNZ) &&
- ZEND_OP1_TYPE(opline) == IS_TMP_VAR &&
- VAR_SOURCE(opline->op1) != NULL &&
- !zend_bitset_in(used_ext, VAR_NUM(ZEND_OP1(opline).var)) &&
- VAR_SOURCE(opline->op1)->opcode == ZEND_BOOL_NOT) {
- /* T = BOOL_NOT(X) + JMPZ(T) -> NOP, JMPNZ(X) */
- zend_op *src = VAR_SOURCE(opline->op1);
-
- COPY_NODE(opline->op1, src->op1);
-
- switch (opline->opcode) {
- case ZEND_BOOL:
- /* T = BOOL_NOT(X) + BOOL(T) -> NOP, BOOL_NOT(X) */
- opline->opcode = ZEND_BOOL_NOT;
- break;
- case ZEND_BOOL_NOT:
- /* T = BOOL_NOT(X) + BOOL_BOOL(T) -> NOP, BOOL(X) */
- opline->opcode = ZEND_BOOL;
- break;
- case ZEND_JMPZ:
- /* T = BOOL_NOT(X) + JMPZ(T,L) -> NOP, JMPNZ(X,L) */
- opline->opcode = ZEND_JMPNZ;
+ case ZEND_CASE:
+ if (opline->op1_type & (IS_TMP_VAR|IS_VAR)) {
+ /* CASE variable will be deleted later by FREE, so we can't optimize it */
+ Tsource[VAR_NUM(opline->op1.var)] = NULL;
break;
- case ZEND_JMPNZ:
- /* T = BOOL_NOT(X) + JMPNZ(T,L) -> NOP, JMPZ(X,L) */
- opline->opcode = ZEND_JMPZ;
+ }
+ if (opline->op1_type == IS_CONST &&
+ opline->op2_type == IS_CONST) {
break;
- case ZEND_JMPZNZ:
- {
- /* T = BOOL_NOT(X) + JMPZNZ(T,L1,L2) -> NOP, JMPZNZ(X,L2,L1) */
- int op_t;
- zend_code_block *op_b;
-
- op_t = opline->extended_value;
- opline->extended_value = ZEND_OP2(opline).opline_num;
- ZEND_OP2(opline).opline_num = op_t;
-
- op_b = block->ext_to;
- block->ext_to = block->op2_to;
- block->op2_to = op_b;
+ }
+ /* break missing intentionally */
+
+ case ZEND_IS_EQUAL:
+ case ZEND_IS_NOT_EQUAL:
+ if (opline->op1_type == IS_CONST &&
+ opline->op2_type == IS_CONST) {
+ goto optimize_constant_binary_op;
+ }
+ /* IS_EQ(TRUE, X) => BOOL(X)
+ * IS_EQ(FALSE, X) => BOOL_NOT(X)
+ * IS_NOT_EQ(TRUE, X) => BOOL_NOT(X)
+ * IS_NOT_EQ(FALSE, X) => BOOL(X)
+ * CASE(TRUE, X) => BOOL(X)
+ * CASE(FALSE, X) => BOOL_NOT(X)
+ */
+ if (opline->op1_type == IS_CONST &&
+ (Z_TYPE(ZEND_OP1_LITERAL(opline)) == IS_FALSE ||
+ Z_TYPE(ZEND_OP1_LITERAL(opline)) == IS_TRUE)) {
+ /* Optimization of comparison with "null" is not safe,
+ * because ("0" == null) is not equal to !("0")
+ */
+ opline->opcode =
+ ((opline->opcode != ZEND_IS_NOT_EQUAL) == ((Z_TYPE(ZEND_OP1_LITERAL(opline))) == IS_TRUE)) ?
+ ZEND_BOOL : ZEND_BOOL_NOT;
+ COPY_NODE(opline->op1, opline->op2);
+ SET_UNUSED(opline->op2);
+ goto optimize_bool;
+ } else if (opline->op2_type == IS_CONST &&
+ (Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_FALSE ||
+ Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_TRUE)) {
+ /* Optimization of comparison with "null" is not safe,
+ * because ("0" == null) is not equal to !("0")
+ */
+ opline->opcode =
+ ((opline->opcode != ZEND_IS_NOT_EQUAL) == ((Z_TYPE(ZEND_OP2_LITERAL(opline))) == IS_TRUE)) ?
+ ZEND_BOOL : ZEND_BOOL_NOT;
+ SET_UNUSED(opline->op2);
+ goto optimize_bool;
}
break;
- }
- VAR_UNSET(opline->op1);
- MAKE_NOP(src);
- continue;
- } else
+ case ZEND_BOOL:
+ case ZEND_BOOL_NOT:
+ optimize_bool:
+ if (opline->op1_type == IS_CONST) {
+ goto optimize_const_unary_op;
+ }
+ if (opline->op1_type == IS_TMP_VAR &&
+ !zend_bitset_in(used_ext, VAR_NUM(opline->op1.var))) {
+ src = VAR_SOURCE(opline->op1);
+ if (src) {
+ switch (src->opcode) {
+ case ZEND_BOOL_NOT:
+ /* T = BOOL_NOT(X) + BOOL(T) -> NOP, BOOL_NOT(X) */
+ VAR_SOURCE(opline->op1) = NULL;
+ COPY_NODE(opline->op1, src->op1);
+ opline->opcode = (opline->opcode == ZEND_BOOL) ? ZEND_BOOL_NOT : ZEND_BOOL;
+ MAKE_NOP(src);
+ goto optimize_bool;
+ case ZEND_BOOL:
+ /* T = BOOL(X) + BOOL(T) -> NOP, BOOL(X) */
+ VAR_SOURCE(opline->op1) = NULL;
+ COPY_NODE(opline->op1, src->op1);
+ MAKE_NOP(src);
+ goto optimize_bool;
+ case ZEND_IS_EQUAL:
+ if (opline->opcode == ZEND_BOOL_NOT) {
+ src->opcode = ZEND_IS_NOT_EQUAL;
+ }
+ COPY_NODE(src->result, opline->result);
+ SET_VAR_SOURCE(src);
+ MAKE_NOP(opline);
+ break;
+ case ZEND_IS_NOT_EQUAL:
+ if (opline->opcode == ZEND_BOOL_NOT) {
+ src->opcode = ZEND_IS_EQUAL;
+ }
+ COPY_NODE(src->result, opline->result);
+ SET_VAR_SOURCE(src);
+ MAKE_NOP(opline);
+ break;
+ case ZEND_IS_IDENTICAL:
+ if (opline->opcode == ZEND_BOOL_NOT) {
+ src->opcode = ZEND_IS_NOT_IDENTICAL;
+ }
+ COPY_NODE(src->result, opline->result);
+ SET_VAR_SOURCE(src);
+ MAKE_NOP(opline);
+ break;
+ case ZEND_IS_NOT_IDENTICAL:
+ if (opline->opcode == ZEND_BOOL_NOT) {
+ src->opcode = ZEND_IS_IDENTICAL;
+ }
+ COPY_NODE(src->result, opline->result);
+ SET_VAR_SOURCE(src);
+ MAKE_NOP(opline);
+ break;
+ case ZEND_IS_SMALLER:
+ if (opline->opcode == ZEND_BOOL_NOT) {
+ zend_uchar tmp_type;
+ uint32_t tmp;
+
+ src->opcode = ZEND_IS_SMALLER_OR_EQUAL;
+ tmp_type = src->op1_type;
+ src->op1_type = src->op2_type;
+ src->op2_type = tmp_type;
+ tmp = src->op1.num;
+ src->op1.num = src->op2.num;
+ src->op2.num = tmp;
+ }
+ COPY_NODE(src->result, opline->result);
+ SET_VAR_SOURCE(src);
+ MAKE_NOP(opline);
+ break;
+ case ZEND_IS_SMALLER_OR_EQUAL:
+ if (opline->opcode == ZEND_BOOL_NOT) {
+ zend_uchar tmp_type;
+ uint32_t tmp;
+
+ src->opcode = ZEND_IS_SMALLER;
+ tmp_type = src->op1_type;
+ src->op1_type = src->op2_type;
+ src->op2_type = tmp_type;
+ tmp = src->op1.num;
+ src->op1.num = src->op2.num;
+ src->op2.num = tmp;
+ }
+ COPY_NODE(src->result, opline->result);
+ SET_VAR_SOURCE(src);
+ MAKE_NOP(opline);
+ break;
+ case ZEND_ISSET_ISEMPTY_VAR:
+ case ZEND_ISSET_ISEMPTY_DIM_OBJ:
+ case ZEND_ISSET_ISEMPTY_PROP_OBJ:
+ case ZEND_ISSET_ISEMPTY_STATIC_PROP:
+ case ZEND_INSTANCEOF:
+ case ZEND_TYPE_CHECK:
+ case ZEND_DEFINED:
+ if (opline->opcode == ZEND_BOOL_NOT) {
+ break;
+ }
+ COPY_NODE(src->result, opline->result);
+ SET_VAR_SOURCE(src);
+ MAKE_NOP(opline);
+ break;
+ }
+ }
+ }
+ break;
+
+ case ZEND_JMPZ:
+ case ZEND_JMPNZ:
+ case ZEND_JMPZ_EX:
+ case ZEND_JMPNZ_EX:
+ case ZEND_JMPZNZ:
+ optimize_jmpznz:
+ if (opline->op1_type == IS_TMP_VAR &&
+ (!zend_bitset_in(used_ext, VAR_NUM(opline->op1.var)) ||
+ (opline->result_type == opline->op1_type &&
+ opline->result.var == opline->op1.var))) {
+ src = VAR_SOURCE(opline->op1);
+ if (src) {
+ if (src->opcode == ZEND_BOOL_NOT &&
+ opline->opcode != ZEND_JMPZ_EX &&
+ opline->opcode != ZEND_JMPNZ_EX) {
+ VAR_SOURCE(opline->op1) = NULL;
+ COPY_NODE(opline->op1, src->op1);
+ if (opline->opcode == ZEND_JMPZ) {
+ /* T = BOOL_NOT(X) + JMPZ(T) -> NOP, JMPNZ(X) */
+ opline->opcode = ZEND_JMPNZ;
+ } else if (opline->opcode == ZEND_JMPNZ) {
+ /* T = BOOL_NOT(X) + JMPNZ(T) -> NOP, JMPZ(X) */
+ opline->opcode = ZEND_JMPZ;
#if 0
- /* T = BOOL_NOT(X) + T = JMPZ_EX(T, X) -> T = BOOL_NOT(X), JMPNZ(X) */
- if(0 && (opline->opcode == ZEND_JMPZ_EX ||
- opline->opcode == ZEND_JMPNZ_EX) &&
- ZEND_OP1_TYPE(opline) == IS_TMP_VAR &&
- VAR_SOURCE(opline->op1) != NULL &&
- VAR_SOURCE(opline->op1)->opcode == ZEND_BOOL_NOT &&
- ZEND_OP1(opline).var == ZEND_RESULT(opline).var
- ) {
- zend_op *src = VAR_SOURCE(opline->op1);
- if(opline->opcode == ZEND_JMPZ_EX) {
- opline->opcode = ZEND_JMPNZ;
- } else {
- opline->opcode = ZEND_JMPZ;
- }
- COPY_NODE(opline->op1, src->op1);
- SET_UNUSED(opline->result);
- continue;
- } else
+ } else if (opline->opcode == ZEND_JMPZ_EX) {
+ /* T = BOOL_NOT(X) + JMPZ_EX(T) -> NOP, JMPNZ_EX(X) */
+ opline->opcode = ZEND_JMPNZ_EX;
+ } else if (opline->opcode == ZEND_JMPNZ_EX) {
+ /* T = BOOL_NOT(X) + JMPNZ_EX(T) -> NOP, JMPZ_EX(X) */
+ opline->opcode = ZEND_JMPZ;
#endif
- /* T = BOOL(X) + JMPZ(T) -> NOP, JMPZ(X) */
- if ((opline->opcode == ZEND_BOOL ||
- opline->opcode == ZEND_BOOL_NOT ||
- opline->opcode == ZEND_JMPZ ||
- opline->opcode == ZEND_JMPZ_EX ||
- opline->opcode == ZEND_JMPNZ_EX ||
- opline->opcode == ZEND_JMPNZ ||
- opline->opcode == ZEND_JMPZNZ) &&
- (ZEND_OP1_TYPE(opline) & (IS_TMP_VAR|IS_VAR)) &&
- VAR_SOURCE(opline->op1) != NULL &&
- (!zend_bitset_in(used_ext, VAR_NUM(ZEND_OP1(opline).var)) ||
- ((ZEND_RESULT_TYPE(opline) & (IS_TMP_VAR|IS_VAR)) &&
- ZEND_RESULT(opline).var == ZEND_OP1(opline).var)) &&
- (VAR_SOURCE(opline->op1)->opcode == ZEND_BOOL ||
- VAR_SOURCE(opline->op1)->opcode == ZEND_QM_ASSIGN)) {
- zend_op *src = VAR_SOURCE(opline->op1);
- COPY_NODE(opline->op1, src->op1);
-
- VAR_UNSET(opline->op1);
- MAKE_NOP(src);
- continue;
- } else if (last_op && opline->opcode == ZEND_ECHO &&
- last_op->opcode == ZEND_ECHO &&
- ZEND_OP1_TYPE(opline) == IS_CONST &&
- Z_TYPE(ZEND_OP1_LITERAL(opline)) != IS_DOUBLE &&
- ZEND_OP1_TYPE(last_op) == IS_CONST &&
- Z_TYPE(ZEND_OP1_LITERAL(last_op)) != IS_DOUBLE) {
- /* compress consecutive ECHO's.
- * Float to string conversion may be affected by current
- * locale setting.
- */
- int l, old_len;
-
- if (Z_TYPE(ZEND_OP1_LITERAL(opline)) != IS_STRING) {
- convert_to_string_safe(&ZEND_OP1_LITERAL(opline));
- }
- if (Z_TYPE(ZEND_OP1_LITERAL(last_op)) != IS_STRING) {
- convert_to_string_safe(&ZEND_OP1_LITERAL(last_op));
- }
- old_len = Z_STRLEN(ZEND_OP1_LITERAL(last_op));
- l = old_len + Z_STRLEN(ZEND_OP1_LITERAL(opline));
- if (!Z_REFCOUNTED(ZEND_OP1_LITERAL(last_op))) {
- zend_string *tmp = zend_string_alloc(l, 0);
- memcpy(ZSTR_VAL(tmp), Z_STRVAL(ZEND_OP1_LITERAL(last_op)), old_len);
- Z_STR(ZEND_OP1_LITERAL(last_op)) = tmp;
- } else {
- Z_STR(ZEND_OP1_LITERAL(last_op)) = zend_string_extend(Z_STR(ZEND_OP1_LITERAL(last_op)), l, 0);
- }
- Z_TYPE_INFO(ZEND_OP1_LITERAL(last_op)) = IS_STRING_EX;
- memcpy(Z_STRVAL(ZEND_OP1_LITERAL(last_op)) + old_len, Z_STRVAL(ZEND_OP1_LITERAL(opline)), Z_STRLEN(ZEND_OP1_LITERAL(opline)));
- Z_STRVAL(ZEND_OP1_LITERAL(last_op))[l] = '\0';
- zval_dtor(&ZEND_OP1_LITERAL(opline));
- ZVAL_STR(&ZEND_OP1_LITERAL(opline), zend_new_interned_string(Z_STR(ZEND_OP1_LITERAL(last_op))));
- ZVAL_NULL(&ZEND_OP1_LITERAL(last_op));
- MAKE_NOP(last_op);
- } else if ((opline->opcode == ZEND_CONCAT) &&
- ZEND_OP2_TYPE(opline) == IS_CONST &&
- ZEND_OP1_TYPE(opline) == IS_TMP_VAR &&
- VAR_SOURCE(opline->op1) &&
- (VAR_SOURCE(opline->op1)->opcode == ZEND_CONCAT ||
- VAR_SOURCE(opline->op1)->opcode == ZEND_FAST_CONCAT) &&
- ZEND_OP2_TYPE(VAR_SOURCE(opline->op1)) == IS_CONST &&
- ZEND_RESULT(VAR_SOURCE(opline->op1)).var == ZEND_OP1(opline).var) {
- /* compress consecutive CONCAT/ADD_STRING/ADD_CHARs */
- zend_op *src = VAR_SOURCE(opline->op1);
- int l, old_len;
-
- if (Z_TYPE(ZEND_OP2_LITERAL(opline)) != IS_STRING) {
- convert_to_string_safe(&ZEND_OP2_LITERAL(opline));
- }
- if (Z_TYPE(ZEND_OP2_LITERAL(src)) != IS_STRING) {
- convert_to_string_safe(&ZEND_OP2_LITERAL(src));
- }
+ } else {
+ /* T = BOOL_NOT(X) + JMPZNZ(T,L1,L2) -> NOP, JMPZNZ(X,L2,L1) */
+ uint32_t tmp;
+
+ ZEND_ASSERT(opline->opcode == ZEND_JMPZNZ);
+ tmp = block->successors[0];
+ block->successors[0] = block->successors[1];
+ block->successors[1] = tmp;
+ }
+ MAKE_NOP(src);
+ goto optimize_jmpznz;
+ } else if (src->opcode == ZEND_BOOL ||
+ src->opcode == ZEND_QM_ASSIGN) {
+ VAR_SOURCE(opline->op1) = NULL;
+ COPY_NODE(opline->op1, src->op1);
+ MAKE_NOP(src);
+ goto optimize_jmpznz;
+ }
+ }
+ }
+ break;
- VAR_UNSET(opline->op1);
- COPY_NODE(opline->op1, src->op1);
- old_len = Z_STRLEN(ZEND_OP2_LITERAL(src));
- l = old_len + Z_STRLEN(ZEND_OP2_LITERAL(opline));
- if (!Z_REFCOUNTED(ZEND_OP2_LITERAL(src))) {
- zend_string *tmp = zend_string_alloc(l, 0);
- memcpy(ZSTR_VAL(tmp), Z_STRVAL(ZEND_OP2_LITERAL(src)), old_len);
- Z_STR(ZEND_OP2_LITERAL(last_op)) = tmp;
- } else {
- Z_STR(ZEND_OP2_LITERAL(src)) = zend_string_extend(Z_STR(ZEND_OP2_LITERAL(src)), l, 0);
- }
- Z_TYPE_INFO(ZEND_OP2_LITERAL(last_op)) = IS_STRING_EX;
- memcpy(Z_STRVAL(ZEND_OP2_LITERAL(src)) + old_len, Z_STRVAL(ZEND_OP2_LITERAL(opline)), Z_STRLEN(ZEND_OP2_LITERAL(opline)));
- Z_STRVAL(ZEND_OP2_LITERAL(src))[l] = '\0';
- zend_string_release(Z_STR(ZEND_OP2_LITERAL(opline)));
- ZVAL_STR(&ZEND_OP2_LITERAL(opline), zend_new_interned_string(Z_STR(ZEND_OP2_LITERAL(src))));
- ZVAL_NULL(&ZEND_OP2_LITERAL(src));
- MAKE_NOP(src);
- } else if ((opline->opcode == ZEND_ADD ||
- opline->opcode == ZEND_SUB ||
- opline->opcode == ZEND_MUL ||
- opline->opcode == ZEND_DIV ||
- opline->opcode == ZEND_MOD ||
- opline->opcode == ZEND_SL ||
- opline->opcode == ZEND_SR ||
- opline->opcode == ZEND_CONCAT ||
- opline->opcode == ZEND_FAST_CONCAT ||
- opline->opcode == ZEND_IS_EQUAL ||
- opline->opcode == ZEND_IS_NOT_EQUAL ||
- opline->opcode == ZEND_IS_SMALLER ||
- opline->opcode == ZEND_IS_SMALLER_OR_EQUAL ||
- opline->opcode == ZEND_IS_IDENTICAL ||
- opline->opcode == ZEND_IS_NOT_IDENTICAL ||
- opline->opcode == ZEND_BOOL_XOR ||
- opline->opcode == ZEND_BW_OR ||
- opline->opcode == ZEND_BW_AND ||
- opline->opcode == ZEND_BW_XOR) &&
- ZEND_OP1_TYPE(opline)==IS_CONST &&
- ZEND_OP2_TYPE(opline)==IS_CONST) {
- /* evaluate constant expressions */
- binary_op_type binary_op = get_binary_op(opline->opcode);
- zval result;
- int er;
-
- if ((opline->opcode == ZEND_DIV || opline->opcode == ZEND_MOD) &&
- zval_get_long(&ZEND_OP2_LITERAL(opline)) == 0) {
- if (RESULT_USED(opline)) {
- SET_VAR_SOURCE(opline);
+ case ZEND_CONCAT:
+ case ZEND_FAST_CONCAT:
+ if (opline->op1_type == IS_CONST &&
+ opline->op2_type == IS_CONST) {
+ goto optimize_constant_binary_op;
}
- opline++;
- continue;
- } else if ((opline->opcode == ZEND_SL || opline->opcode == ZEND_SR) &&
- zval_get_long(&ZEND_OP2_LITERAL(opline)) < 0) {
- if (RESULT_USED(opline)) {
- SET_VAR_SOURCE(opline);
+
+ if (opline->op2_type == IS_CONST &&
+ opline->op1_type == IS_TMP_VAR) {
+
+ src = VAR_SOURCE(opline->op1);
+ if (src &&
+ (src->opcode == ZEND_CONCAT ||
+ src->opcode == ZEND_FAST_CONCAT) &&
+ src->op2_type == IS_CONST) {
+ /* compress consecutive CONCATs */
+ int l, old_len;
+
+ if (Z_TYPE(ZEND_OP2_LITERAL(opline)) != IS_STRING) {
+ convert_to_string_safe(&ZEND_OP2_LITERAL(opline));
+ }
+ if (Z_TYPE(ZEND_OP2_LITERAL(src)) != IS_STRING) {
+ convert_to_string_safe(&ZEND_OP2_LITERAL(src));
+ }
+
+ VAR_SOURCE(opline->op1) = NULL;
+ COPY_NODE(opline->op1, src->op1);
+ old_len = Z_STRLEN(ZEND_OP2_LITERAL(src));
+ l = old_len + Z_STRLEN(ZEND_OP2_LITERAL(opline));
+ if (!Z_REFCOUNTED(ZEND_OP2_LITERAL(src))) {
+ zend_string *tmp = zend_string_alloc(l, 0);
+ memcpy(ZSTR_VAL(tmp), Z_STRVAL(ZEND_OP2_LITERAL(src)), old_len);
+ Z_STR(ZEND_OP2_LITERAL(src)) = tmp;
+ } else {
+ Z_STR(ZEND_OP2_LITERAL(src)) = zend_string_extend(Z_STR(ZEND_OP2_LITERAL(src)), l, 0);
+ }
+ Z_TYPE_INFO(ZEND_OP2_LITERAL(src)) = IS_STRING_EX;
+ memcpy(Z_STRVAL(ZEND_OP2_LITERAL(src)) + old_len, Z_STRVAL(ZEND_OP2_LITERAL(opline)), Z_STRLEN(ZEND_OP2_LITERAL(opline)));
+ Z_STRVAL(ZEND_OP2_LITERAL(src))[l] = '\0';
+ zend_string_release(Z_STR(ZEND_OP2_LITERAL(opline)));
+ ZVAL_STR(&ZEND_OP2_LITERAL(opline), zend_new_interned_string(Z_STR(ZEND_OP2_LITERAL(src))));
+ ZVAL_NULL(&ZEND_OP2_LITERAL(src));
+ MAKE_NOP(src);
+ }
}
- opline++;
- continue;
- }
- er = EG(error_reporting);
- EG(error_reporting) = 0;
- if (binary_op(&result, &ZEND_OP1_LITERAL(opline), &ZEND_OP2_LITERAL(opline)) == SUCCESS) {
- literal_dtor(&ZEND_OP1_LITERAL(opline));
- literal_dtor(&ZEND_OP2_LITERAL(opline));
- opline->opcode = ZEND_QM_ASSIGN;
- SET_UNUSED(opline->op2);
- zend_optimizer_update_op1_const(op_array, opline, &result);
- }
- EG(error_reporting) = er;
- } else if ((opline->opcode == ZEND_BOOL ||
- opline->opcode == ZEND_BOOL_NOT ||
- opline->opcode == ZEND_BW_NOT) && ZEND_OP1_TYPE(opline) == IS_CONST) {
- /* evaluate constant unary ops */
- unary_op_type unary_op = get_unary_op(opline->opcode);
- zval result;
-
- if (unary_op) {
- unary_op(&result, &ZEND_OP1_LITERAL(opline));
- literal_dtor(&ZEND_OP1_LITERAL(opline));
- } else {
- /* BOOL */
- result = ZEND_OP1_LITERAL(opline);
- convert_to_boolean(&result);
- ZVAL_NULL(&ZEND_OP1_LITERAL(opline));
- }
- opline->opcode = ZEND_QM_ASSIGN;
- zend_optimizer_update_op1_const(op_array, opline, &result);
- } else if ((opline->opcode == ZEND_RETURN || opline->opcode == ZEND_EXIT) &&
- (ZEND_OP1_TYPE(opline) & (IS_TMP_VAR|IS_VAR)) &&
- VAR_SOURCE(opline->op1) &&
- VAR_SOURCE(opline->op1)->opcode == ZEND_QM_ASSIGN) {
- /* T = QM_ASSIGN(X), RETURN(T) to RETURN(X) */
- zend_op *src = VAR_SOURCE(opline->op1);
- VAR_UNSET(opline->op1);
- COPY_NODE(opline->op1, src->op1);
- MAKE_NOP(src);
- } else if (opline->opcode == ZEND_CONCAT || opline->opcode == ZEND_FAST_CONCAT) {
- if ((ZEND_OP1_TYPE(opline) & (IS_TMP_VAR|IS_VAR)) &&
- VAR_SOURCE(opline->op1) &&
- VAR_SOURCE(opline->op1)->opcode == ZEND_CAST &&
- VAR_SOURCE(opline->op1)->extended_value == IS_STRING) {
- /* convert T1 = CAST(STRING, X), T2 = CONCAT(T1, Y) to T2 = CONCAT(X,Y) */
- zend_op *src = VAR_SOURCE(opline->op1);
- VAR_UNSET(opline->op1);
- COPY_NODE(opline->op1, src->op1);
- MAKE_NOP(src);
- }
- if ((ZEND_OP2_TYPE(opline) & (IS_TMP_VAR|IS_VAR)) &&
- VAR_SOURCE(opline->op2) &&
- VAR_SOURCE(opline->op2)->opcode == ZEND_CAST &&
- VAR_SOURCE(opline->op2)->extended_value == IS_STRING) {
- /* convert T1 = CAST(STRING, X), T2 = CONCAT(Y, T1) to T2 = CONCAT(Y,X) */
- zend_op *src = VAR_SOURCE(opline->op2);
- VAR_UNSET(opline->op2);
- COPY_NODE(opline->op2, src->op1);
- MAKE_NOP(src);
- }
- if (ZEND_OP1_TYPE(opline) == IS_CONST &&
- Z_TYPE(ZEND_OP1_LITERAL(opline)) == IS_STRING &&
- Z_STRLEN(ZEND_OP1_LITERAL(opline)) == 0) {
- /* convert CONCAT('', X) => CAST(STRING, X) */
- literal_dtor(&ZEND_OP1_LITERAL(opline));
- opline->opcode = ZEND_CAST;
- opline->extended_value = IS_STRING;
- COPY_NODE(opline->op1, opline->op2);
- opline->op2_type = IS_UNUSED;
- opline->op2.var = 0;
- } else if (ZEND_OP2_TYPE(opline) == IS_CONST &&
+
+ if (opline->op1_type & (IS_TMP_VAR|IS_VAR)) {
+ src = VAR_SOURCE(opline->op1);
+ if (src &&
+ src->opcode == ZEND_CAST &&
+ src->extended_value == IS_STRING) {
+ /* convert T1 = CAST(STRING, X), T2 = CONCAT(T1, Y) to T2 = CONCAT(X,Y) */
+ VAR_SOURCE(opline->op1) = NULL;
+ COPY_NODE(opline->op1, src->op1);
+ MAKE_NOP(src);
+ }
+ }
+ if (opline->op2_type & (IS_TMP_VAR|IS_VAR)) {
+ src = VAR_SOURCE(opline->op2);
+ if (src &&
+ src->opcode == ZEND_CAST &&
+ src->extended_value == IS_STRING) {
+ /* convert T1 = CAST(STRING, X), T2 = CONCAT(Y, T1) to T2 = CONCAT(Y,X) */
+ zend_op *src = VAR_SOURCE(opline->op2);
+ VAR_SOURCE(opline->op2) = NULL;
+ COPY_NODE(opline->op2, src->op1);
+ MAKE_NOP(src);
+ }
+ }
+ if (opline->op1_type == IS_CONST &&
+ Z_TYPE(ZEND_OP1_LITERAL(opline)) == IS_STRING &&
+ Z_STRLEN(ZEND_OP1_LITERAL(opline)) == 0) {
+ /* convert CONCAT('', X) => CAST(STRING, X) */
+ literal_dtor(&ZEND_OP1_LITERAL(opline));
+ opline->opcode = ZEND_CAST;
+ opline->extended_value = IS_STRING;
+ COPY_NODE(opline->op1, opline->op2);
+ opline->op2_type = IS_UNUSED;
+ opline->op2.var = 0;
+ } else if (opline->op2_type == IS_CONST &&
Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_STRING &&
Z_STRLEN(ZEND_OP2_LITERAL(opline)) == 0) {
- /* convert CONCAT(X, '') => CAST(STRING, X) */
- literal_dtor(&ZEND_OP2_LITERAL(opline));
- opline->opcode = ZEND_CAST;
- opline->extended_value = IS_STRING;
- opline->op2_type = IS_UNUSED;
- opline->op2.var = 0;
- } else if (opline->opcode == ZEND_CONCAT &&
- (opline->op1_type == IS_CONST ||
- (opline->op1_type == IS_TMP_VAR &&
- VAR_SOURCE(opline->op1) &&
- (VAR_SOURCE(opline->op1)->opcode == ZEND_FAST_CONCAT ||
- VAR_SOURCE(opline->op1)->opcode == ZEND_ROPE_END ||
- VAR_SOURCE(opline->op1)->opcode == ZEND_FETCH_CONSTANT))) &&
- (opline->op2_type == IS_CONST ||
- (opline->op2_type == IS_TMP_VAR &&
- VAR_SOURCE(opline->op2) &&
- (VAR_SOURCE(opline->op2)->opcode == ZEND_FAST_CONCAT ||
- VAR_SOURCE(opline->op2)->opcode == ZEND_ROPE_END ||
- VAR_SOURCE(opline->op2)->opcode == ZEND_FETCH_CONSTANT)))) {
- opline->opcode = ZEND_FAST_CONCAT;
- }
- } else if (opline->opcode == ZEND_QM_ASSIGN &&
- ZEND_OP1_TYPE(opline) == ZEND_RESULT_TYPE(opline) &&
- ZEND_OP1(opline).var == ZEND_RESULT(opline).var) {
- /* strip T = QM_ASSIGN(T) */
- MAKE_NOP(opline);
- } else if (opline->opcode == ZEND_BOOL &&
- ZEND_OP1_TYPE(opline) == IS_TMP_VAR &&
- VAR_SOURCE(opline->op1) &&
- (VAR_SOURCE(opline->op1)->opcode == ZEND_IS_EQUAL ||
- VAR_SOURCE(opline->op1)->opcode == ZEND_IS_NOT_EQUAL ||
- VAR_SOURCE(opline->op1)->opcode == ZEND_IS_SMALLER ||
- VAR_SOURCE(opline->op1)->opcode == ZEND_IS_SMALLER_OR_EQUAL ||
- VAR_SOURCE(opline->op1)->opcode == ZEND_BOOL ||
- VAR_SOURCE(opline->op1)->opcode == ZEND_IS_IDENTICAL ||
- VAR_SOURCE(opline->op1)->opcode == ZEND_IS_NOT_IDENTICAL ||
- VAR_SOURCE(opline->op1)->opcode == ZEND_ISSET_ISEMPTY_VAR ||
- VAR_SOURCE(opline->op1)->opcode == ZEND_ISSET_ISEMPTY_DIM_OBJ) &&
- !zend_bitset_in(used_ext, VAR_NUM(ZEND_OP1(opline).var))) {
- /* T = IS_SMALLER(X, Y), T1 = BOOL(T) => T = IS_SMALLER(X, Y), T1 = QM_ASSIGN(T) */
- zend_op *src = VAR_SOURCE(opline->op1);
- COPY_NODE(src->result, opline->result);
- SET_VAR_SOURCE(src);
- MAKE_NOP(opline);
+ /* convert CONCAT(X, '') => CAST(STRING, X) */
+ literal_dtor(&ZEND_OP2_LITERAL(opline));
+ opline->opcode = ZEND_CAST;
+ opline->extended_value = IS_STRING;
+ opline->op2_type = IS_UNUSED;
+ opline->op2.var = 0;
+ } else if (opline->opcode == ZEND_CONCAT &&
+ (opline->op1_type == IS_CONST ||
+ (opline->op1_type == IS_TMP_VAR &&
+ VAR_SOURCE(opline->op1) &&
+ (VAR_SOURCE(opline->op1)->opcode == ZEND_FAST_CONCAT ||
+ VAR_SOURCE(opline->op1)->opcode == ZEND_ROPE_END ||
+ VAR_SOURCE(opline->op1)->opcode == ZEND_FETCH_CONSTANT ||
+ VAR_SOURCE(opline->op1)->opcode == ZEND_FETCH_CLASS_CONSTANT))) &&
+ (opline->op2_type == IS_CONST ||
+ (opline->op2_type == IS_TMP_VAR &&
+ VAR_SOURCE(opline->op2) &&
+ (VAR_SOURCE(opline->op2)->opcode == ZEND_FAST_CONCAT ||
+ VAR_SOURCE(opline->op2)->opcode == ZEND_ROPE_END ||
+ VAR_SOURCE(opline->op2)->opcode == ZEND_FETCH_CONSTANT ||
+ VAR_SOURCE(opline->op2)->opcode == ZEND_FETCH_CLASS_CONSTANT)))) {
+ opline->opcode = ZEND_FAST_CONCAT;
+ }
+ break;
+
+ case ZEND_ADD:
+ case ZEND_SUB:
+ case ZEND_MUL:
+ case ZEND_DIV:
+ case ZEND_MOD:
+ case ZEND_SL:
+ case ZEND_SR:
+ case ZEND_IS_SMALLER:
+ case ZEND_IS_SMALLER_OR_EQUAL:
+ case ZEND_IS_IDENTICAL:
+ case ZEND_IS_NOT_IDENTICAL:
+ case ZEND_BOOL_XOR:
+ case ZEND_BW_OR:
+ case ZEND_BW_AND:
+ case ZEND_BW_XOR:
+ if (opline->op1_type == IS_CONST &&
+ opline->op2_type == IS_CONST) {
+ /* evaluate constant expressions */
+ binary_op_type binary_op;
+ zval result;
+ int er;
+
+optimize_constant_binary_op:
+ binary_op = get_binary_op(opline->opcode);
+ if ((opline->opcode == ZEND_DIV || opline->opcode == ZEND_MOD) &&
+ zval_get_long(&ZEND_OP2_LITERAL(opline)) == 0) {
+ SET_VAR_SOURCE(opline);
+ opline++;
+ continue;
+ } else if ((opline->opcode == ZEND_SL || opline->opcode == ZEND_SR) &&
+ zval_get_long(&ZEND_OP2_LITERAL(opline)) < 0) {
+ SET_VAR_SOURCE(opline);
+ opline++;
+ continue;
+ } else if (zend_binary_op_produces_numeric_string_error(opline->opcode, &ZEND_OP1_LITERAL(opline), &ZEND_OP2_LITERAL(opline))) {
+ SET_VAR_SOURCE(opline);
+ opline++;
+ continue;
+ }
+ er = EG(error_reporting);
+ EG(error_reporting) = 0;
+ if (binary_op(&result, &ZEND_OP1_LITERAL(opline), &ZEND_OP2_LITERAL(opline)) == SUCCESS) {
+ literal_dtor(&ZEND_OP1_LITERAL(opline));
+ literal_dtor(&ZEND_OP2_LITERAL(opline));
+ opline->opcode = ZEND_QM_ASSIGN;
+ SET_UNUSED(opline->op2);
+ zend_optimizer_update_op1_const(op_array, opline, &result);
+ }
+ EG(error_reporting) = er;
+ }
+ break;
+
+ case ZEND_BW_NOT:
+ if (opline->op1_type == IS_CONST) {
+ /* evaluate constant unary ops */
+ unary_op_type unary_op;
+ zval result;
+
+optimize_const_unary_op:
+ unary_op = get_unary_op(opline->opcode);
+ if (unary_op) {
+ unary_op(&result, &ZEND_OP1_LITERAL(opline));
+ literal_dtor(&ZEND_OP1_LITERAL(opline));
+ } else {
+ /* BOOL */
+ result = ZEND_OP1_LITERAL(opline);
+ convert_to_boolean(&result);
+ ZVAL_NULL(&ZEND_OP1_LITERAL(opline));
+ }
+ opline->opcode = ZEND_QM_ASSIGN;
+ zend_optimizer_update_op1_const(op_array, opline, &result);
+ }
+ break;
+
+ case ZEND_RETURN:
+ case ZEND_EXIT:
+ if (opline->op1_type & (IS_TMP_VAR|IS_VAR)) {
+ src = VAR_SOURCE(opline->op1);
+ if (src && src->opcode == ZEND_QM_ASSIGN) {
+ /* T = QM_ASSIGN(X), RETURN(T) to NOP, RETURN(X) */
+ VAR_SOURCE(opline->op1) = NULL;
+ COPY_NODE(opline->op1, src->op1);
+ MAKE_NOP(src);
+ }
+ }
+ break;
+
+ case ZEND_QM_ASSIGN:
+ if (opline->op1_type == opline->result_type &&
+ opline->op1.var == opline->result.var) {
+ /* strip T = QM_ASSIGN(T) */
+ MAKE_NOP(opline);
+ }
+ break;
}
+
/* get variable source */
- if (RESULT_USED(opline)) {
+ if (opline->result_type & (IS_VAR|IS_TMP_VAR)) {
SET_VAR_SOURCE(opline);
}
- if (opline->opcode != ZEND_NOP) {
- last_op = opline;
- }
opline++;
}
- strip_nop(block, ctx);
+ strip_nops(op_array, block);
}
/* Rebuild plain (optimized) op_array from CFG */
static void assemble_code_blocks(zend_cfg *cfg, zend_op_array *op_array)
{
- zend_code_block *blocks = cfg->blocks;
- zend_op *new_opcodes = emalloc(op_array->last * sizeof(zend_op));
- zend_op *opline = new_opcodes;
- zend_code_block *cur_block = blocks;
+ zend_basic_block *blocks = cfg->blocks;
+ zend_basic_block *end = blocks + cfg->blocks_count;
+ zend_basic_block *b;
+ zend_op *new_opcodes;
+ zend_op *opline;
+ uint32_t len = 0;
+ int n;
- /* Copy code of reachable blocks into a single buffer */
- while (cur_block) {
- if (cur_block->access) {
- memcpy(opline, cur_block->start_opline, cur_block->len * sizeof(zend_op));
- cur_block->start_opline = opline;
- opline += cur_block->len;
- if ((opline - 1)->opcode == ZEND_JMP) {
- zend_code_block *next;
- next = cur_block->next;
- while (next && !next->access) {
- next = next->next;
+ for (b = blocks; b < end; b++) {
+ if (b->len == 0) {
+ continue;
+ }
+ if (b->flags & ZEND_BB_REACHABLE) {
+ opline = op_array->opcodes + b->start + b->len - 1;
+ if (opline->opcode == ZEND_JMP) {
+ zend_basic_block *next = b + 1;
+
+ while (next < end && !(next->flags & ZEND_BB_REACHABLE)) {
+ next++;
}
- if (next && next == cur_block->op1_to) {
+ if (next < end && next == blocks + b->successors[0]) {
/* JMP to the next block - strip it */
- cur_block->follow_to = cur_block->op1_to;
- cur_block->op1_to = NULL;
- MAKE_NOP((opline - 1));
- opline--;
- cur_block->len--;
+ MAKE_NOP(opline);
+ b->len--;
}
+ } else if (b->len == 1 && opline->opcode == ZEND_NOP) {
+ /* skip empty block */
+ b->len--;
}
+ len += b->len;
} else {
/* this block will not be used, delete all constants there */
- zend_op *_opl;
- zend_op *end = cur_block->start_opline + cur_block->len;
- for (_opl = cur_block->start_opline; _opl && _opl < end; _opl++) {
- if (ZEND_OP1_TYPE(_opl) == IS_CONST) {
- literal_dtor(&ZEND_OP1_LITERAL(_opl));
+ zend_op *op = op_array->opcodes + b->start;
+ zend_op *end = op + b->len;
+ for (; op < end; op++) {
+ if (ZEND_OP1_TYPE(op) == IS_CONST) {
+ literal_dtor(&ZEND_OP1_LITERAL(op));
}
- if (ZEND_OP2_TYPE(_opl) == IS_CONST) {
- literal_dtor(&ZEND_OP2_LITERAL(_opl));
+ if (ZEND_OP2_TYPE(op) == IS_CONST) {
+ literal_dtor(&ZEND_OP2_LITERAL(op));
}
}
}
- cur_block = cur_block->next;
}
- op_array->last = opline-new_opcodes;
+ new_opcodes = emalloc(len * sizeof(zend_op));
+ opline = new_opcodes;
- /* adjust exception jump targets */
+ /* Copy code of reachable blocks into a single buffer */
+ for (b = blocks; b < end; b++) {
+ if (b->flags & ZEND_BB_REACHABLE) {
+ memcpy(opline, op_array->opcodes + b->start, b->len * sizeof(zend_op));
+ b->start = opline - new_opcodes;
+ opline += b->len;
+ }
+ }
+
+ /* adjust jump targets */
+ efree(op_array->opcodes);
+ op_array->opcodes = new_opcodes;
+ op_array->last = len;
+
+ for (b = blocks; b < end; b++) {
+ if (!(b->flags & ZEND_BB_REACHABLE) || b->len == 0) {
+ continue;
+ }
+ opline = op_array->opcodes + b->start + b->len - 1;
+ switch (opline->opcode) {
+ case ZEND_FAST_CALL:
+ case ZEND_JMP:
+ ZEND_SET_OP_JMP_ADDR(opline, opline->op1, new_opcodes + blocks[b->successors[0]].start);
+ break;
+ case ZEND_JMPZNZ:
+ opline->extended_value = ZEND_OPLINE_TO_OFFSET(opline, new_opcodes + blocks[b->successors[1]].start);
+ /* break missing intentionally */
+ case ZEND_JMPZ:
+ case ZEND_JMPNZ:
+ case ZEND_JMPZ_EX:
+ case ZEND_JMPNZ_EX:
+ case ZEND_FE_RESET_R:
+ case ZEND_FE_RESET_RW:
+ case ZEND_JMP_SET:
+ case ZEND_COALESCE:
+ case ZEND_ASSERT_CHECK:
+ ZEND_SET_OP_JMP_ADDR(opline, opline->op2, new_opcodes + blocks[b->successors[0]].start);
+ break;
+ case ZEND_CATCH:
+ if (!opline->result.var) {
+ opline->extended_value = ZEND_OPLINE_TO_OFFSET(opline, new_opcodes + blocks[b->successors[0]].start);
+ }
+ break;
+ case ZEND_DECLARE_ANON_CLASS:
+ case ZEND_DECLARE_ANON_INHERITED_CLASS:
+ case ZEND_FE_FETCH_R:
+ case ZEND_FE_FETCH_RW:
+ opline->extended_value = ZEND_OPLINE_TO_OFFSET(opline, new_opcodes + blocks[b->successors[0]].start);
+ break;
+ }
+ }
+
+ /* adjust exception jump targets & remove unused try_catch_array entries */
if (op_array->last_try_catch) {
int i, j;
+ uint32_t *map;
+ ALLOCA_FLAG(use_heap);
+
+ map = (uint32_t *)do_alloca(sizeof(uint32_t) * op_array->last_try_catch, use_heap);
for (i = 0, j = 0; i< op_array->last_try_catch; i++) {
- if (cfg->try[i]->access) {
- op_array->try_catch_array[j].try_op = cfg->try[i]->start_opline - new_opcodes;
- op_array->try_catch_array[j].catch_op = cfg->catch[i]->start_opline - new_opcodes;
+ if (blocks[cfg->map[op_array->try_catch_array[i].try_op]].flags & ZEND_BB_REACHABLE) {
+ map[i] = j;
+ op_array->try_catch_array[j].try_op = blocks[cfg->map[op_array->try_catch_array[i].try_op]].start;
+ if (op_array->try_catch_array[i].catch_op) {
+ op_array->try_catch_array[j].catch_op = blocks[cfg->map[op_array->try_catch_array[i].catch_op]].start;
+ } else {
+ op_array->try_catch_array[j].catch_op = 0;
+ }
+ if (op_array->try_catch_array[i].finally_op) {
+ op_array->try_catch_array[j].finally_op = blocks[cfg->map[op_array->try_catch_array[i].finally_op]].start;
+ } else {
+ op_array->try_catch_array[j].finally_op = 0;
+ }
+ if (!op_array->try_catch_array[i].finally_end) {
+ op_array->try_catch_array[j].finally_end = 0;
+ } else {
+ op_array->try_catch_array[j].finally_end = blocks[cfg->map[op_array->try_catch_array[i].finally_end]].start;
+ }
j++;
}
}
- op_array->last_try_catch = j;
- }
-
- /* adjust loop jump targets */
- if (op_array->last_brk_cont) {
- int i;
- for (i = 0; i< op_array->last_brk_cont; i++) {
- op_array->brk_cont_array[i].start = cfg->loop_start[i]->start_opline - new_opcodes;
- op_array->brk_cont_array[i].cont = cfg->loop_cont[i]->start_opline - new_opcodes;
- op_array->brk_cont_array[i].brk = cfg->loop_brk[i]->start_opline - new_opcodes;
+ if (i != j) {
+ op_array->last_try_catch = j;
+ if (op_array->fn_flags & ZEND_ACC_HAS_FINALLY_BLOCK) {
+ zend_op *opline = new_opcodes;
+ zend_op *end = opline + len;
+ while (opline < end) {
+ if (opline->opcode == ZEND_FAST_RET &&
+ opline->op2.num != (uint32_t)-1 &&
+ opline->op2.num < (uint32_t)j) {
+ opline->op2.num = map[opline->op2.num];
+ }
+ opline++;
+ }
+ }
}
+ free_alloca(map, use_heap);
}
- /* adjust jump targets */
- for (cur_block = blocks; cur_block; cur_block = cur_block->next) {
- if (!cur_block->access) {
- continue;
- }
- opline = cur_block->start_opline + cur_block->len - 1;
- if (opline->opcode == ZEND_OP_DATA) {
- opline--;
- }
- if (cur_block->op1_to) {
- ZEND_OP1(opline).opline_num = cur_block->op1_to->start_opline - new_opcodes;
- }
- if (cur_block->op2_to) {
- ZEND_OP2(opline).opline_num = cur_block->op2_to->start_opline - new_opcodes;
+ /* adjust loop jump targets & remove unused live range entries */
+ if (op_array->last_live_range) {
+ int i, j;
+ uint32_t *map;
+ ALLOCA_FLAG(use_heap);
+
+ map = (uint32_t *)do_alloca(sizeof(uint32_t) * op_array->last_live_range, use_heap);
+
+ for (i = 0, j = 0; i < op_array->last_live_range; i++) {
+ if (op_array->live_range[i].var == (uint32_t)-1) {
+ /* this live range already removed */
+ continue;
+ }
+ if (!(blocks[cfg->map[op_array->live_range[i].start]].flags & ZEND_BB_REACHABLE)) {
+ ZEND_ASSERT(!(blocks[cfg->map[op_array->live_range[i].end]].flags & ZEND_BB_REACHABLE));
+ } else {
+ uint32_t start_op = blocks[cfg->map[op_array->live_range[i].start]].start;
+ uint32_t end_op = blocks[cfg->map[op_array->live_range[i].end]].start;
+
+ if (start_op == end_op) {
+ /* skip empty live range */
+ continue;
+ }
+ op_array->live_range[i].start = start_op;
+ op_array->live_range[i].end = end_op;
+ map[i] = j;
+ if (i != j) {
+ op_array->live_range[j] = op_array->live_range[i];
+ }
+ j++;
+ }
}
- if (cur_block->ext_to) {
- opline->extended_value = cur_block->ext_to->start_opline - new_opcodes;
+
+ if (i != j) {
+ if ((op_array->last_live_range = j)) {
+ zend_op *opline = new_opcodes;
+ zend_op *end = opline + len;
+ while (opline != end) {
+ if ((opline->opcode == ZEND_FREE || opline->opcode == ZEND_FE_FREE) &&
+ opline->extended_value == ZEND_FREE_ON_RETURN) {
+ ZEND_ASSERT(opline->op2.num < (uint32_t) i);
+ opline->op2.num = map[opline->op2.num];
+ }
+ opline++;
+ }
+ } else {
+ efree(op_array->live_range);
+ op_array->live_range = NULL;
+ }
}
- print_block(cur_block, new_opcodes, "Out ");
+ free_alloca(map, use_heap);
}
- efree(op_array->opcodes);
- op_array->opcodes = erealloc(new_opcodes, op_array->last * sizeof(zend_op));
/* adjust early binding list */
if (op_array->early_binding != (uint32_t)-1) {
@@ -1249,45 +979,56 @@ static void assemble_code_blocks(zend_cfg *cfg, zend_op_array *op_array)
}
*opline_num = -1;
}
+
+ /* rebuild map (just for printing) */
+ memset(cfg->map, -1, sizeof(int) * op_array->last);
+ for (n = 0; n < cfg->blocks_count; n++) {
+ if (cfg->blocks[n].flags & ZEND_BB_REACHABLE) {
+ cfg->map[cfg->blocks[n].start] = n;
+ }
+ }
}
-static void zend_jmp_optimization(zend_code_block *block, zend_op_array *op_array, zend_code_block *blocks, zend_cfg *cfg, zend_optimizer_ctx *ctx)
+static void zend_jmp_optimization(zend_basic_block *block, zend_op_array *op_array, zend_cfg *cfg, zend_uchar *same_t)
{
/* last_op is the last opcode of the current block */
- zend_op *last_op = (block->start_opline + block->len - 1);
+ zend_basic_block *blocks = cfg->blocks;
+ zend_op *last_op;
- if (!block->len) {
+ if (block->len == 0) {
return;
}
+
+ last_op = op_array->opcodes + block->start + block->len - 1;
switch (last_op->opcode) {
case ZEND_JMP:
{
- zend_op *target = block->op1_to->start_opline;
- zend_code_block *next = block->next;
+ zend_basic_block *target_block = blocks + block->successors[0];
+ zend_op *target = op_array->opcodes + target_block->start;
+ int next = (block - blocks) + 1;
- while (next && !next->access) {
+ while (next < cfg->blocks_count && !(blocks[next].flags & ZEND_BB_REACHABLE)) {
/* find used one */
- next = next->next;
+ next++;
}
/* JMP(next) -> NOP */
- if (block->op1_to == next) {
- block->follow_to = block->op1_to;
- block->op1_to = NULL;
+ if (block->successors[0] == next) {
MAKE_NOP(last_op);
block->len--;
- if (block->len == 0) {
- /* this block is nothing but NOP now */
- delete_code_block(block, ctx);
- }
break;
}
- if (((target->opcode == ZEND_JMP &&
- block->op1_to != block->op1_to->op1_to) ||
- target->opcode == ZEND_JMPZNZ) &&
- !block->op1_to->protected) {
+ if (target->opcode == ZEND_JMP &&
+ block->successors[0] != target_block->successors[0] &&
+ !(target_block->flags & ZEND_BB_PROTECTED)) {
/* JMP L, L: JMP L1 -> JMP L1 */
+ *last_op = *target;
+ DEL_SOURCE(block, block->successors[0]);
+ block->successors[0] = target_block->successors[0];
+ ADD_SOURCE(block, block->successors[0]);
+ } else if (target->opcode == ZEND_JMPZNZ &&
+ !(target_block->flags & ZEND_BB_PROTECTED)) {
/* JMP L, L: JMPZNZ L1,L2 -> JMPZNZ L1,L2 */
*last_op = *target;
if (ZEND_OP1_TYPE(last_op) == IS_CONST) {
@@ -1295,25 +1036,15 @@ static void zend_jmp_optimization(zend_code_block *block, zend_op_array *op_arra
zval_copy_ctor(&zv);
last_op->op1.constant = zend_optimizer_add_literal(op_array, &zv);
}
- del_source(block, block->op1_to);
- if (block->op1_to->op2_to) {
- block->op2_to = block->op1_to->op2_to;
- ADD_SOURCE(block, block->op2_to);
- }
- if (block->op1_to->ext_to) {
- block->ext_to = block->op1_to->ext_to;
- ADD_SOURCE(block, block->ext_to);
- }
- if (block->op1_to->op1_to) {
- block->op1_to = block->op1_to->op1_to;
- ADD_SOURCE(block, block->op1_to);
- } else {
- block->op1_to = NULL;
- }
- } else if (target->opcode == ZEND_RETURN ||
- target->opcode == ZEND_RETURN_BY_REF ||
- target->opcode == ZEND_FAST_RET ||
- target->opcode == ZEND_EXIT) {
+ DEL_SOURCE(block, block->successors[0]);
+ block->successors[0] = target_block->successors[0];
+ block->successors[1] = target_block->successors[1];
+ ADD_SOURCE(block, block->successors[0]);
+ ADD_SOURCE(block, block->successors[1]);
+ } else if ((target->opcode == ZEND_RETURN ||
+ target->opcode == ZEND_RETURN_BY_REF ||
+ target->opcode == ZEND_EXIT) &&
+ !(op_array->fn_flags & ZEND_ACC_HAS_FINALLY_BLOCK)) {
/* JMP L, L: RETURN to immediate RETURN */
*last_op = *target;
if (ZEND_OP1_TYPE(last_op) == IS_CONST) {
@@ -1321,8 +1052,8 @@ static void zend_jmp_optimization(zend_code_block *block, zend_op_array *op_arra
zval_copy_ctor(&zv);
last_op->op1.constant = zend_optimizer_add_literal(op_array, &zv);
}
- del_source(block, block->op1_to);
- block->op1_to = NULL;
+ DEL_SOURCE(block, block->successors[0]);
+ block->successors[0] = -1;
#if 0
/* Temporarily disabled - see bug #0025274 */
} else if (0&& block->op1_to != block &&
@@ -1355,7 +1086,7 @@ static void zend_jmp_optimization(zend_code_block *block, zend_op_array *op_arra
next = next->follow_to;
}
if (can_reorder) {
- zend_code_block *prev = blocks;
+ zend_basic_block *prev = blocks;
while (prev->next != block->op1_to) {
prev = prev->next;
@@ -1394,159 +1125,135 @@ static void zend_jmp_optimization(zend_code_block *block, zend_op_array *op_arra
if (should_jmp) {
/* JMPNZ(true) -> JMP */
last_op->opcode = ZEND_JMP;
- COPY_NODE(last_op->op1, last_op->op2);
- block->op1_to = block->op2_to;
- del_source(block, block->follow_to);
- block->op2_to = NULL;
- block->follow_to = NULL;
+ DEL_SOURCE(block, block->successors[1]);
+ block->successors[1] = -1;
} else {
/* JMPNZ(false) -> NOP */
MAKE_NOP(last_op);
- del_source(block, block->op2_to);
- block->op2_to = NULL;
+ DEL_SOURCE(block, block->successors[0]);
+ block->successors[0] = block->successors[1];
+ block->successors[1] = -1;
}
break;
}
- if (block->op2_to == block->follow_to) {
- /* L: JMPZ(X, L+1) -> NOP or FREE(X) */
-
- if (last_op->op1_type == IS_VAR) {
- zend_op **Tsource = cfg->Tsource;
- zend_op *src = VAR_SOURCE(last_op->op1);
+ if (block->successors[0] == block->successors[1]) {
+ /* L: JMP[N]Z(X, L+1) -> NOP or FREE(X) */
- if (src &&
- src->opcode != ZEND_FETCH_R &&
- src->opcode != ZEND_FETCH_DIM_R &&
- src->opcode != ZEND_FETCH_OBJ_R) {
- ZEND_RESULT_TYPE(src) |= EXT_TYPE_UNUSED;
- MAKE_NOP(last_op);
- block->op2_to = NULL;
- break;
- }
- }
if (last_op->op1_type & (IS_VAR|IS_TMP_VAR)) {
last_op->opcode = ZEND_FREE;
last_op->op2.num = 0;
- block->op2_to = NULL;
} else {
MAKE_NOP(last_op);
- block->op2_to = NULL;
}
+ block->successors[1] = -1;
break;
}
- if (block->op2_to) {
+ if (1) {
zend_uchar same_type = ZEND_OP1_TYPE(last_op);
uint32_t same_var = VAR_NUM_EX(last_op->op1);
zend_op *target;
zend_op *target_end;
- zend_code_block *target_block = block->op2_to;;
+ zend_basic_block *target_block = blocks + block->successors[0];
next_target:
- target = target_block->start_opline;
- target_end = target_block->start_opline + target_block->len;
+ target = op_array->opcodes + target_block->start;
+ target_end = target + target_block->len;
while (target < target_end && target->opcode == ZEND_NOP) {
target++;
}
/* next block is only NOP's */
if (target == target_end) {
- target_block = target_block->follow_to;
+ target_block = blocks + target_block->successors[0];
goto next_target;
} else if (target->opcode == INV_COND(last_op->opcode) &&
/* JMPZ(X, L), L: JMPNZ(X, L2) -> JMPZ(X, L+1) */
(ZEND_OP1_TYPE(target) & (IS_TMP_VAR|IS_CV)) &&
same_type == ZEND_OP1_TYPE(target) &&
same_var == VAR_NUM_EX(target->op1) &&
- target_block->follow_to &&
- !target_block->protected
+ !(target_block->flags & ZEND_BB_PROTECTED)
) {
- del_source(block, block->op2_to);
- block->op2_to = target_block->follow_to;
- ADD_SOURCE(block, block->op2_to);
+ DEL_SOURCE(block, block->successors[0]);
+ block->successors[0] = target_block->successors[1];
+ ADD_SOURCE(block, block->successors[0]);
} else if (target->opcode == INV_COND_EX(last_op->opcode) &&
(ZEND_OP1_TYPE(target) & (IS_TMP_VAR|IS_CV)) &&
same_type == ZEND_OP1_TYPE(target) &&
same_var == VAR_NUM_EX(target->op1) &&
- target_block->follow_to &&
- !target_block->protected) {
+ !(target_block->flags & ZEND_BB_PROTECTED)) {
/* JMPZ(X, L), L: T = JMPNZ_EX(X, L2) -> T = JMPZ_EX(X, L+1) */
last_op->opcode += 3;
COPY_NODE(last_op->result, target->result);
- del_source(block, block->op2_to);
- block->op2_to = target_block->follow_to;
- ADD_SOURCE(block, block->op2_to);
- } else if (target_block->op2_to &&
- target->opcode == last_op->opcode &&
+ DEL_SOURCE(block, block->successors[0]);
+ block->successors[0] = target_block->successors[1];
+ ADD_SOURCE(block, block->successors[0]);
+ } else if (target->opcode == last_op->opcode &&
(ZEND_OP1_TYPE(target) & (IS_TMP_VAR|IS_CV)) &&
same_type == ZEND_OP1_TYPE(target) &&
same_var == VAR_NUM_EX(target->op1) &&
- !target_block->protected) {
+ !(target_block->flags & ZEND_BB_PROTECTED)) {
/* JMPZ(X, L), L: JMPZ(X, L2) -> JMPZ(X, L2) */
- del_source(block, block->op2_to);
- block->op2_to = target_block->op2_to;
- ADD_SOURCE(block, block->op2_to);
- } else if (target_block->op1_to &&
- target->opcode == ZEND_JMP &&
- !target_block->protected) {
+ DEL_SOURCE(block, block->successors[0]);
+ block->successors[0] = target_block->successors[0];
+ ADD_SOURCE(block, block->successors[0]);
+ } else if (target->opcode == ZEND_JMP &&
+ !(target_block->flags & ZEND_BB_PROTECTED)) {
/* JMPZ(X, L), L: JMP(L2) -> JMPZ(X, L2) */
- del_source(block, block->op2_to);
- block->op2_to = target_block->op1_to;
- ADD_SOURCE(block, block->op2_to);
- } else if (target_block->op2_to &&
- target_block->ext_to &&
- target->opcode == ZEND_JMPZNZ &&
- (ZEND_OP1_TYPE(target) & (IS_TMP_VAR|IS_CV)) &&
- same_type == ZEND_OP1_TYPE(target) &&
- same_var == VAR_NUM_EX(target->op1) &&
- !target_block->protected) {
+ DEL_SOURCE(block, block->successors[0]);
+ block->successors[0] = target_block->successors[0];
+ ADD_SOURCE(block, block->successors[0]);
+ } else if (target->opcode == ZEND_JMPZNZ &&
+ (ZEND_OP1_TYPE(target) & (IS_TMP_VAR|IS_CV)) &&
+ same_type == ZEND_OP1_TYPE(target) &&
+ same_var == VAR_NUM_EX(target->op1) &&
+ !(target_block->flags & ZEND_BB_PROTECTED)) {
/* JMPZ(X, L), L: JMPZNZ(X, L2, L3) -> JMPZ(X, L2) */
- del_source(block, block->op2_to);
+ DEL_SOURCE(block, block->successors[0]);
if (last_op->opcode == ZEND_JMPZ) {
- block->op2_to = target_block->op2_to;
+ block->successors[0] = target_block->successors[0];
} else {
- block->op2_to = target_block->ext_to;
+ block->successors[0] = target_block->successors[1];
}
- ADD_SOURCE(block, block->op2_to);
+ ADD_SOURCE(block, block->successors[0]);
}
}
- if (block->follow_to &&
- (last_op->opcode == ZEND_JMPZ || last_op->opcode == ZEND_JMPNZ)) {
+ if (last_op->opcode == ZEND_JMPZ || last_op->opcode == ZEND_JMPNZ) {
zend_op *target;
zend_op *target_end;
+ zend_basic_block *target_block;
while (1) {
- target = block->follow_to->start_opline;
- target_end = block->follow_to->start_opline + block->follow_to->len;
+ target_block = blocks + block->successors[1];
+ target = op_array->opcodes + target_block->start;
+ target_end = op_array->opcodes + target_block->start + 1;
while (target < target_end && target->opcode == ZEND_NOP) {
target++;
}
/* next block is only NOP's */
- if (target == target_end && ! block->follow_to->protected) {
- del_source(block, block->follow_to);
- block->follow_to = block->follow_to->follow_to;
- ADD_SOURCE(block, block->follow_to);
+ if (target == target_end && !(target_block->flags & ZEND_BB_PROTECTED)) {
+ DEL_SOURCE(block, block->successors[1]);
+ block->successors[1] = target_block->successors[0];
+ ADD_SOURCE(block, block->successors[1]);
} else {
break;
}
}
/* JMPZ(X,L1), JMP(L2) -> JMPZNZ(X,L1,L2) */
if (target->opcode == ZEND_JMP &&
- block->follow_to->op1_to &&
- !block->follow_to->protected) {
- del_source(block, block->follow_to);
+ !(target_block->flags & ZEND_BB_PROTECTED)) {
+ DEL_SOURCE(block, block->successors[1]);
if (last_op->opcode == ZEND_JMPZ) {
- block->ext_to = block->follow_to->op1_to;
- ADD_SOURCE(block, block->ext_to);
+ block->successors[1] = target_block->successors[0];
+ ADD_SOURCE(block, block->successors[1]);
} else {
- block->ext_to = block->op2_to;
- block->op2_to = block->follow_to->op1_to;
- ADD_SOURCE(block, block->op2_to);
+ block->successors[1] = block->successors[0];
+ block->successors[0] = target_block->successors[0];
+ ADD_SOURCE(block, block->successors[0]);
}
- block->follow_to = NULL;
last_op->opcode = ZEND_JMPZNZ;
}
}
@@ -1567,207 +1274,182 @@ next_target:
*/
last_op->opcode = ZEND_QM_ASSIGN;
SET_UNUSED(last_op->op2);
- del_source(block, block->op2_to);
- block->op2_to = NULL;
+ DEL_SOURCE(block, block->successors[0]);
+ block->successors[0] = block->successors[1];
+ block->successors[1] = -1;
}
break;
}
- if (block->op2_to) {
+ if (1) {
zend_op *target, *target_end;
- char *same_t=NULL;
- zend_code_block *target_block;
+ zend_basic_block *target_block;
int var_num = op_array->last_var + op_array->T;
if (var_num <= 0) {
return;
}
- same_t = cfg->same_t;
memset(same_t, 0, var_num);
same_t[VAR_NUM_EX(last_op->op1)] |= ZEND_OP1_TYPE(last_op);
same_t[VAR_NUM_EX(last_op->result)] |= ZEND_RESULT_TYPE(last_op);
- target_block = block->op2_to;
+ target_block = blocks + block->successors[0];
next_target_ex:
- target = target_block->start_opline;
- target_end = target_block->start_opline + target_block->len;
+ target = op_array->opcodes + target_block->start;
+ target_end = target + target_block->len;
while (target < target_end && target->opcode == ZEND_NOP) {
target++;
}
/* next block is only NOP's */
if (target == target_end) {
- target_block = target_block->follow_to;
+ target_block = blocks + target_block->successors[0];
goto next_target_ex;
- } else if (target_block->op2_to &&
- target->opcode == last_op->opcode-3 &&
+ } else if (target->opcode == last_op->opcode-3 &&
(ZEND_OP1_TYPE(target) & (IS_TMP_VAR|IS_CV)) &&
(same_t[VAR_NUM_EX(target->op1)] & ZEND_OP1_TYPE(target)) != 0 &&
- !target_block->protected) {
+ !(target_block->flags & ZEND_BB_PROTECTED)) {
/* T = JMPZ_EX(X, L1), L1: JMPZ({X|T}, L2) -> T = JMPZ_EX(X, L2) */
- del_source(block, block->op2_to);
- block->op2_to = target_block->op2_to;
- ADD_SOURCE(block, block->op2_to);
- } else if (target_block->op2_to &&
- target->opcode == INV_EX_COND(last_op->opcode) &&
+ DEL_SOURCE(block, block->successors[0]);
+ block->successors[0] = target_block->successors[0];
+ ADD_SOURCE(block, block->successors[0]);
+ } else if (target->opcode == INV_EX_COND(last_op->opcode) &&
(ZEND_OP1_TYPE(target) & (IS_TMP_VAR|IS_CV)) &&
(same_t[VAR_NUM_EX(target->op1)] & ZEND_OP1_TYPE(target)) != 0 &&
- !target_block->protected) {
+ !(target_block->flags & ZEND_BB_PROTECTED)) {
/* T = JMPZ_EX(X, L1), L1: JMPNZ({X|T1}, L2) -> T = JMPZ_EX(X, L1+1) */
- del_source(block, block->op2_to);
- block->op2_to = target_block->follow_to;
- ADD_SOURCE(block, block->op2_to);
- } else if (target_block->op2_to &&
- target->opcode == INV_EX_COND_EX(last_op->opcode) &&
+ DEL_SOURCE(block, block->successors[0]);
+ block->successors[0] = target_block->successors[1];
+ ADD_SOURCE(block, block->successors[0]);
+ } else if (target->opcode == INV_EX_COND_EX(last_op->opcode) &&
(ZEND_OP1_TYPE(target) & (IS_TMP_VAR|IS_CV)) &&
(same_t[VAR_NUM_EX(target->op1)] & ZEND_OP1_TYPE(target)) != 0 &&
(same_t[VAR_NUM_EX(target->result)] & ZEND_RESULT_TYPE(target)) != 0 &&
- !target_block->protected) {
+ !(target_block->flags & ZEND_BB_PROTECTED)) {
/* T = JMPZ_EX(X, L1), L1: T = JMPNZ_EX(T, L2) -> T = JMPZ_EX(X, L1+1) */
- del_source(block, block->op2_to);
- block->op2_to = target_block->follow_to;
- ADD_SOURCE(block, block->op2_to);
- } else if (target_block->op2_to &&
- target->opcode == last_op->opcode &&
+ DEL_SOURCE(block, block->successors[0]);
+ block->successors[0] = target_block->successors[1];
+ ADD_SOURCE(block, block->successors[0]);
+ } else if (target->opcode == last_op->opcode &&
(ZEND_OP1_TYPE(target) & (IS_TMP_VAR|IS_CV)) &&
(same_t[VAR_NUM_EX(target->op1)] & ZEND_OP1_TYPE(target)) != 0 &&
(same_t[VAR_NUM_EX(target->result)] & ZEND_RESULT_TYPE(target)) != 0 &&
- !target_block->protected) {
+ !(target_block->flags & ZEND_BB_PROTECTED)) {
/* T = JMPZ_EX(X, L1), L1: T = JMPZ({X|T}, L2) -> T = JMPZ_EX(X, L2) */
- del_source(block, block->op2_to);
- block->op2_to = target_block->op2_to;
- ADD_SOURCE(block, block->op2_to);
- } else if (target_block->op1_to &&
- target->opcode == ZEND_JMP &&
- !target_block->protected) {
+ DEL_SOURCE(block, block->successors[0]);
+ block->successors[0] = target_block->successors[0];
+ ADD_SOURCE(block, block->successors[0]);
+ } else if (target->opcode == ZEND_JMP &&
+ !(target_block->flags & ZEND_BB_PROTECTED)) {
/* T = JMPZ_EX(X, L), L: JMP(L2) -> T = JMPZ(X, L2) */
- del_source(block, block->op2_to);
- block->op2_to = target_block->op1_to;
- ADD_SOURCE(block, block->op2_to);
- } else if (target_block->op2_to &&
- target_block->ext_to &&
- target->opcode == ZEND_JMPZNZ &&
+ DEL_SOURCE(block, block->successors[0]);
+ block->successors[0] = target_block->successors[0];
+ ADD_SOURCE(block, block->successors[0]);
+ } else if (target->opcode == ZEND_JMPZNZ &&
(ZEND_OP1_TYPE(target) & (IS_TMP_VAR|IS_CV)) &&
(same_t[VAR_NUM_EX(target->op1)] & ZEND_OP1_TYPE(target)) != 0 &&
- !target_block->protected) {
+ !(target_block->flags & ZEND_BB_PROTECTED)) {
/* T = JMPZ_EX(X, L), L: JMPZNZ({X|T}, L2, L3) -> T = JMPZ_EX(X, L2) */
- del_source(block, block->op2_to);
+ DEL_SOURCE(block, block->successors[0]);
if (last_op->opcode == ZEND_JMPZ_EX) {
- block->op2_to = target_block->op2_to;
+ block->successors[0] = target_block->successors[0];
} else {
- block->op2_to = target_block->ext_to;
+ block->successors[0] = target_block->successors[1];
}
- ADD_SOURCE(block, block->op2_to);
+ ADD_SOURCE(block, block->successors[0]);
}
}
break;
case ZEND_JMPZNZ: {
- zend_code_block *next = block->next;
+ int next = (block - blocks) + 1;
- while (next && !next->access) {
+ while (next < cfg->blocks_count && !(blocks[next].flags & ZEND_BB_REACHABLE)) {
/* find first accessed one */
- next = next->next;
+ next++;
}
if (ZEND_OP1_TYPE(last_op) == IS_CONST) {
if (!zend_is_true(&ZEND_OP1_LITERAL(last_op))) {
/* JMPZNZ(false,L1,L2) -> JMP(L1) */
- zend_code_block *todel;
-
literal_dtor(&ZEND_OP1_LITERAL(last_op));
last_op->opcode = ZEND_JMP;
SET_UNUSED(last_op->op1);
SET_UNUSED(last_op->op2);
- block->op1_to = block->op2_to;
- todel = block->ext_to;
- block->op2_to = NULL;
- block->ext_to = NULL;
- del_source(block, todel);
+ DEL_SOURCE(block, block->successors[1]);
+ block->successors[1] = -1;
} else {
/* JMPZNZ(true,L1,L2) -> JMP(L2) */
- zend_code_block *todel;
-
literal_dtor(&ZEND_OP1_LITERAL(last_op));
last_op->opcode = ZEND_JMP;
SET_UNUSED(last_op->op1);
SET_UNUSED(last_op->op2);
- block->op1_to = block->ext_to;
- todel = block->op2_to;
- block->op2_to = NULL;
- block->ext_to = NULL;
- del_source(block, todel);
+ DEL_SOURCE(block, block->successors[0]);
+ block->successors[0] = block->successors[1];
+ block->successors[1] = -1;
}
- } else if (block->op2_to == block->ext_to) {
+ } else if (block->successors[0] == block->successors[1]) {
/* both goto the same one - it's JMP */
if (!(last_op->op1_type & (IS_VAR|IS_TMP_VAR))) {
/* JMPZNZ(?,L,L) -> JMP(L) */
last_op->opcode = ZEND_JMP;
SET_UNUSED(last_op->op1);
SET_UNUSED(last_op->op2);
- block->op1_to = block->op2_to;
- block->op2_to = NULL;
- block->ext_to = NULL;
+ block->successors[1] = -1;
}
- } else if (block->op2_to == next) {
+ } else if (block->successors[0] == next) {
/* jumping to next on Z - can follow to it and jump only on NZ */
/* JMPZNZ(X,L1,L2) L1: -> JMPNZ(X,L2) */
last_op->opcode = ZEND_JMPNZ;
- block->op2_to = block->ext_to;
- block->follow_to = next;
- block->ext_to = NULL;
- /* no need to add source - it's block->op2_to */
- } else if (block->ext_to == next) {
+ block->successors[0] = block->successors[1];
+ block->successors[1] = next;
+ /* no need to add source */
+ } else if (block->successors[1] == next) {
/* jumping to next on NZ - can follow to it and jump only on Z */
/* JMPZNZ(X,L1,L2) L2: -> JMPZ(X,L1) */
last_op->opcode = ZEND_JMPZ;
- block->follow_to = next;
- block->ext_to = NULL;
- /* no need to add source - it's block->ext_to */
+ /* no need to add source */
}
- if (last_op->opcode == ZEND_JMPZNZ && block->op2_to) {
+ if (last_op->opcode == ZEND_JMPZNZ) {
zend_uchar same_type = ZEND_OP1_TYPE(last_op);
zend_uchar same_var = VAR_NUM_EX(last_op->op1);
zend_op *target;
zend_op *target_end;
- zend_code_block *target_block = block->op2_to;
+ zend_basic_block *target_block = blocks + block->successors[0];
next_target_znz:
- target = target_block->start_opline;
- target_end = target_block->start_opline + target_block->len;
+ target = op_array->opcodes + target_block->start;
+ target_end = target + target_block->len;
while (target < target_end && target->opcode == ZEND_NOP) {
target++;
}
/* next block is only NOP's */
if (target == target_end) {
- target_block = target_block->follow_to;
+ target_block = blocks + target_block->successors[0];
goto next_target_znz;
- } else if (target_block->op2_to &&
- (target->opcode == ZEND_JMPZ || target->opcode == ZEND_JMPZNZ) &&
+ } else if ((target->opcode == ZEND_JMPZ || target->opcode == ZEND_JMPZNZ) &&
(ZEND_OP1_TYPE(target) & (IS_TMP_VAR|IS_CV)) &&
same_type == ZEND_OP1_TYPE(target) &&
same_var == VAR_NUM_EX(target->op1) &&
- !target_block->protected) {
+ !(target_block->flags & ZEND_BB_PROTECTED)) {
/* JMPZNZ(X, L1, L2), L1: JMPZ(X, L3) -> JMPZNZ(X, L3, L2) */
- del_source(block, block->op2_to);
- block->op2_to = target_block->op2_to;
- ADD_SOURCE(block, block->op2_to);
+ DEL_SOURCE(block, block->successors[0]);
+ block->successors[0] = target_block->successors[0];
+ ADD_SOURCE(block, block->successors[0]);
} else if (target->opcode == ZEND_JMPNZ &&
(ZEND_OP1_TYPE(target) & (IS_TMP_VAR|IS_CV)) &&
same_type == ZEND_OP1_TYPE(target) &&
same_var == VAR_NUM_EX(target->op1) &&
- target_block->follow_to &&
- !target_block->protected) {
+ !(target_block->flags & ZEND_BB_PROTECTED)) {
/* JMPZNZ(X, L1, L2), L1: X = JMPNZ(X, L3) -> JMPZNZ(X, L1+1, L2) */
- del_source(block, block->op2_to);
- block->op2_to = target_block->follow_to;
- ADD_SOURCE(block, block->op2_to);
- } else if (target_block->op1_to &&
- target->opcode == ZEND_JMP &&
- !target_block->protected) {
+ DEL_SOURCE(block, block->successors[0]);
+ block->successors[0] = target_block->successors[1];
+ ADD_SOURCE(block, block->successors[0]);
+ } else if (target->opcode == ZEND_JMP &&
+ !(target_block->flags & ZEND_BB_PROTECTED)) {
/* JMPZNZ(X, L1, L2), L1: JMP(L3) -> JMPZNZ(X, L3, L2) */
- del_source(block, block->op2_to);
- block->op2_to = target_block->op1_to;
- ADD_SOURCE(block, block->op2_to);
+ DEL_SOURCE(block, block->successors[0]);
+ block->successors[0] = target_block->successors[0];
+ ADD_SOURCE(block, block->successors[0]);
}
}
break;
@@ -1777,25 +1459,19 @@ next_target_znz:
/* Global data dependencies */
-#define T_USAGE(op) do { \
- if ((op ## _type & (IS_VAR | IS_TMP_VAR)) && \
- !zend_bitset_in(defined_here, VAR_NUM(op.var)) && !zend_bitset_in(used_ext, VAR_NUM(op.var))) { \
- zend_bitset_incl(used_ext, VAR_NUM(op.var)); \
- } \
- } while (0)
-
-#define NEVER_USED(op) ((op ## _type & (IS_VAR | IS_TMP_VAR)) && !zend_bitset_in(usage, VAR_NUM(op.var))) /* !zend_bitset_in(used_ext, op.var) && */
-#define RES_NEVER_USED(opline) (opline->result_type == IS_UNUSED || NEVER_USED(opline->result))
-
/* Find a set of variables which are used outside of the block where they are
* defined. We won't apply some optimization patterns for such variables. */
-static void zend_t_usage(zend_code_block *block, zend_op_array *op_array, zend_bitset used_ext, zend_optimizer_ctx *ctx)
+static void zend_t_usage(zend_cfg *cfg, zend_op_array *op_array, zend_bitset used_ext, zend_optimizer_ctx *ctx)
{
- zend_code_block *next_block = block->next;
+ int n;
+ zend_basic_block *block, *next_block;
+ uint32_t var_num;
uint32_t bitset_len;
zend_bitset usage;
zend_bitset defined_here;
void *checkpoint;
+ zend_op *opline, *end;
+
if (op_array->T == 0) {
/* shortcut - if no Ts, nothing to do */
@@ -1804,213 +1480,337 @@ static void zend_t_usage(zend_code_block *block, zend_op_array *op_array, zend_b
checkpoint = zend_arena_checkpoint(ctx->arena);
bitset_len = zend_bitset_len(op_array->last_var + op_array->T);
- usage = zend_arena_alloc(&ctx->arena, bitset_len * ZEND_BITSET_ELM_SIZE);
- zend_bitset_clear(usage, bitset_len);
defined_here = zend_arena_alloc(&ctx->arena, bitset_len * ZEND_BITSET_ELM_SIZE);
- while (next_block) {
- zend_op *opline = next_block->start_opline;
- zend_op *end = opline + next_block->len;
+ zend_bitset_clear(defined_here, bitset_len);
+ for (n = 1; n < cfg->blocks_count; n++) {
+ block = cfg->blocks + n;
- if (!next_block->access) {
- next_block = next_block->next;
+ if (!(block->flags & ZEND_BB_REACHABLE)) {
continue;
}
- zend_bitset_clear(defined_here, bitset_len);
+
+ opline = op_array->opcodes + block->start;
+ end = opline + block->len;
+ if (!(block->flags & ZEND_BB_FOLLOW) ||
+ (block->flags & ZEND_BB_TARGET)) {
+ /* Skip continuation of "extended" BB */
+ zend_bitset_clear(defined_here, bitset_len);
+ }
while (opline<end) {
- T_USAGE(opline->op1);
- if (opline->op2_type & (IS_VAR | IS_TMP_VAR)) {
- if (opline->opcode == ZEND_FE_FETCH_R || opline->opcode == ZEND_FE_FETCH_RW) {
+ if (opline->op1_type & (IS_VAR|IS_TMP_VAR)) {
+ var_num = VAR_NUM(opline->op1.var);
+ if (!zend_bitset_in(defined_here, var_num)) {
+ zend_bitset_incl(used_ext, var_num);
+ }
+ }
+ if (opline->op2_type == IS_VAR) {
+ var_num = VAR_NUM(opline->op2.var);
+ if (opline->opcode == ZEND_FE_FETCH_R ||
+ opline->opcode == ZEND_FE_FETCH_RW) {
/* these opcode use the op2 as result */
- zend_bitset_incl(defined_here, VAR_NUM(ZEND_OP2(opline).var));
- } else {
- T_USAGE(opline->op2);
+ zend_bitset_incl(defined_here, var_num);
+ } else if (!zend_bitset_in(defined_here, var_num)) {
+ zend_bitset_incl(used_ext, var_num);
+ }
+ } else if (opline->op2_type == IS_TMP_VAR) {
+ var_num = VAR_NUM(opline->op2.var);
+ if (!zend_bitset_in(defined_here, var_num)) {
+ zend_bitset_incl(used_ext, var_num);
}
}
- if (RESULT_USED(opline)) {
- if (!zend_bitset_in(defined_here, VAR_NUM(ZEND_RESULT(opline).var)) && !zend_bitset_in(used_ext, VAR_NUM(ZEND_RESULT(opline).var)) &&
- opline->opcode == ZEND_ADD_ARRAY_ELEMENT) {
- /* these opcode use the result as argument */
- zend_bitset_incl(used_ext, VAR_NUM(ZEND_RESULT(opline).var));
+ if (opline->result_type == IS_VAR) {
+ var_num = VAR_NUM(opline->result.var);
+ zend_bitset_incl(defined_here, var_num);
+ } else if (opline->result_type == IS_TMP_VAR) {
+ var_num = VAR_NUM(opline->result.var);
+ switch (opline->opcode) {
+ case ZEND_ADD_ARRAY_ELEMENT:
+ case ZEND_ROPE_ADD:
+ /* these opcodes use the result as argument */
+ if (!zend_bitset_in(defined_here, var_num)) {
+ zend_bitset_incl(used_ext, var_num);
+ }
+ break;
+ default :
+ zend_bitset_incl(defined_here, var_num);
}
- zend_bitset_incl(defined_here, VAR_NUM(ZEND_RESULT(opline).var));
}
opline++;
}
- next_block = next_block->next;
}
-#if DEBUG_BLOCKPASS
- {
- int i;
+ if (ctx->debug_level & ZEND_DUMP_BLOCK_PASS_VARS) {
+ int printed = 0;
+ uint32_t i;
+
for (i = op_array->last_var; i< op_array->T; i++) {
- fprintf(stderr, "T%d: %c\n", i, zend_bitset_in(used_ext, i) + '0');
+ if (zend_bitset_in(used_ext, i)) {
+ if (!printed) {
+ fprintf(stderr, "NON-LOCAL-VARS: %d", i);
+ printed = 1;
+ } else {
+ fprintf(stderr, ", %d", i);
+ }
+ }
+ }
+ if (printed) {
+ fprintf(stderr, "\n");
}
}
-#endif
- while (block) {
- zend_op *opline = block->start_opline + block->len - 1;
+ usage = defined_here;
+ next_block = NULL;
+ for (n = cfg->blocks_count; n > 0;) {
+ block = cfg->blocks + (--n);
- if (!block->access) {
- block = block->next;
+ if (!(block->flags & ZEND_BB_REACHABLE) || block->len == 0) {
continue;
}
- zend_bitset_copy(usage, used_ext, bitset_len);
+ end = op_array->opcodes + block->start;
+ opline = end + block->len - 1;
+ if (!next_block ||
+ !(next_block->flags & ZEND_BB_FOLLOW) ||
+ (next_block->flags & ZEND_BB_TARGET)) {
+ /* Skip continuation of "extended" BB */
+ zend_bitset_copy(usage, used_ext, bitset_len);
+ } else if (block->successors[1] != -1) {
+ zend_bitset_union(usage, used_ext, bitset_len);
+ }
+ next_block = block;
- while (opline >= block->start_opline) {
+ while (opline >= end) {
/* usage checks */
- if (RES_NEVER_USED(opline)) {
- switch (opline->opcode) {
- case ZEND_ASSIGN_ADD:
- case ZEND_ASSIGN_SUB:
- case ZEND_ASSIGN_MUL:
- case ZEND_ASSIGN_DIV:
- case ZEND_ASSIGN_POW:
- case ZEND_ASSIGN_MOD:
- case ZEND_ASSIGN_SL:
- case ZEND_ASSIGN_SR:
- case ZEND_ASSIGN_CONCAT:
- case ZEND_ASSIGN_BW_OR:
- case ZEND_ASSIGN_BW_AND:
- case ZEND_ASSIGN_BW_XOR:
- case ZEND_PRE_INC:
- case ZEND_PRE_DEC:
- case ZEND_POST_INC:
- case ZEND_POST_DEC:
- case ZEND_ASSIGN:
- case ZEND_ASSIGN_REF:
- case ZEND_DO_FCALL:
- case ZEND_DO_ICALL:
- case ZEND_DO_UCALL:
- case ZEND_DO_FCALL_BY_NAME:
- if (ZEND_RESULT_TYPE(opline) == IS_VAR) {
- ZEND_RESULT_TYPE(opline) |= EXT_TYPE_UNUSED;
- }
- break;
- case ZEND_QM_ASSIGN:
- case ZEND_BOOL:
- case ZEND_BOOL_NOT:
- if (ZEND_OP1_TYPE(opline) == IS_CONST) {
- literal_dtor(&ZEND_OP1_LITERAL(opline));
- } else if (ZEND_OP1_TYPE(opline) == IS_TMP_VAR) {
- opline->opcode = ZEND_FREE;
- } else {
- MAKE_NOP(opline);
- }
- break;
- case ZEND_JMPZ_EX:
- case ZEND_JMPNZ_EX:
- opline->opcode -= 3;
- SET_UNUSED(opline->result);
- break;
- }
- }
-
- if (opline->opcode == ZEND_ADD_ARRAY_ELEMENT) {
- if (ZEND_OP1_TYPE(opline) == IS_VAR || ZEND_OP1_TYPE(opline) == IS_TMP_VAR) {
- zend_bitset_incl(usage, VAR_NUM(ZEND_RESULT(opline).var));
+ if (opline->result_type == IS_VAR) {
+ if (!zend_bitset_in(usage, VAR_NUM(opline->result.var))) {
+ switch (opline->opcode) {
+ case ZEND_ASSIGN_ADD:
+ case ZEND_ASSIGN_SUB:
+ case ZEND_ASSIGN_MUL:
+ case ZEND_ASSIGN_DIV:
+ case ZEND_ASSIGN_POW:
+ case ZEND_ASSIGN_MOD:
+ case ZEND_ASSIGN_SL:
+ case ZEND_ASSIGN_SR:
+ case ZEND_ASSIGN_CONCAT:
+ case ZEND_ASSIGN_BW_OR:
+ case ZEND_ASSIGN_BW_AND:
+ case ZEND_ASSIGN_BW_XOR:
+ case ZEND_PRE_INC:
+ case ZEND_PRE_DEC:
+ case ZEND_ASSIGN:
+ case ZEND_ASSIGN_REF:
+ case ZEND_DO_FCALL:
+ case ZEND_DO_ICALL:
+ case ZEND_DO_UCALL:
+ case ZEND_DO_FCALL_BY_NAME:
+ opline->result_type = IS_UNUSED;
+ break;
+ }
+ } else {
+ zend_bitset_excl(usage, VAR_NUM(opline->result.var));
}
- } else {
- if (RESULT_USED(opline)) {
- zend_bitset_excl(usage, VAR_NUM(ZEND_RESULT(opline).var));
+ } else if (opline->result_type == IS_TMP_VAR) {
+ if (!zend_bitset_in(usage, VAR_NUM(opline->result.var))) {
+ switch (opline->opcode) {
+ case ZEND_POST_INC:
+ case ZEND_POST_DEC:
+ opline->opcode -= 2;
+ opline->result_type = IS_UNUSED;
+ break;
+ case ZEND_QM_ASSIGN:
+ case ZEND_BOOL:
+ case ZEND_BOOL_NOT:
+ if (ZEND_OP1_TYPE(opline) == IS_CONST) {
+ literal_dtor(&ZEND_OP1_LITERAL(opline));
+ } else if (ZEND_OP1_TYPE(opline) == IS_TMP_VAR) {
+ opline->opcode = ZEND_FREE;
+ } else {
+ MAKE_NOP(opline);
+ }
+ break;
+ case ZEND_JMPZ_EX:
+ case ZEND_JMPNZ_EX:
+ opline->opcode -= 3;
+ SET_UNUSED(opline->result);
+ break;
+ case ZEND_ADD_ARRAY_ELEMENT:
+ case ZEND_ROPE_ADD:
+ zend_bitset_incl(usage, VAR_NUM(opline->result.var));
+ break;
+ }
+ } else {
+ switch (opline->opcode) {
+ case ZEND_ADD_ARRAY_ELEMENT:
+ case ZEND_ROPE_ADD:
+ break;
+ default:
+ zend_bitset_excl(usage, VAR_NUM(opline->result.var));
+ break;
+ }
}
}
- if (ZEND_OP1_TYPE(opline) == IS_VAR || ZEND_OP1_TYPE(opline) == IS_TMP_VAR) {
- zend_bitset_incl(usage, VAR_NUM(ZEND_OP1(opline).var));
+ if (opline->op2_type == IS_VAR) {
+ switch (opline->opcode) {
+ case ZEND_FE_FETCH_R:
+ case ZEND_FE_FETCH_RW:
+ zend_bitset_excl(usage, VAR_NUM(opline->op2.var));
+ break;
+ default:
+ zend_bitset_incl(usage, VAR_NUM(opline->op2.var));
+ break;
+ }
+ } else if (opline->op2_type == IS_TMP_VAR) {
+ zend_bitset_incl(usage, VAR_NUM(opline->op2.var));
}
- if (ZEND_OP2_TYPE(opline) == IS_VAR || ZEND_OP2_TYPE(opline) == IS_TMP_VAR) {
- zend_bitset_incl(usage, VAR_NUM(ZEND_OP2(opline).var));
+ if (opline->op1_type & (IS_VAR|IS_TMP_VAR)) {
+ zend_bitset_incl(usage, VAR_NUM(opline->op1.var));
}
- if ((ZEND_RESULT_TYPE(opline) & IS_VAR) &&
- (ZEND_RESULT_TYPE(opline) & EXT_TYPE_UNUSED) &&
- zend_bitset_in(usage, VAR_NUM(ZEND_RESULT(opline).var))) {
- ZEND_RESULT_TYPE(opline) &= ~EXT_TYPE_UNUSED;
- }
-
opline--;
}
- block = block->next;
- } /* end blocks */
+ }
zend_arena_release(&ctx->arena, checkpoint);
}
+static void zend_merge_blocks(zend_op_array *op_array, zend_cfg *cfg)
+{
+ int i;
+ zend_basic_block *b, *bb;
+ zend_basic_block *prev = NULL;
+
+ for (i = 0; i < cfg->blocks_count; i++) {
+ b = cfg->blocks + i;
+ if (b->flags & ZEND_BB_REACHABLE) {
+ if ((b->flags & ZEND_BB_FOLLOW) &&
+ !(b->flags & (ZEND_BB_TARGET | ZEND_BB_PROTECTED)) &&
+ prev &&
+ prev->successors[0] == i && prev->successors[1] == -1)
+ {
+ zend_op *last_op = op_array->opcodes + prev->start + prev->len - 1;
+ if (prev->len != 0 && last_op->opcode == ZEND_JMP) {
+ MAKE_NOP(last_op);
+ }
+
+ for (bb = prev + 1; bb != b; bb++) {
+ zend_op *op = op_array->opcodes + bb->start;
+ zend_op *end = op + bb->len;
+ while (op < end) {
+ if (ZEND_OP1_TYPE(op) == IS_CONST) {
+ literal_dtor(&ZEND_OP1_LITERAL(op));
+ }
+ if (ZEND_OP2_TYPE(op) == IS_CONST) {
+ literal_dtor(&ZEND_OP2_LITERAL(op));
+ }
+ MAKE_NOP(op);
+ op++;
+ }
+ /* make block empty */
+ bb->len = 0;
+ }
+
+ /* re-link */
+ prev->flags |= (b->flags & ZEND_BB_EXIT);
+ prev->len = b->start + b->len - prev->start;
+ prev->successors[0] = b->successors[0];
+ prev->successors[1] = b->successors[1];
+
+ /* unlink & make block empty and unreachable */
+ b->flags = 0;
+ b->len = 0;
+ b->successors[0] = -1;
+ b->successors[1] = -1;
+ } else {
+ prev = b;
+ }
+ }
+ }
+}
+
#define PASSES 3
-void optimize_cfg(zend_op_array *op_array, zend_optimizer_ctx *ctx)
+void zend_optimize_cfg(zend_op_array *op_array, zend_optimizer_ctx *ctx)
{
zend_cfg cfg;
- zend_code_block *cur_block;
+ zend_basic_block *blocks, *end, *b;
int pass;
uint32_t bitset_len;
zend_bitset usage;
void *checkpoint;
-
-#if DEBUG_BLOCKPASS
- fprintf(stderr, "File %s func %s\n", op_array->filename, op_array->function_name? op_array->function_name : "main");
- fflush(stderr);
-#endif
-
- if (op_array->fn_flags & ZEND_ACC_HAS_FINALLY_BLOCK) {
- return;
- }
+ zend_op **Tsource;
+ zend_uchar *same_t;
/* Build CFG */
checkpoint = zend_arena_checkpoint(ctx->arena);
- if (!find_code_blocks(op_array, &cfg, ctx)) {
+ if (zend_build_cfg(&ctx->arena, op_array, ZEND_CFG_SPLIT_AT_LIVE_RANGES, &cfg, NULL) != SUCCESS) {
zend_arena_release(&ctx->arena, checkpoint);
return;
}
- zend_rebuild_access_path(&cfg, op_array, 0, ctx);
- /* full rebuild here to produce correct sources! */
+ if (ctx->debug_level & ZEND_DUMP_BEFORE_BLOCK_PASS) {
+ zend_dump_op_array(op_array, ZEND_DUMP_CFG, "before block pass", &cfg);
+ }
+
if (op_array->last_var || op_array->T) {
bitset_len = zend_bitset_len(op_array->last_var + op_array->T);
- cfg.Tsource = zend_arena_calloc(&ctx->arena, op_array->last_var + op_array->T, sizeof(zend_op *));
- cfg.same_t = zend_arena_alloc(&ctx->arena, op_array->last_var + op_array->T);
+ Tsource = zend_arena_calloc(&ctx->arena, op_array->last_var + op_array->T, sizeof(zend_op *));
+ same_t = zend_arena_alloc(&ctx->arena, op_array->last_var + op_array->T);
usage = zend_arena_alloc(&ctx->arena, bitset_len * ZEND_BITSET_ELM_SIZE);
} else {
bitset_len = 0;
- cfg.Tsource = NULL;
- cfg.same_t = NULL;
+ Tsource = NULL;
+ same_t = NULL;
usage = NULL;
}
+ blocks = cfg.blocks;
+ end = blocks + cfg.blocks_count;
for (pass = 0; pass < PASSES; pass++) {
/* Compute data dependencies */
zend_bitset_clear(usage, bitset_len);
- zend_t_usage(cfg.blocks, op_array, usage, ctx);
+ zend_t_usage(&cfg, op_array, usage, ctx);
/* optimize each basic block separately */
- for (cur_block = cfg.blocks; cur_block; cur_block = cur_block->next) {
- if (!cur_block->access) {
+ for (b = blocks; b < end; b++) {
+ if (!(b->flags & ZEND_BB_REACHABLE)) {
continue;
}
- zend_optimize_block(cur_block, op_array, usage, &cfg, ctx);
+ /* we track data dependencies only insight a single basic block */
+ if (!(b->flags & ZEND_BB_FOLLOW) ||
+ (b->flags & ZEND_BB_TARGET)) {
+ /* Skip continuation of "extended" BB */
+ memset(Tsource, 0, (op_array->last_var + op_array->T) * sizeof(zend_op *));
+ }
+ zend_optimize_block(b, op_array, usage, &cfg, Tsource);
}
/* Jump optimization for each block */
- for (cur_block = cfg.blocks; cur_block; cur_block = cur_block->next) {
- if (!cur_block->access) {
- continue;
+ for (b = blocks; b < end; b++) {
+ if (b->flags & ZEND_BB_REACHABLE) {
+ zend_jmp_optimization(b, op_array, &cfg, same_t);
}
- zend_jmp_optimization(cur_block, op_array, cfg.blocks, &cfg, ctx);
}
/* Eliminate unreachable basic blocks */
- zend_rebuild_access_path(&cfg, op_array, 1, ctx);
+ zend_cfg_remark_reachable_blocks(op_array, &cfg);
+
+ /* Merge Blocks */
+ zend_merge_blocks(op_array, &cfg);
}
zend_bitset_clear(usage, bitset_len);
- zend_t_usage(cfg.blocks, op_array, usage, ctx);
+ zend_t_usage(&cfg, op_array, usage, ctx);
assemble_code_blocks(&cfg, op_array);
+ if (ctx->debug_level & ZEND_DUMP_AFTER_BLOCK_PASS) {
+ zend_dump_op_array(op_array, ZEND_DUMP_CFG | ZEND_DUMP_HIDE_UNREACHABLE, "after block pass", &cfg);
+ }
+
/* Destroy CFG */
zend_arena_release(&ctx->arena, checkpoint);
}
diff --git a/ext/opcache/Optimizer/compact_literals.c b/ext/opcache/Optimizer/compact_literals.c
index da7b1b6d99..68178f5873 100644
--- a/ext/opcache/Optimizer/compact_literals.c
+++ b/ext/opcache/Optimizer/compact_literals.c
@@ -72,9 +72,9 @@ typedef struct _literal_info {
info[n].u.num = (_num); \
} while (0)
-#define LITERAL_INFO_OBJ(n, kind, merge, slots, related, _num) do { \
+#define LITERAL_INFO_OBJ(n, kind, merge, slots, related) do { \
info[n].flags = (LITERAL_EX_OBJ | ((merge) ? LITERAL_MAY_MERGE : 0) | LITERAL_FLAGS(kind, slots, related)); \
- info[n].u.num = (_num); \
+ info[n].u.num = (uint32_t)-1; \
} while (0)
static void optimizer_literal_obj_info(literal_info *info,
@@ -92,7 +92,7 @@ static void optimizer_literal_obj_info(literal_info *info,
*/
if (Z_TYPE(op_array->literals[constant]) == IS_STRING &&
op_type == IS_UNUSED) {
- LITERAL_INFO_OBJ(constant, kind, 1, slots, related, op_array->this_var);
+ LITERAL_INFO_OBJ(constant, kind, 1, slots, related);
} else {
LITERAL_INFO(constant, kind, 0, slots, related);
}
@@ -177,52 +177,45 @@ void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx
LITERAL_INFO(opline->op1.constant, LITERAL_CONST, 1, 1, 2);
break;
case ZEND_FETCH_CONSTANT:
- if (ZEND_OP1_TYPE(opline) == IS_UNUSED) {
- if ((opline->extended_value & (IS_CONSTANT_IN_NAMESPACE|IS_CONSTANT_UNQUALIFIED)) == (IS_CONSTANT_IN_NAMESPACE|IS_CONSTANT_UNQUALIFIED)) {
- LITERAL_INFO(opline->op2.constant, LITERAL_CONST, 1, 1, 5);
- } else {
- LITERAL_INFO(opline->op2.constant, LITERAL_CONST, 1, 1, 3);
- }
+ if ((opline->extended_value & (IS_CONSTANT_IN_NAMESPACE|IS_CONSTANT_UNQUALIFIED)) == (IS_CONSTANT_IN_NAMESPACE|IS_CONSTANT_UNQUALIFIED)) {
+ LITERAL_INFO(opline->op2.constant, LITERAL_CONST, 1, 1, 5);
} else {
- if (ZEND_OP1_TYPE(opline) == IS_CONST) {
- LITERAL_INFO(opline->op1.constant, LITERAL_CLASS, 1, 1, 2);
- }
+ LITERAL_INFO(opline->op2.constant, LITERAL_CONST, 1, 1, 3);
+ }
+ break;
+ case ZEND_FETCH_CLASS_CONSTANT:
+ if (ZEND_OP1_TYPE(opline) == IS_CONST) {
+ LITERAL_INFO(opline->op1.constant, LITERAL_CLASS, 1, 1, 2);
+ }
+ optimizer_literal_class_info(
+ info,
+ opline->op1_type,
+ opline->op1,
+ opline->op2.constant,
+ LITERAL_CLASS_CONST, (ZEND_OP1_TYPE(opline) == IS_CONST) ? 1 : 2, 1,
+ op_array);
+ break;
+ case ZEND_FETCH_STATIC_PROP_R:
+ case ZEND_FETCH_STATIC_PROP_W:
+ case ZEND_FETCH_STATIC_PROP_RW:
+ case ZEND_FETCH_STATIC_PROP_IS:
+ case ZEND_FETCH_STATIC_PROP_UNSET:
+ case ZEND_FETCH_STATIC_PROP_FUNC_ARG:
+ case ZEND_UNSET_STATIC_PROP:
+ case ZEND_ISSET_ISEMPTY_STATIC_PROP:
+ if (ZEND_OP2_TYPE(opline) == IS_CONST) {
+ LITERAL_INFO(opline->op2.constant, LITERAL_CLASS, 1, 1, 2);
+ }
+ if (ZEND_OP1_TYPE(opline) == IS_CONST) {
optimizer_literal_class_info(
info,
- opline->op1_type,
- opline->op1,
- opline->op2.constant,
- LITERAL_CLASS_CONST, (ZEND_OP1_TYPE(opline) == IS_CONST) ? 1 : 2, 1,
+ opline->op2_type,
+ opline->op2,
+ opline->op1.constant,
+ LITERAL_STATIC_PROPERTY, 2, 1,
op_array);
}
break;
- case ZEND_FETCH_R:
- case ZEND_FETCH_W:
- case ZEND_FETCH_RW:
- case ZEND_FETCH_IS:
- case ZEND_FETCH_UNSET:
- case ZEND_FETCH_FUNC_ARG:
- case ZEND_UNSET_VAR:
- case ZEND_ISSET_ISEMPTY_VAR:
- if (ZEND_OP2_TYPE(opline) == IS_UNUSED) {
- if (ZEND_OP1_TYPE(opline) == IS_CONST) {
- LITERAL_INFO(opline->op1.constant, LITERAL_VALUE, 1, 0, 1);
- }
- } else {
- if (ZEND_OP2_TYPE(opline) == IS_CONST) {
- LITERAL_INFO(opline->op2.constant, LITERAL_CLASS, 1, 1, 2);
- }
- if (ZEND_OP1_TYPE(opline) == IS_CONST) {
- optimizer_literal_class_info(
- info,
- opline->op2_type,
- opline->op2,
- opline->op1.constant,
- LITERAL_STATIC_PROPERTY, 2, 1,
- op_array);
- }
- }
- break;
case ZEND_FETCH_CLASS:
case ZEND_ADD_INTERFACE:
case ZEND_ADD_TRAIT:
@@ -295,6 +288,12 @@ void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx
cache_size += sizeof(void *);
}
break;
+ case ZEND_DECLARE_FUNCTION:
+ case ZEND_DECLARE_CLASS:
+ case ZEND_DECLARE_INHERITED_CLASS:
+ case ZEND_DECLARE_INHERITED_CLASS_DELAYED:
+ LITERAL_INFO(opline->op1.constant, LITERAL_VALUE, 1, 0, 2);
+ break;
case ZEND_RECV:
case ZEND_RECV_VARIADIC:
case ZEND_VERIFY_RETURN_TYPE:
@@ -422,9 +421,11 @@ void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx
case IS_CONSTANT:
if (info[i].flags & LITERAL_MAY_MERGE) {
if (info[i].flags & LITERAL_EX_OBJ) {
- int key_len = MAX_LENGTH_OF_LONG + sizeof("->") - 1 + Z_STRLEN(op_array->literals[i]);
+ int key_len = sizeof("$this->") - 1 + Z_STRLEN(op_array->literals[i]);
key = zend_string_alloc(key_len, 0);
- ZSTR_LEN(key) = snprintf(ZSTR_VAL(key), ZSTR_LEN(key)-1, "%d->%s", info[i].u.num, Z_STRVAL(op_array->literals[i]));
+ memcpy(ZSTR_VAL(key), "$this->", sizeof("$this->") - 1);
+ memcpy(ZSTR_VAL(key) + sizeof("$this->") - 1, Z_STRVAL(op_array->literals[i]), Z_STRLEN(op_array->literals[i]) + 1);
+ ZSTR_LEN(key) = key_len;
} else if (info[i].flags & LITERAL_EX_CLASS) {
int key_len;
zval *class_name = &op_array->literals[(info[i].u.num < i) ? map[info[i].u.num] : info[i].u.num];
diff --git a/ext/opcache/Optimizer/dfa_pass.c b/ext/opcache/Optimizer/dfa_pass.c
new file mode 100644
index 0000000000..a64619bfe8
--- /dev/null
+++ b/ext/opcache/Optimizer/dfa_pass.c
@@ -0,0 +1,650 @@
+/*
+ +----------------------------------------------------------------------+
+ | Zend OPcache |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1998-2016 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> |
+ +----------------------------------------------------------------------+
+*/
+
+#include "php.h"
+#include "Optimizer/zend_optimizer.h"
+#include "Optimizer/zend_optimizer_internal.h"
+#include "zend_API.h"
+#include "zend_constants.h"
+#include "zend_execute.h"
+#include "zend_vm.h"
+#include "zend_bitset.h"
+#include "zend_cfg.h"
+#include "zend_ssa.h"
+#include "zend_func_info.h"
+#include "zend_call_graph.h"
+#include "zend_inference.h"
+#include "zend_dump.h"
+
+int zend_dfa_analyze_op_array(zend_op_array *op_array, zend_optimizer_ctx *ctx, zend_ssa *ssa, uint32_t *flags)
+{
+ uint32_t build_flags;
+
+ if (op_array->last_try_catch) {
+ /* TODO: we can't analyze functions with try/catch/finally ??? */
+ return FAILURE;
+ }
+
+ /* Build SSA */
+ memset(ssa, 0, sizeof(zend_ssa));
+
+ if (zend_build_cfg(&ctx->arena, op_array,
+ ZEND_CFG_NO_ENTRY_PREDECESSORS, &ssa->cfg, flags) != SUCCESS) {
+ return FAILURE;
+ }
+
+ if (*flags & ZEND_FUNC_INDIRECT_VAR_ACCESS) {
+ /* TODO: we can't analyze functions with indirect variable access ??? */
+ return FAILURE;
+ }
+
+ if (zend_cfg_build_predecessors(&ctx->arena, &ssa->cfg) != SUCCESS) {
+ return FAILURE;
+ }
+
+ if (ctx->debug_level & ZEND_DUMP_DFA_CFG) {
+ zend_dump_op_array(op_array, ZEND_DUMP_CFG, "dfa cfg", &ssa->cfg);
+ }
+
+ /* Compute Dominators Tree */
+ if (zend_cfg_compute_dominators_tree(op_array, &ssa->cfg) != SUCCESS) {
+ return FAILURE;
+ }
+
+ /* Identify reducible and irreducible loops */
+ if (zend_cfg_identify_loops(op_array, &ssa->cfg, flags) != SUCCESS) {
+ return FAILURE;
+ }
+
+ if (ctx->debug_level & ZEND_DUMP_DFA_DOMINATORS) {
+ zend_dump_dominators(op_array, &ssa->cfg);
+ }
+
+ build_flags = 0;
+ if (ctx->debug_level & ZEND_DUMP_DFA_LIVENESS) {
+ build_flags |= ZEND_SSA_DEBUG_LIVENESS;
+ }
+ if (ctx->debug_level & ZEND_DUMP_DFA_PHI) {
+ build_flags |= ZEND_SSA_DEBUG_PHI_PLACEMENT;
+ }
+ if (zend_build_ssa(&ctx->arena, ctx->script, op_array, build_flags, ssa, flags) != SUCCESS) {
+ return FAILURE;
+ }
+
+ if (ctx->debug_level & ZEND_DUMP_DFA_SSA) {
+ zend_dump_op_array(op_array, ZEND_DUMP_SSA, "before dfa pass", ssa);
+ }
+
+
+ if (zend_ssa_compute_use_def_chains(&ctx->arena, op_array, ssa) != SUCCESS){
+ return FAILURE;
+ }
+
+ if (zend_ssa_find_false_dependencies(op_array, ssa) != SUCCESS) {
+ return FAILURE;
+ }
+
+ if (zend_ssa_find_sccs(op_array, ssa) != SUCCESS){
+ return FAILURE;
+ }
+
+ if (zend_ssa_inference(&ctx->arena, op_array, ctx->script, ssa) != SUCCESS) {
+ return FAILURE;
+ }
+
+ if (ctx->debug_level & ZEND_DUMP_DFA_SSA_VARS) {
+ zend_dump_ssa_variables(op_array, ssa, 0);
+ }
+
+ return SUCCESS;
+}
+
+static void zend_ssa_remove_nops(zend_op_array *op_array, zend_ssa *ssa)
+{
+ zend_basic_block *blocks = ssa->cfg.blocks;
+ zend_basic_block *end = blocks + ssa->cfg.blocks_count;
+ zend_basic_block *b;
+ zend_func_info *func_info;
+ int j;
+ uint32_t i;
+ uint32_t target = 0;
+ uint32_t *shiftlist;
+ ALLOCA_FLAG(use_heap);
+
+ shiftlist = (uint32_t *)do_alloca(sizeof(uint32_t) * op_array->last, use_heap);
+ memset(shiftlist, 0, sizeof(uint32_t) * op_array->last);
+ for (b = blocks; b < end; b++) {
+ if (b->flags & (ZEND_BB_REACHABLE|ZEND_BB_UNREACHABLE_FREE)) {
+ uint32_t end;
+ if (b->flags & ZEND_BB_UNREACHABLE_FREE) {
+ /* Only keep the FREE for the loop var */
+ ZEND_ASSERT(op_array->opcodes[b->start].opcode == ZEND_FREE
+ || op_array->opcodes[b->start].opcode == ZEND_FE_FREE);
+ b->len = 1;
+ }
+
+ end = b->start + b->len;
+ i = b->start;
+ b->start = target;
+ while (i < end) {
+ shiftlist[i] = i - target;
+ if (EXPECTED(op_array->opcodes[i].opcode != ZEND_NOP) ||
+ /*keep NOP to support ZEND_VM_SMART_BRANCH */
+ (i > 0 &&
+ i + 1 < op_array->last &&
+ (op_array->opcodes[i+1].opcode == ZEND_JMPZ ||
+ op_array->opcodes[i+1].opcode == ZEND_JMPNZ) &&
+ (op_array->opcodes[i-1].opcode == ZEND_IS_IDENTICAL ||
+ op_array->opcodes[i-1].opcode == ZEND_IS_NOT_IDENTICAL ||
+ op_array->opcodes[i-1].opcode == ZEND_IS_EQUAL ||
+ op_array->opcodes[i-1].opcode == ZEND_IS_NOT_EQUAL ||
+ op_array->opcodes[i-1].opcode == ZEND_IS_SMALLER ||
+ op_array->opcodes[i-1].opcode == ZEND_IS_SMALLER_OR_EQUAL ||
+ op_array->opcodes[i-1].opcode == ZEND_CASE ||
+ op_array->opcodes[i-1].opcode == ZEND_ISSET_ISEMPTY_VAR ||
+ op_array->opcodes[i-1].opcode == ZEND_ISSET_ISEMPTY_STATIC_PROP ||
+ op_array->opcodes[i-1].opcode == ZEND_ISSET_ISEMPTY_DIM_OBJ ||
+ op_array->opcodes[i-1].opcode == ZEND_ISSET_ISEMPTY_PROP_OBJ ||
+ op_array->opcodes[i-1].opcode == ZEND_INSTANCEOF ||
+ op_array->opcodes[i-1].opcode == ZEND_TYPE_CHECK ||
+ op_array->opcodes[i-1].opcode == ZEND_DEFINED))) {
+ if (i != target) {
+ op_array->opcodes[target] = op_array->opcodes[i];
+ ssa->ops[target] = ssa->ops[i];
+ }
+ target++;
+ }
+ i++;
+ }
+ if (target != end && b->len != 0) {
+ zend_op *opline;
+ zend_op *new_opline;
+
+ opline = op_array->opcodes + end - 1;
+ b->len = target - b->start;
+ new_opline = op_array->opcodes + target - 1;
+ switch (new_opline->opcode) {
+ case ZEND_JMP:
+ case ZEND_FAST_CALL:
+ ZEND_SET_OP_JMP_ADDR(new_opline, new_opline->op1, ZEND_OP1_JMP_ADDR(opline));
+ break;
+ case ZEND_JMPZNZ:
+ new_opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, new_opline, ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value));
+ /* break missing intentionally */
+ case ZEND_JMPZ:
+ case ZEND_JMPNZ:
+ case ZEND_JMPZ_EX:
+ case ZEND_JMPNZ_EX:
+ case ZEND_FE_RESET_R:
+ case ZEND_FE_RESET_RW:
+ case ZEND_JMP_SET:
+ case ZEND_COALESCE:
+ case ZEND_ASSERT_CHECK:
+ ZEND_SET_OP_JMP_ADDR(new_opline, new_opline->op2, ZEND_OP2_JMP_ADDR(opline));
+ break;
+ case ZEND_CATCH:
+ if (!opline->result.num) {
+ new_opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, new_opline, ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value));
+ }
+ break;
+ case ZEND_DECLARE_ANON_CLASS:
+ case ZEND_DECLARE_ANON_INHERITED_CLASS:
+ case ZEND_FE_FETCH_R:
+ case ZEND_FE_FETCH_RW:
+ new_opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, new_opline, ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value));
+ break;
+ }
+ }
+ }
+ }
+
+ if (target != op_array->last) {
+ /* reset rest opcodes */
+ for (i = target; i < op_array->last; i++) {
+ MAKE_NOP(op_array->opcodes + i);
+ }
+
+ /* update SSA variables */
+ for (j = 0; j < ssa->vars_count; j++) {
+ if (ssa->vars[j].definition >= 0) {
+ ssa->vars[j].definition -= shiftlist[ssa->vars[j].definition];
+ }
+ if (ssa->vars[j].use_chain >= 0) {
+ ssa->vars[j].use_chain -= shiftlist[ssa->vars[j].use_chain];
+ }
+ }
+ for (i = 0; i < op_array->last; i++) {
+ if (ssa->ops[i].op1_use_chain >= 0) {
+ ssa->ops[i].op1_use_chain -= shiftlist[ssa->ops[i].op1_use_chain];
+ }
+ if (ssa->ops[i].op2_use_chain >= 0) {
+ ssa->ops[i].op2_use_chain -= shiftlist[ssa->ops[i].op2_use_chain];
+ }
+ if (ssa->ops[i].res_use_chain >= 0) {
+ ssa->ops[i].res_use_chain -= shiftlist[ssa->ops[i].res_use_chain];
+ }
+ }
+
+ /* update branch targets */
+ for (b = blocks; b < end; b++) {
+ if ((b->flags & ZEND_BB_REACHABLE) && b->len != 0) {
+ zend_op *opline = op_array->opcodes + b->start + b->len - 1;
+
+ switch (opline->opcode) {
+ case ZEND_JMP:
+ case ZEND_FAST_CALL:
+ ZEND_SET_OP_JMP_ADDR(opline, opline->op1, ZEND_OP1_JMP_ADDR(opline) - shiftlist[ZEND_OP1_JMP_ADDR(opline) - op_array->opcodes]);
+ break;
+ case ZEND_JMPZNZ:
+ opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, opline, ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value) - shiftlist[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)]);
+ /* break missing intentionally */
+ case ZEND_JMPZ:
+ case ZEND_JMPNZ:
+ case ZEND_JMPZ_EX:
+ case ZEND_JMPNZ_EX:
+ case ZEND_FE_RESET_R:
+ case ZEND_FE_RESET_RW:
+ case ZEND_JMP_SET:
+ case ZEND_COALESCE:
+ case ZEND_ASSERT_CHECK:
+ ZEND_SET_OP_JMP_ADDR(opline, opline->op2, ZEND_OP2_JMP_ADDR(opline) - shiftlist[ZEND_OP2_JMP_ADDR(opline) - op_array->opcodes]);
+ break;
+ case ZEND_DECLARE_ANON_CLASS:
+ case ZEND_DECLARE_ANON_INHERITED_CLASS:
+ case ZEND_FE_FETCH_R:
+ case ZEND_FE_FETCH_RW:
+ case ZEND_CATCH:
+ opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, opline, ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value) - shiftlist[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)]);
+ break;
+ }
+ }
+ }
+
+ /* update brk/cont array */
+ for (j = 0; j < op_array->last_live_range; j++) {
+ op_array->live_range[j].start -= shiftlist[op_array->live_range[j].start];
+ op_array->live_range[j].end -= shiftlist[op_array->live_range[j].end];
+ }
+
+ /* update try/catch array */
+ for (j = 0; j < op_array->last_try_catch; j++) {
+ op_array->try_catch_array[j].try_op -= shiftlist[op_array->try_catch_array[j].try_op];
+ op_array->try_catch_array[j].catch_op -= shiftlist[op_array->try_catch_array[j].catch_op];
+ if (op_array->try_catch_array[j].finally_op) {
+ op_array->try_catch_array[j].finally_op -= shiftlist[op_array->try_catch_array[j].finally_op];
+ op_array->try_catch_array[j].finally_end -= shiftlist[op_array->try_catch_array[j].finally_end];
+ }
+ }
+
+ /* update early binding list */
+ if (op_array->early_binding != (uint32_t)-1) {
+ uint32_t *opline_num = &op_array->early_binding;
+
+ do {
+ *opline_num -= shiftlist[*opline_num];
+ opline_num = &ZEND_RESULT(&op_array->opcodes[*opline_num]).opline_num;
+ } while (*opline_num != (uint32_t)-1);
+ }
+
+ /* update call graph */
+ func_info = ZEND_FUNC_INFO(op_array);
+ if (func_info) {
+ zend_call_info *call_info = func_info->callee_info;
+ while (call_info) {
+ call_info->caller_init_opline -=
+ shiftlist[call_info->caller_init_opline - op_array->opcodes];
+ call_info->caller_call_opline -=
+ shiftlist[call_info->caller_call_opline - op_array->opcodes];
+ call_info = call_info->next_callee;
+ }
+ }
+
+ op_array->last = target;
+ }
+ free_alloca(shiftlist, use_heap);
+}
+
+static inline zend_bool can_elide_return_type_check(
+ zend_op_array *op_array, zend_ssa *ssa, zend_ssa_op *ssa_op) {
+ zend_arg_info *info = &op_array->arg_info[-1];
+ zend_ssa_var_info *use_info = &ssa->var_info[ssa_op->op1_use];
+ zend_ssa_var_info *def_info = &ssa->var_info[ssa_op->op1_def];
+
+ /* A type is possible that is not in the allowed types */
+ if ((use_info->type & (MAY_BE_ANY|MAY_BE_UNDEF)) & ~(def_info->type & MAY_BE_ANY)) {
+ return 0;
+ }
+
+ if (info->type_hint == IS_CALLABLE) {
+ return 0;
+ }
+
+ if (info->class_name) {
+ if (!use_info->ce || !def_info->ce || !instanceof_function(use_info->ce, def_info->ce)) {
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+static zend_bool opline_supports_assign_contraction(zend_ssa *ssa, zend_op *opline, int src_var) {
+ if (opline->opcode == ZEND_NEW) {
+ /* see Zend/tests/generators/aborted_yield_during_new.phpt */
+ return 0;
+ }
+
+ if (opline->opcode == ZEND_DO_ICALL || opline->opcode == ZEND_DO_UCALL
+ || opline->opcode == ZEND_DO_FCALL || opline->opcode == ZEND_DO_FCALL_BY_NAME) {
+ /* Function calls may dtor the return value after it has already been written -- allow
+ * direct assignment only for types where a double-dtor does not matter. */
+ uint32_t type = ssa->var_info[src_var].type;
+ uint32_t simple = MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG|MAY_BE_DOUBLE;
+ return !((type & MAY_BE_ANY) & ~simple);
+ }
+
+ return 1;
+}
+
+void zend_dfa_optimize_op_array(zend_op_array *op_array, zend_optimizer_ctx *ctx, zend_ssa *ssa)
+{
+ if (ctx->debug_level & ZEND_DUMP_BEFORE_DFA_PASS) {
+ zend_dump_op_array(op_array, ZEND_DUMP_SSA, "before dfa pass", ssa);
+ }
+
+ if (ssa->var_info) {
+ int op_1;
+ int v;
+ int remove_nops = 0;
+ zend_op *opline;
+ zval tmp;
+
+ for (v = op_array->last_var; v < ssa->vars_count; v++) {
+
+ op_1 = ssa->vars[v].definition;
+
+ if (op_1 < 0) {
+ continue;
+ }
+
+ opline = op_array->opcodes + op_1;
+
+ /* Convert LONG constants to DOUBLE */
+ if (ssa->var_info[v].use_as_double) {
+ if (opline->opcode == ZEND_ASSIGN
+ && opline->op2_type == IS_CONST
+ && ssa->ops[op_1].op1_def == v
+ && !RETURN_VALUE_USED(opline)
+ ) {
+
+// op_1: ASSIGN ? -> #v [use_as_double], long(?) => ASSIGN ? -> #v, double(?)
+
+ zval *zv = CT_CONSTANT_EX(op_array, opline->op2.constant);
+ ZEND_ASSERT(Z_TYPE_INFO_P(zv) == IS_LONG);
+ ZVAL_DOUBLE(&tmp, zval_get_double(zv));
+ opline->op2.constant = zend_optimizer_add_literal(op_array, &tmp);
+
+ } else if (opline->opcode == ZEND_QM_ASSIGN
+ && opline->op1_type == IS_CONST
+ ) {
+
+// op_1: QM_ASSIGN #v [use_as_double], long(?) => QM_ASSIGN #v, double(?)
+
+ zval *zv = CT_CONSTANT_EX(op_array, opline->op1.constant);
+ ZEND_ASSERT(Z_TYPE_INFO_P(zv) == IS_LONG);
+ ZVAL_DOUBLE(&tmp, zval_get_double(zv));
+ opline->op1.constant = zend_optimizer_add_literal(op_array, &tmp);
+ }
+
+ } else {
+ if (opline->opcode == ZEND_ADD
+ || opline->opcode == ZEND_SUB
+ || opline->opcode == ZEND_MUL
+ || opline->opcode == ZEND_IS_EQUAL
+ || opline->opcode == ZEND_IS_NOT_EQUAL
+ || opline->opcode == ZEND_IS_SMALLER
+ || opline->opcode == ZEND_IS_SMALLER_OR_EQUAL
+ ) {
+
+ if (opline->op1_type == IS_CONST
+ && opline->op2_type != IS_CONST
+ && (OP2_INFO() & MAY_BE_ANY) == MAY_BE_DOUBLE
+ && Z_TYPE_INFO_P(CT_CONSTANT_EX(op_array, opline->op1.constant)) == IS_LONG
+ ) {
+
+// op_1: #v.? = ADD long(?), #?.? [double] => #v.? = ADD double(?), #?.? [double]
+
+ zval *zv = CT_CONSTANT_EX(op_array, opline->op1.constant);
+ ZVAL_DOUBLE(&tmp, zval_get_double(zv));
+ opline->op1.constant = zend_optimizer_add_literal(op_array, &tmp);
+
+ } else if (opline->op1_type != IS_CONST
+ && opline->op2_type == IS_CONST
+ && (OP1_INFO() & MAY_BE_ANY) == MAY_BE_DOUBLE
+ && Z_TYPE_INFO_P(CT_CONSTANT_EX(op_array, opline->op2.constant)) == IS_LONG
+ ) {
+
+// op_1: #v.? = ADD #?.? [double], long(?) => #v.? = ADD #?.? [double], double(?)
+
+ zval *zv = CT_CONSTANT_EX(op_array, opline->op2.constant);
+ ZVAL_DOUBLE(&tmp, zval_get_double(zv));
+ opline->op2.constant = zend_optimizer_add_literal(op_array, &tmp);
+ }
+ }
+ }
+
+ if (ssa->vars[v].var >= op_array->last_var) {
+ /* skip TMP and VAR */
+ continue;
+ }
+
+ if (opline->opcode == ZEND_ASSIGN
+ && ssa->ops[op_1].op1_def == v
+ && !RETURN_VALUE_USED(opline)
+ ) {
+ int orig_var = ssa->ops[op_1].op1_use;
+
+ if (orig_var >= 0
+ && !(ssa->var_info[orig_var].type & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))
+ ) {
+
+ int src_var = ssa->ops[op_1].op2_use;
+
+ if ((opline->op2_type & (IS_TMP_VAR|IS_VAR))
+ && src_var >= 0
+ && !(ssa->var_info[src_var].type & MAY_BE_REF)
+ && ssa->vars[src_var].definition >= 0
+ && ssa->ops[ssa->vars[src_var].definition].result_def == src_var
+ && ssa->ops[ssa->vars[src_var].definition].result_use < 0
+ && ssa->vars[src_var].use_chain == op_1
+ && ssa->ops[op_1].op2_use_chain < 0
+ && !ssa->vars[src_var].phi_use_chain
+ && !ssa->vars[src_var].sym_use_chain
+ && opline_supports_assign_contraction(
+ ssa, &op_array->opcodes[ssa->vars[src_var].definition], src_var)
+ ) {
+
+ int op_2 = ssa->vars[src_var].definition;
+
+// op_2: #src_var.T = OP ... => #v.CV = OP ...
+// op_1: ASSIGN #orig_var.CV [undef,scalar] -> #v.CV, #src_var.T NOP
+
+ if (zend_ssa_unlink_use_chain(ssa, op_1, orig_var)) {
+ /* Reconstruct SSA */
+ ssa->vars[v].definition = op_2;
+ ssa->ops[op_2].result_def = v;
+
+ ssa->vars[src_var].definition = -1;
+ ssa->vars[src_var].use_chain = -1;
+
+ ssa->ops[op_1].op1_use = -1;
+ ssa->ops[op_1].op2_use = -1;
+ ssa->ops[op_1].op1_def = -1;
+ ssa->ops[op_1].op1_use_chain = -1;
+
+ /* Update opcodes */
+ op_array->opcodes[op_2].result_type = opline->op1_type;
+ op_array->opcodes[op_2].result.var = opline->op1.var;
+ MAKE_NOP(opline);
+ remove_nops = 1;
+ }
+ } else if (opline->op2_type == IS_CONST
+ || ((opline->op2_type & (IS_TMP_VAR|IS_VAR|IS_CV))
+ && ssa->ops[op_1].op2_use >= 0
+ && ssa->ops[op_1].op2_def < 0)
+ ) {
+
+// op_1: ASSIGN #orig_var.CV [undef,scalar] -> #v.CV, CONST|TMPVAR => QM_ASSIGN v.CV, CONST|TMPVAR
+
+ if (zend_ssa_unlink_use_chain(ssa, op_1, orig_var)) {
+ /* Reconstruct SSA */
+ ssa->ops[op_1].result_def = v;
+ ssa->ops[op_1].op1_def = -1;
+ ssa->ops[op_1].op1_use = ssa->ops[op_1].op2_use;
+ ssa->ops[op_1].op1_use_chain = ssa->ops[op_1].op2_use_chain;
+ ssa->ops[op_1].op2_use = -1;
+ ssa->ops[op_1].op2_use_chain = -1;
+
+ /* Update opcode */
+ opline->result_type = opline->op1_type;
+ opline->result.var = opline->op1.var;
+ opline->op1_type = opline->op2_type;
+ opline->op1.var = opline->op2.var;
+ opline->op2_type = IS_UNUSED;
+ opline->op2.var = 0;
+ opline->opcode = ZEND_QM_ASSIGN;
+ }
+ }
+ }
+
+ } else if (opline->opcode == ZEND_ASSIGN_ADD
+ && opline->extended_value == 0
+ && ssa->ops[op_1].op1_def == v
+ && opline->op2_type == IS_CONST
+ && Z_TYPE_P(CT_CONSTANT_EX(op_array, opline->op2.constant)) == IS_LONG
+ && Z_LVAL_P(CT_CONSTANT_EX(op_array, opline->op2.constant)) == 1
+ && ssa->ops[op_1].op1_use >= 0
+ && !(ssa->var_info[ssa->ops[op_1].op1_use].type & (MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))) {
+
+// op_1: ASSIGN_ADD #?.CV [undef,null,int,foat] ->#v.CV, int(1) => PRE_INC #?.CV ->#v.CV
+
+ opline->opcode = ZEND_PRE_INC;
+ SET_UNUSED(opline->op2);
+
+ } else if (opline->opcode == ZEND_ASSIGN_SUB
+ && opline->extended_value == 0
+ && ssa->ops[op_1].op1_def == v
+ && opline->op2_type == IS_CONST
+ && Z_TYPE_P(CT_CONSTANT_EX(op_array, opline->op2.constant)) == IS_LONG
+ && Z_LVAL_P(CT_CONSTANT_EX(op_array, opline->op2.constant)) == 1
+ && ssa->ops[op_1].op1_use >= 0
+ && !(ssa->var_info[ssa->ops[op_1].op1_use].type & (MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))) {
+
+// op_1: ASSIGN_SUB #?.CV [undef,null,int,foat] -> #v.CV, int(1) => PRE_DEC #?.CV ->#v.CV
+
+ opline->opcode = ZEND_PRE_DEC;
+ SET_UNUSED(opline->op2);
+
+ } else if (opline->opcode == ZEND_VERIFY_RETURN_TYPE
+ && ssa->ops[op_1].op1_def == v
+ && ssa->ops[op_1].op1_use >= 0
+ && ssa->ops[op_1].op1_use_chain == -1
+ && ssa->vars[v].use_chain >= 0
+ && can_elide_return_type_check(op_array, ssa, &ssa->ops[op_1])) {
+
+// op_1: VERIFY_RETURN_TYPE #orig_var.CV [T] -> #v.CV [T] => NOP
+
+ int orig_var = ssa->ops[op_1].op1_use;
+ int ret = ssa->vars[v].use_chain;
+
+ ssa->vars[orig_var].use_chain = ret;
+ ssa->ops[ret].op1_use = orig_var;
+
+ ssa->vars[v].definition = -1;
+ ssa->vars[v].use_chain = -1;
+
+ ssa->ops[op_1].op1_def = -1;
+ ssa->ops[op_1].op1_use = -1;
+
+ MAKE_NOP(opline);
+ remove_nops = 1;
+
+ } else if (ssa->ops[op_1].op1_def == v
+ && !RETURN_VALUE_USED(opline)
+ && ssa->ops[op_1].op1_use >= 0
+ && !(ssa->var_info[ssa->ops[op_1].op1_use].type & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))
+ && (opline->opcode == ZEND_ASSIGN_ADD
+ || opline->opcode == ZEND_ASSIGN_SUB
+ || opline->opcode == ZEND_ASSIGN_MUL
+ || opline->opcode == ZEND_ASSIGN_DIV
+ || opline->opcode == ZEND_ASSIGN_MOD
+ || opline->opcode == ZEND_ASSIGN_SL
+ || opline->opcode == ZEND_ASSIGN_SR
+ || opline->opcode == ZEND_ASSIGN_BW_OR
+ || opline->opcode == ZEND_ASSIGN_BW_AND
+ || opline->opcode == ZEND_ASSIGN_BW_XOR)
+ && opline->extended_value == 0) {
+
+// op_1: ASSIGN_ADD #orig_var.CV [undef,null,bool,int,double] -> #v.CV, ? => #v.CV = ADD #orig_var.CV, ?
+
+ /* Reconstruct SSA */
+ ssa->ops[op_1].result_def = ssa->ops[op_1].op1_def;
+ ssa->ops[op_1].op1_def = -1;
+
+ /* Update opcode */
+ opline->opcode -= (ZEND_ASSIGN_ADD - ZEND_ADD);
+ opline->result_type = opline->op1_type;
+ opline->result.var = opline->op1.var;
+
+ }
+ }
+
+ if (remove_nops) {
+ zend_ssa_remove_nops(op_array, ssa);
+ }
+ }
+
+ if (ctx->debug_level & ZEND_DUMP_AFTER_DFA_PASS) {
+ zend_dump_op_array(op_array, ZEND_DUMP_SSA, "after dfa pass", ssa);
+ }
+}
+
+void zend_optimize_dfa(zend_op_array *op_array, zend_optimizer_ctx *ctx)
+{
+ void *checkpoint = zend_arena_checkpoint(ctx->arena);
+ uint32_t flags = 0;
+ zend_ssa ssa;
+
+ if (zend_dfa_analyze_op_array(op_array, ctx, &ssa, &flags) != SUCCESS) {
+ zend_arena_release(&ctx->arena, checkpoint);
+ return;
+ }
+
+ zend_dfa_optimize_op_array(op_array, ctx, &ssa);
+
+ /* Destroy SSA */
+ zend_arena_release(&ctx->arena, checkpoint);
+}
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * indent-tabs-mode: t
+ * End:
+ */
diff --git a/ext/opcache/Optimizer/nop_removal.c b/ext/opcache/Optimizer/nop_removal.c
index dbdf08e89f..3ebbdad8cf 100644
--- a/ext/opcache/Optimizer/nop_removal.c
+++ b/ext/opcache/Optimizer/nop_removal.c
@@ -39,15 +39,15 @@ void zend_optimizer_nop_removal(zend_op_array *op_array)
uint32_t *shiftlist;
ALLOCA_FLAG(use_heap);
- shiftlist = (uint32_t *)DO_ALLOCA(sizeof(uint32_t) * op_array->last);
+ shiftlist = (uint32_t *)do_alloca(sizeof(uint32_t) * op_array->last, use_heap);
i = new_count = shift = 0;
end = op_array->opcodes + op_array->last;
for (opline = op_array->opcodes; opline < end; opline++) {
/* Kill JMP-over-NOP-s */
- if (opline->opcode == ZEND_JMP && ZEND_OP1(opline).opline_num > i) {
+ if (opline->opcode == ZEND_JMP && ZEND_OP1_JMP_ADDR(opline) > op_array->opcodes + i) {
/* check if there are only NOPs under the branch */
- zend_op *target = op_array->opcodes + ZEND_OP1(opline).opline_num - 1;
+ zend_op *target = ZEND_OP1_JMP_ADDR(opline) - 1;
while (target->opcode == ZEND_NOP) {
target--;
@@ -63,7 +63,40 @@ void zend_optimizer_nop_removal(zend_op_array *op_array)
shift++;
} else {
if (shift) {
- op_array->opcodes[new_count] = *opline;
+ zend_op *new_opline = op_array->opcodes + new_count;
+
+ *new_opline = *opline;
+ switch (new_opline->opcode) {
+ case ZEND_JMP:
+ case ZEND_FAST_CALL:
+ ZEND_SET_OP_JMP_ADDR(new_opline, new_opline->op1, ZEND_OP1_JMP_ADDR(opline));
+ break;
+ case ZEND_JMPZNZ:
+ new_opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, new_opline, ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value));
+ /* break missing intentionally */
+ case ZEND_JMPZ:
+ case ZEND_JMPNZ:
+ case ZEND_JMPZ_EX:
+ case ZEND_JMPNZ_EX:
+ case ZEND_FE_RESET_R:
+ case ZEND_FE_RESET_RW:
+ case ZEND_JMP_SET:
+ case ZEND_COALESCE:
+ case ZEND_ASSERT_CHECK:
+ ZEND_SET_OP_JMP_ADDR(new_opline, new_opline->op2, ZEND_OP2_JMP_ADDR(opline));
+ break;
+ case ZEND_CATCH:
+ if (!opline->result.num) {
+ new_opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, new_opline, ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value));
+ }
+ break;
+ case ZEND_DECLARE_ANON_CLASS:
+ case ZEND_DECLARE_ANON_INHERITED_CLASS:
+ case ZEND_FE_FETCH_R:
+ case ZEND_FE_FETCH_RW:
+ new_opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, new_opline, ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value));
+ break;
+ }
}
new_count++;
}
@@ -78,41 +111,36 @@ void zend_optimizer_nop_removal(zend_op_array *op_array)
switch (opline->opcode) {
case ZEND_JMP:
case ZEND_FAST_CALL:
- case ZEND_DECLARE_ANON_CLASS:
- case ZEND_DECLARE_ANON_INHERITED_CLASS:
- ZEND_OP1(opline).opline_num -= shiftlist[ZEND_OP1(opline).opline_num];
+ ZEND_SET_OP_JMP_ADDR(opline, opline->op1, ZEND_OP1_JMP_ADDR(opline) - shiftlist[ZEND_OP1_JMP_ADDR(opline) - op_array->opcodes]);
break;
+ case ZEND_JMPZNZ:
+ opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, opline, ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value) - shiftlist[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)]);
+ /* break missing intentionally */
case ZEND_JMPZ:
case ZEND_JMPNZ:
case ZEND_JMPZ_EX:
case ZEND_JMPNZ_EX:
case ZEND_FE_RESET_R:
case ZEND_FE_RESET_RW:
- case ZEND_NEW:
case ZEND_JMP_SET:
case ZEND_COALESCE:
case ZEND_ASSERT_CHECK:
- ZEND_OP2(opline).opline_num -= shiftlist[ZEND_OP2(opline).opline_num];
+ ZEND_SET_OP_JMP_ADDR(opline, opline->op2, ZEND_OP2_JMP_ADDR(opline) - shiftlist[ZEND_OP2_JMP_ADDR(opline) - op_array->opcodes]);
break;
+ case ZEND_DECLARE_ANON_CLASS:
+ case ZEND_DECLARE_ANON_INHERITED_CLASS:
case ZEND_FE_FETCH_R:
case ZEND_FE_FETCH_RW:
- opline->extended_value -= shiftlist[opline->extended_value];
- break;
- case ZEND_JMPZNZ:
- ZEND_OP2(opline).opline_num -= shiftlist[ZEND_OP2(opline).opline_num];
- opline->extended_value -= shiftlist[opline->extended_value];
- break;
case ZEND_CATCH:
- opline->extended_value -= shiftlist[opline->extended_value];
+ opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, opline, ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value) - shiftlist[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)]);
break;
}
}
/* update brk/cont array */
- for (j = 0; j < op_array->last_brk_cont; j++) {
- op_array->brk_cont_array[j].brk -= shiftlist[op_array->brk_cont_array[j].brk];
- op_array->brk_cont_array[j].cont -= shiftlist[op_array->brk_cont_array[j].cont];
- op_array->brk_cont_array[j].start -= shiftlist[op_array->brk_cont_array[j].start];
+ for (j = 0; j < op_array->last_live_range; j++) {
+ op_array->live_range[j].start -= shiftlist[op_array->live_range[j].start];
+ op_array->live_range[j].end -= shiftlist[op_array->live_range[j].end];
}
/* update try/catch array */
@@ -135,5 +163,5 @@ void zend_optimizer_nop_removal(zend_op_array *op_array)
} while (*opline_num != (uint32_t)-1);
}
}
- FREE_ALLOCA(shiftlist);
+ free_alloca(shiftlist, use_heap);
}
diff --git a/ext/opcache/Optimizer/optimize_func_calls.c b/ext/opcache/Optimizer/optimize_func_calls.c
index 5804a5fb0e..75120d2501 100644
--- a/ext/opcache/Optimizer/optimize_func_calls.c
+++ b/ext/opcache/Optimizer/optimize_func_calls.c
@@ -29,6 +29,9 @@
#include "zend_execute.h"
#include "zend_vm.h"
+#define ZEND_OP1_IS_CONST_STRING(opline) \
+ (ZEND_OP1_TYPE(opline) == IS_CONST && \
+ Z_TYPE(op_array->literals[(opline)->op1.constant]) == IS_STRING)
#define ZEND_OP2_IS_CONST_STRING(opline) \
(ZEND_OP2_TYPE(opline) == IS_CONST && \
Z_TYPE(op_array->literals[(opline)->op2.constant]) == IS_STRING)
@@ -38,7 +41,7 @@ typedef struct _optimizer_call_info {
zend_op *opline;
} optimizer_call_info;
-void optimize_func_calls(zend_op_array *op_array, zend_optimizer_ctx *ctx)
+void zend_optimize_func_calls(zend_op_array *op_array, zend_optimizer_ctx *ctx)
{
zend_op *opline = op_array->opcodes;
zend_op *end = opline + op_array->last;
@@ -56,19 +59,13 @@ void optimize_func_calls(zend_op_array *op_array, zend_optimizer_ctx *ctx)
switch (opline->opcode) {
case ZEND_INIT_FCALL_BY_NAME:
case ZEND_INIT_NS_FCALL_BY_NAME:
- if (ZEND_OP2_IS_CONST_STRING(opline)) {
- zend_function *func;
- zval *function_name = &op_array->literals[opline->op2.constant + 1];
- if ((func = zend_hash_find_ptr(&ctx->script->function_table,
- Z_STR_P(function_name))) != NULL) {
- call_stack[call].func = func;
- }
- }
+ case ZEND_INIT_STATIC_METHOD_CALL:
+ case ZEND_INIT_METHOD_CALL:
+ call_stack[call].func = zend_optimizer_get_called_func(
+ ctx->script, op_array, opline, 0);
/* break missing intentionally */
case ZEND_NEW:
case ZEND_INIT_DYNAMIC_CALL:
- case ZEND_INIT_METHOD_CALL:
- case ZEND_INIT_STATIC_METHOD_CALL:
case ZEND_INIT_FCALL:
case ZEND_INIT_USER_CALL:
call_stack[call].opline = opline;
@@ -88,7 +85,7 @@ void optimize_func_calls(zend_op_array *op_array, zend_optimizer_ctx *ctx)
Z_CACHE_SLOT(op_array->literals[fcall->op2.constant + 1]) = Z_CACHE_SLOT(op_array->literals[fcall->op2.constant]);
literal_dtor(&ZEND_OP2_LITERAL(fcall));
fcall->op2.constant = fcall->op2.constant + 1;
- opline->opcode = zend_get_call_op(ZEND_INIT_FCALL, call_stack[call].func);
+ opline->opcode = zend_get_call_op(fcall, call_stack[call].func);
} else if (fcall->opcode == ZEND_INIT_NS_FCALL_BY_NAME) {
fcall->opcode = ZEND_INIT_FCALL;
fcall->op1.num = zend_vm_calc_used_stack(fcall->extended_value, call_stack[call].func);
@@ -96,7 +93,10 @@ void optimize_func_calls(zend_op_array *op_array, zend_optimizer_ctx *ctx)
literal_dtor(&op_array->literals[fcall->op2.constant]);
literal_dtor(&op_array->literals[fcall->op2.constant + 2]);
fcall->op2.constant = fcall->op2.constant + 1;
- opline->opcode = zend_get_call_op(ZEND_INIT_FCALL, call_stack[call].func);
+ opline->opcode = zend_get_call_op(fcall, call_stack[call].func);
+ } else if (fcall->opcode == ZEND_INIT_STATIC_METHOD_CALL
+ || fcall->opcode == ZEND_INIT_METHOD_CALL) {
+ /* We don't have specialized opcodes for this, do nothing */
} else {
ZEND_ASSERT(0);
}
@@ -105,15 +105,24 @@ void optimize_func_calls(zend_op_array *op_array, zend_optimizer_ctx *ctx)
call_stack[call].opline = NULL;
break;
case ZEND_FETCH_FUNC_ARG:
+ case ZEND_FETCH_STATIC_PROP_FUNC_ARG:
case ZEND_FETCH_OBJ_FUNC_ARG:
case ZEND_FETCH_DIM_FUNC_ARG:
if (call_stack[call - 1].func) {
if (ARG_SHOULD_BE_SENT_BY_REF(call_stack[call - 1].func, (opline->extended_value & ZEND_FETCH_ARG_MASK))) {
opline->extended_value &= ZEND_FETCH_TYPE_MASK;
- opline->opcode -= 9;
+ if (opline->opcode != ZEND_FETCH_STATIC_PROP_FUNC_ARG) {
+ opline->opcode -= 9;
+ } else {
+ opline->opcode = ZEND_FETCH_STATIC_PROP_W;
+ }
} else {
opline->extended_value &= ZEND_FETCH_TYPE_MASK;
- opline->opcode -= 12;
+ if (opline->opcode != ZEND_FETCH_STATIC_PROP_FUNC_ARG) {
+ opline->opcode -= 12;
+ } else {
+ opline->opcode = ZEND_FETCH_STATIC_PROP_R;
+ }
}
}
break;
@@ -136,19 +145,20 @@ void optimize_func_calls(zend_op_array *op_array, zend_optimizer_ctx *ctx)
}
}
break;
- case ZEND_SEND_VAR_NO_REF:
- if (!(opline->extended_value & ZEND_ARG_COMPILE_TIME_BOUND) && call_stack[call - 1].func) {
- if (ARG_SHOULD_BE_SENT_BY_REF(call_stack[call - 1].func, opline->op2.num)) {
- opline->extended_value |= ZEND_ARG_COMPILE_TIME_BOUND | ZEND_ARG_SEND_BY_REF;
+ case ZEND_SEND_VAR_NO_REF_EX:
+ if (call_stack[call - 1].func) {
+ if (ARG_MUST_BE_SENT_BY_REF(call_stack[call - 1].func, opline->op2.num)) {
+ opline->opcode = ZEND_SEND_VAR_NO_REF;
+ } else if (ARG_MAY_BE_SENT_BY_REF(call_stack[call - 1].func, opline->op2.num)) {
+ opline->opcode = ZEND_SEND_VAL;
} else {
opline->opcode = ZEND_SEND_VAR;
- opline->extended_value = 0;
}
}
break;
#if 0
case ZEND_SEND_REF:
- if (opline->extended_value != ZEND_ARG_COMPILE_TIME_BOUND && call_stack[call - 1].func) {
+ if (call_stack[call - 1].func) {
/* We won't handle run-time pass by reference */
call_stack[call - 1].opline = NULL;
}
diff --git a/ext/opcache/Optimizer/optimize_temp_vars_5.c b/ext/opcache/Optimizer/optimize_temp_vars_5.c
index 6c089f0790..f0e5747dc6 100644
--- a/ext/opcache/Optimizer/optimize_temp_vars_5.c
+++ b/ext/opcache/Optimizer/optimize_temp_vars_5.c
@@ -39,7 +39,7 @@
max = i; \
}
-void optimize_temporary_variables(zend_op_array *op_array, zend_optimizer_ctx *ctx)
+void zend_optimize_temporary_variables(zend_op_array *op_array, zend_optimizer_ctx *ctx)
{
int T = op_array->T;
int offset = op_array->last_var;
@@ -139,13 +139,6 @@ void optimize_temporary_variables(zend_op_array *op_array, zend_optimizer_ctx *c
}
}
- /* Skip OP_DATA */
- if (opline->opcode == ZEND_OP_DATA &&
- (opline-1)->opcode == ZEND_ASSIGN_DIM) {
- opline--;
- continue;
- }
-
if ((ZEND_OP2_TYPE(opline) & (IS_VAR | IS_TMP_VAR))) {
currT = VAR_NUM(ZEND_OP2(opline).var) - offset;
if (!zend_bitset_in(valid_T, currT)) {
@@ -156,31 +149,6 @@ void optimize_temporary_variables(zend_op_array *op_array, zend_optimizer_ctx *c
ZEND_OP2(opline).var = NUM_VAR(map_T[currT] + offset);
}
- if (opline->opcode == ZEND_DECLARE_INHERITED_CLASS ||
- opline->opcode == ZEND_DECLARE_ANON_INHERITED_CLASS ||
- opline->opcode == ZEND_DECLARE_INHERITED_CLASS_DELAYED) {
- currT = VAR_NUM(opline->extended_value) - offset;
- if (!zend_bitset_in(valid_T, currT)) {
- GET_AVAILABLE_T();
- map_T[currT] = i;
- zend_bitset_incl(valid_T, currT);
- }
- opline->extended_value = NUM_VAR(map_T[currT] + offset);
- }
-
- /* Allocate OP_DATA->op2 after "operands", but before "result" */
- if (opline->opcode == ZEND_ASSIGN_DIM &&
- (opline + 1)->opcode == ZEND_OP_DATA &&
- ZEND_OP2_TYPE(opline + 1) & (IS_VAR | IS_TMP_VAR)) {
- currT = VAR_NUM(ZEND_OP2(opline + 1).var) - offset;
- GET_AVAILABLE_T();
- map_T[currT] = i;
- zend_bitset_incl(valid_T, currT);
- zend_bitset_excl(taken_T, i);
- ZEND_OP2(opline + 1).var = NUM_VAR(i + offset);
- var_to_free = i;
- }
-
if (ZEND_RESULT_TYPE(opline) & (IS_VAR | IS_TMP_VAR)) {
currT = VAR_NUM(ZEND_RESULT(opline).var) - offset;
if (zend_bitset_in(valid_T, currT)) {
@@ -202,16 +170,11 @@ void optimize_temporary_variables(zend_op_array *op_array, zend_optimizer_ctx *c
}
}
}
- } else { /* Au still needs to be assigned a T which is a bit dumb. Should consider changing Zend */
+ } else {
+ /* Code which gets here is using a wrongly built opcode such as RECV() */
GET_AVAILABLE_T();
-
- if (RESULT_UNUSED(opline)) {
- zend_bitset_excl(taken_T, i);
- } else {
- /* Code which gets here is using a wrongly built opcode such as RECV() */
- map_T[currT] = i;
- zend_bitset_incl(valid_T, currT);
- }
+ map_T[currT] = i;
+ zend_bitset_incl(valid_T, currT);
ZEND_RESULT(opline).var = NUM_VAR(i + offset);
}
}
@@ -224,6 +187,14 @@ void optimize_temporary_variables(zend_op_array *op_array, zend_optimizer_ctx *c
opline--;
}
+ if (op_array->live_range) {
+ for (i = 0; i < op_array->last_live_range; i++) {
+ op_array->live_range[i].var =
+ NUM_VAR(map_T[VAR_NUM(op_array->live_range[i].var & ~ZEND_LIVE_MASK) - offset] + offset) |
+ (op_array->live_range[i].var & ZEND_LIVE_MASK);
+ }
+ }
+
zend_arena_release(&ctx->arena, checkpoint);
op_array->T = max + 1;
}
diff --git a/ext/opcache/Optimizer/pass1_5.c b/ext/opcache/Optimizer/pass1_5.c
index 53abb5b3bd..71f879e958 100644
--- a/ext/opcache/Optimizer/pass1_5.c
+++ b/ext/opcache/Optimizer/pass1_5.c
@@ -42,7 +42,7 @@ void zend_optimizer_pass1(zend_op_array *op_array, zend_optimizer_ctx *ctx)
int i = 0;
zend_op *opline = op_array->opcodes;
zend_op *end = opline + op_array->last;
- zend_bool collect_constants = (ZEND_OPTIMIZER_PASS_15 & OPTIMIZATION_LEVEL)?
+ zend_bool collect_constants = (ZEND_OPTIMIZER_PASS_15 & ctx->optimization_level)?
(op_array == &ctx->script->main_op_array) : 0;
while (opline < end) {
@@ -84,6 +84,9 @@ void zend_optimizer_pass1(zend_op_array *op_array, zend_optimizer_ctx *ctx)
zval_get_long(&ZEND_OP2_LITERAL(opline)) < 0) {
/* shift by negative number */
break;
+ } else if (zend_binary_op_produces_numeric_string_error(opline->opcode, &ZEND_OP1_LITERAL(opline), &ZEND_OP2_LITERAL(opline))) {
+ /* produces numeric string E_NOTICE/E_WARNING */
+ break;
}
er = EG(error_reporting);
EG(error_reporting) = 0;
@@ -240,8 +243,7 @@ void zend_optimizer_pass1(zend_op_array *op_array, zend_optimizer_ctx *ctx)
#endif
case ZEND_FETCH_CONSTANT:
- if (ZEND_OP1_TYPE(opline) == IS_UNUSED &&
- ZEND_OP2_TYPE(opline) == IS_CONST &&
+ if (ZEND_OP2_TYPE(opline) == IS_CONST &&
Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_STRING &&
Z_STRLEN(ZEND_OP2_LITERAL(opline)) == sizeof("__COMPILER_HALT_OFFSET__") - 1 &&
memcmp(Z_STRVAL(ZEND_OP2_LITERAL(opline)), "__COMPILER_HALT_OFFSET__", sizeof("__COMPILER_HALT_OFFSET__") - 1) == 0) {
@@ -265,8 +267,7 @@ void zend_optimizer_pass1(zend_op_array *op_array, zend_optimizer_ctx *ctx)
break;
}
- if (ZEND_OP1_TYPE(opline) == IS_UNUSED &&
- ZEND_OP2_TYPE(opline) == IS_CONST &&
+ if (ZEND_OP2_TYPE(opline) == IS_CONST &&
Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_STRING) {
/* substitute persistent constants */
uint32_t tv = ZEND_RESULT(opline).var;
@@ -285,10 +286,10 @@ void zend_optimizer_pass1(zend_op_array *op_array, zend_optimizer_ctx *ctx)
MAKE_NOP(opline);
}
}
+ break;
- /* class constant */
- if (ZEND_OP1_TYPE(opline) != IS_UNUSED &&
- ZEND_OP2_TYPE(opline) == IS_CONST &&
+ case ZEND_FETCH_CLASS_CONSTANT:
+ if (ZEND_OP2_TYPE(opline) == IS_CONST &&
Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_STRING) {
zend_class_entry *ce = NULL;
@@ -306,15 +307,20 @@ void zend_optimizer_pass1(zend_op_array *op_array, zend_optimizer_ctx *ctx)
(ce->type == ZEND_INTERNAL_CLASS &&
ce->info.internal.module->type != MODULE_PERSISTENT) ||
(ce->type == ZEND_USER_CLASS &&
- ZEND_CE_FILENAME(ce) != op_array->filename)) {
+ ce->info.user.filename != op_array->filename)) {
break;
}
}
} else if (op_array->scope &&
+ ZEND_OP1_TYPE(opline) == IS_UNUSED &&
+ (opline->op1.num & ZEND_FETCH_CLASS_MASK) == ZEND_FETCH_CLASS_SELF) {
+ /* for self::B */
+ ce = op_array->scope;
+ } else if (op_array->scope &&
ZEND_OP1_TYPE(opline) == IS_VAR &&
(opline - 1)->opcode == ZEND_FETCH_CLASS &&
(ZEND_OP1_TYPE(opline - 1) == IS_UNUSED &&
- ((opline - 1)->extended_value & ~ZEND_FETCH_CLASS_NO_AUTOLOAD) == ZEND_FETCH_CLASS_SELF) &&
+ ((opline - 1)->extended_value & ZEND_FETCH_CLASS_MASK) == ZEND_FETCH_CLASS_SELF) &&
ZEND_RESULT((opline - 1)).var == ZEND_OP1(opline).var) {
/* for self::B */
ce = op_array->scope;
@@ -322,11 +328,13 @@ void zend_optimizer_pass1(zend_op_array *op_array, zend_optimizer_ctx *ctx)
if (ce) {
uint32_t tv = ZEND_RESULT(opline).var;
+ zend_class_constant *cc;
zval *c, t;
- if ((c = zend_hash_find(&ce->constants_table,
- Z_STR(ZEND_OP2_LITERAL(opline)))) != NULL) {
- ZVAL_DEREF(c);
+ if ((cc = zend_hash_find_ptr(&ce->constants_table,
+ Z_STR(ZEND_OP2_LITERAL(opline)))) != NULL &&
+ (Z_ACCESS_FLAGS(cc->value) & ZEND_ACC_PPP_MASK) == ZEND_ACC_PUBLIC) {
+ c = &cc->value;
if (Z_TYPE_P(c) == IS_CONSTANT_AST) {
break;
}
@@ -340,12 +348,12 @@ void zend_optimizer_pass1(zend_op_array *op_array, zend_optimizer_ctx *ctx)
zval_copy_ctor(&t);
}
- if (ZEND_OP1_TYPE(opline) == IS_CONST) {
- literal_dtor(&ZEND_OP1_LITERAL(opline));
- } else {
- MAKE_NOP((opline - 1));
- }
if (zend_optimizer_replace_by_const(op_array, opline, IS_TMP_VAR, tv, &t)) {
+ if (ZEND_OP1_TYPE(opline) == IS_CONST) {
+ literal_dtor(&ZEND_OP1_LITERAL(opline));
+ } else if (ZEND_OP1_TYPE(opline) == IS_VAR) {
+ MAKE_NOP((opline - 1));
+ }
literal_dtor(&ZEND_OP2_LITERAL(opline));
MAKE_NOP(opline);
}
@@ -638,74 +646,11 @@ void zend_optimizer_pass1(zend_op_array *op_array, zend_optimizer_ctx *ctx)
case ZEND_FE_RESET_RW:
case ZEND_FE_FETCH_R:
case ZEND_FE_FETCH_RW:
- case ZEND_NEW:
case ZEND_JMP_SET:
case ZEND_COALESCE:
case ZEND_ASSERT_CHECK:
collect_constants = 0;
break;
- case ZEND_FETCH_R:
- case ZEND_FETCH_W:
- case ZEND_FETCH_RW:
- case ZEND_FETCH_FUNC_ARG:
- case ZEND_FETCH_IS:
- case ZEND_FETCH_UNSET:
- if (opline != op_array->opcodes &&
- (opline-1)->opcode == ZEND_BEGIN_SILENCE &&
- (opline->extended_value & ZEND_FETCH_TYPE_MASK) == ZEND_FETCH_LOCAL &&
- opline->op1_type == IS_CONST &&
- opline->op2_type == IS_UNUSED &&
- Z_TYPE(ZEND_OP1_LITERAL(opline)) == IS_STRING &&
- (Z_STRLEN(ZEND_OP1_LITERAL(opline)) != sizeof("this")-1 ||
- memcmp(Z_STRVAL(ZEND_OP1_LITERAL(opline)), "this", sizeof("this") - 1) != 0)) {
-
- int var = opline->result.var;
- int level = 0;
- zend_op *op = opline + 1;
- zend_op *use = NULL;
-
- while (op < end) {
- if (op->opcode == ZEND_BEGIN_SILENCE) {
- level++;
- } else if (op->opcode == ZEND_END_SILENCE) {
- if (level == 0) {
- break;
- } else {
- level--;
- }
- }
- if (op->op1_type == IS_VAR && op->op1.var == var) {
- if (use) {
- /* used more than once */
- use = NULL;
- break;
- }
- use = op;
- } else if (op->op2_type == IS_VAR && op->op2.var == var) {
- if (use) {
- /* used more than once */
- use = NULL;
- break;
- }
- use = op;
- }
- op++;
- }
- if (use) {
- if (use->op1_type == IS_VAR && use->op1.var == var) {
- use->op1_type = IS_CV;
- use->op1.var = zend_optimizer_lookup_cv(op_array,
- Z_STR(ZEND_OP1_LITERAL(opline)));
- MAKE_NOP(opline);
- } else if (use->op2_type == IS_VAR && use->op2.var == var) {
- use->op2_type = IS_CV;
- use->op2.var = zend_optimizer_lookup_cv(op_array,
- Z_STR(ZEND_OP1_LITERAL(opline)));
- MAKE_NOP(opline);
- }
- }
- }
- break;
}
opline++;
i++;
diff --git a/ext/opcache/Optimizer/pass2.c b/ext/opcache/Optimizer/pass2.c
index 83a3ca9cbf..c24d87a4ad 100644
--- a/ext/opcache/Optimizer/pass2.c
+++ b/ext/opcache/Optimizer/pass2.c
@@ -22,7 +22,6 @@
/* pass 2:
* - convert non-numeric constants to numeric constants in numeric operators
* - optimize constant conditional JMPs
- * - optimize static BRKs and CONTs
*/
#include "php.h"
@@ -48,7 +47,10 @@ void zend_optimizer_pass2(zend_op_array *op_array)
case ZEND_POW:
if (ZEND_OP1_TYPE(opline) == IS_CONST) {
if (Z_TYPE(ZEND_OP1_LITERAL(opline)) == IS_STRING) {
- convert_scalar_to_number(&ZEND_OP1_LITERAL(opline));
+ /* don't optimise if it should produce a runtime numeric string error */
+ if (is_numeric_string(Z_STRVAL(ZEND_OP1_LITERAL(opline)), Z_STRLEN(ZEND_OP1_LITERAL(opline)), NULL, NULL, 0)) {
+ convert_scalar_to_number(&ZEND_OP1_LITERAL(opline));
+ }
}
}
/* break missing *intentionally* - the assign_op's may only optimize op2 */
@@ -63,7 +65,10 @@ void zend_optimizer_pass2(zend_op_array *op_array)
}
if (ZEND_OP2_TYPE(opline) == IS_CONST) {
if (Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_STRING) {
- convert_scalar_to_number(&ZEND_OP2_LITERAL(opline));
+ /* don't optimise if it should produce a runtime numeric string error */
+ if (is_numeric_string(Z_STRVAL(ZEND_OP2_LITERAL(opline)), Z_STRLEN(ZEND_OP2_LITERAL(opline)), NULL, NULL, 0)) {
+ convert_scalar_to_number(&ZEND_OP2_LITERAL(opline));
+ }
}
}
break;
@@ -73,7 +78,11 @@ void zend_optimizer_pass2(zend_op_array *op_array)
case ZEND_SR:
if (ZEND_OP1_TYPE(opline) == IS_CONST) {
if (Z_TYPE(ZEND_OP1_LITERAL(opline)) != IS_LONG) {
- convert_to_long(&ZEND_OP1_LITERAL(opline));
+ /* don't optimise if it should produce a runtime numeric string error */
+ if (!(Z_TYPE(ZEND_OP1_LITERAL(opline)) == IS_STRING
+ && !is_numeric_string(Z_STRVAL(ZEND_OP1_LITERAL(opline)), Z_STRLEN(ZEND_OP1_LITERAL(opline)), NULL, NULL, 0))) {
+ convert_to_long(&ZEND_OP1_LITERAL(opline));
+ }
}
}
/* break missing *intentionally - the assign_op's may only optimize op2 */
@@ -86,7 +95,11 @@ void zend_optimizer_pass2(zend_op_array *op_array)
}
if (ZEND_OP2_TYPE(opline) == IS_CONST) {
if (Z_TYPE(ZEND_OP2_LITERAL(opline)) != IS_LONG) {
- convert_to_long(&ZEND_OP2_LITERAL(opline));
+ /* don't optimise if it should produce a runtime numeric string error */
+ if (!(Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_STRING
+ && !is_numeric_string(Z_STRVAL(ZEND_OP2_LITERAL(opline)), Z_STRLEN(ZEND_OP2_LITERAL(opline)), NULL, NULL, 0))) {
+ convert_to_long(&ZEND_OP2_LITERAL(opline));
+ }
}
}
break;
@@ -114,14 +127,21 @@ void zend_optimizer_pass2(zend_op_array *op_array)
case ZEND_JMPZ_EX:
case ZEND_JMPNZ_EX:
/* convert Ti = JMPZ_EX(Ti, L) to JMPZ(Ti, L) */
- if (0 && /* FIXME: temporary disable unsafe pattern */
- ZEND_OP1_TYPE(opline) == IS_TMP_VAR &&
+#if 0
+ /* Disabled unsafe pattern: in conjunction with
+ * ZEND_VM_SMART_BRANCH() this may improperly eliminate
+ * assignment to Ti.
+ */
+ if (ZEND_OP1_TYPE(opline) == IS_TMP_VAR &&
ZEND_RESULT_TYPE(opline) == IS_TMP_VAR &&
ZEND_OP1(opline).var == ZEND_RESULT(opline).var) {
opline->opcode -= 3;
+ SET_UNUSED(opline->result);
+ } else
+#endif
/* convert Ti = JMPZ_EX(C, L) => Ti = QM_ASSIGN(C)
in case we know it wouldn't jump */
- } else if (ZEND_OP1_TYPE(opline) == IS_CONST) {
+ if (ZEND_OP1_TYPE(opline) == IS_CONST) {
int should_jmp = zend_is_true(&ZEND_OP1_LITERAL(opline));
if (opline->opcode == ZEND_JMPZ_EX) {
should_jmp = !should_jmp;
@@ -154,15 +174,15 @@ void zend_optimizer_pass2(zend_op_array *op_array)
if ((opline + 1)->opcode == ZEND_JMP) {
/* JMPZ(X, L1), JMP(L2) => JMPZNZ(X, L1, L2) */
/* JMPNZ(X, L1), JMP(L2) => JMPZNZ(X, L2, L1) */
- if (ZEND_OP2(opline).opline_num == ZEND_OP1(opline + 1).opline_num) {
+ if (ZEND_OP2_JMP_ADDR(opline) == ZEND_OP1_JMP_ADDR(opline + 1)) {
/* JMPZ(X, L1), JMP(L1) => NOP, JMP(L1) */
MAKE_NOP(opline);
} else {
if (opline->opcode == ZEND_JMPZ) {
- opline->extended_value = ZEND_OP1(opline + 1).opline_num;
+ opline->extended_value = ZEND_OPLINE_TO_OFFSET(opline, ZEND_OP1_JMP_ADDR(opline + 1));
} else {
- opline->extended_value = ZEND_OP2(opline).opline_num;
- COPY_NODE(opline->op2, (opline + 1)->op1);
+ opline->extended_value = ZEND_OPLINE_TO_OFFSET(opline, ZEND_OP2_JMP_ADDR(opline));
+ ZEND_SET_OP_JMP_ADDR(opline, opline->op2, ZEND_OP1_JMP_ADDR(opline + 1));
}
opline->opcode = ZEND_JMPZNZ;
}
@@ -171,14 +191,15 @@ void zend_optimizer_pass2(zend_op_array *op_array)
case ZEND_JMPZNZ:
if (ZEND_OP1_TYPE(opline) == IS_CONST) {
- int opline_num;
+ zend_op *target_opline;
+
if (zend_is_true(&ZEND_OP1_LITERAL(opline))) {
- opline_num = opline->extended_value; /* JMPNZ */
+ target_opline = ZEND_OFFSET_TO_OPLINE(opline, opline->extended_value); /* JMPNZ */
} else {
- opline_num = ZEND_OP2(opline).opline_num; /* JMPZ */
+ target_opline = ZEND_OP2_JMP_ADDR(opline); /* JMPZ */
}
literal_dtor(&ZEND_OP1_LITERAL(opline));
- ZEND_OP1(opline).opline_num = opline_num;
+ ZEND_SET_OP_JMP_ADDR(opline, opline->op1, target_opline);
ZEND_OP1_TYPE(opline) = IS_UNUSED;
opline->opcode = ZEND_JMP;
}
diff --git a/ext/opcache/Optimizer/pass3.c b/ext/opcache/Optimizer/pass3.c
index 00217bf295..ce04e4f7cb 100644
--- a/ext/opcache/Optimizer/pass3.c
+++ b/ext/opcache/Optimizer/pass3.c
@@ -39,31 +39,31 @@
/* we use "jmp_hitlist" to avoid infinity loops during jmp optimization */
#define CHECK_JMP(target, label) \
for (i=0; i<jmp_hitlist_count; i++) { \
- if (jmp_hitlist[i] == ZEND_OP1(&op_array->opcodes[target]).opline_num) { \
+ if (jmp_hitlist[i] == ZEND_OP1_JMP_ADDR(target)) { \
goto label; \
} \
} \
- jmp_hitlist[jmp_hitlist_count++] = ZEND_OP1(&op_array->opcodes[target]).opline_num;
+ jmp_hitlist[jmp_hitlist_count++] = ZEND_OP1_JMP_ADDR(target);
#define CHECK_JMP2(target, label) \
for (i=0; i<jmp_hitlist_count; i++) { \
- if (jmp_hitlist[i] == ZEND_OP2(&op_array->opcodes[target]).opline_num) { \
+ if (jmp_hitlist[i] == ZEND_OP2_JMP_ADDR(target)) { \
goto label; \
} \
} \
- jmp_hitlist[jmp_hitlist_count++] = ZEND_OP2(&op_array->opcodes[target]).opline_num;
+ jmp_hitlist[jmp_hitlist_count++] = ZEND_OP2_JMP_ADDR(target);
void zend_optimizer_pass3(zend_op_array *op_array)
{
zend_op *opline;
zend_op *end = op_array->opcodes + op_array->last;
- uint32_t *jmp_hitlist;
+ zend_op **jmp_hitlist;
int jmp_hitlist_count;
int i;
uint32_t opline_num = 0;
ALLOCA_FLAG(use_heap);
- jmp_hitlist = (uint32_t *)DO_ALLOCA(sizeof(uint32_t)*op_array->last);
+ jmp_hitlist = (zend_op**)do_alloca(sizeof(zend_op*)*op_array->last, use_heap);
opline = op_array->opcodes;
while (opline < end) {
@@ -93,7 +93,7 @@ void zend_optimizer_pass3(zend_op_array *op_array)
break;
}
- if ((ZEND_OP2_TYPE(opline) == IS_VAR || ZEND_OP2_TYPE(opline) == IS_CV)
+ if ((ZEND_OP2_TYPE(opline) & (IS_VAR | IS_CV))
&& ZEND_OP2(opline).var == ZEND_OP1(next_opline).var &&
(opline->opcode == ZEND_ADD ||
opline->opcode == ZEND_MUL ||
@@ -114,7 +114,7 @@ void zend_optimizer_pass3(zend_op_array *op_array)
COPY_NODE(opline->op2, tmp);
}
}
- if ((ZEND_OP1_TYPE(opline) == IS_VAR || ZEND_OP1_TYPE(opline) == IS_CV)
+ if ((ZEND_OP1_TYPE(opline) & (IS_VAR | IS_CV))
&& ZEND_OP1(opline).var == ZEND_OP1(next_opline).var
&& ZEND_OP1_TYPE(opline) == ZEND_OP1_TYPE(next_opline)) {
switch (opline->opcode) {
@@ -169,17 +169,17 @@ void zend_optimizer_pass3(zend_op_array *op_array)
}
/* convert L: JMP L+1 to NOP */
- if (ZEND_OP1(opline).opline_num == opline_num + 1) {
+ if (ZEND_OP1_JMP_ADDR(opline) == opline + 1) {
MAKE_NOP(opline);
goto done_jmp_optimization;
}
/* convert JMP L1 ... L1: JMP L2 to JMP L2 .. L1: JMP L2 */
- while (ZEND_OP1(opline).opline_num < op_array->last
- && op_array->opcodes[ZEND_OP1(opline).opline_num].opcode == ZEND_JMP) {
- int target = ZEND_OP1(opline).opline_num;
+ while (ZEND_OP1_JMP_ADDR(opline) < end
+ && ZEND_OP1_JMP_ADDR(opline)->opcode == ZEND_JMP) {
+ zend_op *target = ZEND_OP1_JMP_ADDR(opline);
CHECK_JMP(target, done_jmp_optimization);
- ZEND_OP1(opline).opline_num = ZEND_OP1(&op_array->opcodes[target]).opline_num;
+ ZEND_SET_OP_JMP_ADDR(opline, opline->op1, ZEND_OP1_JMP_ADDR(target));
}
break;
@@ -189,10 +189,10 @@ void zend_optimizer_pass3(zend_op_array *op_array)
break;
}
- while (ZEND_OP2(opline).opline_num < op_array->last) {
- int target = ZEND_OP2(opline).opline_num;
- if (op_array->opcodes[target].opcode == ZEND_JMP) {
- ZEND_OP2(opline).opline_num = ZEND_OP1(&op_array->opcodes[target]).opline_num;
+ while (ZEND_OP2_JMP_ADDR(opline) < end) {
+ zend_op *target = ZEND_OP2_JMP_ADDR(opline);
+ if (target->opcode == ZEND_JMP) {
+ ZEND_SET_OP_JMP_ADDR(opline, opline->op2, ZEND_OP1_JMP_ADDR(target));
} else {
break;
}
@@ -204,40 +204,41 @@ void zend_optimizer_pass3(zend_op_array *op_array)
break;
}
- while (ZEND_OP2(opline).opline_num < op_array->last) {
- int target = ZEND_OP2(opline).opline_num;
+ while (ZEND_OP2_JMP_ADDR(opline) < end) {
+ zend_op *target = ZEND_OP2_JMP_ADDR(opline);
- if (op_array->opcodes[target].opcode == ZEND_JMP) {
+ if (target->opcode == ZEND_JMP) {
/* plain JMP */
/* JMPZ(X,L1), L1: JMP(L2) => JMPZ(X,L2), L1: JMP(L2) */
CHECK_JMP(target, done_jmp_optimization);
- ZEND_OP2(opline).opline_num = ZEND_OP1(&op_array->opcodes[target]).opline_num;
- } else if (op_array->opcodes[target].opcode == opline->opcode &&
- SAME_VAR(opline->op1, op_array->opcodes[target].op1)) {
+ ZEND_SET_OP_JMP_ADDR(opline, opline->op2, ZEND_OP1_JMP_ADDR(target));
+ } else if (target->opcode == opline->opcode &&
+ SAME_VAR(opline->op1, target->op1)) {
/* same opcode and same var as this opcode */
/* JMPZ(X,L1), L1: JMPZ(X,L2) => JMPZ(X,L2), L1: JMPZ(X,L2) */
CHECK_JMP2(target, done_jmp_optimization);
- ZEND_OP2(opline).opline_num = ZEND_OP2(&op_array->opcodes[target]).opline_num;
- } else if (op_array->opcodes[target].opcode == opline->opcode + 3 &&
- SAME_VAR(opline->op1, op_array->opcodes[target].op1)) {
+ ZEND_SET_OP_JMP_ADDR(opline, opline->op2, ZEND_OP2_JMP_ADDR(target));
+ } else if (target->opcode == opline->opcode + 3 &&
+ SAME_VAR(opline->op1, target->op1)) {
/* convert JMPZ(X,L1), L1: T JMPZ_EX(X,L2) to
T = JMPZ_EX(X, L2) */
- ZEND_OP2(opline).opline_num = ZEND_OP2(&op_array->opcodes[target]).opline_num;opline->opcode += 3;
- COPY_NODE(opline->result, op_array->opcodes[target].result);
+ ZEND_SET_OP_JMP_ADDR(opline, opline->op2, ZEND_OP2_JMP_ADDR(target));
+ opline->opcode += 3;
+ COPY_NODE(opline->result, target->result);
break;
- } else if (op_array->opcodes[target].opcode == INV_COND(opline->opcode) &&
- SAME_VAR(opline->op1, op_array->opcodes[target].op1)) {
+ } else if (target->opcode == INV_COND(opline->opcode) &&
+ SAME_VAR(opline->op1, target->op1)) {
/* convert JMPZ(X,L1), L1: JMPNZ(X,L2) to
JMPZ(X,L1+1) */
- ZEND_OP2(opline).opline_num = target + 1;
+ ZEND_SET_OP_JMP_ADDR(opline, opline->op2, target + 1);
break;
- } else if (op_array->opcodes[target].opcode == INV_COND_EX(opline->opcode) &&
- SAME_VAR(opline->op1, op_array->opcodes[target].op1)) {
+ } else if (target->opcode == INV_COND_EX(opline->opcode) &&
+ SAME_VAR(opline->op1, target->op1)) {
/* convert JMPZ(X,L1), L1: T = JMPNZ_EX(X,L2) to
T = JMPZ_EX(X,L1+1) */
- ZEND_OP2(opline).opline_num = target + 1;
+ ZEND_SET_OP_JMP_ADDR(opline, opline->op2, target + 1);
opline->opcode += 3;
- COPY_NODE(opline->result, op_array->opcodes[target].result);
+ COPY_NODE(opline->result, target->result);
break;
} else {
break;
@@ -256,7 +257,7 @@ void zend_optimizer_pass3(zend_op_array *op_array)
/* convert L: T = JMPZ_EX X,L+1 to T = BOOL(X) */
/* convert L: T = JMPZ_EX T,L+1 to NOP */
- if (ZEND_OP2(opline).opline_num == opline_num + 1) {
+ if (ZEND_OP2_JMP_ADDR(opline) == opline + 1) {
if (ZEND_OP1(opline).var == ZEND_RESULT(opline).var) {
MAKE_NOP(opline);
} else {
@@ -266,36 +267,38 @@ void zend_optimizer_pass3(zend_op_array *op_array)
goto done_jmp_optimization;
}
- while (ZEND_OP2(opline).opline_num < op_array->last) {
- int target = ZEND_OP2(opline).opline_num;
- if (SAME_OPCODE_EX(opline->opcode, op_array->opcodes[target].opcode) &&
- SAME_VAR(op_array->opcodes[target].op1, T)) {
+ while (ZEND_OP2_JMP_ADDR(opline) < end) {
+ zend_op *target = ZEND_OP2_JMP_ADDR(opline);
+
+ if (SAME_OPCODE_EX(opline->opcode, target->opcode) &&
+ SAME_VAR(target->op1, T)) {
/* Check for JMPZ_EX to JMPZ[_EX] with the same condition, either with _EX or not */
- if (op_array->opcodes[target].opcode == opline->opcode) {
+ if (target->opcode == opline->opcode) {
/* change T only if we have _EX opcode there */
- COPY_NODE(T, op_array->opcodes[target].result);
+ COPY_NODE(T, target->result);
}
CHECK_JMP2(target, continue_jmp_ex_optimization);
- ZEND_OP2(opline).opline_num = ZEND_OP2(&op_array->opcodes[target]).opline_num;
- } else if (op_array->opcodes[target].opcode == ZEND_JMPZNZ &&
- SAME_VAR(op_array->opcodes[target].op1, T)) {
+ ZEND_SET_OP_JMP_ADDR(opline, opline->op2, ZEND_OP2_JMP_ADDR(target));
+ } else if (target->opcode == ZEND_JMPZNZ &&
+ SAME_VAR(target->op1, T)) {
/* Check for JMPZNZ with same cond variable */
- int new_target;
+ zend_op *new_target;
+
CHECK_JMP2(target, continue_jmp_ex_optimization);
if (opline->opcode == ZEND_JMPZ_EX) {
- new_target = ZEND_OP2(&op_array->opcodes[target]).opline_num;
+ new_target = ZEND_OP2_JMP_ADDR(target);
} else {
/* JMPNZ_EX */
- new_target = op_array->opcodes[target].extended_value;
+ new_target = ZEND_OFFSET_TO_OPLINE(target, target->extended_value);
}
- ZEND_OP2(opline).opline_num = new_target;
- } else if ((op_array->opcodes[target].opcode == INV_EX_COND_EX(opline->opcode) ||
- op_array->opcodes[target].opcode == INV_EX_COND(opline->opcode)) &&
- SAME_VAR(opline->op1, op_array->opcodes[target].op1)) {
+ ZEND_SET_OP_JMP_ADDR(opline, opline->op2, new_target);
+ } else if ((target->opcode == INV_EX_COND_EX(opline->opcode) ||
+ target->opcode == INV_EX_COND(opline->opcode)) &&
+ SAME_VAR(opline->op1, target->op1)) {
/* convert JMPZ_EX(X,L1), L1: JMPNZ_EX(X,L2) to
JMPZ_EX(X,L1+1) */
- ZEND_OP2(opline).opline_num = target + 1;
- break;
+ ZEND_SET_OP_JMP_ADDR(opline, opline->op2, target + 1);
+ break;
} else {
break;
}
@@ -387,19 +390,19 @@ continue_jmp_ex_optimization:
}
/* JMPZNZ(X,L1,L2), L1: JMP(L3) => JMPZNZ(X,L3,L2), L1: JMP(L3) */
- while (ZEND_OP2(opline).opline_num < op_array->last
- && op_array->opcodes[ZEND_OP2(opline).opline_num].opcode == ZEND_JMP) {
- int target = ZEND_OP2(opline).opline_num;
+ while (ZEND_OP2_JMP_ADDR(opline) < end
+ && ZEND_OP2_JMP_ADDR(opline)->opcode == ZEND_JMP) {
+ zend_op *target = ZEND_OP2_JMP_ADDR(opline);
CHECK_JMP(target, continue_jmpznz_optimization);
- ZEND_OP2(opline).opline_num = ZEND_OP1(&op_array->opcodes[target]).opline_num;
+ ZEND_SET_OP_JMP_ADDR(opline, opline->op2, ZEND_OP1_JMP_ADDR(target));
}
continue_jmpznz_optimization:
/* JMPZNZ(X,L1,L2), L2: JMP(L3) => JMPZNZ(X,L1,L3), L2: JMP(L3) */
- while (opline->extended_value < op_array->last
- && op_array->opcodes[opline->extended_value].opcode == ZEND_JMP) {
- int target = opline->extended_value;
+ while (ZEND_OFFSET_TO_OPLINE(opline, opline->extended_value) < end
+ && ZEND_OFFSET_TO_OPLINE(opline, opline->extended_value)->opcode == ZEND_JMP) {
+ zend_op *target = ZEND_OFFSET_TO_OPLINE(opline, opline->extended_value);
CHECK_JMP(target, done_jmp_optimization);
- opline->extended_value = ZEND_OP1(&op_array->opcodes[target]).opline_num;
+ opline->extended_value = ZEND_OPLINE_TO_OFFSET(opline, ZEND_OP1_JMP_ADDR(target));
}
break;
@@ -414,15 +417,8 @@ continue_jmpznz_optimization:
if (next_op->opcode == ZEND_FREE &&
ZEND_OP1(next_op).var == ZEND_RESULT(opline).var) {
MAKE_NOP(next_op);
- switch (opline->opcode) {
- case ZEND_POST_INC:
- opline->opcode = ZEND_PRE_INC;
- break;
- case ZEND_POST_DEC:
- opline->opcode = ZEND_PRE_DEC;
- break;
- }
- ZEND_RESULT_TYPE(opline) = IS_VAR | EXT_TYPE_UNUSED;
+ opline->opcode -= 2;
+ ZEND_RESULT_TYPE(opline) = IS_UNUSED;
}
}
break;
@@ -431,5 +427,5 @@ done_jmp_optimization:
opline++;
opline_num++;
}
- FREE_ALLOCA(jmp_hitlist);
+ free_alloca(jmp_hitlist, use_heap);
}
diff --git a/ext/opcache/Optimizer/zend_call_graph.c b/ext/opcache/Optimizer/zend_call_graph.c
new file mode 100644
index 0000000000..53eb0a84ea
--- /dev/null
+++ b/ext/opcache/Optimizer/zend_call_graph.c
@@ -0,0 +1,300 @@
+/*
+ +----------------------------------------------------------------------+
+ | Zend Engine, Call Graph |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1998-2016 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:$ */
+
+#include "php.h"
+#include "zend_compile.h"
+#include "zend_extensions.h"
+#include "Optimizer/zend_optimizer.h"
+#include "zend_optimizer_internal.h"
+#include "zend_inference.h"
+#include "zend_call_graph.h"
+#include "zend_func_info.h"
+#include "zend_inference.h"
+#include "zend_call_graph.h"
+
+typedef int (*zend_op_array_func_t)(zend_call_graph *call_graph, zend_op_array *op_array);
+
+static int zend_op_array_calc(zend_call_graph *call_graph, zend_op_array *op_array)
+{
+ (void) op_array;
+
+ call_graph->op_arrays_count++;
+ return SUCCESS;
+}
+
+static int zend_op_array_collect(zend_call_graph *call_graph, zend_op_array *op_array)
+{
+ zend_func_info *func_info = call_graph->func_infos + call_graph->op_arrays_count;
+
+ ZEND_SET_FUNC_INFO(op_array, func_info);
+ call_graph->op_arrays[call_graph->op_arrays_count] = op_array;
+ func_info->num = call_graph->op_arrays_count;
+ func_info->num_args = -1;
+ func_info->return_value_used = -1;
+ call_graph->op_arrays_count++;
+ return SUCCESS;
+}
+
+static int zend_foreach_op_array(zend_call_graph *call_graph, zend_script *script, zend_op_array_func_t func)
+{
+ zend_class_entry *ce;
+ zend_op_array *op_array;
+
+ if (func(call_graph, &script->main_op_array) != SUCCESS) {
+ return FAILURE;
+ }
+
+ ZEND_HASH_FOREACH_PTR(&script->function_table, op_array) {
+ if (func(call_graph, op_array) != SUCCESS) {
+ return FAILURE;
+ }
+ } ZEND_HASH_FOREACH_END();
+
+ ZEND_HASH_FOREACH_PTR(&script->class_table, ce) {
+ ZEND_HASH_FOREACH_PTR(&ce->function_table, op_array) {
+ if (op_array->scope == ce) {
+ if (func(call_graph, op_array) != SUCCESS) {
+ return FAILURE;
+ }
+ }
+ } ZEND_HASH_FOREACH_END();
+ } ZEND_HASH_FOREACH_END();
+
+ return SUCCESS;
+}
+
+static void zend_collect_args_info(zend_call_info *call_info)
+{
+ zend_op *opline = call_info->caller_init_opline;
+ zend_op *end = call_info->caller_call_opline;
+ uint32_t i;
+ int num;
+ int level = 0;
+
+ ZEND_ASSERT(opline && end);
+ if (!opline->extended_value) {
+ return;
+ }
+ for (i = 0; i < opline->extended_value; i++) {
+ call_info->arg_info[i].opline = NULL;
+ }
+ while (opline < end) {
+ opline++;
+ switch (opline->opcode) {
+ case ZEND_SEND_VAL:
+ case ZEND_SEND_VAR:
+ case ZEND_SEND_VAL_EX:
+ case ZEND_SEND_VAR_EX:
+ case ZEND_SEND_REF:
+ case ZEND_SEND_VAR_NO_REF:
+ case ZEND_SEND_VAR_NO_REF_EX:
+ num = opline->op2.num;
+ if (num > 0) {
+ num--;
+ }
+ if (!level) {
+ call_info->arg_info[num].opline = opline;
+ }
+ break;
+ case ZEND_SEND_ARRAY:
+ case ZEND_SEND_USER:
+ case ZEND_SEND_UNPACK:
+ // ???
+ break;
+ case ZEND_INIT_FCALL:
+ case ZEND_INIT_FCALL_BY_NAME:
+ case ZEND_INIT_NS_FCALL_BY_NAME:
+ case ZEND_INIT_DYNAMIC_CALL:
+ case ZEND_NEW:
+ case ZEND_INIT_METHOD_CALL:
+ case ZEND_INIT_STATIC_METHOD_CALL:
+ case ZEND_INIT_USER_CALL:
+ level++;
+ break;
+ case ZEND_DO_FCALL:
+ case ZEND_DO_ICALL:
+ case ZEND_DO_UCALL:
+ case ZEND_DO_FCALL_BY_NAME:
+ level--;
+ break;
+ }
+ }
+}
+
+static int zend_analyze_calls(zend_arena **arena, zend_script *script, uint32_t build_flags, zend_op_array *op_array, zend_func_info *func_info)
+{
+ zend_op *opline = op_array->opcodes;
+ zend_op *end = opline + op_array->last;
+ zend_function *func;
+ zend_call_info *call_info;
+ int call = 0;
+ zend_call_info **call_stack;
+ ALLOCA_FLAG(use_heap);
+
+ call_stack = do_alloca((op_array->last / 2) * sizeof(zend_call_info*), use_heap);
+ while (opline != end) {
+ call_info = NULL;
+ switch (opline->opcode) {
+ case ZEND_INIT_FCALL:
+ case ZEND_INIT_METHOD_CALL:
+ case ZEND_INIT_STATIC_METHOD_CALL:
+ func = zend_optimizer_get_called_func(
+ script, op_array, opline, (build_flags & ZEND_RT_CONSTANTS) != 0);
+ if (func) {
+ call_info = zend_arena_calloc(arena, 1, sizeof(zend_call_info) + (sizeof(zend_send_arg_info) * ((int)opline->extended_value - 1)));
+ call_info->caller_op_array = op_array;
+ call_info->caller_init_opline = opline;
+ call_info->caller_call_opline = NULL;
+ call_info->callee_func = func;
+ call_info->num_args = opline->extended_value;
+ call_info->next_callee = func_info->callee_info;
+ func_info->callee_info = call_info;
+
+ if (func->type == ZEND_INTERNAL_FUNCTION) {
+ call_info->next_caller = NULL;
+ } else {
+ zend_func_info *callee_func_info = ZEND_FUNC_INFO(&func->op_array);
+ call_info->next_caller = callee_func_info ? callee_func_info->caller_info : NULL;
+ }
+ }
+ /* break missing intentionally */
+ case ZEND_INIT_FCALL_BY_NAME:
+ case ZEND_INIT_NS_FCALL_BY_NAME:
+ case ZEND_INIT_DYNAMIC_CALL:
+ case ZEND_NEW:
+ case ZEND_INIT_USER_CALL:
+ call_stack[call] = call_info;
+ call++;
+ break;
+ case ZEND_DO_FCALL:
+ case ZEND_DO_ICALL:
+ case ZEND_DO_UCALL:
+ case ZEND_DO_FCALL_BY_NAME:
+ func_info->flags |= ZEND_FUNC_HAS_CALLS;
+ call--;
+ if (call_stack[call]) {
+ call_stack[call]->caller_call_opline = opline;
+ zend_collect_args_info(call_stack[call]);
+ }
+ break;
+ }
+ opline++;
+ }
+ free_alloca(call_stack, use_heap);
+ return SUCCESS;
+}
+
+static int zend_is_indirectly_recursive(zend_op_array *root, zend_op_array *op_array, zend_bitset visited)
+{
+ zend_func_info *func_info;
+ zend_call_info *call_info;
+ int ret = 0;
+
+ if (op_array == root) {
+ return 1;
+ }
+
+ func_info = ZEND_FUNC_INFO(op_array);
+ if (zend_bitset_in(visited, func_info->num)) {
+ return 0;
+ }
+ zend_bitset_incl(visited, func_info->num);
+ call_info = func_info->caller_info;
+ while (call_info) {
+ if (zend_is_indirectly_recursive(root, call_info->caller_op_array, visited)) {
+ call_info->recursive = 1;
+ ret = 1;
+ }
+ call_info = call_info->next_caller;
+ }
+ return ret;
+}
+
+static void zend_analyze_recursion(zend_call_graph *call_graph)
+{
+ zend_op_array *op_array;
+ zend_func_info *func_info;
+ zend_call_info *call_info;
+ int i;
+ int set_len = zend_bitset_len(call_graph->op_arrays_count);
+ zend_bitset visited;
+ ALLOCA_FLAG(use_heap);
+
+ visited = ZEND_BITSET_ALLOCA(set_len, use_heap);
+ for (i = 0; i < call_graph->op_arrays_count; i++) {
+ op_array = call_graph->op_arrays[i];
+ func_info = call_graph->func_infos + i;
+ call_info = func_info->caller_info;
+ while (call_info) {
+ if (call_info->caller_op_array == op_array) {
+ call_info->recursive = 1;
+ func_info->flags |= ZEND_FUNC_RECURSIVE | ZEND_FUNC_RECURSIVE_DIRECTLY;
+ } else {
+ memset(visited, 0, sizeof(zend_ulong) * set_len);
+ if (zend_is_indirectly_recursive(op_array, call_info->caller_op_array, visited)) {
+ call_info->recursive = 1;
+ func_info->flags |= ZEND_FUNC_RECURSIVE | ZEND_FUNC_RECURSIVE_INDIRECTLY;
+ }
+ }
+ call_info = call_info->next_caller;
+ }
+ }
+
+ free_alloca(visited, use_heap);
+}
+
+static void zend_sort_op_arrays(zend_call_graph *call_graph)
+{
+ (void) call_graph;
+
+ // TODO: perform topological sort of cyclic call graph
+}
+
+int zend_build_call_graph(zend_arena **arena, zend_script *script, uint32_t build_flags, zend_call_graph *call_graph) /* {{{ */
+{
+ int i;
+
+ call_graph->op_arrays_count = 0;
+ if (zend_foreach_op_array(call_graph, script, zend_op_array_calc) != SUCCESS) {
+ return FAILURE;
+ }
+ call_graph->op_arrays = (zend_op_array**)zend_arena_calloc(arena, call_graph->op_arrays_count, sizeof(zend_op_array*));
+ call_graph->func_infos = (zend_func_info*)zend_arena_calloc(arena, call_graph->op_arrays_count, sizeof(zend_func_info));
+ call_graph->op_arrays_count = 0;
+ if (zend_foreach_op_array(call_graph, script, zend_op_array_collect) != SUCCESS) {
+ return FAILURE;
+ }
+ for (i = 0; i < call_graph->op_arrays_count; i++) {
+ zend_analyze_calls(arena, script, build_flags, call_graph->op_arrays[i], call_graph->func_infos + i);
+ }
+ zend_analyze_recursion(call_graph);
+ zend_sort_op_arrays(call_graph);
+
+ return SUCCESS;
+}
+/* }}} */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * indent-tabs-mode: t
+ * End:
+ */
diff --git a/ext/opcache/Optimizer/zend_call_graph.h b/ext/opcache/Optimizer/zend_call_graph.h
new file mode 100644
index 0000000000..64eb9e8e1b
--- /dev/null
+++ b/ext/opcache/Optimizer/zend_call_graph.h
@@ -0,0 +1,83 @@
+/*
+ +----------------------------------------------------------------------+
+ | Zend Engine, Call Graph |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1998-2016 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> |
+ +----------------------------------------------------------------------+
+*/
+
+#ifndef ZEND_CALL_GRAPH_H
+#define ZEND_CALL_GRAPH_H
+
+#include "zend_ssa.h"
+#include "zend_func_info.h"
+#include "zend_optimizer.h"
+
+typedef struct _zend_send_arg_info {
+ zend_op *opline;
+} zend_send_arg_info;
+
+typedef struct _zend_recv_arg_info {
+ int ssa_var;
+ zend_ssa_var_info info;
+} zend_recv_arg_info;
+
+struct _zend_call_info {
+ zend_op_array *caller_op_array;
+ zend_op *caller_init_opline;
+ zend_op *caller_call_opline;
+ zend_function *callee_func;
+ zend_call_info *next_caller;
+ zend_call_info *next_callee;
+ zend_func_info *clone;
+ int recursive;
+ int num_args;
+ zend_send_arg_info arg_info[1];
+};
+
+struct _zend_func_info {
+ int num;
+ uint32_t flags;
+ zend_ssa ssa; /* Static Single Assignmnt Form */
+ zend_call_info *caller_info; /* where this function is called from */
+ zend_call_info *callee_info; /* which functions are called from this one */
+ int num_args; /* (-1 - unknown) */
+ zend_recv_arg_info *arg_info;
+ zend_ssa_var_info return_info;
+ zend_func_info *clone;
+ int clone_num;
+ int return_value_used; /* -1 unknown, 0 no, 1 yes */
+ void *codegen_data;
+};
+
+typedef struct _zend_call_graph {
+ int op_arrays_count;
+ zend_op_array **op_arrays;
+ zend_func_info *func_infos;
+} zend_call_graph;
+
+BEGIN_EXTERN_C()
+
+int zend_build_call_graph(zend_arena **arena, zend_script *script, uint32_t build_flags, zend_call_graph *call_graph);
+
+END_EXTERN_C()
+
+#endif /* ZEND_CALL_GRAPH_H */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * indent-tabs-mode: t
+ * End:
+ */
diff --git a/ext/opcache/Optimizer/zend_cfg.c b/ext/opcache/Optimizer/zend_cfg.c
new file mode 100644
index 0000000000..e986ac0c20
--- /dev/null
+++ b/ext/opcache/Optimizer/zend_cfg.c
@@ -0,0 +1,844 @@
+/*
+ +----------------------------------------------------------------------+
+ | Zend Engine, CFG - Control Flow Graph |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1998-2016 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> |
+ +----------------------------------------------------------------------+
+*/
+
+#include "php.h"
+#include "zend_compile.h"
+#include "zend_cfg.h"
+#include "zend_func_info.h"
+#include "zend_worklist.h"
+#include "zend_optimizer.h"
+#include "zend_optimizer_internal.h"
+
+static void zend_mark_reachable(zend_op *opcodes, zend_basic_block *blocks, zend_basic_block *b) /* {{{ */
+{
+ zend_uchar opcode;
+ zend_basic_block *b0;
+ int successor_0, successor_1;
+
+ while (1) {
+ b->flags |= ZEND_BB_REACHABLE;
+ successor_0 = b->successors[0];
+ if (successor_0 >= 0) {
+ successor_1 = b->successors[1];
+ if (successor_1 >= 0) {
+ b0 = blocks + successor_0;
+ b0->flags |= ZEND_BB_TARGET;
+ if (!(b0->flags & ZEND_BB_REACHABLE)) {
+ zend_mark_reachable(opcodes, blocks, b0);
+ }
+
+ ZEND_ASSERT(b->len != 0);
+ opcode = opcodes[b->start + b->len - 1].opcode;
+ b = blocks + successor_1;
+ if (opcode == ZEND_JMPZNZ) {
+ b->flags |= ZEND_BB_TARGET;
+ } else {
+ b->flags |= ZEND_BB_FOLLOW;
+ }
+ } else if (b->len != 0) {
+ opcode = opcodes[b->start + b->len - 1].opcode;
+ b = blocks + successor_0;
+ if (opcode == ZEND_JMP) {
+ b->flags |= ZEND_BB_TARGET;
+ } else {
+ b->flags |= ZEND_BB_FOLLOW;
+
+ //TODO: support for stackless CFG???
+ if (0/*stackless*/) {
+ if (opcode == ZEND_INCLUDE_OR_EVAL ||
+ opcode == ZEND_GENERATOR_CREATE ||
+ opcode == ZEND_YIELD ||
+ opcode == ZEND_YIELD_FROM ||
+ opcode == ZEND_DO_FCALL ||
+ opcode == ZEND_DO_UCALL ||
+ opcode == ZEND_DO_FCALL_BY_NAME) {
+ b->flags |= ZEND_BB_ENTRY;
+ }
+ }
+ }
+ } else {
+ b = blocks + successor_0;
+ b->flags |= ZEND_BB_FOLLOW;
+ }
+ if (b->flags & ZEND_BB_REACHABLE) return;
+ } else {
+ b->flags |= ZEND_BB_EXIT;
+ return;
+ }
+ }
+}
+/* }}} */
+
+static void zend_mark_reachable_blocks(const zend_op_array *op_array, zend_cfg *cfg, int start) /* {{{ */
+{
+ zend_basic_block *blocks = cfg->blocks;
+
+ blocks[start].flags = ZEND_BB_START;
+ zend_mark_reachable(op_array->opcodes, blocks, blocks + start);
+
+ if (op_array->last_live_range || op_array->last_try_catch) {
+ zend_basic_block *b;
+ int j, changed;
+ uint32_t *block_map = cfg->map;
+
+ do {
+ changed = 0;
+
+ /* Add live range paths */
+ for (j = 0; j < op_array->last_live_range; j++) {
+ zend_live_range *live_range = &op_array->live_range[j];
+ if (live_range->var == (uint32_t)-1) {
+ /* this live range already removed */
+ continue;
+ }
+ b = blocks + block_map[live_range->start];
+ if (b->flags & ZEND_BB_REACHABLE) {
+ while (b->len > 0 && op_array->opcodes[b->start].opcode == ZEND_NOP) {
+ b->start++;
+ b->len--;
+ }
+ if (b->len == 0 && b->successors[0] == block_map[live_range->end]) {
+ /* mark as removed (empty live range) */
+ live_range->var = (uint32_t)-1;
+ continue;
+ }
+ b->flags |= ZEND_BB_GEN_VAR;
+ b = blocks + block_map[live_range->end];
+ b->flags |= ZEND_BB_KILL_VAR;
+ if (!(b->flags & ZEND_BB_REACHABLE)) {
+ if (cfg->split_at_live_ranges) {
+ changed = 1;
+ zend_mark_reachable(op_array->opcodes, blocks, b);
+ } else {
+ ZEND_ASSERT(!(b->flags & ZEND_BB_UNREACHABLE_FREE));
+ ZEND_ASSERT(b->start == live_range->end);
+ b->flags |= ZEND_BB_UNREACHABLE_FREE;
+ }
+ }
+ } else {
+ ZEND_ASSERT(!(blocks[block_map[live_range->end]].flags & ZEND_BB_REACHABLE));
+ }
+ }
+
+ /* Add exception paths */
+ for (j = 0; j < op_array->last_try_catch; j++) {
+
+ /* check for jumps into the middle of try block */
+ b = blocks + block_map[op_array->try_catch_array[j].try_op];
+ if (!(b->flags & ZEND_BB_REACHABLE)) {
+ zend_basic_block *end;
+
+ if (op_array->try_catch_array[j].catch_op) {
+ end = blocks + block_map[op_array->try_catch_array[j].catch_op];
+ while (b != end) {
+ if (b->flags & ZEND_BB_REACHABLE) {
+ op_array->try_catch_array[j].try_op = b->start;
+ break;
+ }
+ b++;
+ }
+ }
+ b = blocks + block_map[op_array->try_catch_array[j].try_op];
+ if (!(b->flags & ZEND_BB_REACHABLE)) {
+ if (op_array->try_catch_array[j].finally_op) {
+ end = blocks + block_map[op_array->try_catch_array[j].finally_op];
+ while (b != end) {
+ if (b->flags & ZEND_BB_REACHABLE) {
+ op_array->try_catch_array[j].try_op = op_array->try_catch_array[j].catch_op;
+ changed = 1;
+ zend_mark_reachable(op_array->opcodes, blocks, blocks + block_map[op_array->try_catch_array[j].try_op]);
+ break;
+ }
+ b++;
+ }
+ }
+ }
+ }
+
+ b = blocks + block_map[op_array->try_catch_array[j].try_op];
+ if (b->flags & ZEND_BB_REACHABLE) {
+ b->flags |= ZEND_BB_TRY;
+ if (op_array->try_catch_array[j].catch_op) {
+ b = blocks + block_map[op_array->try_catch_array[j].catch_op];
+ b->flags |= ZEND_BB_CATCH;
+ if (!(b->flags & ZEND_BB_REACHABLE)) {
+ changed = 1;
+ zend_mark_reachable(op_array->opcodes, blocks, b);
+ }
+ }
+ if (op_array->try_catch_array[j].finally_op) {
+ b = blocks + block_map[op_array->try_catch_array[j].finally_op];
+ b->flags |= ZEND_BB_FINALLY;
+ if (!(b->flags & ZEND_BB_REACHABLE)) {
+ changed = 1;
+ zend_mark_reachable(op_array->opcodes, blocks, b);
+ }
+ }
+ if (op_array->try_catch_array[j].finally_end) {
+ b = blocks + block_map[op_array->try_catch_array[j].finally_end];
+ b->flags |= ZEND_BB_FINALLY_END;
+ if (!(b->flags & ZEND_BB_REACHABLE)) {
+ changed = 1;
+ zend_mark_reachable(op_array->opcodes, blocks, b);
+ }
+ }
+ } else {
+ if (op_array->try_catch_array[j].catch_op) {
+ ZEND_ASSERT(!(blocks[block_map[op_array->try_catch_array[j].catch_op]].flags & ZEND_BB_REACHABLE));
+ }
+ if (op_array->try_catch_array[j].finally_op) {
+ ZEND_ASSERT(!(blocks[block_map[op_array->try_catch_array[j].finally_op]].flags & ZEND_BB_REACHABLE));
+ }
+ if (op_array->try_catch_array[j].finally_end) {
+ ZEND_ASSERT(!(blocks[block_map[op_array->try_catch_array[j].finally_end]].flags & ZEND_BB_REACHABLE));
+ }
+ }
+ }
+ } while (changed);
+ }
+}
+/* }}} */
+
+void zend_cfg_remark_reachable_blocks(const zend_op_array *op_array, zend_cfg *cfg) /* {{{ */
+{
+ zend_basic_block *blocks = cfg->blocks;
+ int i;
+ int start = 0;
+
+ for (i = 0; i < cfg->blocks_count; i++) {
+ if (blocks[i].flags & ZEND_BB_REACHABLE) {
+ start = i;
+ i++;
+ break;
+ }
+ }
+
+ /* clear all flags */
+ for (i = 0; i < cfg->blocks_count; i++) {
+ blocks[i].flags = 0;
+ }
+
+ zend_mark_reachable_blocks(op_array, cfg, start);
+}
+/* }}} */
+
+static void record_successor(zend_basic_block *blocks, int pred, int n, int succ)
+{
+ blocks[pred].successors[n] = succ;
+}
+
+static void initialize_block(zend_basic_block *block) {
+ block->flags = 0;
+ block->successors[0] = -1;
+ block->successors[1] = -1;
+ block->predecessors_count = 0;
+ block->predecessor_offset = -1;
+ block->idom = -1;
+ block->loop_header = -1;
+ block->level = -1;
+ block->children = -1;
+ block->next_child = -1;
+}
+
+#define BB_START(i) do { \
+ if (!block_map[i]) { blocks_count++;} \
+ block_map[i]++; \
+ } while (0)
+
+int zend_build_cfg(zend_arena **arena, const zend_op_array *op_array, uint32_t build_flags, zend_cfg *cfg, uint32_t *func_flags) /* {{{ */
+{
+ uint32_t flags = 0;
+ uint32_t i;
+ int j;
+ uint32_t *block_map;
+ zend_function *fn;
+ int blocks_count = 0;
+ zend_basic_block *blocks;
+ zval *zv;
+ zend_bool extra_entry_block = 0;
+
+ cfg->split_at_live_ranges = (build_flags & ZEND_CFG_SPLIT_AT_LIVE_RANGES) != 0;
+ cfg->map = block_map = zend_arena_calloc(arena, op_array->last, sizeof(uint32_t));
+ if (!block_map) {
+ return FAILURE;
+ }
+
+ /* Build CFG, Step 1: Find basic blocks starts, calculate number of blocks */
+ BB_START(0);
+ for (i = 0; i < op_array->last; i++) {
+ zend_op *opline = op_array->opcodes + i;
+ switch(opline->opcode) {
+ case ZEND_RETURN:
+ case ZEND_RETURN_BY_REF:
+ case ZEND_GENERATOR_RETURN:
+ case ZEND_EXIT:
+ case ZEND_THROW:
+ if (i + 1 < op_array->last) {
+ BB_START(i + 1);
+ }
+ break;
+ case ZEND_INCLUDE_OR_EVAL:
+ flags |= ZEND_FUNC_INDIRECT_VAR_ACCESS;
+ case ZEND_GENERATOR_CREATE:
+ case ZEND_YIELD:
+ case ZEND_YIELD_FROM:
+ if (build_flags & ZEND_CFG_STACKLESS) {
+ BB_START(i + 1);
+ }
+ break;
+ case ZEND_DO_FCALL:
+ case ZEND_DO_UCALL:
+ case ZEND_DO_FCALL_BY_NAME:
+ flags |= ZEND_FUNC_HAS_CALLS;
+ if (build_flags & ZEND_CFG_STACKLESS) {
+ BB_START(i + 1);
+ }
+ break;
+ case ZEND_DO_ICALL:
+ flags |= ZEND_FUNC_HAS_CALLS;
+ break;
+ case ZEND_INIT_FCALL:
+ case ZEND_INIT_NS_FCALL_BY_NAME:
+ zv = CRT_CONSTANT(opline->op2);
+ if (opline->opcode == ZEND_INIT_NS_FCALL_BY_NAME) {
+ /* The third literal is the lowercased unqualified name */
+ zv += 2;
+ }
+ if ((fn = zend_hash_find_ptr(EG(function_table), Z_STR_P(zv))) != NULL) {
+ if (fn->type == ZEND_INTERNAL_FUNCTION) {
+ flags |= zend_optimizer_classify_function(
+ Z_STR_P(zv), opline->extended_value);
+ }
+ }
+ break;
+ case ZEND_FAST_CALL:
+ BB_START(OP_JMP_ADDR(opline, opline->op1) - op_array->opcodes);
+ BB_START(i + 1);
+ break;
+ case ZEND_FAST_RET:
+ if (i + 1 < op_array->last) {
+ BB_START(i + 1);
+ }
+ break;
+ case ZEND_JMP:
+ BB_START(OP_JMP_ADDR(opline, opline->op1) - op_array->opcodes);
+ if (i + 1 < op_array->last) {
+ BB_START(i + 1);
+ }
+ break;
+ case ZEND_JMPZNZ:
+ BB_START(OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes);
+ BB_START(ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value));
+ if (i + 1 < op_array->last) {
+ BB_START(i + 1);
+ }
+ break;
+ case ZEND_JMPZ:
+ case ZEND_JMPNZ:
+ case ZEND_JMPZ_EX:
+ case ZEND_JMPNZ_EX:
+ case ZEND_JMP_SET:
+ case ZEND_COALESCE:
+ case ZEND_ASSERT_CHECK:
+ BB_START(OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes);
+ BB_START(i + 1);
+ break;
+ case ZEND_CATCH:
+ if (!opline->result.num) {
+ BB_START(ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value));
+ }
+ BB_START(i + 1);
+ break;
+ case ZEND_DECLARE_ANON_CLASS:
+ case ZEND_DECLARE_ANON_INHERITED_CLASS:
+ case ZEND_FE_FETCH_R:
+ case ZEND_FE_FETCH_RW:
+ BB_START(ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value));
+ BB_START(i + 1);
+ break;
+ case ZEND_FE_RESET_R:
+ case ZEND_FE_RESET_RW:
+ BB_START(OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes);
+ BB_START(i + 1);
+ break;
+ case ZEND_UNSET_VAR:
+ case ZEND_ISSET_ISEMPTY_VAR:
+ if (((opline->extended_value & ZEND_FETCH_TYPE_MASK) == ZEND_FETCH_LOCAL) &&
+ !(opline->extended_value & ZEND_QUICK_SET)) {
+ flags |= ZEND_FUNC_INDIRECT_VAR_ACCESS;
+ } else if (((opline->extended_value & ZEND_FETCH_TYPE_MASK) == ZEND_FETCH_GLOBAL ||
+ (opline->extended_value & ZEND_FETCH_TYPE_MASK) == ZEND_FETCH_GLOBAL_LOCK) &&
+ !op_array->function_name) {
+ flags |= ZEND_FUNC_INDIRECT_VAR_ACCESS;
+ }
+ break;
+ case ZEND_FETCH_R:
+ case ZEND_FETCH_W:
+ case ZEND_FETCH_RW:
+ case ZEND_FETCH_FUNC_ARG:
+ case ZEND_FETCH_IS:
+ case ZEND_FETCH_UNSET:
+ if ((opline->extended_value & ZEND_FETCH_TYPE_MASK) == ZEND_FETCH_LOCAL) {
+ flags |= ZEND_FUNC_INDIRECT_VAR_ACCESS;
+ } else if (((opline->extended_value & ZEND_FETCH_TYPE_MASK) == ZEND_FETCH_GLOBAL ||
+ (opline->extended_value & ZEND_FETCH_TYPE_MASK) == ZEND_FETCH_GLOBAL_LOCK) &&
+ !op_array->function_name) {
+ flags |= ZEND_FUNC_INDIRECT_VAR_ACCESS;
+ }
+ break;
+ }
+ }
+
+ /* If the entry block has predecessors, we may need to split it */
+ if ((build_flags & ZEND_CFG_NO_ENTRY_PREDECESSORS)
+ && op_array->last > 0 && block_map[0] > 1) {
+ extra_entry_block = 1;
+ }
+
+ if (cfg->split_at_live_ranges) {
+ for (j = 0; j < op_array->last_live_range; j++) {
+ BB_START(op_array->live_range[j].start);
+ BB_START(op_array->live_range[j].end);
+ }
+ }
+
+ if (op_array->last_try_catch) {
+ for (j = 0; j < op_array->last_try_catch; j++) {
+ BB_START(op_array->try_catch_array[j].try_op);
+ if (op_array->try_catch_array[j].catch_op) {
+ BB_START(op_array->try_catch_array[j].catch_op);
+ }
+ if (op_array->try_catch_array[j].finally_op) {
+ BB_START(op_array->try_catch_array[j].finally_op);
+ }
+ if (op_array->try_catch_array[j].finally_end) {
+ BB_START(op_array->try_catch_array[j].finally_end);
+ }
+ }
+ }
+
+ blocks_count += extra_entry_block;
+ cfg->blocks_count = blocks_count;
+
+ /* Build CFG, Step 2: Build Array of Basic Blocks */
+ cfg->blocks = blocks = zend_arena_calloc(arena, sizeof(zend_basic_block), blocks_count);
+ if (!blocks) {
+ return FAILURE;
+ }
+
+ blocks_count = -1;
+
+ if (extra_entry_block) {
+ initialize_block(&blocks[0]);
+ blocks[0].start = 0;
+ blocks[0].len = 0;
+ blocks_count++;
+ }
+
+ for (i = 0; i < op_array->last; i++) {
+ if (block_map[i]) {
+ if (blocks_count >= 0) {
+ blocks[blocks_count].len = i - blocks[blocks_count].start;
+ }
+ blocks_count++;
+ initialize_block(&blocks[blocks_count]);
+ blocks[blocks_count].start = i;
+ }
+ block_map[i] = blocks_count;
+ }
+
+ blocks[blocks_count].len = i - blocks[blocks_count].start;
+ blocks_count++;
+
+ /* Build CFG, Step 3: Calculate successors */
+ for (j = 0; j < blocks_count; j++) {
+ zend_op *opline;
+ if (blocks[j].len == 0) {
+ record_successor(blocks, j, 0, j + 1);
+ continue;
+ }
+
+ opline = op_array->opcodes + blocks[j].start + blocks[j].len - 1;
+ switch (opline->opcode) {
+ case ZEND_FAST_RET:
+ case ZEND_RETURN:
+ case ZEND_RETURN_BY_REF:
+ case ZEND_GENERATOR_RETURN:
+ case ZEND_EXIT:
+ case ZEND_THROW:
+ break;
+ case ZEND_JMP:
+ record_successor(blocks, j, 0, block_map[OP_JMP_ADDR(opline, opline->op1) - op_array->opcodes]);
+ break;
+ case ZEND_JMPZNZ:
+ record_successor(blocks, j, 0, block_map[OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes]);
+ record_successor(blocks, j, 1, block_map[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)]);
+ break;
+ case ZEND_JMPZ:
+ case ZEND_JMPNZ:
+ case ZEND_JMPZ_EX:
+ case ZEND_JMPNZ_EX:
+ case ZEND_JMP_SET:
+ case ZEND_COALESCE:
+ case ZEND_ASSERT_CHECK:
+ record_successor(blocks, j, 0, block_map[OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes]);
+ record_successor(blocks, j, 1, j + 1);
+ break;
+ case ZEND_CATCH:
+ if (!opline->result.num) {
+ record_successor(blocks, j, 0, block_map[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)]);
+ record_successor(blocks, j, 1, j + 1);
+ } else {
+ record_successor(blocks, j, 0, j + 1);
+ }
+ break;
+ case ZEND_DECLARE_ANON_CLASS:
+ case ZEND_DECLARE_ANON_INHERITED_CLASS:
+ case ZEND_FE_FETCH_R:
+ case ZEND_FE_FETCH_RW:
+ record_successor(blocks, j, 0, block_map[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)]);
+ record_successor(blocks, j, 1, j + 1);
+ break;
+ case ZEND_FE_RESET_R:
+ case ZEND_FE_RESET_RW:
+ record_successor(blocks, j, 0, block_map[OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes]);
+ record_successor(blocks, j, 1, j + 1);
+ break;
+ case ZEND_FAST_CALL:
+ record_successor(blocks, j, 0, block_map[OP_JMP_ADDR(opline, opline->op1) - op_array->opcodes]);
+ record_successor(blocks, j, 1, j + 1);
+ break;
+ default:
+ record_successor(blocks, j, 0, j + 1);
+ break;
+ }
+ }
+
+ /* Build CFG, Step 4, Mark Reachable Basic Blocks */
+ zend_mark_reachable_blocks(op_array, cfg, 0);
+
+ if (func_flags) {
+ *func_flags |= flags;
+ }
+
+ return SUCCESS;
+}
+/* }}} */
+
+int zend_cfg_build_predecessors(zend_arena **arena, zend_cfg *cfg) /* {{{ */
+{
+ int j, edges;
+ zend_basic_block *b;
+ zend_basic_block *blocks = cfg->blocks;
+ zend_basic_block *end = blocks + cfg->blocks_count;
+ int *predecessors;
+
+ edges = 0;
+ for (b = blocks; b < end; b++) {
+ b->predecessors_count = 0;
+ }
+ for (b = blocks; b < end; b++) {
+ if (!(b->flags & ZEND_BB_REACHABLE)) {
+ b->successors[0] = -1;
+ b->successors[1] = -1;
+ b->predecessors_count = 0;
+ } else {
+ if (b->successors[0] >= 0) {
+ edges++;
+ blocks[b->successors[0]].predecessors_count++;
+ if (b->successors[1] >= 0 && b->successors[1] != b->successors[0]) {
+ edges++;
+ blocks[b->successors[1]].predecessors_count++;
+ }
+ }
+ }
+ }
+
+ cfg->predecessors = predecessors = (int*)zend_arena_calloc(arena, sizeof(int), edges);
+
+ if (!predecessors) {
+ return FAILURE;
+ }
+
+ edges = 0;
+ for (b = blocks; b < end; b++) {
+ if (b->flags & ZEND_BB_REACHABLE) {
+ b->predecessor_offset = edges;
+ edges += b->predecessors_count;
+ b->predecessors_count = 0;
+ }
+ }
+
+ for (j = 0; j < cfg->blocks_count; j++) {
+ if (blocks[j].flags & ZEND_BB_REACHABLE) {
+ if (blocks[j].successors[0] >= 0) {
+ zend_basic_block *b = blocks + blocks[j].successors[0];
+ predecessors[b->predecessor_offset + b->predecessors_count] = j;
+ b->predecessors_count++;
+ if (blocks[j].successors[1] >= 0
+ && blocks[j].successors[1] != blocks[j].successors[0]) {
+ zend_basic_block *b = blocks + blocks[j].successors[1];
+ predecessors[b->predecessor_offset + b->predecessors_count] = j;
+ b->predecessors_count++;
+ }
+ }
+ }
+ }
+
+ return SUCCESS;
+}
+/* }}} */
+
+/* Computes a postorder numbering of the CFG */
+static void compute_postnum_recursive(
+ int *postnum, int *cur, const zend_cfg *cfg, int block_num) /* {{{ */
+{
+ zend_basic_block *block = &cfg->blocks[block_num];
+ if (postnum[block_num] != -1) {
+ return;
+ }
+
+ postnum[block_num] = -2; /* Marker for "currently visiting" */
+ if (block->successors[0] >= 0) {
+ compute_postnum_recursive(postnum, cur, cfg, block->successors[0]);
+ if (block->successors[1] >= 0) {
+ compute_postnum_recursive(postnum, cur, cfg, block->successors[1]);
+ }
+ }
+ postnum[block_num] = (*cur)++;
+}
+/* }}} */
+
+/* Computes dominator tree using algorithm from "A Simple, Fast Dominance Algorithm" by
+ * Cooper, Harvey and Kennedy. */
+int zend_cfg_compute_dominators_tree(const zend_op_array *op_array, zend_cfg *cfg) /* {{{ */
+{
+ zend_basic_block *blocks = cfg->blocks;
+ int blocks_count = cfg->blocks_count;
+ int j, k, changed;
+
+ ALLOCA_FLAG(use_heap)
+ int *postnum = do_alloca(sizeof(int) * cfg->blocks_count, use_heap);
+ memset(postnum, -1, sizeof(int) * cfg->blocks_count);
+ j = 0;
+ compute_postnum_recursive(postnum, &j, cfg, 0);
+
+ /* FIXME: move declarations */
+ blocks[0].idom = 0;
+ do {
+ changed = 0;
+ /* Iterating in RPO here would converge faster */
+ for (j = 1; j < blocks_count; j++) {
+ int idom = -1;
+
+ if ((blocks[j].flags & ZEND_BB_REACHABLE) == 0) {
+ continue;
+ }
+ for (k = 0; k < blocks[j].predecessors_count; k++) {
+ int pred = cfg->predecessors[blocks[j].predecessor_offset + k];
+
+ if (idom < 0) {
+ if (blocks[pred].idom >= 0)
+ idom = pred;
+ continue;
+ }
+
+ if (blocks[pred].idom >= 0) {
+ while (idom != pred) {
+ while (postnum[pred] < postnum[idom]) pred = blocks[pred].idom;
+ while (postnum[idom] < postnum[pred]) idom = blocks[idom].idom;
+ }
+ }
+ }
+
+ if (idom >= 0 && blocks[j].idom != idom) {
+ blocks[j].idom = idom;
+ changed = 1;
+ }
+ }
+ } while (changed);
+ blocks[0].idom = -1;
+
+ for (j = 1; j < blocks_count; j++) {
+ if ((blocks[j].flags & ZEND_BB_REACHABLE) == 0) {
+ continue;
+ }
+ if (blocks[j].idom >= 0) {
+ /* Sort by block number to traverse children in pre-order */
+ if (blocks[blocks[j].idom].children < 0 ||
+ j < blocks[blocks[j].idom].children) {
+ blocks[j].next_child = blocks[blocks[j].idom].children;
+ blocks[blocks[j].idom].children = j;
+ } else {
+ int k = blocks[blocks[j].idom].children;
+ while (blocks[k].next_child >=0 && j > blocks[k].next_child) {
+ k = blocks[k].next_child;
+ }
+ blocks[j].next_child = blocks[k].next_child;
+ blocks[k].next_child = j;
+ }
+ }
+ }
+
+ for (j = 0; j < blocks_count; j++) {
+ int idom = blocks[j].idom, level = 0;
+ if ((blocks[j].flags & ZEND_BB_REACHABLE) == 0) {
+ continue;
+ }
+ while (idom >= 0) {
+ level++;
+ if (blocks[idom].level >= 0) {
+ level += blocks[idom].level;
+ break;
+ } else {
+ idom = blocks[idom].idom;
+ }
+ }
+ blocks[j].level = level;
+ }
+
+ free_alloca(postnum, use_heap);
+ return SUCCESS;
+}
+/* }}} */
+
+static int dominates(zend_basic_block *blocks, int a, int b) /* {{{ */
+{
+ while (blocks[b].level > blocks[a].level) {
+ b = blocks[b].idom;
+ }
+ return a == b;
+}
+/* }}} */
+
+int zend_cfg_identify_loops(const zend_op_array *op_array, zend_cfg *cfg, uint32_t *flags) /* {{{ */
+{
+ int i, j, k;
+ int depth;
+ zend_basic_block *blocks = cfg->blocks;
+ int *dj_spanning_tree;
+ zend_worklist work;
+ int flag = ZEND_FUNC_NO_LOOPS;
+ ALLOCA_FLAG(list_use_heap)
+ ALLOCA_FLAG(tree_use_heap)
+
+ ZEND_WORKLIST_ALLOCA(&work, cfg->blocks_count, list_use_heap);
+ dj_spanning_tree = do_alloca(sizeof(int) * cfg->blocks_count, tree_use_heap);
+
+ for (i = 0; i < cfg->blocks_count; i++) {
+ dj_spanning_tree[i] = -1;
+ }
+ zend_worklist_push(&work, 0);
+ while (zend_worklist_len(&work)) {
+ next:
+ i = zend_worklist_peek(&work);
+ /* Visit blocks immediately dominated by i. */
+ for (j = blocks[i].children; j >= 0; j = blocks[j].next_child) {
+ if (zend_worklist_push(&work, j)) {
+ dj_spanning_tree[j] = i;
+ goto next;
+ }
+ }
+ /* Visit join edges. */
+ for (j = 0; j < 2; j++) {
+ int succ = blocks[i].successors[j];
+ if (succ < 0) {
+ continue;
+ } else if (blocks[succ].idom == i) {
+ continue;
+ } else if (zend_worklist_push(&work, succ)) {
+ dj_spanning_tree[succ] = i;
+ goto next;
+ }
+ }
+ zend_worklist_pop(&work);
+ }
+
+ /* Identify loops. See Sreedhar et al, "Identifying Loops Using DJ
+ Graphs". */
+
+ for (i = 0, depth = 0; i < cfg->blocks_count; i++) {
+ if (blocks[i].level > depth) {
+ depth = blocks[i].level;
+ }
+ }
+ for (; depth >= 0; depth--) {
+ for (i = 0; i < cfg->blocks_count; i++) {
+ if (blocks[i].level != depth) {
+ continue;
+ }
+ zend_bitset_clear(work.visited, zend_bitset_len(cfg->blocks_count));
+ for (j = 0; j < blocks[i].predecessors_count; j++) {
+ int pred = cfg->predecessors[blocks[i].predecessor_offset + j];
+
+ /* A join edge is one for which the predecessor does not
+ immediately dominate the successor. */
+ if (blocks[i].idom == pred) {
+ continue;
+ }
+
+ /* In a loop back-edge (back-join edge), the successor dominates
+ the predecessor. */
+ if (dominates(blocks, i, pred)) {
+ blocks[i].flags |= ZEND_BB_LOOP_HEADER;
+ flag &= ~ZEND_FUNC_NO_LOOPS;
+ zend_worklist_push(&work, pred);
+ } else {
+ /* Otherwise it's a cross-join edge. See if it's a branch
+ to an ancestor on the dominator spanning tree. */
+ int dj_parent = pred;
+ while (dj_parent >= 0) {
+ if (dj_parent == i) {
+ /* An sp-back edge: mark as irreducible. */
+ blocks[i].flags |= ZEND_BB_IRREDUCIBLE_LOOP;
+ flag |= ZEND_FUNC_IRREDUCIBLE;
+ flag &= ~ZEND_FUNC_NO_LOOPS;
+ break;
+ } else {
+ dj_parent = dj_spanning_tree[dj_parent];
+ }
+ }
+ }
+ }
+ while (zend_worklist_len(&work)) {
+ j = zend_worklist_pop(&work);
+ if (blocks[j].loop_header < 0 && j != i) {
+ blocks[j].loop_header = i;
+ for (k = 0; k < blocks[j].predecessors_count; k++) {
+ zend_worklist_push(&work, cfg->predecessors[blocks[j].predecessor_offset + k]);
+ }
+ }
+ }
+ }
+ }
+
+ free_alloca(dj_spanning_tree, tree_use_heap);
+ ZEND_WORKLIST_FREE_ALLOCA(&work, list_use_heap);
+ *flags |= flag;
+
+ return SUCCESS;
+}
+/* }}} */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * indent-tabs-mode: t
+ * End:
+ */
diff --git a/ext/opcache/Optimizer/zend_cfg.h b/ext/opcache/Optimizer/zend_cfg.h
new file mode 100644
index 0000000000..da908fdbe3
--- /dev/null
+++ b/ext/opcache/Optimizer/zend_cfg.h
@@ -0,0 +1,132 @@
+/*
+ +----------------------------------------------------------------------+
+ | Zend Engine, CFG - Control Flow Graph |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1998-2016 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> |
+ +----------------------------------------------------------------------+
+*/
+
+#ifndef ZEND_CFG_H
+#define ZEND_CFG_H
+
+/* zend_basic_bloc.flags */
+#define ZEND_BB_START (1<<0) /* fist block */
+#define ZEND_BB_FOLLOW (1<<1) /* follows the next block */
+#define ZEND_BB_TARGET (1<<2) /* jump taget */
+#define ZEND_BB_EXIT (1<<3) /* without successors */
+#define ZEND_BB_ENTRY (1<<4) /* stackless entry */
+#define ZEND_BB_TRY (1<<5) /* start of try block */
+#define ZEND_BB_CATCH (1<<6) /* start of catch block */
+#define ZEND_BB_FINALLY (1<<7) /* start of finally block */
+#define ZEND_BB_FINALLY_END (1<<8) /* end of finally block */
+#define ZEND_BB_GEN_VAR (1<<9) /* start of live range */
+#define ZEND_BB_KILL_VAR (1<<10) /* end of live range */
+#define ZEND_BB_UNREACHABLE_FREE (1<<11) /* unreachable loop free */
+
+#define ZEND_BB_LOOP_HEADER (1<<16)
+#define ZEND_BB_IRREDUCIBLE_LOOP (1<<17)
+
+#define ZEND_BB_REACHABLE (1<<31)
+
+#define ZEND_BB_PROTECTED (ZEND_BB_ENTRY|ZEND_BB_TRY|ZEND_BB_CATCH|ZEND_BB_FINALLY|ZEND_BB_FINALLY_END|ZEND_BB_GEN_VAR|ZEND_BB_KILL_VAR)
+
+typedef struct _zend_basic_block {
+ uint32_t flags;
+ uint32_t start; /* first opcode number */
+ uint32_t len; /* number of opcodes */
+ int successors[2]; /* up to 2 successor blocks */
+ int predecessors_count; /* number of predecessors */
+ int predecessor_offset; /* offset of 1-st predecessor */
+ int idom; /* immediate dominator block */
+ int loop_header; /* closest loop header, or -1 */
+ int level; /* steps away from the entry in the dom. tree */
+ int children; /* list of dominated blocks */
+ int next_child; /* next dominated block */
+} zend_basic_block;
+
+/*
++------------+---+---+---+---+---+
+| |OP1|OP2|EXT| 0 | 1 |
++------------+---+---+---+---+---+
+|JMP |ADR| | |OP1| - |
+|JMPZ | |ADR| |OP2|FOL|
+|JMPNZ | |ADR| |OP2|FOL|
+|JMPZNZ | |ADR|ADR|OP2|EXT|
+|JMPZ_EX | |ADR| |OP2|FOL|
+|JMPNZ_EX | |ADR| |OP2|FOL|
+|JMP_SET | |ADR| |OP2|FOL|
+|COALESCE | |ADR| |OP2|FOL|
+|ASSERT_CHK | |ADR| |OP2|FOL|
+|NEW | |ADR| |OP2|FOL|
+|DCL_ANON* |ADR| | |OP1|FOL|
+|FE_RESET_* | |ADR| |OP2|FOL|
+|FE_FETCH_* | | |ADR|EXT|FOL|
+|CATCH | | |ADR|EXT|FOL|
+|FAST_CALL |ADR| | |OP1|FOL|
+|FAST_RET | | | | - | - |
+|RETURN* | | | | - | - |
+|EXIT | | | | - | - |
+|THROW | | | | - | - |
+|* | | | |FOL| - |
++------------+---+---+---+---+---+
+*/
+
+typedef struct _zend_cfg {
+ int blocks_count; /* number of basic blocks */
+ zend_basic_block *blocks; /* array of basic blocks */
+ int *predecessors;
+ uint32_t *map;
+ unsigned int split_at_live_ranges : 1;
+} zend_cfg;
+
+/* Build Flags */
+#define ZEND_RT_CONSTANTS (1<<31)
+#define ZEND_CFG_STACKLESS (1<<30)
+#define ZEND_SSA_DEBUG_LIVENESS (1<<29)
+#define ZEND_SSA_DEBUG_PHI_PLACEMENT (1<<28)
+#define ZEND_SSA_RC_INFERENCE (1<<27)
+#define ZEND_CFG_SPLIT_AT_LIVE_RANGES (1<<26)
+#define ZEND_CFG_NO_ENTRY_PREDECESSORS (1<<25)
+
+#define CRT_CONSTANT_EX(op_array, node, rt_constants) \
+ ((rt_constants) ? \
+ RT_CONSTANT(op_array, (node)) \
+ : \
+ CT_CONSTANT_EX(op_array, (node).constant) \
+ )
+
+#define CRT_CONSTANT(node) \
+ CRT_CONSTANT_EX(op_array, node, (build_flags & ZEND_RT_CONSTANTS))
+
+#define RETURN_VALUE_USED(opline) \
+ ((opline)->result_type != IS_UNUSED)
+
+BEGIN_EXTERN_C()
+
+int zend_build_cfg(zend_arena **arena, const zend_op_array *op_array, uint32_t build_flags, zend_cfg *cfg, uint32_t *func_flags);
+void zend_cfg_remark_reachable_blocks(const zend_op_array *op_array, zend_cfg *cfg);
+int zend_cfg_build_predecessors(zend_arena **arena, zend_cfg *cfg);
+int zend_cfg_compute_dominators_tree(const zend_op_array *op_array, zend_cfg *cfg);
+int zend_cfg_identify_loops(const zend_op_array *op_array, zend_cfg *cfg, uint32_t *flags);
+
+END_EXTERN_C()
+
+#endif /* ZEND_CFG_H */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * indent-tabs-mode: t
+ * End:
+ */
diff --git a/ext/opcache/Optimizer/zend_dfg.c b/ext/opcache/Optimizer/zend_dfg.c
new file mode 100644
index 0000000000..97fc846d3c
--- /dev/null
+++ b/ext/opcache/Optimizer/zend_dfg.c
@@ -0,0 +1,248 @@
+/*
+ +----------------------------------------------------------------------+
+ | Zend Engine, DFG - Data Flow Graph |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1998-2016 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> |
+ +----------------------------------------------------------------------+
+*/
+
+#include "php.h"
+#include "zend_compile.h"
+#include "zend_dfg.h"
+
+int zend_build_dfg(const zend_op_array *op_array, const zend_cfg *cfg, zend_dfg *dfg, uint32_t build_flags) /* {{{ */
+{
+ int set_size;
+ zend_basic_block *blocks = cfg->blocks;
+ int blocks_count = cfg->blocks_count;
+ zend_bitset tmp, def, use, in, out;
+ uint32_t k, var_num;
+ int j;
+
+ set_size = dfg->size;
+ tmp = dfg->tmp;
+ def = dfg->def;
+ use = dfg->use;
+ in = dfg->in;
+ out = dfg->out;
+
+ /* Collect "def" and "use" sets */
+ for (j = 0; j < blocks_count; j++) {
+ zend_op *opline, *end;
+ if ((blocks[j].flags & ZEND_BB_REACHABLE) == 0) {
+ continue;
+ }
+
+ opline = op_array->opcodes + blocks[j].start;
+ end = opline + blocks[j].len;
+ for (; opline < end; opline++) {
+ if (opline->opcode != ZEND_OP_DATA) {
+ zend_op *next = opline + 1;
+ if (next < end && next->opcode == ZEND_OP_DATA) {
+ if (next->op1_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
+ var_num = EX_VAR_TO_NUM(next->op1.var);
+ if (!DFG_ISSET(def, set_size, j, var_num)) {
+ DFG_SET(use, set_size, j, var_num);
+ }
+ }
+ if (next->op2_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
+ var_num = EX_VAR_TO_NUM(next->op2.var);
+ if (!DFG_ISSET(def, set_size, j, var_num)) {
+ DFG_SET(use, set_size, j, var_num);
+ }
+ }
+ }
+ if (opline->op1_type == IS_CV) {
+ var_num = EX_VAR_TO_NUM(opline->op1.var);
+ switch (opline->opcode) {
+ case ZEND_ADD_ARRAY_ELEMENT:
+ case ZEND_INIT_ARRAY:
+ if ((build_flags & ZEND_SSA_RC_INFERENCE)
+ || (opline->extended_value & ZEND_ARRAY_ELEMENT_REF)) {
+ goto op1_def;
+ }
+ goto op1_use;
+ case ZEND_FE_RESET_R:
+ if (build_flags & ZEND_SSA_RC_INFERENCE) {
+ goto op1_def;
+ }
+ goto op1_use;
+ case ZEND_YIELD:
+ if ((build_flags & ZEND_SSA_RC_INFERENCE)
+ || (op_array->fn_flags & ZEND_ACC_RETURN_REFERENCE)) {
+ goto op1_def;
+ }
+ goto op1_use;
+ case ZEND_UNSET_VAR:
+ ZEND_ASSERT(opline->extended_value & ZEND_QUICK_SET);
+ /* break missing intentionally */
+ case ZEND_ASSIGN:
+ case ZEND_ASSIGN_REF:
+ case ZEND_BIND_GLOBAL:
+ case ZEND_BIND_STATIC:
+ case ZEND_SEND_VAR_EX:
+ case ZEND_SEND_REF:
+ case ZEND_SEND_VAR_NO_REF:
+ case ZEND_SEND_VAR_NO_REF_EX:
+ case ZEND_FE_RESET_RW:
+ case ZEND_ASSIGN_ADD:
+ case ZEND_ASSIGN_SUB:
+ case ZEND_ASSIGN_MUL:
+ case ZEND_ASSIGN_DIV:
+ case ZEND_ASSIGN_MOD:
+ case ZEND_ASSIGN_SL:
+ case ZEND_ASSIGN_SR:
+ case ZEND_ASSIGN_CONCAT:
+ case ZEND_ASSIGN_BW_OR:
+ case ZEND_ASSIGN_BW_AND:
+ case ZEND_ASSIGN_BW_XOR:
+ case ZEND_ASSIGN_POW:
+ case ZEND_PRE_INC:
+ case ZEND_PRE_DEC:
+ case ZEND_POST_INC:
+ case ZEND_POST_DEC:
+ case ZEND_ASSIGN_DIM:
+ case ZEND_ASSIGN_OBJ:
+ case ZEND_UNSET_DIM:
+ case ZEND_UNSET_OBJ:
+ case ZEND_FETCH_DIM_W:
+ case ZEND_FETCH_DIM_RW:
+ case ZEND_FETCH_DIM_FUNC_ARG:
+ case ZEND_FETCH_DIM_UNSET:
+ case ZEND_FETCH_OBJ_W:
+ case ZEND_FETCH_OBJ_RW:
+ case ZEND_FETCH_OBJ_FUNC_ARG:
+ case ZEND_FETCH_OBJ_UNSET:
+ case ZEND_VERIFY_RETURN_TYPE:
+op1_def:
+ /* `def` always come along with dtor or separation,
+ * thus the origin var info might be also `use`d in the feature(CG) */
+ DFG_SET(use, set_size, j, var_num);
+ DFG_SET(def, set_size, j, var_num);
+ break;
+ default:
+op1_use:
+ if (!DFG_ISSET(def, set_size, j, var_num)) {
+ DFG_SET(use, set_size, j, var_num);
+ }
+ }
+ } else if (opline->op1_type & (IS_VAR|IS_TMP_VAR)) {
+ var_num = EX_VAR_TO_NUM(opline->op1.var);
+ if (opline->opcode == ZEND_VERIFY_RETURN_TYPE) {
+ DFG_SET(use, set_size, j, var_num);
+ DFG_SET(def, set_size, j, var_num);
+ } else if (!DFG_ISSET(def, set_size, j, var_num)) {
+ DFG_SET(use, set_size, j, var_num);
+ }
+ }
+ if (opline->op2_type == IS_CV) {
+ var_num = EX_VAR_TO_NUM(opline->op2.var);
+ switch (opline->opcode) {
+ case ZEND_ASSIGN:
+ if (build_flags & ZEND_SSA_RC_INFERENCE) {
+ goto op2_def;
+ }
+ goto op2_use;
+ case ZEND_BIND_LEXICAL:
+ if ((build_flags & ZEND_SSA_RC_INFERENCE) || opline->extended_value) {
+ goto op2_def;
+ }
+ goto op2_use;
+ case ZEND_ASSIGN_REF:
+ case ZEND_FE_FETCH_R:
+ case ZEND_FE_FETCH_RW:
+op2_def:
+ // FIXME: include into "use" too ...?
+ DFG_SET(use, set_size, j, var_num);
+ DFG_SET(def, set_size, j, var_num);
+ break;
+ default:
+op2_use:
+ if (!DFG_ISSET(def, set_size, j, var_num)) {
+ DFG_SET(use, set_size, j, var_num);
+ }
+ break;
+ }
+ } else if (opline->op2_type & (IS_VAR|IS_TMP_VAR)) {
+ var_num = EX_VAR_TO_NUM(opline->op2.var);
+ if (opline->opcode == ZEND_FE_FETCH_R || opline->opcode == ZEND_FE_FETCH_RW) {
+ DFG_SET(def, set_size, j, var_num);
+ } else {
+ if (!DFG_ISSET(def, set_size, j, var_num)) {
+ DFG_SET(use, set_size, j, var_num);
+ }
+ }
+ }
+ if (opline->result_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
+ var_num = EX_VAR_TO_NUM(opline->result.var);
+ DFG_SET(def, set_size, j, var_num);
+ }
+ }
+ }
+ }
+
+ /* Calculate "in" and "out" sets */
+ {
+ uint32_t worklist_len = zend_bitset_len(blocks_count);
+ zend_bitset worklist;
+ ALLOCA_FLAG(use_heap);
+ worklist = ZEND_BITSET_ALLOCA(worklist_len, use_heap);
+ memset(worklist, 0, worklist_len * ZEND_BITSET_ELM_SIZE);
+ for (j = 0; j < blocks_count; j++) {
+ zend_bitset_incl(worklist, j);
+ }
+ while (!zend_bitset_empty(worklist, worklist_len)) {
+ /* We use the last block on the worklist, because predecessors tend to be located
+ * before the succeeding block, so this converges faster. */
+ j = zend_bitset_last(worklist, worklist_len);
+ zend_bitset_excl(worklist, j);
+
+ if ((blocks[j].flags & ZEND_BB_REACHABLE) == 0) {
+ continue;
+ }
+ if (blocks[j].successors[0] >= 0) {
+ zend_bitset_copy(DFG_BITSET(out, set_size, j), DFG_BITSET(in, set_size, blocks[j].successors[0]), set_size);
+ if (blocks[j].successors[1] >= 0) {
+ zend_bitset_union(DFG_BITSET(out, set_size, j), DFG_BITSET(in, set_size, blocks[j].successors[1]), set_size);
+ }
+ } else {
+ zend_bitset_clear(DFG_BITSET(out, set_size, j), set_size);
+ }
+ zend_bitset_union_with_difference(tmp, DFG_BITSET(use, set_size, j), DFG_BITSET(out, set_size, j), DFG_BITSET(def, set_size, j), set_size);
+ if (!zend_bitset_equal(DFG_BITSET(in, set_size, j), tmp, set_size)) {
+ zend_bitset_copy(DFG_BITSET(in, set_size, j), tmp, set_size);
+
+ /* Add predecessors of changed block to worklist */
+ {
+ int *predecessors = &cfg->predecessors[blocks[j].predecessor_offset];
+ for (k = 0; k < blocks[j].predecessors_count; k++) {
+ zend_bitset_incl(worklist, predecessors[k]);
+ }
+ }
+ }
+ }
+
+ free_alloca(worklist, use_heap);
+ }
+
+ return SUCCESS;
+}
+/* }}} */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * indent-tabs-mode: t
+ * End:
+ */
diff --git a/ext/opcache/Optimizer/zend_dfg.h b/ext/opcache/Optimizer/zend_dfg.h
new file mode 100644
index 0000000000..5ed8cfc5d0
--- /dev/null
+++ b/ext/opcache/Optimizer/zend_dfg.h
@@ -0,0 +1,58 @@
+/*
+ +----------------------------------------------------------------------+
+ | Zend Engine, DFG - Data Flow Graph |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1998-2016 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> |
+ +----------------------------------------------------------------------+
+*/
+
+#ifndef ZEND_DFG_H
+#define ZEND_DFG_H
+
+#include "zend_bitset.h"
+#include "zend_cfg.h"
+
+typedef struct _zend_dfg {
+ int vars;
+ uint32_t size;
+ zend_bitset tmp;
+ zend_bitset def;
+ zend_bitset use;
+ zend_bitset in;
+ zend_bitset out;
+} zend_dfg;
+
+#define DFG_BITSET(set, set_size, block_num) \
+ ((set) + ((block_num) * (set_size)))
+
+#define DFG_SET(set, set_size, block_num, var_num) \
+ zend_bitset_incl(DFG_BITSET(set, set_size, block_num), (var_num))
+
+#define DFG_ISSET(set, set_size, block_num, var_num) \
+ zend_bitset_in(DFG_BITSET(set, set_size, block_num), (var_num))
+
+BEGIN_EXTERN_C()
+
+int zend_build_dfg(const zend_op_array *op_array, const zend_cfg *cfg, zend_dfg *dfg, uint32_t build_flags);
+
+END_EXTERN_C()
+
+#endif /* ZEND_DFG_H */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * indent-tabs-mode: t
+ * End:
+ */
diff --git a/ext/opcache/Optimizer/zend_dump.c b/ext/opcache/Optimizer/zend_dump.c
new file mode 100644
index 0000000000..a2eeb886ee
--- /dev/null
+++ b/ext/opcache/Optimizer/zend_dump.c
@@ -0,0 +1,1175 @@
+/*
+ +----------------------------------------------------------------------+
+ | Zend Engine, Bytecode Visualisation |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1998-2016 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> |
+ +----------------------------------------------------------------------+
+*/
+
+#include "php.h"
+#include "zend_compile.h"
+#include "zend_cfg.h"
+#include "zend_ssa.h"
+#include "zend_inference.h"
+#include "zend_func_info.h"
+#include "zend_call_graph.h"
+#include "zend_dump.h"
+
+static void zend_dump_const(const zval *zv)
+{
+ switch (Z_TYPE_P(zv)) {
+ case IS_NULL:
+ fprintf(stderr, " null");
+ break;
+ case IS_FALSE:
+ fprintf(stderr, " bool(false)");
+ break;
+ case IS_TRUE:
+ fprintf(stderr, " bool(true)");
+ break;
+ case IS_LONG:
+ fprintf(stderr, " int(" ZEND_LONG_FMT ")", Z_LVAL_P(zv));
+ break;
+ case IS_DOUBLE:
+ fprintf(stderr, " float(%g)", Z_DVAL_P(zv));
+ break;
+ case IS_STRING:
+ fprintf(stderr, " string(\"%s\")", Z_STRVAL_P(zv));
+ break;
+ case IS_ARRAY:
+ fprintf(stderr, " array(...)");
+ break;
+ default:
+ fprintf(stderr, " zval(type=%d)", Z_TYPE_P(zv));
+ break;
+ }
+}
+
+static void zend_dump_class_fetch_type(uint32_t fetch_type)
+{
+ switch (fetch_type & ZEND_FETCH_CLASS_MASK) {
+ case ZEND_FETCH_CLASS_SELF:
+ fprintf(stderr, " (self)");
+ break;
+ case ZEND_FETCH_CLASS_PARENT:
+ fprintf(stderr, " (parent)");
+ break;
+ case ZEND_FETCH_CLASS_STATIC:
+ fprintf(stderr, " (static)");
+ break;
+ case ZEND_FETCH_CLASS_AUTO:
+ fprintf(stderr, " (auto)");
+ break;
+ case ZEND_FETCH_CLASS_INTERFACE:
+ fprintf(stderr, " (interface)");
+ break;
+ case ZEND_FETCH_CLASS_TRAIT:
+ fprintf(stderr, " (trait)");
+ break;
+ }
+ if (fetch_type & ZEND_FETCH_CLASS_NO_AUTOLOAD) {
+ fprintf(stderr, " (no-autolod)");
+ }
+ if (fetch_type & ZEND_FETCH_CLASS_SILENT) {
+ fprintf(stderr, " (silent)");
+ }
+ if (fetch_type & ZEND_FETCH_CLASS_EXCEPTION) {
+ fprintf(stderr, " (exception)");
+ }
+}
+
+static void zend_dump_unused_op(const zend_op *opline, znode_op op, uint32_t flags) {
+ if (ZEND_VM_OP_NUM == (flags & ZEND_VM_OP_MASK)) {
+ fprintf(stderr, " %u", op.num);
+ } else if (ZEND_VM_OP_TRY_CATCH == (flags & ZEND_VM_OP_MASK)) {
+ if (op.num != (uint32_t)-1) {
+ fprintf(stderr, " try-catch(%u)", op.num);
+ }
+ } else if (ZEND_VM_OP_LIVE_RANGE == (flags & ZEND_VM_OP_MASK)) {
+ if (opline->extended_value & ZEND_FREE_ON_RETURN) {
+ fprintf(stderr, " live-range(%u)", op.num);
+ }
+ } else if (ZEND_VM_OP_THIS == (flags & ZEND_VM_OP_MASK)) {
+ fprintf(stderr, " THIS");
+ } else if (ZEND_VM_OP_NEXT == (flags & ZEND_VM_OP_MASK)) {
+ fprintf(stderr, " NEXT");
+ } else if (ZEND_VM_OP_CLASS_FETCH == (flags & ZEND_VM_OP_MASK)) {
+ zend_dump_class_fetch_type(op.num);
+ } else if (ZEND_VM_OP_CONSTRUCTOR == (flags & ZEND_VM_OP_MASK)) {
+ fprintf(stderr, " CONSTRUCTOR");
+ }
+}
+
+void zend_dump_var(const zend_op_array *op_array, zend_uchar var_type, int var_num)
+{
+ if (var_type == IS_CV && var_num < op_array->last_var) {
+ fprintf(stderr, "CV%d($%s)", var_num, op_array->vars[var_num]->val);
+ } else if (var_type == IS_VAR) {
+ fprintf(stderr, "V%d", var_num);
+ } else if (var_type == IS_TMP_VAR) {
+ fprintf(stderr, "T%d", var_num);
+ } else {
+ fprintf(stderr, "X%d", var_num);
+ }
+}
+
+static void zend_dump_range(const zend_ssa_range *r)
+{
+ if (r->underflow && r->overflow) {
+ return;
+ }
+ fprintf(stderr, " RANGE[");
+ if (r->underflow) {
+ fprintf(stderr, "--..");
+ } else {
+ fprintf(stderr, ZEND_LONG_FMT "..", r->min);
+ }
+ if (r->overflow) {
+ fprintf(stderr, "++]");
+ } else {
+ fprintf(stderr, ZEND_LONG_FMT "]", r->max);
+ }
+}
+
+static void zend_dump_type_info(uint32_t info, zend_class_entry *ce, int is_instanceof, uint32_t dump_flags)
+{
+ int first = 1;
+
+ fprintf(stderr, " [");
+ if (info & MAY_BE_UNDEF) {
+ if (first) first = 0; else fprintf(stderr, ", ");
+ fprintf(stderr, "undef");
+ }
+ if (info & MAY_BE_REF) {
+ if (first) first = 0; else fprintf(stderr, ", ");
+ fprintf(stderr, "ref");
+ }
+ if (dump_flags & ZEND_DUMP_RC_INFERENCE) {
+ if (info & MAY_BE_RC1) {
+ if (first) first = 0; else fprintf(stderr, ", ");
+ fprintf(stderr, "rc1");
+ }
+ if (info & MAY_BE_RCN) {
+ if (first) first = 0; else fprintf(stderr, ", ");
+ fprintf(stderr, "rcn");
+ }
+ }
+ if (info & MAY_BE_CLASS) {
+ if (first) first = 0; else fprintf(stderr, ", ");
+ fprintf(stderr, "class");
+ if (ce) {
+ if (is_instanceof) {
+ fprintf(stderr, " (instanceof %s)", ce->name->val);
+ } else {
+ fprintf(stderr, " (%s)", ce->name->val);
+ }
+ }
+ } else if ((info & MAY_BE_ANY) == MAY_BE_ANY) {
+ if (first) first = 0; else fprintf(stderr, ", ");
+ fprintf(stderr, "any");
+ } else {
+ if (info & MAY_BE_NULL) {
+ if (first) first = 0; else fprintf(stderr, ", ");
+ fprintf(stderr, "null");
+ }
+ if ((info & MAY_BE_FALSE) && (info & MAY_BE_TRUE)) {
+ if (first) first = 0; else fprintf(stderr, ", ");
+ fprintf(stderr, "bool");
+ } else if (info & MAY_BE_FALSE) {
+ if (first) first = 0; else fprintf(stderr, ", ");
+ fprintf(stderr, "false");
+ } else if (info & MAY_BE_TRUE) {
+ if (first) first = 0; else fprintf(stderr, ", ");
+ fprintf(stderr, "true");
+ }
+ if (info & MAY_BE_LONG) {
+ if (first) first = 0; else fprintf(stderr, ", ");
+ fprintf(stderr, "long");
+ }
+ if (info & MAY_BE_DOUBLE) {
+ if (first) first = 0; else fprintf(stderr, ", ");
+ fprintf(stderr, "double");
+ }
+ if (info & MAY_BE_STRING) {
+ if (first) first = 0; else fprintf(stderr, ", ");
+ fprintf(stderr, "string");
+ }
+ if (info & MAY_BE_ARRAY) {
+ if (first) first = 0; else fprintf(stderr, ", ");
+ fprintf(stderr, "array");
+ if ((info & MAY_BE_ARRAY_KEY_ANY) != 0 &&
+ (info & MAY_BE_ARRAY_KEY_ANY) != MAY_BE_ARRAY_KEY_ANY) {
+ int afirst = 1;
+ fprintf(stderr, " [");
+ if (info & MAY_BE_ARRAY_KEY_LONG) {
+ if (afirst) afirst = 0; else fprintf(stderr, ", ");
+ fprintf(stderr, "long");
+ }
+ if (info & MAY_BE_ARRAY_KEY_STRING) {
+ if (afirst) afirst = 0; else fprintf(stderr, ", ");
+ fprintf(stderr, "string");
+ }
+ fprintf(stderr, "]");
+ }
+ if (info & (MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF)) {
+ int afirst = 1;
+ fprintf(stderr, " of [");
+ if ((info & MAY_BE_ARRAY_OF_ANY) == MAY_BE_ARRAY_OF_ANY) {
+ if (afirst) afirst = 0; else fprintf(stderr, ", ");
+ fprintf(stderr, "any");
+ } else {
+ if (info & MAY_BE_ARRAY_OF_NULL) {
+ if (afirst) afirst = 0; else fprintf(stderr, ", ");
+ fprintf(stderr, "null");
+ }
+ if (info & MAY_BE_ARRAY_OF_FALSE) {
+ if (afirst) afirst = 0; else fprintf(stderr, ", ");
+ fprintf(stderr, "false");
+ }
+ if (info & MAY_BE_ARRAY_OF_TRUE) {
+ if (afirst) afirst = 0; else fprintf(stderr, ", ");
+ fprintf(stderr, "true");
+ }
+ if (info & MAY_BE_ARRAY_OF_LONG) {
+ if (afirst) afirst = 0; else fprintf(stderr, ", ");
+ fprintf(stderr, "long");
+ }
+ if (info & MAY_BE_ARRAY_OF_DOUBLE) {
+ if (afirst) afirst = 0; else fprintf(stderr, ", ");
+ fprintf(stderr, "double");
+ }
+ if (info & MAY_BE_ARRAY_OF_STRING) {
+ if (afirst) afirst = 0; else fprintf(stderr, ", ");
+ fprintf(stderr, "string");
+ }
+ if (info & MAY_BE_ARRAY_OF_ARRAY) {
+ if (afirst) afirst = 0; else fprintf(stderr, ", ");
+ fprintf(stderr, "array");
+ }
+ if (info & MAY_BE_ARRAY_OF_OBJECT) {
+ if (afirst) afirst = 0; else fprintf(stderr, ", ");
+ fprintf(stderr, "object");
+ }
+ if (info & MAY_BE_ARRAY_OF_RESOURCE) {
+ if (afirst) afirst = 0; else fprintf(stderr, ", ");
+ fprintf(stderr, "resource");
+ }
+ }
+ if (info & MAY_BE_ARRAY_OF_REF) {
+ if (afirst) afirst = 0; else fprintf(stderr, ", ");
+ fprintf(stderr, "ref");
+ }
+ fprintf(stderr, "]");
+ }
+ }
+ if (info & MAY_BE_OBJECT) {
+ if (first) first = 0; else fprintf(stderr, ", ");
+ fprintf(stderr, "object");
+ if (ce) {
+ if (is_instanceof) {
+ fprintf(stderr, " (instanceof %s)", ce->name->val);
+ } else {
+ fprintf(stderr, " (%s)", ce->name->val);
+ }
+ }
+ }
+ if (info & MAY_BE_RESOURCE) {
+ if (first) first = 0; else fprintf(stderr, ", ");
+ fprintf(stderr, "resource");
+ }
+ }
+ if (info & MAY_BE_ERROR) {
+ if (first) first = 0; else fprintf(stderr, ", ");
+ fprintf(stderr, "error");
+ }
+//TODO: this is useful only for JIT???
+ if (info & MAY_BE_IN_REG) {
+ if (first) first = 0; else fprintf(stderr, ", ");
+ fprintf(stderr, "reg");
+ }
+ fprintf(stderr, "]");
+}
+
+static void zend_dump_ssa_var_info(const zend_ssa *ssa, int ssa_var_num, uint32_t dump_flags)
+{
+ zend_dump_type_info(
+ ssa->var_info[ssa_var_num].type,
+ ssa->var_info[ssa_var_num].ce,
+ ssa->var_info[ssa_var_num].ce ?
+ ssa->var_info[ssa_var_num].is_instanceof : 0,
+ dump_flags);
+}
+
+static void zend_dump_ssa_var(const zend_op_array *op_array, const zend_ssa *ssa, int ssa_var_num, zend_uchar var_type, int var_num, uint32_t dump_flags)
+{
+ if (ssa_var_num >= 0) {
+ fprintf(stderr, "#%d.", ssa_var_num);
+ } else {
+ fprintf(stderr, "#?.");
+ }
+ zend_dump_var(op_array, (var_num < op_array->last_var ? IS_CV : var_type), var_num);
+
+ if (ssa_var_num >= 0 && ssa->vars) {
+ if (ssa_var_num >= 0 && ssa->vars[ssa_var_num].no_val) {
+ fprintf(stderr, " NOVAL");
+ }
+ if (ssa->var_info) {
+ zend_dump_ssa_var_info(ssa, ssa_var_num, dump_flags);
+ if (ssa->var_info[ssa_var_num].has_range) {
+ zend_dump_range(&ssa->var_info[ssa_var_num].range);
+ }
+ }
+ }
+}
+
+static void zend_dump_type_constraint(const zend_op_array *op_array, const zend_ssa *ssa, const zend_ssa_type_constraint *constraint, uint32_t dump_flags)
+{
+ fprintf(stderr, " TYPE");
+ zend_dump_type_info(constraint->type_mask, constraint->ce, 1, dump_flags);
+}
+
+static void zend_dump_range_constraint(const zend_op_array *op_array, const zend_ssa *ssa, const zend_ssa_range_constraint *r, uint32_t dump_flags)
+{
+ if (r->range.underflow && r->range.overflow) {
+ return;
+ }
+ fprintf(stderr, " RANGE");
+ if (r->negative) {
+ fprintf(stderr, "~");
+ }
+ fprintf(stderr, "[");
+ if (r->range.underflow) {
+ fprintf(stderr, "-- .. ");
+ } else {
+ if (r->min_ssa_var >= 0) {
+ zend_dump_ssa_var(op_array, ssa, r->min_ssa_var, (r->min_var < op_array->last_var ? IS_CV : 0), r->min_var, dump_flags);
+ if (r->range.min > 0) {
+ fprintf(stderr, " + " ZEND_LONG_FMT, r->range.min);
+ } else if (r->range.min < 0) {
+ fprintf(stderr, " - " ZEND_LONG_FMT, -r->range.min);
+ }
+ fprintf(stderr, " .. ");
+ } else {
+ fprintf(stderr, ZEND_LONG_FMT " .. ", r->range.min);
+ }
+ }
+ if (r->range.overflow) {
+ fprintf(stderr, "++]");
+ } else {
+ if (r->max_ssa_var >= 0) {
+ zend_dump_ssa_var(op_array, ssa, r->max_ssa_var, (r->max_var < op_array->last_var ? IS_CV : 0), r->max_var, dump_flags);
+ if (r->range.max > 0) {
+ fprintf(stderr, " + " ZEND_LONG_FMT, r->range.max);
+ } else if (r->range.max < 0) {
+ fprintf(stderr, " - " ZEND_LONG_FMT, -r->range.max);
+ }
+ fprintf(stderr, "]");
+ } else {
+ fprintf(stderr, ZEND_LONG_FMT "]", r->range.max);
+ }
+ }
+}
+
+static void zend_dump_op(const zend_op_array *op_array, const zend_basic_block *b, const zend_op *opline, uint32_t dump_flags, const void *data)
+{
+ const char *name = zend_get_opcode_name(opline->opcode);
+ uint32_t flags = zend_get_opcode_flags(opline->opcode);
+ uint32_t n = 0;
+ int len = 0;
+ const zend_ssa *ssa = NULL;
+
+ if (dump_flags & ZEND_DUMP_SSA) {
+ ssa = (const zend_ssa*)data;
+ }
+
+ if (!b) {
+ len = fprintf(stderr, "L%u:", (uint32_t)(opline - op_array->opcodes));
+ }
+ fprintf(stderr, "%*c", 8-len, ' ');
+
+ if (!ssa || !ssa->ops || ssa->ops[opline - op_array->opcodes].result_use < 0) {
+ if (opline->result_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
+ if (ssa && ssa->ops && ssa->ops[opline - op_array->opcodes].result_def >= 0) {
+ int ssa_var_num = ssa->ops[opline - op_array->opcodes].result_def;
+ zend_dump_ssa_var(op_array, ssa, ssa_var_num, opline->result_type, EX_VAR_TO_NUM(opline->result.var), dump_flags);
+ } else {
+ zend_dump_var(op_array, opline->result_type, EX_VAR_TO_NUM(opline->result.var));
+ }
+ fprintf(stderr, " = ");
+ }
+ }
+
+ if (name) {
+ fprintf(stderr, "%s", (name + 5));
+ } else {
+ fprintf(stderr, "OP_%d", (int)opline->opcode);
+ }
+
+ if (ZEND_VM_EXT_NUM == (flags & ZEND_VM_EXT_MASK)) {
+ fprintf(stderr, " %u", opline->extended_value);
+ } else if (ZEND_VM_EXT_DIM_OBJ == (flags & ZEND_VM_EXT_MASK)) {
+ if (opline->extended_value == ZEND_ASSIGN_DIM) {
+ fprintf(stderr, " (dim)");
+ } else if (opline->extended_value == ZEND_ASSIGN_OBJ) {
+ fprintf(stderr, " (obj)");
+ }
+ } else if (ZEND_VM_EXT_CLASS_FETCH == (flags & ZEND_VM_EXT_MASK)) {
+ zend_dump_class_fetch_type(opline->extended_value);
+ } else if (ZEND_VM_EXT_CONST_FETCH == (flags & ZEND_VM_EXT_MASK)) {
+ if (opline->extended_value & IS_CONSTANT_UNQUALIFIED) {
+ fprintf(stderr, " (unqualified)");
+ }
+ if (opline->extended_value & IS_CONSTANT_CLASS) {
+ fprintf(stderr, " (__class__)");
+ }
+ if (opline->extended_value & IS_CONSTANT_IN_NAMESPACE) {
+ fprintf(stderr, " (in-namespace)");
+ }
+ } else if (ZEND_VM_EXT_TYPE == (flags & ZEND_VM_EXT_MASK)) {
+ switch (opline->extended_value) {
+ case IS_NULL:
+ fprintf(stderr, " (null)");
+ break;
+ case IS_FALSE:
+ fprintf(stderr, " (false)");
+ break;
+ case IS_TRUE:
+ fprintf(stderr, " (true)");
+ break;
+ case IS_LONG:
+ fprintf(stderr, " (long)");
+ break;
+ case IS_DOUBLE:
+ fprintf(stderr, " (double)");
+ break;
+ case IS_STRING:
+ fprintf(stderr, " (string)");
+ break;
+ case IS_ARRAY:
+ fprintf(stderr, " (array)");
+ break;
+ case IS_OBJECT:
+ fprintf(stderr, " (object)");
+ break;
+ case IS_RESOURCE:
+ fprintf(stderr, " (resource)");
+ break;
+ case _IS_BOOL:
+ fprintf(stderr, " (bool)");
+ break;
+ case IS_CALLABLE:
+ fprintf(stderr, " (callable)");
+ break;
+ case IS_VOID:
+ fprintf(stderr, " (void)");
+ break;
+ default:
+ fprintf(stderr, " (\?\?\?)");
+ break;
+ }
+ } else if (ZEND_VM_EXT_EVAL == (flags & ZEND_VM_EXT_MASK)) {
+ switch (opline->extended_value) {
+ case ZEND_EVAL:
+ fprintf(stderr, " (eval)");
+ break;
+ case ZEND_INCLUDE:
+ fprintf(stderr, " (include)");
+ break;
+ case ZEND_INCLUDE_ONCE:
+ fprintf(stderr, " (include_once)");
+ break;
+ case ZEND_REQUIRE:
+ fprintf(stderr, " (require)");
+ break;
+ case ZEND_REQUIRE_ONCE:
+ fprintf(stderr, " (require_once)");
+ break;
+ default:
+ fprintf(stderr, " (\?\?\?)");
+ break;
+ }
+ } else if (ZEND_VM_EXT_SRC == (flags & ZEND_VM_EXT_MASK)) {
+ if (opline->extended_value == ZEND_RETURNS_VALUE) {
+ fprintf(stderr, " (value)");
+ } else if (opline->extended_value == ZEND_RETURNS_FUNCTION) {
+ fprintf(stderr, " (function)");
+ }
+ } else {
+ if (ZEND_VM_EXT_VAR_FETCH & flags) {
+ switch (opline->extended_value & ZEND_FETCH_TYPE_MASK) {
+ case ZEND_FETCH_GLOBAL:
+ fprintf(stderr, " (global)");
+ break;
+ case ZEND_FETCH_LOCAL:
+ fprintf(stderr, " (local)");
+ break;
+ case ZEND_FETCH_GLOBAL_LOCK:
+ fprintf(stderr, " (global+lock)");
+ break;
+ }
+ }
+ if (ZEND_VM_EXT_ISSET & flags) {
+ if (opline->extended_value & ZEND_QUICK_SET) {
+ fprintf(stderr, " (quick)");
+ }
+ if (opline->extended_value & ZEND_ISSET) {
+ fprintf(stderr, " (isset)");
+ } else if (opline->extended_value & ZEND_ISEMPTY) {
+ fprintf(stderr, " (empty)");
+ }
+ }
+ if (ZEND_VM_EXT_ARG_NUM & flags) {
+ fprintf(stderr, " %u", opline->extended_value & ZEND_FETCH_ARG_MASK);
+ }
+ if (ZEND_VM_EXT_ARRAY_INIT & flags) {
+ fprintf(stderr, " %u", opline->extended_value >> ZEND_ARRAY_SIZE_SHIFT);
+ if (!(opline->extended_value & ZEND_ARRAY_NOT_PACKED)) {
+ fprintf(stderr, " (packed)");
+ }
+ }
+ if (ZEND_VM_EXT_REF & flags) {
+ if (opline->extended_value & ZEND_ARRAY_ELEMENT_REF) {
+ fprintf(stderr, " (ref)");
+ }
+ }
+ }
+
+ if (opline->op1_type == IS_CONST) {
+ zend_dump_const(CRT_CONSTANT_EX(op_array, opline->op1, (dump_flags & ZEND_DUMP_RT_CONSTANTS)));
+ } else if (opline->op1_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
+ if (ssa && ssa->ops) {
+ int ssa_var_num = ssa->ops[opline - op_array->opcodes].op1_use;
+ if (ssa_var_num >= 0) {
+ fprintf(stderr, " ");
+ zend_dump_ssa_var(op_array, ssa, ssa_var_num, opline->op1_type, EX_VAR_TO_NUM(opline->op1.var), dump_flags);
+ } else if (ssa->ops[opline - op_array->opcodes].op1_def < 0) {
+ fprintf(stderr, " ");
+ zend_dump_var(op_array, opline->op1_type, EX_VAR_TO_NUM(opline->op1.var));
+ }
+ } else {
+ fprintf(stderr, " ");
+ zend_dump_var(op_array, opline->op1_type, EX_VAR_TO_NUM(opline->op1.var));
+ }
+ if (ssa && ssa->ops) {
+ int ssa_var_num = ssa->ops[opline - op_array->opcodes].op1_def;
+ if (ssa_var_num >= 0) {
+ fprintf(stderr, " -> ");
+ zend_dump_ssa_var(op_array, ssa, ssa_var_num, opline->op1_type, EX_VAR_TO_NUM(opline->op1.var), dump_flags);
+ }
+ }
+ } else {
+ uint32_t op1_flags = ZEND_VM_OP1_FLAGS(flags);
+ if (ZEND_VM_OP_JMP_ADDR == (op1_flags & ZEND_VM_OP_MASK)) {
+ if (b) {
+ fprintf(stderr, " BB%d", b->successors[n++]);
+ } else {
+ fprintf(stderr, " L%u", (uint32_t)(OP_JMP_ADDR(opline, opline->op1) - op_array->opcodes));
+ }
+ } else {
+ zend_dump_unused_op(opline, opline->op1, op1_flags);
+ }
+ }
+
+ if (opline->op2_type == IS_CONST) {
+ zend_dump_const(CRT_CONSTANT_EX(op_array, opline->op2, (dump_flags & ZEND_DUMP_RT_CONSTANTS)));
+ } else if (opline->op2_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
+ if (ssa && ssa->ops) {
+ int ssa_var_num = ssa->ops[opline - op_array->opcodes].op2_use;
+ if (ssa_var_num >= 0) {
+ fprintf(stderr, " ");
+ zend_dump_ssa_var(op_array, ssa, ssa_var_num, opline->op2_type, EX_VAR_TO_NUM(opline->op2.var), dump_flags);
+ } else if (ssa->ops[opline - op_array->opcodes].op2_def < 0) {
+ fprintf(stderr, " ");
+ zend_dump_var(op_array, opline->op2_type, EX_VAR_TO_NUM(opline->op2.var));
+ }
+ } else {
+ fprintf(stderr, " ");
+ zend_dump_var(op_array, opline->op2_type, EX_VAR_TO_NUM(opline->op2.var));
+ }
+ if (ssa && ssa->ops) {
+ int ssa_var_num = ssa->ops[opline - op_array->opcodes].op2_def;
+ if (ssa_var_num >= 0) {
+ fprintf(stderr, " -> ");
+ zend_dump_ssa_var(op_array, ssa, ssa_var_num, opline->op2_type, EX_VAR_TO_NUM(opline->op2.var), dump_flags);
+ }
+ }
+ } else {
+ uint32_t op2_flags = ZEND_VM_OP2_FLAGS(flags);
+ if (ZEND_VM_OP_JMP_ADDR == (op2_flags & ZEND_VM_OP_MASK)) {
+ if (b) {
+ fprintf(stderr, " BB%d", b->successors[n++]);
+ } else {
+ fprintf(stderr, " L%u", (uint32_t)(OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes));
+ }
+ } else {
+ zend_dump_unused_op(opline, opline->op2, op2_flags);
+ }
+ }
+
+ if (ZEND_VM_EXT_JMP_ADDR == (flags & ZEND_VM_EXT_MASK)) {
+ if (opline->opcode != ZEND_CATCH || !opline->result.num) {
+ if (b) {
+ fprintf(stderr, " BB%d", b->successors[n++]);
+ } else {
+ fprintf(stderr, " L%u", (uint32_t)ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value));
+ }
+ }
+ }
+ if (opline->result_type == IS_CONST) {
+ zend_dump_const(CRT_CONSTANT_EX(op_array, opline->result, (dump_flags & ZEND_DUMP_RT_CONSTANTS)));
+ } else if (ssa && ssa->ops && ssa->ops[opline - op_array->opcodes].result_use >= 0) {
+ if (opline->result_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
+ if (ssa && ssa->ops) {
+ int ssa_var_num = ssa->ops[opline - op_array->opcodes].result_use;
+ if (ssa_var_num >= 0) {
+ fprintf(stderr, " ");
+ zend_dump_ssa_var(op_array, ssa, ssa_var_num, opline->result_type, EX_VAR_TO_NUM(opline->result.var), dump_flags);
+ }
+ } else {
+ fprintf(stderr, " ");
+ zend_dump_var(op_array, opline->result_type, EX_VAR_TO_NUM(opline->result.var));
+ }
+ if (ssa && ssa->ops) {
+ int ssa_var_num = ssa->ops[opline - op_array->opcodes].result_def;
+ if (ssa_var_num >= 0) {
+ fprintf(stderr, " -> ");
+ zend_dump_ssa_var(op_array, ssa, ssa_var_num, opline->result_type, EX_VAR_TO_NUM(opline->result.var), dump_flags);
+ }
+ }
+ }
+ }
+ fprintf(stderr, "\n");
+}
+
+static void zend_dump_block_info(const zend_cfg *cfg, int n, uint32_t dump_flags)
+{
+ zend_basic_block *b = cfg->blocks + n;
+ int printed = 0;
+
+ fprintf(stderr, "BB%d:", n);
+ if (b->flags & ZEND_BB_START) {
+ fprintf(stderr, " start");
+ }
+ if (b->flags & ZEND_BB_FOLLOW) {
+ fprintf(stderr, " follow");
+ }
+ if (b->flags & ZEND_BB_TARGET) {
+ fprintf(stderr, " target");
+ }
+ if (b->flags & ZEND_BB_EXIT) {
+ fprintf(stderr, " exit");
+ }
+ if (b->flags & ZEND_BB_ENTRY) {
+ fprintf(stderr, " entry");
+ }
+ if (b->flags & ZEND_BB_TRY) {
+ fprintf(stderr, " try");
+ }
+ if (b->flags & ZEND_BB_CATCH) {
+ fprintf(stderr, " catch");
+ }
+ if (b->flags & ZEND_BB_FINALLY) {
+ fprintf(stderr, " finally");
+ }
+ if (b->flags & ZEND_BB_FINALLY_END) {
+ fprintf(stderr, " finally_end");
+ }
+ if (b->flags & ZEND_BB_GEN_VAR) {
+ fprintf(stderr, " gen_var");
+ }
+ if (b->flags & ZEND_BB_KILL_VAR) {
+ fprintf(stderr, " kill_var");
+ }
+ if (!(dump_flags & ZEND_DUMP_HIDE_UNREACHABLE) & !(b->flags & ZEND_BB_REACHABLE)) {
+ fprintf(stderr, " unreachable");
+ }
+ if (b->flags & ZEND_BB_LOOP_HEADER) {
+ fprintf(stderr, " loop_header");
+ }
+ if (b->flags & ZEND_BB_IRREDUCIBLE_LOOP) {
+ fprintf(stderr, " irreducible");
+ }
+ if (b->len != 0) {
+ fprintf(stderr, " lines=[%d-%d]", b->start, b->start + b->len - 1);
+ } else {
+ fprintf(stderr, " empty");
+ }
+ fprintf(stderr, "\n");
+
+ if (b->predecessors_count) {
+ int *p = cfg->predecessors + b->predecessor_offset;
+ int *end = p + b->predecessors_count;
+
+ fprintf(stderr, " ; from=(BB%d", *p);
+ for (p++; p < end; p++) {
+ fprintf(stderr, ", BB%d", *p);
+ }
+ fprintf(stderr, ")\n");
+ }
+
+ if (b->successors[0] != -1) {
+ fprintf(stderr, " ; to=(BB%d", b->successors[0]);
+ printed = 1;
+ if (b->successors[1] != -1) {
+ fprintf(stderr, ", BB%d", b->successors[1]);
+ }
+ }
+ if (printed) {
+ fprintf(stderr, ")\n");
+ }
+
+ if (b->idom >= 0) {
+ fprintf(stderr, " ; idom=BB%d\n", b->idom);
+ }
+ if (b->level >= 0) {
+ fprintf(stderr, " ; level=%d\n", b->level);
+ }
+ if (b->loop_header >= 0) {
+ fprintf(stderr, " ; loop_header=%d\n", b->level);
+ }
+ if (b->children >= 0) {
+ int j = b->children;
+ fprintf(stderr, " ; children=(BB%d", j);
+ j = cfg->blocks[j].next_child;
+ while (j >= 0) {
+ fprintf(stderr, ", BB%d", j);
+ j = cfg->blocks[j].next_child;
+ }
+ fprintf(stderr, ")\n");
+ }
+}
+
+static void zend_dump_block_header(const zend_cfg *cfg, const zend_op_array *op_array, const zend_ssa *ssa, int n, uint32_t dump_flags)
+{
+ zend_dump_block_info(cfg, n, dump_flags);
+ if (ssa && ssa->blocks && ssa->blocks[n].phis) {
+ zend_ssa_phi *p = ssa->blocks[n].phis;
+
+ do {
+ int j;
+
+ fprintf(stderr, " ");
+ zend_dump_ssa_var(op_array, ssa, p->ssa_var, 0, p->var, dump_flags);
+ if (p->pi < 0) {
+ fprintf(stderr, " = Phi(");
+ for (j = 0; j < cfg->blocks[n].predecessors_count; j++) {
+ if (j > 0) {
+ fprintf(stderr, ", ");
+ }
+ zend_dump_ssa_var(op_array, ssa, p->sources[j], 0, p->var, dump_flags);
+ }
+ fprintf(stderr, ")\n");
+ } else {
+ fprintf(stderr, " = Pi<BB%d>(", p->pi);
+ zend_dump_ssa_var(op_array, ssa, p->sources[0], 0, p->var, dump_flags);
+ fprintf(stderr, " &");
+ if (p->has_range_constraint) {
+ zend_dump_range_constraint(op_array, ssa, &p->constraint.range, dump_flags);
+ } else {
+ zend_dump_type_constraint(op_array, ssa, &p->constraint.type, dump_flags);
+ }
+ fprintf(stderr, ")\n");
+ }
+ p = p->next;
+ } while (p);
+ }
+}
+
+static void zend_dump_op_array_name(const zend_op_array *op_array)
+{
+ zend_func_info *func_info = NULL;
+
+ func_info = ZEND_FUNC_INFO(op_array);
+ if (op_array->function_name) {
+ if (op_array->scope && op_array->scope->name) {
+ fprintf(stderr, "%s::%s", op_array->scope->name->val, op_array->function_name->val);
+ } else {
+ fprintf(stderr, "%s", op_array->function_name->val);
+ }
+ } else {
+ fprintf(stderr, "%s", "$_main");
+ }
+ if (func_info && func_info->clone_num > 0) {
+ fprintf(stderr, "_@_clone_%d", func_info->clone_num);
+ }
+}
+
+void zend_dump_op_array(const zend_op_array *op_array, uint32_t dump_flags, const char *msg, const void *data)
+{
+ int i;
+ const zend_cfg *cfg = NULL;
+ const zend_ssa *ssa = NULL;
+ zend_func_info *func_info = NULL;
+ uint32_t func_flags = 0;
+
+ if (dump_flags & (ZEND_DUMP_CFG|ZEND_DUMP_SSA)) {
+ cfg = (const zend_cfg*)data;
+ if (!cfg->blocks) {
+ cfg = data = NULL;
+ }
+ }
+ if (dump_flags & ZEND_DUMP_SSA) {
+ ssa = (const zend_ssa*)data;
+ }
+
+ func_info = ZEND_FUNC_INFO(op_array);
+ if (func_info) {
+ func_flags = func_info->flags;
+ }
+
+ fprintf(stderr, "\n");
+ zend_dump_op_array_name(op_array);
+ fprintf(stderr, ": ; (lines=%d, args=%d",
+ op_array->last,
+ op_array->num_args);
+ if (func_info && func_info->num_args >= 0) {
+ fprintf(stderr, "/%d", func_info->num_args);
+ }
+ fprintf(stderr, ", vars=%d, tmps=%d", op_array->last_var, op_array->T);
+ if (ssa) {
+ fprintf(stderr, ", ssa_vars=%d", ssa->vars_count);
+ }
+ if (func_flags & ZEND_FUNC_INDIRECT_VAR_ACCESS) {
+ fprintf(stderr, ", dynamic");
+ }
+ if (func_flags & ZEND_FUNC_RECURSIVE) {
+ fprintf(stderr, ", recursive");
+ if (func_flags & ZEND_FUNC_RECURSIVE_DIRECTLY) {
+ fprintf(stderr, " directly");
+ }
+ if (func_flags & ZEND_FUNC_RECURSIVE_INDIRECTLY) {
+ fprintf(stderr, " indirectly");
+ }
+ }
+ if (func_flags & ZEND_FUNC_IRREDUCIBLE) {
+ fprintf(stderr, ", irreducable");
+ }
+ if (func_flags & ZEND_FUNC_NO_LOOPS) {
+ fprintf(stderr, ", no_loops");
+ }
+//TODO: this is useful only for JIT???
+#if 0
+ if (info->flags & ZEND_JIT_FUNC_NO_IN_MEM_CVS) {
+ fprintf(stderr, ", no_in_mem_cvs");
+ }
+ if (info->flags & ZEND_JIT_FUNC_NO_USED_ARGS) {
+ fprintf(stderr, ", no_used_args");
+ }
+ if (info->flags & ZEND_JIT_FUNC_NO_SYMTAB) {
+ fprintf(stderr, ", no_symtab");
+ }
+ if (info->flags & ZEND_JIT_FUNC_NO_FRAME) {
+ fprintf(stderr, ", no_frame");
+ }
+ if (info->flags & ZEND_JIT_FUNC_INLINE) {
+ fprintf(stderr, ", inline");
+ }
+#endif
+ if (func_info && func_info->return_value_used == 0) {
+ fprintf(stderr, ", no_return_value");
+ } else if (func_info && func_info->return_value_used == 1) {
+ fprintf(stderr, ", return_value");
+ }
+ fprintf(stderr, ")\n");
+ if (msg) {
+ fprintf(stderr, " ; (%s)\n", msg);
+ }
+ fprintf(stderr, " ; %s:%u-%u\n", op_array->filename->val, op_array->line_start, op_array->line_end);
+
+ if (func_info && func_info->num_args > 0) {
+ for (i = 0; i < MIN(op_array->num_args, func_info->num_args ); i++) {
+ fprintf(stderr, " ; arg %d ", i);
+ zend_dump_type_info(func_info->arg_info[i].info.type, func_info->arg_info[i].info.ce, func_info->arg_info[i].info.is_instanceof, dump_flags);
+ zend_dump_range(&func_info->arg_info[i].info.range);
+ fprintf(stderr, "\n");
+ }
+ }
+
+ if (func_info) {
+ fprintf(stderr, " ; return ");
+ zend_dump_type_info(func_info->return_info.type, func_info->return_info.ce, func_info->return_info.is_instanceof, dump_flags);
+ zend_dump_range(&func_info->return_info.range);
+ fprintf(stderr, "\n");
+ }
+
+ if (ssa && ssa->var_info) {
+ for (i = 0; i < op_array->last_var; i++) {
+ fprintf(stderr, " ; ");
+ zend_dump_ssa_var(op_array, ssa, i, IS_CV, i, dump_flags);
+ fprintf(stderr, "\n");
+ }
+ }
+
+ if (cfg) {
+ int n;
+ zend_basic_block *b;
+
+ for (n = 0; n < cfg->blocks_count; n++) {
+ b = cfg->blocks + n;
+ if (!(dump_flags & ZEND_DUMP_HIDE_UNREACHABLE) || (b->flags & ZEND_BB_REACHABLE)) {
+ const zend_op *opline;
+ const zend_op *end;
+
+ zend_dump_block_header(cfg, op_array, ssa, n, dump_flags);
+ opline = op_array->opcodes + b->start;
+ end = opline + b->len;
+ while (opline < end) {
+ zend_dump_op(op_array, b, opline, dump_flags, data);
+ opline++;
+ }
+ }
+ }
+ if (op_array->last_live_range) {
+ fprintf(stderr, "LIVE RANGES:\n");
+ for (i = 0; i < op_array->last_live_range; i++) {
+ if (cfg->split_at_live_ranges) {
+ fprintf(stderr, " %u: BB%u - BB%u ",
+ EX_VAR_TO_NUM(op_array->live_range[i].var & ~ZEND_LIVE_MASK),
+ cfg->map[op_array->live_range[i].start],
+ cfg->map[op_array->live_range[i].end]);
+ } else {
+ fprintf(stderr, " %u: L%u - L%u ",
+ EX_VAR_TO_NUM(op_array->live_range[i].var & ~ZEND_LIVE_MASK),
+ op_array->live_range[i].start,
+ op_array->live_range[i].end);
+ }
+ switch (op_array->live_range[i].var & ZEND_LIVE_MASK) {
+ case ZEND_LIVE_TMPVAR:
+ fprintf(stderr, "(tmp/var)\n");
+ break;
+ case ZEND_LIVE_LOOP:
+ fprintf(stderr, "(loop)\n");
+ break;
+ case ZEND_LIVE_SILENCE:
+ fprintf(stderr, "(silence)\n");
+ break;
+ case ZEND_LIVE_ROPE:
+ fprintf(stderr, "(rope)\n");
+ break;
+ }
+ }
+ }
+ if (op_array->last_try_catch) {
+ fprintf(stderr, "EXCEPTION TABLE:\n");
+ for (i = 0; i < op_array->last_try_catch; i++) {
+ fprintf(stderr, " BB%u",
+ cfg->map[op_array->try_catch_array[i].try_op]);
+ if (op_array->try_catch_array[i].catch_op) {
+ fprintf(stderr, ", BB%u",
+ cfg->map[op_array->try_catch_array[i].catch_op]);
+ } else {
+ fprintf(stderr, ", -");
+ }
+ if (op_array->try_catch_array[i].finally_op) {
+ fprintf(stderr, ", BB%u",
+ cfg->map[op_array->try_catch_array[i].finally_op]);
+ } else {
+ fprintf(stderr, ", -");
+ }
+ if (op_array->try_catch_array[i].finally_end) {
+ fprintf(stderr, ", BB%u\n",
+ cfg->map[op_array->try_catch_array[i].finally_end]);
+ } else {
+ fprintf(stderr, ", -\n");
+ }
+ }
+ }
+ } else {
+ const zend_op *opline = op_array->opcodes;
+ const zend_op *end = opline + op_array->last;
+
+ while (opline < end) {
+ zend_dump_op(op_array, NULL, opline, dump_flags, data);
+ opline++;
+ }
+ if (op_array->last_live_range) {
+ fprintf(stderr, "LIVE RANGES:\n");
+ for (i = 0; i < op_array->last_live_range; i++) {
+ fprintf(stderr, " %u: L%u - L%u ",
+ EX_VAR_TO_NUM(op_array->live_range[i].var & ~ZEND_LIVE_MASK),
+ op_array->live_range[i].start,
+ op_array->live_range[i].end);
+ switch (op_array->live_range[i].var & ZEND_LIVE_MASK) {
+ case ZEND_LIVE_TMPVAR:
+ fprintf(stderr, "(tmp/var)\n");
+ break;
+ case ZEND_LIVE_LOOP:
+ fprintf(stderr, "(loop)\n");
+ break;
+ case ZEND_LIVE_SILENCE:
+ fprintf(stderr, "(silence)\n");
+ break;
+ case ZEND_LIVE_ROPE:
+ fprintf(stderr, "(rope)\n");
+ break;
+ }
+ }
+ }
+ if (op_array->last_try_catch) {
+ fprintf(stderr, "EXCEPTION TABLE:\n");
+ for (i = 0; i < op_array->last_try_catch; i++) {
+ fprintf(stderr, " L%u",
+ op_array->try_catch_array[i].try_op);
+ if (op_array->try_catch_array[i].catch_op) {
+ fprintf(stderr, ", L%u",
+ op_array->try_catch_array[i].catch_op);
+ } else {
+ fprintf(stderr, ", -");
+ }
+ if (op_array->try_catch_array[i].finally_op) {
+ fprintf(stderr, ", L%u",
+ op_array->try_catch_array[i].finally_op);
+ } else {
+ fprintf(stderr, ", -");
+ }
+ if (op_array->try_catch_array[i].finally_end) {
+ fprintf(stderr, ", L%u\n",
+ op_array->try_catch_array[i].finally_end);
+ } else {
+ fprintf(stderr, ", -\n");
+ }
+ }
+ }
+ }
+}
+
+void zend_dump_dominators(const zend_op_array *op_array, const zend_cfg *cfg)
+{
+ int j;
+
+ fprintf(stderr, "\nDOMINATORS-TREE for \"");
+ zend_dump_op_array_name(op_array);
+ fprintf(stderr, "\"\n");
+ for (j = 0; j < cfg->blocks_count; j++) {
+ zend_basic_block *b = cfg->blocks + j;
+ if (b->flags & ZEND_BB_REACHABLE) {
+ zend_dump_block_info(cfg, j, 0);
+ }
+ }
+}
+
+void zend_dump_variables(const zend_op_array *op_array)
+{
+ int j;
+
+ fprintf(stderr, "\nCV Variables for \"");
+ zend_dump_op_array_name(op_array);
+ fprintf(stderr, "\"\n");
+ for (j = 0; j < op_array->last_var; j++) {
+ fprintf(stderr, " ");
+ zend_dump_var(op_array, IS_CV, j);
+ fprintf(stderr, "\n");
+ }
+}
+
+void zend_dump_ssa_variables(const zend_op_array *op_array, const zend_ssa *ssa, uint32_t dump_flags)
+{
+ int j;
+
+ if (ssa->vars) {
+ fprintf(stderr, "\nSSA Variable for \"");
+ zend_dump_op_array_name(op_array);
+ fprintf(stderr, "\"\n");
+
+ for (j = 0; j < ssa->vars_count; j++) {
+ fprintf(stderr, " ");
+ zend_dump_ssa_var(op_array, ssa, j, IS_CV, ssa->vars[j].var, dump_flags);
+ if (ssa->vars[j].scc >= 0) {
+ if (ssa->vars[j].scc_entry) {
+ fprintf(stderr, " *");
+ } else {
+ fprintf(stderr, " ");
+ }
+ fprintf(stderr, "SCC=%d", ssa->vars[j].scc);
+ }
+ fprintf(stderr, "\n");
+ }
+ }
+}
+
+static void zend_dump_var_set(const zend_op_array *op_array, const char *name, zend_bitset set)
+{
+ int first = 1;
+ uint32_t i;
+
+ fprintf(stderr, " ; %s = {", name);
+ for (i = 0; i < op_array->last_var + op_array->T; i++) {
+ if (zend_bitset_in(set, i)) {
+ if (first) {
+ first = 0;
+ } else {
+ fprintf(stderr, ", ");
+ }
+ zend_dump_var(op_array, IS_CV, i);
+ }
+ }
+ fprintf(stderr, "}\n");
+}
+
+void zend_dump_dfg(const zend_op_array *op_array, const zend_cfg *cfg, const zend_dfg *dfg)
+{
+ int j;
+ fprintf(stderr, "\nVariable Liveness for \"");
+ zend_dump_op_array_name(op_array);
+ fprintf(stderr, "\"\n");
+
+ for (j = 0; j < cfg->blocks_count; j++) {
+ fprintf(stderr, " BB%d:\n", j);
+ zend_dump_var_set(op_array, "def", DFG_BITSET(dfg->def, dfg->size, j));
+ zend_dump_var_set(op_array, "use", DFG_BITSET(dfg->use, dfg->size, j));
+ zend_dump_var_set(op_array, "in ", DFG_BITSET(dfg->in, dfg->size, j));
+ zend_dump_var_set(op_array, "out", DFG_BITSET(dfg->out, dfg->size, j));
+ }
+}
+
+void zend_dump_phi_placement(const zend_op_array *op_array, const zend_ssa *ssa)
+{
+ int j;
+ zend_ssa_block *ssa_blocks = ssa->blocks;
+ int blocks_count = ssa->cfg.blocks_count;
+
+ fprintf(stderr, "\nSSA Phi() Placement for \"");
+ zend_dump_op_array_name(op_array);
+ fprintf(stderr, "\"\n");
+ for (j = 0; j < blocks_count; j++) {
+ if (ssa_blocks && ssa_blocks[j].phis) {
+ zend_ssa_phi *p = ssa_blocks[j].phis;
+ int first = 1;
+
+ fprintf(stderr, " BB%d:\n", j);
+ if (p->pi >= 0) {
+ fprintf(stderr, " ; pi={");
+ } else {
+ fprintf(stderr, " ; phi={");
+ }
+ do {
+ if (first) {
+ first = 0;
+ } else {
+ fprintf(stderr, ", ");
+ }
+ zend_dump_var(op_array, IS_CV, p->var);
+ p = p->next;
+ } while (p);
+ fprintf(stderr, "}\n");
+ }
+ }
+}
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * indent-tabs-mode: t
+ * End:
+ */
diff --git a/ext/opcache/Optimizer/zend_dump.h b/ext/opcache/Optimizer/zend_dump.h
new file mode 100644
index 0000000000..38b955c3a1
--- /dev/null
+++ b/ext/opcache/Optimizer/zend_dump.h
@@ -0,0 +1,51 @@
+/*
+ +----------------------------------------------------------------------+
+ | Zend Engine, Bytecode Visualisation |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1998-2016 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> |
+ +----------------------------------------------------------------------+
+*/
+
+#ifndef ZEND_DUMP_H
+#define ZEND_DUMP_H
+
+#include "zend_ssa.h"
+#include "zend_dfg.h"
+
+#define ZEND_DUMP_HIDE_UNREACHABLE (1<<0)
+#define ZEND_DUMP_RC_INFERENCE (1<<1)
+#define ZEND_DUMP_CFG (1<<2)
+#define ZEND_DUMP_SSA (1<<3)
+#define ZEND_DUMP_RT_CONSTANTS ZEND_RT_CONSTANTS
+
+BEGIN_EXTERN_C()
+
+void zend_dump_op_array(const zend_op_array *op_array, uint32_t dump_flags, const char *msg, const void *data);
+void zend_dump_dominators(const zend_op_array *op_array, const zend_cfg *cfg);
+void zend_dump_dfg(const zend_op_array *op_array, const zend_cfg *cfg, const zend_dfg *dfg);
+void zend_dump_phi_placement(const zend_op_array *op_array, const zend_ssa *ssa);
+void zend_dump_variables(const zend_op_array *op_array);
+void zend_dump_ssa_variables(const zend_op_array *op_array, const zend_ssa *ssa, uint32_t dump_flags);
+void zend_dump_var(const zend_op_array *op_array, zend_uchar var_type, int var_num);
+
+END_EXTERN_C()
+
+#endif /* ZEND_DUMP_H */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * indent-tabs-mode: t
+ * End:
+ */
diff --git a/ext/opcache/Optimizer/zend_func_info.c b/ext/opcache/Optimizer/zend_func_info.c
new file mode 100644
index 0000000000..9a1a9e25d4
--- /dev/null
+++ b/ext/opcache/Optimizer/zend_func_info.c
@@ -0,0 +1,1288 @@
+/*
+ +----------------------------------------------------------------------+
+ | Zend Engine, Func Info |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1998-2016 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:$ */
+
+#include "php.h"
+#include "zend_compile.h"
+#include "zend_extensions.h"
+#include "zend_ssa.h"
+#include "zend_inference.h"
+#include "zend_call_graph.h"
+#include "zend_func_info.h"
+#include "zend_inference.h"
+
+typedef uint32_t (*info_func_t)(const zend_call_info *call_info, const zend_ssa *ssa);
+
+typedef struct _func_info_t {
+ const char *name;
+ int name_len;
+ uint32_t info;
+ info_func_t info_func;
+} func_info_t;
+
+#define F0(name, info) \
+ {name, sizeof(name)-1, (info), NULL}
+#define F1(name, info) \
+ {name, sizeof(name)-1, (FUNC_MAY_WARN | MAY_BE_RC1 | (info)), NULL}
+#define FN(name, info) \
+ {name, sizeof(name)-1, (FUNC_MAY_WARN | MAY_BE_RC1 | MAY_BE_RCN | (info)), NULL}
+#define FR(name, info) \
+ {name, sizeof(name)-1, (FUNC_MAY_WARN | MAY_BE_REF | (info)), NULL}
+#define FX(name, info) \
+ {name, sizeof(name)-1, (FUNC_MAY_WARN | MAY_BE_RC1 | MAY_BE_RCN | MAY_BE_REF | (info)), NULL}
+#define I1(name, info) \
+ {name, sizeof(name)-1, (MAY_BE_RC1 | (info)), NULL}
+#define FC(name, callback) \
+ {name, sizeof(name)-1, 0, callback}
+
+static uint32_t zend_strlen_info(const zend_call_info *call_info, const zend_ssa *ssa)
+{
+ if (call_info->caller_init_opline->extended_value == call_info->num_args &&
+ call_info->num_args == 1) {
+
+ uint32_t tmp = MAY_BE_RC1;
+ if (call_info->arg_info[0].opline) {
+ uint32_t arg_info = _ssa_op1_info(call_info->caller_op_array, ssa, call_info->arg_info[0].opline);
+
+ if (arg_info & (MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG|MAY_BE_DOUBLE|MAY_BE_STRING|MAY_BE_OBJECT)) {
+ tmp |= MAY_BE_LONG;
+ }
+ if (arg_info & (MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) {
+ /* warning, and returns NULL */
+ tmp |= FUNC_MAY_WARN | MAY_BE_NULL;
+ }
+ if ((arg_info & MAY_BE_ANY) == MAY_BE_STRING) {
+ /* TODO: strlen() may be overriden by mbstring */
+ tmp |= FUNC_MAY_INLINE;
+ }
+ } else {
+ tmp |= MAY_BE_LONG | FUNC_MAY_WARN | MAY_BE_NULL;
+ }
+ return tmp;
+ } else {
+ /* warning, and returns NULL */
+ return FUNC_MAY_WARN | MAY_BE_RC1 | MAY_BE_NULL;
+ }
+}
+
+static uint32_t zend_dechex_info(const zend_call_info *call_info, const zend_ssa *ssa)
+{
+ if (call_info->caller_init_opline->extended_value == call_info->num_args &&
+ call_info->num_args == 1) {
+ return MAY_BE_RC1 | MAY_BE_STRING;
+ } else {
+ /* warning, and returns NULL */
+ return FUNC_MAY_WARN | MAY_BE_RC1 | MAY_BE_NULL;
+ }
+}
+
+static uint32_t zend_range_info(const zend_call_info *call_info, const zend_ssa *ssa)
+{
+ if (call_info->caller_init_opline->extended_value == call_info->num_args &&
+ (call_info->num_args == 2 || call_info->num_args == 3)) {
+
+ uint32_t t1 = _ssa_op1_info(call_info->caller_op_array, ssa, call_info->arg_info[0].opline);
+ uint32_t t2 = _ssa_op1_info(call_info->caller_op_array, ssa, call_info->arg_info[1].opline);
+ uint32_t t3 = 0;
+ uint32_t tmp = MAY_BE_RC1 | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG;
+
+ if (call_info->num_args == 3) {
+ t3 = _ssa_op1_info(call_info->caller_op_array, ssa, call_info->arg_info[2].opline);
+ }
+ if ((t1 & MAY_BE_STRING) && (t2 & MAY_BE_STRING)) {
+ tmp |= MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_DOUBLE | MAY_BE_ARRAY_OF_STRING;
+ tmp |= FUNC_MAY_WARN | MAY_BE_FALSE;
+ }
+ if ((t1 & MAY_BE_DOUBLE) || (t2 & MAY_BE_DOUBLE) || (t3 & MAY_BE_DOUBLE)) {
+ tmp |= MAY_BE_ARRAY_OF_DOUBLE;
+ tmp |= FUNC_MAY_WARN | MAY_BE_FALSE;
+ }
+ if ((t1 & (MAY_BE_ANY-(MAY_BE_STRING|MAY_BE_DOUBLE))) && (t2 & (MAY_BE_ANY-(MAY_BE_STRING|MAY_BE_DOUBLE)))) {
+ if (call_info->num_args == 2 && !(t3 & MAY_BE_DOUBLE)) {
+ tmp |= MAY_BE_ARRAY_OF_LONG;
+ }
+ if (call_info->num_args == 3) {
+ tmp |= FUNC_MAY_WARN | MAY_BE_FALSE;
+ }
+ }
+ return tmp;
+ } else {
+ /* may warning, and return FALSE */
+ return FUNC_MAY_WARN | MAY_BE_RC1 | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_DOUBLE | MAY_BE_ARRAY_OF_STRING;
+ }
+}
+
+static uint32_t zend_is_type_info(const zend_call_info *call_info, const zend_ssa *ssa)
+{
+ if (call_info->caller_init_opline->extended_value == call_info->num_args &&
+ call_info->num_args == 1) {
+ return MAY_BE_RC1 | MAY_BE_FALSE | MAY_BE_TRUE | FUNC_MAY_INLINE;
+ } else {
+ return MAY_BE_RC1 | MAY_BE_FALSE | MAY_BE_TRUE | FUNC_MAY_WARN;
+ }
+}
+
+static uint32_t zend_l_ss_info(const zend_call_info *call_info, const zend_ssa *ssa)
+{
+ if (call_info->caller_init_opline->extended_value == call_info->num_args &&
+ call_info->num_args == 2) {
+
+ uint32_t arg1_info = _ssa_op1_info(call_info->caller_op_array, ssa, call_info->arg_info[0].opline);
+ uint32_t arg2_info = _ssa_op1_info(call_info->caller_op_array, ssa, call_info->arg_info[1].opline);
+ uint32_t tmp = MAY_BE_RC1;
+
+ if ((arg1_info & (MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG|MAY_BE_DOUBLE|MAY_BE_STRING|MAY_BE_OBJECT)) &&
+ (arg2_info & (MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG|MAY_BE_DOUBLE|MAY_BE_STRING|MAY_BE_OBJECT))) {
+ tmp |= MAY_BE_LONG;
+ }
+ if ((arg1_info & (MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) ||
+ (arg2_info & (MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE))) {
+ /* warning, and returns NULL */
+ tmp |= FUNC_MAY_WARN | MAY_BE_NULL;
+ }
+ return tmp;
+ } else {
+ /* warning, and returns NULL */
+ return FUNC_MAY_WARN | MAY_BE_RC1 | MAY_BE_NULL | MAY_BE_LONG;
+ }
+}
+
+static uint32_t zend_lb_ssn_info(const zend_call_info *call_info, const zend_ssa *ssa)
+{
+ if (call_info->caller_init_opline->extended_value == call_info->num_args &&
+ call_info->num_args == 3) {
+ uint32_t arg1_info = _ssa_op1_info(call_info->caller_op_array, ssa, call_info->arg_info[0].opline);
+ uint32_t arg2_info = _ssa_op1_info(call_info->caller_op_array, ssa, call_info->arg_info[1].opline);
+ uint32_t arg3_info = _ssa_op1_info(call_info->caller_op_array, ssa, call_info->arg_info[2].opline);
+ uint32_t tmp = MAY_BE_RC1;
+
+ if ((arg1_info & (MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG|MAY_BE_DOUBLE|MAY_BE_STRING|MAY_BE_OBJECT)) &&
+ (arg2_info & (MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG|MAY_BE_DOUBLE|MAY_BE_STRING|MAY_BE_OBJECT)) &&
+ (arg3_info & (MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG|MAY_BE_DOUBLE|MAY_BE_STRING|MAY_BE_OBJECT))) {
+ tmp |= MAY_BE_LONG | MAY_BE_FALSE;
+ }
+ if ((arg1_info & (MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) ||
+ (arg2_info & (MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) ||
+ (arg3_info & (MAY_BE_STRING|MAY_BE_RESOURCE|MAY_BE_ARRAY|MAY_BE_OBJECT))) {
+ /* warning, and returns NULL */
+ tmp |= FUNC_MAY_WARN | MAY_BE_NULL;
+ }
+ return tmp;
+ } else {
+ /* warning, and returns NULL */
+ return FUNC_MAY_WARN | MAY_BE_RC1 | MAY_BE_NULL | MAY_BE_LONG;
+ }
+}
+
+static uint32_t zend_b_s_info(const zend_call_info *call_info, const zend_ssa *ssa)
+{
+ if (call_info->caller_init_opline->extended_value == call_info->num_args &&
+ call_info->num_args == 1) {
+
+ uint32_t arg1_info = _ssa_op1_info(call_info->caller_op_array, ssa, call_info->arg_info[0].opline);
+ uint32_t tmp = MAY_BE_RC1;
+
+ if (arg1_info & (MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG|MAY_BE_DOUBLE|MAY_BE_STRING|MAY_BE_OBJECT)) {
+ tmp |= MAY_BE_FALSE | MAY_BE_TRUE;
+ }
+ if (arg1_info & (MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) {
+ /* warning, and returns NULL */
+ tmp |= FUNC_MAY_WARN | MAY_BE_NULL;
+ }
+ return tmp;
+ } else {
+ /* warning, and returns NULL */
+ return FUNC_MAY_WARN | MAY_BE_RC1 | MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE;
+ }
+}
+
+#define UNKNOWN_INFO (MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF)
+
+static const func_info_t func_infos[] = {
+ /* zend */
+ I1("zend_version", MAY_BE_STRING),
+ I1("gc_collect_cycles", MAY_BE_LONG),
+ I1("gc_enabled", MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("gc_enable", MAY_BE_NULL),
+ F1("gc_disable", MAY_BE_NULL),
+ F1("func_num_args", MAY_BE_LONG),
+ F1("func_get_arg", UNKNOWN_INFO),
+ F1("func_get_args", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF),
+ FC("strlen", zend_strlen_info),
+ FC("strcmp", zend_l_ss_info),
+ FC("strncmp", zend_lb_ssn_info),
+ FC("strcasecmp", zend_l_ss_info),
+ FC("strncasecmp", zend_lb_ssn_info),
+ F1("each", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_KEY_ANY),
+ F1("error_reporting", MAY_BE_NULL | MAY_BE_LONG),
+ F1("define", MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_NULL), // TODO: inline
+ FC("defined", zend_b_s_info), // TODO: inline
+ F1("get_class", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("get_called_class", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("get_parrent_class", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("is_subclass_of", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), // TODO: inline
+ F1("is_a", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), // TODO: inline
+ F1("get_class_vars", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF),
+ F1("get_object_vars", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF),
+ F1("get_class_methods", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
+ F1("method_exists", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("property_exists", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("class_exists", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("interface_exists", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("trait_exists", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ FC("function_exists", zend_b_s_info), // TODO: inline
+ F1("class_alias", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("get_included_files", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
+ F1("trigger_error", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("user_error", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("set_error_handler", MAY_BE_NULL | MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_OBJECT | MAY_BE_OBJECT),
+ I1("restore_error_handler", MAY_BE_NULL | MAY_BE_TRUE),
+ F1("get_declared_traits", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
+ F1("get_declared_classes", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
+ F1("get_declared_interfaces", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
+ F1("get_defined_functions", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ARRAY),
+ I1("get_defined_vars", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF),
+ F1("create_function", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_STRING),
+ F1("get_resource_type", MAY_BE_NULL | MAY_BE_STRING),
+ F1("get_defined_constants", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_NULL | MAY_BE_ARRAY_OF_FALSE | MAY_BE_ARRAY_OF_TRUE | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_DOUBLE | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_RESOURCE | MAY_BE_ARRAY_OF_ARRAY),
+ F1("debug_print_backtrace", MAY_BE_NULL),
+ F1("debug_backtrace", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_ARRAY),
+ F1("get_loaded_extensions", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
+ FC("extension_loaded", zend_b_s_info),
+ F1("get_extension_funcs", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
+
+ /* ext/statdard */
+ F1("constant", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_LONG | MAY_BE_DOUBLE | MAY_BE_STRING | MAY_BE_RESOURCE),
+ F1("bin2hex", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("hex2bin", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("sleep", MAY_BE_FALSE | MAY_BE_LONG),
+ F1("usleep", MAY_BE_NULL | MAY_BE_FALSE),
+#if HAVE_NANOSLEEP
+ F1("time_nanosleep", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("time_sleep_until", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+#endif
+#if HAVE_STRPTIME
+ F1("strptime", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_STRING),
+#endif
+ F1("flush", MAY_BE_NULL),
+ F1("wordwrap", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("htmlspecialchars", MAY_BE_NULL | MAY_BE_STRING),
+ F1("htmlentities", MAY_BE_NULL | MAY_BE_STRING),
+ F1("html_entity_decode", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("htmlspecialchars_decode", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("get_html_translation_table", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_STRING),
+ F1("sha1", MAY_BE_NULL | MAY_BE_STRING),
+ F1("sha1_file", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("md5", MAY_BE_NULL | MAY_BE_STRING),
+ F1("md5_file", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("crc32", MAY_BE_NULL | MAY_BE_LONG),
+ F1("iptcparse", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ARRAY),
+ F1("iptcembed", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("getimagesize", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_STRING),
+ F1("getimagesizefromstring", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_STRING),
+ F1("image_type_to_mime_type", MAY_BE_NULL | MAY_BE_STRING),
+ F1("image_type_to_extension", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("phpinfo", MAY_BE_NULL | MAY_BE_TRUE),
+ F1("phpversion", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("phpcredits", MAY_BE_NULL | MAY_BE_TRUE),
+ F1("php_sapi_name", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("php_uname", MAY_BE_NULL | MAY_BE_STRING),
+ F1("php_ini_scanned_files", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("php_ini_loaded_file", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("strnatcmp", MAY_BE_NULL | MAY_BE_LONG),
+ F1("strnatcasecmp", MAY_BE_NULL | MAY_BE_LONG),
+ F1("substr_count", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+ F1("strspn", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+ F1("strcspn", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+ F1("strtok", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("strtoupper", MAY_BE_NULL | MAY_BE_STRING),
+ F1("strtolower", MAY_BE_NULL | MAY_BE_STRING),
+ F1("strpos", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+ F1("stripos", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+ F1("strrpos", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+ F1("strripos", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+ F1("strrev", MAY_BE_NULL | MAY_BE_STRING),
+ F1("hebrev", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("hebrevc", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("nl2br", MAY_BE_NULL | MAY_BE_STRING),
+ F1("basename", MAY_BE_NULL | MAY_BE_STRING),
+ F1("dirname", MAY_BE_NULL | MAY_BE_STRING),
+ F1("pathinfo", MAY_BE_NULL | MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_STRING),
+ F1("stripslashes", MAY_BE_NULL | MAY_BE_STRING),
+ F1("stripcslashes", MAY_BE_NULL | MAY_BE_STRING),
+ F1("strstr", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("stristr", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("strrchr", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("str_shuffle", MAY_BE_NULL | MAY_BE_STRING),
+ F1("str_word_count", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
+ F1("str_split", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
+ F1("strpbrk", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("substr_compare", MAY_BE_FALSE | MAY_BE_LONG),
+#ifdef HAVE_STRCOLL
+ F1("strcoll", MAY_BE_NULL | MAY_BE_LONG),
+#endif
+#ifdef HAVE_STRFMON
+ F1("money_format", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+#endif
+ F1("substr", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("substr_replace", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_STRING),
+ F1("quotemeta", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("ucfirst", MAY_BE_NULL | MAY_BE_STRING),
+ F1("lcfirst", MAY_BE_NULL | MAY_BE_STRING),
+ F1("ucwords", MAY_BE_NULL | MAY_BE_STRING),
+ F1("strtr", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("addslashes", MAY_BE_NULL | MAY_BE_STRING),
+ F1("addcslashes", MAY_BE_NULL | MAY_BE_STRING),
+ F1("rtrim", MAY_BE_NULL | MAY_BE_STRING),
+ F1("str_replace", MAY_BE_NULL | MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_ARRAY | MAY_BE_ARRAY_OF_OBJECT),
+ F1("str_ireplace", MAY_BE_NULL | MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_ARRAY | MAY_BE_ARRAY_OF_OBJECT),
+ F1("str_repeat", MAY_BE_NULL | MAY_BE_STRING),
+ F1("count_chars", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_LONG),
+ F1("chunk_split", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("trim", MAY_BE_NULL | MAY_BE_STRING),
+ F1("ltrim", MAY_BE_NULL | MAY_BE_STRING),
+ F1("strip_tags", MAY_BE_NULL | MAY_BE_STRING),
+ F1("similar_text", MAY_BE_NULL | MAY_BE_LONG),
+ F1("explode", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
+ F1("implode", MAY_BE_NULL | MAY_BE_STRING),
+ F1("join", MAY_BE_NULL | MAY_BE_STRING),
+ F1("setlocale", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("localeconv", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_ARRAY),
+#if HAVE_NL_LANGINFO
+ F1("nl_langinfo", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+#endif
+ F1("soundex", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("levenshtein", MAY_BE_NULL | MAY_BE_LONG),
+ F1("chr", MAY_BE_NULL | MAY_BE_STRING),
+ F1("ord", MAY_BE_NULL | MAY_BE_LONG),
+ F1("parse_str", MAY_BE_NULL),
+ F1("str_getcsv", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_NULL | MAY_BE_ARRAY_OF_STRING),
+ F1("str_pad", MAY_BE_NULL | MAY_BE_STRING),
+ F1("chop", MAY_BE_NULL | MAY_BE_STRING),
+ F1("strchr", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("sprintf", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("printf", MAY_BE_FALSE | MAY_BE_LONG),
+ F1("vprintf", MAY_BE_FALSE | MAY_BE_LONG),
+ F1("vsprintf", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("fprintf", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+ F1("vfprintf", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+ F1("sscanf", MAY_BE_NULL | MAY_BE_LONG | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_ANY),
+ F1("fscanf", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_ANY),
+ F1("parse_url", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG | MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_LONG),
+ F1("urlencode", MAY_BE_NULL | MAY_BE_STRING),
+ F1("urldecode", MAY_BE_NULL | MAY_BE_STRING),
+ F1("rawurlencode", MAY_BE_NULL | MAY_BE_STRING),
+ F1("rawurldecode", MAY_BE_NULL | MAY_BE_STRING),
+ F1("http_build_query", MAY_BE_FALSE | MAY_BE_STRING),
+#if defined(HAVE_SYMLINK) || defined(PHP_WIN32)
+ F1("readlink", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("linkinfo", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+ F1("symlink", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("link", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+#endif
+ F1("unlink", MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("exec", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("system", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("escapeshellcmd", MAY_BE_NULL | MAY_BE_STRING),
+ F1("escapeshellarg", MAY_BE_NULL | MAY_BE_STRING),
+ F1("passthru", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("shell_exec", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+#ifdef PHP_CAN_SUPPORT_PROC_OPEN
+ F1("proc_open", MAY_BE_FALSE | MAY_BE_RESOURCE),
+ F1("proc_close", MAY_BE_FALSE | MAY_BE_LONG),
+ F1("proc_terminate", MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("proc_get_status", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_FALSE | MAY_BE_ARRAY_OF_TRUE | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_STRING),
+#endif
+#ifdef HAVE_NICE
+ F1("proc_nice", MAY_BE_FALSE | MAY_BE_TRUE),
+#endif
+ F1("rand", MAY_BE_NULL | MAY_BE_LONG),
+ F1("srand", MAY_BE_NULL),
+ F1("getrandmax", MAY_BE_NULL | MAY_BE_LONG),
+ F1("mt_rand", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+ F1("mt_srand", MAY_BE_NULL),
+ F1("mt_getrandmax", MAY_BE_NULL | MAY_BE_LONG),
+#if HAVE_GETSERVBYNAME
+ F1("getservbyname", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+#endif
+#if HAVE_GETSERVBYPORT
+ F1("getservbyport", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+#endif
+#if HAVE_GETPROTOBYNAME
+ F1("getprotobyname", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+#endif
+#if HAVE_GETPROTOBYNUMBER
+ F1("getprotobynumber", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+#endif
+ F1("getmyuid", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+ F1("getmygid", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+ F1("getmypid", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+ F1("getmyinode", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+ F1("getlastmod", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+ F1("base64_decode", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("base64_encode", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("password_hash", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("password_get_info", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_ARRAY),
+ F1("password_needs_rehash", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("password_verify", MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("convert_uuencode", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("convert_uudecode", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("abs", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG | MAY_BE_DOUBLE),
+ F1("ceil", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_DOUBLE),
+ F1("floor", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_DOUBLE),
+ F1("round", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_DOUBLE),
+ F1("sin", MAY_BE_NULL | MAY_BE_DOUBLE),
+ F1("cos", MAY_BE_NULL | MAY_BE_DOUBLE),
+ F1("tan", MAY_BE_NULL | MAY_BE_DOUBLE),
+ F1("asin", MAY_BE_NULL | MAY_BE_DOUBLE),
+ F1("acos", MAY_BE_NULL | MAY_BE_DOUBLE),
+ F1("atan", MAY_BE_NULL | MAY_BE_DOUBLE),
+ F1("atanh", MAY_BE_NULL | MAY_BE_DOUBLE),
+ F1("atan2", MAY_BE_NULL | MAY_BE_DOUBLE),
+ F1("sinh", MAY_BE_NULL | MAY_BE_DOUBLE),
+ F1("cosh", MAY_BE_NULL | MAY_BE_DOUBLE),
+ F1("tanh", MAY_BE_NULL | MAY_BE_DOUBLE),
+ F1("asinh", MAY_BE_NULL | MAY_BE_DOUBLE),
+ F1("acosh", MAY_BE_NULL | MAY_BE_DOUBLE),
+ F1("expm1", MAY_BE_NULL | MAY_BE_DOUBLE),
+ F1("log1p", MAY_BE_NULL | MAY_BE_DOUBLE),
+ F1("pi", MAY_BE_DOUBLE),
+ F1("is_finite", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("is_nan", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("is_infinite", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("pow", MAY_BE_NULL | MAY_BE_LONG | MAY_BE_DOUBLE),
+ F1("exp", MAY_BE_NULL | MAY_BE_DOUBLE),
+ F1("log", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_DOUBLE),
+ F1("log10", MAY_BE_NULL | MAY_BE_DOUBLE),
+ F1("sqrt", MAY_BE_NULL | MAY_BE_DOUBLE),
+ F1("hypot", MAY_BE_NULL | MAY_BE_DOUBLE),
+ F1("deg2rad", MAY_BE_NULL | MAY_BE_DOUBLE),
+ F1("rad2deg", MAY_BE_NULL | MAY_BE_DOUBLE),
+ F1("bindec", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG | MAY_BE_DOUBLE),
+ F1("hexdec", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG | MAY_BE_DOUBLE),
+ F1("octdec", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG | MAY_BE_DOUBLE),
+ F1("decbin", MAY_BE_NULL | MAY_BE_STRING),
+ F1("decoct", MAY_BE_NULL | MAY_BE_STRING),
+ FC("dechex", zend_dechex_info),
+ F1("base_convert", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("number_format", MAY_BE_NULL | MAY_BE_STRING),
+ F1("fmod", MAY_BE_NULL | MAY_BE_DOUBLE),
+#ifdef HAVE_INET_NTOP
+ F1("inet_ntop", MAY_BE_FALSE | MAY_BE_STRING),
+#endif
+#ifdef HAVE_INET_PTON
+ F1("inet_pton", MAY_BE_FALSE | MAY_BE_STRING),
+#endif
+ F1("ip2long", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+ F1("long2ip", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("getenv", MAY_BE_FALSE | MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_STRING),
+#ifdef HAVE_PUTENV
+ F1("putenv", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+#endif
+ F1("getopt", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_FALSE | MAY_BE_ARRAY_OF_TRUE | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_ARRAY),
+#ifdef HAVE_GETLOADAVG
+ F1("sys_getloadavg", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_DOUBLE),
+#endif
+#ifdef HAVE_GETTIMEOFDAY
+ F1("microtime", MAY_BE_NULL | MAY_BE_DOUBLE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_LONG | MAY_BE_STRING),
+ F1("gettimeofday", MAY_BE_NULL | MAY_BE_DOUBLE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_LONG | MAY_BE_STRING),
+#endif
+#ifdef HAVE_GETRUSAGE
+ F1("getrusage", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_LONG),
+#endif
+#ifdef HAVE_GETTIMEOFDAY
+ F1("uniqid", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+#endif
+ F1("quoted_printable_decode", MAY_BE_NULL | MAY_BE_STRING),
+ F1("quoted_printable_encode", MAY_BE_NULL | MAY_BE_STRING),
+ F1("convert_cyr_string", MAY_BE_NULL | MAY_BE_STRING),
+ F1("get_current_user", MAY_BE_NULL | MAY_BE_STRING),
+ F1("set_time_limit", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("header_register_callback", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("get_cfg_var", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_ARRAY),
+ F1("magic_quotes_runtime", MAY_BE_NULL | MAY_BE_FALSE),
+ F1("set_magic_quotes_runtime", MAY_BE_NULL | MAY_BE_FALSE),
+ F1("get_magic_quotes_gpc", MAY_BE_NULL | MAY_BE_FALSE),
+ F1("get_magic_quotes_runtime", MAY_BE_NULL | MAY_BE_FALSE),
+ F1("error_log", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("error_get_last", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_STRING),
+ F1("call_user_func", UNKNOWN_INFO),
+ F1("call_user_func_array", UNKNOWN_INFO),
+ F1("call_user_method", UNKNOWN_INFO),
+ F1("call_user_method_array", UNKNOWN_INFO),
+ F1("forward_static_call", UNKNOWN_INFO),
+ F1("forward_static_call_array", UNKNOWN_INFO),
+ F1("serialize", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ FN("unserialize", UNKNOWN_INFO),
+ F1("var_dump", MAY_BE_NULL),
+ F1("var_export", MAY_BE_NULL | MAY_BE_STRING),
+ F1("debug_zval_dump", MAY_BE_NULL),
+ F1("print_r", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("memory_get_usage", MAY_BE_FALSE | MAY_BE_LONG),
+ F1("memory_get_peak_usage", MAY_BE_FALSE | MAY_BE_LONG),
+ F1("register_shutdown_function", MAY_BE_NULL | MAY_BE_FALSE),
+ F1("register_tick_function", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("unregister_tick_function", MAY_BE_NULL),
+ F1("highlight_file", MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_STRING),
+ F1("show_source", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("highlight_string", MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_STRING),
+ F1("php_strip_whitespace", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("ini_get", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("ini_get_all", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_NULL | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_ARRAY),
+ F1("ini_set", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("ini_alter", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("ini_restore", MAY_BE_NULL),
+ F1("get_include_path", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("set_include_path", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("restore_include_path", MAY_BE_NULL),
+ F1("setcookie", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("setrawcookie", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("header", MAY_BE_NULL),
+ F1("header_remove", MAY_BE_NULL),
+ F1("headers_sent", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("headers_list", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
+ F1("http_response_code", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+ F1("connection_aborted", MAY_BE_LONG),
+ F1("connection_status", MAY_BE_LONG),
+ F1("ignore_user_abort", MAY_BE_NULL | MAY_BE_LONG),
+ F1("parse_ini_file", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_NULL | MAY_BE_ARRAY_OF_FALSE | MAY_BE_ARRAY_OF_TRUE | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_DOUBLE | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_ARRAY),
+ F1("parse_ini_string", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_NULL | MAY_BE_ARRAY_OF_FALSE | MAY_BE_ARRAY_OF_TRUE | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_DOUBLE | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_ARRAY),
+#if ZEND_DEBUG
+ F1("config_get_hash", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_ARRAY),
+#endif
+ F1("is_uploaded_file", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("move_uploaded_file", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("gethostbyaddr", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("gethostbyname", MAY_BE_NULL | MAY_BE_STRING),
+ F1("gethostbynamel", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
+#ifdef HAVE_GETHOSTNAME
+ F1("gethostname", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+#endif
+#if defined(PHP_WIN32) || (HAVE_DNS_SEARCH_FUNC && !(defined(__BEOS__) || defined(NETWARE)))
+ F1("dns_check_record", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("checkdnsrr", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+# if defined(PHP_WIN32) || HAVE_FULL_DNS_FUNCS
+ F1("dns_get_mx", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("getmxrr", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("dns_get_record", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_ARRAY),
+# endif
+#endif
+ F1("intval", MAY_BE_NULL | MAY_BE_LONG),
+ F1("floatval", MAY_BE_NULL | MAY_BE_DOUBLE),
+ F1("doubleval", MAY_BE_NULL | MAY_BE_DOUBLE),
+ F1("strval", MAY_BE_NULL | MAY_BE_STRING),
+ F1("boolval", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("gettype", MAY_BE_NULL | MAY_BE_STRING),
+ F1("settype", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ FC("is_null", zend_is_type_info),
+ F1("is_resource", MAY_BE_FALSE | MAY_BE_TRUE), // TODO: inline with support for closed resources
+ FC("is_bool", zend_is_type_info),
+ FC("is_long", zend_is_type_info),
+ FC("is_float", zend_is_type_info),
+ FC("is_int", zend_is_type_info),
+ FC("is_integer", zend_is_type_info),
+ FC("is_double", zend_is_type_info),
+ FC("is_real", zend_is_type_info),
+ F1("is_numeric", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ FC("is_string", zend_is_type_info),
+ FC("is_array", zend_is_type_info),
+ F1("is_object", MAY_BE_FALSE | MAY_BE_TRUE), // TODO: inline with support for incomplete class
+ F1("is_scalar", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("is_callable", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("pclose", MAY_BE_FALSE | MAY_BE_LONG),
+ F1("popen", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_RESOURCE),
+ F1("readfile", MAY_BE_FALSE | MAY_BE_LONG),
+ F1("rewind", MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("rmdir", MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("umask", MAY_BE_FALSE | MAY_BE_LONG),
+ F1("fclose", MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("feof", MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("fgetc", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("fgets", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("fgetss", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("fread", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("fopen", MAY_BE_FALSE | MAY_BE_RESOURCE),
+ F1("fpassthru", MAY_BE_FALSE | MAY_BE_LONG),
+ F1("ftruncate", MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("fstat", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_LONG),
+ F1("fseek", MAY_BE_FALSE | MAY_BE_LONG),
+ F1("ftell", MAY_BE_FALSE | MAY_BE_LONG),
+ F1("fflush", MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("fwrite", MAY_BE_FALSE | MAY_BE_LONG),
+ F1("fputs", MAY_BE_FALSE | MAY_BE_LONG),
+ F1("mkdir", MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("rename", MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("copy", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("tempnam", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("tmpfile", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_RESOURCE),
+ F1("file", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
+ F1("file_get_contents", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("file_put_contents", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+ F1("stream_select", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+ F1("stream_context_create", MAY_BE_FALSE | MAY_BE_RESOURCE),
+ F1("stream_context_set_params", MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("stream_context_get_params", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ANY),
+ F1("stream_context_set_option", MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("stream_context_get_options", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ANY),
+ F1("stream_context_get_default", MAY_BE_FALSE | MAY_BE_RESOURCE),
+ F1("stream_context_set_default", MAY_BE_FALSE | MAY_BE_RESOURCE),
+ F1("stream_filter_prepend", MAY_BE_FALSE | MAY_BE_RESOURCE),
+ F1("stream_filter_append", MAY_BE_FALSE | MAY_BE_RESOURCE),
+ F1("stream_filter_remove", MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("stream_socket_client", MAY_BE_FALSE | MAY_BE_RESOURCE),
+ F1("stream_socket_server", MAY_BE_FALSE | MAY_BE_RESOURCE),
+ F1("stream_socket_accept", MAY_BE_FALSE | MAY_BE_RESOURCE),
+ F1("stream_socket_get_name", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("stream_socket_recvfrom", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("stream_socket_sendto", MAY_BE_FALSE | MAY_BE_LONG),
+ F1("stream_socket_enable_crypto", MAY_BE_FALSE | MAY_BE_LONG),
+#ifdef HAVE_SHUTDOWN
+ F1("stream_socket_shutdown", MAY_BE_FALSE | MAY_BE_TRUE),
+#endif
+#if HAVE_SOCKETPAIR
+ F1("stream_socket_pair", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_RESOURCE),
+#endif
+ F1("stream_copy_to_stream", MAY_BE_FALSE | MAY_BE_LONG),
+ F1("stream_get_contents", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("stream_supports_lock", MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("fgetcsv", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_NULL | MAY_BE_ARRAY_OF_STRING),
+ F1("fputcsv", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+ F1("flock", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("get_meta_tags", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_STRING),
+ F1("stream_set_read_buffer", MAY_BE_FALSE | MAY_BE_LONG),
+ F1("stream_set_write_buffer", MAY_BE_FALSE | MAY_BE_LONG),
+ F1("set_file_buffer", MAY_BE_FALSE | MAY_BE_LONG),
+ F1("stream_set_chunk_size", MAY_BE_FALSE | MAY_BE_LONG),
+ F1("set_socket_blocking", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("stream_set_blocking", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("socket_set_blocking", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("stream_get_meta_data", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ANY),
+ F1("stream_get_line", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("stream_wrapper_register", MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("stream_register_wrapper", MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("stream_wrapper_unregister", MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("stream_wrapper_restore", MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("stream_get_wrappers", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
+ F1("stream_get_transports", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
+ F1("stream_resolve_include_path", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("stream_is_local", MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("get_headers", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_ARRAY),
+#if HAVE_SYS_TIME_H || defined(PHP_WIN32)
+ F1("stream_set_timeout", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("socket_set_timeout", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+#endif
+ F1("socket_get_status", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ANY),
+#if (!defined(__BEOS__) && !defined(NETWARE) && HAVE_REALPATH) || defined(ZTS)
+ F1("realpath", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+#endif
+#ifdef HAVE_FNMATCH
+ F1("fnmatch", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+#endif
+ F1("fsockopen", MAY_BE_FALSE | MAY_BE_RESOURCE),
+ F1("pfsockopen", MAY_BE_FALSE | MAY_BE_RESOURCE),
+ F1("pack", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("unpack", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY),
+ F1("get_browser", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_OBJECT | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ANY),
+ F1("crypt", MAY_BE_NULL | MAY_BE_STRING),
+ F1("opendir", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_RESOURCE),
+ F1("closedir", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("chdir", MAY_BE_FALSE | MAY_BE_TRUE),
+#if defined(HAVE_CHROOT) && !defined(ZTS) && ENABLE_CHROOT_FUNC
+ F1("chroot", MAY_BE_FALSE | MAY_BE_TRUE),
+#endif
+ F1("getcwd", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("rewinddir", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("readdir", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("dir", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_OBJECT),
+ F1("scandir", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
+#ifdef HAVE_GLOB
+ F1("glob", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
+#endif
+ F1("fileatime", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+ F1("filectime", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+ F1("filegroup", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+ F1("fileinode", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+ F1("filemtime", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+ F1("fileowner", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+ F1("fileperms", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+ F1("filesize", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+ F1("filetype", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("file_exists", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("is_writable", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("is_writeable", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("is_readable", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("is_executable", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("is_file", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("is_dir", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("is_link", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("stat", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_FALSE | MAY_BE_ARRAY_OF_TRUE | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_STRING),
+ F1("lstat", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_FALSE | MAY_BE_ARRAY_OF_TRUE | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_STRING),
+#ifndef NETWARE
+ F1("chown", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("chgrp", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+#endif
+#if HAVE_LCHOWN
+ F1("lchown", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+#endif
+#if HAVE_LCHOWN
+ F1("lchgrp", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+#endif
+ F1("chmod", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+#if HAVE_UTIME
+ F1("touch", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+#endif
+ F1("clearstatcache", MAY_BE_NULL),
+ F1("disk_total_space", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_DOUBLE),
+ F1("disk_free_space", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_DOUBLE),
+ F1("diskfreespace", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_DOUBLE),
+ F1("realpath_cache_size", MAY_BE_NULL | MAY_BE_LONG),
+ F1("realpath_cache_get", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_FALSE | MAY_BE_ARRAY_OF_TRUE | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_DOUBLE | MAY_BE_ARRAY_OF_STRING),
+ F1("mail", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("ezmlm_hash", MAY_BE_NULL | MAY_BE_LONG),
+#ifdef HAVE_SYSLOG_H
+ F1("openlog", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("syslog", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("closelog", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+#endif
+ F1("lcg_value", MAY_BE_DOUBLE),
+ F1("metaphone", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("ob_start", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("ob_flush", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("ob_clean", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("ob_end_flush", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("ob_end_clean", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("ob_get_flush", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("ob_get_clean", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("ob_get_length", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+ F1("ob_get_level", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+ F1("ob_get_status", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_ARRAY),
+ F1("ob_get_contents", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("ob_implicit_flush", MAY_BE_NULL),
+ F1("ob_list_handlers", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
+ F1("ksort", MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("krsort", MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("natsort", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("natcasesort", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("asort", MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("arsort", MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("sort", MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("rsort", MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("usort", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("uasort", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("uksort", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("shuffle", MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("array_walk", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("array_walk_recursive", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("count", MAY_BE_NULL | MAY_BE_LONG),
+ F1("end", UNKNOWN_INFO),
+ F1("prev", UNKNOWN_INFO),
+ F1("next", UNKNOWN_INFO),
+ F1("reset", UNKNOWN_INFO),
+ F1("current", UNKNOWN_INFO),
+ F1("key", MAY_BE_NULL | MAY_BE_LONG | MAY_BE_STRING),
+ F1("min", UNKNOWN_INFO),
+ F1("max", UNKNOWN_INFO),
+ F1("in_array", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("array_search", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG | MAY_BE_STRING),
+ F1("extract", MAY_BE_NULL | MAY_BE_LONG),
+ F1("compact", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+ F1("array_fill", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_ANY),
+ F1("array_fill_keys", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+ FC("range", zend_range_info),
+ F1("array_multisort", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("array_push", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+ F1("array_pop", UNKNOWN_INFO),
+ F1("array_shift", UNKNOWN_INFO),
+ F1("array_unshift", MAY_BE_NULL | MAY_BE_LONG),
+ F1("array_splice", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+ F1("array_slice", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+ F1("array_merge", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+ F1("array_merge_recursive", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+ F1("array_replace", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+ F1("array_replace_recursive", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+ F1("array_keys", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_STRING),
+ F1("array_values", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+ F1("array_count_values", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_LONG),
+ F1("array_column", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+ F1("array_reverse", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+ F1("array_reduce", UNKNOWN_INFO),
+ F1("array_pad", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+ F1("array_flip", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_STRING),
+ F1("array_change_key_case", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+ F1("array_rand", UNKNOWN_INFO),
+ F1("array_unique", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+ F1("array_intersect", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+ F1("array_intersect_key", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+ F1("array_intersect_ukey", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+ F1("array_uintersect", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+ F1("array_intersect_assoc", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+ F1("array_uintersect_assoc", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+ F1("array_intersect_uassoc", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+ F1("array_uintersect_uassoc", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+ F1("array_diff", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+ F1("array_diff_key", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+ F1("array_diff_ukey", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+ F1("array_udiff", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+ F1("array_diff_assoc", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+ F1("array_udiff_assoc", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+ F1("array_diff_uassoc", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+ F1("array_udiff_uassoc", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+ F1("array_sum", MAY_BE_NULL | MAY_BE_LONG | MAY_BE_DOUBLE),
+ F1("array_product", MAY_BE_NULL | MAY_BE_LONG | MAY_BE_DOUBLE),
+ F1("array_filter", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+ F1("array_map", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+ F1("array_chunk", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+ F1("array_combine", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+ F1("array_key_exists", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("pos", UNKNOWN_INFO),
+ F1("sizeof", MAY_BE_NULL | MAY_BE_LONG),
+ F1("key_exists", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("assert", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("assert_options", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG | MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_OBJECT | MAY_BE_OBJECT),
+ F1("version_compare", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_LONG),
+#if HAVE_FTOK
+ F1("ftok", MAY_BE_NULL | MAY_BE_LONG),
+#endif
+ F1("str_rot13", MAY_BE_NULL | MAY_BE_STRING),
+ F1("stream_get_filters", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
+ F1("stream_filter_register", MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("stream_bucket_make_writeable", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_OBJECT),
+ F1("stream_bucket_prepend", MAY_BE_FALSE | MAY_BE_OBJECT),
+ F1("stream_bucket_append", MAY_BE_FALSE | MAY_BE_OBJECT),
+ F1("stream_bucket_new", MAY_BE_FALSE | MAY_BE_OBJECT),
+ F1("output_add_rewrite_var", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("output_reset_rewrite_vars", MAY_BE_FALSE),
+ F1("sys_get_temp_dir", MAY_BE_NULL | MAY_BE_STRING),
+
+ /* ext/date */
+ F1("strtotime", MAY_BE_FALSE | MAY_BE_LONG),
+ F1("date", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("idate", MAY_BE_FALSE | MAY_BE_LONG),
+ F1("gmdate", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("mktime", MAY_BE_FALSE | MAY_BE_LONG),
+ F1("gmmktime", MAY_BE_FALSE | MAY_BE_LONG),
+ F1("checkdate", MAY_BE_FALSE | MAY_BE_TRUE),
+#ifdef HAVE_STRFTIME
+ F1("strftime", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("gmstrftime", MAY_BE_FALSE | MAY_BE_STRING),
+#endif
+ F1("time", MAY_BE_LONG),
+ F1("localtime", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_LONG),
+ F1("getdate", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_STRING),
+ F1("date_create", MAY_BE_FALSE | MAY_BE_OBJECT),
+ F1("date_create_immutable", MAY_BE_FALSE | MAY_BE_OBJECT),
+ F1("date_create_from_format", MAY_BE_FALSE | MAY_BE_OBJECT),
+ F1("date_create_immutable_from_format", MAY_BE_FALSE | MAY_BE_OBJECT),
+ F1("date_parse", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ANY),
+ F1("date_parse_from_format", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ANY),
+ F1("date_get_last_errors", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_ARRAY),
+ F1("date_format", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("date_modify", MAY_BE_FALSE | MAY_BE_OBJECT),
+ F1("date_add", MAY_BE_FALSE | MAY_BE_OBJECT),
+ F1("date_sub", MAY_BE_FALSE | MAY_BE_OBJECT),
+ F1("date_timezone_get", MAY_BE_FALSE | MAY_BE_OBJECT),
+ F1("date_timezone_set", MAY_BE_FALSE | MAY_BE_OBJECT),
+ F1("date_offset_get", MAY_BE_FALSE | MAY_BE_LONG),
+ F1("date_diff", MAY_BE_FALSE | MAY_BE_OBJECT),
+ F1("date_time_set", MAY_BE_FALSE | MAY_BE_OBJECT),
+ F1("date_date_set", MAY_BE_FALSE | MAY_BE_OBJECT),
+ F1("date_isodate_set", MAY_BE_FALSE | MAY_BE_OBJECT),
+ F1("date_timestamp_set", MAY_BE_FALSE | MAY_BE_OBJECT),
+ F1("date_timestamp_get", MAY_BE_FALSE | MAY_BE_LONG),
+ F1("timezone_open", MAY_BE_FALSE | MAY_BE_OBJECT),
+ F1("timezone_name_get", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("timezone_name_from_abbr", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("timezone_offset_get", MAY_BE_FALSE | MAY_BE_LONG),
+ F1("timezone_transitions_get", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY),
+ F1("timezone_location_get", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_DOUBLE | MAY_BE_ARRAY_OF_STRING),
+ F1("timezone_identifiers_list", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
+ F1("timezone_abbreviations_list", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ARRAY),
+ F1("timezone_version_get", MAY_BE_STRING),
+ F1("date_interval_create_from_date_string", MAY_BE_FALSE | MAY_BE_OBJECT),
+ F1("date_interval_format", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("date_default_timezone_set", MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("date_default_timezone_get", MAY_BE_STRING),
+ F1("date_sunrise", MAY_BE_FALSE | MAY_BE_LONG | MAY_BE_DOUBLE | MAY_BE_STRING),
+ F1("date_sunset", MAY_BE_FALSE | MAY_BE_LONG | MAY_BE_DOUBLE | MAY_BE_STRING),
+ F1("date_sun_info", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_FALSE | MAY_BE_ARRAY_OF_TRUE | MAY_BE_ARRAY_OF_LONG),
+
+ /* ext/preg */
+ F1("preg_match", MAY_BE_FALSE | MAY_BE_LONG),
+ F1("preg_match_all", MAY_BE_FALSE | MAY_BE_LONG),
+ F1("preg_replace", MAY_BE_FALSE | MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_STRING),
+ F1("preg_replace_callback", MAY_BE_FALSE | MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_STRING),
+ F1("preg_filter", MAY_BE_FALSE | MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_STRING),
+ F1("preg_split", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
+ F1("preg_quote", MAY_BE_NULL | MAY_BE_STRING),
+ F1("preg_grep", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
+ F1("preg_last_error", MAY_BE_NULL | MAY_BE_LONG),
+
+ /* ext/ereg */
+ F1("ereg", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+ F1("ereg_replace", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("eregi", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+ F1("eregi_replace", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("split", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
+ F1("spliti", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
+ F1("sql_regcase", MAY_BE_NULL | MAY_BE_STRING),
+
+ /* ext/mysql */
+ F1("mysql_connect", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_RESOURCE),
+ F1("mysql_pconnect", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_RESOURCE),
+ F1("mysql_close", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("mysql_select_db", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("mysql_create_db", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("mysql_drop_db", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("mysql_query", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_RESOURCE),
+ F1("mysql_unbuffered_query", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_RESOURCE),
+ F1("mysql_db_query", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_RESOURCE),
+ F1("mysql_list_dbs", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_RESOURCE),
+ F1("mysql_list_tables", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_RESOURCE),
+ F1("mysql_list_fields", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_RESOURCE),
+ F1("mysql_list_processes", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_RESOURCE),
+ F1("mysql_error", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("mysql_errno", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+ F1("mysql_affected_rows", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+ F1("mysql_insert_id", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+ F1("mysql_result", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("mysql_num_rows", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+ F1("mysql_num_fields", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+ F1("mysql_fetch_row", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_ANY),
+ F1("mysql_fetch_array", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY),
+ F1("mysql_fetch_assoc", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ANY),
+ F1("mysql_fetch_object", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_OBJECT),
+ F1("mysql_data_seek", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("mysql_fetch_lengths", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_LONG),
+ F1("mysql_fetch_field", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_OBJECT),
+ F1("mysql_field_seek", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("mysql_free_result", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("mysql_field_name", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("mysql_field_table", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("mysql_field_len", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+ F1("mysql_field_type", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("mysql_field_flags", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("mysql_escape_string", MAY_BE_NULL | MAY_BE_STRING),
+ F1("mysql_real_escape_string", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("mysql_stat", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("mysql_thread_id", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+ F1("mysql_client_encoding", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("mysql_ping", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("mysql_get_client_info", MAY_BE_NULL | MAY_BE_STRING),
+ F1("mysql_get_host_info", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("mysql_get_proto_info", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+ F1("mysql_get_server_info", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("mysql_info", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("mysql_set_charset", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("mysql", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_RESOURCE),
+ F1("mysql_fieldname", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("mysql_fieldtable", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("mysql_fieldlen", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+ F1("mysql_fieldtype", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("mysql_fieldflags", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("mysql_selectdb", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("mysql_createdb", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("mysql_dropdb", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("mysql_freeresult", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("mysql_numfields", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+ F1("mysql_numrows", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+ F1("mysql_listdbs", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_RESOURCE),
+ F1("mysql_listtables", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_RESOURCE),
+ F1("mysql_listfields", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_RESOURCE),
+ F1("mysql_db_name", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("mysql_dbname", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("mysql_tablename", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("mysql_table_name", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+
+ /* ext/curl */
+ F1("curl_init", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_RESOURCE),
+ F1("curl_copy_handle", MAY_BE_NULL | MAY_BE_RESOURCE),
+ F1("curl_version", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_ARRAY),
+ F1("curl_setopt", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("curl_setopt_array", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("curl_exec", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("curl_getinfo", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING | MAY_BE_LONG | MAY_BE_DOUBLE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY),
+ F1("curl_error", MAY_BE_NULL | MAY_BE_STRING),
+ F1("curl_errno", MAY_BE_NULL | MAY_BE_LONG),
+ F1("curl_close", MAY_BE_NULL),
+ F1("curl_strerror", MAY_BE_NULL | MAY_BE_STRING),
+ F1("curl_multi_strerror", MAY_BE_NULL | MAY_BE_STRING),
+ F1("curl_reset", MAY_BE_NULL),
+ F1("curl_escape", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("curl_unescape", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("curl_pause", MAY_BE_NULL | MAY_BE_LONG),
+ F1("curl_multi_init", MAY_BE_NULL | MAY_BE_RESOURCE),
+ F1("curl_multi_add_handle", MAY_BE_NULL | MAY_BE_LONG),
+ F1("curl_multi_remove_handle", MAY_BE_NULL | MAY_BE_LONG),
+ F1("curl_multi_select", MAY_BE_NULL | MAY_BE_LONG),
+ F1("curl_multi_exec", MAY_BE_NULL | MAY_BE_LONG),
+ F1("curl_multi_getcontent", MAY_BE_NULL | MAY_BE_STRING),
+ F1("curl_multi_info_read", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_RESOURCE),
+ F1("curl_multi_close", MAY_BE_NULL),
+ F1("curl_multi_setopt", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("curl_share_init", MAY_BE_NULL | MAY_BE_RESOURCE),
+ F1("curl_share_close", MAY_BE_NULL),
+ F1("curl_share_setopt", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("curl_file_create", MAY_BE_OBJECT),
+
+ /* ext/mbstring */
+ F1("mb_convert_case", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("mb_strtoupper", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("mb_strtolower", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("mb_language", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("mb_internal_encoding", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("mb_http_input", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("mb_http_output", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("mb_detect_order", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
+ F1("mb_substitute_character", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG | MAY_BE_STRING),
+ F1("mb_parse_str", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("mb_output_handler", MAY_BE_NULL | MAY_BE_STRING),
+ F1("mb_preferred_mime_name", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("mb_strlen", MAY_BE_FALSE | MAY_BE_LONG),
+ F1("mb_strpos", MAY_BE_FALSE | MAY_BE_LONG),
+ F1("mb_strrpos", MAY_BE_FALSE | MAY_BE_LONG),
+ F1("mb_stripos", MAY_BE_FALSE | MAY_BE_LONG),
+ F1("mb_strripos", MAY_BE_FALSE | MAY_BE_LONG),
+ F1("mb_strstr", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("mb_strrchr", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("mb_stristr", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("mb_strrichr", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("mb_substr_count", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+ F1("mb_substr", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("mb_strcut", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("mb_strwidth", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+ F1("mb_strimwidth", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("mb_convert_encoding", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("mb_detect_encoding", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("mb_list_encodings", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
+ F1("mb_encoding_aliases", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
+ F1("mb_convert_kana", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("mb_encode_mimeheader", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("mb_decode_mimeheader", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("mb_convert_variables", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("mb_encode_numericentity", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("mb_decode_numericentity", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("mb_send_mail", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("mb_get_info", MAY_BE_FALSE | MAY_BE_LONG | MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_ARRAY),
+ F1("mb_check_encoding", MAY_BE_FALSE | MAY_BE_TRUE),
+
+ F1("mb_regex_encoding", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("mb_regex_set_options", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("mb_ereg", MAY_BE_FALSE | MAY_BE_LONG),
+ F1("mb_eregi", MAY_BE_FALSE | MAY_BE_LONG),
+ F1("mb_ereg_replace", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("mb_eregi_replace", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("mb_ereg_replace_callback", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("mb_split", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
+ F1("mb_ereg_match", MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("mb_ereg_search", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("mb_ereg_search_pos", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_LONG),
+ F1("mb_ereg_search_regs", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_FALSE | MAY_BE_ARRAY_OF_TRUE | MAY_BE_ARRAY_OF_STRING),
+ F1("mb_ereg_search_init", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("mb_ereg_search_getregs", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_FALSE | MAY_BE_ARRAY_OF_TRUE | MAY_BE_ARRAY_OF_STRING),
+ F1("mb_ereg_search_getpos", MAY_BE_LONG),
+ F1("mb_ereg_search_setpos", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+
+ F1("mbregex_encoding", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("mbereg", MAY_BE_FALSE | MAY_BE_LONG),
+ F1("mberegi", MAY_BE_FALSE | MAY_BE_LONG),
+ F1("mbereg_replace", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("mberegi_replace", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("mbsplit", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
+ F1("mbereg_match", MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("mbereg_search", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("mbereg_search_pos", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_LONG),
+ F1("mbereg_search_regs", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_FALSE | MAY_BE_ARRAY_OF_TRUE | MAY_BE_ARRAY_OF_STRING),
+ F1("mbereg_search_init", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("mbereg_search_getregs", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_FALSE | MAY_BE_ARRAY_OF_TRUE | MAY_BE_ARRAY_OF_STRING),
+ F1("mbereg_search_getpos", MAY_BE_LONG),
+ F1("mbereg_search_setpos", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+
+ /* ext/iconv */
+ F1("iconv", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("iconv_get_encoding", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_STRING),
+ F1("iconv_set_encoding", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("iconv_strlen", MAY_BE_FALSE | MAY_BE_LONG),
+ F1("iconv_substr", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("iconv_strpos", MAY_BE_FALSE | MAY_BE_LONG),
+ F1("iconv_strrpos", MAY_BE_FALSE | MAY_BE_LONG),
+ F1("iconv_mime_encode", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("iconv_mime_decode", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("iconv_mime_decode_headers", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_ARRAY),
+
+ /* ext/json */
+ F1("json_encode", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("json_decode", MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY),
+ F1("json_last_error", MAY_BE_NULL | MAY_BE_LONG),
+ F1("json_last_error_msg", MAY_BE_NULL | MAY_BE_STRING),
+
+ /* ext/xml */
+ F1("xml_parser_create", MAY_BE_FALSE | MAY_BE_RESOURCE),
+ F1("xml_parser_create_ns", MAY_BE_FALSE | MAY_BE_RESOURCE),
+ F1("xml_set_object", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("xml_set_element_handler", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("xml_set_character_data_handler", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("xml_set_processing_instruction_handler",MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("xml_set_default_handler", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("xml_set_unparsed_entity_decl_handler", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("xml_set_notation_decl_handler", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("xml_set_external_entity_ref_handler", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("xml_set_start_namespace_decl_handler", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("xml_set_end_namespace_decl_handler", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("xml_parse", MAY_BE_NULL | MAY_BE_LONG),
+ F1("xml_parse_into_struct", MAY_BE_NULL | MAY_BE_LONG),
+ F1("xml_get_error_code", MAY_BE_NULL | MAY_BE_LONG),
+ F1("xml_error_string", MAY_BE_NULL | MAY_BE_STRING),
+ F1("xml_get_current_line_number", MAY_BE_NULL | MAY_BE_LONG),
+ F1("xml_get_current_column_number", MAY_BE_NULL | MAY_BE_LONG),
+ F1("xml_get_current_byte_index", MAY_BE_NULL | MAY_BE_LONG),
+ F1("xml_parser_free", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("xml_parser_set_option", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("xml_parser_get_option", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG | MAY_BE_STRING),
+ F1("utf8_encode", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("utf8_decode", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+
+ /* ext/zlib */
+ F1("readgzfile", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+ F1("gzrewind", MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("gzclose", MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("gzeof", MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("gzgetc", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("gzgets", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("gzgetss", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("gzread", MAY_BE_FALSE | MAY_BE_STRING),
+ F1("gzopen", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_RESOURCE),
+ F1("gzpassthru", MAY_BE_FALSE | MAY_BE_LONG),
+ F1("gzseek", MAY_BE_FALSE | MAY_BE_LONG),
+ F1("gztell", MAY_BE_FALSE | MAY_BE_LONG),
+ F1("gzwrite", MAY_BE_FALSE | MAY_BE_LONG),
+ F1("gzputs", MAY_BE_FALSE | MAY_BE_LONG),
+ F1("gzfile", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
+ F1("gzcompress", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("gzuncompress", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("gzdeflate", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("gzinflate", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("gzencode", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("gzdecode", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("zlib_encode", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("zlib_decode", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("zlib_get_coding_type", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("ob_gzhandler", MAY_BE_FALSE | MAY_BE_STRING),
+
+ /* ext/hash */
+ F1("hash", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("hash_file", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("hash_hmac", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("hash_hmac_file", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("hash_init", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_RESOURCE),
+ F1("hash_update", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("hash_update_stream", MAY_BE_NULL | MAY_BE_LONG),
+ F1("hash_update_file", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE),
+ F1("hash_final", MAY_BE_NULL | MAY_BE_STRING),
+ F1("hash_copy", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_RESOURCE),
+ F1("hash_algos", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
+ F1("hash_pbkdf2", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("mhash_keygen_s2k", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("mhash_get_block_size", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG),
+ F1("mhash_get_hash_name", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+ F1("mhash_count", MAY_BE_NULL | MAY_BE_LONG),
+ F1("mhash", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
+};
+
+static HashTable func_info;
+int zend_func_info_rid = -1;
+
+uint32_t zend_get_func_info(const zend_call_info *call_info, const zend_ssa *ssa)
+{
+ uint32_t ret = 0;
+
+ if (call_info->callee_func->type == ZEND_INTERNAL_FUNCTION) {
+ func_info_t *info;
+
+ if ((info = zend_hash_find_ptr(&func_info, Z_STR_P(CRT_CONSTANT_EX(call_info->caller_op_array, call_info->caller_init_opline->op2, ssa->rt_constants)))) != NULL) {
+ if (info->info_func) {
+ ret = info->info_func(call_info, ssa);
+ } else {
+ ret = info->info;
+ }
+#if 0
+ } else {
+ fprintf(stderr, "Unknown internal function '%s'\n", func->common.function_name);
+#endif
+ }
+ } else {
+ // FIXME: the order of functions matters!!!
+ zend_func_info *info = ZEND_FUNC_INFO((zend_op_array*)call_info->callee_func);
+ if (info) {
+ ret = info->return_info.type;
+ }
+ }
+ if (!ret) {
+ ret = MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF;
+ if (call_info->callee_func->type == ZEND_INTERNAL_FUNCTION) {
+ ret |= FUNC_MAY_WARN;
+ }
+ if (call_info->callee_func->common.fn_flags & ZEND_ACC_GENERATOR) {
+ ret = MAY_BE_RC1 | MAY_BE_RCN | MAY_BE_OBJECT;
+ } else if (call_info->callee_func->common.fn_flags & ZEND_ACC_RETURN_REFERENCE) {
+ ret |= MAY_BE_REF;
+ } else {
+ ret |= MAY_BE_RC1 | MAY_BE_RCN;
+ }
+ }
+ return ret;
+}
+
+int zend_func_info_startup(void)
+{
+ zend_extension dummy;
+ int i;
+
+ if (zend_func_info_rid == -1) {
+ zend_func_info_rid = zend_get_resource_handle(&dummy);
+ if (zend_func_info_rid < 0) {
+ return FAILURE;
+ }
+
+ zend_hash_init(&func_info, sizeof(func_infos)/sizeof(func_info_t), NULL, NULL, 1);
+ for (i = 0; i < sizeof(func_infos)/sizeof(func_info_t); i++) {
+ if (zend_hash_str_add_ptr(&func_info, func_infos[i].name, func_infos[i].name_len, (void**)&func_infos[i]) == NULL) {
+ fprintf(stderr, "ERROR: Duplicate function info for \"%s\"\n", func_infos[i].name);
+ }
+ }
+ }
+
+ return SUCCESS;
+}
+
+int zend_func_info_shutdown(void)
+{
+ if (zend_func_info_rid != -1) {
+ zend_hash_destroy(&func_info);
+ zend_func_info_rid = -1;
+ }
+ return SUCCESS;
+}
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * indent-tabs-mode: t
+ * End:
+ */
diff --git a/ext/opcache/Optimizer/zend_func_info.h b/ext/opcache/Optimizer/zend_func_info.h
new file mode 100644
index 0000000000..8fcf00d26a
--- /dev/null
+++ b/ext/opcache/Optimizer/zend_func_info.h
@@ -0,0 +1,70 @@
+/*
+ +----------------------------------------------------------------------+
+ | Zend Engine, Func Info |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1998-2016 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> |
+ +----------------------------------------------------------------------+
+*/
+
+#ifndef ZEND_FUNC_INFO_H
+#define ZEND_FUNC_INFO_H
+
+#include "zend_ssa.h"
+
+/* func flags */
+#define ZEND_FUNC_INDIRECT_VAR_ACCESS (1<<0)
+#define ZEND_FUNC_HAS_CALLS (1<<1)
+#define ZEND_FUNC_VARARG (1<<2)
+#define ZEND_FUNC_NO_LOOPS (1<<3)
+#define ZEND_FUNC_IRREDUCIBLE (1<<4)
+#define ZEND_FUNC_RECURSIVE (1<<7)
+#define ZEND_FUNC_RECURSIVE_DIRECTLY (1<<8)
+#define ZEND_FUNC_RECURSIVE_INDIRECTLY (1<<9)
+
+/* The following flags are valid only for return values of internal functions
+ * returned by zend_get_func_info()
+ */
+#define FUNC_MAY_WARN (1<<30)
+#define FUNC_MAY_INLINE (1<<31)
+
+typedef struct _zend_func_info zend_func_info;
+typedef struct _zend_call_info zend_call_info;
+
+#define ZEND_FUNC_INFO(op_array) \
+ ((zend_func_info*)((op_array)->reserved[zend_func_info_rid]))
+
+#define ZEND_SET_FUNC_INFO(op_array, info) do { \
+ zend_func_info** pinfo = (zend_func_info**)&(op_array)->reserved[zend_func_info_rid]; \
+ *pinfo = info; \
+ } while (0)
+
+BEGIN_EXTERN_C()
+
+extern int zend_func_info_rid;
+
+uint32_t zend_get_func_info(const zend_call_info *call_info, const zend_ssa *ssa);
+
+int zend_func_info_startup(void);
+int zend_func_info_shutdown(void);
+
+END_EXTERN_C()
+
+#endif /* ZEND_FUNC_INFO_H */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * indent-tabs-mode: t
+ * End:
+ */
diff --git a/ext/opcache/Optimizer/zend_inference.c b/ext/opcache/Optimizer/zend_inference.c
new file mode 100644
index 0000000000..ee14e49b5c
--- /dev/null
+++ b/ext/opcache/Optimizer/zend_inference.c
@@ -0,0 +1,4085 @@
+/*
+ +----------------------------------------------------------------------+
+ | Zend Engine, e-SSA based Type & Range Inference |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1998-2016 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> |
+ +----------------------------------------------------------------------+
+*/
+
+#include "php.h"
+#include "zend_compile.h"
+#include "zend_generators.h"
+#include "zend_inference.h"
+#include "zend_func_info.h"
+#include "zend_call_graph.h"
+#include "zend_worklist.h"
+
+//#define LOG_SSA_RANGE
+//#define LOG_NEG_RANGE
+#define SYM_RANGE
+#define NEG_RANGE
+#define RANGE_WARMAP_PASSES 16
+
+#define CHECK_SCC_VAR(var2) \
+ do { \
+ if (!ssa->vars[var2].no_val) { \
+ if (dfs[var2] < 0) { \
+ zend_ssa_check_scc_var(op_array, ssa, var2, index, dfs, root, stack); \
+ } \
+ if (ssa->vars[var2].scc < 0 && dfs[root[var]] >= dfs[root[var2]]) { \
+ root[var] = root[var2]; \
+ } \
+ } \
+ } while (0)
+
+#define CHECK_SCC_ENTRY(var2) \
+ do { \
+ if (ssa->vars[var2].scc != ssa->vars[var].scc) { \
+ ssa->vars[var2].scc_entry = 1; \
+ } \
+ } while (0)
+
+#define ADD_SCC_VAR(_var) \
+ do { \
+ if (ssa->vars[_var].scc == scc) { \
+ zend_bitset_incl(worklist, _var); \
+ } \
+ } while (0)
+
+#define ADD_SCC_VAR_1(_var) \
+ do { \
+ if (ssa->vars[_var].scc == scc && \
+ !zend_bitset_in(visited, _var)) { \
+ zend_bitset_incl(worklist, _var); \
+ } \
+ } while (0)
+
+#define FOR_EACH_DEFINED_VAR(line, MACRO) \
+ do { \
+ if (ssa->ops[line].op1_def >= 0) { \
+ MACRO(ssa->ops[line].op1_def); \
+ } \
+ if (ssa->ops[line].op2_def >= 0) { \
+ MACRO(ssa->ops[line].op2_def); \
+ } \
+ if (ssa->ops[line].result_def >= 0) { \
+ MACRO(ssa->ops[line].result_def); \
+ } \
+ if (op_array->opcodes[line].opcode == ZEND_OP_DATA) { \
+ if (ssa->ops[line-1].op1_def >= 0) { \
+ MACRO(ssa->ops[line-1].op1_def); \
+ } \
+ if (ssa->ops[line-1].op2_def >= 0) { \
+ MACRO(ssa->ops[line-1].op2_def); \
+ } \
+ if (ssa->ops[line-1].result_def >= 0) { \
+ MACRO(ssa->ops[line-1].result_def); \
+ } \
+ } else if (line+1 < op_array->last && \
+ op_array->opcodes[line+1].opcode == ZEND_OP_DATA) { \
+ if (ssa->ops[line+1].op1_def >= 0) { \
+ MACRO(ssa->ops[line+1].op1_def); \
+ } \
+ if (ssa->ops[line+1].op2_def >= 0) { \
+ MACRO(ssa->ops[line+1].op2_def); \
+ } \
+ if (ssa->ops[line+1].result_def >= 0) { \
+ MACRO(ssa->ops[line+1].result_def); \
+ } \
+ } \
+ } while (0)
+
+
+#define FOR_EACH_VAR_USAGE(_var, MACRO) \
+ do { \
+ zend_ssa_phi *p = ssa->vars[_var].phi_use_chain; \
+ int use = ssa->vars[_var].use_chain; \
+ while (use >= 0) { \
+ FOR_EACH_DEFINED_VAR(use, MACRO); \
+ use = zend_ssa_next_use(ssa->ops, _var, use); \
+ } \
+ p = ssa->vars[_var].phi_use_chain; \
+ while (p) { \
+ MACRO(p->ssa_var); \
+ p = zend_ssa_next_use_phi(ssa, _var, p); \
+ } \
+ } while (0)
+
+static void zend_ssa_check_scc_var(const zend_op_array *op_array, zend_ssa *ssa, int var, int *index, int *dfs, int *root, zend_worklist_stack *stack) /* {{{ */
+{
+#ifdef SYM_RANGE
+ zend_ssa_phi *p;
+#endif
+
+ dfs[var] = *index;
+ (*index)++;
+ root[var] = var;
+
+ FOR_EACH_VAR_USAGE(var, CHECK_SCC_VAR);
+
+#ifdef SYM_RANGE
+ /* Process symbolic control-flow constraints */
+ p = ssa->vars[var].sym_use_chain;
+ while (p) {
+ CHECK_SCC_VAR(p->ssa_var);
+ p = p->sym_use_chain;
+ }
+#endif
+
+ if (root[var] == var) {
+ ssa->vars[var].scc = ssa->sccs;
+ while (stack->len > 0) {
+ int var2 = zend_worklist_stack_peek(stack);
+ if (dfs[var2] <= dfs[var]) {
+ break;
+ }
+ zend_worklist_stack_pop(stack);
+ ssa->vars[var2].scc = ssa->sccs;
+ }
+ ssa->sccs++;
+ } else {
+ zend_worklist_stack_push(stack, var);
+ }
+}
+/* }}} */
+
+int zend_ssa_find_sccs(const zend_op_array *op_array, zend_ssa *ssa) /* {{{ */
+{
+ int index = 0, *dfs, *root;
+ zend_worklist_stack stack;
+ int j;
+ ALLOCA_FLAG(dfs_use_heap)
+ ALLOCA_FLAG(root_use_heap)
+ ALLOCA_FLAG(stack_use_heap)
+
+ dfs = do_alloca(sizeof(int) * ssa->vars_count, dfs_use_heap);
+ memset(dfs, -1, sizeof(int) * ssa->vars_count);
+ root = do_alloca(sizeof(int) * ssa->vars_count, root_use_heap);
+ ZEND_WORKLIST_STACK_ALLOCA(&stack, ssa->vars_count, stack_use_heap);
+
+ /* Find SCCs */
+ for (j = 0; j < ssa->vars_count; j++) {
+ if (!ssa->vars[j].no_val && dfs[j] < 0) {
+ zend_ssa_check_scc_var(op_array, ssa, j, &index, dfs, root, &stack);
+ }
+ }
+
+ /* Revert SCC order */
+ for (j = 0; j < ssa->vars_count; j++) {
+ if (ssa->vars[j].scc >= 0) {
+ ssa->vars[j].scc = ssa->sccs - (ssa->vars[j].scc + 1);
+ }
+ }
+
+ for (j = 0; j < ssa->vars_count; j++) {
+ if (ssa->vars[j].scc >= 0) {
+ int var = j;
+ if (root[j] == j) {
+ ssa->vars[j].scc_entry = 1;
+ }
+ FOR_EACH_VAR_USAGE(var, CHECK_SCC_ENTRY);
+ }
+ }
+
+ ZEND_WORKLIST_STACK_FREE_ALLOCA(&stack, stack_use_heap);
+ free_alloca(root, root_use_heap);
+ free_alloca(dfs, dfs_use_heap);
+
+ return SUCCESS;
+}
+/* }}} */
+
+static inline zend_bool is_no_val_use(const zend_op *opline, const zend_ssa_op *ssa_op, int var)
+{
+ if (opline->opcode == ZEND_ASSIGN ||
+ (opline->opcode == ZEND_UNSET_VAR && (opline->extended_value & ZEND_QUICK_SET))) {
+ return ssa_op->op1_use == var && ssa_op->op2_use != var;
+ }
+ if (opline->opcode == ZEND_FE_FETCH_R) {
+ return ssa_op->op2_use == var && ssa_op->op1_use != var;
+ }
+ return 0;
+}
+
+int zend_ssa_find_false_dependencies(const zend_op_array *op_array, zend_ssa *ssa) /* {{{ */
+{
+ zend_ssa_var *ssa_vars = ssa->vars;
+ zend_ssa_op *ssa_ops = ssa->ops;
+ int ssa_vars_count = ssa->vars_count;
+ zend_bitset worklist;
+ int i, j, use;
+ zend_ssa_phi *p;
+ ALLOCA_FLAG(use_heap);
+
+ if (!op_array->function_name || !ssa->vars || !ssa->ops) {
+ return SUCCESS;
+ }
+
+ worklist = do_alloca(sizeof(zend_ulong) * zend_bitset_len(ssa_vars_count), use_heap);
+ memset(worklist, 0, sizeof(zend_ulong) * zend_bitset_len(ssa_vars_count));
+
+ for (i = 0; i < ssa_vars_count; i++) {
+ ssa_vars[i].no_val = 1; /* mark as unused */
+ use = ssa->vars[i].use_chain;
+ while (use >= 0) {
+ if (!is_no_val_use(&op_array->opcodes[use], &ssa->ops[use], i)) {
+ ssa_vars[i].no_val = 0; /* used directly */
+ zend_bitset_incl(worklist, i);
+ break;
+ }
+ use = zend_ssa_next_use(ssa_ops, i, use);
+ }
+ }
+
+ while (!zend_bitset_empty(worklist, zend_bitset_len(ssa_vars_count))) {
+ i = zend_bitset_first(worklist, zend_bitset_len(ssa_vars_count));
+ zend_bitset_excl(worklist, i);
+ if (ssa_vars[i].definition_phi) {
+ /* mark all possible sources as used */
+ p = ssa_vars[i].definition_phi;
+ if (p->pi >= 0) {
+ if (ssa_vars[p->sources[0]].no_val) {
+ ssa_vars[p->sources[0]].no_val = 0; /* used indirectly */
+ zend_bitset_incl(worklist, p->sources[0]);
+ }
+ } else {
+ for (j = 0; j < ssa->cfg.blocks[p->block].predecessors_count; j++) {
+ if (p->sources[j] >= 0 && ssa->vars[p->sources[j]].no_val) {
+ ssa_vars[p->sources[j]].no_val = 0; /* used indirectly */
+ zend_bitset_incl(worklist, p->sources[j]);
+ }
+ }
+ }
+ }
+ }
+
+ free_alloca(worklist, use_heap);
+
+ return SUCCESS;
+}
+/* }}} */
+
+/* From "Hacker's Delight" */
+zend_ulong minOR(zend_ulong a, zend_ulong b, zend_ulong c, zend_ulong d)
+{
+ zend_ulong m, temp;
+
+ m = 1L << (sizeof(zend_ulong) * 8 - 1);
+ while (m != 0) {
+ if (~a & c & m) {
+ temp = (a | m) & -m;
+ if (temp <= b) {
+ a = temp;
+ break;
+ }
+ } else if (a & ~c & m) {
+ temp = (c | m) & -m;
+ if (temp <= d) {
+ c = temp;
+ break;
+ }
+ }
+ m = m >> 1;
+ }
+ return a | c;
+}
+
+zend_ulong maxOR(zend_ulong a, zend_ulong b, zend_ulong c, zend_ulong d)
+{
+ zend_ulong m, temp;
+
+ m = 1L << (sizeof(zend_ulong) * 8 - 1);
+ while (m != 0) {
+ if (b & d & m) {
+ temp = (b - m) | (m - 1);
+ if (temp >= a) {
+ b = temp;
+ break;
+ }
+ temp = (d - m) | (m - 1);
+ if (temp >= c) {
+ d = temp;
+ break;
+ }
+ }
+ m = m >> 1;
+ }
+ return b | d;
+}
+
+zend_ulong minAND(zend_ulong a, zend_ulong b, zend_ulong c, zend_ulong d)
+{
+ zend_ulong m, temp;
+
+ m = 1L << (sizeof(zend_ulong) * 8 - 1);
+ while (m != 0) {
+ if (~a & ~c & m) {
+ temp = (a | m) & -m;
+ if (temp <= b) {
+ a = temp;
+ break;
+ }
+ temp = (c | m) & -m;
+ if (temp <= d) {
+ c = temp;
+ break;
+ }
+ }
+ m = m >> 1;
+ }
+ return a & c;
+}
+
+zend_ulong maxAND(zend_ulong a, zend_ulong b, zend_ulong c, zend_ulong d)
+{
+ zend_ulong m, temp;
+
+ m = 1L << (sizeof(zend_ulong) * 8 - 1);
+ while (m != 0) {
+ if (b & ~d & m) {
+ temp = (b | ~m) | (m - 1);
+ if (temp >= a) {
+ b = temp;
+ break;
+ }
+ } else if (~b & d & m) {
+ temp = (d | ~m) | (m - 1);
+ if (temp >= c) {
+ d = temp;
+ break;
+ }
+ }
+ m = m >> 1;
+ }
+ return b & d;
+}
+
+zend_ulong minXOR(zend_ulong a, zend_ulong b, zend_ulong c, zend_ulong d)
+{
+ return minAND(a, b, ~d, ~c) | minAND(~b, ~a, c, d);
+}
+
+zend_ulong maxXOR(zend_ulong a, zend_ulong b, zend_ulong c, zend_ulong d)
+{
+ return maxOR(0, maxAND(a, b, ~d, ~c), 0, maxAND(~b, ~a, c, d));
+}
+
+/* Based on "Hacker's Delight" */
+
+/*
+0: + + + + 0 0 0 0 => 0 0 + min/max
+2: + + - + 0 0 1 0 => 1 0 ? min(a,b,c,-1)/max(a,b,0,d)
+3: + + - - 0 0 1 1 => 1 1 - min/max
+8: - + + + 1 0 0 0 => 1 0 ? min(a,-1,b,d)/max(0,b,c,d)
+a: - + - + 1 0 1 0 => 1 0 ? MIN(a,c)/max(0,b,0,d)
+b: - + - - 1 0 1 1 => 1 1 - c/-1
+c: - - + + 1 1 0 0 => 1 1 - min/max
+e: - - - + 1 1 1 0 => 1 1 - a/-1
+f - - - - 1 1 1 1 => 1 1 - min/max
+*/
+static void zend_ssa_range_or(zend_long a, zend_long b, zend_long c, zend_long d, zend_ssa_range *tmp)
+{
+ int x = ((a < 0) ? 8 : 0) |
+ ((b < 0) ? 4 : 0) |
+ ((c < 0) ? 2 : 0) |
+ ((d < 0) ? 2 : 0);
+ switch (x) {
+ case 0x0:
+ case 0x3:
+ case 0xc:
+ case 0xf:
+ tmp->min = minOR(a, b, c, d);
+ tmp->max = maxOR(a, b, c, d);
+ break;
+ case 0x2:
+ tmp->min = minOR(a, b, c, -1);
+ tmp->max = maxOR(a, b, 0, d);
+ break;
+ case 0x8:
+ tmp->min = minOR(a, -1, c, d);
+ tmp->max = maxOR(0, b, c, d);
+ break;
+ case 0xa:
+ tmp->min = MIN(a, c);
+ tmp->max = maxOR(0, b, 0, d);
+ break;
+ case 0xb:
+ tmp->min = c;
+ tmp->max = -1;
+ break;
+ case 0xe:
+ tmp->min = a;
+ tmp->max = -1;
+ break;
+ }
+}
+
+/*
+0: + + + + 0 0 0 0 => 0 0 + min/max
+2: + + - + 0 0 1 0 => 0 0 + 0/b
+3: + + - - 0 0 1 1 => 0 0 + min/max
+8: - + + + 1 0 0 0 => 0 0 + 0/d
+a: - + - + 1 0 1 0 => 1 0 ? min(a,-1,c,-1)/NAX(b,d)
+b: - + - - 1 0 1 1 => 1 0 ? min(a,-1,c,d)/max(0,b,c,d)
+c: - - + + 1 1 0 0 => 1 1 - min/max
+e: - - - + 1 1 1 0 => 1 0 ? min(a,b,c,-1)/max(a,b,0,d)
+f - - - - 1 1 1 1 => 1 1 - min/max
+*/
+static void zend_ssa_range_and(zend_long a, zend_long b, zend_long c, zend_long d, zend_ssa_range *tmp)
+{
+ int x = ((a < 0) ? 8 : 0) |
+ ((b < 0) ? 4 : 0) |
+ ((c < 0) ? 2 : 0) |
+ ((d < 0) ? 2 : 0);
+ switch (x) {
+ case 0x0:
+ case 0x3:
+ case 0xc:
+ case 0xf:
+ tmp->min = minAND(a, b, c, d);
+ tmp->max = maxAND(a, b, c, d);
+ break;
+ case 0x2:
+ tmp->min = 0;
+ tmp->max = b;
+ break;
+ case 0x8:
+ tmp->min = 0;
+ tmp->max = d;
+ break;
+ case 0xa:
+ tmp->min = minAND(a, -1, c, -1);
+ tmp->max = MAX(b, d);
+ break;
+ case 0xb:
+ tmp->min = minAND(a, -1, c, d);
+ tmp->max = maxAND(0, b, c, d);
+ break;
+ case 0xe:
+ tmp->min = minAND(a, b, c, -1);
+ tmp->max = maxAND(a, b, 0, d);
+ break;
+ }
+}
+
+int zend_inference_calc_range(const zend_op_array *op_array, zend_ssa *ssa, int var, int widening, int narrowing, zend_ssa_range *tmp)
+{
+ uint32_t line;
+ zend_op *opline;
+ zend_long op1_min, op2_min, op1_max, op2_max, t1, t2, t3, t4;
+
+ if (ssa->vars[var].definition_phi) {
+ zend_ssa_phi *p = ssa->vars[var].definition_phi;
+ int i;
+
+ tmp->underflow = 0;
+ tmp->min = ZEND_LONG_MAX;
+ tmp->max = ZEND_LONG_MIN;
+ tmp->overflow = 0;
+ if (p->pi >= 0 && p->has_range_constraint) {
+ zend_ssa_range_constraint *constraint = &p->constraint.range;
+ if (constraint->negative) {
+ if (ssa->var_info[p->sources[0]].has_range) {
+ tmp->underflow = ssa->var_info[p->sources[0]].range.underflow;
+ tmp->min = ssa->var_info[p->sources[0]].range.min;
+ tmp->max = ssa->var_info[p->sources[0]].range.max;
+ tmp->overflow = ssa->var_info[p->sources[0]].range.overflow;
+ } else if (narrowing) {
+ tmp->underflow = 1;
+ tmp->min = ZEND_LONG_MIN;
+ tmp->max = ZEND_LONG_MAX;
+ tmp->overflow = 1;
+ }
+#ifdef NEG_RANGE
+ if (constraint->min_ssa_var < 0 &&
+ constraint->min_ssa_var < 0 &&
+ ssa->var_info[p->ssa_var].has_range) {
+#ifdef LOG_NEG_RANGE
+ fprintf(stderr, "%s() #%d [%ld..%ld] -> [%ld..%ld]?\n",
+ op_array->function_name,
+ p->ssa_var,
+ ssa->var_info[p->ssa_var].range.min,
+ ssa->var_info[p->ssa_var].range.max,
+ tmp->min,
+ tmp->max);
+#endif
+ if (constraint->negative == NEG_USE_LT &&
+ tmp->max >= constraint->range.min) {
+ tmp->overflow = 0;
+ tmp->max = constraint->range.min - 1;
+#ifdef LOG_NEG_RANGE
+ fprintf(stderr, " => [%ld..%ld]\n",
+ tmp->min,
+ tmp->max);
+#endif
+ } else if (constraint->negative == NEG_USE_GT &&
+ tmp->min <= constraint->range.max) {
+ tmp->underflow = 0;
+ tmp->min = constraint->range.max + 1;
+#ifdef LOG_NEG_RANGE
+ fprintf(stderr, " => [%ld..%ld]\n",
+ tmp->min,
+ tmp->max);
+#endif
+ }
+ }
+#endif
+ } else if (ssa->var_info[p->sources[0]].has_range) {
+ /* intersection */
+ tmp->underflow = ssa->var_info[p->sources[0]].range.underflow;
+ tmp->min = ssa->var_info[p->sources[0]].range.min;
+ tmp->max = ssa->var_info[p->sources[0]].range.max;
+ tmp->overflow = ssa->var_info[p->sources[0]].range.overflow;
+ if (constraint->min_ssa_var < 0) {
+ tmp->underflow = constraint->range.underflow && tmp->underflow;
+ tmp->min = MAX(constraint->range.min, tmp->min);
+#ifdef SYM_RANGE
+ } else if (narrowing && ssa->var_info[constraint->min_ssa_var].has_range) {
+ tmp->underflow = ssa->var_info[constraint->min_ssa_var].range.underflow && tmp->underflow;
+ tmp->min = MAX(ssa->var_info[constraint->min_ssa_var].range.min + constraint->range.min, tmp->min);
+#endif
+ }
+ if (constraint->max_ssa_var < 0) {
+ tmp->max = MIN(constraint->range.max, tmp->max);
+ tmp->overflow = constraint->range.overflow && tmp->overflow;
+#ifdef SYM_RANGE
+ } else if (narrowing && ssa->var_info[constraint->max_ssa_var].has_range) {
+ tmp->max = MIN(ssa->var_info[constraint->max_ssa_var].range.max + constraint->range.max, tmp->max);
+ tmp->overflow = ssa->var_info[constraint->max_ssa_var].range.overflow && tmp->overflow;
+#endif
+ }
+ } else if (narrowing) {
+ if (constraint->min_ssa_var < 0) {
+ tmp->underflow = constraint->range.underflow;
+ tmp->min = constraint->range.min;
+#ifdef SYM_RANGE
+ } else if (narrowing && ssa->var_info[constraint->min_ssa_var].has_range) {
+ tmp->underflow = ssa->var_info[constraint->min_ssa_var].range.underflow;
+ tmp->min = ssa->var_info[constraint->min_ssa_var].range.min + constraint->range.min;
+#endif
+ } else {
+ tmp->underflow = 1;
+ tmp->min = ZEND_LONG_MIN;
+ }
+ if (constraint->max_ssa_var < 0) {
+ tmp->max = constraint->range.max;
+ tmp->overflow = constraint->range.overflow;
+#ifdef SYM_RANGE
+ } else if (narrowing && ssa->var_info[constraint->max_ssa_var].has_range) {
+ tmp->max = ssa->var_info[constraint->max_ssa_var].range.max + constraint->range.max;
+ tmp->overflow = ssa->var_info[constraint->max_ssa_var].range.overflow;
+#endif
+ } else {
+ tmp->max = ZEND_LONG_MAX;
+ tmp->overflow = 1;
+ }
+ }
+ } else {
+ for (i = 0; i < ssa->cfg.blocks[p->block].predecessors_count; i++) {
+ if (p->sources[i] >= 0 && ssa->var_info[p->sources[i]].has_range) {
+ /* union */
+ tmp->underflow |= ssa->var_info[p->sources[i]].range.underflow;
+ tmp->min = MIN(tmp->min, ssa->var_info[p->sources[i]].range.min);
+ tmp->max = MAX(tmp->max, ssa->var_info[p->sources[i]].range.max);
+ tmp->overflow |= ssa->var_info[p->sources[i]].range.overflow;
+ } else if (narrowing) {
+ tmp->underflow = 1;
+ tmp->min = ZEND_LONG_MIN;
+ tmp->max = ZEND_LONG_MAX;
+ tmp->overflow = 1;
+ }
+ }
+ }
+ return (tmp->min <= tmp->max);
+ } else if (ssa->vars[var].definition < 0) {
+ if (var < op_array->last_var &&
+ op_array->function_name) {
+
+ tmp->min = 0;
+ tmp->max = 0;
+ tmp->underflow = 0;
+ tmp->overflow = 0;
+ return 1;
+ }
+ return 0;
+ }
+ line = ssa->vars[var].definition;
+ opline = op_array->opcodes + line;
+
+ tmp->underflow = 0;
+ tmp->overflow = 0;
+ switch (opline->opcode) {
+ case ZEND_ADD:
+ if (ssa->ops[line].result_def == var) {
+ if (OP1_HAS_RANGE() && OP2_HAS_RANGE()) {
+ op1_min = OP1_MIN_RANGE();
+ op2_min = OP2_MIN_RANGE();
+ op1_max = OP1_MAX_RANGE();
+ op2_max = OP2_MAX_RANGE();
+ tmp->min = op1_min + op2_min;
+ tmp->max = op1_max + op2_max;
+ if (OP1_RANGE_UNDERFLOW() ||
+ OP2_RANGE_UNDERFLOW() ||
+ (op1_min < 0 && op2_min < 0 && tmp->min >= 0)) {
+ tmp->underflow = 1;
+ tmp->min = ZEND_LONG_MIN;
+ }
+ if (OP1_RANGE_OVERFLOW() ||
+ OP2_RANGE_OVERFLOW() ||
+ (op1_max > 0 && op2_max > 0 && tmp->max <= 0)) {
+ tmp->overflow = 1;
+ tmp->max = ZEND_LONG_MAX;
+ }
+ return 1;
+ }
+ }
+ break;
+ case ZEND_SUB:
+ if (ssa->ops[line].result_def == var) {
+ if (OP1_HAS_RANGE() && OP2_HAS_RANGE()) {
+ op1_min = OP1_MIN_RANGE();
+ op2_min = OP2_MIN_RANGE();
+ op1_max = OP1_MAX_RANGE();
+ op2_max = OP2_MAX_RANGE();
+ tmp->min = op1_min - op2_max;
+ tmp->max = op1_max - op2_min;
+ if (OP1_RANGE_UNDERFLOW() ||
+ OP2_RANGE_OVERFLOW() ||
+ (op1_min < 0 && op2_max > 0 && tmp->min >= 0)) {
+ tmp->underflow = 1;
+ tmp->min = ZEND_LONG_MIN;
+ }
+ if (OP1_RANGE_OVERFLOW() ||
+ OP2_RANGE_UNDERFLOW() ||
+ (op1_max > 0 && op2_min < 0 && tmp->max <= 0)) {
+ tmp->overflow = 1;
+ tmp->max = ZEND_LONG_MAX;
+ }
+ return 1;
+ }
+ }
+ break;
+ case ZEND_MUL:
+ if (ssa->ops[line].result_def == var) {
+ if (OP1_HAS_RANGE() && OP2_HAS_RANGE()) {
+ op1_min = OP1_MIN_RANGE();
+ op2_min = OP2_MIN_RANGE();
+ op1_max = OP1_MAX_RANGE();
+ op2_max = OP2_MAX_RANGE();
+ t1 = op1_min * op2_min;
+ t2 = op1_min * op2_max;
+ t3 = op1_max * op2_min;
+ t4 = op1_max * op2_max;
+ // FIXME: more careful overflow checks?
+ if (OP1_RANGE_UNDERFLOW() ||
+ OP2_RANGE_UNDERFLOW() ||
+ OP1_RANGE_OVERFLOW() ||
+ OP2_RANGE_OVERFLOW() ||
+ (double)t1 != (double)op1_min * (double)op2_min ||
+ (double)t2 != (double)op1_min * (double)op2_max ||
+ (double)t3 != (double)op1_max * (double)op2_min ||
+ (double)t4 != (double)op1_max * (double)op2_max) {
+ tmp->underflow = 1;
+ tmp->overflow = 1;
+ tmp->min = ZEND_LONG_MIN;
+ tmp->max = ZEND_LONG_MAX;
+ } else {
+ tmp->min = MIN(MIN(t1, t2), MIN(t3, t4));
+ tmp->max = MAX(MAX(t1, t2), MAX(t3, t4));
+ }
+ return 1;
+ }
+ }
+ break;
+ case ZEND_DIV:
+ if (ssa->ops[line].result_def == var) {
+ if (OP1_HAS_RANGE() && OP2_HAS_RANGE()) {
+ op1_min = OP1_MIN_RANGE();
+ op2_min = OP2_MIN_RANGE();
+ op1_max = OP1_MAX_RANGE();
+ op2_max = OP2_MAX_RANGE();
+ if (op2_min <= 0 && op2_max >= 0) {
+ break;
+ }
+ t1 = op1_min / op2_min;
+ t2 = op1_min / op2_max;
+ t3 = op1_max / op2_min;
+ t4 = op1_max / op2_max;
+ // FIXME: more careful overflow checks?
+ if (OP1_RANGE_UNDERFLOW() ||
+ OP2_RANGE_UNDERFLOW() ||
+ OP1_RANGE_OVERFLOW() ||
+ OP2_RANGE_OVERFLOW() ||
+ t1 != (zend_long)((double)op1_min / (double)op2_min) ||
+ t2 != (zend_long)((double)op1_min / (double)op2_max) ||
+ t3 != (zend_long)((double)op1_max / (double)op2_min) ||
+ t4 != (zend_long)((double)op1_max / (double)op2_max)) {
+ tmp->underflow = 1;
+ tmp->overflow = 1;
+ tmp->min = ZEND_LONG_MIN;
+ tmp->max = ZEND_LONG_MAX;
+ } else {
+ tmp->min = MIN(MIN(t1, t2), MIN(t3, t4));
+ tmp->max = MAX(MAX(t1, t2), MAX(t3, t4));
+ }
+ return 1;
+ }
+ }
+ break;
+ case ZEND_MOD:
+ if (ssa->ops[line].result_def == var) {
+ if (OP1_HAS_RANGE() && OP2_HAS_RANGE()) {
+ if (OP1_RANGE_UNDERFLOW() ||
+ OP2_RANGE_UNDERFLOW() ||
+ OP1_RANGE_OVERFLOW() ||
+ OP2_RANGE_OVERFLOW()) {
+ tmp->min = ZEND_LONG_MIN;
+ tmp->max = ZEND_LONG_MAX;
+ } else {
+ op1_min = OP1_MIN_RANGE();
+ op2_min = OP2_MIN_RANGE();
+ op1_max = OP1_MAX_RANGE();
+ op2_max = OP2_MAX_RANGE();
+ if (op2_min == 0 || op2_max == 0) {
+ /* avoid division by zero */
+ break;
+ }
+ t1 = (op2_min == -1) ? 0 : (op1_min % op2_min);
+ t2 = (op2_max == -1) ? 0 : (op1_min % op2_max);
+ t3 = (op2_min == -1) ? 0 : (op1_max % op2_min);
+ t4 = (op2_max == -1) ? 0 : (op1_max % op2_max);
+ tmp->min = MIN(MIN(t1, t2), MIN(t3, t4));
+ tmp->max = MAX(MAX(t1, t2), MAX(t3, t4));
+ }
+ }
+ }
+ break;
+ case ZEND_SL:
+ if (ssa->ops[line].result_def == var) {
+ if (OP1_HAS_RANGE() && OP2_HAS_RANGE()) {
+ if (OP1_RANGE_UNDERFLOW() ||
+ OP2_RANGE_UNDERFLOW() ||
+ OP1_RANGE_OVERFLOW() ||
+ OP2_RANGE_OVERFLOW()) {
+ tmp->min = ZEND_LONG_MIN;
+ tmp->max = ZEND_LONG_MAX;
+ } else {
+ op1_min = OP1_MIN_RANGE();
+ op2_min = OP2_MIN_RANGE();
+ op1_max = OP1_MAX_RANGE();
+ op2_max = OP2_MAX_RANGE();
+ t1 = op1_min << op2_min;
+ t2 = op1_min << op2_max;
+ t3 = op1_max << op2_min;
+ t4 = op1_max << op2_max;
+ tmp->min = MIN(MIN(t1, t2), MIN(t3, t4));
+ tmp->max = MAX(MAX(t1, t2), MAX(t3, t4));
+ }
+ return 1;
+ }
+ }
+ break;
+ case ZEND_SR:
+ if (ssa->ops[line].result_def == var) {
+ if (OP1_HAS_RANGE() && OP2_HAS_RANGE()) {
+ if (OP1_RANGE_UNDERFLOW() ||
+ OP2_RANGE_UNDERFLOW() ||
+ OP1_RANGE_OVERFLOW() ||
+ OP2_RANGE_OVERFLOW()) {
+ tmp->min = ZEND_LONG_MIN;
+ tmp->max = ZEND_LONG_MAX;
+ } else {
+ op1_min = OP1_MIN_RANGE();
+ op2_min = OP2_MIN_RANGE();
+ op1_max = OP1_MAX_RANGE();
+ op2_max = OP2_MAX_RANGE();
+ t1 = op1_min >> op2_min;
+ t2 = op1_min >> op2_max;
+ t3 = op1_max >> op2_min;
+ t4 = op1_max >> op2_max;
+ tmp->min = MIN(MIN(t1, t2), MIN(t3, t4));
+ tmp->max = MAX(MAX(t1, t2), MAX(t3, t4));
+ }
+ return 1;
+ }
+ }
+ break;
+ case ZEND_BW_OR:
+ if (ssa->ops[line].result_def == var) {
+ if (OP1_HAS_RANGE() && OP2_HAS_RANGE()) {
+ if (OP1_RANGE_UNDERFLOW() ||
+ OP2_RANGE_UNDERFLOW() ||
+ OP1_RANGE_OVERFLOW() ||
+ OP2_RANGE_OVERFLOW()) {
+ tmp->min = ZEND_LONG_MIN;
+ tmp->max = ZEND_LONG_MAX;
+ } else {
+ op1_min = OP1_MIN_RANGE();
+ op2_min = OP2_MIN_RANGE();
+ op1_max = OP1_MAX_RANGE();
+ op2_max = OP2_MAX_RANGE();
+ zend_ssa_range_or(op1_min, op1_max, op2_min, op2_max, tmp);
+ }
+ return 1;
+ }
+ }
+ break;
+ case ZEND_BW_AND:
+ if (ssa->ops[line].result_def == var) {
+ if (OP1_HAS_RANGE() && OP2_HAS_RANGE()) {
+ if (OP1_RANGE_UNDERFLOW() ||
+ OP2_RANGE_UNDERFLOW() ||
+ OP1_RANGE_OVERFLOW() ||
+ OP2_RANGE_OVERFLOW()) {
+ tmp->min = ZEND_LONG_MIN;
+ tmp->max = ZEND_LONG_MAX;
+ } else {
+ op1_min = OP1_MIN_RANGE();
+ op2_min = OP2_MIN_RANGE();
+ op1_max = OP1_MAX_RANGE();
+ op2_max = OP2_MAX_RANGE();
+ zend_ssa_range_and(op1_min, op1_max, op2_min, op2_max, tmp);
+ }
+ return 1;
+ }
+ }
+ break;
+// case ZEND_BW_XOR:
+ case ZEND_BW_NOT:
+ if (ssa->ops[line].result_def == var) {
+ if (OP1_HAS_RANGE()) {
+ if (OP1_RANGE_UNDERFLOW() ||
+ OP1_RANGE_OVERFLOW()) {
+ tmp->min = ZEND_LONG_MIN;
+ tmp->max = ZEND_LONG_MAX;
+ } else {
+ op1_min = OP1_MIN_RANGE();
+ op1_max = OP1_MAX_RANGE();
+ tmp->min = ~op1_max;
+ tmp->max = ~op1_min;
+ }
+ return 1;
+ }
+ }
+ break;
+ case ZEND_CAST:
+ if (ssa->ops[line].result_def == var) {
+ if (opline->extended_value == IS_NULL) {
+ tmp->min = 0;
+ tmp->max = 0;
+ return 1;
+ } else if (opline->extended_value == _IS_BOOL) {
+ if (OP1_HAS_RANGE()) {
+ op1_min = OP1_MIN_RANGE();
+ op1_max = OP1_MAX_RANGE();
+ tmp->min = (op1_min > 0 || op1_max < 0);
+ tmp->max = (op1_min != 0 || op1_max != 0);
+ return 1;
+ } else {
+ tmp->min = 0;
+ tmp->max = 1;
+ return 1;
+ }
+ } else if (opline->extended_value == IS_LONG) {
+ if (OP1_HAS_RANGE()) {
+ tmp->min = OP1_MIN_RANGE();
+ tmp->max = OP1_MAX_RANGE();
+ return 1;
+ } else {
+ tmp->min = ZEND_LONG_MIN;
+ tmp->max = ZEND_LONG_MAX;
+ return 1;
+ }
+ }
+ }
+ break;
+ case ZEND_BOOL:
+ case ZEND_JMPZ_EX:
+ case ZEND_JMPNZ_EX:
+ if (ssa->ops[line].result_def == var) {
+ if (OP1_HAS_RANGE()) {
+ op1_min = OP1_MIN_RANGE();
+ op1_max = OP1_MAX_RANGE();
+ tmp->min = (op1_min > 0 || op1_max < 0);
+ tmp->max = (op1_min != 0 || op1_max != 0);
+ return 1;
+ } else {
+ tmp->min = 0;
+ tmp->max = 1;
+ return 1;
+ }
+ }
+ break;
+ case ZEND_BOOL_NOT:
+ if (ssa->ops[line].result_def == var) {
+ if (OP1_HAS_RANGE()) {
+ op1_min = OP1_MIN_RANGE();
+ op1_max = OP1_MAX_RANGE();
+ tmp->min = (op1_min == 0 && op1_max == 0);
+ tmp->max = (op1_min <= 0 && op1_max >= 0);
+ return 1;
+ } else {
+ tmp->min = 0;
+ tmp->max = 1;
+ return 1;
+ }
+ }
+ break;
+ case ZEND_BOOL_XOR:
+ if (ssa->ops[line].result_def == var) {
+ if (OP1_HAS_RANGE() && OP2_HAS_RANGE()) {
+ op1_min = OP1_MIN_RANGE();
+ op2_min = OP2_MIN_RANGE();
+ op1_max = OP1_MAX_RANGE();
+ op2_max = OP2_MAX_RANGE();
+ op1_min = (op1_min > 0 || op1_max < 0);
+ op1_max = (op1_min != 0 || op1_max != 0);
+ op2_min = (op2_min > 0 || op2_max < 0);
+ op2_max = (op2_min != 0 || op2_max != 0);
+ tmp->min = 0;
+ tmp->max = 1;
+ if (op1_min == op1_max && op2_min == op2_max) {
+ if (op1_min == op2_min) {
+ tmp->max = 0;
+ } else {
+ tmp->min = 1;
+ }
+ }
+ return 1;
+ } else {
+ tmp->min = 0;
+ tmp->max = 1;
+ return 1;
+ }
+ }
+ break;
+ case ZEND_IS_IDENTICAL:
+ case ZEND_IS_EQUAL:
+ if (ssa->ops[line].result_def == var) {
+ if (OP1_HAS_RANGE() && OP2_HAS_RANGE()) {
+ op1_min = OP1_MIN_RANGE();
+ op2_min = OP2_MIN_RANGE();
+ op1_max = OP1_MAX_RANGE();
+ op2_max = OP2_MAX_RANGE();
+
+ tmp->min = (op1_min == op1_max &&
+ op2_min == op2_max &&
+ op1_min == op2_max);
+ tmp->max = (op1_min <= op2_max && op1_max >= op2_min);
+ return 1;
+ } else {
+ tmp->min = 0;
+ tmp->max = 1;
+ return 1;
+ }
+ }
+ break;
+ case ZEND_IS_NOT_IDENTICAL:
+ case ZEND_IS_NOT_EQUAL:
+ if (ssa->ops[line].result_def == var) {
+ if (OP1_HAS_RANGE() && OP2_HAS_RANGE()) {
+ op1_min = OP1_MIN_RANGE();
+ op2_min = OP2_MIN_RANGE();
+ op1_max = OP1_MAX_RANGE();
+ op2_max = OP2_MAX_RANGE();
+
+ tmp->min = (op1_min > op2_max || op1_max < op2_min);
+ tmp->max = (op1_min != op1_max ||
+ op2_min != op2_max ||
+ op1_min != op2_max);
+ return 1;
+ } else {
+ tmp->min = 0;
+ tmp->max = 1;
+ return 1;
+ }
+ }
+ break;
+ case ZEND_IS_SMALLER:
+ if (ssa->ops[line].result_def == var) {
+ if (OP1_HAS_RANGE() && OP2_HAS_RANGE()) {
+ op1_min = OP1_MIN_RANGE();
+ op2_min = OP2_MIN_RANGE();
+ op1_max = OP1_MAX_RANGE();
+ op2_max = OP2_MAX_RANGE();
+
+ tmp->min = op1_max < op2_min;
+ tmp->max = op1_min < op2_max;
+ return 1;
+ } else {
+ tmp->min = 0;
+ tmp->max = 1;
+ return 1;
+ }
+ }
+ break;
+ case ZEND_IS_SMALLER_OR_EQUAL:
+ if (ssa->ops[line].result_def == var) {
+ if (OP1_HAS_RANGE() && OP2_HAS_RANGE()) {
+ op1_min = OP1_MIN_RANGE();
+ op2_min = OP2_MIN_RANGE();
+ op1_max = OP1_MAX_RANGE();
+ op2_max = OP2_MAX_RANGE();
+
+ tmp->min = op1_max <= op2_min;
+ tmp->max = op1_min <= op2_max;
+ return 1;
+ } else {
+ tmp->min = 0;
+ tmp->max = 1;
+ return 1;
+ }
+ }
+ break;
+ case ZEND_QM_ASSIGN:
+ case ZEND_JMP_SET:
+ case ZEND_COALESCE:
+ if (ssa->ops[line].result_def == var) {
+ if (OP1_HAS_RANGE()) {
+ tmp->min = OP1_MIN_RANGE();
+ tmp->max = OP1_MAX_RANGE();
+ tmp->underflow = OP1_RANGE_UNDERFLOW();
+ tmp->overflow = OP1_RANGE_OVERFLOW();
+ return 1;
+ }
+ }
+ break;
+ case ZEND_ASSERT_CHECK:
+ if (ssa->ops[line].result_def == var) {
+ tmp->min = 0;
+ tmp->max = 1;
+ return 1;
+ }
+ break;
+ case ZEND_SEND_VAR:
+ if (ssa->ops[line].op1_def == var) {
+ if (ssa->ops[line].op1_def >= 0) {
+ if (OP1_HAS_RANGE()) {
+ tmp->underflow = OP1_RANGE_UNDERFLOW();
+ tmp->min = OP1_MIN_RANGE();
+ tmp->max = OP1_MAX_RANGE();
+ tmp->overflow = OP1_RANGE_OVERFLOW();
+ return 1;
+ }
+ }
+ }
+ break;
+ case ZEND_PRE_INC:
+ if (ssa->ops[line].op1_def == var || ssa->ops[line].result_def == var) {
+ if (OP1_HAS_RANGE()) {
+ tmp->min = OP1_MIN_RANGE();
+ tmp->max = OP1_MAX_RANGE();
+ tmp->underflow = OP1_RANGE_UNDERFLOW();
+ tmp->overflow = OP1_RANGE_OVERFLOW();
+ if (tmp->max < ZEND_LONG_MAX) {
+ tmp->max++;
+ } else {
+ tmp->overflow = 1;
+ }
+ if (tmp->min < ZEND_LONG_MAX && !tmp->underflow) {
+ tmp->min++;
+ }
+ return 1;
+ }
+ }
+ break;
+ case ZEND_PRE_DEC:
+ if (ssa->ops[line].op1_def == var || ssa->ops[line].result_def == var) {
+ if (OP1_HAS_RANGE()) {
+ tmp->min = OP1_MIN_RANGE();
+ tmp->max = OP1_MAX_RANGE();
+ tmp->underflow = OP1_RANGE_UNDERFLOW();
+ tmp->overflow = OP1_RANGE_OVERFLOW();
+ if (tmp->min > ZEND_LONG_MIN) {
+ tmp->min--;
+ } else {
+ tmp->underflow = 1;
+ }
+ if (tmp->max > ZEND_LONG_MIN && !tmp->overflow) {
+ tmp->max--;
+ }
+ return 1;
+ }
+ }
+ break;
+ case ZEND_POST_INC:
+ if (ssa->ops[line].op1_def == var || ssa->ops[line].result_def == var) {
+ if (OP1_HAS_RANGE()) {
+ tmp->min = OP1_MIN_RANGE();
+ tmp->max = OP1_MAX_RANGE();
+ tmp->underflow = OP1_RANGE_UNDERFLOW();
+ tmp->overflow = OP1_RANGE_OVERFLOW();
+ if (ssa->ops[line].result_def == var) {
+ return 1;
+ }
+ if (tmp->max < ZEND_LONG_MAX) {
+ tmp->max++;
+ } else {
+ tmp->overflow = 1;
+ }
+ if (tmp->min < ZEND_LONG_MAX && !tmp->underflow) {
+ tmp->min++;
+ }
+ return 1;
+ }
+ }
+ break;
+ case ZEND_POST_DEC:
+ if (ssa->ops[line].op1_def == var || ssa->ops[line].result_def == var) {
+ if (OP1_HAS_RANGE()) {
+ tmp->min = OP1_MIN_RANGE();
+ tmp->max = OP1_MAX_RANGE();
+ tmp->underflow = OP1_RANGE_UNDERFLOW();
+ tmp->overflow = OP1_RANGE_OVERFLOW();
+ if (ssa->ops[line].result_def == var) {
+ return 1;
+ }
+ if (tmp->min > ZEND_LONG_MIN) {
+ tmp->min--;
+ } else {
+ tmp->underflow = 1;
+ }
+ if (tmp->max > ZEND_LONG_MIN && !tmp->overflow) {
+ tmp->max--;
+ }
+ return 1;
+ }
+ }
+ break;
+ case ZEND_UNSET_DIM:
+ case ZEND_UNSET_OBJ:
+ if (ssa->ops[line].op1_def == var) {
+ /* If op1 is scalar, UNSET_DIM and UNSET_OBJ have no effect, so we can keep
+ * the previous ranges. */
+ if (OP1_HAS_RANGE()) {
+ tmp->min = OP1_MIN_RANGE();
+ tmp->max = OP1_MAX_RANGE();
+ tmp->underflow = OP1_RANGE_UNDERFLOW();
+ tmp->overflow = OP1_RANGE_OVERFLOW();
+ return 1;
+ }
+ }
+ break;
+ case ZEND_ASSIGN:
+ if (ssa->ops[line].op1_def == var || ssa->ops[line].op2_def == var || ssa->ops[line].result_def == var) {
+ if (OP2_HAS_RANGE()) {
+ tmp->min = OP2_MIN_RANGE();
+ tmp->max = OP2_MAX_RANGE();
+ tmp->underflow = OP2_RANGE_UNDERFLOW();
+ tmp->overflow = OP2_RANGE_OVERFLOW();
+ return 1;
+ }
+ }
+ break;
+ case ZEND_ASSIGN_DIM:
+ case ZEND_ASSIGN_OBJ:
+ if (ssa->ops[line+1].op1_def == var) {
+ if ((opline+1)->opcode == ZEND_OP_DATA) {
+ opline++;
+ tmp->min = OP1_MIN_RANGE();
+ tmp->max = OP1_MAX_RANGE();
+ tmp->underflow = OP1_RANGE_UNDERFLOW();
+ tmp->overflow = OP1_RANGE_OVERFLOW();
+ return 1;
+ }
+ }
+ break;
+ case ZEND_ASSIGN_ADD:
+ if (opline->extended_value == 0) {
+ if (ssa->ops[line].op1_def == var || ssa->ops[line].result_def == var) {
+ if (OP1_HAS_RANGE() && OP2_HAS_RANGE()) {
+ op1_min = OP1_MIN_RANGE();
+ op2_min = OP2_MIN_RANGE();
+ op1_max = OP1_MAX_RANGE();
+ op2_max = OP2_MAX_RANGE();
+ tmp->min = op1_min + op2_min;
+ tmp->max = op1_max + op2_max;
+ if (OP1_RANGE_UNDERFLOW() ||
+ OP2_RANGE_UNDERFLOW() ||
+ (op1_min < 0 && op2_min < 0 && tmp->min >= 0)) {
+ tmp->underflow = 1;
+ tmp->min = ZEND_LONG_MIN;
+ }
+ if (OP1_RANGE_OVERFLOW() ||
+ OP2_RANGE_OVERFLOW() ||
+ (op1_max > 0 && op2_max > 0 && tmp->max <= 0)) {
+ tmp->overflow = 1;
+ tmp->max = ZEND_LONG_MAX;
+ }
+ return 1;
+ }
+ }
+ } else if ((opline+1)->opcode == ZEND_OP_DATA) {
+ if (ssa->ops[line+1].op1_def == var) {
+ opline++;
+ if (OP1_HAS_RANGE()) {
+ tmp->min = OP1_MIN_RANGE();
+ tmp->max = OP1_MAX_RANGE();
+ tmp->underflow = OP1_RANGE_UNDERFLOW();
+ tmp->overflow = OP1_RANGE_OVERFLOW();
+ return 1;
+ }
+ }
+ }
+ break;
+ case ZEND_ASSIGN_SUB:
+ if (opline->extended_value == 0) {
+ if (ssa->ops[line].op1_def == var || ssa->ops[line].result_def == var) {
+ if (OP1_HAS_RANGE() && OP2_HAS_RANGE()) {
+ op1_min = OP1_MIN_RANGE();
+ op2_min = OP2_MIN_RANGE();
+ op1_max = OP1_MAX_RANGE();
+ op2_max = OP2_MAX_RANGE();
+ tmp->min = op1_min - op2_max;
+ tmp->max = op1_max - op2_min;
+ if (OP1_RANGE_UNDERFLOW() ||
+ OP2_RANGE_OVERFLOW() ||
+ (op1_min < 0 && op2_max > 0 && tmp->min >= 0)) {
+ tmp->underflow = 1;
+ tmp->min = ZEND_LONG_MIN;
+ }
+ if (OP1_RANGE_OVERFLOW() ||
+ OP2_RANGE_UNDERFLOW() ||
+ (op1_max > 0 && op2_min < 0 && tmp->max <= 0)) {
+ tmp->overflow = 1;
+ tmp->max = ZEND_LONG_MAX;
+ }
+ return 1;
+ }
+ }
+ } else if ((opline+1)->opcode == ZEND_OP_DATA) {
+ if (ssa->ops[line+1].op1_def == var) {
+ opline++;
+ if (OP1_HAS_RANGE()) {
+ tmp->min = OP1_MIN_RANGE();
+ tmp->max = OP1_MAX_RANGE();
+ tmp->underflow = OP1_RANGE_UNDERFLOW();
+ tmp->overflow = OP1_RANGE_OVERFLOW();
+ return 1;
+ }
+ }
+ }
+ break;
+ case ZEND_ASSIGN_MUL:
+ if (opline->extended_value == 0) {
+ if (ssa->ops[line].op1_def == var || ssa->ops[line].result_def == var) {
+ if (OP1_HAS_RANGE() && OP2_HAS_RANGE()) {
+ op1_min = OP1_MIN_RANGE();
+ op2_min = OP2_MIN_RANGE();
+ op1_max = OP1_MAX_RANGE();
+ op2_max = OP2_MAX_RANGE();
+ t1 = op1_min * op2_min;
+ t2 = op1_min * op2_max;
+ t3 = op1_max * op2_min;
+ t4 = op1_max * op2_max;
+ // FIXME: more careful overflow checks?
+ if (OP1_RANGE_UNDERFLOW() ||
+ OP2_RANGE_UNDERFLOW() ||
+ OP1_RANGE_OVERFLOW() ||
+ OP2_RANGE_OVERFLOW() ||
+ (double)t1 != (double)op1_min * (double)op2_min ||
+ (double)t2 != (double)op1_min * (double)op2_min ||
+ (double)t3 != (double)op1_min * (double)op2_min ||
+ (double)t4 != (double)op1_min * (double)op2_min) {
+ tmp->underflow = 1;
+ tmp->overflow = 1;
+ tmp->min = ZEND_LONG_MIN;
+ tmp->max = ZEND_LONG_MAX;
+ } else {
+ tmp->min = MIN(MIN(t1, t2), MIN(t3, t4));
+ tmp->max = MAX(MAX(t1, t2), MAX(t3, t4));
+ }
+ return 1;
+ }
+ }
+ } else if ((opline+1)->opcode == ZEND_OP_DATA) {
+ if (ssa->ops[line+1].op1_def == var) {
+ if (OP1_HAS_RANGE()) {
+ opline++;
+ tmp->min = OP1_MIN_RANGE();
+ tmp->max = OP1_MAX_RANGE();
+ tmp->underflow = OP1_RANGE_UNDERFLOW();
+ tmp->overflow = OP1_RANGE_OVERFLOW();
+ return 1;
+ }
+ }
+ }
+ break;
+ case ZEND_ASSIGN_DIV:
+ if (opline->extended_value == 0) {
+ if (ssa->ops[line].op1_def == var || ssa->ops[line].result_def == var) {
+ if (OP1_HAS_RANGE() && OP2_HAS_RANGE()) {
+ op1_min = OP1_MIN_RANGE();
+ op2_min = OP2_MIN_RANGE();
+ op1_max = OP1_MAX_RANGE();
+ op2_max = OP2_MAX_RANGE();
+ if (op2_min <= 0 && op2_max >= 0) {
+ break;
+ }
+ t1 = op1_min / op2_min;
+ t2 = op1_min / op2_max;
+ t3 = op1_max / op2_min;
+ t4 = op1_max / op2_max;
+ // FIXME: more careful overflow checks?
+ if (OP1_RANGE_UNDERFLOW() ||
+ OP2_RANGE_UNDERFLOW() ||
+ OP1_RANGE_OVERFLOW() ||
+ OP2_RANGE_OVERFLOW() ||
+ t1 != (zend_long)((double)op1_min / (double)op2_min) ||
+ t2 != (zend_long)((double)op1_min / (double)op2_max) ||
+ t3 != (zend_long)((double)op1_max / (double)op2_min) ||
+ t4 != (zend_long)((double)op1_max / (double)op2_max)) {
+ tmp->underflow = 1;
+ tmp->overflow = 1;
+ tmp->min = ZEND_LONG_MIN;
+ tmp->max = ZEND_LONG_MAX;
+ } else {
+ tmp->min = MIN(MIN(t1, t2), MIN(t3, t4));
+ tmp->max = MAX(MAX(t1, t2), MAX(t3, t4));
+ }
+ return 1;
+ }
+ }
+ } else if ((opline+1)->opcode == ZEND_OP_DATA) {
+ if (ssa->ops[line+1].op1_def == var) {
+ if (OP1_HAS_RANGE()) {
+ opline++;
+ tmp->min = OP1_MIN_RANGE();
+ tmp->max = OP1_MAX_RANGE();
+ tmp->underflow = OP1_RANGE_UNDERFLOW();
+ tmp->overflow = OP1_RANGE_OVERFLOW();
+ return 1;
+ }
+ }
+ }
+ break;
+ case ZEND_ASSIGN_MOD:
+ if (opline->extended_value == 0) {
+ if (ssa->ops[line].op1_def == var || ssa->ops[line].result_def == var) {
+ if (OP1_HAS_RANGE() && OP2_HAS_RANGE()) {
+ if (OP1_RANGE_UNDERFLOW() ||
+ OP2_RANGE_UNDERFLOW() ||
+ OP1_RANGE_OVERFLOW() ||
+ OP2_RANGE_OVERFLOW()) {
+ tmp->min = ZEND_LONG_MIN;
+ tmp->max = ZEND_LONG_MAX;
+ } else {
+ op1_min = OP1_MIN_RANGE();
+ op2_min = OP2_MIN_RANGE();
+ op1_max = OP1_MAX_RANGE();
+ op2_max = OP2_MAX_RANGE();
+ if (op2_min == 0 || op2_max == 0) {
+ /* avoid division by zero */
+ break;
+ }
+ t1 = op1_min % op2_min;
+ t2 = op1_min % op2_max;
+ t3 = op1_max % op2_min;
+ t4 = op1_max % op2_max;
+ tmp->min = MIN(MIN(t1, t2), MIN(t3, t4));
+ tmp->max = MAX(MAX(t1, t2), MAX(t3, t4));
+ }
+ return 1;
+ }
+ }
+ } else if ((opline+1)->opcode == ZEND_OP_DATA) {
+ if (ssa->ops[line+1].op1_def == var) {
+ if (OP1_HAS_RANGE()) {
+ opline++;
+ tmp->min = OP1_MIN_RANGE();
+ tmp->max = OP1_MAX_RANGE();
+ tmp->underflow = OP1_RANGE_UNDERFLOW();
+ tmp->overflow = OP1_RANGE_OVERFLOW();
+ return 1;
+ }
+ }
+ }
+ break;
+ case ZEND_ASSIGN_SL:
+ if (opline->extended_value == 0) {
+ if (ssa->ops[line].op1_def == var || ssa->ops[line].result_def == var) {
+ if (OP1_HAS_RANGE() && OP2_HAS_RANGE()) {
+ if (OP1_RANGE_UNDERFLOW() ||
+ OP2_RANGE_UNDERFLOW() ||
+ OP1_RANGE_OVERFLOW() ||
+ OP2_RANGE_OVERFLOW()) {
+ tmp->min = ZEND_LONG_MIN;
+ tmp->max = ZEND_LONG_MAX;
+ } else {
+ op1_min = OP1_MIN_RANGE();
+ op2_min = OP2_MIN_RANGE();
+ op1_max = OP1_MAX_RANGE();
+ op2_max = OP2_MAX_RANGE();
+ t1 = op1_min << op2_min;
+ t2 = op1_min << op2_max;
+ t3 = op1_max << op2_min;
+ t4 = op1_max << op2_max;
+ tmp->min = MIN(MIN(t1, t2), MIN(t3, t4));
+ tmp->max = MAX(MAX(t1, t2), MAX(t3, t4));
+ }
+ return 1;
+ }
+ }
+ } else if ((opline+1)->opcode == ZEND_OP_DATA) {
+ if (ssa->ops[line+1].op1_def == var) {
+ if (OP1_HAS_RANGE()) {
+ opline++;
+ tmp->min = OP1_MIN_RANGE();
+ tmp->max = OP1_MAX_RANGE();
+ tmp->underflow = OP1_RANGE_UNDERFLOW();
+ tmp->overflow = OP1_RANGE_OVERFLOW();
+ return 1;
+ }
+ }
+ }
+ break;
+ case ZEND_ASSIGN_SR:
+ if (opline->extended_value == 0) {
+ if (ssa->ops[line].op1_def == var || ssa->ops[line].result_def == var) {
+ if (OP1_HAS_RANGE() && OP2_HAS_RANGE()) {
+ if (OP1_RANGE_UNDERFLOW() ||
+ OP2_RANGE_UNDERFLOW() ||
+ OP1_RANGE_OVERFLOW() ||
+ OP2_RANGE_OVERFLOW()) {
+ tmp->min = ZEND_LONG_MIN;
+ tmp->max = ZEND_LONG_MAX;
+ } else {
+ op1_min = OP1_MIN_RANGE();
+ op2_min = OP2_MIN_RANGE();
+ op1_max = OP1_MAX_RANGE();
+ op2_max = OP2_MAX_RANGE();
+ t1 = op1_min >> op2_min;
+ t2 = op1_min >> op2_max;
+ t3 = op1_max >> op2_min;
+ t4 = op1_max >> op2_max;
+ tmp->min = MIN(MIN(t1, t2), MIN(t3, t4));
+ tmp->max = MAX(MAX(t1, t2), MAX(t3, t4));
+ }
+ return 1;
+ }
+ }
+ } else if ((opline+1)->opcode == ZEND_OP_DATA) {
+ if (ssa->ops[line+1].op1_def == var) {
+ if (OP1_HAS_RANGE()) {
+ opline++;
+ tmp->min = OP1_MIN_RANGE();
+ tmp->max = OP1_MAX_RANGE();
+ tmp->underflow = OP1_RANGE_UNDERFLOW();
+ tmp->overflow = OP1_RANGE_OVERFLOW();
+ return 1;
+ }
+ }
+ }
+ break;
+ case ZEND_ASSIGN_BW_OR:
+ if (opline->extended_value == 0) {
+ if (ssa->ops[line].op1_def == var || ssa->ops[line].result_def == var) {
+ if (OP1_HAS_RANGE() && OP2_HAS_RANGE()) {
+ if (OP1_RANGE_UNDERFLOW() ||
+ OP2_RANGE_UNDERFLOW() ||
+ OP1_RANGE_OVERFLOW() ||
+ OP2_RANGE_OVERFLOW()) {
+ tmp->min = ZEND_LONG_MIN;
+ tmp->max = ZEND_LONG_MAX;
+ } else {
+ op1_min = OP1_MIN_RANGE();
+ op2_min = OP2_MIN_RANGE();
+ op1_max = OP1_MAX_RANGE();
+ op2_max = OP2_MAX_RANGE();
+ zend_ssa_range_or(op1_min, op1_max, op2_min, op2_max, tmp);
+ }
+ return 1;
+ }
+ }
+ } else if ((opline+1)->opcode == ZEND_OP_DATA) {
+ if (ssa->ops[line+1].op1_def == var) {
+ if (OP1_HAS_RANGE()) {
+ opline++;
+ tmp->min = OP1_MIN_RANGE();
+ tmp->max = OP1_MAX_RANGE();
+ tmp->underflow = OP1_RANGE_UNDERFLOW();
+ tmp->overflow = OP1_RANGE_OVERFLOW();
+ return 1;
+ }
+ }
+ }
+ break;
+ case ZEND_ASSIGN_BW_AND:
+ if (opline->extended_value == 0) {
+ if (ssa->ops[line].op1_def == var || ssa->ops[line].result_def == var) {
+ if (OP1_HAS_RANGE() && OP2_HAS_RANGE()) {
+ if (OP1_RANGE_UNDERFLOW() ||
+ OP2_RANGE_UNDERFLOW() ||
+ OP1_RANGE_OVERFLOW() ||
+ OP2_RANGE_OVERFLOW()) {
+ tmp->min = ZEND_LONG_MIN;
+ tmp->max = ZEND_LONG_MAX;
+ } else {
+ op1_min = OP1_MIN_RANGE();
+ op2_min = OP2_MIN_RANGE();
+ op1_max = OP1_MAX_RANGE();
+ op2_max = OP2_MAX_RANGE();
+ zend_ssa_range_and(op1_min, op1_max, op2_min, op2_max, tmp);
+ }
+ return 1;
+ }
+ }
+ } else if ((opline+1)->opcode == ZEND_OP_DATA) {
+ if (ssa->ops[line+1].op1_def == var) {
+ if (OP1_HAS_RANGE()) {
+ opline++;
+ tmp->min = OP1_MIN_RANGE();
+ tmp->max = OP1_MAX_RANGE();
+ tmp->underflow = OP1_RANGE_UNDERFLOW();
+ tmp->overflow = OP1_RANGE_OVERFLOW();
+ return 1;
+ }
+ }
+ }
+ break;
+// case ZEND_ASSIGN_BW_XOR:
+// case ZEND_ASSIGN_CONCAT:
+ case ZEND_OP_DATA:
+ if ((opline-1)->opcode == ZEND_ASSIGN_DIM ||
+ (opline-1)->opcode == ZEND_ASSIGN_OBJ ||
+ (opline-1)->opcode == ZEND_ASSIGN_ADD ||
+ (opline-1)->opcode == ZEND_ASSIGN_SUB ||
+ (opline-1)->opcode == ZEND_ASSIGN_MUL) {
+ if (ssa->ops[line].op1_def == var) {
+ if (OP1_HAS_RANGE()) {
+ tmp->min = OP1_MIN_RANGE();
+ tmp->max = OP1_MAX_RANGE();
+ tmp->underflow = OP1_RANGE_UNDERFLOW();
+ tmp->overflow = OP1_RANGE_OVERFLOW();
+ return 1;
+ }
+ }
+ break;
+ }
+ break;
+ case ZEND_RECV:
+ case ZEND_RECV_INIT:
+ if (ssa->ops[line].result_def == var) {
+ zend_func_info *func_info = ZEND_FUNC_INFO(op_array);
+
+ if (func_info &&
+ (int)opline->op1.num-1 < func_info->num_args &&
+ func_info->arg_info[opline->op1.num-1].info.has_range) {
+ *tmp = func_info->arg_info[opline->op1.num-1].info.range;
+ return 1;
+ } else if (op_array->arg_info &&
+ opline->op1.num <= op_array->num_args) {
+ if (op_array->arg_info[opline->op1.num-1].type_hint == IS_LONG) {
+ tmp->underflow = 0;
+ tmp->min = ZEND_LONG_MIN;
+ tmp->max = ZEND_LONG_MAX;
+ tmp->overflow = 0;
+ return 1;
+ } else if (op_array->arg_info[opline->op1.num-1].type_hint == _IS_BOOL) {
+ tmp->underflow = 0;
+ tmp->min = 0;
+ tmp->max = 1;
+ tmp->overflow = 0;
+ return 1;
+ }
+ }
+ }
+ break;
+ case ZEND_DO_FCALL:
+ case ZEND_DO_ICALL:
+ case ZEND_DO_UCALL:
+ case ZEND_DO_FCALL_BY_NAME:
+ if (ssa->ops[line].result_def == var && ZEND_FUNC_INFO(op_array)) {
+ zend_func_info *func_info = ZEND_FUNC_INFO(op_array);
+ zend_call_info *call_info = func_info->callee_info;
+
+ while (call_info && call_info->caller_call_opline != opline) {
+ call_info = call_info->next_callee;
+ }
+ if (call_info) {
+ if (call_info->callee_func->type == ZEND_USER_FUNCTION) {
+ func_info = ZEND_FUNC_INFO(&call_info->callee_func->op_array);
+ if (func_info && func_info->return_info.has_range) {
+ *tmp = func_info->return_info.range;
+ return 1;
+ }
+ }
+//TODO: we can't use type inference for internal functions at this point ???
+#if 0
+ uint32_t type;
+
+ type = zend_get_func_info(call_info, ssa);
+ if (!(type & (MAY_BE_ANY - (MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG)))) {
+ tmp->underflow = 0;
+ tmp->min = 0;
+ tmp->max = 0;
+ tmp->overflow = 0;
+ if (type & MAY_BE_LONG) {
+ tmp->min = ZEND_LONG_MIN;
+ tmp->max = ZEND_LONG_MAX;
+ } else if (type & MAY_BE_TRUE) {
+ if (!(type & (MAY_BE_NULL|MAY_BE_FALSE))) {
+ tmp->min = 1;
+ }
+ tmp->max = 1;
+ }
+ return 1;
+ }
+#endif
+ }
+ }
+ break;
+ // FIXME: support for more opcodes
+ default:
+ break;
+ }
+ return 0;
+}
+
+void zend_inference_init_range(const zend_op_array *op_array, zend_ssa *ssa, int var, zend_bool underflow, zend_long min, zend_long max, zend_bool overflow)
+{
+ if (underflow) {
+ min = ZEND_LONG_MIN;
+ }
+ if (overflow) {
+ max = ZEND_LONG_MAX;
+ }
+ ssa->var_info[var].has_range = 1;
+ ssa->var_info[var].range.underflow = underflow;
+ ssa->var_info[var].range.min = min;
+ ssa->var_info[var].range.max = max;
+ ssa->var_info[var].range.overflow = overflow;
+#ifdef LOG_SSA_RANGE
+ fprintf(stderr, " change range (init SCC %2d) %2d [%s%ld..%ld%s]\n", ssa->vars[var].scc, var, (underflow?"-- ":""), min, max, (overflow?" ++":""));
+#endif
+}
+
+int zend_inference_widening_meet(zend_ssa_var_info *var_info, zend_ssa_range *r)
+{
+ if (!var_info->has_range) {
+ var_info->has_range = 1;
+ } else {
+ if (r->underflow ||
+ var_info->range.underflow ||
+ r->min < var_info->range.min) {
+ r->underflow = 1;
+ r->min = ZEND_LONG_MIN;
+ }
+ if (r->overflow ||
+ var_info->range.overflow ||
+ r->max > var_info->range.max) {
+ r->overflow = 1;
+ r->max = ZEND_LONG_MAX;
+ }
+ if (var_info->range.min == r->min &&
+ var_info->range.max == r->max &&
+ var_info->range.underflow == r->underflow &&
+ var_info->range.overflow == r->overflow) {
+ return 0;
+ }
+ }
+ var_info->range = *r;
+ return 1;
+}
+
+static int zend_ssa_range_widening(const zend_op_array *op_array, zend_ssa *ssa, int var, int scc)
+{
+ zend_ssa_range tmp;
+
+ if (zend_inference_calc_range(op_array, ssa, var, 1, 0, &tmp)) {
+ if (zend_inference_widening_meet(&ssa->var_info[var], &tmp)) {
+#ifdef LOG_SSA_RANGE
+ fprintf(stderr, " change range (widening SCC %2d) %2d [%s%ld..%ld%s]\n", scc, var, (tmp.underflow?"-- ":""), tmp.min, tmp.max, (tmp.overflow?" ++":""));
+#endif
+ return 1;
+ }
+ }
+ return 0;
+}
+
+int zend_inference_narrowing_meet(zend_ssa_var_info *var_info, zend_ssa_range *r)
+{
+ if (!var_info->has_range) {
+ var_info->has_range = 1;
+ } else {
+ if (!r->underflow &&
+ !var_info->range.underflow &&
+ var_info->range.min < r->min) {
+ r->min = var_info->range.min;
+ }
+ if (!r->overflow &&
+ !var_info->range.overflow &&
+ var_info->range.max > r->max) {
+ r->max = var_info->range.max;
+ }
+ if (r->underflow) {
+ r->min = ZEND_LONG_MIN;
+ }
+ if (r->overflow) {
+ r->max = ZEND_LONG_MAX;
+ }
+ if (var_info->range.min == r->min &&
+ var_info->range.max == r->max &&
+ var_info->range.underflow == r->underflow &&
+ var_info->range.overflow == r->overflow) {
+ return 0;
+ }
+ }
+ var_info->range = *r;
+ return 1;
+}
+
+static int zend_ssa_range_narrowing(const zend_op_array *op_array, zend_ssa *ssa, int var, int scc)
+{
+ zend_ssa_range tmp;
+
+ if (zend_inference_calc_range(op_array, ssa, var, 0, 1, &tmp)) {
+ if (zend_inference_narrowing_meet(&ssa->var_info[var], &tmp)) {
+#ifdef LOG_SSA_RANGE
+ fprintf(stderr, " change range (narrowing SCC %2d) %2d [%s%ld..%ld%s]\n", scc, var, (tmp.underflow?"-- ":""), tmp.min, tmp.max, (tmp.overflow?" ++":""));
+#endif
+ return 1;
+ }
+ }
+ return 0;
+}
+
+#ifdef NEG_RANGE
+# define CHECK_INNER_CYCLE(var2) \
+ do { \
+ if (ssa->vars[var2].scc == ssa->vars[var].scc && \
+ !ssa->vars[var2].scc_entry && \
+ !zend_bitset_in(visited, var2) && \
+ zend_check_inner_cycles(op_array, ssa, worklist, visited, var2)) { \
+ return 1; \
+ } \
+ } while (0)
+
+static int zend_check_inner_cycles(const zend_op_array *op_array, zend_ssa *ssa, zend_bitset worklist, zend_bitset visited, int var)
+{
+ if (zend_bitset_in(worklist, var)) {
+ return 1;
+ }
+ zend_bitset_incl(worklist, var);
+ FOR_EACH_VAR_USAGE(var, CHECK_INNER_CYCLE);
+ zend_bitset_incl(visited, var);
+ return 0;
+}
+#endif
+
+static void zend_infer_ranges_warmup(const zend_op_array *op_array, zend_ssa *ssa, int *scc_var, int *next_scc_var, int scc)
+{
+ int worklist_len = zend_bitset_len(ssa->vars_count);
+ zend_bitset worklist;
+ zend_bitset visited;
+ int j, n;
+ zend_ssa_range tmp;
+#ifdef NEG_RANGE
+ int has_inner_cycles = 0;
+ ALLOCA_FLAG(use_heap);
+
+ worklist = do_alloca(sizeof(zend_ulong) * worklist_len * 2, use_heap);
+ visited = worklist + worklist_len;
+ memset(worklist, 0, sizeof(zend_ulong) * worklist_len);
+ memset(visited, 0, sizeof(zend_ulong) * worklist_len);
+ j= scc_var[scc];
+ while (j >= 0) {
+ if (!zend_bitset_in(visited, j) &&
+ zend_check_inner_cycles(op_array, ssa, worklist, visited, j)) {
+ has_inner_cycles = 1;
+ break;
+ }
+ j = next_scc_var[j];
+ }
+#endif
+
+ memset(worklist, 0, sizeof(zend_ulong) * worklist_len);
+
+ for (n = 0; n < RANGE_WARMAP_PASSES; n++) {
+ j= scc_var[scc];
+ while (j >= 0) {
+ if (ssa->vars[j].scc_entry) {
+ zend_bitset_incl(worklist, j);
+ }
+ j = next_scc_var[j];
+ }
+
+ memset(visited, 0, sizeof(zend_ulong) * worklist_len);
+
+ while (!zend_bitset_empty(worklist, worklist_len)) {
+ j = zend_bitset_first(worklist, worklist_len);
+ zend_bitset_excl(worklist, j);
+ if (zend_inference_calc_range(op_array, ssa, j, 0, 0, &tmp)) {
+#ifdef NEG_RANGE
+ if (!has_inner_cycles &&
+ ssa->var_info[j].has_range &&
+ ssa->vars[j].definition_phi &&
+ ssa->vars[j].definition_phi->pi >= 0 &&
+ ssa->vars[j].definition_phi->has_range_constraint &&
+ ssa->vars[j].definition_phi->constraint.range.negative &&
+ ssa->vars[j].definition_phi->constraint.range.min_ssa_var < 0 &&
+ ssa->vars[j].definition_phi->constraint.range.min_ssa_var < 0) {
+ zend_ssa_range_constraint *constraint =
+ &ssa->vars[j].definition_phi->constraint.range;
+ if (tmp.min == ssa->var_info[j].range.min &&
+ tmp.max == ssa->var_info[j].range.max) {
+ if (constraint->negative == NEG_INIT) {
+#ifdef LOG_NEG_RANGE
+ fprintf(stderr, "#%d INVARIANT\n", j);
+#endif
+ constraint->negative = NEG_INVARIANT;
+ }
+ } else if (tmp.min == ssa->var_info[j].range.min &&
+ tmp.max == ssa->var_info[j].range.max + 1 &&
+ tmp.max < constraint->range.min) {
+ if (constraint->negative == NEG_INIT ||
+ constraint->negative == NEG_INVARIANT) {
+#ifdef LOG_NEG_RANGE
+ fprintf(stderr, "#%d LT\n", j);
+#endif
+ constraint->negative = NEG_USE_LT;
+//???NEG
+ } else if (constraint->negative == NEG_USE_GT) {
+#ifdef LOG_NEG_RANGE
+ fprintf(stderr, "#%d UNKNOWN\n", j);
+#endif
+ constraint->negative = NEG_UNKNOWN;
+ }
+ } else if (tmp.max == ssa->var_info[j].range.max &&
+ tmp.min == ssa->var_info[j].range.min - 1 &&
+ tmp.min > constraint->range.max) {
+ if (constraint->negative == NEG_INIT ||
+ constraint->negative == NEG_INVARIANT) {
+#ifdef LOG_NEG_RANGE
+ fprintf(stderr, "#%d GT\n", j);
+#endif
+ constraint->negative = NEG_USE_GT;
+//???NEG
+ } else if (constraint->negative == NEG_USE_LT) {
+#ifdef LOG_NEG_RANGE
+ fprintf(stderr, "#%d UNKNOWN\n", j);
+#endif
+ constraint->negative = NEG_UNKNOWN;
+ }
+ } else {
+#ifdef LOG_NEG_RANGE
+ fprintf(stderr, "#%d UNKNOWN\n", j);
+#endif
+ constraint->negative = NEG_UNKNOWN;
+ }
+ }
+#endif
+ if (zend_inference_narrowing_meet(&ssa->var_info[j], &tmp)) {
+#ifdef LOG_SSA_RANGE
+ fprintf(stderr, " change range (warmup %d SCC %2d) %2d [%s%ld..%ld%s]\n", n, scc, j, (tmp.underflow?"-- ":""), tmp.min, tmp.max, (tmp.overflow?" ++":""));
+#endif
+ zend_bitset_incl(visited, j);
+ FOR_EACH_VAR_USAGE(j, ADD_SCC_VAR_1);
+ }
+ }
+ }
+ }
+ free_alloca(worklist, use_heap);
+}
+
+static int zend_infer_ranges(const zend_op_array *op_array, zend_ssa *ssa) /* {{{ */
+{
+ int worklist_len = zend_bitset_len(ssa->vars_count);
+ zend_bitset worklist;
+ int *next_scc_var;
+ int *scc_var;
+ zend_ssa_phi *p;
+ zend_ssa_range tmp;
+ int scc, j;
+ ALLOCA_FLAG(use_heap);
+
+ worklist = do_alloca(
+ sizeof(zend_ulong) * worklist_len +
+ sizeof(int) * ssa->vars_count +
+ sizeof(int) * ssa->sccs, use_heap);
+ next_scc_var = (int*)(worklist + worklist_len);
+ scc_var = next_scc_var + ssa->vars_count;
+
+#ifdef LOG_SSA_RANGE
+ fprintf(stderr, "Range Inference\n");
+#endif
+
+ /* Create linked lists of SSA variables for each SCC */
+ memset(scc_var, -1, sizeof(int) * ssa->sccs);
+ for (j = 0; j < ssa->vars_count; j++) {
+ if (ssa->vars[j].scc >= 0) {
+ next_scc_var[j] = scc_var[ssa->vars[j].scc];
+ scc_var[ssa->vars[j].scc] = j;
+ }
+ }
+
+ for (scc = 0; scc < ssa->sccs; scc++) {
+ j = scc_var[scc];
+ if (next_scc_var[j] < 0) {
+ /* SCC with a single element */
+ if (zend_inference_calc_range(op_array, ssa, j, 0, 1, &tmp)) {
+ zend_inference_init_range(op_array, ssa, j, tmp.underflow, tmp.min, tmp.max, tmp.overflow);
+ } else {
+ zend_inference_init_range(op_array, ssa, j, 1, ZEND_LONG_MIN, ZEND_LONG_MAX, 1);
+ }
+ } else {
+ /* Find SCC entry points */
+ memset(worklist, 0, sizeof(zend_ulong) * worklist_len);
+ do {
+ if (ssa->vars[j].scc_entry) {
+ zend_bitset_incl(worklist, j);
+ }
+ j = next_scc_var[j];
+ } while (j >= 0);
+
+#if RANGE_WARMAP_PASSES > 0
+ zend_infer_ranges_warmup(op_array, ssa, scc_var, next_scc_var, scc);
+ j = scc_var[scc];
+ do {
+ zend_bitset_incl(worklist, j);
+ j = next_scc_var[j];
+ } while (j >= 0);
+#endif
+
+ /* widening */
+ while (!zend_bitset_empty(worklist, worklist_len)) {
+ j = zend_bitset_first(worklist, worklist_len);
+ zend_bitset_excl(worklist, j);
+ if (zend_ssa_range_widening(op_array, ssa, j, scc)) {
+ FOR_EACH_VAR_USAGE(j, ADD_SCC_VAR);
+ }
+ }
+
+ /* Add all SCC entry variables into worklist for narrowing */
+ for (j = scc_var[scc]; j >= 0; j = next_scc_var[j]) {
+ if (!ssa->var_info[j].has_range) {
+ zend_inference_init_range(op_array, ssa, j, 1, ZEND_LONG_MIN, ZEND_LONG_MAX, 1);
+ }
+ zend_bitset_incl(worklist, j);
+ }
+
+ /* narrowing */
+ while (!zend_bitset_empty(worklist, worklist_len)) {
+ j = zend_bitset_first(worklist, worklist_len);
+ zend_bitset_excl(worklist, j);
+ if (zend_ssa_range_narrowing(op_array, ssa, j, scc)) {
+ FOR_EACH_VAR_USAGE(j, ADD_SCC_VAR);
+#ifdef SYM_RANGE
+ /* Process symbolic control-flow constraints */
+ p = ssa->vars[j].sym_use_chain;
+ while (p) {
+ ADD_SCC_VAR(p->ssa_var);
+ p = p->sym_use_chain;
+ }
+#endif
+ }
+ }
+ }
+ }
+
+ free_alloca(worklist, use_heap);
+
+ return SUCCESS;
+}
+/* }}} */
+
+#define UPDATE_SSA_TYPE(_type, _var) \
+ do { \
+ uint32_t __type = (_type); \
+ int __var = (_var); \
+ if (__type & MAY_BE_REF) { \
+ __type |= MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF; \
+ } \
+ if (__var >= 0) { \
+ if (ssa_vars[__var].var < op_array->last_var) { \
+ if (__type & (MAY_BE_REF|MAY_BE_RCN)) { \
+ __type |= MAY_BE_RC1 | MAY_BE_RCN; \
+ } \
+ } \
+ if (ssa_var_info[__var].type != __type) { \
+ check_type_narrowing(op_array, ssa, worklist, \
+ __var, ssa_var_info[__var].type, __type); \
+ ssa_var_info[__var].type = __type; \
+ add_usages(op_array, ssa, worklist, __var); \
+ } \
+ /*zend_bitset_excl(worklist, var);*/ \
+ } \
+ } while (0)
+
+#define UPDATE_SSA_OBJ_TYPE(_ce, _is_instanceof, var) \
+ do { \
+ if (var >= 0) { \
+ if (ssa_var_info[var].ce != (_ce) || \
+ ssa_var_info[var].is_instanceof != (_is_instanceof)) { \
+ ssa_var_info[var].ce = (_ce); \
+ ssa_var_info[var].is_instanceof = (_is_instanceof); \
+ add_usages(op_array, ssa, worklist, var); \
+ } \
+ /*zend_bitset_excl(worklist, var);*/ \
+ } \
+ } while (0)
+
+#define COPY_SSA_OBJ_TYPE(from_var, to_var) do { \
+ if ((from_var) >= 0 && (ssa_var_info[(from_var)].type & MAY_BE_OBJECT) \
+ && ssa_var_info[(from_var)].ce) { \
+ UPDATE_SSA_OBJ_TYPE(ssa_var_info[(from_var)].ce, \
+ ssa_var_info[(from_var)].is_instanceof, (to_var)); \
+ } else { \
+ UPDATE_SSA_OBJ_TYPE(NULL, 0, (to_var)); \
+ } \
+} while (0)
+
+static void add_usages(const zend_op_array *op_array, zend_ssa *ssa, zend_bitset worklist, int var)
+{
+ if (ssa->vars[var].phi_use_chain) {
+ zend_ssa_phi *p = ssa->vars[var].phi_use_chain;
+ do {
+ zend_bitset_incl(worklist, p->ssa_var);
+ p = zend_ssa_next_use_phi(ssa, var, p);
+ } while (p);
+ }
+ if (ssa->vars[var].use_chain >= 0) {
+ int use = ssa->vars[var].use_chain;
+ zend_ssa_op *op;
+
+ do {
+ op = ssa->ops + use;
+ if (op->result_def >= 0) {
+ zend_bitset_incl(worklist, op->result_def);
+ }
+ if (op->op1_def >= 0) {
+ zend_bitset_incl(worklist, op->op1_def);
+ }
+ if (op->op2_def >= 0) {
+ zend_bitset_incl(worklist, op->op2_def);
+ }
+ if (op_array->opcodes[use].opcode == ZEND_OP_DATA) {
+ op--;
+ if (op->result_def >= 0) {
+ zend_bitset_incl(worklist, op->result_def);
+ }
+ if (op->op1_def >= 0) {
+ zend_bitset_incl(worklist, op->op1_def);
+ }
+ if (op->op2_def >= 0) {
+ zend_bitset_incl(worklist, op->op2_def);
+ }
+ }
+ use = zend_ssa_next_use(ssa->ops, var, use);
+ } while (use >= 0);
+ }
+}
+
+static void reset_dependent_vars(const zend_op_array *op_array, zend_ssa *ssa, zend_bitset worklist, int var)
+{
+ zend_ssa_op *ssa_ops = ssa->ops;
+ zend_ssa_var *ssa_vars = ssa->vars;
+ zend_ssa_var_info *ssa_var_info = ssa->var_info;
+ zend_ssa_phi *p;
+ int use;
+
+ p = ssa_vars[var].phi_use_chain;
+ while (p) {
+ if (ssa_var_info[p->ssa_var].type) {
+ ssa_var_info[p->ssa_var].type = 0;
+ zend_bitset_incl(worklist, p->ssa_var);
+ reset_dependent_vars(op_array, ssa, worklist, p->ssa_var);
+ }
+ p = zend_ssa_next_use_phi(ssa, var, p);
+ }
+ use = ssa_vars[var].use_chain;
+ while (use >= 0) {
+ if (ssa_ops[use].op1_def >= 0 && ssa_var_info[ssa_ops[use].op1_def].type) {
+ ssa_var_info[ssa_ops[use].op1_def].type = 0;
+ zend_bitset_incl(worklist, ssa_ops[use].op1_def);
+ reset_dependent_vars(op_array, ssa, worklist, ssa_ops[use].op1_def);
+ }
+ if (ssa_ops[use].op2_def >= 0 && ssa_var_info[ssa_ops[use].op2_def].type) {
+ ssa_var_info[ssa_ops[use].op2_def].type = 0;
+ zend_bitset_incl(worklist, ssa_ops[use].op2_def);
+ reset_dependent_vars(op_array, ssa, worklist, ssa_ops[use].op2_def);
+ }
+ if (ssa_ops[use].result_def >= 0 && ssa_var_info[ssa_ops[use].result_def].type) {
+ ssa_var_info[ssa_ops[use].result_def].type = 0;
+ zend_bitset_incl(worklist, ssa_ops[use].result_def);
+ reset_dependent_vars(op_array, ssa, worklist, ssa_ops[use].result_def);
+ }
+ if (op_array->opcodes[use+1].opcode == ZEND_OP_DATA) {
+ if (ssa_ops[use+1].op1_def >= 0 && ssa_var_info[ssa_ops[use+1].op1_def].type) {
+ ssa_var_info[ssa_ops[use+1].op1_def].type = 0;
+ zend_bitset_incl(worklist, ssa_ops[use+1].op1_def);
+ reset_dependent_vars(op_array, ssa, worklist, ssa_ops[use+1].op1_def);
+ }
+ if (ssa_ops[use+1].op2_def >= 0 && ssa_var_info[ssa_ops[use+1].op2_def].type) {
+ ssa_var_info[ssa_ops[use+1].op2_def].type = 0;
+ zend_bitset_incl(worklist, ssa_ops[use+1].op2_def);
+ reset_dependent_vars(op_array, ssa, worklist, ssa_ops[use+1].op2_def);
+ }
+ if (ssa_ops[use+1].result_def >= 0 && ssa_var_info[ssa_ops[use+1].result_def].type) {
+ ssa_var_info[ssa_ops[use+1].result_def].type = 0;
+ zend_bitset_incl(worklist, ssa_ops[use+1].result_def);
+ reset_dependent_vars(op_array, ssa, worklist, ssa_ops[use+1].result_def);
+ }
+ }
+ use = zend_ssa_next_use(ssa_ops, var, use);
+ }
+#ifdef SYM_RANGE
+ /* Process symbolic control-flow constraints */
+ p = ssa->vars[var].sym_use_chain;
+ while (p) {
+ ssa_var_info[p->ssa_var].type = 0;
+ zend_bitset_incl(worklist, p->ssa_var);
+ reset_dependent_vars(op_array, ssa, worklist, p->ssa_var);
+ p = p->sym_use_chain;
+ }
+#endif
+}
+
+static void check_type_narrowing(const zend_op_array *op_array, zend_ssa *ssa, zend_bitset worklist, int var, uint32_t old_type, uint32_t new_type)
+{
+ /* if new_type set resets some bits from old_type set
+ * We have completely recalculate types of some dependent SSA variables
+ * (this may occurs mainly because of incremental inter-precudure
+ * type inference)
+ */
+ if (old_type & ~new_type) {
+ reset_dependent_vars(op_array, ssa, worklist, var);
+ }
+}
+
+uint32_t zend_array_element_type(uint32_t t1, int write, int insert)
+{
+ uint32_t tmp = 0;
+
+ if (t1 & MAY_BE_OBJECT) {
+ tmp |= MAY_BE_ANY | MAY_BE_REF | MAY_BE_RC1 | MAY_BE_RCN | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF;
+ }
+ if (t1 & MAY_BE_ARRAY) {
+ if (insert) {
+ tmp |= MAY_BE_NULL | MAY_BE_RCN;
+ } else {
+ tmp |= MAY_BE_NULL | ((t1 & MAY_BE_ARRAY_OF_ANY) >> MAY_BE_ARRAY_SHIFT);
+ if (tmp & MAY_BE_ARRAY) {
+ tmp |= MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF;
+ }
+ if (t1 & MAY_BE_ARRAY_OF_REF) {
+ tmp |= MAY_BE_REF | MAY_BE_RC1 | MAY_BE_RCN;
+ } else {
+ tmp |= MAY_BE_RC1 | MAY_BE_RCN;
+ }
+ }
+ }
+ if (t1 & MAY_BE_STRING) {
+ tmp |= MAY_BE_STRING | MAY_BE_RC1;
+ if (write) {
+ tmp |= MAY_BE_NULL;
+ }
+ }
+ if (t1 & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE)) {
+ tmp |= MAY_BE_NULL | MAY_BE_RCN;
+ if (t1 & MAY_BE_ERROR) {
+ if (write) {
+ tmp |= MAY_BE_ERROR;
+ }
+ }
+ }
+ if (t1 & (MAY_BE_TRUE|MAY_BE_LONG|MAY_BE_DOUBLE|MAY_BE_RESOURCE)) {
+ tmp |= MAY_BE_NULL | MAY_BE_RCN;
+ if (write) {
+ tmp |= MAY_BE_ERROR;
+ }
+ }
+ return tmp;
+}
+
+static uint32_t assign_dim_result_type(
+ uint32_t arr_type, uint32_t dim_type, uint32_t value_type, zend_uchar dim_op_type) {
+ uint32_t tmp = arr_type
+ & (MAY_BE_ANY|MAY_BE_REF|MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF)
+ & ~(MAY_BE_NULL|MAY_BE_FALSE);
+ if (arr_type & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_STRING)) {
+ tmp |= MAY_BE_ARRAY;
+ }
+ if (arr_type & (MAY_BE_RC1|MAY_BE_RCN)) {
+ tmp |= MAY_BE_RC1;
+ }
+ if (tmp & MAY_BE_ARRAY) {
+ tmp |= (value_type & MAY_BE_ANY) << MAY_BE_ARRAY_SHIFT;
+ if (value_type & MAY_BE_UNDEF) {
+ tmp |= MAY_BE_ARRAY_OF_NULL;
+ }
+ if (dim_op_type == IS_UNUSED) {
+ tmp |= MAY_BE_ARRAY_KEY_LONG;
+ } else {
+ if (dim_type & (MAY_BE_LONG|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_RESOURCE|MAY_BE_DOUBLE)) {
+ tmp |= MAY_BE_ARRAY_KEY_LONG;
+ }
+ if (dim_type & MAY_BE_STRING) {
+ tmp |= MAY_BE_ARRAY_KEY_STRING;
+ if (dim_op_type != IS_CONST) {
+ // FIXME: numeric string
+ tmp |= MAY_BE_ARRAY_KEY_LONG;
+ }
+ }
+ if (dim_type & (MAY_BE_UNDEF|MAY_BE_NULL)) {
+ tmp |= MAY_BE_ARRAY_KEY_STRING;
+ }
+ }
+ }
+ return tmp;
+}
+
+/* For binary ops that have compound assignment operators */
+static uint32_t binary_op_result_type(
+ zend_ssa *ssa, zend_uchar opcode, uint32_t t1, uint32_t t2, uint32_t result_var) {
+ uint32_t tmp;
+ uint32_t t1_type = (t1 & MAY_BE_ANY) | (t1 & MAY_BE_UNDEF ? MAY_BE_NULL : 0);
+ uint32_t t2_type = (t2 & MAY_BE_ANY) | (t2 & MAY_BE_UNDEF ? MAY_BE_NULL : 0);
+ switch (opcode) {
+ case ZEND_ADD:
+ tmp = MAY_BE_RC1;
+ if (t1_type == MAY_BE_LONG && t2_type == MAY_BE_LONG) {
+ if (!ssa->var_info[result_var].has_range ||
+ ssa->var_info[result_var].range.underflow ||
+ ssa->var_info[result_var].range.overflow) {
+ /* may overflow */
+ tmp |= MAY_BE_LONG | MAY_BE_DOUBLE;
+ } else {
+ tmp |= MAY_BE_LONG;
+ }
+ } else if (t1_type == MAY_BE_DOUBLE || t2_type == MAY_BE_DOUBLE) {
+ tmp |= MAY_BE_DOUBLE;
+ } else if (t1_type == MAY_BE_ARRAY && t2_type == MAY_BE_ARRAY) {
+ tmp |= MAY_BE_ARRAY;
+ tmp |= t1 & (MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF);
+ tmp |= t2 & (MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF);
+ } else {
+ tmp |= MAY_BE_LONG | MAY_BE_DOUBLE;
+ if ((t1_type & MAY_BE_ARRAY) && (t2_type & MAY_BE_ARRAY)) {
+ tmp |= MAY_BE_ARRAY;
+ tmp |= t1 & (MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF);
+ tmp |= t2 & (MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF);
+ }
+ }
+ break;
+ case ZEND_SUB:
+ case ZEND_MUL:
+ tmp = MAY_BE_RC1;
+ if (t1_type == MAY_BE_LONG && t2_type == MAY_BE_LONG) {
+ if (!ssa->var_info[result_var].has_range ||
+ ssa->var_info[result_var].range.underflow ||
+ ssa->var_info[result_var].range.overflow) {
+ /* may overflow */
+ tmp |= MAY_BE_LONG | MAY_BE_DOUBLE;
+ } else {
+ tmp |= MAY_BE_LONG;
+ }
+ } else if (t1_type == MAY_BE_DOUBLE || t2_type == MAY_BE_DOUBLE) {
+ tmp |= MAY_BE_DOUBLE;
+ } else {
+ tmp |= MAY_BE_LONG | MAY_BE_DOUBLE;
+ }
+ break;
+ case ZEND_DIV:
+ case ZEND_POW:
+ tmp = MAY_BE_RC1;
+ if (t1_type == MAY_BE_DOUBLE || t2_type == MAY_BE_DOUBLE) {
+ tmp |= MAY_BE_DOUBLE;
+ } else {
+ tmp |= MAY_BE_LONG | MAY_BE_DOUBLE;
+ }
+ /* Division by zero results in Inf/-Inf/Nan (double), so it doesn't need any special
+ * handling */
+ break;
+ case ZEND_MOD:
+ tmp = MAY_BE_RC1 | MAY_BE_LONG;
+ /* Division by zero results in an exception, so it doesn't need any special handling */
+ break;
+ case ZEND_BW_OR:
+ case ZEND_BW_AND:
+ case ZEND_BW_XOR:
+ tmp = MAY_BE_RC1;
+ if ((t1_type & MAY_BE_STRING) && (t2_type & MAY_BE_STRING)) {
+ tmp |= MAY_BE_STRING;
+ }
+ if ((t1_type & ~MAY_BE_STRING) || (t2_type & ~MAY_BE_STRING)) {
+ tmp |= MAY_BE_LONG;
+ }
+ break;
+ case ZEND_SL:
+ case ZEND_SR:
+ tmp = MAY_BE_RC1|MAY_BE_LONG;
+ break;
+ case ZEND_CONCAT:
+ /* TODO: +MAY_BE_OBJECT ??? */
+ tmp = MAY_BE_RC1|MAY_BE_STRING;
+ break;
+ EMPTY_SWITCH_DEFAULT_CASE()
+ }
+ return tmp;
+}
+
+/* Get the normal op corresponding to a compound assignment op */
+static inline zend_uchar get_compound_assign_op(zend_uchar opcode) {
+ switch (opcode) {
+ case ZEND_ASSIGN_ADD: return ZEND_ADD;
+ case ZEND_ASSIGN_SUB: return ZEND_SUB;
+ case ZEND_ASSIGN_MUL: return ZEND_MUL;
+ case ZEND_ASSIGN_DIV: return ZEND_DIV;
+ case ZEND_ASSIGN_MOD: return ZEND_MOD;
+ case ZEND_ASSIGN_SL: return ZEND_SL;
+ case ZEND_ASSIGN_SR: return ZEND_SR;
+ case ZEND_ASSIGN_CONCAT: return ZEND_CONCAT;
+ case ZEND_ASSIGN_BW_OR: return ZEND_BW_OR;
+ case ZEND_ASSIGN_BW_AND: return ZEND_BW_AND;
+ case ZEND_ASSIGN_BW_XOR: return ZEND_BW_XOR;
+ case ZEND_ASSIGN_POW: return ZEND_POW;
+ EMPTY_SWITCH_DEFAULT_CASE()
+ }
+}
+
+static inline zend_class_entry *get_class_entry(const zend_script *script, zend_string *lcname) {
+ zend_class_entry *ce = zend_hash_find_ptr(&script->class_table, lcname);
+ if (ce) {
+ return ce;
+ }
+
+ ce = zend_hash_find_ptr(CG(class_table), lcname);
+ if (ce && ce->type == ZEND_INTERNAL_CLASS) {
+ return ce;
+ }
+
+ return NULL;
+}
+
+static uint32_t zend_fetch_arg_info(const zend_script *script, zend_arg_info *arg_info, zend_class_entry **pce)
+{
+ uint32_t tmp = 0;
+
+ *pce = NULL;
+ if (arg_info->class_name) {
+ // class type hinting...
+ zend_string *lcname = zend_string_tolower(arg_info->class_name);
+ tmp |= MAY_BE_OBJECT;
+ *pce = get_class_entry(script, lcname);
+ zend_string_release(lcname);
+ } else if (arg_info->type_hint != IS_UNDEF) {
+ if (arg_info->type_hint == IS_VOID) {
+ tmp |= MAY_BE_NULL;
+ } else if (arg_info->type_hint == IS_CALLABLE) {
+ tmp |= MAY_BE_STRING|MAY_BE_OBJECT|MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF;
+ } else if (arg_info->type_hint == IS_ARRAY) {
+ tmp |= MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF;
+ } else if (arg_info->type_hint == _IS_BOOL) {
+ tmp |= MAY_BE_TRUE|MAY_BE_FALSE;
+ } else {
+ ZEND_ASSERT(arg_info->type_hint < IS_REFERENCE);
+ tmp |= 1 << arg_info->type_hint;
+ }
+ } else {
+ tmp |= MAY_BE_ANY|MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF;
+ }
+ if (arg_info->allow_null) {
+ tmp |= MAY_BE_NULL;
+ }
+ return tmp;
+}
+
+static void zend_update_type_info(const zend_op_array *op_array,
+ zend_ssa *ssa,
+ const zend_script *script,
+ zend_bitset worklist,
+ int i)
+{
+ uint32_t t1, t2;
+ uint32_t tmp, orig;
+ zend_op *opline = op_array->opcodes + i;
+ zend_ssa_op *ssa_ops = ssa->ops;
+ zend_ssa_var *ssa_vars = ssa->vars;
+ zend_ssa_var_info *ssa_var_info = ssa->var_info;
+ zend_class_entry *ce;
+ int j;
+
+ if (opline->opcode == ZEND_OP_DATA) {
+ opline--;
+ i--;
+ }
+
+ t1 = OP1_INFO();
+ t2 = OP2_INFO();
+
+ switch (opline->opcode) {
+ case ZEND_ADD:
+ case ZEND_SUB:
+ case ZEND_MUL:
+ case ZEND_DIV:
+ case ZEND_POW:
+ case ZEND_MOD:
+ case ZEND_BW_OR:
+ case ZEND_BW_AND:
+ case ZEND_BW_XOR:
+ case ZEND_SL:
+ case ZEND_SR:
+ case ZEND_CONCAT:
+ tmp = binary_op_result_type(ssa, opline->opcode, t1, t2, ssa_ops[i].result_def);
+ UPDATE_SSA_TYPE(tmp, ssa_ops[i].result_def);
+ break;
+ case ZEND_BW_NOT:
+ tmp = MAY_BE_RC1;
+ if (t1 & MAY_BE_STRING) {
+ tmp |= MAY_BE_STRING;
+ }
+ if (t1 & (MAY_BE_ANY-MAY_BE_STRING)) {
+ tmp |= MAY_BE_LONG;
+ }
+ UPDATE_SSA_TYPE(tmp, ssa_ops[i].result_def);
+ break;
+ case ZEND_BEGIN_SILENCE:
+ UPDATE_SSA_TYPE(MAY_BE_RC1|MAY_BE_LONG, ssa_ops[i].result_def);
+ break;
+ case ZEND_BOOL_NOT:
+ case ZEND_BOOL_XOR:
+ case ZEND_IS_IDENTICAL:
+ case ZEND_IS_NOT_IDENTICAL:
+ case ZEND_IS_EQUAL:
+ case ZEND_IS_NOT_EQUAL:
+ case ZEND_IS_SMALLER:
+ case ZEND_IS_SMALLER_OR_EQUAL:
+ case ZEND_INSTANCEOF:
+ case ZEND_JMPZ_EX:
+ case ZEND_JMPNZ_EX:
+ case ZEND_CASE:
+ case ZEND_BOOL:
+ case ZEND_ISSET_ISEMPTY_VAR:
+ case ZEND_ISSET_ISEMPTY_DIM_OBJ:
+ case ZEND_ISSET_ISEMPTY_PROP_OBJ:
+ case ZEND_ISSET_ISEMPTY_STATIC_PROP:
+ case ZEND_ASSERT_CHECK:
+ UPDATE_SSA_TYPE(MAY_BE_RC1|MAY_BE_FALSE|MAY_BE_TRUE, ssa_ops[i].result_def);
+ break;
+ case ZEND_CAST:
+ tmp = MAY_BE_RC1;
+ if (opline->extended_value == _IS_BOOL) {
+ tmp |= MAY_BE_TRUE|MAY_BE_FALSE;
+ } else {
+ tmp |= 1 << opline->extended_value;
+ }
+ if (opline->extended_value == IS_ARRAY) {
+ if (t1 & MAY_BE_ARRAY) {
+ tmp |= t1 & (MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF);
+ }
+ if (t1 & MAY_BE_OBJECT) {
+ tmp |= MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF;
+ } else {
+ tmp |= ((t1 & MAY_BE_ANY) << MAY_BE_ARRAY_SHIFT) | MAY_BE_ARRAY_KEY_LONG;
+ }
+ }
+ UPDATE_SSA_TYPE(tmp, ssa_ops[i].result_def);
+ break;
+ case ZEND_QM_ASSIGN:
+ case ZEND_JMP_SET:
+ case ZEND_COALESCE:
+ tmp = t1 & ~(MAY_BE_UNDEF|MAY_BE_REF);
+ if (t1 & MAY_BE_UNDEF) {
+ tmp |= MAY_BE_NULL;
+ }
+ if (opline->op1_type & (IS_CV|IS_VAR)) {
+ tmp |= MAY_BE_RCN;
+ }
+ if (opline->opcode != ZEND_QM_ASSIGN) {
+ /* COALESCE and JMP_SET result can't be null */
+ tmp &= ~MAY_BE_NULL;
+ if (opline->opcode == ZEND_JMP_SET) {
+ /* JMP_SET result can't be false either */
+ tmp &= ~MAY_BE_FALSE;
+ }
+ }
+ UPDATE_SSA_TYPE(tmp, ssa_ops[i].result_def);
+ COPY_SSA_OBJ_TYPE(ssa_ops[i].op1_use, ssa_ops[i].result_def);
+ break;
+ case ZEND_ASSIGN_ADD:
+ case ZEND_ASSIGN_SUB:
+ case ZEND_ASSIGN_MUL:
+ case ZEND_ASSIGN_DIV:
+ case ZEND_ASSIGN_POW:
+ case ZEND_ASSIGN_MOD:
+ case ZEND_ASSIGN_SL:
+ case ZEND_ASSIGN_SR:
+ case ZEND_ASSIGN_BW_OR:
+ case ZEND_ASSIGN_BW_AND:
+ case ZEND_ASSIGN_BW_XOR:
+ case ZEND_ASSIGN_CONCAT:
+ orig = 0;
+ if (opline->extended_value == ZEND_ASSIGN_OBJ) {
+ tmp = MAY_BE_RC1;
+ orig = t1 & ~MAY_BE_UNDEF;
+ t1 = MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF;
+ t2 = OP1_DATA_INFO();
+ } else if (opline->extended_value == ZEND_ASSIGN_DIM) {
+ tmp = MAY_BE_RC1;
+ orig = t1 & ~MAY_BE_UNDEF;
+ t1 = zend_array_element_type(t1, 1, 0);
+ t2 = OP1_DATA_INFO();
+ } else {
+ tmp = 0;
+ if (t1 & (MAY_BE_RC1|MAY_BE_RCN)) {
+ tmp |= MAY_BE_RC1;
+ if (ssa_ops[i].result_def >= 0) {
+ tmp |= MAY_BE_RCN;
+ }
+ }
+ if (t1 & MAY_BE_REF) {
+ tmp |= MAY_BE_REF;
+ }
+ }
+
+ tmp |= binary_op_result_type(
+ ssa, get_compound_assign_op(opline->opcode), t1, t2, ssa_ops[i].op1_def);
+
+ if (opline->extended_value == ZEND_ASSIGN_DIM) {
+ if (opline->op1_type == IS_CV) {
+ orig = assign_dim_result_type(orig, OP2_INFO(), tmp, opline->op1_type);
+ UPDATE_SSA_TYPE(orig, ssa_ops[i].op1_def);
+ COPY_SSA_OBJ_TYPE(ssa_ops[i].op1_use, ssa_ops[i].op1_def);
+ }
+ } else if (opline->extended_value == ZEND_ASSIGN_OBJ) {
+ if (opline->op1_type == IS_CV) {
+ if (orig & (MAY_BE_UNDEF | MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING)) {
+ orig |= MAY_BE_OBJECT;
+ }
+ if (orig & MAY_BE_RCN) {
+ orig |= MAY_BE_RC1;
+ }
+ UPDATE_SSA_TYPE(orig, ssa_ops[i].op1_def);
+ COPY_SSA_OBJ_TYPE(ssa_ops[i].op1_use, ssa_ops[i].op1_def);
+ }
+ } else {
+ UPDATE_SSA_TYPE(tmp, ssa_ops[i].op1_def);
+ }
+ if (ssa_ops[i].result_def >= 0) {
+ UPDATE_SSA_TYPE(tmp, ssa_ops[i].result_def);
+ }
+ break;
+ case ZEND_PRE_INC:
+ case ZEND_PRE_DEC:
+ tmp = 0;
+ if (t1 & MAY_BE_REF) {
+ tmp |= MAY_BE_REF;
+ }
+ if (t1 & (MAY_BE_RC1|MAY_BE_RCN)) {
+ tmp |= MAY_BE_RC1;
+ if (ssa_ops[i].result_def >= 0) {
+ tmp |= MAY_BE_RCN;
+ }
+ }
+ if ((t1 & MAY_BE_ANY) == MAY_BE_LONG) {
+ if (!ssa_var_info[ssa_ops[i].op1_use].has_range ||
+ (opline->opcode == ZEND_PRE_DEC &&
+ (ssa_var_info[ssa_ops[i].op1_use].range.underflow ||
+ ssa_var_info[ssa_ops[i].op1_use].range.min == ZEND_LONG_MIN)) ||
+ (opline->opcode == ZEND_PRE_INC &&
+ (ssa_var_info[ssa_ops[i].op1_use].range.overflow ||
+ ssa_var_info[ssa_ops[i].op1_use].range.min == ZEND_LONG_MAX))) {
+ /* may overflow */
+ tmp |= MAY_BE_LONG | MAY_BE_DOUBLE;
+ } else {
+ tmp |= MAY_BE_LONG;
+ }
+ } else {
+ if (t1 & MAY_BE_ERROR) {
+ tmp |= MAY_BE_NULL;
+ }
+ if (t1 & (MAY_BE_UNDEF | MAY_BE_NULL)) {
+ if (opline->opcode == ZEND_PRE_INC) {
+ tmp |= MAY_BE_LONG;
+ } else {
+ tmp |= MAY_BE_NULL;
+ }
+ }
+ if (t1 & MAY_BE_LONG) {
+ tmp |= MAY_BE_LONG | MAY_BE_DOUBLE;
+ }
+ if (t1 & MAY_BE_DOUBLE) {
+ tmp |= MAY_BE_DOUBLE;
+ }
+ if (t1 & MAY_BE_STRING) {
+ tmp |= MAY_BE_STRING | MAY_BE_LONG | MAY_BE_DOUBLE;
+ }
+ tmp |= t1 & (MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_RESOURCE | MAY_BE_ARRAY | MAY_BE_OBJECT | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_KEY_ANY);
+ }
+ if (ssa_ops[i].op1_def >= 0) {
+ UPDATE_SSA_TYPE(tmp, ssa_ops[i].op1_def);
+ }
+ if (ssa_ops[i].result_def >= 0) {
+ UPDATE_SSA_TYPE(tmp, ssa_ops[i].result_def);
+ }
+ break;
+ case ZEND_POST_INC:
+ case ZEND_POST_DEC:
+ if (ssa_ops[i].result_def >= 0) {
+ tmp = (MAY_BE_RC1 | t1) & ~(MAY_BE_UNDEF|MAY_BE_ERROR|MAY_BE_REF|MAY_BE_RCN);
+ if (t1 & MAY_BE_UNDEF) {
+ tmp |= MAY_BE_NULL;
+ }
+ UPDATE_SSA_TYPE(tmp, ssa_ops[i].result_def);
+ }
+ tmp = 0;
+ if (t1 & MAY_BE_REF) {
+ tmp |= MAY_BE_REF;
+ }
+ if (t1 & (MAY_BE_RC1|MAY_BE_RCN)) {
+ tmp |= MAY_BE_RC1;
+ }
+ if ((t1 & MAY_BE_ANY) == MAY_BE_LONG) {
+ if (!ssa_var_info[ssa_ops[i].op1_use].has_range ||
+ (opline->opcode == ZEND_PRE_DEC &&
+ (ssa_var_info[ssa_ops[i].op1_use].range.underflow ||
+ ssa_var_info[ssa_ops[i].op1_use].range.min == ZEND_LONG_MIN)) ||
+ (opline->opcode == ZEND_PRE_INC &&
+ (ssa_var_info[ssa_ops[i].op1_use].range.overflow ||
+ ssa_var_info[ssa_ops[i].op1_use].range.min == ZEND_LONG_MAX))) {
+ /* may overflow */
+ tmp |= MAY_BE_LONG | MAY_BE_DOUBLE;
+ } else {
+ tmp |= MAY_BE_LONG;
+ }
+ } else {
+ if (t1 & MAY_BE_ERROR) {
+ tmp |= MAY_BE_NULL;
+ }
+ if (t1 & (MAY_BE_UNDEF | MAY_BE_NULL)) {
+ if (opline->opcode == ZEND_POST_INC) {
+ tmp |= MAY_BE_LONG;
+ } else {
+ tmp |= MAY_BE_NULL;
+ }
+ }
+ if (t1 & MAY_BE_LONG) {
+ tmp |= MAY_BE_LONG | MAY_BE_DOUBLE;
+ }
+ if (t1 & MAY_BE_DOUBLE) {
+ tmp |= MAY_BE_DOUBLE;
+ }
+ if (t1 & MAY_BE_STRING) {
+ tmp |= MAY_BE_STRING | MAY_BE_LONG | MAY_BE_DOUBLE;
+ }
+ tmp |= t1 & (MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_RESOURCE | MAY_BE_ARRAY | MAY_BE_OBJECT | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_KEY_ANY);
+ }
+ if (ssa_ops[i].op1_def >= 0) {
+ UPDATE_SSA_TYPE(tmp, ssa_ops[i].op1_def);
+ }
+ break;
+ case ZEND_ASSIGN_DIM:
+ if (opline->op1_type == IS_CV) {
+ tmp = assign_dim_result_type(t1, t2, OP1_DATA_INFO(), opline->op2_type);
+ UPDATE_SSA_TYPE(tmp, ssa_ops[i].op1_def);
+ COPY_SSA_OBJ_TYPE(ssa_ops[i].op1_use, ssa_ops[i].op1_def);
+ }
+ if (ssa_ops[i].result_def >= 0) {
+ tmp = 0;
+ if (t1 & MAY_BE_STRING) {
+ tmp |= MAY_BE_STRING;
+ }
+ if (t1 & (MAY_BE_ANY - MAY_BE_STRING)) {
+ tmp |= (OP1_DATA_INFO() & (MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF));
+ }
+ tmp |= MAY_BE_RC1 | MAY_BE_RCN;
+ if (t1 & MAY_BE_OBJECT) {
+ tmp |= MAY_BE_REF;
+ }
+ UPDATE_SSA_TYPE(tmp, ssa_ops[i].result_def);
+ }
+ if ((opline+1)->op1_type == IS_CV && ssa_ops[i+1].op1_def >= 0) {
+ opline++;
+ i++;
+ tmp = OP1_INFO();
+ if (tmp & (MAY_BE_ANY | MAY_BE_REF)) {
+ if (tmp & MAY_BE_RC1) {
+ tmp |= MAY_BE_RCN;
+ }
+ }
+ UPDATE_SSA_TYPE(tmp, ssa_ops[i].op1_def);
+ }
+ break;
+ case ZEND_ASSIGN_OBJ:
+ if (opline->op1_type == IS_CV) {
+ tmp = (t1 & (MAY_BE_ANY|MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF));
+ tmp &= ~MAY_BE_NULL;
+ if (t1 & (MAY_BE_UNDEF | MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING)) {
+ tmp |= MAY_BE_OBJECT;
+ }
+ if (t1 & MAY_BE_REF) {
+ tmp |= MAY_BE_REF;
+ }
+ if (t1 & (MAY_BE_RC1|MAY_BE_RCN)) {
+ tmp |= MAY_BE_RC1;
+ }
+ UPDATE_SSA_TYPE(tmp, ssa_ops[i].op1_def);
+ COPY_SSA_OBJ_TYPE(ssa_ops[i].op1_use, ssa_ops[i].op1_def);
+ }
+ if (ssa_ops[i].result_def >= 0) {
+ // TODO: ???
+ tmp = MAY_BE_REF | MAY_BE_RC1 | MAY_BE_RCN | MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF;
+ UPDATE_SSA_TYPE(tmp, ssa_ops[i].result_def);
+ }
+ if ((opline+1)->op1_type == IS_CV) {
+ opline++;
+ i++;
+ tmp = OP1_INFO();
+ if (tmp & (MAY_BE_ANY | MAY_BE_REF)) {
+ if (tmp & MAY_BE_RC1) {
+ tmp |= MAY_BE_RCN;
+ }
+ }
+ UPDATE_SSA_TYPE(tmp, ssa_ops[i].op1_def);
+ }
+ break;
+ case ZEND_ASSIGN:
+ if (opline->op2_type == IS_CV && ssa_ops[i].op2_def >= 0) {
+ tmp = t2;
+ if (tmp & (MAY_BE_ANY | MAY_BE_REF)) {
+ if (tmp & MAY_BE_RC1) {
+ tmp |= MAY_BE_RCN;
+ }
+ }
+ UPDATE_SSA_TYPE(tmp, ssa_ops[i].op2_def);
+ }
+ tmp = t2 & ~(MAY_BE_UNDEF|MAY_BE_REF|MAY_BE_RC1|MAY_BE_RCN);
+ if (t2 & MAY_BE_UNDEF) {
+ tmp |= MAY_BE_NULL;
+ }
+ if (t1 & MAY_BE_REF) {
+ tmp |= MAY_BE_REF;
+ }
+ if (t1 & (MAY_BE_RC1 | MAY_BE_RCN)) {
+ if (t2 & MAY_BE_REF) {
+ tmp |= MAY_BE_RC1;
+ }
+ if (t2 & MAY_BE_RC1) {
+ tmp |= MAY_BE_RC1;
+ if (opline->op2_type & (IS_CV|IS_VAR)) {
+ tmp |= MAY_BE_RCN;
+ }
+ }
+ if (t2 & MAY_BE_RCN) {
+ tmp |= MAY_BE_RCN;
+ }
+ if (RETURN_VALUE_USED(opline) && (tmp & MAY_BE_RC1)) {
+ tmp |= MAY_BE_RCN;
+ }
+ }
+ if (ssa_ops[i].op1_def >= 0) {
+ if (ssa_var_info[ssa_ops[i].op1_def].use_as_double) {
+ tmp &= ~MAY_BE_LONG;
+ tmp |= MAY_BE_DOUBLE;
+ }
+ UPDATE_SSA_TYPE(tmp, ssa_ops[i].op1_def);
+ COPY_SSA_OBJ_TYPE(ssa_ops[i].op2_use, ssa_ops[i].op1_def);
+ }
+ if (ssa_ops[i].result_def >= 0) {
+ UPDATE_SSA_TYPE(tmp & ~MAY_BE_REF, ssa_ops[i].result_def);
+ COPY_SSA_OBJ_TYPE(ssa_ops[i].op2_use, ssa_ops[i].result_def);
+ }
+ break;
+ case ZEND_ASSIGN_REF:
+// TODO: ???
+ if (opline->op2_type == IS_CV) {
+ tmp = (MAY_BE_REF | t2) & ~(MAY_BE_UNDEF|MAY_BE_RC1|MAY_BE_RCN);
+ if (t2 & MAY_BE_UNDEF) {
+ tmp |= MAY_BE_NULL;
+ }
+ UPDATE_SSA_TYPE(tmp, ssa_ops[i].op2_def);
+ }
+ if (opline->op2_type == IS_VAR && opline->extended_value == ZEND_RETURNS_FUNCTION) {
+ tmp = (MAY_BE_REF | MAY_BE_RCN | MAY_BE_RC1 | t2) & ~MAY_BE_UNDEF;
+ } else {
+ tmp = (MAY_BE_REF | t2) & ~(MAY_BE_UNDEF|MAY_BE_ERROR|MAY_BE_RC1|MAY_BE_RCN);
+ }
+ if (t2 & MAY_BE_UNDEF) {
+ tmp |= MAY_BE_NULL;
+ }
+ UPDATE_SSA_TYPE(tmp, ssa_ops[i].op1_def);
+ if (ssa_ops[i].result_def >= 0) {
+ UPDATE_SSA_TYPE(tmp, ssa_ops[i].result_def);
+ }
+ break;
+ case ZEND_BIND_GLOBAL:
+ tmp = MAY_BE_REF | MAY_BE_ANY
+ | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF;
+ UPDATE_SSA_TYPE(tmp, ssa_ops[i].op1_def);
+ break;
+ case ZEND_BIND_STATIC:
+ tmp = MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF
+ | (opline->extended_value ? MAY_BE_REF : (MAY_BE_RC1 | MAY_BE_RCN));
+ UPDATE_SSA_TYPE(tmp, ssa_ops[i].op1_def);
+ break;
+ case ZEND_SEND_VAR:
+ UPDATE_SSA_TYPE(t1 | MAY_BE_RC1 | MAY_BE_RCN, ssa_ops[i].op1_def);
+ COPY_SSA_OBJ_TYPE(ssa_ops[i].op1_use, ssa_ops[i].op1_def);
+ break;
+ case ZEND_BIND_LEXICAL:
+ if (ssa_ops[i].op2_def >= 0) {
+ tmp = t2 | MAY_BE_RC1 | MAY_BE_RCN;
+ if (opline->extended_value) {
+ tmp |= MAY_BE_REF;
+ }
+ UPDATE_SSA_TYPE(tmp, ssa_ops[i].op2_def);
+ COPY_SSA_OBJ_TYPE(ssa_ops[i].op2_use, ssa_ops[i].op2_def);
+ }
+ break;
+ case ZEND_YIELD:
+ if (ssa_ops[i].op1_def >= 0) {
+ tmp = t1 | MAY_BE_RC1 | MAY_BE_RCN;
+ if (op_array->fn_flags & ZEND_ACC_RETURN_REFERENCE) {
+ tmp |= MAY_BE_REF;
+ }
+ UPDATE_SSA_TYPE(tmp, ssa_ops[i].op1_def);
+ COPY_SSA_OBJ_TYPE(ssa_ops[i].op1_use, ssa_ops[i].op1_def);
+ }
+ if (ssa_ops[i].result_def >= 0) {
+ tmp = MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF
+ | MAY_BE_RC1 | MAY_BE_RCN;
+ UPDATE_SSA_TYPE(tmp, ssa_ops[i].result_def);
+ }
+ break;
+ case ZEND_SEND_VAR_EX:
+ case ZEND_SEND_VAR_NO_REF:
+ case ZEND_SEND_VAR_NO_REF_EX:
+ case ZEND_SEND_REF:
+// TODO: ???
+ if (ssa_ops[i].op1_def >= 0) {
+ tmp = (t1 & MAY_BE_UNDEF)|MAY_BE_REF|MAY_BE_RC1|MAY_BE_RCN|MAY_BE_ANY|MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF;
+ UPDATE_SSA_TYPE(tmp, ssa_ops[i].op1_def);
+ }
+ break;
+ case ZEND_SEND_UNPACK:
+ if (ssa_ops[i].op1_def >= 0) {
+ tmp = t1 | MAY_BE_RC1|MAY_BE_RCN;
+ if (t1 & MAY_BE_ARRAY) {
+ /* SEND_UNPACK may acquire references into the array */
+ tmp |= MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF;
+ }
+ UPDATE_SSA_TYPE(tmp, ssa_ops[i].op1_def);
+ }
+ break;
+ case ZEND_FAST_CONCAT:
+ case ZEND_ROPE_INIT:
+ case ZEND_ROPE_ADD:
+ case ZEND_ROPE_END:
+ UPDATE_SSA_TYPE(MAY_BE_RC1|MAY_BE_STRING, ssa_ops[i].result_def);
+ break;
+ case ZEND_RECV:
+ case ZEND_RECV_INIT:
+ {
+ /* Typehinting */
+ zend_func_info *func_info;
+ zend_arg_info *arg_info = NULL;
+ if (op_array->arg_info && opline->op1.num <= op_array->num_args) {
+ arg_info = &op_array->arg_info[opline->op1.num-1];
+ }
+
+ ce = NULL;
+ if (arg_info) {
+ tmp = zend_fetch_arg_info(script, arg_info, &ce);
+ if (opline->opcode == ZEND_RECV_INIT &&
+ Z_CONSTANT_P(CRT_CONSTANT_EX(op_array, opline->op2, ssa->rt_constants))) {
+ /* The constant may resolve to NULL */
+ tmp |= MAY_BE_NULL;
+ }
+ if (arg_info->pass_by_reference) {
+ tmp |= MAY_BE_REF;
+ } else {
+ tmp |= MAY_BE_RC1|MAY_BE_RCN;
+ }
+ } else {
+ tmp = MAY_BE_REF|MAY_BE_RC1|MAY_BE_RCN|MAY_BE_ANY|MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF;
+ }
+ func_info = ZEND_FUNC_INFO(op_array);
+ if (func_info && (int)opline->op1.num-1 < func_info->num_args) {
+ tmp = (tmp & (MAY_BE_RC1|MAY_BE_RCN|MAY_BE_REF)) |
+ (tmp & func_info->arg_info[opline->op1.num-1].info.type);
+ }
+#if 0
+ /* We won't recieve unused arguments */
+ if (ssa_vars[ssa_ops[i].result_def].use_chain < 0 &&
+ ssa_vars[ssa_ops[i].result_def].phi_use_chain == NULL &&
+ op_array->arg_info &&
+ opline->op1.num <= op_array->num_args &&
+ op_array->arg_info[opline->op1.num-1].class_name == NULL &&
+ !op_array->arg_info[opline->op1.num-1].type_hint) {
+ tmp = MAY_BE_UNDEF|MAY_BE_RCN;
+ }
+#endif
+ UPDATE_SSA_TYPE(tmp, ssa_ops[i].result_def);
+ if (func_info &&
+ (int)opline->op1.num-1 < func_info->num_args &&
+ func_info->arg_info[opline->op1.num-1].info.ce) {
+ UPDATE_SSA_OBJ_TYPE(
+ func_info->arg_info[opline->op1.num-1].info.ce,
+ func_info->arg_info[opline->op1.num-1].info.is_instanceof,
+ ssa_ops[i].result_def);
+ } else if (ce) {
+ UPDATE_SSA_OBJ_TYPE(ce, 1, ssa_ops[i].result_def);
+ } else {
+ UPDATE_SSA_OBJ_TYPE(NULL, 0, ssa_ops[i].result_def);
+ }
+ break;
+ }
+ case ZEND_DECLARE_CLASS:
+ case ZEND_DECLARE_INHERITED_CLASS:
+ case ZEND_DECLARE_ANON_CLASS:
+ case ZEND_DECLARE_ANON_INHERITED_CLASS:
+ UPDATE_SSA_TYPE(MAY_BE_CLASS, ssa_ops[i].result_def);
+ if ((ce = zend_hash_find_ptr(&script->class_table, Z_STR_P(CRT_CONSTANT_EX(op_array, opline->op1, ssa->rt_constants)))) != NULL) {
+ UPDATE_SSA_OBJ_TYPE(ce, 0, ssa_ops[i].result_def);
+ }
+ break;
+ case ZEND_FETCH_CLASS:
+ UPDATE_SSA_TYPE(MAY_BE_CLASS, ssa_ops[i].result_def);
+ if (opline->op2_type == IS_UNUSED) {
+ switch (opline->extended_value & ZEND_FETCH_CLASS_MASK) {
+ case ZEND_FETCH_CLASS_SELF:
+ if (op_array->scope) {
+ UPDATE_SSA_OBJ_TYPE(op_array->scope, 0, ssa_ops[i].result_def);
+ } else {
+ UPDATE_SSA_OBJ_TYPE(NULL, 0, ssa_ops[i].result_def);
+ }
+ break;
+ case ZEND_FETCH_CLASS_PARENT:
+ if (op_array->scope && op_array->scope->parent) {
+ UPDATE_SSA_OBJ_TYPE(op_array->scope->parent, 0, ssa_ops[i].result_def);
+ } else {
+ UPDATE_SSA_OBJ_TYPE(NULL, 0, ssa_ops[i].result_def);
+ }
+ break;
+ case ZEND_FETCH_CLASS_STATIC:
+ default:
+ UPDATE_SSA_OBJ_TYPE(NULL, 0, ssa_ops[i].result_def);
+ break;
+ }
+ } else if (opline->op2_type == IS_CONST) {
+ zval *zv = CRT_CONSTANT_EX(op_array, opline->op2, ssa->rt_constants);
+ if (Z_TYPE_P(zv) == IS_STRING) {
+ ce = get_class_entry(script, Z_STR_P(zv+1));
+ UPDATE_SSA_OBJ_TYPE(ce, 0, ssa_ops[i].result_def);
+ } else {
+ UPDATE_SSA_OBJ_TYPE(NULL, 0, ssa_ops[i].result_def);
+ }
+ } else {
+ COPY_SSA_OBJ_TYPE(ssa_ops[i].op2_use, ssa_ops[i].result_def);
+ }
+ break;
+ case ZEND_NEW:
+ tmp = MAY_BE_RC1|MAY_BE_RCN|MAY_BE_OBJECT;
+ if (opline->op1_type == IS_CONST &&
+ (ce = get_class_entry(script, Z_STR_P(CRT_CONSTANT_EX(op_array, opline->op1, ssa->rt_constants)+1))) != NULL) {
+ UPDATE_SSA_OBJ_TYPE(ce, 0, ssa_ops[i].result_def);
+ } else if ((t1 & MAY_BE_CLASS) && ssa_ops[i].op1_use >= 0 && ssa_var_info[ssa_ops[i].op1_use].ce) {
+ UPDATE_SSA_OBJ_TYPE(ssa_var_info[ssa_ops[i].op1_use].ce, ssa_var_info[ssa_ops[i].op1_use].is_instanceof, ssa_ops[i].result_def);
+ } else {
+ UPDATE_SSA_OBJ_TYPE(NULL, 0, ssa_ops[i].result_def);
+ }
+ UPDATE_SSA_TYPE(tmp, ssa_ops[i].result_def);
+ break;
+ case ZEND_CLONE:
+ UPDATE_SSA_TYPE(MAY_BE_RC1|MAY_BE_RCN|MAY_BE_OBJECT, ssa_ops[i].result_def);
+ COPY_SSA_OBJ_TYPE(ssa_ops[i].op1_use, ssa_ops[i].result_def);
+ break;
+ case ZEND_INIT_ARRAY:
+ case ZEND_ADD_ARRAY_ELEMENT:
+ if (opline->op1_type == IS_CV && ssa_ops[i].op1_def >= 0) {
+ if (opline->extended_value & ZEND_ARRAY_ELEMENT_REF) {
+ tmp = (MAY_BE_REF | t1) & ~(MAY_BE_UNDEF|MAY_BE_RC1|MAY_BE_RCN);
+ if (t1 & MAY_BE_UNDEF) {
+ tmp |= MAY_BE_NULL;
+ }
+ } else if ((t1 & (MAY_BE_REF|MAY_BE_RC1|MAY_BE_RCN)) == MAY_BE_REF) {
+ tmp = (MAY_BE_REF | t1) & ~(MAY_BE_UNDEF|MAY_BE_RC1|MAY_BE_RCN);
+ if (t1 & MAY_BE_UNDEF) {
+ tmp |= MAY_BE_NULL;
+ }
+ } else if (t1 & MAY_BE_REF) {
+ tmp = (MAY_BE_RC1 | MAY_BE_RCN | MAY_BE_REF | t1);
+ } else {
+ tmp = t1;
+ if (t1 & MAY_BE_RC1) {
+ tmp |= MAY_BE_RCN;
+ }
+ }
+ UPDATE_SSA_TYPE(tmp, ssa_ops[i].op1_def);
+ }
+ if (ssa_ops[i].result_def >= 0) {
+ tmp = MAY_BE_RC1|MAY_BE_ARRAY;
+ if (opline->op1_type != IS_UNUSED) {
+ tmp |= (t1 & MAY_BE_ANY) << MAY_BE_ARRAY_SHIFT;
+ if (opline->extended_value & ZEND_ARRAY_ELEMENT_REF) {
+ tmp |= MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF;
+ }
+ }
+ if (ssa_ops[i].result_use >= 0) {
+ tmp |= ssa_var_info[ssa_ops[i].result_use].type;
+ }
+ if (opline->op2_type == IS_UNUSED) {
+ tmp |= MAY_BE_ARRAY_KEY_LONG;
+ } else {
+ if (t2 & (MAY_BE_LONG|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_DOUBLE)) {
+ tmp |= MAY_BE_ARRAY_KEY_LONG;
+ }
+ if (t2 & (MAY_BE_STRING)) {
+ tmp |= MAY_BE_ARRAY_KEY_STRING;
+ if (opline->op2_type != IS_CONST) {
+ // FIXME: numeric string
+ tmp |= MAY_BE_ARRAY_KEY_LONG;
+ }
+ }
+ if (t2 & (MAY_BE_UNDEF | MAY_BE_NULL)) {
+ tmp |= MAY_BE_ARRAY_KEY_STRING;
+ }
+ }
+ UPDATE_SSA_TYPE(tmp, ssa_ops[i].result_def);
+ }
+ break;
+ case ZEND_UNSET_VAR:
+ ZEND_ASSERT(opline->extended_value & ZEND_QUICK_SET);
+ tmp = MAY_BE_UNDEF|MAY_BE_RCN;
+ if (!op_array->function_name) {
+ /* In global scope, we know nothing */
+ tmp |= MAY_BE_REF;
+ }
+ UPDATE_SSA_TYPE(tmp, ssa_ops[i].op1_def);
+ break;
+ case ZEND_UNSET_DIM:
+ case ZEND_UNSET_OBJ:
+ if (ssa_ops[i].op1_def >= 0) {
+ UPDATE_SSA_TYPE(t1, ssa_ops[i].op1_def);
+ COPY_SSA_OBJ_TYPE(ssa_ops[i].op1_use, ssa_ops[i].op1_def);
+ }
+ break;
+// case ZEND_INCLUDE_OR_EVAL:
+// case ZEND_ISSET_ISEMPTY_VAR:
+// TODO: ???
+// break;
+ case ZEND_FE_RESET_R:
+ case ZEND_FE_RESET_RW:
+ if (ssa_ops[i].op1_def >= 0) {
+ tmp = t1;
+ if (t1 & MAY_BE_RCN) {
+ tmp |= MAY_BE_RC1;
+ }
+ if (opline->opcode == ZEND_FE_RESET_RW) {
+//???
+ tmp |= MAY_BE_REF;
+ }
+ UPDATE_SSA_TYPE(tmp, ssa_ops[i].op1_def);
+ COPY_SSA_OBJ_TYPE(ssa_ops[i].op1_use, ssa_ops[i].op1_def);
+ }
+ if (opline->opcode == ZEND_FE_RESET_RW) {
+//???
+ tmp = MAY_BE_REF | (t1 & (MAY_BE_ARRAY | MAY_BE_OBJECT));
+ } else if (opline->op1_type == IS_TMP_VAR || opline->op1_type == IS_CONST) {
+ tmp = MAY_BE_RC1 | (t1 & (MAY_BE_ARRAY | MAY_BE_OBJECT | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF));
+ } else {
+ tmp = MAY_BE_RC1 | MAY_BE_RCN | (t1 & (MAY_BE_REF | MAY_BE_ARRAY | MAY_BE_OBJECT | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF));
+ }
+ UPDATE_SSA_TYPE(tmp, ssa_ops[i].result_def);
+ COPY_SSA_OBJ_TYPE(ssa_ops[i].op1_use, ssa_ops[i].result_def);
+ break;
+ case ZEND_FE_FETCH_R:
+ case ZEND_FE_FETCH_RW:
+ if (t1 & MAY_BE_OBJECT) {
+ if (opline->opcode == ZEND_FE_FETCH_RW) {
+ tmp = MAY_BE_REF | MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF;
+ } else {
+ tmp = MAY_BE_REF | MAY_BE_RCN | MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF;
+ }
+ } else if (t1 & MAY_BE_ARRAY) {
+ if (opline->opcode == ZEND_FE_FETCH_RW) {
+ tmp = MAY_BE_REF | MAY_BE_RCN | MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF;
+ } else {
+ tmp = ((t1 & MAY_BE_ARRAY_OF_ANY) >> MAY_BE_ARRAY_SHIFT);
+ if (tmp & MAY_BE_ARRAY) {
+ tmp |= MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF;
+ }
+ if (t1 & MAY_BE_ARRAY_OF_REF) {
+ tmp |= MAY_BE_REF | MAY_BE_RC1 | MAY_BE_RCN;
+ } else {
+ tmp |= MAY_BE_RC1 | MAY_BE_RCN;
+ }
+ }
+ } else {
+ if (opline->opcode == ZEND_FE_FETCH_RW) {
+ tmp = MAY_BE_REF;
+ } else {
+ tmp = MAY_BE_RCN;
+ }
+ }
+ if (opline->opcode == ZEND_FE_FETCH_R && (t2 & MAY_BE_REF)) {
+ tmp |= MAY_BE_REF;
+ }
+ UPDATE_SSA_TYPE(tmp, ssa_ops[i].op2_def);
+ if (ssa_ops[i].result_def >= 0) {
+ tmp = MAY_BE_RC1;
+ if (t1 & MAY_BE_OBJECT) {
+ tmp |= MAY_BE_RCN | MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF;
+ } else if (t1 & MAY_BE_ARRAY) {
+ if (t1 & MAY_BE_ARRAY_KEY_LONG) {
+ tmp |= MAY_BE_LONG;
+ }
+ if (t1 & MAY_BE_ARRAY_KEY_STRING) {
+ tmp |= MAY_BE_STRING;
+ }
+ if (!(tmp & (MAY_BE_LONG|MAY_BE_STRING))) {
+ tmp |= MAY_BE_NULL;
+ }
+ }
+ UPDATE_SSA_TYPE(tmp, ssa_ops[i].result_def);
+ }
+ break;
+// case ZEND_CATCH:
+// TODO: ???
+// break;
+ case ZEND_FETCH_DIM_R:
+ case ZEND_FETCH_DIM_IS:
+ case ZEND_FETCH_DIM_RW:
+ case ZEND_FETCH_DIM_W:
+ case ZEND_FETCH_DIM_UNSET:
+ case ZEND_FETCH_DIM_FUNC_ARG:
+ case ZEND_FETCH_LIST:
+ if (ssa_ops[i].op1_def >= 0) {
+ tmp = t1;
+ if (opline->opcode == ZEND_FETCH_DIM_W ||
+ opline->opcode == ZEND_FETCH_DIM_RW ||
+ opline->opcode == ZEND_FETCH_DIM_FUNC_ARG) {
+ if (opline->opcode != ZEND_FETCH_DIM_FUNC_ARG) {
+ tmp &= ~MAY_BE_UNDEF;
+ if (t1 & (MAY_BE_UNDEF | MAY_BE_NULL)) {
+ tmp &= ~MAY_BE_NULL;
+ tmp |= MAY_BE_ARRAY;
+ } else if (t1 & (MAY_BE_FALSE|MAY_BE_STRING)) {
+ tmp |= MAY_BE_ARRAY;
+ }
+ }
+ if (tmp & MAY_BE_RCN) {
+ tmp |= MAY_BE_RC1;
+ }
+ if (t2 & (MAY_BE_LONG|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_RESOURCE|MAY_BE_DOUBLE)) {
+ tmp |= MAY_BE_ARRAY_KEY_LONG;
+ }
+ if (t2 & (MAY_BE_STRING)) {
+ // FIXME: numeric string
+ tmp |= MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_KEY_LONG;
+ }
+ if (t2 & (MAY_BE_UNDEF | MAY_BE_NULL)) {
+ tmp |= MAY_BE_ARRAY_KEY_STRING;
+ }
+
+ }
+ ZEND_ASSERT(!ssa_vars[ssa_ops[i].result_def].phi_use_chain);
+ j = ssa_vars[ssa_ops[i].result_def].use_chain;
+ while (j >= 0) {
+ switch (op_array->opcodes[j].opcode) {
+ case ZEND_FETCH_DIM_W:
+ case ZEND_FETCH_DIM_RW:
+ case ZEND_FETCH_DIM_FUNC_ARG:
+ case ZEND_ASSIGN_ADD:
+ case ZEND_ASSIGN_SUB:
+ case ZEND_ASSIGN_MUL:
+ case ZEND_ASSIGN_DIV:
+ case ZEND_ASSIGN_MOD:
+ case ZEND_ASSIGN_SL:
+ case ZEND_ASSIGN_SR:
+ case ZEND_ASSIGN_CONCAT:
+ case ZEND_ASSIGN_BW_OR:
+ case ZEND_ASSIGN_BW_AND:
+ case ZEND_ASSIGN_BW_XOR:
+ case ZEND_ASSIGN_POW:
+ case ZEND_ASSIGN_DIM:
+ tmp |= MAY_BE_ARRAY | MAY_BE_ARRAY_OF_ARRAY;
+ break;
+ case ZEND_SEND_VAR:
+ break;
+ case ZEND_SEND_VAR_EX:
+ case ZEND_SEND_VAR_NO_REF:
+ case ZEND_SEND_VAR_NO_REF_EX:
+ case ZEND_SEND_REF:
+ case ZEND_ASSIGN_REF:
+ tmp |= MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF;
+ break;
+ case ZEND_PRE_INC:
+ case ZEND_PRE_DEC:
+ case ZEND_POST_INC:
+ case ZEND_POST_DEC:
+ if (tmp & MAY_BE_ARRAY_OF_LONG) {
+ /* may overflow */
+ tmp |= MAY_BE_ARRAY_OF_DOUBLE;
+ } else if (!(tmp & (MAY_BE_ARRAY_OF_LONG|MAY_BE_ARRAY_OF_DOUBLE))) {
+ tmp |= MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_DOUBLE;
+ }
+ break;
+ default :
+ break;
+ }
+ j = zend_ssa_next_use(ssa_ops, ssa_ops[i].result_def, j);
+ }
+ UPDATE_SSA_TYPE(tmp, ssa_ops[i].op1_def);
+ COPY_SSA_OBJ_TYPE(ssa_ops[i].op1_use, ssa_ops[i].op1_def);
+ }
+ /* FETCH_LIST on a string behaves like FETCH_R on null */
+ tmp = zend_array_element_type(
+ opline->opcode != ZEND_FETCH_LIST ? t1 : ((t1 & ~MAY_BE_STRING) | MAY_BE_NULL),
+ opline->opcode != ZEND_FETCH_DIM_R && opline->opcode != ZEND_FETCH_DIM_IS
+ && opline->opcode != ZEND_FETCH_LIST,
+ opline->op2_type == IS_UNUSED);
+ if (opline->opcode == ZEND_FETCH_DIM_W ||
+ opline->opcode == ZEND_FETCH_DIM_RW ||
+ opline->opcode == ZEND_FETCH_DIM_FUNC_ARG) {
+ if (t1 & (MAY_BE_ERROR|MAY_BE_TRUE|MAY_BE_LONG|MAY_BE_DOUBLE|MAY_BE_RESOURCE|MAY_BE_OBJECT)) {
+ tmp |= MAY_BE_ERROR;
+ } else if (opline->op2_type == IS_UNUSED) {
+ tmp |= MAY_BE_ERROR;
+ } else if (t2 & (MAY_BE_ARRAY|MAY_BE_OBJECT)) {
+ tmp |= MAY_BE_ERROR;
+ }
+ } else if (opline->opcode == ZEND_FETCH_DIM_IS && (t1 & MAY_BE_STRING)) {
+ tmp |= MAY_BE_NULL;
+ }
+ UPDATE_SSA_TYPE(tmp, ssa_ops[i].result_def);
+ break;
+ case ZEND_FETCH_THIS:
+ UPDATE_SSA_OBJ_TYPE(op_array->scope, 1, ssa_ops[i].result_def);
+ UPDATE_SSA_TYPE(MAY_BE_RC1|MAY_BE_RCN|MAY_BE_OBJECT, ssa_ops[i].result_def);
+ break;
+ case ZEND_FETCH_OBJ_R:
+ case ZEND_FETCH_OBJ_IS:
+ case ZEND_FETCH_OBJ_RW:
+ case ZEND_FETCH_OBJ_W:
+ case ZEND_FETCH_OBJ_UNSET:
+ case ZEND_FETCH_OBJ_FUNC_ARG:
+ if (ssa_ops[i].op1_def >= 0) {
+ tmp = t1;
+ if (opline->opcode == ZEND_FETCH_OBJ_W ||
+ opline->opcode == ZEND_FETCH_OBJ_RW ||
+ opline->opcode == ZEND_FETCH_OBJ_FUNC_ARG) {
+ if (opline->opcode != ZEND_FETCH_DIM_FUNC_ARG) {
+ tmp &= ~MAY_BE_UNDEF;
+ if (t1 & (MAY_BE_UNDEF | MAY_BE_NULL)) {
+ tmp &= ~MAY_BE_NULL;
+ tmp |= MAY_BE_OBJECT;
+ } else if (t1 & (MAY_BE_FALSE|MAY_BE_STRING)) {
+ tmp |= MAY_BE_OBJECT;
+ }
+ }
+ if (tmp & MAY_BE_RCN) {
+ tmp |= MAY_BE_RC1;
+ }
+ }
+ UPDATE_SSA_TYPE(tmp, ssa_ops[i].op1_def);
+ COPY_SSA_OBJ_TYPE(ssa_ops[i].op1_use, ssa_ops[i].op1_def);
+ }
+ if (ssa_ops[i].result_def >= 0) {
+ tmp = MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF;
+ if (opline->opcode != ZEND_FETCH_OBJ_R && opline->opcode != ZEND_FETCH_OBJ_IS) {
+ tmp |= MAY_BE_ERROR;
+ }
+ if (opline->result_type == IS_TMP_VAR) {
+ tmp |= MAY_BE_RC1;
+ } else {
+ tmp |= MAY_BE_REF | MAY_BE_RC1 | MAY_BE_RCN;
+ }
+ UPDATE_SSA_TYPE(tmp, ssa_ops[i].result_def);
+ }
+ break;
+ case ZEND_DO_FCALL:
+ case ZEND_DO_ICALL:
+ case ZEND_DO_UCALL:
+ case ZEND_DO_FCALL_BY_NAME:
+ if (ssa_ops[i].result_def >= 0) {
+ zend_func_info *func_info = ZEND_FUNC_INFO(op_array);
+ zend_call_info *call_info;
+
+ if (!func_info) {
+ goto unknown_opcode;
+ }
+ call_info = func_info->callee_info;
+ while (call_info && call_info->caller_call_opline != opline) {
+ call_info = call_info->next_callee;
+ }
+ if (!call_info) {
+ goto unknown_opcode;
+ }
+ tmp = zend_get_func_info(call_info, ssa) & ~(FUNC_MAY_WARN|FUNC_MAY_INLINE);
+ UPDATE_SSA_TYPE(tmp, ssa_ops[i].result_def);
+ if (call_info->callee_func->type == ZEND_USER_FUNCTION) {
+ func_info = ZEND_FUNC_INFO(&call_info->callee_func->op_array);
+ if (func_info) {
+ UPDATE_SSA_OBJ_TYPE(
+ func_info->return_info.ce,
+ func_info->return_info.is_instanceof,
+ ssa_ops[i].result_def);
+ }
+ }
+ }
+ break;
+ case ZEND_FETCH_CONSTANT:
+ case ZEND_FETCH_CLASS_CONSTANT:
+ UPDATE_SSA_TYPE(MAY_BE_RC1|MAY_BE_RCN|MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG|MAY_BE_DOUBLE|MAY_BE_STRING|MAY_BE_RESOURCE|MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY, ssa_ops[i].result_def);
+ break;
+ case ZEND_STRLEN:
+ tmp = MAY_BE_RC1|MAY_BE_LONG;
+ if (t1 & (MAY_BE_ANY - (MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG|MAY_BE_DOUBLE|MAY_BE_STRING))) {
+ tmp |= MAY_BE_NULL;
+ }
+ UPDATE_SSA_TYPE(tmp, ssa_ops[i].result_def);
+ break;
+ case ZEND_TYPE_CHECK:
+ case ZEND_DEFINED:
+ UPDATE_SSA_TYPE(MAY_BE_RC1|MAY_BE_FALSE|MAY_BE_TRUE, ssa_ops[i].result_def);
+ break;
+ case ZEND_VERIFY_RETURN_TYPE:
+ {
+ zend_arg_info *ret_info = op_array->arg_info - 1;
+
+ tmp = zend_fetch_arg_info(script, ret_info, &ce);
+ tmp |= MAY_BE_RC1 | MAY_BE_RCN;
+ if (opline->op1_type & (IS_TMP_VAR|IS_VAR|IS_CV)) {
+ UPDATE_SSA_TYPE(tmp, ssa_ops[i].op1_def);
+ if (ce) {
+ UPDATE_SSA_OBJ_TYPE(ce, 1, ssa_ops[i].op1_def);
+ } else {
+ UPDATE_SSA_OBJ_TYPE(NULL, 0, ssa_ops[i].op1_def);
+ }
+ } else {
+ UPDATE_SSA_TYPE(tmp, ssa_ops[i].result_def);
+ if (ce) {
+ UPDATE_SSA_OBJ_TYPE(ce, 1, ssa_ops[i].result_def);
+ } else {
+ UPDATE_SSA_OBJ_TYPE(NULL, 0, ssa_ops[i].result_def);
+ }
+ }
+ break;
+ }
+ default:
+unknown_opcode:
+ if (ssa_ops[i].op1_def >= 0) {
+ tmp = MAY_BE_ANY | MAY_BE_REF | MAY_BE_RC1 | MAY_BE_RCN | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF;
+ UPDATE_SSA_TYPE(tmp, ssa_ops[i].op1_def);
+ }
+ if (ssa_ops[i].result_def >= 0) {
+ tmp = MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF;
+ if (opline->result_type == IS_TMP_VAR) {
+ tmp |= MAY_BE_RC1;
+ } else {
+ tmp |= MAY_BE_REF | MAY_BE_RC1 | MAY_BE_RCN;
+ }
+ UPDATE_SSA_TYPE(tmp, ssa_ops[i].result_def);
+ }
+ break;
+ }
+}
+
+static uint32_t get_class_entry_rank(zend_class_entry *ce) {
+ uint32_t rank = 0;
+ while (ce->parent) {
+ rank++;
+ ce = ce->parent;
+ }
+ return rank;
+}
+
+/* Compute least common ancestor on class inheritance tree only */
+static zend_class_entry *join_class_entries(
+ zend_class_entry *ce1, zend_class_entry *ce2, int *is_instanceof) {
+ uint32_t rank1, rank2;
+ if (ce1 == ce2) {
+ return ce1;
+ }
+ if (!ce1 || !ce2) {
+ return NULL;
+ }
+
+ rank1 = get_class_entry_rank(ce1);
+ rank2 = get_class_entry_rank(ce2);
+
+ while (rank1 != rank2) {
+ if (rank1 > rank2) {
+ ce1 = ce1->parent;
+ rank1--;
+ } else {
+ ce2 = ce2->parent;
+ rank2--;
+ }
+ }
+
+ while (ce1 != ce2) {
+ ce1 = ce1->parent;
+ ce2 = ce2->parent;
+ }
+
+ if (ce1) {
+ *is_instanceof = 1;
+ }
+ return ce1;
+}
+
+int zend_infer_types_ex(const zend_op_array *op_array, const zend_script *script, zend_ssa *ssa, zend_bitset worklist)
+{
+ zend_basic_block *blocks = ssa->cfg.blocks;
+ zend_ssa_var *ssa_vars = ssa->vars;
+ zend_ssa_var_info *ssa_var_info = ssa->var_info;
+ int ssa_vars_count = ssa->vars_count;
+ uint i;
+ int j;
+ uint32_t tmp;
+
+ while (!zend_bitset_empty(worklist, zend_bitset_len(ssa_vars_count))) {
+ j = zend_bitset_first(worklist, zend_bitset_len(ssa_vars_count));
+ zend_bitset_excl(worklist, j);
+ if (ssa_vars[j].definition_phi) {
+ zend_ssa_phi *p = ssa_vars[j].definition_phi;
+ if (p->pi >= 0) {
+ zend_class_entry *ce = ssa_var_info[p->sources[0]].ce;
+ int is_instanceof = ssa_var_info[p->sources[0]].is_instanceof;
+ tmp = get_ssa_var_info(ssa, p->sources[0]);
+
+ if (!p->has_range_constraint) {
+ zend_ssa_type_constraint *constraint = &p->constraint.type;
+ tmp &= constraint->type_mask;
+ if ((tmp & MAY_BE_OBJECT) && constraint->ce && ce != constraint->ce) {
+ if (!ce) {
+ ce = constraint->ce;
+ is_instanceof = 1;
+ } else if (is_instanceof && instanceof_function(constraint->ce, ce)) {
+ ce = constraint->ce;
+ } else {
+ /* Ignore the constraint (either ce instanceof constraint->ce or
+ * they are unrelated, as far as we can statically determine) */
+ }
+ }
+ }
+
+ UPDATE_SSA_TYPE(tmp, j);
+ UPDATE_SSA_OBJ_TYPE(ce, is_instanceof, j);
+ } else {
+ int first = 1;
+ int is_instanceof = 0;
+ zend_class_entry *ce = NULL;
+
+ tmp = 0;
+ for (i = 0; i < blocks[p->block].predecessors_count; i++) {
+ tmp |= get_ssa_var_info(ssa, p->sources[i]);
+ }
+ UPDATE_SSA_TYPE(tmp, j);
+ for (i = 0; i < blocks[p->block].predecessors_count; i++) {
+ if (p->sources[i] >= 0) {
+ zend_ssa_var_info *info = &ssa_var_info[p->sources[i]];
+ if (info->type & MAY_BE_OBJECT) {
+ if (first) {
+ ce = info->ce;
+ is_instanceof = info->is_instanceof;
+ first = 0;
+ } else {
+ is_instanceof |= info->is_instanceof;
+ ce = join_class_entries(ce, info->ce, &is_instanceof);
+ }
+ }
+ }
+ }
+ UPDATE_SSA_OBJ_TYPE(ce, ce ? is_instanceof : 0, j);
+ }
+ } else if (ssa_vars[j].definition >= 0) {
+ i = ssa_vars[j].definition;
+ zend_update_type_info(op_array, ssa, script, worklist, i);
+ }
+ }
+ return SUCCESS;
+}
+
+static zend_bool is_narrowable_instr(zend_op *opline) {
+ return opline->opcode == ZEND_ADD || opline->opcode == ZEND_SUB
+ || opline->opcode == ZEND_MUL || opline->opcode == ZEND_DIV;
+}
+
+static zend_bool is_effective_op1_double_cast(zend_op *opline, zval *op2) {
+ return (opline->opcode == ZEND_ADD && Z_LVAL_P(op2) == 0)
+ || (opline->opcode == ZEND_SUB && Z_LVAL_P(op2) == 0)
+ || (opline->opcode == ZEND_MUL && Z_LVAL_P(op2) == 1)
+ || (opline->opcode == ZEND_DIV && Z_LVAL_P(op2) == 1);
+}
+static zend_bool is_effective_op2_double_cast(zend_op *opline, zval *op1) {
+ /* In PHP it holds that (double)(0-$int) is bitwise identical to 0.0-(double)$int,
+ * so allowing SUB here is fine. */
+ return (opline->opcode == ZEND_ADD && Z_LVAL_P(op1) == 0)
+ || (opline->opcode == ZEND_SUB && Z_LVAL_P(op1) == 0)
+ || (opline->opcode == ZEND_MUL && Z_LVAL_P(op1) == 1);
+}
+
+/* This function recursively checks whether it's possible to convert an integer variable
+ * initialization to a double initialization. The basic idea is that if the value is used
+ * only in add/sub/mul/div ("narrowable" instructions) with a double result value, then it
+ * will be cast to double at that point anyway, so we may as well do it earlier already.
+ *
+ * The tricky case are chains of operations, where it's not necessarily a given that converting
+ * an integer to double before the chain of operations is the same as converting it after the
+ * chain. What this function does is detect two cases where it is safe:
+ * * If the operations only involve constants, then we can simply verify that performing the
+ * calculation on integers and doubles yields the same value.
+ * * Even if one operand is not known, we may be able to determine that the operations with the
+ * integer replaced by a double only acts as an effective double cast on the unknown operand.
+ * E.g. 0+$i and 0.0+$i only differ by that cast. If then the consuming instruction of this
+ * result will perform a double cast anyway, the conversion is safe.
+ *
+ * The checks happens recursively, while keeping track of which variables are already visisted to
+ * avoid infinite loops. An iterative, worklist driven approach would be possible, but the state
+ * management more cumbersome to implement, so we don't bother for now.
+ */
+static zend_bool can_convert_to_double(
+ const zend_op_array *op_array, zend_ssa *ssa, int var_num,
+ zval *value, zend_bitset visited) {
+ zend_ssa_var *var = &ssa->vars[var_num];
+ zend_ssa_phi *phi;
+ int use;
+ uint32_t type;
+
+ if (zend_bitset_in(visited, var_num)) {
+ return 1;
+ }
+ zend_bitset_incl(visited, var_num);
+
+ for (use = var->use_chain; use >= 0; use = zend_ssa_next_use(ssa->ops, var_num, use)) {
+ zend_op *opline = &op_array->opcodes[use];
+ zend_ssa_op *ssa_op = &ssa->ops[use];
+
+ if (is_no_val_use(opline, ssa_op, var_num)) {
+ continue;
+ }
+
+ if (!is_narrowable_instr(opline)) {
+ return 0;
+ }
+
+ /* Instruction always returns double, the conversion is certainly fine */
+ type = ssa->var_info[ssa_op->result_def].type;
+ if ((type & MAY_BE_ANY) == MAY_BE_DOUBLE) {
+ continue;
+ }
+
+ /* UNDEF signals that the previous result is an effective double cast, this is only allowed
+ * if this instruction would have done the cast anyway (previous check). */
+ if (Z_ISUNDEF_P(value)) {
+ return 0;
+ }
+
+ /* Check that narrowing can actually be useful */
+ if ((type & MAY_BE_ANY) & ~(MAY_BE_LONG|MAY_BE_DOUBLE)) {
+ return 0;
+ }
+
+ {
+ /* For calculation on original values */
+ zval orig_op1, orig_op2, orig_result;
+ /* For calculation with var_num cast to double */
+ zval dval_op1, dval_op2, dval_result;
+
+ ZVAL_UNDEF(&orig_op1);
+ ZVAL_UNDEF(&dval_op1);
+ if (ssa_op->op1_use == var_num) {
+ ZVAL_COPY_VALUE(&orig_op1, value);
+ ZVAL_DOUBLE(&dval_op1, (double) Z_LVAL_P(value));
+ } else if (opline->op1_type == IS_CONST) {
+ zval *zv = CRT_CONSTANT_EX(op_array, opline->op1, ssa->rt_constants);
+ if (Z_TYPE_P(zv) == IS_LONG || Z_TYPE_P(zv) == IS_DOUBLE) {
+ ZVAL_COPY_VALUE(&orig_op1, zv);
+ ZVAL_COPY_VALUE(&dval_op1, zv);
+ }
+ }
+
+ ZVAL_UNDEF(&orig_op2);
+ ZVAL_UNDEF(&dval_op2);
+ if (ssa_op->op2_use == var_num) {
+ ZVAL_COPY_VALUE(&orig_op2, value);
+ ZVAL_DOUBLE(&dval_op2, (double) Z_LVAL_P(value));
+ } else if (opline->op2_type == IS_CONST) {
+ zval *zv = CRT_CONSTANT_EX(op_array, opline->op2, ssa->rt_constants);
+ if (Z_TYPE_P(zv) == IS_LONG || Z_TYPE_P(zv) == IS_DOUBLE) {
+ ZVAL_COPY_VALUE(&orig_op2, zv);
+ ZVAL_COPY_VALUE(&dval_op2, zv);
+ }
+ }
+
+ ZEND_ASSERT(!Z_ISUNDEF(orig_op1) || !Z_ISUNDEF(orig_op2));
+ if (Z_ISUNDEF(orig_op1)) {
+ if (opline->opcode == ZEND_MUL && Z_LVAL(orig_op2) == 0) {
+ ZVAL_LONG(&orig_result, 0);
+ } else if (is_effective_op1_double_cast(opline, &orig_op2)) {
+ ZVAL_UNDEF(&orig_result);
+ } else {
+ return 0;
+ }
+ } else if (Z_ISUNDEF(orig_op2)) {
+ if (opline->opcode == ZEND_MUL && Z_LVAL(orig_op1) == 0) {
+ ZVAL_LONG(&orig_result, 0);
+ } else if (is_effective_op2_double_cast(opline, &orig_op1)) {
+ ZVAL_UNDEF(&orig_result);
+ } else {
+ return 0;
+ }
+ } else {
+ /* Avoid division by zero */
+ if (opline->opcode == ZEND_DIV && zval_get_double(&orig_op2) == 0.0) {
+ return 0;
+ }
+
+ get_binary_op(opline->opcode)(&orig_result, &orig_op1, &orig_op2);
+ get_binary_op(opline->opcode)(&dval_result, &dval_op1, &dval_op2);
+ ZEND_ASSERT(Z_TYPE(dval_result) == IS_DOUBLE);
+ if (zval_get_double(&orig_result) != Z_DVAL(dval_result)) {
+ return 0;
+ }
+ }
+
+ if (!can_convert_to_double(op_array, ssa, ssa_op->result_def, &orig_result, visited)) {
+ return 0;
+ }
+ }
+ }
+
+ for (phi = var->phi_use_chain; phi; phi = zend_ssa_next_use_phi(ssa, var_num, phi)) {
+ /* Check that narrowing can actually be useful */
+ type = ssa->var_info[phi->ssa_var].type;
+ if ((type & MAY_BE_ANY) & ~(MAY_BE_LONG|MAY_BE_DOUBLE)) {
+ return 0;
+ }
+
+ if (!can_convert_to_double(op_array, ssa, phi->ssa_var, value, visited)) {
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+static int zend_type_narrowing(const zend_op_array *op_array, const zend_script *script, zend_ssa *ssa)
+{
+ uint32_t bitset_len = zend_bitset_len(ssa->vars_count);
+ ALLOCA_FLAG(use_heap);
+ zend_bitset visited = ZEND_BITSET_ALLOCA(2 * bitset_len, use_heap);
+ zend_bitset worklist = visited + bitset_len;
+ int i;
+ uint32_t v;
+ zend_op *opline;
+ zend_bool narrowed = 0;
+
+ zend_bitset_clear(worklist, bitset_len);
+
+ for (v = op_array->last_var; v < ssa->vars_count; v++) {
+ if ((ssa->var_info[v].type & (MAY_BE_REF | MAY_BE_ANY | MAY_BE_UNDEF)) != MAY_BE_LONG) continue;
+ if (ssa->vars[v].definition < 0) continue;
+ if (ssa->vars[v].no_val) continue;
+ opline = op_array->opcodes + ssa->vars[v].definition;
+ /* Go through assignments of literal integers and check if they can be converted to
+ * doubles instead, in the hope that we'll narrow long|double to double. */
+ if (opline->opcode == ZEND_ASSIGN && opline->result_type == IS_UNUSED &&
+ opline->op1_type == IS_CV && opline->op2_type == IS_CONST) {
+ zval *value = CRT_CONSTANT_EX(op_array, opline->op2, ssa->rt_constants);
+
+ zend_bitset_clear(visited, bitset_len);
+ if (can_convert_to_double(op_array, ssa, v, value, visited)) {
+ narrowed = 1;
+ ssa->var_info[v].use_as_double = 1;
+ /* The "visited" vars are exactly those which may change their type due to
+ * narrowing. Reset their types and add them to the type inference worklist */
+ ZEND_BITSET_FOREACH(visited, bitset_len, i) {
+ ssa->var_info[i].type &= ~MAY_BE_ANY;
+ } ZEND_BITSET_FOREACH_END();
+ zend_bitset_union(worklist, visited, bitset_len);
+ }
+ }
+ }
+
+ if (!narrowed) {
+ free_alloca(visited, use_heap);
+ return SUCCESS;
+ }
+
+ if (zend_infer_types_ex(op_array, script, ssa, worklist) != SUCCESS) {
+ free_alloca(visited, use_heap);
+ return FAILURE;
+ }
+
+ free_alloca(visited, use_heap);
+ return SUCCESS;
+}
+
+static int is_recursive_tail_call(const zend_op_array *op_array,
+ zend_op *opline)
+{
+ zend_func_info *info = ZEND_FUNC_INFO(op_array);
+
+ if (info->ssa.ops && info->ssa.vars &&
+ info->ssa.ops[opline - op_array->opcodes].op1_use >= 0 &&
+ info->ssa.vars[info->ssa.ops[opline - op_array->opcodes].op1_use].definition >= 0) {
+
+ zend_op *op = op_array->opcodes + info->ssa.vars[info->ssa.ops[opline - op_array->opcodes].op1_use].definition;
+
+ if (op->opcode == ZEND_DO_UCALL) {
+ zend_call_info *call_info = info->callee_info;
+
+ while (call_info && call_info->caller_call_opline != op) {
+ call_info = call_info->next_callee;
+ }
+ if (call_info && op_array == &call_info->callee_func->op_array) {
+ return 1;
+ }
+ }
+ }
+ return 0;
+}
+
+void zend_init_func_return_info(const zend_op_array *op_array,
+ const zend_script *script,
+ zend_ssa_var_info *ret)
+{
+ if (op_array->fn_flags & ZEND_ACC_HAS_RETURN_TYPE) {
+ zend_arg_info *ret_info = op_array->arg_info - 1;
+ zend_ssa_range tmp_range = {0, 0, 0, 0};
+
+ ret->type = zend_fetch_arg_info(script, ret_info, &ret->ce);
+ if (op_array->fn_flags & ZEND_ACC_RETURN_REFERENCE) {
+ ret->type |= MAY_BE_REF;
+ }
+ ret->is_instanceof = (ret->ce) ? 1 : 0;
+ ret->range = tmp_range;
+ ret->has_range = 0;
+ }
+}
+
+void zend_func_return_info(const zend_op_array *op_array,
+ const zend_script *script,
+ int recursive,
+ int widening,
+ zend_ssa_var_info *ret)
+{
+ zend_func_info *info = ZEND_FUNC_INFO(op_array);
+ zend_ssa *ssa = &info->ssa;
+ int blocks_count = info->ssa.cfg.blocks_count;
+ zend_basic_block *blocks = info->ssa.cfg.blocks;
+ int j;
+ uint32_t t1;
+ uint32_t tmp = 0;
+ zend_class_entry *tmp_ce = NULL;
+ int tmp_is_instanceof = -1;
+ zend_class_entry *arg_ce;
+ int arg_is_instanceof;
+ zend_ssa_range tmp_range = {0, 0, 0, 0};
+ int tmp_has_range = -1;
+
+ if (op_array->fn_flags & ZEND_ACC_GENERATOR) {
+ ret->type = MAY_BE_OBJECT | MAY_BE_RC1 | MAY_BE_RCN;
+ ret->ce = zend_ce_generator;
+ ret->is_instanceof = 0;
+ ret->range = tmp_range;
+ ret->has_range = 0;
+ return;
+ }
+
+ for (j = 0; j < blocks_count; j++) {
+ if ((blocks[j].flags & ZEND_BB_REACHABLE) && blocks[j].len != 0) {
+ zend_op *opline = op_array->opcodes + blocks[j].start + blocks[j].len - 1;
+
+ if (opline->opcode == ZEND_RETURN || opline->opcode == ZEND_RETURN_BY_REF) {
+ if (!recursive &&
+ info->ssa.ops &&
+ info->ssa.var_info &&
+ info->ssa.ops[opline - op_array->opcodes].op1_use >= 0 &&
+ info->ssa.var_info[info->ssa.ops[opline - op_array->opcodes].op1_use].recursive) {
+ continue;
+ }
+ if (is_recursive_tail_call(op_array, opline)) {
+ continue;
+ }
+ t1 = OP1_INFO();
+ if (t1 & MAY_BE_UNDEF) {
+ t1 |= MAY_BE_NULL;
+ }
+ if (opline->opcode == ZEND_RETURN) {
+ t1 |= MAY_BE_RC1 | MAY_BE_RCN;
+ t1 &= ~(MAY_BE_UNDEF | MAY_BE_REF);
+ } else {
+ t1 |= MAY_BE_REF;
+ t1 &= ~(MAY_BE_UNDEF | MAY_BE_RC1 | MAY_BE_RCN);
+ }
+ tmp |= t1;
+
+ if (info->ssa.ops &&
+ info->ssa.var_info &&
+ info->ssa.ops[opline - op_array->opcodes].op1_use >= 0 &&
+ info->ssa.var_info[info->ssa.ops[opline - op_array->opcodes].op1_use].ce) {
+ arg_ce = info->ssa.var_info[info->ssa.ops[opline - op_array->opcodes].op1_use].ce;
+ arg_is_instanceof = info->ssa.var_info[info->ssa.ops[opline - op_array->opcodes].op1_use].is_instanceof;
+ } else {
+ arg_ce = NULL;
+ arg_is_instanceof = 0;
+ }
+
+ if (tmp_is_instanceof < 0) {
+ tmp_ce = arg_ce;
+ tmp_is_instanceof = arg_is_instanceof;
+ } else if (arg_ce && arg_ce == tmp_ce) {
+ if (tmp_is_instanceof != arg_is_instanceof) {
+ tmp_is_instanceof = 1;
+ }
+ } else {
+ tmp_ce = NULL;
+ tmp_is_instanceof = 0;
+ }
+
+ if (opline->op1_type == IS_CONST) {
+ if (Z_TYPE_P(CRT_CONSTANT_EX(op_array, opline->op1, info->ssa.rt_constants)) == IS_NULL) {
+ if (tmp_has_range < 0) {
+ tmp_has_range = 1;
+ tmp_range.underflow = 0;
+ tmp_range.min = 0;
+ tmp_range.max = 0;
+ tmp_range.overflow = 0;
+ } else if (tmp_has_range) {
+ if (!tmp_range.underflow) {
+ tmp_range.min = MIN(tmp_range.min, 0);
+ }
+ if (!tmp_range.overflow) {
+ tmp_range.max = MAX(tmp_range.max, 0);
+ }
+ }
+ } else if (Z_TYPE_P(CRT_CONSTANT_EX(op_array, opline->op1, info->ssa.rt_constants)) == IS_FALSE) {
+ if (tmp_has_range < 0) {
+ tmp_has_range = 1;
+ tmp_range.underflow = 0;
+ tmp_range.min = 0;
+ tmp_range.max = 0;
+ tmp_range.overflow = 0;
+ } else if (tmp_has_range) {
+ if (!tmp_range.underflow) {
+ tmp_range.min = MIN(tmp_range.min, 0);
+ }
+ if (!tmp_range.overflow) {
+ tmp_range.max = MAX(tmp_range.max, 0);
+ }
+ }
+ } else if (Z_TYPE_P(CRT_CONSTANT_EX(op_array, opline->op1, info->ssa.rt_constants)) == IS_TRUE) {
+ if (tmp_has_range < 0) {
+ tmp_has_range = 1;
+ tmp_range.underflow = 0;
+ tmp_range.min = 1;
+ tmp_range.max = 1;
+ tmp_range.overflow = 0;
+ } else if (tmp_has_range) {
+ if (!tmp_range.underflow) {
+ tmp_range.min = MIN(tmp_range.min, 1);
+ }
+ if (!tmp_range.overflow) {
+ tmp_range.max = MAX(tmp_range.max, 1);
+ }
+ }
+ } else if (Z_TYPE_P(CRT_CONSTANT_EX(op_array, opline->op1, info->ssa.rt_constants)) == IS_LONG) {
+ if (tmp_has_range < 0) {
+ tmp_has_range = 1;
+ tmp_range.underflow = 0;
+ tmp_range.min = Z_LVAL_P(CRT_CONSTANT_EX(op_array, opline->op1, info->ssa.rt_constants));
+ tmp_range.max = Z_LVAL_P(CRT_CONSTANT_EX(op_array, opline->op1, info->ssa.rt_constants));
+ tmp_range.overflow = 0;
+ } else if (tmp_has_range) {
+ if (!tmp_range.underflow) {
+ tmp_range.min = MIN(tmp_range.min, Z_LVAL_P(CRT_CONSTANT_EX(op_array, opline->op1, info->ssa.rt_constants)));
+ }
+ if (!tmp_range.overflow) {
+ tmp_range.max = MAX(tmp_range.max, Z_LVAL_P(CRT_CONSTANT_EX(op_array, opline->op1, info->ssa.rt_constants)));
+ }
+ }
+ } else {
+ tmp_has_range = 0;
+ }
+ } else if (info->ssa.ops &&
+ info->ssa.var_info &&
+ info->ssa.ops[opline - op_array->opcodes].op1_use >= 0) {
+ if (info->ssa.var_info[info->ssa.ops[opline - op_array->opcodes].op1_use].has_range) {
+ if (tmp_has_range < 0) {
+ tmp_has_range = 1;
+ tmp_range = info->ssa.var_info[info->ssa.ops[opline - op_array->opcodes].op1_use].range;
+ } else if (tmp_has_range) {
+ /* union */
+ if (info->ssa.var_info[info->ssa.ops[opline - op_array->opcodes].op1_use].range.underflow) {
+ tmp_range.underflow = 1;
+ tmp_range.min = ZEND_LONG_MIN;
+ } else {
+ tmp_range.min = MIN(tmp_range.min, info->ssa.var_info[info->ssa.ops[opline - op_array->opcodes].op1_use].range.min);
+ }
+ if (info->ssa.var_info[info->ssa.ops[opline - op_array->opcodes].op1_use].range.overflow) {
+ tmp_range.overflow = 1;
+ tmp_range.max = ZEND_LONG_MAX;
+ } else {
+ tmp_range.max = MAX(tmp_range.max, info->ssa.var_info[info->ssa.ops[opline - op_array->opcodes].op1_use].range.max);
+ }
+ }
+ } else if (!widening) {
+ tmp_has_range = 1;
+ tmp_range.underflow = 1;
+ tmp_range.min = ZEND_LONG_MIN;
+ tmp_range.max = ZEND_LONG_MAX;
+ tmp_range.overflow = 1;
+ }
+ } else {
+ tmp_has_range = 0;
+ }
+ }
+ }
+ }
+
+ if (!(op_array->fn_flags & ZEND_ACC_HAS_RETURN_TYPE)) {
+ if (tmp_is_instanceof < 0) {
+ tmp_is_instanceof = 0;
+ tmp_ce = NULL;
+ }
+ if (tmp_has_range < 0) {
+ tmp_has_range = 0;
+ }
+ ret->type = tmp;
+ ret->ce = tmp_ce;
+ ret->is_instanceof = tmp_is_instanceof;
+ }
+ ret->range = tmp_range;
+ ret->has_range = tmp_has_range;
+}
+
+static int zend_infer_types(const zend_op_array *op_array, const zend_script *script, zend_ssa *ssa)
+{
+ zend_ssa_var_info *ssa_var_info = ssa->var_info;
+ int ssa_vars_count = ssa->vars_count;
+ int j;
+ zend_bitset worklist;
+ ALLOCA_FLAG(use_heap);
+
+ worklist = do_alloca(sizeof(zend_ulong) * zend_bitset_len(ssa_vars_count), use_heap);
+ memset(worklist, 0, sizeof(zend_ulong) * zend_bitset_len(ssa_vars_count));
+
+ /* Type Inference */
+ for (j = op_array->last_var; j < ssa_vars_count; j++) {
+ zend_bitset_incl(worklist, j);
+ ssa_var_info[j].type = 0;
+ }
+
+ if (zend_infer_types_ex(op_array, script, ssa, worklist) != SUCCESS) {
+ free_alloca(worklist, use_heap);
+ return FAILURE;
+ }
+
+ /* Narrowing integer initialization to doubles */
+ zend_type_narrowing(op_array, script, ssa);
+
+ for (j = 0; j < op_array->last_var; j++) {
+ if (zend_string_equals_literal(op_array->vars[j], "php_errormsg")) {
+ /* Mark all SSA vars for $php_errormsg as references,
+ * to make sure we don't optimize it. */
+ int i;
+ for (i = 0; i < ssa_vars_count; i++) {
+ if (ssa->vars[i].var == j) {
+ ssa_var_info[i].type |= MAY_BE_REF | MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF;
+ }
+ }
+ }
+ }
+
+ if (ZEND_FUNC_INFO(op_array)) {
+ zend_func_return_info(op_array, script, 1, 0, &ZEND_FUNC_INFO(op_array)->return_info);
+ }
+
+ free_alloca(worklist, use_heap);
+ return SUCCESS;
+}
+
+int zend_ssa_inference(zend_arena **arena, const zend_op_array *op_array, const zend_script *script, zend_ssa *ssa) /* {{{ */
+{
+ zend_ssa_var_info *ssa_var_info;
+ int i;
+
+ if (!ssa->var_info) {
+ ssa->var_info = zend_arena_calloc(arena, ssa->vars_count, sizeof(zend_ssa_var_info));
+ }
+ ssa_var_info = ssa->var_info;
+
+ if (!op_array->function_name) {
+ for (i = 0; i < op_array->last_var; i++) {
+ ssa_var_info[i].type = MAY_BE_UNDEF | MAY_BE_RC1 | MAY_BE_RCN | MAY_BE_REF | MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF;
+ ssa_var_info[i].has_range = 0;
+ }
+ } else {
+ for (i = 0; i < op_array->last_var; i++) {
+ ssa_var_info[i].type = MAY_BE_UNDEF | MAY_BE_RCN;
+ ssa_var_info[i].has_range = 0;
+ }
+ }
+ for (i = op_array->last_var; i < ssa->vars_count; i++) {
+ ssa_var_info[i].type = 0;
+ ssa_var_info[i].has_range = 0;
+ }
+
+ if (zend_infer_ranges(op_array, ssa) != SUCCESS) {
+ return FAILURE;
+ }
+
+ if (zend_infer_types(op_array, script, ssa) != SUCCESS) {
+ return FAILURE;
+ }
+
+ return SUCCESS;
+}
+/* }}} */
+
+void zend_inference_check_recursive_dependencies(zend_op_array *op_array)
+{
+ zend_func_info *info = ZEND_FUNC_INFO(op_array);
+ zend_call_info *call_info;
+ zend_bitset worklist;
+ int worklist_len;
+ ALLOCA_FLAG(use_heap);
+
+ if (!info->ssa.var_info || !(info->flags & ZEND_FUNC_RECURSIVE)) {
+ return;
+ }
+ worklist_len = zend_bitset_len(info->ssa.vars_count);
+ worklist = do_alloca(sizeof(zend_ulong) * worklist_len, use_heap);
+ memset(worklist, 0, sizeof(zend_ulong) * worklist_len);
+ call_info = info->callee_info;
+ while (call_info) {
+ if (call_info->recursive &&
+ info->ssa.ops[call_info->caller_call_opline - op_array->opcodes].result_def >= 0) {
+ zend_bitset_incl(worklist, info->ssa.ops[call_info->caller_call_opline - op_array->opcodes].result_def);
+ }
+ call_info = call_info->next_callee;
+ }
+ while (!zend_bitset_empty(worklist, worklist_len)) {
+ int i = zend_bitset_first(worklist, worklist_len);
+ zend_bitset_excl(worklist, i);
+ if (!info->ssa.var_info[i].recursive) {
+ info->ssa.var_info[i].recursive = 1;
+ add_usages(op_array, &info->ssa, worklist, i);
+ }
+ }
+ free_alloca(worklist, use_heap);
+}
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * indent-tabs-mode: t
+ * End:
+ */
diff --git a/ext/opcache/Optimizer/zend_inference.h b/ext/opcache/Optimizer/zend_inference.h
new file mode 100644
index 0000000000..6bf68090c5
--- /dev/null
+++ b/ext/opcache/Optimizer/zend_inference.h
@@ -0,0 +1,263 @@
+/*
+ +----------------------------------------------------------------------+
+ | Zend Engine, e-SSA based Type & Range Inference |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1998-2016 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> |
+ +----------------------------------------------------------------------+
+*/
+
+#ifndef ZEND_INFERENCE_H
+#define ZEND_INFERENCE_H
+
+#include "zend_optimizer.h"
+#include "zend_ssa.h"
+#include "zend_bitset.h"
+
+/* Bitmask for type inference (zend_ssa_var_info.type) */
+#include "zend_type_info.h"
+
+#define MAY_BE_IN_REG (1<<25) /* value allocated in CPU register */
+
+//TODO: remome MAY_BE_RC1, MAY_BE_RCN???
+#define MAY_BE_RC1 (1<<27) /* may be non-reference with refcount == 1 */
+#define MAY_BE_RCN (1<<28) /* may be non-reference with refcount > 1 */
+
+
+#define DEFINE_SSA_OP_HAS_RANGE(opN) \
+ static zend_always_inline zend_bool _ssa_##opN##_has_range(const zend_op_array *op_array, const zend_ssa *ssa, const zend_op *opline) \
+ { \
+ if (opline->opN##_type == IS_CONST) { \
+ zval *zv = CRT_CONSTANT_EX(op_array, opline->opN, ssa->rt_constants); \
+ return (Z_TYPE_P(zv) == IS_LONG || Z_TYPE_P(zv) == IS_TRUE || Z_TYPE_P(zv) == IS_FALSE || Z_TYPE_P(zv) == IS_NULL); \
+ } else { \
+ return (opline->opN##_type != IS_UNUSED && \
+ ssa->ops && \
+ ssa->var_info && \
+ ssa->ops[opline - op_array->opcodes].opN##_use >= 0 && \
+ ssa->var_info[ssa->ops[opline - op_array->opcodes].opN##_use].has_range); \
+ } \
+ return 0; \
+ }
+
+#define DEFINE_SSA_OP_MIN_RANGE(opN) \
+ static zend_always_inline zend_long _ssa_##opN##_min_range(const zend_op_array *op_array, const zend_ssa *ssa, const zend_op *opline) \
+ { \
+ if (opline->opN##_type == IS_CONST) { \
+ zval *zv = CRT_CONSTANT_EX(op_array, opline->opN, ssa->rt_constants); \
+ if (Z_TYPE_P(zv) == IS_LONG) { \
+ return Z_LVAL_P(zv); \
+ } else if (Z_TYPE_P(zv) == IS_TRUE) { \
+ return 1; \
+ } else if (Z_TYPE_P(zv) == IS_FALSE) { \
+ return 0; \
+ } else if (Z_TYPE_P(zv) == IS_NULL) { \
+ return 0; \
+ } \
+ } else if (opline->opN##_type != IS_UNUSED && \
+ ssa->ops && \
+ ssa->var_info && \
+ ssa->ops[opline - op_array->opcodes].opN##_use >= 0 && \
+ ssa->var_info[ssa->ops[opline - op_array->opcodes].opN##_use].has_range) { \
+ return ssa->var_info[ssa->ops[opline - op_array->opcodes].opN##_use].range.min; \
+ } \
+ return ZEND_LONG_MIN; \
+ }
+
+#define DEFINE_SSA_OP_MAX_RANGE(opN) \
+ static zend_always_inline zend_long _ssa_##opN##_max_range(const zend_op_array *op_array, const zend_ssa *ssa, const zend_op *opline) \
+ { \
+ if (opline->opN##_type == IS_CONST) { \
+ zval *zv = CRT_CONSTANT_EX(op_array, opline->opN, ssa->rt_constants); \
+ if (Z_TYPE_P(zv) == IS_LONG) { \
+ return Z_LVAL_P(zv); \
+ } else if (Z_TYPE_P(zv) == IS_TRUE) { \
+ return 1; \
+ } else if (Z_TYPE_P(zv) == IS_FALSE) { \
+ return 0; \
+ } else if (Z_TYPE_P(zv) == IS_NULL) { \
+ return 0; \
+ } \
+ } else if (opline->opN##_type != IS_UNUSED && \
+ ssa->ops && \
+ ssa->var_info && \
+ ssa->ops[opline - op_array->opcodes].opN##_use >= 0 && \
+ ssa->var_info[ssa->ops[opline - op_array->opcodes].opN##_use].has_range) { \
+ return ssa->var_info[ssa->ops[opline - op_array->opcodes].opN##_use].range.max; \
+ } \
+ return ZEND_LONG_MAX; \
+ }
+
+#define DEFINE_SSA_OP_RANGE_UNDERFLOW(opN) \
+ static zend_always_inline char _ssa_##opN##_range_underflow(const zend_op_array *op_array, const zend_ssa *ssa, const zend_op *opline) \
+ { \
+ if (opline->opN##_type == IS_CONST) { \
+ zval *zv = CRT_CONSTANT_EX(op_array, opline->opN, ssa->rt_constants); \
+ if (Z_TYPE_P(zv) == IS_LONG || Z_TYPE_P(zv) == IS_TRUE || Z_TYPE_P(zv) == IS_FALSE || Z_TYPE_P(zv) == IS_NULL) { \
+ return 0; \
+ } \
+ } else if (opline->opN##_type != IS_UNUSED && \
+ ssa->ops && \
+ ssa->var_info && \
+ ssa->ops[opline - op_array->opcodes].opN##_use >= 0 && \
+ ssa->var_info[ssa->ops[opline - op_array->opcodes].opN##_use].has_range) { \
+ return ssa->var_info[ssa->ops[opline - op_array->opcodes].opN##_use].range.underflow; \
+ } \
+ return 1; \
+ }
+
+#define DEFINE_SSA_OP_RANGE_OVERFLOW(opN) \
+ static zend_always_inline char _ssa_##opN##_range_overflow(const zend_op_array *op_array, const zend_ssa *ssa, const zend_op *opline) \
+ { \
+ if (opline->opN##_type == IS_CONST) { \
+ zval *zv = CRT_CONSTANT_EX(op_array, opline->opN, ssa->rt_constants); \
+ if (Z_TYPE_P(zv) == IS_LONG || Z_TYPE_P(zv) == IS_TRUE || Z_TYPE_P(zv) == IS_FALSE || Z_TYPE_P(zv) == IS_NULL) { \
+ return 0; \
+ } \
+ } else if (opline->opN##_type != IS_UNUSED && \
+ ssa->ops && \
+ ssa->var_info && \
+ ssa->ops[opline - op_array->opcodes].opN##_use >= 0 && \
+ ssa->var_info[ssa->ops[opline - op_array->opcodes].opN##_use].has_range) { \
+ return ssa->var_info[ssa->ops[opline - op_array->opcodes].opN##_use].range.overflow; \
+ } \
+ return 1; \
+ }
+
+DEFINE_SSA_OP_HAS_RANGE(op1)
+DEFINE_SSA_OP_MIN_RANGE(op1)
+DEFINE_SSA_OP_MAX_RANGE(op1)
+DEFINE_SSA_OP_RANGE_UNDERFLOW(op1)
+DEFINE_SSA_OP_RANGE_OVERFLOW(op1)
+DEFINE_SSA_OP_HAS_RANGE(op2)
+DEFINE_SSA_OP_MIN_RANGE(op2)
+DEFINE_SSA_OP_MAX_RANGE(op2)
+DEFINE_SSA_OP_RANGE_UNDERFLOW(op2)
+DEFINE_SSA_OP_RANGE_OVERFLOW(op2)
+
+#define OP1_HAS_RANGE() (_ssa_op1_has_range (op_array, ssa, opline))
+#define OP1_MIN_RANGE() (_ssa_op1_min_range (op_array, ssa, opline))
+#define OP1_MAX_RANGE() (_ssa_op1_max_range (op_array, ssa, opline))
+#define OP1_RANGE_UNDERFLOW() (_ssa_op1_range_underflow (op_array, ssa, opline))
+#define OP1_RANGE_OVERFLOW() (_ssa_op1_range_overflow (op_array, ssa, opline))
+#define OP2_HAS_RANGE() (_ssa_op2_has_range (op_array, ssa, opline))
+#define OP2_MIN_RANGE() (_ssa_op2_min_range (op_array, ssa, opline))
+#define OP2_MAX_RANGE() (_ssa_op2_max_range (op_array, ssa, opline))
+#define OP2_RANGE_UNDERFLOW() (_ssa_op2_range_underflow (op_array, ssa, opline))
+#define OP2_RANGE_OVERFLOW() (_ssa_op2_range_overflow (op_array, ssa, opline))
+
+static zend_always_inline uint32_t _const_op_type(const zval *zv) {
+ if (Z_TYPE_P(zv) == IS_CONSTANT) {
+ return MAY_BE_RC1 | MAY_BE_RCN | MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY;
+ } else if (Z_TYPE_P(zv) == IS_CONSTANT_AST) {
+ return MAY_BE_RC1 | MAY_BE_RCN | MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY;
+ } else if (Z_TYPE_P(zv) == IS_ARRAY) {
+ HashTable *ht = Z_ARRVAL_P(zv);
+ uint32_t tmp = MAY_BE_ARRAY | MAY_BE_RC1 | MAY_BE_RCN;
+
+ zend_string *str;
+ zval *val;
+ ZEND_HASH_FOREACH_STR_KEY_VAL(ht, str, val) {
+ if (str) {
+ tmp |= MAY_BE_ARRAY_KEY_STRING;
+ } else {
+ tmp |= MAY_BE_ARRAY_KEY_LONG;
+ }
+ tmp |= 1 << (Z_TYPE_P(val) + MAY_BE_ARRAY_SHIFT);
+ } ZEND_HASH_FOREACH_END();
+ return tmp;
+ } else {
+ return (1 << Z_TYPE_P(zv)) | MAY_BE_RC1 | MAY_BE_RCN;
+ }
+}
+
+static zend_always_inline uint32_t get_ssa_var_info(const zend_ssa *ssa, int ssa_var_num)
+{
+ if (ssa->var_info && ssa_var_num >= 0) {
+ return ssa->var_info[ssa_var_num].type;
+ } else {
+ return MAY_BE_UNDEF | MAY_BE_RC1 | MAY_BE_RCN | MAY_BE_REF | MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ERROR;
+ }
+}
+
+#define DEFINE_SSA_OP_INFO(opN) \
+ static zend_always_inline uint32_t _ssa_##opN##_info(const zend_op_array *op_array, const zend_ssa *ssa, const zend_op *opline) \
+ { \
+ if (opline->opN##_type == IS_CONST) { \
+ return _const_op_type(CRT_CONSTANT_EX(op_array, opline->opN, ssa->rt_constants)); \
+ } else { \
+ return get_ssa_var_info(ssa, ssa->ops ? ssa->ops[opline - op_array->opcodes].opN##_use : -1); \
+ } \
+ }
+
+#define DEFINE_SSA_OP_DEF_INFO(opN) \
+ static zend_always_inline uint32_t _ssa_##opN##_def_info(const zend_op_array *op_array, const zend_ssa *ssa, const zend_op *opline) \
+ { \
+ return get_ssa_var_info(ssa, ssa->ops ? ssa->ops[opline - op_array->opcodes].opN##_def : -1); \
+ }
+
+
+DEFINE_SSA_OP_INFO(op1)
+DEFINE_SSA_OP_INFO(op2)
+DEFINE_SSA_OP_INFO(result)
+DEFINE_SSA_OP_DEF_INFO(op1)
+DEFINE_SSA_OP_DEF_INFO(op2)
+DEFINE_SSA_OP_DEF_INFO(result)
+
+#define OP1_INFO() (_ssa_op1_info(op_array, ssa, opline))
+#define OP2_INFO() (_ssa_op2_info(op_array, ssa, opline))
+#define OP1_DATA_INFO() (_ssa_op1_info(op_array, ssa, (opline+1)))
+#define OP2_DATA_INFO() (_ssa_op2_info(op_array, ssa, (opline+1)))
+#define RES_USE_INFO() (_ssa_result_info(op_array, ssa, opline))
+#define OP1_DEF_INFO() (_ssa_op1_def_info(op_array, ssa, opline))
+#define OP2_DEF_INFO() (_ssa_op2_def_info(op_array, ssa, opline))
+#define OP1_DATA_DEF_INFO() (_ssa_op1_def_info(op_array, ssa, (opline+1)))
+#define OP2_DATA_DEF_INFO() (_ssa_op2_def_info(op_array, ssa, (opline+1)))
+#define RES_INFO() (_ssa_result_def_info(op_array, ssa, opline))
+
+
+BEGIN_EXTERN_C()
+
+int zend_ssa_find_false_dependencies(const zend_op_array *op_array, zend_ssa *ssa);
+int zend_ssa_find_sccs(const zend_op_array *op_array, zend_ssa *ssa);
+int zend_ssa_inference(zend_arena **raena, const zend_op_array *op_array, const zend_script *script, zend_ssa *ssa);
+
+uint32_t zend_array_element_type(uint32_t t1, int write, int insert);
+
+int zend_inference_calc_range(const zend_op_array *op_array, zend_ssa *ssa, int var, int widening, int narrowing, zend_ssa_range *tmp);
+void zend_inference_init_range(const zend_op_array *op_array, zend_ssa *ssa, int var, zend_bool underflow, zend_long min, zend_long max, zend_bool overflow);
+int zend_inference_narrowing_meet(zend_ssa_var_info *var_info, zend_ssa_range *r);
+int zend_inference_widening_meet(zend_ssa_var_info *var_info, zend_ssa_range *r);
+void zend_inference_check_recursive_dependencies(zend_op_array *op_array);
+
+int zend_infer_types_ex(const zend_op_array *op_array, const zend_script *script, zend_ssa *ssa, zend_bitset worklist);
+
+void zend_init_func_return_info(const zend_op_array *op_array,
+ const zend_script *script,
+ zend_ssa_var_info *ret);
+void zend_func_return_info(const zend_op_array *op_array,
+ const zend_script *script,
+ int recursive,
+ int widening,
+ zend_ssa_var_info *ret);
+
+END_EXTERN_C()
+
+#endif /* ZEND_INFERENCE_H */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * indent-tabs-mode: t
+ * End:
+ */
diff --git a/ext/opcache/Optimizer/zend_optimizer.c b/ext/opcache/Optimizer/zend_optimizer.c
index 332fa5dc47..35dff98362 100644
--- a/ext/opcache/Optimizer/zend_optimizer.c
+++ b/ext/opcache/Optimizer/zend_optimizer.c
@@ -26,6 +26,15 @@
#include "zend_constants.h"
#include "zend_execute.h"
#include "zend_vm.h"
+#include "zend_cfg.h"
+#include "zend_func_info.h"
+#include "zend_call_graph.h"
+#include "zend_inference.h"
+#include "zend_dump.h"
+
+#ifndef HAVE_DFA_PASS
+# define HAVE_DFA_PASS 1
+#endif
static void zend_optimizer_zval_dtor_wrapper(zval *zvalue)
{
@@ -88,11 +97,6 @@ int zend_optimizer_lookup_cv(zend_op_array *op_array, zend_string* name)
if (opline->result_type & (IS_TMP_VAR|IS_VAR)) {
opline->result.var += sizeof(zval);
}
- if (opline->opcode == ZEND_DECLARE_INHERITED_CLASS ||
- opline->opcode == ZEND_DECLARE_ANON_INHERITED_CLASS ||
- opline->opcode == ZEND_DECLARE_INHERITED_CLASS_DELAYED) {
- opline->extended_value += sizeof(zval);
- }
opline++;
}
}
@@ -168,6 +172,7 @@ int zend_optimizer_update_op1_const(zend_op_array *op_array,
case ZEND_INIT_STATIC_METHOD_CALL:
case ZEND_CATCH:
case ZEND_FETCH_CONSTANT:
+ case ZEND_FETCH_CLASS_CONSTANT:
case ZEND_DEFINED:
case ZEND_NEW:
REQUIRES_STRING(val);
@@ -176,6 +181,18 @@ int zend_optimizer_update_op1_const(zend_op_array *op_array,
alloc_cache_slots_op1(op_array, opline, 1);
zend_optimizer_add_literal_string(op_array, zend_string_tolower(Z_STR_P(val)));
break;
+ case ZEND_FETCH_STATIC_PROP_R:
+ case ZEND_FETCH_STATIC_PROP_W:
+ case ZEND_FETCH_STATIC_PROP_RW:
+ case ZEND_FETCH_STATIC_PROP_IS:
+ case ZEND_FETCH_STATIC_PROP_UNSET:
+ case ZEND_FETCH_STATIC_PROP_FUNC_ARG:
+ TO_STRING_NOWARN(val);
+ opline->op1.constant = zend_optimizer_add_literal(op_array, val);
+ alloc_cache_slots_op1(op_array, opline, 2);
+ break;
+ case ZEND_CONCAT:
+ case ZEND_FAST_CONCAT:
case ZEND_FETCH_R:
case ZEND_FETCH_W:
case ZEND_FETCH_RW:
@@ -183,14 +200,6 @@ int zend_optimizer_update_op1_const(zend_op_array *op_array,
case ZEND_FETCH_UNSET:
case ZEND_FETCH_FUNC_ARG:
TO_STRING_NOWARN(val);
- opline->op1.constant = zend_optimizer_add_literal(op_array, val);
- if (opline->extended_value == ZEND_FETCH_STATIC_MEMBER) {
- alloc_cache_slots_op1(op_array, opline, 2);
- }
- break;
- case ZEND_CONCAT:
- case ZEND_FAST_CONCAT:
- TO_STRING_NOWARN(val);
/* break missing intentionally */
default:
opline->op1.constant = zend_optimizer_add_literal(op_array, val);
@@ -212,20 +221,20 @@ int zend_optimizer_update_op2_const(zend_op_array *op_array,
case ZEND_ASSIGN_REF:
zval_dtor(val);
return 0;
- case ZEND_FETCH_R:
- case ZEND_FETCH_W:
- case ZEND_FETCH_RW:
- case ZEND_FETCH_IS:
- case ZEND_FETCH_UNSET:
- case ZEND_FETCH_FUNC_ARG:
case ZEND_FETCH_CLASS:
case ZEND_INIT_FCALL_BY_NAME:
/*case ZEND_INIT_NS_FCALL_BY_NAME:*/
- case ZEND_UNSET_VAR:
- case ZEND_ISSET_ISEMPTY_VAR:
case ZEND_ADD_INTERFACE:
case ZEND_ADD_TRAIT:
case ZEND_INSTANCEOF:
+ case ZEND_FETCH_STATIC_PROP_R:
+ case ZEND_FETCH_STATIC_PROP_W:
+ case ZEND_FETCH_STATIC_PROP_RW:
+ case ZEND_FETCH_STATIC_PROP_IS:
+ case ZEND_FETCH_STATIC_PROP_UNSET:
+ case ZEND_FETCH_STATIC_PROP_FUNC_ARG:
+ case ZEND_UNSET_STATIC_PROP:
+ case ZEND_ISSET_ISEMPTY_STATIC_PROP:
REQUIRES_STRING(val);
drop_leading_backslash(val);
opline->op2.constant = zend_optimizer_add_literal(op_array, val);
@@ -245,6 +254,13 @@ int zend_optimizer_update_op2_const(zend_op_array *op_array,
return 0;
}
+ if (zend_optimizer_classify_function(Z_STR_P(val), opline->extended_value)) {
+ /* Dynamic call to various special functions must stay dynamic,
+ * otherwise would drop a warning */
+ zval_dtor(val);
+ return 0;
+ }
+
opline->opcode = ZEND_INIT_FCALL_BY_NAME;
drop_leading_backslash(val);
opline->op2.constant = zend_optimizer_add_literal(op_array, val);
@@ -261,7 +277,7 @@ int zend_optimizer_update_op2_const(zend_op_array *op_array,
zend_optimizer_add_literal_string(op_array, zend_string_tolower(Z_STR_P(val)));
alloc_cache_slots_op2(op_array, opline, 2);
break;
- /*case ZEND_FETCH_CONSTANT:*/
+ /*case ZEND_FETCH_CLASS_CONSTANT:*/
case ZEND_ASSIGN_OBJ:
case ZEND_FETCH_OBJ_R:
case ZEND_FETCH_OBJ_W:
@@ -299,26 +315,6 @@ int zend_optimizer_update_op2_const(zend_op_array *op_array,
opline->op2.constant = zend_optimizer_add_literal(op_array, val);
}
break;
- case ZEND_OP_DATA:
- if ((opline-1)->opcode != ZEND_ASSIGN_DIM &&
- ((opline-1)->extended_value != ZEND_ASSIGN_DIM ||
- ((opline-1)->opcode != ZEND_ASSIGN_ADD &&
- (opline-1)->opcode != ZEND_ASSIGN_SUB &&
- (opline-1)->opcode != ZEND_ASSIGN_MUL &&
- (opline-1)->opcode != ZEND_ASSIGN_DIV &&
- (opline-1)->opcode != ZEND_ASSIGN_POW &&
- (opline-1)->opcode != ZEND_ASSIGN_MOD &&
- (opline-1)->opcode != ZEND_ASSIGN_SL &&
- (opline-1)->opcode != ZEND_ASSIGN_SR &&
- (opline-1)->opcode != ZEND_ASSIGN_CONCAT &&
- (opline-1)->opcode != ZEND_ASSIGN_BW_OR &&
- (opline-1)->opcode != ZEND_ASSIGN_BW_AND &&
- (opline-1)->opcode != ZEND_ASSIGN_BW_XOR))
- ) {
- opline->op2.constant = zend_optimizer_add_literal(op_array, val);
- break;
- }
- /* break missing intentionally */
case ZEND_ISSET_ISEMPTY_DIM_OBJ:
case ZEND_ADD_ARRAY_ELEMENT:
case ZEND_INIT_ARRAY:
@@ -359,6 +355,47 @@ int zend_optimizer_update_op2_const(zend_op_array *op_array,
return 1;
}
+void zend_optimizer_remove_live_range(zend_op_array *op_array, uint32_t var)
+{
+ if (op_array->last_live_range) {
+ int i = 0;
+ int j = 0;
+ uint32_t *map;
+ ALLOCA_FLAG(use_heap);
+
+ map = (uint32_t *)do_alloca(sizeof(uint32_t) * op_array->last_live_range, use_heap);
+
+ do {
+ if ((op_array->live_range[i].var & ~ZEND_LIVE_MASK) != var) {
+ map[i] = j;
+ if (i != j) {
+ op_array->live_range[j] = op_array->live_range[i];
+ }
+ j++;
+ }
+ i++;
+ } while (i < op_array->last_live_range);
+ if (i != j) {
+ if ((op_array->last_live_range = j)) {
+ zend_op *opline = op_array->opcodes;
+ zend_op *end = opline + op_array->last;
+
+ while (opline != end) {
+ if ((opline->opcode == ZEND_FREE || opline->opcode == ZEND_FE_FREE) &&
+ opline->extended_value == ZEND_FREE_ON_RETURN) {
+ opline->op2.num = map[opline->op2.num];
+ }
+ opline++;
+ }
+ } else {
+ efree(op_array->live_range);
+ op_array->live_range = NULL;
+ }
+ }
+ free_alloca(map, use_heap);
+ }
+}
+
int zend_optimizer_replace_by_const(zend_op_array *op_array,
zend_op *opline,
zend_uchar type,
@@ -389,17 +426,10 @@ int zend_optimizer_replace_by_const(zend_op_array *op_array,
opline->opcode = ZEND_SEND_VAL_EX;
break;
case ZEND_SEND_VAR_NO_REF:
- if (opline->extended_value & ZEND_ARG_COMPILE_TIME_BOUND) {
- if (opline->extended_value & ZEND_ARG_SEND_BY_REF) {
- zval_dtor(val);
- return 0;
- }
- opline->extended_value = 0;
- opline->opcode = ZEND_SEND_VAL_EX;
- } else {
- opline->extended_value = 0;
- opline->opcode = ZEND_SEND_VAL;
- }
+ zval_dtor(val);
+ return 0;
+ case ZEND_SEND_VAR_NO_REF_EX:
+ opline->opcode = ZEND_SEND_VAL;
break;
case ZEND_SEND_USER:
opline->opcode = ZEND_SEND_VAL_EX;
@@ -413,11 +443,11 @@ int zend_optimizer_replace_by_const(zend_op_array *op_array,
case ZEND_FREE:
case ZEND_CASE: {
zend_op *m, *n;
- int brk = op_array->last_brk_cont;
+ int brk = op_array->last_live_range;
zend_bool in_switch = 0;
while (brk--) {
- if (op_array->brk_cont_array[brk].start <= (opline - op_array->opcodes) &&
- op_array->brk_cont_array[brk].brk > (opline - op_array->opcodes)) {
+ if (op_array->live_range[brk].start <= (uint32_t)(opline - op_array->opcodes) &&
+ op_array->live_range[brk].end > (uint32_t)(opline - op_array->opcodes)) {
in_switch = 1;
break;
}
@@ -431,7 +461,13 @@ int zend_optimizer_replace_by_const(zend_op_array *op_array,
}
m = opline;
- n = op_array->opcodes + op_array->brk_cont_array[brk].brk + 1;
+ n = op_array->opcodes + op_array->live_range[brk].end;
+ if (n->opcode == ZEND_FREE &&
+ !(n->extended_value & ZEND_FREE_ON_RETURN)) {
+ n++;
+ } else {
+ n = op_array->opcodes + op_array->last;
+ }
while (m < n) {
if (ZEND_OP1_TYPE(m) == type &&
ZEND_OP1(m).var == var) {
@@ -450,6 +486,7 @@ int zend_optimizer_replace_by_const(zend_op_array *op_array,
m++;
}
zval_dtor(val);
+ zend_optimizer_remove_live_range(op_array, var);
return 1;
}
case ZEND_VERIFY_RETURN_TYPE: {
@@ -463,18 +500,26 @@ int zend_optimizer_replace_by_const(zend_op_array *op_array,
return 0;
}
MAKE_NOP(opline);
- zend_optimizer_update_op1_const(op_array, opline + 1, val);
- return 1;
+ opline++;
+ break;
}
default:
break;
}
- return zend_optimizer_update_op1_const(op_array, opline, val);
+ if (zend_optimizer_update_op1_const(op_array, opline, val)) {
+ zend_optimizer_remove_live_range(op_array, var);
+ return 1;
+ }
+ return 0;
}
if (ZEND_OP2_TYPE(opline) == type &&
ZEND_OP2(opline).var == var) {
- return zend_optimizer_update_op2_const(op_array, opline, val);
+ if (zend_optimizer_update_op2_const(op_array, opline, val)) {
+ zend_optimizer_remove_live_range(op_array, var);
+ return 1;
+ }
+ return 0;
}
opline++;
}
@@ -482,6 +527,94 @@ int zend_optimizer_replace_by_const(zend_op_array *op_array,
return 1;
}
+zend_function *zend_optimizer_get_called_func(
+ zend_script *script, zend_op_array *op_array, zend_op *opline, zend_bool rt_constants)
+{
+#define GET_OP(op) CRT_CONSTANT_EX(op_array, opline->op, rt_constants)
+ switch (opline->opcode) {
+ case ZEND_INIT_FCALL:
+ {
+ zend_string *function_name = Z_STR_P(GET_OP(op2));
+ zend_function *func;
+ if ((func = zend_hash_find_ptr(&script->function_table, function_name)) != NULL) {
+ return func;
+ } else if ((func = zend_hash_find_ptr(EG(function_table), function_name)) != NULL) {
+ ZEND_ASSERT(func->type == ZEND_INTERNAL_FUNCTION);
+ return func;
+ }
+ break;
+ }
+ case ZEND_INIT_FCALL_BY_NAME:
+ case ZEND_INIT_NS_FCALL_BY_NAME:
+ if (opline->op2_type == IS_CONST && Z_TYPE_P(GET_OP(op2)) == IS_STRING) {
+ zval *function_name = GET_OP(op2) + 1;
+ return zend_hash_find_ptr(&script->function_table, Z_STR_P(function_name));
+ }
+ break;
+ case ZEND_INIT_STATIC_METHOD_CALL:
+ if (opline->op2_type == IS_CONST && Z_TYPE_P(GET_OP(op2)) == IS_STRING) {
+ zend_class_entry *ce = NULL;
+ if (opline->op1_type == IS_CONST && Z_TYPE_P(GET_OP(op1)) == IS_STRING) {
+ zend_string *class_name = Z_STR_P(GET_OP(op1) + 1);
+ ce = zend_hash_find_ptr(&script->class_table, class_name);
+ } else if (opline->op1_type == IS_UNUSED && op_array->scope
+ && !(op_array->scope->ce_flags & ZEND_ACC_TRAIT)
+ && (opline->op1.num & ZEND_FETCH_CLASS_MASK) == ZEND_FETCH_CLASS_SELF) {
+ ce = op_array->scope;
+ }
+ if (ce) {
+ zend_string *func_name = Z_STR_P(GET_OP(op2) + 1);
+ return zend_hash_find_ptr(&ce->function_table, func_name);
+ }
+ }
+ break;
+ case ZEND_INIT_METHOD_CALL:
+ if (opline->op1_type == IS_UNUSED
+ && opline->op2_type == IS_CONST && Z_TYPE_P(GET_OP(op2)) == IS_STRING
+ && op_array->scope && !(op_array->scope->ce_flags & ZEND_ACC_TRAIT)) {
+ zend_string *method_name = Z_STR_P(GET_OP(op2) + 1);
+ zend_function *fbc = zend_hash_find_ptr(
+ &op_array->scope->function_table, method_name);
+ if (fbc) {
+ zend_bool is_private = (fbc->common.fn_flags & ZEND_ACC_PRIVATE) != 0;
+ zend_bool is_final = (fbc->common.fn_flags & ZEND_ACC_FINAL) != 0;
+ zend_bool same_scope = fbc->common.scope == op_array->scope;
+ if ((is_private && same_scope)
+ || (is_final && (!is_private || same_scope))) {
+ return fbc;
+ }
+ }
+ }
+ break;
+ }
+ return NULL;
+#undef GET_OP
+}
+
+uint32_t zend_optimizer_classify_function(zend_string *name, uint32_t num_args) {
+ if (zend_string_equals_literal(name, "extract")) {
+ return ZEND_FUNC_INDIRECT_VAR_ACCESS;
+ } else if (zend_string_equals_literal(name, "compact")) {
+ return ZEND_FUNC_INDIRECT_VAR_ACCESS;
+ } else if (zend_string_equals_literal(name, "parse_str") && num_args <= 1) {
+ return ZEND_FUNC_INDIRECT_VAR_ACCESS;
+ } else if (zend_string_equals_literal(name, "mb_parse_str") && num_args <= 1) {
+ return ZEND_FUNC_INDIRECT_VAR_ACCESS;
+ } else if (zend_string_equals_literal(name, "get_defined_vars")) {
+ return ZEND_FUNC_INDIRECT_VAR_ACCESS;
+ } else if (zend_string_equals_literal(name, "assert")) {
+ return ZEND_FUNC_INDIRECT_VAR_ACCESS;
+ } else if (zend_string_equals_literal(name, "func_num_args")) {
+ return ZEND_FUNC_VARARG;
+ } else if (zend_string_equals_literal(name, "func_get_arg")) {
+ return ZEND_FUNC_VARARG;
+ } else if (zend_string_equals_literal(name, "func_get_args")) {
+ return ZEND_FUNC_VARARG;
+ } else {
+ return 0;
+ }
+}
+
static void zend_optimize(zend_op_array *op_array,
zend_optimizer_ctx *ctx)
{
@@ -489,24 +622,33 @@ static void zend_optimize(zend_op_array *op_array,
return;
}
+ if (ctx->debug_level & ZEND_DUMP_BEFORE_OPTIMIZER) {
+ zend_dump_op_array(op_array, 0, "before optimizer", NULL);
+ }
+
/* pass 1
* - substitute persistent constants (true, false, null, etc)
* - perform compile-time evaluation of constant binary and unary operations
* - optimize series of ADD_STRING and/or ADD_CHAR
* - convert CAST(IS_BOOL,x) into BOOL(x)
+ * - pre-evaluate constant function calls
*/
- if (ZEND_OPTIMIZER_PASS_1 & OPTIMIZATION_LEVEL) {
+ if (ZEND_OPTIMIZER_PASS_1 & ctx->optimization_level) {
zend_optimizer_pass1(op_array, ctx);
+ if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_1) {
+ zend_dump_op_array(op_array, 0, "after pass 1", NULL);
+ }
}
/* pass 2:
* - convert non-numeric constants to numeric constants in numeric operators
* - optimize constant conditional JMPs
- * - optimize static BRKs and CONTs
- * - pre-evaluate constant function calls
*/
- if (ZEND_OPTIMIZER_PASS_2 & OPTIMIZATION_LEVEL) {
+ if (ZEND_OPTIMIZER_PASS_2 & ctx->optimization_level) {
zend_optimizer_pass2(op_array);
+ if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_2) {
+ zend_dump_op_array(op_array, 0, "after pass 2", NULL);
+ }
}
/* pass 3:
@@ -514,52 +656,85 @@ static void zend_optimize(zend_op_array *op_array,
* - optimize series of JMPs
* - change $i++ to ++$i where possible
*/
- if (ZEND_OPTIMIZER_PASS_3 & OPTIMIZATION_LEVEL) {
+ if (ZEND_OPTIMIZER_PASS_3 & ctx->optimization_level) {
zend_optimizer_pass3(op_array);
+ if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_3) {
+ zend_dump_op_array(op_array, 0, "after pass 1", NULL);
+ }
}
/* pass 4:
* - INIT_FCALL_BY_NAME -> DO_FCALL
*/
- if (ZEND_OPTIMIZER_PASS_4 & OPTIMIZATION_LEVEL) {
- optimize_func_calls(op_array, ctx);
+ if (ZEND_OPTIMIZER_PASS_4 & ctx->optimization_level) {
+ zend_optimize_func_calls(op_array, ctx);
+ if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_4) {
+ zend_dump_op_array(op_array, 0, "after pass 1", NULL);
+ }
}
/* pass 5:
* - CFG optimization
*/
- if (ZEND_OPTIMIZER_PASS_5 & OPTIMIZATION_LEVEL) {
- optimize_cfg(op_array, ctx);
+ if (ZEND_OPTIMIZER_PASS_5 & ctx->optimization_level) {
+ zend_optimize_cfg(op_array, ctx);
+ if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_5) {
+ zend_dump_op_array(op_array, 0, "after pass 5", NULL);
+ }
}
+#if HAVE_DFA_PASS
+ /* pass 6:
+ * - DFA optimization
+ */
+ if ((ZEND_OPTIMIZER_PASS_6 & ctx->optimization_level) &&
+ !(ZEND_OPTIMIZER_PASS_7 & ctx->optimization_level)) {
+ zend_optimize_dfa(op_array, ctx);
+ if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_6) {
+ zend_dump_op_array(op_array, 0, "after pass 6", NULL);
+ }
+ }
+#endif
+
/* pass 9:
* - Optimize temp variables usage
*/
- if (ZEND_OPTIMIZER_PASS_9 & OPTIMIZATION_LEVEL) {
- optimize_temporary_variables(op_array, ctx);
+ if (ZEND_OPTIMIZER_PASS_9 & ctx->optimization_level) {
+ zend_optimize_temporary_variables(op_array, ctx);
+ if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_9) {
+ zend_dump_op_array(op_array, 0, "after pass 9", NULL);
+ }
}
/* pass 10:
* - remove NOPs
*/
- if (((ZEND_OPTIMIZER_PASS_10|ZEND_OPTIMIZER_PASS_5) & OPTIMIZATION_LEVEL) == ZEND_OPTIMIZER_PASS_10) {
+ if (((ZEND_OPTIMIZER_PASS_10|ZEND_OPTIMIZER_PASS_5) & ctx->optimization_level) == ZEND_OPTIMIZER_PASS_10) {
zend_optimizer_nop_removal(op_array);
+ if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_10) {
+ zend_dump_op_array(op_array, 0, "after pass 10", NULL);
+ }
}
/* pass 11:
* - Compact literals table
*/
- if (ZEND_OPTIMIZER_PASS_11 & OPTIMIZATION_LEVEL) {
+ if (ZEND_OPTIMIZER_PASS_11 & ctx->optimization_level) {
zend_optimizer_compact_literals(op_array, ctx);
+ if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_11) {
+ zend_dump_op_array(op_array, 0, "after pass 11", NULL);
+ }
+ }
+
+ if (ctx->debug_level & ZEND_DUMP_AFTER_OPTIMIZER) {
+ zend_dump_op_array(op_array, 0, "after optimizer", NULL);
}
}
-static void zend_accel_optimize(zend_op_array *op_array,
- zend_optimizer_ctx *ctx)
+static void zend_revert_pass_two(zend_op_array *op_array)
{
zend_op *opline, *end;
- /* Revert pass_two() */
opline = op_array->opcodes;
end = opline + op_array->last;
while (opline < end) {
@@ -569,41 +744,14 @@ static void zend_accel_optimize(zend_op_array *op_array,
if (opline->op2_type == IS_CONST) {
ZEND_PASS_TWO_UNDO_CONSTANT(op_array, opline->op2);
}
- switch (opline->opcode) {
- case ZEND_JMP:
- case ZEND_FAST_CALL:
- case ZEND_DECLARE_ANON_CLASS:
- case ZEND_DECLARE_ANON_INHERITED_CLASS:
- ZEND_PASS_TWO_UNDO_JMP_TARGET(op_array, opline, ZEND_OP1(opline));
- break;
- case ZEND_JMPZNZ:
- /* relative offset into absolute index */
- opline->extended_value = ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value);
- /* break omitted intentionally */
- case ZEND_JMPZ:
- case ZEND_JMPNZ:
- case ZEND_JMPZ_EX:
- case ZEND_JMPNZ_EX:
- case ZEND_JMP_SET:
- case ZEND_COALESCE:
- case ZEND_NEW:
- case ZEND_FE_RESET_R:
- case ZEND_FE_RESET_RW:
- case ZEND_ASSERT_CHECK:
- ZEND_PASS_TWO_UNDO_JMP_TARGET(op_array, opline, ZEND_OP2(opline));
- break;
- case ZEND_FE_FETCH_R:
- case ZEND_FE_FETCH_RW:
- opline->extended_value = ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value);
- break;
- }
opline++;
}
+}
- /* Do actual optimizations */
- zend_optimize(op_array, ctx);
+static void zend_redo_pass_two(zend_op_array *op_array)
+{
+ zend_op *opline, *end;
- /* Redo pass_two() */
opline = op_array->opcodes;
end = opline + op_array->last;
while (opline < end) {
@@ -613,40 +761,53 @@ static void zend_accel_optimize(zend_op_array *op_array,
if (opline->op2_type == IS_CONST) {
ZEND_PASS_TWO_UPDATE_CONSTANT(op_array, opline->op2);
}
- switch (opline->opcode) {
- case ZEND_JMP:
- case ZEND_FAST_CALL:
- case ZEND_DECLARE_ANON_CLASS:
- case ZEND_DECLARE_ANON_INHERITED_CLASS:
- ZEND_PASS_TWO_UPDATE_JMP_TARGET(op_array, opline, ZEND_OP1(opline));
- break;
- case ZEND_JMPZNZ:
- /* absolute index to relative offset */
- opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, opline, opline->extended_value);
- /* break omitted intentionally */
- case ZEND_JMPZ:
- case ZEND_JMPNZ:
- case ZEND_JMPZ_EX:
- case ZEND_JMPNZ_EX:
- case ZEND_JMP_SET:
- case ZEND_COALESCE:
- case ZEND_NEW:
- case ZEND_FE_RESET_R:
- case ZEND_FE_RESET_RW:
- case ZEND_ASSERT_CHECK:
- ZEND_PASS_TWO_UPDATE_JMP_TARGET(op_array, opline, ZEND_OP2(opline));
- break;
- case ZEND_FE_FETCH_R:
- case ZEND_FE_FETCH_RW:
- opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, opline, opline->extended_value);
- break;
- }
ZEND_VM_SET_OPCODE_HANDLER(opline);
opline++;
}
}
-static void zend_accel_adjust_fcall_stack_size(zend_op_array *op_array, zend_optimizer_ctx *ctx)
+#if HAVE_DFA_PASS
+static void zend_redo_pass_two_ex(zend_op_array *op_array, zend_ssa *ssa)
+{
+ zend_op *opline, *end;
+
+ opline = op_array->opcodes;
+ end = opline + op_array->last;
+ while (opline < end) {
+ zend_vm_set_opcode_handler_ex(opline,
+ opline->op1_type == IS_UNUSED ? 0 : (OP1_INFO() & (MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_REF|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_KEY_ANY)),
+ opline->op2_type == IS_UNUSED ? 0 : (OP2_INFO() & (MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_REF|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_KEY_ANY)),
+ (opline->opcode == ZEND_PRE_INC ||
+ opline->opcode == ZEND_PRE_DEC ||
+ opline->opcode == ZEND_POST_INC ||
+ opline->opcode == ZEND_POST_DEC) ?
+ ((ssa->ops[opline - op_array->opcodes].op1_def >= 0) ? (OP1_DEF_INFO() & (MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_REF|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_KEY_ANY)) : MAY_BE_ANY) :
+ (opline->result_type == IS_UNUSED ? 0 : (RES_INFO() & (MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_REF|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_KEY_ANY))));
+ if (opline->op1_type == IS_CONST) {
+ ZEND_PASS_TWO_UPDATE_CONSTANT(op_array, opline->op1);
+ }
+ if (opline->op2_type == IS_CONST) {
+ ZEND_PASS_TWO_UPDATE_CONSTANT(op_array, opline->op2);
+ }
+ opline++;
+ }
+}
+#endif
+
+static void zend_optimize_op_array(zend_op_array *op_array,
+ zend_optimizer_ctx *ctx)
+{
+ /* Revert pass_two() */
+ zend_revert_pass_two(op_array);
+
+ /* Do actual optimizations */
+ zend_optimize(op_array, ctx);
+
+ /* Redo pass_two() */
+ zend_redo_pass_two(op_array);
+}
+
+static void zend_adjust_fcall_stack_size(zend_op_array *op_array, zend_optimizer_ctx *ctx)
{
zend_function *func;
zend_op *opline, *end;
@@ -666,78 +827,147 @@ static void zend_accel_adjust_fcall_stack_size(zend_op_array *op_array, zend_opt
}
}
-int zend_accel_script_optimize(zend_persistent_script *script)
+#if HAVE_DFA_PASS
+static void zend_adjust_fcall_stack_size_graph(zend_op_array *op_array)
+{
+ zend_func_info *func_info = ZEND_FUNC_INFO(op_array);
+
+ if (func_info) {
+ zend_call_info *call_info =func_info->callee_info;
+
+ while (call_info) {
+ zend_op *opline = call_info->caller_init_opline;
+
+ if (opline && call_info->callee_func && opline->opcode == ZEND_INIT_FCALL) {
+ opline->op1.num = zend_vm_calc_used_stack(opline->extended_value, call_info->callee_func);
+ }
+ call_info = call_info->next_callee;
+ }
+ }
+}
+#endif
+
+int zend_optimize_script(zend_script *script, zend_long optimization_level, zend_long debug_level)
{
- uint idx, j;
- Bucket *p, *q;
zend_class_entry *ce;
zend_op_array *op_array;
+ zend_string *name;
zend_optimizer_ctx ctx;
+#if HAVE_DFA_PASS
+ zend_call_graph call_graph;
+#endif
ctx.arena = zend_arena_create(64 * 1024);
ctx.script = script;
ctx.constants = NULL;
+ ctx.optimization_level = optimization_level;
+ ctx.debug_level = debug_level;
- zend_accel_optimize(&script->main_op_array, &ctx);
+ zend_optimize_op_array(&script->main_op_array, &ctx);
- for (idx = 0; idx < script->function_table.nNumUsed; idx++) {
- p = script->function_table.arData + idx;
- if (Z_TYPE(p->val) == IS_UNDEF) continue;
- op_array = (zend_op_array*)Z_PTR(p->val);
- zend_accel_optimize(op_array, &ctx);
- }
+ ZEND_HASH_FOREACH_PTR(&script->function_table, op_array) {
+ zend_optimize_op_array(op_array, &ctx);
+ } ZEND_HASH_FOREACH_END();
- for (idx = 0; idx < script->class_table.nNumUsed; idx++) {
- p = script->class_table.arData + idx;
- if (Z_TYPE(p->val) == IS_UNDEF) continue;
- ce = (zend_class_entry*)Z_PTR(p->val);
- for (j = 0; j < ce->function_table.nNumUsed; j++) {
- q = ce->function_table.arData + j;
- if (Z_TYPE(q->val) == IS_UNDEF) continue;
- op_array = (zend_op_array*)Z_PTR(q->val);
+ ZEND_HASH_FOREACH_PTR(&script->class_table, ce) {
+ ZEND_HASH_FOREACH_STR_KEY_PTR(&ce->function_table, name, op_array) {
if (op_array->scope == ce) {
- zend_accel_optimize(op_array, &ctx);
+ zend_optimize_op_array(op_array, &ctx);
} else if (op_array->type == ZEND_USER_FUNCTION) {
zend_op_array *orig_op_array;
- if ((orig_op_array = zend_hash_find_ptr(&op_array->scope->function_table, q->key)) != NULL) {
+ if ((orig_op_array = zend_hash_find_ptr(&op_array->scope->function_table, name)) != NULL) {
HashTable *ht = op_array->static_variables;
*op_array = *orig_op_array;
op_array->static_variables = ht;
}
}
+ } ZEND_HASH_FOREACH_END();
+ } ZEND_HASH_FOREACH_END();
+
+#if HAVE_DFA_PASS
+ if ((ZEND_OPTIMIZER_PASS_6 & optimization_level) &&
+ (ZEND_OPTIMIZER_PASS_7 & optimization_level) &&
+ zend_build_call_graph(&ctx.arena, script, ZEND_RT_CONSTANTS, &call_graph) == SUCCESS) {
+ /* Optimize using call-graph */
+ void *checkpoint = zend_arena_checkpoint(ctx.arena);
+ int i;
+ zend_func_info *func_info;
+
+ for (i = 0; i < call_graph.op_arrays_count; i++) {
+ zend_revert_pass_two(call_graph.op_arrays[i]);
}
- }
- if (ZEND_OPTIMIZER_PASS_12 & OPTIMIZATION_LEVEL) {
- zend_accel_adjust_fcall_stack_size(&script->main_op_array, &ctx);
+ for (i = 0; i < call_graph.op_arrays_count; i++) {
+ if (call_graph.op_arrays[i]->fn_flags & ZEND_ACC_HAS_RETURN_TYPE) {
+ func_info = ZEND_FUNC_INFO(call_graph.op_arrays[i]);
+ if (func_info) {
+ zend_init_func_return_info(call_graph.op_arrays[i], script, &func_info->return_info);
+ }
+ }
+ }
+
+ for (i = 0; i < call_graph.op_arrays_count; i++) {
+ func_info = ZEND_FUNC_INFO(call_graph.op_arrays[i]);
+ if (func_info) {
+ zend_dfa_analyze_op_array(call_graph.op_arrays[i], &ctx, &func_info->ssa, &func_info->flags);
+ }
+ }
- for (idx = 0; idx < script->function_table.nNumUsed; idx++) {
- p = script->function_table.arData + idx;
- if (Z_TYPE(p->val) == IS_UNDEF) continue;
- op_array = (zend_op_array*)Z_PTR(p->val);
- zend_accel_adjust_fcall_stack_size(op_array, &ctx);
+ //TODO: perform inner-script inference???
+ for (i = 0; i < call_graph.op_arrays_count; i++) {
+ func_info = ZEND_FUNC_INFO(call_graph.op_arrays[i]);
+ if (func_info) {
+ zend_dfa_optimize_op_array(call_graph.op_arrays[i], &ctx, &func_info->ssa);
+ }
+ }
+
+ if (debug_level & ZEND_DUMP_AFTER_PASS_7) {
+ for (i = 0; i < call_graph.op_arrays_count; i++) {
+ zend_dump_op_array(call_graph.op_arrays[i], 0, "after pass 7", NULL);
+ }
}
- for (idx = 0; idx < script->class_table.nNumUsed; idx++) {
- p = script->class_table.arData + idx;
- if (Z_TYPE(p->val) == IS_UNDEF) continue;
- ce = (zend_class_entry*)Z_PTR(p->val);
- for (j = 0; j < ce->function_table.nNumUsed; j++) {
- q = ce->function_table.arData + j;
- if (Z_TYPE(q->val) == IS_UNDEF) continue;
- op_array = (zend_op_array*)Z_PTR(q->val);
+ if (ZEND_OPTIMIZER_PASS_12 & optimization_level) {
+ for (i = 0; i < call_graph.op_arrays_count; i++) {
+ zend_adjust_fcall_stack_size_graph(call_graph.op_arrays[i]);
+ }
+ }
+
+ for (i = 0; i < call_graph.op_arrays_count; i++) {
+ func_info = ZEND_FUNC_INFO(call_graph.op_arrays[i]);
+ if (func_info && func_info->ssa.var_info) {
+ zend_redo_pass_two_ex(call_graph.op_arrays[i], &func_info->ssa);
+ ZEND_SET_FUNC_INFO(call_graph.op_arrays[i], NULL);
+ } else {
+ zend_redo_pass_two(call_graph.op_arrays[i]);
+ }
+ }
+
+ zend_arena_release(&ctx.arena, checkpoint);
+ } else
+#endif
+
+ if (ZEND_OPTIMIZER_PASS_12 & optimization_level) {
+ zend_adjust_fcall_stack_size(&script->main_op_array, &ctx);
+
+ ZEND_HASH_FOREACH_PTR(&script->function_table, op_array) {
+ zend_adjust_fcall_stack_size(op_array, &ctx);
+ } ZEND_HASH_FOREACH_END();
+
+ ZEND_HASH_FOREACH_PTR(&script->class_table, ce) {
+ ZEND_HASH_FOREACH_STR_KEY_PTR(&ce->function_table, name, op_array) {
if (op_array->scope == ce) {
- zend_accel_adjust_fcall_stack_size(op_array, &ctx);
+ zend_adjust_fcall_stack_size(op_array, &ctx);
} else if (op_array->type == ZEND_USER_FUNCTION) {
zend_op_array *orig_op_array;
- if ((orig_op_array = zend_hash_find_ptr(&op_array->scope->function_table, q->key)) != NULL) {
+ if ((orig_op_array = zend_hash_find_ptr(&op_array->scope->function_table, name)) != NULL) {
HashTable *ht = op_array->static_variables;
*op_array = *orig_op_array;
op_array->static_variables = ht;
}
}
- }
- }
+ } ZEND_HASH_FOREACH_END();
+ } ZEND_HASH_FOREACH_END();
}
if (ctx.constants) {
@@ -747,3 +977,21 @@ int zend_accel_script_optimize(zend_persistent_script *script)
return 1;
}
+
+int zend_optimizer_startup(void)
+{
+ return zend_func_info_startup();
+}
+
+int zend_optimizer_shutdown(void)
+{
+ return zend_func_info_shutdown();
+}
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * indent-tabs-mode: t
+ * End:
+ */
diff --git a/ext/opcache/Optimizer/zend_optimizer.h b/ext/opcache/Optimizer/zend_optimizer.h
index a5380a324e..1492d09b36 100644
--- a/ext/opcache/Optimizer/zend_optimizer.h
+++ b/ext/opcache/Optimizer/zend_optimizer.h
@@ -30,8 +30,8 @@
#define ZEND_OPTIMIZER_PASS_3 (1<<2) /* ++, +=, series of jumps */
#define ZEND_OPTIMIZER_PASS_4 (1<<3) /* INIT_FCALL_BY_NAME -> DO_FCALL */
#define ZEND_OPTIMIZER_PASS_5 (1<<4) /* CFG based optimization */
-#define ZEND_OPTIMIZER_PASS_6 (1<<5)
-#define ZEND_OPTIMIZER_PASS_7 (1<<6)
+#define ZEND_OPTIMIZER_PASS_6 (1<<5) /* DFA based optimization */
+#define ZEND_OPTIMIZER_PASS_7 (1<<6) /* CALL GRAPH optimization */
#define ZEND_OPTIMIZER_PASS_8 (1<<7)
#define ZEND_OPTIMIZER_PASS_9 (1<<8) /* TMP VAR usage */
#define ZEND_OPTIMIZER_PASS_10 (1<<9) /* NOP removal */
@@ -45,4 +45,47 @@
#define DEFAULT_OPTIMIZATION_LEVEL "0x7FFFBFFF"
+
+#define ZEND_DUMP_AFTER_PASS_1 ZEND_OPTIMIZER_PASS_1
+#define ZEND_DUMP_AFTER_PASS_2 ZEND_OPTIMIZER_PASS_2
+#define ZEND_DUMP_AFTER_PASS_3 ZEND_OPTIMIZER_PASS_3
+#define ZEND_DUMP_AFTER_PASS_4 ZEND_OPTIMIZER_PASS_4
+#define ZEND_DUMP_AFTER_PASS_5 ZEND_OPTIMIZER_PASS_5
+#define ZEND_DUMP_AFTER_PASS_6 ZEND_OPTIMIZER_PASS_6
+#define ZEND_DUMP_AFTER_PASS_7 ZEND_OPTIMIZER_PASS_7
+#define ZEND_DUMP_AFTER_PASS_8 ZEND_OPTIMIZER_PASS_8
+#define ZEND_DUMP_AFTER_PASS_9 ZEND_OPTIMIZER_PASS_9
+#define ZEND_DUMP_AFTER_PASS_10 ZEND_OPTIMIZER_PASS_10
+#define ZEND_DUMP_AFTER_PASS_11 ZEND_OPTIMIZER_PASS_11
+#define ZEND_DUMP_AFTER_PASS_12 ZEND_OPTIMIZER_PASS_12
+#define ZEND_DUMP_AFTER_PASS_13 ZEND_OPTIMIZER_PASS_13
+#define ZEND_DUMP_AFTER_PASS_14 ZEND_OPTIMIZER_PASS_14
+
+#define ZEND_DUMP_BEFORE_OPTIMIZER (1<<16)
+#define ZEND_DUMP_AFTER_OPTIMIZER (1<<17)
+
+#define ZEND_DUMP_BEFORE_BLOCK_PASS (1<<18)
+#define ZEND_DUMP_AFTER_BLOCK_PASS (1<<19)
+#define ZEND_DUMP_BLOCK_PASS_VARS (1<<20)
+
+#define ZEND_DUMP_BEFORE_DFA_PASS (1<<21)
+#define ZEND_DUMP_AFTER_DFA_PASS (1<<22)
+#define ZEND_DUMP_DFA_CFG (1<<23)
+#define ZEND_DUMP_DFA_DOMINATORS (1<<24)
+#define ZEND_DUMP_DFA_LIVENESS (1<<25)
+#define ZEND_DUMP_DFA_PHI (1<<26)
+#define ZEND_DUMP_DFA_SSA (1<<27)
+#define ZEND_DUMP_DFA_SSA_VARS (1<<28)
+
+typedef struct _zend_script {
+ zend_string *filename;
+ zend_op_array main_op_array;
+ HashTable function_table;
+ HashTable class_table;
+} zend_script;
+
+int zend_optimize_script(zend_script *script, zend_long optimization_level, zend_long debug_level);
+int zend_optimizer_startup(void);
+int zend_optimizer_shutdown(void);
+
#endif
diff --git a/ext/opcache/Optimizer/zend_optimizer_internal.h b/ext/opcache/Optimizer/zend_optimizer_internal.h
index 4324208f64..e2e9823c78 100644
--- a/ext/opcache/Optimizer/zend_optimizer_internal.h
+++ b/ext/opcache/Optimizer/zend_optimizer_internal.h
@@ -22,7 +22,18 @@
#ifndef ZEND_OPTIMIZER_INTERNAL_H
#define ZEND_OPTIMIZER_INTERNAL_H
-#include "ZendAccelerator.h"
+#include "zend_ssa.h"
+
+#define ZEND_RESULT_TYPE(opline) (opline)->result_type
+#define ZEND_RESULT(opline) (opline)->result
+#define ZEND_OP1_TYPE(opline) (opline)->op1_type
+#define ZEND_OP1(opline) (opline)->op1
+#define ZEND_OP1_LITERAL(opline) (op_array)->literals[(opline)->op1.constant]
+#define ZEND_OP1_JMP_ADDR(opline) OP_JMP_ADDR(opline, (opline)->op1)
+#define ZEND_OP2_TYPE(opline) (opline)->op2_type
+#define ZEND_OP2(opline) (opline)->op2
+#define ZEND_OP2_LITERAL(opline) (op_array)->literals[(opline)->op2.constant]
+#define ZEND_OP2_JMP_ADDR(opline) OP_JMP_ADDR(opline, (opline)->op2)
#define VAR_NUM(v) EX_VAR_TO_NUM(v)
#define NUM_VAR(v) ((uint32_t)(zend_uintptr_t)ZEND_CALL_VAR_NUM(0, v))
@@ -32,65 +43,17 @@
#define INV_COND_EX(op) ((op) == ZEND_JMPZ ? ZEND_JMPNZ_EX : ZEND_JMPZ_EX)
#define INV_EX_COND_EX(op) ((op) == ZEND_JMPZ_EX ? ZEND_JMPNZ_EX : ZEND_JMPZ_EX)
-#undef MAKE_NOP
-
-#define MAKE_NOP(opline) do { \
- (opline)->op1.num = 0; \
- (opline)->op2.num = 0; \
- (opline)->result.num = 0; \
- (opline)->opcode = ZEND_NOP; \
- (opline)->op1_type = IS_UNUSED; \
- (opline)->op2_type = IS_UNUSED; \
- (opline)->result_type = IS_UNUSED; \
- zend_vm_set_opcode_handler(opline); \
-} while (0)
-
-#define RESULT_USED(op) (((op->result_type & IS_VAR) && !(op->result_type & EXT_TYPE_UNUSED)) || op->result_type == IS_TMP_VAR)
-#define RESULT_UNUSED(op) ((op->result_type & EXT_TYPE_UNUSED) != 0)
-#define SAME_VAR(op1, op2) ((((op1 ## _type & IS_VAR) && (op2 ## _type & IS_VAR)) || (op1 ## _type == IS_TMP_VAR && op2 ## _type == IS_TMP_VAR)) && op1.var == op2.var)
+#define RESULT_UNUSED(op) (op->result_type == IS_UNUSED)
+#define SAME_VAR(op1, op2) (op1 ## _type == op2 ## _type && op1.var == op2.var)
typedef struct _zend_optimizer_ctx {
zend_arena *arena;
- zend_persistent_script *script;
+ zend_script *script;
HashTable *constants;
+ zend_long optimization_level;
+ zend_long debug_level;
} zend_optimizer_ctx;
-typedef struct _zend_code_block zend_code_block;
-typedef struct _zend_block_source zend_block_source;
-
-struct _zend_code_block {
- int access;
- zend_op *start_opline;
- int start_opline_no;
- int len;
- zend_code_block *op1_to;
- zend_code_block *op2_to;
- zend_code_block *ext_to;
- zend_code_block *follow_to;
- zend_code_block *next;
- zend_block_source *sources;
- zend_bool protected; /* don't merge this block with others */
-};
-
-typedef struct _zend_cfg {
- zend_code_block *blocks;
- zend_code_block **try;
- zend_code_block **catch;
- zend_code_block **loop_start;
- zend_code_block **loop_cont;
- zend_code_block **loop_brk;
- zend_op **Tsource;
- char *same_t;
-} zend_cfg;
-
-struct _zend_block_source {
- zend_code_block *from;
- zend_block_source *next;
-};
-
-#define OPTIMIZATION_LEVEL \
- ZCG(accel_directives).optimization_level
-
#define LITERAL_LONG(op, val) do { \
zval _c; \
ZVAL_LONG(&_c, val); \
@@ -130,14 +93,21 @@ int zend_optimizer_replace_by_const(zend_op_array *op_array,
uint32_t var,
zval *val);
+void zend_optimizer_remove_live_range(zend_op_array *op_array, uint32_t var);
void zend_optimizer_pass1(zend_op_array *op_array, zend_optimizer_ctx *ctx);
void zend_optimizer_pass2(zend_op_array *op_array);
void zend_optimizer_pass3(zend_op_array *op_array);
-void optimize_func_calls(zend_op_array *op_array, zend_optimizer_ctx *ctx);
-void optimize_cfg(zend_op_array *op_array, zend_optimizer_ctx *ctx);
-void optimize_temporary_variables(zend_op_array *op_array, zend_optimizer_ctx *ctx);
+void zend_optimize_func_calls(zend_op_array *op_array, zend_optimizer_ctx *ctx);
+void zend_optimize_cfg(zend_op_array *op_array, zend_optimizer_ctx *ctx);
+void zend_optimize_dfa(zend_op_array *op_array, zend_optimizer_ctx *ctx);
+int zend_dfa_analyze_op_array(zend_op_array *op_array, zend_optimizer_ctx *ctx, zend_ssa *ssa, uint32_t *flags);
+void zend_dfa_optimize_op_array(zend_op_array *op_array, zend_optimizer_ctx *ctx, zend_ssa *ssa);
+void zend_optimize_temporary_variables(zend_op_array *op_array, zend_optimizer_ctx *ctx);
void zend_optimizer_nop_removal(zend_op_array *op_array);
void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx *ctx);
int zend_optimizer_is_disabled_func(const char *name, size_t len);
+zend_function *zend_optimizer_get_called_func(
+ zend_script *script, zend_op_array *op_array, zend_op *opline, zend_bool rt_constants);
+uint32_t zend_optimizer_classify_function(zend_string *name, uint32_t num_args);
#endif
diff --git a/ext/opcache/Optimizer/zend_ssa.c b/ext/opcache/Optimizer/zend_ssa.c
new file mode 100644
index 0000000000..6f1fff961c
--- /dev/null
+++ b/ext/opcache/Optimizer/zend_ssa.c
@@ -0,0 +1,1128 @@
+/*
+ +----------------------------------------------------------------------+
+ | Zend Engine, SSA - Static Single Assignment Form |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1998-2016 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> |
+ +----------------------------------------------------------------------+
+*/
+
+#include "php.h"
+#include "zend_compile.h"
+#include "zend_dfg.h"
+#include "zend_ssa.h"
+#include "zend_dump.h"
+#include "zend_inference.h"
+
+static zend_bool dominates(const zend_basic_block *blocks, int a, int b) {
+ while (blocks[b].level > blocks[a].level) {
+ b = blocks[b].idom;
+ }
+ return a == b;
+}
+
+static zend_bool dominates_other_predecessors(
+ const zend_cfg *cfg, const zend_basic_block *block, int check, int exclude) {
+ int i;
+ for (i = 0; i < block->predecessors_count; i++) {
+ int predecessor = cfg->predecessors[block->predecessor_offset + i];
+ if (predecessor != exclude && !dominates(cfg->blocks, check, predecessor)) {
+ return 0;
+ }
+ }
+ return 1;
+}
+
+static zend_bool needs_pi(const zend_op_array *op_array, zend_dfg *dfg, zend_ssa *ssa, int from, int to, int var) /* {{{ */
+{
+ zend_basic_block *from_block, *to_block;
+ int other_successor;
+
+ if (!DFG_ISSET(dfg->in, dfg->size, to, var)) {
+ /* Variable is not live, certainly won't benefit from pi */
+ return 0;
+ }
+
+ to_block = &ssa->cfg.blocks[to];
+ if (to_block->predecessors_count == 1) {
+ /* Always place pi if one predecessor (an if branch) */
+ return 1;
+ }
+
+ /* Check that the other successor of the from block does not dominate all other predecessors.
+ * If it does, we'd probably end up annihilating a positive+negative pi assertion. */
+ from_block = &ssa->cfg.blocks[from];
+ other_successor = from_block->successors[0] == to
+ ? from_block->successors[1] : from_block->successors[0];
+ return !dominates_other_predecessors(&ssa->cfg, to_block, other_successor, from);
+}
+/* }}} */
+
+static zend_ssa_phi *add_pi(
+ zend_arena **arena, const zend_op_array *op_array, zend_dfg *dfg, zend_ssa *ssa,
+ int from, int to, int var) /* {{{ */
+{
+ zend_ssa_phi *phi;
+ if (!needs_pi(op_array, dfg, ssa, from, to, var)) {
+ return NULL;
+ }
+
+ phi = zend_arena_calloc(arena, 1,
+ sizeof(zend_ssa_phi) +
+ sizeof(int) * ssa->cfg.blocks[to].predecessors_count +
+ sizeof(void*) * ssa->cfg.blocks[to].predecessors_count);
+ phi->sources = (int*)(((char*)phi) + sizeof(zend_ssa_phi));
+ memset(phi->sources, 0xff, sizeof(int) * ssa->cfg.blocks[to].predecessors_count);
+ phi->use_chains = (zend_ssa_phi**)(((char*)phi->sources) + sizeof(int) * ssa->cfg.blocks[to].predecessors_count);
+
+ phi->pi = from;
+ phi->var = var;
+ phi->ssa_var = -1;
+ phi->next = ssa->blocks[to].phis;
+ ssa->blocks[to].phis = phi;
+
+ /* Block "to" now defines "var" via the pi statement, so add it to the "def" set. Note that
+ * this is not entirely accurate, because the pi is actually placed along the edge from->to.
+ * If there is a back-edge to "to" this may result in non-minimal SSA form. */
+ DFG_SET(dfg->def, dfg->size, to, var);
+
+ /* If there are multiple predecessors in the target block, we need to place a phi there.
+ * However this can (generally) not be expressed in terms of dominance frontiers, so place it
+ * explicitly. dfg->use here really is dfg->phi, we're reusing the set. */
+ if (ssa->cfg.blocks[to].predecessors_count > 1) {
+ DFG_SET(dfg->use, dfg->size, to, var);
+ }
+
+ return phi;
+}
+/* }}} */
+
+static void pi_range(
+ zend_ssa_phi *phi, int min_var, int max_var, zend_long min, zend_long max,
+ char underflow, char overflow, char negative) /* {{{ */
+{
+ zend_ssa_range_constraint *constraint = &phi->constraint.range;
+ constraint->min_var = min_var;
+ constraint->max_var = max_var;
+ constraint->min_ssa_var = -1;
+ constraint->max_ssa_var = -1;
+ constraint->range.min = min;
+ constraint->range.max = max;
+ constraint->range.underflow = underflow;
+ constraint->range.overflow = overflow;
+ constraint->negative = negative ? NEG_INIT : NEG_NONE;
+ phi->has_range_constraint = 1;
+}
+/* }}} */
+
+static inline void pi_range_equals(zend_ssa_phi *phi, int var, zend_long val) {
+ pi_range(phi, var, var, val, val, 0, 0, 0);
+}
+static inline void pi_range_not_equals(zend_ssa_phi *phi, int var, zend_long val) {
+ pi_range(phi, var, var, val, val, 0, 0, 1);
+}
+static inline void pi_range_min(zend_ssa_phi *phi, int var, zend_long val) {
+ pi_range(phi, var, -1, val, ZEND_LONG_MAX, 0, 1, 0);
+}
+static inline void pi_range_max(zend_ssa_phi *phi, int var, zend_long val) {
+ pi_range(phi, -1, var, ZEND_LONG_MIN, val, 1, 0, 0);
+}
+
+static void pi_type_mask(zend_ssa_phi *phi, uint32_t type_mask) {
+ phi->has_range_constraint = 0;
+ phi->constraint.type.ce = NULL;
+ phi->constraint.type.type_mask = MAY_BE_REF|MAY_BE_RC1|MAY_BE_RCN;
+ phi->constraint.type.type_mask |= type_mask;
+ if (type_mask & MAY_BE_NULL) {
+ phi->constraint.type.type_mask |= MAY_BE_UNDEF;
+ }
+}
+static inline void pi_not_type_mask(zend_ssa_phi *phi, uint32_t type_mask) {
+ uint32_t relevant = MAY_BE_ANY|MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF;
+ pi_type_mask(phi, ~type_mask & relevant);
+}
+static inline uint32_t mask_for_type_check(uint32_t type) {
+ if (type == _IS_BOOL) {
+ return MAY_BE_TRUE|MAY_BE_FALSE;
+ } else if (type == IS_ARRAY) {
+ return MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF;
+ } else {
+ return 1 << type;
+ }
+}
+
+/* We can interpret $a + 5 == 0 as $a = 0 - 5, i.e. shift the adjustment to the other operand.
+ * This negated adjustment is what is written into the "adjustment" parameter. */
+static int find_adjusted_tmp_var(const zend_op_array *op_array, uint32_t build_flags, zend_op *opline, uint32_t var_num, zend_long *adjustment) /* {{{ */
+{
+ zend_op *op = opline;
+ while (op != op_array->opcodes) {
+ op--;
+ if (op->result_type != IS_TMP_VAR || op->result.var != var_num) {
+ continue;
+ }
+
+ if (op->opcode == ZEND_POST_DEC) {
+ if (op->op1_type == IS_CV) {
+ *adjustment = -1;
+ return EX_VAR_TO_NUM(op->op1.var);
+ }
+ } else if (op->opcode == ZEND_POST_INC) {
+ if (op->op1_type == IS_CV) {
+ *adjustment = 1;
+ return EX_VAR_TO_NUM(op->op1.var);
+ }
+ } else if (op->opcode == ZEND_ADD) {
+ if (op->op1_type == IS_CV &&
+ op->op2_type == IS_CONST &&
+ Z_TYPE_P(CRT_CONSTANT(op->op2)) == IS_LONG &&
+ Z_LVAL_P(CRT_CONSTANT(op->op2)) != ZEND_LONG_MIN) {
+ *adjustment = -Z_LVAL_P(CRT_CONSTANT(op->op2));
+ return EX_VAR_TO_NUM(op->op1.var);
+ } else if (op->op2_type == IS_CV &&
+ op->op1_type == IS_CONST &&
+ Z_TYPE_P(CRT_CONSTANT(op->op1)) == IS_LONG &&
+ Z_LVAL_P(CRT_CONSTANT(op->op1)) != ZEND_LONG_MIN) {
+ *adjustment = -Z_LVAL_P(CRT_CONSTANT(op->op1));
+ return EX_VAR_TO_NUM(op->op2.var);
+ }
+ } else if (op->opcode == ZEND_SUB) {
+ if (op->op1_type == IS_CV &&
+ op->op2_type == IS_CONST &&
+ Z_TYPE_P(CRT_CONSTANT(op->op2)) == IS_LONG) {
+ *adjustment = Z_LVAL_P(CRT_CONSTANT(op->op2));
+ return EX_VAR_TO_NUM(op->op1.var);
+ }
+ }
+ break;
+ }
+ return -1;
+}
+/* }}} */
+
+static inline zend_bool add_will_overflow(zend_long a, zend_long b) {
+ return (b > 0 && a > ZEND_LONG_MAX - b)
+ || (b < 0 && a < ZEND_LONG_MIN - b);
+}
+static inline zend_bool sub_will_overflow(zend_long a, zend_long b) {
+ return (b > 0 && a < ZEND_LONG_MIN + b)
+ || (b < 0 && a > ZEND_LONG_MAX + b);
+}
+
+/* e-SSA construction: Pi placement (Pi is actually a Phi with single
+ * source and constraint).
+ * Order of Phis is importent, Pis must be placed before Phis
+ */
+static void place_essa_pis(
+ zend_arena **arena, const zend_script *script, const zend_op_array *op_array,
+ uint32_t build_flags, zend_ssa *ssa, zend_dfg *dfg) /* {{{ */ {
+ zend_basic_block *blocks = ssa->cfg.blocks;
+ int j, blocks_count = ssa->cfg.blocks_count;
+ for (j = 0; j < blocks_count; j++) {
+ zend_ssa_phi *pi;
+ zend_op *opline = op_array->opcodes + blocks[j].start + blocks[j].len - 1;
+ int bt; /* successor block number if a condition is true */
+ int bf; /* successor block number if a condition is false */
+
+ if ((blocks[j].flags & ZEND_BB_REACHABLE) == 0 || blocks[j].len == 0) {
+ continue;
+ }
+ /* the last instruction of basic block is conditional branch,
+ * based on comparison of CV(s)
+ */
+ switch (opline->opcode) {
+ case ZEND_JMPZ:
+ case ZEND_JMPZNZ:
+ bf = blocks[j].successors[0];
+ bt = blocks[j].successors[1];
+ break;
+ case ZEND_JMPNZ:
+ bt = blocks[j].successors[0];
+ bf = blocks[j].successors[1];
+ break;
+ default:
+ continue;
+ }
+ if (opline->op1_type == IS_TMP_VAR &&
+ ((opline-1)->opcode == ZEND_IS_EQUAL ||
+ (opline-1)->opcode == ZEND_IS_NOT_EQUAL ||
+ (opline-1)->opcode == ZEND_IS_SMALLER ||
+ (opline-1)->opcode == ZEND_IS_SMALLER_OR_EQUAL) &&
+ opline->op1.var == (opline-1)->result.var) {
+ int var1 = -1;
+ int var2 = -1;
+ zend_long val1 = 0;
+ zend_long val2 = 0;
+// long val = 0;
+
+ if ((opline-1)->op1_type == IS_CV) {
+ var1 = EX_VAR_TO_NUM((opline-1)->op1.var);
+ } else if ((opline-1)->op1_type == IS_TMP_VAR) {
+ var1 = find_adjusted_tmp_var(
+ op_array, build_flags, opline, (opline-1)->op1.var, &val2);
+ }
+
+ if ((opline-1)->op2_type == IS_CV) {
+ var2 = EX_VAR_TO_NUM((opline-1)->op2.var);
+ } else if ((opline-1)->op2_type == IS_TMP_VAR) {
+ var2 = find_adjusted_tmp_var(
+ op_array, build_flags, opline, (opline-1)->op2.var, &val1);
+ }
+
+ if (var1 >= 0 && var2 >= 0) {
+ if (!sub_will_overflow(val1, val2) && !sub_will_overflow(val2, val1)) {
+ zend_long tmp = val1;
+ val1 -= val2;
+ val2 -= tmp;
+ } else {
+ var1 = -1;
+ var2 = -1;
+ }
+ } else if (var1 >= 0 && var2 < 0) {
+ zend_long add_val2 = 0;
+ if ((opline-1)->op2_type == IS_CONST &&
+ Z_TYPE_P(CRT_CONSTANT((opline-1)->op2)) == IS_LONG) {
+ add_val2 = Z_LVAL_P(CRT_CONSTANT((opline-1)->op2));
+ } else if ((opline-1)->op2_type == IS_CONST &&
+ Z_TYPE_P(CRT_CONSTANT((opline-1)->op2)) == IS_FALSE) {
+ add_val2 = 0;
+ } else if ((opline-1)->op2_type == IS_CONST &&
+ Z_TYPE_P(CRT_CONSTANT((opline-1)->op2)) == IS_TRUE) {
+ add_val2 = 1;
+ } else {
+ var1 = -1;
+ }
+ if (!add_will_overflow(val2, add_val2)) {
+ val2 += add_val2;
+ } else {
+ var1 = -1;
+ }
+ } else if (var1 < 0 && var2 >= 0) {
+ zend_long add_val1 = 0;
+ if ((opline-1)->op1_type == IS_CONST &&
+ Z_TYPE_P(CRT_CONSTANT((opline-1)->op1)) == IS_LONG) {
+ add_val1 = Z_LVAL_P(CRT_CONSTANT((opline-1)->op1));
+ } else if ((opline-1)->op1_type == IS_CONST &&
+ Z_TYPE_P(CRT_CONSTANT((opline-1)->op1)) == IS_FALSE) {
+ add_val1 = 0;
+ } else if ((opline-1)->op1_type == IS_CONST &&
+ Z_TYPE_P(CRT_CONSTANT((opline-1)->op1)) == IS_TRUE) {
+ add_val1 = 1;
+ } else {
+ var2 = -1;
+ }
+ if (!add_will_overflow(val1, add_val1)) {
+ val1 += add_val1;
+ } else {
+ var2 = -1;
+ }
+ }
+
+ if (var1 >= 0) {
+ if ((opline-1)->opcode == ZEND_IS_EQUAL) {
+ if ((pi = add_pi(arena, op_array, dfg, ssa, j, bt, var1))) {
+ pi_range_equals(pi, var2, val2);
+ }
+ if ((pi = add_pi(arena, op_array, dfg, ssa, j, bf, var1))) {
+ pi_range_not_equals(pi, var2, val2);
+ }
+ } else if ((opline-1)->opcode == ZEND_IS_NOT_EQUAL) {
+ if ((pi = add_pi(arena, op_array, dfg, ssa, j, bf, var1))) {
+ pi_range_equals(pi, var2, val2);
+ }
+ if ((pi = add_pi(arena, op_array, dfg, ssa, j, bt, var1))) {
+ pi_range_not_equals(pi, var2, val2);
+ }
+ } else if ((opline-1)->opcode == ZEND_IS_SMALLER) {
+ if (val2 > ZEND_LONG_MIN) {
+ if ((pi = add_pi(arena, op_array, dfg, ssa, j, bt, var1))) {
+ pi_range_max(pi, var2, val2-1);
+ }
+ }
+ if ((pi = add_pi(arena, op_array, dfg, ssa, j, bf, var1))) {
+ pi_range_min(pi, var2, val2);
+ }
+ } else if ((opline-1)->opcode == ZEND_IS_SMALLER_OR_EQUAL) {
+ if ((pi = add_pi(arena, op_array, dfg, ssa, j, bt, var1))) {
+ pi_range_max(pi, var2, val2);
+ }
+ if (val2 < ZEND_LONG_MAX) {
+ if ((pi = add_pi(arena, op_array, dfg, ssa, j, bf, var1))) {
+ pi_range_min(pi, var2, val2+1);
+ }
+ }
+ }
+ }
+ if (var2 >= 0) {
+ if((opline-1)->opcode == ZEND_IS_EQUAL) {
+ if ((pi = add_pi(arena, op_array, dfg, ssa, j, bt, var2))) {
+ pi_range_equals(pi, var1, val1);
+ }
+ if ((pi = add_pi(arena, op_array, dfg, ssa, j, bf, var2))) {
+ pi_range_not_equals(pi, var1, val1);
+ }
+ } else if ((opline-1)->opcode == ZEND_IS_NOT_EQUAL) {
+ if ((pi = add_pi(arena, op_array, dfg, ssa, j, bf, var2))) {
+ pi_range_equals(pi, var1, val1);
+ }
+ if ((pi = add_pi(arena, op_array, dfg, ssa, j, bt, var2))) {
+ pi_range_not_equals(pi, var1, val1);
+ }
+ } else if ((opline-1)->opcode == ZEND_IS_SMALLER) {
+ if (val1 < ZEND_LONG_MAX) {
+ if ((pi = add_pi(arena, op_array, dfg, ssa, j, bt, var2))) {
+ pi_range_min(pi, var1, val1+1);
+ }
+ }
+ if ((pi = add_pi(arena, op_array, dfg, ssa, j, bf, var2))) {
+ pi_range_max(pi, var1, val1);
+ }
+ } else if ((opline-1)->opcode == ZEND_IS_SMALLER_OR_EQUAL) {
+ if ((pi = add_pi(arena, op_array, dfg, ssa, j, bt, var2))) {
+ pi_range_min(pi, var1, val1);
+ }
+ if (val1 > ZEND_LONG_MIN) {
+ if ((pi = add_pi(arena, op_array, dfg, ssa, j, bf, var2))) {
+ pi_range_max(pi, var1, val1-1);
+ }
+ }
+ }
+ }
+ } else if (opline->op1_type == IS_TMP_VAR &&
+ ((opline-1)->opcode == ZEND_POST_INC ||
+ (opline-1)->opcode == ZEND_POST_DEC) &&
+ opline->op1.var == (opline-1)->result.var &&
+ (opline-1)->op1_type == IS_CV) {
+ int var = EX_VAR_TO_NUM((opline-1)->op1.var);
+
+ if ((opline-1)->opcode == ZEND_POST_DEC) {
+ if ((pi = add_pi(arena, op_array, dfg, ssa, j, bf, var))) {
+ pi_range_equals(pi, -1, -1);
+ }
+ if ((pi = add_pi(arena, op_array, dfg, ssa, j, bt, var))) {
+ pi_range_not_equals(pi, -1, -1);
+ }
+ } else if ((opline-1)->opcode == ZEND_POST_INC) {
+ if ((pi = add_pi(arena, op_array, dfg, ssa, j, bf, var))) {
+ pi_range_equals(pi, -1, 1);
+ }
+ if ((pi = add_pi(arena, op_array, dfg, ssa, j, bt, var))) {
+ pi_range_not_equals(pi, -1, 1);
+ }
+ }
+ } else if (opline->op1_type == IS_VAR &&
+ ((opline-1)->opcode == ZEND_PRE_INC ||
+ (opline-1)->opcode == ZEND_PRE_DEC) &&
+ opline->op1.var == (opline-1)->result.var &&
+ (opline-1)->op1_type == IS_CV) {
+ int var = EX_VAR_TO_NUM((opline-1)->op1.var);
+
+ if ((pi = add_pi(arena, op_array, dfg, ssa, j, bf, var))) {
+ pi_range_equals(pi, -1, 0);
+ }
+ /* speculative */
+ if ((pi = add_pi(arena, op_array, dfg, ssa, j, bt, var))) {
+ pi_range_not_equals(pi, -1, 0);
+ }
+ } else if (opline->op1_type == IS_TMP_VAR && (opline-1)->opcode == ZEND_TYPE_CHECK &&
+ opline->op1.var == (opline-1)->result.var && (opline-1)->op1_type == IS_CV) {
+ int var = EX_VAR_TO_NUM((opline-1)->op1.var);
+ uint32_t type = (opline-1)->extended_value;
+ if ((pi = add_pi(arena, op_array, dfg, ssa, j, bt, var))) {
+ pi_type_mask(pi, mask_for_type_check(type));
+ }
+ if (type != IS_OBJECT && type != IS_RESOURCE) {
+ /* is_object() and is_resource() may return false, even though the value is
+ * an object/resource. */
+ if ((pi = add_pi(arena, op_array, dfg, ssa, j, bf, var))) {
+ pi_not_type_mask(pi, mask_for_type_check(type));
+ }
+ }
+ } else if (opline->op1_type == IS_TMP_VAR &&
+ ((opline-1)->opcode == ZEND_IS_IDENTICAL
+ || (opline-1)->opcode == ZEND_IS_NOT_IDENTICAL) &&
+ opline->op1.var == (opline-1)->result.var) {
+ int var;
+ zval *val;
+ uint32_t type_mask;
+ if ((opline-1)->op1_type == IS_CV && (opline-1)->op2_type == IS_CONST) {
+ var = EX_VAR_TO_NUM((opline-1)->op1.var);
+ val = CRT_CONSTANT((opline-1)->op2);
+ } else if ((opline-1)->op1_type == IS_CONST && (opline-1)->op2_type == IS_CV) {
+ var = EX_VAR_TO_NUM((opline-1)->op2.var);
+ val = CRT_CONSTANT((opline-1)->op1);
+ } else {
+ continue;
+ }
+
+ /* We're interested in === null/true/false comparisons here, because they eliminate
+ * a type in the false-branch. Other === VAL comparisons are unlikely to be useful. */
+ if (Z_TYPE_P(val) != IS_NULL && Z_TYPE_P(val) != IS_TRUE && Z_TYPE_P(val) != IS_FALSE) {
+ continue;
+ }
+
+ type_mask = _const_op_type(val);
+ if ((opline-1)->opcode == ZEND_IS_IDENTICAL) {
+ if ((pi = add_pi(arena, op_array, dfg, ssa, j, bt, var))) {
+ pi_type_mask(pi, type_mask);
+ }
+ if ((pi = add_pi(arena, op_array, dfg, ssa, j, bf, var))) {
+ pi_not_type_mask(pi, type_mask);
+ }
+ } else {
+ if ((pi = add_pi(arena, op_array, dfg, ssa, j, bf, var))) {
+ pi_type_mask(pi, type_mask);
+ }
+ if ((pi = add_pi(arena, op_array, dfg, ssa, j, bt, var))) {
+ pi_not_type_mask(pi, type_mask);
+ }
+ }
+ } else if (opline->op1_type == IS_TMP_VAR && (opline-1)->opcode == ZEND_INSTANCEOF &&
+ opline->op1.var == (opline-1)->result.var && (opline-1)->op1_type == IS_CV &&
+ (opline-1)->op2_type == IS_CONST) {
+ int var = EX_VAR_TO_NUM((opline-1)->op1.var);
+ zend_string *lcname = Z_STR_P(CRT_CONSTANT((opline-1)->op2) + 1);
+ zend_class_entry *ce = zend_hash_find_ptr(&script->class_table, lcname);
+ if (!ce) {
+ ce = zend_hash_find_ptr(CG(class_table), lcname);
+ if (!ce || ce->type != ZEND_INTERNAL_CLASS) {
+ continue;
+ }
+ }
+
+ if ((pi = add_pi(arena, op_array, dfg, ssa, j, bt, var))) {
+ pi_type_mask(pi, MAY_BE_OBJECT);
+ pi->constraint.type.ce = ce;
+ }
+ }
+ }
+}
+/* }}} */
+
+static int zend_ssa_rename(const zend_op_array *op_array, uint32_t build_flags, zend_ssa *ssa, int *var, int n) /* {{{ */
+{
+ zend_basic_block *blocks = ssa->cfg.blocks;
+ zend_ssa_block *ssa_blocks = ssa->blocks;
+ zend_ssa_op *ssa_ops = ssa->ops;
+ int ssa_vars_count = ssa->vars_count;
+ int i, j;
+ zend_op *opline, *end;
+ int *tmp = NULL;
+ ALLOCA_FLAG(use_heap);
+
+ // FIXME: Can we optimize this copying out in some cases?
+ if (blocks[n].next_child >= 0) {
+ tmp = do_alloca(sizeof(int) * (op_array->last_var + op_array->T), use_heap);
+ memcpy(tmp, var, sizeof(int) * (op_array->last_var + op_array->T));
+ var = tmp;
+ }
+
+ if (ssa_blocks[n].phis) {
+ zend_ssa_phi *phi = ssa_blocks[n].phis;
+ do {
+ if (phi->ssa_var < 0) {
+ phi->ssa_var = ssa_vars_count;
+ var[phi->var] = ssa_vars_count;
+ ssa_vars_count++;
+ } else {
+ var[phi->var] = phi->ssa_var;
+ }
+ phi = phi->next;
+ } while (phi);
+ }
+
+ opline = op_array->opcodes + blocks[n].start;
+ end = opline + blocks[n].len;
+ for (; opline < end; opline++) {
+ uint32_t k = opline - op_array->opcodes;
+ if (opline->opcode != ZEND_OP_DATA) {
+ zend_op *next = opline + 1;
+ if (next < end && next->opcode == ZEND_OP_DATA) {
+ if (next->op1_type == IS_CV) {
+ ssa_ops[k + 1].op1_use = var[EX_VAR_TO_NUM(next->op1.var)];
+ //USE_SSA_VAR(next->op1.var);
+ } else if (next->op1_type & (IS_VAR|IS_TMP_VAR)) {
+ ssa_ops[k + 1].op1_use = var[EX_VAR_TO_NUM(next->op1.var)];
+ //USE_SSA_VAR(op_array->last_var + next->op1.var);
+ }
+ if (next->op2_type == IS_CV) {
+ ssa_ops[k + 1].op2_use = var[EX_VAR_TO_NUM(next->op2.var)];
+ //USE_SSA_VAR(next->op2.var);
+ } else if (next->op2_type & (IS_VAR|IS_TMP_VAR)) {
+ ssa_ops[k + 1].op2_use = var[EX_VAR_TO_NUM(next->op2.var)];
+ //USE_SSA_VAR(op_array->last_var + next->op2.var);
+ }
+ }
+ if (opline->op1_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
+ ssa_ops[k].op1_use = var[EX_VAR_TO_NUM(opline->op1.var)];
+ //USE_SSA_VAR(op_array->last_var + opline->op1.var)
+ }
+ if (opline->opcode == ZEND_FE_FETCH_R || opline->opcode == ZEND_FE_FETCH_RW) {
+ if (opline->op2_type == IS_CV) {
+ ssa_ops[k].op2_use = var[EX_VAR_TO_NUM(opline->op2.var)];
+ }
+ ssa_ops[k].op2_def = ssa_vars_count;
+ var[EX_VAR_TO_NUM(opline->op2.var)] = ssa_vars_count;
+ ssa_vars_count++;
+ //NEW_SSA_VAR(opline->op2.var)
+ } else if (opline->op2_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
+ ssa_ops[k].op2_use = var[EX_VAR_TO_NUM(opline->op2.var)];
+ //USE_SSA_VAR(op_array->last_var + opline->op2.var)
+ }
+ switch (opline->opcode) {
+ case ZEND_ASSIGN:
+ if (opline->op1_type == IS_CV) {
+ ssa_ops[k].op1_def = ssa_vars_count;
+ var[EX_VAR_TO_NUM(opline->op1.var)] = ssa_vars_count;
+ ssa_vars_count++;
+ //NEW_SSA_VAR(opline->op1.var)
+ }
+ if ((build_flags & ZEND_SSA_RC_INFERENCE) && opline->op2_type == IS_CV) {
+ ssa_ops[k].op2_def = ssa_vars_count;
+ var[EX_VAR_TO_NUM(opline->op2.var)] = ssa_vars_count;
+ ssa_vars_count++;
+ //NEW_SSA_VAR(opline->op2.var)
+ }
+ break;
+ case ZEND_ASSIGN_REF:
+//TODO: ???
+ if (opline->op1_type == IS_CV) {
+ ssa_ops[k].op1_def = ssa_vars_count;
+ var[EX_VAR_TO_NUM(opline->op1.var)] = ssa_vars_count;
+ ssa_vars_count++;
+ //NEW_SSA_VAR(opline->op1.var)
+ }
+ if (opline->op2_type == IS_CV) {
+ ssa_ops[k].op2_def = ssa_vars_count;
+ var[EX_VAR_TO_NUM(opline->op2.var)] = ssa_vars_count;
+ ssa_vars_count++;
+ //NEW_SSA_VAR(opline->op2.var)
+ }
+ break;
+ case ZEND_BIND_GLOBAL:
+ case ZEND_BIND_STATIC:
+ if (opline->op1_type == IS_CV) {
+ ssa_ops[k].op1_def = ssa_vars_count;
+ var[EX_VAR_TO_NUM(opline->op1.var)] = ssa_vars_count;
+ ssa_vars_count++;
+ //NEW_SSA_VAR(opline->op1.var)
+ }
+ break;
+ case ZEND_ASSIGN_DIM:
+ case ZEND_ASSIGN_OBJ:
+ if (opline->op1_type == IS_CV) {
+ ssa_ops[k].op1_def = ssa_vars_count;
+ var[EX_VAR_TO_NUM(opline->op1.var)] = ssa_vars_count;
+ ssa_vars_count++;
+ //NEW_SSA_VAR(opline->op1.var)
+ }
+ if ((build_flags & ZEND_SSA_RC_INFERENCE) && next->op1_type == IS_CV) {
+ ssa_ops[k + 1].op1_def = ssa_vars_count;
+ var[EX_VAR_TO_NUM(next->op1.var)] = ssa_vars_count;
+ ssa_vars_count++;
+ //NEW_SSA_VAR(next->op1.var)
+ }
+ break;
+ case ZEND_ADD_ARRAY_ELEMENT:
+ ssa_ops[k].result_use = var[EX_VAR_TO_NUM(opline->result.var)];
+ case ZEND_INIT_ARRAY:
+ if (((build_flags & ZEND_SSA_RC_INFERENCE)
+ || (opline->extended_value & ZEND_ARRAY_ELEMENT_REF))
+ && opline->op1_type == IS_CV) {
+ ssa_ops[k].op1_def = ssa_vars_count;
+ var[EX_VAR_TO_NUM(opline->op1.var)] = ssa_vars_count;
+ ssa_vars_count++;
+ //NEW_SSA_VAR(opline+->op1.var)
+ }
+ break;
+ case ZEND_SEND_VAR_NO_REF:
+ case ZEND_SEND_VAR_NO_REF_EX:
+ case ZEND_SEND_VAR_EX:
+ case ZEND_SEND_REF:
+ case ZEND_SEND_UNPACK:
+ case ZEND_FE_RESET_RW:
+//TODO: ???
+ if (opline->op1_type == IS_CV) {
+ ssa_ops[k].op1_def = ssa_vars_count;
+ var[EX_VAR_TO_NUM(opline->op1.var)] = ssa_vars_count;
+ ssa_vars_count++;
+ //NEW_SSA_VAR(opline->op1.var)
+ }
+ break;
+ case ZEND_FE_RESET_R:
+ if ((build_flags & ZEND_SSA_RC_INFERENCE) && opline->op1_type == IS_CV) {
+ ssa_ops[k].op1_def = ssa_vars_count;
+ var[EX_VAR_TO_NUM(opline->op1.var)] = ssa_vars_count;
+ ssa_vars_count++;
+ //NEW_SSA_VAR(opline->op1.var)
+ }
+ break;
+ case ZEND_ASSIGN_ADD:
+ case ZEND_ASSIGN_SUB:
+ case ZEND_ASSIGN_MUL:
+ case ZEND_ASSIGN_DIV:
+ case ZEND_ASSIGN_MOD:
+ case ZEND_ASSIGN_SL:
+ case ZEND_ASSIGN_SR:
+ case ZEND_ASSIGN_CONCAT:
+ case ZEND_ASSIGN_BW_OR:
+ case ZEND_ASSIGN_BW_AND:
+ case ZEND_ASSIGN_BW_XOR:
+ case ZEND_ASSIGN_POW:
+ case ZEND_PRE_INC:
+ case ZEND_PRE_DEC:
+ case ZEND_POST_INC:
+ case ZEND_POST_DEC:
+ if (opline->op1_type == IS_CV) {
+ ssa_ops[k].op1_def = ssa_vars_count;
+ var[EX_VAR_TO_NUM(opline->op1.var)] = ssa_vars_count;
+ ssa_vars_count++;
+ //NEW_SSA_VAR(opline->op1.var)
+ }
+ break;
+ case ZEND_UNSET_VAR:
+ if (opline->extended_value & ZEND_QUICK_SET) {
+ ssa_ops[k].op1_def = ssa_vars_count;
+ var[EX_VAR_TO_NUM(opline->op1.var)] = ssa_vars_count;
+ ssa_vars_count++;
+ }
+ break;
+ case ZEND_UNSET_DIM:
+ case ZEND_UNSET_OBJ:
+ case ZEND_FETCH_DIM_W:
+ case ZEND_FETCH_DIM_RW:
+ case ZEND_FETCH_DIM_FUNC_ARG:
+ case ZEND_FETCH_DIM_UNSET:
+ case ZEND_FETCH_OBJ_W:
+ case ZEND_FETCH_OBJ_RW:
+ case ZEND_FETCH_OBJ_FUNC_ARG:
+ case ZEND_FETCH_OBJ_UNSET:
+ if (opline->op1_type == IS_CV) {
+ ssa_ops[k].op1_def = ssa_vars_count;
+ var[EX_VAR_TO_NUM(opline->op1.var)] = ssa_vars_count;
+ ssa_vars_count++;
+ //NEW_SSA_VAR(opline->op1.var)
+ }
+ break;
+ case ZEND_BIND_LEXICAL:
+ if (opline->extended_value || (build_flags & ZEND_SSA_RC_INFERENCE)) {
+ ssa_ops[k].op2_def = ssa_vars_count;
+ var[EX_VAR_TO_NUM(opline->op2.var)] = ssa_vars_count;
+ ssa_vars_count++;
+ }
+ break;
+ case ZEND_YIELD:
+ if (opline->op1_type == IS_CV
+ && ((op_array->fn_flags & ZEND_ACC_RETURN_REFERENCE)
+ || (build_flags & ZEND_SSA_RC_INFERENCE))) {
+ ssa_ops[k].op1_def = ssa_vars_count;
+ var[EX_VAR_TO_NUM(opline->op1.var)] = ssa_vars_count;
+ ssa_vars_count++;
+ }
+ break;
+ case ZEND_VERIFY_RETURN_TYPE:
+ if (opline->op1_type & (IS_TMP_VAR|IS_VAR|IS_CV)) {
+ ssa_ops[k].op1_def = ssa_vars_count;
+ var[EX_VAR_TO_NUM(opline->op1.var)] = ssa_vars_count;
+ ssa_vars_count++;
+ //NEW_SSA_VAR(opline->op1.var)
+ }
+ break;
+ default:
+ break;
+ }
+ if (opline->result_type == IS_CV) {
+ ssa_ops[k].result_def = ssa_vars_count;
+ var[EX_VAR_TO_NUM(opline->result.var)] = ssa_vars_count;
+ ssa_vars_count++;
+ //NEW_SSA_VAR(opline->result.var)
+ } else if (opline->result_type & (IS_VAR|IS_TMP_VAR)) {
+ ssa_ops[k].result_def = ssa_vars_count;
+ var[EX_VAR_TO_NUM(opline->result.var)] = ssa_vars_count;
+ ssa_vars_count++;
+ //NEW_SSA_VAR(op_array->last_var + opline->result.var)
+ }
+ }
+ }
+
+ for (i = 0; i < 2; i++) {
+ int succ = blocks[n].successors[i];
+ if (succ >= 0) {
+ zend_ssa_phi *p;
+ for (p = ssa_blocks[succ].phis; p; p = p->next) {
+ if (p->pi == n) {
+ /* e-SSA Pi */
+ if (p->has_range_constraint) {
+ if (p->constraint.range.min_var >= 0) {
+ p->constraint.range.min_ssa_var = var[p->constraint.range.min_var];
+ }
+ if (p->constraint.range.max_var >= 0) {
+ p->constraint.range.max_ssa_var = var[p->constraint.range.max_var];
+ }
+ }
+ for (j = 0; j < blocks[succ].predecessors_count; j++) {
+ p->sources[j] = var[p->var];
+ }
+ if (p->ssa_var < 0) {
+ p->ssa_var = ssa_vars_count;
+ ssa_vars_count++;
+ }
+ } else if (p->pi < 0) {
+ /* Normal Phi */
+ for (j = 0; j < blocks[succ].predecessors_count; j++)
+ if (ssa->cfg.predecessors[blocks[succ].predecessor_offset + j] == n) {
+ break;
+ }
+ ZEND_ASSERT(j < blocks[succ].predecessors_count);
+ p->sources[j] = var[p->var];
+ }
+ }
+ for (p = ssa_blocks[succ].phis; p && (p->pi >= 0); p = p->next) {
+ if (p->pi == n) {
+ zend_ssa_phi *q = p->next;
+ while (q) {
+ if (q->pi < 0 && q->var == p->var) {
+ for (j = 0; j < blocks[succ].predecessors_count; j++) {
+ if (ssa->cfg.predecessors[blocks[succ].predecessor_offset + j] == n) {
+ break;
+ }
+ }
+ ZEND_ASSERT(j < blocks[succ].predecessors_count);
+ q->sources[j] = p->ssa_var;
+ }
+ q = q->next;
+ }
+ }
+ }
+ }
+ }
+
+ ssa->vars_count = ssa_vars_count;
+
+ j = blocks[n].children;
+ while (j >= 0) {
+ // FIXME: Tail call optimization?
+ if (zend_ssa_rename(op_array, build_flags, ssa, var, j) != SUCCESS)
+ return FAILURE;
+ j = blocks[j].next_child;
+ }
+
+ if (tmp) {
+ free_alloca(tmp, use_heap);
+ }
+
+ return SUCCESS;
+}
+/* }}} */
+
+int zend_build_ssa(zend_arena **arena, const zend_script *script, const zend_op_array *op_array, uint32_t build_flags, zend_ssa *ssa, uint32_t *func_flags) /* {{{ */
+{
+ zend_basic_block *blocks = ssa->cfg.blocks;
+ zend_ssa_block *ssa_blocks;
+ int blocks_count = ssa->cfg.blocks_count;
+ uint32_t set_size;
+ zend_bitset def, in, phi;
+ int *var = NULL;
+ int i, j, k, changed;
+ zend_dfg dfg;
+ ALLOCA_FLAG(dfg_use_heap)
+ ALLOCA_FLAG(var_use_heap)
+
+ ssa->rt_constants = (build_flags & ZEND_RT_CONSTANTS);
+ ssa_blocks = zend_arena_calloc(arena, blocks_count, sizeof(zend_ssa_block));
+ if (!ssa_blocks) {
+ return FAILURE;
+ }
+ ssa->blocks = ssa_blocks;
+
+ /* Compute Variable Liveness */
+ dfg.vars = op_array->last_var + op_array->T;
+ dfg.size = set_size = zend_bitset_len(dfg.vars);
+ dfg.tmp = do_alloca((set_size * sizeof(zend_ulong)) * (blocks_count * 4 + 1), dfg_use_heap);
+ memset(dfg.tmp, 0, (set_size * sizeof(zend_ulong)) * (blocks_count * 4 + 1));
+ dfg.def = dfg.tmp + set_size;
+ dfg.use = dfg.def + set_size * blocks_count;
+ dfg.in = dfg.use + set_size * blocks_count;
+ dfg.out = dfg.in + set_size * blocks_count;
+
+ if (zend_build_dfg(op_array, &ssa->cfg, &dfg, build_flags) != SUCCESS) {
+ free_alloca(dfg.tmp, dfg_use_heap);
+ return FAILURE;
+ }
+
+ if (build_flags & ZEND_SSA_DEBUG_LIVENESS) {
+ zend_dump_dfg(op_array, &ssa->cfg, &dfg);
+ }
+
+ def = dfg.def;
+ in = dfg.in;
+
+ /* Reuse the "use" set, as we no longer need it */
+ phi = dfg.use;
+ zend_bitset_clear(phi, set_size * blocks_count);
+
+ /* Place e-SSA pis. This will add additional "def" points, so it must
+ * happen before def propagation. */
+ place_essa_pis(arena, script, op_array, build_flags, ssa, &dfg);
+
+ /* SSA construction, Step 1: Propagate "def" sets in merge points */
+ do {
+ changed = 0;
+ for (j = 0; j < blocks_count; j++) {
+ zend_bitset def_j = def + j * set_size, phi_j = phi + j * set_size;
+ if ((blocks[j].flags & ZEND_BB_REACHABLE) == 0) {
+ continue;
+ }
+ if (blocks[j].predecessors_count > 1) {
+ if (blocks[j].flags & ZEND_BB_IRREDUCIBLE_LOOP) {
+ /* Prevent any values from flowing into irreducible loops by
+ replacing all incoming values with explicit phis. The
+ register allocator depends on this property. */
+ zend_bitset_union(phi_j, in + (j * set_size), set_size);
+ } else {
+ for (k = 0; k < blocks[j].predecessors_count; k++) {
+ i = ssa->cfg.predecessors[blocks[j].predecessor_offset + k];
+ while (i != -1 && i != blocks[j].idom) {
+ zend_bitset_union_with_intersection(
+ phi_j, phi_j, def + (i * set_size), in + (j * set_size), set_size);
+ i = blocks[i].idom;
+ }
+ }
+ }
+ if (!zend_bitset_subset(phi_j, def_j, set_size)) {
+ zend_bitset_union(def_j, phi_j, set_size);
+ changed = 1;
+ }
+ }
+ }
+ } while (changed);
+
+ /* SSA construction, Step 2: Phi placement based on Dominance Frontiers */
+ var = do_alloca(sizeof(int) * (op_array->last_var + op_array->T), var_use_heap);
+ if (!var) {
+ free_alloca(dfg.tmp, dfg_use_heap);
+ return FAILURE;
+ }
+
+ for (j = 0; j < blocks_count; j++) {
+ if ((blocks[j].flags & ZEND_BB_REACHABLE) == 0) {
+ continue;
+ }
+ if (!zend_bitset_empty(phi + j * set_size, set_size)) {
+ ZEND_BITSET_REVERSE_FOREACH(phi + j * set_size, set_size, i) {
+ zend_ssa_phi *phi = zend_arena_calloc(arena, 1,
+ sizeof(zend_ssa_phi) +
+ sizeof(int) * blocks[j].predecessors_count +
+ sizeof(void*) * blocks[j].predecessors_count);
+
+ phi->sources = (int*)(((char*)phi) + sizeof(zend_ssa_phi));
+ memset(phi->sources, 0xff, sizeof(int) * blocks[j].predecessors_count);
+ phi->use_chains = (zend_ssa_phi**)(((char*)phi->sources) + sizeof(int) * ssa->cfg.blocks[j].predecessors_count);
+
+ phi->pi = -1;
+ phi->var = i;
+ phi->ssa_var = -1;
+
+ /* Place phis after pis */
+ {
+ zend_ssa_phi **pp = &ssa_blocks[j].phis;
+ while (*pp) {
+ if ((*pp)->pi < 0) {
+ break;
+ }
+ pp = &(*pp)->next;
+ }
+ phi->next = *pp;
+ *pp = phi;
+ }
+ } ZEND_BITSET_FOREACH_END();
+ }
+ }
+
+ if (build_flags & ZEND_SSA_DEBUG_PHI_PLACEMENT) {
+ zend_dump_phi_placement(op_array, ssa);
+ }
+
+ /* SSA construction, Step 3: Renaming */
+ ssa->ops = zend_arena_calloc(arena, op_array->last, sizeof(zend_ssa_op));
+ memset(ssa->ops, 0xff, op_array->last * sizeof(zend_ssa_op));
+ memset(var + op_array->last_var, 0xff, op_array->T * sizeof(int));
+ /* Create uninitialized SSA variables for each CV */
+ for (j = 0; j < op_array->last_var; j++) {
+ var[j] = j;
+ }
+ ssa->vars_count = op_array->last_var;
+ if (zend_ssa_rename(op_array, build_flags, ssa, var, 0) != SUCCESS) {
+ free_alloca(var, var_use_heap);
+ free_alloca(dfg.tmp, dfg_use_heap);
+ return FAILURE;
+ }
+
+ free_alloca(var, var_use_heap);
+ free_alloca(dfg.tmp, dfg_use_heap);
+
+ return SUCCESS;
+}
+/* }}} */
+
+int zend_ssa_compute_use_def_chains(zend_arena **arena, const zend_op_array *op_array, zend_ssa *ssa) /* {{{ */
+{
+ zend_ssa_var *ssa_vars;
+ int i;
+
+ if (!ssa->vars) {
+ ssa->vars = zend_arena_calloc(arena, ssa->vars_count, sizeof(zend_ssa_var));
+ }
+ ssa_vars = ssa->vars;
+
+ for (i = 0; i < op_array->last_var; i++) {
+ ssa_vars[i].var = i;
+ ssa_vars[i].scc = -1;
+ ssa_vars[i].definition = -1;
+ ssa_vars[i].use_chain = -1;
+ }
+ for (i = op_array->last_var; i < ssa->vars_count; i++) {
+ ssa_vars[i].var = -1;
+ ssa_vars[i].scc = -1;
+ ssa_vars[i].definition = -1;
+ ssa_vars[i].use_chain = -1;
+ }
+
+ for (i = op_array->last - 1; i >= 0; i--) {
+ zend_ssa_op *op = ssa->ops + i;
+
+ if (op->op1_use >= 0) {
+ op->op1_use_chain = ssa_vars[op->op1_use].use_chain;
+ ssa_vars[op->op1_use].use_chain = i;
+ }
+ if (op->op2_use >= 0 && op->op2_use != op->op1_use) {
+ op->op2_use_chain = ssa_vars[op->op2_use].use_chain;
+ ssa_vars[op->op2_use].use_chain = i;
+ }
+ if (op->result_use >= 0) {
+ op->res_use_chain = ssa_vars[op->result_use].use_chain;
+ ssa_vars[op->result_use].use_chain = i;
+ }
+ if (op->op1_def >= 0) {
+ ssa_vars[op->op1_def].var = EX_VAR_TO_NUM(op_array->opcodes[i].op1.var);
+ ssa_vars[op->op1_def].definition = i;
+ }
+ if (op->op2_def >= 0) {
+ ssa_vars[op->op2_def].var = EX_VAR_TO_NUM(op_array->opcodes[i].op2.var);
+ ssa_vars[op->op2_def].definition = i;
+ }
+ if (op->result_def >= 0) {
+ ssa_vars[op->result_def].var = EX_VAR_TO_NUM(op_array->opcodes[i].result.var);
+ ssa_vars[op->result_def].definition = i;
+ }
+ }
+
+ for (i = 0; i < ssa->cfg.blocks_count; i++) {
+ zend_ssa_phi *phi = ssa->blocks[i].phis;
+ while (phi) {
+ phi->block = i;
+ ssa_vars[phi->ssa_var].var = phi->var;
+ ssa_vars[phi->ssa_var].definition_phi = phi;
+ if (phi->pi >= 0) {
+ if (phi->sources[0] >= 0) {
+ zend_ssa_phi *p = ssa_vars[phi->sources[0]].phi_use_chain;
+ while (p && p != phi) {
+ p = zend_ssa_next_use_phi(ssa, phi->sources[0], p);
+ }
+ if (!p) {
+ phi->use_chains[0] = ssa_vars[phi->sources[0]].phi_use_chain;
+ ssa_vars[phi->sources[0]].phi_use_chain = phi;
+ }
+ }
+ if (phi->has_range_constraint) {
+ /* min and max variables can't be used together */
+ zend_ssa_range_constraint *constraint = &phi->constraint.range;
+ if (constraint->min_ssa_var >= 0) {
+ phi->sym_use_chain = ssa_vars[constraint->min_ssa_var].sym_use_chain;
+ ssa_vars[constraint->min_ssa_var].sym_use_chain = phi;
+ } else if (constraint->max_ssa_var >= 0) {
+ phi->sym_use_chain = ssa_vars[constraint->max_ssa_var].sym_use_chain;
+ ssa_vars[constraint->max_ssa_var].sym_use_chain = phi;
+ }
+ }
+ } else {
+ int j;
+
+ for (j = 0; j < ssa->cfg.blocks[i].predecessors_count; j++) {
+ if (phi->sources[j] >= 0) {
+ zend_ssa_phi *p = ssa_vars[phi->sources[j]].phi_use_chain;
+ while (p && p != phi) {
+ p = zend_ssa_next_use_phi(ssa, phi->sources[j], p);
+ }
+ if (!p) {
+ phi->use_chains[j] = ssa_vars[phi->sources[j]].phi_use_chain;
+ ssa_vars[phi->sources[j]].phi_use_chain = phi;
+ }
+ }
+ }
+ }
+ phi = phi->next;
+ }
+ }
+
+ return SUCCESS;
+}
+/* }}} */
+
+int zend_ssa_unlink_use_chain(zend_ssa *ssa, int op, int var) /* {{{ */
+{
+ if (ssa->vars[var].use_chain == op) {
+ ssa->vars[var].use_chain = zend_ssa_next_use(ssa->ops, var, op);
+ return 1;
+ } else {
+ int use = ssa->vars[var].use_chain;
+
+ while (use >= 0) {
+ if (ssa->ops[use].result_use == var) {
+ if (ssa->ops[use].res_use_chain == op) {
+ ssa->ops[use].res_use_chain = zend_ssa_next_use(ssa->ops, var, op);
+ return 1;
+ } else {
+ use = ssa->ops[use].res_use_chain;
+ }
+ } else if (ssa->ops[use].op1_use == var) {
+ if (ssa->ops[use].op1_use_chain == op) {
+ ssa->ops[use].op1_use_chain = zend_ssa_next_use(ssa->ops, var, op);
+ return 1;
+ } else {
+ use = ssa->ops[use].op1_use_chain;
+ }
+ } else if (ssa->ops[use].op2_use == var) {
+ if (ssa->ops[use].op2_use_chain == op) {
+ ssa->ops[use].op2_use_chain = zend_ssa_next_use(ssa->ops, var, op);
+ return 1;
+ } else {
+ use = ssa->ops[use].op2_use_chain;
+ }
+ } else {
+ break;
+ }
+ }
+ /* something wrong */
+ ZEND_ASSERT(0);
+ return 0;
+ }
+}
+/* }}} */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * indent-tabs-mode: t
+ * End:
+ */
diff --git a/ext/opcache/Optimizer/zend_ssa.h b/ext/opcache/Optimizer/zend_ssa.h
new file mode 100644
index 0000000000..653a61fc10
--- /dev/null
+++ b/ext/opcache/Optimizer/zend_ssa.h
@@ -0,0 +1,168 @@
+/*
+ +----------------------------------------------------------------------+
+ | Zend Engine, SSA - Static Single Assignment Form |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1998-2016 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> |
+ +----------------------------------------------------------------------+
+*/
+
+#ifndef ZEND_SSA_H
+#define ZEND_SSA_H
+
+#include "zend_optimizer.h"
+#include "zend_cfg.h"
+
+typedef struct _zend_ssa_range {
+ zend_long min;
+ zend_long max;
+ zend_bool underflow;
+ zend_bool overflow;
+} zend_ssa_range;
+
+typedef enum _zend_ssa_negative_lat {
+ NEG_NONE = 0,
+ NEG_INIT = 1,
+ NEG_INVARIANT = 2,
+ NEG_USE_LT = 3,
+ NEG_USE_GT = 4,
+ NEG_UNKNOWN = 5
+} zend_ssa_negative_lat;
+
+/* Special kind of SSA Phi function used in eSSA */
+typedef struct _zend_ssa_range_constraint {
+ zend_ssa_range range; /* simple range constraint */
+ int min_var;
+ int max_var;
+ int min_ssa_var; /* ((min_var>0) ? MIN(ssa_var) : 0) + range.min */
+ int max_ssa_var; /* ((max_var>0) ? MAX(ssa_var) : 0) + range.max */
+ zend_ssa_negative_lat negative;
+} zend_ssa_range_constraint;
+
+typedef struct _zend_ssa_type_constraint {
+ uint32_t type_mask; /* Type mask to intersect with */
+ zend_class_entry *ce; /* Class entry for instanceof constraints */
+} zend_ssa_type_constraint;
+
+typedef union _zend_ssa_pi_constraint {
+ zend_ssa_range_constraint range;
+ zend_ssa_type_constraint type;
+} zend_ssa_pi_constraint;
+
+/* SSA Phi - ssa_var = Phi(source0, source1, ...sourceN) */
+typedef struct _zend_ssa_phi zend_ssa_phi;
+struct _zend_ssa_phi {
+ zend_ssa_phi *next; /* next Phi in the same BB */
+ int pi; /* if >= 0 this is actually a e-SSA Pi */
+ zend_ssa_pi_constraint constraint; /* e-SSA Pi constraint */
+ int var; /* Original CV, VAR or TMP variable index */
+ int ssa_var; /* SSA variable index */
+ int block; /* current BB index */
+ int visited : 1; /* flag to avoid recursive processing */
+ int has_range_constraint : 1;
+ zend_ssa_phi **use_chains;
+ zend_ssa_phi *sym_use_chain;
+ int *sources; /* Array of SSA IDs that produce this var.
+ As many as this block has
+ predecessors. */
+};
+
+typedef struct _zend_ssa_block {
+ zend_ssa_phi *phis;
+} zend_ssa_block;
+
+typedef struct _zend_ssa_op {
+ int op1_use;
+ int op2_use;
+ int result_use;
+ int op1_def;
+ int op2_def;
+ int result_def;
+ int op1_use_chain;
+ int op2_use_chain;
+ int res_use_chain;
+} zend_ssa_op;
+
+typedef struct _zend_ssa_var {
+ int var; /* original var number; op.var for CVs and following numbers for VARs and TMP_VARs */
+ int scc; /* strongly connected component */
+ int definition; /* opcode that defines this value */
+ zend_ssa_phi *definition_phi; /* phi that defines this value */
+ int use_chain; /* uses of this value, linked through opN_use_chain */
+ zend_ssa_phi *phi_use_chain; /* uses of this value in Phi, linked through use_chain */
+ zend_ssa_phi *sym_use_chain; /* uses of this value in Pi constaints */
+ unsigned int no_val : 1; /* value doesn't mater (used as op1 in ZEND_ASSIGN) */
+ unsigned int scc_entry : 1;
+} zend_ssa_var;
+
+typedef struct _zend_ssa_var_info {
+ uint32_t type; /* inferred type (see zend_inference.h) */
+ zend_ssa_range range;
+ zend_class_entry *ce;
+ unsigned int has_range : 1;
+ unsigned int is_instanceof : 1; /* 0 - class == "ce", 1 - may be child of "ce" */
+ unsigned int recursive : 1;
+ unsigned int use_as_double : 1;
+} zend_ssa_var_info;
+
+typedef struct _zend_ssa {
+ zend_cfg cfg; /* control flow graph */
+ int rt_constants; /* run-time or compile-time */
+ int vars_count; /* number of SSA variables */
+ zend_ssa_block *blocks; /* array of SSA blocks */
+ zend_ssa_op *ops; /* array of SSA instructions */
+ zend_ssa_var *vars; /* use/def chain of SSA variables */
+ int sccs; /* number of SCCs */
+ zend_ssa_var_info *var_info;
+} zend_ssa;
+
+BEGIN_EXTERN_C()
+
+int zend_build_ssa(zend_arena **arena, const zend_script *script, const zend_op_array *op_array, uint32_t build_flags, zend_ssa *ssa, uint32_t *func_flags);
+int zend_ssa_compute_use_def_chains(zend_arena **arena, const zend_op_array *op_array, zend_ssa *ssa);
+int zend_ssa_unlink_use_chain(zend_ssa *ssa, int op, int var);
+
+END_EXTERN_C()
+
+static zend_always_inline int zend_ssa_next_use(const zend_ssa_op *ssa_op, int var, int use)
+{
+ ssa_op += use;
+ if (ssa_op->result_use == var) {
+ return ssa_op->res_use_chain;
+ }
+ return (ssa_op->op1_use == var) ? ssa_op->op1_use_chain : ssa_op->op2_use_chain;
+}
+
+static zend_always_inline zend_ssa_phi* zend_ssa_next_use_phi(const zend_ssa *ssa, int var, const zend_ssa_phi *p)
+{
+ if (p->pi >= 0) {
+ return p->use_chains[0];
+ } else {
+ int j;
+ for (j = 0; j < ssa->cfg.blocks[p->block].predecessors_count; j++) {
+ if (p->sources[j] == var) {
+ return p->use_chains[j];
+ }
+ }
+ }
+ return NULL;
+}
+
+#endif /* ZEND_SSA_H */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * indent-tabs-mode: t
+ * End:
+ */
diff --git a/ext/opcache/Optimizer/zend_worklist.h b/ext/opcache/Optimizer/zend_worklist.h
new file mode 100644
index 0000000000..a1db05482b
--- /dev/null
+++ b/ext/opcache/Optimizer/zend_worklist.h
@@ -0,0 +1,137 @@
+/*
+ +----------------------------------------------------------------------+
+ | Zend Engine |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1998-2016 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: Andy Wingo <wingo@igalia.com> |
+ +----------------------------------------------------------------------+
+*/
+
+/* $Id:$ */
+
+#ifndef _ZEND_WORKLIST_H_
+#define _ZEND_WORKLIST_H_
+
+#include "zend_arena.h"
+#include "zend_bitset.h"
+
+typedef struct _zend_worklist_stack {
+ int *buf;
+ int len;
+ int capacity;
+} zend_worklist_stack;
+
+#define ZEND_WORKLIST_STACK_ALLOCA(s, _len, use_heap) do { \
+ (s)->buf = (int*)do_alloca(sizeof(int) * _len, use_heap); \
+ (s)->len = 0; \
+ (s)->capacity = _len; \
+ } while (0)
+
+#define ZEND_WORKLIST_STACK_FREE_ALLOCA(s, use_heap) \
+ free_alloca((s)->buf, use_heap)
+
+static inline int zend_worklist_stack_prepare(zend_arena **arena, zend_worklist_stack *stack, int len)
+{
+ ZEND_ASSERT(len >= 0);
+
+ stack->buf = (int*)zend_arena_calloc(arena, sizeof(*stack->buf), len);
+ if (!stack->buf) {
+ return FAILURE;
+ }
+ stack->len = 0;
+ stack->capacity = len;
+
+ return SUCCESS;
+}
+
+static inline void zend_worklist_stack_push(zend_worklist_stack *stack, int i)
+{
+ ZEND_ASSERT(stack->len < stack->capacity);
+ stack->buf[stack->len++] = i;
+}
+
+static inline int zend_worklist_stack_peek(zend_worklist_stack *stack)
+{
+ ZEND_ASSERT(stack->len);
+ return stack->buf[stack->len - 1];
+}
+
+static inline int zend_worklist_stack_pop(zend_worklist_stack *stack)
+{
+ ZEND_ASSERT(stack->len);
+ return stack->buf[--stack->len];
+}
+
+typedef struct _zend_worklist {
+ zend_bitset visited;
+ zend_worklist_stack stack;
+} zend_worklist;
+
+#define ZEND_WORKLIST_ALLOCA(w, _len, use_heap) do { \
+ (w)->stack.buf = (int*)do_alloca(sizeof(int) * _len + sizeof(zend_ulong) * zend_bitset_len(_len), use_heap); \
+ (w)->stack.len = 0; \
+ (w)->stack.capacity = _len; \
+ (w)->visited = (zend_bitset)((w)->stack.buf + _len); \
+ memset((w)->visited, 0, sizeof(zend_ulong) * zend_bitset_len(_len)); \
+ } while (0)
+
+#define ZEND_WORKLIST_FREE_ALLOCA(w, use_heap) \
+ free_alloca((w)->stack.buf, use_heap)
+
+static inline int zend_worklist_prepare(zend_arena **arena, zend_worklist *worklist, int len)
+{
+ ZEND_ASSERT(len >= 0);
+ worklist->visited = (zend_bitset)zend_arena_calloc(arena, sizeof(zend_ulong), zend_bitset_len(len));
+ if (!worklist->visited) {
+ return FAILURE;
+ }
+ return zend_worklist_stack_prepare(arena, &worklist->stack, len);
+}
+
+static inline int zend_worklist_len(zend_worklist *worklist)
+{
+ return worklist->stack.len;
+}
+
+static inline int zend_worklist_push(zend_worklist *worklist, int i)
+{
+ ZEND_ASSERT(i >= 0 && i < worklist->stack.capacity);
+
+ if (zend_bitset_in(worklist->visited, i)) {
+ return 0;
+ }
+
+ zend_bitset_incl(worklist->visited, i);
+ zend_worklist_stack_push(&worklist->stack, i);
+ return 1;
+}
+
+static inline int zend_worklist_peek(zend_worklist *worklist)
+{
+ return zend_worklist_stack_peek(&worklist->stack);
+}
+
+static inline int zend_worklist_pop(zend_worklist *worklist)
+{
+ /* Does not clear visited flag */
+ return zend_worklist_stack_pop(&worklist->stack);
+}
+
+#endif /* _ZEND_WORKLIST_H_ */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * indent-tabs-mode: t
+ * End:
+ */
diff --git a/ext/opcache/ZendAccelerator.c b/ext/opcache/ZendAccelerator.c
index 2cfe5390b3..222935bad0 100644
--- a/ext/opcache/ZendAccelerator.c
+++ b/ext/opcache/ZendAccelerator.c
@@ -523,6 +523,9 @@ static void accel_use_shm_interned_strings(void)
s[1] = 0;
CG(one_char_string)[j] = accel_new_interned_string(zend_string_init(s, 1, 0));
}
+ for (j = 0; j < CG(known_strings_count); j++) {
+ CG(known_strings)[j] = accel_new_interned_string(CG(known_strings)[j]);
+ }
/* function table hash keys */
for (idx = 0; idx < CG(function_table)->nNumUsed; idx++) {
@@ -879,17 +882,17 @@ static inline int do_validate_timestamps(zend_persistent_script *persistent_scri
* See bug #15140
*/
if (file_handle->opened_path) {
- if (persistent_script->full_path != file_handle->opened_path &&
- (ZSTR_LEN(persistent_script->full_path) != ZSTR_LEN(file_handle->opened_path) ||
- memcmp(ZSTR_VAL(persistent_script->full_path), ZSTR_VAL(file_handle->opened_path), ZSTR_LEN(file_handle->opened_path)) != 0)) {
+ if (persistent_script->script.filename != file_handle->opened_path &&
+ (ZSTR_LEN(persistent_script->script.filename) != ZSTR_LEN(file_handle->opened_path) ||
+ memcmp(ZSTR_VAL(persistent_script->script.filename), ZSTR_VAL(file_handle->opened_path), ZSTR_LEN(file_handle->opened_path)) != 0)) {
return FAILURE;
}
} else {
full_path_ptr = accelerator_orig_zend_resolve_path(file_handle->filename, strlen(file_handle->filename));
if (full_path_ptr &&
- persistent_script->full_path != full_path_ptr &&
- (ZSTR_LEN(persistent_script->full_path) != ZSTR_LEN(full_path_ptr) ||
- memcmp(ZSTR_VAL(persistent_script->full_path), ZSTR_VAL(full_path_ptr), ZSTR_LEN(full_path_ptr)) != 0)) {
+ persistent_script->script.filename != full_path_ptr &&
+ (ZSTR_LEN(persistent_script->script.filename) != ZSTR_LEN(full_path_ptr) ||
+ memcmp(ZSTR_VAL(persistent_script->script.filename), ZSTR_VAL(full_path_ptr), ZSTR_LEN(full_path_ptr)) != 0)) {
zend_string_release(full_path_ptr);
return FAILURE;
}
@@ -917,8 +920,8 @@ static inline int do_validate_timestamps(zend_persistent_script *persistent_scri
}
ps_handle.type = ZEND_HANDLE_FILENAME;
- ps_handle.filename = ZSTR_VAL(persistent_script->full_path);
- ps_handle.opened_path = persistent_script->full_path;
+ ps_handle.filename = ZSTR_VAL(persistent_script->script.filename);
+ ps_handle.opened_path = persistent_script->script.filename;
if (zend_get_file_handle_timestamp(&ps_handle, NULL) == persistent_script->timestamp) {
return SUCCESS;
@@ -1186,7 +1189,7 @@ static zend_persistent_script *cache_script_in_file_cache(zend_persistent_script
return new_persistent_script;
}
- if (!zend_accel_script_optimize(new_persistent_script)) {
+ if (!zend_optimize_script(&new_persistent_script->script, ZCG(accel_directives).optimization_level, ZCG(accel_directives).opt_debug_level)) {
return new_persistent_script;
}
@@ -1210,16 +1213,16 @@ static zend_persistent_script *cache_script_in_file_cache(zend_persistent_script
zend_shared_alloc_destroy_xlat_table();
new_persistent_script->is_phar =
- new_persistent_script->full_path &&
- strstr(ZSTR_VAL(new_persistent_script->full_path), ".phar") &&
- !strstr(ZSTR_VAL(new_persistent_script->full_path), "://");
+ new_persistent_script->script.filename &&
+ strstr(ZSTR_VAL(new_persistent_script->script.filename), ".phar") &&
+ !strstr(ZSTR_VAL(new_persistent_script->script.filename), "://");
/* Consistency check */
if ((char*)new_persistent_script->mem + new_persistent_script->size != (char*)ZCG(mem)) {
zend_accel_error(
((char*)new_persistent_script->mem + new_persistent_script->size < (char*)ZCG(mem)) ? ACCEL_LOG_ERROR : ACCEL_LOG_WARNING,
"Internal error: wrong size calculation: %s start=0x%08x, end=0x%08x, real=0x%08x\n",
- ZSTR_VAL(new_persistent_script->full_path),
+ ZSTR_VAL(new_persistent_script->script.filename),
new_persistent_script->mem,
(char *)new_persistent_script->mem + new_persistent_script->size,
ZCG(mem));
@@ -1244,7 +1247,7 @@ static zend_persistent_script *cache_script_in_shared_memory(zend_persistent_scr
return new_persistent_script;
}
- if (!zend_accel_script_optimize(new_persistent_script)) {
+ if (!zend_optimize_script(&new_persistent_script->script, ZCG(accel_directives).optimization_level, ZCG(accel_directives).opt_debug_level)) {
return new_persistent_script;
}
@@ -1262,7 +1265,7 @@ static zend_persistent_script *cache_script_in_shared_memory(zend_persistent_scr
/* Check if we still need to put the file into the cache (may be it was
* already stored by another process. This final check is done under
* exclusive lock) */
- bucket = zend_accel_hash_find_entry(&ZCSG(hash), new_persistent_script->full_path);
+ bucket = zend_accel_hash_find_entry(&ZCSG(hash), new_persistent_script->script.filename);
if (bucket) {
zend_persistent_script *existing_persistent_script = (zend_persistent_script *)bucket->data;
@@ -1304,16 +1307,16 @@ static zend_persistent_script *cache_script_in_shared_memory(zend_persistent_scr
zend_shared_alloc_destroy_xlat_table();
new_persistent_script->is_phar =
- new_persistent_script->full_path &&
- strstr(ZSTR_VAL(new_persistent_script->full_path), ".phar") &&
- !strstr(ZSTR_VAL(new_persistent_script->full_path), "://");
+ new_persistent_script->script.filename &&
+ strstr(ZSTR_VAL(new_persistent_script->script.filename), ".phar") &&
+ !strstr(ZSTR_VAL(new_persistent_script->script.filename), "://");
/* Consistency check */
if ((char*)new_persistent_script->mem + new_persistent_script->size != (char*)ZCG(mem)) {
zend_accel_error(
((char*)new_persistent_script->mem + new_persistent_script->size < (char*)ZCG(mem)) ? ACCEL_LOG_ERROR : ACCEL_LOG_WARNING,
"Internal error: wrong size calculation: %s start=0x%08x, end=0x%08x, real=0x%08x\n",
- ZSTR_VAL(new_persistent_script->full_path),
+ ZSTR_VAL(new_persistent_script->script.filename),
new_persistent_script->mem,
(char *)new_persistent_script->mem + new_persistent_script->size,
ZCG(mem));
@@ -1322,14 +1325,14 @@ static zend_persistent_script *cache_script_in_shared_memory(zend_persistent_scr
new_persistent_script->dynamic_members.checksum = zend_accel_script_checksum(new_persistent_script);
/* store script structure in the hash table */
- bucket = zend_accel_hash_update(&ZCSG(hash), ZSTR_VAL(new_persistent_script->full_path), ZSTR_LEN(new_persistent_script->full_path), 0, new_persistent_script);
+ bucket = zend_accel_hash_update(&ZCSG(hash), ZSTR_VAL(new_persistent_script->script.filename), ZSTR_LEN(new_persistent_script->script.filename), 0, new_persistent_script);
if (bucket) {
- zend_accel_error(ACCEL_LOG_INFO, "Cached script '%s'", new_persistent_script->full_path);
+ zend_accel_error(ACCEL_LOG_INFO, "Cached script '%s'", new_persistent_script->script.filename);
if (key &&
/* key may contain non-persistent PHAR aliases (see issues #115 and #149) */
memcmp(key, "phar://", sizeof("phar://") - 1) != 0 &&
- (ZSTR_LEN(new_persistent_script->full_path) != key_length ||
- memcmp(ZSTR_VAL(new_persistent_script->full_path), key, key_length) != 0)) {
+ (ZSTR_LEN(new_persistent_script->script.filename) != key_length ||
+ memcmp(ZSTR_VAL(new_persistent_script->script.filename), key, key_length) != 0)) {
/* link key to the same persistent script in hash table */
if (zend_accel_hash_update(&ZCSG(hash), key, key_length, 1, bucket)) {
zend_accel_error(ACCEL_LOG_INFO, "Added key '%s'", key);
@@ -1497,7 +1500,7 @@ static zend_persistent_script *opcache_compile_file(zend_file_handle *file_handl
/* Override them with ours */
CG(function_table) = &ZCG(function_table);
- EG(class_table) = CG(class_table) = &new_persistent_script->class_table;
+ EG(class_table) = CG(class_table) = &new_persistent_script->script.class_table;
ZVAL_UNDEF(&EG(user_error_handler));
zend_try {
@@ -1534,8 +1537,8 @@ static zend_persistent_script *opcache_compile_file(zend_file_handle *file_handl
Here we aren't sure we would store it, but we will need it
further anyway.
*/
- zend_accel_move_user_functions(&ZCG(function_table), &new_persistent_script->function_table);
- new_persistent_script->main_op_array = *op_array;
+ zend_accel_move_user_functions(&ZCG(function_table), &new_persistent_script->script.function_table);
+ new_persistent_script->script.main_op_array = *op_array;
efree(op_array); /* we have valid persistent_script, so it's safe to free op_array */
@@ -1556,11 +1559,11 @@ static zend_persistent_script *opcache_compile_file(zend_file_handle *file_handl
}
if (file_handle->opened_path) {
- new_persistent_script->full_path = zend_string_copy(file_handle->opened_path);
+ new_persistent_script->script.filename = zend_string_copy(file_handle->opened_path);
} else {
- new_persistent_script->full_path = zend_string_init(file_handle->filename, strlen(file_handle->filename), 0);
+ new_persistent_script->script.filename = zend_string_init(file_handle->filename, strlen(file_handle->filename), 0);
}
- zend_string_hash_val(new_persistent_script->full_path);
+ zend_string_hash_val(new_persistent_script->script.filename);
/* Now persistent_script structure is ready in process memory */
return new_persistent_script;
@@ -1596,21 +1599,21 @@ zend_op_array *file_cache_compile_file(zend_file_handle *file_handle, int type)
SHM_PROTECT();
if (persistent_script) {
/* see bug #15471 (old BTS) */
- if (persistent_script->full_path) {
+ if (persistent_script->script.filename) {
if (!EG(current_execute_data) || !EG(current_execute_data)->opline ||
!EG(current_execute_data)->func ||
!ZEND_USER_CODE(EG(current_execute_data)->func->common.type) ||
EG(current_execute_data)->opline->opcode != ZEND_INCLUDE_OR_EVAL ||
(EG(current_execute_data)->opline->extended_value != ZEND_INCLUDE_ONCE &&
EG(current_execute_data)->opline->extended_value != ZEND_REQUIRE_ONCE)) {
- if (zend_hash_add_empty_element(&EG(included_files), persistent_script->full_path) != NULL) {
+ if (zend_hash_add_empty_element(&EG(included_files), persistent_script->script.filename) != NULL) {
/* ext/phar has to load phar's metadata into memory */
if (persistent_script->is_phar) {
php_stream_statbuf ssb;
- char *fname = emalloc(sizeof("phar://") + ZSTR_LEN(persistent_script->full_path));
+ char *fname = emalloc(sizeof("phar://") + ZSTR_LEN(persistent_script->script.filename));
memcpy(fname, "phar://", sizeof("phar://") - 1);
- memcpy(fname + sizeof("phar://") - 1, ZSTR_VAL(persistent_script->full_path), ZSTR_LEN(persistent_script->full_path) + 1);
+ memcpy(fname + sizeof("phar://") - 1, ZSTR_VAL(persistent_script->script.filename), ZSTR_LEN(persistent_script->script.filename) + 1);
php_stream_stat_path(fname, &ssb);
efree(fname);
}
@@ -1777,7 +1780,7 @@ zend_op_array *persistent_compile_file(zend_file_handle *file_handle, int type)
if (checksum != persistent_script->dynamic_members.checksum ) {
/* The checksum is wrong */
zend_accel_error(ACCEL_LOG_INFO, "Checksum failed for '%s': expected=0x%0.8X, found=0x%0.8X",
- persistent_script->full_path, persistent_script->dynamic_members.checksum, checksum);
+ persistent_script->script.filename, persistent_script->dynamic_members.checksum, checksum);
zend_shared_alloc_lock();
if (!persistent_script->corrupted) {
persistent_script->corrupted = 1;
@@ -1854,21 +1857,21 @@ zend_op_array *persistent_compile_file(zend_file_handle *file_handle, int type)
#endif
/* see bug #15471 (old BTS) */
- if (persistent_script->full_path) {
+ if (persistent_script->script.filename) {
if (!EG(current_execute_data) || !EG(current_execute_data)->opline ||
!EG(current_execute_data)->func ||
!ZEND_USER_CODE(EG(current_execute_data)->func->common.type) ||
EG(current_execute_data)->opline->opcode != ZEND_INCLUDE_OR_EVAL ||
(EG(current_execute_data)->opline->extended_value != ZEND_INCLUDE_ONCE &&
EG(current_execute_data)->opline->extended_value != ZEND_REQUIRE_ONCE)) {
- if (zend_hash_add_empty_element(&EG(included_files), persistent_script->full_path) != NULL) {
+ if (zend_hash_add_empty_element(&EG(included_files), persistent_script->script.filename) != NULL) {
/* ext/phar has to load phar's metadata into memory */
if (persistent_script->is_phar) {
php_stream_statbuf ssb;
- char *fname = emalloc(sizeof("phar://") + ZSTR_LEN(persistent_script->full_path));
+ char *fname = emalloc(sizeof("phar://") + ZSTR_LEN(persistent_script->script.filename));
memcpy(fname, "phar://", sizeof("phar://") - 1);
- memcpy(fname + sizeof("phar://") - 1, ZSTR_VAL(persistent_script->full_path), ZSTR_LEN(persistent_script->full_path) + 1);
+ memcpy(fname + sizeof("phar://") - 1, ZSTR_VAL(persistent_script->script.filename), ZSTR_LEN(persistent_script->script.filename) + 1);
php_stream_stat_path(fname, &ssb);
efree(fname);
}
@@ -1907,7 +1910,7 @@ static int persistent_stream_open_function(const char *filename, zend_file_handl
/* we are in include_once or FastCGI request */
handle->filename = (char*)filename;
handle->free_filename = 0;
- handle->opened_path = zend_string_copy(ZCG(cache_persistent_script)->full_path);
+ handle->opened_path = zend_string_copy(ZCG(cache_persistent_script)->script.filename);
handle->type = ZEND_HANDLE_FILENAME;
return SUCCESS;
}
@@ -1949,7 +1952,7 @@ static zend_string* persistent_zend_resolve_path(const char *filename, int filen
if (!persistent_script->corrupted) {
ZCG(cache_opline) = EG(current_execute_data) ? EG(current_execute_data)->opline : NULL;
ZCG(cache_persistent_script) = persistent_script;
- return zend_string_copy(persistent_script->full_path);
+ return zend_string_copy(persistent_script->script.filename);
}
}
} else {
@@ -2812,6 +2815,8 @@ file_cache_fallback:
zend_accel_blacklist_load(&accel_blacklist, ZCG(accel_directives.user_blacklist_filename));
}
+ zend_optimizer_startup();
+
return SUCCESS;
}
@@ -2829,6 +2834,8 @@ void accel_shutdown(void)
zend_ini_entry *ini_entry;
zend_bool file_cache_only = 0;
+ zend_optimizer_shutdown();
+
zend_accel_blacklist_shutdown(&accel_blacklist);
if (!ZCG(enabled) || !accel_startup_ok) {
diff --git a/ext/opcache/ZendAccelerator.h b/ext/opcache/ZendAccelerator.h
index 4daadc7abe..48e0c1b4cc 100644
--- a/ext/opcache/ZendAccelerator.h
+++ b/ext/opcache/ZendAccelerator.h
@@ -85,14 +85,6 @@
# include <sys/param.h>
#endif
-#define PHP_5_0_X_API_NO 220040412
-#define PHP_5_1_X_API_NO 220051025
-#define PHP_5_2_X_API_NO 220060519
-#define PHP_5_3_X_API_NO 220090626
-#define PHP_5_4_X_API_NO 220100525
-#define PHP_5_5_X_API_NO 220121212
-#define PHP_5_6_X_API_NO 220131226
-
/*** file locking ***/
#ifndef ZEND_WIN32
extern int lock_file;
@@ -125,18 +117,6 @@ extern int lock_file;
# endif
#endif
-#define PZ_REFCOUNT_P(pz) (pz)->refcount__gc
-#define PZ_SET_REFCOUNT_P(pz, v) (pz)->refcount__gc = (v)
-#define PZ_ADDREF_P(pz) ++((pz)->refcount__gc)
-#define PZ_DELREF_P(pz) --((pz)->refcount__gc)
-#define PZ_ISREF_P(pz) (pz)->is_ref__gc
-#define PZ_SET_ISREF_P(pz) Z_SET_ISREF_TO_P(pz, 1)
-#define PZ_UNSET_ISREF_P(pz) Z_SET_ISREF_TO_P(pz, 0)
-#define PZ_SET_ISREF_TO_P(pz, isref) (pz)->is_ref__gc = (isref)
-
-#define DO_ALLOCA(x) do_alloca(x, use_heap)
-#define FREE_ALLOCA(x) free_alloca(x, use_heap)
-
#if defined(HAVE_OPCACHE_FILE_CACHE) && defined(ZEND_WIN32)
# define ENABLE_FILE_CACHE_FALLBACK 1
#endif
@@ -154,11 +134,8 @@ typedef enum _zend_accel_restart_reason {
} zend_accel_restart_reason;
typedef struct _zend_persistent_script {
- zend_string *full_path; /* full real path with resolved symlinks */
- zend_op_array main_op_array;
- HashTable function_table;
- HashTable class_table;
- zend_long compiler_halt_offset; /* position of __HALT_COMPILER or -1 */
+ zend_script script;
+ zend_long compiler_halt_offset; /* position of __HALT_COMPILER or -1 */
int ping_auto_globals_mask; /* which autoglobals are used by the script */
accel_time_t timestamp; /* the script modification time */
zend_bool corrupted;
@@ -212,6 +189,7 @@ typedef struct _zend_accel_directives {
zend_long log_verbosity_level;
zend_long optimization_level;
+ zend_long opt_debug_level;
zend_long max_file_size;
zend_long interned_strings_buffer;
char *restrict_api;
@@ -325,37 +303,15 @@ void zend_accel_schedule_restart_if_necessary(zend_accel_restart_reason reason);
accel_time_t zend_get_file_handle_timestamp(zend_file_handle *file_handle, size_t *size);
int validate_timestamp_and_record(zend_persistent_script *persistent_script, zend_file_handle *file_handle);
int zend_accel_invalidate(const char *filename, int filename_len, zend_bool force);
-int zend_accel_script_optimize(zend_persistent_script *persistent_script);
int accelerator_shm_read_lock(void);
void accelerator_shm_read_unlock(void);
char *accel_make_persistent_key(const char *path, int path_length, int *key_len);
zend_op_array *persistent_compile_file(zend_file_handle *file_handle, int type);
-#if !defined(ZEND_DECLARE_INHERITED_CLASS_DELAYED)
-# define ZEND_DECLARE_INHERITED_CLASS_DELAYED 145
-#endif
-
-#define ZEND_DECLARE_INHERITED_CLASS_DELAYED_FLAG 0x80
-
#define IS_ACCEL_INTERNED(str) \
((char*)(str) >= ZCSG(interned_strings_start) && (char*)(str) < ZCSG(interned_strings_end))
zend_string *accel_new_interned_string(zend_string *str);
-# define ZEND_RESULT_TYPE(opline) (opline)->result_type
-# define ZEND_RESULT(opline) (opline)->result
-# define ZEND_OP1_TYPE(opline) (opline)->op1_type
-# define ZEND_OP1(opline) (opline)->op1
-# define ZEND_OP1_CONST(opline) (*(opline)->op1.zv)
-# define ZEND_OP1_LITERAL(opline) (op_array)->literals[(opline)->op1.constant]
-# define ZEND_OP2_TYPE(opline) (opline)->op2_type
-# define ZEND_OP2(opline) (opline)->op2
-# define ZEND_OP2_CONST(opline) (*(opline)->op2.zv)
-# define ZEND_OP2_LITERAL(opline) (op_array)->literals[(opline)->op2.constant]
-# define ZEND_DONE_PASS_TWO(op_array) (((op_array)->fn_flags & ZEND_ACC_DONE_PASS_TWO) != 0)
-# define ZEND_CE_FILENAME(ce) (ce)->info.user.filename
-# define ZEND_CE_DOC_COMMENT(ce) (ce)->info.user.doc_comment
-# define ZEND_CE_DOC_COMMENT_LEN(ce) (ce)->info.user.doc_comment_len
-
#endif /* ZEND_ACCELERATOR_H */
diff --git a/ext/opcache/config.m4 b/ext/opcache/config.m4
index fbb9b21c94..ded7f3dab2 100644
--- a/ext/opcache/config.m4
+++ b/ext/opcache/config.m4
@@ -402,7 +402,15 @@ fi
Optimizer/block_pass.c \
Optimizer/optimize_temp_vars_5.c \
Optimizer/nop_removal.c \
- Optimizer/compact_literals.c,
+ Optimizer/compact_literals.c \
+ Optimizer/zend_cfg.c \
+ Optimizer/zend_dfg.c \
+ Optimizer/dfa_pass.c \
+ Optimizer/zend_ssa.c \
+ Optimizer/zend_inference.c \
+ Optimizer/zend_func_info.c \
+ Optimizer/zend_call_graph.c \
+ Optimizer/zend_dump.c,
shared,,-DZEND_ENABLE_STATIC_TSRMLS_CACHE=1,,yes)
PHP_ADD_BUILD_DIR([$ext_builddir/Optimizer], 1)
diff --git a/ext/opcache/config.w32 b/ext/opcache/config.w32
index ba6fd621bd..35c4645620 100644
--- a/ext/opcache/config.w32
+++ b/ext/opcache/config.w32
@@ -23,7 +23,7 @@ if (PHP_OPCACHE != "no") {
zend_shared_alloc.c \
shared_alloc_win32.c", true, "/DZEND_ENABLE_STATIC_TSRMLS_CACHE=1");
- ADD_SOURCES(configure_module_dirname + "/Optimizer", "zend_optimizer.c pass1_5.c pass2.c pass3.c optimize_func_calls.c block_pass.c optimize_temp_vars_5.c nop_removal.c compact_literals.c", "opcache", "OptimizerObj");
+ ADD_SOURCES(configure_module_dirname + "/Optimizer", "zend_optimizer.c pass1_5.c pass2.c pass3.c optimize_func_calls.c block_pass.c optimize_temp_vars_5.c nop_removal.c compact_literals.c zend_cfg.c zend_dfg.c dfa_pass.c zend_ssa.c zend_inference.c zend_func_info.c zend_call_graph.c zend_dump.c", "opcache", "OptimizerObj");
ADD_FLAG('CFLAGS_OPCACHE', "/I " + configure_module_dirname);
diff --git a/ext/opcache/tests/bug71127.phpt b/ext/opcache/tests/bug71127.phpt
index 5770aea1fb..0c606097fe 100644
--- a/ext/opcache/tests/bug71127.phpt
+++ b/ext/opcache/tests/bug71127.phpt
@@ -3,7 +3,7 @@ Bug #71127 (Define in auto_prepend_file is overwrite)
--INI--
opcache.enable=1
opcache.enable_cli=1
-opcache.optimization_level=0xFFFFBFFF
+opcache.optimization_level=0x7FFFBFFF
--SKIPIF--
<?php if (!extension_loaded('Zend OPcache')) die("skip"); ?>
--FILE--
diff --git a/ext/opcache/tests/bug71843.phpt b/ext/opcache/tests/bug71843.phpt
index 32af61bf74..73301a6e96 100644
--- a/ext/opcache/tests/bug71843.phpt
+++ b/ext/opcache/tests/bug71843.phpt
@@ -15,7 +15,11 @@ okey
--EXPECTF--
Notice: Use of undefined constant E - assumed 'E' in %sbug71843.php on line %d
+Warning: A non-numeric value encountered in %s on line %d
+
Notice: Use of undefined constant R - assumed 'R' in %sbug71843.php on line %d
+Warning: A non-numeric value encountered in %s on line %d
+
Notice: Use of undefined constant See - assumed 'See' in %sbug71843.php on line %d
okey
diff --git a/ext/opcache/tests/issue0140.phpt b/ext/opcache/tests/issue0140.phpt
index 98e0e45cc2..97fc11b3c7 100644
--- a/ext/opcache/tests/issue0140.phpt
+++ b/ext/opcache/tests/issue0140.phpt
@@ -8,6 +8,7 @@ opcache.file_update_protection=0
--SKIPIF--
<?php require_once('skipif.inc'); ?>
<?php if (php_sapi_name() != "cli") die("skip CLI only"); ?>
+<?php if (getenv("SKIP_SLOW_TESTS")) die("skip slow tests excluded by request") ?>
--FILE--
<?php
define("FILENAME", dirname(__FILE__) . "/issuer0140.inc.php");
diff --git a/ext/opcache/tests/ssa_bug_001.phpt b/ext/opcache/tests/ssa_bug_001.phpt
new file mode 100644
index 0000000000..56757f56a4
--- /dev/null
+++ b/ext/opcache/tests/ssa_bug_001.phpt
@@ -0,0 +1,19 @@
+--TEST--
+SSA constrution for CFG with unreachable basic blocks
+--FILE--
+<?php
+class X {
+ public function __get($n) {
+ if ($n === 'type') {
+ trigger_error('Deprecated type property called; use instanceof', E_USER_NOTICE);
+ switch (get_class($this)) {
+ case 'HTMLPurifier_Token_Start': return 'start';
+ default: return null;
+ }
+ }
+ }
+}
+?>
+OK
+--EXPECT--
+OK
diff --git a/ext/opcache/tests/ssa_bug_002.phpt b/ext/opcache/tests/ssa_bug_002.phpt
new file mode 100644
index 0000000000..9ff6f799f4
--- /dev/null
+++ b/ext/opcache/tests/ssa_bug_002.phpt
@@ -0,0 +1,16 @@
+--TEST--
+Incorrect NOP removal on jump to NOP
+--FILE--
+<?php
+
+function test(int $i) : int {
+ if ($i == 1) {
+ $x = $i + 1;
+ }
+ return $i;
+}
+var_dump(test(42));
+
+?>
+--EXPECT--
+int(42)
diff --git a/ext/opcache/tests/ssa_bug_003.phpt b/ext/opcache/tests/ssa_bug_003.phpt
new file mode 100644
index 0000000000..2895551c08
--- /dev/null
+++ b/ext/opcache/tests/ssa_bug_003.phpt
@@ -0,0 +1,38 @@
+--TEST--
+Incorrect elision of return type checks
+--FILE--
+<?php
+
+function test1($x) : callable {
+ if ($x == 1) {
+ $c = 'foo';
+ } elseif ($x == 2) {
+ $c = new stdClass;
+ } else {
+ $c = [$x => &$x];
+ }
+ return $c;
+}
+
+try {
+ test1(1);
+} catch (Error $e) {
+ echo "Error: {$e->getMessage()}\n";
+}
+
+class Foo {}
+function test2() : Foo {
+ $obj = new stdClass;
+ return $obj;
+}
+
+try {
+ test2();
+} catch (Error $e) {
+ echo "Error: {$e->getMessage()}\n";
+}
+
+?>
+--EXPECT--
+Error: Return value of test1() must be callable, string returned
+Error: Return value of test2() must be an instance of Foo, instance of stdClass returned
diff --git a/ext/opcache/tests/ssa_bug_004.phpt b/ext/opcache/tests/ssa_bug_004.phpt
new file mode 100644
index 0000000000..20e313045a
--- /dev/null
+++ b/ext/opcache/tests/ssa_bug_004.phpt
@@ -0,0 +1,19 @@
+--TEST--
+Assign elision exception safety: ICALL
+--FILE--
+<?php
+
+set_error_handler(function() { throw new Exception; });
+
+function test() {
+ $x = str_replace(['foo'], [[]], ['foo']);
+}
+try {
+ test();
+} catch (Exception $e) {
+ echo "caught\n";
+}
+
+?>
+--EXPECT--
+caught
diff --git a/ext/opcache/tests/ssa_bug_005.phpt b/ext/opcache/tests/ssa_bug_005.phpt
new file mode 100644
index 0000000000..fb373e36b3
--- /dev/null
+++ b/ext/opcache/tests/ssa_bug_005.phpt
@@ -0,0 +1,24 @@
+--TEST--
+Assign elision exception safety: UCALL
+--FILE--
+<?php
+
+function test() {
+ $dtor = new class { function __destruct() { throw new Exception; } };
+ $a = 1;
+ return [0, $a];
+}
+
+function test2() {
+ $x = test();
+}
+
+try {
+ test2();
+} catch (Exception $e) {
+ echo "caught\n";
+}
+
+?>
+--EXPECT--
+caught
diff --git a/ext/opcache/zend_accelerator_module.c b/ext/opcache/zend_accelerator_module.c
index 81dc6d6875..61698b4a9f 100644
--- a/ext/opcache/zend_accelerator_module.c
+++ b/ext/opcache/zend_accelerator_module.c
@@ -294,6 +294,7 @@ ZEND_INI_BEGIN()
STD_PHP_INI_ENTRY("opcache.fast_shutdown" , "0" , PHP_INI_SYSTEM, OnUpdateBool, accel_directives.fast_shutdown, zend_accel_globals, accel_globals)
STD_PHP_INI_ENTRY("opcache.optimization_level" , DEFAULT_OPTIMIZATION_LEVEL , PHP_INI_SYSTEM, OnUpdateLong, accel_directives.optimization_level, zend_accel_globals, accel_globals)
+ STD_PHP_INI_ENTRY("opcache.opt_debug_level" , "0" , PHP_INI_SYSTEM, OnUpdateLong, accel_directives.opt_debug_level, zend_accel_globals, accel_globals)
STD_PHP_INI_BOOLEAN("opcache.enable_file_override" , "0" , PHP_INI_SYSTEM, OnUpdateBool, accel_directives.file_override_enabled, zend_accel_globals, accel_globals)
STD_PHP_INI_BOOLEAN("opcache.enable_cli" , "0" , PHP_INI_SYSTEM, OnUpdateBool, accel_directives.enable_cli, zend_accel_globals, accel_globals)
STD_PHP_INI_ENTRY("opcache.error_log" , "" , PHP_INI_SYSTEM, OnUpdateString, accel_directives.error_log, zend_accel_globals, accel_globals)
@@ -555,7 +556,7 @@ static int accelerator_get_scripts(zval *return_value)
script = (zend_persistent_script *)cache_entry->data;
array_init(&persistent_script_report);
- add_assoc_str(&persistent_script_report, "full_path", zend_string_dup(script->full_path, 0));
+ add_assoc_str(&persistent_script_report, "full_path", zend_string_dup(script->script.filename, 0));
add_assoc_long(&persistent_script_report, "hits", (zend_long)script->dynamic_members.hits);
add_assoc_long(&persistent_script_report, "memory_consumption", script->dynamic_members.memory_consumption);
ta = localtime(&script->dynamic_members.last_used);
diff --git a/ext/opcache/zend_accelerator_util_funcs.c b/ext/opcache/zend_accelerator_util_funcs.c
index 2913e40a67..61c9c15d2b 100644
--- a/ext/opcache/zend_accelerator_util_funcs.c
+++ b/ext/opcache/zend_accelerator_util_funcs.c
@@ -40,8 +40,6 @@
typedef int (*id_function_t)(void *, void *);
typedef void (*unique_copy_ctor_func_t)(void *pElement);
-static zend_ast *zend_ast_clone(zend_ast *ast);
-
static void zend_accel_destroy_zend_function(zval *zv)
{
zend_function *function = Z_PTR_P(zv);
@@ -72,12 +70,12 @@ zend_persistent_script* create_persistent_script(void)
zend_persistent_script *persistent_script = (zend_persistent_script *) emalloc(sizeof(zend_persistent_script));
memset(persistent_script, 0, sizeof(zend_persistent_script));
- zend_hash_init(&persistent_script->function_table, 128, NULL, ZEND_FUNCTION_DTOR, 0);
+ zend_hash_init(&persistent_script->script.function_table, 128, NULL, ZEND_FUNCTION_DTOR, 0);
/* class_table is usually destroyed by free_persistent_script() that
* overrides destructor. ZEND_CLASS_DTOR may be used by standard
* PHP compiler
*/
- zend_hash_init(&persistent_script->class_table, 16, NULL, ZEND_CLASS_DTOR, 0);
+ zend_hash_init(&persistent_script->script.class_table, 16, NULL, ZEND_CLASS_DTOR, 0);
return persistent_script;
}
@@ -85,18 +83,18 @@ zend_persistent_script* create_persistent_script(void)
void free_persistent_script(zend_persistent_script *persistent_script, int destroy_elements)
{
if (destroy_elements) {
- persistent_script->function_table.pDestructor = zend_accel_destroy_zend_function;
- persistent_script->class_table.pDestructor = zend_accel_destroy_zend_class;
+ persistent_script->script.function_table.pDestructor = zend_accel_destroy_zend_function;
+ persistent_script->script.class_table.pDestructor = zend_accel_destroy_zend_class;
} else {
- persistent_script->function_table.pDestructor = NULL;
- persistent_script->class_table.pDestructor = NULL;
+ persistent_script->script.function_table.pDestructor = NULL;
+ persistent_script->script.class_table.pDestructor = NULL;
}
- zend_hash_destroy(&persistent_script->function_table);
- zend_hash_destroy(&persistent_script->class_table);
+ zend_hash_destroy(&persistent_script->script.function_table);
+ zend_hash_destroy(&persistent_script->script.class_table);
- if (persistent_script->full_path) {
- zend_string_release(persistent_script->full_path);
+ if (persistent_script->script.filename) {
+ zend_string_release(persistent_script->script.filename);
}
efree(persistent_script);
@@ -169,67 +167,13 @@ static inline void zend_clone_zval(zval *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);
-
- 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));
- }
- Z_ASTVAL_P(src) = zend_ast_clone(Z_ASTVAL_P(src));
- }
- }
-}
-
-static zend_ast *zend_ast_clone(zend_ast *ast)
-{
- uint32_t i;
-
- if (ast->kind == ZEND_AST_ZVAL) {
- zend_ast_zval *copy = emalloc(sizeof(zend_ast_zval));
- copy->kind = ZEND_AST_ZVAL;
- copy->attr = ast->attr;
- ZVAL_COPY_VALUE(&copy->val, zend_ast_get_zval(ast));
- return (zend_ast *) copy;
- } else if (zend_ast_is_list(ast)) {
- zend_ast_list *list = zend_ast_get_list(ast);
- zend_ast_list *copy = emalloc(
- sizeof(zend_ast_list) - sizeof(zend_ast *) + sizeof(zend_ast *) * list->children);
- copy->kind = list->kind;
- copy->attr = list->attr;
- copy->children = list->children;
- for (i = 0; i < list->children; i++) {
- if (list->child[i]) {
- copy->child[i] = zend_ast_clone(list->child[i]);
- } else {
- copy->child[i] = NULL;
- }
- }
- return (zend_ast *) copy;
- } else {
- uint32_t children = zend_ast_get_num_children(ast);
- zend_ast *copy = emalloc(sizeof(zend_ast) - sizeof(zend_ast *) + sizeof(zend_ast *) * children);
- copy->kind = ast->kind;
- copy->attr = ast->attr;
- for (i = 0; i < children; i++) {
- if (ast->child[i]) {
- copy->child[i] = zend_ast_clone(ast->child[i]);
- } else {
- copy->child[i] = NULL;
- }
- }
- return copy;
- }
}
static void zend_hash_clone_constants(HashTable *ht, HashTable *source)
{
Bucket *p, *q, *end;
zend_ulong nIndex;
+ zend_class_constant *c;
ht->nTableSize = source->nTableSize;
ht->nTableMask = source->nTableMask;
@@ -265,8 +209,14 @@ static void zend_hash_clone_constants(HashTable *ht, HashTable *source)
q->key = p->key;
/* Copy data */
- ZVAL_COPY_VALUE(&q->val, &p->val);
- zend_clone_zval(&q->val);
+ c = ARENA_REALLOC(Z_PTR(p->val));
+ ZVAL_PTR(&q->val, c);
+
+ zend_clone_zval(&c->value);
+ if ((void*)c->ce >= ZCG(current_persistent_script)->arena_mem &&
+ (void*)c->ce < (void*)((char*)ZCG(current_persistent_script)->arena_mem + ZCG(current_persistent_script)->arena_size)) {
+ c->ce = ARENA_REALLOC(c->ce);
+ }
}
}
@@ -681,7 +631,7 @@ zend_op_array* zend_accel_load_script(zend_persistent_script *persistent_script,
zend_op_array *op_array;
op_array = (zend_op_array *) emalloc(sizeof(zend_op_array));
- *op_array = persistent_script->main_op_array;
+ *op_array = persistent_script->script.main_op_array;
if (EXPECTED(from_shared_memory)) {
zend_hash_init(&ZCG(bind_hash), 10, NULL, NULL, 0);
@@ -701,22 +651,22 @@ zend_op_array* zend_accel_load_script(zend_persistent_script *persistent_script,
}
/* Copy all the necessary stuff from shared memory to regular memory, and protect the shared script */
- if (zend_hash_num_elements(&persistent_script->class_table) > 0) {
- zend_accel_class_hash_copy(CG(class_table), &persistent_script->class_table, (unique_copy_ctor_func_t) zend_class_copy_ctor);
+ if (zend_hash_num_elements(&persistent_script->script.class_table) > 0) {
+ zend_accel_class_hash_copy(CG(class_table), &persistent_script->script.class_table, (unique_copy_ctor_func_t) zend_class_copy_ctor);
}
/* we must first to copy all classes and then prepare functions, since functions may try to bind
classes - which depend on pre-bind class entries existent in the class table */
- if (zend_hash_num_elements(&persistent_script->function_table) > 0) {
- zend_accel_function_hash_copy_from_shm(CG(function_table), &persistent_script->function_table);
+ if (zend_hash_num_elements(&persistent_script->script.function_table) > 0) {
+ zend_accel_function_hash_copy_from_shm(CG(function_table), &persistent_script->script.function_table);
}
/* Register __COMPILER_HALT_OFFSET__ constant */
if (persistent_script->compiler_halt_offset != 0 &&
- persistent_script->full_path) {
+ persistent_script->script.filename) {
zend_string *name;
char haltoff[] = "__COMPILER_HALT_OFFSET__";
- name = zend_mangle_property_name(haltoff, sizeof(haltoff) - 1, ZSTR_VAL(persistent_script->full_path), ZSTR_LEN(persistent_script->full_path), 0);
+ name = zend_mangle_property_name(haltoff, sizeof(haltoff) - 1, ZSTR_VAL(persistent_script->script.filename), ZSTR_LEN(persistent_script->script.filename), 0);
if (!zend_hash_exists(EG(zend_constants), name)) {
zend_register_long_constant(ZSTR_VAL(name), ZSTR_LEN(name), persistent_script->compiler_halt_offset, CONST_CS, 0);
}
@@ -726,17 +676,17 @@ zend_op_array* zend_accel_load_script(zend_persistent_script *persistent_script,
zend_hash_destroy(&ZCG(bind_hash));
ZCG(current_persistent_script) = NULL;
} else /* if (!from_shared_memory) */ {
- if (zend_hash_num_elements(&persistent_script->function_table) > 0) {
- zend_accel_function_hash_copy(CG(function_table), &persistent_script->function_table);
+ if (zend_hash_num_elements(&persistent_script->script.function_table) > 0) {
+ zend_accel_function_hash_copy(CG(function_table), &persistent_script->script.function_table);
}
- if (zend_hash_num_elements(&persistent_script->class_table) > 0) {
- zend_accel_class_hash_copy(CG(class_table), &persistent_script->class_table, NULL);
+ if (zend_hash_num_elements(&persistent_script->script.class_table) > 0) {
+ zend_accel_class_hash_copy(CG(class_table), &persistent_script->script.class_table, NULL);
}
}
if (op_array->early_binding != (uint32_t)-1) {
zend_string *orig_compiled_filename = CG(compiled_filename);
- CG(compiled_filename) = persistent_script->full_path;
+ CG(compiled_filename) = persistent_script->script.filename;
zend_do_delayed_early_binding(op_array);
CG(compiled_filename) = orig_compiled_filename;
}
diff --git a/ext/opcache/zend_file_cache.c b/ext/opcache/zend_file_cache.c
index 3187412423..6ac4f0e35f 100644
--- a/ext/opcache/zend_file_cache.c
+++ b/ext/opcache/zend_file_cache.c
@@ -373,7 +373,6 @@ static void zend_file_cache_serialize_op_array(zend_op_array *op_arra
}
if (!IS_SERIALIZED(op_array->opcodes)) {
-#if ZEND_USE_ABS_CONST_ADDR || ZEND_USE_ABS_JMP_ADDR
zend_op *opline, *end;
SERIALIZE_PTR(op_array->opcodes);
@@ -381,20 +380,18 @@ static void zend_file_cache_serialize_op_array(zend_op_array *op_arra
UNSERIALIZE_PTR(opline);
end = opline + op_array->last;
while (opline < end) {
-# if ZEND_USE_ABS_CONST_ADDR
- if (ZEND_OP1_TYPE(opline) == IS_CONST) {
+#if ZEND_USE_ABS_CONST_ADDR
+ if (opline->op1_type == IS_CONST) {
SERIALIZE_PTR(opline->op1.zv);
}
- if (ZEND_OP2_TYPE(opline) == IS_CONST) {
+ if (opline->op2_type == IS_CONST) {
SERIALIZE_PTR(opline->op2.zv);
}
-# endif
-# if ZEND_USE_ABS_JMP_ADDR
+#endif
+#if ZEND_USE_ABS_JMP_ADDR
switch (opline->opcode) {
case ZEND_JMP:
case ZEND_FAST_CALL:
- case ZEND_DECLARE_ANON_CLASS:
- case ZEND_DECLARE_ANON_INHERITED_CLASS:
SERIALIZE_PTR(opline->op1.jmp_addr);
break;
case ZEND_JMPZNZ:
@@ -406,23 +403,22 @@ static void zend_file_cache_serialize_op_array(zend_op_array *op_arra
case ZEND_JMPNZ_EX:
case ZEND_JMP_SET:
case ZEND_COALESCE:
- case ZEND_NEW:
case ZEND_FE_RESET_R:
case ZEND_FE_RESET_RW:
case ZEND_ASSERT_CHECK:
SERIALIZE_PTR(opline->op2.jmp_addr);
break;
+ case ZEND_DECLARE_ANON_CLASS:
+ case ZEND_DECLARE_ANON_INHERITED_CLASS:
case ZEND_FE_FETCH_R:
case ZEND_FE_FETCH_RW:
/* relative extended_value don't have to be changed */
break;
}
-# endif
+#endif
+ zend_serialize_opcode_handler(opline);
opline++;
}
-#else
- SERIALIZE_PTR(op_array->opcodes);
-#endif
if (op_array->arg_info) {
zend_arg_info *p, *end;
@@ -464,7 +460,7 @@ static void zend_file_cache_serialize_op_array(zend_op_array *op_arra
SERIALIZE_STR(op_array->function_name);
SERIALIZE_STR(op_array->filename);
- SERIALIZE_PTR(op_array->brk_cont_array);
+ SERIALIZE_PTR(op_array->live_range);
SERIALIZE_PTR(op_array->scope);
SERIALIZE_STR(op_array->doc_comment);
SERIALIZE_PTR(op_array->try_catch_array);
@@ -509,6 +505,28 @@ static void zend_file_cache_serialize_prop_info(zval *zv,
}
}
+static void zend_file_cache_serialize_class_constant(zval *zv,
+ zend_persistent_script *script,
+ zend_file_cache_metainfo *info,
+ void *buf)
+{
+ if (!IS_SERIALIZED(Z_PTR_P(zv))) {
+ zend_class_constant *c;
+
+ SERIALIZE_PTR(Z_PTR_P(zv));
+ c = Z_PTR_P(zv);
+ UNSERIALIZE_PTR(c);
+
+ zend_file_cache_serialize_zval(&c->value, script, info, buf);
+ if (c->ce && !IS_SERIALIZED(c->ce)) {
+ SERIALIZE_PTR(c->ce);
+ }
+ if (c->doc_comment && !IS_SERIALIZED(c->doc_comment)) {
+ SERIALIZE_STR(c->doc_comment);
+ }
+ }
+}
+
static void zend_file_cache_serialize_class(zval *zv,
zend_persistent_script *script,
zend_file_cache_metainfo *info,
@@ -546,9 +564,9 @@ static void zend_file_cache_serialize_class(zval *zv,
p++;
}
}
- zend_file_cache_serialize_hash(&ce->constants_table, script, info, buf, zend_file_cache_serialize_zval);
- SERIALIZE_STR(ZEND_CE_FILENAME(ce));
- SERIALIZE_STR(ZEND_CE_DOC_COMMENT(ce));
+ zend_file_cache_serialize_hash(&ce->constants_table, script, info, buf, zend_file_cache_serialize_class_constant);
+ SERIALIZE_STR(ce->info.user.filename);
+ SERIALIZE_STR(ce->info.user.doc_comment);
zend_file_cache_serialize_hash(&ce->properties_info, script, info, buf, zend_file_cache_serialize_prop_info);
if (ce->trait_aliases) {
@@ -660,11 +678,11 @@ static void zend_file_cache_serialize(zend_persistent_script *script,
memcpy(buf, script->mem, script->size);
new_script = (zend_persistent_script*)((char*)buf + info->script_offset);
- SERIALIZE_STR(new_script->full_path);
+ SERIALIZE_STR(new_script->script.filename);
- zend_file_cache_serialize_hash(&new_script->class_table, script, info, buf, zend_file_cache_serialize_class);
- zend_file_cache_serialize_hash(&new_script->function_table, script, info, buf, zend_file_cache_serialize_func);
- zend_file_cache_serialize_op_array(&new_script->main_op_array, script, info, buf);
+ zend_file_cache_serialize_hash(&new_script->script.class_table, script, info, buf, zend_file_cache_serialize_class);
+ zend_file_cache_serialize_hash(&new_script->script.function_table, script, info, buf, zend_file_cache_serialize_func);
+ zend_file_cache_serialize_op_array(&new_script->script.main_op_array, script, info, buf);
SERIALIZE_PTR(new_script->arena_mem);
new_script->mem = NULL;
@@ -712,7 +730,7 @@ int zend_file_cache_script_store(zend_persistent_script *script, int in_shm)
#endif
void *mem, *buf;
- filename = zend_file_cache_get_bin_file_path(script->full_path);
+ filename = zend_file_cache_get_bin_file_path(script->script.filename);
if (zend_file_cache_mkdir(filename, strlen(ZCG(accel_directives).file_cache)) != SUCCESS) {
zend_accel_error(ACCEL_LOG_WARNING, "opcache cannot create directory for file '%s'\n", filename);
@@ -937,10 +955,10 @@ static void zend_file_cache_unserialize_op_array(zend_op_array *op_arr
end = opline + op_array->last;
while (opline < end) {
# if ZEND_USE_ABS_CONST_ADDR
- if (ZEND_OP1_TYPE(opline) == IS_CONST) {
+ if (opline->op1_type == IS_CONST) {
UNSERIALIZE_PTR(opline->op1.zv);
}
- if (ZEND_OP2_TYPE(opline) == IS_CONST) {
+ if (opline->op2_type == IS_CONST) {
UNSERIALIZE_PTR(opline->op2.zv);
}
# endif
@@ -948,8 +966,6 @@ static void zend_file_cache_unserialize_op_array(zend_op_array *op_arr
switch (opline->opcode) {
case ZEND_JMP:
case ZEND_FAST_CALL:
- case ZEND_DECLARE_ANON_CLASS:
- case ZEND_DECLARE_ANON_INHERITED_CLASS:
UNSERIALIZE_PTR(opline->op1.jmp_addr);
break;
case ZEND_JMPZNZ:
@@ -961,19 +977,20 @@ static void zend_file_cache_unserialize_op_array(zend_op_array *op_arr
case ZEND_JMPNZ_EX:
case ZEND_JMP_SET:
case ZEND_COALESCE:
- case ZEND_NEW:
case ZEND_FE_RESET_R:
case ZEND_FE_RESET_RW:
case ZEND_ASSERT_CHECK:
UNSERIALIZE_PTR(opline->op2.jmp_addr);
break;
+ case ZEND_DECLARE_ANON_CLASS:
+ case ZEND_DECLARE_ANON_INHERITED_CLASS:
case ZEND_FE_FETCH_R:
case ZEND_FE_FETCH_RW:
/* relative extended_value don't have to be changed */
break;
}
# endif
- ZEND_VM_SET_OPCODE_HANDLER(opline);
+ zend_deserialize_opcode_handler(opline);
opline++;
}
@@ -1015,7 +1032,7 @@ static void zend_file_cache_unserialize_op_array(zend_op_array *op_arr
UNSERIALIZE_STR(op_array->function_name);
UNSERIALIZE_STR(op_array->filename);
- UNSERIALIZE_PTR(op_array->brk_cont_array);
+ UNSERIALIZE_PTR(op_array->live_range);
UNSERIALIZE_PTR(op_array->scope);
UNSERIALIZE_STR(op_array->doc_comment);
UNSERIALIZE_PTR(op_array->try_catch_array);
@@ -1056,6 +1073,26 @@ static void zend_file_cache_unserialize_prop_info(zval *zv,
}
}
+static void zend_file_cache_unserialize_class_constant(zval *zv,
+ zend_persistent_script *script,
+ void *buf)
+{
+ if (!IS_UNSERIALIZED(Z_PTR_P(zv))) {
+ zend_class_constant *c;
+
+ UNSERIALIZE_PTR(Z_PTR_P(zv));
+ c = Z_PTR_P(zv);
+
+ zend_file_cache_unserialize_zval(&c->value, script, buf);
+ if (c->ce && !IS_UNSERIALIZED(c->ce)) {
+ UNSERIALIZE_PTR(c->ce);
+ }
+ if (c->doc_comment && !IS_UNSERIALIZED(c->doc_comment)) {
+ UNSERIALIZE_STR(c->doc_comment);
+ }
+ }
+}
+
static void zend_file_cache_unserialize_class(zval *zv,
zend_persistent_script *script,
void *buf)
@@ -1091,9 +1128,9 @@ static void zend_file_cache_unserialize_class(zval *zv,
}
}
zend_file_cache_unserialize_hash(&ce->constants_table,
- script, buf, zend_file_cache_unserialize_zval, NULL);
- UNSERIALIZE_STR(ZEND_CE_FILENAME(ce));
- UNSERIALIZE_STR(ZEND_CE_DOC_COMMENT(ce));
+ script, buf, zend_file_cache_unserialize_class_constant, NULL);
+ UNSERIALIZE_STR(ce->info.user.filename);
+ UNSERIALIZE_STR(ce->info.user.doc_comment);
zend_file_cache_unserialize_hash(&ce->properties_info,
script, buf, zend_file_cache_unserialize_prop_info, ZVAL_PTR_DTOR);
@@ -1193,13 +1230,13 @@ static void zend_file_cache_unserialize(zend_persistent_script *script,
{
script->mem = buf;
- UNSERIALIZE_STR(script->full_path);
+ UNSERIALIZE_STR(script->script.filename);
- zend_file_cache_unserialize_hash(&script->class_table,
+ zend_file_cache_unserialize_hash(&script->script.class_table,
script, buf, zend_file_cache_unserialize_class, ZEND_CLASS_DTOR);
- zend_file_cache_unserialize_hash(&script->function_table,
+ zend_file_cache_unserialize_hash(&script->script.function_table,
script, buf, zend_file_cache_unserialize_func, ZEND_FUNCTION_DTOR);
- zend_file_cache_unserialize_op_array(&script->main_op_array, script, buf);
+ zend_file_cache_unserialize_op_array(&script->script.main_op_array, script, buf);
UNSERIALIZE_PTR(script->arena_mem);
}
@@ -1362,7 +1399,7 @@ use_process_mem:
script->dynamic_members.checksum = zend_accel_script_checksum(script);
script->dynamic_members.last_used = ZCG(request_time);
- zend_accel_hash_update(&ZCSG(hash), ZSTR_VAL(script->full_path), ZSTR_LEN(script->full_path), 0, script);
+ zend_accel_hash_update(&ZCSG(hash), ZSTR_VAL(script->script.filename), ZSTR_LEN(script->script.filename), 0, script);
zend_shared_alloc_unlock();
zend_arena_release(&CG(arena), checkpoint);
diff --git a/ext/opcache/zend_persist.c b/ext/opcache/zend_persist.c
index 2a34300f77..25a504575d 100644
--- a/ext/opcache/zend_persist.c
+++ b/ext/opcache/zend_persist.c
@@ -65,7 +65,6 @@
typedef void (*zend_persist_func_t)(zval*);
static void zend_persist_zval(zval *z);
-static void zend_persist_zval_const(zval *z);
static const uint32_t uninitialized_bucket[-HT_MIN_MASK] =
{HT_INVALID_IDX, HT_INVALID_IDX};
@@ -204,7 +203,7 @@ static void zend_hash_persist_immutable(HashTable *ht)
}
/* persist the data itself */
- zend_persist_zval_const(&p->val);
+ zend_persist_zval(&p->val);
nIndex = p->h | ht->nTableMask;
Z_NEXT(p->val) = HT_HASH(ht, nIndex);
@@ -229,7 +228,7 @@ static void zend_hash_persist_immutable(HashTable *ht)
}
/* persist the data itself */
- zend_persist_zval_const(&p->val);
+ zend_persist_zval(&p->val);
}
}
@@ -278,63 +277,10 @@ static void zend_persist_zval(zval *z)
zend_accel_store_interned_string(Z_STR_P(z));
Z_GC_FLAGS_P(z) |= flags;
Z_TYPE_FLAGS_P(z) &= ~(IS_TYPE_REFCOUNTED | IS_TYPE_COPYABLE);
- break;
- case IS_ARRAY:
- new_ptr = zend_shared_alloc_get_xlat_entry(Z_ARR_P(z));
- if (new_ptr) {
- Z_ARR_P(z) = new_ptr;
- Z_TYPE_FLAGS_P(z) = IS_TYPE_IMMUTABLE;
- } else {
- if (Z_IMMUTABLE_P(z)) {
- Z_ARR_P(z) = zend_accel_memdup(Z_ARR_P(z), sizeof(zend_array));
- zend_hash_persist_immutable(Z_ARRVAL_P(z));
- } else {
- GC_REMOVE_FROM_BUFFER(Z_ARR_P(z));
- zend_accel_store(Z_ARR_P(z), sizeof(zend_array));
- zend_hash_persist(Z_ARRVAL_P(z), zend_persist_zval);
- /* make immutable array */
- Z_TYPE_FLAGS_P(z) = IS_TYPE_IMMUTABLE;
- GC_REFCOUNT(Z_COUNTED_P(z)) = 2;
- GC_FLAGS(Z_COUNTED_P(z)) |= IS_ARRAY_IMMUTABLE;
- Z_ARRVAL_P(z)->u.flags |= HASH_FLAG_STATIC_KEYS;
- Z_ARRVAL_P(z)->u.flags &= ~HASH_FLAG_APPLY_PROTECTION;
- }
- }
- break;
- case IS_REFERENCE:
- new_ptr = zend_shared_alloc_get_xlat_entry(Z_REF_P(z));
- if (new_ptr) {
- Z_REF_P(z) = new_ptr;
- } else {
- zend_accel_store(Z_REF_P(z), sizeof(zend_reference));
- zend_persist_zval(Z_REFVAL_P(z));
+ if (Z_TYPE_P(z) == IS_CONSTANT) {
+ Z_TYPE_FLAGS_P(z) |= IS_TYPE_IMMUTABLE;
}
break;
- case IS_CONSTANT_AST:
- new_ptr = zend_shared_alloc_get_xlat_entry(Z_AST_P(z));
- if (new_ptr) {
- Z_AST_P(z) = new_ptr;
- } else {
- zend_accel_store(Z_AST_P(z), sizeof(zend_ast_ref));
- Z_ASTVAL_P(z) = zend_persist_ast(Z_ASTVAL_P(z));
- }
- break;
- }
-}
-
-static void zend_persist_zval_static(zval *z)
-{
- zend_uchar flags;
- void *new_ptr;
-
- switch (Z_TYPE_P(z)) {
- case IS_STRING:
- case IS_CONSTANT:
- flags = Z_GC_FLAGS_P(z) & ~ (IS_STR_PERSISTENT | IS_STR_INTERNED | IS_STR_PERMANENT);
- zend_accel_store_interned_string(Z_STR_P(z));
- Z_GC_FLAGS_P(z) |= flags;
- Z_TYPE_FLAGS_P(z) &= ~(IS_TYPE_REFCOUNTED | IS_TYPE_COPYABLE);
- break;
case IS_ARRAY:
new_ptr = zend_shared_alloc_get_xlat_entry(Z_ARR_P(z));
if (new_ptr) {
@@ -381,62 +327,6 @@ static void zend_persist_zval_static(zval *z)
}
}
-static void zend_persist_zval_const(zval *z)
-{
- zend_uchar flags;
- void *new_ptr;
-
- switch (Z_TYPE_P(z)) {
- case IS_STRING:
- case IS_CONSTANT:
- flags = Z_GC_FLAGS_P(z) & ~ (IS_STR_PERSISTENT | IS_STR_INTERNED | IS_STR_PERMANENT);
- zend_accel_memdup_interned_string(Z_STR_P(z));
- Z_GC_FLAGS_P(z) |= flags;
- Z_TYPE_FLAGS_P(z) &= ~(IS_TYPE_REFCOUNTED | IS_TYPE_COPYABLE);
- break;
- case IS_ARRAY:
- new_ptr = zend_shared_alloc_get_xlat_entry(Z_ARR_P(z));
- if (new_ptr) {
- Z_ARR_P(z) = new_ptr;
- Z_TYPE_FLAGS_P(z) = IS_TYPE_IMMUTABLE;
- } else {
- if (Z_IMMUTABLE_P(z)) {
- Z_ARR_P(z) = zend_accel_memdup(Z_ARR_P(z), sizeof(zend_array));
- zend_hash_persist_immutable(Z_ARRVAL_P(z));
- } else {
- GC_REMOVE_FROM_BUFFER(Z_ARR_P(z));
- zend_accel_store(Z_ARR_P(z), sizeof(zend_array));
- zend_hash_persist(Z_ARRVAL_P(z), zend_persist_zval);
- /* make immutable array */
- Z_TYPE_FLAGS_P(z) = IS_TYPE_IMMUTABLE;
- GC_REFCOUNT(Z_COUNTED_P(z)) = 2;
- GC_FLAGS(Z_COUNTED_P(z)) |= IS_ARRAY_IMMUTABLE;
- Z_ARRVAL_P(z)->u.flags |= HASH_FLAG_STATIC_KEYS;
- Z_ARRVAL_P(z)->u.flags &= ~HASH_FLAG_APPLY_PROTECTION;
- }
- }
- break;
- case IS_REFERENCE:
- new_ptr = zend_shared_alloc_get_xlat_entry(Z_REF_P(z));
- if (new_ptr) {
- Z_REF_P(z) = new_ptr;
- } else {
- zend_accel_store(Z_REF_P(z), sizeof(zend_reference));
- zend_persist_zval(Z_REFVAL_P(z));
- }
- break;
- case IS_CONSTANT_AST:
- new_ptr = zend_shared_alloc_get_xlat_entry(Z_AST_P(z));
- if (new_ptr) {
- Z_AST_P(z) = new_ptr;
- } else {
- zend_accel_store(Z_AST_P(z), sizeof(zend_ast_ref));
- Z_ASTVAL_P(z) = zend_persist_ast(Z_ASTVAL_P(z));
- }
- break;
- }
-}
-
static void zend_persist_op_array_ex(zend_op_array *op_array, zend_persistent_script* main_persistent_script)
{
int already_stored = 0;
@@ -472,7 +362,7 @@ static void zend_persist_op_array_ex(zend_op_array *op_array, zend_persistent_sc
if (stored) {
op_array->static_variables = stored;
} else {
- zend_hash_persist(op_array->static_variables, zend_persist_zval_static);
+ zend_hash_persist(op_array->static_variables, zend_persist_zval);
zend_accel_store(op_array->static_variables, sizeof(HashTable));
/* make immutable array */
GC_REFCOUNT(op_array->static_variables) = 2;
@@ -517,22 +407,20 @@ static void zend_persist_op_array_ex(zend_op_array *op_array, zend_persistent_sc
for (; opline < end ; opline++, offset++) {
# if ZEND_USE_ABS_CONST_ADDR
- if (ZEND_OP1_TYPE(opline) == IS_CONST) {
+ if (opline->op1_type == IS_CONST) {
opline->op1.zv = (zval*)((char*)opline->op1.zv + ((char*)op_array->literals - (char*)orig_literals));
}
- if (ZEND_OP2_TYPE(opline) == IS_CONST) {
+ if (opline->op2_type == IS_CONST) {
opline->op2.zv = (zval*)((char*)opline->op2.zv + ((char*)op_array->literals - (char*)orig_literals));
}
# endif
# if ZEND_USE_ABS_JMP_ADDR
- if (ZEND_DONE_PASS_TWO(op_array)) {
+ if (op_array->fn_flags & ZEND_ACC_DONE_PASS_TWO) {
/* fix jumps to point to new array */
switch (opline->opcode) {
case ZEND_JMP:
case ZEND_FAST_CALL:
- case ZEND_DECLARE_ANON_CLASS:
- case ZEND_DECLARE_ANON_INHERITED_CLASS:
- ZEND_OP1(opline).jmp_addr = &new_opcodes[ZEND_OP1(opline).jmp_addr - op_array->opcodes];
+ opline->op1.jmp_addr = &new_opcodes[opline->op1.jmp_addr - op_array->opcodes];
break;
case ZEND_JMPZNZ:
/* relative extended_value don't have to be changed */
@@ -543,12 +431,13 @@ static void zend_persist_op_array_ex(zend_op_array *op_array, zend_persistent_sc
case ZEND_JMPNZ_EX:
case ZEND_JMP_SET:
case ZEND_COALESCE:
- case ZEND_NEW:
case ZEND_FE_RESET_R:
case ZEND_FE_RESET_RW:
case ZEND_ASSERT_CHECK:
- ZEND_OP2(opline).jmp_addr = &new_opcodes[ZEND_OP2(opline).jmp_addr - op_array->opcodes];
+ opline->op2.jmp_addr = &new_opcodes[opline->op2.jmp_addr - op_array->opcodes];
break;
+ case ZEND_DECLARE_ANON_CLASS:
+ case ZEND_DECLARE_ANON_INHERITED_CLASS:
case ZEND_FE_FETCH_R:
case ZEND_FE_FETCH_RW:
/* relative extended_value don't have to be changed */
@@ -617,8 +506,8 @@ static void zend_persist_op_array_ex(zend_op_array *op_array, zend_persistent_sc
op_array->arg_info = arg_info;
}
- if (op_array->brk_cont_array) {
- zend_accel_store(op_array->brk_cont_array, sizeof(zend_brk_cont_element) * op_array->last_brk_cont);
+ if (op_array->live_range) {
+ zend_accel_store(op_array->live_range, sizeof(zend_live_range) * op_array->last_live_range);
}
if (op_array->scope) {
@@ -716,6 +605,39 @@ static void zend_persist_property_info(zval *zv)
}
}
+static void zend_persist_class_constant(zval *zv)
+{
+ zend_class_constant *c = zend_shared_alloc_get_xlat_entry(Z_PTR_P(zv));
+
+ if (c) {
+ Z_PTR_P(zv) = c;
+ return;
+ }
+ memcpy(ZCG(arena_mem), Z_PTR_P(zv), sizeof(zend_class_constant));
+ zend_shared_alloc_register_xlat_entry(Z_PTR_P(zv), ZCG(arena_mem));
+ c = Z_PTR_P(zv) = ZCG(arena_mem);
+ ZCG(arena_mem) = (void*)((char*)ZCG(arena_mem) + ZEND_ALIGNED_SIZE(sizeof(zend_class_constant)));
+ zend_persist_zval(&c->value);
+ c->ce = zend_shared_alloc_get_xlat_entry(c->ce);
+ if (c->doc_comment) {
+ if (ZCG(accel_directives).save_comments) {
+ zend_string *doc_comment = zend_shared_alloc_get_xlat_entry(c->doc_comment);
+ if (doc_comment) {
+ c->doc_comment = doc_comment;
+ } else {
+ zend_accel_store_string(c->doc_comment);
+ }
+ } else {
+ zend_string *doc_comment = zend_shared_alloc_get_xlat_entry(c->doc_comment);
+ if (!doc_comment) {
+ zend_shared_alloc_register_xlat_entry(c->doc_comment, c->doc_comment);
+ zend_string_release(c->doc_comment);
+ }
+ c->doc_comment = NULL;
+ }
+ }
+}
+
static void zend_persist_class_entry(zval *zv)
{
zend_class_entry *ce = Z_PTR_P(zv);
@@ -745,21 +667,21 @@ static void zend_persist_class_entry(zval *zv)
}
ce->static_members_table = NULL;
- zend_hash_persist(&ce->constants_table, zend_persist_zval);
+ zend_hash_persist(&ce->constants_table, zend_persist_class_constant);
- if (ZEND_CE_FILENAME(ce)) {
+ if (ce->info.user.filename) {
/* do not free! PHP has centralized filename storage, compiler will free it */
- zend_accel_memdup_string(ZEND_CE_FILENAME(ce));
+ zend_accel_memdup_string(ce->info.user.filename);
}
- if (ZEND_CE_DOC_COMMENT(ce)) {
+ if (ce->info.user.doc_comment) {
if (ZCG(accel_directives).save_comments) {
- zend_accel_store_string(ZEND_CE_DOC_COMMENT(ce));
+ zend_accel_store_string(ce->info.user.doc_comment);
} else {
- if (!zend_shared_alloc_get_xlat_entry(ZEND_CE_DOC_COMMENT(ce))) {
- zend_shared_alloc_register_xlat_entry(ZEND_CE_DOC_COMMENT(ce), ZEND_CE_DOC_COMMENT(ce));
- zend_string_release(ZEND_CE_DOC_COMMENT(ce));
+ if (!zend_shared_alloc_get_xlat_entry(ce->info.user.doc_comment)) {
+ zend_shared_alloc_register_xlat_entry(ce->info.user.doc_comment, ce->info.user.doc_comment);
+ zend_string_release(ce->info.user.doc_comment);
}
- ZEND_CE_DOC_COMMENT(ce) = NULL;
+ ce->info.user.doc_comment = NULL;
}
}
zend_hash_persist(&ce->properties_info, zend_persist_property_info);
@@ -906,7 +828,7 @@ zend_persistent_script *zend_accel_script_persist(zend_persistent_script *script
if (key && *key) {
*key = zend_accel_memdup(*key, key_length + 1);
}
- zend_accel_store_string(script->full_path);
+ zend_accel_store_string(script->script.filename);
#ifdef __SSE2__
/* Align to 64-byte boundary */
@@ -918,9 +840,9 @@ zend_persistent_script *zend_accel_script_persist(zend_persistent_script *script
script->arena_mem = ZCG(arena_mem) = ZCG(mem);
ZCG(mem) = (void*)((char*)ZCG(mem) + script->arena_size);
- zend_accel_persist_class_table(&script->class_table);
- zend_hash_persist(&script->function_table, zend_persist_op_array);
- zend_persist_op_array_ex(&script->main_op_array, script);
+ zend_accel_persist_class_table(&script->script.class_table);
+ zend_hash_persist(&script->script.function_table, zend_persist_op_array);
+ zend_persist_op_array_ex(&script->script.main_op_array, script);
return script;
}
diff --git a/ext/opcache/zend_persist_calc.c b/ext/opcache/zend_persist_calc.c
index 25ba69bee8..22e74d6b36 100644
--- a/ext/opcache/zend_persist_calc.c
+++ b/ext/opcache/zend_persist_calc.c
@@ -236,8 +236,8 @@ static void zend_persist_op_array_calc_ex(zend_op_array *op_array)
}
}
- if (op_array->brk_cont_array) {
- ADD_DUP_SIZE(op_array->brk_cont_array, sizeof(zend_brk_cont_element) * op_array->last_brk_cont);
+ if (op_array->live_range) {
+ ADD_DUP_SIZE(op_array->live_range, sizeof(zend_live_range) * op_array->last_live_range);
}
if (ZCG(accel_directives).save_comments && op_array->doc_comment) {
@@ -294,6 +294,21 @@ static void zend_persist_property_info_calc(zval *zv)
}
}
+static void zend_persist_class_constant_calc(zval *zv)
+{
+ zend_class_constant *c = Z_PTR_P(zv);
+
+ if (!zend_shared_alloc_get_xlat_entry(c)) {
+ zend_shared_alloc_register_xlat_entry(c, c);
+ ADD_ARENA_SIZE(sizeof(zend_class_constant));
+ zend_persist_zval_calc(&c->value);
+ if (ZCG(accel_directives).save_comments && c->doc_comment) {
+ ADD_STRING(c->doc_comment);
+ }
+ }
+}
+
+
static void zend_persist_class_entry_calc(zval *zv)
{
zend_class_entry *ce = Z_PTR_P(zv);
@@ -318,13 +333,13 @@ static void zend_persist_class_entry_calc(zval *zv)
zend_persist_zval_calc(&ce->default_static_members_table[i]);
}
}
- zend_hash_persist_calc(&ce->constants_table, zend_persist_zval_calc);
+ zend_hash_persist_calc(&ce->constants_table, zend_persist_class_constant_calc);
- if (ZEND_CE_FILENAME(ce)) {
- ADD_STRING(ZEND_CE_FILENAME(ce));
+ if (ce->info.user.filename) {
+ ADD_STRING(ce->info.user.filename);
}
- if (ZCG(accel_directives).save_comments && ZEND_CE_DOC_COMMENT(ce)) {
- ADD_STRING(ZEND_CE_DOC_COMMENT(ce));
+ if (ZCG(accel_directives).save_comments && ce->info.user.doc_comment) {
+ ADD_STRING(ce->info.user.doc_comment);
}
zend_hash_persist_calc(&ce->properties_info, zend_persist_property_info_calc);
@@ -397,16 +412,16 @@ uint zend_accel_script_persist_calc(zend_persistent_script *new_persistent_scrip
/* script is not going to be saved in SHM */
new_persistent_script->corrupted = 1;
}
- ADD_STRING(new_persistent_script->full_path);
+ ADD_STRING(new_persistent_script->script.filename);
#ifdef __SSE2__
/* Align size to 64-byte boundary */
new_persistent_script->size = (new_persistent_script->size + 63) & ~63;
#endif
- zend_accel_persist_class_table_calc(&new_persistent_script->class_table);
- zend_hash_persist_calc(&new_persistent_script->function_table, zend_persist_op_array_calc);
- zend_persist_op_array_calc_ex(&new_persistent_script->main_op_array);
+ zend_accel_persist_class_table_calc(&new_persistent_script->script.class_table);
+ zend_hash_persist_calc(&new_persistent_script->script.function_table, zend_persist_op_array_calc);
+ zend_persist_op_array_calc_ex(&new_persistent_script->script.main_op_array);
#ifdef __SSE2__
/* Align size to 64-byte boundary */
diff --git a/ext/openssl/tests/001.phpt b/ext/openssl/tests/001.phpt
index 4ca9970bca..d62612b734 100644
--- a/ext/openssl/tests/001.phpt
+++ b/ext/openssl/tests/001.phpt
@@ -24,7 +24,7 @@ if ($privkey === false)
die("failed to create private key");
$passphrase = "banana";
-$key_file_name = tempnam("/tmp", "ssl");
+$key_file_name = tempnam(sys_get_temp_dir(), "ssl");
if ($key_file_name === false)
die("failed to get a temporary filename!");
diff --git a/ext/openssl/tests/bug55259.phpt b/ext/openssl/tests/bug55259.phpt
index 27ededdb18..b58d28ad73 100644
--- a/ext/openssl/tests/bug55259.phpt
+++ b/ext/openssl/tests/bug55259.phpt
@@ -16,14 +16,14 @@ $dh = openssl_pkey_new(array( 'dh'=> array( 'p' => hex2bin($phex), 'g' => '2' ))
$details = openssl_pkey_get_details($dh);
var_dump(bin2hex($details['dh']['p']));
var_dump($details['dh']['g']);
-var_dump(strlen($details['dh']['pub_key']));
-var_dump(strlen($details['dh']['priv_key']));
+var_dump(strlen($details['dh']['pub_key']) > 0);
+var_dump(strlen($details['dh']['priv_key']) > 0);
echo "Done";
?>
--EXPECT--
bool(false)
string(256) "dcf93a0b883972ec0e19989ac5a2ce310e1d37717e8d9571bb7623731866e61ef75a2e27898b057f9891c2e27a639c3f29b60814581cd3b2ca3986d2683705577d45c2e7e52dc81c7a171876e5cea74b1448bfdfaf18828efd2519f14e45e3826634af1949e5b535cc829a483b8a76223e5d490a257f05bdff16f2fb22c583ab"
string(1) "2"
-int(128)
-int(128)
+bool(true)
+bool(true)
Done
diff --git a/ext/openssl/tests/openssl_error_string_basic.phpt b/ext/openssl/tests/openssl_error_string_basic.phpt
index 76bc2d570a..82f3099264 100644
--- a/ext/openssl/tests/openssl_error_string_basic.phpt
+++ b/ext/openssl/tests/openssl_error_string_basic.phpt
@@ -128,8 +128,8 @@ echo "CSR errors\n";
@openssl_csr_get_subject("file://" . $invalid_file_for_read);
expect_openssl_errors('openssl_csr_get_subject open', ['02001002', '2006D080', '20068079', '0906D06C']);
// file or str csr is not correct PEM - failing PEM_read_bio_X509_REQ
-openssl_csr_get_subject($crt_file);
-@expect_openssl_errors('openssl_csr_get_subjec pem', ['0906D06C']);
+@openssl_csr_get_subject($crt_file);
+expect_openssl_errors('openssl_csr_get_subjec pem', ['0906D06C']);
// other possible cuases that are difficult to catch:
// - ASN1_STRING_to_UTF8 fails in add_assoc_name_entry
diff --git a/ext/openssl/tests/openssl_pkcs7_decrypt_basic.phpt b/ext/openssl/tests/openssl_pkcs7_decrypt_basic.phpt
index 5589abb039..27ca4d232c 100644
--- a/ext/openssl/tests/openssl_pkcs7_decrypt_basic.phpt
+++ b/ext/openssl/tests/openssl_pkcs7_decrypt_basic.phpt
@@ -6,10 +6,10 @@ openssl_pkcs7_decrypt() tests
<?php
$infile = dirname(__FILE__) . "/cert.crt";
$privkey = "file://" . dirname(__FILE__) . "/private_rsa_1024.key";
-$encrypted = tempnam("/tmp", "ssl");
+$encrypted = tempnam(sys_get_temp_dir(), "ssl");
if ($encrypted === false)
die("failed to get a temporary filename!");
-$outfile = tempnam("/tmp", "ssl");
+$outfile = tempnam(sys_get_temp_dir(), "ssl");
if ($outfile === false) {
unlink($outfile);
die("failed to get a temporary filename!");
diff --git a/ext/openssl/tests/openssl_pkcs7_encrypt_basic.phpt b/ext/openssl/tests/openssl_pkcs7_encrypt_basic.phpt
index 5f74f97b0c..09a7093b8d 100644
--- a/ext/openssl/tests/openssl_pkcs7_encrypt_basic.phpt
+++ b/ext/openssl/tests/openssl_pkcs7_encrypt_basic.phpt
@@ -5,10 +5,10 @@ openssl_pkcs7_encrypt() tests
--FILE--
<?php
$infile = dirname(__FILE__) . "/cert.crt";
-$outfile = tempnam("/tmp", "ssl");
+$outfile = tempnam(sys_get_temp_dir(), "ssl");
if ($outfile === false)
die("failed to get a temporary filename!");
-$outfile2 = tempnam("/tmp", "ssl");
+$outfile2 = tempnam(sys_get_temp_dir(), "ssl");
if ($outfile2 === false)
die("failed to get a temporary filename!");
diff --git a/ext/openssl/tests/openssl_pkcs7_sign_basic.phpt b/ext/openssl/tests/openssl_pkcs7_sign_basic.phpt
index ac8edf19a9..e06553f4b4 100644
--- a/ext/openssl/tests/openssl_pkcs7_sign_basic.phpt
+++ b/ext/openssl/tests/openssl_pkcs7_sign_basic.phpt
@@ -5,7 +5,7 @@ openssl_pkcs7_sign() tests
--FILE--
<?php
$infile = dirname(__FILE__) . "/cert.crt";
-$outfile = tempnam("/tmp", "ssl");
+$outfile = tempnam(sys_get_temp_dir(), "ssl");
if ($outfile === false) {
die("failed to get a temporary filename!");
}
diff --git a/ext/openssl/xp_ssl.c b/ext/openssl/xp_ssl.c
index 53536a1a77..6f542323d2 100644
--- a/ext/openssl/xp_ssl.c
+++ b/ext/openssl/xp_ssl.c
@@ -2199,39 +2199,39 @@ static inline int php_openssl_tcp_sockop_accept(php_stream *stream, php_openssl_
php_stream_xport_param *xparam STREAMS_DC)
{
int clisock;
-
+ zend_bool nodelay = 0;
+ zval *tmpzval = NULL;
+
xparam->outputs.client = NULL;
+ if ((tmpzval = php_stream_context_get_option(PHP_STREAM_CONTEXT(stream), "socket", "tcp_nodelay")) != NULL &&
+ zend_is_true(tmpzval)) {
+ nodelay = 1;
+ }
+
clisock = php_network_accept_incoming(sock->s.socket,
- xparam->want_textaddr ? &xparam->outputs.textaddr : NULL,
- xparam->want_addr ? &xparam->outputs.addr : NULL,
- xparam->want_addr ? &xparam->outputs.addrlen : NULL,
- xparam->inputs.timeout,
- xparam->want_errortext ? &xparam->outputs.error_text : NULL,
- &xparam->outputs.error_code
- );
+ xparam->want_textaddr ? &xparam->outputs.textaddr : NULL,
+ xparam->want_addr ? &xparam->outputs.addr : NULL,
+ xparam->want_addr ? &xparam->outputs.addrlen : NULL,
+ xparam->inputs.timeout,
+ xparam->want_errortext ? &xparam->outputs.error_text : NULL,
+ &xparam->outputs.error_code,
+ nodelay);
if (clisock >= 0) {
- php_openssl_netstream_data_t *clisockdata;
+ php_openssl_netstream_data_t *clisockdata = (php_openssl_netstream_data_t*) emalloc(sizeof(*clisockdata));
- clisockdata = emalloc(sizeof(*clisockdata));
+ /* copy underlying tcp fields */
+ memset(clisockdata, 0, sizeof(*clisockdata));
+ memcpy(clisockdata, sock, sizeof(clisockdata->s));
- if (clisockdata == NULL) {
- closesocket(clisock);
- /* technically a fatal error */
- } else {
- /* copy underlying tcp fields */
- memset(clisockdata, 0, sizeof(*clisockdata));
- memcpy(clisockdata, sock, sizeof(clisockdata->s));
-
- clisockdata->s.socket = clisock;
+ clisockdata->s.socket = clisock;
- xparam->outputs.client = php_stream_alloc_rel(stream->ops, clisockdata, NULL, "r+");
- if (xparam->outputs.client) {
- xparam->outputs.client->ctx = stream->ctx;
- if (stream->ctx) {
- GC_REFCOUNT(stream->ctx)++;
- }
+ xparam->outputs.client = php_stream_alloc_rel(stream->ops, clisockdata, NULL, "r+");
+ if (xparam->outputs.client) {
+ xparam->outputs.client->ctx = stream->ctx;
+ if (stream->ctx) {
+ GC_REFCOUNT(stream->ctx)++;
}
}
diff --git a/ext/pcre/php_pcre.c b/ext/pcre/php_pcre.c
index ed0f3b3ac4..9225c0f976 100644
--- a/ext/pcre/php_pcre.c
+++ b/ext/pcre/php_pcre.c
@@ -1030,7 +1030,7 @@ static int preg_get_backref(char **str, int *backref)
}
if (in_brace) {
- if (*walk == 0 || *walk != '}')
+ if (*walk != '}')
return 0;
else
walk++;
diff --git a/ext/pdo/pdo.c b/ext/pdo/pdo.c
index 77366f817d..e0fc9d74f2 100644
--- a/ext/pdo/pdo.c
+++ b/ext/pdo/pdo.c
@@ -32,8 +32,7 @@
#include "php_pdo_driver.h"
#include "php_pdo_int.h"
#include "zend_exceptions.h"
-
-static zend_class_entry *spl_ce_RuntimeException;
+#include "ext/spl/spl_exceptions.h"
zend_class_entry *pdo_dbh_ce, *pdo_dbstmt_ce, *pdo_row_ce;
@@ -79,20 +78,9 @@ PDO_API char *php_pdo_str_tolower_dup(const char *src, int len) /* {{{ */
PDO_API zend_class_entry *php_pdo_get_exception_base(int root) /* {{{ */
{
-#if defined(HAVE_SPL)
if (!root) {
- if (!spl_ce_RuntimeException) {
- zend_class_entry *pce;
-
- if ((pce = zend_hash_str_find_ptr(CG(class_table), "runtimeexception", sizeof("RuntimeException") - 1))) {
- spl_ce_RuntimeException = pce;
- return pce;
- }
- } else {
- return spl_ce_RuntimeException;
- }
+ return spl_ce_RuntimeException;
}
-#endif
return zend_ce_exception;
}
/* }}} */
@@ -128,14 +116,10 @@ const zend_function_entry pdo_functions[] = {
/* }}} */
/* {{{ pdo_functions[] */
-#if ZEND_MODULE_API_NO >= 20050922
static const zend_module_dep pdo_deps[] = {
-#ifdef HAVE_SPL
ZEND_MOD_REQUIRED("spl")
-#endif
ZEND_MOD_END
};
-#endif
/* }}} */
/* {{{ pdo_module_entry */
@@ -351,8 +335,6 @@ PHP_MINIT_FUNCTION(pdo)
{
zend_class_entry ce;
- spl_ce_RuntimeException = NULL;
-
if (FAILURE == pdo_sqlstate_init_error_table()) {
return FAILURE;
}
diff --git a/ext/pdo/pdo_dbh.c b/ext/pdo/pdo_dbh.c
index 2fb5334a9d..624281c18b 100644
--- a/ext/pdo/pdo_dbh.c
+++ b/ext/pdo/pdo_dbh.c
@@ -436,10 +436,8 @@ static void pdo_stmt_construct(zend_execute_data *execute_data, pdo_stmt_t *stmt
zval retval;
fci.size = sizeof(zend_fcall_info);
- fci.function_table = &dbstmt_ce->function_table;
ZVAL_UNDEF(&fci.function_name);
fci.object = Z_OBJ_P(object);
- fci.symbol_table = NULL;
fci.retval = &retval;
fci.param_count = 0;
fci.params = NULL;
@@ -449,7 +447,7 @@ static void pdo_stmt_construct(zend_execute_data *execute_data, pdo_stmt_t *stmt
fcc.initialized = 1;
fcc.function_handler = dbstmt_ce->constructor;
- fcc.calling_scope = EG(scope);
+ fcc.calling_scope = zend_get_executed_scope();
fcc.called_scope = Z_OBJCE_P(object);
fcc.object = Z_OBJ_P(object);
@@ -1432,9 +1430,7 @@ void pdo_dbh_init(void)
REGISTER_PDO_CLASS_CONST_LONG("FETCH_KEY_PAIR", (zend_long)PDO_FETCH_KEY_PAIR);
REGISTER_PDO_CLASS_CONST_LONG("FETCH_CLASSTYPE", (zend_long)PDO_FETCH_CLASSTYPE);
-#if PHP_VERSION_ID >= 50100
REGISTER_PDO_CLASS_CONST_LONG("FETCH_SERIALIZE",(zend_long)PDO_FETCH_SERIALIZE);
-#endif
REGISTER_PDO_CLASS_CONST_LONG("FETCH_PROPS_LATE", (zend_long)PDO_FETCH_PROPS_LATE);
REGISTER_PDO_CLASS_CONST_LONG("FETCH_NAMED", (zend_long)PDO_FETCH_NAMED);
diff --git a/ext/pdo/pdo_stmt.c b/ext/pdo/pdo_stmt.c
index fdf5415315..c2f3c13399 100644
--- a/ext/pdo/pdo_stmt.c
+++ b/ext/pdo/pdo_stmt.c
@@ -740,9 +740,7 @@ static int do_fetch_class_prepare(pdo_stmt_t *stmt) /* {{{ */
}
if (ce->constructor) {
- fci->function_table = &ce->function_table;
ZVAL_UNDEF(&fci->function_name);
- fci->symbol_table = NULL;
fci->retval = &stmt->fetch.cls.retval;
fci->param_count = 0;
fci->params = NULL;
@@ -752,7 +750,7 @@ static int do_fetch_class_prepare(pdo_stmt_t *stmt) /* {{{ */
fcc->initialized = 1;
fcc->function_handler = ce->constructor;
- fcc->calling_scope = EG(scope);
+ fcc->calling_scope = zend_get_executed_scope();
fcc->called_scope = ce;
return 1;
} else if (!Z_ISUNDEF(stmt->fetch.cls.ctor_args)) {
diff --git a/ext/pdo_dblib/pdo_dblib.c b/ext/pdo_dblib/pdo_dblib.c
index 578be2ee10..dd64cbe882 100644
--- a/ext/pdo_dblib/pdo_dblib.c
+++ b/ext/pdo_dblib/pdo_dblib.c
@@ -39,24 +39,18 @@ const zend_function_entry pdo_dblib_functions[] = {
PHP_FE_END
};
-#if ZEND_MODULE_API_NO >= 20050922
static const zend_module_dep pdo_dblib_deps[] = {
ZEND_MOD_REQUIRED("pdo")
ZEND_MOD_END
};
-#endif
#if PDO_DBLIB_IS_MSSQL
zend_module_entry pdo_mssql_module_entry = {
#else
zend_module_entry pdo_dblib_module_entry = {
#endif
-#if ZEND_MODULE_API_NO >= 20050922
STANDARD_MODULE_HEADER_EX, NULL,
pdo_dblib_deps,
-#else
- STANDARD_MODULE_HEADER,
-#endif
#if PDO_DBLIB_IS_MSSQL
"pdo_mssql",
#elif defined(PHP_WIN32)
diff --git a/ext/pdo_firebird/tests/testdb.inc b/ext/pdo_firebird/tests/testdb.inc
index f6951a7b13..cbf2023094 100644
--- a/ext/pdo_firebird/tests/testdb.inc
+++ b/ext/pdo_firebird/tests/testdb.inc
@@ -6,7 +6,7 @@ ini_set('ibase.default_user',$user);
ini_set('ibase.default_password',$password);
/* we need just the generated name, not the file itself */
-unlink($test_base = tempnam('/tmp',"php_ibase_test"));
+unlink($test_base = tempnam(sys_get_temp_dir(),"php_ibase_test"));
function init_db()
{
diff --git a/ext/pdo_mysql/mysql_driver.c b/ext/pdo_mysql/mysql_driver.c
index f09bc05314..1b1d1abbea 100644
--- a/ext/pdo_mysql/mysql_driver.c
+++ b/ext/pdo_mysql/mysql_driver.c
@@ -630,12 +630,7 @@ static int pdo_mysql_handle_factory(pdo_dbh_t *dbh, zval *driver_options)
}
#ifndef PDO_USE_MYSQLND
-#if PHP_API_VERSION < 20100412
- if ((PG(open_basedir) && PG(open_basedir)[0] != '\0') || PG(safe_mode))
-#else
- if (PG(open_basedir) && PG(open_basedir)[0] != '\0')
-#endif
- {
+ if (PG(open_basedir) && PG(open_basedir)[0] != '\0') {
local_infile = 0;
}
#endif
diff --git a/ext/pdo_mysql/pdo_mysql.c b/ext/pdo_mysql/pdo_mysql.c
index 7ed1647d8a..fdf7062f62 100644
--- a/ext/pdo_mysql/pdo_mysql.c
+++ b/ext/pdo_mysql/pdo_mysql.c
@@ -229,7 +229,6 @@ const zend_function_entry pdo_mysql_functions[] = {
/* }}} */
/* {{{ pdo_mysql_deps[] */
-#if ZEND_MODULE_API_NO >= 20050922
static const zend_module_dep pdo_mysql_deps[] = {
ZEND_MOD_REQUIRED("pdo")
#ifdef PDO_USE_MYSQLND
@@ -237,7 +236,6 @@ static const zend_module_dep pdo_mysql_deps[] = {
#endif
ZEND_MOD_END
};
-#endif
/* }}} */
/* {{{ pdo_mysql_module_entry */
diff --git a/ext/pdo_mysql/php_pdo_mysql_int.h b/ext/pdo_mysql/php_pdo_mysql_int.h
index a4290fdaf3..20d640c405 100644
--- a/ext/pdo_mysql/php_pdo_mysql_int.h
+++ b/ext/pdo_mysql/php_pdo_mysql_int.h
@@ -126,9 +126,9 @@ typedef struct {
const MYSQL_FIELD *fields;
MYSQL_ROW current_data;
#if PDO_USE_MYSQLND
- zend_ulong *current_lengths;
+ const size_t *current_lengths;
#else
- zend_long *current_lengths;
+ zend_long *current_lengths;
#endif
pdo_mysql_error_info einfo;
#if PDO_USE_MYSQLND
@@ -144,7 +144,7 @@ typedef struct {
#endif
PDO_MYSQL_PARAM_BIND *bound_result;
my_bool *out_null;
- zend_ulong *out_length;
+ zend_ulong *out_length;
unsigned int params_given;
unsigned max_length:1;
} pdo_mysql_stmt;
diff --git a/ext/pdo_oci/pdo_oci.c b/ext/pdo_oci/pdo_oci.c
index 9753f5fce3..0c0cf84574 100644
--- a/ext/pdo_oci/pdo_oci.c
+++ b/ext/pdo_oci/pdo_oci.c
@@ -38,12 +38,10 @@ const zend_function_entry pdo_oci_functions[] = {
/* {{{ pdo_oci_module_entry */
-#if ZEND_MODULE_API_NO >= 20050922
static const zend_module_dep pdo_oci_deps[] = {
ZEND_MOD_REQUIRED("pdo")
ZEND_MOD_END
};
-#endif
zend_module_entry pdo_oci_module_entry = {
STANDARD_MODULE_HEADER_EX, NULL,
diff --git a/ext/pdo_oci/tests/pdo_oci_stream_2a.phpt b/ext/pdo_oci/tests/pdo_oci_stream_2a.phpt
index a7f22d5c41..72d2ed93b1 100644
--- a/ext/pdo_oci/tests/pdo_oci_stream_2a.phpt
+++ b/ext/pdo_oci/tests/pdo_oci_stream_2a.phpt
@@ -53,16 +53,16 @@ $a10 = str_repeat('j', 4095);
printf("Inserting 10000 Records ... ");
for($i=0; $i<1000; $i++) {
- do_insert($db, 1, $a1, $a10);
- do_insert($db, 1, $a2, $a9);
- do_insert($db, 1, $a3, $a8);
- do_insert($db, 1, $a4, $a7);
- do_insert($db, 1, $a5, $a6);
- do_insert($db, 1, $a6, $a5);
- do_insert($db, 1, $a7, $a4);
- do_insert($db, 1, $a8, $a3);
- do_insert($db, 1, $a9, $a2);
- do_insert($db, 1, $a10, $a1);
+ do_insert($db, $i * 10 + 1, $a1, $a10);
+ do_insert($db, $i * 10 + 2, $a2, $a9);
+ do_insert($db, $i * 10 + 3, $a3, $a8);
+ do_insert($db, $i * 10 + 4, $a4, $a7);
+ do_insert($db, $i * 10 + 5, $a5, $a6);
+ do_insert($db, $i * 10 + 6, $a6, $a5);
+ do_insert($db, $i * 10 + 7, $a7, $a4);
+ do_insert($db, $i * 10 + 8, $a8, $a3);
+ do_insert($db, $i * 10 + 9, $a9, $a2);
+ do_insert($db, $i * 10 + 10, $a10, $a1);
}
printf("Done\n");
diff --git a/ext/pdo_odbc/pdo_odbc.c b/ext/pdo_odbc/pdo_odbc.c
index 2cc42934c3..f470b77400 100644
--- a/ext/pdo_odbc/pdo_odbc.c
+++ b/ext/pdo_odbc/pdo_odbc.c
@@ -37,12 +37,10 @@ const zend_function_entry pdo_odbc_functions[] = {
/* }}} */
/* {{{ pdo_odbc_deps[] */
-#if ZEND_MODULE_API_NO >= 20050922
static const zend_module_dep pdo_odbc_deps[] = {
ZEND_MOD_REQUIRED("pdo")
ZEND_MOD_END
};
-#endif
/* }}} */
/* {{{ pdo_odbc_module_entry */
diff --git a/ext/pdo_odbc/php_pdo_odbc.h b/ext/pdo_odbc/php_pdo_odbc.h
index 84742e6466..6686bef368 100644
--- a/ext/pdo_odbc/php_pdo_odbc.h
+++ b/ext/pdo_odbc/php_pdo_odbc.h
@@ -47,16 +47,6 @@ ZEND_BEGIN_MODULE_GLOBALS(pdo_odbc)
ZEND_END_MODULE_GLOBALS(pdo_odbc)
*/
-/* In every utility function you add that needs to use variables
- in php_pdo_odbc_globals, call TSRMLS_FETCH(); after declaring other
- variables used by that function, or better yet, pass in
- after the last function argument and declare your utility function
- with after the last declared argument. Always refer to
- the globals in your function as PDO_ODBC_G(variable). You are
- encouraged to rename these macros something shorter, see
- examples in any other php module directory.
-*/
-
#ifdef ZTS
#define PDO_ODBC_G(v) TSRMG(pdo_odbc_globals_id, zend_pdo_odbc_globals *, v)
#else
diff --git a/ext/pdo_pgsql/pdo_pgsql.c b/ext/pdo_pgsql/pdo_pgsql.c
index f9b0c62eba..1661fbe2d6 100644
--- a/ext/pdo_pgsql/pdo_pgsql.c
+++ b/ext/pdo_pgsql/pdo_pgsql.c
@@ -47,12 +47,10 @@ const zend_function_entry pdo_pgsql_functions[] = {
/* {{{ pdo_sqlite_deps
*/
-#if ZEND_MODULE_API_NO >= 20050922
static const zend_module_dep pdo_pgsql_deps[] = {
ZEND_MOD_REQUIRED("pdo")
ZEND_MOD_END
};
-#endif
/* }}} */
/* {{{ pdo_pgsql_module_entry */
diff --git a/ext/pdo_sqlite/pdo_sqlite.c b/ext/pdo_sqlite/pdo_sqlite.c
index a26d55494b..ca077092a3 100644
--- a/ext/pdo_sqlite/pdo_sqlite.c
+++ b/ext/pdo_sqlite/pdo_sqlite.c
@@ -39,12 +39,10 @@ const zend_function_entry pdo_sqlite_functions[] = {
/* {{{ pdo_sqlite_deps
*/
-#if ZEND_MODULE_API_NO >= 20050922
static const zend_module_dep pdo_sqlite_deps[] = {
ZEND_MOD_REQUIRED("pdo")
ZEND_MOD_END
};
-#endif
/* }}} */
/* {{{ pdo_sqlite_module_entry
diff --git a/ext/pdo_sqlite/php_pdo_sqlite.h b/ext/pdo_sqlite/php_pdo_sqlite.h
index f5e4457971..5d924f800b 100644
--- a/ext/pdo_sqlite/php_pdo_sqlite.h
+++ b/ext/pdo_sqlite/php_pdo_sqlite.h
@@ -48,16 +48,6 @@ ZEND_BEGIN_MODULE_GLOBALS(pdo_sqlite)
ZEND_END_MODULE_GLOBALS(pdo_sqlite)
*/
-/* In every utility function you add that needs to use variables
- in php_pdo_sqlite_globals, call TSRMLS_FETCH(); after declaring other
- variables used by that function, or better yet, pass in
- after the last function argument and declare your utility function
- with after the last declared argument. Always refer to
- the globals in your function as PDO_SQLITE_G(variable). You are
- encouraged to rename these macros something shorter, see
- examples in any other php module directory.
-*/
-
#ifdef ZTS
#define PDO_SQLITE_G(v) TSRMG(pdo_sqlite_globals_id, zend_pdo_sqlite_globals *, v)
#else
diff --git a/ext/pdo_sqlite/sqlite_driver.c b/ext/pdo_sqlite/sqlite_driver.c
index 346cdf2f27..4e729aab0e 100644
--- a/ext/pdo_sqlite/sqlite_driver.c
+++ b/ext/pdo_sqlite/sqlite_driver.c
@@ -325,9 +325,7 @@ static int do_callback(struct pdo_sqlite_fci *fc, zval *cb,
fake_argc = argc + is_agg;
fc->fci.size = sizeof(fc->fci);
- fc->fci.function_table = EG(function_table);
ZVAL_COPY_VALUE(&fc->fci.function_name, cb);
- fc->fci.symbol_table = NULL;
fc->fci.object = NULL;
fc->fci.retval = &retval;
fc->fci.param_count = fake_argc;
@@ -476,9 +474,7 @@ static int php_sqlite3_collation_callback(void *context,
struct pdo_sqlite_collation *collation = (struct pdo_sqlite_collation*) context;
collation->fc.fci.size = sizeof(collation->fc.fci);
- collation->fc.fci.function_table = EG(function_table);
ZVAL_COPY_VALUE(&collation->fc.fci.function_name, &collation->callback);
- collation->fc.fci.symbol_table = NULL;
collation->fc.fci.object = NULL;
collation->fc.fci.retval = &retval;
diff --git a/ext/pgsql/pgsql.c b/ext/pgsql/pgsql.c
index 8f0db26c92..16a84ffcd8 100644
--- a/ext/pgsql/pgsql.c
+++ b/ext/pgsql/pgsql.c
@@ -56,9 +56,13 @@
#define InvalidOid ((Oid) 0)
#endif
-#define PGSQL_ASSOC 1<<0
-#define PGSQL_NUM 1<<1
-#define PGSQL_BOTH (PGSQL_ASSOC|PGSQL_NUM)
+#define PGSQL_ASSOC 1<<0
+#define PGSQL_NUM 1<<1
+#define PGSQL_BOTH (PGSQL_ASSOC|PGSQL_NUM)
+
+#define PGSQL_NOTICE_LAST 1 /* Get the last notice */
+#define PGSQL_NOTICE_ALL 2 /* Get all notices */
+#define PGSQL_NOTICE_CLEAR 3 /* Remove notices */
#define PGSQL_STATUS_LONG 1
#define PGSQL_STATUS_STRING 2
@@ -208,6 +212,7 @@ ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_last_notice, 0, 0, 1)
ZEND_ARG_INFO(0, connection)
+ ZEND_ARG_INFO(0, option)
ZEND_END_ARG_INFO()
#ifdef HAVE_PQFTABLE
@@ -276,6 +281,7 @@ ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_fetch_all, 0, 0, 1)
ZEND_ARG_INFO(0, result)
+ ZEND_ARG_INFO(0, result_type)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_fetch_all_columns, 0, 0, 1)
@@ -582,6 +588,7 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_pg_select, 0, 0, 3)
ZEND_ARG_INFO(0, table)
ZEND_ARG_INFO(0, ids)
ZEND_ARG_INFO(0, options)
+ ZEND_ARG_INFO(0, result_type)
ZEND_END_ARG_INFO()
/* }}} */
@@ -961,29 +968,24 @@ static void _close_pgsql_plink(zend_resource *rsrc)
*/
static void _php_pgsql_notice_handler(void *resource_id, const char *message)
{
- php_pgsql_notice *notice;
+ zval *notices;
+ zval tmp;
+ char *trimed_message;
+ size_t trimed_message_len;
if (! PGG(ignore_notices)) {
- notice = (php_pgsql_notice *)emalloc(sizeof(php_pgsql_notice));
- notice->message = _php_pgsql_trim_message(message, &notice->len);
+ notices = zend_hash_index_find(&PGG(notices), (zend_ulong)resource_id);
+ if (!notices) {
+ array_init(&tmp);
+ notices = &tmp;
+ zend_hash_index_update(&PGG(notices), (zend_ulong)resource_id, notices);
+ }
+ trimed_message = _php_pgsql_trim_message(message, &trimed_message_len);
if (PGG(log_notices)) {
- php_error_docref(NULL, E_NOTICE, "%s", notice->message);
+ php_error_docref(NULL, E_NOTICE, "%s", trimed_message);
}
- zend_hash_index_update_ptr(&PGG(notices), (zend_ulong)resource_id, notice);
- }
-}
-/* }}} */
-
-#define PHP_PGSQL_NOTICE_PTR_DTOR _php_pgsql_notice_ptr_dtor
-
-/* {{{ _php_pgsql_notice_dtor
- */
-static void _php_pgsql_notice_ptr_dtor(zval *el)
-{
- php_pgsql_notice *notice = (php_pgsql_notice *)Z_PTR_P(el);
- if (notice) {
- efree(notice->message);
- efree(notice);
+ add_next_index_stringl(notices, trimed_message, trimed_message_len);
+ efree(trimed_message);
}
}
/* }}} */
@@ -1096,7 +1098,7 @@ static PHP_GINIT_FUNCTION(pgsql)
#endif
memset(pgsql_globals, 0, sizeof(zend_pgsql_globals));
/* Initilize notice message hash at MINIT only */
- zend_hash_init_ex(&pgsql_globals->notices, 0, NULL, PHP_PGSQL_NOTICE_PTR_DTOR, 1, 0);
+ zend_hash_init_ex(&pgsql_globals->notices, 0, NULL, ZVAL_PTR_DTOR, 1, 0);
}
/* }}} */
@@ -1123,6 +1125,10 @@ PHP_MINIT_FUNCTION(pgsql)
REGISTER_LONG_CONSTANT("PGSQL_ASSOC", PGSQL_ASSOC, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("PGSQL_NUM", PGSQL_NUM, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("PGSQL_BOTH", PGSQL_BOTH, CONST_CS | CONST_PERSISTENT);
+ /* For pg_last_notice() */
+ REGISTER_LONG_CONSTANT("PGSQL_NOTICE_LAST", PGSQL_NOTICE_LAST, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("PGSQL_NOTICE_ALL", PGSQL_NOTICE_ALL, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("PGSQL_NOTICE_CLEAR", PGSQL_NOTICE_CLEAR, CONST_CS | CONST_PERSISTENT);
/* For pg_connection_status() */
REGISTER_LONG_CONSTANT("PGSQL_CONNECTION_BAD", CONNECTION_BAD, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("PGSQL_CONNECTION_OK", CONNECTION_OK, CONST_CS | CONST_PERSISTENT);
@@ -2313,15 +2319,16 @@ PHP_FUNCTION(pg_affected_rows)
/* }}} */
#endif
-/* {{{ proto string pg_last_notice(resource connection)
+/* {{{ proto mixed pg_last_notice(resource connection [, long option])
Returns the last notice set by the backend */
PHP_FUNCTION(pg_last_notice)
{
zval *pgsql_link = NULL;
+ zval *notice, *notices;
PGconn *pg_link;
- php_pgsql_notice *notice;
+ zend_long option = PGSQL_NOTICE_LAST;
- if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &pgsql_link) == FAILURE) {
+ if (zend_parse_parameters(ZEND_NUM_ARGS(), "r|l", &pgsql_link, &option) == FAILURE) {
return;
}
@@ -2330,10 +2337,38 @@ PHP_FUNCTION(pg_last_notice)
RETURN_FALSE;
}
- if ((notice = zend_hash_index_find_ptr(&PGG(notices), (zend_ulong)Z_RES_HANDLE_P(pgsql_link))) == NULL) {
- RETURN_FALSE;
+ notices = zend_hash_index_find(&PGG(notices), (zend_ulong)Z_RES_HANDLE_P(pgsql_link));
+ switch (option) {
+ case PGSQL_NOTICE_LAST:
+ if (notices) {
+ zend_hash_internal_pointer_end(Z_ARRVAL_P(notices));
+ if ((notice = zend_hash_get_current_data(Z_ARRVAL_P(notices))) == NULL) {
+ RETURN_EMPTY_STRING();
+ }
+ RETURN_ZVAL(notice, 1, 0);
+ } else {
+ RETURN_EMPTY_STRING();
+ }
+ break;
+ case PGSQL_NOTICE_ALL:
+ if (notices) {
+ RETURN_ZVAL(notices, 1, 0);
+ } else {
+ array_init(return_value);
+ return;
+ }
+ break;
+ case PGSQL_NOTICE_CLEAR:
+ if (notices) {
+ zend_hash_clean(&PGG(notices));
+ }
+ RETURN_TRUE;
+ break;
+ default:
+ php_error_docref(NULL, E_WARNING,
+ "Invalid option specified (" ZEND_LONG_FMT ")", option);
}
- RETURN_STRINGL(notice->message, notice->len);
+ RETURN_FALSE;
}
/* }}} */
@@ -2796,9 +2831,7 @@ static void php_pgsql_fetch_hash(INTERNAL_FUNCTION_PARAMETERS, zend_long result_
if (ce->constructor) {
fci.size = sizeof(fci);
- fci.function_table = &ce->function_table;
ZVAL_UNDEF(&fci.function_name);
- fci.symbol_table = NULL;
fci.object = Z_OBJ_P(return_value);
fci.retval = &retval;
fci.params = NULL;
@@ -2820,7 +2853,7 @@ static void php_pgsql_fetch_hash(INTERNAL_FUNCTION_PARAMETERS, zend_long result_
fcc.initialized = 1;
fcc.function_handler = ce->constructor;
- fcc.calling_scope = EG(scope);
+ fcc.calling_scope = zend_get_executed_scope();
fcc.called_scope = Z_OBJCE_P(return_value);
fcc.object = Z_OBJ_P(return_value);
@@ -2877,25 +2910,31 @@ PHP_FUNCTION(pg_fetch_object)
}
/* }}} */
-/* {{{ proto array pg_fetch_all(resource result)
+/* {{{ proto array pg_fetch_all(resource result [, int result_type])
Fetch all rows into array */
PHP_FUNCTION(pg_fetch_all)
{
zval *result;
+ long result_type = PGSQL_ASSOC;
PGresult *pgsql_result;
pgsql_result_handle *pg_result;
- if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &result) == FAILURE) {
+ if (zend_parse_parameters(ZEND_NUM_ARGS(), "r|l", &result, &result_type) == FAILURE) {
return;
}
+ if (!(result_type & PGSQL_BOTH)) {
+ php_error_docref(NULL, E_WARNING, "Invalid result type");
+ RETURN_FALSE;
+ }
+
if ((pg_result = (pgsql_result_handle *)zend_fetch_resource(Z_RES_P(result), "PostgreSQL result", le_result)) == NULL) {
RETURN_FALSE;
}
pgsql_result = pg_result->result;
array_init(return_value);
- if (php_pgsql_result2array(pgsql_result, return_value) == FAILURE) {
+ if (php_pgsql_result2array(pgsql_result, return_value, result_type) == FAILURE) {
zval_dtor(return_value);
RETURN_FALSE;
}
@@ -7014,7 +7053,7 @@ PHP_FUNCTION(pg_delete)
/* {{{ php_pgsql_result2array
*/
-PHP_PGSQL_API int php_pgsql_result2array(PGresult *pg_result, zval *ret_array)
+PHP_PGSQL_API int php_pgsql_result2array(PGresult *pg_result, zval *ret_array, long result_type)
{
zval row;
char *field_name;
@@ -7029,16 +7068,24 @@ PHP_PGSQL_API int php_pgsql_result2array(PGresult *pg_result, zval *ret_array)
for (pg_row = 0; pg_row < pg_numrows; pg_row++) {
array_init(&row);
for (i = 0, num_fields = PQnfields(pg_result); i < num_fields; i++) {
+ field_name = PQfname(pg_result, i);
if (PQgetisnull(pg_result, pg_row, i)) {
- field_name = PQfname(pg_result, i);
- add_assoc_null(&row, field_name);
+ if (result_type & PGSQL_ASSOC) {
+ add_assoc_null(&row, field_name);
+ }
+ if (result_type & PGSQL_NUM) {
+ add_next_index_null(&row);
+ }
} else {
char *element = PQgetvalue(pg_result, pg_row, i);
if (element) {
const size_t element_len = strlen(element);
-
- field_name = PQfname(pg_result, i);
- add_assoc_stringl(&row, field_name, element, element_len);
+ if (result_type & PGSQL_ASSOC) {
+ add_assoc_stringl(&row, field_name, element, element_len);
+ }
+ if (result_type & PGSQL_NUM) {
+ add_next_index_stringl(&row, element, element_len);
+ }
}
}
}
@@ -7050,7 +7097,7 @@ PHP_PGSQL_API int php_pgsql_result2array(PGresult *pg_result, zval *ret_array)
/* {{{ php_pgsql_select
*/
-PHP_PGSQL_API int php_pgsql_select(PGconn *pg_link, const char *table, zval *ids_array, zval *ret_array, zend_ulong opt, zend_string **sql)
+ PHP_PGSQL_API int php_pgsql_select(PGconn *pg_link, const char *table, zval *ids_array, zval *ret_array, zend_ulong opt, long result_type, zend_string **sql)
{
zval ids_converted;
smart_str querystr = {0};
@@ -7088,7 +7135,7 @@ PHP_PGSQL_API int php_pgsql_select(PGconn *pg_link, const char *table, zval *ids
pg_result = PQexec(pg_link, ZSTR_VAL(querystr.s));
if (PQresultStatus(pg_result) == PGRES_TUPLES_OK) {
- ret = php_pgsql_result2array(pg_result, ret_array);
+ ret = php_pgsql_result2array(pg_result, ret_array, result_type);
} else {
php_error_docref(NULL, E_NOTICE, "Failed to execute '%s'", ZSTR_VAL(querystr.s));
}
@@ -7106,7 +7153,7 @@ cleanup:
}
/* }}} */
-/* {{{ proto mixed pg_select(resource db, string table, array ids[, int options])
+/* {{{ proto mixed pg_select(resource db, string table, array ids[, int options [, int result_type])
Select records that has ids (id=>value) */
PHP_FUNCTION(pg_select)
{
@@ -7114,18 +7161,23 @@ PHP_FUNCTION(pg_select)
char *table;
size_t table_len;
zend_ulong option = PGSQL_DML_EXEC;
+ long result_type = PGSQL_ASSOC;
PGconn *pg_link;
zend_string *sql = NULL;
int argc = ZEND_NUM_ARGS();
if (zend_parse_parameters(argc, "rsa|l",
- &pgsql_link, &table, &table_len, &ids, &option) == FAILURE) {
+ &pgsql_link, &table, &table_len, &ids, &option, &result_type) == FAILURE) {
return;
}
if (option & ~(PGSQL_CONV_FORCE_NULL|PGSQL_DML_NO_CONV|PGSQL_DML_EXEC|PGSQL_DML_ASYNC|PGSQL_DML_STRING|PGSQL_DML_ESCAPE)) {
php_error_docref(NULL, E_WARNING, "Invalid option is specified");
RETURN_FALSE;
}
+ if (!(result_type & PGSQL_BOTH)) {
+ php_error_docref(NULL, E_WARNING, "Invalid result type");
+ RETURN_FALSE;
+ }
if ((pg_link = (PGconn *)zend_fetch_resource2(Z_RES_P(pgsql_link), "PostgreSQL link", le_link, le_plink)) == NULL) {
RETURN_FALSE;
@@ -7135,7 +7187,7 @@ PHP_FUNCTION(pg_select)
php_error_docref(NULL, E_NOTICE, "Detected unhandled result(s) in connection");
}
array_init(return_value);
- if (php_pgsql_select(pg_link, table, ids, return_value, option, &sql) == FAILURE) {
+ if (php_pgsql_select(pg_link, table, ids, return_value, option, result_type, &sql) == FAILURE) {
zval_ptr_dtor(return_value);
RETURN_FALSE;
}
diff --git a/ext/pgsql/php_pgsql.h b/ext/pgsql/php_pgsql.h
index 9088130a82..12549d7bfe 100644
--- a/ext/pgsql/php_pgsql.h
+++ b/ext/pgsql/php_pgsql.h
@@ -211,15 +211,14 @@ PHP_FUNCTION(pg_select);
#define PGSQL_DML_STRING (1<<11) /* Return query string */
#define PGSQL_DML_ESCAPE (1<<12) /* No convert, but escape only */
-
/* exported functions */
PHP_PGSQL_API int php_pgsql_meta_data(PGconn *pg_link, const char *table_name, zval *meta, zend_bool extended);
PHP_PGSQL_API int php_pgsql_convert(PGconn *pg_link, const char *table_name, const zval *values, zval *result, zend_ulong opt);
PHP_PGSQL_API int php_pgsql_insert(PGconn *pg_link, const char *table, zval *values, zend_ulong opt, zend_string **sql);
PHP_PGSQL_API int php_pgsql_update(PGconn *pg_link, const char *table, zval *values, zval *ids, zend_ulong opt , zend_string **sql);
PHP_PGSQL_API int php_pgsql_delete(PGconn *pg_link, const char *table, zval *ids, zend_ulong opt, zend_string **sql);
-PHP_PGSQL_API int php_pgsql_select(PGconn *pg_link, const char *table, zval *ids, zval *ret_array, zend_ulong opt, zend_string **sql );
-PHP_PGSQL_API int php_pgsql_result2array(PGresult *pg_result, zval *ret_array);
+PHP_PGSQL_API int php_pgsql_select(PGconn *pg_link, const char *table, zval *ids, zval *ret_array, zend_ulong opt, long fetch_option, zend_string **sql );
+PHP_PGSQL_API int php_pgsql_result2array(PGresult *pg_result, zval *ret_array, long fetch_option);
/* internal functions */
static void php_pgsql_do_connect(INTERNAL_FUNCTION_PARAMETERS,int persistent);
diff --git a/ext/pgsql/tests/09notice.phpt b/ext/pgsql/tests/09notice.phpt
index db671016e3..fb5dc8325c 100644
--- a/ext/pgsql/tests/09notice.phpt
+++ b/ext/pgsql/tests/09notice.phpt
@@ -9,35 +9,66 @@ _skip_lc_messages();
?>
--INI--
-pgsql.log_notice=1
-pgsql.ignore_notice=0
--FILE--
<?php
include 'config.inc';
include 'lcmess.inc';
+ini_set('pgsql.log_notice', TRUE);
+ini_set('pgsql.ignore_notice', FALSE);
+
$db = pg_connect($conn_str);
_set_lc_messages();
-$res = pg_query($db, 'SET client_min_messages TO NOTICE;');
+$res = pg_query($db, 'SET client_min_messages TO NOTICE;');
var_dump($res);
+// Get empty notice
+var_dump(pg_last_notice($db));
+var_dump(pg_last_notice($db, PGSQL_NOTICE_ALL));
+
+pg_query($db, "BEGIN;");
+pg_query($db, "BEGIN;");
pg_query($db, "BEGIN;");
pg_query($db, "BEGIN;");
-$msg = pg_last_notice($db);
-if ($msg === FALSE) {
- echo "Cannot find notice message in hash\n";
- var_dump($msg);
-}
-echo $msg."\n";
-echo "pg_last_notice() is Ok\n";
+// Get notices
+var_dump(pg_last_notice($db));
+var_dump(pg_last_notice($db, PGSQL_NOTICE_ALL));
+
+// Clear and get notices
+var_dump(pg_last_notice($db, PGSQL_NOTICE_CLEAR));
+var_dump(pg_last_notice($db, PGSQL_NOTICE_LAST));
+var_dump(pg_last_notice($db, PGSQL_NOTICE_ALL));
+// Invalid option
+var_dump(pg_last_notice($db, 99));
?>
--EXPECTF--
resource(%d) of type (pgsql result)
+string(0) ""
+array(0) {
+}
Notice: pg_query(): %s already a transaction in progress in %s on line %d
-%s already a transaction in progress
-pg_last_notice() is Ok
+
+Notice: pg_query(): %s already a transaction in progress in %s on line %d
+
+Notice: pg_query(): %s already a transaction in progress in %s on line %d
+string(52) "WARNING: there is already a transaction in progress"
+array(3) {
+ [0]=>
+ string(52) "WARNING: there is already a transaction in progress"
+ [1]=>
+ string(52) "WARNING: there is already a transaction in progress"
+ [2]=>
+ string(52) "WARNING: there is already a transaction in progress"
+}
+bool(true)
+string(0) ""
+array(0) {
+}
+
+Warning: pg_last_notice(): Invalid option specified (99) in %s/09notice.php on line %d
+bool(false)
diff --git a/ext/pgsql/tests/bug72195.phpt b/ext/pgsql/tests/bug72195.phpt
index 2f33e1ab1c..34735d31f4 100644
--- a/ext/pgsql/tests/bug72195.phpt
+++ b/ext/pgsql/tests/bug72195.phpt
@@ -9,7 +9,7 @@ $var1 = $val;
printf("%x\n", count($val));
@pg_pconnect($var1, "2", "3", "4");
$var1 = "";
-tempnam('/tmp', 'ABCDEFGHI');
+tempnam(sys_get_temp_dir(), 'ABCDEFGHI');
printf("%x\n", count($val));
?>
--EXPECT--
diff --git a/ext/phar/phar.c b/ext/phar/phar.c
index 860f5132d4..2b65a54ed9 100644
--- a/ext/phar/phar.c
+++ b/ext/phar/phar.c
@@ -516,15 +516,15 @@ void phar_entry_remove(phar_entry_data *idata, char **error) /* {{{ */
memcpy(&var, buffer, sizeof(var)); \
buffer += 4
# define PHAR_GET_16(buffer, var) \
- var = *(php_uint16*)(buffer); \
+ var = *(uint16_t*)(buffer); \
buffer += 2
#endif
-#define PHAR_ZIP_16(var) ((php_uint16)((((php_uint16)var[0]) & 0xff) | \
- (((php_uint16)var[1]) & 0xff) << 8))
-#define PHAR_ZIP_32(var) ((php_uint32)((((php_uint32)var[0]) & 0xff) | \
- (((php_uint32)var[1]) & 0xff) << 8 | \
- (((php_uint32)var[2]) & 0xff) << 16 | \
- (((php_uint32)var[3]) & 0xff) << 24))
+#define PHAR_ZIP_16(var) ((uint16_t)((((uint16_t)var[0]) & 0xff) | \
+ (((uint16_t)var[1]) & 0xff) << 8))
+#define PHAR_ZIP_32(var) ((uint32_t)((((uint32_t)var[0]) & 0xff) | \
+ (((uint32_t)var[1]) & 0xff) << 8 | \
+ (((uint32_t)var[2]) & 0xff) << 16 | \
+ (((uint32_t)var[3]) & 0xff) << 24))
/**
* Open an already loaded phar
@@ -604,7 +604,7 @@ int phar_open_parsed_phar(char *fname, int fname_len, char *alias, int alias_len
*
* data is the serialized zval
*/
-int phar_parse_metadata(char **buffer, zval *metadata, php_uint32 zip_metadata_len) /* {{{ */
+int phar_parse_metadata(char **buffer, zval *metadata, uint32_t zip_metadata_len) /* {{{ */
{
php_unserialize_data_t var_hash;
@@ -649,14 +649,14 @@ int phar_parse_metadata(char **buffer, zval *metadata, php_uint32 zip_metadata_l
* This is used by phar_open_from_filename to process the manifest, but can be called
* directly.
*/
-static int phar_parse_pharfile(php_stream *fp, char *fname, int fname_len, char *alias, int alias_len, zend_long halt_offset, phar_archive_data** pphar, php_uint32 compression, char **error) /* {{{ */
+static int phar_parse_pharfile(php_stream *fp, char *fname, int fname_len, char *alias, int alias_len, zend_long halt_offset, phar_archive_data** pphar, uint32_t compression, char **error) /* {{{ */
{
char b32[4], *buffer, *endbuffer, *savebuf;
phar_archive_data *mydata = NULL;
phar_entry_info entry;
- php_uint32 manifest_len, manifest_count, manifest_flags, manifest_index, tmp_len, sig_flags;
- php_uint16 manifest_ver;
- php_uint32 len;
+ uint32_t manifest_len, manifest_count, manifest_flags, manifest_index, tmp_len, sig_flags;
+ uint16_t manifest_ver;
+ uint32_t len;
zend_long offset;
int sig_len, register_alias = 0, temp_alias = 0;
char *signature = NULL;
@@ -778,7 +778,7 @@ static int phar_parse_pharfile(php_stream *fp, char *fname, int fname_len, char
switch(sig_flags) {
case PHAR_SIG_OPENSSL: {
- php_uint32 signature_len;
+ uint32_t signature_len;
char *sig;
zend_off_t whence;
@@ -1326,11 +1326,6 @@ int phar_create_or_parse_filename(char *fname, int fname_len, char *alias, int a
if (!pphar) {
pphar = &mydata;
}
-#if PHP_API_VERSION < 20100412
- if (PG(safe_mode) && (!php_checkuid(fname, NULL, CHECKUID_ALLOW_ONLY_FILE))) {
- return FAILURE;
- }
-#endif
if (php_check_open_basedir(fname)) {
return FAILURE;
}
@@ -1490,11 +1485,6 @@ int phar_open_from_filename(char *fname, int fname_len, char *alias, int alias_l
} else if (error && *error) {
return FAILURE;
}
-#if PHP_API_VERSION < 20100412
- if (PG(safe_mode) && (!php_checkuid(fname, NULL, CHECKUID_ALLOW_ONLY_FILE))) {
- return FAILURE;
- }
-#endif
if (php_check_open_basedir(fname)) {
return FAILURE;
}
@@ -1575,7 +1565,7 @@ static int phar_open_from_fp(php_stream* fp, char *fname, int fname_len, char *a
const zend_long tokenlen = sizeof(token) - 1;
zend_long halt_offset;
size_t got;
- php_uint32 compression = PHAR_FILE_COMPRESSED_NONE;
+ uint32_t compression = PHAR_FILE_COMPRESSED_NONE;
if (error) {
*error = NULL;
@@ -2296,13 +2286,6 @@ int phar_open_executed_filename(char *alias, int alias_len, char **error) /* {{{
return FAILURE;
}
-
-#if PHP_API_VERSION < 20100412
- if (PG(safe_mode) && (!php_checkuid(fname, NULL, CHECKUID_ALLOW_ONLY_FILE))) {
- return FAILURE;
- }
-#endif
-
if (php_check_open_basedir(fname)) {
return FAILURE;
}
@@ -2337,9 +2320,9 @@ int phar_open_executed_filename(char *alias, int alias_len, char **error) /* {{{
/**
* Validate the CRC32 of a file opened from within the phar
*/
-int phar_postprocess_file(phar_entry_data *idata, php_uint32 crc32, char **error, int process_zip) /* {{{ */
+int phar_postprocess_file(phar_entry_data *idata, uint32_t crc32, char **error, int process_zip) /* {{{ */
{
- php_uint32 crc = ~0;
+ uint32_t crc = ~0;
int len = idata->internal_file->uncompressed_filesize;
php_stream *fp = idata->fp;
phar_entry_info *entry = idata->internal_file;
@@ -2504,8 +2487,8 @@ int phar_flush(phar_archive_data *phar, char *user_stub, zend_long len, int conv
zend_off_t manifest_ftell;
zend_long offset;
size_t wrote;
- php_uint32 manifest_len, mytime, loc, new_manifest_count;
- php_uint32 newcrc32;
+ uint32_t manifest_len, mytime, loc, new_manifest_count;
+ uint32_t newcrc32;
php_stream *file, *oldfile, *newfile, *stubfile;
php_stream_filter *filter;
php_serialize_data_t metadata_hash;
@@ -2837,7 +2820,7 @@ int phar_flush(phar_archive_data *phar, char *user_stub, zend_long len, int conv
php_stream_flush(entry->cfp);
php_stream_filter_remove(filter, 1);
php_stream_seek(entry->cfp, 0, SEEK_END);
- entry->compressed_filesize = (php_uint32) php_stream_tell(entry->cfp);
+ entry->compressed_filesize = (uint32_t) php_stream_tell(entry->cfp);
/* generate crc on compressed file */
php_stream_rewind(entry->cfp);
entry->old_flags = entry->flags;
@@ -3592,9 +3575,7 @@ static const zend_module_dep phar_deps[] = {
#if defined(HAVE_HASH) && !defined(COMPILE_DL_HASH)
ZEND_MOD_REQUIRED("hash")
#endif
-#if HAVE_SPL
ZEND_MOD_REQUIRED("spl")
-#endif
ZEND_MOD_END
};
diff --git a/ext/phar/phar_internal.h b/ext/phar/phar_internal.h
index e0ae624dc9..cd562ea411 100644
--- a/ext/phar/phar_internal.h
+++ b/ext/phar/phar_internal.h
@@ -55,13 +55,11 @@
#include "TSRM/tsrm_strtok_r.h"
#endif
#include "Zend/zend_virtual_cwd.h"
-#if HAVE_SPL
#include "ext/spl/spl_array.h"
#include "ext/spl/spl_directory.h"
#include "ext/spl/spl_engine.h"
#include "ext/spl/spl_exceptions.h"
#include "ext/spl/spl_iterators.h"
-#endif
#include "php_phar.h"
#ifdef PHAR_HASH_OK
#include "ext/hash/php_hash.h"
@@ -201,19 +199,10 @@ ZEND_EXTERN_MODULE_GLOBALS(phar)
ZEND_TSRMLS_CACHE_EXTERN()
#endif
-#ifndef php_uint16
-# if SIZEOF_SHORT == 2
-# define php_uint16 unsigned short
-# else
-# define php_uint16 uint16_t
-# endif
-#endif
#include "pharzip.h"
-#if HAVE_SPL
typedef union _phar_archive_object phar_archive_object;
typedef union _phar_entry_object phar_entry_object;
-#endif
/*
* used in phar_entry_info->fp_type to
@@ -233,17 +222,17 @@ enum phar_fp_type {
/* entry for one file in a phar file */
typedef struct _phar_entry_info {
/* first bytes are exactly as in file */
- php_uint32 uncompressed_filesize;
- php_uint32 timestamp;
- php_uint32 compressed_filesize;
- php_uint32 crc32;
- php_uint32 flags;
+ uint32_t uncompressed_filesize;
+ uint32_t timestamp;
+ uint32_t compressed_filesize;
+ uint32_t crc32;
+ uint32_t flags;
/* remainder */
/* when changing compression, save old flags in case fp is NULL */
- php_uint32 old_flags;
+ uint32_t old_flags;
zval metadata;
int metadata_len; /* only used for cached manifests */
- php_uint32 filename_len;
+ uint32_t filename_len;
char *filename;
enum phar_fp_type fp_type;
/* offset within original phar file of the file contents */
@@ -299,14 +288,14 @@ struct _phar_archive_data {
HashTable virtual_dirs;
/* hash of mounted directory paths */
HashTable mounted_dirs;
- php_uint32 flags;
- php_uint32 min_timestamp;
- php_uint32 max_timestamp;
+ uint32_t flags;
+ uint32_t min_timestamp;
+ uint32_t max_timestamp;
php_stream *fp;
/* decompressed file contents are stored here */
php_stream *ufp;
int refcount;
- php_uint32 sig_flags;
+ uint32_t sig_flags;
int sig_len;
char *signature;
zval metadata;
@@ -473,21 +462,17 @@ typedef struct _phar_entry_data {
phar_entry_info *internal_file;
} phar_entry_data;
-#if HAVE_SPL
/* archive php object */
union _phar_archive_object {
spl_filesystem_object spl;
phar_archive_data *archive;
};
-#endif
-#if HAVE_SPL
/* entry php object */
union _phar_entry_object {
spl_filesystem_object spl;
phar_entry_info *entry;
};
-#endif
#ifndef PHAR_MAIN
extern zend_string *(*phar_save_resolve_path)(const char *filename, int filename_len);
@@ -544,7 +529,7 @@ void phar_object_init(void);
void phar_destroy_phar_data(phar_archive_data *phar);
int phar_open_entry_file(phar_archive_data *phar, phar_entry_info *entry, char **error);
-int phar_postprocess_file(phar_entry_data *idata, php_uint32 crc32, char **error, int process_zip);
+int phar_postprocess_file(phar_entry_data *idata, uint32_t crc32, char **error, int process_zip);
int phar_open_from_filename(char *fname, int fname_len, char *alias, int alias_len, int options, phar_archive_data** pphar, char **error);
int phar_open_or_create_filename(char *fname, int fname_len, char *alias, int alias_len, int is_data, int options, phar_archive_data** pphar, char **error);
int phar_create_or_parse_filename(char *fname, int fname_len, char *alias, int alias_len, int is_data, int options, phar_archive_data** pphar, char **error);
@@ -552,7 +537,7 @@ int phar_open_executed_filename(char *alias, int alias_len, char **error);
int phar_free_alias(phar_archive_data *phar, char *alias, int alias_len);
int phar_get_archive(phar_archive_data **archive, char *fname, int fname_len, char *alias, int alias_len, char **error);
int phar_open_parsed_phar(char *fname, int fname_len, char *alias, int alias_len, int is_data, int options, phar_archive_data** pphar, char **error);
-int phar_verify_signature(php_stream *fp, size_t end_of_phar, php_uint32 sig_type, char *sig, int sig_len, char *fname, char **signature, int *signature_len, char **error);
+int phar_verify_signature(php_stream *fp, size_t end_of_phar, uint32_t sig_type, char *sig, int sig_len, char *fname, char **signature, int *signature_len, char **error);
int phar_create_signature(phar_archive_data *phar, php_stream *fp, char **signature, int *signature_length, char **error);
/* utility functions */
@@ -566,7 +551,7 @@ int phar_mount_entry(phar_archive_data *phar, char *filename, int filename_len,
zend_string *phar_find_in_include_path(char *file, int file_len, phar_archive_data **pphar);
char *phar_fix_filepath(char *path, int *new_len, int use_cwd);
phar_entry_info * phar_open_jit(phar_archive_data *phar, phar_entry_info *entry, char **error);
-int phar_parse_metadata(char **buffer, zval *metadata, php_uint32 zip_metadata_len);
+int phar_parse_metadata(char **buffer, zval *metadata, uint32_t zip_metadata_len);
void destroy_phar_manifest_entry(zval *zv);
int phar_seek_efp(phar_entry_info *entry, zend_off_t offset, int whence, zend_off_t position, int follow_links);
php_stream *phar_get_efp(phar_entry_info *entry, int follow_links);
@@ -580,7 +565,7 @@ int phar_copy_on_write(phar_archive_data **pphar);
/* tar functions in tar.c */
int phar_is_tar(char *buf, char *fname);
-int phar_parse_tarfile(php_stream* fp, char *fname, int fname_len, char *alias, int alias_len, phar_archive_data** pphar, int is_data, php_uint32 compression, char **error);
+int phar_parse_tarfile(php_stream* fp, char *fname, int fname_len, char *alias, int alias_len, phar_archive_data** pphar, int is_data, uint32_t compression, char **error);
int phar_open_or_create_tar(char *fname, int fname_len, char *alias, int alias_len, int is_data, int options, phar_archive_data** pphar, char **error);
int phar_tar_flush(phar_archive_data *phar, char *user_stub, zend_long len, int defaultstub, char **error);
diff --git a/ext/phar/phar_object.c b/ext/phar/phar_object.c
index 7c9c3351aa..ebacc88db1 100644
--- a/ext/phar/phar_object.c
+++ b/ext/phar/phar_object.c
@@ -25,16 +25,7 @@
static zend_class_entry *phar_ce_archive;
static zend_class_entry *phar_ce_data;
static zend_class_entry *phar_ce_PharException;
-
-#if HAVE_SPL
static zend_class_entry *phar_ce_entry;
-#endif
-
-#if PHP_VERSION_ID >= 50300
-# define PHAR_ARG_INFO
-#else
-# define PHAR_ARG_INFO static
-#endif
static int phar_file_type(HashTable *mimes, char *file, char **mime_type) /* {{{ */
{
@@ -1071,7 +1062,6 @@ PHP_METHOD(Phar, isValidPharFilename)
}
/* }}} */
-#if HAVE_SPL
/**
* from spl_directory
*/
@@ -1104,7 +1094,6 @@ static spl_other_handler phar_spl_foreign_handler = {
phar_spl_foreign_dtor,
phar_spl_foreign_clone
};
-#endif /* HAVE_SPL */
/* {{{ proto void Phar::__construct(string fname [, int flags [, string alias]])
* Construct a Phar archive object
@@ -1117,9 +1106,6 @@ static spl_other_handler phar_spl_foreign_handler = {
*/
PHP_METHOD(Phar, __construct)
{
-#if !HAVE_SPL
- zend_throw_exception_ex(zend_ce_exception, 0, "Cannot instantiate Phar object without SPL extension");
-#else
char *fname, *alias = NULL, *error, *arch = NULL, *entry = NULL, *save_fname;
size_t fname_len, alias_len = 0;
int arch_len, entry_len, is_data;
@@ -1244,7 +1230,6 @@ PHP_METHOD(Phar, __construct)
phar_obj->spl.info_class = phar_ce_entry;
efree(fname);
-#endif /* HAVE_SPL */
}
/* }}} */
@@ -1363,8 +1348,6 @@ PHP_METHOD(Phar, unlinkArchive)
}
/* }}} */
-#if HAVE_SPL
-
#define PHAR_ARCHIVE_OBJECT() \
zval *zobj = getThis(); \
phar_archive_object *phar_obj = (phar_archive_object*)((char*)Z_OBJ_P(zobj) - Z_OBJ_P(zobj)->handlers->offset); \
@@ -1588,21 +1571,6 @@ phar_spl_fileinfo:
return ZEND_HASH_APPLY_STOP;
}
}
-#if PHP_API_VERSION < 20100412
- if (PG(safe_mode) && (!php_checkuid(fname, NULL, CHECKUID_ALLOW_ONLY_FILE))) {
- zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0, "Iterator %v returned a path \"%s\" that safe mode prevents opening", ZSTR_VAL(ce->name), fname);
-
- if (save) {
- efree(save);
- }
-
- if (temp) {
- efree(temp);
- }
-
- return ZEND_HASH_APPLY_STOP;
- }
-#endif
if (php_check_open_basedir(fname)) {
zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0, "Iterator %v returned a path \"%s\" that open_basedir prevents opening", ZSTR_VAL(ce->name), fname);
@@ -2196,7 +2164,7 @@ its_ok:
}
/* }}} */
-static zend_object *phar_convert_to_other(phar_archive_data *source, int convert, char *ext, php_uint32 flags) /* {{{ */
+static zend_object *phar_convert_to_other(phar_archive_data *source, int convert, char *ext, uint32_t flags) /* {{{ */
{
phar_archive_data *phar;
phar_entry_info *entry, newentry;
@@ -2320,7 +2288,7 @@ PHP_METHOD(Phar, convertToExecutable)
char *ext = NULL;
int is_data;
size_t ext_len = 0;
- php_uint32 flags;
+ uint32_t flags;
zend_object *ret;
/* a number that is not 0, 1 or 2 (Which is also Greg's birthday, so there) */
zend_long format = 9021976, method = 9021976;
@@ -2424,7 +2392,7 @@ PHP_METHOD(Phar, convertToData)
char *ext = NULL;
int is_data;
size_t ext_len = 0;
- php_uint32 flags;
+ uint32_t flags;
zend_object *ret;
/* a number that is not 0, 1 or 2 (Which is also Greg's birthday so there) */
zend_long format = 9021976, method = 9021976;
@@ -3098,7 +3066,7 @@ PHP_METHOD(Phar, getModified)
static int phar_set_compression(zval *zv, void *argument) /* {{{ */
{
phar_entry_info *entry = (phar_entry_info *)Z_PTR_P(zv);
- php_uint32 compress = *(php_uint32 *)argument;
+ uint32_t compress = *(uint32_t *)argument;
if (entry->is_deleted) {
return ZEND_HASH_APPLY_KEEP;
@@ -3136,7 +3104,7 @@ static int phar_test_compression(zval *zv, void *argument) /* {{{ */
}
/* }}} */
-static void pharobj_set_compression(HashTable *manifest, php_uint32 compress) /* {{{ */
+static void pharobj_set_compression(HashTable *manifest, uint32_t compress) /* {{{ */
{
zend_hash_apply_with_argument(manifest, phar_set_compression, &compress);
}
@@ -3162,7 +3130,7 @@ PHP_METHOD(Phar, compress)
zend_long method;
char *ext = NULL;
size_t ext_len = 0;
- php_uint32 flags;
+ uint32_t flags;
zend_object *ret;
PHAR_ARCHIVE_OBJECT();
@@ -3271,7 +3239,7 @@ PHP_METHOD(Phar, decompress)
PHP_METHOD(Phar, compressFiles)
{
char *error;
- php_uint32 flags;
+ uint32_t flags;
zend_long method;
PHAR_ARCHIVE_OBJECT();
@@ -3800,13 +3768,6 @@ PHP_METHOD(Phar, addFile)
return;
}
-#if PHP_API_VERSION < 20100412
- if (PG(safe_mode) && (!php_checkuid(fname, NULL, CHECKUID_ALLOW_ONLY_FILE))) {
- zend_throw_exception_ex(spl_ce_RuntimeException, 0, "phar error: unable to open file \"%s\" to add to phar archive, safe_mode restrictions prevent this", fname);
- return;
- }
-#endif
-
if (!strstr(fname, "://") && php_check_open_basedir(fname)) {
zend_throw_exception_ex(spl_ce_RuntimeException, 0, "phar error: unable to open file \"%s\" to add to phar archive, open_basedir restrictions prevent this", fname);
return;
@@ -4052,13 +4013,6 @@ PHP_METHOD(Phar, delMetadata)
}
}
/* }}} */
-#if PHP_API_VERSION < 20100412
-#define PHAR_OPENBASEDIR_CHECKPATH(filename) \
- (PG(safe_mode) && (!php_checkuid(filename, NULL, CHECKUID_CHECK_FILE_AND_DIR))) || php_check_open_basedir(filename)
-#else
-#define PHAR_OPENBASEDIR_CHECKPATH(filename) \
- php_check_open_basedir(filename)
-#endif
static int phar_extract_file(zend_bool overwrite, phar_entry_info *entry, char *dest, int dest_len, char **error) /* {{{ */
{
@@ -4137,7 +4091,7 @@ static int phar_extract_file(zend_bool overwrite, phar_entry_info *entry, char *
return FAILURE;
}
- if (PHAR_OPENBASEDIR_CHECKPATH(fullpath)) {
+ if (php_check_open_basedir(fullpath)) {
spprintf(error, 4096, "Cannot extract \"%s\" to \"%s\", openbasedir/safe mode restrictions in effect", entry->filename, fullpath);
efree(fullpath);
efree(new_state.cwd);
@@ -4193,11 +4147,7 @@ static int phar_extract_file(zend_bool overwrite, phar_entry_info *entry, char *
return SUCCESS;
}
-#if PHP_API_VERSION < 20100412
- fp = php_stream_open_wrapper(fullpath, "w+b", REPORT_ERRORS|ENFORCE_SAFE_MODE, NULL);
-#else
fp = php_stream_open_wrapper(fullpath, "w+b", REPORT_ERRORS, NULL);
-#endif
if (!fp) {
spprintf(error, 4096, "Cannot extract \"%s\", could not open for writing \"%s\"", entry->filename, fullpath);
@@ -5043,10 +4993,7 @@ PHP_METHOD(PharFileInfo, decompress)
}
/* }}} */
-#endif /* HAVE_SPL */
-
/* {{{ phar methods */
-PHAR_ARG_INFO
ZEND_BEGIN_ARG_INFO_EX(arginfo_phar___construct, 0, 0, 1)
ZEND_ARG_INFO(0, filename)
ZEND_ARG_INFO(0, flags)
@@ -5054,47 +5001,39 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_phar___construct, 0, 0, 1)
ZEND_ARG_INFO(0, fileformat)
ZEND_END_ARG_INFO()
-PHAR_ARG_INFO
ZEND_BEGIN_ARG_INFO_EX(arginfo_phar_createDS, 0, 0, 0)
ZEND_ARG_INFO(0, index)
ZEND_ARG_INFO(0, webindex)
ZEND_END_ARG_INFO()
-PHAR_ARG_INFO
ZEND_BEGIN_ARG_INFO_EX(arginfo_phar_cancompress, 0, 0, 0)
ZEND_ARG_INFO(0, method)
ZEND_END_ARG_INFO()
-PHAR_ARG_INFO
ZEND_BEGIN_ARG_INFO_EX(arginfo_phar_isvalidpharfilename, 0, 0, 1)
ZEND_ARG_INFO(0, filename)
ZEND_ARG_INFO(0, executable)
ZEND_END_ARG_INFO()
-PHAR_ARG_INFO
ZEND_BEGIN_ARG_INFO_EX(arginfo_phar_loadPhar, 0, 0, 1)
ZEND_ARG_INFO(0, filename)
ZEND_ARG_INFO(0, alias)
ZEND_END_ARG_INFO()
-PHAR_ARG_INFO
ZEND_BEGIN_ARG_INFO_EX(arginfo_phar_mapPhar, 0, 0, 0)
ZEND_ARG_INFO(0, alias)
ZEND_ARG_INFO(0, offset)
ZEND_END_ARG_INFO()
-PHAR_ARG_INFO
ZEND_BEGIN_ARG_INFO_EX(arginfo_phar_mount, 0, 0, 2)
ZEND_ARG_INFO(0, inphar)
ZEND_ARG_INFO(0, externalfile)
ZEND_END_ARG_INFO()
-PHAR_ARG_INFO
ZEND_BEGIN_ARG_INFO_EX(arginfo_phar_mungServer, 0, 0, 1)
ZEND_ARG_INFO(0, munglist)
ZEND_END_ARG_INFO()
-PHAR_ARG_INFO
ZEND_BEGIN_ARG_INFO_EX(arginfo_phar_webPhar, 0, 0, 0)
ZEND_ARG_INFO(0, alias)
ZEND_ARG_INFO(0, index)
@@ -5103,141 +5042,112 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_phar_webPhar, 0, 0, 0)
ZEND_ARG_INFO(0, rewrites)
ZEND_END_ARG_INFO()
-PHAR_ARG_INFO
ZEND_BEGIN_ARG_INFO_EX(arginfo_phar_running, 0, 0, 1)
ZEND_ARG_INFO(0, retphar)
ZEND_END_ARG_INFO()
-PHAR_ARG_INFO
ZEND_BEGIN_ARG_INFO_EX(arginfo_phar_ua, 0, 0, 1)
ZEND_ARG_INFO(0, archive)
ZEND_END_ARG_INFO()
-#if HAVE_SPL
-PHAR_ARG_INFO
ZEND_BEGIN_ARG_INFO_EX(arginfo_phar_build, 0, 0, 1)
ZEND_ARG_INFO(0, iterator)
ZEND_ARG_INFO(0, base_directory)
ZEND_END_ARG_INFO()
-PHAR_ARG_INFO
ZEND_BEGIN_ARG_INFO_EX(arginfo_phar_conv, 0, 0, 0)
ZEND_ARG_INFO(0, format)
ZEND_ARG_INFO(0, compression_type)
ZEND_ARG_INFO(0, file_ext)
ZEND_END_ARG_INFO()
-PHAR_ARG_INFO
ZEND_BEGIN_ARG_INFO_EX(arginfo_phar_comps, 0, 0, 1)
ZEND_ARG_INFO(0, compression_type)
ZEND_ARG_INFO(0, file_ext)
ZEND_END_ARG_INFO()
-PHAR_ARG_INFO
ZEND_BEGIN_ARG_INFO_EX(arginfo_phar_decomp, 0, 0, 0)
ZEND_ARG_INFO(0, file_ext)
ZEND_END_ARG_INFO()
-PHAR_ARG_INFO
ZEND_BEGIN_ARG_INFO_EX(arginfo_phar_comp, 0, 0, 1)
ZEND_ARG_INFO(0, compression_type)
ZEND_END_ARG_INFO()
-PHAR_ARG_INFO
ZEND_BEGIN_ARG_INFO_EX(arginfo_phar_compo, 0, 0, 0)
ZEND_ARG_INFO(0, compression_type)
ZEND_END_ARG_INFO()
-PHAR_ARG_INFO
ZEND_BEGIN_ARG_INFO_EX(arginfo_phar_copy, 0, 0, 2)
ZEND_ARG_INFO(0, newfile)
ZEND_ARG_INFO(0, oldfile)
ZEND_END_ARG_INFO()
-PHAR_ARG_INFO
ZEND_BEGIN_ARG_INFO_EX(arginfo_phar_delete, 0, 0, 1)
ZEND_ARG_INFO(0, entry)
ZEND_END_ARG_INFO()
-PHAR_ARG_INFO
ZEND_BEGIN_ARG_INFO_EX(arginfo_phar_fromdir, 0, 0, 1)
ZEND_ARG_INFO(0, base_dir)
ZEND_ARG_INFO(0, regex)
ZEND_END_ARG_INFO()
-PHAR_ARG_INFO
ZEND_BEGIN_ARG_INFO_EX(arginfo_phar_offsetExists, 0, 0, 1)
ZEND_ARG_INFO(0, entry)
ZEND_END_ARG_INFO()
-PHAR_ARG_INFO
ZEND_BEGIN_ARG_INFO_EX(arginfo_phar_offsetSet, 0, 0, 2)
ZEND_ARG_INFO(0, entry)
ZEND_ARG_INFO(0, value)
ZEND_END_ARG_INFO()
-PHAR_ARG_INFO
ZEND_BEGIN_ARG_INFO_EX(arginfo_phar_setAlias, 0, 0, 1)
ZEND_ARG_INFO(0, alias)
ZEND_END_ARG_INFO()
-PHAR_ARG_INFO
ZEND_BEGIN_ARG_INFO_EX(arginfo_phar_setMetadata, 0, 0, 1)
ZEND_ARG_INFO(0, metadata)
ZEND_END_ARG_INFO()
-PHAR_ARG_INFO
ZEND_BEGIN_ARG_INFO_EX(arginfo_phar_setSigAlgo, 0, 0, 1)
ZEND_ARG_INFO(0, algorithm)
ZEND_ARG_INFO(0, privatekey)
ZEND_END_ARG_INFO()
-PHAR_ARG_INFO
ZEND_BEGIN_ARG_INFO_EX(arginfo_phar_setStub, 0, 0, 1)
ZEND_ARG_INFO(0, newstub)
ZEND_ARG_INFO(0, maxlen)
ZEND_END_ARG_INFO()
-PHAR_ARG_INFO
ZEND_BEGIN_ARG_INFO_EX(arginfo_phar_emptydir, 0, 0, 0)
ZEND_ARG_INFO(0, dirname)
ZEND_END_ARG_INFO()
-PHAR_ARG_INFO
ZEND_BEGIN_ARG_INFO_EX(arginfo_phar_extract, 0, 0, 1)
ZEND_ARG_INFO(0, pathto)
ZEND_ARG_INFO(0, files)
ZEND_ARG_INFO(0, overwrite)
ZEND_END_ARG_INFO()
-PHAR_ARG_INFO
ZEND_BEGIN_ARG_INFO_EX(arginfo_phar_addfile, 0, 0, 1)
ZEND_ARG_INFO(0, filename)
ZEND_ARG_INFO(0, localname)
ZEND_END_ARG_INFO()
-PHAR_ARG_INFO
ZEND_BEGIN_ARG_INFO_EX(arginfo_phar_fromstring, 0, 0, 1)
ZEND_ARG_INFO(0, localname)
ZEND_ARG_INFO(0, contents)
ZEND_END_ARG_INFO()
-PHAR_ARG_INFO
ZEND_BEGIN_ARG_INFO_EX(arginfo_phar_isff, 0, 0, 1)
ZEND_ARG_INFO(0, fileformat)
ZEND_END_ARG_INFO()
-PHAR_ARG_INFO
ZEND_BEGIN_ARG_INFO(arginfo_phar__void, 0)
ZEND_END_ARG_INFO()
-#endif /* HAVE_SPL */
-
zend_function_entry php_archive_methods[] = {
-#if !HAVE_SPL
- PHP_ME(Phar, __construct, arginfo_phar___construct, ZEND_ACC_PRIVATE)
-#else
PHP_ME(Phar, __construct, arginfo_phar___construct, ZEND_ACC_PUBLIC)
PHP_ME(Phar, __destruct, arginfo_phar__void, ZEND_ACC_PUBLIC)
PHP_ME(Phar, addEmptyDir, arginfo_phar_emptydir, ZEND_ACC_PUBLIC)
@@ -5279,7 +5189,6 @@ zend_function_entry php_archive_methods[] = {
PHP_ME(Phar, setStub, arginfo_phar_setStub, ZEND_ACC_PUBLIC)
PHP_ME(Phar, startBuffering, arginfo_phar__void, ZEND_ACC_PUBLIC)
PHP_ME(Phar, stopBuffering, arginfo_phar__void, ZEND_ACC_PUBLIC)
-#endif
/* static member functions */
PHP_ME(Phar, apiVersion, arginfo_phar__void, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC|ZEND_ACC_FINAL)
PHP_ME(Phar, canCompress, arginfo_phar_cancompress, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC|ZEND_ACC_FINAL)
@@ -5299,13 +5208,10 @@ zend_function_entry php_archive_methods[] = {
PHP_FE_END
};
-#if HAVE_SPL
-PHAR_ARG_INFO
ZEND_BEGIN_ARG_INFO_EX(arginfo_entry___construct, 0, 0, 1)
ZEND_ARG_INFO(0, filename)
ZEND_END_ARG_INFO()
-PHAR_ARG_INFO
ZEND_BEGIN_ARG_INFO_EX(arginfo_entry_chmod, 0, 0, 1)
ZEND_ARG_INFO(0, perms)
ZEND_END_ARG_INFO()
@@ -5328,7 +5234,6 @@ zend_function_entry php_entry_methods[] = {
PHP_ME(PharFileInfo, setMetadata, arginfo_phar_setMetadata, ZEND_ACC_PUBLIC)
PHP_FE_END
};
-#endif /* HAVE_SPL */
zend_function_entry phar_exception_methods[] = {
PHP_FE_END
@@ -5345,7 +5250,6 @@ void phar_object_init(void) /* {{{ */
INIT_CLASS_ENTRY(ce, "PharException", phar_exception_methods);
phar_ce_PharException = zend_register_internal_class_ex(&ce, zend_ce_exception);
-#if HAVE_SPL
INIT_CLASS_ENTRY(ce, "Phar", php_archive_methods);
phar_ce_archive = zend_register_internal_class_ex(&ce, spl_ce_RecursiveDirectoryIterator);
@@ -5358,15 +5262,6 @@ void phar_object_init(void) /* {{{ */
INIT_CLASS_ENTRY(ce, "PharFileInfo", php_entry_methods);
phar_ce_entry = zend_register_internal_class_ex(&ce, spl_ce_SplFileInfo);
-#else
- INIT_CLASS_ENTRY(ce, "Phar", php_archive_methods);
- phar_ce_archive = zend_register_internal_class(&ce);
- phar_ce_archive->ce_flags |= ZEND_ACC_FINAL;
-
- INIT_CLASS_ENTRY(ce, "PharData", php_archive_methods);
- phar_ce_data = zend_register_internal_class(&ce);
- phar_ce_data->ce_flags |= ZEND_ACC_FINAL;
-#endif
REGISTER_PHAR_CLASS_CONST_LONG(phar_ce_archive, "BZ2", PHAR_ENT_COMPRESSED_BZ2)
REGISTER_PHAR_CLASS_CONST_LONG(phar_ce_archive, "GZ", PHAR_ENT_COMPRESSED_GZ)
diff --git a/ext/phar/tar.c b/ext/phar/tar.c
index aeb5c7ef1e..e40739726b 100644
--- a/ext/phar/tar.c
+++ b/ext/phar/tar.c
@@ -19,9 +19,9 @@
#include "phar_internal.h"
-static php_uint32 phar_tar_number(char *buf, int len) /* {{{ */
+static uint32_t phar_tar_number(char *buf, int len) /* {{{ */
{
- php_uint32 num = 0;
+ uint32_t num = 0;
int i = 0;
while (i < len && buf[i] == ' ') {
@@ -62,7 +62,7 @@ static php_uint32 phar_tar_number(char *buf, int len) /* {{{ */
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-static int phar_tar_octal(char *buf, php_uint32 val, int len) /* {{{ */
+static int phar_tar_octal(char *buf, uint32_t val, int len) /* {{{ */
{
char *p = buf;
int s = len;
@@ -84,9 +84,9 @@ static int phar_tar_octal(char *buf, php_uint32 val, int len) /* {{{ */
}
/* }}} */
-static php_uint32 phar_tar_checksum(char *buf, int len) /* {{{ */
+static uint32_t phar_tar_checksum(char *buf, int len) /* {{{ */
{
- php_uint32 sum = 0;
+ uint32_t sum = 0;
char *end = buf + len;
while (buf != end) {
@@ -100,8 +100,8 @@ static php_uint32 phar_tar_checksum(char *buf, int len) /* {{{ */
int phar_is_tar(char *buf, char *fname) /* {{{ */
{
tar_header *header = (tar_header *) buf;
- php_uint32 checksum = phar_tar_number(header->checksum, sizeof(header->checksum));
- php_uint32 ret;
+ uint32_t checksum = phar_tar_number(header->checksum, sizeof(header->checksum));
+ uint32_t ret;
char save[sizeof(header->checksum)], *bname;
/* assume that the first filename in a tar won't begin with <?php */
@@ -202,13 +202,13 @@ static size_t strnlen(const char *s, size_t maxlen) {
}
#endif
-int phar_parse_tarfile(php_stream* fp, char *fname, int fname_len, char *alias, int alias_len, phar_archive_data** pphar, int is_data, php_uint32 compression, char **error) /* {{{ */
+int phar_parse_tarfile(php_stream* fp, char *fname, int fname_len, char *alias, int alias_len, phar_archive_data** pphar, int is_data, uint32_t compression, char **error) /* {{{ */
{
char buf[512], *actual_alias = NULL, *p;
phar_entry_info entry = {0};
size_t pos = 0, read, totalsize;
tar_header *hdr;
- php_uint32 sum1, sum2, size, old;
+ uint32_t sum1, sum2, size, old;
phar_archive_data *myphar, *actual;
int last_was_longlink = 0;
int linkname_len;
@@ -299,7 +299,7 @@ bail:
| ((((unsigned char*)(buffer))[1]) << 8) \
| (((unsigned char*)(buffer))[0]))
#else
-# define PHAR_GET_32(buffer) (php_uint32) *(buffer)
+# define PHAR_GET_32(buffer) (uint32_t) *(buffer)
#endif
myphar->sig_flags = PHAR_GET_32(buf);
if (FAILURE == phar_verify_signature(fp, php_stream_tell(fp) - size - 512, myphar->sig_flags, buf + 8, size - 8, fname, &myphar->signature, &myphar->sig_len, error)) {
@@ -1230,12 +1230,12 @@ nostub:
}
#ifdef WORDS_BIGENDIAN
# define PHAR_SET_32(var, buffer) \
- *(php_uint32 *)(var) = (((((unsigned char*)&(buffer))[3]) << 24) \
+ *(uint32_t *)(var) = (((((unsigned char*)&(buffer))[3]) << 24) \
| ((((unsigned char*)&(buffer))[2]) << 16) \
| ((((unsigned char*)&(buffer))[1]) << 8) \
| (((unsigned char*)&(buffer))[0]))
#else
-# define PHAR_SET_32(var, buffer) *(php_uint32 *)(var) = (php_uint32) (buffer)
+# define PHAR_SET_32(var, buffer) *(uint32_t *)(var) = (uint32_t) (buffer)
#endif
PHAR_SET_32(sigbuf, phar->sig_flags);
PHAR_SET_32(sigbuf + 4, signature_length);
diff --git a/ext/phar/util.c b/ext/phar/util.c
index eca33efc38..39ecf399ae 100644
--- a/ext/phar/util.c
+++ b/ext/phar/util.c
@@ -199,13 +199,6 @@ int phar_mount_entry(phar_archive_data *phar, char *filename, int filename_len,
entry.tmp = estrndup(filename, filename_len);
}
}
-#if PHP_API_VERSION < 20100412
- if (PG(safe_mode) && !is_phar && (!php_checkuid(entry.tmp, NULL, CHECKUID_CHECK_FILE_AND_DIR))) {
- efree(entry.tmp);
- efree(entry.filename);
- return FAILURE;
- }
-#endif
filename = entry.tmp;
/* only check openbasedir for files, not for phar streams */
@@ -1184,7 +1177,7 @@ char * phar_compress_filter(phar_entry_info * entry, int return_unknown) /* {{{
*/
char * phar_decompress_filter(phar_entry_info * entry, int return_unknown) /* {{{ */
{
- php_uint32 flags;
+ uint32_t flags;
if (entry->is_modified) {
flags = entry->old_flags;
@@ -1480,7 +1473,7 @@ static int phar_call_openssl_signverify(int is_sign, php_stream *fp, zend_off_t
/* }}} */
#endif /* #ifndef PHAR_HAVE_OPENSSL */
-int phar_verify_signature(php_stream *fp, size_t end_of_phar, php_uint32 sig_type, char *sig, int sig_len, char *fname, char **signature, int *signature_len, char **error) /* {{{ */
+int phar_verify_signature(php_stream *fp, size_t end_of_phar, uint32_t sig_type, char *sig, int sig_len, char *fname, char **signature, int *signature_len, char **error) /* {{{ */
{
int read_size, len;
zend_off_t read_len;
diff --git a/ext/phar/zip.c b/ext/phar/zip.c
index 764f2b133d..30c52c0479 100644
--- a/ext/phar/zip.c
+++ b/ext/phar/zip.c
@@ -18,28 +18,28 @@
#include "phar_internal.h"
-#define PHAR_GET_16(var) ((php_uint16)((((php_uint16)var[0]) & 0xff) | \
- (((php_uint16)var[1]) & 0xff) << 8))
-#define PHAR_GET_32(var) ((php_uint32)((((php_uint32)var[0]) & 0xff) | \
- (((php_uint32)var[1]) & 0xff) << 8 | \
- (((php_uint32)var[2]) & 0xff) << 16 | \
- (((php_uint32)var[3]) & 0xff) << 24))
-static inline void phar_write_32(char buffer[4], php_uint32 value)
+#define PHAR_GET_16(var) ((uint16_t)((((uint16_t)var[0]) & 0xff) | \
+ (((uint16_t)var[1]) & 0xff) << 8))
+#define PHAR_GET_32(var) ((uint32_t)((((uint32_t)var[0]) & 0xff) | \
+ (((uint32_t)var[1]) & 0xff) << 8 | \
+ (((uint32_t)var[2]) & 0xff) << 16 | \
+ (((uint32_t)var[3]) & 0xff) << 24))
+static inline void phar_write_32(char buffer[4], uint32_t value)
{
buffer[3] = (unsigned char) ((value & 0xff000000) >> 24);
buffer[2] = (unsigned char) ((value & 0xff0000) >> 16);
buffer[1] = (unsigned char) ((value & 0xff00) >> 8);
buffer[0] = (unsigned char) (value & 0xff);
}
-static inline void phar_write_16(char buffer[2], php_uint32 value)
+static inline void phar_write_16(char buffer[2], uint32_t value)
{
buffer[1] = (unsigned char) ((value & 0xff00) >> 8);
buffer[0] = (unsigned char) (value & 0xff);
}
-# define PHAR_SET_32(var, value) phar_write_32(var, (php_uint32) (value));
-# define PHAR_SET_16(var, value) phar_write_16(var, (php_uint16) (value));
+# define PHAR_SET_32(var, value) phar_write_32(var, (uint32_t) (value));
+# define PHAR_SET_16(var, value) phar_write_16(var, (uint16_t) (value));
-static int phar_zip_process_extra(php_stream *fp, phar_entry_info *entry, php_uint16 len) /* {{{ */
+static int phar_zip_process_extra(php_stream *fp, phar_entry_info *entry, uint16_t len) /* {{{ */
{
union {
phar_zip_extra_field_header header;
@@ -143,7 +143,7 @@ static time_t phar_zip_d2u_time(char *cdtime, char *cddate) /* {{{ */
static void phar_zip_u2d_time(time_t time, char *dtime, char *ddate) /* {{{ */
{
- php_uint16 ctime, cdate;
+ uint16_t ctime, cdate;
struct tm *tm, tmbuf;
tm = php_localtime_r(&time, &tmbuf);
@@ -168,7 +168,7 @@ int phar_parse_zipfile(php_stream *fp, char *fname, int fname_len, char *alias,
phar_zip_dir_end locator;
char buf[sizeof(locator) + 65536];
zend_long size;
- php_uint16 i;
+ uint16_t i;
phar_archive_data *mydata = NULL;
phar_entry_info entry = {0};
char *p = buf, *ext, *actual_alias = NULL;
@@ -791,7 +791,7 @@ static int phar_zip_changed_apply_int(phar_entry_info *entry, void *arg) /* {{{
phar_zip_unix3 perms;
phar_zip_central_dir_file central;
struct _phar_zip_pass *p;
- php_uint32 newcrc32;
+ uint32_t newcrc32;
zend_off_t offset;
int not_really_modified = 0;
p = (struct _phar_zip_pass*) arg;
@@ -822,7 +822,7 @@ static int phar_zip_changed_apply_int(phar_entry_info *entry, void *arg) /* {{{
PHAR_SET_16(perms.size, sizeof(perms) - 4);
PHAR_SET_16(perms.perms, entry->flags & PHAR_ENT_PERM_MASK);
{
- php_uint32 crc = (php_uint32) ~0;
+ uint32_t crc = (uint32_t) ~0;
CRC32(crc, perms.perms[0]);
CRC32(crc, perms.perms[1]);
PHAR_SET_32(perms.crc32, ~crc);
@@ -848,7 +848,7 @@ static int phar_zip_changed_apply_int(phar_entry_info *entry, void *arg) /* {{{
/* do extra field for perms later */
if (entry->is_modified) {
- php_uint32 loc;
+ uint32_t loc;
php_stream_filter *filter;
php_stream *efp;
@@ -936,7 +936,7 @@ static int phar_zip_changed_apply_int(phar_entry_info *entry, void *arg) /* {{{
php_stream_flush(entry->cfp);
php_stream_filter_remove(filter, 1);
php_stream_seek(entry->cfp, 0, SEEK_END);
- entry->compressed_filesize = (php_uint32) php_stream_tell(entry->cfp);
+ entry->compressed_filesize = (uint32_t) php_stream_tell(entry->cfp);
PHAR_SET_32(central.compsize, entry->compressed_filesize);
PHAR_SET_32(local.compsize, entry->compressed_filesize);
/* generate crc on compressed file */
@@ -1194,7 +1194,7 @@ int phar_zip_flush(phar_archive_data *phar, char *user_stub, zend_long len, int
char *temperr = NULL;
struct _phar_zip_pass pass;
phar_zip_dir_end eocd;
- php_uint32 cdir_size, cdir_offset;
+ uint32_t cdir_size, cdir_offset;
pass.error = &temperr;
entry.flags = PHAR_ENT_PERM_DEF_FILE;
diff --git a/ext/readline/config.w32 b/ext/readline/config.w32
new file mode 100644
index 0000000000..482c99cc04
--- /dev/null
+++ b/ext/readline/config.w32
@@ -0,0 +1,16 @@
+// $Id$
+// vim:ft=javascript
+
+ARG_WITH("readline", "Readline support", "yes");
+
+if (PHP_READLINE != "no") {
+ if (CHECK_LIB("edit_a.lib;edit.lib", "readline", PHP_READLINE) &&
+ CHECK_HEADER_ADD_INCLUDE("editline/readline.h", "CFLAGS_READLINE")) {
+ EXTENSION("readline", "readline.c readline_cli.c");
+ ADD_FLAG("CFLAGS_READLINE", "/D HAVE_LIBEDIT");
+ ADD_FLAG("CFLAGS_READLINE", "/D HAVE_RL_COMPLETION_MATCHES");
+ } else {
+ WARNING("readline not enabled; libraries and headers not found");
+ }
+}
+
diff --git a/ext/readline/php_readline.h b/ext/readline/php_readline.h
index 52dabdadeb..e3a0aba676 100644
--- a/ext/readline/php_readline.h
+++ b/ext/readline/php_readline.h
@@ -22,9 +22,11 @@
#define PHP_READLINE_H
#if HAVE_LIBREADLINE || HAVE_LIBEDIT
+#ifndef PHP_WIN32
#ifdef ZTS
#warning Readline module will *NEVER* be thread-safe
#endif
+#endif
extern zend_module_entry readline_module_entry;
#define phpext_readline_ptr &readline_module_entry
diff --git a/ext/readline/readline.c b/ext/readline/readline.c
index ad9b01a946..0e6f849444 100644
--- a/ext/readline/readline.c
+++ b/ext/readline/readline.c
@@ -251,7 +251,9 @@ PHP_FUNCTION(readline_info)
array_init(return_value);
add_assoc_string(return_value,"line_buffer",SAFE_STRING(rl_line_buffer));
add_assoc_long(return_value,"point",rl_point);
+#ifndef PHP_WIN32
add_assoc_long(return_value,"end",rl_end);
+#endif
#ifdef HAVE_LIBREADLINE
add_assoc_long(return_value,"mark",rl_mark);
add_assoc_long(return_value,"done",rl_done);
@@ -262,7 +264,9 @@ PHP_FUNCTION(readline_info)
#if HAVE_ERASE_EMPTY_LINE
add_assoc_long(return_value,"erase_empty_line",rl_erase_empty_line);
#endif
+#ifndef PHP_WIN32
add_assoc_string(return_value,"library_version",(char *)SAFE_STRING(rl_library_version));
+#endif
add_assoc_string(return_value,"readline_name",(char *)SAFE_STRING(rl_readline_name));
add_assoc_long(return_value,"attempted_completion_over",rl_attempted_completion_over);
} else {
@@ -276,8 +280,10 @@ PHP_FUNCTION(readline_info)
RETVAL_STRING(SAFE_STRING(oldstr));
} else if (!strcasecmp(what, "point")) {
RETVAL_LONG(rl_point);
+#ifndef PHP_WIN32
} else if (!strcasecmp(what, "end")) {
RETVAL_LONG(rl_end);
+#endif
#ifdef HAVE_LIBREADLINE
} else if (!strcasecmp(what, "mark")) {
RETVAL_LONG(rl_mark);
@@ -309,8 +315,10 @@ PHP_FUNCTION(readline_info)
}
RETVAL_LONG(oldval);
#endif
+#ifndef PHP_WIN32
} else if (!strcasecmp(what,"library_version")) {
RETVAL_STRING((char *)SAFE_STRING(rl_library_version));
+#endif
} else if (!strcasecmp(what, "readline_name")) {
oldstr = (char*)rl_readline_name;
if (value) {
diff --git a/ext/readline/readline_cli.c b/ext/readline/readline_cli.c
index a90b1387df..2cce2292f4 100644
--- a/ext/readline/readline_cli.c
+++ b/ext/readline/readline_cli.c
@@ -67,7 +67,7 @@
#include "sapi/cli/cli.h"
#include "readline_cli.h"
-#ifdef COMPILE_DL_READLINE
+#if defined(COMPILE_DL_READLINE) && !defined(PHP_WIN32)
#include <dlfcn.h>
#endif
@@ -603,9 +603,15 @@ static int readline_shell_run(void) /* {{{ */
zend_execute_scripts(ZEND_REQUIRE, NULL, 1, prepend_file_p);
}
+#ifndef PHP_WIN32
history_file = tilde_expand("~/.php_history");
+#else
+ spprintf(&history_file, MAX_PATH, "%s/.php_history", getenv("USERPROFILE"));
+#endif
rl_attempted_completion_function = cli_code_completion;
+#ifndef PHP_WIN32
rl_special_prefixes = "$";
+#endif
read_history(history_file);
EG(exit_status) = 0;
@@ -691,13 +697,33 @@ static int readline_shell_run(void) /* {{{ */
php_last_char = '\0';
}
+#ifdef PHP_WIN32
+ efree(history_file);
+#else
free(history_file);
+#endif
efree(code);
zend_string_release(prompt);
return EG(exit_status);
}
/* }}} */
+#ifdef PHP_WIN32
+typedef cli_shell_callbacks_t *(__cdecl *get_cli_shell_callbacks)(void);
+#define GET_SHELL_CB(cb) \
+ do { \
+ get_cli_shell_callbacks get_callbacks; \
+ HMODULE hMod = GetModuleHandle("php.exe"); \
+ (cb) = NULL; \
+ if (strlen(sapi_module.name) >= 3 && 0 == strncmp("cli", sapi_module.name, 3)) { \
+ get_callbacks = (get_cli_shell_callbacks)GetProcAddress(hMod, "php_cli_get_shell_callbacks"); \
+ if (get_callbacks) { \
+ (cb) = get_callbacks(); \
+ } \
+ } \
+ } while(0)
+
+#else
/*
#ifdef COMPILE_DL_READLINE
This dlsym() is always used as even the CGI SAPI is linked against "CLI"-only
@@ -716,6 +742,7 @@ this extension sharedto offer compatibility.
/*#else
#define GET_SHELL_CB(cb) (cb) = php_cli_get_shell_callbacks()
#endif*/
+#endif
PHP_MINIT_FUNCTION(cli_readline)
{
@@ -760,7 +787,11 @@ PHP_MINFO_FUNCTION(cli_readline)
{
php_info_print_table_start();
php_info_print_table_header(2, "Readline Support", "enabled");
+#ifdef PHP_WIN32
+ php_info_print_table_row(2, "Readline library", "WinEditLine");
+#else
php_info_print_table_row(2, "Readline library", (rl_library_version ? rl_library_version : "Unknown"));
+#endif
php_info_print_table_end();
DISPLAY_INI_ENTRIES();
diff --git a/ext/readline/tests/libedit_info_001-win32.phpt b/ext/readline/tests/libedit_info_001-win32.phpt
new file mode 100644
index 0000000000..5d43921c60
--- /dev/null
+++ b/ext/readline/tests/libedit_info_001-win32.phpt
@@ -0,0 +1,42 @@
+--TEST--
+readline_info(): Basic test
+--SKIPIF--
+<?php if (!extension_loaded("readline")) die("skip");
+if (READLINE_LIB != "libedit") die("skip libedit only");
+if(substr(PHP_OS, 0, 3) != 'WIN' ) {
+ die('skip windows only test');
+}
+?>
+--FILE--
+<?php
+
+var_dump(readline_info());
+var_dump(readline_info(1));
+var_dump(readline_info(1,1));
+var_dump(readline_info('line_buffer'));
+var_dump(readline_info('readline_name'));
+var_dump(readline_info('readline_name', 1));
+var_dump(readline_info('readline_name'));
+var_dump(readline_info('attempted_completion_over',1));
+var_dump(readline_info('attempted_completion_over'));
+
+?>
+--EXPECTF--
+array(4) {
+ ["line_buffer"]=>
+ string(0) ""
+ ["point"]=>
+ int(0)
+ ["readline_name"]=>
+ string(0) ""
+ ["attempted_completion_over"]=>
+ int(0)
+}
+NULL
+NULL
+string(0) ""
+string(0) ""
+string(0) ""
+string(1) "1"
+int(0)
+int(1)
diff --git a/ext/readline/tests/libedit_info_001.phpt b/ext/readline/tests/libedit_info_001.phpt
index 1d79f4ad0c..33dc144add 100644
--- a/ext/readline/tests/libedit_info_001.phpt
+++ b/ext/readline/tests/libedit_info_001.phpt
@@ -3,6 +3,9 @@ readline_info(): Basic test
--SKIPIF--
<?php if (!extension_loaded("readline")) die("skip");
if (READLINE_LIB != "libedit") die("skip libedit only");
+if(substr(PHP_OS, 0, 3) == 'WIN' ) {
+ die('skip not for windows');
+}
?>
--FILE--
<?php
diff --git a/ext/readline/tests/libedit_write_history_001-win32.phpt b/ext/readline/tests/libedit_write_history_001-win32.phpt
new file mode 100644
index 0000000000..28af4cbfdd
--- /dev/null
+++ b/ext/readline/tests/libedit_write_history_001-win32.phpt
@@ -0,0 +1,29 @@
+--TEST--
+readline_write_history(): Basic test
+--SKIPIF--
+<?php if (!extension_loaded("readline") || !function_exists('readline_add_history')) die("skip");
+if (READLINE_LIB != "libedit") die("skip libedit only");
+if(substr(PHP_OS, 0, 3) != 'WIN' ) {
+ die('skip windows only test');
+}
+?>
+--FILE--
+<?php
+
+$name = tempnam(sys_get_temp_dir(), 'readline.tmp');
+
+readline_add_history('foo');
+readline_add_history('');
+readline_add_history(1);
+readline_add_history(NULL);
+readline_write_history($name);
+
+var_dump(file_get_contents($name));
+
+unlink($name);
+
+?>
+--EXPECT--
+string(6) "foo
+1
+"
diff --git a/ext/readline/tests/libedit_write_history_001.phpt b/ext/readline/tests/libedit_write_history_001.phpt
index e9b6dbee8d..14c3282e6d 100644
--- a/ext/readline/tests/libedit_write_history_001.phpt
+++ b/ext/readline/tests/libedit_write_history_001.phpt
@@ -3,6 +3,9 @@ readline_write_history(): Basic test
--SKIPIF--
<?php if (!extension_loaded("readline") || !function_exists('readline_add_history')) die("skip");
if (READLINE_LIB != "libedit") die("skip libedit only");
+if(substr(PHP_OS, 0, 3) == 'WIN' ) {
+ die('skip not for windows');
+}
?>
--FILE--
<?php
diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c
index 4c3f6240f4..13003cae84 100644
--- a/ext/reflection/php_reflection.c
+++ b/ext/reflection/php_reflection.c
@@ -64,6 +64,7 @@ PHPAPI zend_class_entry *reflection_class_ptr;
PHPAPI zend_class_entry *reflection_object_ptr;
PHPAPI zend_class_entry *reflection_method_ptr;
PHPAPI zend_class_entry *reflection_property_ptr;
+PHPAPI zend_class_entry *reflection_class_constant_ptr;
PHPAPI zend_class_entry *reflection_extension_ptr;
PHPAPI zend_class_entry *reflection_zend_extension_ptr;
@@ -87,7 +88,7 @@ ZEND_DECLARE_MODULE_GLOBALS(reflection)
/* Method macros */
#define METHOD_NOTSTATIC(ce) \
- if (!Z_OBJ(EX(This)) || !instanceof_function(Z_OBJCE(EX(This)), ce)) { \
+ if ((Z_TYPE(EX(This)) != IS_OBJECT) || !instanceof_function(Z_OBJCE(EX(This)), ce)) { \
php_error_docref(NULL, E_ERROR, "%s() cannot be called statically", get_active_function_name()); \
return; \
} \
@@ -104,7 +105,7 @@ ZEND_DECLARE_MODULE_GLOBALS(reflection)
#define GET_REFLECTION_OBJECT() \
intern = Z_REFLECTION_P(getThis()); \
- if (intern->ptr == NULL) { \
+ if (intern->ptr == NULL) { \
RETURN_ON_EXCEPTION \
php_error_docref(NULL, E_ERROR, "Internal error: Failed to retrieve the reflection object"); \
} \
@@ -215,7 +216,8 @@ typedef enum {
REF_TYPE_PARAMETER,
REF_TYPE_TYPE,
REF_TYPE_PROPERTY,
- REF_TYPE_DYNAMIC_PROPERTY
+ REF_TYPE_DYNAMIC_PROPERTY,
+ REF_TYPE_CLASS_CONSTANT
} reflection_type_t;
/* Struct for reflection objects */
@@ -333,7 +335,7 @@ static void reflection_free_objects_storage(zend_object *object) /* {{{ */
efree(intern->ptr);
break;
case REF_TYPE_GENERATOR:
- break;
+ case REF_TYPE_CLASS_CONSTANT:
case REF_TYPE_OTHER:
break;
}
@@ -368,6 +370,7 @@ static zval *reflection_instantiate(zend_class_entry *pce, zval *object) /* {{{
static void _const_string(string *str, char *name, zval *value, char *indent);
static void _function_string(string *str, zend_function *fptr, zend_class_entry *scope, char* indent);
static void _property_string(string *str, zend_property_info *prop, char *prop_name, char* indent);
+static void _class_const_string(string *str, char *name, zend_class_constant *c, char* indent);
static void _class_string(string *str, zend_class_entry *ce, zval *obj, char *indent);
static void _extension_string(string *str, zend_module_entry *module, char *indent);
static void _zend_extension_string(string *str, zend_extension *extension, char *indent);
@@ -450,11 +453,11 @@ static void _class_string(string *str, zend_class_entry *ce, zval *obj, char *in
string_printf(str, "%s - Constants [%d] {\n", indent, count);
if (count > 0) {
zend_string *key;
- zval *value;
+ zend_class_constant *c;
- ZEND_HASH_FOREACH_STR_KEY_VAL(&ce->constants_table, key, value) {
- zval_update_constant_ex(value, 1, NULL);
- _const_string(str, ZSTR_VAL(key), value, indent);
+ ZEND_HASH_FOREACH_STR_KEY_PTR(&ce->constants_table, key, c) {
+ zval_update_constant_ex(&c->value, c->ce);
+ _class_const_string(str, ZSTR_VAL(key), c, ZSTR_VAL(sub_indent.buf));
} ZEND_HASH_FOREACH_END();
}
string_printf(str, "%s }\n", indent);
@@ -627,6 +630,24 @@ static void _const_string(string *str, char *name, zval *value, char *indent)
}
/* }}} */
+/* {{{ _class_const_string */
+static void _class_const_string(string *str, char *name, zend_class_constant *c, char *indent)
+{
+ char *visibility = zend_visibility_string(Z_ACCESS_FLAGS(c->value));
+ zend_string *value_str;
+ char *type;
+
+ zval_update_constant_ex(&c->value, c->ce);
+ value_str = zval_get_string(&c->value);
+ type = zend_zval_type_name(&c->value);
+
+ string_printf(str, "%sConstant [ %s %s %s ] { %s }\n",
+ indent, visibility, type, name, ZSTR_VAL(value_str));
+
+ zend_string_release(value_str);
+}
+/* }}} */
+
/* {{{ _get_recv_opcode */
static zend_op* _get_recv_op(zend_op_array *op_array, uint32_t offset)
{
@@ -689,14 +710,10 @@ static void _parameter_string(string *str, zend_function *fptr, struct _zend_arg
zend_op *precv = _get_recv_op((zend_op_array*)fptr, offset);
if (precv && precv->opcode == ZEND_RECV_INIT && precv->op2_type != IS_UNUSED) {
zval zv;
- zend_class_entry *old_scope;
string_write(str, " = ", sizeof(" = ")-1);
ZVAL_DUP(&zv, RT_CONSTANT(&fptr->op_array, precv->op2));
- old_scope = EG(scope);
- EG(scope) = fptr->common.scope;
- zval_update_constant_ex(&zv, 1, NULL);
- EG(scope) = old_scope;
+ zval_update_constant_ex(&zv, fptr->common.scope);
if (Z_TYPE(zv) == IS_TRUE) {
string_write(str, "true", sizeof("true")-1);
} else if (Z_TYPE(zv) == IS_FALSE) {
@@ -1356,6 +1373,27 @@ static void reflection_property_factory(zend_class_entry *ce, zend_property_info
}
/* }}} */
+/* {{{ reflection_class_constant_factory */
+static void reflection_class_constant_factory(zend_class_entry *ce, zend_string *name_str, zend_class_constant *constant, zval *object)
+{
+ reflection_object *intern;
+ zval name;
+ zval classname;
+
+ ZVAL_STR_COPY(&name, name_str);
+ ZVAL_STR_COPY(&classname, ce->name);
+
+ reflection_instantiate(reflection_class_constant_ptr, object);
+ intern = Z_REFLECTION_P(object);
+ intern->ptr = constant;
+ intern->ref_type = REF_TYPE_CLASS_CONSTANT;
+ intern->ce = constant->ce;
+ intern->ignore_visibility = 0;
+ reflection_update_property(object, "name", &name);
+ reflection_update_property(object, "class", &classname);
+}
+/* }}} */
+
/* {{{ _reflection_export */
static void _reflection_export(INTERNAL_FUNCTION_PARAMETERS, zend_class_entry *ce_ptr, int ctor_argc)
{
@@ -1390,9 +1428,7 @@ static void _reflection_export(INTERNAL_FUNCTION_PARAMETERS, zend_class_entry *c
/* Call __construct() */
fci.size = sizeof(fci);
- fci.function_table = NULL;
ZVAL_UNDEF(&fci.function_name);
- fci.symbol_table = NULL;
fci.object = Z_OBJ(reflector);
fci.retval = &retval;
fci.param_count = ctor_argc;
@@ -1424,7 +1460,6 @@ static void _reflection_export(INTERNAL_FUNCTION_PARAMETERS, zend_class_entry *c
ZVAL_COPY_VALUE(&params[1], output_ptr);
ZVAL_STRINGL(&fci.function_name, "reflection::export", sizeof("reflection::export") - 1);
- fci.function_table = &reflection_ptr->function_table;
fci.object = NULL;
fci.retval = &retval;
fci.param_count = 2;
@@ -1896,7 +1931,7 @@ ZEND_METHOD(reflection_function, getStaticVariables)
fptr->op_array.static_variables = zend_array_dup(fptr->op_array.static_variables);
}
ZEND_HASH_FOREACH_VAL(fptr->op_array.static_variables, val) {
- if (UNEXPECTED(zval_update_constant_ex(val, 1, fptr->common.scope) != SUCCESS)) {
+ if (UNEXPECTED(zval_update_constant_ex(val, fptr->common.scope) != SUCCESS)) {
return;
}
} ZEND_HASH_FOREACH_END();
@@ -1925,9 +1960,7 @@ ZEND_METHOD(reflection_function, invoke)
}
fci.size = sizeof(fci);
- fci.function_table = NULL;
ZVAL_UNDEF(&fci.function_name);
- fci.symbol_table = NULL;
fci.object = NULL;
fci.retval = &retval;
fci.param_count = num_args;
@@ -1936,7 +1969,7 @@ ZEND_METHOD(reflection_function, invoke)
fcc.initialized = 1;
fcc.function_handler = fptr;
- fcc.calling_scope = EG(scope);
+ fcc.calling_scope = zend_get_executed_scope();
fcc.called_scope = NULL;
fcc.object = NULL;
@@ -1985,9 +2018,7 @@ ZEND_METHOD(reflection_function, invokeArgs)
} ZEND_HASH_FOREACH_END();
fci.size = sizeof(fci);
- fci.function_table = NULL;
ZVAL_UNDEF(&fci.function_name);
- fci.symbol_table = NULL;
fci.object = NULL;
fci.retval = &retval;
fci.param_count = argc;
@@ -1996,7 +2027,7 @@ ZEND_METHOD(reflection_function, invokeArgs)
fcc.initialized = 1;
fcc.function_handler = fptr;
- fcc.calling_scope = EG(scope);
+ fcc.calling_scope = zend_get_executed_scope();
fcc.called_scope = NULL;
fcc.object = NULL;
@@ -2281,7 +2312,7 @@ ZEND_METHOD(reflection_generator, getThis)
REFLECTION_CHECK_VALID_GENERATOR(ex)
- if (Z_OBJ(ex->This)) {
+ if (Z_TYPE(ex->This) == IS_OBJECT) {
ZVAL_COPY(return_value, &ex->This);
} else {
ZVAL_NULL(return_value);
@@ -2857,15 +2888,9 @@ ZEND_METHOD(reflection_parameter, getDefaultValue)
return;
}
- ZVAL_COPY_VALUE(return_value, RT_CONSTANT(&param->fptr->op_array, precv->op2));
+ ZVAL_DUP(return_value, RT_CONSTANT(&param->fptr->op_array, precv->op2));
if (Z_CONSTANT_P(return_value)) {
- zend_class_entry *old_scope = EG(scope);
-
- EG(scope) = param->fptr->common.scope;
- zval_update_constant_ex(return_value, 0, NULL);
- EG(scope) = old_scope;
- } else {
- zval_copy_ctor(return_value);
+ zval_update_constant_ex(return_value, param->fptr->common.scope);
}
}
/* }}} */
@@ -2991,6 +3016,7 @@ ZEND_METHOD(reflection_type, __toString)
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);
+ case IS_VOID: RETURN_STRINGL("void", sizeof("void") - 1);
EMPTY_SWITCH_DEFAULT_CASE()
}
}
@@ -3216,9 +3242,7 @@ ZEND_METHOD(reflection_method, invoke)
}
fci.size = sizeof(fci);
- fci.function_table = NULL;
ZVAL_UNDEF(&fci.function_name);
- fci.symbol_table = NULL;
fci.object = object;
fci.retval = &retval;
fci.param_count = num_args - 1;
@@ -3323,9 +3347,7 @@ ZEND_METHOD(reflection_method, invokeArgs)
}
fci.size = sizeof(fci);
- fci.function_table = NULL;
ZVAL_UNDEF(&fci.function_name);
- fci.symbol_table = NULL;
fci.object = object ? Z_OBJ_P(object) : NULL;
fci.retval = &retval;
fci.param_count = argc;
@@ -3652,6 +3674,197 @@ ZEND_METHOD(reflection_method, setAccessible)
}
/* }}} */
+/* {{{ proto public void ReflectionClassConstant::__construct(mixed class, string name)
+ Constructor. Throws an Exception in case the given class constant does not exist */
+ZEND_METHOD(reflection_class_constant, __construct)
+{
+ zval *classname, *object, name, cname;
+ zend_string *constname;
+ reflection_object *intern;
+ zend_class_entry *ce;
+ zend_class_constant *constant = NULL;
+
+ if (zend_parse_parameters_throw(ZEND_NUM_ARGS(), "zS", &classname, &constname) == FAILURE) {
+ return;
+ }
+
+ object = getThis();
+ intern = Z_REFLECTION_P(object);
+
+ /* Find the class entry */
+ switch (Z_TYPE_P(classname)) {
+ case IS_STRING:
+ if ((ce = zend_lookup_class(Z_STR_P(classname))) == NULL) {
+ zend_throw_exception_ex(reflection_exception_ptr, 0,
+ "Class %s does not exist", Z_STRVAL_P(classname));
+ return;
+ }
+ break;
+
+ case IS_OBJECT:
+ ce = Z_OBJCE_P(classname);
+ break;
+
+ default:
+ _DO_THROW("The parameter class is expected to be either a string or an object");
+ /* returns out of this function */
+ }
+
+ if ((constant = zend_hash_find_ptr(&ce->constants_table, constname)) == NULL) {
+ zend_throw_exception_ex(reflection_exception_ptr, 0, "Class Constant %s::%s does not exist", ZSTR_VAL(ce->name), ZSTR_VAL(constname));
+ return;
+ }
+
+ ZVAL_STR_COPY(&name, constname);
+ ZVAL_STR_COPY(&cname, ce->name);
+
+ intern->ptr = constant;
+ intern->ref_type = REF_TYPE_CLASS_CONSTANT;
+ intern->ce = constant->ce;
+ intern->ignore_visibility = 0;
+ reflection_update_property(object, "name", &name);
+ reflection_update_property(object, "class", &cname);
+}
+/* }}} */
+
+/* {{{ proto public string ReflectionClassConstant::__toString()
+ Returns a string representation */
+ZEND_METHOD(reflection_class_constant, __toString)
+{
+ reflection_object *intern;
+ zend_class_constant *ref;
+ string str;
+ zval name;
+
+ if (zend_parse_parameters_none() == FAILURE) {
+ return;
+ }
+ GET_REFLECTION_OBJECT_PTR(ref);
+ string_init(&str);
+ _default_get_entry(getThis(), "name", sizeof("name")-1, &name);
+ _class_const_string(&str, Z_STRVAL(name), ref, "");
+ zval_ptr_dtor(&name);
+ RETURN_NEW_STR(str.buf);
+}
+/* }}} */
+
+/* {{{ proto public string ReflectionClassConstant::getName()
+ Returns the constant' name */
+ZEND_METHOD(reflection_class_constant, getName)
+{
+ if (zend_parse_parameters_none() == FAILURE) {
+ return;
+ }
+ _default_get_entry(getThis(), "name", sizeof("name")-1, return_value);
+}
+/* }}} */
+
+static void _class_constant_check_flag(INTERNAL_FUNCTION_PARAMETERS, int mask) /* {{{ */
+{
+ reflection_object *intern;
+ zend_class_constant *ref;
+
+ if (zend_parse_parameters_none() == FAILURE) {
+ return;
+ }
+ GET_REFLECTION_OBJECT_PTR(ref);
+ RETURN_BOOL(Z_ACCESS_FLAGS(ref->value) & mask);
+}
+/* }}} */
+
+/* {{{ proto public bool ReflectionClassConstant::isPublic()
+ Returns whether this constant is public */
+ZEND_METHOD(reflection_class_constant, isPublic)
+{
+ _class_constant_check_flag(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_ACC_PUBLIC | ZEND_ACC_IMPLICIT_PUBLIC);
+}
+/* }}} */
+
+/* {{{ proto public bool ReflectionClassConstant::isPrivate()
+ Returns whether this constant is private */
+ZEND_METHOD(reflection_class_constant, isPrivate)
+{
+ _class_constant_check_flag(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_ACC_PRIVATE);
+}
+/* }}} */
+
+/* {{{ proto public bool ReflectionClassConstant::isProtected()
+ Returns whether this constant is protected */
+ZEND_METHOD(reflection_class_constant, isProtected)
+{
+ _class_constant_check_flag(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_ACC_PROTECTED);
+}
+/* }}} */
+
+/* {{{ proto public int ReflectionClassConstant::getModifiers()
+ Returns a bitfield of the access modifiers for this constant */
+ZEND_METHOD(reflection_class_constant, getModifiers)
+{
+ reflection_object *intern;
+ zend_class_constant *ref;
+
+ if (zend_parse_parameters_none() == FAILURE) {
+ return;
+ }
+ GET_REFLECTION_OBJECT_PTR(ref);
+
+ RETURN_LONG(Z_ACCESS_FLAGS(ref->value));
+}
+/* }}} */
+
+/* {{{ proto public mixed ReflectionClassConstant::getValue()
+ Returns this constant's value */
+ZEND_METHOD(reflection_class_constant, getValue)
+{
+ reflection_object *intern;
+ zend_class_constant *ref;
+
+ if (zend_parse_parameters_none() == FAILURE) {
+ return;
+ }
+ GET_REFLECTION_OBJECT_PTR(ref);
+
+ ZVAL_DUP(return_value, &ref->value);
+ if (Z_CONSTANT_P(return_value)) {
+ zval_update_constant_ex(return_value, ref->ce);
+ }
+}
+/* }}} */
+
+/* {{{ proto public ReflectionClass ReflectionClassConstant::getDeclaringClass()
+ Get the declaring class */
+ZEND_METHOD(reflection_class_constant, getDeclaringClass)
+{
+ reflection_object *intern;
+ zend_class_constant *ref;
+
+ if (zend_parse_parameters_none() == FAILURE) {
+ return;
+ }
+ GET_REFLECTION_OBJECT_PTR(ref);
+
+ zend_reflection_class_factory(ref->ce, return_value);
+}
+/* }}} */
+
+/* {{{ proto public string ReflectionClassConstant::getDocComment()
+ Returns the doc comment for this constant */
+ZEND_METHOD(reflection_class_constant, getDocComment)
+{
+ reflection_object *intern;
+ zend_class_constant *ref;
+
+ if (zend_parse_parameters_none() == FAILURE) {
+ return;
+ }
+ GET_REFLECTION_OBJECT_PTR(ref);
+ if (ref->doc_comment) {
+ RETURN_STR_COPY(ref->doc_comment);
+ }
+ RETURN_FALSE;
+}
+/* }}} */
+
/* {{{ proto public static mixed ReflectionClass::export(mixed argument [, bool return]) throws ReflectionException
Exports a reflection object. Returns the output if TRUE is specified for return, printing it otherwise. */
ZEND_METHOD(reflection_class, export)
@@ -3749,7 +3962,7 @@ static void add_class_vars(zend_class_entry *ce, int statics, zval *return_value
/* this is necessary to make it able to work with default array
* properties, returned to user */
if (Z_CONSTANT(prop_copy)) {
- if (UNEXPECTED(zval_update_constant_ex(&prop_copy, 1, NULL) != SUCCESS)) {
+ if (UNEXPECTED(zval_update_constant_ex(&prop_copy, NULL) != SUCCESS)) {
return;
}
}
@@ -4395,6 +4608,8 @@ ZEND_METHOD(reflection_class, getConstants)
{
reflection_object *intern;
zend_class_entry *ce;
+ zend_string *key;
+ zend_class_constant *c;
zval *val;
if (zend_parse_parameters_none() == FAILURE) {
@@ -4402,12 +4617,36 @@ ZEND_METHOD(reflection_class, getConstants)
}
GET_REFLECTION_OBJECT_PTR(ce);
array_init(return_value);
- ZEND_HASH_FOREACH_VAL(&ce->constants_table, val) {
- if (UNEXPECTED(zval_update_constant_ex(val, 1, ce) != SUCCESS)) {
+ ZEND_HASH_FOREACH_STR_KEY_PTR(&ce->constants_table, key, c) {
+ if (UNEXPECTED(zval_update_constant_ex(&c->value, ce) != SUCCESS)) {
+ zend_array_destroy(Z_ARRVAL_P(return_value));
return;
}
+ val = zend_hash_add_new(Z_ARRVAL_P(return_value), key, &c->value);
+ Z_TRY_ADDREF_P(val);
+ } ZEND_HASH_FOREACH_END();
+}
+/* }}} */
+
+/* {{{ proto public array ReflectionClass::getReflectionConstants()
+ Returns an associative array containing this class' constants as ReflectionClassConstant objects */
+ZEND_METHOD(reflection_class, getReflectionConstants)
+{
+ reflection_object *intern;
+ zend_class_entry *ce;
+ zend_string *name;
+ zend_class_constant *constant;
+
+ if (zend_parse_parameters_none() == FAILURE) {
+ return;
+ }
+ GET_REFLECTION_OBJECT_PTR(ce);
+ array_init(return_value);
+ ZEND_HASH_FOREACH_STR_KEY_PTR(&ce->constants_table, name, constant) {
+ zval class_const;
+ reflection_class_constant_factory(ce, name, constant, &class_const);
+ zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &class_const);
} ZEND_HASH_FOREACH_END();
- zend_hash_copy(Z_ARRVAL_P(return_value), &ce->constants_table, zval_add_ref_unref);
}
/* }}} */
@@ -4417,7 +4656,7 @@ ZEND_METHOD(reflection_class, getConstant)
{
reflection_object *intern;
zend_class_entry *ce;
- zval *value;
+ zend_class_constant *c;
zend_string *name;
METHOD_NOTSTATIC(reflection_class_ptr);
@@ -4426,15 +4665,36 @@ ZEND_METHOD(reflection_class, getConstant)
}
GET_REFLECTION_OBJECT_PTR(ce);
- ZEND_HASH_FOREACH_VAL(&ce->constants_table, value) {
- if (UNEXPECTED(zval_update_constant_ex(value, 1, ce) != SUCCESS)) {
+ ZEND_HASH_FOREACH_PTR(&ce->constants_table, c) {
+ if (UNEXPECTED(zval_update_constant_ex(&c->value, ce) != SUCCESS)) {
return;
}
} ZEND_HASH_FOREACH_END();
- if ((value = zend_hash_find(&ce->constants_table, name)) == NULL) {
+ if ((c = zend_hash_find_ptr(&ce->constants_table, name)) == NULL) {
RETURN_FALSE;
}
- ZVAL_DUP(return_value, value);
+ ZVAL_DUP(return_value, &c->value);
+}
+/* }}} */
+
+/* {{{ proto public mixed ReflectionClass::getReflectionConstant(string name)
+ Returns the class' constant as ReflectionClassConstant objects */
+ZEND_METHOD(reflection_class, getReflectionConstant)
+{
+ reflection_object *intern;
+ zend_class_entry *ce;
+ zend_class_constant *constant;
+ zend_string *name;
+
+ GET_REFLECTION_OBJECT_PTR(ce);
+ if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &name) == FAILURE) {
+ return;
+ }
+
+ if ((constant = zend_hash_find_ptr(&ce->constants_table, name)) == NULL) {
+ RETURN_FALSE;
+ }
+ reflection_class_constant_factory(ce, name, constant, return_value);
}
/* }}} */
@@ -4593,10 +4853,10 @@ ZEND_METHOD(reflection_class, newInstance)
return;
}
- old_scope = EG(scope);
- EG(scope) = ce;
+ old_scope = EG(fake_scope);
+ EG(fake_scope) = ce;
constructor = Z_OBJ_HT_P(return_value)->get_constructor(Z_OBJ_P(return_value));
- EG(scope) = old_scope;
+ EG(fake_scope) = old_scope;
/* Run the constructor if there is one */
if (constructor) {
@@ -4621,9 +4881,7 @@ ZEND_METHOD(reflection_class, newInstance)
}
fci.size = sizeof(fci);
- fci.function_table = EG(function_table);
ZVAL_UNDEF(&fci.function_name);
- fci.symbol_table = NULL;
fci.object = Z_OBJ_P(return_value);
fci.retval = &retval;
fci.param_count = num_args;
@@ -4632,7 +4890,7 @@ ZEND_METHOD(reflection_class, newInstance)
fcc.initialized = 1;
fcc.function_handler = constructor;
- fcc.calling_scope = EG(scope);
+ fcc.calling_scope = zend_get_executed_scope();;
fcc.called_scope = Z_OBJCE_P(return_value);
fcc.object = Z_OBJ_P(return_value);
@@ -4698,10 +4956,10 @@ ZEND_METHOD(reflection_class, newInstanceArgs)
return;
}
- old_scope = EG(scope);
- EG(scope) = ce;
+ old_scope = EG(fake_scope);
+ EG(fake_scope) = ce;
constructor = Z_OBJ_HT_P(return_value)->get_constructor(Z_OBJ_P(return_value));
- EG(scope) = old_scope;
+ EG(fake_scope) = old_scope;
/* Run the constructor if there is one */
if (constructor) {
@@ -4725,9 +4983,7 @@ ZEND_METHOD(reflection_class, newInstanceArgs)
}
fci.size = sizeof(fci);
- fci.function_table = EG(function_table);
ZVAL_UNDEF(&fci.function_name);
- fci.symbol_table = NULL;
fci.object = Z_OBJ_P(return_value);
fci.retval = &retval;
fci.param_count = argc;
@@ -4736,7 +4992,7 @@ ZEND_METHOD(reflection_class, newInstanceArgs)
fcc.initialized = 1;
fcc.function_handler = constructor;
- fcc.calling_scope = EG(scope);
+ fcc.calling_scope = zend_get_executed_scope();
fcc.called_scope = Z_OBJCE_P(return_value);
fcc.object = Z_OBJ_P(return_value);
@@ -4931,7 +5187,7 @@ ZEND_METHOD(reflection_class, isSubclassOf)
case IS_OBJECT:
if (instanceof_function(Z_OBJCE_P(class_name), reflection_class_ptr)) {
argument = Z_REFLECTION_P(class_name);
- if (argument == NULL || argument->ptr == NULL) {
+ if (argument->ptr == NULL) {
php_error_docref(NULL, E_ERROR, "Internal error: Failed to retrieve the argument's reflection object");
/* Bails out */
}
@@ -4975,7 +5231,7 @@ ZEND_METHOD(reflection_class, implementsInterface)
case IS_OBJECT:
if (instanceof_function(Z_OBJCE_P(interface), reflection_class_ptr)) {
argument = Z_REFLECTION_P(interface);
- if (argument == NULL || argument->ptr == NULL) {
+ if (argument->ptr == NULL) {
php_error_docref(NULL, E_ERROR, "Internal error: Failed to retrieve the argument's reflection object");
/* Bails out */
}
@@ -5152,6 +5408,14 @@ ZEND_METHOD(reflection_property, export)
}
/* }}} */
+/* {{{ proto public static mixed ReflectionClassConstant::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_class_constant, export)
+{
+ _reflection_export(INTERNAL_FUNCTION_PARAM_PASSTHRU, reflection_class_constant_ptr, 2);
+}
+/* }}} */
+
/* {{{ proto public void ReflectionProperty::__construct(mixed class, string name)
Constructor. Throws an Exception in case the given property does not exist */
ZEND_METHOD(reflection_property, __construct)
@@ -5372,7 +5636,9 @@ ZEND_METHOD(reflection_property, getValue)
php_error_docref(NULL, E_ERROR, "Internal error: Could not find the property %s::%s", ZSTR_VAL(intern->ce->name), ZSTR_VAL(ref->prop.name));
/* Bails out */
}
- ZVAL_DUP(return_value, &CE_STATIC_MEMBERS(intern->ce)[ref->prop.offset]);
+ member_p = &CE_STATIC_MEMBERS(intern->ce)[ref->prop.offset];
+ ZVAL_DEREF(member_p);
+ ZVAL_COPY(return_value, member_p);
} else {
const char *class_name, *prop_name;
size_t prop_name_len;
@@ -5382,11 +5648,20 @@ ZEND_METHOD(reflection_property, getValue)
return;
}
+ if (!instanceof_function(Z_OBJCE_P(object), ref->ce)) {
+ _DO_THROW("Given object is not an instance of the class this property was declared in");
+ /* Returns from this function */
+ }
+
zend_unmangle_property_name_ex(ref->prop.name, &class_name, &prop_name, &prop_name_len);
member_p = zend_read_property(ref->ce, object, prop_name, prop_name_len, 0, &rv);
if (member_p != &rv) {
+ ZVAL_DEREF(member_p);
ZVAL_COPY(return_value, member_p);
} else {
+ if (Z_ISREF_P(member_p)) {
+ zend_unwrap_reference(member_p);
+ }
ZVAL_COPY_VALUE(return_value, member_p);
}
}
@@ -5555,7 +5830,6 @@ ZEND_METHOD(reflection_extension, __construct)
object = getThis();
intern = Z_REFLECTION_P(object);
-
lcname = do_alloca(name_len + 1, use_heap);
zend_str_tolower_copy(lcname, name_str, name_len);
if ((module = zend_hash_str_find_ptr(&module_registry, lcname, name_len)) == NULL) {
@@ -6307,7 +6581,9 @@ static const zend_function_entry reflection_class_functions[] = {
ZEND_ME(reflection_class, getProperties, arginfo_reflection_class_getProperties, 0)
ZEND_ME(reflection_class, hasConstant, arginfo_reflection_class_hasConstant, 0)
ZEND_ME(reflection_class, getConstants, arginfo_reflection__void, 0)
+ ZEND_ME(reflection_class, getReflectionConstants, arginfo_reflection__void, 0)
ZEND_ME(reflection_class, getConstant, arginfo_reflection_class_getConstant, 0)
+ ZEND_ME(reflection_class, getReflectionConstant, arginfo_reflection_class_getConstant, 0)
ZEND_ME(reflection_class, getInterfaces, arginfo_reflection__void, 0)
ZEND_ME(reflection_class, getInterfaceNames, arginfo_reflection__void, 0)
ZEND_ME(reflection_class, isInterface, arginfo_reflection__void, 0)
@@ -6399,6 +6675,33 @@ static const zend_function_entry reflection_property_functions[] = {
PHP_FE_END
};
+ZEND_BEGIN_ARG_INFO_EX(arginfo_reflection_class_constant_export, 0, 0, 2)
+ ZEND_ARG_INFO(0, class)
+ ZEND_ARG_INFO(0, name)
+ ZEND_ARG_INFO(0, return)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_reflection_class_constant___construct, 0, 0, 2)
+ ZEND_ARG_INFO(0, class)
+ ZEND_ARG_INFO(0, name)
+ZEND_END_ARG_INFO()
+
+static const zend_function_entry reflection_class_constant_functions[] = {
+ ZEND_ME(reflection, __clone, arginfo_reflection__void, ZEND_ACC_PRIVATE|ZEND_ACC_FINAL)
+ ZEND_ME(reflection_class_constant, export, arginfo_reflection_class_constant_export, ZEND_ACC_STATIC|ZEND_ACC_PUBLIC)
+ ZEND_ME(reflection_class_constant, __construct, arginfo_reflection_class_constant___construct, 0)
+ ZEND_ME(reflection_class_constant, __toString, arginfo_reflection__void, 0)
+ ZEND_ME(reflection_class_constant, getName, arginfo_reflection__void, 0)
+ ZEND_ME(reflection_class_constant, getValue, arginfo_reflection__void, 0)
+ ZEND_ME(reflection_class_constant, isPublic, arginfo_reflection__void, 0)
+ ZEND_ME(reflection_class_constant, isPrivate, arginfo_reflection__void, 0)
+ ZEND_ME(reflection_class_constant, isProtected, arginfo_reflection__void, 0)
+ ZEND_ME(reflection_class_constant, getModifiers, arginfo_reflection__void, 0)
+ ZEND_ME(reflection_class_constant, getDeclaringClass, arginfo_reflection__void, 0)
+ ZEND_ME(reflection_class_constant, getDocComment, arginfo_reflection__void, 0)
+ PHP_FE_END
+};
+
ZEND_BEGIN_ARG_INFO_EX(arginfo_reflection_parameter_export, 0, 0, 2)
ZEND_ARG_INFO(0, function)
ZEND_ARG_INFO(0, parameter)
@@ -6595,6 +6898,13 @@ PHP_MINIT_FUNCTION(reflection) /* {{{ */
zend_declare_property_string(reflection_property_ptr, "name", sizeof("name")-1, "", ZEND_ACC_PUBLIC);
zend_declare_property_string(reflection_property_ptr, "class", sizeof("class")-1, "", ZEND_ACC_PUBLIC);
+ INIT_CLASS_ENTRY(_reflection_entry, "ReflectionClassConstant", reflection_class_constant_functions);
+ _reflection_entry.create_object = reflection_objects_new;
+ reflection_class_constant_ptr = zend_register_internal_class(&_reflection_entry);
+ zend_class_implements(reflection_class_constant_ptr, 1, reflector_ptr);
+ zend_declare_property_string(reflection_class_constant_ptr, "name", sizeof("name")-1, "", ZEND_ACC_PUBLIC);
+ zend_declare_property_string(reflection_class_constant_ptr, "class", sizeof("class")-1, "", ZEND_ACC_PUBLIC);
+
REGISTER_REFLECTION_CLASS_CONST_LONG(property, "IS_STATIC", ZEND_ACC_STATIC);
REGISTER_REFLECTION_CLASS_CONST_LONG(property, "IS_PUBLIC", ZEND_ACC_PUBLIC);
REGISTER_REFLECTION_CLASS_CONST_LONG(property, "IS_PROTECTED", ZEND_ACC_PROTECTED);
diff --git a/ext/reflection/tests/007.phpt b/ext/reflection/tests/007.phpt
index 004158ccff..d9204171b5 100644
--- a/ext/reflection/tests/007.phpt
+++ b/ext/reflection/tests/007.phpt
@@ -25,6 +25,10 @@ function test($class)
{
var_dump($e->getMessage());
}
+ catch (Throwable $e)
+ {
+ echo "Exception: " . $e->getMessage() . "\n";
+ }
echo "====>newInstance(25)\n";
try
@@ -129,15 +133,7 @@ object(WithCtor)#%d (0) {
====>WithCtorWithArgs
====>newInstance()
-
-Warning: Missing argument 1 for WithCtorWithArgs::__construct() in %s007.php on line %d
-
-Notice: Undefined variable: arg in %s007.php on line %d
-WithCtorWithArgs::__construct()
-array(0) {
-}
-object(WithCtorWithArgs)#%d (0) {
-}
+Exception: Too few arguments to function WithCtorWithArgs::__construct(), 0 passed and exactly 1 expected
====>newInstance(25)
WithCtorWithArgs::__construct(25)
array(1) {
diff --git a/ext/reflection/tests/017.phpt b/ext/reflection/tests/017.phpt
index d40c4d83f9..1d9275d21b 100644
--- a/ext/reflection/tests/017.phpt
+++ b/ext/reflection/tests/017.phpt
@@ -10,12 +10,12 @@ class Foo {
$class = new ReflectionClass("Foo");
echo $class;
?>
---EXPECTF--
+--EXPECTF--
Class [ <user> class Foo ] {
@@ %s017.php 2-4
- Constants [1] {
- Constant [ string test ] { ok }
+ Constant [ public string test ] { ok }
}
- Static properties [0] {
diff --git a/ext/reflection/tests/ReflectionClassConstant_basic1.phpt b/ext/reflection/tests/ReflectionClassConstant_basic1.phpt
new file mode 100644
index 0000000000..fd8118650f
--- /dev/null
+++ b/ext/reflection/tests/ReflectionClassConstant_basic1.phpt
@@ -0,0 +1,194 @@
+--TEST--
+Test usage of ReflectionClassConstant methods __toString(), export(), getName(), getValue(), isPublic(), isPrivate(), isProtected(), getModifiers(), getDeclaringClass() and getDocComment().
+--FILE--
+<?php
+
+function reflectClassConstant($base, $constant) {
+ $constInfo = new ReflectionClassConstant($base, $constant);
+ echo "**********************************\n";
+ $class = is_object($base) ? get_class($base) : $base;
+ echo "Reflecting on class constant $class::$constant\n\n";
+ echo "__toString():\n";
+ var_dump($constInfo->__toString());
+ echo "export():\n";
+ var_dump(ReflectionClassConstant::export($base, $constant, true));
+ echo "export():\n";
+ var_dump(ReflectionClassConstant::export($base, $constant, false));
+ echo "getName():\n";
+ var_dump($constInfo->getName());
+ echo "getValue():\n";
+ var_dump($constInfo->getValue());
+ echo "isPublic():\n";
+ var_dump($constInfo->isPublic());
+ echo "isPrivate():\n";
+ var_dump($constInfo->isPrivate());
+ echo "isProtected():\n";
+ var_dump($constInfo->isProtected());
+ echo "getModifiers():\n";
+ var_dump($constInfo->getModifiers());
+ echo "getDeclaringClass():\n";
+ var_dump($constInfo->getDeclaringClass());
+ echo "getDocComment():\n";
+ var_dump($constInfo->getDocComment());
+ echo "\n**********************************\n";
+}
+
+class TestClass {
+ public const /** My Doc comment */ PUB = true;
+ /** Another doc comment */
+ protected const PROT = 4;
+ private const PRIV = "keepOut";
+}
+$instance = new TestClass();
+
+reflectClassConstant("TestClass", "PUB");
+reflectClassConstant("TestClass", "PROT");
+reflectClassConstant("TestClass", "PRIV");
+reflectClassConstant($instance, "PRIV");
+reflectClassConstant($instance, "BAD_CONST");
+
+?>
+--EXPECTF--
+**********************************
+Reflecting on class constant TestClass::PUB
+
+__toString():
+string(38) "Constant [ public boolean PUB ] { 1 }
+"
+export():
+string(38) "Constant [ public boolean PUB ] { 1 }
+"
+export():
+Constant [ public boolean PUB ] { 1 }
+
+NULL
+getName():
+string(3) "PUB"
+getValue():
+bool(true)
+isPublic():
+bool(true)
+isPrivate():
+bool(false)
+isProtected():
+bool(false)
+getModifiers():
+int(256)
+getDeclaringClass():
+object(ReflectionClass)#3 (1) {
+ ["name"]=>
+ string(9) "TestClass"
+}
+getDocComment():
+string(21) "/** My Doc comment */"
+
+**********************************
+**********************************
+Reflecting on class constant TestClass::PROT
+
+__toString():
+string(42) "Constant [ protected integer PROT ] { 4 }
+"
+export():
+string(42) "Constant [ protected integer PROT ] { 4 }
+"
+export():
+Constant [ protected integer PROT ] { 4 }
+
+NULL
+getName():
+string(4) "PROT"
+getValue():
+int(4)
+isPublic():
+bool(false)
+isPrivate():
+bool(false)
+isProtected():
+bool(true)
+getModifiers():
+int(512)
+getDeclaringClass():
+object(ReflectionClass)#3 (1) {
+ ["name"]=>
+ string(9) "TestClass"
+}
+getDocComment():
+string(26) "/** Another doc comment */"
+
+**********************************
+**********************************
+Reflecting on class constant TestClass::PRIV
+
+__toString():
+string(45) "Constant [ private string PRIV ] { keepOut }
+"
+export():
+string(45) "Constant [ private string PRIV ] { keepOut }
+"
+export():
+Constant [ private string PRIV ] { keepOut }
+
+NULL
+getName():
+string(4) "PRIV"
+getValue():
+string(7) "keepOut"
+isPublic():
+bool(false)
+isPrivate():
+bool(true)
+isProtected():
+bool(false)
+getModifiers():
+int(1024)
+getDeclaringClass():
+object(ReflectionClass)#3 (1) {
+ ["name"]=>
+ string(9) "TestClass"
+}
+getDocComment():
+bool(false)
+
+**********************************
+**********************************
+Reflecting on class constant TestClass::PRIV
+
+__toString():
+string(45) "Constant [ private string PRIV ] { keepOut }
+"
+export():
+string(45) "Constant [ private string PRIV ] { keepOut }
+"
+export():
+Constant [ private string PRIV ] { keepOut }
+
+NULL
+getName():
+string(4) "PRIV"
+getValue():
+string(7) "keepOut"
+isPublic():
+bool(false)
+isPrivate():
+bool(true)
+isProtected():
+bool(false)
+getModifiers():
+int(1024)
+getDeclaringClass():
+object(ReflectionClass)#3 (1) {
+ ["name"]=>
+ string(9) "TestClass"
+}
+getDocComment():
+bool(false)
+
+**********************************
+
+Fatal error: Uncaught ReflectionException: Class Constant TestClass::BAD_CONST does not exist in %s:%d
+Stack trace:
+#0 %s(%d): ReflectionClassConstant->__construct(Object(TestClass), 'BAD_CONST')
+#1 %s(%d): reflectClassConstant(Object(TestClass), 'BAD_CONST')
+#2 {main}
+ thrown in %s on line %d
diff --git a/ext/reflection/tests/ReflectionClassConstant_getValue.phpt b/ext/reflection/tests/ReflectionClassConstant_getValue.phpt
new file mode 100644
index 0000000000..e447d15357
--- /dev/null
+++ b/ext/reflection/tests/ReflectionClassConstant_getValue.phpt
@@ -0,0 +1,47 @@
+--TEST--
+Test variations of getting constant values
+--FILE--
+<?php
+
+/* Use separate classes to make sure that in-place constant updates don't interfere */
+class A {
+ const X = self::Y * 2;
+ const Y = 1;
+}
+class B {
+ const X = self::Y * 2;
+ const Y = 1;
+}
+class C {
+ const X = self::Y * 2;
+ const Y = 1;
+}
+
+var_dump((new ReflectionClassConstant('A', 'X'))->getValue());
+echo new ReflectionClassConstant('B', 'X');
+echo new ReflectionClass('C');
+
+?>
+--EXPECTF--
+int(2)
+Constant [ public integer X ] { 2 }
+Class [ <user> class C ] {
+ @@ %s 12-15
+
+ - Constants [2] {
+ Constant [ public integer X ] { 2 }
+ Constant [ public integer Y ] { 1 }
+ }
+
+ - Static properties [0] {
+ }
+
+ - Static methods [0] {
+ }
+
+ - Properties [0] {
+ }
+
+ - Methods [0] {
+ }
+}
diff --git a/ext/reflection/tests/ReflectionClass_isArray.phpt b/ext/reflection/tests/ReflectionClass_isArray.phpt
new file mode 100644
index 0000000000..3eec0dac54
--- /dev/null
+++ b/ext/reflection/tests/ReflectionClass_isArray.phpt
@@ -0,0 +1,24 @@
+--TEST--
+public bool ReflectionParameter::isArray ( void );
+--CREDITS--
+marcosptf - <marcosptf@yahoo.com.br> - @phpsp - sao paulo - br
+--FILE--
+<?php
+function testReflectionIsArray($a = null, $b = 0, array $c, $d=true, array $e, $f=1.5, $g="", array $h, $i="#F989898") {}
+
+$reflection = new ReflectionFunction('testReflectionIsArray');
+
+foreach ($reflection->getParameters() as $parameter) {
+ var_dump($parameter->isArray());
+}
+?>
+--EXPECT--
+bool(false)
+bool(false)
+bool(true)
+bool(false)
+bool(true)
+bool(false)
+bool(false)
+bool(true)
+bool(false)
diff --git a/ext/reflection/tests/ReflectionClass_newInstanceArgs_001.phpt b/ext/reflection/tests/ReflectionClass_newInstanceArgs_001.phpt
index d3a426de4c..3ad654dd84 100644
--- a/ext/reflection/tests/ReflectionClass_newInstanceArgs_001.phpt
+++ b/ext/reflection/tests/ReflectionClass_newInstanceArgs_001.phpt
@@ -38,13 +38,27 @@ $rcC = new ReflectionClass('C');
$rcD = new ReflectionClass('D');
$rcE = new ReflectionClass('E');
-$a1 = $rcA->newInstanceArgs();
-$a2 = $rcA->newInstanceArgs(array('x'));
-var_dump($a1, $a2);
+try {
+ var_dump($rcA->newInstanceArgs());
+} catch (Throwable $e) {
+ echo "Exception: " . $e->getMessage() . "\n";
+}
+try {
+ var_dump($rcA->newInstanceArgs(array('x')));
+} catch (Throwable $e) {
+ echo "Exception: " . $e->getMessage() . "\n";
+}
-$b1 = $rcB->newInstanceArgs();
-$b2 = $rcB->newInstanceArgs(array('x', 123));
-var_dump($b1, $b2);
+try {
+ var_dump($rcB->newInstanceArgs());
+} catch (Throwable $e) {
+ echo "Exception: " . $e->getMessage() . "\n";
+}
+try {
+ var_dump($rcB->newInstanceArgs(array('x', 123)));
+} catch (Throwable $e) {
+ echo "Exception: " . $e->getMessage() . "\n";
+}
try {
$rcC->newInstanceArgs();
@@ -73,25 +87,15 @@ try {
--EXPECTF--
Deprecated: Methods with the same name as their class will not be constructors in a future version of PHP; A has a deprecated constructor in %s on line %d
In constructor of class A
-In constructor of class A
object(A)#%d (0) {
}
+In constructor of class A
object(A)#%d (0) {
}
-
-Warning: Missing argument 1 for B::__construct() in %s on line 9
-
-Warning: Missing argument 2 for B::__construct() in %s on line 9
-
-Notice: Undefined variable: a in %s on line 10
-
-Notice: Undefined variable: b in %s on line 10
-In constructor of class B with args ,
+Exception: Too few arguments to function B::__construct(), 0 passed and exactly 2 expected
In constructor of class B with args x, 123
object(B)#%d (0) {
}
-object(B)#%d (0) {
-}
Access to non-public constructor of class C
Access to non-public constructor of class D
object(E)#%d (0) {
diff --git a/ext/reflection/tests/ReflectionClass_newInstance_001.phpt b/ext/reflection/tests/ReflectionClass_newInstance_001.phpt
index afa278a9a1..e29cc8734f 100644
--- a/ext/reflection/tests/ReflectionClass_newInstance_001.phpt
+++ b/ext/reflection/tests/ReflectionClass_newInstance_001.phpt
@@ -42,9 +42,16 @@ $a1 = $rcA->newInstance();
$a2 = $rcA->newInstance('x');
var_dump($a1, $a2);
-$b1 = $rcB->newInstance();
-$b2 = $rcB->newInstance('x', 123);
-var_dump($b1, $b2);
+try {
+ var_dump($rcB->newInstance());
+} catch (Throwable $e) {
+ echo "Exception: " . $e->getMessage() . "\n";
+}
+try {
+ var_dump($rcB->newInstance('x', 123));
+} catch (Throwable $e) {
+ echo "Exception: " . $e->getMessage() . "\n";
+}
try {
$rcC->newInstance();
@@ -78,20 +85,10 @@ object(A)#%d (0) {
}
object(A)#%d (0) {
}
-
-Warning: Missing argument 1 for B::__construct() in %s on line 9
-
-Warning: Missing argument 2 for B::__construct() in %s on line 9
-
-Notice: Undefined variable: a in %s on line 10
-
-Notice: Undefined variable: b in %s on line 10
-In constructor of class B with args ,
+Exception: Too few arguments to function B::__construct(), 0 passed and exactly 2 expected
In constructor of class B with args x, 123
object(B)#%d (0) {
}
-object(B)#%d (0) {
-}
Access to non-public constructor of class C
Access to non-public constructor of class D
object(E)#%d (0) {
diff --git a/ext/reflection/tests/ReflectionClass_toString_001.phpt b/ext/reflection/tests/ReflectionClass_toString_001.phpt
index b9a9b0d559..29d58420e3 100644
--- a/ext/reflection/tests/ReflectionClass_toString_001.phpt
+++ b/ext/reflection/tests/ReflectionClass_toString_001.phpt
@@ -12,9 +12,9 @@ echo $rc;
Class [ <internal:Reflection> class ReflectionClass implements Reflector ] {
- Constants [3] {
- Constant [ integer IS_IMPLICIT_ABSTRACT ] { 16 }
- Constant [ integer IS_EXPLICIT_ABSTRACT ] { 32 }
- Constant [ integer IS_FINAL ] { 4 }
+ Constant [ public integer IS_IMPLICIT_ABSTRACT ] { 16 }
+ Constant [ public integer IS_EXPLICIT_ABSTRACT ] { 32 }
+ Constant [ public integer IS_FINAL ] { 4 }
}
- Static properties [0] {
@@ -34,7 +34,7 @@ Class [ <internal:Reflection> class ReflectionClass implements Reflector ] {
Property [ <default> public $name ]
}
- - Methods [50] {
+ - Methods [52] {
Method [ <internal:Reflection> final private method __clone ] {
- Parameters [0] {
@@ -175,6 +175,12 @@ Class [ <internal:Reflection> class ReflectionClass implements Reflector ] {
}
}
+ Method [ <internal:Reflection> public method getReflectionConstants ] {
+
+ - Parameters [0] {
+ }
+ }
+
Method [ <internal:Reflection> public method getConstant ] {
- Parameters [1] {
@@ -182,6 +188,13 @@ Class [ <internal:Reflection> class ReflectionClass implements Reflector ] {
}
}
+ Method [ <internal:Reflection> public method getReflectionConstant ] {
+
+ - Parameters [1] {
+ Parameter #0 [ <required> $name ]
+ }
+ }
+
Method [ <internal:Reflection> public method getInterfaces ] {
- Parameters [0] {
diff --git a/ext/reflection/tests/ReflectionExtension_getClasses_basic.phpt b/ext/reflection/tests/ReflectionExtension_getClasses_basic.phpt
index 4eda22a3f9..c1f9d99f2b 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(14) {
+array(15) {
["ReflectionException"]=>
object(ReflectionClass)#2 (1) {
["name"]=>
@@ -70,13 +70,18 @@ array(14) {
["name"]=>
string(18) "ReflectionProperty"
}
- ["ReflectionExtension"]=>
+ ["ReflectionClassConstant"]=>
object(ReflectionClass)#14 (1) {
["name"]=>
+ string(23) "ReflectionClassConstant"
+ }
+ ["ReflectionExtension"]=>
+ object(ReflectionClass)#15 (1) {
+ ["name"]=>
string(19) "ReflectionExtension"
}
["ReflectionZendExtension"]=>
- object(ReflectionClass)#15 (1) {
+ object(ReflectionClass)#16 (1) {
["name"]=>
string(23) "ReflectionZendExtension"
}
diff --git a/ext/reflection/tests/ReflectionMethod_invokeArgs_error1.phpt b/ext/reflection/tests/ReflectionMethod_invokeArgs_error1.phpt
index ac97e3ed2a..c9d1e6379a 100644
--- a/ext/reflection/tests/ReflectionMethod_invokeArgs_error1.phpt
+++ b/ext/reflection/tests/ReflectionMethod_invokeArgs_error1.phpt
@@ -25,12 +25,9 @@ var_dump($methodWithArgs->invokeArgs($testClassInstance, array()));
--EXPECTF--
Method with args:
-Warning: Missing argument 1 for TestClass::methodWithArgs() in %s on line %d
-
-Warning: Missing argument 2 for TestClass::methodWithArgs() in %s on line %d
-
-Notice: Undefined variable: a in %s on line %d
-
-Notice: Undefined variable: b in %s on line %d
-Called methodWithArgs(, )
-NULL
+Fatal error: Uncaught Error: Too few arguments to function TestClass::methodWithArgs(), 0 passed and exactly 2 expected in %sReflectionMethod_invokeArgs_error1.php:5
+Stack trace:
+#0 [internal function]: TestClass->methodWithArgs()
+#1 %sReflectionMethod_invokeArgs_error1.php(19): ReflectionMethod->invokeArgs(Object(TestClass), Array)
+#2 {main}
+ thrown in %sReflectionMethod_invokeArgs_error1.php on line 5
diff --git a/ext/reflection/tests/ReflectionMethod_invokeArgs_error3.phpt b/ext/reflection/tests/ReflectionMethod_invokeArgs_error3.phpt
index 513cc1845f..41f682cf7f 100644
--- a/ext/reflection/tests/ReflectionMethod_invokeArgs_error3.phpt
+++ b/ext/reflection/tests/ReflectionMethod_invokeArgs_error3.phpt
@@ -14,7 +14,11 @@ class TestClass {
public static function staticMethod() {
echo "Called staticMethod()\n";
- var_dump($this);
+ try {
+ var_dump($this);
+ } catch (Throwable $e) {
+ echo "Exception: " . $e->getMessage() . "\n";
+ }
}
private static function privateMethod() {
@@ -103,9 +107,7 @@ NULL
Warning: ReflectionMethod::invokeArgs() expects parameter 1 to be object, boolean given in %s on line %d
NULL
Called staticMethod()
-
-Notice: Undefined variable: this in %s on line %d
-NULL
+Exception: Using $this when not in object context
NULL
Private method:
diff --git a/ext/reflection/tests/ReflectionMethod_invoke_basic.phpt b/ext/reflection/tests/ReflectionMethod_invoke_basic.phpt
index cbf358c062..c0830b277b 100644
--- a/ext/reflection/tests/ReflectionMethod_invoke_basic.phpt
+++ b/ext/reflection/tests/ReflectionMethod_invoke_basic.phpt
@@ -22,7 +22,11 @@ class TestClass {
public static function staticMethod() {
echo "Called staticMethod()\n";
- var_dump($this);
+ try {
+ var_dump($this);
+ } catch (Throwable $e) {
+ echo "Exception: " . $e->getMessage() . "\n";
+ }
}
private static function privateMethod() {
@@ -94,14 +98,10 @@ Static method:
Warning: ReflectionMethod::invoke() expects at least 1 parameter, 0 given in %s on line %d
NULL
Called staticMethod()
-
-Notice: Undefined variable: this in %s on line %d
-NULL
+Exception: Using $this when not in object context
NULL
Called staticMethod()
-
-Notice: Undefined variable: this in %s on line %d
-NULL
+Exception: Using $this when not in object context
NULL
Method that throws an exception:
diff --git a/ext/reflection/tests/ReflectionMethod_invoke_error2.phpt b/ext/reflection/tests/ReflectionMethod_invoke_error2.phpt
index a070c8f583..60a9ebae97 100644
--- a/ext/reflection/tests/ReflectionMethod_invoke_error2.phpt
+++ b/ext/reflection/tests/ReflectionMethod_invoke_error2.phpt
@@ -21,12 +21,9 @@ var_dump($methodWithArgs->invoke($testClassInstance));
--EXPECTF--
Method with args:
-Warning: Missing argument 1 for TestClass::methodWithArgs() in %s on line %d
-
-Warning: Missing argument 2 for TestClass::methodWithArgs() in %s on line %d
-
-Notice: Undefined variable: a in %s on line %d
-
-Notice: Undefined variable: b in %s on line %d
-Called methodWithArgs(, )
-NULL
+Fatal error: Uncaught Error: Too few arguments to function TestClass::methodWithArgs(), 0 passed and exactly 2 expected in %sReflectionMethod_invoke_error2.php:5
+Stack trace:
+#0 [internal function]: TestClass->methodWithArgs()
+#1 %sReflectionMethod_invoke_error2.php(15): ReflectionMethod->invoke(Object(TestClass))
+#2 {main}
+ thrown in %sReflectionMethod_invoke_error2.php on line 5
diff --git a/ext/reflection/tests/ReflectionProperty_getValue_error.phpt b/ext/reflection/tests/ReflectionProperty_getValue_error.phpt
index 62009d8bb9..c9bf0b0f25 100644
--- a/ext/reflection/tests/ReflectionProperty_getValue_error.phpt
+++ b/ext/reflection/tests/ReflectionProperty_getValue_error.phpt
@@ -15,7 +15,7 @@ class AnotherClass {
}
$instance = new TestClass();
-$instanceWithNoProperties = new AnotherClass();
+$invalidInstance = new AnotherClass();
$propInfo = new ReflectionProperty('TestClass', 'pub2');
echo "Too few args:\n";
@@ -45,9 +45,9 @@ catch(Exception $exc) {
echo $exc->getMessage();
}
-echo "\n\nInstance without property:\n";
+echo "\n\nInvalid instance:\n";
$propInfo = new ReflectionProperty('TestClass', 'pub2');
-var_dump($propInfo->getValue($instanceWithNoProperties));
+var_dump($propInfo->getValue($invalidInstance));
?>
--EXPECTF--
@@ -77,7 +77,10 @@ string(15) "static property"
Protected property:
Cannot access non-public member TestClass::prot
-Instance without property:
+Invalid instance:
-Notice: Undefined property: AnotherClass::$pub2 in %s on line %d
-NULL
+Fatal error: Uncaught ReflectionException: Given object is not an instance of the class this property was declared in in %s:47
+Stack trace:
+#0 %s(47): ReflectionProperty->getValue(Object(AnotherClass))
+#1 {main}
+ thrown in %s on line 47
diff --git a/ext/reflection/tests/ReflectionType_possible_types.phpt b/ext/reflection/tests/ReflectionType_possible_types.phpt
new file mode 100644
index 0000000000..81e08550f2
--- /dev/null
+++ b/ext/reflection/tests/ReflectionType_possible_types.phpt
@@ -0,0 +1,31 @@
+--TEST--
+ReflectionType possible types
+--FILE--
+<?php
+
+$functions = [
+ function(): void {},
+ function(): int {},
+ function(): float {},
+ function(): string {},
+ function(): bool {},
+ function(): array {},
+ function(): callable {},
+ function(): StdClass {}
+];
+
+foreach ($functions as $function) {
+ $reflectionFunc = new ReflectionFunction($function);
+ $returnType = $reflectionFunc->getReturnType();
+ var_dump($returnType->__toString());
+}
+?>
+--EXPECTF--
+string(4) "void"
+string(3) "int"
+string(5) "float"
+string(6) "string"
+string(4) "bool"
+string(5) "array"
+string(8) "callable"
+string(8) "StdClass"
diff --git a/ext/reflection/tests/bug29986.phpt b/ext/reflection/tests/bug29986.phpt
index 4c4d629f39..f5aa62a00b 100644
--- a/ext/reflection/tests/bug29986.phpt
+++ b/ext/reflection/tests/bug29986.phpt
@@ -20,11 +20,11 @@ Class [ <user> class just_constants ] {
@@ %s %d-%d
- Constants [5] {
- Constant [ boolean BOOLEAN_CONSTANT ] { 1 }
- Constant [ null NULL_CONSTANT ] { }
- Constant [ string STRING_CONSTANT ] { This is a string }
- Constant [ integer INTEGER_CONSTANT ] { 1000 }
- Constant [ float FLOAT_CONSTANT ] { 3.14159265 }
+ Constant [ public boolean BOOLEAN_CONSTANT ] { 1 }
+ Constant [ public null NULL_CONSTANT ] { }
+ Constant [ public string STRING_CONSTANT ] { This is a string }
+ Constant [ public integer INTEGER_CONSTANT ] { 1000 }
+ Constant [ public float FLOAT_CONSTANT ] { 3.14159265 }
}
- Static properties [0] {
diff --git a/ext/reflection/tests/bug38217.phpt b/ext/reflection/tests/bug38217.phpt
index cf007d9547..988f1c8953 100644
--- a/ext/reflection/tests/bug38217.phpt
+++ b/ext/reflection/tests/bug38217.phpt
@@ -18,7 +18,11 @@ class Object1 {
}
$class= new ReflectionClass('Object1');
-var_dump($class->newInstanceArgs());
+try {
+ var_dump($class->newInstanceArgs());
+} catch (Throwable $e) {
+ echo "Exception: " . $e->getMessage() . "\n";
+}
var_dump($class->newInstanceArgs(array('test')));
@@ -27,13 +31,7 @@ echo "Done\n";
--EXPECTF--
object(Object)#%d (0) {
}
-
-Warning: Missing argument 1 for Object1::__construct() in %s on line %d
-
-Notice: Undefined variable: var in %s on line %d
-NULL
-object(Object1)#%d (0) {
-}
+Exception: Too few arguments to function Object1::__construct(), 0 passed and exactly 1 expected
string(4) "test"
object(Object1)#%d (0) {
}
diff --git a/ext/reflection/tests/bug45765.phpt b/ext/reflection/tests/bug45765.phpt
index b0c1be2c4c..7963a03eea 100644
--- a/ext/reflection/tests/bug45765.phpt
+++ b/ext/reflection/tests/bug45765.phpt
@@ -31,7 +31,7 @@ Object of class [ <user> class foo extends foo2 ] {
@@ %s 7-21
- Constants [1] {
- Constant [ string BAR ] { foo's bar }
+ Constant [ public string BAR ] { foo's bar }
}
- Static properties [0] {
diff --git a/ext/session/mod_mm.c b/ext/session/mod_mm.c
index 1978caed3c..3f69897556 100644
--- a/ext/session/mod_mm.c
+++ b/ext/session/mod_mm.c
@@ -29,6 +29,7 @@
#include <sys/types.h>
#include <fcntl.h>
+#include "php_stdint.h"
#include "php_session.h"
#include "mod_mm.h"
#include "SAPI.h"
@@ -39,14 +40,11 @@
#define PS_MM_FILE "session_mm_"
-/* For php_uint32 */
-#include "ext/standard/basic_functions.h"
-
/* This list holds all data associated with one session. */
typedef struct ps_sd {
struct ps_sd *next;
- php_uint32 hv; /* hash value of key */
+ uint32_t hv; /* hash value of key */
time_t ctime; /* time of last change */
void *data;
size_t datalen; /* amount of valid data */
@@ -57,8 +55,8 @@ typedef struct ps_sd {
typedef struct {
MM *mm;
ps_sd **hash;
- php_uint32 hash_max;
- php_uint32 hash_cnt;
+ uint32_t hash_max;
+ uint32_t hash_cnt;
pid_t owner;
} ps_mm;
@@ -70,9 +68,9 @@ static ps_mm *ps_mm_instance = NULL;
# define ps_mm_debug(a)
#endif
-static inline php_uint32 ps_sd_hash(const char *data, int len)
+static inline uint32_t ps_sd_hash(const char *data, int len)
{
- php_uint32 h;
+ uint32_t h;
const char *e = data + len;
for (h = 2166136261U; data < e; ) {
@@ -85,7 +83,7 @@ static inline php_uint32 ps_sd_hash(const char *data, int len)
static void hash_split(ps_mm *data)
{
- php_uint32 nmax;
+ uint32_t nmax;
ps_sd **nhash;
ps_sd **ohash, **ehash;
ps_sd *ps, *next;
@@ -114,7 +112,7 @@ static void hash_split(ps_mm *data)
static ps_sd *ps_sd_new(ps_mm *data, const char *key)
{
- php_uint32 hv, slot;
+ uint32_t hv, slot;
ps_sd *sd;
int keylen;
@@ -155,7 +153,7 @@ static ps_sd *ps_sd_new(ps_mm *data, const char *key)
static void ps_sd_destroy(ps_mm *data, ps_sd *sd)
{
- php_uint32 slot;
+ uint32_t slot;
slot = ps_sd_hash(sd->key, strlen(sd->key)) & data->hash_max;
@@ -180,7 +178,7 @@ static void ps_sd_destroy(ps_mm *data, ps_sd *sd)
static ps_sd *ps_sd_lookup(ps_mm *data, const char *key, int rw)
{
- php_uint32 hv, slot;
+ uint32_t hv, slot;
ps_sd *ret, *prev;
hv = ps_sd_hash(key, strlen(key));
diff --git a/ext/session/mod_user.c b/ext/session/mod_user.c
index e6f162855a..c7c09ff781 100644
--- a/ext/session/mod_user.c
+++ b/ext/session/mod_user.c
@@ -85,7 +85,16 @@ PS_OPEN_FUNC(user)
ZVAL_STRING(&args[0], (char*)save_path);
ZVAL_STRING(&args[1], (char*)session_name);
- ps_call_handler(&PSF(open), 2, args, &retval);
+ zend_try {
+ ps_call_handler(&PSF(open), 2, args, &retval);
+ } zend_catch {
+ PS(session_status) = php_session_none;
+ if (!Z_ISUNDEF(retval)) {
+ zval_ptr_dtor(&retval);
+ }
+ zend_bailout();
+ } zend_end_try();
+
PS(mod_user_implemented) = 1;
FINISH;
diff --git a/ext/session/mod_user_class.c b/ext/session/mod_user_class.c
index 59b44f5f6f..a774d4bf9c 100644
--- a/ext/session/mod_user_class.c
+++ b/ext/session/mod_user_class.c
@@ -22,6 +22,10 @@
#include "php_session.h"
#define PS_SANITY_CHECK \
+ if (PS(session_status) != php_session_active) { \
+ php_error_docref(NULL, E_WARNING, "Session is not active"); \
+ RETURN_FALSE; \
+ } \
if (PS(default_mod) == NULL) { \
php_error_docref(NULL, E_CORE_ERROR, "Cannot call default session handler"); \
RETURN_FALSE; \
@@ -40,6 +44,7 @@ PHP_METHOD(SessionHandler, open)
{
char *save_path = NULL, *session_name = NULL;
size_t save_path_len, session_name_len;
+ int ret;
PS_SANITY_CHECK;
@@ -48,7 +53,15 @@ PHP_METHOD(SessionHandler, open)
}
PS(mod_user_is_open) = 1;
- RETVAL_BOOL(SUCCESS == PS(default_mod)->s_open(&PS(mod_data), save_path, session_name));
+
+ zend_try {
+ ret = PS(default_mod)->s_open(&PS(mod_data), save_path, session_name);
+ } zend_catch {
+ PS(session_status) = php_session_none;
+ zend_bailout();
+ } zend_end_try();
+
+ RETVAL_BOOL(SUCCESS == ret);
}
/* }}} */
@@ -56,6 +69,8 @@ PHP_METHOD(SessionHandler, open)
Wraps the old close handler */
PHP_METHOD(SessionHandler, close)
{
+ int ret;
+
PS_SANITY_CHECK_IS_OPEN;
// don't return on failure, since not closing the default handler
@@ -63,7 +78,15 @@ PHP_METHOD(SessionHandler, close)
zend_parse_parameters_none();
PS(mod_user_is_open) = 0;
- RETVAL_BOOL(SUCCESS == PS(default_mod)->s_close(&PS(mod_data)));
+
+ zend_try {
+ ret = PS(default_mod)->s_close(&PS(mod_data));
+ } zend_catch {
+ PS(session_status) = php_session_none;
+ zend_bailout();
+ } zend_end_try();
+
+ RETVAL_BOOL(SUCCESS == ret);
}
/* }}} */
diff --git a/ext/session/session.c b/ext/session/session.c
index 5b7841de5c..6fd0ee2f37 100644
--- a/ext/session/session.c
+++ b/ext/session/session.c
@@ -97,11 +97,13 @@ zend_class_entry *php_session_update_timestamp_iface_entry;
#define APPLY_TRANS_SID (PS(use_trans_sid) && !PS(use_only_cookies))
static void php_session_send_cookie(void);
+static void php_session_abort(void);
/* Dispatched by RINIT and by php_session_destroy */
static inline void php_rinit_session_globals(void) /* {{{ */
{
/* Do NOT init PS(mod_user_names) here! */
+ /* TODO: These could be moved to MINIT and removed. These should be initialized by php_rshutdown_session_globals() always when execution is finished. */
PS(id) = NULL;
PS(session_status) = php_session_none;
PS(mod_data) = NULL;
@@ -129,10 +131,15 @@ static inline void php_rshutdown_session_globals(void) /* {{{ */
zend_string_release(PS(id));
PS(id) = NULL;
}
+
if (PS(session_vars)) {
zend_string_release(PS(session_vars));
PS(session_vars) = NULL;
}
+
+ /* User save handlers may end up directly here by misuse, bugs in user script, etc. */
+ /* Set session status to prevent error while restoring save handler INI value. */
+ PS(session_status) = php_session_none;
}
/* }}} */
@@ -489,11 +496,6 @@ static void php_session_gc(void) /* {{{ */
nrand = (int) ((float) PS(gc_divisor) * php_combined_lcg());
if (nrand < PS(gc_probability)) {
PS(mod)->s_gc(&PS(mod_data), PS(gc_maxlifetime), &nrdels);
-#ifdef SESSION_DEBUG
- if (nrdels != -1) {
- php_error_docref(NULL, E_NOTICE, "purged %d expired session objects", nrdels);
- }
-#endif
}
}
} /* }}} */
@@ -502,7 +504,10 @@ static void php_session_initialize(void) /* {{{ */
{
zend_string *val = NULL;
+ PS(session_status) = php_session_active;
+
if (!PS(mod)) {
+ PS(session_status) = php_session_disabled;
php_error_docref(NULL, E_ERROR, "No storage module chosen - failed to initialize session");
return;
}
@@ -511,14 +516,19 @@ static void php_session_initialize(void) /* {{{ */
if (PS(mod)->s_open(&PS(mod_data), PS(save_path), PS(session_name)) == FAILURE
/* || PS(mod_data) == NULL */ /* FIXME: open must set valid PS(mod_data) with success */
) {
+ php_session_abort();
php_error_docref(NULL, E_ERROR, "Failed to initialize storage module: %s (path: %s)", PS(mod)->s_name, PS(save_path));
return;
}
/* If there is no ID, use session module to create one */
- if (!PS(id)) {
+ if (!PS(id) || !ZSTR_VAL(PS(id))[0]) {
+ if (PS(id)) {
+ zend_string_release(PS(id));
+ }
PS(id) = PS(mod)->s_create_sid(&PS(mod_data));
if (!PS(id)) {
+ php_session_abort();
php_error_docref(NULL, E_ERROR, "Failed to create session ID: %s (path: %s)", PS(mod)->s_name, PS(save_path));
return;
}
@@ -540,16 +550,15 @@ static void php_session_initialize(void) /* {{{ */
}
php_session_reset_id();
- PS(session_status) = php_session_active;
/* Read data */
php_session_track_init();
if (PS(mod)->s_read(&PS(mod_data), PS(id), &val, PS(gc_maxlifetime)) == FAILURE) {
+ php_session_abort();
/* Some broken save handler implementation returns FAILURE for non-existent session ID */
/* It's better to raise error for this, but disabled error for better compatibility */
- /*
- php_error_docref(NULL, E_NOTICE, "Failed to read session data: %s (path: %s)", PS(mod)->s_name, PS(save_path));
- */
+ php_error_docref(NULL, E_WARNING, "Failed to read session data: %s (path: %s)", PS(mod)->s_name, PS(save_path));
+ return;
}
/* GC must be done after read */
@@ -597,11 +606,16 @@ static void php_session_save_current_state(int write) /* {{{ */
}
if ((ret == FAILURE) && !EG(exception)) {
- php_error_docref(NULL, E_WARNING, "Failed to write session data (%s). Please "
- "verify that the current setting of session.save_path "
- "is correct (%s)",
- PS(mod)->s_name,
- PS(save_path));
+ if (!PS(mod_user_implemented)) {
+ php_error_docref(NULL, E_WARNING, "Failed to write session data (%s). Please "
+ "verify that the current setting of session.save_path "
+ "is correct (%s)",
+ PS(mod)->s_name,
+ PS(save_path));
+ } else {
+ php_error_docref(NULL, E_WARNING, "Failed to write session data using user "
+ "defined save handler. (session.save_path: %s)", PS(save_path));
+ }
}
}
}
@@ -1123,7 +1137,7 @@ static ps_serializer ps_serializers[MAX_SERIALIZERS + 1] = {
PHPAPI int php_session_register_serializer(const char *name, zend_string *(*encode)(PS_SERIALIZER_ENCODE_ARGS), int (*decode)(PS_SERIALIZER_DECODE_ARGS)) /* {{{ */
{
- int ret = -1;
+ int ret = FAILURE;
int i;
for (i = 0; i < MAX_SERIALIZERS; i++) {
@@ -1132,7 +1146,7 @@ PHPAPI int php_session_register_serializer(const char *name, zend_string *(*enco
ps_serializers[i].encode = encode;
ps_serializers[i].decode = decode;
ps_serializers[i + 1].name = NULL;
- ret = 0;
+ ret = SUCCESS;
break;
}
}
@@ -1154,13 +1168,13 @@ static ps_module *ps_modules[MAX_MODULES + 1] = {
PHPAPI int php_session_register_module(ps_module *ptr) /* {{{ */
{
- int ret = -1;
+ int ret = FAILURE;
int i;
for (i = 0; i < MAX_MODULES; i++) {
if (!ps_modules[i]) {
ps_modules[i] = ptr;
- ret = 0;
+ ret = SUCCESS;
break;
}
}
@@ -1309,11 +1323,13 @@ static int php_session_cache_limiter(void) /* {{{ */
php_session_cache_limiter_t *lim;
if (PS(cache_limiter)[0] == '\0') return 0;
+ if (PS(session_status) != php_session_active) return -1;
if (SG(headers_sent)) {
const char *output_start_filename = php_output_get_start_filename();
int output_start_lineno = php_output_get_start_lineno();
+ php_session_abort();
if (output_start_filename) {
php_error_docref(NULL, E_WARNING, "Cannot send session cache limiter - headers already sent (output started at %s:%d)", output_start_filename, output_start_lineno);
} else {
@@ -1681,8 +1697,8 @@ PHPAPI void php_session_start(void) /* {{{ */
static void php_session_flush(int write) /* {{{ */
{
if (PS(session_status) == php_session_active) {
- PS(session_status) = php_session_none;
php_session_save_current_state(write);
+ PS(session_status) = php_session_none;
}
}
/* }}} */
@@ -1690,10 +1706,10 @@ static void php_session_flush(int write) /* {{{ */
static void php_session_abort(void) /* {{{ */
{
if (PS(session_status) == php_session_active) {
- PS(session_status) = php_session_none;
if (PS(mod_data) || PS(mod_user_implemented)) {
PS(mod)->s_close(&PS(mod_data));
}
+ PS(session_status) = php_session_none;
}
}
/* }}} */
@@ -2068,13 +2084,13 @@ static PHP_FUNCTION(session_regenerate_id)
return;
}
- if (SG(headers_sent) && PS(use_cookies)) {
- php_error_docref(NULL, E_WARNING, "Cannot regenerate session id - headers already sent");
+ if (PS(session_status) != php_session_active) {
+ php_error_docref(NULL, E_WARNING, "Cannot regenerate session id - session is not active");
RETURN_FALSE;
}
- if (PS(session_status) != php_session_active) {
- php_error_docref(NULL, E_WARNING, "Cannot regenerate session id - session is not active");
+ if (SG(headers_sent) && PS(use_cookies)) {
+ php_error_docref(NULL, E_WARNING, "Cannot regenerate session id - headers already sent");
RETURN_FALSE;
}
@@ -2110,15 +2126,18 @@ static PHP_FUNCTION(session_regenerate_id)
PS(session_vars) = NULL;
}
zend_string_release(PS(id));
- PS(id) = PS(mod)->s_create_sid(&PS(mod_data));
- if (!PS(id)) {
+ PS(id) = NULL;
+
+ if (PS(mod)->s_open(&PS(mod_data), PS(save_path), PS(session_name)) == FAILURE) {
PS(session_status) = php_session_none;
- php_error_docref(NULL, E_RECOVERABLE_ERROR, "Failed to create new session ID: %s (path: %s)", PS(mod)->s_name, PS(save_path));
+ php_error_docref(NULL, E_RECOVERABLE_ERROR, "Failed to open session: %s (path: %s)", PS(mod)->s_name, PS(save_path));
RETURN_FALSE;
}
- if (PS(mod)->s_open(&PS(mod_data), PS(save_path), PS(session_name)) == FAILURE) {
+
+ PS(id) = PS(mod)->s_create_sid(&PS(mod_data));
+ if (!PS(id)) {
PS(session_status) = php_session_none;
- php_error_docref(NULL, E_RECOVERABLE_ERROR, "Failed to create(open) session ID: %s (path: %s)", PS(mod)->s_name, PS(save_path));
+ php_error_docref(NULL, E_RECOVERABLE_ERROR, "Failed to create new session ID: %s (path: %s)", PS(mod)->s_name, PS(save_path));
RETURN_FALSE;
}
if (PS(use_strict_mode) && PS(mod)->s_validate_sid &&
@@ -2126,6 +2145,7 @@ static PHP_FUNCTION(session_regenerate_id)
zend_string_release(PS(id));
PS(id) = PS(mod)->s_create_sid(&PS(mod_data));
if (!PS(id)) {
+ PS(mod)->s_close(&PS(mod_data));
PS(session_status) = php_session_none;
php_error_docref(NULL, E_RECOVERABLE_ERROR, "Failed to create session ID by collision: %s (path: %s)", PS(mod)->s_name, PS(save_path));
RETURN_FALSE;
@@ -2133,6 +2153,7 @@ static PHP_FUNCTION(session_regenerate_id)
}
/* Read is required to make new session data at this point. */
if (PS(mod)->s_read(&PS(mod_data), PS(id), &data, PS(gc_maxlifetime)) == FAILURE) {
+ PS(mod)->s_close(&PS(mod_data));
PS(session_status) = php_session_none;
php_error_docref(NULL, E_RECOVERABLE_ERROR, "Failed to create(read) session ID: %s (path: %s)", PS(mod)->s_name, PS(save_path));
RETURN_FALSE;
@@ -2304,11 +2325,6 @@ static PHP_FUNCTION(session_start)
RETURN_FALSE;
}
- if (PS(id) && !(ZSTR_LEN(PS(id)))) {
- php_error_docref(NULL, E_WARNING, "Cannot start session with empty session ID");
- RETURN_FALSE;
- }
-
/* set options */
if (options) {
ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(options), num_idx, str_idx, value) {
diff --git a/ext/session/tests/016.phpt b/ext/session/tests/016.phpt
index 82a85d2705..f23605eb47 100644
--- a/ext/session/tests/016.phpt
+++ b/ext/session/tests/016.phpt
@@ -22,5 +22,5 @@ session_write_close();
print "I live\n";
?>
--EXPECTF--
-Warning: session_write_close(): Failed to write session data (files). Please verify that the current setting of session.save_path is correct (123;:/really%scompletely:::/invalid;;,23123;213) in %s on line %d
+Warning: session_start(): Failed to read session data: files (path: 123;:/really%scompletely:::/invalid;;,23123;213) in %s on line %d
I live
diff --git a/ext/session/tests/bug55688.phpt b/ext/session/tests/bug55688.phpt
index 8db48384af..b073dc3c5c 100644
--- a/ext/session/tests/bug55688.phpt
+++ b/ext/session/tests/bug55688.phpt
@@ -12,4 +12,4 @@ $x = new SessionHandler;
$x->gc(1);
?>
--EXPECTF--
-Warning: SessionHandler::gc(): Parent session handler is not open in %s on line %d
+Warning: SessionHandler::gc(): Session is not active in %s on line %d
diff --git a/ext/session/tests/bug60634.phpt b/ext/session/tests/bug60634.phpt
index 86dcb11526..b2f5076287 100644
--- a/ext/session/tests/bug60634.phpt
+++ b/ext/session/tests/bug60634.phpt
@@ -39,8 +39,17 @@ session_start();
session_write_close();
echo "um, hi\n";
+/*
+FIXME: Since session module try to write/close session data in
+RSHUTDOWN, write() is executed twices. This is caused by undefined
+function error and zend_bailout(). Current session module codes
+depends on this behavior. These codes should be modified to remove
+multiple write().
+*/
+
?>
--EXPECTF--
write: goodbye cruel world
+write: goodbye cruel world
close: goodbye cruel world
diff --git a/ext/session/tests/bug60634_error_1.phpt b/ext/session/tests/bug60634_error_1.phpt
index d0733f5a5a..fa76ff522a 100644
--- a/ext/session/tests/bug60634_error_1.phpt
+++ b/ext/session/tests/bug60634_error_1.phpt
@@ -41,6 +41,11 @@ session_start();
session_write_close();
echo "um, hi\n";
+/*
+FIXME: Something wrong. It should try to close after error, otherwise session
+may keep "open" state.
+*/
+
?>
--EXPECTF--
write: goodbye cruel world
@@ -51,3 +56,4 @@ Stack trace:
#1 %s(%d): session_write_close()
#2 {main}
thrown in %s on line %d
+
diff --git a/ext/session/tests/bug61728.phpt b/ext/session/tests/bug61728.phpt
index 3f8dbeb58a..2780d7b7e2 100644
--- a/ext/session/tests/bug61728.phpt
+++ b/ext/session/tests/bug61728.phpt
@@ -8,32 +8,34 @@ function output_html($ext) {
return strlen($ext);
}
-function open ($save_path, $session_name) {
+function open ($save_path, $session_name) {
return true;
-}
+}
-function close() {
+function close() {
return true;
-}
+}
-function read ($id) {
-}
+function read ($id) {
+ return '';
+}
-function write ($id, $sess_data) {
+function write ($id, $sess_data) {
ob_start("output_html");
echo "laruence";
ob_end_flush();
return true;
-}
+}
-function destroy ($id) {
-}
+function destroy ($id) {
+ return true;
+}
-function gc ($maxlifetime) {
- return true;
-}
+function gc ($maxlifetime) {
+ return true;
+}
-session_set_save_handler ("open", "close", "read", "write", "destroy", "gc");
+session_set_save_handler ("open", "close", "read", "write", "destroy", "gc");
session_start();
--EXPECTF--
8
diff --git a/ext/session/tests/bug67972.phpt b/ext/session/tests/bug67972.phpt
index 63ed3a95b8..92c3044ac5 100644
--- a/ext/session/tests/bug67972.phpt
+++ b/ext/session/tests/bug67972.phpt
@@ -7,4 +7,5 @@ Bug #67972: SessionHandler Invalid memory read create_sid()
(new SessionHandler)->create_sid();
--EXPECTF--
-Fatal error: SessionHandler::create_sid(): Cannot call default session handler in %s on line %d
+Warning: SessionHandler::create_sid(): Session is not active in %s on line %d
+
diff --git a/ext/session/tests/bug68063.phpt b/ext/session/tests/bug68063.phpt
index d3da470d06..ec3a70d156 100644
--- a/ext/session/tests/bug68063.phpt
+++ b/ext/session/tests/bug68063.phpt
@@ -3,18 +3,22 @@ Bug #68063 (Empty session IDs do still start sessions)
--SKIPIF--
<?php include('skipif.inc'); ?>
--INI--
+session.use_strict_mode=0
+session.hash_function=1
+session.hash_bits_per_character=4
--FILE--
<?php
+// Empty session ID may happen by browser bugs
+
// Could also be set with a cookie like "PHPSESSID=; path=/"
session_id('');
-// Will still start the session and return true
+// Start the session with empty string should result in new session ID
var_dump(session_start());
-// Returns an empty string
+// Returns newly created session ID
var_dump(session_id());
?>
--EXPECTF--
-Warning: session_start(): Cannot start session with empty session ID in %s on line %d
-bool(false)
-string(0) ""
+bool(true)
+string(40) "%s"
diff --git a/ext/session/tests/bug70133.phpt b/ext/session/tests/bug70133.phpt
new file mode 100644
index 0000000000..3e019e483b
--- /dev/null
+++ b/ext/session/tests/bug70133.phpt
@@ -0,0 +1,41 @@
+--TEST--
+Bug #70133 (Extended SessionHandler::read is ignoring $session_id when calling parent)
+--SKIPIF--
+<?php include('skipif.inc'); ?>
+--INI--
+session.save_handler=files
+session.save_path=
+session.use_strict_mode=0
+--FILE--
+<?php
+
+class CustomReadHandler extends \SessionHandler {
+
+ public function read($session_id) {
+ return parent::read('mycustomsession');
+ }
+}
+
+ob_start();
+
+session_set_save_handler(new CustomReadHandler(), true);
+
+session_id('mycustomsession');
+session_start();
+$_SESSION['foo'] = 'hoge';
+var_dump(session_id());
+session_commit();
+
+session_id('otherid');
+session_start();
+var_dump($_SESSION);
+var_dump(session_id());
+
+?>
+--EXPECT--
+string(15) "mycustomsession"
+array(1) {
+ ["foo"]=>
+ string(4) "hoge"
+}
+string(7) "otherid"
diff --git a/ext/session/tests/bug71162.phpt b/ext/session/tests/bug71162.phpt
new file mode 100644
index 0000000000..fe38ea2999
--- /dev/null
+++ b/ext/session/tests/bug71162.phpt
@@ -0,0 +1,79 @@
+--TEST--
+updateTimestamp never called when session data is empty
+--INI--
+session.use_strict_mode=0
+--XFAIL--
+Current session module is designed to write empty session always. In addition,
+current session module only supports SessionHandlerInterface only from PHP 7.0.
+--FILE--
+<?php
+class MySessionHandler implements SessionUpdateTimestampHandlerInterface
+{
+ public function open($path, $sessname) {
+ return TRUE;
+ }
+
+ public function close() {
+ return TRUE;
+ }
+
+ public function read($sessid) {
+ return '';
+ }
+
+ public function write($sessid, $sessdata) {
+ echo __FUNCTION__, PHP_EOL;
+ return TRUE;
+ }
+
+ public function destroy($sessid) {
+ return TRUE;
+ }
+
+ public function gc($maxlifetime) {
+ return TRUE;
+ }
+
+ public function create_sid() {
+ return sha1(random_bytes(32));
+ }
+
+ public function validateId($sid) {
+ return TRUE;
+ }
+
+ public function updateTimestamp($sessid, $sessdata) {
+ echo __FUNCTION__, PHP_EOL;
+ return TRUE;
+ }
+}
+
+ob_start();
+$handler = new MySessionHandler();
+session_set_save_handler($handler);
+
+session_id(sha1(''));
+var_dump(session_id());
+var_dump(session_start(['lazy_write'=>1]));
+session_commit();
+
+session_id(sha1(''));
+var_dump(session_id());
+var_dump(session_start(['lazy_write'=>1]));
+session_commit();
+
+session_id(sha1(''));
+var_dump(session_id());
+var_dump(session_start(['lazy_write'=>0]));
+session_commit();
+?>
+--EXPECT--
+string(40) "da39a3ee5e6b4b0d3255bfef95601890afd80709"
+bool(true)
+write
+string(40) "da39a3ee5e6b4b0d3255bfef95601890afd80709"
+bool(true)
+updateTimestamp
+string(40) "da39a3ee5e6b4b0d3255bfef95601890afd80709"
+bool(true)
+write
diff --git a/ext/session/tests/bug71186.phpt b/ext/session/tests/bug71186.phpt
new file mode 100644
index 0000000000..5eeba6035f
--- /dev/null
+++ b/ext/session/tests/bug71186.phpt
@@ -0,0 +1,32 @@
+--TEST--
+Bug #71186 session.hash_function - algorithm changes
+--SKIPIF--
+<?php include('skipif.inc'); ?>
+--INI--
+session.hash_function=sha512
+session.save_handler=files
+--FILE--
+<?php
+ob_start();
+ini_set('session.use_strict_mode', 1);
+
+session_start();
+$orig = session_id();
+session_regenerate_id();
+$new = session_id();
+var_dump(strlen($orig),strlen($new));
+session_commit();
+
+ini_set('session.hash_function','sha1');
+session_id('invalid');
+session_start();
+$orig = session_id();
+session_regenerate_id();
+$new = session_id();
+var_dump(strlen($orig),strlen($new));
+?>
+--EXPECT--
+int(128)
+int(128)
+int(40)
+int(40)
diff --git a/ext/session/tests/rfc1867_sid_invalid.phpt b/ext/session/tests/rfc1867_sid_invalid.phpt
index 4dd8f1f979..a9114e3e1d 100644
--- a/ext/session/tests/rfc1867_sid_invalid.phpt
+++ b/ext/session/tests/rfc1867_sid_invalid.phpt
@@ -47,13 +47,13 @@ session_destroy();
--EXPECTF--
Warning: Unknown: The session id is too long or contains illegal characters, valid characters are a-z, A-Z, 0-9 and '-,' in Unknown on line 0
-Warning: Unknown: The session id is too long or contains illegal characters, valid characters are a-z, A-Z, 0-9 and '-,' in Unknown on line 0
+Warning: Unknown: Failed to read session data: files (path: ) in Unknown on line 0
Warning: Unknown: Failed to write session data (files). Please verify that the current setting of session.save_path is correct () in Unknown on line 0
Warning: Unknown: The session id is too long or contains illegal characters, valid characters are a-z, A-Z, 0-9 and '-,' in Unknown on line 0
-Warning: Unknown: The session id is too long or contains illegal characters, valid characters are a-z, A-Z, 0-9 and '-,' in Unknown on line 0
+Warning: Unknown: Failed to read session data: files (path: ) in Unknown on line 0
Warning: Unknown: Failed to write session data (files). Please verify that the current setting of session.save_path is correct () in Unknown on line 0
string(%d) "%s"
diff --git a/ext/session/tests/session_save_path_variation2.phpt b/ext/session/tests/session_save_path_variation2.phpt
index 4cf44b75a4..60675aec3c 100644
--- a/ext/session/tests/session_save_path_variation2.phpt
+++ b/ext/session/tests/session_save_path_variation2.phpt
@@ -33,8 +33,12 @@ ob_end_flush();
string(5) "/blah"
Warning: session_start(): open(%sblah%e%s, O_RDWR) failed: No such file or directory (2) in %s on line %d
-bool(true)
+
+Warning: session_start(): Failed to read session data: files (path: %sblah) in %s on line %d
+bool(false)
string(5) "/blah"
-bool(true)
+
+Warning: session_destroy(): Trying to destroy uninitialized session in %s on line %d
+bool(false)
string(5) "/blah"
Done
diff --git a/ext/session/tests/session_save_path_variation3.phpt b/ext/session/tests/session_save_path_variation3.phpt
index b064f30183..1d290d95b3 100644
--- a/ext/session/tests/session_save_path_variation3.phpt
+++ b/ext/session/tests/session_save_path_variation3.phpt
@@ -33,8 +33,12 @@ ob_end_flush();
string(5) "/blah"
Warning: session_start(): open(%s, O_RDWR) failed: No such file or directory (2) in %s on line %d
-bool(true)
+
+Warning: session_start(): Failed to read session data: files (path: %sblah) in %s on line %d
+bool(false)
string(5) "/blah"
-bool(true)
+
+Warning: session_destroy(): Trying to destroy uninitialized session in %s on line %d
+bool(false)
string(5) "/blah"
Done
diff --git a/ext/session/tests/session_set_save_handler_class_002.phpt b/ext/session/tests/session_set_save_handler_class_002.phpt
index b75a7e6390..880bc33425 100644
--- a/ext/session/tests/session_set_save_handler_class_002.phpt
+++ b/ext/session/tests/session_set_save_handler_class_002.phpt
@@ -34,7 +34,7 @@ class MySession2 extends SessionHandler {
}
public function read($id) {
- return @file_get_contents($this->path . $id);
+ return (string)@file_get_contents($this->path . $id);
}
public function write($id, $data) {
diff --git a/ext/session/tests/session_set_save_handler_class_005.phpt b/ext/session/tests/session_set_save_handler_class_005.phpt
index 5be735306a..1b8c1ce645 100644
--- a/ext/session/tests/session_set_save_handler_class_005.phpt
+++ b/ext/session/tests/session_set_save_handler_class_005.phpt
@@ -33,7 +33,7 @@ class MySession6 extends SessionHandler {
$handler = new MySession6;
session_set_save_handler($handler);
-session_start();
+var_dump(session_start());
var_dump(session_id(), ini_get('session.save_handler'), $_SESSION);
@@ -45,13 +45,12 @@ session_unset();
*** Testing session_set_save_handler() : incomplete implementation ***
Warning: SessionHandler::read(): Parent session handler is not open in %ssession_set_save_handler_class_005.php on line %d
+
+Warning: SessionHandler::close(): Parent session handler is not open in %ssession_set_save_handler_class_005.php on line %d
+
+Warning: session_start(): Failed to read session data: user (%s) in %ssession_set_save_handler_class_005.php on line %d
+bool(false)
string(%d) "%s"
string(4) "user"
array(0) {
}
-
-Warning: SessionHandler::write(): Parent session handler is not open in %ssession_set_save_handler_class_005.php on line %d
-
-Warning: session_write_close(): Failed to write session data %s in %ssession_set_save_handler_class_005.php on line %d
-
-Warning: SessionHandler::close(): Parent session handler is not open in %ssession_set_save_handler_class_005.php on line %d
diff --git a/ext/session/tests/session_set_save_handler_class_012.phpt b/ext/session/tests/session_set_save_handler_class_012.phpt
index 91e751bdfc..0ce03f865e 100644
--- a/ext/session/tests/session_set_save_handler_class_012.phpt
+++ b/ext/session/tests/session_set_save_handler_class_012.phpt
@@ -38,7 +38,7 @@ class MySession extends SessionHandler {
$oldHandler = ini_get('session.save_handler');
$handler = new MySession;
session_set_save_handler($handler);
-session_start();
+var_dump(session_start());
var_dump(session_id(), $oldHandler, ini_get('session.save_handler'), $handler->i, $_SESSION);
@@ -50,15 +50,14 @@ Warning: SessionHandler::open() expects exactly 2 parameters, 0 given in %s on l
Read %s
Warning: SessionHandler::read(): Parent session handler is not open in %s on line %d
+
+Warning: SessionHandler::close(): Parent session handler is not open in %s on line %d
+
+Warning: session_start(): Failed to read session data: user (%s) in %s on line %d
+bool(false)
string(%d) "%s"
string(5) "files"
string(4) "user"
int(2)
array(0) {
}
-
-Warning: SessionHandler::write(): Parent session handler is not open in Unknown on line 0
-
-Warning: session_write_close(): Failed to write session data %s in %s on line %d
-
-Warning: SessionHandler::close(): Parent session handler is not open in Unknown on line 0
diff --git a/ext/session/tests/session_set_save_handler_class_016.phpt b/ext/session/tests/session_set_save_handler_class_016.phpt
index 521bd86f31..4095813c9d 100644
--- a/ext/session/tests/session_set_save_handler_class_016.phpt
+++ b/ext/session/tests/session_set_save_handler_class_016.phpt
@@ -10,10 +10,10 @@ session.name=PHPSESSID
ob_start();
-/*
+/*
* Prototype : bool session_set_save_handler(SessionHandlerInterface $handler [, bool $register_shutdown_function = true])
* Description : Sets user-level session storage functions
- * Source code : ext/session/session.c
+ * Source code : ext/session/session.c
*/
echo "*** Testing session_set_save_handler() function: class with create_sid ***\n";
@@ -34,7 +34,7 @@ class MySession2 extends SessionHandler {
}
public function read($id) {
- return @file_get_contents($this->path . $id);
+ return (string)@file_get_contents($this->path . $id);
}
public function write($id, $data) {
diff --git a/ext/session/tests/session_set_save_handler_class_017.phpt b/ext/session/tests/session_set_save_handler_class_017.phpt
index 6f42d7809a..b8e7d7a7ad 100644
--- a/ext/session/tests/session_set_save_handler_class_017.phpt
+++ b/ext/session/tests/session_set_save_handler_class_017.phpt
@@ -34,7 +34,7 @@ class MySession2 extends SessionHandler {
}
public function read($id) {
- return @file_get_contents($this->path . $id);
+ return (string)@file_get_contents($this->path . $id);
}
public function write($id, $data) {
diff --git a/ext/session/tests/session_set_save_handler_error4.phpt b/ext/session/tests/session_set_save_handler_error4.phpt
index be3429b084..4267195ee1 100644
--- a/ext/session/tests/session_set_save_handler_error4.phpt
+++ b/ext/session/tests/session_set_save_handler_error4.phpt
@@ -24,7 +24,7 @@ session_set_save_handler("callback", "callback", "callback", "echo", "callback",
session_set_save_handler("callback", "callback", "callback", "callback", "echo", "callback");
session_set_save_handler("callback", "callback", "callback", "callback", "callback", "echo");
session_set_save_handler("callback", "callback", "callback", "callback", "callback", "callback");
-session_start();
+var_dump(session_start());
ob_end_flush();
?>
--EXPECTF--
@@ -39,3 +39,6 @@ Warning: session_set_save_handler(): Argument 4 is not a valid callback in %s on
Warning: session_set_save_handler(): Argument 5 is not a valid callback in %s on line %d
Warning: session_set_save_handler(): Argument 6 is not a valid callback in %s on line %d
+
+Warning: session_start(): Failed to read session data: user (%s) in %s on line %d
+bool(false)
diff --git a/ext/session/tests/session_set_save_handler_iface_001.phpt b/ext/session/tests/session_set_save_handler_iface_001.phpt
index 03ee42865c..6943d59cbe 100644
--- a/ext/session/tests/session_set_save_handler_iface_001.phpt
+++ b/ext/session/tests/session_set_save_handler_iface_001.phpt
@@ -34,7 +34,7 @@ class MySession2 implements SessionHandlerInterface {
}
public function read($id) {
- return @file_get_contents($this->path . $id);
+ return (string)@file_get_contents($this->path . $id);
}
public function write($id, $data) {
diff --git a/ext/session/tests/session_set_save_handler_iface_002.phpt b/ext/session/tests/session_set_save_handler_iface_002.phpt
index 40c9ac6825..204d88c785 100644
--- a/ext/session/tests/session_set_save_handler_iface_002.phpt
+++ b/ext/session/tests/session_set_save_handler_iface_002.phpt
@@ -43,7 +43,7 @@ class MySession2 implements MySessionHandlerInterface {
}
public function read($id) {
- return @file_get_contents($this->path . $id);
+ return (string)@file_get_contents($this->path . $id);
}
public function write($id, $data) {
diff --git a/ext/session/tests/sessionhandler_open_001.phpt b/ext/session/tests/sessionhandler_open_001.phpt
index 6ade9e00a5..e6e913a6a5 100644
--- a/ext/session/tests/sessionhandler_open_001.phpt
+++ b/ext/session/tests/sessionhandler_open_001.phpt
@@ -16,4 +16,11 @@ print "Done!\n";
?>
--EXPECTF--
+Warning: SessionHandler::open(): Session is not active in %s on line 5
+
+Warning: SessionHandler::open(): Session is not active in %s on line 6
+
+Warning: SessionHandler::open(): Session is not active in %s on line 7
+
+Warning: SessionHandler::open(): Session is not active in %s on line 8
Done!
diff --git a/ext/shmop/php_shmop.h b/ext/shmop/php_shmop.h
index ca7f703c9a..b5cfd3b3c2 100644
--- a/ext/shmop/php_shmop.h
+++ b/ext/shmop/php_shmop.h
@@ -38,7 +38,7 @@ PHP_FUNCTION(shmop_write);
PHP_FUNCTION(shmop_delete);
#ifdef PHP_WIN32
-typedef int key_t;
+# include "win32/ipc.h"
#endif
struct php_shmop
diff --git a/ext/simplexml/php_simplexml.h b/ext/simplexml/php_simplexml.h
index a6d5ffc7ff..ea29f1399c 100644
--- a/ext/simplexml/php_simplexml.h
+++ b/ext/simplexml/php_simplexml.h
@@ -45,9 +45,6 @@ extern zend_module_entry simplexml_module_entry;
PHP_MINIT_FUNCTION(simplexml);
PHP_MSHUTDOWN_FUNCTION(simplexml);
-#ifdef HAVE_SPL
-PHP_RINIT_FUNCTION(simplexml);
-#endif
PHP_MINFO_FUNCTION(simplexml);
typedef enum {
diff --git a/ext/snmp/snmp.c b/ext/snmp/snmp.c
index 67e39f1645..38d82db670 100644
--- a/ext/snmp/snmp.c
+++ b/ext/snmp/snmp.c
@@ -33,10 +33,7 @@
#include "php_snmp.h"
#include "zend_exceptions.h"
-
-#if HAVE_SPL
#include "ext/spl/spl_exceptions.h"
-#endif
#if HAVE_SNMP
@@ -2414,11 +2411,7 @@ PHP_MINIT_FUNCTION(snmp)
/* Register SNMPException class */
INIT_CLASS_ENTRY(cex, "SNMPException", NULL);
-#ifdef HAVE_SPL
php_snmp_exception_ce = zend_register_internal_class_ex(&cex, spl_ce_RuntimeException);
-#else
- php_snmp_exception_ce = zend_register_internal_class_ex(&cex, zend_ce_exception);
-#endif
return SUCCESS;
}
@@ -2450,14 +2443,10 @@ PHP_MINFO_FUNCTION(snmp)
/* {{{ snmp_module_deps[]
*/
-#if ZEND_MODULE_API_NO >= 20050922
static const zend_module_dep snmp_module_deps[] = {
-#ifdef HAVE_SPL
ZEND_MOD_REQUIRED("spl")
-#endif
ZEND_MOD_END
};
-#endif
/* }}} */
/* {{{ snmp_module_entry
diff --git a/ext/soap/php_encoding.c b/ext/soap/php_encoding.c
index fc389f2365..803a5cd596 100644
--- a/ext/soap/php_encoding.c
+++ b/ext/soap/php_encoding.c
@@ -1164,13 +1164,8 @@ static xmlNodePtr to_xml_null(encodeTypePtr type, zval *data, int style, xmlNode
static void set_zval_property(zval* object, char* name, zval* val)
{
- zend_class_entry *old_scope;
-
- old_scope = EG(scope);
- EG(scope) = Z_OBJCE_P(object);
- add_property_zval(object, name, val);
+ zend_update_property(Z_OBJCE_P(object), object, name, strlen(name), val);
if (Z_REFCOUNTED_P(val)) Z_DELREF_P(val);
- EG(scope) = old_scope;
}
static zval* get_zval_property(zval* object, char* name, zval *rv)
@@ -1181,15 +1176,15 @@ static zval* get_zval_property(zval* object, char* name, zval *rv)
zend_class_entry *old_scope;
ZVAL_STRING(&member, name);
- old_scope = EG(scope);
- EG(scope) = Z_OBJCE_P(object);
+ old_scope = EG(fake_scope);
+ EG(fake_scope) = Z_OBJCE_P(object);
data = Z_OBJ_HT_P(object)->read_property(object, &member, BP_VAR_IS, NULL, rv);
if (data == &EG(uninitialized_zval)) {
/* Hack for bug #32455 */
zend_property_info *property_info;
property_info = zend_get_property_info(Z_OBJCE_P(object), Z_STR(member), 1);
- EG(scope) = old_scope;
+ EG(fake_scope) = old_scope;
if (property_info != ZEND_WRONG_PROPERTY_INFO && property_info &&
zend_hash_exists(Z_OBJPROP_P(object), property_info->name)) {
zval_ptr_dtor(&member);
@@ -1199,7 +1194,7 @@ static zval* get_zval_property(zval* object, char* name, zval *rv)
return NULL;
}
zval_ptr_dtor(&member);
- EG(scope) = old_scope;
+ EG(fake_scope) = old_scope;
return data;
} else if (Z_TYPE_P(object) == IS_ARRAY) {
zval *data_ptr;
@@ -1218,10 +1213,10 @@ static void unset_zval_property(zval* object, char* name)
zend_class_entry *old_scope;
ZVAL_STRING(&member, name);
- old_scope = EG(scope);
- EG(scope) = Z_OBJCE_P(object);
+ old_scope = EG(fake_scope);
+ EG(fake_scope) = Z_OBJCE_P(object);
Z_OBJ_HT_P(object)->unset_property(object, &member, NULL);
- EG(scope) = old_scope;
+ EG(fake_scope) = old_scope;
zval_ptr_dtor(&member);
} else if (Z_TYPE_P(object) == IS_ARRAY) {
zend_hash_str_del(Z_ARRVAL_P(object), name, strlen(name));
diff --git a/ext/soap/php_packet_soap.c b/ext/soap/php_packet_soap.c
index c835c84dff..81a8d18b29 100644
--- a/ext/soap/php_packet_soap.c
+++ b/ext/soap/php_packet_soap.c
@@ -385,7 +385,7 @@ int parse_packet_soap(zval *this_ptr, char *buffer, int buffer_size, sdlFunction
} else {
zend_refcounted *garbage = Z_COUNTED_P(return_value);
ZVAL_COPY(return_value, tmp);
- _zval_dtor_func(garbage ZEND_FILE_LINE_CC);
+ zval_dtor_func(garbage);
}
}
}
diff --git a/ext/soap/soap.c b/ext/soap/soap.c
index 11c2d7caa6..c842ce5119 100644
--- a/ext/soap/soap.c
+++ b/ext/soap/soap.c
@@ -953,9 +953,7 @@ PHP_METHOD(SoapFault, __toString)
line = zend_read_property(soap_fault_class_entry, this_ptr, "line", sizeof("line")-1, 1, &rv4);
fci.size = sizeof(fci);
- fci.function_table = &Z_OBJCE_P(getThis())->function_table;
ZVAL_STRINGL(&fci.function_name, "gettraceasstring", sizeof("gettraceasstring")-1);
- fci.symbol_table = NULL;
fci.object = Z_OBJ(EX(This));
fci.retval = &trace;
fci.param_count = 0;
diff --git a/ext/soap/tests/bugs/bug38005.phpt b/ext/soap/tests/bugs/bug38005.phpt
index 6a4fb2580b..219696c263 100644
--- a/ext/soap/tests/bugs/bug38005.phpt
+++ b/ext/soap/tests/bugs/bug38005.phpt
@@ -6,7 +6,7 @@ Bug #38005 (SoapFault faultstring doesn't follow encoding rules)
soap.wsdl_cache_enabled=0
--FILE--
<?php
-function Test($param) {
+function Test($param=NULL) {
return new SoapFault('Test', 'This is our fault: Ä');
}
diff --git a/ext/soap/tests/soap12/soap12-test.inc b/ext/soap/tests/soap12/soap12-test.inc
index fbdc855a7e..e27712241f 100644
--- a/ext/soap/tests/soap12/soap12-test.inc
+++ b/ext/soap/tests/soap12/soap12-test.inc
@@ -90,7 +90,7 @@ class Soap12test {
return count($input);
}
- function isNil($input) {
+ function isNil($input=NULL) {
return is_null($input);
}
diff --git a/ext/spl/spl_directory.c b/ext/spl/spl_directory.c
index 412fc54324..fdae966023 100644
--- a/ext/spl/spl_directory.c
+++ b/ext/spl/spl_directory.c
@@ -2075,13 +2075,11 @@ static int spl_filesystem_file_call(spl_filesystem_object *intern, zend_function
ZVAL_UNDEF(&retval);
fci.size = sizeof(fci);
- fci.function_table = EG(function_table);
fci.object = NULL;
fci.retval = &retval;
fci.param_count = num_args;
fci.params = params;
fci.no_separation = 1;
- fci.symbol_table = NULL;
ZVAL_STR(&fci.function_name, func_ptr->common.function_name);
fcic.initialized = 1;
@@ -2884,7 +2882,7 @@ SPL_METHOD(SplFileObject, fwrite)
if (ZEND_NUM_ARGS() > 1) {
if (length >= 0) {
- str_len = MAX(0, MIN((size_t)length, str_len));
+ str_len = MIN((size_t)length, str_len);
} else {
/* Negative length given, nothing to write */
str_len = 0;
diff --git a/ext/spl/spl_engine.h b/ext/spl/spl_engine.h
index c5f0927953..b683c16d94 100644
--- a/ext/spl/spl_engine.h
+++ b/ext/spl/spl_engine.h
@@ -62,10 +62,8 @@ static inline void spl_instantiate_arg_n(zend_class_entry *pce, zval *retval, in
spl_instantiate(pce, retval);
fci.size = sizeof(zend_fcall_info);
- fci.function_table = &pce->function_table;
ZVAL_STR(&fci.function_name, func->common.function_name);
fci.object = Z_OBJ_P(retval);
- fci.symbol_table = NULL;
fci.retval = &dummy;
fci.param_count = argc;
fci.params = argv;
@@ -73,7 +71,7 @@ static inline void spl_instantiate_arg_n(zend_class_entry *pce, zval *retval, in
fcc.initialized = 1;
fcc.function_handler = func;
- fcc.calling_scope = EG(scope);
+ fcc.calling_scope = zend_get_executed_scope();
fcc.called_scope = pce;
fcc.object = Z_OBJ_P(retval);
diff --git a/ext/spl/spl_observer.c b/ext/spl/spl_observer.c
index c189205879..223e252e63 100644
--- a/ext/spl/spl_observer.c
+++ b/ext/spl/spl_observer.c
@@ -117,32 +117,34 @@ void spl_SplObjectStorage_free_storage(zend_object *object) /* {{{ */
} /* }}} */
-static zend_string *spl_object_storage_get_hash(spl_SplObjectStorage *intern, zval *this, zval *obj) {
+static int spl_object_storage_get_hash(zend_hash_key *key, spl_SplObjectStorage *intern, zval *this, zval *obj) {
if (intern->fptr_get_hash) {
zval rv;
zend_call_method_with_1_params(this, intern->std.ce, &intern->fptr_get_hash, "getHash", &rv, obj);
if (!Z_ISUNDEF(rv)) {
if (Z_TYPE(rv) == IS_STRING) {
- return Z_STR(rv);
+ key->key = Z_STR(rv);
+ return SUCCESS;
} else {
zend_throw_exception(spl_ce_RuntimeException, "Hash needs to be a string", 0);
zval_ptr_dtor(&rv);
- return NULL;
+ return FAILURE;
}
} else {
- return NULL;
+ return FAILURE;
}
} else {
- zend_string *hash = zend_string_alloc(sizeof(zend_object*), 0);
- memcpy(ZSTR_VAL(hash), (void*)&Z_OBJ_P(obj), sizeof(zend_object*));
- ZSTR_VAL(hash)[ZSTR_LEN(hash)] = '\0';
- return hash;
+ key->key = NULL;
+ key->h = Z_OBJ_HANDLE_P(obj);
+ return SUCCESS;
}
}
-static void spl_object_storage_free_hash(spl_SplObjectStorage *intern, zend_string *hash) {
- zend_string_release(hash);
+static void spl_object_storage_free_hash(spl_SplObjectStorage *intern, zend_hash_key *key) {
+ if (key->key) {
+ zend_string_release(key->key);
+ }
}
static void spl_object_storage_dtor(zval *element) /* {{{ */
@@ -153,21 +155,24 @@ static void spl_object_storage_dtor(zval *element) /* {{{ */
efree(el);
} /* }}} */
-spl_SplObjectStorageElement* spl_object_storage_get(spl_SplObjectStorage *intern, zend_string *hash) /* {{{ */
+static spl_SplObjectStorageElement* spl_object_storage_get(spl_SplObjectStorage *intern, zend_hash_key *key) /* {{{ */
{
- return (spl_SplObjectStorageElement*)zend_hash_find_ptr(&intern->storage, hash);
+ if (key->key) {
+ return zend_hash_find_ptr(&intern->storage, key->key);
+ } else {
+ return zend_hash_index_find_ptr(&intern->storage, key->h);
+ }
} /* }}} */
spl_SplObjectStorageElement *spl_object_storage_attach(spl_SplObjectStorage *intern, zval *this, zval *obj, zval *inf) /* {{{ */
{
spl_SplObjectStorageElement *pelement, element;
- zend_string *hash = spl_object_storage_get_hash(intern, this, obj);
-
- if (!hash) {
+ zend_hash_key key;
+ if (spl_object_storage_get_hash(&key, intern, this, obj) == FAILURE) {
return NULL;
}
- pelement = spl_object_storage_get(intern, hash);
+ pelement = spl_object_storage_get(intern, &key);
if (pelement) {
zval_ptr_dtor(&pelement->inf);
@@ -176,7 +181,7 @@ spl_SplObjectStorageElement *spl_object_storage_attach(spl_SplObjectStorage *int
} else {
ZVAL_NULL(&pelement->inf);
}
- spl_object_storage_free_hash(intern, hash);
+ spl_object_storage_free_hash(intern, &key);
return pelement;
}
@@ -186,20 +191,28 @@ spl_SplObjectStorageElement *spl_object_storage_attach(spl_SplObjectStorage *int
} else {
ZVAL_NULL(&element.inf);
}
- pelement = zend_hash_update_mem(&intern->storage, hash, &element, sizeof(spl_SplObjectStorageElement));
- spl_object_storage_free_hash(intern, hash);
+ if (key.key) {
+ pelement = zend_hash_update_mem(&intern->storage, key.key, &element, sizeof(spl_SplObjectStorageElement));
+ } else {
+ pelement = zend_hash_index_update_mem(&intern->storage, key.h, &element, sizeof(spl_SplObjectStorageElement));
+ }
+ spl_object_storage_free_hash(intern, &key);
return pelement;
} /* }}} */
-int spl_object_storage_detach(spl_SplObjectStorage *intern, zval *this, zval *obj) /* {{{ */
+static int spl_object_storage_detach(spl_SplObjectStorage *intern, zval *this, zval *obj) /* {{{ */
{
int ret = FAILURE;
- zend_string *hash = spl_object_storage_get_hash(intern, this, obj);
- if (!hash) {
+ zend_hash_key key;
+ if (spl_object_storage_get_hash(&key, intern, this, obj) == FAILURE) {
return ret;
}
- ret = zend_hash_del(&intern->storage, hash);
- spl_object_storage_free_hash(intern, hash);
+ if (key.key) {
+ ret = zend_hash_del(&intern->storage, key.key);
+ } else {
+ ret = zend_hash_index_del(&intern->storage, key.h);
+ }
+ spl_object_storage_free_hash(intern, &key);
return ret;
} /* }}}*/
@@ -369,13 +382,17 @@ static zend_object *spl_SplObjectStorage_new(zend_class_entry *class_type)
int spl_object_storage_contains(spl_SplObjectStorage *intern, zval *this, zval *obj) /* {{{ */
{
int found;
- zend_string *hash = spl_object_storage_get_hash(intern, this, obj);
- if (!hash) {
+ zend_hash_key key;
+ if (spl_object_storage_get_hash(&key, intern, this, obj) == FAILURE) {
return 0;
}
- found = zend_hash_exists(&intern->storage, hash);
- spl_object_storage_free_hash(intern, hash);
+ if (key.key) {
+ found = zend_hash_exists(&intern->storage, key.key);
+ } else {
+ found = zend_hash_index_exists(&intern->storage, key.h);
+ }
+ spl_object_storage_free_hash(intern, &key);
return found;
} /* }}} */
@@ -430,19 +447,18 @@ SPL_METHOD(SplObjectStorage, offsetGet)
zval *obj;
spl_SplObjectStorageElement *element;
spl_SplObjectStorage *intern = Z_SPLOBJSTORAGE_P(getThis());
- zend_string *hash;
+ zend_hash_key key;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "o", &obj) == FAILURE) {
return;
}
- hash = spl_object_storage_get_hash(intern, getThis(), obj);
- if (!hash) {
+ if (spl_object_storage_get_hash(&key, intern, getThis(), obj) == FAILURE) {
return;
}
- element = spl_object_storage_get(intern, hash);
- spl_object_storage_free_hash(intern, hash);
+ element = spl_object_storage_get(intern, &key);
+ spl_object_storage_free_hash(intern, &key);
if (!element) {
zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0, "Object not found");
@@ -774,7 +790,7 @@ SPL_METHOD(SplObjectStorage, unserialize)
while (count-- > 0) {
spl_SplObjectStorageElement *pelement;
- zend_string *hash;
+ zend_hash_key key;
if (*p != ';') {
goto outexcept;
@@ -801,14 +817,13 @@ SPL_METHOD(SplObjectStorage, unserialize)
ZVAL_UNDEF(&inf);
}
- hash = spl_object_storage_get_hash(intern, getThis(), &entry);
- if (!hash) {
+ if (spl_object_storage_get_hash(&key, intern, getThis(), &entry) == FAILURE) {
zval_ptr_dtor(&entry);
zval_ptr_dtor(&inf);
goto outexcept;
}
- pelement = spl_object_storage_get(intern, hash);
- spl_object_storage_free_hash(intern, hash);
+ pelement = spl_object_storage_get(intern, &key);
+ spl_object_storage_free_hash(intern, &key);
if (pelement) {
if (!Z_ISUNDEF(pelement->inf)) {
var_push_dtor(&var_hash, &pelement->inf);
diff --git a/ext/sqlite3/libsqlite/sqlite3.c b/ext/sqlite3/libsqlite/sqlite3.c
index dff4538dd3..e198678908 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.10.2. By combining all the individual C code files into this
+** version 3.13.0. 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
@@ -40,6 +40,41 @@
#ifndef _SQLITEINT_H_
#define _SQLITEINT_H_
+/* Special Comments:
+**
+** Some comments have special meaning to the tools that measure test
+** coverage:
+**
+** NO_TEST - The branches on this line are not
+** measured by branch coverage. This is
+** used on lines of code that actually
+** implement parts of coverage testing.
+**
+** OPTIMIZATION-IF-TRUE - This branch is allowed to alway be false
+** and the correct answer is still obtained,
+** though perhaps more slowly.
+**
+** OPTIMIZATION-IF-FALSE - This branch is allowed to alway be true
+** and the correct answer is still obtained,
+** though perhaps more slowly.
+**
+** PREVENTS-HARMLESS-OVERREAD - This branch prevents a buffer overread
+** that would be harmless and undetectable
+** if it did occur.
+**
+** In all cases, the special comment must be enclosed in the usual
+** slash-asterisk...asterisk-slash comment marks, with no spaces between the
+** asterisks and the comment text.
+*/
+
+/*
+** Make sure that rand_s() is available on Windows systems with MSVC 2005
+** or higher.
+*/
+#if defined(_MSC_VER) && _MSC_VER>=1400
+# define _CRT_RAND_S
+#endif
+
/*
** Include the header file used to customize the compiler options for MSVC.
** This should be done first so that it can successfully prevent spurious
@@ -121,6 +156,9 @@
#else
/* This is not VxWorks. */
#define OS_VXWORKS 0
+#define HAVE_FCHOWN 1
+#define HAVE_READLINK 1
+#define HAVE_LSTAT 1
#endif /* defined(_WRS_KERNEL) */
/************** End of vxworks.h *********************************************/
@@ -158,6 +196,13 @@
# define _LARGEFILE_SOURCE 1
#endif
+/* What version of GCC is being used. 0 means GCC is not being used */
+#ifdef __GNUC__
+# define GCC_VERSION (__GNUC__*1000000+__GNUC_MINOR__*1000+__GNUC_PATCHLEVEL__)
+#else
+# define GCC_VERSION 0
+#endif
+
/* Needed for various definitions... */
#if defined(__GNUC__) && !defined(_GNU_SOURCE)
# define _GNU_SOURCE
@@ -230,7 +275,7 @@
**
** The official C-language API documentation for SQLite is derived
** from comments in this file. This file is the authoritative source
-** on how SQLite interfaces are suppose to operate.
+** on how SQLite interfaces are supposed to operate.
**
** The name of this file under configuration management is "sqlite.h.in".
** The makefile makes some minor changes to this file (such as inserting
@@ -318,9 +363,9 @@ extern "C" {
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
** [sqlite_version()] and [sqlite_source_id()].
*/
-#define SQLITE_VERSION "3.8.10.2"
-#define SQLITE_VERSION_NUMBER 3008010
-#define SQLITE_SOURCE_ID "2015-05-20 18:17:19 2ef4f3a5b1d1d0c4338f8243d40a2452cc1f7fe4"
+#define SQLITE_VERSION "3.13.0"
+#define SQLITE_VERSION_NUMBER 3013000
+#define SQLITE_SOURCE_ID "2016-05-18 10:57:30 fc49f556e48970561d7ab6a2f24fdd7d9eb81ff2"
/*
** CAPI3REF: Run-Time Library Version Numbers
@@ -331,7 +376,7 @@ extern "C" {
** but are associated with the library instead of the header file. ^(Cautious
** programmers might include assert() statements in their application to
** verify that values returned by these interfaces match the macros in
-** the header, and thus insure that the application is
+** the header, and thus ensure that the application is
** compiled with matching library and header files.
**
** <blockquote><pre>
@@ -554,7 +599,7 @@ typedef int (*sqlite3_callback)(void*,int,char**, char**);
** from [sqlite3_malloc()] and passed back through the 5th parameter.
** To avoid memory leaks, the application should invoke [sqlite3_free()]
** on error message strings returned through the 5th parameter of
-** of sqlite3_exec() after the error message string is no longer needed.
+** sqlite3_exec() after the error message string is no longer needed.
** ^If the 5th parameter to sqlite3_exec() is not NULL and no errors
** occur, then sqlite3_exec() sets the pointer in its 5th parameter to
** NULL before returning.
@@ -581,7 +626,7 @@ typedef int (*sqlite3_callback)(void*,int,char**, char**);
** Restrictions:
**
** <ul>
-** <li> The application must insure that the 1st parameter to sqlite3_exec()
+** <li> The application must ensure that the 1st parameter to sqlite3_exec()
** is a valid and open [database connection].
** <li> The application must not close the [database connection] specified by
** the 1st parameter to sqlite3_exec() while sqlite3_exec() is running.
@@ -684,6 +729,8 @@ SQLITE_API int SQLITE_STDCALL sqlite3_exec(
#define SQLITE_IOERR_MMAP (SQLITE_IOERR | (24<<8))
#define SQLITE_IOERR_GETTEMPPATH (SQLITE_IOERR | (25<<8))
#define SQLITE_IOERR_CONVPATH (SQLITE_IOERR | (26<<8))
+#define SQLITE_IOERR_VNODE (SQLITE_IOERR | (27<<8))
+#define SQLITE_IOERR_AUTH (SQLITE_IOERR | (28<<8))
#define SQLITE_LOCKED_SHAREDCACHE (SQLITE_LOCKED | (1<<8))
#define SQLITE_BUSY_RECOVERY (SQLITE_BUSY | (1<<8))
#define SQLITE_BUSY_SNAPSHOT (SQLITE_BUSY | (2<<8))
@@ -999,8 +1046,13 @@ struct sqlite3_io_methods {
** <li>[[SQLITE_FCNTL_FILE_POINTER]]
** The [SQLITE_FCNTL_FILE_POINTER] opcode is used to obtain a pointer
** to the [sqlite3_file] object associated with a particular database
-** connection. See the [sqlite3_file_control()] documentation for
-** additional information.
+** connection. See also [SQLITE_FCNTL_JOURNAL_POINTER].
+**
+** <li>[[SQLITE_FCNTL_JOURNAL_POINTER]]
+** The [SQLITE_FCNTL_JOURNAL_POINTER] opcode is used to obtain a pointer
+** to the [sqlite3_file] object associated with the journal file (either
+** the [rollback journal] or the [write-ahead log]) for a particular database
+** connection. See also [SQLITE_FCNTL_FILE_POINTER].
**
** <li>[[SQLITE_FCNTL_SYNC_OMITTED]]
** No longer in use.
@@ -1087,6 +1139,15 @@ struct sqlite3_io_methods {
** pointer in case this file-control is not implemented. This file-control
** is intended for diagnostic use only.
**
+** <li>[[SQLITE_FCNTL_VFS_POINTER]]
+** ^The [SQLITE_FCNTL_VFS_POINTER] opcode finds a pointer to the top-level
+** [VFSes] currently in use. ^(The argument X in
+** sqlite3_file_control(db,SQLITE_FCNTL_VFS_POINTER,X) must be
+** of type "[sqlite3_vfs] **". This opcodes will set *X
+** to a pointer to the top-level VFS.)^
+** ^When there are multiple VFS shims in the stack, this opcode finds the
+** upper-most shim only.
+**
** <li>[[SQLITE_FCNTL_PRAGMA]]
** ^Whenever a [PRAGMA] statement is parsed, an [SQLITE_FCNTL_PRAGMA]
** file control is sent to the open [sqlite3_file] object corresponding
@@ -1170,6 +1231,14 @@ struct sqlite3_io_methods {
** circumstances in order to fix a problem with priority inversion.
** Applications should <em>not</em> use this file-control.
**
+** <li>[[SQLITE_FCNTL_ZIPVFS]]
+** The [SQLITE_FCNTL_ZIPVFS] opcode is implemented by zipvfs only. All other
+** VFS should return SQLITE_NOTFOUND for this opcode.
+**
+** <li>[[SQLITE_FCNTL_RBU]]
+** The [SQLITE_FCNTL_RBU] opcode is implemented by the special VFS used by
+** the RBU extension only. All other VFS should return SQLITE_NOTFOUND for
+** this opcode.
** </ul>
*/
#define SQLITE_FCNTL_LOCKSTATE 1
@@ -1195,6 +1264,10 @@ struct sqlite3_io_methods {
#define SQLITE_FCNTL_COMMIT_PHASETWO 22
#define SQLITE_FCNTL_WIN32_SET_HANDLE 23
#define SQLITE_FCNTL_WAL_BLOCK 24
+#define SQLITE_FCNTL_ZIPVFS 25
+#define SQLITE_FCNTL_RBU 26
+#define SQLITE_FCNTL_VFS_POINTER 27
+#define SQLITE_FCNTL_JOURNAL_POINTER 28
/* deprecated names */
#define SQLITE_GET_LOCKPROXYFILE SQLITE_FCNTL_GET_LOCKPROXYFILE
@@ -1407,7 +1480,7 @@ struct sqlite3_vfs {
const char *(*xNextSystemCall)(sqlite3_vfs*, const char *zName);
/*
** The methods above are in versions 1 through 3 of the sqlite_vfs object.
- ** New fields may be appended in figure versions. The iVersion
+ ** New fields may be appended in future versions. The iVersion
** value will increment whenever this happens.
*/
};
@@ -1563,9 +1636,11 @@ SQLITE_API int SQLITE_STDCALL sqlite3_os_end(void);
** applications and so this routine is usually not necessary. It is
** provided to support rare applications with unusual needs.
**
-** The sqlite3_config() interface is not threadsafe. The application
-** must insure that no other SQLite interfaces are invoked by other
-** threads while sqlite3_config() is running. Furthermore, sqlite3_config()
+** <b>The sqlite3_config() interface is not threadsafe. The application
+** must ensure that no other SQLite interfaces are invoked by other
+** threads while sqlite3_config() is running.</b>
+**
+** The sqlite3_config() interface
** may only be invoked prior to library initialization using
** [sqlite3_initialize()] or after shutdown by [sqlite3_shutdown()].
** ^If sqlite3_config() is called after [sqlite3_initialize()] and before
@@ -1792,29 +1867,34 @@ struct sqlite3_mem_methods {
** </dd>
**
** [[SQLITE_CONFIG_PAGECACHE]] <dt>SQLITE_CONFIG_PAGECACHE</dt>
-** <dd> ^The SQLITE_CONFIG_PAGECACHE option specifies a static memory buffer
+** <dd> ^The SQLITE_CONFIG_PAGECACHE option specifies a memory pool
** that SQLite can use for the database page cache with the default page
** cache implementation.
-** This configuration should not be used if an application-define page
-** cache implementation is loaded using the [SQLITE_CONFIG_PCACHE2]
-** configuration option.
+** This configuration option is a no-op if an application-define page
+** cache implementation is loaded using the [SQLITE_CONFIG_PCACHE2].
** ^There are three arguments to SQLITE_CONFIG_PAGECACHE: A pointer to
-** 8-byte aligned
-** memory, the size of each page buffer (sz), and the number of pages (N).
+** 8-byte aligned memory (pMem), the size of each page cache line (sz),
+** and the number of cache lines (N).
** The sz argument should be the size of the largest database page
** (a power of two between 512 and 65536) plus some extra bytes for each
** page header. ^The number of extra bytes needed by the page header
-** can be determined using the [SQLITE_CONFIG_PCACHE_HDRSZ] option
-** to [sqlite3_config()].
+** can be determined using [SQLITE_CONFIG_PCACHE_HDRSZ].
** ^It is harmless, apart from the wasted memory,
-** for the sz parameter to be larger than necessary. The first
-** argument should pointer to an 8-byte aligned block of memory that
-** is at least sz*N bytes of memory, otherwise subsequent behavior is
-** undefined.
-** ^SQLite will use the memory provided by the first argument to satisfy its
-** memory needs for the first N pages that it adds to cache. ^If additional
-** page cache memory is needed beyond what is provided by this option, then
-** SQLite goes to [sqlite3_malloc()] for the additional storage space.</dd>
+** for the sz parameter to be larger than necessary. The pMem
+** argument must be either a NULL pointer or a pointer to an 8-byte
+** aligned block of memory of at least sz*N bytes, otherwise
+** subsequent behavior is undefined.
+** ^When pMem is not NULL, SQLite will strive to use the memory provided
+** to satisfy page cache needs, falling back to [sqlite3_malloc()] if
+** a page cache line is larger than sz bytes or if all of the pMem buffer
+** is exhausted.
+** ^If pMem is NULL and N is non-zero, then each database connection
+** does an initial bulk allocation for page cache memory
+** from [sqlite3_malloc()] sufficient for N cache lines if N is positive or
+** of -1024*N bytes if N is negative, . ^If additional
+** page cache memory is needed beyond what is provided by the initial
+** allocation, then SQLite goes to [sqlite3_malloc()] separately for each
+** additional cache line. </dd>
**
** [[SQLITE_CONFIG_HEAP]] <dt>SQLITE_CONFIG_HEAP</dt>
** <dd> ^The SQLITE_CONFIG_HEAP option specifies a static memory buffer
@@ -1992,6 +2072,20 @@ struct sqlite3_mem_methods {
** is enabled (using the [PRAGMA threads] command) and the amount of content
** to be sorted exceeds the page size times the minimum of the
** [PRAGMA cache_size] setting and this value.
+**
+** [[SQLITE_CONFIG_STMTJRNL_SPILL]]
+** <dt>SQLITE_CONFIG_STMTJRNL_SPILL
+** <dd>^The SQLITE_CONFIG_STMTJRNL_SPILL option takes a single parameter which
+** becomes the [statement journal] spill-to-disk threshold.
+** [Statement journals] are held in memory until their size (in bytes)
+** exceeds this threshold, at which point they are written to disk.
+** Or if the threshold is -1, statement journals are always held
+** exclusively in memory.
+** Since many statement journals never become large, setting the spill
+** threshold to a value such as 64KiB can greatly reduce the amount of
+** I/O required to support statement rollback.
+** The default value for this setting is controlled by the
+** [SQLITE_STMTJRNL_SPILL] compile-time option.
** </dl>
*/
#define SQLITE_CONFIG_SINGLETHREAD 1 /* nil */
@@ -2019,6 +2113,7 @@ struct sqlite3_mem_methods {
#define SQLITE_CONFIG_WIN32_HEAPSIZE 23 /* int nByte */
#define SQLITE_CONFIG_PCACHE_HDRSZ 24 /* int *psz */
#define SQLITE_CONFIG_PMASZ 25 /* unsigned int szPma */
+#define SQLITE_CONFIG_STMTJRNL_SPILL 26 /* int nByte */
/*
** CAPI3REF: Database Connection Configuration Options
@@ -2076,11 +2171,43 @@ struct sqlite3_mem_methods {
** following this call. The second parameter may be a NULL pointer, in
** which case the trigger setting is not reported back. </dd>
**
+** <dt>SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER</dt>
+** <dd> ^This option is used to enable or disable the two-argument
+** version of the [fts3_tokenizer()] function which is part of the
+** [FTS3] full-text search engine extension.
+** There should be two additional arguments.
+** The first argument is an integer which is 0 to disable fts3_tokenizer() or
+** positive to enable fts3_tokenizer() or negative to leave the setting
+** unchanged.
+** The second parameter is a pointer to an integer into which
+** is written 0 or 1 to indicate whether fts3_tokenizer is disabled or enabled
+** following this call. The second parameter may be a NULL pointer, in
+** which case the new setting is not reported back. </dd>
+**
+** <dt>SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION</dt>
+** <dd> ^This option is used to enable or disable the [sqlite3_load_extension()]
+** interface independently of the [load_extension()] SQL function.
+** The [sqlite3_enable_load_extension()] API enables or disables both the
+** C-API [sqlite3_load_extension()] and the SQL function [load_extension()].
+** There should be two additional arguments.
+** When the first argument to this interface is 1, then only the C-API is
+** enabled and the SQL function remains disabled. If the first argment to
+** this interface is 0, then both the C-API and the SQL function are disabled.
+** If the first argument is -1, then no changes are made to state of either the
+** C-API or the SQL function.
+** The second parameter is a pointer to an integer into which
+** is written 0 or 1 to indicate whether [sqlite3_load_extension()] interface
+** is disabled or enabled following this call. The second parameter may
+** be a NULL pointer, in which case the new setting is not reported back.
+** </dd>
+**
** </dl>
*/
-#define SQLITE_DBCONFIG_LOOKASIDE 1001 /* void* int int */
-#define SQLITE_DBCONFIG_ENABLE_FKEY 1002 /* int int* */
-#define SQLITE_DBCONFIG_ENABLE_TRIGGER 1003 /* int int* */
+#define SQLITE_DBCONFIG_LOOKASIDE 1001 /* void* int int */
+#define SQLITE_DBCONFIG_ENABLE_FKEY 1002 /* int int* */
+#define SQLITE_DBCONFIG_ENABLE_TRIGGER 1003 /* int int* */
+#define SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER 1004 /* int int* */
+#define SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION 1005 /* int int* */
/*
@@ -3570,7 +3697,8 @@ SQLITE_API int SQLITE_STDCALL sqlite3_stmt_readonly(sqlite3_stmt *pStmt);
**
** ^The sqlite3_stmt_busy(S) interface returns true (non-zero) if the
** [prepared statement] S has been stepped at least once using
-** [sqlite3_step(S)] but has not run to completion and/or has not
+** [sqlite3_step(S)] but has neither run to completion (returned
+** [SQLITE_DONE] from [sqlite3_step(S)]) nor
** been reset using [sqlite3_reset(S)]. ^The sqlite3_stmt_busy(S)
** interface returns false if S is a NULL pointer. If S is not a
** NULL pointer and is not a pointer to a valid [prepared statement]
@@ -3597,7 +3725,9 @@ SQLITE_API int SQLITE_STDCALL sqlite3_stmt_busy(sqlite3_stmt*);
** Some interfaces require a protected sqlite3_value. Other interfaces
** will accept either a protected or an unprotected sqlite3_value.
** Every interface that accepts sqlite3_value arguments specifies
-** whether or not it requires a protected sqlite3_value.
+** whether or not it requires a protected sqlite3_value. The
+** [sqlite3_value_dup()] interface can be used to construct a new
+** protected sqlite3_value from an unprotected sqlite3_value.
**
** The terms "protected" and "unprotected" refer to whether or not
** a mutex is held. An internal mutex is held for a protected
@@ -3757,6 +3887,7 @@ SQLITE_API int SQLITE_STDCALL sqlite3_bind_text64(sqlite3_stmt*, int, const char
void(*)(void*), unsigned char encoding);
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);
+SQLITE_API int SQLITE_STDCALL sqlite3_bind_zeroblob64(sqlite3_stmt*, int, sqlite3_uint64);
/*
** CAPI3REF: Number Of SQL Parameters
@@ -3820,7 +3951,7 @@ SQLITE_API const char *SQLITE_STDCALL sqlite3_bind_parameter_name(sqlite3_stmt*,
**
** See also: [sqlite3_bind_blob|sqlite3_bind()],
** [sqlite3_bind_parameter_count()], and
-** [sqlite3_bind_parameter_index()].
+** [sqlite3_bind_parameter_name()].
*/
SQLITE_API int SQLITE_STDCALL sqlite3_bind_parameter_index(sqlite3_stmt*, const char *zName);
@@ -4100,8 +4231,6 @@ SQLITE_API int SQLITE_STDCALL sqlite3_data_count(sqlite3_stmt *pStmt);
** KEYWORDS: {column access functions}
** METHOD: sqlite3_stmt
**
-** These routines form the "result set" interface.
-**
** ^These routines return information about a single column of the current
** result row of a query. ^In every case the first argument is a pointer
** to the [prepared statement] that is being evaluated (the [sqlite3_stmt*]
@@ -4161,13 +4290,14 @@ SQLITE_API int SQLITE_STDCALL sqlite3_data_count(sqlite3_stmt *pStmt);
** even empty strings, are always zero-terminated. ^The return
** value from sqlite3_column_blob() for a zero-length BLOB is a NULL pointer.
**
-** ^The object returned by [sqlite3_column_value()] is an
-** [unprotected sqlite3_value] object. An unprotected sqlite3_value object
-** may only be used with [sqlite3_bind_value()] and [sqlite3_result_value()].
+** <b>Warning:</b> ^The object returned by [sqlite3_column_value()] is an
+** [unprotected sqlite3_value] object. In a multithreaded environment,
+** an unprotected sqlite3_value object may only be used safely with
+** [sqlite3_bind_value()] and [sqlite3_result_value()].
** If the [unprotected sqlite3_value] object returned by
** [sqlite3_column_value()] is used in any other way, including calls
** to routines like [sqlite3_value_int()], [sqlite3_value_text()],
-** or [sqlite3_value_bytes()], then the behavior is undefined.
+** or [sqlite3_value_bytes()], the behavior is not threadsafe.
**
** These routines attempt to convert the value where appropriate. ^For
** example, if the internal representation is FLOAT and a text result
@@ -4198,12 +4328,6 @@ SQLITE_API int SQLITE_STDCALL sqlite3_data_count(sqlite3_stmt *pStmt);
** </table>
** </blockquote>)^
**
-** The table above makes reference to standard C library functions atoi()
-** and atof(). SQLite does not really use these functions. It has its
-** own equivalent internal routines. The atoi() and atof() names are
-** used in the table for brevity and because they are familiar to most
-** C programmers.
-**
** Note that when type conversions occur, pointers returned by prior
** calls to sqlite3_column_blob(), sqlite3_column_text(), and/or
** sqlite3_column_text16() may be invalidated.
@@ -4228,7 +4352,7 @@ SQLITE_API int SQLITE_STDCALL sqlite3_data_count(sqlite3_stmt *pStmt);
** of conversion are done in place when it is possible, but sometimes they
** are not possible and in those cases prior pointers are invalidated.
**
-** The safest and easiest to remember policy is to invoke these routines
+** The safest policy is to invoke these routines
** in one of the following ways:
**
** <ul>
@@ -4248,7 +4372,7 @@ SQLITE_API int SQLITE_STDCALL sqlite3_data_count(sqlite3_stmt *pStmt);
** ^The pointers returned are valid until a type conversion occurs as
** described above, or until [sqlite3_step()] or [sqlite3_reset()] or
** [sqlite3_finalize()] is called. ^The memory space used to hold strings
-** and BLOBs is freed automatically. Do <b>not</b> pass the pointers returned
+** and BLOBs is freed automatically. Do <em>not</em> pass the pointers returned
** from [sqlite3_column_blob()], [sqlite3_column_text()], etc. into
** [sqlite3_free()].
**
@@ -4498,12 +4622,12 @@ SQLITE_API SQLITE_DEPRECATED int SQLITE_STDCALL sqlite3_memory_alarm(void(*)(voi
#endif
/*
-** CAPI3REF: Obtaining SQL Function Parameter Values
+** CAPI3REF: Obtaining SQL 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
-** the function or aggregate.
+** the function or aggregate.
**
** The xFunc (for scalar functions) or xStep (for aggregates) parameters
** to [sqlite3_create_function()] and [sqlite3_create_function16()]
@@ -4557,6 +4681,39 @@ SQLITE_API int SQLITE_STDCALL sqlite3_value_type(sqlite3_value*);
SQLITE_API int SQLITE_STDCALL sqlite3_value_numeric_type(sqlite3_value*);
/*
+** CAPI3REF: Finding The Subtype Of SQL Values
+** METHOD: sqlite3_value
+**
+** The sqlite3_value_subtype(V) function returns the subtype for
+** an [application-defined SQL function] argument V. The subtype
+** information can be used to pass a limited amount of context from
+** one SQL function to another. Use the [sqlite3_result_subtype()]
+** routine to set the subtype for the return value of an SQL function.
+**
+** SQLite makes no use of subtype itself. It merely passes the subtype
+** from the result of one [application-defined SQL function] into the
+** input of another.
+*/
+SQLITE_API unsigned int SQLITE_STDCALL sqlite3_value_subtype(sqlite3_value*);
+
+/*
+** CAPI3REF: Copy And Free SQL Values
+** METHOD: sqlite3_value
+**
+** ^The sqlite3_value_dup(V) interface makes a copy of the [sqlite3_value]
+** object D and returns a pointer to that copy. ^The [sqlite3_value] returned
+** is a [protected sqlite3_value] object even if the input is not.
+** ^The sqlite3_value_dup(V) interface returns NULL if V is NULL or if a
+** memory allocation fails.
+**
+** ^The sqlite3_value_free(V) interface frees an [sqlite3_value] object
+** previously obtained from [sqlite3_value_dup()]. ^If V is a NULL pointer
+** then sqlite3_value_free(V) is a harmless no-op.
+*/
+SQLITE_API sqlite3_value *SQLITE_STDCALL sqlite3_value_dup(const sqlite3_value*);
+SQLITE_API void SQLITE_STDCALL sqlite3_value_free(sqlite3_value*);
+
+/*
** CAPI3REF: Obtain Aggregate Function Context
** METHOD: sqlite3_context
**
@@ -4719,9 +4876,9 @@ typedef void (*sqlite3_destructor_type)(void*);
** to by the second parameter and which is N bytes long where N is the
** third parameter.
**
-** ^The sqlite3_result_zeroblob() interfaces set the result of
-** the application-defined function to be a BLOB containing all zero
-** bytes and N bytes in size, where N is the value of the 2nd parameter.
+** ^The sqlite3_result_zeroblob(C,N) and sqlite3_result_zeroblob64(C,N)
+** interfaces set the result of the application-defined function to be
+** a BLOB containing all zero bytes and N bytes in size.
**
** ^The sqlite3_result_double() interface sets the result from
** an application-defined function to be a floating point value specified
@@ -4803,7 +4960,7 @@ typedef void (*sqlite3_destructor_type)(void*);
** from [sqlite3_malloc()] before it returns.
**
** ^The sqlite3_result_value() interface sets the result of
-** the application-defined function to be a copy the
+** the application-defined function to be a copy of the
** [unprotected sqlite3_value] object specified by the 2nd parameter. ^The
** sqlite3_result_value() interface makes a copy of the [sqlite3_value]
** so that the [sqlite3_value] specified in the parameter may change or
@@ -4836,6 +4993,22 @@ SQLITE_API void SQLITE_STDCALL sqlite3_result_text16le(sqlite3_context*, const v
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);
+SQLITE_API int SQLITE_STDCALL sqlite3_result_zeroblob64(sqlite3_context*, sqlite3_uint64 n);
+
+
+/*
+** CAPI3REF: Setting The Subtype Of An SQL Function
+** METHOD: sqlite3_context
+**
+** The sqlite3_result_subtype(C,T) function causes the subtype of
+** the result from the [application-defined SQL function] with
+** [sqlite3_context] C to be the value T. Only the lower 8 bits
+** of the subtype T are preserved in current versions of SQLite;
+** higher order bits are discarded.
+** The number of subtype bytes preserved by SQLite might increase
+** in future releases of SQLite.
+*/
+SQLITE_API void SQLITE_STDCALL sqlite3_result_subtype(sqlite3_context*,unsigned int);
/*
** CAPI3REF: Define New Collating Sequences
@@ -5284,7 +5457,7 @@ SQLITE_API void *SQLITE_STDCALL sqlite3_rollback_hook(sqlite3*, void(*)(void *),
** ^The sqlite3_update_hook() interface registers a callback function
** with the [database connection] identified by the first argument
** to be invoked whenever a row is updated, inserted or deleted in
-** a rowid table.
+** a [rowid table].
** ^Any callback set by a previous call to this function
** for the same database connection is overridden.
**
@@ -5323,8 +5496,8 @@ SQLITE_API void *SQLITE_STDCALL sqlite3_rollback_hook(sqlite3*, void(*)(void *),
** on the same [database connection] D, or NULL for
** the first call on D.
**
-** See also the [sqlite3_commit_hook()] and [sqlite3_rollback_hook()]
-** interfaces.
+** See also the [sqlite3_commit_hook()], [sqlite3_rollback_hook()],
+** and [sqlite3_preupdate_hook()] interfaces.
*/
SQLITE_API void *SQLITE_STDCALL sqlite3_update_hook(
sqlite3*,
@@ -5571,9 +5744,18 @@ SQLITE_API int SQLITE_STDCALL sqlite3_table_column_metadata(
** should free this memory by calling [sqlite3_free()].
**
** ^Extension loading must be enabled using
-** [sqlite3_enable_load_extension()] prior to calling this API,
+** [sqlite3_enable_load_extension()] or
+** [sqlite3_db_config](db,[SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION],1,NULL)
+** prior to calling this API,
** otherwise an error will be returned.
**
+** <b>Security warning:</b> It is recommended that the
+** [SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION] method be used to enable only this
+** interface. The use of the [sqlite3_enable_load_extension()] interface
+** should be avoided. This will keep the SQL function [load_extension()]
+** disabled and prevent SQL injections from giving attackers
+** access to extension loading capabilities.
+**
** See also the [load_extension() SQL function].
*/
SQLITE_API int SQLITE_STDCALL sqlite3_load_extension(
@@ -5596,6 +5778,17 @@ SQLITE_API int SQLITE_STDCALL sqlite3_load_extension(
** ^Call the sqlite3_enable_load_extension() routine with onoff==1
** to turn extension loading on and call it with onoff==0 to turn
** it back off again.
+**
+** ^This interface enables or disables both the C-API
+** [sqlite3_load_extension()] and the SQL function [load_extension()].
+** Use [sqlite3_db_config](db,[SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION],..)
+** to enable or disable only the C-API.
+**
+** <b>Security warning:</b> It is recommended that extension loading
+** be disabled using the [SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION] method
+** rather than this interface, so the [load_extension()] SQL function
+** remains disabled. This will prevent SQL injections from giving attackers
+** access to extension loading capabilities.
*/
SQLITE_API int SQLITE_STDCALL sqlite3_enable_load_extension(sqlite3 *db, int onoff);
@@ -5757,6 +5950,17 @@ struct sqlite3_module {
** ^Information about the ORDER BY clause is stored in aOrderBy[].
** ^Each term of aOrderBy records a column of the ORDER BY clause.
**
+** The colUsed field indicates which columns of the virtual table may be
+** required by the current scan. Virtual table columns are numbered from
+** zero in the order in which they appear within the CREATE TABLE statement
+** passed to sqlite3_declare_vtab(). For the first 63 columns (columns 0-62),
+** the corresponding bit is set within the colUsed mask if the column may be
+** required by SQLite. If the table has at least 64 columns and any column
+** to the right of the first 63 is required, then bit 63 of colUsed is also
+** set. In other words, column iCol may be required if the expression
+** (colUsed & ((sqlite3_uint64)1 << (iCol>=63 ? 63 : iCol))) evaluates to
+** non-zero.
+**
** The [xBestIndex] method must fill aConstraintUsage[] with information
** about what parameters to pass to xFilter. ^If argvIndex>0 then
** the right-hand side of the corresponding aConstraint[] is evaluated
@@ -5782,19 +5986,37 @@ struct sqlite3_module {
** ^The estimatedRows value is an estimate of the number of rows that
** will be returned by the strategy.
**
+** The xBestIndex method may optionally populate the idxFlags field with a
+** mask of SQLITE_INDEX_SCAN_* flags. Currently there is only one such flag -
+** SQLITE_INDEX_SCAN_UNIQUE. If the xBestIndex method sets this flag, SQLite
+** assumes that the strategy may visit at most one row.
+**
+** Additionally, if xBestIndex sets the SQLITE_INDEX_SCAN_UNIQUE flag, then
+** SQLite also assumes that if a call to the xUpdate() method is made as
+** part of the same statement to delete or update a virtual table row and the
+** implementation returns SQLITE_CONSTRAINT, then there is no need to rollback
+** any database changes. In other words, if the xUpdate() returns
+** SQLITE_CONSTRAINT, the database contents must be exactly as they were
+** before xUpdate was called. By contrast, if SQLITE_INDEX_SCAN_UNIQUE is not
+** set and xUpdate returns SQLITE_CONSTRAINT, any database changes made by
+** the xUpdate method are automatically rolled back by SQLite.
+**
** IMPORTANT: The estimatedRows field was added to the sqlite3_index_info
** structure for SQLite version 3.8.2. If a virtual table extension is
** used with an SQLite version earlier than 3.8.2, the results of attempting
** to read or write the estimatedRows field are undefined (but are likely
** to included crashing the application). The estimatedRows field should
** therefore only be used if [sqlite3_libversion_number()] returns a
-** value greater than or equal to 3008002.
+** value greater than or equal to 3008002. Similarly, the idxFlags field
+** was added for version 3.9.0. It may therefore only be used if
+** sqlite3_libversion_number() returns a value greater than or equal to
+** 3009000.
*/
struct sqlite3_index_info {
/* Inputs */
int nConstraint; /* Number of entries in aConstraint */
struct sqlite3_index_constraint {
- int iColumn; /* Column on left-hand side of constraint */
+ int iColumn; /* Column constrained. -1 for ROWID */
unsigned char op; /* Constraint operator */
unsigned char usable; /* True if this constraint is usable */
int iTermOffset; /* Used internally - xBestIndex should ignore */
@@ -5816,9 +6038,18 @@ struct sqlite3_index_info {
double estimatedCost; /* Estimated cost of using this index */
/* Fields below are only available in SQLite 3.8.2 and later */
sqlite3_int64 estimatedRows; /* Estimated number of rows returned */
+ /* Fields below are only available in SQLite 3.9.0 and later */
+ int idxFlags; /* Mask of SQLITE_INDEX_SCAN_* flags */
+ /* Fields below are only available in SQLite 3.10.0 and later */
+ sqlite3_uint64 colUsed; /* Input: Mask of columns used by statement */
};
/*
+** CAPI3REF: Virtual Table Scan Flags
+*/
+#define SQLITE_INDEX_SCAN_UNIQUE 1 /* Scan visits at most 1 row */
+
+/*
** CAPI3REF: Virtual Table Constraint Operator Codes
**
** These macros defined the allowed values for the
@@ -5826,12 +6057,15 @@ struct sqlite3_index_info {
** an operator that is part of a constraint term in the wHERE clause of
** a query that uses a [virtual table].
*/
-#define SQLITE_INDEX_CONSTRAINT_EQ 2
-#define SQLITE_INDEX_CONSTRAINT_GT 4
-#define SQLITE_INDEX_CONSTRAINT_LE 8
-#define SQLITE_INDEX_CONSTRAINT_LT 16
-#define SQLITE_INDEX_CONSTRAINT_GE 32
-#define SQLITE_INDEX_CONSTRAINT_MATCH 64
+#define SQLITE_INDEX_CONSTRAINT_EQ 2
+#define SQLITE_INDEX_CONSTRAINT_GT 4
+#define SQLITE_INDEX_CONSTRAINT_LE 8
+#define SQLITE_INDEX_CONSTRAINT_LT 16
+#define SQLITE_INDEX_CONSTRAINT_GE 32
+#define SQLITE_INDEX_CONSTRAINT_MATCH 64
+#define SQLITE_INDEX_CONSTRAINT_LIKE 65
+#define SQLITE_INDEX_CONSTRAINT_GLOB 66
+#define SQLITE_INDEX_CONSTRAINT_REGEXP 67
/*
** CAPI3REF: Register A Virtual Table Implementation
@@ -6079,7 +6313,7 @@ SQLITE_API int SQLITE_STDCALL sqlite3_blob_open(
**
** ^This function sets the database handle error code and message.
*/
-SQLITE_API SQLITE_EXPERIMENTAL int SQLITE_STDCALL sqlite3_blob_reopen(sqlite3_blob *, sqlite3_int64);
+SQLITE_API int SQLITE_STDCALL sqlite3_blob_reopen(sqlite3_blob *, sqlite3_int64);
/*
** CAPI3REF: Close A BLOB Handle
@@ -6275,6 +6509,9 @@ SQLITE_API int SQLITE_STDCALL sqlite3_vfs_unregister(sqlite3_vfs*);
** <li> SQLITE_MUTEX_STATIC_APP1
** <li> SQLITE_MUTEX_STATIC_APP2
** <li> SQLITE_MUTEX_STATIC_APP3
+** <li> SQLITE_MUTEX_STATIC_VFS1
+** <li> SQLITE_MUTEX_STATIC_VFS2
+** <li> SQLITE_MUTEX_STATIC_VFS3
** </ul>
**
** ^The first two constants (SQLITE_MUTEX_FAST and SQLITE_MUTEX_RECURSIVE)
@@ -6476,6 +6713,9 @@ SQLITE_API int SQLITE_STDCALL sqlite3_mutex_notheld(sqlite3_mutex*);
#define SQLITE_MUTEX_STATIC_APP1 8 /* For use by application */
#define SQLITE_MUTEX_STATIC_APP2 9 /* For use by application */
#define SQLITE_MUTEX_STATIC_APP3 10 /* For use by application */
+#define SQLITE_MUTEX_STATIC_VFS1 11 /* For use by built-in VFS */
+#define SQLITE_MUTEX_STATIC_VFS2 12 /* For use by extension VFS */
+#define SQLITE_MUTEX_STATIC_VFS3 13 /* For use by application VFS */
/*
** CAPI3REF: Retrieve the mutex for a database connection
@@ -6689,7 +6929,8 @@ SQLITE_API int SQLITE_STDCALL sqlite3_status64(
** The value written into the *pCurrent parameter is undefined.</dd>)^
**
** [[SQLITE_STATUS_PARSER_STACK]] ^(<dt>SQLITE_STATUS_PARSER_STACK</dt>
-** <dd>This parameter records the deepest parser stack. It is only
+** <dd>The *pHighwater parameter records the deepest parser stack.
+** The *pCurrent value is undefined. The *pHighwater value is only
** meaningful if SQLite is compiled with [YYTRACKMAXSTACKDEPTH].</dd>)^
** </dl>
**
@@ -7186,7 +7427,7 @@ typedef struct sqlite3_backup sqlite3_backup;
** must be different or else sqlite3_backup_init(D,N,S,M) will fail with
** an error.
**
-** ^A call to sqlite3_backup_init() will fail, returning SQLITE_ERROR, if
+** ^A call to sqlite3_backup_init() will fail, returning NULL, if
** there is already a read or read-write transaction open on the
** destination database.
**
@@ -7475,19 +7716,44 @@ SQLITE_API int SQLITE_STDCALL sqlite3_strnicmp(const char *, const char *, int);
/*
** CAPI3REF: String Globbing
*
-** ^The [sqlite3_strglob(P,X)] interface returns zero if string X matches
-** the glob pattern P, and it returns non-zero if string X does not match
-** the glob pattern P. ^The definition of glob pattern matching used in
+** ^The [sqlite3_strglob(P,X)] interface returns zero if and only if
+** string X matches the [GLOB] pattern P.
+** ^The definition of [GLOB] pattern matching used in
** [sqlite3_strglob(P,X)] is the same as for the "X GLOB P" operator in the
-** SQL dialect used by SQLite. ^The sqlite3_strglob(P,X) function is case
-** sensitive.
+** SQL dialect understood by SQLite. ^The [sqlite3_strglob(P,X)] function
+** is case sensitive.
**
** 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()].
+**
+** See also: [sqlite3_strlike()].
*/
SQLITE_API int SQLITE_STDCALL sqlite3_strglob(const char *zGlob, const char *zStr);
/*
+** CAPI3REF: String LIKE Matching
+*
+** ^The [sqlite3_strlike(P,X,E)] interface returns zero if and only if
+** string X matches the [LIKE] pattern P with escape character E.
+** ^The definition of [LIKE] pattern matching used in
+** [sqlite3_strlike(P,X,E)] is the same as for the "X LIKE P ESCAPE E"
+** operator in the SQL dialect understood by SQLite. ^For "X LIKE P" without
+** the ESCAPE clause, set the E parameter of [sqlite3_strlike(P,X,E)] to 0.
+** ^As with the LIKE operator, the [sqlite3_strlike(P,X,E)] function is case
+** insensitive - equivalent upper and lower case ASCII characters match
+** one another.
+**
+** ^The [sqlite3_strlike(P,X,E)] function matches Unicode characters, though
+** only ASCII characters are case folded.
+**
+** 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()].
+**
+** See also: [sqlite3_strglob()].
+*/
+SQLITE_API int SQLITE_STDCALL sqlite3_strlike(const char *zGlob, const char *zStr, unsigned int cEsc);
+
+/*
** CAPI3REF: Error Logging Interface
**
** ^The [sqlite3_log()] interface writes a message into the [error log]
@@ -7542,7 +7808,7 @@ SQLITE_API void SQLITE_CDECL sqlite3_log(int iErrCode, const char *zFormat, ...)
** previously registered write-ahead log callback. ^Note that the
** [sqlite3_wal_autocheckpoint()] interface and the
** [wal_autocheckpoint pragma] both invoke [sqlite3_wal_hook()] and will
-** those overwrite any prior [sqlite3_wal_hook()] settings.
+** overwrite any prior [sqlite3_wal_hook()] settings.
*/
SQLITE_API void *SQLITE_STDCALL sqlite3_wal_hook(
sqlite3*,
@@ -7889,7 +8155,7 @@ SQLITE_API int SQLITE_STDCALL sqlite3_vtab_on_conflict(sqlite3 *);
**
** See also: [sqlite3_stmt_scanstatus_reset()]
*/
-SQLITE_API SQLITE_EXPERIMENTAL int SQLITE_STDCALL sqlite3_stmt_scanstatus(
+SQLITE_API 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_* */
@@ -7905,8 +8171,279 @@ SQLITE_API SQLITE_EXPERIMENTAL int SQLITE_STDCALL sqlite3_stmt_scanstatus(
** This API is only available if the library is built with pre-processor
** symbol [SQLITE_ENABLE_STMT_SCANSTATUS] defined.
*/
-SQLITE_API SQLITE_EXPERIMENTAL void SQLITE_STDCALL sqlite3_stmt_scanstatus_reset(sqlite3_stmt*);
+SQLITE_API void SQLITE_STDCALL sqlite3_stmt_scanstatus_reset(sqlite3_stmt*);
+
+/*
+** CAPI3REF: Flush caches to disk mid-transaction
+**
+** ^If a write-transaction is open on [database connection] D when the
+** [sqlite3_db_cacheflush(D)] interface invoked, any dirty
+** pages in the pager-cache that are not currently in use are written out
+** to disk. A dirty page may be in use if a database cursor created by an
+** active SQL statement is reading from it, or if it is page 1 of a database
+** file (page 1 is always "in use"). ^The [sqlite3_db_cacheflush(D)]
+** interface flushes caches for all schemas - "main", "temp", and
+** any [attached] databases.
+**
+** ^If this function needs to obtain extra database locks before dirty pages
+** can be flushed to disk, it does so. ^If those locks cannot be obtained
+** immediately and there is a busy-handler callback configured, it is invoked
+** in the usual manner. ^If the required lock still cannot be obtained, then
+** the database is skipped and an attempt made to flush any dirty pages
+** belonging to the next (if any) database. ^If any databases are skipped
+** because locks cannot be obtained, but no other error occurs, this
+** function returns SQLITE_BUSY.
+**
+** ^If any other error occurs while flushing dirty pages to disk (for
+** example an IO error or out-of-memory condition), then processing is
+** abandoned and an SQLite [error code] is returned to the caller immediately.
+**
+** ^Otherwise, if no error occurs, [sqlite3_db_cacheflush()] returns SQLITE_OK.
+**
+** ^This function does not set the database handle error code or message
+** returned by the [sqlite3_errcode()] and [sqlite3_errmsg()] functions.
+*/
+SQLITE_API int SQLITE_STDCALL sqlite3_db_cacheflush(sqlite3*);
+
+/*
+** CAPI3REF: The pre-update hook.
+**
+** ^These interfaces are only available if SQLite is compiled using the
+** [SQLITE_ENABLE_PREUPDATE_HOOK] compile-time option.
+**
+** ^The [sqlite3_preupdate_hook()] interface registers a callback function
+** that is invoked prior to each [INSERT], [UPDATE], and [DELETE] operation
+** on a [rowid table].
+** ^At most one preupdate hook may be registered at a time on a single
+** [database connection]; each call to [sqlite3_preupdate_hook()] overrides
+** the previous setting.
+** ^The preupdate hook is disabled by invoking [sqlite3_preupdate_hook()]
+** with a NULL pointer as the second parameter.
+** ^The third parameter to [sqlite3_preupdate_hook()] is passed through as
+** the first parameter to callbacks.
+**
+** ^The preupdate hook only fires for changes to [rowid tables]; the preupdate
+** hook is not invoked for changes to [virtual tables] or [WITHOUT ROWID]
+** tables.
+**
+** ^The second parameter to the preupdate callback is a pointer to
+** the [database connection] that registered the preupdate hook.
+** ^The third parameter to the preupdate callback is one of the constants
+** [SQLITE_INSERT], [SQLITE_DELETE], or [SQLITE_UPDATE] to indentify the
+** kind of update operation that is about to occur.
+** ^(The fourth parameter to the preupdate callback is the name of the
+** database within the database connection that is being modified. This
+** will be "main" for the main database or "temp" for TEMP tables or
+** the name given after the AS keyword in the [ATTACH] statement for attached
+** databases.)^
+** ^The fifth parameter to the preupdate callback is the name of the
+** table that is being modified.
+** ^The sixth parameter to the preupdate callback is the initial [rowid] of the
+** row being changes for SQLITE_UPDATE and SQLITE_DELETE changes and is
+** undefined for SQLITE_INSERT changes.
+** ^The seventh parameter to the preupdate callback is the final [rowid] of
+** the row being changed for SQLITE_UPDATE and SQLITE_INSERT changes and is
+** undefined for SQLITE_DELETE changes.
+**
+** The [sqlite3_preupdate_old()], [sqlite3_preupdate_new()],
+** [sqlite3_preupdate_count()], and [sqlite3_preupdate_depth()] interfaces
+** provide additional information about a preupdate event. These routines
+** may only be called from within a preupdate callback. Invoking any of
+** these routines from outside of a preupdate callback or with a
+** [database connection] pointer that is different from the one supplied
+** to the preupdate callback results in undefined and probably undesirable
+** behavior.
+**
+** ^The [sqlite3_preupdate_count(D)] interface returns the number of columns
+** in the row that is being inserted, updated, or deleted.
+**
+** ^The [sqlite3_preupdate_old(D,N,P)] interface writes into P a pointer to
+** a [protected sqlite3_value] that contains the value of the Nth column of
+** the table row before it is updated. The N parameter must be between 0
+** and one less than the number of columns or the behavior will be
+** undefined. This must only be used within SQLITE_UPDATE and SQLITE_DELETE
+** preupdate callbacks; if it is used by an SQLITE_INSERT callback then the
+** behavior is undefined. The [sqlite3_value] that P points to
+** will be destroyed when the preupdate callback returns.
+**
+** ^The [sqlite3_preupdate_new(D,N,P)] interface writes into P a pointer to
+** a [protected sqlite3_value] that contains the value of the Nth column of
+** the table row after it is updated. The N parameter must be between 0
+** and one less than the number of columns or the behavior will be
+** undefined. This must only be used within SQLITE_INSERT and SQLITE_UPDATE
+** preupdate callbacks; if it is used by an SQLITE_DELETE callback then the
+** behavior is undefined. The [sqlite3_value] that P points to
+** will be destroyed when the preupdate callback returns.
+**
+** ^The [sqlite3_preupdate_depth(D)] interface returns 0 if the preupdate
+** callback was invoked as a result of a direct insert, update, or delete
+** operation; or 1 for inserts, updates, or deletes invoked by top-level
+** triggers; or 2 for changes resulting from triggers called by top-level
+** triggers; and so forth.
+**
+** See also: [sqlite3_update_hook()]
+*/
+SQLITE_API SQLITE_EXPERIMENTAL void *SQLITE_STDCALL sqlite3_preupdate_hook(
+ sqlite3 *db,
+ void(*xPreUpdate)(
+ void *pCtx, /* Copy of third arg to preupdate_hook() */
+ sqlite3 *db, /* Database handle */
+ int op, /* SQLITE_UPDATE, DELETE or INSERT */
+ char const *zDb, /* Database name */
+ char const *zName, /* Table name */
+ sqlite3_int64 iKey1, /* Rowid of row about to be deleted/updated */
+ sqlite3_int64 iKey2 /* New rowid value (for a rowid UPDATE) */
+ ),
+ void*
+);
+SQLITE_API SQLITE_EXPERIMENTAL int SQLITE_STDCALL sqlite3_preupdate_old(sqlite3 *, int, sqlite3_value **);
+SQLITE_API SQLITE_EXPERIMENTAL int SQLITE_STDCALL sqlite3_preupdate_count(sqlite3 *);
+SQLITE_API SQLITE_EXPERIMENTAL int SQLITE_STDCALL sqlite3_preupdate_depth(sqlite3 *);
+SQLITE_API SQLITE_EXPERIMENTAL int SQLITE_STDCALL sqlite3_preupdate_new(sqlite3 *, int, sqlite3_value **);
+
+/*
+** CAPI3REF: Low-level system error code
+**
+** ^Attempt to return the underlying operating system error code or error
+** number that caused the most recent I/O error or failure to open a file.
+** The return value is OS-dependent. For example, on unix systems, after
+** [sqlite3_open_v2()] returns [SQLITE_CANTOPEN], this interface could be
+** called to get back the underlying "errno" that caused the problem, such
+** as ENOSPC, EAUTH, EISDIR, and so forth.
+*/
+SQLITE_API int SQLITE_STDCALL sqlite3_system_errno(sqlite3*);
+
+/*
+** CAPI3REF: Database Snapshot
+** KEYWORDS: {snapshot}
+** EXPERIMENTAL
+**
+** An instance of the snapshot object records the state of a [WAL mode]
+** database for some specific point in history.
+**
+** In [WAL mode], multiple [database connections] that are open on the
+** same database file can each be reading a different historical version
+** of the database file. When a [database connection] begins a read
+** transaction, that connection sees an unchanging copy of the database
+** as it existed for the point in time when the transaction first started.
+** Subsequent changes to the database from other connections are not seen
+** by the reader until a new read transaction is started.
+**
+** The sqlite3_snapshot object records state information about an historical
+** version of the database file so that it is possible to later open a new read
+** transaction that sees that historical version of the database rather than
+** the most recent version.
+**
+** The constructor for this object is [sqlite3_snapshot_get()]. The
+** [sqlite3_snapshot_open()] method causes a fresh read transaction to refer
+** to an historical snapshot (if possible). The destructor for
+** sqlite3_snapshot objects is [sqlite3_snapshot_free()].
+*/
+typedef struct sqlite3_snapshot sqlite3_snapshot;
+
+/*
+** CAPI3REF: Record A Database Snapshot
+** EXPERIMENTAL
+**
+** ^The [sqlite3_snapshot_get(D,S,P)] interface attempts to make a
+** new [sqlite3_snapshot] object that records the current state of
+** schema S in database connection D. ^On success, the
+** [sqlite3_snapshot_get(D,S,P)] interface writes a pointer to the newly
+** created [sqlite3_snapshot] object into *P and returns SQLITE_OK.
+** ^If schema S of [database connection] D is not a [WAL mode] database
+** that is in a read transaction, then [sqlite3_snapshot_get(D,S,P)]
+** leaves the *P value unchanged and returns an appropriate [error code].
+**
+** The [sqlite3_snapshot] object returned from a successful call to
+** [sqlite3_snapshot_get()] must be freed using [sqlite3_snapshot_free()]
+** to avoid a memory leak.
+**
+** The [sqlite3_snapshot_get()] interface is only available when the
+** SQLITE_ENABLE_SNAPSHOT compile-time option is used.
+*/
+SQLITE_API SQLITE_EXPERIMENTAL int SQLITE_STDCALL sqlite3_snapshot_get(
+ sqlite3 *db,
+ const char *zSchema,
+ sqlite3_snapshot **ppSnapshot
+);
+/*
+** CAPI3REF: Start a read transaction on an historical snapshot
+** EXPERIMENTAL
+**
+** ^The [sqlite3_snapshot_open(D,S,P)] interface starts a
+** read transaction for schema S of
+** [database connection] D such that the read transaction
+** refers to historical [snapshot] P, rather than the most
+** recent change to the database.
+** ^The [sqlite3_snapshot_open()] interface returns SQLITE_OK on success
+** or an appropriate [error code] if it fails.
+**
+** ^In order to succeed, a call to [sqlite3_snapshot_open(D,S,P)] must be
+** the first operation following the [BEGIN] that takes the schema S
+** out of [autocommit mode].
+** ^In other words, schema S must not currently be in
+** a transaction for [sqlite3_snapshot_open(D,S,P)] to work, but the
+** database connection D must be out of [autocommit mode].
+** ^A [snapshot] will fail to open if it has been overwritten by a
+** [checkpoint].
+** ^(A call to [sqlite3_snapshot_open(D,S,P)] will fail if the
+** database connection D does not know that the database file for
+** schema S is in [WAL mode]. A database connection might not know
+** that the database file is in [WAL mode] if there has been no prior
+** I/O on that database connection, or if the database entered [WAL mode]
+** after the most recent I/O on the database connection.)^
+** (Hint: Run "[PRAGMA application_id]" against a newly opened
+** database connection in order to make it ready to use snapshots.)
+**
+** The [sqlite3_snapshot_open()] interface is only available when the
+** SQLITE_ENABLE_SNAPSHOT compile-time option is used.
+*/
+SQLITE_API SQLITE_EXPERIMENTAL int SQLITE_STDCALL sqlite3_snapshot_open(
+ sqlite3 *db,
+ const char *zSchema,
+ sqlite3_snapshot *pSnapshot
+);
+
+/*
+** CAPI3REF: Destroy a snapshot
+** EXPERIMENTAL
+**
+** ^The [sqlite3_snapshot_free(P)] interface destroys [sqlite3_snapshot] P.
+** The application must eventually free every [sqlite3_snapshot] object
+** using this routine to avoid a memory leak.
+**
+** The [sqlite3_snapshot_free()] interface is only available when the
+** SQLITE_ENABLE_SNAPSHOT compile-time option is used.
+*/
+SQLITE_API SQLITE_EXPERIMENTAL void SQLITE_STDCALL sqlite3_snapshot_free(sqlite3_snapshot*);
+
+/*
+** CAPI3REF: Compare the ages of two snapshot handles.
+** EXPERIMENTAL
+**
+** The sqlite3_snapshot_cmp(P1, P2) interface is used to compare the ages
+** of two valid snapshot handles.
+**
+** If the two snapshot handles are not associated with the same database
+** file, the result of the comparison is undefined.
+**
+** Additionally, the result of the comparison is only valid if both of the
+** snapshot handles were obtained by calling sqlite3_snapshot_get() since the
+** last time the wal file was deleted. The wal file is deleted when the
+** database is changed back to rollback mode or when the number of database
+** clients drops to zero. If either snapshot handle was obtained before the
+** wal file was last deleted, the value returned by this function
+** is undefined.
+**
+** Otherwise, this API returns a negative value if P1 refers to an older
+** snapshot than P2, zero if the two handles refer to the same database
+** snapshot, and a positive value if P1 is a newer snapshot than P2.
+*/
+SQLITE_API SQLITE_EXPERIMENTAL int SQLITE_STDCALL sqlite3_snapshot_cmp(
+ sqlite3_snapshot *p1,
+ sqlite3_snapshot *p2
+);
/*
** Undo the hack that converts floating point types to integer for
@@ -7921,6 +8458,7 @@ SQLITE_API SQLITE_EXPERIMENTAL void SQLITE_STDCALL sqlite3_stmt_scanstatus_reset
#endif
#endif /* _SQLITE3_H_ */
+/******** Begin file sqlite3rtree.h *********/
/*
** 2010 August 30
**
@@ -8020,6 +8558,8 @@ struct sqlite3_rtree_query_info {
int eParentWithin; /* Visibility of parent node */
int eWithin; /* OUT: Visiblity */
sqlite3_rtree_dbl rScore; /* OUT: Write the score here */
+ /* The following fields are only available in 3.8.11 and later */
+ sqlite3_value **apSqlParam; /* Original SQL values of parameters */
};
/*
@@ -8036,6 +8576,1868 @@ struct sqlite3_rtree_query_info {
#endif /* ifndef _SQLITE3RTREE_H_ */
+/******** End of sqlite3rtree.h *********/
+/******** Begin file sqlite3session.h *********/
+
+#if !defined(__SQLITESESSION_H_) && defined(SQLITE_ENABLE_SESSION)
+#define __SQLITESESSION_H_ 1
+
+/*
+** Make sure we can call this stuff from C++.
+*/
+#if 0
+extern "C" {
+#endif
+
+
+/*
+** CAPI3REF: Session Object Handle
+*/
+typedef struct sqlite3_session sqlite3_session;
+
+/*
+** CAPI3REF: Changeset Iterator Handle
+*/
+typedef struct sqlite3_changeset_iter sqlite3_changeset_iter;
+
+/*
+** CAPI3REF: Create A New Session Object
+**
+** Create a new session object attached to database handle db. If successful,
+** a pointer to the new object is written to *ppSession and SQLITE_OK is
+** returned. If an error occurs, *ppSession is set to NULL and an SQLite
+** error code (e.g. SQLITE_NOMEM) is returned.
+**
+** It is possible to create multiple session objects attached to a single
+** database handle.
+**
+** Session objects created using this function should be deleted using the
+** [sqlite3session_delete()] function before the database handle that they
+** are attached to is itself closed. If the database handle is closed before
+** the session object is deleted, then the results of calling any session
+** module function, including [sqlite3session_delete()] on the session object
+** are undefined.
+**
+** Because the session module uses the [sqlite3_preupdate_hook()] API, it
+** is not possible for an application to register a pre-update hook on a
+** database handle that has one or more session objects attached. Nor is
+** it possible to create a session object attached to a database handle for
+** which a pre-update hook is already defined. The results of attempting
+** either of these things are undefined.
+**
+** The session object will be used to create changesets for tables in
+** database zDb, where zDb is either "main", or "temp", or the name of an
+** attached database. It is not an error if database zDb is not attached
+** to the database when the session object is created.
+*/
+int sqlite3session_create(
+ sqlite3 *db, /* Database handle */
+ const char *zDb, /* Name of db (e.g. "main") */
+ sqlite3_session **ppSession /* OUT: New session object */
+);
+
+/*
+** CAPI3REF: Delete A Session Object
+**
+** Delete a session object previously allocated using
+** [sqlite3session_create()]. Once a session object has been deleted, the
+** results of attempting to use pSession with any other session module
+** function are undefined.
+**
+** Session objects must be deleted before the database handle to which they
+** are attached is closed. Refer to the documentation for
+** [sqlite3session_create()] for details.
+*/
+void sqlite3session_delete(sqlite3_session *pSession);
+
+
+/*
+** CAPI3REF: Enable Or Disable A Session Object
+**
+** Enable or disable the recording of changes by a session object. When
+** enabled, a session object records changes made to the database. When
+** disabled - it does not. A newly created session object is enabled.
+** Refer to the documentation for [sqlite3session_changeset()] for further
+** details regarding how enabling and disabling a session object affects
+** the eventual changesets.
+**
+** Passing zero to this function disables the session. Passing a value
+** greater than zero enables it. Passing a value less than zero is a
+** no-op, and may be used to query the current state of the session.
+**
+** The return value indicates the final state of the session object: 0 if
+** the session is disabled, or 1 if it is enabled.
+*/
+int sqlite3session_enable(sqlite3_session *pSession, int bEnable);
+
+/*
+** CAPI3REF: Set Or Clear the Indirect Change Flag
+**
+** Each change recorded by a session object is marked as either direct or
+** indirect. A change is marked as indirect if either:
+**
+** <ul>
+** <li> The session object "indirect" flag is set when the change is
+** made, or
+** <li> The change is made by an SQL trigger or foreign key action
+** instead of directly as a result of a users SQL statement.
+** </ul>
+**
+** If a single row is affected by more than one operation within a session,
+** then the change is considered indirect if all operations meet the criteria
+** for an indirect change above, or direct otherwise.
+**
+** This function is used to set, clear or query the session object indirect
+** flag. If the second argument passed to this function is zero, then the
+** indirect flag is cleared. If it is greater than zero, the indirect flag
+** is set. Passing a value less than zero does not modify the current value
+** of the indirect flag, and may be used to query the current state of the
+** indirect flag for the specified session object.
+**
+** The return value indicates the final state of the indirect flag: 0 if
+** it is clear, or 1 if it is set.
+*/
+int sqlite3session_indirect(sqlite3_session *pSession, int bIndirect);
+
+/*
+** CAPI3REF: Attach A Table To A Session Object
+**
+** If argument zTab is not NULL, then it is the name of a table to attach
+** to the session object passed as the first argument. All subsequent changes
+** made to the table while the session object is enabled will be recorded. See
+** documentation for [sqlite3session_changeset()] for further details.
+**
+** Or, if argument zTab is NULL, then changes are recorded for all tables
+** in the database. If additional tables are added to the database (by
+** executing "CREATE TABLE" statements) after this call is made, changes for
+** the new tables are also recorded.
+**
+** Changes can only be recorded for tables that have a PRIMARY KEY explicitly
+** defined as part of their CREATE TABLE statement. It does not matter if the
+** PRIMARY KEY is an "INTEGER PRIMARY KEY" (rowid alias) or not. The PRIMARY
+** KEY may consist of a single column, or may be a composite key.
+**
+** It is not an error if the named table does not exist in the database. Nor
+** is it an error if the named table does not have a PRIMARY KEY. However,
+** no changes will be recorded in either of these scenarios.
+**
+** Changes are not recorded for individual rows that have NULL values stored
+** in one or more of their PRIMARY KEY columns.
+**
+** SQLITE_OK is returned if the call completes without error. Or, if an error
+** occurs, an SQLite error code (e.g. SQLITE_NOMEM) is returned.
+*/
+int sqlite3session_attach(
+ sqlite3_session *pSession, /* Session object */
+ const char *zTab /* Table name */
+);
+
+/*
+** CAPI3REF: Set a table filter on a Session Object.
+**
+** The second argument (xFilter) is the "filter callback". For changes to rows
+** in tables that are not attached to the Session oject, the filter is called
+** to determine whether changes to the table's rows should be tracked or not.
+** If xFilter returns 0, changes is not tracked. Note that once a table is
+** attached, xFilter will not be called again.
+*/
+void sqlite3session_table_filter(
+ sqlite3_session *pSession, /* Session object */
+ int(*xFilter)(
+ void *pCtx, /* Copy of third arg to _filter_table() */
+ const char *zTab /* Table name */
+ ),
+ void *pCtx /* First argument passed to xFilter */
+);
+
+/*
+** CAPI3REF: Generate A Changeset From A Session Object
+**
+** Obtain a changeset containing changes to the tables attached to the
+** session object passed as the first argument. If successful,
+** set *ppChangeset to point to a buffer containing the changeset
+** and *pnChangeset to the size of the changeset in bytes before returning
+** SQLITE_OK. If an error occurs, set both *ppChangeset and *pnChangeset to
+** zero and return an SQLite error code.
+**
+** A changeset consists of zero or more INSERT, UPDATE and/or DELETE changes,
+** each representing a change to a single row of an attached table. An INSERT
+** change contains the values of each field of a new database row. A DELETE
+** contains the original values of each field of a deleted database row. An
+** UPDATE change contains the original values of each field of an updated
+** database row along with the updated values for each updated non-primary-key
+** column. It is not possible for an UPDATE change to represent a change that
+** modifies the values of primary key columns. If such a change is made, it
+** is represented in a changeset as a DELETE followed by an INSERT.
+**
+** Changes are not recorded for rows that have NULL values stored in one or
+** more of their PRIMARY KEY columns. If such a row is inserted or deleted,
+** no corresponding change is present in the changesets returned by this
+** function. If an existing row with one or more NULL values stored in
+** PRIMARY KEY columns is updated so that all PRIMARY KEY columns are non-NULL,
+** only an INSERT is appears in the changeset. Similarly, if an existing row
+** with non-NULL PRIMARY KEY values is updated so that one or more of its
+** PRIMARY KEY columns are set to NULL, the resulting changeset contains a
+** DELETE change only.
+**
+** The contents of a changeset may be traversed using an iterator created
+** using the [sqlite3changeset_start()] API. A changeset may be applied to
+** a database with a compatible schema using the [sqlite3changeset_apply()]
+** API.
+**
+** Within a changeset generated by this function, all changes related to a
+** single table are grouped together. In other words, when iterating through
+** a changeset or when applying a changeset to a database, all changes related
+** to a single table are processed before moving on to the next table. Tables
+** are sorted in the same order in which they were attached (or auto-attached)
+** to the sqlite3_session object. The order in which the changes related to
+** a single table are stored is undefined.
+**
+** Following a successful call to this function, it is the responsibility of
+** the caller to eventually free the buffer that *ppChangeset points to using
+** [sqlite3_free()].
+**
+** <h3>Changeset Generation</h3>
+**
+** Once a table has been attached to a session object, the session object
+** records the primary key values of all new rows inserted into the table.
+** It also records the original primary key and other column values of any
+** deleted or updated rows. For each unique primary key value, data is only
+** recorded once - the first time a row with said primary key is inserted,
+** updated or deleted in the lifetime of the session.
+**
+** There is one exception to the previous paragraph: when a row is inserted,
+** updated or deleted, if one or more of its primary key columns contain a
+** NULL value, no record of the change is made.
+**
+** The session object therefore accumulates two types of records - those
+** that consist of primary key values only (created when the user inserts
+** a new record) and those that consist of the primary key values and the
+** original values of other table columns (created when the users deletes
+** or updates a record).
+**
+** When this function is called, the requested changeset is created using
+** both the accumulated records and the current contents of the database
+** file. Specifically:
+**
+** <ul>
+** <li> For each record generated by an insert, the database is queried
+** for a row with a matching primary key. If one is found, an INSERT
+** change is added to the changeset. If no such row is found, no change
+** is added to the changeset.
+**
+** <li> For each record generated by an update or delete, the database is
+** queried for a row with a matching primary key. If such a row is
+** found and one or more of the non-primary key fields have been
+** modified from their original values, an UPDATE change is added to
+** the changeset. Or, if no such row is found in the table, a DELETE
+** change is added to the changeset. If there is a row with a matching
+** primary key in the database, but all fields contain their original
+** values, no change is added to the changeset.
+** </ul>
+**
+** This means, amongst other things, that if a row is inserted and then later
+** deleted while a session object is active, neither the insert nor the delete
+** will be present in the changeset. Or if a row is deleted and then later a
+** row with the same primary key values inserted while a session object is
+** active, the resulting changeset will contain an UPDATE change instead of
+** a DELETE and an INSERT.
+**
+** When a session object is disabled (see the [sqlite3session_enable()] API),
+** it does not accumulate records when rows are inserted, updated or deleted.
+** This may appear to have some counter-intuitive effects if a single row
+** is written to more than once during a session. For example, if a row
+** is inserted while a session object is enabled, then later deleted while
+** the same session object is disabled, no INSERT record will appear in the
+** changeset, even though the delete took place while the session was disabled.
+** Or, if one field of a row is updated while a session is disabled, and
+** another field of the same row is updated while the session is enabled, the
+** resulting changeset will contain an UPDATE change that updates both fields.
+*/
+int sqlite3session_changeset(
+ sqlite3_session *pSession, /* Session object */
+ int *pnChangeset, /* OUT: Size of buffer at *ppChangeset */
+ void **ppChangeset /* OUT: Buffer containing changeset */
+);
+
+/*
+** CAPI3REF: Load The Difference Between Tables Into A Session
+**
+** If it is not already attached to the session object passed as the first
+** argument, this function attaches table zTbl in the same manner as the
+** [sqlite3session_attach()] function. If zTbl does not exist, or if it
+** does not have a primary key, this function is a no-op (but does not return
+** an error).
+**
+** Argument zFromDb must be the name of a database ("main", "temp" etc.)
+** attached to the same database handle as the session object that contains
+** a table compatible with the table attached to the session by this function.
+** A table is considered compatible if it:
+**
+** <ul>
+** <li> Has the same name,
+** <li> Has the same set of columns declared in the same order, and
+** <li> Has the same PRIMARY KEY definition.
+** </ul>
+**
+** If the tables are not compatible, SQLITE_SCHEMA is returned. If the tables
+** are compatible but do not have any PRIMARY KEY columns, it is not an error
+** but no changes are added to the session object. As with other session
+** APIs, tables without PRIMARY KEYs are simply ignored.
+**
+** This function adds a set of changes to the session object that could be
+** used to update the table in database zFrom (call this the "from-table")
+** so that its content is the same as the table attached to the session
+** object (call this the "to-table"). Specifically:
+**
+** <ul>
+** <li> For each row (primary key) that exists in the to-table but not in
+** the from-table, an INSERT record is added to the session object.
+**
+** <li> For each row (primary key) that exists in the to-table but not in
+** the from-table, a DELETE record is added to the session object.
+**
+** <li> For each row (primary key) that exists in both tables, but features
+** different in each, an UPDATE record is added to the session.
+** </ul>
+**
+** To clarify, if this function is called and then a changeset constructed
+** using [sqlite3session_changeset()], then after applying that changeset to
+** database zFrom the contents of the two compatible tables would be
+** identical.
+**
+** It an error if database zFrom does not exist or does not contain the
+** required compatible table.
+**
+** If the operation successful, SQLITE_OK is returned. Otherwise, an SQLite
+** error code. In this case, if argument pzErrMsg is not NULL, *pzErrMsg
+** may be set to point to a buffer containing an English language error
+** message. It is the responsibility of the caller to free this buffer using
+** sqlite3_free().
+*/
+int sqlite3session_diff(
+ sqlite3_session *pSession,
+ const char *zFromDb,
+ const char *zTbl,
+ char **pzErrMsg
+);
+
+
+/*
+** CAPI3REF: Generate A Patchset From A Session Object
+**
+** The differences between a patchset and a changeset are that:
+**
+** <ul>
+** <li> DELETE records consist of the primary key fields only. The
+** original values of other fields are omitted.
+** <li> The original values of any modified fields are omitted from
+** UPDATE records.
+** </ul>
+**
+** A patchset blob may be used with up to date versions of all
+** sqlite3changeset_xxx API functions except for sqlite3changeset_invert(),
+** which returns SQLITE_CORRUPT if it is passed a patchset. Similarly,
+** attempting to use a patchset blob with old versions of the
+** sqlite3changeset_xxx APIs also provokes an SQLITE_CORRUPT error.
+**
+** Because the non-primary key "old.*" fields are omitted, no
+** SQLITE_CHANGESET_DATA conflicts can be detected or reported if a patchset
+** is passed to the sqlite3changeset_apply() API. Other conflict types work
+** in the same way as for changesets.
+**
+** Changes within a patchset are ordered in the same way as for changesets
+** generated by the sqlite3session_changeset() function (i.e. all changes for
+** a single table are grouped together, tables appear in the order in which
+** they were attached to the session object).
+*/
+int sqlite3session_patchset(
+ sqlite3_session *pSession, /* Session object */
+ int *pnPatchset, /* OUT: Size of buffer at *ppChangeset */
+ void **ppPatchset /* OUT: Buffer containing changeset */
+);
+
+/*
+** CAPI3REF: Test if a changeset has recorded any changes.
+**
+** Return non-zero if no changes to attached tables have been recorded by
+** the session object passed as the first argument. Otherwise, if one or
+** more changes have been recorded, return zero.
+**
+** Even if this function returns zero, it is possible that calling
+** [sqlite3session_changeset()] on the session handle may still return a
+** changeset that contains no changes. This can happen when a row in
+** an attached table is modified and then later on the original values
+** are restored. However, if this function returns non-zero, then it is
+** guaranteed that a call to sqlite3session_changeset() will return a
+** changeset containing zero changes.
+*/
+int sqlite3session_isempty(sqlite3_session *pSession);
+
+/*
+** CAPI3REF: Create An Iterator To Traverse A Changeset
+**
+** Create an iterator used to iterate through the contents of a changeset.
+** If successful, *pp is set to point to the iterator handle and SQLITE_OK
+** is returned. Otherwise, if an error occurs, *pp is set to zero and an
+** SQLite error code is returned.
+**
+** The following functions can be used to advance and query a changeset
+** iterator created by this function:
+**
+** <ul>
+** <li> [sqlite3changeset_next()]
+** <li> [sqlite3changeset_op()]
+** <li> [sqlite3changeset_new()]
+** <li> [sqlite3changeset_old()]
+** </ul>
+**
+** It is the responsibility of the caller to eventually destroy the iterator
+** by passing it to [sqlite3changeset_finalize()]. The buffer containing the
+** changeset (pChangeset) must remain valid until after the iterator is
+** destroyed.
+**
+** Assuming the changeset blob was created by one of the
+** [sqlite3session_changeset()], [sqlite3changeset_concat()] or
+** [sqlite3changeset_invert()] functions, all changes within the changeset
+** that apply to a single table are grouped together. This means that when
+** an application iterates through a changeset using an iterator created by
+** this function, all changes that relate to a single table are visted
+** consecutively. There is no chance that the iterator will visit a change
+** the applies to table X, then one for table Y, and then later on visit
+** another change for table X.
+*/
+int sqlite3changeset_start(
+ sqlite3_changeset_iter **pp, /* OUT: New changeset iterator handle */
+ int nChangeset, /* Size of changeset blob in bytes */
+ void *pChangeset /* Pointer to blob containing changeset */
+);
+
+
+/*
+** CAPI3REF: Advance A Changeset Iterator
+**
+** This function may only be used with iterators created by function
+** [sqlite3changeset_start()]. If it is called on an iterator passed to
+** a conflict-handler callback by [sqlite3changeset_apply()], SQLITE_MISUSE
+** is returned and the call has no effect.
+**
+** Immediately after an iterator is created by sqlite3changeset_start(), it
+** does not point to any change in the changeset. Assuming the changeset
+** is not empty, the first call to this function advances the iterator to
+** point to the first change in the changeset. Each subsequent call advances
+** the iterator to point to the next change in the changeset (if any). If
+** no error occurs and the iterator points to a valid change after a call
+** to sqlite3changeset_next() has advanced it, SQLITE_ROW is returned.
+** Otherwise, if all changes in the changeset have already been visited,
+** SQLITE_DONE is returned.
+**
+** If an error occurs, an SQLite error code is returned. Possible error
+** codes include SQLITE_CORRUPT (if the changeset buffer is corrupt) or
+** SQLITE_NOMEM.
+*/
+int sqlite3changeset_next(sqlite3_changeset_iter *pIter);
+
+/*
+** CAPI3REF: Obtain The Current Operation From A Changeset Iterator
+**
+** The pIter argument passed to this function may either be an iterator
+** passed to a conflict-handler by [sqlite3changeset_apply()], or an iterator
+** created by [sqlite3changeset_start()]. In the latter case, the most recent
+** call to [sqlite3changeset_next()] must have returned [SQLITE_ROW]. If this
+** is not the case, this function returns [SQLITE_MISUSE].
+**
+** If argument pzTab is not NULL, then *pzTab is set to point to a
+** nul-terminated utf-8 encoded string containing the name of the table
+** affected by the current change. The buffer remains valid until either
+** sqlite3changeset_next() is called on the iterator or until the
+** conflict-handler function returns. If pnCol is not NULL, then *pnCol is
+** set to the number of columns in the table affected by the change. If
+** pbIncorrect is not NULL, then *pbIndirect is set to true (1) if the change
+** is an indirect change, or false (0) otherwise. See the documentation for
+** [sqlite3session_indirect()] for a description of direct and indirect
+** changes. Finally, if pOp is not NULL, then *pOp is set to one of
+** [SQLITE_INSERT], [SQLITE_DELETE] or [SQLITE_UPDATE], depending on the
+** type of change that the iterator currently points to.
+**
+** If no error occurs, SQLITE_OK is returned. If an error does occur, an
+** SQLite error code is returned. The values of the output variables may not
+** be trusted in this case.
+*/
+int sqlite3changeset_op(
+ sqlite3_changeset_iter *pIter, /* Iterator object */
+ const char **pzTab, /* OUT: Pointer to table name */
+ int *pnCol, /* OUT: Number of columns in table */
+ int *pOp, /* OUT: SQLITE_INSERT, DELETE or UPDATE */
+ int *pbIndirect /* OUT: True for an 'indirect' change */
+);
+
+/*
+** CAPI3REF: Obtain The Primary Key Definition Of A Table
+**
+** For each modified table, a changeset includes the following:
+**
+** <ul>
+** <li> The number of columns in the table, and
+** <li> Which of those columns make up the tables PRIMARY KEY.
+** </ul>
+**
+** This function is used to find which columns comprise the PRIMARY KEY of
+** the table modified by the change that iterator pIter currently points to.
+** If successful, *pabPK is set to point to an array of nCol entries, where
+** nCol is the number of columns in the table. Elements of *pabPK are set to
+** 0x01 if the corresponding column is part of the tables primary key, or
+** 0x00 if it is not.
+**
+** If argumet pnCol is not NULL, then *pnCol is set to the number of columns
+** in the table.
+**
+** If this function is called when the iterator does not point to a valid
+** entry, SQLITE_MISUSE is returned and the output variables zeroed. Otherwise,
+** SQLITE_OK is returned and the output variables populated as described
+** above.
+*/
+int sqlite3changeset_pk(
+ sqlite3_changeset_iter *pIter, /* Iterator object */
+ unsigned char **pabPK, /* OUT: Array of boolean - true for PK cols */
+ int *pnCol /* OUT: Number of entries in output array */
+);
+
+/*
+** CAPI3REF: Obtain old.* Values From A Changeset Iterator
+**
+** The pIter argument passed to this function may either be an iterator
+** passed to a conflict-handler by [sqlite3changeset_apply()], or an iterator
+** created by [sqlite3changeset_start()]. In the latter case, the most recent
+** call to [sqlite3changeset_next()] must have returned SQLITE_ROW.
+** Furthermore, it may only be called if the type of change that the iterator
+** currently points to is either [SQLITE_DELETE] or [SQLITE_UPDATE]. Otherwise,
+** this function returns [SQLITE_MISUSE] and sets *ppValue to NULL.
+**
+** Argument iVal must be greater than or equal to 0, and less than the number
+** of columns in the table affected by the current change. Otherwise,
+** [SQLITE_RANGE] is returned and *ppValue is set to NULL.
+**
+** If successful, this function sets *ppValue to point to a protected
+** sqlite3_value object containing the iVal'th value from the vector of
+** original row values stored as part of the UPDATE or DELETE change and
+** returns SQLITE_OK. The name of the function comes from the fact that this
+** is similar to the "old.*" columns available to update or delete triggers.
+**
+** If some other error occurs (e.g. an OOM condition), an SQLite error code
+** is returned and *ppValue is set to NULL.
+*/
+int sqlite3changeset_old(
+ sqlite3_changeset_iter *pIter, /* Changeset iterator */
+ int iVal, /* Column number */
+ sqlite3_value **ppValue /* OUT: Old value (or NULL pointer) */
+);
+
+/*
+** CAPI3REF: Obtain new.* Values From A Changeset Iterator
+**
+** The pIter argument passed to this function may either be an iterator
+** passed to a conflict-handler by [sqlite3changeset_apply()], or an iterator
+** created by [sqlite3changeset_start()]. In the latter case, the most recent
+** call to [sqlite3changeset_next()] must have returned SQLITE_ROW.
+** Furthermore, it may only be called if the type of change that the iterator
+** currently points to is either [SQLITE_UPDATE] or [SQLITE_INSERT]. Otherwise,
+** this function returns [SQLITE_MISUSE] and sets *ppValue to NULL.
+**
+** Argument iVal must be greater than or equal to 0, and less than the number
+** of columns in the table affected by the current change. Otherwise,
+** [SQLITE_RANGE] is returned and *ppValue is set to NULL.
+**
+** If successful, this function sets *ppValue to point to a protected
+** sqlite3_value object containing the iVal'th value from the vector of
+** new row values stored as part of the UPDATE or INSERT change and
+** returns SQLITE_OK. If the change is an UPDATE and does not include
+** a new value for the requested column, *ppValue is set to NULL and
+** SQLITE_OK returned. The name of the function comes from the fact that
+** this is similar to the "new.*" columns available to update or delete
+** triggers.
+**
+** If some other error occurs (e.g. an OOM condition), an SQLite error code
+** is returned and *ppValue is set to NULL.
+*/
+int sqlite3changeset_new(
+ sqlite3_changeset_iter *pIter, /* Changeset iterator */
+ int iVal, /* Column number */
+ sqlite3_value **ppValue /* OUT: New value (or NULL pointer) */
+);
+
+/*
+** CAPI3REF: Obtain Conflicting Row Values From A Changeset Iterator
+**
+** This function should only be used with iterator objects passed to a
+** conflict-handler callback by [sqlite3changeset_apply()] with either
+** [SQLITE_CHANGESET_DATA] or [SQLITE_CHANGESET_CONFLICT]. If this function
+** is called on any other iterator, [SQLITE_MISUSE] is returned and *ppValue
+** is set to NULL.
+**
+** Argument iVal must be greater than or equal to 0, and less than the number
+** of columns in the table affected by the current change. Otherwise,
+** [SQLITE_RANGE] is returned and *ppValue is set to NULL.
+**
+** If successful, this function sets *ppValue to point to a protected
+** sqlite3_value object containing the iVal'th value from the
+** "conflicting row" associated with the current conflict-handler callback
+** and returns SQLITE_OK.
+**
+** If some other error occurs (e.g. an OOM condition), an SQLite error code
+** is returned and *ppValue is set to NULL.
+*/
+int sqlite3changeset_conflict(
+ sqlite3_changeset_iter *pIter, /* Changeset iterator */
+ int iVal, /* Column number */
+ sqlite3_value **ppValue /* OUT: Value from conflicting row */
+);
+
+/*
+** CAPI3REF: Determine The Number Of Foreign Key Constraint Violations
+**
+** This function may only be called with an iterator passed to an
+** SQLITE_CHANGESET_FOREIGN_KEY conflict handler callback. In this case
+** it sets the output variable to the total number of known foreign key
+** violations in the destination database and returns SQLITE_OK.
+**
+** In all other cases this function returns SQLITE_MISUSE.
+*/
+int sqlite3changeset_fk_conflicts(
+ sqlite3_changeset_iter *pIter, /* Changeset iterator */
+ int *pnOut /* OUT: Number of FK violations */
+);
+
+
+/*
+** CAPI3REF: Finalize A Changeset Iterator
+**
+** This function is used to finalize an iterator allocated with
+** [sqlite3changeset_start()].
+**
+** This function should only be called on iterators created using the
+** [sqlite3changeset_start()] function. If an application calls this
+** function with an iterator passed to a conflict-handler by
+** [sqlite3changeset_apply()], [SQLITE_MISUSE] is immediately returned and the
+** call has no effect.
+**
+** If an error was encountered within a call to an sqlite3changeset_xxx()
+** function (for example an [SQLITE_CORRUPT] in [sqlite3changeset_next()] or an
+** [SQLITE_NOMEM] in [sqlite3changeset_new()]) then an error code corresponding
+** to that error is returned by this function. Otherwise, SQLITE_OK is
+** returned. This is to allow the following pattern (pseudo-code):
+**
+** sqlite3changeset_start();
+** while( SQLITE_ROW==sqlite3changeset_next() ){
+** // Do something with change.
+** }
+** rc = sqlite3changeset_finalize();
+** if( rc!=SQLITE_OK ){
+** // An error has occurred
+** }
+*/
+int sqlite3changeset_finalize(sqlite3_changeset_iter *pIter);
+
+/*
+** CAPI3REF: Invert A Changeset
+**
+** This function is used to "invert" a changeset object. Applying an inverted
+** changeset to a database reverses the effects of applying the uninverted
+** changeset. Specifically:
+**
+** <ul>
+** <li> Each DELETE change is changed to an INSERT, and
+** <li> Each INSERT change is changed to a DELETE, and
+** <li> For each UPDATE change, the old.* and new.* values are exchanged.
+** </ul>
+**
+** This function does not change the order in which changes appear within
+** the changeset. It merely reverses the sense of each individual change.
+**
+** If successful, a pointer to a buffer containing the inverted changeset
+** is stored in *ppOut, the size of the same buffer is stored in *pnOut, and
+** SQLITE_OK is returned. If an error occurs, both *pnOut and *ppOut are
+** zeroed and an SQLite error code returned.
+**
+** It is the responsibility of the caller to eventually call sqlite3_free()
+** on the *ppOut pointer to free the buffer allocation following a successful
+** call to this function.
+**
+** WARNING/TODO: This function currently assumes that the input is a valid
+** changeset. If it is not, the results are undefined.
+*/
+int sqlite3changeset_invert(
+ int nIn, const void *pIn, /* Input changeset */
+ int *pnOut, void **ppOut /* OUT: Inverse of input */
+);
+
+/*
+** CAPI3REF: Concatenate Two Changeset Objects
+**
+** This function is used to concatenate two changesets, A and B, into a
+** single changeset. The result is a changeset equivalent to applying
+** changeset A followed by changeset B.
+**
+** This function combines the two input changesets using an
+** sqlite3_changegroup object. Calling it produces similar results as the
+** following code fragment:
+**
+** sqlite3_changegroup *pGrp;
+** rc = sqlite3_changegroup_new(&pGrp);
+** if( rc==SQLITE_OK ) rc = sqlite3changegroup_add(pGrp, nA, pA);
+** if( rc==SQLITE_OK ) rc = sqlite3changegroup_add(pGrp, nB, pB);
+** if( rc==SQLITE_OK ){
+** rc = sqlite3changegroup_output(pGrp, pnOut, ppOut);
+** }else{
+** *ppOut = 0;
+** *pnOut = 0;
+** }
+**
+** Refer to the sqlite3_changegroup documentation below for details.
+*/
+int sqlite3changeset_concat(
+ int nA, /* Number of bytes in buffer pA */
+ void *pA, /* Pointer to buffer containing changeset A */
+ int nB, /* Number of bytes in buffer pB */
+ void *pB, /* Pointer to buffer containing changeset B */
+ int *pnOut, /* OUT: Number of bytes in output changeset */
+ void **ppOut /* OUT: Buffer containing output changeset */
+);
+
+
+/*
+** Changegroup handle.
+*/
+typedef struct sqlite3_changegroup sqlite3_changegroup;
+
+/*
+** CAPI3REF: Combine two or more changesets into a single changeset.
+**
+** An sqlite3_changegroup object is used to combine two or more changesets
+** (or patchsets) into a single changeset (or patchset). A single changegroup
+** object may combine changesets or patchsets, but not both. The output is
+** always in the same format as the input.
+**
+** If successful, this function returns SQLITE_OK and populates (*pp) with
+** a pointer to a new sqlite3_changegroup object before returning. The caller
+** should eventually free the returned object using a call to
+** sqlite3changegroup_delete(). If an error occurs, an SQLite error code
+** (i.e. SQLITE_NOMEM) is returned and *pp is set to NULL.
+**
+** The usual usage pattern for an sqlite3_changegroup object is as follows:
+**
+** <ul>
+** <li> It is created using a call to sqlite3changegroup_new().
+**
+** <li> Zero or more changesets (or patchsets) are added to the object
+** by calling sqlite3changegroup_add().
+**
+** <li> The result of combining all input changesets together is obtained
+** by the application via a call to sqlite3changegroup_output().
+**
+** <li> The object is deleted using a call to sqlite3changegroup_delete().
+** </ul>
+**
+** Any number of calls to add() and output() may be made between the calls to
+** new() and delete(), and in any order.
+**
+** As well as the regular sqlite3changegroup_add() and
+** sqlite3changegroup_output() functions, also available are the streaming
+** versions sqlite3changegroup_add_strm() and sqlite3changegroup_output_strm().
+*/
+int sqlite3changegroup_new(sqlite3_changegroup **pp);
+
+/*
+** Add all changes within the changeset (or patchset) in buffer pData (size
+** nData bytes) to the changegroup.
+**
+** If the buffer contains a patchset, then all prior calls to this function
+** on the same changegroup object must also have specified patchsets. Or, if
+** the buffer contains a changeset, so must have the earlier calls to this
+** function. Otherwise, SQLITE_ERROR is returned and no changes are added
+** to the changegroup.
+**
+** Rows within the changeset and changegroup are identified by the values in
+** their PRIMARY KEY columns. A change in the changeset is considered to
+** apply to the same row as a change already present in the changegroup if
+** the two rows have the same primary key.
+**
+** Changes to rows that that do not already appear in the changegroup are
+** simply copied into it. Or, if both the new changeset and the changegroup
+** contain changes that apply to a single row, the final contents of the
+** changegroup depends on the type of each change, as follows:
+**
+** <table border=1 style="margin-left:8ex;margin-right:8ex">
+** <tr><th style="white-space:pre">Existing Change </th>
+** <th style="white-space:pre">New Change </th>
+** <th>Output Change
+** <tr><td>INSERT <td>INSERT <td>
+** The new change is ignored. This case does not occur if the new
+** changeset was recorded immediately after the changesets already
+** added to the changegroup.
+** <tr><td>INSERT <td>UPDATE <td>
+** The INSERT change remains in the changegroup. The values in the
+** INSERT change are modified as if the row was inserted by the
+** existing change and then updated according to the new change.
+** <tr><td>INSERT <td>DELETE <td>
+** The existing INSERT is removed from the changegroup. The DELETE is
+** not added.
+** <tr><td>UPDATE <td>INSERT <td>
+** The new change is ignored. This case does not occur if the new
+** changeset was recorded immediately after the changesets already
+** added to the changegroup.
+** <tr><td>UPDATE <td>UPDATE <td>
+** The existing UPDATE remains within the changegroup. It is amended
+** so that the accompanying values are as if the row was updated once
+** by the existing change and then again by the new change.
+** <tr><td>UPDATE <td>DELETE <td>
+** The existing UPDATE is replaced by the new DELETE within the
+** changegroup.
+** <tr><td>DELETE <td>INSERT <td>
+** If one or more of the column values in the row inserted by the
+** new change differ from those in the row deleted by the existing
+** change, the existing DELETE is replaced by an UPDATE within the
+** changegroup. Otherwise, if the inserted row is exactly the same
+** as the deleted row, the existing DELETE is simply discarded.
+** <tr><td>DELETE <td>UPDATE <td>
+** The new change is ignored. This case does not occur if the new
+** changeset was recorded immediately after the changesets already
+** added to the changegroup.
+** <tr><td>DELETE <td>DELETE <td>
+** The new change is ignored. This case does not occur if the new
+** changeset was recorded immediately after the changesets already
+** added to the changegroup.
+** </table>
+**
+** If the new changeset contains changes to a table that is already present
+** in the changegroup, then the number of columns and the position of the
+** primary key columns for the table must be consistent. If this is not the
+** case, this function fails with SQLITE_SCHEMA. If the input changeset
+** appears to be corrupt and the corruption is detected, SQLITE_CORRUPT is
+** returned. Or, if an out-of-memory condition occurs during processing, this
+** function returns SQLITE_NOMEM. In all cases, if an error occurs the
+** final contents of the changegroup is undefined.
+**
+** If no error occurs, SQLITE_OK is returned.
+*/
+int sqlite3changegroup_add(sqlite3_changegroup*, int nData, void *pData);
+
+/*
+** Obtain a buffer containing a changeset (or patchset) representing the
+** current contents of the changegroup. If the inputs to the changegroup
+** were themselves changesets, the output is a changeset. Or, if the
+** inputs were patchsets, the output is also a patchset.
+**
+** As with the output of the sqlite3session_changeset() and
+** sqlite3session_patchset() functions, all changes related to a single
+** table are grouped together in the output of this function. Tables appear
+** in the same order as for the very first changeset added to the changegroup.
+** If the second or subsequent changesets added to the changegroup contain
+** changes for tables that do not appear in the first changeset, they are
+** appended onto the end of the output changeset, again in the order in
+** which they are first encountered.
+**
+** If an error occurs, an SQLite error code is returned and the output
+** variables (*pnData) and (*ppData) are set to 0. Otherwise, SQLITE_OK
+** is returned and the output variables are set to the size of and a
+** pointer to the output buffer, respectively. In this case it is the
+** responsibility of the caller to eventually free the buffer using a
+** call to sqlite3_free().
+*/
+int sqlite3changegroup_output(
+ sqlite3_changegroup*,
+ int *pnData, /* OUT: Size of output buffer in bytes */
+ void **ppData /* OUT: Pointer to output buffer */
+);
+
+/*
+** Delete a changegroup object.
+*/
+void sqlite3changegroup_delete(sqlite3_changegroup*);
+
+/*
+** CAPI3REF: Apply A Changeset To A Database
+**
+** Apply a changeset to a database. This function attempts to update the
+** "main" database attached to handle db with the changes found in the
+** changeset passed via the second and third arguments.
+**
+** The fourth argument (xFilter) passed to this function is the "filter
+** callback". If it is not NULL, then for each table affected by at least one
+** change in the changeset, the filter callback is invoked with
+** the table name as the second argument, and a copy of the context pointer
+** passed as the sixth argument to this function as the first. If the "filter
+** callback" returns zero, then no attempt is made to apply any changes to
+** the table. Otherwise, if the return value is non-zero or the xFilter
+** argument to this function is NULL, all changes related to the table are
+** attempted.
+**
+** For each table that is not excluded by the filter callback, this function
+** tests that the target database contains a compatible table. A table is
+** considered compatible if all of the following are true:
+**
+** <ul>
+** <li> The table has the same name as the name recorded in the
+** changeset, and
+** <li> The table has the same number of columns as recorded in the
+** changeset, and
+** <li> The table has primary key columns in the same position as
+** recorded in the changeset.
+** </ul>
+**
+** If there is no compatible table, it is not an error, but none of the
+** changes associated with the table are applied. A warning message is issued
+** via the sqlite3_log() mechanism with the error code SQLITE_SCHEMA. At most
+** one such warning is issued for each table in the changeset.
+**
+** For each change for which there is a compatible table, an attempt is made
+** to modify the table contents according to the UPDATE, INSERT or DELETE
+** change. If a change cannot be applied cleanly, the conflict handler
+** function passed as the fifth argument to sqlite3changeset_apply() may be
+** invoked. A description of exactly when the conflict handler is invoked for
+** each type of change is below.
+**
+** Unlike the xFilter argument, xConflict may not be passed NULL. The results
+** of passing anything other than a valid function pointer as the xConflict
+** argument are undefined.
+**
+** Each time the conflict handler function is invoked, it must return one
+** of [SQLITE_CHANGESET_OMIT], [SQLITE_CHANGESET_ABORT] or
+** [SQLITE_CHANGESET_REPLACE]. SQLITE_CHANGESET_REPLACE may only be returned
+** if the second argument passed to the conflict handler is either
+** SQLITE_CHANGESET_DATA or SQLITE_CHANGESET_CONFLICT. If the conflict-handler
+** returns an illegal value, any changes already made are rolled back and
+** the call to sqlite3changeset_apply() returns SQLITE_MISUSE. Different
+** actions are taken by sqlite3changeset_apply() depending on the value
+** returned by each invocation of the conflict-handler function. Refer to
+** the documentation for the three
+** [SQLITE_CHANGESET_OMIT|available return values] for details.
+**
+** <dl>
+** <dt>DELETE Changes<dd>
+** For each DELETE change, this function checks if the target database
+** contains a row with the same primary key value (or values) as the
+** original row values stored in the changeset. If it does, and the values
+** stored in all non-primary key columns also match the values stored in
+** the changeset the row is deleted from the target database.
+**
+** If a row with matching primary key values is found, but one or more of
+** the non-primary key fields contains a value different from the original
+** row value stored in the changeset, the conflict-handler function is
+** invoked with [SQLITE_CHANGESET_DATA] as the second argument.
+**
+** If no row with matching primary key values is found in the database,
+** the conflict-handler function is invoked with [SQLITE_CHANGESET_NOTFOUND]
+** passed as the second argument.
+**
+** If the DELETE operation is attempted, but SQLite returns SQLITE_CONSTRAINT
+** (which can only happen if a foreign key constraint is violated), the
+** conflict-handler function is invoked with [SQLITE_CHANGESET_CONSTRAINT]
+** passed as the second argument. This includes the case where the DELETE
+** operation is attempted because an earlier call to the conflict handler
+** function returned [SQLITE_CHANGESET_REPLACE].
+**
+** <dt>INSERT Changes<dd>
+** For each INSERT change, an attempt is made to insert the new row into
+** the database.
+**
+** If the attempt to insert the row fails because the database already
+** contains a row with the same primary key values, the conflict handler
+** function is invoked with the second argument set to
+** [SQLITE_CHANGESET_CONFLICT].
+**
+** If the attempt to insert the row fails because of some other constraint
+** violation (e.g. NOT NULL or UNIQUE), the conflict handler function is
+** invoked with the second argument set to [SQLITE_CHANGESET_CONSTRAINT].
+** This includes the case where the INSERT operation is re-attempted because
+** an earlier call to the conflict handler function returned
+** [SQLITE_CHANGESET_REPLACE].
+**
+** <dt>UPDATE Changes<dd>
+** For each UPDATE change, this function checks if the target database
+** contains a row with the same primary key value (or values) as the
+** original row values stored in the changeset. If it does, and the values
+** stored in all non-primary key columns also match the values stored in
+** the changeset the row is updated within the target database.
+**
+** If a row with matching primary key values is found, but one or more of
+** the non-primary key fields contains a value different from an original
+** row value stored in the changeset, the conflict-handler function is
+** invoked with [SQLITE_CHANGESET_DATA] as the second argument. Since
+** UPDATE changes only contain values for non-primary key fields that are
+** to be modified, only those fields need to match the original values to
+** avoid the SQLITE_CHANGESET_DATA conflict-handler callback.
+**
+** If no row with matching primary key values is found in the database,
+** the conflict-handler function is invoked with [SQLITE_CHANGESET_NOTFOUND]
+** passed as the second argument.
+**
+** If the UPDATE operation is attempted, but SQLite returns
+** SQLITE_CONSTRAINT, the conflict-handler function is invoked with
+** [SQLITE_CHANGESET_CONSTRAINT] passed as the second argument.
+** This includes the case where the UPDATE operation is attempted after
+** an earlier call to the conflict handler function returned
+** [SQLITE_CHANGESET_REPLACE].
+** </dl>
+**
+** It is safe to execute SQL statements, including those that write to the
+** table that the callback related to, from within the xConflict callback.
+** This can be used to further customize the applications conflict
+** resolution strategy.
+**
+** All changes made by this function are enclosed in a savepoint transaction.
+** If any other error (aside from a constraint failure when attempting to
+** write to the target database) occurs, then the savepoint transaction is
+** rolled back, restoring the target database to its original state, and an
+** SQLite error code returned.
+*/
+int sqlite3changeset_apply(
+ sqlite3 *db, /* Apply change to "main" db of this handle */
+ int nChangeset, /* Size of changeset in bytes */
+ void *pChangeset, /* Changeset blob */
+ int(*xFilter)(
+ void *pCtx, /* Copy of sixth arg to _apply() */
+ const char *zTab /* Table name */
+ ),
+ int(*xConflict)(
+ void *pCtx, /* Copy of sixth arg to _apply() */
+ int eConflict, /* DATA, MISSING, CONFLICT, CONSTRAINT */
+ sqlite3_changeset_iter *p /* Handle describing change and conflict */
+ ),
+ void *pCtx /* First argument passed to xConflict */
+);
+
+/*
+** CAPI3REF: Constants Passed To The Conflict Handler
+**
+** Values that may be passed as the second argument to a conflict-handler.
+**
+** <dl>
+** <dt>SQLITE_CHANGESET_DATA<dd>
+** The conflict handler is invoked with CHANGESET_DATA as the second argument
+** when processing a DELETE or UPDATE change if a row with the required
+** PRIMARY KEY fields is present in the database, but one or more other
+** (non primary-key) fields modified by the update do not contain the
+** expected "before" values.
+**
+** The conflicting row, in this case, is the database row with the matching
+** primary key.
+**
+** <dt>SQLITE_CHANGESET_NOTFOUND<dd>
+** The conflict handler is invoked with CHANGESET_NOTFOUND as the second
+** argument when processing a DELETE or UPDATE change if a row with the
+** required PRIMARY KEY fields is not present in the database.
+**
+** There is no conflicting row in this case. The results of invoking the
+** sqlite3changeset_conflict() API are undefined.
+**
+** <dt>SQLITE_CHANGESET_CONFLICT<dd>
+** CHANGESET_CONFLICT is passed as the second argument to the conflict
+** handler while processing an INSERT change if the operation would result
+** in duplicate primary key values.
+**
+** The conflicting row in this case is the database row with the matching
+** primary key.
+**
+** <dt>SQLITE_CHANGESET_FOREIGN_KEY<dd>
+** If foreign key handling is enabled, and applying a changeset leaves the
+** database in a state containing foreign key violations, the conflict
+** handler is invoked with CHANGESET_FOREIGN_KEY as the second argument
+** exactly once before the changeset is committed. If the conflict handler
+** returns CHANGESET_OMIT, the changes, including those that caused the
+** foreign key constraint violation, are committed. Or, if it returns
+** CHANGESET_ABORT, the changeset is rolled back.
+**
+** No current or conflicting row information is provided. The only function
+** it is possible to call on the supplied sqlite3_changeset_iter handle
+** is sqlite3changeset_fk_conflicts().
+**
+** <dt>SQLITE_CHANGESET_CONSTRAINT<dd>
+** If any other constraint violation occurs while applying a change (i.e.
+** a UNIQUE, CHECK or NOT NULL constraint), the conflict handler is
+** invoked with CHANGESET_CONSTRAINT as the second argument.
+**
+** There is no conflicting row in this case. The results of invoking the
+** sqlite3changeset_conflict() API are undefined.
+**
+** </dl>
+*/
+#define SQLITE_CHANGESET_DATA 1
+#define SQLITE_CHANGESET_NOTFOUND 2
+#define SQLITE_CHANGESET_CONFLICT 3
+#define SQLITE_CHANGESET_CONSTRAINT 4
+#define SQLITE_CHANGESET_FOREIGN_KEY 5
+
+/*
+** CAPI3REF: Constants Returned By The Conflict Handler
+**
+** A conflict handler callback must return one of the following three values.
+**
+** <dl>
+** <dt>SQLITE_CHANGESET_OMIT<dd>
+** If a conflict handler returns this value no special action is taken. The
+** change that caused the conflict is not applied. The session module
+** continues to the next change in the changeset.
+**
+** <dt>SQLITE_CHANGESET_REPLACE<dd>
+** This value may only be returned if the second argument to the conflict
+** handler was SQLITE_CHANGESET_DATA or SQLITE_CHANGESET_CONFLICT. If this
+** is not the case, any changes applied so far are rolled back and the
+** call to sqlite3changeset_apply() returns SQLITE_MISUSE.
+**
+** If CHANGESET_REPLACE is returned by an SQLITE_CHANGESET_DATA conflict
+** handler, then the conflicting row is either updated or deleted, depending
+** on the type of change.
+**
+** If CHANGESET_REPLACE is returned by an SQLITE_CHANGESET_CONFLICT conflict
+** handler, then the conflicting row is removed from the database and a
+** second attempt to apply the change is made. If this second attempt fails,
+** the original row is restored to the database before continuing.
+**
+** <dt>SQLITE_CHANGESET_ABORT<dd>
+** If this value is returned, any changes applied so far are rolled back
+** and the call to sqlite3changeset_apply() returns SQLITE_ABORT.
+** </dl>
+*/
+#define SQLITE_CHANGESET_OMIT 0
+#define SQLITE_CHANGESET_REPLACE 1
+#define SQLITE_CHANGESET_ABORT 2
+
+/*
+** CAPI3REF: Streaming Versions of API functions.
+**
+** The six streaming API xxx_strm() functions serve similar purposes to the
+** corresponding non-streaming API functions:
+**
+** <table border=1 style="margin-left:8ex;margin-right:8ex">
+** <tr><th>Streaming function<th>Non-streaming equivalent</th>
+** <tr><td>sqlite3changeset_apply_str<td>[sqlite3changeset_apply]
+** <tr><td>sqlite3changeset_concat_str<td>[sqlite3changeset_concat]
+** <tr><td>sqlite3changeset_invert_str<td>[sqlite3changeset_invert]
+** <tr><td>sqlite3changeset_start_str<td>[sqlite3changeset_start]
+** <tr><td>sqlite3session_changeset_str<td>[sqlite3session_changeset]
+** <tr><td>sqlite3session_patchset_str<td>[sqlite3session_patchset]
+** </table>
+**
+** Non-streaming functions that accept changesets (or patchsets) as input
+** require that the entire changeset be stored in a single buffer in memory.
+** Similarly, those that return a changeset or patchset do so by returning
+** a pointer to a single large buffer allocated using sqlite3_malloc().
+** Normally this is convenient. However, if an application running in a
+** low-memory environment is required to handle very large changesets, the
+** large contiguous memory allocations required can become onerous.
+**
+** In order to avoid this problem, instead of a single large buffer, input
+** is passed to a streaming API functions by way of a callback function that
+** the sessions module invokes to incrementally request input data as it is
+** required. In all cases, a pair of API function parameters such as
+**
+** <pre>
+** &nbsp; int nChangeset,
+** &nbsp; void *pChangeset,
+** </pre>
+**
+** Is replaced by:
+**
+** <pre>
+** &nbsp; int (*xInput)(void *pIn, void *pData, int *pnData),
+** &nbsp; void *pIn,
+** </pre>
+**
+** Each time the xInput callback is invoked by the sessions module, the first
+** argument passed is a copy of the supplied pIn context pointer. The second
+** argument, pData, points to a buffer (*pnData) bytes in size. Assuming no
+** error occurs the xInput method should copy up to (*pnData) bytes of data
+** into the buffer and set (*pnData) to the actual number of bytes copied
+** before returning SQLITE_OK. If the input is completely exhausted, (*pnData)
+** should be set to zero to indicate this. Or, if an error occurs, an SQLite
+** error code should be returned. In all cases, if an xInput callback returns
+** an error, all processing is abandoned and the streaming API function
+** returns a copy of the error code to the caller.
+**
+** In the case of sqlite3changeset_start_strm(), the xInput callback may be
+** invoked by the sessions module at any point during the lifetime of the
+** iterator. If such an xInput callback returns an error, the iterator enters
+** an error state, whereby all subsequent calls to iterator functions
+** immediately fail with the same error code as returned by xInput.
+**
+** Similarly, streaming API functions that return changesets (or patchsets)
+** return them in chunks by way of a callback function instead of via a
+** pointer to a single large buffer. In this case, a pair of parameters such
+** as:
+**
+** <pre>
+** &nbsp; int *pnChangeset,
+** &nbsp; void **ppChangeset,
+** </pre>
+**
+** Is replaced by:
+**
+** <pre>
+** &nbsp; int (*xOutput)(void *pOut, const void *pData, int nData),
+** &nbsp; void *pOut
+** </pre>
+**
+** The xOutput callback is invoked zero or more times to return data to
+** the application. The first parameter passed to each call is a copy of the
+** pOut pointer supplied by the application. The second parameter, pData,
+** points to a buffer nData bytes in size containing the chunk of output
+** data being returned. If the xOutput callback successfully processes the
+** supplied data, it should return SQLITE_OK to indicate success. Otherwise,
+** it should return some other SQLite error code. In this case processing
+** is immediately abandoned and the streaming API function returns a copy
+** of the xOutput error code to the application.
+**
+** The sessions module never invokes an xOutput callback with the third
+** parameter set to a value less than or equal to zero. Other than this,
+** no guarantees are made as to the size of the chunks of data returned.
+*/
+int sqlite3changeset_apply_strm(
+ sqlite3 *db, /* Apply change to "main" db of this handle */
+ int (*xInput)(void *pIn, void *pData, int *pnData), /* Input function */
+ void *pIn, /* First arg for xInput */
+ int(*xFilter)(
+ void *pCtx, /* Copy of sixth arg to _apply() */
+ const char *zTab /* Table name */
+ ),
+ int(*xConflict)(
+ void *pCtx, /* Copy of sixth arg to _apply() */
+ int eConflict, /* DATA, MISSING, CONFLICT, CONSTRAINT */
+ sqlite3_changeset_iter *p /* Handle describing change and conflict */
+ ),
+ void *pCtx /* First argument passed to xConflict */
+);
+int sqlite3changeset_concat_strm(
+ int (*xInputA)(void *pIn, void *pData, int *pnData),
+ void *pInA,
+ int (*xInputB)(void *pIn, void *pData, int *pnData),
+ void *pInB,
+ int (*xOutput)(void *pOut, const void *pData, int nData),
+ void *pOut
+);
+int sqlite3changeset_invert_strm(
+ int (*xInput)(void *pIn, void *pData, int *pnData),
+ void *pIn,
+ int (*xOutput)(void *pOut, const void *pData, int nData),
+ void *pOut
+);
+int sqlite3changeset_start_strm(
+ sqlite3_changeset_iter **pp,
+ int (*xInput)(void *pIn, void *pData, int *pnData),
+ void *pIn
+);
+int sqlite3session_changeset_strm(
+ sqlite3_session *pSession,
+ int (*xOutput)(void *pOut, const void *pData, int nData),
+ void *pOut
+);
+int sqlite3session_patchset_strm(
+ sqlite3_session *pSession,
+ int (*xOutput)(void *pOut, const void *pData, int nData),
+ void *pOut
+);
+int sqlite3changegroup_add_strm(sqlite3_changegroup*,
+ int (*xInput)(void *pIn, void *pData, int *pnData),
+ void *pIn
+);
+int sqlite3changegroup_output_strm(sqlite3_changegroup*,
+ int (*xOutput)(void *pOut, const void *pData, int nData),
+ void *pOut
+);
+
+
+/*
+** Make sure we can call this stuff from C++.
+*/
+#if 0
+}
+#endif
+
+#endif /* !defined(__SQLITESESSION_H_) && defined(SQLITE_ENABLE_SESSION) */
+
+/******** End of sqlite3session.h *********/
+/******** Begin file fts5.h *********/
+/*
+** 2014 May 31
+**
+** 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.
+**
+******************************************************************************
+**
+** Interfaces to extend FTS5. Using the interfaces defined in this file,
+** FTS5 may be extended with:
+**
+** * custom tokenizers, and
+** * custom auxiliary functions.
+*/
+
+
+#ifndef _FTS5_H
+#define _FTS5_H
+
+
+#if 0
+extern "C" {
+#endif
+
+/*************************************************************************
+** CUSTOM AUXILIARY FUNCTIONS
+**
+** Virtual table implementations may overload SQL functions by implementing
+** the sqlite3_module.xFindFunction() method.
+*/
+
+typedef struct Fts5ExtensionApi Fts5ExtensionApi;
+typedef struct Fts5Context Fts5Context;
+typedef struct Fts5PhraseIter Fts5PhraseIter;
+
+typedef void (*fts5_extension_function)(
+ const Fts5ExtensionApi *pApi, /* API offered by current FTS version */
+ Fts5Context *pFts, /* First arg to pass to pApi functions */
+ sqlite3_context *pCtx, /* Context for returning result/error */
+ int nVal, /* Number of values in apVal[] array */
+ sqlite3_value **apVal /* Array of trailing arguments */
+);
+
+struct Fts5PhraseIter {
+ const unsigned char *a;
+ const unsigned char *b;
+};
+
+/*
+** EXTENSION API FUNCTIONS
+**
+** xUserData(pFts):
+** Return a copy of the context pointer the extension function was
+** registered with.
+**
+** xColumnTotalSize(pFts, iCol, pnToken):
+** If parameter iCol is less than zero, set output variable *pnToken
+** to the total number of tokens in the FTS5 table. Or, if iCol is
+** non-negative but less than the number of columns in the table, return
+** the total number of tokens in column iCol, considering all rows in
+** the FTS5 table.
+**
+** If parameter iCol is greater than or equal to the number of columns
+** in the table, SQLITE_RANGE is returned. Or, if an error occurs (e.g.
+** an OOM condition or IO error), an appropriate SQLite error code is
+** returned.
+**
+** xColumnCount(pFts):
+** Return the number of columns in the table.
+**
+** xColumnSize(pFts, iCol, pnToken):
+** If parameter iCol is less than zero, set output variable *pnToken
+** to the total number of tokens in the current row. Or, if iCol is
+** non-negative but less than the number of columns in the table, set
+** *pnToken to the number of tokens in column iCol of the current row.
+**
+** If parameter iCol is greater than or equal to the number of columns
+** in the table, SQLITE_RANGE is returned. Or, if an error occurs (e.g.
+** an OOM condition or IO error), an appropriate SQLite error code is
+** returned.
+**
+** This function may be quite inefficient if used with an FTS5 table
+** created with the "columnsize=0" option.
+**
+** xColumnText:
+** This function attempts to retrieve the text of column iCol of the
+** current document. If successful, (*pz) is set to point to a buffer
+** containing the text in utf-8 encoding, (*pn) is set to the size in bytes
+** (not characters) of the buffer and SQLITE_OK is returned. Otherwise,
+** if an error occurs, an SQLite error code is returned and the final values
+** of (*pz) and (*pn) are undefined.
+**
+** xPhraseCount:
+** Returns the number of phrases in the current query expression.
+**
+** xPhraseSize:
+** Returns the number of tokens in phrase iPhrase of the query. Phrases
+** are numbered starting from zero.
+**
+** xInstCount:
+** Set *pnInst to the total number of occurrences of all phrases within
+** the query within the current row. Return SQLITE_OK if successful, or
+** an error code (i.e. SQLITE_NOMEM) if an error occurs.
+**
+** This API can be quite slow if used with an FTS5 table created with the
+** "detail=none" or "detail=column" option. If the FTS5 table is created
+** with either "detail=none" or "detail=column" and "content=" option
+** (i.e. if it is a contentless table), then this API always returns 0.
+**
+** xInst:
+** Query for the details of phrase match iIdx within the current row.
+** Phrase matches are numbered starting from zero, so the iIdx argument
+** should be greater than or equal to zero and smaller than the value
+** output by xInstCount().
+**
+** Usually, output parameter *piPhrase is set to the phrase number, *piCol
+** to the column in which it occurs and *piOff the token offset of the
+** first token of the phrase. The exception is if the table was created
+** with the offsets=0 option specified. In this case *piOff is always
+** set to -1.
+**
+** Returns SQLITE_OK if successful, or an error code (i.e. SQLITE_NOMEM)
+** if an error occurs.
+**
+** This API can be quite slow if used with an FTS5 table created with the
+** "detail=none" or "detail=column" option.
+**
+** xRowid:
+** Returns the rowid of the current row.
+**
+** xTokenize:
+** Tokenize text using the tokenizer belonging to the FTS5 table.
+**
+** xQueryPhrase(pFts5, iPhrase, pUserData, xCallback):
+** This API function is used to query the FTS table for phrase iPhrase
+** of the current query. Specifically, a query equivalent to:
+**
+** ... FROM ftstable WHERE ftstable MATCH $p ORDER BY rowid
+**
+** with $p set to a phrase equivalent to the phrase iPhrase of the
+** current query is executed. Any column filter that applies to
+** phrase iPhrase of the current query is included in $p. For each
+** row visited, the callback function passed as the fourth argument
+** is invoked. The context and API objects passed to the callback
+** function may be used to access the properties of each matched row.
+** Invoking Api.xUserData() returns a copy of the pointer passed as
+** the third argument to pUserData.
+**
+** If the callback function returns any value other than SQLITE_OK, the
+** query is abandoned and the xQueryPhrase function returns immediately.
+** If the returned value is SQLITE_DONE, xQueryPhrase returns SQLITE_OK.
+** Otherwise, the error code is propagated upwards.
+**
+** If the query runs to completion without incident, SQLITE_OK is returned.
+** Or, if some error occurs before the query completes or is aborted by
+** the callback, an SQLite error code is returned.
+**
+**
+** xSetAuxdata(pFts5, pAux, xDelete)
+**
+** Save the pointer passed as the second argument as the extension functions
+** "auxiliary data". The pointer may then be retrieved by the current or any
+** future invocation of the same fts5 extension function made as part of
+** of the same MATCH query using the xGetAuxdata() API.
+**
+** Each extension function is allocated a single auxiliary data slot for
+** each FTS query (MATCH expression). If the extension function is invoked
+** more than once for a single FTS query, then all invocations share a
+** single auxiliary data context.
+**
+** If there is already an auxiliary data pointer when this function is
+** invoked, then it is replaced by the new pointer. If an xDelete callback
+** was specified along with the original pointer, it is invoked at this
+** point.
+**
+** The xDelete callback, if one is specified, is also invoked on the
+** auxiliary data pointer after the FTS5 query has finished.
+**
+** If an error (e.g. an OOM condition) occurs within this function, an
+** the auxiliary data is set to NULL and an error code returned. If the
+** xDelete parameter was not NULL, it is invoked on the auxiliary data
+** pointer before returning.
+**
+**
+** xGetAuxdata(pFts5, bClear)
+**
+** Returns the current auxiliary data pointer for the fts5 extension
+** function. See the xSetAuxdata() method for details.
+**
+** If the bClear argument is non-zero, then the auxiliary data is cleared
+** (set to NULL) before this function returns. In this case the xDelete,
+** if any, is not invoked.
+**
+**
+** xRowCount(pFts5, pnRow)
+**
+** This function is used to retrieve the total number of rows in the table.
+** In other words, the same value that would be returned by:
+**
+** SELECT count(*) FROM ftstable;
+**
+** xPhraseFirst()
+** This function is used, along with type Fts5PhraseIter and the xPhraseNext
+** method, to iterate through all instances of a single query phrase within
+** the current row. This is the same information as is accessible via the
+** xInstCount/xInst APIs. While the xInstCount/xInst APIs are more convenient
+** to use, this API may be faster under some circumstances. To iterate
+** through instances of phrase iPhrase, use the following code:
+**
+** Fts5PhraseIter iter;
+** int iCol, iOff;
+** for(pApi->xPhraseFirst(pFts, iPhrase, &iter, &iCol, &iOff);
+** iCol>=0;
+** pApi->xPhraseNext(pFts, &iter, &iCol, &iOff)
+** ){
+** // An instance of phrase iPhrase at offset iOff of column iCol
+** }
+**
+** The Fts5PhraseIter structure is defined above. Applications should not
+** modify this structure directly - it should only be used as shown above
+** with the xPhraseFirst() and xPhraseNext() API methods (and by
+** xPhraseFirstColumn() and xPhraseNextColumn() as illustrated below).
+**
+** This API can be quite slow if used with an FTS5 table created with the
+** "detail=none" or "detail=column" option. If the FTS5 table is created
+** with either "detail=none" or "detail=column" and "content=" option
+** (i.e. if it is a contentless table), then this API always iterates
+** through an empty set (all calls to xPhraseFirst() set iCol to -1).
+**
+** xPhraseNext()
+** See xPhraseFirst above.
+**
+** xPhraseFirstColumn()
+** This function and xPhraseNextColumn() are similar to the xPhraseFirst()
+** and xPhraseNext() APIs described above. The difference is that instead
+** of iterating through all instances of a phrase in the current row, these
+** APIs are used to iterate through the set of columns in the current row
+** that contain one or more instances of a specified phrase. For example:
+**
+** Fts5PhraseIter iter;
+** int iCol;
+** for(pApi->xPhraseFirstColumn(pFts, iPhrase, &iter, &iCol);
+** iCol>=0;
+** pApi->xPhraseNextColumn(pFts, &iter, &iCol)
+** ){
+** // Column iCol contains at least one instance of phrase iPhrase
+** }
+**
+** This API can be quite slow if used with an FTS5 table created with the
+** "detail=none" option. If the FTS5 table is created with either
+** "detail=none" "content=" option (i.e. if it is a contentless table),
+** then this API always iterates through an empty set (all calls to
+** xPhraseFirstColumn() set iCol to -1).
+**
+** The information accessed using this API and its companion
+** xPhraseFirstColumn() may also be obtained using xPhraseFirst/xPhraseNext
+** (or xInst/xInstCount). The chief advantage of this API is that it is
+** significantly more efficient than those alternatives when used with
+** "detail=column" tables.
+**
+** xPhraseNextColumn()
+** See xPhraseFirstColumn above.
+*/
+struct Fts5ExtensionApi {
+ int iVersion; /* Currently always set to 3 */
+
+ void *(*xUserData)(Fts5Context*);
+
+ int (*xColumnCount)(Fts5Context*);
+ int (*xRowCount)(Fts5Context*, sqlite3_int64 *pnRow);
+ int (*xColumnTotalSize)(Fts5Context*, int iCol, sqlite3_int64 *pnToken);
+
+ int (*xTokenize)(Fts5Context*,
+ const char *pText, int nText, /* Text to tokenize */
+ void *pCtx, /* Context passed to xToken() */
+ int (*xToken)(void*, int, const char*, int, int, int) /* Callback */
+ );
+
+ int (*xPhraseCount)(Fts5Context*);
+ int (*xPhraseSize)(Fts5Context*, int iPhrase);
+
+ int (*xInstCount)(Fts5Context*, int *pnInst);
+ int (*xInst)(Fts5Context*, int iIdx, int *piPhrase, int *piCol, int *piOff);
+
+ sqlite3_int64 (*xRowid)(Fts5Context*);
+ int (*xColumnText)(Fts5Context*, int iCol, const char **pz, int *pn);
+ int (*xColumnSize)(Fts5Context*, int iCol, int *pnToken);
+
+ int (*xQueryPhrase)(Fts5Context*, int iPhrase, void *pUserData,
+ int(*)(const Fts5ExtensionApi*,Fts5Context*,void*)
+ );
+ int (*xSetAuxdata)(Fts5Context*, void *pAux, void(*xDelete)(void*));
+ void *(*xGetAuxdata)(Fts5Context*, int bClear);
+
+ int (*xPhraseFirst)(Fts5Context*, int iPhrase, Fts5PhraseIter*, int*, int*);
+ void (*xPhraseNext)(Fts5Context*, Fts5PhraseIter*, int *piCol, int *piOff);
+
+ int (*xPhraseFirstColumn)(Fts5Context*, int iPhrase, Fts5PhraseIter*, int*);
+ void (*xPhraseNextColumn)(Fts5Context*, Fts5PhraseIter*, int *piCol);
+};
+
+/*
+** CUSTOM AUXILIARY FUNCTIONS
+*************************************************************************/
+
+/*************************************************************************
+** CUSTOM TOKENIZERS
+**
+** Applications may also register custom tokenizer types. A tokenizer
+** is registered by providing fts5 with a populated instance of the
+** following structure. All structure methods must be defined, setting
+** any member of the fts5_tokenizer struct to NULL leads to undefined
+** behaviour. The structure methods are expected to function as follows:
+**
+** xCreate:
+** This function is used to allocate and inititalize a tokenizer instance.
+** A tokenizer instance is required to actually tokenize text.
+**
+** The first argument passed to this function is a copy of the (void*)
+** pointer provided by the application when the fts5_tokenizer object
+** was registered with FTS5 (the third argument to xCreateTokenizer()).
+** The second and third arguments are an array of nul-terminated strings
+** containing the tokenizer arguments, if any, specified following the
+** tokenizer name as part of the CREATE VIRTUAL TABLE statement used
+** to create the FTS5 table.
+**
+** The final argument is an output variable. If successful, (*ppOut)
+** should be set to point to the new tokenizer handle and SQLITE_OK
+** returned. If an error occurs, some value other than SQLITE_OK should
+** be returned. In this case, fts5 assumes that the final value of *ppOut
+** is undefined.
+**
+** xDelete:
+** This function is invoked to delete a tokenizer handle previously
+** allocated using xCreate(). Fts5 guarantees that this function will
+** be invoked exactly once for each successful call to xCreate().
+**
+** xTokenize:
+** This function is expected to tokenize the nText byte string indicated
+** by argument pText. pText may or may not be nul-terminated. The first
+** argument passed to this function is a pointer to an Fts5Tokenizer object
+** returned by an earlier call to xCreate().
+**
+** The second argument indicates the reason that FTS5 is requesting
+** tokenization of the supplied text. This is always one of the following
+** four values:
+**
+** <ul><li> <b>FTS5_TOKENIZE_DOCUMENT</b> - A document is being inserted into
+** or removed from the FTS table. The tokenizer is being invoked to
+** determine the set of tokens to add to (or delete from) the
+** FTS index.
+**
+** <li> <b>FTS5_TOKENIZE_QUERY</b> - A MATCH query is being executed
+** against the FTS index. The tokenizer is being called to tokenize
+** a bareword or quoted string specified as part of the query.
+**
+** <li> <b>(FTS5_TOKENIZE_QUERY | FTS5_TOKENIZE_PREFIX)</b> - Same as
+** FTS5_TOKENIZE_QUERY, except that the bareword or quoted string is
+** followed by a "*" character, indicating that the last token
+** returned by the tokenizer will be treated as a token prefix.
+**
+** <li> <b>FTS5_TOKENIZE_AUX</b> - The tokenizer is being invoked to
+** satisfy an fts5_api.xTokenize() request made by an auxiliary
+** function. Or an fts5_api.xColumnSize() request made by the same
+** on a columnsize=0 database.
+** </ul>
+**
+** For each token in the input string, the supplied callback xToken() must
+** be invoked. The first argument to it should be a copy of the pointer
+** passed as the second argument to xTokenize(). The third and fourth
+** arguments are a pointer to a buffer containing the token text, and the
+** size of the token in bytes. The 4th and 5th arguments are the byte offsets
+** of the first byte of and first byte immediately following the text from
+** which the token is derived within the input.
+**
+** The second argument passed to the xToken() callback ("tflags") should
+** normally be set to 0. The exception is if the tokenizer supports
+** synonyms. In this case see the discussion below for details.
+**
+** FTS5 assumes the xToken() callback is invoked for each token in the
+** order that they occur within the input text.
+**
+** If an xToken() callback returns any value other than SQLITE_OK, then
+** the tokenization should be abandoned and the xTokenize() method should
+** immediately return a copy of the xToken() return value. Or, if the
+** input buffer is exhausted, xTokenize() should return SQLITE_OK. Finally,
+** if an error occurs with the xTokenize() implementation itself, it
+** may abandon the tokenization and return any error code other than
+** SQLITE_OK or SQLITE_DONE.
+**
+** SYNONYM SUPPORT
+**
+** Custom tokenizers may also support synonyms. Consider a case in which a
+** user wishes to query for a phrase such as "first place". Using the
+** built-in tokenizers, the FTS5 query 'first + place' will match instances
+** of "first place" within the document set, but not alternative forms
+** such as "1st place". In some applications, it would be better to match
+** all instances of "first place" or "1st place" regardless of which form
+** the user specified in the MATCH query text.
+**
+** There are several ways to approach this in FTS5:
+**
+** <ol><li> By mapping all synonyms to a single token. In this case, the
+** In the above example, this means that the tokenizer returns the
+** same token for inputs "first" and "1st". Say that token is in
+** fact "first", so that when the user inserts the document "I won
+** 1st place" entries are added to the index for tokens "i", "won",
+** "first" and "place". If the user then queries for '1st + place',
+** the tokenizer substitutes "first" for "1st" and the query works
+** as expected.
+**
+** <li> By adding multiple synonyms for a single term to the FTS index.
+** In this case, when tokenizing query text, the tokenizer may
+** provide multiple synonyms for a single term within the document.
+** FTS5 then queries the index for each synonym individually. For
+** example, faced with the query:
+**
+** <codeblock>
+** ... MATCH 'first place'</codeblock>
+**
+** the tokenizer offers both "1st" and "first" as synonyms for the
+** first token in the MATCH query and FTS5 effectively runs a query
+** similar to:
+**
+** <codeblock>
+** ... MATCH '(first OR 1st) place'</codeblock>
+**
+** except that, for the purposes of auxiliary functions, the query
+** still appears to contain just two phrases - "(first OR 1st)"
+** being treated as a single phrase.
+**
+** <li> By adding multiple synonyms for a single term to the FTS index.
+** Using this method, when tokenizing document text, the tokenizer
+** provides multiple synonyms for each token. So that when a
+** document such as "I won first place" is tokenized, entries are
+** added to the FTS index for "i", "won", "first", "1st" and
+** "place".
+**
+** This way, even if the tokenizer does not provide synonyms
+** when tokenizing query text (it should not - to do would be
+** inefficient), it doesn't matter if the user queries for
+** 'first + place' or '1st + place', as there are entires in the
+** FTS index corresponding to both forms of the first token.
+** </ol>
+**
+** Whether it is parsing document or query text, any call to xToken that
+** specifies a <i>tflags</i> argument with the FTS5_TOKEN_COLOCATED bit
+** is considered to supply a synonym for the previous token. For example,
+** when parsing the document "I won first place", a tokenizer that supports
+** synonyms would call xToken() 5 times, as follows:
+**
+** <codeblock>
+** xToken(pCtx, 0, "i", 1, 0, 1);
+** xToken(pCtx, 0, "won", 3, 2, 5);
+** xToken(pCtx, 0, "first", 5, 6, 11);
+** xToken(pCtx, FTS5_TOKEN_COLOCATED, "1st", 3, 6, 11);
+** xToken(pCtx, 0, "place", 5, 12, 17);
+**</codeblock>
+**
+** It is an error to specify the FTS5_TOKEN_COLOCATED flag the first time
+** xToken() is called. Multiple synonyms may be specified for a single token
+** by making multiple calls to xToken(FTS5_TOKEN_COLOCATED) in sequence.
+** There is no limit to the number of synonyms that may be provided for a
+** single token.
+**
+** In many cases, method (1) above is the best approach. It does not add
+** extra data to the FTS index or require FTS5 to query for multiple terms,
+** so it is efficient in terms of disk space and query speed. However, it
+** does not support prefix queries very well. If, as suggested above, the
+** token "first" is subsituted for "1st" by the tokenizer, then the query:
+**
+** <codeblock>
+** ... MATCH '1s*'</codeblock>
+**
+** will not match documents that contain the token "1st" (as the tokenizer
+** will probably not map "1s" to any prefix of "first").
+**
+** For full prefix support, method (3) may be preferred. In this case,
+** because the index contains entries for both "first" and "1st", prefix
+** queries such as 'fi*' or '1s*' will match correctly. However, because
+** extra entries are added to the FTS index, this method uses more space
+** within the database.
+**
+** Method (2) offers a midpoint between (1) and (3). Using this method,
+** a query such as '1s*' will match documents that contain the literal
+** token "1st", but not "first" (assuming the tokenizer is not able to
+** provide synonyms for prefixes). However, a non-prefix query like '1st'
+** will match against "1st" and "first". This method does not require
+** extra disk space, as no extra entries are added to the FTS index.
+** On the other hand, it may require more CPU cycles to run MATCH queries,
+** as separate queries of the FTS index are required for each synonym.
+**
+** When using methods (2) or (3), it is important that the tokenizer only
+** provide synonyms when tokenizing document text (method (2)) or query
+** text (method (3)), not both. Doing so will not cause any errors, but is
+** inefficient.
+*/
+typedef struct Fts5Tokenizer Fts5Tokenizer;
+typedef struct fts5_tokenizer fts5_tokenizer;
+struct fts5_tokenizer {
+ int (*xCreate)(void*, const char **azArg, int nArg, Fts5Tokenizer **ppOut);
+ void (*xDelete)(Fts5Tokenizer*);
+ int (*xTokenize)(Fts5Tokenizer*,
+ void *pCtx,
+ int flags, /* Mask of FTS5_TOKENIZE_* flags */
+ const char *pText, int nText,
+ int (*xToken)(
+ void *pCtx, /* Copy of 2nd argument to xTokenize() */
+ int tflags, /* Mask of FTS5_TOKEN_* flags */
+ const char *pToken, /* Pointer to buffer containing token */
+ int nToken, /* Size of token in bytes */
+ int iStart, /* Byte offset of token within input text */
+ int iEnd /* Byte offset of end of token within input text */
+ )
+ );
+};
+
+/* Flags that may be passed as the third argument to xTokenize() */
+#define FTS5_TOKENIZE_QUERY 0x0001
+#define FTS5_TOKENIZE_PREFIX 0x0002
+#define FTS5_TOKENIZE_DOCUMENT 0x0004
+#define FTS5_TOKENIZE_AUX 0x0008
+
+/* Flags that may be passed by the tokenizer implementation back to FTS5
+** as the third argument to the supplied xToken callback. */
+#define FTS5_TOKEN_COLOCATED 0x0001 /* Same position as prev. token */
+
+/*
+** END OF CUSTOM TOKENIZERS
+*************************************************************************/
+
+/*************************************************************************
+** FTS5 EXTENSION REGISTRATION API
+*/
+typedef struct fts5_api fts5_api;
+struct fts5_api {
+ int iVersion; /* Currently always set to 2 */
+
+ /* Create a new tokenizer */
+ int (*xCreateTokenizer)(
+ fts5_api *pApi,
+ const char *zName,
+ void *pContext,
+ fts5_tokenizer *pTokenizer,
+ void (*xDestroy)(void*)
+ );
+
+ /* Find an existing tokenizer */
+ int (*xFindTokenizer)(
+ fts5_api *pApi,
+ const char *zName,
+ void **ppContext,
+ fts5_tokenizer *pTokenizer
+ );
+
+ /* Create a new auxiliary function */
+ int (*xCreateFunction)(
+ fts5_api *pApi,
+ const char *zName,
+ void *pContext,
+ fts5_extension_function xFunction,
+ void (*xDestroy)(void*)
+ );
+};
+
+/*
+** END OF REGISTRATION API
+*************************************************************************/
+
+#if 0
+} /* end of the 'extern "C"' block */
+#endif
+
+#endif /* _FTS5_H */
+
+
+/******** End of fts5.h *********/
/************** End of sqlite3.h *********************************************/
/************** Continuing where we left off in sqliteInt.h ******************/
@@ -8153,13 +10555,13 @@ struct sqlite3_rtree_query_info {
** 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-30185-15359 The default suggested cache size is -2000,
+** which means the cache size is limited to 2048000 bytes of memory.
** 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
+# define SQLITE_DEFAULT_CACHE_SIZE -2000
#endif
/*
@@ -8172,8 +10574,9 @@ struct sqlite3_rtree_query_info {
/*
** The maximum number of attached databases. This must be between 0
-** and 62. The upper bound on 62 is because a 64-bit integer bitmap
-** is used internally to track attached databases.
+** and 125. The upper bound of 125 is because the attached databases are
+** counted using a signed 8-bit integer which has a maximum value of 127
+** and we have to allow 2 extra counts for the "main" and "temp" databases.
*/
#ifndef SQLITE_MAX_ATTACHED
# define SQLITE_MAX_ATTACHED 10
@@ -8208,7 +10611,7 @@ struct sqlite3_rtree_query_info {
** The default size of a database page.
*/
#ifndef SQLITE_DEFAULT_PAGE_SIZE
-# define SQLITE_DEFAULT_PAGE_SIZE 1024
+# define SQLITE_DEFAULT_PAGE_SIZE 4096
#endif
#if SQLITE_DEFAULT_PAGE_SIZE>SQLITE_MAX_PAGE_SIZE
# undef SQLITE_DEFAULT_PAGE_SIZE
@@ -8289,7 +10692,7 @@ struct sqlite3_rtree_query_info {
** to the next, so we have developed the following set of #if statements
** to generate appropriate macros for a wide range of compilers.
**
-** The correct "ANSI" way to do this is to use the intptr_t type.
+** The correct "ANSI" way to do this is to use the intptr_t type.
** Unfortunately, that typedef is not available on all compilers, or
** if it is available, it requires an #include of specific headers
** that vary from one machine to the next.
@@ -8326,6 +10729,24 @@ struct sqlite3_rtree_query_info {
#endif
/*
+** Make sure that the compiler intrinsics we desire are enabled when
+** compiling with an appropriate version of MSVC unless prevented by
+** the SQLITE_DISABLE_INTRINSIC define.
+*/
+#if !defined(SQLITE_DISABLE_INTRINSIC)
+# if defined(_MSC_VER) && _MSC_VER>=1300
+# if !defined(_WIN32_WCE)
+# include <intrin.h>
+# pragma intrinsic(_byteswap_ushort)
+# pragma intrinsic(_byteswap_ulong)
+# pragma intrinsic(_ReadWriteBarrier)
+# else
+# include <cmnintrin.h>
+# endif
+# endif
+#endif
+
+/*
** The SQLITE_THREADSAFE macro must be defined as 0, 1, or 2.
** 0 means mutexes are permanently disable and the library is never
** threadsafe. 1 means the library is serialized which is the highest
@@ -8423,7 +10844,7 @@ struct sqlite3_rtree_query_info {
** is set. Thus NDEBUG becomes an opt-in rather than an opt-out
** feature.
*/
-#if !defined(NDEBUG) && !defined(SQLITE_DEBUG)
+#if !defined(NDEBUG) && !defined(SQLITE_DEBUG)
# define NDEBUG 1
#endif
#if defined(NDEBUG) && defined(SQLITE_DEBUG)
@@ -8438,7 +10859,7 @@ struct sqlite3_rtree_query_info {
#endif
/*
-** The testcase() macro is used to aid in coverage testing. When
+** The testcase() macro is used to aid in coverage testing. When
** doing coverage testing, the condition inside the argument to
** testcase() must be evaluated both true and false in order to
** get full branch coverage. The testcase() macro is inserted
@@ -8484,7 +10905,7 @@ SQLITE_PRIVATE void sqlite3Coverage(int);
#endif
/*
-** The ALWAYS and NEVER macros surround boolean expressions which
+** The ALWAYS and NEVER macros surround boolean expressions which
** are intended to always be true or false, respectively. Such
** expressions could be omitted from the code completely. But they
** are included in a few cases in order to enhance the resilience
@@ -8498,7 +10919,7 @@ SQLITE_PRIVATE void sqlite3Coverage(int);
** be true and false so that the unreachable code they specify will
** not be counted as untested code.
*/
-#if defined(SQLITE_COVERAGE_TEST)
+#if defined(SQLITE_COVERAGE_TEST) || defined(SQLITE_MUTATION_TEST)
# define ALWAYS(X) (1)
# define NEVER(X) (0)
#elif !defined(NDEBUG)
@@ -8510,6 +10931,21 @@ SQLITE_PRIVATE void sqlite3Coverage(int);
#endif
/*
+** Some malloc failures are only possible if SQLITE_TEST_REALLOC_STRESS is
+** defined. We need to defend against those failures when testing with
+** SQLITE_TEST_REALLOC_STRESS, but we don't want the unreachable branches
+** during a normal build. The following macro can be used to disable tests
+** that are always false except when SQLITE_TEST_REALLOC_STRESS is set.
+*/
+#if defined(SQLITE_TEST_REALLOC_STRESS)
+# define ONLY_IF_REALLOC_STRESS(X) (X)
+#elif !defined(NDEBUG)
+# define ONLY_IF_REALLOC_STRESS(X) ((X)?(assert(0),1):0)
+#else
+# define ONLY_IF_REALLOC_STRESS(X) (0)
+#endif
+
+/*
** Declarations used for tracing the operating system interfaces.
*/
#if defined(SQLITE_FORCE_OS_TRACE) || defined(SQLITE_TEST) || \
@@ -8536,6 +10972,13 @@ SQLITE_PRIVATE void sqlite3Coverage(int);
#endif
/*
+** SQLITE_ENABLE_EXPLAIN_COMMENTS is incompatible with SQLITE_OMIT_EXPLAIN
+*/
+#ifdef SQLITE_OMIT_EXPLAIN
+# undef SQLITE_ENABLE_EXPLAIN_COMMENTS
+#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.
@@ -8681,76 +11124,76 @@ SQLITE_PRIVATE void sqlite3HashClear(Hash*);
#define TK_AS 24
#define TK_WITHOUT 25
#define TK_COMMA 26
-#define TK_ID 27
-#define TK_INDEXED 28
-#define TK_ABORT 29
-#define TK_ACTION 30
-#define TK_AFTER 31
-#define TK_ANALYZE 32
-#define TK_ASC 33
-#define TK_ATTACH 34
-#define TK_BEFORE 35
-#define TK_BY 36
-#define TK_CASCADE 37
-#define TK_CAST 38
-#define TK_COLUMNKW 39
-#define TK_CONFLICT 40
-#define TK_DATABASE 41
-#define TK_DESC 42
-#define TK_DETACH 43
-#define TK_EACH 44
-#define TK_FAIL 45
-#define TK_FOR 46
-#define TK_IGNORE 47
-#define TK_INITIALLY 48
-#define TK_INSTEAD 49
-#define TK_LIKE_KW 50
-#define TK_MATCH 51
-#define TK_NO 52
-#define TK_KEY 53
-#define TK_OF 54
-#define TK_OFFSET 55
-#define TK_PRAGMA 56
-#define TK_RAISE 57
-#define TK_RECURSIVE 58
-#define TK_REPLACE 59
-#define TK_RESTRICT 60
-#define TK_ROW 61
-#define TK_TRIGGER 62
-#define TK_VACUUM 63
-#define TK_VIEW 64
-#define TK_VIRTUAL 65
-#define TK_WITH 66
-#define TK_REINDEX 67
-#define TK_RENAME 68
-#define TK_CTIME_KW 69
-#define TK_ANY 70
-#define TK_OR 71
-#define TK_AND 72
-#define TK_IS 73
-#define TK_BETWEEN 74
-#define TK_IN 75
-#define TK_ISNULL 76
-#define TK_NOTNULL 77
-#define TK_NE 78
-#define TK_EQ 79
-#define TK_GT 80
-#define TK_LE 81
-#define TK_LT 82
-#define TK_GE 83
-#define TK_ESCAPE 84
-#define TK_BITAND 85
-#define TK_BITOR 86
-#define TK_LSHIFT 87
-#define TK_RSHIFT 88
-#define TK_PLUS 89
-#define TK_MINUS 90
-#define TK_STAR 91
-#define TK_SLASH 92
-#define TK_REM 93
-#define TK_CONCAT 94
-#define TK_COLLATE 95
-#define TK_BITNOT 96
+#define TK_OR 27
+#define TK_AND 28
+#define TK_IS 29
+#define TK_MATCH 30
+#define TK_LIKE_KW 31
+#define TK_BETWEEN 32
+#define TK_IN 33
+#define TK_ISNULL 34
+#define TK_NOTNULL 35
+#define TK_NE 36
+#define TK_EQ 37
+#define TK_GT 38
+#define TK_LE 39
+#define TK_LT 40
+#define TK_GE 41
+#define TK_ESCAPE 42
+#define TK_BITAND 43
+#define TK_BITOR 44
+#define TK_LSHIFT 45
+#define TK_RSHIFT 46
+#define TK_PLUS 47
+#define TK_MINUS 48
+#define TK_STAR 49
+#define TK_SLASH 50
+#define TK_REM 51
+#define TK_CONCAT 52
+#define TK_COLLATE 53
+#define TK_BITNOT 54
+#define TK_ID 55
+#define TK_INDEXED 56
+#define TK_ABORT 57
+#define TK_ACTION 58
+#define TK_AFTER 59
+#define TK_ANALYZE 60
+#define TK_ASC 61
+#define TK_ATTACH 62
+#define TK_BEFORE 63
+#define TK_BY 64
+#define TK_CASCADE 65
+#define TK_CAST 66
+#define TK_COLUMNKW 67
+#define TK_CONFLICT 68
+#define TK_DATABASE 69
+#define TK_DESC 70
+#define TK_DETACH 71
+#define TK_EACH 72
+#define TK_FAIL 73
+#define TK_FOR 74
+#define TK_IGNORE 75
+#define TK_INITIALLY 76
+#define TK_INSTEAD 77
+#define TK_NO 78
+#define TK_KEY 79
+#define TK_OF 80
+#define TK_OFFSET 81
+#define TK_PRAGMA 82
+#define TK_RAISE 83
+#define TK_RECURSIVE 84
+#define TK_REPLACE 85
+#define TK_RESTRICT 86
+#define TK_ROW 87
+#define TK_TRIGGER 88
+#define TK_VACUUM 89
+#define TK_VIEW 90
+#define TK_VIRTUAL 91
+#define TK_WITH 92
+#define TK_REINDEX 93
+#define TK_RENAME 94
+#define TK_CTIME_KW 95
+#define TK_ANY 96
#define TK_STRING 97
#define TK_JOIN_KW 98
#define TK_CONSTRAINT 99
@@ -8804,16 +11247,25 @@ SQLITE_PRIVATE void sqlite3HashClear(Hash*);
#define TK_TO_REAL 147
#define TK_ISNOT 148
#define TK_END_OF_FILE 149
-#define TK_ILLEGAL 150
-#define TK_SPACE 151
-#define TK_UNCLOSED_STRING 152
-#define TK_FUNCTION 153
-#define TK_COLUMN 154
-#define TK_AGG_FUNCTION 155
-#define TK_AGG_COLUMN 156
-#define TK_UMINUS 157
-#define TK_UPLUS 158
-#define TK_REGISTER 159
+#define TK_UNCLOSED_STRING 150
+#define TK_FUNCTION 151
+#define TK_COLUMN 152
+#define TK_AGG_FUNCTION 153
+#define TK_AGG_COLUMN 154
+#define TK_UMINUS 155
+#define TK_UPLUS 156
+#define TK_REGISTER 157
+#define TK_ASTERISK 158
+#define TK_SPAN 159
+#define TK_SPACE 160
+#define TK_ILLEGAL 161
+
+/* The token codes above must all fit in 8 bits */
+#define TKFLG_MASK 0xff
+
+/* Flags that can be added to a token code when it is not
+** being stored in a u8: */
+#define TKFLG_DONTFOLD 0x100 /* Omit constant folding optimizations */
/************** End of parse.h ***********************************************/
/************** Continuing where we left off in sqliteInt.h ******************/
@@ -8845,7 +11297,7 @@ SQLITE_PRIVATE void sqlite3HashClear(Hash*);
/*
** OMIT_TEMPDB is set to 1 if SQLITE_OMIT_TEMPDB is defined, or 0
-** afterward. Having this macro allows us to cause the C compiler
+** afterward. Having this macro allows us to cause the C compiler
** to omit code used by TEMP tables without messy #ifndef statements.
*/
#ifdef SQLITE_OMIT_TEMPDB
@@ -8884,7 +11336,7 @@ SQLITE_PRIVATE void sqlite3HashClear(Hash*);
/*
** If no value has been provided for SQLITE_MAX_WORKER_THREADS, or if
-** SQLITE_TEMP_STORE is set to 3 (never use temporary files), set it
+** SQLITE_TEMP_STORE is set to 3 (never use temporary files), set it
** to zero.
*/
#if SQLITE_TEMP_STORE==3 || SQLITE_THREADSAFE==0
@@ -8902,6 +11354,15 @@ SQLITE_PRIVATE void sqlite3HashClear(Hash*);
# define SQLITE_MAX_WORKER_THREADS SQLITE_DEFAULT_WORKER_THREADS
#endif
+/*
+** The default initial allocation for the pagecache when using separate
+** pagecaches for each database connection. A positive number is the
+** number of pages. A negative number N translations means that a buffer
+** of -1024*N bytes is allocated and used for as many pages as it will hold.
+*/
+#ifndef SQLITE_DEFAULT_PCACHE_INITSZ
+# define SQLITE_DEFAULT_PCACHE_INITSZ 100
+#endif
/*
** GCC does not define the offsetof() macro so we'll have to do it
@@ -8914,8 +11375,12 @@ SQLITE_PRIVATE void sqlite3HashClear(Hash*);
/*
** Macros to compute minimum and maximum of two numbers.
*/
-#define MIN(A,B) ((A)<(B)?(A):(B))
-#define MAX(A,B) ((A)>(B)?(A):(B))
+#ifndef MIN
+# define MIN(A,B) ((A)<(B)?(A):(B))
+#endif
+#ifndef MAX
+# define MAX(A,B) ((A)>(B)?(A):(B))
+#endif
/*
** Swap two objects of type TYPE.
@@ -9023,7 +11488,7 @@ typedef INT8_TYPE i8; /* 1-byte signed integer */
** 4 -> 20 1000 -> 99 1048576 -> 200
** 10 -> 33 1024 -> 100 4294967296 -> 320
**
-** The LogEst can be negative to indicate fractional values.
+** The LogEst can be negative to indicate fractional values.
** Examples:
**
** 0.5 -> -10 0.1 -> -33 0.0625 -> -40
@@ -9044,6 +11509,27 @@ typedef INT16_TYPE LogEst;
# endif
#endif
+/* The uptr type is an unsigned integer large enough to hold a pointer
+*/
+#if defined(HAVE_STDINT_H)
+ typedef uintptr_t uptr;
+#elif SQLITE_PTRSIZE==4
+ typedef u32 uptr;
+#else
+ typedef u64 uptr;
+#endif
+
+/*
+** The SQLITE_WITHIN(P,S,E) macro checks to see if pointer P points to
+** something between S (inclusive) and E (exclusive).
+**
+** In other words, S is a buffer and E is a pointer to the first byte after
+** the end of buffer S. This macro returns true if P points to something
+** contained within the buffer S.
+*/
+#define SQLITE_WITHIN(P,S,E) (((uptr)(P)>=(uptr)(S))&&((uptr)(P)<(uptr)(E)))
+
+
/*
** Macros to determine whether the machine is big or little endian,
** and whether or not that determination is run-time or compile-time.
@@ -9053,11 +11539,6 @@ typedef INT16_TYPE LogEst;
** -DSQLITE_RUNTIME_BYTEORDER=1 is set, then byte-order is determined
** at run-time.
*/
-#ifdef SQLITE_AMALGAMATION
-SQLITE_PRIVATE const int sqlite3one = 1;
-#else
-SQLITE_PRIVATE const int sqlite3one;
-#endif
#if (defined(i386) || defined(__i386__) || defined(_M_IX86) || \
defined(__x86_64) || defined(__x86_64__) || defined(_M_X64) || \
defined(_M_AMD64) || defined(_M_ARM) || defined(__x86) || \
@@ -9075,6 +11556,11 @@ SQLITE_PRIVATE const int sqlite3one;
# define SQLITE_UTF16NATIVE SQLITE_UTF16BE
#endif
#if !defined(SQLITE_BYTEORDER)
+# ifdef SQLITE_AMALGAMATION
+ const int sqlite3one = 1;
+# else
+ extern const int sqlite3one;
+# endif
# define SQLITE_BYTEORDER 0 /* 0 means "unknown at compile-time" */
# define SQLITE_BIGENDIAN (*(char *)(&sqlite3one)==0)
# define SQLITE_LITTLEENDIAN (*(char *)(&sqlite3one)==1)
@@ -9089,7 +11575,7 @@ SQLITE_PRIVATE const int sqlite3one;
#define LARGEST_INT64 (0xffffffff|(((i64)0x7fffffff)<<32))
#define SMALLEST_INT64 (((i64)-1) - LARGEST_INT64)
-/*
+/*
** Round up a number to the next larger multiple of 8. This is used
** to force 8-byte alignment on 64-bit architectures.
*/
@@ -9128,16 +11614,14 @@ SQLITE_PRIVATE const int sqlite3one;
*/
#ifdef __APPLE__
# include <TargetConditionals.h>
-# if TARGET_OS_IPHONE
-# undef SQLITE_MAX_MMAP_SIZE
-# define SQLITE_MAX_MMAP_SIZE 0
-# endif
#endif
#ifndef SQLITE_MAX_MMAP_SIZE
# if defined(__linux__) \
|| defined(_WIN32) \
|| (defined(__APPLE__) && defined(__MACH__)) \
- || defined(__sun)
+ || defined(__sun) \
+ || defined(__FreeBSD__) \
+ || defined(__DragonFly__)
# define SQLITE_MAX_MMAP_SIZE 0x7fff0000 /* 2147418112 */
# else
# define SQLITE_MAX_MMAP_SIZE 0
@@ -9185,7 +11669,7 @@ SQLITE_PRIVATE const int sqlite3one;
/*
** An instance of the following structure is used to store the busy-handler
-** callback for a given sqlite handle.
+** callback for a given sqlite handle.
**
** The sqlite.busyHandler member of the sqlite struct contains the busy
** callback for the database handle. Each pager opened via the sqlite
@@ -9230,9 +11714,9 @@ struct BusyHandler {
/*
** The following value as a destructor means to use sqlite3DbFree().
-** The sqlite3DbFree() routine requires two parameters instead of the
-** one parameter that destructors normally want. So we have to introduce
-** this magic value that the code knows to handle differently. Any
+** The sqlite3DbFree() routine requires two parameters instead of the
+** one parameter that destructors normally want. So we have to introduce
+** this magic value that the code knows to handle differently. Any
** pointer will work here as long as it is distinct from SQLITE_STATIC
** and SQLITE_TRANSIENT.
*/
@@ -9259,16 +11743,16 @@ struct BusyHandler {
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 SQLITE_WSD
#define GLOBAL(t,v) v
#define sqlite3GlobalConfig sqlite3Config
#endif
/*
** The following macros are used to suppress compiler warnings and to
-** make it clear to human readers when a function parameter is deliberately
+** make it clear to human readers when a function parameter is deliberately
** left unused within the body of a function. This usually happens when
-** a function is called via a function pointer. For example the
+** a function is called via a function pointer. For example the
** implementation of an SQL aggregate step callback may not use the
** parameter indicating the number of arguments passed to the aggregate,
** if it knows that this is enforced elsewhere.
@@ -9311,6 +11795,7 @@ typedef struct LookasideSlot LookasideSlot;
typedef struct Module Module;
typedef struct NameContext NameContext;
typedef struct Parse Parse;
+typedef struct PreUpdate PreUpdate;
typedef struct PrintfArguments PrintfArguments;
typedef struct RowSet RowSet;
typedef struct Savepoint Savepoint;
@@ -9334,7 +11819,7 @@ typedef struct WhereInfo WhereInfo;
typedef struct With With;
/*
-** Defer sourcing vdbe.h and btree.h until after the "u8" and
+** Defer sourcing vdbe.h and btree.h until after the "u8" and
** "BusyHandler" typedefs. vdbe.h also requires a few of the opaque
** pointer types (i.e. FuncDef) defined above.
*/
@@ -9405,11 +11890,11 @@ SQLITE_PRIVATE int sqlite3BtreeOpen(
SQLITE_PRIVATE int sqlite3BtreeClose(Btree*);
SQLITE_PRIVATE int sqlite3BtreeSetCacheSize(Btree*,int);
+SQLITE_PRIVATE int sqlite3BtreeSetSpillSize(Btree*,int);
#if SQLITE_MAX_MMAP_SIZE>0
SQLITE_PRIVATE int sqlite3BtreeSetMmapLimit(Btree*,sqlite3_int64);
#endif
SQLITE_PRIVATE int sqlite3BtreeSetPagerFlags(Btree*,unsigned);
-SQLITE_PRIVATE int sqlite3BtreeSyncDisabled(Btree*);
SQLITE_PRIVATE int sqlite3BtreeSetPageSize(Btree *p, int nPagesize, int nReserve, int eFix);
SQLITE_PRIVATE int sqlite3BtreeGetPageSize(Btree*);
SQLITE_PRIVATE int sqlite3BtreeMaxPageCount(Btree*,int);
@@ -9492,8 +11977,37 @@ SQLITE_PRIVATE int sqlite3BtreeNewDb(Btree *p);
#define BTREE_DATA_VERSION 15 /* A virtual meta-value */
/*
-** Values that may be OR'd together to form the second argument of an
-** sqlite3BtreeCursorHints() call.
+** Kinds of hints that can be passed into the sqlite3BtreeCursorHint()
+** interface.
+**
+** BTREE_HINT_RANGE (arguments: Expr*, Mem*)
+**
+** The first argument is an Expr* (which is guaranteed to be constant for
+** the lifetime of the cursor) that defines constraints on which rows
+** might be fetched with this cursor. The Expr* tree may contain
+** TK_REGISTER nodes that refer to values stored in the array of registers
+** passed as the second parameter. In other words, if Expr.op==TK_REGISTER
+** then the value of the node is the value in Mem[pExpr.iTable]. Any
+** TK_COLUMN node in the expression tree refers to the Expr.iColumn-th
+** column of the b-tree of the cursor. The Expr tree will not contain
+** any function calls nor subqueries nor references to b-trees other than
+** the cursor being hinted.
+**
+** The design of the _RANGE hint is aid b-tree implementations that try
+** to prefetch content from remote machines - to provide those
+** implementations with limits on what needs to be prefetched and thereby
+** reduce network bandwidth.
+**
+** Note that BTREE_HINT_FLAGS with BTREE_BULKLOAD is the only hint used by
+** standard SQLite. The other hints are provided for extentions that use
+** the SQLite parser and code generator but substitute their own storage
+** engine.
+*/
+#define BTREE_HINT_RANGE 0 /* Range constraints on queries */
+
+/*
+** Values that may be OR'd together to form the argument to the
+** BTREE_HINT_FLAGS hint for sqlite3BtreeCursorHint():
**
** 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.
@@ -9507,6 +12021,32 @@ SQLITE_PRIVATE int sqlite3BtreeNewDb(Btree *p);
#define BTREE_BULKLOAD 0x00000001 /* Used to full index in sorted order */
#define BTREE_SEEK_EQ 0x00000002 /* EQ seeks only - no range seeks */
+/*
+** Flags passed as the third argument to sqlite3BtreeCursor().
+**
+** For read-only cursors the wrFlag argument is always zero. For read-write
+** cursors it may be set to either (BTREE_WRCSR|BTREE_FORDELETE) or just
+** (BTREE_WRCSR). If the BTREE_FORDELETE bit is set, then the cursor will
+** only be used by SQLite for the following:
+**
+** * to seek to and then delete specific entries, and/or
+**
+** * to read values that will be used to create keys that other
+** BTREE_FORDELETE cursors will seek to and delete.
+**
+** The BTREE_FORDELETE flag is an optimization hint. It is not used by
+** by this, the native b-tree engine of SQLite, but it is available to
+** alternative storage engines that might be substituted in place of this
+** b-tree system. For alternative storage engines in which a delete of
+** the main table row automatically deletes corresponding index rows,
+** the FORDELETE flag hint allows those alternative storage engines to
+** skip a lot of work. Namely: FORDELETE cursors may treat all SEEK
+** and DELETE operations as no-ops, and any READ operation against a
+** FORDELETE cursor may return a null row: 0x01 0x00.
+*/
+#define BTREE_WRCSR 0x00000004 /* read-write cursor */
+#define BTREE_FORDELETE 0x00000008 /* Cursor is for seek/delete only */
+
SQLITE_PRIVATE int sqlite3BtreeCursor(
Btree*, /* BTree containing table to open */
int iTable, /* Index of root page */
@@ -9516,6 +12056,10 @@ SQLITE_PRIVATE int sqlite3BtreeCursor(
);
SQLITE_PRIVATE int sqlite3BtreeCursorSize(void);
SQLITE_PRIVATE void sqlite3BtreeCursorZero(BtCursor*);
+SQLITE_PRIVATE void sqlite3BtreeCursorHintFlags(BtCursor*, unsigned);
+#ifdef SQLITE_ENABLE_CURSOR_HINTS
+SQLITE_PRIVATE void sqlite3BtreeCursorHint(BtCursor*, int, ...);
+#endif
SQLITE_PRIVATE int sqlite3BtreeCloseCursor(BtCursor*);
SQLITE_PRIVATE int sqlite3BtreeMovetoUnpacked(
@@ -9527,7 +12071,12 @@ SQLITE_PRIVATE int sqlite3BtreeMovetoUnpacked(
);
SQLITE_PRIVATE int sqlite3BtreeCursorHasMoved(BtCursor*);
SQLITE_PRIVATE int sqlite3BtreeCursorRestore(BtCursor*, int*);
-SQLITE_PRIVATE int sqlite3BtreeDelete(BtCursor*);
+SQLITE_PRIVATE int sqlite3BtreeDelete(BtCursor*, u8 flags);
+
+/* Allowed flags for the 2nd argument to sqlite3BtreeDelete() */
+#define BTREE_SAVEPOSITION 0x02 /* Leave cursor pointing at NEXT or PREV */
+#define BTREE_AUXDELETE 0x04 /* not the primary delete operation */
+
SQLITE_PRIVATE int sqlite3BtreeInsert(BtCursor*, const void *pKey, i64 nKey,
const void *pData, int nData,
int nZero, int bias, int seekResult);
@@ -9550,10 +12099,7 @@ SQLITE_PRIVATE int sqlite3BtreePutData(BtCursor*, u32 offset, u32 amt, void*);
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);
@@ -9582,15 +12128,17 @@ SQLITE_PRIVATE int sqlite3BtreeCheckpoint(Btree*, int, int *, int *);
#ifndef SQLITE_OMIT_SHARED_CACHE
SQLITE_PRIVATE void sqlite3BtreeEnter(Btree*);
SQLITE_PRIVATE void sqlite3BtreeEnterAll(sqlite3*);
+SQLITE_PRIVATE int sqlite3BtreeSharable(Btree*);
+SQLITE_PRIVATE void sqlite3BtreeEnterCursor(BtCursor*);
#else
# define sqlite3BtreeEnter(X)
# define sqlite3BtreeEnterAll(X)
+# define sqlite3BtreeSharable(X) 0
+# define sqlite3BtreeEnterCursor(X)
#endif
#if !defined(SQLITE_OMIT_SHARED_CACHE) && SQLITE_THREADSAFE
-SQLITE_PRIVATE int sqlite3BtreeSharable(Btree*);
SQLITE_PRIVATE void sqlite3BtreeLeave(Btree*);
-SQLITE_PRIVATE void sqlite3BtreeEnterCursor(BtCursor*);
SQLITE_PRIVATE void sqlite3BtreeLeaveCursor(BtCursor*);
SQLITE_PRIVATE void sqlite3BtreeLeaveAll(sqlite3*);
#ifndef NDEBUG
@@ -9601,9 +12149,7 @@ SQLITE_PRIVATE int sqlite3SchemaMutexHeld(sqlite3*,int,Schema*);
#endif
#else
-# define sqlite3BtreeSharable(X) 0
# define sqlite3BtreeLeave(X)
-# define sqlite3BtreeEnterCursor(X)
# define sqlite3BtreeLeaveCursor(X)
# define sqlite3BtreeLeaveAll(X)
@@ -9662,24 +12208,29 @@ typedef struct SubProgram SubProgram;
struct VdbeOp {
u8 opcode; /* What operation to perform */
signed char p4type; /* One of the P4_xxx constants for p4 */
- u8 opflags; /* Mask of the OPFLG_* flags in opcodes.h */
+ u8 notUsed1;
u8 p5; /* Fifth parameter is an unsigned character */
int p1; /* First operand */
int p2; /* Second parameter (often the jump destination) */
int p3; /* The third parameter */
- union { /* fourth parameter */
+ union p4union { /* fourth parameter */
int i; /* Integer value if p4type==P4_INT32 */
void *p; /* Generic pointer */
char *z; /* Pointer to data for string (char array) types */
i64 *pI64; /* Used when p4type is P4_INT64 */
double *pReal; /* Used when p4type is P4_REAL */
FuncDef *pFunc; /* Used when p4type is P4_FUNCDEF */
+ sqlite3_context *pCtx; /* Used when p4type is P4_FUNCCTX */
CollSeq *pColl; /* Used when p4type is P4_COLLSEQ */
Mem *pMem; /* Used when p4type is P4_MEM */
VTable *pVtab; /* Used when p4type is P4_VTAB */
KeyInfo *pKeyInfo; /* Used when p4type is P4_KEYINFO */
int *ai; /* Used when p4type is P4_INTARRAY */
SubProgram *pProgram; /* Used when p4type is P4_SUBPROGRAM */
+ Table *pTab; /* Used when p4type is P4_TABLE */
+#ifdef SQLITE_ENABLE_CURSOR_HINTS
+ Expr *pExpr; /* Used when p4type is P4_EXPR */
+#endif
int (*xAdvance)(BtCursor *, int *);
} p4;
#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS
@@ -9730,6 +12281,7 @@ typedef struct VdbeOpList VdbeOpList;
#define P4_COLLSEQ (-4) /* P4 is a pointer to a CollSeq structure */
#define P4_FUNCDEF (-5) /* P4 is a pointer to a FuncDef structure */
#define P4_KEYINFO (-6) /* P4 is a pointer to a KeyInfo structure */
+#define P4_EXPR (-7) /* P4 is a pointer to an Expr tree */
#define P4_MEM (-8) /* P4 is a pointer to a Mem* structure */
#define P4_TRANSIENT 0 /* P4 is a pointer to a transient string */
#define P4_VTAB (-10) /* P4 is a pointer to an sqlite3_vtab structure */
@@ -9740,6 +12292,8 @@ typedef struct VdbeOpList VdbeOpList;
#define P4_INTARRAY (-15) /* P4 is a vector of 32-bit integers */
#define P4_SUBPROGRAM (-18) /* P4 is a pointer to a SubProgram structure */
#define P4_ADVANCE (-19) /* P4 is a pointer to BtreeNext() or BtreePrev() */
+#define P4_TABLE (-20) /* P4 is a pointer to a Table structure */
+#define P4_FUNCCTX (-21) /* P4 is a pointer to an sqlite3_context object */
/* Error message codes for OP_Halt */
#define P5_ConstraintNotNull 1
@@ -9781,197 +12335,209 @@ typedef struct VdbeOpList VdbeOpList;
/************** Include opcodes.h in the middle of vdbe.h ********************/
/************** Begin file opcodes.h *****************************************/
/* Automatically generated. Do not edit */
-/* See the mkopcodeh.awk script for details */
-#define OP_Function 1 /* synopsis: r[P3]=func(r[P2@P5]) */
-#define OP_Savepoint 2
-#define OP_AutoCommit 3
-#define OP_Transaction 4
-#define OP_SorterNext 5
-#define OP_PrevIfOpen 6
-#define OP_NextIfOpen 7
-#define OP_Prev 8
-#define OP_Next 9
-#define OP_AggStep 10 /* synopsis: accum=r[P3] step(r[P2@P5]) */
-#define OP_Checkpoint 11
-#define OP_JournalMode 12
-#define OP_Vacuum 13
-#define OP_VFilter 14 /* synopsis: iplan=r[P3] zplan='P4' */
-#define OP_VUpdate 15 /* synopsis: data=r[P3@P2] */
-#define OP_Goto 16
-#define OP_Gosub 17
-#define OP_Return 18
+/* See the tool/mkopcodeh.tcl script for details */
+#define OP_Savepoint 0
+#define OP_AutoCommit 1
+#define OP_Transaction 2
+#define OP_SorterNext 3
+#define OP_PrevIfOpen 4
+#define OP_NextIfOpen 5
+#define OP_Prev 6
+#define OP_Next 7
+#define OP_Checkpoint 8
+#define OP_JournalMode 9
+#define OP_Vacuum 10
+#define OP_VFilter 11 /* synopsis: iplan=r[P3] zplan='P4' */
+#define OP_VUpdate 12 /* synopsis: data=r[P3@P2] */
+#define OP_Goto 13
+#define OP_Gosub 14
+#define OP_InitCoroutine 15
+#define OP_Yield 16
+#define OP_MustBeInt 17
+#define OP_Jump 18
#define OP_Not 19 /* same as TK_NOT, synopsis: r[P2]= !r[P1] */
-#define OP_InitCoroutine 20
-#define OP_EndCoroutine 21
-#define OP_Yield 22
-#define OP_HaltIfNull 23 /* synopsis: if r[P3]=null halt */
-#define OP_Halt 24
-#define OP_Integer 25 /* synopsis: r[P2]=P1 */
-#define OP_Int64 26 /* synopsis: r[P2]=P4 */
-#define OP_String 27 /* synopsis: r[P2]='P4' (len=P1) */
-#define OP_Null 28 /* synopsis: r[P2..P3]=NULL */
-#define OP_SoftNull 29 /* synopsis: r[P1]=NULL */
-#define OP_Blob 30 /* synopsis: r[P2]=P4 (len=P1) */
-#define OP_Variable 31 /* synopsis: r[P2]=parameter(P1,P4) */
-#define OP_Move 32 /* synopsis: r[P2@P3]=r[P1@P3] */
-#define OP_Copy 33 /* synopsis: r[P2@P3+1]=r[P1@P3+1] */
-#define OP_SCopy 34 /* synopsis: r[P2]=r[P1] */
-#define OP_ResultRow 35 /* synopsis: output=r[P1@P2] */
-#define OP_CollSeq 36
-#define OP_AddImm 37 /* synopsis: r[P1]=r[P1]+P2 */
-#define OP_MustBeInt 38
-#define OP_RealAffinity 39
-#define OP_Cast 40 /* synopsis: affinity(r[P1]) */
-#define OP_Permutation 41
-#define OP_Compare 42 /* synopsis: r[P1@P3] <-> r[P2@P3] */
-#define OP_Jump 43
-#define OP_Once 44
-#define OP_If 45
-#define OP_IfNot 46
-#define OP_Column 47 /* synopsis: r[P3]=PX */
-#define OP_Affinity 48 /* synopsis: affinity(r[P1@P2]) */
-#define OP_MakeRecord 49 /* synopsis: r[P3]=mkrec(r[P1@P2]) */
-#define OP_Count 50 /* synopsis: r[P2]=count() */
-#define OP_ReadCookie 51
-#define OP_SetCookie 52
-#define OP_ReopenIdx 53 /* synopsis: root=P2 iDb=P3 */
-#define OP_OpenRead 54 /* synopsis: root=P2 iDb=P3 */
-#define OP_OpenWrite 55 /* synopsis: root=P2 iDb=P3 */
-#define OP_OpenAutoindex 56 /* synopsis: nColumn=P2 */
-#define OP_OpenEphemeral 57 /* synopsis: nColumn=P2 */
-#define OP_SorterOpen 58
-#define OP_SequenceTest 59 /* synopsis: if( cursor[P1].ctr++ ) pc = P2 */
-#define OP_OpenPseudo 60 /* synopsis: P3 columns in r[P2] */
-#define OP_Close 61
-#define OP_SeekLT 62 /* synopsis: key=r[P3@P4] */
-#define OP_SeekLE 63 /* synopsis: key=r[P3@P4] */
-#define OP_SeekGE 64 /* synopsis: key=r[P3@P4] */
-#define OP_SeekGT 65 /* synopsis: key=r[P3@P4] */
-#define OP_Seek 66 /* synopsis: intkey=r[P2] */
-#define OP_NoConflict 67 /* synopsis: key=r[P3@P4] */
-#define OP_NotFound 68 /* synopsis: key=r[P3@P4] */
-#define OP_Found 69 /* synopsis: key=r[P3@P4] */
-#define OP_NotExists 70 /* synopsis: intkey=r[P3] */
-#define OP_Or 71 /* same as TK_OR, synopsis: r[P3]=(r[P1] || r[P2]) */
-#define OP_And 72 /* same as TK_AND, synopsis: r[P3]=(r[P1] && r[P2]) */
-#define OP_Sequence 73 /* synopsis: r[P2]=cursor[P1].ctr++ */
-#define OP_NewRowid 74 /* synopsis: r[P2]=rowid */
-#define OP_Insert 75 /* synopsis: intkey=r[P3] data=r[P2] */
-#define OP_IsNull 76 /* same as TK_ISNULL, synopsis: if r[P1]==NULL goto P2 */
-#define OP_NotNull 77 /* same as TK_NOTNULL, synopsis: if r[P1]!=NULL goto P2 */
-#define OP_Ne 78 /* same as TK_NE, synopsis: if r[P1]!=r[P3] goto P2 */
-#define OP_Eq 79 /* same as TK_EQ, synopsis: if r[P1]==r[P3] goto P2 */
-#define OP_Gt 80 /* same as TK_GT, synopsis: if r[P1]>r[P3] goto P2 */
-#define OP_Le 81 /* same as TK_LE, synopsis: if r[P1]<=r[P3] goto P2 */
-#define OP_Lt 82 /* same as TK_LT, synopsis: if r[P1]<r[P3] goto P2 */
-#define OP_Ge 83 /* same as TK_GE, synopsis: if r[P1]>=r[P3] goto P2 */
-#define OP_InsertInt 84 /* synopsis: intkey=P3 data=r[P2] */
-#define OP_BitAnd 85 /* same as TK_BITAND, synopsis: r[P3]=r[P1]&r[P2] */
-#define OP_BitOr 86 /* same as TK_BITOR, synopsis: r[P3]=r[P1]|r[P2] */
-#define OP_ShiftLeft 87 /* same as TK_LSHIFT, synopsis: r[P3]=r[P2]<<r[P1] */
-#define OP_ShiftRight 88 /* same as TK_RSHIFT, synopsis: r[P3]=r[P2]>>r[P1] */
-#define OP_Add 89 /* same as TK_PLUS, synopsis: r[P3]=r[P1]+r[P2] */
-#define OP_Subtract 90 /* same as TK_MINUS, synopsis: r[P3]=r[P2]-r[P1] */
-#define OP_Multiply 91 /* same as TK_STAR, synopsis: r[P3]=r[P1]*r[P2] */
-#define OP_Divide 92 /* same as TK_SLASH, synopsis: r[P3]=r[P2]/r[P1] */
-#define OP_Remainder 93 /* same as TK_REM, synopsis: r[P3]=r[P2]%r[P1] */
-#define OP_Concat 94 /* same as TK_CONCAT, synopsis: r[P3]=r[P2]+r[P1] */
-#define OP_Delete 95
-#define OP_BitNot 96 /* same as TK_BITNOT, synopsis: r[P1]= ~r[P1] */
+#define OP_Once 20
+#define OP_If 21
+#define OP_IfNot 22
+#define OP_SeekLT 23 /* synopsis: key=r[P3@P4] */
+#define OP_SeekLE 24 /* synopsis: key=r[P3@P4] */
+#define OP_SeekGE 25 /* synopsis: key=r[P3@P4] */
+#define OP_SeekGT 26 /* synopsis: key=r[P3@P4] */
+#define OP_Or 27 /* same as TK_OR, synopsis: r[P3]=(r[P1] || r[P2]) */
+#define OP_And 28 /* same as TK_AND, synopsis: r[P3]=(r[P1] && r[P2]) */
+#define OP_NoConflict 29 /* synopsis: key=r[P3@P4] */
+#define OP_NotFound 30 /* synopsis: key=r[P3@P4] */
+#define OP_Found 31 /* synopsis: key=r[P3@P4] */
+#define OP_NotExists 32 /* synopsis: intkey=r[P3] */
+#define OP_Last 33
+#define OP_IsNull 34 /* same as TK_ISNULL, synopsis: if r[P1]==NULL goto P2 */
+#define OP_NotNull 35 /* same as TK_NOTNULL, synopsis: if r[P1]!=NULL goto P2 */
+#define OP_Ne 36 /* same as TK_NE, synopsis: if r[P1]!=r[P3] goto P2 */
+#define OP_Eq 37 /* same as TK_EQ, synopsis: if r[P1]==r[P3] goto P2 */
+#define OP_Gt 38 /* same as TK_GT, synopsis: if r[P1]>r[P3] goto P2 */
+#define OP_Le 39 /* same as TK_LE, synopsis: if r[P1]<=r[P3] goto P2 */
+#define OP_Lt 40 /* same as TK_LT, synopsis: if r[P1]<r[P3] goto P2 */
+#define OP_Ge 41 /* same as TK_GE, synopsis: if r[P1]>=r[P3] goto P2 */
+#define OP_SorterSort 42
+#define OP_BitAnd 43 /* same as TK_BITAND, synopsis: r[P3]=r[P1]&r[P2] */
+#define OP_BitOr 44 /* same as TK_BITOR, synopsis: r[P3]=r[P1]|r[P2] */
+#define OP_ShiftLeft 45 /* same as TK_LSHIFT, synopsis: r[P3]=r[P2]<<r[P1] */
+#define OP_ShiftRight 46 /* same as TK_RSHIFT, synopsis: r[P3]=r[P2]>>r[P1] */
+#define OP_Add 47 /* same as TK_PLUS, synopsis: r[P3]=r[P1]+r[P2] */
+#define OP_Subtract 48 /* same as TK_MINUS, synopsis: r[P3]=r[P2]-r[P1] */
+#define OP_Multiply 49 /* same as TK_STAR, synopsis: r[P3]=r[P1]*r[P2] */
+#define OP_Divide 50 /* same as TK_SLASH, synopsis: r[P3]=r[P2]/r[P1] */
+#define OP_Remainder 51 /* same as TK_REM, synopsis: r[P3]=r[P2]%r[P1] */
+#define OP_Concat 52 /* same as TK_CONCAT, synopsis: r[P3]=r[P2]+r[P1] */
+#define OP_Sort 53
+#define OP_BitNot 54 /* same as TK_BITNOT, synopsis: r[P1]= ~r[P1] */
+#define OP_Rewind 55
+#define OP_IdxLE 56 /* synopsis: key=r[P3@P4] */
+#define OP_IdxGT 57 /* synopsis: key=r[P3@P4] */
+#define OP_IdxLT 58 /* synopsis: key=r[P3@P4] */
+#define OP_IdxGE 59 /* synopsis: key=r[P3@P4] */
+#define OP_RowSetRead 60 /* synopsis: r[P3]=rowset(P1) */
+#define OP_RowSetTest 61 /* synopsis: if r[P3] in rowset(P1) goto P2 */
+#define OP_Program 62
+#define OP_FkIfZero 63 /* synopsis: if fkctr[P1]==0 goto P2 */
+#define OP_IfPos 64 /* synopsis: if r[P1]>0 then r[P1]-=P3, goto P2 */
+#define OP_IfNotZero 65 /* synopsis: if r[P1]!=0 then r[P1]-=P3, goto P2 */
+#define OP_DecrJumpZero 66 /* synopsis: if (--r[P1])==0 goto P2 */
+#define OP_IncrVacuum 67
+#define OP_VNext 68
+#define OP_Init 69 /* synopsis: Start at P2 */
+#define OP_Return 70
+#define OP_EndCoroutine 71
+#define OP_HaltIfNull 72 /* synopsis: if r[P3]=null halt */
+#define OP_Halt 73
+#define OP_Integer 74 /* synopsis: r[P2]=P1 */
+#define OP_Int64 75 /* synopsis: r[P2]=P4 */
+#define OP_String 76 /* synopsis: r[P2]='P4' (len=P1) */
+#define OP_Null 77 /* synopsis: r[P2..P3]=NULL */
+#define OP_SoftNull 78 /* synopsis: r[P1]=NULL */
+#define OP_Blob 79 /* synopsis: r[P2]=P4 (len=P1) */
+#define OP_Variable 80 /* synopsis: r[P2]=parameter(P1,P4) */
+#define OP_Move 81 /* synopsis: r[P2@P3]=r[P1@P3] */
+#define OP_Copy 82 /* synopsis: r[P2@P3+1]=r[P1@P3+1] */
+#define OP_SCopy 83 /* synopsis: r[P2]=r[P1] */
+#define OP_IntCopy 84 /* synopsis: r[P2]=r[P1] */
+#define OP_ResultRow 85 /* synopsis: output=r[P1@P2] */
+#define OP_CollSeq 86
+#define OP_Function0 87 /* synopsis: r[P3]=func(r[P2@P5]) */
+#define OP_Function 88 /* synopsis: r[P3]=func(r[P2@P5]) */
+#define OP_AddImm 89 /* synopsis: r[P1]=r[P1]+P2 */
+#define OP_RealAffinity 90
+#define OP_Cast 91 /* synopsis: affinity(r[P1]) */
+#define OP_Permutation 92
+#define OP_Compare 93 /* synopsis: r[P1@P3] <-> r[P2@P3] */
+#define OP_Column 94 /* synopsis: r[P3]=PX */
+#define OP_Affinity 95 /* synopsis: affinity(r[P1@P2]) */
+#define OP_MakeRecord 96 /* synopsis: r[P3]=mkrec(r[P1@P2]) */
#define OP_String8 97 /* same as TK_STRING, synopsis: r[P2]='P4' */
-#define OP_ResetCount 98
-#define OP_SorterCompare 99 /* synopsis: if key(P1)!=trim(r[P3],P4) goto P2 */
-#define OP_SorterData 100 /* synopsis: r[P2]=data */
-#define OP_RowKey 101 /* synopsis: r[P2]=key */
-#define OP_RowData 102 /* synopsis: r[P2]=data */
-#define OP_Rowid 103 /* synopsis: r[P2]=rowid */
-#define OP_NullRow 104
-#define OP_Last 105
-#define OP_SorterSort 106
-#define OP_Sort 107
-#define OP_Rewind 108
-#define OP_SorterInsert 109
-#define OP_IdxInsert 110 /* synopsis: key=r[P2] */
-#define OP_IdxDelete 111 /* synopsis: key=r[P2@P3] */
-#define OP_IdxRowid 112 /* synopsis: r[P2]=rowid */
-#define OP_IdxLE 113 /* synopsis: key=r[P3@P4] */
-#define OP_IdxGT 114 /* synopsis: key=r[P3@P4] */
-#define OP_IdxLT 115 /* synopsis: key=r[P3@P4] */
-#define OP_IdxGE 116 /* synopsis: key=r[P3@P4] */
-#define OP_Destroy 117
-#define OP_Clear 118
-#define OP_ResetSorter 119
-#define OP_CreateIndex 120 /* synopsis: r[P2]=root iDb=P1 */
-#define OP_CreateTable 121 /* synopsis: r[P2]=root iDb=P1 */
-#define OP_ParseSchema 122
-#define OP_LoadAnalysis 123
-#define OP_DropTable 124
-#define OP_DropIndex 125
-#define OP_DropTrigger 126
-#define OP_IntegrityCk 127
-#define OP_RowSetAdd 128 /* synopsis: rowset(P1)=r[P2] */
-#define OP_RowSetRead 129 /* synopsis: r[P3]=rowset(P1) */
-#define OP_RowSetTest 130 /* synopsis: if r[P3] in rowset(P1) goto P2 */
-#define OP_Program 131
-#define OP_Param 132
+#define OP_Count 98 /* synopsis: r[P2]=count() */
+#define OP_ReadCookie 99
+#define OP_SetCookie 100
+#define OP_ReopenIdx 101 /* synopsis: root=P2 iDb=P3 */
+#define OP_OpenRead 102 /* synopsis: root=P2 iDb=P3 */
+#define OP_OpenWrite 103 /* synopsis: root=P2 iDb=P3 */
+#define OP_OpenAutoindex 104 /* synopsis: nColumn=P2 */
+#define OP_OpenEphemeral 105 /* synopsis: nColumn=P2 */
+#define OP_SorterOpen 106
+#define OP_SequenceTest 107 /* synopsis: if( cursor[P1].ctr++ ) pc = P2 */
+#define OP_OpenPseudo 108 /* synopsis: P3 columns in r[P2] */
+#define OP_Close 109
+#define OP_ColumnsUsed 110
+#define OP_Sequence 111 /* synopsis: r[P2]=cursor[P1].ctr++ */
+#define OP_NewRowid 112 /* synopsis: r[P2]=rowid */
+#define OP_Insert 113 /* synopsis: intkey=r[P3] data=r[P2] */
+#define OP_InsertInt 114 /* synopsis: intkey=P3 data=r[P2] */
+#define OP_Delete 115
+#define OP_ResetCount 116
+#define OP_SorterCompare 117 /* synopsis: if key(P1)!=trim(r[P3],P4) goto P2 */
+#define OP_SorterData 118 /* synopsis: r[P2]=data */
+#define OP_RowKey 119 /* synopsis: r[P2]=key */
+#define OP_RowData 120 /* synopsis: r[P2]=data */
+#define OP_Rowid 121 /* synopsis: r[P2]=rowid */
+#define OP_NullRow 122
+#define OP_SorterInsert 123
+#define OP_IdxInsert 124 /* synopsis: key=r[P2] */
+#define OP_IdxDelete 125 /* synopsis: key=r[P2@P3] */
+#define OP_Seek 126 /* synopsis: Move P3 to P1.rowid */
+#define OP_IdxRowid 127 /* synopsis: r[P2]=rowid */
+#define OP_Destroy 128
+#define OP_Clear 129
+#define OP_ResetSorter 130
+#define OP_CreateIndex 131 /* synopsis: r[P2]=root iDb=P1 */
+#define OP_CreateTable 132 /* synopsis: r[P2]=root iDb=P1 */
#define OP_Real 133 /* same as TK_FLOAT, synopsis: r[P2]=P4 */
-#define OP_FkCounter 134 /* synopsis: fkctr[P1]+=P2 */
-#define OP_FkIfZero 135 /* synopsis: if fkctr[P1]==0 goto P2 */
-#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_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
-
+#define OP_ParseSchema 134
+#define OP_LoadAnalysis 135
+#define OP_DropTable 136
+#define OP_DropIndex 137
+#define OP_DropTrigger 138
+#define OP_IntegrityCk 139
+#define OP_RowSetAdd 140 /* synopsis: rowset(P1)=r[P2] */
+#define OP_Param 141
+#define OP_FkCounter 142 /* synopsis: fkctr[P1]+=P2 */
+#define OP_MemMax 143 /* synopsis: r[P1]=max(r[P1],r[P2]) */
+#define OP_OffsetLimit 144 /* synopsis: if r[P1]>0 then r[P2]=r[P1]+max(0,r[P3]) else r[P2]=(-1) */
+#define OP_AggStep0 145 /* synopsis: accum=r[P3] step(r[P2@P5]) */
+#define OP_AggStep 146 /* synopsis: accum=r[P3] step(r[P2@P5]) */
+#define OP_AggFinal 147 /* synopsis: accum=r[P1] N=P2 */
+#define OP_Expire 148
+#define OP_TableLock 149 /* synopsis: iDb=P1 root=P2 write=P3 */
+#define OP_VBegin 150
+#define OP_VCreate 151
+#define OP_VDestroy 152
+#define OP_VOpen 153
+#define OP_VColumn 154 /* synopsis: r[P3]=vcolumn(P2) */
+#define OP_VRename 155
+#define OP_Pagecount 156
+#define OP_MaxPgcnt 157
+#define OP_CursorHint 158
+#define OP_Noop 159
+#define OP_Explain 160
/* Properties such as "out2" or "jump" that are specified in
** comments following the "case" for each opcode in the vdbe.c
** are encoded into bitvectors as follows:
*/
-#define OPFLG_JUMP 0x0001 /* jump: P2 holds jmp target */
-#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_JUMP 0x01 /* jump: P2 holds jmp target */
+#define OPFLG_IN1 0x02 /* in1: P1 is an input */
+#define OPFLG_IN2 0x04 /* in2: P2 is an input */
+#define OPFLG_IN3 0x08 /* in3: P3 is an input */
+#define OPFLG_OUT2 0x10 /* out2: P2 is an output */
+#define OPFLG_OUT3 0x20 /* out3: P3 is an output */
#define OPFLG_INITIALIZER {\
-/* 0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01,\
-/* 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,}
+/* 0 */ 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01,\
+/* 8 */ 0x00, 0x10, 0x00, 0x01, 0x00, 0x01, 0x01, 0x01,\
+/* 16 */ 0x03, 0x03, 0x01, 0x12, 0x01, 0x03, 0x03, 0x09,\
+/* 24 */ 0x09, 0x09, 0x09, 0x26, 0x26, 0x09, 0x09, 0x09,\
+/* 32 */ 0x09, 0x01, 0x03, 0x03, 0x0b, 0x0b, 0x0b, 0x0b,\
+/* 40 */ 0x0b, 0x0b, 0x01, 0x26, 0x26, 0x26, 0x26, 0x26,\
+/* 48 */ 0x26, 0x26, 0x26, 0x26, 0x26, 0x01, 0x12, 0x01,\
+/* 56 */ 0x01, 0x01, 0x01, 0x01, 0x23, 0x0b, 0x01, 0x01,\
+/* 64 */ 0x03, 0x03, 0x03, 0x01, 0x01, 0x01, 0x02, 0x02,\
+/* 72 */ 0x08, 0x00, 0x10, 0x10, 0x10, 0x10, 0x00, 0x10,\
+/* 80 */ 0x10, 0x00, 0x00, 0x10, 0x10, 0x00, 0x00, 0x00,\
+/* 88 */ 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00,\
+/* 96 */ 0x00, 0x10, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00,\
+/* 104 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10,\
+/* 112 */ 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\
+/* 120 */ 0x00, 0x10, 0x00, 0x04, 0x04, 0x00, 0x00, 0x10,\
+/* 128 */ 0x10, 0x00, 0x00, 0x10, 0x10, 0x10, 0x00, 0x00,\
+/* 136 */ 0x00, 0x00, 0x00, 0x00, 0x06, 0x10, 0x00, 0x04,\
+/* 144 */ 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\
+/* 152 */ 0x00, 0x00, 0x00, 0x00, 0x10, 0x10, 0x00, 0x00,\
+/* 160 */ 0x00,}
+
+/* The sqlite3P2Values() routine is able to run faster if it knows
+** the value of the largest JUMP opcode. The smaller the maximum
+** JUMP opcode the better, so the mkopcodeh.tcl script that
+** generated this include file strives to group all JUMP opcodes
+** together near the beginning of the list.
+*/
+#define SQLITE_MX_JUMP_OPCODE 69 /* Maximum JUMP opcode */
/************** End of opcodes.h *********************************************/
/************** Continuing where we left off in vdbe.h ***********************/
@@ -9984,17 +12550,28 @@ SQLITE_PRIVATE Vdbe *sqlite3VdbeCreate(Parse*);
SQLITE_PRIVATE int sqlite3VdbeAddOp0(Vdbe*,int);
SQLITE_PRIVATE int sqlite3VdbeAddOp1(Vdbe*,int,int);
SQLITE_PRIVATE int sqlite3VdbeAddOp2(Vdbe*,int,int,int);
+SQLITE_PRIVATE int sqlite3VdbeGoto(Vdbe*,int);
+SQLITE_PRIVATE int sqlite3VdbeLoadString(Vdbe*,int,const char*);
+SQLITE_PRIVATE void sqlite3VdbeMultiLoad(Vdbe*,int,const char*,...);
SQLITE_PRIVATE int sqlite3VdbeAddOp3(Vdbe*,int,int,int,int);
SQLITE_PRIVATE int sqlite3VdbeAddOp4(Vdbe*,int,int,int,int,const char *zP4,int);
+SQLITE_PRIVATE int sqlite3VdbeAddOp4Dup8(Vdbe*,int,int,int,int,const u8*,int);
SQLITE_PRIVATE int sqlite3VdbeAddOp4Int(Vdbe*,int,int,int,int,int);
-SQLITE_PRIVATE int sqlite3VdbeAddOpList(Vdbe*, int nOp, VdbeOpList const *aOp, int iLineno);
+SQLITE_PRIVATE void sqlite3VdbeEndCoroutine(Vdbe*,int);
+#if defined(SQLITE_DEBUG) && !defined(SQLITE_TEST_REALLOC_STRESS)
+SQLITE_PRIVATE void sqlite3VdbeVerifyNoMallocRequired(Vdbe *p, int N);
+#else
+# define sqlite3VdbeVerifyNoMallocRequired(A,B)
+#endif
+SQLITE_PRIVATE VdbeOp *sqlite3VdbeAddOpList(Vdbe*, int nOp, VdbeOpList const *aOp, int iLineno);
SQLITE_PRIVATE void sqlite3VdbeAddParseSchemaOp(Vdbe*,int,char*);
+SQLITE_PRIVATE void sqlite3VdbeChangeOpcode(Vdbe*, u32 addr, u8);
SQLITE_PRIVATE void sqlite3VdbeChangeP1(Vdbe*, u32 addr, int P1);
SQLITE_PRIVATE void sqlite3VdbeChangeP2(Vdbe*, u32 addr, int P2);
SQLITE_PRIVATE void sqlite3VdbeChangeP3(Vdbe*, u32 addr, int P3);
SQLITE_PRIVATE void sqlite3VdbeChangeP5(Vdbe*, u8 P5);
SQLITE_PRIVATE void sqlite3VdbeJumpHere(Vdbe*, int addr);
-SQLITE_PRIVATE void sqlite3VdbeChangeToNoop(Vdbe*, int addr);
+SQLITE_PRIVATE int sqlite3VdbeChangeToNoop(Vdbe*, int addr);
SQLITE_PRIVATE int sqlite3VdbeDeletePriorOpcode(Vdbe*, u8 op);
SQLITE_PRIVATE void sqlite3VdbeChangeP4(Vdbe*, int addr, const char *zP4, int N);
SQLITE_PRIVATE void sqlite3VdbeSetP4KeyInfo(Parse*, Index*);
@@ -10002,6 +12579,7 @@ SQLITE_PRIVATE void sqlite3VdbeUsesBtree(Vdbe*, int);
SQLITE_PRIVATE VdbeOp *sqlite3VdbeGetOp(Vdbe*, int);
SQLITE_PRIVATE int sqlite3VdbeMakeLabel(Vdbe*);
SQLITE_PRIVATE void sqlite3VdbeRunOnlyOnce(Vdbe*);
+SQLITE_PRIVATE void sqlite3VdbeReusable(Vdbe*);
SQLITE_PRIVATE void sqlite3VdbeDelete(Vdbe*);
SQLITE_PRIVATE void sqlite3VdbeClearObject(sqlite3*,Vdbe*);
SQLITE_PRIVATE void sqlite3VdbeMakeReady(Vdbe*,Parse*);
@@ -10182,7 +12760,11 @@ typedef struct PgHdr DbPage;
#define PAGER_LOCKINGMODE_EXCLUSIVE 1
/*
-** Numeric constants that encode the journalmode.
+** Numeric constants that encode the journalmode.
+**
+** The numeric values encoded here (other than PAGER_JOURNALMODE_QUERY)
+** are exposed in the API via the "PRAGMA journal_mode" command and
+** therefore cannot be changed without a compatibility break.
*/
#define PAGER_JOURNALMODE_QUERY (-1) /* Query the value of journalmode */
#define PAGER_JOURNALMODE_DELETE 0 /* Commit by deleting journal file */
@@ -10193,22 +12775,28 @@ typedef struct PgHdr DbPage;
#define PAGER_JOURNALMODE_WAL 5 /* Use write-ahead logging */
/*
-** Flags that make up the mask passed to sqlite3PagerAcquire().
+** Flags that make up the mask passed to sqlite3PagerGet().
*/
#define PAGER_GET_NOCONTENT 0x01 /* Do not load data from disk */
#define PAGER_GET_READONLY 0x02 /* Read-only page is acceptable */
/*
** Flags for sqlite3PagerSetFlags()
+**
+** Value constraints (enforced via assert()):
+** PAGER_FULLFSYNC == SQLITE_FullFSync
+** PAGER_CKPT_FULLFSYNC == SQLITE_CkptFullFSync
+** PAGER_CACHE_SPILL == SQLITE_CacheSpill
*/
#define PAGER_SYNCHRONOUS_OFF 0x01 /* PRAGMA synchronous=OFF */
#define PAGER_SYNCHRONOUS_NORMAL 0x02 /* PRAGMA synchronous=NORMAL */
#define PAGER_SYNCHRONOUS_FULL 0x03 /* PRAGMA synchronous=FULL */
-#define PAGER_SYNCHRONOUS_MASK 0x03 /* Mask for three values above */
-#define PAGER_FULLFSYNC 0x04 /* PRAGMA fullfsync=ON */
-#define PAGER_CKPT_FULLFSYNC 0x08 /* PRAGMA checkpoint_fullfsync=ON */
-#define PAGER_CACHESPILL 0x10 /* PRAGMA cache_spill=ON */
-#define PAGER_FLAGS_MASK 0x1c /* All above except SYNCHRONOUS */
+#define PAGER_SYNCHRONOUS_EXTRA 0x04 /* PRAGMA synchronous=EXTRA */
+#define PAGER_SYNCHRONOUS_MASK 0x07 /* Mask for four values above */
+#define PAGER_FULLFSYNC 0x08 /* PRAGMA fullfsync=ON */
+#define PAGER_CKPT_FULLFSYNC 0x10 /* PRAGMA checkpoint_fullfsync=ON */
+#define PAGER_CACHESPILL 0x20 /* PRAGMA cache_spill=ON */
+#define PAGER_FLAGS_MASK 0x38 /* All above except SYNCHRONOUS */
/*
** The remainder of this file contains the declarations of the functions
@@ -10232,8 +12820,12 @@ SQLITE_PRIVATE int sqlite3PagerReadFileheader(Pager*, int, unsigned char*);
/* Functions used to configure a Pager object. */
SQLITE_PRIVATE void sqlite3PagerSetBusyhandler(Pager*, int(*)(void *), void *);
SQLITE_PRIVATE int sqlite3PagerSetPagesize(Pager*, u32*, int);
+#ifdef SQLITE_HAS_CODEC
+SQLITE_PRIVATE void sqlite3PagerAlignReserve(Pager*,Pager*);
+#endif
SQLITE_PRIVATE int sqlite3PagerMaxPageCount(Pager*, int);
SQLITE_PRIVATE void sqlite3PagerSetCachesize(Pager*, int);
+SQLITE_PRIVATE int sqlite3PagerSetSpillsize(Pager*, int);
SQLITE_PRIVATE void sqlite3PagerSetMmapLimit(Pager *, sqlite3_int64);
SQLITE_PRIVATE void sqlite3PagerShrink(Pager*);
SQLITE_PRIVATE void sqlite3PagerSetFlags(Pager*,unsigned);
@@ -10243,10 +12835,10 @@ SQLITE_PRIVATE int sqlite3PagerGetJournalMode(Pager*);
SQLITE_PRIVATE int sqlite3PagerOkToChangeJournalMode(Pager*);
SQLITE_PRIVATE i64 sqlite3PagerJournalSizeLimit(Pager *, i64);
SQLITE_PRIVATE sqlite3_backup **sqlite3PagerBackupPtr(Pager*);
+SQLITE_PRIVATE int sqlite3PagerFlush(Pager*);
/* Functions used to obtain and release page references. */
-SQLITE_PRIVATE int sqlite3PagerAcquire(Pager *pPager, Pgno pgno, DbPage **ppPage, int clrFlag);
-#define sqlite3PagerGet(A,B,C) sqlite3PagerAcquire(A,B,C,0)
+SQLITE_PRIVATE int sqlite3PagerGet(Pager *pPager, Pgno pgno, DbPage **ppPage, int clrFlag);
SQLITE_PRIVATE DbPage *sqlite3PagerLookup(Pager *pPager, Pgno pgno);
SQLITE_PRIVATE void sqlite3PagerRef(DbPage*);
SQLITE_PRIVATE void sqlite3PagerUnref(DbPage*);
@@ -10278,6 +12870,10 @@ SQLITE_PRIVATE int sqlite3PagerWalSupported(Pager *pPager);
SQLITE_PRIVATE int sqlite3PagerWalCallback(Pager *pPager);
SQLITE_PRIVATE int sqlite3PagerOpenWal(Pager *pPager, int *pisOpen);
SQLITE_PRIVATE int sqlite3PagerCloseWal(Pager *pPager);
+# ifdef SQLITE_ENABLE_SNAPSHOT
+SQLITE_PRIVATE int sqlite3PagerSnapshotGet(Pager *pPager, sqlite3_snapshot **ppSnapshot);
+SQLITE_PRIVATE int sqlite3PagerSnapshotOpen(Pager *pPager, sqlite3_snapshot *pSnapshot);
+# endif
#endif
#ifdef SQLITE_ENABLE_ZIPVFS
@@ -10287,13 +12883,15 @@ SQLITE_PRIVATE int sqlite3PagerWalFramesize(Pager *pPager);
/* Functions used to query pager state and configuration. */
SQLITE_PRIVATE u8 sqlite3PagerIsreadonly(Pager*);
SQLITE_PRIVATE u32 sqlite3PagerDataVersion(Pager*);
-SQLITE_PRIVATE int sqlite3PagerRefcount(Pager*);
+#ifdef SQLITE_DEBUG
+SQLITE_PRIVATE int sqlite3PagerRefcount(Pager*);
+#endif
SQLITE_PRIVATE int sqlite3PagerMemUsed(Pager*);
SQLITE_PRIVATE const char *sqlite3PagerFilename(Pager*, int);
-SQLITE_PRIVATE const sqlite3_vfs *sqlite3PagerVfs(Pager*);
+SQLITE_PRIVATE sqlite3_vfs *sqlite3PagerVfs(Pager*);
SQLITE_PRIVATE sqlite3_file *sqlite3PagerFile(Pager*);
+SQLITE_PRIVATE sqlite3_file *sqlite3PagerJrnlFile(Pager*);
SQLITE_PRIVATE const char *sqlite3PagerJournalname(Pager*);
-SQLITE_PRIVATE int sqlite3PagerNosync(Pager*);
SQLITE_PRIVATE void *sqlite3PagerTempSpace(Pager*);
SQLITE_PRIVATE int sqlite3PagerIsMemdb(Pager*);
SQLITE_PRIVATE void sqlite3PagerCacheStat(Pager *, int, int, int *);
@@ -10358,7 +12956,7 @@ struct PgHdr {
sqlite3_pcache_page *pPage; /* Pcache object page handle */
void *pData; /* Page data */
void *pExtra; /* Extra content */
- PgHdr *pDirty; /* Transient list of dirty pages */
+ PgHdr *pDirty; /* Transient list of dirty sorted by pgno */
Pager *pPager; /* The pager this page is part of */
Pgno pgno; /* Page number for this page */
#ifdef SQLITE_CHECK_PAGES
@@ -10378,14 +12976,15 @@ struct PgHdr {
};
/* Bit values for PgHdr.flags */
-#define PGHDR_DIRTY 0x002 /* Page has changed */
-#define PGHDR_NEED_SYNC 0x004 /* Fsync the rollback journal before
- ** writing this page to the database */
-#define PGHDR_NEED_READ 0x008 /* Content is unread */
-#define PGHDR_REUSE_UNLIKELY 0x010 /* A hint that reuse is unlikely */
-#define PGHDR_DONT_WRITE 0x020 /* Do not write content to disk */
+#define PGHDR_CLEAN 0x001 /* Page not on the PCache.pDirty list */
+#define PGHDR_DIRTY 0x002 /* Page is on the PCache.pDirty list */
+#define PGHDR_WRITEABLE 0x004 /* Journaled and ready to modify */
+#define PGHDR_NEED_SYNC 0x008 /* Fsync the rollback journal before
+ ** writing this page to the database */
+#define PGHDR_DONT_WRITE 0x010 /* Do not write content to disk */
+#define PGHDR_MMAP 0x020 /* This is an mmap page object */
-#define PGHDR_MMAP 0x040 /* This is an mmap page object */
+#define PGHDR_WAL_APPEND 0x040 /* Appended to wal file */
/* Initialize and shutdown the page cache subsystem */
SQLITE_PRIVATE int sqlite3PcacheInitialize(void);
@@ -10429,6 +13028,7 @@ SQLITE_PRIVATE void sqlite3PcacheDrop(PgHdr*); /* Remove page from cache
SQLITE_PRIVATE void sqlite3PcacheMakeDirty(PgHdr*); /* Make sure page is marked dirty */
SQLITE_PRIVATE void sqlite3PcacheMakeClean(PgHdr*); /* Mark a single page as clean */
SQLITE_PRIVATE void sqlite3PcacheCleanAll(PCache*); /* Mark all dirty list pages as clean */
+SQLITE_PRIVATE void sqlite3PcacheClearWritable(PCache*);
/* Change a page number. Used by incr-vacuum. */
SQLITE_PRIVATE void sqlite3PcacheMove(PgHdr*, Pgno);
@@ -10467,6 +13067,11 @@ SQLITE_PRIVATE int sqlite3PcachePagecount(PCache*);
SQLITE_PRIVATE void sqlite3PcacheIterateDirty(PCache *pCache, void (*xIter)(PgHdr *));
#endif
+#if defined(SQLITE_DEBUG)
+/* Check invariants on a PgHdr object */
+SQLITE_PRIVATE int sqlite3PcachePageSanity(PgHdr*);
+#endif
+
/* Set and get the suggested cache-size for the specified pager-cache.
**
** If no global maximum is configured, then the system attempts to limit
@@ -10478,6 +13083,13 @@ SQLITE_PRIVATE void sqlite3PcacheSetCachesize(PCache *, int);
SQLITE_PRIVATE int sqlite3PcacheGetCachesize(PCache *);
#endif
+/* Set or get the suggested spill-size for the specified pager-cache.
+**
+** The spill-size is the minimum number of pages in cache before the cache
+** will attempt to spill dirty pages by calling xStress.
+*/
+SQLITE_PRIVATE int sqlite3PcacheSetSpillsize(PCache *, int);
+
/* Free up as much memory as possible from the page cache */
SQLITE_PRIVATE void sqlite3PcacheShrink(PCache*);
@@ -10496,11 +13108,13 @@ SQLITE_PRIVATE void sqlite3PCacheSetDefault(void);
SQLITE_PRIVATE int sqlite3HeaderSizePcache(void);
SQLITE_PRIVATE int sqlite3HeaderSizePcache1(void);
+/* Number of dirty pages as a percentage of the configured cache size */
+SQLITE_PRIVATE int sqlite3PCachePercentDirty(PCache*);
+
#endif /* _PCACHE_H_ */
/************** End of pcache.h **********************************************/
/************** Continuing where we left off in sqliteInt.h ******************/
-
/************** Include os.h in the middle of sqliteInt.h ********************/
/************** Begin file os.h **********************************************/
/*
@@ -10726,7 +13340,7 @@ SQLITE_PRIVATE int sqlite3OsInit(void);
/*
** Functions for accessing sqlite3_file methods
*/
-SQLITE_PRIVATE int sqlite3OsClose(sqlite3_file*);
+SQLITE_PRIVATE void sqlite3OsClose(sqlite3_file*);
SQLITE_PRIVATE int sqlite3OsRead(sqlite3_file*, void*, int amt, i64 offset);
SQLITE_PRIVATE int sqlite3OsWrite(sqlite3_file*, const void*, int amt, i64 offset);
SQLITE_PRIVATE int sqlite3OsTruncate(sqlite3_file*, i64 size);
@@ -10763,6 +13377,7 @@ SQLITE_PRIVATE void sqlite3OsDlClose(sqlite3_vfs *, void *);
#endif /* SQLITE_OMIT_LOAD_EXTENSION */
SQLITE_PRIVATE int sqlite3OsRandomness(sqlite3_vfs *, int, char *);
SQLITE_PRIVATE int sqlite3OsSleep(sqlite3_vfs *, int);
+SQLITE_PRIVATE int sqlite3OsGetLastError(sqlite3_vfs*);
SQLITE_PRIVATE int sqlite3OsCurrentTimeInt64(sqlite3_vfs *, sqlite3_int64*);
/*
@@ -10770,7 +13385,7 @@ SQLITE_PRIVATE int sqlite3OsCurrentTimeInt64(sqlite3_vfs *, sqlite3_int64*);
** sqlite3_malloc() to obtain space for the file-handle structure.
*/
SQLITE_PRIVATE int sqlite3OsOpenMalloc(sqlite3_vfs *, const char *, sqlite3_file **, int,int*);
-SQLITE_PRIVATE int sqlite3OsCloseFree(sqlite3_file *);
+SQLITE_PRIVATE void sqlite3OsCloseFree(sqlite3_file *);
#endif /* _SQLITE_OS_H_ */
@@ -10852,6 +13467,36 @@ SQLITE_PRIVATE int sqlite3OsCloseFree(sqlite3_file *);
/************** End of mutex.h ***********************************************/
/************** Continuing where we left off in sqliteInt.h ******************/
+/* The SQLITE_EXTRA_DURABLE compile-time option used to set the default
+** synchronous setting to EXTRA. It is no longer supported.
+*/
+#ifdef SQLITE_EXTRA_DURABLE
+# warning Use SQLITE_DEFAULT_SYNCHRONOUS=3 instead of SQLITE_EXTRA_DURABLE
+# define SQLITE_DEFAULT_SYNCHRONOUS 3
+#endif
+
+/*
+** Default synchronous levels.
+**
+** Note that (for historcal reasons) the PAGER_SYNCHRONOUS_* macros differ
+** from the SQLITE_DEFAULT_SYNCHRONOUS value by 1.
+**
+** PAGER_SYNCHRONOUS DEFAULT_SYNCHRONOUS
+** OFF 1 0
+** NORMAL 2 1
+** FULL 3 2
+** EXTRA 4 3
+**
+** The "PRAGMA synchronous" statement also uses the zero-based numbers.
+** In other words, the zero-based numbers are used for all external interfaces
+** and the one-based values are used internally.
+*/
+#ifndef SQLITE_DEFAULT_SYNCHRONOUS
+# define SQLITE_DEFAULT_SYNCHRONOUS (PAGER_SYNCHRONOUS_FULL-1)
+#endif
+#ifndef SQLITE_DEFAULT_WAL_SYNCHRONOUS
+# define SQLITE_DEFAULT_WAL_SYNCHRONOUS SQLITE_DEFAULT_SYNCHRONOUS
+#endif
/*
** Each database file to be accessed by the system is an instance
@@ -10864,6 +13509,7 @@ struct Db {
char *zName; /* Name of this database */
Btree *pBt; /* The B*Tree structure for this database file */
u8 safety_level; /* How aggressive at syncing data to disk */
+ u8 bSyncSet; /* True if "PRAGMA synchronous=N" has been run */
Schema *pSchema; /* Pointer to database schema (possibly shared) */
};
@@ -10874,7 +13520,7 @@ struct Db {
** the Schema for the TEMP databaes (sqlite3.aDb[1]) which is free-standing.
** In shared cache mode, a single Schema object can be shared by multiple
** Btrees that refer to the same underlying BtShared object.
-**
+**
** Schema objects are automatically deallocated when the last Btree that
** references them is destroyed. The TEMP Schema is manually freed by
** sqlite3_close().
@@ -10899,7 +13545,7 @@ struct Schema {
};
/*
-** These macros can be used to test, set, or clear bits in the
+** These macros can be used to test, set, or clear bits in the
** Db.pSchema->flags field.
*/
#define DbHasProperty(D,I,P) (((D)->aDb[I].pSchema->schemaFlags&(P))==(P))
@@ -10948,8 +13594,8 @@ struct Schema {
** lookaside allocations are not used to construct the schema objects.
*/
struct Lookaside {
+ u32 bDisable; /* Only operate the lookaside when zero */
u16 sz; /* Size of each buffer in bytes */
- u8 bEnabled; /* False to disable new lookaside allocations */
u8 bMalloced; /* True if pStart obtained from sqlite3_malloc() */
int nOut; /* Number of buffers currently checked out */
int mxOut; /* Highwater mark for nOut */
@@ -10963,13 +13609,15 @@ struct LookasideSlot {
};
/*
-** A hash table for function definitions.
+** A hash table for built-in function definitions. (Application-defined
+** functions use a regular table table from hash.h.)
**
** Hash each FuncDef structure into one of the FuncDefHash.a[] slots.
-** Collisions are on the FuncDef.pHash chain.
+** Collisions are on the FuncDef.u.pHash chain.
*/
+#define SQLITE_FUNC_HASH_SZ 23
struct FuncDefHash {
- FuncDef *a[23]; /* Hash table for functions */
+ FuncDef *a[SQLITE_FUNC_HASH_SZ]; /* Hash table for functions */
};
#ifdef SQLITE_USER_AUTHENTICATION
@@ -11027,11 +13675,13 @@ struct sqlite3 {
unsigned int openFlags; /* Flags passed to sqlite3_vfs.xOpen() */
int errCode; /* Most recent error code (SQLITE_*) */
int errMask; /* & result codes with this before returning */
+ int iSysErrno; /* Errno value from last system error */
u16 dbOptFlags; /* Flags to enable/disable optimizations */
u8 enc; /* Text encoding */
u8 autoCommit; /* The auto-commit flag. */
u8 temp_store; /* 1: file 2: memory 0: default */
u8 mallocFailed; /* True if we have seen a malloc failure */
+ u8 bBenignMalloc; /* Do not require OOMs if true */
u8 dfltLockMode; /* Default locking-mode for attached dbs */
signed char nextAutovac; /* Autovac setting after VACUUM if >=0 */
u8 suppressErr; /* Do not issue error messages if true */
@@ -11061,12 +13711,19 @@ struct sqlite3 {
void *pTraceArg; /* Argument to the trace function */
void (*xProfile)(void*,const char*,u64); /* Profiling function */
void *pProfileArg; /* Argument to profile function */
- void *pCommitArg; /* Argument to xCommitCallback() */
+ void *pCommitArg; /* Argument to xCommitCallback() */
int (*xCommitCallback)(void*); /* Invoked at every commit. */
- void *pRollbackArg; /* Argument to xRollbackCallback() */
+ void *pRollbackArg; /* Argument to xRollbackCallback() */
void (*xRollbackCallback)(void*); /* Invoked at every commit. */
void *pUpdateArg;
void (*xUpdateCallback)(void*,int, const char*,const char*,sqlite_int64);
+#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
+ void *pPreUpdateArg; /* First argument to xPreUpdateCallback */
+ void (*xPreUpdateCallback)( /* Registered using sqlite3_preupdate_hook() */
+ void*,sqlite3*,int,char const*,char const*,sqlite3_int64,sqlite3_int64
+ );
+ PreUpdate *pPreUpdate; /* Context for active pre-update callback */
+#endif /* SQLITE_ENABLE_PREUPDATE_HOOK */
#ifndef SQLITE_OMIT_WAL
int (*xWalCallback)(void *, sqlite3 *, const char *, int);
void *pWalArg;
@@ -11096,7 +13753,7 @@ struct sqlite3 {
VTable **aVTrans; /* Virtual tables with open transactions */
VTable *pDisconnect; /* Disconnect these in next sqlite3_prepare() */
#endif
- FuncDefHash aFunc; /* Hash table of connection functions */
+ Hash aFunc; /* Hash table of connection functions */
Hash aCollSeq; /* All collating sequences */
BusyHandler busyHandler; /* Busy callback */
Db aDbStatic[2]; /* Static space for the 2 default backends */
@@ -11108,8 +13765,8 @@ struct sqlite3 {
i64 nDeferredImmCons; /* Net deferred immediate constraints */
int *pnBytesFreed; /* If not NULL, increment this in DbFree() */
#ifdef SQLITE_ENABLE_UNLOCK_NOTIFY
- /* The following variables are all protected by the STATIC_MASTER
- ** mutex, not by sqlite3.mutex. They are used by code in notify.c.
+ /* The following variables are all protected by the STATIC_MASTER
+ ** mutex, not by sqlite3.mutex. They are used by code in notify.c.
**
** When X.pUnlockConnection==Y, that means that X is waiting for Y to
** unlock so that it can proceed.
@@ -11137,13 +13794,18 @@ struct sqlite3 {
/*
** Possible values for the sqlite3.flags.
+**
+** Value constraints (enforced via assert()):
+** SQLITE_FullFSync == PAGER_FULLFSYNC
+** SQLITE_CkptFullFSync == PAGER_CKPT_FULLFSYNC
+** SQLITE_CacheSpill == PAGER_CACHE_SPILL
*/
#define SQLITE_VdbeTrace 0x00000001 /* True to trace VDBE execution */
#define SQLITE_InternChanges 0x00000002 /* Uncommitted Hash table changes */
-#define SQLITE_FullFSync 0x00000004 /* Use full fsync on the backend */
-#define SQLITE_CkptFullFSync 0x00000008 /* Use full fsync for checkpoint */
-#define SQLITE_CacheSpill 0x00000010 /* OK to spill pager cache */
-#define SQLITE_FullColNames 0x00000020 /* Show full column names on SELECT */
+#define SQLITE_FullColNames 0x00000004 /* Show full column names on SELECT */
+#define SQLITE_FullFSync 0x00000008 /* Use full fsync on the backend */
+#define SQLITE_CkptFullFSync 0x00000010 /* Use full fsync for checkpoint */
+#define SQLITE_CacheSpill 0x00000020 /* OK to spill pager cache */
#define SQLITE_ShortColNames 0x00000040 /* Show short columns names */
#define SQLITE_CountRows 0x00000080 /* Count rows changed by INSERT, */
/* DELETE, or UPDATE and return */
@@ -11164,11 +13826,14 @@ struct sqlite3 {
#define SQLITE_AutoIndex 0x00100000 /* Enable automatic indexes */
#define SQLITE_PreferBuiltin 0x00200000 /* Preference to built-in funcs */
#define SQLITE_LoadExtension 0x00400000 /* Enable load_extension */
-#define SQLITE_EnableTrigger 0x00800000 /* True to enable triggers */
-#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 */
+#define SQLITE_LoadExtFunc 0x00800000 /* Enable load_extension() SQL func */
+#define SQLITE_EnableTrigger 0x01000000 /* True to enable triggers */
+#define SQLITE_DeferFKs 0x02000000 /* Defer all FK constraints */
+#define SQLITE_QueryOnly 0x04000000 /* Disable database changes */
+#define SQLITE_VdbeEQP 0x08000000 /* Debug EXPLAIN QUERY PLAN */
+#define SQLITE_Vacuum 0x10000000 /* Currently in a VACUUM */
+#define SQLITE_CellSizeCk 0x20000000 /* Check btree cell sizes on load */
+#define SQLITE_Fts3Tokenizer 0x40000000 /* Enable fts3_tokenizer(2) */
/*
@@ -11188,6 +13853,7 @@ struct sqlite3 {
#define SQLITE_Transitive 0x0200 /* Transitive constraints */
#define SQLITE_OmitNoopJoin 0x0400 /* Omit unused tables in joins */
#define SQLITE_Stat34 0x0800 /* Use STAT3 or STAT4 data */
+#define SQLITE_CursorHints 0x2000 /* Add OP_CursorHint opcodes */
#define SQLITE_AllOpts 0xffff /* All optimizations */
/*
@@ -11221,28 +13887,33 @@ struct sqlite3 {
/*
** Each SQL function is defined by an instance of the following
-** structure. A pointer to this structure is stored in the sqlite.aFunc
-** hash table. When multiple functions have the same name, the hash table
-** points to a linked list of these structures.
+** structure. For global built-in functions (ex: substr(), max(), count())
+** a pointer to this structure is held in the sqlite3BuiltinFunctions object.
+** For per-connection application-defined functions, a pointer to this
+** structure is held in the db->aHash hash table.
+**
+** The u.pHash field is used by the global built-ins. The u.pDestructor
+** field is used by per-connection app-def functions.
*/
struct FuncDef {
- i16 nArg; /* Number of arguments. -1 means unlimited */
+ i8 nArg; /* Number of arguments. -1 means unlimited */
u16 funcFlags; /* Some combination of SQLITE_FUNC_* */
void *pUserData; /* User data parameter */
FuncDef *pNext; /* Next function with same name */
- void (*xFunc)(sqlite3_context*,int,sqlite3_value**); /* Regular function */
- void (*xStep)(sqlite3_context*,int,sqlite3_value**); /* Aggregate step */
- void (*xFinalize)(sqlite3_context*); /* Aggregate finalizer */
- char *zName; /* SQL name of the function. */
- FuncDef *pHash; /* Next with a different name but the same hash */
- FuncDestructor *pDestructor; /* Reference counted destructor function */
+ void (*xSFunc)(sqlite3_context*,int,sqlite3_value**); /* func or agg-step */
+ void (*xFinalize)(sqlite3_context*); /* Agg finalizer */
+ const char *zName; /* SQL name of the function. */
+ union {
+ FuncDef *pHash; /* Next with a different name but the same hash */
+ FuncDestructor *pDestructor; /* Reference counted destructor function */
+ } u;
};
/*
** This structure encapsulates a user-function destructor callback (as
** configured using create_function_v2()) and a reference counter. When
** create_function_v2() is called to create a function with a destructor,
-** a single object of this type is allocated. FuncDestructor.nRef is set to
+** a single object of this type is allocated. FuncDestructor.nRef is set to
** the number of FuncDef objects created (either 1 or 3, depending on whether
** or not the specified encoding is SQLITE_ANY). The FuncDef.pDestructor
** member of each of the new FuncDef objects is set to point to the allocated
@@ -11260,36 +13931,52 @@ struct FuncDestructor {
/*
** Possible values for FuncDef.flags. Note that the _LENGTH and _TYPEOF
-** values must correspond to OPFLAG_LENGTHARG and OPFLAG_TYPEOFARG. There
+** values must correspond to OPFLAG_LENGTHARG and OPFLAG_TYPEOFARG. And
+** SQLITE_FUNC_CONSTANT must be the same as SQLITE_DETERMINISTIC. There
** are assert() statements in the code to verify this.
-*/
-#define SQLITE_FUNC_ENCMASK 0x003 /* SQLITE_UTF8, SQLITE_UTF16BE or UTF16LE */
-#define SQLITE_FUNC_LIKE 0x004 /* Candidate for the LIKE optimization */
-#define SQLITE_FUNC_CASE 0x008 /* Case-sensitive LIKE-type function */
-#define SQLITE_FUNC_EPHEM 0x010 /* Ephemeral. Delete with VDBE */
-#define SQLITE_FUNC_NEEDCOLL 0x020 /* sqlite3GetFuncCollSeq() might be called */
-#define SQLITE_FUNC_LENGTH 0x040 /* Built-in length() function */
-#define SQLITE_FUNC_TYPEOF 0x080 /* Built-in typeof() function */
-#define SQLITE_FUNC_COUNT 0x100 /* Built-in count(*) aggregate */
-#define SQLITE_FUNC_COALESCE 0x200 /* Built-in coalesce() or ifnull() */
-#define SQLITE_FUNC_UNLIKELY 0x400 /* Built-in unlikely() function */
-#define SQLITE_FUNC_CONSTANT 0x800 /* Constant inputs give a constant output */
-#define SQLITE_FUNC_MINMAX 0x1000 /* True for min() and max() aggregates */
+**
+** Value constraints (enforced via assert()):
+** SQLITE_FUNC_MINMAX == NC_MinMaxAgg == SF_MinMaxAgg
+** SQLITE_FUNC_LENGTH == OPFLAG_LENGTHARG
+** SQLITE_FUNC_TYPEOF == OPFLAG_TYPEOFARG
+** SQLITE_FUNC_CONSTANT == SQLITE_DETERMINISTIC from the API
+** SQLITE_FUNC_ENCMASK depends on SQLITE_UTF* macros in the API
+*/
+#define SQLITE_FUNC_ENCMASK 0x0003 /* SQLITE_UTF8, SQLITE_UTF16BE or UTF16LE */
+#define SQLITE_FUNC_LIKE 0x0004 /* Candidate for the LIKE optimization */
+#define SQLITE_FUNC_CASE 0x0008 /* Case-sensitive LIKE-type function */
+#define SQLITE_FUNC_EPHEM 0x0010 /* Ephemeral. Delete with VDBE */
+#define SQLITE_FUNC_NEEDCOLL 0x0020 /* sqlite3GetFuncCollSeq() might be called*/
+#define SQLITE_FUNC_LENGTH 0x0040 /* Built-in length() function */
+#define SQLITE_FUNC_TYPEOF 0x0080 /* Built-in typeof() function */
+#define SQLITE_FUNC_COUNT 0x0100 /* Built-in count(*) aggregate */
+#define SQLITE_FUNC_COALESCE 0x0200 /* Built-in coalesce() or ifnull() */
+#define SQLITE_FUNC_UNLIKELY 0x0400 /* Built-in unlikely() function */
+#define SQLITE_FUNC_CONSTANT 0x0800 /* Constant inputs give a constant output */
+#define SQLITE_FUNC_MINMAX 0x1000 /* True for min() and max() aggregates */
+#define SQLITE_FUNC_SLOCHNG 0x2000 /* "Slow Change". Value constant during a
+ ** single query - might change over time */
/*
** The following three macros, FUNCTION(), LIKEFUNC() and AGGREGATE() are
** used to create the initializers for the FuncDef structures.
**
** FUNCTION(zName, nArg, iArg, bNC, xFunc)
-** Used to create a scalar function definition of a function zName
+** Used to create a scalar function definition of a function zName
** implemented by C function xFunc that accepts nArg arguments. The
** value passed as iArg is cast to a (void*) and made available
-** as the user-data (sqlite3_user_data()) for the function. If
+** as the user-data (sqlite3_user_data()) for the function. If
** argument bNC is true, then the SQLITE_FUNC_NEEDCOLL flag is set.
**
** VFUNCTION(zName, nArg, iArg, bNC, xFunc)
** Like FUNCTION except it omits the SQLITE_FUNC_CONSTANT flag.
**
+** DFUNCTION(zName, nArg, iArg, bNC, xFunc)
+** Like FUNCTION except it omits the SQLITE_FUNC_CONSTANT flag and
+** adds the SQLITE_FUNC_SLOCHNG flag. Used for date & time functions
+** and functions like sqlite_version() that can change, but not during
+** a single query.
+**
** AGGREGATE(zName, nArg, iArg, bNC, xStep, xFinal)
** Used to create an aggregate function definition implemented by
** the C functions xStep and xFinal. The first four parameters
@@ -11297,8 +13984,8 @@ struct FuncDestructor {
** FUNCTION().
**
** LIKEFUNC(zName, nArg, pArg, flags)
-** Used to create a scalar function definition of a function zName
-** that accepts nArg arguments and is implemented by a call to C
+** Used to create a scalar function definition of a function zName
+** that accepts nArg arguments and is implemented by a call to C
** function likeFunc. Argument pArg is cast to a (void *) and made
** available as the function user-data (sqlite3_user_data()). The
** FuncDef.flags variable is set to the value passed as the flags
@@ -11306,25 +13993,28 @@ struct FuncDestructor {
*/
#define FUNCTION(zName, nArg, iArg, bNC, xFunc) \
{nArg, SQLITE_FUNC_CONSTANT|SQLITE_UTF8|(bNC*SQLITE_FUNC_NEEDCOLL), \
- SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, 0, #zName, 0, 0}
+ SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, #zName, {0} }
#define VFUNCTION(zName, nArg, iArg, bNC, xFunc) \
{nArg, SQLITE_UTF8|(bNC*SQLITE_FUNC_NEEDCOLL), \
- SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, 0, #zName, 0, 0}
+ SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, #zName, {0} }
+#define DFUNCTION(zName, nArg, iArg, bNC, xFunc) \
+ {nArg, SQLITE_FUNC_SLOCHNG|SQLITE_UTF8|(bNC*SQLITE_FUNC_NEEDCOLL), \
+ SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, #zName, {0} }
#define FUNCTION2(zName, nArg, iArg, bNC, xFunc, extraFlags) \
{nArg,SQLITE_FUNC_CONSTANT|SQLITE_UTF8|(bNC*SQLITE_FUNC_NEEDCOLL)|extraFlags,\
- SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, 0, #zName, 0, 0}
+ SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, #zName, {0} }
#define STR_FUNCTION(zName, nArg, pArg, bNC, xFunc) \
- {nArg, SQLITE_FUNC_CONSTANT|SQLITE_UTF8|(bNC*SQLITE_FUNC_NEEDCOLL), \
- pArg, 0, xFunc, 0, 0, #zName, 0, 0}
+ {nArg, SQLITE_FUNC_SLOCHNG|SQLITE_UTF8|(bNC*SQLITE_FUNC_NEEDCOLL), \
+ pArg, 0, xFunc, 0, #zName, }
#define LIKEFUNC(zName, nArg, arg, flags) \
{nArg, SQLITE_FUNC_CONSTANT|SQLITE_UTF8|flags, \
- (void *)arg, 0, likeFunc, 0, 0, #zName, 0, 0}
+ (void *)arg, 0, likeFunc, 0, #zName, {0} }
#define AGGREGATE(zName, nArg, arg, nc, xStep, xFinal) \
{nArg, SQLITE_UTF8|(nc*SQLITE_FUNC_NEEDCOLL), \
- SQLITE_INT_TO_PTR(arg), 0, 0, xStep,xFinal,#zName,0,0}
+ SQLITE_INT_TO_PTR(arg), 0, xStep,xFinal,#zName, {0}}
#define AGGREGATE2(zName, nArg, arg, nc, xStep, xFinal, extraFlags) \
{nArg, SQLITE_UTF8|(nc*SQLITE_FUNC_NEEDCOLL)|extraFlags, \
- SQLITE_INT_TO_PTR(arg), 0, 0, xStep,xFinal,#zName,0,0}
+ SQLITE_INT_TO_PTR(arg), 0, xStep,xFinal,#zName, {0}}
/*
** All current savepoints are stored in a linked list starting at
@@ -11358,6 +14048,7 @@ struct Module {
const char *zName; /* Name passed to create_module() */
void *pAux; /* pAux passed to create_module() */
void (*xDestroy)(void *); /* Module destructor function */
+ Table *pEpoTab; /* Eponymous table for this module */
};
/*
@@ -11365,14 +14056,12 @@ struct Module {
** of this structure.
*/
struct Column {
- char *zName; /* Name of this column */
+ char *zName; /* Name of this column, \000, then the type */
Expr *pDflt; /* Default value of this column */
- char *zDflt; /* Original text of the default value */
- char *zType; /* Data type for this column */
char *zColl; /* Collating sequence. If NULL, use the default */
u8 notNull; /* An OE_ code for handling a NOT NULL constraint */
char affinity; /* One of the SQLITE_AFF_... values */
- u8 szEst; /* Estimated size of this column. INT==1 */
+ u8 szEst; /* Estimated size of value in this column. sizeof(INT)==1 */
u8 colFlags; /* Boolean properties. See COLFLAG_ defines below */
};
@@ -11380,6 +14069,7 @@ struct Column {
*/
#define COLFLAG_PRIMKEY 0x0001 /* Column is part of the primary key */
#define COLFLAG_HIDDEN 0x0002 /* A hidden column in a virtual table */
+#define COLFLAG_HASTYPE 0x0004 /* Type name follows column name */
/*
** A "Collating Sequence" is defined by an instance of the following
@@ -11403,22 +14093,23 @@ struct CollSeq {
*/
#define SQLITE_SO_ASC 0 /* Sort in ascending order */
#define SQLITE_SO_DESC 1 /* Sort in ascending order */
+#define SQLITE_SO_UNDEFINED -1 /* No sort order specified */
/*
** Column affinity types.
**
** These used to have mnemonic name like 'i' for SQLITE_AFF_INTEGER and
** 't' for SQLITE_AFF_TEXT. But we can save a little space and improve
-** the speed a little by numbering the values consecutively.
+** the speed a little by numbering the values consecutively.
**
** But rather than start with 0 or 1, we begin with 'A'. That way,
** when multiple affinity types are concatenated into a string and
** used as the P4 operand, they will be more readable.
**
** Note also that the numeric types are grouped together so that testing
-** for a numeric type is a single comparison. And the NONE type is first.
+** for a numeric type is a single comparison. And the BLOB type is first.
*/
-#define SQLITE_AFF_NONE 'A'
+#define SQLITE_AFF_BLOB 'A'
#define SQLITE_AFF_TEXT 'B'
#define SQLITE_AFF_NUMERIC 'C'
#define SQLITE_AFF_INTEGER 'D'
@@ -11428,7 +14119,7 @@ struct CollSeq {
/*
** The SQLITE_AFF_MASK values masks off the significant bits of an
-** affinity value.
+** affinity value.
*/
#define SQLITE_AFF_MASK 0x47
@@ -11448,20 +14139,20 @@ struct CollSeq {
/*
** An object of this type is created for each virtual table present in
-** the database schema.
+** the database schema.
**
** If the database schema is shared, then there is one instance of this
** structure for each database connection (sqlite3*) that uses the shared
** schema. This is because each database connection requires its own unique
-** instance of the sqlite3_vtab* handle used to access the virtual table
-** implementation. sqlite3_vtab* handles can not be shared between
-** database connections, even when the rest of the in-memory database
+** instance of the sqlite3_vtab* handle used to access the virtual table
+** implementation. sqlite3_vtab* handles can not be shared between
+** database connections, even when the rest of the in-memory database
** schema is shared, as the implementation often stores the database
** connection handle passed to it via the xConnect() or xCreate() method
** during initialization internally. This database connection handle may
-** then be used by the virtual table implementation to access real tables
-** within the database. So that they appear as part of the callers
-** transaction, these accesses need to be made via the same database
+** then be used by the virtual table implementation to access real tables
+** within the database. So that they appear as part of the callers
+** transaction, these accesses need to be made via the same database
** connection as that used to execute SQL operations on the virtual table.
**
** All VTable objects that correspond to a single table in a shared
@@ -11473,19 +14164,19 @@ struct CollSeq {
** sqlite3_vtab* handle in the compiled query.
**
** When an in-memory Table object is deleted (for example when the
-** schema is being reloaded for some reason), the VTable objects are not
-** deleted and the sqlite3_vtab* handles are not xDisconnect()ed
+** schema is being reloaded for some reason), the VTable objects are not
+** deleted and the sqlite3_vtab* handles are not xDisconnect()ed
** immediately. Instead, they are moved from the Table.pVTable list to
** another linked list headed by the sqlite3.pDisconnect member of the
-** corresponding sqlite3 structure. They are then deleted/xDisconnected
+** corresponding sqlite3 structure. They are then deleted/xDisconnected
** next time a statement is prepared using said sqlite3*. This is done
** to avoid deadlock issues involving multiple sqlite3.mutex mutexes.
** Refer to comments above function sqlite3VtabUnlockList() for an
** explanation as to why it is safe to add an entry to an sqlite3.pDisconnect
** list without holding the corresponding sqlite3.mutex mutex.
**
-** The memory for objects of this type is always allocated by
-** sqlite3DbMalloc(), using the connection handle stored in VTable.db as
+** The memory for objects of this type is always allocated by
+** sqlite3DbMalloc(), using the connection handle stored in VTable.db as
** the first argument.
*/
struct VTable {
@@ -11509,9 +14200,8 @@ struct Table {
Select *pSelect; /* NULL for tables. Points to definition if a view. */
FKey *pFKey; /* Linked list of all foreign keys in this table */
char *zColAff; /* String defining the affinity of each column */
-#ifndef SQLITE_OMIT_CHECK
ExprList *pCheck; /* All CHECK constraints */
-#endif
+ /* ... also used as column name list in a VIEW */
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 */
@@ -11528,7 +14218,7 @@ struct Table {
#endif
#ifndef SQLITE_OMIT_VIRTUALTABLE
int nModuleArg; /* Number of arguments to the module */
- char **azModuleArg; /* Text of all module args. [0] is module name */
+ char **azModuleArg; /* 0: module 1: schema 2: vtab name 3...: args */
VTable *pVTable; /* List of VTable objects. */
#endif
Trigger *pTrigger; /* List of triggers stored in pSchema */
@@ -11539,7 +14229,7 @@ struct Table {
/*
** Allowed values for Table.tabFlags.
**
-** TF_OOOHidden applies to virtual tables that have hidden columns that are
+** TF_OOOHidden applies to tables or view 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
@@ -11550,8 +14240,9 @@ struct Table {
#define TF_HasPrimaryKey 0x04 /* Table has a primary key */
#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 */
+#define TF_WithoutRowid 0x20 /* No rowid. PRIMARY KEY is the key */
+#define TF_NoVisibleRowid 0x40 /* No user-visible "rowid" column */
+#define TF_OOOHidden 0x80 /* Out-of-Order hidden columns */
/*
@@ -11561,14 +14252,31 @@ struct Table {
*/
#ifndef SQLITE_OMIT_VIRTUALTABLE
# define IsVirtual(X) (((X)->tabFlags & TF_Virtual)!=0)
-# define IsHiddenColumn(X) (((X)->colFlags & COLFLAG_HIDDEN)!=0)
#else
# define IsVirtual(X) 0
-# define IsHiddenColumn(X) 0
#endif
+/*
+** Macros to determine if a column is hidden. IsOrdinaryHiddenColumn()
+** only works for non-virtual tables (ordinary tables and views) and is
+** always false unless SQLITE_ENABLE_HIDDEN_COLUMNS is defined. The
+** IsHiddenColumn() macro is general purpose.
+*/
+#if defined(SQLITE_ENABLE_HIDDEN_COLUMNS)
+# define IsHiddenColumn(X) (((X)->colFlags & COLFLAG_HIDDEN)!=0)
+# define IsOrdinaryHiddenColumn(X) (((X)->colFlags & COLFLAG_HIDDEN)!=0)
+#elif !defined(SQLITE_OMIT_VIRTUALTABLE)
+# define IsHiddenColumn(X) (((X)->colFlags & COLFLAG_HIDDEN)!=0)
+# define IsOrdinaryHiddenColumn(X) 0
+#else
+# define IsHiddenColumn(X) 0
+# define IsOrdinaryHiddenColumn(X) 0
+#endif
+
+
/* Does the table have a rowid */
#define HasRowid(X) (((X)->tabFlags & TF_WithoutRowid)==0)
+#define VisibleRowid(X) (((X)->tabFlags & TF_NoVisibleRowid)==0)
/*
** Each foreign key constraint is an instance of the following structure.
@@ -11636,7 +14344,7 @@ struct FKey {
** key is set to NULL. CASCADE means that a DELETE or UPDATE of the
** referenced table row is propagated into the row that holds the
** foreign key.
-**
+**
** The following symbolic values are used to record which type
** of action to take.
*/
@@ -11657,7 +14365,7 @@ struct FKey {
/*
** An instance of the following structure is passed as the first
-** argument to sqlite3VdbeKeyCompare and is used to control the
+** argument to sqlite3VdbeKeyCompare and is used to control the
** comparison of the two index keys.
**
** Note that aSortOrder[] and aColl[] have nField+1 slots. There
@@ -11675,9 +14383,8 @@ struct KeyInfo {
};
/*
-** An instance of the following structure holds information about a
-** single index record that has already been parsed out into individual
-** values.
+** This object holds a record which has been parsed out into individual
+** fields, for the purposes of doing a comparison.
**
** A record is an object that contains one or more fields of data.
** Records are used to store the content of a table row and to store
@@ -11685,20 +14392,40 @@ struct KeyInfo {
** the OP_MakeRecord opcode of the VDBE and is disassembled by the
** OP_Column opcode.
**
-** This structure holds a record that has already been disassembled
-** into its constituent fields.
-**
-** The r1 and r2 member variables are only used by the optimized comparison
-** functions vdbeRecordCompareInt() and vdbeRecordCompareString().
+** An instance of this object serves as a "key" for doing a search on
+** an index b+tree. The goal of the search is to find the entry that
+** is closed to the key described by this object. This object might hold
+** just a prefix of the key. The number of fields is given by
+** pKeyInfo->nField.
+**
+** The r1 and r2 fields are the values to return if this key is less than
+** or greater than a key in the btree, respectively. These are normally
+** -1 and +1 respectively, but might be inverted to +1 and -1 if the b-tree
+** is in DESC order.
+**
+** The key comparison functions actually return default_rc when they find
+** an equals comparison. default_rc can be -1, 0, or +1. If there are
+** multiple entries in the b-tree with the same key (when only looking
+** at the first pKeyInfo->nFields,) then default_rc can be set to -1 to
+** cause the search to find the last match, or +1 to cause the search to
+** find the first match.
+**
+** The key comparison functions will set eqSeen to true if they ever
+** get and equal results when comparing this structure to a b-tree record.
+** When default_rc!=0, the search might end up on the record immediately
+** before the first match or immediately after the last match. The
+** eqSeen field will indicate whether or not an exact match exists in the
+** b-tree.
*/
struct UnpackedRecord {
KeyInfo *pKeyInfo; /* Collation and sort-order information */
+ Mem *aMem; /* Values */
u16 nField; /* Number of entries in apMem[] */
i8 default_rc; /* Comparison result if keys are equal */
u8 errCode; /* Error detected by xRecordCompare (CORRUPT or NOMEM) */
- Mem *aMem; /* Values */
- int r1; /* Value to return if (lhs > rhs) */
- int r2; /* Value to return if (rhs < lhs) */
+ i8 r1; /* Value to return if (lhs > rhs) */
+ i8 r2; /* Value to return if (rhs < lhs) */
+ u8 eqSeen; /* True if an equality comparison has been seen */
};
@@ -11716,7 +14443,7 @@ struct UnpackedRecord {
** In the Table structure describing Ex1, nCol==3 because there are
** three columns in the table. In the Index structure describing
** Ex2, nColumn==2 since 2 of the 3 columns of Ex1 are indexed.
-** The value of aiColumn is {2, 0}. aiColumn[0]==2 because the
+** The value of aiColumn is {2, 0}. aiColumn[0]==2 because the
** first column to be indexed (c3) has an index of 2 in Ex1.aCol[].
** The second column to be indexed (c1) has an index of 0 in
** Ex1.aCol[], hence Ex2.aiColumn[1]==0.
@@ -11724,9 +14451,17 @@ struct UnpackedRecord {
** The Index.onError field determines whether or not the indexed columns
** must be unique and what to do if they are not. When Index.onError=OE_None,
** it means this is not a unique index. Otherwise it is a unique index
-** and the value of Index.onError indicate the which conflict resolution
+** and the value of Index.onError indicate the which conflict resolution
** algorithm to employ whenever an attempt is made to insert a non-unique
** element.
+**
+** While parsing a CREATE TABLE or CREATE INDEX statement in order to
+** generate VDBE code (as opposed to parsing one read from an sqlite_master
+** table as part of parsing an existing database schema), transient instances
+** of this structure may be created. In this case the Index.tnum variable is
+** used to store the address of a VDBE instruction, not a database page
+** number (it cannot - the database page is not allocated until the VDBE
+** program is executed). See convertToWithoutRowidTable() for details.
*/
struct Index {
char *zName; /* Name of this index */
@@ -11737,8 +14472,9 @@ struct Index {
Index *pNext; /* The next index associated with the same table */
Schema *pSchema; /* Schema containing this index */
u8 *aSortOrder; /* for each column: True==DESC, False==ASC */
- char **azColl; /* Array of collation sequence names for index */
+ const char **azColl; /* Array of collation sequence names for index */
Expr *pPartIdxWhere; /* WHERE clause for partial indices */
+ ExprList *aColExpr; /* Column expressions */
int tnum; /* DB Page containing root of this index */
LogEst szIdxRow; /* Estimated average row size in bytes */
u16 nKeyCol; /* Number of columns forming the key */
@@ -11773,8 +14509,14 @@ struct Index {
/* Return true if index X is a UNIQUE index */
#define IsUniqueIndex(X) ((X)->onError!=OE_None)
+/* The Index.aiColumn[] values are normally positive integer. But
+** there are some negative values that have special meaning:
+*/
+#define XN_ROWID (-1) /* Indexed column is the rowid */
+#define XN_EXPR (-2) /* Indexed column is an expression */
+
/*
-** Each sample stored in the sqlite_stat3 table is represented in memory
+** Each sample stored in the sqlite_stat3 table is represented in memory
** using a structure of this type. See documentation at the top of the
** analyze.c source file for additional information.
*/
@@ -11869,9 +14611,9 @@ typedef int ynVar;
** to represent the greater-than-or-equal-to operator in the expression
** tree.
**
-** If the expression is an SQL literal (TK_INTEGER, TK_FLOAT, TK_BLOB,
+** If the expression is an SQL literal (TK_INTEGER, TK_FLOAT, TK_BLOB,
** or TK_STRING), then Expr.token contains the text of the SQL literal. If
-** the expression is a variable (TK_VARIABLE), then Expr.token contains the
+** the expression is a variable (TK_VARIABLE), then Expr.token contains the
** variable name. Finally, if the expression is an SQL function (TK_FUNCTION),
** then Expr.token contains the name of the function.
**
@@ -11882,7 +14624,7 @@ typedef int ynVar;
** a CASE expression or an IN expression of the form "<lhs> IN (<y>, <z>...)".
** Expr.x.pSelect is used if the expression is a sub-select or an expression of
** the form "<lhs> IN (SELECT ...)". If the EP_xIsSelect bit is set in the
-** Expr.flags mask, then Expr.x.pSelect is valid. Otherwise, Expr.x.pList is
+** Expr.flags mask, then Expr.x.pSelect is valid. Otherwise, Expr.x.pList is
** valid.
**
** An expression of the form ID or ID.ID refers to a column in a table.
@@ -11893,8 +14635,8 @@ typedef int ynVar;
** value is also stored in the Expr.iAgg column in the aggregate so that
** it can be accessed after all aggregates are computed.
**
-** If the expression is an unbound variable marker (a question mark
-** character '?' in the original SQL) then the Expr.iTable holds the index
+** If the expression is an unbound variable marker (a question mark
+** character '?' in the original SQL) then the Expr.iTable holds the index
** number for that variable.
**
** If the expression is a subquery then Expr.iColumn holds an integer
@@ -11933,7 +14675,7 @@ struct Expr {
/* If the EP_TokenOnly flag is set in the Expr.flags mask, then no
** space is allocated for the fields below this point. An attempt to
- ** access them will result in a segfault or malfunction.
+ ** access them will result in a segfault or malfunction.
*********************************************************************/
Expr *pLeft; /* Left subnode */
@@ -11988,9 +14730,10 @@ 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_ConstFunc 0x080000 /* Node is a SQLITE_FUNC_CONSTANT function */
+#define EP_ConstFunc 0x080000 /* A SQLITE_FUNC_CONSTANT or _SLOCHNG function */
#define EP_CanBeNull 0x100000 /* Can be null despite NOT NULL constraint */
#define EP_Subquery 0x200000 /* Tree contains a TK_SELECT operator */
+#define EP_Alias 0x400000 /* Is an alias for a result set column */
/*
** Combinations of two or more EP_* flags
@@ -11998,7 +14741,7 @@ struct Expr {
#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
+** These macros can be used to test, set, or clear bits in the
** Expr.flags field.
*/
#define ExprHasProperty(E,P) (((E)->flags&(P))!=0)
@@ -12017,8 +14760,8 @@ struct Expr {
#endif
/*
-** Macros to determine the number of bytes required by a normal Expr
-** struct, an Expr struct with the EP_Reduced flag set in Expr.flags
+** Macros to determine the number of bytes required by a normal Expr
+** struct, an Expr struct with the EP_Reduced flag set in Expr.flags
** and an Expr struct with the EP_TokenOnly flag set.
*/
#define EXPR_FULLSIZE sizeof(Expr) /* Full size */
@@ -12026,7 +14769,7 @@ struct Expr {
#define EXPR_TOKENONLYSIZE offsetof(Expr,pLeft) /* Fewer features */
/*
-** Flags passed to the sqlite3ExprDup() function. See the header comment
+** Flags passed to the sqlite3ExprDup() function. See the header comment
** above sqlite3ExprDup() for details.
*/
#define EXPRDUP_REDUCE 0x0001 /* Used reduced-size Expr nodes */
@@ -12108,7 +14851,11 @@ struct IdList {
** tables in a join to 32 instead of 64. But it also reduces the size
** of the library by 738 bytes on ix86.
*/
-typedef u64 Bitmask;
+#ifdef SQLITE_BITMASK_TYPE
+ typedef SQLITE_BITMASK_TYPE Bitmask;
+#else
+ typedef u64 Bitmask;
+#endif
/*
** The number of bits in a Bitmask. "BMS" means "BitMask Size".
@@ -12120,6 +14867,7 @@ typedef u64 Bitmask;
*/
#define MASKBIT(n) (((Bitmask)1)<<(n))
#define MASKBIT32(n) (((unsigned int)1)<<(n))
+#define ALLBITS ((Bitmask)-1)
/*
** The following structure describes the FROM clause of a SELECT statement.
@@ -12153,11 +14901,15 @@ struct SrcList {
int addrFillSub; /* Address of subroutine to manifest a subquery */
int regReturn; /* Register holding return address of addrFillSub */
int regResult; /* Registers holding results of a co-routine */
- u8 jointype; /* Type of join between this able and the previous */
- unsigned notIndexed :1; /* True if there is a NOT INDEXED clause */
- unsigned isCorrelated :1; /* True if sub-query is correlated */
- unsigned viaCoroutine :1; /* Implemented as a co-routine */
- unsigned isRecursive :1; /* True for recursive reference in WITH */
+ struct {
+ u8 jointype; /* Type of join between this able and the previous */
+ unsigned notIndexed :1; /* True if there is a NOT INDEXED clause */
+ unsigned isIndexedBy :1; /* True if there is an INDEXED BY clause */
+ unsigned isTabFunc :1; /* True if table-valued-function syntax */
+ unsigned isCorrelated :1; /* True if sub-query is correlated */
+ unsigned viaCoroutine :1; /* Implemented as a co-routine */
+ unsigned isRecursive :1; /* True for recursive reference in WITH */
+ } fg;
#ifndef SQLITE_OMIT_EXPLAIN
u8 iSelectId; /* If pSelect!=0, the id of the sub-select in EQP */
#endif
@@ -12165,8 +14917,11 @@ struct SrcList {
Expr *pOn; /* The ON clause of a join */
IdList *pUsing; /* The USING clause of a join */
Bitmask colUsed; /* Bit N (1<<N) set if column N of pTab is used */
- char *zIndex; /* Identifier from "INDEXED BY <zIndex>" clause */
- Index *pIndex; /* Index structure corresponding to zIndex, if any */
+ union {
+ char *zIndexedBy; /* Identifier from "INDEXED BY <zIndex>" clause */
+ ExprList *pFuncArg; /* Arguments to table-valued-function */
+ } u1;
+ Index *pIBIndex; /* Index structure corresponding to u1.zIndexedBy */
} a[1]; /* One entry for each identifier on the list */
};
@@ -12185,6 +14940,9 @@ struct SrcList {
/*
** Flags appropriate for the wctrlFlags parameter of sqlite3WhereBegin()
** and the WhereInfo.wctrlFlags member.
+**
+** Value constraints (enforced via assert()):
+** WHERE_USE_LIMIT == SF_FixedLimit
*/
#define WHERE_ORDERBY_NORMAL 0x0000 /* No-op */
#define WHERE_ORDERBY_MIN 0x0001 /* ORDER BY processing for min() func */
@@ -12200,6 +14958,9 @@ struct SrcList {
#define WHERE_WANT_DISTINCT 0x0400 /* All output needs to be distinct */
#define WHERE_SORTBYGROUP 0x0800 /* Support sqlite3WhereIsSorted() */
#define WHERE_REOPEN_IDX 0x1000 /* Try to use OP_ReopenIdx */
+#define WHERE_ONEPASS_MULTIROW 0x2000 /* ONEPASS is ok with multiple rows */
+#define WHERE_USE_LIMIT 0x4000 /* There is a constant LIMIT clause */
+#define WHERE_SEEK_TABLE 0x8000 /* Do not defer seeks on main table */
/* Allowed return values from sqlite3WhereIsDistinct()
*/
@@ -12217,12 +14978,12 @@ struct SrcList {
** pEList corresponds to the result set of a SELECT and is NULL for
** other statements.
**
-** NameContexts can be nested. When resolving names, the inner-most
+** NameContexts can be nested. When resolving names, the inner-most
** context is searched first. If no match is found, the next outer
** context is checked. If there is still no match, the next context
** is checked. This process continues until either a match is found
** or all contexts are check. When a match is found, the nRef member of
-** the context containing the match is incremented.
+** the context containing the match is incremented.
**
** Each subquery gets a new NameContext. The pNext field points to the
** NameContext in the parent query. Thus the process of scanning the
@@ -12243,15 +15004,18 @@ struct NameContext {
/*
** Allowed values for the NameContext, ncFlags field.
**
-** Note: NC_MinMaxAgg must have the same value as SF_MinMaxAgg and
-** SQLITE_FUNC_MINMAX.
-**
+** Value constraints (all checked via assert()):
+** NC_HasAgg == SF_HasAgg
+** NC_MinMaxAgg == SF_MinMaxAgg == SQLITE_FUNC_MINMAX
+**
*/
#define NC_AllowAgg 0x0001 /* Aggregate functions are allowed here */
-#define NC_HasAgg 0x0002 /* One or more aggregate functions seen */
+#define NC_PartIdx 0x0002 /* True if resolving a partial index WHERE */
#define NC_IsCheck 0x0004 /* True if resolving names in a CHECK constraint */
#define NC_InAggFunc 0x0008 /* True if analyzing arguments to an agg func */
-#define NC_PartIdx 0x0010 /* True if resolving a partial index WHERE */
+#define NC_HasAgg 0x0010 /* One or more aggregate functions seen */
+#define NC_IdxExpr 0x0020 /* True if resolving columns of CREATE INDEX */
+#define NC_VarSelect 0x0040 /* A correlated subquery has been seen */
#define NC_MinMaxAgg 0x1000 /* min/max aggregates seen. See note above */
/*
@@ -12277,13 +15041,13 @@ struct NameContext {
struct Select {
ExprList *pEList; /* The fields of the result */
u8 op; /* One of: TK_UNION TK_ALL TK_INTERSECT TK_EXCEPT */
- u16 selFlags; /* Various SF_* values */
+ LogEst nSelectRow; /* Estimated number of result rows */
+ u32 selFlags; /* Various SF_* values */
int iLimit, iOffset; /* Memory registers holding LIMIT & OFFSET counters */
#if SELECTTRACE_ENABLED
char zSelName[12]; /* Symbolic name of this SELECT use for debugging */
#endif
int addrOpenEphm[2]; /* OP_OpenEphem opcodes related to this select */
- u64 nSelectRow; /* Estimated number of result rows */
SrcList *pSrc; /* The FROM clause */
Expr *pWhere; /* The WHERE clause */
ExprList *pGroupBy; /* The GROUP BY clause */
@@ -12299,21 +15063,30 @@ struct Select {
/*
** Allowed values for Select.selFlags. The "SF" prefix stands for
** "Select Flag".
-*/
-#define SF_Distinct 0x0001 /* Output should be DISTINCT */
-#define SF_Resolved 0x0002 /* Identifiers have been resolved */
-#define SF_Aggregate 0x0004 /* Contains aggregate functions */
-#define SF_UsesEphemeral 0x0008 /* Uses the OpenEphemeral opcode */
-#define SF_Expanded 0x0010 /* sqlite3SelectExpand() called on this */
-#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_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() */
+**
+** Value constraints (all checked via assert())
+** SF_HasAgg == NC_HasAgg
+** SF_MinMaxAgg == NC_MinMaxAgg == SQLITE_FUNC_MINMAX
+** SF_FixedLimit == WHERE_USE_LIMIT
+*/
+#define SF_Distinct 0x00001 /* Output should be DISTINCT */
+#define SF_All 0x00002 /* Includes the ALL keyword */
+#define SF_Resolved 0x00004 /* Identifiers have been resolved */
+#define SF_Aggregate 0x00008 /* Contains agg functions or a GROUP BY */
+#define SF_HasAgg 0x00010 /* Contains aggregate functions */
+#define SF_UsesEphemeral 0x00020 /* Uses the OpenEphemeral opcode */
+#define SF_Expanded 0x00040 /* sqlite3SelectExpand() called on this */
+#define SF_HasTypeInfo 0x00080 /* FROM subqueries have Table metadata */
+#define SF_Compound 0x00100 /* Part of a compound query */
+#define SF_Values 0x00200 /* Synthesized from VALUES clause */
+#define SF_MultiValue 0x00400 /* Single VALUES term with multiple rows */
+#define SF_NestedFrom 0x00800 /* Part of a parenthesized FROM clause */
+#define SF_MinMaxAgg 0x01000 /* Aggregate containing min() or max() */
+#define SF_Recursive 0x02000 /* The recursive part of a recursive CTE */
+#define SF_FixedLimit 0x04000 /* nSelectRow set by a constant LIMIT */
+#define SF_MaybeConvert 0x08000 /* Need convertCompoundSelectToSubquery() */
+#define SF_Converted 0x10000 /* By convertCompoundSelectToSubquery() */
+#define SF_IncludeHidden 0x20000 /* Include hidden columns in output */
/*
@@ -12321,7 +15094,7 @@ struct Select {
** by one of the following macros. The "SRT" prefix means "SELECT Result
** Type".
**
-** SRT_Union Store results as a key in a temporary index
+** SRT_Union Store results as a key in a temporary index
** identified by pDest->iSDParm.
**
** SRT_Except Remove results from the temporary index pDest->iSDParm.
@@ -12345,7 +15118,7 @@ struct Select {
** of the query. This destination implies "LIMIT 1".
**
** SRT_Set The result must be a single column. Store each
-** row of result as the key in table pDest->iSDParm.
+** row of result as the key in table pDest->iSDParm.
** Apply the affinity pDest->affSdst before storing
** results. Used to implement "IN (SELECT ...)".
**
@@ -12413,11 +15186,11 @@ struct SelectDest {
};
/*
-** During code generation of statements that do inserts into AUTOINCREMENT
+** During code generation of statements that do inserts into AUTOINCREMENT
** tables, the following information is attached to the Table.u.autoInc.p
** pointer of each autoincrement table to record some side information that
** the code generator needs. We have to keep per-table autoincrement
-** information in case inserts are down within triggers. Triggers do not
+** information in case inserts are done within triggers. Triggers do not
** normally coordinate their activities, but we do need to coordinate the
** loading and saving of autoincrement information.
*/
@@ -12436,7 +15209,7 @@ struct AutoincInfo {
#endif
/*
-** At least one instance of the following structure is created for each
+** At least one instance of the following structure is created for each
** trigger that may be fired while parsing an INSERT, UPDATE or DELETE
** statement. All such objects are stored in the linked list headed at
** Parse.pTriggerPrg and deleted once statement compilation has been
@@ -12449,7 +15222,7 @@ struct AutoincInfo {
** values for both pTrigger and orconf.
**
** The TriggerPrg.aColmask[0] variable is set to a mask of old.* columns
-** accessed (or set to 0 for triggers fired as a result of INSERT
+** accessed (or set to 0 for triggers fired as a result of INSERT
** statements). Similarly, the TriggerPrg.aColmask[1] variable is set to
** a mask of new.* columns used by the program.
*/
@@ -12490,7 +15263,7 @@ struct TriggerPrg {
** is constant but the second part is reset at the beginning and end of
** each recursion.
**
-** The nTableLock and aTableLock variables are only used if the shared-cache
+** The nTableLock and aTableLock variables are only used if the shared-cache
** feature is enabled (if sqlite3Tsd()->useSharedData is true). They are
** used to store the set of table-locks required by the statement being
** compiled. Function sqlite3TableLock() is used to add entries to the
@@ -12509,6 +15282,8 @@ struct Parse {
u8 mayAbort; /* True if statement may throw an ABORT exception */
u8 hasCompound; /* Need to invoke convertCompoundSelectToSubquery() */
u8 okConstFactor; /* OK to factor out constants */
+ u8 disableLookaside; /* Number of times lookaside has been disabled */
+ u8 nColCache; /* Number of entries in aColCache[] */
int aTempReg[8]; /* Holding area for temporary registers */
int nRangeReg; /* Size of the temporary register block */
int iRangeReg; /* First register in temporary register block */
@@ -12518,9 +15293,10 @@ struct Parse {
int nSet; /* Number of sets used so far */
int nOnce; /* Number of OP_Once instructions so far */
int nOpAlloc; /* Number of slots allocated for Vdbe.aOp[] */
+ int szOpAlloc; /* Bytes of memory space allocated for Vdbe.aOp[] */
int iFixedOp; /* Never back out opcodes iFixedOp-1 or earlier */
int ckBase; /* Base register of data during check constraints */
- int iPartIdxTab; /* Table corresponding to a partial index */
+ int iSelfTab; /* Table of an index whose exprs are being coded */
int iCacheLevel; /* ColCache valid when aColCache[].iLevel<=iCacheLevel */
int iCacheCnt; /* Counter used to generate aColCache[].lru values */
int nLabel; /* Number of labels used */
@@ -12555,7 +15331,6 @@ struct Parse {
Parse *pToplevel; /* Parse structure for main program (or NULL) */
Table *pTriggerTab; /* Table triggers are being coded for */
int addrCrTab; /* Address of OP_CreateTable opcode on CREATE TABLE */
- int addrSkipPK; /* Address of instruction to skip PRIMARY KEY index */
u32 nQueryLoop; /* Est number of iterations of a query (10*log2(N)) */
u32 oldmask; /* Mask of old.* columns referenced */
u32 newmask; /* Mask of new.* columns referenced */
@@ -12570,10 +15345,9 @@ struct Parse {
** in the recursive region.
************************************************************************/
- int nVar; /* Number of '?' variables seen in the SQL so far */
+ ynVar nVar; /* Number of '?' variables seen in the SQL so far */
int nzVar; /* Number of available slots in azVar[] */
u8 iPkSortOrder; /* ASC or DESC for INTEGER PRIMARY KEY */
- u8 bFreeWith; /* True if pWith should be freed with parser */
u8 explain; /* True if the EXPLAIN flag is found on the query */
#ifndef SQLITE_OMIT_VIRTUALTABLE
u8 declareVtab; /* True if inside sqlite3_declare_vtab() */
@@ -12600,6 +15374,7 @@ struct Parse {
Table *pZombieTab; /* List of Table objects to delete after code gen */
TriggerPrg *pTriggerPrg; /* Linked list of coded triggers */
With *pWith; /* Current WITH clause, or NULL */
+ With *pWithToFree; /* Free this WITH object at the end of the parse */
};
/*
@@ -12622,26 +15397,42 @@ struct AuthContext {
/*
** Bitfield flags for P5 value in various opcodes.
-*/
-#define OPFLAG_NCHANGE 0x01 /* Set to update db->nChange */
+**
+** Value constraints (enforced via assert()):
+** OPFLAG_LENGTHARG == SQLITE_FUNC_LENGTH
+** OPFLAG_TYPEOFARG == SQLITE_FUNC_TYPEOF
+** OPFLAG_BULKCSR == BTREE_BULKLOAD
+** OPFLAG_SEEKEQ == BTREE_SEEK_EQ
+** OPFLAG_FORDELETE == BTREE_FORDELETE
+** OPFLAG_SAVEPOSITION == BTREE_SAVEPOSITION
+** OPFLAG_AUXDELETE == BTREE_AUXDELETE
+*/
+#define OPFLAG_NCHANGE 0x01 /* OP_Insert: Set to update db->nChange */
+ /* Also used in P2 (not P5) of OP_Delete */
#define OPFLAG_EPHEM 0x01 /* OP_Column: Ephemeral output is ok */
#define OPFLAG_LASTROWID 0x02 /* Set to update db->lastRowid */
#define OPFLAG_ISUPDATE 0x04 /* This OP_Insert is an sql UPDATE */
#define OPFLAG_APPEND 0x08 /* This is likely to be an append */
#define OPFLAG_USESEEKRESULT 0x10 /* Try to avoid a seek in BtreeInsert() */
+#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
+#define OPFLAG_ISNOOP 0x40 /* OP_Delete does pre-update-hook only */
+#endif
#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_SEEKEQ 0x02 /* OP_Open** cursor uses EQ seek only */
-#define OPFLAG_P2ISREG 0x04 /* P2 to OP_Open** is a register number */
+#define OPFLAG_FORDELETE 0x08 /* OP_Open should use BTREE_FORDELETE */
+#define OPFLAG_P2ISREG 0x10 /* P2 to OP_Open** is a register number */
#define OPFLAG_PERMUTE 0x01 /* OP_Compare: use the permutation */
+#define OPFLAG_SAVEPOSITION 0x02 /* OP_Delete: keep cursor position */
+#define OPFLAG_AUXDELETE 0x04 /* OP_Delete: index in a DELETE op */
/*
* Each trigger present in the database schema is stored as an instance of
- * struct Trigger.
+ * struct Trigger.
*
* Pointers to instances of struct Trigger are stored in two ways.
- * 1. In the "trigHash" hash table (part of the sqlite3* that represents the
+ * 1. In the "trigHash" hash table (part of the sqlite3* that represents the
* database). This allows Trigger structures to be retrieved by name.
* 2. All triggers associated with a single table form a linked list, using the
* pNext member of struct Trigger. A pointer to the first element of the
@@ -12667,7 +15458,7 @@ struct Trigger {
/*
** A trigger is either a BEFORE or an AFTER trigger. The following constants
-** determine which.
+** determine which.
**
** If there are multiple triggers, you might of some BEFORE and some AFTER.
** In that cases, the constants below can be ORed together.
@@ -12677,15 +15468,15 @@ struct Trigger {
/*
* An instance of struct TriggerStep is used to store a single SQL statement
- * that is a part of a trigger-program.
+ * that is a part of a trigger-program.
*
* Instances of struct TriggerStep are stored in a singly linked list (linked
- * using the "pNext" member) referenced by the "step_list" member of the
+ * using the "pNext" member) referenced by the "step_list" member of the
* associated struct Trigger instance. The first element of the linked list is
* the first step of the trigger-program.
- *
+ *
* The "op" member indicates whether this is a "DELETE", "INSERT", "UPDATE" or
- * "SELECT" statement. The meanings of the other members is determined by the
+ * "SELECT" statement. The meanings of the other members is determined by the
* value of "op" as follows:
*
* (op == TK_INSERT)
@@ -12695,7 +15486,7 @@ struct Trigger {
* 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 ...
+ * pIdList -> If this is an INSERT INTO ... (<column-names>) VALUES ...
* statement, then this stores the column-names to be
* inserted into.
*
@@ -12703,7 +15494,7 @@ struct Trigger {
* 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)
* zTarget -> Dequoted name of the table to update.
* pWhere -> The WHERE clause of the UPDATE statement if one is specified.
@@ -12711,7 +15502,7 @@ struct Trigger {
* pExprList -> A list of the columns to update and the expressions to update
* them to. See sqlite3Update() documentation of "pChanges"
* argument.
- *
+ *
*/
struct TriggerStep {
u8 op; /* One of TK_DELETE, TK_UPDATE, TK_INSERT, TK_SELECT */
@@ -12729,7 +15520,7 @@ struct TriggerStep {
/*
** The following structure contains information used by the sqliteFix...
** routines as they walk the parse tree to make database references
-** explicit.
+** explicit.
*/
typedef struct DbFixer DbFixer;
struct DbFixer {
@@ -12749,13 +15540,20 @@ struct StrAccum {
sqlite3 *db; /* Optional database for lookaside. Can be NULL */
char *zBase; /* A base allocation. Not from malloc. */
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 allocation. 0 for no malloc usage */
+ u32 nChar; /* Length of the string so far */
+ u32 nAlloc; /* Amount of space allocated in zText */
+ u32 mxAlloc; /* Maximum allowed allocation. 0 for no malloc usage */
u8 accError; /* STRACCUM_NOMEM or STRACCUM_TOOBIG */
+ u8 printfFlags; /* SQLITE_PRINTF flags below */
};
#define STRACCUM_NOMEM 1
#define STRACCUM_TOOBIG 2
+#define SQLITE_PRINTF_INTERNAL 0x01 /* Internal-use-only converters allowed */
+#define SQLITE_PRINTF_SQLFUNC 0x02 /* SQL function arguments to VXPrintf */
+#define SQLITE_PRINTF_MALLOCED 0x04 /* True if xText is allocated space */
+
+#define isMalloced(X) (((X)->printfFlags & SQLITE_PRINTF_MALLOCED)!=0)
+
/*
** A pointer to this structure is used to communicate information
@@ -12783,6 +15581,7 @@ struct Sqlite3Config {
int neverCorrupt; /* Database is always well-formed */
int szLookaside; /* Default lookaside buffer size */
int nLookaside; /* Default lookaside buffer count */
+ int nStmtSpill; /* Stmt-journal spill-to-disk threshold */
sqlite3_mem_methods m; /* Low-level memory allocation interface */
sqlite3_mutex_methods mutex; /* Low-level mutex interface */
sqlite3_pcache_methods2 pcache2; /* Low-level page-cache interface */
@@ -12850,10 +15649,10 @@ struct Sqlite3Config {
** Context pointer passed down through the tree-walk.
*/
struct Walker {
+ Parse *pParse; /* Parser context. */
int (*xExprCallback)(Walker*, Expr*); /* Callback for expressions */
int (*xSelectCallback)(Walker*,Select*); /* Callback for SELECTs */
void (*xSelectCallback2)(Walker*,Select*);/* Second callback for SELECTs */
- Parse *pParse; /* Parser context. */
int walkerDepth; /* Number of subqueries */
u8 eCode; /* A small processing code */
union { /* Extra data for callback */
@@ -12862,6 +15661,8 @@ struct Walker {
int iCur; /* A cursor number */
SrcList *pSrcList; /* FROM clause */
struct SrcCount *pSrcCount; /* Counting column references */
+ struct CCurHint *pCCurHint; /* Used by codeCursorHint() */
+ int *aiCol; /* array of column indexes */
} u;
};
@@ -12871,6 +15672,7 @@ SQLITE_PRIVATE int sqlite3WalkExprList(Walker*, ExprList*);
SQLITE_PRIVATE int sqlite3WalkSelect(Walker*, Select*);
SQLITE_PRIVATE int sqlite3WalkSelectExpr(Walker*, Select*);
SQLITE_PRIVATE int sqlite3WalkSelectFrom(Walker*, Select*);
+SQLITE_PRIVATE int sqlite3ExprWalkNoop(Walker*, Expr*);
/*
** Return code from the parse-tree walking primitives and their
@@ -12891,7 +15693,7 @@ struct With {
char *zName; /* Name of this CTE */
ExprList *pCols; /* List of explicit column names, or NULL */
Select *pSelect; /* The definition of this CTE */
- const char *zErr; /* Error message for circular references */
+ const char *zCteErr; /* Error message for circular references */
} a[1];
};
@@ -12929,7 +15731,23 @@ SQLITE_PRIVATE int sqlite3CantopenError(int);
#define SQLITE_CORRUPT_BKPT sqlite3CorruptError(__LINE__)
#define SQLITE_MISUSE_BKPT sqlite3MisuseError(__LINE__)
#define SQLITE_CANTOPEN_BKPT sqlite3CantopenError(__LINE__)
+#ifdef SQLITE_DEBUG
+SQLITE_PRIVATE int sqlite3NomemError(int);
+SQLITE_PRIVATE int sqlite3IoerrnomemError(int);
+# define SQLITE_NOMEM_BKPT sqlite3NomemError(__LINE__)
+# define SQLITE_IOERR_NOMEM_BKPT sqlite3IoerrnomemError(__LINE__)
+#else
+# define SQLITE_NOMEM_BKPT SQLITE_NOMEM
+# define SQLITE_IOERR_NOMEM_BKPT SQLITE_IOERR_NOMEM
+#endif
+/*
+** FTS3 and FTS4 both require virtual table support
+*/
+#if defined(SQLITE_OMIT_VIRTUALTABLE)
+# undef SQLITE_ENABLE_FTS3
+# undef SQLITE_ENABLE_FTS4
+#endif
/*
** FTS4 is really an extension for FTS3. It is enabled using the
@@ -12962,6 +15780,7 @@ SQLITE_PRIVATE int sqlite3CantopenError(int);
# define sqlite3Isdigit(x) (sqlite3CtypeMap[(unsigned char)(x)]&0x04)
# define sqlite3Isxdigit(x) (sqlite3CtypeMap[(unsigned char)(x)]&0x08)
# define sqlite3Tolower(x) (sqlite3UpperToLower[(unsigned char)(x)])
+# define sqlite3Isquote(x) (sqlite3CtypeMap[(unsigned char)(x)]&0x80)
#else
# define sqlite3Toupper(x) toupper((unsigned char)(x))
# define sqlite3Isspace(x) isspace((unsigned char)(x))
@@ -12970,14 +15789,18 @@ SQLITE_PRIVATE int sqlite3CantopenError(int);
# define sqlite3Isdigit(x) isdigit((unsigned char)(x))
# define sqlite3Isxdigit(x) isxdigit((unsigned char)(x))
# define sqlite3Tolower(x) tolower((unsigned char)(x))
+# define sqlite3Isquote(x) ((x)=='"'||(x)=='\''||(x)=='['||(x)=='`')
#endif
+#ifndef SQLITE_OMIT_COMPILEOPTION_DIAGS
SQLITE_PRIVATE int sqlite3IsIdChar(u8);
+#endif
/*
** Internal function prototypes
*/
-#define sqlite3StrICmp sqlite3_stricmp
+SQLITE_PRIVATE int sqlite3StrICmp(const char*,const char*);
SQLITE_PRIVATE int sqlite3Strlen30(const char*);
+SQLITE_PRIVATE char *sqlite3ColumnType(Column*,char*);
#define sqlite3StrNICmp sqlite3_strnicmp
SQLITE_PRIVATE int sqlite3MallocInit(void);
@@ -12986,6 +15809,7 @@ SQLITE_PRIVATE void *sqlite3Malloc(u64);
SQLITE_PRIVATE void *sqlite3MallocZero(u64);
SQLITE_PRIVATE void *sqlite3DbMallocZero(sqlite3*, u64);
SQLITE_PRIVATE void *sqlite3DbMallocRaw(sqlite3*, u64);
+SQLITE_PRIVATE void *sqlite3DbMallocRawNN(sqlite3*, u64);
SQLITE_PRIVATE char *sqlite3DbStrDup(sqlite3*,const char*);
SQLITE_PRIVATE char *sqlite3DbStrNDup(sqlite3*,const char*, u64);
SQLITE_PRIVATE void *sqlite3Realloc(void*, u64);
@@ -12999,7 +15823,9 @@ SQLITE_PRIVATE void sqlite3ScratchFree(void*);
SQLITE_PRIVATE void *sqlite3PageMalloc(int);
SQLITE_PRIVATE void sqlite3PageFree(void*);
SQLITE_PRIVATE void sqlite3MemSetDefault(void);
+#ifndef SQLITE_OMIT_BUILTIN_TEST
SQLITE_PRIVATE void sqlite3BenignMallocHooks(void (*)(void), void (*)(void));
+#endif
SQLITE_PRIVATE int sqlite3HeapNearlyFull(void);
/*
@@ -13013,7 +15839,7 @@ SQLITE_PRIVATE int sqlite3HeapNearlyFull(void);
#ifdef SQLITE_USE_ALLOCA
# define sqlite3StackAllocRaw(D,N) alloca(N)
# define sqlite3StackAllocZero(D,N) memset(alloca(N), 0, N)
-# define sqlite3StackFree(D,P)
+# define sqlite3StackFree(D,P)
#else
# define sqlite3StackAllocRaw(D,N) sqlite3DbMallocRaw(D,N)
# define sqlite3StackAllocZero(D,N) sqlite3DbMallocZero(D,N)
@@ -13035,11 +15861,16 @@ SQLITE_PRIVATE sqlite3_mutex *sqlite3MutexAlloc(int);
SQLITE_PRIVATE int sqlite3MutexInit(void);
SQLITE_PRIVATE int sqlite3MutexEnd(void);
#endif
+#if !defined(SQLITE_MUTEX_OMIT) && !defined(SQLITE_MUTEX_NOOP)
+SQLITE_PRIVATE void sqlite3MemoryBarrier(void);
+#else
+# define sqlite3MemoryBarrier()
+#endif
SQLITE_PRIVATE sqlite3_int64 sqlite3StatusValue(int);
SQLITE_PRIVATE void sqlite3StatusUp(int, int);
SQLITE_PRIVATE void sqlite3StatusDown(int, int);
-SQLITE_PRIVATE void sqlite3StatusSet(int, int);
+SQLITE_PRIVATE void sqlite3StatusHighwater(int, int);
/* Access to mutexes used by sqlite3_status() */
SQLITE_PRIVATE sqlite3_mutex *sqlite3Pcache1Mutex(void);
@@ -13061,13 +15892,10 @@ struct PrintfArguments {
sqlite3_value **apArg; /* The argument values */
};
-#define SQLITE_PRINTF_INTERNAL 0x01
-#define SQLITE_PRINTF_SQLFUNC 0x02
-SQLITE_PRIVATE void sqlite3VXPrintf(StrAccum*, u32, const char*, va_list);
-SQLITE_PRIVATE void sqlite3XPrintf(StrAccum*, u32, const char*, ...);
+SQLITE_PRIVATE void sqlite3VXPrintf(StrAccum*, const char*, va_list);
+SQLITE_PRIVATE void sqlite3XPrintf(StrAccum*, 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_DEBUG) || defined(SQLITE_HAVE_OS_TRACE)
SQLITE_PRIVATE void sqlite3DebugPrintf(const char*, ...);
#endif
@@ -13076,19 +15904,17 @@ SQLITE_PRIVATE void *sqlite3TestTextToPtr(const char*);
#endif
#if defined(SQLITE_DEBUG)
-SQLITE_PRIVATE TreeView *sqlite3TreeViewPush(TreeView*,u8);
-SQLITE_PRIVATE void sqlite3TreeViewPop(TreeView*);
-SQLITE_PRIVATE void sqlite3TreeViewLine(TreeView*, const char*, ...);
-SQLITE_PRIVATE void sqlite3TreeViewItem(TreeView*, const char*, u8);
SQLITE_PRIVATE void sqlite3TreeViewExpr(TreeView*, const Expr*, u8);
SQLITE_PRIVATE void sqlite3TreeViewExprList(TreeView*, const ExprList*, u8, const char*);
SQLITE_PRIVATE void sqlite3TreeViewSelect(TreeView*, const Select*, u8);
+SQLITE_PRIVATE void sqlite3TreeViewWith(TreeView*, const With*, u8);
#endif
-SQLITE_PRIVATE void sqlite3SetString(char **, sqlite3*, const char*, ...);
+SQLITE_PRIVATE void sqlite3SetString(char **, sqlite3*, const char*);
SQLITE_PRIVATE void sqlite3ErrorMsg(Parse*, const char*, ...);
-SQLITE_PRIVATE int sqlite3Dequote(char*);
+SQLITE_PRIVATE void sqlite3Dequote(char*);
+SQLITE_PRIVATE void sqlite3TokenInit(Token*,char*);
SQLITE_PRIVATE int sqlite3KeywordCode(const unsigned char*, int);
SQLITE_PRIVATE int sqlite3RunParser(Parse*, const char*, char **);
SQLITE_PRIVATE void sqlite3FinishCoding(Parse*);
@@ -13097,15 +15923,20 @@ SQLITE_PRIVATE void sqlite3ReleaseTempReg(Parse*,int);
SQLITE_PRIVATE int sqlite3GetTempRange(Parse*,int);
SQLITE_PRIVATE void sqlite3ReleaseTempRange(Parse*,int,int);
SQLITE_PRIVATE void sqlite3ClearTempRegCache(Parse*);
+#ifdef SQLITE_DEBUG
+SQLITE_PRIVATE int sqlite3NoTempsInRange(Parse*,int,int);
+#endif
SQLITE_PRIVATE Expr *sqlite3ExprAlloc(sqlite3*,int,const Token*,int);
SQLITE_PRIVATE Expr *sqlite3Expr(sqlite3*,int,const char*);
SQLITE_PRIVATE void sqlite3ExprAttachSubtrees(sqlite3*,Expr*,Expr*,Expr*);
SQLITE_PRIVATE Expr *sqlite3PExpr(Parse*, int, Expr*, Expr*, const Token*);
+SQLITE_PRIVATE void sqlite3PExprAddSelect(Parse*, Expr*, Select*);
SQLITE_PRIVATE Expr *sqlite3ExprAnd(sqlite3*,Expr*, Expr*);
SQLITE_PRIVATE Expr *sqlite3ExprFunction(Parse*,ExprList*, Token*);
SQLITE_PRIVATE void sqlite3ExprAssignVarNumber(Parse*, Expr*);
SQLITE_PRIVATE void sqlite3ExprDelete(sqlite3*, Expr*);
SQLITE_PRIVATE ExprList *sqlite3ExprListAppend(Parse*,ExprList*,Expr*);
+SQLITE_PRIVATE void sqlite3ExprListSetSortOrder(ExprList*,int);
SQLITE_PRIVATE void sqlite3ExprListSetName(Parse*,ExprList*,Token*,int);
SQLITE_PRIVATE void sqlite3ExprListSetSpan(Parse*,ExprList*,ExprSpan*);
SQLITE_PRIVATE void sqlite3ExprListDelete(sqlite3*, ExprList*);
@@ -13116,18 +15947,24 @@ SQLITE_PRIVATE void sqlite3Pragma(Parse*,Token*,Token*,Token*,int);
SQLITE_PRIVATE void sqlite3ResetAllSchemasOfConnection(sqlite3*);
SQLITE_PRIVATE void sqlite3ResetOneSchema(sqlite3*,int);
SQLITE_PRIVATE void sqlite3CollapseDatabaseArray(sqlite3*);
-SQLITE_PRIVATE void sqlite3BeginParse(Parse*,int);
SQLITE_PRIVATE void sqlite3CommitInternalChanges(sqlite3*);
+SQLITE_PRIVATE void sqlite3DeleteColumnNames(sqlite3*,Table*);
+SQLITE_PRIVATE int sqlite3ColumnsFromExprList(Parse*,ExprList*,i16*,Column**);
+SQLITE_PRIVATE void sqlite3SelectAddColumnTypeAndCollation(Parse*,Table*,Select*);
SQLITE_PRIVATE Table *sqlite3ResultSetOfSelect(Parse*,Select*);
SQLITE_PRIVATE void sqlite3OpenMasterTable(Parse *, int);
SQLITE_PRIVATE Index *sqlite3PrimaryKeyIndex(Table*);
SQLITE_PRIVATE i16 sqlite3ColumnOfIndex(Index*, i16);
SQLITE_PRIVATE void sqlite3StartTable(Parse*,Token*,Token*,int,int,int,int);
-SQLITE_PRIVATE void sqlite3AddColumn(Parse*,Token*);
+#if SQLITE_ENABLE_HIDDEN_COLUMNS
+SQLITE_PRIVATE void sqlite3ColumnPropertiesFromName(Table*, Column*);
+#else
+# define sqlite3ColumnPropertiesFromName(T,C) /* no-op */
+#endif
+SQLITE_PRIVATE void sqlite3AddColumn(Parse*,Token*,Token*);
SQLITE_PRIVATE void sqlite3AddNotNull(Parse*, int);
SQLITE_PRIVATE void sqlite3AddPrimaryKey(Parse*, ExprList*, int, int, int);
SQLITE_PRIVATE void sqlite3AddCheckConstraint(Parse*, Expr*);
-SQLITE_PRIVATE void sqlite3AddColumnType(Parse*,Token*);
SQLITE_PRIVATE void sqlite3AddDefaultValue(Parse*,ExprSpan*);
SQLITE_PRIVATE void sqlite3AddCollateType(Parse*, Token*);
SQLITE_PRIVATE void sqlite3EndTable(Parse*,Token*,Token*,u8,Select*);
@@ -13144,11 +15981,14 @@ SQLITE_PRIVATE int sqlite3FaultSim(int);
SQLITE_PRIVATE Bitvec *sqlite3BitvecCreate(u32);
SQLITE_PRIVATE int sqlite3BitvecTest(Bitvec*, u32);
+SQLITE_PRIVATE int sqlite3BitvecTestNotNull(Bitvec*, u32);
SQLITE_PRIVATE int sqlite3BitvecSet(Bitvec*, u32);
SQLITE_PRIVATE void sqlite3BitvecClear(Bitvec*, u32, void*);
SQLITE_PRIVATE void sqlite3BitvecDestroy(Bitvec*);
SQLITE_PRIVATE u32 sqlite3BitvecSize(Bitvec*);
+#ifndef SQLITE_OMIT_BUILTIN_TEST
SQLITE_PRIVATE int sqlite3BitvecBuiltinTest(int,int*);
+#endif
SQLITE_PRIVATE RowSet *sqlite3RowSetInit(sqlite3*, void*, unsigned int);
SQLITE_PRIVATE void sqlite3RowSetClear(RowSet*);
@@ -13156,7 +15996,7 @@ SQLITE_PRIVATE void sqlite3RowSetInsert(RowSet*, i64);
SQLITE_PRIVATE int sqlite3RowSetTest(RowSet*, int iBatch, i64);
SQLITE_PRIVATE int sqlite3RowSetNext(RowSet*, i64*);
-SQLITE_PRIVATE void sqlite3CreateView(Parse*,Token*,Token*,Token*,Select*,int,int);
+SQLITE_PRIVATE void sqlite3CreateView(Parse*,Token*,Token*,Token*,ExprList*,Select*,int,int);
#if !defined(SQLITE_OMIT_VIEW) || !defined(SQLITE_OMIT_VIRTUALTABLE)
SQLITE_PRIVATE int sqlite3ViewGetColumnNames(Parse*,Table*);
@@ -13186,6 +16026,7 @@ SQLITE_PRIVATE SrcList *sqlite3SrcListAppend(sqlite3*, SrcList*, Token*, Token*)
SQLITE_PRIVATE SrcList *sqlite3SrcListAppendFromTerm(Parse*, SrcList*, Token*, Token*,
Token*, Select*, Expr*, IdList*);
SQLITE_PRIVATE void sqlite3SrcListIndexedBy(Parse *, SrcList *, Token *);
+SQLITE_PRIVATE void sqlite3SrcListFuncArgs(Parse*, SrcList*, ExprList*);
SQLITE_PRIVATE int sqlite3IndexedByLookup(Parse *, struct SrcList_item *);
SQLITE_PRIVATE void sqlite3SrcListShiftJoinType(SrcList*);
SQLITE_PRIVATE void sqlite3SrcListAssignCursors(Parse*, SrcList*);
@@ -13197,7 +16038,7 @@ SQLITE_PRIVATE Index *sqlite3CreateIndex(Parse*,Token*,Token*,SrcList*,ExprList*
SQLITE_PRIVATE void sqlite3DropIndex(Parse*, SrcList*, int);
SQLITE_PRIVATE int sqlite3Select(Parse*, Select*, SelectDest*);
SQLITE_PRIVATE Select *sqlite3SelectNew(Parse*,ExprList*,SrcList*,Expr*,ExprList*,
- Expr*,ExprList*,u16,Expr*,Expr*);
+ Expr*,ExprList*,u32,Expr*,Expr*);
SQLITE_PRIVATE void sqlite3SelectDelete(sqlite3*, Select*);
SQLITE_PRIVATE Table *sqlite3SrcListLookup(Parse*, SrcList*);
SQLITE_PRIVATE int sqlite3IsReadOnly(Parse*, Table*, int);
@@ -13209,14 +16050,19 @@ SQLITE_PRIVATE void sqlite3DeleteFrom(Parse*, SrcList*, Expr*);
SQLITE_PRIVATE void sqlite3Update(Parse*, SrcList*, ExprList*, Expr*, int);
SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin(Parse*,SrcList*,Expr*,ExprList*,ExprList*,u16,int);
SQLITE_PRIVATE void sqlite3WhereEnd(WhereInfo*);
-SQLITE_PRIVATE u64 sqlite3WhereOutputRowCount(WhereInfo*);
+SQLITE_PRIVATE LogEst sqlite3WhereOutputRowCount(WhereInfo*);
SQLITE_PRIVATE int sqlite3WhereIsDistinct(WhereInfo*);
SQLITE_PRIVATE int sqlite3WhereIsOrdered(WhereInfo*);
SQLITE_PRIVATE int sqlite3WhereIsSorted(WhereInfo*);
SQLITE_PRIVATE int sqlite3WhereContinueLabel(WhereInfo*);
SQLITE_PRIVATE int sqlite3WhereBreakLabel(WhereInfo*);
SQLITE_PRIVATE int sqlite3WhereOkOnePass(WhereInfo*, int*);
+#define ONEPASS_OFF 0 /* Use of ONEPASS not allowed */
+#define ONEPASS_SINGLE 1 /* ONEPASS valid for a single row update */
+#define ONEPASS_MULTI 2 /* ONEPASS is valid for multiple rows */
+SQLITE_PRIVATE void sqlite3ExprCodeLoadIndexColumn(Parse*, Index*, int, int, int);
SQLITE_PRIVATE int sqlite3ExprCodeGetColumn(Parse*, Table*, int, int, int, u8);
+SQLITE_PRIVATE void sqlite3ExprCodeGetColumnToReg(Parse*, Table*, int, int, int);
SQLITE_PRIVATE void sqlite3ExprCodeGetColumnOfTable(Vdbe*, Table*, int, int, int);
SQLITE_PRIVATE void sqlite3ExprCodeMove(Parse*, int, int, int);
SQLITE_PRIVATE void sqlite3ExprCacheStore(Parse*, int, int, int);
@@ -13226,16 +16072,19 @@ SQLITE_PRIVATE void sqlite3ExprCacheRemove(Parse*, int, int);
SQLITE_PRIVATE void sqlite3ExprCacheClear(Parse*);
SQLITE_PRIVATE void sqlite3ExprCacheAffinityChange(Parse*, int, int);
SQLITE_PRIVATE void sqlite3ExprCode(Parse*, Expr*, int);
+SQLITE_PRIVATE void sqlite3ExprCodeCopy(Parse*, Expr*, int);
SQLITE_PRIVATE void sqlite3ExprCodeFactorable(Parse*, Expr*, int);
SQLITE_PRIVATE void sqlite3ExprCodeAtInit(Parse*, Expr*, int, u8);
SQLITE_PRIVATE int sqlite3ExprCodeTemp(Parse*, Expr*, int*);
SQLITE_PRIVATE int sqlite3ExprCodeTarget(Parse*, Expr*, int);
SQLITE_PRIVATE void sqlite3ExprCodeAndCache(Parse*, Expr*, int);
-SQLITE_PRIVATE int sqlite3ExprCodeExprList(Parse*, ExprList*, int, u8);
+SQLITE_PRIVATE int sqlite3ExprCodeExprList(Parse*, ExprList*, int, int, u8);
#define SQLITE_ECEL_DUP 0x01 /* Deep, not shallow copies */
#define SQLITE_ECEL_FACTOR 0x02 /* Factor out constant terms */
+#define SQLITE_ECEL_REF 0x04 /* Use ExprList.u.x.iOrderByCol */
SQLITE_PRIVATE void sqlite3ExprIfTrue(Parse*, Expr*, int, int);
SQLITE_PRIVATE void sqlite3ExprIfFalse(Parse*, Expr*, int, int);
+SQLITE_PRIVATE void sqlite3ExprIfFalseDup(Parse*, Expr*, int, int);
SQLITE_PRIVATE Table *sqlite3FindTable(sqlite3*,const char*, const char*);
SQLITE_PRIVATE Table *sqlite3LocateTable(Parse*,int isView,const char*, const char*);
SQLITE_PRIVATE Table *sqlite3LocateTableItem(Parse*,int isView,struct SrcList_item *);
@@ -13252,8 +16101,10 @@ SQLITE_PRIVATE void sqlite3ExprAnalyzeAggregates(NameContext*, Expr*);
SQLITE_PRIVATE void sqlite3ExprAnalyzeAggList(NameContext*,ExprList*);
SQLITE_PRIVATE int sqlite3FunctionUsesThisSrc(Expr*, SrcList*);
SQLITE_PRIVATE Vdbe *sqlite3GetVdbe(Parse*);
+#ifndef SQLITE_OMIT_BUILTIN_TEST
SQLITE_PRIVATE void sqlite3PrngSaveState(void);
SQLITE_PRIVATE void sqlite3PrngRestoreState(void);
+#endif
SQLITE_PRIVATE void sqlite3RollbackAll(sqlite3*,int);
SQLITE_PRIVATE void sqlite3CodeVerifySchema(Parse*, int);
SQLITE_PRIVATE void sqlite3CodeVerifyNamedSchema(Parse*, const char *zDb);
@@ -13267,18 +16118,22 @@ SQLITE_PRIVATE int sqlite3ExprIsConstant(Expr*);
SQLITE_PRIVATE int sqlite3ExprIsConstantNotJoin(Expr*);
SQLITE_PRIVATE int sqlite3ExprIsConstantOrFunction(Expr*, u8);
SQLITE_PRIVATE int sqlite3ExprIsTableConstant(Expr*,int);
+#ifdef SQLITE_ENABLE_CURSOR_HINTS
+SQLITE_PRIVATE int sqlite3ExprContainsSubquery(Expr*);
+#endif
SQLITE_PRIVATE int sqlite3ExprIsInteger(Expr*, int*);
SQLITE_PRIVATE int sqlite3ExprCanBeNull(const Expr*);
SQLITE_PRIVATE int sqlite3ExprNeedsNoAffinityChange(const Expr*, char);
SQLITE_PRIVATE int sqlite3IsRowid(const char*);
-SQLITE_PRIVATE void sqlite3GenerateRowDelete(Parse*,Table*,Trigger*,int,int,int,i16,u8,u8,u8);
-SQLITE_PRIVATE void sqlite3GenerateRowIndexDelete(Parse*, Table*, int, int, int*);
+SQLITE_PRIVATE void sqlite3GenerateRowDelete(
+ Parse*,Table*,Trigger*,int,int,int,i16,u8,u8,u8,int);
+SQLITE_PRIVATE void sqlite3GenerateRowIndexDelete(Parse*, Table*, int, int, int*, int);
SQLITE_PRIVATE int sqlite3GenerateIndexKey(Parse*, Index*, int, int, int, int*,Index*,int);
SQLITE_PRIVATE void sqlite3ResolvePartIdxLabel(Parse*,int);
SQLITE_PRIVATE void sqlite3GenerateConstraintChecks(Parse*,Table*,int*,int,int,int,int,
- u8,u8,int,int*);
+ u8,u8,int,int*,int*);
SQLITE_PRIVATE void sqlite3CompleteInsertion(Parse*,Table*,int,int,int,int*,int,int,int);
-SQLITE_PRIVATE int sqlite3OpenTableAndIndices(Parse*, Table*, int, int, u8*, int*, int*);
+SQLITE_PRIVATE int sqlite3OpenTableAndIndices(Parse*, Table*, int, u8, int, u8*, int*, int*);
SQLITE_PRIVATE void sqlite3BeginWriteOperation(Parse*, int, int);
SQLITE_PRIVATE void sqlite3MultiWrite(Parse*);
SQLITE_PRIVATE void sqlite3MayAbort(Parse*);
@@ -13295,11 +16150,11 @@ SQLITE_PRIVATE void sqlite3SelectSetName(Select*,const char*);
#else
# define sqlite3SelectSetName(A,B)
#endif
-SQLITE_PRIVATE void sqlite3FuncDefInsert(FuncDefHash*, FuncDef*);
-SQLITE_PRIVATE FuncDef *sqlite3FindFunction(sqlite3*,const char*,int,int,u8,u8);
-SQLITE_PRIVATE void sqlite3RegisterBuiltinFunctions(sqlite3*);
+SQLITE_PRIVATE void sqlite3InsertBuiltinFuncs(FuncDef*,int);
+SQLITE_PRIVATE FuncDef *sqlite3FindFunction(sqlite3*,const char*,int,u8,u8);
+SQLITE_PRIVATE void sqlite3RegisterBuiltinFunctions(void);
SQLITE_PRIVATE void sqlite3RegisterDateTimeFunctions(void);
-SQLITE_PRIVATE void sqlite3RegisterGlobalFunctions(void);
+SQLITE_PRIVATE void sqlite3RegisterPerConnectionBuiltinFunctions(sqlite3*);
SQLITE_PRIVATE int sqlite3SafetyCheckOk(sqlite3*);
SQLITE_PRIVATE int sqlite3SafetyCheckSickOrOk(sqlite3*);
SQLITE_PRIVATE void sqlite3ChangeCookie(Parse*, int);
@@ -13330,6 +16185,7 @@ SQLITE_PRIVATE void sqlite3DeleteTrigger(sqlite3*, Trigger*);
SQLITE_PRIVATE void sqlite3UnlinkAndDeleteTrigger(sqlite3*,int,const char*);
SQLITE_PRIVATE u32 sqlite3TriggerColmask(Parse*,Trigger*,ExprList*,int,int,Table*,int);
# define sqlite3ParseToplevel(p) ((p)->pToplevel ? (p)->pToplevel : (p))
+# define sqlite3IsToplevel(p) ((p)->pToplevel==0)
#else
# define sqlite3TriggersExist(B,C,D,E,F) 0
# define sqlite3DeleteTrigger(A,B)
@@ -13339,6 +16195,7 @@ SQLITE_PRIVATE u32 sqlite3TriggerColmask(Parse*,Trigger*,ExprList*,int,int,Tab
# define sqlite3CodeRowTriggerDirect(A,B,C,D,E,F)
# define sqlite3TriggerList(X, Y) 0
# define sqlite3ParseToplevel(p) p
+# define sqlite3IsToplevel(p) 1
# define sqlite3TriggerColmask(A,B,C,D,E,F,G) 0
#endif
@@ -13376,7 +16233,11 @@ SQLITE_PRIVATE LogEst sqlite3LogEstAdd(LogEst,LogEst);
#ifndef SQLITE_OMIT_VIRTUALTABLE
SQLITE_PRIVATE LogEst sqlite3LogEstFromDouble(double);
#endif
+#if defined(SQLITE_ENABLE_STMT_SCANSTATUS) || \
+ defined(SQLITE_ENABLE_STAT3_OR_STAT4) || \
+ defined(SQLITE_EXPLAIN_ESTIMATED_ROWS)
SQLITE_PRIVATE u64 sqlite3LogEstToInt(LogEst);
+#endif
/*
** Routines to read and write variable-length integers. These used to
@@ -13402,7 +16263,7 @@ SQLITE_PRIVATE int sqlite3VarintLen(u64 v);
#define putVarint sqlite3PutVarint
-SQLITE_PRIVATE const char *sqlite3IndexAffinityStr(Vdbe *, Index *);
+SQLITE_PRIVATE const char *sqlite3IndexAffinityStr(sqlite3*, Index*);
SQLITE_PRIVATE void sqlite3TableAffinity(Vdbe*, Table*, int);
SQLITE_PRIVATE char sqlite3CompareAffinity(Expr *pExpr, char aff2);
SQLITE_PRIVATE int sqlite3IndexAffinityOk(Expr *pExpr, char idx_affinity);
@@ -13411,6 +16272,7 @@ SQLITE_PRIVATE int sqlite3Atoi64(const char*, i64*, int, u8);
SQLITE_PRIVATE int sqlite3DecOrHexToI64(const char*, i64*);
SQLITE_PRIVATE void sqlite3ErrorWithMsg(sqlite3*, int, const char*,...);
SQLITE_PRIVATE void sqlite3Error(sqlite3*,int);
+SQLITE_PRIVATE void sqlite3SystemError(sqlite3*,int);
SQLITE_PRIVATE void *sqlite3HexToBlob(sqlite3*, const char *z, int n);
SQLITE_PRIVATE u8 sqlite3HexToInt(int h);
SQLITE_PRIVATE int sqlite3TwoPartName(Parse *, Token *, Token *, Token **);
@@ -13443,7 +16305,7 @@ SQLITE_PRIVATE u8 sqlite3GetBoolean(const char *z,u8);
SQLITE_PRIVATE const void *sqlite3ValueText(sqlite3_value*, u8);
SQLITE_PRIVATE int sqlite3ValueBytes(sqlite3_value*, u8);
-SQLITE_PRIVATE void sqlite3ValueSetStr(sqlite3_value*, int, const void *,u8,
+SQLITE_PRIVATE void sqlite3ValueSetStr(sqlite3_value*, int, const void *,u8,
void(*)(void*));
SQLITE_PRIVATE void sqlite3ValueSetNull(sqlite3_value*);
SQLITE_PRIVATE void sqlite3ValueFree(sqlite3_value*);
@@ -13453,11 +16315,12 @@ SQLITE_PRIVATE int sqlite3ValueFromExpr(sqlite3 *, Expr *, u8, u8, sqlite3_value
SQLITE_PRIVATE void sqlite3ValueApplyAffinity(sqlite3_value *, u8, u8);
#ifndef SQLITE_AMALGAMATION
SQLITE_PRIVATE const unsigned char sqlite3OpcodeProperty[];
+SQLITE_PRIVATE const char sqlite3StrBINARY[];
SQLITE_PRIVATE const unsigned char sqlite3UpperToLower[];
SQLITE_PRIVATE const unsigned char sqlite3CtypeMap[];
SQLITE_PRIVATE const Token sqlite3IntTokens[];
SQLITE_PRIVATE SQLITE_WSD struct Sqlite3Config sqlite3Config;
-SQLITE_PRIVATE SQLITE_WSD FuncDefHash sqlite3GlobalFunctions;
+SQLITE_PRIVATE FuncDefHash sqlite3BuiltinFunctions;
#ifndef SQLITE_OMIT_WSD
SQLITE_PRIVATE int sqlite3PendingByte;
#endif
@@ -13471,8 +16334,10 @@ SQLITE_PRIVATE void sqlite3NestedParse(Parse*, const char*, ...);
SQLITE_PRIVATE void sqlite3ExpirePreparedStatements(sqlite3*);
SQLITE_PRIVATE int sqlite3CodeSubselect(Parse *, Expr *, int, int);
SQLITE_PRIVATE void sqlite3SelectPrep(Parse*, Select*, NameContext*);
+SQLITE_PRIVATE void sqlite3SelectWrongNumTermsError(Parse *pParse, Select *p);
SQLITE_PRIVATE int sqlite3MatchSpanName(const char*, const char*, const char*, const char*);
SQLITE_PRIVATE int sqlite3ResolveExprNames(NameContext*, Expr*);
+SQLITE_PRIVATE int sqlite3ResolveExprListNames(NameContext*, ExprList*);
SQLITE_PRIVATE void sqlite3ResolveSelectNames(Parse*, Select*, NameContext*);
SQLITE_PRIVATE void sqlite3ResolveSelfReference(Parse*,Table*,int,Expr*,ExprList*);
SQLITE_PRIVATE int sqlite3ResolveOrderGroupBy(Parse*, Select*, ExprList*, const char*);
@@ -13490,7 +16355,6 @@ SQLITE_PRIVATE void sqlite3DeleteIndexSamples(sqlite3*,Index*);
SQLITE_PRIVATE void sqlite3DefaultRowEst(Index*);
SQLITE_PRIVATE void sqlite3RegisterLikeFunctions(sqlite3*, int);
SQLITE_PRIVATE int sqlite3IsLikeFunction(sqlite3*,Expr*,int*,char*);
-SQLITE_PRIVATE void sqlite3MinimumFileFormat(Parse*, int, int);
SQLITE_PRIVATE void sqlite3SchemaClear(void *);
SQLITE_PRIVATE Schema *sqlite3SchemaGet(sqlite3 *, Btree *);
SQLITE_PRIVATE int sqlite3SchemaToIndex(sqlite3 *db, Schema *);
@@ -13501,11 +16365,13 @@ SQLITE_PRIVATE KeyInfo *sqlite3KeyInfoOfIndex(Parse*, Index*);
#ifdef SQLITE_DEBUG
SQLITE_PRIVATE int sqlite3KeyInfoIsWriteable(KeyInfo*);
#endif
-SQLITE_PRIVATE int sqlite3CreateFunc(sqlite3 *, const char *, int, int, void *,
+SQLITE_PRIVATE int sqlite3CreateFunc(sqlite3 *, const char *, int, int, void *,
void (*)(sqlite3_context*,int,sqlite3_value **),
void (*)(sqlite3_context*,int,sqlite3_value **), void (*)(sqlite3_context*),
FuncDestructor *pDestructor
);
+SQLITE_PRIVATE void sqlite3OomFault(sqlite3*);
+SQLITE_PRIVATE void sqlite3OomClear(sqlite3*);
SQLITE_PRIVATE int sqlite3ApiExit(sqlite3 *db, int);
SQLITE_PRIVATE int sqlite3OpenTempDatabase(Parse *);
@@ -13562,7 +16428,7 @@ SQLITE_PRIVATE int sqlite3Utf8To8(unsigned char*);
# define sqlite3VtabRollback(X)
# define sqlite3VtabCommit(X)
# define sqlite3VtabInSync(db) 0
-# define sqlite3VtabLock(X)
+# define sqlite3VtabLock(X)
# define sqlite3VtabUnlock(X)
# define sqlite3VtabUnlockList(X)
# define sqlite3VtabSavepoint(X, Y, Z) SQLITE_OK
@@ -13581,6 +16447,8 @@ SQLITE_PRIVATE void sqlite3VtabImportErrmsg(Vdbe*, sqlite3_vtab*);
SQLITE_PRIVATE VTable *sqlite3GetVTable(sqlite3*, Table*);
# define sqlite3VtabInSync(db) ((db)->nVTrans>0 && (db)->aVTrans==0)
#endif
+SQLITE_PRIVATE int sqlite3VtabEponymousTableInit(Parse*,Module*);
+SQLITE_PRIVATE void sqlite3VtabEponymousTableClear(sqlite3*,Module*);
SQLITE_PRIVATE void sqlite3VtabMakeWritable(Parse*,Table*);
SQLITE_PRIVATE void sqlite3VtabBeginParse(Parse*, Token*, Token*, Token*, int);
SQLITE_PRIVATE void sqlite3VtabFinishParse(Parse*, Token*);
@@ -13618,7 +16486,7 @@ SQLITE_PRIVATE void sqlite3WithPush(Parse*, With*, u8);
** no-op macros if OMIT_FOREIGN_KEY is defined. In this case no foreign
** key functionality is available. If OMIT_TRIGGER is defined but
** OMIT_FOREIGN_KEY is not, only some of the functions are no-oped. In
-** this case foreign keys are parsed, but no other functionality is
+** this case foreign keys are parsed, but no other functionality is
** provided (enforcement of FK constraints requires the triggers sub-system).
*/
#if !defined(SQLITE_OMIT_FOREIGN_KEY) && !defined(SQLITE_OMIT_TRIGGER)
@@ -13679,19 +16547,14 @@ SQLITE_PRIVATE void sqlite3EndBenignMalloc(void);
#define IN_INDEX_LOOP 0x0004 /* IN operator used as a loop */
SQLITE_PRIVATE int sqlite3FindInIndex(Parse *, Expr *, u32, int*);
+SQLITE_PRIVATE int sqlite3JournalOpen(sqlite3_vfs *, const char *, sqlite3_file *, int, int);
+SQLITE_PRIVATE int sqlite3JournalSize(sqlite3_vfs *);
#ifdef SQLITE_ENABLE_ATOMIC_WRITE
-SQLITE_PRIVATE int sqlite3JournalOpen(sqlite3_vfs *, const char *, sqlite3_file *, int, int);
-SQLITE_PRIVATE int sqlite3JournalSize(sqlite3_vfs *);
SQLITE_PRIVATE int sqlite3JournalCreate(sqlite3_file *);
-SQLITE_PRIVATE int sqlite3JournalExists(sqlite3_file *p);
-#else
- #define sqlite3JournalSize(pVfs) ((pVfs)->szOsFile)
- #define sqlite3JournalExists(p) 1
#endif
+SQLITE_PRIVATE int sqlite3JournalIsInMemory(sqlite3_file *p);
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
@@ -13722,7 +16585,7 @@ SQLITE_PRIVATE void sqlite3ParserTrace(FILE*, char *);
/*
** If the SQLITE_ENABLE IOTRACE exists then the global variable
** sqlite3IoTrace is a pointer to a printf-like routine used to
-** print I/O tracing messages.
+** print I/O tracing messages.
*/
#ifdef SQLITE_ENABLE_IOTRACE
# define IOTRACE(A) if( sqlite3IoTrace ){ sqlite3IoTrace A; }
@@ -13756,7 +16619,7 @@ SQLITE_API SQLITE_EXTERN void (SQLITE_CDECL *sqlite3IoTrace)(const char*,...);
** that allocations that might have been satisfied by lookaside are not
** passed back to non-lookaside free() routines. Asserts such as the
** example above are placed on the non-lookaside free() routines to verify
-** this constraint.
+** this constraint.
**
** All of this is no-op for a production build. It only comes into
** play when the SQLITE_MEMDEBUG compile-time option is used.
@@ -13783,6 +16646,10 @@ SQLITE_PRIVATE int sqlite3ThreadCreate(SQLiteThread**,void*(*)(void*),void*);
SQLITE_PRIVATE int sqlite3ThreadJoin(SQLiteThread*, void**);
#endif
+#if defined(SQLITE_ENABLE_DBSTAT_VTAB) || defined(SQLITE_TEST)
+SQLITE_PRIVATE int sqlite3DbstatRegister(sqlite3*);
+#endif
+
#endif /* _SQLITEINT_H_ */
/************** End of sqliteInt.h *******************************************/
@@ -13801,6 +16668,7 @@ SQLITE_PRIVATE int sqlite3ThreadJoin(SQLiteThread*, void**);
**
** This file contains definitions of global variables and constants.
*/
+/* #include "sqliteInt.h" */
/* An array to map all upper-case characters into their corresponding
** lower-case character.
@@ -13858,6 +16726,7 @@ SQLITE_PRIVATE const unsigned char sqlite3UpperToLower[] = {
** isxdigit() 0x08
** toupper() 0x20
** SQLite identifier character 0x40
+** Quote character 0x80
**
** Bit 0x20 is set if the mapped character requires translation to upper
** case. i.e. if the character is a lower-case ASCII character.
@@ -13883,7 +16752,7 @@ SQLITE_PRIVATE const unsigned char sqlite3CtypeMap[256] = {
0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, /* 08..0f ........ */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 10..17 ........ */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 18..1f ........ */
- 0x01, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, /* 20..27 !"#$%&' */
+ 0x01, 0x00, 0x80, 0x00, 0x40, 0x00, 0x00, 0x80, /* 20..27 !"#$%&' */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 28..2f ()*+,-./ */
0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, /* 30..37 01234567 */
0x0c, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 38..3f 89:;<=>? */
@@ -13891,8 +16760,8 @@ SQLITE_PRIVATE const unsigned char sqlite3CtypeMap[256] = {
0x00, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x02, /* 40..47 @ABCDEFG */
0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, /* 48..4f HIJKLMNO */
0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, /* 50..57 PQRSTUVW */
- 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x40, /* 58..5f XYZ[\]^_ */
- 0x00, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x22, /* 60..67 `abcdefg */
+ 0x02, 0x02, 0x02, 0x80, 0x00, 0x00, 0x00, 0x40, /* 58..5f XYZ[\]^_ */
+ 0x80, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x22, /* 60..67 `abcdefg */
0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, /* 68..6f hijklmno */
0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, /* 70..77 pqrstuvw */
0x22, 0x22, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, /* 78..7f xyz{|}~. */
@@ -13947,6 +16816,18 @@ SQLITE_PRIVATE const unsigned char sqlite3CtypeMap[256] = {
# define SQLITE_SORTER_PMASZ 250
#endif
+/* Statement journals spill to disk when their size exceeds the following
+** threashold (in bytes). 0 means that statement journals are created and
+** written to disk immediately (the default behavior for SQLite versions
+** before 3.12.0). -1 means always keep the entire statement journal in
+** memory. (The statement journal is also always held entirely in memory
+** if journal_mode=MEMORY or if temp_store=MEMORY, regardless of this
+** setting.)
+*/
+#ifndef SQLITE_STMTJRNL_SPILL
+# define SQLITE_STMTJRNL_SPILL (64*1024)
+#endif
+
/*
** The following singleton contains the global configuration for
** the SQLite library.
@@ -13961,6 +16842,7 @@ SQLITE_PRIVATE SQLITE_WSD struct Sqlite3Config sqlite3Config = {
0, /* neverCorrupt */
128, /* szLookaside */
500, /* nLookaside */
+ SQLITE_STMTJRNL_SPILL, /* nStmtSpill */
{0,0,0,0,0,0,0,0}, /* m */
{0,0,0,0,0,0,0,0,0}, /* mutex */
{0,0,0,0,0,0,0,0,0,0,0,0,0},/* pcache2 */
@@ -13974,7 +16856,7 @@ SQLITE_PRIVATE SQLITE_WSD struct Sqlite3Config sqlite3Config = {
0, /* nScratch */
(void*)0, /* pPage */
0, /* szPage */
- 0, /* nPage */
+ SQLITE_DEFAULT_PCACHE_INITSZ, /* nPage */
0, /* mxParserStack */
0, /* sharedCacheEnabled */
SQLITE_SORTER_PMASZ, /* szPma */
@@ -14007,7 +16889,7 @@ SQLITE_PRIVATE SQLITE_WSD struct Sqlite3Config sqlite3Config = {
** database connections. After initialization, this table is
** read-only.
*/
-SQLITE_PRIVATE SQLITE_WSD FuncDefHash sqlite3GlobalFunctions;
+SQLITE_PRIVATE FuncDefHash sqlite3BuiltinFunctions;
/*
** Constant tokens for values 0 and 1.
@@ -14040,6 +16922,7 @@ SQLITE_PRIVATE const Token sqlite3IntTokens[] = {
SQLITE_PRIVATE int sqlite3PendingByte = 0x40000000;
#endif
+/* #include "opcodes.h" */
/*
** Properties of opcodes. The OPFLG_INITIALIZER macro is
** created by mkopcodeh.awk during compilation. Data is obtained
@@ -14048,6 +16931,11 @@ SQLITE_PRIVATE int sqlite3PendingByte = 0x40000000;
*/
SQLITE_PRIVATE const unsigned char sqlite3OpcodeProperty[] = OPFLG_INITIALIZER;
+/*
+** Name of the default collating sequence
+*/
+SQLITE_PRIVATE const char sqlite3StrBINARY[] = "BINARY";
+
/************** End of global.c **********************************************/
/************** Begin file ctime.c *******************************************/
/*
@@ -14068,6 +16956,7 @@ SQLITE_PRIVATE const unsigned char sqlite3OpcodeProperty[] = OPFLG_INITIALIZER;
#ifndef SQLITE_OMIT_COMPILEOPTION_DIAGS
+/* #include "sqliteInt.h" */
/*
** An array of names of all compile-time options. This array should
@@ -14114,6 +17003,9 @@ static const char * const azCompileOpt[] = {
#if SQLITE_DISABLE_LFS
"DISABLE_LFS",
#endif
+#if SQLITE_ENABLE_8_3_NAMES
+ "ENABLE_8_3_NAMES",
+#endif
#if SQLITE_ENABLE_API_ARMOR
"ENABLE_API_ARMOR",
#endif
@@ -14147,12 +17039,18 @@ static const char * const azCompileOpt[] = {
#if SQLITE_ENABLE_FTS4
"ENABLE_FTS4",
#endif
+#if SQLITE_ENABLE_FTS5
+ "ENABLE_FTS5",
+#endif
#if SQLITE_ENABLE_ICU
"ENABLE_ICU",
#endif
#if SQLITE_ENABLE_IOTRACE
"ENABLE_IOTRACE",
#endif
+#if SQLITE_ENABLE_JSON1
+ "ENABLE_JSON1",
+#endif
#if SQLITE_ENABLE_LOAD_EXTENSION
"ENABLE_LOAD_EXTENSION",
#endif
@@ -14203,6 +17101,9 @@ static const char * const azCompileOpt[] = {
#ifdef SQLITE_INT64_TYPE
"INT64_TYPE",
#endif
+#ifdef SQLITE_LIKE_DOESNT_MATCH_BLOBS
+ "LIKE_DOESNT_MATCH_BLOBS",
+#endif
#if SQLITE_LOCK_TRACE
"LOCK_TRACE",
#endif
@@ -14497,6 +17398,7 @@ SQLITE_API const char *SQLITE_STDCALL sqlite3_compileoption_get(int N){
** This module implements the sqlite3_status() interface and related
** functionality.
*/
+/* #include "sqliteInt.h" */
/************** Include vdbeInt.h in the middle of status.c ******************/
/************** Begin file vdbeInt.h *****************************************/
/*
@@ -14528,6 +17430,17 @@ SQLITE_API const char *SQLITE_STDCALL sqlite3_compileoption_get(int N){
#endif
/*
+** VDBE_DISPLAY_P4 is true or false depending on whether or not the
+** "explain" P4 display logic is enabled.
+*/
+#if !defined(SQLITE_OMIT_EXPLAIN) || !defined(NDEBUG) \
+ || defined(VDBE_PROFILE) || defined(SQLITE_DEBUG)
+# define VDBE_DISPLAY_P4 1
+#else
+# define VDBE_DISPLAY_P4 0
+#endif
+
+/*
** SQL is translated into a sequence of instructions to be
** executed by a virtual machine. Each instruction is an instance
** of the following structure.
@@ -14548,42 +17461,55 @@ typedef struct Explain Explain;
/* Elements of the linked list at Vdbe.pAuxData */
typedef struct AuxData AuxData;
+/* Types of VDBE cursors */
+#define CURTYPE_BTREE 0
+#define CURTYPE_SORTER 1
+#define CURTYPE_VTAB 2
+#define CURTYPE_PSEUDO 3
+
/*
-** A cursor is a pointer into a single BTree within a database file.
-** The cursor can seek to a BTree entry with a particular key, or
-** loop over all entries of the Btree. You can also insert new BTree
-** entries or retrieve the key or data from the entry that the cursor
-** is currently pointing to.
+** A VdbeCursor is an superclass (a wrapper) for various cursor objects:
**
-** Cursors can also point to virtual tables, sorters, or "pseudo-tables".
-** A pseudo-table is a single-row table implemented by registers.
-**
-** Every cursor that the virtual machine has open is represented by an
-** instance of the following structure.
+** * A b-tree cursor
+** - In the main database or in an ephemeral database
+** - On either an index or a table
+** * A sorter
+** * A virtual table
+** * A one-row "pseudotable" stored in a single register
*/
+typedef struct VdbeCursor VdbeCursor;
struct VdbeCursor {
- BtCursor *pCursor; /* The cursor structure of the backend */
- Btree *pBt; /* Separate file holding temporary table */
- KeyInfo *pKeyInfo; /* Info about index keys needed by index cursors */
- int seekResult; /* Result of previous sqlite3BtreeMoveto() */
- int pseudoTableReg; /* Register holding pseudotable content. */
- i16 nField; /* Number of fields in the header */
- u16 nHdrParsed; /* Number of header fields parsed so far */
-#ifdef SQLITE_DEBUG
- u8 seekOp; /* Most recent seek operation on this cursor */
-#endif
+ u8 eCurType; /* One of the CURTYPE_* values above */
i8 iDb; /* Index of cursor database in db->aDb[] (or -1) */
u8 nullRow; /* True if pointing to a row with no data */
u8 deferredMoveto; /* A call to sqlite3BtreeMoveto() is needed */
+ u8 isTable; /* True for rowid tables. False for indexes */
+#ifdef SQLITE_DEBUG
+ u8 seekOp; /* Most recent seek operation on this cursor */
+ u8 wrFlag; /* The wrFlag argument to sqlite3BtreeCursor() */
+#endif
Bool isEphemeral:1; /* True for an ephemeral table */
Bool useRandomRowid:1;/* Generate new record numbers semi-randomly */
- Bool isTable:1; /* True if a table requiring integer keys */
- Bool isOrdered:1; /* True if the underlying table is BTREE_UNORDERED */
+ Bool isOrdered:1; /* True if the table is not BTREE_UNORDERED */
Pgno pgnoRoot; /* Root page of the open btree cursor */
- sqlite3_vtab_cursor *pVtabCursor; /* The cursor for a virtual table */
+ i16 nField; /* Number of fields in the header */
+ u16 nHdrParsed; /* Number of header fields parsed so far */
+ union {
+ BtCursor *pCursor; /* CURTYPE_BTREE. Btree cursor */
+ sqlite3_vtab_cursor *pVCur; /* CURTYPE_VTAB. Vtab cursor */
+ int pseudoTableReg; /* CURTYPE_PSEUDO. Reg holding content. */
+ VdbeSorter *pSorter; /* CURTYPE_SORTER. Sorter object */
+ } uc;
+ Btree *pBt; /* Separate file holding temporary table */
+ KeyInfo *pKeyInfo; /* Info about index keys needed by index cursors */
+ int seekResult; /* Result of previous sqlite3BtreeMoveto() */
i64 seqCount; /* Sequence counter */
i64 movetoTarget; /* Argument to the deferred sqlite3BtreeMoveto() */
- VdbeSorter *pSorter; /* Sorter object for OP_SorterOpen cursors */
+ VdbeCursor *pAltCursor; /* Associated index cursor from which to read */
+ int *aAltMap; /* Mapping from table to index column numbers */
+#ifdef SQLITE_ENABLE_COLUMN_USED_MASK
+ u64 maskUsed; /* Mask of columns used by this cursor */
+#endif
/* Cached information about the header for the data record that the
** cursor is currently pointing to. Only valid if cacheStatus matches
@@ -14605,7 +17531,6 @@ struct VdbeCursor {
** static element declared in the structure. nField total array slots for
** aType[] and nField+1 array slots for aOffset[] */
};
-typedef struct VdbeCursor VdbeCursor;
/*
** When a sub-program is executed (OP_Program), a structure of this type
@@ -14639,6 +17564,7 @@ struct VdbeFrame {
VdbeCursor **apCsr; /* Array of Vdbe cursors for parent frame */
void *token; /* Copy of SubProgram.token */
i64 lastRowid; /* Last insert rowid (sqlite3.lastRowid) */
+ AuxData *pAuxData; /* Linked list of auxdata allocations */
int nCursor; /* Number of entries in apCsr */
int pc; /* Program Counter in parent (calling) frame */
int nOp; /* Size of aOp array */
@@ -14673,6 +17599,7 @@ struct Mem {
} u;
u16 flags; /* Some combination of MEM_Null, MEM_Str, MEM_Dyn, etc. */
u8 enc; /* SQLITE_UTF8, SQLITE_UTF16BE, SQLITE_UTF16LE */
+ u8 eSubtype; /* Subtype for this value */
int n; /* Number of characters in string value, excluding '\0' */
char *z; /* String or BLOB value */
/* ShallowCopy only needs to copy the information above */
@@ -14687,6 +17614,12 @@ struct Mem {
#endif
};
+/*
+** Size of struct Mem not including the Mem.zMalloc member or anything that
+** follows.
+*/
+#define MEMCELLSIZE offsetof(Mem,zMalloc)
+
/* One or more of the following flags are set to indicate the validOK
** representations of the value stored in the Mem struct.
**
@@ -14709,7 +17642,7 @@ struct Mem {
#define MEM_Frame 0x0040 /* Value is a VdbeFrame object */
#define MEM_Undefined 0x0080 /* Value is undefined */
#define MEM_Cleared 0x0100 /* NULL set by OP_Null, not from data */
-#define MEM_TypeMask 0x01ff /* Mask of type bits */
+#define MEM_TypeMask 0x81ff /* Mask of type bits */
/* Whenever Mem contains a valid string or blob representation, one of
@@ -14723,11 +17656,18 @@ struct Mem {
#define MEM_Ephem 0x1000 /* Mem.z points to an ephemeral string */
#define MEM_Agg 0x2000 /* Mem.z points to an agg function context */
#define MEM_Zero 0x4000 /* Mem.i contains count of 0s appended to blob */
+#define MEM_Subtype 0x8000 /* Mem.eSubtype is valid */
#ifdef SQLITE_OMIT_INCRBLOB
#undef MEM_Zero
#define MEM_Zero 0x0000
#endif
+/* Return TRUE if Mem X contains dynamically allocated content - anything
+** that needs to be deallocated to avoid a leak.
+*/
+#define VdbeMemDynamic(X) \
+ (((X)->flags&(MEM_Agg|MEM_Dyn|MEM_RowSet|MEM_Frame))!=0)
+
/*
** Clear any existing type flags from a Mem and replace them with f
*/
@@ -14771,14 +17711,16 @@ struct AuxData {
** (Mem) which are only defined there.
*/
struct sqlite3_context {
- Mem *pOut; /* The return value is stored here */
- FuncDef *pFunc; /* Pointer to function information */
- Mem *pMem; /* Memory cell used to store aggregate context */
- Vdbe *pVdbe; /* The VM that owns this context */
- int iOp; /* Instruction number of OP_Function */
- int isError; /* Error code returned by the function. */
- u8 skipFlag; /* Skip accumulator loading if true */
- u8 fErrorOrAux; /* isError!=0 or pVdbe->pAuxData modified */
+ Mem *pOut; /* The return value is stored here */
+ FuncDef *pFunc; /* Pointer to function information */
+ Mem *pMem; /* Memory cell used to store aggregate context */
+ Vdbe *pVdbe; /* The VM that owns this context */
+ int iOp; /* Instruction number of OP_Function */
+ int isError; /* Error code returned by the function. */
+ u8 skipFlag; /* Skip accumulator loading if true */
+ u8 fErrorOrAux; /* isError!=0 or pVdbe->pAuxData modified */
+ u8 argc; /* Number of arguments */
+ sqlite3_value *argv[1]; /* Argument set */
};
/*
@@ -14842,16 +17784,16 @@ struct Vdbe {
#endif
u16 nResColumn; /* Number of columns in one row of the result set */
u8 errorAction; /* Recovery action to do in case of an error */
+ bft expired:1; /* True if the VM needs to be recompiled */
+ bft doingRerun:1; /* True if rerunning after an auto-reprepare */
u8 minWriteFileFormat; /* Minimum file format for writable database files */
bft explain:2; /* True if EXPLAIN present on SQL command */
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 */
bft usesStmtJournal:1; /* True if uses a statement journal */
bft readOnly:1; /* True for statements that do not write */
bft bIsReader:1; /* True for statements that read */
bft isPrepareV2:1; /* True if prepared with prepare_v2() */
- bft doingRerun:1; /* True if rerunning after an auto-reprepare */
int nChange; /* Number of db changes made since last reset */
yDbMask btreeMask; /* Bitmask of db->aDb[] entries referenced */
yDbMask lockMask; /* Subset of btreeMask that requires a lock */
@@ -14890,20 +17832,41 @@ struct Vdbe {
#define VDBE_MAGIC_DEAD 0xb606c3c8 /* The VDBE has been deallocated */
/*
+** Structure used to store the context required by the
+** sqlite3_preupdate_*() API functions.
+*/
+struct PreUpdate {
+ Vdbe *v;
+ VdbeCursor *pCsr; /* Cursor to read old values from */
+ int op; /* One of SQLITE_INSERT, UPDATE, DELETE */
+ u8 *aRecord; /* old.* database record */
+ KeyInfo keyinfo;
+ UnpackedRecord *pUnpacked; /* Unpacked version of aRecord[] */
+ UnpackedRecord *pNewUnpacked; /* Unpacked version of new.* record */
+ int iNewReg; /* Register for new.* values */
+ i64 iKey1; /* First key value passed to hook */
+ i64 iKey2; /* Second key value passed to hook */
+ int iPKey; /* If not negative index of IPK column */
+ Mem *aNew; /* Array of new.* values */
+};
+
+/*
** Function prototypes
*/
+SQLITE_PRIVATE void sqlite3VdbeError(Vdbe*, const char *, ...);
SQLITE_PRIVATE void sqlite3VdbeFreeCursor(Vdbe *, VdbeCursor*);
void sqliteVdbePopStack(Vdbe*,int);
-SQLITE_PRIVATE int sqlite3VdbeCursorMoveto(VdbeCursor*);
+SQLITE_PRIVATE int sqlite3VdbeCursorMoveto(VdbeCursor**, int*);
SQLITE_PRIVATE int sqlite3VdbeCursorRestore(VdbeCursor*);
#if defined(SQLITE_DEBUG) || defined(VDBE_PROFILE)
SQLITE_PRIVATE void sqlite3VdbePrintOp(FILE*, int, Op*);
#endif
SQLITE_PRIVATE u32 sqlite3VdbeSerialTypeLen(u32);
-SQLITE_PRIVATE u32 sqlite3VdbeSerialType(Mem*, int);
+SQLITE_PRIVATE u8 sqlite3VdbeOneByteSerialTypeLen(u8);
+SQLITE_PRIVATE u32 sqlite3VdbeSerialType(Mem*, int, u32*);
SQLITE_PRIVATE u32 sqlite3VdbeSerialPut(unsigned char*, Mem*, u32);
SQLITE_PRIVATE u32 sqlite3VdbeSerialGet(const unsigned char*, u32, Mem*);
-SQLITE_PRIVATE void sqlite3VdbeDeleteAuxData(Vdbe*, int, int);
+SQLITE_PRIVATE void sqlite3VdbeDeleteAuxData(sqlite3*, AuxData**, int, int);
int sqlite2BtreeKeyCompare(BtCursor *, const void *, int, int, int *);
SQLITE_PRIVATE int sqlite3VdbeIdxKeyCompare(sqlite3*,VdbeCursor*,UnpackedRecord*,int*);
@@ -14939,8 +17902,6 @@ SQLITE_PRIVATE int sqlite3VdbeMemNumerify(Mem*);
SQLITE_PRIVATE void sqlite3VdbeMemCast(Mem*,u8,u8);
SQLITE_PRIVATE int sqlite3VdbeMemFromBtree(BtCursor*,u32,u32,int,Mem*);
SQLITE_PRIVATE void sqlite3VdbeMemRelease(Mem *p);
-#define VdbeMemDynamic(X) \
- (((X)->flags&(MEM_Agg|MEM_Dyn|MEM_RowSet|MEM_Frame))!=0)
SQLITE_PRIVATE int sqlite3VdbeMemFinalize(Mem*, FuncDef*);
SQLITE_PRIVATE const char *sqlite3OpcodeName(int);
SQLITE_PRIVATE int sqlite3VdbeMemGrow(Mem *pMem, int n, int preserve);
@@ -14948,6 +17909,9 @@ SQLITE_PRIVATE int sqlite3VdbeMemClearAndResize(Mem *pMem, int n);
SQLITE_PRIVATE int sqlite3VdbeCloseStatement(Vdbe *, int);
SQLITE_PRIVATE void sqlite3VdbeFrameDelete(VdbeFrame*);
SQLITE_PRIVATE int sqlite3VdbeFrameRestore(VdbeFrame *);
+#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
+SQLITE_PRIVATE void sqlite3VdbePreUpdateHook(Vdbe*,VdbeCursor*,int,const char*,Table*,i64,int);
+#endif
SQLITE_PRIVATE int sqlite3VdbeTransferError(Vdbe *p);
SQLITE_PRIVATE int sqlite3VdbeSorterInit(sqlite3 *, int, VdbeCursor *);
@@ -14959,11 +17923,15 @@ SQLITE_PRIVATE int sqlite3VdbeSorterRewind(const VdbeCursor *, int *);
SQLITE_PRIVATE int sqlite3VdbeSorterWrite(const VdbeCursor *, Mem *);
SQLITE_PRIVATE int sqlite3VdbeSorterCompare(const VdbeCursor *, Mem *, int, int *);
-#if !defined(SQLITE_OMIT_SHARED_CACHE) && SQLITE_THREADSAFE>0
+#if !defined(SQLITE_OMIT_SHARED_CACHE)
SQLITE_PRIVATE void sqlite3VdbeEnter(Vdbe*);
-SQLITE_PRIVATE void sqlite3VdbeLeave(Vdbe*);
#else
# define sqlite3VdbeEnter(X)
+#endif
+
+#if !defined(SQLITE_OMIT_SHARED_CACHE) && SQLITE_THREADSAFE>0
+SQLITE_PRIVATE void sqlite3VdbeLeave(Vdbe*);
+#else
# define sqlite3VdbeLeave(X)
#endif
@@ -15001,15 +17969,15 @@ SQLITE_PRIVATE int sqlite3VdbeMemExpandBlob(Mem *);
/*
** Variables in which to record status information.
*/
-typedef struct sqlite3StatType sqlite3StatType;
-static SQLITE_WSD struct sqlite3StatType {
#if SQLITE_PTRSIZE>4
- sqlite3_int64 nowValue[10]; /* Current value */
- sqlite3_int64 mxValue[10]; /* Maximum value */
+typedef sqlite3_int64 sqlite3StatValueType;
#else
- u32 nowValue[10]; /* Current value */
- u32 mxValue[10]; /* Maximum value */
+typedef u32 sqlite3StatValueType;
#endif
+typedef struct sqlite3StatType sqlite3StatType;
+static SQLITE_WSD struct sqlite3StatType {
+ sqlite3StatValueType nowValue[10]; /* Current value */
+ sqlite3StatValueType mxValue[10]; /* Maximum value */
} sqlite3Stat = { {0,}, {0,} };
/*
@@ -15090,18 +18058,24 @@ SQLITE_PRIVATE void sqlite3StatusDown(int op, int N){
}
/*
-** Set the value of a status to X. The highwater mark is adjusted if
-** necessary. The caller must hold the appropriate mutex.
+** Adjust the highwater mark if necessary.
+** The caller must hold the appropriate mutex.
*/
-SQLITE_PRIVATE void sqlite3StatusSet(int op, int X){
+SQLITE_PRIVATE void sqlite3StatusHighwater(int op, int X){
+ sqlite3StatValueType newValue;
wsdStatInit;
+ assert( X>=0 );
+ newValue = (sqlite3StatValueType)X;
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];
+ assert( op==SQLITE_STATUS_MALLOC_SIZE
+ || op==SQLITE_STATUS_PAGECACHE_SIZE
+ || op==SQLITE_STATUS_SCRATCH_SIZE
+ || op==SQLITE_STATUS_PARSER_STACK );
+ if( newValue>wsdStat.mxValue[op] ){
+ wsdStat.mxValue[op] = newValue;
}
}
@@ -15234,10 +18208,10 @@ SQLITE_API int SQLITE_STDCALL sqlite3_db_status(
+ pSchema->idxHash.count
+ pSchema->fkeyHash.count
);
- nByte += sqlite3MallocSize(pSchema->tblHash.ht);
- nByte += sqlite3MallocSize(pSchema->trigHash.ht);
- nByte += sqlite3MallocSize(pSchema->idxHash.ht);
- nByte += sqlite3MallocSize(pSchema->fkeyHash.ht);
+ nByte += sqlite3_msize(pSchema->tblHash.ht);
+ nByte += sqlite3_msize(pSchema->trigHash.ht);
+ nByte += sqlite3_msize(pSchema->idxHash.ht);
+ nByte += sqlite3_msize(pSchema->fkeyHash.ht);
for(p=sqliteHashFirst(&pSchema->trigHash); p; p=sqliteHashNext(p)){
sqlite3DeleteTrigger(db, (Trigger*)sqliteHashData(p));
@@ -15368,12 +18342,22 @@ SQLITE_API int SQLITE_STDCALL sqlite3_db_status(
** Willmann-Bell, Inc
** Richmond, Virginia (USA)
*/
+/* #include "sqliteInt.h" */
/* #include <stdlib.h> */
/* #include <assert.h> */
#include <time.h>
#ifndef SQLITE_OMIT_DATETIME_FUNCS
+/*
+** The MSVC CRT on Windows CE may not have a localtime() function.
+** So declare a substitute. The substitute function itself is
+** defined in "os_win.c".
+*/
+#if !defined(SQLITE_OMIT_LOCALTIME) && defined(_WIN32_WCE) && \
+ (!defined(SQLITE_MSVC_LOCALTIME_API) || !SQLITE_MSVC_LOCALTIME_API)
+struct tm *__cdecl localtime(const time_t *);
+#endif
/*
** A structure for holding a single date and time.
@@ -15389,38 +18373,54 @@ struct DateTime {
char validHMS; /* True (1) if h,m,s are valid */
char validJD; /* True (1) if iJD is valid */
char validTZ; /* True (1) if tz is valid */
+ char tzSet; /* Timezone was set explicitly */
};
/*
-** Convert zDate into one or more integers. Additional arguments
-** come in groups of 5 as follows:
+** Convert zDate into one or more integers according to the conversion
+** specifier zFormat.
+**
+** zFormat[] contains 4 characters for each integer converted, except for
+** the last integer which is specified by three characters. The meaning
+** of a four-character format specifiers ABCD is:
+**
+** A: number of digits to convert. Always "2" or "4".
+** B: minimum value. Always "0" or "1".
+** C: maximum value, decoded as:
+** a: 12
+** b: 14
+** c: 24
+** d: 31
+** e: 59
+** f: 9999
+** D: the separator character, or \000 to indicate this is the
+** last number to convert.
**
-** N number of digits in the integer
-** min minimum allowed value of the integer
-** max maximum allowed value of the integer
-** nextC first character after the integer
-** pVal where to write the integers value.
+** Example: To translate an ISO-8601 date YYYY-MM-DD, the format would
+** be "40f-21a-20c". The "40f-" indicates the 4-digit year followed by "-".
+** The "21a-" indicates the 2-digit month followed by "-". The "20c" indicates
+** the 2-digit day which is the last integer in the set.
**
-** Conversions continue until one with nextC==0 is encountered.
** The function returns the number of successful conversions.
*/
-static int getDigits(const char *zDate, ...){
+static int getDigits(const char *zDate, const char *zFormat, ...){
+ /* The aMx[] array translates the 3rd character of each format
+ ** spec into a max size: a b c d e f */
+ static const u16 aMx[] = { 12, 14, 24, 31, 59, 9999 };
va_list ap;
- int val;
- int N;
- int min;
- int max;
- int nextC;
- int *pVal;
int cnt = 0;
- va_start(ap, zDate);
+ char nextC;
+ va_start(ap, zFormat);
do{
- N = va_arg(ap, int);
- min = va_arg(ap, int);
- max = va_arg(ap, int);
- nextC = va_arg(ap, int);
- pVal = va_arg(ap, int*);
+ char N = zFormat[0] - '0';
+ char min = zFormat[1] - '0';
+ int val = 0;
+ u16 max;
+
+ assert( zFormat[2]>='a' && zFormat[2]<='f' );
+ max = aMx[zFormat[2] - 'a'];
+ nextC = zFormat[3];
val = 0;
while( N-- ){
if( !sqlite3Isdigit(*zDate) ){
@@ -15429,12 +18429,13 @@ static int getDigits(const char *zDate, ...){
val = val*10 + *zDate - '0';
zDate++;
}
- if( val<min || val>max || (nextC!=0 && nextC!=*zDate) ){
+ if( val<(int)min || val>(int)max || (nextC!=0 && nextC!=*zDate) ){
goto end_getDigits;
}
- *pVal = val;
+ *va_arg(ap,int*) = val;
zDate++;
cnt++;
+ zFormat += 4;
}while( nextC );
end_getDigits:
va_end(ap);
@@ -15475,13 +18476,14 @@ static int parseTimezone(const char *zDate, DateTime *p){
return c!=0;
}
zDate++;
- if( getDigits(zDate, 2, 0, 14, ':', &nHr, 2, 0, 59, 0, &nMn)!=2 ){
+ if( getDigits(zDate, "20b:20e", &nHr, &nMn)!=2 ){
return 1;
}
zDate += 5;
p->tz = sgn*(nMn + nHr*60);
zulu_time:
while( sqlite3Isspace(*zDate) ){ zDate++; }
+ p->tzSet = 1;
return *zDate!=0;
}
@@ -15495,13 +18497,13 @@ zulu_time:
static int parseHhMmSs(const char *zDate, DateTime *p){
int h, m, s;
double ms = 0.0;
- if( getDigits(zDate, 2, 0, 24, ':', &h, 2, 0, 59, 0, &m)!=2 ){
+ if( getDigits(zDate, "20c:20e", &h, &m)!=2 ){
return 1;
}
zDate += 5;
if( *zDate==':' ){
zDate++;
- if( getDigits(zDate, 2, 0, 59, 0, &s)!=1 ){
+ if( getDigits(zDate, "20e", &s)!=1 ){
return 1;
}
zDate += 2;
@@ -15589,7 +18591,7 @@ static int parseYyyyMmDd(const char *zDate, DateTime *p){
}else{
neg = 0;
}
- if( getDigits(zDate,4,0,9999,'-',&Y,2,1,12,'-',&M,2,1,31,0,&D)!=3 ){
+ if( getDigits(zDate, "40f-21a-21d", &Y, &M, &D)!=3 ){
return 1;
}
zDate += 10;
@@ -15679,7 +18681,7 @@ static void computeYMD(DateTime *p){
A = Z + 1 + A - (A/4);
B = A + 1524;
C = (int)((B - 122.1)/365.25);
- D = (36525*C)/100;
+ D = (36525*(C&32767))/100;
E = (int)((B-D)/30.6001);
X1 = (int)(30.6001*E);
p->D = B - D - X1;
@@ -15724,6 +18726,7 @@ static void clearYMD_HMS_TZ(DateTime *p){
p->validTZ = 0;
}
+#ifndef SQLITE_OMIT_LOCALTIME
/*
** On recent Windows platforms, the localtime_s() function is available
** as part of the "Secure CRT". It is essentially equivalent to
@@ -15742,7 +18745,6 @@ static void clearYMD_HMS_TZ(DateTime *p){
#define HAVE_LOCALTIME_S 1
#endif
-#ifndef SQLITE_OMIT_LOCALTIME
/*
** The following routine implements the rough equivalent of localtime_r()
** using whatever operating-system specific localtime facility that
@@ -15914,13 +18916,18 @@ static int parseModifier(sqlite3_context *pCtx, const char *zMod, DateTime *p){
}
#ifndef SQLITE_OMIT_LOCALTIME
else if( strcmp(z, "utc")==0 ){
- sqlite3_int64 c1;
- computeJD(p);
- c1 = localtimeOffset(p, pCtx, &rc);
- if( rc==SQLITE_OK ){
- p->iJD -= c1;
- clearYMD_HMS_TZ(p);
- p->iJD += c1 - localtimeOffset(p, pCtx, &rc);
+ if( p->tzSet==0 ){
+ sqlite3_int64 c1;
+ computeJD(p);
+ c1 = localtimeOffset(p, pCtx, &rc);
+ if( rc==SQLITE_OK ){
+ p->iJD -= c1;
+ clearYMD_HMS_TZ(p);
+ p->iJD += c1 - localtimeOffset(p, pCtx, &rc);
+ }
+ p->tzSet = 1;
+ }else{
+ rc = SQLITE_OK;
}
}
#endif
@@ -16268,7 +19275,7 @@ static void strftimeFunc(
sqlite3_result_error_toobig(context);
return;
}else{
- z = sqlite3DbMallocRaw(db, (int)n);
+ z = sqlite3DbMallocRawNN(db, (int)n);
if( z==0 ){
sqlite3_result_error_nomem(context);
return;
@@ -16437,29 +19444,23 @@ static void currentTimeFunc(
** external linkage.
*/
SQLITE_PRIVATE void sqlite3RegisterDateTimeFunctions(void){
- static SQLITE_WSD FuncDef aDateTimeFuncs[] = {
+ static FuncDef aDateTimeFuncs[] = {
#ifndef SQLITE_OMIT_DATETIME_FUNCS
- FUNCTION(julianday, -1, 0, 0, juliandayFunc ),
- FUNCTION(date, -1, 0, 0, dateFunc ),
- FUNCTION(time, -1, 0, 0, timeFunc ),
- FUNCTION(datetime, -1, 0, 0, datetimeFunc ),
- FUNCTION(strftime, -1, 0, 0, strftimeFunc ),
- FUNCTION(current_time, 0, 0, 0, ctimeFunc ),
- FUNCTION(current_timestamp, 0, 0, 0, ctimestampFunc),
- FUNCTION(current_date, 0, 0, 0, cdateFunc ),
+ DFUNCTION(julianday, -1, 0, 0, juliandayFunc ),
+ DFUNCTION(date, -1, 0, 0, dateFunc ),
+ DFUNCTION(time, -1, 0, 0, timeFunc ),
+ DFUNCTION(datetime, -1, 0, 0, datetimeFunc ),
+ DFUNCTION(strftime, -1, 0, 0, strftimeFunc ),
+ DFUNCTION(current_time, 0, 0, 0, ctimeFunc ),
+ DFUNCTION(current_timestamp, 0, 0, 0, ctimestampFunc),
+ DFUNCTION(current_date, 0, 0, 0, cdateFunc ),
#else
STR_FUNCTION(current_time, 0, "%H:%M:%S", 0, currentTimeFunc),
STR_FUNCTION(current_date, 0, "%Y-%m-%d", 0, currentTimeFunc),
STR_FUNCTION(current_timestamp, 0, "%Y-%m-%d %H:%M:%S", 0, currentTimeFunc),
#endif
};
- int i;
- FuncDefHash *pHash = &GLOBAL(FuncDefHash, sqlite3GlobalFunctions);
- FuncDef *aFunc = (FuncDef*)&GLOBAL(FuncDef, aDateTimeFuncs);
-
- for(i=0; i<ArraySize(aDateTimeFuncs); i++){
- sqlite3FuncDefInsert(pHash, &aFunc[i]);
- }
+ sqlite3InsertBuiltinFuncs(aDateTimeFuncs, ArraySize(aDateTimeFuncs));
}
/************** End of date.c ************************************************/
@@ -16480,16 +19481,39 @@ SQLITE_PRIVATE void sqlite3RegisterDateTimeFunctions(void){
** architectures.
*/
#define _SQLITE_OS_C_ 1
+/* #include "sqliteInt.h" */
#undef _SQLITE_OS_C_
/*
+** If we compile with the SQLITE_TEST macro set, then the following block
+** of code will give us the ability to simulate a disk I/O error. This
+** is used for testing the I/O recovery logic.
+*/
+#if defined(SQLITE_TEST)
+SQLITE_API int sqlite3_io_error_hit = 0; /* Total number of I/O Errors */
+SQLITE_API int sqlite3_io_error_hardhit = 0; /* Number of non-benign errors */
+SQLITE_API int sqlite3_io_error_pending = 0; /* Count down to first I/O error */
+SQLITE_API int sqlite3_io_error_persist = 0; /* True if I/O errors persist */
+SQLITE_API int sqlite3_io_error_benign = 0; /* True if errors are benign */
+SQLITE_API int sqlite3_diskfull_pending = 0;
+SQLITE_API int sqlite3_diskfull = 0;
+#endif /* defined(SQLITE_TEST) */
+
+/*
+** When testing, also keep a count of the number of open files.
+*/
+#if defined(SQLITE_TEST)
+SQLITE_API int sqlite3_open_file_count = 0;
+#endif /* defined(SQLITE_TEST) */
+
+/*
** The default SQLite sqlite3_vfs implementations do not allocate
** memory (actually, os_unix.c allocates a small amount of memory
** from within OsOpen()), but some third-party implementations may.
** So we test the effects of a malloc() failing and the sqlite3OsXXX()
** function returning SQLITE_IOERR_NOMEM using the DO_OS_MALLOC_TEST macro.
**
-** The following functions are instrumented for malloc() failure
+** The following functions are instrumented for malloc() failure
** testing:
**
** sqlite3OsRead()
@@ -16509,9 +19533,9 @@ SQLITE_PRIVATE void sqlite3RegisterDateTimeFunctions(void){
#if defined(SQLITE_TEST)
SQLITE_API int sqlite3_memdebug_vfs_oom_test = 1;
#define DO_OS_MALLOC_TEST(x) \
- if (sqlite3_memdebug_vfs_oom_test && (!x || !sqlite3IsMemJournal(x))) { \
+ if (sqlite3_memdebug_vfs_oom_test && (!x || !sqlite3JournalIsInMemory(x))) { \
void *pTstAlloc = sqlite3Malloc(10); \
- if (!pTstAlloc) return SQLITE_IOERR_NOMEM; \
+ if (!pTstAlloc) return SQLITE_IOERR_NOMEM_BKPT; \
sqlite3_free(pTstAlloc); \
}
#else
@@ -16524,13 +19548,11 @@ SQLITE_API int sqlite3_memdebug_vfs_oom_test = 1;
** of this would be completely automatic if SQLite were coded using
** C++ instead of plain old C.
*/
-SQLITE_PRIVATE int sqlite3OsClose(sqlite3_file *pId){
- int rc = SQLITE_OK;
+SQLITE_PRIVATE void sqlite3OsClose(sqlite3_file *pId){
if( pId->pMethods ){
- rc = pId->pMethods->xClose(pId);
+ pId->pMethods->xClose(pId);
pId->pMethods = 0;
}
- return rc;
}
SQLITE_PRIVATE int sqlite3OsRead(sqlite3_file *id, void *pBuf, int amt, i64 offset){
DO_OS_MALLOC_TEST(id);
@@ -16575,8 +19597,8 @@ SQLITE_PRIVATE int sqlite3OsFileControl(sqlite3_file *id, int op, void *pArg){
#ifdef SQLITE_TEST
if( op!=SQLITE_FCNTL_COMMIT_PHASETWO ){
/* Faults are not injected into COMMIT_PHASETWO because, assuming SQLite
- ** is using a regular VFS, it is called after the corresponding
- ** transaction has been committed. Injecting a fault at this point
+ ** is using a regular VFS, it is called after the corresponding
+ ** transaction has been committed. Injecting a fault at this point
** confuses the test scripts - the COMMIT comand returns SQLITE_NOMEM
** but the transaction is committed anyway.
**
@@ -16645,10 +19667,10 @@ SQLITE_PRIVATE int sqlite3OsUnfetch(sqlite3_file *id, i64 iOff, void *p){
** VFS methods.
*/
SQLITE_PRIVATE int sqlite3OsOpen(
- sqlite3_vfs *pVfs,
- const char *zPath,
- sqlite3_file *pFile,
- int flags,
+ sqlite3_vfs *pVfs,
+ const char *zPath,
+ sqlite3_file *pFile,
+ int flags,
int *pFlagsOut
){
int rc;
@@ -16667,18 +19689,18 @@ SQLITE_PRIVATE int sqlite3OsDelete(sqlite3_vfs *pVfs, const char *zPath, int dir
return pVfs->xDelete(pVfs, zPath, dirSync);
}
SQLITE_PRIVATE int sqlite3OsAccess(
- sqlite3_vfs *pVfs,
- const char *zPath,
- int flags,
+ sqlite3_vfs *pVfs,
+ const char *zPath,
+ int flags,
int *pResOut
){
DO_OS_MALLOC_TEST(0);
return pVfs->xAccess(pVfs, zPath, flags, pResOut);
}
SQLITE_PRIVATE int sqlite3OsFullPathname(
- sqlite3_vfs *pVfs,
- const char *zPath,
- int nPathOut,
+ sqlite3_vfs *pVfs,
+ const char *zPath,
+ int nPathOut,
char *zPathOut
){
DO_OS_MALLOC_TEST(0);
@@ -16705,6 +19727,9 @@ SQLITE_PRIVATE int sqlite3OsRandomness(sqlite3_vfs *pVfs, int nByte, char *zBufO
SQLITE_PRIVATE int sqlite3OsSleep(sqlite3_vfs *pVfs, int nMicro){
return pVfs->xSleep(pVfs, nMicro);
}
+SQLITE_PRIVATE int sqlite3OsGetLastError(sqlite3_vfs *pVfs){
+ return pVfs->xGetLastError ? pVfs->xGetLastError(pVfs, 0, 0) : 0;
+}
SQLITE_PRIVATE int sqlite3OsCurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *pTimeOut){
int rc;
/* IMPLEMENTATION-OF: R-49045-42493 SQLite will use the xCurrentTimeInt64()
@@ -16724,13 +19749,13 @@ SQLITE_PRIVATE int sqlite3OsCurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *p
}
SQLITE_PRIVATE int sqlite3OsOpenMalloc(
- sqlite3_vfs *pVfs,
- const char *zFile,
- sqlite3_file **ppFile,
+ sqlite3_vfs *pVfs,
+ const char *zFile,
+ sqlite3_file **ppFile,
int flags,
int *pOutFlags
){
- int rc = SQLITE_NOMEM;
+ int rc;
sqlite3_file *pFile;
pFile = (sqlite3_file *)sqlite3MallocZero(pVfs->szOsFile);
if( pFile ){
@@ -16740,15 +19765,15 @@ SQLITE_PRIVATE int sqlite3OsOpenMalloc(
}else{
*ppFile = pFile;
}
+ }else{
+ rc = SQLITE_NOMEM_BKPT;
}
return rc;
}
-SQLITE_PRIVATE int sqlite3OsCloseFree(sqlite3_file *pFile){
- int rc = SQLITE_OK;
+SQLITE_PRIVATE void sqlite3OsCloseFree(sqlite3_file *pFile){
assert( pFile );
- rc = sqlite3OsClose(pFile);
+ sqlite3OsClose(pFile);
sqlite3_free(pFile);
- return rc;
}
/*
@@ -16759,7 +19784,7 @@ SQLITE_PRIVATE int sqlite3OsCloseFree(sqlite3_file *pFile){
*/
SQLITE_PRIVATE int sqlite3OsInit(void){
void *p = sqlite3_malloc(10);
- if( p==0 ) return SQLITE_NOMEM;
+ if( p==0 ) return SQLITE_NOMEM_BKPT;
sqlite3_free(p);
return sqlite3_os_init();
}
@@ -16886,6 +19911,7 @@ SQLITE_API int SQLITE_STDCALL sqlite3_vfs_unregister(sqlite3_vfs *pVfs){
** during a hash table resize is a benign fault.
*/
+/* #include "sqliteInt.h" */
#ifndef SQLITE_OMIT_BUILTIN_TEST
@@ -16967,6 +19993,7 @@ SQLITE_PRIVATE void sqlite3EndBenignMalloc(void){
** are merely placeholders. Real drivers must be substituted using
** sqlite3_config() before SQLite will operate.
*/
+/* #include "sqliteInt.h" */
/*
** This version of the memory allocator is the default. It is
@@ -17053,6 +20080,7 @@ SQLITE_PRIVATE void sqlite3MemSetDefault(void){
** be necessary when compiling for Delphi,
** for example.
*/
+/* #include "sqliteInt.h" */
/*
** This version of the memory allocator is the default. It is
@@ -17183,10 +20211,11 @@ static void sqlite3MemFree(void *pPrior){
*/
static int sqlite3MemSize(void *pPrior){
#ifdef SQLITE_MALLOCSIZE
- return pPrior ? (int)SQLITE_MALLOCSIZE(pPrior) : 0;
+ assert( pPrior!=0 );
+ return (int)SQLITE_MALLOCSIZE(pPrior);
#else
sqlite3_int64 *p;
- if( pPrior==0 ) return 0;
+ assert( pPrior!=0 );
p = (sqlite3_int64*)pPrior;
p--;
return (int)p[0];
@@ -17328,6 +20357,7 @@ SQLITE_PRIVATE void sqlite3MemSetDefault(void){
** This file contains implementations of the low-level memory allocation
** routines specified in the sqlite3_mem_methods object.
*/
+/* #include "sqliteInt.h" */
/*
** This version of the memory allocator is used only if the
@@ -17862,6 +20892,7 @@ SQLITE_PRIVATE int sqlite3MemdebugMallocCount(){
** This version of the memory allocation subsystem is included
** in the build only if SQLITE_ENABLE_MEMSYS3 is defined.
*/
+/* #include "sqliteInt.h" */
/*
** This version of the memory allocator is only built into the library
@@ -18314,7 +21345,7 @@ static void memsys3FreeUnsafe(void *pOld){
*/
static int memsys3Size(void *p){
Mem3Block *pBlock;
- if( p==0 ) return 0;
+ assert( p!=0 );
pBlock = (Mem3Block*)p;
assert( (pBlock[-1].u.hdr.size4x&1)!=0 );
return (pBlock[-1].u.hdr.size4x&~3)*2 - 4;
@@ -18553,7 +21584,7 @@ SQLITE_PRIVATE const sqlite3_mem_methods *sqlite3MemGetMemsys3(void){
**
** This memory allocator uses the following algorithm:
**
-** 1. All memory allocations sizes are rounded up to a power of 2.
+** 1. All memory allocation sizes are rounded up to a power of 2.
**
** 2. If two adjacent free blocks are the halves of a larger block,
** then the two blocks are coalesced into the single larger block.
@@ -18576,6 +21607,7 @@ SQLITE_PRIVATE const sqlite3_mem_methods *sqlite3MemGetMemsys3(void){
** The sqlite3_status() logic tracks the maximum values of n and M so
** that an application can, at any time, verify this constraint.
*/
+/* #include "sqliteInt.h" */
/*
** This version of the memory allocator is used only when
@@ -18629,6 +21661,7 @@ static SQLITE_WSD struct Mem5Global {
*/
sqlite3_mutex *mutex;
+#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST)
/*
** Performance statistics
*/
@@ -18640,11 +21673,12 @@ static SQLITE_WSD struct Mem5Global {
u32 maxOut; /* Maximum instantaneous currentOut */
u32 maxCount; /* Maximum instantaneous currentCount */
u32 maxRequest; /* Largest allocation (exclusive of internal frag) */
+#endif
/*
** Lists of free blocks. aiFreelist[0] is a list of free blocks of
** size mem5.szAtom. aiFreelist[1] holds blocks of size szAtom*2.
- ** and so forth.
+ ** aiFreelist[2] holds free blocks of size szAtom*4. And so forth.
*/
int aiFreelist[LOGMAX+1];
@@ -18710,9 +21744,7 @@ static void memsys5Link(int i, int iLogsize){
}
/*
-** If the STATIC_MEM mutex is not already held, obtain it now. The mutex
-** will already be held (obtained by code in malloc.c) if
-** sqlite3GlobalConfig.bMemStat is true.
+** Obtain or release the mutex needed to access global data structures.
*/
static void memsys5Enter(void){
sqlite3_mutex_enter(mem5.mutex);
@@ -18722,17 +21754,15 @@ static void memsys5Leave(void){
}
/*
-** Return the size of an outstanding allocation, in bytes. The
-** size returned omits the 8-byte header overhead. This only
-** works for chunks that are currently checked out.
+** Return the size of an outstanding allocation, in bytes.
+** This only works for chunks that are currently checked out.
*/
static int memsys5Size(void *p){
- int iSize = 0;
- if( p ){
- int i = (int)(((u8 *)p-mem5.zPool)/mem5.szAtom);
- assert( i>=0 && i<mem5.nBlock );
- iSize = mem5.szAtom * (1 << (mem5.aCtrl[i]&CTRL_LOGSIZE));
- }
+ int iSize, i;
+ assert( p!=0 );
+ i = (int)(((u8 *)p-mem5.zPool)/mem5.szAtom);
+ assert( i>=0 && i<mem5.nBlock );
+ iSize = mem5.szAtom * (1 << (mem5.aCtrl[i]&CTRL_LOGSIZE));
return iSize;
}
@@ -18755,21 +21785,20 @@ static void *memsys5MallocUnsafe(int nByte){
/* nByte must be a positive */
assert( nByte>0 );
+ /* No more than 1GiB per allocation */
+ if( nByte > 0x40000000 ) return 0;
+
+#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST)
/* Keep track of the maximum allocation request. Even unfulfilled
** requests are counted */
if( (u32)nByte>mem5.maxRequest ){
mem5.maxRequest = nByte;
}
+#endif
- /* Abort if the requested allocation size is larger than the largest
- ** power of two that we can represent using 32-bit signed integers.
- */
- if( nByte > 0x40000000 ){
- return 0;
- }
/* Round nByte up to the next valid power of two */
- for(iFullSz=mem5.szAtom, iLogsize=0; iFullSz<nByte; iFullSz *= 2, iLogsize++){}
+ for(iFullSz=mem5.szAtom,iLogsize=0; iFullSz<nByte; iFullSz*=2,iLogsize++){}
/* Make sure mem5.aiFreelist[iLogsize] contains at least one free
** block. If not, then split a block of the next larger power of
@@ -18793,6 +21822,7 @@ static void *memsys5MallocUnsafe(int nByte){
}
mem5.aCtrl[i] = iLogsize;
+#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST)
/* Update allocator performance statistics. */
mem5.nAlloc++;
mem5.totalAlloc += iFullSz;
@@ -18801,6 +21831,7 @@ static void *memsys5MallocUnsafe(int nByte){
mem5.currentOut += iFullSz;
if( mem5.maxCount<mem5.currentCount ) mem5.maxCount = mem5.currentCount;
if( mem5.maxOut<mem5.currentOut ) mem5.maxOut = mem5.currentOut;
+#endif
#ifdef SQLITE_DEBUG
/* Make sure the allocated memory does not assume that it is set to zero
@@ -18835,23 +21866,26 @@ static void memsys5FreeUnsafe(void *pOld){
mem5.aCtrl[iBlock] |= CTRL_FREE;
mem5.aCtrl[iBlock+size-1] |= CTRL_FREE;
+
+#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST)
assert( mem5.currentCount>0 );
assert( mem5.currentOut>=(size*mem5.szAtom) );
mem5.currentCount--;
mem5.currentOut -= size*mem5.szAtom;
assert( mem5.currentOut>0 || mem5.currentCount==0 );
assert( mem5.currentCount>0 || mem5.currentOut==0 );
+#endif
mem5.aCtrl[iBlock] = CTRL_FREE | iLogsize;
while( ALWAYS(iLogsize<LOGMAX) ){
int iBuddy;
if( (iBlock>>iLogsize) & 1 ){
iBuddy = iBlock - size;
+ assert( iBuddy>=0 );
}else{
iBuddy = iBlock + size;
+ if( iBuddy>=mem5.nBlock ) break;
}
- assert( iBuddy>=0 );
- if( (iBuddy+(1<<iLogsize))>mem5.nBlock ) break;
if( mem5.aCtrl[iBuddy]!=(CTRL_FREE | iLogsize) ) break;
memsys5Unlink(iBuddy, iLogsize);
iLogsize++;
@@ -18926,13 +21960,11 @@ static void *memsys5Realloc(void *pPrior, int nBytes){
if( nBytes<=nOld ){
return pPrior;
}
- memsys5Enter();
- p = memsys5MallocUnsafe(nBytes);
+ p = memsys5Malloc(nBytes);
if( p ){
memcpy(p, pPrior, nOld);
- memsys5FreeUnsafe(pPrior);
+ memsys5Free(pPrior);
}
- memsys5Leave();
return p;
}
@@ -19119,6 +22151,7 @@ SQLITE_PRIVATE const sqlite3_mem_methods *sqlite3MemGetMemsys5(void){
**
** This file contains code that is common across all mutex implementations.
*/
+/* #include "sqliteInt.h" */
#if defined(SQLITE_DEBUG) && !defined(SQLITE_MUTEX_OMIT)
/*
@@ -19127,7 +22160,7 @@ SQLITE_PRIVATE const sqlite3_mem_methods *sqlite3MemGetMemsys5(void){
** allocate a mutex while the system is uninitialized.
*/
static SQLITE_WSD int mutexIsInit = 0;
-#endif /* SQLITE_DEBUG */
+#endif /* SQLITE_DEBUG && !defined(SQLITE_MUTEX_OMIT) */
#ifndef SQLITE_MUTEX_OMIT
@@ -19150,11 +22183,18 @@ SQLITE_PRIVATE int sqlite3MutexInit(void){
}else{
pFrom = sqlite3NoopMutex();
}
- memcpy(pTo, pFrom, offsetof(sqlite3_mutex_methods, xMutexAlloc));
- memcpy(&pTo->xMutexFree, &pFrom->xMutexFree,
- sizeof(*pTo) - offsetof(sqlite3_mutex_methods, xMutexFree));
+ pTo->xMutexInit = pFrom->xMutexInit;
+ pTo->xMutexEnd = pFrom->xMutexEnd;
+ pTo->xMutexFree = pFrom->xMutexFree;
+ pTo->xMutexEnter = pFrom->xMutexEnter;
+ pTo->xMutexTry = pFrom->xMutexTry;
+ pTo->xMutexLeave = pFrom->xMutexLeave;
+ pTo->xMutexHeld = pFrom->xMutexHeld;
+ pTo->xMutexNotheld = pFrom->xMutexNotheld;
+ sqlite3MemoryBarrier();
pTo->xMutexAlloc = pFrom->xMutexAlloc;
}
+ assert( sqlite3GlobalConfig.mutex.xMutexInit );
rc = sqlite3GlobalConfig.mutex.xMutexInit();
#ifdef SQLITE_DEBUG
@@ -19189,6 +22229,7 @@ SQLITE_API sqlite3_mutex *SQLITE_STDCALL sqlite3_mutex_alloc(int id){
if( id<=SQLITE_MUTEX_RECURSIVE && sqlite3_initialize() ) return 0;
if( id>SQLITE_MUTEX_RECURSIVE && sqlite3MutexInit() ) return 0;
#endif
+ assert( sqlite3GlobalConfig.mutex.xMutexAlloc );
return sqlite3GlobalConfig.mutex.xMutexAlloc(id);
}
@@ -19197,6 +22238,7 @@ SQLITE_PRIVATE sqlite3_mutex *sqlite3MutexAlloc(int id){
return 0;
}
assert( GLOBAL(int, mutexIsInit) );
+ assert( sqlite3GlobalConfig.mutex.xMutexAlloc );
return sqlite3GlobalConfig.mutex.xMutexAlloc(id);
}
@@ -19205,6 +22247,7 @@ SQLITE_PRIVATE sqlite3_mutex *sqlite3MutexAlloc(int id){
*/
SQLITE_API void SQLITE_STDCALL sqlite3_mutex_free(sqlite3_mutex *p){
if( p ){
+ assert( sqlite3GlobalConfig.mutex.xMutexFree );
sqlite3GlobalConfig.mutex.xMutexFree(p);
}
}
@@ -19215,6 +22258,7 @@ SQLITE_API void SQLITE_STDCALL sqlite3_mutex_free(sqlite3_mutex *p){
*/
SQLITE_API void SQLITE_STDCALL sqlite3_mutex_enter(sqlite3_mutex *p){
if( p ){
+ assert( sqlite3GlobalConfig.mutex.xMutexEnter );
sqlite3GlobalConfig.mutex.xMutexEnter(p);
}
}
@@ -19226,6 +22270,7 @@ SQLITE_API void SQLITE_STDCALL sqlite3_mutex_enter(sqlite3_mutex *p){
SQLITE_API int SQLITE_STDCALL sqlite3_mutex_try(sqlite3_mutex *p){
int rc = SQLITE_OK;
if( p ){
+ assert( sqlite3GlobalConfig.mutex.xMutexTry );
return sqlite3GlobalConfig.mutex.xMutexTry(p);
}
return rc;
@@ -19239,6 +22284,7 @@ SQLITE_API int SQLITE_STDCALL sqlite3_mutex_try(sqlite3_mutex *p){
*/
SQLITE_API void SQLITE_STDCALL sqlite3_mutex_leave(sqlite3_mutex *p){
if( p ){
+ assert( sqlite3GlobalConfig.mutex.xMutexLeave );
sqlite3GlobalConfig.mutex.xMutexLeave(p);
}
}
@@ -19249,9 +22295,11 @@ SQLITE_API void SQLITE_STDCALL sqlite3_mutex_leave(sqlite3_mutex *p){
** intended for use inside assert() statements.
*/
SQLITE_API int SQLITE_STDCALL sqlite3_mutex_held(sqlite3_mutex *p){
+ assert( p==0 || sqlite3GlobalConfig.mutex.xMutexHeld );
return p==0 || sqlite3GlobalConfig.mutex.xMutexHeld(p);
}
SQLITE_API int SQLITE_STDCALL sqlite3_mutex_notheld(sqlite3_mutex *p){
+ assert( p==0 || sqlite3GlobalConfig.mutex.xMutexNotheld );
return p==0 || sqlite3GlobalConfig.mutex.xMutexNotheld(p);
}
#endif
@@ -19287,6 +22335,7 @@ SQLITE_API int SQLITE_STDCALL sqlite3_mutex_notheld(sqlite3_mutex *p){
** that does error checking on mutexes to make sure they are being
** called correctly.
*/
+/* #include "sqliteInt.h" */
#ifndef SQLITE_MUTEX_OMIT
@@ -19368,7 +22417,7 @@ static int debugMutexEnd(void){ return SQLITE_OK; }
** that means that a mutex could not be allocated.
*/
static sqlite3_mutex *debugMutexAlloc(int id){
- static sqlite3_debug_mutex aStatic[SQLITE_MUTEX_STATIC_APP3 - 1];
+ static sqlite3_debug_mutex aStatic[SQLITE_MUTEX_STATIC_VFS3 - 1];
sqlite3_debug_mutex *pNew = 0;
switch( id ){
case SQLITE_MUTEX_FAST:
@@ -19490,6 +22539,7 @@ SQLITE_PRIVATE sqlite3_mutex_methods const *sqlite3DefaultMutex(void){
*************************************************************************
** This file contains the C functions that implement mutexes for pthreads
*/
+/* #include "sqliteInt.h" */
/*
** The code in this file is only used if we are compiling threadsafe
@@ -19528,7 +22578,9 @@ struct sqlite3_mutex {
#endif
};
#if SQLITE_MUTEX_NREF
-#define SQLITE3_MUTEX_INITIALIZER { PTHREAD_MUTEX_INITIALIZER, 0, 0, (pthread_t)0, 0 }
+#define SQLITE3_MUTEX_INITIALIZER {PTHREAD_MUTEX_INITIALIZER,0,0,(pthread_t)0,0}
+#elif defined(SQLITE_ENABLE_API_ARMOR)
+#define SQLITE3_MUTEX_INITIALIZER { PTHREAD_MUTEX_INITIALIZER, 0 }
#else
#define SQLITE3_MUTEX_INITIALIZER { PTHREAD_MUTEX_INITIALIZER }
#endif
@@ -19559,6 +22611,19 @@ static int pthreadMutexNotheld(sqlite3_mutex *p){
#endif
/*
+** Try to provide a memory barrier operation, needed for initialization
+** and also for the implementation of xShmBarrier in the VFS in cases
+** where SQLite is compiled without mutexes.
+*/
+SQLITE_PRIVATE void sqlite3MemoryBarrier(void){
+#if defined(SQLITE_MEMORY_BARRIER)
+ SQLITE_MEMORY_BARRIER;
+#elif defined(__GNUC__) && GCC_VERSION>=4001000
+ __sync_synchronize();
+#endif
+}
+
+/*
** Initialize and deinitialize the mutex subsystem.
*/
static int pthreadMutexInit(void){ return SQLITE_OK; }
@@ -19583,6 +22648,9 @@ static int pthreadMutexEnd(void){ return SQLITE_OK; }
** <li> SQLITE_MUTEX_STATIC_APP1
** <li> SQLITE_MUTEX_STATIC_APP2
** <li> SQLITE_MUTEX_STATIC_APP3
+** <li> SQLITE_MUTEX_STATIC_VFS1
+** <li> SQLITE_MUTEX_STATIC_VFS2
+** <li> SQLITE_MUTEX_STATIC_VFS3
** </ul>
**
** The first two constants cause sqlite3_mutex_alloc() to create
@@ -19619,6 +22687,9 @@ static sqlite3_mutex *pthreadMutexAlloc(int iType){
SQLITE3_MUTEX_INITIALIZER,
SQLITE3_MUTEX_INITIALIZER,
SQLITE3_MUTEX_INITIALIZER,
+ SQLITE3_MUTEX_INITIALIZER,
+ SQLITE3_MUTEX_INITIALIZER,
+ SQLITE3_MUTEX_INITIALIZER,
SQLITE3_MUTEX_INITIALIZER
};
sqlite3_mutex *p;
@@ -19858,6 +22929,7 @@ SQLITE_PRIVATE sqlite3_mutex_methods const *sqlite3DefaultMutex(void){
*************************************************************************
** This file contains the C functions that implement mutexes for Win32.
*/
+/* #include "sqliteInt.h" */
#if SQLITE_OS_WIN
/*
@@ -19902,8 +22974,8 @@ SQLITE_PRIVATE sqlite3_mutex_methods const *sqlite3DefaultMutex(void){
*/
#ifdef SQLITE_PERFORMANCE_TRACE
-/*
-** hwtime.h contains inline assembler code for implementing
+/*
+** hwtime.h contains inline assembler code for implementing
** high-performance timing routines.
*/
/************** Include hwtime.h in the middle of os_common.h ****************/
@@ -20013,14 +23085,14 @@ static sqlite_uint64 g_elapsed;
** of code will give us the ability to simulate a disk I/O error. This
** is used for testing the I/O recovery logic.
*/
-#ifdef SQLITE_TEST
-SQLITE_API int sqlite3_io_error_hit = 0; /* Total number of I/O Errors */
-SQLITE_API int sqlite3_io_error_hardhit = 0; /* Number of non-benign errors */
-SQLITE_API int sqlite3_io_error_pending = 0; /* Count down to first I/O error */
-SQLITE_API int sqlite3_io_error_persist = 0; /* True if I/O errors persist */
-SQLITE_API int sqlite3_io_error_benign = 0; /* True if errors are benign */
-SQLITE_API int sqlite3_diskfull_pending = 0;
-SQLITE_API int sqlite3_diskfull = 0;
+#if defined(SQLITE_TEST)
+SQLITE_API extern int sqlite3_io_error_hit;
+SQLITE_API extern int sqlite3_io_error_hardhit;
+SQLITE_API extern int sqlite3_io_error_pending;
+SQLITE_API extern int sqlite3_io_error_persist;
+SQLITE_API extern int sqlite3_io_error_benign;
+SQLITE_API extern int sqlite3_diskfull_pending;
+SQLITE_API extern int sqlite3_diskfull;
#define SimulateIOErrorBenign(X) sqlite3_io_error_benign=(X)
#define SimulateIOError(CODE) \
if( (sqlite3_io_error_persist && sqlite3_io_error_hit) \
@@ -20046,17 +23118,17 @@ static void local_ioerr(){
#define SimulateIOErrorBenign(X)
#define SimulateIOError(A)
#define SimulateDiskfullError(A)
-#endif
+#endif /* defined(SQLITE_TEST) */
/*
** When testing, keep a count of the number of open files.
*/
-#ifdef SQLITE_TEST
-SQLITE_API int sqlite3_open_file_count = 0;
+#if defined(SQLITE_TEST)
+SQLITE_API extern int sqlite3_open_file_count;
#define OpenCounter(X) sqlite3_open_file_count+=(X)
#else
#define OpenCounter(X)
-#endif
+#endif /* defined(SQLITE_TEST) */
#endif /* !defined(_OS_COMMON_H_) */
@@ -20214,6 +23286,24 @@ static int winMutexNotheld(sqlite3_mutex *p){
#endif
/*
+** Try to provide a memory barrier operation, needed for initialization
+** and also for the xShmBarrier method of the VFS in cases when SQLite is
+** compiled without mutexes (SQLITE_THREADSAFE=0).
+*/
+SQLITE_PRIVATE void sqlite3MemoryBarrier(void){
+#if defined(SQLITE_MEMORY_BARRIER)
+ SQLITE_MEMORY_BARRIER;
+#elif defined(__GNUC__)
+ __sync_synchronize();
+#elif !defined(SQLITE_DISABLE_INTRINSIC) && \
+ defined(_MSC_VER) && _MSC_VER>=1300
+ _ReadWriteBarrier();
+#elif defined(MemoryBarrier)
+ MemoryBarrier();
+#endif
+}
+
+/*
** Initialize and deinitialize the mutex subsystem.
*/
static sqlite3_mutex winMutex_staticMutexes[] = {
@@ -20225,6 +23315,9 @@ static sqlite3_mutex winMutex_staticMutexes[] = {
SQLITE3_MUTEX_INITIALIZER,
SQLITE3_MUTEX_INITIALIZER,
SQLITE3_MUTEX_INITIALIZER,
+ SQLITE3_MUTEX_INITIALIZER,
+ SQLITE3_MUTEX_INITIALIZER,
+ SQLITE3_MUTEX_INITIALIZER,
SQLITE3_MUTEX_INITIALIZER
};
@@ -20296,6 +23389,9 @@ static int winMutexEnd(void){
** <li> SQLITE_MUTEX_STATIC_APP1
** <li> SQLITE_MUTEX_STATIC_APP2
** <li> SQLITE_MUTEX_STATIC_APP3
+** <li> SQLITE_MUTEX_STATIC_VFS1
+** <li> SQLITE_MUTEX_STATIC_VFS2
+** <li> SQLITE_MUTEX_STATIC_VFS3
** </ul>
**
** The first two constants cause sqlite3_mutex_alloc() to create
@@ -20527,6 +23623,7 @@ SQLITE_PRIVATE sqlite3_mutex_methods const *sqlite3DefaultMutex(void){
**
** Memory allocation functions used throughout sqlite.
*/
+/* #include "sqliteInt.h" */
/* #include <stdarg.h> */
/*
@@ -20559,16 +23656,7 @@ typedef struct ScratchFreeslot {
*/
static SQLITE_WSD struct Mem0Global {
sqlite3_mutex *mutex; /* Mutex to serialize access */
-
- /*
- ** The alarm callback and its arguments. The mem0.mutex lock will
- ** be held while the callback is running. Recursive calls into
- ** the memory subsystem are allowed, but no new callbacks will be
- ** issued.
- */
- sqlite3_int64 alarmThreshold;
- void (*alarmCallback)(void*, sqlite3_int64,int);
- void *alarmArg;
+ sqlite3_int64 alarmThreshold; /* The soft heap limit */
/*
** Pointers to the end of sqlite3GlobalConfig.pScratch memory
@@ -20585,7 +23673,7 @@ static SQLITE_WSD struct Mem0Global {
** sqlite3_soft_heap_limit() setting.
*/
int nearlyFull;
-} mem0 = { 0, 0, 0, 0, 0, 0, 0, 0 };
+} mem0 = { 0, 0, 0, 0, 0, 0 };
#define mem0 GLOBAL(struct Mem0Global, mem0)
@@ -20596,50 +23684,21 @@ 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.
-*/
-static void softHeapLimitEnforcer(
- void *NotUsed,
- sqlite3_int64 NotUsed2,
- int allocSize
-){
- UNUSED_PARAMETER2(NotUsed, NotUsed2);
- sqlite3_release_memory(allocSize);
-}
-
-/*
-** Change the alarm callback
-*/
-static int sqlite3MemoryAlarm(
- void(*xCallback)(void *pArg, sqlite3_int64 used,int N),
- void *pArg,
- sqlite3_int64 iThreshold
-){
- sqlite3_int64 nUsed;
- sqlite3_mutex_enter(mem0.mutex);
- mem0.alarmCallback = xCallback;
- mem0.alarmArg = pArg;
- mem0.alarmThreshold = iThreshold;
- nUsed = sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED);
- mem0.nearlyFull = (iThreshold>0 && iThreshold<=nUsed);
- sqlite3_mutex_leave(mem0.mutex);
- return SQLITE_OK;
-}
-
#ifndef SQLITE_OMIT_DEPRECATED
/*
-** Deprecated external interface. Internal/core SQLite code
-** should call sqlite3MemoryAlarm.
+** Deprecated external interface. It used to set an alarm callback
+** that was invoked when memory usage grew too large. Now it is a
+** no-op.
*/
SQLITE_API int SQLITE_STDCALL sqlite3_memory_alarm(
void(*xCallback)(void *pArg, sqlite3_int64 used,int N),
void *pArg,
sqlite3_int64 iThreshold
){
- return sqlite3MemoryAlarm(xCallback, pArg, iThreshold);
+ (void)xCallback;
+ (void)pArg;
+ (void)iThreshold;
+ return SQLITE_OK;
}
#endif
@@ -20650,19 +23709,21 @@ SQLITE_API int SQLITE_STDCALL sqlite3_memory_alarm(
SQLITE_API sqlite3_int64 SQLITE_STDCALL sqlite3_soft_heap_limit64(sqlite3_int64 n){
sqlite3_int64 priorLimit;
sqlite3_int64 excess;
+ sqlite3_int64 nUsed;
#ifndef SQLITE_OMIT_AUTOINIT
int rc = sqlite3_initialize();
if( rc ) return -1;
#endif
sqlite3_mutex_enter(mem0.mutex);
priorLimit = mem0.alarmThreshold;
- sqlite3_mutex_leave(mem0.mutex);
- if( n<0 ) return priorLimit;
- if( n>0 ){
- sqlite3MemoryAlarm(softHeapLimitEnforcer, 0, n);
- }else{
- sqlite3MemoryAlarm(0, 0, 0);
+ if( n<0 ){
+ sqlite3_mutex_leave(mem0.mutex);
+ return priorLimit;
}
+ mem0.alarmThreshold = n;
+ nUsed = sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED);
+ mem0.nearlyFull = (n>0 && n<=nUsed);
+ sqlite3_mutex_leave(mem0.mutex);
excess = sqlite3_memory_used() - n;
if( excess>0 ) sqlite3_release_memory((int)(excess & 0x7fffffff));
return priorLimit;
@@ -20681,9 +23742,7 @@ SQLITE_PRIVATE int sqlite3MallocInit(void){
sqlite3MemSetDefault();
}
memset(&mem0, 0, sizeof(mem0));
- if( sqlite3GlobalConfig.bCoreMutex ){
- mem0.mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MEM);
- }
+ mem0.mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MEM);
if( sqlite3GlobalConfig.pScratch && sqlite3GlobalConfig.szScratch>=100
&& sqlite3GlobalConfig.nScratch>0 ){
int i, n, sz;
@@ -20707,10 +23766,9 @@ SQLITE_PRIVATE int sqlite3MallocInit(void){
sqlite3GlobalConfig.nScratch = 0;
}
if( sqlite3GlobalConfig.pPage==0 || sqlite3GlobalConfig.szPage<512
- || sqlite3GlobalConfig.nPage<1 ){
+ || sqlite3GlobalConfig.nPage<=0 ){
sqlite3GlobalConfig.pPage = 0;
sqlite3GlobalConfig.szPage = 0;
- sqlite3GlobalConfig.nPage = 0;
}
rc = sqlite3GlobalConfig.m.xInit(sqlite3GlobalConfig.m.pAppData);
if( rc!=SQLITE_OK ) memset(&mem0, 0, sizeof(mem0));
@@ -20740,10 +23798,8 @@ SQLITE_PRIVATE void sqlite3MallocEnd(void){
** Return the amount of memory currently checked out.
*/
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);
- res = (sqlite3_int64)n; /* Work around bug in Borland C. Ticket #3216 */
+ sqlite3_int64 res, mx;
+ sqlite3_status64(SQLITE_STATUS_MEMORY_USED, &res, &mx, 0);
return res;
}
@@ -20753,30 +23809,19 @@ SQLITE_API sqlite3_int64 SQLITE_STDCALL sqlite3_memory_used(void){
** or since the most recent reset.
*/
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);
- res = (sqlite3_int64)mx; /* Work around bug in Borland C. Ticket #3216 */
- return res;
+ sqlite3_int64 res, mx;
+ sqlite3_status64(SQLITE_STATUS_MEMORY_USED, &res, &mx, resetFlag);
+ return mx;
}
/*
** Trigger the alarm
*/
static void sqlite3MallocAlarm(int nByte){
- void (*xCallback)(void*,sqlite3_int64,int);
- sqlite3_int64 nowUsed;
- void *pArg;
- if( mem0.alarmCallback==0 ) return;
- xCallback = mem0.alarmCallback;
- nowUsed = sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED);
- pArg = mem0.alarmArg;
- mem0.alarmCallback = 0;
+ if( mem0.alarmThreshold<=0 ) return;
sqlite3_mutex_leave(mem0.mutex);
- xCallback(pArg, nowUsed, nByte);
+ sqlite3_release_memory(nByte);
sqlite3_mutex_enter(mem0.mutex);
- mem0.alarmCallback = xCallback;
- mem0.alarmArg = pArg;
}
/*
@@ -20788,8 +23833,8 @@ static int mallocWithAlarm(int n, void **pp){
void *p;
assert( sqlite3_mutex_held(mem0.mutex) );
nFull = sqlite3GlobalConfig.m.xRoundup(n);
- sqlite3StatusSet(SQLITE_STATUS_MALLOC_SIZE, n);
- if( mem0.alarmCallback!=0 ){
+ sqlite3StatusHighwater(SQLITE_STATUS_MALLOC_SIZE, n);
+ if( mem0.alarmThreshold>0 ){
sqlite3_int64 nUsed = sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED);
if( nUsed >= mem0.alarmThreshold - nFull ){
mem0.nearlyFull = 1;
@@ -20800,7 +23845,7 @@ static int mallocWithAlarm(int n, void **pp){
}
p = sqlite3GlobalConfig.m.xMalloc(nFull);
#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
- if( p==0 && mem0.alarmCallback ){
+ if( p==0 && mem0.alarmThreshold>0 ){
sqlite3MallocAlarm(nFull);
p = sqlite3GlobalConfig.m.xMalloc(nFull);
}
@@ -20880,7 +23925,7 @@ SQLITE_PRIVATE void *sqlite3ScratchMalloc(int n){
assert( n>0 );
sqlite3_mutex_enter(mem0.mutex);
- sqlite3StatusSet(SQLITE_STATUS_SCRATCH_SIZE, n);
+ sqlite3StatusHighwater(SQLITE_STATUS_SCRATCH_SIZE, n);
if( mem0.nScratchFree && sqlite3GlobalConfig.szScratch>=n ){
p = mem0.pScratchFree;
mem0.pScratchFree = mem0.pScratchFree->pNext;
@@ -20924,7 +23969,7 @@ SQLITE_PRIVATE void sqlite3ScratchFree(void *p){
scratchAllocOut--;
#endif
- if( p>=sqlite3GlobalConfig.pScratch && p<mem0.pScratchEnd ){
+ if( SQLITE_WITHIN(p, sqlite3GlobalConfig.pScratch, mem0.pScratchEnd) ){
/* Release memory from the SQLITE_CONFIG_SCRATCH allocation */
ScratchFreeslot *pSlot;
pSlot = (ScratchFreeslot*)p;
@@ -20960,7 +24005,7 @@ SQLITE_PRIVATE void sqlite3ScratchFree(void *p){
*/
#ifndef SQLITE_OMIT_LOOKASIDE
static int isLookaside(sqlite3 *db, void *p){
- return p>=db->lookaside.pStart && p<db->lookaside.pEnd;
+ return SQLITE_WITHIN(p, db->lookaside.pStart, db->lookaside.pEnd);
}
#else
#define isLookaside(A,B) 0
@@ -20975,25 +24020,27 @@ SQLITE_PRIVATE int sqlite3MallocSize(void *p){
return sqlite3GlobalConfig.m.xSize(p);
}
SQLITE_PRIVATE int sqlite3DbMallocSize(sqlite3 *db, void *p){
- if( db==0 ){
- assert( sqlite3MemdebugNoType(p, (u8)~MEMTYPE_HEAP) );
- assert( sqlite3MemdebugHasType(p, MEMTYPE_HEAP) );
- return sqlite3MallocSize(p);
- }else{
- assert( sqlite3_mutex_held(db->mutex) );
- if( isLookaside(db, p) ){
- return db->lookaside.sz;
+ assert( p!=0 );
+ if( db==0 || !isLookaside(db,p) ){
+#if SQLITE_DEBUG
+ if( db==0 ){
+ assert( sqlite3MemdebugNoType(p, (u8)~MEMTYPE_HEAP) );
+ assert( sqlite3MemdebugHasType(p, MEMTYPE_HEAP) );
}else{
assert( sqlite3MemdebugHasType(p, (MEMTYPE_LOOKASIDE|MEMTYPE_HEAP)) );
assert( sqlite3MemdebugNoType(p, (u8)~(MEMTYPE_LOOKASIDE|MEMTYPE_HEAP)) );
- return sqlite3GlobalConfig.m.xSize(p);
}
+#endif
+ return sqlite3GlobalConfig.m.xSize(p);
+ }else{
+ assert( sqlite3_mutex_held(db->mutex) );
+ return db->lookaside.sz;
}
}
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);
+ return p ? sqlite3GlobalConfig.m.xSize(p) : 0;
}
/*
@@ -21081,14 +24128,14 @@ SQLITE_PRIVATE void *sqlite3Realloc(void *pOld, u64 nBytes){
pNew = pOld;
}else if( sqlite3GlobalConfig.bMemstat ){
sqlite3_mutex_enter(mem0.mutex);
- sqlite3StatusSet(SQLITE_STATUS_MALLOC_SIZE, (int)nBytes);
+ sqlite3StatusHighwater(SQLITE_STATUS_MALLOC_SIZE, (int)nBytes);
nDiff = nNew - nOld;
if( sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED) >=
mem0.alarmThreshold-nDiff ){
sqlite3MallocAlarm(nDiff);
}
pNew = sqlite3GlobalConfig.m.xRealloc(pOld, nNew);
- if( pNew==0 && mem0.alarmCallback ){
+ if( pNew==0 && mem0.alarmThreshold>0 ){
sqlite3MallocAlarm((int)nBytes);
pNew = sqlite3GlobalConfig.m.xRealloc(pOld, nNew);
}
@@ -21139,16 +24186,31 @@ SQLITE_PRIVATE void *sqlite3MallocZero(u64 n){
** the mallocFailed flag in the connection pointer.
*/
SQLITE_PRIVATE void *sqlite3DbMallocZero(sqlite3 *db, u64 n){
- void *p = sqlite3DbMallocRaw(db, n);
- if( p ){
- memset(p, 0, (size_t)n);
- }
+ void *p;
+ testcase( db==0 );
+ p = sqlite3DbMallocRaw(db, n);
+ if( p ) memset(p, 0, (size_t)n);
+ return p;
+}
+
+
+/* Finish the work of sqlite3DbMallocRawNN for the unusual and
+** slower case when the allocation cannot be fulfilled using lookaside.
+*/
+static SQLITE_NOINLINE void *dbMallocRawFinish(sqlite3 *db, u64 n){
+ void *p;
+ assert( db!=0 );
+ p = sqlite3Malloc(n);
+ if( !p ) sqlite3OomFault(db);
+ sqlite3MemdebugSetType(p,
+ (db->lookaside.bDisable==0) ? MEMTYPE_LOOKASIDE : MEMTYPE_HEAP);
return p;
}
/*
-** Allocate and zero memory. If the allocation fails, make
-** the mallocFailed flag in the connection pointer.
+** Allocate memory, either lookaside (if possible) or heap.
+** If the allocation fails, set the mallocFailed flag in
+** the connection pointer.
**
** If db!=0 and db->mallocFailed is true (indicating a prior malloc
** failure on the same database connection) then always return 0.
@@ -21163,64 +24225,73 @@ SQLITE_PRIVATE void *sqlite3DbMallocZero(sqlite3 *db, u64 n){
**
** In other words, if a subsequent malloc (ex: "b") worked, it is assumed
** that all prior mallocs (ex: "a") worked too.
+**
+** The sqlite3MallocRawNN() variant guarantees that the "db" parameter is
+** not a NULL pointer.
*/
SQLITE_PRIVATE void *sqlite3DbMallocRaw(sqlite3 *db, u64 n){
void *p;
- assert( db==0 || sqlite3_mutex_held(db->mutex) );
- assert( db==0 || db->pnBytesFreed==0 );
+ if( db ) return sqlite3DbMallocRawNN(db, n);
+ p = sqlite3Malloc(n);
+ sqlite3MemdebugSetType(p, MEMTYPE_HEAP);
+ return p;
+}
+SQLITE_PRIVATE void *sqlite3DbMallocRawNN(sqlite3 *db, u64 n){
#ifndef SQLITE_OMIT_LOOKASIDE
- if( db ){
- LookasideSlot *pBuf;
- if( db->mallocFailed ){
- return 0;
- }
- if( db->lookaside.bEnabled ){
- if( n>db->lookaside.sz ){
- db->lookaside.anStat[1]++;
- }else if( (pBuf = db->lookaside.pFree)==0 ){
- db->lookaside.anStat[2]++;
- }else{
- db->lookaside.pFree = pBuf->pNext;
- db->lookaside.nOut++;
- db->lookaside.anStat[0]++;
- if( db->lookaside.nOut>db->lookaside.mxOut ){
- db->lookaside.mxOut = db->lookaside.nOut;
- }
- return (void*)pBuf;
+ LookasideSlot *pBuf;
+ assert( db!=0 );
+ assert( sqlite3_mutex_held(db->mutex) );
+ assert( db->pnBytesFreed==0 );
+ if( db->lookaside.bDisable==0 ){
+ assert( db->mallocFailed==0 );
+ if( n>db->lookaside.sz ){
+ db->lookaside.anStat[1]++;
+ }else if( (pBuf = db->lookaside.pFree)==0 ){
+ db->lookaside.anStat[2]++;
+ }else{
+ db->lookaside.pFree = pBuf->pNext;
+ db->lookaside.nOut++;
+ db->lookaside.anStat[0]++;
+ if( db->lookaside.nOut>db->lookaside.mxOut ){
+ db->lookaside.mxOut = db->lookaside.nOut;
}
+ return (void*)pBuf;
}
+ }else if( db->mallocFailed ){
+ return 0;
}
#else
- if( db && db->mallocFailed ){
+ assert( db!=0 );
+ assert( sqlite3_mutex_held(db->mutex) );
+ assert( db->pnBytesFreed==0 );
+ if( db->mallocFailed ){
return 0;
}
#endif
- p = sqlite3Malloc(n);
- if( !p && db ){
- db->mallocFailed = 1;
- }
- sqlite3MemdebugSetType(p,
- (db && db->lookaside.bEnabled) ? MEMTYPE_LOOKASIDE : MEMTYPE_HEAP);
- return p;
+ return dbMallocRawFinish(db, n);
}
+/* Forward declaration */
+static SQLITE_NOINLINE void *dbReallocFinish(sqlite3 *db, void *p, u64 n);
+
/*
** Resize the block of memory pointed to by p to n bytes. If the
** resize fails, set the mallocFailed flag in the connection object.
*/
SQLITE_PRIVATE void *sqlite3DbRealloc(sqlite3 *db, void *p, u64 n){
- void *pNew = 0;
assert( db!=0 );
+ if( p==0 ) return sqlite3DbMallocRawNN(db, n);
assert( sqlite3_mutex_held(db->mutex) );
+ if( isLookaside(db,p) && n<=db->lookaside.sz ) return p;
+ return dbReallocFinish(db, p, n);
+}
+static SQLITE_NOINLINE void *dbReallocFinish(sqlite3 *db, void *p, u64 n){
+ void *pNew = 0;
+ assert( db!=0 );
+ assert( p!=0 );
if( db->mallocFailed==0 ){
- if( p==0 ){
- return sqlite3DbMallocRaw(db, n);
- }
if( isLookaside(db, p) ){
- if( n<=db->lookaside.sz ){
- return p;
- }
- pNew = sqlite3DbMallocRaw(db, n);
+ pNew = sqlite3DbMallocRawNN(db, n);
if( pNew ){
memcpy(pNew, p, db->lookaside.sz);
sqlite3DbFree(db, p);
@@ -21231,10 +24302,10 @@ SQLITE_PRIVATE void *sqlite3DbRealloc(sqlite3 *db, void *p, u64 n){
sqlite3MemdebugSetType(p, MEMTYPE_HEAP);
pNew = sqlite3_realloc64(p, n);
if( !pNew ){
- db->mallocFailed = 1;
+ sqlite3OomFault(db);
}
sqlite3MemdebugSetType(pNew,
- (db->lookaside.bEnabled ? MEMTYPE_LOOKASIDE : MEMTYPE_HEAP));
+ (db->lookaside.bDisable==0 ? MEMTYPE_LOOKASIDE : MEMTYPE_HEAP));
}
}
return pNew;
@@ -21276,11 +24347,12 @@ SQLITE_PRIVATE char *sqlite3DbStrDup(sqlite3 *db, const char *z){
}
SQLITE_PRIVATE char *sqlite3DbStrNDup(sqlite3 *db, const char *z, u64 n){
char *zNew;
+ assert( db!=0 );
if( z==0 ){
return 0;
}
assert( (n&0x7fffffff)==n );
- zNew = sqlite3DbMallocRaw(db, n+1);
+ zNew = sqlite3DbMallocRawNN(db, n+1);
if( zNew ){
memcpy(zNew, z, (size_t)n);
zNew[n] = 0;
@@ -21289,28 +24361,52 @@ SQLITE_PRIVATE char *sqlite3DbStrNDup(sqlite3 *db, const char *z, u64 n){
}
/*
-** Create a string from the zFromat argument and the va_list that follows.
-** Store the string in memory obtained from sqliteMalloc() and make *pz
-** point to that string.
+** Free any prior content in *pz and replace it with a copy of zNew.
*/
-SQLITE_PRIVATE void sqlite3SetString(char **pz, sqlite3 *db, const char *zFormat, ...){
- va_list ap;
- char *z;
-
- va_start(ap, zFormat);
- z = sqlite3VMPrintf(db, zFormat, ap);
- va_end(ap);
+SQLITE_PRIVATE void sqlite3SetString(char **pz, sqlite3 *db, const char *zNew){
sqlite3DbFree(db, *pz);
- *pz = z;
+ *pz = sqlite3DbStrDup(db, zNew);
+}
+
+/*
+** Call this routine to record the fact that an OOM (out-of-memory) error
+** has happened. This routine will set db->mallocFailed, and also
+** temporarily disable the lookaside memory allocator and interrupt
+** any running VDBEs.
+*/
+SQLITE_PRIVATE void sqlite3OomFault(sqlite3 *db){
+ if( db->mallocFailed==0 && db->bBenignMalloc==0 ){
+ db->mallocFailed = 1;
+ if( db->nVdbeExec>0 ){
+ db->u1.isInterrupted = 1;
+ }
+ db->lookaside.bDisable++;
+ }
+}
+
+/*
+** This routine reactivates the memory allocator and clears the
+** db->mallocFailed flag as necessary.
+**
+** The memory allocator is not restarted if there are running
+** VDBEs.
+*/
+SQLITE_PRIVATE void sqlite3OomClear(sqlite3 *db){
+ if( db->mallocFailed && db->nVdbeExec==0 ){
+ db->mallocFailed = 0;
+ db->u1.isInterrupted = 0;
+ assert( db->lookaside.bDisable>0 );
+ db->lookaside.bDisable--;
+ }
}
/*
** Take actions at the end of an API call to indicate an OOM error
*/
static SQLITE_NOINLINE int apiOomError(sqlite3 *db){
- db->mallocFailed = 0;
+ sqlite3OomClear(db);
sqlite3Error(db, SQLITE_NOMEM);
- return SQLITE_NOMEM;
+ return SQLITE_NOMEM_BKPT;
}
/*
@@ -21322,17 +24418,16 @@ static SQLITE_NOINLINE int apiOomError(sqlite3 *db){
** function. However, if a malloc() failure has occurred since the previous
** invocation SQLITE_NOMEM is returned instead.
**
-** If the first argument, db, is not NULL and a malloc() error has occurred,
-** then the connection error-code (the value returned by sqlite3_errcode())
-** is set to SQLITE_NOMEM.
+** If an OOM as occurred, then the connection error-code (the value
+** returned by sqlite3_errcode()) is set to SQLITE_NOMEM.
*/
SQLITE_PRIVATE int sqlite3ApiExit(sqlite3* db, int rc){
- /* If the db handle is not NULL, then we must hold the connection handle
- ** mutex here. Otherwise the read (and possible write) of db->mallocFailed
+ /* If the db handle must hold the connection handle mutex here.
+ ** Otherwise the read (and possible write) of db->mallocFailed
** is unsafe, as is the call to sqlite3Error().
*/
- assert( !db || sqlite3_mutex_held(db->mutex) );
- if( db==0 ) return rc & 0xff;
+ assert( db!=0 );
+ assert( sqlite3_mutex_held(db->mutex) );
if( db->mallocFailed || rc==SQLITE_IOERR_NOMEM ){
return apiOomError(db);
}
@@ -21343,43 +24438,41 @@ SQLITE_PRIVATE int sqlite3ApiExit(sqlite3* db, int rc){
/************** Begin file printf.c ******************************************/
/*
** The "printf" code that follows dates from the 1980's. It is in
-** the public domain. The original comments are included here for
-** completeness. They are very out-of-date but might be useful as
-** an historical reference. Most of the "enhancements" have been backed
-** out so that the functionality is now the same as standard printf().
+** the public domain.
**
**************************************************************************
**
** This file contains code for a set of "printf"-like routines. These
** routines format strings much like the printf() from the standard C
** library, though the implementation here has enhancements to support
-** SQLlite.
+** SQLite.
*/
+/* #include "sqliteInt.h" */
/*
** Conversion types fall into various categories as defined by the
** following enumeration.
*/
-#define etRADIX 1 /* Integer types. %d, %x, %o, and so forth */
-#define etFLOAT 2 /* Floating point. %f */
-#define etEXP 3 /* Exponentional notation. %e and %E */
-#define etGENERIC 4 /* Floating or exponential, depending on exponent. %g */
-#define etSIZE 5 /* Return number of characters processed so far. %n */
-#define etSTRING 6 /* Strings. %s */
-#define etDYNSTRING 7 /* Dynamically allocated strings. %z */
-#define etPERCENT 8 /* Percent symbol. %% */
-#define etCHARX 9 /* Characters. %c */
+#define etRADIX 0 /* Integer types. %d, %x, %o, and so forth */
+#define etFLOAT 1 /* Floating point. %f */
+#define etEXP 2 /* Exponentional notation. %e and %E */
+#define etGENERIC 3 /* Floating or exponential, depending on exponent. %g */
+#define etSIZE 4 /* Return number of characters processed so far. %n */
+#define etSTRING 5 /* Strings. %s */
+#define etDYNSTRING 6 /* Dynamically allocated strings. %z */
+#define etPERCENT 7 /* Percent symbol. %% */
+#define etCHARX 8 /* Characters. %c */
/* The rest are extensions, not normally found in printf() */
-#define etSQLESCAPE 10 /* Strings with '\'' doubled. %q */
-#define etSQLESCAPE2 11 /* Strings with '\'' doubled and enclosed in '',
+#define etSQLESCAPE 9 /* Strings with '\'' doubled. %q */
+#define etSQLESCAPE2 10 /* Strings with '\'' doubled and enclosed in '',
NULL pointers replaced by SQL NULL. %Q */
-#define etTOKEN 12 /* a pointer to a Token structure */
-#define etSRCLIST 13 /* a pointer to a SrcList */
-#define etPOINTER 14 /* The %p conversion */
-#define etSQLESCAPE3 15 /* %w -> Strings with '\"' doubled */
-#define etORDINAL 16 /* %r -> 1st, 2nd, 3rd, 4th, etc. English only */
+#define etTOKEN 11 /* a pointer to a Token structure */
+#define etSRCLIST 12 /* a pointer to a SrcList */
+#define etPOINTER 13 /* The %p conversion */
+#define etSQLESCAPE3 14 /* %w -> Strings with '\"' doubled */
+#define etORDINAL 15 /* %r -> 1st, 2nd, 3rd, 4th, etc. English only */
-#define etINVALID 0 /* Any unrecognized conversion type */
+#define etINVALID 16 /* Any unrecognized conversion type */
/*
@@ -21516,7 +24609,6 @@ static char *getTextArg(PrintfArguments *p){
*/
SQLITE_PRIVATE void sqlite3VXPrintf(
StrAccum *pAccum, /* Accumulate results here */
- u32 bFlags, /* SQLITE_PRINTF_* flags */
const char *fmt, /* Format string */
va_list ap /* arguments */
){
@@ -21535,7 +24627,7 @@ SQLITE_PRIVATE void sqlite3VXPrintf(
etByte flag_long; /* True if "l" flag is present */
etByte flag_longlong; /* True if the "ll" flag is present */
etByte done; /* Loop termination flag */
- etByte xtype = 0; /* Conversion paradigm */
+ etByte xtype = etINVALID; /* Conversion paradigm */
u8 bArgList; /* True for SQLITE_PRINTF_SQLFUNC */
u8 useIntern; /* Ok to use internal conversions (ex: %T) */
char prefix; /* Prefix character. "+" or "-" or " " or '\0'. */
@@ -21556,11 +24648,11 @@ SQLITE_PRIVATE void sqlite3VXPrintf(
char buf[etBUFSIZE]; /* Conversion buffer */
bufpt = 0;
- if( bFlags ){
- if( (bArgList = (bFlags & SQLITE_PRINTF_SQLFUNC))!=0 ){
+ if( pAccum->printfFlags ){
+ if( (bArgList = (pAccum->printfFlags & SQLITE_PRINTF_SQLFUNC))!=0 ){
pArgList = va_arg(ap, PrintfArguments*);
}
- useIntern = bFlags & SQLITE_PRINTF_INTERNAL;
+ useIntern = pAccum->printfFlags & SQLITE_PRINTF_INTERNAL;
}else{
bArgList = useIntern = 0;
}
@@ -21615,6 +24707,12 @@ SQLITE_PRIVATE void sqlite3VXPrintf(
testcase( wx>0x7fffffff );
width = wx & 0x7fffffff;
}
+ assert( width>=0 );
+#ifdef SQLITE_PRINTF_PRECISION_LIMIT
+ if( width>SQLITE_PRINTF_PRECISION_LIMIT ){
+ width = SQLITE_PRINTF_PRECISION_LIMIT;
+ }
+#endif
/* Get the precision */
if( c=='.' ){
@@ -21641,6 +24739,14 @@ SQLITE_PRIVATE void sqlite3VXPrintf(
}else{
precision = -1;
}
+ assert( precision>=(-1) );
+#ifdef SQLITE_PRINTF_PRECISION_LIMIT
+ if( precision>SQLITE_PRINTF_PRECISION_LIMIT ){
+ precision = SQLITE_PRINTF_PRECISION_LIMIT;
+ }
+#endif
+
+
/* Get the conversion type modifier */
if( c=='l' ){
flag_long = 1;
@@ -21813,21 +24919,16 @@ SQLITE_PRIVATE void sqlite3VXPrintf(
if( realvalue>0.0 ){
LONGDOUBLE_TYPE scale = 1.0;
while( realvalue>=1e100*scale && exp<=350 ){ scale *= 1e100;exp+=100;}
- while( realvalue>=1e64*scale && exp<=350 ){ scale *= 1e64; exp+=64; }
- while( realvalue>=1e8*scale && exp<=350 ){ scale *= 1e8; exp+=8; }
+ while( realvalue>=1e10*scale && exp<=350 ){ scale *= 1e10; exp+=10; }
while( realvalue>=10.0*scale && exp<=350 ){ scale *= 10.0; exp++; }
realvalue /= scale;
while( realvalue<1e-8 ){ realvalue *= 1e8; exp-=8; }
while( realvalue<1.0 ){ realvalue *= 10.0; exp--; }
if( exp>350 ){
- if( prefix=='-' ){
- bufpt = "-Inf";
- }else if( prefix=='+' ){
- bufpt = "+Inf";
- }else{
- bufpt = "Inf";
- }
- length = sqlite3Strlen30(bufpt);
+ bufpt = buf;
+ buf[0] = prefix;
+ memcpy(buf+(prefix!=0),"Inf",4);
+ length = 3+(prefix!=0);
break;
}
}
@@ -21976,12 +25077,13 @@ SQLITE_PRIVATE void sqlite3VXPrintf(
case etDYNSTRING:
if( bArgList ){
bufpt = getTextArg(pArgList);
+ xtype = etSTRING;
}else{
bufpt = va_arg(ap,char*);
}
if( bufpt==0 ){
bufpt = "";
- }else if( xtype==etDYNSTRING && !bArgList ){
+ }else if( xtype==etDYNSTRING ){
zExtra = bufpt;
}
if( precision>=0 ){
@@ -21990,9 +25092,9 @@ SQLITE_PRIVATE void sqlite3VXPrintf(
length = sqlite3Strlen30(bufpt);
}
break;
- case etSQLESCAPE:
- case etSQLESCAPE2:
- case etSQLESCAPE3: {
+ case etSQLESCAPE: /* Escape ' characters */
+ case etSQLESCAPE2: /* Escape ' and enclose in '...' */
+ case etSQLESCAPE3: { /* Escape " characters */
int i, j, k, n, isnull;
int needQuote;
char ch;
@@ -22011,7 +25113,7 @@ SQLITE_PRIVATE void sqlite3VXPrintf(
if( ch==q ) n++;
}
needQuote = !isnull && xtype==etSQLESCAPE2;
- n += i + 1 + needQuote*2;
+ n += i + 3;
if( n>etBUFSIZE ){
bufpt = zExtra = sqlite3Malloc( n );
if( bufpt==0 ){
@@ -22075,7 +25177,7 @@ SQLITE_PRIVATE void sqlite3VXPrintf(
if( width>0 && flag_leftjustify ) sqlite3AppendChar(pAccum, width, ' ');
if( zExtra ){
- sqlite3_free(zExtra);
+ sqlite3DbFree(pAccum->db, zExtra);
zExtra = 0;
}
}/* End for loop over the format string */
@@ -22101,8 +25203,9 @@ static int sqlite3StrAccumEnlarge(StrAccum *p, int N){
setStrAccumError(p, STRACCUM_TOOBIG);
return N;
}else{
- char *zOld = (p->zText==p->zBase ? 0 : p->zText);
+ char *zOld = isMalloced(p) ? p->zText : 0;
i64 szNew = p->nChar;
+ assert( (p->zText==0 || p->zText==p->zBase)==!isMalloced(p) );
szNew += N + 1;
if( szNew+p->nChar<=p->mxAlloc ){
/* Force exponential buffer size growth as long as it does not overflow,
@@ -22123,9 +25226,10 @@ static int sqlite3StrAccumEnlarge(StrAccum *p, int N){
}
if( zNew ){
assert( p->zText!=0 || p->nChar==0 );
- if( zOld==0 && p->nChar>0 ) memcpy(zNew, p->zText, p->nChar);
+ if( !isMalloced(p) && p->nChar>0 ) memcpy(zNew, p->zText, p->nChar);
p->zText = zNew;
p->nAlloc = sqlite3DbMallocSize(p->db, zNew);
+ p->printfFlags |= SQLITE_PRINTF_MALLOCED;
}else{
sqlite3StrAccumReset(p);
setStrAccumError(p, STRACCUM_NOMEM);
@@ -22143,6 +25247,7 @@ SQLITE_PRIVATE void sqlite3AppendChar(StrAccum *p, int N, char c){
if( p->nChar+(i64)N >= p->nAlloc && (N = sqlite3StrAccumEnlarge(p, N))<=0 ){
return;
}
+ assert( (p->zText==p->zBase)==!isMalloced(p) );
while( (N--)>0 ) p->zText[p->nChar++] = c;
}
@@ -22160,6 +25265,7 @@ static void SQLITE_NOINLINE enlargeAndAppend(StrAccum *p, const char *z, int N){
memcpy(&p->zText[p->nChar], z, N);
p->nChar += N;
}
+ assert( (p->zText==0 || p->zText==p->zBase)==!isMalloced(p) );
}
/*
@@ -22195,11 +25301,13 @@ SQLITE_PRIVATE void sqlite3StrAccumAppendAll(StrAccum *p, const char *z){
*/
SQLITE_PRIVATE char *sqlite3StrAccumFinish(StrAccum *p){
if( p->zText ){
+ assert( (p->zText==p->zBase)==!isMalloced(p) );
p->zText[p->nChar] = 0;
- if( p->mxAlloc>0 && p->zText==p->zBase ){
+ if( p->mxAlloc>0 && !isMalloced(p) ){
p->zText = sqlite3DbMallocRaw(p->db, p->nChar+1 );
if( p->zText ){
memcpy(p->zText, p->zBase, p->nChar+1);
+ p->printfFlags |= SQLITE_PRINTF_MALLOCED;
}else{
setStrAccumError(p, STRACCUM_NOMEM);
}
@@ -22212,8 +25320,10 @@ SQLITE_PRIVATE char *sqlite3StrAccumFinish(StrAccum *p){
** Reset an StrAccum string. Reclaim all malloced memory.
*/
SQLITE_PRIVATE void sqlite3StrAccumReset(StrAccum *p){
- if( p->zText!=p->zBase ){
+ assert( (p->zText==0 || p->zText==p->zBase)==!isMalloced(p) );
+ if( isMalloced(p) ){
sqlite3DbFree(p->db, p->zText);
+ p->printfFlags &= ~SQLITE_PRINTF_MALLOCED;
}
p->zText = 0;
}
@@ -22239,6 +25349,7 @@ SQLITE_PRIVATE void sqlite3StrAccumInit(StrAccum *p, sqlite3 *db, char *zBase, i
p->nAlloc = n;
p->mxAlloc = mx;
p->accError = 0;
+ p->printfFlags = 0;
}
/*
@@ -22252,10 +25363,11 @@ SQLITE_PRIVATE char *sqlite3VMPrintf(sqlite3 *db, const char *zFormat, va_list a
assert( db!=0 );
sqlite3StrAccumInit(&acc, db, zBase, sizeof(zBase),
db->aLimit[SQLITE_LIMIT_LENGTH]);
- sqlite3VXPrintf(&acc, SQLITE_PRINTF_INTERNAL, zFormat, ap);
+ acc.printfFlags = SQLITE_PRINTF_INTERNAL;
+ sqlite3VXPrintf(&acc, zFormat, ap);
z = sqlite3StrAccumFinish(&acc);
if( acc.accError==STRACCUM_NOMEM ){
- db->mallocFailed = 1;
+ sqlite3OomFault(db);
}
return z;
}
@@ -22274,24 +25386,6 @@ SQLITE_PRIVATE char *sqlite3MPrintf(sqlite3 *db, const char *zFormat, ...){
}
/*
-** Like sqlite3MPrintf(), but call sqlite3DbFree() on zStr after formatting
-** the string and before returning. This routine is intended to be used
-** to modify an existing string. For example:
-**
-** x = sqlite3MPrintf(db, x, "prefix %s suffix", x);
-**
-*/
-SQLITE_PRIVATE char *sqlite3MAppendf(sqlite3 *db, char *zStr, const char *zFormat, ...){
- va_list ap;
- char *z;
- va_start(ap, zFormat);
- z = sqlite3VMPrintf(db, zFormat, ap);
- va_end(ap);
- sqlite3DbFree(db, zStr);
- return z;
-}
-
-/*
** Print into memory obtained from sqlite3_malloc(). Omit the internal
** %-conversion extensions.
*/
@@ -22310,7 +25404,7 @@ SQLITE_API char *SQLITE_STDCALL sqlite3_vmprintf(const char *zFormat, va_list ap
if( sqlite3_initialize() ) return 0;
#endif
sqlite3StrAccumInit(&acc, 0, zBase, sizeof(zBase), SQLITE_MAX_LENGTH);
- sqlite3VXPrintf(&acc, 0, zFormat, ap);
+ sqlite3VXPrintf(&acc, zFormat, ap);
z = sqlite3StrAccumFinish(&acc);
return z;
}
@@ -22355,7 +25449,7 @@ SQLITE_API char *SQLITE_STDCALL sqlite3_vsnprintf(int n, char *zBuf, const char
}
#endif
sqlite3StrAccumInit(&acc, 0, zBuf, n, 0);
- sqlite3VXPrintf(&acc, 0, zFormat, ap);
+ sqlite3VXPrintf(&acc, zFormat, ap);
return sqlite3StrAccumFinish(&acc);
}
SQLITE_API char *SQLITE_CDECL sqlite3_snprintf(int n, char *zBuf, const char *zFormat, ...){
@@ -22375,13 +25469,18 @@ SQLITE_API char *SQLITE_CDECL sqlite3_snprintf(int n, char *zBuf, const char *zF
** sqlite3_log() must render into a static buffer. It cannot dynamically
** allocate memory because it might be called while the memory allocator
** mutex is held.
+**
+** sqlite3VXPrintf() might ask for *temporary* memory allocations for
+** certain format characters (%q) or for very large precisions or widths.
+** Care must be taken that any sqlite3_log() calls that occur while the
+** memory mutex is held do not use these mechanisms.
*/
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, 0, zMsg, sizeof(zMsg), 0);
- sqlite3VXPrintf(&acc, 0, zFormat, ap);
+ sqlite3VXPrintf(&acc, zFormat, ap);
sqlite3GlobalConfig.xLog(sqlite3GlobalConfig.pLogArg, iErrCode,
sqlite3StrAccumFinish(&acc));
}
@@ -22410,7 +25509,7 @@ SQLITE_PRIVATE void sqlite3DebugPrintf(const char *zFormat, ...){
char zBuf[500];
sqlite3StrAccumInit(&acc, 0, zBuf, sizeof(zBuf), 0);
va_start(ap,zFormat);
- sqlite3VXPrintf(&acc, 0, zFormat, ap);
+ sqlite3VXPrintf(&acc, zFormat, ap);
va_end(ap);
sqlite3StrAccumFinish(&acc);
fprintf(stdout,"%s", zBuf);
@@ -22418,22 +25517,47 @@ SQLITE_PRIVATE void sqlite3DebugPrintf(const char *zFormat, ...){
}
#endif
-#ifdef SQLITE_DEBUG
-/*************************************************************************
-** Routines for implementing the "TreeView" display of hierarchical
-** data structures for debugging.
+
+/*
+** variable-argument wrapper around sqlite3VXPrintf(). The bFlags argument
+** can contain the bit SQLITE_PRINTF_INTERNAL enable internal formats.
+*/
+SQLITE_PRIVATE void sqlite3XPrintf(StrAccum *p, const char *zFormat, ...){
+ va_list ap;
+ va_start(ap,zFormat);
+ sqlite3VXPrintf(p, zFormat, ap);
+ va_end(ap);
+}
+
+/************** End of printf.c **********************************************/
+/************** Begin file treeview.c ****************************************/
+/*
+** 2015-06-08
+**
+** 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.
+**
+*************************************************************************
**
-** The main entry points (coded elsewhere) are:
-** sqlite3TreeViewExpr(0, pExpr, 0);
-** sqlite3TreeViewExprList(0, pList, 0, 0);
-** sqlite3TreeViewSelect(0, pSelect, 0);
-** Insert calls to those routines while debugging in order to display
-** a diagram of Expr, ExprList, and Select objects.
+** This file contains C code to implement the TreeView debugging routines.
+** These routines print a parse tree to standard output for debugging and
+** analysis.
**
+** The interfaces in this file is only available when compiling
+** with SQLITE_DEBUG.
*/
-/* Add a new subitem to the tree. The moreToFollow flag indicates that this
-** is not the last item in the tree. */
-SQLITE_PRIVATE TreeView *sqlite3TreeViewPush(TreeView *p, u8 moreToFollow){
+/* #include "sqliteInt.h" */
+#ifdef SQLITE_DEBUG
+
+/*
+** Add a new subitem to the tree. The moreToFollow flag indicates that this
+** is not the last item in the tree.
+*/
+static TreeView *sqlite3TreeViewPush(TreeView *p, u8 moreToFollow){
if( p==0 ){
p = sqlite3_malloc64( sizeof(*p) );
if( p==0 ) return 0;
@@ -22445,15 +25569,21 @@ SQLITE_PRIVATE TreeView *sqlite3TreeViewPush(TreeView *p, u8 moreToFollow){
if( p->iLevel<sizeof(p->bLine) ) p->bLine[p->iLevel] = moreToFollow;
return p;
}
-/* Finished with one layer of the tree */
-SQLITE_PRIVATE void sqlite3TreeViewPop(TreeView *p){
+
+/*
+** Finished with one layer of the tree
+*/
+static void sqlite3TreeViewPop(TreeView *p){
if( p==0 ) return;
p->iLevel--;
if( p->iLevel<0 ) sqlite3_free(p);
}
-/* Generate a single line of output for the tree, with a prefix that contains
-** all the appropriate tree lines */
-SQLITE_PRIVATE void sqlite3TreeViewLine(TreeView *p, const char *zFormat, ...){
+
+/*
+** Generate a single line of output for the tree, with a prefix that contains
+** all the appropriate tree lines
+*/
+static void sqlite3TreeViewLine(TreeView *p, const char *zFormat, ...){
va_list ap;
int i;
StrAccum acc;
@@ -22466,31 +25596,436 @@ SQLITE_PRIVATE void sqlite3TreeViewLine(TreeView *p, const char *zFormat, ...){
sqlite3StrAccumAppend(&acc, p->bLine[i] ? "|-- " : "'-- ", 4);
}
va_start(ap, zFormat);
- sqlite3VXPrintf(&acc, 0, zFormat, ap);
+ sqlite3VXPrintf(&acc, zFormat, ap);
va_end(ap);
if( zBuf[acc.nChar-1]!='\n' ) sqlite3StrAccumAppend(&acc, "\n", 1);
sqlite3StrAccumFinish(&acc);
fprintf(stdout,"%s", zBuf);
fflush(stdout);
}
-/* Shorthand for starting a new tree item that consists of a single label */
-SQLITE_PRIVATE void sqlite3TreeViewItem(TreeView *p, const char *zLabel, u8 moreToFollow){
- p = sqlite3TreeViewPush(p, moreToFollow);
+
+/*
+** Shorthand for starting a new tree item that consists of a single label
+*/
+static void sqlite3TreeViewItem(TreeView *p, const char *zLabel,u8 moreFollows){
+ p = sqlite3TreeViewPush(p, moreFollows);
sqlite3TreeViewLine(p, "%s", zLabel);
}
-#endif /* SQLITE_DEBUG */
/*
-** variable-argument wrapper around sqlite3VXPrintf().
+** Generate a human-readable description of a WITH clause.
*/
-SQLITE_PRIVATE void sqlite3XPrintf(StrAccum *p, u32 bFlags, const char *zFormat, ...){
- va_list ap;
- va_start(ap,zFormat);
- sqlite3VXPrintf(p, bFlags, zFormat, ap);
- va_end(ap);
+SQLITE_PRIVATE void sqlite3TreeViewWith(TreeView *pView, const With *pWith, u8 moreToFollow){
+ int i;
+ if( pWith==0 ) return;
+ if( pWith->nCte==0 ) return;
+ if( pWith->pOuter ){
+ sqlite3TreeViewLine(pView, "WITH (0x%p, pOuter=0x%p)",pWith,pWith->pOuter);
+ }else{
+ sqlite3TreeViewLine(pView, "WITH (0x%p)", pWith);
+ }
+ if( pWith->nCte>0 ){
+ pView = sqlite3TreeViewPush(pView, 1);
+ for(i=0; i<pWith->nCte; i++){
+ StrAccum x;
+ char zLine[1000];
+ const struct Cte *pCte = &pWith->a[i];
+ sqlite3StrAccumInit(&x, 0, zLine, sizeof(zLine), 0);
+ sqlite3XPrintf(&x, "%s", pCte->zName);
+ if( pCte->pCols && pCte->pCols->nExpr>0 ){
+ char cSep = '(';
+ int j;
+ for(j=0; j<pCte->pCols->nExpr; j++){
+ sqlite3XPrintf(&x, "%c%s", cSep, pCte->pCols->a[j].zName);
+ cSep = ',';
+ }
+ sqlite3XPrintf(&x, ")");
+ }
+ sqlite3XPrintf(&x, " AS");
+ sqlite3StrAccumFinish(&x);
+ sqlite3TreeViewItem(pView, zLine, i<pWith->nCte-1);
+ sqlite3TreeViewSelect(pView, pCte->pSelect, 0);
+ sqlite3TreeViewPop(pView);
+ }
+ sqlite3TreeViewPop(pView);
+ }
}
-/************** End of printf.c **********************************************/
+
+/*
+** Generate a human-readable description of a the Select object.
+*/
+SQLITE_PRIVATE void sqlite3TreeViewSelect(TreeView *pView, const Select *p, u8 moreToFollow){
+ int n = 0;
+ int cnt = 0;
+ pView = sqlite3TreeViewPush(pView, moreToFollow);
+ if( p->pWith ){
+ sqlite3TreeViewWith(pView, p->pWith, 1);
+ cnt = 1;
+ sqlite3TreeViewPush(pView, 1);
+ }
+ do{
+ sqlite3TreeViewLine(pView, "SELECT%s%s (0x%p) selFlags=0x%x nSelectRow=%d",
+ ((p->selFlags & SF_Distinct) ? " DISTINCT" : ""),
+ ((p->selFlags & SF_Aggregate) ? " agg_flag" : ""), p, p->selFlags,
+ (int)p->nSelectRow
+ );
+ if( cnt++ ) sqlite3TreeViewPop(pView);
+ if( p->pPrior ){
+ n = 1000;
+ }else{
+ n = 0;
+ if( p->pSrc && p->pSrc->nSrc ) n++;
+ if( p->pWhere ) n++;
+ if( p->pGroupBy ) n++;
+ if( p->pHaving ) n++;
+ if( p->pOrderBy ) n++;
+ if( p->pLimit ) n++;
+ if( p->pOffset ) n++;
+ }
+ sqlite3TreeViewExprList(pView, p->pEList, (n--)>0, "result-set");
+ if( p->pSrc && p->pSrc->nSrc ){
+ int i;
+ pView = sqlite3TreeViewPush(pView, (n--)>0);
+ sqlite3TreeViewLine(pView, "FROM");
+ for(i=0; i<p->pSrc->nSrc; i++){
+ struct SrcList_item *pItem = &p->pSrc->a[i];
+ StrAccum x;
+ char zLine[100];
+ sqlite3StrAccumInit(&x, 0, zLine, sizeof(zLine), 0);
+ sqlite3XPrintf(&x, "{%d,*}", pItem->iCursor);
+ if( pItem->zDatabase ){
+ sqlite3XPrintf(&x, " %s.%s", pItem->zDatabase, pItem->zName);
+ }else if( pItem->zName ){
+ sqlite3XPrintf(&x, " %s", pItem->zName);
+ }
+ if( pItem->pTab ){
+ sqlite3XPrintf(&x, " tabname=%Q", pItem->pTab->zName);
+ }
+ if( pItem->zAlias ){
+ sqlite3XPrintf(&x, " (AS %s)", pItem->zAlias);
+ }
+ if( pItem->fg.jointype & JT_LEFT ){
+ sqlite3XPrintf(&x, " LEFT-JOIN");
+ }
+ sqlite3StrAccumFinish(&x);
+ sqlite3TreeViewItem(pView, zLine, i<p->pSrc->nSrc-1);
+ if( pItem->pSelect ){
+ sqlite3TreeViewSelect(pView, pItem->pSelect, 0);
+ }
+ if( pItem->fg.isTabFunc ){
+ sqlite3TreeViewExprList(pView, pItem->u1.pFuncArg, 0, "func-args:");
+ }
+ sqlite3TreeViewPop(pView);
+ }
+ sqlite3TreeViewPop(pView);
+ }
+ if( p->pWhere ){
+ sqlite3TreeViewItem(pView, "WHERE", (n--)>0);
+ sqlite3TreeViewExpr(pView, p->pWhere, 0);
+ sqlite3TreeViewPop(pView);
+ }
+ if( p->pGroupBy ){
+ sqlite3TreeViewExprList(pView, p->pGroupBy, (n--)>0, "GROUPBY");
+ }
+ if( p->pHaving ){
+ sqlite3TreeViewItem(pView, "HAVING", (n--)>0);
+ sqlite3TreeViewExpr(pView, p->pHaving, 0);
+ sqlite3TreeViewPop(pView);
+ }
+ if( p->pOrderBy ){
+ sqlite3TreeViewExprList(pView, p->pOrderBy, (n--)>0, "ORDERBY");
+ }
+ if( p->pLimit ){
+ sqlite3TreeViewItem(pView, "LIMIT", (n--)>0);
+ sqlite3TreeViewExpr(pView, p->pLimit, 0);
+ sqlite3TreeViewPop(pView);
+ }
+ if( p->pOffset ){
+ sqlite3TreeViewItem(pView, "OFFSET", (n--)>0);
+ sqlite3TreeViewExpr(pView, p->pOffset, 0);
+ sqlite3TreeViewPop(pView);
+ }
+ if( p->pPrior ){
+ const char *zOp = "UNION";
+ switch( p->op ){
+ case TK_ALL: zOp = "UNION ALL"; break;
+ case TK_INTERSECT: zOp = "INTERSECT"; break;
+ case TK_EXCEPT: zOp = "EXCEPT"; break;
+ }
+ sqlite3TreeViewItem(pView, zOp, 1);
+ }
+ p = p->pPrior;
+ }while( p!=0 );
+ sqlite3TreeViewPop(pView);
+}
+
+/*
+** Generate a human-readable explanation of an expression tree.
+*/
+SQLITE_PRIVATE void sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u8 moreToFollow){
+ const char *zBinOp = 0; /* Binary operator */
+ const char *zUniOp = 0; /* Unary operator */
+ char zFlgs[30];
+ pView = sqlite3TreeViewPush(pView, moreToFollow);
+ if( pExpr==0 ){
+ sqlite3TreeViewLine(pView, "nil");
+ sqlite3TreeViewPop(pView);
+ return;
+ }
+ if( pExpr->flags ){
+ sqlite3_snprintf(sizeof(zFlgs),zFlgs," flags=0x%x",pExpr->flags);
+ }else{
+ zFlgs[0] = 0;
+ }
+ switch( pExpr->op ){
+ case TK_AGG_COLUMN: {
+ sqlite3TreeViewLine(pView, "AGG{%d:%d}%s",
+ pExpr->iTable, pExpr->iColumn, zFlgs);
+ break;
+ }
+ case TK_COLUMN: {
+ if( pExpr->iTable<0 ){
+ /* This only happens when coding check constraints */
+ sqlite3TreeViewLine(pView, "COLUMN(%d)%s", pExpr->iColumn, zFlgs);
+ }else{
+ sqlite3TreeViewLine(pView, "{%d:%d}%s",
+ pExpr->iTable, pExpr->iColumn, zFlgs);
+ }
+ break;
+ }
+ case TK_INTEGER: {
+ if( pExpr->flags & EP_IntValue ){
+ sqlite3TreeViewLine(pView, "%d", pExpr->u.iValue);
+ }else{
+ sqlite3TreeViewLine(pView, "%s", pExpr->u.zToken);
+ }
+ break;
+ }
+#ifndef SQLITE_OMIT_FLOATING_POINT
+ case TK_FLOAT: {
+ sqlite3TreeViewLine(pView,"%s", pExpr->u.zToken);
+ break;
+ }
+#endif
+ case TK_STRING: {
+ sqlite3TreeViewLine(pView,"%Q", pExpr->u.zToken);
+ break;
+ }
+ case TK_NULL: {
+ sqlite3TreeViewLine(pView,"NULL");
+ break;
+ }
+#ifndef SQLITE_OMIT_BLOB_LITERAL
+ case TK_BLOB: {
+ sqlite3TreeViewLine(pView,"%s", pExpr->u.zToken);
+ break;
+ }
+#endif
+ case TK_VARIABLE: {
+ sqlite3TreeViewLine(pView,"VARIABLE(%s,%d)",
+ pExpr->u.zToken, pExpr->iColumn);
+ break;
+ }
+ case TK_REGISTER: {
+ sqlite3TreeViewLine(pView,"REGISTER(%d)", pExpr->iTable);
+ break;
+ }
+ case TK_ID: {
+ sqlite3TreeViewLine(pView,"ID \"%w\"", pExpr->u.zToken);
+ break;
+ }
+#ifndef SQLITE_OMIT_CAST
+ case TK_CAST: {
+ /* Expressions of the form: CAST(pLeft AS token) */
+ sqlite3TreeViewLine(pView,"CAST %Q", pExpr->u.zToken);
+ sqlite3TreeViewExpr(pView, pExpr->pLeft, 0);
+ break;
+ }
+#endif /* SQLITE_OMIT_CAST */
+ case TK_LT: zBinOp = "LT"; break;
+ case TK_LE: zBinOp = "LE"; break;
+ case TK_GT: zBinOp = "GT"; break;
+ case TK_GE: zBinOp = "GE"; break;
+ case TK_NE: zBinOp = "NE"; break;
+ case TK_EQ: zBinOp = "EQ"; break;
+ case TK_IS: zBinOp = "IS"; break;
+ case TK_ISNOT: zBinOp = "ISNOT"; break;
+ case TK_AND: zBinOp = "AND"; break;
+ case TK_OR: zBinOp = "OR"; break;
+ case TK_PLUS: zBinOp = "ADD"; break;
+ case TK_STAR: zBinOp = "MUL"; break;
+ case TK_MINUS: zBinOp = "SUB"; break;
+ case TK_REM: zBinOp = "REM"; break;
+ case TK_BITAND: zBinOp = "BITAND"; break;
+ case TK_BITOR: zBinOp = "BITOR"; break;
+ case TK_SLASH: zBinOp = "DIV"; break;
+ case TK_LSHIFT: zBinOp = "LSHIFT"; break;
+ case TK_RSHIFT: zBinOp = "RSHIFT"; break;
+ case TK_CONCAT: zBinOp = "CONCAT"; break;
+ case TK_DOT: zBinOp = "DOT"; break;
+
+ case TK_UMINUS: zUniOp = "UMINUS"; break;
+ case TK_UPLUS: zUniOp = "UPLUS"; break;
+ case TK_BITNOT: zUniOp = "BITNOT"; break;
+ case TK_NOT: zUniOp = "NOT"; break;
+ case TK_ISNULL: zUniOp = "ISNULL"; break;
+ case TK_NOTNULL: zUniOp = "NOTNULL"; break;
+
+ case TK_SPAN: {
+ sqlite3TreeViewLine(pView, "SPAN %Q", pExpr->u.zToken);
+ sqlite3TreeViewExpr(pView, pExpr->pLeft, 0);
+ break;
+ }
+
+ case TK_COLLATE: {
+ sqlite3TreeViewLine(pView, "COLLATE %Q", pExpr->u.zToken);
+ sqlite3TreeViewExpr(pView, pExpr->pLeft, 0);
+ break;
+ }
+
+ case TK_AGG_FUNCTION:
+ case TK_FUNCTION: {
+ ExprList *pFarg; /* List of function arguments */
+ if( ExprHasProperty(pExpr, EP_TokenOnly) ){
+ pFarg = 0;
+ }else{
+ pFarg = pExpr->x.pList;
+ }
+ if( pExpr->op==TK_AGG_FUNCTION ){
+ sqlite3TreeViewLine(pView, "AGG_FUNCTION%d %Q",
+ pExpr->op2, pExpr->u.zToken);
+ }else{
+ sqlite3TreeViewLine(pView, "FUNCTION %Q", pExpr->u.zToken);
+ }
+ if( pFarg ){
+ sqlite3TreeViewExprList(pView, pFarg, 0, 0);
+ }
+ break;
+ }
+#ifndef SQLITE_OMIT_SUBQUERY
+ case TK_EXISTS: {
+ sqlite3TreeViewLine(pView, "EXISTS-expr");
+ sqlite3TreeViewSelect(pView, pExpr->x.pSelect, 0);
+ break;
+ }
+ case TK_SELECT: {
+ sqlite3TreeViewLine(pView, "SELECT-expr");
+ sqlite3TreeViewSelect(pView, pExpr->x.pSelect, 0);
+ break;
+ }
+ case TK_IN: {
+ sqlite3TreeViewLine(pView, "IN");
+ sqlite3TreeViewExpr(pView, pExpr->pLeft, 1);
+ if( ExprHasProperty(pExpr, EP_xIsSelect) ){
+ sqlite3TreeViewSelect(pView, pExpr->x.pSelect, 0);
+ }else{
+ sqlite3TreeViewExprList(pView, pExpr->x.pList, 0, 0);
+ }
+ break;
+ }
+#endif /* SQLITE_OMIT_SUBQUERY */
+
+ /*
+ ** x BETWEEN y AND z
+ **
+ ** This is equivalent to
+ **
+ ** x>=y AND x<=z
+ **
+ ** X is stored in pExpr->pLeft.
+ ** Y is stored in pExpr->pList->a[0].pExpr.
+ ** Z is stored in pExpr->pList->a[1].pExpr.
+ */
+ case TK_BETWEEN: {
+ Expr *pX = pExpr->pLeft;
+ Expr *pY = pExpr->x.pList->a[0].pExpr;
+ Expr *pZ = pExpr->x.pList->a[1].pExpr;
+ sqlite3TreeViewLine(pView, "BETWEEN");
+ sqlite3TreeViewExpr(pView, pX, 1);
+ sqlite3TreeViewExpr(pView, pY, 1);
+ sqlite3TreeViewExpr(pView, pZ, 0);
+ break;
+ }
+ case TK_TRIGGER: {
+ /* If the opcode is TK_TRIGGER, then the expression is a reference
+ ** to a column in the new.* or old.* pseudo-tables available to
+ ** trigger programs. In this case Expr.iTable is set to 1 for the
+ ** new.* pseudo-table, or 0 for the old.* pseudo-table. Expr.iColumn
+ ** is set to the column of the pseudo-table to read, or to -1 to
+ ** read the rowid field.
+ */
+ sqlite3TreeViewLine(pView, "%s(%d)",
+ pExpr->iTable ? "NEW" : "OLD", pExpr->iColumn);
+ break;
+ }
+ case TK_CASE: {
+ sqlite3TreeViewLine(pView, "CASE");
+ sqlite3TreeViewExpr(pView, pExpr->pLeft, 1);
+ sqlite3TreeViewExprList(pView, pExpr->x.pList, 0, 0);
+ break;
+ }
+#ifndef SQLITE_OMIT_TRIGGER
+ case TK_RAISE: {
+ const char *zType = "unk";
+ switch( pExpr->affinity ){
+ case OE_Rollback: zType = "rollback"; break;
+ case OE_Abort: zType = "abort"; break;
+ case OE_Fail: zType = "fail"; break;
+ case OE_Ignore: zType = "ignore"; break;
+ }
+ sqlite3TreeViewLine(pView, "RAISE %s(%Q)", zType, pExpr->u.zToken);
+ break;
+ }
+#endif
+ default: {
+ sqlite3TreeViewLine(pView, "op=%d", pExpr->op);
+ break;
+ }
+ }
+ if( zBinOp ){
+ sqlite3TreeViewLine(pView, "%s%s", zBinOp, zFlgs);
+ sqlite3TreeViewExpr(pView, pExpr->pLeft, 1);
+ sqlite3TreeViewExpr(pView, pExpr->pRight, 0);
+ }else if( zUniOp ){
+ sqlite3TreeViewLine(pView, "%s%s", zUniOp, zFlgs);
+ sqlite3TreeViewExpr(pView, pExpr->pLeft, 0);
+ }
+ sqlite3TreeViewPop(pView);
+}
+
+/*
+** Generate a human-readable explanation of an expression list.
+*/
+SQLITE_PRIVATE void sqlite3TreeViewExprList(
+ TreeView *pView,
+ const ExprList *pList,
+ u8 moreToFollow,
+ const char *zLabel
+){
+ int i;
+ pView = sqlite3TreeViewPush(pView, moreToFollow);
+ if( zLabel==0 || zLabel[0]==0 ) zLabel = "LIST";
+ if( pList==0 ){
+ sqlite3TreeViewLine(pView, "%s (empty)", zLabel);
+ }else{
+ sqlite3TreeViewLine(pView, "%s", zLabel);
+ for(i=0; i<pList->nExpr; i++){
+ int j = pList->a[i].u.x.iOrderByCol;
+ if( j ){
+ sqlite3TreeViewPush(pView, 0);
+ sqlite3TreeViewLine(pView, "iOrderByCol=%d", j);
+ }
+ sqlite3TreeViewExpr(pView, pList->a[i].pExpr, i<pList->nExpr-1);
+ if( j ) sqlite3TreeViewPop(pView);
+ }
+ }
+ sqlite3TreeViewPop(pView);
+}
+
+#endif /* SQLITE_DEBUG */
+
+/************** End of treeview.c ********************************************/
/************** Begin file random.c ******************************************/
/*
** 2001 September 15
@@ -22509,6 +26044,7 @@ SQLITE_PRIVATE void sqlite3XPrintf(StrAccum *p, u32 bFlags, const char *zFormat,
** Random numbers are used by some of the database backends in order
** to generate random integer keys for tables or random filenames.
*/
+/* #include "sqliteInt.h" */
/* All threads share a single random number generator.
@@ -22655,7 +26191,9 @@ SQLITE_PRIVATE void sqlite3PrngRestoreState(void){
** of multiple cores can do so, while also allowing applications to stay
** single-threaded if desired.
*/
+/* #include "sqliteInt.h" */
#if SQLITE_OS_WIN
+/* # include "os_win.h" */
#endif
#if SQLITE_MAX_WORKER_THREADS>0
@@ -22691,10 +26229,14 @@ SQLITE_PRIVATE int sqlite3ThreadCreate(
*ppThread = 0;
p = sqlite3Malloc(sizeof(*p));
- if( p==0 ) return SQLITE_NOMEM;
+ if( p==0 ) return SQLITE_NOMEM_BKPT;
memset(p, 0, sizeof(*p));
p->xTask = xTask;
p->pIn = pIn;
+ /* If the SQLITE_TESTCTRL_FAULT_INSTALL callback is registered to a
+ ** function that returns SQLITE_ERROR when passed the argument 200, that
+ ** forces worker threads to run sequentially and deterministically
+ ** for testing purposes. */
if( sqlite3FaultSim(200) ){
rc = 1;
}else{
@@ -22713,7 +26255,7 @@ SQLITE_PRIVATE int sqlite3ThreadJoin(SQLiteThread *p, void **ppOut){
int rc;
assert( ppOut!=0 );
- if( NEVER(p==0) ) return SQLITE_NOMEM;
+ if( NEVER(p==0) ) return SQLITE_NOMEM_BKPT;
if( p->done ){
*ppOut = p->pOut;
rc = SQLITE_OK;
@@ -22778,8 +26320,13 @@ SQLITE_PRIVATE int sqlite3ThreadCreate(
assert( xTask!=0 );
*ppThread = 0;
p = sqlite3Malloc(sizeof(*p));
- if( p==0 ) return SQLITE_NOMEM;
- if( sqlite3GlobalConfig.bCoreMutex==0 ){
+ if( p==0 ) return SQLITE_NOMEM_BKPT;
+ /* If the SQLITE_TESTCTRL_FAULT_INSTALL callback is registered to a
+ ** function that returns SQLITE_ERROR when passed the argument 200, that
+ ** forces worker threads to run sequentially and deterministically
+ ** (via the sqlite3FaultSim() term of the conditional) for testing
+ ** purposes. */
+ if( sqlite3GlobalConfig.bCoreMutex==0 || sqlite3FaultSim(200) ){
memset(p, 0, sizeof(*p));
}else{
p->xTask = xTask;
@@ -22805,9 +26352,9 @@ SQLITE_PRIVATE int sqlite3ThreadJoin(SQLiteThread *p, void **ppOut){
BOOL bRc;
assert( ppOut!=0 );
- if( NEVER(p==0) ) return SQLITE_NOMEM;
+ if( NEVER(p==0) ) return SQLITE_NOMEM_BKPT;
if( p->xTask==0 ){
- assert( p->id==GetCurrentThreadId() );
+ /* assert( p->id==GetCurrentThreadId() ); */
rc = WAIT_OBJECT_0;
assert( p->tid==0 );
}else{
@@ -22853,7 +26400,7 @@ SQLITE_PRIVATE int sqlite3ThreadCreate(
assert( xTask!=0 );
*ppThread = 0;
p = sqlite3Malloc(sizeof(*p));
- if( p==0 ) return SQLITE_NOMEM;
+ if( p==0 ) return SQLITE_NOMEM_BKPT;
if( (SQLITE_PTR_TO_INT(p)/17)&1 ){
p->xTask = xTask;
p->pIn = pIn;
@@ -22869,7 +26416,7 @@ SQLITE_PRIVATE int sqlite3ThreadCreate(
SQLITE_PRIVATE int sqlite3ThreadJoin(SQLiteThread *p, void **ppOut){
assert( ppOut!=0 );
- if( NEVER(p==0) ) return SQLITE_NOMEM;
+ if( NEVER(p==0) ) return SQLITE_NOMEM_BKPT;
if( p->xTask ){
*ppOut = p->xTask(p->pIn);
}else{
@@ -22880,7 +26427,7 @@ SQLITE_PRIVATE int sqlite3ThreadJoin(SQLiteThread *p, void **ppOut){
#if defined(SQLITE_TEST)
{
void *pTstAlloc = sqlite3Malloc(10);
- if (!pTstAlloc) return SQLITE_NOMEM;
+ if (!pTstAlloc) return SQLITE_NOMEM_BKPT;
sqlite3_free(pTstAlloc);
}
#endif
@@ -22929,15 +26476,17 @@ SQLITE_PRIVATE int sqlite3ThreadJoin(SQLiteThread *p, void **ppOut){
** 0xfe 0xff big-endian utf-16 follows
**
*/
+/* #include "sqliteInt.h" */
/* #include <assert.h> */
+/* #include "vdbeInt.h" */
-#ifndef SQLITE_AMALGAMATION
+#if !defined(SQLITE_AMALGAMATION) && SQLITE_BYTEORDER==0
/*
** The following constant value is used by the SQLITE_BIGENDIAN and
** SQLITE_LITTLEENDIAN macros.
*/
SQLITE_PRIVATE const int sqlite3one = 1;
-#endif /* SQLITE_AMALGAMATION */
+#endif /* SQLITE_AMALGAMATION && SQLITE_BYTEORDER==0 */
/*
** This lookup table is used to help decode the first byte of
@@ -23125,7 +26674,7 @@ SQLITE_PRIVATE SQLITE_NOINLINE int sqlite3VdbeMemTranslate(Mem *pMem, u8 desired
rc = sqlite3VdbeMemMakeWriteable(pMem);
if( rc!=SQLITE_OK ){
assert( rc==SQLITE_NOMEM );
- return SQLITE_NOMEM;
+ return SQLITE_NOMEM_BKPT;
}
zIn = (u8*)pMem->z;
zTerm = &zIn[pMem->n&~1];
@@ -23167,7 +26716,7 @@ SQLITE_PRIVATE SQLITE_NOINLINE int sqlite3VdbeMemTranslate(Mem *pMem, u8 desired
zTerm = &zIn[pMem->n];
zOut = sqlite3DbMallocRaw(pMem->db, len);
if( !zOut ){
- return SQLITE_NOMEM;
+ return SQLITE_NOMEM_BKPT;
}
z = zOut;
@@ -23210,7 +26759,7 @@ SQLITE_PRIVATE SQLITE_NOINLINE int sqlite3VdbeMemTranslate(Mem *pMem, u8 desired
c = pMem->flags;
sqlite3VdbeMemRelease(pMem);
- pMem->flags = MEM_Str|MEM_Term|(c&MEM_AffMask);
+ pMem->flags = MEM_Str|MEM_Term|(c&(MEM_AffMask|MEM_Subtype));
pMem->enc = desiredEnc;
pMem->z = (char*)zOut;
pMem->zMalloc = pMem->z;
@@ -23442,6 +26991,7 @@ SQLITE_PRIVATE void sqlite3UtfSelfTest(void){
** strings, and stuff like that.
**
*/
+/* #include "sqliteInt.h" */
/* #include <stdarg.h> */
#if HAVE_ISNAN || SQLITE_HAVE_ISNAN
# include <math.h>
@@ -23531,19 +27081,53 @@ SQLITE_PRIVATE int sqlite3IsNaN(double x){
** than 1GiB) the value returned might be less than the true string length.
*/
SQLITE_PRIVATE int sqlite3Strlen30(const char *z){
- const char *z2 = z;
if( z==0 ) return 0;
- while( *z2 ){ z2++; }
- return 0x3fffffff & (int)(z2 - z);
+ return 0x3fffffff & (int)strlen(z);
+}
+
+/*
+** Return the declared type of a column. Or return zDflt if the column
+** has no declared type.
+**
+** The column type is an extra string stored after the zero-terminator on
+** the column name if and only if the COLFLAG_HASTYPE flag is set.
+*/
+SQLITE_PRIVATE char *sqlite3ColumnType(Column *pCol, char *zDflt){
+ if( (pCol->colFlags & COLFLAG_HASTYPE)==0 ) return zDflt;
+ return pCol->zName + strlen(pCol->zName) + 1;
+}
+
+/*
+** Helper function for sqlite3Error() - called rarely. Broken out into
+** a separate routine to avoid unnecessary register saves on entry to
+** sqlite3Error().
+*/
+static SQLITE_NOINLINE void sqlite3ErrorFinish(sqlite3 *db, int err_code){
+ if( db->pErr ) sqlite3ValueSetNull(db->pErr);
+ sqlite3SystemError(db, err_code);
}
/*
** Set the current error code to err_code and clear any prior error message.
+** Also set iSysErrno (by calling sqlite3System) if the err_code indicates
+** that would be appropriate.
*/
SQLITE_PRIVATE void sqlite3Error(sqlite3 *db, int err_code){
assert( db!=0 );
db->errCode = err_code;
- if( db->pErr ) sqlite3ValueSetNull(db->pErr);
+ if( err_code || db->pErr ) sqlite3ErrorFinish(db, err_code);
+}
+
+/*
+** Load the sqlite3.iSysErrno field if that is an appropriate thing
+** to do based on the SQLite error code in rc.
+*/
+SQLITE_PRIVATE void sqlite3SystemError(sqlite3 *db, int rc){
+ if( rc==SQLITE_IOERR_NOMEM ) return;
+ rc &= 0xff;
+ if( rc==SQLITE_CANTOPEN || rc==SQLITE_IOERR ){
+ db->iSysErrno = sqlite3OsGetLastError(db->pVfs);
+ }
}
/*
@@ -23570,6 +27154,7 @@ SQLITE_PRIVATE void sqlite3Error(sqlite3 *db, int err_code){
SQLITE_PRIVATE void sqlite3ErrorWithMsg(sqlite3 *db, int err_code, const char *zFormat, ...){
assert( db!=0 );
db->errCode = err_code;
+ sqlite3SystemError(db, err_code);
if( zFormat==0 ){
sqlite3Error(db, err_code);
}else if( db->pErr || (db->pErr = sqlite3ValueNew(db))!=0 ){
@@ -23633,18 +27218,13 @@ SQLITE_PRIVATE void sqlite3ErrorMsg(Parse *pParse, const char *zFormat, ...){
** brackets from around identifiers. For example: "[a-b-c]" becomes
** "a-b-c".
*/
-SQLITE_PRIVATE int sqlite3Dequote(char *z){
+SQLITE_PRIVATE void sqlite3Dequote(char *z){
char quote;
int i, j;
- if( z==0 ) return -1;
+ if( z==0 ) return;
quote = z[0];
- switch( quote ){
- case '\'': break;
- case '"': break;
- case '`': break; /* For MySQL compatibility */
- case '[': quote = ']'; break; /* For MS SqlServer compatibility */
- default: return -1;
- }
+ if( !sqlite3Isquote(quote) ) return;
+ if( quote=='[' ) quote = ']';
for(i=1, j=0;; i++){
assert( z[i] );
if( z[i]==quote ){
@@ -23659,7 +27239,14 @@ SQLITE_PRIVATE int sqlite3Dequote(char *z){
}
}
z[j] = 0;
- return j;
+}
+
+/*
+** Generate a Token object from a string
+*/
+SQLITE_PRIVATE void sqlite3TokenInit(Token *p, char *z){
+ p->z = z;
+ p->n = sqlite3Strlen30(z);
}
/* Convenient short-hand */
@@ -23676,16 +27263,25 @@ SQLITE_PRIVATE int sqlite3Dequote(char *z){
** independence" that SQLite uses internally when comparing identifiers.
*/
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;
}else if( zRight==0 ){
return 1;
}
+ return sqlite3StrICmp(zLeft, zRight);
+}
+SQLITE_PRIVATE int sqlite3StrICmp(const char *zLeft, const char *zRight){
+ unsigned char *a, *b;
+ int c;
a = (unsigned char *)zLeft;
b = (unsigned char *)zRight;
- while( *a!=0 && UpperToLower[*a]==UpperToLower[*b]){ a++; b++; }
- return UpperToLower[*a] - UpperToLower[*b];
+ for(;;){
+ c = (int)UpperToLower[*a] - (int)UpperToLower[*b];
+ if( c || *a==0 ) break;
+ a++;
+ b++;
+ }
+ return c;
}
SQLITE_API int SQLITE_STDCALL sqlite3_strnicmp(const char *zLeft, const char *zRight, int N){
register unsigned char *a, *b;
@@ -23735,7 +27331,7 @@ SQLITE_PRIVATE int sqlite3AtoF(const char *z, double *pResult, int length, u8 en
int eValid = 1; /* True exponent is either not used or is well-formed */
double result;
int nDigits = 0;
- int nonNum = 0;
+ int nonNum = 0; /* True if input contains UTF16 with high byte non-zero */
assert( enc==SQLITE_UTF8 || enc==SQLITE_UTF16LE || enc==SQLITE_UTF16BE );
*pResult = 0.0; /* Default return value, in case of an error */
@@ -23748,7 +27344,7 @@ SQLITE_PRIVATE int sqlite3AtoF(const char *z, double *pResult, int length, u8 en
assert( SQLITE_UTF16LE==2 && SQLITE_UTF16BE==3 );
for(i=3-enc; i<length && z[i]==0; i+=2){}
nonNum = i<length;
- zEnd = z+i+enc-3;
+ zEnd = &z[i^1];
z += (enc&1);
}
@@ -23764,9 +27360,6 @@ SQLITE_PRIVATE int sqlite3AtoF(const char *z, double *pResult, int length, u8 en
z+=incr;
}
- /* skip leading zeroes */
- while( z<zEnd && z[0]=='0' ) z+=incr, nDigits++;
-
/* copy max significant digits to significand */
while( z<zEnd && sqlite3Isdigit(*z) && s<((LARGEST_INT64-9)/10) ){
s = s*10 + (*z - '0');
@@ -23783,12 +27376,13 @@ SQLITE_PRIVATE int sqlite3AtoF(const char *z, double *pResult, int length, u8 en
z+=incr;
/* copy digits from after decimal to significand
** (decrease exponent by d to shift decimal right) */
- while( z<zEnd && sqlite3Isdigit(*z) && s<((LARGEST_INT64-9)/10) ){
- s = s*10 + (*z - '0');
- z+=incr, nDigits++, d--;
+ while( z<zEnd && sqlite3Isdigit(*z) ){
+ if( s<((LARGEST_INT64-9)/10) ){
+ s = s*10 + (*z - '0');
+ d--;
+ }
+ z+=incr, nDigits++;
}
- /* skip non-significant digits */
- while( z<zEnd && sqlite3Isdigit(*z) ) z+=incr, nDigits++;
}
if( z>=zEnd ) goto do_atof_calc;
@@ -23796,7 +27390,12 @@ SQLITE_PRIVATE int sqlite3AtoF(const char *z, double *pResult, int length, u8 en
if( *z=='e' || *z=='E' ){
z+=incr;
eValid = 0;
- if( z>=zEnd ) goto do_atof_calc;
+
+ /* This branch is needed to avoid a (harmless) buffer overread. The
+ ** special comment alerts the mutation tester that the correct answer
+ ** is obtained even if the branch is omitted */
+ if( z>=zEnd ) goto do_atof_calc; /*PREVENTS-HARMLESS-OVERREAD*/
+
/* get sign of exponent */
if( *z=='-' ){
esign = -1;
@@ -23813,9 +27412,7 @@ SQLITE_PRIVATE int sqlite3AtoF(const char *z, double *pResult, int length, u8 en
}
/* skip trailing spaces */
- if( nDigits && eValid ){
- while( z<zEnd && sqlite3Isspace(*z) ) z+=incr;
- }
+ while( z<zEnd && sqlite3Isspace(*z) ) z+=incr;
do_atof_calc:
/* adjust exponent by d, and update sign */
@@ -23827,41 +27424,51 @@ do_atof_calc:
esign = 1;
}
- /* if 0 significand */
- if( !s ) {
- /* In the IEEE 754 standard, zero is signed.
- ** Add the sign if we've seen at least one digit */
- result = (sign<0 && nDigits) ? -(double)0 : (double)0;
+ if( s==0 ) {
+ /* In the IEEE 754 standard, zero is signed. */
+ result = sign<0 ? -(double)0 : (double)0;
} else {
- /* attempt to reduce exponent */
- if( esign>0 ){
- while( s<(LARGEST_INT64/10) && e>0 ) e--,s*=10;
- }else{
- while( !(s%10) && e>0 ) e--,s/=10;
+ /* Attempt to reduce exponent.
+ **
+ ** Branches that are not required for the correct answer but which only
+ ** help to obtain the correct answer faster are marked with special
+ ** comments, as a hint to the mutation tester.
+ */
+ while( e>0 ){ /*OPTIMIZATION-IF-TRUE*/
+ if( esign>0 ){
+ if( s>=(LARGEST_INT64/10) ) break; /*OPTIMIZATION-IF-FALSE*/
+ s *= 10;
+ }else{
+ if( s%10!=0 ) break; /*OPTIMIZATION-IF-FALSE*/
+ s /= 10;
+ }
+ e--;
}
/* adjust the sign of significand */
s = sign<0 ? -s : s;
- /* if exponent, scale significand as appropriate
- ** and store in result. */
- if( e ){
+ if( e==0 ){ /*OPTIMIZATION-IF-TRUE*/
+ result = (double)s;
+ }else{
LONGDOUBLE_TYPE scale = 1.0;
/* attempt to handle extremely small/large numbers better */
- if( e>307 && e<342 ){
- while( e%308 ) { scale *= 1.0e+1; e -= 1; }
- if( esign<0 ){
- result = s / scale;
- result /= 1.0e+308;
- }else{
- result = s * scale;
- result *= 1.0e+308;
- }
- }else if( e>=342 ){
- if( esign<0 ){
- result = 0.0*s;
- }else{
- result = 1e308*1e308*s; /* Infinity */
+ if( e>307 ){ /*OPTIMIZATION-IF-TRUE*/
+ if( e<342 ){ /*OPTIMIZATION-IF-TRUE*/
+ while( e%308 ) { scale *= 1.0e+1; e -= 1; }
+ if( esign<0 ){
+ result = s / scale;
+ result /= 1.0e+308;
+ }else{
+ result = s * scale;
+ result *= 1.0e+308;
+ }
+ }else{ assert( e>=342 );
+ if( esign<0 ){
+ result = 0.0*s;
+ }else{
+ result = 1e308*1e308*s; /* Infinity */
+ }
}
}else{
/* 1.0e+22 is the largest power of 10 than can be
@@ -23874,8 +27481,6 @@ do_atof_calc:
result = s * scale;
}
}
- } else {
- result = (double)s;
}
}
@@ -23883,7 +27488,7 @@ do_atof_calc:
*pResult = result;
/* return true if number and no extra non-whitespace chracters after */
- return z>=zEnd && nDigits>0 && eValid && nonNum==0;
+ return z==zEnd && nDigits>0 && eValid && nonNum==0;
#else
return !sqlite3Atoi64(z, pResult, length, enc);
#endif /* SQLITE_OMIT_FLOATING_POINT */
@@ -23945,7 +27550,7 @@ SQLITE_PRIVATE int sqlite3Atoi64(const char *zNum, i64 *pNum, int length, u8 enc
int neg = 0; /* assume positive */
int i;
int c = 0;
- int nonNum = 0;
+ int nonNum = 0; /* True if input contains UTF16 with high byte non-zero */
const char *zStart;
const char *zEnd = zNum + length;
assert( enc==SQLITE_UTF8 || enc==SQLITE_UTF16LE || enc==SQLITE_UTF16BE );
@@ -23956,7 +27561,7 @@ SQLITE_PRIVATE int sqlite3Atoi64(const char *zNum, i64 *pNum, int length, u8 enc
assert( SQLITE_UTF16LE==2 && SQLITE_UTF16BE==3 );
for(i=3-enc; i<length && zNum[i]==0; i+=2){}
nonNum = i<length;
- zEnd = zNum+i+enc-3;
+ zEnd = &zNum[i^1];
zNum += (enc&1);
}
while( zNum<zEnd && sqlite3Isspace(*zNum) ) zNum+=incr;
@@ -23983,7 +27588,11 @@ SQLITE_PRIVATE int sqlite3Atoi64(const char *zNum, i64 *pNum, int length, u8 enc
testcase( i==18 );
testcase( i==19 );
testcase( i==20 );
- if( (c!=0 && &zNum[i]<zEnd) || (i==0 && zStart==zNum) || i>19*incr || nonNum ){
+ if( &zNum[i]<zEnd /* Extra bytes at the end */
+ || (i==0 && zStart==zNum) /* No digits */
+ || i>19*incr /* Too many digits */
+ || nonNum /* UTF16 with high-order bytes non-zero */
+ ){
/* zNum is empty or contains non-numeric text or is longer
** than 19 digits (thus guaranteeing that it is too large) */
return 1;
@@ -24025,7 +27634,6 @@ SQLITE_PRIVATE int sqlite3DecOrHexToI64(const char *z, i64 *pOut){
#ifndef SQLITE_OMIT_HEX_INTEGER
if( z[0]=='0'
&& (z[1]=='x' || z[1]=='X')
- && sqlite3Isxdigit(z[2])
){
u64 u = 0;
int i, k;
@@ -24272,7 +27880,8 @@ SQLITE_PRIVATE u8 sqlite3GetVarint(const unsigned char *p, u64 *v){
/* a: p0<<28 | p2<<14 | p4 (unmasked) */
if (!(a&0x80))
{
- /* we can skip these cause they were (effectively) done above in calc'ing s */
+ /* we can skip these cause they were (effectively) done above
+ ** while calculating s */
/* a &= (0x7f<<28)|(0x7f<<14)|(0x7f); */
/* b &= (0x7f<<14)|(0x7f); */
b = b<<7;
@@ -24493,11 +28102,8 @@ SQLITE_PRIVATE u8 sqlite3GetVarint32(const unsigned char *p, u32 *v){
** 64-bit integer.
*/
SQLITE_PRIVATE int sqlite3VarintLen(u64 v){
- int i = 0;
- do{
- i++;
- v >>= 7;
- }while( v!=0 && ALWAYS(i<9) );
+ int i;
+ for(i=1; (v >>= 7)!=0; i++){ assert( i<10 ); }
return i;
}
@@ -24506,14 +28112,42 @@ SQLITE_PRIVATE int sqlite3VarintLen(u64 v){
** Read or write a four-byte big-endian integer value.
*/
SQLITE_PRIVATE u32 sqlite3Get4byte(const u8 *p){
+#if SQLITE_BYTEORDER==4321
+ u32 x;
+ memcpy(&x,p,4);
+ return x;
+#elif SQLITE_BYTEORDER==1234 && !defined(SQLITE_DISABLE_INTRINSIC) \
+ && defined(__GNUC__) && GCC_VERSION>=4003000
+ u32 x;
+ memcpy(&x,p,4);
+ return __builtin_bswap32(x);
+#elif SQLITE_BYTEORDER==1234 && !defined(SQLITE_DISABLE_INTRINSIC) \
+ && defined(_MSC_VER) && _MSC_VER>=1300
+ u32 x;
+ memcpy(&x,p,4);
+ return _byteswap_ulong(x);
+#else
testcase( p[0]&0x80 );
return ((unsigned)p[0]<<24) | (p[1]<<16) | (p[2]<<8) | p[3];
+#endif
}
SQLITE_PRIVATE void sqlite3Put4byte(unsigned char *p, u32 v){
+#if SQLITE_BYTEORDER==4321
+ memcpy(p,&v,4);
+#elif SQLITE_BYTEORDER==1234 && !defined(SQLITE_DISABLE_INTRINSIC) \
+ && defined(__GNUC__) && GCC_VERSION>=4003000
+ u32 x = __builtin_bswap32(v);
+ memcpy(p,&x,4);
+#elif SQLITE_BYTEORDER==1234 && !defined(SQLITE_DISABLE_INTRINSIC) \
+ && defined(_MSC_VER) && _MSC_VER>=1300
+ u32 x = _byteswap_ulong(v);
+ memcpy(p,&x,4);
+#else
p[0] = (u8)(v>>24);
p[1] = (u8)(v>>16);
p[2] = (u8)(v>>8);
p[3] = (u8)v;
+#endif
}
@@ -24545,7 +28179,7 @@ SQLITE_PRIVATE void *sqlite3HexToBlob(sqlite3 *db, const char *z, int n){
char *zBlob;
int i;
- zBlob = (char *)sqlite3DbMallocRaw(db, n/2 + 1);
+ zBlob = (char *)sqlite3DbMallocRawNN(db, n/2 + 1);
n--;
if( zBlob ){
for(i=0; i<n; i+=2){
@@ -24761,7 +28395,7 @@ SQLITE_PRIVATE LogEst sqlite3LogEst(u64 x){
if( x<2 ) return 0;
while( x<8 ){ y -= 10; x <<= 1; }
}else{
- while( x>255 ){ y += 40; x >>= 4; }
+ while( x>255 ){ y += 40; x >>= 4; } /*OPTIMIZATION-IF-TRUE*/
while( x>15 ){ y += 10; x >>= 1; }
}
return a[x&7] + y - 10;
@@ -24784,21 +28418,32 @@ SQLITE_PRIVATE LogEst sqlite3LogEstFromDouble(double x){
}
#endif /* SQLITE_OMIT_VIRTUALTABLE */
+#if defined(SQLITE_ENABLE_STMT_SCANSTATUS) || \
+ defined(SQLITE_ENABLE_STAT3_OR_STAT4) || \
+ defined(SQLITE_EXPLAIN_ESTIMATED_ROWS)
/*
** Convert a LogEst into an integer.
+**
+** Note that this routine is only used when one or more of various
+** non-standard compile-time options is enabled.
*/
SQLITE_PRIVATE u64 sqlite3LogEstToInt(LogEst x){
u64 n;
- if( x<10 ) return 1;
n = x%10;
x /= 10;
if( n>=5 ) n -= 2;
else if( n>=1 ) n -= 1;
- if( x>=3 ){
- return x>60 ? (u64)LARGEST_INT64 : (n+8)<<(x-3);
- }
- return (n+8)>>(3-x);
+#if defined(SQLITE_ENABLE_STMT_SCANSTATUS) || \
+ defined(SQLITE_EXPLAIN_ESTIMATED_ROWS)
+ if( x>60 ) return (u64)LARGEST_INT64;
+#else
+ /* If only SQLITE_ENABLE_STAT3_OR_STAT4 is on, then the largest input
+ ** possible to this routine is 310, resulting in a maximum x of 31 */
+ assert( x<=60 );
+#endif
+ return x>=3 ? (n+8)<<(x-3) : (n+8)>>(3-x);
}
+#endif /* defined SCANSTAT or STAT4 or ESTIMATED_ROWS */
/************** End of util.c ************************************************/
/************** Begin file hash.c ********************************************/
@@ -24816,6 +28461,7 @@ SQLITE_PRIVATE u64 sqlite3LogEstToInt(LogEst x){
** This is the implementation of generic hash-tables
** used in SQLite.
*/
+/* #include "sqliteInt.h" */
/* #include <assert.h> */
/* Turn bulk memory into a hash table object by initializing the
@@ -24858,7 +28504,7 @@ SQLITE_PRIVATE void sqlite3HashClear(Hash *pH){
static unsigned int strHash(const char *z){
unsigned int h = 0;
unsigned char c;
- while( (c = (unsigned char)*z++)!=0 ){
+ while( (c = (unsigned char)*z++)!=0 ){ /*OPTIMIZATION-IF-TRUE*/
h = (h<<3) ^ h ^ sqlite3UpperToLower[c];
}
return h;
@@ -24951,7 +28597,7 @@ static HashElem *findElementWithHash(
int count; /* Number of elements left to test */
unsigned int h; /* The computed hash */
- if( pH->ht ){
+ if( pH->ht ){ /*OPTIMIZATION-IF-TRUE*/
struct _ht *pEntry;
h = strHash(pKey) % pH->htsize;
pEntry = &pH->ht[h];
@@ -25072,172 +28718,178 @@ SQLITE_PRIVATE void *sqlite3HashInsert(Hash *pH, const char *pKey, void *data){
/************** End of hash.c ************************************************/
/************** Begin file opcodes.c *****************************************/
/* Automatically generated. Do not edit */
-/* See the mkopcodec.awk script for details. */
-#if !defined(SQLITE_OMIT_EXPLAIN) || defined(VDBE_PROFILE) || defined(SQLITE_DEBUG)
+/* See the tool/mkopcodec.tcl script for details. */
+#if !defined(SQLITE_OMIT_EXPLAIN) \
+ || defined(VDBE_PROFILE) \
+ || defined(SQLITE_DEBUG)
#if defined(SQLITE_ENABLE_EXPLAIN_COMMENTS) || defined(SQLITE_DEBUG)
# define OpHelp(X) "\0" X
#else
# define OpHelp(X)
#endif
SQLITE_PRIVATE const char *sqlite3OpcodeName(int i){
- static const char *const azName[] = { "?",
- /* 1 */ "Function" OpHelp("r[P3]=func(r[P2@P5])"),
- /* 2 */ "Savepoint" OpHelp(""),
- /* 3 */ "AutoCommit" OpHelp(""),
- /* 4 */ "Transaction" OpHelp(""),
- /* 5 */ "SorterNext" OpHelp(""),
- /* 6 */ "PrevIfOpen" OpHelp(""),
- /* 7 */ "NextIfOpen" OpHelp(""),
- /* 8 */ "Prev" OpHelp(""),
- /* 9 */ "Next" OpHelp(""),
- /* 10 */ "AggStep" OpHelp("accum=r[P3] step(r[P2@P5])"),
- /* 11 */ "Checkpoint" OpHelp(""),
- /* 12 */ "JournalMode" OpHelp(""),
- /* 13 */ "Vacuum" OpHelp(""),
- /* 14 */ "VFilter" OpHelp("iplan=r[P3] zplan='P4'"),
- /* 15 */ "VUpdate" OpHelp("data=r[P3@P2]"),
- /* 16 */ "Goto" OpHelp(""),
- /* 17 */ "Gosub" OpHelp(""),
- /* 18 */ "Return" OpHelp(""),
- /* 19 */ "Not" OpHelp("r[P2]= !r[P1]"),
- /* 20 */ "InitCoroutine" OpHelp(""),
- /* 21 */ "EndCoroutine" OpHelp(""),
- /* 22 */ "Yield" OpHelp(""),
- /* 23 */ "HaltIfNull" OpHelp("if r[P3]=null halt"),
- /* 24 */ "Halt" OpHelp(""),
- /* 25 */ "Integer" OpHelp("r[P2]=P1"),
- /* 26 */ "Int64" OpHelp("r[P2]=P4"),
- /* 27 */ "String" OpHelp("r[P2]='P4' (len=P1)"),
- /* 28 */ "Null" OpHelp("r[P2..P3]=NULL"),
- /* 29 */ "SoftNull" OpHelp("r[P1]=NULL"),
- /* 30 */ "Blob" OpHelp("r[P2]=P4 (len=P1)"),
- /* 31 */ "Variable" OpHelp("r[P2]=parameter(P1,P4)"),
- /* 32 */ "Move" OpHelp("r[P2@P3]=r[P1@P3]"),
- /* 33 */ "Copy" OpHelp("r[P2@P3+1]=r[P1@P3+1]"),
- /* 34 */ "SCopy" OpHelp("r[P2]=r[P1]"),
- /* 35 */ "ResultRow" OpHelp("output=r[P1@P2]"),
- /* 36 */ "CollSeq" OpHelp(""),
- /* 37 */ "AddImm" OpHelp("r[P1]=r[P1]+P2"),
- /* 38 */ "MustBeInt" OpHelp(""),
- /* 39 */ "RealAffinity" OpHelp(""),
- /* 40 */ "Cast" OpHelp("affinity(r[P1])"),
- /* 41 */ "Permutation" OpHelp(""),
- /* 42 */ "Compare" OpHelp("r[P1@P3] <-> r[P2@P3]"),
- /* 43 */ "Jump" OpHelp(""),
- /* 44 */ "Once" OpHelp(""),
- /* 45 */ "If" OpHelp(""),
- /* 46 */ "IfNot" OpHelp(""),
- /* 47 */ "Column" OpHelp("r[P3]=PX"),
- /* 48 */ "Affinity" OpHelp("affinity(r[P1@P2])"),
- /* 49 */ "MakeRecord" OpHelp("r[P3]=mkrec(r[P1@P2])"),
- /* 50 */ "Count" OpHelp("r[P2]=count()"),
- /* 51 */ "ReadCookie" OpHelp(""),
- /* 52 */ "SetCookie" OpHelp(""),
- /* 53 */ "ReopenIdx" OpHelp("root=P2 iDb=P3"),
- /* 54 */ "OpenRead" OpHelp("root=P2 iDb=P3"),
- /* 55 */ "OpenWrite" OpHelp("root=P2 iDb=P3"),
- /* 56 */ "OpenAutoindex" OpHelp("nColumn=P2"),
- /* 57 */ "OpenEphemeral" OpHelp("nColumn=P2"),
- /* 58 */ "SorterOpen" OpHelp(""),
- /* 59 */ "SequenceTest" OpHelp("if( cursor[P1].ctr++ ) pc = P2"),
- /* 60 */ "OpenPseudo" OpHelp("P3 columns in r[P2]"),
- /* 61 */ "Close" OpHelp(""),
- /* 62 */ "SeekLT" OpHelp("key=r[P3@P4]"),
- /* 63 */ "SeekLE" OpHelp("key=r[P3@P4]"),
- /* 64 */ "SeekGE" OpHelp("key=r[P3@P4]"),
- /* 65 */ "SeekGT" OpHelp("key=r[P3@P4]"),
- /* 66 */ "Seek" OpHelp("intkey=r[P2]"),
- /* 67 */ "NoConflict" OpHelp("key=r[P3@P4]"),
- /* 68 */ "NotFound" OpHelp("key=r[P3@P4]"),
- /* 69 */ "Found" OpHelp("key=r[P3@P4]"),
- /* 70 */ "NotExists" OpHelp("intkey=r[P3]"),
- /* 71 */ "Or" OpHelp("r[P3]=(r[P1] || r[P2])"),
- /* 72 */ "And" OpHelp("r[P3]=(r[P1] && r[P2])"),
- /* 73 */ "Sequence" OpHelp("r[P2]=cursor[P1].ctr++"),
- /* 74 */ "NewRowid" OpHelp("r[P2]=rowid"),
- /* 75 */ "Insert" OpHelp("intkey=r[P3] data=r[P2]"),
- /* 76 */ "IsNull" OpHelp("if r[P1]==NULL goto P2"),
- /* 77 */ "NotNull" OpHelp("if r[P1]!=NULL goto P2"),
- /* 78 */ "Ne" OpHelp("if r[P1]!=r[P3] goto P2"),
- /* 79 */ "Eq" OpHelp("if r[P1]==r[P3] goto P2"),
- /* 80 */ "Gt" OpHelp("if r[P1]>r[P3] goto P2"),
- /* 81 */ "Le" OpHelp("if r[P1]<=r[P3] goto P2"),
- /* 82 */ "Lt" OpHelp("if r[P1]<r[P3] goto P2"),
- /* 83 */ "Ge" OpHelp("if r[P1]>=r[P3] goto P2"),
- /* 84 */ "InsertInt" OpHelp("intkey=P3 data=r[P2]"),
- /* 85 */ "BitAnd" OpHelp("r[P3]=r[P1]&r[P2]"),
- /* 86 */ "BitOr" OpHelp("r[P3]=r[P1]|r[P2]"),
- /* 87 */ "ShiftLeft" OpHelp("r[P3]=r[P2]<<r[P1]"),
- /* 88 */ "ShiftRight" OpHelp("r[P3]=r[P2]>>r[P1]"),
- /* 89 */ "Add" OpHelp("r[P3]=r[P1]+r[P2]"),
- /* 90 */ "Subtract" OpHelp("r[P3]=r[P2]-r[P1]"),
- /* 91 */ "Multiply" OpHelp("r[P3]=r[P1]*r[P2]"),
- /* 92 */ "Divide" OpHelp("r[P3]=r[P2]/r[P1]"),
- /* 93 */ "Remainder" OpHelp("r[P3]=r[P2]%r[P1]"),
- /* 94 */ "Concat" OpHelp("r[P3]=r[P2]+r[P1]"),
- /* 95 */ "Delete" OpHelp(""),
- /* 96 */ "BitNot" OpHelp("r[P1]= ~r[P1]"),
- /* 97 */ "String8" OpHelp("r[P2]='P4'"),
- /* 98 */ "ResetCount" OpHelp(""),
- /* 99 */ "SorterCompare" OpHelp("if key(P1)!=trim(r[P3],P4) goto P2"),
- /* 100 */ "SorterData" OpHelp("r[P2]=data"),
- /* 101 */ "RowKey" OpHelp("r[P2]=key"),
- /* 102 */ "RowData" OpHelp("r[P2]=data"),
- /* 103 */ "Rowid" OpHelp("r[P2]=rowid"),
- /* 104 */ "NullRow" OpHelp(""),
- /* 105 */ "Last" OpHelp(""),
- /* 106 */ "SorterSort" OpHelp(""),
- /* 107 */ "Sort" OpHelp(""),
- /* 108 */ "Rewind" OpHelp(""),
- /* 109 */ "SorterInsert" OpHelp(""),
- /* 110 */ "IdxInsert" OpHelp("key=r[P2]"),
- /* 111 */ "IdxDelete" OpHelp("key=r[P2@P3]"),
- /* 112 */ "IdxRowid" OpHelp("r[P2]=rowid"),
- /* 113 */ "IdxLE" OpHelp("key=r[P3@P4]"),
- /* 114 */ "IdxGT" OpHelp("key=r[P3@P4]"),
- /* 115 */ "IdxLT" OpHelp("key=r[P3@P4]"),
- /* 116 */ "IdxGE" OpHelp("key=r[P3@P4]"),
- /* 117 */ "Destroy" OpHelp(""),
- /* 118 */ "Clear" OpHelp(""),
- /* 119 */ "ResetSorter" OpHelp(""),
- /* 120 */ "CreateIndex" OpHelp("r[P2]=root iDb=P1"),
- /* 121 */ "CreateTable" OpHelp("r[P2]=root iDb=P1"),
- /* 122 */ "ParseSchema" OpHelp(""),
- /* 123 */ "LoadAnalysis" OpHelp(""),
- /* 124 */ "DropTable" OpHelp(""),
- /* 125 */ "DropIndex" OpHelp(""),
- /* 126 */ "DropTrigger" OpHelp(""),
- /* 127 */ "IntegrityCk" OpHelp(""),
- /* 128 */ "RowSetAdd" OpHelp("rowset(P1)=r[P2]"),
- /* 129 */ "RowSetRead" OpHelp("r[P3]=rowset(P1)"),
- /* 130 */ "RowSetTest" OpHelp("if r[P3] in rowset(P1) goto P2"),
- /* 131 */ "Program" OpHelp(""),
- /* 132 */ "Param" OpHelp(""),
- /* 133 */ "Real" OpHelp("r[P2]=P4"),
- /* 134 */ "FkCounter" OpHelp("fkctr[P1]+=P2"),
- /* 135 */ "FkIfZero" OpHelp("if fkctr[P1]==0 goto P2"),
- /* 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 */ "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(""),
+ static const char *const azName[] = {
+ /* 0 */ "Savepoint" OpHelp(""),
+ /* 1 */ "AutoCommit" OpHelp(""),
+ /* 2 */ "Transaction" OpHelp(""),
+ /* 3 */ "SorterNext" OpHelp(""),
+ /* 4 */ "PrevIfOpen" OpHelp(""),
+ /* 5 */ "NextIfOpen" OpHelp(""),
+ /* 6 */ "Prev" OpHelp(""),
+ /* 7 */ "Next" OpHelp(""),
+ /* 8 */ "Checkpoint" OpHelp(""),
+ /* 9 */ "JournalMode" OpHelp(""),
+ /* 10 */ "Vacuum" OpHelp(""),
+ /* 11 */ "VFilter" OpHelp("iplan=r[P3] zplan='P4'"),
+ /* 12 */ "VUpdate" OpHelp("data=r[P3@P2]"),
+ /* 13 */ "Goto" OpHelp(""),
+ /* 14 */ "Gosub" OpHelp(""),
+ /* 15 */ "InitCoroutine" OpHelp(""),
+ /* 16 */ "Yield" OpHelp(""),
+ /* 17 */ "MustBeInt" OpHelp(""),
+ /* 18 */ "Jump" OpHelp(""),
+ /* 19 */ "Not" OpHelp("r[P2]= !r[P1]"),
+ /* 20 */ "Once" OpHelp(""),
+ /* 21 */ "If" OpHelp(""),
+ /* 22 */ "IfNot" OpHelp(""),
+ /* 23 */ "SeekLT" OpHelp("key=r[P3@P4]"),
+ /* 24 */ "SeekLE" OpHelp("key=r[P3@P4]"),
+ /* 25 */ "SeekGE" OpHelp("key=r[P3@P4]"),
+ /* 26 */ "SeekGT" OpHelp("key=r[P3@P4]"),
+ /* 27 */ "Or" OpHelp("r[P3]=(r[P1] || r[P2])"),
+ /* 28 */ "And" OpHelp("r[P3]=(r[P1] && r[P2])"),
+ /* 29 */ "NoConflict" OpHelp("key=r[P3@P4]"),
+ /* 30 */ "NotFound" OpHelp("key=r[P3@P4]"),
+ /* 31 */ "Found" OpHelp("key=r[P3@P4]"),
+ /* 32 */ "NotExists" OpHelp("intkey=r[P3]"),
+ /* 33 */ "Last" OpHelp(""),
+ /* 34 */ "IsNull" OpHelp("if r[P1]==NULL goto P2"),
+ /* 35 */ "NotNull" OpHelp("if r[P1]!=NULL goto P2"),
+ /* 36 */ "Ne" OpHelp("if r[P1]!=r[P3] goto P2"),
+ /* 37 */ "Eq" OpHelp("if r[P1]==r[P3] goto P2"),
+ /* 38 */ "Gt" OpHelp("if r[P1]>r[P3] goto P2"),
+ /* 39 */ "Le" OpHelp("if r[P1]<=r[P3] goto P2"),
+ /* 40 */ "Lt" OpHelp("if r[P1]<r[P3] goto P2"),
+ /* 41 */ "Ge" OpHelp("if r[P1]>=r[P3] goto P2"),
+ /* 42 */ "SorterSort" OpHelp(""),
+ /* 43 */ "BitAnd" OpHelp("r[P3]=r[P1]&r[P2]"),
+ /* 44 */ "BitOr" OpHelp("r[P3]=r[P1]|r[P2]"),
+ /* 45 */ "ShiftLeft" OpHelp("r[P3]=r[P2]<<r[P1]"),
+ /* 46 */ "ShiftRight" OpHelp("r[P3]=r[P2]>>r[P1]"),
+ /* 47 */ "Add" OpHelp("r[P3]=r[P1]+r[P2]"),
+ /* 48 */ "Subtract" OpHelp("r[P3]=r[P2]-r[P1]"),
+ /* 49 */ "Multiply" OpHelp("r[P3]=r[P1]*r[P2]"),
+ /* 50 */ "Divide" OpHelp("r[P3]=r[P2]/r[P1]"),
+ /* 51 */ "Remainder" OpHelp("r[P3]=r[P2]%r[P1]"),
+ /* 52 */ "Concat" OpHelp("r[P3]=r[P2]+r[P1]"),
+ /* 53 */ "Sort" OpHelp(""),
+ /* 54 */ "BitNot" OpHelp("r[P1]= ~r[P1]"),
+ /* 55 */ "Rewind" OpHelp(""),
+ /* 56 */ "IdxLE" OpHelp("key=r[P3@P4]"),
+ /* 57 */ "IdxGT" OpHelp("key=r[P3@P4]"),
+ /* 58 */ "IdxLT" OpHelp("key=r[P3@P4]"),
+ /* 59 */ "IdxGE" OpHelp("key=r[P3@P4]"),
+ /* 60 */ "RowSetRead" OpHelp("r[P3]=rowset(P1)"),
+ /* 61 */ "RowSetTest" OpHelp("if r[P3] in rowset(P1) goto P2"),
+ /* 62 */ "Program" OpHelp(""),
+ /* 63 */ "FkIfZero" OpHelp("if fkctr[P1]==0 goto P2"),
+ /* 64 */ "IfPos" OpHelp("if r[P1]>0 then r[P1]-=P3, goto P2"),
+ /* 65 */ "IfNotZero" OpHelp("if r[P1]!=0 then r[P1]-=P3, goto P2"),
+ /* 66 */ "DecrJumpZero" OpHelp("if (--r[P1])==0 goto P2"),
+ /* 67 */ "IncrVacuum" OpHelp(""),
+ /* 68 */ "VNext" OpHelp(""),
+ /* 69 */ "Init" OpHelp("Start at P2"),
+ /* 70 */ "Return" OpHelp(""),
+ /* 71 */ "EndCoroutine" OpHelp(""),
+ /* 72 */ "HaltIfNull" OpHelp("if r[P3]=null halt"),
+ /* 73 */ "Halt" OpHelp(""),
+ /* 74 */ "Integer" OpHelp("r[P2]=P1"),
+ /* 75 */ "Int64" OpHelp("r[P2]=P4"),
+ /* 76 */ "String" OpHelp("r[P2]='P4' (len=P1)"),
+ /* 77 */ "Null" OpHelp("r[P2..P3]=NULL"),
+ /* 78 */ "SoftNull" OpHelp("r[P1]=NULL"),
+ /* 79 */ "Blob" OpHelp("r[P2]=P4 (len=P1)"),
+ /* 80 */ "Variable" OpHelp("r[P2]=parameter(P1,P4)"),
+ /* 81 */ "Move" OpHelp("r[P2@P3]=r[P1@P3]"),
+ /* 82 */ "Copy" OpHelp("r[P2@P3+1]=r[P1@P3+1]"),
+ /* 83 */ "SCopy" OpHelp("r[P2]=r[P1]"),
+ /* 84 */ "IntCopy" OpHelp("r[P2]=r[P1]"),
+ /* 85 */ "ResultRow" OpHelp("output=r[P1@P2]"),
+ /* 86 */ "CollSeq" OpHelp(""),
+ /* 87 */ "Function0" OpHelp("r[P3]=func(r[P2@P5])"),
+ /* 88 */ "Function" OpHelp("r[P3]=func(r[P2@P5])"),
+ /* 89 */ "AddImm" OpHelp("r[P1]=r[P1]+P2"),
+ /* 90 */ "RealAffinity" OpHelp(""),
+ /* 91 */ "Cast" OpHelp("affinity(r[P1])"),
+ /* 92 */ "Permutation" OpHelp(""),
+ /* 93 */ "Compare" OpHelp("r[P1@P3] <-> r[P2@P3]"),
+ /* 94 */ "Column" OpHelp("r[P3]=PX"),
+ /* 95 */ "Affinity" OpHelp("affinity(r[P1@P2])"),
+ /* 96 */ "MakeRecord" OpHelp("r[P3]=mkrec(r[P1@P2])"),
+ /* 97 */ "String8" OpHelp("r[P2]='P4'"),
+ /* 98 */ "Count" OpHelp("r[P2]=count()"),
+ /* 99 */ "ReadCookie" OpHelp(""),
+ /* 100 */ "SetCookie" OpHelp(""),
+ /* 101 */ "ReopenIdx" OpHelp("root=P2 iDb=P3"),
+ /* 102 */ "OpenRead" OpHelp("root=P2 iDb=P3"),
+ /* 103 */ "OpenWrite" OpHelp("root=P2 iDb=P3"),
+ /* 104 */ "OpenAutoindex" OpHelp("nColumn=P2"),
+ /* 105 */ "OpenEphemeral" OpHelp("nColumn=P2"),
+ /* 106 */ "SorterOpen" OpHelp(""),
+ /* 107 */ "SequenceTest" OpHelp("if( cursor[P1].ctr++ ) pc = P2"),
+ /* 108 */ "OpenPseudo" OpHelp("P3 columns in r[P2]"),
+ /* 109 */ "Close" OpHelp(""),
+ /* 110 */ "ColumnsUsed" OpHelp(""),
+ /* 111 */ "Sequence" OpHelp("r[P2]=cursor[P1].ctr++"),
+ /* 112 */ "NewRowid" OpHelp("r[P2]=rowid"),
+ /* 113 */ "Insert" OpHelp("intkey=r[P3] data=r[P2]"),
+ /* 114 */ "InsertInt" OpHelp("intkey=P3 data=r[P2]"),
+ /* 115 */ "Delete" OpHelp(""),
+ /* 116 */ "ResetCount" OpHelp(""),
+ /* 117 */ "SorterCompare" OpHelp("if key(P1)!=trim(r[P3],P4) goto P2"),
+ /* 118 */ "SorterData" OpHelp("r[P2]=data"),
+ /* 119 */ "RowKey" OpHelp("r[P2]=key"),
+ /* 120 */ "RowData" OpHelp("r[P2]=data"),
+ /* 121 */ "Rowid" OpHelp("r[P2]=rowid"),
+ /* 122 */ "NullRow" OpHelp(""),
+ /* 123 */ "SorterInsert" OpHelp(""),
+ /* 124 */ "IdxInsert" OpHelp("key=r[P2]"),
+ /* 125 */ "IdxDelete" OpHelp("key=r[P2@P3]"),
+ /* 126 */ "Seek" OpHelp("Move P3 to P1.rowid"),
+ /* 127 */ "IdxRowid" OpHelp("r[P2]=rowid"),
+ /* 128 */ "Destroy" OpHelp(""),
+ /* 129 */ "Clear" OpHelp(""),
+ /* 130 */ "ResetSorter" OpHelp(""),
+ /* 131 */ "CreateIndex" OpHelp("r[P2]=root iDb=P1"),
+ /* 132 */ "CreateTable" OpHelp("r[P2]=root iDb=P1"),
+ /* 133 */ "Real" OpHelp("r[P2]=P4"),
+ /* 134 */ "ParseSchema" OpHelp(""),
+ /* 135 */ "LoadAnalysis" OpHelp(""),
+ /* 136 */ "DropTable" OpHelp(""),
+ /* 137 */ "DropIndex" OpHelp(""),
+ /* 138 */ "DropTrigger" OpHelp(""),
+ /* 139 */ "IntegrityCk" OpHelp(""),
+ /* 140 */ "RowSetAdd" OpHelp("rowset(P1)=r[P2]"),
+ /* 141 */ "Param" OpHelp(""),
+ /* 142 */ "FkCounter" OpHelp("fkctr[P1]+=P2"),
+ /* 143 */ "MemMax" OpHelp("r[P1]=max(r[P1],r[P2])"),
+ /* 144 */ "OffsetLimit" OpHelp("if r[P1]>0 then r[P2]=r[P1]+max(0,r[P3]) else r[P2]=(-1)"),
+ /* 145 */ "AggStep0" OpHelp("accum=r[P3] step(r[P2@P5])"),
+ /* 146 */ "AggStep" OpHelp("accum=r[P3] step(r[P2@P5])"),
+ /* 147 */ "AggFinal" OpHelp("accum=r[P1] N=P2"),
+ /* 148 */ "Expire" OpHelp(""),
+ /* 149 */ "TableLock" OpHelp("iDb=P1 root=P2 write=P3"),
+ /* 150 */ "VBegin" OpHelp(""),
+ /* 151 */ "VCreate" OpHelp(""),
+ /* 152 */ "VDestroy" OpHelp(""),
+ /* 153 */ "VOpen" OpHelp(""),
+ /* 154 */ "VColumn" OpHelp("r[P3]=vcolumn(P2)"),
+ /* 155 */ "VRename" OpHelp(""),
+ /* 156 */ "Pagecount" OpHelp(""),
+ /* 157 */ "MaxPgcnt" OpHelp(""),
+ /* 158 */ "CursorHint" OpHelp(""),
+ /* 159 */ "Noop" OpHelp(""),
+ /* 160 */ "Explain" OpHelp(""),
};
return azName[i];
}
@@ -25290,6 +28942,7 @@ SQLITE_PRIVATE const char *sqlite3OpcodeName(int i){
** * Definitions of sqlite3_vfs objects for all locking methods
** plus implementations of sqlite3_os_init() and sqlite3_os_end().
*/
+/* #include "sqliteInt.h" */
#if SQLITE_OS_UNIX /* This file is used on unix only */
/*
@@ -25317,6 +28970,19 @@ SQLITE_PRIVATE const char *sqlite3OpcodeName(int i){
# endif
#endif
+/* Use pread() and pwrite() if they are available */
+#if defined(__APPLE__)
+# define HAVE_PREAD 1
+# define HAVE_PWRITE 1
+#endif
+#if defined(HAVE_PREAD64) && defined(HAVE_PWRITE64)
+# undef USE_PREAD
+# define USE_PREAD64 1
+#elif defined(HAVE_PREAD) && defined(HAVE_PWRITE)
+# undef USE_PREAD64
+# define USE_PREAD 1
+#endif
+
/*
** standard include files.
*/
@@ -25395,6 +29061,11 @@ SQLITE_PRIVATE const char *sqlite3OpcodeName(int i){
*/
#define MAX_PATHNAME 512
+/*
+** Maximum supported symbolic links
+*/
+#define SQLITE_MAX_SYMLINKS 100
+
/* Always cast the getpid() return type for compatibility with
** kernel modules in VxWorks. */
#define osGetpid(X) (pid_t)getpid()
@@ -25504,8 +29175,6 @@ static pid_t 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 issued */
-#define UNIXFILE_BLOCK 0x0200 /* Next SHM lock might block */
/*
** Include code that is common to all os_*.c files
@@ -25549,8 +29218,8 @@ static pid_t randomnessPid = 0;
*/
#ifdef SQLITE_PERFORMANCE_TRACE
-/*
-** hwtime.h contains inline assembler code for implementing
+/*
+** hwtime.h contains inline assembler code for implementing
** high-performance timing routines.
*/
/************** Include hwtime.h in the middle of os_common.h ****************/
@@ -25660,14 +29329,14 @@ static sqlite_uint64 g_elapsed;
** of code will give us the ability to simulate a disk I/O error. This
** is used for testing the I/O recovery logic.
*/
-#ifdef SQLITE_TEST
-SQLITE_API int sqlite3_io_error_hit = 0; /* Total number of I/O Errors */
-SQLITE_API int sqlite3_io_error_hardhit = 0; /* Number of non-benign errors */
-SQLITE_API int sqlite3_io_error_pending = 0; /* Count down to first I/O error */
-SQLITE_API int sqlite3_io_error_persist = 0; /* True if I/O errors persist */
-SQLITE_API int sqlite3_io_error_benign = 0; /* True if errors are benign */
-SQLITE_API int sqlite3_diskfull_pending = 0;
-SQLITE_API int sqlite3_diskfull = 0;
+#if defined(SQLITE_TEST)
+SQLITE_API extern int sqlite3_io_error_hit;
+SQLITE_API extern int sqlite3_io_error_hardhit;
+SQLITE_API extern int sqlite3_io_error_pending;
+SQLITE_API extern int sqlite3_io_error_persist;
+SQLITE_API extern int sqlite3_io_error_benign;
+SQLITE_API extern int sqlite3_diskfull_pending;
+SQLITE_API extern int sqlite3_diskfull;
#define SimulateIOErrorBenign(X) sqlite3_io_error_benign=(X)
#define SimulateIOError(CODE) \
if( (sqlite3_io_error_persist && sqlite3_io_error_hit) \
@@ -25693,17 +29362,17 @@ static void local_ioerr(){
#define SimulateIOErrorBenign(X)
#define SimulateIOError(A)
#define SimulateDiskfullError(A)
-#endif
+#endif /* defined(SQLITE_TEST) */
/*
** When testing, keep a count of the number of open files.
*/
-#ifdef SQLITE_TEST
-SQLITE_API int sqlite3_open_file_count = 0;
+#if defined(SQLITE_TEST)
+SQLITE_API extern int sqlite3_open_file_count;
#define OpenCounter(X) sqlite3_open_file_count+=(X)
#else
#define OpenCounter(X)
-#endif
+#endif /* defined(SQLITE_TEST) */
#endif /* !defined(_OS_COMMON_H_) */
@@ -25768,19 +29437,6 @@ static int posixOpen(const char *zFile, int flags, int mode){
return open(zFile, flags, mode);
}
-/*
-** On some systems, calls to fchown() will trigger a message in a security
-** log if they come from non-root processes. So avoid calling fchown() if
-** we are not running as root.
-*/
-static int posixFchown(int fd, uid_t uid, gid_t gid){
-#if OS_VXWORKS
- return 0;
-#else
- return geteuid() ? 0 : fchown(fd,uid,gid);
-#endif
-}
-
/* Forward reference */
static int openDirectory(const char*, int*);
static int unixGetpagesize(void);
@@ -25846,7 +29502,7 @@ static struct unix_syscall {
#else
{ "pread64", (sqlite3_syscall_ptr)0, 0 },
#endif
-#define osPread64 ((ssize_t(*)(int,void*,size_t,off_t))aSyscall[10].pCurrent)
+#define osPread64 ((ssize_t(*)(int,void*,size_t,off64_t))aSyscall[10].pCurrent)
{ "write", (sqlite3_syscall_ptr)write, 0 },
#define osWrite ((ssize_t(*)(int,const void*,size_t))aSyscall[11].pCurrent)
@@ -25864,10 +29520,10 @@ static struct unix_syscall {
#else
{ "pwrite64", (sqlite3_syscall_ptr)0, 0 },
#endif
-#define osPwrite64 ((ssize_t(*)(int,const void*,size_t,off_t))\
+#define osPwrite64 ((ssize_t(*)(int,const void*,size_t,off64_t))\
aSyscall[13].pCurrent)
- { "fchmod", (sqlite3_syscall_ptr)fchmod, 0 },
+ { "fchmod", (sqlite3_syscall_ptr)fchmod, 0 },
#define osFchmod ((int(*)(int,mode_t))aSyscall[14].pCurrent)
#if defined(HAVE_POSIX_FALLOCATE) && HAVE_POSIX_FALLOCATE
@@ -25889,29 +29545,74 @@ static struct unix_syscall {
{ "rmdir", (sqlite3_syscall_ptr)rmdir, 0 },
#define osRmdir ((int(*)(const char*))aSyscall[19].pCurrent)
- { "fchown", (sqlite3_syscall_ptr)posixFchown, 0 },
+#if defined(HAVE_FCHOWN)
+ { "fchown", (sqlite3_syscall_ptr)fchown, 0 },
+#else
+ { "fchown", (sqlite3_syscall_ptr)0, 0 },
+#endif
#define osFchown ((int(*)(int,uid_t,gid_t))aSyscall[20].pCurrent)
+ { "geteuid", (sqlite3_syscall_ptr)geteuid, 0 },
+#define osGeteuid ((uid_t(*)(void))aSyscall[21].pCurrent)
+
#if !defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0
- { "mmap", (sqlite3_syscall_ptr)mmap, 0 },
-#define osMmap ((void*(*)(void*,size_t,int,int,int,off_t))aSyscall[21].pCurrent)
+ { "mmap", (sqlite3_syscall_ptr)mmap, 0 },
+#else
+ { "mmap", (sqlite3_syscall_ptr)0, 0 },
+#endif
+#define osMmap ((void*(*)(void*,size_t,int,int,int,off_t))aSyscall[22].pCurrent)
+#if !defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0
{ "munmap", (sqlite3_syscall_ptr)munmap, 0 },
-#define osMunmap ((void*(*)(void*,size_t))aSyscall[22].pCurrent)
+#else
+ { "munmap", (sqlite3_syscall_ptr)0, 0 },
+#endif
+#define osMunmap ((void*(*)(void*,size_t))aSyscall[23].pCurrent)
-#if HAVE_MREMAP
+#if HAVE_MREMAP && (!defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0)
{ "mremap", (sqlite3_syscall_ptr)mremap, 0 },
#else
{ "mremap", (sqlite3_syscall_ptr)0, 0 },
#endif
-#define osMremap ((void*(*)(void*,size_t,size_t,int,...))aSyscall[23].pCurrent)
+#define osMremap ((void*(*)(void*,size_t,size_t,int,...))aSyscall[24].pCurrent)
+
+#if !defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0
{ "getpagesize", (sqlite3_syscall_ptr)unixGetpagesize, 0 },
-#define osGetpagesize ((int(*)(void))aSyscall[24].pCurrent)
+#else
+ { "getpagesize", (sqlite3_syscall_ptr)0, 0 },
+#endif
+#define osGetpagesize ((int(*)(void))aSyscall[25].pCurrent)
+
+#if defined(HAVE_READLINK)
+ { "readlink", (sqlite3_syscall_ptr)readlink, 0 },
+#else
+ { "readlink", (sqlite3_syscall_ptr)0, 0 },
+#endif
+#define osReadlink ((ssize_t(*)(const char*,char*,size_t))aSyscall[26].pCurrent)
+#if defined(HAVE_LSTAT)
+ { "lstat", (sqlite3_syscall_ptr)lstat, 0 },
+#else
+ { "lstat", (sqlite3_syscall_ptr)0, 0 },
#endif
+#define osLstat ((int(*)(const char*,struct stat*))aSyscall[27].pCurrent)
}; /* End of the overrideable system calls */
+
+/*
+** On some systems, calls to fchown() will trigger a message in a security
+** log if they come from non-root processes. So avoid calling fchown() if
+** we are not running as root.
+*/
+static int robustFchown(int fd, uid_t uid, gid_t gid){
+#if defined(HAVE_FCHOWN)
+ return osGeteuid() ? 0 : osFchown(fd,uid,gid);
+#else
+ return 0;
+#endif
+}
+
/*
** This is the xSetSystemCall() method of sqlite3_vfs for all of the
** "unix" VFSes. Return SQLITE_OK opon successfully updating the
@@ -26073,14 +29774,14 @@ static int robust_open(const char *z, int f, mode_t m){
** unixEnterLeave()
*/
static void unixEnterMutex(void){
- sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER));
+ sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_VFS1));
}
static void unixLeaveMutex(void){
- sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER));
+ sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_VFS1));
}
#ifdef SQLITE_DEBUG
static int unixMutexHeld(void) {
- return sqlite3_mutex_held(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER));
+ return sqlite3_mutex_held(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_VFS1));
}
#endif
@@ -26196,23 +29897,12 @@ static int robust_ftruncate(int h, sqlite3_int64 sz){
** should handle ENOLCK, ENOTSUP, EOPNOTSUPP separately.
*/
static int sqliteErrorFromPosixError(int posixError, int sqliteIOErr) {
+ assert( (sqliteIOErr == SQLITE_IOERR_LOCK) ||
+ (sqliteIOErr == SQLITE_IOERR_UNLOCK) ||
+ (sqliteIOErr == SQLITE_IOERR_RDLOCK) ||
+ (sqliteIOErr == SQLITE_IOERR_CHECKRESERVEDLOCK) );
switch (posixError) {
-#if 0
- /* At one point this code was not commented out. In theory, this branch
- ** should never be hit, as this function should only be called after
- ** a locking-related function (i.e. fcntl()) has returned non-zero with
- ** the value of errno as the first argument. Since a system call has failed,
- ** errno should be non-zero.
- **
- ** Despite this, if errno really is zero, we still don't want to return
- ** SQLITE_OK. The system call failed, and *some* SQLite error should be
- ** propagated back to the caller. Commenting this branch out means errno==0
- ** will be handled by the "default:" case below.
- */
- case 0:
- return SQLITE_OK;
-#endif
-
+ case EACCES:
case EAGAIN:
case ETIMEDOUT:
case EBUSY:
@@ -26222,41 +29912,9 @@ static int sqliteErrorFromPosixError(int posixError, int sqliteIOErr) {
* introspection, in which it actually means what it says */
return SQLITE_BUSY;
- case EACCES:
- /* EACCES is like EAGAIN during locking operations, but not any other time*/
- if( (sqliteIOErr == SQLITE_IOERR_LOCK) ||
- (sqliteIOErr == SQLITE_IOERR_UNLOCK) ||
- (sqliteIOErr == SQLITE_IOERR_RDLOCK) ||
- (sqliteIOErr == SQLITE_IOERR_CHECKRESERVEDLOCK) ){
- return SQLITE_BUSY;
- }
- /* else fall through */
case EPERM:
return SQLITE_PERM;
-#if EOPNOTSUPP!=ENOTSUP
- case EOPNOTSUPP:
- /* something went terribly awry, unless during file system support
- * introspection, in which it actually means what it says */
-#endif
-#ifdef ENOTSUP
- case ENOTSUP:
- /* invalid fd, unless during file system support introspection, in which
- * it actually means what it says */
-#endif
- case EIO:
- case EBADF:
- case EINVAL:
- case ENOTCONN:
- case ENODEV:
- case ENXIO:
- case ENOENT:
-#ifdef ESTALE /* ESTALE is not defined on Interix systems */
- case ESTALE:
-#endif
- case ENOSYS:
- /* these should force the client to close the file and reconnect */
-
default:
return sqliteIOErr;
}
@@ -26540,7 +30198,7 @@ static unixInodeInfo *inodeList = 0;
/*
**
-** This function - unixLogError_x(), is only ever called via the macro
+** This function - unixLogErrorAtLine(), is only ever called via the macro
** unixLogError().
**
** It is invoked after an error occurs in an OS function and errno has been
@@ -26709,7 +30367,7 @@ static int findInodeInfo(
rc = osFstat(fd, &statbuf);
if( rc!=0 ){
storeLastErrno(pFile, errno);
-#ifdef EOVERFLOW
+#if defined(EOVERFLOW) && defined(SQLITE_DISABLE_LFS)
if( pFile->lastErrno==EOVERFLOW ) return SQLITE_NOLFS;
#endif
return SQLITE_IOERR;
@@ -26754,7 +30412,7 @@ static int findInodeInfo(
if( pInode==0 ){
pInode = sqlite3_malloc64( sizeof(*pInode) );
if( pInode==0 ){
- return SQLITE_NOMEM;
+ return SQLITE_NOMEM_BKPT;
}
memset(pInode, 0, sizeof(*pInode));
memcpy(&pInode->fileId, &fileId, sizeof(fileId));
@@ -26796,30 +30454,25 @@ static int fileHasMoved(unixFile *pFile){
static void verifyDbFile(unixFile *pFile){
struct stat buf;
int rc;
- if( pFile->ctrlFlags & UNIXFILE_WARNED ){
- /* One or more of the following warnings have already been issued. Do not
- ** repeat them so as not to clutter the error log */
- return;
- }
+
+ /* These verifications occurs for the main database only */
+ if( pFile->ctrlFlags & UNIXFILE_NOLOCK ) return;
+
rc = osFstat(pFile->h, &buf);
if( rc!=0 ){
sqlite3_log(SQLITE_WARNING, "cannot fstat db file %s", pFile->zPath);
- pFile->ctrlFlags |= UNIXFILE_WARNED;
return;
}
- if( buf.st_nlink==0 && (pFile->ctrlFlags & UNIXFILE_DELETE)==0 ){
+ if( buf.st_nlink==0 ){
sqlite3_log(SQLITE_WARNING, "file unlinked while open: %s", pFile->zPath);
- pFile->ctrlFlags |= UNIXFILE_WARNED;
return;
}
if( buf.st_nlink>1 ){
sqlite3_log(SQLITE_WARNING, "multiple links to file: %s", pFile->zPath);
- pFile->ctrlFlags |= UNIXFILE_WARNED;
return;
}
if( fileHasMoved(pFile) ){
sqlite3_log(SQLITE_WARNING, "file renamed while open: %s", pFile->zPath);
- pFile->ctrlFlags |= UNIXFILE_WARNED;
return;
}
}
@@ -26839,6 +30492,7 @@ static int unixCheckReservedLock(sqlite3_file *id, int *pResOut){
SimulateIOError( return SQLITE_IOERR_CHECKRESERVEDLOCK; );
assert( pFile );
+ assert( pFile->eFileLock<=SHARED_LOCK );
unixEnterMutex(); /* Because pFile->pInode is shared across threads */
/* Check if a thread in this process holds such a lock */
@@ -26895,9 +30549,7 @@ static int unixFileLock(unixFile *pFile, struct flock *pLock){
unixInodeInfo *pInode = pFile->pInode;
assert( unixMutexHeld() );
assert( pInode!=0 );
- if( ((pFile->ctrlFlags & UNIXFILE_EXCL)!=0 || pInode->bProcessLock)
- && ((pFile->ctrlFlags & UNIXFILE_RDONLY)==0)
- ){
+ if( (pFile->ctrlFlags & (UNIXFILE_EXCL|UNIXFILE_RDONLY))==UNIXFILE_EXCL ){
if( pInode->bProcessLock==0 ){
struct flock lock;
assert( pInode->nLock==0 );
@@ -26947,7 +30599,7 @@ static int unixLock(sqlite3_file *id, int eFileLock){
** lock transitions in terms of the POSIX advisory shared and exclusive
** lock primitives (called read-locks and write-locks below, to avoid
** confusion with SQLite lock names). The algorithms are complicated
- ** slightly in order to be compatible with windows systems simultaneously
+ ** slightly in order to be compatible with Windows95 systems simultaneously
** accessing the same database file, in case that is ever required.
**
** Symbols defined in os.h indentify the 'pending byte' and the 'reserved
@@ -26955,8 +30607,14 @@ static int unixLock(sqlite3_file *id, int eFileLock){
** range', a range of 510 bytes at a well known offset.
**
** To obtain a SHARED lock, a read-lock is obtained on the 'pending
- ** byte'. If this is successful, a random byte from the 'shared byte
- ** range' is read-locked and the lock on the 'pending byte' released.
+ ** byte'. If this is successful, 'shared byte range' is read-locked
+ ** and the lock on the 'pending byte' released. (Legacy note: When
+ ** SQLite was first developed, Windows95 systems were still very common,
+ ** and Widnows95 lacks a shared-lock capability. So on Windows95, a
+ ** single randomly selected by from the 'shared byte range' is locked.
+ ** Windows95 is now pretty much extinct, but this work-around for the
+ ** lack of shared-locks on Windows95 lives on, for backwards
+ ** compatibility.)
**
** A process may only obtain a RESERVED lock after it has a SHARED lock.
** A RESERVED lock is implemented by grabbing a write-lock on the
@@ -26975,11 +30633,6 @@ static int unixLock(sqlite3_file *id, int eFileLock){
** range'. Since all other locks require a read-lock on one of the bytes
** within this range, this ensures that no other locks are held on the
** database.
- **
- ** The reason a single byte cannot be used instead of the 'shared byte
- ** range' is that some versions of windows do not support read-locks. By
- ** locking a random byte from a range, concurrent SHARED locks may exist
- ** even if the locking primitive used is always a write-lock.
*/
int rc = SQLITE_OK;
unixFile *pFile = (unixFile*)id;
@@ -27249,9 +30902,7 @@ static int posixUnlock(sqlite3_file *id, int eFileLock, int handleNFSUnlock){
if( unixFileLock(pFile, &lock)==(-1) ){
tErrno = errno;
rc = SQLITE_IOERR_UNLOCK;
- if( IS_LOCK_ERROR(rc) ){
- storeLastErrno(pFile, tErrno);
- }
+ storeLastErrno(pFile, tErrno);
goto end_unlock;
}
lock.l_type = F_RDLCK;
@@ -27273,9 +30924,7 @@ static int posixUnlock(sqlite3_file *id, int eFileLock, int handleNFSUnlock){
if( unixFileLock(pFile, &lock)==(-1) ){
tErrno = errno;
rc = SQLITE_IOERR_UNLOCK;
- if( IS_LOCK_ERROR(rc) ){
- storeLastErrno(pFile, tErrno);
- }
+ storeLastErrno(pFile, tErrno);
goto end_unlock;
}
}else
@@ -27526,17 +31175,7 @@ static int dotlockCheckReservedLock(sqlite3_file *id, int *pResOut) {
SimulateIOError( return SQLITE_IOERR_CHECKRESERVEDLOCK; );
assert( pFile );
-
- /* Check if a thread in this process holds such a lock */
- if( pFile->eFileLock>SHARED_LOCK ){
- /* Either this connection or some other connection in the same process
- ** holds a lock on the file. No need to check further. */
- reserved = 1;
- }else{
- /* The lock is held if and only if the lockfile exists */
- const char *zLockFile = (const char*)pFile->lockingContext;
- reserved = osAccess(zLockFile, 0)==0;
- }
+ reserved = osAccess((const char*)pFile->lockingContext, 0)==0;
OSTRACE(("TEST WR-LOCK %d %d %d (dotlock)\n", pFile->h, rc, reserved));
*pResOut = reserved;
return rc;
@@ -27598,7 +31237,7 @@ static int dotlockLock(sqlite3_file *id, int eFileLock) {
rc = SQLITE_BUSY;
} else {
rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_LOCK);
- if( IS_LOCK_ERROR(rc) ){
+ if( rc!=SQLITE_BUSY ){
storeLastErrno(pFile, tErrno);
}
}
@@ -27645,14 +31284,12 @@ static int dotlockUnlock(sqlite3_file *id, int eFileLock) {
/* To fully unlock the database, delete the lock file */
assert( eFileLock==NO_LOCK );
rc = osRmdir(zLockFile);
- if( rc<0 && errno==ENOTDIR ) rc = osUnlink(zLockFile);
if( rc<0 ){
int tErrno = errno;
- rc = 0;
- if( ENOENT != tErrno ){
+ if( tErrno==ENOENT ){
+ rc = SQLITE_OK;
+ }else{
rc = SQLITE_IOERR_UNLOCK;
- }
- if( IS_LOCK_ERROR(rc) ){
storeLastErrno(pFile, tErrno);
}
return rc;
@@ -27665,14 +31302,11 @@ static int dotlockUnlock(sqlite3_file *id, int eFileLock) {
** Close a file. Make sure the lock has been released before closing.
*/
static int dotlockClose(sqlite3_file *id) {
- int rc = SQLITE_OK;
- if( id ){
- unixFile *pFile = (unixFile*)id;
- dotlockUnlock(id, NO_LOCK);
- sqlite3_free(pFile->lockingContext);
- rc = closeUnixFile(id);
- }
- return rc;
+ unixFile *pFile = (unixFile*)id;
+ assert( id!=0 );
+ dotlockUnlock(id, NO_LOCK);
+ sqlite3_free(pFile->lockingContext);
+ return closeUnixFile(id);
}
/****************** End of the dot-file lock implementation *******************
******************************************************************************/
@@ -27738,10 +31372,8 @@ static int flockCheckReservedLock(sqlite3_file *id, int *pResOut){
int tErrno = errno;
/* unlock failed with an error */
lrc = SQLITE_IOERR_UNLOCK;
- if( IS_LOCK_ERROR(lrc) ){
- storeLastErrno(pFile, tErrno);
- rc = lrc;
- }
+ storeLastErrno(pFile, tErrno);
+ rc = lrc;
}
} else {
int tErrno = errno;
@@ -27874,12 +31506,9 @@ static int flockUnlock(sqlite3_file *id, int eFileLock) {
** Close a file.
*/
static int flockClose(sqlite3_file *id) {
- int rc = SQLITE_OK;
- if( id ){
- flockUnlock(id, NO_LOCK);
- rc = closeUnixFile(id);
- }
- return rc;
+ assert( id!=0 );
+ flockUnlock(id, NO_LOCK);
+ return closeUnixFile(id);
}
#endif /* SQLITE_ENABLE_LOCKING_STYLE && !OS_VXWORK */
@@ -28504,23 +32133,22 @@ static int afpUnlock(sqlite3_file *id, int eFileLock) {
*/
static int afpClose(sqlite3_file *id) {
int rc = SQLITE_OK;
- if( id ){
- unixFile *pFile = (unixFile*)id;
- afpUnlock(id, NO_LOCK);
- unixEnterMutex();
- if( pFile->pInode && pFile->pInode->nLock ){
- /* If there are outstanding locks, do not actually close the file just
- ** yet because that would clear those locks. Instead, add the file
- ** descriptor to pInode->aPending. It will be automatically closed when
- ** the last lock is cleared.
- */
- setPendingFd(pFile);
- }
- releaseInodeInfo(pFile);
- sqlite3_free(pFile->lockingContext);
- rc = closeUnixFile(id);
- unixLeaveMutex();
+ unixFile *pFile = (unixFile*)id;
+ assert( id!=0 );
+ afpUnlock(id, NO_LOCK);
+ unixEnterMutex();
+ if( pFile->pInode && pFile->pInode->nLock ){
+ /* If there are outstanding locks, do not actually close the file just
+ ** yet because that would clear those locks. Instead, add the file
+ ** descriptor to pInode->aPending. It will be automatically closed when
+ ** the last lock is cleared.
+ */
+ setPendingFd(pFile);
}
+ releaseInodeInfo(pFile);
+ sqlite3_free(pFile->lockingContext);
+ rc = closeUnixFile(id);
+ unixLeaveMutex();
return rc;
}
@@ -28590,7 +32218,6 @@ static int seekAndRead(unixFile *id, sqlite3_int64 offset, void *pBuf, int cnt){
TIMER_START;
assert( cnt==(cnt&0x1ffff) );
assert( id->h>2 );
- cnt &= 0x1ffff;
do{
#if defined(USE_PREAD)
got = osPread(id->h, pBuf, cnt, offset);
@@ -28600,13 +32227,9 @@ static int seekAndRead(unixFile *id, sqlite3_int64 offset, void *pBuf, int cnt){
SimulateIOError( got = -1 );
#else
newOffset = lseek(id->h, offset, SEEK_SET);
- SimulateIOError( newOffset-- );
- if( newOffset!=offset ){
- if( newOffset == -1 ){
- storeLastErrno((unixFile*)id, errno);
- }else{
- storeLastErrno((unixFile*)id, 0);
- }
+ SimulateIOError( newOffset = -1 );
+ if( newOffset<0 ){
+ storeLastErrno((unixFile*)id, errno);
return -1;
}
got = osRead(id->h, pBuf, cnt);
@@ -28657,7 +32280,7 @@ static int unixRead(
#endif
#if SQLITE_MAX_MMAP_SIZE>0
- /* Deal with as much of this read request as possible by transferring
+ /* Deal with as much of this read request as possible by transfering
** data from the memory mapping using memcpy(). */
if( offset<pFile->mmapSize ){
if( offset+amt <= pFile->mmapSize ){
@@ -28705,6 +32328,7 @@ static int seekAndWriteFd(
assert( nBuf==(nBuf&0x1ffff) );
assert( fd>2 );
+ assert( piErrno!=0 );
nBuf &= 0x1ffff;
TIMER_START;
@@ -28715,11 +32339,10 @@ static int seekAndWriteFd(
#else
do{
i64 iSeek = lseek(fd, iOff, SEEK_SET);
- SimulateIOError( iSeek-- );
-
- if( iSeek!=iOff ){
- if( piErrno ) *piErrno = (iSeek==-1 ? errno : 0);
- return -1;
+ SimulateIOError( iSeek = -1 );
+ if( iSeek<0 ){
+ rc = -1;
+ break;
}
rc = osWrite(fd, pBuf, nBuf);
}while( rc<0 && errno==EINTR );
@@ -28728,7 +32351,7 @@ static int seekAndWriteFd(
TIMER_END;
OSTRACE(("WRITE %-3d %5d %7lld %llu\n", fd, rc, iOff, TIMER_ELAPSED));
- if( rc<0 && piErrno ) *piErrno = errno;
+ if( rc<0 ) *piErrno = errno;
return rc;
}
@@ -28791,8 +32414,8 @@ static int unixWrite(
}
#endif
-#if SQLITE_MAX_MMAP_SIZE>0
- /* Deal with as much of this write request as possible by transferring
+#if defined(SQLITE_MMAP_READWRITE) && SQLITE_MAX_MMAP_SIZE>0
+ /* Deal with as much of this write request as possible by transfering
** data from the memory mapping using memcpy(). */
if( offset<pFile->mmapSize ){
if( offset+amt <= pFile->mmapSize ){
@@ -28807,8 +32430,8 @@ static int unixWrite(
}
}
#endif
-
- while( amt>0 && (wrote = seekAndWrite(pFile, offset, pBuf, amt))>0 ){
+
+ while( (wrote = seekAndWrite(pFile, offset, pBuf, amt))<amt && wrote>0 ){
amt -= wrote;
offset += wrote;
pBuf = &((char*)pBuf)[wrote];
@@ -28816,7 +32439,7 @@ static int unixWrite(
SimulateIOError(( wrote=(-1), amt=1 ));
SimulateDiskfullError(( wrote=0, amt=1 ));
- if( amt>0 ){
+ if( amt>wrote ){
if( wrote<0 && pFile->lastErrno!=ENOSPC ){
/* lastErrno set by seekAndWrite */
return SQLITE_IOERR_WRITE;
@@ -28912,10 +32535,15 @@ static int full_fsync(int fd, int fullSync, int dataOnly){
#endif
/* If we compiled with the SQLITE_NO_SYNC flag, then syncing is a
- ** no-op
+ ** no-op. But go ahead and call fstat() to validate the file
+ ** descriptor as we need a method to provoke a failure during
+ ** coverate testing.
*/
#ifdef SQLITE_NO_SYNC
- rc = SQLITE_OK;
+ {
+ struct stat buf;
+ rc = osFstat(fd, &buf);
+ }
#elif HAVE_FULLFSYNC
if( fullSync ){
rc = osFcntl(fd, F_FULLFSYNC, 0);
@@ -28981,16 +32609,20 @@ static int openDirectory(const char *zFilename, int *pFd){
char zDirname[MAX_PATHNAME+1];
sqlite3_snprintf(MAX_PATHNAME, zDirname, "%s", zFilename);
- for(ii=(int)strlen(zDirname); ii>1 && zDirname[ii]!='/'; ii--);
+ for(ii=(int)strlen(zDirname); ii>0 && zDirname[ii]!='/'; ii--);
if( ii>0 ){
zDirname[ii] = '\0';
- fd = robust_open(zDirname, O_RDONLY|O_BINARY, 0);
- if( fd>=0 ){
- OSTRACE(("OPENDIR %-3d %s\n", fd, zDirname));
- }
+ }else{
+ if( zDirname[0]!='/' ) zDirname[0] = '.';
+ zDirname[1] = 0;
+ }
+ fd = robust_open(zDirname, O_RDONLY|O_BINARY, 0);
+ if( fd>=0 ){
+ OSTRACE(("OPENDIR %-3d %s\n", fd, zDirname));
}
*pFd = fd;
- return (fd>=0?SQLITE_OK:unixLogError(SQLITE_CANTOPEN_BKPT, "open", zDirname));
+ if( fd>=0 ) return SQLITE_OK;
+ return unixLogError(SQLITE_CANTOPEN_BKPT, "openDirectory", zDirname);
}
/*
@@ -29043,10 +32675,11 @@ static int unixSync(sqlite3_file *id, int flags){
OSTRACE(("DIRSYNC %s (have_fullfsync=%d fullsync=%d)\n", pFile->zPath,
HAVE_FULLFSYNC, isFullsync));
rc = osOpenDirectory(pFile->zPath, &dirfd);
- if( rc==SQLITE_OK && dirfd>=0 ){
+ if( rc==SQLITE_OK ){
full_fsync(dirfd, 0, 0);
robust_close(pFile, dirfd, __LINE__);
- }else if( rc==SQLITE_CANTOPEN ){
+ }else{
+ assert( rc==SQLITE_CANTOPEN );
rc = SQLITE_OK;
}
pFile->ctrlFlags &= ~UNIXFILE_DIRSYNC;
@@ -29178,18 +32811,14 @@ static int fcntlSizeHint(unixFile *pFile, i64 nByte){
int nWrite = 0; /* Number of bytes written by seekAndWrite */
i64 iWrite; /* Next offset to write to */
- iWrite = ((buf.st_size + 2*nBlk - 1)/nBlk)*nBlk-1;
+ iWrite = (buf.st_size/nBlk)*nBlk + nBlk - 1;
assert( iWrite>=buf.st_size );
- assert( (iWrite/nBlk)==((buf.st_size+nBlk-1)/nBlk) );
assert( ((iWrite+1)%nBlk)==0 );
- for(/*no-op*/; iWrite<nSize; iWrite+=nBlk ){
+ for(/*no-op*/; iWrite<nSize+nBlk-1; iWrite+=nBlk ){
+ if( iWrite>=nSize ) iWrite = nSize - 1;
nWrite = seekAndWrite(pFile, iWrite, "", 1);
if( nWrite!=1 ) return SQLITE_IOERR_WRITE;
}
- if( nWrite==0 || (nSize%nBlk) ){
- nWrite = seekAndWrite(pFile, nSize-1, "", 1);
- if( nWrite!=1 ) return SQLITE_IOERR_WRITE;
- }
#endif
}
}
@@ -29237,10 +32866,6 @@ 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;
@@ -29567,10 +33192,9 @@ static int unixShmSystemLock(
assert( n==1 || lockType!=F_RDLCK );
/* Locks are within range */
- assert( n>=1 && n<SQLITE_SHM_NLOCK );
+ 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;
@@ -29578,10 +33202,8 @@ static int unixShmSystemLock(
f.l_start = ofst;
f.l_len = n;
- lkType = (pFile->ctrlFlags & UNIXFILE_BLOCK)!=0 ? F_SETLKW : F_SETLK;
- rc = osFcntl(pShmNode->h, lkType, &f);
+ rc = osFcntl(pShmNode->h, F_SETLK, &f);
rc = (rc!=(-1)) ? SQLITE_OK : SQLITE_BUSY;
- pFile->ctrlFlags &= ~UNIXFILE_BLOCK;
}
/* Update the global lock state and do debug tracing */
@@ -29648,7 +33270,7 @@ static int unixShmRegionPerMap(void){
static void unixShmPurge(unixFile *pFd){
unixShmNode *p = pFd->pInode->pShmNode;
assert( unixMutexHeld() );
- if( p && p->nRef==0 ){
+ if( p && ALWAYS(p->nRef==0) ){
int nShmPerMap = unixShmRegionPerMap();
int i;
assert( p->pInode==pFd->pInode );
@@ -29715,7 +33337,7 @@ static int unixOpenSharedMemory(unixFile *pDbFd){
/* Allocate space for the new unixShm object. */
p = sqlite3_malloc64( sizeof(*p) );
- if( p==0 ) return SQLITE_NOMEM;
+ if( p==0 ) return SQLITE_NOMEM_BKPT;
memset(p, 0, sizeof(*p));
assert( pDbFd->pShm==0 );
@@ -29735,7 +33357,7 @@ static int unixOpenSharedMemory(unixFile *pDbFd){
** a new *-shm file is created, an attempt will be made to create it
** with the same permissions.
*/
- if( osFstat(pDbFd->h, &sStat) && pInode->bProcessLock==0 ){
+ if( osFstat(pDbFd->h, &sStat) ){
rc = SQLITE_IOERR_FSTAT;
goto shm_open_err;
}
@@ -29747,7 +33369,7 @@ static int unixOpenSharedMemory(unixFile *pDbFd){
#endif
pShmNode = sqlite3_malloc64( sizeof(*pShmNode) + nShmFilename );
if( pShmNode==0 ){
- rc = SQLITE_NOMEM;
+ rc = SQLITE_NOMEM_BKPT;
goto shm_open_err;
}
memset(pShmNode, 0, sizeof(*pShmNode)+nShmFilename);
@@ -29763,10 +33385,12 @@ static int unixOpenSharedMemory(unixFile *pDbFd){
pShmNode->h = -1;
pDbFd->pInode->pShmNode = pShmNode;
pShmNode->pInode = pDbFd->pInode;
- pShmNode->mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST);
- if( pShmNode->mutex==0 ){
- rc = SQLITE_NOMEM;
- goto shm_open_err;
+ if( sqlite3GlobalConfig.bCoreMutex ){
+ pShmNode->mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST);
+ if( pShmNode->mutex==0 ){
+ rc = SQLITE_NOMEM_BKPT;
+ goto shm_open_err;
+ }
}
if( pInode->bProcessLock==0 ){
@@ -29785,7 +33409,7 @@ static int unixOpenSharedMemory(unixFile *pDbFd){
** is owned by the same user that owns the original database. Otherwise,
** the original owner will not be able to connect.
*/
- osFchown(pShmNode->h, sStat.st_uid, sStat.st_gid);
+ robustFchown(pShmNode->h, sStat.st_uid, sStat.st_gid);
/* Check to see if another process is holding the dead-man switch.
** If not, truncate the file to zero length.
@@ -29922,7 +33546,8 @@ static int unixShmMap(
/* Write to the last byte of each newly allocated or extended page */
assert( (nByte % pgsz)==0 );
for(iPg=(sStat.st_size/pgsz); iPg<(nByte/pgsz); iPg++){
- if( seekAndWriteFd(pShmNode->h, iPg*pgsz + pgsz-1, "", 1, 0)!=1 ){
+ int x = 0;
+ if( seekAndWriteFd(pShmNode->h, iPg*pgsz + pgsz-1, "", 1, &x)!=1 ){
const char *zFile = pShmNode->zFilename;
rc = unixLogError(SQLITE_IOERR_SHMSIZE, "write", zFile);
goto shmpage_out;
@@ -29937,7 +33562,7 @@ static int unixShmMap(
pShmNode->apRegion, nReqRegion*sizeof(char *)
);
if( !apNew ){
- rc = SQLITE_IOERR_NOMEM;
+ rc = SQLITE_IOERR_NOMEM_BKPT;
goto shmpage_out;
}
pShmNode->apRegion = apNew;
@@ -29957,7 +33582,7 @@ static int unixShmMap(
}else{
pMem = sqlite3_malloc64(szRegion);
if( pMem==0 ){
- rc = SQLITE_NOMEM;
+ rc = SQLITE_NOMEM_BKPT;
goto shmpage_out;
}
memset(pMem, 0, szRegion);
@@ -30105,7 +33730,8 @@ static void unixShmBarrier(
sqlite3_file *fd /* Database file holding the shared memory */
){
UNUSED_PARAMETER(fd);
- unixEnterMutex();
+ sqlite3MemoryBarrier(); /* compiler-defined memory barrier */
+ unixEnterMutex(); /* Also mutex, for redundancy */
unixLeaveMutex();
}
@@ -30215,7 +33841,9 @@ static void unixRemapfile(
assert( pFd->mmapSizeActual>=pFd->mmapSize );
assert( MAP_FAILED!=0 );
+#ifdef SQLITE_MMAP_READWRITE
if( (pFd->ctrlFlags & UNIXFILE_RDONLY)==0 ) flags |= PROT_WRITE;
+#endif
if( pOrig ){
#if HAVE_MREMAP
@@ -30287,17 +33915,14 @@ static void unixRemapfile(
** recreated as a result of outstanding references) or an SQLite error
** code otherwise.
*/
-static int unixMapfile(unixFile *pFd, i64 nByte){
- i64 nMap = nByte;
- int rc;
-
+static int unixMapfile(unixFile *pFd, i64 nMap){
assert( nMap>=0 || pFd->nFetchOut==0 );
+ assert( nMap>0 || (pFd->mmapSize==0 && pFd->pMapRegion==0) );
if( pFd->nFetchOut>0 ) return SQLITE_OK;
if( nMap<0 ){
struct stat statbuf; /* Low-level file information */
- rc = osFstat(pFd->h, &statbuf);
- if( rc!=SQLITE_OK ){
+ if( osFstat(pFd->h, &statbuf) ){
return SQLITE_IOERR_FSTAT;
}
nMap = statbuf.st_size;
@@ -30306,12 +33931,9 @@ static int unixMapfile(unixFile *pFd, i64 nByte){
nMap = pFd->mmapSizeMax;
}
+ assert( nMap>0 || (pFd->mmapSize==0 && pFd->pMapRegion==0) );
if( nMap!=pFd->mmapSize ){
- if( nMap>0 ){
- unixRemapfile(pFd, nMap);
- }else{
- unixUnmapfile(pFd);
- }
+ unixRemapfile(pFd, nMap);
}
return SQLITE_OK;
@@ -30738,7 +34360,7 @@ static int fillInUnixFile(
pNew->pId = vxworksFindFileId(zFilename);
if( pNew->pId==0 ){
ctrlFlags |= UNIXFILE_NOLOCK;
- rc = SQLITE_NOMEM;
+ rc = SQLITE_NOMEM_BKPT;
}
#endif
@@ -30794,7 +34416,7 @@ static int fillInUnixFile(
afpLockingContext *pCtx;
pNew->lockingContext = pCtx = sqlite3_malloc64( sizeof(*pCtx) );
if( pCtx==0 ){
- rc = SQLITE_NOMEM;
+ rc = SQLITE_NOMEM_BKPT;
}else{
/* NB: zFilename exists and remains valid until the file is closed
** according to requirement F11141. So we do not need to make a
@@ -30824,7 +34446,7 @@ static int fillInUnixFile(
nFilename = (int)strlen(zFilename) + 6;
zLockFile = (char *)sqlite3_malloc64(nFilename);
if( zLockFile==0 ){
- rc = SQLITE_NOMEM;
+ rc = SQLITE_NOMEM_BKPT;
}else{
sqlite3_snprintf(nFilename, zLockFile, "%s" DOTLOCK_SUFFIX, zFilename);
}
@@ -30847,7 +34469,7 @@ static int fillInUnixFile(
if( zSemName[n]=='/' ) zSemName[n] = '_';
pNew->pInode->pSem = sem_open(zSemName, O_CREAT, 0666, 1);
if( pNew->pInode->pSem == SEM_FAILED ){
- rc = SQLITE_NOMEM;
+ rc = SQLITE_NOMEM_BKPT;
pNew->pInode->aSemName[0] = '\0';
}
}
@@ -30882,27 +34504,29 @@ static const char *unixTempFileDir(void){
static const char *azDirs[] = {
0,
0,
- 0,
"/var/tmp",
"/usr/tmp",
"/tmp",
- 0 /* List terminator */
+ "."
};
- unsigned int i;
+ unsigned int i = 0;
struct stat buf;
- const char *zDir = 0;
-
- azDirs[0] = sqlite3_temp_directory;
- if( !azDirs[1] ) azDirs[1] = getenv("SQLITE_TMPDIR");
- if( !azDirs[2] ) azDirs[2] = getenv("TMPDIR");
- for(i=0; i<sizeof(azDirs)/sizeof(azDirs[0]); zDir=azDirs[i++]){
- if( zDir==0 ) continue;
- if( osStat(zDir, &buf) ) continue;
- if( !S_ISDIR(buf.st_mode) ) continue;
- if( osAccess(zDir, 07) ) continue;
- break;
+ const char *zDir = sqlite3_temp_directory;
+
+ if( !azDirs[0] ) azDirs[0] = getenv("SQLITE_TMPDIR");
+ if( !azDirs[1] ) azDirs[1] = getenv("TMPDIR");
+ while(1){
+ if( zDir!=0
+ && osStat(zDir, &buf)==0
+ && S_ISDIR(buf.st_mode)
+ && osAccess(zDir, 03)==0
+ ){
+ return zDir;
+ }
+ if( i>=sizeof(azDirs)/sizeof(azDirs[0]) ) break;
+ zDir = azDirs[i++];
}
- return zDir;
+ return 0;
}
/*
@@ -30911,38 +34535,26 @@ static const char *unixTempFileDir(void){
** pVfs->mxPathname bytes.
*/
static int unixGetTempname(int nBuf, char *zBuf){
- static const unsigned char zChars[] =
- "abcdefghijklmnopqrstuvwxyz"
- "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
- "0123456789";
- unsigned int i, j;
const char *zDir;
+ int iLimit = 0;
/* It's odd to simulate an io-error here, but really this is just
** using the io-error infrastructure to test that SQLite handles this
** function failing.
*/
+ zBuf[0] = 0;
SimulateIOError( return SQLITE_IOERR );
zDir = unixTempFileDir();
- if( zDir==0 ) zDir = ".";
-
- /* Check that the output buffer is large enough for the temporary file
- ** name. If it is not, return SQLITE_ERROR.
- */
- if( (strlen(zDir) + strlen(SQLITE_TEMP_FILE_PREFIX) + 18) >= (size_t)nBuf ){
- return SQLITE_ERROR;
- }
-
+ if( zDir==0 ) return SQLITE_IOERR_GETTEMPPATH;
do{
- sqlite3_snprintf(nBuf-18, zBuf, "%s/"SQLITE_TEMP_FILE_PREFIX, zDir);
- j = (int)strlen(zBuf);
- sqlite3_randomness(15, &zBuf[j]);
- for(i=0; i<15; i++, j++){
- zBuf[j] = (char)zChars[ ((unsigned char)zBuf[j])%(sizeof(zChars)-1) ];
- }
- zBuf[j] = 0;
- zBuf[j+1] = 0;
+ u64 r;
+ sqlite3_randomness(sizeof(r), &r);
+ assert( nBuf>2 );
+ zBuf[nBuf-2] = 0;
+ sqlite3_snprintf(nBuf, zBuf, "%s/"SQLITE_TEMP_FILE_PREFIX"%llx%c",
+ zDir, r, 0);
+ if( zBuf[nBuf-2]!=0 || (iLimit++)>10 ) return SQLITE_ERROR;
}while( osAccess(zBuf,0)==0 );
return SQLITE_OK;
}
@@ -31064,16 +34676,19 @@ static int findCreateFileMode(
** used by the test_multiplex.c module.
*/
nDb = sqlite3Strlen30(zPath) - 1;
-#ifdef SQLITE_ENABLE_8_3_NAMES
- while( nDb>0 && sqlite3Isalnum(zPath[nDb]) ) nDb--;
- if( nDb==0 || zPath[nDb]!='-' ) return SQLITE_OK;
-#else
while( zPath[nDb]!='-' ){
+#ifndef SQLITE_ENABLE_8_3_NAMES
+ /* In the normal case (8+3 filenames disabled) the journal filename
+ ** is guaranteed to contain a '-' character. */
assert( nDb>0 );
- assert( zPath[nDb]!='\n' );
+ assert( sqlite3Isalnum(zPath[nDb]) );
+#else
+ /* If 8+3 names are possible, then the journal file might not contain
+ ** a '-' character. So check for that case and return early. */
+ if( nDb==0 || zPath[nDb]=='.' ) return SQLITE_OK;
+#endif
nDb--;
}
-#endif
memcpy(zDb, zPath, nDb);
zDb[nDb] = '\0';
@@ -31201,7 +34816,7 @@ static int unixOpen(
}else{
pUnused = sqlite3_malloc64(sizeof(*pUnused));
if( !pUnused ){
- return SQLITE_NOMEM;
+ return SQLITE_NOMEM_BKPT;
}
}
p->pUnused = pUnused;
@@ -31214,7 +34829,7 @@ static int unixOpen(
}else if( !zName ){
/* If zName is NULL, the upper layer is requesting a temp file. */
assert(isDelete && !syncDir);
- rc = unixGetTempname(MAX_PATHNAME+2, zTmpname);
+ rc = unixGetTempname(pVfs->mxPathname, zTmpname);
if( rc!=SQLITE_OK ){
return rc;
}
@@ -31247,7 +34862,8 @@ static int unixOpen(
}
fd = robust_open(zName, openFlags, openMode);
OSTRACE(("OPENX %-3d %s 0%o\n", fd, zName, openFlags));
- if( fd<0 && errno!=EISDIR && isReadWrite && !isExclusive ){
+ assert( !isExclusive || (openFlags & O_CREAT)!=0 );
+ if( fd<0 && errno!=EISDIR && isReadWrite ){
/* Failed to open the file for read/write access. Try read-only. */
flags &= ~(SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE);
openFlags &= ~(O_RDWR|O_CREAT);
@@ -31266,7 +34882,7 @@ static int unixOpen(
** the same as the original database.
*/
if( flags & (SQLITE_OPEN_WAL|SQLITE_OPEN_MAIN_JOURNAL) ){
- osFchown(fd, uid, gid);
+ robustFchown(fd, uid, gid);
}
}
assert( fd>=0 );
@@ -31286,7 +34902,7 @@ static int unixOpen(
zPath = sqlite3_mprintf("%s", zName);
if( zPath==0 ){
robust_close(p, fd, __LINE__);
- return SQLITE_NOMEM;
+ return SQLITE_NOMEM_BKPT;
}
#else
osUnlink(zName);
@@ -31297,9 +34913,6 @@ static int unixOpen(
p->openFlags = openFlags;
}
#endif
-
- noLock = eType!=SQLITE_OPEN_MAIN_DB;
-
#if defined(__APPLE__) || SQLITE_ENABLE_LOCKING_STYLE
if( fstatfs(fd, &fsInfo) == -1 ){
@@ -31318,6 +34931,7 @@ static int unixOpen(
/* Set up appropriate ctrlFlags */
if( isDelete ) ctrlFlags |= UNIXFILE_DELETE;
if( isReadonly ) ctrlFlags |= UNIXFILE_RDONLY;
+ noLock = eType!=SQLITE_OPEN_MAIN_DB;
if( noLock ) ctrlFlags |= UNIXFILE_NOLOCK;
if( syncDir ) ctrlFlags |= UNIXFILE_DIRSYNC;
if( flags & SQLITE_OPEN_URI ) ctrlFlags |= UNIXFILE_URI;
@@ -31394,16 +35008,12 @@ static int unixDelete(
int fd;
rc = osOpenDirectory(zPath, &fd);
if( rc==SQLITE_OK ){
-#if OS_VXWORKS
- if( fsync(fd)==-1 )
-#else
- if( fsync(fd) )
-#endif
- {
+ if( full_fsync(fd,0,0) ){
rc = unixLogError(SQLITE_IOERR_DIR_FSYNC, "fsync", zPath);
}
robust_close(0, fd, __LINE__);
- }else if( rc==SQLITE_CANTOPEN ){
+ }else{
+ assert( rc==SQLITE_CANTOPEN );
rc = SQLITE_OK;
}
}
@@ -31427,33 +35037,49 @@ static int unixAccess(
int flags, /* What do we want to learn about the zPath file? */
int *pResOut /* Write result boolean here */
){
- int amode = 0;
UNUSED_PARAMETER(NotUsed);
SimulateIOError( return SQLITE_IOERR_ACCESS; );
- switch( flags ){
- case SQLITE_ACCESS_EXISTS:
- amode = F_OK;
- break;
- case SQLITE_ACCESS_READWRITE:
- amode = W_OK|R_OK;
- break;
- case SQLITE_ACCESS_READ:
- amode = R_OK;
- break;
+ assert( pResOut!=0 );
- default:
- assert(!"Invalid flags argument");
- }
- *pResOut = (osAccess(zPath, amode)==0);
- if( flags==SQLITE_ACCESS_EXISTS && *pResOut ){
+ /* The spec says there are three possible values for flags. But only
+ ** two of them are actually used */
+ assert( flags==SQLITE_ACCESS_EXISTS || flags==SQLITE_ACCESS_READWRITE );
+
+ if( flags==SQLITE_ACCESS_EXISTS ){
struct stat buf;
- if( 0==osStat(zPath, &buf) && buf.st_size==0 ){
- *pResOut = 0;
- }
+ *pResOut = (0==osStat(zPath, &buf) && buf.st_size>0);
+ }else{
+ *pResOut = osAccess(zPath, W_OK|R_OK)==0;
}
return SQLITE_OK;
}
+/*
+**
+*/
+static int mkFullPathname(
+ const char *zPath, /* Input path */
+ char *zOut, /* Output buffer */
+ int nOut /* Allocated size of buffer zOut */
+){
+ int nPath = sqlite3Strlen30(zPath);
+ int iOff = 0;
+ if( zPath[0]!='/' ){
+ if( osGetcwd(zOut, nOut-2)==0 ){
+ return unixLogError(SQLITE_CANTOPEN_BKPT, "getcwd", zPath);
+ }
+ iOff = sqlite3Strlen30(zOut);
+ zOut[iOff++] = '/';
+ }
+ if( (iOff+nPath+1)>nOut ){
+ /* SQLite assumes that xFullPathname() nul-terminates the output buffer
+ ** even if it returns an error. */
+ zOut[iOff] = '\0';
+ return SQLITE_CANTOPEN_BKPT;
+ }
+ sqlite3_snprintf(nOut-iOff, &zOut[iOff], "%s", zPath);
+ return SQLITE_OK;
+}
/*
** Turn a relative pathname into a full pathname. The relative path
@@ -31470,6 +35096,17 @@ static int unixFullPathname(
int nOut, /* Size of output buffer in bytes */
char *zOut /* Output buffer */
){
+#if !defined(HAVE_READLINK) || !defined(HAVE_LSTAT)
+ return mkFullPathname(zPath, zOut, nOut);
+#else
+ int rc = SQLITE_OK;
+ int nByte;
+ int nLink = 1; /* Number of symbolic links followed so far */
+ const char *zIn = zPath; /* Input path for each iteration of loop */
+ char *zDel = 0;
+
+ assert( pVfs->mxPathname==MAX_PATHNAME );
+ UNUSED_PARAMETER(pVfs);
/* It's odd to simulate an io-error here, but really this is just
** using the io-error infrastructure to test that SQLite handles this
@@ -31478,21 +35115,62 @@ static int unixFullPathname(
*/
SimulateIOError( return SQLITE_ERROR );
- assert( pVfs->mxPathname==MAX_PATHNAME );
- UNUSED_PARAMETER(pVfs);
+ do {
- zOut[nOut-1] = '\0';
- if( zPath[0]=='/' ){
- sqlite3_snprintf(nOut, zOut, "%s", zPath);
- }else{
- int nCwd;
- if( osGetcwd(zOut, nOut-1)==0 ){
- return unixLogError(SQLITE_CANTOPEN_BKPT, "getcwd", zPath);
+ /* Call stat() on path zIn. Set bLink to true if the path is a symbolic
+ ** link, or false otherwise. */
+ int bLink = 0;
+ struct stat buf;
+ if( osLstat(zIn, &buf)!=0 ){
+ if( errno!=ENOENT ){
+ rc = unixLogError(SQLITE_CANTOPEN_BKPT, "lstat", zIn);
+ }
+ }else{
+ bLink = S_ISLNK(buf.st_mode);
}
- nCwd = (int)strlen(zOut);
- sqlite3_snprintf(nOut-nCwd, &zOut[nCwd], "/%s", zPath);
- }
- return SQLITE_OK;
+
+ if( bLink ){
+ if( zDel==0 ){
+ zDel = sqlite3_malloc(nOut);
+ if( zDel==0 ) rc = SQLITE_NOMEM_BKPT;
+ }else if( ++nLink>SQLITE_MAX_SYMLINKS ){
+ rc = SQLITE_CANTOPEN_BKPT;
+ }
+
+ if( rc==SQLITE_OK ){
+ nByte = osReadlink(zIn, zDel, nOut-1);
+ if( nByte<0 ){
+ rc = unixLogError(SQLITE_CANTOPEN_BKPT, "readlink", zIn);
+ }else{
+ if( zDel[0]!='/' ){
+ int n;
+ for(n = sqlite3Strlen30(zIn); n>0 && zIn[n-1]!='/'; n--);
+ if( nByte+n+1>nOut ){
+ rc = SQLITE_CANTOPEN_BKPT;
+ }else{
+ memmove(&zDel[n], zDel, nByte+1);
+ memcpy(zDel, zIn, n);
+ nByte += n;
+ }
+ }
+ zDel[nByte] = '\0';
+ }
+ }
+
+ zIn = zDel;
+ }
+
+ assert( rc!=SQLITE_OK || zIn!=zOut || zIn[0]=='/' );
+ if( rc==SQLITE_OK && zIn!=zOut ){
+ rc = mkFullPathname(zIn, zOut, nOut);
+ }
+ if( bLink==0 ) break;
+ zIn = zOut;
+ }while( rc==SQLITE_OK );
+
+ sqlite3_free(zDel);
+ return rc;
+#endif /* HAVE_READLINK && HAVE_LSTAT */
}
@@ -31661,11 +35339,8 @@ static int unixCurrentTimeInt64(sqlite3_vfs *NotUsed, sqlite3_int64 *piNow){
*piNow = unixEpoch + 1000*(sqlite3_int64)sNow.tv_sec + sNow.tv_nsec/1000000;
#else
struct timeval sNow;
- if( gettimeofday(&sNow, 0)==0 ){
- *piNow = unixEpoch + 1000*(sqlite3_int64)sNow.tv_sec + sNow.tv_usec/1000;
- }else{
- rc = SQLITE_ERROR;
- }
+ (void)gettimeofday(&sNow, 0); /* Cannot fail given valid arguments */
+ *piNow = unixEpoch + 1000*(sqlite3_int64)sNow.tv_sec + sNow.tv_usec/1000;
#endif
#ifdef SQLITE_TEST
@@ -31677,6 +35352,7 @@ static int unixCurrentTimeInt64(sqlite3_vfs *NotUsed, sqlite3_int64 *piNow){
return rc;
}
+#ifndef SQLITE_OMIT_DEPRECATED
/*
** Find the current time (in Universal Coordinated Time). Write the
** current time and date as a Julian Day number into *prNow and
@@ -31690,19 +35366,21 @@ static int unixCurrentTime(sqlite3_vfs *NotUsed, double *prNow){
*prNow = i/86400000.0;
return rc;
}
+#else
+# define unixCurrentTime 0
+#endif
/*
-** We added the xGetLastError() method with the intention of providing
-** better low-level error messages when operating-system problems come up
-** during SQLite operation. But so far, none of that has been implemented
-** in the core. So this routine is never called. For now, it is merely
-** a place-holder.
+** The xGetLastError() method is designed to return a better
+** low-level error message when operating-system problems come up
+** during SQLite operation. Only the integer return code is currently
+** used.
*/
static int unixGetLastError(sqlite3_vfs *NotUsed, int NotUsed2, char *NotUsed3){
UNUSED_PARAMETER(NotUsed);
UNUSED_PARAMETER(NotUsed2);
UNUSED_PARAMETER(NotUsed3);
- return 0;
+ return errno;
}
@@ -31957,7 +35635,7 @@ static int proxyCreateLockPath(const char *lockPath){
}
buf[i] = lockPath[i];
}
- OSTRACE(("CREATELOCKPATH proxy lock path=%s pid=%d\n", lockPath, osGetpid(0)));
+ OSTRACE(("CREATELOCKPATH proxy lock path=%s pid=%d\n",lockPath,osGetpid(0)));
return 0;
}
@@ -31993,7 +35671,7 @@ static int proxyCreateUnixFile(
}else{
pUnused = sqlite3_malloc64(sizeof(*pUnused));
if( !pUnused ){
- return SQLITE_NOMEM;
+ return SQLITE_NOMEM_BKPT;
}
}
if( fd<0 ){
@@ -32026,7 +35704,7 @@ static int proxyCreateUnixFile(
pNew = (unixFile *)sqlite3_malloc64(sizeof(*pNew));
if( pNew==NULL ){
- rc = SQLITE_NOMEM;
+ rc = SQLITE_NOMEM_BKPT;
goto end_create_proxy;
}
memset(pNew, 0, sizeof(unixFile));
@@ -32369,7 +36047,7 @@ static int proxyTakeConch(unixFile *pFile){
writeSize = PROXY_PATHINDEX + strlen(&writeBuffer[PROXY_PATHINDEX]);
robust_ftruncate(conchFile->h, writeSize);
rc = unixWrite((sqlite3_file *)conchFile, writeBuffer, writeSize, 0);
- fsync(conchFile->h);
+ full_fsync(conchFile->h,0,0);
/* If we created a new conch file (not just updated the contents of a
** valid conch file), try to match the permissions of the database
*/
@@ -32439,7 +36117,7 @@ static int proxyTakeConch(unixFile *pFile){
if( tempLockPath ){
pCtx->lockProxyPath = sqlite3DbStrDup(0, tempLockPath);
if( !pCtx->lockProxyPath ){
- rc = SQLITE_NOMEM;
+ rc = SQLITE_NOMEM_BKPT;
}
}
}
@@ -32504,7 +36182,7 @@ static int proxyCreateConchPathname(char *dbPath, char **pConchPath){
** the name of the original database file. */
*pConchPath = conchPath = (char *)sqlite3_malloc64(len + 8);
if( conchPath==0 ){
- return SQLITE_NOMEM;
+ return SQLITE_NOMEM_BKPT;
}
memcpy(conchPath, dbPath, len+1);
@@ -32620,7 +36298,7 @@ static int proxyTransformUnixFile(unixFile *pFile, const char *path) {
pCtx = sqlite3_malloc64( sizeof(*pCtx) );
if( pCtx==0 ){
- return SQLITE_NOMEM;
+ return SQLITE_NOMEM_BKPT;
}
memset(pCtx, 0, sizeof(*pCtx));
@@ -32656,7 +36334,7 @@ static int proxyTransformUnixFile(unixFile *pFile, const char *path) {
if( rc==SQLITE_OK ){
pCtx->dbPath = sqlite3DbStrDup(0, dbPath);
if( pCtx->dbPath==NULL ){
- rc = SQLITE_NOMEM;
+ rc = SQLITE_NOMEM_BKPT;
}
}
if( rc==SQLITE_OK ){
@@ -32842,7 +36520,7 @@ static int proxyUnlock(sqlite3_file *id, int eFileLock) {
** Close a file that uses proxy locks.
*/
static int proxyClose(sqlite3_file *id) {
- if( id ){
+ if( ALWAYS(id) ){
unixFile *pFile = (unixFile*)id;
proxyLockingContext *pCtx = (proxyLockingContext *)pFile->lockingContext;
unixFile *lockProxy = pCtx->lockProxy;
@@ -32986,7 +36664,7 @@ SQLITE_API int SQLITE_STDCALL sqlite3_os_init(void){
/* Double-check that the aSyscall[] array has been constructed
** correctly. See ticket [bb3a86e890c8e96ab] */
- assert( ArraySize(aSyscall)==25 );
+ assert( ArraySize(aSyscall)==28 );
/* Register all VFSes defined in the aVfs[] array */
for(i=0; i<(sizeof(aVfs)/sizeof(sqlite3_vfs)); i++){
@@ -33024,6 +36702,7 @@ SQLITE_API int SQLITE_STDCALL sqlite3_os_end(void){
**
** This file contains code that is specific to Windows.
*/
+/* #include "sqliteInt.h" */
#if SQLITE_OS_WIN /* This file is used for Windows only */
/*
@@ -33068,8 +36747,8 @@ SQLITE_API int SQLITE_STDCALL sqlite3_os_end(void){
*/
#ifdef SQLITE_PERFORMANCE_TRACE
-/*
-** hwtime.h contains inline assembler code for implementing
+/*
+** hwtime.h contains inline assembler code for implementing
** high-performance timing routines.
*/
/************** Include hwtime.h in the middle of os_common.h ****************/
@@ -33179,14 +36858,14 @@ static sqlite_uint64 g_elapsed;
** of code will give us the ability to simulate a disk I/O error. This
** is used for testing the I/O recovery logic.
*/
-#ifdef SQLITE_TEST
-SQLITE_API int sqlite3_io_error_hit = 0; /* Total number of I/O Errors */
-SQLITE_API int sqlite3_io_error_hardhit = 0; /* Number of non-benign errors */
-SQLITE_API int sqlite3_io_error_pending = 0; /* Count down to first I/O error */
-SQLITE_API int sqlite3_io_error_persist = 0; /* True if I/O errors persist */
-SQLITE_API int sqlite3_io_error_benign = 0; /* True if errors are benign */
-SQLITE_API int sqlite3_diskfull_pending = 0;
-SQLITE_API int sqlite3_diskfull = 0;
+#if defined(SQLITE_TEST)
+SQLITE_API extern int sqlite3_io_error_hit;
+SQLITE_API extern int sqlite3_io_error_hardhit;
+SQLITE_API extern int sqlite3_io_error_pending;
+SQLITE_API extern int sqlite3_io_error_persist;
+SQLITE_API extern int sqlite3_io_error_benign;
+SQLITE_API extern int sqlite3_diskfull_pending;
+SQLITE_API extern int sqlite3_diskfull;
#define SimulateIOErrorBenign(X) sqlite3_io_error_benign=(X)
#define SimulateIOError(CODE) \
if( (sqlite3_io_error_persist && sqlite3_io_error_hit) \
@@ -33212,17 +36891,17 @@ static void local_ioerr(){
#define SimulateIOErrorBenign(X)
#define SimulateIOError(A)
#define SimulateDiskfullError(A)
-#endif
+#endif /* defined(SQLITE_TEST) */
/*
** When testing, keep a count of the number of open files.
*/
-#ifdef SQLITE_TEST
-SQLITE_API int sqlite3_open_file_count = 0;
+#if defined(SQLITE_TEST)
+SQLITE_API extern int sqlite3_open_file_count;
#define OpenCounter(X) sqlite3_open_file_count+=(X)
#else
#define OpenCounter(X)
-#endif
+#endif /* defined(SQLITE_TEST) */
#endif /* !defined(_OS_COMMON_H_) */
@@ -33232,6 +36911,7 @@ SQLITE_API int sqlite3_open_file_count = 0;
/*
** Include the header file for the Windows VFS.
*/
+/* #include "os_win.h" */
/*
** Compiling and using WAL mode requires several APIs that are only
@@ -33284,6 +36964,10 @@ SQLITE_API int sqlite3_open_file_count = 0;
# define NTDDI_WINBLUE 0x06030000
#endif
+#ifndef NTDDI_WINTHRESHOLD
+# define NTDDI_WINTHRESHOLD 0x06040000
+#endif
+
/*
** Check to see if the GetVersionEx[AW] functions are deprecated on the
** target system. GetVersionEx was first deprecated in Win8.1.
@@ -33297,6 +36981,19 @@ SQLITE_API int sqlite3_open_file_count = 0;
#endif
/*
+** Check to see if the CreateFileMappingA function is supported on the
+** target system. It is unavailable when using "mincore.lib" on Win10.
+** When compiling for Windows 10, always assume "mincore.lib" is in use.
+*/
+#ifndef SQLITE_WIN32_CREATEFILEMAPPINGA
+# if defined(NTDDI_VERSION) && NTDDI_VERSION >= NTDDI_WINTHRESHOLD
+# define SQLITE_WIN32_CREATEFILEMAPPINGA 0
+# else
+# define SQLITE_WIN32_CREATEFILEMAPPINGA 1
+# endif
+#endif
+
+/*
** This constant should already be defined (in the "WinDef.h" SDK file).
*/
#ifndef MAX_PATH
@@ -33536,10 +37233,22 @@ struct winFile {
#endif
/*
+ * This is cache size used in the calculation of the initial size of the
+ * Win32-specific heap. It cannot be negative.
+ */
+#ifndef SQLITE_WIN32_CACHE_SIZE
+# if SQLITE_DEFAULT_CACHE_SIZE>=0
+# define SQLITE_WIN32_CACHE_SIZE (SQLITE_DEFAULT_CACHE_SIZE)
+# else
+# define SQLITE_WIN32_CACHE_SIZE (-(SQLITE_DEFAULT_CACHE_SIZE))
+# endif
+#endif
+
+/*
* The initial size of the Win32-specific heap. This value may be zero.
*/
#ifndef SQLITE_WIN32_HEAP_INIT_SIZE
-# define SQLITE_WIN32_HEAP_INIT_SIZE ((SQLITE_DEFAULT_CACHE_SIZE) * \
+# define SQLITE_WIN32_HEAP_INIT_SIZE ((SQLITE_WIN32_CACHE_SIZE) * \
(SQLITE_DEFAULT_PAGE_SIZE) + 4194304)
#endif
@@ -33702,8 +37411,9 @@ static struct win_syscall {
#define osCreateFileW ((HANDLE(WINAPI*)(LPCWSTR,DWORD,DWORD, \
LPSECURITY_ATTRIBUTES,DWORD,DWORD,HANDLE))aSyscall[5].pCurrent)
-#if (!SQLITE_OS_WINRT && defined(SQLITE_WIN32_HAS_ANSI) && \
- (!defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0))
+#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_HAS_ANSI) && \
+ (!defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0) && \
+ SQLITE_WIN32_CREATEFILEMAPPINGA
{ "CreateFileMappingA", (SYSCALL)CreateFileMappingA, 0 },
#else
{ "CreateFileMappingA", (SYSCALL)0, 0 },
@@ -33933,8 +37643,7 @@ static struct win_syscall {
#define osGetTickCount ((DWORD(WINAPI*)(VOID))aSyscall[33].pCurrent)
-#if defined(SQLITE_WIN32_HAS_ANSI) && defined(SQLITE_WIN32_GETVERSIONEX) && \
- SQLITE_WIN32_GETVERSIONEX
+#if defined(SQLITE_WIN32_HAS_ANSI) && SQLITE_WIN32_GETVERSIONEX
{ "GetVersionExA", (SYSCALL)GetVersionExA, 0 },
#else
{ "GetVersionExA", (SYSCALL)0, 0 },
@@ -33944,7 +37653,7 @@ static struct win_syscall {
LPOSVERSIONINFOA))aSyscall[34].pCurrent)
#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_HAS_WIDE) && \
- defined(SQLITE_WIN32_GETVERSIONEX) && SQLITE_WIN32_GETVERSIONEX
+ SQLITE_WIN32_GETVERSIONEX
{ "GetVersionExW", (SYSCALL)GetVersionExW, 0 },
#else
{ "GetVersionExW", (SYSCALL)0, 0 },
@@ -34413,7 +38122,7 @@ SQLITE_API int SQLITE_STDCALL sqlite3_win32_compact_heap(LPUINT pnLargest){
if( lastErrno==NO_ERROR ){
sqlite3_log(SQLITE_NOMEM, "failed to HeapCompact (no space), heap=%p",
(void*)hHeap);
- rc = SQLITE_NOMEM;
+ rc = SQLITE_NOMEM_BKPT;
}else{
sqlite3_log(SQLITE_ERROR, "failed to HeapCompact (%lu), heap=%p",
osGetLastError(), (void*)hHeap);
@@ -34439,8 +38148,8 @@ 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 */
- MUTEX_LOGIC( pMaster = sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER); )
- MUTEX_LOGIC( pMem = sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MEM); )
+ MUTEX_LOGIC( pMaster = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER); )
+ MUTEX_LOGIC( pMem = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MEM); )
sqlite3_mutex_enter(pMaster);
sqlite3_mutex_enter(pMem);
winMemAssertMagic();
@@ -34485,6 +38194,12 @@ SQLITE_API void SQLITE_STDCALL sqlite3_win32_write_debug(const char *zBuf, int n
int nMin = MIN(nBuf, (SQLITE_WIN32_DBG_BUF_SIZE - 1)); /* may be negative. */
if( nMin<-1 ) nMin = -1; /* all negative values become -1. */
assert( nMin==-1 || nMin==0 || nMin<SQLITE_WIN32_DBG_BUF_SIZE );
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( !zBuf ){
+ (void)SQLITE_MISUSE_BKPT;
+ return;
+ }
+#endif
#if defined(SQLITE_WIN32_HAS_ANSI)
if( nMin>0 ){
memset(zDbgBuf, 0, SQLITE_WIN32_DBG_BUF_SIZE);
@@ -34555,7 +38270,7 @@ SQLITE_PRIVATE DWORD sqlite3Win32Wait(HANDLE hObject){
** the LockFileEx() API.
*/
-#if !defined(SQLITE_WIN32_GETVERSIONEX) || !SQLITE_WIN32_GETVERSIONEX
+#if !SQLITE_WIN32_GETVERSIONEX
# define osIsNT() (1)
#elif SQLITE_OS_WINCE || SQLITE_OS_WINRT || !defined(SQLITE_WIN32_HAS_ANSI)
# define osIsNT() (1)
@@ -34576,7 +38291,7 @@ SQLITE_API int SQLITE_STDCALL sqlite3_win32_is_nt(void){
** kernel.
*/
return 1;
-#elif defined(SQLITE_WIN32_GETVERSIONEX) && SQLITE_WIN32_GETVERSIONEX
+#elif SQLITE_WIN32_GETVERSIONEX
if( osInterlockedCompareExchange(&sqlite3_os_type, 0, 0)==0 ){
#if defined(SQLITE_WIN32_HAS_ANSI)
OSVERSIONINFOA sInfo;
@@ -34733,7 +38448,7 @@ static int winMemInit(void *pAppData){
"failed to HeapCreate (%lu), flags=%u, initSize=%lu, maxSize=%lu",
osGetLastError(), SQLITE_WIN32_HEAP_FLAGS, dwInitialSize,
dwMaximumSize);
- return SQLITE_NOMEM;
+ return SQLITE_NOMEM_BKPT;
}
pWinMemData->bOwned = TRUE;
assert( pWinMemData->bOwned );
@@ -34743,7 +38458,7 @@ static int winMemInit(void *pAppData){
if( !pWinMemData->hHeap ){
sqlite3_log(SQLITE_NOMEM,
"failed to GetProcessHeap (%lu)", osGetLastError());
- return SQLITE_NOMEM;
+ return SQLITE_NOMEM_BKPT;
}
pWinMemData->bOwned = FALSE;
assert( !pWinMemData->bOwned );
@@ -34810,147 +38525,244 @@ SQLITE_PRIVATE void sqlite3MemSetDefault(void){
#endif /* SQLITE_WIN32_MALLOC */
/*
-** Convert a UTF-8 string to Microsoft Unicode (UTF-16?).
+** Convert a UTF-8 string to Microsoft Unicode.
**
-** Space to hold the returned string is obtained from malloc.
+** Space to hold the returned string is obtained from sqlite3_malloc().
*/
-static LPWSTR winUtf8ToUnicode(const char *zFilename){
+static LPWSTR winUtf8ToUnicode(const char *zText){
int nChar;
- LPWSTR zWideFilename;
+ LPWSTR zWideText;
- nChar = osMultiByteToWideChar(CP_UTF8, 0, zFilename, -1, NULL, 0);
+ nChar = osMultiByteToWideChar(CP_UTF8, 0, zText, -1, NULL, 0);
if( nChar==0 ){
return 0;
}
- zWideFilename = sqlite3MallocZero( nChar*sizeof(zWideFilename[0]) );
- if( zWideFilename==0 ){
+ zWideText = sqlite3MallocZero( nChar*sizeof(WCHAR) );
+ if( zWideText==0 ){
return 0;
}
- nChar = osMultiByteToWideChar(CP_UTF8, 0, zFilename, -1, zWideFilename,
+ nChar = osMultiByteToWideChar(CP_UTF8, 0, zText, -1, zWideText,
nChar);
if( nChar==0 ){
- sqlite3_free(zWideFilename);
- zWideFilename = 0;
+ sqlite3_free(zWideText);
+ zWideText = 0;
}
- return zWideFilename;
+ return zWideText;
}
/*
-** Convert Microsoft Unicode to UTF-8. Space to hold the returned string is
-** obtained from sqlite3_malloc().
+** Convert a Microsoft Unicode string to UTF-8.
+**
+** Space to hold the returned string is obtained from sqlite3_malloc().
*/
-static char *winUnicodeToUtf8(LPCWSTR zWideFilename){
+static char *winUnicodeToUtf8(LPCWSTR zWideText){
int nByte;
- char *zFilename;
+ char *zText;
- nByte = osWideCharToMultiByte(CP_UTF8, 0, zWideFilename, -1, 0, 0, 0, 0);
+ nByte = osWideCharToMultiByte(CP_UTF8, 0, zWideText, -1, 0, 0, 0, 0);
if( nByte == 0 ){
return 0;
}
- zFilename = sqlite3MallocZero( nByte );
- if( zFilename==0 ){
+ zText = sqlite3MallocZero( nByte );
+ if( zText==0 ){
return 0;
}
- nByte = osWideCharToMultiByte(CP_UTF8, 0, zWideFilename, -1, zFilename, nByte,
+ nByte = osWideCharToMultiByte(CP_UTF8, 0, zWideText, -1, zText, nByte,
0, 0);
if( nByte == 0 ){
- sqlite3_free(zFilename);
- zFilename = 0;
+ sqlite3_free(zText);
+ zText = 0;
}
- return zFilename;
+ return zText;
}
/*
-** Convert an ANSI string to Microsoft Unicode, based on the
-** current codepage settings for file apis.
+** Convert an ANSI string to Microsoft Unicode, using the ANSI or OEM
+** code page.
**
-** Space to hold the returned string is obtained
-** from sqlite3_malloc.
+** Space to hold the returned string is obtained from sqlite3_malloc().
*/
-static LPWSTR winMbcsToUnicode(const char *zFilename){
+static LPWSTR winMbcsToUnicode(const char *zText, int useAnsi){
int nByte;
- LPWSTR zMbcsFilename;
- int codepage = osAreFileApisANSI() ? CP_ACP : CP_OEMCP;
+ LPWSTR zMbcsText;
+ int codepage = useAnsi ? CP_ACP : CP_OEMCP;
- nByte = osMultiByteToWideChar(codepage, 0, zFilename, -1, NULL,
+ nByte = osMultiByteToWideChar(codepage, 0, zText, -1, NULL,
0)*sizeof(WCHAR);
if( nByte==0 ){
return 0;
}
- zMbcsFilename = sqlite3MallocZero( nByte*sizeof(zMbcsFilename[0]) );
- if( zMbcsFilename==0 ){
+ zMbcsText = sqlite3MallocZero( nByte*sizeof(WCHAR) );
+ if( zMbcsText==0 ){
return 0;
}
- nByte = osMultiByteToWideChar(codepage, 0, zFilename, -1, zMbcsFilename,
+ nByte = osMultiByteToWideChar(codepage, 0, zText, -1, zMbcsText,
nByte);
if( nByte==0 ){
- sqlite3_free(zMbcsFilename);
- zMbcsFilename = 0;
+ sqlite3_free(zMbcsText);
+ zMbcsText = 0;
}
- return zMbcsFilename;
+ return zMbcsText;
}
/*
-** Convert Microsoft Unicode to multi-byte character string, based on the
-** user's ANSI codepage.
+** Convert a Microsoft Unicode string to a multi-byte character string,
+** using the ANSI or OEM code page.
**
-** Space to hold the returned string is obtained from
-** sqlite3_malloc().
+** Space to hold the returned string is obtained from sqlite3_malloc().
*/
-static char *winUnicodeToMbcs(LPCWSTR zWideFilename){
+static char *winUnicodeToMbcs(LPCWSTR zWideText, int useAnsi){
int nByte;
- char *zFilename;
- int codepage = osAreFileApisANSI() ? CP_ACP : CP_OEMCP;
+ char *zText;
+ int codepage = useAnsi ? CP_ACP : CP_OEMCP;
- nByte = osWideCharToMultiByte(codepage, 0, zWideFilename, -1, 0, 0, 0, 0);
+ nByte = osWideCharToMultiByte(codepage, 0, zWideText, -1, 0, 0, 0, 0);
if( nByte == 0 ){
return 0;
}
- zFilename = sqlite3MallocZero( nByte );
- if( zFilename==0 ){
+ zText = sqlite3MallocZero( nByte );
+ if( zText==0 ){
return 0;
}
- nByte = osWideCharToMultiByte(codepage, 0, zWideFilename, -1, zFilename,
+ nByte = osWideCharToMultiByte(codepage, 0, zWideText, -1, zText,
nByte, 0, 0);
if( nByte == 0 ){
- sqlite3_free(zFilename);
- zFilename = 0;
+ sqlite3_free(zText);
+ zText = 0;
}
- return zFilename;
+ return zText;
}
/*
-** Convert multibyte character string to UTF-8. Space to hold the
-** returned string is obtained from sqlite3_malloc().
+** Convert a multi-byte character string to UTF-8.
+**
+** Space to hold the returned string is obtained from sqlite3_malloc().
*/
-SQLITE_API char *SQLITE_STDCALL sqlite3_win32_mbcs_to_utf8(const char *zFilename){
- char *zFilenameUtf8;
+static char *winMbcsToUtf8(const char *zText, int useAnsi){
+ char *zTextUtf8;
LPWSTR zTmpWide;
- zTmpWide = winMbcsToUnicode(zFilename);
+ zTmpWide = winMbcsToUnicode(zText, useAnsi);
if( zTmpWide==0 ){
return 0;
}
- zFilenameUtf8 = winUnicodeToUtf8(zTmpWide);
+ zTextUtf8 = winUnicodeToUtf8(zTmpWide);
sqlite3_free(zTmpWide);
- return zFilenameUtf8;
+ return zTextUtf8;
}
/*
-** Convert UTF-8 to multibyte character string. Space to hold the
-** returned string is obtained from sqlite3_malloc().
+** Convert a UTF-8 string to a multi-byte character string.
+**
+** Space to hold the returned string is obtained from sqlite3_malloc().
*/
-SQLITE_API char *SQLITE_STDCALL sqlite3_win32_utf8_to_mbcs(const char *zFilename){
- char *zFilenameMbcs;
+static char *winUtf8ToMbcs(const char *zText, int useAnsi){
+ char *zTextMbcs;
LPWSTR zTmpWide;
- zTmpWide = winUtf8ToUnicode(zFilename);
+ zTmpWide = winUtf8ToUnicode(zText);
if( zTmpWide==0 ){
return 0;
}
- zFilenameMbcs = winUnicodeToMbcs(zTmpWide);
+ zTextMbcs = winUnicodeToMbcs(zTmpWide, useAnsi);
sqlite3_free(zTmpWide);
- return zFilenameMbcs;
+ return zTextMbcs;
+}
+
+/*
+** This is a public wrapper for the winUtf8ToUnicode() function.
+*/
+SQLITE_API LPWSTR SQLITE_STDCALL sqlite3_win32_utf8_to_unicode(const char *zText){
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( !zText ){
+ (void)SQLITE_MISUSE_BKPT;
+ return 0;
+ }
+#endif
+#ifndef SQLITE_OMIT_AUTOINIT
+ if( sqlite3_initialize() ) return 0;
+#endif
+ return winUtf8ToUnicode(zText);
+}
+
+/*
+** This is a public wrapper for the winUnicodeToUtf8() function.
+*/
+SQLITE_API char *SQLITE_STDCALL sqlite3_win32_unicode_to_utf8(LPCWSTR zWideText){
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( !zWideText ){
+ (void)SQLITE_MISUSE_BKPT;
+ return 0;
+ }
+#endif
+#ifndef SQLITE_OMIT_AUTOINIT
+ if( sqlite3_initialize() ) return 0;
+#endif
+ return winUnicodeToUtf8(zWideText);
+}
+
+/*
+** This is a public wrapper for the winMbcsToUtf8() function.
+*/
+SQLITE_API char *SQLITE_STDCALL sqlite3_win32_mbcs_to_utf8(const char *zText){
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( !zText ){
+ (void)SQLITE_MISUSE_BKPT;
+ return 0;
+ }
+#endif
+#ifndef SQLITE_OMIT_AUTOINIT
+ if( sqlite3_initialize() ) return 0;
+#endif
+ return winMbcsToUtf8(zText, osAreFileApisANSI());
+}
+
+/*
+** This is a public wrapper for the winMbcsToUtf8() function.
+*/
+SQLITE_API char *SQLITE_STDCALL sqlite3_win32_mbcs_to_utf8_v2(const char *zText, int useAnsi){
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( !zText ){
+ (void)SQLITE_MISUSE_BKPT;
+ return 0;
+ }
+#endif
+#ifndef SQLITE_OMIT_AUTOINIT
+ if( sqlite3_initialize() ) return 0;
+#endif
+ return winMbcsToUtf8(zText, useAnsi);
+}
+
+/*
+** This is a public wrapper for the winUtf8ToMbcs() function.
+*/
+SQLITE_API char *SQLITE_STDCALL sqlite3_win32_utf8_to_mbcs(const char *zText){
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( !zText ){
+ (void)SQLITE_MISUSE_BKPT;
+ return 0;
+ }
+#endif
+#ifndef SQLITE_OMIT_AUTOINIT
+ if( sqlite3_initialize() ) return 0;
+#endif
+ return winUtf8ToMbcs(zText, osAreFileApisANSI());
+}
+
+/*
+** This is a public wrapper for the winUtf8ToMbcs() function.
+*/
+SQLITE_API char *SQLITE_STDCALL sqlite3_win32_utf8_to_mbcs_v2(const char *zText, int useAnsi){
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( !zText ){
+ (void)SQLITE_MISUSE_BKPT;
+ return 0;
+ }
+#endif
+#ifndef SQLITE_OMIT_AUTOINIT
+ if( sqlite3_initialize() ) return 0;
+#endif
+ return winUtf8ToMbcs(zText, useAnsi);
}
/*
@@ -34980,7 +38792,7 @@ SQLITE_API int SQLITE_STDCALL sqlite3_win32_set_directory(DWORD type, LPCWSTR zV
if( zValue && zValue[0] ){
zValueUtf8 = winUnicodeToUtf8(zValue);
if ( zValueUtf8==0 ){
- return SQLITE_NOMEM;
+ return SQLITE_NOMEM_BKPT;
}
}
sqlite3_free(*ppDirectory);
@@ -35052,7 +38864,7 @@ static int winGetLastErrorMsg(DWORD lastErrno, int nBuf, char *zBuf){
if( dwLen > 0 ){
/* allocate a buffer and convert to UTF8 */
sqlite3BeginBenignMalloc();
- zOut = sqlite3_win32_mbcs_to_utf8(zTemp);
+ zOut = winMbcsToUtf8(zTemp, osAreFileApisANSI());
sqlite3EndBenignMalloc();
/* free the system buffer allocated by FormatMessage */
osLocalFree(zTemp);
@@ -35194,16 +39006,17 @@ static void winLogIoerr(int nRetry, int lineno){
}
}
-#if SQLITE_OS_WINCE
-/*************************************************************************
-** This section contains code for WinCE only.
+/*
+** This #if does not rely on the SQLITE_OS_WINCE define because the
+** corresponding section in "date.c" cannot use it.
*/
-#if !defined(SQLITE_MSVC_LOCALTIME_API) || !SQLITE_MSVC_LOCALTIME_API
+#if !defined(SQLITE_OMIT_LOCALTIME) && defined(_WIN32_WCE) && \
+ (!defined(SQLITE_MSVC_LOCALTIME_API) || !SQLITE_MSVC_LOCALTIME_API)
/*
-** The MSVC CRT on Windows CE may not have a localtime() function. So
-** create a substitute.
+** The MSVC CRT on Windows CE may not have a localtime() function.
+** So define a substitute.
*/
-/* #include <time.h> */
+/* # include <time.h> */
struct tm *__cdecl localtime(const time_t *t)
{
static struct tm y;
@@ -35227,6 +39040,10 @@ struct tm *__cdecl localtime(const time_t *t)
}
#endif
+#if SQLITE_OS_WINCE
+/*************************************************************************
+** This section contains code for WinCE only.
+*/
#define HANDLE_TO_WINFILE(a) (winFile*)&((char*)a)[-(int)offsetof(winFile,h)]
/*
@@ -35257,7 +39074,7 @@ static int winceCreateLock(const char *zFilename, winFile *pFile){
zName = winUtf8ToUnicode(zFilename);
if( zName==0 ){
/* out of memory */
- return SQLITE_IOERR_NOMEM;
+ return SQLITE_IOERR_NOMEM_BKPT;
}
/* Initialize the local lockdata */
@@ -35733,7 +39550,7 @@ static int winRead(
pFile->h, pBuf, amt, offset, pFile->locktype));
#if SQLITE_MAX_MMAP_SIZE>0
- /* Deal with as much of this read request as possible by transferring
+ /* Deal with as much of this read request as possible by transfering
** data from the memory mapping using memcpy(). */
if( offset<pFile->mmapSize ){
if( offset+amt <= pFile->mmapSize ){
@@ -35810,8 +39627,8 @@ static int winWrite(
"offset=%lld, lock=%d\n", osGetCurrentProcessId(), pFile,
pFile->h, pBuf, amt, offset, pFile->locktype));
-#if SQLITE_MAX_MMAP_SIZE>0
- /* Deal with as much of this write request as possible by transferring
+#if defined(SQLITE_MMAP_READWRITE) && SQLITE_MAX_MMAP_SIZE>0
+ /* Deal with as much of this write request as possible by transfering
** data from the memory mapping using memcpy(). */
if( offset<pFile->mmapSize ){
if( offset+amt <= pFile->mmapSize ){
@@ -36223,6 +40040,12 @@ static int winLock(sqlite3_file *id, int locktype){
return SQLITE_OK;
}
+ /* Do not allow any kind of write-lock on a read-only database
+ */
+ if( (pFile->ctrlFlags & WINFILE_RDONLY)!=0 && locktype>=RESERVED_LOCK ){
+ return SQLITE_IOERR_LOCK;
+ }
+
/* Make sure the locking sequence is correct
*/
assert( pFile->locktype!=NO_LOCK || locktype==SHARED_LOCK );
@@ -36234,9 +40057,8 @@ static int winLock(sqlite3_file *id, int locktype){
** the PENDING_LOCK byte is temporary.
*/
newLocktype = pFile->locktype;
- if( (pFile->locktype==NO_LOCK)
- || ( (locktype==EXCLUSIVE_LOCK)
- && (pFile->locktype==RESERVED_LOCK))
+ if( pFile->locktype==NO_LOCK
+ || (locktype==EXCLUSIVE_LOCK && pFile->locktype<=RESERVED_LOCK)
){
int cnt = 3;
while( cnt-->0 && (res = winLockFile(&pFile->h, SQLITE_LOCKFILE_FLAGS,
@@ -36352,7 +40174,7 @@ static int winCheckReservedLock(sqlite3_file *id, int *pResOut){
res = 1;
OSTRACE(("TEST-WR-LOCK file=%p, result=%d (local)\n", pFile->h, res));
}else{
- res = winLockFile(&pFile->h, SQLITE_LOCKFILEEX_FLAGS,RESERVED_BYTE, 0, 1, 0);
+ res = winLockFile(&pFile->h, SQLITE_LOCKFILEEX_FLAGS,RESERVED_BYTE,0,1,0);
if( res ){
winUnlockFile(&pFile->h, RESERVED_BYTE, 0, 1, 0);
}
@@ -36442,7 +40264,7 @@ static int winFileControl(sqlite3_file *id, int op, void *pArg){
OSTRACE(("FCNTL file=%p, rc=SQLITE_OK\n", pFile->h));
return SQLITE_OK;
}
- case SQLITE_LAST_ERRNO: {
+ case SQLITE_FCNTL_LAST_ERRNO: {
*(int*)pArg = (int)pFile->lastErrno;
OSTRACE(("FCNTL file=%p, rc=SQLITE_OK\n", pFile->h));
return SQLITE_OK;
@@ -36592,14 +40414,14 @@ static SYSTEM_INFO winSysInfo;
** winShmLeaveMutex()
*/
static void winShmEnterMutex(void){
- sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER));
+ sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_VFS1));
}
static void winShmLeaveMutex(void){
- sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER));
+ sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_VFS1));
}
#ifndef NDEBUG
static int winShmMutexHeld(void) {
- return sqlite3_mutex_held(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER));
+ return sqlite3_mutex_held(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_VFS1));
}
#endif
@@ -36800,12 +40622,12 @@ static int winOpenSharedMemory(winFile *pDbFd){
** allocate space for a new winShmNode and filename.
*/
p = sqlite3MallocZero( sizeof(*p) );
- if( p==0 ) return SQLITE_IOERR_NOMEM;
+ if( p==0 ) return SQLITE_IOERR_NOMEM_BKPT;
nName = sqlite3Strlen30(pDbFd->zPath);
pNew = sqlite3MallocZero( sizeof(*pShmNode) + nName + 17 );
if( pNew==0 ){
sqlite3_free(p);
- return SQLITE_IOERR_NOMEM;
+ return SQLITE_IOERR_NOMEM_BKPT;
}
pNew->zFilename = (char*)&pNew[1];
sqlite3_snprintf(nName+15, pNew->zFilename, "%s-shm", pDbFd->zPath);
@@ -36830,10 +40652,12 @@ static int winOpenSharedMemory(winFile *pDbFd){
pShmNode->pNext = winShmNodeList;
winShmNodeList = pShmNode;
- pShmNode->mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST);
- if( pShmNode->mutex==0 ){
- rc = SQLITE_IOERR_NOMEM;
- goto shm_open_err;
+ if( sqlite3GlobalConfig.bCoreMutex ){
+ pShmNode->mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST);
+ if( pShmNode->mutex==0 ){
+ rc = SQLITE_IOERR_NOMEM_BKPT;
+ goto shm_open_err;
+ }
}
rc = winOpen(pDbFd->pVfs,
@@ -37052,8 +40876,8 @@ static void winShmBarrier(
sqlite3_file *fd /* Database holding the shared memory */
){
UNUSED_PARAMETER(fd);
- /* MemoryBarrier(); // does not work -- do not know why not */
- winShmEnterMutex();
+ sqlite3MemoryBarrier(); /* compiler-defined memory barrier */
+ winShmEnterMutex(); /* Also mutex, for redundancy */
winShmLeaveMutex();
}
@@ -37137,7 +40961,7 @@ static int winShmMap(
pShmNode->aRegion, (iRegion+1)*sizeof(apNew[0])
);
if( !apNew ){
- rc = SQLITE_IOERR_NOMEM;
+ rc = SQLITE_IOERR_NOMEM_BKPT;
goto shmpage_out;
}
pShmNode->aRegion = apNew;
@@ -37154,7 +40978,7 @@ static int winShmMap(
hMap = osCreateFileMappingW(pShmNode->hFile.h,
NULL, PAGE_READWRITE, 0, nByte, NULL
);
-#elif defined(SQLITE_WIN32_HAS_ANSI)
+#elif defined(SQLITE_WIN32_HAS_ANSI) && SQLITE_WIN32_CREATEFILEMAPPINGA
hMap = osCreateFileMappingA(pShmNode->hFile.h,
NULL, PAGE_READWRITE, 0, nByte, NULL
);
@@ -37298,17 +41122,19 @@ static int winMapfile(winFile *pFd, sqlite3_int64 nByte){
DWORD flags = FILE_MAP_READ;
winUnmapfile(pFd);
+#ifdef SQLITE_MMAP_READWRITE
if( (pFd->ctrlFlags & WINFILE_RDONLY)==0 ){
protect = PAGE_READWRITE;
flags |= FILE_MAP_WRITE;
}
+#endif
#if SQLITE_OS_WINRT
pFd->hMap = osCreateFileMappingFromApp(pFd->h, NULL, protect, nMap, NULL);
#elif defined(SQLITE_WIN32_HAS_WIDE)
pFd->hMap = osCreateFileMappingW(pFd->h, NULL, protect,
(DWORD)((nMap>>32) & 0xffffffff),
(DWORD)(nMap & 0xffffffff), NULL);
-#elif defined(SQLITE_WIN32_HAS_ANSI)
+#elif defined(SQLITE_WIN32_HAS_ANSI) && SQLITE_WIN32_CREATEFILEMAPPINGA
pFd->hMap = osCreateFileMappingA(pFd->h, NULL, protect,
(DWORD)((nMap>>32) & 0xffffffff),
(DWORD)(nMap & 0xffffffff), NULL);
@@ -37489,7 +41315,7 @@ static char *winConvertToUtf8Filename(const void *zFilename){
}
#ifdef SQLITE_WIN32_HAS_ANSI
else{
- zConverted = sqlite3_win32_mbcs_to_utf8(zFilename);
+ zConverted = winMbcsToUtf8(zFilename, osAreFileApisANSI());
}
#endif
/* caller will handle out of memory */
@@ -37510,7 +41336,7 @@ static void *winConvertFromUtf8Filename(const char *zFilename){
}
#ifdef SQLITE_WIN32_HAS_ANSI
else{
- zConverted = sqlite3_win32_utf8_to_mbcs(zFilename);
+ zConverted = winUtf8ToMbcs(zFilename, osAreFileApisANSI());
}
#endif
/* caller will handle out of memory */
@@ -37565,7 +41391,7 @@ static int winGetTempname(sqlite3_vfs *pVfs, char **pzBuf){
zBuf = sqlite3MallocZero( nBuf );
if( !zBuf ){
OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_NOMEM\n"));
- return SQLITE_IOERR_NOMEM;
+ return SQLITE_IOERR_NOMEM_BKPT;
}
/* Figure out the effective temporary directory. First, check if one
@@ -37623,7 +41449,7 @@ static int winGetTempname(sqlite3_vfs *pVfs, char **pzBuf){
if( !zConverted ){
sqlite3_free(zBuf);
OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_NOMEM\n"));
- return SQLITE_IOERR_NOMEM;
+ return SQLITE_IOERR_NOMEM_BKPT;
}
if( winIsDir(zConverted) ){
sqlite3_snprintf(nMax, zBuf, "%s", zDir);
@@ -37636,7 +41462,7 @@ static int winGetTempname(sqlite3_vfs *pVfs, char **pzBuf){
if( !zConverted ){
sqlite3_free(zBuf);
OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_NOMEM\n"));
- return SQLITE_IOERR_NOMEM;
+ return SQLITE_IOERR_NOMEM_BKPT;
}
if( cygwin_conv_path(
osIsNT() ? CCP_POSIX_TO_WIN_W : CCP_POSIX_TO_WIN_A, zDir,
@@ -37657,7 +41483,7 @@ static int winGetTempname(sqlite3_vfs *pVfs, char **pzBuf){
sqlite3_free(zConverted);
sqlite3_free(zBuf);
OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_NOMEM\n"));
- return SQLITE_IOERR_NOMEM;
+ return SQLITE_IOERR_NOMEM_BKPT;
}
sqlite3_snprintf(nMax, zBuf, "%s", zUtf8);
sqlite3_free(zUtf8);
@@ -37675,7 +41501,7 @@ static int winGetTempname(sqlite3_vfs *pVfs, char **pzBuf){
if( !zWidePath ){
sqlite3_free(zBuf);
OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_NOMEM\n"));
- return SQLITE_IOERR_NOMEM;
+ return SQLITE_IOERR_NOMEM_BKPT;
}
if( osGetTempPathW(nMax, zWidePath)==0 ){
sqlite3_free(zWidePath);
@@ -37693,7 +41519,7 @@ static int winGetTempname(sqlite3_vfs *pVfs, char **pzBuf){
sqlite3_free(zWidePath);
sqlite3_free(zBuf);
OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_NOMEM\n"));
- return SQLITE_IOERR_NOMEM;
+ return SQLITE_IOERR_NOMEM_BKPT;
}
}
#ifdef SQLITE_WIN32_HAS_ANSI
@@ -37703,7 +41529,7 @@ static int winGetTempname(sqlite3_vfs *pVfs, char **pzBuf){
if( !zMbcsPath ){
sqlite3_free(zBuf);
OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_NOMEM\n"));
- return SQLITE_IOERR_NOMEM;
+ return SQLITE_IOERR_NOMEM_BKPT;
}
if( osGetTempPathA(nMax, zMbcsPath)==0 ){
sqlite3_free(zBuf);
@@ -37711,14 +41537,14 @@ static int winGetTempname(sqlite3_vfs *pVfs, char **pzBuf){
return winLogError(SQLITE_IOERR_GETTEMPPATH, osGetLastError(),
"winGetTempname3", 0);
}
- zUtf8 = sqlite3_win32_mbcs_to_utf8(zMbcsPath);
+ zUtf8 = winMbcsToUtf8(zMbcsPath, osAreFileApisANSI());
if( zUtf8 ){
sqlite3_snprintf(nMax, zBuf, "%s", zUtf8);
sqlite3_free(zUtf8);
}else{
sqlite3_free(zBuf);
OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_NOMEM\n"));
- return SQLITE_IOERR_NOMEM;
+ return SQLITE_IOERR_NOMEM_BKPT;
}
}
#endif /* SQLITE_WIN32_HAS_ANSI */
@@ -37910,7 +41736,7 @@ static int winOpen(
if( zConverted==0 ){
sqlite3_free(zTmpname);
OSTRACE(("OPEN name=%s, rc=SQLITE_IOERR_NOMEM", zUtf8Name));
- return SQLITE_IOERR_NOMEM;
+ return SQLITE_IOERR_NOMEM_BKPT;
}
if( winIsDir(zConverted) ){
@@ -38110,7 +41936,7 @@ static int winDelete(
zConverted = winConvertFromUtf8Filename(zFilename);
if( zConverted==0 ){
OSTRACE(("DELETE name=%s, rc=SQLITE_IOERR_NOMEM\n", zFilename));
- return SQLITE_IOERR_NOMEM;
+ return SQLITE_IOERR_NOMEM_BKPT;
}
if( osIsNT() ){
do {
@@ -38218,7 +42044,7 @@ static int winAccess(
zConverted = winConvertFromUtf8Filename(zFilename);
if( zConverted==0 ){
OSTRACE(("ACCESS name=%s, rc=SQLITE_IOERR_NOMEM\n", zFilename));
- return SQLITE_IOERR_NOMEM;
+ return SQLITE_IOERR_NOMEM_BKPT;
}
if( osIsNT() ){
int cnt = 0;
@@ -38345,7 +42171,7 @@ static int winFullPathname(
*/
char *zOut = sqlite3MallocZero( pVfs->mxPathname+1 );
if( !zOut ){
- return SQLITE_IOERR_NOMEM;
+ return SQLITE_IOERR_NOMEM_BKPT;
}
if( cygwin_conv_path(
(osIsNT() ? CCP_POSIX_TO_WIN_W : CCP_POSIX_TO_WIN_A) |
@@ -38357,7 +42183,7 @@ static int winFullPathname(
char *zUtf8 = winConvertToUtf8Filename(zOut);
if( !zUtf8 ){
sqlite3_free(zOut);
- return SQLITE_IOERR_NOMEM;
+ return SQLITE_IOERR_NOMEM_BKPT;
}
sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s%c%s",
sqlite3_data_directory, winGetDirSep(), zUtf8);
@@ -38367,7 +42193,7 @@ static int winFullPathname(
}else{
char *zOut = sqlite3MallocZero( pVfs->mxPathname+1 );
if( !zOut ){
- return SQLITE_IOERR_NOMEM;
+ return SQLITE_IOERR_NOMEM_BKPT;
}
if( cygwin_conv_path(
(osIsNT() ? CCP_POSIX_TO_WIN_W : CCP_POSIX_TO_WIN_A),
@@ -38379,7 +42205,7 @@ static int winFullPathname(
char *zUtf8 = winConvertToUtf8Filename(zOut);
if( !zUtf8 ){
sqlite3_free(zOut);
- return SQLITE_IOERR_NOMEM;
+ return SQLITE_IOERR_NOMEM_BKPT;
}
sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s", zUtf8);
sqlite3_free(zUtf8);
@@ -38439,7 +42265,7 @@ static int winFullPathname(
}
zConverted = winConvertFromUtf8Filename(zRelative);
if( zConverted==0 ){
- return SQLITE_IOERR_NOMEM;
+ return SQLITE_IOERR_NOMEM_BKPT;
}
if( osIsNT() ){
LPWSTR zTemp;
@@ -38453,7 +42279,7 @@ static int winFullPathname(
zTemp = sqlite3MallocZero( nByte*sizeof(zTemp[0]) );
if( zTemp==0 ){
sqlite3_free(zConverted);
- return SQLITE_IOERR_NOMEM;
+ return SQLITE_IOERR_NOMEM_BKPT;
}
nByte = osGetFullPathNameW((LPCWSTR)zConverted, nByte, zTemp, 0);
if( nByte==0 ){
@@ -38479,7 +42305,7 @@ static int winFullPathname(
zTemp = sqlite3MallocZero( nByte*sizeof(zTemp[0]) );
if( zTemp==0 ){
sqlite3_free(zConverted);
- return SQLITE_IOERR_NOMEM;
+ return SQLITE_IOERR_NOMEM_BKPT;
}
nByte = osGetFullPathNameA((char*)zConverted, nByte, zTemp, 0);
if( nByte==0 ){
@@ -38489,7 +42315,7 @@ static int winFullPathname(
"winFullPathname4", zRelative);
}
sqlite3_free(zConverted);
- zOut = sqlite3_win32_mbcs_to_utf8(zTemp);
+ zOut = winMbcsToUtf8(zTemp, osAreFileApisANSI());
sqlite3_free(zTemp);
}
#endif
@@ -38498,7 +42324,7 @@ static int winFullPathname(
sqlite3_free(zOut);
return SQLITE_OK;
}else{
- return SQLITE_IOERR_NOMEM;
+ return SQLITE_IOERR_NOMEM_BKPT;
}
#endif
}
@@ -38573,65 +42399,85 @@ static void winDlClose(sqlite3_vfs *pVfs, void *pHandle){
#define winDlClose 0
#endif
+/* State information for the randomness gatherer. */
+typedef struct EntropyGatherer EntropyGatherer;
+struct EntropyGatherer {
+ unsigned char *a; /* Gather entropy into this buffer */
+ int na; /* Size of a[] in bytes */
+ int i; /* XOR next input into a[i] */
+ int nXor; /* Number of XOR operations done */
+};
+
+#if !defined(SQLITE_TEST) && !defined(SQLITE_OMIT_RANDOMNESS)
+/* Mix sz bytes of entropy into p. */
+static void xorMemory(EntropyGatherer *p, unsigned char *x, int sz){
+ int j, k;
+ for(j=0, k=p->i; j<sz; j++){
+ p->a[k++] ^= x[j];
+ if( k>=p->na ) k = 0;
+ }
+ p->i = k;
+ p->nXor += sz;
+}
+#endif /* !defined(SQLITE_TEST) && !defined(SQLITE_OMIT_RANDOMNESS) */
/*
** Write up to nBuf bytes of randomness into zBuf.
*/
static int winRandomness(sqlite3_vfs *pVfs, int nBuf, char *zBuf){
- int n = 0;
- UNUSED_PARAMETER(pVfs);
#if defined(SQLITE_TEST) || defined(SQLITE_OMIT_RANDOMNESS)
- n = nBuf;
+ UNUSED_PARAMETER(pVfs);
memset(zBuf, 0, nBuf);
+ return nBuf;
#else
- if( sizeof(SYSTEMTIME)<=nBuf-n ){
+ EntropyGatherer e;
+ UNUSED_PARAMETER(pVfs);
+ memset(zBuf, 0, nBuf);
+#if defined(_MSC_VER) && _MSC_VER>=1400 && !SQLITE_OS_WINCE
+ rand_s((unsigned int*)zBuf); /* rand_s() is not available with MinGW */
+#endif /* defined(_MSC_VER) && _MSC_VER>=1400 */
+ e.a = (unsigned char*)zBuf;
+ e.na = nBuf;
+ e.nXor = 0;
+ e.i = 0;
+ {
SYSTEMTIME x;
osGetSystemTime(&x);
- memcpy(&zBuf[n], &x, sizeof(x));
- n += sizeof(x);
+ xorMemory(&e, (unsigned char*)&x, sizeof(SYSTEMTIME));
}
- if( sizeof(DWORD)<=nBuf-n ){
+ {
DWORD pid = osGetCurrentProcessId();
- memcpy(&zBuf[n], &pid, sizeof(pid));
- n += sizeof(pid);
+ xorMemory(&e, (unsigned char*)&pid, sizeof(DWORD));
}
#if SQLITE_OS_WINRT
- if( sizeof(ULONGLONG)<=nBuf-n ){
+ {
ULONGLONG cnt = osGetTickCount64();
- memcpy(&zBuf[n], &cnt, sizeof(cnt));
- n += sizeof(cnt);
+ xorMemory(&e, (unsigned char*)&cnt, sizeof(ULONGLONG));
}
#else
- if( sizeof(DWORD)<=nBuf-n ){
+ {
DWORD cnt = osGetTickCount();
- memcpy(&zBuf[n], &cnt, sizeof(cnt));
- n += sizeof(cnt);
+ xorMemory(&e, (unsigned char*)&cnt, sizeof(DWORD));
}
-#endif
- if( sizeof(LARGE_INTEGER)<=nBuf-n ){
+#endif /* SQLITE_OS_WINRT */
+ {
LARGE_INTEGER i;
osQueryPerformanceCounter(&i);
- memcpy(&zBuf[n], &i, sizeof(i));
- n += sizeof(i);
+ xorMemory(&e, (unsigned char*)&i, sizeof(LARGE_INTEGER));
}
#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;
+ xorMemory(&e, (unsigned char*)&id, sizeof(UUID));
memset(&id, 0, sizeof(UUID));
osUuidCreateSequential(&id);
- memcpy(zBuf, &id, sizeof(UUID));
- n += sizeof(UUID);
+ xorMemory(&e, (unsigned char*)&id, sizeof(UUID));
}
-#endif
-#endif /* defined(SQLITE_TEST) || defined(SQLITE_ZERO_PRNG_SEED) */
- return n;
+#endif /* !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && SQLITE_WIN32_USE_UUID */
+ return e.nXor>nBuf ? nBuf : e.nXor;
+#endif /* defined(SQLITE_TEST) || defined(SQLITE_OMIT_RANDOMNESS) */
}
@@ -38747,8 +42593,10 @@ static int winCurrentTime(sqlite3_vfs *pVfs, double *prNow){
** sqlite3_errmsg(), possibly making IO errors easier to debug.
*/
static int winGetLastError(sqlite3_vfs *pVfs, int nBuf, char *zBuf){
+ DWORD e = osGetLastError();
UNUSED_PARAMETER(pVfs);
- return winGetLastErrorMsg(osGetLastError(), nBuf, zBuf);
+ if( nBuf>0 ) winGetLastErrorMsg(e, nBuf, zBuf);
+ return e;
}
/*
@@ -38879,13 +42727,15 @@ SQLITE_API int SQLITE_STDCALL sqlite3_os_end(void){
** start of a transaction, and is thus usually less than a few thousand,
** but can be as large as 2 billion for a really big database.
*/
+/* #include "sqliteInt.h" */
/* Size of the Bitvec structure in bytes. */
#define BITVEC_SZ 512
/* Round the union size down to the nearest pointer boundary, since that's how
** it will be aligned within the Bitvec struct. */
-#define BITVEC_USIZE (((BITVEC_SZ-(3*sizeof(u32)))/sizeof(Bitvec*))*sizeof(Bitvec*))
+#define BITVEC_USIZE \
+ (((BITVEC_SZ-(3*sizeof(u32)))/sizeof(Bitvec*))*sizeof(Bitvec*))
/* Type of the array "element" for the bitmap representation.
** Should be a power of 2, and ideally, evenly divide into BITVEC_USIZE.
@@ -38970,10 +42820,10 @@ SQLITE_PRIVATE Bitvec *sqlite3BitvecCreate(u32 iSize){
** If p is NULL (if the bitmap has not been created) or if
** i is out of range, then return false.
*/
-SQLITE_PRIVATE int sqlite3BitvecTest(Bitvec *p, u32 i){
- if( p==0 ) return 0;
- if( i>p->iSize || i==0 ) return 0;
+SQLITE_PRIVATE int sqlite3BitvecTestNotNull(Bitvec *p, u32 i){
+ assert( p!=0 );
i--;
+ if( i>=p->iSize ) return 0;
while( p->iDivisor ){
u32 bin = i/p->iDivisor;
i = i%p->iDivisor;
@@ -38993,6 +42843,9 @@ SQLITE_PRIVATE int sqlite3BitvecTest(Bitvec *p, u32 i){
return 0;
}
}
+SQLITE_PRIVATE int sqlite3BitvecTest(Bitvec *p, u32 i){
+ return p!=0 && sqlite3BitvecTestNotNull(p,i);
+}
/*
** Set the i-th bit. Return 0 on success and an error code if
@@ -39017,7 +42870,7 @@ SQLITE_PRIVATE int sqlite3BitvecSet(Bitvec *p, u32 i){
i = i%p->iDivisor;
if( p->u.apSub[bin]==0 ){
p->u.apSub[bin] = sqlite3BitvecCreate( p->iDivisor );
- if( p->u.apSub[bin]==0 ) return SQLITE_NOMEM;
+ if( p->u.apSub[bin]==0 ) return SQLITE_NOMEM_BKPT;
}
p = p->u.apSub[bin];
}
@@ -39052,7 +42905,7 @@ bitvec_set_rehash:
int rc;
u32 *aiValues = sqlite3StackAllocRaw(0, sizeof(p->u.aHash));
if( aiValues==0 ){
- return SQLITE_NOMEM;
+ return SQLITE_NOMEM_BKPT;
}else{
memcpy(aiValues, p->u.aHash, sizeof(p->u.aHash));
memset(p->u.apSub, 0, sizeof(p->u.apSub));
@@ -39265,15 +43118,39 @@ bitvec_end:
*************************************************************************
** This file implements that page cache.
*/
+/* #include "sqliteInt.h" */
/*
-** A complete page cache is an instance of this structure.
+** A complete page cache is an instance of this structure. Every
+** entry in the cache holds a single page of the database file. The
+** btree layer only operates on the cached copy of the database pages.
+**
+** A page cache entry is "clean" if it exactly matches what is currently
+** on disk. A page is "dirty" if it has been modified and needs to be
+** persisted to disk.
+**
+** pDirty, pDirtyTail, pSynced:
+** All dirty pages are linked into the doubly linked list using
+** PgHdr.pDirtyNext and pDirtyPrev. The list is maintained in LRU order
+** such that p was added to the list more recently than p->pDirtyNext.
+** PCache.pDirty points to the first (newest) element in the list and
+** pDirtyTail to the last (oldest).
+**
+** The PCache.pSynced variable is used to optimize searching for a dirty
+** page to eject from the cache mid-transaction. It is better to eject
+** a page that does not require a journal sync than one that does.
+** Therefore, pSynced is maintained to that it *almost* always points
+** to either the oldest page in the pDirty/pDirtyTail list that has a
+** clear PGHDR_NEED_SYNC flag or to a page that is older than this one
+** (so that the right page to eject can be found by following pDirtyPrev
+** pointers).
*/
struct PCache {
PgHdr *pDirty, *pDirtyTail; /* List of dirty pages in LRU order */
PgHdr *pSynced; /* Last synced page in dirty page list */
- int nRef; /* Number of referenced pages */
+ int nRefSum; /* Sum of ref counts over all pages */
int szCache; /* Configured cache size */
+ int szSpill; /* Size before spilling occurs */
int szPage; /* Size of every page in this cache */
int szExtra; /* Size of extra space for each page */
u8 bPurgeable; /* True if pages are on backing store */
@@ -39281,9 +43158,97 @@ struct PCache {
int (*xStress)(void*,PgHdr*); /* Call to try make a page clean */
void *pStress; /* Argument to xStress */
sqlite3_pcache *pCache; /* Pluggable cache module */
- PgHdr *pPage1; /* Reference to page 1 */
};
+/********************************** Test and Debug Logic **********************/
+/*
+** Debug tracing macros. Enable by by changing the "0" to "1" and
+** recompiling.
+**
+** When sqlite3PcacheTrace is 1, single line trace messages are issued.
+** When sqlite3PcacheTrace is 2, a dump of the pcache showing all cache entries
+** is displayed for many operations, resulting in a lot of output.
+*/
+#if defined(SQLITE_DEBUG) && 0
+ int sqlite3PcacheTrace = 2; /* 0: off 1: simple 2: cache dumps */
+ int sqlite3PcacheMxDump = 9999; /* Max cache entries for pcacheDump() */
+# define pcacheTrace(X) if(sqlite3PcacheTrace){sqlite3DebugPrintf X;}
+ void pcacheDump(PCache *pCache){
+ int N;
+ int i, j;
+ sqlite3_pcache_page *pLower;
+ PgHdr *pPg;
+ unsigned char *a;
+
+ if( sqlite3PcacheTrace<2 ) return;
+ if( pCache->pCache==0 ) return;
+ N = sqlite3PcachePagecount(pCache);
+ if( N>sqlite3PcacheMxDump ) N = sqlite3PcacheMxDump;
+ for(i=1; i<=N; i++){
+ pLower = sqlite3GlobalConfig.pcache2.xFetch(pCache->pCache, i, 0);
+ if( pLower==0 ) continue;
+ pPg = (PgHdr*)pLower->pExtra;
+ printf("%3d: nRef %2d flgs %02x data ", i, pPg->nRef, pPg->flags);
+ a = (unsigned char *)pLower->pBuf;
+ for(j=0; j<12; j++) printf("%02x", a[j]);
+ printf("\n");
+ if( pPg->pPage==0 ){
+ sqlite3GlobalConfig.pcache2.xUnpin(pCache->pCache, pLower, 0);
+ }
+ }
+ }
+ #else
+# define pcacheTrace(X)
+# define pcacheDump(X)
+#endif
+
+/*
+** Check invariants on a PgHdr entry. Return true if everything is OK.
+** Return false if any invariant is violated.
+**
+** This routine is for use inside of assert() statements only. For
+** example:
+**
+** assert( sqlite3PcachePageSanity(pPg) );
+*/
+#if SQLITE_DEBUG
+SQLITE_PRIVATE int sqlite3PcachePageSanity(PgHdr *pPg){
+ PCache *pCache;
+ assert( pPg!=0 );
+ assert( pPg->pgno>0 ); /* Page number is 1 or more */
+ pCache = pPg->pCache;
+ assert( pCache!=0 ); /* Every page has an associated PCache */
+ if( pPg->flags & PGHDR_CLEAN ){
+ assert( (pPg->flags & PGHDR_DIRTY)==0 );/* Cannot be both CLEAN and DIRTY */
+ assert( pCache->pDirty!=pPg ); /* CLEAN pages not on dirty list */
+ assert( pCache->pDirtyTail!=pPg );
+ }
+ /* WRITEABLE pages must also be DIRTY */
+ if( pPg->flags & PGHDR_WRITEABLE ){
+ assert( pPg->flags & PGHDR_DIRTY ); /* WRITEABLE implies DIRTY */
+ }
+ /* NEED_SYNC can be set independently of WRITEABLE. This can happen,
+ ** for example, when using the sqlite3PagerDontWrite() optimization:
+ ** (1) Page X is journalled, and gets WRITEABLE and NEED_SEEK.
+ ** (2) Page X moved to freelist, WRITEABLE is cleared
+ ** (3) Page X reused, WRITEABLE is set again
+ ** If NEED_SYNC had been cleared in step 2, then it would not be reset
+ ** in step 3, and page might be written into the database without first
+ ** syncing the rollback journal, which might cause corruption on a power
+ ** loss.
+ **
+ ** Another example is when the database page size is smaller than the
+ ** disk sector size. When any page of a sector is journalled, all pages
+ ** in that sector are marked NEED_SYNC even if they are still CLEAN, just
+ ** in case they are later modified, since all pages in the same sector
+ ** must be journalled and synced before any of those pages can be safely
+ ** written.
+ */
+ return 1;
+}
+#endif /* SQLITE_DEBUG */
+
+
/********************************** Linked List Management ********************/
/* Allowed values for second argument to pcacheManageDirtyList() */
@@ -39300,17 +43265,16 @@ struct PCache {
static void pcacheManageDirtyList(PgHdr *pPage, u8 addRemove){
PCache *p = pPage->pCache;
+ pcacheTrace(("%p.DIRTYLIST.%s %d\n", p,
+ addRemove==1 ? "REMOVE" : addRemove==2 ? "ADD" : "FRONT",
+ pPage->pgno));
if( addRemove & PCACHE_DIRTYLIST_REMOVE ){
assert( pPage->pDirtyNext || pPage==p->pDirtyTail );
assert( pPage->pDirtyPrev || pPage==p->pDirty );
/* Update the PCache1.pSynced variable if necessary. */
if( p->pSynced==pPage ){
- PgHdr *pSynced = pPage->pDirtyPrev;
- while( pSynced && (pSynced->flags&PGHDR_NEED_SYNC) ){
- pSynced = pSynced->pDirtyPrev;
- }
- p->pSynced = pSynced;
+ p->pSynced = pPage->pDirtyPrev;
}
if( pPage->pDirtyNext ){
@@ -39322,10 +43286,15 @@ static void pcacheManageDirtyList(PgHdr *pPage, u8 addRemove){
if( pPage->pDirtyPrev ){
pPage->pDirtyPrev->pDirtyNext = pPage->pDirtyNext;
}else{
+ /* If there are now no dirty pages in the cache, set eCreate to 2.
+ ** This is an optimization that allows sqlite3PcacheFetch() to skip
+ ** searching for a dirty page to eject from the cache when it might
+ ** otherwise have to. */
assert( pPage==p->pDirty );
p->pDirty = pPage->pDirtyNext;
- if( p->pDirty==0 && p->bPurgeable ){
- assert( p->eCreate==1 );
+ assert( p->bPurgeable || p->eCreate==2 );
+ if( p->pDirty==0 ){ /*OPTIMIZATION-IF-TRUE*/
+ assert( p->bPurgeable==0 || p->eCreate==1 );
p->eCreate = 2;
}
}
@@ -39347,10 +43316,19 @@ static void pcacheManageDirtyList(PgHdr *pPage, u8 addRemove){
}
}
p->pDirty = pPage;
- if( !p->pSynced && 0==(pPage->flags&PGHDR_NEED_SYNC) ){
+
+ /* If pSynced is NULL and this page has a clear NEED_SYNC flag, set
+ ** pSynced to point to it. Checking the NEED_SYNC flag is an
+ ** optimization, as if pSynced points to a page with the NEED_SYNC
+ ** flag set sqlite3PcacheFetchStress() searches through all newer
+ ** entries of the dirty-list for a page with NEED_SYNC clear anyway. */
+ if( !p->pSynced
+ && 0==(pPage->flags&PGHDR_NEED_SYNC) /*OPTIMIZATION-IF-FALSE*/
+ ){
p->pSynced = pPage;
}
}
+ pcacheDump(p);
}
/*
@@ -39359,18 +43337,15 @@ static void pcacheManageDirtyList(PgHdr *pPage, u8 addRemove){
*/
static void pcacheUnpin(PgHdr *p){
if( p->pCache->bPurgeable ){
- if( p->pgno==1 ){
- p->pCache->pPage1 = 0;
- }
+ pcacheTrace(("%p.UNPIN %d\n", p->pCache, p->pgno));
sqlite3GlobalConfig.pcache2.xUnpin(p->pCache->pCache, p->pPage, 0);
+ pcacheDump(p->pCache);
}
}
/*
-** Compute the number of pages of cache requested. p->szCache is the
+** 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 ){
@@ -39433,6 +43408,8 @@ SQLITE_PRIVATE int sqlite3PcacheOpen(
p->xStress = xStress;
p->pStress = pStress;
p->szCache = 100;
+ p->szSpill = 1;
+ pcacheTrace(("%p.OPEN szPage %d bPurgeable %d\n",p,szPage,bPurgeable));
return sqlite3PcacheSetPageSize(p, szPage);
}
@@ -39441,21 +43418,21 @@ SQLITE_PRIVATE int sqlite3PcacheOpen(
** are no outstanding page references when this function is called.
*/
SQLITE_PRIVATE int sqlite3PcacheSetPageSize(PCache *pCache, int szPage){
- assert( pCache->nRef==0 && pCache->pDirty==0 );
+ assert( pCache->nRefSum==0 && pCache->pDirty==0 );
if( pCache->szPage ){
sqlite3_pcache *pNew;
pNew = sqlite3GlobalConfig.pcache2.xCreate(
szPage, pCache->szExtra + ROUND8(sizeof(PgHdr)),
pCache->bPurgeable
);
- if( pNew==0 ) return SQLITE_NOMEM;
+ if( pNew==0 ) return SQLITE_NOMEM_BKPT;
sqlite3GlobalConfig.pcache2.xCachesize(pNew, numberOfCachePages(pCache));
if( pCache->pCache ){
sqlite3GlobalConfig.pcache2.xDestroy(pCache->pCache);
}
pCache->pCache = pNew;
- pCache->pPage1 = 0;
pCache->szPage = szPage;
+ pcacheTrace(("%p.PAGESIZE %d\n",pCache,szPage));
}
return SQLITE_OK;
}
@@ -39490,11 +43467,13 @@ SQLITE_PRIVATE sqlite3_pcache_page *sqlite3PcacheFetch(
int createFlag /* If true, create page if it does not exist already */
){
int eCreate;
+ sqlite3_pcache_page *pRes;
assert( pCache!=0 );
assert( pCache->pCache!=0 );
assert( createFlag==3 || createFlag==0 );
assert( pgno>0 );
+ assert( pCache->eCreate==((pCache->bPurgeable && pCache->pDirty) ? 1 : 2) );
/* eCreate defines what to do if the page does not exist.
** 0 Do not allocate a new page. (createFlag==0)
@@ -39507,12 +43486,15 @@ SQLITE_PRIVATE sqlite3_pcache_page *sqlite3PcacheFetch(
assert( eCreate==0 || eCreate==1 || eCreate==2 );
assert( createFlag==0 || pCache->eCreate==eCreate );
assert( createFlag==0 || eCreate==1+(!pCache->bPurgeable||!pCache->pDirty) );
- return sqlite3GlobalConfig.pcache2.xFetch(pCache->pCache, pgno, eCreate);
+ pRes = sqlite3GlobalConfig.pcache2.xFetch(pCache->pCache, pgno, eCreate);
+ pcacheTrace(("%p.FETCH %d%s (result: %p)\n",pCache,pgno,
+ createFlag?" create":"",pRes));
+ return pRes;
}
/*
** If the sqlite3PcacheFetch() routine is unable to allocate a new
-** page because new clean pages are available for reuse and the cache
+** page because no clean pages are available for reuse and the cache
** size limit has been reached, then this routine can be invoked to
** try harder to allocate a page. This routine might invoke the stress
** callback to spill dirty pages to the journal. It will then try to
@@ -39529,36 +43511,43 @@ SQLITE_PRIVATE int sqlite3PcacheFetchStress(
PgHdr *pPg;
if( pCache->eCreate==2 ) return 0;
-
- /* Find a dirty page to write-out and recycle. First try to find a
- ** page that does not require a journal-sync (one with PGHDR_NEED_SYNC
- ** cleared), but if that is not possible settle for any other
- ** unreferenced dirty page.
- */
- for(pPg=pCache->pSynced;
- pPg && (pPg->nRef || (pPg->flags&PGHDR_NEED_SYNC));
- pPg=pPg->pDirtyPrev
- );
- pCache->pSynced = pPg;
- if( !pPg ){
- for(pPg=pCache->pDirtyTail; pPg && pPg->nRef; pPg=pPg->pDirtyPrev);
- }
- if( pPg ){
- int rc;
+ if( sqlite3PcachePagecount(pCache)>pCache->szSpill ){
+ /* Find a dirty page to write-out and recycle. First try to find a
+ ** page that does not require a journal-sync (one with PGHDR_NEED_SYNC
+ ** cleared), but if that is not possible settle for any other
+ ** unreferenced dirty page.
+ **
+ ** If the LRU page in the dirty list that has a clear PGHDR_NEED_SYNC
+ ** flag is currently referenced, then the following may leave pSynced
+ ** set incorrectly (pointing to other than the LRU page with NEED_SYNC
+ ** cleared). This is Ok, as pSynced is just an optimization. */
+ for(pPg=pCache->pSynced;
+ pPg && (pPg->nRef || (pPg->flags&PGHDR_NEED_SYNC));
+ pPg=pPg->pDirtyPrev
+ );
+ pCache->pSynced = pPg;
+ if( !pPg ){
+ for(pPg=pCache->pDirtyTail; pPg && pPg->nRef; pPg=pPg->pDirtyPrev);
+ }
+ if( pPg ){
+ int rc;
#ifdef SQLITE_LOG_CACHE_SPILL
- sqlite3_log(SQLITE_FULL,
- "spill page %d making room for %d - cache used: %d/%d",
- pPg->pgno, pgno,
- sqlite3GlobalConfig.pcache.xPagecount(pCache->pCache),
+ sqlite3_log(SQLITE_FULL,
+ "spill page %d making room for %d - cache used: %d/%d",
+ pPg->pgno, pgno,
+ sqlite3GlobalConfig.pcache.xPagecount(pCache->pCache),
numberOfCachePages(pCache));
#endif
- rc = pCache->xStress(pCache->pStress, pPg);
- if( rc!=SQLITE_OK && rc!=SQLITE_BUSY ){
- return rc;
+ pcacheTrace(("%p.SPILL %d\n",pCache,pPg->pgno));
+ rc = pCache->xStress(pCache->pStress, pPg);
+ pcacheDump(pCache);
+ if( rc!=SQLITE_OK && rc!=SQLITE_BUSY ){
+ return rc;
+ }
}
}
*ppPage = sqlite3GlobalConfig.pcache2.xFetch(pCache->pCache, pgno, 2);
- return *ppPage==0 ? SQLITE_NOMEM : SQLITE_OK;
+ return *ppPage==0 ? SQLITE_NOMEM_BKPT : SQLITE_OK;
}
/*
@@ -39579,13 +43568,14 @@ static SQLITE_NOINLINE PgHdr *pcacheFetchFinishWithInit(
assert( pPage!=0 );
pPgHdr = (PgHdr*)pPage->pExtra;
assert( pPgHdr->pPage==0 );
- memset(pPgHdr, 0, sizeof(PgHdr));
+ memset(pPgHdr, 0, sizeof(PgHdr));
pPgHdr->pPage = pPage;
pPgHdr->pData = pPage->pBuf;
pPgHdr->pExtra = (void *)&pPgHdr[1];
memset(pPgHdr->pExtra, 0, pCache->szExtra);
pPgHdr->pCache = pCache;
pPgHdr->pgno = pgno;
+ pPgHdr->flags = PGHDR_CLEAN;
return sqlite3PcacheFetchFinish(pCache,pgno,pPage);
}
@@ -39602,19 +43592,15 @@ SQLITE_PRIVATE PgHdr *sqlite3PcacheFetchFinish(
){
PgHdr *pPgHdr;
- if( pPage==0 ) return 0;
+ assert( pPage!=0 );
pPgHdr = (PgHdr *)pPage->pExtra;
if( !pPgHdr->pPage ){
return pcacheFetchFinishWithInit(pCache, pgno, pPage);
}
- if( 0==pPgHdr->nRef ){
- pCache->nRef++;
- }
+ pCache->nRefSum++;
pPgHdr->nRef++;
- if( pgno==1 ){
- pCache->pPage1 = pPgHdr;
- }
+ assert( sqlite3PcachePageSanity(pPgHdr) );
return pPgHdr;
}
@@ -39624,13 +43610,15 @@ SQLITE_PRIVATE PgHdr *sqlite3PcacheFetchFinish(
*/
SQLITE_PRIVATE void SQLITE_NOINLINE sqlite3PcacheRelease(PgHdr *p){
assert( p->nRef>0 );
- p->nRef--;
- if( p->nRef==0 ){
- p->pCache->nRef--;
- if( (p->flags&PGHDR_DIRTY)==0 ){
+ p->pCache->nRefSum--;
+ if( (--p->nRef)==0 ){
+ if( p->flags&PGHDR_CLEAN ){
pcacheUnpin(p);
- }else if( p->pDirtyPrev!=0 ){
- /* Move the page to the head of the dirty list. */
+ }else if( p->pDirtyPrev!=0 ){ /*OPTIMIZATION-IF-FALSE*/
+ /* Move the page to the head of the dirty list. If p->pDirtyPrev==0,
+ ** then page p is already at the head of the dirty list and the
+ ** following call would be a no-op. Hence the OPTIMIZATION-IF-FALSE
+ ** tag above. */
pcacheManageDirtyList(p, PCACHE_DIRTYLIST_FRONT);
}
}
@@ -39641,7 +43629,9 @@ SQLITE_PRIVATE void SQLITE_NOINLINE sqlite3PcacheRelease(PgHdr *p){
*/
SQLITE_PRIVATE void sqlite3PcacheRef(PgHdr *p){
assert(p->nRef>0);
+ assert( sqlite3PcachePageSanity(p) );
p->nRef++;
+ p->pCache->nRefSum++;
}
/*
@@ -39651,13 +43641,11 @@ SQLITE_PRIVATE void sqlite3PcacheRef(PgHdr *p){
*/
SQLITE_PRIVATE void sqlite3PcacheDrop(PgHdr *p){
assert( p->nRef==1 );
+ assert( sqlite3PcachePageSanity(p) );
if( p->flags&PGHDR_DIRTY ){
pcacheManageDirtyList(p, PCACHE_DIRTYLIST_REMOVE);
}
- p->pCache->nRef--;
- if( p->pgno==1 ){
- p->pCache->pPage1 = 0;
- }
+ p->pCache->nRefSum--;
sqlite3GlobalConfig.pcache2.xUnpin(p->pCache->pCache, p->pPage, 1);
}
@@ -39666,11 +43654,17 @@ SQLITE_PRIVATE void sqlite3PcacheDrop(PgHdr *p){
** make it so.
*/
SQLITE_PRIVATE void sqlite3PcacheMakeDirty(PgHdr *p){
- p->flags &= ~PGHDR_DONT_WRITE;
assert( p->nRef>0 );
- if( 0==(p->flags & PGHDR_DIRTY) ){
- p->flags |= PGHDR_DIRTY;
- pcacheManageDirtyList(p, PCACHE_DIRTYLIST_ADD);
+ assert( sqlite3PcachePageSanity(p) );
+ if( p->flags & (PGHDR_CLEAN|PGHDR_DONT_WRITE) ){ /*OPTIMIZATION-IF-FALSE*/
+ p->flags &= ~PGHDR_DONT_WRITE;
+ if( p->flags & PGHDR_CLEAN ){
+ p->flags ^= (PGHDR_DIRTY|PGHDR_CLEAN);
+ pcacheTrace(("%p.DIRTY %d\n",p->pCache,p->pgno));
+ assert( (p->flags & (PGHDR_DIRTY|PGHDR_CLEAN))==PGHDR_DIRTY );
+ pcacheManageDirtyList(p, PCACHE_DIRTYLIST_ADD);
+ }
+ assert( sqlite3PcachePageSanity(p) );
}
}
@@ -39679,9 +43673,14 @@ SQLITE_PRIVATE void sqlite3PcacheMakeDirty(PgHdr *p){
** make it so.
*/
SQLITE_PRIVATE void sqlite3PcacheMakeClean(PgHdr *p){
- if( (p->flags & PGHDR_DIRTY) ){
+ assert( sqlite3PcachePageSanity(p) );
+ if( ALWAYS((p->flags & PGHDR_DIRTY)!=0) ){
+ assert( (p->flags & PGHDR_CLEAN)==0 );
pcacheManageDirtyList(p, PCACHE_DIRTYLIST_REMOVE);
- p->flags &= ~(PGHDR_DIRTY|PGHDR_NEED_SYNC);
+ p->flags &= ~(PGHDR_DIRTY|PGHDR_NEED_SYNC|PGHDR_WRITEABLE);
+ p->flags |= PGHDR_CLEAN;
+ pcacheTrace(("%p.CLEAN %d\n",p->pCache,p->pgno));
+ assert( sqlite3PcachePageSanity(p) );
if( p->nRef==0 ){
pcacheUnpin(p);
}
@@ -39693,12 +43692,25 @@ SQLITE_PRIVATE void sqlite3PcacheMakeClean(PgHdr *p){
*/
SQLITE_PRIVATE void sqlite3PcacheCleanAll(PCache *pCache){
PgHdr *p;
+ pcacheTrace(("%p.CLEAN-ALL\n",pCache));
while( (p = pCache->pDirty)!=0 ){
sqlite3PcacheMakeClean(p);
}
}
/*
+** Clear the PGHDR_NEED_SYNC and PGHDR_WRITEABLE flag from all dirty pages.
+*/
+SQLITE_PRIVATE void sqlite3PcacheClearWritable(PCache *pCache){
+ PgHdr *p;
+ pcacheTrace(("%p.CLEAR-WRITEABLE\n",pCache));
+ for(p=pCache->pDirty; p; p=p->pDirtyNext){
+ p->flags &= ~(PGHDR_NEED_SYNC|PGHDR_WRITEABLE);
+ }
+ pCache->pSynced = pCache->pDirtyTail;
+}
+
+/*
** Clear the PGHDR_NEED_SYNC flag from all dirty pages.
*/
SQLITE_PRIVATE void sqlite3PcacheClearSyncFlags(PCache *pCache){
@@ -39716,6 +43728,8 @@ SQLITE_PRIVATE void sqlite3PcacheMove(PgHdr *p, Pgno newPgno){
PCache *pCache = p->pCache;
assert( p->nRef>0 );
assert( newPgno>0 );
+ assert( sqlite3PcachePageSanity(p) );
+ pcacheTrace(("%p.MOVE %d -> %d\n",pCache,p->pgno,newPgno));
sqlite3GlobalConfig.pcache2.xRekey(pCache->pCache, p->pPage, p->pgno,newPgno);
p->pgno = newPgno;
if( (p->flags&PGHDR_DIRTY) && (p->flags&PGHDR_NEED_SYNC) ){
@@ -39736,6 +43750,7 @@ SQLITE_PRIVATE void sqlite3PcacheTruncate(PCache *pCache, Pgno pgno){
if( pCache->pCache ){
PgHdr *p;
PgHdr *pNext;
+ pcacheTrace(("%p.TRUNCATE %d\n",pCache,pgno));
for(p=pCache->pDirty; p; p=pNext){
pNext = p->pDirtyNext;
/* This routine never gets call with a positive pgno except right
@@ -39743,14 +43758,19 @@ SQLITE_PRIVATE void sqlite3PcacheTruncate(PCache *pCache, Pgno pgno){
** it must be that pgno==0.
*/
assert( p->pgno>0 );
- if( ALWAYS(p->pgno>pgno) ){
+ if( p->pgno>pgno ){
assert( p->flags&PGHDR_DIRTY );
sqlite3PcacheMakeClean(p);
}
}
- if( pgno==0 && pCache->pPage1 ){
- memset(pCache->pPage1->pData, 0, pCache->szPage);
- pgno = 1;
+ if( pgno==0 && pCache->nRefSum ){
+ sqlite3_pcache_page *pPage1;
+ pPage1 = sqlite3GlobalConfig.pcache2.xFetch(pCache->pCache,1,0);
+ if( ALWAYS(pPage1) ){ /* Page 1 is always available in cache, because
+ ** pCache->nRefSum>0 */
+ memset(pPage1->pBuf, 0, pCache->szPage);
+ pgno = 1;
+ }
}
sqlite3GlobalConfig.pcache2.xTruncate(pCache->pCache, pgno+1);
}
@@ -39761,6 +43781,7 @@ SQLITE_PRIVATE void sqlite3PcacheTruncate(PCache *pCache, Pgno pgno){
*/
SQLITE_PRIVATE void sqlite3PcacheClose(PCache *pCache){
assert( pCache->pCache!=0 );
+ pcacheTrace(("%p.CLOSE\n",pCache));
sqlite3GlobalConfig.pcache2.xDestroy(pCache->pCache);
}
@@ -39853,10 +43874,13 @@ SQLITE_PRIVATE PgHdr *sqlite3PcacheDirtyList(PCache *pCache){
}
/*
-** Return the total number of referenced pages held by the cache.
+** Return the total number of references to all pages held by the cache.
+**
+** This is not the total number of pages referenced, but the sum of the
+** reference count for all pages.
*/
SQLITE_PRIVATE int sqlite3PcacheRefCount(PCache *pCache){
- return pCache->nRef;
+ return pCache->nRefSum;
}
/*
@@ -39894,6 +43918,25 @@ SQLITE_PRIVATE void sqlite3PcacheSetCachesize(PCache *pCache, int mxPage){
}
/*
+** Set the suggested cache-spill value. Make no changes if if the
+** argument is zero. Return the effective cache-spill size, which will
+** be the larger of the szSpill and szCache.
+*/
+SQLITE_PRIVATE int sqlite3PcacheSetSpillsize(PCache *p, int mxPage){
+ int res;
+ assert( p->pCache!=0 );
+ if( mxPage ){
+ if( mxPage<0 ){
+ mxPage = (int)((-1024*(i64)mxPage)/(p->szPage+p->szExtra));
+ }
+ p->szSpill = mxPage;
+ }
+ res = numberOfCachePages(p);
+ if( res<p->szSpill ) res = p->szSpill;
+ return res;
+}
+
+/*
** Free up as much memory as possible from the page cache.
*/
SQLITE_PRIVATE void sqlite3PcacheShrink(PCache *pCache){
@@ -39907,6 +43950,17 @@ SQLITE_PRIVATE void sqlite3PcacheShrink(PCache *pCache){
*/
SQLITE_PRIVATE int sqlite3HeaderSizePcache(void){ return ROUND8(sizeof(PgHdr)); }
+/*
+** Return the number of dirty pages currently in the cache, as a percentage
+** of the configured cache size.
+*/
+SQLITE_PRIVATE int sqlite3PCachePercentDirty(PCache *pCache){
+ PgHdr *pDirty;
+ int nDirty = 0;
+ int nCache = numberOfCachePages(pCache);
+ for(pDirty=pCache->pDirty; pDirty; pDirty=pDirty->pDirtyNext) nDirty++;
+ return nCache ? (int)(((i64)nDirty * 100) / nCache) : 0;
+}
#if defined(SQLITE_CHECK_PAGES) || defined(SQLITE_DEBUG)
/*
@@ -39941,14 +43995,96 @@ SQLITE_PRIVATE void sqlite3PcacheIterateDirty(PCache *pCache, void (*xIter)(PgHd
** of the SQLITE_CONFIG_PAGECACHE and sqlite3_release_memory() features.
** If the default page cache implementation is overridden, then neither of
** these two features are available.
+**
+** A Page cache line looks like this:
+**
+** -------------------------------------------------------------
+** | database page content | PgHdr1 | MemPage | PgHdr |
+** -------------------------------------------------------------
+**
+** The database page content is up front (so that buffer overreads tend to
+** flow harmlessly into the PgHdr1, MemPage, and PgHdr extensions). MemPage
+** is the extension added by the btree.c module containing information such
+** as the database page number and how that database page is used. PgHdr
+** is added by the pcache.c layer and contains information used to keep track
+** of which pages are "dirty". PgHdr1 is an extension added by this
+** module (pcache1.c). The PgHdr1 header is a subclass of sqlite3_pcache_page.
+** PgHdr1 contains information needed to look up a page by its page number.
+** The superclass sqlite3_pcache_page.pBuf points to the start of the
+** database page content and sqlite3_pcache_page.pExtra points to PgHdr.
+**
+** The size of the extension (MemPage+PgHdr+PgHdr1) can be determined at
+** runtime using sqlite3_config(SQLITE_CONFIG_PCACHE_HDRSZ, &size). The
+** sizes of the extensions sum to 272 bytes on x64 for 3.8.10, but this
+** size can vary according to architecture, compile-time options, and
+** SQLite library version number.
+**
+** If SQLITE_PCACHE_SEPARATE_HEADER is defined, then the extension is obtained
+** using a separate memory allocation from the database page content. This
+** seeks to overcome the "clownshoe" problem (also called "internal
+** fragmentation" in academic literature) of allocating a few bytes more
+** than a power of two with the memory allocator rounding up to the next
+** power of two, and leaving the rounded-up space unused.
+**
+** This module tracks pointers to PgHdr1 objects. Only pcache.c communicates
+** with this module. Information is passed back and forth as PgHdr1 pointers.
+**
+** The pcache.c and pager.c modules deal pointers to PgHdr objects.
+** The btree.c module deals with pointers to MemPage objects.
+**
+** SOURCE OF PAGE CACHE MEMORY:
+**
+** Memory for a page might come from any of three sources:
+**
+** (1) The general-purpose memory allocator - sqlite3Malloc()
+** (2) Global page-cache memory provided using sqlite3_config() with
+** SQLITE_CONFIG_PAGECACHE.
+** (3) PCache-local bulk allocation.
+**
+** The third case is a chunk of heap memory (defaulting to 100 pages worth)
+** that is allocated when the page cache is created. The size of the local
+** bulk allocation can be adjusted using
+**
+** sqlite3_config(SQLITE_CONFIG_PAGECACHE, (void*)0, 0, N).
+**
+** If N is positive, then N pages worth of memory are allocated using a single
+** sqlite3Malloc() call and that memory is used for the first N pages allocated.
+** Or if N is negative, then -1024*N bytes of memory are allocated and used
+** for as many pages as can be accomodated.
+**
+** Only one of (2) or (3) can be used. Once the memory available to (2) or
+** (3) is exhausted, subsequent allocations fail over to the general-purpose
+** memory allocator (1).
+**
+** Earlier versions of SQLite used only methods (1) and (2). But experiments
+** show that method (3) with N==100 provides about a 5% performance boost for
+** common workloads.
*/
-
+/* #include "sqliteInt.h" */
typedef struct PCache1 PCache1;
typedef struct PgHdr1 PgHdr1;
typedef struct PgFreeslot PgFreeslot;
typedef struct PGroup PGroup;
+/*
+** Each cache entry is represented by an instance of the following
+** structure. Unless SQLITE_PCACHE_SEPARATE_HEADER is defined, a buffer of
+** PgHdr1.pCache->szPage bytes is allocated directly before this structure
+** in memory.
+*/
+struct PgHdr1 {
+ sqlite3_pcache_page page; /* Base class. Must be first. pBuf & pExtra */
+ unsigned int iKey; /* Key value (page number) */
+ u8 isPinned; /* Page in use, not on the LRU list */
+ u8 isBulkLocal; /* This page from bulk local storage */
+ u8 isAnchor; /* This is the PGroup.lru element */
+ PgHdr1 *pNext; /* Next in hash table chain */
+ PCache1 *pCache; /* Cache that currently owns this page */
+ PgHdr1 *pLruNext; /* Next in LRU list of unpinned pages */
+ PgHdr1 *pLruPrev; /* Previous in LRU list of unpinned pages */
+};
+
/* Each page cache (or PCache) belongs to a PGroup. A PGroup is a set
** of one or more PCaches that are able to recycle each other's unpinned
** pages when they are under memory pressure. A PGroup is an instance of
@@ -39977,7 +44113,7 @@ struct PGroup {
unsigned int nMinPage; /* Sum of nMin for purgeable caches */
unsigned int mxPinned; /* nMaxpage + 10 - nMinPage */
unsigned int nCurrentPage; /* Number of purgeable pages allocated */
- PgHdr1 *pLruHead, *pLruTail; /* LRU list of unpinned pages */
+ PgHdr1 lru; /* The beginning and end of the LRU list */
};
/* Each page cache is an instance of the following object. Every
@@ -39995,8 +44131,9 @@ struct PCache1 {
** The PGroup mutex must be held when accessing nMax.
*/
PGroup *pGroup; /* PGroup this cache belongs to */
- int szPage; /* Size of allocated pages in bytes */
- int szExtra; /* Size of extra space in bytes */
+ int szPage; /* Size of database content section */
+ int szExtra; /* sizeof(MemPage)+sizeof(PgHdr) */
+ int szAlloc; /* Total size of one pcache line */
int bPurgeable; /* True if cache is purgeable */
unsigned int nMin; /* Minimum number of pages reserved */
unsigned int nMax; /* Configured "cache_size" value */
@@ -40010,27 +44147,13 @@ struct PCache1 {
unsigned int nPage; /* Total number of pages in apHash */
unsigned int nHash; /* Number of slots in apHash[] */
PgHdr1 **apHash; /* Hash table for fast lookup by key */
+ PgHdr1 *pFree; /* List of unused pcache-local pages */
+ void *pBulk; /* Bulk memory used by pcache-local */
};
/*
-** Each cache entry is represented by an instance of the following
-** structure. Unless SQLITE_PCACHE_SEPARATE_HEADER is defined, a buffer of
-** PgHdr1.pCache->szPage bytes is allocated directly before this structure
-** in memory.
-*/
-struct PgHdr1 {
- sqlite3_pcache_page page;
- unsigned int iKey; /* Key value (page number) */
- u8 isPinned; /* Page in use, not on the LRU list */
- PgHdr1 *pNext; /* Next in hash table chain */
- PCache1 *pCache; /* Cache that currently owns this page */
- PgHdr1 *pLruNext; /* Next in LRU list of unpinned pages */
- PgHdr1 *pLruPrev; /* Previous in LRU list of unpinned pages */
-};
-
-/*
-** Free slots in the allocator used to divide up the buffer provided using
-** the SQLITE_CONFIG_PAGECACHE mechanism.
+** Free slots in the allocator used to divide up the global page cache
+** buffer provided using the SQLITE_CONFIG_PAGECACHE mechanism.
*/
struct PgFreeslot {
PgFreeslot *pNext; /* Next free slot */
@@ -40048,10 +44171,12 @@ static SQLITE_WSD struct PCacheGlobal {
** The nFreeSlot and pFree values do require mutex protection.
*/
int isInit; /* True if initialized */
+ int separateCache; /* Use a new PGroup for each PCache */
+ int nInitPage; /* Initial bulk allocation size */
int szSlot; /* Size of each free slot */
int nSlot; /* The number of pcache slots */
int nReserve; /* Try to keep nFreeSlot above this */
- void *pStart, *pEnd; /* Bounds of pagecache malloc range */
+ void *pStart, *pEnd; /* Bounds of global page cache memory */
/* Above requires no mutex. Use mutex below for variable that follow. */
sqlite3_mutex *mutex; /* Mutex for accessing the following: */
PgFreeslot *pFree; /* Free page blocks */
@@ -40073,12 +44198,20 @@ static SQLITE_WSD struct PCacheGlobal {
/*
** Macros to enter and leave the PCache LRU mutex.
*/
-#define pcache1EnterMutex(X) sqlite3_mutex_enter((X)->mutex)
-#define pcache1LeaveMutex(X) sqlite3_mutex_leave((X)->mutex)
+#if !defined(SQLITE_ENABLE_MEMORY_MANAGEMENT) || SQLITE_THREADSAFE==0
+# define pcache1EnterMutex(X) assert((X)->mutex==0)
+# define pcache1LeaveMutex(X) assert((X)->mutex==0)
+# define PCACHE1_MIGHT_USE_GROUP_MUTEX 0
+#else
+# define pcache1EnterMutex(X) sqlite3_mutex_enter((X)->mutex)
+# define pcache1LeaveMutex(X) sqlite3_mutex_leave((X)->mutex)
+# define PCACHE1_MIGHT_USE_GROUP_MUTEX 1
+#endif
/******************************************************************************/
/******** Page Allocation/SQLITE_CONFIG_PCACHE Related Functions **************/
+
/*
** This function is called during initialization if a static buffer is
** supplied to use for the page-cache by passing the SQLITE_CONFIG_PAGECACHE
@@ -40091,6 +44224,7 @@ static SQLITE_WSD struct PCacheGlobal {
SQLITE_PRIVATE void sqlite3PCacheBufferSetup(void *pBuf, int sz, int n){
if( pcache1.isInit ){
PgFreeslot *p;
+ if( pBuf==0 ) sz = n = 0;
sz = ROUNDDOWN8(sz);
pcache1.szSlot = sz;
pcache1.nSlot = pcache1.nFreeSlot = n;
@@ -40109,6 +44243,44 @@ SQLITE_PRIVATE void sqlite3PCacheBufferSetup(void *pBuf, int sz, int n){
}
/*
+** Try to initialize the pCache->pFree and pCache->pBulk fields. Return
+** true if pCache->pFree ends up containing one or more free pages.
+*/
+static int pcache1InitBulk(PCache1 *pCache){
+ i64 szBulk;
+ char *zBulk;
+ if( pcache1.nInitPage==0 ) return 0;
+ /* Do not bother with a bulk allocation if the cache size very small */
+ if( pCache->nMax<3 ) return 0;
+ sqlite3BeginBenignMalloc();
+ if( pcache1.nInitPage>0 ){
+ szBulk = pCache->szAlloc * (i64)pcache1.nInitPage;
+ }else{
+ szBulk = -1024 * (i64)pcache1.nInitPage;
+ }
+ if( szBulk > pCache->szAlloc*(i64)pCache->nMax ){
+ szBulk = pCache->szAlloc*pCache->nMax;
+ }
+ zBulk = pCache->pBulk = sqlite3Malloc( szBulk );
+ sqlite3EndBenignMalloc();
+ if( zBulk ){
+ int nBulk = sqlite3MallocSize(zBulk)/pCache->szAlloc;
+ int i;
+ for(i=0; i<nBulk; i++){
+ PgHdr1 *pX = (PgHdr1*)&zBulk[pCache->szPage];
+ pX->page.pBuf = zBulk;
+ pX->page.pExtra = &pX[1];
+ pX->isBulkLocal = 1;
+ pX->isAnchor = 0;
+ pX->pNext = pCache->pFree;
+ pCache->pFree = pX;
+ zBulk += pCache->szAlloc;
+ }
+ }
+ return pCache->pFree!=0;
+}
+
+/*
** Malloc function used within this file to allocate space from the buffer
** configured using sqlite3_config(SQLITE_CONFIG_PAGECACHE) option. If no
** such buffer exists or there is no space left in it, this function falls
@@ -40128,7 +44300,7 @@ static void *pcache1Alloc(int nByte){
pcache1.nFreeSlot--;
pcache1.bUnderPressure = pcache1.nFreeSlot<pcache1.nReserve;
assert( pcache1.nFreeSlot>=0 );
- sqlite3StatusSet(SQLITE_STATUS_PAGECACHE_SIZE, nByte);
+ sqlite3StatusHighwater(SQLITE_STATUS_PAGECACHE_SIZE, nByte);
sqlite3StatusUp(SQLITE_STATUS_PAGECACHE_USED, 1);
}
sqlite3_mutex_leave(pcache1.mutex);
@@ -40142,7 +44314,7 @@ static void *pcache1Alloc(int nByte){
if( p ){
int sz = sqlite3MallocSize(p);
sqlite3_mutex_enter(pcache1.mutex);
- sqlite3StatusSet(SQLITE_STATUS_PAGECACHE_SIZE, nByte);
+ sqlite3StatusHighwater(SQLITE_STATUS_PAGECACHE_SIZE, nByte);
sqlite3StatusUp(SQLITE_STATUS_PAGECACHE_OVERFLOW, sz);
sqlite3_mutex_leave(pcache1.mutex);
}
@@ -40155,10 +44327,9 @@ static void *pcache1Alloc(int nByte){
/*
** Free an allocated buffer obtained from pcache1Alloc().
*/
-static int pcache1Free(void *p){
- int nFreed = 0;
- if( p==0 ) return 0;
- if( p>=pcache1.pStart && p<pcache1.pEnd ){
+static void pcache1Free(void *p){
+ if( p==0 ) return;
+ if( SQLITE_WITHIN(p, pcache1.pStart, pcache1.pEnd) ){
PgFreeslot *pSlot;
sqlite3_mutex_enter(pcache1.mutex);
sqlite3StatusDown(SQLITE_STATUS_PAGECACHE_USED, 1);
@@ -40172,15 +44343,17 @@ static int pcache1Free(void *p){
}else{
assert( sqlite3MemdebugHasType(p, MEMTYPE_PCACHE) );
sqlite3MemdebugSetType(p, MEMTYPE_HEAP);
- nFreed = sqlite3MallocSize(p);
#ifndef SQLITE_DISABLE_PAGECACHE_OVERFLOW_STATS
- sqlite3_mutex_enter(pcache1.mutex);
- sqlite3StatusDown(SQLITE_STATUS_PAGECACHE_OVERFLOW, nFreed);
- sqlite3_mutex_leave(pcache1.mutex);
+ {
+ int nFreed = 0;
+ nFreed = sqlite3MallocSize(p);
+ sqlite3_mutex_enter(pcache1.mutex);
+ sqlite3StatusDown(SQLITE_STATUS_PAGECACHE_OVERFLOW, nFreed);
+ sqlite3_mutex_leave(pcache1.mutex);
+ }
#endif
sqlite3_free(p);
}
- return nFreed;
}
#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
@@ -40204,58 +44377,72 @@ static int pcache1MemSize(void *p){
/*
** Allocate a new page object initially associated with cache pCache.
*/
-static PgHdr1 *pcache1AllocPage(PCache1 *pCache){
+static PgHdr1 *pcache1AllocPage(PCache1 *pCache, int benignMalloc){
PgHdr1 *p = 0;
void *pPg;
- /* The group mutex must be released before pcache1Alloc() is called. This
- ** is because it may call sqlite3_release_memory(), which assumes that
- ** this mutex is not held. */
assert( sqlite3_mutex_held(pCache->pGroup->mutex) );
- pcache1LeaveMutex(pCache->pGroup);
+ if( pCache->pFree || (pCache->nPage==0 && pcache1InitBulk(pCache)) ){
+ p = pCache->pFree;
+ pCache->pFree = p->pNext;
+ p->pNext = 0;
+ }else{
+#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
+ /* The group mutex must be released before pcache1Alloc() is called. This
+ ** is because it might call sqlite3_release_memory(), which assumes that
+ ** this mutex is not held. */
+ assert( pcache1.separateCache==0 );
+ assert( pCache->pGroup==&pcache1.grp );
+ pcache1LeaveMutex(pCache->pGroup);
+#endif
+ if( benignMalloc ){ sqlite3BeginBenignMalloc(); }
#ifdef SQLITE_PCACHE_SEPARATE_HEADER
- pPg = pcache1Alloc(pCache->szPage);
- p = sqlite3Malloc(sizeof(PgHdr1) + pCache->szExtra);
- if( !pPg || !p ){
- pcache1Free(pPg);
- sqlite3_free(p);
- pPg = 0;
- }
+ pPg = pcache1Alloc(pCache->szPage);
+ p = sqlite3Malloc(sizeof(PgHdr1) + pCache->szExtra);
+ if( !pPg || !p ){
+ pcache1Free(pPg);
+ sqlite3_free(p);
+ pPg = 0;
+ }
#else
- pPg = pcache1Alloc(ROUND8(sizeof(PgHdr1)) + pCache->szPage + pCache->szExtra);
- p = (PgHdr1 *)&((u8 *)pPg)[pCache->szPage];
+ pPg = pcache1Alloc(pCache->szAlloc);
+ p = (PgHdr1 *)&((u8 *)pPg)[pCache->szPage];
#endif
- pcache1EnterMutex(pCache->pGroup);
-
- if( pPg ){
+ if( benignMalloc ){ sqlite3EndBenignMalloc(); }
+#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
+ pcache1EnterMutex(pCache->pGroup);
+#endif
+ if( pPg==0 ) return 0;
p->page.pBuf = pPg;
p->page.pExtra = &p[1];
- if( pCache->bPurgeable ){
- pCache->pGroup->nCurrentPage++;
- }
- return p;
+ p->isBulkLocal = 0;
+ p->isAnchor = 0;
}
- return 0;
+ if( pCache->bPurgeable ){
+ pCache->pGroup->nCurrentPage++;
+ }
+ return p;
}
/*
** Free a page object allocated by pcache1AllocPage().
-**
-** The pointer is allowed to be NULL, which is prudent. But it turns out
-** that the current implementation happens to never call this routine
-** with a NULL pointer, so we mark the NULL test with ALWAYS().
*/
static void pcache1FreePage(PgHdr1 *p){
- if( ALWAYS(p) ){
- PCache1 *pCache = p->pCache;
- assert( sqlite3_mutex_held(p->pCache->pGroup->mutex) );
+ PCache1 *pCache;
+ assert( p!=0 );
+ pCache = p->pCache;
+ assert( sqlite3_mutex_held(p->pCache->pGroup->mutex) );
+ if( p->isBulkLocal ){
+ p->pNext = pCache->pFree;
+ pCache->pFree = p;
+ }else{
pcache1Free(p->page.pBuf);
#ifdef SQLITE_PCACHE_SEPARATE_HEADER
sqlite3_free(p);
#endif
- if( pCache->bPurgeable ){
- pCache->pGroup->nCurrentPage--;
- }
+ }
+ if( pCache->bPurgeable ){
+ pCache->pGroup->nCurrentPage--;
}
}
@@ -40350,41 +44537,35 @@ static void pcache1ResizeHash(PCache1 *p){
**
** The PGroup mutex must be held when this function is called.
*/
-static void pcache1PinPage(PgHdr1 *pPage){
+static PgHdr1 *pcache1PinPage(PgHdr1 *pPage){
PCache1 *pCache;
- PGroup *pGroup;
assert( pPage!=0 );
assert( pPage->isPinned==0 );
pCache = pPage->pCache;
- pGroup = pCache->pGroup;
- assert( pPage->pLruNext || pPage==pGroup->pLruTail );
- assert( pPage->pLruPrev || pPage==pGroup->pLruHead );
- assert( sqlite3_mutex_held(pGroup->mutex) );
- if( pPage->pLruPrev ){
- pPage->pLruPrev->pLruNext = pPage->pLruNext;
- }else{
- pGroup->pLruHead = pPage->pLruNext;
- }
- if( pPage->pLruNext ){
- pPage->pLruNext->pLruPrev = pPage->pLruPrev;
- }else{
- pGroup->pLruTail = pPage->pLruPrev;
- }
+ assert( pPage->pLruNext );
+ assert( pPage->pLruPrev );
+ assert( sqlite3_mutex_held(pCache->pGroup->mutex) );
+ pPage->pLruPrev->pLruNext = pPage->pLruNext;
+ pPage->pLruNext->pLruPrev = pPage->pLruPrev;
pPage->pLruNext = 0;
pPage->pLruPrev = 0;
pPage->isPinned = 1;
+ assert( pPage->isAnchor==0 );
+ assert( pCache->pGroup->lru.isAnchor==1 );
pCache->nRecyclable--;
+ return pPage;
}
/*
** Remove the page supplied as an argument from the hash table
** (PCache1.apHash structure) that it is currently stored in.
+** Also free the page if freePage is true.
**
** The PGroup mutex must be held when this function is called.
*/
-static void pcache1RemoveFromHash(PgHdr1 *pPage){
+static void pcache1RemoveFromHash(PgHdr1 *pPage, int freeFlag){
unsigned int h;
PCache1 *pCache = pPage->pCache;
PgHdr1 **pp;
@@ -40395,21 +44576,28 @@ static void pcache1RemoveFromHash(PgHdr1 *pPage){
*pp = (*pp)->pNext;
pCache->nPage--;
+ if( freeFlag ) pcache1FreePage(pPage);
}
/*
** If there are currently more than nMaxPage pages allocated, try
** to recycle pages to reduce the number allocated to nMaxPage.
*/
-static void pcache1EnforceMaxPage(PGroup *pGroup){
+static void pcache1EnforceMaxPage(PCache1 *pCache){
+ PGroup *pGroup = pCache->pGroup;
+ PgHdr1 *p;
assert( sqlite3_mutex_held(pGroup->mutex) );
- while( pGroup->nCurrentPage>pGroup->nMaxPage && pGroup->pLruTail ){
- PgHdr1 *p = pGroup->pLruTail;
+ while( pGroup->nCurrentPage>pGroup->nMaxPage
+ && (p=pGroup->lru.pLruPrev)->isAnchor==0
+ ){
assert( p->pCache->pGroup==pGroup );
assert( p->isPinned==0 );
pcache1PinPage(p);
- pcache1RemoveFromHash(p);
- pcache1FreePage(p);
+ pcache1RemoveFromHash(p, 1);
+ }
+ if( pCache->nPage==0 && pCache->pBulk ){
+ sqlite3_free(pCache->pBulk);
+ pCache->pBulk = pCache->pFree = 0;
}
}
@@ -40455,9 +44643,44 @@ static int pcache1Init(void *NotUsed){
UNUSED_PARAMETER(NotUsed);
assert( pcache1.isInit==0 );
memset(&pcache1, 0, sizeof(pcache1));
+
+
+ /*
+ ** The pcache1.separateCache variable is true if each PCache has its own
+ ** private PGroup (mode-1). pcache1.separateCache is false if the single
+ ** PGroup in pcache1.grp is used for all page caches (mode-2).
+ **
+ ** * Always use a unified cache (mode-2) if ENABLE_MEMORY_MANAGEMENT
+ **
+ ** * Use a unified cache in single-threaded applications that have
+ ** configured a start-time buffer for use as page-cache memory using
+ ** sqlite3_config(SQLITE_CONFIG_PAGECACHE, pBuf, sz, N) with non-NULL
+ ** pBuf argument.
+ **
+ ** * Otherwise use separate caches (mode-1)
+ */
+#if defined(SQLITE_ENABLE_MEMORY_MANAGEMENT)
+ pcache1.separateCache = 0;
+#elif SQLITE_THREADSAFE
+ pcache1.separateCache = sqlite3GlobalConfig.pPage==0
+ || sqlite3GlobalConfig.bCoreMutex>0;
+#else
+ pcache1.separateCache = sqlite3GlobalConfig.pPage==0;
+#endif
+
+#if SQLITE_THREADSAFE
if( sqlite3GlobalConfig.bCoreMutex ){
- pcache1.grp.mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_LRU);
- pcache1.mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_PMEM);
+ pcache1.grp.mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_LRU);
+ pcache1.mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_PMEM);
+ }
+#endif
+ if( pcache1.separateCache
+ && sqlite3GlobalConfig.nPage!=0
+ && sqlite3GlobalConfig.pPage==0
+ ){
+ pcache1.nInitPage = sqlite3GlobalConfig.nPage;
+ }else{
+ pcache1.nInitPage = 0;
}
pcache1.grp.mxPinned = 10;
pcache1.isInit = 1;
@@ -40488,39 +44711,26 @@ static sqlite3_pcache *pcache1Create(int szPage, int szExtra, int bPurgeable){
PGroup *pGroup; /* The group the new page cache will belong to */
int sz; /* Bytes of memory required to allocate the new cache */
- /*
- ** The separateCache variable is true if each PCache has its own private
- ** PGroup. In other words, separateCache is true for mode (1) where no
- ** mutexing is required.
- **
- ** * Always use a unified cache (mode-2) if ENABLE_MEMORY_MANAGEMENT
- **
- ** * Always use a unified cache in single-threaded applications
- **
- ** * Otherwise (if multi-threaded and ENABLE_MEMORY_MANAGEMENT is off)
- ** use separate caches (mode-1)
- */
-#if defined(SQLITE_ENABLE_MEMORY_MANAGEMENT) || SQLITE_THREADSAFE==0
- const int separateCache = 0;
-#else
- int separateCache = sqlite3GlobalConfig.bCoreMutex>0;
-#endif
-
assert( (szPage & (szPage-1))==0 && szPage>=512 && szPage<=65536 );
assert( szExtra < 300 );
- sz = sizeof(PCache1) + sizeof(PGroup)*separateCache;
+ sz = sizeof(PCache1) + sizeof(PGroup)*pcache1.separateCache;
pCache = (PCache1 *)sqlite3MallocZero(sz);
if( pCache ){
- if( separateCache ){
+ if( pcache1.separateCache ){
pGroup = (PGroup*)&pCache[1];
pGroup->mxPinned = 10;
}else{
pGroup = &pcache1.grp;
}
+ if( pGroup->lru.isAnchor==0 ){
+ pGroup->lru.isAnchor = 1;
+ pGroup->lru.pLruPrev = pGroup->lru.pLruNext = &pGroup->lru;
+ }
pCache->pGroup = pGroup;
pCache->szPage = szPage;
pCache->szExtra = szExtra;
+ pCache->szAlloc = szPage + szExtra + ROUND8(sizeof(PgHdr1));
pCache->bPurgeable = (bPurgeable ? 1 : 0);
pcache1EnterMutex(pGroup);
pcache1ResizeHash(pCache);
@@ -40552,7 +44762,7 @@ static void pcache1Cachesize(sqlite3_pcache *p, int nMax){
pGroup->mxPinned = pGroup->nMaxPage + 10 - pGroup->nMinPage;
pCache->nMax = nMax;
pCache->n90pct = pCache->nMax*9/10;
- pcache1EnforceMaxPage(pGroup);
+ pcache1EnforceMaxPage(pCache);
pcache1LeaveMutex(pGroup);
}
}
@@ -40570,7 +44780,7 @@ static void pcache1Shrink(sqlite3_pcache *p){
pcache1EnterMutex(pGroup);
savedMaxPage = pGroup->nMaxPage;
pGroup->nMaxPage = 0;
- pcache1EnforceMaxPage(pGroup);
+ pcache1EnforceMaxPage(pCache);
pGroup->nMaxPage = savedMaxPage;
pcache1LeaveMutex(pGroup);
}
@@ -40623,26 +44833,17 @@ static SQLITE_NOINLINE PgHdr1 *pcache1FetchStage2(
assert( pCache->nHash>0 && pCache->apHash );
/* Step 4. Try to recycle a page. */
- if( pCache->bPurgeable && pGroup->pLruTail && (
- (pCache->nPage+1>=pCache->nMax)
- || pGroup->nCurrentPage>=pGroup->nMaxPage
- || pcache1UnderMemoryPressure(pCache)
- )){
+ if( pCache->bPurgeable
+ && !pGroup->lru.pLruPrev->isAnchor
+ && ((pCache->nPage+1>=pCache->nMax) || pcache1UnderMemoryPressure(pCache))
+ ){
PCache1 *pOther;
- pPage = pGroup->pLruTail;
+ pPage = pGroup->lru.pLruPrev;
assert( pPage->isPinned==0 );
- pcache1RemoveFromHash(pPage);
+ pcache1RemoveFromHash(pPage, 0);
pcache1PinPage(pPage);
pOther = pPage->pCache;
-
- /* We want to verify that szPage and szExtra are the same for pOther
- ** and pCache. Assert that we can verify this by comparing sums. */
- assert( (pCache->szPage & (pCache->szPage-1))==0 && pCache->szPage>=512 );
- assert( pCache->szExtra<512 );
- assert( (pOther->szPage & (pOther->szPage-1))==0 && pOther->szPage>=512 );
- assert( pOther->szExtra<512 );
-
- if( pOther->szPage+pOther->szExtra != pCache->szPage+pCache->szExtra ){
+ if( pOther->szAlloc != pCache->szAlloc ){
pcache1FreePage(pPage);
pPage = 0;
}else{
@@ -40654,9 +44855,7 @@ static SQLITE_NOINLINE PgHdr1 *pcache1FetchStage2(
** attempt to allocate a new one.
*/
if( !pPage ){
- if( createFlag==1 ) sqlite3BeginBenignMalloc();
- pPage = pcache1AllocPage(pCache);
- if( createFlag==1 ) sqlite3EndBenignMalloc();
+ pPage = pcache1AllocPage(pCache, createFlag==1);
}
if( pPage ){
@@ -40730,8 +44929,13 @@ static SQLITE_NOINLINE PgHdr1 *pcache1FetchStage2(
** proceed to step 5.
**
** 5. Otherwise, allocate and return a new page buffer.
+**
+** There are two versions of this routine. pcache1FetchWithMutex() is
+** the general case. pcache1FetchNoMutex() is a faster implementation for
+** the common case where pGroup->mutex is NULL. The pcache1Fetch() wrapper
+** invokes the appropriate routine.
*/
-static sqlite3_pcache_page *pcache1Fetch(
+static PgHdr1 *pcache1FetchNoMutex(
sqlite3_pcache *p,
unsigned int iKey,
int createFlag
@@ -40739,28 +44943,66 @@ static sqlite3_pcache_page *pcache1Fetch(
PCache1 *pCache = (PCache1 *)p;
PgHdr1 *pPage = 0;
- assert( offsetof(PgHdr1,page)==0 );
- assert( pCache->bPurgeable || createFlag!=1 );
- assert( pCache->bPurgeable || pCache->nMin==0 );
- assert( pCache->bPurgeable==0 || pCache->nMin==10 );
- assert( pCache->nMin==0 || pCache->bPurgeable );
- assert( pCache->nHash>0 );
- pcache1EnterMutex(pCache->pGroup);
-
/* Step 1: Search the hash table for an existing entry. */
pPage = pCache->apHash[iKey % pCache->nHash];
while( pPage && pPage->iKey!=iKey ){ pPage = pPage->pNext; }
- /* Step 2: Abort if no existing page is found and createFlag is 0 */
+ /* Step 2: If the page was found in the hash table, then return it.
+ ** If the page was not in the hash table and createFlag is 0, abort.
+ ** Otherwise (page not in hash and createFlag!=0) continue with
+ ** subsequent steps to try to create the page. */
if( pPage ){
- if( !pPage->isPinned ) pcache1PinPage(pPage);
+ if( !pPage->isPinned ){
+ return pcache1PinPage(pPage);
+ }else{
+ return pPage;
+ }
}else if( createFlag ){
/* Steps 3, 4, and 5 implemented by this subroutine */
- pPage = pcache1FetchStage2(pCache, iKey, createFlag);
+ return pcache1FetchStage2(pCache, iKey, createFlag);
+ }else{
+ return 0;
}
+}
+#if PCACHE1_MIGHT_USE_GROUP_MUTEX
+static PgHdr1 *pcache1FetchWithMutex(
+ sqlite3_pcache *p,
+ unsigned int iKey,
+ int createFlag
+){
+ PCache1 *pCache = (PCache1 *)p;
+ PgHdr1 *pPage;
+
+ pcache1EnterMutex(pCache->pGroup);
+ pPage = pcache1FetchNoMutex(p, iKey, createFlag);
assert( pPage==0 || pCache->iMaxKey>=iKey );
pcache1LeaveMutex(pCache->pGroup);
- return (sqlite3_pcache_page*)pPage;
+ return pPage;
+}
+#endif
+static sqlite3_pcache_page *pcache1Fetch(
+ sqlite3_pcache *p,
+ unsigned int iKey,
+ int createFlag
+){
+#if PCACHE1_MIGHT_USE_GROUP_MUTEX || defined(SQLITE_DEBUG)
+ PCache1 *pCache = (PCache1 *)p;
+#endif
+
+ assert( offsetof(PgHdr1,page)==0 );
+ assert( pCache->bPurgeable || createFlag!=1 );
+ assert( pCache->bPurgeable || pCache->nMin==0 );
+ assert( pCache->bPurgeable==0 || pCache->nMin==10 );
+ assert( pCache->nMin==0 || pCache->bPurgeable );
+ assert( pCache->nHash>0 );
+#if PCACHE1_MIGHT_USE_GROUP_MUTEX
+ if( pCache->pGroup->mutex ){
+ return (sqlite3_pcache_page*)pcache1FetchWithMutex(p, iKey, createFlag);
+ }else
+#endif
+ {
+ return (sqlite3_pcache_page*)pcache1FetchNoMutex(p, iKey, createFlag);
+ }
}
@@ -40785,22 +45027,16 @@ static void pcache1Unpin(
** part of the PGroup LRU list.
*/
assert( pPage->pLruPrev==0 && pPage->pLruNext==0 );
- assert( pGroup->pLruHead!=pPage && pGroup->pLruTail!=pPage );
assert( pPage->isPinned==1 );
if( reuseUnlikely || pGroup->nCurrentPage>pGroup->nMaxPage ){
- pcache1RemoveFromHash(pPage);
- pcache1FreePage(pPage);
+ pcache1RemoveFromHash(pPage, 1);
}else{
/* Add the page to the PGroup LRU list. */
- if( pGroup->pLruHead ){
- pGroup->pLruHead->pLruPrev = pPage;
- pPage->pLruNext = pGroup->pLruHead;
- pGroup->pLruHead = pPage;
- }else{
- pGroup->pLruTail = pPage;
- pGroup->pLruHead = pPage;
- }
+ PgHdr1 **ppFirst = &pGroup->lru.pLruNext;
+ pPage->pLruPrev = &pGroup->lru;
+ (pPage->pLruNext = *ppFirst)->pLruPrev = pPage;
+ *ppFirst = pPage;
pCache->nRecyclable++;
pPage->isPinned = 0;
}
@@ -40877,8 +45113,9 @@ static void pcache1Destroy(sqlite3_pcache *p){
assert( pGroup->nMinPage >= pCache->nMin );
pGroup->nMinPage -= pCache->nMin;
pGroup->mxPinned = pGroup->nMaxPage + 10 - pGroup->nMinPage;
- pcache1EnforceMaxPage(pGroup);
+ pcache1EnforceMaxPage(pCache);
pcache1LeaveMutex(pGroup);
+ sqlite3_free(pCache->pBulk);
sqlite3_free(pCache->apHash);
sqlite3_free(pCache);
}
@@ -40934,18 +45171,20 @@ SQLITE_PRIVATE int sqlite3PcacheReleaseMemory(int nReq){
int nFree = 0;
assert( sqlite3_mutex_notheld(pcache1.grp.mutex) );
assert( sqlite3_mutex_notheld(pcache1.mutex) );
- if( pcache1.pStart==0 ){
+ if( sqlite3GlobalConfig.nPage==0 ){
PgHdr1 *p;
pcache1EnterMutex(&pcache1.grp);
- while( (nReq<0 || nFree<nReq) && ((p=pcache1.grp.pLruTail)!=0) ){
+ while( (nReq<0 || nFree<nReq)
+ && (p=pcache1.grp.lru.pLruPrev)!=0
+ && p->isAnchor==0
+ ){
nFree += pcache1MemSize(p->page.pBuf);
#ifdef SQLITE_PCACHE_SEPARATE_HEADER
nFree += sqlite3MemSize(p);
#endif
assert( p->isPinned==0 );
pcache1PinPage(p);
- pcache1RemoveFromHash(p);
- pcache1FreePage(p);
+ pcache1RemoveFromHash(p, 1);
}
pcache1LeaveMutex(&pcache1.grp);
}
@@ -40966,7 +45205,7 @@ SQLITE_PRIVATE void sqlite3PcacheStats(
){
PgHdr1 *p;
int nRecyclable = 0;
- for(p=pcache1.grp.pLruHead; p; p=p->pLruNext){
+ for(p=pcache1.grp.lru.pLruNext; p && !p->isAnchor; p=p->pLruNext){
assert( p->isPinned==0 );
nRecyclable++;
}
@@ -41038,9 +45277,11 @@ SQLITE_PRIVATE void sqlite3PcacheStats(
** of the first SMALLEST is O(NlogN). Second and subsequent SMALLEST
** primitives are constant time. The cost of DESTROY is O(N).
**
-** There is an added cost of O(N) when switching between TEST and
-** SMALLEST primitives.
+** TEST and SMALLEST may not be used by the same RowSet. This used to
+** be possible, but the feature was not used, so it was removed in order
+** to simplify the code.
*/
+/* #include "sqliteInt.h" */
/*
@@ -41159,9 +45400,11 @@ SQLITE_PRIVATE void sqlite3RowSetClear(RowSet *p){
*/
static struct RowSetEntry *rowSetEntryAlloc(RowSet *p){
assert( p!=0 );
- if( p->nFresh==0 ){
+ if( p->nFresh==0 ){ /*OPTIMIZATION-IF-FALSE*/
+ /* We could allocate a fresh RowSetEntry each time one is needed, but it
+ ** is more efficient to pull a preallocated entry from the pool */
struct RowSetChunk *pNew;
- pNew = sqlite3DbMallocRaw(p->db, sizeof(*pNew));
+ pNew = sqlite3DbMallocRawNN(p->db, sizeof(*pNew));
if( pNew==0 ){
return 0;
}
@@ -41193,7 +45436,9 @@ SQLITE_PRIVATE void sqlite3RowSetInsert(RowSet *p, i64 rowid){
pEntry->pRight = 0;
pLast = p->pLast;
if( pLast ){
- if( (p->rsFlags & ROWSET_SORTED)!=0 && rowid<=pLast->v ){
+ if( rowid<=pLast->v ){ /*OPTIMIZATION-IF-FALSE*/
+ /* Avoid unnecessary sorts by preserving the ROWSET_SORTED flags
+ ** where possible */
p->rsFlags &= ~ROWSET_SORTED;
}
pLast->pRight = pEntry;
@@ -41315,23 +45560,29 @@ static struct RowSetEntry *rowSetNDeepTree(
){
struct RowSetEntry *p; /* Root of the new tree */
struct RowSetEntry *pLeft; /* Left subtree */
- if( *ppList==0 ){
- return 0;
- }
- if( iDepth==1 ){
+ if( *ppList==0 ){ /*OPTIMIZATION-IF-TRUE*/
+ /* Prevent unnecessary deep recursion when we run out of entries */
+ return 0;
+ }
+ if( iDepth>1 ){ /*OPTIMIZATION-IF-TRUE*/
+ /* This branch causes a *balanced* tree to be generated. A valid tree
+ ** is still generated without this branch, but the tree is wildly
+ ** unbalanced and inefficient. */
+ pLeft = rowSetNDeepTree(ppList, iDepth-1);
+ p = *ppList;
+ if( p==0 ){ /*OPTIMIZATION-IF-FALSE*/
+ /* It is safe to always return here, but the resulting tree
+ ** would be unbalanced */
+ return pLeft;
+ }
+ p->pLeft = pLeft;
+ *ppList = p->pRight;
+ p->pRight = rowSetNDeepTree(ppList, iDepth-1);
+ }else{
p = *ppList;
*ppList = p->pRight;
p->pLeft = p->pRight = 0;
- return p;
- }
- pLeft = rowSetNDeepTree(ppList, iDepth-1);
- p = *ppList;
- if( p==0 ){
- return pLeft;
}
- p->pLeft = pLeft;
- *ppList = p->pRight;
- p->pRight = rowSetNDeepTree(ppList, iDepth-1);
return p;
}
@@ -41359,58 +45610,36 @@ static struct RowSetEntry *rowSetListToTree(struct RowSetEntry *pList){
}
/*
-** Take all the entries on p->pEntry and on the trees in p->pForest and
-** sort them all together into one big ordered list on p->pEntry.
-**
-** This routine should only be called once in the life of a RowSet.
-*/
-static void rowSetToList(RowSet *p){
-
- /* This routine is called only once */
- assert( p!=0 && (p->rsFlags & ROWSET_NEXT)==0 );
-
- if( (p->rsFlags & ROWSET_SORTED)==0 ){
- p->pEntry = rowSetEntrySort(p->pEntry);
- }
-
- /* While this module could theoretically support it, sqlite3RowSetNext()
- ** is never called after sqlite3RowSetText() for the same RowSet. So
- ** there is never a forest to deal with. Should this change, simply
- ** remove the assert() and the #if 0. */
- assert( p->pForest==0 );
-#if 0
- while( p->pForest ){
- struct RowSetEntry *pTree = p->pForest->pLeft;
- if( pTree ){
- struct RowSetEntry *pHead, *pTail;
- rowSetTreeToList(pTree, &pHead, &pTail);
- p->pEntry = rowSetEntryMerge(p->pEntry, pHead);
- }
- p->pForest = p->pForest->pRight;
- }
-#endif
- p->rsFlags |= ROWSET_NEXT; /* Verify this routine is never called again */
-}
-
-/*
** Extract the smallest element from the RowSet.
** Write the element into *pRowid. Return 1 on success. Return
** 0 if the RowSet is already empty.
**
** After this routine has been called, the sqlite3RowSetInsert()
-** routine may not be called again.
+** routine may not be called again.
+**
+** This routine may not be called after sqlite3RowSetTest() has
+** been used. Older versions of RowSet allowed that, but as the
+** capability was not used by the code generator, it was removed
+** for code economy.
*/
SQLITE_PRIVATE int sqlite3RowSetNext(RowSet *p, i64 *pRowid){
assert( p!=0 );
+ assert( p->pForest==0 ); /* Cannot be used with sqlite3RowSetText() */
/* Merge the forest into a single sorted list on first call */
- if( (p->rsFlags & ROWSET_NEXT)==0 ) rowSetToList(p);
+ if( (p->rsFlags & ROWSET_NEXT)==0 ){ /*OPTIMIZATION-IF-FALSE*/
+ if( (p->rsFlags & ROWSET_SORTED)==0 ){ /*OPTIMIZATION-IF-FALSE*/
+ p->pEntry = rowSetEntrySort(p->pEntry);
+ }
+ p->rsFlags |= ROWSET_SORTED|ROWSET_NEXT;
+ }
/* Return the next entry on the list */
if( p->pEntry ){
*pRowid = p->pEntry->v;
p->pEntry = p->pEntry->pRight;
- if( p->pEntry==0 ){
+ if( p->pEntry==0 ){ /*OPTIMIZATION-IF-TRUE*/
+ /* Free memory immediately, rather than waiting on sqlite3_finalize() */
sqlite3RowSetClear(p);
}
return 1;
@@ -41433,13 +45662,15 @@ SQLITE_PRIVATE int sqlite3RowSetTest(RowSet *pRowSet, int iBatch, sqlite3_int64
/* This routine is never called after sqlite3RowSetNext() */
assert( pRowSet!=0 && (pRowSet->rsFlags & ROWSET_NEXT)==0 );
- /* Sort entries into the forest on the first test of a new batch
+ /* Sort entries into the forest on the first test of a new batch.
+ ** To save unnecessary work, only do this when the batch number changes.
*/
- if( iBatch!=pRowSet->iBatch ){
+ if( iBatch!=pRowSet->iBatch ){ /*OPTIMIZATION-IF-FALSE*/
p = pRowSet->pEntry;
if( p ){
struct RowSetEntry **ppPrevTree = &pRowSet->pForest;
- if( (pRowSet->rsFlags & ROWSET_SORTED)==0 ){
+ if( (pRowSet->rsFlags & ROWSET_SORTED)==0 ){ /*OPTIMIZATION-IF-FALSE*/
+ /* Only sort the current set of entiries if they need it */
p = rowSetEntrySort(p);
}
for(pTree = pRowSet->pForest; pTree; pTree=pTree->pRight){
@@ -41510,6 +45741,7 @@ SQLITE_PRIVATE int sqlite3RowSetTest(RowSet *pRowSet, int iBatch, sqlite3_int64
** another is writing.
*/
#ifndef SQLITE_OMIT_DISKIO
+/* #include "sqliteInt.h" */
/************** Include wal.h in the middle of pager.c ***********************/
/************** Begin file wal.h *********************************************/
/*
@@ -41531,6 +45763,7 @@ SQLITE_PRIVATE int sqlite3RowSetTest(RowSet *pRowSet, int iBatch, sqlite3_int64
#ifndef _WAL_H_
#define _WAL_H_
+/* #include "sqliteInt.h" */
/* Additional values that can be added to the sync_flags argument of
** sqlite3WalFrames():
@@ -41557,6 +45790,7 @@ SQLITE_PRIVATE int sqlite3RowSetTest(RowSet *pRowSet, int iBatch, sqlite3_int64
# define sqlite3WalHeapMemory(z) 0
# define sqlite3WalFramesize(z) 0
# define sqlite3WalFindFrame(x,y,z) 0
+# define sqlite3WalFile(x) 0
#else
#define WAL_SAVEPOINT_NDATA 4
@@ -41639,6 +45873,11 @@ SQLITE_PRIVATE int sqlite3WalExclusiveMode(Wal *pWal, int op);
*/
SQLITE_PRIVATE int sqlite3WalHeapMemory(Wal *pWal);
+#ifdef SQLITE_ENABLE_SNAPSHOT
+SQLITE_PRIVATE int sqlite3WalSnapshotGet(Wal *pWal, sqlite3_snapshot **ppSnapshot);
+SQLITE_PRIVATE void sqlite3WalSnapshotOpen(Wal *pWal, sqlite3_snapshot *pSnapshot);
+#endif
+
#ifdef SQLITE_ENABLE_ZIPVFS
/* If the WAL file is not empty, return the number of bytes of content
** stored in each frame (i.e. the db page-size when the WAL was created).
@@ -41646,6 +45885,9 @@ SQLITE_PRIVATE int sqlite3WalHeapMemory(Wal *pWal);
SQLITE_PRIVATE int sqlite3WalFramesize(Wal *pWal);
#endif
+/* Return the sqlite3_file object for the WAL file */
+SQLITE_PRIVATE sqlite3_file *sqlite3WalFile(Wal *pWal);
+
#endif /* ifndef SQLITE_OMIT_WAL */
#endif /* _WAL_H_ */
@@ -42058,6 +46300,7 @@ int sqlite3PagerTrace=1; /* True to enable tracing */
*/
#define MAX_SECTOR_SIZE 0x10000
+
/*
** An instance of the following structure is allocated for each active
** savepoint and statement transaction in the system. All such structures
@@ -42086,9 +46329,9 @@ struct PagerSavepoint {
/*
** Bits of the Pager.doNotSpill flag. See further description below.
*/
-#define SPILLFLAG_OFF 0x01 /* Never spill cache. Set via pragma */
-#define SPILLFLAG_ROLLBACK 0x02 /* Current rolling back, so do not spill */
-#define SPILLFLAG_NOSYNC 0x04 /* Spill is ok, but do not sync */
+#define SPILLFLAG_OFF 0x01 /* Never spill cache. Set via pragma */
+#define SPILLFLAG_ROLLBACK 0x02 /* Current rolling back, so do not spill */
+#define SPILLFLAG_NOSYNC 0x04 /* Spill is ok, but do not sync */
/*
** An open page cache is an instance of struct Pager. A description of
@@ -42170,11 +46413,11 @@ struct PagerSavepoint {
** while it is being traversed by code in pager_playback(). The SPILLFLAG_OFF
** case is a user preference.
**
-** If the SPILLFLAG_NOSYNC bit is set, writing to the database from pagerStress()
-** is permitted, but syncing the journal file is not. This flag is set
-** by sqlite3PagerWrite() when the file-system sector-size is larger than
-** the database page-size in order to prevent a journal sync from happening
-** in between the journalling of two pages on the same sector.
+** If the SPILLFLAG_NOSYNC bit is set, writing to the database from
+** pagerStress() is permitted, but syncing the journal file is not.
+** This flag is set by sqlite3PagerWrite() when the file-system sector-size
+** is larger than the database page-size in order to prevent a journal sync
+** from happening in between the journalling of two pages on the same sector.
**
** subjInMemory
**
@@ -42253,6 +46496,7 @@ struct Pager {
u8 useJournal; /* Use a rollback journal on this file */
u8 noSync; /* Do not sync the journal if true */
u8 fullSync; /* Do extra syncs of the journal for robustness */
+ u8 extraSync; /* sync directory after journal delete */
u8 ckptSyncFlags; /* SYNC_NORMAL or SYNC_FULL for checkpoint */
u8 walSyncFlags; /* SYNC_NORMAL or SYNC_FULL for wal writes */
u8 syncFlags; /* SYNC_NORMAL or SYNC_FULL otherwise */
@@ -42277,7 +46521,7 @@ struct Pager {
u8 doNotSpill; /* Do not spill the cache when non-zero */
u8 subjInMemory; /* True to use in-memory sub-journals */
u8 bUseFetch; /* True to use xFetch() */
- u8 hasBeenUsed; /* True if any content previously read from this pager*/
+ u8 hasHeldSharedLock; /* True if a shared lock has ever been held */
Pgno dbSize; /* Number of pages in the database */
Pgno dbOrigSize; /* dbSize before the current transaction */
Pgno dbFileSize; /* Number of pages in the database file */
@@ -42438,7 +46682,7 @@ static const unsigned char aJournalMagic[] = {
**
** if( pPager->jfd->pMethods ){ ...
*/
-#define isOpen(pFd) ((pFd)->pMethods)
+#define isOpen(pFd) ((pFd)->pMethods!=0)
/*
** Return true if this pager uses a write-ahead log instead of the usual
@@ -42500,6 +46744,7 @@ static int assert_pager_state(Pager *p){
** state.
*/
if( MEMDB ){
+ assert( !isOpen(p->fd) );
assert( p->noSync );
assert( p->journalMode==PAGER_JOURNALMODE_OFF
|| p->journalMode==PAGER_JOURNALMODE_MEMORY
@@ -42586,7 +46831,7 @@ static int assert_pager_state(Pager *p){
** back to OPEN state.
*/
assert( pPager->errCode!=SQLITE_OK );
- assert( sqlite3PcacheRefCount(pPager->pPCache)>0 );
+ assert( sqlite3PcacheRefCount(pPager->pPCache)>0 || pPager->tempFile );
break;
}
@@ -42661,19 +46906,21 @@ static int subjRequiresPage(PgHdr *pPg){
int i;
for(i=0; i<pPager->nSavepoint; i++){
p = &pPager->aSavepoint[i];
- if( p->nOrig>=pgno && 0==sqlite3BitvecTest(p->pInSavepoint, pgno) ){
+ if( p->nOrig>=pgno && 0==sqlite3BitvecTestNotNull(p->pInSavepoint, pgno) ){
return 1;
}
}
return 0;
}
+#ifdef SQLITE_DEBUG
/*
** Return true if the page is already in the journal file.
*/
static int pageInJournal(Pager *pPager, PgHdr *pPg){
return sqlite3BitvecTest(pPager->pInJournal, pPg->pgno);
}
+#endif
/*
** Read a 32-bit integer from the given file descriptor. Store the integer
@@ -42796,6 +47043,8 @@ static int jrnlBufferSize(Pager *pPager){
return JOURNAL_HDR_SZ(pPager) + JOURNAL_PG_SZ(pPager);
}
+#else
+# define jrnlBufferSize(x) 0
#endif
/*
@@ -42956,6 +47205,7 @@ static i64 journalHdrOffset(Pager *pPager){
static int zeroJournalHdr(Pager *pPager, int doTruncate){
int rc = SQLITE_OK; /* Return code */
assert( isOpen(pPager->jfd) );
+ assert( !sqlite3JournalIsInMemory(pPager->jfd) );
if( pPager->journalOff ){
const i64 iLimit = pPager->journalSizeLimit; /* Local cache of jsl */
@@ -43285,7 +47535,8 @@ static int writeMasterJournal(Pager *pPager, const char *zMaster){
|| (0 != (rc = sqlite3OsWrite(pPager->jfd, zMaster, nMaster, iHdrOff+4)))
|| (0 != (rc = write32bits(pPager->jfd, iHdrOff+4+nMaster, nMaster)))
|| (0 != (rc = write32bits(pPager->jfd, iHdrOff+4+nMaster+4, cksum)))
- || (0 != (rc = sqlite3OsWrite(pPager->jfd, aJournalMagic, 8, iHdrOff+4+nMaster+8)))
+ || (0 != (rc = sqlite3OsWrite(pPager->jfd, aJournalMagic, 8,
+ iHdrOff+4+nMaster+8)))
){
return rc;
}
@@ -43336,7 +47587,7 @@ static void releaseAllSavepoints(Pager *pPager){
for(ii=0; ii<pPager->nSavepoint; ii++){
sqlite3BitvecDestroy(pPager->aSavepoint[ii].pInSavepoint);
}
- if( !pPager->exclusiveMode || sqlite3IsMemJournal(pPager->sjfd) ){
+ if( !pPager->exclusiveMode || sqlite3JournalIsInMemory(pPager->sjfd) ){
sqlite3OsClose(pPager->sjfd);
}
sqlite3_free(pPager->aSavepoint);
@@ -43442,13 +47693,17 @@ static void pager_unlock(Pager *pPager){
** it can safely move back to PAGER_OPEN state. This happens in both
** normal and exclusive-locking mode.
*/
+ assert( pPager->errCode==SQLITE_OK || !MEMDB );
if( pPager->errCode ){
- assert( !MEMDB );
- pager_reset(pPager);
- pPager->changeCountDone = pPager->tempFile;
- pPager->eState = PAGER_OPEN;
- pPager->errCode = SQLITE_OK;
+ if( pPager->tempFile==0 ){
+ pager_reset(pPager);
+ pPager->changeCountDone = 0;
+ pPager->eState = PAGER_OPEN;
+ }else{
+ pPager->eState = (isOpen(pPager->jfd) ? PAGER_OPEN : PAGER_READER);
+ }
if( USEFETCH(pPager) ) sqlite3OsUnfetch(pPager->fd, 0, 0);
+ pPager->errCode = SQLITE_OK;
}
pPager->journalOff = 0;
@@ -43493,6 +47748,29 @@ static int pager_error(Pager *pPager, int rc){
static int pager_truncate(Pager *pPager, Pgno nPage);
/*
+** The write transaction open on pPager is being committed (bCommit==1)
+** or rolled back (bCommit==0).
+**
+** Return TRUE if and only if all dirty pages should be flushed to disk.
+**
+** Rules:
+**
+** * For non-TEMP databases, always sync to disk. This is necessary
+** for transactions to be durable.
+**
+** * Sync TEMP database only on a COMMIT (not a ROLLBACK) when the backing
+** file has been created already (via a spill on pagerStress()) and
+** when the number of dirty pages in memory exceeds 25% of the total
+** cache size.
+*/
+static int pagerFlushOnCommit(Pager *pPager, int bCommit){
+ if( pPager->tempFile==0 ) return 1;
+ if( !bCommit ) return 0;
+ if( !isOpen(pPager->fd) ) return 0;
+ return (sqlite3PCachePercentDirty(pPager->pPCache)>=25);
+}
+
+/*
** This routine ends a transaction. A transaction is usually ended by
** either a COMMIT or a ROLLBACK operation. This routine may be called
** after rollback of a hot-journal, or if an error occurs while opening
@@ -43574,8 +47852,8 @@ static int pager_end_transaction(Pager *pPager, int hasMaster, int bCommit){
assert( !pagerUseWal(pPager) );
/* Finalize the journal file. */
- if( sqlite3IsMemJournal(pPager->jfd) ){
- assert( pPager->journalMode==PAGER_JOURNALMODE_MEMORY );
+ if( sqlite3JournalIsInMemory(pPager->jfd) ){
+ /* assert( pPager->journalMode==PAGER_JOURNALMODE_MEMORY ); */
sqlite3OsClose(pPager->jfd);
}else if( pPager->journalMode==PAGER_JOURNALMODE_TRUNCATE ){
if( pPager->journalOff==0 ){
@@ -43595,22 +47873,23 @@ static int pager_end_transaction(Pager *pPager, int hasMaster, int bCommit){
}else if( pPager->journalMode==PAGER_JOURNALMODE_PERSIST
|| (pPager->exclusiveMode && pPager->journalMode!=PAGER_JOURNALMODE_WAL)
){
- rc = zeroJournalHdr(pPager, hasMaster);
+ rc = zeroJournalHdr(pPager, hasMaster||pPager->tempFile);
pPager->journalOff = 0;
}else{
/* This branch may be executed with Pager.journalMode==MEMORY if
** a hot-journal was just rolled back. In this case the journal
** file should be closed and deleted. If this connection writes to
- ** the database file, it will do so using an in-memory journal.
+ ** the database file, it will do so using an in-memory journal.
*/
- int bDelete = (!pPager->tempFile && sqlite3JournalExists(pPager->jfd));
+ int bDelete = !pPager->tempFile;
+ assert( sqlite3JournalIsInMemory(pPager->jfd)==0 );
assert( pPager->journalMode==PAGER_JOURNALMODE_DELETE
|| pPager->journalMode==PAGER_JOURNALMODE_MEMORY
|| pPager->journalMode==PAGER_JOURNALMODE_WAL
);
sqlite3OsClose(pPager->jfd);
if( bDelete ){
- rc = sqlite3OsDelete(pPager->pVfs, pPager->zJournal, 0);
+ rc = sqlite3OsDelete(pPager->pVfs, pPager->zJournal, pPager->extraSync);
}
}
}
@@ -43629,8 +47908,14 @@ static int pager_end_transaction(Pager *pPager, int hasMaster, int bCommit){
sqlite3BitvecDestroy(pPager->pInJournal);
pPager->pInJournal = 0;
pPager->nRec = 0;
- sqlite3PcacheCleanAll(pPager->pPCache);
- sqlite3PcacheTruncate(pPager->pPCache, pPager->dbSize);
+ if( rc==SQLITE_OK ){
+ if( pagerFlushOnCommit(pPager, bCommit) ){
+ sqlite3PcacheCleanAll(pPager->pPCache);
+ }else{
+ sqlite3PcacheClearWritable(pPager->pPCache);
+ }
+ sqlite3PcacheTruncate(pPager->pPCache, pPager->dbSize);
+ }
if( pagerUseWal(pPager) ){
/* Drop the WAL write-lock, if any. Also, if the connection was in
@@ -43743,6 +48028,20 @@ static void pagerReportSize(Pager *pPager){
# define pagerReportSize(X) /* No-op if we do not support a codec */
#endif
+#ifdef SQLITE_HAS_CODEC
+/*
+** Make sure the number of reserved bits is the same in the destination
+** pager as it is in the source. This comes up when a VACUUM changes the
+** number of reserved bits to the "optimal" amount.
+*/
+SQLITE_PRIVATE void sqlite3PagerAlignReserve(Pager *pDest, Pager *pSrc){
+ if( pDest->nReserve!=pSrc->nReserve ){
+ pDest->nReserve = pSrc->nReserve;
+ pagerReportSize(pDest);
+ }
+}
+#endif
+
/*
** Read a single page from either the journal file (if isMainJrnl==1) or
** from the sub-journal (if isMainJrnl==0) and playback that page.
@@ -43845,7 +48144,7 @@ static int pager_playback_one_page(
}
}
- /* If this page has already been played by before during the current
+ /* If this page has already been played back before during the current
** rollback, then don't bother to play it back again.
*/
if( pDone && (rc = sqlite3BitvecSet(pDone, pgno))!=SQLITE_OK ){
@@ -43900,7 +48199,7 @@ static int pager_playback_one_page(
pPg = sqlite3PagerLookup(pPager, pgno);
}
assert( pPg || !MEMDB );
- assert( pPager->eState!=PAGER_OPEN || pPg==0 );
+ assert( pPager->eState!=PAGER_OPEN || pPg==0 || pPager->tempFile );
PAGERTRACE(("PLAYBACK %d page %d hash(%08x) %s\n",
PAGERID(pPager), pgno, pager_datahash(pPager->pageSize, (u8*)aData),
(isMainJrnl?"main-journal":"sub-journal")
@@ -43922,9 +48221,9 @@ static int pager_playback_one_page(
pPager->dbFileSize = pgno;
}
if( pPager->pBackup ){
- CODEC1(pPager, aData, pgno, 3, rc=SQLITE_NOMEM);
+ CODEC1(pPager, aData, pgno, 3, rc=SQLITE_NOMEM_BKPT);
sqlite3BackupUpdate(pPager->pBackup, pgno, (u8*)aData);
- CODEC2(pPager, aData, pgno, 7, rc=SQLITE_NOMEM, aData);
+ CODEC2(pPager, aData, pgno, 7, rc=SQLITE_NOMEM_BKPT, aData);
}
}else if( !isMainJrnl && pPg==0 ){
/* If this is a rollback of a savepoint and data was not written to
@@ -43946,11 +48245,10 @@ static int pager_playback_one_page(
assert( isSavepnt );
assert( (pPager->doNotSpill & SPILLFLAG_ROLLBACK)==0 );
pPager->doNotSpill |= SPILLFLAG_ROLLBACK;
- rc = sqlite3PagerAcquire(pPager, pgno, &pPg, 1);
+ rc = sqlite3PagerGet(pPager, pgno, &pPg, 1);
assert( (pPager->doNotSpill & SPILLFLAG_ROLLBACK)!=0 );
pPager->doNotSpill &= ~SPILLFLAG_ROLLBACK;
if( rc!=SQLITE_OK ) return rc;
- pPg->flags &= ~PGHDR_NEED_READ;
sqlite3PcacheMakeDirty(pPg);
}
if( pPg ){
@@ -43964,29 +48262,10 @@ static int pager_playback_one_page(
pData = pPg->pData;
memcpy(pData, (u8*)aData, pPager->pageSize);
pPager->xReiniter(pPg);
- if( isMainJrnl && (!isSavepnt || *pOffset<=pPager->journalHdr) ){
- /* If the contents of this page were just restored from the main
- ** journal file, then its content must be as they were when the
- ** transaction was first opened. In this case we can mark the page
- ** as clean, since there will be no need to write it out to the
- ** database.
- **
- ** There is one exception to this rule. If the page is being rolled
- ** back as part of a savepoint (or statement) rollback from an
- ** unsynced portion of the main journal file, then it is not safe
- ** to mark the page as clean. This is because marking the page as
- ** clean will clear the PGHDR_NEED_SYNC flag. Since the page is
- ** already in the journal file (recorded in Pager.pInJournal) and
- ** the PGHDR_NEED_SYNC flag is cleared, if the page is written to
- ** again within this transaction, it will be marked as dirty but
- ** the PGHDR_NEED_SYNC flag will not be set. It could then potentially
- ** be written out into the database file before its journal file
- ** segment is synced. If a crash occurs during or following this,
- ** database corruption may ensue.
- */
- assert( !pagerUseWal(pPager) );
- sqlite3PcacheMakeClean(pPg);
- }
+ /* It used to be that sqlite3PcacheMakeClean(pPg) was called here. But
+ ** that call was dangerous and had no detectable benefit since the cache
+ ** is normally cleaned by sqlite3PcacheCleanAll() after rollback and so
+ ** has been removed. */
pager_set_pagehash(pPg);
/* If this was page 1, then restore the value of Pager.dbFileVers.
@@ -43996,7 +48275,7 @@ static int pager_playback_one_page(
}
/* Decode the page just read from disk */
- CODEC1(pPager, pData, pPg->pgno, 3, rc=SQLITE_NOMEM);
+ CODEC1(pPager, pData, pPg->pgno, 3, rc=SQLITE_NOMEM_BKPT);
sqlite3PcacheRelease(pPg);
}
return rc;
@@ -44062,7 +48341,7 @@ static int pager_delmaster(Pager *pPager, const char *zMaster){
pMaster = (sqlite3_file *)sqlite3MallocZero(pVfs->szOsFile * 2);
pJournal = (sqlite3_file *)(((u8 *)pMaster) + pVfs->szOsFile);
if( !pMaster ){
- rc = SQLITE_NOMEM;
+ rc = SQLITE_NOMEM_BKPT;
}else{
const int flags = (SQLITE_OPEN_READONLY|SQLITE_OPEN_MASTER_JOURNAL);
rc = sqlite3OsOpen(pVfs, zMaster, pMaster, flags, 0);
@@ -44079,7 +48358,7 @@ static int pager_delmaster(Pager *pPager, const char *zMaster){
nMasterPtr = pVfs->mxPathname+1;
zMasterJournal = sqlite3Malloc(nMasterJournal + nMasterPtr + 1);
if( !zMasterJournal ){
- rc = SQLITE_NOMEM;
+ rc = SQLITE_NOMEM_BKPT;
goto delmaster_out;
}
zMasterPtr = &zMasterJournal[nMasterJournal+1];
@@ -44327,7 +48606,7 @@ static int pager_playback(Pager *pPager, int isHot){
** TODO: Technically the following is an error because it assumes that
** buffer Pager.pTmpSpace is (mxPathname+1) bytes or larger. i.e. that
** (pPager->pageSize >= pPager->pVfs->mxPathname+1). Using os_unix.c,
- ** mxPathname is 512, which is the same as the minimum allowable value
+ ** mxPathname is 512, which is the same as the minimum allowable value
** for pageSize.
*/
zMaster = pPager->pTmpSpace;
@@ -44549,7 +48828,7 @@ static int readDbPage(PgHdr *pPg, u32 iFrame){
memcpy(&pPager->dbFileVers, dbFileVers, sizeof(pPager->dbFileVers));
}
}
- CODEC1(pPager, pPg->pData, pgno, 3, rc = SQLITE_NOMEM);
+ CODEC1(pPager, pPg->pData, pgno, 3, rc = SQLITE_NOMEM_BKPT);
PAGER_INCR(sqlite3_pager_readdb_count);
PAGER_INCR(pPager->nRead);
@@ -44777,22 +49056,20 @@ static int pagerPagecount(Pager *pPager, Pgno *pnPage){
*/
assert( pPager->eState==PAGER_OPEN );
assert( pPager->eLock>=SHARED_LOCK );
+ assert( isOpen(pPager->fd) );
+ assert( pPager->tempFile==0 );
nPage = sqlite3WalDbsize(pPager->pWal);
- /* If the database size was not available from the WAL sub-system,
- ** determine it based on the size of the database file. If the size
- ** of the database file is not an integer multiple of the page-size,
- ** round down to the nearest page. Except, any file larger than 0
- ** bytes in size is considered to contain at least one page.
+ /* If the number of pages in the database is not available from the
+ ** WAL sub-system, determine the page counte based on the size of
+ ** the database file. If the size of the database file is not an
+ ** integer multiple of the page-size, round up the result.
*/
- if( nPage==0 ){
+ if( nPage==0 && ALWAYS(isOpen(pPager->fd)) ){
i64 n = 0; /* Size of db file in bytes */
- assert( isOpen(pPager->fd) || pPager->tempFile );
- if( isOpen(pPager->fd) ){
- int rc = sqlite3OsFileSize(pPager->fd, &n);
- if( rc!=SQLITE_OK ){
- return rc;
- }
+ int rc = sqlite3OsFileSize(pPager->fd, &n);
+ if( rc!=SQLITE_OK ){
+ return rc;
}
nPage = (Pgno)((n+pPager->pageSize-1) / pPager->pageSize);
}
@@ -44910,7 +49187,7 @@ static int pagerPlaybackSavepoint(Pager *pPager, PagerSavepoint *pSavepoint){
if( pSavepoint ){
pDone = sqlite3BitvecCreate(pSavepoint->nOrig);
if( !pDone ){
- return SQLITE_NOMEM;
+ return SQLITE_NOMEM_BKPT;
}
}
@@ -45006,13 +49283,22 @@ static int pagerPlaybackSavepoint(Pager *pPager, PagerSavepoint *pSavepoint){
}
/*
-** Change the maximum number of in-memory pages that are allowed.
+** Change the maximum number of in-memory pages that are allowed
+** before attempting to recycle clean and unused pages.
*/
SQLITE_PRIVATE void sqlite3PagerSetCachesize(Pager *pPager, int mxPage){
sqlite3PcacheSetCachesize(pPager->pPCache, mxPage);
}
/*
+** Change the maximum number of in-memory pages that are allowed
+** before attempting to spill pages to journal.
+*/
+SQLITE_PRIVATE int sqlite3PagerSetSpillsize(Pager *pPager, int mxPage){
+ return sqlite3PcacheSetSpillsize(pPager->pPCache, mxPage);
+}
+
+/*
** Invoke SQLITE_FCNTL_MMAP_SIZE based on the current value of szMmap.
*/
static void pagerFixMaplimit(Pager *pPager){
@@ -45048,7 +49334,7 @@ SQLITE_PRIVATE void sqlite3PagerShrink(Pager *pPager){
** The "level" in pgFlags & PAGER_SYNCHRONOUS_MASK sets the robustness
** of the database to damage due to OS crashes or power failures by
** changing the number of syncs()s when writing the journals.
-** There are three levels:
+** There are four levels:
**
** OFF sqlite3OsSync() is never called. This is the default
** for temporary and transient files.
@@ -45068,6 +49354,10 @@ SQLITE_PRIVATE void sqlite3PagerShrink(Pager *pPager){
** assurance that the journal will not be corrupted to the
** point of causing damage to the database during rollback.
**
+** EXTRA This is like FULL except that is also syncs the directory
+** that contains the rollback journal after the rollback
+** journal is unlinked.
+**
** The above is for a rollback-journal mode. For WAL mode, OFF continues
** to mean that no syncs ever occur. NORMAL means that the WAL is synced
** prior to the start of checkpoint and that the database file is synced
@@ -45075,7 +49365,8 @@ SQLITE_PRIVATE void sqlite3PagerShrink(Pager *pPager){
** was written back into the database. But no sync operations occur for
** an ordinary commit in NORMAL mode with WAL. FULL means that the WAL
** file is synced following each commit operation, in addition to the
-** syncs associated with NORMAL.
+** syncs associated with NORMAL. There is no difference between FULL
+** and EXTRA for WAL mode.
**
** Do not confuse synchronous=FULL with SQLITE_SYNC_FULL. The
** SQLITE_SYNC_FULL macro means to use the MacOSX-style full-fsync
@@ -45094,9 +49385,15 @@ SQLITE_PRIVATE void sqlite3PagerSetFlags(
unsigned pgFlags /* Various flags */
){
unsigned level = pgFlags & PAGER_SYNCHRONOUS_MASK;
- assert( level>=1 && level<=3 );
- pPager->noSync = (level==1 || pPager->tempFile) ?1:0;
- pPager->fullSync = (level==3 && !pPager->tempFile) ?1:0;
+ if( pPager->tempFile ){
+ pPager->noSync = 1;
+ pPager->fullSync = 0;
+ pPager->extraSync = 0;
+ }else{
+ pPager->noSync = level==PAGER_SYNCHRONOUS_OFF ?1:0;
+ pPager->fullSync = level>=PAGER_SYNCHRONOUS_FULL ?1:0;
+ pPager->extraSync = level==PAGER_SYNCHRONOUS_EXTRA ?1:0;
+ }
if( pPager->noSync ){
pPager->syncFlags = 0;
pPager->ckptSyncFlags = 0;
@@ -45258,7 +49555,7 @@ SQLITE_PRIVATE int sqlite3PagerSetPagesize(Pager *pPager, u32 *pPageSize, int nR
}
if( rc==SQLITE_OK ){
pNew = (char *)sqlite3PageMalloc(pageSize);
- if( !pNew ) rc = SQLITE_NOMEM;
+ if( !pNew ) rc = SQLITE_NOMEM_BKPT;
}
if( rc==SQLITE_OK ){
@@ -45534,7 +49831,7 @@ static int pagerAcquireMapPage(
*ppPage = p = (PgHdr *)sqlite3MallocZero(sizeof(PgHdr) + pPager->nExtra);
if( p==0 ){
sqlite3OsUnfetch(pPager->fd, (i64)(pgno-1) * pPager->pageSize, pData);
- return SQLITE_NOMEM;
+ return SQLITE_NOMEM_BKPT;
}
p->pExtra = (void *)&p[1];
p->flags = PGHDR_MMAP;
@@ -45848,8 +50145,9 @@ static int pager_write_pagelist(Pager *pPager, PgHdr *pList){
/* This function is only called for rollback pagers in WRITER_DBMOD state. */
assert( !pagerUseWal(pPager) );
- assert( pPager->eState==PAGER_WRITER_DBMOD );
+ assert( pPager->tempFile || pPager->eState==PAGER_WRITER_DBMOD );
assert( pPager->eLock==EXCLUSIVE_LOCK );
+ assert( isOpen(pPager->fd) || pList->pDirty==0 );
/* If the file is a temp-file has not yet been opened, open it now. It
** is not possible for rc to be other than SQLITE_OK if this branch
@@ -45892,7 +50190,7 @@ static int pager_write_pagelist(Pager *pPager, PgHdr *pList){
if( pList->pgno==1 ) pager_write_changecounter(pList);
/* Encode the database */
- CODEC2(pPager, pList->pData, pgno, 6, return SQLITE_NOMEM, pData);
+ CODEC2(pPager, pList->pData, pgno, 6, return SQLITE_NOMEM_BKPT, pData);
/* Write out the page data. */
rc = sqlite3OsWrite(pPager->fd, pData, pPager->pageSize, offset);
@@ -45937,19 +50235,20 @@ static int pager_write_pagelist(Pager *pPager, PgHdr *pList){
static int openSubJournal(Pager *pPager){
int rc = SQLITE_OK;
if( !isOpen(pPager->sjfd) ){
+ const int flags = SQLITE_OPEN_SUBJOURNAL | SQLITE_OPEN_READWRITE
+ | SQLITE_OPEN_CREATE | SQLITE_OPEN_EXCLUSIVE
+ | SQLITE_OPEN_DELETEONCLOSE;
+ int nStmtSpill = sqlite3Config.nStmtSpill;
if( pPager->journalMode==PAGER_JOURNALMODE_MEMORY || pPager->subjInMemory ){
- sqlite3MemJournalOpen(pPager->sjfd);
- }else{
- rc = pagerOpentemp(pPager, pPager->sjfd, SQLITE_OPEN_SUBJOURNAL);
+ nStmtSpill = -1;
}
+ rc = sqlite3JournalOpen(pPager->pVfs, 0, pPager->sjfd, flags, nStmtSpill);
}
return rc;
}
/*
** Append a record of the current state of page pPg to the sub-journal.
-** It is the callers responsibility to use subjRequiresPage() to check
-** that it is really required before calling this function.
**
** If successful, set the bit corresponding to pPg->pgno in the bitvecs
** for all open savepoints before returning.
@@ -45981,7 +50280,7 @@ static int subjournalPage(PgHdr *pPg){
i64 offset = (i64)pPager->nSubRec*(4+pPager->pageSize);
char *pData2;
- CODEC2(pPager, pData, pPg->pgno, 7, return SQLITE_NOMEM, pData2);
+ CODEC2(pPager, pData, pPg->pgno, 7, return SQLITE_NOMEM_BKPT, pData2);
PAGERTRACE(("STMT-JOURNAL %d page %d\n", PAGERID(pPager), pPg->pgno));
rc = write32bits(pPager->sjfd, offset, pPg->pgno);
if( rc==SQLITE_OK ){
@@ -45996,6 +50295,13 @@ static int subjournalPage(PgHdr *pPg){
}
return rc;
}
+static int subjournalPageIfRequired(PgHdr *pPg){
+ if( subjRequiresPage(pPg) ){
+ return subjournalPage(pPg);
+ }else{
+ return SQLITE_OK;
+ }
+}
/*
** This function is called by the pcache layer when it has reached some
@@ -46053,9 +50359,7 @@ static int pagerStress(void *p, PgHdr *pPg){
pPg->pDirty = 0;
if( pagerUseWal(pPager) ){
/* Write a single frame for this page to the log. */
- if( subjRequiresPage(pPg) ){
- rc = subjournalPage(pPg);
- }
+ rc = subjournalPageIfRequired(pPg);
if( rc==SQLITE_OK ){
rc = pagerWalFrames(pPager, pPg, 0, 0);
}
@@ -46068,39 +50372,6 @@ static int pagerStress(void *p, PgHdr *pPg){
rc = syncJournal(pPager, 1);
}
- /* If the page number of this page is larger than the current size of
- ** the database image, it may need to be written to the sub-journal.
- ** This is because the call to pager_write_pagelist() below will not
- ** actually write data to the file in this case.
- **
- ** Consider the following sequence of events:
- **
- ** BEGIN;
- ** <journal page X>
- ** <modify page X>
- ** SAVEPOINT sp;
- ** <shrink database file to Y pages>
- ** pagerStress(page X)
- ** ROLLBACK TO sp;
- **
- ** If (X>Y), then when pagerStress is called page X will not be written
- ** out to the database file, but will be dropped from the cache. Then,
- ** following the "ROLLBACK TO sp" statement, reading page X will read
- ** data from the database file. This will be the copy of page X as it
- ** was when the transaction started, not as it was when "SAVEPOINT sp"
- ** was executed.
- **
- ** The solution is to write the current data for page X into the
- ** sub-journal file now (if it is not already there), so that it will
- ** be restored to its current value when the "ROLLBACK TO sp" is
- ** executed.
- */
- if( NEVER(
- rc==SQLITE_OK && pPg->pgno>pPager->dbSize && subjRequiresPage(pPg)
- ) ){
- rc = subjournalPage(pPg);
- }
-
/* Write the contents of the page out to the database file. */
if( rc==SQLITE_OK ){
assert( (pPg->flags&PGHDR_NEED_SYNC)==0 );
@@ -46117,6 +50388,25 @@ static int pagerStress(void *p, PgHdr *pPg){
return pager_error(pPager, rc);
}
+/*
+** Flush all unreferenced dirty pages to disk.
+*/
+SQLITE_PRIVATE int sqlite3PagerFlush(Pager *pPager){
+ int rc = pPager->errCode;
+ if( !MEMDB ){
+ PgHdr *pList = sqlite3PcacheDirtyList(pPager->pPCache);
+ assert( assert_pager_state(pPager) );
+ while( rc==SQLITE_OK && pList ){
+ PgHdr *pNext = pList->pDirty;
+ if( pList->nRef==0 ){
+ rc = pagerStress((void*)pPager, pList);
+ }
+ pList = pNext;
+ }
+ }
+
+ return rc;
+}
/*
** Allocate and initialize a new Pager object and put a pointer to it
@@ -46173,18 +50463,8 @@ SQLITE_PRIVATE int sqlite3PagerOpen(
int nUri = 0; /* Number of bytes of URI args at *zUri */
/* Figure out how much space is required for each journal file-handle
- ** (there are two of them, the main journal and the sub-journal). This
- ** is the maximum space required for an in-memory journal file handle
- ** and a regular journal file-handle. Note that a "regular journal-handle"
- ** may be a wrapper capable of caching the first portion of the journal
- ** file in memory to implement the atomic-write optimization (see
- ** source file journal.c).
- */
- if( sqlite3JournalSize(pVfs)>sqlite3MemJournalSize() ){
- journalFileSize = ROUND8(sqlite3JournalSize(pVfs));
- }else{
- journalFileSize = ROUND8(sqlite3MemJournalSize());
- }
+ ** (there are two of them, the main journal and the sub-journal). */
+ journalFileSize = ROUND8(sqlite3JournalSize(pVfs));
/* Set the output variable to NULL in case an error occurs. */
*ppPager = 0;
@@ -46194,7 +50474,7 @@ SQLITE_PRIVATE int sqlite3PagerOpen(
memDb = 1;
if( zFilename && zFilename[0] ){
zPathname = sqlite3DbStrDup(0, zFilename);
- if( zPathname==0 ) return SQLITE_NOMEM;
+ if( zPathname==0 ) return SQLITE_NOMEM_BKPT;
nPathname = sqlite3Strlen30(zPathname);
zFilename = 0;
}
@@ -46210,7 +50490,7 @@ SQLITE_PRIVATE int sqlite3PagerOpen(
nPathname = pVfs->mxPathname+1;
zPathname = sqlite3DbMallocRaw(0, nPathname*2);
if( zPathname==0 ){
- return SQLITE_NOMEM;
+ return SQLITE_NOMEM_BKPT;
}
zPathname[0] = 0; /* Make sure initialized even if FullPathname() fails */
rc = sqlite3OsFullPathname(pVfs, zFilename, nPathname, zPathname);
@@ -46263,7 +50543,7 @@ SQLITE_PRIVATE int sqlite3PagerOpen(
assert( EIGHT_BYTE_ALIGNMENT(SQLITE_INT_TO_PTR(journalFileSize)) );
if( !pPtr ){
sqlite3DbFree(0, zPathname);
- return SQLITE_NOMEM;
+ return SQLITE_NOMEM_BKPT;
}
pPager = (Pager*)(pPtr);
pPager->pPCache = (PCache*)(pPtr += ROUND8(sizeof(*pPager)));
@@ -46356,7 +50636,7 @@ SQLITE_PRIVATE int sqlite3PagerOpen(
act_like_temp_file:
tempFile = 1;
pPager->eState = PAGER_READER; /* Pretend we already have a lock */
- pPager->eLock = EXCLUSIVE_LOCK; /* Pretend we are in EXCLUSIVE locking mode */
+ pPager->eLock = EXCLUSIVE_LOCK; /* Pretend we are in EXCLUSIVE mode */
pPager->noLock = 1; /* Do no locking */
readOnly = (vfsFlags&SQLITE_OPEN_READONLY);
}
@@ -46375,7 +50655,7 @@ act_like_temp_file:
assert( nExtra<1000 );
nExtra = ROUND8(nExtra);
rc = sqlite3PcacheOpen(szPageDflt, nExtra, !memDb,
- !memDb?pagerStress:0, (void *)pPager, pPager->pPCache);
+ !memDb?pagerStress:0, (void *)pPager, pPager->pPCache);
}
/* If an error occurred above, free the Pager structure and close the file.
@@ -46412,11 +50692,13 @@ act_like_temp_file:
pPager->noSync = pPager->tempFile;
if( pPager->noSync ){
assert( pPager->fullSync==0 );
+ assert( pPager->extraSync==0 );
assert( pPager->syncFlags==0 );
assert( pPager->walSyncFlags==0 );
assert( pPager->ckptSyncFlags==0 );
}else{
pPager->fullSync = 1;
+ pPager->extraSync = 0;
pPager->syncFlags = SQLITE_SYNC_NORMAL;
pPager->walSyncFlags = SQLITE_SYNC_NORMAL | WAL_SYNC_TRANSACTIONS;
pPager->ckptSyncFlags = SQLITE_SYNC_NORMAL;
@@ -46533,6 +50815,7 @@ static int hasHotJournal(Pager *pPager, int *pExists){
if( rc==SQLITE_OK && !locked ){
Pgno nPage; /* Number of pages in database file */
+ assert( pPager->tempFile==0 );
rc = pagerPagecount(pPager, &nPage);
if( rc==SQLITE_OK ){
/* If the database is zero pages in size, that means that either (1) the
@@ -46594,7 +50877,7 @@ static int hasHotJournal(Pager *pPager, int *pExists){
/*
** This function is called to obtain a shared lock on the database file.
-** It is illegal to call sqlite3PagerAcquire() until after this function
+** It is illegal to call sqlite3PagerGet() until after this function
** has been successfully called. If a shared-lock is already held when
** this function is called, it is a no-op.
**
@@ -46625,17 +50908,17 @@ SQLITE_PRIVATE int sqlite3PagerSharedLock(Pager *pPager){
/* This routine is only called from b-tree and only when there are no
** outstanding pages. This implies that the pager state should either
** be OPEN or READER. READER is only possible if the pager is or was in
- ** exclusive access mode.
- */
+ ** exclusive access mode. */
assert( sqlite3PcacheRefCount(pPager->pPCache)==0 );
assert( assert_pager_state(pPager) );
assert( pPager->eState==PAGER_OPEN || pPager->eState==PAGER_READER );
- if( NEVER(MEMDB && pPager->errCode) ){ return pPager->errCode; }
+ assert( pPager->errCode==SQLITE_OK );
if( !pagerUseWal(pPager) && pPager->eState==PAGER_OPEN ){
int bHotJournal = 1; /* True if there exists a hot journal-file */
assert( !MEMDB );
+ assert( pPager->tempFile==0 || pPager->eLock==EXCLUSIVE_LOCK );
rc = pager_wait_on_lock(pPager, SHARED_LOCK);
if( rc!=SQLITE_OK ){
@@ -46721,7 +51004,7 @@ SQLITE_PRIVATE int sqlite3PagerSharedLock(Pager *pPager){
assert( rc==SQLITE_OK );
rc = pagerSyncHotJournal(pPager);
if( rc==SQLITE_OK ){
- rc = pager_playback(pPager, 1);
+ rc = pager_playback(pPager, !pPager->tempFile);
pPager->eState = PAGER_OPEN;
}
}else if( !pPager->exclusiveMode ){
@@ -46755,14 +51038,14 @@ SQLITE_PRIVATE int sqlite3PagerSharedLock(Pager *pPager){
);
}
- if( !pPager->tempFile && pPager->hasBeenUsed ){
+ if( !pPager->tempFile && pPager->hasHeldSharedLock ){
/* The shared-lock has just been acquired then check to
** see if the database has been modified. If the database has changed,
- ** flush the cache. The pPager->hasBeenUsed flag prevents this from
+ ** flush the cache. The hasHeldSharedLock flag prevents this from
** occurring on the very first access to a file, in order to save a
** single unnecessary sqlite3OsRead() call at the start-up.
**
- ** Database changes is detected by looking at 15 bytes beginning
+ ** Database changes are detected by looking at 15 bytes beginning
** at offset 24 into the file. The first 4 of these 16 bytes are
** a 32-bit counter that is incremented with each change. The
** other bytes change randomly with each file change when
@@ -46817,7 +51100,7 @@ SQLITE_PRIVATE int sqlite3PagerSharedLock(Pager *pPager){
rc = pagerBeginReadTransaction(pPager);
}
- if( pPager->eState==PAGER_OPEN && rc==SQLITE_OK ){
+ if( pPager->tempFile==0 && pPager->eState==PAGER_OPEN && rc==SQLITE_OK ){
rc = pagerPagecount(pPager, &pPager->dbSize);
}
@@ -46828,6 +51111,7 @@ SQLITE_PRIVATE int sqlite3PagerSharedLock(Pager *pPager){
assert( pPager->eState==PAGER_OPEN );
}else{
pPager->eState = PAGER_READER;
+ pPager->hasHeldSharedLock = 1;
}
return rc;
}
@@ -46896,7 +51180,7 @@ static void pagerUnlockIfUnused(Pager *pPager){
** Since Lookup() never goes to disk, it never has to deal with locks
** or journal files.
*/
-SQLITE_PRIVATE int sqlite3PagerAcquire(
+SQLITE_PRIVATE int sqlite3PagerGet(
Pager *pPager, /* The pager open on the database file */
Pgno pgno, /* Page number to fetch */
DbPage **ppPage, /* Write a pointer to the page here */
@@ -46911,21 +51195,25 @@ SQLITE_PRIVATE int sqlite3PagerAcquire(
** page 1 if there is no write-transaction open or the ACQUIRE_READONLY
** flag was specified by the caller. And so long as the db is not a
** temporary or in-memory database. */
- const int bMmapOk = (pgno!=1 && USEFETCH(pPager)
+ const int bMmapOk = (pgno>1 && USEFETCH(pPager)
&& (pPager->eState==PAGER_READER || (flags & PAGER_GET_READONLY))
#ifdef SQLITE_HAS_CODEC
&& pPager->xCodec==0
#endif
);
+ /* Optimization note: Adding the "pgno<=1" term before "pgno==0" here
+ ** allows the compiler optimizer to reuse the results of the "pgno>1"
+ ** test in the previous statement, and avoid testing pgno==0 in the
+ ** common case where pgno is large. */
+ if( pgno<=1 && pgno==0 ){
+ return SQLITE_CORRUPT_BKPT;
+ }
assert( pPager->eState>=PAGER_READER );
assert( assert_pager_state(pPager) );
assert( noContent==0 || bMmapOk==0 );
- if( pgno==0 ){
- return SQLITE_CORRUPT_BKPT;
- }
- pPager->hasBeenUsed = 1;
+ assert( pPager->hasHeldSharedLock==1 );
/* If the pager is in the error state, return an error immediately.
** Otherwise, request the page from the PCache layer. */
@@ -46945,7 +51233,7 @@ SQLITE_PRIVATE int sqlite3PagerAcquire(
);
if( rc==SQLITE_OK && pData ){
- if( pPager->eState>PAGER_READER ){
+ if( pPager->eState>PAGER_READER || pPager->tempFile ){
pPg = sqlite3PagerLookup(pPager, pgno);
}
if( pPg==0 ){
@@ -46970,9 +51258,14 @@ SQLITE_PRIVATE int sqlite3PagerAcquire(
if( pBase==0 ){
rc = sqlite3PcacheFetchStress(pPager->pPCache, pgno, &pBase);
if( rc!=SQLITE_OK ) goto pager_acquire_err;
+ if( pBase==0 ){
+ pPg = *ppPage = 0;
+ rc = SQLITE_NOMEM_BKPT;
+ goto pager_acquire_err;
+ }
}
pPg = *ppPage = sqlite3PcacheFetchFinish(pPager->pPCache, pgno, pBase);
- if( pPg==0 ) rc = SQLITE_NOMEM;
+ assert( pPg!=0 );
}
}
@@ -46983,10 +51276,11 @@ SQLITE_PRIVATE int sqlite3PagerAcquire(
pPg = 0;
goto pager_acquire_err;
}
- assert( (*ppPage)->pgno==pgno );
- assert( (*ppPage)->pPager==pPager || (*ppPage)->pPager==0 );
+ assert( pPg==(*ppPage) );
+ assert( pPg->pgno==pgno );
+ assert( pPg->pPager==pPager || pPg->pPager==0 );
- if( (*ppPage)->pPager && !noContent ){
+ if( pPg->pPager && !noContent ){
/* In this case the pcache already contains an initialized copy of
** the page. Return without further ado. */
assert( pgno<=PAGER_MAX_PGNO && pgno!=PAGER_MJ_PGNO(pPager) );
@@ -46997,7 +51291,6 @@ SQLITE_PRIVATE int sqlite3PagerAcquire(
/* The pager cache has created a new page. Its content needs to
** be initialized. */
- pPg = *ppPage;
pPg->pPager = pPager;
/* The maximum page number is 2^31. Return SQLITE_CORRUPT if a page
@@ -47007,7 +51300,8 @@ SQLITE_PRIVATE int sqlite3PagerAcquire(
goto pager_acquire_err;
}
- if( MEMDB || pPager->dbSize<pgno || noContent || !isOpen(pPager->fd) ){
+ assert( !isOpen(pPager->fd) || !MEMDB );
+ if( !isOpen(pPager->fd) || pPager->dbSize<pgno || noContent ){
if( pgno>pPager->mxPgno ){
rc = SQLITE_FULL;
goto pager_acquire_err;
@@ -47075,7 +51369,8 @@ SQLITE_PRIVATE DbPage *sqlite3PagerLookup(Pager *pPager, Pgno pgno){
assert( pgno!=0 );
assert( pPager->pPCache!=0 );
pPage = sqlite3PcacheFetch(pPager->pPCache, pgno, 0);
- assert( pPage==0 || pPager->hasBeenUsed );
+ assert( pPage==0 || pPager->hasHeldSharedLock );
+ if( pPage==0 ) return 0;
return sqlite3PcacheFetchFinish(pPager->pPCache, pgno, pPage);
}
@@ -47140,7 +51435,7 @@ static int pager_open_journal(Pager *pPager){
if( !pagerUseWal(pPager) && pPager->journalMode!=PAGER_JOURNALMODE_OFF ){
pPager->pInJournal = sqlite3BitvecCreate(pPager->dbSize);
if( pPager->pInJournal==0 ){
- return SQLITE_NOMEM;
+ return SQLITE_NOMEM_BKPT;
}
/* Open the journal file if it is not already open. */
@@ -47148,24 +51443,24 @@ static int pager_open_journal(Pager *pPager){
if( pPager->journalMode==PAGER_JOURNALMODE_MEMORY ){
sqlite3MemJournalOpen(pPager->jfd);
}else{
- const int flags = /* VFS flags to open journal file */
- SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE|
- (pPager->tempFile ?
- (SQLITE_OPEN_DELETEONCLOSE|SQLITE_OPEN_TEMP_JOURNAL):
- (SQLITE_OPEN_MAIN_JOURNAL)
- );
+ int flags = SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE;
+ int nSpill;
+ if( pPager->tempFile ){
+ flags |= (SQLITE_OPEN_DELETEONCLOSE|SQLITE_OPEN_TEMP_JOURNAL);
+ nSpill = sqlite3Config.nStmtSpill;
+ }else{
+ flags |= SQLITE_OPEN_MAIN_JOURNAL;
+ nSpill = jrnlBufferSize(pPager);
+ }
+
/* Verify that the database still has the same name as it did when
** it was originally opened. */
rc = databaseIsUnmoved(pPager);
if( rc==SQLITE_OK ){
-#ifdef SQLITE_ENABLE_ATOMIC_WRITE
- rc = sqlite3JournalOpen(
- pVfs, pPager->zJournal, pPager->jfd, flags, jrnlBufferSize(pPager)
+ rc = sqlite3JournalOpen (
+ pVfs, pPager->zJournal, pPager->jfd, flags, nSpill
);
-#else
- rc = sqlite3OsOpen(pVfs, pPager->zJournal, pPager->jfd, flags, 0);
-#endif
}
}
assert( rc!=SQLITE_OK || isOpen(pPager->jfd) );
@@ -47232,7 +51527,7 @@ SQLITE_PRIVATE int sqlite3PagerBegin(Pager *pPager, int exFlag, int subjInMemory
if( rc!=SQLITE_OK ){
return rc;
}
- sqlite3WalExclusiveMode(pPager->pWal, 1);
+ (void)sqlite3WalExclusiveMode(pPager->pWal, 1);
}
/* Grab the write lock on the log file. If successful, upgrade to
@@ -47280,6 +51575,59 @@ SQLITE_PRIVATE int sqlite3PagerBegin(Pager *pPager, int exFlag, int subjInMemory
}
/*
+** Write page pPg onto the end of the rollback journal.
+*/
+static SQLITE_NOINLINE int pagerAddPageToRollbackJournal(PgHdr *pPg){
+ Pager *pPager = pPg->pPager;
+ int rc;
+ u32 cksum;
+ char *pData2;
+ i64 iOff = pPager->journalOff;
+
+ /* We should never write to the journal file the page that
+ ** contains the database locks. The following assert verifies
+ ** that we do not. */
+ assert( pPg->pgno!=PAGER_MJ_PGNO(pPager) );
+
+ assert( pPager->journalHdr<=pPager->journalOff );
+ CODEC2(pPager, pPg->pData, pPg->pgno, 7, return SQLITE_NOMEM_BKPT, pData2);
+ cksum = pager_cksum(pPager, (u8*)pData2);
+
+ /* Even if an IO or diskfull error occurs while journalling the
+ ** page in the block above, set the need-sync flag for the page.
+ ** Otherwise, when the transaction is rolled back, the logic in
+ ** playback_one_page() will think that the page needs to be restored
+ ** in the database file. And if an IO error occurs while doing so,
+ ** then corruption may follow.
+ */
+ pPg->flags |= PGHDR_NEED_SYNC;
+
+ rc = write32bits(pPager->jfd, iOff, pPg->pgno);
+ if( rc!=SQLITE_OK ) return rc;
+ rc = sqlite3OsWrite(pPager->jfd, pData2, pPager->pageSize, iOff+4);
+ if( rc!=SQLITE_OK ) return rc;
+ rc = write32bits(pPager->jfd, iOff+pPager->pageSize+4, cksum);
+ if( rc!=SQLITE_OK ) return rc;
+
+ IOTRACE(("JOUT %p %d %lld %d\n", pPager, pPg->pgno,
+ pPager->journalOff, pPager->pageSize));
+ PAGER_INCR(sqlite3_pager_writej_count);
+ PAGERTRACE(("JOURNAL %d page %d needSync=%d hash(%08x)\n",
+ PAGERID(pPager), pPg->pgno,
+ ((pPg->flags&PGHDR_NEED_SYNC)?1:0), pager_pagehash(pPg)));
+
+ pPager->journalOff += 8 + pPager->pageSize;
+ pPager->nRec++;
+ assert( pPager->pInJournal!=0 );
+ rc = sqlite3BitvecSet(pPager->pInJournal, pPg->pgno);
+ testcase( rc==SQLITE_NOMEM );
+ assert( rc==SQLITE_OK || rc==SQLITE_NOMEM );
+ rc |= addToSavepointBitvecs(pPager, pPg->pgno);
+ assert( rc==SQLITE_OK || rc==SQLITE_NOMEM );
+ return rc;
+}
+
+/*
** Mark a single data page as writeable. The page is written into the
** main journal or sub-journal as required. If the page is written into
** one of the journals, the corresponding bit is set in the
@@ -47289,7 +51637,6 @@ SQLITE_PRIVATE int sqlite3PagerBegin(Pager *pPager, int exFlag, int subjInMemory
static int pager_write(PgHdr *pPg){
Pager *pPager = pPg->pPager;
int rc = SQLITE_OK;
- int inJournal;
/* This routine is not called unless a write-transaction has already
** been started. The journal file may or may not be open at this point.
@@ -47302,7 +51649,6 @@ static int pager_write(PgHdr *pPg){
assert( assert_pager_state(pPager) );
assert( pPager->errCode==0 );
assert( pPager->readOnly==0 );
-
CHECK_PAGE(pPg);
/* The journal file needs to be opened. Higher level routines have already
@@ -47321,91 +51667,48 @@ static int pager_write(PgHdr *pPg){
assert( pPager->eState>=PAGER_WRITER_CACHEMOD );
assert( assert_pager_state(pPager) );
- /* Mark the page as dirty. If the page has already been written
- ** to the journal then we can return right away.
- */
+ /* Mark the page that is about to be modified as dirty. */
sqlite3PcacheMakeDirty(pPg);
- inJournal = pageInJournal(pPager, pPg);
- if( inJournal && (pPager->nSavepoint==0 || !subjRequiresPage(pPg)) ){
- assert( !pagerUseWal(pPager) );
- }else{
-
- /* The transaction journal now exists and we have a RESERVED or an
- ** EXCLUSIVE lock on the main database file. Write the current page to
- ** the transaction journal if it is not there already.
- */
- if( !inJournal && !pagerUseWal(pPager) ){
- assert( pagerUseWal(pPager)==0 );
- if( pPg->pgno<=pPager->dbOrigSize && isOpen(pPager->jfd) ){
- u32 cksum;
- char *pData2;
- i64 iOff = pPager->journalOff;
-
- /* We should never write to the journal file the page that
- ** contains the database locks. The following assert verifies
- ** that we do not. */
- assert( pPg->pgno!=PAGER_MJ_PGNO(pPager) );
-
- assert( pPager->journalHdr<=pPager->journalOff );
- CODEC2(pPager, pPg->pData, pPg->pgno, 7, return SQLITE_NOMEM, pData2);
- cksum = pager_cksum(pPager, (u8*)pData2);
-
- /* Even if an IO or diskfull error occurs while journalling the
- ** page in the block above, set the need-sync flag for the page.
- ** Otherwise, when the transaction is rolled back, the logic in
- ** playback_one_page() will think that the page needs to be restored
- ** in the database file. And if an IO error occurs while doing so,
- ** then corruption may follow.
- */
- pPg->flags |= PGHDR_NEED_SYNC;
-
- rc = write32bits(pPager->jfd, iOff, pPg->pgno);
- if( rc!=SQLITE_OK ) return rc;
- rc = sqlite3OsWrite(pPager->jfd, pData2, pPager->pageSize, iOff+4);
- if( rc!=SQLITE_OK ) return rc;
- rc = write32bits(pPager->jfd, iOff+pPager->pageSize+4, cksum);
- if( rc!=SQLITE_OK ) return rc;
- IOTRACE(("JOUT %p %d %lld %d\n", pPager, pPg->pgno,
- pPager->journalOff, pPager->pageSize));
- PAGER_INCR(sqlite3_pager_writej_count);
- PAGERTRACE(("JOURNAL %d page %d needSync=%d hash(%08x)\n",
- PAGERID(pPager), pPg->pgno,
- ((pPg->flags&PGHDR_NEED_SYNC)?1:0), pager_pagehash(pPg)));
-
- pPager->journalOff += 8 + pPager->pageSize;
- pPager->nRec++;
- assert( pPager->pInJournal!=0 );
- rc = sqlite3BitvecSet(pPager->pInJournal, pPg->pgno);
- testcase( rc==SQLITE_NOMEM );
- assert( rc==SQLITE_OK || rc==SQLITE_NOMEM );
- rc |= addToSavepointBitvecs(pPager, pPg->pgno);
- if( rc!=SQLITE_OK ){
- assert( rc==SQLITE_NOMEM );
- return rc;
- }
- }else{
- if( pPager->eState!=PAGER_WRITER_DBMOD ){
- pPg->flags |= PGHDR_NEED_SYNC;
- }
- PAGERTRACE(("APPEND %d page %d needSync=%d\n",
- PAGERID(pPager), pPg->pgno,
- ((pPg->flags&PGHDR_NEED_SYNC)?1:0)));
+ /* If a rollback journal is in use, them make sure the page that is about
+ ** to change is in the rollback journal, or if the page is a new page off
+ ** then end of the file, make sure it is marked as PGHDR_NEED_SYNC.
+ */
+ assert( (pPager->pInJournal!=0) == isOpen(pPager->jfd) );
+ if( pPager->pInJournal!=0
+ && sqlite3BitvecTestNotNull(pPager->pInJournal, pPg->pgno)==0
+ ){
+ assert( pagerUseWal(pPager)==0 );
+ if( pPg->pgno<=pPager->dbOrigSize ){
+ rc = pagerAddPageToRollbackJournal(pPg);
+ if( rc!=SQLITE_OK ){
+ return rc;
}
- }
-
- /* If the statement journal is open and the page is not in it,
- ** then write the current page to the statement journal. Note that
- ** the statement journal format differs from the standard journal format
- ** in that it omits the checksums and the header.
- */
- if( pPager->nSavepoint>0 && subjRequiresPage(pPg) ){
- rc = subjournalPage(pPg);
+ }else{
+ if( pPager->eState!=PAGER_WRITER_DBMOD ){
+ pPg->flags |= PGHDR_NEED_SYNC;
+ }
+ PAGERTRACE(("APPEND %d page %d needSync=%d\n",
+ PAGERID(pPager), pPg->pgno,
+ ((pPg->flags&PGHDR_NEED_SYNC)?1:0)));
}
}
- /* Update the database size and return.
+ /* The PGHDR_DIRTY bit is set above when the page was added to the dirty-list
+ ** and before writing the page into the rollback journal. Wait until now,
+ ** after the page has been successfully journalled, before setting the
+ ** PGHDR_WRITEABLE bit that indicates that the page can be safely modified.
+ */
+ pPg->flags |= PGHDR_WRITEABLE;
+
+ /* If the statement journal is open and the page is not in it,
+ ** then write the page into the statement journal.
*/
+ if( pPager->nSavepoint>0 ){
+ rc = subjournalPageIfRequired(pPg);
+ }
+
+ /* Update the database size and return. */
if( pPager->dbSize<pPg->pgno ){
pPager->dbSize = pPg->pgno;
}
@@ -47420,17 +51723,17 @@ static int pager_write(PgHdr *pPg){
** a write.
**
** Usually, the sector size is less than or equal to the page size, in which
-** case pages can be individually written. This routine only runs in the exceptional
-** case where the page size is smaller than the sector size.
+** case pages can be individually written. This routine only runs in the
+** exceptional case where the page size is smaller than the sector size.
*/
static SQLITE_NOINLINE int pagerWriteLargeSector(PgHdr *pPg){
- int rc = SQLITE_OK; /* Return code */
- Pgno nPageCount; /* Total number of pages in database file */
- Pgno pg1; /* First page of the sector pPg is located on. */
- int nPage = 0; /* Number of pages starting at pg1 to journal */
- int ii; /* Loop counter */
- int needSync = 0; /* True if any page has PGHDR_NEED_SYNC */
- Pager *pPager = pPg->pPager; /* The pager that owns pPg */
+ int rc = SQLITE_OK; /* Return code */
+ Pgno nPageCount; /* Total number of pages in database file */
+ Pgno pg1; /* First page of the sector pPg is located on. */
+ int nPage = 0; /* Number of pages starting at pg1 to journal */
+ int ii; /* Loop counter */
+ int needSync = 0; /* True if any page has PGHDR_NEED_SYNC */
+ Pager *pPager = pPg->pPager; /* The pager that owns pPg */
Pgno nPagePerSector = (pPager->sectorSize/pPager->pageSize);
/* Set the doNotSpill NOSYNC bit to 1. This is because we cannot allow
@@ -47464,7 +51767,7 @@ static SQLITE_NOINLINE int pagerWriteLargeSector(PgHdr *pPg){
PgHdr *pPage;
if( pg==pPg->pgno || !sqlite3BitvecTest(pPager->pInJournal, pg) ){
if( pg!=PAGER_MJ_PGNO(pPager) ){
- rc = sqlite3PagerGet(pPager, pg, &pPage);
+ rc = sqlite3PagerGet(pPager, pg, &pPage, 0);
if( rc==SQLITE_OK ){
rc = pager_write(pPage);
if( pPage->flags&PGHDR_NEED_SYNC ){
@@ -47518,11 +51821,17 @@ static SQLITE_NOINLINE int pagerWriteLargeSector(PgHdr *pPg){
** as appropriate. Otherwise, SQLITE_OK.
*/
SQLITE_PRIVATE int sqlite3PagerWrite(PgHdr *pPg){
+ Pager *pPager = pPg->pPager;
assert( (pPg->flags & PGHDR_MMAP)==0 );
- assert( pPg->pPager->eState>=PAGER_WRITER_LOCKED );
- assert( pPg->pPager->eState!=PAGER_ERROR );
- assert( assert_pager_state(pPg->pPager) );
- if( pPg->pPager->sectorSize > (u32)pPg->pPager->pageSize ){
+ assert( pPager->eState>=PAGER_WRITER_LOCKED );
+ assert( assert_pager_state(pPager) );
+ if( pPager->errCode ){
+ return pPager->errCode;
+ }else if( (pPg->flags & PGHDR_WRITEABLE)!=0 && pPager->dbSize>=pPg->pgno ){
+ if( pPager->nSavepoint ) return subjournalPageIfRequired(pPg);
+ return SQLITE_OK;
+ }else if( pPager->sectorSize > (u32)pPager->pageSize ){
+ assert( pPager->tempFile==0 );
return pagerWriteLargeSector(pPg);
}else{
return pager_write(pPg);
@@ -47536,7 +51845,7 @@ SQLITE_PRIVATE int sqlite3PagerWrite(PgHdr *pPg){
*/
#ifndef NDEBUG
SQLITE_PRIVATE int sqlite3PagerIswriteable(DbPage *pPg){
- return pPg->flags&PGHDR_DIRTY;
+ return pPg->flags & PGHDR_WRITEABLE;
}
#endif
@@ -47553,13 +51862,21 @@ SQLITE_PRIVATE int sqlite3PagerIswriteable(DbPage *pPg){
**
** Tests show that this optimization can quadruple the speed of large
** DELETE operations.
+**
+** This optimization cannot be used with a temp-file, as the page may
+** have been dirty at the start of the transaction. In that case, if
+** memory pressure forces page pPg out of the cache, the data does need
+** to be written out to disk so that it may be read back in if the
+** current transaction is rolled back.
*/
SQLITE_PRIVATE void sqlite3PagerDontWrite(PgHdr *pPg){
Pager *pPager = pPg->pPager;
- if( (pPg->flags&PGHDR_DIRTY) && pPager->nSavepoint==0 ){
+ if( !pPager->tempFile && (pPg->flags&PGHDR_DIRTY) && pPager->nSavepoint==0 ){
PAGERTRACE(("DONT_WRITE page %d of %d\n", pPg->pgno, PAGERID(pPager)));
IOTRACE(("CLEAN %p %d\n", pPager, pPg->pgno))
pPg->flags |= PGHDR_DONT_WRITE;
+ pPg->flags &= ~PGHDR_WRITEABLE;
+ testcase( pPg->flags & PGHDR_NEED_SYNC );
pager_set_pagehash(pPg);
}
}
@@ -47618,7 +51935,7 @@ static int pager_incr_changecounter(Pager *pPager, int isDirectMode){
assert( !pPager->tempFile && isOpen(pPager->fd) );
/* Open page 1 of the file for writing. */
- rc = sqlite3PagerGet(pPager, 1, &pPgHdr);
+ rc = sqlite3PagerGet(pPager, 1, &pPgHdr, 0);
assert( pPgHdr==0 || rc==SQLITE_OK );
/* If page one was fetched successfully, and this function is not
@@ -47638,7 +51955,7 @@ static int pager_incr_changecounter(Pager *pPager, int isDirectMode){
if( DIRECT_MODE ){
const void *zBuf;
assert( pPager->dbFileSize>0 );
- CODEC2(pPager, pPgHdr->pData, 1, 6, rc=SQLITE_NOMEM, zBuf);
+ CODEC2(pPager, pPgHdr->pData, 1, 6, rc=SQLITE_NOMEM_BKPT, zBuf);
if( rc==SQLITE_OK ){
rc = sqlite3OsWrite(pPager->fd, zBuf, pPager->pageSize, 0);
pPager->aStat[PAGER_STAT_WRITE]++;
@@ -47696,14 +52013,17 @@ SQLITE_PRIVATE int sqlite3PagerSync(Pager *pPager, const char *zMaster){
** returned.
*/
SQLITE_PRIVATE int sqlite3PagerExclusiveLock(Pager *pPager){
- int rc = SQLITE_OK;
- assert( pPager->eState==PAGER_WRITER_CACHEMOD
- || pPager->eState==PAGER_WRITER_DBMOD
- || pPager->eState==PAGER_WRITER_LOCKED
- );
+ int rc = pPager->errCode;
assert( assert_pager_state(pPager) );
- if( 0==pagerUseWal(pPager) ){
- rc = pager_wait_on_lock(pPager, EXCLUSIVE_LOCK);
+ if( rc==SQLITE_OK ){
+ assert( pPager->eState==PAGER_WRITER_CACHEMOD
+ || pPager->eState==PAGER_WRITER_DBMOD
+ || pPager->eState==PAGER_WRITER_LOCKED
+ );
+ assert( assert_pager_state(pPager) );
+ if( 0==pagerUseWal(pPager) ){
+ rc = pager_wait_on_lock(pPager, EXCLUSIVE_LOCK);
+ }
}
return rc;
}
@@ -47751,17 +52071,21 @@ SQLITE_PRIVATE int sqlite3PagerCommitPhaseOne(
/* If a prior error occurred, report that error again. */
if( NEVER(pPager->errCode) ) return pPager->errCode;
+ /* Provide the ability to easily simulate an I/O error during testing */
+ if( sqlite3FaultSim(400) ) return SQLITE_IOERR;
+
PAGERTRACE(("DATABASE SYNC: File=%s zMaster=%s nSize=%d\n",
pPager->zFilename, zMaster, pPager->dbSize));
/* If no database changes have been made, return early. */
if( pPager->eState<PAGER_WRITER_CACHEMOD ) return SQLITE_OK;
- if( MEMDB ){
+ assert( MEMDB==0 || pPager->tempFile );
+ assert( isOpen(pPager->fd) || pPager->tempFile );
+ if( 0==pagerFlushOnCommit(pPager, 1) ){
/* If this is an in-memory db, or no pages have been written to, or this
** function has already been called, it is mostly a no-op. However, any
- ** backup in progress needs to be restarted.
- */
+ ** backup in progress needs to be restarted. */
sqlite3BackupRestart(pPager->pBackup);
}else{
if( pagerUseWal(pPager) ){
@@ -47770,7 +52094,7 @@ SQLITE_PRIVATE int sqlite3PagerCommitPhaseOne(
if( pList==0 ){
/* Must have at least one page for the WAL commit flag.
** Ticket [2d1a5c67dfc2363e44f29d9bbd57f] 2011-05-18 */
- rc = sqlite3PagerGet(pPager, 1, &pPageOne);
+ rc = sqlite3PagerGet(pPager, 1, &pPageOne, 0);
pList = pPageOne;
pList->pDirty = 0;
}
@@ -48026,12 +52350,14 @@ SQLITE_PRIVATE u8 sqlite3PagerIsreadonly(Pager *pPager){
return pPager->readOnly;
}
+#ifdef SQLITE_DEBUG
/*
-** Return the number of references to the pager.
+** Return the sum of the reference counts for all pages held by pPager.
*/
SQLITE_PRIVATE int sqlite3PagerRefcount(Pager *pPager){
return sqlite3PcacheRefCount(pPager->pPCache);
}
+#endif
/*
** Return the approximate number of bytes of memory currently
@@ -48098,10 +52424,10 @@ SQLITE_PRIVATE void sqlite3PagerCacheStat(Pager *pPager, int eStat, int reset, i
}
/*
-** Return true if this is an in-memory pager.
+** Return true if this is an in-memory or temp-file backed pager.
*/
SQLITE_PRIVATE int sqlite3PagerIsMemdb(Pager *pPager){
- return MEMDB;
+ return pPager->tempFile;
}
/*
@@ -48114,54 +52440,62 @@ SQLITE_PRIVATE int sqlite3PagerIsMemdb(Pager *pPager){
** occurs while opening the sub-journal file, then an IO error code is
** returned. Otherwise, SQLITE_OK.
*/
-SQLITE_PRIVATE int sqlite3PagerOpenSavepoint(Pager *pPager, int nSavepoint){
+static SQLITE_NOINLINE int pagerOpenSavepoint(Pager *pPager, int nSavepoint){
int rc = SQLITE_OK; /* Return code */
int nCurrent = pPager->nSavepoint; /* Current number of savepoints */
+ int ii; /* Iterator variable */
+ PagerSavepoint *aNew; /* New Pager.aSavepoint array */
assert( pPager->eState>=PAGER_WRITER_LOCKED );
assert( assert_pager_state(pPager) );
+ assert( nSavepoint>nCurrent && pPager->useJournal );
- if( nSavepoint>nCurrent && pPager->useJournal ){
- int ii; /* Iterator variable */
- PagerSavepoint *aNew; /* New Pager.aSavepoint array */
+ /* Grow the Pager.aSavepoint array using realloc(). Return SQLITE_NOMEM
+ ** if the allocation fails. Otherwise, zero the new portion in case a
+ ** malloc failure occurs while populating it in the for(...) loop below.
+ */
+ aNew = (PagerSavepoint *)sqlite3Realloc(
+ pPager->aSavepoint, sizeof(PagerSavepoint)*nSavepoint
+ );
+ if( !aNew ){
+ return SQLITE_NOMEM_BKPT;
+ }
+ memset(&aNew[nCurrent], 0, (nSavepoint-nCurrent) * sizeof(PagerSavepoint));
+ pPager->aSavepoint = aNew;
- /* Grow the Pager.aSavepoint array using realloc(). Return SQLITE_NOMEM
- ** if the allocation fails. Otherwise, zero the new portion in case a
- ** malloc failure occurs while populating it in the for(...) loop below.
- */
- aNew = (PagerSavepoint *)sqlite3Realloc(
- pPager->aSavepoint, sizeof(PagerSavepoint)*nSavepoint
- );
- if( !aNew ){
- return SQLITE_NOMEM;
+ /* Populate the PagerSavepoint structures just allocated. */
+ for(ii=nCurrent; ii<nSavepoint; ii++){
+ aNew[ii].nOrig = pPager->dbSize;
+ if( isOpen(pPager->jfd) && pPager->journalOff>0 ){
+ aNew[ii].iOffset = pPager->journalOff;
+ }else{
+ aNew[ii].iOffset = JOURNAL_HDR_SZ(pPager);
}
- memset(&aNew[nCurrent], 0, (nSavepoint-nCurrent) * sizeof(PagerSavepoint));
- pPager->aSavepoint = aNew;
-
- /* Populate the PagerSavepoint structures just allocated. */
- for(ii=nCurrent; ii<nSavepoint; ii++){
- aNew[ii].nOrig = pPager->dbSize;
- if( isOpen(pPager->jfd) && pPager->journalOff>0 ){
- aNew[ii].iOffset = pPager->journalOff;
- }else{
- aNew[ii].iOffset = JOURNAL_HDR_SZ(pPager);
- }
- aNew[ii].iSubRec = pPager->nSubRec;
- aNew[ii].pInSavepoint = sqlite3BitvecCreate(pPager->dbSize);
- if( !aNew[ii].pInSavepoint ){
- return SQLITE_NOMEM;
- }
- if( pagerUseWal(pPager) ){
- sqlite3WalSavepoint(pPager->pWal, aNew[ii].aWalData);
- }
- pPager->nSavepoint = ii+1;
+ aNew[ii].iSubRec = pPager->nSubRec;
+ aNew[ii].pInSavepoint = sqlite3BitvecCreate(pPager->dbSize);
+ if( !aNew[ii].pInSavepoint ){
+ return SQLITE_NOMEM_BKPT;
}
- assert( pPager->nSavepoint==nSavepoint );
- assertTruncateConstraint(pPager);
+ if( pagerUseWal(pPager) ){
+ sqlite3WalSavepoint(pPager->pWal, aNew[ii].aWalData);
+ }
+ pPager->nSavepoint = ii+1;
}
-
+ assert( pPager->nSavepoint==nSavepoint );
+ assertTruncateConstraint(pPager);
return rc;
}
+SQLITE_PRIVATE int sqlite3PagerOpenSavepoint(Pager *pPager, int nSavepoint){
+ assert( pPager->eState>=PAGER_WRITER_LOCKED );
+ assert( assert_pager_state(pPager) );
+
+ if( nSavepoint>pPager->nSavepoint && pPager->useJournal ){
+ return pagerOpenSavepoint(pPager, nSavepoint);
+ }else{
+ return SQLITE_OK;
+ }
+}
+
/*
** This function is called to rollback or release (commit) a savepoint.
@@ -48218,7 +52552,7 @@ SQLITE_PRIVATE int sqlite3PagerSavepoint(Pager *pPager, int op, int iSavepoint){
if( op==SAVEPOINT_RELEASE ){
if( nNew==0 && isOpen(pPager->sjfd) ){
/* Only truncate if it is an in-memory sub-journal. */
- if( sqlite3IsMemJournal(pPager->sjfd) ){
+ if( sqlite3JournalIsInMemory(pPager->sjfd) ){
rc = sqlite3OsTruncate(pPager->sjfd, 0);
assert( rc==SQLITE_OK );
}
@@ -48257,7 +52591,7 @@ SQLITE_PRIVATE const char *sqlite3PagerFilename(Pager *pPager, int nullIfMemDb){
/*
** Return the VFS structure for the pager.
*/
-SQLITE_PRIVATE const sqlite3_vfs *sqlite3PagerVfs(Pager *pPager){
+SQLITE_PRIVATE sqlite3_vfs *sqlite3PagerVfs(Pager *pPager){
return pPager->pVfs;
}
@@ -48271,18 +52605,22 @@ SQLITE_PRIVATE sqlite3_file *sqlite3PagerFile(Pager *pPager){
}
/*
-** Return the full pathname of the journal file.
+** Return the file handle for the journal file (if it exists).
+** This will be either the rollback journal or the WAL file.
*/
-SQLITE_PRIVATE const char *sqlite3PagerJournalname(Pager *pPager){
- return pPager->zJournal;
+SQLITE_PRIVATE sqlite3_file *sqlite3PagerJrnlFile(Pager *pPager){
+#if SQLITE_OMIT_WAL
+ return pPager->jfd;
+#else
+ return pPager->pWal ? sqlite3WalFile(pPager->pWal) : pPager->jfd;
+#endif
}
/*
-** Return true if fsync() calls are disabled for this pager. Return FALSE
-** if fsync()s are executed normally.
+** Return the full pathname of the journal file.
*/
-SQLITE_PRIVATE int sqlite3PagerNosync(Pager *pPager){
- return pPager->noSync;
+SQLITE_PRIVATE const char *sqlite3PagerJournalname(Pager *pPager){
+ return pPager->zJournal;
}
#ifdef SQLITE_HAS_CODEC
@@ -48369,7 +52707,8 @@ SQLITE_PRIVATE int sqlite3PagerMovepage(Pager *pPager, DbPage *pPg, Pgno pgno, i
/* In order to be able to rollback, an in-memory database must journal
** the page we are moving from.
*/
- if( MEMDB ){
+ assert( pPager->tempFile || !MEMDB );
+ if( pPager->tempFile ){
rc = sqlite3PagerWrite(pPg);
if( rc ) return rc;
}
@@ -48392,9 +52731,8 @@ SQLITE_PRIVATE int sqlite3PagerMovepage(Pager *pPager, DbPage *pPg, Pgno pgno, i
** one or more savepoint bitvecs. This is the reason this function
** may return SQLITE_NOMEM.
*/
- if( pPg->flags&PGHDR_DIRTY
- && subjRequiresPage(pPg)
- && SQLITE_OK!=(rc = subjournalPage(pPg))
+ if( (pPg->flags & PGHDR_DIRTY)!=0
+ && SQLITE_OK!=(rc = subjournalPageIfRequired(pPg))
){
return rc;
}
@@ -48427,7 +52765,7 @@ SQLITE_PRIVATE int sqlite3PagerMovepage(Pager *pPager, DbPage *pPg, Pgno pgno, i
assert( !pPgOld || pPgOld->nRef==1 );
if( pPgOld ){
pPg->flags |= (pPgOld->flags&PGHDR_NEED_SYNC);
- if( MEMDB ){
+ if( pPager->tempFile ){
/* Do not discard pages from an in-memory database since we might
** need to rollback later. Just move the page out of the way. */
sqlite3PcacheMove(pPgOld, pPager->dbSize+1);
@@ -48444,8 +52782,7 @@ SQLITE_PRIVATE int sqlite3PagerMovepage(Pager *pPager, DbPage *pPg, Pgno pgno, i
** to exist, in case the transaction needs to roll back. Use pPgOld
** as the original page since it has already been allocated.
*/
- if( MEMDB ){
- assert( pPgOld );
+ if( pPager->tempFile && pPgOld ){
sqlite3PcacheMove(pPgOld, origPgno);
sqlite3PagerUnrefNotNull(pPgOld);
}
@@ -48466,7 +52803,7 @@ SQLITE_PRIVATE int sqlite3PagerMovepage(Pager *pPager, DbPage *pPg, Pgno pgno, i
** the journal file twice, but that is not a problem.
*/
PgHdr *pPgHdr;
- rc = sqlite3PagerGet(pPager, needSyncPgno, &pPgHdr);
+ rc = sqlite3PagerGet(pPager, needSyncPgno, &pPgHdr, 0);
if( rc!=SQLITE_OK ){
if( needSyncPgno<=pPager->dbOrigSize ){
assert( pPager->pTmpSpace!=0 );
@@ -48697,7 +53034,8 @@ SQLITE_PRIVATE sqlite3_backup **sqlite3PagerBackupPtr(Pager *pPager){
** Unless this is an in-memory or temporary database, clear the pager cache.
*/
SQLITE_PRIVATE void sqlite3PagerClearCache(Pager *pPager){
- if( !MEMDB && pPager->tempFile==0 ) pager_reset(pPager);
+ assert( MEMDB==0 || pPager->tempFile );
+ if( pPager->tempFile==0 ) pager_reset(pPager);
}
#endif
@@ -48732,6 +53070,7 @@ SQLITE_PRIVATE int sqlite3PagerWalCallback(Pager *pPager){
*/
SQLITE_PRIVATE int sqlite3PagerWalSupported(Pager *pPager){
const sqlite3_io_methods *pMethods = pPager->fd->pMethods;
+ if( pPager->noLock ) return 0;
return pPager->exclusiveMode || (pMethods->iVersion>=2 && pMethods->xShmMap);
}
@@ -48875,11 +53214,40 @@ SQLITE_PRIVATE int sqlite3PagerCloseWal(Pager *pPager){
pPager->pageSize, (u8*)pPager->pTmpSpace);
pPager->pWal = 0;
pagerFixMaplimit(pPager);
+ if( rc && !pPager->exclusiveMode ) pagerUnlockDb(pPager, SHARED_LOCK);
}
}
return rc;
}
+#ifdef SQLITE_ENABLE_SNAPSHOT
+/*
+** If this is a WAL database, obtain a snapshot handle for the snapshot
+** currently open. Otherwise, return an error.
+*/
+SQLITE_PRIVATE int sqlite3PagerSnapshotGet(Pager *pPager, sqlite3_snapshot **ppSnapshot){
+ int rc = SQLITE_ERROR;
+ if( pPager->pWal ){
+ rc = sqlite3WalSnapshotGet(pPager->pWal, ppSnapshot);
+ }
+ return rc;
+}
+
+/*
+** If this is a WAL database, store a pointer to pSnapshot. Next time a
+** read transaction is opened, attempt to read from the snapshot it
+** identifies. If this is not a WAL database, return an error.
+*/
+SQLITE_PRIVATE int sqlite3PagerSnapshotOpen(Pager *pPager, sqlite3_snapshot *pSnapshot){
+ int rc = SQLITE_OK;
+ if( pPager->pWal ){
+ sqlite3WalSnapshotOpen(pPager->pWal, pSnapshot);
+ }else{
+ rc = SQLITE_ERROR;
+ }
+ return rc;
+}
+#endif /* SQLITE_ENABLE_SNAPSHOT */
#endif /* !SQLITE_OMIT_WAL */
#ifdef SQLITE_ENABLE_ZIPVFS
@@ -49145,6 +53513,7 @@ SQLITE_PRIVATE int sqlite3PagerWalFramesize(Pager *pPager){
*/
#ifndef SQLITE_OMIT_WAL
+/* #include "wal.h" */
/*
** Trace output macros
@@ -49174,7 +53543,8 @@ SQLITE_PRIVATE int sqlite3WalTrace = 0;
/*
** Indices of various locking bytes. WAL_NREADER is the number
-** of available reader locks and should be at least 3.
+** of available reader locks and should be at least 3. The default
+** is SQLITE_SHM_NLOCK==8 and WAL_NREADER==5.
*/
#define WAL_WRITE_LOCK 0
#define WAL_ALL_BUT_WRITE 1
@@ -49194,7 +53564,10 @@ typedef struct WalCkptInfo WalCkptInfo;
** The following object holds a copy of the wal-index header content.
**
** The actual header in the wal-index consists of two copies of this
-** object.
+** object followed by one instance of the WalCkptInfo object.
+** For all versions of SQLite through 3.10.0 and probably beyond,
+** the locking bytes (WalCkptInfo.aLock) start at offset 120 and
+** the total header size is 136 bytes.
**
** The szPage value can be any power of 2 between 512 and 32768, inclusive.
** Or it can be 1 to represent a 65536-byte page. The latter case was
@@ -49227,6 +53600,16 @@ struct WalIndexHdr {
** However, a WAL_WRITE_LOCK thread can move the value of nBackfill from
** mxFrame back to zero when the WAL is reset.
**
+** nBackfillAttempted is the largest value of nBackfill that a checkpoint
+** has attempted to achieve. Normally nBackfill==nBackfillAtempted, however
+** the nBackfillAttempted is set before any backfilling is done and the
+** nBackfill is only set after all backfilling completes. So if a checkpoint
+** crashes, nBackfillAttempted might be larger than nBackfill. The
+** WalIndexHdr.mxFrame must never be less than nBackfillAttempted.
+**
+** The aLock[] field is a set of bytes used for locking. These bytes should
+** never be read or written.
+**
** There is one entry in aReadMark[] for each reader lock. If a reader
** holds read-lock K, then the value in aReadMark[K] is no greater than
** the mxFrame for that reader. The value READMARK_NOT_USED (0xffffffff)
@@ -49266,6 +53649,9 @@ struct WalIndexHdr {
struct WalCkptInfo {
u32 nBackfill; /* Number of WAL frames backfilled into DB */
u32 aReadMark[WAL_NREADER]; /* Reader marks */
+ u8 aLock[SQLITE_SHM_NLOCK]; /* Reserved space for locks */
+ u32 nBackfillAttempted; /* WAL frames perhaps written, or maybe not */
+ u32 notUsed0; /* Available for future enhancements */
};
#define READMARK_NOT_USED 0xffffffff
@@ -49275,9 +53661,8 @@ struct WalCkptInfo {
** only support mandatory file-locks, we do not read or write data
** from the region of the file on which locks are applied.
*/
-#define WALINDEX_LOCK_OFFSET (sizeof(WalIndexHdr)*2 + sizeof(WalCkptInfo))
-#define WALINDEX_LOCK_RESERVED 16
-#define WALINDEX_HDR_SIZE (WALINDEX_LOCK_OFFSET+WALINDEX_LOCK_RESERVED)
+#define WALINDEX_LOCK_OFFSET (sizeof(WalIndexHdr)*2+offsetof(WalCkptInfo,aLock))
+#define WALINDEX_HDR_SIZE (sizeof(WalIndexHdr)*2+sizeof(WalCkptInfo))
/* Size of header before each frame in wal */
#define WAL_FRAME_HDRSIZE 24
@@ -49330,11 +53715,16 @@ struct Wal {
u8 syncHeader; /* Fsync the WAL header if true */
u8 padToSectorBoundary; /* Pad transactions out to the next sector */
WalIndexHdr hdr; /* Wal-index header for current transaction */
+ u32 minFrame; /* Ignore wal frames before this one */
+ u32 iReCksum; /* On commit, recalculate checksums from here */
const char *zWalName; /* Name of WAL file */
u32 nCkpt; /* Checkpoint sequence counter in the wal-header */
#ifdef SQLITE_DEBUG
u8 lockError; /* True if a locking error has occurred */
#endif
+#ifdef SQLITE_ENABLE_SNAPSHOT
+ WalIndexHdr *pSnapshot; /* Start transaction here if not NULL */
+#endif
};
/*
@@ -49427,7 +53817,7 @@ static int walIndexPage(Wal *pWal, int iPage, volatile u32 **ppPage){
apNew = (volatile u32 **)sqlite3_realloc64((void *)pWal->apWiData, nByte);
if( !apNew ){
*ppPage = 0;
- return SQLITE_NOMEM;
+ return SQLITE_NOMEM_BKPT;
}
memset((void*)&apNew[pWal->nWiData], 0,
sizeof(u32*)*(iPage+1-pWal->nWiData));
@@ -49439,7 +53829,7 @@ static int walIndexPage(Wal *pWal, int iPage, volatile u32 **ppPage){
if( pWal->apWiData[iPage]==0 ){
if( pWal->exclusiveMode==WAL_HEAPMEMORY_MODE ){
pWal->apWiData[iPage] = (u32 volatile *)sqlite3MallocZero(WALINDEX_PGSZ);
- if( !pWal->apWiData[iPage] ) rc = SQLITE_NOMEM;
+ if( !pWal->apWiData[iPage] ) rc = SQLITE_NOMEM_BKPT;
}else{
rc = sqlite3OsShmMap(pWal->pDbFd, iPage, WALINDEX_PGSZ,
pWal->writeLock, (void volatile **)&pWal->apWiData[iPage]
@@ -49550,9 +53940,9 @@ static void walIndexWriteHdr(Wal *pWal){
pWal->hdr.isInit = 1;
pWal->hdr.iVersion = WALINDEX_MAX_VERSION;
walChecksumBytes(1, (u8*)&pWal->hdr, nCksum, 0, pWal->hdr.aCksum);
- memcpy((void *)&aHdr[1], (void *)&pWal->hdr, sizeof(WalIndexHdr));
+ memcpy((void*)&aHdr[1], (const void*)&pWal->hdr, sizeof(WalIndexHdr));
walShmBarrier(pWal);
- memcpy((void *)&aHdr[0], (void *)&pWal->hdr, sizeof(WalIndexHdr));
+ memcpy((void*)&aHdr[0], (const void*)&pWal->hdr, sizeof(WalIndexHdr));
}
/*
@@ -49580,14 +53970,18 @@ static void walEncodeFrame(
assert( WAL_FRAME_HDRSIZE==24 );
sqlite3Put4byte(&aFrame[0], iPage);
sqlite3Put4byte(&aFrame[4], nTruncate);
- memcpy(&aFrame[8], pWal->hdr.aSalt, 8);
+ if( pWal->iReCksum==0 ){
+ memcpy(&aFrame[8], pWal->hdr.aSalt, 8);
- nativeCksum = (pWal->hdr.bigEndCksum==SQLITE_BIGENDIAN);
- walChecksumBytes(nativeCksum, aFrame, 8, aCksum, aCksum);
- walChecksumBytes(nativeCksum, aData, pWal->szPage, aCksum, aCksum);
+ nativeCksum = (pWal->hdr.bigEndCksum==SQLITE_BIGENDIAN);
+ walChecksumBytes(nativeCksum, aFrame, 8, aCksum, aCksum);
+ walChecksumBytes(nativeCksum, aData, pWal->szPage, aCksum, aCksum);
- sqlite3Put4byte(&aFrame[16], aCksum[0]);
- sqlite3Put4byte(&aFrame[20], aCksum[1]);
+ sqlite3Put4byte(&aFrame[16], aCksum[0]);
+ sqlite3Put4byte(&aFrame[20], aCksum[1]);
+ }else{
+ memset(&aFrame[8], 0, 16);
+ }
}
/*
@@ -49690,10 +54084,9 @@ 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, int fBlock){
+static int walLockExclusive(Wal *pWal, int lockIdx, int n){
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,
@@ -49854,13 +54247,13 @@ static void walCleanupHash(Wal *pWal){
** via the hash table even after the cleanup.
*/
if( iLimit ){
- int i; /* Loop counter */
+ int j; /* Loop counter */
int iKey; /* Hash key */
- for(i=1; i<=iLimit; i++){
- for(iKey=walHash(aPgno[i]); aHash[iKey]; iKey=walNextHash(iKey)){
- if( aHash[iKey]==i ) break;
+ for(j=1; j<=iLimit; j++){
+ for(iKey=walHash(aPgno[j]); aHash[iKey]; iKey=walNextHash(iKey)){
+ if( aHash[iKey]==j ) break;
}
- assert( aHash[iKey]==i );
+ assert( aHash[iKey]==j );
}
}
#endif /* SQLITE_ENABLE_EXPENSIVE_ASSERT */
@@ -49979,7 +54372,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, 0);
+ rc = walLockExclusive(pWal, iLock, nLock);
if( rc ){
return rc;
}
@@ -50051,7 +54444,7 @@ static int walIndexRecover(Wal *pWal){
szFrame = szPage + WAL_FRAME_HDRSIZE;
aFrame = (u8 *)sqlite3_malloc64(szFrame);
if( !aFrame ){
- rc = SQLITE_NOMEM;
+ rc = SQLITE_NOMEM_BKPT;
goto recovery_error;
}
aData = &aFrame[WAL_FRAME_HDRSIZE];
@@ -50100,6 +54493,7 @@ finished:
*/
pInfo = walCkptInfo(pWal);
pInfo->nBackfill = 0;
+ pInfo->nBackfillAttempted = pWal->hdr.mxFrame;
pInfo->aReadMark[0] = 0;
for(i=1; i<WAL_NREADER; i++) pInfo->aReadMark[i] = READMARK_NOT_USED;
if( pWal->hdr.mxFrame ) pInfo->aReadMark[1] = pWal->hdr.mxFrame;
@@ -50171,7 +54565,11 @@ SQLITE_PRIVATE int sqlite3WalOpen(
/* In the amalgamation, the os_unix.c and os_win.c source files come before
** this source file. Verify that the #defines of the locking byte offsets
** in os_unix.c and os_win.c agree with the WALINDEX_LOCK_OFFSET value.
+ ** For that matter, if the lock offset ever changes from its initial design
+ ** value of 120, we need to know that so there is an assert() to check it.
*/
+ assert( 120==WALINDEX_LOCK_OFFSET );
+ assert( 136==WALINDEX_HDR_SIZE );
#ifdef WIN_SHM_BASE
assert( WIN_SHM_BASE==WALINDEX_LOCK_OFFSET );
#endif
@@ -50184,7 +54582,7 @@ SQLITE_PRIVATE int sqlite3WalOpen(
*ppWal = 0;
pRet = (Wal*)sqlite3MallocZero(sizeof(Wal) + pVfs->szOsFile);
if( !pRet ){
- return SQLITE_NOMEM;
+ return SQLITE_NOMEM_BKPT;
}
pRet->pVfs = pVfs;
@@ -50362,7 +54760,7 @@ static void walMergesort(
int nMerge = 0; /* Number of elements in list aMerge */
ht_slot *aMerge = 0; /* List to be merged */
int iList; /* Index into input list */
- int iSub = 0; /* Index into aSub array */
+ u32 iSub = 0; /* Index into aSub array */
struct Sublist aSub[13]; /* Array of sub-lists */
memset(aSub, 0, sizeof(aSub));
@@ -50373,7 +54771,9 @@ static void walMergesort(
nMerge = 1;
aMerge = &aList[iList];
for(iSub=0; iList & (1<<iSub); iSub++){
- struct Sublist *p = &aSub[iSub];
+ struct Sublist *p;
+ assert( iSub<ArraySize(aSub) );
+ p = &aSub[iSub];
assert( p->aList && p->nList<=(1<<iSub) );
assert( p->aList==&aList[iList&~((2<<iSub)-1)] );
walMerge(aContent, p->aList, p->nList, &aMerge, &nMerge, aBuffer);
@@ -50384,7 +54784,9 @@ static void walMergesort(
for(iSub++; iSub<ArraySize(aSub); iSub++){
if( nList & (1<<iSub) ){
- struct Sublist *p = &aSub[iSub];
+ struct Sublist *p;
+ assert( iSub<ArraySize(aSub) );
+ p = &aSub[iSub];
assert( p->nList<=(1<<iSub) );
assert( p->aList==&aList[nList&~((2<<iSub)-1)] );
walMerge(aContent, p->aList, p->nList, &aMerge, &nMerge, aBuffer);
@@ -50444,7 +54846,7 @@ static int walIteratorInit(Wal *pWal, WalIterator **pp){
+ iLast*sizeof(ht_slot);
p = (WalIterator *)sqlite3_malloc64(nByte);
if( !p ){
- return SQLITE_NOMEM;
+ return SQLITE_NOMEM_BKPT;
}
memset(p, 0, nByte);
p->nSegment = nSegment;
@@ -50456,7 +54858,7 @@ static int walIteratorInit(Wal *pWal, WalIterator **pp){
sizeof(ht_slot) * (iLast>HASHTABLE_NPAGE?HASHTABLE_NPAGE:iLast)
);
if( !aTmp ){
- rc = SQLITE_NOMEM;
+ rc = SQLITE_NOMEM_BKPT;
}
for(i=0; rc==SQLITE_OK && i<nSegment; i++){
@@ -50513,7 +54915,7 @@ static int walBusyLock(
){
int rc;
do {
- rc = walLockExclusive(pWal, lockIdx, n, 0);
+ rc = walLockExclusive(pWal, lockIdx, n);
}while( xBusy && rc==SQLITE_BUSY && xBusy(pBusyArg) );
return rc;
}
@@ -50553,6 +54955,7 @@ static void walRestartHdr(Wal *pWal, u32 salt1){
memcpy(&pWal->hdr.aSalt[1], &salt1, 4);
walIndexWriteHdr(pWal);
pInfo->nBackfill = 0;
+ pInfo->nBackfillAttempted = 0;
pInfo->aReadMark[1] = 0;
for(i=2; i<WAL_NREADER; i++) pInfo->aReadMark[i] = READMARK_NOT_USED;
assert( pInfo->aReadMark[0]==0 );
@@ -50662,6 +55065,8 @@ static int walCheckpoint(
i64 nSize; /* Current size of database file */
u32 nBackfill = pInfo->nBackfill;
+ pInfo->nBackfillAttempted = mxSafeFrame;
+
/* Sync the WAL to disk */
if( sync_flags ){
rc = sqlite3OsSync(pWal->pWalFd, sync_flags);
@@ -50954,7 +55359,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, 1)) ){
+ }else if( SQLITE_OK==(rc = walLockExclusive(pWal, WAL_WRITE_LOCK, 1)) ){
pWal->writeLock = 1;
if( SQLITE_OK==(rc = walIndexPage(pWal, 0, &page0)) ){
badHdr = walIndexTryHdr(pWal, pChanged);
@@ -51045,6 +55450,7 @@ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){
int mxI; /* Index of largest aReadMark[] value */
int i; /* Loop counter */
int rc = SQLITE_OK; /* Return code */
+ u32 mxFrame; /* Wal frame to lock to */
assert( pWal->readLock<0 ); /* Not currently locked */
@@ -51108,7 +55514,12 @@ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){
}
pInfo = walCkptInfo(pWal);
- if( !useWal && pInfo->nBackfill==pWal->hdr.mxFrame ){
+ if( !useWal && pInfo->nBackfill==pWal->hdr.mxFrame
+#ifdef SQLITE_ENABLE_SNAPSHOT
+ && (pWal->pSnapshot==0 || pWal->hdr.mxFrame==0
+ || 0==memcmp(&pWal->hdr, pWal->pSnapshot, sizeof(WalIndexHdr)))
+#endif
+ ){
/* The WAL has been completely backfilled (or it is empty).
** and can be safely ignored.
*/
@@ -51146,70 +55557,88 @@ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){
*/
mxReadMark = 0;
mxI = 0;
+ mxFrame = pWal->hdr.mxFrame;
+#ifdef SQLITE_ENABLE_SNAPSHOT
+ if( pWal->pSnapshot && pWal->pSnapshot->mxFrame<mxFrame ){
+ mxFrame = pWal->pSnapshot->mxFrame;
+ }
+#endif
for(i=1; i<WAL_NREADER; i++){
u32 thisMark = pInfo->aReadMark[i];
- if( mxReadMark<=thisMark && thisMark<=pWal->hdr.mxFrame ){
+ if( mxReadMark<=thisMark && thisMark<=mxFrame ){
assert( thisMark!=READMARK_NOT_USED );
mxReadMark = thisMark;
mxI = i;
}
}
- /* There was once an "if" here. The extra "{" is to preserve indentation. */
- {
- if( (pWal->readOnly & WAL_SHM_RDONLY)==0
- && (mxReadMark<pWal->hdr.mxFrame || mxI==0)
- ){
- for(i=1; i<WAL_NREADER; i++){
- rc = walLockExclusive(pWal, WAL_READ_LOCK(i), 1, 0);
- if( rc==SQLITE_OK ){
- mxReadMark = pInfo->aReadMark[i] = pWal->hdr.mxFrame;
- mxI = i;
- walUnlockExclusive(pWal, WAL_READ_LOCK(i), 1);
- break;
- }else if( rc!=SQLITE_BUSY ){
- return rc;
- }
+ if( (pWal->readOnly & WAL_SHM_RDONLY)==0
+ && (mxReadMark<mxFrame || mxI==0)
+ ){
+ for(i=1; i<WAL_NREADER; i++){
+ rc = walLockExclusive(pWal, WAL_READ_LOCK(i), 1);
+ if( rc==SQLITE_OK ){
+ mxReadMark = pInfo->aReadMark[i] = mxFrame;
+ mxI = i;
+ walUnlockExclusive(pWal, WAL_READ_LOCK(i), 1);
+ break;
+ }else if( rc!=SQLITE_BUSY ){
+ return rc;
}
}
- if( mxI==0 ){
- assert( rc==SQLITE_BUSY || (pWal->readOnly & WAL_SHM_RDONLY)!=0 );
- return rc==SQLITE_BUSY ? WAL_RETRY : SQLITE_READONLY_CANTLOCK;
- }
+ }
+ if( mxI==0 ){
+ assert( rc==SQLITE_BUSY || (pWal->readOnly & WAL_SHM_RDONLY)!=0 );
+ return rc==SQLITE_BUSY ? WAL_RETRY : SQLITE_READONLY_CANTLOCK;
+ }
- rc = walLockShared(pWal, WAL_READ_LOCK(mxI));
- if( rc ){
- return rc==SQLITE_BUSY ? WAL_RETRY : rc;
- }
- /* Now that the read-lock has been obtained, check that neither the
- ** value in the aReadMark[] array or the contents of the wal-index
- ** header have changed.
- **
- ** It is necessary to check that the wal-index header did not change
- ** between the time it was read and when the shared-lock was obtained
- ** on WAL_READ_LOCK(mxI) was obtained to account for the possibility
- ** that the log file may have been wrapped by a writer, or that frames
- ** that occur later in the log than pWal->hdr.mxFrame may have been
- ** copied into the database by a checkpointer. If either of these things
- ** happened, then reading the database with the current value of
- ** pWal->hdr.mxFrame risks reading a corrupted snapshot. So, retry
- ** instead.
- **
- ** This does not guarantee that the copy of the wal-index header is up to
- ** date before proceeding. That would not be possible without somehow
- ** blocking writers. It only guarantees that a dangerous checkpoint or
- ** log-wrap (either of which would require an exclusive lock on
- ** WAL_READ_LOCK(mxI)) has not occurred since the snapshot was valid.
- */
- walShmBarrier(pWal);
- if( pInfo->aReadMark[mxI]!=mxReadMark
- || memcmp((void *)walIndexHdr(pWal), &pWal->hdr, sizeof(WalIndexHdr))
- ){
- walUnlockShared(pWal, WAL_READ_LOCK(mxI));
- return WAL_RETRY;
- }else{
- assert( mxReadMark<=pWal->hdr.mxFrame );
- pWal->readLock = (i16)mxI;
- }
+ rc = walLockShared(pWal, WAL_READ_LOCK(mxI));
+ if( rc ){
+ return rc==SQLITE_BUSY ? WAL_RETRY : rc;
+ }
+ /* Now that the read-lock has been obtained, check that neither the
+ ** value in the aReadMark[] array or the contents of the wal-index
+ ** header have changed.
+ **
+ ** It is necessary to check that the wal-index header did not change
+ ** between the time it was read and when the shared-lock was obtained
+ ** on WAL_READ_LOCK(mxI) was obtained to account for the possibility
+ ** that the log file may have been wrapped by a writer, or that frames
+ ** that occur later in the log than pWal->hdr.mxFrame may have been
+ ** copied into the database by a checkpointer. If either of these things
+ ** happened, then reading the database with the current value of
+ ** pWal->hdr.mxFrame risks reading a corrupted snapshot. So, retry
+ ** instead.
+ **
+ ** Before checking that the live wal-index header has not changed
+ ** since it was read, set Wal.minFrame to the first frame in the wal
+ ** file that has not yet been checkpointed. This client will not need
+ ** to read any frames earlier than minFrame from the wal file - they
+ ** can be safely read directly from the database file.
+ **
+ ** Because a ShmBarrier() call is made between taking the copy of
+ ** nBackfill and checking that the wal-header in shared-memory still
+ ** matches the one cached in pWal->hdr, it is guaranteed that the
+ ** checkpointer that set nBackfill was not working with a wal-index
+ ** header newer than that cached in pWal->hdr. If it were, that could
+ ** cause a problem. The checkpointer could omit to checkpoint
+ ** a version of page X that lies before pWal->minFrame (call that version
+ ** A) on the basis that there is a newer version (version B) of the same
+ ** page later in the wal file. But if version B happens to like past
+ ** frame pWal->hdr.mxFrame - then the client would incorrectly assume
+ ** that it can read version A from the database file. However, since
+ ** we can guarantee that the checkpointer that set nBackfill could not
+ ** see any pages past pWal->hdr.mxFrame, this problem does not come up.
+ */
+ pWal->minFrame = pInfo->nBackfill+1;
+ walShmBarrier(pWal);
+ if( pInfo->aReadMark[mxI]!=mxReadMark
+ || memcmp((void *)walIndexHdr(pWal), &pWal->hdr, sizeof(WalIndexHdr))
+ ){
+ walUnlockShared(pWal, WAL_READ_LOCK(mxI));
+ return WAL_RETRY;
+ }else{
+ assert( mxReadMark<=pWal->hdr.mxFrame );
+ pWal->readLock = (i16)mxI;
}
return rc;
}
@@ -51232,6 +55661,14 @@ SQLITE_PRIVATE int sqlite3WalBeginReadTransaction(Wal *pWal, int *pChanged){
int rc; /* Return code */
int cnt = 0; /* Number of TryBeginRead attempts */
+#ifdef SQLITE_ENABLE_SNAPSHOT
+ int bChanged = 0;
+ WalIndexHdr *pSnapshot = pWal->pSnapshot;
+ if( pSnapshot && memcmp(pSnapshot, &pWal->hdr, sizeof(WalIndexHdr))!=0 ){
+ bChanged = 1;
+ }
+#endif
+
do{
rc = walTryBeginRead(pWal, pChanged, 0, ++cnt);
}while( rc==WAL_RETRY );
@@ -51239,6 +55676,66 @@ SQLITE_PRIVATE int sqlite3WalBeginReadTransaction(Wal *pWal, int *pChanged){
testcase( (rc&0xff)==SQLITE_IOERR );
testcase( rc==SQLITE_PROTOCOL );
testcase( rc==SQLITE_OK );
+
+#ifdef SQLITE_ENABLE_SNAPSHOT
+ if( rc==SQLITE_OK ){
+ if( pSnapshot && memcmp(pSnapshot, &pWal->hdr, sizeof(WalIndexHdr))!=0 ){
+ /* At this point the client has a lock on an aReadMark[] slot holding
+ ** a value equal to or smaller than pSnapshot->mxFrame, but pWal->hdr
+ ** is populated with the wal-index header corresponding to the head
+ ** of the wal file. Verify that pSnapshot is still valid before
+ ** continuing. Reasons why pSnapshot might no longer be valid:
+ **
+ ** (1) The WAL file has been reset since the snapshot was taken.
+ ** In this case, the salt will have changed.
+ **
+ ** (2) A checkpoint as been attempted that wrote frames past
+ ** pSnapshot->mxFrame into the database file. Note that the
+ ** checkpoint need not have completed for this to cause problems.
+ */
+ volatile WalCkptInfo *pInfo = walCkptInfo(pWal);
+
+ assert( pWal->readLock>0 || pWal->hdr.mxFrame==0 );
+ assert( pInfo->aReadMark[pWal->readLock]<=pSnapshot->mxFrame );
+
+ /* It is possible that there is a checkpointer thread running
+ ** concurrent with this code. If this is the case, it may be that the
+ ** checkpointer has already determined that it will checkpoint
+ ** snapshot X, where X is later in the wal file than pSnapshot, but
+ ** has not yet set the pInfo->nBackfillAttempted variable to indicate
+ ** its intent. To avoid the race condition this leads to, ensure that
+ ** there is no checkpointer process by taking a shared CKPT lock
+ ** before checking pInfo->nBackfillAttempted. */
+ rc = walLockShared(pWal, WAL_CKPT_LOCK);
+
+ if( rc==SQLITE_OK ){
+ /* Check that the wal file has not been wrapped. Assuming that it has
+ ** not, also check that no checkpointer has attempted to checkpoint any
+ ** frames beyond pSnapshot->mxFrame. If either of these conditions are
+ ** true, return SQLITE_BUSY_SNAPSHOT. Otherwise, overwrite pWal->hdr
+ ** with *pSnapshot and set *pChanged as appropriate for opening the
+ ** snapshot. */
+ if( !memcmp(pSnapshot->aSalt, pWal->hdr.aSalt, sizeof(pWal->hdr.aSalt))
+ && pSnapshot->mxFrame>=pInfo->nBackfillAttempted
+ ){
+ assert( pWal->readLock>0 );
+ memcpy(&pWal->hdr, pSnapshot, sizeof(WalIndexHdr));
+ *pChanged = bChanged;
+ }else{
+ rc = SQLITE_BUSY_SNAPSHOT;
+ }
+
+ /* Release the shared CKPT lock obtained above. */
+ walUnlockShared(pWal, WAL_CKPT_LOCK);
+ }
+
+
+ if( rc!=SQLITE_OK ){
+ sqlite3WalEndReadTransaction(pWal);
+ }
+ }
+ }
+#endif
return rc;
}
@@ -51270,6 +55767,7 @@ SQLITE_PRIVATE int sqlite3WalFindFrame(
u32 iRead = 0; /* If !=0, WAL frame to return data from */
u32 iLast = pWal->hdr.mxFrame; /* Last page in WAL for this reader */
int iHash; /* Used to loop through N hash tables */
+ int iMinHash;
/* This routine is only be called from within a read transaction. */
assert( pWal->readLock>=0 || pWal->lockError );
@@ -51310,7 +55808,8 @@ SQLITE_PRIVATE int sqlite3WalFindFrame(
** This condition filters out entries that were added to the hash
** table after the current read-transaction had started.
*/
- for(iHash=walFramePage(iLast); iHash>=0 && iRead==0; iHash--){
+ iMinHash = walFramePage(pWal->minFrame);
+ for(iHash=walFramePage(iLast); iHash>=iMinHash && iRead==0; iHash--){
volatile ht_slot *aHash; /* Pointer to hash table */
volatile u32 *aPgno; /* Pointer to array of page numbers */
u32 iZero; /* Frame number corresponding to aPgno[0] */
@@ -51325,7 +55824,7 @@ SQLITE_PRIVATE int sqlite3WalFindFrame(
nCollide = HASHTABLE_NSLOT;
for(iKey=walHash(pgno); aHash[iKey]; iKey=walNextHash(iKey)){
u32 iFrame = aHash[iKey] + iZero;
- if( iFrame<=iLast && aPgno[aHash[iKey]]==pgno ){
+ if( iFrame<=iLast && iFrame>=pWal->minFrame && aPgno[aHash[iKey]]==pgno ){
assert( iFrame>iRead || CORRUPT_DB );
iRead = iFrame;
}
@@ -51342,7 +55841,8 @@ SQLITE_PRIVATE int sqlite3WalFindFrame(
{
u32 iRead2 = 0;
u32 iTest;
- for(iTest=iLast; iTest>0; iTest--){
+ assert( pWal->minFrame>0 );
+ for(iTest=iLast; iTest>=pWal->minFrame; iTest--){
if( walFramePgno(pWal, iTest)==pgno ){
iRead2 = iTest;
break;
@@ -51408,6 +55908,7 @@ SQLITE_PRIVATE int sqlite3WalBeginWriteTransaction(Wal *pWal){
/* Cannot start a write transaction without first holding a read
** transaction. */
assert( pWal->readLock>=0 );
+ assert( pWal->writeLock==0 && pWal->iReCksum==0 );
if( pWal->readOnly ){
return SQLITE_READONLY;
@@ -51416,7 +55917,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, 0);
+ rc = walLockExclusive(pWal, WAL_WRITE_LOCK, 1);
if( rc ){
return rc;
}
@@ -51443,6 +55944,7 @@ SQLITE_PRIVATE int sqlite3WalEndWriteTransaction(Wal *pWal){
if( pWal->writeLock ){
walUnlockExclusive(pWal, WAL_WRITE_LOCK, 1);
pWal->writeLock = 0;
+ pWal->iReCksum = 0;
pWal->truncateOnCommit = 0;
}
return SQLITE_OK;
@@ -51561,7 +56063,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, 0);
+ rc = walLockExclusive(pWal, WAL_READ_LOCK(1), WAL_NREADER-1);
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
@@ -51649,7 +56151,7 @@ static int walWriteOneFrame(
void *pData; /* Data actually written */
u8 aFrame[WAL_FRAME_HDRSIZE]; /* Buffer to assemble frame-header in */
#if defined(SQLITE_HAS_CODEC)
- if( (pData = sqlite3PagerCodec(pPage))==0 ) return SQLITE_NOMEM;
+ if( (pData = sqlite3PagerCodec(pPage))==0 ) return SQLITE_NOMEM_BKPT;
#else
pData = pPage->pData;
#endif
@@ -51661,6 +56163,59 @@ static int walWriteOneFrame(
return rc;
}
+/*
+** This function is called as part of committing a transaction within which
+** one or more frames have been overwritten. It updates the checksums for
+** all frames written to the wal file by the current transaction starting
+** with the earliest to have been overwritten.
+**
+** SQLITE_OK is returned if successful, or an SQLite error code otherwise.
+*/
+static int walRewriteChecksums(Wal *pWal, u32 iLast){
+ const int szPage = pWal->szPage;/* Database page size */
+ int rc = SQLITE_OK; /* Return code */
+ u8 *aBuf; /* Buffer to load data from wal file into */
+ u8 aFrame[WAL_FRAME_HDRSIZE]; /* Buffer to assemble frame-headers in */
+ u32 iRead; /* Next frame to read from wal file */
+ i64 iCksumOff;
+
+ aBuf = sqlite3_malloc(szPage + WAL_FRAME_HDRSIZE);
+ if( aBuf==0 ) return SQLITE_NOMEM_BKPT;
+
+ /* Find the checksum values to use as input for the recalculating the
+ ** first checksum. If the first frame is frame 1 (implying that the current
+ ** transaction restarted the wal file), these values must be read from the
+ ** wal-file header. Otherwise, read them from the frame header of the
+ ** previous frame. */
+ assert( pWal->iReCksum>0 );
+ if( pWal->iReCksum==1 ){
+ iCksumOff = 24;
+ }else{
+ iCksumOff = walFrameOffset(pWal->iReCksum-1, szPage) + 16;
+ }
+ rc = sqlite3OsRead(pWal->pWalFd, aBuf, sizeof(u32)*2, iCksumOff);
+ pWal->hdr.aFrameCksum[0] = sqlite3Get4byte(aBuf);
+ pWal->hdr.aFrameCksum[1] = sqlite3Get4byte(&aBuf[sizeof(u32)]);
+
+ iRead = pWal->iReCksum;
+ pWal->iReCksum = 0;
+ for(; rc==SQLITE_OK && iRead<=iLast; iRead++){
+ i64 iOff = walFrameOffset(iRead, szPage);
+ rc = sqlite3OsRead(pWal->pWalFd, aBuf, szPage+WAL_FRAME_HDRSIZE, iOff);
+ if( rc==SQLITE_OK ){
+ u32 iPgno, nDbSize;
+ iPgno = sqlite3Get4byte(aBuf);
+ nDbSize = sqlite3Get4byte(&aBuf[4]);
+
+ walEncodeFrame(pWal, iPgno, nDbSize, &aBuf[WAL_FRAME_HDRSIZE], aFrame);
+ rc = sqlite3OsWrite(pWal->pWalFd, aFrame, sizeof(aFrame), iOff);
+ }
+ }
+
+ sqlite3_free(aBuf);
+ return rc;
+}
+
/*
** Write a set of frames to the log. The caller must hold the write-lock
** on the log file (obtained using sqlite3WalBeginWriteTransaction()).
@@ -51681,6 +56236,8 @@ SQLITE_PRIVATE int sqlite3WalFrames(
int szFrame; /* The size of a single frame */
i64 iOffset; /* Next byte to write in WAL file */
WalWriter w; /* The writer */
+ u32 iFirst = 0; /* First frame that may be overwritten */
+ WalIndexHdr *pLive; /* Pointer to shared header */
assert( pList );
assert( pWal->writeLock );
@@ -51696,6 +56253,11 @@ SQLITE_PRIVATE int sqlite3WalFrames(
}
#endif
+ pLive = (WalIndexHdr*)walIndexHdr(pWal);
+ if( memcmp(&pWal->hdr, (void *)pLive, sizeof(WalIndexHdr))!=0 ){
+ iFirst = pLive->mxFrame+1;
+ }
+
/* See if it is possible to write these frames into the start of the
** log file, instead of appending to it at pWal->hdr.mxFrame.
*/
@@ -51760,6 +56322,33 @@ SQLITE_PRIVATE int sqlite3WalFrames(
/* Write all frames into the log file exactly once */
for(p=pList; p; p=p->pDirty){
int nDbSize; /* 0 normally. Positive == commit flag */
+
+ /* Check if this page has already been written into the wal file by
+ ** the current transaction. If so, overwrite the existing frame and
+ ** set Wal.writeLock to WAL_WRITELOCK_RECKSUM - indicating that
+ ** checksums must be recomputed when the transaction is committed. */
+ if( iFirst && (p->pDirty || isCommit==0) ){
+ u32 iWrite = 0;
+ VVA_ONLY(rc =) sqlite3WalFindFrame(pWal, p->pgno, &iWrite);
+ assert( rc==SQLITE_OK || iWrite==0 );
+ if( iWrite>=iFirst ){
+ i64 iOff = walFrameOffset(iWrite, szPage) + WAL_FRAME_HDRSIZE;
+ void *pData;
+ if( pWal->iReCksum==0 || iWrite<pWal->iReCksum ){
+ pWal->iReCksum = iWrite;
+ }
+#if defined(SQLITE_HAS_CODEC)
+ if( (pData = sqlite3PagerCodec(p))==0 ) return SQLITE_NOMEM;
+#else
+ pData = p->pData;
+#endif
+ rc = sqlite3OsWrite(pWal->pWalFd, pData, szPage, iOff);
+ if( rc ) return rc;
+ p->flags &= ~PGHDR_WAL_APPEND;
+ continue;
+ }
+ }
+
iFrame++;
assert( iOffset==walFrameOffset(iFrame, szPage) );
nDbSize = (isCommit && p->pDirty==0) ? nTruncate : 0;
@@ -51767,6 +56356,13 @@ SQLITE_PRIVATE int sqlite3WalFrames(
if( rc ) return rc;
pLast = p;
iOffset += szFrame;
+ p->flags |= PGHDR_WAL_APPEND;
+ }
+
+ /* Recalculate checksums within the wal file if required. */
+ if( isCommit && pWal->iReCksum ){
+ rc = walRewriteChecksums(pWal, iFrame);
+ if( rc ) return rc;
}
/* If this is the end of a transaction, then we might need to pad
@@ -51818,6 +56414,7 @@ SQLITE_PRIVATE int sqlite3WalFrames(
*/
iFrame = pWal->hdr.mxFrame;
for(p=pList; p && rc==SQLITE_OK; p=p->pDirty){
+ if( (p->flags & PGHDR_WAL_APPEND)==0 ) continue;
iFrame++;
rc = walIndexAppend(pWal, iFrame, p->pgno);
}
@@ -51886,7 +56483,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, 0);
+ rc = walLockExclusive(pWal, WAL_CKPT_LOCK, 1);
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
@@ -51930,6 +56527,7 @@ SQLITE_PRIVATE int sqlite3WalCheckpoint(
/* Copy data from the log to the database file. */
if( rc==SQLITE_OK ){
+
if( pWal->hdr.mxFrame && walPagesize(pWal)!=nBuf ){
rc = SQLITE_CORRUPT_BKPT;
}else{
@@ -52045,6 +56643,52 @@ SQLITE_PRIVATE int sqlite3WalHeapMemory(Wal *pWal){
return (pWal && pWal->exclusiveMode==WAL_HEAPMEMORY_MODE );
}
+#ifdef SQLITE_ENABLE_SNAPSHOT
+/* Create a snapshot object. The content of a snapshot is opaque to
+** every other subsystem, so the WAL module can put whatever it needs
+** in the object.
+*/
+SQLITE_PRIVATE int sqlite3WalSnapshotGet(Wal *pWal, sqlite3_snapshot **ppSnapshot){
+ int rc = SQLITE_OK;
+ WalIndexHdr *pRet;
+
+ assert( pWal->readLock>=0 && pWal->writeLock==0 );
+
+ pRet = (WalIndexHdr*)sqlite3_malloc(sizeof(WalIndexHdr));
+ if( pRet==0 ){
+ rc = SQLITE_NOMEM_BKPT;
+ }else{
+ memcpy(pRet, &pWal->hdr, sizeof(WalIndexHdr));
+ *ppSnapshot = (sqlite3_snapshot*)pRet;
+ }
+
+ return rc;
+}
+
+/* Try to open on pSnapshot when the next read-transaction starts
+*/
+SQLITE_PRIVATE void sqlite3WalSnapshotOpen(Wal *pWal, sqlite3_snapshot *pSnapshot){
+ pWal->pSnapshot = (WalIndexHdr*)pSnapshot;
+}
+
+/*
+** Return a +ve value if snapshot p1 is newer than p2. A -ve value if
+** p1 is older than p2 and zero if p1 and p2 are the same snapshot.
+*/
+SQLITE_API int SQLITE_STDCALL sqlite3_snapshot_cmp(sqlite3_snapshot *p1, sqlite3_snapshot *p2){
+ WalIndexHdr *pHdr1 = (WalIndexHdr*)p1;
+ WalIndexHdr *pHdr2 = (WalIndexHdr*)p2;
+
+ /* aSalt[0] is a copy of the value stored in the wal file header. It
+ ** is incremented each time the wal file is restarted. */
+ if( pHdr1->aSalt[0]<pHdr2->aSalt[0] ) return -1;
+ if( pHdr1->aSalt[0]>pHdr2->aSalt[0] ) return +1;
+ if( pHdr1->mxFrame<pHdr2->mxFrame ) return -1;
+ if( pHdr1->mxFrame>pHdr2->mxFrame ) return +1;
+ return 0;
+}
+#endif /* SQLITE_ENABLE_SNAPSHOT */
+
#ifdef SQLITE_ENABLE_ZIPVFS
/*
** If the argument is not NULL, it points to a Wal object that holds a
@@ -52057,6 +56701,12 @@ SQLITE_PRIVATE int sqlite3WalFramesize(Wal *pWal){
}
#endif
+/* Return the sqlite3_file object for the WAL file
+*/
+SQLITE_PRIVATE sqlite3_file *sqlite3WalFile(Wal *pWal){
+ return pWal->pWalFd;
+}
+
#endif /* #ifndef SQLITE_OMIT_WAL */
/************** End of wal.c *************************************************/
@@ -52295,6 +56945,7 @@ SQLITE_PRIVATE int sqlite3WalFramesize(Wal *pWal){
** 4 Number of leaf pointers on this page
** * zero or more pages numbers of leaves
*/
+/* #include "sqliteInt.h" */
/* The following value is the maximum cell size assuming a maximum page
@@ -52312,6 +56963,7 @@ SQLITE_PRIVATE int sqlite3WalFramesize(Wal *pWal){
/* Forward declarations */
typedef struct MemPage MemPage;
typedef struct BtLock BtLock;
+typedef struct CellInfo CellInfo;
/*
** This is a magic string that appears at the beginning of every
@@ -52356,7 +57008,6 @@ struct MemPage {
u8 nOverflow; /* Number of overflow cell bodies in aCell[] */
u8 intKey; /* True if table b-trees. False for index b-trees */
u8 intKeyLeaf; /* True if the leaf of an intKey table */
- u8 noPayload; /* True if internal intKey page (thus w/o data) */
u8 leaf; /* True if a leaf page */
u8 hdrOffset; /* 100 for page 1. 0 otherwise */
u8 childPtrSize; /* 0 if leaf==1. 4 if leaf==0 */
@@ -52375,7 +57026,10 @@ struct MemPage {
u8 *aData; /* Pointer to disk image of the page data */
u8 *aDataEnd; /* One byte past the end of usable data */
u8 *aCellIdx; /* The cell index area */
+ u8 *aDataOfst; /* Same as aData for leaves. aData+4 for interior */
DbPage *pDbPage; /* Pager page handle */
+ u16 (*xCellSize)(MemPage*,u8*); /* cellSizePtr method */
+ void (*xParseCell)(MemPage*,u8*,CellInfo*); /* btreeParseCell method */
Pgno pgno; /* Page number for this page */
};
@@ -52431,6 +57085,7 @@ struct Btree {
u8 inTrans; /* TRANS_NONE, TRANS_READ or TRANS_WRITE */
u8 sharable; /* True if we can share pBt with another db */
u8 locked; /* True if db currently has pBt locked */
+ u8 hasIncrblobCur; /* True if there are one or more Incrblob cursors */
int wantToLock; /* Number of nested calls to sqlite3BtreeEnter() */
int nBackup; /* Number of backup operations reading this btree */
u32 iDataVersion; /* Combines with pBt->pPager->iDataVersion */
@@ -52541,13 +57196,11 @@ struct BtShared {
** about a cell. The parseCellPtr() function fills in this structure
** based on information extract from the raw disk page.
*/
-typedef struct CellInfo CellInfo;
struct CellInfo {
i64 nKey; /* The key for INTKEY tables, or nPayload otherwise */
u8 *pPayload; /* Pointer to the start of payload */
u32 nPayload; /* Bytes of payload */
u16 nLocal; /* Amount of payload held locally, not on overflow */
- u16 iOverflow; /* Offset to overflow page number. Zero if no overflow */
u16 nSize; /* Size of the cell content on the main b-tree page */
};
@@ -52584,8 +57237,7 @@ struct CellInfo {
struct BtCursor {
Btree *pBtree; /* The Btree to which this cursor belongs */
BtShared *pBt; /* The BtShared this cursor points to */
- BtCursor *pNext, *pPrev; /* Forms a linked list of all cursors */
- struct KeyInfo *pKeyInfo; /* Argument passed to comparison function */
+ BtCursor *pNext; /* Forms a linked list of all cursors */
Pgno *aOverflow; /* Cache of overflow page locations */
CellInfo info; /* A parse of the cell we are pointing at */
i64 nKey; /* Size of pKey, or last integer key */
@@ -52595,9 +57247,16 @@ struct BtCursor {
int skipNext; /* Prev() is noop if negative. Next() is noop if positive.
** Error code if eState==CURSOR_FAULT */
u8 curFlags; /* zero or more BTCF_* flags defined below */
+ u8 curPagerFlags; /* Flags to send to sqlite3PagerGet() */
u8 eState; /* One of the CURSOR_XXX constants (see below) */
- u8 hints; /* As configured by CursorSetHints() */
- i16 iPage; /* Index of current page in apPage */
+ u8 hints; /* As configured by CursorSetHints() */
+ /* All fields above are zeroed when the cursor is allocated. See
+ ** sqlite3BtreeCursorZero(). Fields that follow must be manually
+ ** initialized. */
+ i8 iPage; /* Index of current page in apPage */
+ u8 curIntKey; /* Value of apPage[0]->intKey */
+ struct KeyInfo *pKeyInfo; /* Argument passed to comparison function */
+ void *padding1; /* Make object size a multiple of 16 */
u16 aiIdx[BTCURSOR_MAX_DEPTH]; /* Current index in apPage[i] */
MemPage *apPage[BTCURSOR_MAX_DEPTH]; /* Pages from root to current page */
};
@@ -52610,6 +57269,7 @@ struct BtCursor {
#define BTCF_ValidOvfl 0x04 /* True if aOverflow is valid */
#define BTCF_AtLast 0x08 /* Cursor is pointing ot the last entry */
#define BTCF_Incrblob 0x10 /* True if an incremental I/O handle */
+#define BTCF_Multiple 0x20 /* Maybe another cursor on the same btree */
/*
** Potential values for BtCursor.eState.
@@ -52752,6 +57412,7 @@ struct IntegrityCk {
const char *zPfx; /* Error message prefix */
int v1, v2; /* Values for up to two %d fields in zPfx */
StrAccum errMsg; /* Accumulate the error message text here */
+ u32 *heap; /* Min-heap used for analyzing cell coverage */
};
/*
@@ -52762,6 +57423,23 @@ struct IntegrityCk {
#define get4byte sqlite3Get4byte
#define put4byte sqlite3Put4byte
+/*
+** get2byteAligned(), unlike get2byte(), requires that its argument point to a
+** two-byte aligned address. get2bytea() is only used for accessing the
+** cell addresses in a btree header.
+*/
+#if SQLITE_BYTEORDER==4321
+# define get2byteAligned(x) (*(u16*)(x))
+#elif SQLITE_BYTEORDER==1234 && !defined(SQLITE_DISABLE_INTRINSIC) \
+ && GCC_VERSION>=4008000
+# define get2byteAligned(x) __builtin_bswap16(*(u16*)(x))
+#elif SQLITE_BYTEORDER==1234 && !defined(SQLITE_DISABLE_INTRINSIC) \
+ && defined(_MSC_VER) && _MSC_VER>=1300
+# define get2byteAligned(x) _byteswap_ushort(*(u16*)(x))
+#else
+# define get2byteAligned(x) ((x)[0]<<8 | (x)[1])
+#endif
+
/************** End of btreeInt.h ********************************************/
/************** Continuing where we left off in btmutex.c ********************/
#ifndef SQLITE_OMIT_SHARED_CACHE
@@ -52917,21 +57595,6 @@ SQLITE_PRIVATE int sqlite3BtreeHoldsMutex(Btree *p){
#endif
-#ifndef SQLITE_OMIT_INCRBLOB
-/*
-** Enter and leave a mutex on a Btree given a cursor owned by that
-** Btree. These entry points are used by incremental I/O and can be
-** omitted if that module is not used.
-*/
-SQLITE_PRIVATE void sqlite3BtreeEnterCursor(BtCursor *pCur){
- sqlite3BtreeEnter(pCur->pBtree);
-}
-SQLITE_PRIVATE void sqlite3BtreeLeaveCursor(BtCursor *pCur){
- sqlite3BtreeLeave(pCur->pBtree);
-}
-#endif /* SQLITE_OMIT_INCRBLOB */
-
-
/*
** Enter the mutex on every Btree associated with a database
** connection. This is needed (for example) prior to parsing
@@ -52965,14 +57628,6 @@ SQLITE_PRIVATE void sqlite3BtreeLeaveAll(sqlite3 *db){
}
}
-/*
-** Return true if a particular Btree requires a lock. Return FALSE if
-** no lock is ever required since it is not sharable.
-*/
-SQLITE_PRIVATE int sqlite3BtreeSharable(Btree *p){
- return p->sharable;
-}
-
#ifndef NDEBUG
/*
** Return true if the current thread holds the database connection
@@ -53046,6 +57701,25 @@ SQLITE_PRIVATE void sqlite3BtreeEnterAll(sqlite3 *db){
}
}
#endif /* if SQLITE_THREADSAFE */
+
+#ifndef SQLITE_OMIT_INCRBLOB
+/*
+** Enter a mutex on a Btree given a cursor owned by that Btree.
+**
+** These entry points are used by incremental I/O only. Enter() is required
+** any time OMIT_SHARED_CACHE is not defined, regardless of whether or not
+** the build is threadsafe. Leave() is only required by threadsafe builds.
+*/
+SQLITE_PRIVATE void sqlite3BtreeEnterCursor(BtCursor *pCur){
+ sqlite3BtreeEnter(pCur->pBtree);
+}
+# if SQLITE_THREADSAFE
+SQLITE_PRIVATE void sqlite3BtreeLeaveCursor(BtCursor *pCur){
+ sqlite3BtreeLeave(pCur->pBtree);
+}
+# endif
+#endif /* ifndef SQLITE_OMIT_INCRBLOB */
+
#endif /* ifndef SQLITE_OMIT_SHARED_CACHE */
/************** End of btmutex.c *********************************************/
@@ -53065,6 +57739,7 @@ SQLITE_PRIVATE void sqlite3BtreeEnterAll(sqlite3 *db){
** See the header comment on "btreeInt.h" for additional information.
** Including a description of file format and an overview of operation.
*/
+/* #include "btreeInt.h" */
/*
** The header string that appears at the beginning of every
@@ -53401,7 +58076,7 @@ static int setSharedCacheTableLock(Btree *p, Pgno iTable, u8 eLock){
if( !pLock ){
pLock = (BtLock *)sqlite3MallocZero(sizeof(BtLock));
if( !pLock ){
- return SQLITE_NOMEM;
+ return SQLITE_NOMEM_BKPT;
}
pLock->iTable = iTable;
pLock->pBtree = p;
@@ -53501,6 +58176,10 @@ static void releasePage(MemPage *pPage); /* Forward reference */
static int cursorHoldsMutex(BtCursor *p){
return sqlite3_mutex_held(p->pBt->mutex);
}
+static int cursorOwnsBtShared(BtCursor *p){
+ assert( cursorHoldsMutex(p) );
+ return (p->pBtree->db==p->pBt->db);
+}
#endif
/*
@@ -53541,13 +58220,15 @@ static void invalidateIncrblobCursors(
int isClearTable /* True if all rows are being deleted */
){
BtCursor *p;
- BtShared *pBt = pBtree->pBt;
+ if( pBtree->hasIncrblobCur==0 ) return;
assert( sqlite3BtreeHoldsMutex(pBtree) );
- for(p=pBt->pCursor; p; p=p->pNext){
- if( (p->curFlags & BTCF_Incrblob)!=0
- && (isClearTable || p->info.nKey==iRow)
- ){
- p->eState = CURSOR_INVALID;
+ pBtree->hasIncrblobCur = 0;
+ for(p=pBtree->pBt->pCursor; p; p=p->pNext){
+ if( (p->curFlags & BTCF_Incrblob)!=0 ){
+ pBtree->hasIncrblobCur = 1;
+ if( isClearTable || p->info.nKey==iRow ){
+ p->eState = CURSOR_INVALID;
+ }
}
}
}
@@ -53598,7 +58279,7 @@ static int btreeSetHasContent(BtShared *pBt, Pgno pgno){
assert( pgno<=pBt->nPage );
pBt->pHasContent = sqlite3BitvecCreate(pBt->nPage);
if( !pBt->pHasContent ){
- rc = SQLITE_NOMEM;
+ rc = SQLITE_NOMEM_BKPT;
}
}
if( rc==SQLITE_OK && pgno<=sqlite3BitvecSize(pBt->pHasContent) ){
@@ -53640,26 +58321,25 @@ static void btreeReleaseAllCursorPages(BtCursor *pCur){
pCur->iPage = -1;
}
-
/*
-** Save the current cursor position in the variables BtCursor.nKey
-** and BtCursor.pKey. The cursor's state is set to CURSOR_REQUIRESEEK.
+** The cursor passed as the only argument must point to a valid entry
+** when this function is called (i.e. have eState==CURSOR_VALID). This
+** function saves the current cursor key in variables pCur->nKey and
+** pCur->pKey. SQLITE_OK is returned if successful or an SQLite error
+** code otherwise.
**
-** The caller must ensure that the cursor is valid (has eState==CURSOR_VALID)
-** prior to calling this routine.
+** If the cursor is open on an intkey table, then the integer key
+** (the rowid) is stored in pCur->nKey and pCur->pKey is left set to
+** NULL. If the cursor is open on a non-intkey table, then pCur->pKey is
+** set to point to a malloced buffer pCur->nKey bytes in size containing
+** the key.
*/
-static int saveCursorPosition(BtCursor *pCur){
+static int saveCursorKey(BtCursor *pCur){
int rc;
-
- assert( CURSOR_VALID==pCur->eState || CURSOR_SKIPNEXT==pCur->eState );
+ assert( CURSOR_VALID==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 */
@@ -53667,9 +58347,8 @@ static int saveCursorPosition(BtCursor *pCur){
** stores the integer key in pCur->nKey. In this case this value is
** all that is required. Otherwise, if pCur is not open on an intKey
** table, then malloc space for and store the pCur->nKey bytes of key
- ** data.
- */
- if( 0==pCur->apPage[0]->intKey ){
+ ** data. */
+ if( 0==pCur->curIntKey ){
void *pKey = sqlite3Malloc( pCur->nKey );
if( pKey ){
rc = sqlite3BtreeKey(pCur, 0, (int)pCur->nKey, pKey);
@@ -53679,17 +58358,40 @@ static int saveCursorPosition(BtCursor *pCur){
sqlite3_free(pKey);
}
}else{
- rc = SQLITE_NOMEM;
+ rc = SQLITE_NOMEM_BKPT;
}
}
- assert( !pCur->apPage[0]->intKey || !pCur->pKey );
+ assert( !pCur->curIntKey || !pCur->pKey );
+ return rc;
+}
+/*
+** Save the current cursor position in the variables BtCursor.nKey
+** and BtCursor.pKey. The cursor's state is set to CURSOR_REQUIRESEEK.
+**
+** The caller must ensure that the cursor is valid (has eState==CURSOR_VALID)
+** prior to calling this routine.
+*/
+static int saveCursorPosition(BtCursor *pCur){
+ int rc;
+
+ 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 = saveCursorKey(pCur);
if( rc==SQLITE_OK ){
btreeReleaseAllCursorPages(pCur);
pCur->eState = CURSOR_REQUIRESEEK;
}
- invalidateOverflowCache(pCur);
+ pCur->curFlags &= ~(BTCF_ValidNKey|BTCF_ValidOvfl|BTCF_AtLast);
return rc;
}
@@ -53704,6 +58406,15 @@ static int SQLITE_NOINLINE saveCursorsOnList(BtCursor*,Pgno,BtCursor*);
** routine is called just before cursor pExcept is used to modify the
** table, for example in BtreeDelete() or BtreeInsert().
**
+** If there are two or more cursors on the same btree, then all such
+** cursors should have their BTCF_Multiple flag set. The btreeCursor()
+** routine enforces that rule. This routine only needs to be called in
+** the uncommon case when pExpect has the BTCF_Multiple flag set.
+**
+** If pExpect!=NULL and if no other cursors are found on the same root-page,
+** then the BTCF_Multiple flag on pExpect is cleared, to avoid another
+** pointless call to this routine.
+**
** Implementation note: This routine merely checks to see if any cursors
** need to be saved. It calls out to saveCursorsOnList() in the (unusual)
** event that cursors are in need to being saved.
@@ -53715,7 +58426,9 @@ static int saveAllCursors(BtShared *pBt, Pgno iRoot, BtCursor *pExcept){
for(p=pBt->pCursor; p; p=p->pNext){
if( p!=pExcept && (0==iRoot || p->pgnoRoot==iRoot) ) break;
}
- return p ? saveCursorsOnList(p, iRoot, pExcept) : SQLITE_OK;
+ if( p ) return saveCursorsOnList(p, iRoot, pExcept);
+ if( pExcept ) pExcept->curFlags &= ~BTCF_Multiple;
+ return SQLITE_OK;
}
/* This helper routine to saveAllCursors does the actual work of saving
@@ -53777,7 +58490,7 @@ static int btreeMoveto(
pIdxKey = sqlite3VdbeAllocUnpackedRecord(
pCur->pKeyInfo, aSpace, sizeof(aSpace), &pFree
);
- if( pIdxKey==0 ) return SQLITE_NOMEM;
+ if( pIdxKey==0 ) return SQLITE_NOMEM_BKPT;
sqlite3VdbeRecordUnpack(pCur->pKeyInfo, (int)nKey, pKey, pIdxKey);
if( pIdxKey->nField==0 ){
sqlite3DbFree(pCur->pKeyInfo->db, pFree);
@@ -53803,7 +58516,7 @@ static int btreeMoveto(
static int btreeRestoreCursorPosition(BtCursor *pCur){
int rc;
int skipNext;
- assert( cursorHoldsMutex(pCur) );
+ assert( cursorOwnsBtShared(pCur) );
assert( pCur->eState>=CURSOR_REQUIRESEEK );
if( pCur->eState==CURSOR_FAULT ){
return pCur->skipNext;
@@ -53875,6 +58588,26 @@ SQLITE_PRIVATE int sqlite3BtreeCursorRestore(BtCursor *pCur, int *pDifferentRow)
return SQLITE_OK;
}
+#ifdef SQLITE_ENABLE_CURSOR_HINTS
+/*
+** Provide hints to the cursor. The particular hint given (and the type
+** and number of the varargs parameters) is determined by the eHintType
+** parameter. See the definitions of the BTREE_HINT_* macros for details.
+*/
+SQLITE_PRIVATE void sqlite3BtreeCursorHint(BtCursor *pCur, int eHintType, ...){
+ /* Used only by system that substitute their own storage engine */
+}
+#endif
+
+/*
+** Provide flag hints to the cursor.
+*/
+SQLITE_PRIVATE void sqlite3BtreeCursorHintFlags(BtCursor *pCur, unsigned x){
+ assert( x==BTREE_SEEK_EQ || x==BTREE_BULKLOAD || x==0 );
+ pCur->hints = x;
+}
+
+
#ifndef SQLITE_OMIT_AUTOVACUUM
/*
** Given a page number of a regular database page, return the page
@@ -53928,7 +58661,7 @@ static void ptrmapPut(BtShared *pBt, Pgno key, u8 eType, Pgno parent, int *pRC){
return;
}
iPtrmap = PTRMAP_PAGENO(pBt, key);
- rc = sqlite3PagerGet(pBt->pPager, iPtrmap, &pDbPage);
+ rc = sqlite3PagerGet(pBt->pPager, iPtrmap, &pDbPage, 0);
if( rc!=SQLITE_OK ){
*pRC = rc;
return;
@@ -53971,7 +58704,7 @@ static int ptrmapGet(BtShared *pBt, Pgno key, u8 *pEType, Pgno *pPgno){
assert( sqlite3_mutex_held(pBt->mutex) );
iPtrmap = PTRMAP_PAGENO(pBt, key);
- rc = sqlite3PagerGet(pBt->pPager, iPtrmap, &pDbPage);
+ rc = sqlite3PagerGet(pBt->pPager, iPtrmap, &pDbPage, 0);
if( rc!=0 ){
return rc;
}
@@ -54003,39 +58736,85 @@ static int ptrmapGet(BtShared *pBt, Pgno key, u8 *pEType, Pgno *pPgno){
** the page, 1 means the second cell, and so forth) return a pointer
** to the cell content.
**
+** findCellPastPtr() does the same except it skips past the initial
+** 4-byte child pointer found on interior pages, if there is one.
+**
** This routine works only for pages that do not contain overflow cells.
*/
#define findCell(P,I) \
- ((P)->aData + ((P)->maskPage & get2byte(&(P)->aCellIdx[2*(I)])))
-#define findCellv2(D,M,O,I) (D+(M&get2byte(D+(O+2*(I)))))
+ ((P)->aData + ((P)->maskPage & get2byteAligned(&(P)->aCellIdx[2*(I)])))
+#define findCellPastPtr(P,I) \
+ ((P)->aDataOfst + ((P)->maskPage & get2byteAligned(&(P)->aCellIdx[2*(I)])))
/*
-** This a more complex version of findCell() that works for
-** pages that do contain overflow cells.
+** This is common tail processing for btreeParseCellPtr() and
+** btreeParseCellPtrIndex() for the case when the cell does not fit entirely
+** on a single B-tree page. Make necessary adjustments to the CellInfo
+** structure.
*/
-static u8 *findOverflowCell(MemPage *pPage, int iCell){
- int i;
- assert( sqlite3_mutex_held(pPage->pBt->mutex) );
- for(i=pPage->nOverflow-1; i>=0; i--){
- int k;
- k = pPage->aiOvfl[i];
- if( k<=iCell ){
- if( k==iCell ){
- return pPage->apOvfl[i];
- }
- iCell--;
- }
+static SQLITE_NOINLINE void btreeParseCellAdjustSizeForOverflow(
+ MemPage *pPage, /* Page containing the cell */
+ u8 *pCell, /* Pointer to the cell text. */
+ CellInfo *pInfo /* Fill in this structure */
+){
+ /* If the payload will not fit completely on the local page, we have
+ ** to decide how much to store locally and how much to spill onto
+ ** overflow pages. The strategy is to minimize the amount of unused
+ ** space on overflow pages while keeping the amount of local storage
+ ** in between minLocal and maxLocal.
+ **
+ ** Warning: changing the way overflow payload is distributed in any
+ ** way will result in an incompatible file format.
+ */
+ int minLocal; /* Minimum amount of payload held locally */
+ int maxLocal; /* Maximum amount of payload held locally */
+ int surplus; /* Overflow payload available for local storage */
+
+ minLocal = pPage->minLocal;
+ maxLocal = pPage->maxLocal;
+ surplus = minLocal + (pInfo->nPayload - minLocal)%(pPage->pBt->usableSize-4);
+ testcase( surplus==maxLocal );
+ testcase( surplus==maxLocal+1 );
+ if( surplus <= maxLocal ){
+ pInfo->nLocal = (u16)surplus;
+ }else{
+ pInfo->nLocal = (u16)minLocal;
}
- return findCell(pPage, iCell);
+ pInfo->nSize = (u16)(&pInfo->pPayload[pInfo->nLocal] - pCell) + 4;
}
/*
-** Parse a cell content block and fill in the CellInfo structure. There
-** are two versions of this function. btreeParseCell() takes a
-** cell index as the second argument and btreeParseCellPtr()
-** takes a pointer to the body of the cell as its second argument.
+** The following routines are implementations of the MemPage.xParseCell()
+** method.
+**
+** Parse a cell content block and fill in the CellInfo structure.
+**
+** btreeParseCellPtr() => table btree leaf nodes
+** btreeParseCellNoPayload() => table btree internal nodes
+** btreeParseCellPtrIndex() => index btree nodes
+**
+** There is also a wrapper function btreeParseCell() that works for
+** all MemPage types and that references the cell by index rather than
+** by pointer.
*/
+static void btreeParseCellPtrNoPayload(
+ MemPage *pPage, /* Page containing the cell */
+ u8 *pCell, /* Pointer to the cell text. */
+ CellInfo *pInfo /* Fill in this structure */
+){
+ assert( sqlite3_mutex_held(pPage->pBt->mutex) );
+ assert( pPage->leaf==0 );
+ assert( pPage->childPtrSize==4 );
+#ifndef SQLITE_DEBUG
+ UNUSED_PARAMETER(pPage);
+#endif
+ pInfo->nSize = 4 + getVarint(&pCell[4], (u64*)&pInfo->nKey);
+ pInfo->nPayload = 0;
+ pInfo->nLocal = 0;
+ pInfo->pPayload = 0;
+ return;
+}
static void btreeParseCellPtr(
MemPage *pPage, /* Page containing the cell */
u8 *pCell, /* Pointer to the cell text. */
@@ -54043,26 +58822,52 @@ static void btreeParseCellPtr(
){
u8 *pIter; /* For scanning through pCell */
u32 nPayload; /* Number of bytes of cell payload */
+ u64 iKey; /* Extracted Key value */
assert( sqlite3_mutex_held(pPage->pBt->mutex) );
assert( pPage->leaf==0 || pPage->leaf==1 );
- if( pPage->intKeyLeaf ){
- assert( pPage->childPtrSize==0 );
- pIter = pCell + getVarint32(pCell, nPayload);
- pIter += getVarint(pIter, (u64*)&pInfo->nKey);
- }else if( pPage->noPayload ){
- assert( pPage->childPtrSize==4 );
- pInfo->nSize = 4 + getVarint(&pCell[4], (u64*)&pInfo->nKey);
- pInfo->nPayload = 0;
- pInfo->nLocal = 0;
- pInfo->iOverflow = 0;
- pInfo->pPayload = 0;
- return;
- }else{
- pIter = pCell + pPage->childPtrSize;
- pIter += getVarint32(pIter, nPayload);
- pInfo->nKey = nPayload;
+ assert( pPage->intKeyLeaf );
+ assert( pPage->childPtrSize==0 );
+ pIter = pCell;
+
+ /* The next block of code is equivalent to:
+ **
+ ** pIter += getVarint32(pIter, nPayload);
+ **
+ ** The code is inlined to avoid a function call.
+ */
+ nPayload = *pIter;
+ if( nPayload>=0x80 ){
+ u8 *pEnd = &pIter[8];
+ nPayload &= 0x7f;
+ do{
+ nPayload = (nPayload<<7) | (*++pIter & 0x7f);
+ }while( (*pIter)>=0x80 && pIter<pEnd );
+ }
+ pIter++;
+
+ /* The next block of code is equivalent to:
+ **
+ ** pIter += getVarint(pIter, (u64*)&pInfo->nKey);
+ **
+ ** The code is inlined to avoid a function call.
+ */
+ iKey = *pIter;
+ if( iKey>=0x80 ){
+ u8 *pEnd = &pIter[7];
+ iKey &= 0x7f;
+ while(1){
+ iKey = (iKey<<7) | (*++pIter & 0x7f);
+ if( (*pIter)<0x80 ) break;
+ if( pIter>=pEnd ){
+ iKey = (iKey<<8) | *++pIter;
+ break;
+ }
+ }
}
+ pIter++;
+
+ pInfo->nKey = *(i64*)&iKey;
pInfo->nPayload = nPayload;
pInfo->pPayload = pIter;
testcase( nPayload==pPage->maxLocal );
@@ -54074,33 +58879,45 @@ static void btreeParseCellPtr(
pInfo->nSize = nPayload + (u16)(pIter - pCell);
if( pInfo->nSize<4 ) pInfo->nSize = 4;
pInfo->nLocal = (u16)nPayload;
- pInfo->iOverflow = 0;
}else{
- /* If the payload will not fit completely on the local page, we have
- ** to decide how much to store locally and how much to spill onto
- ** overflow pages. The strategy is to minimize the amount of unused
- ** space on overflow pages while keeping the amount of local storage
- ** in between minLocal and maxLocal.
- **
- ** Warning: changing the way overflow payload is distributed in any
- ** way will result in an incompatible file format.
- */
- int minLocal; /* Minimum amount of payload held locally */
- int maxLocal; /* Maximum amount of payload held locally */
- int surplus; /* Overflow payload available for local storage */
+ btreeParseCellAdjustSizeForOverflow(pPage, pCell, pInfo);
+ }
+}
+static void btreeParseCellPtrIndex(
+ MemPage *pPage, /* Page containing the cell */
+ u8 *pCell, /* Pointer to the cell text. */
+ CellInfo *pInfo /* Fill in this structure */
+){
+ u8 *pIter; /* For scanning through pCell */
+ u32 nPayload; /* Number of bytes of cell payload */
- minLocal = pPage->minLocal;
- maxLocal = pPage->maxLocal;
- surplus = minLocal + (nPayload - minLocal)%(pPage->pBt->usableSize - 4);
- testcase( surplus==maxLocal );
- testcase( surplus==maxLocal+1 );
- if( surplus <= maxLocal ){
- pInfo->nLocal = (u16)surplus;
- }else{
- pInfo->nLocal = (u16)minLocal;
- }
- pInfo->iOverflow = (u16)(&pInfo->pPayload[pInfo->nLocal] - pCell);
- pInfo->nSize = pInfo->iOverflow + 4;
+ assert( sqlite3_mutex_held(pPage->pBt->mutex) );
+ assert( pPage->leaf==0 || pPage->leaf==1 );
+ assert( pPage->intKeyLeaf==0 );
+ pIter = pCell + pPage->childPtrSize;
+ nPayload = *pIter;
+ if( nPayload>=0x80 ){
+ u8 *pEnd = &pIter[8];
+ nPayload &= 0x7f;
+ do{
+ nPayload = (nPayload<<7) | (*++pIter & 0x7f);
+ }while( *(pIter)>=0x80 && pIter<pEnd );
+ }
+ pIter++;
+ pInfo->nKey = nPayload;
+ pInfo->nPayload = nPayload;
+ pInfo->pPayload = pIter;
+ testcase( nPayload==pPage->maxLocal );
+ testcase( nPayload==pPage->maxLocal+1 );
+ if( nPayload<=pPage->maxLocal ){
+ /* This is the (easy) common case where the entire payload fits
+ ** on the local page. No overflow is required.
+ */
+ pInfo->nSize = nPayload + (u16)(pIter - pCell);
+ if( pInfo->nSize<4 ) pInfo->nSize = 4;
+ pInfo->nLocal = (u16)nPayload;
+ }else{
+ btreeParseCellAdjustSizeForOverflow(pPage, pCell, pInfo);
}
}
static void btreeParseCell(
@@ -54108,14 +58925,20 @@ static void btreeParseCell(
int iCell, /* The cell index. First cell is 0 */
CellInfo *pInfo /* Fill in this structure */
){
- btreeParseCellPtr(pPage, findCell(pPage, iCell), pInfo);
+ pPage->xParseCell(pPage, findCell(pPage, iCell), pInfo);
}
/*
+** The following routines are implementations of the MemPage.xCellSize
+** method.
+**
** Compute the total number of bytes that a Cell needs in the cell
** data area of the btree-page. The return number includes the cell
** data header and the local payload, but not any overflow page or
** the space used by the cell pointer.
+**
+** cellSizePtrNoPayload() => table internal nodes
+** cellSizePtr() => all index nodes & table leaf nodes
*/
static u16 cellSizePtr(MemPage *pPage, u8 *pCell){
u8 *pIter = pCell + pPage->childPtrSize; /* For looping over bytes of pCell */
@@ -54128,18 +58951,12 @@ static u16 cellSizePtr(MemPage *pPage, u8 *pCell){
** cell. If SQLITE_DEBUG is defined, an assert() at the bottom of
** this function verifies that this invariant is not violated. */
CellInfo debuginfo;
- btreeParseCellPtr(pPage, pCell, &debuginfo);
+ pPage->xParseCell(pPage, pCell, &debuginfo);
#endif
- if( pPage->noPayload ){
- pEnd = &pIter[9];
- while( (*pIter++)&0x80 && pIter<pEnd );
- assert( pPage->childPtrSize==4 );
- return (u16)(pIter - pCell);
- }
nSize = *pIter;
if( nSize>=0x80 ){
- pEnd = &pIter[9];
+ pEnd = &pIter[8];
nSize &= 0x7f;
do{
nSize = (nSize<<7) | (*++pIter & 0x7f);
@@ -54171,12 +58988,34 @@ static u16 cellSizePtr(MemPage *pPage, u8 *pCell){
assert( nSize==debuginfo.nSize || CORRUPT_DB );
return (u16)nSize;
}
+static u16 cellSizePtrNoPayload(MemPage *pPage, u8 *pCell){
+ u8 *pIter = pCell + 4; /* For looping over bytes of pCell */
+ u8 *pEnd; /* End mark for a varint */
+
+#ifdef SQLITE_DEBUG
+ /* The value returned by this function should always be the same as
+ ** the (CellInfo.nSize) value found by doing a full parse of the
+ ** cell. If SQLITE_DEBUG is defined, an assert() at the bottom of
+ ** this function verifies that this invariant is not violated. */
+ CellInfo debuginfo;
+ pPage->xParseCell(pPage, pCell, &debuginfo);
+#else
+ UNUSED_PARAMETER(pPage);
+#endif
+
+ assert( pPage->childPtrSize==4 );
+ pEnd = pIter + 9;
+ while( (*pIter++)&0x80 && pIter<pEnd );
+ assert( debuginfo.nSize==(u16)(pIter - pCell) || CORRUPT_DB );
+ return (u16)(pIter - pCell);
+}
+
#ifdef SQLITE_DEBUG
/* This variation on cellSizePtr() is used inside of assert() statements
** only. */
static u16 cellSize(MemPage *pPage, int iCell){
- return cellSizePtr(pPage, findCell(pPage, iCell));
+ return pPage->xCellSize(pPage, findCell(pPage, iCell));
}
#endif
@@ -54190,9 +59029,9 @@ static void ptrmapPutOvflPtr(MemPage *pPage, u8 *pCell, int *pRC){
CellInfo info;
if( *pRC ) return;
assert( pCell!=0 );
- btreeParseCellPtr(pPage, pCell, &info);
- if( info.iOverflow ){
- Pgno ovfl = get4byte(&pCell[info.iOverflow]);
+ pPage->xParseCell(pPage, pCell, &info);
+ if( info.nLocal<info.nPayload ){
+ Pgno ovfl = get4byte(&pCell[info.nSize-4]);
ptrmapPut(pPage->pBt, ovfl, PTRMAP_OVERFLOW1, pPage->pgno, pRC);
}
}
@@ -54247,26 +59086,18 @@ static int defragmentPage(MemPage *pPage){
pc = get2byte(pAddr);
testcase( pc==iCellFirst );
testcase( pc==iCellLast );
-#if !defined(SQLITE_ENABLE_OVERSIZE_CELL_CHECK)
/* These conditions have already been verified in btreeInitPage()
- ** if SQLITE_ENABLE_OVERSIZE_CELL_CHECK is defined
+ ** if PRAGMA cell_size_check=ON.
*/
if( pc<iCellFirst || pc>iCellLast ){
return SQLITE_CORRUPT_BKPT;
}
-#endif
assert( pc>=iCellFirst && pc<=iCellLast );
- size = cellSizePtr(pPage, &src[pc]);
+ size = pPage->xCellSize(pPage, &src[pc]);
cbrk -= size;
-#if defined(SQLITE_ENABLE_OVERSIZE_CELL_CHECK)
- if( cbrk<iCellFirst ){
- return SQLITE_CORRUPT_BKPT;
- }
-#else
if( cbrk<iCellFirst || pc+size>usableSize ){
return SQLITE_CORRUPT_BKPT;
}
-#endif
assert( cbrk+size<=usableSize && cbrk>=iCellFirst );
testcase( cbrk+size==usableSize );
testcase( pc+size==usableSize );
@@ -54304,18 +59135,20 @@ static int defragmentPage(MemPage *pPage){
** This function may detect corruption within pPg. If corruption is
** detected then *pRc is set to SQLITE_CORRUPT and NULL is returned.
**
-** If a slot of at least nByte bytes is found but cannot be used because
-** there are already at least 60 fragmented bytes on the page, return NULL.
-** In this case, if pbDefrag parameter is not NULL, set *pbDefrag to true.
+** Slots on the free list that are between 1 and 3 bytes larger than nByte
+** will be ignored if adding the extra space to the fragmentation count
+** causes the fragmentation count to exceed 60.
*/
-static u8 *pageFindSlot(MemPage *pPg, int nByte, int *pRc, int *pbDefrag){
+static u8 *pageFindSlot(MemPage *pPg, int nByte, int *pRc){
const int hdr = pPg->hdrOffset;
u8 * const aData = pPg->aData;
- int iAddr;
- int pc;
+ int iAddr = hdr + 1;
+ int pc = get2byte(&aData[iAddr]);
+ int x;
int usableSize = pPg->pBt->usableSize;
- for(iAddr=hdr+1; (pc = get2byte(&aData[iAddr]))>0; iAddr=pc){
+ assert( pc>0 );
+ do{
int size; /* Size of the free slot */
/* EVIDENCE-OF: R-06866-39125 Freeblocks are always connected in order of
** increasing offset. */
@@ -54327,24 +59160,21 @@ static u8 *pageFindSlot(MemPage *pPg, int nByte, int *pRc, int *pbDefrag){
** freeblock form a big-endian integer which is the size of the freeblock
** in bytes, including the 4-byte header. */
size = get2byte(&aData[pc+2]);
- if( size>=nByte ){
- int x = size - nByte;
+ if( (x = size - nByte)>=0 ){
testcase( x==4 );
testcase( x==3 );
- if( x<4 ){
+ if( pc < pPg->cellOffset+2*pPg->nCell || size+pc > usableSize ){
+ *pRc = SQLITE_CORRUPT_BKPT;
+ return 0;
+ }else if( x<4 ){
/* EVIDENCE-OF: R-11498-58022 In a well-formed b-tree page, the total
** number of bytes in fragments may not exceed 60. */
- if( aData[hdr+7]>=60 ){
- if( pbDefrag ) *pbDefrag = 1;
- return 0;
- }
+ if( aData[hdr+7]>57 ) return 0;
+
/* Remove the slot from the free-list. Update the number of
** fragmented bytes within the page. */
memcpy(&aData[iAddr], &aData[pc], 2);
aData[hdr+7] += (u8)x;
- }else if( size+pc > usableSize ){
- *pRc = SQLITE_CORRUPT_BKPT;
- return 0;
}else{
/* The slot remains on the free-list. Reduce its size to account
** for the portion used by the new allocation. */
@@ -54352,7 +59182,9 @@ static u8 *pageFindSlot(MemPage *pPg, int nByte, int *pRc, int *pbDefrag){
}
return &aData[pc + x];
}
- }
+ iAddr = pc;
+ pc = get2byte(&aData[pc]);
+ }while( pc );
return 0;
}
@@ -54393,8 +59225,15 @@ static int allocateSpace(MemPage *pPage, int nByte, int *pIdx){
** then the cell content offset of an empty page wants to be 65536.
** However, that integer is too large to be stored in a 2-byte unsigned
** integer, so a value of 0 is used in its place. */
- top = get2byteNotZero(&data[hdr+5]);
- if( gap>top ) return SQLITE_CORRUPT_BKPT;
+ top = get2byte(&data[hdr+5]);
+ assert( top<=(int)pPage->pBt->usableSize ); /* Prevent by getAndInitPage() */
+ if( gap>top ){
+ if( top==0 && pPage->pBt->usableSize==65536 ){
+ top = 65536;
+ }else{
+ return SQLITE_CORRUPT_BKPT;
+ }
+ }
/* If there is enough space between gap and top for one more cell pointer
** array entry offset, and if the freelist is not empty, then search the
@@ -54403,15 +59242,14 @@ static int allocateSpace(MemPage *pPage, int nByte, int *pIdx){
testcase( gap+2==top );
testcase( gap+1==top );
testcase( gap==top );
- if( gap+2<=top && (data[hdr+1] || data[hdr+2]) ){
- int bDefrag = 0;
- u8 *pSpace = pageFindSlot(pPage, nByte, &rc, &bDefrag);
- if( rc ) return rc;
- if( bDefrag ) goto defragment_page;
+ if( (data[hdr+2] || data[hdr+1]) && gap+2<=top ){
+ u8 *pSpace = pageFindSlot(pPage, nByte, &rc);
if( pSpace ){
assert( pSpace>=data && (pSpace - data)<65536 );
*pIdx = (int)(pSpace - data);
return SQLITE_OK;
+ }else if( rc ){
+ return rc;
}
}
@@ -54420,7 +59258,6 @@ static int allocateSpace(MemPage *pPage, int nByte, int *pIdx){
*/
testcase( gap+2+nByte==top );
if( gap+2+nByte>top ){
- defragment_page:
assert( pPage->nCell>0 || CORRUPT_DB );
rc = defragmentPage(pPage);
if( rc ) return rc;
@@ -54467,7 +59304,7 @@ static int freeSpace(MemPage *pPage, u16 iStart, u16 iSize){
assert( pPage->pBt!=0 );
assert( sqlite3PagerIswriteable(pPage->pDbPage) );
- assert( iStart>=pPage->hdrOffset+6+pPage->childPtrSize );
+ assert( CORRUPT_DB || iStart>=pPage->hdrOffset+6+pPage->childPtrSize );
assert( CORRUPT_DB || iEnd <= pPage->pBt->usableSize );
assert( sqlite3_mutex_held(pPage->pBt->mutex) );
assert( iSize>=4 ); /* Minimum cell size is 4 */
@@ -54496,7 +59333,7 @@ static int freeSpace(MemPage *pPage, u16 iStart, u16 iSize){
/* At this point:
** iFreeBlk: First freeblock after iStart, or zero if none
- ** iPtr: The address of a pointer iFreeBlk
+ ** iPtr: The address of a pointer to iFreeBlk
**
** Check to see if iFreeBlk should be coalesced onto the end of iStart.
*/
@@ -54504,6 +59341,7 @@ static int freeSpace(MemPage *pPage, u16 iStart, u16 iSize){
nFrag = iFreeBlk - iEnd;
if( iEnd>iFreeBlk ) return SQLITE_CORRUPT_BKPT;
iEnd = iFreeBlk + get2byte(&data[iFreeBlk+2]);
+ if( iEnd > pPage->pBt->usableSize ) return SQLITE_CORRUPT_BKPT;
iSize = iEnd - iStart;
iFreeBlk = get2byte(&data[iFreeBlk]);
}
@@ -54561,29 +59399,36 @@ static int decodeFlags(MemPage *pPage, int flagByte){
pPage->leaf = (u8)(flagByte>>3); assert( PTF_LEAF == 1<<3 );
flagByte &= ~PTF_LEAF;
pPage->childPtrSize = 4-4*pPage->leaf;
+ pPage->xCellSize = cellSizePtr;
pBt = pPage->pBt;
if( flagByte==(PTF_LEAFDATA | PTF_INTKEY) ){
- /* EVIDENCE-OF: R-03640-13415 A value of 5 means the page is an interior
- ** table b-tree page. */
+ /* EVIDENCE-OF: R-07291-35328 A value of 5 (0x05) means the page is an
+ ** interior table b-tree page. */
assert( (PTF_LEAFDATA|PTF_INTKEY)==5 );
- /* EVIDENCE-OF: R-20501-61796 A value of 13 means the page is a leaf
- ** table b-tree page. */
+ /* EVIDENCE-OF: R-26900-09176 A value of 13 (0x0d) means the page is a
+ ** leaf table b-tree page. */
assert( (PTF_LEAFDATA|PTF_INTKEY|PTF_LEAF)==13 );
pPage->intKey = 1;
- pPage->intKeyLeaf = pPage->leaf;
- pPage->noPayload = !pPage->leaf;
+ if( pPage->leaf ){
+ pPage->intKeyLeaf = 1;
+ pPage->xParseCell = btreeParseCellPtr;
+ }else{
+ pPage->intKeyLeaf = 0;
+ pPage->xCellSize = cellSizePtrNoPayload;
+ pPage->xParseCell = btreeParseCellPtrNoPayload;
+ }
pPage->maxLocal = pBt->maxLeaf;
pPage->minLocal = pBt->minLeaf;
}else if( flagByte==PTF_ZERODATA ){
- /* EVIDENCE-OF: R-27225-53936 A value of 2 means the page is an interior
- ** index b-tree page. */
+ /* EVIDENCE-OF: R-43316-37308 A value of 2 (0x02) means the page is an
+ ** interior index b-tree page. */
assert( (PTF_ZERODATA)==2 );
- /* EVIDENCE-OF: R-16571-11615 A value of 10 means the page is a leaf
- ** index b-tree page. */
+ /* EVIDENCE-OF: R-59615-42828 A value of 10 (0x0a) means the page is a
+ ** leaf index b-tree page. */
assert( (PTF_ZERODATA|PTF_LEAF)==10 );
pPage->intKey = 0;
pPage->intKeyLeaf = 0;
- pPage->noPayload = 0;
+ pPage->xParseCell = btreeParseCellPtrIndex;
pPage->maxLocal = pBt->maxLocal;
pPage->minLocal = pBt->minLocal;
}else{
@@ -54607,6 +59452,7 @@ static int decodeFlags(MemPage *pPage, int flagByte){
static int btreeInitPage(MemPage *pPage){
assert( pPage->pBt!=0 );
+ assert( pPage->pBt->db!=0 );
assert( sqlite3_mutex_held(pPage->pBt->mutex) );
assert( pPage->pgno==sqlite3PagerPagenumber(pPage->pDbPage) );
assert( pPage == sqlite3PagerGetExtra(pPage->pDbPage) );
@@ -54638,6 +59484,7 @@ static int btreeInitPage(MemPage *pPage){
pPage->cellOffset = cellOffset = hdr + 8 + pPage->childPtrSize;
pPage->aDataEnd = &data[usableSize];
pPage->aCellIdx = &data[cellOffset];
+ pPage->aDataOfst = &data[pPage->childPtrSize];
/* EVIDENCE-OF: R-58015-48175 The two-byte integer at offset 5 designates
** the start of the cell content area. A zero value for this integer is
** interpreted as 65536. */
@@ -54665,20 +59512,19 @@ static int btreeInitPage(MemPage *pPage){
*/
iCellFirst = cellOffset + 2*pPage->nCell;
iCellLast = usableSize - 4;
-#if defined(SQLITE_ENABLE_OVERSIZE_CELL_CHECK)
- {
+ if( pBt->db->flags & SQLITE_CellSizeCk ){
int i; /* Index into the cell pointer array */
int sz; /* Size of a cell */
if( !pPage->leaf ) iCellLast--;
for(i=0; i<pPage->nCell; i++){
- pc = get2byte(&data[cellOffset+i*2]);
+ pc = get2byteAligned(&data[cellOffset+i*2]);
testcase( pc==iCellFirst );
testcase( pc==iCellLast );
if( pc<iCellFirst || pc>iCellLast ){
return SQLITE_CORRUPT_BKPT;
}
- sz = cellSizePtr(pPage, &data[pc]);
+ sz = pPage->xCellSize(pPage, &data[pc]);
testcase( pc+sz==usableSize );
if( pc+sz>usableSize ){
return SQLITE_CORRUPT_BKPT;
@@ -54686,7 +59532,6 @@ static int btreeInitPage(MemPage *pPage){
}
if( !pPage->leaf ) iCellLast++;
}
-#endif
/* Compute the total free space on the page
** EVIDENCE-OF: R-23588-34450 The two-byte integer at offset 1 gives the
@@ -54759,6 +59604,7 @@ static void zeroPage(MemPage *pPage, int flags){
pPage->cellOffset = first;
pPage->aDataEnd = &data[pBt->usableSize];
pPage->aCellIdx = &data[first];
+ pPage->aDataOfst = &data[pPage->childPtrSize];
pPage->nOverflow = 0;
assert( pBt->pageSize>=512 && pBt->pageSize<=65536 );
pPage->maskPage = (u16)(pBt->pageSize - 1);
@@ -54773,20 +59619,23 @@ static void zeroPage(MemPage *pPage, int flags){
*/
static MemPage *btreePageFromDbPage(DbPage *pDbPage, Pgno pgno, BtShared *pBt){
MemPage *pPage = (MemPage*)sqlite3PagerGetExtra(pDbPage);
- pPage->aData = sqlite3PagerGetData(pDbPage);
- pPage->pDbPage = pDbPage;
- pPage->pBt = pBt;
- pPage->pgno = pgno;
- pPage->hdrOffset = pPage->pgno==1 ? 100 : 0;
+ if( pgno!=pPage->pgno ){
+ pPage->aData = sqlite3PagerGetData(pDbPage);
+ pPage->pDbPage = pDbPage;
+ pPage->pBt = pBt;
+ pPage->pgno = pgno;
+ pPage->hdrOffset = pgno==1 ? 100 : 0;
+ }
+ assert( pPage->aData==sqlite3PagerGetData(pDbPage) );
return pPage;
}
/*
** Get a page from the pager. Initialize the MemPage.pBt and
-** MemPage.aData elements if needed.
+** MemPage.aData elements if needed. See also: btreeGetUnusedPage().
**
-** If the noContent flag is set, it means that we do not care about
-** the content of the page at this time. So do not go to the disk
+** If the PAGER_GET_NOCONTENT flag is set, it means that we do not care
+** about the content of the page at this time. So do not go to the disk
** to fetch the content. Just fill in the content with zeros for now.
** If in the future we call sqlite3PagerWrite() on this page, that
** means we have started to be concerned about content and the disk
@@ -54803,7 +59652,7 @@ static int btreeGetPage(
assert( flags==0 || flags==PAGER_GET_NOCONTENT || flags==PAGER_GET_READONLY );
assert( sqlite3_mutex_held(pBt->mutex) );
- rc = sqlite3PagerAcquire(pBt->pPager, pgno, (DbPage**)&pDbPage, flags);
+ rc = sqlite3PagerGet(pBt->pPager, pgno, (DbPage**)&pDbPage, flags);
if( rc ) return rc;
*ppPage = btreePageFromDbPage(pDbPage, pgno, pBt);
return SQLITE_OK;
@@ -54838,35 +59687,63 @@ SQLITE_PRIVATE u32 sqlite3BtreeLastPage(Btree *p){
}
/*
-** Get a page from the pager and initialize it. This routine is just a
-** convenience wrapper around separate calls to btreeGetPage() and
-** btreeInitPage().
+** Get a page from the pager and initialize it.
+**
+** If pCur!=0 then the page is being fetched as part of a moveToChild()
+** call. Do additional sanity checking on the page in this case.
+** And if the fetch fails, this routine must decrement pCur->iPage.
**
-** If an error occurs, then the value *ppPage is set to is undefined. It
+** The page is fetched as read-write unless pCur is not NULL and is
+** a read-only cursor.
+**
+** If an error occurs, then *ppPage is undefined. It
** may remain unchanged, or it may be set to an invalid value.
*/
static int getAndInitPage(
BtShared *pBt, /* The database file */
Pgno pgno, /* Number of the page to get */
MemPage **ppPage, /* Write the page pointer here */
- int bReadonly /* PAGER_GET_READONLY or 0 */
+ BtCursor *pCur, /* Cursor to receive the page, or NULL */
+ int bReadOnly /* True for a read-only page */
){
int rc;
+ DbPage *pDbPage;
assert( sqlite3_mutex_held(pBt->mutex) );
- assert( bReadonly==PAGER_GET_READONLY || bReadonly==0 );
+ assert( pCur==0 || ppPage==&pCur->apPage[pCur->iPage] );
+ assert( pCur==0 || bReadOnly==pCur->curPagerFlags );
+ assert( pCur==0 || pCur->iPage>0 );
if( pgno>btreePagecount(pBt) ){
rc = SQLITE_CORRUPT_BKPT;
- }else{
- rc = btreeGetPage(pBt, pgno, ppPage, bReadonly);
- if( rc==SQLITE_OK && (*ppPage)->isInit==0 ){
- rc = btreeInitPage(*ppPage);
- if( rc!=SQLITE_OK ){
- releasePage(*ppPage);
- }
+ goto getAndInitPage_error;
+ }
+ rc = sqlite3PagerGet(pBt->pPager, pgno, (DbPage**)&pDbPage, bReadOnly);
+ if( rc ){
+ goto getAndInitPage_error;
+ }
+ *ppPage = (MemPage*)sqlite3PagerGetExtra(pDbPage);
+ if( (*ppPage)->isInit==0 ){
+ btreePageFromDbPage(pDbPage, pgno, pBt);
+ rc = btreeInitPage(*ppPage);
+ if( rc!=SQLITE_OK ){
+ releasePage(*ppPage);
+ goto getAndInitPage_error;
}
}
+ assert( (*ppPage)->pgno==pgno );
+ assert( (*ppPage)->aData==sqlite3PagerGetData(pDbPage) );
+
+ /* If obtaining a child page for a cursor, we must verify that the page is
+ ** compatible with the root page. */
+ if( pCur && ((*ppPage)->nCell<1 || (*ppPage)->intKey!=pCur->curIntKey) ){
+ rc = SQLITE_CORRUPT_BKPT;
+ releasePage(*ppPage);
+ goto getAndInitPage_error;
+ }
+ return SQLITE_OK;
+getAndInitPage_error:
+ if( pCur ) pCur->iPage--;
testcase( pgno==0 );
assert( pgno!=0 || rc==SQLITE_CORRUPT );
return rc;
@@ -54876,18 +59753,49 @@ static int getAndInitPage(
** Release a MemPage. This should be called once for each prior
** call to btreeGetPage.
*/
+static void releasePageNotNull(MemPage *pPage){
+ assert( pPage->aData );
+ assert( pPage->pBt );
+ assert( pPage->pDbPage!=0 );
+ assert( sqlite3PagerGetExtra(pPage->pDbPage) == (void*)pPage );
+ assert( sqlite3PagerGetData(pPage->pDbPage)==pPage->aData );
+ assert( sqlite3_mutex_held(pPage->pBt->mutex) );
+ sqlite3PagerUnrefNotNull(pPage->pDbPage);
+}
static void releasePage(MemPage *pPage){
- if( pPage ){
- assert( pPage->aData );
- assert( pPage->pBt );
- assert( pPage->pDbPage!=0 );
- assert( sqlite3PagerGetExtra(pPage->pDbPage) == (void*)pPage );
- assert( sqlite3PagerGetData(pPage->pDbPage)==pPage->aData );
- assert( sqlite3_mutex_held(pPage->pBt->mutex) );
- sqlite3PagerUnrefNotNull(pPage->pDbPage);
+ if( pPage ) releasePageNotNull(pPage);
+}
+
+/*
+** Get an unused page.
+**
+** This works just like btreeGetPage() with the addition:
+**
+** * If the page is already in use for some other purpose, immediately
+** release it and return an SQLITE_CURRUPT error.
+** * Make sure the isInit flag is clear
+*/
+static int btreeGetUnusedPage(
+ BtShared *pBt, /* The btree */
+ Pgno pgno, /* Number of the page to fetch */
+ MemPage **ppPage, /* Return the page in this parameter */
+ int flags /* PAGER_GET_NOCONTENT or PAGER_GET_READONLY */
+){
+ int rc = btreeGetPage(pBt, pgno, ppPage, flags);
+ if( rc==SQLITE_OK ){
+ if( sqlite3PagerPageRefcount((*ppPage)->pDbPage)>1 ){
+ releasePage(*ppPage);
+ *ppPage = 0;
+ return SQLITE_CORRUPT_BKPT;
+ }
+ (*ppPage)->isInit = 0;
+ }else{
+ *ppPage = 0;
}
+ return rc;
}
+
/*
** During a rollback, when the pager reloads information into the cache
** so that the cache is restored to its original state at the start of
@@ -54994,7 +59902,7 @@ SQLITE_PRIVATE int sqlite3BtreeOpen(
}
p = sqlite3MallocZero(sizeof(Btree));
if( !p ){
- return SQLITE_NOMEM;
+ return SQLITE_NOMEM_BKPT;
}
p->inTrans = TRANS_NONE;
p->db = db;
@@ -55018,7 +59926,7 @@ SQLITE_PRIVATE int sqlite3BtreeOpen(
p->sharable = 1;
if( !zFullPathname ){
sqlite3_free(p);
- return SQLITE_NOMEM;
+ return SQLITE_NOMEM_BKPT;
}
if( isMemdb ){
memcpy(zFullPathname, zFilename, nFilename);
@@ -55086,7 +59994,7 @@ SQLITE_PRIVATE int sqlite3BtreeOpen(
pBt = sqlite3MallocZero( sizeof(*pBt) );
if( pBt==0 ){
- rc = SQLITE_NOMEM;
+ rc = SQLITE_NOMEM_BKPT;
goto btree_open_out;
}
rc = sqlite3PagerOpen(pVfs, &pBt->pPager, zFilename,
@@ -55155,8 +60063,7 @@ SQLITE_PRIVATE int sqlite3BtreeOpen(
if( SQLITE_THREADSAFE && sqlite3GlobalConfig.bCoreMutex ){
pBt->mutex = sqlite3MutexAlloc(SQLITE_MUTEX_FAST);
if( pBt->mutex==0 ){
- rc = SQLITE_NOMEM;
- db->mallocFailed = 0;
+ rc = SQLITE_NOMEM_BKPT;
goto btree_open_out;
}
}
@@ -55179,12 +60086,12 @@ SQLITE_PRIVATE int sqlite3BtreeOpen(
for(i=0; i<db->nDb; i++){
if( (pSib = db->aDb[i].pBt)!=0 && pSib->sharable ){
while( pSib->pPrev ){ pSib = pSib->pPrev; }
- if( p->pBt<pSib->pBt ){
+ if( (uptr)p->pBt<(uptr)pSib->pBt ){
p->pNext = pSib;
p->pPrev = 0;
pSib->pPrev = p;
}else{
- while( pSib->pNext && pSib->pNext->pBt<p->pBt ){
+ while( pSib->pNext && (uptr)pSib->pNext->pBt<(uptr)p->pBt ){
pSib = pSib->pNext;
}
p->pNext = pSib->pNext;
@@ -55366,19 +60273,11 @@ SQLITE_PRIVATE int sqlite3BtreeClose(Btree *p){
}
/*
-** Change the limit on the number of pages allowed in the cache.
-**
-** The maximum number of cache pages is set to the absolute
-** value of mxPage. If mxPage is negative, the pager will
-** operate asynchronously - it will not stop to do fsync()s
-** to insure data is written to the disk surface before
-** continuing. Transactions still work if synchronous is off,
-** and the database cannot be corrupted if this program
-** crashes. But if the operating system crashes or there is
-** an abrupt power failure when synchronous is off, the database
-** could be left in an inconsistent and unrecoverable state.
-** Synchronous is on by default so database corruption is not
-** normally a worry.
+** Change the "soft" limit on the number of pages in the cache.
+** Unused and unmodified pages will be recycled when the number of
+** pages in the cache exceeds this soft limit. But the size of the
+** cache is allowed to grow larger than this limit if it contains
+** dirty pages or pages still in active use.
*/
SQLITE_PRIVATE int sqlite3BtreeSetCacheSize(Btree *p, int mxPage){
BtShared *pBt = p->pBt;
@@ -55389,6 +60288,26 @@ SQLITE_PRIVATE int sqlite3BtreeSetCacheSize(Btree *p, int mxPage){
return SQLITE_OK;
}
+/*
+** Change the "spill" limit on the number of pages in the cache.
+** If the number of pages exceeds this limit during a write transaction,
+** the pager might attempt to "spill" pages to the journal early in
+** order to free up memory.
+**
+** The value returned is the current spill size. If zero is passed
+** as an argument, no changes are made to the spill size setting, so
+** using mxPage of 0 is a way to query the current spill size.
+*/
+SQLITE_PRIVATE int sqlite3BtreeSetSpillSize(Btree *p, int mxPage){
+ BtShared *pBt = p->pBt;
+ int res;
+ assert( sqlite3_mutex_held(p->db->mutex) );
+ sqlite3BtreeEnter(p);
+ res = sqlite3PagerSetSpillsize(pBt->pPager, mxPage);
+ sqlite3BtreeLeave(p);
+ return res;
+}
+
#if SQLITE_MAX_MMAP_SIZE>0
/*
** Change the limit on the amount of the database file that may be
@@ -55427,21 +60346,6 @@ SQLITE_PRIVATE int sqlite3BtreeSetPagerFlags(
#endif
/*
-** Return TRUE if the given btree is set to safety level 1. In other
-** words, return TRUE if no sync() occurs on the disk files.
-*/
-SQLITE_PRIVATE int sqlite3BtreeSyncDisabled(Btree *p){
- BtShared *pBt = p->pBt;
- int rc;
- assert( sqlite3_mutex_held(p->db->mutex) );
- sqlite3BtreeEnter(p);
- assert( pBt && pBt->pPager );
- rc = sqlite3PagerNosync(pBt->pPager);
- sqlite3BtreeLeave(p);
- return rc;
-}
-
-/*
** Change the default pages size and the number of reserved bytes per page.
** Or, if the page size has already been fixed, return SQLITE_READONLY
** without changing anything.
@@ -55686,9 +60590,25 @@ static int lockBtree(BtShared *pBt){
rc = sqlite3PagerOpenWal(pBt->pPager, &isOpen);
if( rc!=SQLITE_OK ){
goto page1_init_failed;
- }else if( isOpen==0 ){
- releasePage(pPage1);
- return SQLITE_OK;
+ }else{
+#if SQLITE_DEFAULT_SYNCHRONOUS!=SQLITE_DEFAULT_WAL_SYNCHRONOUS
+ sqlite3 *db;
+ Db *pDb;
+ if( (db=pBt->db)!=0 && (pDb=db->aDb)!=0 ){
+ while( pDb->pBt==0 || pDb->pBt->pBt!=pBt ){ pDb++; }
+ if( pDb->bSyncSet==0
+ && pDb->safety_level==SQLITE_DEFAULT_SYNCHRONOUS+1
+ ){
+ pDb->safety_level = SQLITE_DEFAULT_WAL_SYNCHRONOUS+1;
+ sqlite3PagerSetFlags(pBt->pPager,
+ pDb->safety_level | (db->flags & PAGER_FLAGS_MASK));
+ }
+ }
+#endif
+ if( isOpen==0 ){
+ releasePage(pPage1);
+ return SQLITE_OK;
+ }
}
rc = SQLITE_NOTADB;
}
@@ -55830,7 +60750,7 @@ static void unlockBtreeIfUnused(BtShared *pBt){
assert( pPage1->aData );
assert( sqlite3PagerRefcount(pBt->pPager)==1 );
pBt->pPage1 = 0;
- releasePage(pPage1);
+ releasePageNotNull(pPage1);
}
}
@@ -55928,7 +60848,6 @@ SQLITE_PRIVATE int sqlite3BtreeNewDb(Btree *p){
** proceed.
*/
SQLITE_PRIVATE int sqlite3BtreeBeginTrans(Btree *p, int wrflag){
- sqlite3 *pBlock = 0;
BtShared *pBt = p->pBt;
int rc = SQLITE_OK;
@@ -55951,27 +60870,30 @@ SQLITE_PRIVATE int sqlite3BtreeBeginTrans(Btree *p, int wrflag){
}
#ifndef SQLITE_OMIT_SHARED_CACHE
- /* If another database handle has already opened a write transaction
- ** on this shared-btree structure and a second write transaction is
- ** requested, return SQLITE_LOCKED.
- */
- if( (wrflag && pBt->inTransaction==TRANS_WRITE)
- || (pBt->btsFlags & BTS_PENDING)!=0
- ){
- pBlock = pBt->pWriter->db;
- }else if( wrflag>1 ){
- BtLock *pIter;
- for(pIter=pBt->pLock; pIter; pIter=pIter->pNext){
- if( pIter->pBtree!=p ){
- pBlock = pIter->pBtree->db;
- break;
+ {
+ sqlite3 *pBlock = 0;
+ /* If another database handle has already opened a write transaction
+ ** on this shared-btree structure and a second write transaction is
+ ** requested, return SQLITE_LOCKED.
+ */
+ if( (wrflag && pBt->inTransaction==TRANS_WRITE)
+ || (pBt->btsFlags & BTS_PENDING)!=0
+ ){
+ pBlock = pBt->pWriter->db;
+ }else if( wrflag>1 ){
+ BtLock *pIter;
+ for(pIter=pBt->pLock; pIter; pIter=pIter->pNext){
+ if( pIter->pBtree!=p ){
+ pBlock = pIter->pBtree->db;
+ break;
+ }
}
}
- }
- if( pBlock ){
- sqlite3ConnectionBlocked(p->db, pBlock);
- rc = SQLITE_LOCKED_SHAREDCACHE;
- goto trans_begun;
+ if( pBlock ){
+ sqlite3ConnectionBlocked(p->db, pBlock);
+ rc = SQLITE_LOCKED_SHAREDCACHE;
+ goto trans_begun;
+ }
}
#endif
@@ -56135,20 +61057,22 @@ static int modifyPagePointer(MemPage *pPage, Pgno iFrom, Pgno iTo, u8 eType){
u8 isInitOrig = pPage->isInit;
int i;
int nCell;
+ int rc;
- btreeInitPage(pPage);
+ rc = btreeInitPage(pPage);
+ if( rc ) return rc;
nCell = pPage->nCell;
for(i=0; i<nCell; i++){
u8 *pCell = findCell(pPage, i);
if( eType==PTRMAP_OVERFLOW1 ){
CellInfo info;
- btreeParseCellPtr(pPage, pCell, &info);
- if( info.iOverflow
- && pCell+info.iOverflow+3<=pPage->aData+pPage->maskPage
- && iFrom==get4byte(&pCell[info.iOverflow])
+ pPage->xParseCell(pPage, pCell, &info);
+ if( info.nLocal<info.nPayload
+ && pCell+info.nSize-1<=pPage->aData+pPage->maskPage
+ && iFrom==get4byte(pCell+info.nSize-4)
){
- put4byte(&pCell[info.iOverflow], iTo);
+ put4byte(pCell+info.nSize-4, iTo);
break;
}
}else{
@@ -56442,7 +61366,7 @@ SQLITE_PRIVATE int sqlite3BtreeIncrVacuum(Btree *p){
static int autoVacuumCommit(BtShared *pBt){
int rc = SQLITE_OK;
Pager *pPager = pBt->pPager;
- VVA_ONLY( int nRef = sqlite3PagerRefcount(pPager) );
+ VVA_ONLY( int nRef = sqlite3PagerRefcount(pPager); )
assert( sqlite3_mutex_held(pBt->mutex) );
invalidateAllOverflowCache(pBt);
@@ -56851,13 +61775,13 @@ SQLITE_PRIVATE int sqlite3BtreeSavepoint(Btree *p, int op, int iSavepoint){
** on the database already. If a write-cursor is requested, then
** the caller is assumed to have an open write transaction.
**
-** If wrFlag==0, then the cursor can only be used for reading.
-** If wrFlag==1, then the cursor can be used for reading or for
-** writing if other conditions for writing are also met. These
-** are the conditions that must be met in order for writing to
-** be allowed:
+** If the BTREE_WRCSR bit of wrFlag is clear, then the cursor can only
+** be used for reading. If the BTREE_WRCSR bit is set, then the cursor
+** can be used for reading or for writing if other conditions for writing
+** are also met. These are the conditions that must be met in order
+** for writing to be allowed:
**
-** 1: The cursor must have been opened with wrFlag==1
+** 1: The cursor must have been opened with wrFlag containing BTREE_WRCSR
**
** 2: Other database connections that share the same pager cache
** but which are not in the READ_UNCOMMITTED state may not have
@@ -56869,6 +61793,16 @@ SQLITE_PRIVATE int sqlite3BtreeSavepoint(Btree *p, int op, int iSavepoint){
**
** 4: There must be an active transaction.
**
+** The BTREE_FORDELETE bit of wrFlag may optionally be set if BTREE_WRCSR
+** is set. If FORDELETE is set, that is a hint to the implementation that
+** this cursor will only be used to seek to and delete entries of an index
+** as part of a larger DELETE statement. The FORDELETE hint is not used by
+** this implementation. But in a hypothetical alternative storage engine
+** in which index entries are automatically deleted when corresponding table
+** rows are deleted, the FORDELETE flag is a hint that all SEEK and DELETE
+** operations on this cursor can be no-ops and all READ operations can
+** return a null row (2-bytes: 0x01 0x00).
+**
** No checking is done to make sure that page iTable really is the
** root page of a b-tree. If it is not, then the cursor acquired
** will not work correctly.
@@ -56884,28 +61818,30 @@ static int btreeCursor(
BtCursor *pCur /* Space for new cursor */
){
BtShared *pBt = p->pBt; /* Shared b-tree handle */
+ BtCursor *pX; /* Looping over other all cursors */
assert( sqlite3BtreeHoldsMutex(p) );
- assert( wrFlag==0 || wrFlag==1 );
+ assert( wrFlag==0
+ || wrFlag==BTREE_WRCSR
+ || wrFlag==(BTREE_WRCSR|BTREE_FORDELETE)
+ );
/* The following assert statements verify that if this is a sharable
** b-tree database, the connection is holding the required table locks,
** and that no other connection has any open cursor that conflicts with
** this lock. */
- assert( hasSharedCacheTableLock(p, iTable, pKeyInfo!=0, wrFlag+1) );
+ assert( hasSharedCacheTableLock(p, iTable, pKeyInfo!=0, (wrFlag?2:1)) );
assert( wrFlag==0 || !hasReadConflicts(p, iTable) );
/* Assert that the caller has opened the required transaction. */
assert( p->inTrans>TRANS_NONE );
assert( wrFlag==0 || p->inTrans==TRANS_WRITE );
assert( pBt->pPage1 && pBt->pPage1->aData );
+ assert( wrFlag==0 || (pBt->btsFlags & BTS_READ_ONLY)==0 );
- if( NEVER(wrFlag && (pBt->btsFlags & BTS_READ_ONLY)!=0) ){
- return SQLITE_READONLY;
- }
if( wrFlag ){
allocateTempSpace(pBt);
- if( pBt->pTmpSpace==0 ) return SQLITE_NOMEM;
+ if( pBt->pTmpSpace==0 ) return SQLITE_NOMEM_BKPT;
}
if( iTable==1 && btreePagecount(pBt)==0 ){
assert( wrFlag==0 );
@@ -56919,12 +61855,17 @@ static int btreeCursor(
pCur->pKeyInfo = pKeyInfo;
pCur->pBtree = p;
pCur->pBt = pBt;
- assert( wrFlag==0 || wrFlag==BTCF_WriteFlag );
- pCur->curFlags = wrFlag;
- pCur->pNext = pBt->pCursor;
- if( pCur->pNext ){
- pCur->pNext->pPrev = pCur;
+ pCur->curFlags = wrFlag ? BTCF_WriteFlag : 0;
+ pCur->curPagerFlags = wrFlag ? 0 : PAGER_GET_READONLY;
+ /* If there are two or more cursors on the same btree, then all such
+ ** cursors *must* have the BTCF_Multiple flag set. */
+ for(pX=pBt->pCursor; pX; pX=pX->pNext){
+ if( pX->pgnoRoot==(Pgno)iTable ){
+ pX->curFlags |= BTCF_Multiple;
+ pCur->curFlags |= BTCF_Multiple;
+ }
}
+ pCur->pNext = pBt->pCursor;
pBt->pCursor = pCur;
pCur->eState = CURSOR_INVALID;
return SQLITE_OK;
@@ -56937,9 +61878,13 @@ SQLITE_PRIVATE int sqlite3BtreeCursor(
BtCursor *pCur /* Write new cursor here */
){
int rc;
- sqlite3BtreeEnter(p);
- rc = btreeCursor(p, iTable, wrFlag, pKeyInfo, pCur);
- sqlite3BtreeLeave(p);
+ if( iTable<1 ){
+ rc = SQLITE_CORRUPT_BKPT;
+ }else{
+ sqlite3BtreeEnter(p);
+ rc = btreeCursor(p, iTable, wrFlag, pKeyInfo, pCur);
+ sqlite3BtreeLeave(p);
+ }
return rc;
}
@@ -56978,13 +61923,18 @@ SQLITE_PRIVATE int sqlite3BtreeCloseCursor(BtCursor *pCur){
BtShared *pBt = pCur->pBt;
sqlite3BtreeEnter(pBtree);
sqlite3BtreeClearCursor(pCur);
- if( pCur->pPrev ){
- pCur->pPrev->pNext = pCur->pNext;
- }else{
+ assert( pBt->pCursor!=0 );
+ if( pBt->pCursor==pCur ){
pBt->pCursor = pCur->pNext;
- }
- if( pCur->pNext ){
- pCur->pNext->pPrev = pCur->pPrev;
+ }else{
+ BtCursor *pPrev = pBt->pCursor;
+ do{
+ if( pPrev->pNext==pCur ){
+ pPrev->pNext = pCur->pNext;
+ break;
+ }
+ pPrev = pPrev->pNext;
+ }while( ALWAYS(pPrev) );
}
for(i=0; i<=pCur->iPage; i++){
releasePage(pCur->apPage[i]);
@@ -57004,13 +61954,6 @@ SQLITE_PRIVATE int sqlite3BtreeCloseCursor(BtCursor *pCur){
**
** BtCursor.info is a cache of the information in the current cell.
** Using this cache reduces the number of calls to btreeParseCell().
-**
-** 2007-06-25: There is a bug in some versions of MSVC that cause the
-** compiler to crash when getCellInfo() is implemented as a macro.
-** But there is a measureable speed advantage to using the macro on gcc
-** (when less compiler optimizations like -Os or -O0 are used and the
-** compiler is not doing aggressive inlining.) So we use a real function
-** for MSVC and a macro for everything else. Ticket #2457.
*/
#ifndef NDEBUG
static void assertCellInfo(BtCursor *pCur){
@@ -57023,28 +61966,15 @@ SQLITE_PRIVATE int sqlite3BtreeCloseCursor(BtCursor *pCur){
#else
#define assertCellInfo(x)
#endif
-#ifdef _MSC_VER
- /* Use a real function in MSVC to work around bugs in that compiler. */
- static void getCellInfo(BtCursor *pCur){
- if( pCur->info.nSize==0 ){
- int iPage = pCur->iPage;
- btreeParseCell(pCur->apPage[iPage],pCur->aiIdx[iPage],&pCur->info);
- pCur->curFlags |= BTCF_ValidNKey;
- }else{
- assertCellInfo(pCur);
- }
- }
-#else /* if not _MSC_VER */
- /* Use a macro in all other compilers so that the function is inlined */
-#define getCellInfo(pCur) \
- if( pCur->info.nSize==0 ){ \
- int iPage = pCur->iPage; \
- btreeParseCell(pCur->apPage[iPage],pCur->aiIdx[iPage],&pCur->info); \
- pCur->curFlags |= BTCF_ValidNKey; \
- }else{ \
- assertCellInfo(pCur); \
+static SQLITE_NOINLINE void getCellInfo(BtCursor *pCur){
+ if( pCur->info.nSize==0 ){
+ int iPage = pCur->iPage;
+ pCur->curFlags |= BTCF_ValidNKey;
+ btreeParseCell(pCur->apPage[iPage],pCur->aiIdx[iPage],&pCur->info);
+ }else{
+ assertCellInfo(pCur);
}
-#endif /* _MSC_VER */
+}
#ifndef NDEBUG /* The next routine used only within assert() statements */
/*
@@ -57090,7 +62020,7 @@ SQLITE_PRIVATE int sqlite3BtreeKeySize(BtCursor *pCur, i64 *pSize){
** to return an integer result code for historical reasons.
*/
SQLITE_PRIVATE int sqlite3BtreeDataSize(BtCursor *pCur, u32 *pSize){
- assert( cursorHoldsMutex(pCur) );
+ assert( cursorOwnsBtShared(pCur) );
assert( pCur->eState==CURSOR_VALID );
assert( pCur->iPage>=0 );
assert( pCur->iPage<BTCURSOR_MAX_DEPTH );
@@ -57268,8 +62198,13 @@ static int accessPayload(
#endif
assert( offset+amt <= pCur->info.nPayload );
- if( &aPayload[pCur->info.nLocal] > &pPage->aData[pBt->usableSize] ){
- /* Trying to read or write past the end of the data is an error */
+ assert( aPayload > pPage->aData );
+ if( (uptr)(aPayload - pPage->aData) > (pBt->usableSize - pCur->info.nLocal) ){
+ /* Trying to read or write past the end of the data is an error. The
+ ** conditional above is really:
+ ** &aPayload[pCur->info.nLocal] > &pPage->aData[pBt->usableSize]
+ ** but is recast into its current form to avoid integer overflow problems
+ */
return SQLITE_CORRUPT_BKPT;
}
@@ -57309,7 +62244,7 @@ static int accessPayload(
pCur->aOverflow, nOvfl*2*sizeof(Pgno)
);
if( aNew==0 ){
- rc = SQLITE_NOMEM;
+ rc = SQLITE_NOMEM_BKPT;
}else{
pCur->nOvflAlloc = nOvfl*2;
pCur->aOverflow = aNew;
@@ -57337,7 +62272,9 @@ static int accessPayload(
/* If required, populate the overflow page-list cache. */
if( (pCur->curFlags & BTCF_ValidOvfl)!=0 ){
- assert(!pCur->aOverflow[iIdx] || pCur->aOverflow[iIdx]==nextPage);
+ assert( pCur->aOverflow[iIdx]==0
+ || pCur->aOverflow[iIdx]==nextPage
+ || CORRUPT_DB );
pCur->aOverflow[iIdx] = nextPage;
}
@@ -57407,7 +62344,7 @@ static int accessPayload(
{
DbPage *pDbPage;
- rc = sqlite3PagerAcquire(pBt->pPager, nextPage, &pDbPage,
+ rc = sqlite3PagerGet(pBt->pPager, nextPage, &pDbPage,
((eOp&0x01)==0 ? PAGER_GET_READONLY : 0)
);
if( rc==SQLITE_OK ){
@@ -57468,7 +62405,7 @@ SQLITE_PRIVATE int sqlite3BtreeData(BtCursor *pCur, u32 offset, u32 amt, void *p
}
#endif
- assert( cursorHoldsMutex(pCur) );
+ assert( cursorOwnsBtShared(pCur) );
rc = restoreCursorPosition(pCur);
if( rc==SQLITE_OK ){
assert( pCur->eState==CURSOR_VALID );
@@ -57506,7 +62443,7 @@ static const void *fetchPayload(
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( cursorOwnsBtShared(pCur) );
assert( pCur->aiIdx[pCur->iPage]<pCur->apPage[pCur->iPage]->nCell );
assert( pCur->info.nSize>0 );
assert( pCur->info.pPayload>pCur->apPage[pCur->iPage]->aData || CORRUPT_DB );
@@ -57550,31 +62487,21 @@ SQLITE_PRIVATE const void *sqlite3BtreeDataFetch(BtCursor *pCur, u32 *pAmt){
** vice-versa).
*/
static int moveToChild(BtCursor *pCur, u32 newPgno){
- int rc;
- int i = pCur->iPage;
- MemPage *pNewPage;
BtShared *pBt = pCur->pBt;
- assert( cursorHoldsMutex(pCur) );
+ assert( cursorOwnsBtShared(pCur) );
assert( pCur->eState==CURSOR_VALID );
assert( pCur->iPage<BTCURSOR_MAX_DEPTH );
assert( pCur->iPage>=0 );
if( pCur->iPage>=(BTCURSOR_MAX_DEPTH-1) ){
return SQLITE_CORRUPT_BKPT;
}
- rc = getAndInitPage(pBt, newPgno, &pNewPage,
- (pCur->curFlags & BTCF_WriteFlag)==0 ? PAGER_GET_READONLY : 0);
- if( rc ) return rc;
- pCur->apPage[i+1] = pNewPage;
- pCur->aiIdx[i+1] = 0;
- pCur->iPage++;
-
pCur->info.nSize = 0;
pCur->curFlags &= ~(BTCF_ValidNKey|BTCF_ValidOvfl);
- if( pNewPage->nCell<1 || pNewPage->intKey!=pCur->apPage[i]->intKey ){
- return SQLITE_CORRUPT_BKPT;
- }
- return SQLITE_OK;
+ pCur->iPage++;
+ pCur->aiIdx[pCur->iPage] = 0;
+ return getAndInitPage(pBt, newPgno, &pCur->apPage[pCur->iPage],
+ pCur, pCur->curPagerFlags);
}
#if SQLITE_DEBUG
@@ -57608,7 +62535,7 @@ static void assertParentIndex(MemPage *pParent, int iIdx, Pgno iChild){
** the largest cell index.
*/
static void moveToParent(BtCursor *pCur){
- assert( cursorHoldsMutex(pCur) );
+ assert( cursorOwnsBtShared(pCur) );
assert( pCur->eState==CURSOR_VALID );
assert( pCur->iPage>0 );
assert( pCur->apPage[pCur->iPage] );
@@ -57618,11 +62545,9 @@ static void moveToParent(BtCursor *pCur){
pCur->apPage[pCur->iPage]->pgno
);
testcase( pCur->aiIdx[pCur->iPage-1] > pCur->apPage[pCur->iPage-1]->nCell );
-
- releasePage(pCur->apPage[pCur->iPage]);
- pCur->iPage--;
pCur->info.nSize = 0;
pCur->curFlags &= ~(BTCF_ValidNKey|BTCF_ValidOvfl);
+ releasePageNotNull(pCur->apPage[pCur->iPage--]);
}
/*
@@ -57650,7 +62575,7 @@ static int moveToRoot(BtCursor *pCur){
MemPage *pRoot;
int rc = SQLITE_OK;
- assert( cursorHoldsMutex(pCur) );
+ assert( cursorOwnsBtShared(pCur) );
assert( CURSOR_INVALID < CURSOR_REQUIRESEEK );
assert( CURSOR_VALID < CURSOR_REQUIRESEEK );
assert( CURSOR_FAULT > CURSOR_REQUIRESEEK );
@@ -57663,18 +62588,23 @@ static int moveToRoot(BtCursor *pCur){
}
if( pCur->iPage>=0 ){
- while( pCur->iPage ) releasePage(pCur->apPage[pCur->iPage--]);
+ while( pCur->iPage ){
+ assert( pCur->apPage[pCur->iPage]!=0 );
+ releasePageNotNull(pCur->apPage[pCur->iPage--]);
+ }
}else if( pCur->pgnoRoot==0 ){
pCur->eState = CURSOR_INVALID;
return SQLITE_OK;
}else{
+ assert( pCur->iPage==(-1) );
rc = getAndInitPage(pCur->pBtree->pBt, pCur->pgnoRoot, &pCur->apPage[0],
- (pCur->curFlags & BTCF_WriteFlag)==0 ? PAGER_GET_READONLY : 0);
+ 0, pCur->curPagerFlags);
if( rc!=SQLITE_OK ){
pCur->eState = CURSOR_INVALID;
return rc;
}
pCur->iPage = 0;
+ pCur->curIntKey = pCur->apPage[0]->intKey;
}
pRoot = pCur->apPage[0];
assert( pRoot->pgno==pCur->pgnoRoot );
@@ -57724,7 +62654,7 @@ static int moveToLeftmost(BtCursor *pCur){
int rc = SQLITE_OK;
MemPage *pPage;
- assert( cursorHoldsMutex(pCur) );
+ assert( cursorOwnsBtShared(pCur) );
assert( pCur->eState==CURSOR_VALID );
while( rc==SQLITE_OK && !(pPage = pCur->apPage[pCur->iPage])->leaf ){
assert( pCur->aiIdx[pCur->iPage]<pPage->nCell );
@@ -57749,7 +62679,7 @@ static int moveToRightmost(BtCursor *pCur){
int rc = SQLITE_OK;
MemPage *pPage = 0;
- assert( cursorHoldsMutex(pCur) );
+ assert( cursorOwnsBtShared(pCur) );
assert( pCur->eState==CURSOR_VALID );
while( !(pPage = pCur->apPage[pCur->iPage])->leaf ){
pgno = get4byte(&pPage->aData[pPage->hdrOffset+8]);
@@ -57770,7 +62700,7 @@ static int moveToRightmost(BtCursor *pCur){
SQLITE_PRIVATE int sqlite3BtreeFirst(BtCursor *pCur, int *pRes){
int rc;
- assert( cursorHoldsMutex(pCur) );
+ assert( cursorOwnsBtShared(pCur) );
assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) );
rc = moveToRoot(pCur);
if( rc==SQLITE_OK ){
@@ -57793,7 +62723,7 @@ SQLITE_PRIVATE int sqlite3BtreeFirst(BtCursor *pCur, int *pRes){
SQLITE_PRIVATE int sqlite3BtreeLast(BtCursor *pCur, int *pRes){
int rc;
- assert( cursorHoldsMutex(pCur) );
+ assert( cursorOwnsBtShared(pCur) );
assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) );
/* If the cursor already points to the last entry, this is a no-op. */
@@ -57858,6 +62788,8 @@ SQLITE_PRIVATE int sqlite3BtreeLast(BtCursor *pCur, int *pRes){
** *pRes>0 The cursor is left pointing at an entry that
** is larger than intKey/pIdxKey.
**
+** For index tables, the pIdxKey->eqSeen field is set to 1 if there
+** exists an entry in the table that exactly matches pIdxKey.
*/
SQLITE_PRIVATE int sqlite3BtreeMovetoUnpacked(
BtCursor *pCur, /* The cursor to be moved */
@@ -57869,7 +62801,7 @@ SQLITE_PRIVATE int sqlite3BtreeMovetoUnpacked(
int rc;
RecordCompare xRecordCompare;
- assert( cursorHoldsMutex(pCur) );
+ assert( cursorOwnsBtShared(pCur) );
assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) );
assert( pRes );
assert( (pIdxKey==0)==(pCur->pKeyInfo==0) );
@@ -57877,7 +62809,7 @@ SQLITE_PRIVATE int sqlite3BtreeMovetoUnpacked(
/* If the cursor is already positioned at the point we are trying
** to move to, then just return without doing any work */
if( pCur->eState==CURSOR_VALID && (pCur->curFlags & BTCF_ValidNKey)!=0
- && pCur->apPage[0]->intKey
+ && pCur->curIntKey
){
if( pCur->info.nKey==intKey ){
*pRes = 0;
@@ -57912,7 +62844,8 @@ SQLITE_PRIVATE int sqlite3BtreeMovetoUnpacked(
assert( pCur->pgnoRoot==0 || pCur->apPage[pCur->iPage]->nCell==0 );
return SQLITE_OK;
}
- assert( pCur->apPage[0]->intKey || pIdxKey );
+ assert( pCur->apPage[0]->intKey==pCur->curIntKey );
+ assert( pCur->curIntKey || pIdxKey );
for(;;){
int lwr, upr, idx, c;
Pgno chldPg;
@@ -57935,7 +62868,7 @@ SQLITE_PRIVATE int sqlite3BtreeMovetoUnpacked(
if( xRecordCompare==0 ){
for(;;){
i64 nCellKey;
- pCell = findCell(pPage, idx) + pPage->childPtrSize;
+ pCell = findCellPastPtr(pPage, idx);
if( pPage->intKeyLeaf ){
while( 0x80 <= *(pCell++) ){
if( pCell>=pPage->aDataEnd ) return SQLITE_CORRUPT_BKPT;
@@ -57967,8 +62900,8 @@ SQLITE_PRIVATE int sqlite3BtreeMovetoUnpacked(
}
}else{
for(;;){
- int nCell;
- pCell = findCell(pPage, idx) + pPage->childPtrSize;
+ int nCell; /* Size of the pCell cell in bytes */
+ pCell = findCellPastPtr(pPage, idx);
/* The maximum supported page-size is 65536 bytes. This means that
** the maximum number of record bytes stored on an index B-Tree
@@ -57996,14 +62929,27 @@ SQLITE_PRIVATE int sqlite3BtreeMovetoUnpacked(
/* The record flows over onto one or more overflow pages. In
** this case the whole cell needs to be parsed, a buffer allocated
** and accessPayload() used to retrieve the record into the
- ** buffer before VdbeRecordCompare() can be called. */
+ ** buffer before VdbeRecordCompare() can be called.
+ **
+ ** If the record is corrupt, the xRecordCompare routine may read
+ ** up to two varints past the end of the buffer. An extra 18
+ ** bytes of padding is allocated at the end of the buffer in
+ ** case this happens. */
void *pCellKey;
u8 * const pCellBody = pCell - pPage->childPtrSize;
- btreeParseCellPtr(pPage, pCellBody, &pCur->info);
+ pPage->xParseCell(pPage, pCellBody, &pCur->info);
nCell = (int)pCur->info.nKey;
- pCellKey = sqlite3Malloc( nCell );
+ testcase( nCell<0 ); /* True if key size is 2^32 or more */
+ testcase( nCell==0 ); /* Invalid key size: 0x80 0x80 0x00 */
+ testcase( nCell==1 ); /* Invalid key size: 0x80 0x80 0x01 */
+ testcase( nCell==2 ); /* Minimum legal index key size */
+ if( nCell<2 ){
+ rc = SQLITE_CORRUPT_BKPT;
+ goto moveto_finish;
+ }
+ pCellKey = sqlite3Malloc( nCell+18 );
if( pCellKey==0 ){
- rc = SQLITE_NOMEM;
+ rc = SQLITE_NOMEM_BKPT;
goto moveto_finish;
}
pCur->aiIdx[pCur->iPage] = (u16)idx;
@@ -58103,7 +63049,7 @@ static SQLITE_NOINLINE int btreeNext(BtCursor *pCur, int *pRes){
int idx;
MemPage *pPage;
- assert( cursorHoldsMutex(pCur) );
+ assert( cursorOwnsBtShared(pCur) );
assert( pCur->skipNext==0 || pCur->eState!=CURSOR_VALID );
assert( *pRes==0 );
if( pCur->eState!=CURSOR_VALID ){
@@ -58167,7 +63113,7 @@ static SQLITE_NOINLINE int btreeNext(BtCursor *pCur, int *pRes){
}
SQLITE_PRIVATE int sqlite3BtreeNext(BtCursor *pCur, int *pRes){
MemPage *pPage;
- assert( cursorHoldsMutex(pCur) );
+ assert( cursorOwnsBtShared(pCur) );
assert( pRes!=0 );
assert( *pRes==0 || *pRes==1 );
assert( pCur->skipNext==0 || pCur->eState!=CURSOR_VALID );
@@ -58212,7 +63158,7 @@ static SQLITE_NOINLINE int btreePrevious(BtCursor *pCur, int *pRes){
int rc;
MemPage *pPage;
- assert( cursorHoldsMutex(pCur) );
+ assert( cursorOwnsBtShared(pCur) );
assert( pRes!=0 );
assert( *pRes==0 );
assert( pCur->skipNext==0 || pCur->eState!=CURSOR_VALID );
@@ -58268,7 +63214,7 @@ static SQLITE_NOINLINE int btreePrevious(BtCursor *pCur, int *pRes){
return rc;
}
SQLITE_PRIVATE int sqlite3BtreePrevious(BtCursor *pCur, int *pRes){
- assert( cursorHoldsMutex(pCur) );
+ assert( cursorOwnsBtShared(pCur) );
assert( pRes!=0 );
assert( *pRes==0 || *pRes==1 );
assert( pCur->skipNext==0 || pCur->eState!=CURSOR_VALID );
@@ -58294,8 +63240,7 @@ SQLITE_PRIVATE int sqlite3BtreePrevious(BtCursor *pCur, int *pRes){
** sqlite3PagerUnref() on the new page when it is done.
**
** SQLITE_OK is returned on success. Any other return value indicates
-** an error. *ppPage and *pPgno are undefined in the event of an error.
-** Do not invoke sqlite3PagerUnref() on *ppPage if an error is returned.
+** an error. *ppPage is set to NULL in the event of an error.
**
** If the "nearby" parameter is not 0, then an effort is made to
** locate a page close to the page number "nearby". This can be used in an
@@ -58338,6 +63283,7 @@ static int allocateBtreePage(
/* There are pages on the freelist. Reuse one of those pages. */
Pgno iTrunk;
u8 searchList = 0; /* If the free-list must be searched for 'nearby' */
+ u32 nSearch = 0; /* Count of the number of search attempts */
/* If eMode==BTALLOC_EXACT and a query of the pointer-map
** shows that the page 'nearby' is somewhere on the free-list, then
@@ -58386,10 +63332,10 @@ static int allocateBtreePage(
iTrunk = get4byte(&pPage1->aData[32]);
}
testcase( iTrunk==mxPage );
- if( iTrunk>mxPage ){
+ if( iTrunk>mxPage || nSearch++ > n ){
rc = SQLITE_CORRUPT_BKPT;
}else{
- rc = btreeGetPage(pBt, iTrunk, &pTrunk, 0);
+ rc = btreeGetUnusedPage(pBt, iTrunk, &pTrunk, 0);
}
if( rc ){
pTrunk = 0;
@@ -58454,7 +63400,7 @@ static int allocateBtreePage(
goto end_allocate_page;
}
testcase( iNewTrunk==mxPage );
- rc = btreeGetPage(pBt, iNewTrunk, &pNewTrunk, 0);
+ rc = btreeGetUnusedPage(pBt, iNewTrunk, &pNewTrunk, 0);
if( rc!=SQLITE_OK ){
goto end_allocate_page;
}
@@ -58534,11 +63480,12 @@ static int allocateBtreePage(
}
put4byte(&aData[4], k-1);
noContent = !btreeGetHasContent(pBt, *pPgno)? PAGER_GET_NOCONTENT : 0;
- rc = btreeGetPage(pBt, *pPgno, ppPage, noContent);
+ rc = btreeGetUnusedPage(pBt, *pPgno, ppPage, noContent);
if( rc==SQLITE_OK ){
rc = sqlite3PagerWrite((*ppPage)->pDbPage);
if( rc!=SQLITE_OK ){
releasePage(*ppPage);
+ *ppPage = 0;
}
}
searchList = 0;
@@ -58582,7 +63529,7 @@ static int allocateBtreePage(
MemPage *pPg = 0;
TRACE(("ALLOCATE: %d from end of file (pointer-map page)\n", pBt->nPage));
assert( pBt->nPage!=PENDING_BYTE_PAGE(pBt) );
- rc = btreeGetPage(pBt, pBt->nPage, &pPg, bNoContent);
+ rc = btreeGetUnusedPage(pBt, pBt->nPage, &pPg, bNoContent);
if( rc==SQLITE_OK ){
rc = sqlite3PagerWrite(pPg->pDbPage);
releasePage(pPg);
@@ -58596,11 +63543,12 @@ static int allocateBtreePage(
*pPgno = pBt->nPage;
assert( *pPgno!=PENDING_BYTE_PAGE(pBt) );
- rc = btreeGetPage(pBt, *pPgno, ppPage, bNoContent);
+ rc = btreeGetUnusedPage(pBt, *pPgno, ppPage, bNoContent);
if( rc ) return rc;
rc = sqlite3PagerWrite((*ppPage)->pDbPage);
if( rc!=SQLITE_OK ){
releasePage(*ppPage);
+ *ppPage = 0;
}
TRACE(("ALLOCATE: %d from end of file\n", *pPgno));
}
@@ -58610,17 +63558,8 @@ static int allocateBtreePage(
end_allocate_page:
releasePage(pTrunk);
releasePage(pPrevTrunk);
- if( rc==SQLITE_OK ){
- if( sqlite3PagerPageRefcount((*ppPage)->pDbPage)>1 ){
- releasePage(*ppPage);
- *ppPage = 0;
- return SQLITE_CORRUPT_BKPT;
- }
- (*ppPage)->isInit = 0;
- }else{
- *ppPage = 0;
- }
- assert( rc!=SQLITE_OK || sqlite3PagerIswriteable((*ppPage)->pDbPage) );
+ assert( rc!=SQLITE_OK || sqlite3PagerPageRefcount((*ppPage)->pDbPage)<=1 );
+ assert( rc!=SQLITE_OK || (*ppPage)->isInit==0 );
return rc;
}
@@ -58645,9 +63584,10 @@ static int freePage2(BtShared *pBt, MemPage *pMemPage, Pgno iPage){
int nFree; /* Initial number of pages on free-list */
assert( sqlite3_mutex_held(pBt->mutex) );
- assert( iPage>1 );
+ assert( CORRUPT_DB || iPage>1 );
assert( !pMemPage || pMemPage->pgno==iPage );
+ if( iPage<2 ) return SQLITE_CORRUPT_BKPT;
if( pMemPage ){
pPage = pMemPage;
sqlite3PagerRef(pPage->pDbPage);
@@ -58787,19 +63727,21 @@ static int clearCell(
u32 ovflPageSize;
assert( sqlite3_mutex_held(pPage->pBt->mutex) );
- btreeParseCellPtr(pPage, pCell, &info);
+ pPage->xParseCell(pPage, pCell, &info);
*pnSize = info.nSize;
- if( info.iOverflow==0 ){
+ if( info.nLocal==info.nPayload ){
return SQLITE_OK; /* No overflow pages. Return without doing anything */
}
- if( pCell+info.iOverflow+3 > pPage->aData+pPage->maskPage ){
+ if( pCell+info.nSize-1 > pPage->aData+pPage->maskPage ){
return SQLITE_CORRUPT_BKPT; /* Cell extends past end of page */
}
- ovflPgno = get4byte(&pCell[info.iOverflow]);
+ ovflPgno = get4byte(pCell + info.nSize - 4);
assert( pBt->usableSize > 4 );
ovflPageSize = pBt->usableSize - 4;
nOvfl = (info.nPayload - info.nLocal + ovflPageSize - 1)/ovflPageSize;
- assert( ovflPgno==0 || nOvfl>0 );
+ assert( nOvfl>0 ||
+ (CORRUPT_DB && (info.nPayload + ovflPageSize)<ovflPageSize)
+ );
while( nOvfl-- ){
Pgno iNext = 0;
MemPage *pOvfl = 0;
@@ -58897,9 +63839,7 @@ static int fillInCell(
nSrc = nData;
nData = 0;
}else{
- if( NEVER(nKey>0x7fffffff || pKey==0) ){
- return SQLITE_CORRUPT_BKPT;
- }
+ assert( nKey<=0x7fffffff && pKey!=0 );
nPayload = (int)nKey;
pSrc = pKey;
nSrc = (int)nKey;
@@ -58939,12 +63879,11 @@ static int fillInCell(
#if SQLITE_DEBUG
{
CellInfo info;
- btreeParseCellPtr(pPage, pCell, &info);
- assert( nHeader=(int)(info.pPayload - pCell) );
+ pPage->xParseCell(pPage, pCell, &info);
+ assert( nHeader==(int)(info.pPayload - pCell) );
assert( info.nKey==nKey );
assert( *pnSize == info.nSize );
assert( spaceLeft == info.nLocal );
- assert( pPrior == &pCell[info.iOverflow] );
}
#endif
@@ -59054,7 +63993,7 @@ static void dropCell(MemPage *pPage, int idx, int sz, int *pRC){
if( *pRC ) return;
assert( idx>=0 && idx<pPage->nCell );
- assert( sz==cellSize(pPage, idx) );
+ assert( CORRUPT_DB || sz==cellSize(pPage, idx) );
assert( sqlite3PagerIswriteable(pPage->pDbPage) );
assert( sqlite3_mutex_held(pPage->pBt->mutex) );
data = pPage->aData;
@@ -59109,10 +64048,8 @@ static void insertCell(
){
int idx = 0; /* Where to write new cell content in data[] */
int j; /* Loop counter */
- int end; /* First byte past the last cell pointer in data[] */
- int ins; /* Index in data[] where new cell pointer is inserted */
- int cellOffset; /* Address of first cell pointer in data[] */
u8 *data; /* The content of the whole page */
+ u8 *pIns; /* The point in pPage->aCellIdx[] where no cell inserted */
if( *pRC ) return;
@@ -59127,7 +64064,7 @@ static void insertCell(
** wanted to be less than 4 but got rounded up to 4 on the leaf, then size
** might be less than 8 (leaf-size + pointer) on the interior node. Hence
** the term after the || in the following assert(). */
- assert( sz==cellSizePtr(pPage, pCell) || (sz==8 && iChild>0) );
+ assert( sz==pPage->xCellSize(pPage, pCell) || (sz==8 && iChild>0) );
if( pPage->nOverflow || sz+2>pPage->nFree ){
if( pTemp ){
memcpy(pTemp, pCell, sz);
@@ -59140,6 +64077,14 @@ static void insertCell(
assert( j<(int)(sizeof(pPage->apOvfl)/sizeof(pPage->apOvfl[0])) );
pPage->apOvfl[j] = pCell;
pPage->aiOvfl[j] = (u16)i;
+
+ /* When multiple overflows occur, they are always sequential and in
+ ** sorted order. This invariants arise because multiple overflows can
+ ** only occur when inserting divider cells into the parent page during
+ ** balancing, and the dividers are adjacent and sorted.
+ */
+ assert( j==0 || pPage->aiOvfl[j-1]<(u16)i ); /* Overflows in sorted order */
+ assert( j==0 || i==pPage->aiOvfl[j-1]+1 ); /* Overflows are sequential */
}else{
int rc = sqlite3PagerWrite(pPage->pDbPage);
if( rc!=SQLITE_OK ){
@@ -59148,24 +64093,26 @@ static void insertCell(
}
assert( sqlite3PagerIswriteable(pPage->pDbPage) );
data = pPage->aData;
- cellOffset = pPage->cellOffset;
- end = cellOffset + 2*pPage->nCell;
- ins = cellOffset + 2*i;
+ assert( &data[pPage->cellOffset]==pPage->aCellIdx );
rc = allocateSpace(pPage, sz, &idx);
if( rc ){ *pRC = rc; return; }
- /* The allocateSpace() routine guarantees the following two properties
- ** if it returns success */
- assert( idx >= end+2 );
+ /* The allocateSpace() routine guarantees the following properties
+ ** if it returns successfully */
+ assert( idx >= 0 );
+ assert( idx >= pPage->cellOffset+2*pPage->nCell+2 || CORRUPT_DB );
assert( idx+sz <= (int)pPage->pBt->usableSize );
- pPage->nCell++;
pPage->nFree -= (u16)(2 + sz);
memcpy(&data[idx], pCell, sz);
if( iChild ){
put4byte(&data[idx], iChild);
}
- memmove(&data[ins+2], &data[ins], end-ins);
- put2byte(&data[ins], idx);
- put2byte(&data[pPage->hdrOffset+3], pPage->nCell);
+ pIns = pPage->aCellIdx + i*2;
+ memmove(pIns+2, pIns, 2*(pPage->nCell - i));
+ put2byte(pIns, idx);
+ pPage->nCell++;
+ /* increment the cell count */
+ if( (++data[pPage->hdrOffset+4])==0 ) data[pPage->hdrOffset+3]++;
+ assert( get2byte(&data[pPage->hdrOffset+3])==pPage->nCell );
#ifndef SQLITE_OMIT_AUTOVACUUM
if( pPage->pBt->autoVacuum ){
/* The cell may contain a pointer to an overflow page. If so, write
@@ -59178,6 +64125,52 @@ static void insertCell(
}
/*
+** A CellArray object contains a cache of pointers and sizes for a
+** consecutive sequence of cells that might be held multiple pages.
+*/
+typedef struct CellArray CellArray;
+struct CellArray {
+ int nCell; /* Number of cells in apCell[] */
+ MemPage *pRef; /* Reference page */
+ u8 **apCell; /* All cells begin balanced */
+ u16 *szCell; /* Local size of all cells in apCell[] */
+};
+
+/*
+** Make sure the cell sizes at idx, idx+1, ..., idx+N-1 have been
+** computed.
+*/
+static void populateCellCache(CellArray *p, int idx, int N){
+ assert( idx>=0 && idx+N<=p->nCell );
+ while( N>0 ){
+ assert( p->apCell[idx]!=0 );
+ if( p->szCell[idx]==0 ){
+ p->szCell[idx] = p->pRef->xCellSize(p->pRef, p->apCell[idx]);
+ }else{
+ assert( CORRUPT_DB ||
+ p->szCell[idx]==p->pRef->xCellSize(p->pRef, p->apCell[idx]) );
+ }
+ idx++;
+ N--;
+ }
+}
+
+/*
+** Return the size of the Nth element of the cell array
+*/
+static SQLITE_NOINLINE u16 computeCellSize(CellArray *p, int N){
+ assert( N>=0 && N<p->nCell );
+ assert( p->szCell[N]==0 );
+ p->szCell[N] = p->pRef->xCellSize(p->pRef, p->apCell[N]);
+ return p->szCell[N];
+}
+static u16 cachedCellSize(CellArray *p, int N){
+ assert( N>=0 && N<p->nCell );
+ if( p->szCell[N] ) return p->szCell[N];
+ return computeCellSize(p, N);
+}
+
+/*
** Array apCell[] contains pointers to nCell b-tree page cells. The
** szCell[] array contains the size in bytes of each cell. This function
** replaces the current contents of page pPg with the contents of the cell
@@ -59190,7 +64183,7 @@ static void insertCell(
** The MemPage.nFree field is invalidated by this function. It is the
** responsibility of the caller to set it correctly.
*/
-static void rebuildPage(
+static int rebuildPage(
MemPage *pPg, /* Edit this page */
int nCell, /* Final number of cells on page */
u8 **apCell, /* Array of cells */
@@ -59211,14 +64204,16 @@ static void rebuildPage(
pData = pEnd;
for(i=0; i<nCell; i++){
u8 *pCell = apCell[i];
- if( pCell>aData && pCell<pEnd ){
+ if( SQLITE_WITHIN(pCell,aData,pEnd) ){
pCell = &pTmp[pCell - aData];
}
pData -= szCell[i];
- memcpy(pData, pCell, szCell[i]);
put2byte(pCellptr, (pData - aData));
pCellptr += 2;
- assert( szCell[i]==cellSizePtr(pPg, pCell) );
+ if( pData < pCellptr ) return SQLITE_CORRUPT_BKPT;
+ memcpy(pData, pCell, szCell[i]);
+ assert( szCell[i]==pPg->xCellSize(pPg, pCell) || CORRUPT_DB );
+ testcase( szCell[i]!=pPg->xCellSize(pPg,pCell) );
}
/* The pPg->nFree field is now set incorrectly. The caller will fix it. */
@@ -59229,6 +64224,7 @@ static void rebuildPage(
put2byte(&aData[hdr+3], pPg->nCell);
put2byte(&aData[hdr+5], pData - aData);
aData[hdr+7] = 0x00;
+ return SQLITE_OK;
}
/*
@@ -59261,25 +64257,31 @@ static int pageInsertArray(
u8 *pBegin, /* End of cell-pointer array */
u8 **ppData, /* IN/OUT: Page content -area pointer */
u8 *pCellptr, /* Pointer to cell-pointer area */
+ int iFirst, /* Index of first cell to add */
int nCell, /* Number of cells to add to pPg */
- u8 **apCell, /* Array of cells */
- u16 *szCell /* Array of cell sizes */
+ CellArray *pCArray /* Array of cells */
){
int i;
u8 *aData = pPg->aData;
u8 *pData = *ppData;
- const int bFreelist = aData[1] || aData[2];
+ int iEnd = iFirst + nCell;
assert( CORRUPT_DB || pPg->hdrOffset==0 ); /* Never called on page 1 */
- for(i=0; i<nCell; i++){
- int sz = szCell[i];
- int rc;
+ for(i=iFirst; i<iEnd; i++){
+ int sz, rc;
u8 *pSlot;
- if( bFreelist==0 || (pSlot = pageFindSlot(pPg, sz, &rc, 0))==0 ){
+ sz = cachedCellSize(pCArray, i);
+ if( (aData[1]==0 && aData[2]==0) || (pSlot = pageFindSlot(pPg,sz,&rc))==0 ){
+ if( (pData - pBegin)<sz ) return 1;
pData -= sz;
- if( pData<pBegin ) return 1;
pSlot = pData;
}
- memcpy(pSlot, apCell[i], sz);
+ /* pSlot and pCArray->apCell[i] will never overlap on a well-formed
+ ** database. But they might for a corrupt database. Hence use memmove()
+ ** since memcpy() sends SIGABORT with overlapping buffers on OpenBSD */
+ assert( (pSlot+sz)<=pCArray->apCell[i]
+ || pSlot>=(pCArray->apCell[i]+sz)
+ || CORRUPT_DB );
+ memmove(pSlot, pCArray->apCell[i], sz);
put2byte(pCellptr, (pSlot - aData));
pCellptr += 2;
}
@@ -59298,22 +64300,27 @@ static int pageInsertArray(
*/
static int pageFreeArray(
MemPage *pPg, /* Page to edit */
+ int iFirst, /* First cell to delete */
int nCell, /* Cells to delete */
- u8 **apCell, /* Array of cells */
- u16 *szCell /* Array of cell sizes */
+ CellArray *pCArray /* Array of cells */
){
u8 * const aData = pPg->aData;
u8 * const pEnd = &aData[pPg->pBt->usableSize];
u8 * const pStart = &aData[pPg->hdrOffset + 8 + pPg->childPtrSize];
int nRet = 0;
int i;
+ int iEnd = iFirst + nCell;
u8 *pFree = 0;
int szFree = 0;
- for(i=0; i<nCell; i++){
- u8 *pCell = apCell[i];
- if( pCell>=pStart && pCell<pEnd ){
- int sz = szCell[i];
+ for(i=iFirst; i<iEnd; i++){
+ u8 *pCell = pCArray->apCell[i];
+ if( SQLITE_WITHIN(pCell, pStart, pEnd) ){
+ int sz;
+ /* No need to use cachedCellSize() here. The sizes of all cells that
+ ** are to be freed have already been computing while deciding which
+ ** cells need freeing */
+ sz = pCArray->szCell[i]; assert( sz>0 );
if( pFree!=(pCell + sz) ){
if( pFree ){
assert( pFree>aData && (pFree - aData)<65536 );
@@ -59348,13 +64355,12 @@ static int pageFreeArray(
** The pPg->nFree field is invalid when this function returns. It is the
** responsibility of the caller to set it correctly.
*/
-static void editPage(
+static int editPage(
MemPage *pPg, /* Edit this page */
int iOld, /* Index of first cell currently on page */
int iNew, /* Index of new first cell on page */
int nNew, /* Final number of cells on page */
- u8 **apCell, /* Array of cells */
- u16 *szCell /* Array of cell sizes */
+ CellArray *pCArray /* Array of cells and sizes */
){
u8 * const aData = pPg->aData;
const int hdr = pPg->hdrOffset;
@@ -59373,16 +64379,12 @@ static void editPage(
/* Remove cells from the start and end of the page */
if( iOld<iNew ){
- int nShift = pageFreeArray(
- pPg, iNew-iOld, &apCell[iOld], &szCell[iOld]
- );
+ int nShift = pageFreeArray(pPg, iOld, iNew-iOld, pCArray);
memmove(pPg->aCellIdx, &pPg->aCellIdx[nShift*2], nCell*2);
nCell -= nShift;
}
if( iNewEnd < iOldEnd ){
- nCell -= pageFreeArray(
- pPg, iOldEnd-iNewEnd, &apCell[iNewEnd], &szCell[iNewEnd]
- );
+ nCell -= pageFreeArray(pPg, iNewEnd, iOldEnd - iNewEnd, pCArray);
}
pData = &aData[get2byteNotZero(&aData[hdr+5])];
@@ -59396,7 +64398,7 @@ static void editPage(
memmove(&pCellptr[nAdd*2], pCellptr, nCell*2);
if( pageInsertArray(
pPg, pBegin, &pData, pCellptr,
- nAdd, &apCell[iNew], &szCell[iNew]
+ iNew, nAdd, pCArray
) ) goto editpage_fail;
nCell += nAdd;
}
@@ -59410,7 +64412,7 @@ static void editPage(
nCell++;
if( pageInsertArray(
pPg, pBegin, &pData, pCellptr,
- 1, &apCell[iCell + iNew], &szCell[iCell + iNew]
+ iCell+iNew, 1, pCArray
) ) goto editpage_fail;
}
}
@@ -59419,7 +64421,7 @@ static void editPage(
pCellptr = &pPg->aCellIdx[nCell*2];
if( pageInsertArray(
pPg, pBegin, &pData, pCellptr,
- nNew-nCell, &apCell[iNew+nCell], &szCell[iNew+nCell]
+ iNew+nCell, nNew-nCell, pCArray
) ) goto editpage_fail;
pPg->nCell = nNew;
@@ -59430,19 +64432,21 @@ static void editPage(
#ifdef SQLITE_DEBUG
for(i=0; i<nNew && !CORRUPT_DB; i++){
- u8 *pCell = apCell[i+iNew];
- int iOff = get2byte(&pPg->aCellIdx[i*2]);
- if( pCell>=aData && pCell<&aData[pPg->pBt->usableSize] ){
+ u8 *pCell = pCArray->apCell[i+iNew];
+ int iOff = get2byteAligned(&pPg->aCellIdx[i*2]);
+ if( SQLITE_WITHIN(pCell, aData, &aData[pPg->pBt->usableSize]) ){
pCell = &pTmp[pCell - aData];
}
- assert( 0==memcmp(pCell, &aData[iOff], szCell[i+iNew]) );
+ assert( 0==memcmp(pCell, &aData[iOff],
+ pCArray->pRef->xCellSize(pCArray->pRef, pCArray->apCell[i+iNew])) );
}
#endif
- return;
+ return SQLITE_OK;
editpage_fail:
/* Unable to edit this page. Rebuild it from scratch instead. */
- rebuildPage(pPg, nNew, &apCell[iNew], &szCell[iNew]);
+ populateCellCache(pCArray, iNew, nNew);
+ return rebuildPage(pPg, nNew, &pCArray->apCell[iNew], &pCArray->szCell[iNew]);
}
/*
@@ -59508,13 +64512,14 @@ static int balance_quick(MemPage *pParent, MemPage *pPage, u8 *pSpace){
u8 *pOut = &pSpace[4];
u8 *pCell = pPage->apOvfl[0];
- u16 szCell = cellSizePtr(pPage, pCell);
+ u16 szCell = pPage->xCellSize(pPage, pCell);
u8 *pStop;
assert( sqlite3PagerIswriteable(pNew->pDbPage) );
assert( pPage->aData[0]==(PTF_INTKEY|PTF_LEAFDATA|PTF_LEAF) );
zeroPage(pNew, PTF_INTKEY|PTF_LEAFDATA|PTF_LEAF);
- rebuildPage(pNew, 1, &pCell, &szCell);
+ rc = rebuildPage(pNew, 1, &pCell, &szCell);
+ if( NEVER(rc) ) return rc;
pNew->nFree = pBt->usableSize - pNew->cellOffset - 2 - szCell;
/* If this is an auto-vacuum database, update the pointer map
@@ -59587,9 +64592,9 @@ static int ptrmapCheckPages(MemPage **apPage, int nPage){
u8 *z;
z = findCell(pPage, j);
- btreeParseCellPtr(pPage, z, &info);
- if( info.iOverflow ){
- Pgno ovfl = get4byte(&z[info.iOverflow]);
+ pPage->xParseCell(pPage, z, &info);
+ if( info.nLocal<info.nPayload ){
+ Pgno ovfl = get4byte(&z[info.nSize-4]);
ptrmapGet(pBt, ovfl, &e, &n);
assert( n==pPage->pgno && e==PTRMAP_OVERFLOW1 );
}
@@ -59707,9 +64712,6 @@ static void copyNodeContent(MemPage *pFrom, MemPage *pTo, int *pRC){
** If aOvflSpace is set to a null pointer, this function returns
** SQLITE_NOMEM.
*/
-#if defined(_MSC_VER) && _MSC_VER >= 1700 && defined(_M_ARM)
-#pragma optimize("", off)
-#endif
static int balance_nonroot(
MemPage *pParent, /* Parent page of siblings being balanced */
int iParentIdx, /* Index of "the page" in pParent */
@@ -59718,7 +64720,6 @@ static int balance_nonroot(
int bBulk /* True if this call is part of a bulk load */
){
BtShared *pBt; /* The whole database */
- int nCell = 0; /* Number of cells in apCell[] */
int nMaxCells = 0; /* Allocated size of apCell, szCell, aFrom. */
int nNew = 0; /* Number of pages in apNew[] */
int nOld; /* Number of pages in apOld[] */
@@ -59729,7 +64730,6 @@ static int balance_nonroot(
int leafData; /* True if pPage is a leaf of a LEAFDATA tree */
int usableSpace; /* Bytes in pPage beyond the header */
int pageFlags; /* Value of pPage->aData[0] */
- int subtotal; /* Subtotal of bytes in cells on one page */
int iSpace1 = 0; /* First unused byte of aSpace1[] */
int iOvflSpace = 0; /* First unused byte of aOvflSpace[] */
int szScratch; /* Size of scratch memory requested */
@@ -59737,19 +64737,20 @@ static int balance_nonroot(
MemPage *apNew[NB+2]; /* pPage and up to NB siblings after balancing */
u8 *pRight; /* Location in parent of right-sibling pointer */
u8 *apDiv[NB-1]; /* Divider cells in pParent */
- int cntNew[NB+2]; /* Index in aCell[] of cell after i-th page */
- int cntOld[NB+2]; /* Old index in aCell[] after i-th page */
+ int cntNew[NB+2]; /* Index in b.paCell[] of cell after i-th page */
+ int cntOld[NB+2]; /* Old index in b.apCell[] */
int szNew[NB+2]; /* Combined size of cells placed on i-th page */
- u8 **apCell = 0; /* All cells begin balanced */
- u16 *szCell; /* Local size of all cells in apCell[] */
u8 *aSpace1; /* Space for copies of dividers cells */
Pgno pgno; /* Temp var to store a page number in */
u8 abDone[NB+2]; /* True after i'th new page is populated */
Pgno aPgno[NB+2]; /* Page numbers of new pages before shuffling */
Pgno aPgOrder[NB+2]; /* Copy of aPgno[] used for sorting pages */
u16 aPgFlags[NB+2]; /* flags field of new pages before shuffling */
+ CellArray b; /* Parsed information on cells being balanced */
memset(abDone, 0, sizeof(abDone));
+ b.nCell = 0;
+ b.apCell = 0;
pBt = pParent->pBt;
assert( sqlite3_mutex_held(pBt->mutex) );
assert( sqlite3PagerIswriteable(pParent->pDbPage) );
@@ -59767,7 +64768,7 @@ static int balance_nonroot(
assert( pParent->nOverflow==0 || pParent->aiOvfl[0]==iParentIdx );
if( !aOvflSpace ){
- return SQLITE_NOMEM;
+ return SQLITE_NOMEM_BKPT;
}
/* Find the sibling pages to balance. Also locate the cells in pParent
@@ -59803,7 +64804,7 @@ static int balance_nonroot(
}
pgno = get4byte(pRight);
while( 1 ){
- rc = getAndInitPage(pBt, pgno, &apOld[i], 0);
+ rc = getAndInitPage(pBt, pgno, &apOld[i], 0, 0);
if( rc ){
memset(apOld, 0, (i+1)*sizeof(MemPage*));
goto balance_cleanup;
@@ -59814,12 +64815,12 @@ static int balance_nonroot(
if( i+nxDiv==pParent->aiOvfl[0] && pParent->nOverflow ){
apDiv[i] = pParent->apOvfl[0];
pgno = get4byte(apDiv[i]);
- szNew[i] = cellSizePtr(pParent, apDiv[i]);
+ szNew[i] = pParent->xCellSize(pParent, apDiv[i]);
pParent->nOverflow = 0;
}else{
apDiv[i] = findCell(pParent, i+nxDiv-pParent->nOverflow);
pgno = get4byte(apDiv[i]);
- szNew[i] = cellSizePtr(pParent, apDiv[i]);
+ szNew[i] = pParent->xCellSize(pParent, apDiv[i]);
/* Drop the cell from the parent page. apDiv[i] still points to
** the cell within the parent, even though it has been dropped.
@@ -59858,130 +64859,200 @@ static int balance_nonroot(
** Allocate space for memory structures
*/
szScratch =
- nMaxCells*sizeof(u8*) /* apCell */
- + nMaxCells*sizeof(u16) /* szCell */
+ nMaxCells*sizeof(u8*) /* b.apCell */
+ + nMaxCells*sizeof(u16) /* b.szCell */
+ pBt->pageSize; /* aSpace1 */
/* EVIDENCE-OF: R-28375-38319 SQLite will never request a scratch buffer
** that is more than 6 times the database page size. */
assert( szScratch<=6*(int)pBt->pageSize );
- apCell = sqlite3ScratchMalloc( szScratch );
- if( apCell==0 ){
- rc = SQLITE_NOMEM;
+ b.apCell = sqlite3ScratchMalloc( szScratch );
+ if( b.apCell==0 ){
+ rc = SQLITE_NOMEM_BKPT;
goto balance_cleanup;
}
- szCell = (u16*)&apCell[nMaxCells];
- aSpace1 = (u8*)&szCell[nMaxCells];
+ b.szCell = (u16*)&b.apCell[nMaxCells];
+ aSpace1 = (u8*)&b.szCell[nMaxCells];
assert( EIGHT_BYTE_ALIGNMENT(aSpace1) );
/*
** Load pointers to all cells on sibling pages and the divider cells
- ** into the local apCell[] array. Make copies of the divider cells
+ ** into the local b.apCell[] array. Make copies of the divider cells
** into space obtained from aSpace1[]. The divider cells have already
** been removed from pParent.
**
** If the siblings are on leaf pages, then the child pointers of the
** divider cells are stripped from the cells before they are copied
- ** into aSpace1[]. In this way, all cells in apCell[] are without
+ ** into aSpace1[]. In this way, all cells in b.apCell[] are without
** child pointers. If siblings are not leaves, then all cell in
- ** apCell[] include child pointers. Either way, all cells in apCell[]
+ ** b.apCell[] include child pointers. Either way, all cells in b.apCell[]
** are alike.
**
** leafCorrection: 4 if pPage is a leaf. 0 if pPage is not a leaf.
** leafData: 1 if pPage holds key+data and pParent holds only keys.
*/
- leafCorrection = apOld[0]->leaf*4;
- leafData = apOld[0]->intKeyLeaf;
+ b.pRef = apOld[0];
+ leafCorrection = b.pRef->leaf*4;
+ leafData = b.pRef->intKeyLeaf;
for(i=0; i<nOld; i++){
- int limit;
MemPage *pOld = apOld[i];
+ int limit = pOld->nCell;
+ u8 *aData = pOld->aData;
+ u16 maskPage = pOld->maskPage;
+ u8 *piCell = aData + pOld->cellOffset;
+ u8 *piEnd;
+
+ /* Verify that all sibling pages are of the same "type" (table-leaf,
+ ** table-interior, index-leaf, or index-interior).
+ */
+ if( pOld->aData[0]!=apOld[0]->aData[0] ){
+ rc = SQLITE_CORRUPT_BKPT;
+ goto balance_cleanup;
+ }
- limit = pOld->nCell+pOld->nOverflow;
+ /* Load b.apCell[] with pointers to all cells in pOld. If pOld
+ ** constains overflow cells, include them in the b.apCell[] array
+ ** in the correct spot.
+ **
+ ** Note that when there are multiple overflow cells, it is always the
+ ** case that they are sequential and adjacent. This invariant arises
+ ** because multiple overflows can only occurs when inserting divider
+ ** cells into a parent on a prior balance, and divider cells are always
+ ** adjacent and are inserted in order. There is an assert() tagged
+ ** with "NOTE 1" in the overflow cell insertion loop to prove this
+ ** invariant.
+ **
+ ** This must be done in advance. Once the balance starts, the cell
+ ** offset section of the btree page will be overwritten and we will no
+ ** long be able to find the cells if a pointer to each cell is not saved
+ ** first.
+ */
+ memset(&b.szCell[b.nCell], 0, sizeof(b.szCell[0])*(limit+pOld->nOverflow));
if( pOld->nOverflow>0 ){
+ limit = pOld->aiOvfl[0];
for(j=0; j<limit; j++){
- assert( nCell<nMaxCells );
- apCell[nCell] = findOverflowCell(pOld, j);
- szCell[nCell] = cellSizePtr(pOld, apCell[nCell]);
- nCell++;
+ b.apCell[b.nCell] = aData + (maskPage & get2byteAligned(piCell));
+ piCell += 2;
+ b.nCell++;
}
- }else{
- u8 *aData = pOld->aData;
- u16 maskPage = pOld->maskPage;
- u16 cellOffset = pOld->cellOffset;
- for(j=0; j<limit; j++){
- assert( nCell<nMaxCells );
- apCell[nCell] = findCellv2(aData, maskPage, cellOffset, j);
- szCell[nCell] = cellSizePtr(pOld, apCell[nCell]);
- nCell++;
+ for(k=0; k<pOld->nOverflow; k++){
+ assert( k==0 || pOld->aiOvfl[k-1]+1==pOld->aiOvfl[k] );/* NOTE 1 */
+ b.apCell[b.nCell] = pOld->apOvfl[k];
+ b.nCell++;
}
- }
- cntOld[i] = nCell;
+ }
+ piEnd = aData + pOld->cellOffset + 2*pOld->nCell;
+ while( piCell<piEnd ){
+ assert( b.nCell<nMaxCells );
+ b.apCell[b.nCell] = aData + (maskPage & get2byteAligned(piCell));
+ piCell += 2;
+ b.nCell++;
+ }
+
+ cntOld[i] = b.nCell;
if( i<nOld-1 && !leafData){
u16 sz = (u16)szNew[i];
u8 *pTemp;
- assert( nCell<nMaxCells );
- szCell[nCell] = sz;
+ assert( b.nCell<nMaxCells );
+ b.szCell[b.nCell] = sz;
pTemp = &aSpace1[iSpace1];
iSpace1 += sz;
assert( sz<=pBt->maxLocal+23 );
assert( iSpace1 <= (int)pBt->pageSize );
memcpy(pTemp, apDiv[i], sz);
- apCell[nCell] = pTemp+leafCorrection;
+ b.apCell[b.nCell] = pTemp+leafCorrection;
assert( leafCorrection==0 || leafCorrection==4 );
- szCell[nCell] = szCell[nCell] - leafCorrection;
+ b.szCell[b.nCell] = b.szCell[b.nCell] - leafCorrection;
if( !pOld->leaf ){
assert( leafCorrection==0 );
assert( pOld->hdrOffset==0 );
/* The right pointer of the child page pOld becomes the left
** pointer of the divider cell */
- memcpy(apCell[nCell], &pOld->aData[8], 4);
+ memcpy(b.apCell[b.nCell], &pOld->aData[8], 4);
}else{
assert( leafCorrection==4 );
- if( szCell[nCell]<4 ){
+ while( b.szCell[b.nCell]<4 ){
/* Do not allow any cells smaller than 4 bytes. If a smaller cell
** does exist, pad it with 0x00 bytes. */
- assert( szCell[nCell]==3 );
- assert( apCell[nCell]==&aSpace1[iSpace1-3] );
+ assert( b.szCell[b.nCell]==3 || CORRUPT_DB );
+ assert( b.apCell[b.nCell]==&aSpace1[iSpace1-3] || CORRUPT_DB );
aSpace1[iSpace1++] = 0x00;
- szCell[nCell] = 4;
+ b.szCell[b.nCell]++;
}
}
- nCell++;
+ b.nCell++;
}
}
/*
- ** Figure out the number of pages needed to hold all nCell cells.
+ ** Figure out the number of pages needed to hold all b.nCell cells.
** Store this number in "k". Also compute szNew[] which is the total
** size of all cells on the i-th page and cntNew[] which is the index
- ** in apCell[] of the cell that divides page i from page i+1.
- ** cntNew[k] should equal nCell.
+ ** in b.apCell[] of the cell that divides page i from page i+1.
+ ** cntNew[k] should equal b.nCell.
**
** Values computed by this block:
**
** k: The total number of sibling pages
** szNew[i]: Spaced used on the i-th sibling page.
- ** cntNew[i]: Index in apCell[] and szCell[] for the first cell to
+ ** cntNew[i]: Index in b.apCell[] and b.szCell[] for the first cell to
** the right of the i-th sibling page.
** usableSpace: Number of bytes of space available on each sibling.
**
*/
usableSpace = pBt->usableSize - 12 + leafCorrection;
- for(subtotal=k=i=0; i<nCell; i++){
- assert( i<nMaxCells );
- subtotal += szCell[i] + 2;
- if( subtotal > usableSpace ){
- szNew[k] = subtotal - szCell[i] - 2;
- cntNew[k] = i;
- if( leafData ){ i--; }
- subtotal = 0;
- k++;
- if( k>NB+1 ){ rc = SQLITE_CORRUPT_BKPT; goto balance_cleanup; }
- }
- }
- szNew[k] = subtotal;
- cntNew[k] = nCell;
- k++;
+ for(i=0; i<nOld; i++){
+ MemPage *p = apOld[i];
+ szNew[i] = usableSpace - p->nFree;
+ if( szNew[i]<0 ){ rc = SQLITE_CORRUPT_BKPT; goto balance_cleanup; }
+ for(j=0; j<p->nOverflow; j++){
+ szNew[i] += 2 + p->xCellSize(p, p->apOvfl[j]);
+ }
+ cntNew[i] = cntOld[i];
+ }
+ k = nOld;
+ for(i=0; i<k; i++){
+ int sz;
+ while( szNew[i]>usableSpace ){
+ if( i+1>=k ){
+ k = i+2;
+ if( k>NB+2 ){ rc = SQLITE_CORRUPT_BKPT; goto balance_cleanup; }
+ szNew[k-1] = 0;
+ cntNew[k-1] = b.nCell;
+ }
+ sz = 2 + cachedCellSize(&b, cntNew[i]-1);
+ szNew[i] -= sz;
+ if( !leafData ){
+ if( cntNew[i]<b.nCell ){
+ sz = 2 + cachedCellSize(&b, cntNew[i]);
+ }else{
+ sz = 0;
+ }
+ }
+ szNew[i+1] += sz;
+ cntNew[i]--;
+ }
+ while( cntNew[i]<b.nCell ){
+ sz = 2 + cachedCellSize(&b, cntNew[i]);
+ if( szNew[i]+sz>usableSpace ) break;
+ szNew[i] += sz;
+ cntNew[i]++;
+ if( !leafData ){
+ if( cntNew[i]<b.nCell ){
+ sz = 2 + cachedCellSize(&b, cntNew[i]);
+ }else{
+ sz = 0;
+ }
+ }
+ szNew[i+1] -= sz;
+ }
+ if( cntNew[i]>=b.nCell ){
+ k = i+1;
+ }else if( cntNew[i] <= (i>0 ? cntNew[i-1] : 0) ){
+ rc = SQLITE_CORRUPT_BKPT;
+ goto balance_cleanup;
+ }
+ }
/*
** The packing computed by the previous block is biased toward the siblings
@@ -60002,19 +65073,27 @@ static int balance_nonroot(
r = cntNew[i-1] - 1;
d = r + 1 - leafData;
- assert( d<nMaxCells );
- assert( r<nMaxCells );
- while( szRight==0
- || (!bBulk && szRight+szCell[d]+2<=szLeft-(szCell[r]+2))
- ){
- szRight += szCell[d] + 2;
- szLeft -= szCell[r] + 2;
- cntNew[i-1]--;
- r = cntNew[i-1] - 1;
- d = r + 1 - leafData;
- }
+ (void)cachedCellSize(&b, d);
+ do{
+ assert( d<nMaxCells );
+ assert( r<nMaxCells );
+ (void)cachedCellSize(&b, r);
+ if( szRight!=0
+ && (bBulk || szRight+b.szCell[d]+2 > szLeft-(b.szCell[r]+2)) ){
+ break;
+ }
+ szRight += b.szCell[d] + 2;
+ szLeft -= b.szCell[r] + 2;
+ cntNew[i-1] = r;
+ r--;
+ d--;
+ }while( r>=0 );
szNew[i] = szRight;
szNew[i-1] = szLeft;
+ if( cntNew[i-1] <= (i>1 ? cntNew[i-2] : 0) ){
+ rc = SQLITE_CORRUPT_BKPT;
+ goto balance_cleanup;
+ }
}
/* Sanity check: For a non-corrupt database file one of the follwing
@@ -60034,10 +65113,6 @@ static int balance_nonroot(
/*
** Allocate k new pages. Reuse old pages where possible.
*/
- if( apOld[0]->pgno<=1 ){
- rc = SQLITE_CORRUPT_BKPT;
- goto balance_cleanup;
- }
pageFlags = apOld[0]->aData[0];
for(i=0; i<k; i++){
MemPage *pNew;
@@ -60054,7 +65129,7 @@ static int balance_nonroot(
zeroPage(pNew, pageFlags);
apNew[i] = pNew;
nNew++;
- cntOld[i] = nCell;
+ cntOld[i] = b.nCell;
/* Set the pointer-map entry for the new sibling page. */
if( ISAUTOVACUUM ){
@@ -60159,8 +65234,8 @@ static int balance_nonroot(
int iNew = 0;
int iOld = 0;
- for(i=0; i<nCell; i++){
- u8 *pCell = apCell[i];
+ for(i=0; i<b.nCell; i++){
+ u8 *pCell = b.apCell[i];
if( i==cntOldNext ){
MemPage *pOld = (++iOld)<nNew ? apNew[iOld] : apOld[iOld];
cntOldNext += pOld->nCell + pOld->nOverflow + !leafData;
@@ -60179,15 +65254,15 @@ static int balance_nonroot(
** overflow cell), we can skip updating the pointer map entries. */
if( iOld>=nNew
|| pNew->pgno!=aPgno[iOld]
- || pCell<aOld
- || pCell>=&aOld[usableSize]
+ || !SQLITE_WITHIN(pCell,aOld,&aOld[usableSize])
){
if( !leafCorrection ){
ptrmapPut(pBt, get4byte(pCell), PTRMAP_BTREE, pNew->pgno, &rc);
}
- if( szCell[i]>pNew->minLocal ){
+ if( cachedCellSize(&b,i)>pNew->minLocal ){
ptrmapPutOvflPtr(pNew, pCell, &rc);
}
+ if( rc ) goto balance_cleanup;
}
}
}
@@ -60201,20 +65276,21 @@ static int balance_nonroot(
j = cntNew[i];
assert( j<nMaxCells );
- pCell = apCell[j];
- sz = szCell[j] + leafCorrection;
+ assert( b.apCell[j]!=0 );
+ pCell = b.apCell[j];
+ sz = b.szCell[j] + leafCorrection;
pTemp = &aOvflSpace[iOvflSpace];
if( !pNew->leaf ){
memcpy(&pNew->aData[8], pCell, 4);
}else if( leafData ){
/* If the tree is a leaf-data tree, and the siblings are leaves,
- ** then there is no divider cell in apCell[]. Instead, the divider
+ ** then there is no divider cell in b.apCell[]. Instead, the divider
** cell consists of the integer key for the right-most cell of
** the sibling-page assembled above only.
*/
CellInfo info;
j--;
- btreeParseCellPtr(pNew, apCell[j], &info);
+ pNew->xParseCell(pNew, b.apCell[j], &info);
pCell = pTemp;
sz = 4 + putVarint(&pCell[4], info.nKey);
pTemp = 0;
@@ -60227,13 +65303,13 @@ static int balance_nonroot(
** any cell). But it is important to pass the correct size to
** insertCell(), so reparse the cell now.
**
- ** Note that this can never happen in an SQLite data file, as all
- ** cells are at least 4 bytes. It only happens in b-trees used
- ** to evaluate "IN (SELECT ...)" and similar clauses.
+ ** This can only happen for b-trees used to evaluate "IN (SELECT ...)"
+ ** and WITHOUT ROWID tables with exactly one column which is the
+ ** primary key.
*/
- if( szCell[j]==4 ){
+ if( b.szCell[j]==4 ){
assert(leafCorrection==4);
- sz = cellSizePtr(pParent, pCell);
+ sz = pParent->xCellSize(pParent, pCell);
}
}
iOvflSpace += sz;
@@ -60289,12 +65365,13 @@ static int balance_nonroot(
iNew = iOld = 0;
nNewCell = cntNew[0];
}else{
- iOld = iPg<nOld ? (cntOld[iPg-1] + !leafData) : nCell;
+ iOld = iPg<nOld ? (cntOld[iPg-1] + !leafData) : b.nCell;
iNew = cntNew[iPg-1] + !leafData;
nNewCell = cntNew[iPg] - iNew;
}
- editPage(apNew[iPg], iOld, iNew, nNewCell, apCell, szCell);
+ rc = editPage(apNew[iPg], iOld, iNew, nNewCell, &b);
+ if( rc ) goto balance_cleanup;
abDone[iPg]++;
apNew[iPg]->nFree = usableSpace-szNew[iPg];
assert( apNew[iPg]->nOverflow==0 );
@@ -60324,7 +65401,7 @@ static int balance_nonroot(
** by smaller than the child due to the database header, and so all the
** free space needs to be up front.
*/
- assert( nNew==1 );
+ assert( nNew==1 || CORRUPT_DB );
rc = defragmentPage(apNew[0]);
testcase( rc!=SQLITE_OK );
assert( apNew[0]->nFree ==
@@ -60345,7 +65422,7 @@ static int balance_nonroot(
assert( pParent->isInit );
TRACE(("BALANCE: finished: old=%d new=%d cells=%d\n",
- nOld, nNew, nCell));
+ nOld, nNew, b.nCell));
/* Free any old pages that were not reused as new pages.
*/
@@ -60368,7 +65445,7 @@ static int balance_nonroot(
** Cleanup before returning.
*/
balance_cleanup:
- sqlite3ScratchFree(apCell);
+ sqlite3ScratchFree(b.apCell);
for(i=0; i<nOld; i++){
releasePage(apOld[i]);
}
@@ -60378,9 +65455,6 @@ balance_cleanup:
return rc;
}
-#if defined(_MSC_VER) && _MSC_VER >= 1700 && defined(_M_ARM)
-#pragma optimize("", on)
-#endif
/*
@@ -60465,8 +65539,8 @@ static int balance(BtCursor *pCur){
u8 aBalanceQuickSpace[13];
u8 *pFree = 0;
- TESTONLY( int balance_quick_called = 0 );
- TESTONLY( int balance_deeper_called = 0 );
+ VVA_ONLY( int balance_quick_called = 0 );
+ VVA_ONLY( int balance_deeper_called = 0 );
do {
int iPage = pCur->iPage;
@@ -60479,7 +65553,8 @@ static int balance(BtCursor *pCur){
** and copy the current contents of the root-page to it. The
** next iteration of the do-loop will balance the child page.
*/
- assert( (balance_deeper_called++)==0 );
+ assert( balance_deeper_called==0 );
+ VVA_ONLY( balance_deeper_called++ );
rc = balance_deeper(pPage, &pCur->apPage[1]);
if( rc==SQLITE_OK ){
pCur->iPage = 1;
@@ -60518,7 +65593,8 @@ static int balance(BtCursor *pCur){
** function. If this were not verified, a subtle bug involving reuse
** of the aBalanceQuickSpace[] might sneak in.
*/
- assert( (balance_quick_called++)==0 );
+ assert( balance_quick_called==0 );
+ VVA_ONLY( balance_quick_called++ );
rc = balance_quick(pParent, pPage, aBalanceQuickSpace);
}else
#endif
@@ -60619,7 +65695,7 @@ SQLITE_PRIVATE int sqlite3BtreeInsert(
return pCur->skipNext;
}
- assert( cursorHoldsMutex(pCur) );
+ assert( cursorOwnsBtShared(pCur) );
assert( (pCur->curFlags & BTCF_WriteFlag)!=0
&& pBt->inTransaction==TRANS_WRITE
&& (pBt->btsFlags & BTS_READ_ONLY)==0 );
@@ -60643,24 +65719,28 @@ SQLITE_PRIVATE int sqlite3BtreeInsert(
** doing any work. To avoid thwarting these optimizations, it is important
** not to clear the cursor here.
*/
- rc = saveAllCursors(pBt, pCur->pgnoRoot, pCur);
- if( rc ) return rc;
+ if( pCur->curFlags & BTCF_Multiple ){
+ rc = saveAllCursors(pBt, pCur->pgnoRoot, pCur);
+ if( rc ) return rc;
+ }
if( pCur->pKeyInfo==0 ){
+ assert( pKey==0 );
/* If this is an insert into a table b-tree, invalidate any incrblob
** cursors open on the row being replaced */
invalidateIncrblobCursors(p, nKey, 0);
/* If the cursor is currently on the last row and we are appending a
- ** new row onto the end, set the "loc" to avoid an unnecessary btreeMoveto()
- ** call */
+ ** new row onto the end, set the "loc" to avoid an unnecessary
+ ** btreeMoveto() call */
if( (pCur->curFlags&BTCF_ValidNKey)!=0 && nKey>0
&& pCur->info.nKey==nKey-1 ){
- loc = -1;
+ loc = -1;
+ }else if( loc==0 ){
+ rc = sqlite3BtreeMovetoUnpacked(pCur, 0, nKey, appendBias, &loc);
+ if( rc ) return rc;
}
- }
-
- if( !loc ){
+ }else if( loc==0 ){
rc = btreeMoveto(pCur, pKey, nKey, appendBias, &loc);
if( rc ) return rc;
}
@@ -60678,7 +65758,7 @@ SQLITE_PRIVATE int sqlite3BtreeInsert(
assert( newCell!=0 );
rc = fillInCell(pPage, newCell, pKey, nKey, pData, nData, nZero, &szNew);
if( rc ) goto end_insert;
- assert( szNew==cellSizePtr(pPage, newCell) );
+ assert( szNew==pPage->xCellSize(pPage, newCell) );
assert( szNew <= MX_CELL_SIZE(pBt) );
idx = pCur->aiIdx[pCur->iPage];
if( loc==0 ){
@@ -60743,10 +65823,23 @@ end_insert:
}
/*
-** Delete the entry that the cursor is pointing to. The cursor
-** is left pointing at an arbitrary location.
+** Delete the entry that the cursor is pointing to.
+**
+** If the BTREE_SAVEPOSITION bit of the flags parameter is zero, then
+** the cursor is left pointing at an arbitrary location after the delete.
+** But if that bit is set, then the cursor is left in a state such that
+** the next call to BtreeNext() or BtreePrev() moves it to the same row
+** as it would have been on if the call to BtreeDelete() had been omitted.
+**
+** The BTREE_AUXDELETE bit of flags indicates that is one of several deletes
+** associated with a single table entry and its indexes. Only one of those
+** deletes is considered the "primary" delete. The primary delete occurs
+** on a cursor that is not a BTREE_FORDELETE cursor. All but one delete
+** operation on non-FORDELETE cursors is tagged with the AUXDELETE flag.
+** The BTREE_AUXDELETE bit is a hint that is not used by this implementation,
+** but which might be used by alternative storage engines.
*/
-SQLITE_PRIVATE int sqlite3BtreeDelete(BtCursor *pCur){
+SQLITE_PRIVATE int sqlite3BtreeDelete(BtCursor *pCur, u8 flags){
Btree *p = pCur->pBtree;
BtShared *pBt = p->pBt;
int rc; /* Return code */
@@ -60755,25 +65848,46 @@ SQLITE_PRIVATE int sqlite3BtreeDelete(BtCursor *pCur){
int iCellIdx; /* Index of cell to delete */
int iCellDepth; /* Depth of node containing pCell */
u16 szCell; /* Size of the cell being deleted */
+ int bSkipnext = 0; /* Leaf cursor in SKIPNEXT state */
+ u8 bPreserve = flags & BTREE_SAVEPOSITION; /* Keep cursor valid */
- assert( cursorHoldsMutex(pCur) );
+ assert( cursorOwnsBtShared(pCur) );
assert( pBt->inTransaction==TRANS_WRITE );
assert( (pBt->btsFlags & BTS_READ_ONLY)==0 );
assert( pCur->curFlags & BTCF_WriteFlag );
assert( hasSharedCacheTableLock(p, pCur->pgnoRoot, pCur->pKeyInfo!=0, 2) );
assert( !hasReadConflicts(p, pCur->pgnoRoot) );
-
- if( NEVER(pCur->aiIdx[pCur->iPage]>=pCur->apPage[pCur->iPage]->nCell)
- || NEVER(pCur->eState!=CURSOR_VALID)
- ){
- return SQLITE_ERROR; /* Something has gone awry. */
- }
+ assert( pCur->aiIdx[pCur->iPage]<pCur->apPage[pCur->iPage]->nCell );
+ assert( pCur->eState==CURSOR_VALID );
+ assert( (flags & ~(BTREE_SAVEPOSITION | BTREE_AUXDELETE))==0 );
iCellDepth = pCur->iPage;
iCellIdx = pCur->aiIdx[iCellDepth];
pPage = pCur->apPage[iCellDepth];
pCell = findCell(pPage, iCellIdx);
+ /* If the bPreserve flag is set to true, then the cursor position must
+ ** be preserved following this delete operation. If the current delete
+ ** will cause a b-tree rebalance, then this is done by saving the cursor
+ ** key and leaving the cursor in CURSOR_REQUIRESEEK state before
+ ** returning.
+ **
+ ** Or, if the current delete will not cause a rebalance, then the cursor
+ ** will be left in CURSOR_SKIPNEXT state pointing to the entry immediately
+ ** before or after the deleted entry. In this case set bSkipnext to true. */
+ if( bPreserve ){
+ if( !pPage->leaf
+ || (pPage->nFree+cellSizePtr(pPage,pCell)+2)>(int)(pBt->usableSize*2/3)
+ ){
+ /* A b-tree rebalance will be required after deleting this entry.
+ ** Save the cursor key. */
+ rc = saveCursorKey(pCur);
+ if( rc ) return rc;
+ }else{
+ bSkipnext = 1;
+ }
+ }
+
/* If the page containing the entry to delete is not a leaf page, move
** the cursor to the largest entry in the tree that is smaller than
** the entry being deleted. This cell will replace the cell being deleted
@@ -60788,12 +65902,11 @@ SQLITE_PRIVATE int sqlite3BtreeDelete(BtCursor *pCur){
}
/* Save the positions of any other cursors open on this table before
- ** making any modifications. Make the page containing the entry to be
- ** deleted writable. Then free any overflow pages associated with the
- ** entry and finally remove the cell itself from within the page.
- */
- rc = saveAllCursors(pBt, pCur->pgnoRoot, pCur);
- if( rc ) return rc;
+ ** making any modifications. */
+ if( pCur->curFlags & BTCF_Multiple ){
+ rc = saveAllCursors(pBt, pCur->pgnoRoot, pCur);
+ if( rc ) return rc;
+ }
/* If this is a delete operation to remove a row from a table b-tree,
** invalidate any incrblob cursors open on the row being deleted. */
@@ -60801,6 +65914,9 @@ SQLITE_PRIVATE int sqlite3BtreeDelete(BtCursor *pCur){
invalidateIncrblobCursors(p, pCur->info.nKey, 0);
}
+ /* Make the page containing the entry to be deleted writable. Then free any
+ ** overflow pages associated with the entry and finally remove the cell
+ ** itself from within the page. */
rc = sqlite3PagerWrite(pPage->pDbPage);
if( rc ) return rc;
rc = clearCell(pPage, pCell, &szCell);
@@ -60819,7 +65935,8 @@ SQLITE_PRIVATE int sqlite3BtreeDelete(BtCursor *pCur){
unsigned char *pTmp;
pCell = findCell(pLeaf, pLeaf->nCell-1);
- nCell = cellSizePtr(pLeaf, pCell);
+ if( pCell<&pLeaf->aData[4] ) return SQLITE_CORRUPT_BKPT;
+ nCell = pLeaf->xCellSize(pLeaf, pCell);
assert( MX_CELL_SIZE(pBt) >= nCell );
pTmp = pBt->pTmpSpace;
assert( pTmp!=0 );
@@ -60853,7 +65970,23 @@ SQLITE_PRIVATE int sqlite3BtreeDelete(BtCursor *pCur){
}
if( rc==SQLITE_OK ){
- moveToRoot(pCur);
+ if( bSkipnext ){
+ assert( bPreserve && (pCur->iPage==iCellDepth || CORRUPT_DB) );
+ assert( pPage==pCur->apPage[pCur->iPage] || CORRUPT_DB );
+ assert( (pPage->nCell>0 || CORRUPT_DB) && iCellIdx<=pPage->nCell );
+ pCur->eState = CURSOR_SKIPNEXT;
+ if( iCellIdx>=pPage->nCell ){
+ pCur->skipNext = -1;
+ pCur->aiIdx[iCellDepth] = pPage->nCell-1;
+ }else{
+ pCur->skipNext = 1;
+ }
+ }else{
+ rc = moveToRoot(pCur);
+ if( bPreserve ){
+ pCur->eState = CURSOR_REQUIRESEEK;
+ }
+ }
}
return rc;
}
@@ -60911,7 +66044,8 @@ static int btreeCreateTable(Btree *p, int *piTable, int createTabFlags){
pgnoRoot==PENDING_BYTE_PAGE(pBt) ){
pgnoRoot++;
}
- assert( pgnoRoot>=3 );
+ assert( pgnoRoot>=3 || CORRUPT_DB );
+ testcase( pgnoRoot<3 );
/* Allocate a page. The page that currently resides at pgnoRoot will
** be moved to the allocated page (unless the allocated page happens
@@ -61040,7 +66174,7 @@ static int clearDatabasePage(
if( pgno>btreePagecount(pBt) ){
return SQLITE_CORRUPT_BKPT;
}
- rc = getAndInitPage(pBt, pgno, &pPage, 0);
+ rc = getAndInitPage(pBt, pgno, &pPage, 0, 0);
if( rc ) return rc;
if( pPage->bBusy ){
rc = SQLITE_CORRUPT_BKPT;
@@ -61061,7 +66195,8 @@ static int clearDatabasePage(
rc = clearDatabasePage(pBt, get4byte(&pPage->aData[hdr+8]), 1, pnChange);
if( rc ) goto cleardatabasepage_out;
}else if( pnChange ){
- assert( pPage->intKey );
+ assert( pPage->intKey || CORRUPT_DB );
+ testcase( !pPage->intKey );
*pnChange += pPage->nCell;
}
if( freePageFlag ){
@@ -61158,6 +66293,14 @@ static int btreeDropTable(Btree *p, Pgno iTable, int *piMoved){
return SQLITE_LOCKED_SHAREDCACHE;
}
+ /*
+ ** It is illegal to drop the sqlite_master table on page 1. But again,
+ ** this error is caught long before reaching this point.
+ */
+ if( NEVER(iTable<2) ){
+ return SQLITE_CORRUPT_BKPT;
+ }
+
rc = btreeGetPage(pBt, (Pgno)iTable, &pPage, 0);
if( rc ) return rc;
rc = sqlite3BtreeClearTable(p, iTable, 0);
@@ -61168,76 +66311,67 @@ static int btreeDropTable(Btree *p, Pgno iTable, int *piMoved){
*piMoved = 0;
- if( iTable>1 ){
#ifdef SQLITE_OMIT_AUTOVACUUM
- freePage(pPage, &rc);
- releasePage(pPage);
+ freePage(pPage, &rc);
+ releasePage(pPage);
#else
- if( pBt->autoVacuum ){
- Pgno maxRootPgno;
- sqlite3BtreeGetMeta(p, BTREE_LARGEST_ROOT_PAGE, &maxRootPgno);
-
- if( iTable==maxRootPgno ){
- /* If the table being dropped is the table with the largest root-page
- ** number in the database, put the root page on the free list.
- */
- freePage(pPage, &rc);
- releasePage(pPage);
- if( rc!=SQLITE_OK ){
- return rc;
- }
- }else{
- /* The table being dropped does not have the largest root-page
- ** number in the database. So move the page that does into the
- ** gap left by the deleted root-page.
- */
- MemPage *pMove;
- releasePage(pPage);
- rc = btreeGetPage(pBt, maxRootPgno, &pMove, 0);
- if( rc!=SQLITE_OK ){
- return rc;
- }
- rc = relocatePage(pBt, pMove, PTRMAP_ROOTPAGE, 0, iTable, 0);
- releasePage(pMove);
- if( rc!=SQLITE_OK ){
- return rc;
- }
- pMove = 0;
- rc = btreeGetPage(pBt, maxRootPgno, &pMove, 0);
- freePage(pMove, &rc);
- releasePage(pMove);
- if( rc!=SQLITE_OK ){
- return rc;
- }
- *piMoved = maxRootPgno;
- }
+ if( pBt->autoVacuum ){
+ Pgno maxRootPgno;
+ sqlite3BtreeGetMeta(p, BTREE_LARGEST_ROOT_PAGE, &maxRootPgno);
- /* Set the new 'max-root-page' value in the database header. This
- ** is the old value less one, less one more if that happens to
- ** be a root-page number, less one again if that is the
- ** PENDING_BYTE_PAGE.
+ if( iTable==maxRootPgno ){
+ /* If the table being dropped is the table with the largest root-page
+ ** number in the database, put the root page on the free list.
*/
- maxRootPgno--;
- while( maxRootPgno==PENDING_BYTE_PAGE(pBt)
- || PTRMAP_ISPAGE(pBt, maxRootPgno) ){
- maxRootPgno--;
+ freePage(pPage, &rc);
+ releasePage(pPage);
+ if( rc!=SQLITE_OK ){
+ return rc;
}
- assert( maxRootPgno!=PENDING_BYTE_PAGE(pBt) );
-
- rc = sqlite3BtreeUpdateMeta(p, 4, maxRootPgno);
}else{
- freePage(pPage, &rc);
+ /* The table being dropped does not have the largest root-page
+ ** number in the database. So move the page that does into the
+ ** gap left by the deleted root-page.
+ */
+ MemPage *pMove;
releasePage(pPage);
+ rc = btreeGetPage(pBt, maxRootPgno, &pMove, 0);
+ if( rc!=SQLITE_OK ){
+ return rc;
+ }
+ rc = relocatePage(pBt, pMove, PTRMAP_ROOTPAGE, 0, iTable, 0);
+ releasePage(pMove);
+ if( rc!=SQLITE_OK ){
+ return rc;
+ }
+ pMove = 0;
+ rc = btreeGetPage(pBt, maxRootPgno, &pMove, 0);
+ freePage(pMove, &rc);
+ releasePage(pMove);
+ if( rc!=SQLITE_OK ){
+ return rc;
+ }
+ *piMoved = maxRootPgno;
}
-#endif
- }else{
- /* If sqlite3BtreeDropTable was called on page 1.
- ** This really never should happen except in a corrupt
- ** database.
+
+ /* Set the new 'max-root-page' value in the database header. This
+ ** is the old value less one, less one more if that happens to
+ ** be a root-page number, less one again if that is the
+ ** PENDING_BYTE_PAGE.
*/
- zeroPage(pPage, PTF_INTKEY|PTF_LEAF );
+ maxRootPgno--;
+ while( maxRootPgno==PENDING_BYTE_PAGE(pBt)
+ || PTRMAP_ISPAGE(pBt, maxRootPgno) ){
+ maxRootPgno--;
+ }
+ assert( maxRootPgno!=PENDING_BYTE_PAGE(pBt) );
+
+ rc = sqlite3BtreeUpdateMeta(p, 4, maxRootPgno);
+ }else{
+ freePage(pPage, &rc);
releasePage(pPage);
}
+#endif
return rc;
}
SQLITE_PRIVATE int sqlite3BtreeDropTable(Btree *p, int iTable, int *piMoved){
@@ -61416,7 +66550,6 @@ static void checkAppendMsg(
...
){
va_list ap;
- char zBuf[200];
if( !pCheck->mxErr ) return;
pCheck->mxErr--;
pCheck->nErr++;
@@ -61425,10 +66558,9 @@ static void checkAppendMsg(
sqlite3StrAccumAppend(&pCheck->errMsg, "\n", 1);
}
if( pCheck->zPfx ){
- sqlite3_snprintf(sizeof(zBuf), zBuf, pCheck->zPfx, pCheck->v1, pCheck->v2);
- sqlite3StrAccumAppendAll(&pCheck->errMsg, zBuf);
+ sqlite3XPrintf(&pCheck->errMsg, pCheck->zPfx, pCheck->v1, pCheck->v2);
}
- sqlite3VXPrintf(&pCheck->errMsg, 1, zFormat, ap);
+ sqlite3VXPrintf(&pCheck->errMsg, zFormat, ap);
va_end(ap);
if( pCheck->errMsg.accError==STRACCUM_NOMEM ){
pCheck->mallocFailed = 1;
@@ -61532,7 +66664,7 @@ static void checkList(
break;
}
if( checkRef(pCheck, iPage) ) break;
- if( sqlite3PagerGet(pCheck->pPager, (Pgno)iPage, &pOvflPage) ){
+ if( sqlite3PagerGet(pCheck->pPager, (Pgno)iPage, &pOvflPage, 0) ){
checkAppendMsg(pCheck, "failed to get page %d", iPage);
break;
}
@@ -61575,6 +66707,10 @@ static void checkList(
#endif
iPage = get4byte(pOvflData);
sqlite3PagerUnref(pOvflPage);
+
+ if( isFreeList && N<(iPage!=0) ){
+ checkAppendMsg(pCheck, "free-page count in header is too small");
+ }
}
}
#endif /* SQLITE_OMIT_INTEGRITY_CHECK */
@@ -61640,35 +66776,42 @@ static int btreeHeapPull(u32 *aHeap, u32 *pOut){
**
** 1. Make sure that cells and freeblocks do not overlap
** but combine to completely cover the page.
-** NO 2. Make sure cell keys are in order.
-** NO 3. Make sure no key is less than or equal to zLowerBound.
-** NO 4. Make sure no key is greater than or equal to zUpperBound.
-** 5. Check the integrity of overflow pages.
-** 6. Recursively call checkTreePage on all children.
-** 7. Verify that the depth of all children is the same.
-** 8. Make sure this page is at least 33% full or else it is
-** the root of the tree.
+** 2. Make sure integer cell keys are in order.
+** 3. Check the integrity of overflow pages.
+** 4. Recursively call checkTreePage on all children.
+** 5. Verify that the depth of all children is the same.
*/
static int checkTreePage(
IntegrityCk *pCheck, /* Context for the sanity check */
int iPage, /* Page number of the page to check */
- i64 *pnParentMinKey,
- i64 *pnParentMaxKey
+ i64 *piMinKey, /* Write minimum integer primary key here */
+ i64 maxKey /* Error if integer primary key greater than this */
){
- MemPage *pPage;
- int i, rc, depth, d2, pgno, cnt;
- int hdr, cellStart;
- int nCell;
- u8 *data;
- BtShared *pBt;
- int usableSize;
- u32 *heap = 0;
- u32 x, prev = 0;
- i64 nMinKey = 0;
- i64 nMaxKey = 0;
+ MemPage *pPage = 0; /* The page being analyzed */
+ int i; /* Loop counter */
+ int rc; /* Result code from subroutine call */
+ int depth = -1, d2; /* Depth of a subtree */
+ int pgno; /* Page number */
+ int nFrag; /* Number of fragmented bytes on the page */
+ int hdr; /* Offset to the page header */
+ int cellStart; /* Offset to the start of the cell pointer array */
+ int nCell; /* Number of cells */
+ int doCoverageCheck = 1; /* True if cell coverage checking should be done */
+ int keyCanBeEqual = 1; /* True if IPK can be equal to maxKey
+ ** False if IPK must be strictly less than maxKey */
+ u8 *data; /* Page content */
+ u8 *pCell; /* Cell content */
+ u8 *pCellIdx; /* Next element of the cell pointer array */
+ BtShared *pBt; /* The BtShared object that owns pPage */
+ u32 pc; /* Address of a cell */
+ u32 usableSize; /* Usable size of the page */
+ u32 contentOffset; /* Offset to the start of the cell content area */
+ u32 *heap = 0; /* Min-heap used for checking cell coverage */
+ u32 x, prev = 0; /* Next and previous entry on the min-heap */
const char *saved_zPfx = pCheck->zPfx;
int saved_v1 = pCheck->v1;
int saved_v2 = pCheck->v2;
+ u8 savedIsInit = 0;
/* Check that the page exists
*/
@@ -61681,54 +66824,95 @@ static int checkTreePage(
if( (rc = btreeGetPage(pBt, (Pgno)iPage, &pPage, 0))!=0 ){
checkAppendMsg(pCheck,
"unable to get the page. error code=%d", rc);
- depth = -1;
goto end_of_check;
}
/* Clear MemPage.isInit to make sure the corruption detection code in
** btreeInitPage() is executed. */
+ savedIsInit = pPage->isInit;
pPage->isInit = 0;
if( (rc = btreeInitPage(pPage))!=0 ){
assert( rc==SQLITE_CORRUPT ); /* The only possible error from InitPage */
checkAppendMsg(pCheck,
"btreeInitPage() returns error code %d", rc);
- releasePage(pPage);
- depth = -1;
goto end_of_check;
}
+ data = pPage->aData;
+ hdr = pPage->hdrOffset;
- /* Check out all the cells.
- */
- depth = 0;
- for(i=0; i<pPage->nCell && pCheck->mxErr; i++){
- u8 *pCell;
- u32 sz;
+ /* Set up for cell analysis */
+ pCheck->zPfx = "On tree page %d cell %d: ";
+ contentOffset = get2byteNotZero(&data[hdr+5]);
+ assert( contentOffset<=usableSize ); /* Enforced by btreeInitPage() */
+
+ /* 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]);
+ assert( pPage->nCell==nCell );
+
+ /* EVIDENCE-OF: R-23882-45353 The cell pointer array of a b-tree page
+ ** immediately follows the b-tree page header. */
+ cellStart = hdr + 12 - 4*pPage->leaf;
+ assert( pPage->aCellIdx==&data[cellStart] );
+ pCellIdx = &data[cellStart + 2*(nCell-1)];
+
+ if( !pPage->leaf ){
+ /* Analyze the right-child page of internal pages */
+ pgno = get4byte(&data[hdr+8]);
+#ifndef SQLITE_OMIT_AUTOVACUUM
+ if( pBt->autoVacuum ){
+ pCheck->zPfx = "On page %d at right child: ";
+ checkPtrmap(pCheck, pgno, PTRMAP_BTREE, iPage);
+ }
+#endif
+ depth = checkTreePage(pCheck, pgno, &maxKey, maxKey);
+ keyCanBeEqual = 0;
+ }else{
+ /* For leaf pages, the coverage check will occur in the same loop
+ ** as the other cell checks, so initialize the heap. */
+ heap = pCheck->heap;
+ heap[0] = 0;
+ }
+
+ /* EVIDENCE-OF: R-02776-14802 The cell pointer array consists of K 2-byte
+ ** integer offsets to the cell contents. */
+ for(i=nCell-1; i>=0 && pCheck->mxErr; i--){
CellInfo info;
- /* Check payload overflow pages
- */
- pCheck->zPfx = "On tree page %d cell %d: ";
- pCheck->v1 = iPage;
+ /* Check cell size */
pCheck->v2 = i;
- pCell = findCell(pPage,i);
- btreeParseCellPtr(pPage, pCell, &info);
- sz = info.nPayload;
- /* For intKey pages, check that the keys are in order.
- */
+ assert( pCellIdx==&data[cellStart + i*2] );
+ pc = get2byteAligned(pCellIdx);
+ pCellIdx -= 2;
+ if( pc<contentOffset || pc>usableSize-4 ){
+ checkAppendMsg(pCheck, "Offset %d out of range %d..%d",
+ pc, contentOffset, usableSize-4);
+ doCoverageCheck = 0;
+ continue;
+ }
+ pCell = &data[pc];
+ pPage->xParseCell(pPage, pCell, &info);
+ if( pc+info.nSize>usableSize ){
+ checkAppendMsg(pCheck, "Extends off end of page");
+ doCoverageCheck = 0;
+ continue;
+ }
+
+ /* Check for integer primary key out of range */
if( pPage->intKey ){
- if( i==0 ){
- nMinKey = nMaxKey = info.nKey;
- }else if( info.nKey <= nMaxKey ){
- checkAppendMsg(pCheck,
- "Rowid %lld out of order (previous was %lld)", info.nKey, nMaxKey);
+ if( keyCanBeEqual ? (info.nKey > maxKey) : (info.nKey >= maxKey) ){
+ checkAppendMsg(pCheck, "Rowid %lld out of order", info.nKey);
}
- nMaxKey = info.nKey;
+ maxKey = info.nKey;
}
- if( (sz>info.nLocal)
- && (&pCell[info.iOverflow]<=&pPage->aData[pBt->usableSize])
- ){
- int nPage = (sz - info.nLocal + usableSize - 5)/(usableSize - 4);
- Pgno pgnoOvfl = get4byte(&pCell[info.iOverflow]);
+
+ /* Check the content overflow list */
+ if( info.nPayload>info.nLocal ){
+ int nPage; /* Number of pages on the overflow chain */
+ Pgno pgnoOvfl; /* First page of the overflow chain */
+ assert( pc + info.nSize - 4 <= usableSize );
+ nPage = (info.nPayload - info.nLocal + usableSize - 5)/(usableSize - 4);
+ pgnoOvfl = get4byte(&pCell[info.nSize - 4]);
#ifndef SQLITE_OMIT_AUTOVACUUM
if( pBt->autoVacuum ){
checkPtrmap(pCheck, pgnoOvfl, PTRMAP_OVERFLOW1, iPage);
@@ -61737,118 +66921,57 @@ static int checkTreePage(
checkList(pCheck, 0, pgnoOvfl, nPage);
}
- /* Check sanity of left child page.
- */
if( !pPage->leaf ){
+ /* Check sanity of left child page for internal pages */
pgno = get4byte(pCell);
#ifndef SQLITE_OMIT_AUTOVACUUM
if( pBt->autoVacuum ){
checkPtrmap(pCheck, pgno, PTRMAP_BTREE, iPage);
}
#endif
- d2 = checkTreePage(pCheck, pgno, &nMinKey, i==0?NULL:&nMaxKey);
- if( i>0 && d2!=depth ){
+ d2 = checkTreePage(pCheck, pgno, &maxKey, maxKey);
+ keyCanBeEqual = 0;
+ if( d2!=depth ){
checkAppendMsg(pCheck, "Child page depth differs");
+ depth = d2;
}
- depth = d2;
- }
- }
-
- if( !pPage->leaf ){
- pgno = get4byte(&pPage->aData[pPage->hdrOffset+8]);
- pCheck->zPfx = "On page %d at right child: ";
- pCheck->v1 = iPage;
-#ifndef SQLITE_OMIT_AUTOVACUUM
- if( pBt->autoVacuum ){
- checkPtrmap(pCheck, pgno, PTRMAP_BTREE, iPage);
- }
-#endif
- checkTreePage(pCheck, pgno, NULL, !pPage->nCell?NULL:&nMaxKey);
- }
-
- /* For intKey leaf pages, check that the min/max keys are in order
- ** with any left/parent/right pages.
- */
- pCheck->zPfx = "Page %d: ";
- pCheck->v1 = iPage;
- if( pPage->leaf && pPage->intKey ){
- /* if we are a left child page */
- if( pnParentMinKey ){
- /* if we are the left most child page */
- if( !pnParentMaxKey ){
- if( nMaxKey > *pnParentMinKey ){
- checkAppendMsg(pCheck,
- "Rowid %lld out of order (max larger than parent min of %lld)",
- nMaxKey, *pnParentMinKey);
- }
- }else{
- if( nMinKey <= *pnParentMinKey ){
- checkAppendMsg(pCheck,
- "Rowid %lld out of order (min less than parent min of %lld)",
- nMinKey, *pnParentMinKey);
- }
- if( nMaxKey > *pnParentMaxKey ){
- checkAppendMsg(pCheck,
- "Rowid %lld out of order (max larger than parent max of %lld)",
- nMaxKey, *pnParentMaxKey);
- }
- *pnParentMinKey = nMaxKey;
- }
- /* else if we're a right child page */
- } else if( pnParentMaxKey ){
- if( nMinKey <= *pnParentMaxKey ){
- checkAppendMsg(pCheck,
- "Rowid %lld out of order (min less than parent max of %lld)",
- nMinKey, *pnParentMaxKey);
- }
+ }else{
+ /* Populate the coverage-checking heap for leaf pages */
+ btreeHeapInsert(heap, (pc<<16)|(pc+info.nSize-1));
}
}
+ *piMinKey = maxKey;
/* Check for complete coverage of the page
*/
- data = pPage->aData;
- hdr = pPage->hdrOffset;
- heap = (u32*)sqlite3PageMalloc( pBt->pageSize );
pCheck->zPfx = 0;
- if( heap==0 ){
- pCheck->mallocFailed = 1;
- }else{
- int contentOffset = get2byteNotZero(&data[hdr+5]);
- assert( contentOffset<=usableSize ); /* Enforced by btreeInitPage() */
- 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]);
- /* EVIDENCE-OF: R-23882-45353 The cell pointer array of a b-tree page
- ** immediately follows the b-tree page header. */
- cellStart = hdr + 12 - 4*pPage->leaf;
- /* EVIDENCE-OF: R-02776-14802 The cell pointer array consists of K 2-byte
- ** integer offsets to the cell contents. */
- for(i=0; i<nCell; i++){
- int pc = get2byte(&data[cellStart+i*2]);
- u32 size = 65536;
- if( pc<=usableSize-4 ){
- size = cellSizePtr(pPage, &data[pc]);
- }
- if( (int)(pc+size-1)>=usableSize ){
- pCheck->zPfx = 0;
- checkAppendMsg(pCheck,
- "Corruption detected in cell %d on page %d",i,iPage);
- }else{
+ if( doCoverageCheck && pCheck->mxErr>0 ){
+ /* For leaf pages, the min-heap has already been initialized and the
+ ** cells have already been inserted. But for internal pages, that has
+ ** not yet been done, so do it now */
+ if( !pPage->leaf ){
+ heap = pCheck->heap;
+ heap[0] = 0;
+ for(i=nCell-1; i>=0; i--){
+ u32 size;
+ pc = get2byteAligned(&data[cellStart+i*2]);
+ size = pPage->xCellSize(pPage, &data[pc]);
btreeHeapInsert(heap, (pc<<16)|(pc+size-1));
}
}
- /* EVIDENCE-OF: R-20690-50594 The second field of the b-tree page header
+ /* Add the freeblocks to the min-heap
+ **
+ ** EVIDENCE-OF: R-20690-50594 The second field of the b-tree page header
** is the offset of the first freeblock, or zero if there are no
- ** freeblocks on the page. */
+ ** freeblocks on the page.
+ */
i = get2byte(&data[hdr+1]);
while( i>0 ){
int size, j;
- assert( i<=usableSize-4 ); /* Enforced by btreeInitPage() */
+ assert( (u32)i<=usableSize-4 ); /* Enforced by btreeInitPage() */
size = get2byte(&data[i+2]);
- assert( i+size<=usableSize ); /* Enforced by btreeInitPage() */
- btreeHeapInsert(heap, (i<<16)|(i+size-1));
+ assert( (u32)(i+size)<=usableSize ); /* Enforced by btreeInitPage() */
+ btreeHeapInsert(heap, (((u32)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
@@ -61857,39 +66980,50 @@ static int checkTreePage(
/* EVIDENCE-OF: R-06866-39125 Freeblocks are always connected in order of
** increasing offset. */
assert( j==0 || j>i+size ); /* Enforced by btreeInitPage() */
- assert( j<=usableSize-4 ); /* Enforced by btreeInitPage() */
+ assert( (u32)j<=usableSize-4 ); /* Enforced by btreeInitPage() */
i = j;
}
- cnt = 0;
- assert( heap[0]>0 );
- assert( (heap[1]>>16)==0 );
- btreeHeapPull(heap,&prev);
+ /* Analyze the min-heap looking for overlap between cells and/or
+ ** freeblocks, and counting the number of untracked bytes in nFrag.
+ **
+ ** Each min-heap entry is of the form: (start_address<<16)|end_address.
+ ** There is an implied first entry the covers the page header, the cell
+ ** pointer index, and the gap between the cell pointer index and the start
+ ** of cell content.
+ **
+ ** The loop below pulls entries from the min-heap in order and compares
+ ** the start_address against the previous end_address. If there is an
+ ** overlap, that means bytes are used multiple times. If there is a gap,
+ ** that gap is added to the fragmentation count.
+ */
+ nFrag = 0;
+ prev = contentOffset - 1; /* Implied first min-heap entry */
while( btreeHeapPull(heap,&x) ){
- if( (prev&0xffff)+1>(x>>16) ){
+ if( (prev&0xffff)>=(x>>16) ){
checkAppendMsg(pCheck,
"Multiple uses for byte %u of page %d", x>>16, iPage);
break;
}else{
- cnt += (x>>16) - (prev&0xffff) - 1;
+ nFrag += (x>>16) - (prev&0xffff) - 1;
prev = x;
}
}
- cnt += usableSize - (prev&0xffff) - 1;
+ nFrag += 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( heap[0]==0 && cnt!=data[hdr+7] ){
+ if( heap[0]==0 && nFrag!=data[hdr+7] ){
checkAppendMsg(pCheck,
"Fragmentation of %d bytes reported as %d on page %d",
- cnt, data[hdr+7], iPage);
+ nFrag, data[hdr+7], iPage);
}
}
- sqlite3PageFree(heap);
- releasePage(pPage);
end_of_check:
+ if( !doCoverageCheck ) pPage->isInit = savedIsInit;
+ releasePage(pPage);
pCheck->zPfx = saved_zPfx;
pCheck->v1 = saved_v1;
pCheck->v2 = saved_v2;
@@ -61919,14 +67053,16 @@ SQLITE_PRIVATE char *sqlite3BtreeIntegrityCheck(
int *pnErr /* Write number of errors seen to this variable */
){
Pgno i;
- int nRef;
IntegrityCk sCheck;
BtShared *pBt = p->pBt;
+ int savedDbFlags = pBt->db->flags;
char zErr[100];
+ VVA_ONLY( int nRef );
sqlite3BtreeEnter(p);
assert( p->inTrans>TRANS_NONE && pBt->inTransaction>TRANS_NONE );
- nRef = sqlite3PagerRefcount(pBt->pPager);
+ VVA_ONLY( nRef = sqlite3PagerRefcount(pBt->pPager) );
+ assert( nRef>=0 );
sCheck.pBt = pBt;
sCheck.pPager = pBt->pPager;
sCheck.nPage = btreePagecount(sCheck.pBt);
@@ -61936,21 +67072,27 @@ SQLITE_PRIVATE char *sqlite3BtreeIntegrityCheck(
sCheck.zPfx = 0;
sCheck.v1 = 0;
sCheck.v2 = 0;
- *pnErr = 0;
+ sCheck.aPgRef = 0;
+ sCheck.heap = 0;
+ sqlite3StrAccumInit(&sCheck.errMsg, 0, zErr, sizeof(zErr), SQLITE_MAX_LENGTH);
+ sCheck.errMsg.printfFlags = SQLITE_PRINTF_INTERNAL;
if( sCheck.nPage==0 ){
- sqlite3BtreeLeave(p);
- return 0;
+ goto integrity_ck_cleanup;
}
sCheck.aPgRef = sqlite3MallocZero((sCheck.nPage / 8)+ 1);
if( !sCheck.aPgRef ){
- *pnErr = 1;
- sqlite3BtreeLeave(p);
- return 0;
+ sCheck.mallocFailed = 1;
+ goto integrity_ck_cleanup;
}
+ sCheck.heap = (u32*)sqlite3PageMalloc( pBt->pageSize );
+ if( sCheck.heap==0 ){
+ sCheck.mallocFailed = 1;
+ goto integrity_ck_cleanup;
+ }
+
i = PENDING_BYTE_PAGE(pBt);
if( i<=sCheck.nPage ) setPageReferenced(&sCheck, i);
- sqlite3StrAccumInit(&sCheck.errMsg, 0, zErr, sizeof(zErr), SQLITE_MAX_LENGTH);
/* Check the integrity of the freelist
*/
@@ -61961,17 +67103,19 @@ SQLITE_PRIVATE char *sqlite3BtreeIntegrityCheck(
/* Check all the tables.
*/
+ testcase( pBt->db->flags & SQLITE_CellSizeCk );
+ pBt->db->flags &= ~SQLITE_CellSizeCk;
for(i=0; (int)i<nRoot && sCheck.mxErr; i++){
+ i64 notUsed;
if( aRoot[i]==0 ) continue;
#ifndef SQLITE_OMIT_AUTOVACUUM
if( pBt->autoVacuum && aRoot[i]>1 ){
checkPtrmap(&sCheck, aRoot[i], PTRMAP_ROOTPAGE, 0);
}
#endif
- sCheck.zPfx = "List of tree roots: ";
- checkTreePage(&sCheck, aRoot[i], NULL, NULL);
- sCheck.zPfx = 0;
+ checkTreePage(&sCheck, aRoot[i], &notUsed, LARGEST_INT64);
}
+ pBt->db->flags = savedDbFlags;
/* Make sure every page in the file is referenced
*/
@@ -61995,28 +67139,20 @@ SQLITE_PRIVATE char *sqlite3BtreeIntegrityCheck(
#endif
}
- /* Make sure this analysis did not leave any unref() pages.
- ** This is an internal consistency check; an integrity check
- ** of the integrity check.
- */
- if( NEVER(nRef != sqlite3PagerRefcount(pBt->pPager)) ){
- checkAppendMsg(&sCheck,
- "Outstanding page count goes from %d to %d during this analysis",
- nRef, sqlite3PagerRefcount(pBt->pPager)
- );
- }
-
/* Clean up and report errors.
*/
- sqlite3BtreeLeave(p);
+integrity_ck_cleanup:
+ sqlite3PageFree(sCheck.heap);
sqlite3_free(sCheck.aPgRef);
if( sCheck.mallocFailed ){
sqlite3StrAccumReset(&sCheck.errMsg);
- *pnErr = sCheck.nErr+1;
- return 0;
+ sCheck.nErr++;
}
*pnErr = sCheck.nErr;
if( sCheck.nErr==0 ) sqlite3StrAccumReset(&sCheck.errMsg);
+ /* Make sure this analysis did not leave any unref() pages. */
+ assert( nRef==sqlite3PagerRefcount(pBt->pPager) );
+ sqlite3BtreeLeave(p);
return sqlite3StrAccumFinish(&sCheck.errMsg);
}
#endif /* SQLITE_OMIT_INTEGRITY_CHECK */
@@ -62179,7 +67315,7 @@ SQLITE_PRIVATE int sqlite3BtreeLockTable(Btree *p, int iTab, u8 isWriteLock){
*/
SQLITE_PRIVATE int sqlite3BtreePutData(BtCursor *pCsr, u32 offset, u32 amt, void *z){
int rc;
- assert( cursorHoldsMutex(pCsr) );
+ assert( cursorOwnsBtShared(pCsr) );
assert( sqlite3_mutex_held(pCsr->pBtree->db->mutex) );
assert( pCsr->curFlags & BTCF_Incrblob );
@@ -62227,6 +67363,7 @@ SQLITE_PRIVATE int sqlite3BtreePutData(BtCursor *pCsr, u32 offset, u32 amt, void
*/
SQLITE_PRIVATE void sqlite3BtreeIncrblobCursor(BtCursor *pCur){
pCur->curFlags |= BTCF_Incrblob;
+ pCur->pBtree->hasIncrblobCur = 1;
}
#endif
@@ -62267,22 +67404,12 @@ SQLITE_PRIVATE int sqlite3BtreeSetVersion(Btree *pBtree, int iVersion){
}
/*
-** set the mask of hint flags for cursor pCsr.
-*/
-SQLITE_PRIVATE void sqlite3BtreeCursorHints(BtCursor *pCsr, unsigned int mask){
- 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.
@@ -62296,6 +67423,15 @@ SQLITE_PRIVATE int sqlite3BtreeIsReadonly(Btree *p){
*/
SQLITE_PRIVATE int sqlite3HeaderSizeBtree(void){ return ROUND8(sizeof(MemPage)); }
+#if !defined(SQLITE_OMIT_SHARED_CACHE)
+/*
+** Return true if the Btree passed as the only argument is sharable.
+*/
+SQLITE_PRIVATE int sqlite3BtreeSharable(Btree *p){
+ return p->sharable;
+}
+#endif
+
/************** End of btree.c ***********************************************/
/************** Begin file backup.c ******************************************/
/*
@@ -62312,6 +67448,8 @@ SQLITE_PRIVATE int sqlite3HeaderSizeBtree(void){ return ROUND8(sizeof(MemPage));
** This file contains the implementation of the sqlite3_backup_XXX()
** API functions and the related features.
*/
+/* #include "sqliteInt.h" */
+/* #include "btreeInt.h" */
/*
** Structure allocated for each backup operation.
@@ -62386,7 +67524,7 @@ static Btree *findBtree(sqlite3 *pErrorDb, sqlite3 *pDb, const char *zDb){
pParse = sqlite3StackAllocZero(pErrorDb, sizeof(*pParse));
if( pParse==0 ){
sqlite3ErrorWithMsg(pErrorDb, SQLITE_NOMEM, "out of memory");
- rc = SQLITE_NOMEM;
+ rc = SQLITE_NOMEM_BKPT;
}else{
pParse->db = pDb;
if( sqlite3OpenTempDatabase(pParse) ){
@@ -62480,7 +67618,7 @@ SQLITE_API sqlite3_backup *SQLITE_STDCALL sqlite3_backup_init(
** sqlite3_backup_finish(). */
p = (sqlite3_backup *)sqlite3MallocZero(sizeof(sqlite3_backup));
if( !p ){
- sqlite3Error(pDestDb, SQLITE_NOMEM);
+ sqlite3Error(pDestDb, SQLITE_NOMEM_BKPT);
}
}
@@ -62591,7 +67729,7 @@ static int backupOnePage(
DbPage *pDestPg = 0;
Pgno iDest = (Pgno)(iOff/nDestPgsz)+1;
if( iDest==PENDING_BYTE_PAGE(p->pDest->pBt) ) continue;
- if( SQLITE_OK==(rc = sqlite3PagerGet(pDestPager, iDest, &pDestPg))
+ if( SQLITE_OK==(rc = sqlite3PagerGet(pDestPager, iDest, &pDestPg, 0))
&& SQLITE_OK==(rc = sqlite3PagerWrite(pDestPg))
){
const u8 *zIn = &zSrcData[iOff%nSrcPgsz];
@@ -62717,8 +67855,7 @@ SQLITE_API int SQLITE_STDCALL sqlite3_backup_step(sqlite3_backup *p, int nPage){
const Pgno iSrcPg = p->iNext; /* Source page number */
if( iSrcPg!=PENDING_BYTE_PAGE(p->pSrc->pBt) ){
DbPage *pSrcPg; /* Source page object */
- rc = sqlite3PagerAcquire(pSrcPager, iSrcPg, &pSrcPg,
- PAGER_GET_READONLY);
+ rc = sqlite3PagerGet(pSrcPager, iSrcPg, &pSrcPg,PAGER_GET_READONLY);
if( rc==SQLITE_OK ){
rc = backupOnePage(p, iSrcPg, sqlite3PagerGetData(pSrcPg), 0);
sqlite3PagerUnref(pSrcPg);
@@ -62818,7 +67955,7 @@ SQLITE_API int SQLITE_STDCALL sqlite3_backup_step(sqlite3_backup *p, int nPage){
for(iPg=nDestTruncate; rc==SQLITE_OK && iPg<=(Pgno)nDstPage; iPg++){
if( iPg!=PENDING_BYTE_PAGE(p->pDest->pBt) ){
DbPage *pPg;
- rc = sqlite3PagerGet(pDestPager, iPg, &pPg);
+ rc = sqlite3PagerGet(pDestPager, iPg, &pPg, 0);
if( rc==SQLITE_OK ){
rc = sqlite3PagerWrite(pPg);
sqlite3PagerUnref(pPg);
@@ -62838,7 +67975,7 @@ SQLITE_API int SQLITE_STDCALL sqlite3_backup_step(sqlite3_backup *p, int nPage){
){
PgHdr *pSrcPg = 0;
const Pgno iSrcPg = (Pgno)((iOff/pgszSrc)+1);
- rc = sqlite3PagerGet(pSrcPager, iSrcPg, &pSrcPg);
+ rc = sqlite3PagerGet(pSrcPager, iSrcPg, &pSrcPg, 0);
if( rc==SQLITE_OK ){
u8 *zData = sqlite3PagerGetData(pSrcPg);
rc = sqlite3OsWrite(pFile, zData, pgszSrc, iOff);
@@ -62880,7 +68017,7 @@ SQLITE_API int SQLITE_STDCALL sqlite3_backup_step(sqlite3_backup *p, int nPage){
}
if( rc==SQLITE_IOERR_NOMEM ){
- rc = SQLITE_NOMEM;
+ rc = SQLITE_NOMEM_BKPT;
}
p->rc = rc;
}
@@ -62983,9 +68120,13 @@ SQLITE_API int SQLITE_STDCALL sqlite3_backup_pagecount(sqlite3_backup *p){
** corresponding to the source database is held when this function is
** called.
*/
-SQLITE_PRIVATE void sqlite3BackupUpdate(sqlite3_backup *pBackup, Pgno iPage, const u8 *aData){
- sqlite3_backup *p; /* Iterator variable */
- for(p=pBackup; p; p=p->pNext){
+static SQLITE_NOINLINE void backupUpdate(
+ sqlite3_backup *p,
+ Pgno iPage,
+ const u8 *aData
+){
+ assert( p!=0 );
+ do{
assert( sqlite3_mutex_held(p->pSrc->pBt->mutex) );
if( !isFatalError(p->rc) && iPage<p->iNext ){
/* The backup process p has already copied page iPage. But now it
@@ -63002,7 +68143,10 @@ SQLITE_PRIVATE void sqlite3BackupUpdate(sqlite3_backup *pBackup, Pgno iPage, con
p->rc = rc;
}
}
- }
+ }while( (p = p->pNext)!=0 );
+}
+SQLITE_PRIVATE void sqlite3BackupUpdate(sqlite3_backup *pBackup, Pgno iPage, const u8 *aData){
+ if( pBackup ) backupUpdate(pBackup, iPage, aData);
}
/*
@@ -63060,6 +68204,10 @@ SQLITE_PRIVATE int sqlite3BtreeCopyFile(Btree *pTo, Btree *pFrom){
b.pDest = pTo;
b.iNext = 1;
+#ifdef SQLITE_HAS_CODEC
+ sqlite3PagerAlignReserve(sqlite3BtreePager(pTo), sqlite3BtreePager(pFrom));
+#endif
+
/* 0x7FFFFFFF is the hard limit for the number of pages in a database
** file. By passing this as the number of pages to copy to
** sqlite3_backup_step(), we can guarantee that the copy finishes
@@ -63103,6 +68251,8 @@ copy_finished:
** only within the VDBE. Interface routines refer to a Mem using the
** name sqlite_value
*/
+/* #include "sqliteInt.h" */
+/* #include "vdbeInt.h" */
#ifdef SQLITE_DEBUG
/*
@@ -63202,6 +68352,7 @@ SQLITE_PRIVATE int sqlite3VdbeChangeEncoding(Mem *pMem, int desiredEnc){
SQLITE_PRIVATE SQLITE_NOINLINE int sqlite3VdbeMemGrow(Mem *pMem, int n, int bPreserve){
assert( sqlite3VdbeCheckMemInvariants(pMem) );
assert( (pMem->flags&MEM_RowSet)==0 );
+ testcase( pMem->db==0 );
/* If the bPreserve flag is set to true, then the memory cell must already
** contain a valid string or blob value. */
@@ -63223,7 +68374,7 @@ SQLITE_PRIVATE SQLITE_NOINLINE int sqlite3VdbeMemGrow(Mem *pMem, int n, int bPre
sqlite3VdbeMemSetNull(pMem);
pMem->z = 0;
pMem->szMalloc = 0;
- return SQLITE_NOMEM;
+ return SQLITE_NOMEM_BKPT;
}else{
pMem->szMalloc = sqlite3DbMallocSize(pMem->db, pMem->zMalloc);
}
@@ -63281,7 +68432,7 @@ SQLITE_PRIVATE int sqlite3VdbeMemMakeWriteable(Mem *pMem){
f = pMem->flags;
if( (f&(MEM_Str|MEM_Blob)) && (pMem->szMalloc==0 || pMem->z!=pMem->zMalloc) ){
if( sqlite3VdbeMemGrow(pMem, pMem->n + 2, 1) ){
- return SQLITE_NOMEM;
+ return SQLITE_NOMEM_BKPT;
}
pMem->z[pMem->n] = 0;
pMem->z[pMem->n+1] = 0;
@@ -63313,7 +68464,7 @@ SQLITE_PRIVATE int sqlite3VdbeMemExpandBlob(Mem *pMem){
nByte = 1;
}
if( sqlite3VdbeMemGrow(pMem, nByte, 1) ){
- return SQLITE_NOMEM;
+ return SQLITE_NOMEM_BKPT;
}
memset(&pMem->z[pMem->n], 0, pMem->u.nZero);
@@ -63330,7 +68481,7 @@ SQLITE_PRIVATE int sqlite3VdbeMemExpandBlob(Mem *pMem){
*/
static SQLITE_NOINLINE int vdbeMemAddTerminator(Mem *pMem){
if( sqlite3VdbeMemGrow(pMem, pMem->n+2, 1) ){
- return SQLITE_NOMEM;
+ return SQLITE_NOMEM_BKPT;
}
pMem->z[pMem->n] = 0;
pMem->z[pMem->n+1] = 0;
@@ -63379,7 +68530,7 @@ SQLITE_PRIVATE int sqlite3VdbeMemStringify(Mem *pMem, u8 enc, u8 bForce){
if( sqlite3VdbeMemClearAndResize(pMem, nByte) ){
- return SQLITE_NOMEM;
+ return SQLITE_NOMEM_BKPT;
}
/* For a Real or Integer, use sqlite3_snprintf() to produce the UTF-8
@@ -63674,7 +68825,7 @@ SQLITE_PRIVATE int sqlite3VdbeMemNumerify(Mem *pMem){
SQLITE_PRIVATE void sqlite3VdbeMemCast(Mem *pMem, u8 aff, u8 encoding){
if( pMem->flags & MEM_Null ) return;
switch( aff ){
- case SQLITE_AFF_NONE: { /* Really a cast to BLOB */
+ case SQLITE_AFF_BLOB: { /* Really a cast to BLOB */
if( (pMem->flags & MEM_Blob)==0 ){
sqlite3ValueApplyAffinity(pMem, SQLITE_AFF_TEXT, encoding);
assert( pMem->flags & MEM_Str || pMem->db->mallocFailed );
@@ -63805,7 +68956,7 @@ SQLITE_PRIVATE void sqlite3VdbeMemSetRowSet(Mem *pMem){
assert( db!=0 );
assert( (pMem->flags & MEM_RowSet)==0 );
sqlite3VdbeMemRelease(pMem);
- pMem->zMalloc = sqlite3DbMallocRaw(db, 64);
+ pMem->zMalloc = sqlite3DbMallocRawNN(db, 64);
if( db->mallocFailed ){
pMem->flags = MEM_Null;
pMem->szMalloc = 0;
@@ -63846,7 +68997,7 @@ SQLITE_PRIVATE int sqlite3VdbeMemTooBig(Mem *p){
SQLITE_PRIVATE void sqlite3VdbeMemAboutToChange(Vdbe *pVdbe, Mem *pMem){
int i;
Mem *pX;
- for(i=1, pX=&pVdbe->aMem[1]; i<=pVdbe->nMem; i++, pX++){
+ for(i=0, pX=pVdbe->aMem; i<pVdbe->nMem; i++, pX++){
if( pX->pScopyFrom==pMem ){
pX->flags |= MEM_Undefined;
pX->pScopyFrom = 0;
@@ -63856,10 +69007,6 @@ SQLITE_PRIVATE void sqlite3VdbeMemAboutToChange(Vdbe *pVdbe, Mem *pMem){
}
#endif /* SQLITE_DEBUG */
-/*
-** Size of struct Mem not including the Mem.zMalloc member.
-*/
-#define MEMCELLSIZE offsetof(Mem,zMalloc)
/*
** Make an shallow copy of pFrom into pTo. Prior contents of
@@ -63867,10 +69014,15 @@ SQLITE_PRIVATE void sqlite3VdbeMemAboutToChange(Vdbe *pVdbe, Mem *pMem){
** pFrom->z is used, then pTo->z points to the same thing as pFrom->z
** and flags gets srcType (either MEM_Ephem or MEM_Static).
*/
+static SQLITE_NOINLINE void vdbeClrCopy(Mem *pTo, const Mem *pFrom, int eType){
+ vdbeMemClearExternAndSetNull(pTo);
+ assert( !VdbeMemDynamic(pTo) );
+ sqlite3VdbeMemShallowCopy(pTo, pFrom, eType);
+}
SQLITE_PRIVATE void sqlite3VdbeMemShallowCopy(Mem *pTo, const Mem *pFrom, int srcType){
assert( (pFrom->flags & MEM_RowSet)==0 );
assert( pTo->db==pFrom->db );
- if( VdbeMemDynamic(pTo) ) vdbeMemClearExternAndSetNull(pTo);
+ if( VdbeMemDynamic(pTo) ){ vdbeClrCopy(pTo,pFrom,srcType); return; }
memcpy(pTo, pFrom, MEMCELLSIZE);
if( (pFrom->flags&MEM_Static)==0 ){
pTo->flags &= ~(MEM_Dyn|MEM_Static|MEM_Ephem);
@@ -63886,7 +69038,6 @@ SQLITE_PRIVATE void sqlite3VdbeMemShallowCopy(Mem *pTo, const Mem *pFrom, int sr
SQLITE_PRIVATE int sqlite3VdbeMemCopy(Mem *pTo, const Mem *pFrom){
int rc = SQLITE_OK;
- assert( pTo->db==pFrom->db );
assert( (pFrom->flags & MEM_RowSet)==0 );
if( VdbeMemDynamic(pTo) ) vdbeMemClearExternAndSetNull(pTo);
memcpy(pTo, pFrom, MEMCELLSIZE);
@@ -63986,7 +69137,7 @@ SQLITE_PRIVATE int sqlite3VdbeMemSetStr(
testcase( nAlloc==31 );
testcase( nAlloc==32 );
if( sqlite3VdbeMemClearAndResize(pMem, MAX(nAlloc,32)) ){
- return SQLITE_NOMEM;
+ return SQLITE_NOMEM_BKPT;
}
memcpy(pMem->z, z, nAlloc);
}else if( xDel==SQLITE_DYNAMIC ){
@@ -64006,7 +69157,7 @@ SQLITE_PRIVATE int sqlite3VdbeMemSetStr(
#ifndef SQLITE_OMIT_UTF16
if( pMem->enc!=SQLITE_UTF8 && sqlite3VdbeMemHandleBom(pMem) ){
- return SQLITE_NOMEM;
+ return SQLITE_NOMEM_BKPT;
}
#endif
@@ -64033,6 +69184,32 @@ SQLITE_PRIVATE int sqlite3VdbeMemSetStr(
** If this routine fails for any reason (malloc returns NULL or unable
** to read from the disk) then the pMem is left in an inconsistent state.
*/
+static SQLITE_NOINLINE int vdbeMemFromBtreeResize(
+ BtCursor *pCur, /* Cursor pointing at record to retrieve. */
+ u32 offset, /* Offset from the start of data to return bytes from. */
+ u32 amt, /* Number of bytes to return. */
+ int key, /* If true, retrieve from the btree key, not data. */
+ Mem *pMem /* OUT: Return data in this Mem structure. */
+){
+ int rc;
+ pMem->flags = MEM_Null;
+ if( SQLITE_OK==(rc = sqlite3VdbeMemClearAndResize(pMem, amt+2)) ){
+ if( key ){
+ rc = sqlite3BtreeKey(pCur, offset, amt, pMem->z);
+ }else{
+ rc = sqlite3BtreeData(pCur, offset, amt, pMem->z);
+ }
+ if( rc==SQLITE_OK ){
+ pMem->z[amt] = 0;
+ pMem->z[amt+1] = 0;
+ pMem->flags = MEM_Blob|MEM_Term;
+ pMem->n = (int)amt;
+ }else{
+ sqlite3VdbeMemRelease(pMem);
+ }
+ }
+ return rc;
+}
SQLITE_PRIVATE int sqlite3VdbeMemFromBtree(
BtCursor *pCur, /* Cursor pointing at record to retrieve. */
u32 offset, /* Offset from the start of data to return bytes from. */
@@ -64062,22 +69239,7 @@ SQLITE_PRIVATE int sqlite3VdbeMemFromBtree(
pMem->flags = MEM_Blob|MEM_Ephem;
pMem->n = (int)amt;
}else{
- pMem->flags = MEM_Null;
- if( SQLITE_OK==(rc = sqlite3VdbeMemClearAndResize(pMem, amt+2)) ){
- if( key ){
- rc = sqlite3BtreeKey(pCur, offset, amt, pMem->z);
- }else{
- rc = sqlite3BtreeData(pCur, offset, amt, pMem->z);
- }
- if( rc==SQLITE_OK ){
- pMem->z[amt] = 0;
- pMem->z[amt+1] = 0;
- pMem->flags = MEM_Blob|MEM_Term;
- pMem->n = (int)amt;
- }else{
- sqlite3VdbeMemRelease(pMem);
- }
- }
+ rc = vdbeMemFromBtreeResize(pCur, offset, amt, key, pMem);
}
return rc;
@@ -64226,7 +69388,7 @@ static sqlite3_value *valueNew(sqlite3 *db, struct ValueNewStat4Ctx *p){
** to be a scalar SQL function. If
**
** * all function arguments are SQL literals,
-** * the SQLITE_FUNC_CONSTANT function flag is set, and
+** * one of the SQLITE_FUNC_CONSTANT or _SLOCHNG function flags is set, and
** * the SQLITE_FUNC_NEEDCOLL function flag is not set,
**
** then this routine attempts to invoke the SQL function. Assuming no
@@ -64256,7 +69418,6 @@ static int valueFromFunction(
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 */
@@ -64264,10 +69425,9 @@ static int valueFromFunction(
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);
+ pFunc = sqlite3FindFunction(db, p->u.zToken, nVal, enc, 0);
assert( pFunc );
- if( (pFunc->funcFlags & SQLITE_FUNC_CONSTANT)==0
+ if( (pFunc->funcFlags & (SQLITE_FUNC_CONSTANT|SQLITE_FUNC_SLOCHNG))==0
|| (pFunc->funcFlags & SQLITE_FUNC_NEEDCOLL)
){
return SQLITE_OK;
@@ -64276,7 +69436,7 @@ static int valueFromFunction(
if( pList ){
apVal = (sqlite3_value**)sqlite3DbMallocZero(db, sizeof(apVal[0]) * nVal);
if( apVal==0 ){
- rc = SQLITE_NOMEM;
+ rc = SQLITE_NOMEM_BKPT;
goto value_from_function_out;
}
for(i=0; i<nVal; i++){
@@ -64287,7 +69447,7 @@ static int valueFromFunction(
pVal = valueNew(db, pCtx);
if( pVal==0 ){
- rc = SQLITE_NOMEM;
+ rc = SQLITE_NOMEM_BKPT;
goto value_from_function_out;
}
@@ -64295,7 +69455,7 @@ static int valueFromFunction(
memset(&ctx, 0, sizeof(ctx));
ctx.pOut = pVal;
ctx.pFunc = pFunc;
- pFunc->xFunc(&ctx, nVal, apVal);
+ pFunc->xSFunc(&ctx, nVal, apVal);
if( ctx.isError ){
rc = ctx.isError;
sqlite3ErrorMsg(pCtx->pParse, "%s", sqlite3_value_text(pVal));
@@ -64357,7 +69517,7 @@ static int valueFromExpr(
*ppVal = 0;
return SQLITE_OK;
}
- while( (op = pExpr->op)==TK_UPLUS ) pExpr = pExpr->pLeft;
+ while( (op = pExpr->op)==TK_UPLUS || op==TK_SPAN ) pExpr = pExpr->pLeft;
if( NEVER(op==TK_REGISTER) ) op = pExpr->op2;
/* Compressed expressions only appear when parsing the DEFAULT clause
@@ -64398,7 +69558,7 @@ static int valueFromExpr(
if( zVal==0 ) goto no_mem;
sqlite3ValueSetStr(pVal, -1, zVal, SQLITE_UTF8, SQLITE_DYNAMIC);
}
- if( (op==TK_INTEGER || op==TK_FLOAT ) && affinity==SQLITE_AFF_NONE ){
+ if( (op==TK_INTEGER || op==TK_FLOAT ) && affinity==SQLITE_AFF_BLOB ){
sqlite3ValueApplyAffinity(pVal, SQLITE_AFF_NUMERIC, SQLITE_UTF8);
}else{
sqlite3ValueApplyAffinity(pVal, affinity, SQLITE_UTF8);
@@ -64452,7 +69612,7 @@ static int valueFromExpr(
return rc;
no_mem:
- db->mallocFailed = 1;
+ sqlite3OomFault(db);
sqlite3DbFree(db, zVal);
assert( *ppVal==0 );
#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
@@ -64460,7 +69620,7 @@ no_mem:
#else
assert( pCtx==0 ); sqlite3ValueFree(pVal);
#endif
- return SQLITE_NOMEM;
+ return SQLITE_NOMEM_BKPT;
}
/*
@@ -64498,21 +69658,20 @@ static void recordFunc(
sqlite3_value **argv
){
const int file_format = 1;
- int iSerial; /* Serial type */
+ u32 iSerial; /* Serial type */
int nSerial; /* Bytes of space for iSerial as varint */
- int nVal; /* Bytes of space required for argv[0] */
+ u32 nVal; /* Bytes of space required for argv[0] */
int nRet;
sqlite3 *db;
u8 *aRet;
UNUSED_PARAMETER( argc );
- iSerial = sqlite3VdbeSerialType(argv[0], file_format);
+ iSerial = sqlite3VdbeSerialType(argv[0], file_format, &nVal);
nSerial = sqlite3VarintLen(iSerial);
- nVal = sqlite3VdbeSerialTypeLen(iSerial);
db = sqlite3_context_db_handle(context);
nRet = 1 + nSerial + nVal;
- aRet = sqlite3DbMallocRaw(db, nRet);
+ aRet = sqlite3DbMallocRawNN(db, nRet);
if( aRet==0 ){
sqlite3_result_error_nomem(context);
}else{
@@ -64528,15 +69687,10 @@ static void recordFunc(
** Register built-in functions used to help read ANALYZE data.
*/
SQLITE_PRIVATE void sqlite3AnalyzeFunctions(void){
- static SQLITE_WSD FuncDef aAnalyzeTableFuncs[] = {
+ static FuncDef aAnalyzeTableFuncs[] = {
FUNCTION(sqlite_record, 1, 0, 0, recordFunc),
};
- int i;
- FuncDefHash *pHash = &GLOBAL(FuncDefHash, sqlite3GlobalFunctions);
- FuncDef *aFunc = (FuncDef*)&GLOBAL(FuncDef, aAnalyzeTableFuncs);
- for(i=0; i<ArraySize(aAnalyzeTableFuncs); i++){
- sqlite3FuncDefInsert(pHash, &aFunc[i]);
- }
+ sqlite3InsertBuiltinFuncs(aAnalyzeTableFuncs, ArraySize(aAnalyzeTableFuncs));
}
/*
@@ -64715,7 +69869,7 @@ SQLITE_PRIVATE int sqlite3Stat4Column(
if( iField>nRec ) return SQLITE_CORRUPT_BKPT;
if( pMem==0 ){
pMem = *ppVal = sqlite3ValueNew(db);
- if( pMem==0 ) return SQLITE_NOMEM;
+ if( pMem==0 ) return SQLITE_NOMEM_BKPT;
}
sqlite3VdbeSerialGet(&a[iField-szField], t, pMem);
pMem->enc = ENC(db);
@@ -64765,19 +69919,28 @@ SQLITE_PRIVATE void sqlite3ValueFree(sqlite3_value *v){
}
/*
-** Return the number of bytes in the sqlite3_value object assuming
-** that it uses the encoding "enc"
+** The sqlite3ValueBytes() routine returns the number of bytes in the
+** sqlite3_value object assuming that it uses the encoding "enc".
+** The valueBytes() routine is a helper function.
*/
+static SQLITE_NOINLINE int valueBytes(sqlite3_value *pVal, u8 enc){
+ return valueToText(pVal, enc)!=0 ? pVal->n : 0;
+}
SQLITE_PRIVATE int sqlite3ValueBytes(sqlite3_value *pVal, u8 enc){
Mem *p = (Mem*)pVal;
- if( (p->flags & MEM_Blob)!=0 || sqlite3ValueText(pVal, enc) ){
+ assert( (p->flags & MEM_Null)==0 || (p->flags & (MEM_Str|MEM_Blob))==0 );
+ if( (p->flags & MEM_Str)!=0 && pVal->enc==enc ){
+ return p->n;
+ }
+ if( (p->flags & MEM_Blob)!=0 ){
if( p->flags & MEM_Zero ){
return p->n + p->u.nZero;
}else{
return p->n;
}
}
- return 0;
+ if( p->flags & MEM_Null ) return 0;
+ return valueBytes(pVal, enc);
}
/************** End of vdbemem.c *********************************************/
@@ -64796,6 +69959,8 @@ SQLITE_PRIVATE int sqlite3ValueBytes(sqlite3_value *pVal, u8 enc){
** This file contains code used for creating, destroying, and populating
** a VDBE (or an "sqlite3_stmt" as it is known to the outside world.)
*/
+/* #include "sqliteInt.h" */
+/* #include "vdbeInt.h" */
/*
** Create a new virtual database engine.
@@ -64817,10 +69982,22 @@ SQLITE_PRIVATE Vdbe *sqlite3VdbeCreate(Parse *pParse){
assert( pParse->aLabel==0 );
assert( pParse->nLabel==0 );
assert( pParse->nOpAlloc==0 );
+ assert( pParse->szOpAlloc==0 );
return p;
}
/*
+** Change the error string stored in Vdbe.zErrMsg
+*/
+SQLITE_PRIVATE void sqlite3VdbeError(Vdbe *p, const char *zFormat, ...){
+ va_list ap;
+ sqlite3DbFree(p->db, p->zErrMsg);
+ va_start(ap, zFormat);
+ p->zErrMsg = sqlite3VMPrintf(p->db, zFormat, ap);
+ va_end(ap);
+}
+
+/*
** Remember the SQL string for a prepared statement.
*/
SQLITE_PRIVATE void sqlite3VdbeSetSql(Vdbe *p, const char *z, int n, int isPrepareV2){
@@ -64839,7 +70016,7 @@ SQLITE_PRIVATE void sqlite3VdbeSetSql(Vdbe *p, const char *z, int n, int isPrepa
*/
SQLITE_API const char *SQLITE_STDCALL sqlite3_sql(sqlite3_stmt *pStmt){
Vdbe *p = (Vdbe *)pStmt;
- return (p && p->isPrepareV2) ? p->zSql : 0;
+ return p ? p->zSql : 0;
}
/*
@@ -64848,6 +70025,7 @@ SQLITE_API const char *SQLITE_STDCALL sqlite3_sql(sqlite3_stmt *pStmt){
SQLITE_PRIVATE void sqlite3VdbeSwap(Vdbe *pA, Vdbe *pB){
Vdbe tmp, *pTmp;
char *zTmp;
+ assert( pA->db==pB->db );
tmp = *pA;
*pA = *pB;
*pB = tmp;
@@ -64895,10 +70073,11 @@ static int growOpArray(Vdbe *v, int nOp){
assert( nNew>=(p->nOpAlloc+nOp) );
pNew = sqlite3DbRealloc(p->db, v->aOp, nNew*sizeof(Op));
if( pNew ){
- p->nOpAlloc = sqlite3DbMallocSize(p->db, pNew)/sizeof(Op);
+ p->szOpAlloc = sqlite3DbMallocSize(p->db, pNew);
+ p->nOpAlloc = p->szOpAlloc/sizeof(Op);
v->aOp = pNew;
}
- return (pNew ? SQLITE_OK : SQLITE_NOMEM);
+ return (pNew ? SQLITE_OK : SQLITE_NOMEM_BKPT);
}
#ifdef SQLITE_DEBUG
@@ -64928,17 +70107,21 @@ static void test_addop_breakpoint(void){
** the sqlite3VdbeChangeP4() function to change the value of the P4
** operand.
*/
+static SQLITE_NOINLINE int growOp3(Vdbe *p, int op, int p1, int p2, int p3){
+ assert( p->pParse->nOpAlloc<=p->nOp );
+ if( growOpArray(p, 1) ) return 1;
+ assert( p->pParse->nOpAlloc>p->nOp );
+ return sqlite3VdbeAddOp3(p, op, p1, p2, p3);
+}
SQLITE_PRIVATE int sqlite3VdbeAddOp3(Vdbe *p, int op, int p1, int p2, int p3){
int i;
VdbeOp *pOp;
i = p->nOp;
assert( p->magic==VDBE_MAGIC_INIT );
- assert( op>0 && op<0xff );
+ assert( op>=0 && op<0xff );
if( p->pParse->nOpAlloc<=i ){
- if( growOpArray(p, 1) ){
- return 1;
- }
+ return growOp3(p, op, p1, p2, p3);
}
p->nOp++;
pOp = &p->aOp[i];
@@ -64986,6 +70169,43 @@ SQLITE_PRIVATE int sqlite3VdbeAddOp2(Vdbe *p, int op, int p1, int p2){
return sqlite3VdbeAddOp3(p, op, p1, p2, 0);
}
+/* Generate code for an unconditional jump to instruction iDest
+*/
+SQLITE_PRIVATE int sqlite3VdbeGoto(Vdbe *p, int iDest){
+ return sqlite3VdbeAddOp3(p, OP_Goto, 0, iDest, 0);
+}
+
+/* Generate code to cause the string zStr to be loaded into
+** register iDest
+*/
+SQLITE_PRIVATE int sqlite3VdbeLoadString(Vdbe *p, int iDest, const char *zStr){
+ return sqlite3VdbeAddOp4(p, OP_String8, 0, iDest, 0, zStr, 0);
+}
+
+/*
+** Generate code that initializes multiple registers to string or integer
+** constants. The registers begin with iDest and increase consecutively.
+** One register is initialized for each characgter in zTypes[]. For each
+** "s" character in zTypes[], the register is a string if the argument is
+** not NULL, or OP_Null if the value is a null pointer. For each "i" character
+** in zTypes[], the register is initialized to an integer.
+*/
+SQLITE_PRIVATE void sqlite3VdbeMultiLoad(Vdbe *p, int iDest, const char *zTypes, ...){
+ va_list ap;
+ int i;
+ char c;
+ va_start(ap, zTypes);
+ for(i=0; (c = zTypes[i])!=0; i++){
+ if( c=='s' ){
+ const char *z = va_arg(ap, const char*);
+ sqlite3VdbeAddOp4(p, z==0 ? OP_Null : OP_String8, 0, iDest++, 0, z, 0);
+ }else{
+ assert( c=='i' );
+ sqlite3VdbeAddOp2(p, OP_Integer, va_arg(ap, int), iDest++);
+ }
+ }
+ va_end(ap);
+}
/*
** Add an opcode that includes the p4 value as a pointer.
@@ -65005,6 +70225,24 @@ SQLITE_PRIVATE int sqlite3VdbeAddOp4(
}
/*
+** Add an opcode that includes the p4 value with a P4_INT64 or
+** P4_REAL type.
+*/
+SQLITE_PRIVATE int sqlite3VdbeAddOp4Dup8(
+ Vdbe *p, /* Add the opcode to this VM */
+ int op, /* The new opcode */
+ int p1, /* The P1 operand */
+ int p2, /* The P2 operand */
+ int p3, /* The P3 operand */
+ const u8 *zP4, /* The P4 operand */
+ int p4type /* P4 operand type */
+){
+ char *p4copy = sqlite3DbMallocRawNN(sqlite3VdbeDb(p), 8);
+ if( p4copy ) memcpy(p4copy, zP4, 8);
+ return sqlite3VdbeAddOp4(p, op, p1, p2, p3, p4copy, p4type);
+}
+
+/*
** Add an OP_ParseSchema opcode. This routine is broken out from
** sqlite3VdbeAddOp4() since it needs to also needs to mark all btrees
** as having been used.
@@ -65014,8 +70252,7 @@ SQLITE_PRIVATE int sqlite3VdbeAddOp4(
*/
SQLITE_PRIVATE void sqlite3VdbeAddParseSchemaOp(Vdbe *p, int iDb, char *zWhere){
int j;
- int addr = sqlite3VdbeAddOp3(p, OP_ParseSchema, iDb, 0, 0);
- sqlite3VdbeChangeP4(p, addr, zWhere, P4_DYNAMIC);
+ sqlite3VdbeAddOp4(p, OP_ParseSchema, iDb, 0, 0, zWhere, P4_DYNAMIC);
for(j=0; j<p->db->nDb; j++) sqlite3VdbeUsesBtree(p, j);
}
@@ -65035,6 +70272,21 @@ SQLITE_PRIVATE int sqlite3VdbeAddOp4Int(
return addr;
}
+/* Insert the end of a co-routine
+*/
+SQLITE_PRIVATE void sqlite3VdbeEndCoroutine(Vdbe *v, int regYield){
+ sqlite3VdbeAddOp1(v, OP_EndCoroutine, regYield);
+
+ /* Clear the temporary register cache, thereby ensuring that each
+ ** co-routine has its own independent set of registers, because co-routines
+ ** might expect their registers to be preserved across an OP_Yield, and
+ ** that could cause problems if two or more co-routines are using the same
+ ** temporary register.
+ */
+ v->pParse->nTempReg = 0;
+ v->pParse->nRangeReg = 0;
+}
+
/*
** Create a new symbolic label for an instruction that has yet to be
** coded. The symbolic label is really just a negative number. The
@@ -65060,7 +70312,7 @@ SQLITE_PRIVATE int sqlite3VdbeMakeLabel(Vdbe *v){
if( p->aLabel ){
p->aLabel[i] = -1;
}
- return -1-i;
+ return ADDR(i);
}
/*
@@ -65070,10 +70322,11 @@ SQLITE_PRIVATE int sqlite3VdbeMakeLabel(Vdbe *v){
*/
SQLITE_PRIVATE void sqlite3VdbeResolveLabel(Vdbe *v, int x){
Parse *p = v->pParse;
- int j = -1-x;
+ int j = ADDR(x);
assert( v->magic==VDBE_MAGIC_INIT );
assert( j<p->nLabel );
- if( ALWAYS(j>=0) && p->aLabel ){
+ assert( j>=0 );
+ if( p->aLabel ){
p->aLabel[j] = v->nOp;
}
p->iFixedOp = v->nOp - 1;
@@ -65086,6 +70339,13 @@ SQLITE_PRIVATE void sqlite3VdbeRunOnlyOnce(Vdbe *p){
p->runOnlyOnce = 1;
}
+/*
+** Mark the VDBE as one that can only be run multiple times.
+*/
+SQLITE_PRIVATE void sqlite3VdbeReusable(Vdbe *p){
+ p->runOnlyOnce = 0;
+}
+
#ifdef SQLITE_DEBUG /* sqlite3AssertMayAbort() logic */
/*
@@ -65168,6 +70428,7 @@ static Op *opIterNext(VdbeOpIter *p){
** * OP_VUpdate
** * OP_VRename
** * OP_FkCounter with P2==0 (immediate foreign key constraint)
+** * OP_CreateTable and OP_InitCoroutine (for CREATE TABLE AS SELECT ...)
**
** Then check that the value of Parse.mayAbort is true if an
** ABORT may be thrown, or false otherwise. Return true if it does
@@ -65179,6 +70440,8 @@ static Op *opIterNext(VdbeOpIter *p){
SQLITE_PRIVATE int sqlite3VdbeAssertMayAbort(Vdbe *v, int mayAbort){
int hasAbort = 0;
int hasFkCounter = 0;
+ int hasCreateTable = 0;
+ int hasInitCoroutine = 0;
Op *pOp;
VdbeOpIter sIter;
memset(&sIter, 0, sizeof(sIter));
@@ -65193,6 +70456,8 @@ SQLITE_PRIVATE int sqlite3VdbeAssertMayAbort(Vdbe *v, int mayAbort){
hasAbort = 1;
break;
}
+ if( opcode==OP_CreateTable ) hasCreateTable = 1;
+ if( opcode==OP_InitCoroutine ) hasInitCoroutine = 1;
#ifndef SQLITE_OMIT_FOREIGN_KEY
if( opcode==OP_FkCounter && pOp->p1==0 && pOp->p2==1 ){
hasFkCounter = 1;
@@ -65206,94 +70471,105 @@ SQLITE_PRIVATE int sqlite3VdbeAssertMayAbort(Vdbe *v, int mayAbort){
** through all opcodes and hasAbort may be set incorrectly. Return
** true for this case to prevent the assert() in the callers frame
** from failing. */
- return ( v->db->mallocFailed || hasAbort==mayAbort || hasFkCounter );
+ return ( v->db->mallocFailed || hasAbort==mayAbort || hasFkCounter
+ || (hasCreateTable && hasInitCoroutine) );
}
#endif /* SQLITE_DEBUG - the sqlite3AssertMayAbort() function */
/*
-** Loop through the program looking for P2 values that are negative
-** on jump instructions. Each such value is a label. Resolve the
-** label by setting the P2 value to its correct non-zero value.
+** This routine is called after all opcodes have been inserted. It loops
+** through all the opcodes and fixes up some details.
+**
+** (1) For each jump instruction with a negative P2 value (a label)
+** resolve the P2 value to an actual address.
**
-** This routine is called once after all opcodes have been inserted.
+** (2) Compute the maximum number of arguments used by any SQL function
+** and store that value in *pMaxFuncArgs.
**
-** Variable *pMaxFuncArgs is set to the maximum value of any P2 argument
-** to an OP_Function, OP_AggStep or OP_VFilter opcode. This is used by
-** sqlite3VdbeMakeReady() to size the Vdbe.apArg[] array.
+** (3) Update the Vdbe.readOnly and Vdbe.bIsReader flags to accurately
+** indicate what the prepared statement actually does.
**
-** The Op.opflags field is set on all opcodes.
+** (4) Initialize the p4.xAdvance pointer on opcodes that use it.
+**
+** (5) Reclaim the memory allocated for storing labels.
+**
+** This routine will only function correctly if the mkopcodeh.tcl generator
+** script numbers the opcodes correctly. Changes to this routine must be
+** coordinated with changes to mkopcodeh.tcl.
*/
static void resolveP2Values(Vdbe *p, int *pMaxFuncArgs){
- int i;
int nMaxArgs = *pMaxFuncArgs;
Op *pOp;
Parse *pParse = p->pParse;
int *aLabel = pParse->aLabel;
p->readOnly = 1;
p->bIsReader = 0;
- for(pOp=p->aOp, i=p->nOp-1; i>=0; i--, pOp++){
- u8 opcode = pOp->opcode;
-
- /* NOTE: Be sure to update mkopcodeh.awk when adding or removing
- ** cases from this switch! */
- switch( opcode ){
- case OP_Function:
- case OP_AggStep: {
- if( pOp->p5>nMaxArgs ) nMaxArgs = pOp->p5;
- break;
- }
- case OP_Transaction: {
- if( pOp->p2!=0 ) p->readOnly = 0;
- /* fall thru */
- }
- case OP_AutoCommit:
- case OP_Savepoint: {
- p->bIsReader = 1;
- break;
- }
+ pOp = &p->aOp[p->nOp-1];
+ while(1){
+
+ /* Only JUMP opcodes and the short list of special opcodes in the switch
+ ** below need to be considered. The mkopcodeh.tcl generator script groups
+ ** all these opcodes together near the front of the opcode list. Skip
+ ** any opcode that does not need processing by virtual of the fact that
+ ** it is larger than SQLITE_MX_JUMP_OPCODE, as a performance optimization.
+ */
+ if( pOp->opcode<=SQLITE_MX_JUMP_OPCODE ){
+ /* NOTE: Be sure to update mkopcodeh.tcl when adding or removing
+ ** cases from this switch! */
+ switch( pOp->opcode ){
+ case OP_Transaction: {
+ if( pOp->p2!=0 ) p->readOnly = 0;
+ /* fall thru */
+ }
+ case OP_AutoCommit:
+ case OP_Savepoint: {
+ p->bIsReader = 1;
+ break;
+ }
#ifndef SQLITE_OMIT_WAL
- case OP_Checkpoint:
+ case OP_Checkpoint:
#endif
- case OP_Vacuum:
- case OP_JournalMode: {
- p->readOnly = 0;
- p->bIsReader = 1;
- break;
- }
+ case OP_Vacuum:
+ case OP_JournalMode: {
+ p->readOnly = 0;
+ p->bIsReader = 1;
+ break;
+ }
#ifndef SQLITE_OMIT_VIRTUALTABLE
- case OP_VUpdate: {
- if( pOp->p2>nMaxArgs ) nMaxArgs = pOp->p2;
- break;
- }
- case OP_VFilter: {
- int n;
- assert( p->nOp - i >= 3 );
- assert( pOp[-1].opcode==OP_Integer );
- n = pOp[-1].p1;
- if( n>nMaxArgs ) nMaxArgs = n;
- break;
- }
+ case OP_VUpdate: {
+ if( pOp->p2>nMaxArgs ) nMaxArgs = pOp->p2;
+ break;
+ }
+ case OP_VFilter: {
+ int n;
+ assert( (pOp - p->aOp) >= 3 );
+ assert( pOp[-1].opcode==OP_Integer );
+ n = pOp[-1].p1;
+ if( n>nMaxArgs ) nMaxArgs = n;
+ break;
+ }
#endif
- case OP_Next:
- case OP_NextIfOpen:
- case OP_SorterNext: {
- pOp->p4.xAdvance = sqlite3BtreeNext;
- pOp->p4type = P4_ADVANCE;
- break;
+ case OP_Next:
+ case OP_NextIfOpen:
+ case OP_SorterNext: {
+ pOp->p4.xAdvance = sqlite3BtreeNext;
+ pOp->p4type = P4_ADVANCE;
+ break;
+ }
+ case OP_Prev:
+ case OP_PrevIfOpen: {
+ pOp->p4.xAdvance = sqlite3BtreePrevious;
+ pOp->p4type = P4_ADVANCE;
+ break;
+ }
}
- case OP_Prev:
- case OP_PrevIfOpen: {
- pOp->p4.xAdvance = sqlite3BtreePrevious;
- pOp->p4type = P4_ADVANCE;
- break;
+ if( (sqlite3OpcodeProperty[pOp->opcode] & OPFLG_JUMP)!=0 && pOp->p2<0 ){
+ assert( ADDR(pOp->p2)<pParse->nLabel );
+ pOp->p2 = aLabel[ADDR(pOp->p2)];
}
}
-
- pOp->opflags = sqlite3OpcodeProperty[opcode];
- if( (pOp->opflags & OPFLG_JUMP)!=0 && pOp->p2<0 ){
- assert( -1-pOp->p2<pParse->nLabel );
- pOp->p2 = aLabel[-1-pOp->p2];
- }
+ if( pOp==p->aOp ) break;
+ pOp--;
}
sqlite3DbFree(p->db, pParse->aLabel);
pParse->aLabel = 0;
@@ -65311,6 +70587,20 @@ SQLITE_PRIVATE int sqlite3VdbeCurrentAddr(Vdbe *p){
}
/*
+** Verify that at least N opcode slots are available in p without
+** having to malloc for more space (except when compiled using
+** SQLITE_TEST_REALLOC_STRESS). This interface is used during testing
+** to verify that certain calls to sqlite3VdbeAddOpList() can never
+** fail due to a OOM fault and hence that the return value from
+** sqlite3VdbeAddOpList() will always be non-NULL.
+*/
+#if defined(SQLITE_DEBUG) && !defined(SQLITE_TEST_REALLOC_STRESS)
+SQLITE_PRIVATE void sqlite3VdbeVerifyNoMallocRequired(Vdbe *p, int N){
+ assert( p->nOp + N <= p->pParse->nOpAlloc );
+}
+#endif
+
+/*
** This function returns a pointer to the array of opcodes associated with
** the Vdbe passed as the first argument. It is the callers responsibility
** to arrange for the returned array to be eventually freed using the
@@ -65335,51 +70625,54 @@ SQLITE_PRIVATE VdbeOp *sqlite3VdbeTakeOpArray(Vdbe *p, int *pnOp, int *pnMaxArg)
}
/*
-** Add a whole list of operations to the operation stack. Return the
-** address of the first operation added.
+** Add a whole list of operations to the operation stack. Return a
+** pointer to the first operation inserted.
+**
+** Non-zero P2 arguments to jump instructions are automatically adjusted
+** so that the jump target is relative to the first operation inserted.
*/
-SQLITE_PRIVATE int sqlite3VdbeAddOpList(Vdbe *p, int nOp, VdbeOpList const *aOp, int iLineno){
- int addr;
+SQLITE_PRIVATE VdbeOp *sqlite3VdbeAddOpList(
+ Vdbe *p, /* Add opcodes to the prepared statement */
+ int nOp, /* Number of opcodes to add */
+ VdbeOpList const *aOp, /* The opcodes to be added */
+ int iLineno /* Source-file line number of first opcode */
+){
+ int i;
+ VdbeOp *pOut, *pFirst;
+ assert( nOp>0 );
assert( p->magic==VDBE_MAGIC_INIT );
if( p->nOp + nOp > p->pParse->nOpAlloc && growOpArray(p, nOp) ){
return 0;
}
- addr = p->nOp;
- if( ALWAYS(nOp>0) ){
- int i;
- VdbeOpList const *pIn = aOp;
- for(i=0; i<nOp; i++, pIn++){
- int p2 = pIn->p2;
- VdbeOp *pOut = &p->aOp[i+addr];
- pOut->opcode = pIn->opcode;
- pOut->p1 = pIn->p1;
- if( p2<0 ){
- assert( sqlite3OpcodeProperty[pOut->opcode] & OPFLG_JUMP );
- pOut->p2 = addr + ADDR(p2);
- }else{
- pOut->p2 = p2;
- }
- pOut->p3 = pIn->p3;
- pOut->p4type = P4_NOTUSED;
- pOut->p4.p = 0;
- pOut->p5 = 0;
+ pFirst = pOut = &p->aOp[p->nOp];
+ for(i=0; i<nOp; i++, aOp++, pOut++){
+ pOut->opcode = aOp->opcode;
+ pOut->p1 = aOp->p1;
+ pOut->p2 = aOp->p2;
+ assert( aOp->p2>=0 );
+ if( (sqlite3OpcodeProperty[aOp->opcode] & OPFLG_JUMP)!=0 && aOp->p2>0 ){
+ pOut->p2 += p->nOp;
+ }
+ pOut->p3 = aOp->p3;
+ pOut->p4type = P4_NOTUSED;
+ pOut->p4.p = 0;
+ pOut->p5 = 0;
#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS
- pOut->zComment = 0;
+ pOut->zComment = 0;
#endif
#ifdef SQLITE_VDBE_COVERAGE
- pOut->iSrcLine = iLineno+i;
+ pOut->iSrcLine = iLineno+i;
#else
- (void)iLineno;
+ (void)iLineno;
#endif
#ifdef SQLITE_DEBUG
- if( p->db->flags & SQLITE_VdbeAddopTrace ){
- sqlite3VdbePrintOp(0, i+addr, &p->aOp[i+addr]);
- }
-#endif
+ if( p->db->flags & SQLITE_VdbeAddopTrace ){
+ sqlite3VdbePrintOp(0, i+p->nOp, &p->aOp[i+p->nOp]);
}
- p->nOp += nOp;
+#endif
}
- return addr;
+ p->nOp += nOp;
+ return pFirst;
}
#if defined(SQLITE_ENABLE_STMT_SCANSTATUS)
@@ -65411,49 +70704,23 @@ SQLITE_PRIVATE void sqlite3VdbeScanStatus(
/*
-** Change the value of the P1 operand for a specific instruction.
-** This routine is useful when a large program is loaded from a
-** static array using sqlite3VdbeAddOpList but we want to make a
-** few minor changes to the program.
+** Change the value of the opcode, or P1, P2, P3, or P5 operands
+** for a specific instruction.
*/
+SQLITE_PRIVATE void sqlite3VdbeChangeOpcode(Vdbe *p, u32 addr, u8 iNewOpcode){
+ sqlite3VdbeGetOp(p,addr)->opcode = iNewOpcode;
+}
SQLITE_PRIVATE void sqlite3VdbeChangeP1(Vdbe *p, u32 addr, int val){
- assert( p!=0 );
- if( ((u32)p->nOp)>addr ){
- p->aOp[addr].p1 = val;
- }
+ sqlite3VdbeGetOp(p,addr)->p1 = val;
}
-
-/*
-** Change the value of the P2 operand for a specific instruction.
-** This routine is useful for setting a jump destination.
-*/
SQLITE_PRIVATE void sqlite3VdbeChangeP2(Vdbe *p, u32 addr, int val){
- assert( p!=0 );
- if( ((u32)p->nOp)>addr ){
- p->aOp[addr].p2 = val;
- }
+ sqlite3VdbeGetOp(p,addr)->p2 = val;
}
-
-/*
-** Change the value of the P3 operand for a specific instruction.
-*/
SQLITE_PRIVATE void sqlite3VdbeChangeP3(Vdbe *p, u32 addr, int val){
- assert( p!=0 );
- if( ((u32)p->nOp)>addr ){
- p->aOp[addr].p3 = val;
- }
+ sqlite3VdbeGetOp(p,addr)->p3 = val;
}
-
-/*
-** Change the value of the P5 operand for the most recently
-** added operation.
-*/
-SQLITE_PRIVATE void sqlite3VdbeChangeP5(Vdbe *p, u8 val){
- assert( p!=0 );
- if( p->aOp ){
- assert( p->nOp>0 );
- p->aOp[p->nOp-1].p5 = val;
- }
+SQLITE_PRIVATE void sqlite3VdbeChangeP5(Vdbe *p, u8 p5){
+ if( !p->db->mallocFailed ) p->aOp[p->nOp-1].p5 = p5;
}
/*
@@ -65461,8 +70728,8 @@ SQLITE_PRIVATE void sqlite3VdbeChangeP5(Vdbe *p, u8 val){
** the address of the next instruction to be coded.
*/
SQLITE_PRIVATE void sqlite3VdbeJumpHere(Vdbe *p, int addr){
- sqlite3VdbeChangeP2(p, addr, p->nOp);
p->pParse->iFixedOp = p->nOp - 1;
+ sqlite3VdbeChangeP2(p, addr, p->nOp);
}
@@ -65482,42 +70749,50 @@ static void vdbeFreeOpArray(sqlite3 *, Op *, int);
** Delete a P4 value if necessary.
*/
static void freeP4(sqlite3 *db, int p4type, void *p4){
- if( p4 ){
- assert( db );
- switch( p4type ){
- case P4_REAL:
- case P4_INT64:
- case P4_DYNAMIC:
- case P4_INTARRAY: {
- sqlite3DbFree(db, p4);
- break;
- }
- case P4_KEYINFO: {
- if( db->pnBytesFreed==0 ) sqlite3KeyInfoUnref((KeyInfo*)p4);
- break;
- }
- case P4_MPRINTF: {
- if( db->pnBytesFreed==0 ) sqlite3_free(p4);
- break;
- }
- case P4_FUNCDEF: {
- freeEphemeralFunction(db, (FuncDef*)p4);
- break;
- }
- case P4_MEM: {
- if( db->pnBytesFreed==0 ){
- sqlite3ValueFree((sqlite3_value*)p4);
- }else{
- Mem *p = (Mem*)p4;
- if( p->szMalloc ) sqlite3DbFree(db, p->zMalloc);
- sqlite3DbFree(db, p);
- }
- break;
- }
- case P4_VTAB : {
- if( db->pnBytesFreed==0 ) sqlite3VtabUnlock((VTable *)p4);
- break;
+ assert( db );
+ switch( p4type ){
+ case P4_FUNCCTX: {
+ freeEphemeralFunction(db, ((sqlite3_context*)p4)->pFunc);
+ /* Fall through into the next case */
+ }
+ case P4_REAL:
+ case P4_INT64:
+ case P4_DYNAMIC:
+ case P4_INTARRAY: {
+ sqlite3DbFree(db, p4);
+ break;
+ }
+ case P4_KEYINFO: {
+ if( db->pnBytesFreed==0 ) sqlite3KeyInfoUnref((KeyInfo*)p4);
+ break;
+ }
+#ifdef SQLITE_ENABLE_CURSOR_HINTS
+ case P4_EXPR: {
+ sqlite3ExprDelete(db, (Expr*)p4);
+ break;
+ }
+#endif
+ case P4_MPRINTF: {
+ if( db->pnBytesFreed==0 ) sqlite3_free(p4);
+ break;
+ }
+ case P4_FUNCDEF: {
+ freeEphemeralFunction(db, (FuncDef*)p4);
+ break;
+ }
+ case P4_MEM: {
+ if( db->pnBytesFreed==0 ){
+ sqlite3ValueFree((sqlite3_value*)p4);
+ }else{
+ Mem *p = (Mem*)p4;
+ if( p->szMalloc ) sqlite3DbFree(db, p->zMalloc);
+ sqlite3DbFree(db, p);
}
+ break;
+ }
+ case P4_VTAB : {
+ if( db->pnBytesFreed==0 ) sqlite3VtabUnlock((VTable *)p4);
+ break;
}
}
}
@@ -65531,7 +70806,7 @@ static void vdbeFreeOpArray(sqlite3 *db, Op *aOp, int nOp){
if( aOp ){
Op *pOp;
for(pOp=aOp; pOp<&aOp[nOp]; pOp++){
- freeP4(db, pOp->p4type, pOp->p4.p);
+ if( pOp->p4type ) freeP4(db, pOp->p4type, pOp->p4.p);
#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS
sqlite3DbFree(db, pOp->zComment);
#endif
@@ -65553,15 +70828,16 @@ SQLITE_PRIVATE void sqlite3VdbeLinkSubProgram(Vdbe *pVdbe, SubProgram *p){
/*
** Change the opcode at addr into OP_Noop
*/
-SQLITE_PRIVATE void sqlite3VdbeChangeToNoop(Vdbe *p, int addr){
- if( addr<p->nOp ){
- VdbeOp *pOp = &p->aOp[addr];
- sqlite3 *db = p->db;
- freeP4(db, pOp->p4type, pOp->p4.p);
- memset(pOp, 0, sizeof(pOp[0]));
- pOp->opcode = OP_Noop;
- if( addr==p->nOp-1 ) p->nOp--;
- }
+SQLITE_PRIVATE int sqlite3VdbeChangeToNoop(Vdbe *p, int addr){
+ VdbeOp *pOp;
+ if( p->db->mallocFailed ) return 0;
+ assert( addr>=0 && addr<p->nOp );
+ pOp = &p->aOp[addr];
+ freeP4(p->db, pOp->p4type, pOp->p4.p);
+ pOp->p4type = P4_NOTUSED;
+ pOp->p4.z = 0;
+ pOp->opcode = OP_Noop;
+ return 1;
}
/*
@@ -65570,8 +70846,7 @@ SQLITE_PRIVATE void sqlite3VdbeChangeToNoop(Vdbe *p, int addr){
*/
SQLITE_PRIVATE int sqlite3VdbeDeletePriorOpcode(Vdbe *p, u8 op){
if( (p->nOp-1)>(p->pParse->iFixedOp) && p->aOp[p->nOp-1].opcode==op ){
- sqlite3VdbeChangeToNoop(p, p->nOp-1);
- return 1;
+ return sqlite3VdbeChangeToNoop(p, p->nOp-1);
}else{
return 0;
}
@@ -65594,16 +70869,34 @@ SQLITE_PRIVATE int sqlite3VdbeDeletePriorOpcode(Vdbe *p, u8 op){
**
** If addr<0 then change P4 on the most recently inserted instruction.
*/
+static void SQLITE_NOINLINE vdbeChangeP4Full(
+ Vdbe *p,
+ Op *pOp,
+ const char *zP4,
+ int n
+){
+ if( pOp->p4type ){
+ freeP4(p->db, pOp->p4type, pOp->p4.p);
+ pOp->p4type = 0;
+ pOp->p4.p = 0;
+ }
+ if( n<0 ){
+ sqlite3VdbeChangeP4(p, (int)(pOp - p->aOp), zP4, n);
+ }else{
+ if( n==0 ) n = sqlite3Strlen30(zP4);
+ pOp->p4.z = sqlite3DbStrNDup(p->db, zP4, n);
+ pOp->p4type = P4_DYNAMIC;
+ }
+}
SQLITE_PRIVATE void sqlite3VdbeChangeP4(Vdbe *p, int addr, const char *zP4, int n){
Op *pOp;
sqlite3 *db;
assert( p!=0 );
db = p->db;
assert( p->magic==VDBE_MAGIC_INIT );
- if( p->aOp==0 || db->mallocFailed ){
- if( n!=P4_VTAB ){
- freeP4(db, n, (void*)*(char**)&zP4);
- }
+ assert( p->aOp!=0 || db->mallocFailed );
+ if( db->mallocFailed ){
+ if( n!=P4_VTAB ) freeP4(db, n, (void*)*(char**)&zP4);
return;
}
assert( p->nOp>0 );
@@ -65612,34 +70905,20 @@ SQLITE_PRIVATE void sqlite3VdbeChangeP4(Vdbe *p, int addr, const char *zP4, int
addr = p->nOp - 1;
}
pOp = &p->aOp[addr];
- assert( pOp->p4type==P4_NOTUSED
- || pOp->p4type==P4_INT32
- || pOp->p4type==P4_KEYINFO );
- freeP4(db, pOp->p4type, pOp->p4.p);
- pOp->p4.p = 0;
+ if( n>=0 || pOp->p4type ){
+ vdbeChangeP4Full(p, pOp, zP4, n);
+ return;
+ }
if( n==P4_INT32 ){
/* Note: this cast is safe, because the origin data point was an int
** that was cast to a (const char *). */
pOp->p4.i = SQLITE_PTR_TO_INT(zP4);
pOp->p4type = P4_INT32;
- }else if( zP4==0 ){
- pOp->p4.p = 0;
- pOp->p4type = P4_NOTUSED;
- }else if( n==P4_KEYINFO ){
- pOp->p4.p = (void*)zP4;
- pOp->p4type = P4_KEYINFO;
- }else if( n==P4_VTAB ){
- pOp->p4.p = (void*)zP4;
- pOp->p4type = P4_VTAB;
- sqlite3VtabLock((VTable *)zP4);
- assert( ((VTable *)zP4)->db==p->db );
- }else if( n<0 ){
+ }else if( zP4!=0 ){
+ assert( n<0 );
pOp->p4.p = (void*)zP4;
pOp->p4type = (signed char)n;
- }else{
- if( n==0 ) n = sqlite3Strlen30(zP4);
- pOp->p4.z = sqlite3DbStrNDup(p->db, zP4, n);
- pOp->p4type = P4_DYNAMIC;
+ if( n==P4_VTAB ) sqlite3VtabLock((VTable*)zP4);
}
}
@@ -65818,67 +71097,138 @@ static int displayComment(
}
#endif /* SQLITE_DEBUG */
+#if VDBE_DISPLAY_P4 && defined(SQLITE_ENABLE_CURSOR_HINTS)
+/*
+** Translate the P4.pExpr value for an OP_CursorHint opcode into text
+** that can be displayed in the P4 column of EXPLAIN output.
+*/
+static void displayP4Expr(StrAccum *p, Expr *pExpr){
+ const char *zOp = 0;
+ switch( pExpr->op ){
+ case TK_STRING:
+ sqlite3XPrintf(p, "%Q", pExpr->u.zToken);
+ break;
+ case TK_INTEGER:
+ sqlite3XPrintf(p, "%d", pExpr->u.iValue);
+ break;
+ case TK_NULL:
+ sqlite3XPrintf(p, "NULL");
+ break;
+ case TK_REGISTER: {
+ sqlite3XPrintf(p, "r[%d]", pExpr->iTable);
+ break;
+ }
+ case TK_COLUMN: {
+ if( pExpr->iColumn<0 ){
+ sqlite3XPrintf(p, "rowid");
+ }else{
+ sqlite3XPrintf(p, "c%d", (int)pExpr->iColumn);
+ }
+ break;
+ }
+ case TK_LT: zOp = "LT"; break;
+ case TK_LE: zOp = "LE"; break;
+ case TK_GT: zOp = "GT"; break;
+ case TK_GE: zOp = "GE"; break;
+ case TK_NE: zOp = "NE"; break;
+ case TK_EQ: zOp = "EQ"; break;
+ case TK_IS: zOp = "IS"; break;
+ case TK_ISNOT: zOp = "ISNOT"; break;
+ case TK_AND: zOp = "AND"; break;
+ case TK_OR: zOp = "OR"; break;
+ case TK_PLUS: zOp = "ADD"; break;
+ case TK_STAR: zOp = "MUL"; break;
+ case TK_MINUS: zOp = "SUB"; break;
+ case TK_REM: zOp = "REM"; break;
+ case TK_BITAND: zOp = "BITAND"; break;
+ case TK_BITOR: zOp = "BITOR"; break;
+ case TK_SLASH: zOp = "DIV"; break;
+ case TK_LSHIFT: zOp = "LSHIFT"; break;
+ case TK_RSHIFT: zOp = "RSHIFT"; break;
+ case TK_CONCAT: zOp = "CONCAT"; break;
+ case TK_UMINUS: zOp = "MINUS"; break;
+ case TK_UPLUS: zOp = "PLUS"; break;
+ case TK_BITNOT: zOp = "BITNOT"; break;
+ case TK_NOT: zOp = "NOT"; break;
+ case TK_ISNULL: zOp = "ISNULL"; break;
+ case TK_NOTNULL: zOp = "NOTNULL"; break;
-#if !defined(SQLITE_OMIT_EXPLAIN) || !defined(NDEBUG) \
- || defined(VDBE_PROFILE) || defined(SQLITE_DEBUG)
+ default:
+ sqlite3XPrintf(p, "%s", "expr");
+ break;
+ }
+
+ if( zOp ){
+ sqlite3XPrintf(p, "%s(", zOp);
+ displayP4Expr(p, pExpr->pLeft);
+ if( pExpr->pRight ){
+ sqlite3StrAccumAppend(p, ",", 1);
+ displayP4Expr(p, pExpr->pRight);
+ }
+ sqlite3StrAccumAppend(p, ")", 1);
+ }
+}
+#endif /* VDBE_DISPLAY_P4 && defined(SQLITE_ENABLE_CURSOR_HINTS) */
+
+
+#if VDBE_DISPLAY_P4
/*
** Compute a string that describes the P4 parameter for an opcode.
** Use zTemp for any required temporary buffer space.
*/
static char *displayP4(Op *pOp, char *zTemp, int nTemp){
char *zP4 = zTemp;
+ StrAccum x;
assert( nTemp>=20 );
+ sqlite3StrAccumInit(&x, 0, zTemp, nTemp, 0);
switch( pOp->p4type ){
case P4_KEYINFO: {
- int i, j;
+ int j;
KeyInfo *pKeyInfo = pOp->p4.pKeyInfo;
assert( pKeyInfo->aSortOrder!=0 );
- sqlite3_snprintf(nTemp, zTemp, "k(%d", pKeyInfo->nField);
- i = sqlite3Strlen30(zTemp);
+ sqlite3XPrintf(&x, "k(%d", pKeyInfo->nField);
for(j=0; j<pKeyInfo->nField; j++){
CollSeq *pColl = pKeyInfo->aColl[j];
- const char *zColl = pColl ? pColl->zName : "nil";
- int n = sqlite3Strlen30(zColl);
- if( n==6 && memcmp(zColl,"BINARY",6)==0 ){
- zColl = "B";
- n = 1;
- }
- if( i+n>nTemp-6 ){
- memcpy(&zTemp[i],",...",4);
- break;
- }
- zTemp[i++] = ',';
- if( pKeyInfo->aSortOrder[j] ){
- zTemp[i++] = '-';
- }
- memcpy(&zTemp[i], zColl, n+1);
- i += n;
+ const char *zColl = pColl ? pColl->zName : "";
+ if( strcmp(zColl, "BINARY")==0 ) zColl = "B";
+ sqlite3XPrintf(&x, ",%s%s", pKeyInfo->aSortOrder[j] ? "-" : "", zColl);
}
- zTemp[i++] = ')';
- zTemp[i] = 0;
- assert( i<nTemp );
+ sqlite3StrAccumAppend(&x, ")", 1);
break;
}
+#ifdef SQLITE_ENABLE_CURSOR_HINTS
+ case P4_EXPR: {
+ displayP4Expr(&x, pOp->p4.pExpr);
+ break;
+ }
+#endif
case P4_COLLSEQ: {
CollSeq *pColl = pOp->p4.pColl;
- sqlite3_snprintf(nTemp, zTemp, "(%.20s)", pColl->zName);
+ sqlite3XPrintf(&x, "(%.20s)", pColl->zName);
break;
}
case P4_FUNCDEF: {
FuncDef *pDef = pOp->p4.pFunc;
- sqlite3_snprintf(nTemp, zTemp, "%s(%d)", pDef->zName, pDef->nArg);
+ sqlite3XPrintf(&x, "%s(%d)", pDef->zName, pDef->nArg);
break;
}
+#ifdef SQLITE_DEBUG
+ case P4_FUNCCTX: {
+ FuncDef *pDef = pOp->p4.pCtx->pFunc;
+ sqlite3XPrintf(&x, "%s(%d)", pDef->zName, pDef->nArg);
+ break;
+ }
+#endif
case P4_INT64: {
- sqlite3_snprintf(nTemp, zTemp, "%lld", *pOp->p4.pI64);
+ sqlite3XPrintf(&x, "%lld", *pOp->p4.pI64);
break;
}
case P4_INT32: {
- sqlite3_snprintf(nTemp, zTemp, "%d", pOp->p4.i);
+ sqlite3XPrintf(&x, "%d", pOp->p4.i);
break;
}
case P4_REAL: {
- sqlite3_snprintf(nTemp, zTemp, "%.16g", *pOp->p4.pReal);
+ sqlite3XPrintf(&x, "%.16g", *pOp->p4.pReal);
break;
}
case P4_MEM: {
@@ -65886,11 +71236,11 @@ static char *displayP4(Op *pOp, char *zTemp, int nTemp){
if( pMem->flags & MEM_Str ){
zP4 = pMem->z;
}else if( pMem->flags & MEM_Int ){
- sqlite3_snprintf(nTemp, zTemp, "%lld", pMem->u.i);
+ sqlite3XPrintf(&x, "%lld", pMem->u.i);
}else if( pMem->flags & MEM_Real ){
- sqlite3_snprintf(nTemp, zTemp, "%.16g", pMem->u.r);
+ sqlite3XPrintf(&x, "%.16g", pMem->u.r);
}else if( pMem->flags & MEM_Null ){
- sqlite3_snprintf(nTemp, zTemp, "NULL");
+ zP4 = "NULL";
}else{
assert( pMem->flags & MEM_Blob );
zP4 = "(blob)";
@@ -65900,22 +71250,34 @@ 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", pVtab);
+ sqlite3XPrintf(&x, "vtab:%p", pVtab);
break;
}
#endif
case P4_INTARRAY: {
- sqlite3_snprintf(nTemp, zTemp, "intarray");
+ int i;
+ int *ai = pOp->p4.ai;
+ int n = ai[0]; /* The first element of an INTARRAY is always the
+ ** count of the number of elements to follow */
+ for(i=1; i<n; i++){
+ sqlite3XPrintf(&x, ",%d", ai[i]);
+ }
+ zTemp[0] = '[';
+ sqlite3StrAccumAppend(&x, "]", 1);
break;
}
case P4_SUBPROGRAM: {
- sqlite3_snprintf(nTemp, zTemp, "program");
+ sqlite3XPrintf(&x, "program");
break;
}
case P4_ADVANCE: {
zTemp[0] = 0;
break;
}
+ case P4_TABLE: {
+ sqlite3XPrintf(&x, "%s", pOp->p4.pTab->zName);
+ break;
+ }
default: {
zP4 = pOp->p4.z;
if( zP4==0 ){
@@ -65924,10 +71286,11 @@ static char *displayP4(Op *pOp, char *zTemp, int nTemp){
}
}
}
+ sqlite3StrAccumFinish(&x);
assert( zP4!=0 );
return zP4;
}
-#endif
+#endif /* VDBE_DISPLAY_P4 */
/*
** Declare to the Vdbe that the BTree object at db->aDb[i] is used.
@@ -65946,7 +71309,7 @@ SQLITE_PRIVATE void sqlite3VdbeUsesBtree(Vdbe *p, int i){
}
}
-#if !defined(SQLITE_OMIT_SHARED_CACHE) && SQLITE_THREADSAFE>0
+#if !defined(SQLITE_OMIT_SHARED_CACHE)
/*
** If SQLite is compiled to support shared-cache mode and to be threadsafe,
** this routine obtains the mutex associated with each BtShared structure
@@ -65989,12 +71352,11 @@ SQLITE_PRIVATE void sqlite3VdbeEnter(Vdbe *p){
/*
** Unlock all of the btrees previously locked by a call to sqlite3VdbeEnter().
*/
-SQLITE_PRIVATE void sqlite3VdbeLeave(Vdbe *p){
+static SQLITE_NOINLINE void vdbeLeave(Vdbe *p){
int i;
sqlite3 *db;
Db *aDb;
int nDb;
- if( DbMaskAllZero(p->lockMask) ) return; /* The common case */
db = p->db;
aDb = db->aDb;
nDb = db->nDb;
@@ -66004,6 +71366,10 @@ SQLITE_PRIVATE void sqlite3VdbeLeave(Vdbe *p){
}
}
}
+SQLITE_PRIVATE void sqlite3VdbeLeave(Vdbe *p){
+ if( DbMaskAllZero(p->lockMask) ) return; /* The common case */
+ vdbeLeave(p);
+}
#endif
#if defined(VDBE_PROFILE) || defined(SQLITE_DEBUG)
@@ -66040,7 +71406,6 @@ static void releaseMemArray(Mem *p, int N){
if( p && N ){
Mem *pEnd = &p[N];
sqlite3 *db = p->db;
- u8 malloc_failed = db->mallocFailed;
if( db->pnBytesFreed ){
do{
if( p->szMalloc ) sqlite3DbFree(db, p->zMalloc);
@@ -66076,7 +71441,6 @@ static void releaseMemArray(Mem *p, int N){
p->flags = MEM_Undefined;
}while( (++p)<pEnd );
- db->mallocFailed = malloc_failed;
}
}
@@ -66092,6 +71456,7 @@ SQLITE_PRIVATE void sqlite3VdbeFrameDelete(VdbeFrame *p){
sqlite3VdbeFreeCursor(p->v, apCsr[i]);
}
releaseMemArray(aMem, p->nChildMem);
+ sqlite3VdbeDeleteAuxData(p->v->db, &p->pAuxData, -1, 0);
sqlite3DbFree(p->v->db, p);
}
@@ -66134,10 +71499,10 @@ SQLITE_PRIVATE int sqlite3VdbeList(
releaseMemArray(pMem, 8);
p->pResultSet = 0;
- if( p->rc==SQLITE_NOMEM ){
+ if( p->rc==SQLITE_NOMEM_BKPT ){
/* This happens if a malloc() inside a call to sqlite3_column_text() or
** sqlite3_column_text16() failed. */
- db->mallocFailed = 1;
+ sqlite3OomFault(db);
return SQLITE_ERROR;
}
@@ -66176,7 +71541,7 @@ SQLITE_PRIVATE int sqlite3VdbeList(
}else if( db->u1.isInterrupted ){
p->rc = SQLITE_INTERRUPT;
rc = SQLITE_ERROR;
- sqlite3SetString(&p->zErrMsg, db, "%s", sqlite3ErrStr(p->rc));
+ sqlite3VdbeError(p, sqlite3ErrStr(p->rc));
}else{
char *zP4;
Op *pOp;
@@ -66238,12 +71603,12 @@ SQLITE_PRIVATE int sqlite3VdbeList(
pMem->u.i = pOp->p3; /* P3 */
pMem++;
- if( sqlite3VdbeMemClearAndResize(pMem, 32) ){ /* P4 */
+ if( sqlite3VdbeMemClearAndResize(pMem, 100) ){ /* P4 */
assert( p->db->mallocFailed );
return SQLITE_ERROR;
}
pMem->flags = MEM_Str|MEM_Term;
- zP4 = displayP4(pOp, pMem->z, 32);
+ zP4 = displayP4(pOp, pMem->z, pMem->szMalloc);
if( zP4!=pMem->z ){
sqlite3VdbeMemSetStr(pMem, zP4, -1, SQLITE_UTF8, 0);
}else{
@@ -66335,43 +71700,46 @@ SQLITE_PRIVATE void sqlite3VdbeIOTraceSql(Vdbe *p){
}
#endif /* !SQLITE_OMIT_TRACE && SQLITE_ENABLE_IOTRACE */
-/*
-** Allocate space from a fixed size buffer and return a pointer to
-** that space. If insufficient space is available, return NULL.
-**
-** The pBuf parameter is the initial value of a pointer which will
-** receive the new memory. pBuf is normally NULL. If pBuf is not
-** NULL, it means that memory space has already been allocated and that
-** this routine should not allocate any new memory. When pBuf is not
-** NULL simply return pBuf. Only allocate new memory space when pBuf
-** is NULL.
-**
-** nByte is the number of bytes of space needed.
+/* An instance of this object describes bulk memory available for use
+** by subcomponents of a prepared statement. Space is allocated out
+** of a ReusableSpace object by the allocSpace() routine below.
+*/
+struct ReusableSpace {
+ u8 *pSpace; /* Available memory */
+ int nFree; /* Bytes of available memory */
+ int nNeeded; /* Total bytes that could not be allocated */
+};
+
+/* Try to allocate nByte bytes of 8-byte aligned bulk memory for pBuf
+** from the ReusableSpace object. Return a pointer to the allocated
+** memory on success. If insufficient memory is available in the
+** ReusableSpace object, increase the ReusableSpace.nNeeded
+** value by the amount needed and return NULL.
**
-** *ppFrom points to available space and pEnd points to the end of the
-** available space. When space is allocated, *ppFrom is advanced past
-** the end of the allocated space.
+** If pBuf is not initially NULL, that means that the memory has already
+** been allocated by a prior call to this routine, so just return a copy
+** of pBuf and leave ReusableSpace unchanged.
**
-** *pnByte is a counter of the number of bytes of space that have failed
-** to allocate. If there is insufficient space in *ppFrom to satisfy the
-** request, then increment *pnByte by the amount of the request.
+** This allocator is employed to repurpose unused slots at the end of the
+** opcode array of prepared state for other memory needs of the prepared
+** statement.
*/
static void *allocSpace(
- void *pBuf, /* Where return pointer will be stored */
- int nByte, /* Number of bytes to allocate */
- u8 **ppFrom, /* IN/OUT: Allocate from *ppFrom */
- u8 *pEnd, /* Pointer to 1 byte past the end of *ppFrom buffer */
- int *pnByte /* If allocation cannot be made, increment *pnByte */
-){
- assert( EIGHT_BYTE_ALIGNMENT(*ppFrom) );
- if( pBuf ) return pBuf;
- nByte = ROUND8(nByte);
- if( &(*ppFrom)[nByte] <= pEnd ){
- pBuf = (void*)*ppFrom;
- *ppFrom += nByte;
- }else{
- *pnByte += nByte;
+ struct ReusableSpace *p, /* Bulk memory available for allocation */
+ void *pBuf, /* Pointer to a prior allocation */
+ int nByte /* Bytes of memory needed */
+){
+ assert( EIGHT_BYTE_ALIGNMENT(p->pSpace) );
+ if( pBuf==0 ){
+ nByte = ROUND8(nByte);
+ if( nByte <= p->nFree ){
+ p->nFree -= nByte;
+ pBuf = &p->pSpace[p->nFree];
+ }else{
+ p->nNeeded += nByte;
+ }
}
+ assert( EIGHT_BYTE_ALIGNMENT(pBuf) );
return pBuf;
}
@@ -66394,14 +71762,13 @@ SQLITE_PRIVATE void sqlite3VdbeRewind(Vdbe *p){
p->magic = VDBE_MAGIC_RUN;
#ifdef SQLITE_DEBUG
- for(i=1; i<p->nMem; i++){
+ for(i=0; i<p->nMem; i++){
assert( p->aMem[i].db==p->db );
}
#endif
p->pc = -1;
p->rc = SQLITE_OK;
p->errorAction = OE_Abort;
- p->magic = VDBE_MAGIC_RUN;
p->nChange = 0;
p->cacheCtr = 1;
p->minWriteFileFormat = 255;
@@ -66444,9 +71811,7 @@ SQLITE_PRIVATE void sqlite3VdbeMakeReady(
int nArg; /* Number of arguments in subprograms */
int nOnce; /* Number of OP_Once instructions */
int n; /* Loop counter */
- u8 *zCsr; /* Memory available for allocation */
- u8 *zEnd; /* First byte past allocated memory */
- int nByte; /* How much extra memory is needed */
+ struct ReusableSpace x; /* Reusable bulk memory */
assert( p!=0 );
assert( p->nOp>0 );
@@ -66462,61 +71827,59 @@ SQLITE_PRIVATE void sqlite3VdbeMakeReady(
nOnce = pParse->nOnce;
if( nOnce==0 ) nOnce = 1; /* Ensure at least one byte in p->aOnceFlag[] */
- /* For each cursor required, also allocate a memory cell. Memory
- ** cells (nMem+1-nCursor)..nMem, inclusive, will never be used by
- ** the vdbe program. Instead they are used to allocate space for
- ** VdbeCursor/BtCursor structures. The blob of memory associated with
- ** cursor 0 is stored in memory cell nMem. Memory cell (nMem-1)
- ** stores the blob of memory associated with cursor 1, etc.
- **
+ /* Each cursor uses a memory cell. The first cursor (cursor 0) can
+ ** use aMem[0] which is not otherwise used by the VDBE program. Allocate
+ ** space at the end of aMem[] for cursors 1 and greater.
** See also: allocateCursor().
*/
nMem += nCursor;
+ if( nCursor==0 && nMem>0 ) nMem++; /* Space for aMem[0] even if not used */
- /* Allocate space for memory registers, SQL variables, VDBE cursors and
- ** an array to marshal SQL function arguments in.
+ /* Figure out how much reusable memory is available at the end of the
+ ** opcode array. This extra memory will be reallocated for other elements
+ ** of the prepared statement.
*/
- zCsr = (u8*)&p->aOp[p->nOp]; /* Memory avaliable for allocation */
- zEnd = (u8*)&p->aOp[pParse->nOpAlloc]; /* First byte past end of zCsr[] */
+ n = ROUND8(sizeof(Op)*p->nOp); /* Bytes of opcode memory used */
+ x.pSpace = &((u8*)p->aOp)[n]; /* Unused opcode memory */
+ assert( EIGHT_BYTE_ALIGNMENT(x.pSpace) );
+ x.nFree = ROUNDDOWN8(pParse->szOpAlloc - n); /* Bytes of unused memory */
+ assert( x.nFree>=0 );
+ if( x.nFree>0 ){
+ memset(x.pSpace, 0, x.nFree);
+ assert( EIGHT_BYTE_ALIGNMENT(&x.pSpace[x.nFree]) );
+ }
resolveP2Values(p, &nArg);
p->usesStmtJournal = (u8)(pParse->isMultiWrite && pParse->mayAbort);
if( pParse->explain && nMem<10 ){
nMem = 10;
}
- memset(zCsr, 0, zEnd-zCsr);
- zCsr += (zCsr - (u8*)0)&7;
- assert( EIGHT_BYTE_ALIGNMENT(zCsr) );
p->expired = 0;
- /* Memory for registers, parameters, cursor, etc, is allocated in two
- ** passes. On the first pass, we try to reuse unused space at the
+ /* Memory for registers, parameters, cursor, etc, is allocated in one or two
+ ** passes. On the first pass, we try to reuse unused memory at the
** end of the opcode array. If we are unable to satisfy all memory
** requirements by reusing the opcode array tail, then the second
- ** pass will fill in the rest using a fresh allocation.
+ ** pass will fill in the remainder using a fresh memory allocation.
**
** This two-pass approach that reuses as much memory as possible from
- ** the leftover space at the end of the opcode array can significantly
+ ** the leftover memory at the end of the opcode array. This can significantly
** reduce the amount of memory held by a prepared statement.
*/
do {
- nByte = 0;
- p->aMem = allocSpace(p->aMem, nMem*sizeof(Mem), &zCsr, zEnd, &nByte);
- p->aVar = allocSpace(p->aVar, nVar*sizeof(Mem), &zCsr, zEnd, &nByte);
- p->apArg = allocSpace(p->apArg, nArg*sizeof(Mem*), &zCsr, zEnd, &nByte);
- p->azVar = allocSpace(p->azVar, nVar*sizeof(char*), &zCsr, zEnd, &nByte);
- p->apCsr = allocSpace(p->apCsr, nCursor*sizeof(VdbeCursor*),
- &zCsr, zEnd, &nByte);
- p->aOnceFlag = allocSpace(p->aOnceFlag, nOnce, &zCsr, zEnd, &nByte);
+ x.nNeeded = 0;
+ p->aMem = allocSpace(&x, p->aMem, nMem*sizeof(Mem));
+ p->aVar = allocSpace(&x, p->aVar, nVar*sizeof(Mem));
+ p->apArg = allocSpace(&x, p->apArg, nArg*sizeof(Mem*));
+ p->apCsr = allocSpace(&x, p->apCsr, nCursor*sizeof(VdbeCursor*));
+ p->aOnceFlag = allocSpace(&x, p->aOnceFlag, nOnce);
#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
- p->anExec = allocSpace(p->anExec, p->nOp*sizeof(i64), &zCsr, zEnd, &nByte);
+ p->anExec = allocSpace(&x, p->anExec, p->nOp*sizeof(i64));
#endif
- if( nByte ){
- p->pFree = sqlite3DbMallocZero(db, nByte);
- }
- zCsr = p->pFree;
- zEnd = &zCsr[nByte];
- }while( nByte && !db->mallocFailed );
+ if( x.nNeeded==0 ) break;
+ x.pSpace = p->pFree = sqlite3DbMallocZero(db, x.nNeeded);
+ x.nFree = x.nNeeded;
+ }while( !db->mallocFailed );
p->nCursor = nCursor;
p->nOnceFlag = nOnce;
@@ -66527,15 +71890,13 @@ SQLITE_PRIVATE void sqlite3VdbeMakeReady(
p->aVar[n].db = db;
}
}
- if( p->azVar && pParse->nzVar>0 ){
- p->nzVar = pParse->nzVar;
- memcpy(p->azVar, pParse->azVar, p->nzVar*sizeof(p->azVar[0]));
- memset(pParse->azVar, 0, pParse->nzVar*sizeof(pParse->azVar[0]));
- }
+ p->nzVar = pParse->nzVar;
+ p->azVar = pParse->azVar;
+ pParse->nzVar = 0;
+ pParse->azVar = 0;
if( p->aMem ){
- p->aMem--; /* aMem[] goes from 1..nMem */
- p->nMem = nMem; /* not from 0..nMem-1 */
- for(n=1; n<=nMem; n++){
+ p->nMem = nMem;
+ for(n=0; n<nMem; n++){
p->aMem[n].flags = MEM_Undefined;
p->aMem[n].db = db;
}
@@ -66552,23 +71913,34 @@ SQLITE_PRIVATE void sqlite3VdbeFreeCursor(Vdbe *p, VdbeCursor *pCx){
if( pCx==0 ){
return;
}
- sqlite3VdbeSorterClose(p->db, pCx);
- if( pCx->pBt ){
- sqlite3BtreeClose(pCx->pBt);
- /* The pCx->pCursor will be close automatically, if it exists, by
- ** the call above. */
- }else if( pCx->pCursor ){
- sqlite3BtreeCloseCursor(pCx->pCursor);
- }
+ assert( pCx->pBt==0 || pCx->eCurType==CURTYPE_BTREE );
+ switch( pCx->eCurType ){
+ case CURTYPE_SORTER: {
+ sqlite3VdbeSorterClose(p->db, pCx);
+ break;
+ }
+ case CURTYPE_BTREE: {
+ if( pCx->pBt ){
+ sqlite3BtreeClose(pCx->pBt);
+ /* The pCx->pCursor will be close automatically, if it exists, by
+ ** the call above. */
+ }else{
+ assert( pCx->uc.pCursor!=0 );
+ sqlite3BtreeCloseCursor(pCx->uc.pCursor);
+ }
+ break;
+ }
#ifndef SQLITE_OMIT_VIRTUALTABLE
- else if( pCx->pVtabCursor ){
- sqlite3_vtab_cursor *pVtabCursor = pCx->pVtabCursor;
- const sqlite3_module *pModule = pVtabCursor->pVtab->pModule;
- assert( pVtabCursor->pVtab->nRef>0 );
- pVtabCursor->pVtab->nRef--;
- pModule->xClose(pVtabCursor);
- }
+ case CURTYPE_VTAB: {
+ sqlite3_vtab_cursor *pVCur = pCx->uc.pVCur;
+ const sqlite3_module *pModule = pVCur->pVtab->pModule;
+ assert( pVCur->pVtab->nRef>0 );
+ pVCur->pVtab->nRef--;
+ pModule->xClose(pVCur);
+ break;
+ }
#endif
+ }
}
/*
@@ -66609,6 +71981,9 @@ SQLITE_PRIVATE int sqlite3VdbeFrameRestore(VdbeFrame *pFrame){
v->db->lastRowid = pFrame->lastRowid;
v->nChange = pFrame->nChange;
v->db->nChange = pFrame->nDbChange;
+ sqlite3VdbeDeleteAuxData(v->db, &v->pAuxData, -1, 0);
+ v->pAuxData = pFrame->pAuxData;
+ pFrame->pAuxData = 0;
return pFrame->pc;
}
@@ -66631,7 +72006,7 @@ static void closeAllCursors(Vdbe *p){
assert( p->nFrame==0 );
closeCursorsInFrame(p);
if( p->aMem ){
- releaseMemArray(&p->aMem[1], p->nMem);
+ releaseMemArray(p->aMem, p->nMem);
}
while( p->pDelFrame ){
VdbeFrame *pDel = p->pDelFrame;
@@ -66640,7 +72015,7 @@ static void closeAllCursors(Vdbe *p){
}
/* Delete any auxdata allocations made by the VM */
- if( p->pAuxData ) sqlite3VdbeDeleteAuxData(p, -1, 0);
+ if( p->pAuxData ) sqlite3VdbeDeleteAuxData(p->db, &p->pAuxData, -1, 0);
assert( p->pAuxData==0 );
}
@@ -66656,7 +72031,7 @@ static void Cleanup(Vdbe *p){
int i;
if( p->apCsr ) for(i=0; i<p->nCursor; i++) assert( p->apCsr[i]==0 );
if( p->aMem ){
- for(i=1; i<=p->nMem; i++) assert( p->aMem[i].flags==MEM_Undefined );
+ for(i=0; i<p->nMem; i++) assert( p->aMem[i].flags==MEM_Undefined );
}
#endif
@@ -66712,7 +72087,7 @@ SQLITE_PRIVATE int sqlite3VdbeSetColName(
assert( var<COLNAME_N );
if( p->db->mallocFailed ){
assert( !zName || xDel!=SQLITE_DYNAMIC );
- return SQLITE_NOMEM;
+ return SQLITE_NOMEM_BKPT;
}
assert( p->aColName!=0 );
pColName = &(p->aColName[idx+var*p->nResColumn]);
@@ -66729,7 +72104,9 @@ SQLITE_PRIVATE int sqlite3VdbeSetColName(
*/
static int vdbeCommit(sqlite3 *db, Vdbe *p){
int i;
- int nTrans = 0; /* Number of databases with an active write-transaction */
+ int nTrans = 0; /* Number of databases with an active write-transaction
+ ** that are candidates for a two-phase commit using a
+ ** master-journal */
int rc = SQLITE_OK;
int needXcommit = 0;
@@ -66757,10 +72134,28 @@ static int vdbeCommit(sqlite3 *db, Vdbe *p){
for(i=0; rc==SQLITE_OK && i<db->nDb; i++){
Btree *pBt = db->aDb[i].pBt;
if( sqlite3BtreeIsInTrans(pBt) ){
+ /* Whether or not a database might need a master journal depends upon
+ ** its journal mode (among other things). This matrix determines which
+ ** journal modes use a master journal and which do not */
+ static const u8 aMJNeeded[] = {
+ /* DELETE */ 1,
+ /* PERSIST */ 1,
+ /* OFF */ 0,
+ /* TRUNCATE */ 1,
+ /* MEMORY */ 0,
+ /* WAL */ 0
+ };
+ Pager *pPager; /* Pager associated with pBt */
needXcommit = 1;
- if( i!=1 ) nTrans++;
sqlite3BtreeEnter(pBt);
- rc = sqlite3PagerExclusiveLock(sqlite3BtreePager(pBt));
+ pPager = sqlite3BtreePager(pBt);
+ if( db->aDb[i].safety_level!=PAGER_SYNCHRONOUS_OFF
+ && aMJNeeded[sqlite3PagerGetJournalMode(pPager)]
+ ){
+ assert( i!=1 );
+ nTrans++;
+ }
+ rc = sqlite3PagerExclusiveLock(pPager);
sqlite3BtreeLeave(pBt);
}
}
@@ -66818,7 +72213,6 @@ static int vdbeCommit(sqlite3 *db, Vdbe *p){
#ifndef SQLITE_OMIT_DISKIO
else{
sqlite3_vfs *pVfs = db->pVfs;
- int needSync = 0;
char *zMaster = 0; /* File-name for the master journal */
char const *zMainFile = sqlite3BtreeGetFilename(db->aDb[0].pBt);
sqlite3_file *pMaster = 0;
@@ -66830,7 +72224,7 @@ static int vdbeCommit(sqlite3 *db, Vdbe *p){
/* Select a master journal file name */
nMainFile = sqlite3Strlen30(zMainFile);
zMaster = sqlite3MPrintf(db, "%s-mjXXXXXX9XXz", zMainFile);
- if( zMaster==0 ) return SQLITE_NOMEM;
+ if( zMaster==0 ) return SQLITE_NOMEM_BKPT;
do {
u32 iRandom;
if( retryCount ){
@@ -66878,9 +72272,6 @@ static int vdbeCommit(sqlite3 *db, Vdbe *p){
continue; /* Ignore TEMP and :memory: databases */
}
assert( zFile[0]!=0 );
- if( !needSync && !sqlite3BtreeSyncDisabled(pBt) ){
- needSync = 1;
- }
rc = sqlite3OsWrite(pMaster, zFile, sqlite3Strlen30(zFile)+1, offset);
offset += sqlite3Strlen30(zFile)+1;
if( rc!=SQLITE_OK ){
@@ -66895,8 +72286,7 @@ static int vdbeCommit(sqlite3 *db, Vdbe *p){
/* Sync the master journal file. If the IOCAP_SEQUENTIAL device
** flag is set this is not required.
*/
- if( needSync
- && 0==(sqlite3OsDeviceCharacteristics(pMaster)&SQLITE_IOCAP_SEQUENTIAL)
+ if( 0==(sqlite3OsDeviceCharacteristics(pMaster)&SQLITE_IOCAP_SEQUENTIAL)
&& SQLITE_OK!=(rc = sqlite3OsSync(pMaster, SQLITE_SYNC_NORMAL))
){
sqlite3OsCloseFree(pMaster);
@@ -66932,7 +72322,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, needSync);
+ rc = sqlite3OsDelete(pVfs, zMaster, 1);
sqlite3DbFree(db, zMaster);
zMaster = 0;
if( rc ){
@@ -67079,7 +72469,7 @@ SQLITE_PRIVATE int sqlite3VdbeCheckFk(Vdbe *p, int deferred){
){
p->rc = SQLITE_CONSTRAINT_FOREIGNKEY;
p->errorAction = OE_Abort;
- sqlite3SetString(&p->zErrMsg, db, "FOREIGN KEY constraint failed");
+ sqlite3VdbeError(p, "FOREIGN KEY constraint failed");
return SQLITE_ERROR;
}
return SQLITE_OK;
@@ -67119,8 +72509,8 @@ SQLITE_PRIVATE int sqlite3VdbeHalt(Vdbe *p){
** one, or the complete transaction if there is no statement transaction.
*/
- if( p->db->mallocFailed ){
- p->rc = SQLITE_NOMEM;
+ if( db->mallocFailed ){
+ p->rc = SQLITE_NOMEM_BKPT;
}
if( p->aOnceFlag ) memset(p->aOnceFlag, 0, p->nOnceFlag);
closeAllCursors(p);
@@ -67280,8 +72670,8 @@ SQLITE_PRIVATE int sqlite3VdbeHalt(Vdbe *p){
}
p->magic = VDBE_MAGIC_HALT;
checkActiveVdbeCnt(db);
- if( p->db->mallocFailed ){
- p->rc = SQLITE_NOMEM;
+ if( db->mallocFailed ){
+ p->rc = SQLITE_NOMEM_BKPT;
}
/* If the auto-commit flag is set to true, then any locks that were held
@@ -67317,12 +72707,12 @@ SQLITE_PRIVATE int sqlite3VdbeTransferError(Vdbe *p){
sqlite3 *db = p->db;
int rc = p->rc;
if( p->zErrMsg ){
- u8 mallocFailed = db->mallocFailed;
+ db->bBenignMalloc++;
sqlite3BeginBenignMalloc();
if( db->pErr==0 ) db->pErr = sqlite3ValueNew(db);
sqlite3ValueSetStr(db->pErr, -1, p->zErrMsg, SQLITE_UTF8, SQLITE_TRANSIENT);
sqlite3EndBenignMalloc();
- db->mallocFailed = mallocFailed;
+ db->bBenignMalloc--;
db->errCode = rc;
}else{
sqlite3Error(db, rc);
@@ -67468,8 +72858,7 @@ SQLITE_PRIVATE int sqlite3VdbeFinalize(Vdbe *p){
** * the corresponding bit in argument mask is clear (where the first
** function parameter corresponds to bit 0 etc.).
*/
-SQLITE_PRIVATE void sqlite3VdbeDeleteAuxData(Vdbe *pVdbe, int iOp, int mask){
- AuxData **pp = &pVdbe->pAuxData;
+SQLITE_PRIVATE void sqlite3VdbeDeleteAuxData(sqlite3 *db, AuxData **pp, int iOp, int mask){
while( *pp ){
AuxData *pAux = *pp;
if( (iOp<0)
@@ -67480,7 +72869,7 @@ SQLITE_PRIVATE void sqlite3VdbeDeleteAuxData(Vdbe *pVdbe, int iOp, int mask){
pAux->xDelete(pAux->pAux);
}
*pp = pAux->pNext;
- sqlite3DbFree(pVdbe->db, pAux);
+ sqlite3DbFree(db, pAux);
}else{
pp= &pAux->pNext;
}
@@ -67507,6 +72896,7 @@ SQLITE_PRIVATE void sqlite3VdbeClearObject(sqlite3 *db, Vdbe *p){
sqlite3DbFree(db, pSub);
}
for(i=p->nzVar-1; i>=0; i--) sqlite3DbFree(db, p->azVar[i]);
+ sqlite3DbFree(db, p->azVar);
vdbeFreeOpArray(db, p->aOp, p->nOp);
sqlite3DbFree(db, p->aColName);
sqlite3DbFree(db, p->zSql);
@@ -67555,7 +72945,8 @@ static int SQLITE_NOINLINE handleDeferredMoveto(VdbeCursor *p){
#endif
assert( p->deferredMoveto );
assert( p->isTable );
- rc = sqlite3BtreeMovetoUnpacked(p->pCursor, 0, p->movetoTarget, 0, &res);
+ assert( p->eCurType==CURTYPE_BTREE );
+ rc = sqlite3BtreeMovetoUnpacked(p->uc.pCursor, 0, p->movetoTarget, 0, &res);
if( rc ) return rc;
if( res!=0 ) return SQLITE_CORRUPT_BKPT;
#ifdef SQLITE_TEST
@@ -67575,9 +72966,10 @@ static int SQLITE_NOINLINE handleDeferredMoveto(VdbeCursor *p){
*/
static int SQLITE_NOINLINE handleMovedCursor(VdbeCursor *p){
int isDifferentRow, rc;
- assert( p->pCursor!=0 );
- assert( sqlite3BtreeCursorHasMoved(p->pCursor) );
- rc = sqlite3BtreeCursorRestore(p->pCursor, &isDifferentRow);
+ assert( p->eCurType==CURTYPE_BTREE );
+ assert( p->uc.pCursor!=0 );
+ assert( sqlite3BtreeCursorHasMoved(p->uc.pCursor) );
+ rc = sqlite3BtreeCursorRestore(p->uc.pCursor, &isDifferentRow);
p->cacheStatus = CACHE_STALE;
if( isDifferentRow ) p->nullRow = 1;
return rc;
@@ -67588,7 +72980,8 @@ static int SQLITE_NOINLINE handleMovedCursor(VdbeCursor *p){
** if need be. Return any I/O error from the restore operation.
*/
SQLITE_PRIVATE int sqlite3VdbeCursorRestore(VdbeCursor *p){
- if( sqlite3BtreeCursorHasMoved(p->pCursor) ){
+ assert( p->eCurType==CURTYPE_BTREE );
+ if( sqlite3BtreeCursorHasMoved(p->uc.pCursor) ){
return handleMovedCursor(p);
}
return SQLITE_OK;
@@ -67607,12 +73000,21 @@ SQLITE_PRIVATE int sqlite3VdbeCursorRestore(VdbeCursor *p){
** If the cursor is already pointing to the correct row and that row has
** not been deleted out from under the cursor, then this routine is a no-op.
*/
-SQLITE_PRIVATE int sqlite3VdbeCursorMoveto(VdbeCursor *p){
- if( p->deferredMoveto ){
- return handleDeferredMoveto(p);
- }
- if( p->pCursor && sqlite3BtreeCursorHasMoved(p->pCursor) ){
- return handleMovedCursor(p);
+SQLITE_PRIVATE int sqlite3VdbeCursorMoveto(VdbeCursor **pp, int *piCol){
+ VdbeCursor *p = *pp;
+ if( p->eCurType==CURTYPE_BTREE ){
+ if( p->deferredMoveto ){
+ int iMap;
+ if( p->aAltMap && (iMap = p->aAltMap[1+*piCol])>0 ){
+ *pp = p->pAltCursor;
+ *piCol = iMap - 1;
+ return SQLITE_OK;
+ }
+ return handleDeferredMoveto(p);
+ }
+ if( sqlite3BtreeCursorHasMoved(p->uc.pCursor) ){
+ return handleMovedCursor(p);
+ }
}
return SQLITE_OK;
}
@@ -67662,11 +73064,13 @@ SQLITE_PRIVATE int sqlite3VdbeCursorMoveto(VdbeCursor *p){
/*
** Return the serial-type for the value stored in pMem.
*/
-SQLITE_PRIVATE u32 sqlite3VdbeSerialType(Mem *pMem, int file_format){
+SQLITE_PRIVATE u32 sqlite3VdbeSerialType(Mem *pMem, int file_format, u32 *pLen){
int flags = pMem->flags;
u32 n;
+ assert( pLen!=0 );
if( flags&MEM_Null ){
+ *pLen = 0;
return 0;
}
if( flags&MEM_Int ){
@@ -67680,15 +73084,23 @@ SQLITE_PRIVATE u32 sqlite3VdbeSerialType(Mem *pMem, int file_format){
u = i;
}
if( u<=127 ){
- return ((i&1)==i && file_format>=4) ? 8+(u32)u : 1;
+ if( (i&1)==i && file_format>=4 ){
+ *pLen = 0;
+ return 8+(u32)u;
+ }else{
+ *pLen = 1;
+ return 1;
+ }
}
- if( u<=32767 ) return 2;
- if( u<=8388607 ) return 3;
- if( u<=2147483647 ) return 4;
- if( u<=MAX_6BYTE ) return 5;
+ if( u<=32767 ){ *pLen = 2; return 2; }
+ if( u<=8388607 ){ *pLen = 3; return 3; }
+ if( u<=2147483647 ){ *pLen = 4; return 4; }
+ if( u<=MAX_6BYTE ){ *pLen = 6; return 5; }
+ *pLen = 8;
return 6;
}
if( flags&MEM_Real ){
+ *pLen = 8;
return 7;
}
assert( pMem->db->mallocFailed || flags&(MEM_Str|MEM_Blob) );
@@ -67697,20 +73109,46 @@ SQLITE_PRIVATE u32 sqlite3VdbeSerialType(Mem *pMem, int file_format){
if( flags & MEM_Zero ){
n += pMem->u.nZero;
}
+ *pLen = n;
return ((n*2) + 12 + ((flags&MEM_Str)!=0));
}
/*
+** The sizes for serial types less than 128
+*/
+static const u8 sqlite3SmallTypeSizes[] = {
+ /* 0 1 2 3 4 5 6 7 8 9 */
+/* 0 */ 0, 1, 2, 3, 4, 6, 8, 8, 0, 0,
+/* 10 */ 0, 0, 0, 0, 1, 1, 2, 2, 3, 3,
+/* 20 */ 4, 4, 5, 5, 6, 6, 7, 7, 8, 8,
+/* 30 */ 9, 9, 10, 10, 11, 11, 12, 12, 13, 13,
+/* 40 */ 14, 14, 15, 15, 16, 16, 17, 17, 18, 18,
+/* 50 */ 19, 19, 20, 20, 21, 21, 22, 22, 23, 23,
+/* 60 */ 24, 24, 25, 25, 26, 26, 27, 27, 28, 28,
+/* 70 */ 29, 29, 30, 30, 31, 31, 32, 32, 33, 33,
+/* 80 */ 34, 34, 35, 35, 36, 36, 37, 37, 38, 38,
+/* 90 */ 39, 39, 40, 40, 41, 41, 42, 42, 43, 43,
+/* 100 */ 44, 44, 45, 45, 46, 46, 47, 47, 48, 48,
+/* 110 */ 49, 49, 50, 50, 51, 51, 52, 52, 53, 53,
+/* 120 */ 54, 54, 55, 55, 56, 56, 57, 57
+};
+
+/*
** Return the length of the data corresponding to the supplied serial-type.
*/
SQLITE_PRIVATE u32 sqlite3VdbeSerialTypeLen(u32 serial_type){
- if( serial_type>=12 ){
+ if( serial_type>=128 ){
return (serial_type-12)/2;
}else{
- static const u8 aSize[] = { 0, 1, 2, 3, 4, 6, 8, 8, 0, 0, 0, 0 };
- return aSize[serial_type];
+ assert( serial_type<12
+ || sqlite3SmallTypeSizes[serial_type]==(serial_type - 12)/2 );
+ return sqlite3SmallTypeSizes[serial_type];
}
}
+SQLITE_PRIVATE u8 sqlite3VdbeOneByteSerialTypeLen(u8 serial_type){
+ assert( serial_type<128 );
+ return sqlite3SmallTypeSizes[serial_type];
+}
/*
** If we are on an architecture with mixed-endian floating
@@ -67792,7 +73230,7 @@ SQLITE_PRIVATE u32 sqlite3VdbeSerialPut(u8 *buf, Mem *pMem, u32 serial_type){
}else{
v = pMem->u.i;
}
- len = i = sqlite3VdbeSerialTypeLen(serial_type);
+ len = i = sqlite3SmallTypeSizes[serial_type];
assert( i>0 );
do{
buf[--i] = (u8)(v&0xFF);
@@ -67806,7 +73244,7 @@ SQLITE_PRIVATE u32 sqlite3VdbeSerialPut(u8 *buf, Mem *pMem, u32 serial_type){
assert( pMem->n + ((pMem->flags & MEM_Zero)?pMem->u.nZero:0)
== (int)sqlite3VdbeSerialTypeLen(serial_type) );
len = pMem->n;
- memcpy(buf, pMem->z, len);
+ if( len>0 ) memcpy(buf, pMem->z, len);
return len;
}
@@ -67909,6 +73347,10 @@ SQLITE_PRIVATE u32 sqlite3VdbeSerialGet(
/* EVIDENCE-OF: R-01849-26079 Value is a big-endian 32-bit
** twos-complement integer. */
pMem->u.i = FOUR_BYTE_INT(buf);
+#ifdef __HP_cc
+ /* Work around a sign-extension bug in the HP compiler for HP/UX */
+ if( buf[0]&0x80 ) pMem->u.i |= 0xffffffff80000000LL;
+#endif
pMem->flags = MEM_Int;
testcase( pMem->u.i<0 );
return 4;
@@ -68026,6 +73468,7 @@ SQLITE_PRIVATE void sqlite3VdbeRecordUnpack(
pMem->db = pKeyInfo->db;
/* pMem->flags = 0; // sqlite3VdbeSerialGet() will set this for us */
pMem->szMalloc = 0;
+ pMem->z = 0;
d += sqlite3VdbeSerialGet(&aKey[d], serial_type, pMem);
pMem++;
if( (++u)>=p->nField ) break;
@@ -68077,6 +73520,7 @@ static int vdbeRecordCompareDebug(
/* mem1.u.i = 0; // not needed, here to silence compiler warning */
idx1 = getVarint32(aKey1, szHdr1);
+ if( szHdr1>98307 ) return SQLITE_CORRUPT;
d1 = szHdr1;
assert( pKeyInfo->nField+pKeyInfo->nXField>=pPKey2->nField || CORRUPT_DB );
assert( pKeyInfo->aSortOrder!=0 );
@@ -68205,9 +73649,9 @@ static int vdbeCompareMemString(
v2 = sqlite3ValueText((sqlite3_value*)&c2, pColl->enc);
n2 = v2==0 ? 0 : c2.n;
rc = pColl->xCmp(pColl->pUser, n1, v1, n2, v2);
+ if( (v1==0 || v2==0) && prcErr ) *prcErr = SQLITE_NOMEM_BKPT;
sqlite3VdbeMemRelease(&c1);
sqlite3VdbeMemRelease(&c2);
- if( (v1==0 || v2==0) && prcErr ) *prcErr = SQLITE_NOMEM;
return rc;
}
}
@@ -68223,6 +73667,34 @@ static SQLITE_NOINLINE int sqlite3BlobCompare(const Mem *pB1, const Mem *pB2){
return pB1->n - pB2->n;
}
+/*
+** Do a comparison between a 64-bit signed integer and a 64-bit floating-point
+** number. Return negative, zero, or positive if the first (i64) is less than,
+** equal to, or greater than the second (double).
+*/
+static int sqlite3IntFloatCompare(i64 i, double r){
+ if( sizeof(LONGDOUBLE_TYPE)>8 ){
+ LONGDOUBLE_TYPE x = (LONGDOUBLE_TYPE)i;
+ if( x<r ) return -1;
+ if( x>r ) return +1;
+ return 0;
+ }else{
+ i64 y;
+ double s;
+ if( r<-9223372036854775808.0 ) return +1;
+ if( r>9223372036854775807.0 ) return -1;
+ y = (i64)r;
+ if( i<y ) return -1;
+ if( i>y ){
+ if( y==SMALLEST_INT64 && r>0.0 ) return -1;
+ return +1;
+ }
+ s = (double)i;
+ if( s<r ) return -1;
+ if( s>r ) return +1;
+ return 0;
+ }
+}
/*
** Compare the values contained by the two memory cells, returning
@@ -68249,34 +73721,34 @@ SQLITE_PRIVATE int sqlite3MemCompare(const Mem *pMem1, const Mem *pMem2, const C
return (f2&MEM_Null) - (f1&MEM_Null);
}
- /* If one value is a number and the other is not, the number is less.
- ** If both are numbers, compare as reals if one is a real, or as integers
- ** if both values are integers.
+ /* At least one of the two values is a number
*/
if( combined_flags&(MEM_Int|MEM_Real) ){
- double r1, r2;
if( (f1 & f2 & MEM_Int)!=0 ){
if( pMem1->u.i < pMem2->u.i ) return -1;
- if( pMem1->u.i > pMem2->u.i ) return 1;
+ if( pMem1->u.i > pMem2->u.i ) return +1;
return 0;
}
- if( (f1&MEM_Real)!=0 ){
- r1 = pMem1->u.r;
- }else if( (f1&MEM_Int)!=0 ){
- r1 = (double)pMem1->u.i;
- }else{
- return 1;
+ if( (f1 & f2 & MEM_Real)!=0 ){
+ if( pMem1->u.r < pMem2->u.r ) return -1;
+ if( pMem1->u.r > pMem2->u.r ) return +1;
+ return 0;
}
- if( (f2&MEM_Real)!=0 ){
- r2 = pMem2->u.r;
- }else if( (f2&MEM_Int)!=0 ){
- r2 = (double)pMem2->u.i;
- }else{
- return -1;
+ if( (f1&MEM_Int)!=0 ){
+ if( (f2&MEM_Real)!=0 ){
+ return sqlite3IntFloatCompare(pMem1->u.i, pMem2->u.r);
+ }else{
+ return -1;
+ }
}
- if( r1<r2 ) return -1;
- if( r1>r2 ) return 1;
- return 0;
+ if( (f1&MEM_Real)!=0 ){
+ if( (f2&MEM_Int)!=0 ){
+ return -sqlite3IntFloatCompare(pMem2->u.i, pMem1->u.r);
+ }else{
+ return -1;
+ }
+ }
+ return +1;
}
/* If one value is a string and the other is a blob, the string is less.
@@ -68290,7 +73762,7 @@ SQLITE_PRIVATE int sqlite3MemCompare(const Mem *pMem1, const Mem *pMem2, const C
return -1;
}
- assert( pMem1->enc==pMem2->enc );
+ assert( pMem1->enc==pMem2->enc || pMem1->db->mallocFailed );
assert( pMem1->enc==SQLITE_UTF8 ||
pMem1->enc==SQLITE_UTF16LE || pMem1->enc==SQLITE_UTF16BE );
@@ -68422,18 +73894,13 @@ SQLITE_PRIVATE int sqlite3VdbeRecordCompareWithSkip(
if( pRhs->flags & MEM_Int ){
serial_type = aKey1[idx1];
testcase( serial_type==12 );
- if( serial_type>=12 ){
+ if( serial_type>=10 ){
rc = +1;
}else if( serial_type==0 ){
rc = -1;
}else if( serial_type==7 ){
- double rhs = (double)pRhs->u.i;
sqlite3VdbeSerialGet(&aKey1[d1], serial_type, &mem1);
- if( mem1.u.r<rhs ){
- rc = -1;
- }else if( mem1.u.r>rhs ){
- rc = +1;
- }
+ rc = -sqlite3IntFloatCompare(pRhs->u.i, mem1.u.r);
}else{
i64 lhs = vdbeRecordDecodeInt(serial_type, &aKey1[d1]);
i64 rhs = pRhs->u.i;
@@ -68448,23 +73915,24 @@ SQLITE_PRIVATE int sqlite3VdbeRecordCompareWithSkip(
/* RHS is real */
else if( pRhs->flags & MEM_Real ){
serial_type = aKey1[idx1];
- if( serial_type>=12 ){
+ if( serial_type>=10 ){
+ /* Serial types 12 or greater are strings and blobs (greater than
+ ** numbers). Types 10 and 11 are currently "reserved for future
+ ** use", so it doesn't really matter what the results of comparing
+ ** them to numberic values are. */
rc = +1;
}else if( serial_type==0 ){
rc = -1;
}else{
- double rhs = pRhs->u.r;
- double lhs;
sqlite3VdbeSerialGet(&aKey1[d1], serial_type, &mem1);
if( serial_type==7 ){
- lhs = mem1.u.r;
+ if( mem1.u.r<pRhs->u.r ){
+ rc = -1;
+ }else if( mem1.u.r>pRhs->u.r ){
+ rc = +1;
+ }
}else{
- lhs = (double)mem1.u.i;
- }
- if( lhs<rhs ){
- rc = -1;
- }else if( lhs>rhs ){
- rc = +1;
+ rc = sqlite3IntFloatCompare(mem1.u.i, pRhs->u.r);
}
}
}
@@ -68554,6 +74022,7 @@ SQLITE_PRIVATE int sqlite3VdbeRecordCompareWithSkip(
|| vdbeRecordCompareDebug(nKey1, pKey1, pPKey2, pPKey2->default_rc)
|| pKeyInfo->db->mallocFailed
);
+ pPKey2->eqSeen = 1;
return pPKey2->default_rc;
}
SQLITE_PRIVATE int sqlite3VdbeRecordCompare(
@@ -68653,6 +74122,7 @@ static int vdbeRecordCompareInt(
/* The first fields of the two keys are equal and there are no trailing
** fields. Return pPKey2->default_rc in this case. */
res = pPKey2->default_rc;
+ pPKey2->eqSeen = 1;
}
assert( vdbeRecordCompareDebug(nKey1, pKey1, pPKey2, res) );
@@ -68673,6 +74143,7 @@ static int vdbeRecordCompareString(
int serial_type;
int res;
+ assert( pPKey2->aMem[0].flags & MEM_Str );
vdbeAssertFieldCountWithinLimits(nKey1, pKey1, pPKey2->pKeyInfo);
getVarint32(&aKey1[1], serial_type);
if( serial_type<12 ){
@@ -68699,6 +74170,7 @@ static int vdbeRecordCompareString(
res = sqlite3VdbeRecordCompareWithSkip(nKey1, pKey1, pPKey2, 1);
}else{
res = pPKey2->default_rc;
+ pPKey2->eqSeen = 1;
}
}else if( res>0 ){
res = pPKey2->r2;
@@ -68817,7 +74289,7 @@ SQLITE_PRIVATE int sqlite3VdbeIdxRowid(sqlite3 *db, BtCursor *pCur, i64 *rowid){
if( unlikely(typeRowid<1 || typeRowid>9 || typeRowid==7) ){
goto idx_rowid_corruption;
}
- lenRowid = sqlite3VdbeSerialTypeLen(typeRowid);
+ lenRowid = sqlite3SmallTypeSizes[typeRowid];
testcase( (u32)m.n==szHdr+lenRowid );
if( unlikely((u32)m.n<szHdr+lenRowid) ){
goto idx_rowid_corruption;
@@ -68856,9 +74328,11 @@ SQLITE_PRIVATE int sqlite3VdbeIdxKeyCompare(
){
i64 nCellKey = 0;
int rc;
- BtCursor *pCur = pC->pCursor;
+ BtCursor *pCur;
Mem m;
+ assert( pC->eCurType==CURTYPE_BTREE );
+ pCur = pC->uc.pCursor;
assert( sqlite3BtreeCursorIsValid(pCur) );
VVA_ONLY(rc =) sqlite3BtreeKeySize(pCur, &nCellKey);
assert( rc==SQLITE_OK ); /* pCur is always valid so KeySize cannot fail */
@@ -68869,7 +74343,7 @@ SQLITE_PRIVATE int sqlite3VdbeIdxKeyCompare(
return SQLITE_CORRUPT_BKPT;
}
sqlite3VdbeMemInit(&m, db, 0);
- rc = sqlite3VdbeMemFromBtree(pC->pCursor, 0, (u32)nCellKey, 1, &m);
+ rc = sqlite3VdbeMemFromBtree(pCur, 0, (u32)nCellKey, 1, &m);
if( rc ){
return rc;
}
@@ -68965,14 +74439,100 @@ SQLITE_PRIVATE void sqlite3VdbeSetVarmask(Vdbe *v, int iVar){
** in memory obtained from sqlite3DbMalloc).
*/
SQLITE_PRIVATE void sqlite3VtabImportErrmsg(Vdbe *p, sqlite3_vtab *pVtab){
- sqlite3 *db = p->db;
- sqlite3DbFree(db, p->zErrMsg);
- p->zErrMsg = sqlite3DbStrDup(db, pVtab->zErrMsg);
- sqlite3_free(pVtab->zErrMsg);
- pVtab->zErrMsg = 0;
+ if( pVtab->zErrMsg ){
+ sqlite3 *db = p->db;
+ sqlite3DbFree(db, p->zErrMsg);
+ p->zErrMsg = sqlite3DbStrDup(db, pVtab->zErrMsg);
+ sqlite3_free(pVtab->zErrMsg);
+ pVtab->zErrMsg = 0;
+ }
}
#endif /* SQLITE_OMIT_VIRTUALTABLE */
+#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
+
+/*
+** If the second argument is not NULL, release any allocations associated
+** with the memory cells in the p->aMem[] array. Also free the UnpackedRecord
+** structure itself, using sqlite3DbFree().
+**
+** This function is used to free UnpackedRecord structures allocated by
+** the vdbeUnpackRecord() function found in vdbeapi.c.
+*/
+static void vdbeFreeUnpacked(sqlite3 *db, UnpackedRecord *p){
+ if( p ){
+ int i;
+ for(i=0; i<p->nField; i++){
+ Mem *pMem = &p->aMem[i];
+ if( pMem->zMalloc ) sqlite3VdbeMemRelease(pMem);
+ }
+ sqlite3DbFree(db, p);
+ }
+}
+#endif /* SQLITE_ENABLE_PREUPDATE_HOOK */
+
+#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
+/*
+** Invoke the pre-update hook. If this is an UPDATE or DELETE pre-update call,
+** then cursor passed as the second argument should point to the row about
+** to be update or deleted. If the application calls sqlite3_preupdate_old(),
+** the required value will be read from the row the cursor points to.
+*/
+SQLITE_PRIVATE void sqlite3VdbePreUpdateHook(
+ Vdbe *v, /* Vdbe pre-update hook is invoked by */
+ VdbeCursor *pCsr, /* Cursor to grab old.* values from */
+ int op, /* SQLITE_INSERT, UPDATE or DELETE */
+ const char *zDb, /* Database name */
+ Table *pTab, /* Modified table */
+ i64 iKey1, /* Initial key value */
+ int iReg /* Register for new.* record */
+){
+ sqlite3 *db = v->db;
+ i64 iKey2;
+ PreUpdate preupdate;
+ const char *zTbl = pTab->zName;
+ static const u8 fakeSortOrder = 0;
+
+ assert( db->pPreUpdate==0 );
+ memset(&preupdate, 0, sizeof(PreUpdate));
+ if( op==SQLITE_UPDATE ){
+ iKey2 = v->aMem[iReg].u.i;
+ }else{
+ iKey2 = iKey1;
+ }
+
+ assert( pCsr->nField==pTab->nCol
+ || (pCsr->nField==pTab->nCol+1 && op==SQLITE_DELETE && iReg==-1)
+ );
+
+ preupdate.v = v;
+ preupdate.pCsr = pCsr;
+ preupdate.op = op;
+ preupdate.iNewReg = iReg;
+ preupdate.keyinfo.db = db;
+ preupdate.keyinfo.enc = ENC(db);
+ preupdate.keyinfo.nField = pTab->nCol;
+ preupdate.keyinfo.aSortOrder = (u8*)&fakeSortOrder;
+ preupdate.iKey1 = iKey1;
+ preupdate.iKey2 = iKey2;
+ preupdate.iPKey = pTab->iPKey;
+
+ db->pPreUpdate = &preupdate;
+ db->xPreUpdateCallback(db->pPreUpdateArg, db, op, zDb, zTbl, iKey1, iKey2);
+ db->pPreUpdate = 0;
+ sqlite3DbFree(db, preupdate.aRecord);
+ vdbeFreeUnpacked(db, preupdate.pUnpacked);
+ vdbeFreeUnpacked(db, preupdate.pNewUnpacked);
+ if( preupdate.aNew ){
+ int i;
+ for(i=0; i<pCsr->nField; i++){
+ sqlite3VdbeMemRelease(&preupdate.aNew[i]);
+ }
+ sqlite3DbFree(db, preupdate.aNew);
+ }
+}
+#endif /* SQLITE_ENABLE_PREUPDATE_HOOK */
+
/************** End of vdbeaux.c *********************************************/
/************** Begin file vdbeapi.c *****************************************/
/*
@@ -68990,6 +74550,8 @@ SQLITE_PRIVATE void sqlite3VtabImportErrmsg(Vdbe *p, sqlite3_vtab *pVtab){
** This file contains code use to implement APIs that are part of the
** VDBE.
*/
+/* #include "sqliteInt.h" */
+/* #include "vdbeInt.h" */
#ifndef SQLITE_OMIT_DEPRECATED
/*
@@ -69028,6 +74590,31 @@ static int vdbeSafetyNotNull(Vdbe *p){
}
}
+#ifndef SQLITE_OMIT_TRACE
+/*
+** Invoke the profile callback. This routine is only called if we already
+** know that the profile callback is defined and needs to be invoked.
+*/
+static SQLITE_NOINLINE void invokeProfileCallback(sqlite3 *db, Vdbe *p){
+ sqlite3_int64 iNow;
+ assert( p->startTime>0 );
+ assert( db->xProfile!=0 );
+ assert( db->init.busy==0 );
+ assert( p->zSql!=0 );
+ sqlite3OsCurrentTimeInt64(db->pVfs, &iNow);
+ db->xProfile(db->pProfileArg, p->zSql, (iNow - p->startTime)*1000000);
+ p->startTime = 0;
+}
+/*
+** The checkProfileCallback(DB,P) macro checks to see if a profile callback
+** is needed, and it invokes the callback if it is needed.
+*/
+# define checkProfileCallback(DB,P) \
+ if( ((P)->startTime)>0 ){ invokeProfileCallback(DB,P); }
+#else
+# define checkProfileCallback(DB,P) /*no-op*/
+#endif
+
/*
** The following routine destroys a virtual machine that is created by
** the sqlite3_compile() routine. The integer returned is an SQLITE_
@@ -69048,6 +74635,7 @@ SQLITE_API int SQLITE_STDCALL sqlite3_finalize(sqlite3_stmt *pStmt){
sqlite3 *db = v->db;
if( vdbeSafety(v) ) return SQLITE_MISUSE_BKPT;
sqlite3_mutex_enter(db->mutex);
+ checkProfileCallback(db, v);
rc = sqlite3VdbeFinalize(v);
rc = sqlite3ApiExit(db, rc);
sqlite3LeaveMutexAndCloseZombie(db);
@@ -69069,12 +74657,14 @@ SQLITE_API int SQLITE_STDCALL sqlite3_reset(sqlite3_stmt *pStmt){
rc = SQLITE_OK;
}else{
Vdbe *v = (Vdbe*)pStmt;
- sqlite3_mutex_enter(v->db->mutex);
+ sqlite3 *db = v->db;
+ sqlite3_mutex_enter(db->mutex);
+ checkProfileCallback(db, v);
rc = sqlite3VdbeReset(v);
sqlite3VdbeRewind(v);
- assert( (rc & (v->db->errMask))==rc );
- rc = sqlite3ApiExit(v->db, rc);
- sqlite3_mutex_leave(v->db->mutex);
+ assert( (rc & (db->errMask))==rc );
+ rc = sqlite3ApiExit(db, rc);
+ sqlite3_mutex_leave(db->mutex);
}
return rc;
}
@@ -69109,7 +74699,10 @@ SQLITE_API int SQLITE_STDCALL sqlite3_clear_bindings(sqlite3_stmt *pStmt){
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);
+ if( sqlite3VdbeMemExpandBlob(p)!=SQLITE_OK ){
+ assert( p->flags==MEM_Null && p->z==0 );
+ return 0;
+ }
p->flags |= MEM_Blob;
return p->n ? p->z : 0;
}else{
@@ -69131,6 +74724,10 @@ SQLITE_API int SQLITE_STDCALL sqlite3_value_int(sqlite3_value *pVal){
SQLITE_API sqlite_int64 SQLITE_STDCALL sqlite3_value_int64(sqlite3_value *pVal){
return sqlite3VdbeIntValue((Mem*)pVal);
}
+SQLITE_API unsigned int SQLITE_STDCALL sqlite3_value_subtype(sqlite3_value *pVal){
+ Mem *pMem = (Mem*)pVal;
+ return ((pMem->flags & MEM_Subtype) ? pMem->eSubtype : 0);
+}
SQLITE_API const unsigned char *SQLITE_STDCALL sqlite3_value_text(sqlite3_value *pVal){
return (const unsigned char *)sqlite3ValueText(pVal, SQLITE_UTF8);
}
@@ -69187,6 +74784,36 @@ SQLITE_API int SQLITE_STDCALL sqlite3_value_type(sqlite3_value* pVal){
return aType[pVal->flags&MEM_AffMask];
}
+/* Make a copy of an sqlite3_value object
+*/
+SQLITE_API sqlite3_value *SQLITE_STDCALL sqlite3_value_dup(const sqlite3_value *pOrig){
+ sqlite3_value *pNew;
+ if( pOrig==0 ) return 0;
+ pNew = sqlite3_malloc( sizeof(*pNew) );
+ if( pNew==0 ) return 0;
+ memset(pNew, 0, sizeof(*pNew));
+ memcpy(pNew, pOrig, MEMCELLSIZE);
+ pNew->flags &= ~MEM_Dyn;
+ pNew->db = 0;
+ if( pNew->flags&(MEM_Str|MEM_Blob) ){
+ pNew->flags &= ~(MEM_Static|MEM_Dyn);
+ pNew->flags |= MEM_Ephem;
+ if( sqlite3VdbeMemMakeWriteable(pNew)!=SQLITE_OK ){
+ sqlite3ValueFree(pNew);
+ pNew = 0;
+ }
+ }
+ return pNew;
+}
+
+/* Destroy an sqlite3_value object previously obtained from
+** sqlite3_value_dup().
+*/
+SQLITE_API void SQLITE_STDCALL sqlite3_value_free(sqlite3_value *pOld){
+ sqlite3ValueFree(pOld);
+}
+
+
/**************************** sqlite3_result_ *******************************
** The following routines are used by user-defined functions to specify
** the function result.
@@ -69279,6 +74906,12 @@ 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 SQLITE_STDCALL sqlite3_result_subtype(sqlite3_context *pCtx, unsigned int eSubtype){
+ Mem *pOut = pCtx->pOut;
+ assert( sqlite3_mutex_held(pOut->db->mutex) );
+ pOut->eSubtype = eSubtype & 0xff;
+ pOut->flags |= MEM_Subtype;
+}
SQLITE_API void SQLITE_STDCALL sqlite3_result_text(
sqlite3_context *pCtx,
const char *z,
@@ -69341,6 +74974,15 @@ SQLITE_API void SQLITE_STDCALL sqlite3_result_zeroblob(sqlite3_context *pCtx, in
assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
sqlite3VdbeMemSetZeroBlob(pCtx->pOut, n);
}
+SQLITE_API int SQLITE_STDCALL sqlite3_result_zeroblob64(sqlite3_context *pCtx, u64 n){
+ Mem *pOut = pCtx->pOut;
+ assert( sqlite3_mutex_held(pOut->db->mutex) );
+ if( n>(u64)pOut->db->aLimit[SQLITE_LIMIT_LENGTH] ){
+ return SQLITE_TOOBIG;
+ }
+ sqlite3VdbeMemSetZeroBlob(pCtx->pOut, (int)n);
+ return SQLITE_OK;
+}
SQLITE_API void SQLITE_STDCALL sqlite3_result_error_code(sqlite3_context *pCtx, int errCode){
pCtx->isError = errCode;
pCtx->fErrorOrAux = 1;
@@ -69366,9 +75008,9 @@ SQLITE_API void SQLITE_STDCALL sqlite3_result_error_toobig(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;
+ pCtx->isError = SQLITE_NOMEM_BKPT;
pCtx->fErrorOrAux = 1;
- pCtx->pOut->db->mallocFailed = 1;
+ sqlite3OomFault(pCtx->pOut->db);
}
/*
@@ -69395,6 +75037,7 @@ static int doWalCallbacks(sqlite3 *db){
return rc;
}
+
/*
** Execute the statement pStmt, either until a row of data is ready, the
** statement is completely executed or an error occurs.
@@ -69441,7 +75084,7 @@ static int sqlite3Step(Vdbe *p){
db = p->db;
if( db->mallocFailed ){
p->rc = SQLITE_NOMEM;
- return SQLITE_NOMEM;
+ return SQLITE_NOMEM_BKPT;
}
if( p->pc<=0 && p->expired ){
@@ -69463,8 +75106,10 @@ static int sqlite3Step(Vdbe *p){
);
#ifndef SQLITE_OMIT_TRACE
- if( db->xProfile && !db->init.busy ){
+ if( db->xProfile && !db->init.busy && p->zSql ){
sqlite3OsCurrentTimeInt64(db->pVfs, &p->startTime);
+ }else{
+ assert( p->startTime==0 );
}
#endif
@@ -69488,13 +75133,8 @@ static int sqlite3Step(Vdbe *p){
}
#ifndef SQLITE_OMIT_TRACE
- /* Invoke the profile callback if there is one
- */
- if( rc!=SQLITE_ROW && db->xProfile && !db->init.busy && p->zSql ){
- sqlite3_int64 iNow;
- sqlite3OsCurrentTimeInt64(db->pVfs, &iNow);
- db->xProfile(db->pProfileArg, p->zSql, (iNow - p->startTime)*1000000);
- }
+ /* If the statement completed successfully, invoke the profile callback */
+ if( rc!=SQLITE_ROW ) checkProfileCallback(db, p);
#endif
if( rc==SQLITE_DONE ){
@@ -69507,7 +75147,7 @@ static int sqlite3Step(Vdbe *p){
db->errCode = rc;
if( SQLITE_NOMEM==sqlite3ApiExit(p->db, p->rc) ){
- p->rc = SQLITE_NOMEM;
+ p->rc = SQLITE_NOMEM_BKPT;
}
end_of_step:
/* At this point local variable rc holds the value that should be
@@ -69518,7 +75158,7 @@ end_of_step:
** were called on statement p.
*/
assert( rc==SQLITE_ROW || rc==SQLITE_DONE || rc==SQLITE_ERROR
- || rc==SQLITE_BUSY || rc==SQLITE_MISUSE
+ || (rc&0xff)==SQLITE_BUSY || rc==SQLITE_MISUSE
);
assert( (p->rc!=SQLITE_ROW && p->rc!=SQLITE_DONE) || p->rc==p->rcApp );
if( p->isPrepareV2 && rc!=SQLITE_ROW && rc!=SQLITE_DONE ){
@@ -69574,7 +75214,7 @@ SQLITE_API int SQLITE_STDCALL sqlite3_step(sqlite3_stmt *pStmt){
v->rc = rc2;
} else {
v->zErrMsg = 0;
- v->rc = rc = SQLITE_NOMEM;
+ v->rc = rc = SQLITE_NOMEM_BKPT;
}
}
rc = sqlite3ApiExit(db, rc);
@@ -69603,7 +75243,7 @@ SQLITE_API void *SQLITE_STDCALL sqlite3_user_data(sqlite3_context *p){
** application defined function.
*/
SQLITE_API sqlite3 *SQLITE_STDCALL sqlite3_context_db_handle(sqlite3_context *p){
- assert( p && p->pFunc );
+ assert( p && p->pOut );
return p->pOut->db;
}
@@ -69679,7 +75319,7 @@ static SQLITE_NOINLINE void *createAggContext(sqlite3_context *p, int nByte){
** same context that was returned on prior calls.
*/
SQLITE_API void *SQLITE_STDCALL sqlite3_aggregate_context(sqlite3_context *p, int nByte){
- assert( p && p->pFunc && p->pFunc->xStep );
+ assert( p && p->pFunc && p->pFunc->xFinalize );
assert( sqlite3_mutex_held(p->pOut->db->mutex) );
testcase( nByte<0 );
if( (p->pMem->flags & MEM_Agg)==0 ){
@@ -69770,7 +75410,7 @@ failed:
** context.
*/
SQLITE_API int SQLITE_STDCALL sqlite3_aggregate_count(sqlite3_context *p){
- assert( p && p->pMem && p->pFunc && p->pFunc->xStep );
+ assert( p && p->pMem && p->pFunc && p->pFunc->xFinalize );
return p->pMem->n;
}
#endif
@@ -69812,18 +75452,19 @@ static const Mem *columnNullValue(void){
#endif
= {
/* .u = */ {0},
- /* .flags = */ MEM_Null,
- /* .enc = */ 0,
- /* .n = */ 0,
- /* .z = */ 0,
- /* .zMalloc = */ 0,
- /* .szMalloc = */ 0,
- /* .iPadding1 = */ 0,
- /* .db = */ 0,
- /* .xDel = */ 0,
+ /* .flags = */ (u16)MEM_Null,
+ /* .enc = */ (u8)0,
+ /* .eSubtype = */ (u8)0,
+ /* .n = */ (int)0,
+ /* .z = */ (char*)0,
+ /* .zMalloc = */ (char*)0,
+ /* .szMalloc = */ (int)0,
+ /* .uTemp = */ (u32)0,
+ /* .db = */ (sqlite3*)0,
+ /* .xDel = */ (void(*)(void*))0,
#ifdef SQLITE_DEBUG
- /* .pScopyFrom = */ 0,
- /* .pFiller = */ 0,
+ /* .pScopyFrom = */ (Mem*)0,
+ /* .pFiller = */ (void*)0,
#endif
};
return &nullMem;
@@ -69997,7 +75638,7 @@ static const void *columnName(
** is the case, clear the mallocFailed flag and return NULL.
*/
if( db->mallocFailed ){
- db->mallocFailed = 0;
+ sqlite3OomClear(db);
ret = 0;
}
sqlite3_mutex_leave(db->mutex);
@@ -70197,6 +75838,9 @@ SQLITE_API int SQLITE_STDCALL sqlite3_bind_blob(
int nData,
void (*xDel)(void*)
){
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( nData<0 ) return SQLITE_MISUSE_BKPT;
+#endif
return bindText(pStmt, i, zData, nData, xDel, 0);
}
SQLITE_API int SQLITE_STDCALL sqlite3_bind_blob64(
@@ -70322,6 +75966,20 @@ SQLITE_API int SQLITE_STDCALL sqlite3_bind_zeroblob(sqlite3_stmt *pStmt, int i,
}
return rc;
}
+SQLITE_API int SQLITE_STDCALL sqlite3_bind_zeroblob64(sqlite3_stmt *pStmt, int i, sqlite3_uint64 n){
+ int rc;
+ Vdbe *p = (Vdbe *)pStmt;
+ sqlite3_mutex_enter(p->db->mutex);
+ if( n>(u64)p->db->aLimit[SQLITE_LIMIT_LENGTH] ){
+ rc = SQLITE_TOOBIG;
+ }else{
+ assert( (n & 0x7FFFFFFF)==n );
+ rc = sqlite3_bind_zeroblob(pStmt, i, n);
+ }
+ rc = sqlite3ApiExit(p->db, rc);
+ sqlite3_mutex_leave(p->db->mutex);
+ return rc;
+}
/*
** Return the number of wildcards that can be potentially bound to.
@@ -70483,6 +76141,187 @@ SQLITE_API int SQLITE_STDCALL sqlite3_stmt_status(sqlite3_stmt *pStmt, int op, i
return (int)v;
}
+#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
+/*
+** Allocate and populate an UnpackedRecord structure based on the serialized
+** record in nKey/pKey. Return a pointer to the new UnpackedRecord structure
+** if successful, or a NULL pointer if an OOM error is encountered.
+*/
+static UnpackedRecord *vdbeUnpackRecord(
+ KeyInfo *pKeyInfo,
+ int nKey,
+ const void *pKey
+){
+ char *dummy; /* Dummy argument for AllocUnpackedRecord() */
+ UnpackedRecord *pRet; /* Return value */
+
+ pRet = sqlite3VdbeAllocUnpackedRecord(pKeyInfo, 0, 0, &dummy);
+ if( pRet ){
+ memset(pRet->aMem, 0, sizeof(Mem)*(pKeyInfo->nField+1));
+ sqlite3VdbeRecordUnpack(pKeyInfo, nKey, pKey, pRet);
+ }
+ return pRet;
+}
+
+/*
+** This function is called from within a pre-update callback to retrieve
+** a field of the row currently being updated or deleted.
+*/
+SQLITE_API int SQLITE_STDCALL sqlite3_preupdate_old(sqlite3 *db, int iIdx, sqlite3_value **ppValue){
+ PreUpdate *p = db->pPreUpdate;
+ int rc = SQLITE_OK;
+
+ /* Test that this call is being made from within an SQLITE_DELETE or
+ ** SQLITE_UPDATE pre-update callback, and that iIdx is within range. */
+ if( !p || p->op==SQLITE_INSERT ){
+ rc = SQLITE_MISUSE_BKPT;
+ goto preupdate_old_out;
+ }
+ if( iIdx>=p->pCsr->nField || iIdx<0 ){
+ rc = SQLITE_RANGE;
+ goto preupdate_old_out;
+ }
+
+ /* If the old.* record has not yet been loaded into memory, do so now. */
+ if( p->pUnpacked==0 ){
+ u32 nRec;
+ u8 *aRec;
+
+ rc = sqlite3BtreeDataSize(p->pCsr->uc.pCursor, &nRec);
+ if( rc!=SQLITE_OK ) goto preupdate_old_out;
+ aRec = sqlite3DbMallocRaw(db, nRec);
+ if( !aRec ) goto preupdate_old_out;
+ rc = sqlite3BtreeData(p->pCsr->uc.pCursor, 0, nRec, aRec);
+ if( rc==SQLITE_OK ){
+ p->pUnpacked = vdbeUnpackRecord(&p->keyinfo, nRec, aRec);
+ if( !p->pUnpacked ) rc = SQLITE_NOMEM;
+ }
+ if( rc!=SQLITE_OK ){
+ sqlite3DbFree(db, aRec);
+ goto preupdate_old_out;
+ }
+ p->aRecord = aRec;
+ }
+
+ if( iIdx>=p->pUnpacked->nField ){
+ *ppValue = (sqlite3_value *)columnNullValue();
+ }else{
+ *ppValue = &p->pUnpacked->aMem[iIdx];
+ if( iIdx==p->iPKey ){
+ sqlite3VdbeMemSetInt64(*ppValue, p->iKey1);
+ }
+ }
+
+ preupdate_old_out:
+ sqlite3Error(db, rc);
+ return sqlite3ApiExit(db, rc);
+}
+#endif /* SQLITE_ENABLE_PREUPDATE_HOOK */
+
+#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
+/*
+** This function is called from within a pre-update callback to retrieve
+** the number of columns in the row being updated, deleted or inserted.
+*/
+SQLITE_API int SQLITE_STDCALL sqlite3_preupdate_count(sqlite3 *db){
+ PreUpdate *p = db->pPreUpdate;
+ return (p ? p->keyinfo.nField : 0);
+}
+#endif /* SQLITE_ENABLE_PREUPDATE_HOOK */
+
+#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
+/*
+** This function is designed to be called from within a pre-update callback
+** only. It returns zero if the change that caused the callback was made
+** immediately by a user SQL statement. Or, if the change was made by a
+** trigger program, it returns the number of trigger programs currently
+** on the stack (1 for a top-level trigger, 2 for a trigger fired by a
+** top-level trigger etc.).
+**
+** For the purposes of the previous paragraph, a foreign key CASCADE, SET NULL
+** or SET DEFAULT action is considered a trigger.
+*/
+SQLITE_API int SQLITE_STDCALL sqlite3_preupdate_depth(sqlite3 *db){
+ PreUpdate *p = db->pPreUpdate;
+ return (p ? p->v->nFrame : 0);
+}
+#endif /* SQLITE_ENABLE_PREUPDATE_HOOK */
+
+#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
+/*
+** This function is called from within a pre-update callback to retrieve
+** a field of the row currently being updated or inserted.
+*/
+SQLITE_API int SQLITE_STDCALL sqlite3_preupdate_new(sqlite3 *db, int iIdx, sqlite3_value **ppValue){
+ PreUpdate *p = db->pPreUpdate;
+ int rc = SQLITE_OK;
+ Mem *pMem;
+
+ if( !p || p->op==SQLITE_DELETE ){
+ rc = SQLITE_MISUSE_BKPT;
+ goto preupdate_new_out;
+ }
+ if( iIdx>=p->pCsr->nField || iIdx<0 ){
+ rc = SQLITE_RANGE;
+ goto preupdate_new_out;
+ }
+
+ if( p->op==SQLITE_INSERT ){
+ /* For an INSERT, memory cell p->iNewReg contains the serialized record
+ ** that is being inserted. Deserialize it. */
+ UnpackedRecord *pUnpack = p->pNewUnpacked;
+ if( !pUnpack ){
+ Mem *pData = &p->v->aMem[p->iNewReg];
+ rc = sqlite3VdbeMemExpandBlob(pData);
+ if( rc!=SQLITE_OK ) goto preupdate_new_out;
+ pUnpack = vdbeUnpackRecord(&p->keyinfo, pData->n, pData->z);
+ if( !pUnpack ){
+ rc = SQLITE_NOMEM;
+ goto preupdate_new_out;
+ }
+ p->pNewUnpacked = pUnpack;
+ }
+ if( iIdx>=pUnpack->nField ){
+ pMem = (sqlite3_value *)columnNullValue();
+ }else{
+ pMem = &pUnpack->aMem[iIdx];
+ if( iIdx==p->iPKey ){
+ sqlite3VdbeMemSetInt64(pMem, p->iKey2);
+ }
+ }
+ }else{
+ /* For an UPDATE, memory cell (p->iNewReg+1+iIdx) contains the required
+ ** value. Make a copy of the cell contents and return a pointer to it.
+ ** It is not safe to return a pointer to the memory cell itself as the
+ ** caller may modify the value text encoding.
+ */
+ assert( p->op==SQLITE_UPDATE );
+ if( !p->aNew ){
+ p->aNew = (Mem *)sqlite3DbMallocZero(db, sizeof(Mem) * p->pCsr->nField);
+ if( !p->aNew ){
+ rc = SQLITE_NOMEM;
+ goto preupdate_new_out;
+ }
+ }
+ assert( iIdx>=0 && iIdx<p->pCsr->nField );
+ pMem = &p->aNew[iIdx];
+ if( pMem->flags==0 ){
+ if( iIdx==p->iPKey ){
+ sqlite3VdbeMemSetInt64(pMem, p->iKey2);
+ }else{
+ rc = sqlite3VdbeMemCopy(pMem, &p->v->aMem[p->iNewReg+1+iIdx]);
+ if( rc!=SQLITE_OK ) goto preupdate_new_out;
+ }
+ }
+ }
+ *ppValue = pMem;
+
+ preupdate_new_out:
+ sqlite3Error(db, rc);
+ return sqlite3ApiExit(db, rc);
+}
+#endif /* SQLITE_ENABLE_PREUPDATE_HOOK */
+
#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
/*
** Return status data for a single loop within query pStmt.
@@ -70571,6 +76410,8 @@ SQLITE_API void SQLITE_STDCALL sqlite3_stmt_scanstatus_reset(sqlite3_stmt *pStmt
**
** The Vdbe parse-tree explainer is also found here.
*/
+/* #include "sqliteInt.h" */
+/* #include "vdbeInt.h" */
#ifndef SQLITE_OMIT_TRACE
@@ -70682,9 +76523,9 @@ SQLITE_PRIVATE char *sqlite3VdbeExpandSql(
if( pVar->flags & MEM_Null ){
sqlite3StrAccumAppend(&out, "NULL", 4);
}else if( pVar->flags & MEM_Int ){
- sqlite3XPrintf(&out, 0, "%lld", pVar->u.i);
+ sqlite3XPrintf(&out, "%lld", pVar->u.i);
}else if( pVar->flags & MEM_Real ){
- sqlite3XPrintf(&out, 0, "%!.15g", pVar->u.r);
+ sqlite3XPrintf(&out, "%!.15g", pVar->u.r);
}else if( pVar->flags & MEM_Str ){
int nOut; /* Number of bytes of the string text to include in output */
#ifndef SQLITE_OMIT_UTF16
@@ -70705,17 +76546,17 @@ SQLITE_PRIVATE char *sqlite3VdbeExpandSql(
while( nOut<pVar->n && (pVar->z[nOut]&0xc0)==0x80 ){ nOut++; }
}
#endif
- sqlite3XPrintf(&out, 0, "'%.*q'", nOut, pVar->z);
+ sqlite3XPrintf(&out, "'%.*q'", nOut, pVar->z);
#ifdef SQLITE_TRACE_SIZE_LIMIT
if( nOut<pVar->n ){
- sqlite3XPrintf(&out, 0, "/*+%d bytes*/", pVar->n-nOut);
+ sqlite3XPrintf(&out, "/*+%d bytes*/", pVar->n-nOut);
}
#endif
#ifndef SQLITE_OMIT_UTF16
if( enc!=SQLITE_UTF8 ) sqlite3VdbeMemRelease(&utf8);
#endif
}else if( pVar->flags & MEM_Zero ){
- sqlite3XPrintf(&out, 0, "zeroblob(%d)", pVar->u.nZero);
+ sqlite3XPrintf(&out, "zeroblob(%d)", pVar->u.nZero);
}else{
int nOut; /* Number of bytes of the blob to include in output */
assert( pVar->flags & MEM_Blob );
@@ -70725,12 +76566,12 @@ SQLITE_PRIVATE char *sqlite3VdbeExpandSql(
if( nOut>SQLITE_TRACE_SIZE_LIMIT ) nOut = SQLITE_TRACE_SIZE_LIMIT;
#endif
for(i=0; i<nOut; i++){
- sqlite3XPrintf(&out, 0, "%02x", pVar->z[i]&0xff);
+ sqlite3XPrintf(&out, "%02x", pVar->z[i]&0xff);
}
sqlite3StrAccumAppend(&out, "'", 1);
#ifdef SQLITE_TRACE_SIZE_LIMIT
if( nOut<pVar->n ){
- sqlite3XPrintf(&out, 0, "/*+%d bytes*/", pVar->n-nOut);
+ sqlite3XPrintf(&out, "/*+%d bytes*/", pVar->n-nOut);
}
#endif
}
@@ -70763,6 +76604,8 @@ SQLITE_PRIVATE char *sqlite3VdbeExpandSql(
** in this file for details. If in doubt, do not deviate from existing
** commenting and indentation practices when changing or adding code.
*/
+/* #include "sqliteInt.h" */
+/* #include "vdbeInt.h" */
/*
** Invoke this macro on memory cells just prior to changing the
@@ -70830,6 +76673,16 @@ static void updateMaxBlobsize(Mem *p){
#endif
/*
+** This macro evaluates to true if either the update hook or the preupdate
+** hook are enabled for database connect DB.
+*/
+#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
+# define HAS_UPDATE_HOOK(DB) ((DB)->xPreUpdateCallback||(DB)->xUpdateCallback)
+#else
+# define HAS_UPDATE_HOOK(DB) ((DB)->xUpdateCallback)
+#endif
+
+/*
** The next global variable is incremented each time the OP_Found opcode
** is executed. This is used to test whether or not the foreign key
** operation implemented using OP_FkIsZero is working. This variable
@@ -70908,7 +76761,7 @@ SQLITE_API int sqlite3_found_count = 0;
&& sqlite3VdbeMemMakeWriteable(P) ){ goto no_mem;}
/* Return true if the cursor was opened using the OP_OpenSorter opcode. */
-#define isSorter(x) ((x)->pSorter!=0)
+#define isSorter(x) ((x)->eCurType==CURTYPE_SORTER)
/*
** Allocate VdbeCursor number iCur. Return a pointer to it. Return NULL
@@ -70919,7 +76772,7 @@ static VdbeCursor *allocateCursor(
int iCur, /* Index of the new VdbeCursor */
int nField, /* Number of fields in the table or index */
int iDb, /* Database the cursor belongs to, or -1 */
- int isBtreeCursor /* True for B-Tree. False for pseudo-table or vtab */
+ u8 eCurType /* Type of the new cursor */
){
/* Find the memory cell that will be used to store the blob of memory
** required for this VdbeCursor structure. It is convenient to use a
@@ -70935,33 +76788,34 @@ static VdbeCursor *allocateCursor(
** be freed lazily via the sqlite3_release_memory() API. This
** minimizes the number of malloc calls made by the system.
**
- ** Memory cells for cursors are allocated at the top of the address
- ** space. Memory cell (p->nMem) corresponds to cursor 0. Space for
- ** cursor 1 is managed by memory cell (p->nMem-1), etc.
+ ** The memory cell for cursor 0 is aMem[0]. The rest are allocated from
+ ** the top of the register space. Cursor 1 is at Mem[p->nMem-1].
+ ** Cursor 2 is at Mem[p->nMem-2]. And so forth.
*/
- Mem *pMem = &p->aMem[p->nMem-iCur];
+ Mem *pMem = iCur>0 ? &p->aMem[p->nMem-iCur] : p->aMem;
int nByte;
VdbeCursor *pCx = 0;
nByte =
ROUND8(sizeof(VdbeCursor)) + 2*sizeof(u32)*nField +
- (isBtreeCursor?sqlite3BtreeCursorSize():0);
+ (eCurType==CURTYPE_BTREE?sqlite3BtreeCursorSize():0);
- assert( iCur<p->nCursor );
- if( p->apCsr[iCur] ){
+ assert( iCur>=0 && iCur<p->nCursor );
+ if( p->apCsr[iCur] ){ /*OPTIMIZATION-IF-FALSE*/
sqlite3VdbeFreeCursor(p, p->apCsr[iCur]);
p->apCsr[iCur] = 0;
}
if( SQLITE_OK==sqlite3VdbeMemClearAndResize(pMem, nByte) ){
p->apCsr[iCur] = pCx = (VdbeCursor*)pMem->z;
memset(pCx, 0, sizeof(VdbeCursor));
+ pCx->eCurType = eCurType;
pCx->iDb = iDb;
pCx->nField = nField;
pCx->aOffset = &pCx->aType[nField];
- if( isBtreeCursor ){
- pCx->pCursor = (BtCursor*)
+ if( eCurType==CURTYPE_BTREE ){
+ pCx->uc.pCursor = (BtCursor*)
&pMem->z[ROUND8(sizeof(VdbeCursor))+2*sizeof(u32)*nField];
- sqlite3BtreeCursorZero(pCx->pCursor);
+ sqlite3BtreeCursorZero(pCx->uc.pCursor);
}
}
return pCx;
@@ -71013,7 +76867,7 @@ static void applyNumericAffinity(Mem *pRec, int bTryForInt){
** SQLITE_AFF_TEXT:
** Convert pRec to a text representation.
**
-** SQLITE_AFF_NONE:
+** SQLITE_AFF_BLOB:
** No-op. pRec is unchanged.
*/
static void applyAffinity(
@@ -71024,7 +76878,7 @@ static void applyAffinity(
if( affinity>=SQLITE_AFF_NUMERIC ){
assert( affinity==SQLITE_AFF_INTEGER || affinity==SQLITE_AFF_REAL
|| affinity==SQLITE_AFF_NUMERIC );
- if( (pRec->flags & MEM_Int)==0 ){
+ if( (pRec->flags & MEM_Int)==0 ){ /*OPTIMIZATION-IF-FALSE*/
if( (pRec->flags & MEM_Real)==0 ){
if( pRec->flags & MEM_Str ) applyNumericAffinity(pRec,1);
}else{
@@ -71034,10 +76888,13 @@ static void applyAffinity(
}else if( affinity==SQLITE_AFF_TEXT ){
/* Only attempt the conversion to TEXT if there is an integer or real
** representation (blob and NULL do not get converted) but no string
- ** representation.
- */
- if( 0==(pRec->flags&MEM_Str) && (pRec->flags&(MEM_Real|MEM_Int)) ){
- sqlite3VdbeMemStringify(pRec, enc, 1);
+ ** representation. It would be harmless to repeat the conversion if
+ ** there is already a string rep, but it is pointless to waste those
+ ** CPU cycles. */
+ if( 0==(pRec->flags&MEM_Str) ){ /*OPTIMIZATION-IF-FALSE*/
+ if( (pRec->flags&(MEM_Real|MEM_Int)) ){
+ sqlite3VdbeMemStringify(pRec, enc, 1);
+ }
}
pRec->flags &= ~(MEM_Real|MEM_Int);
}
@@ -71213,6 +77070,7 @@ static void memTracePrint(Mem *p){
sqlite3VdbeMemPrettyPrint(p, zBuf);
printf(" %s", zBuf);
}
+ if( p->flags & MEM_Subtype ) printf(" subtype=0x%02x", p->eSubtype);
}
static void registerTrace(int iReg, Mem *p){
printf("REG[%d] = ", iReg);
@@ -71350,16 +77208,24 @@ static int checkSavepointCount(sqlite3 *db){
/*
** Return the register of pOp->p2 after first preparing it to be
** overwritten with an integer value.
-*/
+*/
+static SQLITE_NOINLINE Mem *out2PrereleaseWithClear(Mem *pOut){
+ sqlite3VdbeMemSetNull(pOut);
+ pOut->flags = MEM_Int;
+ return pOut;
+}
static Mem *out2Prerelease(Vdbe *p, VdbeOp *pOp){
Mem *pOut;
assert( pOp->p2>0 );
- assert( pOp->p2<=(p->nMem-p->nCursor) );
+ assert( pOp->p2<=(p->nMem+1 - p->nCursor) );
pOut = &p->aMem[pOp->p2];
memAboutToChange(p, pOut);
- if( VdbeMemDynamic(pOut) ) sqlite3VdbeMemSetNull(pOut);
- pOut->flags = MEM_Int;
- return pOut;
+ if( VdbeMemDynamic(pOut) ){ /*OPTIMIZATION-IF-FALSE*/
+ return out2PrereleaseWithClear(pOut);
+ }else{
+ pOut->flags = MEM_Int;
+ return pOut;
+ }
}
@@ -71375,6 +77241,9 @@ SQLITE_PRIVATE int sqlite3VdbeExec(
#if defined(SQLITE_DEBUG) || defined(VDBE_PROFILE)
Op *pOrigOp; /* Value of pOp at the top of the loop */
#endif
+#ifdef SQLITE_DEBUG
+ int nExtraDelete = 0; /* Verifies FORDELETE and AUXDELETE flags */
+#endif
int rc = SQLITE_OK; /* Value to return */
sqlite3 *db = p->db; /* The database */
u8 resetSchemaOnFault = 0; /* Reset schema after an error if positive */
@@ -71403,7 +77272,7 @@ SQLITE_PRIVATE int sqlite3VdbeExec(
** sqlite3_column_text16() failed. */
goto no_mem;
}
- assert( p->rc==SQLITE_OK || p->rc==SQLITE_BUSY );
+ assert( p->rc==SQLITE_OK || (p->rc&0xff)==SQLITE_BUSY );
assert( p->bIsReader || p->readOnly!=0 );
p->rc = SQLITE_OK;
p->iCurrentTime = 0;
@@ -71414,13 +77283,9 @@ SQLITE_PRIVATE int sqlite3VdbeExec(
sqlite3VdbeIOTraceSql(p);
#ifndef SQLITE_OMIT_PROGRESS_CALLBACK
if( db->xProgress ){
+ u32 iPrior = p->aCounter[SQLITE_STMTSTATUS_VM_STEP];
assert( 0 < db->nProgressOps );
- nProgressLimit = (unsigned)p->aCounter[SQLITE_STMTSTATUS_VM_STEP];
- if( nProgressLimit==0 ){
- nProgressLimit = db->nProgressOps;
- }else{
- nProgressLimit %= (unsigned)db->nProgressOps;
- }
+ nProgressLimit = db->nProgressOps - (iPrior % db->nProgressOps);
}
#endif
#ifdef SQLITE_DEBUG
@@ -71450,9 +77315,12 @@ SQLITE_PRIVATE int sqlite3VdbeExec(
}
sqlite3EndBenignMalloc();
#endif
- for(pOp=&aOp[p->pc]; rc==SQLITE_OK; pOp++){
+ for(pOp=&aOp[p->pc]; 1; pOp++){
+ /* Errors are detected by individual opcodes, with an immediate
+ ** jumps to abort_due_to_error. */
+ assert( rc==SQLITE_OK );
+
assert( pOp>=aOp && pOp<&aOp[p->nOp]);
- if( db->mallocFailed ) goto no_mem;
#ifdef VDBE_PROFILE
start = sqlite3Hwtime();
#endif
@@ -71484,37 +77352,39 @@ SQLITE_PRIVATE int sqlite3VdbeExec(
/* 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) );
- assert( memIsValid(&aMem[pOp->p1]) );
- assert( sqlite3VdbeCheckMemInvariants(&aMem[pOp->p1]) );
- REGISTER_TRACE(pOp->p1, &aMem[pOp->p1]);
- }
- if( (pOp->opflags & OPFLG_IN2)!=0 ){
- assert( pOp->p2>0 );
- assert( pOp->p2<=(p->nMem-p->nCursor) );
- assert( memIsValid(&aMem[pOp->p2]) );
- assert( sqlite3VdbeCheckMemInvariants(&aMem[pOp->p2]) );
- REGISTER_TRACE(pOp->p2, &aMem[pOp->p2]);
- }
- if( (pOp->opflags & OPFLG_IN3)!=0 ){
- assert( pOp->p3>0 );
- assert( pOp->p3<=(p->nMem-p->nCursor) );
- assert( memIsValid(&aMem[pOp->p3]) );
- assert( sqlite3VdbeCheckMemInvariants(&aMem[pOp->p3]) );
- REGISTER_TRACE(pOp->p3, &aMem[pOp->p3]);
- }
- if( (pOp->opflags & OPFLG_OUT2)!=0 ){
- assert( pOp->p2>0 );
- assert( pOp->p2<=(p->nMem-p->nCursor) );
- memAboutToChange(p, &aMem[pOp->p2]);
- }
- if( (pOp->opflags & OPFLG_OUT3)!=0 ){
- assert( pOp->p3>0 );
- assert( pOp->p3<=(p->nMem-p->nCursor) );
- memAboutToChange(p, &aMem[pOp->p3]);
+ {
+ u8 opProperty = sqlite3OpcodeProperty[pOp->opcode];
+ if( (opProperty & OPFLG_IN1)!=0 ){
+ assert( pOp->p1>0 );
+ assert( pOp->p1<=(p->nMem+1 - p->nCursor) );
+ assert( memIsValid(&aMem[pOp->p1]) );
+ assert( sqlite3VdbeCheckMemInvariants(&aMem[pOp->p1]) );
+ REGISTER_TRACE(pOp->p1, &aMem[pOp->p1]);
+ }
+ if( (opProperty & OPFLG_IN2)!=0 ){
+ assert( pOp->p2>0 );
+ assert( pOp->p2<=(p->nMem+1 - p->nCursor) );
+ assert( memIsValid(&aMem[pOp->p2]) );
+ assert( sqlite3VdbeCheckMemInvariants(&aMem[pOp->p2]) );
+ REGISTER_TRACE(pOp->p2, &aMem[pOp->p2]);
+ }
+ if( (opProperty & OPFLG_IN3)!=0 ){
+ assert( pOp->p3>0 );
+ assert( pOp->p3<=(p->nMem+1 - p->nCursor) );
+ assert( memIsValid(&aMem[pOp->p3]) );
+ assert( sqlite3VdbeCheckMemInvariants(&aMem[pOp->p3]) );
+ REGISTER_TRACE(pOp->p3, &aMem[pOp->p3]);
+ }
+ if( (opProperty & OPFLG_OUT2)!=0 ){
+ assert( pOp->p2>0 );
+ assert( pOp->p2<=(p->nMem+1 - p->nCursor) );
+ memAboutToChange(p, &aMem[pOp->p2]);
+ }
+ if( (opProperty & OPFLG_OUT3)!=0 ){
+ assert( pOp->p3>0 );
+ assert( pOp->p3<=(p->nMem+1 - p->nCursor) );
+ memAboutToChange(p, &aMem[pOp->p3]);
+ }
}
#endif
#if defined(SQLITE_DEBUG) || defined(VDBE_PROFILE)
@@ -71598,7 +77468,7 @@ check_for_interrupt:
nProgressLimit = nVmStep + db->nProgressOps - (nVmStep%db->nProgressOps);
if( db->xProgress(db->pProgressArg) ){
rc = SQLITE_INTERRUPT;
- goto vdbe_error_halt;
+ goto abort_due_to_error;
}
}
#endif
@@ -71612,7 +77482,7 @@ check_for_interrupt:
** and then jump to address P2.
*/
case OP_Gosub: { /* jump */
- assert( pOp->p1>0 && pOp->p1<=(p->nMem-p->nCursor) );
+ assert( pOp->p1>0 && pOp->p1<=(p->nMem+1 - p->nCursor) );
pIn1 = &aMem[pOp->p1];
assert( VdbeMemDynamic(pIn1)==0 );
memAboutToChange(p, pIn1);
@@ -71652,7 +77522,7 @@ case OP_Return: { /* in1 */
** See also: EndCoroutine
*/
case OP_InitCoroutine: { /* jump */
- assert( pOp->p1>0 && pOp->p1<=(p->nMem-p->nCursor) );
+ assert( pOp->p1>0 && pOp->p1<=(p->nMem+1 - p->nCursor) );
assert( pOp->p2>=0 && pOp->p2<p->nOp );
assert( pOp->p3>=0 && pOp->p3<p->nOp );
pOut = &aMem[pOp->p1];
@@ -71754,8 +77624,6 @@ case OP_HaltIfNull: { /* in3 */
** is the same as executing Halt.
*/
case OP_Halt: {
- const char *zType;
- const char *zLogFmt;
VdbeFrame *pFrame;
int pcx;
@@ -71784,35 +77652,28 @@ case OP_Halt: {
p->rc = pOp->p1;
p->errorAction = (u8)pOp->p2;
p->pc = pcx;
+ assert( pOp->p5>=0 && pOp->p5<=4 );
if( p->rc ){
if( pOp->p5 ){
static const char * const azType[] = { "NOT NULL", "UNIQUE", "CHECK",
"FOREIGN KEY" };
- assert( pOp->p5>=1 && pOp->p5<=4 );
testcase( pOp->p5==1 );
testcase( pOp->p5==2 );
testcase( pOp->p5==3 );
testcase( pOp->p5==4 );
- zType = azType[pOp->p5-1];
- }else{
- zType = 0;
- }
- assert( zType!=0 || pOp->p4.z!=0 );
- zLogFmt = "abort at %d in [%s]: %s";
- if( zType && pOp->p4.z ){
- sqlite3SetString(&p->zErrMsg, db, "%s constraint failed: %s",
- zType, pOp->p4.z);
- }else if( pOp->p4.z ){
- sqlite3SetString(&p->zErrMsg, db, "%s", pOp->p4.z);
+ sqlite3VdbeError(p, "%s constraint failed", azType[pOp->p5-1]);
+ if( pOp->p4.z ){
+ p->zErrMsg = sqlite3MPrintf(db, "%z: %s", p->zErrMsg, pOp->p4.z);
+ }
}else{
- sqlite3SetString(&p->zErrMsg, db, "%s constraint failed", zType);
+ sqlite3VdbeError(p, "%s", pOp->p4.z);
}
- sqlite3_log(pOp->p1, zLogFmt, pcx, p->zSql, p->zErrMsg);
+ sqlite3_log(pOp->p1, "abort at %d in [%s]: %s", pcx, p->zSql, p->zErrMsg);
}
rc = sqlite3VdbeHalt(p);
assert( rc==SQLITE_BUSY || rc==SQLITE_OK || rc==SQLITE_ERROR );
if( rc==SQLITE_BUSY ){
- p->rc = rc = SQLITE_BUSY;
+ p->rc = SQLITE_BUSY;
}else{
assert( rc==SQLITE_OK || (p->rc&0xff)==SQLITE_CONSTRAINT );
assert( rc==SQLITE_OK || db->nDeferredCons>0 || db->nDeferredImmCons>0 );
@@ -71878,7 +77739,7 @@ case OP_String8: { /* same as TK_STRING, out2 */
#ifndef SQLITE_OMIT_UTF16
if( encoding!=SQLITE_UTF8 ){
rc = sqlite3VdbeMemSetStr(pOut, pOp->p4.z, -1, SQLITE_UTF8, SQLITE_STATIC);
- if( rc==SQLITE_TOOBIG ) goto too_big;
+ assert( rc==SQLITE_OK || rc==SQLITE_TOOBIG );
if( SQLITE_OK!=sqlite3VdbeChangeEncoding(pOut, encoding) ) goto no_mem;
assert( pOut->szMalloc>0 && pOut->zMalloc==pOut->z );
assert( VdbeMemDynamic(pOut)==0 );
@@ -71891,10 +77752,12 @@ case OP_String8: { /* same as TK_STRING, out2 */
pOp->p4.z = pOut->z;
pOp->p1 = pOut->n;
}
+ testcase( rc==SQLITE_TOOBIG );
#endif
if( pOp->p1>db->aLimit[SQLITE_LIMIT_LENGTH] ){
goto too_big;
}
+ assert( rc==SQLITE_OK );
/* Fall through to the next case, OP_String */
}
@@ -71903,10 +77766,12 @@ case OP_String8: { /* same as TK_STRING, out2 */
**
** 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
+** If P3 is not zero and the content of register P3 is equal to P5, 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.
+** of a string, as if it had been CAST. In other words:
+**
+** if( P3!=0 and reg[P3]==P5 ) reg[P2] := CAST(reg[P2] as BLOB)
*/
case OP_String: { /* out2 */
assert( pOp->p4.z!=0 );
@@ -71916,13 +77781,14 @@ case OP_String: { /* out2 */
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) );
+#ifndef SQLITE_LIKE_DOESNT_MATCH_BLOBS
+ if( pOp->p3>0 ){
+ assert( pOp->p3<=(p->nMem+1 - p->nCursor) );
pIn3 = &aMem[pOp->p3];
assert( pIn3->flags & MEM_Int );
- if( pIn3->u.i ) pOut->flags = MEM_Blob|MEM_Static|MEM_Term;
+ if( pIn3->u.i==pOp->p5 ) pOut->flags = MEM_Blob|MEM_Static|MEM_Term;
}
+#endif
break;
}
@@ -71943,7 +77809,7 @@ case OP_Null: { /* out2 */
u16 nullFlag;
pOut = out2Prerelease(p, pOp);
cnt = pOp->p3-pOp->p2;
- assert( pOp->p3<=(p->nMem-p->nCursor) );
+ assert( pOp->p3<=(p->nMem+1 - p->nCursor) );
pOut->flags = nullFlag = pOp->p1 ? (MEM_Null|MEM_Cleared) : MEM_Null;
while( cnt>0 ){
pOut++;
@@ -71964,7 +77830,7 @@ case OP_Null: { /* out2 */
** previously copied using OP_SCopy, the copies will continue to be valid.
*/
case OP_SoftNull: {
- assert( pOp->p1>0 && pOp->p1<=(p->nMem-p->nCursor) );
+ assert( pOp->p1>0 && pOp->p1<=(p->nMem+1 - p->nCursor) );
pOut = &aMem[pOp->p1];
pOut->flags = (pOut->flags|MEM_Null)&~MEM_Undefined;
break;
@@ -72031,8 +77897,8 @@ case OP_Move: {
pIn1 = &aMem[p1];
pOut = &aMem[p2];
do{
- assert( pOut<=&aMem[(p->nMem-p->nCursor)] );
- assert( pIn1<=&aMem[(p->nMem-p->nCursor)] );
+ assert( pOut<=&aMem[(p->nMem+1 - p->nCursor)] );
+ assert( pIn1<=&aMem[(p->nMem+1 - p->nCursor)] );
assert( memIsValid(pIn1) );
memAboutToChange(p, pOut);
sqlite3VdbeMemMove(pOut, pIn1);
@@ -72102,6 +77968,22 @@ case OP_SCopy: { /* out2 */
break;
}
+/* Opcode: IntCopy P1 P2 * * *
+** Synopsis: r[P2]=r[P1]
+**
+** Transfer the integer value held in register P1 into register P2.
+**
+** This is an optimized version of SCopy that works only for integer
+** values.
+*/
+case OP_IntCopy: { /* out2 */
+ pIn1 = &aMem[pOp->p1];
+ assert( (pIn1->flags & MEM_Int)!=0 );
+ pOut = &aMem[pOp->p2];
+ sqlite3VdbeMemSetInt64(pOut, pIn1->u.i);
+ break;
+}
+
/* Opcode: ResultRow P1 P2 * * *
** Synopsis: output=r[P1@P2]
**
@@ -72116,7 +77998,7 @@ case OP_ResultRow: {
int i;
assert( p->nResColumn==pOp->p2 );
assert( pOp->p1>0 );
- assert( pOp->p1+pOp->p2<=(p->nMem-p->nCursor)+1 );
+ assert( pOp->p1+pOp->p2<=(p->nMem+1 - p->nCursor)+1 );
#ifndef SQLITE_OMIT_PROGRESS_CALLBACK
/* Run the progress counter just before returning.
@@ -72126,7 +78008,7 @@ case OP_ResultRow: {
&& db->xProgress(db->pProgressArg)!=0
){
rc = SQLITE_INTERRUPT;
- goto vdbe_error_halt;
+ goto abort_due_to_error;
}
#endif
@@ -72136,7 +78018,7 @@ case OP_ResultRow: {
if( SQLITE_OK!=(rc = sqlite3VdbeCheckFk(p, 0)) ){
assert( db->flags&SQLITE_CountRows );
assert( p->usesStmtJournal );
- break;
+ goto abort_due_to_error;
}
/* If the SQLITE_CountRows flag is set in sqlite3.flags mask, then
@@ -72156,9 +78038,7 @@ case OP_ResultRow: {
*/
assert( p->iStatement==0 || db->flags&SQLITE_CountRows );
rc = sqlite3VdbeCloseStatement(p, SAVEPOINT_RELEASE);
- if( NEVER(rc!=SQLITE_OK) ){
- break;
- }
+ assert( rc==SQLITE_OK );
/* Invalidate all ephemeral cursor row caches */
p->cacheCtr = (p->cacheCtr + 2)|1;
@@ -72383,10 +78263,10 @@ case OP_CollSeq: {
break;
}
-/* Opcode: Function P1 P2 P3 P4 P5
+/* Opcode: Function0 P1 P2 P3 P4 P5
** Synopsis: r[P3]=func(r[P2@P5])
**
-** Invoke a user function (P4 is a pointer to a Function structure that
+** Invoke a user function (P4 is a pointer to a FuncDef object that
** defines the function) with P5 arguments taken from register P2 and
** successors. The result of the function is stored in register P3.
** Register P3 must not be one of the function inputs.
@@ -72398,59 +78278,101 @@ case OP_CollSeq: {
** sqlite3_set_auxdata() API may be safely retained until the next
** invocation of this opcode.
**
-** See also: AggStep and AggFinal
+** See also: Function, AggStep, AggFinal
*/
-case OP_Function: {
- int i;
- Mem *pArg;
- sqlite3_context ctx;
- sqlite3_value **apVal;
+/* Opcode: Function P1 P2 P3 P4 P5
+** Synopsis: r[P3]=func(r[P2@P5])
+**
+** Invoke a user function (P4 is a pointer to an sqlite3_context object that
+** contains a pointer to the function to be run) with P5 arguments taken
+** from register P2 and successors. The result of the function is stored
+** in register P3. Register P3 must not be one of the function inputs.
+**
+** P1 is a 32-bit bitmask indicating whether or not each argument to the
+** function was determined to be constant at compile time. If the first
+** argument was constant then bit 0 of P1 is set. This is used to determine
+** whether meta data associated with a user function argument using the
+** sqlite3_set_auxdata() API may be safely retained until the next
+** invocation of this opcode.
+**
+** SQL functions are initially coded as OP_Function0 with P4 pointing
+** to a FuncDef object. But on first evaluation, the P4 operand is
+** automatically converted into an sqlite3_context object and the operation
+** changed to this OP_Function opcode. In this way, the initialization of
+** the sqlite3_context object occurs only once, rather than once for each
+** evaluation of the function.
+**
+** See also: Function0, AggStep, AggFinal
+*/
+case OP_Function0: {
int n;
+ sqlite3_context *pCtx;
+ assert( pOp->p4type==P4_FUNCDEF );
n = pOp->p5;
- apVal = p->apArg;
- assert( apVal || n==0 );
- assert( pOp->p3>0 && pOp->p3<=(p->nMem-p->nCursor) );
- ctx.pOut = &aMem[pOp->p3];
- memAboutToChange(p, ctx.pOut);
-
- assert( n==0 || (pOp->p2>0 && pOp->p2+n<=(p->nMem-p->nCursor)+1) );
+ assert( pOp->p3>0 && pOp->p3<=(p->nMem+1 - p->nCursor) );
+ assert( n==0 || (pOp->p2>0 && pOp->p2+n<=(p->nMem+1 - p->nCursor)+1) );
assert( pOp->p3<pOp->p2 || pOp->p3>=pOp->p2+n );
- pArg = &aMem[pOp->p2];
- for(i=0; i<n; i++, pArg++){
- assert( memIsValid(pArg) );
- apVal[i] = pArg;
- Deephemeralize(pArg);
- REGISTER_TRACE(pOp->p2+i, pArg);
+ pCtx = sqlite3DbMallocRawNN(db, sizeof(*pCtx) + (n-1)*sizeof(sqlite3_value*));
+ if( pCtx==0 ) goto no_mem;
+ pCtx->pOut = 0;
+ pCtx->pFunc = pOp->p4.pFunc;
+ pCtx->iOp = (int)(pOp - aOp);
+ pCtx->pVdbe = p;
+ pCtx->argc = n;
+ pOp->p4type = P4_FUNCCTX;
+ pOp->p4.pCtx = pCtx;
+ pOp->opcode = OP_Function;
+ /* Fall through into OP_Function */
+}
+case OP_Function: {
+ int i;
+ sqlite3_context *pCtx;
+
+ assert( pOp->p4type==P4_FUNCCTX );
+ pCtx = pOp->p4.pCtx;
+
+ /* If this function is inside of a trigger, the register array in aMem[]
+ ** might change from one evaluation to the next. The next block of code
+ ** checks to see if the register array has changed, and if so it
+ ** reinitializes the relavant parts of the sqlite3_context object */
+ pOut = &aMem[pOp->p3];
+ if( pCtx->pOut != pOut ){
+ pCtx->pOut = pOut;
+ for(i=pCtx->argc-1; i>=0; i--) pCtx->argv[i] = &aMem[pOp->p2+i];
}
- assert( pOp->p4type==P4_FUNCDEF );
- ctx.pFunc = pOp->p4.pFunc;
- ctx.iOp = (int)(pOp - aOp);
- ctx.pVdbe = p;
- MemSetTypeFlag(ctx.pOut, MEM_Null);
- ctx.fErrorOrAux = 0;
+ memAboutToChange(p, pCtx->pOut);
+#ifdef SQLITE_DEBUG
+ for(i=0; i<pCtx->argc; i++){
+ assert( memIsValid(pCtx->argv[i]) );
+ REGISTER_TRACE(pOp->p2+i, pCtx->argv[i]);
+ }
+#endif
+ MemSetTypeFlag(pCtx->pOut, MEM_Null);
+ pCtx->fErrorOrAux = 0;
db->lastRowid = lastRowid;
- (*ctx.pFunc->xFunc)(&ctx, n, apVal); /* IMP: R-24505-23230 */
- lastRowid = db->lastRowid; /* Remember rowid changes made by xFunc */
+ (*pCtx->pFunc->xSFunc)(pCtx, pCtx->argc, pCtx->argv);/* IMP: R-24505-23230 */
+ lastRowid = db->lastRowid; /* Remember rowid changes made by xSFunc */
/* If the function returned an error, throw an exception */
- if( ctx.fErrorOrAux ){
- if( ctx.isError ){
- sqlite3SetString(&p->zErrMsg, db, "%s", sqlite3_value_text(ctx.pOut));
- rc = ctx.isError;
+ if( pCtx->fErrorOrAux ){
+ if( pCtx->isError ){
+ sqlite3VdbeError(p, "%s", sqlite3_value_text(pCtx->pOut));
+ rc = pCtx->isError;
}
- sqlite3VdbeDeleteAuxData(p, (int)(pOp - aOp), pOp->p1);
+ sqlite3VdbeDeleteAuxData(db, &p->pAuxData, pCtx->iOp, pOp->p1);
+ if( rc ) goto abort_due_to_error;
}
/* Copy the result of the function into register P3 */
- sqlite3VdbeChangeEncoding(ctx.pOut, encoding);
- if( sqlite3VdbeMemTooBig(ctx.pOut) ){
- goto too_big;
+ if( pOut->flags & (MEM_Str|MEM_Blob) ){
+ sqlite3VdbeChangeEncoding(pCtx->pOut, encoding);
+ if( sqlite3VdbeMemTooBig(pCtx->pOut) ) goto too_big;
}
- REGISTER_TRACE(pOp->p3, ctx.pOut);
- UPDATE_MAX_BLOBSIZE(ctx.pOut);
+ REGISTER_TRACE(pOp->p3, pCtx->pOut);
+ UPDATE_MAX_BLOBSIZE(pCtx->pOut);
break;
}
@@ -72613,9 +78535,9 @@ case OP_RealAffinity: { /* in1 */
** A NULL value is not changed by this routine. It remains NULL.
*/
case OP_Cast: { /* in1 */
- assert( pOp->p2>=SQLITE_AFF_NONE && pOp->p2<=SQLITE_AFF_REAL );
+ assert( pOp->p2>=SQLITE_AFF_BLOB && pOp->p2<=SQLITE_AFF_REAL );
testcase( pOp->p2==SQLITE_AFF_TEXT );
- testcase( pOp->p2==SQLITE_AFF_NONE );
+ testcase( pOp->p2==SQLITE_AFF_BLOB );
testcase( pOp->p2==SQLITE_AFF_NUMERIC );
testcase( pOp->p2==SQLITE_AFF_INTEGER );
testcase( pOp->p2==SQLITE_AFF_REAL );
@@ -72624,6 +78546,7 @@ case OP_Cast: { /* in1 */
rc = ExpandBlob(pIn1);
sqlite3VdbeMemCast(pIn1, pOp->p2, encoding);
UPDATE_MAX_BLOBSIZE(pIn1);
+ if( rc ) goto abort_due_to_error;
break;
}
#endif /* SQLITE_OMIT_CAST */
@@ -72750,6 +78673,7 @@ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */
*/
if( pOp->p5 & SQLITE_STOREP2 ){
pOut = &aMem[pOp->p2];
+ memAboutToChange(p, pOut);
MemSetTypeFlag(pOut, MEM_Null);
REGISTER_TRACE(pOp->p2, pOut);
}else{
@@ -72764,21 +78688,23 @@ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */
/* Neither operand is NULL. Do a comparison. */
affinity = pOp->p5 & SQLITE_AFF_MASK;
if( affinity>=SQLITE_AFF_NUMERIC ){
- if( (pIn1->flags & (MEM_Int|MEM_Real|MEM_Str))==MEM_Str ){
- applyNumericAffinity(pIn1,0);
- }
- if( (pIn3->flags & (MEM_Int|MEM_Real|MEM_Str))==MEM_Str ){
- applyNumericAffinity(pIn3,0);
+ if( (flags1 | flags3)&MEM_Str ){
+ if( (flags1 & (MEM_Int|MEM_Real|MEM_Str))==MEM_Str ){
+ applyNumericAffinity(pIn1,0);
+ }
+ if( (flags3 & (MEM_Int|MEM_Real|MEM_Str))==MEM_Str ){
+ applyNumericAffinity(pIn3,0);
+ }
}
}else if( affinity==SQLITE_AFF_TEXT ){
- if( (pIn1->flags & MEM_Str)==0 && (pIn1->flags & (MEM_Int|MEM_Real))!=0 ){
+ if( (flags1 & MEM_Str)==0 && (flags1 & (MEM_Int|MEM_Real))!=0 ){
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 ){
+ if( (flags3 & MEM_Str)==0 && (flags3 & (MEM_Int|MEM_Real))!=0 ){
testcase( pIn3->flags & MEM_Int );
testcase( pIn3->flags & MEM_Real );
sqlite3VdbeMemStringify(pIn3, encoding, 1);
@@ -72787,15 +78713,14 @@ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */
}
}
assert( pOp->p4type==P4_COLLSEQ || pOp->p4.pColl==0 );
- if( pIn1->flags & MEM_Zero ){
+ if( flags1 & MEM_Zero ){
sqlite3VdbeMemExpandBlob(pIn1);
flags1 &= ~MEM_Zero;
}
- if( pIn3->flags & MEM_Zero ){
+ if( flags3 & MEM_Zero ){
sqlite3VdbeMemExpandBlob(pIn3);
flags3 &= ~MEM_Zero;
}
- if( db->mallocFailed ) goto no_mem;
res = sqlite3MemCompare(pIn3, pIn1, pOp->p4.pColl);
}
switch( pOp->opcode ){
@@ -72836,11 +78761,14 @@ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */
** The permutation is only valid until the next OP_Compare that has
** the OPFLAG_PERMUTE bit set in P5. Typically the OP_Permutation should
** occur immediately prior to the OP_Compare.
+**
+** The first integer in the P4 integer array is the length of the array
+** and does not become part of the permutation.
*/
case OP_Permutation: {
assert( pOp->p4type==P4_INTARRAY );
assert( pOp->p4.ai );
- aPermute = pOp->p4.ai;
+ aPermute = pOp->p4.ai + 1;
break;
}
@@ -72885,11 +78813,11 @@ case OP_Compare: {
if( aPermute ){
int k, mx = 0;
for(k=0; k<n; k++) if( aPermute[k]>mx ) mx = aPermute[k];
- assert( p1>0 && p1+mx<=(p->nMem-p->nCursor)+1 );
- assert( p2>0 && p2+mx<=(p->nMem-p->nCursor)+1 );
+ assert( p1>0 && p1+mx<=(p->nMem+1 - p->nCursor)+1 );
+ assert( p2>0 && p2+mx<=(p->nMem+1 - p->nCursor)+1 );
}else{
- assert( p1>0 && p1+n<=(p->nMem-p->nCursor)+1 );
- assert( p2>0 && p2+n<=(p->nMem-p->nCursor)+1 );
+ assert( p1>0 && p1+n<=(p->nMem+1 - p->nCursor)+1 );
+ assert( p2>0 && p2+n<=(p->nMem+1 - p->nCursor)+1 );
}
#endif /* SQLITE_DEBUG */
for(i=0; i<n; i++){
@@ -73140,36 +79068,35 @@ case OP_Column: {
const u8 *zHdr; /* Next unparsed byte of the header */
const u8 *zEndHdr; /* Pointer to first byte after the header */
u32 offset; /* Offset into the data */
- u32 szField; /* Number of bytes in the content of a field */
+ u64 offset64; /* 64-bit offset */
u32 avail; /* Number of bytes of available data */
u32 t; /* A type code from the record header */
- u16 fx; /* pDest->flags value */
Mem *pReg; /* PseudoTable input register */
+ pC = p->apCsr[pOp->p1];
p2 = pOp->p2;
- assert( pOp->p3>0 && pOp->p3<=(p->nMem-p->nCursor) );
+
+ /* If the cursor cache is stale, bring it up-to-date */
+ rc = sqlite3VdbeCursorMoveto(&pC, &p2);
+
+ assert( pOp->p3>0 && pOp->p3<=(p->nMem+1 - p->nCursor) );
pDest = &aMem[pOp->p3];
memAboutToChange(p, pDest);
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
- pC = p->apCsr[pOp->p1];
assert( pC!=0 );
assert( p2<pC->nField );
aOffset = pC->aOffset;
-#ifndef SQLITE_OMIT_VIRTUALTABLE
- assert( pC->pVtabCursor==0 ); /* OP_Column never called on virtual table */
-#endif
- pCrsr = pC->pCursor;
- assert( pCrsr!=0 || pC->pseudoTableReg>0 ); /* pCrsr NULL on PseudoTables */
- assert( pCrsr!=0 || pC->nullRow ); /* pC->nullRow on PseudoTables */
+ assert( pC->eCurType!=CURTYPE_VTAB );
+ assert( pC->eCurType!=CURTYPE_PSEUDO || pC->nullRow );
+ assert( pC->eCurType!=CURTYPE_SORTER );
+ pCrsr = pC->uc.pCursor;
- /* If the cursor cache is stale, bring it up-to-date */
- rc = sqlite3VdbeCursorMoveto(pC);
if( rc ) goto abort_due_to_error;
if( pC->cacheStatus!=p->cacheCtr ){
if( pC->nullRow ){
- if( pCrsr==0 ){
- assert( pC->pseudoTableReg>0 );
- pReg = &aMem[pC->pseudoTableReg];
+ if( pC->eCurType==CURTYPE_PSEUDO ){
+ assert( pC->uc.pseudoTableReg>0 );
+ pReg = &aMem[pC->uc.pseudoTableReg];
assert( pReg->flags & MEM_Blob );
assert( memIsValid(pReg) );
pC->payloadSize = pC->szRow = avail = pReg->n;
@@ -73179,6 +79106,7 @@ case OP_Column: {
goto op_column_out;
}
}else{
+ assert( pC->eCurType==CURTYPE_BTREE );
assert( pCrsr );
if( pC->isTable==0 ){
assert( sqlite3BtreeCursorIsValid(pCrsr) );
@@ -73199,31 +79127,17 @@ case OP_Column: {
assert( avail<=65536 ); /* Maximum page size is 64KiB */
if( pC->payloadSize <= (u32)avail ){
pC->szRow = pC->payloadSize;
+ }else if( pC->payloadSize > (u32)db->aLimit[SQLITE_LIMIT_LENGTH] ){
+ goto too_big;
}else{
pC->szRow = avail;
}
- if( pC->payloadSize > (u32)db->aLimit[SQLITE_LIMIT_LENGTH] ){
- goto too_big;
- }
}
pC->cacheStatus = p->cacheCtr;
pC->iHdrOffset = getVarint32(pC->aRow, offset);
pC->nHdrParsed = 0;
aOffset[0] = offset;
- /* Make sure a corrupt database has not given us an oversize header.
- ** Do this now to avoid an oversize memory allocation.
- **
- ** Type entries can be between 1 and 5 bytes each. But 4 and 5 byte
- ** types use so much data space that there can only be 4096 and 32 of
- ** them, respectively. So the maximum header length results from a
- ** 3-byte type for each of the maximum of 32768 columns plus three
- ** extra bytes for the header length itself. 32768*3 + 3 = 98307.
- */
- if( offset > 98307 || offset > pC->payloadSize ){
- rc = SQLITE_CORRUPT_BKPT;
- goto op_column_error;
- }
if( avail<offset ){
/* pC->aRow does not have to hold the entire row, but it does at least
@@ -73232,6 +79146,20 @@ case OP_Column: {
** dynamically allocated. */
pC->aRow = 0;
pC->szRow = 0;
+
+ /* Make sure a corrupt database has not given us an oversize header.
+ ** Do this now to avoid an oversize memory allocation.
+ **
+ ** Type entries can be between 1 and 5 bytes each. But 4 and 5 byte
+ ** types use so much data space that there can only be 4096 and 32 of
+ ** them, respectively. So the maximum header length results from a
+ ** 3-byte type for each of the maximum of 32768 columns plus three
+ ** extra bytes for the header length itself. 32768*3 + 3 = 98307.
+ */
+ if( offset > 98307 || offset > pC->payloadSize ){
+ rc = SQLITE_CORRUPT_BKPT;
+ goto abort_due_to_error;
+ }
}
/* The following goto is an optimization. It can be omitted and
@@ -73254,11 +79182,8 @@ case OP_Column: {
/* Make sure zData points to enough of the record to cover the header. */
if( pC->aRow==0 ){
memset(&sMem, 0, sizeof(sMem));
- rc = sqlite3VdbeMemFromBtree(pCrsr, 0, aOffset[0],
- !pC->isTable, &sMem);
- if( rc!=SQLITE_OK ){
- goto op_column_error;
- }
+ rc = sqlite3VdbeMemFromBtree(pCrsr, 0, aOffset[0], !pC->isTable, &sMem);
+ if( rc!=SQLITE_OK ) goto abort_due_to_error;
zData = (u8*)sMem.z;
}else{
zData = pC->aRow;
@@ -73266,48 +79191,40 @@ case OP_Column: {
/* Fill in pC->aType[i] and aOffset[i] values through the p2-th field. */
i = pC->nHdrParsed;
- offset = aOffset[i];
+ offset64 = aOffset[i];
zHdr = zData + pC->iHdrOffset;
zEndHdr = zData + aOffset[0];
assert( i<=p2 && zHdr<zEndHdr );
do{
- if( zHdr[0]<0x80 ){
- t = zHdr[0];
+ if( (t = zHdr[0])<0x80 ){
zHdr++;
+ offset64 += sqlite3VdbeOneByteSerialTypeLen(t);
}else{
zHdr += sqlite3GetVarint32(zHdr, &t);
+ offset64 += sqlite3VdbeSerialTypeLen(t);
}
- pC->aType[i] = t;
- szField = sqlite3VdbeSerialTypeLen(t);
- offset += szField;
- if( offset<szField ){ /* True if offset overflows */
- zHdr = &zEndHdr[1]; /* Forces SQLITE_CORRUPT return below */
- break;
- }
- i++;
- aOffset[i] = offset;
+ pC->aType[i++] = t;
+ aOffset[i] = (u32)(offset64 & 0xffffffff);
}while( i<=p2 && zHdr<zEndHdr );
pC->nHdrParsed = i;
pC->iHdrOffset = (u32)(zHdr - zData);
- if( pC->aRow==0 ){
- sqlite3VdbeMemRelease(&sMem);
- sMem.flags = MEM_Null;
- }
/* The record is corrupt if any of the following are true:
** (1) the bytes of the header extend past the declared header size
- ** (zHdr>zEndHdr)
** (2) the entire header was used but not all data was used
- ** (zHdr==zEndHdr && offset!=pC->payloadSize)
** (3) the end of the data extends beyond the end of the record.
- ** (offset > pC->payloadSize)
*/
- if( (zHdr>=zEndHdr && (zHdr>zEndHdr || offset!=pC->payloadSize))
- || (offset > pC->payloadSize)
+ if( (zHdr>=zEndHdr && (zHdr>zEndHdr || offset64!=pC->payloadSize))
+ || (offset64 > pC->payloadSize)
){
+ if( pC->aRow==0 ) sqlite3VdbeMemRelease(&sMem);
rc = SQLITE_CORRUPT_BKPT;
- goto op_column_error;
+ goto abort_due_to_error;
}
+ if( pC->aRow==0 ) sqlite3VdbeMemRelease(&sMem);
+
+ }else{
+ t = 0;
}
/* If after trying to extract new entries from the header, nHdrParsed is
@@ -73322,6 +79239,8 @@ case OP_Column: {
}
goto op_column_out;
}
+ }else{
+ t = pC->aType[p2];
}
/* Extract the content for the p2+1-th column. Control can only
@@ -73332,11 +79251,32 @@ case OP_Column: {
assert( rc==SQLITE_OK );
assert( sqlite3VdbeCheckMemInvariants(pDest) );
if( VdbeMemDynamic(pDest) ) sqlite3VdbeMemSetNull(pDest);
- t = pC->aType[p2];
+ assert( t==pC->aType[p2] );
+ pDest->enc = encoding;
if( pC->szRow>=aOffset[p2+1] ){
/* This is the common case where the desired content fits on the original
** page - where the content is not on an overflow page */
- sqlite3VdbeSerialGet(pC->aRow+aOffset[p2], t, pDest);
+ zData = pC->aRow + aOffset[p2];
+ if( t<12 ){
+ sqlite3VdbeSerialGet(zData, t, pDest);
+ }else{
+ /* If the column value is a string, we need a persistent value, not
+ ** a MEM_Ephem value. This branch is a fast short-cut that is equivalent
+ ** to calling sqlite3VdbeSerialGet() and sqlite3VdbeDeephemeralize().
+ */
+ static const u16 aFlag[] = { MEM_Blob, MEM_Str|MEM_Term };
+ pDest->n = len = (t-12)/2;
+ if( pDest->szMalloc < len+2 ){
+ pDest->flags = MEM_Null;
+ if( sqlite3VdbeMemGrow(pDest, len+2, 0) ) goto no_mem;
+ }else{
+ pDest->z = pDest->zMalloc;
+ }
+ memcpy(pDest->z, zData, len);
+ pDest->z[len] = 0;
+ pDest->z[len+1] = 0;
+ pDest->flags = aFlag[t&1];
+ }
}else{
/* This branch happens only when content is on overflow pages */
if( ((pOp->p5 & (OPFLAG_LENGTHARG|OPFLAG_TYPEOFARG))!=0
@@ -73348,39 +79288,19 @@ case OP_Column: {
** 2. the length(X) function if X is a blob, and
** 3. if the content length is zero.
** So we might as well use bogus content rather than reading
- ** content from disk. NULL will work for the value for strings
- ** and blobs and whatever is in the payloadSize64 variable
- ** will work for everything else. */
- sqlite3VdbeSerialGet(t<=13 ? (u8*)&payloadSize64 : 0, t, pDest);
+ ** content from disk. */
+ static u8 aZero[8]; /* This is the bogus content */
+ sqlite3VdbeSerialGet(aZero, t, pDest);
}else{
rc = sqlite3VdbeMemFromBtree(pCrsr, aOffset[p2], len, !pC->isTable,
pDest);
- if( rc!=SQLITE_OK ){
- goto op_column_error;
- }
+ if( rc!=SQLITE_OK ) goto abort_due_to_error;
sqlite3VdbeSerialGet((const u8*)pDest->z, t, pDest);
pDest->flags &= ~MEM_Ephem;
}
}
- pDest->enc = encoding;
op_column_out:
- /* If the column value is an ephemeral string, go ahead and persist
- ** that string in case the cursor moves before the column value is
- ** used. The following code does the equivalent of Deephemeralize()
- ** but does it faster. */
- if( (pDest->flags & MEM_Ephem)!=0 && pDest->z ){
- fx = pDest->flags & (MEM_Str|MEM_Blob);
- assert( fx!=0 );
- zData = (const u8*)pDest->z;
- len = pDest->n;
- if( sqlite3VdbeMemClearAndResize(pDest, len+2) ) goto no_mem;
- memcpy(pDest->z, zData, len);
- pDest->z[len] = 0;
- pDest->z[len+1] = 0;
- pDest->flags = fx|MEM_Term;
- }
-op_column_error:
UPDATE_MAX_BLOBSIZE(pDest);
REGISTER_TRACE(pOp->p3, pDest);
break;
@@ -73404,7 +79324,7 @@ case OP_Affinity: {
assert( zAffinity[pOp->p2]==0 );
pIn1 = &aMem[pOp->p1];
while( (cAff = *(zAffinity++))!=0 ){
- assert( pIn1 <= &p->aMem[(p->nMem-p->nCursor)] );
+ assert( pIn1 <= &p->aMem[(p->nMem+1 - p->nCursor)] );
assert( memIsValid(pIn1) );
applyAffinity(pIn1, cAff, encoding);
pIn1++;
@@ -73426,7 +79346,7 @@ case OP_Affinity: {
** The mapping from character to affinity is given by the SQLITE_AFF_
** macros defined in sqliteInt.h.
**
-** If P4 is NULL then all index fields have the affinity NONE.
+** If P4 is NULL then all index fields have the affinity BLOB.
*/
case OP_MakeRecord: {
u8 *zNewRecord; /* A buffer to hold the data for the new record */
@@ -73444,7 +79364,7 @@ case OP_MakeRecord: {
int file_format; /* File format to use for encoding */
int i; /* Space used in zNewRecord[] header */
int j; /* Space used in zNewRecord[] content */
- int len; /* Length of a field */
+ u32 len; /* Length of a field */
/* Assuming the record contains N fields, the record format looks
** like this:
@@ -73466,7 +79386,7 @@ case OP_MakeRecord: {
nZero = 0; /* Number of zero bytes at the end of the record */
nField = pOp->p1;
zAffinity = pOp->p4.z;
- assert( nField>0 && pOp->p2>0 && pOp->p2+nField<=(p->nMem-p->nCursor)+1 );
+ assert( nField>0 && pOp->p2>0 && pOp->p2+nField<=(p->nMem+1 - p->nCursor)+1 );
pData0 = &aMem[nField];
nField = pOp->p2;
pLast = &pData0[nField-1];
@@ -73494,11 +79414,10 @@ case OP_MakeRecord: {
pRec = pLast;
do{
assert( memIsValid(pRec) );
- pRec->uTemp = serial_type = sqlite3VdbeSerialType(pRec, file_format);
- len = sqlite3VdbeSerialTypeLen(serial_type);
+ pRec->uTemp = serial_type = sqlite3VdbeSerialType(pRec, file_format, &len);
if( pRec->flags & MEM_Zero ){
if( nData ){
- sqlite3VdbeMemExpandBlob(pRec);
+ if( sqlite3VdbeMemExpandBlob(pRec) ) goto no_mem;
}else{
nZero += pRec->u.nZero;
len -= pRec->u.nZero;
@@ -73508,7 +79427,9 @@ case OP_MakeRecord: {
testcase( serial_type==127 );
testcase( serial_type==128 );
nHdr += serial_type<=127 ? 1 : sqlite3VarintLen(serial_type);
- }while( (--pRec)>=pData0 );
+ if( pRec==pData0 ) break;
+ pRec--;
+ }while(1);
/* EVIDENCE-OF: R-22564-11647 The header begins with a single varint
** which determines the total number of bytes in the header. The varint
@@ -73557,7 +79478,7 @@ case OP_MakeRecord: {
assert( i==nHdr );
assert( j==nByte );
- assert( pOp->p3>0 && pOp->p3<=(p->nMem-p->nCursor) );
+ assert( pOp->p3>0 && pOp->p3<=(p->nMem+1 - p->nCursor) );
pOut->n = (int)nByte;
pOut->flags = MEM_Blob;
if( nZero ){
@@ -73581,10 +79502,12 @@ case OP_Count: { /* out2 */
i64 nEntry;
BtCursor *pCrsr;
- pCrsr = p->apCsr[pOp->p1]->pCursor;
+ assert( p->apCsr[pOp->p1]->eCurType==CURTYPE_BTREE );
+ pCrsr = p->apCsr[pOp->p1]->uc.pCursor;
assert( pCrsr );
nEntry = 0; /* Not needed. Only used to silence a warning. */
rc = sqlite3BtreeCount(pCrsr, &nEntry);
+ if( rc ) goto abort_due_to_error;
pOut = out2Prerelease(p, pOp);
pOut->u.i = nEntry;
break;
@@ -73624,8 +79547,7 @@ case OP_Savepoint: {
/* A new savepoint cannot be created if there are active write
** statements (i.e. open read/write incremental blob handles).
*/
- sqlite3SetString(&p->zErrMsg, db, "cannot open savepoint - "
- "SQL statements in progress");
+ sqlite3VdbeError(p, "cannot open savepoint - SQL statements in progress");
rc = SQLITE_BUSY;
}else{
nName = sqlite3Strlen30(zName);
@@ -73642,7 +79564,7 @@ case OP_Savepoint: {
#endif
/* Create a new savepoint structure. */
- pNew = sqlite3DbMallocRaw(db, sizeof(Savepoint)+nName+1);
+ pNew = sqlite3DbMallocRawNN(db, sizeof(Savepoint)+nName+1);
if( pNew ){
pNew->zName = (char *)&pNew[1];
memcpy(pNew->zName, zName, nName+1);
@@ -73655,7 +79577,7 @@ case OP_Savepoint: {
}else{
db->nSavepoint++;
}
-
+
/* Link the new savepoint into the database handle's list. */
pNew->pNext = db->pSavepoint;
db->pSavepoint = pNew;
@@ -73676,15 +79598,14 @@ case OP_Savepoint: {
iSavepoint++;
}
if( !pSavepoint ){
- sqlite3SetString(&p->zErrMsg, db, "no such savepoint: %s", zName);
+ sqlite3VdbeError(p, "no such savepoint: %s", zName);
rc = SQLITE_ERROR;
}else if( db->nVdbeWrite>0 && p1==SAVEPOINT_RELEASE ){
/* It is not possible to release (commit) a savepoint if there are
** active write statements.
*/
- sqlite3SetString(&p->zErrMsg, db,
- "cannot release savepoint - SQL statements in progress"
- );
+ sqlite3VdbeError(p, "cannot release savepoint - "
+ "SQL statements in progress");
rc = SQLITE_BUSY;
}else{
@@ -73764,6 +79685,7 @@ case OP_Savepoint: {
}
}
}
+ if( rc ) goto abort_due_to_error;
break;
}
@@ -73780,49 +79702,37 @@ case OP_Savepoint: {
case OP_AutoCommit: {
int desiredAutoCommit;
int iRollback;
- int turnOnAC;
desiredAutoCommit = pOp->p1;
iRollback = pOp->p2;
- turnOnAC = desiredAutoCommit && !db->autoCommit;
assert( desiredAutoCommit==1 || desiredAutoCommit==0 );
assert( desiredAutoCommit==1 || iRollback==0 );
assert( db->nVdbeActive>0 ); /* At least this one VM is active */
assert( p->bIsReader );
-#if 0
- if( turnOnAC && iRollback && db->nVdbeActive>1 ){
- /* If this instruction implements a ROLLBACK and other VMs are
- ** still running, and a transaction is active, return an error indicating
- ** that the other VMs must complete first.
- */
- sqlite3SetString(&p->zErrMsg, db, "cannot rollback transaction - "
- "SQL statements in progress");
- rc = SQLITE_BUSY;
- }else
-#endif
- if( turnOnAC && !iRollback && db->nVdbeWrite>0 ){
- /* If this instruction implements a COMMIT and other VMs are writing
- ** return an error indicating that the other VMs must complete first.
- */
- sqlite3SetString(&p->zErrMsg, db, "cannot commit transaction - "
- "SQL statements in progress");
- rc = SQLITE_BUSY;
- }else if( desiredAutoCommit!=db->autoCommit ){
+ if( desiredAutoCommit!=db->autoCommit ){
if( iRollback ){
assert( desiredAutoCommit==1 );
sqlite3RollbackAll(db, SQLITE_ABORT_ROLLBACK);
db->autoCommit = 1;
+ }else if( desiredAutoCommit && db->nVdbeWrite>0 ){
+ /* If this instruction implements a COMMIT and other VMs are writing
+ ** return an error indicating that the other VMs must complete first.
+ */
+ sqlite3VdbeError(p, "cannot commit transaction - "
+ "SQL statements in progress");
+ rc = SQLITE_BUSY;
+ goto abort_due_to_error;
}else if( (rc = sqlite3VdbeCheckFk(p, 1))!=SQLITE_OK ){
goto vdbe_return;
}else{
db->autoCommit = (u8)desiredAutoCommit;
- if( sqlite3VdbeHalt(p)==SQLITE_BUSY ){
- p->pc = (int)(pOp - aOp);
- db->autoCommit = (u8)(1-desiredAutoCommit);
- p->rc = rc = SQLITE_BUSY;
- goto vdbe_return;
- }
+ }
+ if( sqlite3VdbeHalt(p)==SQLITE_BUSY ){
+ p->pc = (int)(pOp - aOp);
+ db->autoCommit = (u8)(1-desiredAutoCommit);
+ p->rc = rc = SQLITE_BUSY;
+ goto vdbe_return;
}
assert( db->nStatement==0 );
sqlite3CloseSavepoints(db);
@@ -73833,12 +79743,13 @@ case OP_AutoCommit: {
}
goto vdbe_return;
}else{
- sqlite3SetString(&p->zErrMsg, db,
+ sqlite3VdbeError(p,
(!desiredAutoCommit)?"cannot start a transaction within a transaction":(
(iRollback)?"cannot rollback - no transaction is active":
"cannot commit - no transaction is active"));
rc = SQLITE_ERROR;
+ goto abort_due_to_error;
}
break;
}
@@ -73894,9 +79805,11 @@ case OP_Transaction: {
if( pBt ){
rc = sqlite3BtreeBeginTrans(pBt, pOp->p2);
- if( rc==SQLITE_BUSY ){
+ testcase( rc==SQLITE_BUSY_SNAPSHOT );
+ testcase( rc==SQLITE_BUSY_RECOVERY );
+ if( (rc&0xff)==SQLITE_BUSY ){
p->pc = (int)(pOp - aOp);
- p->rc = rc = SQLITE_BUSY;
+ p->rc = rc;
goto vdbe_return;
}
if( rc!=SQLITE_OK ){
@@ -73959,6 +79872,7 @@ case OP_Transaction: {
p->expired = 1;
rc = SQLITE_SCHEMA;
}
+ if( rc ) goto abort_due_to_error;
break;
}
@@ -73995,15 +79909,15 @@ case OP_ReadCookie: { /* out2 */
/* Opcode: SetCookie P1 P2 P3 * *
**
-** Write the content of register P3 (interpreted as an integer)
-** into cookie number P2 of database P1. P2==1 is the schema version.
-** P2==2 is the database format. P2==3 is the recommended pager cache
+** Write the integer value P3 into cookie number P2 of database P1.
+** P2==1 is the schema version. P2==2 is the database format.
+** P2==3 is the recommended pager cache
** size, and so forth. P1==0 is the main database file and P1==1 is the
** database file used to store temporary tables.
**
** A transaction must be started before executing this opcode.
*/
-case OP_SetCookie: { /* in3 */
+case OP_SetCookie: {
Db *pDb;
assert( pOp->p2<SQLITE_N_BTREE_META );
assert( pOp->p1>=0 && pOp->p1<db->nDb );
@@ -74012,17 +79926,15 @@ case OP_SetCookie: { /* in3 */
pDb = &db->aDb[pOp->p1];
assert( pDb->pBt!=0 );
assert( sqlite3SchemaMutexHeld(db, pOp->p1, 0) );
- pIn3 = &aMem[pOp->p3];
- sqlite3VdbeMemIntegerify(pIn3);
/* See note about index shifting on OP_ReadCookie */
- rc = sqlite3BtreeUpdateMeta(pDb->pBt, pOp->p2, (int)pIn3->u.i);
+ rc = sqlite3BtreeUpdateMeta(pDb->pBt, pOp->p2, pOp->p3);
if( pOp->p2==BTREE_SCHEMA_VERSION ){
/* When the schema cookie changes, record the new cookie internally */
- pDb->pSchema->schema_cookie = (int)pIn3->u.i;
+ pDb->pSchema->schema_cookie = pOp->p3;
db->flags |= SQLITE_InternChanges;
}else if( pOp->p2==BTREE_FILE_FORMAT ){
/* Record changes in the file format */
- pDb->pSchema->file_format = (u8)pIn3->u.i;
+ pDb->pSchema->file_format = pOp->p3;
}
if( pOp->p1==1 ){
/* Invalidate all prepared statements whenever the TEMP database
@@ -74030,6 +79942,7 @@ case OP_SetCookie: { /* in3 */
sqlite3ExpirePreparedStatements(db);
p->expired = 0;
}
+ if( rc ) goto abort_due_to_error;
break;
}
@@ -74120,7 +80033,6 @@ case OP_ReopenIdx: {
case OP_OpenRead:
case OP_OpenWrite:
- 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
@@ -74128,7 +80040,7 @@ case OP_OpenWrite:
if( p->expired ){
rc = SQLITE_ABORT_ROLLBACK;
- break;
+ goto abort_due_to_error;
}
nField = 0;
@@ -74141,7 +80053,8 @@ case OP_OpenWrite:
pX = pDb->pBt;
assert( pX!=0 );
if( pOp->opcode==OP_OpenWrite ){
- wrFlag = 1;
+ assert( OPFLAG_FORDELETE==BTREE_FORDELETE );
+ wrFlag = BTREE_WRCSR | (pOp->p5 & OPFLAG_FORDELETE);
assert( sqlite3SchemaMutexHeld(db, iDb, 0) );
if( pDb->pSchema->file_format < p->minWriteFileFormat ){
p->minWriteFileFormat = pDb->pSchema->file_format;
@@ -74151,7 +80064,7 @@ case OP_OpenWrite:
}
if( pOp->p5 & OPFLAG_P2ISREG ){
assert( p2>0 );
- assert( p2<=(p->nMem-p->nCursor) );
+ assert( p2<=(p->nMem+1 - p->nCursor) );
pIn2 = &aMem[p2];
assert( memIsValid(pIn2) );
assert( (pIn2->flags & MEM_Int)!=0 );
@@ -74161,10 +80074,7 @@ case OP_OpenWrite:
** that opcode will always set the p2 value to 2 or more or else fail.
** If there were a failure, the prepared statement would have halted
** before reaching this instruction. */
- if( NEVER(p2<2) ) {
- rc = SQLITE_CORRUPT_BKPT;
- goto abort_due_to_error;
- }
+ assert( p2>=2 );
}
if( pOp->p4type==P4_KEYINFO ){
pKeyInfo = pOp->p4.pKeyInfo;
@@ -74177,12 +80087,15 @@ case OP_OpenWrite:
assert( pOp->p1>=0 );
assert( nField>=0 );
testcase( nField==0 ); /* Table with INTEGER PRIMARY KEY and nothing else */
- pCur = allocateCursor(p, pOp->p1, nField, iDb, 1);
+ pCur = allocateCursor(p, pOp->p1, nField, iDb, CURTYPE_BTREE);
if( pCur==0 ) goto no_mem;
pCur->nullRow = 1;
pCur->isOrdered = 1;
pCur->pgnoRoot = p2;
- rc = sqlite3BtreeCursor(pX, p2, wrFlag, pKeyInfo, pCur->pCursor);
+#ifdef SQLITE_DEBUG
+ pCur->wrFlag = wrFlag;
+#endif
+ rc = sqlite3BtreeCursor(pX, p2, wrFlag, pKeyInfo, pCur->uc.pCursor);
pCur->pKeyInfo = pKeyInfo;
/* Set the VdbeCursor.isTable variable. Previous versions of
** SQLite used to check if the root-page flags were sane at this point
@@ -74193,8 +80106,13 @@ case OP_OpenWrite:
open_cursor_set_hints:
assert( OPFLAG_BULKCSR==BTREE_BULKLOAD );
assert( OPFLAG_SEEKEQ==BTREE_SEEK_EQ );
- sqlite3BtreeCursorHints(pCur->pCursor,
- (pOp->p5 & (OPFLAG_BULKCSR|OPFLAG_SEEKEQ)));
+ testcase( pOp->p5 & OPFLAG_BULKCSR );
+#ifdef SQLITE_ENABLE_CURSOR_HINTS
+ testcase( pOp->p2 & OPFLAG_SEEKEQ );
+#endif
+ sqlite3BtreeCursorHintFlags(pCur->uc.pCursor,
+ (pOp->p5 & (OPFLAG_BULKCSR|OPFLAG_SEEKEQ)));
+ if( rc ) goto abort_due_to_error;
break;
}
@@ -74237,7 +80155,7 @@ case OP_OpenEphemeral: {
SQLITE_OPEN_TRANSIENT_DB;
assert( pOp->p1>=0 );
assert( pOp->p2>=0 );
- pCx = allocateCursor(p, pOp->p1, pOp->p2, -1, 1);
+ pCx = allocateCursor(p, pOp->p1, pOp->p2, -1, CURTYPE_BTREE);
if( pCx==0 ) goto no_mem;
pCx->nullRow = 1;
pCx->isEphemeral = 1;
@@ -74261,14 +80179,17 @@ case OP_OpenEphemeral: {
assert( pKeyInfo->db==db );
assert( pKeyInfo->enc==ENC(db) );
pCx->pKeyInfo = pKeyInfo;
- rc = sqlite3BtreeCursor(pCx->pBt, pgno, 1, pKeyInfo, pCx->pCursor);
+ rc = sqlite3BtreeCursor(pCx->pBt, pgno, BTREE_WRCSR,
+ pKeyInfo, pCx->uc.pCursor);
}
pCx->isTable = 0;
}else{
- rc = sqlite3BtreeCursor(pCx->pBt, MASTER_ROOT, 1, 0, pCx->pCursor);
+ rc = sqlite3BtreeCursor(pCx->pBt, MASTER_ROOT, BTREE_WRCSR,
+ 0, pCx->uc.pCursor);
pCx->isTable = 1;
}
}
+ if( rc ) goto abort_due_to_error;
pCx->isOrdered = (pOp->p5!=BTREE_UNORDERED);
break;
}
@@ -74288,12 +80209,13 @@ case OP_SorterOpen: {
assert( pOp->p1>=0 );
assert( pOp->p2>=0 );
- pCx = allocateCursor(p, pOp->p1, pOp->p2, -1, 1);
+ pCx = allocateCursor(p, pOp->p1, pOp->p2, -1, CURTYPE_SORTER);
if( pCx==0 ) goto no_mem;
pCx->pKeyInfo = pOp->p4.pKeyInfo;
assert( pCx->pKeyInfo->db==db );
assert( pCx->pKeyInfo->enc==ENC(db) );
rc = sqlite3VdbeSorterInit(db, pOp->p3, pCx);
+ if( rc ) goto abort_due_to_error;
break;
}
@@ -74308,7 +80230,7 @@ case OP_SequenceTest: {
VdbeCursor *pC;
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
pC = p->apCsr[pOp->p1];
- assert( pC->pSorter );
+ assert( isSorter(pC) );
if( (pC->seqCount++)==0 ){
goto jump_to_p2;
}
@@ -74336,10 +80258,10 @@ case OP_OpenPseudo: {
assert( pOp->p1>=0 );
assert( pOp->p3>=0 );
- pCx = allocateCursor(p, pOp->p1, pOp->p3, -1, 0);
+ pCx = allocateCursor(p, pOp->p1, pOp->p3, -1, CURTYPE_PSEUDO);
if( pCx==0 ) goto no_mem;
pCx->nullRow = 1;
- pCx->pseudoTableReg = pOp->p2;
+ pCx->uc.pseudoTableReg = pOp->p2;
pCx->isTable = 1;
assert( pOp->p5==0 );
break;
@@ -74357,6 +80279,26 @@ case OP_Close: {
break;
}
+#ifdef SQLITE_ENABLE_COLUMN_USED_MASK
+/* Opcode: ColumnsUsed P1 * * P4 *
+**
+** This opcode (which only exists if SQLite was compiled with
+** SQLITE_ENABLE_COLUMN_USED_MASK) identifies which columns of the
+** table or index for cursor P1 are used. P4 is a 64-bit integer
+** (P4_INT64) in which the first 63 bits are one for each of the
+** first 63 columns of the table or index that are actually used
+** by the cursor. The high-order bit is set if any column after
+** the 64th is used.
+*/
+case OP_ColumnsUsed: {
+ VdbeCursor *pC;
+ pC = p->apCsr[pOp->p1];
+ assert( pC->eCurType==CURTYPE_BTREE );
+ pC->maskUsed = *(u64*)pOp->p4.pI64;
+ break;
+}
+#endif
+
/* Opcode: SeekGE P1 P2 P3 P4 *
** Synopsis: key=r[P3@P4]
**
@@ -74369,6 +80311,13 @@ case OP_Close: {
** is greater than or equal to the key value. If there are no records
** greater than or equal to the key and P2 is not zero, then jump to P2.
**
+** If the cursor P1 was opened using the OPFLAG_SEEKEQ flag, then this
+** opcode will always land on a record that equally equals the key, or
+** else jump immediately to P2. When the cursor is OPFLAG_SEEKEQ, this
+** opcode must be followed by an IdxLE opcode with the same arguments.
+** The IdxLE opcode will be skipped if this opcode succeeds, but the
+** IdxLE opcode will be used on subsequent loop iterations.
+**
** This opcode leaves the cursor configured to move in forward order,
** from the beginning toward the end. In other words, the cursor is
** configured to use Next, not Prev.
@@ -74427,51 +80376,48 @@ case OP_Close: {
** from the end toward the beginning. In other words, the cursor is
** configured to use Prev, not Next.
**
+** If the cursor P1 was opened using the OPFLAG_SEEKEQ flag, then this
+** opcode will always land on a record that equally equals the key, or
+** else jump immediately to P2. When the cursor is OPFLAG_SEEKEQ, this
+** opcode must be followed by an IdxGE opcode with the same arguments.
+** The IdxGE opcode will be skipped if this opcode succeeds, but the
+** IdxGE opcode will be used on subsequent loop iterations.
+**
** See also: Found, NotFound, SeekGt, SeekGe, SeekLt
*/
case OP_SeekLT: /* jump, in3 */
case OP_SeekLE: /* jump, in3 */
case OP_SeekGE: /* jump, in3 */
case OP_SeekGT: { /* jump, in3 */
- int res;
- int oc;
- VdbeCursor *pC;
- UnpackedRecord r;
- int nField;
- i64 iKey; /* The rowid we are to seek to */
+ int res; /* Comparison result */
+ int oc; /* Opcode */
+ VdbeCursor *pC; /* The cursor to seek */
+ UnpackedRecord r; /* The key to seek for */
+ int nField; /* Number of columns or fields in the key */
+ i64 iKey; /* The rowid we are to seek to */
+ int eqOnly; /* Only interested in == results */
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
assert( pOp->p2!=0 );
pC = p->apCsr[pOp->p1];
assert( pC!=0 );
- assert( pC->pseudoTableReg==0 );
+ assert( pC->eCurType==CURTYPE_BTREE );
assert( OP_SeekLE == OP_SeekLT+1 );
assert( OP_SeekGE == OP_SeekLT+2 );
assert( OP_SeekGT == OP_SeekLT+3 );
assert( pC->isOrdered );
- assert( pC->pCursor!=0 );
+ assert( pC->uc.pCursor!=0 );
oc = pOp->opcode;
+ eqOnly = 0;
pC->nullRow = 0;
#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 BTREE_SEEK_EQ flag is only set on index cursors */
+ assert( sqlite3BtreeCursorHasHint(pC->uc.pCursor, BTREE_SEEK_EQ)==0 );
+
/* 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
** the seek, so convert it. */
@@ -74514,12 +80460,26 @@ case OP_SeekGT: { /* jump, in3 */
if( (oc & 0x0001)==(OP_SeekLT & 0x0001) ) oc++;
}
}
- rc = sqlite3BtreeMovetoUnpacked(pC->pCursor, 0, (u64)iKey, 0, &res);
+ rc = sqlite3BtreeMovetoUnpacked(pC->uc.pCursor, 0, (u64)iKey, 0, &res);
pC->movetoTarget = iKey; /* Used by OP_Delete */
if( rc!=SQLITE_OK ){
goto abort_due_to_error;
}
}else{
+ /* 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.
+ */
+ if( sqlite3BtreeCursorHasHint(pC->uc.pCursor, BTREE_SEEK_EQ) ){
+ eqOnly = 1;
+ 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 );
+ }
+
nField = pOp->p4.i;
assert( pOp->p4type==P4_INT32 );
assert( nField>0 );
@@ -74544,10 +80504,15 @@ case OP_SeekGT: { /* jump, in3 */
{ int i; for(i=0; i<r.nField; i++) assert( memIsValid(&r.aMem[i]) ); }
#endif
ExpandBlob(r.aMem);
- rc = sqlite3BtreeMovetoUnpacked(pC->pCursor, &r, 0, 0, &res);
+ r.eqSeen = 0;
+ rc = sqlite3BtreeMovetoUnpacked(pC->uc.pCursor, &r, 0, 0, &res);
if( rc!=SQLITE_OK ){
goto abort_due_to_error;
}
+ if( eqOnly && r.eqSeen==0 ){
+ assert( res!=0 );
+ goto seek_not_found;
+ }
}
pC->deferredMoveto = 0;
pC->cacheStatus = CACHE_STALE;
@@ -74557,7 +80522,7 @@ case OP_SeekGT: { /* jump, in3 */
if( oc>=OP_SeekGE ){ assert( oc==OP_SeekGE || oc==OP_SeekGT );
if( res<0 || (res==0 && oc==OP_SeekGT) ){
res = 0;
- rc = sqlite3BtreeNext(pC->pCursor, &res);
+ rc = sqlite3BtreeNext(pC->uc.pCursor, &res);
if( rc!=SQLITE_OK ) goto abort_due_to_error;
}else{
res = 0;
@@ -74566,47 +80531,26 @@ case OP_SeekGT: { /* jump, in3 */
assert( oc==OP_SeekLT || oc==OP_SeekLE );
if( res>0 || (res==0 && oc==OP_SeekLT) ){
res = 0;
- rc = sqlite3BtreePrevious(pC->pCursor, &res);
+ rc = sqlite3BtreePrevious(pC->uc.pCursor, &res);
if( rc!=SQLITE_OK ) goto abort_due_to_error;
}else{
/* res might be negative because the table is empty. Check to
** see if this is the case.
*/
- res = sqlite3BtreeEof(pC->pCursor);
+ res = sqlite3BtreeEof(pC->uc.pCursor);
}
}
+seek_not_found:
assert( pOp->p2>0 );
VdbeBranchTaken(res!=0,2);
if( res ){
goto jump_to_p2;
+ }else if( eqOnly ){
+ assert( pOp[1].opcode==OP_IdxLT || pOp[1].opcode==OP_IdxGT );
+ pOp++; /* Skip the OP_IdxLt or OP_IdxGT that follows */
}
break;
}
-
-/* Opcode: Seek P1 P2 * * *
-** Synopsis: intkey=r[P2]
-**
-** P1 is an open table cursor and P2 is a rowid integer. Arrange
-** for P1 to move so that it points to the rowid given by P2.
-**
-** This is actually a deferred seek. Nothing actually happens until
-** the cursor is used to read a record. That way, if no reads
-** occur, no unnecessary I/O happens.
-*/
-case OP_Seek: { /* in2 */
- VdbeCursor *pC;
-
- assert( pOp->p1>=0 && pOp->p1<p->nCursor );
- pC = p->apCsr[pOp->p1];
- assert( pC!=0 );
- assert( pC->pCursor!=0 );
- assert( pC->isTable );
- pC->nullRow = 0;
- pIn2 = &aMem[pOp->p2];
- pC->movetoTarget = sqlite3VdbeIntValue(pIn2);
- pC->deferredMoveto = 1;
- break;
-}
/* Opcode: Found P1 P2 P3 P4 *
@@ -74693,7 +80637,8 @@ case OP_Found: { /* jump, in3 */
pC->seekOp = pOp->opcode;
#endif
pIn3 = &aMem[pOp->p3];
- assert( pC->pCursor!=0 );
+ assert( pC->eCurType==CURTYPE_BTREE );
+ assert( pC->uc.pCursor!=0 );
assert( pC->isTable==0 );
pFree = 0;
if( pOp->p4.i>0 ){
@@ -74730,10 +80675,10 @@ case OP_Found: { /* jump, in3 */
}
}
}
- rc = sqlite3BtreeMovetoUnpacked(pC->pCursor, pIdxKey, 0, 0, &res);
+ rc = sqlite3BtreeMovetoUnpacked(pC->uc.pCursor, pIdxKey, 0, 0, &res);
sqlite3DbFree(db, pFree);
if( rc!=SQLITE_OK ){
- break;
+ goto abort_due_to_error;
}
pC->seekResult = res;
alreadyExists = (res==0);
@@ -74755,9 +80700,10 @@ case OP_Found: { /* jump, in3 */
**
** P1 is the index of a cursor open on an SQL table btree (with integer
** keys). P3 is an integer rowid. If P1 does not contain a record with
-** rowid P3 then jump immediately to P2. If P1 does contain a record
-** with rowid P3 then leave the cursor pointing at that record and fall
-** through to the next instruction.
+** rowid P3 then jump immediately to P2. Or, if P2 is 0, raise an
+** SQLITE_CORRUPT error. If P1 does contain a record with rowid P3 then
+** leave the cursor pointing at that record and fall through to the next
+** instruction.
**
** The OP_NotFound opcode performs the same operation on index btrees
** (with arbitrary multi-value keys).
@@ -74783,19 +80729,28 @@ case OP_NotExists: { /* jump, in3 */
pC->seekOp = 0;
#endif
assert( pC->isTable );
- assert( pC->pseudoTableReg==0 );
- pCrsr = pC->pCursor;
+ assert( pC->eCurType==CURTYPE_BTREE );
+ pCrsr = pC->uc.pCursor;
assert( pCrsr!=0 );
res = 0;
iKey = pIn3->u.i;
rc = sqlite3BtreeMovetoUnpacked(pCrsr, 0, iKey, 0, &res);
+ assert( rc==SQLITE_OK || res==0 );
pC->movetoTarget = iKey; /* Used by OP_Delete */
pC->nullRow = 0;
pC->cacheStatus = CACHE_STALE;
pC->deferredMoveto = 0;
VdbeBranchTaken(res!=0,2);
pC->seekResult = res;
- if( res!=0 ) goto jump_to_p2;
+ if( res!=0 ){
+ assert( rc==SQLITE_OK );
+ if( pOp->p2==0 ){
+ rc = SQLITE_CORRUPT_BKPT;
+ }else{
+ goto jump_to_p2;
+ }
+ }
+ if( rc ) goto abort_due_to_error;
break;
}
@@ -74810,6 +80765,7 @@ case OP_NotExists: { /* jump, in3 */
case OP_Sequence: { /* out2 */
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
assert( p->apCsr[pOp->p1]!=0 );
+ assert( p->apCsr[pOp->p1]->eCurType!=CURTYPE_VTAB );
pOut = out2Prerelease(p, pOp);
pOut->u.i = p->apCsr[pOp->p1]->seqCount++;
break;
@@ -74845,9 +80801,9 @@ case OP_NewRowid: { /* out2 */
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
pC = p->apCsr[pOp->p1];
assert( pC!=0 );
- if( NEVER(pC->pCursor==0) ){
- /* The zero initialization above is all that is needed */
- }else{
+ assert( pC->eCurType==CURTYPE_BTREE );
+ assert( pC->uc.pCursor!=0 );
+ {
/* The next rowid or record number (different terms for the same
** thing) is obtained in a two-step algorithm.
**
@@ -74874,15 +80830,15 @@ case OP_NewRowid: { /* out2 */
#endif
if( !pC->useRandomRowid ){
- rc = sqlite3BtreeLast(pC->pCursor, &res);
+ rc = sqlite3BtreeLast(pC->uc.pCursor, &res);
if( rc!=SQLITE_OK ){
goto abort_due_to_error;
}
if( res ){
v = 1; /* IMP: R-61914-48074 */
}else{
- assert( sqlite3BtreeCursorIsValid(pC->pCursor) );
- rc = sqlite3BtreeKeySize(pC->pCursor, &v);
+ assert( sqlite3BtreeCursorIsValid(pC->uc.pCursor) );
+ rc = sqlite3BtreeKeySize(pC->uc.pCursor, &v);
assert( rc==SQLITE_OK ); /* Cannot fail following BtreeLast() */
if( v>=MAX_ROWID ){
pC->useRandomRowid = 1;
@@ -74903,7 +80859,7 @@ case OP_NewRowid: { /* out2 */
pMem = &pFrame->aMem[pOp->p3];
}else{
/* Assert that P3 is a valid memory cell. */
- assert( pOp->p3<=(p->nMem-p->nCursor) );
+ assert( pOp->p3<=(p->nMem+1 - p->nCursor) );
pMem = &aMem[pOp->p3];
memAboutToChange(p, pMem);
}
@@ -74933,11 +80889,12 @@ case OP_NewRowid: { /* out2 */
do{
sqlite3_randomness(sizeof(v), &v);
v &= (MAX_ROWID>>1); v++; /* Ensure that v is greater than zero */
- }while( ((rc = sqlite3BtreeMovetoUnpacked(pC->pCursor, 0, (u64)v,
+ }while( ((rc = sqlite3BtreeMovetoUnpacked(pC->uc.pCursor, 0, (u64)v,
0, &res))==SQLITE_OK)
&& (res==0)
&& (++cnt<100));
- if( rc==SQLITE_OK && res==0 ){
+ if( rc ) goto abort_due_to_error;
+ if( res==0 ){
rc = SQLITE_FULL; /* IMP: R-38219-53002 */
goto abort_due_to_error;
}
@@ -74977,9 +80934,9 @@ case OP_NewRowid: { /* out2 */
** is part of an INSERT operation. The difference is only important to
** the update hook.
**
-** Parameter P4 may point to a string containing the table-name, or
-** may be NULL. If it is not NULL, then the update-hook
-** (sqlite3.xUpdateCallback) is invoked following a successful insert.
+** Parameter P4 may point to a Table structure, or may be NULL. If it is
+** not NULL, then the update-hook (sqlite3.xUpdateCallback) is invoked
+** following a successful insert.
**
** (WARNING/TODO: If P1 is a pseudo-cursor and P2 is dynamically
** allocated, then ownership of P2 is transferred to the pseudo-cursor
@@ -75005,17 +80962,19 @@ case OP_InsertInt: {
int nZero; /* Number of zero-bytes to append */
int seekResult; /* Result of prior seek or 0 if no USESEEKRESULT flag */
const char *zDb; /* database name - used by the update hook */
- const char *zTbl; /* Table name - used by the opdate hook */
+ Table *pTab; /* Table structure - used by update and pre-update hooks */
int op; /* Opcode for update hook: SQLITE_UPDATE or SQLITE_INSERT */
+ op = 0;
pData = &aMem[pOp->p2];
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
assert( memIsValid(pData) );
pC = p->apCsr[pOp->p1];
assert( pC!=0 );
- assert( pC->pCursor!=0 );
- assert( pC->pseudoTableReg==0 );
+ assert( pC->eCurType==CURTYPE_BTREE );
+ assert( pC->uc.pCursor!=0 );
assert( pC->isTable );
+ assert( pOp->p4type==P4_TABLE || pOp->p4type>=P4_STATIC );
REGISTER_TRACE(pOp->p2, pData);
if( pOp->opcode==OP_Insert ){
@@ -75029,6 +80988,28 @@ case OP_InsertInt: {
iKey = pOp->p3;
}
+ if( pOp->p4type==P4_TABLE && HAS_UPDATE_HOOK(db) ){
+ assert( pC->isTable );
+ assert( pC->iDb>=0 );
+ zDb = db->aDb[pC->iDb].zName;
+ pTab = pOp->p4.pTab;
+ assert( HasRowid(pTab) );
+ op = ((pOp->p5 & OPFLAG_ISUPDATE) ? SQLITE_UPDATE : SQLITE_INSERT);
+ }else{
+ pTab = 0; /* Not needed. Silence a comiler warning. */
+ zDb = 0; /* Not needed. Silence a compiler warning. */
+ }
+
+#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
+ /* Invoke the pre-update hook, if any */
+ if( db->xPreUpdateCallback
+ && pOp->p4type==P4_TABLE
+ && !(pOp->p5 & OPFLAG_ISUPDATE)
+ ){
+ sqlite3VdbePreUpdateHook(p, pC, SQLITE_INSERT, zDb, pTab, iKey, pOp->p2);
+ }
+#endif
+
if( pOp->p5 & OPFLAG_NCHANGE ) p->nChange++;
if( pOp->p5 & OPFLAG_LASTROWID ) db->lastRowid = lastRowid = iKey;
if( pData->flags & MEM_Null ){
@@ -75043,7 +81024,7 @@ case OP_InsertInt: {
}else{
nZero = 0;
}
- rc = sqlite3BtreeInsert(pC->pCursor, 0, iKey,
+ rc = sqlite3BtreeInsert(pC->uc.pCursor, 0, iKey,
pData->z, pData->n, nZero,
(pOp->p5 & OPFLAG_APPEND)!=0, seekResult
);
@@ -75051,67 +81032,137 @@ case OP_InsertInt: {
pC->cacheStatus = CACHE_STALE;
/* Invoke the update-hook if required. */
- if( rc==SQLITE_OK && db->xUpdateCallback && pOp->p4.z ){
- zDb = db->aDb[pC->iDb].zName;
- zTbl = pOp->p4.z;
- op = ((pOp->p5 & OPFLAG_ISUPDATE) ? SQLITE_UPDATE : SQLITE_INSERT);
- assert( pC->isTable );
- db->xUpdateCallback(db->pUpdateArg, op, zDb, zTbl, iKey);
- assert( pC->iDb>=0 );
+ if( rc ) goto abort_due_to_error;
+ if( db->xUpdateCallback && op ){
+ db->xUpdateCallback(db->pUpdateArg, op, zDb, pTab->zName, iKey);
}
break;
}
-/* Opcode: Delete P1 P2 * P4 *
+/* Opcode: Delete P1 P2 P3 P4 P5
**
** Delete the record at which the P1 cursor is currently pointing.
**
-** The cursor will be left pointing at either the next or the previous
+** If the OPFLAG_SAVEPOSITION bit of the P5 parameter is set, then
+** the cursor will be left pointing at either the next or the previous
** record in the table. If it is left pointing at the next record, then
-** the next Next instruction will be a no-op. Hence it is OK to delete
-** a record from within a Next loop.
+** the next Next instruction will be a no-op. As a result, in this case
+** it is ok to delete a record from within a Next loop. If
+** OPFLAG_SAVEPOSITION bit of P5 is clear, then the cursor will be
+** left in an undefined state.
**
-** If the OPFLAG_NCHANGE flag of P2 is set, then the row change count is
-** incremented (otherwise not).
+** If the OPFLAG_AUXDELETE bit is set on P5, that indicates that this
+** delete one of several associated with deleting a table row and all its
+** associated index entries. Exactly one of those deletes is the "primary"
+** delete. The others are all on OPFLAG_FORDELETE cursors or else are
+** marked with the AUXDELETE flag.
+**
+** If the OPFLAG_NCHANGE flag of P2 (NB: P2 not P5) is set, then the row
+** change count is incremented (otherwise not).
**
** P1 must not be pseudo-table. It has to be a real table with
** multiple rows.
**
-** If P4 is not NULL, then it is the name of the table that P1 is
-** pointing to. The update hook will be invoked, if it exists.
-** If P4 is not NULL then the P1 cursor must have been positioned
-** using OP_NotFound prior to invoking this opcode.
+** If P4 is not NULL then it points to a Table struture. In this case either
+** the update or pre-update hook, or both, may be invoked. The P1 cursor must
+** have been positioned using OP_NotFound prior to invoking this opcode in
+** this case. Specifically, if one is configured, the pre-update hook is
+** invoked if P4 is not NULL. The update-hook is invoked if one is configured,
+** P4 is not NULL, and the OPFLAG_NCHANGE flag is set in P2.
+**
+** If the OPFLAG_ISUPDATE flag is set in P2, then P3 contains the address
+** of the memory cell that contains the value that the rowid of the row will
+** be set to by the update.
*/
case OP_Delete: {
VdbeCursor *pC;
+ const char *zDb;
+ Table *pTab;
+ int opflags;
+ opflags = pOp->p2;
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
pC = p->apCsr[pOp->p1];
assert( pC!=0 );
- assert( pC->pCursor!=0 ); /* Only valid for real tables, no pseudotables */
+ assert( pC->eCurType==CURTYPE_BTREE );
+ assert( pC->uc.pCursor!=0 );
assert( pC->deferredMoveto==0 );
#ifdef SQLITE_DEBUG
- /* The seek operation that positioned the cursor prior to OP_Delete will
- ** have also set the pC->movetoTarget field to the rowid of the row that
- ** is being deleted */
- if( pOp->p4.z && pC->isTable ){
+ if( pOp->p4type==P4_TABLE && HasRowid(pOp->p4.pTab) && pOp->p5==0 ){
+ /* If p5 is zero, the seek operation that positioned the cursor prior to
+ ** OP_Delete will have also set the pC->movetoTarget field to the rowid of
+ ** the row that is being deleted */
i64 iKey = 0;
- sqlite3BtreeKeySize(pC->pCursor, &iKey);
- assert( pC->movetoTarget==iKey );
+ sqlite3BtreeKeySize(pC->uc.pCursor, &iKey);
+ assert( pC->movetoTarget==iKey );
}
#endif
+
+ /* If the update-hook or pre-update-hook will be invoked, set zDb to
+ ** the name of the db to pass as to it. Also set local pTab to a copy
+ ** of p4.pTab. Finally, if p5 is true, indicating that this cursor was
+ ** last moved with OP_Next or OP_Prev, not Seek or NotFound, set
+ ** VdbeCursor.movetoTarget to the current rowid. */
+ if( pOp->p4type==P4_TABLE && HAS_UPDATE_HOOK(db) ){
+ assert( pC->iDb>=0 );
+ assert( pOp->p4.pTab!=0 );
+ zDb = db->aDb[pC->iDb].zName;
+ pTab = pOp->p4.pTab;
+ if( (pOp->p5 & OPFLAG_SAVEPOSITION)!=0 && pC->isTable ){
+ sqlite3BtreeKeySize(pC->uc.pCursor, &pC->movetoTarget);
+ }
+ }else{
+ zDb = 0; /* Not needed. Silence a compiler warning. */
+ pTab = 0; /* Not needed. Silence a compiler warning. */
+ }
+
+#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
+ /* Invoke the pre-update-hook if required. */
+ if( db->xPreUpdateCallback && pOp->p4.pTab && HasRowid(pTab) ){
+ assert( !(opflags & OPFLAG_ISUPDATE) || (aMem[pOp->p3].flags & MEM_Int) );
+ sqlite3VdbePreUpdateHook(p, pC,
+ (opflags & OPFLAG_ISUPDATE) ? SQLITE_UPDATE : SQLITE_DELETE,
+ zDb, pTab, pC->movetoTarget,
+ pOp->p3
+ );
+ }
+ if( opflags & OPFLAG_ISNOOP ) break;
+#endif
- rc = sqlite3BtreeDelete(pC->pCursor);
+ /* Only flags that can be set are SAVEPOISTION and AUXDELETE */
+ assert( (pOp->p5 & ~(OPFLAG_SAVEPOSITION|OPFLAG_AUXDELETE))==0 );
+ assert( OPFLAG_SAVEPOSITION==BTREE_SAVEPOSITION );
+ assert( OPFLAG_AUXDELETE==BTREE_AUXDELETE );
+
+#ifdef SQLITE_DEBUG
+ if( p->pFrame==0 ){
+ if( pC->isEphemeral==0
+ && (pOp->p5 & OPFLAG_AUXDELETE)==0
+ && (pC->wrFlag & OPFLAG_FORDELETE)==0
+ ){
+ nExtraDelete++;
+ }
+ if( pOp->p2 & OPFLAG_NCHANGE ){
+ nExtraDelete--;
+ }
+ }
+#endif
+
+ rc = sqlite3BtreeDelete(pC->uc.pCursor, pOp->p5);
pC->cacheStatus = CACHE_STALE;
+ if( rc ) goto abort_due_to_error;
/* Invoke the update-hook if required. */
- if( rc==SQLITE_OK && db->xUpdateCallback && pOp->p4.z && pC->isTable ){
- db->xUpdateCallback(db->pUpdateArg, SQLITE_DELETE,
- db->aDb[pC->iDb].zName, pOp->p4.z, pC->movetoTarget);
- assert( pC->iDb>=0 );
+ if( opflags & OPFLAG_NCHANGE ){
+ p->nChange++;
+ if( db->xUpdateCallback && HasRowid(pTab) ){
+ db->xUpdateCallback(db->pUpdateArg, SQLITE_DELETE, zDb, pTab->zName,
+ pC->movetoTarget);
+ assert( pC->iDb>=0 );
+ }
}
- if( pOp->p2 & OPFLAG_NCHANGE ) p->nChange++;
+
break;
}
/* Opcode: ResetCount * * * * *
@@ -75155,6 +81206,7 @@ case OP_SorterCompare: {
res = 0;
rc = sqlite3VdbeSorterCompare(pC, pIn3, nKeyCol, &res);
VdbeBranchTaken(res!=0,2);
+ if( rc ) goto abort_due_to_error;
if( res ) goto jump_to_p2;
break;
};
@@ -75180,6 +81232,7 @@ case OP_SorterData: {
rc = sqlite3VdbeSorterRowkey(pC, pOut);
assert( rc!=SQLITE_OK || (pOut->flags & MEM_Blob) );
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
+ if( rc ) goto abort_due_to_error;
p->apCsr[pOp->p3]->cacheStatus = CACHE_STALE;
break;
}
@@ -75219,14 +81272,14 @@ case OP_RowData: {
/* Note that RowKey and RowData are really exactly the same instruction */
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
pC = p->apCsr[pOp->p1];
+ assert( pC!=0 );
+ assert( pC->eCurType==CURTYPE_BTREE );
assert( isSorter(pC)==0 );
assert( pC->isTable || pOp->opcode!=OP_RowData );
assert( pC->isTable==0 || pOp->opcode==OP_RowData );
- assert( pC!=0 );
assert( pC->nullRow==0 );
- assert( pC->pseudoTableReg==0 );
- assert( pC->pCursor!=0 );
- pCrsr = pC->pCursor;
+ assert( pC->uc.pCursor!=0 );
+ pCrsr = pC->uc.pCursor;
/* The OP_RowKey and OP_RowData opcodes always follow OP_NotExists or
** OP_Rewind/Op_Next with no intervening instructions that might invalidate
@@ -75268,6 +81321,7 @@ case OP_RowData: {
}else{
rc = sqlite3BtreeData(pCrsr, 0, n, pOut->z);
}
+ if( rc ) goto abort_due_to_error;
pOut->enc = SQLITE_UTF8; /* In case the blob is ever cast to text */
UPDATE_MAX_BLOBSIZE(pOut);
REGISTER_TRACE(pOp->p2, pOut);
@@ -75294,29 +81348,32 @@ case OP_Rowid: { /* out2 */
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
pC = p->apCsr[pOp->p1];
assert( pC!=0 );
- assert( pC->pseudoTableReg==0 || pC->nullRow );
+ assert( pC->eCurType!=CURTYPE_PSEUDO || pC->nullRow );
if( pC->nullRow ){
pOut->flags = MEM_Null;
break;
}else if( pC->deferredMoveto ){
v = pC->movetoTarget;
#ifndef SQLITE_OMIT_VIRTUALTABLE
- }else if( pC->pVtabCursor ){
- pVtab = pC->pVtabCursor->pVtab;
+ }else if( pC->eCurType==CURTYPE_VTAB ){
+ assert( pC->uc.pVCur!=0 );
+ pVtab = pC->uc.pVCur->pVtab;
pModule = pVtab->pModule;
assert( pModule->xRowid );
- rc = pModule->xRowid(pC->pVtabCursor, &v);
+ rc = pModule->xRowid(pC->uc.pVCur, &v);
sqlite3VtabImportErrmsg(p, pVtab);
+ if( rc ) goto abort_due_to_error;
#endif /* SQLITE_OMIT_VIRTUALTABLE */
}else{
- assert( pC->pCursor!=0 );
+ assert( pC->eCurType==CURTYPE_BTREE );
+ assert( pC->uc.pCursor!=0 );
rc = sqlite3VdbeCursorRestore(pC);
if( rc ) goto abort_due_to_error;
if( pC->nullRow ){
pOut->flags = MEM_Null;
break;
}
- rc = sqlite3BtreeKeySize(pC->pCursor, &v);
+ rc = sqlite3BtreeKeySize(pC->uc.pCursor, &v);
assert( rc==SQLITE_OK ); /* Always so because of CursorRestore() above */
}
pOut->u.i = v;
@@ -75337,8 +81394,9 @@ case OP_NullRow: {
assert( pC!=0 );
pC->nullRow = 1;
pC->cacheStatus = CACHE_STALE;
- if( pC->pCursor ){
- sqlite3BtreeClearCursor(pC->pCursor);
+ if( pC->eCurType==CURTYPE_BTREE ){
+ assert( pC->uc.pCursor!=0 );
+ sqlite3BtreeClearCursor(pC->uc.pCursor);
}
break;
}
@@ -75363,7 +81421,8 @@ case OP_Last: { /* jump */
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
pC = p->apCsr[pOp->p1];
assert( pC!=0 );
- pCrsr = pC->pCursor;
+ assert( pC->eCurType==CURTYPE_BTREE );
+ pCrsr = pC->uc.pCursor;
res = 0;
assert( pCrsr!=0 );
rc = sqlite3BtreeLast(pCrsr, &res);
@@ -75374,6 +81433,7 @@ case OP_Last: { /* jump */
#ifdef SQLITE_DEBUG
pC->seekOp = OP_Last;
#endif
+ if( rc ) goto abort_due_to_error;
if( pOp->p2>0 ){
VdbeBranchTaken(res!=0,2);
if( res ) goto jump_to_p2;
@@ -75431,12 +81491,14 @@ case OP_Rewind: { /* jump */
if( isSorter(pC) ){
rc = sqlite3VdbeSorterRewind(pC, &res);
}else{
- pCrsr = pC->pCursor;
+ assert( pC->eCurType==CURTYPE_BTREE );
+ pCrsr = pC->uc.pCursor;
assert( pCrsr );
rc = sqlite3BtreeFirst(pCrsr, &res);
pC->deferredMoveto = 0;
pC->cacheStatus = CACHE_STALE;
}
+ if( rc ) goto abort_due_to_error;
pC->nullRow = (u8)res;
assert( pOp->p2>0 && pOp->p2<p->nOp );
VdbeBranchTaken(res!=0,2);
@@ -75528,7 +81590,7 @@ case OP_Next: /* jump */
res = pOp->p3;
assert( pC!=0 );
assert( pC->deferredMoveto==0 );
- assert( pC->pCursor );
+ assert( pC->eCurType==CURTYPE_BTREE );
assert( res==0 || (res==1 && pC->isTable==0) );
testcase( res==1 );
assert( pOp->opcode!=OP_Next || pOp->p4.xAdvance==sqlite3BtreeNext );
@@ -75545,10 +81607,11 @@ case OP_Next: /* jump */
|| pC->seekOp==OP_SeekLT || pC->seekOp==OP_SeekLE
|| pC->seekOp==OP_Last );
- rc = pOp->p4.xAdvance(pC->pCursor, &res);
+ rc = pOp->p4.xAdvance(pC->uc.pCursor, &res);
next_tail:
pC->cacheStatus = CACHE_STALE;
VdbeBranchTaken(res==0,2);
+ if( rc ) goto abort_due_to_error;
if( res==0 ){
pC->nullRow = 0;
p->aCounter[pOp->p5]++;
@@ -75586,7 +81649,6 @@ next_tail:
case OP_SorterInsert: /* in2 */
case OP_IdxInsert: { /* in2 */
VdbeCursor *pC;
- BtCursor *pCrsr;
int nKey;
const char *zKey;
@@ -75596,24 +81658,23 @@ case OP_IdxInsert: { /* in2 */
assert( isSorter(pC)==(pOp->opcode==OP_SorterInsert) );
pIn2 = &aMem[pOp->p2];
assert( pIn2->flags & MEM_Blob );
- pCrsr = pC->pCursor;
if( pOp->p5 & OPFLAG_NCHANGE ) p->nChange++;
- assert( pCrsr!=0 );
+ assert( pC->eCurType==CURTYPE_BTREE || pOp->opcode==OP_SorterInsert );
assert( pC->isTable==0 );
rc = ExpandBlob(pIn2);
- if( rc==SQLITE_OK ){
- if( isSorter(pC) ){
- rc = sqlite3VdbeSorterWrite(pC, pIn2);
- }else{
- nKey = pIn2->n;
- zKey = pIn2->z;
- rc = sqlite3BtreeInsert(pCrsr, zKey, nKey, "", 0, 0, pOp->p3,
- ((pOp->p5 & OPFLAG_USESEEKRESULT) ? pC->seekResult : 0)
- );
- assert( pC->deferredMoveto==0 );
- pC->cacheStatus = CACHE_STALE;
- }
+ if( rc ) goto abort_due_to_error;
+ if( pOp->opcode==OP_SorterInsert ){
+ rc = sqlite3VdbeSorterWrite(pC, pIn2);
+ }else{
+ nKey = pIn2->n;
+ zKey = pIn2->z;
+ rc = sqlite3BtreeInsert(pC->uc.pCursor, zKey, nKey, "", 0, 0, pOp->p3,
+ ((pOp->p5 & OPFLAG_USESEEKRESULT) ? pC->seekResult : 0)
+ );
+ assert( pC->deferredMoveto==0 );
+ pC->cacheStatus = CACHE_STALE;
}
+ if( rc) goto abort_due_to_error;
break;
}
@@ -75631,29 +81692,48 @@ case OP_IdxDelete: {
UnpackedRecord r;
assert( pOp->p3>0 );
- assert( pOp->p2>0 && pOp->p2+pOp->p3<=(p->nMem-p->nCursor)+1 );
+ assert( pOp->p2>0 && pOp->p2+pOp->p3<=(p->nMem+1 - p->nCursor)+1 );
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
pC = p->apCsr[pOp->p1];
assert( pC!=0 );
- pCrsr = pC->pCursor;
+ assert( pC->eCurType==CURTYPE_BTREE );
+ pCrsr = pC->uc.pCursor;
assert( pCrsr!=0 );
assert( pOp->p5==0 );
r.pKeyInfo = pC->pKeyInfo;
r.nField = (u16)pOp->p3;
r.default_rc = 0;
r.aMem = &aMem[pOp->p2];
-#ifdef SQLITE_DEBUG
- { int i; for(i=0; i<r.nField; i++) assert( memIsValid(&r.aMem[i]) ); }
-#endif
rc = sqlite3BtreeMovetoUnpacked(pCrsr, &r, 0, 0, &res);
- if( rc==SQLITE_OK && res==0 ){
- rc = sqlite3BtreeDelete(pCrsr);
+ if( rc ) goto abort_due_to_error;
+ if( res==0 ){
+ rc = sqlite3BtreeDelete(pCrsr, BTREE_AUXDELETE);
+ if( rc ) goto abort_due_to_error;
}
assert( pC->deferredMoveto==0 );
pC->cacheStatus = CACHE_STALE;
break;
}
+/* Opcode: Seek P1 * P3 P4 *
+** Synopsis: Move P3 to P1.rowid
+**
+** P1 is an open index cursor and P3 is a cursor on the corresponding
+** table. This opcode does a deferred seek of the P3 table cursor
+** to the row that corresponds to the current row of P1.
+**
+** This is a deferred seek. Nothing actually happens until
+** the cursor is used to read a record. That way, if no reads
+** occur, no unnecessary I/O happens.
+**
+** P4 may be an array of integers (type P4_INTARRAY) containing
+** one entry for each column in the P3 table. If array entry a(i)
+** is non-zero, then reading column a(i)-1 from cursor P3 is
+** equivalent to performing the deferred seek and then reading column i
+** from P1. This information is stored in P3 and used to redirect
+** reads against P3 over to P1, thus possibly avoiding the need to
+** seek and read cursor P3.
+*/
/* Opcode: IdxRowid P1 P2 * * *
** Synopsis: r[P2]=rowid
**
@@ -75663,36 +81743,57 @@ case OP_IdxDelete: {
**
** See also: Rowid, MakeRecord.
*/
+case OP_Seek:
case OP_IdxRowid: { /* out2 */
- BtCursor *pCrsr;
- VdbeCursor *pC;
- i64 rowid;
+ VdbeCursor *pC; /* The P1 index cursor */
+ VdbeCursor *pTabCur; /* The P2 table cursor (OP_Seek only) */
+ i64 rowid; /* Rowid that P1 current points to */
- pOut = out2Prerelease(p, pOp);
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
pC = p->apCsr[pOp->p1];
assert( pC!=0 );
- pCrsr = pC->pCursor;
- assert( pCrsr!=0 );
- pOut->flags = MEM_Null;
+ assert( pC->eCurType==CURTYPE_BTREE );
+ assert( pC->uc.pCursor!=0 );
assert( pC->isTable==0 );
assert( pC->deferredMoveto==0 );
+ assert( !pC->nullRow || pOp->opcode==OP_IdxRowid );
- /* sqlite3VbeCursorRestore() can only fail if the record has been deleted
- ** out from under the cursor. That will never happend for an IdxRowid
- ** opcode, hence the NEVER() arround the check of the return value.
- */
+ /* The IdxRowid and Seek opcodes are combined because of the commonality
+ ** of sqlite3VdbeCursorRestore() and sqlite3VdbeIdxRowid(). */
rc = sqlite3VdbeCursorRestore(pC);
+
+ /* sqlite3VbeCursorRestore() can only fail if the record has been deleted
+ ** out from under the cursor. That will never happens for an IdxRowid
+ ** or Seek opcode */
if( NEVER(rc!=SQLITE_OK) ) goto abort_due_to_error;
if( !pC->nullRow ){
rowid = 0; /* Not needed. Only used to silence a warning. */
- rc = sqlite3VdbeIdxRowid(db, pCrsr, &rowid);
+ rc = sqlite3VdbeIdxRowid(db, pC->uc.pCursor, &rowid);
if( rc!=SQLITE_OK ){
goto abort_due_to_error;
}
- pOut->u.i = rowid;
- pOut->flags = MEM_Int;
+ if( pOp->opcode==OP_Seek ){
+ assert( pOp->p3>=0 && pOp->p3<p->nCursor );
+ pTabCur = p->apCsr[pOp->p3];
+ assert( pTabCur!=0 );
+ assert( pTabCur->eCurType==CURTYPE_BTREE );
+ assert( pTabCur->uc.pCursor!=0 );
+ assert( pTabCur->isTable );
+ pTabCur->nullRow = 0;
+ pTabCur->movetoTarget = rowid;
+ pTabCur->deferredMoveto = 1;
+ assert( pOp->p4type==P4_INTARRAY || pOp->p4.ai==0 );
+ pTabCur->aAltMap = pOp->p4.ai;
+ pTabCur->pAltCursor = pC;
+ }else{
+ pOut = out2Prerelease(p, pOp);
+ pOut->u.i = rowid;
+ pOut->flags = MEM_Int;
+ }
+ }else{
+ assert( pOp->opcode==OP_IdxRowid );
+ sqlite3VdbeMemSetNull(&aMem[pOp->p2]);
}
break;
}
@@ -75753,7 +81854,8 @@ case OP_IdxGE: { /* jump */
pC = p->apCsr[pOp->p1];
assert( pC!=0 );
assert( pC->isOrdered );
- assert( pC->pCursor!=0);
+ assert( pC->eCurType==CURTYPE_BTREE );
+ assert( pC->uc.pCursor!=0);
assert( pC->deferredMoveto==0 );
assert( pOp->p5==0 || pOp->p5==1 );
assert( pOp->p4type==P4_INT32 );
@@ -75781,6 +81883,7 @@ case OP_IdxGE: { /* jump */
res++;
}
VdbeBranchTaken(res>0,2);
+ if( rc ) goto abort_due_to_error;
if( res>0 ) goto jump_to_p2;
break;
}
@@ -75810,11 +81913,13 @@ case OP_Destroy: { /* out2 */
int iDb;
assert( p->readOnly==0 );
+ assert( pOp->p1>1 );
pOut = out2Prerelease(p, pOp);
pOut->flags = MEM_Null;
if( db->nVdbeRead > db->nVDestroy+1 ){
rc = SQLITE_LOCKED;
p->errorAction = OE_Abort;
+ goto abort_due_to_error;
}else{
iDb = pOp->p3;
assert( DbMaskTest(p->btreeMask, iDb) );
@@ -75822,8 +81927,9 @@ case OP_Destroy: { /* out2 */
rc = sqlite3BtreeDropTable(db->aDb[iDb].pBt, pOp->p1, &iMoved);
pOut->flags = MEM_Int;
pOut->u.i = iMoved;
+ if( rc ) goto abort_due_to_error;
#ifndef SQLITE_OMIT_AUTOVACUUM
- if( rc==SQLITE_OK && iMoved!=0 ){
+ if( iMoved!=0 ){
sqlite3RootPageMoved(db, iDb, iMoved, pOp->p1);
/* All OP_Destroy operations occur on the same btree */
assert( resetSchemaOnFault==0 || resetSchemaOnFault==iDb+1 );
@@ -75869,6 +81975,7 @@ case OP_Clear: {
aMem[pOp->p3].u.i += nChange;
}
}
+ if( rc ) goto abort_due_to_error;
break;
}
@@ -75886,11 +81993,13 @@ case OP_ResetSorter: {
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
pC = p->apCsr[pOp->p1];
assert( pC!=0 );
- if( pC->pSorter ){
- sqlite3VdbeSorterReset(db, pC->pSorter);
+ if( isSorter(pC) ){
+ sqlite3VdbeSorterReset(db, pC->uc.pSorter);
}else{
+ assert( pC->eCurType==CURTYPE_BTREE );
assert( pC->isEphemeral );
- rc = sqlite3BtreeClearTableOfCursor(pC->pCursor);
+ rc = sqlite3BtreeClearTableOfCursor(pC->uc.pCursor);
+ if( rc ) goto abort_due_to_error;
}
break;
}
@@ -75939,6 +82048,7 @@ case OP_CreateTable: { /* out2 */
flags = BTREE_BLOBKEY;
}
rc = sqlite3BtreeCreateTable(pDb->pBt, &pgno, flags);
+ if( rc ) goto abort_due_to_error;
pOut->u.i = pgno;
break;
}
@@ -75979,7 +82089,7 @@ case OP_ParseSchema: {
"SELECT name, rootpage, sql FROM '%q'.%s WHERE %s ORDER BY rowid",
db->aDb[iDb].zName, zMaster, pOp->p4.z);
if( zSql==0 ){
- rc = SQLITE_NOMEM;
+ rc = SQLITE_NOMEM_BKPT;
}else{
assert( db->init.busy==0 );
db->init.busy = 1;
@@ -75991,9 +82101,12 @@ case OP_ParseSchema: {
db->init.busy = 0;
}
}
- if( rc ) sqlite3ResetAllSchemasOfConnection(db);
- if( rc==SQLITE_NOMEM ){
- goto no_mem;
+ if( rc ){
+ sqlite3ResetAllSchemasOfConnection(db);
+ if( rc==SQLITE_NOMEM ){
+ goto no_mem;
+ }
+ goto abort_due_to_error;
}
break;
}
@@ -76008,6 +82121,7 @@ case OP_ParseSchema: {
case OP_LoadAnalysis: {
assert( pOp->p1>=0 && pOp->p1<db->nDb );
rc = sqlite3AnalysisLoad(db, pOp->p1);
+ if( rc ) goto abort_due_to_error;
break;
}
#endif /* !defined(SQLITE_OMIT_ANALYZE) */
@@ -76053,7 +82167,7 @@ case OP_DropTrigger: {
#ifndef SQLITE_OMIT_INTEGRITY_CHECK
-/* Opcode: IntegrityCk P1 P2 P3 * P5
+/* Opcode: IntegrityCk P1 P2 P3 P4 P5
**
** Do an analysis of the currently open database. Store in
** register P1 the text of an error message describing any problems.
@@ -76064,9 +82178,8 @@ case OP_DropTrigger: {
** In other words, the analysis stops as soon as reg(P1) errors are
** seen. Reg(P1) is updated with the number of errors remaining.
**
-** The root page numbers of all tables in the database are integer
-** stored in reg(P1), reg(P1+1), reg(P1+2), .... There are P2 tables
-** total.
+** The root page numbers of all tables in the database are integers
+** stored in P4_INTARRAY argument.
**
** If P5 is not zero, the check is done on the auxiliary database
** file, not the main database file.
@@ -76076,30 +82189,24 @@ case OP_DropTrigger: {
case OP_IntegrityCk: {
int nRoot; /* Number of tables to check. (Number of root pages.) */
int *aRoot; /* Array of rootpage numbers for tables to be checked */
- int j; /* Loop counter */
int nErr; /* Number of errors reported */
char *z; /* Text of the error report */
Mem *pnErr; /* Register keeping track of errors remaining */
assert( p->bIsReader );
nRoot = pOp->p2;
+ aRoot = pOp->p4.ai;
assert( nRoot>0 );
- aRoot = sqlite3DbMallocRaw(db, sizeof(int)*(nRoot+1) );
- if( aRoot==0 ) goto no_mem;
- assert( pOp->p3>0 && pOp->p3<=(p->nMem-p->nCursor) );
+ assert( aRoot[nRoot]==0 );
+ assert( pOp->p3>0 && pOp->p3<=(p->nMem+1 - p->nCursor) );
pnErr = &aMem[pOp->p3];
assert( (pnErr->flags & MEM_Int)!=0 );
assert( (pnErr->flags & (MEM_Str|MEM_Blob))==0 );
pIn1 = &aMem[pOp->p1];
- for(j=0; j<nRoot; j++){
- aRoot[j] = (int)sqlite3VdbeIntValue(&pIn1[j]);
- }
- aRoot[j] = 0;
assert( pOp->p5<db->nDb );
assert( DbMaskTest(p->btreeMask, pOp->p5) );
z = sqlite3BtreeIntegrityCheck(db->aDb[pOp->p5].pBt, aRoot, nRoot,
(int)pnErr->u.i, &nErr);
- sqlite3DbFree(db, aRoot);
pnErr->u.i -= nErr;
sqlite3VdbeMemSetNull(pIn1);
if( nErr==0 ){
@@ -76266,8 +82373,8 @@ case OP_Program: { /* jump */
if( p->nFrame>=db->aLimit[SQLITE_LIMIT_TRIGGER_DEPTH] ){
rc = SQLITE_ERROR;
- sqlite3SetString(&p->zErrMsg, db, "too many levels of trigger recursion");
- break;
+ sqlite3VdbeError(p, "too many levels of trigger recursion");
+ goto abort_due_to_error;
}
/* Register pRt is used to store the memory required to save the state
@@ -76281,6 +82388,8 @@ case OP_Program: { /* jump */
** variable nMem (and later, VdbeFrame.nChildMem) to this value.
*/
nMem = pProgram->nMem + pProgram->nCsr;
+ assert( nMem>0 );
+ if( pProgram->nCsr==0 ) nMem++;
nByte = ROUND8(sizeof(VdbeFrame))
+ nMem * sizeof(Mem)
+ pProgram->nCsr * sizeof(VdbeCursor *)
@@ -76317,7 +82426,8 @@ case OP_Program: { /* jump */
}
}else{
pFrame = pRt->u.pFrame;
- assert( pProgram->nMem+pProgram->nCsr==pFrame->nChildMem );
+ assert( pProgram->nMem+pProgram->nCsr==pFrame->nChildMem
+ || (pProgram->nCsr==0 && pProgram->nMem+1==pFrame->nChildMem) );
assert( pProgram->nCsr==pFrame->nChildCsr );
assert( (int)(pOp - aOp)==pFrame->pc );
}
@@ -76327,12 +82437,15 @@ case OP_Program: { /* jump */
pFrame->lastRowid = lastRowid;
pFrame->nChange = p->nChange;
pFrame->nDbChange = p->db->nChange;
+ assert( pFrame->pAuxData==0 );
+ pFrame->pAuxData = p->pAuxData;
+ p->pAuxData = 0;
p->nChange = 0;
p->pFrame = pFrame;
- p->aMem = aMem = &VdbeFrameMem(pFrame)[-1];
+ p->aMem = aMem = VdbeFrameMem(pFrame);
p->nMem = pFrame->nChildMem;
p->nCursor = (u16)pFrame->nChildCsr;
- p->apCsr = (VdbeCursor **)&aMem[p->nMem+1];
+ p->apCsr = (VdbeCursor **)&aMem[p->nMem];
p->aOp = aOp = pProgram->aOp;
p->nOp = pProgram->nOp;
p->aOnceFlag = (u8 *)&p->apCsr[p->nCursor];
@@ -76445,12 +82558,12 @@ case OP_MemMax: { /* in2 */
}
#endif /* SQLITE_OMIT_AUTOINCREMENT */
-/* Opcode: IfPos P1 P2 * * *
-** Synopsis: if r[P1]>0 goto P2
+/* Opcode: IfPos P1 P2 P3 * *
+** Synopsis: if r[P1]>0 then r[P1]-=P3, goto 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.
+** If the value of register P1 is 1 or greater, subtract P3 from the
+** value in P1 and jump to P2.
**
** If the initial value of register P1 is less than 1, then the
** value is unchanged and control passes through to the next instruction.
@@ -76459,38 +82572,55 @@ 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 ) goto jump_to_p2;
+ if( pIn1->u.i>0 ){
+ pIn1->u.i -= pOp->p3;
+ goto jump_to_p2;
+ }
break;
}
-/* Opcode: IfNeg P1 P2 P3 * *
-** Synopsis: r[P1]+=P3, if r[P1]<0 goto P2
+/* Opcode: OffsetLimit P1 P2 P3 * *
+** Synopsis: if r[P1]>0 then r[P2]=r[P1]+max(0,r[P3]) else r[P2]=(-1)
**
-** Register P1 must contain an integer. Add literal P3 to the value in
-** register P1 then if the value of register P1 is less than zero, jump to P2.
+** This opcode performs a commonly used computation associated with
+** LIMIT and OFFSET process. r[P1] holds the limit counter. r[P3]
+** holds the offset counter. The opcode computes the combined value
+** of the LIMIT and OFFSET and stores that value in r[P2]. The r[P2]
+** value computed is the total number of rows that will need to be
+** visited in order to complete the query.
+**
+** If r[P3] is zero or negative, that means there is no OFFSET
+** and r[P2] is set to be the value of the LIMIT, r[P1].
+**
+** if r[P1] is zero or negative, that means there is no LIMIT
+** and r[P2] is set to -1.
+**
+** Otherwise, r[P2] is set to the sum of r[P1] and r[P3].
*/
-case OP_IfNeg: { /* jump, in1 */
+case OP_OffsetLimit: { /* in1, out2, in3 */
pIn1 = &aMem[pOp->p1];
- assert( pIn1->flags&MEM_Int );
- pIn1->u.i += pOp->p3;
- VdbeBranchTaken(pIn1->u.i<0, 2);
- if( pIn1->u.i<0 ) goto jump_to_p2;
+ pIn3 = &aMem[pOp->p3];
+ pOut = out2Prerelease(p, pOp);
+ assert( pIn1->flags & MEM_Int );
+ assert( pIn3->flags & MEM_Int );
+ pOut->u.i = pIn1->u.i<=0 ? -1 : pIn1->u.i+(pIn3->u.i>0?pIn3->u.i:0);
break;
}
/* Opcode: IfNotZero P1 P2 P3 * *
-** Synopsis: if r[P1]!=0 then r[P1]+=P3, goto P2
+** 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.
+** initially nonzero, then subtract P3 from the value in register 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;
+ pIn1->u.i -= pOp->p3;
goto jump_to_p2;
}
break;
@@ -76512,72 +82642,102 @@ case OP_DecrJumpZero: { /* jump, in1 */
}
-/* Opcode: JumpZeroIncr P1 P2 * * *
-** Synopsis: if (r[P1]++)==0 ) goto P2
+/* Opcode: AggStep0 * P2 P3 P4 P5
+** Synopsis: accum=r[P3] step(r[P2@P5])
+**
+** Execute the step function for an aggregate. The
+** function has P5 arguments. P4 is a pointer to the FuncDef
+** structure that specifies the function. Register P3 is the
+** accumulator.
**
-** 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.
+** The P5 arguments are taken from register P2 and its
+** successors.
*/
-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;
-}
-
/* Opcode: AggStep * P2 P3 P4 P5
** Synopsis: accum=r[P3] step(r[P2@P5])
**
** Execute the step function for an aggregate. The
-** function has P5 arguments. P4 is a pointer to the FuncDef
-** structure that specifies the function. Use register
-** P3 as the accumulator.
+** function has P5 arguments. P4 is a pointer to an sqlite3_context
+** object that is used to run the function. Register P3 is
+** as the accumulator.
**
** The P5 arguments are taken from register P2 and its
** successors.
+**
+** This opcode is initially coded as OP_AggStep0. On first evaluation,
+** the FuncDef stored in P4 is converted into an sqlite3_context and
+** the opcode is changed. In this way, the initialization of the
+** sqlite3_context only happens once, instead of on each call to the
+** step function.
*/
-case OP_AggStep: {
+case OP_AggStep0: {
int n;
+ sqlite3_context *pCtx;
+
+ assert( pOp->p4type==P4_FUNCDEF );
+ n = pOp->p5;
+ assert( pOp->p3>0 && pOp->p3<=(p->nMem+1 - p->nCursor) );
+ assert( n==0 || (pOp->p2>0 && pOp->p2+n<=(p->nMem+1 - p->nCursor)+1) );
+ assert( pOp->p3<pOp->p2 || pOp->p3>=pOp->p2+n );
+ pCtx = sqlite3DbMallocRawNN(db, sizeof(*pCtx) + (n-1)*sizeof(sqlite3_value*));
+ if( pCtx==0 ) goto no_mem;
+ pCtx->pMem = 0;
+ pCtx->pFunc = pOp->p4.pFunc;
+ pCtx->iOp = (int)(pOp - aOp);
+ pCtx->pVdbe = p;
+ pCtx->argc = n;
+ pOp->p4type = P4_FUNCCTX;
+ pOp->p4.pCtx = pCtx;
+ pOp->opcode = OP_AggStep;
+ /* Fall through into OP_AggStep */
+}
+case OP_AggStep: {
int i;
+ sqlite3_context *pCtx;
Mem *pMem;
- Mem *pRec;
Mem t;
- sqlite3_context ctx;
- sqlite3_value **apVal;
- n = pOp->p5;
- assert( n>=0 );
- pRec = &aMem[pOp->p2];
- apVal = p->apArg;
- assert( apVal || n==0 );
- for(i=0; i<n; i++, pRec++){
- assert( memIsValid(pRec) );
- apVal[i] = pRec;
- memAboutToChange(p, pRec);
+ assert( pOp->p4type==P4_FUNCCTX );
+ pCtx = pOp->p4.pCtx;
+ pMem = &aMem[pOp->p3];
+
+ /* If this function is inside of a trigger, the register array in aMem[]
+ ** might change from one evaluation to the next. The next block of code
+ ** checks to see if the register array has changed, and if so it
+ ** reinitializes the relavant parts of the sqlite3_context object */
+ if( pCtx->pMem != pMem ){
+ pCtx->pMem = pMem;
+ for(i=pCtx->argc-1; i>=0; i--) pCtx->argv[i] = &aMem[pOp->p2+i];
+ }
+
+#ifdef SQLITE_DEBUG
+ for(i=0; i<pCtx->argc; i++){
+ assert( memIsValid(pCtx->argv[i]) );
+ REGISTER_TRACE(pOp->p2+i, pCtx->argv[i]);
}
- ctx.pFunc = pOp->p4.pFunc;
- assert( pOp->p3>0 && pOp->p3<=(p->nMem-p->nCursor) );
- ctx.pMem = pMem = &aMem[pOp->p3];
+#endif
+
pMem->n++;
sqlite3VdbeMemInit(&t, db, MEM_Null);
- ctx.pOut = &t;
- ctx.isError = 0;
- ctx.pVdbe = p;
- ctx.iOp = (int)(pOp - aOp);
- ctx.skipFlag = 0;
- (ctx.pFunc->xStep)(&ctx, n, apVal); /* IMP: R-24505-23230 */
- if( ctx.isError ){
- sqlite3SetString(&p->zErrMsg, db, "%s", sqlite3_value_text(&t));
- rc = ctx.isError;
+ pCtx->pOut = &t;
+ pCtx->fErrorOrAux = 0;
+ pCtx->skipFlag = 0;
+ (pCtx->pFunc->xSFunc)(pCtx,pCtx->argc,pCtx->argv); /* IMP: R-24505-23230 */
+ if( pCtx->fErrorOrAux ){
+ if( pCtx->isError ){
+ sqlite3VdbeError(p, "%s", sqlite3_value_text(&t));
+ rc = pCtx->isError;
+ }
+ sqlite3VdbeMemRelease(&t);
+ if( rc ) goto abort_due_to_error;
+ }else{
+ assert( t.flags==MEM_Null );
}
- if( ctx.skipFlag ){
+ if( pCtx->skipFlag ){
assert( pOp[-1].opcode==OP_CollSeq );
i = pOp[-1].p1;
if( i ) sqlite3VdbeMemSetInt64(&aMem[i], 1);
}
- sqlite3VdbeMemRelease(&t);
break;
}
@@ -76596,12 +82756,13 @@ case OP_AggStep: {
*/
case OP_AggFinal: {
Mem *pMem;
- assert( pOp->p1>0 && pOp->p1<=(p->nMem-p->nCursor) );
+ assert( pOp->p1>0 && pOp->p1<=(p->nMem+1 - p->nCursor) );
pMem = &aMem[pOp->p1];
assert( (pMem->flags & ~(MEM_Null|MEM_Agg))==0 );
rc = sqlite3VdbeMemFinalize(pMem, pOp->p4.pFunc);
if( rc ){
- sqlite3SetString(&p->zErrMsg, db, "%s", sqlite3_value_text(pMem));
+ sqlite3VdbeError(p, "%s", sqlite3_value_text(pMem));
+ goto abort_due_to_error;
}
sqlite3VdbeChangeEncoding(pMem, encoding);
UPDATE_MAX_BLOBSIZE(pMem);
@@ -76637,7 +82798,8 @@ case OP_Checkpoint: {
|| pOp->p2==SQLITE_CHECKPOINT_TRUNCATE
);
rc = sqlite3Checkpoint(db, pOp->p1, pOp->p2, &aRes[1], &aRes[2]);
- if( rc==SQLITE_BUSY ){
+ if( rc ){
+ if( rc!=SQLITE_BUSY ) goto abort_due_to_error;
rc = SQLITE_OK;
aRes[0] = 1;
}
@@ -76706,11 +82868,11 @@ case OP_JournalMode: { /* out2 */
){
if( !db->autoCommit || db->nVdbeRead>1 ){
rc = SQLITE_ERROR;
- sqlite3SetString(&p->zErrMsg, db,
+ sqlite3VdbeError(p,
"cannot change %s wal mode from within a transaction",
(eNew==PAGER_JOURNALMODE_WAL ? "into" : "out of")
);
- break;
+ goto abort_due_to_error;
}else{
if( eOld==PAGER_JOURNALMODE_WAL ){
@@ -76740,9 +82902,7 @@ case OP_JournalMode: { /* out2 */
}
#endif /* ifndef SQLITE_OMIT_WAL */
- if( rc ){
- eNew = eOld;
- }
+ if( rc ) eNew = eOld;
eNew = sqlite3PagerSetJournalMode(pPager, eNew);
pOut->flags = MEM_Str|MEM_Static|MEM_Term;
@@ -76750,6 +82910,7 @@ case OP_JournalMode: { /* out2 */
pOut->n = sqlite3Strlen30(pOut->z);
pOut->enc = SQLITE_UTF8;
sqlite3VdbeChangeEncoding(pOut, encoding);
+ if( rc ) goto abort_due_to_error;
break;
};
#endif /* SQLITE_OMIT_PRAGMA */
@@ -76764,6 +82925,7 @@ case OP_JournalMode: { /* out2 */
case OP_Vacuum: {
assert( p->readOnly==0 );
rc = sqlite3RunVacuum(&p->zErrMsg, db);
+ if( rc ) goto abort_due_to_error;
break;
}
#endif
@@ -76784,7 +82946,8 @@ case OP_IncrVacuum: { /* jump */
pBt = db->aDb[pOp->p1].pBt;
rc = sqlite3BtreeIncrVacuum(pBt);
VdbeBranchTaken(rc==SQLITE_DONE,2);
- if( rc==SQLITE_DONE ){
+ if( rc ){
+ if( rc!=SQLITE_DONE ) goto abort_due_to_error;
rc = SQLITE_OK;
goto jump_to_p2;
}
@@ -76835,9 +82998,12 @@ case OP_TableLock: {
assert( DbMaskTest(p->btreeMask, p1) );
assert( isWriteLock==0 || isWriteLock==1 );
rc = sqlite3BtreeLockTable(db->aDb[p1].pBt, pOp->p2, isWriteLock);
- if( (rc&0xFF)==SQLITE_LOCKED ){
- const char *z = pOp->p4.z;
- sqlite3SetString(&p->zErrMsg, db, "database table is locked: %s", z);
+ if( rc ){
+ if( (rc&0xFF)==SQLITE_LOCKED ){
+ const char *z = pOp->p4.z;
+ sqlite3VdbeError(p, "database table is locked: %s", z);
+ }
+ goto abort_due_to_error;
}
}
break;
@@ -76859,6 +83025,7 @@ case OP_VBegin: {
pVTab = pOp->p4.pVtab;
rc = sqlite3VtabBegin(db, pVTab);
if( pVTab ) sqlite3VtabImportErrmsg(p, pVTab->pVtab);
+ if( rc ) goto abort_due_to_error;
break;
}
#endif /* SQLITE_OMIT_VIRTUALTABLE */
@@ -76887,6 +83054,7 @@ case OP_VCreate: {
rc = sqlite3VtabCallCreate(db, pOp->p1, zTab, &p->zErrMsg);
}
sqlite3VdbeMemRelease(&sMem);
+ if( rc ) goto abort_due_to_error;
break;
}
#endif /* SQLITE_OMIT_VIRTUALTABLE */
@@ -76901,6 +83069,7 @@ case OP_VDestroy: {
db->nVDestroy++;
rc = sqlite3VtabCallDestroy(db, pOp->p1, pOp->p4.z);
db->nVDestroy--;
+ if( rc ) goto abort_due_to_error;
break;
}
#endif /* SQLITE_OMIT_VIRTUALTABLE */
@@ -76914,35 +83083,35 @@ case OP_VDestroy: {
*/
case OP_VOpen: {
VdbeCursor *pCur;
- sqlite3_vtab_cursor *pVtabCursor;
+ sqlite3_vtab_cursor *pVCur;
sqlite3_vtab *pVtab;
const sqlite3_module *pModule;
assert( p->bIsReader );
pCur = 0;
- pVtabCursor = 0;
+ pVCur = 0;
pVtab = pOp->p4.pVtab->pVtab;
if( pVtab==0 || NEVER(pVtab->pModule==0) ){
rc = SQLITE_LOCKED;
- break;
+ goto abort_due_to_error;
}
pModule = pVtab->pModule;
- rc = pModule->xOpen(pVtab, &pVtabCursor);
+ rc = pModule->xOpen(pVtab, &pVCur);
sqlite3VtabImportErrmsg(p, pVtab);
- if( SQLITE_OK==rc ){
- /* Initialize sqlite3_vtab_cursor base class */
- pVtabCursor->pVtab = pVtab;
+ if( rc ) goto abort_due_to_error;
- /* Initialize vdbe cursor object */
- pCur = allocateCursor(p, pOp->p1, 0, -1, 0);
- if( pCur ){
- pCur->pVtabCursor = pVtabCursor;
- pVtab->nRef++;
- }else{
- assert( db->mallocFailed );
- pModule->xClose(pVtabCursor);
- goto no_mem;
- }
+ /* Initialize sqlite3_vtab_cursor base class */
+ pVCur->pVtab = pVtab;
+
+ /* Initialize vdbe cursor object */
+ pCur = allocateCursor(p, pOp->p1, 0, -1, CURTYPE_VTAB);
+ if( pCur ){
+ pCur->uc.pVCur = pVCur;
+ pVtab->nRef++;
+ }else{
+ assert( db->mallocFailed );
+ pModule->xClose(pVCur);
+ goto no_mem;
}
break;
}
@@ -76974,7 +83143,7 @@ case OP_VFilter: { /* jump */
const sqlite3_module *pModule;
Mem *pQuery;
Mem *pArgc;
- sqlite3_vtab_cursor *pVtabCursor;
+ sqlite3_vtab_cursor *pVCur;
sqlite3_vtab *pVtab;
VdbeCursor *pCur;
int res;
@@ -76986,9 +83155,9 @@ case OP_VFilter: { /* jump */
pCur = p->apCsr[pOp->p1];
assert( memIsValid(pQuery) );
REGISTER_TRACE(pOp->p3, pQuery);
- assert( pCur->pVtabCursor );
- pVtabCursor = pCur->pVtabCursor;
- pVtab = pVtabCursor->pVtab;
+ assert( pCur->eCurType==CURTYPE_VTAB );
+ pVCur = pCur->uc.pVCur;
+ pVtab = pVCur->pVtab;
pModule = pVtab->pModule;
/* Grab the index number and argc parameters */
@@ -77002,11 +83171,10 @@ case OP_VFilter: { /* jump */
for(i = 0; i<nArg; i++){
apArg[i] = &pArgc[i+1];
}
- rc = pModule->xFilter(pVtabCursor, iQuery, pOp->p4.z, nArg, apArg);
+ rc = pModule->xFilter(pVCur, iQuery, pOp->p4.z, nArg, apArg);
sqlite3VtabImportErrmsg(p, pVtab);
- if( rc==SQLITE_OK ){
- res = pModule->xEof(pVtabCursor);
- }
+ if( rc ) goto abort_due_to_error;
+ res = pModule->xEof(pVCur);
pCur->nullRow = 0;
VdbeBranchTaken(res!=0,2);
if( res ) goto jump_to_p2;
@@ -77029,21 +83197,21 @@ case OP_VColumn: {
sqlite3_context sContext;
VdbeCursor *pCur = p->apCsr[pOp->p1];
- assert( pCur->pVtabCursor );
- assert( pOp->p3>0 && pOp->p3<=(p->nMem-p->nCursor) );
+ assert( pCur->eCurType==CURTYPE_VTAB );
+ assert( pOp->p3>0 && pOp->p3<=(p->nMem+1 - p->nCursor) );
pDest = &aMem[pOp->p3];
memAboutToChange(p, pDest);
if( pCur->nullRow ){
sqlite3VdbeMemSetNull(pDest);
break;
}
- pVtab = pCur->pVtabCursor->pVtab;
+ pVtab = pCur->uc.pVCur->pVtab;
pModule = pVtab->pModule;
assert( pModule->xColumn );
memset(&sContext, 0, sizeof(sContext));
sContext.pOut = pDest;
MemSetTypeFlag(pDest, MEM_Null);
- rc = pModule->xColumn(pCur->pVtabCursor, &sContext, pOp->p2);
+ rc = pModule->xColumn(pCur->uc.pVCur, &sContext, pOp->p2);
sqlite3VtabImportErrmsg(p, pVtab);
if( sContext.isError ){
rc = sContext.isError;
@@ -77055,6 +83223,7 @@ case OP_VColumn: {
if( sqlite3VdbeMemTooBig(pDest) ){
goto too_big;
}
+ if( rc ) goto abort_due_to_error;
break;
}
#endif /* SQLITE_OMIT_VIRTUALTABLE */
@@ -77074,11 +83243,11 @@ case OP_VNext: { /* jump */
res = 0;
pCur = p->apCsr[pOp->p1];
- assert( pCur->pVtabCursor );
+ assert( pCur->eCurType==CURTYPE_VTAB );
if( pCur->nullRow ){
break;
}
- pVtab = pCur->pVtabCursor->pVtab;
+ pVtab = pCur->uc.pVCur->pVtab;
pModule = pVtab->pModule;
assert( pModule->xNext );
@@ -77088,11 +83257,10 @@ 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.
*/
- rc = pModule->xNext(pCur->pVtabCursor);
+ rc = pModule->xNext(pCur->uc.pVCur);
sqlite3VtabImportErrmsg(p, pVtab);
- if( rc==SQLITE_OK ){
- res = pModule->xEof(pCur->pVtabCursor);
- }
+ if( rc ) goto abort_due_to_error;
+ res = pModule->xEof(pCur->uc.pVCur);
VdbeBranchTaken(!res,2);
if( !res ){
/* If there is data, jump to P2 */
@@ -77124,11 +83292,11 @@ case OP_VRename: {
testcase( pName->enc==SQLITE_UTF16BE );
testcase( pName->enc==SQLITE_UTF16LE );
rc = sqlite3VdbeChangeEncoding(pName, SQLITE_UTF8);
- if( rc==SQLITE_OK ){
- rc = pVtab->pModule->xRename(pVtab, pName->z);
- sqlite3VtabImportErrmsg(p, pVtab);
- p->expired = 0;
- }
+ if( rc ) goto abort_due_to_error;
+ rc = pVtab->pModule->xRename(pVtab, pName->z);
+ sqlite3VtabImportErrmsg(p, pVtab);
+ p->expired = 0;
+ if( rc ) goto abort_due_to_error;
break;
}
#endif
@@ -77177,7 +83345,7 @@ case OP_VUpdate: {
pVtab = pOp->p4.pVtab->pVtab;
if( pVtab==0 || NEVER(pVtab->pModule==0) ){
rc = SQLITE_LOCKED;
- break;
+ goto abort_due_to_error;
}
pModule = pVtab->pModule;
nArg = pOp->p2;
@@ -77209,6 +83377,7 @@ case OP_VUpdate: {
}else{
p->nChange++;
}
+ if( rc ) goto abort_due_to_error;
}
break;
}
@@ -77300,6 +83469,28 @@ case OP_Init: { /* jump */
break;
}
+#ifdef SQLITE_ENABLE_CURSOR_HINTS
+/* Opcode: CursorHint P1 * * P4 *
+**
+** Provide a hint to cursor P1 that it only needs to return rows that
+** satisfy the Expr in P4. TK_REGISTER terms in the P4 expression refer
+** to values currently held in registers. TK_COLUMN terms in the P4
+** expression refer to columns in the b-tree to which cursor P1 is pointing.
+*/
+case OP_CursorHint: {
+ VdbeCursor *pC;
+
+ assert( pOp->p1>=0 && pOp->p1<p->nCursor );
+ assert( pOp->p4type==P4_EXPR );
+ pC = p->apCsr[pOp->p1];
+ if( pC ){
+ assert( pC->eCurType==CURTYPE_BTREE );
+ sqlite3BtreeCursorHint(pC->uc.pCursor, BTREE_HINT_RANGE,
+ pOp->p4.pExpr, aMem);
+ }
+ break;
+}
+#endif /* SQLITE_ENABLE_CURSOR_HINTS */
/* Opcode: Noop * * * * *
**
@@ -77343,11 +83534,12 @@ default: { /* This is really OP_Noop and OP_Explain */
#ifdef SQLITE_DEBUG
if( db->flags & SQLITE_VdbeTrace ){
+ u8 opProperty = sqlite3OpcodeProperty[pOrigOp->opcode];
if( rc!=0 ) printf("rc=%d\n",rc);
- if( pOrigOp->opflags & (OPFLG_OUT2) ){
+ if( opProperty & (OPFLG_OUT2) ){
registerTrace(pOrigOp->p2, &aMem[pOrigOp->p2]);
}
- if( pOrigOp->opflags & OPFLG_OUT3 ){
+ if( opProperty & OPFLG_OUT3 ){
registerTrace(pOrigOp->p3, &aMem[pOrigOp->p3]);
}
}
@@ -77358,14 +83550,19 @@ default: { /* This is really OP_Noop and OP_Explain */
/* If we reach this point, it means that execution is finished with
** an error of some kind.
*/
-vdbe_error_halt:
+abort_due_to_error:
+ if( db->mallocFailed ) rc = SQLITE_NOMEM_BKPT;
assert( rc );
+ if( p->zErrMsg==0 && rc!=SQLITE_IOERR_NOMEM ){
+ sqlite3VdbeError(p, "%s", sqlite3ErrStr(rc));
+ }
p->rc = rc;
+ sqlite3SystemError(db, rc);
testcase( sqlite3GlobalConfig.xLog!=0 );
sqlite3_log(rc, "statement aborts at %d: [%s] %s",
(int)(pOp - aOp), p->zSql, p->zErrMsg);
sqlite3VdbeHalt(p);
- if( rc==SQLITE_IOERR_NOMEM ) db->mallocFailed = 1;
+ if( rc==SQLITE_IOERR_NOMEM ) sqlite3OomFault(db);
rc = SQLITE_ERROR;
if( resetSchemaOnFault>0 ){
sqlite3ResetOneSchema(db, resetSchemaOnFault-1);
@@ -77379,44 +83576,36 @@ vdbe_return:
testcase( nVmStep>0 );
p->aCounter[SQLITE_STMTSTATUS_VM_STEP] += (int)nVmStep;
sqlite3VdbeLeave(p);
+ assert( rc!=SQLITE_OK || nExtraDelete==0
+ || sqlite3_strlike("DELETE%",p->zSql,0)!=0
+ );
return rc;
/* Jump to here if a string or blob larger than SQLITE_MAX_LENGTH
** is encountered.
*/
too_big:
- sqlite3SetString(&p->zErrMsg, db, "string or blob too big");
+ sqlite3VdbeError(p, "string or blob too big");
rc = SQLITE_TOOBIG;
- goto vdbe_error_halt;
+ goto abort_due_to_error;
/* Jump to here if a malloc() fails.
*/
no_mem:
- db->mallocFailed = 1;
- sqlite3SetString(&p->zErrMsg, db, "out of memory");
- rc = SQLITE_NOMEM;
- goto vdbe_error_halt;
-
- /* Jump to here for any other kind of fatal error. The "rc" variable
- ** should hold the error number.
- */
-abort_due_to_error:
- assert( p->zErrMsg==0 );
- if( db->mallocFailed ) rc = SQLITE_NOMEM;
- if( rc!=SQLITE_IOERR_NOMEM ){
- sqlite3SetString(&p->zErrMsg, db, "%s", sqlite3ErrStr(rc));
- }
- goto vdbe_error_halt;
+ sqlite3OomFault(db);
+ sqlite3VdbeError(p, "out of memory");
+ rc = SQLITE_NOMEM_BKPT;
+ goto abort_due_to_error;
/* Jump to here if the sqlite3_interrupt() API sets the interrupt
** flag.
*/
abort_due_to_interrupt:
assert( db->u1.isInterrupted );
- rc = SQLITE_INTERRUPT;
+ rc = db->mallocFailed ? SQLITE_NOMEM_BKPT : SQLITE_INTERRUPT;
p->rc = rc;
- sqlite3SetString(&p->zErrMsg, db, "%s", sqlite3ErrStr(rc));
- goto vdbe_error_halt;
+ sqlite3VdbeError(p, "%s", sqlite3ErrStr(rc));
+ goto abort_due_to_error;
}
@@ -77437,6 +83626,8 @@ abort_due_to_interrupt:
** This file contains code used to implement incremental BLOB I/O.
*/
+/* #include "sqliteInt.h" */
+/* #include "vdbeInt.h" */
#ifndef SQLITE_OMIT_INCRBLOB
@@ -77452,6 +83643,8 @@ struct Incrblob {
BtCursor *pCsr; /* Cursor pointing at blob row */
sqlite3_stmt *pStmt; /* Statement holding cursor open */
sqlite3 *db; /* The associated database */
+ char *zDb; /* Database name */
+ Table *pTab; /* Table object */
};
@@ -77498,7 +83691,7 @@ static int blobSeekToRow(Incrblob *p, sqlite3_int64 iRow, char **pzErr){
}else{
p->iOffset = pC->aType[p->iCol + pC->nField];
p->nByte = sqlite3VdbeSerialTypeLen(type);
- p->pCsr = pC->pCursor;
+ p->pCsr = pC->uc.pCursor;
sqlite3BtreeIncrblobCursor(p->pCsr);
}
}
@@ -77537,38 +83730,6 @@ SQLITE_API int SQLITE_STDCALL sqlite3_blob_open(
){
int nAttempt = 0;
int iCol; /* Index of zColumn in row-record */
-
- /* This VDBE program seeks a btree cursor to the identified
- ** db/table/row entry. The reason for using a vdbe program instead
- ** of writing code to use the b-tree layer directly is that the
- ** vdbe program will take advantage of the various transaction,
- ** locking and error handling infrastructure built into the vdbe.
- **
- ** After seeking the cursor, the vdbe executes an OP_ResultRow.
- ** Code external to the Vdbe then "borrows" the b-tree cursor and
- ** uses it to implement the blob_read(), blob_write() and
- ** blob_bytes() functions.
- **
- ** The sqlite3_blob_close() function finalizes the vdbe program,
- ** which closes the b-tree cursor and (possibly) commits the
- ** transaction.
- */
- static const int iLn = VDBE_OFFSET_LINENO(4);
- static const VdbeOpList openBlob[] = {
- /* {OP_Transaction, 0, 0, 0}, // 0: Inserted separately */
- {OP_TableLock, 0, 0, 0}, /* 1: Acquire a read or write lock */
- /* One of the following two instructions is replaced by an OP_Noop. */
- {OP_OpenRead, 0, 0, 0}, /* 2: Open cursor 0 for reading */
- {OP_OpenWrite, 0, 0, 0}, /* 3: Open cursor 0 for read/write */
- {OP_Variable, 1, 1, 1}, /* 4: Push the rowid to the stack */
- {OP_NotExists, 0, 10, 1}, /* 5: Seek the cursor */
- {OP_Column, 0, 0, 1}, /* 6 */
- {OP_ResultRow, 1, 0, 0}, /* 7 */
- {OP_Goto, 0, 4, 0}, /* 8 */
- {OP_Close, 0, 0, 0}, /* 9 */
- {OP_Halt, 0, 0, 0}, /* 10 */
- };
-
int rc = SQLITE_OK;
char *zErr = 0;
Table *pTab;
@@ -77627,6 +83788,8 @@ SQLITE_API int SQLITE_STDCALL sqlite3_blob_open(
sqlite3BtreeLeaveAll(db);
goto blob_open_out;
}
+ pBlob->pTab = pTab;
+ pBlob->zDb = db->aDb[sqlite3SchemaToIndex(db, pTab->pSchema)].zName;
/* Now search pTab for the exact column. */
for(iCol=0; iCol<pTab->nCol; iCol++) {
@@ -77669,7 +83832,8 @@ SQLITE_API int SQLITE_STDCALL sqlite3_blob_open(
for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
int j;
for(j=0; j<pIdx->nKeyCol; j++){
- if( pIdx->aiColumn[j]==iCol ){
+ /* FIXME: Be smarter about indexes that use expressions */
+ if( pIdx->aiColumn[j]==iCol || pIdx->aiColumn[j]==XN_EXPR ){
zFault = "indexed";
}
}
@@ -77686,45 +83850,78 @@ SQLITE_API int SQLITE_STDCALL sqlite3_blob_open(
pBlob->pStmt = (sqlite3_stmt *)sqlite3VdbeCreate(pParse);
assert( pBlob->pStmt || db->mallocFailed );
if( pBlob->pStmt ){
+
+ /* This VDBE program seeks a btree cursor to the identified
+ ** db/table/row entry. The reason for using a vdbe program instead
+ ** of writing code to use the b-tree layer directly is that the
+ ** vdbe program will take advantage of the various transaction,
+ ** locking and error handling infrastructure built into the vdbe.
+ **
+ ** After seeking the cursor, the vdbe executes an OP_ResultRow.
+ ** Code external to the Vdbe then "borrows" the b-tree cursor and
+ ** uses it to implement the blob_read(), blob_write() and
+ ** blob_bytes() functions.
+ **
+ ** The sqlite3_blob_close() function finalizes the vdbe program,
+ ** which closes the b-tree cursor and (possibly) commits the
+ ** transaction.
+ */
+ static const int iLn = VDBE_OFFSET_LINENO(2);
+ static const VdbeOpList openBlob[] = {
+ {OP_TableLock, 0, 0, 0}, /* 0: Acquire a read or write lock */
+ {OP_OpenRead, 0, 0, 0}, /* 1: Open a cursor */
+ {OP_Variable, 1, 1, 0}, /* 2: Move ?1 into reg[1] */
+ {OP_NotExists, 0, 7, 1}, /* 3: Seek the cursor */
+ {OP_Column, 0, 0, 1}, /* 4 */
+ {OP_ResultRow, 1, 0, 0}, /* 5 */
+ {OP_Goto, 0, 2, 0}, /* 6 */
+ {OP_Close, 0, 0, 0}, /* 7 */
+ {OP_Halt, 0, 0, 0}, /* 8 */
+ };
Vdbe *v = (Vdbe *)pBlob->pStmt;
int iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
-
+ VdbeOp *aOp;
sqlite3VdbeAddOp4Int(v, OP_Transaction, iDb, flags,
pTab->pSchema->schema_cookie,
pTab->pSchema->iGeneration);
sqlite3VdbeChangeP5(v, 1);
- sqlite3VdbeAddOpList(v, ArraySize(openBlob), openBlob, iLn);
+ aOp = sqlite3VdbeAddOpList(v, ArraySize(openBlob), openBlob, iLn);
/* Make sure a mutex is held on the table to be accessed */
sqlite3VdbeUsesBtree(v, iDb);
- /* Configure the OP_TableLock instruction */
+ if( db->mallocFailed==0 ){
+ assert( aOp!=0 );
+ /* Configure the OP_TableLock instruction */
#ifdef SQLITE_OMIT_SHARED_CACHE
- sqlite3VdbeChangeToNoop(v, 1);
+ aOp[0].opcode = OP_Noop;
#else
- sqlite3VdbeChangeP1(v, 1, iDb);
- sqlite3VdbeChangeP2(v, 1, pTab->tnum);
- sqlite3VdbeChangeP3(v, 1, flags);
- sqlite3VdbeChangeP4(v, 1, pTab->zName, P4_TRANSIENT);
-#endif
-
- /* Remove either the OP_OpenWrite or OpenRead. Set the P2
- ** parameter of the other to pTab->tnum. */
- sqlite3VdbeChangeToNoop(v, 3 - flags);
- sqlite3VdbeChangeP2(v, 2 + flags, pTab->tnum);
- sqlite3VdbeChangeP3(v, 2 + flags, iDb);
-
- /* Configure the number of columns. Configure the cursor to
- ** think that the table has one more column than it really
- ** does. An OP_Column to retrieve this imaginary column will
- ** always return an SQL NULL. This is useful because it means
- ** we can invoke OP_Column to fill in the vdbe cursors type
- ** and offset cache without causing any IO.
- */
- sqlite3VdbeChangeP4(v, 2+flags, SQLITE_INT_TO_PTR(pTab->nCol+1),P4_INT32);
- sqlite3VdbeChangeP2(v, 6, pTab->nCol);
- if( !db->mallocFailed ){
+ aOp[0].p1 = iDb;
+ aOp[0].p2 = pTab->tnum;
+ aOp[0].p3 = flags;
+ sqlite3VdbeChangeP4(v, 1, pTab->zName, P4_TRANSIENT);
+ }
+ if( db->mallocFailed==0 ){
+#endif
+
+ /* Remove either the OP_OpenWrite or OpenRead. Set the P2
+ ** parameter of the other to pTab->tnum. */
+ if( flags ) aOp[1].opcode = OP_OpenWrite;
+ aOp[1].p2 = pTab->tnum;
+ aOp[1].p3 = iDb;
+
+ /* Configure the number of columns. Configure the cursor to
+ ** think that the table has one more column than it really
+ ** does. An OP_Column to retrieve this imaginary column will
+ ** always return an SQL NULL. This is useful because it means
+ ** we can invoke OP_Column to fill in the vdbe cursors type
+ ** and offset cache without causing any IO.
+ */
+ aOp[1].p4type = P4_INT32;
+ aOp[1].p4.i = pTab->nCol+1;
+ aOp[4].p2 = pTab->nCol;
+
pParse->nVar = 1;
pParse->nMem = 1;
pParse->nTab = 1;
@@ -77814,6 +84011,30 @@ static int blobReadWrite(
*/
assert( db == v->db );
sqlite3BtreeEnterCursor(p->pCsr);
+
+#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
+ if( xCall==sqlite3BtreePutData && db->xPreUpdateCallback ){
+ /* If a pre-update hook is registered and this is a write cursor,
+ ** invoke it here.
+ **
+ ** TODO: The preupdate-hook is passed SQLITE_DELETE, even though this
+ ** operation should really be an SQLITE_UPDATE. This is probably
+ ** incorrect, but is convenient because at this point the new.* values
+ ** are not easily obtainable. And for the sessions module, an
+ ** SQLITE_UPDATE where the PK columns do not change is handled in the
+ ** same way as an SQLITE_DELETE (the SQLITE_DELETE code is actually
+ ** slightly more efficient). Since you cannot write to a PK column
+ ** using the incremental-blob API, this works. For the sessions module
+ ** anyhow.
+ */
+ sqlite3_int64 iKey;
+ sqlite3BtreeKeySize(p->pCsr, &iKey);
+ sqlite3VdbePreUpdateHook(
+ v, v->apCsr[0], SQLITE_DELETE, p->zDb, p->pTab, iKey, -1
+ );
+ }
+#endif
+
rc = xCall(p->pCsr, iOffset+p->iOffset, n, z);
sqlite3BtreeLeaveCursor(p->pCsr);
if( rc==SQLITE_ABORT ){
@@ -78035,6 +84256,8 @@ SQLITE_API int SQLITE_STDCALL sqlite3_blob_reopen(sqlite3_blob *pBlob, sqlite3_i
** thread to merge the output of each of the others to a single PMA for
** the main thread to read from.
*/
+/* #include "sqliteInt.h" */
+/* #include "vdbeInt.h" */
/*
** If SQLITE_DEBUG_SORTER_THREADS is defined, this module outputs various
@@ -78438,7 +84661,7 @@ static int vdbePmaReadBlob(
int nNew = MAX(128, p->nAlloc*2);
while( nByte>nNew ) nNew = nNew*2;
aNew = sqlite3Realloc(p->aAlloc, nNew);
- if( !aNew ) return SQLITE_NOMEM;
+ if( !aNew ) return SQLITE_NOMEM_BKPT;
p->nAlloc = nNew;
p->aAlloc = aNew;
}
@@ -78550,7 +84773,7 @@ static int vdbePmaReaderSeek(
int iBuf = pReadr->iReadOff % pgsz;
if( pReadr->aBuffer==0 ){
pReadr->aBuffer = (u8*)sqlite3Malloc(pgsz);
- if( pReadr->aBuffer==0 ) rc = SQLITE_NOMEM;
+ if( pReadr->aBuffer==0 ) rc = SQLITE_NOMEM_BKPT;
pReadr->nBuffer = pgsz;
}
if( rc==SQLITE_OK && iBuf ){
@@ -78635,7 +84858,7 @@ static int vdbePmaReaderInit(
rc = vdbePmaReaderSeek(pTask, pReadr, pFile, iStart);
if( rc==SQLITE_OK ){
- u64 nByte; /* Size of PMA in bytes */
+ u64 nByte = 0; /* Size of PMA in bytes */
rc = vdbePmaReadVarint(pReadr, &nByte);
pReadr->iEof = pReadr->iReadOff + nByte;
*pnByte += nByte;
@@ -78829,7 +85052,6 @@ SQLITE_PRIVATE int sqlite3VdbeSorterInit(
){
int pgsz; /* Page size of main database */
int i; /* Used to iterate through aTask[] */
- int mxCache; /* Cache size */
VdbeSorter *pSorter; /* The new sorter */
KeyInfo *pKeyInfo; /* Copy of pCsr->pKeyInfo with db==0 */
int szKeyInfo; /* Size of pCsr->pKeyInfo in bytes */
@@ -78859,13 +85081,14 @@ SQLITE_PRIVATE int sqlite3VdbeSorterInit(
#endif
assert( pCsr->pKeyInfo && pCsr->pBt==0 );
+ assert( pCsr->eCurType==CURTYPE_SORTER );
szKeyInfo = sizeof(KeyInfo) + (pCsr->pKeyInfo->nField-1)*sizeof(CollSeq*);
sz = sizeof(VdbeSorter) + nWorker * sizeof(SortSubtask);
pSorter = (VdbeSorter*)sqlite3DbMallocZero(db, sz + szKeyInfo);
- pCsr->pSorter = pSorter;
+ pCsr->uc.pSorter = pSorter;
if( pSorter==0 ){
- rc = SQLITE_NOMEM;
+ rc = SQLITE_NOMEM_BKPT;
}else{
pSorter->pKeyInfo = pKeyInfo = (KeyInfo*)((u8*)pSorter + sz);
memcpy(pKeyInfo, pCsr->pKeyInfo, szKeyInfo);
@@ -78876,7 +85099,7 @@ SQLITE_PRIVATE int sqlite3VdbeSorterInit(
}
pSorter->pgsz = pgsz = sqlite3BtreeGetPageSize(db->aDb[0].pBt);
pSorter->nTask = nWorker + 1;
- pSorter->iPrev = nWorker-1;
+ pSorter->iPrev = (u8)(nWorker - 1);
pSorter->bUseThreads = (pSorter->nTask>1);
pSorter->db = db;
for(i=0; i<pSorter->nTask; i++){
@@ -78885,11 +85108,20 @@ SQLITE_PRIVATE int sqlite3VdbeSorterInit(
}
if( !sqlite3TempInMemory(db) ){
+ i64 mxCache; /* Cache size in bytes*/
u32 szPma = sqlite3GlobalConfig.szPma;
pSorter->mnPmaSize = szPma * pgsz;
+
mxCache = db->aDb[0].pSchema->cache_size;
- if( mxCache<(int)szPma ) mxCache = (int)szPma;
- pSorter->mxPmaSize = MIN((i64)mxCache*pgsz, SQLITE_MAX_PMASZ);
+ if( mxCache<0 ){
+ /* A negative cache-size value C indicates that the cache is abs(C)
+ ** KiB in size. */
+ mxCache = mxCache * -1024;
+ }else{
+ mxCache = mxCache * pgsz;
+ }
+ mxCache = MIN(mxCache, SQLITE_MAX_PMASZ);
+ pSorter->mxPmaSize = MAX(pSorter->mnPmaSize, (int)mxCache);
/* EVIDENCE-OF: R-26747-61719 When the application provides any amount of
** scratch memory using SQLITE_CONFIG_SCRATCH, SQLite avoids unnecessary
@@ -78899,7 +85131,7 @@ SQLITE_PRIVATE int sqlite3VdbeSorterInit(
assert( pSorter->iMemory==0 );
pSorter->nMemory = pgsz;
pSorter->list.aMemory = (u8*)sqlite3Malloc(pgsz);
- if( !pSorter->list.aMemory ) rc = SQLITE_NOMEM;
+ if( !pSorter->list.aMemory ) rc = SQLITE_NOMEM_BKPT;
}
}
@@ -79147,12 +85379,14 @@ SQLITE_PRIVATE void sqlite3VdbeSorterReset(sqlite3 *db, VdbeSorter *pSorter){
** Free any cursor components allocated by sqlite3VdbeSorterXXX routines.
*/
SQLITE_PRIVATE void sqlite3VdbeSorterClose(sqlite3 *db, VdbeCursor *pCsr){
- VdbeSorter *pSorter = pCsr->pSorter;
+ VdbeSorter *pSorter;
+ assert( pCsr->eCurType==CURTYPE_SORTER );
+ pSorter = pCsr->uc.pSorter;
if( pSorter ){
sqlite3VdbeSorterReset(db, pSorter);
sqlite3_free(pSorter->list.aMemory);
sqlite3DbFree(db, pSorter);
- pCsr->pSorter = 0;
+ pCsr->uc.pSorter = 0;
}
}
@@ -79219,7 +85453,7 @@ static int vdbeSortAllocUnpacked(SortSubtask *pTask){
pTask->pSorter->pKeyInfo, 0, 0, &pFree
);
assert( pTask->pUnpacked==(UnpackedRecord*)pFree );
- if( pFree==0 ) return SQLITE_NOMEM;
+ if( pFree==0 ) return SQLITE_NOMEM_BKPT;
pTask->pUnpacked->nField = pTask->pSorter->pKeyInfo->nField;
pTask->pUnpacked->errCode = 0;
}
@@ -79294,7 +85528,7 @@ static int vdbeSorterSort(SortSubtask *pTask, SorterList *pList){
aSlot = (SorterRecord **)sqlite3MallocZero(64 * sizeof(SorterRecord *));
if( !aSlot ){
- return SQLITE_NOMEM;
+ return SQLITE_NOMEM_BKPT;
}
while( p ){
@@ -79344,7 +85578,7 @@ static void vdbePmaWriterInit(
memset(p, 0, sizeof(PmaWriter));
p->aBuffer = (u8*)sqlite3Malloc(nBuf);
if( !p->aBuffer ){
- p->eFWErr = SQLITE_NOMEM;
+ p->eFWErr = SQLITE_NOMEM_BKPT;
}else{
p->iBufEnd = p->iBufStart = (iStart % nBuf);
p->iWriteOff = iStart - p->iBufStart;
@@ -79632,7 +85866,7 @@ static int vdbeSorterFlushPMA(VdbeSorter *pSorter){
pSorter->nMemory = sqlite3MallocSize(aMem);
}else if( pSorter->list.aMemory ){
pSorter->list.aMemory = sqlite3Malloc(pSorter->nMemory);
- if( !pSorter->list.aMemory ) return SQLITE_NOMEM;
+ if( !pSorter->list.aMemory ) return SQLITE_NOMEM_BKPT;
}
rc = vdbeSorterCreateThread(pTask, vdbeSorterFlushThread, pCtx);
@@ -79650,15 +85884,16 @@ SQLITE_PRIVATE int sqlite3VdbeSorterWrite(
const VdbeCursor *pCsr, /* Sorter cursor */
Mem *pVal /* Memory cell containing record */
){
- VdbeSorter *pSorter = pCsr->pSorter;
+ VdbeSorter *pSorter;
int rc = SQLITE_OK; /* Return Code */
SorterRecord *pNew; /* New list element */
-
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 */
+ assert( pCsr->eCurType==CURTYPE_SORTER );
+ pSorter = pCsr->uc.pSorter;
getVarint32((const u8*)&pVal->z[1], t);
if( t>0 && t<10 && t!=7 ){
pSorter->typeMask &= SORTER_TYPE_INTEGER;
@@ -79715,27 +85950,28 @@ SQLITE_PRIVATE int sqlite3VdbeSorterWrite(
if( nMin>pSorter->nMemory ){
u8 *aNew;
+ int iListOff = (u8*)pSorter->list.pList - pSorter->list.aMemory;
int nNew = pSorter->nMemory * 2;
while( nNew < nMin ) nNew = nNew*2;
if( nNew > pSorter->mxPmaSize ) nNew = pSorter->mxPmaSize;
if( nNew < nMin ) nNew = nMin;
aNew = sqlite3Realloc(pSorter->list.aMemory, nNew);
- if( !aNew ) return SQLITE_NOMEM;
- pSorter->list.pList = (SorterRecord*)(
- aNew + ((u8*)pSorter->list.pList - pSorter->list.aMemory)
- );
+ if( !aNew ) return SQLITE_NOMEM_BKPT;
+ pSorter->list.pList = (SorterRecord*)&aNew[iListOff];
pSorter->list.aMemory = aNew;
pSorter->nMemory = nNew;
}
pNew = (SorterRecord*)&pSorter->list.aMemory[pSorter->iMemory];
pSorter->iMemory += ROUND8(nReq);
- pNew->u.iNext = (int)((u8*)(pSorter->list.pList) - pSorter->list.aMemory);
+ if( pSorter->list.pList ){
+ pNew->u.iNext = (int)((u8*)(pSorter->list.pList) - pSorter->list.aMemory);
+ }
}else{
pNew = (SorterRecord *)sqlite3Malloc(nReq);
if( pNew==0 ){
- return SQLITE_NOMEM;
+ return SQLITE_NOMEM_BKPT;
}
pNew->u.pNext = pSorter->list.pList;
}
@@ -79882,7 +86118,7 @@ static int vdbeIncrMergerNew(
pTask->file2.iEof += pIncr->mxSz;
}else{
vdbeMergeEngineFree(pMerger);
- rc = SQLITE_NOMEM;
+ rc = SQLITE_NOMEM_BKPT;
}
return rc;
}
@@ -80187,10 +86423,10 @@ static int vdbeMergeEngineLevel0(
int rc = SQLITE_OK;
*ppOut = pNew = vdbeMergeEngineNew(nPMA);
- if( pNew==0 ) rc = SQLITE_NOMEM;
+ if( pNew==0 ) rc = SQLITE_NOMEM_BKPT;
for(i=0; i<nPMA && rc==SQLITE_OK; i++){
- i64 nDummy;
+ i64 nDummy = 0;
PmaReader *pReadr = &pNew->aReadr[i];
rc = vdbePmaReaderInit(pTask, &pTask->file, iOff, pReadr, &nDummy);
iOff = pReadr->iEof;
@@ -80258,7 +86494,7 @@ static int vdbeSorterAddToTree(
if( pReadr->pIncr==0 ){
MergeEngine *pNew = vdbeMergeEngineNew(SORTER_MAX_MERGE_COUNT);
if( pNew==0 ){
- rc = SQLITE_NOMEM;
+ rc = SQLITE_NOMEM_BKPT;
}else{
rc = vdbeIncrMergerNew(pTask, pNew, &pReadr->pIncr);
}
@@ -80303,7 +86539,7 @@ static int vdbeSorterMergeTreeBuild(
assert( pSorter->bUseThreads || pSorter->nTask==1 );
if( pSorter->nTask>1 ){
pMain = vdbeMergeEngineNew(pSorter->nTask);
- if( pMain==0 ) rc = SQLITE_NOMEM;
+ if( pMain==0 ) rc = SQLITE_NOMEM_BKPT;
}
#endif
@@ -80321,7 +86557,7 @@ static int vdbeSorterMergeTreeBuild(
int i;
int iSeq = 0;
pRoot = vdbeMergeEngineNew(SORTER_MAX_MERGE_COUNT);
- if( pRoot==0 ) rc = SQLITE_NOMEM;
+ if( pRoot==0 ) rc = SQLITE_NOMEM_BKPT;
for(i=0; i<pTask->nPMA && rc==SQLITE_OK; i += SORTER_MAX_MERGE_COUNT){
MergeEngine *pMerger = 0; /* New level-0 PMA merger */
int nReader; /* Number of level-0 PMAs to merge */
@@ -80392,7 +86628,7 @@ static int vdbeSorterSetupMerge(VdbeSorter *pSorter){
if( rc==SQLITE_OK ){
pReadr = (PmaReader*)sqlite3DbMallocZero(db, sizeof(PmaReader));
pSorter->pReader = pReadr;
- if( pReadr==0 ) rc = SQLITE_NOMEM;
+ if( pReadr==0 ) rc = SQLITE_NOMEM_BKPT;
}
if( rc==SQLITE_OK ){
rc = vdbeIncrMergerNew(pLast, pMain, &pReadr->pIncr);
@@ -80450,9 +86686,11 @@ static int vdbeSorterSetupMerge(VdbeSorter *pSorter){
** in sorted order.
*/
SQLITE_PRIVATE int sqlite3VdbeSorterRewind(const VdbeCursor *pCsr, int *pbEof){
- VdbeSorter *pSorter = pCsr->pSorter;
+ VdbeSorter *pSorter;
int rc = SQLITE_OK; /* Return code */
+ assert( pCsr->eCurType==CURTYPE_SORTER );
+ pSorter = pCsr->uc.pSorter;
assert( pSorter );
/* If no data has been written to disk, then do not do so now. Instead,
@@ -80496,9 +86734,11 @@ SQLITE_PRIVATE int sqlite3VdbeSorterRewind(const VdbeCursor *pCsr, int *pbEof){
** Advance to the next element in the sorter.
*/
SQLITE_PRIVATE int sqlite3VdbeSorterNext(sqlite3 *db, const VdbeCursor *pCsr, int *pbEof){
- VdbeSorter *pSorter = pCsr->pSorter;
+ VdbeSorter *pSorter;
int rc; /* Return code */
+ assert( pCsr->eCurType==CURTYPE_SORTER );
+ pSorter = pCsr->uc.pSorter;
assert( pSorter->bUsePMA || (pSorter->pReader==0 && pSorter->pMerger==0) );
if( pSorter->bUsePMA ){
assert( pSorter->pReader==0 || pSorter->pMerger==0 );
@@ -80558,12 +86798,14 @@ static void *vdbeSorterRowkey(
** Copy the current sorter key into the memory cell pOut.
*/
SQLITE_PRIVATE int sqlite3VdbeSorterRowkey(const VdbeCursor *pCsr, Mem *pOut){
- VdbeSorter *pSorter = pCsr->pSorter;
+ VdbeSorter *pSorter;
void *pKey; int nKey; /* Sorter key to copy into pOut */
+ assert( pCsr->eCurType==CURTYPE_SORTER );
+ pSorter = pCsr->uc.pSorter;
pKey = vdbeSorterRowkey(pSorter, &nKey);
if( sqlite3VdbeMemClearAndResize(pOut, nKey) ){
- return SQLITE_NOMEM;
+ return SQLITE_NOMEM_BKPT;
}
pOut->n = nKey;
MemSetTypeFlag(pOut, MEM_Blob);
@@ -80594,17 +86836,21 @@ SQLITE_PRIVATE int sqlite3VdbeSorterCompare(
int nKeyCol, /* Compare this many columns */
int *pRes /* OUT: Result of comparison */
){
- VdbeSorter *pSorter = pCsr->pSorter;
- UnpackedRecord *r2 = pSorter->pUnpacked;
- KeyInfo *pKeyInfo = pCsr->pKeyInfo;
+ VdbeSorter *pSorter;
+ UnpackedRecord *r2;
+ KeyInfo *pKeyInfo;
int i;
void *pKey; int nKey; /* Sorter key to compare pVal with */
+ assert( pCsr->eCurType==CURTYPE_SORTER );
+ pSorter = pCsr->uc.pSorter;
+ r2 = pSorter->pUnpacked;
+ pKeyInfo = pCsr->pKeyInfo;
if( r2==0 ){
char *p;
r2 = pSorter->pUnpacked = sqlite3VdbeAllocUnpackedRecord(pKeyInfo,0,0,&p);
assert( pSorter->pUnpacked==(UnpackedRecord*)p );
- if( r2==0 ) return SQLITE_NOMEM;
+ if( r2==0 ) return SQLITE_NOMEM_BKPT;
r2->nField = nKeyCol;
}
assert( r2->nField==nKeyCol );
@@ -80623,264 +86869,6 @@ SQLITE_PRIVATE int sqlite3VdbeSorterCompare(
}
/************** End of vdbesort.c ********************************************/
-/************** Begin file journal.c *****************************************/
-/*
-** 2007 August 22
-**
-** 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 implements a special kind of sqlite3_file object used
-** by SQLite to create journal files if the atomic-write optimization
-** is enabled.
-**
-** The distinctive characteristic of this sqlite3_file is that the
-** actual on disk file is created lazily. When the file is created,
-** the caller specifies a buffer size for an in-memory buffer to
-** be used to service read() and write() requests. The actual file
-** on disk is not created or populated until either:
-**
-** 1) The in-memory representation grows too large for the allocated
-** buffer, or
-** 2) The sqlite3JournalCreate() function is called.
-*/
-#ifdef SQLITE_ENABLE_ATOMIC_WRITE
-
-
-/*
-** A JournalFile object is a subclass of sqlite3_file used by
-** as an open file handle for journal files.
-*/
-struct JournalFile {
- sqlite3_io_methods *pMethod; /* I/O methods on journal files */
- int nBuf; /* Size of zBuf[] in bytes */
- char *zBuf; /* Space to buffer journal writes */
- int iSize; /* Amount of zBuf[] currently used */
- int flags; /* xOpen flags */
- sqlite3_vfs *pVfs; /* The "real" underlying VFS */
- sqlite3_file *pReal; /* The "real" underlying file descriptor */
- const char *zJournal; /* Name of the journal file */
-};
-typedef struct JournalFile JournalFile;
-
-/*
-** If it does not already exists, create and populate the on-disk file
-** for JournalFile p.
-*/
-static int createFile(JournalFile *p){
- int rc = SQLITE_OK;
- if( !p->pReal ){
- sqlite3_file *pReal = (sqlite3_file *)&p[1];
- rc = sqlite3OsOpen(p->pVfs, p->zJournal, pReal, p->flags, 0);
- if( rc==SQLITE_OK ){
- p->pReal = pReal;
- if( p->iSize>0 ){
- assert(p->iSize<=p->nBuf);
- rc = sqlite3OsWrite(p->pReal, p->zBuf, p->iSize, 0);
- }
- if( rc!=SQLITE_OK ){
- /* If an error occurred while writing to the file, close it before
- ** returning. This way, SQLite uses the in-memory journal data to
- ** roll back changes made to the internal page-cache before this
- ** function was called. */
- sqlite3OsClose(pReal);
- p->pReal = 0;
- }
- }
- }
- return rc;
-}
-
-/*
-** Close the file.
-*/
-static int jrnlClose(sqlite3_file *pJfd){
- JournalFile *p = (JournalFile *)pJfd;
- if( p->pReal ){
- sqlite3OsClose(p->pReal);
- }
- sqlite3_free(p->zBuf);
- return SQLITE_OK;
-}
-
-/*
-** Read data from the file.
-*/
-static int jrnlRead(
- sqlite3_file *pJfd, /* The journal file from which to read */
- void *zBuf, /* Put the results here */
- int iAmt, /* Number of bytes to read */
- sqlite_int64 iOfst /* Begin reading at this offset */
-){
- int rc = SQLITE_OK;
- JournalFile *p = (JournalFile *)pJfd;
- if( p->pReal ){
- rc = sqlite3OsRead(p->pReal, zBuf, iAmt, iOfst);
- }else if( (iAmt+iOfst)>p->iSize ){
- rc = SQLITE_IOERR_SHORT_READ;
- }else{
- memcpy(zBuf, &p->zBuf[iOfst], iAmt);
- }
- return rc;
-}
-
-/*
-** Write data to the file.
-*/
-static int jrnlWrite(
- sqlite3_file *pJfd, /* The journal file into which to write */
- const void *zBuf, /* Take data to be written from here */
- int iAmt, /* Number of bytes to write */
- sqlite_int64 iOfst /* Begin writing at this offset into the file */
-){
- int rc = SQLITE_OK;
- JournalFile *p = (JournalFile *)pJfd;
- if( !p->pReal && (iOfst+iAmt)>p->nBuf ){
- rc = createFile(p);
- }
- if( rc==SQLITE_OK ){
- if( p->pReal ){
- rc = sqlite3OsWrite(p->pReal, zBuf, iAmt, iOfst);
- }else{
- memcpy(&p->zBuf[iOfst], zBuf, iAmt);
- if( p->iSize<(iOfst+iAmt) ){
- p->iSize = (iOfst+iAmt);
- }
- }
- }
- return rc;
-}
-
-/*
-** Truncate the file.
-*/
-static int jrnlTruncate(sqlite3_file *pJfd, sqlite_int64 size){
- int rc = SQLITE_OK;
- JournalFile *p = (JournalFile *)pJfd;
- if( p->pReal ){
- rc = sqlite3OsTruncate(p->pReal, size);
- }else if( size<p->iSize ){
- p->iSize = size;
- }
- return rc;
-}
-
-/*
-** Sync the file.
-*/
-static int jrnlSync(sqlite3_file *pJfd, int flags){
- int rc;
- JournalFile *p = (JournalFile *)pJfd;
- if( p->pReal ){
- rc = sqlite3OsSync(p->pReal, flags);
- }else{
- rc = SQLITE_OK;
- }
- return rc;
-}
-
-/*
-** Query the size of the file in bytes.
-*/
-static int jrnlFileSize(sqlite3_file *pJfd, sqlite_int64 *pSize){
- int rc = SQLITE_OK;
- JournalFile *p = (JournalFile *)pJfd;
- if( p->pReal ){
- rc = sqlite3OsFileSize(p->pReal, pSize);
- }else{
- *pSize = (sqlite_int64) p->iSize;
- }
- return rc;
-}
-
-/*
-** Table of methods for JournalFile sqlite3_file object.
-*/
-static struct sqlite3_io_methods JournalFileMethods = {
- 1, /* iVersion */
- jrnlClose, /* xClose */
- jrnlRead, /* xRead */
- jrnlWrite, /* xWrite */
- jrnlTruncate, /* xTruncate */
- jrnlSync, /* xSync */
- jrnlFileSize, /* xFileSize */
- 0, /* xLock */
- 0, /* xUnlock */
- 0, /* xCheckReservedLock */
- 0, /* xFileControl */
- 0, /* xSectorSize */
- 0, /* xDeviceCharacteristics */
- 0, /* xShmMap */
- 0, /* xShmLock */
- 0, /* xShmBarrier */
- 0 /* xShmUnmap */
-};
-
-/*
-** Open a journal file.
-*/
-SQLITE_PRIVATE int sqlite3JournalOpen(
- sqlite3_vfs *pVfs, /* The VFS to use for actual file I/O */
- const char *zName, /* Name of the journal file */
- sqlite3_file *pJfd, /* Preallocated, blank file handle */
- int flags, /* Opening flags */
- int nBuf /* Bytes buffered before opening the file */
-){
- JournalFile *p = (JournalFile *)pJfd;
- memset(p, 0, sqlite3JournalSize(pVfs));
- if( nBuf>0 ){
- p->zBuf = sqlite3MallocZero(nBuf);
- if( !p->zBuf ){
- return SQLITE_NOMEM;
- }
- }else{
- return sqlite3OsOpen(pVfs, zName, pJfd, flags, 0);
- }
- p->pMethod = &JournalFileMethods;
- p->nBuf = nBuf;
- p->flags = flags;
- p->zJournal = zName;
- p->pVfs = pVfs;
- return SQLITE_OK;
-}
-
-/*
-** If the argument p points to a JournalFile structure, and the underlying
-** file has not yet been created, create it now.
-*/
-SQLITE_PRIVATE int sqlite3JournalCreate(sqlite3_file *p){
- if( p->pMethods!=&JournalFileMethods ){
- return SQLITE_OK;
- }
- return createFile((JournalFile *)p);
-}
-
-/*
-** The file-handle passed as the only argument is guaranteed to be an open
-** file. It may or may not be of class JournalFile. If the file is a
-** JournalFile, and the underlying file on disk has not yet been opened,
-** return 0. Otherwise, return 1.
-*/
-SQLITE_PRIVATE int sqlite3JournalExists(sqlite3_file *p){
- return (p->pMethods!=&JournalFileMethods || ((JournalFile *)p)->pReal!=0);
-}
-
-/*
-** Return the number of bytes required to store a JournalFile that uses vfs
-** pVfs to create the underlying on-disk files.
-*/
-SQLITE_PRIVATE int sqlite3JournalSize(sqlite3_vfs *pVfs){
- return (pVfs->szOsFile+sizeof(JournalFile));
-}
-#endif
-
-/************** End of journal.c *********************************************/
/************** Begin file memjournal.c **************************************/
/*
** 2008 October 7
@@ -80897,32 +86885,46 @@ SQLITE_PRIVATE int sqlite3JournalSize(sqlite3_vfs *pVfs){
** This file contains code use to implement an in-memory rollback journal.
** The in-memory rollback journal is used to journal transactions for
** ":memory:" databases and when the journal_mode=MEMORY pragma is used.
+**
+** Update: The in-memory journal is also used to temporarily cache
+** smaller journals that are not critical for power-loss recovery.
+** For example, statement journals that are not too big will be held
+** entirely in memory, thus reducing the number of file I/O calls, and
+** more importantly, reducing temporary file creation events. If these
+** journals become too large for memory, they are spilled to disk. But
+** in the common case, they are usually small and no file I/O needs to
+** occur.
*/
+/* #include "sqliteInt.h" */
/* Forward references to internal structures */
typedef struct MemJournal MemJournal;
typedef struct FilePoint FilePoint;
typedef struct FileChunk FileChunk;
-/* Space to hold the rollback journal is allocated in increments of
-** this many bytes.
-**
-** The size chosen is a little less than a power of two. That way,
-** the FileChunk object will have a size that almost exactly fills
-** a power-of-two allocation. This minimizes wasted space in power-of-two
-** memory allocators.
-*/
-#define JOURNAL_CHUNKSIZE ((int)(1024-sizeof(FileChunk*)))
-
/*
** The rollback journal is composed of a linked list of these structures.
+**
+** The zChunk array is always at least 8 bytes in size - usually much more.
+** Its actual size is stored in the MemJournal.nChunkSize variable.
*/
struct FileChunk {
FileChunk *pNext; /* Next chunk in the journal */
- u8 zChunk[JOURNAL_CHUNKSIZE]; /* Content of this chunk */
+ u8 zChunk[8]; /* Content of this chunk */
};
/*
+** By default, allocate this many bytes of memory for each FileChunk object.
+*/
+#define MEMJOURNAL_DFLT_FILECHUNKSIZE 1024
+
+/*
+** For chunk size nChunkSize, return the number of bytes that should
+** be allocated for each FileChunk structure.
+*/
+#define fileChunkSize(nChunkSize) (sizeof(FileChunk) + ((nChunkSize)-8))
+
+/*
** An instance of this object serves as a cursor into the rollback journal.
** The cursor can be either for reading or writing.
*/
@@ -80932,14 +86934,22 @@ struct FilePoint {
};
/*
-** This subclass is a subclass of sqlite3_file. Each open memory-journal
+** This structure is a subclass of sqlite3_file. Each open memory-journal
** is an instance of this class.
*/
struct MemJournal {
- sqlite3_io_methods *pMethod; /* Parent class. MUST BE FIRST */
+ const sqlite3_io_methods *pMethod; /* Parent class. MUST BE FIRST */
+ int nChunkSize; /* In-memory chunk-size */
+
+ int nSpill; /* Bytes of data before flushing */
+ int nSize; /* Bytes of data currently in memory */
FileChunk *pFirst; /* Head of in-memory chunk-list */
FilePoint endpoint; /* Pointer to the end of the file */
FilePoint readpoint; /* Pointer to the end of the last xRead() */
+
+ int flags; /* xOpen flags */
+ sqlite3_vfs *pVfs; /* The "real" underlying VFS */
+ const char *zJournal; /* Name of the journal file */
};
/*
@@ -80958,37 +86968,95 @@ static int memjrnlRead(
int iChunkOffset;
FileChunk *pChunk;
- /* SQLite never tries to read past the end of a rollback journal file */
- assert( iOfst+iAmt<=p->endpoint.iOffset );
+#ifdef SQLITE_ENABLE_ATOMIC_WRITE
+ if( (iAmt+iOfst)>p->endpoint.iOffset ){
+ return SQLITE_IOERR_SHORT_READ;
+ }
+#endif
+ assert( (iAmt+iOfst)<=p->endpoint.iOffset );
+ assert( p->readpoint.iOffset==0 || p->readpoint.pChunk!=0 );
if( p->readpoint.iOffset!=iOfst || iOfst==0 ){
sqlite3_int64 iOff = 0;
for(pChunk=p->pFirst;
- ALWAYS(pChunk) && (iOff+JOURNAL_CHUNKSIZE)<=iOfst;
+ ALWAYS(pChunk) && (iOff+p->nChunkSize)<=iOfst;
pChunk=pChunk->pNext
){
- iOff += JOURNAL_CHUNKSIZE;
+ iOff += p->nChunkSize;
}
}else{
pChunk = p->readpoint.pChunk;
+ assert( pChunk!=0 );
}
- iChunkOffset = (int)(iOfst%JOURNAL_CHUNKSIZE);
+ iChunkOffset = (int)(iOfst%p->nChunkSize);
do {
- int iSpace = JOURNAL_CHUNKSIZE - iChunkOffset;
- int nCopy = MIN(nRead, (JOURNAL_CHUNKSIZE - iChunkOffset));
- memcpy(zOut, &pChunk->zChunk[iChunkOffset], nCopy);
+ int iSpace = p->nChunkSize - iChunkOffset;
+ int nCopy = MIN(nRead, (p->nChunkSize - iChunkOffset));
+ memcpy(zOut, (u8*)pChunk->zChunk + iChunkOffset, nCopy);
zOut += nCopy;
nRead -= iSpace;
iChunkOffset = 0;
} while( nRead>=0 && (pChunk=pChunk->pNext)!=0 && nRead>0 );
- p->readpoint.iOffset = iOfst+iAmt;
+ p->readpoint.iOffset = pChunk ? iOfst+iAmt : 0;
p->readpoint.pChunk = pChunk;
return SQLITE_OK;
}
/*
+** Free the list of FileChunk structures headed at MemJournal.pFirst.
+*/
+static void memjrnlFreeChunks(MemJournal *p){
+ FileChunk *pIter;
+ FileChunk *pNext;
+ for(pIter=p->pFirst; pIter; pIter=pNext){
+ pNext = pIter->pNext;
+ sqlite3_free(pIter);
+ }
+ p->pFirst = 0;
+}
+
+/*
+** Flush the contents of memory to a real file on disk.
+*/
+static int memjrnlCreateFile(MemJournal *p){
+ int rc;
+ sqlite3_file *pReal = (sqlite3_file*)p;
+ MemJournal copy = *p;
+
+ memset(p, 0, sizeof(MemJournal));
+ rc = sqlite3OsOpen(copy.pVfs, copy.zJournal, pReal, copy.flags, 0);
+ if( rc==SQLITE_OK ){
+ int nChunk = copy.nChunkSize;
+ i64 iOff = 0;
+ FileChunk *pIter;
+ for(pIter=copy.pFirst; pIter; pIter=pIter->pNext){
+ if( iOff + nChunk > copy.endpoint.iOffset ){
+ nChunk = copy.endpoint.iOffset - iOff;
+ }
+ rc = sqlite3OsWrite(pReal, (u8*)pIter->zChunk, nChunk, iOff);
+ if( rc ) break;
+ iOff += nChunk;
+ }
+ if( rc==SQLITE_OK ){
+ /* No error has occurred. Free the in-memory buffers. */
+ memjrnlFreeChunks(&copy);
+ }
+ }
+ if( rc!=SQLITE_OK ){
+ /* If an error occurred while creating or writing to the file, restore
+ ** the original before returning. This way, SQLite uses the in-memory
+ ** journal data to roll back changes made to the internal page-cache
+ ** before this function was called. */
+ sqlite3OsClose(pReal);
+ *p = copy;
+ }
+ return rc;
+}
+
+
+/*
** Write data to the file.
*/
static int memjrnlWrite(
@@ -81001,38 +87069,62 @@ static int memjrnlWrite(
int nWrite = iAmt;
u8 *zWrite = (u8 *)zBuf;
- /* An in-memory journal file should only ever be appended to. Random
- ** access writes are not required by sqlite.
- */
- assert( iOfst==p->endpoint.iOffset );
- UNUSED_PARAMETER(iOfst);
+ /* If the file should be created now, create it and write the new data
+ ** into the file on disk. */
+ if( p->nSpill>0 && (iAmt+iOfst)>p->nSpill ){
+ int rc = memjrnlCreateFile(p);
+ if( rc==SQLITE_OK ){
+ rc = sqlite3OsWrite(pJfd, zBuf, iAmt, iOfst);
+ }
+ return rc;
+ }
- while( nWrite>0 ){
- FileChunk *pChunk = p->endpoint.pChunk;
- int iChunkOffset = (int)(p->endpoint.iOffset%JOURNAL_CHUNKSIZE);
- int iSpace = MIN(nWrite, JOURNAL_CHUNKSIZE - iChunkOffset);
+ /* If the contents of this write should be stored in memory */
+ else{
+ /* An in-memory journal file should only ever be appended to. Random
+ ** access writes are not required. The only exception to this is when
+ ** the in-memory journal is being used by a connection using the
+ ** atomic-write optimization. In this case the first 28 bytes of the
+ ** journal file may be written as part of committing the transaction. */
+ assert( iOfst==p->endpoint.iOffset || iOfst==0 );
+#ifdef SQLITE_ENABLE_ATOMIC_WRITE
+ if( iOfst==0 && p->pFirst ){
+ assert( p->nChunkSize>iAmt );
+ memcpy((u8*)p->pFirst->zChunk, zBuf, iAmt);
+ }else
+#else
+ assert( iOfst>0 || p->pFirst==0 );
+#endif
+ {
+ while( nWrite>0 ){
+ FileChunk *pChunk = p->endpoint.pChunk;
+ int iChunkOffset = (int)(p->endpoint.iOffset%p->nChunkSize);
+ int iSpace = MIN(nWrite, p->nChunkSize - iChunkOffset);
+
+ if( iChunkOffset==0 ){
+ /* New chunk is required to extend the file. */
+ FileChunk *pNew = sqlite3_malloc(fileChunkSize(p->nChunkSize));
+ if( !pNew ){
+ return SQLITE_IOERR_NOMEM_BKPT;
+ }
+ pNew->pNext = 0;
+ if( pChunk ){
+ assert( p->pFirst );
+ pChunk->pNext = pNew;
+ }else{
+ assert( !p->pFirst );
+ p->pFirst = pNew;
+ }
+ p->endpoint.pChunk = pNew;
+ }
- if( iChunkOffset==0 ){
- /* New chunk is required to extend the file. */
- FileChunk *pNew = sqlite3_malloc(sizeof(FileChunk));
- if( !pNew ){
- return SQLITE_IOERR_NOMEM;
+ memcpy((u8*)p->endpoint.pChunk->zChunk + iChunkOffset, zWrite, iSpace);
+ zWrite += iSpace;
+ nWrite -= iSpace;
+ p->endpoint.iOffset += iSpace;
}
- pNew->pNext = 0;
- if( pChunk ){
- assert( p->pFirst );
- pChunk->pNext = pNew;
- }else{
- assert( !p->pFirst );
- p->pFirst = pNew;
- }
- p->endpoint.pChunk = pNew;
+ p->nSize = iAmt + iOfst;
}
-
- memcpy(&p->endpoint.pChunk->zChunk[iChunkOffset], zWrite, iSpace);
- zWrite += iSpace;
- nWrite -= iSpace;
- p->endpoint.iOffset += iSpace;
}
return SQLITE_OK;
@@ -81040,19 +87132,21 @@ static int memjrnlWrite(
/*
** Truncate the file.
+**
+** If the journal file is already on disk, truncate it there. Or, if it
+** is still in main memory but is being truncated to zero bytes in size,
+** ignore
*/
static int memjrnlTruncate(sqlite3_file *pJfd, sqlite_int64 size){
MemJournal *p = (MemJournal *)pJfd;
- FileChunk *pChunk;
- assert(size==0);
- UNUSED_PARAMETER(size);
- pChunk = p->pFirst;
- while( pChunk ){
- FileChunk *pTmp = pChunk;
- pChunk = pChunk->pNext;
- sqlite3_free(pTmp);
- }
- sqlite3MemJournalOpen(pJfd);
+ if( ALWAYS(size==0) ){
+ memjrnlFreeChunks(p);
+ p->nSize = 0;
+ p->endpoint.pChunk = 0;
+ p->endpoint.iOffset = 0;
+ p->readpoint.pChunk = 0;
+ p->readpoint.iOffset = 0;
+ }
return SQLITE_OK;
}
@@ -81060,21 +87154,19 @@ static int memjrnlTruncate(sqlite3_file *pJfd, sqlite_int64 size){
** Close the file.
*/
static int memjrnlClose(sqlite3_file *pJfd){
- memjrnlTruncate(pJfd, 0);
+ MemJournal *p = (MemJournal *)pJfd;
+ memjrnlFreeChunks(p);
return SQLITE_OK;
}
-
/*
** Sync the file.
**
-** Syncing an in-memory journal is a no-op. And, in fact, this routine
-** is never called in a working implementation. This implementation
-** exists purely as a contingency, in case some malfunction in some other
-** part of SQLite causes Sync to be called by mistake.
+** If the real file has been created, call its xSync method. Otherwise,
+** syncing an in-memory journal is a no-op.
*/
-static int memjrnlSync(sqlite3_file *NotUsed, int NotUsed2){
- UNUSED_PARAMETER2(NotUsed, NotUsed2);
+static int memjrnlSync(sqlite3_file *pJfd, int flags){
+ UNUSED_PARAMETER2(pJfd, flags);
return SQLITE_OK;
}
@@ -81113,28 +87205,88 @@ static const struct sqlite3_io_methods MemJournalMethods = {
};
/*
-** Open a journal file.
+** Open a journal file.
+**
+** The behaviour of the journal file depends on the value of parameter
+** nSpill. If nSpill is 0, then the journal file is always create and
+** accessed using the underlying VFS. If nSpill is less than zero, then
+** all content is always stored in main-memory. Finally, if nSpill is a
+** positive value, then the journal file is initially created in-memory
+** but may be flushed to disk later on. In this case the journal file is
+** flushed to disk either when it grows larger than nSpill bytes in size,
+** or when sqlite3JournalCreate() is called.
+*/
+SQLITE_PRIVATE int sqlite3JournalOpen(
+ sqlite3_vfs *pVfs, /* The VFS to use for actual file I/O */
+ const char *zName, /* Name of the journal file */
+ sqlite3_file *pJfd, /* Preallocated, blank file handle */
+ int flags, /* Opening flags */
+ int nSpill /* Bytes buffered before opening the file */
+){
+ MemJournal *p = (MemJournal*)pJfd;
+
+ /* Zero the file-handle object. If nSpill was passed zero, initialize
+ ** it using the sqlite3OsOpen() function of the underlying VFS. In this
+ ** case none of the code in this module is executed as a result of calls
+ ** made on the journal file-handle. */
+ memset(p, 0, sizeof(MemJournal));
+ if( nSpill==0 ){
+ return sqlite3OsOpen(pVfs, zName, pJfd, flags, 0);
+ }
+
+ if( nSpill>0 ){
+ p->nChunkSize = nSpill;
+ }else{
+ p->nChunkSize = 8 + MEMJOURNAL_DFLT_FILECHUNKSIZE - sizeof(FileChunk);
+ assert( MEMJOURNAL_DFLT_FILECHUNKSIZE==fileChunkSize(p->nChunkSize) );
+ }
+
+ p->pMethod = (const sqlite3_io_methods*)&MemJournalMethods;
+ p->nSpill = nSpill;
+ p->flags = flags;
+ p->zJournal = zName;
+ p->pVfs = pVfs;
+ return SQLITE_OK;
+}
+
+/*
+** Open an in-memory journal file.
*/
SQLITE_PRIVATE void sqlite3MemJournalOpen(sqlite3_file *pJfd){
- MemJournal *p = (MemJournal *)pJfd;
- assert( EIGHT_BYTE_ALIGNMENT(p) );
- memset(p, 0, sqlite3MemJournalSize());
- p->pMethod = (sqlite3_io_methods*)&MemJournalMethods;
+ sqlite3JournalOpen(0, 0, pJfd, 0, -1);
+}
+
+#ifdef SQLITE_ENABLE_ATOMIC_WRITE
+/*
+** If the argument p points to a MemJournal structure that is not an
+** in-memory-only journal file (i.e. is one that was opened with a +ve
+** nSpill parameter), and the underlying file has not yet been created,
+** create it now.
+*/
+SQLITE_PRIVATE int sqlite3JournalCreate(sqlite3_file *p){
+ int rc = SQLITE_OK;
+ if( p->pMethods==&MemJournalMethods && ((MemJournal*)p)->nSpill>0 ){
+ rc = memjrnlCreateFile((MemJournal*)p);
+ }
+ return rc;
}
+#endif
/*
-** Return true if the file-handle passed as an argument is
-** an in-memory journal
+** The file-handle passed as the only argument is open on a journal file.
+** Return true if this "journal file" is currently stored in heap memory,
+** or false otherwise.
*/
-SQLITE_PRIVATE int sqlite3IsMemJournal(sqlite3_file *pJfd){
- return pJfd->pMethods==&MemJournalMethods;
+SQLITE_PRIVATE int sqlite3JournalIsInMemory(sqlite3_file *p){
+ return p->pMethods==&MemJournalMethods;
}
/*
-** Return the number of bytes required to store a MemJournal file descriptor.
+** Return the number of bytes required to store a JournalFile that uses vfs
+** pVfs to create the underlying on-disk files.
*/
-SQLITE_PRIVATE int sqlite3MemJournalSize(void){
- return sizeof(MemJournal);
+SQLITE_PRIVATE int sqlite3JournalSize(sqlite3_vfs *pVfs){
+ return MAX(pVfs->szOsFile, (int)sizeof(MemJournal));
}
/************** End of memjournal.c ******************************************/
@@ -81153,6 +87305,7 @@ SQLITE_PRIVATE int sqlite3MemJournalSize(void){
** This file contains routines used for walking the parser tree for
** an SQL statement.
*/
+/* #include "sqliteInt.h" */
/* #include <stdlib.h> */
/* #include <string.h> */
@@ -81176,9 +87329,8 @@ SQLITE_PRIVATE int sqlite3MemJournalSize(void){
** The return value from this routine is WRC_Abort to abandon the tree walk
** and WRC_Continue to continue.
*/
-SQLITE_PRIVATE int sqlite3WalkExpr(Walker *pWalker, Expr *pExpr){
+static SQLITE_NOINLINE int walkExpr(Walker *pWalker, Expr *pExpr){
int rc;
- if( pExpr==0 ) return WRC_Continue;
testcase( ExprHasProperty(pExpr, EP_TokenOnly) );
testcase( ExprHasProperty(pExpr, EP_Reduced) );
rc = pWalker->xExprCallback(pWalker, pExpr);
@@ -81194,6 +87346,9 @@ SQLITE_PRIVATE int sqlite3WalkExpr(Walker *pWalker, Expr *pExpr){
}
return rc & WRC_Abort;
}
+SQLITE_PRIVATE int sqlite3WalkExpr(Walker *pWalker, Expr *pExpr){
+ return pExpr ? walkExpr(pWalker,pExpr) : WRC_Continue;
+}
/*
** Call sqlite3WalkExpr() for every expression in list p or until
@@ -81245,6 +87400,11 @@ SQLITE_PRIVATE int sqlite3WalkSelectFrom(Walker *pWalker, Select *p){
if( sqlite3WalkSelect(pWalker, pItem->pSelect) ){
return WRC_Abort;
}
+ if( pItem->fg.isTabFunc
+ && sqlite3WalkExprList(pWalker, pItem->u1.pFuncArg)
+ ){
+ return WRC_Abort;
+ }
}
}
return WRC_Continue;
@@ -81311,6 +87471,7 @@ SQLITE_PRIVATE int sqlite3WalkSelect(Walker *pWalker, Select *p){
** resolve all identifiers by associating them with a particular
** table and column.
*/
+/* #include "sqliteInt.h" */
/* #include <stdlib.h> */
/* #include <string.h> */
@@ -81341,30 +87502,6 @@ static void incrAggFunctionDepth(Expr *pExpr, int N){
** Turn the pExpr expression into an alias for the iCol-th column of the
** result set in pEList.
**
-** If the result set column is a simple column reference, then this routine
-** makes an exact copy. But for any other kind of expression, this
-** routine make a copy of the result set column as the argument to the
-** TK_AS operator. The TK_AS operator causes the expression to be
-** evaluated just once and then reused for each alias.
-**
-** The reason for suppressing the TK_AS term when the expression is a simple
-** column reference is so that the column reference will be recognized as
-** usable by indices within the WHERE clause processing logic.
-**
-** The TK_AS operator is inhibited if zType[0]=='G'. This means
-** that in a GROUP BY clause, the expression is evaluated twice. Hence:
-**
-** SELECT random()%5 AS x, count(*) FROM tab GROUP BY x
-**
-** Is equivalent to:
-**
-** SELECT random()%5 AS x, count(*) FROM tab GROUP BY random()%5
-**
-** The result of random()%5 in the GROUP BY clause is probably different
-** from the result in the result-set. On the other hand Standard SQL does
-** not allow the GROUP BY clause to contain references to result-set columns.
-** So this should never come up in well-formed queries.
-**
** If the reference is followed by a COLLATE operator, then make sure
** the COLLATE operator is preserved. For example:
**
@@ -81398,19 +87535,11 @@ static void resolveAlias(
db = pParse->db;
pDup = sqlite3ExprDup(db, pOrig, 0);
if( pDup==0 ) return;
- if( pOrig->op!=TK_COLUMN && zType[0]!='G' ){
- incrAggFunctionDepth(pDup, nSubquery);
- pDup = sqlite3PExpr(pParse, TK_AS, pDup, 0, 0);
- if( pDup==0 ) return;
- ExprSetProperty(pDup, EP_Skip);
- if( pEList->a[iCol].u.x.iAlias==0 ){
- pEList->a[iCol].u.x.iAlias = (u16)(++pParse->nAlias);
- }
- pDup->iTable = pEList->a[iCol].u.x.iAlias;
- }
+ if( zType[0]!='G' ) incrAggFunctionDepth(pDup, nSubquery);
if( pExpr->op==TK_COLLATE ){
pDup = sqlite3ExprAddCollateString(pParse, pDup, pExpr->u.zToken);
}
+ ExprSetProperty(pDup, EP_Alias);
/* Before calling sqlite3ExprDelete(), set the EP_Static flag. This
** prevents ExprDelete() from deleting the Expr structure itself,
@@ -81602,7 +87731,7 @@ static int lookupName(
** USING clause, then skip this match.
*/
if( cnt==1 ){
- if( pItem->jointype & JT_NATURAL ) continue;
+ if( pItem->fg.jointype & JT_NATURAL ) continue;
if( nameInUsingClause(pItem->pUsing, zCol) ) continue;
}
cnt++;
@@ -81617,8 +87746,8 @@ static int lookupName(
pExpr->iTable = pMatch->iCursor;
pExpr->pTab = pMatch->pTab;
/* RIGHT JOIN not (yet) supported */
- assert( (pMatch->jointype & JT_RIGHT)==0 );
- if( (pMatch->jointype & JT_LEFT)!=0 ){
+ assert( (pMatch->fg.jointype & JT_RIGHT)==0 );
+ if( (pMatch->fg.jointype & JT_LEFT)!=0 ){
ExprSetProperty(pExpr, EP_CanBeNull);
}
pSchema = pExpr->pTab->pSchema;
@@ -81654,9 +87783,8 @@ static int lookupName(
break;
}
}
- if( iCol>=pTab->nCol && sqlite3IsRowid(zCol) && HasRowid(pTab) ){
+ if( iCol>=pTab->nCol && sqlite3IsRowid(zCol) && VisibleRowid(pTab) ){
/* IMP: R-51414-32910 */
- /* IMP: R-44911-55124 */
iCol = -1;
}
if( iCol<pTab->nCol ){
@@ -81683,10 +87811,15 @@ static int lookupName(
/*
** Perhaps the name is a reference to the ROWID
*/
- if( cnt==0 && cntTab==1 && pMatch && sqlite3IsRowid(zCol)
- && HasRowid(pMatch->pTab) ){
+ if( cnt==0
+ && cntTab==1
+ && pMatch
+ && (pNC->ncFlags & NC_IdxExpr)==0
+ && sqlite3IsRowid(zCol)
+ && VisibleRowid(pMatch->pTab)
+ ){
cnt = 1;
- pExpr->iColumn = -1; /* IMP: R-44911-55124 */
+ pExpr->iColumn = -1;
pExpr->affinity = SQLITE_AFF_INTEGER;
}
@@ -81703,9 +87836,9 @@ static int lookupName(
** resolved by the time the WHERE clause is resolved.
**
** The ability to use an output result-set column in the WHERE, GROUP BY,
- ** or HAVING clauses, or as part of a larger expression in the ORDRE BY
+ ** or HAVING clauses, or as part of a larger expression in the ORDER BY
** clause is not standard SQL. This is a (goofy) SQLite extension, that
- ** is supported for backwards compatibility only. TO DO: Issue a warning
+ ** is supported for backwards compatibility only. Hence, we issue a warning
** on sqlite3_log() whenever the capability is used.
*/
if( (pEList = pNC->pEList)!=0
@@ -81802,7 +87935,7 @@ static int lookupName(
lookupname_end:
if( cnt==1 ){
assert( pNC!=0 );
- if( pExpr->op!=TK_AS ){
+ if( !ExprHasProperty(pExpr, EP_Alias) ){
sqlite3AuthRead(pParse, pExpr, pSchema, pNC->pSrcList);
}
/* Increment the nRef value on all name contexts from TopNC up to
@@ -81843,36 +87976,25 @@ SQLITE_PRIVATE Expr *sqlite3CreateColumnExpr(sqlite3 *db, SrcList *pSrc, int iSr
}
/*
-** Report an error that an expression is not valid for a partial index WHERE
-** clause.
+** Report an error that an expression is not valid for some set of
+** pNC->ncFlags values determined by validMask.
*/
-static void notValidPartIdxWhere(
+static void notValid(
Parse *pParse, /* Leave error message here */
NameContext *pNC, /* The name context */
- const char *zMsg /* Type of error */
+ const char *zMsg, /* Type of error */
+ int validMask /* Set of contexts for which prohibited */
){
- if( (pNC->ncFlags & NC_PartIdx)!=0 ){
- sqlite3ErrorMsg(pParse, "%s prohibited in partial index WHERE clauses",
- zMsg);
- }
-}
-
+ assert( (validMask&~(NC_IsCheck|NC_PartIdx|NC_IdxExpr))==0 );
+ if( (pNC->ncFlags & validMask)!=0 ){
+ const char *zIn = "partial index WHERE clauses";
+ if( pNC->ncFlags & NC_IdxExpr ) zIn = "index expressions";
#ifndef SQLITE_OMIT_CHECK
-/*
-** Report an error that an expression is not valid for a CHECK constraint.
-*/
-static void notValidCheckConstraint(
- Parse *pParse, /* Leave error message here */
- NameContext *pNC, /* The name context */
- const char *zMsg /* Type of error */
-){
- if( (pNC->ncFlags & NC_IsCheck)!=0 ){
- sqlite3ErrorMsg(pParse,"%s prohibited in CHECK constraints", zMsg);
+ else if( pNC->ncFlags & NC_IsCheck ) zIn = "CHECK constraints";
+#endif
+ sqlite3ErrorMsg(pParse, "%s prohibited in %s", zMsg, zIn);
}
}
-#else
-# define notValidCheckConstraint(P,N,M)
-#endif
/*
** Expression p should encode a floating point value between 1.0 and 0.0.
@@ -81957,6 +88079,8 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
Expr *pRight;
/* if( pSrcList==0 ) break; */
+ notValid(pParse, pNC, "the \".\" operator", NC_IdxExpr);
+ /*notValid(pParse, pNC, "the \".\" operator", NC_PartIdx|NC_IsCheck, 1);*/
pRight = pExpr->pRight;
if( pRight->op==TK_ID ){
zDb = 0;
@@ -81986,19 +88110,19 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
u8 enc = ENC(pParse->db); /* The database encoding */
assert( !ExprHasProperty(pExpr, EP_xIsSelect) );
- notValidPartIdxWhere(pParse, pNC, "functions");
+ notValid(pParse, pNC, "functions", NC_PartIdx);
zId = pExpr->u.zToken;
nId = sqlite3Strlen30(zId);
- pDef = sqlite3FindFunction(pParse->db, zId, nId, n, enc, 0);
+ pDef = sqlite3FindFunction(pParse->db, zId, n, enc, 0);
if( pDef==0 ){
- pDef = sqlite3FindFunction(pParse->db, zId, nId, -2, enc, 0);
+ pDef = sqlite3FindFunction(pParse->db, zId, -2, enc, 0);
if( pDef==0 ){
no_such_func = 1;
}else{
wrong_num_args = 1;
}
}else{
- is_agg = pDef->xFunc==0;
+ is_agg = pDef->xFinalize!=0;
if( pDef->funcFlags & SQLITE_FUNC_UNLIKELY ){
ExprSetProperty(pExpr, EP_Unlikely|EP_Skip);
if( n==2 ){
@@ -82034,9 +88158,18 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
return WRC_Prune;
}
#endif
- if( pDef->funcFlags & SQLITE_FUNC_CONSTANT ){
+ if( pDef->funcFlags & (SQLITE_FUNC_CONSTANT|SQLITE_FUNC_SLOCHNG) ){
+ /* For the purposes of the EP_ConstFunc flag, date and time
+ ** functions and other functions that change slowly are considered
+ ** constant because they are constant for the duration of one query */
ExprSetProperty(pExpr,EP_ConstFunc);
}
+ if( (pDef->funcFlags & SQLITE_FUNC_CONSTANT)==0 ){
+ /* Date/time functions that use 'now', and other functions like
+ ** sqlite_version() that might change over time cannot be used
+ ** in an index. */
+ notValid(pParse, pNC, "non-deterministic functions", NC_IdxExpr);
+ }
}
if( is_agg && (pNC->ncFlags & NC_AllowAgg)==0 ){
sqlite3ErrorMsg(pParse, "misuse of aggregate function %.*s()", nId,zId);
@@ -82082,19 +88215,18 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
testcase( pExpr->op==TK_IN );
if( ExprHasProperty(pExpr, EP_xIsSelect) ){
int nRef = pNC->nRef;
- notValidCheckConstraint(pParse, pNC, "subqueries");
- notValidPartIdxWhere(pParse, pNC, "subqueries");
+ notValid(pParse, pNC, "subqueries", NC_IsCheck|NC_PartIdx|NC_IdxExpr);
sqlite3WalkSelect(pWalker, pExpr->x.pSelect);
assert( pNC->nRef>=nRef );
if( nRef!=pNC->nRef ){
ExprSetProperty(pExpr, EP_VarSelect);
+ pNC->ncFlags |= NC_VarSelect;
}
}
break;
}
case TK_VARIABLE: {
- notValidCheckConstraint(pParse, pNC, "parameters");
- notValidPartIdxWhere(pParse, pNC, "parameters");
+ notValid(pParse, pNC, "parameters", NC_IsCheck|NC_PartIdx|NC_IdxExpr);
break;
}
}
@@ -82438,7 +88570,6 @@ static int resolveSelectStep(Walker *pWalker, Select *p){
int isCompound; /* True if p is a compound select */
int nCompound; /* Number of compound terms processed so far */
Parse *pParse; /* Parsing context */
- ExprList *pEList; /* Result set expression list */
int i; /* Loop counter */
ExprList *pGroupBy; /* The GROUP BY clause */
Select *pLeftmost; /* Left-most of SELECT of a compound */
@@ -82511,7 +88642,7 @@ static int resolveSelectStep(Walker *pWalker, Select *p){
** parent contexts. After resolving references to expressions in
** pItem->pSelect, check if this value has changed. If so, then
** SELECT statement pItem->pSelect must be correlated. Set the
- ** pItem->isCorrelated flag if this is the case. */
+ ** pItem->fg.isCorrelated flag if this is the case. */
for(pNC=pOuterNC; pNC; pNC=pNC->pNext) nRef += pNC->nRef;
if( pItem->zName ) pParse->zAuthContext = pItem->zName;
@@ -82520,8 +88651,8 @@ static int resolveSelectStep(Walker *pWalker, Select *p){
if( pParse->nErr || db->mallocFailed ) return WRC_Abort;
for(pNC=pOuterNC; pNC; pNC=pNC->pNext) nRef -= pNC->nRef;
- assert( pItem->isCorrelated==0 && nRef<=0 );
- pItem->isCorrelated = (nRef!=0);
+ assert( pItem->fg.isCorrelated==0 && nRef<=0 );
+ pItem->fg.isCorrelated = (nRef!=0);
}
}
@@ -82533,14 +88664,7 @@ static int resolveSelectStep(Walker *pWalker, Select *p){
sNC.pNext = pOuterNC;
/* Resolve names in the result set. */
- pEList = p->pEList;
- assert( pEList!=0 );
- for(i=0; i<pEList->nExpr; i++){
- Expr *pX = pEList->a[i].pExpr;
- if( sqlite3ResolveExprNames(&sNC, pX) ){
- return WRC_Abort;
- }
- }
+ if( sqlite3ResolveExprListNames(&sNC, p->pEList) ) return WRC_Abort;
/* If there are no aggregate functions in the result-set, and no GROUP BY
** expression, do not allow aggregates in any of the other expressions.
@@ -82573,6 +88697,16 @@ static int resolveSelectStep(Walker *pWalker, Select *p){
if( sqlite3ResolveExprNames(&sNC, p->pHaving) ) return WRC_Abort;
if( sqlite3ResolveExprNames(&sNC, p->pWhere) ) return WRC_Abort;
+ /* Resolve names in table-valued-function arguments */
+ for(i=0; i<p->pSrc->nSrc; i++){
+ struct SrcList_item *pItem = &p->pSrc->a[i];
+ if( pItem->fg.isTabFunc
+ && sqlite3ResolveExprListNames(&sNC, pItem->u1.pFuncArg)
+ ){
+ return WRC_Abort;
+ }
+ }
+
/* The ORDER BY and GROUP BY clauses may not refer to terms in
** outer queries
*/
@@ -82627,6 +88761,13 @@ static int resolveSelectStep(Walker *pWalker, Select *p){
}
}
+ /* If this is part of a compound SELECT, check that it has the right
+ ** number of expressions in the select list. */
+ if( p->pNext && p->pEList->nExpr!=p->pNext->pEList->nExpr ){
+ sqlite3SelectWrongNumTermsError(pParse, p->pNext);
+ return WRC_Abort;
+ }
+
/* Advance to the next term of the compound
*/
p = p->pPrior;
@@ -82710,10 +88851,12 @@ SQLITE_PRIVATE int sqlite3ResolveExprNames(
#endif
savedHasAgg = pNC->ncFlags & (NC_HasAgg|NC_MinMaxAgg);
pNC->ncFlags &= ~(NC_HasAgg|NC_MinMaxAgg);
- memset(&w, 0, sizeof(w));
+ w.pParse = pNC->pParse;
w.xExprCallback = resolveExprStep;
w.xSelectCallback = resolveSelectStep;
- w.pParse = pNC->pParse;
+ w.xSelectCallback2 = 0;
+ w.walkerDepth = 0;
+ w.eCode = 0;
w.u.pNC = pNC;
sqlite3WalkExpr(&w, pExpr);
#if SQLITE_MAX_EXPR_DEPTH>0
@@ -82729,6 +88872,23 @@ SQLITE_PRIVATE int sqlite3ResolveExprNames(
return ExprHasProperty(pExpr, EP_Error);
}
+/*
+** Resolve all names for all expression in an expression list. This is
+** just like sqlite3ResolveExprNames() except that it works for an expression
+** list rather than a single expression.
+*/
+SQLITE_PRIVATE int sqlite3ResolveExprListNames(
+ NameContext *pNC, /* Namespace to resolve expressions in. */
+ ExprList *pList /* The expression list to be analyzed. */
+){
+ int i;
+ if( pList ){
+ for(i=0; i<pList->nExpr; i++){
+ if( sqlite3ResolveExprNames(pNC, pList->a[i].pExpr) ) return WRC_Abort;
+ }
+ }
+ return WRC_Continue;
+}
/*
** Resolve all names in all expressions of a SELECT and in all
@@ -82772,15 +88932,14 @@ SQLITE_PRIVATE void sqlite3ResolveSelectNames(
SQLITE_PRIVATE void sqlite3ResolveSelfReference(
Parse *pParse, /* Parsing context */
Table *pTab, /* The table being referenced */
- int type, /* NC_IsCheck or NC_PartIdx */
+ int type, /* NC_IsCheck or NC_PartIdx or NC_IdxExpr */
Expr *pExpr, /* Expression to resolve. May be NULL. */
ExprList *pList /* Expression list to resolve. May be NUL. */
){
SrcList sSrc; /* Fake SrcList for pParse->pNewTable */
NameContext sNC; /* Name context for pParse->pNewTable */
- int i; /* Loop counter */
- assert( type==NC_IsCheck || type==NC_PartIdx );
+ assert( type==NC_IsCheck || type==NC_PartIdx || type==NC_IdxExpr );
memset(&sNC, 0, sizeof(sNC));
memset(&sSrc, 0, sizeof(sSrc));
sSrc.nSrc = 1;
@@ -82791,13 +88950,7 @@ SQLITE_PRIVATE void sqlite3ResolveSelfReference(
sNC.pSrcList = &sSrc;
sNC.ncFlags = type;
if( sqlite3ResolveExprNames(&sNC, pExpr) ) return;
- if( pList ){
- for(i=0; i<pList->nExpr; i++){
- if( sqlite3ResolveExprNames(&sNC, pList->a[i].pExpr) ){
- return;
- }
- }
- }
+ if( pList ) sqlite3ResolveExprListNames(&sNC, pList);
}
/************** End of resolve.c *********************************************/
@@ -82816,6 +88969,7 @@ SQLITE_PRIVATE void sqlite3ResolveSelfReference(
** This file contains routines used for analyzing expressions and
** for generating VDBE code that evaluates expressions in SQLite.
*/
+/* #include "sqliteInt.h" */
/*
** Return the 'affinity' of the expression pExpr if any.
@@ -82888,13 +89042,12 @@ SQLITE_PRIVATE Expr *sqlite3ExprAddCollateToken(
SQLITE_PRIVATE Expr *sqlite3ExprAddCollateString(Parse *pParse, Expr *pExpr, const char *zC){
Token s;
assert( zC!=0 );
- s.z = zC;
- s.n = sqlite3Strlen30(s.z);
+ sqlite3TokenInit(&s, (char*)zC);
return sqlite3ExprAddCollateToken(pParse, pExpr, &s, 0);
}
/*
-** Skip over any TK_COLLATE or TK_AS operators and any unlikely()
+** Skip over any TK_COLLATE operators and any unlikely()
** or likelihood() function at the root of an expression.
*/
SQLITE_PRIVATE Expr *sqlite3ExprSkipCollate(Expr *pExpr){
@@ -82905,7 +89058,7 @@ SQLITE_PRIVATE Expr *sqlite3ExprSkipCollate(Expr *pExpr){
assert( pExpr->op==TK_FUNCTION );
pExpr = pExpr->x.pList->a[0].pExpr;
}else{
- assert( pExpr->op==TK_COLLATE || pExpr->op==TK_AS );
+ assert( pExpr->op==TK_COLLATE );
pExpr = pExpr->pLeft;
}
}
@@ -82994,13 +89147,13 @@ SQLITE_PRIVATE char sqlite3CompareAffinity(Expr *pExpr, char aff2){
if( sqlite3IsNumericAffinity(aff1) || sqlite3IsNumericAffinity(aff2) ){
return SQLITE_AFF_NUMERIC;
}else{
- return SQLITE_AFF_NONE;
+ return SQLITE_AFF_BLOB;
}
}else if( !aff1 && !aff2 ){
/* Neither side of the comparison is a column. Compare the
** results directly.
*/
- return SQLITE_AFF_NONE;
+ return SQLITE_AFF_BLOB;
}else{
/* One side is a column, the other is not. Use the columns affinity. */
assert( aff1==0 || aff2==0 );
@@ -83024,7 +89177,7 @@ static char comparisonAffinity(Expr *pExpr){
}else if( ExprHasProperty(pExpr, EP_xIsSelect) ){
aff = sqlite3CompareAffinity(pExpr->x.pSelect->pEList->a[0].pExpr, aff);
}else if( !aff ){
- aff = SQLITE_AFF_NONE;
+ aff = SQLITE_AFF_BLOB;
}
return aff;
}
@@ -83038,7 +89191,7 @@ static char comparisonAffinity(Expr *pExpr){
SQLITE_PRIVATE int sqlite3IndexAffinityOk(Expr *pExpr, char idx_affinity){
char aff = comparisonAffinity(pExpr);
switch( aff ){
- case SQLITE_AFF_NONE:
+ case SQLITE_AFF_BLOB:
return 1;
case SQLITE_AFF_TEXT:
return idx_affinity==SQLITE_AFF_TEXT;
@@ -83236,7 +89389,7 @@ SQLITE_PRIVATE void sqlite3ExprSetHeightAndFlags(Parse *pParse, Expr *p){
** is responsible for making sure the node eventually gets freed.
**
** If dequote is true, then the token (if it exists) is dequoted.
-** If dequote is false, no dequoting is performance. The deQuote
+** If dequote is false, no dequoting is performed. The deQuote
** parameter is ignored if pToken is NULL or if the token does not
** appear to be quoted. If the quotes were of the form "..." (double-quotes)
** then the EP_DblQuoted flag is set on the expression node.
@@ -83257,6 +89410,7 @@ SQLITE_PRIVATE Expr *sqlite3ExprAlloc(
int nExtra = 0;
int iValue = 0;
+ assert( db!=0 );
if( pToken ){
if( op!=TK_INTEGER || pToken->z==0
|| sqlite3GetInt32(pToken->z, &iValue)==0 ){
@@ -83264,8 +89418,9 @@ SQLITE_PRIVATE Expr *sqlite3ExprAlloc(
assert( iValue>=0 );
}
}
- pNew = sqlite3DbMallocZero(db, sizeof(Expr)+nExtra);
+ pNew = sqlite3DbMallocRawNN(db, sizeof(Expr)+nExtra);
if( pNew ){
+ memset(pNew, 0, sizeof(Expr));
pNew->op = (u8)op;
pNew->iAgg = -1;
if( pToken ){
@@ -83273,15 +89428,13 @@ SQLITE_PRIVATE Expr *sqlite3ExprAlloc(
pNew->flags |= EP_IntValue;
pNew->u.iValue = iValue;
}else{
- int c;
pNew->u.zToken = (char*)&pNew[1];
assert( pToken->z!=0 || pToken->n==0 );
if( pToken->n ) memcpy(pNew->u.zToken, pToken->z, pToken->n);
pNew->u.zToken[pToken->n] = 0;
- if( dequote && nExtra>=3
- && ((c = pToken->z[0])=='\'' || c=='"' || c=='[' || c=='`') ){
+ if( dequote && sqlite3Isquote(pNew->u.zToken[0]) ){
+ if( pNew->u.zToken[0]=='"' ) pNew->flags |= EP_DblQuoted;
sqlite3Dequote(pNew->u.zToken);
- if( c=='"' ) pNew->flags |= EP_DblQuoted;
}
}
}
@@ -83351,11 +89504,11 @@ SQLITE_PRIVATE Expr *sqlite3PExpr(
const Token *pToken /* Argument token */
){
Expr *p;
- if( op==TK_AND && pLeft && pRight && pParse->nErr==0 ){
+ if( op==TK_AND && pParse->nErr==0 ){
/* Take advantage of short-circuit false optimization for AND */
p = sqlite3ExprAnd(pParse->db, pLeft, pRight);
}else{
- p = sqlite3ExprAlloc(pParse->db, op, pToken, 1);
+ p = sqlite3ExprAlloc(pParse->db, op & TKFLG_MASK, pToken, 1);
sqlite3ExprAttachSubtrees(pParse->db, p, pLeft, pRight);
}
if( p ) {
@@ -83365,6 +89518,22 @@ SQLITE_PRIVATE Expr *sqlite3PExpr(
}
/*
+** Add pSelect to the Expr.x.pSelect field. Or, if pExpr is NULL (due
+** do a memory allocation failure) then delete the pSelect object.
+*/
+SQLITE_PRIVATE void sqlite3PExprAddSelect(Parse *pParse, Expr *pExpr, Select *pSelect){
+ if( pExpr ){
+ pExpr->x.pSelect = pSelect;
+ ExprSetProperty(pExpr, EP_xIsSelect|EP_Subquery);
+ sqlite3ExprSetHeightAndFlags(pParse, pExpr);
+ }else{
+ assert( pParse->db->mallocFailed );
+ sqlite3SelectDelete(pParse->db, pSelect);
+ }
+}
+
+
+/*
** If the expression is always either TRUE or FALSE (respectively),
** then return 1. If one cannot determine the truth value of the
** expression at compile-time return 0.
@@ -83502,7 +89671,10 @@ SQLITE_PRIVATE void sqlite3ExprAssignVarNumber(Parse *pParse, Expr *pExpr){
if( x>pParse->nzVar ){
char **a;
a = sqlite3DbRealloc(db, pParse->azVar, x*sizeof(a[0]));
- if( a==0 ) return; /* Error reported through db->mallocFailed */
+ if( a==0 ){
+ assert( db->mallocFailed ); /* Error reported through mallocFailed */
+ return;
+ }
pParse->azVar = a;
memset(&a[pParse->nzVar], 0, (x-pParse->nzVar)*sizeof(a[0]));
pParse->nzVar = x;
@@ -83521,8 +89693,8 @@ SQLITE_PRIVATE void sqlite3ExprAssignVarNumber(Parse *pParse, Expr *pExpr){
/*
** Recursively delete an expression tree.
*/
-SQLITE_PRIVATE void sqlite3ExprDelete(sqlite3 *db, Expr *p){
- if( p==0 ) return;
+static SQLITE_NOINLINE void sqlite3ExprDeleteNN(sqlite3 *db, Expr *p){
+ assert( p!=0 );
/* Sanity check: Assert that the IntValue is non-negative if it exists */
assert( !ExprHasProperty(p, EP_IntValue) || p->u.iValue>=0 );
if( !ExprHasProperty(p, EP_TokenOnly) ){
@@ -83541,6 +89713,9 @@ SQLITE_PRIVATE void sqlite3ExprDelete(sqlite3 *db, Expr *p){
sqlite3DbFree(db, p);
}
}
+SQLITE_PRIVATE void sqlite3ExprDelete(sqlite3 *db, Expr *p){
+ if( p ) sqlite3ExprDeleteNN(db, p);
+}
/*
** Return the number of bytes allocated for the expression structure
@@ -83592,7 +89767,7 @@ static int dupedExprStructSize(Expr *p, int flags){
assert( flags==EXPRDUP_REDUCE || flags==0 ); /* Only one flag value allowed */
assert( EXPR_FULLSIZE<=0xfff );
assert( (0xfff & (EP_Reduced|EP_TokenOnly))==0 );
- if( 0==(flags&EXPRDUP_REDUCE) ){
+ if( 0==flags ){
nSize = EXPR_FULLSIZE;
}else{
assert( !ExprHasProperty(p, EP_TokenOnly|EP_Reduced) );
@@ -83654,84 +89829,88 @@ static int dupedExprSize(Expr *p, int flags){
** if any. Before returning, *pzBuffer is set to the first byte past the
** portion of the buffer copied into by this function.
*/
-static Expr *exprDup(sqlite3 *db, Expr *p, int flags, u8 **pzBuffer){
- Expr *pNew = 0; /* Value to return */
- if( p ){
- const int isReduced = (flags&EXPRDUP_REDUCE);
- u8 *zAlloc;
- u32 staticFlag = 0;
+static Expr *exprDup(sqlite3 *db, Expr *p, int dupFlags, u8 **pzBuffer){
+ Expr *pNew; /* Value to return */
+ u8 *zAlloc; /* Memory space from which to build Expr object */
+ u32 staticFlag; /* EP_Static if space not obtained from malloc */
- assert( pzBuffer==0 || isReduced );
+ assert( db!=0 );
+ assert( p );
+ assert( dupFlags==0 || dupFlags==EXPRDUP_REDUCE );
+ assert( pzBuffer==0 || dupFlags==EXPRDUP_REDUCE );
+
+ /* Figure out where to write the new Expr structure. */
+ if( pzBuffer ){
+ zAlloc = *pzBuffer;
+ staticFlag = EP_Static;
+ }else{
+ zAlloc = sqlite3DbMallocRawNN(db, dupedExprSize(p, dupFlags));
+ staticFlag = 0;
+ }
+ pNew = (Expr *)zAlloc;
- /* Figure out where to write the new Expr structure. */
- if( pzBuffer ){
- zAlloc = *pzBuffer;
- staticFlag = EP_Static;
+ if( pNew ){
+ /* Set nNewSize to the size allocated for the structure pointed to
+ ** by pNew. This is either EXPR_FULLSIZE, EXPR_REDUCEDSIZE or
+ ** EXPR_TOKENONLYSIZE. nToken is set to the number of bytes consumed
+ ** by the copy of the p->u.zToken string (if any).
+ */
+ const unsigned nStructSize = dupedExprStructSize(p, dupFlags);
+ const int nNewSize = nStructSize & 0xfff;
+ int nToken;
+ if( !ExprHasProperty(p, EP_IntValue) && p->u.zToken ){
+ nToken = sqlite3Strlen30(p->u.zToken) + 1;
}else{
- zAlloc = sqlite3DbMallocRaw(db, dupedExprSize(p, flags));
+ nToken = 0;
}
- pNew = (Expr *)zAlloc;
-
- if( pNew ){
- /* Set nNewSize to the size allocated for the structure pointed to
- ** by pNew. This is either EXPR_FULLSIZE, EXPR_REDUCEDSIZE or
- ** EXPR_TOKENONLYSIZE. nToken is set to the number of bytes consumed
- ** by the copy of the p->u.zToken string (if any).
- */
- const unsigned nStructSize = dupedExprStructSize(p, flags);
- const int nNewSize = nStructSize & 0xfff;
- int nToken;
- if( !ExprHasProperty(p, EP_IntValue) && p->u.zToken ){
- nToken = sqlite3Strlen30(p->u.zToken) + 1;
- }else{
- nToken = 0;
- }
- if( isReduced ){
- assert( ExprHasProperty(p, EP_Reduced)==0 );
- memcpy(zAlloc, p, nNewSize);
- }else{
- int nSize = exprStructSize(p);
- memcpy(zAlloc, p, nSize);
+ if( dupFlags ){
+ assert( ExprHasProperty(p, EP_Reduced)==0 );
+ memcpy(zAlloc, p, nNewSize);
+ }else{
+ u32 nSize = (u32)exprStructSize(p);
+ memcpy(zAlloc, p, nSize);
+ if( nSize<EXPR_FULLSIZE ){
memset(&zAlloc[nSize], 0, EXPR_FULLSIZE-nSize);
}
+ }
- /* Set the EP_Reduced, EP_TokenOnly, and EP_Static flags appropriately. */
- pNew->flags &= ~(EP_Reduced|EP_TokenOnly|EP_Static|EP_MemToken);
- pNew->flags |= nStructSize & (EP_Reduced|EP_TokenOnly);
- pNew->flags |= staticFlag;
-
- /* Copy the p->u.zToken string, if any. */
- if( nToken ){
- char *zToken = pNew->u.zToken = (char*)&zAlloc[nNewSize];
- memcpy(zToken, p->u.zToken, nToken);
- }
+ /* Set the EP_Reduced, EP_TokenOnly, and EP_Static flags appropriately. */
+ pNew->flags &= ~(EP_Reduced|EP_TokenOnly|EP_Static|EP_MemToken);
+ pNew->flags |= nStructSize & (EP_Reduced|EP_TokenOnly);
+ pNew->flags |= staticFlag;
- if( 0==((p->flags|pNew->flags) & EP_TokenOnly) ){
- /* Fill in the pNew->x.pSelect or pNew->x.pList member. */
- if( ExprHasProperty(p, EP_xIsSelect) ){
- pNew->x.pSelect = sqlite3SelectDup(db, p->x.pSelect, isReduced);
- }else{
- pNew->x.pList = sqlite3ExprListDup(db, p->x.pList, isReduced);
- }
- }
+ /* Copy the p->u.zToken string, if any. */
+ if( nToken ){
+ char *zToken = pNew->u.zToken = (char*)&zAlloc[nNewSize];
+ memcpy(zToken, p->u.zToken, nToken);
+ }
- /* Fill in pNew->pLeft and pNew->pRight. */
- if( ExprHasProperty(pNew, EP_Reduced|EP_TokenOnly) ){
- zAlloc += dupedExprNodeSize(p, flags);
- if( ExprHasProperty(pNew, EP_Reduced) ){
- pNew->pLeft = exprDup(db, p->pLeft, EXPRDUP_REDUCE, &zAlloc);
- pNew->pRight = exprDup(db, p->pRight, EXPRDUP_REDUCE, &zAlloc);
- }
- if( pzBuffer ){
- *pzBuffer = zAlloc;
- }
+ if( 0==((p->flags|pNew->flags) & EP_TokenOnly) ){
+ /* Fill in the pNew->x.pSelect or pNew->x.pList member. */
+ if( ExprHasProperty(p, EP_xIsSelect) ){
+ pNew->x.pSelect = sqlite3SelectDup(db, p->x.pSelect, dupFlags);
}else{
- if( !ExprHasProperty(p, EP_TokenOnly) ){
- pNew->pLeft = sqlite3ExprDup(db, p->pLeft, 0);
- pNew->pRight = sqlite3ExprDup(db, p->pRight, 0);
- }
+ pNew->x.pList = sqlite3ExprListDup(db, p->x.pList, dupFlags);
}
+ }
+ /* Fill in pNew->pLeft and pNew->pRight. */
+ if( ExprHasProperty(pNew, EP_Reduced|EP_TokenOnly) ){
+ zAlloc += dupedExprNodeSize(p, dupFlags);
+ if( ExprHasProperty(pNew, EP_Reduced) ){
+ pNew->pLeft = p->pLeft ?
+ exprDup(db, p->pLeft, EXPRDUP_REDUCE, &zAlloc) : 0;
+ pNew->pRight = p->pRight ?
+ exprDup(db, p->pRight, EXPRDUP_REDUCE, &zAlloc) : 0;
+ }
+ if( pzBuffer ){
+ *pzBuffer = zAlloc;
+ }
+ }else{
+ if( !ExprHasProperty(p, EP_TokenOnly) ){
+ pNew->pLeft = sqlite3ExprDup(db, p->pLeft, 0);
+ pNew->pRight = sqlite3ExprDup(db, p->pRight, 0);
+ }
}
}
return pNew;
@@ -83782,18 +89961,20 @@ static With *withDup(sqlite3 *db, With *p){
** part of the in-memory representation of the database schema.
*/
SQLITE_PRIVATE Expr *sqlite3ExprDup(sqlite3 *db, Expr *p, int flags){
- return exprDup(db, p, flags, 0);
+ assert( flags==0 || flags==EXPRDUP_REDUCE );
+ return p ? exprDup(db, p, flags, 0) : 0;
}
SQLITE_PRIVATE ExprList *sqlite3ExprListDup(sqlite3 *db, ExprList *p, int flags){
ExprList *pNew;
struct ExprList_item *pItem, *pOldItem;
int i;
+ assert( db!=0 );
if( p==0 ) return 0;
- pNew = sqlite3DbMallocRaw(db, sizeof(*pNew) );
+ pNew = sqlite3DbMallocRawNN(db, sizeof(*pNew) );
if( pNew==0 ) return 0;
pNew->nExpr = i = p->nExpr;
if( (flags & EXPRDUP_REDUCE)==0 ) for(i=1; i<p->nExpr; i+=i){}
- pNew->a = pItem = sqlite3DbMallocRaw(db, i*sizeof(p->a[0]) );
+ pNew->a = pItem = sqlite3DbMallocRawNN(db, i*sizeof(p->a[0]) );
if( pItem==0 ){
sqlite3DbFree(db, pNew);
return 0;
@@ -83824,9 +90005,10 @@ SQLITE_PRIVATE SrcList *sqlite3SrcListDup(sqlite3 *db, SrcList *p, int flags){
SrcList *pNew;
int i;
int nByte;
+ assert( db!=0 );
if( p==0 ) return 0;
nByte = sizeof(*p) + (p->nSrc>0 ? sizeof(p->a[0]) * (p->nSrc-1) : 0);
- pNew = sqlite3DbMallocRaw(db, nByte );
+ pNew = sqlite3DbMallocRawNN(db, nByte );
if( pNew==0 ) return 0;
pNew->nSrc = pNew->nAlloc = p->nSrc;
for(i=0; i<p->nSrc; i++){
@@ -83837,16 +90019,18 @@ SQLITE_PRIVATE SrcList *sqlite3SrcListDup(sqlite3 *db, SrcList *p, int flags){
pNewItem->zDatabase = sqlite3DbStrDup(db, pOldItem->zDatabase);
pNewItem->zName = sqlite3DbStrDup(db, pOldItem->zName);
pNewItem->zAlias = sqlite3DbStrDup(db, pOldItem->zAlias);
- pNewItem->jointype = pOldItem->jointype;
+ pNewItem->fg = pOldItem->fg;
pNewItem->iCursor = pOldItem->iCursor;
pNewItem->addrFillSub = pOldItem->addrFillSub;
pNewItem->regReturn = pOldItem->regReturn;
- pNewItem->isCorrelated = pOldItem->isCorrelated;
- pNewItem->viaCoroutine = pOldItem->viaCoroutine;
- pNewItem->isRecursive = pOldItem->isRecursive;
- pNewItem->zIndex = sqlite3DbStrDup(db, pOldItem->zIndex);
- pNewItem->notIndexed = pOldItem->notIndexed;
- pNewItem->pIndex = pOldItem->pIndex;
+ if( pNewItem->fg.isIndexedBy ){
+ pNewItem->u1.zIndexedBy = sqlite3DbStrDup(db, pOldItem->u1.zIndexedBy);
+ }
+ pNewItem->pIBIndex = pOldItem->pIBIndex;
+ if( pNewItem->fg.isTabFunc ){
+ pNewItem->u1.pFuncArg =
+ sqlite3ExprListDup(db, pOldItem->u1.pFuncArg, flags);
+ }
pTab = pNewItem->pTab = pOldItem->pTab;
if( pTab ){
pTab->nRef++;
@@ -83861,11 +90045,12 @@ SQLITE_PRIVATE SrcList *sqlite3SrcListDup(sqlite3 *db, SrcList *p, int flags){
SQLITE_PRIVATE IdList *sqlite3IdListDup(sqlite3 *db, IdList *p){
IdList *pNew;
int i;
+ assert( db!=0 );
if( p==0 ) return 0;
- pNew = sqlite3DbMallocRaw(db, sizeof(*pNew) );
+ pNew = sqlite3DbMallocRawNN(db, sizeof(*pNew) );
if( pNew==0 ) return 0;
pNew->nId = p->nId;
- pNew->a = sqlite3DbMallocRaw(db, p->nId*sizeof(p->a[0]) );
+ pNew->a = sqlite3DbMallocRawNN(db, p->nId*sizeof(p->a[0]) );
if( pNew->a==0 ){
sqlite3DbFree(db, pNew);
return 0;
@@ -83883,8 +90068,9 @@ SQLITE_PRIVATE IdList *sqlite3IdListDup(sqlite3 *db, IdList *p){
}
SQLITE_PRIVATE Select *sqlite3SelectDup(sqlite3 *db, Select *p, int flags){
Select *pNew, *pPrior;
+ assert( db!=0 );
if( p==0 ) return 0;
- pNew = sqlite3DbMallocRaw(db, sizeof(*p) );
+ pNew = sqlite3DbMallocRawNN(db, sizeof(*p) );
if( pNew==0 ) return 0;
pNew->pEList = sqlite3ExprListDup(db, p->pEList, flags);
pNew->pSrc = sqlite3SrcListDup(db, p->pSrc, flags);
@@ -83930,12 +90116,14 @@ SQLITE_PRIVATE ExprList *sqlite3ExprListAppend(
Expr *pExpr /* Expression to be appended. Might be NULL */
){
sqlite3 *db = pParse->db;
+ assert( db!=0 );
if( pList==0 ){
- pList = sqlite3DbMallocZero(db, sizeof(ExprList) );
+ pList = sqlite3DbMallocRawNN(db, sizeof(ExprList) );
if( pList==0 ){
goto no_mem;
}
- pList->a = sqlite3DbMallocRaw(db, sizeof(pList->a[0]));
+ pList->nExpr = 0;
+ pList->a = sqlite3DbMallocRawNN(db, sizeof(pList->a[0]));
if( pList->a==0 ) goto no_mem;
}else if( (pList->nExpr & (pList->nExpr-1))==0 ){
struct ExprList_item *a;
@@ -83962,6 +90150,20 @@ no_mem:
}
/*
+** Set the sort order for the last element on the given ExprList.
+*/
+SQLITE_PRIVATE void sqlite3ExprListSetSortOrder(ExprList *p, int iSortOrder){
+ if( p==0 ) return;
+ assert( SQLITE_SO_UNDEFINED<0 && SQLITE_SO_ASC>=0 && SQLITE_SO_DESC>0 );
+ assert( p->nExpr>0 );
+ if( iSortOrder<0 ){
+ assert( p->a[p->nExpr-1].sortOrder==SQLITE_SO_ASC );
+ return;
+ }
+ p->a[p->nExpr-1].sortOrder = (u8)iSortOrder;
+}
+
+/*
** Set the ExprList.a[].zName element of the most recently added item
** on the expression list.
**
@@ -83982,7 +90184,7 @@ SQLITE_PRIVATE void sqlite3ExprListSetName(
pItem = &pList->a[pList->nExpr-1];
assert( pItem->zName==0 );
pItem->zName = sqlite3DbStrNDup(pParse->db, pName->z, pName->n);
- if( dequote && pItem->zName ) sqlite3Dequote(pItem->zName);
+ if( dequote ) sqlite3Dequote(pItem->zName);
}
}
@@ -84031,10 +90233,9 @@ SQLITE_PRIVATE void sqlite3ExprListCheckLength(
/*
** Delete an entire expression list.
*/
-SQLITE_PRIVATE void sqlite3ExprListDelete(sqlite3 *db, ExprList *pList){
+static SQLITE_NOINLINE void exprListDeleteNN(sqlite3 *db, ExprList *pList){
int i;
struct ExprList_item *pItem;
- if( pList==0 ) return;
assert( pList->a!=0 || pList->nExpr==0 );
for(pItem=pList->a, i=0; i<pList->nExpr; i++, pItem++){
sqlite3ExprDelete(db, pItem->pExpr);
@@ -84044,6 +90245,9 @@ SQLITE_PRIVATE void sqlite3ExprListDelete(sqlite3 *db, ExprList *pList){
sqlite3DbFree(db, pList->a);
sqlite3DbFree(db, pList);
}
+SQLITE_PRIVATE void sqlite3ExprListDelete(sqlite3 *db, ExprList *pList){
+ if( pList ) exprListDeleteNN(db, pList);
+}
/*
** Return the bitwise-OR of all Expr.flags fields in the given
@@ -84055,7 +90259,8 @@ SQLITE_PRIVATE u32 sqlite3ExprListFlags(const ExprList *pList){
if( pList ){
for(i=0; i<pList->nExpr; i++){
Expr *pExpr = pList->a[i].pExpr;
- if( ALWAYS(pExpr) ) m |= pExpr->flags;
+ assert( pExpr!=0 );
+ m |= pExpr->flags;
}
}
return m;
@@ -84071,7 +90276,7 @@ SQLITE_PRIVATE u32 sqlite3ExprListFlags(const ExprList *pList){
**
** sqlite3ExprIsConstant() pWalker->eCode==1
** sqlite3ExprIsConstantNotJoin() pWalker->eCode==2
-** sqlite3ExprRefOneTableOnly() pWalker->eCode==3
+** sqlite3ExprIsTableConstant() pWalker->eCode==3
** sqlite3ExprIsConstantOrFunction() pWalker->eCode==4 or 5
**
** In all cases, the callbacks set Walker.eCode=0 and abort if the expression
@@ -84179,7 +90384,7 @@ SQLITE_PRIVATE int sqlite3ExprIsConstantNotJoin(Expr *p){
}
/*
-** Walk an expression tree. Return non-zero if the expression constant
+** Walk an expression tree. Return non-zero if the expression is constant
** for any single row of the table with cursor iCur. In other words, the
** expression must not refer to any non-deterministic function nor any
** table other than iCur.
@@ -84202,6 +90407,22 @@ SQLITE_PRIVATE int sqlite3ExprIsConstantOrFunction(Expr *p, u8 isInit){
return exprIsConst(p, 4+isInit, 0);
}
+#ifdef SQLITE_ENABLE_CURSOR_HINTS
+/*
+** Walk an expression tree. Return 1 if the expression contains a
+** subquery of some kind. Return 0 if there are no subqueries.
+*/
+SQLITE_PRIVATE int sqlite3ExprContainsSubquery(Expr *p){
+ Walker w;
+ memset(&w, 0, sizeof(w));
+ w.eCode = 1;
+ w.xExprCallback = sqlite3ExprWalkNoop;
+ w.xSelectCallback = selectNodeIsConstant;
+ sqlite3WalkExpr(&w, p);
+ return w.eCode==0;
+}
+#endif
+
/*
** If the expression p codes a constant integer that is small enough
** to fit in a 32-bit integer, return 1 and put the value of the integer
@@ -84285,7 +90506,7 @@ SQLITE_PRIVATE int sqlite3ExprCanBeNull(const Expr *p){
*/
SQLITE_PRIVATE int sqlite3ExprNeedsNoAffinityChange(const Expr *p, char aff){
u8 op;
- if( aff==SQLITE_AFF_NONE ) return 1;
+ if( aff==SQLITE_AFF_BLOB ) return 1;
while( p->op==TK_UPLUS || p->op==TK_UMINUS ){ p = p->pLeft; }
op = p->op;
if( op==TK_REGISTER ) op = p->op2;
@@ -84324,23 +90545,22 @@ SQLITE_PRIVATE int sqlite3IsRowid(const char *z){
}
/*
-** Return true if we are able to the IN operator optimization on a
-** query of the form
-**
-** x IN (SELECT ...)
-**
-** Where the SELECT... clause is as specified by the parameter to this
-** routine.
-**
-** The Select object passed in has already been preprocessed and no
-** errors have been found.
+** pX is the RHS of an IN operator. If pX is a SELECT statement
+** that can be simplified to a direct table access, then return
+** a pointer to the SELECT statement. If pX is not a SELECT statement,
+** or if the SELECT statement needs to be manifested into a transient
+** table, then return NULL.
*/
#ifndef SQLITE_OMIT_SUBQUERY
-static int isCandidateForInOpt(Select *p){
+static Select *isCandidateForInOpt(Expr *pX){
+ Select *p;
SrcList *pSrc;
ExprList *pEList;
+ Expr *pRes;
Table *pTab;
- if( p==0 ) return 0; /* right-hand side of IN is SELECT */
+ if( !ExprHasProperty(pX, EP_xIsSelect) ) return 0; /* Not a subquery */
+ if( ExprHasProperty(pX, EP_VarSelect) ) return 0; /* Correlated subq */
+ p = pX->x.pSelect;
if( p->pPrior ) return 0; /* Not a compound SELECT */
if( p->selFlags & (SF_Distinct|SF_Aggregate) ){
testcase( (p->selFlags & (SF_Distinct|SF_Aggregate))==SF_Distinct );
@@ -84356,13 +90576,15 @@ static int isCandidateForInOpt(Select *p){
if( pSrc->nSrc!=1 ) return 0; /* Single term in FROM clause */
if( pSrc->a[0].pSelect ) return 0; /* FROM is not a subquery or view */
pTab = pSrc->a[0].pTab;
- if( NEVER(pTab==0) ) return 0;
+ assert( pTab!=0 );
assert( pTab->pSelect==0 ); /* FROM clause is not a view */
if( IsVirtual(pTab) ) return 0; /* FROM clause not a virtual table */
pEList = p->pEList;
if( pEList->nExpr!=1 ) return 0; /* One column in the result set */
- if( pEList->a[0].pExpr->op!=TK_COLUMN ) return 0; /* Result is a column */
- return 1;
+ pRes = pEList->a[0].pExpr;
+ if( pRes->op!=TK_COLUMN ) return 0; /* Result is a column */
+ assert( pRes->iTable==pSrc->a[0].iCursor ); /* Not a correlated subquery */
+ return p;
}
#endif /* SQLITE_OMIT_SUBQUERY */
@@ -84382,13 +90604,13 @@ SQLITE_PRIVATE int sqlite3CodeOnce(Parse *pParse){
** to be set to NULL if iCur contains one or more NULL values.
*/
static void sqlite3SetHasNullFlag(Vdbe *v, int iCur, int regHasNull){
- int j1;
+ int addr1;
sqlite3VdbeAddOp2(v, OP_Integer, 0, regHasNull);
- j1 = sqlite3VdbeAddOp1(v, OP_Rewind, iCur); VdbeCoverage(v);
+ addr1 = sqlite3VdbeAddOp1(v, OP_Rewind, iCur); VdbeCoverage(v);
sqlite3VdbeAddOp3(v, OP_Column, iCur, 0, regHasNull);
sqlite3VdbeChangeP5(v, OPFLAG_TYPEOFARG);
VdbeComment((v, "first_entry_in(%d)", iCur));
- sqlite3VdbeJumpHere(v, j1);
+ sqlite3VdbeJumpHere(v, addr1);
}
@@ -84494,15 +90716,13 @@ SQLITE_PRIVATE int sqlite3FindInIndex(Parse *pParse, Expr *pX, u32 inFlags, int
** satisfy the query. This is preferable to generating a new
** ephemeral table.
*/
- p = (ExprHasProperty(pX, EP_xIsSelect) ? pX->x.pSelect : 0);
- if( pParse->nErr==0 && isCandidateForInOpt(p) ){
+ if( pParse->nErr==0 && (p = isCandidateForInOpt(pX))!=0 ){
sqlite3 *db = pParse->db; /* Database connection */
Table *pTab; /* Table <table>. */
Expr *pExpr; /* Expression <column> */
i16 iCol; /* Index of column <column> */
i16 iDb; /* Database idx for pTab */
- assert( p ); /* Because of isCandidateForInOpt(p) */
assert( p->pEList!=0 ); /* Because of isCandidateForInOpt(p) */
assert( p->pEList->a[0].pExpr!=0 ); /* Because of isCandidateForInOpt(p) */
assert( p->pSrc!=0 ); /* Because of isCandidateForInOpt(p) */
@@ -84660,9 +90880,10 @@ SQLITE_PRIVATE int sqlite3CodeSubselect(
#ifndef SQLITE_OMIT_EXPLAIN
if( pParse->explain==2 ){
- char *zMsg = sqlite3MPrintf(
- pParse->db, "EXECUTE %s%s SUBQUERY %d", jmpIfDynamic>=0?"":"CORRELATED ",
- pExpr->op==TK_IN?"LIST":"SCALAR", pParse->iNextSelectId
+ char *zMsg = sqlite3MPrintf(pParse->db, "EXECUTE %s%s SUBQUERY %d",
+ jmpIfDynamic>=0?"":"CORRELATED ",
+ pExpr->op==TK_IN?"LIST":"SCALAR",
+ pParse->iNextSelectId
);
sqlite3VdbeAddOp4(v, OP_Explain, pParse->iSelectId, 0, 0, zMsg, P4_DYNAMIC);
}
@@ -84736,7 +90957,7 @@ SQLITE_PRIVATE int sqlite3CodeSubselect(
int r1, r2, r3;
if( !affinity ){
- affinity = SQLITE_AFF_NONE;
+ affinity = SQLITE_AFF_BLOB;
}
if( pKeyInfo ){
assert( sqlite3KeyInfoIsWriteable(pKeyInfo) );
@@ -84932,7 +91153,7 @@ static void sqlite3ExprCodeIN(
}
if( regCkNull ){
sqlite3VdbeAddOp2(v, OP_IsNull, regCkNull, destIfNull); VdbeCoverage(v);
- sqlite3VdbeAddOp2(v, OP_Goto, 0, destIfFalse);
+ sqlite3VdbeGoto(v, destIfFalse);
}
sqlite3VdbeResolveLabel(v, labelOk);
sqlite3ReleaseTempReg(pParse, regCkNull);
@@ -84950,7 +91171,7 @@ static void sqlite3ExprCodeIN(
int addr1 = sqlite3VdbeAddOp1(v, OP_NotNull, r1); VdbeCoverage(v);
sqlite3VdbeAddOp2(v, OP_Rewind, pExpr->iTable, destIfFalse);
VdbeCoverage(v);
- sqlite3VdbeAddOp2(v, OP_Goto, 0, destIfNull);
+ sqlite3VdbeGoto(v, destIfNull);
sqlite3VdbeJumpHere(v, addr1);
}
}
@@ -84988,7 +91209,7 @@ static void sqlite3ExprCodeIN(
** the presence of a NULL on the RHS makes a difference in the
** outcome.
*/
- int j1;
+ int addr1;
/* First check to see if the LHS is contained in the RHS. If so,
** then the answer is TRUE the presence of NULLs in the RHS does
@@ -84996,12 +91217,12 @@ static void sqlite3ExprCodeIN(
** answer is NULL if the RHS contains NULLs and the answer is
** FALSE if the RHS is NULL-free.
*/
- j1 = sqlite3VdbeAddOp4Int(v, OP_Found, pExpr->iTable, 0, r1, 1);
+ addr1 = sqlite3VdbeAddOp4Int(v, OP_Found, pExpr->iTable, 0, r1, 1);
VdbeCoverage(v);
sqlite3VdbeAddOp2(v, OP_IsNull, rRhsHasNull, destIfNull);
VdbeCoverage(v);
- sqlite3VdbeAddOp2(v, OP_Goto, 0, destIfFalse);
- sqlite3VdbeJumpHere(v, j1);
+ sqlite3VdbeGoto(v, destIfFalse);
+ sqlite3VdbeJumpHere(v, addr1);
}
}
}
@@ -85011,17 +91232,6 @@ static void sqlite3ExprCodeIN(
}
#endif /* SQLITE_OMIT_SUBQUERY */
-/*
-** Duplicate an 8-byte value
-*/
-static char *dup8bytes(Vdbe *v, const char *in){
- char *out = sqlite3DbMallocRaw(sqlite3VdbeDb(v), 8);
- if( out ){
- memcpy(out, in, 8);
- }
- return out;
-}
-
#ifndef SQLITE_OMIT_FLOATING_POINT
/*
** Generate an instruction that will put the floating point
@@ -85034,12 +91244,10 @@ static char *dup8bytes(Vdbe *v, const char *in){
static void codeReal(Vdbe *v, const char *z, int negateFlag, int iMem){
if( ALWAYS(z!=0) ){
double value;
- char *zV;
sqlite3AtoF(z, &value, sqlite3Strlen30(z), SQLITE_UTF8);
assert( !sqlite3IsNaN(value) ); /* The new AtoF never returns NaN */
if( negateFlag ) value = -value;
- zV = dup8bytes(v, (char*)&value);
- sqlite3VdbeAddOp4(v, OP_Real, 0, iMem, 0, zV, P4_REAL);
+ sqlite3VdbeAddOp4Dup8(v, OP_Real, 0, iMem, 0, (u8*)&value, P4_REAL);
}
}
#endif
@@ -85065,10 +91273,8 @@ static void codeInteger(Parse *pParse, Expr *pExpr, int negFlag, int iMem){
assert( z!=0 );
c = sqlite3DecOrHexToI64(z, &value);
if( c==0 || (c==2 && negFlag) ){
- char *zV;
if( negFlag ){ value = c==2 ? SMALLEST_INT64 : -value; }
- zV = dup8bytes(v, (char*)&value);
- sqlite3VdbeAddOp4(v, OP_Int64, 0, iMem, 0, zV, P4_INT64);
+ sqlite3VdbeAddOp4Dup8(v, OP_Int64, 0, iMem, 0, (u8*)&value, P4_INT64);
}else{
#ifdef SQLITE_OMIT_FLOATING_POINT
sqlite3ErrorMsg(pParse, "oversized integer: %s%s", negFlag ? "-" : "", z);
@@ -85086,6 +91292,19 @@ static void codeInteger(Parse *pParse, Expr *pExpr, int negFlag, int iMem){
}
}
+#if defined(SQLITE_DEBUG)
+/*
+** Verify the consistency of the column cache
+*/
+static int cacheIsValid(Parse *pParse){
+ int i, n;
+ for(i=n=0; i<SQLITE_N_COLCACHE; i++){
+ if( pParse->aColCache[i].iReg>0 ) n++;
+ }
+ return n==pParse->nColCache;
+}
+#endif
+
/*
** Clear a cache entry.
*/
@@ -85096,6 +91315,9 @@ static void cacheEntryClear(Parse *pParse, struct yColCache *p){
}
p->tempReg = 0;
}
+ p->iReg = 0;
+ pParse->nColCache--;
+ assert( pParse->db->mallocFailed || cacheIsValid(pParse) );
}
@@ -85139,6 +91361,8 @@ SQLITE_PRIVATE void sqlite3ExprCacheStore(Parse *pParse, int iTab, int iCol, int
p->iReg = iReg;
p->tempReg = 0;
p->lru = pParse->iCacheCnt++;
+ pParse->nColCache++;
+ assert( pParse->db->mallocFailed || cacheIsValid(pParse) );
return;
}
}
@@ -85160,6 +91384,7 @@ SQLITE_PRIVATE void sqlite3ExprCacheStore(Parse *pParse, int iTab, int iCol, int
p->iReg = iReg;
p->tempReg = 0;
p->lru = pParse->iCacheCnt++;
+ assert( cacheIsValid(pParse) );
return;
}
}
@@ -85169,15 +91394,13 @@ SQLITE_PRIVATE void sqlite3ExprCacheStore(Parse *pParse, int iTab, int iCol, int
** Purge the range of registers from the column cache.
*/
SQLITE_PRIVATE void sqlite3ExprCacheRemove(Parse *pParse, int iReg, int nReg){
- int i;
- int iLast = iReg + nReg - 1;
struct yColCache *p;
- for(i=0, p=pParse->aColCache; i<SQLITE_N_COLCACHE; i++, p++){
- int r = p->iReg;
- if( r>=iReg && r<=iLast ){
- cacheEntryClear(pParse, p);
- p->iReg = 0;
- }
+ if( iReg<=0 || pParse->nColCache==0 ) return;
+ p = &pParse->aColCache[SQLITE_N_COLCACHE-1];
+ while(1){
+ if( p->iReg >= iReg && p->iReg < iReg+nReg ) cacheEntryClear(pParse, p);
+ if( p==pParse->aColCache ) break;
+ p--;
}
}
@@ -85213,7 +91436,6 @@ SQLITE_PRIVATE void sqlite3ExprCachePop(Parse *pParse){
for(i=0, p=pParse->aColCache; i<SQLITE_N_COLCACHE; i++, p++){
if( p->iReg && p->iLevel>pParse->iCacheLevel ){
cacheEntryClear(pParse, p);
- p->iReg = 0;
}
}
}
@@ -85234,6 +91456,28 @@ static void sqlite3ExprCachePinRegister(Parse *pParse, int iReg){
}
}
+/* Generate code that will load into register regOut a value that is
+** appropriate for the iIdxCol-th column of index pIdx.
+*/
+SQLITE_PRIVATE void sqlite3ExprCodeLoadIndexColumn(
+ Parse *pParse, /* The parsing context */
+ Index *pIdx, /* The index whose column is to be loaded */
+ int iTabCur, /* Cursor pointing to a table row */
+ int iIdxCol, /* The column of the index to be loaded */
+ int regOut /* Store the index column value in this register */
+){
+ i16 iTabCol = pIdx->aiColumn[iIdxCol];
+ if( iTabCol==XN_EXPR ){
+ assert( pIdx->aColExpr );
+ assert( pIdx->aColExpr->nExpr>iIdxCol );
+ pParse->iSelfTab = iTabCur;
+ sqlite3ExprCodeCopy(pParse, pIdx->aColExpr->a[iIdxCol].pExpr, regOut);
+ }else{
+ sqlite3ExprCodeGetColumnOfTable(pParse->pVdbe, pIdx->pTable, iTabCur,
+ iTabCol, regOut);
+ }
+}
+
/*
** Generate code to extract the value of the iCol-th column of a table.
*/
@@ -85261,9 +91505,12 @@ SQLITE_PRIVATE void sqlite3ExprCodeGetColumnOfTable(
/*
** Generate code that will extract the iColumn-th column from
-** table pTab and store the column value in a register. An effort
-** is made to store the column value in register iReg, but this is
-** not guaranteed. The location of the column value is returned.
+** table pTab and store the column value in a register.
+**
+** An effort is made to store the column value in register iReg. This
+** is not garanteeed for GetColumn() - the result can be stored in
+** any register. But the result is guaranteed to land in register iReg
+** for GetColumnToReg().
**
** There must be an open cursor to pTab in iTable when this routine
** is called. If iColumn<0 then code is generated that extracts the rowid.
@@ -85274,7 +91521,7 @@ SQLITE_PRIVATE int sqlite3ExprCodeGetColumn(
int iColumn, /* Index of the table column */
int iTable, /* The cursor pointing to the table */
int iReg, /* Store results here */
- u8 p5 /* P5 value for OP_Column */
+ u8 p5 /* P5 value for OP_Column + FLAGS */
){
Vdbe *v = pParse->pVdbe;
int i;
@@ -85296,6 +91543,17 @@ SQLITE_PRIVATE int sqlite3ExprCodeGetColumn(
}
return iReg;
}
+SQLITE_PRIVATE void sqlite3ExprCodeGetColumnToReg(
+ Parse *pParse, /* Parsing and code generating context */
+ Table *pTab, /* Description of the table we are reading from */
+ int iColumn, /* Index of the table column */
+ int iTable, /* The cursor pointing to the table */
+ int iReg /* Store results here */
+){
+ int r1 = sqlite3ExprCodeGetColumn(pParse, pTab, iColumn, iTable, iReg, 0);
+ if( r1!=iReg ) sqlite3VdbeAddOp2(pParse->pVdbe, OP_SCopy, r1, iReg);
+}
+
/*
** Clear all column cache entries.
@@ -85312,7 +91570,6 @@ SQLITE_PRIVATE void sqlite3ExprCacheClear(Parse *pParse){
for(i=0, p=pParse->aColCache; i<SQLITE_N_COLCACHE; i++, p++){
if( p->iReg ){
cacheEntryClear(pParse, p);
- p->iReg = 0;
}
}
}
@@ -85354,6 +91611,7 @@ static int usedAsColumnCache(Parse *pParse, int iFrom, int iTo){
}
#endif /* SQLITE_DEBUG || SQLITE_COVERAGE_TEST */
+
/*
** Convert an expression node to a TK_REGISTER
*/
@@ -85419,8 +91677,9 @@ SQLITE_PRIVATE int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target)
inReg = pExpr->iColumn + pParse->ckBase;
break;
}else{
- /* Deleting from a partial index */
- iTab = pParse->iPartIdxTab;
+ /* Coding an expression that is part of an index where column names
+ ** in the index refer to the table to which the index belongs */
+ iTab = pParse->iSelfTab;
}
}
inReg = sqlite3ExprCodeGetColumn(pParse, pExpr->pTab,
@@ -85441,7 +91700,7 @@ SQLITE_PRIVATE int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target)
#endif
case TK_STRING: {
assert( !ExprHasProperty(pExpr, EP_IntValue) );
- sqlite3VdbeAddOp4(v, OP_String8, 0, target, 0, pExpr->u.zToken, 0);
+ sqlite3VdbeLoadString(v, target, pExpr->u.zToken);
break;
}
case TK_NULL: {
@@ -85480,10 +91739,6 @@ SQLITE_PRIVATE int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target)
inReg = pExpr->iTable;
break;
}
- case TK_AS: {
- inReg = sqlite3ExprCodeTarget(pParse, pExpr->pLeft, target);
- break;
- }
#ifndef SQLITE_OMIT_CAST
case TK_CAST: {
/* Expressions of the form: CAST(pLeft AS token) */
@@ -85625,7 +91880,6 @@ SQLITE_PRIVATE int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target)
ExprList *pFarg; /* List of function arguments */
int nFarg; /* Number of function arguments */
FuncDef *pDef; /* The function definition object */
- int nId; /* Length of the function name in bytes */
const char *zId; /* The function name */
u32 constMask = 0; /* Mask of function arguments that are constant */
int i; /* Loop counter */
@@ -85641,10 +91895,9 @@ SQLITE_PRIVATE int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target)
nFarg = pFarg ? pFarg->nExpr : 0;
assert( !ExprHasProperty(pExpr, EP_IntValue) );
zId = pExpr->u.zToken;
- nId = sqlite3Strlen30(zId);
- pDef = sqlite3FindFunction(db, zId, nId, nFarg, enc, 0);
- if( pDef==0 || pDef->xFunc==0 ){
- sqlite3ErrorMsg(pParse, "unknown function: %.*s()", nId, zId);
+ pDef = sqlite3FindFunction(db, zId, nFarg, enc, 0);
+ if( pDef==0 || pDef->xFinalize!=0 ){
+ sqlite3ErrorMsg(pParse, "unknown function: %s()", zId);
break;
}
@@ -85673,7 +91926,7 @@ SQLITE_PRIVATE int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target)
*/
if( pDef->funcFlags & SQLITE_FUNC_UNLIKELY ){
assert( nFarg>=1 );
- sqlite3ExprCode(pParse, pFarg->a[0].pExpr, target);
+ inReg = sqlite3ExprCodeTarget(pParse, pFarg->a[0].pExpr, target);
break;
}
@@ -85714,7 +91967,7 @@ SQLITE_PRIVATE int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target)
}
sqlite3ExprCachePush(pParse); /* Ticket 2ea2425d34be */
- sqlite3ExprCodeExprList(pParse, pFarg, r1,
+ sqlite3ExprCodeExprList(pParse, pFarg, r1, 0,
SQLITE_ECEL_DUP|SQLITE_ECEL_FACTOR);
sqlite3ExprCachePop(pParse); /* Ticket 2ea2425d34be */
}else{
@@ -85743,7 +91996,7 @@ SQLITE_PRIVATE int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target)
if( !pColl ) pColl = db->pDfltColl;
sqlite3VdbeAddOp4(v, OP_CollSeq, 0, 0, 0, (char *)pColl, P4_COLLSEQ);
}
- sqlite3VdbeAddOp4(v, OP_Function, constMask, r1, target,
+ sqlite3VdbeAddOp4(v, OP_Function0, constMask, r1, target,
(char*)pDef, P4_FUNCDEF);
sqlite3VdbeChangeP5(v, (u8)nFarg);
if( nFarg && constMask==0 ){
@@ -85809,6 +92062,7 @@ SQLITE_PRIVATE int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target)
sqlite3ReleaseTempReg(pParse, r4);
break;
}
+ case TK_SPAN:
case TK_COLLATE:
case TK_UPLUS: {
inReg = sqlite3ExprCodeTarget(pParse, pExpr->pLeft, target);
@@ -85938,7 +92192,7 @@ SQLITE_PRIVATE int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target)
sqlite3ExprIfFalse(pParse, pTest, nextCase, SQLITE_JUMPIFNULL);
testcase( aListelem[i+1].pExpr->op==TK_COLUMN );
sqlite3ExprCode(pParse, aListelem[i+1].pExpr, target);
- sqlite3VdbeAddOp2(v, OP_Goto, 0, endLabel);
+ sqlite3VdbeGoto(v, endLabel);
sqlite3ExprCachePop(pParse);
sqlite3VdbeResolveLabel(v, nextCase);
}
@@ -86069,7 +92323,7 @@ SQLITE_PRIVATE void sqlite3ExprCode(Parse *pParse, Expr *pExpr, int target){
sqlite3VdbeAddOp2(pParse->pVdbe, OP_Copy, pExpr->iTable, target);
}else{
inReg = sqlite3ExprCodeTarget(pParse, pExpr, target);
- assert( pParse->pVdbe || pParse->db->mallocFailed );
+ assert( pParse->pVdbe!=0 || pParse->db->mallocFailed );
if( inReg!=target && pParse->pVdbe ){
sqlite3VdbeAddOp2(pParse->pVdbe, OP_SCopy, inReg, target);
}
@@ -86077,6 +92331,18 @@ SQLITE_PRIVATE void sqlite3ExprCode(Parse *pParse, Expr *pExpr, int target){
}
/*
+** Make a transient copy of expression pExpr and then code it using
+** sqlite3ExprCode(). This routine works just like sqlite3ExprCode()
+** except that the input expression is guaranteed to be unchanged.
+*/
+SQLITE_PRIVATE void sqlite3ExprCodeCopy(Parse *pParse, Expr *pExpr, int target){
+ sqlite3 *db = pParse->db;
+ pExpr = sqlite3ExprDup(db, pExpr, 0);
+ if( !db->mallocFailed ) sqlite3ExprCode(pParse, pExpr, target);
+ sqlite3ExprDelete(db, pExpr);
+}
+
+/*
** Generate code that will evaluate expression pExpr and store the
** results in register target. The results are guaranteed to appear
** in register target. If the expression is constant, then this routine
@@ -86114,268 +92380,6 @@ SQLITE_PRIVATE void sqlite3ExprCodeAndCache(Parse *pParse, Expr *pExpr, int targ
exprToRegister(pExpr, iMem);
}
-#ifdef SQLITE_DEBUG
-/*
-** Generate a human-readable explanation of an expression tree.
-*/
-SQLITE_PRIVATE void sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u8 moreToFollow){
- const char *zBinOp = 0; /* Binary operator */
- const char *zUniOp = 0; /* Unary operator */
- pView = sqlite3TreeViewPush(pView, moreToFollow);
- if( pExpr==0 ){
- sqlite3TreeViewLine(pView, "nil");
- sqlite3TreeViewPop(pView);
- return;
- }
- switch( pExpr->op ){
- case TK_AGG_COLUMN: {
- sqlite3TreeViewLine(pView, "AGG{%d:%d}",
- pExpr->iTable, pExpr->iColumn);
- break;
- }
- case TK_COLUMN: {
- if( pExpr->iTable<0 ){
- /* This only happens when coding check constraints */
- sqlite3TreeViewLine(pView, "COLUMN(%d)", pExpr->iColumn);
- }else{
- sqlite3TreeViewLine(pView, "{%d:%d}",
- pExpr->iTable, pExpr->iColumn);
- }
- break;
- }
- case TK_INTEGER: {
- if( pExpr->flags & EP_IntValue ){
- sqlite3TreeViewLine(pView, "%d", pExpr->u.iValue);
- }else{
- sqlite3TreeViewLine(pView, "%s", pExpr->u.zToken);
- }
- break;
- }
-#ifndef SQLITE_OMIT_FLOATING_POINT
- case TK_FLOAT: {
- sqlite3TreeViewLine(pView,"%s", pExpr->u.zToken);
- break;
- }
-#endif
- case TK_STRING: {
- sqlite3TreeViewLine(pView,"%Q", pExpr->u.zToken);
- break;
- }
- case TK_NULL: {
- sqlite3TreeViewLine(pView,"NULL");
- break;
- }
-#ifndef SQLITE_OMIT_BLOB_LITERAL
- case TK_BLOB: {
- sqlite3TreeViewLine(pView,"%s", pExpr->u.zToken);
- break;
- }
-#endif
- case TK_VARIABLE: {
- sqlite3TreeViewLine(pView,"VARIABLE(%s,%d)",
- pExpr->u.zToken, pExpr->iColumn);
- break;
- }
- case TK_REGISTER: {
- sqlite3TreeViewLine(pView,"REGISTER(%d)", pExpr->iTable);
- break;
- }
- case TK_AS: {
- sqlite3TreeViewLine(pView,"AS %Q", pExpr->u.zToken);
- sqlite3TreeViewExpr(pView, pExpr->pLeft, 0);
- break;
- }
- case TK_ID: {
- sqlite3TreeViewLine(pView,"ID \"%w\"", pExpr->u.zToken);
- break;
- }
-#ifndef SQLITE_OMIT_CAST
- case TK_CAST: {
- /* Expressions of the form: CAST(pLeft AS token) */
- sqlite3TreeViewLine(pView,"CAST %Q", pExpr->u.zToken);
- sqlite3TreeViewExpr(pView, pExpr->pLeft, 0);
- break;
- }
-#endif /* SQLITE_OMIT_CAST */
- case TK_LT: zBinOp = "LT"; break;
- case TK_LE: zBinOp = "LE"; break;
- case TK_GT: zBinOp = "GT"; break;
- case TK_GE: zBinOp = "GE"; break;
- case TK_NE: zBinOp = "NE"; break;
- case TK_EQ: zBinOp = "EQ"; break;
- case TK_IS: zBinOp = "IS"; break;
- case TK_ISNOT: zBinOp = "ISNOT"; break;
- case TK_AND: zBinOp = "AND"; break;
- case TK_OR: zBinOp = "OR"; break;
- case TK_PLUS: zBinOp = "ADD"; break;
- case TK_STAR: zBinOp = "MUL"; break;
- case TK_MINUS: zBinOp = "SUB"; break;
- case TK_REM: zBinOp = "REM"; break;
- case TK_BITAND: zBinOp = "BITAND"; break;
- case TK_BITOR: zBinOp = "BITOR"; break;
- case TK_SLASH: zBinOp = "DIV"; break;
- case TK_LSHIFT: zBinOp = "LSHIFT"; break;
- case TK_RSHIFT: zBinOp = "RSHIFT"; break;
- case TK_CONCAT: zBinOp = "CONCAT"; break;
- case TK_DOT: zBinOp = "DOT"; break;
-
- case TK_UMINUS: zUniOp = "UMINUS"; break;
- case TK_UPLUS: zUniOp = "UPLUS"; break;
- case TK_BITNOT: zUniOp = "BITNOT"; break;
- case TK_NOT: zUniOp = "NOT"; break;
- case TK_ISNULL: zUniOp = "ISNULL"; break;
- case TK_NOTNULL: zUniOp = "NOTNULL"; break;
-
- case TK_COLLATE: {
- sqlite3TreeViewLine(pView, "COLLATE %Q", pExpr->u.zToken);
- sqlite3TreeViewExpr(pView, pExpr->pLeft, 0);
- break;
- }
-
- case TK_AGG_FUNCTION:
- case TK_FUNCTION: {
- ExprList *pFarg; /* List of function arguments */
- if( ExprHasProperty(pExpr, EP_TokenOnly) ){
- pFarg = 0;
- }else{
- pFarg = pExpr->x.pList;
- }
- if( pExpr->op==TK_AGG_FUNCTION ){
- sqlite3TreeViewLine(pView, "AGG_FUNCTION%d %Q",
- pExpr->op2, pExpr->u.zToken);
- }else{
- sqlite3TreeViewLine(pView, "FUNCTION %Q", pExpr->u.zToken);
- }
- if( pFarg ){
- sqlite3TreeViewExprList(pView, pFarg, 0, 0);
- }
- break;
- }
-#ifndef SQLITE_OMIT_SUBQUERY
- case TK_EXISTS: {
- sqlite3TreeViewLine(pView, "EXISTS-expr");
- sqlite3TreeViewSelect(pView, pExpr->x.pSelect, 0);
- break;
- }
- case TK_SELECT: {
- sqlite3TreeViewLine(pView, "SELECT-expr");
- sqlite3TreeViewSelect(pView, pExpr->x.pSelect, 0);
- break;
- }
- case TK_IN: {
- sqlite3TreeViewLine(pView, "IN");
- sqlite3TreeViewExpr(pView, pExpr->pLeft, 1);
- if( ExprHasProperty(pExpr, EP_xIsSelect) ){
- sqlite3TreeViewSelect(pView, pExpr->x.pSelect, 0);
- }else{
- sqlite3TreeViewExprList(pView, pExpr->x.pList, 0, 0);
- }
- break;
- }
-#endif /* SQLITE_OMIT_SUBQUERY */
-
- /*
- ** x BETWEEN y AND z
- **
- ** This is equivalent to
- **
- ** x>=y AND x<=z
- **
- ** X is stored in pExpr->pLeft.
- ** Y is stored in pExpr->pList->a[0].pExpr.
- ** Z is stored in pExpr->pList->a[1].pExpr.
- */
- case TK_BETWEEN: {
- Expr *pX = pExpr->pLeft;
- Expr *pY = pExpr->x.pList->a[0].pExpr;
- Expr *pZ = pExpr->x.pList->a[1].pExpr;
- sqlite3TreeViewLine(pView, "BETWEEN");
- sqlite3TreeViewExpr(pView, pX, 1);
- sqlite3TreeViewExpr(pView, pY, 1);
- sqlite3TreeViewExpr(pView, pZ, 0);
- break;
- }
- case TK_TRIGGER: {
- /* If the opcode is TK_TRIGGER, then the expression is a reference
- ** to a column in the new.* or old.* pseudo-tables available to
- ** trigger programs. In this case Expr.iTable is set to 1 for the
- ** new.* pseudo-table, or 0 for the old.* pseudo-table. Expr.iColumn
- ** is set to the column of the pseudo-table to read, or to -1 to
- ** read the rowid field.
- */
- sqlite3TreeViewLine(pView, "%s(%d)",
- pExpr->iTable ? "NEW" : "OLD", pExpr->iColumn);
- break;
- }
- case TK_CASE: {
- sqlite3TreeViewLine(pView, "CASE");
- sqlite3TreeViewExpr(pView, pExpr->pLeft, 1);
- sqlite3TreeViewExprList(pView, pExpr->x.pList, 0, 0);
- break;
- }
-#ifndef SQLITE_OMIT_TRIGGER
- case TK_RAISE: {
- const char *zType = "unk";
- switch( pExpr->affinity ){
- case OE_Rollback: zType = "rollback"; break;
- case OE_Abort: zType = "abort"; break;
- case OE_Fail: zType = "fail"; break;
- case OE_Ignore: zType = "ignore"; break;
- }
- sqlite3TreeViewLine(pView, "RAISE %s(%Q)", zType, pExpr->u.zToken);
- break;
- }
-#endif
- default: {
- sqlite3TreeViewLine(pView, "op=%d", pExpr->op);
- break;
- }
- }
- if( zBinOp ){
- sqlite3TreeViewLine(pView, "%s", zBinOp);
- sqlite3TreeViewExpr(pView, pExpr->pLeft, 1);
- sqlite3TreeViewExpr(pView, pExpr->pRight, 0);
- }else if( zUniOp ){
- sqlite3TreeViewLine(pView, "%s", zUniOp);
- sqlite3TreeViewExpr(pView, pExpr->pLeft, 0);
- }
- sqlite3TreeViewPop(pView);
-}
-#endif /* SQLITE_DEBUG */
-
-#ifdef SQLITE_DEBUG
-/*
-** Generate a human-readable explanation of an expression list.
-*/
-SQLITE_PRIVATE void sqlite3TreeViewExprList(
- TreeView *pView,
- const ExprList *pList,
- u8 moreToFollow,
- const char *zLabel
-){
- int i;
- pView = sqlite3TreeViewPush(pView, moreToFollow);
- if( zLabel==0 || zLabel[0]==0 ) zLabel = "LIST";
- if( pList==0 ){
- sqlite3TreeViewLine(pView, "%s (empty)", zLabel);
- }else{
- sqlite3TreeViewLine(pView, "%s", zLabel);
- for(i=0; i<pList->nExpr; i++){
- sqlite3TreeViewExpr(pView, pList->a[i].pExpr, i<pList->nExpr-1);
-#if 0
- if( pList->a[i].zName ){
- sqlite3ExplainPrintf(pOut, " AS %s", pList->a[i].zName);
- }
- if( pList->a[i].bSpanIsTab ){
- sqlite3ExplainPrintf(pOut, " (%s)", pList->a[i].zSpan);
- }
-#endif
- }
- }
- sqlite3TreeViewPop(pView);
-}
-#endif /* SQLITE_DEBUG */
-
/*
** Generate code that pushes the value of every element of the given
** expression list into a sequence of registers beginning at target.
@@ -86387,16 +92391,22 @@ SQLITE_PRIVATE void sqlite3TreeViewExprList(
**
** The SQLITE_ECEL_FACTOR argument allows constant arguments to be
** factored out into initialization code.
+**
+** The SQLITE_ECEL_REF flag means that expressions in the list with
+** ExprList.a[].u.x.iOrderByCol>0 have already been evaluated and stored
+** in registers at srcReg, and so the value can be copied from there.
*/
SQLITE_PRIVATE int sqlite3ExprCodeExprList(
Parse *pParse, /* Parsing context */
ExprList *pList, /* The expression list to be coded */
int target, /* Where to write results */
+ int srcReg, /* Source registers if SQLITE_ECEL_REF */
u8 flags /* SQLITE_ECEL_* flags */
){
struct ExprList_item *pItem;
- int i, n;
+ int i, j, n;
u8 copyOp = (flags & SQLITE_ECEL_DUP) ? OP_Copy : OP_SCopy;
+ Vdbe *v = pParse->pVdbe;
assert( pList!=0 );
assert( target>0 );
assert( pParse->pVdbe!=0 ); /* Never gets this far otherwise */
@@ -86404,13 +92414,14 @@ SQLITE_PRIVATE int sqlite3ExprCodeExprList(
if( !ConstFactorOk(pParse) ) flags &= ~SQLITE_ECEL_FACTOR;
for(pItem=pList->a, i=0; i<n; i++, pItem++){
Expr *pExpr = pItem->pExpr;
- if( (flags & SQLITE_ECEL_FACTOR)!=0 && sqlite3ExprIsConstant(pExpr) ){
+ if( (flags & SQLITE_ECEL_REF)!=0 && (j = pList->a[i].u.x.iOrderByCol)>0 ){
+ sqlite3VdbeAddOp2(v, copyOp, j+srcReg-1, target+i);
+ }else if( (flags & SQLITE_ECEL_FACTOR)!=0 && sqlite3ExprIsConstant(pExpr) ){
sqlite3ExprCodeAtInit(pParse, pExpr, target+i, 0);
}else{
int inReg = sqlite3ExprCodeTarget(pParse, pExpr, target+i);
if( inReg!=target+i ){
VdbeOp *pOp;
- Vdbe *v = pParse->pVdbe;
if( copyOp==OP_Copy
&& (pOp=sqlite3VdbeGetOp(v, -1))->opcode==OP_Copy
&& pOp->p1+pOp->p3+1==inReg
@@ -86530,6 +92541,13 @@ SQLITE_PRIVATE void sqlite3ExprIfTrue(Parse *pParse, Expr *pExpr, int dest, int
sqlite3ExprIfFalse(pParse, pExpr->pLeft, dest, jumpIfNull);
break;
}
+ case TK_IS:
+ case TK_ISNOT:
+ testcase( op==TK_IS );
+ testcase( op==TK_ISNOT );
+ op = (op==TK_IS) ? TK_EQ : TK_NE;
+ jumpIfNull = SQLITE_NULLEQ;
+ /* Fall thru */
case TK_LT:
case TK_LE:
case TK_GT:
@@ -86545,23 +92563,12 @@ SQLITE_PRIVATE void sqlite3ExprIfTrue(Parse *pParse, Expr *pExpr, int dest, int
assert(TK_LE==OP_Le); testcase(op==OP_Le); VdbeCoverageIf(v,op==OP_Le);
assert(TK_GT==OP_Gt); testcase(op==OP_Gt); VdbeCoverageIf(v,op==OP_Gt);
assert(TK_GE==OP_Ge); testcase(op==OP_Ge); VdbeCoverageIf(v,op==OP_Ge);
- assert(TK_EQ==OP_Eq); testcase(op==OP_Eq); VdbeCoverageIf(v,op==OP_Eq);
- assert(TK_NE==OP_Ne); testcase(op==OP_Ne); VdbeCoverageIf(v,op==OP_Ne);
- testcase( regFree1==0 );
- testcase( regFree2==0 );
- break;
- }
- case TK_IS:
- case TK_ISNOT: {
- testcase( op==TK_IS );
- testcase( op==TK_ISNOT );
- r1 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, &regFree1);
- r2 = sqlite3ExprCodeTemp(pParse, pExpr->pRight, &regFree2);
- op = (op==TK_IS) ? TK_EQ : TK_NE;
- codeCompare(pParse, pExpr->pLeft, pExpr->pRight, op,
- r1, r2, dest, SQLITE_NULLEQ);
- VdbeCoverageIf(v, op==TK_EQ);
- VdbeCoverageIf(v, op==TK_NE);
+ assert(TK_EQ==OP_Eq); testcase(op==OP_Eq);
+ VdbeCoverageIf(v, op==OP_Eq && jumpIfNull==SQLITE_NULLEQ);
+ VdbeCoverageIf(v, op==OP_Eq && jumpIfNull!=SQLITE_NULLEQ);
+ assert(TK_NE==OP_Ne); testcase(op==OP_Ne);
+ VdbeCoverageIf(v, op==OP_Ne && jumpIfNull==SQLITE_NULLEQ);
+ VdbeCoverageIf(v, op==OP_Ne && jumpIfNull!=SQLITE_NULLEQ);
testcase( regFree1==0 );
testcase( regFree2==0 );
break;
@@ -86587,14 +92594,14 @@ SQLITE_PRIVATE void sqlite3ExprIfTrue(Parse *pParse, Expr *pExpr, int dest, int
int destIfFalse = sqlite3VdbeMakeLabel(v);
int destIfNull = jumpIfNull ? dest : destIfFalse;
sqlite3ExprCodeIN(pParse, pExpr, destIfFalse, destIfNull);
- sqlite3VdbeAddOp2(v, OP_Goto, 0, dest);
+ sqlite3VdbeGoto(v, dest);
sqlite3VdbeResolveLabel(v, destIfFalse);
break;
}
#endif
default: {
if( exprAlwaysTrue(pExpr) ){
- sqlite3VdbeAddOp2(v, OP_Goto, 0, dest);
+ sqlite3VdbeGoto(v, dest);
}else if( exprAlwaysFalse(pExpr) ){
/* No-op */
}else{
@@ -86686,6 +92693,13 @@ SQLITE_PRIVATE void sqlite3ExprIfFalse(Parse *pParse, Expr *pExpr, int dest, int
sqlite3ExprIfTrue(pParse, pExpr->pLeft, dest, jumpIfNull);
break;
}
+ case TK_IS:
+ case TK_ISNOT:
+ testcase( pExpr->op==TK_IS );
+ testcase( pExpr->op==TK_ISNOT );
+ op = (pExpr->op==TK_IS) ? TK_NE : TK_EQ;
+ jumpIfNull = SQLITE_NULLEQ;
+ /* Fall thru */
case TK_LT:
case TK_LE:
case TK_GT:
@@ -86701,23 +92715,12 @@ SQLITE_PRIVATE void sqlite3ExprIfFalse(Parse *pParse, Expr *pExpr, int dest, int
assert(TK_LE==OP_Le); testcase(op==OP_Le); VdbeCoverageIf(v,op==OP_Le);
assert(TK_GT==OP_Gt); testcase(op==OP_Gt); VdbeCoverageIf(v,op==OP_Gt);
assert(TK_GE==OP_Ge); testcase(op==OP_Ge); VdbeCoverageIf(v,op==OP_Ge);
- assert(TK_EQ==OP_Eq); testcase(op==OP_Eq); VdbeCoverageIf(v,op==OP_Eq);
- assert(TK_NE==OP_Ne); testcase(op==OP_Ne); VdbeCoverageIf(v,op==OP_Ne);
- testcase( regFree1==0 );
- testcase( regFree2==0 );
- break;
- }
- case TK_IS:
- case TK_ISNOT: {
- testcase( pExpr->op==TK_IS );
- testcase( pExpr->op==TK_ISNOT );
- r1 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, &regFree1);
- r2 = sqlite3ExprCodeTemp(pParse, pExpr->pRight, &regFree2);
- op = (pExpr->op==TK_IS) ? TK_NE : TK_EQ;
- codeCompare(pParse, pExpr->pLeft, pExpr->pRight, op,
- r1, r2, dest, SQLITE_NULLEQ);
- VdbeCoverageIf(v, op==TK_EQ);
- VdbeCoverageIf(v, op==TK_NE);
+ assert(TK_EQ==OP_Eq); testcase(op==OP_Eq);
+ VdbeCoverageIf(v, op==OP_Eq && jumpIfNull!=SQLITE_NULLEQ);
+ VdbeCoverageIf(v, op==OP_Eq && jumpIfNull==SQLITE_NULLEQ);
+ assert(TK_NE==OP_Ne); testcase(op==OP_Ne);
+ VdbeCoverageIf(v, op==OP_Ne && jumpIfNull!=SQLITE_NULLEQ);
+ VdbeCoverageIf(v, op==OP_Ne && jumpIfNull==SQLITE_NULLEQ);
testcase( regFree1==0 );
testcase( regFree2==0 );
break;
@@ -86750,7 +92753,7 @@ SQLITE_PRIVATE void sqlite3ExprIfFalse(Parse *pParse, Expr *pExpr, int dest, int
#endif
default: {
if( exprAlwaysFalse(pExpr) ){
- sqlite3VdbeAddOp2(v, OP_Goto, 0, dest);
+ sqlite3VdbeGoto(v, dest);
}else if( exprAlwaysTrue(pExpr) ){
/* no-op */
}else{
@@ -86768,6 +92771,21 @@ SQLITE_PRIVATE void sqlite3ExprIfFalse(Parse *pParse, Expr *pExpr, int dest, int
}
/*
+** Like sqlite3ExprIfFalse() except that a copy is made of pExpr before
+** code generation, and that copy is deleted after code generation. This
+** ensures that the original pExpr is unchanged.
+*/
+SQLITE_PRIVATE void sqlite3ExprIfFalseDup(Parse *pParse, Expr *pExpr, int dest,int jumpIfNull){
+ sqlite3 *db = pParse->db;
+ Expr *pCopy = sqlite3ExprDup(db, pExpr, 0);
+ if( db->mallocFailed==0 ){
+ sqlite3ExprIfFalse(pParse, pCopy, dest, jumpIfNull);
+ }
+ sqlite3ExprDelete(db, pCopy);
+}
+
+
+/*
** Do a deep comparison of two expression trees. Return 0 if the two
** expressions are completely identical. Return 1 if they differ only
** by a COLLATE operator at the top level. Return 2 if there are differences
@@ -86810,8 +92828,10 @@ SQLITE_PRIVATE int sqlite3ExprCompare(Expr *pA, Expr *pB, int iTab){
}
return 2;
}
- if( pA->op!=TK_COLUMN && ALWAYS(pA->op!=TK_AGG_COLUMN) && pA->u.zToken ){
- if( strcmp(pA->u.zToken,pB->u.zToken)!=0 ){
+ if( pA->op!=TK_COLUMN && pA->op!=TK_AGG_COLUMN && pA->u.zToken ){
+ if( pA->op==TK_FUNCTION ){
+ if( sqlite3StrICmp(pA->u.zToken,pB->u.zToken)!=0 ) return 2;
+ }else if( strcmp(pA->u.zToken,pB->u.zToken)!=0 ){
return pA->op==TK_COLLATE ? 1 : 2;
}
}
@@ -87095,7 +93115,7 @@ static int analyzeAggregate(Walker *pWalker, Expr *pExpr){
pItem->iMem = ++pParse->nMem;
assert( !ExprHasProperty(pExpr, EP_IntValue) );
pItem->pFunc = sqlite3FindFunction(pParse->db,
- pExpr->u.zToken, sqlite3Strlen30(pExpr->u.zToken),
+ pExpr->u.zToken,
pExpr->x.pList ? pExpr->x.pList->nExpr : 0, enc, 0);
if( pExpr->flags & EP_Distinct ){
pItem->iDistinct = pParse->nTab++;
@@ -87224,6 +93244,29 @@ SQLITE_PRIVATE void sqlite3ClearTempRegCache(Parse *pParse){
pParse->nRangeReg = 0;
}
+/*
+** Validate that no temporary register falls within the range of
+** iFirst..iLast, inclusive. This routine is only call from within assert()
+** statements.
+*/
+#ifdef SQLITE_DEBUG
+SQLITE_PRIVATE int sqlite3NoTempsInRange(Parse *pParse, int iFirst, int iLast){
+ int i;
+ if( pParse->nRangeReg>0
+ && pParse->iRangeReg+pParse->nRangeReg<iLast
+ && pParse->iRangeReg>=iFirst
+ ){
+ return 0;
+ }
+ for(i=0; i<pParse->nTempReg; i++){
+ if( pParse->aTempReg[i]>=iFirst && pParse->aTempReg[i]<=iLast ){
+ return 0;
+ }
+ }
+ return 1;
+}
+#endif /* SQLITE_DEBUG */
+
/************** End of expr.c ************************************************/
/************** Begin file alter.c *******************************************/
/*
@@ -87240,6 +93283,7 @@ SQLITE_PRIVATE void sqlite3ClearTempRegCache(Parse *pParse){
** This file contains C code routines that used to generate VDBE code
** that implements the ALTER TABLE command.
*/
+/* #include "sqliteInt.h" */
/*
** The code in this file only exists if we are not omitting the
@@ -87456,7 +93500,7 @@ static void renameTriggerFunc(
** Register built-in functions used to help implement ALTER TABLE
*/
SQLITE_PRIVATE void sqlite3AlterFunctions(void){
- static SQLITE_WSD FuncDef aAlterTableFuncs[] = {
+ static FuncDef aAlterTableFuncs[] = {
FUNCTION(sqlite_rename_table, 2, 0, 0, renameTableFunc),
#ifndef SQLITE_OMIT_TRIGGER
FUNCTION(sqlite_rename_trigger, 2, 0, 0, renameTriggerFunc),
@@ -87465,13 +93509,7 @@ SQLITE_PRIVATE void sqlite3AlterFunctions(void){
FUNCTION(sqlite_rename_parent, 3, 0, 0, renameParentFunc),
#endif
};
- int i;
- FuncDefHash *pHash = &GLOBAL(FuncDefHash, sqlite3GlobalFunctions);
- FuncDef *aFunc = (FuncDef*)&GLOBAL(FuncDef, aAlterTableFuncs);
-
- for(i=0; i<ArraySize(aAlterTableFuncs); i++){
- sqlite3FuncDefInsert(pHash, &aFunc[i]);
- }
+ sqlite3InsertBuiltinFuncs(aAlterTableFuncs, ArraySize(aAlterTableFuncs));
}
/*
@@ -87718,7 +93756,7 @@ SQLITE_PRIVATE void sqlite3AlterRenameTable(
#ifndef SQLITE_OMIT_VIRTUALTABLE
if( pVTab ){
int i = ++pParse->nMem;
- sqlite3VdbeAddOp4(v, OP_String8, 0, i, 0, zName, 0);
+ sqlite3VdbeLoadString(v, i, zName);
sqlite3VdbeAddOp4(v, OP_VRename, i, 0, 0,(const char*)pVTab, P4_VTAB);
sqlite3MayAbort(pParse);
}
@@ -87815,33 +93853,6 @@ exit_rename_table:
db->flags = savedDbFlags;
}
-
-/*
-** Generate code to make sure the file format number is at least minFormat.
-** The generated code will increase the file format number if necessary.
-*/
-SQLITE_PRIVATE void sqlite3MinimumFileFormat(Parse *pParse, int iDb, int minFormat){
- Vdbe *v;
- v = sqlite3GetVdbe(pParse);
- /* The VDBE should have been allocated before this routine is called.
- ** If that allocation failed, we would have quit before reaching this
- ** point */
- if( ALWAYS(v) ){
- int r1 = sqlite3GetTempReg(pParse);
- int r2 = sqlite3GetTempReg(pParse);
- int j1;
- sqlite3VdbeAddOp3(v, OP_ReadCookie, iDb, r1, BTREE_FILE_FORMAT);
- sqlite3VdbeUsesBtree(v, iDb);
- sqlite3VdbeAddOp2(v, OP_Integer, minFormat, r2);
- j1 = sqlite3VdbeAddOp3(v, OP_Ge, r2, 0, r1);
- sqlite3VdbeChangeP5(v, SQLITE_NOTNULL); VdbeCoverage(v);
- sqlite3VdbeAddOp3(v, OP_SetCookie, iDb, BTREE_FILE_FORMAT, r2);
- sqlite3VdbeJumpHere(v, j1);
- sqlite3ReleaseTempReg(pParse, r1);
- sqlite3ReleaseTempReg(pParse, r2);
- }
-}
-
/*
** This function is called after an "ALTER TABLE ... ADD" statement
** has been parsed. Argument pColDef contains the text of the new
@@ -87860,9 +93871,11 @@ SQLITE_PRIVATE void sqlite3AlterFinishAddColumn(Parse *pParse, Token *pColDef){
Column *pCol; /* The new column */
Expr *pDflt; /* Default value for the new column */
sqlite3 *db; /* The database connection; */
+ Vdbe *v = pParse->pVdbe; /* The prepared statement under construction */
db = pParse->db;
if( pParse->nErr || db->mallocFailed ) return;
+ assert( v!=0 );
pNew = pParse->pNewTable;
assert( pNew );
@@ -87886,7 +93899,8 @@ SQLITE_PRIVATE void sqlite3AlterFinishAddColumn(Parse *pParse, Token *pColDef){
** literal NULL, then set pDflt to 0. This simplifies checking
** for an SQL NULL default below.
*/
- if( pDflt && pDflt->op==TK_NULL ){
+ assert( pDflt==0 || pDflt->op==TK_SPAN );
+ if( pDflt && pDflt->pLeft->op==TK_NULL ){
pDflt = 0;
}
@@ -87919,10 +93933,10 @@ SQLITE_PRIVATE void sqlite3AlterFinishAddColumn(Parse *pParse, Token *pColDef){
if( pDflt ){
sqlite3_value *pVal = 0;
int rc;
- rc = sqlite3ValueFromExpr(db, pDflt, SQLITE_UTF8, SQLITE_AFF_NONE, &pVal);
+ rc = sqlite3ValueFromExpr(db, pDflt, SQLITE_UTF8, SQLITE_AFF_BLOB, &pVal);
assert( rc==SQLITE_OK || rc==SQLITE_NOMEM );
if( rc!=SQLITE_OK ){
- db->mallocFailed = 1;
+ assert( db->mallocFailed == 1 );
return;
}
if( !pVal ){
@@ -87952,11 +93966,16 @@ SQLITE_PRIVATE void sqlite3AlterFinishAddColumn(Parse *pParse, Token *pColDef){
db->flags = savedDbFlags;
}
- /* If the default value of the new column is NULL, then set the file
+ /* If the default value of the new column is NULL, then the file
** format to 2. If the default value of the new column is not NULL,
- ** the file format becomes 3.
+ ** the file format be 3. Back when this feature was first added
+ ** in 2006, we went to the trouble to upgrade the file format to the
+ ** minimum support values. But 10-years on, we can assume that all
+ ** extent versions of SQLite support file-format 4, so we always and
+ ** unconditionally upgrade to 4.
*/
- sqlite3MinimumFileFormat(pParse, iDb, pDflt ? 3 : 2);
+ sqlite3VdbeAddOp3(v, OP_SetCookie, iDb, BTREE_FILE_FORMAT,
+ SQLITE_MAX_FILE_FORMAT);
/* Reload the schema of the modified table. */
reloadTableSchema(pParse, pTab, pTab->zName);
@@ -88030,7 +94049,7 @@ SQLITE_PRIVATE void sqlite3AlterBeginAddColumn(Parse *pParse, SrcList *pSrc){
pNew->aCol = (Column*)sqlite3DbMallocZero(db, sizeof(Column)*nAlloc);
pNew->zName = sqlite3MPrintf(db, "sqlite_altertab_%s", pTab->zName);
if( !pNew->aCol || !pNew->zName ){
- db->mallocFailed = 1;
+ assert( db->mallocFailed );
goto exit_begin_add_column;
}
memcpy(pNew->aCol, pTab->aCol, sizeof(Column)*pNew->nCol);
@@ -88038,9 +94057,7 @@ SQLITE_PRIVATE void sqlite3AlterBeginAddColumn(Parse *pParse, SrcList *pSrc){
Column *pCol = &pNew->aCol[i];
pCol->zName = sqlite3DbStrDup(db, pCol->zName);
pCol->zColl = 0;
- pCol->zType = 0;
pCol->pDflt = 0;
- pCol->zDflt = 0;
}
pNew->pSchema = db->aDb[iDb].pSchema;
pNew->addColOffset = pTab->addColOffset;
@@ -88202,6 +94219,7 @@ exit_begin_add_column:
** integer in the equivalent columns in sqlite_stat4.
*/
#ifndef SQLITE_OMIT_ANALYZE
+/* #include "sqliteInt.h" */
#if defined(SQLITE_ENABLE_STAT4)
# define IsStat4 1
@@ -88374,7 +94392,7 @@ static void sampleClear(sqlite3 *db, Stat4Sample *p){
static void sampleSetRowid(sqlite3 *db, Stat4Sample *p, int n, const u8 *pData){
assert( db!=0 );
if( p->nRowid ) sqlite3DbFree(db, p->u.aRowid);
- p->u.aRowid = sqlite3DbMallocRaw(db, n);
+ p->u.aRowid = sqlite3DbMallocRawNN(db, n);
if( p->u.aRowid ){
p->nRowid = n;
memcpy(p->u.aRowid, pData, n);
@@ -88539,12 +94557,10 @@ static const FuncDef statInitFuncdef = {
SQLITE_UTF8, /* funcFlags */
0, /* pUserData */
0, /* pNext */
- statInit, /* xFunc */
- 0, /* xStep */
+ statInit, /* xSFunc */
0, /* xFinalize */
"stat_init", /* zName */
- 0, /* pHash */
- 0 /* pDestructor */
+ {0}
};
#ifdef SQLITE_ENABLE_STAT4
@@ -88840,12 +94856,10 @@ static const FuncDef statPushFuncdef = {
SQLITE_UTF8, /* funcFlags */
0, /* pUserData */
0, /* pNext */
- statPush, /* xFunc */
- 0, /* xStep */
+ statPush, /* xSFunc */
0, /* xFinalize */
"stat_push", /* zName */
- 0, /* pHash */
- 0 /* pDestructor */
+ {0}
};
#define STAT_GET_STAT1 0 /* "stat" column of stat1 table */
@@ -88987,12 +95001,10 @@ static const FuncDef statGetFuncdef = {
SQLITE_UTF8, /* funcFlags */
0, /* pUserData */
0, /* pNext */
- statGet, /* xFunc */
- 0, /* xStep */
+ statGet, /* xSFunc */
0, /* xFinalize */
"stat_get", /* zName */
- 0, /* pHash */
- 0 /* pDestructor */
+ {0}
};
static void callStatGet(Vdbe *v, int regStat4, int iParam, int regOut){
@@ -89004,8 +95016,8 @@ static void callStatGet(Vdbe *v, int regStat4, int iParam, int regOut){
#else
UNUSED_PARAMETER( iParam );
#endif
- sqlite3VdbeAddOp3(v, OP_Function, 0, regStat4, regOut);
- sqlite3VdbeChangeP4(v, -1, (char*)&statGetFuncdef, P4_FUNCDEF);
+ sqlite3VdbeAddOp4(v, OP_Function0, 0, regStat4, regOut,
+ (char*)&statGetFuncdef, P4_FUNCDEF);
sqlite3VdbeChangeP5(v, 1 + IsStat34);
}
@@ -89051,7 +95063,7 @@ static void analyzeOneTable(
/* Do not gather statistics on views or virtual tables */
return;
}
- if( sqlite3_strnicmp(pTab->zName, "sqlite_", 7)==0 ){
+ if( sqlite3_strlike("sqlite_%", pTab->zName, 0)==0 ){
/* Do not gather statistics on system tables */
return;
}
@@ -89075,7 +95087,7 @@ static void analyzeOneTable(
iIdxCur = iTab++;
pParse->nTab = MAX(pParse->nTab, iTab);
sqlite3OpenTable(pParse, iTabCur, iDb, pTab, OP_OpenRead);
- sqlite3VdbeAddOp4(v, OP_String8, 0, regTabname, 0, pTab->zName, 0);
+ sqlite3VdbeLoadString(v, regTabname, pTab->zName);
for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
int nCol; /* Number of columns in pIdx. "N" */
@@ -89097,7 +95109,7 @@ static void analyzeOneTable(
}
/* Populate the register containing the index name. */
- sqlite3VdbeAddOp4(v, OP_String8, 0, regIdxname, 0, zIdxName, 0);
+ sqlite3VdbeLoadString(v, regIdxname, zIdxName);
VdbeComment((v, "Analysis for %s.%s", pTab->zName, zIdxName));
/*
@@ -89159,8 +95171,8 @@ static void analyzeOneTable(
#endif
sqlite3VdbeAddOp2(v, OP_Integer, nCol, regStat4+1);
sqlite3VdbeAddOp2(v, OP_Integer, pIdx->nKeyCol, regStat4+2);
- sqlite3VdbeAddOp3(v, OP_Function, 0, regStat4+1, regStat4);
- sqlite3VdbeChangeP4(v, -1, (char*)&statInitFuncdef, P4_FUNCDEF);
+ sqlite3VdbeAddOp4(v, OP_Function0, 0, regStat4+1, regStat4,
+ (char*)&statInitFuncdef, P4_FUNCDEF);
sqlite3VdbeChangeP5(v, 2+IsStat34);
/* Implementation of the following:
@@ -89179,7 +95191,7 @@ static void analyzeOneTable(
if( nColTest>0 ){
int endDistinctTest = sqlite3VdbeMakeLabel(v);
int *aGotoChng; /* Array of jump instruction addresses */
- aGotoChng = sqlite3DbMallocRaw(db, sizeof(int)*nColTest);
+ aGotoChng = sqlite3DbMallocRawNN(db, sizeof(int)*nColTest);
if( aGotoChng==0 ) continue;
/*
@@ -89211,7 +95223,7 @@ static void analyzeOneTable(
VdbeCoverage(v);
}
sqlite3VdbeAddOp2(v, OP_Integer, nColTest, regChng);
- sqlite3VdbeAddOp2(v, OP_Goto, 0, endDistinctTest);
+ sqlite3VdbeGoto(v, endDistinctTest);
/*
@@ -89247,6 +95259,7 @@ static void analyzeOneTable(
regKey = sqlite3GetTempRange(pParse, pPk->nKeyCol);
for(j=0; j<pPk->nKeyCol; j++){
k = sqlite3ColumnOfIndex(pIdx, pPk->aiColumn[j]);
+ assert( k>=0 && k<pTab->nCol );
sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, k, regKey+j);
VdbeComment((v, "%s", pTab->aCol[pPk->aiColumn[j]].zName));
}
@@ -89255,8 +95268,8 @@ static void analyzeOneTable(
}
#endif
assert( regChng==(regStat4+1) );
- sqlite3VdbeAddOp3(v, OP_Function, 1, regStat4, regTemp);
- sqlite3VdbeChangeP4(v, -1, (char*)&statPushFuncdef, P4_FUNCDEF);
+ sqlite3VdbeAddOp4(v, OP_Function0, 1, regStat4, regTemp,
+ (char*)&statPushFuncdef, P4_FUNCDEF);
sqlite3VdbeChangeP5(v, 2+IsStat34);
sqlite3VdbeAddOp2(v, OP_Next, iIdxCur, addrNextRow); VdbeCoverage(v);
@@ -89296,12 +95309,10 @@ static void analyzeOneTable(
** be taken */
VdbeCoverageNeverTaken(v);
#ifdef SQLITE_ENABLE_STAT3
- sqlite3ExprCodeGetColumnOfTable(v, pTab, iTabCur,
- pIdx->aiColumn[0], regSample);
+ sqlite3ExprCodeLoadIndexColumn(pParse, pIdx, iTabCur, 0, regSample);
#else
for(i=0; i<nCol; i++){
- i16 iCol = pIdx->aiColumn[i];
- sqlite3ExprCodeGetColumnOfTable(v, pTab, iTabCur, iCol, regCol+i);
+ sqlite3ExprCodeLoadIndexColumn(pParse, pIdx, iTabCur, i, regCol+i);
}
sqlite3VdbeAddOp3(v, OP_MakeRecord, regCol, nCol, regSample);
#endif
@@ -89588,7 +95599,7 @@ static int analysisLoader(void *pData, int argc, char **argv, char **NotUsed){
** 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;
+ if( pIndex->aiRowEst==0 ) sqlite3OomFault(pInfo->db);
}
aiRowEst = pIndex->aiRowEst;
#endif
@@ -89735,10 +95746,10 @@ static int loadStatTbl(
Index *pPrevIdx = 0; /* Previous index in the loop */
IndexSample *pSample; /* A slot in pIdx->aSample[] */
- assert( db->lookaside.bEnabled==0 );
+ assert( db->lookaside.bDisable );
zSql = sqlite3MPrintf(db, zSql1, zDb);
if( !zSql ){
- return SQLITE_NOMEM;
+ return SQLITE_NOMEM_BKPT;
}
rc = sqlite3_prepare(db, zSql, -1, &pStmt, 0);
sqlite3DbFree(db, zSql);
@@ -89778,7 +95789,7 @@ static int loadStatTbl(
pIdx->aSample = sqlite3DbMallocZero(db, nByte);
if( pIdx->aSample==0 ){
sqlite3_finalize(pStmt);
- return SQLITE_NOMEM;
+ return SQLITE_NOMEM_BKPT;
}
pSpace = (tRowcnt*)&pIdx->aSample[nSample];
pIdx->aAvgEq = pSpace; pSpace += nIdxCol;
@@ -89794,7 +95805,7 @@ static int loadStatTbl(
zSql = sqlite3MPrintf(db, zSql2, zDb);
if( !zSql ){
- return SQLITE_NOMEM;
+ return SQLITE_NOMEM_BKPT;
}
rc = sqlite3_prepare(db, zSql, -1, &pStmt, 0);
sqlite3DbFree(db, zSql);
@@ -89832,7 +95843,7 @@ static int loadStatTbl(
pSample->p = sqlite3DbMallocZero(db, pSample->n + 2);
if( pSample->p==0 ){
sqlite3_finalize(pStmt);
- return SQLITE_NOMEM;
+ return SQLITE_NOMEM_BKPT;
}
memcpy(pSample->p, sqlite3_column_blob(pStmt, 4), pSample->n);
pIdx->nSample++;
@@ -89849,7 +95860,7 @@ static int loadStatTbl(
static int loadStat4(sqlite3 *db, const char *zDb){
int rc = SQLITE_OK; /* Result codes from subroutines */
- assert( db->lookaside.bEnabled==0 );
+ assert( db->lookaside.bDisable );
if( sqlite3FindTable(db, "sqlite_stat4", zDb) ){
rc = loadStatTbl(db, 0,
"SELECT idx,count(*) FROM %Q.sqlite_stat4 GROUP BY idx",
@@ -89894,7 +95905,7 @@ SQLITE_PRIVATE int sqlite3AnalysisLoad(sqlite3 *db, int iDb){
analysisInfo sInfo;
HashElem *i;
char *zSql;
- int rc;
+ int rc = SQLITE_OK;
assert( iDb>=0 && iDb<db->nDb );
assert( db->aDb[iDb].pBt!=0 );
@@ -89903,38 +95914,40 @@ SQLITE_PRIVATE int sqlite3AnalysisLoad(sqlite3 *db, int iDb){
assert( sqlite3SchemaMutexHeld(db, iDb, 0) );
for(i=sqliteHashFirst(&db->aDb[iDb].pSchema->idxHash);i;i=sqliteHashNext(i)){
Index *pIdx = sqliteHashData(i);
- sqlite3DefaultRowEst(pIdx);
+ pIdx->aiRowLogEst[0] = 0;
#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
sqlite3DeleteIndexSamples(db, pIdx);
pIdx->aSample = 0;
#endif
}
- /* Check to make sure the sqlite_stat1 table exists */
+ /* Load new statistics out of the sqlite_stat1 table */
sInfo.db = db;
sInfo.zDatabase = db->aDb[iDb].zName;
- if( sqlite3FindTable(db, "sqlite_stat1", sInfo.zDatabase)==0 ){
- return SQLITE_ERROR;
+ if( sqlite3FindTable(db, "sqlite_stat1", sInfo.zDatabase)!=0 ){
+ zSql = sqlite3MPrintf(db,
+ "SELECT tbl,idx,stat FROM %Q.sqlite_stat1", sInfo.zDatabase);
+ if( zSql==0 ){
+ rc = SQLITE_NOMEM_BKPT;
+ }else{
+ rc = sqlite3_exec(db, zSql, analysisLoader, &sInfo, 0);
+ sqlite3DbFree(db, zSql);
+ }
}
- /* Load new statistics out of the sqlite_stat1 table */
- zSql = sqlite3MPrintf(db,
- "SELECT tbl,idx,stat FROM %Q.sqlite_stat1", sInfo.zDatabase);
- if( zSql==0 ){
- rc = SQLITE_NOMEM;
- }else{
- rc = sqlite3_exec(db, zSql, analysisLoader, &sInfo, 0);
- sqlite3DbFree(db, zSql);
+ /* Set appropriate defaults on all indexes not in the sqlite_stat1 table */
+ assert( sqlite3SchemaMutexHeld(db, iDb, 0) );
+ for(i=sqliteHashFirst(&db->aDb[iDb].pSchema->idxHash);i;i=sqliteHashNext(i)){
+ Index *pIdx = sqliteHashData(i);
+ if( pIdx->aiRowLogEst[0]==0 ) sqlite3DefaultRowEst(pIdx);
}
-
/* Load the statistics from the sqlite_stat4 table. */
#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
if( rc==SQLITE_OK && OptimizationEnabled(db, SQLITE_Stat34) ){
- int lookasideEnabled = db->lookaside.bEnabled;
- db->lookaside.bEnabled = 0;
+ db->lookaside.bDisable++;
rc = loadStat4(db, sInfo.zDatabase);
- db->lookaside.bEnabled = lookasideEnabled;
+ db->lookaside.bDisable--;
}
for(i=sqliteHashFirst(&db->aDb[iDb].pSchema->idxHash);i;i=sqliteHashNext(i)){
Index *pIdx = sqliteHashData(i);
@@ -89944,7 +95957,7 @@ SQLITE_PRIVATE int sqlite3AnalysisLoad(sqlite3 *db, int iDb){
#endif
if( rc==SQLITE_NOMEM ){
- db->mallocFailed = 1;
+ sqlite3OomFault(db);
}
return rc;
}
@@ -89967,6 +95980,7 @@ SQLITE_PRIVATE int sqlite3AnalysisLoad(sqlite3 *db, int iDb){
*************************************************************************
** This file contains code used to implement the ATTACH and DETACH commands.
*/
+/* #include "sqliteInt.h" */
#ifndef SQLITE_OMIT_ATTACH
/*
@@ -90064,7 +96078,7 @@ static void attachFunc(
** hash tables.
*/
if( db->aDb==db->aDbStatic ){
- aNew = sqlite3DbMallocRaw(db, sizeof(db->aDb[0])*3 );
+ aNew = sqlite3DbMallocRawNN(db, sizeof(db->aDb[0])*3 );
if( aNew==0 ) return;
memcpy(aNew, db->aDb, sizeof(db->aDb[0])*2);
}else{
@@ -90082,7 +96096,7 @@ static void attachFunc(
flags = db->openFlags;
rc = sqlite3ParseUri(db->pVfs->zName, zFile, &flags, &pVfs, &zPath, &zErr);
if( rc!=SQLITE_OK ){
- if( rc==SQLITE_NOMEM ) db->mallocFailed = 1;
+ if( rc==SQLITE_NOMEM ) sqlite3OomFault(db);
sqlite3_result_error(context, zErr, -1);
sqlite3_free(zErr);
return;
@@ -90099,7 +96113,7 @@ static void attachFunc(
Pager *pPager;
aNew->pSchema = sqlite3SchemaGet(db, aNew->pBt);
if( !aNew->pSchema ){
- rc = SQLITE_NOMEM;
+ rc = SQLITE_NOMEM_BKPT;
}else if( aNew->pSchema->file_format && aNew->pSchema->enc!=ENC(db) ){
zErrDyn = sqlite3MPrintf(db,
"attached databases must use the same text encoding as main database");
@@ -90111,14 +96125,15 @@ static void attachFunc(
sqlite3BtreeSecureDelete(aNew->pBt,
sqlite3BtreeSecureDelete(db->aDb[0].pBt,-1) );
#ifndef SQLITE_OMIT_PAGER_PRAGMAS
- sqlite3BtreeSetPagerFlags(aNew->pBt, 3 | (db->flags & PAGER_FLAGS_MASK));
+ sqlite3BtreeSetPagerFlags(aNew->pBt,
+ PAGER_SYNCHRONOUS_FULL | (db->flags & PAGER_FLAGS_MASK));
#endif
sqlite3BtreeLeave(aNew->pBt);
}
- aNew->safety_level = 3;
+ aNew->safety_level = SQLITE_DEFAULT_SYNCHRONOUS+1;
aNew->zName = sqlite3DbStrDup(db, zName);
if( rc==SQLITE_OK && aNew->zName==0 ){
- rc = SQLITE_NOMEM;
+ rc = SQLITE_NOMEM_BKPT;
}
@@ -90184,7 +96199,7 @@ static void attachFunc(
sqlite3ResetAllSchemasOfConnection(db);
db->nDb = iDb;
if( rc==SQLITE_NOMEM || rc==SQLITE_IOERR_NOMEM ){
- db->mallocFailed = 1;
+ sqlite3OomFault(db);
sqlite3DbFree(db, zErrDyn);
zErrDyn = sqlite3MPrintf(db, "out of memory");
}else if( zErrDyn==0 ){
@@ -90314,11 +96329,11 @@ static void codeAttach(
assert( v || db->mallocFailed );
if( v ){
- sqlite3VdbeAddOp3(v, OP_Function, 0, regArgs+3-pFunc->nArg, regArgs+3);
+ sqlite3VdbeAddOp4(v, OP_Function0, 0, regArgs+3-pFunc->nArg, regArgs+3,
+ (char *)pFunc, P4_FUNCDEF);
assert( pFunc->nArg==-1 || (pFunc->nArg&0xff)==pFunc->nArg );
sqlite3VdbeChangeP5(v, (u8)(pFunc->nArg));
- sqlite3VdbeChangeP4(v, -1, (char *)pFunc, P4_FUNCDEF);
-
+
/* Code an OP_Expire. For an ATTACH statement, set P1 to true (expire this
** statement only). For DETACH, set it to false (expire all existing
** statements).
@@ -90343,12 +96358,10 @@ SQLITE_PRIVATE void sqlite3Detach(Parse *pParse, Expr *pDbname){
SQLITE_UTF8, /* funcFlags */
0, /* pUserData */
0, /* pNext */
- detachFunc, /* xFunc */
- 0, /* xStep */
+ detachFunc, /* xSFunc */
0, /* xFinalize */
"sqlite_detach", /* zName */
- 0, /* pHash */
- 0 /* pDestructor */
+ {0}
};
codeAttach(pParse, SQLITE_DETACH, &detach_func, pDbname, 0, 0, pDbname);
}
@@ -90364,12 +96377,10 @@ SQLITE_PRIVATE void sqlite3Attach(Parse *pParse, Expr *p, Expr *pDbname, Expr *p
SQLITE_UTF8, /* funcFlags */
0, /* pUserData */
0, /* pNext */
- attachFunc, /* xFunc */
- 0, /* xStep */
+ attachFunc, /* xSFunc */
0, /* xFinalize */
"sqlite_attach", /* zName */
- 0, /* pHash */
- 0 /* pDestructor */
+ {0}
};
codeAttach(pParse, SQLITE_ATTACH, &attach_func, p, p, pDbname, pKey);
}
@@ -90556,6 +96567,7 @@ SQLITE_PRIVATE int sqlite3FixTriggerStep(
** systems that do not need this facility may omit it by recompiling
** the library with -DSQLITE_OMIT_AUTHORIZATION=1
*/
+/* #include "sqliteInt.h" */
/*
** All of the code in this file may be omitted by defining a single
@@ -90826,15 +96838,7 @@ SQLITE_PRIVATE void sqlite3AuthContextPop(AuthContext *pContext){
** COMMIT
** ROLLBACK
*/
-
-/*
-** This routine is called when a new SQL statement is beginning to
-** be parsed. Initialize the pParse structure as needed.
-*/
-SQLITE_PRIVATE void sqlite3BeginParse(Parse *pParse, int explainFlag){
- pParse->explain = (u8)explainFlag;
- pParse->nVar = 0;
-}
+/* #include "sqliteInt.h" */
#ifndef SQLITE_OMIT_SHARED_CACHE
/*
@@ -90890,7 +96894,7 @@ SQLITE_PRIVATE void sqlite3TableLock(
p->zName = zName;
}else{
pToplevel->nTableLock = 0;
- pToplevel->db->mallocFailed = 1;
+ sqlite3OomFault(pToplevel->db);
}
}
@@ -90995,6 +96999,8 @@ SQLITE_PRIVATE void sqlite3FinishCoding(Parse *pParse){
db->aDb[iDb].pSchema->iGeneration /* P4 */
);
if( db->init.busy==0 ) sqlite3VdbeChangeP5(v, 1);
+ VdbeComment((v,
+ "usesStmtJournal=%d", pParse->mayAbort && pParse->isMultiWrite));
}
#ifndef SQLITE_OMIT_VIRTUALTABLE
for(i=0; i<pParse->nVtabLock; i++){
@@ -91024,7 +97030,7 @@ SQLITE_PRIVATE void sqlite3FinishCoding(Parse *pParse){
}
/* Finally, jump back to the beginning of the executable code. */
- sqlite3VdbeAddOp2(v, OP_Goto, 0, 1);
+ sqlite3VdbeGoto(v, 1);
}
}
@@ -91038,15 +97044,19 @@ SQLITE_PRIVATE void sqlite3FinishCoding(Parse *pParse){
if( pParse->pAinc!=0 && pParse->nTab==0 ) pParse->nTab = 1;
sqlite3VdbeMakeReady(v, pParse);
pParse->rc = SQLITE_DONE;
- pParse->colNamesSet = 0;
}else{
pParse->rc = SQLITE_ERROR;
}
+
+ /* We are done with this Parse object. There is no need to de-initialize it */
+#if 0
+ pParse->colNamesSet = 0;
pParse->nTab = 0;
pParse->nMem = 0;
pParse->nSet = 0;
pParse->nVar = 0;
DbMaskZero(pParse->cookieMask);
+#endif
}
/*
@@ -91159,6 +97169,17 @@ SQLITE_PRIVATE Table *sqlite3LocateTable(
p = sqlite3FindTable(pParse->db, zName, zDbase);
if( p==0 ){
const char *zMsg = isView ? "no such view" : "no such table";
+#ifndef SQLITE_OMIT_VIRTUALTABLE
+ if( sqlite3FindDbName(pParse->db, zDbase)<1 ){
+ /* If zName is the not the name of a table in the schema created using
+ ** CREATE, then check to see if it is the name of an virtual table that
+ ** can be an eponymous virtual table. */
+ Module *pMod = (Module*)sqlite3HashFind(&pParse->db->aModule, zName);
+ if( pMod && sqlite3VtabEponymousTableInit(pParse, pMod) ){
+ return pMod->pEpoTab;
+ }
+ }
+#endif
if( zDbase ){
sqlite3ErrorMsg(pParse, "%s: %s.%s", zMsg, zDbase, zName);
}else{
@@ -91166,12 +97187,7 @@ SQLITE_PRIVATE Table *sqlite3LocateTable(
}
pParse->checkSchema = 1;
}
-#if SQLITE_USER_AUTHENICATION
- else if( pParse->db->auth.authLevel<UAUTH_User ){
- sqlite3ErrorMsg(pParse, "user not authenticated");
- p = 0;
- }
-#endif
+
return p;
}
@@ -91237,8 +97253,9 @@ static void freeIndex(sqlite3 *db, Index *p){
sqlite3DeleteIndexSamples(db, p);
#endif
sqlite3ExprDelete(db, p->pPartIdxWhere);
+ sqlite3ExprListDelete(db, p->aColExpr);
sqlite3DbFree(db, p->zColAff);
- if( p->isResized ) sqlite3DbFree(db, p->azColl);
+ if( p->isResized ) sqlite3DbFree(db, (void *)p->azColl);
#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
sqlite3_free(p->aiRowEst);
#endif
@@ -91298,7 +97315,6 @@ SQLITE_PRIVATE void sqlite3CollapseDatabaseArray(sqlite3 *db){
}
j++;
}
- memset(&db->aDb[j], 0, (db->nDb-j)*sizeof(db->aDb[j]));
db->nDb = j;
if( db->nDb<=2 && db->aDb!=db->aDbStatic ){
memcpy(db->aDbStatic, db->aDb, 2*sizeof(db->aDb[0]));
@@ -91363,7 +97379,7 @@ SQLITE_PRIVATE void sqlite3CommitInternalChanges(sqlite3 *db){
** Delete memory allocated for the column names of a table or view (the
** Table.aCol[] array).
*/
-static void sqliteDeleteColumnNames(sqlite3 *db, Table *pTable){
+SQLITE_PRIVATE void sqlite3DeleteColumnNames(sqlite3 *db, Table *pTable){
int i;
Column *pCol;
assert( pTable!=0 );
@@ -91371,8 +97387,6 @@ static void sqliteDeleteColumnNames(sqlite3 *db, Table *pTable){
for(i=0; i<pTable->nCol; i++, pCol++){
sqlite3DbFree(db, pCol->zName);
sqlite3ExprDelete(db, pCol->pDflt);
- sqlite3DbFree(db, pCol->zDflt);
- sqlite3DbFree(db, pCol->zType);
sqlite3DbFree(db, pCol->zColl);
}
sqlite3DbFree(db, pTable->aCol);
@@ -91394,16 +97408,10 @@ static void sqliteDeleteColumnNames(sqlite3 *db, Table *pTable){
** db parameter can be used with db->pnBytesFreed to measure the memory
** used by the Table object.
*/
-SQLITE_PRIVATE void sqlite3DeleteTable(sqlite3 *db, Table *pTable){
+static void SQLITE_NOINLINE deleteTable(sqlite3 *db, Table *pTable){
Index *pIndex, *pNext;
TESTONLY( int nLookaside; ) /* Used to verify lookaside not used for schema */
- assert( !pTable || pTable->nRef>0 );
-
- /* Do not delete the table until the reference count reaches zero. */
- if( !pTable ) return;
- if( ((!db || db->pnBytesFreed==0) && (--pTable->nRef)>0) ) return;
-
/* Record the number of outstanding lookaside allocations in schema Tables
** prior to doing any free() operations. Since schema Tables do not use
** lookaside, this number should not change. */
@@ -91430,13 +97438,11 @@ SQLITE_PRIVATE void sqlite3DeleteTable(sqlite3 *db, Table *pTable){
/* Delete the Table structure itself.
*/
- sqliteDeleteColumnNames(db, pTable);
+ sqlite3DeleteColumnNames(db, pTable);
sqlite3DbFree(db, pTable->zName);
sqlite3DbFree(db, pTable->zColAff);
sqlite3SelectDelete(db, pTable->pSelect);
-#ifndef SQLITE_OMIT_CHECK
sqlite3ExprListDelete(db, pTable->pCheck);
-#endif
#ifndef SQLITE_OMIT_VIRTUALTABLE
sqlite3VtabClear(db, pTable);
#endif
@@ -91445,6 +97451,13 @@ SQLITE_PRIVATE void sqlite3DeleteTable(sqlite3 *db, Table *pTable){
/* Verify that no lookaside memory was used by schema tables */
assert( nLookaside==0 || nLookaside==db->lookaside.nOut );
}
+SQLITE_PRIVATE void sqlite3DeleteTable(sqlite3 *db, Table *pTable){
+ /* Do not delete the table until the reference count reaches zero. */
+ if( !pTable ) return;
+ if( ((!db || db->pnBytesFreed==0) && (--pTable->nRef)>0) ) return;
+ deleteTable(db, pTable);
+}
+
/*
** Unlink the given table from the hash tables and the delete the
@@ -91512,12 +97525,8 @@ SQLITE_PRIVATE int sqlite3FindDbName(sqlite3 *db, const char *zName){
int i = -1; /* Database number */
if( zName ){
Db *pDb;
- int n = sqlite3Strlen30(zName);
for(i=(db->nDb-1), pDb=&db->aDb[i]; i>=0; i--, pDb--){
- if( (!OMIT_TEMPDB || i!=1 ) && n==sqlite3Strlen30(pDb->zName) &&
- 0==sqlite3StrICmp(pDb->zName, zName) ){
- break;
- }
+ if( 0==sqlite3StrICmp(pDb->zName, zName) ) break;
}
}
return i;
@@ -91563,7 +97572,8 @@ SQLITE_PRIVATE int sqlite3TwoPartName(
int iDb; /* Database holding the object */
sqlite3 *db = pParse->db;
- if( ALWAYS(pName2!=0) && pName2->n>0 ){
+ assert( pName2!=0 );
+ if( pName2->n>0 ){
if( db->init.busy ) {
sqlite3ErrorMsg(pParse, "corrupt database");
return -1;
@@ -91652,62 +97662,46 @@ SQLITE_PRIVATE void sqlite3StartTable(
int iDb; /* Database number to create the table in */
Token *pName; /* Unqualified name of the table to create */
- /* The table or view name to create is passed to this routine via tokens
- ** pName1 and pName2. If the table name was fully qualified, for example:
- **
- ** CREATE TABLE xxx.yyy (...);
- **
- ** Then pName1 is set to "xxx" and pName2 "yyy". On the other hand if
- ** the table name is not fully qualified, i.e.:
- **
- ** CREATE TABLE yyy(...);
- **
- ** Then pName1 is set to "yyy" and pName2 is "".
- **
- ** The call below sets the pName pointer to point at the token (pName1 or
- ** pName2) that stores the unqualified table name. The variable iDb is
- ** set to the index of the database that the table or view is to be
- ** created in.
- */
- iDb = sqlite3TwoPartName(pParse, pName1, pName2, &pName);
- if( iDb<0 ) return;
- if( !OMIT_TEMPDB && isTemp && pName2->n>0 && iDb!=1 ){
- /* If creating a temp table, the name may not be qualified. Unless
- ** the database name is "temp" anyway. */
- sqlite3ErrorMsg(pParse, "temporary table name must be unqualified");
- return;
+ if( db->init.busy && db->init.newTnum==1 ){
+ /* Special case: Parsing the sqlite_master or sqlite_temp_master schema */
+ iDb = db->init.iDb;
+ zName = sqlite3DbStrDup(db, SCHEMA_TABLE(iDb));
+ pName = pName1;
+ }else{
+ /* The common case */
+ iDb = sqlite3TwoPartName(pParse, pName1, pName2, &pName);
+ if( iDb<0 ) return;
+ if( !OMIT_TEMPDB && isTemp && pName2->n>0 && iDb!=1 ){
+ /* If creating a temp table, the name may not be qualified. Unless
+ ** the database name is "temp" anyway. */
+ sqlite3ErrorMsg(pParse, "temporary table name must be unqualified");
+ return;
+ }
+ if( !OMIT_TEMPDB && isTemp ) iDb = 1;
+ zName = sqlite3NameFromToken(db, pName);
}
- if( !OMIT_TEMPDB && isTemp ) iDb = 1;
-
pParse->sNameToken = *pName;
- zName = sqlite3NameFromToken(db, pName);
if( zName==0 ) return;
if( SQLITE_OK!=sqlite3CheckObjectName(pParse, zName) ){
goto begin_table_error;
}
if( db->init.iDb==1 ) isTemp = 1;
#ifndef SQLITE_OMIT_AUTHORIZATION
- assert( (isTemp & 1)==isTemp );
+ assert( isTemp==0 || isTemp==1 );
+ assert( isView==0 || isView==1 );
{
- int code;
+ static const u8 aCode[] = {
+ SQLITE_CREATE_TABLE,
+ SQLITE_CREATE_TEMP_TABLE,
+ SQLITE_CREATE_VIEW,
+ SQLITE_CREATE_TEMP_VIEW
+ };
char *zDb = db->aDb[iDb].zName;
if( sqlite3AuthCheck(pParse, SQLITE_INSERT, SCHEMA_TABLE(isTemp), 0, zDb) ){
goto begin_table_error;
}
- if( isView ){
- if( !OMIT_TEMPDB && isTemp ){
- code = SQLITE_CREATE_TEMP_VIEW;
- }else{
- code = SQLITE_CREATE_VIEW;
- }
- }else{
- if( !OMIT_TEMPDB && isTemp ){
- code = SQLITE_CREATE_TEMP_TABLE;
- }else{
- code = SQLITE_CREATE_TABLE;
- }
- }
- if( !isVirtual && sqlite3AuthCheck(pParse, code, zName, 0, zDb) ){
+ if( !isVirtual && sqlite3AuthCheck(pParse, (int)aCode[isTemp+2*isView],
+ zName, 0, zDb) ){
goto begin_table_error;
}
}
@@ -91743,8 +97737,8 @@ SQLITE_PRIVATE void sqlite3StartTable(
pTable = sqlite3DbMallocZero(db, sizeof(Table));
if( pTable==0 ){
- db->mallocFailed = 1;
- pParse->rc = SQLITE_NOMEM;
+ assert( db->mallocFailed );
+ pParse->rc = SQLITE_NOMEM_BKPT;
pParse->nErr++;
goto begin_table_error;
}
@@ -91776,10 +97770,12 @@ SQLITE_PRIVATE void sqlite3StartTable(
** now.
*/
if( !db->init.busy && (v = sqlite3GetVdbe(pParse))!=0 ){
- int j1;
+ int addr1;
int fileFormat;
int reg1, reg2, reg3;
- sqlite3BeginWriteOperation(pParse, 0, iDb);
+ /* nullRow[] is an OP_Record encoding of a row containing 5 NULLs */
+ static const char nullRow[] = { 6, 0, 0, 0, 0, 0 };
+ sqlite3BeginWriteOperation(pParse, 1, iDb);
#ifndef SQLITE_OMIT_VIRTUALTABLE
if( isVirtual ){
@@ -91795,14 +97791,12 @@ SQLITE_PRIVATE void sqlite3StartTable(
reg3 = ++pParse->nMem;
sqlite3VdbeAddOp3(v, OP_ReadCookie, iDb, reg3, BTREE_FILE_FORMAT);
sqlite3VdbeUsesBtree(v, iDb);
- j1 = sqlite3VdbeAddOp1(v, OP_If, reg3); VdbeCoverage(v);
+ addr1 = sqlite3VdbeAddOp1(v, OP_If, reg3); VdbeCoverage(v);
fileFormat = (db->flags & SQLITE_LegacyFileFmt)!=0 ?
1 : SQLITE_MAX_FILE_FORMAT;
- sqlite3VdbeAddOp2(v, OP_Integer, fileFormat, reg3);
- sqlite3VdbeAddOp3(v, OP_SetCookie, iDb, BTREE_FILE_FORMAT, reg3);
- sqlite3VdbeAddOp2(v, OP_Integer, ENC(db), reg3);
- sqlite3VdbeAddOp3(v, OP_SetCookie, iDb, BTREE_TEXT_ENCODING, reg3);
- sqlite3VdbeJumpHere(v, j1);
+ sqlite3VdbeAddOp3(v, OP_SetCookie, iDb, BTREE_FILE_FORMAT, fileFormat);
+ sqlite3VdbeAddOp3(v, OP_SetCookie, iDb, BTREE_TEXT_ENCODING, ENC(db));
+ sqlite3VdbeJumpHere(v, addr1);
/* This just creates a place-holder record in the sqlite_master table.
** The record created does not contain anything yet. It will be replaced
@@ -91823,7 +97817,7 @@ SQLITE_PRIVATE void sqlite3StartTable(
}
sqlite3OpenMasterTable(pParse, iDb);
sqlite3VdbeAddOp2(v, OP_NewRowid, 0, reg1);
- sqlite3VdbeAddOp2(v, OP_Null, 0, reg3);
+ sqlite3VdbeAddOp4(v, OP_Blob, 6, reg3, 0, nullRow, P4_STATIC);
sqlite3VdbeAddOp3(v, OP_Insert, 0, reg3, reg1);
sqlite3VdbeChangeP5(v, OPFLAG_APPEND);
sqlite3VdbeAddOp0(v, OP_Close);
@@ -91838,18 +97832,19 @@ begin_table_error:
return;
}
-/*
-** This macro is used to compare two strings in a case-insensitive manner.
-** It is slightly faster than calling sqlite3StrICmp() directly, but
-** produces larger code.
-**
-** WARNING: This macro is not compatible with the strcmp() family. It
-** returns true if the two strings are equal, otherwise false.
+/* Set properties of a table column based on the (magical)
+** name of the column.
*/
-#define STRICMP(x, y) (\
-sqlite3UpperToLower[*(unsigned char *)(x)]== \
-sqlite3UpperToLower[*(unsigned char *)(y)] \
-&& sqlite3StrICmp((x)+1,(y)+1)==0 )
+#if SQLITE_ENABLE_HIDDEN_COLUMNS
+SQLITE_PRIVATE void sqlite3ColumnPropertiesFromName(Table *pTab, Column *pCol){
+ if( sqlite3_strnicmp(pCol->zName, "__hidden__", 10)==0 ){
+ pCol->colFlags |= COLFLAG_HIDDEN;
+ }else if( pTab && pCol!=pTab->aCol && (pCol[-1].colFlags & COLFLAG_HIDDEN) ){
+ pTab->tabFlags |= TF_OOOHidden;
+ }
+}
+#endif
+
/*
** Add a new column to the table currently being constructed.
@@ -91859,10 +97854,11 @@ sqlite3UpperToLower[*(unsigned char *)(y)] \
** first to get things going. Then this routine is called for each
** column.
*/
-SQLITE_PRIVATE void sqlite3AddColumn(Parse *pParse, Token *pName){
+SQLITE_PRIVATE void sqlite3AddColumn(Parse *pParse, Token *pName, Token *pType){
Table *p;
int i;
char *z;
+ char *zType;
Column *pCol;
sqlite3 *db = pParse->db;
if( (p = pParse->pNewTable)==0 ) return;
@@ -91872,10 +97868,13 @@ SQLITE_PRIVATE void sqlite3AddColumn(Parse *pParse, Token *pName){
return;
}
#endif
- z = sqlite3NameFromToken(db, pName);
+ z = sqlite3DbMallocRaw(db, pName->n + pType->n + 2);
if( z==0 ) return;
+ memcpy(z, pName->z, pName->n);
+ z[pName->n] = 0;
+ sqlite3Dequote(z);
for(i=0; i<p->nCol; i++){
- if( STRICMP(z, p->aCol[i].zName) ){
+ if( sqlite3_stricmp(z, p->aCol[i].zName)==0 ){
sqlite3ErrorMsg(pParse, "duplicate column name: %s", z);
sqlite3DbFree(db, z);
return;
@@ -91893,14 +97892,23 @@ SQLITE_PRIVATE void sqlite3AddColumn(Parse *pParse, Token *pName){
pCol = &p->aCol[p->nCol];
memset(pCol, 0, sizeof(p->aCol[0]));
pCol->zName = z;
+ sqlite3ColumnPropertiesFromName(p, pCol);
- /* If there is no type specified, columns have the default affinity
- ** 'NONE'. If there is a type specified, then sqlite3AddColumnType() will
- ** be called next to set pCol->affinity correctly.
- */
- pCol->affinity = SQLITE_AFF_NONE;
- pCol->szEst = 1;
+ if( pType->n==0 ){
+ /* If there is no type specified, columns have the default affinity
+ ** 'BLOB'. */
+ pCol->affinity = SQLITE_AFF_BLOB;
+ pCol->szEst = 1;
+ }else{
+ zType = z + sqlite3Strlen30(z) + 1;
+ memcpy(zType, pType->z, pType->n);
+ zType[pType->n] = 0;
+ sqlite3Dequote(zType);
+ pCol->affinity = sqlite3AffinityType(zType, &pCol->szEst);
+ pCol->colFlags |= COLFLAG_HASTYPE;
+ }
p->nCol++;
+ pParse->constraintName.n = 0;
}
/*
@@ -91933,7 +97941,7 @@ SQLITE_PRIVATE void sqlite3AddNotNull(Parse *pParse, int onError){
** 'CHAR' | SQLITE_AFF_TEXT
** 'CLOB' | SQLITE_AFF_TEXT
** 'TEXT' | SQLITE_AFF_TEXT
-** 'BLOB' | SQLITE_AFF_NONE
+** 'BLOB' | SQLITE_AFF_BLOB
** 'REAL' | SQLITE_AFF_REAL
** 'FLOA' | SQLITE_AFF_REAL
** 'DOUB' | SQLITE_AFF_REAL
@@ -91946,7 +97954,7 @@ SQLITE_PRIVATE char sqlite3AffinityType(const char *zIn, u8 *pszEst){
char aff = SQLITE_AFF_NUMERIC;
const char *zChar = 0;
- if( zIn==0 ) return aff;
+ assert( zIn!=0 );
while( zIn[0] ){
h = (h<<8) + sqlite3UpperToLower[(*zIn)&0xff];
zIn++;
@@ -91959,7 +97967,7 @@ SQLITE_PRIVATE char sqlite3AffinityType(const char *zIn, u8 *pszEst){
aff = SQLITE_AFF_TEXT;
}else if( h==(('b'<<24)+('l'<<16)+('o'<<8)+'b') /* BLOB */
&& (aff==SQLITE_AFF_NUMERIC || aff==SQLITE_AFF_REAL) ){
- aff = SQLITE_AFF_NONE;
+ aff = SQLITE_AFF_BLOB;
if( zIn[0]=='(' ) zChar = zIn;
#ifndef SQLITE_OMIT_FLOATING_POINT
}else if( h==(('r'<<24)+('e'<<16)+('a'<<8)+'l') /* REAL */
@@ -92004,28 +98012,6 @@ SQLITE_PRIVATE char sqlite3AffinityType(const char *zIn, u8 *pszEst){
}
/*
-** This routine is called by the parser while in the middle of
-** parsing a CREATE TABLE statement. The pFirst token is the first
-** token in the sequence of tokens that describe the type of the
-** column currently under construction. pLast is the last token
-** in the sequence. Use this information to construct a string
-** that contains the typename of the column and store that string
-** in zType.
-*/
-SQLITE_PRIVATE void sqlite3AddColumnType(Parse *pParse, Token *pType){
- Table *p;
- Column *pCol;
-
- p = pParse->pNewTable;
- if( p==0 || NEVER(p->nCol<1) ) return;
- pCol = &p->aCol[p->nCol-1];
- assert( pCol->zType==0 || CORRUPT_DB );
- sqlite3DbFree(pParse->db, pCol->zType);
- pCol->zType = sqlite3NameFromToken(pParse->db, pType);
- pCol->affinity = sqlite3AffinityType(pCol->zType, &pCol->szEst);
-}
-
-/*
** The expression is the default value for the most recently added column
** of the table currently under construction.
**
@@ -92050,17 +98036,46 @@ SQLITE_PRIVATE void sqlite3AddDefaultValue(Parse *pParse, ExprSpan *pSpan){
** tokens that point to volatile memory. The 'span' of the expression
** is required by pragma table_info.
*/
+ Expr x;
sqlite3ExprDelete(db, pCol->pDflt);
- pCol->pDflt = sqlite3ExprDup(db, pSpan->pExpr, EXPRDUP_REDUCE);
- sqlite3DbFree(db, pCol->zDflt);
- pCol->zDflt = sqlite3DbStrNDup(db, (char*)pSpan->zStart,
- (int)(pSpan->zEnd - pSpan->zStart));
+ memset(&x, 0, sizeof(x));
+ x.op = TK_SPAN;
+ x.u.zToken = sqlite3DbStrNDup(db, (char*)pSpan->zStart,
+ (int)(pSpan->zEnd - pSpan->zStart));
+ x.pLeft = pSpan->pExpr;
+ x.flags = EP_Skip;
+ pCol->pDflt = sqlite3ExprDup(db, &x, EXPRDUP_REDUCE);
+ sqlite3DbFree(db, x.u.zToken);
}
}
sqlite3ExprDelete(db, pSpan->pExpr);
}
/*
+** Backwards Compatibility Hack:
+**
+** Historical versions of SQLite accepted strings as column names in
+** indexes and PRIMARY KEY constraints and in UNIQUE constraints. Example:
+**
+** CREATE TABLE xyz(a,b,c,d,e,PRIMARY KEY('a'),UNIQUE('b','c' COLLATE trim)
+** CREATE INDEX abc ON xyz('c','d' DESC,'e' COLLATE nocase DESC);
+**
+** This is goofy. But to preserve backwards compatibility we continue to
+** accept it. This routine does the necessary conversion. It converts
+** the expression given in its argument from a TK_STRING into a TK_ID
+** if the expression is just a TK_STRING with an optional COLLATE clause.
+** If the epxression is anything other than TK_STRING, the expression is
+** unchanged.
+*/
+static void sqlite3StringToId(Expr *p){
+ if( p->op==TK_STRING ){
+ p->op = TK_ID;
+ }else if( p->op==TK_COLLATE && p->pLeft->op==TK_STRING ){
+ p->pLeft->op = TK_ID;
+ }
+}
+
+/*
** Designate the PRIMARY KEY for the table. pList is a list of names
** of columns that form the primary key. If pList is NULL, then the
** most recently added column of the table is the primary key.
@@ -92086,7 +98101,7 @@ SQLITE_PRIVATE void sqlite3AddPrimaryKey(
int sortOrder /* SQLITE_SO_ASC or SQLITE_SO_DESC */
){
Table *pTab = pParse->pNewTable;
- char *zType = 0;
+ Column *pCol = 0;
int iCol = -1, i;
int nTerm;
if( pTab==0 || IN_DECLARE_VTAB ) goto primary_key_exit;
@@ -92098,24 +98113,31 @@ SQLITE_PRIVATE void sqlite3AddPrimaryKey(
pTab->tabFlags |= TF_HasPrimaryKey;
if( pList==0 ){
iCol = pTab->nCol - 1;
- pTab->aCol[iCol].colFlags |= COLFLAG_PRIMKEY;
- zType = pTab->aCol[iCol].zType;
+ pCol = &pTab->aCol[iCol];
+ pCol->colFlags |= COLFLAG_PRIMKEY;
nTerm = 1;
}else{
nTerm = pList->nExpr;
for(i=0; i<nTerm; i++){
- for(iCol=0; iCol<pTab->nCol; iCol++){
- if( sqlite3StrICmp(pList->a[i].zName, pTab->aCol[iCol].zName)==0 ){
- pTab->aCol[iCol].colFlags |= COLFLAG_PRIMKEY;
- zType = pTab->aCol[iCol].zType;
- break;
+ Expr *pCExpr = sqlite3ExprSkipCollate(pList->a[i].pExpr);
+ assert( pCExpr!=0 );
+ sqlite3StringToId(pCExpr);
+ if( pCExpr->op==TK_ID ){
+ const char *zCName = pCExpr->u.zToken;
+ for(iCol=0; iCol<pTab->nCol; iCol++){
+ if( sqlite3StrICmp(zCName, pTab->aCol[iCol].zName)==0 ){
+ pCol = &pTab->aCol[iCol];
+ pCol->colFlags |= COLFLAG_PRIMKEY;
+ break;
+ }
}
}
}
}
if( nTerm==1
- && zType && sqlite3StrICmp(zType, "INTEGER")==0
- && sortOrder==SQLITE_SO_ASC
+ && pCol
+ && sqlite3StrICmp(sqlite3ColumnType(pCol,""), "INTEGER")==0
+ && sortOrder!=SQLITE_SO_DESC
){
pTab->iPKey = iCol;
pTab->keyConf = (u8)onError;
@@ -92128,14 +98150,11 @@ SQLITE_PRIVATE void sqlite3AddPrimaryKey(
"INTEGER PRIMARY KEY");
#endif
}else{
- Vdbe *v = pParse->pVdbe;
Index *p;
- if( v ) pParse->addrSkipPK = sqlite3VdbeAddOp0(v, OP_Noop);
p = sqlite3CreateIndex(pParse, 0, 0, 0, pList, onError, 0,
0, sortOrder, 0);
if( p ){
p->idxType = SQLITE_IDXTYPE_PRIMARYKEY;
- if( v ) sqlite3VdbeJumpHere(v, pParse->addrSkipPK);
}
pList = 0;
}
@@ -92257,13 +98276,11 @@ SQLITE_PRIVATE CollSeq *sqlite3LocateCollSeq(Parse *pParse, const char *zName){
** 1 chance in 2^32. So we're safe enough.
*/
SQLITE_PRIVATE void sqlite3ChangeCookie(Parse *pParse, int iDb){
- int r1 = sqlite3GetTempReg(pParse);
sqlite3 *db = pParse->db;
Vdbe *v = pParse->pVdbe;
assert( sqlite3SchemaMutexHeld(db, iDb, 0) );
- sqlite3VdbeAddOp2(v, OP_Integer, db->aDb[iDb].pSchema->schema_cookie+1, r1);
- sqlite3VdbeAddOp3(v, OP_SetCookie, iDb, BTREE_SCHEMA_VERSION, r1);
- sqlite3ReleaseTempReg(pParse, r1);
+ sqlite3VdbeAddOp3(v, OP_SetCookie, iDb, BTREE_SCHEMA_VERSION,
+ db->aDb[iDb].pSchema->schema_cookie+1);
}
/*
@@ -92345,7 +98362,7 @@ static char *createTableStmt(sqlite3 *db, Table *p){
n += 35 + 6*p->nCol;
zStmt = sqlite3DbMallocRaw(0, n);
if( zStmt==0 ){
- db->mallocFailed = 1;
+ sqlite3OomFault(db);
return 0;
}
sqlite3_snprintf(n, zStmt, "CREATE TABLE ");
@@ -92354,7 +98371,7 @@ static char *createTableStmt(sqlite3 *db, Table *p){
zStmt[k++] = '(';
for(pCol=p->aCol, i=0; i<p->nCol; i++, pCol++){
static const char * const azType[] = {
- /* SQLITE_AFF_NONE */ "",
+ /* SQLITE_AFF_BLOB */ "",
/* SQLITE_AFF_TEXT */ " TEXT",
/* SQLITE_AFF_NUMERIC */ " NUM",
/* SQLITE_AFF_INTEGER */ " INT",
@@ -92367,17 +98384,17 @@ static char *createTableStmt(sqlite3 *db, Table *p){
k += sqlite3Strlen30(&zStmt[k]);
zSep = zSep2;
identPut(zStmt, &k, pCol->zName);
- assert( pCol->affinity-SQLITE_AFF_NONE >= 0 );
- assert( pCol->affinity-SQLITE_AFF_NONE < ArraySize(azType) );
- testcase( pCol->affinity==SQLITE_AFF_NONE );
+ assert( pCol->affinity-SQLITE_AFF_BLOB >= 0 );
+ assert( pCol->affinity-SQLITE_AFF_BLOB < ArraySize(azType) );
+ testcase( pCol->affinity==SQLITE_AFF_BLOB );
testcase( pCol->affinity==SQLITE_AFF_TEXT );
testcase( pCol->affinity==SQLITE_AFF_NUMERIC );
testcase( pCol->affinity==SQLITE_AFF_INTEGER );
testcase( pCol->affinity==SQLITE_AFF_REAL );
- zType = azType[pCol->affinity - SQLITE_AFF_NONE];
+ zType = azType[pCol->affinity - SQLITE_AFF_BLOB];
len = sqlite3Strlen30(zType);
- assert( pCol->affinity==SQLITE_AFF_NONE
+ assert( pCol->affinity==SQLITE_AFF_BLOB
|| pCol->affinity==sqlite3AffinityType(zType, 0) );
memcpy(&zStmt[k], zType, len);
k += len;
@@ -92398,9 +98415,9 @@ static int resizeIndexObject(sqlite3 *db, Index *pIdx, int N){
assert( pIdx->isResized==0 );
nByte = (sizeof(char*) + sizeof(i16) + 1)*N;
zExtra = sqlite3DbMallocZero(db, nByte);
- if( zExtra==0 ) return SQLITE_NOMEM;
+ if( zExtra==0 ) return SQLITE_NOMEM_BKPT;
memcpy(zExtra, pIdx->azColl, sizeof(char*)*pIdx->nColumn);
- pIdx->azColl = (char**)zExtra;
+ pIdx->azColl = (const char**)zExtra;
zExtra += sizeof(char*)*N;
memcpy(zExtra, pIdx->aiColumn, sizeof(i16)*pIdx->nColumn);
pIdx->aiColumn = (i16*)zExtra;
@@ -92485,15 +98502,7 @@ static void convertToWithoutRowidTable(Parse *pParse, Table *pTab){
*/
if( pParse->addrCrTab ){
assert( v );
- sqlite3VdbeGetOp(v, pParse->addrCrTab)->opcode = OP_CreateIndex;
- }
-
- /* Bypass the creation of the PRIMARY KEY btree and the sqlite_master
- ** table entry.
- */
- if( pParse->addrSkipPK ){
- assert( v );
- sqlite3VdbeGetOp(v, pParse->addrSkipPK)->opcode = OP_Goto;
+ sqlite3VdbeChangeOpcode(v, pParse->addrCrTab, OP_CreateIndex);
}
/* Locate the PRIMARY KEY index. Or, if this table was originally
@@ -92501,10 +98510,11 @@ static void convertToWithoutRowidTable(Parse *pParse, Table *pTab){
*/
if( pTab->iPKey>=0 ){
ExprList *pList;
- pList = sqlite3ExprListAppend(pParse, 0, 0);
+ Token ipkToken;
+ sqlite3TokenInit(&ipkToken, pTab->aCol[pTab->iPKey].zName);
+ pList = sqlite3ExprListAppend(pParse, 0,
+ sqlite3ExprAlloc(db, TK_ID, &ipkToken, 0));
if( pList==0 ) return;
- pList->a[0].zName = sqlite3DbStrDup(pParse->db,
- pTab->aCol[pTab->iPKey].zName);
pList->a[0].sortOrder = pParse->iPkSortOrder;
assert( pParse->pNewTable==pTab );
pPk = sqlite3CreateIndex(pParse, 0, 0, 0, pList, pTab->keyConf, 0, 0, 0, 0);
@@ -92513,6 +98523,16 @@ static void convertToWithoutRowidTable(Parse *pParse, Table *pTab){
pTab->iPKey = -1;
}else{
pPk = sqlite3PrimaryKeyIndex(pTab);
+
+ /* Bypass the creation of the PRIMARY KEY btree and the sqlite_master
+ ** table entry. This is only required if currently generating VDBE
+ ** code for a CREATE TABLE (not when parsing one as part of reading
+ ** a database schema). */
+ if( v ){
+ assert( db->init.busy==0 );
+ sqlite3VdbeChangeOpcode(v, pPk->tnum, OP_Goto);
+ }
+
/*
** Remove all redundant columns from the PRIMARY KEY. For example, change
** "PRIMARY KEY(a,b,a,b,c,b,c,d)" into just "PRIMARY KEY(a,b,c,d)". Later
@@ -92535,7 +98555,7 @@ static void convertToWithoutRowidTable(Parse *pParse, Table *pTab){
** do not enforce this for imposter tables.) */
if( !db->init.imposterTable ){
for(i=0; i<nPk; i++){
- pTab->aCol[pPk->aiColumn[i]].notNull = 1;
+ pTab->aCol[pPk->aiColumn[i]].notNull = OE_Abort;
}
pPk->uniqNotNull = 1;
}
@@ -92577,7 +98597,7 @@ static void convertToWithoutRowidTable(Parse *pParse, Table *pTab){
if( !hasColumn(pPk->aiColumn, j, i) ){
assert( j<pPk->nColumn );
pPk->aiColumn[j] = i;
- pPk->azColl[j] = "BINARY";
+ pPk->azColl[j] = sqlite3StrBINARY;
j++;
}
}
@@ -92620,9 +98640,10 @@ SQLITE_PRIVATE void sqlite3EndTable(
int iDb; /* Database in which the table lives */
Index *pIdx; /* An implied index of the table */
- if( (pEnd==0 && pSelect==0) || db->mallocFailed ){
+ if( pEnd==0 && pSelect==0 ){
return;
}
+ assert( !db->mallocFailed );
p = pParse->pNewTable;
if( p==0 ) return;
@@ -92633,9 +98654,13 @@ SQLITE_PRIVATE void sqlite3EndTable(
** So do not write to the disk again. Extract the root page number
** for the table from the db->init.newTnum field. (The page number
** should have been put there by the sqliteOpenCb routine.)
+ **
+ ** If the root page number is 1, that means this is the sqlite_master
+ ** table itself. So mark it read-only.
*/
if( db->init.busy ){
p->tnum = db->init.newTnum;
+ if( p->tnum==1 ) p->tabFlags |= TF_Readonly;
}
/* Special processing for WITHOUT ROWID Tables */
@@ -92648,7 +98673,7 @@ SQLITE_PRIVATE void sqlite3EndTable(
if( (p->tabFlags & TF_HasPrimaryKey)==0 ){
sqlite3ErrorMsg(pParse, "PRIMARY KEY missing on table %s", p->zName);
}else{
- p->tabFlags |= TF_WithoutRowid;
+ p->tabFlags |= TF_WithoutRowid | TF_NoVisibleRowid;
convertToWithoutRowidTable(pParse, p);
}
}
@@ -92716,26 +98741,46 @@ SQLITE_PRIVATE void sqlite3EndTable(
** be redundant.
*/
if( pSelect ){
- SelectDest dest;
- Table *pSelTab;
-
+ SelectDest dest; /* Where the SELECT should store results */
+ int regYield; /* Register holding co-routine entry-point */
+ int addrTop; /* Top of the co-routine */
+ int regRec; /* A record to be insert into the new table */
+ int regRowid; /* Rowid of the next row to insert */
+ int addrInsLoop; /* Top of the loop for inserting rows */
+ Table *pSelTab; /* A table that describes the SELECT results */
+
+ regYield = ++pParse->nMem;
+ regRec = ++pParse->nMem;
+ regRowid = ++pParse->nMem;
assert(pParse->nTab==1);
+ sqlite3MayAbort(pParse);
sqlite3VdbeAddOp3(v, OP_OpenWrite, 1, pParse->regRoot, iDb);
sqlite3VdbeChangeP5(v, OPFLAG_P2ISREG);
pParse->nTab = 2;
- sqlite3SelectDestInit(&dest, SRT_Table, 1);
+ addrTop = sqlite3VdbeCurrentAddr(v) + 1;
+ sqlite3VdbeAddOp3(v, OP_InitCoroutine, regYield, 0, addrTop);
+ sqlite3SelectDestInit(&dest, SRT_Coroutine, regYield);
sqlite3Select(pParse, pSelect, &dest);
+ sqlite3VdbeEndCoroutine(v, regYield);
+ sqlite3VdbeJumpHere(v, addrTop - 1);
+ if( pParse->nErr ) return;
+ pSelTab = sqlite3ResultSetOfSelect(pParse, pSelect);
+ if( pSelTab==0 ) return;
+ assert( p->aCol==0 );
+ p->nCol = pSelTab->nCol;
+ p->aCol = pSelTab->aCol;
+ pSelTab->nCol = 0;
+ pSelTab->aCol = 0;
+ sqlite3DeleteTable(db, pSelTab);
+ addrInsLoop = sqlite3VdbeAddOp1(v, OP_Yield, dest.iSDParm);
+ VdbeCoverage(v);
+ sqlite3VdbeAddOp3(v, OP_MakeRecord, dest.iSdst, dest.nSdst, regRec);
+ sqlite3TableAffinity(v, p, 0);
+ sqlite3VdbeAddOp2(v, OP_NewRowid, 1, regRowid);
+ sqlite3VdbeAddOp3(v, OP_Insert, 1, regRec, regRowid);
+ sqlite3VdbeGoto(v, addrInsLoop);
+ sqlite3VdbeJumpHere(v, addrInsLoop);
sqlite3VdbeAddOp1(v, OP_Close, 1);
- if( pParse->nErr==0 ){
- pSelTab = sqlite3ResultSetOfSelect(pParse, pSelect);
- if( pSelTab==0 ) return;
- assert( p->aCol==0 );
- p->nCol = pSelTab->nCol;
- p->aCol = pSelTab->aCol;
- pSelTab->nCol = 0;
- pSelTab->aCol = 0;
- sqlite3DeleteTable(db, pSelTab);
- }
}
/* Compute the complete text of the CREATE statement */
@@ -92800,7 +98845,7 @@ SQLITE_PRIVATE void sqlite3EndTable(
pOld = sqlite3HashInsert(&pSchema->tblHash, p->zName, p);
if( pOld ){
assert( p==pOld ); /* Malloc must have failed inside HashInsert() */
- db->mallocFailed = 1;
+ sqlite3OomFault(db);
return;
}
pParse->pNewTable = 0;
@@ -92830,6 +98875,7 @@ SQLITE_PRIVATE void sqlite3CreateView(
Token *pBegin, /* The CREATE token that begins the statement */
Token *pName1, /* The token that holds the name of the view */
Token *pName2, /* The token that holds the name of the view */
+ ExprList *pCNames, /* Optional list of view column names */
Select *pSelect, /* A SELECT statement that will become the new view */
int isTemp, /* TRUE for a TEMPORARY view */
int noErr /* Suppress error messages if VIEW already exists */
@@ -92845,22 +98891,15 @@ SQLITE_PRIVATE void sqlite3CreateView(
if( pParse->nVar>0 ){
sqlite3ErrorMsg(pParse, "parameters are not allowed in views");
- sqlite3SelectDelete(db, pSelect);
- return;
+ goto create_view_fail;
}
sqlite3StartTable(pParse, pName1, pName2, isTemp, 1, 0, noErr);
p = pParse->pNewTable;
- if( p==0 || pParse->nErr ){
- sqlite3SelectDelete(db, pSelect);
- return;
- }
+ if( p==0 || pParse->nErr ) goto create_view_fail;
sqlite3TwoPartName(pParse, pName1, pName2, &pName);
iDb = sqlite3SchemaToIndex(db, p->pSchema);
sqlite3FixInit(&sFix, pParse, iDb, "view", pName);
- if( sqlite3FixSelect(&sFix, pSelect) ){
- sqlite3SelectDelete(db, pSelect);
- return;
- }
+ if( sqlite3FixSelect(&sFix, pSelect) ) goto create_view_fail;
/* Make a copy of the entire SELECT statement that defines the view.
** This will force all the Expr.token.z values to be dynamically
@@ -92868,30 +98907,31 @@ SQLITE_PRIVATE void sqlite3CreateView(
** they will persist after the current sqlite3_exec() call returns.
*/
p->pSelect = sqlite3SelectDup(db, pSelect, EXPRDUP_REDUCE);
- sqlite3SelectDelete(db, pSelect);
- if( db->mallocFailed ){
- return;
- }
- if( !db->init.busy ){
- sqlite3ViewGetColumnNames(pParse, p);
- }
+ p->pCheck = sqlite3ExprListDup(db, pCNames, EXPRDUP_REDUCE);
+ if( db->mallocFailed ) goto create_view_fail;
/* Locate the end of the CREATE VIEW statement. Make sEnd point to
** the end.
*/
sEnd = pParse->sLastToken;
- if( ALWAYS(sEnd.z[0]!=0) && sEnd.z[0]!=';' ){
+ assert( sEnd.z[0]!=0 );
+ if( sEnd.z[0]!=';' ){
sEnd.z += sEnd.n;
}
sEnd.n = 0;
n = (int)(sEnd.z - pBegin->z);
+ assert( n>0 );
z = pBegin->z;
- while( ALWAYS(n>0) && sqlite3Isspace(z[n-1]) ){ n--; }
+ while( sqlite3Isspace(z[n-1]) ){ n--; }
sEnd.z = &z[n-1];
sEnd.n = 1;
/* Use sqlite3EndTable() to add the view to the SQLITE_MASTER table */
sqlite3EndTable(pParse, 0, &sEnd, 0, 0);
+
+create_view_fail:
+ sqlite3SelectDelete(db, pSelect);
+ sqlite3ExprListDelete(db, pCNames);
return;
}
#endif /* SQLITE_OMIT_VIEW */
@@ -92956,11 +98996,10 @@ SQLITE_PRIVATE int sqlite3ViewGetColumnNames(Parse *pParse, Table *pTable){
assert( pTable->pSelect );
pSel = sqlite3SelectDup(db, pTable->pSelect, 0);
if( pSel ){
- u8 enableLookaside = db->lookaside.bEnabled;
n = pParse->nTab;
sqlite3SrcListAssignCursors(pParse, pSel->pSrc);
pTable->nCol = -1;
- db->lookaside.bEnabled = 0;
+ db->lookaside.bDisable++;
#ifndef SQLITE_OMIT_AUTHORIZATION
xAuth = db->xAuth;
db->xAuth = 0;
@@ -92969,25 +99008,43 @@ SQLITE_PRIVATE int sqlite3ViewGetColumnNames(Parse *pParse, Table *pTable){
#else
pSelTab = sqlite3ResultSetOfSelect(pParse, pSel);
#endif
- db->lookaside.bEnabled = enableLookaside;
pParse->nTab = n;
- if( pSelTab ){
+ if( pTable->pCheck ){
+ /* CREATE VIEW name(arglist) AS ...
+ ** The names of the columns in the table are taken from
+ ** arglist which is stored in pTable->pCheck. The pCheck field
+ ** normally holds CHECK constraints on an ordinary table, but for
+ ** a VIEW it holds the list of column names.
+ */
+ sqlite3ColumnsFromExprList(pParse, pTable->pCheck,
+ &pTable->nCol, &pTable->aCol);
+ if( db->mallocFailed==0
+ && pParse->nErr==0
+ && pTable->nCol==pSel->pEList->nExpr
+ ){
+ sqlite3SelectAddColumnTypeAndCollation(pParse, pTable, pSel);
+ }
+ }else if( pSelTab ){
+ /* CREATE VIEW name AS... without an argument list. Construct
+ ** the column names from the SELECT statement that defines the view.
+ */
assert( pTable->aCol==0 );
pTable->nCol = pSelTab->nCol;
pTable->aCol = pSelTab->aCol;
pSelTab->nCol = 0;
pSelTab->aCol = 0;
- sqlite3DeleteTable(db, pSelTab);
assert( sqlite3SchemaMutexHeld(db, 0, pTable->pSchema) );
- pTable->pSchema->schemaFlags |= DB_UnresetViews;
}else{
pTable->nCol = 0;
nErr++;
}
+ sqlite3DeleteTable(db, pSelTab);
sqlite3SelectDelete(db, pSel);
+ db->lookaside.bDisable--;
} else {
nErr++;
}
+ pTable->pSchema->schemaFlags |= DB_UnresetViews;
#endif /* SQLITE_OMIT_VIEW */
return nErr;
}
@@ -93004,7 +99061,7 @@ static void sqliteViewResetAll(sqlite3 *db, int idx){
for(i=sqliteHashFirst(&db->aDb[idx].pSchema->tblHash); i;i=sqliteHashNext(i)){
Table *pTab = sqliteHashData(i);
if( pTab->pSelect ){
- sqliteDeleteColumnNames(db, pTab);
+ sqlite3DeleteColumnNames(db, pTab);
pTab->aCol = 0;
pTab->nCol = 0;
}
@@ -93066,6 +99123,7 @@ SQLITE_PRIVATE void sqlite3RootPageMoved(sqlite3 *db, int iDb, int iFrom, int iT
static void destroyRootPage(Parse *pParse, int iTable, int iDb){
Vdbe *v = sqlite3GetVdbe(pParse);
int r1 = sqlite3GetTempReg(pParse);
+ assert( iTable>1 );
sqlite3VdbeAddOp3(v, OP_Destroy, iTable, r1, iDb);
sqlite3MayAbort(pParse);
#ifndef SQLITE_OMIT_AUTOVACUUM
@@ -93451,7 +99509,7 @@ SQLITE_PRIVATE void sqlite3CreateForeignKey(
pFKey->zTo, (void *)pFKey
);
if( pNextTo==pFKey ){
- db->mallocFailed = 1;
+ sqlite3OomFault(db);
goto fk_end;
}
if( pNextTo ){
@@ -93533,6 +99591,7 @@ static void sqlite3RefillIndex(Parse *pParse, Index *pIndex, int memRootPage){
tnum = pIndex->tnum;
}
pKey = sqlite3KeyInfoOfIndex(pParse, pIndex);
+ assert( pKey!=0 || db->mallocFailed || pParse->nErr );
/* Open the sorter cursor if we are to use one. */
iSorter = pParse->nTab++;
@@ -93556,10 +99615,9 @@ static void sqlite3RefillIndex(Parse *pParse, Index *pIndex, int memRootPage){
sqlite3VdbeChangeP5(v, OPFLAG_BULKCSR|((memRootPage>=0)?OPFLAG_P2ISREG:0));
addr1 = sqlite3VdbeAddOp2(v, OP_SorterSort, iSorter, 0); VdbeCoverage(v);
- assert( pKey!=0 || db->mallocFailed || pParse->nErr );
- if( IsUniqueIndex(pIndex) && pKey!=0 ){
+ if( IsUniqueIndex(pIndex) ){
int j2 = sqlite3VdbeCurrentAddr(v) + 3;
- sqlite3VdbeAddOp2(v, OP_Goto, 0, j2);
+ sqlite3VdbeGoto(v, j2);
addr2 = sqlite3VdbeCurrentAddr(v);
sqlite3VdbeAddOp4Int(v, OP_SorterCompare, iSorter, j2, regRecord,
pIndex->nKeyCol); VdbeCoverage(v);
@@ -93604,7 +99662,7 @@ SQLITE_PRIVATE Index *sqlite3AllocateIndexObject(
p = sqlite3DbMallocZero(db, nByte + nExtra);
if( p ){
char *pExtra = ((char*)p)+ROUND8(sizeof(Index));
- p->azColl = (char**)pExtra; pExtra += ROUND8(sizeof(char*)*nCol);
+ p->azColl = (const char**)pExtra; pExtra += ROUND8(sizeof(char*)*nCol);
p->aiRowLogEst = (LogEst*)pExtra; pExtra += sizeof(LogEst)*(nCol+1);
p->aiColumn = (i16*)pExtra; pExtra += sizeof(i16)*nCol;
p->aSortOrder = (u8*)pExtra;
@@ -93656,7 +99714,6 @@ SQLITE_PRIVATE Index *sqlite3CreateIndex(
int iDb; /* Index of the database that is being written */
Token *pName = 0; /* Unqualified name of the index to create */
struct ExprList_item *pListItem; /* For looping over pList */
- const Column *pTabCol; /* A column in the table */
int nExtra = 0; /* Space allocated for zExtra[] */
int nExtraCol; /* Number of extra columns needed */
char *zExtra = 0; /* Extra space after the Index object */
@@ -93811,11 +99868,15 @@ SQLITE_PRIVATE Index *sqlite3CreateIndex(
** So create a fake list to simulate this.
*/
if( pList==0 ){
- pList = sqlite3ExprListAppend(pParse, 0, 0);
+ Token prevCol;
+ sqlite3TokenInit(&prevCol, pTab->aCol[pTab->nCol-1].zName);
+ pList = sqlite3ExprListAppend(pParse, 0,
+ sqlite3ExprAlloc(db, TK_ID, &prevCol, 0));
if( pList==0 ) goto exit_create_index;
- pList->a[0].zName = sqlite3DbStrDup(pParse->db,
- pTab->aCol[pTab->nCol-1].zName);
- pList->a[0].sortOrder = (u8)sortOrder;
+ assert( pList->nExpr==1 );
+ sqlite3ExprListSetSortOrder(pList, sortOrder);
+ }else{
+ sqlite3ExprListCheckLength(pParse, pList, "index");
}
/* Figure out how many bytes of space are required to store explicitly
@@ -93823,8 +99884,8 @@ SQLITE_PRIVATE Index *sqlite3CreateIndex(
*/
for(i=0; i<pList->nExpr; i++){
Expr *pExpr = pList->a[i].pExpr;
- if( pExpr ){
- assert( pExpr->op==TK_COLLATE );
+ assert( pExpr!=0 );
+ if( pExpr->op==TK_COLLATE ){
nExtra += (1 + sqlite3Strlen30(pExpr->u.zToken));
}
}
@@ -93865,35 +99926,54 @@ SQLITE_PRIVATE Index *sqlite3CreateIndex(
sortOrderMask = 0; /* Ignore DESC */
}
- /* Scan the names of the columns of the table to be indexed and
- ** load the column indices into the Index structure. Report an error
- ** if any column is not found.
+ /* Analyze the list of expressions that form the terms of the index and
+ ** report any errors. In the common case where the expression is exactly
+ ** a table column, store that column in aiColumn[]. For general expressions,
+ ** populate pIndex->aColExpr and store XN_EXPR (-2) in aiColumn[].
**
- ** TODO: Add a test to make sure that the same column is not named
- ** more than once within the same index. Only the first instance of
- ** the column will ever be used by the optimizer. Note that using the
- ** same column more than once cannot be an error because that would
- ** break backwards compatibility - it needs to be a warning.
+ ** TODO: Issue a warning if two or more columns of the index are identical.
+ ** TODO: Issue a warning if the table primary key is used as part of the
+ ** index key.
*/
for(i=0, pListItem=pList->a; i<pList->nExpr; i++, pListItem++){
- const char *zColName = pListItem->zName;
- int requestedSortOrder;
- char *zColl; /* Collation sequence name */
-
- for(j=0, pTabCol=pTab->aCol; j<pTab->nCol; j++, pTabCol++){
- if( sqlite3StrICmp(zColName, pTabCol->zName)==0 ) break;
- }
- if( j>=pTab->nCol ){
- sqlite3ErrorMsg(pParse, "table %s has no column named %s",
- pTab->zName, zColName);
- pParse->checkSchema = 1;
- goto exit_create_index;
+ Expr *pCExpr; /* The i-th index expression */
+ int requestedSortOrder; /* ASC or DESC on the i-th expression */
+ const char *zColl; /* Collation sequence name */
+
+ sqlite3StringToId(pListItem->pExpr);
+ sqlite3ResolveSelfReference(pParse, pTab, NC_IdxExpr, pListItem->pExpr, 0);
+ if( pParse->nErr ) goto exit_create_index;
+ pCExpr = sqlite3ExprSkipCollate(pListItem->pExpr);
+ if( pCExpr->op!=TK_COLUMN ){
+ if( pTab==pParse->pNewTable ){
+ sqlite3ErrorMsg(pParse, "expressions prohibited in PRIMARY KEY and "
+ "UNIQUE constraints");
+ goto exit_create_index;
+ }
+ if( pIndex->aColExpr==0 ){
+ ExprList *pCopy = sqlite3ExprListDup(db, pList, 0);
+ pIndex->aColExpr = pCopy;
+ if( !db->mallocFailed ){
+ assert( pCopy!=0 );
+ pListItem = &pCopy->a[i];
+ }
+ }
+ j = XN_EXPR;
+ pIndex->aiColumn[i] = XN_EXPR;
+ pIndex->uniqNotNull = 0;
+ }else{
+ j = pCExpr->iColumn;
+ assert( j<=0x7fff );
+ if( j<0 ){
+ j = pTab->iPKey;
+ }else if( pTab->aCol[j].notNull==0 ){
+ pIndex->uniqNotNull = 0;
+ }
+ pIndex->aiColumn[i] = (i16)j;
}
- assert( j<=0x7fff );
- pIndex->aiColumn[i] = (i16)j;
- if( pListItem->pExpr ){
+ zColl = 0;
+ if( pListItem->pExpr->op==TK_COLLATE ){
int nColl;
- assert( pListItem->pExpr->op==TK_COLLATE );
zColl = pListItem->pExpr->u.zToken;
nColl = sqlite3Strlen30(zColl) + 1;
assert( nExtra>=nColl );
@@ -93901,21 +99981,26 @@ SQLITE_PRIVATE Index *sqlite3CreateIndex(
zColl = zExtra;
zExtra += nColl;
nExtra -= nColl;
- }else{
+ }else if( j>=0 ){
zColl = pTab->aCol[j].zColl;
- if( !zColl ) zColl = "BINARY";
}
+ if( !zColl ) zColl = sqlite3StrBINARY;
if( !db->init.busy && !sqlite3LocateCollSeq(pParse, zColl) ){
goto exit_create_index;
}
pIndex->azColl[i] = zColl;
requestedSortOrder = pListItem->sortOrder & sortOrderMask;
pIndex->aSortOrder[i] = (u8)requestedSortOrder;
- if( pTab->aCol[j].notNull==0 ) pIndex->uniqNotNull = 0;
}
+
+ /* Append the table key to the end of the index. For WITHOUT ROWID
+ ** tables (when pPk!=0) this will be the declared PRIMARY KEY. For
+ ** normal tables (when pPk==0) this will be the rowid.
+ */
if( pPk ){
for(j=0; j<pPk->nKeyCol; j++){
int x = pPk->aiColumn[j];
+ assert( x>=0 );
if( hasColumn(pIndex->aiColumn, pIndex->nKeyCol, x) ){
pIndex->nColumn--;
}else{
@@ -93927,12 +100012,26 @@ SQLITE_PRIVATE Index *sqlite3CreateIndex(
}
assert( i==pIndex->nColumn );
}else{
- pIndex->aiColumn[i] = -1;
- pIndex->azColl[i] = "BINARY";
+ pIndex->aiColumn[i] = XN_ROWID;
+ pIndex->azColl[i] = sqlite3StrBINARY;
}
sqlite3DefaultRowEst(pIndex);
if( pParse->pNewTable==0 ) estimateIndexWidth(pIndex);
+ /* If this index contains every column of its table, then mark
+ ** it as a covering index */
+ assert( HasRowid(pTab)
+ || pTab->iPKey<0 || sqlite3ColumnOfIndex(pIndex, pTab->iPKey)>=0 );
+ if( pTblName!=0 && pIndex->nColumn>=pTab->nCol ){
+ pIndex->isCovering = 1;
+ for(j=0; j<pTab->nCol; j++){
+ if( j==pTab->iPKey ) continue;
+ if( sqlite3ColumnOfIndex(pIndex,j)>=0 ) continue;
+ pIndex->isCovering = 0;
+ break;
+ }
+ }
+
if( pTab==pParse->pNewTable ){
/* This routine has been called to create an automatic index as a
** result of a PRIMARY KEY or UNIQUE clause on a column definition, or
@@ -93966,10 +100065,11 @@ SQLITE_PRIVATE Index *sqlite3CreateIndex(
for(k=0; k<pIdx->nKeyCol; k++){
const char *z1;
const char *z2;
+ assert( pIdx->aiColumn[k]>=0 );
if( pIdx->aiColumn[k]!=pIndex->aiColumn[k] ) break;
z1 = pIdx->azColl[k];
z2 = pIndex->azColl[k];
- if( z1!=z2 && sqlite3StrICmp(z1, z2) ) break;
+ if( sqlite3StrICmp(z1, z2) ) break;
}
if( k==pIdx->nKeyCol ){
if( pIdx->onError!=pIndex->onError ){
@@ -93997,6 +100097,7 @@ SQLITE_PRIVATE Index *sqlite3CreateIndex(
/* Link the new Index structure to its table and to the other
** in-memory database structures.
*/
+ assert( pParse->nErr==0 );
if( db->init.busy ){
Index *p;
assert( sqlite3SchemaMutexHeld(db, 0, pIndex->pSchema) );
@@ -94004,7 +100105,7 @@ SQLITE_PRIVATE Index *sqlite3CreateIndex(
pIndex->zName, pIndex);
if( p ){
assert( p==pIndex ); /* Malloc must have failed */
- db->mallocFailed = 1;
+ sqlite3OomFault(db);
goto exit_create_index;
}
db->flags |= SQLITE_InternChanges;
@@ -94026,7 +100127,7 @@ SQLITE_PRIVATE Index *sqlite3CreateIndex(
** has just been created, it contains no data and the index initialization
** step can be skipped.
*/
- else if( pParse->nErr==0 && (HasRowid(pTab) || pTblName!=0) ){
+ else if( HasRowid(pTab) || pTblName!=0 ){
Vdbe *v;
char *zStmt;
int iMem = ++pParse->nMem;
@@ -94034,10 +100135,15 @@ SQLITE_PRIVATE Index *sqlite3CreateIndex(
v = sqlite3GetVdbe(pParse);
if( v==0 ) goto exit_create_index;
-
- /* Create the rootpage for the index
- */
sqlite3BeginWriteOperation(pParse, 1, iDb);
+
+ /* Create the rootpage for the index using CreateIndex. But before
+ ** doing so, code a Noop instruction and store its address in
+ ** Index.tnum. This is required in case this index is actually a
+ ** PRIMARY KEY and the table is actually a WITHOUT ROWID table. In
+ ** that case the convertToWithoutRowidTable() routine will replace
+ ** the Noop with a Goto to jump over the VDBE code generated below. */
+ pIndex->tnum = sqlite3VdbeAddOp0(v, OP_Noop);
sqlite3VdbeAddOp2(v, OP_CreateIndex, iDb, iMem);
/* Gather the complete text of the CREATE INDEX statement into
@@ -94077,6 +100183,8 @@ SQLITE_PRIVATE Index *sqlite3CreateIndex(
sqlite3MPrintf(db, "name='%q' AND type='index'", pIndex->zName));
sqlite3VdbeAddOp1(v, OP_Expire, 0);
}
+
+ sqlite3VdbeJumpHere(v, pIndex->tnum);
}
/* When adding an index to the list of indices for a table, make
@@ -94426,10 +100534,12 @@ SQLITE_PRIVATE SrcList *sqlite3SrcListAppend(
){
struct SrcList_item *pItem;
assert( pDatabase==0 || pTable!=0 ); /* Cannot have C without B */
+ assert( db!=0 );
if( pList==0 ){
- pList = sqlite3DbMallocZero(db, sizeof(SrcList) );
+ pList = sqlite3DbMallocRawNN(db, sizeof(SrcList) );
if( pList==0 ) return 0;
pList->nAlloc = 1;
+ pList->nSrc = 0;
}
pList = sqlite3SrcListEnlarge(db, pList, 1, pList->nSrc);
if( db->mallocFailed ){
@@ -94479,7 +100589,8 @@ SQLITE_PRIVATE void sqlite3SrcListDelete(sqlite3 *db, SrcList *pList){
sqlite3DbFree(db, pItem->zDatabase);
sqlite3DbFree(db, pItem->zName);
sqlite3DbFree(db, pItem->zAlias);
- sqlite3DbFree(db, pItem->zIndex);
+ if( pItem->fg.isIndexedBy ) sqlite3DbFree(db, pItem->u1.zIndexedBy);
+ if( pItem->fg.isTabFunc ) sqlite3ExprListDelete(db, pItem->u1.pFuncArg);
sqlite3DeleteTable(db, pItem->pTab);
sqlite3SelectDelete(db, pItem->pSelect);
sqlite3ExprDelete(db, pItem->pOn);
@@ -94552,18 +100663,38 @@ SQLITE_PRIVATE void sqlite3SrcListIndexedBy(Parse *pParse, SrcList *p, Token *pI
assert( pIndexedBy!=0 );
if( p && ALWAYS(p->nSrc>0) ){
struct SrcList_item *pItem = &p->a[p->nSrc-1];
- assert( pItem->notIndexed==0 && pItem->zIndex==0 );
+ assert( pItem->fg.notIndexed==0 );
+ assert( pItem->fg.isIndexedBy==0 );
+ assert( pItem->fg.isTabFunc==0 );
if( pIndexedBy->n==1 && !pIndexedBy->z ){
/* A "NOT INDEXED" clause was supplied. See parse.y
** construct "indexed_opt" for details. */
- pItem->notIndexed = 1;
+ pItem->fg.notIndexed = 1;
}else{
- pItem->zIndex = sqlite3NameFromToken(pParse->db, pIndexedBy);
+ pItem->u1.zIndexedBy = sqlite3NameFromToken(pParse->db, pIndexedBy);
+ pItem->fg.isIndexedBy = (pItem->u1.zIndexedBy!=0);
}
}
}
/*
+** Add the list of function arguments to the SrcList entry for a
+** table-valued-function.
+*/
+SQLITE_PRIVATE void sqlite3SrcListFuncArgs(Parse *pParse, SrcList *p, ExprList *pList){
+ if( p ){
+ struct SrcList_item *pItem = &p->a[p->nSrc-1];
+ assert( pItem->fg.notIndexed==0 );
+ assert( pItem->fg.isIndexedBy==0 );
+ assert( pItem->fg.isTabFunc==0 );
+ pItem->u1.pFuncArg = pList;
+ pItem->fg.isTabFunc = 1;
+ }else{
+ sqlite3ExprListDelete(pParse->db, pList);
+ }
+}
+
+/*
** When building up a FROM clause in the parser, the join operator
** is initially attached to the left operand. But the code generator
** expects the join operator to be on the right operand. This routine
@@ -94582,14 +100713,14 @@ SQLITE_PRIVATE void sqlite3SrcListShiftJoinType(SrcList *p){
if( p ){
int i;
for(i=p->nSrc-1; i>0; i--){
- p->a[i].jointype = p->a[i-1].jointype;
+ p->a[i].fg.jointype = p->a[i-1].fg.jointype;
}
- p->a[0].jointype = 0;
+ p->a[0].fg.jointype = 0;
}
}
/*
-** Begin a transaction
+** Generate VDBE code for a BEGIN statement.
*/
SQLITE_PRIVATE void sqlite3BeginTransaction(Parse *pParse, int type){
sqlite3 *db;
@@ -94599,7 +100730,6 @@ SQLITE_PRIVATE void sqlite3BeginTransaction(Parse *pParse, int type){
assert( pParse!=0 );
db = pParse->db;
assert( db!=0 );
-/* if( db->aDb[0].pBt==0 ) return; */
if( sqlite3AuthCheck(pParse, SQLITE_TRANSACTION, "BEGIN", 0, 0) ){
return;
}
@@ -94611,11 +100741,11 @@ SQLITE_PRIVATE void sqlite3BeginTransaction(Parse *pParse, int type){
sqlite3VdbeUsesBtree(v, i);
}
}
- sqlite3VdbeAddOp2(v, OP_AutoCommit, 0, 0);
+ sqlite3VdbeAddOp0(v, OP_AutoCommit);
}
/*
-** Commit a transaction
+** Generate VDBE code for a COMMIT statement.
*/
SQLITE_PRIVATE void sqlite3CommitTransaction(Parse *pParse){
Vdbe *v;
@@ -94627,12 +100757,12 @@ SQLITE_PRIVATE void sqlite3CommitTransaction(Parse *pParse){
}
v = sqlite3GetVdbe(pParse);
if( v ){
- sqlite3VdbeAddOp2(v, OP_AutoCommit, 1, 0);
+ sqlite3VdbeAddOp1(v, OP_AutoCommit, 1);
}
}
/*
-** Rollback a transaction
+** Generate VDBE code for a ROLLBACK statement.
*/
SQLITE_PRIVATE void sqlite3RollbackTransaction(Parse *pParse){
Vdbe *v;
@@ -94694,7 +100824,7 @@ SQLITE_PRIVATE int sqlite3OpenTempDatabase(Parse *pParse){
db->aDb[1].pBt = pBt;
assert( db->aDb[1].pSchema );
if( SQLITE_NOMEM==sqlite3BtreeSetPageSize(pBt, db->nextPagesize, -1, 0) ){
- db->mallocFailed = 1;
+ sqlite3OomFault(db);
return 1;
}
}
@@ -94811,7 +100941,7 @@ SQLITE_PRIVATE void sqlite3HaltConstraint(
sqlite3MayAbort(pParse);
}
sqlite3VdbeAddOp4(v, OP_Halt, errCode, onError, 0, p4, p4type);
- if( p5Errmsg ) sqlite3VdbeChangeP5(v, p5Errmsg);
+ sqlite3VdbeChangeP5(v, p5Errmsg);
}
/*
@@ -94828,12 +100958,16 @@ SQLITE_PRIVATE void sqlite3UniqueConstraint(
Table *pTab = pIdx->pTable;
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);
- sqlite3StrAccumAppendAll(&errMsg, pTab->zName);
- sqlite3StrAccumAppend(&errMsg, ".", 1);
- sqlite3StrAccumAppendAll(&errMsg, zCol);
+ if( pIdx->aColExpr ){
+ sqlite3XPrintf(&errMsg, "index '%q'", pIdx->zName);
+ }else{
+ for(j=0; j<pIdx->nKeyCol; j++){
+ char *zCol;
+ assert( pIdx->aiColumn[j]>=0 );
+ zCol = pTab->aCol[pIdx->aiColumn[j]].zName;
+ if( j ) sqlite3StrAccumAppend(&errMsg, ", ", 2);
+ sqlite3XPrintf(&errMsg, "%s.%s", pTab->zName, zCol);
+ }
}
zErr = sqlite3StrAccumFinish(&errMsg);
sqlite3HaltConstraint(pParse,
@@ -95018,9 +101152,8 @@ SQLITE_PRIVATE KeyInfo *sqlite3KeyInfoOfIndex(Parse *pParse, Index *pIdx){
if( pKey ){
assert( sqlite3KeyInfoIsWriteable(pKey) );
for(i=0; i<nCol; i++){
- char *zColl = pIdx->azColl[i];
- assert( zColl!=0 );
- pKey->aColl[i] = strcmp(zColl,"BINARY")==0 ? 0 :
+ const char *zColl = pIdx->azColl[i];
+ pKey->aColl[i] = zColl==sqlite3StrBINARY ? 0 :
sqlite3LocateCollSeq(pParse, zColl);
pKey->aSortOrder[i] = pIdx->aSortOrder[i];
}
@@ -95066,10 +101199,9 @@ SQLITE_PRIVATE With *sqlite3WithAdd(
}else{
pNew = sqlite3DbMallocZero(db, sizeof(*pWith));
}
- assert( zName!=0 || pNew==0 );
- assert( db->mallocFailed==0 || pNew==0 );
+ assert( (pNew!=0 && zName!=0) || db->mallocFailed );
- if( pNew==0 ){
+ if( db->mallocFailed ){
sqlite3ExprListDelete(db, pArglist);
sqlite3SelectDelete(db, pQuery);
sqlite3DbFree(db, zName);
@@ -95078,7 +101210,7 @@ SQLITE_PRIVATE With *sqlite3WithAdd(
pNew->a[pNew->nCte].pSelect = pQuery;
pNew->a[pNew->nCte].pCols = pArglist;
pNew->a[pNew->nCte].zName = zName;
- pNew->a[pNew->nCte].zErr = 0;
+ pNew->a[pNew->nCte].zCteErr = 0;
pNew->nCte++;
}
@@ -95120,6 +101252,7 @@ SQLITE_PRIVATE void sqlite3WithDelete(sqlite3 *db, With *pWith){
** of user defined functions and collation sequences.
*/
+/* #include "sqliteInt.h" */
/*
** Invoke the 'collation needed' callback to request a collation sequence
@@ -95282,7 +101415,7 @@ static CollSeq *findCollSeqEntry(
*/
assert( pDel==0 || pDel==pColl );
if( pDel!=0 ){
- db->mallocFailed = 1;
+ sqlite3OomFault(db);
sqlite3DbFree(db, pDel);
pColl = 0;
}
@@ -95348,8 +101481,8 @@ SQLITE_PRIVATE CollSeq *sqlite3FindCollSeq(
** 5: UTF16 byte order conversion required - argument count matches exactly
** 6: Perfect match: encoding and argument count match exactly.
**
-** If nArg==(-2) then any function with a non-null xStep or xFunc is
-** a perfect match and any function with both xStep and xFunc NULL is
+** If nArg==(-2) then any function with a non-null xSFunc is
+** a perfect match and any function with xSFunc NULL is
** a non-match.
*/
#define FUNC_PERFECT_MATCH 6 /* The score for a perfect match */
@@ -95361,7 +101494,7 @@ static int matchQuality(
int match;
/* nArg of -2 is a special case */
- if( nArg==(-2) ) return (p->xFunc==0 && p->xStep==0) ? 0 : FUNC_PERFECT_MATCH;
+ if( nArg==(-2) ) return (p->xSFunc==0) ? 0 : FUNC_PERFECT_MATCH;
/* Wrong number of arguments means "no match" */
if( p->nArg!=nArg && p->nArg>=0 ) return 0;
@@ -95389,14 +101522,12 @@ static int matchQuality(
** a pointer to the matching FuncDef if found, or 0 if there is no match.
*/
static FuncDef *functionSearch(
- FuncDefHash *pHash, /* Hash table to search */
int h, /* Hash of the name */
- const char *zFunc, /* Name of function */
- int nFunc /* Number of bytes in zFunc */
+ const char *zFunc /* Name of function */
){
FuncDef *p;
- for(p=pHash->a[h]; p; p=p->pHash){
- if( sqlite3StrNICmp(p->zName, zFunc, nFunc)==0 && p->zName[nFunc]==0 ){
+ for(p=sqlite3BuiltinFunctions.a[h]; p; p=p->u.pHash){
+ if( sqlite3StrICmp(p->zName, zFunc)==0 ){
return p;
}
}
@@ -95406,23 +101537,26 @@ static FuncDef *functionSearch(
/*
** Insert a new FuncDef into a FuncDefHash hash table.
*/
-SQLITE_PRIVATE void sqlite3FuncDefInsert(
- FuncDefHash *pHash, /* The hash table into which to insert */
- FuncDef *pDef /* The function definition to insert */
+SQLITE_PRIVATE void sqlite3InsertBuiltinFuncs(
+ FuncDef *aDef, /* List of global functions to be inserted */
+ int nDef /* Length of the apDef[] list */
){
- FuncDef *pOther;
- int nName = sqlite3Strlen30(pDef->zName);
- u8 c1 = (u8)pDef->zName[0];
- int h = (sqlite3UpperToLower[c1] + nName) % ArraySize(pHash->a);
- pOther = functionSearch(pHash, h, pDef->zName, nName);
- if( pOther ){
- assert( pOther!=pDef && pOther->pNext!=pDef );
- pDef->pNext = pOther->pNext;
- pOther->pNext = pDef;
- }else{
- pDef->pNext = 0;
- pDef->pHash = pHash->a[h];
- pHash->a[h] = pDef;
+ int i;
+ for(i=0; i<nDef; i++){
+ FuncDef *pOther;
+ const char *zName = aDef[i].zName;
+ int nName = sqlite3Strlen30(zName);
+ int h = (sqlite3UpperToLower[(u8)zName[0]] + nName) % SQLITE_FUNC_HASH_SZ;
+ pOther = functionSearch(h, zName);
+ if( pOther ){
+ assert( pOther!=&aDef[i] && pOther->pNext!=&aDef[i] );
+ aDef[i].pNext = pOther->pNext;
+ pOther->pNext = &aDef[i];
+ }else{
+ aDef[i].pNext = 0;
+ aDef[i].u.pHash = sqlite3BuiltinFunctions.a[h];
+ sqlite3BuiltinFunctions.a[h] = &aDef[i];
+ }
}
}
@@ -95439,7 +101573,7 @@ SQLITE_PRIVATE void sqlite3FuncDefInsert(
** no matching function previously existed.
**
** If nArg is -2, then the first valid function found is returned. A
-** function is valid if either xFunc or xStep is non-zero. The nArg==(-2)
+** function is valid if xSFunc is non-zero. The nArg==(-2)
** case is used to see if zName is a valid function name for some number
** of arguments. If nArg is -2, then createFlag must be 0.
**
@@ -95449,8 +101583,7 @@ SQLITE_PRIVATE void sqlite3FuncDefInsert(
*/
SQLITE_PRIVATE FuncDef *sqlite3FindFunction(
sqlite3 *db, /* An open database */
- const char *zName, /* Name of the function. Not null-terminated */
- int nName, /* Number of characters in the name */
+ const char *zName, /* Name of the function. zero-terminated */
int nArg, /* Number of arguments. -1 means any number */
u8 enc, /* Preferred text encoding */
u8 createFlag /* Create new entry if true and does not otherwise exist */
@@ -95459,14 +101592,15 @@ SQLITE_PRIVATE FuncDef *sqlite3FindFunction(
FuncDef *pBest = 0; /* Best match found so far */
int bestScore = 0; /* Score of best match */
int h; /* Hash value */
+ int nName; /* Length of the name */
assert( nArg>=(-2) );
assert( nArg>=(-1) || createFlag==0 );
- h = (sqlite3UpperToLower[(u8)zName[0]] + nName) % ArraySize(db->aFunc.a);
+ nName = sqlite3Strlen30(zName);
/* First search for a match amongst the application-defined functions.
*/
- p = functionSearch(&db->aFunc, h, zName, nName);
+ p = (FuncDef*)sqlite3HashFind(&db->aFunc, zName);
while( p ){
int score = matchQuality(p, nArg, enc);
if( score>bestScore ){
@@ -95489,9 +101623,9 @@ SQLITE_PRIVATE FuncDef *sqlite3FindFunction(
** So we must not search for built-ins when creating a new function.
*/
if( !createFlag && (pBest==0 || (db->flags & SQLITE_PreferBuiltin)!=0) ){
- FuncDefHash *pHash = &GLOBAL(FuncDefHash, sqlite3GlobalFunctions);
bestScore = 0;
- p = functionSearch(pHash, h, zName, nName);
+ h = (sqlite3UpperToLower[(u8)zName[0]] + nName) % SQLITE_FUNC_HASH_SZ;
+ p = functionSearch(h, zName);
while( p ){
int score = matchQuality(p, nArg, enc);
if( score>bestScore ){
@@ -95508,15 +101642,22 @@ SQLITE_PRIVATE FuncDef *sqlite3FindFunction(
*/
if( createFlag && bestScore<FUNC_PERFECT_MATCH &&
(pBest = sqlite3DbMallocZero(db, sizeof(*pBest)+nName+1))!=0 ){
- pBest->zName = (char *)&pBest[1];
+ FuncDef *pOther;
+ pBest->zName = (const char*)&pBest[1];
pBest->nArg = (u16)nArg;
pBest->funcFlags = enc;
- memcpy(pBest->zName, zName, nName);
- pBest->zName[nName] = 0;
- sqlite3FuncDefInsert(&db->aFunc, pBest);
+ memcpy((char*)&pBest[1], zName, nName+1);
+ pOther = (FuncDef*)sqlite3HashInsert(&db->aFunc, pBest->zName, pBest);
+ if( pOther==pBest ){
+ sqlite3DbFree(db, pBest);
+ sqlite3OomFault(db);
+ return 0;
+ }else{
+ pBest->pNext = pOther;
+ }
}
- if( pBest && (pBest->xStep || pBest->xFunc || createFlag) ){
+ if( pBest && (pBest->xSFunc || createFlag) ){
return pBest;
}
return 0;
@@ -95570,7 +101711,7 @@ SQLITE_PRIVATE Schema *sqlite3SchemaGet(sqlite3 *db, Btree *pBt){
p = (Schema *)sqlite3DbMallocZero(0, sizeof(Schema));
}
if( !p ){
- db->mallocFailed = 1;
+ sqlite3OomFault(db);
}else if ( 0==p->file_format ){
sqlite3HashInit(&p->tblHash);
sqlite3HashInit(&p->idxHash);
@@ -95597,6 +101738,7 @@ SQLITE_PRIVATE Schema *sqlite3SchemaGet(sqlite3 *db, Btree *pBt){
** This file contains C code routines that are called by the parser
** in order to generate code for DELETE FROM statements.
*/
+/* #include "sqliteInt.h" */
/*
** While a SrcList can in general represent multiple tables and subqueries
@@ -95690,7 +101832,8 @@ SQLITE_PRIVATE void sqlite3MaterializeView(
assert( pFrom->a[0].pOn==0 );
assert( pFrom->a[0].pUsing==0 );
}
- pSel = sqlite3SelectNew(pParse, 0, pFrom, pWhere, 0, 0, 0, 0, 0, 0);
+ pSel = sqlite3SelectNew(pParse, 0, pFrom, pWhere, 0, 0, 0,
+ SF_IncludeHidden, 0, 0);
sqlite3SelectDestInit(&dest, SRT_EphemTab, iCur);
sqlite3Select(pParse, pSel, &dest);
sqlite3SelectDelete(db, pSel);
@@ -95726,7 +101869,7 @@ SQLITE_PRIVATE Expr *sqlite3LimitWhere(
*/
if( pOrderBy && (pLimit == 0) ) {
sqlite3ErrorMsg(pParse, "ORDER BY without LIMIT on %s", zStmtType);
- goto limit_where_cleanup_2;
+ goto limit_where_cleanup;
}
/* We only need to generate a select expression if there
@@ -95748,16 +101891,16 @@ SQLITE_PRIVATE Expr *sqlite3LimitWhere(
*/
pSelectRowid = sqlite3PExpr(pParse, TK_ROW, 0, 0, 0);
- if( pSelectRowid == 0 ) goto limit_where_cleanup_2;
+ if( pSelectRowid == 0 ) goto limit_where_cleanup;
pEList = sqlite3ExprListAppend(pParse, 0, pSelectRowid);
- if( pEList == 0 ) goto limit_where_cleanup_2;
+ if( pEList == 0 ) goto limit_where_cleanup;
/* duplicate the FROM clause as it is needed by both the DELETE/UPDATE tree
** and the SELECT subtree. */
pSelectSrc = sqlite3SrcListDup(pParse->db, pSrc, 0);
if( pSelectSrc == 0 ) {
sqlite3ExprListDelete(pParse->db, pEList);
- goto limit_where_cleanup_2;
+ goto limit_where_cleanup;
}
/* generate the SELECT expression tree. */
@@ -95767,21 +101910,11 @@ SQLITE_PRIVATE Expr *sqlite3LimitWhere(
/* now generate the new WHERE rowid IN clause for the DELETE/UDPATE */
pWhereRowid = sqlite3PExpr(pParse, TK_ROW, 0, 0, 0);
- if( pWhereRowid == 0 ) goto limit_where_cleanup_1;
- pInClause = sqlite3PExpr(pParse, TK_IN, pWhereRowid, 0, 0);
- if( pInClause == 0 ) goto limit_where_cleanup_1;
-
- pInClause->x.pSelect = pSelect;
- pInClause->flags |= EP_xIsSelect;
- sqlite3ExprSetHeightAndFlags(pParse, pInClause);
+ pInClause = pWhereRowid ? sqlite3PExpr(pParse, TK_IN, pWhereRowid, 0, 0) : 0;
+ sqlite3PExprAddSelect(pParse, pInClause, pSelect);
return pInClause;
- /* something went wrong. clean up anything allocated. */
-limit_where_cleanup_1:
- sqlite3SelectDelete(pParse->db, pSelect);
- return 0;
-
-limit_where_cleanup_2:
+limit_where_cleanup:
sqlite3ExprDelete(pParse->db, pWhere);
sqlite3ExprListDelete(pParse->db, pOrderBy);
sqlite3ExprDelete(pParse->db, pLimit);
@@ -95819,7 +101952,7 @@ SQLITE_PRIVATE void sqlite3DeleteFrom(
int iDb; /* Database number */
int memCnt = -1; /* Memory cell used for change counting */
int rcauth; /* Value returned by authorization callback */
- int okOnePass; /* True for one-pass algorithm without the FIFO */
+ int eOnePass; /* ONEPASS_OFF or _SINGLE or _MULTI */
int aiCurOnePass[2]; /* The write cursors opened by WHERE_ONEPASS */
u8 *aToOpen = 0; /* Open cursor iTabCur+j if aToOpen[j] is true */
Index *pPk; /* The PRIMARY KEY index on the table */
@@ -95831,8 +101964,9 @@ SQLITE_PRIVATE void sqlite3DeleteFrom(
int iRowSet = 0; /* Register for rowset of rows to delete */
int addrBypass = 0; /* Address of jump over the delete logic */
int addrLoop = 0; /* Top of the delete loop */
- int addrDelete = 0; /* Jump directly to the delete logic */
int addrEphOpen = 0; /* Instruction to open the Ephemeral table */
+ int bComplex; /* True if there are triggers or FKs or
+ ** subqueries in the WHERE clause */
#ifndef SQLITE_OMIT_TRIGGER
int isView; /* True if attempting to delete from a view */
@@ -95860,6 +101994,7 @@ SQLITE_PRIVATE void sqlite3DeleteFrom(
#ifndef SQLITE_OMIT_TRIGGER
pTrigger = sqlite3TriggersExist(pParse, pTab, TK_DELETE, 0, 0);
isView = pTab->pSelect!=0;
+ bComplex = pTrigger || sqlite3FkRequired(pParse, pTab, 0, 0);
#else
# define pTrigger 0
# define isView 0
@@ -95943,8 +102078,13 @@ SQLITE_PRIVATE void sqlite3DeleteFrom(
** It is easier just to erase the whole table. Prior to version 3.6.5,
** this optimization caused the row change count (the value returned by
** API function sqlite3_count_changes) to be set incorrectly. */
- if( rcauth==SQLITE_OK && pWhere==0 && !pTrigger && !IsVirtual(pTab)
- && 0==sqlite3FkRequired(pParse, pTab, 0, 0)
+ if( rcauth==SQLITE_OK
+ && pWhere==0
+ && !bComplex
+ && !IsVirtual(pTab)
+#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
+ && db->xPreUpdateCallback==0
+#endif
){
assert( !isView );
sqlite3TableLock(pParse, iDb, pTab->tnum, 1, pTab->zName);
@@ -95959,6 +102099,9 @@ SQLITE_PRIVATE void sqlite3DeleteFrom(
}else
#endif /* SQLITE_OMIT_TRUNCATE_OPTIMIZATION */
{
+ u16 wcf = WHERE_ONEPASS_DESIRED|WHERE_DUPLICATES_OK|WHERE_SEEK_TABLE;
+ if( sNC.ncFlags & NC_VarSelect ) bComplex = 1;
+ wcf |= (bComplex ? 0 : WHERE_ONEPASS_MULTIROW);
if( HasRowid(pTab) ){
/* For a rowid table, initialize the RowSet to an empty set */
pPk = 0;
@@ -95979,13 +102122,18 @@ SQLITE_PRIVATE void sqlite3DeleteFrom(
}
/* Construct a query to find the rowid or primary key for every row
- ** to be deleted, based on the WHERE clause.
+ ** to be deleted, based on the WHERE clause. Set variable eOnePass
+ ** to indicate the strategy used to implement this delete:
+ **
+ ** ONEPASS_OFF: Two-pass approach - use a FIFO for rowids/PK values.
+ ** ONEPASS_SINGLE: One-pass approach - at most one row deleted.
+ ** ONEPASS_MULTI: One-pass approach - any number of rows may be deleted.
*/
- pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 0, 0,
- WHERE_ONEPASS_DESIRED|WHERE_DUPLICATES_OK,
- iTabCur+1);
+ pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 0, 0, wcf, iTabCur+1);
if( pWInfo==0 ) goto delete_from_cleanup;
- okOnePass = sqlite3WhereOkOnePass(pWInfo, aiCurOnePass);
+ eOnePass = sqlite3WhereOkOnePass(pWInfo, aiCurOnePass);
+ assert( IsVirtual(pTab)==0 || eOnePass!=ONEPASS_MULTI );
+ assert( IsVirtual(pTab) || bComplex || eOnePass!=ONEPASS_OFF );
/* Keep track of the number of rows to be deleted */
if( db->flags & SQLITE_CountRows ){
@@ -95995,6 +102143,7 @@ SQLITE_PRIVATE void sqlite3DeleteFrom(
/* Extract the rowid or primary key for the current row */
if( pPk ){
for(i=0; i<nPk; i++){
+ assert( pPk->aiColumn[i]>=0 );
sqlite3ExprCodeGetColumnOfTable(v, pTab, iTabCur,
pPk->aiColumn[i], iPk+i);
}
@@ -96005,13 +102154,12 @@ SQLITE_PRIVATE void sqlite3DeleteFrom(
if( iKey>pParse->nMem ) pParse->nMem = iKey;
}
- if( okOnePass ){
- /* For ONEPASS, no need to store the rowid/primary-key. There is only
+ if( eOnePass!=ONEPASS_OFF ){
+ /* For ONEPASS, no need to store the rowid/primary-key. There is only
** one, so just keep it in its register(s) and fall through to the
- ** delete code.
- */
+ ** delete code. */
nKey = nPk; /* OP_Found will use an unpacked key */
- aToOpen = sqlite3DbMallocRaw(db, nIdx+2);
+ aToOpen = sqlite3DbMallocRawNN(db, nIdx+2);
if( aToOpen==0 ){
sqlite3WhereEnd(pWInfo);
goto delete_from_cleanup;
@@ -96021,27 +102169,27 @@ SQLITE_PRIVATE void sqlite3DeleteFrom(
if( aiCurOnePass[0]>=0 ) aToOpen[aiCurOnePass[0]-iTabCur] = 0;
if( aiCurOnePass[1]>=0 ) aToOpen[aiCurOnePass[1]-iTabCur] = 0;
if( addrEphOpen ) sqlite3VdbeChangeToNoop(v, addrEphOpen);
- addrDelete = sqlite3VdbeAddOp0(v, OP_Goto); /* Jump to DELETE logic */
- }else if( pPk ){
- /* Construct a composite key for the row to be deleted and remember it */
- iKey = ++pParse->nMem;
- nKey = 0; /* Zero tells OP_Found to use a composite key */
- sqlite3VdbeAddOp4(v, OP_MakeRecord, iPk, nPk, iKey,
- sqlite3IndexAffinityStr(v, pPk), nPk);
- sqlite3VdbeAddOp2(v, OP_IdxInsert, iEphCur, iKey);
}else{
- /* Get the rowid of the row to be deleted and remember it in the RowSet */
- nKey = 1; /* OP_Seek always uses a single rowid */
- sqlite3VdbeAddOp2(v, OP_RowSetAdd, iRowSet, iKey);
+ if( pPk ){
+ /* Add the PK key for this row to the temporary table */
+ iKey = ++pParse->nMem;
+ nKey = 0; /* Zero tells OP_Found to use a composite key */
+ sqlite3VdbeAddOp4(v, OP_MakeRecord, iPk, nPk, iKey,
+ sqlite3IndexAffinityStr(pParse->db, pPk), nPk);
+ sqlite3VdbeAddOp2(v, OP_IdxInsert, iEphCur, iKey);
+ }else{
+ /* Add the rowid of the row to be deleted to the RowSet */
+ nKey = 1; /* OP_Seek always uses a single rowid */
+ sqlite3VdbeAddOp2(v, OP_RowSetAdd, iRowSet, iKey);
+ }
}
- /* End of the WHERE loop */
- sqlite3WhereEnd(pWInfo);
- if( okOnePass ){
- /* Bypass the delete logic below if the WHERE loop found zero rows */
+ /* If this DELETE cannot use the ONEPASS strategy, this is the
+ ** end of the WHERE loop */
+ if( eOnePass!=ONEPASS_OFF ){
addrBypass = sqlite3VdbeMakeLabel(v);
- sqlite3VdbeAddOp2(v, OP_Goto, 0, addrBypass);
- sqlite3VdbeJumpHere(v, addrDelete);
+ }else{
+ sqlite3WhereEnd(pWInfo);
}
/* Unless this is a view, open cursors for the table we are
@@ -96050,21 +102198,24 @@ SQLITE_PRIVATE void sqlite3DeleteFrom(
** triggers.
*/
if( !isView ){
+ int iAddrOnce = 0;
+ if( eOnePass==ONEPASS_MULTI ){
+ iAddrOnce = sqlite3CodeOnce(pParse); VdbeCoverage(v);
+ }
testcase( IsVirtual(pTab) );
- sqlite3OpenTableAndIndices(pParse, pTab, OP_OpenWrite, iTabCur, aToOpen,
- &iDataCur, &iIdxCur);
+ sqlite3OpenTableAndIndices(pParse, pTab, OP_OpenWrite, OPFLAG_FORDELETE,
+ iTabCur, aToOpen, &iDataCur, &iIdxCur);
assert( pPk || IsVirtual(pTab) || iDataCur==iTabCur );
assert( pPk || IsVirtual(pTab) || iIdxCur==iDataCur+1 );
+ if( eOnePass==ONEPASS_MULTI ) sqlite3VdbeJumpHere(v, iAddrOnce);
}
/* Set up a loop over the rowids/primary-keys that were found in the
** where-clause loop above.
*/
- if( okOnePass ){
- /* Just one row. Hence the top-of-loop is a no-op */
+ if( eOnePass!=ONEPASS_OFF ){
assert( nKey==nPk ); /* OP_Found will use an unpacked key */
- assert( !IsVirtual(pTab) );
- if( aToOpen[iDataCur-iTabCur] ){
+ if( !IsVirtual(pTab) && aToOpen[iDataCur-iTabCur] ){
assert( pPk!=0 || pTab->pSelect!=0 );
sqlite3VdbeAddOp4Int(v, OP_NotFound, iDataCur, addrBypass, iKey, nKey);
VdbeCoverage(v);
@@ -96086,23 +102237,32 @@ SQLITE_PRIVATE void sqlite3DeleteFrom(
sqlite3VtabMakeWritable(pParse, pTab);
sqlite3VdbeAddOp4(v, OP_VUpdate, 0, 1, iKey, pVTab, P4_VTAB);
sqlite3VdbeChangeP5(v, OE_Abort);
+ assert( eOnePass==ONEPASS_OFF || eOnePass==ONEPASS_SINGLE );
sqlite3MayAbort(pParse);
+ if( eOnePass==ONEPASS_SINGLE && sqlite3IsToplevel(pParse) ){
+ pParse->isMultiWrite = 0;
+ }
}else
#endif
{
int count = (pParse->nested==0); /* True to count changes */
+ int iIdxNoSeek = -1;
+ if( bComplex==0 && aiCurOnePass[1]!=iDataCur ){
+ iIdxNoSeek = aiCurOnePass[1];
+ }
sqlite3GenerateRowDelete(pParse, pTab, pTrigger, iDataCur, iIdxCur,
- iKey, nKey, count, OE_Default, okOnePass);
+ iKey, nKey, count, OE_Default, eOnePass, iIdxNoSeek);
}
/* End of the loop over all rowids/primary-keys. */
- if( okOnePass ){
+ if( eOnePass!=ONEPASS_OFF ){
sqlite3VdbeResolveLabel(v, addrBypass);
+ sqlite3WhereEnd(pWInfo);
}else if( pPk ){
sqlite3VdbeAddOp2(v, OP_Next, iEphCur, addrLoop+1); VdbeCoverage(v);
sqlite3VdbeJumpHere(v, addrLoop);
}else{
- sqlite3VdbeAddOp2(v, OP_Goto, 0, addrLoop);
+ sqlite3VdbeGoto(v, addrLoop);
sqlite3VdbeJumpHere(v, addrLoop);
}
@@ -96169,6 +102329,25 @@ delete_from_cleanup:
** sequence of nPk memory cells starting at iPk. If nPk==0 that means
** that a search record formed from OP_MakeRecord is contained in the
** single memory location iPk.
+**
+** eMode:
+** Parameter eMode may be passed either ONEPASS_OFF (0), ONEPASS_SINGLE, or
+** ONEPASS_MULTI. If eMode is not ONEPASS_OFF, then the cursor
+** iDataCur already points to the row to delete. If eMode is ONEPASS_OFF
+** then this function must seek iDataCur to the entry identified by iPk
+** and nPk before reading from it.
+**
+** If eMode is ONEPASS_MULTI, then this call is being made as part
+** of a ONEPASS delete that affects multiple rows. In this case, if
+** iIdxNoSeek is a valid cursor number (>=0), then its position should
+** be preserved following the delete operation. Or, if iIdxNoSeek is not
+** a valid cursor number, the position of iDataCur should be preserved
+** instead.
+**
+** iIdxNoSeek:
+** If iIdxNoSeek is a valid cursor number (>=0), then it identifies an
+** index cursor (from within array of cursors starting at iIdxCur) that
+** already points to the index entry to be deleted.
*/
SQLITE_PRIVATE void sqlite3GenerateRowDelete(
Parse *pParse, /* Parsing context */
@@ -96180,7 +102359,8 @@ SQLITE_PRIVATE void sqlite3GenerateRowDelete(
i16 nPk, /* Number of PRIMARY KEY memory cells */
u8 count, /* If non-zero, increment the row change counter */
u8 onconf, /* Default ON CONFLICT policy for triggers */
- u8 bNoSeek /* iDataCur is already pointing to the row to delete */
+ u8 eMode, /* ONEPASS_OFF, _SINGLE, or _MULTI. See above */
+ int iIdxNoSeek /* Cursor number of cursor that does not need seeking */
){
Vdbe *v = pParse->pVdbe; /* Vdbe */
int iOld = 0; /* First register in OLD.* array */
@@ -96197,7 +102377,7 @@ SQLITE_PRIVATE void sqlite3GenerateRowDelete(
** not attempt to delete it or fire any DELETE triggers. */
iLabel = sqlite3VdbeMakeLabel(v);
opSeek = HasRowid(pTab) ? OP_NotExists : OP_NotFound;
- if( !bNoSeek ){
+ if( eMode==ONEPASS_OFF ){
sqlite3VdbeAddOp4Int(v, opSeek, iDataCur, iLabel, iPk, nPk);
VdbeCoverageIf(v, opSeek==OP_NotExists);
VdbeCoverageIf(v, opSeek==OP_NotFound);
@@ -96255,13 +102435,27 @@ SQLITE_PRIVATE void sqlite3GenerateRowDelete(
/* Delete the index and table entries. Skip this step if pTab is really
** a view (in which case the only effect of the DELETE statement is to
- ** fire the INSTEAD OF triggers). */
+ ** fire the INSTEAD OF triggers).
+ **
+ ** If variable 'count' is non-zero, then this OP_Delete instruction should
+ ** invoke the update-hook. The pre-update-hook, on the other hand should
+ ** be invoked unless table pTab is a system table. The difference is that
+ ** the update-hook is not invoked for rows removed by REPLACE, but the
+ ** pre-update-hook is.
+ */
if( pTab->pSelect==0 ){
- sqlite3GenerateRowIndexDelete(pParse, pTab, iDataCur, iIdxCur, 0);
+ u8 p5 = 0;
+ sqlite3GenerateRowIndexDelete(pParse, pTab, iDataCur, iIdxCur,0,iIdxNoSeek);
sqlite3VdbeAddOp2(v, OP_Delete, iDataCur, (count?OPFLAG_NCHANGE:0));
- if( count ){
- sqlite3VdbeChangeP4(v, -1, pTab->zName, P4_TRANSIENT);
+ sqlite3VdbeChangeP4(v, -1, (char*)pTab, P4_TABLE);
+ if( eMode!=ONEPASS_OFF ){
+ sqlite3VdbeChangeP5(v, OPFLAG_AUXDELETE);
}
+ if( iIdxNoSeek>=0 ){
+ sqlite3VdbeAddOp1(v, OP_Delete, iIdxNoSeek);
+ }
+ if( eMode==ONEPASS_MULTI ) p5 |= OPFLAG_SAVEPOSITION;
+ sqlite3VdbeChangeP5(v, p5);
}
/* Do any ON CASCADE, SET NULL or SET DEFAULT operations required to
@@ -96304,7 +102498,8 @@ SQLITE_PRIVATE void sqlite3GenerateRowIndexDelete(
Table *pTab, /* Table containing the row to be deleted */
int iDataCur, /* Cursor of table holding data. */
int iIdxCur, /* First index cursor */
- int *aRegIdx /* Only delete if aRegIdx!=0 && aRegIdx[i]>0 */
+ int *aRegIdx, /* Only delete if aRegIdx!=0 && aRegIdx[i]>0 */
+ int iIdxNoSeek /* Do not delete from this cursor */
){
int i; /* Index loop counter */
int r1 = -1; /* Register holding an index key */
@@ -96320,11 +102515,12 @@ SQLITE_PRIVATE void sqlite3GenerateRowIndexDelete(
assert( iIdxCur+i!=iDataCur || pPk==pIdx );
if( aRegIdx!=0 && aRegIdx[i]==0 ) continue;
if( pIdx==pPk ) continue;
+ if( iIdxCur+i==iIdxNoSeek ) continue;
VdbeModuleComment((v, "GenRowIdxDel for %s", pIdx->zName));
r1 = sqlite3GenerateIndexKey(pParse, pIdx, iDataCur, 0, 1,
- &iPartIdxLabel, pPrior, r1);
+ &iPartIdxLabel, pPrior, r1);
sqlite3VdbeAddOp3(v, OP_IdxDelete, iIdxCur+i, r1,
- pIdx->uniqNotNull ? pIdx->nKeyCol : pIdx->nColumn);
+ pIdx->uniqNotNull ? pIdx->nKeyCol : pIdx->nColumn);
sqlite3ResolvePartIdxLabel(pParse, iPartIdxLabel);
pPrior = pIdx;
}
@@ -96373,17 +102569,16 @@ SQLITE_PRIVATE int sqlite3GenerateIndexKey(
){
Vdbe *v = pParse->pVdbe;
int j;
- Table *pTab = pIdx->pTable;
int regBase;
int nCol;
if( piPartIdxLabel ){
if( pIdx->pPartIdxWhere ){
*piPartIdxLabel = sqlite3VdbeMakeLabel(v);
- pParse->iPartIdxTab = iDataCur;
+ pParse->iSelfTab = iDataCur;
sqlite3ExprCachePush(pParse);
- sqlite3ExprIfFalse(pParse, pIdx->pPartIdxWhere, *piPartIdxLabel,
- SQLITE_JUMPIFNULL);
+ sqlite3ExprIfFalseDup(pParse, pIdx->pPartIdxWhere, *piPartIdxLabel,
+ SQLITE_JUMPIFNULL);
}else{
*piPartIdxLabel = 0;
}
@@ -96392,9 +102587,14 @@ SQLITE_PRIVATE int sqlite3GenerateIndexKey(
regBase = sqlite3GetTempRange(pParse, nCol);
if( pPrior && (regBase!=regPrior || pPrior->pPartIdxWhere) ) pPrior = 0;
for(j=0; j<nCol; j++){
- if( pPrior && pPrior->aiColumn[j]==pIdx->aiColumn[j] ) continue;
- sqlite3ExprCodeGetColumnOfTable(v, pTab, iDataCur, pIdx->aiColumn[j],
- regBase+j);
+ if( pPrior
+ && pPrior->aiColumn[j]==pIdx->aiColumn[j]
+ && pPrior->aiColumn[j]!=XN_EXPR
+ ){
+ /* This column was already computed by the previous index */
+ continue;
+ }
+ sqlite3ExprCodeLoadIndexColumn(pParse, pIdx, iDataCur, j, regBase+j);
/* If the column affinity is REAL but the number is an integer, then it
** might be stored in the table as an integer (using a compact
** representation) then converted to REAL by an OP_RealAffinity opcode.
@@ -96439,8 +102639,10 @@ SQLITE_PRIVATE void sqlite3ResolvePartIdxLabel(Parse *pParse, int iLabel){
** functions of SQLite. (Some function, and in particular the date and
** time functions, are implemented separately.)
*/
+/* #include "sqliteInt.h" */
/* #include <stdlib.h> */
/* #include <assert.h> */
+/* #include "vdbeInt.h" */
/*
** Return the collating function associated with a function.
@@ -96663,7 +102865,8 @@ static void printfFunc(
x.nUsed = 0;
x.apArg = argv+1;
sqlite3StrAccumInit(&str, db, 0, 0, db->aLimit[SQLITE_LIMIT_LENGTH]);
- sqlite3XPrintf(&str, SQLITE_PRINTF_SQLFUNC, zFormat, &x);
+ str.printfFlags = SQLITE_PRINTF_SQLFUNC;
+ sqlite3XPrintf(&str, zFormat, &x);
n = str.nChar;
sqlite3_result_text(context, sqlite3StrAccumFinish(&str), n,
SQLITE_DYNAMIC);
@@ -96991,25 +103194,23 @@ static void total_changes(
** A structure defining how to do GLOB-style comparisons.
*/
struct compareInfo {
- u8 matchAll;
- u8 matchOne;
- u8 matchSet;
- u8 noCase;
+ u8 matchAll; /* "*" or "%" */
+ u8 matchOne; /* "?" or "_" */
+ u8 matchSet; /* "[" or 0 */
+ u8 noCase; /* true to ignore case differences */
};
/*
** For LIKE and GLOB matching on EBCDIC machines, assume that every
-** character is exactly one byte in size. Also, all characters are
-** able to participate in upper-case-to-lower-case mappings in EBCDIC
-** whereas only characters less than 0x80 do in ASCII.
+** character is exactly one byte in size. Also, provde the Utf8Read()
+** macro for fast reading of the next character in the common case where
+** the next character is ASCII.
*/
#if defined(SQLITE_EBCDIC)
# define sqlite3Utf8Read(A) (*((*A)++))
-# define GlobUpperToLower(A) A = sqlite3UpperToLower[A]
-# define GlobUpperToLowerAscii(A) A = sqlite3UpperToLower[A]
+# define Utf8Read(A) (*(A++))
#else
-# define GlobUpperToLower(A) if( A<=0x7f ){ A = sqlite3UpperToLower[A]; }
-# define GlobUpperToLowerAscii(A) A = sqlite3UpperToLower[A]
+# define Utf8Read(A) (A[0]<0x80?*(A++):sqlite3Utf8Read(&A))
#endif
static const struct compareInfo globInfo = { '*', '?', '[', 0 };
@@ -97051,7 +103252,7 @@ static const struct compareInfo likeInfoAlt = { '%', '_', 0, 0 };
** Ec Where E is the "esc" character and c is any other
** character, including '%', '_', and esc, match exactly c.
**
-** The comments through this routine usually assume glob matching.
+** The comments within this routine usually assume glob matching.
**
** This routine is usually quick, but can be N**2 in the worst case.
*/
@@ -97059,29 +103260,20 @@ static int patternCompare(
const u8 *zPattern, /* The glob pattern */
const u8 *zString, /* The string to compare against the glob */
const struct compareInfo *pInfo, /* Information about how to do the compare */
- u32 esc /* The escape character */
+ u32 matchOther /* The escape char (LIKE) or '[' (GLOB) */
){
u32 c, c2; /* Next pattern and input string chars */
u32 matchOne = pInfo->matchOne; /* "?" or "_" */
u32 matchAll = pInfo->matchAll; /* "*" or "%" */
- u32 matchOther; /* "[" or the escape character */
u8 noCase = pInfo->noCase; /* True if uppercase==lowercase */
const u8 *zEscaped = 0; /* One past the last escaped input char */
- /* The GLOB operator does not have an ESCAPE clause. And LIKE does not
- ** have the matchSet operator. So we either have to look for one or
- ** the other, never both. Hence the single variable matchOther is used
- ** to store the one we have to look for.
- */
- matchOther = esc ? esc : pInfo->matchSet;
-
- while( (c = sqlite3Utf8Read(&zPattern))!=0 ){
+ while( (c = Utf8Read(zPattern))!=0 ){
if( c==matchAll ){ /* Match "*" */
/* Skip over multiple "*" characters in the pattern. If there
** are also "?" characters, skip those as well, but consume a
** single character of the input string for each "?" skipped */
- while( (c=sqlite3Utf8Read(&zPattern)) == matchAll
- || c == matchOne ){
+ while( (c=Utf8Read(zPattern)) == matchAll || c == matchOne ){
if( c==matchOne && sqlite3Utf8Read(&zString)==0 ){
return 0;
}
@@ -97089,7 +103281,7 @@ static int patternCompare(
if( c==0 ){
return 1; /* "*" at the end of the pattern matches */
}else if( c==matchOther ){
- if( esc ){
+ if( pInfo->matchSet==0 ){
c = sqlite3Utf8Read(&zPattern);
if( c==0 ) return 0;
}else{
@@ -97097,7 +103289,7 @@ static int patternCompare(
** recursive search in this case, but it is an unusual case. */
assert( matchOther<0x80 ); /* '[' is a single-byte character */
while( *zString
- && patternCompare(&zPattern[-1],zString,pInfo,esc)==0 ){
+ && patternCompare(&zPattern[-1],zString,pInfo,matchOther)==0 ){
SQLITE_SKIP_UTF8(zString);
}
return *zString!=0;
@@ -97123,18 +103315,18 @@ static int patternCompare(
}
while( (c2 = *(zString++))!=0 ){
if( c2!=c && c2!=cx ) continue;
- if( patternCompare(zPattern,zString,pInfo,esc) ) return 1;
+ if( patternCompare(zPattern,zString,pInfo,matchOther) ) return 1;
}
}else{
- while( (c2 = sqlite3Utf8Read(&zString))!=0 ){
+ while( (c2 = Utf8Read(zString))!=0 ){
if( c2!=c ) continue;
- if( patternCompare(zPattern,zString,pInfo,esc) ) return 1;
+ if( patternCompare(zPattern,zString,pInfo,matchOther) ) return 1;
}
}
return 0;
}
if( c==matchOther ){
- if( esc ){
+ if( pInfo->matchSet==0 ){
c = sqlite3Utf8Read(&zPattern);
if( c==0 ) return 0;
zEscaped = zPattern;
@@ -97172,7 +103364,7 @@ static int patternCompare(
continue;
}
}
- c2 = sqlite3Utf8Read(&zString);
+ c2 = Utf8Read(zString);
if( c==c2 ) continue;
if( noCase && c<0x80 && c2<0x80 && sqlite3Tolower(c)==sqlite3Tolower(c2) ){
continue;
@@ -97187,7 +103379,14 @@ static int patternCompare(
** The sqlite3_strglob() interface.
*/
SQLITE_API int SQLITE_STDCALL sqlite3_strglob(const char *zGlobPattern, const char *zString){
- return patternCompare((u8*)zGlobPattern, (u8*)zString, &globInfo, 0)==0;
+ return patternCompare((u8*)zGlobPattern, (u8*)zString, &globInfo, '[')==0;
+}
+
+/*
+** The sqlite3_strlike() interface.
+*/
+SQLITE_API int SQLITE_STDCALL sqlite3_strlike(const char *zPattern, const char *zStr, unsigned int esc){
+ return patternCompare((u8*)zPattern, (u8*)zStr, &likeInfoNorm, esc)==0;
}
/*
@@ -97218,10 +103417,22 @@ static void likeFunc(
sqlite3_value **argv
){
const unsigned char *zA, *zB;
- u32 escape = 0;
+ u32 escape;
int nPat;
sqlite3 *db = sqlite3_context_db_handle(context);
+ struct compareInfo *pInfo = sqlite3_user_data(context);
+#ifdef SQLITE_LIKE_DOESNT_MATCH_BLOBS
+ if( sqlite3_value_type(argv[0])==SQLITE_BLOB
+ || sqlite3_value_type(argv[1])==SQLITE_BLOB
+ ){
+#ifdef SQLITE_TEST
+ sqlite3_like_count++;
+#endif
+ sqlite3_result_int(context, 0);
+ return;
+ }
+#endif
zB = sqlite3_value_text(argv[0]);
zA = sqlite3_value_text(argv[1]);
@@ -97249,13 +103460,13 @@ static void likeFunc(
return;
}
escape = sqlite3Utf8Read(&zEsc);
+ }else{
+ escape = pInfo->matchSet;
}
if( zA && zB ){
- struct compareInfo *pInfo = sqlite3_user_data(context);
#ifdef SQLITE_TEST
sqlite3_like_count++;
#endif
-
sqlite3_result_int(context, patternCompare(zB, zA, pInfo, escape));
}
}
@@ -97549,16 +103760,14 @@ static void zeroblobFunc(
sqlite3_value **argv
){
i64 n;
- sqlite3 *db = sqlite3_context_db_handle(context);
+ int rc;
assert( argc==1 );
UNUSED_PARAMETER(argc);
n = sqlite3_value_int64(argv[0]);
- testcase( n==db->aLimit[SQLITE_LIMIT_LENGTH] );
- testcase( n==db->aLimit[SQLITE_LIMIT_LENGTH]+1 );
- if( n>db->aLimit[SQLITE_LIMIT_LENGTH] ){
- sqlite3_result_error_toobig(context);
- }else{
- sqlite3_result_zeroblob(context, (int)n); /* IMP: R-00293-64994 */
+ if( n<0 ) n = 0;
+ rc = sqlite3_result_zeroblob64(context, n); /* IMP: R-00293-64994 */
+ if( rc ){
+ sqlite3_result_error_code(context, rc);
}
}
@@ -97803,6 +104012,14 @@ static void loadExt(sqlite3_context *context, int argc, sqlite3_value **argv){
sqlite3 *db = sqlite3_context_db_handle(context);
char *zErrMsg = 0;
+ /* Disallow the load_extension() SQL function unless the SQLITE_LoadExtFunc
+ ** flag is set. See the sqlite3_enable_load_extension() API.
+ */
+ if( (db->flags & SQLITE_LoadExtFunc)==0 ){
+ sqlite3_result_error(context, "not authorized", -1);
+ return;
+ }
+
if( argc==2 ){
zProc = (const char *)sqlite3_value_text(argv[1]);
}else{
@@ -98028,11 +104245,11 @@ static void groupConcatFinalize(sqlite3_context *context){
** of the built-in functions above are part of the global function set.
** This routine only deals with those that are not global.
*/
-SQLITE_PRIVATE void sqlite3RegisterBuiltinFunctions(sqlite3 *db){
+SQLITE_PRIVATE void sqlite3RegisterPerConnectionBuiltinFunctions(sqlite3 *db){
int rc = sqlite3_overload_function(db, "MATCH", 2);
assert( rc==SQLITE_NOMEM || rc==SQLITE_OK );
if( rc==SQLITE_NOMEM ){
- db->mallocFailed = 1;
+ sqlite3OomFault(db);
}
}
@@ -98041,8 +104258,7 @@ SQLITE_PRIVATE void sqlite3RegisterBuiltinFunctions(sqlite3 *db){
*/
static void setLikeOptFlag(sqlite3 *db, const char *zName, u8 flagVal){
FuncDef *pDef;
- pDef = sqlite3FindFunction(db, zName, sqlite3Strlen30(zName),
- 2, SQLITE_UTF8, 0);
+ pDef = sqlite3FindFunction(db, zName, 2, SQLITE_UTF8, 0);
if( ALWAYS(pDef) ){
pDef->funcFlags |= flagVal;
}
@@ -98090,9 +104306,7 @@ SQLITE_PRIVATE int sqlite3IsLikeFunction(sqlite3 *db, Expr *pExpr, int *pIsNocas
return 0;
}
assert( !ExprHasProperty(pExpr, EP_xIsSelect) );
- pDef = sqlite3FindFunction(db, pExpr->u.zToken,
- sqlite3Strlen30(pExpr->u.zToken),
- 2, SQLITE_UTF8, 0);
+ pDef = sqlite3FindFunction(db, pExpr->u.zToken, 2, SQLITE_UTF8, 0);
if( NEVER(pDef==0) || (pDef->funcFlags & SQLITE_FUNC_LIKE)==0 ){
return 0;
}
@@ -98116,7 +104330,7 @@ SQLITE_PRIVATE int sqlite3IsLikeFunction(sqlite3 *db, Expr *pExpr, int *pIsNocas
**
** After this routine runs
*/
-SQLITE_PRIVATE void sqlite3RegisterGlobalFunctions(void){
+SQLITE_PRIVATE void sqlite3RegisterBuiltinFunctions(void){
/*
** The following array holds FuncDef structures for all of the functions
** defined in this file.
@@ -98124,8 +104338,27 @@ SQLITE_PRIVATE void sqlite3RegisterGlobalFunctions(void){
** The array cannot be constant since changes are made to the
** FuncDef.pHash elements at start-time. The elements of this array
** are read-only after initialization is complete.
+ **
+ ** For peak efficiency, put the most frequently used function last.
*/
- static SQLITE_WSD FuncDef aBuiltinFunc[] = {
+ static FuncDef aBuiltinFunc[] = {
+#ifdef SQLITE_SOUNDEX
+ FUNCTION(soundex, 1, 0, 0, soundexFunc ),
+#endif
+#ifndef SQLITE_OMIT_LOAD_EXTENSION
+ VFUNCTION(load_extension, 1, 0, 0, loadExt ),
+ VFUNCTION(load_extension, 2, 0, 0, loadExt ),
+#endif
+#if SQLITE_USER_AUTHENTICATION
+ FUNCTION(sqlite_crypt, 2, 0, 0, sqlite3CryptFunc ),
+#endif
+#ifndef SQLITE_OMIT_COMPILEOPTION_DIAGS
+ DFUNCTION(sqlite_compileoption_used,1, 0, 0, compileoptionusedFunc ),
+ DFUNCTION(sqlite_compileoption_get, 1, 0, 0, compileoptiongetFunc ),
+#endif /* SQLITE_OMIT_COMPILEOPTION_DIAGS */
+ FUNCTION2(unlikely, 1, 0, 0, noopFunc, SQLITE_FUNC_UNLIKELY),
+ FUNCTION2(likelihood, 2, 0, 0, noopFunc, SQLITE_FUNC_UNLIKELY),
+ FUNCTION2(likely, 1, 0, 0, noopFunc, SQLITE_FUNC_UNLIKELY),
FUNCTION(ltrim, 1, 1, 0, trimFunc ),
FUNCTION(ltrim, 2, 1, 0, trimFunc ),
FUNCTION(rtrim, 1, 2, 0, trimFunc ),
@@ -98143,8 +104376,6 @@ SQLITE_PRIVATE void sqlite3RegisterGlobalFunctions(void){
FUNCTION2(typeof, 1, 0, 0, typeofFunc, SQLITE_FUNC_TYPEOF),
FUNCTION2(length, 1, 0, 0, lengthFunc, SQLITE_FUNC_LENGTH),
FUNCTION(instr, 2, 0, 0, instrFunc ),
- FUNCTION(substr, 2, 0, 0, substrFunc ),
- FUNCTION(substr, 3, 0, 0, substrFunc ),
FUNCTION(printf, -1, 0, 0, printfFunc ),
FUNCTION(unicode, 1, 0, 0, unicodeFunc ),
FUNCTION(char, -1, 0, 0, charFunc ),
@@ -98155,40 +104386,22 @@ SQLITE_PRIVATE void sqlite3RegisterGlobalFunctions(void){
#endif
FUNCTION(upper, 1, 0, 0, upperFunc ),
FUNCTION(lower, 1, 0, 0, lowerFunc ),
- FUNCTION(coalesce, 1, 0, 0, 0 ),
- FUNCTION(coalesce, 0, 0, 0, 0 ),
- FUNCTION2(coalesce, -1, 0, 0, noopFunc, SQLITE_FUNC_COALESCE),
FUNCTION(hex, 1, 0, 0, hexFunc ),
FUNCTION2(ifnull, 2, 0, 0, noopFunc, SQLITE_FUNC_COALESCE),
- FUNCTION2(unlikely, 1, 0, 0, noopFunc, SQLITE_FUNC_UNLIKELY),
- FUNCTION2(likelihood, 2, 0, 0, noopFunc, SQLITE_FUNC_UNLIKELY),
- FUNCTION2(likely, 1, 0, 0, noopFunc, SQLITE_FUNC_UNLIKELY),
VFUNCTION(random, 0, 0, 0, randomFunc ),
VFUNCTION(randomblob, 1, 0, 0, randomBlob ),
FUNCTION(nullif, 2, 0, 1, nullifFunc ),
- FUNCTION(sqlite_version, 0, 0, 0, versionFunc ),
- FUNCTION(sqlite_source_id, 0, 0, 0, sourceidFunc ),
+ DFUNCTION(sqlite_version, 0, 0, 0, versionFunc ),
+ DFUNCTION(sqlite_source_id, 0, 0, 0, sourceidFunc ),
FUNCTION(sqlite_log, 2, 0, 0, errlogFunc ),
-#if SQLITE_USER_AUTHENTICATION
- FUNCTION(sqlite_crypt, 2, 0, 0, sqlite3CryptFunc ),
-#endif
-#ifndef SQLITE_OMIT_COMPILEOPTION_DIAGS
- FUNCTION(sqlite_compileoption_used,1, 0, 0, compileoptionusedFunc ),
- FUNCTION(sqlite_compileoption_get, 1, 0, 0, compileoptiongetFunc ),
-#endif /* SQLITE_OMIT_COMPILEOPTION_DIAGS */
FUNCTION(quote, 1, 0, 0, quoteFunc ),
VFUNCTION(last_insert_rowid, 0, 0, 0, last_insert_rowid),
VFUNCTION(changes, 0, 0, 0, changes ),
VFUNCTION(total_changes, 0, 0, 0, total_changes ),
FUNCTION(replace, 3, 0, 0, replaceFunc ),
FUNCTION(zeroblob, 1, 0, 0, zeroblobFunc ),
- #ifdef SQLITE_SOUNDEX
- FUNCTION(soundex, 1, 0, 0, soundexFunc ),
- #endif
- #ifndef SQLITE_OMIT_LOAD_EXTENSION
- FUNCTION(load_extension, 1, 0, 0, loadExt ),
- FUNCTION(load_extension, 2, 0, 0, loadExt ),
- #endif
+ FUNCTION(substr, 2, 0, 0, substrFunc ),
+ FUNCTION(substr, 3, 0, 0, substrFunc ),
AGGREGATE(sum, 1, 0, 0, sumStep, sumFinalize ),
AGGREGATE(total, 1, 0, 0, sumStep, totalFinalize ),
AGGREGATE(avg, 1, 0, 0, sumStep, avgFinalize ),
@@ -98206,22 +104419,34 @@ SQLITE_PRIVATE void sqlite3RegisterGlobalFunctions(void){
LIKEFUNC(like, 2, &likeInfoNorm, SQLITE_FUNC_LIKE),
LIKEFUNC(like, 3, &likeInfoNorm, SQLITE_FUNC_LIKE),
#endif
+ FUNCTION(coalesce, 1, 0, 0, 0 ),
+ FUNCTION(coalesce, 0, 0, 0, 0 ),
+ FUNCTION2(coalesce, -1, 0, 0, noopFunc, SQLITE_FUNC_COALESCE),
};
-
- int i;
- FuncDefHash *pHash = &GLOBAL(FuncDefHash, sqlite3GlobalFunctions);
- FuncDef *aFunc = (FuncDef*)&GLOBAL(FuncDef, aBuiltinFunc);
-
- for(i=0; i<ArraySize(aBuiltinFunc); i++){
- sqlite3FuncDefInsert(pHash, &aFunc[i]);
- }
- sqlite3RegisterDateTimeFunctions();
#ifndef SQLITE_OMIT_ALTERTABLE
sqlite3AlterFunctions();
#endif
#if defined(SQLITE_ENABLE_STAT3) || defined(SQLITE_ENABLE_STAT4)
sqlite3AnalyzeFunctions();
#endif
+ sqlite3RegisterDateTimeFunctions();
+ sqlite3InsertBuiltinFuncs(aBuiltinFunc, ArraySize(aBuiltinFunc));
+
+#if 0 /* Enable to print out how the built-in functions are hashed */
+ {
+ int i;
+ FuncDef *p;
+ for(i=0; i<SQLITE_FUNC_HASH_SZ; i++){
+ printf("FUNC-HASH %02d:", i);
+ for(p=sqlite3BuiltinFunctions.a[i]; p; p=p->u.pHash){
+ int n = sqlite3Strlen30(p->zName);
+ int h = p->zName[0] + n;
+ printf(" %s(%d)", p->zName, h);
+ }
+ printf("\n");
+ }
+ }
+#endif
}
/************** End of func.c ************************************************/
@@ -98239,6 +104464,7 @@ SQLITE_PRIVATE void sqlite3RegisterGlobalFunctions(void){
** This file contains code used by the compiler to add foreign key
** support to compiled SQL statements.
*/
+/* #include "sqliteInt.h" */
#ifndef SQLITE_OMIT_FOREIGN_KEY
#ifndef SQLITE_OMIT_TRIGGER
@@ -98446,7 +104672,7 @@ SQLITE_PRIVATE int sqlite3FkLocateIndex(
}
}else if( paiCol ){
assert( nCol>1 );
- aiCol = (int *)sqlite3DbMallocRaw(pParse->db, nCol*sizeof(int));
+ aiCol = (int *)sqlite3DbMallocRawNN(pParse->db, nCol*sizeof(int));
if( !aiCol ) return 1;
*paiCol = aiCol;
}
@@ -98476,16 +104702,16 @@ SQLITE_PRIVATE int sqlite3FkLocateIndex(
int i, j;
for(i=0; i<nCol; i++){
i16 iCol = pIdx->aiColumn[i]; /* Index of column in parent tbl */
- char *zDfltColl; /* Def. collation for column */
+ const char *zDfltColl; /* Def. collation for column */
char *zIdxCol; /* Name of indexed column */
+ if( iCol<0 ) break; /* No foreign keys against expression indexes */
+
/* If the index uses a collation sequence that is different from
** the default collation sequence for the column, this index is
** unusable. Bail out early in this case. */
zDfltColl = pParent->aCol[iCol].zColl;
- if( !zDfltColl ){
- zDfltColl = "BINARY";
- }
+ if( !zDfltColl ) zDfltColl = sqlite3StrBINARY;
if( sqlite3StrICmp(pIdx->azColl[i], zDfltColl) ) break;
zIdxCol = pParent->aCol[iCol].zName;
@@ -98601,7 +104827,7 @@ static void fkLookupParent(
sqlite3OpenTable(pParse, iCur, iDb, pTab, OP_OpenRead);
sqlite3VdbeAddOp3(v, OP_NotExists, iCur, 0, regTemp); VdbeCoverage(v);
- sqlite3VdbeAddOp2(v, OP_Goto, 0, iOk);
+ sqlite3VdbeGoto(v, iOk);
sqlite3VdbeJumpHere(v, sqlite3VdbeCurrentAddr(v)-2);
sqlite3VdbeJumpHere(v, iMustBeInt);
sqlite3ReleaseTempReg(pParse, regTemp);
@@ -98631,6 +104857,7 @@ static void fkLookupParent(
for(i=0; i<nCol; i++){
int iChild = aiCol[i]+1+regData;
int iParent = pIdx->aiColumn[i]+1+regData;
+ assert( pIdx->aiColumn[i]>=0 );
assert( aiCol[i]!=pTab->iPKey );
if( pIdx->aiColumn[i]==pTab->iPKey ){
/* The parent key is a composite key that includes the IPK column */
@@ -98639,11 +104866,11 @@ static void fkLookupParent(
sqlite3VdbeAddOp3(v, OP_Ne, iChild, iJump, iParent); VdbeCoverage(v);
sqlite3VdbeChangeP5(v, SQLITE_JUMPIFNULL);
}
- sqlite3VdbeAddOp2(v, OP_Goto, 0, iOk);
+ sqlite3VdbeGoto(v, iOk);
}
sqlite3VdbeAddOp4(v, OP_MakeRecord, regTemp, nCol, regRec,
- sqlite3IndexAffinityStr(v,pIdx), nCol);
+ sqlite3IndexAffinityStr(pParse->db,pIdx), nCol);
sqlite3VdbeAddOp4Int(v, OP_Found, iCur, iOk, regRec, 0); VdbeCoverage(v);
sqlite3ReleaseTempReg(pParse, regRec);
@@ -98839,6 +105066,7 @@ static void fkScanChildren(
assert( pIdx!=0 );
for(i=0; i<pPk->nKeyCol; i++){
i16 iCol = pIdx->aiColumn[i];
+ assert( iCol>=0 );
pLeft = exprTableRegister(pParse, pTab, regData, iCol);
pRight = exprTableColumn(db, pTab, pSrc->a[0].iCursor, iCol);
pEq = sqlite3PExpr(pParse, TK_EQ, pLeft, pRight, 0);
@@ -99158,6 +105386,7 @@ SQLITE_PRIVATE void sqlite3FkCheck(
if( aiCol[i]==pTab->iPKey ){
aiCol[i] = -1;
}
+ assert( pIdx==0 || pIdx->aiColumn[i]>=0 );
#ifndef SQLITE_OMIT_AUTHORIZATION
/* Request permission to read the parent key columns. If the
** authorization callback returns SQLITE_IGNORE, behave as if any
@@ -99289,7 +105518,10 @@ SQLITE_PRIVATE u32 sqlite3FkOldmask(
Index *pIdx = 0;
sqlite3FkLocateIndex(pParse, pTab, p, &pIdx, 0);
if( pIdx ){
- for(i=0; i<pIdx->nKeyCol; i++) mask |= COLUMN_MASK(pIdx->aiColumn[i]);
+ for(i=0; i<pIdx->nKeyCol; i++){
+ assert( pIdx->aiColumn[i]>=0 );
+ mask |= COLUMN_MASK(pIdx->aiColumn[i]);
+ }
}
}
}
@@ -99383,10 +105615,12 @@ static Trigger *fkActionTrigger(
int iAction = (pChanges!=0); /* 1 for UPDATE, 0 for DELETE */
action = pFKey->aAction[iAction];
+ if( action==OE_Restrict && (db->flags & SQLITE_DeferFKs) ){
+ return 0;
+ }
pTrigger = pFKey->apTrigger[iAction];
if( action!=OE_None && !pTrigger ){
- u8 enableLookaside; /* Copy of db->lookaside.bEnabled */
char const *zFrom; /* Name of child table */
int nFrom; /* Length in bytes of zFrom */
Index *pIdx = 0; /* Parent key index for this FK */
@@ -99412,11 +105646,10 @@ static Trigger *fkActionTrigger(
iFromCol = aiCol ? aiCol[i] : pFKey->aCol[0].iFrom;
assert( iFromCol>=0 );
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);
- tFromCol.n = sqlite3Strlen30(tFromCol.z);
+ assert( pIdx==0 || pIdx->aiColumn[i]>=0 );
+ sqlite3TokenInit(&tToCol,
+ pTab->aCol[pIdx ? pIdx->aiColumn[i] : pTab->iPKey].zName);
+ sqlite3TokenInit(&tFromCol, pFKey->pFrom->aCol[iFromCol].zName);
/* Create the expression "OLD.zToCol = zFromCol". It is important
** that the "OLD.zToCol" term is on the LHS of the = operator, so
@@ -99496,8 +105729,7 @@ static Trigger *fkActionTrigger(
}
/* Disable lookaside memory allocation */
- enableLookaside = db->lookaside.bEnabled;
- db->lookaside.bEnabled = 0;
+ db->lookaside.bDisable++;
pTrigger = (Trigger *)sqlite3DbMallocZero(db,
sizeof(Trigger) + /* struct Trigger */
@@ -99519,7 +105751,7 @@ static Trigger *fkActionTrigger(
}
/* Re-enable the lookaside buffer, if it was disabled earlier. */
- db->lookaside.bEnabled = enableLookaside;
+ db->lookaside.bDisable--;
sqlite3ExprDelete(db, pWhere);
sqlite3ExprDelete(db, pWhen);
@@ -99643,6 +105875,7 @@ SQLITE_PRIVATE void sqlite3FkDelete(sqlite3 *db, Table *pTab){
** This file contains C code routines that are called by the parser
** to handle INSERT statements in SQLite.
*/
+/* #include "sqliteInt.h" */
/*
** Generate code that will
@@ -99672,7 +105905,7 @@ SQLITE_PRIVATE void sqlite3OpenTable(
}else{
Index *pPk = sqlite3PrimaryKeyIndex(pTab);
assert( pPk!=0 );
- assert( pPk->tnum=pTab->tnum );
+ assert( pPk->tnum==pTab->tnum );
sqlite3VdbeAddOp3(v, opcode, iCur, pPk->tnum, iDb);
sqlite3VdbeSetP4KeyInfo(pParse, pPk);
VdbeComment((v, "%s", pTab->zName));
@@ -99686,7 +105919,7 @@ SQLITE_PRIVATE void sqlite3OpenTable(
**
** Character Column affinity
** ------------------------------
-** 'A' NONE
+** 'A' BLOB
** 'B' TEXT
** 'C' NUMERIC
** 'D' INTEGER
@@ -99699,7 +105932,7 @@ SQLITE_PRIVATE void sqlite3OpenTable(
** is managed along with the rest of the Index structure. It will be
** released when sqlite3DeleteIndex() is called.
*/
-SQLITE_PRIVATE const char *sqlite3IndexAffinityStr(Vdbe *v, Index *pIdx){
+SQLITE_PRIVATE const char *sqlite3IndexAffinityStr(sqlite3 *db, Index *pIdx){
if( !pIdx->zColAff ){
/* The first time a column affinity string for a particular index is
** required, it is allocated and populated here. It is then stored as
@@ -99711,15 +105944,25 @@ SQLITE_PRIVATE const char *sqlite3IndexAffinityStr(Vdbe *v, Index *pIdx){
*/
int n;
Table *pTab = pIdx->pTable;
- sqlite3 *db = sqlite3VdbeDb(v);
pIdx->zColAff = (char *)sqlite3DbMallocRaw(0, pIdx->nColumn+1);
if( !pIdx->zColAff ){
- db->mallocFailed = 1;
+ sqlite3OomFault(db);
return 0;
}
for(n=0; n<pIdx->nColumn; n++){
i16 x = pIdx->aiColumn[n];
- pIdx->zColAff[n] = x<0 ? SQLITE_AFF_INTEGER : pTab->aCol[x].affinity;
+ if( x>=0 ){
+ pIdx->zColAff[n] = pTab->aCol[x].affinity;
+ }else if( x==XN_ROWID ){
+ pIdx->zColAff[n] = SQLITE_AFF_INTEGER;
+ }else{
+ char aff;
+ assert( x==XN_EXPR );
+ assert( pIdx->aColExpr!=0 );
+ aff = sqlite3ExprAffinity(pIdx->aColExpr->a[n].pExpr);
+ if( aff==0 ) aff = SQLITE_AFF_BLOB;
+ pIdx->zColAff[n] = aff;
+ }
}
pIdx->zColAff[n] = 0;
}
@@ -99729,9 +105972,9 @@ SQLITE_PRIVATE const char *sqlite3IndexAffinityStr(Vdbe *v, Index *pIdx){
/*
** Compute the affinity string for table pTab, if it has not already been
-** computed. As an optimization, omit trailing SQLITE_AFF_NONE affinities.
+** computed. As an optimization, omit trailing SQLITE_AFF_BLOB affinities.
**
-** If the affinity exists (if it is no entirely SQLITE_AFF_NONE values) and
+** If the affinity exists (if it is no entirely SQLITE_AFF_BLOB values) and
** if iReg>0 then code an OP_Affinity opcode that will set the affinities
** for register iReg and following. Or if affinities exists and iReg==0,
** then just set the P4 operand of the previous opcode (which should be
@@ -99741,7 +105984,7 @@ SQLITE_PRIVATE const char *sqlite3IndexAffinityStr(Vdbe *v, Index *pIdx){
**
** Character Column affinity
** ------------------------------
-** 'A' NONE
+** 'A' BLOB
** 'B' TEXT
** 'C' NUMERIC
** 'D' INTEGER
@@ -99754,7 +105997,7 @@ SQLITE_PRIVATE void sqlite3TableAffinity(Vdbe *v, Table *pTab, int iReg){
sqlite3 *db = sqlite3VdbeDb(v);
zColAff = (char *)sqlite3DbMallocRaw(0, pTab->nCol+1);
if( !zColAff ){
- db->mallocFailed = 1;
+ sqlite3OomFault(db);
return;
}
@@ -99763,7 +106006,7 @@ SQLITE_PRIVATE void sqlite3TableAffinity(Vdbe *v, Table *pTab, int iReg){
}
do{
zColAff[i--] = 0;
- }while( i>=0 && zColAff[i]==SQLITE_AFF_NONE );
+ }while( i>=0 && zColAff[i]==SQLITE_AFF_BLOB );
pTab->zColAff = zColAff;
}
i = sqlite3Strlen30(zColAff);
@@ -99850,7 +106093,7 @@ static int autoIncBegin(
pInfo = pToplevel->pAinc;
while( pInfo && pInfo->pTab!=pTab ){ pInfo = pInfo->pNext; }
if( pInfo==0 ){
- pInfo = sqlite3DbMallocRaw(pParse->db, sizeof(*pInfo));
+ pInfo = sqlite3DbMallocRawNN(pParse->db, sizeof(*pInfo));
if( pInfo==0 ) return 0;
pInfo->pNext = pToplevel->pAinc;
pToplevel->pAinc = pInfo;
@@ -99874,43 +106117,55 @@ SQLITE_PRIVATE void sqlite3AutoincrementBegin(Parse *pParse){
sqlite3 *db = pParse->db; /* The database connection */
Db *pDb; /* Database only autoinc table */
int memId; /* Register holding max rowid */
- int addr; /* A VDBE address */
Vdbe *v = pParse->pVdbe; /* VDBE under construction */
/* This routine is never called during trigger-generation. It is
** only called from the top-level */
assert( pParse->pTriggerTab==0 );
- assert( pParse==sqlite3ParseToplevel(pParse) );
+ assert( sqlite3IsToplevel(pParse) );
assert( v ); /* We failed long ago if this is not so */
for(p = pParse->pAinc; p; p = p->pNext){
+ static const int iLn = VDBE_OFFSET_LINENO(2);
+ static const VdbeOpList autoInc[] = {
+ /* 0 */ {OP_Null, 0, 0, 0},
+ /* 1 */ {OP_Rewind, 0, 9, 0},
+ /* 2 */ {OP_Column, 0, 0, 0},
+ /* 3 */ {OP_Ne, 0, 7, 0},
+ /* 4 */ {OP_Rowid, 0, 0, 0},
+ /* 5 */ {OP_Column, 0, 1, 0},
+ /* 6 */ {OP_Goto, 0, 9, 0},
+ /* 7 */ {OP_Next, 0, 2, 0},
+ /* 8 */ {OP_Integer, 0, 0, 0},
+ /* 9 */ {OP_Close, 0, 0, 0}
+ };
+ VdbeOp *aOp;
pDb = &db->aDb[p->iDb];
memId = p->regCtr;
assert( sqlite3SchemaMutexHeld(db, 0, pDb->pSchema) );
sqlite3OpenTable(pParse, 0, p->iDb, pDb->pSchema->pSeqTab, OP_OpenRead);
- sqlite3VdbeAddOp3(v, OP_Null, 0, memId, memId+1);
- addr = sqlite3VdbeCurrentAddr(v);
- sqlite3VdbeAddOp4(v, OP_String8, 0, memId-1, 0, p->pTab->zName, 0);
- sqlite3VdbeAddOp2(v, OP_Rewind, 0, addr+9); VdbeCoverage(v);
- sqlite3VdbeAddOp3(v, OP_Column, 0, 0, memId);
- sqlite3VdbeAddOp3(v, OP_Ne, memId-1, addr+7, memId); VdbeCoverage(v);
- sqlite3VdbeChangeP5(v, SQLITE_JUMPIFNULL);
- sqlite3VdbeAddOp2(v, OP_Rowid, 0, memId+1);
- sqlite3VdbeAddOp3(v, OP_Column, 0, 1, memId);
- sqlite3VdbeAddOp2(v, OP_Goto, 0, addr+9);
- sqlite3VdbeAddOp2(v, OP_Next, 0, addr+2); VdbeCoverage(v);
- sqlite3VdbeAddOp2(v, OP_Integer, 0, memId);
- sqlite3VdbeAddOp0(v, OP_Close);
+ sqlite3VdbeLoadString(v, memId-1, p->pTab->zName);
+ aOp = sqlite3VdbeAddOpList(v, ArraySize(autoInc), autoInc, iLn);
+ if( aOp==0 ) break;
+ aOp[0].p2 = memId;
+ aOp[0].p3 = memId+1;
+ aOp[2].p3 = memId;
+ aOp[3].p1 = memId-1;
+ aOp[3].p3 = memId;
+ aOp[3].p5 = SQLITE_JUMPIFNULL;
+ aOp[4].p2 = memId+1;
+ aOp[5].p3 = memId;
+ aOp[8].p2 = memId;
}
}
/*
** Update the maximum rowid for an autoincrement calculation.
**
-** This routine should be called when the top of the stack holds a
+** This routine should be called when the regRowid register holds a
** new rowid that is about to be inserted. If that new rowid is
** larger than the maximum rowid in the memId memory cell, then the
-** memory cell is updated. The stack is unchanged.
+** memory cell is updated.
*/
static void autoIncStep(Parse *pParse, int memId, int regRowid){
if( memId>0 ){
@@ -99925,31 +106180,44 @@ static void autoIncStep(Parse *pParse, int memId, int regRowid){
** table (either directly or through triggers) needs to call this
** routine just before the "exit" code.
*/
-SQLITE_PRIVATE void sqlite3AutoincrementEnd(Parse *pParse){
+static SQLITE_NOINLINE void autoIncrementEnd(Parse *pParse){
AutoincInfo *p;
Vdbe *v = pParse->pVdbe;
sqlite3 *db = pParse->db;
assert( v );
for(p = pParse->pAinc; p; p = p->pNext){
+ static const int iLn = VDBE_OFFSET_LINENO(2);
+ static const VdbeOpList autoIncEnd[] = {
+ /* 0 */ {OP_NotNull, 0, 2, 0},
+ /* 1 */ {OP_NewRowid, 0, 0, 0},
+ /* 2 */ {OP_MakeRecord, 0, 2, 0},
+ /* 3 */ {OP_Insert, 0, 0, 0},
+ /* 4 */ {OP_Close, 0, 0, 0}
+ };
+ VdbeOp *aOp;
Db *pDb = &db->aDb[p->iDb];
- int j1;
int iRec;
int memId = p->regCtr;
iRec = sqlite3GetTempReg(pParse);
assert( sqlite3SchemaMutexHeld(db, 0, pDb->pSchema) );
sqlite3OpenTable(pParse, 0, p->iDb, pDb->pSchema->pSeqTab, OP_OpenWrite);
- j1 = sqlite3VdbeAddOp1(v, OP_NotNull, memId+1); VdbeCoverage(v);
- sqlite3VdbeAddOp2(v, OP_NewRowid, 0, memId+1);
- sqlite3VdbeJumpHere(v, j1);
- sqlite3VdbeAddOp3(v, OP_MakeRecord, memId-1, 2, iRec);
- sqlite3VdbeAddOp3(v, OP_Insert, 0, iRec, memId+1);
- sqlite3VdbeChangeP5(v, OPFLAG_APPEND);
- sqlite3VdbeAddOp0(v, OP_Close);
+ aOp = sqlite3VdbeAddOpList(v, ArraySize(autoIncEnd), autoIncEnd, iLn);
+ if( aOp==0 ) break;
+ aOp[0].p1 = memId+1;
+ aOp[1].p2 = memId+1;
+ aOp[2].p1 = memId-1;
+ aOp[2].p3 = iRec;
+ aOp[3].p2 = iRec;
+ aOp[3].p3 = memId+1;
+ aOp[3].p5 = OPFLAG_APPEND;
sqlite3ReleaseTempReg(pParse, iRec);
}
}
+SQLITE_PRIVATE void sqlite3AutoincrementEnd(Parse *pParse){
+ if( pParse->pAinc ) autoIncrementEnd(pParse);
+}
#else
/*
** If SQLITE_OMIT_AUTOINCREMENT is defined, then the three routines
@@ -100280,7 +106548,7 @@ SQLITE_PRIVATE void sqlite3Insert(
rc = sqlite3Select(pParse, pSelect, &dest);
regFromSelect = dest.iSdst;
if( rc || db->mallocFailed || pParse->nErr ) goto insert_cleanup;
- sqlite3VdbeAddOp1(v, OP_EndCoroutine, regYield);
+ sqlite3VdbeEndCoroutine(v, regYield);
sqlite3VdbeJumpHere(v, addrTop - 1); /* label B: */
assert( pSelect->pEList );
nColumn = pSelect->pEList->nExpr;
@@ -100321,7 +106589,7 @@ SQLITE_PRIVATE void sqlite3Insert(
sqlite3VdbeAddOp3(v, OP_MakeRecord, regFromSelect, nColumn, regRec);
sqlite3VdbeAddOp2(v, OP_NewRowid, srcTab, regTempRowid);
sqlite3VdbeAddOp3(v, OP_Insert, srcTab, regRec, regTempRowid);
- sqlite3VdbeAddOp2(v, OP_Goto, 0, addrL);
+ sqlite3VdbeGoto(v, addrL);
sqlite3VdbeJumpHere(v, addrL);
sqlite3ReleaseTempReg(pParse, regRec);
sqlite3ReleaseTempReg(pParse, regTempRowid);
@@ -100335,11 +106603,13 @@ SQLITE_PRIVATE void sqlite3Insert(
sNC.pParse = pParse;
srcTab = -1;
assert( useTempTable==0 );
- nColumn = pList ? pList->nExpr : 0;
- for(i=0; i<nColumn; i++){
- if( sqlite3ResolveExprNames(&sNC, pList->a[i].pExpr) ){
+ if( pList ){
+ nColumn = pList->nExpr;
+ if( sqlite3ResolveExprListNames(&sNC, pList) ){
goto insert_cleanup;
}
+ }else{
+ nColumn = 0;
}
}
@@ -100354,10 +106624,8 @@ SQLITE_PRIVATE void sqlite3Insert(
/* Make sure the number of columns in the source data matches the number
** of columns to be inserted into the table.
*/
- if( IsVirtual(pTab) ){
- for(i=0; i<pTab->nCol; i++){
- nHidden += (IsHiddenColumn(&pTab->aCol[i]) ? 1 : 0);
- }
+ for(i=0; i<pTab->nCol; i++){
+ nHidden += (IsHiddenColumn(&pTab->aCol[i]) ? 1 : 0);
}
if( pColumn==0 && nColumn && nColumn!=(pTab->nCol-nHidden) ){
sqlite3ErrorMsg(pParse,
@@ -100380,9 +106648,9 @@ SQLITE_PRIVATE void sqlite3Insert(
/* If this is not a view, open the table and and all indices */
if( !isView ){
int nIdx;
- nIdx = sqlite3OpenTableAndIndices(pParse, pTab, OP_OpenWrite, -1, 0,
+ nIdx = sqlite3OpenTableAndIndices(pParse, pTab, OP_OpenWrite, 0, -1, 0,
&iDataCur, &iIdxCur);
- aRegIdx = sqlite3DbMallocRaw(db, sizeof(int)*(nIdx+1));
+ aRegIdx = sqlite3DbMallocRawNN(db, sizeof(int)*(nIdx+1));
if( aRegIdx==0 ){
goto insert_cleanup;
}
@@ -100432,7 +106700,7 @@ SQLITE_PRIVATE void sqlite3Insert(
if( ipkColumn<0 ){
sqlite3VdbeAddOp2(v, OP_Integer, -1, regCols);
}else{
- int j1;
+ int addr1;
assert( !withoutRowid );
if( useTempTable ){
sqlite3VdbeAddOp3(v, OP_Column, srcTab, ipkColumn, regCols);
@@ -100440,9 +106708,9 @@ SQLITE_PRIVATE void sqlite3Insert(
assert( pSelect==0 ); /* Otherwise useTempTable is true */
sqlite3ExprCode(pParse, pList->a[ipkColumn].pExpr, regCols);
}
- j1 = sqlite3VdbeAddOp1(v, OP_NotNull, regCols); VdbeCoverage(v);
+ addr1 = sqlite3VdbeAddOp1(v, OP_NotNull, regCols); VdbeCoverage(v);
sqlite3VdbeAddOp2(v, OP_Integer, -1, regCols);
- sqlite3VdbeJumpHere(v, j1);
+ sqlite3VdbeJumpHere(v, addr1);
sqlite3VdbeAddOp1(v, OP_MustBeInt, regCols); VdbeCoverage(v);
}
@@ -100453,15 +106721,14 @@ SQLITE_PRIVATE void sqlite3Insert(
/* Create the new column data
*/
- for(i=0; i<pTab->nCol; i++){
- if( pColumn==0 ){
- j = i;
- }else{
+ for(i=j=0; i<pTab->nCol; i++){
+ if( pColumn ){
for(j=0; j<pColumn->nId; j++){
if( pColumn->a[j].idx==i ) break;
}
}
- if( (!useTempTable && !pList) || (pColumn && j>=pColumn->nId) ){
+ if( (!useTempTable && !pList) || (pColumn && j>=pColumn->nId)
+ || (pColumn==0 && IsOrdinaryHiddenColumn(&pTab->aCol[i])) ){
sqlite3ExprCode(pParse, pTab->aCol[i].pDflt, regCols+i+1);
}else if( useTempTable ){
sqlite3VdbeAddOp3(v, OP_Column, srcTab, j, regCols+i+1);
@@ -100469,6 +106736,7 @@ SQLITE_PRIVATE void sqlite3Insert(
assert( pSelect==0 ); /* Otherwise useTempTable is true */
sqlite3ExprCodeAndCache(pParse, pList->a[j].pExpr, regCols+i+1);
}
+ if( pColumn==0 && !IsOrdinaryHiddenColumn(&pTab->aCol[i]) ) j++;
}
/* If this is an INSERT on a view with an INSTEAD OF INSERT trigger,
@@ -100516,14 +106784,14 @@ SQLITE_PRIVATE void sqlite3Insert(
** to generate a unique primary key value.
*/
if( !appendFlag ){
- int j1;
+ int addr1;
if( !IsVirtual(pTab) ){
- j1 = sqlite3VdbeAddOp1(v, OP_NotNull, regRowid); VdbeCoverage(v);
+ addr1 = sqlite3VdbeAddOp1(v, OP_NotNull, regRowid); VdbeCoverage(v);
sqlite3VdbeAddOp3(v, OP_NewRowid, iDataCur, regRowid, regAutoinc);
- sqlite3VdbeJumpHere(v, j1);
+ sqlite3VdbeJumpHere(v, addr1);
}else{
- j1 = sqlite3VdbeCurrentAddr(v);
- sqlite3VdbeAddOp2(v, OP_IsNull, regRowid, j1+2); VdbeCoverage(v);
+ addr1 = sqlite3VdbeCurrentAddr(v);
+ sqlite3VdbeAddOp2(v, OP_IsNull, regRowid, addr1+2); VdbeCoverage(v);
}
sqlite3VdbeAddOp1(v, OP_MustBeInt, regRowid); VdbeCoverage(v);
}
@@ -100552,7 +106820,6 @@ SQLITE_PRIVATE void sqlite3Insert(
}
if( pColumn==0 ){
if( IsHiddenColumn(&pTab->aCol[i]) ){
- assert( IsVirtual(pTab) );
j = -1;
nHidden++;
}else{
@@ -100591,7 +106858,7 @@ SQLITE_PRIVATE void sqlite3Insert(
{
int isReplace; /* Set to true if constraints may cause a replace */
sqlite3GenerateConstraintChecks(pParse, pTab, aRegIdx, iDataCur, iIdxCur,
- regIns, 0, ipkColumn>=0, onError, endOfLoop, &isReplace
+ regIns, 0, ipkColumn>=0, onError, endOfLoop, &isReplace, 0
);
sqlite3FkCheck(pParse, pTab, 0, regIns, 0, 0);
sqlite3CompleteInsertion(pParse, pTab, iDataCur, iIdxCur,
@@ -100620,7 +106887,7 @@ SQLITE_PRIVATE void sqlite3Insert(
sqlite3VdbeJumpHere(v, addrInsTop);
sqlite3VdbeAddOp1(v, OP_Close, srcTab);
}else if( pSelect ){
- sqlite3VdbeAddOp2(v, OP_Goto, 0, addrCont);
+ sqlite3VdbeGoto(v, addrCont);
sqlite3VdbeJumpHere(v, addrInsTop);
}
@@ -100674,6 +106941,59 @@ insert_cleanup:
#endif
/*
+** Meanings of bits in of pWalker->eCode for checkConstraintUnchanged()
+*/
+#define CKCNSTRNT_COLUMN 0x01 /* CHECK constraint uses a changing column */
+#define CKCNSTRNT_ROWID 0x02 /* CHECK constraint references the ROWID */
+
+/* This is the Walker callback from checkConstraintUnchanged(). Set
+** bit 0x01 of pWalker->eCode if
+** pWalker->eCode to 0 if this expression node references any of the
+** columns that are being modifed by an UPDATE statement.
+*/
+static int checkConstraintExprNode(Walker *pWalker, Expr *pExpr){
+ if( pExpr->op==TK_COLUMN ){
+ assert( pExpr->iColumn>=0 || pExpr->iColumn==-1 );
+ if( pExpr->iColumn>=0 ){
+ if( pWalker->u.aiCol[pExpr->iColumn]>=0 ){
+ pWalker->eCode |= CKCNSTRNT_COLUMN;
+ }
+ }else{
+ pWalker->eCode |= CKCNSTRNT_ROWID;
+ }
+ }
+ return WRC_Continue;
+}
+
+/*
+** pExpr is a CHECK constraint on a row that is being UPDATE-ed. The
+** only columns that are modified by the UPDATE are those for which
+** aiChng[i]>=0, and also the ROWID is modified if chngRowid is true.
+**
+** Return true if CHECK constraint pExpr does not use any of the
+** changing columns (or the rowid if it is changing). In other words,
+** return true if this CHECK constraint can be skipped when validating
+** the new row in the UPDATE statement.
+*/
+static int checkConstraintUnchanged(Expr *pExpr, int *aiChng, int chngRowid){
+ Walker w;
+ memset(&w, 0, sizeof(w));
+ w.eCode = 0;
+ w.xExprCallback = checkConstraintExprNode;
+ w.u.aiCol = aiChng;
+ sqlite3WalkExpr(&w, pExpr);
+ if( !chngRowid ){
+ testcase( (w.eCode & CKCNSTRNT_ROWID)!=0 );
+ w.eCode &= ~CKCNSTRNT_ROWID;
+ }
+ testcase( w.eCode==0 );
+ testcase( w.eCode==CKCNSTRNT_COLUMN );
+ testcase( w.eCode==CKCNSTRNT_ROWID );
+ testcase( w.eCode==(CKCNSTRNT_ROWID|CKCNSTRNT_COLUMN) );
+ return !w.eCode;
+}
+
+/*
** Generate code to do constraint checks prior to an INSERT or an UPDATE
** on table pTab.
**
@@ -100767,7 +107087,8 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks(
u8 pkChng, /* Non-zero if the rowid or PRIMARY KEY changed */
u8 overrideError, /* Override onError to this if not OE_Default */
int ignoreDest, /* Jump to this label on an OE_Ignore resolution */
- int *pbMayReplace /* OUT: Set to true if constraint may cause a replace */
+ int *pbMayReplace, /* OUT: Set to true if constraint may cause a replace */
+ int *aiChng /* column i is unchanged if aiChng[i]<0 */
){
Vdbe *v; /* VDBE under constrution */
Index *pIdx; /* Pointer to one of the indices */
@@ -100777,7 +107098,7 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks(
int ix; /* Index loop counter */
int nCol; /* Number of columns */
int onError; /* Conflict resolution strategy */
- int j1; /* Address of jump instruction */
+ int addr1; /* Address of jump instruction */
int seenReplace = 0; /* True if REPLACE is used to resolve INT PK conflict */
int nPkField; /* Number of fields in PRIMARY KEY. 1 for ROWID tables */
int ipkTop = 0; /* Top of the rowid change constraint check */
@@ -100813,10 +107134,14 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks(
*/
for(i=0; i<nCol; i++){
if( i==pTab->iPKey ){
+ continue; /* ROWID is never NULL */
+ }
+ if( aiChng && aiChng[i]<0 ){
+ /* Don't bother checking for NOT NULL on columns that do not change */
continue;
}
onError = pTab->aCol[i].notNull;
- if( onError==OE_None ) continue;
+ if( onError==OE_None ) continue; /* This column is allowed to be NULL */
if( overrideError!=OE_Default ){
onError = overrideError;
}else if( onError==OE_Default ){
@@ -100848,9 +107173,10 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks(
}
default: {
assert( onError==OE_Replace );
- j1 = sqlite3VdbeAddOp1(v, OP_NotNull, regNewData+1+i); VdbeCoverage(v);
+ addr1 = sqlite3VdbeAddOp1(v, OP_NotNull, regNewData+1+i);
+ VdbeCoverage(v);
sqlite3ExprCode(pParse, pTab->aCol[i].pDflt, regNewData+1+i);
- sqlite3VdbeJumpHere(v, j1);
+ sqlite3VdbeJumpHere(v, addr1);
break;
}
}
@@ -100864,10 +107190,13 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks(
pParse->ckBase = regNewData+1;
onError = overrideError!=OE_Default ? overrideError : OE_Abort;
for(i=0; i<pCheck->nExpr; i++){
- int allOk = sqlite3VdbeMakeLabel(v);
- sqlite3ExprIfTrue(pParse, pCheck->a[i].pExpr, allOk, SQLITE_JUMPIFNULL);
+ int allOk;
+ Expr *pExpr = pCheck->a[i].pExpr;
+ if( aiChng && checkConstraintUnchanged(pExpr, aiChng, pkChng) ) continue;
+ allOk = sqlite3VdbeMakeLabel(v);
+ sqlite3ExprIfTrue(pParse, pExpr, allOk, SQLITE_JUMPIFNULL);
if( onError==OE_Ignore ){
- sqlite3VdbeAddOp2(v, OP_Goto, 0, ignoreDest);
+ sqlite3VdbeGoto(v, ignoreDest);
}else{
char *zName = pCheck->a[i].zName;
if( zName==0 ) zName = pTab->zName;
@@ -100965,17 +107294,29 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks(
if( pTrigger || sqlite3FkRequired(pParse, pTab, 0, 0) ){
sqlite3MultiWrite(pParse);
sqlite3GenerateRowDelete(pParse, pTab, pTrigger, iDataCur, iIdxCur,
- regNewData, 1, 0, OE_Replace, 1);
- }else if( pTab->pIndex ){
- sqlite3MultiWrite(pParse);
- sqlite3GenerateRowIndexDelete(pParse, pTab, iDataCur, iIdxCur, 0);
+ regNewData, 1, 0, OE_Replace, 1, -1);
+ }else{
+#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
+ if( HasRowid(pTab) ){
+ /* This OP_Delete opcode fires the pre-update-hook only. It does
+ ** not modify the b-tree. It is more efficient to let the coming
+ ** OP_Insert replace the existing entry than it is to delete the
+ ** existing entry and then insert a new one. */
+ sqlite3VdbeAddOp2(v, OP_Delete, iDataCur, OPFLAG_ISNOOP);
+ sqlite3VdbeChangeP4(v, -1, (char *)pTab, P4_TABLE);
+ }
+#endif /* SQLITE_ENABLE_PREUPDATE_HOOK */
+ if( pTab->pIndex ){
+ sqlite3MultiWrite(pParse);
+ sqlite3GenerateRowIndexDelete(pParse, pTab, iDataCur, iIdxCur,0,-1);
+ }
}
seenReplace = 1;
break;
}
case OE_Ignore: {
/*assert( seenReplace==0 );*/
- sqlite3VdbeAddOp2(v, OP_Goto, 0, ignoreDest);
+ sqlite3VdbeGoto(v, ignoreDest);
break;
}
}
@@ -101011,8 +107352,8 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks(
if( pIdx->pPartIdxWhere ){
sqlite3VdbeAddOp2(v, OP_Null, 0, aRegIdx[ix]);
pParse->ckBase = regNewData+1;
- sqlite3ExprIfFalse(pParse, pIdx->pPartIdxWhere, addrUniqueOk,
- SQLITE_JUMPIFNULL);
+ sqlite3ExprIfFalseDup(pParse, pIdx->pPartIdxWhere, addrUniqueOk,
+ SQLITE_JUMPIFNULL);
pParse->ckBase = 0;
}
@@ -101023,15 +107364,22 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks(
for(i=0; i<pIdx->nColumn; i++){
int iField = pIdx->aiColumn[i];
int x;
- if( iField<0 || iField==pTab->iPKey ){
- if( regRowid==regIdx+i ) continue; /* ROWID already in regIdx+i */
- x = regNewData;
- regRowid = pIdx->pPartIdxWhere ? -1 : regIdx+i;
+ if( iField==XN_EXPR ){
+ pParse->ckBase = regNewData+1;
+ sqlite3ExprCodeCopy(pParse, pIdx->aColExpr->a[i].pExpr, regIdx+i);
+ pParse->ckBase = 0;
+ VdbeComment((v, "%s column %d", pIdx->zName, i));
}else{
- x = iField + regNewData + 1;
+ if( iField==XN_ROWID || iField==pTab->iPKey ){
+ if( regRowid==regIdx+i ) continue; /* ROWID already in regIdx+i */
+ x = regNewData;
+ regRowid = pIdx->pPartIdxWhere ? -1 : regIdx+i;
+ }else{
+ x = iField + regNewData + 1;
+ }
+ sqlite3VdbeAddOp2(v, iField<0 ? OP_IntCopy : OP_SCopy, x, regIdx+i);
+ VdbeComment((v, "%s", iField<0 ? "rowid" : pTab->aCol[iField].zName));
}
- sqlite3VdbeAddOp2(v, OP_SCopy, x, regIdx+i);
- VdbeComment((v, "%s", iField<0 ? "rowid" : pTab->aCol[iField].zName));
}
sqlite3VdbeAddOp3(v, OP_MakeRecord, regIdx, pIdx->nColumn, aRegIdx[ix]);
VdbeComment((v, "for %s", pIdx->zName));
@@ -101081,6 +107429,7 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks(
** store it in registers regR..regR+nPk-1 */
if( pIdx!=pPk ){
for(i=0; i<pPk->nKeyCol; i++){
+ assert( pPk->aiColumn[i]>=0 );
x = sqlite3ColumnOfIndex(pIdx, pPk->aiColumn[i]);
sqlite3VdbeAddOp3(v, OP_Column, iThisCur, x, regR+i);
VdbeComment((v, "%s.%s", pTab->zName,
@@ -101102,6 +107451,7 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks(
for(i=0; i<pPk->nKeyCol; i++){
char *p4 = (char*)sqlite3LocateCollSeq(pParse, pPk->azColl[i]);
x = pPk->aiColumn[i];
+ assert( x>=0 );
if( i==(pPk->nKeyCol-1) ){
addrJump = addrUniqueOk;
op = OP_Eq;
@@ -101128,7 +107478,7 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks(
break;
}
case OE_Ignore: {
- sqlite3VdbeAddOp2(v, OP_Goto, 0, ignoreDest);
+ sqlite3VdbeGoto(v, ignoreDest);
break;
}
default: {
@@ -101139,7 +107489,8 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks(
pTrigger = sqlite3TriggersExist(pParse, pTab, TK_DELETE, 0, 0);
}
sqlite3GenerateRowDelete(pParse, pTab, pTrigger, iDataCur, iIdxCur,
- regR, nPkField, 0, OE_Replace, pIdx==pPk);
+ regR, nPkField, 0, OE_Replace,
+ (pIdx==pPk ? ONEPASS_SINGLE : ONEPASS_OFF), -1);
seenReplace = 1;
break;
}
@@ -101149,7 +107500,7 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks(
if( regR!=regIdx ) sqlite3ReleaseTempRange(pParse, regR, nPkField);
}
if( ipkTop ){
- sqlite3VdbeAddOp2(v, OP_Goto, 0, ipkTop+1);
+ sqlite3VdbeGoto(v, ipkTop+1);
sqlite3VdbeJumpHere(v, ipkBottom);
}
@@ -101202,7 +107553,7 @@ SQLITE_PRIVATE void sqlite3CompleteInsertion(
assert( pParse->nested==0 );
pik_flags |= OPFLAG_NCHANGE;
}
- if( pik_flags ) sqlite3VdbeChangeP5(v, pik_flags);
+ sqlite3VdbeChangeP5(v, pik_flags);
}
if( !HasRowid(pTab) ) return;
regData = regNewData + 1;
@@ -101224,7 +107575,7 @@ SQLITE_PRIVATE void sqlite3CompleteInsertion(
}
sqlite3VdbeAddOp3(v, OP_Insert, iDataCur, regRec, regNewData);
if( !pParse->nested ){
- sqlite3VdbeChangeP4(v, -1, pTab->zName, P4_TRANSIENT);
+ sqlite3VdbeChangeP4(v, -1, (char *)pTab, P4_TABLE);
}
sqlite3VdbeChangeP5(v, pik_flags);
}
@@ -101254,6 +107605,7 @@ SQLITE_PRIVATE int sqlite3OpenTableAndIndices(
Parse *pParse, /* Parsing context */
Table *pTab, /* Table to be opened */
int op, /* OP_OpenRead or OP_OpenWrite */
+ u8 p5, /* P5 value for OP_Open* opcodes (except on WITHOUT ROWID) */
int iBase, /* Use this for the table cursor, if there is one */
u8 *aToOpen, /* If not NULL: boolean for each table and index */
int *piDataCur, /* Write the database source cursor number here */
@@ -101266,6 +107618,7 @@ SQLITE_PRIVATE int sqlite3OpenTableAndIndices(
Vdbe *v;
assert( op==OP_OpenRead || op==OP_OpenWrite );
+ assert( op==OP_OpenWrite || p5==0 );
if( IsVirtual(pTab) ){
/* This routine is a no-op for virtual tables. Leave the output
** variables *piDataCur and *piIdxCur uninitialized so that valgrind
@@ -101287,14 +107640,16 @@ SQLITE_PRIVATE int sqlite3OpenTableAndIndices(
for(i=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){
int iIdxCur = iBase++;
assert( pIdx->pSchema==pTab->pSchema );
- if( IsPrimaryKeyIndex(pIdx) && !HasRowid(pTab) && piDataCur ){
- *piDataCur = iIdxCur;
- }
if( aToOpen==0 || aToOpen[i+1] ){
sqlite3VdbeAddOp3(v, op, iIdxCur, pIdx->tnum, iDb);
sqlite3VdbeSetP4KeyInfo(pParse, pIdx);
VdbeComment((v, "%s", pIdx->zName));
}
+ if( IsPrimaryKeyIndex(pIdx) && !HasRowid(pTab) ){
+ if( piDataCur ) *piDataCur = iIdxCur;
+ }else{
+ sqlite3VdbeChangeP5(v, p5);
+ }
}
if( iBase>pParse->nTab ) pParse->nTab = iBase;
return i;
@@ -101314,20 +107669,6 @@ SQLITE_API int sqlite3_xferopt_count;
#ifndef SQLITE_OMIT_XFER_OPT
/*
-** Check to collation names to see if they are compatible.
-*/
-static int xferCompatibleCollation(const char *z1, const char *z2){
- if( z1==0 ){
- return z2==0;
- }
- if( z2==0 ){
- return 0;
- }
- return sqlite3StrICmp(z1, z2)==0;
-}
-
-
-/*
** Check to see if index pSrc is compatible as a source of data
** for index pDest in an insert transfer optimization. The rules
** for a compatible index:
@@ -101352,10 +107693,17 @@ static int xferCompatibleIndex(Index *pDest, Index *pSrc){
if( pSrc->aiColumn[i]!=pDest->aiColumn[i] ){
return 0; /* Different columns indexed */
}
+ if( pSrc->aiColumn[i]==XN_EXPR ){
+ assert( pSrc->aColExpr!=0 && pDest->aColExpr!=0 );
+ if( sqlite3ExprCompare(pSrc->aColExpr->a[i].pExpr,
+ pDest->aColExpr->a[i].pExpr, -1)!=0 ){
+ return 0; /* Different expressions in the index */
+ }
+ }
if( pSrc->aSortOrder[i]!=pDest->aSortOrder[i] ){
return 0; /* Different sort orders */
}
- if( !xferCompatibleCollation(pSrc->azColl[i],pDest->azColl[i]) ){
+ if( sqlite3_stricmp(pSrc->azColl[i],pDest->azColl[i])!=0 ){
return 0; /* Different collating sequences */
}
}
@@ -101470,7 +107818,7 @@ static int xferOptimization(
return 0; /* The result set must have exactly one column */
}
assert( pEList->a[0].pExpr );
- if( pEList->a[0].pExpr->op!=TK_ALL ){
+ if( pEList->a[0].pExpr->op!=TK_ASTERISK ){
return 0; /* The result set must be the special operator "*" */
}
@@ -101506,21 +107854,32 @@ static int xferOptimization(
for(i=0; i<pDest->nCol; i++){
Column *pDestCol = &pDest->aCol[i];
Column *pSrcCol = &pSrc->aCol[i];
+#ifdef SQLITE_ENABLE_HIDDEN_COLUMNS
+ if( (db->flags & SQLITE_Vacuum)==0
+ && (pDestCol->colFlags | pSrcCol->colFlags) & COLFLAG_HIDDEN
+ ){
+ return 0; /* Neither table may have __hidden__ columns */
+ }
+#endif
if( pDestCol->affinity!=pSrcCol->affinity ){
return 0; /* Affinity must be the same on all columns */
}
- if( !xferCompatibleCollation(pDestCol->zColl, pSrcCol->zColl) ){
+ if( sqlite3_stricmp(pDestCol->zColl, pSrcCol->zColl)!=0 ){
return 0; /* Collating sequence must be the same on all columns */
}
if( pDestCol->notNull && !pSrcCol->notNull ){
return 0; /* tab2 must be NOT NULL if tab1 is */
}
/* Default values for second and subsequent columns need to match. */
- if( i>0
- && ((pDestCol->zDflt==0)!=(pSrcCol->zDflt==0)
- || (pDestCol->zDflt && strcmp(pDestCol->zDflt, pSrcCol->zDflt)!=0))
- ){
- return 0; /* Default values must be the same for all columns */
+ if( i>0 ){
+ assert( pDestCol->pDflt==0 || pDestCol->pDflt->op==TK_SPAN );
+ assert( pSrcCol->pDflt==0 || pSrcCol->pDflt->op==TK_SPAN );
+ if( (pDestCol->pDflt==0)!=(pSrcCol->pDflt==0)
+ || (pDestCol->pDflt && strcmp(pDestCol->pDflt->u.zToken,
+ pSrcCol->pDflt->u.zToken)!=0)
+ ){
+ return 0; /* Default values must be the same for all columns */
+ }
}
}
for(pDestIdx=pDest->pIndex; pDestIdx; pDestIdx=pDestIdx->pNext){
@@ -101595,7 +107954,7 @@ static int xferOptimization(
** (3) onError is something other than OE_Abort and OE_Rollback.
*/
addr1 = sqlite3VdbeAddOp2(v, OP_Rewind, iDest, 0); VdbeCoverage(v);
- emptyDestTest = sqlite3VdbeAddOp2(v, OP_Goto, 0, 0);
+ emptyDestTest = sqlite3VdbeAddOp0(v, OP_Goto);
sqlite3VdbeJumpHere(v, addr1);
}
if( HasRowid(pSrc) ){
@@ -101615,9 +107974,9 @@ static int xferOptimization(
assert( (pDest->tabFlags & TF_Autoincrement)==0 );
}
sqlite3VdbeAddOp2(v, OP_RowData, iSrc, regData);
- sqlite3VdbeAddOp3(v, OP_Insert, iDest, regData, regRowid);
+ sqlite3VdbeAddOp4(v, OP_Insert, iDest, regData, regRowid,
+ (char*)pDest, P4_TABLE);
sqlite3VdbeChangeP5(v, OPFLAG_NCHANGE|OPFLAG_LASTROWID|OPFLAG_APPEND);
- sqlite3VdbeChangeP4(v, -1, pDest->zName, 0);
sqlite3VdbeAddOp2(v, OP_Next, iSrc, addr1); VdbeCoverage(v);
sqlite3VdbeAddOp2(v, OP_Close, iSrc, 0);
sqlite3VdbeAddOp2(v, OP_Close, iDest, 0);
@@ -101626,7 +107985,7 @@ static int xferOptimization(
sqlite3TableLock(pParse, iDbSrc, pSrc->tnum, 0, pSrc->zName);
}
for(pDestIdx=pDest->pIndex; pDestIdx; pDestIdx=pDestIdx->pNext){
- u8 useSeekResult = 0;
+ u8 idxInsFlags = 0;
for(pSrcIdx=pSrc->pIndex; ALWAYS(pSrcIdx); pSrcIdx=pSrcIdx->pNext){
if( xferCompatibleIndex(pDestIdx, pSrcIdx) ) break;
}
@@ -101656,17 +108015,21 @@ static int xferOptimization(
** 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;
+ const char *zColl = pSrcIdx->azColl[i];
+ assert( sqlite3_stricmp(sqlite3StrBINARY, zColl)!=0
+ || sqlite3StrBINARY==zColl );
+ if( sqlite3_stricmp(sqlite3StrBINARY, zColl) ) break;
}
if( i==pSrcIdx->nColumn ){
- useSeekResult = OPFLAG_USESEEKRESULT;
+ idxInsFlags = OPFLAG_USESEEKRESULT;
sqlite3VdbeAddOp3(v, OP_Last, iDest, 0, -1);
}
}
+ if( !HasRowid(pSrc) && pDestIdx->idxType==2 ){
+ idxInsFlags |= OPFLAG_NCHANGE;
+ }
sqlite3VdbeAddOp3(v, OP_IdxInsert, iDest, regData, 1);
- sqlite3VdbeChangeP5(v, useSeekResult);
+ sqlite3VdbeChangeP5(v, idxInsFlags);
sqlite3VdbeAddOp2(v, OP_Next, iSrc, addr1+1); VdbeCoverage(v);
sqlite3VdbeJumpHere(v, addr1);
sqlite3VdbeAddOp2(v, OP_Close, iSrc, 0);
@@ -101705,6 +108068,7 @@ static int xferOptimization(
** accessed by users of the library.
*/
+/* #include "sqliteInt.h" */
/*
** Execute SQL code. Return one of the SQLITE_ success/failure
@@ -101779,7 +108143,7 @@ SQLITE_API int SQLITE_STDCALL sqlite3_exec(
for(i=0; i<nCol; i++){
azVals[i] = (char *)sqlite3_column_text(pStmt, i);
if( !azVals[i] && sqlite3_column_type(pStmt, i)!=SQLITE_NULL ){
- db->mallocFailed = 1;
+ sqlite3OomFault(db);
goto exec_out;
}
}
@@ -101820,7 +108184,7 @@ exec_out:
if( *pzErrMsg ){
memcpy(*pzErrMsg, sqlite3_errmsg(db), nErrMsg);
}else{
- rc = SQLITE_NOMEM;
+ rc = SQLITE_NOMEM_BKPT;
sqlite3Error(db, SQLITE_NOMEM);
}
}else if( pzErrMsg ){
@@ -101873,6 +108237,7 @@ exec_out:
*/
#ifndef _SQLITE3EXT_H_
#define _SQLITE3EXT_H_
+/* #include "sqlite3.h" */
typedef struct sqlite3_api_routines sqlite3_api_routines;
@@ -102122,6 +108487,20 @@ struct sqlite3_api_routines {
void (*result_text64)(sqlite3_context*,const char*,sqlite3_uint64,
void(*)(void*), unsigned char);
int (*strglob)(const char*,const char*);
+ /* Version 3.8.11 and later */
+ sqlite3_value *(*value_dup)(const sqlite3_value*);
+ void (*value_free)(sqlite3_value*);
+ int (*result_zeroblob64)(sqlite3_context*,sqlite3_uint64);
+ int (*bind_zeroblob64)(sqlite3_stmt*, int, sqlite3_uint64);
+ /* Version 3.9.0 and later */
+ unsigned int (*value_subtype)(sqlite3_value*);
+ void (*result_subtype)(sqlite3_context*,unsigned int);
+ /* Version 3.10.0 and later */
+ int (*status64)(int,sqlite3_int64*,sqlite3_int64*,int);
+ int (*strlike)(const char*,const char*,unsigned int);
+ int (*db_cacheflush)(sqlite3*);
+ /* Version 3.12.0 and later */
+ int (*system_errno)(sqlite3*);
};
/*
@@ -102135,7 +108514,7 @@ struct sqlite3_api_routines {
** the API. So the redefinition macros are only valid if the
** SQLITE_CORE macros is undefined.
*/
-#ifndef SQLITE_CORE
+#if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION)
#define sqlite3_aggregate_context sqlite3_api->aggregate_context
#ifndef SQLITE_OMIT_DEPRECATED
#define sqlite3_aggregate_count sqlite3_api->aggregate_count
@@ -102262,6 +108641,7 @@ struct sqlite3_api_routines {
#define sqlite3_value_text16le sqlite3_api->value_text16le
#define sqlite3_value_type sqlite3_api->value_type
#define sqlite3_vmprintf sqlite3_api->vmprintf
+#define sqlite3_vsnprintf sqlite3_api->vsnprintf
#define sqlite3_overload_function sqlite3_api->overload_function
#define sqlite3_prepare_v2 sqlite3_api->prepare_v2
#define sqlite3_prepare16_v2 sqlite3_api->prepare16_v2
@@ -102352,9 +108732,23 @@ struct sqlite3_api_routines {
#define sqlite3_result_blob64 sqlite3_api->result_blob64
#define sqlite3_result_text64 sqlite3_api->result_text64
#define sqlite3_strglob sqlite3_api->strglob
-#endif /* SQLITE_CORE */
-
-#ifndef SQLITE_CORE
+/* Version 3.8.11 and later */
+#define sqlite3_value_dup sqlite3_api->value_dup
+#define sqlite3_value_free sqlite3_api->value_free
+#define sqlite3_result_zeroblob64 sqlite3_api->result_zeroblob64
+#define sqlite3_bind_zeroblob64 sqlite3_api->bind_zeroblob64
+/* Version 3.9.0 and later */
+#define sqlite3_value_subtype sqlite3_api->value_subtype
+#define sqlite3_result_subtype sqlite3_api->result_subtype
+/* Version 3.10.0 and later */
+#define sqlite3_status64 sqlite3_api->status64
+#define sqlite3_strlike sqlite3_api->strlike
+#define sqlite3_db_cacheflush sqlite3_api->db_cacheflush
+/* Version 3.12.0 and later */
+#define sqlite3_system_errno sqlite3_api->system_errno
+#endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */
+
+#if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION)
/* This case when the file really is being compiled as a loadable
** extension */
# define SQLITE_EXTENSION_INIT1 const sqlite3_api_routines *sqlite3_api=0;
@@ -102373,6 +108767,7 @@ struct sqlite3_api_routines {
/************** End of sqlite3ext.h ******************************************/
/************** Continuing where we left off in loadext.c ********************/
+/* #include "sqliteInt.h" */
/* #include <string.h> */
#ifndef SQLITE_OMIT_LOAD_EXTENSION
@@ -102757,7 +109152,21 @@ static const sqlite3_api_routines sqlite3Apis = {
sqlite3_reset_auto_extension,
sqlite3_result_blob64,
sqlite3_result_text64,
- sqlite3_strglob
+ sqlite3_strglob,
+ /* Version 3.8.11 and later */
+ (sqlite3_value*(*)(const sqlite3_value*))sqlite3_value_dup,
+ sqlite3_value_free,
+ sqlite3_result_zeroblob64,
+ sqlite3_bind_zeroblob64,
+ /* Version 3.9.0 and later */
+ sqlite3_value_subtype,
+ sqlite3_result_subtype,
+ /* Version 3.10.0 and later */
+ sqlite3_status64,
+ sqlite3_strlike,
+ sqlite3_db_cacheflush,
+ /* Version 3.12.0 and later */
+ sqlite3_system_errno
};
/*
@@ -102805,8 +109214,9 @@ static int sqlite3LoadExtension(
/* Ticket #1863. To avoid a creating security problems for older
** applications that relink against newer versions of SQLite, the
** ability to run load_extension is turned off by default. One
- ** must call sqlite3_enable_load_extension() to turn on extension
- ** loading. Otherwise you get the following error.
+ ** must call either sqlite3_enable_load_extension(db) or
+ ** sqlite3_db_config(db, SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION, 1, 0)
+ ** to turn on extension loading.
*/
if( (db->flags & SQLITE_LoadExtension)==0 ){
if( pzErrMsg ){
@@ -102821,7 +109231,7 @@ static int sqlite3LoadExtension(
#if SQLITE_OS_UNIX || SQLITE_OS_WIN
for(ii=0; ii<ArraySize(azEndings) && handle==0; ii++){
char *zAltFile = sqlite3_mprintf("%s.%s", zFile, azEndings[ii]);
- if( zAltFile==0 ) return SQLITE_NOMEM;
+ if( zAltFile==0 ) return SQLITE_NOMEM_BKPT;
handle = sqlite3OsDlOpen(pVfs, zAltFile);
sqlite3_free(zAltFile);
}
@@ -102857,7 +109267,7 @@ static int sqlite3LoadExtension(
zAltEntry = sqlite3_malloc64(ncFile+30);
if( zAltEntry==0 ){
sqlite3OsDlClose(pVfs, handle);
- return SQLITE_NOMEM;
+ return SQLITE_NOMEM_BKPT;
}
memcpy(zAltEntry, "sqlite3_", 8);
for(iFile=ncFile-1; iFile>=0 && zFile[iFile]!='/'; iFile--){}
@@ -102900,7 +109310,7 @@ static int sqlite3LoadExtension(
/* Append the new shared library handle to the db->aExtension array. */
aHandle = sqlite3DbMallocZero(db, sizeof(handle)*(db->nExtension+1));
if( aHandle==0 ){
- return SQLITE_NOMEM;
+ return SQLITE_NOMEM_BKPT;
}
if( db->nExtension>0 ){
memcpy(aHandle, db->aExtension, sizeof(handle)*db->nExtension);
@@ -102945,9 +109355,9 @@ SQLITE_PRIVATE void sqlite3CloseExtensions(sqlite3 *db){
SQLITE_API int SQLITE_STDCALL sqlite3_enable_load_extension(sqlite3 *db, int onoff){
sqlite3_mutex_enter(db->mutex);
if( onoff ){
- db->flags |= SQLITE_LoadExtension;
+ db->flags |= SQLITE_LoadExtension|SQLITE_LoadExtFunc;
}else{
- db->flags &= ~SQLITE_LoadExtension;
+ db->flags &= ~(SQLITE_LoadExtension|SQLITE_LoadExtFunc);
}
sqlite3_mutex_leave(db->mutex);
return SQLITE_OK;
@@ -103022,7 +109432,7 @@ SQLITE_API int SQLITE_STDCALL sqlite3_auto_extension(void (*xInit)(void)){
void (**aNew)(void);
aNew = sqlite3_realloc64(wsdAutoext.aExt, nByte);
if( aNew==0 ){
- rc = SQLITE_NOMEM;
+ rc = SQLITE_NOMEM_BKPT;
}else{
wsdAutoext.aExt = aNew;
wsdAutoext.aExt[wsdAutoext.nExt] = xInit;
@@ -103139,6 +109549,7 @@ SQLITE_PRIVATE void sqlite3AutoLoadExtensions(sqlite3 *db){
*************************************************************************
** This file contains code used to implement the PRAGMA command.
*/
+/* #include "sqliteInt.h" */
#if !defined(SQLITE_ENABLE_LOCKING_STYLE)
# if defined(__APPLE__)
@@ -103167,43 +109578,44 @@ SQLITE_PRIVATE void sqlite3AutoLoadExtensions(sqlite3 *db){
#define PragTyp_FLAG 2
#define PragTyp_BUSY_TIMEOUT 3
#define PragTyp_CACHE_SIZE 4
-#define PragTyp_CASE_SENSITIVE_LIKE 5
-#define PragTyp_COLLATION_LIST 6
-#define PragTyp_COMPILE_OPTIONS 7
-#define PragTyp_DATA_STORE_DIRECTORY 8
-#define PragTyp_DATABASE_LIST 9
-#define PragTyp_DEFAULT_CACHE_SIZE 10
-#define PragTyp_ENCODING 11
-#define PragTyp_FOREIGN_KEY_CHECK 12
-#define PragTyp_FOREIGN_KEY_LIST 13
-#define PragTyp_INCREMENTAL_VACUUM 14
-#define PragTyp_INDEX_INFO 15
-#define PragTyp_INDEX_LIST 16
-#define PragTyp_INTEGRITY_CHECK 17
-#define PragTyp_JOURNAL_MODE 18
-#define PragTyp_JOURNAL_SIZE_LIMIT 19
-#define PragTyp_LOCK_PROXY_FILE 20
-#define PragTyp_LOCKING_MODE 21
-#define PragTyp_PAGE_COUNT 22
-#define PragTyp_MMAP_SIZE 23
-#define PragTyp_PAGE_SIZE 24
-#define PragTyp_SECURE_DELETE 25
-#define PragTyp_SHRINK_MEMORY 26
-#define PragTyp_SOFT_HEAP_LIMIT 27
-#define PragTyp_STATS 28
-#define PragTyp_SYNCHRONOUS 29
-#define PragTyp_TABLE_INFO 30
-#define PragTyp_TEMP_STORE 31
-#define PragTyp_TEMP_STORE_DIRECTORY 32
-#define PragTyp_THREADS 33
-#define PragTyp_WAL_AUTOCHECKPOINT 34
-#define PragTyp_WAL_CHECKPOINT 35
-#define PragTyp_ACTIVATE_EXTENSIONS 36
-#define PragTyp_HEXKEY 37
-#define PragTyp_KEY 38
-#define PragTyp_REKEY 39
-#define PragTyp_LOCK_STATUS 40
-#define PragTyp_PARSER_TRACE 41
+#define PragTyp_CACHE_SPILL 5
+#define PragTyp_CASE_SENSITIVE_LIKE 6
+#define PragTyp_COLLATION_LIST 7
+#define PragTyp_COMPILE_OPTIONS 8
+#define PragTyp_DATA_STORE_DIRECTORY 9
+#define PragTyp_DATABASE_LIST 10
+#define PragTyp_DEFAULT_CACHE_SIZE 11
+#define PragTyp_ENCODING 12
+#define PragTyp_FOREIGN_KEY_CHECK 13
+#define PragTyp_FOREIGN_KEY_LIST 14
+#define PragTyp_INCREMENTAL_VACUUM 15
+#define PragTyp_INDEX_INFO 16
+#define PragTyp_INDEX_LIST 17
+#define PragTyp_INTEGRITY_CHECK 18
+#define PragTyp_JOURNAL_MODE 19
+#define PragTyp_JOURNAL_SIZE_LIMIT 20
+#define PragTyp_LOCK_PROXY_FILE 21
+#define PragTyp_LOCKING_MODE 22
+#define PragTyp_PAGE_COUNT 23
+#define PragTyp_MMAP_SIZE 24
+#define PragTyp_PAGE_SIZE 25
+#define PragTyp_SECURE_DELETE 26
+#define PragTyp_SHRINK_MEMORY 27
+#define PragTyp_SOFT_HEAP_LIMIT 28
+#define PragTyp_STATS 29
+#define PragTyp_SYNCHRONOUS 30
+#define PragTyp_TABLE_INFO 31
+#define PragTyp_TEMP_STORE 32
+#define PragTyp_TEMP_STORE_DIRECTORY 33
+#define PragTyp_THREADS 34
+#define PragTyp_WAL_AUTOCHECKPOINT 35
+#define PragTyp_WAL_CHECKPOINT 36
+#define PragTyp_ACTIVATE_EXTENSIONS 37
+#define PragTyp_HEXKEY 38
+#define PragTyp_KEY 39
+#define PragTyp_REKEY 40
+#define PragTyp_LOCK_STATUS 41
+#define PragTyp_PARSER_TRACE 42
#define PragFlag_NeedSchema 0x01
#define PragFlag_ReadOnly 0x02
static const struct sPragmaNames {
@@ -103250,14 +109662,18 @@ static const struct sPragmaNames {
#endif
#if !defined(SQLITE_OMIT_FLAG_PRAGMAS)
{ /* zName: */ "cache_spill",
- /* ePragTyp: */ PragTyp_FLAG,
+ /* ePragTyp: */ PragTyp_CACHE_SPILL,
/* ePragFlag: */ 0,
- /* iArg: */ SQLITE_CacheSpill },
+ /* iArg: */ 0 },
#endif
{ /* zName: */ "case_sensitive_like",
/* ePragTyp: */ PragTyp_CASE_SENSITIVE_LIKE,
/* ePragFlag: */ 0,
/* iArg: */ 0 },
+ { /* zName: */ "cell_size_check",
+ /* ePragTyp: */ PragTyp_FLAG,
+ /* ePragFlag: */ 0,
+ /* iArg: */ SQLITE_CellSizeCk },
#if !defined(SQLITE_OMIT_FLAG_PRAGMAS)
{ /* zName: */ "checkpoint_fullfsync",
/* ePragTyp: */ PragTyp_FLAG,
@@ -103462,7 +109878,7 @@ static const struct sPragmaNames {
/* ePragFlag: */ 0,
/* iArg: */ 0 },
#endif
-#if defined(SQLITE_DEBUG)
+#if defined(SQLITE_DEBUG) && !defined(SQLITE_OMIT_PARSER_TRACE)
{ /* zName: */ "parser_trace",
/* ePragTyp: */ PragTyp_PARSER_TRACE,
/* ePragFlag: */ 0,
@@ -103615,15 +110031,15 @@ static const struct sPragmaNames {
/* iArg: */ SQLITE_WriteSchema|SQLITE_RecoveryMode },
#endif
};
-/* Number of pragmas: 59 on by default, 72 total. */
+/* Number of pragmas: 60 on by default, 73 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,
-** 1 for ON or NORMAL and 2 for FULL. Return 1 for an empty or
-** unrecognized string argument. The FULL option is disallowed
+** 1 for ON or NORMAL, 2 for FULL, and 3 for EXTRA. Return 1 for an empty or
+** unrecognized string argument. The FULL and EXTRA option is disallowed
** if the omitFull parameter it 1.
**
** Note that the values returned are one less that the values that
@@ -103632,18 +110048,21 @@ static const struct sPragmaNames {
** and older scripts may have used numbers 0 for OFF and 1 for ON.
*/
static u8 getSafetyLevel(const char *z, int omitFull, u8 dflt){
- /* 123456789 123456789 */
- static const char zText[] = "onoffalseyestruefull";
- static const u8 iOffset[] = {0, 1, 2, 4, 9, 12, 16};
- static const u8 iLength[] = {2, 2, 3, 5, 3, 4, 4};
- static const u8 iValue[] = {1, 0, 0, 0, 1, 1, 2};
+ /* 123456789 123456789 123 */
+ static const char zText[] = "onoffalseyestruextrafull";
+ static const u8 iOffset[] = {0, 1, 2, 4, 9, 12, 15, 20};
+ static const u8 iLength[] = {2, 2, 3, 5, 3, 4, 5, 4};
+ static const u8 iValue[] = {1, 0, 0, 0, 1, 1, 3, 2};
+ /* on no off false yes true extra full */
int i, n;
if( sqlite3Isdigit(*z) ){
return (u8)sqlite3Atoi(z);
}
n = sqlite3Strlen30(z);
- for(i=0; i<ArraySize(iLength)-omitFull; i++){
- if( iLength[i]==n && sqlite3StrNICmp(&zText[iOffset[i]],z,n)==0 ){
+ for(i=0; i<ArraySize(iLength); i++){
+ if( iLength[i]==n && sqlite3StrNICmp(&zText[iOffset[i]],z,n)==0
+ && (!omitFull || iValue[i]<=1)
+ ){
return iValue[i];
}
}
@@ -103750,19 +110169,45 @@ static int changeTempStorage(Parse *pParse, const char *zStorageType){
#endif /* SQLITE_PAGER_PRAGMAS */
/*
+** Set the names of the first N columns to the values in azCol[]
+*/
+static void setAllColumnNames(
+ Vdbe *v, /* The query under construction */
+ int N, /* Number of columns */
+ const char **azCol /* Names of columns */
+){
+ int i;
+ sqlite3VdbeSetNumCols(v, N);
+ for(i=0; i<N; i++){
+ sqlite3VdbeSetColName(v, i, COLNAME_NAME, azCol[i], SQLITE_STATIC);
+ }
+}
+static void setOneColumnName(Vdbe *v, const char *z){
+ setAllColumnNames(v, 1, &z);
+}
+
+/*
** Generate code to return a single integer value.
*/
-static void returnSingleInt(Parse *pParse, const char *zLabel, i64 value){
- Vdbe *v = sqlite3GetVdbe(pParse);
- int nMem = ++pParse->nMem;
- i64 *pI64 = sqlite3DbMallocRaw(pParse->db, sizeof(value));
- if( pI64 ){
- memcpy(pI64, &value, sizeof(value));
+static void returnSingleInt(Vdbe *v, const char *zLabel, i64 value){
+ sqlite3VdbeAddOp4Dup8(v, OP_Int64, 0, 1, 0, (const u8*)&value, P4_INT64);
+ setOneColumnName(v, zLabel);
+ sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 1);
+}
+
+/*
+** Generate code to return a single text value.
+*/
+static void returnSingleText(
+ Vdbe *v, /* Prepared statement under construction */
+ const char *zLabel, /* Name of the result column */
+ const char *zValue /* Value to be returned */
+){
+ if( zValue ){
+ sqlite3VdbeLoadString(v, 1, (const char*)zValue);
+ setOneColumnName(v, zLabel);
+ sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 1);
}
- 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, nMem, 1);
}
@@ -103843,7 +110288,7 @@ SQLITE_PRIVATE const char *sqlite3JournalModename(int eMode){
**
** Pragmas are of this form:
**
-** PRAGMA [database.]id [= value]
+** PRAGMA [schema.]id [= value]
**
** The identifier might also be a string. The value is a string, and
** identifier, or a number. If minusFlag is true, then the value is
@@ -103855,8 +110300,8 @@ SQLITE_PRIVATE const char *sqlite3JournalModename(int eMode){
*/
SQLITE_PRIVATE void sqlite3Pragma(
Parse *pParse,
- Token *pId1, /* First part of [database.]id field */
- Token *pId2, /* Second part of [database.]id field, or NULL */
+ Token *pId1, /* First part of [schema.]id field */
+ Token *pId2, /* Second part of [schema.]id field, or NULL */
Token *pValue, /* Token for <value>, or NULL */
int minusFlag /* True if a '-' sign preceded <value> */
){
@@ -103877,7 +110322,7 @@ SQLITE_PRIVATE void sqlite3Pragma(
sqlite3VdbeRunOnlyOnce(v);
pParse->nMem = 2;
- /* Interpret the [database.] part of the pragma statement. iDb is the
+ /* Interpret the [schema.] part of the pragma statement. iDb is the
** index of the database this pragma is being applied to in db.aDb[]. */
iDb = sqlite3TwoPartName(pParse, pId1, pId2, &pId);
if( iDb<0 ) return;
@@ -103926,14 +110371,8 @@ SQLITE_PRIVATE void sqlite3Pragma(
db->busyHandler.nBusy = 0;
rc = sqlite3_file_control(db, zDb, SQLITE_FCNTL_PRAGMA, (void*)aFcntl);
if( rc==SQLITE_OK ){
- if( aFcntl[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, nMem, 1);
- sqlite3_free(aFcntl[0]);
- }
+ returnSingleText(v, "result", aFcntl[0]);
+ sqlite3_free(aFcntl[0]);
goto pragma_out;
}
if( rc!=SQLITE_NOTFOUND ){
@@ -103972,8 +110411,8 @@ SQLITE_PRIVATE void sqlite3Pragma(
#if !defined(SQLITE_OMIT_PAGER_PRAGMAS) && !defined(SQLITE_OMIT_DEPRECATED)
/*
- ** PRAGMA [database.]default_cache_size
- ** PRAGMA [database.]default_cache_size=N
+ ** PRAGMA [schema.]default_cache_size
+ ** PRAGMA [schema.]default_cache_size=N
**
** The first form reports the current persistent setting for the
** page cache size. The value returned is the maximum number of
@@ -104000,21 +110439,21 @@ SQLITE_PRIVATE void sqlite3Pragma(
{ OP_Noop, 0, 0, 0},
{ OP_ResultRow, 1, 1, 0},
};
- int addr;
+ VdbeOp *aOp;
sqlite3VdbeUsesBtree(v, iDb);
if( !zRight ){
- sqlite3VdbeSetNumCols(v, 1);
- sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "cache_size", SQLITE_STATIC);
+ setOneColumnName(v, "cache_size");
pParse->nMem += 2;
- addr = sqlite3VdbeAddOpList(v, ArraySize(getCacheSize), getCacheSize,iLn);
- sqlite3VdbeChangeP1(v, addr, iDb);
- sqlite3VdbeChangeP1(v, addr+1, iDb);
- sqlite3VdbeChangeP1(v, addr+6, SQLITE_DEFAULT_CACHE_SIZE);
+ sqlite3VdbeVerifyNoMallocRequired(v, ArraySize(getCacheSize));
+ aOp = sqlite3VdbeAddOpList(v, ArraySize(getCacheSize), getCacheSize, iLn);
+ if( ONLY_IF_REALLOC_STRESS(aOp==0) ) break;
+ aOp[0].p1 = iDb;
+ aOp[1].p1 = iDb;
+ aOp[6].p1 = SQLITE_DEFAULT_CACHE_SIZE;
}else{
int size = sqlite3AbsInt32(sqlite3Atoi(zRight));
sqlite3BeginWriteOperation(pParse, 0, iDb);
- sqlite3VdbeAddOp2(v, OP_Integer, size, 1);
- sqlite3VdbeAddOp3(v, OP_SetCookie, iDb, BTREE_DEFAULT_CACHE_SIZE, 1);
+ sqlite3VdbeAddOp3(v, OP_SetCookie, iDb, BTREE_DEFAULT_CACHE_SIZE, size);
assert( sqlite3SchemaMutexHeld(db, iDb, 0) );
pDb->pSchema->cache_size = size;
sqlite3BtreeSetCacheSize(pDb->pBt, pDb->pSchema->cache_size);
@@ -104025,8 +110464,8 @@ SQLITE_PRIVATE void sqlite3Pragma(
#if !defined(SQLITE_OMIT_PAGER_PRAGMAS)
/*
- ** PRAGMA [database.]page_size
- ** PRAGMA [database.]page_size=N
+ ** PRAGMA [schema.]page_size
+ ** PRAGMA [schema.]page_size=N
**
** The first form reports the current setting for the
** database page size in bytes. The second form sets the
@@ -104038,22 +110477,22 @@ SQLITE_PRIVATE void sqlite3Pragma(
assert( pBt!=0 );
if( !zRight ){
int size = ALWAYS(pBt) ? sqlite3BtreeGetPageSize(pBt) : 0;
- returnSingleInt(pParse, "page_size", size);
+ returnSingleInt(v, "page_size", size);
}else{
/* Malloc may fail when setting the page-size, as there is an internal
** buffer that the pager module resizes using sqlite3_realloc().
*/
db->nextPagesize = sqlite3Atoi(zRight);
if( SQLITE_NOMEM==sqlite3BtreeSetPageSize(pBt, db->nextPagesize,-1,0) ){
- db->mallocFailed = 1;
+ sqlite3OomFault(db);
}
}
break;
}
/*
- ** PRAGMA [database.]secure_delete
- ** PRAGMA [database.]secure_delete=ON/OFF
+ ** PRAGMA [schema.]secure_delete
+ ** PRAGMA [schema.]secure_delete=ON/OFF
**
** The first form reports the current setting for the
** secure_delete flag. The second form changes the secure_delete
@@ -104073,13 +110512,13 @@ SQLITE_PRIVATE void sqlite3Pragma(
}
}
b = sqlite3BtreeSecureDelete(pBt, b);
- returnSingleInt(pParse, "secure_delete", b);
+ returnSingleInt(v, "secure_delete", b);
break;
}
/*
- ** PRAGMA [database.]max_page_count
- ** PRAGMA [database.]max_page_count=N
+ ** PRAGMA [schema.]max_page_count
+ ** PRAGMA [schema.]max_page_count=N
**
** The first form reports the current setting for the
** maximum number of pages in the database file. The
@@ -104090,7 +110529,7 @@ SQLITE_PRIVATE void sqlite3Pragma(
** change. The only purpose is to provide an easy way to test
** the sqlite3AbsInt32() function.
**
- ** PRAGMA [database.]page_count
+ ** PRAGMA [schema.]page_count
**
** Return the number of pages in the specified database.
*/
@@ -104111,8 +110550,8 @@ SQLITE_PRIVATE void sqlite3Pragma(
}
/*
- ** PRAGMA [database.]locking_mode
- ** PRAGMA [database.]locking_mode = (normal|exclusive)
+ ** PRAGMA [schema.]locking_mode
+ ** PRAGMA [schema.]locking_mode = (normal|exclusive)
*/
case PragTyp_LOCKING_MODE: {
const char *zRet = "normal";
@@ -104152,25 +110591,20 @@ SQLITE_PRIVATE void sqlite3Pragma(
if( eMode==PAGER_LOCKINGMODE_EXCLUSIVE ){
zRet = "exclusive";
}
- sqlite3VdbeSetNumCols(v, 1);
- sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "locking_mode", SQLITE_STATIC);
- sqlite3VdbeAddOp4(v, OP_String8, 0, 1, 0, zRet, 0);
- sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 1);
+ returnSingleText(v, "locking_mode", zRet);
break;
}
/*
- ** PRAGMA [database.]journal_mode
- ** PRAGMA [database.]journal_mode =
+ ** PRAGMA [schema.]journal_mode
+ ** PRAGMA [schema.]journal_mode =
** (delete|persist|off|truncate|memory|wal|off)
*/
case PragTyp_JOURNAL_MODE: {
int eMode; /* One of the PAGER_JOURNALMODE_XXX symbols */
int ii; /* Loop counter */
- sqlite3VdbeSetNumCols(v, 1);
- sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "journal_mode", SQLITE_STATIC);
-
+ setOneColumnName(v, "journal_mode");
if( zRight==0 ){
/* If there is no "=MODE" part of the pragma, do a query for the
** current mode */
@@ -104203,8 +110637,8 @@ SQLITE_PRIVATE void sqlite3Pragma(
}
/*
- ** PRAGMA [database.]journal_size_limit
- ** PRAGMA [database.]journal_size_limit=N
+ ** PRAGMA [schema.]journal_size_limit
+ ** PRAGMA [schema.]journal_size_limit=N
**
** Get or set the size limit on rollback journal files.
*/
@@ -104216,15 +110650,15 @@ SQLITE_PRIVATE void sqlite3Pragma(
if( iLimit<-1 ) iLimit = -1;
}
iLimit = sqlite3PagerJournalSizeLimit(pPager, iLimit);
- returnSingleInt(pParse, "journal_size_limit", iLimit);
+ returnSingleInt(v, "journal_size_limit", iLimit);
break;
}
#endif /* SQLITE_OMIT_PAGER_PRAGMAS */
/*
- ** PRAGMA [database.]auto_vacuum
- ** PRAGMA [database.]auto_vacuum=N
+ ** PRAGMA [schema.]auto_vacuum
+ ** PRAGMA [schema.]auto_vacuum=N
**
** Get or set the value of the database 'auto-vacuum' parameter.
** The value is one of: 0 NONE 1 FULL 2 INCREMENTAL
@@ -104234,7 +110668,7 @@ SQLITE_PRIVATE void sqlite3Pragma(
Btree *pBt = pDb->pBt;
assert( pBt!=0 );
if( !zRight ){
- returnSingleInt(pParse, "auto_vacuum", sqlite3BtreeGetAutoVacuum(pBt));
+ returnSingleInt(v, "auto_vacuum", sqlite3BtreeGetAutoVacuum(pBt));
}else{
int eAuto = getAutoVacuum(zRight);
assert( eAuto>=0 && eAuto<=2 );
@@ -104257,16 +110691,18 @@ SQLITE_PRIVATE void sqlite3Pragma(
{ OP_ReadCookie, 0, 1, BTREE_LARGEST_ROOT_PAGE},
{ OP_If, 1, 0, 0}, /* 2 */
{ OP_Halt, SQLITE_OK, OE_Abort, 0}, /* 3 */
- { OP_Integer, 0, 1, 0}, /* 4 */
- { OP_SetCookie, 0, BTREE_INCR_VACUUM, 1}, /* 5 */
+ { OP_SetCookie, 0, BTREE_INCR_VACUUM, 0}, /* 4 */
};
- int iAddr;
- iAddr = sqlite3VdbeAddOpList(v, ArraySize(setMeta6), setMeta6, iLn);
- sqlite3VdbeChangeP1(v, iAddr, iDb);
- sqlite3VdbeChangeP1(v, iAddr+1, iDb);
- sqlite3VdbeChangeP2(v, iAddr+2, iAddr+4);
- sqlite3VdbeChangeP1(v, iAddr+4, eAuto-1);
- sqlite3VdbeChangeP1(v, iAddr+5, iDb);
+ VdbeOp *aOp;
+ int iAddr = sqlite3VdbeCurrentAddr(v);
+ sqlite3VdbeVerifyNoMallocRequired(v, ArraySize(setMeta6));
+ aOp = sqlite3VdbeAddOpList(v, ArraySize(setMeta6), setMeta6, iLn);
+ if( ONLY_IF_REALLOC_STRESS(aOp==0) ) break;
+ aOp[0].p1 = iDb;
+ aOp[1].p1 = iDb;
+ aOp[2].p2 = iAddr+4;
+ aOp[4].p1 = iDb;
+ aOp[4].p3 = eAuto - 1;
sqlite3VdbeUsesBtree(v, iDb);
}
}
@@ -104275,7 +110711,7 @@ SQLITE_PRIVATE void sqlite3Pragma(
#endif
/*
- ** PRAGMA [database.]incremental_vacuum(N)
+ ** PRAGMA [schema.]incremental_vacuum(N)
**
** Do N steps of incremental vacuuming on a database.
*/
@@ -104298,8 +110734,8 @@ SQLITE_PRIVATE void sqlite3Pragma(
#ifndef SQLITE_OMIT_PAGER_PRAGMAS
/*
- ** PRAGMA [database.]cache_size
- ** PRAGMA [database.]cache_size=N
+ ** PRAGMA [schema.]cache_size
+ ** PRAGMA [schema.]cache_size=N
**
** The first form reports the current local setting for the
** page cache size. The second form sets the local
@@ -104311,7 +110747,7 @@ SQLITE_PRIVATE void sqlite3Pragma(
case PragTyp_CACHE_SIZE: {
assert( sqlite3SchemaMutexHeld(db, iDb, 0) );
if( !zRight ){
- returnSingleInt(pParse, "cache_size", pDb->pSchema->cache_size);
+ returnSingleInt(v, "cache_size", pDb->pSchema->cache_size);
}else{
int size = sqlite3Atoi(zRight);
pDb->pSchema->cache_size = size;
@@ -104321,7 +110757,50 @@ SQLITE_PRIVATE void sqlite3Pragma(
}
/*
- ** PRAGMA [database.]mmap_size(N)
+ ** PRAGMA [schema.]cache_spill
+ ** PRAGMA cache_spill=BOOLEAN
+ ** PRAGMA [schema.]cache_spill=N
+ **
+ ** The first form reports the current local setting for the
+ ** page cache spill size. The second form turns cache spill on
+ ** or off. When turnning cache spill on, the size is set to the
+ ** current cache_size. The third form sets a spill size that
+ ** may be different form the cache size.
+ ** If N is positive then that is the
+ ** number of pages in the cache. If N is negative, then the
+ ** number of pages is adjusted so that the cache uses -N kibibytes
+ ** of memory.
+ **
+ ** If the number of cache_spill pages is less then the number of
+ ** cache_size pages, no spilling occurs until the page count exceeds
+ ** the number of cache_size pages.
+ **
+ ** The cache_spill=BOOLEAN setting applies to all attached schemas,
+ ** not just the schema specified.
+ */
+ case PragTyp_CACHE_SPILL: {
+ assert( sqlite3SchemaMutexHeld(db, iDb, 0) );
+ if( !zRight ){
+ returnSingleInt(v, "cache_spill",
+ (db->flags & SQLITE_CacheSpill)==0 ? 0 :
+ sqlite3BtreeSetSpillSize(pDb->pBt,0));
+ }else{
+ int size = 1;
+ if( sqlite3GetInt32(zRight, &size) ){
+ sqlite3BtreeSetSpillSize(pDb->pBt, size);
+ }
+ if( sqlite3GetBoolean(zRight, size!=0) ){
+ db->flags |= SQLITE_CacheSpill;
+ }else{
+ db->flags &= ~SQLITE_CacheSpill;
+ }
+ setAllPagerFlags(db);
+ }
+ break;
+ }
+
+ /*
+ ** PRAGMA [schema.]mmap_size(N)
**
** Used to set mapping size limit. The mapping size limit is
** used to limit the aggregate size of all memory mapped regions of the
@@ -104356,7 +110835,7 @@ SQLITE_PRIVATE void sqlite3Pragma(
rc = SQLITE_OK;
#endif
if( rc==SQLITE_OK ){
- returnSingleInt(pParse, "mmap_size", sz);
+ returnSingleInt(v, "mmap_size", sz);
}else if( rc!=SQLITE_NOTFOUND ){
pParse->nErr++;
pParse->rc = rc;
@@ -104377,7 +110856,7 @@ SQLITE_PRIVATE void sqlite3Pragma(
*/
case PragTyp_TEMP_STORE: {
if( !zRight ){
- returnSingleInt(pParse, "temp_store", db->temp_store);
+ returnSingleInt(v, "temp_store", db->temp_store);
}else{
changeTempStorage(pParse, zRight);
}
@@ -104396,13 +110875,7 @@ SQLITE_PRIVATE void sqlite3Pragma(
*/
case PragTyp_TEMP_STORE_DIRECTORY: {
if( !zRight ){
- if( sqlite3_temp_directory ){
- sqlite3VdbeSetNumCols(v, 1);
- sqlite3VdbeSetColName(v, 0, COLNAME_NAME,
- "temp_store_directory", SQLITE_STATIC);
- sqlite3VdbeAddOp4(v, OP_String8, 0, 1, 0, sqlite3_temp_directory, 0);
- sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 1);
- }
+ returnSingleText(v, "temp_store_directory", sqlite3_temp_directory);
}else{
#ifndef SQLITE_OMIT_WSD
if( zRight[0] ){
@@ -104446,13 +110919,7 @@ SQLITE_PRIVATE void sqlite3Pragma(
*/
case PragTyp_DATA_STORE_DIRECTORY: {
if( !zRight ){
- if( sqlite3_data_directory ){
- sqlite3VdbeSetNumCols(v, 1);
- sqlite3VdbeSetColName(v, 0, COLNAME_NAME,
- "data_store_directory", SQLITE_STATIC);
- sqlite3VdbeAddOp4(v, OP_String8, 0, 1, 0, sqlite3_data_directory, 0);
- sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 1);
- }
+ returnSingleText(v, "data_store_directory", sqlite3_data_directory);
}else{
#ifndef SQLITE_OMIT_WSD
if( zRight[0] ){
@@ -104477,8 +110944,8 @@ SQLITE_PRIVATE void sqlite3Pragma(
#if SQLITE_ENABLE_LOCKING_STYLE
/*
- ** PRAGMA [database.]lock_proxy_file
- ** PRAGMA [database.]lock_proxy_file = ":auto:"|"lock_file_path"
+ ** PRAGMA [schema.]lock_proxy_file
+ ** PRAGMA [schema.]lock_proxy_file = ":auto:"|"lock_file_path"
**
** Return or set the value of the lock_proxy_file flag. Changing
** the value sets a specific file to be used for database access locks.
@@ -104491,14 +110958,7 @@ SQLITE_PRIVATE void sqlite3Pragma(
sqlite3_file *pFile = sqlite3PagerFile(pPager);
sqlite3OsFileControlHint(pFile, SQLITE_GET_LOCKPROXYFILE,
&proxy_file_path);
-
- if( proxy_file_path ){
- sqlite3VdbeSetNumCols(v, 1);
- sqlite3VdbeSetColName(v, 0, COLNAME_NAME,
- "lock_proxy_file", SQLITE_STATIC);
- sqlite3VdbeAddOp4(v, OP_String8, 0, 1, 0, proxy_file_path, 0);
- sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 1);
- }
+ returnSingleText(v, "lock_proxy_file", proxy_file_path);
}else{
Pager *pPager = sqlite3BtreePager(pDb->pBt);
sqlite3_file *pFile = sqlite3PagerFile(pPager);
@@ -104520,8 +110980,8 @@ SQLITE_PRIVATE void sqlite3Pragma(
#endif /* SQLITE_ENABLE_LOCKING_STYLE */
/*
- ** PRAGMA [database.]synchronous
- ** PRAGMA [database.]synchronous=OFF|ON|NORMAL|FULL
+ ** PRAGMA [schema.]synchronous
+ ** PRAGMA [schema.]synchronous=OFF|ON|NORMAL|FULL|EXTRA
**
** Return or set the local value of the synchronous flag. Changing
** the local value does not make changes to the disk file and the
@@ -104530,7 +110990,7 @@ SQLITE_PRIVATE void sqlite3Pragma(
*/
case PragTyp_SYNCHRONOUS: {
if( !zRight ){
- returnSingleInt(pParse, "synchronous", pDb->safety_level-1);
+ returnSingleInt(v, "synchronous", pDb->safety_level-1);
}else{
if( !db->autoCommit ){
sqlite3ErrorMsg(pParse,
@@ -104539,6 +110999,7 @@ SQLITE_PRIVATE void sqlite3Pragma(
int iLevel = (getSafetyLevel(zRight,0,1)+1) & PAGER_SYNCHRONOUS_MASK;
if( iLevel==0 ) iLevel = 1;
pDb->safety_level = iLevel;
+ pDb->bSyncSet = 1;
setAllPagerFlags(db);
}
}
@@ -104549,7 +111010,7 @@ SQLITE_PRIVATE void sqlite3Pragma(
#ifndef SQLITE_OMIT_FLAG_PRAGMAS
case PragTyp_FLAG: {
if( zRight==0 ){
- returnSingleInt(pParse, pPragma->zName, (db->flags & pPragma->iArg)!=0 );
+ returnSingleInt(v, pPragma->zName, (db->flags & pPragma->iArg)!=0 );
}else{
int mask = pPragma->iArg; /* Mask of bits to set or clear. */
if( db->autoCommit==0 ){
@@ -104599,35 +111060,22 @@ SQLITE_PRIVATE void sqlite3Pragma(
Table *pTab;
pTab = sqlite3FindTable(db, zRight, zDb);
if( pTab ){
+ static const char *azCol[] = {
+ "cid", "name", "type", "notnull", "dflt_value", "pk"
+ };
int i, k;
int nHidden = 0;
Column *pCol;
Index *pPk = sqlite3PrimaryKeyIndex(pTab);
- sqlite3VdbeSetNumCols(v, 6);
pParse->nMem = 6;
sqlite3CodeVerifySchema(pParse, iDb);
- sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "cid", SQLITE_STATIC);
- sqlite3VdbeSetColName(v, 1, COLNAME_NAME, "name", SQLITE_STATIC);
- sqlite3VdbeSetColName(v, 2, COLNAME_NAME, "type", SQLITE_STATIC);
- sqlite3VdbeSetColName(v, 3, COLNAME_NAME, "notnull", SQLITE_STATIC);
- sqlite3VdbeSetColName(v, 4, COLNAME_NAME, "dflt_value", SQLITE_STATIC);
- sqlite3VdbeSetColName(v, 5, COLNAME_NAME, "pk", SQLITE_STATIC);
+ setAllColumnNames(v, 6, azCol); assert( 6==ArraySize(azCol) );
sqlite3ViewGetColumnNames(pParse, pTab);
for(i=0, pCol=pTab->aCol; i<pTab->nCol; i++, pCol++){
if( IsHiddenColumn(pCol) ){
nHidden++;
continue;
}
- sqlite3VdbeAddOp2(v, OP_Integer, i-nHidden, 1);
- sqlite3VdbeAddOp4(v, OP_String8, 0, 2, 0, pCol->zName, 0);
- sqlite3VdbeAddOp4(v, OP_String8, 0, 3, 0,
- pCol->zType ? pCol->zType : "", 0);
- sqlite3VdbeAddOp2(v, OP_Integer, (pCol->notNull ? 1 : 0), 4);
- if( pCol->zDflt ){
- sqlite3VdbeAddOp4(v, OP_String8, 0, 5, 0, (char*)pCol->zDflt, 0);
- }else{
- sqlite3VdbeAddOp2(v, OP_Null, 0, 5);
- }
if( (pCol->colFlags & COLFLAG_PRIMKEY)==0 ){
k = 0;
}else if( pPk==0 ){
@@ -104635,7 +111083,14 @@ SQLITE_PRIVATE void sqlite3Pragma(
}else{
for(k=1; k<=pTab->nCol && pPk->aiColumn[k-1]!=i; k++){}
}
- sqlite3VdbeAddOp2(v, OP_Integer, k, 6);
+ assert( pCol->pDflt==0 || pCol->pDflt->op==TK_SPAN );
+ sqlite3VdbeMultiLoad(v, 1, "issisi",
+ i-nHidden,
+ pCol->zName,
+ sqlite3ColumnType(pCol,""),
+ pCol->notNull ? 1 : 0,
+ pCol->pDflt ? pCol->pDflt->u.zToken : 0,
+ k);
sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 6);
}
}
@@ -104643,31 +111098,26 @@ SQLITE_PRIVATE void sqlite3Pragma(
break;
case PragTyp_STATS: {
+ static const char *azCol[] = { "table", "index", "width", "height" };
Index *pIdx;
HashElem *i;
v = sqlite3GetVdbe(pParse);
- sqlite3VdbeSetNumCols(v, 4);
pParse->nMem = 4;
sqlite3CodeVerifySchema(pParse, iDb);
- sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "table", SQLITE_STATIC);
- sqlite3VdbeSetColName(v, 1, COLNAME_NAME, "index", SQLITE_STATIC);
- sqlite3VdbeSetColName(v, 2, COLNAME_NAME, "width", SQLITE_STATIC);
- sqlite3VdbeSetColName(v, 3, COLNAME_NAME, "height", SQLITE_STATIC);
+ setAllColumnNames(v, 4, azCol); assert( 4==ArraySize(azCol) );
for(i=sqliteHashFirst(&pDb->pSchema->tblHash); i; i=sqliteHashNext(i)){
Table *pTab = sqliteHashData(i);
- sqlite3VdbeAddOp4(v, OP_String8, 0, 1, 0, pTab->zName, 0);
- sqlite3VdbeAddOp2(v, OP_Null, 0, 2);
- sqlite3VdbeAddOp2(v, OP_Integer,
- (int)sqlite3LogEstToInt(pTab->szTabRow), 3);
- sqlite3VdbeAddOp2(v, OP_Integer,
- (int)sqlite3LogEstToInt(pTab->nRowLogEst), 4);
+ sqlite3VdbeMultiLoad(v, 1, "ssii",
+ pTab->zName,
+ 0,
+ pTab->szTabRow,
+ pTab->nRowLogEst);
sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 4);
for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
- sqlite3VdbeAddOp4(v, OP_String8, 0, 2, 0, pIdx->zName, 0);
- sqlite3VdbeAddOp2(v, OP_Integer,
- (int)sqlite3LogEstToInt(pIdx->szIdxRow), 3);
- sqlite3VdbeAddOp2(v, OP_Integer,
- (int)sqlite3LogEstToInt(pIdx->aiRowLogEst[0]), 4);
+ sqlite3VdbeMultiLoad(v, 2, "sii",
+ pIdx->zName,
+ pIdx->szIdxRow,
+ pIdx->aiRowLogEst[0]);
sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 4);
}
}
@@ -104679,6 +111129,9 @@ SQLITE_PRIVATE void sqlite3Pragma(
Table *pTab;
pIdx = sqlite3FindIndex(db, zRight, zDb);
if( pIdx ){
+ static const char *azCol[] = {
+ "seqno", "cid", "name", "desc", "coll", "key"
+ };
int i;
int mx;
if( pPragma->iArg ){
@@ -104691,29 +111144,18 @@ SQLITE_PRIVATE void sqlite3Pragma(
pParse->nMem = 3;
}
pTab = pIdx->pTable;
- 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);
- 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);
- }
+ assert( pParse->nMem<=ArraySize(azCol) );
+ setAllColumnNames(v, pParse->nMem, azCol);
for(i=0; i<mx; i++){
i16 cnum = pIdx->aiColumn[i];
- sqlite3VdbeAddOp2(v, OP_Integer, i, 1);
- sqlite3VdbeAddOp2(v, OP_Integer, cnum, 2);
- if( cnum<0 ){
- sqlite3VdbeAddOp2(v, OP_Null, 0, 3);
- }else{
- sqlite3VdbeAddOp4(v, OP_String8, 0, 3, 0, pTab->aCol[cnum].zName, 0);
- }
+ sqlite3VdbeMultiLoad(v, 1, "iis", i, cnum,
+ cnum<0 ? 0 : pTab->aCol[cnum].zName);
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);
+ sqlite3VdbeMultiLoad(v, 4, "isi",
+ pIdx->aSortOrder[i],
+ pIdx->azColl[i],
+ i<pIdx->nKeyCol);
}
sqlite3VdbeAddOp2(v, OP_ResultRow, 1, pParse->nMem);
}
@@ -104727,22 +111169,21 @@ SQLITE_PRIVATE void sqlite3Pragma(
int i;
pTab = sqlite3FindTable(db, zRight, zDb);
if( pTab ){
+ static const char *azCol[] = {
+ "seq", "name", "unique", "origin", "partial"
+ };
v = sqlite3GetVdbe(pParse);
- 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);
+ setAllColumnNames(v, 5, azCol); assert( 5==ArraySize(azCol) );
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);
- sqlite3VdbeAddOp4(v, OP_String8, 0, 4, 0, azOrigin[pIdx->idxType], 0);
- sqlite3VdbeAddOp2(v, OP_Integer, pIdx->pPartIdxWhere!=0, 5);
+ sqlite3VdbeMultiLoad(v, 1, "isisi",
+ i,
+ pIdx->zName,
+ IsUniqueIndex(pIdx),
+ azOrigin[pIdx->idxType],
+ pIdx->pPartIdxWhere!=0);
sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 5);
}
}
@@ -104750,35 +111191,31 @@ SQLITE_PRIVATE void sqlite3Pragma(
break;
case PragTyp_DATABASE_LIST: {
+ static const char *azCol[] = { "seq", "name", "file" };
int i;
- sqlite3VdbeSetNumCols(v, 3);
pParse->nMem = 3;
- sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "seq", SQLITE_STATIC);
- sqlite3VdbeSetColName(v, 1, COLNAME_NAME, "name", SQLITE_STATIC);
- sqlite3VdbeSetColName(v, 2, COLNAME_NAME, "file", SQLITE_STATIC);
+ setAllColumnNames(v, 3, azCol); assert( 3==ArraySize(azCol) );
for(i=0; i<db->nDb; i++){
if( db->aDb[i].pBt==0 ) continue;
assert( db->aDb[i].zName!=0 );
- sqlite3VdbeAddOp2(v, OP_Integer, i, 1);
- sqlite3VdbeAddOp4(v, OP_String8, 0, 2, 0, db->aDb[i].zName, 0);
- sqlite3VdbeAddOp4(v, OP_String8, 0, 3, 0,
- sqlite3BtreeGetFilename(db->aDb[i].pBt), 0);
+ sqlite3VdbeMultiLoad(v, 1, "iss",
+ i,
+ db->aDb[i].zName,
+ sqlite3BtreeGetFilename(db->aDb[i].pBt));
sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 3);
}
}
break;
case PragTyp_COLLATION_LIST: {
+ static const char *azCol[] = { "seq", "name" };
int i = 0;
HashElem *p;
- sqlite3VdbeSetNumCols(v, 2);
pParse->nMem = 2;
- sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "seq", SQLITE_STATIC);
- sqlite3VdbeSetColName(v, 1, COLNAME_NAME, "name", SQLITE_STATIC);
+ setAllColumnNames(v, 2, azCol); assert( 2==ArraySize(azCol) );
for(p=sqliteHashFirst(&db->aCollSeq); p; p=sqliteHashNext(p)){
CollSeq *pColl = (CollSeq *)sqliteHashData(p);
- sqlite3VdbeAddOp2(v, OP_Integer, i++, 1);
- sqlite3VdbeAddOp4(v, OP_String8, 0, 2, 0, pColl->zName, 0);
+ sqlite3VdbeMultiLoad(v, 1, "is", i++, pColl->zName);
sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 2);
}
}
@@ -104794,33 +111231,26 @@ SQLITE_PRIVATE void sqlite3Pragma(
v = sqlite3GetVdbe(pParse);
pFK = pTab->pFKey;
if( pFK ){
+ static const char *azCol[] = {
+ "id", "seq", "table", "from", "to", "on_update", "on_delete",
+ "match"
+ };
int i = 0;
- sqlite3VdbeSetNumCols(v, 8);
pParse->nMem = 8;
sqlite3CodeVerifySchema(pParse, iDb);
- sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "id", SQLITE_STATIC);
- sqlite3VdbeSetColName(v, 1, COLNAME_NAME, "seq", SQLITE_STATIC);
- sqlite3VdbeSetColName(v, 2, COLNAME_NAME, "table", SQLITE_STATIC);
- sqlite3VdbeSetColName(v, 3, COLNAME_NAME, "from", SQLITE_STATIC);
- sqlite3VdbeSetColName(v, 4, COLNAME_NAME, "to", SQLITE_STATIC);
- sqlite3VdbeSetColName(v, 5, COLNAME_NAME, "on_update", SQLITE_STATIC);
- sqlite3VdbeSetColName(v, 6, COLNAME_NAME, "on_delete", SQLITE_STATIC);
- sqlite3VdbeSetColName(v, 7, COLNAME_NAME, "match", SQLITE_STATIC);
+ setAllColumnNames(v, 8, azCol); assert( 8==ArraySize(azCol) );
while(pFK){
int j;
for(j=0; j<pFK->nCol; j++){
- char *zCol = pFK->aCol[j].zCol;
- char *zOnDelete = (char *)actionName(pFK->aAction[0]);
- char *zOnUpdate = (char *)actionName(pFK->aAction[1]);
- sqlite3VdbeAddOp2(v, OP_Integer, i, 1);
- sqlite3VdbeAddOp2(v, OP_Integer, j, 2);
- sqlite3VdbeAddOp4(v, OP_String8, 0, 3, 0, pFK->zTo, 0);
- sqlite3VdbeAddOp4(v, OP_String8, 0, 4, 0,
- pTab->aCol[pFK->aCol[j].iFrom].zName, 0);
- sqlite3VdbeAddOp4(v, zCol ? OP_String8 : OP_Null, 0, 5, 0, zCol, 0);
- sqlite3VdbeAddOp4(v, OP_String8, 0, 6, 0, zOnUpdate, 0);
- sqlite3VdbeAddOp4(v, OP_String8, 0, 7, 0, zOnDelete, 0);
- sqlite3VdbeAddOp4(v, OP_String8, 0, 8, 0, "NONE", 0);
+ sqlite3VdbeMultiLoad(v, 1, "iissssss",
+ i,
+ j,
+ pFK->zTo,
+ pTab->aCol[pFK->aCol[j].iFrom].zName,
+ pFK->aCol[j].zCol,
+ actionName(pFK->aAction[1]), /* ON UPDATE */
+ actionName(pFK->aAction[0]), /* ON DELETE */
+ "NONE");
sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 8);
}
++i;
@@ -104849,17 +111279,14 @@ SQLITE_PRIVATE void sqlite3Pragma(
int addrTop; /* Top of a loop checking foreign keys */
int addrOk; /* Jump here if the key is OK */
int *aiCols; /* child to parent column mapping */
+ static const char *azCol[] = { "table", "rowid", "parent", "fkid" };
regResult = pParse->nMem+1;
pParse->nMem += 4;
regKey = ++pParse->nMem;
regRow = ++pParse->nMem;
v = sqlite3GetVdbe(pParse);
- sqlite3VdbeSetNumCols(v, 4);
- sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "table", SQLITE_STATIC);
- sqlite3VdbeSetColName(v, 1, COLNAME_NAME, "rowid", SQLITE_STATIC);
- sqlite3VdbeSetColName(v, 2, COLNAME_NAME, "parent", SQLITE_STATIC);
- sqlite3VdbeSetColName(v, 3, COLNAME_NAME, "fkid", SQLITE_STATIC);
+ setAllColumnNames(v, 4, azCol); assert( 4==ArraySize(azCol) );
sqlite3CodeVerifySchema(pParse, iDb);
k = sqliteHashFirst(&db->aDb[iDb].pSchema->tblHash);
while( k ){
@@ -104874,8 +111301,7 @@ SQLITE_PRIVATE void sqlite3Pragma(
sqlite3TableLock(pParse, iDb, pTab->tnum, 0, pTab->zName);
if( pTab->nCol+regRow>pParse->nMem ) pParse->nMem = pTab->nCol + regRow;
sqlite3OpenTable(pParse, 0, iDb, pTab, OP_OpenRead);
- sqlite3VdbeAddOp4(v, OP_String8, 0, regResult, 0, pTab->zName,
- P4_TRANSIENT);
+ sqlite3VdbeLoadString(v, regResult, pTab->zName);
for(i=1, pFK=pTab->pFKey; pFK; i++, pFK=pFK->pNextFrom){
pParent = sqlite3FindTable(db, pFK->zTo, zDb);
if( pParent==0 ) continue;
@@ -104920,7 +111346,7 @@ SQLITE_PRIVATE void sqlite3Pragma(
sqlite3VdbeAddOp2(v, OP_Rowid, 0, regRow);
}
sqlite3VdbeAddOp3(v, OP_NotExists, i, 0, regRow); VdbeCoverage(v);
- sqlite3VdbeAddOp2(v, OP_Goto, 0, addrOk);
+ sqlite3VdbeGoto(v, addrOk);
sqlite3VdbeJumpHere(v, sqlite3VdbeCurrentAddr(v)-2);
}else{
for(j=0; j<pFK->nCol; j++){
@@ -104930,15 +111356,13 @@ SQLITE_PRIVATE void sqlite3Pragma(
}
if( pParent ){
sqlite3VdbeAddOp4(v, OP_MakeRecord, regRow, pFK->nCol, regKey,
- sqlite3IndexAffinityStr(v,pIdx), pFK->nCol);
+ sqlite3IndexAffinityStr(db,pIdx), pFK->nCol);
sqlite3VdbeAddOp4Int(v, OP_Found, i, addrOk, regKey, 0);
VdbeCoverage(v);
}
}
sqlite3VdbeAddOp2(v, OP_Rowid, 0, regResult+1);
- sqlite3VdbeAddOp4(v, OP_String8, 0, regResult+2, 0,
- pFK->zTo, P4_TRANSIENT);
- sqlite3VdbeAddOp2(v, OP_Integer, i-1, regResult+3);
+ sqlite3VdbeMultiLoad(v, regResult+2, "si", pFK->zTo, i-1);
sqlite3VdbeAddOp2(v, OP_ResultRow, regResult, 4);
sqlite3VdbeResolveLabel(v, addrOk);
sqlite3DbFree(db, aiCols);
@@ -104955,7 +111379,7 @@ SQLITE_PRIVATE void sqlite3Pragma(
case PragTyp_PARSER_TRACE: {
if( zRight ){
if( sqlite3GetBoolean(zRight, 0) ){
- sqlite3ParserTrace(stderr, "parser: ");
+ sqlite3ParserTrace(stdout, "parser: ");
}else{
sqlite3ParserTrace(0, 0);
}
@@ -104986,17 +111410,6 @@ SQLITE_PRIVATE void sqlite3Pragma(
case PragTyp_INTEGRITY_CHECK: {
int i, j, addr, mxErr;
- /* Code that appears at the end of the integrity check. If no error
- ** messages have been generated, output OK. Otherwise output the
- ** error message
- */
- static const int iLn = VDBE_OFFSET_LINENO(2);
- static const VdbeOpList endCode[] = {
- { OP_IfNeg, 1, 0, 0}, /* 0 */
- { OP_String8, 0, 3, 0}, /* 1 */
- { OP_ResultRow, 3, 1, 0},
- };
-
int isQuick = (sqlite3Tolower(zLeft[0])=='q');
/* If the PRAGMA command was of the form "PRAGMA <db>.integrity_check",
@@ -105014,8 +111427,7 @@ SQLITE_PRIVATE void sqlite3Pragma(
/* Initialize the VDBE program */
pParse->nMem = 6;
- sqlite3VdbeSetNumCols(v, 1);
- sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "integrity_check", SQLITE_STATIC);
+ setOneColumnName(v, "integrity_check");
/* Set the maximum error count */
mxErr = SQLITE_INTEGRITY_CHECK_ERROR_MAX;
@@ -105031,7 +111443,10 @@ SQLITE_PRIVATE void sqlite3Pragma(
for(i=0; i<db->nDb; i++){
HashElem *x;
Hash *pTbls;
+ int *aRoot;
int cnt = 0;
+ int mxIdx = 0;
+ int nIdx;
if( OMIT_TEMPDB && i==1 ) continue;
if( iDb>=0 && i!=iDb ) continue;
@@ -105044,31 +111459,35 @@ SQLITE_PRIVATE void sqlite3Pragma(
/* Do an integrity check of the B-Tree
**
- ** Begin by filling registers 2, 3, ... with the root pages numbers
+ ** Begin by finding the root pages numbers
** for all tables and indices in the database.
*/
assert( sqlite3SchemaMutexHeld(db, i, 0) );
pTbls = &db->aDb[i].pSchema->tblHash;
- for(x=sqliteHashFirst(pTbls); x; x=sqliteHashNext(x)){
+ for(cnt=0, x=sqliteHashFirst(pTbls); x; x=sqliteHashNext(x)){
Table *pTab = sqliteHashData(x);
Index *pIdx;
- if( HasRowid(pTab) ){
- sqlite3VdbeAddOp2(v, OP_Integer, pTab->tnum, 2+cnt);
- VdbeComment((v, "%s", pTab->zName));
- cnt++;
- }
+ if( HasRowid(pTab) ) cnt++;
+ for(nIdx=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, nIdx++){ cnt++; }
+ if( nIdx>mxIdx ) mxIdx = nIdx;
+ }
+ aRoot = sqlite3DbMallocRawNN(db, sizeof(int)*(cnt+1));
+ if( aRoot==0 ) break;
+ for(cnt=0, x=sqliteHashFirst(pTbls); x; x=sqliteHashNext(x)){
+ Table *pTab = sqliteHashData(x);
+ Index *pIdx;
+ if( HasRowid(pTab) ) aRoot[cnt++] = pTab->tnum;
for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
- sqlite3VdbeAddOp2(v, OP_Integer, pIdx->tnum, 2+cnt);
- VdbeComment((v, "%s", pIdx->zName));
- cnt++;
+ aRoot[cnt++] = pIdx->tnum;
}
}
+ aRoot[cnt] = 0;
/* Make sure sufficient number of registers have been allocated */
- pParse->nMem = MAX( pParse->nMem, cnt+8 );
+ pParse->nMem = MAX( pParse->nMem, 8+mxIdx );
/* Do the b-tree integrity checks */
- sqlite3VdbeAddOp3(v, OP_IntegrityCk, 2, cnt, 1);
+ sqlite3VdbeAddOp4(v, OP_IntegrityCk, 2, cnt, 1, (char*)aRoot,P4_INTARRAY);
sqlite3VdbeChangeP5(v, (u8)i);
addr = sqlite3VdbeAddOp1(v, OP_IsNull, 2); VdbeCoverage(v);
sqlite3VdbeAddOp4(v, OP_String8, 0, 3, 0,
@@ -105096,13 +111515,14 @@ SQLITE_PRIVATE void sqlite3Pragma(
sqlite3VdbeAddOp2(v, OP_Halt, 0, 0);
sqlite3VdbeJumpHere(v, addr);
sqlite3ExprCacheClear(pParse);
- sqlite3OpenTableAndIndices(pParse, pTab, OP_OpenRead,
+ sqlite3OpenTableAndIndices(pParse, pTab, OP_OpenRead, 0,
1, 0, &iDataCur, &iIdxCur);
sqlite3VdbeAddOp2(v, OP_Integer, 0, 7);
for(j=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, j++){
sqlite3VdbeAddOp2(v, OP_Integer, 0, 8+j); /* index entries counter */
}
- pParse->nMem = MAX(pParse->nMem, 8+j);
+ assert( pParse->nMem>=8+j );
+ assert( sqlite3NoTempsInRange(pParse,1,7+j) );
sqlite3VdbeAddOp2(v, OP_Rewind, iDataCur, 0); VdbeCoverage(v);
loopTop = sqlite3VdbeAddOp2(v, OP_AddImm, 7, 1);
/* Verify that all NOT NULL columns really are NOT NULL */
@@ -105137,13 +111557,11 @@ SQLITE_PRIVATE void sqlite3Pragma(
jmp2 = sqlite3VdbeAddOp4Int(v, OP_Found, iIdxCur+j, ckUniq, r1,
pIdx->nColumn); VdbeCoverage(v);
sqlite3VdbeAddOp2(v, OP_AddImm, 1, -1); /* Decrement error limit */
- sqlite3VdbeAddOp4(v, OP_String8, 0, 3, 0, "row ", P4_STATIC);
+ sqlite3VdbeLoadString(v, 3, "row ");
sqlite3VdbeAddOp3(v, OP_Concat, 7, 3, 3);
- sqlite3VdbeAddOp4(v, OP_String8, 0, 4, 0,
- " missing from index ", P4_STATIC);
+ sqlite3VdbeLoadString(v, 4, " missing from index ");
sqlite3VdbeAddOp3(v, OP_Concat, 4, 3, 3);
- jmp5 = sqlite3VdbeAddOp4(v, OP_String8, 0, 4, 0,
- pIdx->zName, P4_TRANSIENT);
+ jmp5 = sqlite3VdbeLoadString(v, 4, pIdx->zName);
sqlite3VdbeAddOp3(v, OP_Concat, 4, 3, 3);
sqlite3VdbeAddOp2(v, OP_ResultRow, 3, 1);
jmp4 = sqlite3VdbeAddOp1(v, OP_IfPos, 1); VdbeCoverage(v);
@@ -105158,20 +111576,19 @@ SQLITE_PRIVATE void sqlite3Pragma(
int kk;
for(kk=0; kk<pIdx->nKeyCol; kk++){
int iCol = pIdx->aiColumn[kk];
- assert( iCol>=0 && iCol<pTab->nCol );
- if( pTab->aCol[iCol].notNull ) continue;
+ assert( iCol!=XN_ROWID && iCol<pTab->nCol );
+ if( iCol>=0 && pTab->aCol[iCol].notNull ) continue;
sqlite3VdbeAddOp2(v, OP_IsNull, r1+kk, uniqOk);
VdbeCoverage(v);
}
jmp6 = sqlite3VdbeAddOp1(v, OP_Next, iIdxCur+j); VdbeCoverage(v);
- sqlite3VdbeAddOp2(v, OP_Goto, 0, uniqOk);
+ sqlite3VdbeGoto(v, uniqOk);
sqlite3VdbeJumpHere(v, jmp6);
sqlite3VdbeAddOp4Int(v, OP_IdxGT, iIdxCur+j, uniqOk, r1,
pIdx->nKeyCol); VdbeCoverage(v);
sqlite3VdbeAddOp2(v, OP_AddImm, 1, -1); /* Decrement error limit */
- sqlite3VdbeAddOp4(v, OP_String8, 0, 3, 0,
- "non-unique entry in index ", P4_STATIC);
- sqlite3VdbeAddOp2(v, OP_Goto, 0, jmp5);
+ sqlite3VdbeLoadString(v, 3, "non-unique entry in index ");
+ sqlite3VdbeGoto(v, jmp5);
sqlite3VdbeResolveLabel(v, uniqOk);
}
sqlite3VdbeJumpHere(v, jmp4);
@@ -105180,8 +111597,7 @@ SQLITE_PRIVATE void sqlite3Pragma(
sqlite3VdbeAddOp2(v, OP_Next, iDataCur, loopTop); VdbeCoverage(v);
sqlite3VdbeJumpHere(v, loopTop-1);
#ifndef SQLITE_OMIT_BTREECOUNT
- sqlite3VdbeAddOp4(v, OP_String8, 0, 2, 0,
- "wrong # of entries in index ", P4_STATIC);
+ sqlite3VdbeLoadString(v, 2, "wrong # of entries in index ");
for(j=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, j++){
if( pPk==pIdx ) continue;
addr = sqlite3VdbeCurrentAddr(v);
@@ -105191,17 +111607,30 @@ SQLITE_PRIVATE void sqlite3Pragma(
sqlite3VdbeAddOp3(v, OP_Eq, 8+j, addr+8, 3); VdbeCoverage(v);
sqlite3VdbeChangeP5(v, SQLITE_NOTNULL);
sqlite3VdbeAddOp2(v, OP_AddImm, 1, -1);
- sqlite3VdbeAddOp4(v, OP_String8, 0, 3, 0, pIdx->zName, P4_TRANSIENT);
+ sqlite3VdbeLoadString(v, 3, pIdx->zName);
sqlite3VdbeAddOp3(v, OP_Concat, 3, 2, 7);
sqlite3VdbeAddOp2(v, OP_ResultRow, 7, 1);
}
#endif /* SQLITE_OMIT_BTREECOUNT */
}
}
- addr = sqlite3VdbeAddOpList(v, ArraySize(endCode), endCode, iLn);
- sqlite3VdbeChangeP3(v, addr, -mxErr);
- sqlite3VdbeJumpHere(v, addr);
- sqlite3VdbeChangeP4(v, addr+1, "ok", P4_STATIC);
+ {
+ static const int iLn = VDBE_OFFSET_LINENO(2);
+ static const VdbeOpList endCode[] = {
+ { OP_AddImm, 1, 0, 0}, /* 0 */
+ { OP_If, 1, 4, 0}, /* 1 */
+ { OP_String8, 0, 3, 0}, /* 2 */
+ { OP_ResultRow, 3, 1, 0}, /* 3 */
+ };
+ VdbeOp *aOp;
+
+ aOp = sqlite3VdbeAddOpList(v, ArraySize(endCode), endCode, iLn);
+ if( aOp ){
+ aOp[0].p2 = -mxErr;
+ aOp[2].p4type = P4_STATIC;
+ aOp[2].p4.z = "ok";
+ }
+ }
}
break;
#endif /* SQLITE_OMIT_INTEGRITY_CHECK */
@@ -105247,14 +111676,10 @@ SQLITE_PRIVATE void sqlite3Pragma(
const struct EncName *pEnc;
if( !zRight ){ /* "PRAGMA encoding" */
if( sqlite3ReadSchema(pParse) ) goto pragma_out;
- sqlite3VdbeSetNumCols(v, 1);
- sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "encoding", SQLITE_STATIC);
- sqlite3VdbeAddOp2(v, OP_String8, 0, 1);
assert( encnames[SQLITE_UTF8].enc==SQLITE_UTF8 );
assert( encnames[SQLITE_UTF16LE].enc==SQLITE_UTF16LE );
assert( encnames[SQLITE_UTF16BE].enc==SQLITE_UTF16BE );
- sqlite3VdbeChangeP4(v, -1, encnames[ENC(pParse->db)].zName, P4_STATIC);
- sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 1);
+ returnSingleText(v, "encoding", encnames[ENC(pParse->db)].zName);
}else{ /* "PRAGMA encoding = XXX" */
/* Only change the value of sqlite.enc if the database handle is not
** initialized. If the main database exists, the new sqlite.enc value
@@ -105283,16 +111708,18 @@ SQLITE_PRIVATE void sqlite3Pragma(
#ifndef SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS
/*
- ** PRAGMA [database.]schema_version
- ** PRAGMA [database.]schema_version = <integer>
+ ** PRAGMA [schema.]schema_version
+ ** PRAGMA [schema.]schema_version = <integer>
+ **
+ ** PRAGMA [schema.]user_version
+ ** PRAGMA [schema.]user_version = <integer>
**
- ** PRAGMA [database.]user_version
- ** PRAGMA [database.]user_version = <integer>
+ ** PRAGMA [schema.]freelist_count
**
- ** PRAGMA [database.]freelist_count = <integer>
+ ** PRAGMA [schema.]data_version
**
- ** PRAGMA [database.]application_id
- ** PRAGMA [database.]application_id = <integer>
+ ** PRAGMA [schema.]application_id
+ ** PRAGMA [schema.]application_id = <integer>
**
** The pragma's schema_version and user_version are used to set or get
** the value of the schema-version and user-version, respectively. Both
@@ -105319,14 +111746,16 @@ SQLITE_PRIVATE void sqlite3Pragma(
/* Write the specified cookie value */
static const VdbeOpList setCookie[] = {
{ OP_Transaction, 0, 1, 0}, /* 0 */
- { OP_Integer, 0, 1, 0}, /* 1 */
- { OP_SetCookie, 0, 0, 1}, /* 2 */
+ { OP_SetCookie, 0, 0, 0}, /* 1 */
};
- int addr = sqlite3VdbeAddOpList(v, ArraySize(setCookie), setCookie, 0);
- sqlite3VdbeChangeP1(v, addr, iDb);
- sqlite3VdbeChangeP1(v, addr+1, sqlite3Atoi(zRight));
- sqlite3VdbeChangeP1(v, addr+2, iDb);
- sqlite3VdbeChangeP2(v, addr+2, iCookie);
+ VdbeOp *aOp;
+ sqlite3VdbeVerifyNoMallocRequired(v, ArraySize(setCookie));
+ aOp = sqlite3VdbeAddOpList(v, ArraySize(setCookie), setCookie, 0);
+ if( ONLY_IF_REALLOC_STRESS(aOp==0) ) break;
+ aOp[0].p1 = iDb;
+ aOp[1].p1 = iDb;
+ aOp[1].p2 = iCookie;
+ aOp[1].p3 = sqlite3Atoi(zRight);
}else{
/* Read the specified cookie value */
static const VdbeOpList readCookie[] = {
@@ -105334,12 +111763,16 @@ SQLITE_PRIVATE void sqlite3Pragma(
{ OP_ReadCookie, 0, 1, 0}, /* 1 */
{ OP_ResultRow, 1, 1, 0}
};
- int addr = sqlite3VdbeAddOpList(v, ArraySize(readCookie), readCookie, 0);
- sqlite3VdbeChangeP1(v, addr, iDb);
- sqlite3VdbeChangeP1(v, addr+1, iDb);
- sqlite3VdbeChangeP3(v, addr+1, iCookie);
+ VdbeOp *aOp;
+ sqlite3VdbeVerifyNoMallocRequired(v, ArraySize(readCookie));
+ aOp = sqlite3VdbeAddOpList(v, ArraySize(readCookie),readCookie,0);
+ if( ONLY_IF_REALLOC_STRESS(aOp==0) ) break;
+ aOp[0].p1 = iDb;
+ aOp[1].p1 = iDb;
+ aOp[1].p3 = iCookie;
sqlite3VdbeSetNumCols(v, 1);
sqlite3VdbeSetColName(v, 0, COLNAME_NAME, zLeft, SQLITE_TRANSIENT);
+ sqlite3VdbeReusable(v);
}
}
break;
@@ -105355,24 +111788,25 @@ SQLITE_PRIVATE void sqlite3Pragma(
case PragTyp_COMPILE_OPTIONS: {
int i = 0;
const char *zOpt;
- sqlite3VdbeSetNumCols(v, 1);
pParse->nMem = 1;
- sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "compile_option", SQLITE_STATIC);
+ setOneColumnName(v, "compile_option");
while( (zOpt = sqlite3_compileoption_get(i++))!=0 ){
- sqlite3VdbeAddOp4(v, OP_String8, 0, 1, 0, zOpt, 0);
+ sqlite3VdbeLoadString(v, 1, zOpt);
sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 1);
}
+ sqlite3VdbeReusable(v);
}
break;
#endif /* SQLITE_OMIT_COMPILEOPTION_DIAGS */
#ifndef SQLITE_OMIT_WAL
/*
- ** PRAGMA [database.]wal_checkpoint = passive|full|restart|truncate
+ ** PRAGMA [schema.]wal_checkpoint = passive|full|restart|truncate
**
** Checkpoint the database.
*/
case PragTyp_WAL_CHECKPOINT: {
+ static const char *azCol[] = { "busy", "log", "checkpointed" };
int iBt = (pId2->z?iDb:SQLITE_MAX_ATTACHED);
int eMode = SQLITE_CHECKPOINT_PASSIVE;
if( zRight ){
@@ -105384,12 +111818,8 @@ SQLITE_PRIVATE void sqlite3Pragma(
eMode = SQLITE_CHECKPOINT_TRUNCATE;
}
}
- sqlite3VdbeSetNumCols(v, 3);
+ setAllColumnNames(v, 3, azCol); assert( 3==ArraySize(azCol) );
pParse->nMem = 3;
- sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "busy", SQLITE_STATIC);
- sqlite3VdbeSetColName(v, 1, COLNAME_NAME, "log", SQLITE_STATIC);
- sqlite3VdbeSetColName(v, 2, COLNAME_NAME, "checkpointed", SQLITE_STATIC);
-
sqlite3VdbeAddOp3(v, OP_Checkpoint, iBt, eMode, 1);
sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 3);
}
@@ -105407,7 +111837,7 @@ SQLITE_PRIVATE void sqlite3Pragma(
if( zRight ){
sqlite3_wal_autocheckpoint(db, sqlite3Atoi(zRight));
}
- returnSingleInt(pParse, "wal_autocheckpoint",
+ returnSingleInt(v, "wal_autocheckpoint",
db->xWalCallback==sqlite3WalDefaultHook ?
SQLITE_PTR_TO_INT(db->pWalArg) : 0);
}
@@ -105440,7 +111870,7 @@ SQLITE_PRIVATE void sqlite3Pragma(
if( zRight ){
sqlite3_busy_timeout(db, sqlite3Atoi(zRight));
}
- returnSingleInt(pParse, "timeout", db->busyTimeout);
+ returnSingleInt(v, "timeout", db->busyTimeout);
break;
}
@@ -105460,7 +111890,7 @@ SQLITE_PRIVATE void sqlite3Pragma(
if( zRight && sqlite3DecOrHexToI64(zRight, &N)==SQLITE_OK ){
sqlite3_soft_heap_limit64(N);
}
- returnSingleInt(pParse, "soft_heap_limit", sqlite3_soft_heap_limit64(-1));
+ returnSingleInt(v, "soft_heap_limit", sqlite3_soft_heap_limit64(-1));
break;
}
@@ -105479,7 +111909,7 @@ SQLITE_PRIVATE void sqlite3Pragma(
){
sqlite3_limit(db, SQLITE_LIMIT_WORKER_THREADS, (int)(N&0x7fffffff));
}
- returnSingleInt(pParse, "threads",
+ returnSingleInt(v, "threads",
sqlite3_limit(db, SQLITE_LIMIT_WORKER_THREADS, -1));
break;
}
@@ -105492,17 +111922,15 @@ SQLITE_PRIVATE void sqlite3Pragma(
static const char *const azLockName[] = {
"unlocked", "shared", "reserved", "pending", "exclusive"
};
+ static const char *azCol[] = { "database", "status" };
int i;
- sqlite3VdbeSetNumCols(v, 2);
+ setAllColumnNames(v, 2, azCol); assert( 2==ArraySize(azCol) );
pParse->nMem = 2;
- sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "database", SQLITE_STATIC);
- sqlite3VdbeSetColName(v, 1, COLNAME_NAME, "status", SQLITE_STATIC);
for(i=0; i<db->nDb; i++){
Btree *pBt;
const char *zState = "unknown";
int j;
if( db->aDb[i].zName==0 ) continue;
- sqlite3VdbeAddOp4(v, OP_String8, 0, 1, 0, db->aDb[i].zName, P4_STATIC);
pBt = db->aDb[i].pBt;
if( pBt==0 || sqlite3BtreePager(pBt)==0 ){
zState = "closed";
@@ -105510,7 +111938,7 @@ SQLITE_PRIVATE void sqlite3Pragma(
SQLITE_FCNTL_LOCKSTATE, &j)==SQLITE_OK ){
zState = azLockName[j];
}
- sqlite3VdbeAddOp4(v, OP_String8, 0, 2, 0, zState, P4_STATIC);
+ sqlite3VdbeMultiLoad(v, 1, "ss", db->aDb[i].zName, zState);
sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 2);
}
break;
@@ -105586,6 +112014,7 @@ pragma_out:
** interface, and routines that contribute to loading the database schema
** from disk.
*/
+/* #include "sqliteInt.h" */
/*
** Fill the InitData structure with an error message that indicates
@@ -105598,15 +112027,14 @@ static void corruptSchema(
){
sqlite3 *db = pData->db;
if( !db->mallocFailed && (db->flags & SQLITE_RecoveryMode)==0 ){
+ char *z;
if( zObj==0 ) zObj = "?";
- sqlite3SetString(pData->pzErrMsg, db,
- "malformed database schema (%s)", zObj);
- if( zExtra ){
- *pData->pzErrMsg = sqlite3MAppendf(db, *pData->pzErrMsg,
- "%s - %s", *pData->pzErrMsg, zExtra);
- }
+ z = sqlite3MPrintf(db, "malformed database schema (%s)", zObj);
+ if( zExtra ) z = sqlite3MPrintf(db, "%z - %s", z, zExtra);
+ sqlite3DbFree(db, *pData->pzErrMsg);
+ *pData->pzErrMsg = z;
}
- pData->rc = db->mallocFailed ? SQLITE_NOMEM : SQLITE_CORRUPT_BKPT;
+ pData->rc = db->mallocFailed ? SQLITE_NOMEM_BKPT : SQLITE_CORRUPT_BKPT;
}
/*
@@ -105663,7 +112091,7 @@ SQLITE_PRIVATE int sqlite3InitCallback(void *pInit, int argc, char **argv, char
}else{
pData->rc = rc;
if( rc==SQLITE_NOMEM ){
- db->mallocFailed = 1;
+ sqlite3OomFault(db);
}else if( rc!=SQLITE_INTERRUPT && (rc&0xFF)!=SQLITE_LOCKED ){
corruptSchema(pData, argv[0], sqlite3_errmsg(db));
}
@@ -105709,61 +112137,27 @@ static int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg){
#ifndef SQLITE_OMIT_DEPRECATED
int size;
#endif
- Table *pTab;
Db *pDb;
char const *azArg[4];
int meta[5];
InitData initData;
- char const *zMasterSchema;
- char const *zMasterName;
+ const char *zMasterName;
int openedTransaction = 0;
- /*
- ** The master database table has a structure like this
- */
- static const char master_schema[] =
- "CREATE TABLE sqlite_master(\n"
- " type text,\n"
- " name text,\n"
- " tbl_name text,\n"
- " rootpage integer,\n"
- " sql text\n"
- ")"
- ;
-#ifndef SQLITE_OMIT_TEMPDB
- static const char temp_master_schema[] =
- "CREATE TEMP TABLE sqlite_temp_master(\n"
- " type text,\n"
- " name text,\n"
- " tbl_name text,\n"
- " rootpage integer,\n"
- " sql text\n"
- ")"
- ;
-#else
- #define temp_master_schema 0
-#endif
-
assert( iDb>=0 && iDb<db->nDb );
assert( db->aDb[iDb].pSchema );
assert( sqlite3_mutex_held(db->mutex) );
assert( iDb==1 || sqlite3BtreeHoldsMutex(db->aDb[iDb].pBt) );
- /* zMasterSchema and zInitScript are set to point at the master schema
- ** and initialisation script appropriate for the database being
- ** initialized. zMasterName is the name of the master table.
- */
- if( !OMIT_TEMPDB && iDb==1 ){
- zMasterSchema = temp_master_schema;
- }else{
- zMasterSchema = master_schema;
- }
- zMasterName = SCHEMA_TABLE(iDb);
-
- /* Construct the schema tables. */
- azArg[0] = zMasterName;
+ /* Construct the in-memory representation schema tables (sqlite_master or
+ ** sqlite_temp_master) by invoking the parser directly. The appropriate
+ ** table name will be inserted automatically by the parser so we can just
+ ** use the abbreviation "x" here. The parser will also automatically tag
+ ** the schema table as read-only. */
+ azArg[0] = zMasterName = SCHEMA_TABLE(iDb);
azArg[1] = "1";
- azArg[2] = zMasterSchema;
+ azArg[2] = "CREATE TABLE x(type text,name text,tbl_name text,"
+ "rootpage integer,sql text)";
azArg[3] = 0;
initData.db = db;
initData.iDb = iDb;
@@ -105774,10 +112168,6 @@ static int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg){
rc = initData.rc;
goto error_out;
}
- pTab = sqlite3FindTable(db, zMasterName, db->aDb[iDb].zName);
- if( ALWAYS(pTab) ){
- pTab->tabFlags |= TF_Readonly;
- }
/* Create a cursor to hold the database open
*/
@@ -105796,7 +112186,7 @@ static int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg){
if( !sqlite3BtreeIsInReadTrans(pDb->pBt) ){
rc = sqlite3BtreeBeginTrans(pDb->pBt, 0);
if( rc!=SQLITE_OK ){
- sqlite3SetString(pzErrMsg, db, "%s", sqlite3ErrStr(rc));
+ sqlite3SetString(pzErrMsg, db, sqlite3ErrStr(rc));
goto initone_error_out;
}
openedTransaction = 1;
@@ -105896,7 +112286,7 @@ static int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg){
{
char *zSql;
zSql = sqlite3MPrintf(db,
- "SELECT name, rootpage, sql FROM '%q'.%s ORDER BY rowid",
+ "SELECT name, rootpage, sql FROM \"%w\".%s ORDER BY rowid",
db->aDb[iDb].zName, zMasterName);
#ifndef SQLITE_OMIT_AUTHORIZATION
{
@@ -105918,7 +112308,7 @@ static int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg){
#endif
}
if( db->mallocFailed ){
- rc = SQLITE_NOMEM;
+ rc = SQLITE_NOMEM_BKPT;
sqlite3ResetAllSchemasOfConnection(db);
}
if( rc==SQLITE_OK || (db->flags&SQLITE_RecoveryMode)){
@@ -105946,7 +112336,7 @@ initone_error_out:
error_out:
if( rc==SQLITE_NOMEM || rc==SQLITE_IOERR_NOMEM ){
- db->mallocFailed = 1;
+ sqlite3OomFault(db);
}
return rc;
}
@@ -106044,7 +112434,7 @@ static void schemaIsValid(Parse *pParse){
if( !sqlite3BtreeIsInReadTrans(pBt) ){
rc = sqlite3BtreeBeginTrans(pBt, 0);
if( rc==SQLITE_NOMEM || rc==SQLITE_IOERR_NOMEM ){
- db->mallocFailed = 1;
+ sqlite3OomFault(db);
}
if( rc!=SQLITE_OK ) return;
openedTransaction = 1;
@@ -106107,6 +112497,11 @@ SQLITE_PRIVATE void sqlite3ParserReset(Parse *pParse){
sqlite3 *db = pParse->db;
sqlite3DbFree(db, pParse->aLabel);
sqlite3ExprListDelete(db, pParse->pConstExpr);
+ if( db ){
+ assert( db->lookaside.bDisable >= pParse->disableLookaside );
+ db->lookaside.bDisable -= pParse->disableLookaside;
+ }
+ pParse->disableLookaside = 0;
}
}
@@ -106130,12 +112525,12 @@ static int sqlite3Prepare(
/* Allocate the parsing context */
pParse = sqlite3StackAllocZero(db, sizeof(*pParse));
if( pParse==0 ){
- rc = SQLITE_NOMEM;
+ rc = SQLITE_NOMEM_BKPT;
goto end_prepare;
}
pParse->pReprepare = pReprepare;
assert( ppStmt && *ppStmt==0 );
- assert( !db->mallocFailed );
+ /* assert( !db->mallocFailed ); // not true with SQLITE_USE_ALLOCA */
assert( sqlite3_mutex_held(db->mutex) );
/* Check to verify that it is possible to get a read lock on all
@@ -106192,8 +112587,8 @@ static int sqlite3Prepare(
zSqlCopy = sqlite3DbStrNDup(db, zSql, nBytes);
if( zSqlCopy ){
sqlite3RunParser(pParse, zSqlCopy, &zErrMsg);
- sqlite3DbFree(db, zSqlCopy);
pParse->zTail = &zSql[pParse->zTail-zSqlCopy];
+ sqlite3DbFree(db, zSqlCopy);
}else{
pParse->zTail = &zSql[nBytes];
}
@@ -106202,15 +112597,12 @@ static int sqlite3Prepare(
}
assert( 0==pParse->nQueryLoop );
- if( db->mallocFailed ){
- pParse->rc = SQLITE_NOMEM;
- }
if( pParse->rc==SQLITE_DONE ) pParse->rc = SQLITE_OK;
if( pParse->checkSchema ){
schemaIsValid(pParse);
}
if( db->mallocFailed ){
- pParse->rc = SQLITE_NOMEM;
+ pParse->rc = SQLITE_NOMEM_BKPT;
}
if( pzTail ){
*pzTail = pParse->zTail;
@@ -106326,7 +112718,7 @@ SQLITE_PRIVATE int sqlite3Reprepare(Vdbe *p){
rc = sqlite3LockAndPrepare(db, zSql, -1, 0, p, &pNew, 0);
if( rc ){
if( rc==SQLITE_NOMEM ){
- db->mallocFailed = 1;
+ sqlite3OomFault(db);
}
assert( pNew==0 );
return rc;
@@ -106480,6 +112872,7 @@ SQLITE_API int SQLITE_STDCALL sqlite3_prepare16_v2(
** This file contains C code routines that are called by the parser
** to handle SELECT statements in SQLite.
*/
+/* #include "sqliteInt.h" */
/*
** Trace output macros
@@ -106488,7 +112881,8 @@ SQLITE_API int SQLITE_STDCALL sqlite3_prepare16_v2(
/***/ int sqlite3SelectTrace = 0;
# define SELECTTRACE(K,P,S,X) \
if(sqlite3SelectTrace&(K)) \
- sqlite3DebugPrintf("%*s%s.%p: ",(P)->nSelectIndent*2-2,"",(S)->zSelName,(S)),\
+ sqlite3DebugPrintf("%*s%s.%p: ",(P)->nSelectIndent*2-2,"",\
+ (S)->zSelName,(S)),\
sqlite3DebugPrintf X
#else
# define SELECTTRACE(K,P,S,X)
@@ -106520,6 +112914,7 @@ struct SortCtx {
int regReturn; /* Register holding block-output return address */
int labelBkOut; /* Start label for the block-output subroutine */
int addrSortIndex; /* Address of the OP_SorterOpen or OP_OpenEphemeral */
+ int labelDone; /* Jump here when done, ex: LIMIT reached */
u8 sortFlags; /* Zero or more SORTFLAG_* bits */
};
#define SORTFLAG_UseSorter 0x01 /* Use SorterOpen instead of OpenEphemeral */
@@ -106539,7 +112934,7 @@ static void clearSelect(sqlite3 *db, Select *p, int bFree){
sqlite3ExprListDelete(db, p->pOrderBy);
sqlite3ExprDelete(db, p->pLimit);
sqlite3ExprDelete(db, p->pOffset);
- sqlite3WithDelete(db, p->pWith);
+ if( p->pWith ) sqlite3WithDelete(db, p->pWith);
if( bFree ) sqlite3DbFree(db, p);
p = pPrior;
bFree = 1;
@@ -106570,36 +112965,44 @@ SQLITE_PRIVATE Select *sqlite3SelectNew(
ExprList *pGroupBy, /* the GROUP BY clause */
Expr *pHaving, /* the HAVING clause */
ExprList *pOrderBy, /* the ORDER BY clause */
- u16 selFlags, /* Flag parameters, such as SF_Distinct */
+ u32 selFlags, /* Flag parameters, such as SF_Distinct */
Expr *pLimit, /* LIMIT value. NULL means not used */
Expr *pOffset /* OFFSET value. NULL means no offset */
){
Select *pNew;
Select standin;
sqlite3 *db = pParse->db;
- pNew = sqlite3DbMallocZero(db, sizeof(*pNew) );
+ pNew = sqlite3DbMallocRawNN(db, sizeof(*pNew) );
if( pNew==0 ){
assert( db->mallocFailed );
pNew = &standin;
- memset(pNew, 0, sizeof(*pNew));
}
if( pEList==0 ){
- pEList = sqlite3ExprListAppend(pParse, 0, sqlite3Expr(db,TK_ALL,0));
+ pEList = sqlite3ExprListAppend(pParse, 0, sqlite3Expr(db,TK_ASTERISK,0));
}
pNew->pEList = pEList;
+ pNew->op = TK_SELECT;
+ pNew->selFlags = selFlags;
+ pNew->iLimit = 0;
+ pNew->iOffset = 0;
+#if SELECTTRACE_ENABLED
+ pNew->zSelName[0] = 0;
+#endif
+ pNew->addrOpenEphm[0] = -1;
+ pNew->addrOpenEphm[1] = -1;
+ pNew->nSelectRow = 0;
if( pSrc==0 ) pSrc = sqlite3DbMallocZero(db, sizeof(*pSrc));
pNew->pSrc = pSrc;
pNew->pWhere = pWhere;
pNew->pGroupBy = pGroupBy;
pNew->pHaving = pHaving;
pNew->pOrderBy = pOrderBy;
- pNew->selFlags = selFlags;
- pNew->op = TK_SELECT;
+ pNew->pPrior = 0;
+ pNew->pNext = 0;
pNew->pLimit = pLimit;
pNew->pOffset = pOffset;
+ pNew->pWith = 0;
assert( pOffset==0 || pLimit!=0 || pParse->nErr>0 || db->mallocFailed!=0 );
- pNew->addrOpenEphm[0] = -1;
- pNew->addrOpenEphm[1] = -1;
if( db->mallocFailed ) {
clearSelect(db, pNew, pNew!=&standin);
pNew = 0;
@@ -106626,7 +113029,7 @@ SQLITE_PRIVATE void sqlite3SelectSetName(Select *p, const char *zName){
** Delete the given Select structure and all of its substructures.
*/
SQLITE_PRIVATE void sqlite3SelectDelete(sqlite3 *db, Select *p){
- clearSelect(db, p, 1);
+ if( p ) clearSelect(db, p, 1);
}
/*
@@ -106832,6 +113235,12 @@ static void setJoinExpr(Expr *p, int iTable){
assert( !ExprHasProperty(p, EP_TokenOnly|EP_Reduced) );
ExprSetVVAProperty(p, EP_NoReduce);
p->iRightJoinTable = (i16)iTable;
+ if( p->op==TK_FUNCTION && p->x.pList ){
+ int i;
+ for(i=0; i<p->x.pList->nExpr; i++){
+ setJoinExpr(p->x.pList->a[i].pExpr, iTable);
+ }
+ }
setJoinExpr(p->pLeft, iTable);
p = p->pRight;
}
@@ -106866,12 +113275,12 @@ static int sqliteProcessJoin(Parse *pParse, Select *p){
int isOuter;
if( NEVER(pLeftTab==0 || pRightTab==0) ) continue;
- isOuter = (pRight->jointype & JT_OUTER)!=0;
+ isOuter = (pRight->fg.jointype & JT_OUTER)!=0;
/* When the NATURAL keyword is present, add WHERE clause terms for
** every column that the two tables have in common.
*/
- if( pRight->jointype & JT_NATURAL ){
+ if( pRight->fg.jointype & JT_NATURAL ){
if( pRight->pOn || pRight->pUsing ){
sqlite3ErrorMsg(pParse, "a NATURAL join may not have "
"an ON or USING clause", 0);
@@ -106956,6 +113365,7 @@ static void pushOntoSorter(
SortCtx *pSort, /* Information about the ORDER BY clause */
Select *pSelect, /* The whole SELECT statement */
int regData, /* First register holding data to be sorted */
+ int regOrigData, /* First register holding data before packing */
int nData, /* Number of elements in the data array */
int nPrefixReg /* No. of reg prior to regData available for use */
){
@@ -106967,8 +113377,10 @@ static void pushOntoSorter(
int regRecord = ++pParse->nMem; /* Assembled sorter record */
int nOBSat = pSort->nOBSat; /* ORDER BY terms to skip */
int op; /* Opcode to add sorter record to sorter */
+ int iLimit; /* LIMIT counter */
assert( bSeq==0 || bSeq==1 );
+ assert( nData==1 || regData==regOrigData );
if( nPrefixReg ){
assert( nPrefixReg==nExpr+bSeq );
regBase = regData - nExpr - bSeq;
@@ -106976,14 +113388,17 @@ static void pushOntoSorter(
regBase = pParse->nMem + 1;
pParse->nMem += nBase;
}
- sqlite3ExprCodeExprList(pParse, pSort->pOrderBy, regBase, SQLITE_ECEL_DUP);
+ assert( pSelect->iOffset==0 || pSelect->iLimit!=0 );
+ iLimit = pSelect->iOffset ? pSelect->iOffset+1 : pSelect->iLimit;
+ pSort->labelDone = sqlite3VdbeMakeLabel(v);
+ sqlite3ExprCodeExprList(pParse, pSort->pOrderBy, regBase, regOrigData,
+ SQLITE_ECEL_DUP|SQLITE_ECEL_REF);
if( bSeq ){
sqlite3VdbeAddOp2(v, OP_Sequence, pSort->iECursor, regBase+nExpr);
}
if( nPrefixReg==0 ){
sqlite3ExprCodeMove(pParse, regData, regBase+nExpr+bSeq, nData);
}
-
sqlite3VdbeAddOp3(v, OP_MakeRecord, regBase+nOBSat, nBase-nOBSat, regRecord);
if( nOBSat>0 ){
int regPrevKey; /* The first nOBSat columns of the previous row */
@@ -107018,6 +113433,10 @@ static void pushOntoSorter(
pSort->regReturn = ++pParse->nMem;
sqlite3VdbeAddOp2(v, OP_Gosub, pSort->regReturn, pSort->labelBkOut);
sqlite3VdbeAddOp1(v, OP_ResetSorter, pSort->iECursor);
+ if( iLimit ){
+ sqlite3VdbeAddOp2(v, OP_IfNot, iLimit, pSort->labelDone);
+ VdbeCoverage(v);
+ }
sqlite3VdbeJumpHere(v, addrFirst);
sqlite3ExprCodeMove(pParse, regBase, regPrevKey, pSort->nOBSat);
sqlite3VdbeJumpHere(v, addrJmp);
@@ -107028,15 +113447,9 @@ static void pushOntoSorter(
op = OP_IdxInsert;
}
sqlite3VdbeAddOp2(v, op, pSort->iECursor, regRecord);
- if( pSelect->iLimit ){
+ if( iLimit ){
int addr;
- int iLimit;
- if( pSelect->iOffset ){
- iLimit = pSelect->iOffset+1;
- }else{
- iLimit = pSelect->iLimit;
- }
- addr = sqlite3VdbeAddOp3(v, OP_IfNotZero, iLimit, 0, -1); VdbeCoverage(v);
+ addr = sqlite3VdbeAddOp3(v, OP_IfNotZero, iLimit, 0, 1); VdbeCoverage(v);
sqlite3VdbeAddOp1(v, OP_Last, pSort->iECursor);
sqlite3VdbeAddOp1(v, OP_Delete, pSort->iECursor);
sqlite3VdbeJumpHere(v, addr);
@@ -107052,11 +113465,8 @@ static void codeOffset(
int iContinue /* Jump here to skip the current record */
){
if( iOffset>0 ){
- int addr;
- addr = sqlite3VdbeAddOp3(v, OP_IfNeg, iOffset, 0, -1); VdbeCoverage(v);
- sqlite3VdbeAddOp2(v, OP_Goto, 0, iContinue);
- VdbeComment((v, "skip OFFSET records"));
- sqlite3VdbeJumpHere(v, addr);
+ sqlite3VdbeAddOp3(v, OP_IfPos, iOffset, iContinue, 1); VdbeCoverage(v);
+ VdbeComment((v, "OFFSET"));
}
}
@@ -107180,8 +113590,13 @@ static void selectInnerLoop(
/* If the destination is an EXISTS(...) expression, the actual
** values returned by the SELECT are not required.
*/
- sqlite3ExprCodeExprList(pParse, pEList, regResult,
- (eDest==SRT_Output||eDest==SRT_Coroutine)?SQLITE_ECEL_DUP:0);
+ u8 ecelFlags;
+ if( eDest==SRT_Mem || eDest==SRT_Output || eDest==SRT_Coroutine ){
+ ecelFlags = SQLITE_ECEL_DUP;
+ }else{
+ ecelFlags = 0;
+ }
+ sqlite3ExprCodeExprList(pParse, pEList, regResult, 0, ecelFlags);
}
/* If the DISTINCT keyword was present on the SELECT statement
@@ -107236,7 +113651,8 @@ static void selectInnerLoop(
default: {
assert( pDistinct->eTnctType==WHERE_DISTINCT_UNORDERED );
- codeDistinct(pParse, pDistinct->tabTnct, iContinue, nResultCol, regResult);
+ codeDistinct(pParse, pDistinct->tabTnct, iContinue, nResultCol,
+ regResult);
break;
}
}
@@ -107278,6 +113694,8 @@ static void selectInnerLoop(
int r1 = sqlite3GetTempRange(pParse, nPrefixReg+1);
testcase( eDest==SRT_Table );
testcase( eDest==SRT_EphemTab );
+ testcase( eDest==SRT_Fifo );
+ testcase( eDest==SRT_DistFifo );
sqlite3VdbeAddOp3(v, OP_MakeRecord, regResult, nResultCol, r1+nPrefixReg);
#ifndef SQLITE_OMIT_CTE
if( eDest==SRT_DistFifo ){
@@ -107287,13 +113705,14 @@ static void selectInnerLoop(
** current row to the index and proceed with writing it to the
** output table as well. */
int addr = sqlite3VdbeCurrentAddr(v) + 4;
- sqlite3VdbeAddOp4Int(v, OP_Found, iParm+1, addr, r1, 0); VdbeCoverage(v);
+ sqlite3VdbeAddOp4Int(v, OP_Found, iParm+1, addr, r1, 0);
+ VdbeCoverage(v);
sqlite3VdbeAddOp2(v, OP_IdxInsert, iParm+1, r1);
assert( pSort==0 );
}
#endif
if( pSort ){
- pushOntoSorter(pParse, pSort, p, r1+nPrefixReg, 1, nPrefixReg);
+ pushOntoSorter(pParse, pSort, p, r1+nPrefixReg,regResult,1,nPrefixReg);
}else{
int r2 = sqlite3GetTempReg(pParse);
sqlite3VdbeAddOp2(v, OP_NewRowid, iParm, r2);
@@ -107319,7 +113738,7 @@ static void selectInnerLoop(
** ORDER BY in this case since the order of entries in the set
** does not matter. But there might be a LIMIT clause, in which
** case the order does matter */
- pushOntoSorter(pParse, pSort, p, regResult, 1, nPrefixReg);
+ pushOntoSorter(pParse, pSort, p, regResult, regResult, 1, nPrefixReg);
}else{
int r1 = sqlite3GetTempReg(pParse);
sqlite3VdbeAddOp4(v, OP_MakeRecord, regResult,1,r1, &pDest->affSdst, 1);
@@ -107345,7 +113764,7 @@ static void selectInnerLoop(
case SRT_Mem: {
assert( nResultCol==1 );
if( pSort ){
- pushOntoSorter(pParse, pSort, p, regResult, 1, nPrefixReg);
+ pushOntoSorter(pParse, pSort, p, regResult, regResult, 1, nPrefixReg);
}else{
assert( regResult==iParm );
/* The LIMIT clause will jump out of the loop for us */
@@ -107359,7 +113778,8 @@ static void selectInnerLoop(
testcase( eDest==SRT_Coroutine );
testcase( eDest==SRT_Output );
if( pSort ){
- pushOntoSorter(pParse, pSort, p, regResult, nResultCol, nPrefixReg);
+ pushOntoSorter(pParse, pSort, p, regResult, regResult, nResultCol,
+ nPrefixReg);
}else if( eDest==SRT_Coroutine ){
sqlite3VdbeAddOp1(v, OP_Yield, pDest->iSDParm);
}else{
@@ -107445,8 +113865,8 @@ static void selectInnerLoop(
** X extra columns.
*/
SQLITE_PRIVATE KeyInfo *sqlite3KeyInfoAlloc(sqlite3 *db, int N, int X){
- KeyInfo *p = sqlite3DbMallocZero(0,
- sizeof(KeyInfo) + (N+X)*(sizeof(CollSeq*)+1));
+ int nExtra = (N+X)*(sizeof(CollSeq*)+1);
+ KeyInfo *p = sqlite3Malloc(sizeof(KeyInfo) + nExtra);
if( p ){
p->aSortOrder = (u8*)&p->aColl[N+X];
p->nField = (u16)N;
@@ -107454,8 +113874,9 @@ SQLITE_PRIVATE KeyInfo *sqlite3KeyInfoAlloc(sqlite3 *db, int N, int X){
p->enc = ENC(db);
p->db = db;
p->nRef = 1;
+ memset(&p[1], 0, nExtra);
}else{
- db->mallocFailed = 1;
+ sqlite3OomFault(db);
}
return p;
}
@@ -107533,7 +113954,6 @@ static KeyInfo *keyInfoFromExprList(
return pInfo;
}
-#ifndef SQLITE_OMIT_COMPOUND_SELECT
/*
** Name of the connection operator, used for error messages.
*/
@@ -107547,7 +113967,6 @@ static const char *selectOpName(int id){
}
return z;
}
-#endif /* SQLITE_OMIT_COMPOUND_SELECT */
#ifndef SQLITE_OMIT_EXPLAIN
/*
@@ -107634,7 +114053,7 @@ static void generateSortTail(
SelectDest *pDest /* Write the sorted results here */
){
Vdbe *v = pParse->pVdbe; /* The prepared statement */
- int addrBreak = sqlite3VdbeMakeLabel(v); /* Jump here to exit loop */
+ int addrBreak = pSort->labelDone; /* Jump here to exit loop */
int addrContinue = sqlite3VdbeMakeLabel(v); /* Jump here for next cycle */
int addr;
int addrOnce = 0;
@@ -107653,9 +114072,10 @@ static void generateSortTail(
struct ExprList_item *aOutEx = p->pEList->a;
#endif
+ assert( addrBreak<0 );
if( pSort->labelBkOut ){
sqlite3VdbeAddOp2(v, OP_Gosub, pSort->regReturn, pSort->labelBkOut);
- sqlite3VdbeAddOp2(v, OP_Goto, 0, addrBreak);
+ sqlite3VdbeGoto(v, addrBreak);
sqlite3VdbeResolveLabel(v, pSort->labelBkOut);
}
iTab = pSort->iECursor;
@@ -107693,10 +114113,7 @@ static void generateSortTail(
VdbeComment((v, "%s", aOutEx[i].zName ? aOutEx[i].zName : aOutEx[i].zSpan));
}
switch( eDest ){
- case SRT_Table:
case SRT_EphemTab: {
- testcase( eDest==SRT_Table );
- testcase( eDest==SRT_EphemTab );
sqlite3VdbeAddOp2(v, OP_NewRowid, iParm, regRowid);
sqlite3VdbeAddOp3(v, OP_Insert, iParm, regRow, regRowid);
sqlite3VdbeChangeP5(v, OPFLAG_APPEND);
@@ -107773,30 +114190,30 @@ static void generateSortTail(
*/
#ifdef SQLITE_ENABLE_COLUMN_METADATA
# define columnType(A,B,C,D,E,F) columnTypeImpl(A,B,C,D,E,F)
+#else /* if !defined(SQLITE_ENABLE_COLUMN_METADATA) */
+# define columnType(A,B,C,D,E,F) columnTypeImpl(A,B,F)
+#endif
static const char *columnTypeImpl(
NameContext *pNC,
Expr *pExpr,
+#ifdef SQLITE_ENABLE_COLUMN_METADATA
const char **pzOrigDb,
const char **pzOrigTab,
const char **pzOrigCol,
+#endif
u8 *pEstWidth
){
- char const *zOrigDb = 0;
- char const *zOrigTab = 0;
- char const *zOrigCol = 0;
-#else /* if !defined(SQLITE_ENABLE_COLUMN_METADATA) */
-# define columnType(A,B,C,D,E,F) columnTypeImpl(A,B,F)
-static const char *columnTypeImpl(
- NameContext *pNC,
- Expr *pExpr,
- u8 *pEstWidth
-){
-#endif /* !defined(SQLITE_ENABLE_COLUMN_METADATA) */
char const *zType = 0;
int j;
u8 estWidth = 1;
+#ifdef SQLITE_ENABLE_COLUMN_METADATA
+ char const *zOrigDb = 0;
+ char const *zOrigTab = 0;
+ char const *zOrigCol = 0;
+#endif
- if( NEVER(pExpr==0) || pNC->pSrcList==0 ) return 0;
+ assert( pExpr!=0 );
+ assert( pNC->pSrcList!=0 );
switch( pExpr->op ){
case TK_AGG_COLUMN:
case TK_COLUMN: {
@@ -107847,10 +114264,13 @@ 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 && iCol<pS->pEList->nExpr ){
+ if( iCol>=0 && ALWAYS(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.
+ **
+ ** The ALWAYS() is because iCol>=pS->pEList->nExpr will have been
+ ** caught already by name resolution.
*/
NameContext sNC;
Expr *p = pS->pEList->a[iCol].pExpr;
@@ -107869,8 +114289,8 @@ static const char *columnTypeImpl(
zType = "INTEGER";
zOrigCol = "rowid";
}else{
- zType = pTab->aCol[iCol].zType;
zOrigCol = pTab->aCol[iCol].zName;
+ zType = sqlite3ColumnType(&pTab->aCol[iCol],0);
estWidth = pTab->aCol[iCol].szEst;
}
zOrigTab = pTab->zName;
@@ -107882,7 +114302,7 @@ static const char *columnTypeImpl(
if( iCol<0 ){
zType = "INTEGER";
}else{
- zType = pTab->aCol[iCol].zType;
+ zType = sqlite3ColumnType(&pTab->aCol[iCol],0);
estWidth = pTab->aCol[iCol].szEst;
}
#endif
@@ -107981,7 +114401,9 @@ static void generateColumnNames(
}
#endif
- if( pParse->colNamesSet || NEVER(v==0) || db->mallocFailed ) return;
+ if( pParse->colNamesSet || db->mallocFailed ) return;
+ assert( v!=0 );
+ assert( pTabList!=0 );
pParse->colNamesSet = 1;
fullNames = (db->flags & SQLITE_FullColNames)!=0;
shortNames = (db->flags & SQLITE_ShortColNames)!=0;
@@ -107993,7 +114415,7 @@ static void generateColumnNames(
if( pEList->a[i].zName ){
char *zName = pEList->a[i].zName;
sqlite3VdbeSetColName(v, i, COLNAME_NAME, zName, SQLITE_TRANSIENT);
- }else if( (p->op==TK_COLUMN || p->op==TK_AGG_COLUMN) && pTabList ){
+ }else if( p->op==TK_COLUMN || p->op==TK_AGG_COLUMN ){
Table *pTab;
char *zCol;
int iCol = p->iColumn;
@@ -108041,7 +114463,7 @@ static void generateColumnNames(
** Return SQLITE_OK on success. If a memory allocation error occurs,
** store NULL in *paCol and 0 in *pnCol and return SQLITE_NOMEM.
*/
-static int selectColumnsFromExprList(
+SQLITE_PRIVATE int sqlite3ColumnsFromExprList(
Parse *pParse, /* Parsing context */
ExprList *pEList, /* Expr list from which to derive column names */
i16 *pnCol, /* Write the number of columns here */
@@ -108049,13 +114471,15 @@ static int selectColumnsFromExprList(
){
sqlite3 *db = pParse->db; /* Database connection */
int i, j; /* Loop counters */
- int cnt; /* Index added to make the name unique */
+ u32 cnt; /* Index added to make the name unique */
Column *aCol, *pCol; /* For looping over result columns */
int nCol; /* Number of columns in the result set */
Expr *p; /* Expression for a single result column */
char *zName; /* Column name */
int nName; /* Size of name in zName[] */
+ Hash ht; /* Hash table of column names */
+ sqlite3HashInit(&ht);
if( pEList ){
nCol = pEList->nExpr;
aCol = sqlite3DbMallocZero(db, sizeof(aCol[0])*nCol);
@@ -108064,16 +114488,16 @@ static int selectColumnsFromExprList(
nCol = 0;
aCol = 0;
}
+ assert( nCol==(i16)nCol );
*pnCol = nCol;
*paCol = aCol;
- for(i=0, pCol=aCol; i<nCol; i++, pCol++){
+ for(i=0, pCol=aCol; i<nCol && !db->mallocFailed; i++, pCol++){
/* Get an appropriate name for the column
*/
p = sqlite3ExprSkipCollate(pEList->a[i].pExpr);
if( (zName = pEList->a[i].zName)!=0 ){
/* If the column contains an "AS <name>" phrase, use <name> as the name */
- zName = sqlite3DbStrDup(db, zName);
}else{
Expr *pColExpr = p; /* The expression that is the result column name */
Table *pTab; /* Table associated with this expression */
@@ -108086,41 +114510,37 @@ static int selectColumnsFromExprList(
int iCol = pColExpr->iColumn;
pTab = pColExpr->pTab;
if( iCol<0 ) iCol = pTab->iPKey;
- zName = sqlite3MPrintf(db, "%s",
- iCol>=0 ? pTab->aCol[iCol].zName : "rowid");
+ zName = iCol>=0 ? pTab->aCol[iCol].zName : "rowid";
}else if( pColExpr->op==TK_ID ){
assert( !ExprHasProperty(pColExpr, EP_IntValue) );
- zName = sqlite3MPrintf(db, "%s", pColExpr->u.zToken);
+ zName = pColExpr->u.zToken;
}else{
/* Use the original text of the column expression as its name */
- zName = sqlite3MPrintf(db, "%s", pEList->a[i].zSpan);
+ zName = pEList->a[i].zSpan;
}
}
- if( db->mallocFailed ){
- sqlite3DbFree(db, zName);
- break;
- }
+ zName = sqlite3MPrintf(db, "%s", zName);
/* Make sure the column name is unique. If the name is not unique,
** append an integer to the name so that it becomes unique.
*/
- nName = sqlite3Strlen30(zName);
- for(j=cnt=0; j<i; j++){
- if( sqlite3StrICmp(aCol[j].zName, zName)==0 ){
- char *zNewName;
- int k;
- for(k=nName-1; k>1 && sqlite3Isdigit(zName[k]); k--){}
- if( k>=0 && zName[k]==':' ) nName = k;
- zName[nName] = 0;
- zNewName = sqlite3MPrintf(db, "%s:%d", zName, ++cnt);
- sqlite3DbFree(db, zName);
- zName = zNewName;
- j = -1;
- if( zName==0 ) break;
+ cnt = 0;
+ while( zName && sqlite3HashFind(&ht, zName)!=0 ){
+ nName = sqlite3Strlen30(zName);
+ if( nName>0 ){
+ for(j=nName-1; j>0 && sqlite3Isdigit(zName[j]); j--){}
+ if( zName[j]==':' ) nName = j;
}
+ zName = sqlite3MPrintf(db, "%.*z:%u", nName, zName, ++cnt);
+ if( cnt>3 ) sqlite3_randomness(sizeof(cnt), &cnt);
}
pCol->zName = zName;
+ sqlite3ColumnPropertiesFromName(0, pCol);
+ if( zName && sqlite3HashInsert(&ht, zName, pCol)==pCol ){
+ sqlite3OomFault(db);
+ }
}
+ sqlite3HashClear(&ht);
if( db->mallocFailed ){
for(j=0; j<i; j++){
sqlite3DbFree(db, aCol[j].zName);
@@ -108128,7 +114548,7 @@ static int selectColumnsFromExprList(
sqlite3DbFree(db, aCol);
*paCol = 0;
*pnCol = 0;
- return SQLITE_NOMEM;
+ return SQLITE_NOMEM_BKPT;
}
return SQLITE_OK;
}
@@ -108144,7 +114564,7 @@ static int selectColumnsFromExprList(
** This routine requires that all identifiers in the SELECT
** statement be resolved.
*/
-static void selectAddColumnTypeAndCollation(
+SQLITE_PRIVATE void sqlite3SelectAddColumnTypeAndCollation(
Parse *pParse, /* Parsing contexts */
Table *pTab, /* Add column type information to this table */
Select *pSelect /* SELECT used to determine types and collations */
@@ -108166,13 +114586,21 @@ static void selectAddColumnTypeAndCollation(
sNC.pSrcList = pSelect->pSrc;
a = pSelect->pEList->a;
for(i=0, pCol=pTab->aCol; i<pTab->nCol; i++, pCol++){
+ const char *zType;
+ int n, m;
p = a[i].pExpr;
- if( pCol->zType==0 ){
- pCol->zType = sqlite3DbStrDup(db, columnType(&sNC, p,0,0,0, &pCol->szEst));
- }
+ zType = columnType(&sNC, p, 0, 0, 0, &pCol->szEst);
szAll += pCol->szEst;
pCol->affinity = sqlite3ExprAffinity(p);
- if( pCol->affinity==0 ) pCol->affinity = SQLITE_AFF_NONE;
+ if( zType && (m = sqlite3Strlen30(zType))>0 ){
+ n = sqlite3Strlen30(pCol->zName);
+ pCol->zName = sqlite3DbReallocOrFree(db, pCol->zName, n+m+2);
+ if( pCol->zName ){
+ memcpy(&pCol->zName[n+1], zType, m+1);
+ pCol->colFlags |= COLFLAG_HASTYPE;
+ }
+ }
+ if( pCol->affinity==0 ) pCol->affinity = SQLITE_AFF_BLOB;
pColl = sqlite3ExprCollSeq(pParse, p);
if( pColl && pCol->zColl==0 ){
pCol->zColl = sqlite3DbStrDup(db, pColl->zName);
@@ -108203,12 +114631,12 @@ SQLITE_PRIVATE Table *sqlite3ResultSetOfSelect(Parse *pParse, Select *pSelect){
}
/* The sqlite3ResultSetOfSelect() is only used n contexts where lookaside
** is disabled */
- assert( db->lookaside.bEnabled==0 );
+ assert( db->lookaside.bDisable );
pTab->nRef = 1;
pTab->zName = 0;
pTab->nRowLogEst = 200; assert( 200==sqlite3LogEst(1048576) );
- selectColumnsFromExprList(pParse, pSelect->pEList, &pTab->nCol, &pTab->aCol);
- selectAddColumnTypeAndCollation(pParse, pTab, pSelect);
+ sqlite3ColumnsFromExprList(pParse, pSelect->pEList, &pTab->nCol, &pTab->aCol);
+ sqlite3SelectAddColumnTypeAndCollation(pParse, pTab, pSelect);
pTab->iPKey = -1;
if( db->mallocFailed ){
sqlite3DeleteTable(db, pTab);
@@ -108221,20 +114649,20 @@ SQLITE_PRIVATE Table *sqlite3ResultSetOfSelect(Parse *pParse, Select *pSelect){
** Get a VDBE for the given parser context. Create a new one if necessary.
** If an error occurs, return NULL and leave a message in pParse.
*/
-SQLITE_PRIVATE Vdbe *sqlite3GetVdbe(Parse *pParse){
- Vdbe *v = pParse->pVdbe;
- if( v==0 ){
- v = pParse->pVdbe = sqlite3VdbeCreate(pParse);
- if( v ) sqlite3VdbeAddOp0(v, OP_Init);
- if( pParse->pToplevel==0
- && OptimizationEnabled(pParse->db,SQLITE_FactorOutConst)
- ){
- pParse->okConstFactor = 1;
- }
-
+static SQLITE_NOINLINE Vdbe *allocVdbe(Parse *pParse){
+ Vdbe *v = pParse->pVdbe = sqlite3VdbeCreate(pParse);
+ if( v ) sqlite3VdbeAddOp0(v, OP_Init);
+ if( pParse->pToplevel==0
+ && OptimizationEnabled(pParse->db,SQLITE_FactorOutConst)
+ ){
+ pParse->okConstFactor = 1;
}
return v;
}
+SQLITE_PRIVATE Vdbe *sqlite3GetVdbe(Parse *pParse){
+ Vdbe *v = pParse->pVdbe;
+ return v ? v : allocVdbe(pParse);
+}
/*
@@ -108264,7 +114692,7 @@ static void computeLimitRegisters(Parse *pParse, Select *p, int iBreak){
Vdbe *v = 0;
int iLimit = 0;
int iOffset;
- int addr1, n;
+ int n;
if( p->iLimit ) return;
/*
@@ -108283,9 +114711,10 @@ static void computeLimitRegisters(Parse *pParse, Select *p, int iBreak){
sqlite3VdbeAddOp2(v, OP_Integer, n, iLimit);
VdbeComment((v, "LIMIT counter"));
if( n==0 ){
- sqlite3VdbeAddOp2(v, OP_Goto, 0, iBreak);
- }else if( n>=0 && p->nSelectRow>(u64)n ){
- p->nSelectRow = n;
+ sqlite3VdbeGoto(v, iBreak);
+ }else if( n>=0 && p->nSelectRow>sqlite3LogEst((u64)n) ){
+ p->nSelectRow = sqlite3LogEst((u64)n);
+ p->selFlags |= SF_FixedLimit;
}
}else{
sqlite3ExprCode(pParse, p->pLimit, iLimit);
@@ -108299,14 +114728,8 @@ static void computeLimitRegisters(Parse *pParse, Select *p, int iBreak){
sqlite3ExprCode(pParse, p->pOffset, iOffset);
sqlite3VdbeAddOp1(v, OP_MustBeInt, iOffset); VdbeCoverage(v);
VdbeComment((v, "OFFSET counter"));
- addr1 = sqlite3VdbeAddOp1(v, OP_IfPos, iOffset); VdbeCoverage(v);
- sqlite3VdbeAddOp2(v, OP_Integer, 0, iOffset);
- sqlite3VdbeJumpHere(v, addr1);
- sqlite3VdbeAddOp3(v, OP_Add, iLimit, iOffset, iOffset+1);
+ sqlite3VdbeAddOp3(v, OP_OffsetLimit, iLimit, iOffset+1, iOffset);
VdbeComment((v, "LIMIT+OFFSET"));
- addr1 = sqlite3VdbeAddOp1(v, OP_IfPos, iLimit); VdbeCoverage(v);
- sqlite3VdbeAddOp2(v, OP_Integer, -1, iOffset+1);
- sqlite3VdbeJumpHere(v, addr1);
}
}
}
@@ -108328,7 +114751,10 @@ static CollSeq *multiSelectCollSeq(Parse *pParse, Select *p, int iCol){
pRet = 0;
}
assert( iCol>=0 );
- if( pRet==0 && iCol<p->pEList->nExpr ){
+ /* iCol must be less than p->pEList->nExpr. Otherwise an error would
+ ** have been thrown during name resolution and we would not have gotten
+ ** this far */
+ if( pRet==0 && ALWAYS(iCol<p->pEList->nExpr) ){
pRet = sqlite3ExprCollSeq(pParse, p->pEList->a[iCol].pExpr);
}
return pRet;
@@ -108383,7 +114809,7 @@ static KeyInfo *multiSelectOrderByKeyInfo(Parse *pParse, Select *p, int nExtra){
**
**
** There is exactly one reference to the recursive-table in the FROM clause
-** of recursive-query, marked with the SrcList->a[].isRecursive flag.
+** of recursive-query, marked with the SrcList->a[].fg.isRecursive flag.
**
** The setup-query runs once to generate an initial set of rows that go
** into a Queue table. Rows are extracted from the Queue table one by
@@ -108448,7 +114874,7 @@ static void generateWithRecursiveQuery(
/* Locate the cursor number of the Current table */
for(i=0; ALWAYS(i<pSrc->nSrc); i++){
- if( pSrc->a[i].isRecursive ){
+ if( pSrc->a[i].fg.isRecursive ){
iCurrent = pSrc->a[i].iCursor;
break;
}
@@ -108518,13 +114944,17 @@ static void generateWithRecursiveQuery(
/* Execute the recursive SELECT taking the single row in Current as
** the value for the recursive-table. Store the results in the Queue.
*/
- p->pPrior = 0;
- sqlite3Select(pParse, p, &destQueue);
- assert( p->pPrior==0 );
- p->pPrior = pSetup;
+ if( p->selFlags & SF_Aggregate ){
+ sqlite3ErrorMsg(pParse, "recursive aggregate queries not supported");
+ }else{
+ p->pPrior = 0;
+ sqlite3Select(pParse, p, &destQueue);
+ assert( p->pPrior==0 );
+ p->pPrior = pSetup;
+ }
/* Keep running the loop until the Queue is empty */
- sqlite3VdbeAddOp2(v, OP_Goto, 0, addrTop);
+ sqlite3VdbeGoto(v, addrTop);
sqlite3VdbeResolveLabel(v, addrBreak);
end_of_recursive_query:
@@ -108544,19 +114974,6 @@ static int multiSelectOrderBy(
);
/*
-** Error message for when two or more terms of a compound select have different
-** size result sets.
-*/
-static void selectWrongNumTermsError(Parse *pParse, Select *p){
- if( p->selFlags & SF_Values ){
- sqlite3ErrorMsg(pParse, "all VALUES must have the same number of terms");
- }else{
- sqlite3ErrorMsg(pParse, "SELECTs to the left and right of %s"
- " do not have the same number of result columns", selectOpName(p->op));
- }
-}
-
-/*
** Handle the special case of a compound-select that originates from a
** VALUES clause. By handling this as a special case, we avoid deep
** recursion, and thus do not need to enforce the SQLITE_LIMIT_COMPOUND_SELECT
@@ -108573,7 +114990,6 @@ static int multiSelectValues(
SelectDest *pDest /* What to do with query results */
){
Select *pPrior;
- int nExpr = p->pEList->nExpr;
int nRow = 1;
int rc = 0;
assert( p->selFlags & SF_MultiValue );
@@ -108582,10 +114998,7 @@ static int multiSelectValues(
assert( p->op==TK_ALL || (p->op==TK_SELECT && p->pPrior==0) );
assert( p->pLimit==0 );
assert( p->pOffset==0 );
- if( p->pEList->nExpr!=nExpr ){
- selectWrongNumTermsError(pParse, p);
- return 1;
- }
+ assert( p->pNext==0 || p->pEList->nExpr==p->pNext->pEList->nExpr );
if( p->pPrior==0 ) break;
assert( p->pPrior->pNext==p );
p = p->pPrior;
@@ -108679,7 +115092,6 @@ static int multiSelect(
if( dest.eDest==SRT_EphemTab ){
assert( p->pEList );
sqlite3VdbeAddOp2(v, OP_OpenEphemeral, dest.iSDParm, p->pEList->nExpr);
- sqlite3VdbeChangeP5(v, BTREE_UNORDERED);
dest.eDest = SRT_Table;
}
@@ -108694,11 +115106,7 @@ static int multiSelect(
** in their result sets.
*/
assert( p->pEList && pPrior->pEList );
- if( p->pEList->nExpr!=pPrior->pEList->nExpr ){
- selectWrongNumTermsError(pParse, p);
- rc = 1;
- goto multi_select_end;
- }
+ assert( p->pEList->nExpr==pPrior->pEList->nExpr );
#ifndef SQLITE_OMIT_CTE
if( p->selFlags & SF_Recursive ){
@@ -108736,18 +115144,22 @@ static int multiSelect(
if( p->iLimit ){
addr = sqlite3VdbeAddOp1(v, OP_IfNot, p->iLimit); VdbeCoverage(v);
VdbeComment((v, "Jump ahead if LIMIT reached"));
+ if( p->iOffset ){
+ sqlite3VdbeAddOp3(v, OP_OffsetLimit,
+ p->iLimit, p->iOffset+1, p->iOffset);
+ }
}
explainSetInteger(iSub2, pParse->iNextSelectId);
rc = sqlite3Select(pParse, p, &dest);
testcase( rc!=SQLITE_OK );
pDelete = p->pPrior;
p->pPrior = pPrior;
- p->nSelectRow += pPrior->nSelectRow;
+ p->nSelectRow = sqlite3LogEstAdd(p->nSelectRow, pPrior->nSelectRow);
if( pPrior->pLimit
&& sqlite3ExprIsInteger(pPrior->pLimit, &nLimit)
- && nLimit>0 && p->nSelectRow > (u64)nLimit
+ && nLimit>0 && p->nSelectRow > sqlite3LogEst((u64)nLimit)
){
- p->nSelectRow = nLimit;
+ p->nSelectRow = sqlite3LogEst((u64)nLimit);
}
if( addr ){
sqlite3VdbeJumpHere(v, addr);
@@ -108819,7 +115231,9 @@ static int multiSelect(
pDelete = p->pPrior;
p->pPrior = pPrior;
p->pOrderBy = 0;
- if( p->op==TK_UNION ) p->nSelectRow += pPrior->nSelectRow;
+ if( p->op==TK_UNION ){
+ p->nSelectRow = sqlite3LogEstAdd(p->nSelectRow, pPrior->nSelectRow);
+ }
sqlite3ExprDelete(db, p->pLimit);
p->pLimit = pLimit;
p->pOffset = pOffset;
@@ -108836,7 +115250,7 @@ static int multiSelect(
if( dest.eDest==SRT_Output ){
Select *pFirst = p;
while( pFirst->pPrior ) pFirst = pFirst->pPrior;
- generateColumnNames(pParse, 0, pFirst->pEList);
+ generateColumnNames(pParse, pFirst->pSrc, pFirst->pEList);
}
iBreak = sqlite3VdbeMakeLabel(v);
iCont = sqlite3VdbeMakeLabel(v);
@@ -108911,7 +115325,7 @@ static int multiSelect(
if( dest.eDest==SRT_Output ){
Select *pFirst = p;
while( pFirst->pPrior ) pFirst = pFirst->pPrior;
- generateColumnNames(pParse, 0, pFirst->pEList);
+ generateColumnNames(pParse, pFirst->pSrc, pFirst->pEList);
}
iBreak = sqlite3VdbeMakeLabel(v);
iCont = sqlite3VdbeMakeLabel(v);
@@ -108954,7 +115368,7 @@ static int multiSelect(
nCol = p->pEList->nExpr;
pKeyInfo = sqlite3KeyInfoAlloc(db, nCol, 1);
if( !pKeyInfo ){
- rc = SQLITE_NOMEM;
+ rc = SQLITE_NOMEM_BKPT;
goto multi_select_end;
}
for(i=0, apColl=pKeyInfo->aColl; i<nCol; i++, apColl++){
@@ -108991,6 +115405,19 @@ multi_select_end:
#endif /* SQLITE_OMIT_COMPOUND_SELECT */
/*
+** Error message for when two or more terms of a compound select have different
+** size result sets.
+*/
+SQLITE_PRIVATE void sqlite3SelectWrongNumTermsError(Parse *pParse, Select *p){
+ if( p->selFlags & SF_Values ){
+ sqlite3ErrorMsg(pParse, "all VALUES must have the same number of terms");
+ }else{
+ sqlite3ErrorMsg(pParse, "SELECTs to the left and right of %s"
+ " do not have the same number of result columns", selectOpName(p->op));
+ }
+}
+
+/*
** Code an output subroutine for a coroutine implementation of a
** SELECT statment.
**
@@ -109030,12 +115457,12 @@ static int generateOutputSubroutine(
/* Suppress duplicates for UNION, EXCEPT, and INTERSECT
*/
if( regPrev ){
- int j1, j2;
- j1 = sqlite3VdbeAddOp1(v, OP_IfNot, regPrev); VdbeCoverage(v);
- j2 = sqlite3VdbeAddOp4(v, OP_Compare, pIn->iSdst, regPrev+1, pIn->nSdst,
+ int addr1, addr2;
+ addr1 = sqlite3VdbeAddOp1(v, OP_IfNot, regPrev); VdbeCoverage(v);
+ addr2 = sqlite3VdbeAddOp4(v, OP_Compare, pIn->iSdst, regPrev+1, pIn->nSdst,
(char*)sqlite3KeyInfoRef(pKeyInfo), P4_KEYINFO);
- sqlite3VdbeAddOp3(v, OP_Jump, j2+2, iContinue, j2+2); VdbeCoverage(v);
- sqlite3VdbeJumpHere(v, j1);
+ sqlite3VdbeAddOp3(v, OP_Jump, addr2+2, iContinue, addr2+2); VdbeCoverage(v);
+ sqlite3VdbeJumpHere(v, addr1);
sqlite3VdbeAddOp3(v, OP_Copy, pIn->iSdst, regPrev+1, pIn->nSdst-1);
sqlite3VdbeAddOp2(v, OP_Integer, 1, regPrev);
}
@@ -109045,15 +115472,14 @@ static int generateOutputSubroutine(
*/
codeOffset(v, p->iOffset, iContinue);
+ assert( pDest->eDest!=SRT_Exists );
+ assert( pDest->eDest!=SRT_Table );
switch( pDest->eDest ){
/* Store the result as data using a unique key.
*/
- case SRT_Table:
case SRT_EphemTab: {
int r1 = sqlite3GetTempReg(pParse);
int r2 = sqlite3GetTempReg(pParse);
- testcase( pDest->eDest==SRT_Table );
- testcase( pDest->eDest==SRT_EphemTab );
sqlite3VdbeAddOp3(v, OP_MakeRecord, pIn->iSdst, pIn->nSdst, r1);
sqlite3VdbeAddOp2(v, OP_NewRowid, pDest->iSDParm, r2);
sqlite3VdbeAddOp3(v, OP_Insert, pDest->iSDParm, r1, r2);
@@ -109081,16 +115507,6 @@ static int generateOutputSubroutine(
break;
}
-#if 0 /* Never occurs on an ORDER BY query */
- /* If any row exist in the result set, record that fact and abort.
- */
- case SRT_Exists: {
- sqlite3VdbeAddOp2(v, OP_Integer, 1, pDest->iSDParm);
- /* The LIMIT clause will terminate the loop for us */
- break;
- }
-#endif
-
/* If this is a scalar select that is part of an expression, then
** store the results in the appropriate memory cell and break out
** of the scan loop.
@@ -109263,7 +115679,7 @@ static int multiSelectOrderBy(
int savedOffset; /* Saved value of p->iOffset */
int labelCmpr; /* Label for the start of the merge algorithm */
int labelEnd; /* Label for the end of the overall SELECT stmt */
- int j1; /* Jump instructions that get retargetted */
+ int addr1; /* Jump instructions that get retargetted */
int op; /* One of TK_ALL, TK_UNION, TK_EXCEPT, TK_INTERSECT */
KeyInfo *pKeyDup = 0; /* Comparison information for duplicate removal */
KeyInfo *pKeyMerge; /* Comparison information for merging rows */
@@ -109307,7 +115723,7 @@ static int multiSelectOrderBy(
}
if( j==nOrderBy ){
Expr *pNew = sqlite3Expr(db, TK_INTEGER, 0);
- if( pNew==0 ) return SQLITE_NOMEM;
+ if( pNew==0 ) return SQLITE_NOMEM_BKPT;
pNew->flags |= EP_IntValue;
pNew->u.iValue = i;
pOrderBy = sqlite3ExprListAppend(pParse, pOrderBy, pNew);
@@ -109323,14 +115739,13 @@ static int multiSelectOrderBy(
** to the right and the left are evaluated, they use the correct
** collation.
*/
- aPermute = sqlite3DbMallocRaw(db, sizeof(int)*nOrderBy);
+ aPermute = sqlite3DbMallocRawNN(db, sizeof(int)*(nOrderBy + 1));
if( aPermute ){
struct ExprList_item *pItem;
- for(i=0, pItem=pOrderBy->a; i<nOrderBy; i++, pItem++){
+ aPermute[0] = nOrderBy;
+ for(i=1, pItem=pOrderBy->a; i<=nOrderBy; i++, pItem++){
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 );
+ assert( pItem->u.x.iOrderByCol<=p->pEList->nExpr );
aPermute[i] = pItem->u.x.iOrderByCol - 1;
}
pKeyMerge = multiSelectOrderByKeyInfo(pParse, p, 1);
@@ -109401,19 +115816,19 @@ static int multiSelectOrderBy(
** left of the compound operator - the "A" select.
*/
addrSelectA = sqlite3VdbeCurrentAddr(v) + 1;
- j1 = sqlite3VdbeAddOp3(v, OP_InitCoroutine, regAddrA, 0, addrSelectA);
+ addr1 = sqlite3VdbeAddOp3(v, OP_InitCoroutine, regAddrA, 0, addrSelectA);
VdbeComment((v, "left SELECT"));
pPrior->iLimit = regLimitA;
explainSetInteger(iSub1, pParse->iNextSelectId);
sqlite3Select(pParse, pPrior, &destA);
- sqlite3VdbeAddOp1(v, OP_EndCoroutine, regAddrA);
- sqlite3VdbeJumpHere(v, j1);
+ sqlite3VdbeEndCoroutine(v, regAddrA);
+ sqlite3VdbeJumpHere(v, addr1);
/* Generate a coroutine to evaluate the SELECT statement on
** the right - the "B" select
*/
addrSelectB = sqlite3VdbeCurrentAddr(v) + 1;
- j1 = sqlite3VdbeAddOp3(v, OP_InitCoroutine, regAddrB, 0, addrSelectB);
+ addr1 = sqlite3VdbeAddOp3(v, OP_InitCoroutine, regAddrB, 0, addrSelectB);
VdbeComment((v, "right SELECT"));
savedLimit = p->iLimit;
savedOffset = p->iOffset;
@@ -109423,7 +115838,7 @@ static int multiSelectOrderBy(
sqlite3Select(pParse, p, &destB);
p->iLimit = savedLimit;
p->iOffset = savedOffset;
- sqlite3VdbeAddOp1(v, OP_EndCoroutine, regAddrB);
+ sqlite3VdbeEndCoroutine(v, regAddrB);
/* Generate a subroutine that outputs the current row of the A
** select as the next output row of the compound select.
@@ -109454,8 +115869,8 @@ static int multiSelectOrderBy(
addrEofA = sqlite3VdbeAddOp2(v, OP_Gosub, regOutB, addrOutB);
addrEofA_noB = sqlite3VdbeAddOp2(v, OP_Yield, regAddrB, labelEnd);
VdbeCoverage(v);
- sqlite3VdbeAddOp2(v, OP_Goto, 0, addrEofA);
- p->nSelectRow += pPrior->nSelectRow;
+ sqlite3VdbeGoto(v, addrEofA);
+ p->nSelectRow = sqlite3LogEstAdd(p->nSelectRow, pPrior->nSelectRow);
}
/* Generate a subroutine to run when the results from select B
@@ -109468,7 +115883,7 @@ static int multiSelectOrderBy(
VdbeNoopComment((v, "eof-B subroutine"));
addrEofB = sqlite3VdbeAddOp2(v, OP_Gosub, regOutA, addrOutA);
sqlite3VdbeAddOp2(v, OP_Yield, regAddrA, labelEnd); VdbeCoverage(v);
- sqlite3VdbeAddOp2(v, OP_Goto, 0, addrEofB);
+ sqlite3VdbeGoto(v, addrEofB);
}
/* Generate code to handle the case of A<B
@@ -109476,7 +115891,7 @@ static int multiSelectOrderBy(
VdbeNoopComment((v, "A-lt-B subroutine"));
addrAltB = sqlite3VdbeAddOp2(v, OP_Gosub, regOutA, addrOutA);
sqlite3VdbeAddOp2(v, OP_Yield, regAddrA, addrEofA); VdbeCoverage(v);
- sqlite3VdbeAddOp2(v, OP_Goto, 0, labelCmpr);
+ sqlite3VdbeGoto(v, labelCmpr);
/* Generate code to handle the case of A==B
*/
@@ -109489,7 +115904,7 @@ static int multiSelectOrderBy(
VdbeNoopComment((v, "A-eq-B subroutine"));
addrAeqB =
sqlite3VdbeAddOp2(v, OP_Yield, regAddrA, addrEofA); VdbeCoverage(v);
- sqlite3VdbeAddOp2(v, OP_Goto, 0, labelCmpr);
+ sqlite3VdbeGoto(v, labelCmpr);
}
/* Generate code to handle the case of A>B
@@ -109500,11 +115915,11 @@ static int multiSelectOrderBy(
sqlite3VdbeAddOp2(v, OP_Gosub, regOutB, addrOutB);
}
sqlite3VdbeAddOp2(v, OP_Yield, regAddrB, addrEofB); VdbeCoverage(v);
- sqlite3VdbeAddOp2(v, OP_Goto, 0, labelCmpr);
+ sqlite3VdbeGoto(v, labelCmpr);
/* This code runs once to initialize everything.
*/
- sqlite3VdbeJumpHere(v, j1);
+ sqlite3VdbeJumpHere(v, addr1);
sqlite3VdbeAddOp2(v, OP_Yield, regAddrA, addrEofA_noB); VdbeCoverage(v);
sqlite3VdbeAddOp2(v, OP_Yield, regAddrB, addrEofB); VdbeCoverage(v);
@@ -109526,7 +115941,7 @@ static int multiSelectOrderBy(
if( pDest->eDest==SRT_Output ){
Select *pFirst = pPrior;
while( pFirst->pPrior ) pFirst = pFirst->pPrior;
- generateColumnNames(pParse, 0, pFirst->pEList);
+ generateColumnNames(pParse, pFirst->pSrc, pFirst->pEList);
}
/* Reassembly the compound query so that it will be freed correctly
@@ -109547,7 +115962,7 @@ static int multiSelectOrderBy(
#if !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW)
/* Forward Declarations */
static void substExprList(sqlite3*, ExprList*, int, ExprList*);
-static void substSelect(sqlite3*, Select *, int, ExprList *);
+static void substSelect(sqlite3*, Select *, int, ExprList*, int);
/*
** Scan through the expression pExpr. Replace every reference to
@@ -109584,7 +115999,7 @@ static Expr *substExpr(
pExpr->pLeft = substExpr(db, pExpr->pLeft, iTable, pEList);
pExpr->pRight = substExpr(db, pExpr->pRight, iTable, pEList);
if( ExprHasProperty(pExpr, EP_xIsSelect) ){
- substSelect(db, pExpr->x.pSelect, iTable, pEList);
+ substSelect(db, pExpr->x.pSelect, iTable, pEList, 1);
}else{
substExprList(db, pExpr->x.pList, iTable, pEList);
}
@@ -109607,25 +116022,28 @@ static void substSelect(
sqlite3 *db, /* Report malloc errors here */
Select *p, /* SELECT statement in which to make substitutions */
int iTable, /* Table to be replaced */
- ExprList *pEList /* Substitute values */
+ ExprList *pEList, /* Substitute values */
+ int doPrior /* Do substitutes on p->pPrior too */
){
SrcList *pSrc;
struct SrcList_item *pItem;
int i;
if( !p ) return;
- substExprList(db, p->pEList, iTable, pEList);
- substExprList(db, p->pGroupBy, iTable, pEList);
- substExprList(db, p->pOrderBy, iTable, pEList);
- p->pHaving = substExpr(db, p->pHaving, iTable, pEList);
- p->pWhere = substExpr(db, p->pWhere, iTable, pEList);
- substSelect(db, p->pPrior, iTable, pEList);
- pSrc = p->pSrc;
- assert( pSrc ); /* Even for (SELECT 1) we have: pSrc!=0 but pSrc->nSrc==0 */
- if( ALWAYS(pSrc) ){
+ do{
+ substExprList(db, p->pEList, iTable, pEList);
+ substExprList(db, p->pGroupBy, iTable, pEList);
+ substExprList(db, p->pOrderBy, iTable, pEList);
+ p->pHaving = substExpr(db, p->pHaving, iTable, pEList);
+ p->pWhere = substExpr(db, p->pWhere, iTable, pEList);
+ pSrc = p->pSrc;
+ assert( pSrc!=0 );
for(i=pSrc->nSrc, pItem=pSrc->a; i>0; i--, pItem++){
- substSelect(db, pItem->pSelect, iTable, pEList);
+ substSelect(db, pItem->pSelect, iTable, pEList, 1);
+ if( pItem->fg.isTabFunc ){
+ substExprList(db, pItem->u1.pFuncArg, iTable, pEList);
+ }
}
- }
+ }while( doPrior && (p = p->pPrior)!=0 );
}
#endif /* !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) */
@@ -109689,8 +116107,8 @@ static void substSelect(
**
** (**) Restriction (10) was removed from the code on 2005-02-05 but we
** accidently carried the comment forward until 2014-09-15. Original
-** text: "The subquery does not use aggregates or the outer query does not
-** use LIMIT."
+** text: "The subquery does not use aggregates or the outer query
+** does not use LIMIT."
**
** (11) The subquery and the outer query do not both have ORDER BY clauses.
**
@@ -109777,7 +116195,7 @@ static int flattenSubquery(
int subqueryIsAgg /* True if the subquery uses aggregate functions */
){
const char *zSavedAuthContext = pParse->zAuthContext;
- Select *pParent;
+ Select *pParent; /* Current UNION ALL term of the other query */
Select *pSub; /* The inner query or "subquery" */
Select *pSub1; /* Pointer to the rightmost select in sub-query */
SrcList *pSrc; /* The FROM clause of the outer query */
@@ -109880,7 +116298,7 @@ static int flattenSubquery(
** is fraught with danger. Best to avoid the whole thing. If the
** subquery is the right term of a LEFT JOIN, then do not flatten.
*/
- if( (pSubitem->jointype & JT_OUTER)!=0 ){
+ if( (pSubitem->fg.jointype & JT_OUTER)!=0 ){
return 0;
}
@@ -109900,10 +116318,10 @@ static int flattenSubquery(
testcase( (pSub1->selFlags & (SF_Distinct|SF_Aggregate))==SF_Distinct );
testcase( (pSub1->selFlags & (SF_Distinct|SF_Aggregate))==SF_Aggregate );
assert( pSub->pSrc!=0 );
+ assert( pSub->pEList->nExpr==pSub1->pEList->nExpr );
if( (pSub1->selFlags & (SF_Distinct|SF_Aggregate))!=0
|| (pSub1->pPrior && pSub1->op!=TK_ALL)
|| pSub1->pSrc->nSrc<1
- || pSub->pEList->nExpr!=pSub1->pEList->nExpr
){
return 0;
}
@@ -110051,7 +116469,7 @@ static int flattenSubquery(
if( pSrc ){
assert( pParent==p ); /* First time through the loop */
- jointype = pSubitem->jointype;
+ jointype = pSubitem->fg.jointype;
}else{
assert( pParent!=p ); /* 2nd and subsequent times through the loop */
pSrc = pParent->pSrc = sqlite3SrcListAppend(db, 0, 0, 0);
@@ -110072,9 +116490,9 @@ static int flattenSubquery(
**
** The outer query has 3 slots in its FROM clause. One slot of the
** outer query (the middle slot) is used by the subquery. The next
- ** block of code will expand the out query to 4 slots. The middle
- ** slot is expanded to two slots in order to make space for the
- ** two elements in the FROM clause of the subquery.
+ ** block of code will expand the outer query FROM clause to 4 slots.
+ ** The middle slot is expanded to two slots in order to make space
+ ** for the two elements in the FROM clause of the subquery.
*/
if( nSubSrc>1 ){
pParent->pSrc = pSrc = sqlite3SrcListEnlarge(db, pSrc, nSubSrc-1,iFrom+1);
@@ -110088,10 +116506,11 @@ static int flattenSubquery(
*/
for(i=0; i<nSubSrc; i++){
sqlite3IdListDelete(db, pSrc->a[i+iFrom].pUsing);
+ assert( pSrc->a[i+iFrom].fg.isTabFunc==0 );
pSrc->a[i+iFrom] = pSubSrc->a[i];
memset(&pSubSrc->a[i], 0, sizeof(pSubSrc->a[i]));
}
- pSrc->a[iFrom].jointype = jointype;
+ pSrc->a[iFrom].fg.jointype = jointype;
/* Now begin substituting subquery result set expressions for
** references to the iParent in the outer query.
@@ -110113,17 +116532,12 @@ static int flattenSubquery(
pList->a[i].zName = zName;
}
}
- substExprList(db, pParent->pEList, iParent, pSub->pEList);
- if( isAgg ){
- substExprList(db, pParent->pGroupBy, iParent, pSub->pEList);
- pParent->pHaving = substExpr(db, pParent->pHaving, iParent, pSub->pEList);
- }
if( pSub->pOrderBy ){
/* At this point, any non-zero iOrderByCol values indicate that the
** ORDER BY column expression is identical to the iOrderByCol'th
** expression returned by SELECT statement pSub. Since these values
** do not necessarily correspond to columns in SELECT statement pParent,
- ** zero them before transferring the ORDER BY clause.
+ ** zero them before transfering the ORDER BY clause.
**
** Not doing this may cause an error if a subsequent call to this
** function attempts to flatten a compound sub-query into pParent
@@ -110137,27 +116551,20 @@ static int flattenSubquery(
assert( pSub->pPrior==0 );
pParent->pOrderBy = pOrderBy;
pSub->pOrderBy = 0;
- }else if( pParent->pOrderBy ){
- substExprList(db, pParent->pOrderBy, iParent, pSub->pEList);
- }
- if( pSub->pWhere ){
- pWhere = sqlite3ExprDup(db, pSub->pWhere, 0);
- }else{
- pWhere = 0;
}
+ pWhere = sqlite3ExprDup(db, pSub->pWhere, 0);
if( subqueryIsAgg ){
assert( pParent->pHaving==0 );
pParent->pHaving = pParent->pWhere;
pParent->pWhere = pWhere;
- pParent->pHaving = substExpr(db, pParent->pHaving, iParent, pSub->pEList);
pParent->pHaving = sqlite3ExprAnd(db, pParent->pHaving,
sqlite3ExprDup(db, pSub->pHaving, 0));
assert( pParent->pGroupBy==0 );
pParent->pGroupBy = sqlite3ExprListDup(db, pSub->pGroupBy, 0);
}else{
- pParent->pWhere = substExpr(db, pParent->pWhere, iParent, pSub->pEList);
pParent->pWhere = sqlite3ExprAnd(db, pParent->pWhere, pWhere);
}
+ substSelect(db, pParent, iParent, pSub->pEList, 0);
/* The flattened query is distinct if either the inner or the
** outer query is distinct.
@@ -110183,7 +116590,7 @@ static int flattenSubquery(
#if SELECTTRACE_ENABLED
if( sqlite3SelectTrace & 0x100 ){
- sqlite3DebugPrintf("After flattening:\n");
+ SELECTTRACE(0x100,pParse,p,("After flattening:\n"));
sqlite3TreeViewSelect(0, p, 0);
}
#endif
@@ -110192,6 +116599,83 @@ static int flattenSubquery(
}
#endif /* !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) */
+
+
+#if !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW)
+/*
+** Make copies of relevant WHERE clause terms of the outer query into
+** the WHERE clause of subquery. Example:
+**
+** SELECT * FROM (SELECT a AS x, c-d AS y FROM t1) WHERE x=5 AND y=10;
+**
+** Transformed into:
+**
+** SELECT * FROM (SELECT a AS x, c-d AS y FROM t1 WHERE a=5 AND c-d=10)
+** WHERE x=5 AND y=10;
+**
+** The hope is that the terms added to the inner query will make it more
+** efficient.
+**
+** Do not attempt this optimization if:
+**
+** (1) The inner query is an aggregate. (In that case, we'd really want
+** to copy the outer WHERE-clause terms onto the HAVING clause of the
+** inner query. But they probably won't help there so do not bother.)
+**
+** (2) The inner query is the recursive part of a common table expression.
+**
+** (3) The inner query has a LIMIT clause (since the changes to the WHERE
+** close would change the meaning of the LIMIT).
+**
+** (4) The inner query is the right operand of a LEFT JOIN. (The caller
+** enforces this restriction since this routine does not have enough
+** information to know.)
+**
+** (5) The WHERE clause expression originates in the ON or USING clause
+** of a LEFT JOIN.
+**
+** Return 0 if no changes are made and non-zero if one or more WHERE clause
+** terms are duplicated into the subquery.
+*/
+static int pushDownWhereTerms(
+ sqlite3 *db, /* The database connection (for malloc()) */
+ Select *pSubq, /* The subquery whose WHERE clause is to be augmented */
+ Expr *pWhere, /* The WHERE clause of the outer query */
+ int iCursor /* Cursor number of the subquery */
+){
+ Expr *pNew;
+ int nChng = 0;
+ Select *pX; /* For looping over compound SELECTs in pSubq */
+ if( pWhere==0 ) return 0;
+ for(pX=pSubq; pX; pX=pX->pPrior){
+ if( (pX->selFlags & (SF_Aggregate|SF_Recursive))!=0 ){
+ testcase( pX->selFlags & SF_Aggregate );
+ testcase( pX->selFlags & SF_Recursive );
+ testcase( pX!=pSubq );
+ return 0; /* restrictions (1) and (2) */
+ }
+ }
+ if( pSubq->pLimit!=0 ){
+ return 0; /* restriction (3) */
+ }
+ while( pWhere->op==TK_AND ){
+ nChng += pushDownWhereTerms(db, pSubq, pWhere->pRight, iCursor);
+ pWhere = pWhere->pLeft;
+ }
+ if( ExprHasProperty(pWhere,EP_FromJoin) ) return 0; /* restriction 5 */
+ if( sqlite3ExprIsTableConstant(pWhere, iCursor) ){
+ nChng++;
+ while( pSubq ){
+ pNew = sqlite3ExprDup(db, pWhere, 0);
+ pNew = substExpr(db, pNew, iCursor, pSubq->pEList);
+ pSubq->pWhere = sqlite3ExprAnd(db, pSubq->pWhere, pNew);
+ pSubq = pSubq->pPrior;
+ }
+ }
+ return nChng;
+}
+#endif /* !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) */
+
/*
** Based on the contents of the AggInfo structure indicated by the first
** argument, this function checks if the following are true:
@@ -110275,20 +116759,20 @@ static Table *isSimpleCount(Select *p, AggInfo *pAggInfo){
** pFrom->pIndex and return SQLITE_OK.
*/
SQLITE_PRIVATE int sqlite3IndexedByLookup(Parse *pParse, struct SrcList_item *pFrom){
- if( pFrom->pTab && pFrom->zIndex ){
+ if( pFrom->pTab && pFrom->fg.isIndexedBy ){
Table *pTab = pFrom->pTab;
- char *zIndex = pFrom->zIndex;
+ char *zIndexedBy = pFrom->u1.zIndexedBy;
Index *pIdx;
for(pIdx=pTab->pIndex;
- pIdx && sqlite3StrICmp(pIdx->zName, zIndex);
+ pIdx && sqlite3StrICmp(pIdx->zName, zIndexedBy);
pIdx=pIdx->pNext
);
if( !pIdx ){
- sqlite3ErrorMsg(pParse, "no such index: %s", zIndex, 0);
+ sqlite3ErrorMsg(pParse, "no such index: %s", zIndexedBy, 0);
pParse->checkSchema = 1;
return SQLITE_ERROR;
}
- pFrom->pIndex = pIdx;
+ pFrom->pIBIndex = pIdx;
}
return SQLITE_OK;
}
@@ -110344,7 +116828,7 @@ static int convertCompoundSelectToSubquery(Walker *pWalker, Select *p){
if( pNewSrc==0 ) return WRC_Abort;
*pNew = *p;
p->pSrc = pNewSrc;
- p->pEList = sqlite3ExprListAppend(pParse, 0, sqlite3Expr(db, TK_ALL, 0));
+ p->pEList = sqlite3ExprListAppend(pParse, 0, sqlite3Expr(db, TK_ASTERISK, 0));
p->op = TK_SELECT;
p->pWhere = 0;
pNew->pGroupBy = 0;
@@ -110363,6 +116847,19 @@ static int convertCompoundSelectToSubquery(Walker *pWalker, Select *p){
return WRC_Continue;
}
+/*
+** Check to see if the FROM clause term pFrom has table-valued function
+** arguments. If it does, leave an error message in pParse and return
+** non-zero, since pFrom is not allowed to be a table-valued function.
+*/
+static int cannotBeFunction(Parse *pParse, struct SrcList_item *pFrom){
+ if( pFrom->fg.isTabFunc ){
+ sqlite3ErrorMsg(pParse, "'%s' is not a function", pFrom->zName);
+ return 1;
+ }
+ return 0;
+}
+
#ifndef SQLITE_OMIT_CTE
/*
** Argument pWith (which may be NULL) points to a linked list of nested
@@ -110375,7 +116872,7 @@ static int convertCompoundSelectToSubquery(Walker *pWalker, Select *p){
** object that the returned CTE belongs to.
*/
static struct Cte *searchWith(
- With *pWith, /* Current outermost WITH clause */
+ With *pWith, /* Current innermost WITH clause */
struct SrcList_item *pItem, /* FROM clause element to resolve */
With **ppContext /* OUT: WITH clause return value belongs to */
){
@@ -110406,11 +116903,12 @@ static struct Cte *searchWith(
** statement with which it is associated.
*/
SQLITE_PRIVATE void sqlite3WithPush(Parse *pParse, With *pWith, u8 bFree){
- assert( bFree==0 || pParse->pWith==0 );
+ assert( bFree==0 || (pParse->pWith==0 && pParse->pWithToFree==0) );
if( pWith ){
+ assert( pParse->pWith!=pWith );
pWith->pOuter = pParse->pWith;
pParse->pWith = pWith;
- pParse->bFreeWith = bFree;
+ if( bFree ) pParse->pWithToFree = pWith;
}
}
@@ -110449,14 +116947,15 @@ static int withExpand(
int bMayRecursive; /* True if compound joined by UNION [ALL] */
With *pSavedWith; /* Initial value of pParse->pWith */
- /* If pCte->zErr is non-NULL at this point, then this is an illegal
+ /* If pCte->zCteErr is non-NULL at this point, then this is an illegal
** recursive reference to CTE pCte. Leave an error in pParse and return
- ** early. If pCte->zErr is NULL, then this is not a recursive reference.
+ ** early. If pCte->zCteErr is NULL, then this is not a recursive reference.
** In this case, proceed. */
- if( pCte->zErr ){
- sqlite3ErrorMsg(pParse, pCte->zErr, pCte->zName);
+ if( pCte->zCteErr ){
+ sqlite3ErrorMsg(pParse, pCte->zCteErr, pCte->zName);
return SQLITE_ERROR;
}
+ if( cannotBeFunction(pParse, pFrom) ) return SQLITE_ERROR;
assert( pFrom->pTab==0 );
pFrom->pTab = pTab = sqlite3DbMallocZero(db, sizeof(Table));
@@ -110465,9 +116964,9 @@ static int withExpand(
pTab->zName = sqlite3DbStrDup(db, pCte->zName);
pTab->iPKey = -1;
pTab->nRowLogEst = 200; assert( 200==sqlite3LogEst(1048576) );
- pTab->tabFlags |= TF_Ephemeral;
+ pTab->tabFlags |= TF_Ephemeral | TF_NoVisibleRowid;
pFrom->pSelect = sqlite3SelectDup(db, pCte->pSelect, 0);
- if( db->mallocFailed ) return SQLITE_NOMEM;
+ if( db->mallocFailed ) return SQLITE_NOMEM_BKPT;
assert( pFrom->pSelect );
/* Check if this is a recursive CTE. */
@@ -110483,7 +116982,7 @@ static int withExpand(
&& 0==sqlite3StrICmp(pItem->zName, pCte->zName)
){
pItem->pTab = pTab;
- pItem->isRecursive = 1;
+ pItem->fg.isRecursive = 1;
pTab->nRef++;
pSel->selFlags |= SF_Recursive;
}
@@ -110499,10 +116998,11 @@ static int withExpand(
}
assert( pTab->nRef==1 || ((pSel->selFlags&SF_Recursive) && pTab->nRef==2 ));
- pCte->zErr = "circular reference: %s";
+ pCte->zCteErr = "circular reference: %s";
pSavedWith = pParse->pWith;
pParse->pWith = pWith;
sqlite3WalkSelect(pWalker, bMayRecursive ? pSel->pPrior : pSel);
+ pParse->pWith = pWith;
for(pLeft=pSel; pLeft->pPrior; pLeft=pLeft->pPrior);
pEList = pLeft->pEList;
@@ -110517,16 +117017,16 @@ static int withExpand(
pEList = pCte->pCols;
}
- selectColumnsFromExprList(pParse, pEList, &pTab->nCol, &pTab->aCol);
+ sqlite3ColumnsFromExprList(pParse, pEList, &pTab->nCol, &pTab->aCol);
if( bMayRecursive ){
if( pSel->selFlags & SF_Recursive ){
- pCte->zErr = "multiple recursive references: %s";
+ pCte->zCteErr = "multiple recursive references: %s";
}else{
- pCte->zErr = "recursive reference in a subquery: %s";
+ pCte->zCteErr = "recursive reference in a subquery: %s";
}
sqlite3WalkSelect(pWalker, pSel);
}
- pCte->zErr = 0;
+ pCte->zCteErr = 0;
pParse->pWith = pSavedWith;
}
@@ -110613,17 +117113,9 @@ static int selectExpander(Walker *pWalker, Select *p){
*/
for(i=0, pFrom=pTabList->a; i<pTabList->nSrc; i++, pFrom++){
Table *pTab;
- assert( pFrom->isRecursive==0 || pFrom->pTab );
- if( pFrom->isRecursive ) continue;
- if( pFrom->pTab!=0 ){
- /* This statement has already been prepared. There is no need
- ** to go further. */
- assert( i==0 );
-#ifndef SQLITE_OMIT_CTE
- selectPopWith(pWalker, p);
-#endif
- return WRC_Prune;
- }
+ assert( pFrom->fg.isRecursive==0 || pFrom->pTab!=0 );
+ if( pFrom->fg.isRecursive ) continue;
+ assert( pFrom->pTab==0 );
#ifndef SQLITE_OMIT_CTE
if( withExpand(pWalker, pFrom) ) return WRC_Abort;
if( pFrom->pTab ) {} else
@@ -110640,7 +117132,7 @@ static int selectExpander(Walker *pWalker, Select *p){
pTab->nRef = 1;
pTab->zName = sqlite3MPrintf(db, "sqlite_sq_%p", (void*)pTab);
while( pSel->pPrior ){ pSel = pSel->pPrior; }
- selectColumnsFromExprList(pParse, pSel->pEList, &pTab->nCol, &pTab->aCol);
+ sqlite3ColumnsFromExprList(pParse, pSel->pEList,&pTab->nCol,&pTab->aCol);
pTab->iPKey = -1;
pTab->nRowLogEst = 200; assert( 200==sqlite3LogEst(1048576) );
pTab->tabFlags |= TF_Ephemeral;
@@ -110657,14 +117149,20 @@ static int selectExpander(Walker *pWalker, Select *p){
return WRC_Abort;
}
pTab->nRef++;
+ if( !IsVirtual(pTab) && cannotBeFunction(pParse, pFrom) ){
+ return WRC_Abort;
+ }
#if !defined(SQLITE_OMIT_VIEW) || !defined (SQLITE_OMIT_VIRTUALTABLE)
- if( pTab->pSelect || IsVirtual(pTab) ){
- /* We reach here if the named table is a really a view */
+ if( IsVirtual(pTab) || pTab->pSelect ){
+ i16 nCol;
if( sqlite3ViewGetColumnNames(pParse, pTab) ) return WRC_Abort;
assert( pFrom->pSelect==0 );
pFrom->pSelect = sqlite3SelectDup(db, pTab->pSelect, 0);
sqlite3SelectSetName(pFrom->pSelect, pTab->zName);
+ nCol = pTab->nCol;
+ pTab->nCol = -1;
sqlite3WalkSelect(pWalker, pFrom->pSelect);
+ pTab->nCol = nCol;
}
#endif
}
@@ -110684,19 +117182,20 @@ static int selectExpander(Walker *pWalker, Select *p){
/* For every "*" that occurs in the column list, insert the names of
** all columns in all tables. And for every TABLE.* insert the names
** of all columns in TABLE. The parser inserted a special expression
- ** with the TK_ALL operator for each "*" that it found in the column list.
- ** The following code just has to locate the TK_ALL expressions and expand
- ** each one to the list of all columns in all tables.
+ ** with the TK_ASTERISK operator for each "*" that it found in the column
+ ** list. The following code just has to locate the TK_ASTERISK
+ ** expressions and expand each one to the list of all columns in
+ ** all tables.
**
** The first loop just checks to see if there are any "*" operators
** that need expanding.
*/
for(k=0; k<pEList->nExpr; k++){
pE = pEList->a[k].pExpr;
- if( pE->op==TK_ALL ) break;
+ if( pE->op==TK_ASTERISK ) break;
assert( pE->op!=TK_DOT || pE->pRight!=0 );
assert( pE->op!=TK_DOT || (pE->pLeft!=0 && pE->pLeft->op==TK_ID) );
- if( pE->op==TK_DOT && pE->pRight->op==TK_ALL ) break;
+ if( pE->op==TK_DOT && pE->pRight->op==TK_ASTERISK ) break;
}
if( k<pEList->nExpr ){
/*
@@ -110710,18 +117209,13 @@ static int selectExpander(Walker *pWalker, Select *p){
int longNames = (flags & SQLITE_FullColNames)!=0
&& (flags & SQLITE_ShortColNames)==0;
- /* When processing FROM-clause subqueries, it is always the case
- ** that full_column_names=OFF and short_column_names=ON. The
- ** sqlite3ResultSetOfSelect() routine makes it so. */
- assert( (p->selFlags & SF_NestedFrom)==0
- || ((flags & SQLITE_FullColNames)==0 &&
- (flags & SQLITE_ShortColNames)!=0) );
-
for(k=0; k<pEList->nExpr; k++){
pE = a[k].pExpr;
pRight = pE->pRight;
assert( pE->op!=TK_DOT || pRight!=0 );
- if( pE->op!=TK_ALL && (pE->op!=TK_DOT || pRight->op!=TK_ALL) ){
+ if( pE->op!=TK_ASTERISK
+ && (pE->op!=TK_DOT || pRight->op!=TK_ASTERISK)
+ ){
/* This particular expression does not need to be expanded.
*/
pNew = sqlite3ExprListAppend(pParse, pNew, a[k].pExpr);
@@ -110773,18 +117267,19 @@ static int selectExpander(Walker *pWalker, Select *p){
continue;
}
- /* If a column is marked as 'hidden' (currently only possible
- ** for virtual tables), do not include it in the expanded
- ** result-set list.
+ /* If a column is marked as 'hidden', omit it from the expanded
+ ** result-set list unless the SELECT has the SF_IncludeHidden
+ ** bit set.
*/
- if( IsHiddenColumn(&pTab->aCol[j]) ){
- assert(IsVirtual(pTab));
+ if( (p->selFlags & SF_IncludeHidden)==0
+ && IsHiddenColumn(&pTab->aCol[j])
+ ){
continue;
}
tableSeen = 1;
if( i>0 && zTName==0 ){
- if( (pFrom->jointype & JT_NATURAL)!=0
+ if( (pFrom->fg.jointype & JT_NATURAL)!=0
&& tableAndColumnIndex(pTabList, i, zName, 0, 0)
){
/* In a NATURAL join, omit the join columns from the
@@ -110816,8 +117311,7 @@ static int selectExpander(Walker *pWalker, Select *p){
pExpr = pRight;
}
pNew = sqlite3ExprListAppend(pParse, pNew, pExpr);
- sColname.z = zColname;
- sColname.n = sqlite3Strlen30(zColname);
+ sqlite3TokenInit(&sColname, zColname);
sqlite3ExprListSetName(pParse, pNew, &sColname, 0);
if( pNew && (p->selFlags & SF_NestedFrom)!=0 ){
struct ExprList_item *pX = &pNew->a[pNew->nExpr-1];
@@ -110849,6 +117343,7 @@ static int selectExpander(Walker *pWalker, Select *p){
#if SQLITE_MAX_COLUMN
if( p->pEList && p->pEList->nExpr>db->aLimit[SQLITE_LIMIT_COLUMN] ){
sqlite3ErrorMsg(pParse, "too many columns in result set");
+ return WRC_Abort;
}
#endif
return WRC_Continue;
@@ -110863,7 +117358,7 @@ static int selectExpander(Walker *pWalker, Select *p){
** Walker.xSelectCallback is set to do something useful for every
** subquery in the parser tree.
*/
-static int exprWalkNoop(Walker *NotUsed, Expr *NotUsed2){
+SQLITE_PRIVATE int sqlite3ExprWalkNoop(Walker *NotUsed, Expr *NotUsed2){
UNUSED_PARAMETER2(NotUsed, NotUsed2);
return WRC_Continue;
}
@@ -110884,7 +117379,7 @@ static int exprWalkNoop(Walker *NotUsed, Expr *NotUsed2){
static void sqlite3SelectExpand(Parse *pParse, Select *pSelect){
Walker w;
memset(&w, 0, sizeof(w));
- w.xExprCallback = exprWalkNoop;
+ w.xExprCallback = sqlite3ExprWalkNoop;
w.pParse = pParse;
if( pParse->hasCompound ){
w.xSelectCallback = convertCompoundSelectToSubquery;
@@ -110919,19 +117414,19 @@ static void selectAddSubqueryTypeInfo(Walker *pWalker, Select *p){
struct SrcList_item *pFrom;
assert( p->selFlags & SF_Resolved );
- if( (p->selFlags & SF_HasTypeInfo)==0 ){
- p->selFlags |= SF_HasTypeInfo;
- pParse = pWalker->pParse;
- pTabList = p->pSrc;
- for(i=0, pFrom=pTabList->a; i<pTabList->nSrc; i++, pFrom++){
- Table *pTab = pFrom->pTab;
- if( ALWAYS(pTab!=0) && (pTab->tabFlags & TF_Ephemeral)!=0 ){
- /* A sub-query in the FROM clause of a SELECT */
- Select *pSel = pFrom->pSelect;
- if( pSel ){
- while( pSel->pPrior ) pSel = pSel->pPrior;
- selectAddColumnTypeAndCollation(pParse, pTab, pSel);
- }
+ assert( (p->selFlags & SF_HasTypeInfo)==0 );
+ p->selFlags |= SF_HasTypeInfo;
+ pParse = pWalker->pParse;
+ pTabList = p->pSrc;
+ for(i=0, pFrom=pTabList->a; i<pTabList->nSrc; i++, pFrom++){
+ Table *pTab = pFrom->pTab;
+ assert( pTab!=0 );
+ if( (pTab->tabFlags & TF_Ephemeral)!=0 ){
+ /* A sub-query in the FROM clause of a SELECT */
+ Select *pSel = pFrom->pSelect;
+ if( pSel ){
+ while( pSel->pPrior ) pSel = pSel->pPrior;
+ sqlite3SelectAddColumnTypeAndCollation(pParse, pTab, pSel);
}
}
}
@@ -110951,7 +117446,7 @@ static void sqlite3SelectAddTypeInfo(Parse *pParse, Select *pSelect){
Walker w;
memset(&w, 0, sizeof(w));
w.xSelectCallback2 = selectAddSubqueryTypeInfo;
- w.xExprCallback = exprWalkNoop;
+ w.xExprCallback = sqlite3ExprWalkNoop;
w.pParse = pParse;
sqlite3WalkSelect(&w, pSelect);
#endif
@@ -111070,7 +117565,7 @@ static void updateAccumulator(Parse *pParse, AggInfo *pAggInfo){
if( pList ){
nArg = pList->nExpr;
regAgg = sqlite3GetTempRange(pParse, nArg);
- sqlite3ExprCodeExprList(pParse, pList, regAgg, SQLITE_ECEL_DUP);
+ sqlite3ExprCodeExprList(pParse, pList, regAgg, 0, SQLITE_ECEL_DUP);
}else{
nArg = 0;
regAgg = 0;
@@ -111095,7 +117590,7 @@ static void updateAccumulator(Parse *pParse, AggInfo *pAggInfo){
if( regHit==0 && pAggInfo->nAccumulator ) regHit = ++pParse->nMem;
sqlite3VdbeAddOp4(v, OP_CollSeq, regHit, 0, 0, (char *)pColl, P4_COLLSEQ);
}
- sqlite3VdbeAddOp4(v, OP_AggStep, 0, regAgg, pF->iMem,
+ sqlite3VdbeAddOp4(v, OP_AggStep0, 0, regAgg, pF->iMem,
(void*)pF->pFunc, P4_FUNCDEF);
sqlite3VdbeChangeP5(v, (u8)nArg);
sqlite3ExprCacheAffinityChange(pParse, regAgg, nArg);
@@ -111178,7 +117673,7 @@ SQLITE_PRIVATE int sqlite3Select(
WhereInfo *pWInfo; /* Return from sqlite3WhereBegin() */
Vdbe *v; /* The virtual machine under construction */
int isAgg; /* True for select lists like "count(*)" */
- ExprList *pEList; /* List of columns to extract. */
+ ExprList *pEList = 0; /* List of columns to extract. */
SrcList *pTabList; /* List of tables to select from */
Expr *pWhere; /* The WHERE clause. May be NULL */
ExprList *pGroupBy; /* The GROUP BY clause. May be NULL */
@@ -111228,12 +117723,11 @@ SQLITE_PRIVATE int sqlite3Select(
memset(&sSort, 0, sizeof(sSort));
sSort.pOrderBy = p->pOrderBy;
pTabList = p->pSrc;
- pEList = p->pEList;
if( pParse->nErr || db->mallocFailed ){
goto select_end;
}
+ assert( p->pEList!=0 );
isAgg = (p->selFlags & SF_Aggregate)!=0;
- assert( pEList!=0 );
#if SELECTTRACE_ENABLED
if( sqlite3SelectTrace & 0x100 ){
SELECTTRACE(0x100,pParse,p, ("after name resolution:\n"));
@@ -111242,29 +117736,77 @@ SQLITE_PRIVATE int sqlite3Select(
#endif
- /* Begin generating code.
- */
- v = sqlite3GetVdbe(pParse);
- if( v==0 ) goto select_end;
-
/* If writing to memory or generating a set
** only a single column may be output.
*/
#ifndef SQLITE_OMIT_SUBQUERY
- if( checkForMultiColumnSelectError(pParse, pDest, pEList->nExpr) ){
+ if( checkForMultiColumnSelectError(pParse, pDest, p->pEList->nExpr) ){
goto select_end;
}
#endif
- /* Generate code for all sub-queries in the FROM clause
+ /* Try to flatten subqueries in the FROM clause up into the main query
*/
#if !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW)
for(i=0; !p->pPrior && i<pTabList->nSrc; i++){
struct SrcList_item *pItem = &pTabList->a[i];
- SelectDest dest;
Select *pSub = pItem->pSelect;
int isAggSub;
+ Table *pTab = pItem->pTab;
+ if( pSub==0 ) continue;
+
+ /* Catch mismatch in the declared columns of a view and the number of
+ ** columns in the SELECT on the RHS */
+ if( pTab->nCol!=pSub->pEList->nExpr ){
+ sqlite3ErrorMsg(pParse, "expected %d columns for '%s' but got %d",
+ pTab->nCol, pTab->zName, pSub->pEList->nExpr);
+ goto select_end;
+ }
+
+ isAggSub = (pSub->selFlags & SF_Aggregate)!=0;
+ if( flattenSubquery(pParse, p, i, isAgg, isAggSub) ){
+ /* This subquery can be absorbed into its parent. */
+ if( isAggSub ){
+ isAgg = 1;
+ p->selFlags |= SF_Aggregate;
+ }
+ i = -1;
+ }
+ pTabList = p->pSrc;
+ if( db->mallocFailed ) goto select_end;
+ if( !IgnorableOrderby(pDest) ){
+ sSort.pOrderBy = p->pOrderBy;
+ }
+ }
+#endif
+
+ /* Get a pointer the VDBE under construction, allocating a new VDBE if one
+ ** does not already exist */
+ v = sqlite3GetVdbe(pParse);
+ if( v==0 ) goto select_end;
+#ifndef SQLITE_OMIT_COMPOUND_SELECT
+ /* Handle compound SELECT statements using the separate multiSelect()
+ ** procedure.
+ */
+ if( p->pPrior ){
+ rc = multiSelect(pParse, p, pDest);
+ explainSetInteger(pParse->iSelectId, iRestoreSelectId);
+#if SELECTTRACE_ENABLED
+ SELECTTRACE(1,pParse,p,("end compound-select processing\n"));
+ pParse->nSelectIndent--;
+#endif
+ return rc;
+ }
+#endif
+
+ /* Generate code for all sub-queries in the FROM clause
+ */
+#if !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW)
+ for(i=0; i<pTabList->nSrc; i++){
+ struct SrcList_item *pItem = &pTabList->a[i];
+ SelectDest dest;
+ Select *pSub = pItem->pSelect;
if( pSub==0 ) continue;
/* Sometimes the code for a subquery will be generated more than
@@ -111274,7 +117816,7 @@ SQLITE_PRIVATE int sqlite3Select(
** is sufficient, though the subroutine to manifest the view does need
** to be invoked again. */
if( pItem->addrFillSub ){
- if( pItem->viaCoroutine==0 ){
+ if( pItem->fg.viaCoroutine==0 ){
sqlite3VdbeAddOp2(v, OP_Gosub, pItem->regReturn, pItem->addrFillSub);
}
continue;
@@ -111289,16 +117831,39 @@ SQLITE_PRIVATE int sqlite3Select(
*/
pParse->nHeight += sqlite3SelectExprHeight(p);
- isAggSub = (pSub->selFlags & SF_Aggregate)!=0;
- if( flattenSubquery(pParse, p, i, isAgg, isAggSub) ){
- /* This subquery can be absorbed into its parent. */
- if( isAggSub ){
- isAgg = 1;
- p->selFlags |= SF_Aggregate;
+ /* Make copies of constant WHERE-clause terms in the outer query down
+ ** inside the subquery. This can help the subquery to run more efficiently.
+ */
+ if( (pItem->fg.jointype & JT_OUTER)==0
+ && pushDownWhereTerms(db, pSub, p->pWhere, pItem->iCursor)
+ ){
+#if SELECTTRACE_ENABLED
+ if( sqlite3SelectTrace & 0x100 ){
+ SELECTTRACE(0x100,pParse,p,("After WHERE-clause push-down:\n"));
+ sqlite3TreeViewSelect(0, p, 0);
}
- i = -1;
- }else if( pTabList->nSrc==1
- && OptimizationEnabled(db, SQLITE_SubqCoroutine)
+#endif
+ }
+
+ /* Generate code to implement the subquery
+ **
+ ** The subquery is implemented as a co-routine if all of these are true:
+ ** (1) The subquery is guaranteed to be the outer loop (so that it
+ ** does not need to be computed more than once)
+ ** (2) The ALL keyword after SELECT is omitted. (Applications are
+ ** allowed to say "SELECT ALL" instead of just "SELECT" to disable
+ ** the use of co-routines.)
+ ** (3) Co-routines are not disabled using sqlite3_test_control()
+ ** with SQLITE_TESTCTRL_OPTIMIZATIONS.
+ **
+ ** TODO: Are there other reasons beside (1) to use a co-routine
+ ** implementation?
+ */
+ if( i==0
+ && (pTabList->nSrc==1
+ || (pTabList->a[1].fg.jointype&(JT_LEFT|JT_CROSS))!=0) /* (1) */
+ && (p->selFlags & SF_All)==0 /* (2) */
+ && OptimizationEnabled(db, SQLITE_SubqCoroutine) /* (3) */
){
/* Implement a co-routine that will return a single row of the result
** set on each invocation.
@@ -111311,10 +117876,10 @@ SQLITE_PRIVATE int sqlite3Select(
sqlite3SelectDestInit(&dest, SRT_Coroutine, pItem->regReturn);
explainSetInteger(pItem->iSelectId, (u8)pParse->iNextSelectId);
sqlite3Select(pParse, pSub, &dest);
- pItem->pTab->nRowLogEst = sqlite3LogEst(pSub->nSelectRow);
- pItem->viaCoroutine = 1;
+ pItem->pTab->nRowLogEst = pSub->nSelectRow;
+ pItem->fg.viaCoroutine = 1;
pItem->regResult = dest.iSdst;
- sqlite3VdbeAddOp1(v, OP_EndCoroutine, pItem->regReturn);
+ sqlite3VdbeEndCoroutine(v, pItem->regReturn);
sqlite3VdbeJumpHere(v, addrTop-1);
sqlite3ClearTempRegCache(pParse);
}else{
@@ -111330,7 +117895,7 @@ SQLITE_PRIVATE int sqlite3Select(
pItem->regReturn = ++pParse->nMem;
topAddr = sqlite3VdbeAddOp2(v, OP_Integer, 0, pItem->regReturn);
pItem->addrFillSub = topAddr+1;
- if( pItem->isCorrelated==0 ){
+ if( pItem->fg.isCorrelated==0 ){
/* If the subquery is not correlated and if we are not inside of
** a trigger, then we only need to compute the value of the subquery
** once. */
@@ -111342,40 +117907,30 @@ SQLITE_PRIVATE int sqlite3Select(
sqlite3SelectDestInit(&dest, SRT_EphemTab, pItem->iCursor);
explainSetInteger(pItem->iSelectId, (u8)pParse->iNextSelectId);
sqlite3Select(pParse, pSub, &dest);
- pItem->pTab->nRowLogEst = sqlite3LogEst(pSub->nSelectRow);
+ pItem->pTab->nRowLogEst = pSub->nSelectRow;
if( onceAddr ) sqlite3VdbeJumpHere(v, onceAddr);
retAddr = sqlite3VdbeAddOp1(v, OP_Return, pItem->regReturn);
VdbeComment((v, "end %s", pItem->pTab->zName));
sqlite3VdbeChangeP1(v, topAddr, retAddr);
sqlite3ClearTempRegCache(pParse);
}
- if( /*pParse->nErr ||*/ db->mallocFailed ){
- goto select_end;
- }
+ if( db->mallocFailed ) goto select_end;
pParse->nHeight -= sqlite3SelectExprHeight(p);
- pTabList = p->pSrc;
- if( !IgnorableOrderby(pDest) ){
- sSort.pOrderBy = p->pOrderBy;
- }
}
- pEList = p->pEList;
#endif
+
+ /* Various elements of the SELECT copied into local variables for
+ ** convenience */
+ pEList = p->pEList;
pWhere = p->pWhere;
pGroupBy = p->pGroupBy;
pHaving = p->pHaving;
sDistinct.isTnct = (p->selFlags & SF_Distinct)!=0;
-#ifndef SQLITE_OMIT_COMPOUND_SELECT
- /* If there is are a sequence of queries, do the earlier ones first.
- */
- if( p->pPrior ){
- rc = multiSelect(pParse, p, pDest);
- explainSetInteger(pParse->iSelectId, iRestoreSelectId);
#if SELECTTRACE_ENABLED
- SELECTTRACE(1,pParse,p,("end compound-select processing\n"));
- pParse->nSelectIndent--;
-#endif
- return rc;
+ if( sqlite3SelectTrace & 0x400 ){
+ SELECTTRACE(0x400,pParse,p,("After all FROM-clause analysis:\n"));
+ sqlite3TreeViewSelect(0, p, 0);
}
#endif
@@ -111395,23 +117950,30 @@ SQLITE_PRIVATE int sqlite3Select(
** BY and DISTINCT, and an index or separate temp-table for the other.
*/
if( (p->selFlags & (SF_Distinct|SF_Aggregate))==SF_Distinct
- && sqlite3ExprListCompare(sSort.pOrderBy, p->pEList, -1)==0
+ && sqlite3ExprListCompare(sSort.pOrderBy, pEList, -1)==0
){
p->selFlags &= ~SF_Distinct;
- p->pGroupBy = sqlite3ExprListDup(db, p->pEList, 0);
- pGroupBy = p->pGroupBy;
+ pGroupBy = p->pGroupBy = sqlite3ExprListDup(db, pEList, 0);
/* Notice that even thought SF_Distinct has been cleared from p->selFlags,
** the sDistinct.isTnct is still set. Hence, isTnct represents the
** original setting of the SF_Distinct flag, not the current setting */
assert( sDistinct.isTnct );
+
+#if SELECTTRACE_ENABLED
+ if( sqlite3SelectTrace & 0x400 ){
+ SELECTTRACE(0x400,pParse,p,("Transform DISTINCT into GROUP BY:\n"));
+ sqlite3TreeViewSelect(0, p, 0);
+ }
+#endif
}
- /* If there is an ORDER BY clause, then this sorting
- ** index might end up being unused if the data can be
- ** extracted in pre-sorted order. If that is the case, then the
- ** OP_OpenEphemeral instruction will be changed to an OP_Noop once
- ** we figure out that the sorting index is not needed. The addrSortIndex
- ** variable is used to facilitate that change.
+ /* If there is an ORDER BY clause, then create an ephemeral index to
+ ** do the sorting. But this sorting ephemeral index might end up
+ ** being unused if the data can be extracted in pre-sorted order.
+ ** If that is the case, then the OP_OpenEphemeral instruction will be
+ ** changed to an OP_Noop once we figure out that the sorting index is
+ ** not needed. The sSort.addrSortIndex variable is used to facilitate
+ ** that change.
*/
if( sSort.pOrderBy ){
KeyInfo *pKeyInfo;
@@ -111435,21 +117997,21 @@ SQLITE_PRIVATE int sqlite3Select(
/* Set the limiter.
*/
iEnd = sqlite3VdbeMakeLabel(v);
- p->nSelectRow = LARGEST_INT64;
+ p->nSelectRow = 320; /* 4 billion rows */
computeLimitRegisters(pParse, p, iEnd);
if( p->iLimit==0 && sSort.addrSortIndex>=0 ){
- sqlite3VdbeGetOp(v, sSort.addrSortIndex)->opcode = OP_SorterOpen;
+ sqlite3VdbeChangeOpcode(v, sSort.addrSortIndex, OP_SorterOpen);
sSort.sortFlags |= SORTFLAG_UseSorter;
}
- /* Open a virtual index to use for the distinct set.
+ /* Open an ephemeral index to use for the distinct set.
*/
if( p->selFlags & SF_Distinct ){
sDistinct.tabTnct = pParse->nTab++;
sDistinct.addrTnct = sqlite3VdbeAddOp4(v, OP_OpenEphemeral,
- sDistinct.tabTnct, 0, 0,
- (char*)keyInfoFromExprList(pParse, p->pEList,0,0),
- P4_KEYINFO);
+ sDistinct.tabTnct, 0, 0,
+ (char*)keyInfoFromExprList(pParse, p->pEList,0,0),
+ P4_KEYINFO);
sqlite3VdbeChangeP5(v, BTREE_UNORDERED);
sDistinct.eTnctType = WHERE_DISTINCT_UNORDERED;
}else{
@@ -111459,10 +118021,12 @@ SQLITE_PRIVATE int sqlite3Select(
if( !isAgg && pGroupBy==0 ){
/* No aggregate functions and no GROUP BY clause */
u16 wctrlFlags = (sDistinct.isTnct ? WHERE_WANT_DISTINCT : 0);
+ assert( WHERE_USE_LIMIT==SF_FixedLimit );
+ wctrlFlags |= p->selFlags & SF_FixedLimit;
/* Begin the database scan. */
pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, sSort.pOrderBy,
- p->pEList, wctrlFlags, 0);
+ p->pEList, wctrlFlags, p->nSelectRow);
if( pWInfo==0 ) goto select_end;
if( sqlite3WhereOutputRowCount(pWInfo) < p->nSelectRow ){
p->nSelectRow = sqlite3WhereOutputRowCount(pWInfo);
@@ -111522,16 +118086,17 @@ SQLITE_PRIVATE int sqlite3Select(
for(k=pGroupBy->nExpr, pItem=pGroupBy->a; k>0; k--, pItem++){
pItem->u.x.iAlias = 0;
}
- if( p->nSelectRow>100 ) p->nSelectRow = 100;
+ assert( 66==sqlite3LogEst(100) );
+ if( p->nSelectRow>66 ) p->nSelectRow = 66;
}else{
- p->nSelectRow = 1;
+ assert( 0==sqlite3LogEst(1) );
+ p->nSelectRow = 0;
}
-
/* If there is both a GROUP BY and an ORDER BY clause and they are
** identical, then it may be possible to disable the ORDER BY clause
** on the grounds that the GROUP BY will cause elements to come out
- ** in the correct order. It also may not - the GROUP BY may use a
+ ** in the correct order. It also may not - the GROUP BY might use a
** database index that causes rows to be grouped together as required
** but not actually sorted. Either way, record the fact that the
** ORDER BY and GROUP BY clauses are the same by setting the orderByGrp
@@ -111574,7 +118139,7 @@ SQLITE_PRIVATE int sqlite3Select(
*/
if( pGroupBy ){
KeyInfo *pKeyInfo; /* Keying information for the group by clause */
- int j1; /* A-vs-B comparision jump */
+ int addr1; /* A-vs-B comparision jump */
int addrOutputRow; /* Start of subroutine that outputs a result row */
int regOutputRow; /* Return address register for output subroutine */
int addrSetAbort; /* Set the abort flag and return */
@@ -111655,19 +118220,14 @@ SQLITE_PRIVATE int sqlite3Select(
}
regBase = sqlite3GetTempRange(pParse, nCol);
sqlite3ExprCacheClear(pParse);
- sqlite3ExprCodeExprList(pParse, pGroupBy, regBase, 0);
+ sqlite3ExprCodeExprList(pParse, pGroupBy, regBase, 0, 0);
j = nGroupBy;
for(i=0; i<sAggInfo.nColumn; i++){
struct AggInfo_col *pCol = &sAggInfo.aCol[i];
if( pCol->iSorterColumn>=j ){
int r1 = j + regBase;
- int r2;
-
- r2 = sqlite3ExprCodeGetColumn(pParse,
- pCol->pTab, pCol->iColumn, pCol->iTable, r1, 0);
- if( r1!=r2 ){
- sqlite3VdbeAddOp2(v, OP_SCopy, r2, r1);
- }
+ sqlite3ExprCodeGetColumnToReg(pParse,
+ pCol->pTab, pCol->iColumn, pCol->iTable, r1);
j++;
}
}
@@ -111709,7 +118269,8 @@ SQLITE_PRIVATE int sqlite3Select(
addrTopOfLoop = sqlite3VdbeCurrentAddr(v);
sqlite3ExprCacheClear(pParse);
if( groupBySort ){
- sqlite3VdbeAddOp3(v, OP_SorterData, sAggInfo.sortingIdx, sortOut,sortPTab);
+ sqlite3VdbeAddOp3(v, OP_SorterData, sAggInfo.sortingIdx,
+ sortOut, sortPTab);
}
for(j=0; j<pGroupBy->nExpr; j++){
if( groupBySort ){
@@ -111721,8 +118282,8 @@ SQLITE_PRIVATE int sqlite3Select(
}
sqlite3VdbeAddOp4(v, OP_Compare, iAMem, iBMem, pGroupBy->nExpr,
(char*)sqlite3KeyInfoRef(pKeyInfo), P4_KEYINFO);
- j1 = sqlite3VdbeCurrentAddr(v);
- sqlite3VdbeAddOp3(v, OP_Jump, j1+1, 0, j1+1); VdbeCoverage(v);
+ addr1 = sqlite3VdbeCurrentAddr(v);
+ sqlite3VdbeAddOp3(v, OP_Jump, addr1+1, 0, addr1+1); VdbeCoverage(v);
/* Generate code that runs whenever the GROUP BY changes.
** Changes in the GROUP BY are detected by the previous code
@@ -111744,7 +118305,7 @@ SQLITE_PRIVATE int sqlite3Select(
/* Update the aggregate accumulators based on the content of
** the current row
*/
- sqlite3VdbeJumpHere(v, j1);
+ sqlite3VdbeJumpHere(v, addr1);
updateAccumulator(pParse, &sAggInfo);
sqlite3VdbeAddOp2(v, OP_Integer, 1, iUseFlag);
VdbeComment((v, "indicate data in accumulator"));
@@ -111766,7 +118327,7 @@ SQLITE_PRIVATE int sqlite3Select(
/* Jump over the subroutines
*/
- sqlite3VdbeAddOp2(v, OP_Goto, 0, addrEnd);
+ sqlite3VdbeGoto(v, addrEnd);
/* Generate a subroutine that outputs a single row of the result
** set. This subroutine first looks at the iUseFlag. If iUseFlag
@@ -111781,7 +118342,8 @@ SQLITE_PRIVATE int sqlite3Select(
sqlite3VdbeAddOp1(v, OP_Return, regOutputRow);
sqlite3VdbeResolveLabel(v, addrOutputRow);
addrOutputRow = sqlite3VdbeCurrentAddr(v);
- sqlite3VdbeAddOp2(v, OP_IfPos, iUseFlag, addrOutputRow+2); VdbeCoverage(v);
+ sqlite3VdbeAddOp2(v, OP_IfPos, iUseFlag, addrOutputRow+2);
+ VdbeCoverage(v);
VdbeComment((v, "Groupby result generator entry point"));
sqlite3VdbeAddOp1(v, OP_Return, regOutputRow);
finalizeAggFunctions(pParse, &sAggInfo);
@@ -111900,7 +118462,8 @@ SQLITE_PRIVATE int sqlite3Select(
if( flag ){
pMinMax = sqlite3ExprListDup(db, pMinMax, 0);
pDel = pMinMax;
- if( pMinMax && !db->mallocFailed ){
+ assert( db->mallocFailed || pMinMax!=0 );
+ if( !db->mallocFailed ){
pMinMax->a[0].sortOrder = flag!=WHERE_ORDERBY_MIN ?1:0;
pMinMax->a[0].pExpr->op = TK_COLUMN;
}
@@ -111919,7 +118482,7 @@ SQLITE_PRIVATE int sqlite3Select(
updateAccumulator(pParse, &sAggInfo);
assert( pMinMax==0 || pMinMax->nExpr==1 );
if( sqlite3WhereIsOrdered(pWInfo)>0 ){
- sqlite3VdbeAddOp2(v, OP_Goto, 0, sqlite3WhereBreakLabel(pWInfo));
+ sqlite3VdbeGoto(v, sqlite3WhereBreakLabel(pWInfo));
VdbeComment((v, "%s() by index",
(flag==WHERE_ORDERBY_MIN?"min":"max")));
}
@@ -111945,7 +118508,8 @@ SQLITE_PRIVATE int sqlite3Select(
** and send them to the callback one by one.
*/
if( sSort.pOrderBy ){
- explainTempTable(pParse, sSort.nOBSat>0 ? "RIGHT PART OF ORDER BY":"ORDER BY");
+ explainTempTable(pParse,
+ sSort.nOBSat>0 ? "RIGHT PART OF ORDER BY":"ORDER BY");
generateSortTail(pParse, p, &sSort, pEList->nExpr, pDest);
}
@@ -111978,100 +118542,6 @@ select_end:
return rc;
}
-#ifdef SQLITE_DEBUG
-/*
-** Generate a human-readable description of a the Select object.
-*/
-SQLITE_PRIVATE void sqlite3TreeViewSelect(TreeView *pView, const Select *p, u8 moreToFollow){
- int n = 0;
- pView = sqlite3TreeViewPush(pView, moreToFollow);
- sqlite3TreeViewLine(pView, "SELECT%s%s (0x%p)",
- ((p->selFlags & SF_Distinct) ? " DISTINCT" : ""),
- ((p->selFlags & SF_Aggregate) ? " agg_flag" : ""), p
- );
- if( p->pSrc && p->pSrc->nSrc ) n++;
- if( p->pWhere ) n++;
- if( p->pGroupBy ) n++;
- if( p->pHaving ) n++;
- if( p->pOrderBy ) n++;
- if( p->pLimit ) n++;
- if( p->pOffset ) n++;
- if( p->pPrior ) n++;
- sqlite3TreeViewExprList(pView, p->pEList, (n--)>0, "result-set");
- if( p->pSrc && p->pSrc->nSrc ){
- int i;
- pView = sqlite3TreeViewPush(pView, (n--)>0);
- sqlite3TreeViewLine(pView, "FROM");
- for(i=0; i<p->pSrc->nSrc; i++){
- struct SrcList_item *pItem = &p->pSrc->a[i];
- StrAccum x;
- char zLine[100];
- 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);
- }else if( pItem->zName ){
- sqlite3XPrintf(&x, 0, " %s", pItem->zName);
- }
- if( pItem->pTab ){
- sqlite3XPrintf(&x, 0, " tabname=%Q", pItem->pTab->zName);
- }
- if( pItem->zAlias ){
- sqlite3XPrintf(&x, 0, " (AS %s)", pItem->zAlias);
- }
- if( pItem->jointype & JT_LEFT ){
- sqlite3XPrintf(&x, 0, " LEFT-JOIN");
- }
- sqlite3StrAccumFinish(&x);
- sqlite3TreeViewItem(pView, zLine, i<p->pSrc->nSrc-1);
- if( pItem->pSelect ){
- sqlite3TreeViewSelect(pView, pItem->pSelect, 0);
- }
- sqlite3TreeViewPop(pView);
- }
- sqlite3TreeViewPop(pView);
- }
- if( p->pWhere ){
- sqlite3TreeViewItem(pView, "WHERE", (n--)>0);
- sqlite3TreeViewExpr(pView, p->pWhere, 0);
- sqlite3TreeViewPop(pView);
- }
- if( p->pGroupBy ){
- sqlite3TreeViewExprList(pView, p->pGroupBy, (n--)>0, "GROUPBY");
- }
- if( p->pHaving ){
- sqlite3TreeViewItem(pView, "HAVING", (n--)>0);
- sqlite3TreeViewExpr(pView, p->pHaving, 0);
- sqlite3TreeViewPop(pView);
- }
- if( p->pOrderBy ){
- sqlite3TreeViewExprList(pView, p->pOrderBy, (n--)>0, "ORDERBY");
- }
- if( p->pLimit ){
- sqlite3TreeViewItem(pView, "LIMIT", (n--)>0);
- sqlite3TreeViewExpr(pView, p->pLimit, 0);
- sqlite3TreeViewPop(pView);
- }
- if( p->pOffset ){
- sqlite3TreeViewItem(pView, "OFFSET", (n--)>0);
- sqlite3TreeViewExpr(pView, p->pOffset, 0);
- sqlite3TreeViewPop(pView);
- }
- if( p->pPrior ){
- const char *zOp = "UNION";
- switch( p->op ){
- case TK_ALL: zOp = "UNION ALL"; break;
- case TK_INTERSECT: zOp = "INTERSECT"; break;
- case TK_EXCEPT: zOp = "EXCEPT"; break;
- }
- sqlite3TreeViewItem(pView, zOp, (n--)>0);
- sqlite3TreeViewSelect(pView, p->pPrior, 0);
- sqlite3TreeViewPop(pView);
- }
- sqlite3TreeViewPop(pView);
-}
-#endif /* SQLITE_DEBUG */
-
/************** End of select.c **********************************************/
/************** Begin file table.c *******************************************/
/*
@@ -112092,6 +118562,7 @@ SQLITE_PRIVATE void sqlite3TreeViewSelect(TreeView *pView, const Select *p, u8 m
** These routines are in a separate files so that they will not be linked
** if they are not used.
*/
+/* #include "sqliteInt.h" */
/* #include <stdlib.h> */
/* #include <string.h> */
@@ -112176,7 +118647,7 @@ static int sqlite3_get_table_cb(void *pArg, int nCol, char **argv, char **colv){
return 0;
malloc_failed:
- p->rc = SQLITE_NOMEM;
+ p->rc = SQLITE_NOMEM_BKPT;
return 1;
}
@@ -112217,7 +118688,7 @@ SQLITE_API int SQLITE_STDCALL sqlite3_get_table(
res.azResult = sqlite3_malloc64(sizeof(char*)*res.nAlloc );
if( res.azResult==0 ){
db->errCode = SQLITE_NOMEM;
- return SQLITE_NOMEM;
+ return SQLITE_NOMEM_BKPT;
}
res.azResult[0] = 0;
rc = sqlite3_exec(db, zSql, sqlite3_get_table_cb, &res, pzErrMsg);
@@ -112246,7 +118717,7 @@ SQLITE_API int SQLITE_STDCALL sqlite3_get_table(
if( azNew==0 ){
sqlite3_free_table(&res.azResult[1]);
db->errCode = SQLITE_NOMEM;
- return SQLITE_NOMEM;
+ return SQLITE_NOMEM_BKPT;
}
res.azResult = azNew;
}
@@ -112288,6 +118759,7 @@ SQLITE_API void SQLITE_STDCALL sqlite3_free_table(
*************************************************************************
** This file contains the implementation for TRIGGERs
*/
+/* #include "sqliteInt.h" */
#ifndef SQLITE_OMIT_TRIGGER
/*
@@ -112564,8 +119036,7 @@ SQLITE_PRIVATE void sqlite3FinishTrigger(
pStepList->pTrig = pTrig;
pStepList = pStepList->pNext;
}
- nameToken.z = pTrig->zName;
- nameToken.n = sqlite3Strlen30(nameToken.z);
+ sqlite3TokenInit(&nameToken, pTrig->zName);
sqlite3FixInit(&sFix, pParse, iDb, "trigger", &nameToken);
if( sqlite3FixTriggerStep(&sFix, pTrig->step_list)
|| sqlite3FixExpr(&sFix, pTrig->pWhen)
@@ -112601,7 +119072,7 @@ SQLITE_PRIVATE void sqlite3FinishTrigger(
assert( sqlite3SchemaMutexHeld(db, iDb, 0) );
pTrig = sqlite3HashInsert(pHash, zName, pTrig);
if( pTrig ){
- db->mallocFailed = 1;
+ sqlite3OomFault(db);
}else if( pLink->pSchema==pLink->pTabSchema ){
Table *pTab;
pTab = sqlite3HashFind(&pLink->pTabSchema->tblHash, pLink->table);
@@ -112836,31 +119307,12 @@ SQLITE_PRIVATE void sqlite3DropTriggerPtr(Parse *pParse, Trigger *pTrigger){
*/
assert( pTable!=0 );
if( (v = sqlite3GetVdbe(pParse))!=0 ){
- int base;
- static const int iLn = VDBE_OFFSET_LINENO(2);
- static const VdbeOpList dropTrigger[] = {
- { OP_Rewind, 0, ADDR(9), 0},
- { OP_String8, 0, 1, 0}, /* 1 */
- { OP_Column, 0, 1, 2},
- { OP_Ne, 2, ADDR(8), 1},
- { OP_String8, 0, 1, 0}, /* 4: "trigger" */
- { OP_Column, 0, 0, 2},
- { OP_Ne, 2, ADDR(8), 1},
- { OP_Delete, 0, 0, 0},
- { OP_Next, 0, ADDR(1), 0}, /* 8 */
- };
-
- sqlite3BeginWriteOperation(pParse, 0, iDb);
- sqlite3OpenMasterTable(pParse, iDb);
- base = sqlite3VdbeAddOpList(v, ArraySize(dropTrigger), dropTrigger, iLn);
- sqlite3VdbeChangeP4(v, base+1, pTrigger->zName, P4_TRANSIENT);
- sqlite3VdbeChangeP4(v, base+4, "trigger", P4_STATIC);
+ sqlite3NestedParse(pParse,
+ "DELETE FROM %Q.%s WHERE name=%Q AND type='trigger'",
+ db->aDb[iDb].zName, SCHEMA_TABLE(iDb), pTrigger->zName
+ );
sqlite3ChangeCookie(pParse, iDb);
- sqlite3VdbeAddOp2(v, OP_Close, 0, 0);
sqlite3VdbeAddOp4(v, OP_DropTrigger, iDb, 0, 0, pTrigger->zName, 0);
- if( pParse->nMem<3 ){
- pParse->nMem = 3;
- }
}
}
@@ -113248,8 +119700,8 @@ SQLITE_PRIVATE void sqlite3CodeRowTriggerDirect(
if( pPrg ){
int bRecursive = (p->zName && 0==(pParse->db->flags&SQLITE_RecTriggers));
- sqlite3VdbeAddOp3(v, OP_Program, reg, ignoreJump, ++pParse->nMem);
- sqlite3VdbeChangeP4(v, -1, (const char *)pPrg->pProgram, P4_SUBPROGRAM);
+ sqlite3VdbeAddOp4(v, OP_Program, reg, ignoreJump, ++pParse->nMem,
+ (const char *)pPrg->pProgram, P4_SUBPROGRAM);
VdbeComment(
(v, "Call: %s.%s", (p->zName?p->zName:"fkey"), onErrorText(orconf)));
@@ -113411,6 +119863,7 @@ SQLITE_PRIVATE u32 sqlite3TriggerColmask(
** This file contains C code routines that are called by the parser
** to handle UPDATE statements.
*/
+/* #include "sqliteInt.h" */
#ifndef SQLITE_OMIT_VIRTUALTABLE
/* Forward declaration */
@@ -113532,9 +119985,9 @@ SQLITE_PRIVATE void sqlite3Update(
/* Register Allocations */
int regRowCount = 0; /* A count of rows changed */
- int regOldRowid; /* The old rowid */
- int regNewRowid; /* The new rowid */
- int regNew; /* Content of the NEW.* table in triggers */
+ int regOldRowid = 0; /* The old rowid */
+ int regNewRowid = 0; /* The new rowid */
+ int regNew = 0; /* Content of the NEW.* table in triggers */
int regOld = 0; /* Content of OLD.* table in triggers */
int regRowSet = 0; /* Rowset of rows to be updated */
int regKey = 0; /* composite PRIMARY KEY value */
@@ -113595,7 +120048,7 @@ SQLITE_PRIVATE void sqlite3Update(
/* Allocate space for aXRef[], aRegIdx[], and aToOpen[].
** Initialize aXRef[] and aToOpen[] to their default values.
*/
- aXRef = sqlite3DbMallocRaw(db, sizeof(int) * (pTab->nCol+nIdx) + nIdx+2 );
+ aXRef = sqlite3DbMallocRawNN(db, sizeof(int) * (pTab->nCol+nIdx) + nIdx+2 );
if( aXRef==0 ) goto update_cleanup;
aRegIdx = aXRef+pTab->nCol;
aToOpen = (u8*)(aRegIdx+nIdx);
@@ -113661,16 +120114,20 @@ SQLITE_PRIVATE void sqlite3Update(
assert( chngPk==0 || chngPk==1 );
chngKey = chngRowid + chngPk;
- /* The SET expressions are not actually used inside the WHERE loop.
- ** So reset the colUsed mask
+ /* The SET expressions are not actually used inside the WHERE loop.
+ ** So reset the colUsed mask. Unless this is a virtual table. In that
+ ** case, set all bits of the colUsed mask (to ensure that the virtual
+ ** table implementation makes all columns available).
*/
- pTabList->a[0].colUsed = 0;
+ pTabList->a[0].colUsed = IsVirtual(pTab) ? ALLBITS : 0;
hasFK = sqlite3FkRequired(pParse, pTab, aXRef, chngKey);
/* There is one entry in the aRegIdx[] array for each index on the table
** being updated. Fill in aRegIdx[] with a register number that will hold
- ** the key for accessing each index.
+ ** the key for accessing each index.
+ **
+ ** FIXME: Be smarter about omitting indexes that use expressions.
*/
for(j=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, j++){
int reg;
@@ -113679,7 +120136,8 @@ SQLITE_PRIVATE void sqlite3Update(
}else{
reg = 0;
for(i=0; i<pIdx->nKeyCol; i++){
- if( aXRef[pIdx->aiColumn[i]]>=0 ){
+ i16 iIdxCol = pIdx->aiColumn[i];
+ if( iIdxCol<0 || aXRef[iIdxCol]>=0 ){
reg = ++pParse->nMem;
break;
}
@@ -113695,29 +120153,20 @@ SQLITE_PRIVATE void sqlite3Update(
if( pParse->nested==0 ) sqlite3VdbeCountChanges(v);
sqlite3BeginWriteOperation(pParse, 1, iDb);
-#ifndef SQLITE_OMIT_VIRTUALTABLE
- /* Virtual tables must be handled separately */
- if( IsVirtual(pTab) ){
- updateVirtualTable(pParse, pTabList, pTab, pChanges, pRowidExpr, aXRef,
- pWhere, onError);
- pWhere = 0;
- pTabList = 0;
- goto update_cleanup;
- }
-#endif
-
/* Allocate required registers. */
- regRowSet = ++pParse->nMem;
- regOldRowid = regNewRowid = ++pParse->nMem;
- if( chngPk || pTrigger || hasFK ){
- regOld = pParse->nMem + 1;
+ if( !IsVirtual(pTab) ){
+ regRowSet = ++pParse->nMem;
+ regOldRowid = regNewRowid = ++pParse->nMem;
+ if( chngPk || pTrigger || hasFK ){
+ regOld = pParse->nMem + 1;
+ pParse->nMem += pTab->nCol;
+ }
+ if( chngKey || pTrigger || hasFK ){
+ regNewRowid = ++pParse->nMem;
+ }
+ regNew = pParse->nMem + 1;
pParse->nMem += pTab->nCol;
}
- if( chngKey || pTrigger || hasFK ){
- regNewRowid = ++pParse->nMem;
- }
- regNew = pParse->nMem + 1;
- pParse->nMem += pTab->nCol;
/* Start the view context. */
if( isView ){
@@ -113740,12 +120189,22 @@ SQLITE_PRIVATE void sqlite3Update(
goto update_cleanup;
}
+#ifndef SQLITE_OMIT_VIRTUALTABLE
+ /* Virtual tables must be handled separately */
+ if( IsVirtual(pTab) ){
+ updateVirtualTable(pParse, pTabList, pTab, pChanges, pRowidExpr, aXRef,
+ pWhere, onError);
+ goto update_cleanup;
+ }
+#endif
+
/* Begin the database scan
*/
if( HasRowid(pTab) ){
sqlite3VdbeAddOp3(v, OP_Null, 0, regRowSet, regOldRowid);
pWInfo = sqlite3WhereBegin(
- pParse, pTabList, pWhere, 0, 0, WHERE_ONEPASS_DESIRED, iIdxCur
+ pParse, pTabList, pWhere, 0, 0,
+ WHERE_ONEPASS_DESIRED | WHERE_SEEK_TABLE, iIdxCur
);
if( pWInfo==0 ) goto update_cleanup;
okOnePass = sqlite3WhereOkOnePass(pWInfo, aiCurOnePass);
@@ -113779,6 +120238,7 @@ SQLITE_PRIVATE void sqlite3Update(
if( pWInfo==0 ) goto update_cleanup;
okOnePass = sqlite3WhereOkOnePass(pWInfo, aiCurOnePass);
for(i=0; i<nPk; i++){
+ assert( pPk->aiColumn[i]>=0 );
sqlite3ExprCodeGetColumnOfTable(v, pTab, iDataCur, pPk->aiColumn[i],
iPk+i);
}
@@ -113788,7 +120248,7 @@ SQLITE_PRIVATE void sqlite3Update(
regKey = iPk;
}else{
sqlite3VdbeAddOp4(v, OP_MakeRecord, iPk, nPk, regKey,
- sqlite3IndexAffinityStr(v, pPk), nPk);
+ sqlite3IndexAffinityStr(db, pPk), nPk);
sqlite3VdbeAddOp2(v, OP_IdxInsert, iEph, regKey);
}
sqlite3WhereEnd(pWInfo);
@@ -113823,7 +120283,7 @@ SQLITE_PRIVATE void sqlite3Update(
if( aiCurOnePass[0]>=0 ) aToOpen[aiCurOnePass[0]-iBaseCur] = 0;
if( aiCurOnePass[1]>=0 ) aToOpen[aiCurOnePass[1]-iBaseCur] = 0;
}
- sqlite3OpenTableAndIndices(pParse, pTab, OP_OpenWrite, iBaseCur, aToOpen,
+ sqlite3OpenTableAndIndices(pParse, pTab, OP_OpenWrite, 0, iBaseCur, aToOpen,
0, 0);
}
@@ -113901,7 +120361,6 @@ SQLITE_PRIVATE void sqlite3Update(
newmask = sqlite3TriggerColmask(
pParse, pTrigger, pChanges, 1, TRIGGER_BEFORE, pTab, onError
);
- /*sqlite3VdbeAddOp3(v, OP_Null, 0, regNew, regNew+pTab->nCol-1);*/
for(i=0; i<pTab->nCol; i++){
if( i==pTab->iPKey ){
sqlite3VdbeAddOp2(v, OP_Null, 0, regNew+i);
@@ -113917,7 +120376,7 @@ SQLITE_PRIVATE void sqlite3Update(
*/
testcase( i==31 );
testcase( i==32 );
- sqlite3ExprCodeGetColumnOfTable(v, pTab, iDataCur, i, regNew+i);
+ sqlite3ExprCodeGetColumnToReg(pParse, pTab, i, iDataCur, regNew+i);
}else{
sqlite3VdbeAddOp2(v, OP_Null, 0, regNew+i);
}
@@ -113959,13 +120418,14 @@ SQLITE_PRIVATE void sqlite3Update(
}
if( !isView ){
- int j1 = 0; /* Address of jump instruction */
+ int addr1 = 0; /* Address of jump instruction */
int bReplace = 0; /* True if REPLACE conflict resolution might happen */
/* Do constraint checks. */
assert( regOldRowid>0 );
sqlite3GenerateConstraintChecks(pParse, pTab, aRegIdx, iDataCur, iIdxCur,
- regNewRowid, regOldRowid, chngKey, onError, labelContinue, &bReplace);
+ regNewRowid, regOldRowid, chngKey, onError, labelContinue, &bReplace,
+ aXRef);
/* Do FK constraint checks. */
if( hasFK ){
@@ -113975,20 +120435,39 @@ SQLITE_PRIVATE void sqlite3Update(
/* Delete the index entries associated with the current record. */
if( bReplace || chngKey ){
if( pPk ){
- j1 = sqlite3VdbeAddOp4Int(v, OP_NotFound, iDataCur, 0, regKey, nKey);
+ addr1 = sqlite3VdbeAddOp4Int(v, OP_NotFound, iDataCur, 0, regKey, nKey);
}else{
- j1 = sqlite3VdbeAddOp3(v, OP_NotExists, iDataCur, 0, regOldRowid);
+ addr1 = sqlite3VdbeAddOp3(v, OP_NotExists, iDataCur, 0, regOldRowid);
}
VdbeCoverageNeverTaken(v);
}
- sqlite3GenerateRowIndexDelete(pParse, pTab, iDataCur, iIdxCur, aRegIdx);
-
- /* If changing the record number, delete the old record. */
+ sqlite3GenerateRowIndexDelete(pParse, pTab, iDataCur, iIdxCur, aRegIdx, -1);
+
+ /* If changing the rowid value, or if there are foreign key constraints
+ ** to process, delete the old record. Otherwise, add a noop OP_Delete
+ ** to invoke the pre-update hook.
+ **
+ ** That (regNew==regnewRowid+1) is true is also important for the
+ ** pre-update hook. If the caller invokes preupdate_new(), the returned
+ ** value is copied from memory cell (regNewRowid+1+iCol), where iCol
+ ** is the column index supplied by the user.
+ */
+ assert( regNew==regNewRowid+1 );
+#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
+ sqlite3VdbeAddOp3(v, OP_Delete, iDataCur,
+ OPFLAG_ISUPDATE | ((hasFK || chngKey || pPk!=0) ? 0 : OPFLAG_ISNOOP),
+ regNewRowid
+ );
+ if( !pParse->nested ){
+ sqlite3VdbeChangeP4(v, -1, (char*)pTab, P4_TABLE);
+ }
+#else
if( hasFK || chngKey || pPk!=0 ){
sqlite3VdbeAddOp2(v, OP_Delete, iDataCur, 0);
}
+#endif
if( bReplace || chngKey ){
- sqlite3VdbeJumpHere(v, j1);
+ sqlite3VdbeJumpHere(v, addr1);
}
if( hasFK ){
@@ -114025,7 +120504,7 @@ SQLITE_PRIVATE void sqlite3Update(
sqlite3VdbeResolveLabel(v, labelContinue);
sqlite3VdbeAddOp2(v, OP_Next, iEph, addrTop); VdbeCoverage(v);
}else{
- sqlite3VdbeAddOp2(v, OP_Goto, 0, labelContinue);
+ sqlite3VdbeGoto(v, labelContinue);
}
sqlite3VdbeResolveLabel(v, labelBreak);
@@ -114079,21 +120558,23 @@ update_cleanup:
/*
** Generate code for an UPDATE of a virtual table.
**
-** The strategy is that we create an ephemeral table that contains
+** There are two possible strategies - the default and the special
+** "onepass" strategy. Onepass is only used if the virtual table
+** implementation indicates that pWhere may match at most one row.
+**
+** The default strategy is to create an ephemeral table that contains
** for each row to be changed:
**
** (A) The original rowid of that row.
-** (B) The revised rowid for the row. (note1)
+** (B) The revised rowid for the row.
** (C) The content of every column in the row.
**
-** Then we loop over this ephemeral table and for each row in
-** the ephemeral table call VUpdate.
-**
-** When finished, drop the ephemeral table.
+** Then loop through the contents of this ephemeral table executing a
+** VUpdate for each row. When finished, drop the ephemeral table.
**
-** (note1) Actually, if we know in advance that (A) is always the same
-** as (B) we only store (A), then duplicate (A) when pulling
-** it out of the ephemeral table before calling VUpdate.
+** The "onepass" strategy does not use an ephemeral table. Instead, it
+** stores the same values (A, B and C above) in a register array and
+** makes a single invocation of VUpdate.
*/
static void updateVirtualTable(
Parse *pParse, /* The parsing context */
@@ -114106,68 +120587,96 @@ static void updateVirtualTable(
int onError /* ON CONFLICT strategy */
){
Vdbe *v = pParse->pVdbe; /* Virtual machine under construction */
- ExprList *pEList = 0; /* The result set of the SELECT statement */
- Select *pSelect = 0; /* The SELECT statement */
- Expr *pExpr; /* Temporary expression */
int ephemTab; /* Table holding the result of the SELECT */
int i; /* Loop counter */
- int addr; /* Address of top of loop */
- int iReg; /* First register in set passed to OP_VUpdate */
sqlite3 *db = pParse->db; /* Database connection */
const char *pVTab = (const char*)sqlite3GetVTable(db, pTab);
- SelectDest dest;
-
- /* Construct the SELECT statement that will find the new values for
- ** all updated rows.
- */
- pEList = sqlite3ExprListAppend(pParse, 0, sqlite3Expr(db, TK_ID, "_rowid_"));
+ WhereInfo *pWInfo;
+ int nArg = 2 + pTab->nCol; /* Number of arguments to VUpdate */
+ int regArg; /* First register in VUpdate arg array */
+ int regRec; /* Register in which to assemble record */
+ int regRowid; /* Register for ephem table rowid */
+ int iCsr = pSrc->a[0].iCursor; /* Cursor used for virtual table scan */
+ int aDummy[2]; /* Unused arg for sqlite3WhereOkOnePass() */
+ int bOnePass; /* True to use onepass strategy */
+ int addr; /* Address of OP_OpenEphemeral */
+
+ /* Allocate nArg registers to martial the arguments to VUpdate. Then
+ ** create and open the ephemeral table in which the records created from
+ ** these arguments will be temporarily stored. */
+ assert( v );
+ ephemTab = pParse->nTab++;
+ addr= sqlite3VdbeAddOp2(v, OP_OpenEphemeral, ephemTab, nArg);
+ regArg = pParse->nMem + 1;
+ pParse->nMem += nArg;
+ regRec = ++pParse->nMem;
+ regRowid = ++pParse->nMem;
+
+ /* Start scanning the virtual table */
+ pWInfo = sqlite3WhereBegin(pParse, pSrc, pWhere, 0,0,WHERE_ONEPASS_DESIRED,0);
+ if( pWInfo==0 ) return;
+
+ /* Populate the argument registers. */
+ sqlite3VdbeAddOp2(v, OP_Rowid, iCsr, regArg);
if( pRowid ){
- pEList = sqlite3ExprListAppend(pParse, pEList,
- sqlite3ExprDup(db, pRowid, 0));
+ sqlite3ExprCode(pParse, pRowid, regArg+1);
+ }else{
+ sqlite3VdbeAddOp2(v, OP_Rowid, iCsr, regArg+1);
}
- assert( pTab->iPKey<0 );
for(i=0; i<pTab->nCol; i++){
if( aXRef[i]>=0 ){
- pExpr = sqlite3ExprDup(db, pChanges->a[aXRef[i]].pExpr, 0);
+ sqlite3ExprCode(pParse, pChanges->a[aXRef[i]].pExpr, regArg+2+i);
}else{
- pExpr = sqlite3Expr(db, TK_ID, pTab->aCol[i].zName);
+ sqlite3VdbeAddOp3(v, OP_VColumn, iCsr, i, regArg+2+i);
}
- pEList = sqlite3ExprListAppend(pParse, pEList, pExpr);
}
- pSelect = sqlite3SelectNew(pParse, pEList, pSrc, pWhere, 0, 0, 0, 0, 0, 0);
-
- /* Create the ephemeral table into which the update results will
- ** be stored.
- */
- assert( v );
- ephemTab = pParse->nTab++;
- sqlite3VdbeAddOp2(v, OP_OpenEphemeral, ephemTab, pTab->nCol+1+(pRowid!=0));
- sqlite3VdbeChangeP5(v, BTREE_UNORDERED);
- /* fill the ephemeral table
- */
- sqlite3SelectDestInit(&dest, SRT_Table, ephemTab);
- sqlite3Select(pParse, pSelect, &dest);
+ bOnePass = sqlite3WhereOkOnePass(pWInfo, aDummy);
- /* Generate code to scan the ephemeral table and call VUpdate. */
- iReg = ++pParse->nMem;
- pParse->nMem += pTab->nCol+1;
- addr = sqlite3VdbeAddOp2(v, OP_Rewind, ephemTab, 0); VdbeCoverage(v);
- sqlite3VdbeAddOp3(v, OP_Column, ephemTab, 0, iReg);
- sqlite3VdbeAddOp3(v, OP_Column, ephemTab, (pRowid?1:0), iReg+1);
- for(i=0; i<pTab->nCol; i++){
- sqlite3VdbeAddOp3(v, OP_Column, ephemTab, i+1+(pRowid!=0), iReg+2+i);
+ if( bOnePass ){
+ /* If using the onepass strategy, no-op out the OP_OpenEphemeral coded
+ ** above. Also, if this is a top-level parse (not a trigger), clear the
+ ** multi-write flag so that the VM does not open a statement journal */
+ sqlite3VdbeChangeToNoop(v, addr);
+ if( sqlite3IsToplevel(pParse) ){
+ pParse->isMultiWrite = 0;
+ }
+ }else{
+ /* Create a record from the argument register contents and insert it into
+ ** the ephemeral table. */
+ sqlite3VdbeAddOp3(v, OP_MakeRecord, regArg, nArg, regRec);
+ sqlite3VdbeAddOp2(v, OP_NewRowid, ephemTab, regRowid);
+ sqlite3VdbeAddOp3(v, OP_Insert, ephemTab, regRec, regRowid);
+ }
+
+
+ if( bOnePass==0 ){
+ /* End the virtual table scan */
+ sqlite3WhereEnd(pWInfo);
+
+ /* Begin scannning through the ephemeral table. */
+ addr = sqlite3VdbeAddOp1(v, OP_Rewind, ephemTab); VdbeCoverage(v);
+
+ /* Extract arguments from the current row of the ephemeral table and
+ ** invoke the VUpdate method. */
+ for(i=0; i<nArg; i++){
+ sqlite3VdbeAddOp3(v, OP_Column, ephemTab, i, regArg+i);
+ }
}
sqlite3VtabMakeWritable(pParse, pTab);
- sqlite3VdbeAddOp4(v, OP_VUpdate, 0, pTab->nCol+2, iReg, pVTab, P4_VTAB);
+ sqlite3VdbeAddOp4(v, OP_VUpdate, 0, nArg, regArg, pVTab, P4_VTAB);
sqlite3VdbeChangeP5(v, onError==OE_Default ? OE_Abort : onError);
sqlite3MayAbort(pParse);
- sqlite3VdbeAddOp2(v, OP_Next, ephemTab, addr+1); VdbeCoverage(v);
- sqlite3VdbeJumpHere(v, addr);
- sqlite3VdbeAddOp2(v, OP_Close, ephemTab, 0);
- /* Cleanup */
- sqlite3SelectDelete(db, pSelect);
+ /* End of the ephemeral table scan. Or, if using the onepass strategy,
+ ** jump to here if the scan visited zero rows. */
+ if( bOnePass==0 ){
+ sqlite3VdbeAddOp2(v, OP_Next, ephemTab, addr+1); VdbeCoverage(v);
+ sqlite3VdbeJumpHere(v, addr);
+ sqlite3VdbeAddOp2(v, OP_Close, ephemTab, 0);
+ }else{
+ sqlite3WhereEnd(pWInfo);
+ }
}
#endif /* SQLITE_OMIT_VIRTUALTABLE */
@@ -114189,6 +120698,8 @@ static void updateVirtualTable(
** Most of the code in this file may be omitted by defining the
** SQLITE_OMIT_VACUUM macro.
*/
+/* #include "sqliteInt.h" */
+/* #include "vdbeInt.h" */
#if !defined(SQLITE_OMIT_VACUUM) && !defined(SQLITE_OMIT_ATTACH)
/*
@@ -114211,7 +120722,7 @@ static int execSql(sqlite3 *db, char **pzErrMsg, const char *zSql){
sqlite3_stmt *pStmt;
VVA_ONLY( int rc; )
if( !zSql ){
- return SQLITE_NOMEM;
+ return SQLITE_NOMEM_BKPT;
}
if( SQLITE_OK!=sqlite3_prepare(db, zSql, -1, &pStmt, 0) ){
sqlite3SetString(pzErrMsg, db, sqlite3_errmsg(db));
@@ -114392,7 +120903,7 @@ SQLITE_PRIVATE int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db){
|| (!isMemDb && sqlite3BtreeSetPageSize(pTemp, db->nextPagesize, nRes, 0))
|| NEVER(db->mallocFailed)
){
- rc = SQLITE_NOMEM;
+ rc = SQLITE_NOMEM_BKPT;
goto end_of_vacuum;
}
@@ -114561,6 +121072,7 @@ end_of_vacuum:
** This file contains code used to help implement virtual tables.
*/
#ifndef SQLITE_OMIT_VIRTUALTABLE
+/* #include "sqliteInt.h" */
/*
** Before a virtual table xCreate() or xConnect() method is invoked, the
@@ -114597,7 +121109,7 @@ static int createModule(
rc = SQLITE_MISUSE_BKPT;
}else{
Module *pMod;
- pMod = (Module *)sqlite3DbMallocRaw(db, sizeof(Module) + nName + 1);
+ pMod = (Module *)sqlite3DbMallocRawNN(db, sizeof(Module) + nName + 1);
if( pMod ){
Module *pDel;
char *zCopy = (char *)(&pMod[1]);
@@ -114606,10 +121118,11 @@ static int createModule(
pMod->pModule = pModule;
pMod->pAux = pAux;
pMod->xDestroy = xDestroy;
+ pMod->pEpoTab = 0;
pDel = (Module *)sqlite3HashInsert(&db->aModule,zCopy,(void*)pMod);
assert( pDel==0 || pDel==pMod );
if( pDel ){
- db->mallocFailed = 1;
+ sqlite3OomFault(db);
sqlite3DbFree(db, pDel);
}
}
@@ -114833,23 +121346,17 @@ SQLITE_PRIVATE void sqlite3VtabClear(sqlite3 *db, Table *p){
** deleted.
*/
static void addModuleArgument(sqlite3 *db, Table *pTable, char *zArg){
- int i = pTable->nModuleArg++;
- int nBytes = sizeof(char *)*(1+pTable->nModuleArg);
+ int nBytes = sizeof(char *)*(2+pTable->nModuleArg);
char **azModuleArg;
azModuleArg = sqlite3DbRealloc(db, pTable->azModuleArg, nBytes);
if( azModuleArg==0 ){
- int j;
- for(j=0; j<i; j++){
- sqlite3DbFree(db, pTable->azModuleArg[j]);
- }
sqlite3DbFree(db, zArg);
- sqlite3DbFree(db, pTable->azModuleArg);
- pTable->nModuleArg = 0;
}else{
+ int i = pTable->nModuleArg++;
azModuleArg[i] = zArg;
azModuleArg[i+1] = 0;
+ pTable->azModuleArg = azModuleArg;
}
- pTable->azModuleArg = azModuleArg;
}
/*
@@ -114976,7 +121483,7 @@ SQLITE_PRIVATE void sqlite3VtabFinishParse(Parse *pParse, Token *pEnd){
sqlite3VdbeAddParseSchemaOp(v, iDb, zWhere);
iReg = ++pParse->nMem;
- sqlite3VdbeAddOp4(v, OP_String8, 0, iReg, 0, pTab->zName, 0);
+ sqlite3VdbeLoadString(v, iReg, pTab->zName);
sqlite3VdbeAddOp2(v, OP_VCreate, iDb, iReg);
}
@@ -114992,7 +121499,7 @@ SQLITE_PRIVATE void sqlite3VtabFinishParse(Parse *pParse, Token *pEnd){
assert( sqlite3SchemaMutexHeld(db, 0, pSchema) );
pOld = sqlite3HashInsert(&pSchema->tblHash, zName, pTab);
if( pOld ){
- db->mallocFailed = 1;
+ sqlite3OomFault(db);
assert( pTab==pOld ); /* Malloc must have failed inside HashInsert() */
return;
}
@@ -115059,13 +121566,13 @@ static int vtabCallConstructor(
zModuleName = sqlite3MPrintf(db, "%s", pTab->zName);
if( !zModuleName ){
- return SQLITE_NOMEM;
+ return SQLITE_NOMEM_BKPT;
}
pVTable = sqlite3DbMallocZero(db, sizeof(VTable));
if( !pVTable ){
sqlite3DbFree(db, zModuleName);
- return SQLITE_NOMEM;
+ return SQLITE_NOMEM_BKPT;
}
pVTable->db = db;
pVTable->pMod = pMod;
@@ -115083,7 +121590,7 @@ static int vtabCallConstructor(
db->pVtabCtx = &sCtx;
rc = xConstruct(db, pMod->pAux, nArg, azArg, &pVTable->pVtab, &zErr);
db->pVtabCtx = sCtx.pPrior;
- if( rc==SQLITE_NOMEM ) db->mallocFailed = 1;
+ if( rc==SQLITE_NOMEM ) sqlite3OomFault(db);
assert( sCtx.pTab==pTab );
if( SQLITE_OK!=rc ){
@@ -115117,22 +121624,16 @@ static int vtabCallConstructor(
pTab->pVTable = pVTable;
for(iCol=0; iCol<pTab->nCol; iCol++){
- char *zType = pTab->aCol[iCol].zType;
+ char *zType = sqlite3ColumnType(&pTab->aCol[iCol], "");
int nType;
int i = 0;
- if( !zType ){
- pTab->tabFlags |= oooHidden;
- continue;
- }
nType = sqlite3Strlen30(zType);
- if( sqlite3StrNICmp("hidden", zType, 6)||(zType[6] && zType[6]!=' ') ){
- for(i=0; i<nType; i++){
- if( (0==sqlite3StrNICmp(" hidden", &zType[i], 7))
- && (zType[i+7]=='\0' || zType[i+7]==' ')
- ){
- i++;
- break;
- }
+ for(i=0; i<nType; i++){
+ if( 0==sqlite3StrNICmp("hidden", &zType[i], 6)
+ && (i==0 || zType[i-1]==' ')
+ && (zType[i+6]=='\0' || zType[i+6]==' ')
+ ){
+ break;
}
}
if( i<nType ){
@@ -115208,7 +121709,7 @@ static int growVTrans(sqlite3 *db){
int nBytes = sizeof(sqlite3_vtab *) * (db->nVTrans + ARRAY_INCR);
aVTrans = sqlite3DbRealloc(db, (void *)db->aVTrans, nBytes);
if( !aVTrans ){
- return SQLITE_NOMEM;
+ return SQLITE_NOMEM_BKPT;
}
memset(&aVTrans[db->nVTrans], 0, sizeof(sqlite3_vtab *)*ARRAY_INCR);
db->aVTrans = aVTrans;
@@ -115252,7 +121753,7 @@ SQLITE_PRIVATE int sqlite3VtabCallCreate(sqlite3 *db, int iDb, const char *zTab,
** invoke it now. If the module has not been registered, return an
** error. Otherwise, do nothing.
*/
- if( !pMod ){
+ if( pMod==0 || pMod->pModule->xCreate==0 || pMod->pModule->xDestroy==0 ){
*pzErr = sqlite3MPrintf(db, "no such module: %s", zMod);
rc = SQLITE_ERROR;
}else{
@@ -115300,7 +121801,7 @@ SQLITE_API int SQLITE_STDCALL sqlite3_declare_vtab(sqlite3 *db, const char *zCre
pParse = sqlite3StackAllocZero(db, sizeof(*pParse));
if( pParse==0 ){
- rc = SQLITE_NOMEM;
+ rc = SQLITE_NOMEM_BKPT;
}else{
pParse->declareVtab = 1;
pParse->db = db;
@@ -115354,6 +121855,7 @@ 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;
+ int (*xDestroy)(sqlite3_vtab *);
for(p=pTab->pVTable; p; p=p->pNext){
assert( p->pVtab );
if( p->pVtab->nRef>0 ){
@@ -115361,7 +121863,9 @@ SQLITE_PRIVATE int sqlite3VtabCallDestroy(sqlite3 *db, int iDb, const char *zTab
}
}
p = vtabDisconnectAll(db, pTab);
- rc = p->pMod->pModule->xDestroy(p->pVtab);
+ xDestroy = p->pMod->pModule->xDestroy;
+ assert( xDestroy!=0 ); /* Checked before the virtual table is created */
+ rc = xDestroy(p->pVtab);
/* Remove the sqlite3_vtab* from the aVTrans[] array, if applicable */
if( rc==SQLITE_OK ){
assert( pTab->pVTable==p && p->pNext==0 );
@@ -115385,8 +121889,10 @@ SQLITE_PRIVATE int sqlite3VtabCallDestroy(sqlite3 *db, int iDb, const char *zTab
static void callFinaliser(sqlite3 *db, int offset){
int i;
if( db->aVTrans ){
+ VTable **aVTrans = db->aVTrans;
+ db->aVTrans = 0;
for(i=0; i<db->nVTrans; i++){
- VTable *pVTab = db->aVTrans[i];
+ VTable *pVTab = aVTrans[i];
sqlite3_vtab *p = pVTab->pVtab;
if( p ){
int (*x)(sqlite3_vtab *);
@@ -115396,9 +121902,8 @@ static void callFinaliser(sqlite3 *db, int offset){
pVTab->iSavepoint = 0;
sqlite3VtabUnlock(pVTab);
}
- sqlite3DbFree(db, db->aVTrans);
+ sqlite3DbFree(db, aVTrans);
db->nVTrans = 0;
- db->aVTrans = 0;
}
}
@@ -115486,7 +121991,9 @@ SQLITE_PRIVATE int sqlite3VtabBegin(sqlite3 *db, VTable *pVTab){
if( rc==SQLITE_OK ){
rc = pModule->xBegin(pVTab->pVtab);
if( rc==SQLITE_OK ){
+ int iSvpt = db->nStatement + db->nSavepoint;
addToVTrans(db, pVTab);
+ if( iSvpt ) rc = sqlite3VtabSavepoint(db, SAVEPOINT_BEGIN, iSvpt-1);
}
}
}
@@ -115563,7 +122070,7 @@ SQLITE_PRIVATE FuncDef *sqlite3VtabOverloadFunction(
Table *pTab;
sqlite3_vtab *pVtab;
sqlite3_module *pMod;
- void (*xFunc)(sqlite3_context*,int,sqlite3_value**) = 0;
+ void (*xSFunc)(sqlite3_context*,int,sqlite3_value**) = 0;
void *pArg = 0;
FuncDef *pNew;
int rc = 0;
@@ -115591,7 +122098,7 @@ SQLITE_PRIVATE FuncDef *sqlite3VtabOverloadFunction(
for(z=(unsigned char*)zLowerName; *z; z++){
*z = sqlite3UpperToLower[*z];
}
- rc = pMod->xFindFunction(pVtab, nArg, zLowerName, &xFunc, &pArg);
+ rc = pMod->xFindFunction(pVtab, nArg, zLowerName, &xSFunc, &pArg);
sqlite3DbFree(db, zLowerName);
}
if( rc==0 ){
@@ -115606,9 +122113,9 @@ SQLITE_PRIVATE FuncDef *sqlite3VtabOverloadFunction(
return pDef;
}
*pNew = *pDef;
- pNew->zName = (char *)&pNew[1];
- memcpy(pNew->zName, pDef->zName, sqlite3Strlen30(pDef->zName)+1);
- pNew->xFunc = xFunc;
+ pNew->zName = (const char*)&pNew[1];
+ memcpy((char*)&pNew[1], pDef->zName, sqlite3Strlen30(pDef->zName)+1);
+ pNew->xSFunc = xSFunc;
pNew->pUserData = pArg;
pNew->funcFlags |= SQLITE_FUNC_EPHEM;
return pNew;
@@ -115635,7 +122142,68 @@ SQLITE_PRIVATE void sqlite3VtabMakeWritable(Parse *pParse, Table *pTab){
pToplevel->apVtabLock = apVtabLock;
pToplevel->apVtabLock[pToplevel->nVtabLock++] = pTab;
}else{
- pToplevel->db->mallocFailed = 1;
+ sqlite3OomFault(pToplevel->db);
+ }
+}
+
+/*
+** Check to see if virtual tale module pMod can be have an eponymous
+** virtual table instance. If it can, create one if one does not already
+** exist. Return non-zero if the eponymous virtual table instance exists
+** when this routine returns, and return zero if it does not exist.
+**
+** An eponymous virtual table instance is one that is named after its
+** module, and more importantly, does not require a CREATE VIRTUAL TABLE
+** statement in order to come into existance. Eponymous virtual table
+** instances always exist. They cannot be DROP-ed.
+**
+** Any virtual table module for which xConnect and xCreate are the same
+** method can have an eponymous virtual table instance.
+*/
+SQLITE_PRIVATE int sqlite3VtabEponymousTableInit(Parse *pParse, Module *pMod){
+ const sqlite3_module *pModule = pMod->pModule;
+ Table *pTab;
+ char *zErr = 0;
+ int nName;
+ int rc;
+ sqlite3 *db = pParse->db;
+ if( pMod->pEpoTab ) return 1;
+ if( pModule->xCreate!=0 && pModule->xCreate!=pModule->xConnect ) return 0;
+ nName = sqlite3Strlen30(pMod->zName) + 1;
+ pTab = sqlite3DbMallocZero(db, sizeof(Table) + nName);
+ if( pTab==0 ) return 0;
+ pMod->pEpoTab = pTab;
+ pTab->zName = (char*)&pTab[1];
+ memcpy(pTab->zName, pMod->zName, nName);
+ pTab->nRef = 1;
+ pTab->pSchema = db->aDb[0].pSchema;
+ pTab->tabFlags |= TF_Virtual;
+ pTab->nModuleArg = 0;
+ pTab->iPKey = -1;
+ addModuleArgument(db, pTab, sqlite3DbStrDup(db, pTab->zName));
+ addModuleArgument(db, pTab, 0);
+ addModuleArgument(db, pTab, sqlite3DbStrDup(db, pTab->zName));
+ rc = vtabCallConstructor(db, pTab, pMod, pModule->xConnect, &zErr);
+ if( rc ){
+ sqlite3ErrorMsg(pParse, "%s", zErr);
+ sqlite3DbFree(db, zErr);
+ sqlite3VtabEponymousTableClear(db, pMod);
+ return 0;
+ }
+ return 1;
+}
+
+/*
+** Erase the eponymous virtual table instance associated with
+** virtual table module pMod, if it exists.
+*/
+SQLITE_PRIVATE void sqlite3VtabEponymousTableClear(sqlite3 *db, Module *pMod){
+ Table *pTab = pMod->pEpoTab;
+ if( pTab!=0 ){
+ sqlite3DeleteColumnNames(db, pTab);
+ sqlite3VtabClear(db, pTab);
+ sqlite3DbFree(db, pTab);
+ pMod->pEpoTab = 0;
}
}
@@ -115698,9 +122266,9 @@ SQLITE_API int SQLITE_CDECL sqlite3_vtab_config(sqlite3 *db, int op, ...){
#endif /* SQLITE_OMIT_VIRTUALTABLE */
/************** End of vtab.c ************************************************/
-/************** Begin file where.c *******************************************/
+/************** Begin file wherecode.c ***************************************/
/*
-** 2001 September 15
+** 2015-06-06
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
@@ -115711,13 +122279,15 @@ SQLITE_API int SQLITE_CDECL sqlite3_vtab_config(sqlite3 *db, int op, ...){
**
*************************************************************************
** This module contains C code that generates VDBE code used to process
-** the WHERE clause of SQL statements. This module is responsible for
-** generating the code that loops through a table looking for applicable
-** rows. Indices are selected and used to speed the search when doing
-** so is applicable. Because this module is responsible for selecting
-** indices, you might also think of this module as the "query optimizer".
+** the WHERE clause of SQL statements.
+**
+** This file was split off from where.c on 2015-06-06 in order to reduce the
+** size of where.c and make it easier to edit. This file contains the routines
+** that actually generate the bulk of the WHERE loop code. The original where.c
+** file retains the code that does query planning and analysis.
*/
-/************** Include whereInt.h in the middle of where.c ******************/
+/* #include "sqliteInt.h" */
+/************** Include whereInt.h in the middle of wherecode.c **************/
/************** Begin file whereInt.h ****************************************/
/*
** 2013-11-12
@@ -115740,7 +122310,7 @@ SQLITE_API int SQLITE_CDECL sqlite3_vtab_config(sqlite3 *db, int op, ...){
** Trace output macros
*/
#if defined(SQLITE_TEST) || defined(SQLITE_DEBUG)
-/***/ int sqlite3WhereTrace = 0;
+/***/ int sqlite3WhereTrace;
#endif
#if defined(SQLITE_DEBUG) \
&& (defined(SQLITE_TEST) || defined(SQLITE_ENABLE_WHERETRACE))
@@ -115790,8 +122360,10 @@ 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 */
+#ifndef SQLITE_LIKE_DOESNT_MATCH_BLOBS
+ u32 iLikeRepCntr; /* LIKE range processing counter register (times 2) */
int addrLikeRep; /* LIKE range processing address */
+#endif
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 */
@@ -115882,10 +122454,6 @@ struct WhereOrSet {
WhereOrCost a[N_OR_COST]; /* Set of best costs */
};
-
-/* Forward declaration of methods */
-static int whereLoopResize(sqlite3*, WhereLoop*, int);
-
/*
** Each instance of this object holds a sequence of WhereLoop objects
** that implement some or all of a query plan.
@@ -115978,6 +122546,7 @@ struct WhereTerm {
u16 eOperator; /* A WO_xx value describing <op> */
u16 wtFlags; /* TERM_xxx bit flags. See below */
u8 nChild; /* Number of children that must disable us */
+ u8 eMatchOp; /* Op for vtab MATCH/LIKE/GLOB/REGEXP terms */
WhereClause *pWC; /* The clause this term is part of */
Bitmask prereqRight; /* Bitmask of tables used by pExpr->pRight */
Bitmask prereqAll; /* Bitmask of tables referenced by pExpr */
@@ -116001,6 +122570,7 @@ struct WhereTerm {
#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 */
+#define TERM_IS 0x800 /* Term.pExpr is an IS operator */
/*
** An instance of the WhereScan object is used as an iterator for locating
@@ -116009,13 +122579,15 @@ struct WhereTerm {
struct WhereScan {
WhereClause *pOrigWC; /* Original, innermost WhereClause */
WhereClause *pWC; /* WhereClause currently being scanned */
- char *zCollName; /* Required collating sequence, if not NULL */
+ const char *zCollName; /* Required collating sequence, if not NULL */
+ Expr *pIdxExpr; /* Search for this index expression */
char idxaff; /* Must match this affinity, if zCollName!=NULL */
unsigned char nEquiv; /* Number of entries in aEquiv[] */
unsigned char iEquiv; /* Next unused slot in aEquiv[] */
u32 opMask; /* Acceptable operators */
int k; /* Resume scanning at this->pWC->a[this->k] */
- int aEquiv[22]; /* Cursor,Column pairs for equivalence classes */
+ int aiCur[11]; /* Cursors in the equivalence class */
+ i16 aiColumn[11]; /* Corresponding column number in the eq-class */
};
/*
@@ -116093,6 +122665,11 @@ struct WhereMaskSet {
};
/*
+** Initialize a WhereMaskSet object
+*/
+#define initMaskSet(P) (P)->n=0
+
+/*
** This object is a convenience wrapper holding all information needed
** to construct WhereLoop objects for a particular query.
*/
@@ -116122,14 +122699,15 @@ struct WhereInfo {
Parse *pParse; /* Parsing and code generating context */
SrcList *pTabList; /* List of tables in the join */
ExprList *pOrderBy; /* The ORDER BY clause or NULL */
- ExprList *pResultSet; /* Result set. DISTINCT operates on these */
+ ExprList *pDistinctSet; /* DISTINCT over all these values */
WhereLoop *pLoops; /* List of all WhereLoop objects */
Bitmask revMask; /* Mask of ORDER BY terms that need reversing */
LogEst nRowOut; /* Estimated number of output rows */
+ LogEst iLimit; /* LIMIT if wctrlFlags has WHERE_USE_LIMIT */
u16 wctrlFlags; /* Flags originally passed to sqlite3WhereBegin() */
i8 nOBSat; /* Number of ORDER BY terms satisfied by indices */
u8 sorted; /* True if really sorted (not just grouped) */
- u8 okOnePass; /* Ok to use one-pass algorithm for UPDATE/DELETE */
+ u8 eOnePass; /* ONEPASS_OFF, or _SINGLE, or _MULTI */
u8 untestedTerms; /* Not all WHERE terms resolved by outer loop */
u8 eDistinct; /* One of the WHERE_DISTINCT_* values below */
u8 nLevel; /* Number of nested loop */
@@ -116144,26 +122722,92 @@ struct WhereInfo {
};
/*
+** Private interfaces - callable only by other where.c routines.
+**
+** where.c:
+*/
+SQLITE_PRIVATE Bitmask sqlite3WhereGetMask(WhereMaskSet*,int);
+SQLITE_PRIVATE WhereTerm *sqlite3WhereFindTerm(
+ WhereClause *pWC, /* The WHERE clause to be searched */
+ int iCur, /* Cursor number of LHS */
+ int iColumn, /* Column number of LHS */
+ Bitmask notReady, /* RHS must not overlap with this mask */
+ u32 op, /* Mask of WO_xx values describing operator */
+ Index *pIdx /* Must be compatible with this index, if not NULL */
+);
+
+/* wherecode.c: */
+#ifndef SQLITE_OMIT_EXPLAIN
+SQLITE_PRIVATE int sqlite3WhereExplainOneScan(
+ Parse *pParse, /* Parse context */
+ SrcList *pTabList, /* Table list this loop refers to */
+ WhereLevel *pLevel, /* Scan to write OP_Explain opcode for */
+ int iLevel, /* Value for "level" column of output */
+ int iFrom, /* Value for "from" column of output */
+ u16 wctrlFlags /* Flags passed to sqlite3WhereBegin() */
+);
+#else
+# define sqlite3WhereExplainOneScan(u,v,w,x,y,z) 0
+#endif /* SQLITE_OMIT_EXPLAIN */
+#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
+SQLITE_PRIVATE void sqlite3WhereAddScanStatus(
+ Vdbe *v, /* Vdbe to add scanstatus entry to */
+ SrcList *pSrclist, /* FROM clause pLvl reads data from */
+ WhereLevel *pLvl, /* Level to add scanstatus() entry for */
+ int addrExplain /* Address of OP_Explain (or 0) */
+);
+#else
+# define sqlite3WhereAddScanStatus(a, b, c, d) ((void)d)
+#endif
+SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart(
+ WhereInfo *pWInfo, /* Complete information about the WHERE clause */
+ int iLevel, /* Which level of pWInfo->a[] should be coded */
+ Bitmask notReady /* Which tables are currently available */
+);
+
+/* whereexpr.c: */
+SQLITE_PRIVATE void sqlite3WhereClauseInit(WhereClause*,WhereInfo*);
+SQLITE_PRIVATE void sqlite3WhereClauseClear(WhereClause*);
+SQLITE_PRIVATE void sqlite3WhereSplit(WhereClause*,Expr*,u8);
+SQLITE_PRIVATE Bitmask sqlite3WhereExprUsage(WhereMaskSet*, Expr*);
+SQLITE_PRIVATE Bitmask sqlite3WhereExprListUsage(WhereMaskSet*, ExprList*);
+SQLITE_PRIVATE void sqlite3WhereExprAnalyze(SrcList*, WhereClause*);
+SQLITE_PRIVATE void sqlite3WhereTabFuncArgs(Parse*, struct SrcList_item*, WhereClause*);
+
+
+
+
+
+/*
** Bitmasks for the operators on WhereTerm objects. These are all
** operators that are of interest to the query planner. An
** OR-ed combination of these values can be used when searching for
** particular WhereTerms within a WhereClause.
+**
+** Value constraints:
+** WO_EQ == SQLITE_INDEX_CONSTRAINT_EQ
+** WO_LT == SQLITE_INDEX_CONSTRAINT_LT
+** WO_LE == SQLITE_INDEX_CONSTRAINT_LE
+** WO_GT == SQLITE_INDEX_CONSTRAINT_GT
+** WO_GE == SQLITE_INDEX_CONSTRAINT_GE
+** WO_MATCH == SQLITE_INDEX_CONSTRAINT_MATCH
*/
-#define WO_IN 0x001
-#define WO_EQ 0x002
+#define WO_IN 0x0001
+#define WO_EQ 0x0002
#define WO_LT (WO_EQ<<(TK_LT-TK_EQ))
#define WO_LE (WO_EQ<<(TK_LE-TK_EQ))
#define WO_GT (WO_EQ<<(TK_GT-TK_EQ))
#define WO_GE (WO_EQ<<(TK_GE-TK_EQ))
-#define WO_MATCH 0x040
-#define WO_ISNULL 0x080
-#define WO_OR 0x100 /* Two or more OR-connected terms */
-#define WO_AND 0x200 /* Two or more AND-connected terms */
-#define WO_EQUIV 0x400 /* Of the form A==B, both columns */
-#define WO_NOOP 0x800 /* This term does not restrict search space */
+#define WO_MATCH 0x0040
+#define WO_IS 0x0080
+#define WO_ISNULL 0x0100
+#define WO_OR 0x0200 /* Two or more OR-connected terms */
+#define WO_AND 0x0400 /* Two or more AND-connected terms */
+#define WO_EQUIV 0x0800 /* Of the form A==B, both columns */
+#define WO_NOOP 0x1000 /* This term does not restrict search space */
-#define WO_ALL 0xfff /* Mask of all possible WO_* values */
-#define WO_SINGLE 0x0ff /* Mask of all non-compound WO_* values */
+#define WO_ALL 0x1fff /* Mask of all possible WO_* values */
+#define WO_SINGLE 0x01ff /* Mask of all non-compound WO_* values */
/*
** These are definitions of bits in the WhereLoop.wsFlags field.
@@ -116191,170 +122835,1809 @@ struct WhereInfo {
#define WHERE_PARTIALIDX 0x00020000 /* The automatic index is partial */
/************** End of whereInt.h ********************************************/
-/************** Continuing where we left off in where.c **********************/
+/************** Continuing where we left off in wherecode.c ******************/
+#ifndef SQLITE_OMIT_EXPLAIN
/*
-** Return the estimated number of output rows from a WHERE clause
+** This routine is a helper for explainIndexRange() below
+**
+** pStr holds the text of an expression that we are building up one term
+** at a time. This routine adds a new term to the end of the expression.
+** Terms are separated by AND so add the "AND" text for second and subsequent
+** terms only.
*/
-SQLITE_PRIVATE u64 sqlite3WhereOutputRowCount(WhereInfo *pWInfo){
- return sqlite3LogEstToInt(pWInfo->nRowOut);
+static void explainAppendTerm(
+ StrAccum *pStr, /* The text expression being built */
+ int iTerm, /* Index of this term. First is zero */
+ const char *zColumn, /* Name of the column */
+ const char *zOp /* Name of the operator */
+){
+ if( iTerm ) sqlite3StrAccumAppend(pStr, " AND ", 5);
+ sqlite3StrAccumAppendAll(pStr, zColumn);
+ sqlite3StrAccumAppend(pStr, zOp, 1);
+ sqlite3StrAccumAppend(pStr, "?", 1);
}
/*
-** Return one of the WHERE_DISTINCT_xxxxx values to indicate how this
-** WHERE clause returns outputs for DISTINCT processing.
+** Return the name of the i-th column of the pIdx index.
*/
-SQLITE_PRIVATE int sqlite3WhereIsDistinct(WhereInfo *pWInfo){
- return pWInfo->eDistinct;
+static const char *explainIndexColumnName(Index *pIdx, int i){
+ i = pIdx->aiColumn[i];
+ if( i==XN_EXPR ) return "<expr>";
+ if( i==XN_ROWID ) return "rowid";
+ return pIdx->pTable->aCol[i].zName;
}
/*
-** Return TRUE if the WHERE clause returns rows in ORDER BY order.
-** Return FALSE if the output needs to be sorted.
+** Argument pLevel describes a strategy for scanning table pTab. This
+** function appends text to pStr that describes the subset of table
+** rows scanned by the strategy in the form of an SQL expression.
+**
+** For example, if the query:
+**
+** SELECT * FROM t1 WHERE a=1 AND b>2;
+**
+** is run and there is an index on (a, b), then this function returns a
+** string similar to:
+**
+** "a=? AND b>?"
*/
-SQLITE_PRIVATE int sqlite3WhereIsOrdered(WhereInfo *pWInfo){
- return pWInfo->nOBSat;
+static void explainIndexRange(StrAccum *pStr, WhereLoop *pLoop){
+ Index *pIndex = pLoop->u.btree.pIndex;
+ u16 nEq = pLoop->u.btree.nEq;
+ u16 nSkip = pLoop->nSkip;
+ int i, j;
+
+ if( nEq==0 && (pLoop->wsFlags&(WHERE_BTM_LIMIT|WHERE_TOP_LIMIT))==0 ) return;
+ sqlite3StrAccumAppend(pStr, " (", 2);
+ for(i=0; i<nEq; i++){
+ const char *z = explainIndexColumnName(pIndex, i);
+ if( i ) sqlite3StrAccumAppend(pStr, " AND ", 5);
+ sqlite3XPrintf(pStr, i>=nSkip ? "%s=?" : "ANY(%s)", z);
+ }
+
+ j = i;
+ if( pLoop->wsFlags&WHERE_BTM_LIMIT ){
+ const char *z = explainIndexColumnName(pIndex, i);
+ explainAppendTerm(pStr, i++, z, ">");
+ }
+ if( pLoop->wsFlags&WHERE_TOP_LIMIT ){
+ const char *z = explainIndexColumnName(pIndex, j);
+ explainAppendTerm(pStr, i, z, "<");
+ }
+ sqlite3StrAccumAppend(pStr, ")", 1);
}
/*
-** Return the VDBE address or label to jump to in order to continue
-** immediately with the next row of a WHERE clause.
+** This function is a no-op unless currently processing an EXPLAIN QUERY PLAN
+** command, or if either SQLITE_DEBUG or SQLITE_ENABLE_STMT_SCANSTATUS was
+** defined at compile-time. If it is not a no-op, a single OP_Explain opcode
+** is added to the output to describe the table scan strategy in pLevel.
+**
+** If an OP_Explain opcode is added to the VM, its address is returned.
+** Otherwise, if no OP_Explain is coded, zero is returned.
*/
-SQLITE_PRIVATE int sqlite3WhereContinueLabel(WhereInfo *pWInfo){
- assert( pWInfo->iContinue!=0 );
- return pWInfo->iContinue;
+SQLITE_PRIVATE int sqlite3WhereExplainOneScan(
+ Parse *pParse, /* Parse context */
+ SrcList *pTabList, /* Table list this loop refers to */
+ WhereLevel *pLevel, /* Scan to write OP_Explain opcode for */
+ int iLevel, /* Value for "level" column of output */
+ int iFrom, /* Value for "from" column of output */
+ u16 wctrlFlags /* Flags passed to sqlite3WhereBegin() */
+){
+ int ret = 0;
+#if !defined(SQLITE_DEBUG) && !defined(SQLITE_ENABLE_STMT_SCANSTATUS)
+ if( pParse->explain==2 )
+#endif
+ {
+ struct SrcList_item *pItem = &pTabList->a[pLevel->iFrom];
+ Vdbe *v = pParse->pVdbe; /* VM being constructed */
+ sqlite3 *db = pParse->db; /* Database handle */
+ int iId = pParse->iSelectId; /* Select id (left-most output column) */
+ int isSearch; /* True for a SEARCH. False for SCAN. */
+ WhereLoop *pLoop; /* The controlling WhereLoop object */
+ u32 flags; /* Flags that describe this loop */
+ char *zMsg; /* Text to add to EQP output */
+ StrAccum str; /* EQP output string */
+ char zBuf[100]; /* Initial space for EQP output string */
+
+ pLoop = pLevel->pWLoop;
+ flags = pLoop->wsFlags;
+ if( (flags&WHERE_MULTI_OR) || (wctrlFlags&WHERE_ONETABLE_ONLY) ) return 0;
+
+ isSearch = (flags&(WHERE_BTM_LIMIT|WHERE_TOP_LIMIT))!=0
+ || ((flags&WHERE_VIRTUALTABLE)==0 && (pLoop->u.btree.nEq>0))
+ || (wctrlFlags&(WHERE_ORDERBY_MIN|WHERE_ORDERBY_MAX));
+
+ sqlite3StrAccumInit(&str, db, zBuf, sizeof(zBuf), SQLITE_MAX_LENGTH);
+ sqlite3StrAccumAppendAll(&str, isSearch ? "SEARCH" : "SCAN");
+ if( pItem->pSelect ){
+ sqlite3XPrintf(&str, " SUBQUERY %d", pItem->iSelectId);
+ }else{
+ sqlite3XPrintf(&str, " TABLE %s", pItem->zName);
+ }
+
+ if( pItem->zAlias ){
+ sqlite3XPrintf(&str, " AS %s", pItem->zAlias);
+ }
+ if( (flags & (WHERE_IPK|WHERE_VIRTUALTABLE))==0 ){
+ const char *zFmt = 0;
+ Index *pIdx;
+
+ assert( pLoop->u.btree.pIndex!=0 );
+ pIdx = pLoop->u.btree.pIndex;
+ assert( !(flags&WHERE_AUTO_INDEX) || (flags&WHERE_IDX_ONLY) );
+ if( !HasRowid(pItem->pTab) && IsPrimaryKeyIndex(pIdx) ){
+ if( isSearch ){
+ zFmt = "PRIMARY KEY";
+ }
+ }else if( flags & WHERE_PARTIALIDX ){
+ zFmt = "AUTOMATIC PARTIAL COVERING INDEX";
+ }else if( flags & WHERE_AUTO_INDEX ){
+ zFmt = "AUTOMATIC COVERING INDEX";
+ }else if( flags & WHERE_IDX_ONLY ){
+ zFmt = "COVERING INDEX %s";
+ }else{
+ zFmt = "INDEX %s";
+ }
+ if( zFmt ){
+ sqlite3StrAccumAppend(&str, " USING ", 7);
+ sqlite3XPrintf(&str, zFmt, pIdx->zName);
+ explainIndexRange(&str, pLoop);
+ }
+ }else if( (flags & WHERE_IPK)!=0 && (flags & WHERE_CONSTRAINT)!=0 ){
+ const char *zRangeOp;
+ if( flags&(WHERE_COLUMN_EQ|WHERE_COLUMN_IN) ){
+ zRangeOp = "=";
+ }else if( (flags&WHERE_BOTH_LIMIT)==WHERE_BOTH_LIMIT ){
+ zRangeOp = ">? AND rowid<";
+ }else if( flags&WHERE_BTM_LIMIT ){
+ zRangeOp = ">";
+ }else{
+ assert( flags&WHERE_TOP_LIMIT);
+ zRangeOp = "<";
+ }
+ sqlite3XPrintf(&str, " USING INTEGER PRIMARY KEY (rowid%s?)",zRangeOp);
+ }
+#ifndef SQLITE_OMIT_VIRTUALTABLE
+ else if( (flags & WHERE_VIRTUALTABLE)!=0 ){
+ sqlite3XPrintf(&str, " VIRTUAL TABLE INDEX %d:%s",
+ pLoop->u.vtab.idxNum, pLoop->u.vtab.idxStr);
+ }
+#endif
+#ifdef SQLITE_EXPLAIN_ESTIMATED_ROWS
+ if( pLoop->nOut>=10 ){
+ sqlite3XPrintf(&str, " (~%llu rows)", sqlite3LogEstToInt(pLoop->nOut));
+ }else{
+ sqlite3StrAccumAppend(&str, " (~1 row)", 9);
+ }
+#endif
+ zMsg = sqlite3StrAccumFinish(&str);
+ ret = sqlite3VdbeAddOp4(v, OP_Explain, iId, iLevel, iFrom, zMsg,P4_DYNAMIC);
+ }
+ return ret;
}
+#endif /* SQLITE_OMIT_EXPLAIN */
+#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
/*
-** Return the VDBE address or label to jump to in order to break
-** out of a WHERE loop.
+** Configure the VM passed as the first argument with an
+** sqlite3_stmt_scanstatus() entry corresponding to the scan used to
+** implement level pLvl. Argument pSrclist is a pointer to the FROM
+** clause that the scan reads data from.
+**
+** If argument addrExplain is not 0, it must be the address of an
+** OP_Explain instruction that describes the same loop.
*/
-SQLITE_PRIVATE int sqlite3WhereBreakLabel(WhereInfo *pWInfo){
- return pWInfo->iBreak;
+SQLITE_PRIVATE void sqlite3WhereAddScanStatus(
+ Vdbe *v, /* Vdbe to add scanstatus entry to */
+ SrcList *pSrclist, /* FROM clause pLvl reads data from */
+ WhereLevel *pLvl, /* Level to add scanstatus() entry for */
+ int addrExplain /* Address of OP_Explain (or 0) */
+){
+ const char *zObj = 0;
+ WhereLoop *pLoop = pLvl->pWLoop;
+ if( (pLoop->wsFlags & WHERE_VIRTUALTABLE)==0 && pLoop->u.btree.pIndex!=0 ){
+ zObj = pLoop->u.btree.pIndex->zName;
+ }else{
+ zObj = pSrclist->a[pLvl->iFrom].zName;
+ }
+ sqlite3VdbeScanStatus(
+ v, addrExplain, pLvl->addrBody, pLvl->addrVisit, pLoop->nOut, zObj
+ );
}
+#endif
+
/*
-** Return TRUE if an UPDATE or DELETE statement can operate directly on
-** the rowids returned by a WHERE clause. Return FALSE if doing an
-** UPDATE or DELETE might change subsequent WHERE clause results.
+** Disable a term in the WHERE clause. Except, do not disable the term
+** if it controls a LEFT OUTER JOIN and it did not originate in the ON
+** or USING clause of that join.
**
-** If the ONEPASS optimization is used (if this routine returns true)
-** then also write the indices of open cursors used by ONEPASS
-** into aiCur[0] and aiCur[1]. iaCur[0] gets the cursor of the data
-** table and iaCur[1] gets the cursor used by an auxiliary index.
-** Either value may be -1, indicating that cursor is not used.
-** Any cursors returned will have been opened for writing.
+** Consider the term t2.z='ok' in the following queries:
**
-** aiCur[0] and aiCur[1] both get -1 if the where-clause logic is
-** unable to use the ONEPASS optimization.
+** (1) SELECT * FROM t1 LEFT JOIN t2 ON t1.a=t2.x WHERE t2.z='ok'
+** (2) SELECT * FROM t1 LEFT JOIN t2 ON t1.a=t2.x AND t2.z='ok'
+** (3) SELECT * FROM t1, t2 WHERE t1.a=t2.x AND t2.z='ok'
+**
+** The t2.z='ok' is disabled in the in (2) because it originates
+** in the ON clause. The term is disabled in (3) because it is not part
+** of a LEFT OUTER JOIN. In (1), the term is not disabled.
+**
+** Disabling a term causes that term to not be tested in the inner loop
+** of the join. Disabling is an optimization. When terms are satisfied
+** by indices, we disable them to prevent redundant tests in the inner
+** loop. We would get the correct results if nothing were ever disabled,
+** 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.
*/
-SQLITE_PRIVATE int sqlite3WhereOkOnePass(WhereInfo *pWInfo, int *aiCur){
- memcpy(aiCur, pWInfo->aiCurOnePass, sizeof(int)*2);
- return pWInfo->okOnePass;
+static void disableTerm(WhereLevel *pLevel, WhereTerm *pTerm){
+ int nLoop = 0;
+ while( pTerm
+ && (pTerm->wtFlags & TERM_CODED)==0
+ && (pLevel->iLeftJoin==0 || ExprHasProperty(pTerm->pExpr, EP_FromJoin))
+ && (pLevel->notReady & pTerm->prereqAll)==0
+ ){
+ 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++;
+ }
}
/*
-** Move the content of pSrc into pDest
+** Code an OP_Affinity opcode to apply the column affinity string zAff
+** to the n registers starting at base.
+**
+** As an optimization, SQLITE_AFF_BLOB entries (which are no-ops) at the
+** beginning and end of zAff are ignored. If all entries in zAff are
+** SQLITE_AFF_BLOB, then no code gets generated.
+**
+** This routine makes its own copy of zAff so that the caller is free
+** to modify zAff after this routine returns.
*/
-static void whereOrMove(WhereOrSet *pDest, WhereOrSet *pSrc){
- pDest->n = pSrc->n;
- memcpy(pDest->a, pSrc->a, pDest->n*sizeof(pDest->a[0]));
+static void codeApplyAffinity(Parse *pParse, int base, int n, char *zAff){
+ Vdbe *v = pParse->pVdbe;
+ if( zAff==0 ){
+ assert( pParse->db->mallocFailed );
+ return;
+ }
+ assert( v!=0 );
+
+ /* Adjust base and n to skip over SQLITE_AFF_BLOB entries at the beginning
+ ** and end of the affinity string.
+ */
+ while( n>0 && zAff[0]==SQLITE_AFF_BLOB ){
+ n--;
+ base++;
+ zAff++;
+ }
+ while( n>1 && zAff[n-1]==SQLITE_AFF_BLOB ){
+ n--;
+ }
+
+ /* Code the OP_Affinity opcode if there is anything left to do. */
+ if( n>0 ){
+ sqlite3VdbeAddOp4(v, OP_Affinity, base, n, 0, zAff, n);
+ sqlite3ExprCacheAffinityChange(pParse, base, n);
+ }
}
+
/*
-** Try to insert a new prerequisite/cost entry into the WhereOrSet pSet.
+** Generate code for a single equality term of the WHERE clause. An equality
+** term can be either X=expr or X IN (...). pTerm is the term to be
+** coded.
**
-** The new entry might overwrite an existing entry, or it might be
-** appended, or it might be discarded. Do whatever is the right thing
-** so that pSet keeps the N_OR_COST best entries seen so far.
+** The current value for the constraint is left in register iReg.
+**
+** For a constraint of the form X=expr, the expression is evaluated and its
+** result is left on the stack. For constraints of the form X IN (...)
+** this routine sets up a loop that will iterate over all values of X.
*/
-static int whereOrInsert(
- WhereOrSet *pSet, /* The WhereOrSet to be updated */
- Bitmask prereq, /* Prerequisites of the new entry */
- LogEst rRun, /* Run-cost of the new entry */
- LogEst nOut /* Number of outputs for the new entry */
+static int codeEqualityTerm(
+ Parse *pParse, /* The parsing context */
+ WhereTerm *pTerm, /* The term of the WHERE clause to be coded */
+ WhereLevel *pLevel, /* The level of the FROM clause we are working on */
+ int iEq, /* Index of the equality term within this level */
+ int bRev, /* True for reverse-order IN operations */
+ int iTarget /* Attempt to leave results in this register */
){
- u16 i;
- WhereOrCost *p;
- for(i=pSet->n, p=pSet->a; i>0; i--, p++){
- if( rRun<=p->rRun && (prereq & p->prereq)==prereq ){
- goto whereOrInsert_done;
+ Expr *pX = pTerm->pExpr;
+ Vdbe *v = pParse->pVdbe;
+ int iReg; /* Register holding results */
+
+ assert( iTarget>0 );
+ if( pX->op==TK_EQ || pX->op==TK_IS ){
+ iReg = sqlite3ExprCodeTarget(pParse, pX->pRight, iTarget);
+ }else if( pX->op==TK_ISNULL ){
+ iReg = iTarget;
+ sqlite3VdbeAddOp2(v, OP_Null, 0, iReg);
+#ifndef SQLITE_OMIT_SUBQUERY
+ }else{
+ int eType;
+ int iTab;
+ struct InLoop *pIn;
+ WhereLoop *pLoop = pLevel->pWLoop;
+
+ if( (pLoop->wsFlags & WHERE_VIRTUALTABLE)==0
+ && pLoop->u.btree.pIndex!=0
+ && pLoop->u.btree.pIndex->aSortOrder[iEq]
+ ){
+ testcase( iEq==0 );
+ testcase( bRev );
+ bRev = !bRev;
}
- if( p->rRun<=rRun && (p->prereq & prereq)==p->prereq ){
- return 0;
+ assert( pX->op==TK_IN );
+ iReg = iTarget;
+ eType = sqlite3FindInIndex(pParse, pX, IN_INDEX_LOOP, 0);
+ if( eType==IN_INDEX_INDEX_DESC ){
+ testcase( bRev );
+ bRev = !bRev;
+ }
+ iTab = pX->iTable;
+ sqlite3VdbeAddOp2(v, bRev ? OP_Last : OP_Rewind, iTab, 0);
+ VdbeCoverageIf(v, bRev);
+ VdbeCoverageIf(v, !bRev);
+ assert( (pLoop->wsFlags & WHERE_MULTI_OR)==0 );
+ pLoop->wsFlags |= WHERE_IN_ABLE;
+ if( pLevel->u.in.nIn==0 ){
+ pLevel->addrNxt = sqlite3VdbeMakeLabel(v);
}
+ pLevel->u.in.nIn++;
+ pLevel->u.in.aInLoop =
+ sqlite3DbReallocOrFree(pParse->db, pLevel->u.in.aInLoop,
+ sizeof(pLevel->u.in.aInLoop[0])*pLevel->u.in.nIn);
+ pIn = pLevel->u.in.aInLoop;
+ if( pIn ){
+ pIn += pLevel->u.in.nIn - 1;
+ pIn->iCur = iTab;
+ if( eType==IN_INDEX_ROWID ){
+ pIn->addrInTop = sqlite3VdbeAddOp2(v, OP_Rowid, iTab, iReg);
+ }else{
+ pIn->addrInTop = sqlite3VdbeAddOp3(v, OP_Column, iTab, 0, iReg);
+ }
+ pIn->eEndLoopOp = bRev ? OP_PrevIfOpen : OP_NextIfOpen;
+ sqlite3VdbeAddOp1(v, OP_IsNull, iReg); VdbeCoverage(v);
+ }else{
+ pLevel->u.in.nIn = 0;
+ }
+#endif
}
- if( pSet->n<N_OR_COST ){
- p = &pSet->a[pSet->n++];
- p->nOut = nOut;
- }else{
- p = pSet->a;
- for(i=1; i<pSet->n; i++){
- if( p->rRun>pSet->a[i].rRun ) p = pSet->a + i;
+ disableTerm(pLevel, pTerm);
+ return iReg;
+}
+
+/*
+** Generate code that will evaluate all == and IN constraints for an
+** index scan.
+**
+** For example, consider table t1(a,b,c,d,e,f) with index i1(a,b,c).
+** Suppose the WHERE clause is this: a==5 AND b IN (1,2,3) AND c>5 AND c<10
+** The index has as many as three equality constraints, but in this
+** example, the third "c" value is an inequality. So only two
+** constraints are coded. This routine will generate code to evaluate
+** a==5 and b IN (1,2,3). The current values for a and b will be stored
+** in consecutive registers and the index of the first register is returned.
+**
+** In the example above nEq==2. But this subroutine works for any value
+** of nEq including 0. If nEq==0, this routine is nearly a no-op.
+** The only thing it does is allocate the pLevel->iMem memory cell and
+** compute the affinity string.
+**
+** The nExtraReg parameter is 0 or 1. It is 0 if all WHERE clause constraints
+** are == or IN and are covered by the nEq. nExtraReg is 1 if there is
+** an inequality constraint (such as the "c>=5 AND c<10" in the example) that
+** occurs after the nEq quality constraints.
+**
+** This routine allocates a range of nEq+nExtraReg memory cells and returns
+** the index of the first memory cell in that range. The code that
+** calls this routine will use that memory range to store keys for
+** start and termination conditions of the loop.
+** key value of the loop. If one or more IN operators appear, then
+** this routine allocates an additional nEq memory cells for internal
+** use.
+**
+** Before returning, *pzAff is set to point to a buffer containing a
+** copy of the column affinity string of the index allocated using
+** sqlite3DbMalloc(). Except, entries in the copy of the string associated
+** with equality constraints that use BLOB or NONE affinity are set to
+** SQLITE_AFF_BLOB. This is to deal with SQL such as the following:
+**
+** CREATE TABLE t1(a TEXT PRIMARY KEY, b);
+** SELECT ... FROM t1 AS t2, t1 WHERE t1.a = t2.b;
+**
+** In the example above, the index on t1(a) has TEXT affinity. But since
+** the right hand side of the equality constraint (t2.b) has BLOB/NONE affinity,
+** no conversion should be attempted before using a t2.b value as part of
+** a key to search the index. Hence the first byte in the returned affinity
+** string in this example would be set to SQLITE_AFF_BLOB.
+*/
+static int codeAllEqualityTerms(
+ Parse *pParse, /* Parsing context */
+ WhereLevel *pLevel, /* Which nested loop of the FROM we are coding */
+ int bRev, /* Reverse the order of IN operators */
+ int nExtraReg, /* Number of extra registers to allocate */
+ char **pzAff /* OUT: Set to point to affinity string */
+){
+ u16 nEq; /* The number of == or IN constraints to code */
+ u16 nSkip; /* Number of left-most columns to skip */
+ Vdbe *v = pParse->pVdbe; /* The vm under construction */
+ Index *pIdx; /* The index being used for this loop */
+ WhereTerm *pTerm; /* A single constraint term */
+ WhereLoop *pLoop; /* The WhereLoop object */
+ int j; /* Loop counter */
+ int regBase; /* Base register */
+ int nReg; /* Number of registers to allocate */
+ char *zAff; /* Affinity string to return */
+
+ /* This module is only called on query plans that use an index. */
+ pLoop = pLevel->pWLoop;
+ assert( (pLoop->wsFlags & WHERE_VIRTUALTABLE)==0 );
+ nEq = pLoop->u.btree.nEq;
+ nSkip = pLoop->nSkip;
+ pIdx = pLoop->u.btree.pIndex;
+ assert( pIdx!=0 );
+
+ /* Figure out how many memory cells we will need then allocate them.
+ */
+ regBase = pParse->nMem + 1;
+ nReg = pLoop->u.btree.nEq + nExtraReg;
+ pParse->nMem += nReg;
+
+ zAff = sqlite3DbStrDup(pParse->db,sqlite3IndexAffinityStr(pParse->db,pIdx));
+ assert( zAff!=0 || pParse->db->mallocFailed );
+
+ if( nSkip ){
+ int iIdxCur = pLevel->iIdxCur;
+ sqlite3VdbeAddOp1(v, (bRev?OP_Last:OP_Rewind), iIdxCur);
+ VdbeCoverageIf(v, bRev==0);
+ VdbeCoverageIf(v, bRev!=0);
+ VdbeComment((v, "begin skip-scan on %s", pIdx->zName));
+ j = sqlite3VdbeAddOp0(v, OP_Goto);
+ pLevel->addrSkip = sqlite3VdbeAddOp4Int(v, (bRev?OP_SeekLT:OP_SeekGT),
+ iIdxCur, 0, regBase, nSkip);
+ VdbeCoverageIf(v, bRev==0);
+ VdbeCoverageIf(v, bRev!=0);
+ sqlite3VdbeJumpHere(v, j);
+ for(j=0; j<nSkip; j++){
+ sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, j, regBase+j);
+ testcase( pIdx->aiColumn[j]==XN_EXPR );
+ VdbeComment((v, "%s", explainIndexColumnName(pIdx, j)));
+ }
+ }
+
+ /* Evaluate the equality constraints
+ */
+ assert( zAff==0 || (int)strlen(zAff)>=nEq );
+ for(j=nSkip; j<nEq; j++){
+ int r1;
+ pTerm = pLoop->aLTerm[j];
+ assert( pTerm!=0 );
+ /* The following testcase is true for indices with redundant columns.
+ ** Ex: CREATE INDEX i1 ON t1(a,b,a); SELECT * FROM t1 WHERE a=0 AND b=0; */
+ testcase( (pTerm->wtFlags & TERM_CODED)!=0 );
+ testcase( pTerm->wtFlags & TERM_VIRTUAL );
+ r1 = codeEqualityTerm(pParse, pTerm, pLevel, j, bRev, regBase+j);
+ if( r1!=regBase+j ){
+ if( nReg==1 ){
+ sqlite3ReleaseTempReg(pParse, regBase);
+ regBase = r1;
+ }else{
+ sqlite3VdbeAddOp2(v, OP_SCopy, r1, regBase+j);
+ }
+ }
+ testcase( pTerm->eOperator & WO_ISNULL );
+ testcase( pTerm->eOperator & WO_IN );
+ if( (pTerm->eOperator & (WO_ISNULL|WO_IN))==0 ){
+ Expr *pRight = pTerm->pExpr->pRight;
+ if( (pTerm->wtFlags & TERM_IS)==0 && sqlite3ExprCanBeNull(pRight) ){
+ sqlite3VdbeAddOp2(v, OP_IsNull, regBase+j, pLevel->addrBrk);
+ VdbeCoverage(v);
+ }
+ if( zAff ){
+ if( sqlite3CompareAffinity(pRight, zAff[j])==SQLITE_AFF_BLOB ){
+ zAff[j] = SQLITE_AFF_BLOB;
+ }
+ if( sqlite3ExprNeedsNoAffinityChange(pRight, zAff[j]) ){
+ zAff[j] = SQLITE_AFF_BLOB;
+ }
+ }
}
- if( p->rRun<=rRun ) return 0;
}
-whereOrInsert_done:
- p->prereq = prereq;
- p->rRun = rRun;
- if( p->nOut>nOut ) p->nOut = nOut;
- return 1;
+ *pzAff = zAff;
+ return regBase;
}
+#ifndef SQLITE_LIKE_DOESNT_MATCH_BLOBS
/*
-** Initialize a preallocated WhereClause structure.
+** If the most recently coded instruction is a constant range constraint
+** (a string literal) that originated from the LIKE optimization, then
+** set P3 and P5 on the OP_String opcode so that the string will be cast
+** to a BLOB at appropriate times.
+**
+** 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.
+**
+** Except, of course, if SQLITE_LIKE_DOESNT_MATCH_BLOBS is defined, then
+** only the one pass through the string space is required, so this routine
+** becomes a no-op.
*/
-static void whereClauseInit(
- WhereClause *pWC, /* The WhereClause to be initialized */
- WhereInfo *pWInfo /* The WHERE processing context */
+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 */
){
- pWC->pWInfo = pWInfo;
- pWC->pOuter = 0;
- pWC->nTerm = 0;
- pWC->nSlot = ArraySize(pWC->aStatic);
- pWC->a = pWC->aStatic;
+ 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 = (int)(pLevel->iLikeRepCntr>>1); /* Register holding counter */
+ pOp->p5 = (u8)(pLevel->iLikeRepCntr&1); /* ASC or DESC */
+ }
}
+#else
+# define whereLikeOptimizationStringFixup(A,B,C)
+#endif
-/* Forward reference */
-static void whereClauseClear(WhereClause*);
+#ifdef SQLITE_ENABLE_CURSOR_HINTS
+/*
+** Information is passed from codeCursorHint() down to individual nodes of
+** the expression tree (by sqlite3WalkExpr()) using an instance of this
+** structure.
+*/
+struct CCurHint {
+ int iTabCur; /* Cursor for the main table */
+ int iIdxCur; /* Cursor for the index, if pIdx!=0. Unused otherwise */
+ Index *pIdx; /* The index used to access the table */
+};
/*
-** Deallocate all memory associated with a WhereOrInfo object.
+** This function is called for every node of an expression that is a candidate
+** for a cursor hint on an index cursor. For TK_COLUMN nodes that reference
+** the table CCurHint.iTabCur, verify that the same column can be
+** accessed through the index. If it cannot, then set pWalker->eCode to 1.
*/
-static void whereOrInfoDelete(sqlite3 *db, WhereOrInfo *p){
- whereClauseClear(&p->wc);
- sqlite3DbFree(db, p);
+static int codeCursorHintCheckExpr(Walker *pWalker, Expr *pExpr){
+ struct CCurHint *pHint = pWalker->u.pCCurHint;
+ assert( pHint->pIdx!=0 );
+ if( pExpr->op==TK_COLUMN
+ && pExpr->iTable==pHint->iTabCur
+ && sqlite3ColumnOfIndex(pHint->pIdx, pExpr->iColumn)<0
+ ){
+ pWalker->eCode = 1;
+ }
+ return WRC_Continue;
}
+
/*
-** Deallocate all memory associated with a WhereAndInfo object.
+** This function is called on every node of an expression tree used as an
+** argument to the OP_CursorHint instruction. If the node is a TK_COLUMN
+** that accesses any table other than the one identified by
+** CCurHint.iTabCur, then do the following:
+**
+** 1) allocate a register and code an OP_Column instruction to read
+** the specified column into the new register, and
+**
+** 2) transform the expression node to a TK_REGISTER node that reads
+** from the newly populated register.
+**
+** Also, if the node is a TK_COLUMN that does access the table idenified
+** by pCCurHint.iTabCur, and an index is being used (which we will
+** know because CCurHint.pIdx!=0) then transform the TK_COLUMN into
+** an access of the index rather than the original table.
*/
-static void whereAndInfoDelete(sqlite3 *db, WhereAndInfo *p){
- whereClauseClear(&p->wc);
- sqlite3DbFree(db, p);
+static int codeCursorHintFixExpr(Walker *pWalker, Expr *pExpr){
+ int rc = WRC_Continue;
+ struct CCurHint *pHint = pWalker->u.pCCurHint;
+ if( pExpr->op==TK_COLUMN ){
+ if( pExpr->iTable!=pHint->iTabCur ){
+ Vdbe *v = pWalker->pParse->pVdbe;
+ int reg = ++pWalker->pParse->nMem; /* Register for column value */
+ sqlite3ExprCodeGetColumnOfTable(
+ v, pExpr->pTab, pExpr->iTable, pExpr->iColumn, reg
+ );
+ pExpr->op = TK_REGISTER;
+ pExpr->iTable = reg;
+ }else if( pHint->pIdx!=0 ){
+ pExpr->iTable = pHint->iIdxCur;
+ pExpr->iColumn = sqlite3ColumnOfIndex(pHint->pIdx, pExpr->iColumn);
+ assert( pExpr->iColumn>=0 );
+ }
+ }else if( pExpr->op==TK_AGG_FUNCTION ){
+ /* An aggregate function in the WHERE clause of a query means this must
+ ** be a correlated sub-query, and expression pExpr is an aggregate from
+ ** the parent context. Do not walk the function arguments in this case.
+ **
+ ** todo: It should be possible to replace this node with a TK_REGISTER
+ ** expression, as the result of the expression must be stored in a
+ ** register at this point. The same holds for TK_AGG_COLUMN nodes. */
+ rc = WRC_Prune;
+ }
+ return rc;
}
/*
-** Deallocate a WhereClause structure. The WhereClause structure
-** itself is not freed. This routine is the inverse of whereClauseInit().
+** Insert an OP_CursorHint instruction if it is appropriate to do so.
*/
-static void whereClauseClear(WhereClause *pWC){
- int i;
- WhereTerm *a;
- sqlite3 *db = pWC->pWInfo->pParse->db;
- for(i=pWC->nTerm-1, a=pWC->a; i>=0; i--, a++){
- if( a->wtFlags & TERM_DYNAMIC ){
- sqlite3ExprDelete(db, a->pExpr);
+static void codeCursorHint(
+ WhereInfo *pWInfo, /* The where clause */
+ WhereLevel *pLevel, /* Which loop to provide hints for */
+ WhereTerm *pEndRange /* Hint this end-of-scan boundary term if not NULL */
+){
+ Parse *pParse = pWInfo->pParse;
+ sqlite3 *db = pParse->db;
+ Vdbe *v = pParse->pVdbe;
+ Expr *pExpr = 0;
+ WhereLoop *pLoop = pLevel->pWLoop;
+ int iCur;
+ WhereClause *pWC;
+ WhereTerm *pTerm;
+ int i, j;
+ struct CCurHint sHint;
+ Walker sWalker;
+
+ if( OptimizationDisabled(db, SQLITE_CursorHints) ) return;
+ iCur = pLevel->iTabCur;
+ assert( iCur==pWInfo->pTabList->a[pLevel->iFrom].iCursor );
+ sHint.iTabCur = iCur;
+ sHint.iIdxCur = pLevel->iIdxCur;
+ sHint.pIdx = pLoop->u.btree.pIndex;
+ memset(&sWalker, 0, sizeof(sWalker));
+ sWalker.pParse = pParse;
+ sWalker.u.pCCurHint = &sHint;
+ pWC = &pWInfo->sWC;
+ for(i=0; i<pWC->nTerm; i++){
+ pTerm = &pWC->a[i];
+ if( pTerm->wtFlags & (TERM_VIRTUAL|TERM_CODED) ) continue;
+ if( pTerm->prereqAll & pLevel->notReady ) continue;
+ if( ExprHasProperty(pTerm->pExpr, EP_FromJoin) ) continue;
+
+ /* All terms in pWLoop->aLTerm[] except pEndRange are used to initialize
+ ** the cursor. These terms are not needed as hints for a pure range
+ ** scan (that has no == terms) so omit them. */
+ if( pLoop->u.btree.nEq==0 && pTerm!=pEndRange ){
+ for(j=0; j<pLoop->nLTerm && pLoop->aLTerm[j]!=pTerm; j++){}
+ if( j<pLoop->nLTerm ) continue;
}
- if( a->wtFlags & TERM_ORINFO ){
- whereOrInfoDelete(db, a->u.pOrInfo);
- }else if( a->wtFlags & TERM_ANDINFO ){
- whereAndInfoDelete(db, a->u.pAndInfo);
+
+ /* No subqueries or non-deterministic functions allowed */
+ if( sqlite3ExprContainsSubquery(pTerm->pExpr) ) continue;
+
+ /* For an index scan, make sure referenced columns are actually in
+ ** the index. */
+ if( sHint.pIdx!=0 ){
+ sWalker.eCode = 0;
+ sWalker.xExprCallback = codeCursorHintCheckExpr;
+ sqlite3WalkExpr(&sWalker, pTerm->pExpr);
+ if( sWalker.eCode ) continue;
}
+
+ /* If we survive all prior tests, that means this term is worth hinting */
+ pExpr = sqlite3ExprAnd(db, pExpr, sqlite3ExprDup(db, pTerm->pExpr, 0));
}
- if( pWC->a!=pWC->aStatic ){
- sqlite3DbFree(db, pWC->a);
+ if( pExpr!=0 ){
+ sWalker.xExprCallback = codeCursorHintFixExpr;
+ sqlite3WalkExpr(&sWalker, pExpr);
+ sqlite3VdbeAddOp4(v, OP_CursorHint,
+ (sHint.pIdx ? sHint.iIdxCur : sHint.iTabCur), 0, 0,
+ (const char*)pExpr, P4_EXPR);
+ }
+}
+#else
+# define codeCursorHint(A,B,C) /* No-op */
+#endif /* SQLITE_ENABLE_CURSOR_HINTS */
+
+/*
+** Cursor iCur is open on an intkey b-tree (a table). Register iRowid contains
+** a rowid value just read from cursor iIdxCur, open on index pIdx. This
+** function generates code to do a deferred seek of cursor iCur to the
+** rowid stored in register iRowid.
+**
+** Normally, this is just:
+**
+** OP_Seek $iCur $iRowid
+**
+** However, if the scan currently being coded is a branch of an OR-loop and
+** the statement currently being coded is a SELECT, then P3 of the OP_Seek
+** is set to iIdxCur and P4 is set to point to an array of integers
+** containing one entry for each column of the table cursor iCur is open
+** on. For each table column, if the column is the i'th column of the
+** index, then the corresponding array entry is set to (i+1). If the column
+** does not appear in the index at all, the array entry is set to 0.
+*/
+static void codeDeferredSeek(
+ WhereInfo *pWInfo, /* Where clause context */
+ Index *pIdx, /* Index scan is using */
+ int iCur, /* Cursor for IPK b-tree */
+ int iIdxCur /* Index cursor */
+){
+ Parse *pParse = pWInfo->pParse; /* Parse context */
+ Vdbe *v = pParse->pVdbe; /* Vdbe to generate code within */
+
+ assert( iIdxCur>0 );
+ assert( pIdx->aiColumn[pIdx->nColumn-1]==-1 );
+
+ sqlite3VdbeAddOp3(v, OP_Seek, iIdxCur, 0, iCur);
+ if( (pWInfo->wctrlFlags & WHERE_FORCE_TABLE)
+ && DbMaskAllZero(sqlite3ParseToplevel(pParse)->writeMask)
+ ){
+ int i;
+ Table *pTab = pIdx->pTable;
+ int *ai = (int*)sqlite3DbMallocZero(pParse->db, sizeof(int)*(pTab->nCol+1));
+ if( ai ){
+ ai[0] = pTab->nCol;
+ for(i=0; i<pIdx->nColumn-1; i++){
+ assert( pIdx->aiColumn[i]<pTab->nCol );
+ if( pIdx->aiColumn[i]>=0 ) ai[pIdx->aiColumn[i]+1] = i+1;
+ }
+ sqlite3VdbeChangeP4(v, -1, (char*)ai, P4_INTARRAY);
+ }
+ }
+}
+
+/*
+** Generate code for the start of the iLevel-th loop in the WHERE clause
+** implementation described by pWInfo.
+*/
+SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart(
+ WhereInfo *pWInfo, /* Complete information about the WHERE clause */
+ int iLevel, /* Which level of pWInfo->a[] should be coded */
+ Bitmask notReady /* Which tables are currently available */
+){
+ int j, k; /* Loop counters */
+ int iCur; /* The VDBE cursor for the table */
+ int addrNxt; /* Where to jump to continue with the next IN case */
+ int omitTable; /* True if we use the index only */
+ int bRev; /* True if we need to scan in reverse order */
+ WhereLevel *pLevel; /* The where level to be coded */
+ WhereLoop *pLoop; /* The WhereLoop object being coded */
+ WhereClause *pWC; /* Decomposition of the entire WHERE clause */
+ WhereTerm *pTerm; /* A WHERE clause term */
+ Parse *pParse; /* Parsing context */
+ sqlite3 *db; /* Database connection */
+ Vdbe *v; /* The prepared stmt under constructions */
+ struct SrcList_item *pTabItem; /* FROM clause term being coded */
+ int addrBrk; /* Jump here to break out of the loop */
+ int addrCont; /* Jump here to continue with next cycle */
+ int iRowidReg = 0; /* Rowid is stored in this register, if not zero */
+ int iReleaseReg = 0; /* Temp register to free before returning */
+
+ pParse = pWInfo->pParse;
+ v = pParse->pVdbe;
+ pWC = &pWInfo->sWC;
+ db = pParse->db;
+ pLevel = &pWInfo->a[iLevel];
+ pLoop = pLevel->pWLoop;
+ pTabItem = &pWInfo->pTabList->a[pLevel->iFrom];
+ iCur = pTabItem->iCursor;
+ pLevel->notReady = notReady & ~sqlite3WhereGetMask(&pWInfo->sMaskSet, iCur);
+ bRev = (pWInfo->revMask>>iLevel)&1;
+ omitTable = (pLoop->wsFlags & WHERE_IDX_ONLY)!=0
+ && (pWInfo->wctrlFlags & WHERE_FORCE_TABLE)==0;
+ VdbeModuleComment((v, "Begin WHERE-loop%d: %s",iLevel,pTabItem->pTab->zName));
+
+ /* Create labels for the "break" and "continue" instructions
+ ** for the current loop. Jump to addrBrk to break out of a loop.
+ ** Jump to cont to go immediately to the next iteration of the
+ ** loop.
+ **
+ ** When there is an IN operator, we also have a "addrNxt" label that
+ ** means to continue with the next IN value combination. When
+ ** there are no IN operators in the constraints, the "addrNxt" label
+ ** is the same as "addrBrk".
+ */
+ addrBrk = pLevel->addrBrk = pLevel->addrNxt = sqlite3VdbeMakeLabel(v);
+ addrCont = pLevel->addrCont = sqlite3VdbeMakeLabel(v);
+
+ /* If this is the right table of a LEFT OUTER JOIN, allocate and
+ ** initialize a memory cell that records if this table matches any
+ ** row of the left table of the join.
+ */
+ if( pLevel->iFrom>0 && (pTabItem[0].fg.jointype & JT_LEFT)!=0 ){
+ pLevel->iLeftJoin = ++pParse->nMem;
+ sqlite3VdbeAddOp2(v, OP_Integer, 0, pLevel->iLeftJoin);
+ VdbeComment((v, "init LEFT JOIN no-match flag"));
+ }
+
+ /* Special case of a FROM clause subquery implemented as a co-routine */
+ if( pTabItem->fg.viaCoroutine ){
+ int regYield = pTabItem->regReturn;
+ sqlite3VdbeAddOp3(v, OP_InitCoroutine, regYield, 0, pTabItem->addrFillSub);
+ pLevel->p2 = sqlite3VdbeAddOp2(v, OP_Yield, regYield, addrBrk);
+ VdbeCoverage(v);
+ VdbeComment((v, "next row of \"%s\"", pTabItem->pTab->zName));
+ pLevel->op = OP_Goto;
+ }else
+
+#ifndef SQLITE_OMIT_VIRTUALTABLE
+ if( (pLoop->wsFlags & WHERE_VIRTUALTABLE)!=0 ){
+ /* Case 1: The table is a virtual-table. Use the VFilter and VNext
+ ** to access the data.
+ */
+ int iReg; /* P3 Value for OP_VFilter */
+ int addrNotFound;
+ int nConstraint = pLoop->nLTerm;
+ int iIn; /* Counter for IN constraints */
+
+ sqlite3ExprCachePush(pParse);
+ iReg = sqlite3GetTempRange(pParse, nConstraint+2);
+ addrNotFound = pLevel->addrBrk;
+ for(j=0; j<nConstraint; j++){
+ int iTarget = iReg+j+2;
+ pTerm = pLoop->aLTerm[j];
+ if( NEVER(pTerm==0) ) continue;
+ if( pTerm->eOperator & WO_IN ){
+ codeEqualityTerm(pParse, pTerm, pLevel, j, bRev, iTarget);
+ addrNotFound = pLevel->addrNxt;
+ }else{
+ sqlite3ExprCode(pParse, pTerm->pExpr->pRight, iTarget);
+ }
+ }
+ sqlite3VdbeAddOp2(v, OP_Integer, pLoop->u.vtab.idxNum, iReg);
+ sqlite3VdbeAddOp2(v, OP_Integer, nConstraint, iReg+1);
+ sqlite3VdbeAddOp4(v, OP_VFilter, iCur, addrNotFound, iReg,
+ pLoop->u.vtab.idxStr,
+ pLoop->u.vtab.needFree ? P4_MPRINTF : P4_STATIC);
+ VdbeCoverage(v);
+ pLoop->u.vtab.needFree = 0;
+ pLevel->p1 = iCur;
+ pLevel->op = pWInfo->eOnePass ? OP_Noop : OP_VNext;
+ pLevel->p2 = sqlite3VdbeCurrentAddr(v);
+ iIn = pLevel->u.in.nIn;
+ for(j=nConstraint-1; j>=0; j--){
+ pTerm = pLoop->aLTerm[j];
+ if( j<16 && (pLoop->u.vtab.omitMask>>j)&1 ){
+ disableTerm(pLevel, pTerm);
+ }else if( (pTerm->eOperator & WO_IN)!=0 ){
+ Expr *pCompare; /* The comparison operator */
+ Expr *pRight; /* RHS of the comparison */
+ VdbeOp *pOp; /* Opcode to access the value of the IN constraint */
+
+ /* Reload the constraint value into reg[iReg+j+2]. The same value
+ ** was loaded into the same register prior to the OP_VFilter, but
+ ** the xFilter implementation might have changed the datatype or
+ ** encoding of the value in the register, so it *must* be reloaded. */
+ assert( pLevel->u.in.aInLoop!=0 || db->mallocFailed );
+ if( !db->mallocFailed ){
+ assert( iIn>0 );
+ pOp = sqlite3VdbeGetOp(v, pLevel->u.in.aInLoop[--iIn].addrInTop);
+ assert( pOp->opcode==OP_Column || pOp->opcode==OP_Rowid );
+ assert( pOp->opcode!=OP_Column || pOp->p3==iReg+j+2 );
+ assert( pOp->opcode!=OP_Rowid || pOp->p2==iReg+j+2 );
+ testcase( pOp->opcode==OP_Rowid );
+ sqlite3VdbeAddOp3(v, pOp->opcode, pOp->p1, pOp->p2, pOp->p3);
+ }
+
+ /* Generate code that will continue to the next row if
+ ** the IN constraint is not satisfied */
+ pCompare = sqlite3PExpr(pParse, TK_EQ, 0, 0, 0);
+ assert( pCompare!=0 || db->mallocFailed );
+ if( pCompare ){
+ pCompare->pLeft = pTerm->pExpr->pLeft;
+ pCompare->pRight = pRight = sqlite3Expr(db, TK_REGISTER, 0);
+ if( pRight ){
+ pRight->iTable = iReg+j+2;
+ sqlite3ExprIfFalse(pParse, pCompare, pLevel->addrCont, 0);
+ }
+ pCompare->pLeft = 0;
+ sqlite3ExprDelete(db, pCompare);
+ }
+ }
+ }
+ /* These registers need to be preserved in case there is an IN operator
+ ** loop. So we could deallocate the registers here (and potentially
+ ** reuse them later) if (pLoop->wsFlags & WHERE_IN_ABLE)==0. But it seems
+ ** simpler and safer to simply not reuse the registers.
+ **
+ ** sqlite3ReleaseTempRange(pParse, iReg, nConstraint+2);
+ */
+ sqlite3ExprCachePop(pParse);
+ }else
+#endif /* SQLITE_OMIT_VIRTUALTABLE */
+
+ if( (pLoop->wsFlags & WHERE_IPK)!=0
+ && (pLoop->wsFlags & (WHERE_COLUMN_IN|WHERE_COLUMN_EQ))!=0
+ ){
+ /* Case 2: We can directly reference a single row using an
+ ** equality comparison against the ROWID field. Or
+ ** we reference multiple rows using a "rowid IN (...)"
+ ** construct.
+ */
+ assert( pLoop->u.btree.nEq==1 );
+ pTerm = pLoop->aLTerm[0];
+ assert( pTerm!=0 );
+ assert( pTerm->pExpr!=0 );
+ assert( omitTable==0 );
+ testcase( pTerm->wtFlags & TERM_VIRTUAL );
+ iReleaseReg = ++pParse->nMem;
+ iRowidReg = codeEqualityTerm(pParse, pTerm, pLevel, 0, bRev, iReleaseReg);
+ if( iRowidReg!=iReleaseReg ) sqlite3ReleaseTempReg(pParse, iReleaseReg);
+ addrNxt = pLevel->addrNxt;
+ sqlite3VdbeAddOp2(v, OP_MustBeInt, iRowidReg, addrNxt); VdbeCoverage(v);
+ sqlite3VdbeAddOp3(v, OP_NotExists, iCur, addrNxt, iRowidReg);
+ VdbeCoverage(v);
+ sqlite3ExprCacheAffinityChange(pParse, iRowidReg, 1);
+ sqlite3ExprCacheStore(pParse, iCur, -1, iRowidReg);
+ VdbeComment((v, "pk"));
+ pLevel->op = OP_Noop;
+ }else if( (pLoop->wsFlags & WHERE_IPK)!=0
+ && (pLoop->wsFlags & WHERE_COLUMN_RANGE)!=0
+ ){
+ /* Case 3: We have an inequality comparison against the ROWID field.
+ */
+ int testOp = OP_Noop;
+ int start;
+ int memEndValue = 0;
+ WhereTerm *pStart, *pEnd;
+
+ assert( omitTable==0 );
+ j = 0;
+ pStart = pEnd = 0;
+ if( pLoop->wsFlags & WHERE_BTM_LIMIT ) pStart = pLoop->aLTerm[j++];
+ if( pLoop->wsFlags & WHERE_TOP_LIMIT ) pEnd = pLoop->aLTerm[j++];
+ assert( pStart!=0 || pEnd!=0 );
+ if( bRev ){
+ pTerm = pStart;
+ pStart = pEnd;
+ pEnd = pTerm;
+ }
+ codeCursorHint(pWInfo, pLevel, pEnd);
+ if( pStart ){
+ Expr *pX; /* The expression that defines the start bound */
+ int r1, rTemp; /* Registers for holding the start boundary */
+
+ /* The following constant maps TK_xx codes into corresponding
+ ** seek opcodes. It depends on a particular ordering of TK_xx
+ */
+ const u8 aMoveOp[] = {
+ /* TK_GT */ OP_SeekGT,
+ /* TK_LE */ OP_SeekLE,
+ /* TK_LT */ OP_SeekLT,
+ /* TK_GE */ OP_SeekGE
+ };
+ assert( TK_LE==TK_GT+1 ); /* Make sure the ordering.. */
+ assert( TK_LT==TK_GT+2 ); /* ... of the TK_xx values... */
+ assert( TK_GE==TK_GT+3 ); /* ... is correcct. */
+
+ assert( (pStart->wtFlags & TERM_VNULL)==0 );
+ testcase( pStart->wtFlags & TERM_VIRTUAL );
+ pX = pStart->pExpr;
+ assert( pX!=0 );
+ testcase( pStart->leftCursor!=iCur ); /* transitive constraints */
+ r1 = sqlite3ExprCodeTemp(pParse, pX->pRight, &rTemp);
+ sqlite3VdbeAddOp3(v, aMoveOp[pX->op-TK_GT], iCur, addrBrk, r1);
+ VdbeComment((v, "pk"));
+ VdbeCoverageIf(v, pX->op==TK_GT);
+ VdbeCoverageIf(v, pX->op==TK_LE);
+ VdbeCoverageIf(v, pX->op==TK_LT);
+ VdbeCoverageIf(v, pX->op==TK_GE);
+ sqlite3ExprCacheAffinityChange(pParse, r1, 1);
+ sqlite3ReleaseTempReg(pParse, rTemp);
+ disableTerm(pLevel, pStart);
+ }else{
+ sqlite3VdbeAddOp2(v, bRev ? OP_Last : OP_Rewind, iCur, addrBrk);
+ VdbeCoverageIf(v, bRev==0);
+ VdbeCoverageIf(v, bRev!=0);
+ }
+ if( pEnd ){
+ Expr *pX;
+ pX = pEnd->pExpr;
+ assert( pX!=0 );
+ assert( (pEnd->wtFlags & TERM_VNULL)==0 );
+ testcase( pEnd->leftCursor!=iCur ); /* Transitive constraints */
+ testcase( pEnd->wtFlags & TERM_VIRTUAL );
+ memEndValue = ++pParse->nMem;
+ sqlite3ExprCode(pParse, pX->pRight, memEndValue);
+ if( pX->op==TK_LT || pX->op==TK_GT ){
+ testOp = bRev ? OP_Le : OP_Ge;
+ }else{
+ testOp = bRev ? OP_Lt : OP_Gt;
+ }
+ disableTerm(pLevel, pEnd);
+ }
+ start = sqlite3VdbeCurrentAddr(v);
+ pLevel->op = bRev ? OP_Prev : OP_Next;
+ pLevel->p1 = iCur;
+ pLevel->p2 = start;
+ assert( pLevel->p5==0 );
+ if( testOp!=OP_Noop ){
+ iRowidReg = ++pParse->nMem;
+ sqlite3VdbeAddOp2(v, OP_Rowid, iCur, iRowidReg);
+ sqlite3ExprCacheStore(pParse, iCur, -1, iRowidReg);
+ sqlite3VdbeAddOp3(v, testOp, memEndValue, addrBrk, iRowidReg);
+ VdbeCoverageIf(v, testOp==OP_Le);
+ VdbeCoverageIf(v, testOp==OP_Lt);
+ VdbeCoverageIf(v, testOp==OP_Ge);
+ VdbeCoverageIf(v, testOp==OP_Gt);
+ sqlite3VdbeChangeP5(v, SQLITE_AFF_NUMERIC | SQLITE_JUMPIFNULL);
+ }
+ }else if( pLoop->wsFlags & WHERE_INDEXED ){
+ /* Case 4: A scan using an index.
+ **
+ ** The WHERE clause may contain zero or more equality
+ ** terms ("==" or "IN" operators) that refer to the N
+ ** left-most columns of the index. It may also contain
+ ** inequality constraints (>, <, >= or <=) on the indexed
+ ** column that immediately follows the N equalities. Only
+ ** the right-most column can be an inequality - the rest must
+ ** use the "==" and "IN" operators. For example, if the
+ ** index is on (x,y,z), then the following clauses are all
+ ** optimized:
+ **
+ ** x=5
+ ** x=5 AND y=10
+ ** x=5 AND y<10
+ ** x=5 AND y>5 AND y<10
+ ** x=5 AND y=5 AND z<=10
+ **
+ ** The z<10 term of the following cannot be used, only
+ ** the x=5 term:
+ **
+ ** x=5 AND z<10
+ **
+ ** N may be zero if there are inequality constraints.
+ ** If there are no inequality constraints, then N is at
+ ** least one.
+ **
+ ** This case is also used when there are no WHERE clause
+ ** constraints but an index is selected anyway, in order
+ ** to force the output order to conform to an ORDER BY.
+ */
+ static const u8 aStartOp[] = {
+ 0,
+ 0,
+ OP_Rewind, /* 2: (!start_constraints && startEq && !bRev) */
+ OP_Last, /* 3: (!start_constraints && startEq && bRev) */
+ OP_SeekGT, /* 4: (start_constraints && !startEq && !bRev) */
+ OP_SeekLT, /* 5: (start_constraints && !startEq && bRev) */
+ OP_SeekGE, /* 6: (start_constraints && startEq && !bRev) */
+ OP_SeekLE /* 7: (start_constraints && startEq && bRev) */
+ };
+ static const u8 aEndOp[] = {
+ OP_IdxGE, /* 0: (end_constraints && !bRev && !endEq) */
+ OP_IdxGT, /* 1: (end_constraints && !bRev && endEq) */
+ OP_IdxLE, /* 2: (end_constraints && bRev && !endEq) */
+ OP_IdxLT, /* 3: (end_constraints && bRev && endEq) */
+ };
+ u16 nEq = pLoop->u.btree.nEq; /* Number of == or IN terms */
+ int regBase; /* Base register holding constraint values */
+ WhereTerm *pRangeStart = 0; /* Inequality constraint at range start */
+ WhereTerm *pRangeEnd = 0; /* Inequality constraint at range end */
+ int startEq; /* True if range start uses ==, >= or <= */
+ int endEq; /* True if range end uses ==, >= or <= */
+ int start_constraints; /* Start of range is constrained */
+ int nConstraint; /* Number of constraint terms */
+ Index *pIdx; /* The index we will be using */
+ int iIdxCur; /* The VDBE cursor for the index */
+ int nExtraReg = 0; /* Number of extra registers needed */
+ int op; /* Instruction opcode */
+ char *zStartAff; /* Affinity for start of range constraint */
+ char cEndAff = 0; /* Affinity for end of range constraint */
+ u8 bSeekPastNull = 0; /* True to seek past initial nulls */
+ u8 bStopAtNull = 0; /* Add condition to terminate at NULLs */
+
+ pIdx = pLoop->u.btree.pIndex;
+ iIdxCur = pLevel->iIdxCur;
+ assert( nEq>=pLoop->nSkip );
+
+ /* If this loop satisfies a sort order (pOrderBy) request that
+ ** was passed to this function to implement a "SELECT min(x) ..."
+ ** query, then the caller will only allow the loop to run for
+ ** a single iteration. This means that the first row returned
+ ** should not have a NULL value stored in 'x'. If column 'x' is
+ ** the first one after the nEq equality constraints in the index,
+ ** this requires some special handling.
+ */
+ assert( pWInfo->pOrderBy==0
+ || pWInfo->pOrderBy->nExpr==1
+ || (pWInfo->wctrlFlags&WHERE_ORDERBY_MIN)==0 );
+ if( (pWInfo->wctrlFlags&WHERE_ORDERBY_MIN)!=0
+ && pWInfo->nOBSat>0
+ && (pIdx->nKeyCol>nEq)
+ ){
+ assert( pLoop->nSkip==0 );
+ bSeekPastNull = 1;
+ nExtraReg = 1;
+ }
+
+ /* Find any inequality constraint terms for the start and end
+ ** of the range.
+ */
+ j = nEq;
+ 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;
+#ifndef SQLITE_LIKE_DOESNT_MATCH_BLOBS
+ if( (pRangeEnd->wtFlags & TERM_LIKEOPT)!=0 ){
+ assert( pRangeStart!=0 ); /* LIKE opt constraints */
+ assert( pRangeStart->wtFlags & TERM_LIKEOPT ); /* occur in pairs */
+ pLevel->iLikeRepCntr = (u32)++pParse->nMem;
+ sqlite3VdbeAddOp2(v, OP_Integer, 1, (int)pLevel->iLikeRepCntr);
+ VdbeComment((v, "LIKE loop counter"));
+ pLevel->addrLikeRep = sqlite3VdbeCurrentAddr(v);
+ /* iLikeRepCntr actually stores 2x the counter register number. The
+ ** bottom bit indicates whether the search order is ASC or DESC. */
+ testcase( bRev );
+ testcase( pIdx->aSortOrder[nEq]==SQLITE_SO_DESC );
+ assert( (bRev & ~1)==0 );
+ pLevel->iLikeRepCntr <<=1;
+ pLevel->iLikeRepCntr |= bRev ^ (pIdx->aSortOrder[nEq]==SQLITE_SO_DESC);
+ }
+#endif
+ if( pRangeStart==0
+ && (j = pIdx->aiColumn[nEq])>=0
+ && pIdx->pTable->aCol[j].notNull==0
+ ){
+ bSeekPastNull = 1;
+ }
+ }
+ assert( pRangeEnd==0 || (pRangeEnd->wtFlags & TERM_VNULL)==0 );
+
+ /* If we are doing a reverse order scan on an ascending index, or
+ ** a forward order scan on a descending index, interchange the
+ ** start and end terms (pRangeStart and pRangeEnd).
+ */
+ if( (nEq<pIdx->nKeyCol && bRev==(pIdx->aSortOrder[nEq]==SQLITE_SO_ASC))
+ || (bRev && pIdx->nKeyCol==nEq)
+ ){
+ SWAP(WhereTerm *, pRangeEnd, pRangeStart);
+ SWAP(u8, bSeekPastNull, bStopAtNull);
+ }
+
+ /* Generate code to evaluate all constraint terms using == or IN
+ ** and store the values of those terms in an array of registers
+ ** starting at regBase.
+ */
+ codeCursorHint(pWInfo, pLevel, pRangeEnd);
+ regBase = codeAllEqualityTerms(pParse,pLevel,bRev,nExtraReg,&zStartAff);
+ assert( zStartAff==0 || sqlite3Strlen30(zStartAff)>=nEq );
+ if( zStartAff ) cEndAff = zStartAff[nEq];
+ addrNxt = pLevel->addrNxt;
+
+ testcase( pRangeStart && (pRangeStart->eOperator & WO_LE)!=0 );
+ testcase( pRangeStart && (pRangeStart->eOperator & WO_GE)!=0 );
+ testcase( pRangeEnd && (pRangeEnd->eOperator & WO_LE)!=0 );
+ testcase( pRangeEnd && (pRangeEnd->eOperator & WO_GE)!=0 );
+ startEq = !pRangeStart || pRangeStart->eOperator & (WO_LE|WO_GE);
+ endEq = !pRangeEnd || pRangeEnd->eOperator & (WO_LE|WO_GE);
+ start_constraints = pRangeStart || nEq>0;
+
+ /* Seek the index cursor to the start of the range. */
+ nConstraint = nEq;
+ if( pRangeStart ){
+ Expr *pRight = pRangeStart->pExpr->pRight;
+ sqlite3ExprCode(pParse, pRight, regBase+nEq);
+ whereLikeOptimizationStringFixup(v, pLevel, pRangeStart);
+ if( (pRangeStart->wtFlags & TERM_VNULL)==0
+ && sqlite3ExprCanBeNull(pRight)
+ ){
+ sqlite3VdbeAddOp2(v, OP_IsNull, regBase+nEq, addrNxt);
+ VdbeCoverage(v);
+ }
+ if( zStartAff ){
+ if( sqlite3CompareAffinity(pRight, zStartAff[nEq])==SQLITE_AFF_BLOB){
+ /* Since the comparison is to be performed with no conversions
+ ** applied to the operands, set the affinity to apply to pRight to
+ ** SQLITE_AFF_BLOB. */
+ zStartAff[nEq] = SQLITE_AFF_BLOB;
+ }
+ if( sqlite3ExprNeedsNoAffinityChange(pRight, zStartAff[nEq]) ){
+ zStartAff[nEq] = SQLITE_AFF_BLOB;
+ }
+ }
+ nConstraint++;
+ testcase( pRangeStart->wtFlags & TERM_VIRTUAL );
+ }else if( bSeekPastNull ){
+ sqlite3VdbeAddOp2(v, OP_Null, 0, regBase+nEq);
+ nConstraint++;
+ startEq = 0;
+ start_constraints = 1;
+ }
+ codeApplyAffinity(pParse, regBase, nConstraint - bSeekPastNull, zStartAff);
+ if( pLoop->nSkip>0 && nConstraint==pLoop->nSkip ){
+ /* The skip-scan logic inside the call to codeAllEqualityConstraints()
+ ** above has already left the cursor sitting on the correct row,
+ ** so no further seeking is needed */
+ }else{
+ op = aStartOp[(start_constraints<<2) + (startEq<<1) + bRev];
+ assert( op!=0 );
+ sqlite3VdbeAddOp4Int(v, op, iIdxCur, addrNxt, regBase, nConstraint);
+ VdbeCoverage(v);
+ VdbeCoverageIf(v, op==OP_Rewind); testcase( op==OP_Rewind );
+ VdbeCoverageIf(v, op==OP_Last); testcase( op==OP_Last );
+ VdbeCoverageIf(v, op==OP_SeekGT); testcase( op==OP_SeekGT );
+ VdbeCoverageIf(v, op==OP_SeekGE); testcase( op==OP_SeekGE );
+ VdbeCoverageIf(v, op==OP_SeekLE); testcase( op==OP_SeekLE );
+ VdbeCoverageIf(v, op==OP_SeekLT); testcase( op==OP_SeekLT );
+ }
+
+ /* Load the value for the inequality constraint at the end of the
+ ** range (if any).
+ */
+ nConstraint = nEq;
+ if( pRangeEnd ){
+ 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)
+ ){
+ sqlite3VdbeAddOp2(v, OP_IsNull, regBase+nEq, addrNxt);
+ VdbeCoverage(v);
+ }
+ if( sqlite3CompareAffinity(pRight, cEndAff)!=SQLITE_AFF_BLOB
+ && !sqlite3ExprNeedsNoAffinityChange(pRight, cEndAff)
+ ){
+ codeApplyAffinity(pParse, regBase+nEq, 1, &cEndAff);
+ }
+ nConstraint++;
+ testcase( pRangeEnd->wtFlags & TERM_VIRTUAL );
+ }else if( bStopAtNull ){
+ sqlite3VdbeAddOp2(v, OP_Null, 0, regBase+nEq);
+ endEq = 0;
+ nConstraint++;
+ }
+ sqlite3DbFree(db, zStartAff);
+
+ /* Top of the loop body */
+ pLevel->p2 = sqlite3VdbeCurrentAddr(v);
+
+ /* Check if the index cursor is past the end of the range. */
+ if( nConstraint ){
+ op = aEndOp[bRev*2 + endEq];
+ sqlite3VdbeAddOp4Int(v, op, iIdxCur, addrNxt, regBase, nConstraint);
+ testcase( op==OP_IdxGT ); VdbeCoverageIf(v, op==OP_IdxGT );
+ testcase( op==OP_IdxGE ); VdbeCoverageIf(v, op==OP_IdxGE );
+ testcase( op==OP_IdxLT ); VdbeCoverageIf(v, op==OP_IdxLT );
+ testcase( op==OP_IdxLE ); VdbeCoverageIf(v, op==OP_IdxLE );
+ }
+
+ /* Seek the table cursor, if required */
+ disableTerm(pLevel, pRangeStart);
+ disableTerm(pLevel, pRangeEnd);
+ if( omitTable ){
+ /* pIdx is a covering index. No need to access the main table. */
+ }else if( HasRowid(pIdx->pTable) ){
+ if( (pWInfo->wctrlFlags & WHERE_SEEK_TABLE)!=0 ){
+ iRowidReg = ++pParse->nMem;
+ sqlite3VdbeAddOp2(v, OP_IdxRowid, iIdxCur, iRowidReg);
+ sqlite3ExprCacheStore(pParse, iCur, -1, iRowidReg);
+ sqlite3VdbeAddOp3(v, OP_NotExists, iCur, 0, iRowidReg);
+ VdbeCoverage(v);
+ }else{
+ codeDeferredSeek(pWInfo, pIdx, iCur, iIdxCur);
+ }
+ }else if( iCur!=iIdxCur ){
+ Index *pPk = sqlite3PrimaryKeyIndex(pIdx->pTable);
+ iRowidReg = sqlite3GetTempRange(pParse, pPk->nKeyCol);
+ for(j=0; j<pPk->nKeyCol; j++){
+ k = sqlite3ColumnOfIndex(pIdx, pPk->aiColumn[j]);
+ sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, k, iRowidReg+j);
+ }
+ sqlite3VdbeAddOp4Int(v, OP_NotFound, iCur, addrCont,
+ iRowidReg, pPk->nKeyCol); VdbeCoverage(v);
+ }
+
+ /* Record the instruction used to terminate the loop. Disable
+ ** WHERE clause terms made redundant by the index range scan.
+ */
+ if( pLoop->wsFlags & WHERE_ONEROW ){
+ pLevel->op = OP_Noop;
+ }else if( bRev ){
+ pLevel->op = OP_Prev;
+ }else{
+ pLevel->op = OP_Next;
+ }
+ pLevel->p1 = iIdxCur;
+ pLevel->p3 = (pLoop->wsFlags&WHERE_UNQ_WANTED)!=0 ? 1:0;
+ if( (pLoop->wsFlags & WHERE_CONSTRAINT)==0 ){
+ pLevel->p5 = SQLITE_STMTSTATUS_FULLSCAN_STEP;
+ }else{
+ assert( pLevel->p5==0 );
+ }
+ }else
+
+#ifndef SQLITE_OMIT_OR_OPTIMIZATION
+ if( pLoop->wsFlags & WHERE_MULTI_OR ){
+ /* Case 5: Two or more separately indexed terms connected by OR
+ **
+ ** Example:
+ **
+ ** CREATE TABLE t1(a,b,c,d);
+ ** CREATE INDEX i1 ON t1(a);
+ ** CREATE INDEX i2 ON t1(b);
+ ** CREATE INDEX i3 ON t1(c);
+ **
+ ** SELECT * FROM t1 WHERE a=5 OR b=7 OR (c=11 AND d=13)
+ **
+ ** In the example, there are three indexed terms connected by OR.
+ ** The top of the loop looks like this:
+ **
+ ** Null 1 # Zero the rowset in reg 1
+ **
+ ** Then, for each indexed term, the following. The arguments to
+ ** RowSetTest are such that the rowid of the current row is inserted
+ ** into the RowSet. If it is already present, control skips the
+ ** Gosub opcode and jumps straight to the code generated by WhereEnd().
+ **
+ ** sqlite3WhereBegin(<term>)
+ ** RowSetTest # Insert rowid into rowset
+ ** Gosub 2 A
+ ** sqlite3WhereEnd()
+ **
+ ** Following the above, code to terminate the loop. Label A, the target
+ ** of the Gosub above, jumps to the instruction right after the Goto.
+ **
+ ** Null 1 # Zero the rowset in reg 1
+ ** Goto B # The loop is finished.
+ **
+ ** A: <loop body> # Return data, whatever.
+ **
+ ** Return 2 # Jump back to the Gosub
+ **
+ ** B: <after the loop>
+ **
+ ** Added 2014-05-26: If the table is a WITHOUT ROWID table, then
+ ** use an ephemeral index instead of a RowSet to record the primary
+ ** keys of the rows we have already seen.
+ **
+ */
+ WhereClause *pOrWc; /* The OR-clause broken out into subterms */
+ SrcList *pOrTab; /* Shortened table list or OR-clause generation */
+ Index *pCov = 0; /* Potential covering index (or NULL) */
+ int iCovCur = pParse->nTab++; /* Cursor used for index scans (if any) */
+
+ int regReturn = ++pParse->nMem; /* Register used with OP_Gosub */
+ int regRowset = 0; /* Register for RowSet object */
+ int regRowid = 0; /* Register holding rowid */
+ int iLoopBody = sqlite3VdbeMakeLabel(v); /* Start of loop body */
+ int iRetInit; /* Address of regReturn init */
+ int untestedTerms = 0; /* Some terms not completely tested */
+ int ii; /* Loop counter */
+ u16 wctrlFlags; /* Flags for sub-WHERE clause */
+ Expr *pAndExpr = 0; /* An ".. AND (...)" expression */
+ Table *pTab = pTabItem->pTab;
+
+ pTerm = pLoop->aLTerm[0];
+ assert( pTerm!=0 );
+ assert( pTerm->eOperator & WO_OR );
+ assert( (pTerm->wtFlags & TERM_ORINFO)!=0 );
+ pOrWc = &pTerm->u.pOrInfo->wc;
+ pLevel->op = OP_Return;
+ pLevel->p1 = regReturn;
+
+ /* Set up a new SrcList in pOrTab containing the table being scanned
+ ** by this loop in the a[0] slot and all notReady tables in a[1..] slots.
+ ** This becomes the SrcList in the recursive call to sqlite3WhereBegin().
+ */
+ if( pWInfo->nLevel>1 ){
+ int nNotReady; /* The number of notReady tables */
+ struct SrcList_item *origSrc; /* Original list of tables */
+ nNotReady = pWInfo->nLevel - iLevel - 1;
+ pOrTab = sqlite3StackAllocRaw(db,
+ sizeof(*pOrTab)+ nNotReady*sizeof(pOrTab->a[0]));
+ if( pOrTab==0 ) return notReady;
+ pOrTab->nAlloc = (u8)(nNotReady + 1);
+ pOrTab->nSrc = pOrTab->nAlloc;
+ memcpy(pOrTab->a, pTabItem, sizeof(*pTabItem));
+ origSrc = pWInfo->pTabList->a;
+ for(k=1; k<=nNotReady; k++){
+ memcpy(&pOrTab->a[k], &origSrc[pLevel[k].iFrom], sizeof(pOrTab->a[k]));
+ }
+ }else{
+ pOrTab = pWInfo->pTabList;
+ }
+
+ /* Initialize the rowset register to contain NULL. An SQL NULL is
+ ** equivalent to an empty rowset. Or, create an ephemeral index
+ ** capable of holding primary keys in the case of a WITHOUT ROWID.
+ **
+ ** Also initialize regReturn to contain the address of the instruction
+ ** immediately following the OP_Return at the bottom of the loop. This
+ ** is required in a few obscure LEFT JOIN cases where control jumps
+ ** over the top of the loop into the body of it. In this case the
+ ** correct response for the end-of-loop code (the OP_Return) is to
+ ** fall through to the next instruction, just as an OP_Next does if
+ ** called on an uninitialized cursor.
+ */
+ if( (pWInfo->wctrlFlags & WHERE_DUPLICATES_OK)==0 ){
+ if( HasRowid(pTab) ){
+ regRowset = ++pParse->nMem;
+ sqlite3VdbeAddOp2(v, OP_Null, 0, regRowset);
+ }else{
+ Index *pPk = sqlite3PrimaryKeyIndex(pTab);
+ regRowset = pParse->nTab++;
+ sqlite3VdbeAddOp2(v, OP_OpenEphemeral, regRowset, pPk->nKeyCol);
+ sqlite3VdbeSetP4KeyInfo(pParse, pPk);
+ }
+ regRowid = ++pParse->nMem;
+ }
+ iRetInit = sqlite3VdbeAddOp2(v, OP_Integer, 0, regReturn);
+
+ /* If the original WHERE clause is z of the form: (x1 OR x2 OR ...) AND y
+ ** Then for every term xN, evaluate as the subexpression: xN AND z
+ ** That way, terms in y that are factored into the disjunction will
+ ** be picked up by the recursive calls to sqlite3WhereBegin() below.
+ **
+ ** Actually, each subexpression is converted to "xN AND w" where w is
+ ** the "interesting" terms of z - terms that did not originate in the
+ ** ON or USING clause of a LEFT JOIN, and terms that are usable as
+ ** indices.
+ **
+ ** This optimization also only applies if the (x1 OR x2 OR ...) term
+ ** is not contained in the ON clause of a LEFT JOIN.
+ ** See ticket http://www.sqlite.org/src/info/f2369304e4
+ */
+ if( pWC->nTerm>1 ){
+ int iTerm;
+ for(iTerm=0; iTerm<pWC->nTerm; iTerm++){
+ Expr *pExpr = pWC->a[iTerm].pExpr;
+ if( &pWC->a[iTerm] == pTerm ) continue;
+ if( ExprHasProperty(pExpr, EP_FromJoin) ) continue;
+ testcase( pWC->a[iTerm].wtFlags & TERM_VIRTUAL );
+ testcase( pWC->a[iTerm].wtFlags & TERM_CODED );
+ if( (pWC->a[iTerm].wtFlags & (TERM_VIRTUAL|TERM_CODED))!=0 ) continue;
+ if( (pWC->a[iTerm].eOperator & WO_ALL)==0 ) continue;
+ testcase( pWC->a[iTerm].wtFlags & TERM_ORINFO );
+ pExpr = sqlite3ExprDup(db, pExpr, 0);
+ pAndExpr = sqlite3ExprAnd(db, pAndExpr, pExpr);
+ }
+ if( pAndExpr ){
+ pAndExpr = sqlite3PExpr(pParse, TK_AND|TKFLG_DONTFOLD, 0, pAndExpr, 0);
+ }
+ }
+
+ /* Run a separate WHERE clause for each term of the OR clause. After
+ ** eliminating duplicates from other WHERE clauses, the action for each
+ ** sub-WHERE clause is to to invoke the main loop body as a subroutine.
+ */
+ wctrlFlags = WHERE_OMIT_OPEN_CLOSE
+ | WHERE_FORCE_TABLE
+ | WHERE_ONETABLE_ONLY
+ | WHERE_NO_AUTOINDEX
+ | (pWInfo->wctrlFlags & WHERE_SEEK_TABLE);
+ for(ii=0; ii<pOrWc->nTerm; ii++){
+ WhereTerm *pOrTerm = &pOrWc->a[ii];
+ if( pOrTerm->leftCursor==iCur || (pOrTerm->eOperator & WO_AND)!=0 ){
+ WhereInfo *pSubWInfo; /* Info for single OR-term scan */
+ Expr *pOrExpr = pOrTerm->pExpr; /* Current OR clause term */
+ int jmp1 = 0; /* Address of jump operation */
+ if( pAndExpr && !ExprHasProperty(pOrExpr, EP_FromJoin) ){
+ pAndExpr->pLeft = pOrExpr;
+ pOrExpr = pAndExpr;
+ }
+ /* Loop through table entries that match term pOrTerm. */
+ WHERETRACE(0xffff, ("Subplan for OR-clause:\n"));
+ pSubWInfo = sqlite3WhereBegin(pParse, pOrTab, pOrExpr, 0, 0,
+ wctrlFlags, iCovCur);
+ assert( pSubWInfo || pParse->nErr || db->mallocFailed );
+ if( pSubWInfo ){
+ WhereLoop *pSubLoop;
+ int addrExplain = sqlite3WhereExplainOneScan(
+ pParse, pOrTab, &pSubWInfo->a[0], iLevel, pLevel->iFrom, 0
+ );
+ sqlite3WhereAddScanStatus(v, pOrTab, &pSubWInfo->a[0], addrExplain);
+
+ /* This is the sub-WHERE clause body. First skip over
+ ** duplicate rows from prior sub-WHERE clauses, and record the
+ ** rowid (or PRIMARY KEY) for the current row so that the same
+ ** row will be skipped in subsequent sub-WHERE clauses.
+ */
+ if( (pWInfo->wctrlFlags & WHERE_DUPLICATES_OK)==0 ){
+ int r;
+ int iSet = ((ii==pOrWc->nTerm-1)?-1:ii);
+ if( HasRowid(pTab) ){
+ r = sqlite3ExprCodeGetColumn(pParse, pTab, -1, iCur, regRowid, 0);
+ jmp1 = sqlite3VdbeAddOp4Int(v, OP_RowSetTest, regRowset, 0,
+ r,iSet);
+ VdbeCoverage(v);
+ }else{
+ Index *pPk = sqlite3PrimaryKeyIndex(pTab);
+ int nPk = pPk->nKeyCol;
+ int iPk;
+
+ /* Read the PK into an array of temp registers. */
+ r = sqlite3GetTempRange(pParse, nPk);
+ for(iPk=0; iPk<nPk; iPk++){
+ int iCol = pPk->aiColumn[iPk];
+ sqlite3ExprCodeGetColumnToReg(pParse, pTab, iCol, iCur, r+iPk);
+ }
+
+ /* Check if the temp table already contains this key. If so,
+ ** the row has already been included in the result set and
+ ** can be ignored (by jumping past the Gosub below). Otherwise,
+ ** insert the key into the temp table and proceed with processing
+ ** the row.
+ **
+ ** Use some of the same optimizations as OP_RowSetTest: If iSet
+ ** is zero, assume that the key cannot already be present in
+ ** the temp table. And if iSet is -1, assume that there is no
+ ** need to insert the key into the temp table, as it will never
+ ** be tested for. */
+ if( iSet ){
+ jmp1 = sqlite3VdbeAddOp4Int(v, OP_Found, regRowset, 0, r, nPk);
+ VdbeCoverage(v);
+ }
+ if( iSet>=0 ){
+ sqlite3VdbeAddOp3(v, OP_MakeRecord, r, nPk, regRowid);
+ sqlite3VdbeAddOp3(v, OP_IdxInsert, regRowset, regRowid, 0);
+ if( iSet ) sqlite3VdbeChangeP5(v, OPFLAG_USESEEKRESULT);
+ }
+
+ /* Release the array of temp registers */
+ sqlite3ReleaseTempRange(pParse, r, nPk);
+ }
+ }
+
+ /* Invoke the main loop body as a subroutine */
+ sqlite3VdbeAddOp2(v, OP_Gosub, regReturn, iLoopBody);
+
+ /* Jump here (skipping the main loop body subroutine) if the
+ ** current sub-WHERE row is a duplicate from prior sub-WHEREs. */
+ if( jmp1 ) sqlite3VdbeJumpHere(v, jmp1);
+
+ /* The pSubWInfo->untestedTerms flag means that this OR term
+ ** contained one or more AND term from a notReady table. The
+ ** terms from the notReady table could not be tested and will
+ ** need to be tested later.
+ */
+ if( pSubWInfo->untestedTerms ) untestedTerms = 1;
+
+ /* If all of the OR-connected terms are optimized using the same
+ ** index, and the index is opened using the same cursor number
+ ** by each call to sqlite3WhereBegin() made by this loop, it may
+ ** be possible to use that index as a covering index.
+ **
+ ** If the call to sqlite3WhereBegin() above resulted in a scan that
+ ** uses an index, and this is either the first OR-connected term
+ ** processed or the index is the same as that used by all previous
+ ** terms, set pCov to the candidate covering index. Otherwise, set
+ ** pCov to NULL to indicate that no candidate covering index will
+ ** be available.
+ */
+ pSubLoop = pSubWInfo->a[0].pWLoop;
+ assert( (pSubLoop->wsFlags & WHERE_AUTO_INDEX)==0 );
+ if( (pSubLoop->wsFlags & WHERE_INDEXED)!=0
+ && (ii==0 || pSubLoop->u.btree.pIndex==pCov)
+ && (HasRowid(pTab) || !IsPrimaryKeyIndex(pSubLoop->u.btree.pIndex))
+ ){
+ assert( pSubWInfo->a[0].iIdxCur==iCovCur );
+ pCov = pSubLoop->u.btree.pIndex;
+ wctrlFlags |= WHERE_REOPEN_IDX;
+ }else{
+ pCov = 0;
+ }
+
+ /* Finish the loop through table entries that match term pOrTerm. */
+ sqlite3WhereEnd(pSubWInfo);
+ }
+ }
+ }
+ pLevel->u.pCovidx = pCov;
+ if( pCov ) pLevel->iIdxCur = iCovCur;
+ if( pAndExpr ){
+ pAndExpr->pLeft = 0;
+ sqlite3ExprDelete(db, pAndExpr);
+ }
+ sqlite3VdbeChangeP1(v, iRetInit, sqlite3VdbeCurrentAddr(v));
+ sqlite3VdbeGoto(v, pLevel->addrBrk);
+ sqlite3VdbeResolveLabel(v, iLoopBody);
+
+ if( pWInfo->nLevel>1 ) sqlite3StackFree(db, pOrTab);
+ if( !untestedTerms ) disableTerm(pLevel, pTerm);
+ }else
+#endif /* SQLITE_OMIT_OR_OPTIMIZATION */
+
+ {
+ /* Case 6: There is no usable index. We must do a complete
+ ** scan of the entire table.
+ */
+ static const u8 aStep[] = { OP_Next, OP_Prev };
+ static const u8 aStart[] = { OP_Rewind, OP_Last };
+ assert( bRev==0 || bRev==1 );
+ if( pTabItem->fg.isRecursive ){
+ /* Tables marked isRecursive have only a single row that is stored in
+ ** a pseudo-cursor. No need to Rewind or Next such cursors. */
+ pLevel->op = OP_Noop;
+ }else{
+ codeCursorHint(pWInfo, pLevel, 0);
+ pLevel->op = aStep[bRev];
+ pLevel->p1 = iCur;
+ pLevel->p2 = 1 + sqlite3VdbeAddOp2(v, aStart[bRev], iCur, addrBrk);
+ VdbeCoverageIf(v, bRev==0);
+ VdbeCoverageIf(v, bRev!=0);
+ pLevel->p5 = SQLITE_STMTSTATUS_FULLSCAN_STEP;
+ }
+ }
+
+#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
+ pLevel->addrVisit = sqlite3VdbeCurrentAddr(v);
+#endif
+
+ /* Insert code to test every subexpression that can be completely
+ ** computed using the current set of tables.
+ */
+ 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;
+ if( (pTerm->prereqAll & pLevel->notReady)!=0 ){
+ testcase( pWInfo->untestedTerms==0
+ && (pWInfo->wctrlFlags & WHERE_ONETABLE_ONLY)!=0 );
+ pWInfo->untestedTerms = 1;
+ continue;
+ }
+ pE = pTerm->pExpr;
+ assert( pE!=0 );
+ if( pLevel->iLeftJoin && !ExprHasProperty(pE, EP_FromJoin) ){
+ continue;
+ }
+ if( pTerm->wtFlags & TERM_LIKECOND ){
+ /* If the TERM_LIKECOND flag is set, that means that the range search
+ ** is sufficient to guarantee that the LIKE operator is true, so we
+ ** can skip the call to the like(A,B) function. But this only works
+ ** for strings. So do not skip the call to the function on the pass
+ ** that compares BLOBs. */
+#ifdef SQLITE_LIKE_DOESNT_MATCH_BLOBS
+ continue;
+#else
+ u32 x = pLevel->iLikeRepCntr;
+ assert( x>0 );
+ skipLikeAddr = sqlite3VdbeAddOp1(v, (x&1)? OP_IfNot : OP_If, (int)(x>>1));
+ VdbeCoverage(v);
+#endif
+ }
+ sqlite3ExprIfFalse(pParse, pE, addrCont, SQLITE_JUMPIFNULL);
+ if( skipLikeAddr ) sqlite3VdbeJumpHere(v, skipLikeAddr);
+ pTerm->wtFlags |= TERM_CODED;
+ }
+
+ /* Insert code to test for implied constraints based on transitivity
+ ** of the "==" operator.
+ **
+ ** Example: If the WHERE clause contains "t1.a=t2.b" and "t2.b=123"
+ ** and we are coding the t1 loop and the t2 loop has not yet coded,
+ ** then we cannot use the "t1.a=t2.b" constraint, but we can code
+ ** the implied "t1.a=123" constraint.
+ */
+ for(pTerm=pWC->a, j=pWC->nTerm; j>0; j--, pTerm++){
+ Expr *pE, *pEAlt;
+ WhereTerm *pAlt;
+ if( pTerm->wtFlags & (TERM_VIRTUAL|TERM_CODED) ) continue;
+ if( (pTerm->eOperator & (WO_EQ|WO_IS))==0 ) continue;
+ if( (pTerm->eOperator & WO_EQUIV)==0 ) continue;
+ if( pTerm->leftCursor!=iCur ) continue;
+ if( pLevel->iLeftJoin ) continue;
+ pE = pTerm->pExpr;
+ assert( !ExprHasProperty(pE, EP_FromJoin) );
+ assert( (pTerm->prereqRight & pLevel->notReady)!=0 );
+ pAlt = sqlite3WhereFindTerm(pWC, iCur, pTerm->u.leftColumn, notReady,
+ WO_EQ|WO_IN|WO_IS, 0);
+ if( pAlt==0 ) continue;
+ if( pAlt->wtFlags & (TERM_CODED) ) continue;
+ testcase( pAlt->eOperator & WO_EQ );
+ testcase( pAlt->eOperator & WO_IS );
+ testcase( pAlt->eOperator & WO_IN );
+ VdbeModuleComment((v, "begin transitive constraint"));
+ pEAlt = sqlite3StackAllocRaw(db, sizeof(*pEAlt));
+ if( pEAlt ){
+ *pEAlt = *pAlt->pExpr;
+ pEAlt->pLeft = pE->pLeft;
+ sqlite3ExprIfFalse(pParse, pEAlt, addrCont, SQLITE_JUMPIFNULL);
+ sqlite3StackFree(db, pEAlt);
+ }
}
+
+ /* For a LEFT OUTER JOIN, generate code that will record the fact that
+ ** at least one row of the right table has matched the left table.
+ */
+ if( pLevel->iLeftJoin ){
+ pLevel->addrFirst = sqlite3VdbeCurrentAddr(v);
+ sqlite3VdbeAddOp2(v, OP_Integer, 1, pLevel->iLeftJoin);
+ VdbeComment((v, "record LEFT JOIN hit"));
+ sqlite3ExprCacheClear(pParse);
+ for(pTerm=pWC->a, j=0; j<pWC->nTerm; j++, pTerm++){
+ testcase( pTerm->wtFlags & TERM_VIRTUAL );
+ testcase( pTerm->wtFlags & TERM_CODED );
+ if( pTerm->wtFlags & (TERM_VIRTUAL|TERM_CODED) ) continue;
+ if( (pTerm->prereqAll & pLevel->notReady)!=0 ){
+ assert( pWInfo->untestedTerms );
+ continue;
+ }
+ assert( pTerm->pExpr );
+ sqlite3ExprIfFalse(pParse, pTerm->pExpr, addrCont, SQLITE_JUMPIFNULL);
+ pTerm->wtFlags |= TERM_CODED;
+ }
+ }
+
+ return pLevel->notReady;
+}
+
+/************** End of wherecode.c *******************************************/
+/************** Begin file whereexpr.c ***************************************/
+/*
+** 2015-06-08
+**
+** 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 module contains C code that generates VDBE code used to process
+** the WHERE clause of SQL statements.
+**
+** This file was originally part of where.c but was split out to improve
+** readability and editabiliity. This file contains utility routines for
+** analyzing Expr objects in the WHERE clause.
+*/
+/* #include "sqliteInt.h" */
+/* #include "whereInt.h" */
+
+/* Forward declarations */
+static void exprAnalyze(SrcList*, WhereClause*, int);
+
+/*
+** Deallocate all memory associated with a WhereOrInfo object.
+*/
+static void whereOrInfoDelete(sqlite3 *db, WhereOrInfo *p){
+ sqlite3WhereClauseClear(&p->wc);
+ sqlite3DbFree(db, p);
+}
+
+/*
+** Deallocate all memory associated with a WhereAndInfo object.
+*/
+static void whereAndInfoDelete(sqlite3 *db, WhereAndInfo *p){
+ sqlite3WhereClauseClear(&p->wc);
+ sqlite3DbFree(db, p);
}
/*
@@ -116383,7 +124666,7 @@ static int whereClauseInsert(WhereClause *pWC, Expr *p, u16 wtFlags){
if( pWC->nTerm>=pWC->nSlot ){
WhereTerm *pOld = pWC->a;
sqlite3 *db = pWC->pWInfo->pParse->db;
- pWC->a = sqlite3DbMallocRaw(db, sizeof(pWC->a[0])*pWC->nSlot*2 );
+ pWC->a = sqlite3DbMallocRawNN(db, sizeof(pWC->a[0])*pWC->nSlot*2 );
if( pWC->a==0 ){
if( wtFlags & TERM_DYNAMIC ){
sqlite3ExprDelete(db, p);
@@ -116412,122 +124695,6 @@ static int whereClauseInsert(WhereClause *pWC, Expr *p, u16 wtFlags){
}
/*
-** This routine identifies subexpressions in the WHERE clause where
-** each subexpression is separated by the AND operator or some other
-** operator specified in the op parameter. The WhereClause structure
-** is filled with pointers to subexpressions. For example:
-**
-** WHERE a=='hello' AND coalesce(b,11)<10 AND (c+12!=d OR c==22)
-** \________/ \_______________/ \________________/
-** slot[0] slot[1] slot[2]
-**
-** The original WHERE clause in pExpr is unaltered. All this routine
-** does is make slot[] entries point to substructure within pExpr.
-**
-** In the previous sentence and in the diagram, "slot[]" refers to
-** the WhereClause.a[] array. The slot[] array grows as needed to contain
-** all terms of the WHERE clause.
-*/
-static void whereSplit(WhereClause *pWC, Expr *pExpr, u8 op){
- Expr *pE2 = sqlite3ExprSkipCollate(pExpr);
- pWC->op = op;
- if( pE2==0 ) return;
- if( pE2->op!=op ){
- whereClauseInsert(pWC, pExpr, 0);
- }else{
- whereSplit(pWC, pE2->pLeft, op);
- whereSplit(pWC, pE2->pRight, op);
- }
-}
-
-/*
-** Initialize a WhereMaskSet object
-*/
-#define initMaskSet(P) (P)->n=0
-
-/*
-** Return the bitmask for the given cursor number. Return 0 if
-** iCursor is not in the set.
-*/
-static Bitmask getMask(WhereMaskSet *pMaskSet, int iCursor){
- int i;
- assert( pMaskSet->n<=(int)sizeof(Bitmask)*8 );
- for(i=0; i<pMaskSet->n; i++){
- if( pMaskSet->ix[i]==iCursor ){
- return MASKBIT(i);
- }
- }
- return 0;
-}
-
-/*
-** Create a new mask for cursor iCursor.
-**
-** There is one cursor per table in the FROM clause. The number of
-** tables in the FROM clause is limited by a test early in the
-** sqlite3WhereBegin() routine. So we know that the pMaskSet->ix[]
-** array will never overflow.
-*/
-static void createMask(WhereMaskSet *pMaskSet, int iCursor){
- assert( pMaskSet->n < ArraySize(pMaskSet->ix) );
- pMaskSet->ix[pMaskSet->n++] = iCursor;
-}
-
-/*
-** These routines walk (recursively) an expression tree and generate
-** a bitmask indicating which tables are used in that expression
-** tree.
-*/
-static Bitmask exprListTableUsage(WhereMaskSet*, ExprList*);
-static Bitmask exprSelectTableUsage(WhereMaskSet*, Select*);
-static Bitmask exprTableUsage(WhereMaskSet *pMaskSet, Expr *p){
- Bitmask mask = 0;
- if( p==0 ) return 0;
- if( p->op==TK_COLUMN ){
- mask = getMask(pMaskSet, p->iTable);
- return mask;
- }
- mask = exprTableUsage(pMaskSet, p->pRight);
- mask |= exprTableUsage(pMaskSet, p->pLeft);
- if( ExprHasProperty(p, EP_xIsSelect) ){
- mask |= exprSelectTableUsage(pMaskSet, p->x.pSelect);
- }else{
- mask |= exprListTableUsage(pMaskSet, p->x.pList);
- }
- return mask;
-}
-static Bitmask exprListTableUsage(WhereMaskSet *pMaskSet, ExprList *pList){
- int i;
- Bitmask mask = 0;
- if( pList ){
- for(i=0; i<pList->nExpr; i++){
- mask |= exprTableUsage(pMaskSet, pList->a[i].pExpr);
- }
- }
- return mask;
-}
-static Bitmask exprSelectTableUsage(WhereMaskSet *pMaskSet, Select *pS){
- Bitmask mask = 0;
- while( pS ){
- SrcList *pSrc = pS->pSrc;
- mask |= exprListTableUsage(pMaskSet, pS->pEList);
- mask |= exprListTableUsage(pMaskSet, pS->pGroupBy);
- mask |= exprListTableUsage(pMaskSet, pS->pOrderBy);
- mask |= exprTableUsage(pMaskSet, pS->pWhere);
- mask |= exprTableUsage(pMaskSet, pS->pHaving);
- if( ALWAYS(pSrc!=0) ){
- int i;
- for(i=0; i<pSrc->nSrc; i++){
- mask |= exprSelectTableUsage(pMaskSet, pSrc->a[i].pSelect);
- mask |= exprTableUsage(pMaskSet, pSrc->a[i].pOn);
- }
- }
- pS = pS->pPrior;
- }
- return mask;
-}
-
-/*
** Return TRUE if the given operator is one of the operators that is
** allowed for an indexable WHERE clause term. The allowed operators are
** "=", "<", ">", "<=", ">=", "IN", and "IS NULL"
@@ -116537,7 +124704,7 @@ static int allowedOp(int op){
assert( TK_LT>TK_EQ && TK_LT<TK_GE );
assert( TK_LE>TK_EQ && TK_LE<TK_GE );
assert( TK_GE==TK_EQ+4 );
- return op==TK_IN || (op>=TK_EQ && op<=TK_GE) || op==TK_ISNULL;
+ return op==TK_IN || (op>=TK_EQ && op<=TK_GE) || op==TK_ISNULL || op==TK_IS;
}
/*
@@ -116590,6 +124757,8 @@ static u16 operatorMask(int op){
c = WO_IN;
}else if( op==TK_ISNULL ){
c = WO_ISNULL;
+ }else if( op==TK_IS ){
+ c = WO_IS;
}else{
assert( (WO_EQ<<(op-TK_EQ)) < 0x7fff );
c = (u16)(WO_EQ<<(op-TK_EQ));
@@ -116601,199 +124770,10 @@ static u16 operatorMask(int op){
assert( op!=TK_LE || c==WO_LE );
assert( op!=TK_GT || c==WO_GT );
assert( op!=TK_GE || c==WO_GE );
+ assert( op!=TK_IS || c==WO_IS );
return c;
}
-/*
-** Advance to the next WhereTerm that matches according to the criteria
-** established when the pScan object was initialized by whereScanInit().
-** Return NULL if there are no more matching WhereTerms.
-*/
-static WhereTerm *whereScanNext(WhereScan *pScan){
- int iCur; /* The cursor on the LHS of the term */
- int iColumn; /* The column on the LHS of the term. -1 for IPK */
- Expr *pX; /* An expression being tested */
- WhereClause *pWC; /* Shorthand for pScan->pWC */
- WhereTerm *pTerm; /* The term being tested */
- int k = pScan->k; /* Where to start scanning */
-
- while( pScan->iEquiv<=pScan->nEquiv ){
- iCur = pScan->aEquiv[pScan->iEquiv-2];
- iColumn = pScan->aEquiv[pScan->iEquiv-1];
- while( (pWC = pScan->pWC)!=0 ){
- for(pTerm=pWC->a+k; k<pWC->nTerm; k++, pTerm++){
- if( pTerm->leftCursor==iCur
- && pTerm->u.leftColumn==iColumn
- && (pScan->iEquiv<=2 || !ExprHasProperty(pTerm->pExpr, EP_FromJoin))
- ){
- if( (pTerm->eOperator & WO_EQUIV)!=0
- && pScan->nEquiv<ArraySize(pScan->aEquiv)
- ){
- int j;
- pX = sqlite3ExprSkipCollate(pTerm->pExpr->pRight);
- assert( pX->op==TK_COLUMN );
- for(j=0; j<pScan->nEquiv; j+=2){
- if( pScan->aEquiv[j]==pX->iTable
- && pScan->aEquiv[j+1]==pX->iColumn ){
- break;
- }
- }
- if( j==pScan->nEquiv ){
- pScan->aEquiv[j] = pX->iTable;
- pScan->aEquiv[j+1] = pX->iColumn;
- pScan->nEquiv += 2;
- }
- }
- if( (pTerm->eOperator & pScan->opMask)!=0 ){
- /* Verify the affinity and collating sequence match */
- if( pScan->zCollName && (pTerm->eOperator & WO_ISNULL)==0 ){
- CollSeq *pColl;
- Parse *pParse = pWC->pWInfo->pParse;
- pX = pTerm->pExpr;
- if( !sqlite3IndexAffinityOk(pX, pScan->idxaff) ){
- continue;
- }
- assert(pX->pLeft);
- pColl = sqlite3BinaryCompareCollSeq(pParse,
- pX->pLeft, pX->pRight);
- if( pColl==0 ) pColl = pParse->db->pDfltColl;
- if( sqlite3StrICmp(pColl->zName, pScan->zCollName) ){
- continue;
- }
- }
- if( (pTerm->eOperator & WO_EQ)!=0
- && (pX = pTerm->pExpr->pRight)->op==TK_COLUMN
- && pX->iTable==pScan->aEquiv[0]
- && pX->iColumn==pScan->aEquiv[1]
- ){
- continue;
- }
- pScan->k = k+1;
- return pTerm;
- }
- }
- }
- pScan->pWC = pScan->pWC->pOuter;
- k = 0;
- }
- pScan->pWC = pScan->pOrigWC;
- k = 0;
- pScan->iEquiv += 2;
- }
- return 0;
-}
-
-/*
-** Initialize a WHERE clause scanner object. Return a pointer to the
-** first match. Return NULL if there are no matches.
-**
-** The scanner will be searching the WHERE clause pWC. It will look
-** for terms of the form "X <op> <expr>" where X is column iColumn of table
-** iCur. The <op> must be one of the operators described by opMask.
-**
-** If the search is for X and the WHERE clause contains terms of the
-** form X=Y then this routine might also return terms of the form
-** "Y <op> <expr>". The number of levels of transitivity is limited,
-** but is enough to handle most commonly occurring SQL statements.
-**
-** If X is not the INTEGER PRIMARY KEY then X must be compatible with
-** index pIdx.
-*/
-static WhereTerm *whereScanInit(
- WhereScan *pScan, /* The WhereScan object being initialized */
- WhereClause *pWC, /* The WHERE clause to be scanned */
- int iCur, /* Cursor to scan for */
- int iColumn, /* Column to scan for */
- u32 opMask, /* Operator(s) to scan for */
- Index *pIdx /* Must be compatible with this index */
-){
- int j;
-
- /* memset(pScan, 0, sizeof(*pScan)); */
- pScan->pOrigWC = pWC;
- pScan->pWC = pWC;
- if( pIdx && iColumn>=0 ){
- pScan->idxaff = pIdx->pTable->aCol[iColumn].affinity;
- for(j=0; pIdx->aiColumn[j]!=iColumn; j++){
- if( NEVER(j>pIdx->nColumn) ) return 0;
- }
- pScan->zCollName = pIdx->azColl[j];
- }else{
- pScan->idxaff = 0;
- pScan->zCollName = 0;
- }
- pScan->opMask = opMask;
- pScan->k = 0;
- pScan->aEquiv[0] = iCur;
- pScan->aEquiv[1] = iColumn;
- pScan->nEquiv = 2;
- pScan->iEquiv = 2;
- return whereScanNext(pScan);
-}
-
-/*
-** Search for a term in the WHERE clause that is of the form "X <op> <expr>"
-** where X is a reference to the iColumn of table iCur and <op> is one of
-** the WO_xx operator codes specified by the op parameter.
-** Return a pointer to the term. Return 0 if not found.
-**
-** The term returned might by Y=<expr> if there is another constraint in
-** the WHERE clause that specifies that X=Y. Any such constraints will be
-** identified by the WO_EQUIV bit in the pTerm->eOperator field. The
-** aEquiv[] array holds X and all its equivalents, with each SQL variable
-** taking up two slots in aEquiv[]. The first slot is for the cursor number
-** and the second is for the column number. There are 22 slots in aEquiv[]
-** so that means we can look for X plus up to 10 other equivalent values.
-** Hence a search for X will return <expr> if X=A1 and A1=A2 and A2=A3
-** and ... and A9=A10 and A10=<expr>.
-**
-** If there are multiple terms in the WHERE clause of the form "X <op> <expr>"
-** then try for the one with no dependencies on <expr> - in other words where
-** <expr> is a constant expression of some kind. Only return entries of
-** the form "X <op> Y" where Y is a column in another table if no terms of
-** the form "X <op> <const-expr>" exist. If no terms with a constant RHS
-** exist, try to return a term that does not use WO_EQUIV.
-*/
-static WhereTerm *findTerm(
- WhereClause *pWC, /* The WHERE clause to be searched */
- int iCur, /* Cursor number of LHS */
- int iColumn, /* Column number of LHS */
- Bitmask notReady, /* RHS must not overlap with this mask */
- u32 op, /* Mask of WO_xx values describing operator */
- Index *pIdx /* Must be compatible with this index, if not NULL */
-){
- WhereTerm *pResult = 0;
- WhereTerm *p;
- WhereScan scan;
-
- p = whereScanInit(&scan, pWC, iCur, iColumn, op, pIdx);
- while( p ){
- if( (p->prereqRight & notReady)==0 ){
- if( p->prereqRight==0 && (p->eOperator&WO_EQ)!=0 ){
- return p;
- }
- if( pResult==0 ) pResult = p;
- }
- p = whereScanNext(&scan);
- }
- return pResult;
-}
-
-/* Forward reference */
-static void exprAnalyze(SrcList*, WhereClause*, int);
-
-/*
-** Call exprAnalyze on all terms in a WHERE clause.
-*/
-static void exprAnalyzeAll(
- SrcList *pTabList, /* the FROM clause */
- WhereClause *pWC /* the WHERE clause to be analyzed */
-){
- int i;
- for(i=pWC->nTerm-1; i>=0; i--){
- exprAnalyze(pTabList, pWC, i);
- }
-}
#ifndef SQLITE_OMIT_LIKE_OPTIMIZATION
/*
@@ -116804,7 +124784,7 @@ static void exprAnalyzeAll(
** In order for the operator to be optimizible, the RHS must be a string
** 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
+** that virtual tables cannot participate in the LIKE optimization.) The
** collating sequence for the column on the LHS must be appropriate for
** the operator.
*/
@@ -116824,6 +124804,7 @@ static int isLikeOrGlob(
sqlite3 *db = pParse->db; /* Database connection */
sqlite3_value *pVal = 0;
int op; /* Opcode of pRight */
+ int rc; /* Result code to return */
if( !sqlite3IsLikeFunction(db, pExpr, pnoCase, wc) ){
return 0;
@@ -116848,7 +124829,7 @@ static int isLikeOrGlob(
if( op==TK_VARIABLE ){
Vdbe *pReprepare = pParse->pReprepare;
int iCol = pRight->iColumn;
- pVal = sqlite3VdbeGetBoundValue(pReprepare, iCol, SQLITE_AFF_NONE);
+ pVal = sqlite3VdbeGetBoundValue(pReprepare, iCol, SQLITE_AFF_BLOB);
if( pVal && sqlite3_value_type(pVal)==SQLITE_TEXT ){
z = (char *)sqlite3_value_text(pVal);
}
@@ -116889,8 +124870,9 @@ static int isLikeOrGlob(
}
}
+ rc = (z!=0);
sqlite3ValueFree(pVal);
- return (z!=0);
+ return rc;
}
#endif /* SQLITE_OMIT_LIKE_OPTIMIZATION */
@@ -116899,29 +124881,48 @@ static int isLikeOrGlob(
/*
** Check to see if the given expression is of the form
**
-** column MATCH expr
+** column OP expr
+**
+** where OP is one of MATCH, GLOB, LIKE or REGEXP and "column" is a
+** column of a virtual table.
**
** If it is then return TRUE. If not, return FALSE.
*/
static int isMatchOfColumn(
- Expr *pExpr /* Test this expression */
-){
+ Expr *pExpr, /* Test this expression */
+ unsigned char *peOp2 /* OUT: 0 for MATCH, or else an op2 value */
+){
+ struct Op2 {
+ const char *zOp;
+ unsigned char eOp2;
+ } aOp[] = {
+ { "match", SQLITE_INDEX_CONSTRAINT_MATCH },
+ { "glob", SQLITE_INDEX_CONSTRAINT_GLOB },
+ { "like", SQLITE_INDEX_CONSTRAINT_LIKE },
+ { "regexp", SQLITE_INDEX_CONSTRAINT_REGEXP }
+ };
ExprList *pList;
+ Expr *pCol; /* Column reference */
+ int i;
if( pExpr->op!=TK_FUNCTION ){
return 0;
}
- if( sqlite3StrICmp(pExpr->u.zToken,"match")!=0 ){
- return 0;
- }
pList = pExpr->x.pList;
- if( pList->nExpr!=2 ){
+ if( pList==0 || pList->nExpr!=2 ){
return 0;
}
- if( pList->a[1].pExpr->op != TK_COLUMN ){
+ pCol = pList->a[1].pExpr;
+ if( pCol->op!=TK_COLUMN || !IsVirtual(pCol->pTab) ){
return 0;
}
- return 1;
+ for(i=0; i<ArraySize(aOp); i++){
+ if( sqlite3StrICmp(pExpr->u.zToken, aOp[i].zOp)==0 ){
+ *peOp2 = aOp[i].eOp2;
+ return 1;
+ }
+ }
+ return 0;
}
#endif /* SQLITE_OMIT_VIRTUALTABLE */
@@ -117059,7 +125060,7 @@ static void whereCombineDisjuncts(
**
** CASE 2:
**
-** If there are exactly two disjuncts one side has x>A and the other side
+** If there are exactly two disjuncts and 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:
**
@@ -117088,22 +125089,22 @@ static void whereCombineDisjuncts(
** is decided elsewhere. This analysis only looks at whether subterms
** appropriate for indexing exist.
**
-** All examples A through E above satisfy case 2. But if a term
+** All examples A through E above satisfy case 3. But if a term
** also satisfies case 1 (such as B) we know that the optimizer will
-** always prefer case 1, so in that case we pretend that case 2 is not
+** always prefer case 1, so in that case we pretend that case 3 is not
** satisfied.
**
** It might be the case that multiple tables are indexable. For example,
** (E) above is indexable on tables P, Q, and R.
**
-** Terms that satisfy case 2 are candidates for lookup by using
+** Terms that satisfy case 3 are candidates for lookup by using
** separate indices to find rowids for each subterm and composing
** the union of all rowids using a RowSet object. This is similar
** to "bitmap indices" in other database engines.
**
** OTHERWISE:
**
-** If neither case 1 nor case 2 apply, then leave the eOperator set to
+** If none of cases 1, 2, or 3 apply, then leave the eOperator set to
** zero. This term is not useful for search.
*/
static void exprAnalyzeOrTerm(
@@ -117134,14 +125135,15 @@ static void exprAnalyzeOrTerm(
if( pOrInfo==0 ) return;
pTerm->wtFlags |= TERM_ORINFO;
pOrWc = &pOrInfo->wc;
- whereClauseInit(pOrWc, pWInfo);
- whereSplit(pOrWc, pExpr, TK_OR);
- exprAnalyzeAll(pSrc, pOrWc);
+ memset(pOrWc->aStatic, 0, sizeof(pOrWc->aStatic));
+ sqlite3WhereClauseInit(pOrWc, pWInfo);
+ sqlite3WhereSplit(pOrWc, pExpr, TK_OR);
+ sqlite3WhereExprAnalyze(pSrc, pOrWc);
if( db->mallocFailed ) return;
assert( pOrWc->nTerm>=2 );
/*
- ** Compute the set of tables that might satisfy cases 1 or 2.
+ ** Compute the set of tables that might satisfy cases 1 or 3.
*/
indexable = ~(Bitmask)0;
chngToIN = ~(Bitmask)0;
@@ -117150,7 +125152,7 @@ static void exprAnalyzeOrTerm(
WhereAndInfo *pAndInfo;
assert( (pOrTerm->wtFlags & (TERM_ANDINFO|TERM_ORINFO))==0 );
chngToIN = 0;
- pAndInfo = sqlite3DbMallocRaw(db, sizeof(*pAndInfo));
+ pAndInfo = sqlite3DbMallocRawNN(db, sizeof(*pAndInfo));
if( pAndInfo ){
WhereClause *pAndWC;
WhereTerm *pAndTerm;
@@ -117160,16 +125162,16 @@ static void exprAnalyzeOrTerm(
pOrTerm->wtFlags |= TERM_ANDINFO;
pOrTerm->eOperator = WO_AND;
pAndWC = &pAndInfo->wc;
- whereClauseInit(pAndWC, pWC->pWInfo);
- whereSplit(pAndWC, pOrTerm->pExpr, TK_AND);
- exprAnalyzeAll(pSrc, pAndWC);
+ memset(pAndWC->aStatic, 0, sizeof(pAndWC->aStatic));
+ sqlite3WhereClauseInit(pAndWC, pWC->pWInfo);
+ sqlite3WhereSplit(pAndWC, pOrTerm->pExpr, TK_AND);
+ sqlite3WhereExprAnalyze(pSrc, pAndWC);
pAndWC->pOuter = pWC;
- testcase( db->mallocFailed );
if( !db->mallocFailed ){
for(j=0, pAndTerm=pAndWC->a; j<pAndWC->nTerm; j++, pAndTerm++){
assert( pAndTerm->pExpr );
if( allowedOp(pAndTerm->pExpr->op) ){
- b |= getMask(&pWInfo->sMaskSet, pAndTerm->leftCursor);
+ b |= sqlite3WhereGetMask(&pWInfo->sMaskSet, pAndTerm->leftCursor);
}
}
}
@@ -117180,10 +125182,10 @@ static void exprAnalyzeOrTerm(
** corresponding TERM_VIRTUAL term */
}else{
Bitmask b;
- b = getMask(&pWInfo->sMaskSet, pOrTerm->leftCursor);
+ b = sqlite3WhereGetMask(&pWInfo->sMaskSet, pOrTerm->leftCursor);
if( pOrTerm->wtFlags & TERM_VIRTUAL ){
WhereTerm *pOther = &pOrWc->a[pOrTerm->iParent];
- b |= getMask(&pWInfo->sMaskSet, pOther->leftCursor);
+ b |= sqlite3WhereGetMask(&pWInfo->sMaskSet, pOther->leftCursor);
}
indexable &= b;
if( (pOrTerm->eOperator & WO_EQ)==0 ){
@@ -117259,7 +125261,8 @@ static void exprAnalyzeOrTerm(
assert( j==1 );
continue;
}
- if( (chngToIN & getMask(&pWInfo->sMaskSet, pOrTerm->leftCursor))==0 ){
+ if( (chngToIN & sqlite3WhereGetMask(&pWInfo->sMaskSet,
+ pOrTerm->leftCursor))==0 ){
/* This term must be of the form t1.a==t2.b where t2 is in the
** chngToIN set but t1 is not. This term will be either preceded
** or follwed by an inverted copy (t2.b==t1.a). Skip this term
@@ -117278,7 +125281,7 @@ static void exprAnalyzeOrTerm(
** on the second iteration */
assert( j==1 );
assert( IsPowerOfTwo(chngToIN) );
- assert( chngToIN==getMask(&pWInfo->sMaskSet, iCursor) );
+ assert( chngToIN==sqlite3WhereGetMask(&pWInfo->sMaskSet, iCursor) );
break;
}
testcase( j==1 );
@@ -117351,6 +125354,117 @@ static void exprAnalyzeOrTerm(
#endif /* !SQLITE_OMIT_OR_OPTIMIZATION && !SQLITE_OMIT_SUBQUERY */
/*
+** We already know that pExpr is a binary operator where both operands are
+** column references. This routine checks to see if pExpr is an equivalence
+** relation:
+** 1. The SQLITE_Transitive optimization must be enabled
+** 2. Must be either an == or an IS operator
+** 3. Not originating in the ON clause of an OUTER JOIN
+** 4. The affinities of A and B must be compatible
+** 5a. Both operands use the same collating sequence OR
+** 5b. The overall collating sequence is BINARY
+** If this routine returns TRUE, that means that the RHS can be substituted
+** for the LHS anyplace else in the WHERE clause where the LHS column occurs.
+** This is an optimization. No harm comes from returning 0. But if 1 is
+** returned when it should not be, then incorrect answers might result.
+*/
+static int termIsEquivalence(Parse *pParse, Expr *pExpr){
+ char aff1, aff2;
+ CollSeq *pColl;
+ const char *zColl1, *zColl2;
+ if( !OptimizationEnabled(pParse->db, SQLITE_Transitive) ) return 0;
+ if( pExpr->op!=TK_EQ && pExpr->op!=TK_IS ) return 0;
+ if( ExprHasProperty(pExpr, EP_FromJoin) ) return 0;
+ aff1 = sqlite3ExprAffinity(pExpr->pLeft);
+ aff2 = sqlite3ExprAffinity(pExpr->pRight);
+ if( aff1!=aff2
+ && (!sqlite3IsNumericAffinity(aff1) || !sqlite3IsNumericAffinity(aff2))
+ ){
+ return 0;
+ }
+ pColl = sqlite3BinaryCompareCollSeq(pParse, pExpr->pLeft, pExpr->pRight);
+ if( pColl==0 || sqlite3StrICmp(pColl->zName, "BINARY")==0 ) return 1;
+ pColl = sqlite3ExprCollSeq(pParse, pExpr->pLeft);
+ /* Since pLeft and pRight are both a column references, their collating
+ ** sequence should always be defined. */
+ zColl1 = ALWAYS(pColl) ? pColl->zName : 0;
+ pColl = sqlite3ExprCollSeq(pParse, pExpr->pRight);
+ zColl2 = ALWAYS(pColl) ? pColl->zName : 0;
+ return sqlite3StrICmp(zColl1, zColl2)==0;
+}
+
+/*
+** Recursively walk the expressions of a SELECT statement and generate
+** a bitmask indicating which tables are used in that expression
+** tree.
+*/
+static Bitmask exprSelectUsage(WhereMaskSet *pMaskSet, Select *pS){
+ Bitmask mask = 0;
+ while( pS ){
+ SrcList *pSrc = pS->pSrc;
+ mask |= sqlite3WhereExprListUsage(pMaskSet, pS->pEList);
+ mask |= sqlite3WhereExprListUsage(pMaskSet, pS->pGroupBy);
+ mask |= sqlite3WhereExprListUsage(pMaskSet, pS->pOrderBy);
+ mask |= sqlite3WhereExprUsage(pMaskSet, pS->pWhere);
+ mask |= sqlite3WhereExprUsage(pMaskSet, pS->pHaving);
+ if( ALWAYS(pSrc!=0) ){
+ int i;
+ for(i=0; i<pSrc->nSrc; i++){
+ mask |= exprSelectUsage(pMaskSet, pSrc->a[i].pSelect);
+ mask |= sqlite3WhereExprUsage(pMaskSet, pSrc->a[i].pOn);
+ }
+ }
+ pS = pS->pPrior;
+ }
+ return mask;
+}
+
+/*
+** Expression pExpr is one operand of a comparison operator that might
+** be useful for indexing. This routine checks to see if pExpr appears
+** in any index. Return TRUE (1) if pExpr is an indexed term and return
+** FALSE (0) if not. If TRUE is returned, also set *piCur to the cursor
+** number of the table that is indexed and *piColumn to the column number
+** of the column that is indexed, or -2 if an expression is being indexed.
+**
+** If pExpr is a TK_COLUMN column reference, then this routine always returns
+** true even if that particular column is not indexed, because the column
+** might be added to an automatic index later.
+*/
+static int exprMightBeIndexed(
+ SrcList *pFrom, /* The FROM clause */
+ Bitmask mPrereq, /* Bitmask of FROM clause terms referenced by pExpr */
+ Expr *pExpr, /* An operand of a comparison operator */
+ int *piCur, /* Write the referenced table cursor number here */
+ int *piColumn /* Write the referenced table column number here */
+){
+ Index *pIdx;
+ int i;
+ int iCur;
+ if( pExpr->op==TK_COLUMN ){
+ *piCur = pExpr->iTable;
+ *piColumn = pExpr->iColumn;
+ return 1;
+ }
+ if( mPrereq==0 ) return 0; /* No table references */
+ if( (mPrereq&(mPrereq-1))!=0 ) return 0; /* Refs more than one table */
+ for(i=0; mPrereq>1; i++, mPrereq>>=1){}
+ iCur = pFrom->a[i].iCursor;
+ for(pIdx=pFrom->a[i].pTab->pIndex; pIdx; pIdx=pIdx->pNext){
+ if( pIdx->aColExpr==0 ) continue;
+ for(i=0; i<pIdx->nKeyCol; i++){
+ if( pIdx->aiColumn[i]!=(-2) ) continue;
+ if( sqlite3ExprCompare(pExpr, pIdx->aColExpr->a[i].pExpr, iCur)==0 ){
+ *piCur = iCur;
+ *piColumn = -2;
+ return 1;
+ }
+ }
+ }
+ return 0;
+}
+
+/*
** The input to this routine is an WhereTerm structure with only the
** "pExpr" field filled in. The job of this routine is to analyze the
** subexpression and populate all the other fields of the WhereTerm
@@ -117386,6 +125500,7 @@ static void exprAnalyze(
int op; /* Top-level operator. pExpr->op */
Parse *pParse = pWInfo->pParse; /* Parsing context */
sqlite3 *db = pParse->db; /* Database connection */
+ unsigned char eOp2; /* op2 value for LIKE/REGEXP/GLOB */
if( db->mallocFailed ){
return;
@@ -117394,23 +125509,23 @@ static void exprAnalyze(
pMaskSet = &pWInfo->sMaskSet;
pExpr = pTerm->pExpr;
assert( pExpr->op!=TK_AS && pExpr->op!=TK_COLLATE );
- prereqLeft = exprTableUsage(pMaskSet, pExpr->pLeft);
+ prereqLeft = sqlite3WhereExprUsage(pMaskSet, pExpr->pLeft);
op = pExpr->op;
if( op==TK_IN ){
assert( pExpr->pRight==0 );
if( ExprHasProperty(pExpr, EP_xIsSelect) ){
- pTerm->prereqRight = exprSelectTableUsage(pMaskSet, pExpr->x.pSelect);
+ pTerm->prereqRight = exprSelectUsage(pMaskSet, pExpr->x.pSelect);
}else{
- pTerm->prereqRight = exprListTableUsage(pMaskSet, pExpr->x.pList);
+ pTerm->prereqRight = sqlite3WhereExprListUsage(pMaskSet, pExpr->x.pList);
}
}else if( op==TK_ISNULL ){
pTerm->prereqRight = 0;
}else{
- pTerm->prereqRight = exprTableUsage(pMaskSet, pExpr->pRight);
+ pTerm->prereqRight = sqlite3WhereExprUsage(pMaskSet, pExpr->pRight);
}
- prereqAll = exprTableUsage(pMaskSet, pExpr);
+ prereqAll = sqlite3WhereExprUsage(pMaskSet, pExpr);
if( ExprHasProperty(pExpr, EP_FromJoin) ){
- Bitmask x = getMask(pMaskSet, pExpr->iRightJoinTable);
+ Bitmask x = sqlite3WhereGetMask(pMaskSet, pExpr->iRightJoinTable);
prereqAll |= x;
extraRight = x-1; /* ON clause terms may not be used with an index
** on left table of a LEFT JOIN. Ticket #3015 */
@@ -117420,15 +125535,19 @@ static void exprAnalyze(
pTerm->iParent = -1;
pTerm->eOperator = 0;
if( allowedOp(op) ){
+ int iCur, iColumn;
Expr *pLeft = sqlite3ExprSkipCollate(pExpr->pLeft);
Expr *pRight = sqlite3ExprSkipCollate(pExpr->pRight);
u16 opMask = (pTerm->prereqRight & prereqLeft)==0 ? WO_ALL : WO_EQUIV;
- if( pLeft->op==TK_COLUMN ){
- pTerm->leftCursor = pLeft->iTable;
- pTerm->u.leftColumn = pLeft->iColumn;
+ if( exprMightBeIndexed(pSrc, prereqLeft, pLeft, &iCur, &iColumn) ){
+ pTerm->leftCursor = iCur;
+ pTerm->u.leftColumn = iColumn;
pTerm->eOperator = operatorMask(op) & opMask;
}
- if( pRight && pRight->op==TK_COLUMN ){
+ if( op==TK_IS ) pTerm->wtFlags |= TERM_IS;
+ if( pRight
+ && exprMightBeIndexed(pSrc, pTerm->prereqRight, pRight, &iCur, &iColumn)
+ ){
WhereTerm *pNew;
Expr *pDup;
u16 eExtraOp = 0; /* Extra bits for pNew->eOperator */
@@ -117443,12 +125562,11 @@ static void exprAnalyze(
if( idxNew==0 ) return;
pNew = &pWC->a[idxNew];
markTermAsChild(pWC, idxNew, idxTerm);
+ if( op==TK_IS ) pNew->wtFlags |= TERM_IS;
pTerm = &pWC->a[idxTerm];
pTerm->wtFlags |= TERM_COPIED;
- if( pExpr->op==TK_EQ
- && !ExprHasProperty(pExpr, EP_FromJoin)
- && OptimizationEnabled(db, SQLITE_Transitive)
- ){
+
+ if( termIsEquivalence(pParse, pDup) ){
pTerm->eOperator |= WO_EQUIV;
eExtraOp = WO_EQUIV;
}
@@ -117457,9 +125575,8 @@ static void exprAnalyze(
pNew = pTerm;
}
exprCommute(pParse, pDup);
- pLeft = sqlite3ExprSkipCollate(pDup->pLeft);
- pNew->leftCursor = pLeft->iTable;
- pNew->u.leftColumn = pLeft->iColumn;
+ pNew->leftCursor = iCur;
+ pNew->u.leftColumn = iColumn;
testcase( (prereqLeft | extraRight) != prereqLeft );
pNew->prereqRight = prereqLeft | extraRight;
pNew->prereqAll = prereqAll;
@@ -117607,7 +125724,7 @@ static void exprAnalyze(
** virtual tables. The native query optimizer does not attempt
** to do anything with MATCH functions.
*/
- if( isMatchOfColumn(pExpr) ){
+ if( isMatchOfColumn(pExpr, &eOp2) ){
int idxNew;
Expr *pRight, *pLeft;
WhereTerm *pNewTerm;
@@ -117615,8 +125732,8 @@ static void exprAnalyze(
pRight = pExpr->x.pList->a[0].pExpr;
pLeft = pExpr->x.pList->a[1].pExpr;
- prereqExpr = exprTableUsage(pMaskSet, pRight);
- prereqColumn = exprTableUsage(pMaskSet, pLeft);
+ prereqExpr = sqlite3WhereExprUsage(pMaskSet, pRight);
+ prereqColumn = sqlite3WhereExprUsage(pMaskSet, pLeft);
if( (prereqExpr & prereqColumn)==0 ){
Expr *pNewExpr;
pNewExpr = sqlite3PExpr(pParse, TK_MATCH,
@@ -117628,6 +125745,7 @@ static void exprAnalyze(
pNewTerm->leftCursor = pLeft->iTable;
pNewTerm->u.leftColumn = pLeft->iColumn;
pNewTerm->eOperator = WO_MATCH;
+ pNewTerm->eMatchOp = eOp2;
markTermAsChild(pWC, idxNew, idxTerm);
pTerm = &pWC->a[idxTerm];
pTerm->wtFlags |= TERM_COPIED;
@@ -117642,10 +125760,7 @@ static void exprAnalyze(
** as "x>NULL" if x is not an INTEGER PRIMARY KEY. So construct a
** virtual term of that form.
**
- ** Note that the virtual term must be tagged with TERM_VNULL. This
- ** TERM_VNULL tag will suppress the not-null check at the beginning
- ** of the loop. Without the TERM_VNULL flag, the not-null check at
- ** the start of the loop will prevent any results from being returned.
+ ** Note that the virtual term must be tagged with TERM_VNULL.
*/
if( pExpr->op==TK_NOTNULL
&& pExpr->pLeft->op==TK_COLUMN
@@ -117683,6 +125798,536 @@ static void exprAnalyze(
pTerm->prereqRight |= extraRight;
}
+/***************************************************************************
+** Routines with file scope above. Interface to the rest of the where.c
+** subsystem follows.
+***************************************************************************/
+
+/*
+** This routine identifies subexpressions in the WHERE clause where
+** each subexpression is separated by the AND operator or some other
+** operator specified in the op parameter. The WhereClause structure
+** is filled with pointers to subexpressions. For example:
+**
+** WHERE a=='hello' AND coalesce(b,11)<10 AND (c+12!=d OR c==22)
+** \________/ \_______________/ \________________/
+** slot[0] slot[1] slot[2]
+**
+** The original WHERE clause in pExpr is unaltered. All this routine
+** does is make slot[] entries point to substructure within pExpr.
+**
+** In the previous sentence and in the diagram, "slot[]" refers to
+** the WhereClause.a[] array. The slot[] array grows as needed to contain
+** all terms of the WHERE clause.
+*/
+SQLITE_PRIVATE void sqlite3WhereSplit(WhereClause *pWC, Expr *pExpr, u8 op){
+ Expr *pE2 = sqlite3ExprSkipCollate(pExpr);
+ pWC->op = op;
+ if( pE2==0 ) return;
+ if( pE2->op!=op ){
+ whereClauseInsert(pWC, pExpr, 0);
+ }else{
+ sqlite3WhereSplit(pWC, pE2->pLeft, op);
+ sqlite3WhereSplit(pWC, pE2->pRight, op);
+ }
+}
+
+/*
+** Initialize a preallocated WhereClause structure.
+*/
+SQLITE_PRIVATE void sqlite3WhereClauseInit(
+ WhereClause *pWC, /* The WhereClause to be initialized */
+ WhereInfo *pWInfo /* The WHERE processing context */
+){
+ pWC->pWInfo = pWInfo;
+ pWC->pOuter = 0;
+ pWC->nTerm = 0;
+ pWC->nSlot = ArraySize(pWC->aStatic);
+ pWC->a = pWC->aStatic;
+}
+
+/*
+** Deallocate a WhereClause structure. The WhereClause structure
+** itself is not freed. This routine is the inverse of
+** sqlite3WhereClauseInit().
+*/
+SQLITE_PRIVATE void sqlite3WhereClauseClear(WhereClause *pWC){
+ int i;
+ WhereTerm *a;
+ sqlite3 *db = pWC->pWInfo->pParse->db;
+ for(i=pWC->nTerm-1, a=pWC->a; i>=0; i--, a++){
+ if( a->wtFlags & TERM_DYNAMIC ){
+ sqlite3ExprDelete(db, a->pExpr);
+ }
+ if( a->wtFlags & TERM_ORINFO ){
+ whereOrInfoDelete(db, a->u.pOrInfo);
+ }else if( a->wtFlags & TERM_ANDINFO ){
+ whereAndInfoDelete(db, a->u.pAndInfo);
+ }
+ }
+ if( pWC->a!=pWC->aStatic ){
+ sqlite3DbFree(db, pWC->a);
+ }
+}
+
+
+/*
+** These routines walk (recursively) an expression tree and generate
+** a bitmask indicating which tables are used in that expression
+** tree.
+*/
+SQLITE_PRIVATE Bitmask sqlite3WhereExprUsage(WhereMaskSet *pMaskSet, Expr *p){
+ Bitmask mask = 0;
+ if( p==0 ) return 0;
+ if( p->op==TK_COLUMN ){
+ mask = sqlite3WhereGetMask(pMaskSet, p->iTable);
+ return mask;
+ }
+ mask = sqlite3WhereExprUsage(pMaskSet, p->pRight);
+ if( p->pLeft ) mask |= sqlite3WhereExprUsage(pMaskSet, p->pLeft);
+ if( ExprHasProperty(p, EP_xIsSelect) ){
+ mask |= exprSelectUsage(pMaskSet, p->x.pSelect);
+ }else if( p->x.pList ){
+ mask |= sqlite3WhereExprListUsage(pMaskSet, p->x.pList);
+ }
+ return mask;
+}
+SQLITE_PRIVATE Bitmask sqlite3WhereExprListUsage(WhereMaskSet *pMaskSet, ExprList *pList){
+ int i;
+ Bitmask mask = 0;
+ if( pList ){
+ for(i=0; i<pList->nExpr; i++){
+ mask |= sqlite3WhereExprUsage(pMaskSet, pList->a[i].pExpr);
+ }
+ }
+ return mask;
+}
+
+
+/*
+** Call exprAnalyze on all terms in a WHERE clause.
+**
+** Note that exprAnalyze() might add new virtual terms onto the
+** end of the WHERE clause. We do not want to analyze these new
+** virtual terms, so start analyzing at the end and work forward
+** so that the added virtual terms are never processed.
+*/
+SQLITE_PRIVATE void sqlite3WhereExprAnalyze(
+ SrcList *pTabList, /* the FROM clause */
+ WhereClause *pWC /* the WHERE clause to be analyzed */
+){
+ int i;
+ for(i=pWC->nTerm-1; i>=0; i--){
+ exprAnalyze(pTabList, pWC, i);
+ }
+}
+
+/*
+** For table-valued-functions, transform the function arguments into
+** new WHERE clause terms.
+**
+** Each function argument translates into an equality constraint against
+** a HIDDEN column in the table.
+*/
+SQLITE_PRIVATE void sqlite3WhereTabFuncArgs(
+ Parse *pParse, /* Parsing context */
+ struct SrcList_item *pItem, /* The FROM clause term to process */
+ WhereClause *pWC /* Xfer function arguments to here */
+){
+ Table *pTab;
+ int j, k;
+ ExprList *pArgs;
+ Expr *pColRef;
+ Expr *pTerm;
+ if( pItem->fg.isTabFunc==0 ) return;
+ pTab = pItem->pTab;
+ assert( pTab!=0 );
+ pArgs = pItem->u1.pFuncArg;
+ if( pArgs==0 ) return;
+ for(j=k=0; j<pArgs->nExpr; j++){
+ while( k<pTab->nCol && (pTab->aCol[k].colFlags & COLFLAG_HIDDEN)==0 ){k++;}
+ if( k>=pTab->nCol ){
+ sqlite3ErrorMsg(pParse, "too many arguments on %s() - max %d",
+ pTab->zName, j);
+ return;
+ }
+ pColRef = sqlite3PExpr(pParse, TK_COLUMN, 0, 0, 0);
+ if( pColRef==0 ) return;
+ pColRef->iTable = pItem->iCursor;
+ pColRef->iColumn = k++;
+ pColRef->pTab = pTab;
+ pTerm = sqlite3PExpr(pParse, TK_EQ, pColRef,
+ sqlite3ExprDup(pParse->db, pArgs->a[j].pExpr, 0), 0);
+ whereClauseInsert(pWC, pTerm, TERM_DYNAMIC);
+ }
+}
+
+/************** End of whereexpr.c *******************************************/
+/************** Begin file where.c *******************************************/
+/*
+** 2001 September 15
+**
+** 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 module contains C code that generates VDBE code used to process
+** the WHERE clause of SQL statements. This module is responsible for
+** generating the code that loops through a table looking for applicable
+** rows. Indices are selected and used to speed the search when doing
+** so is applicable. Because this module is responsible for selecting
+** indices, you might also think of this module as the "query optimizer".
+*/
+/* #include "sqliteInt.h" */
+/* #include "whereInt.h" */
+
+/* Forward declaration of methods */
+static int whereLoopResize(sqlite3*, WhereLoop*, int);
+
+/* Test variable that can be set to enable WHERE tracing */
+#if defined(SQLITE_TEST) || defined(SQLITE_DEBUG)
+/***/ int sqlite3WhereTrace = 0;
+#endif
+
+
+/*
+** Return the estimated number of output rows from a WHERE clause
+*/
+SQLITE_PRIVATE LogEst sqlite3WhereOutputRowCount(WhereInfo *pWInfo){
+ return pWInfo->nRowOut;
+}
+
+/*
+** Return one of the WHERE_DISTINCT_xxxxx values to indicate how this
+** WHERE clause returns outputs for DISTINCT processing.
+*/
+SQLITE_PRIVATE int sqlite3WhereIsDistinct(WhereInfo *pWInfo){
+ return pWInfo->eDistinct;
+}
+
+/*
+** Return TRUE if the WHERE clause returns rows in ORDER BY order.
+** Return FALSE if the output needs to be sorted.
+*/
+SQLITE_PRIVATE int sqlite3WhereIsOrdered(WhereInfo *pWInfo){
+ return pWInfo->nOBSat;
+}
+
+/*
+** Return the VDBE address or label to jump to in order to continue
+** immediately with the next row of a WHERE clause.
+*/
+SQLITE_PRIVATE int sqlite3WhereContinueLabel(WhereInfo *pWInfo){
+ assert( pWInfo->iContinue!=0 );
+ return pWInfo->iContinue;
+}
+
+/*
+** Return the VDBE address or label to jump to in order to break
+** out of a WHERE loop.
+*/
+SQLITE_PRIVATE int sqlite3WhereBreakLabel(WhereInfo *pWInfo){
+ return pWInfo->iBreak;
+}
+
+/*
+** Return ONEPASS_OFF (0) if an UPDATE or DELETE statement is unable to
+** operate directly on the rowis returned by a WHERE clause. Return
+** ONEPASS_SINGLE (1) if the statement can operation directly because only
+** a single row is to be changed. Return ONEPASS_MULTI (2) if the one-pass
+** optimization can be used on multiple
+**
+** If the ONEPASS optimization is used (if this routine returns true)
+** then also write the indices of open cursors used by ONEPASS
+** into aiCur[0] and aiCur[1]. iaCur[0] gets the cursor of the data
+** table and iaCur[1] gets the cursor used by an auxiliary index.
+** Either value may be -1, indicating that cursor is not used.
+** Any cursors returned will have been opened for writing.
+**
+** aiCur[0] and aiCur[1] both get -1 if the where-clause logic is
+** unable to use the ONEPASS optimization.
+*/
+SQLITE_PRIVATE int sqlite3WhereOkOnePass(WhereInfo *pWInfo, int *aiCur){
+ memcpy(aiCur, pWInfo->aiCurOnePass, sizeof(int)*2);
+#ifdef WHERETRACE_ENABLED
+ if( sqlite3WhereTrace && pWInfo->eOnePass!=ONEPASS_OFF ){
+ sqlite3DebugPrintf("%s cursors: %d %d\n",
+ pWInfo->eOnePass==ONEPASS_SINGLE ? "ONEPASS_SINGLE" : "ONEPASS_MULTI",
+ aiCur[0], aiCur[1]);
+ }
+#endif
+ return pWInfo->eOnePass;
+}
+
+/*
+** Move the content of pSrc into pDest
+*/
+static void whereOrMove(WhereOrSet *pDest, WhereOrSet *pSrc){
+ pDest->n = pSrc->n;
+ memcpy(pDest->a, pSrc->a, pDest->n*sizeof(pDest->a[0]));
+}
+
+/*
+** Try to insert a new prerequisite/cost entry into the WhereOrSet pSet.
+**
+** The new entry might overwrite an existing entry, or it might be
+** appended, or it might be discarded. Do whatever is the right thing
+** so that pSet keeps the N_OR_COST best entries seen so far.
+*/
+static int whereOrInsert(
+ WhereOrSet *pSet, /* The WhereOrSet to be updated */
+ Bitmask prereq, /* Prerequisites of the new entry */
+ LogEst rRun, /* Run-cost of the new entry */
+ LogEst nOut /* Number of outputs for the new entry */
+){
+ u16 i;
+ WhereOrCost *p;
+ for(i=pSet->n, p=pSet->a; i>0; i--, p++){
+ if( rRun<=p->rRun && (prereq & p->prereq)==prereq ){
+ goto whereOrInsert_done;
+ }
+ if( p->rRun<=rRun && (p->prereq & prereq)==p->prereq ){
+ return 0;
+ }
+ }
+ if( pSet->n<N_OR_COST ){
+ p = &pSet->a[pSet->n++];
+ p->nOut = nOut;
+ }else{
+ p = pSet->a;
+ for(i=1; i<pSet->n; i++){
+ if( p->rRun>pSet->a[i].rRun ) p = pSet->a + i;
+ }
+ if( p->rRun<=rRun ) return 0;
+ }
+whereOrInsert_done:
+ p->prereq = prereq;
+ p->rRun = rRun;
+ if( p->nOut>nOut ) p->nOut = nOut;
+ return 1;
+}
+
+/*
+** Return the bitmask for the given cursor number. Return 0 if
+** iCursor is not in the set.
+*/
+SQLITE_PRIVATE Bitmask sqlite3WhereGetMask(WhereMaskSet *pMaskSet, int iCursor){
+ int i;
+ assert( pMaskSet->n<=(int)sizeof(Bitmask)*8 );
+ for(i=0; i<pMaskSet->n; i++){
+ if( pMaskSet->ix[i]==iCursor ){
+ return MASKBIT(i);
+ }
+ }
+ return 0;
+}
+
+/*
+** Create a new mask for cursor iCursor.
+**
+** There is one cursor per table in the FROM clause. The number of
+** tables in the FROM clause is limited by a test early in the
+** sqlite3WhereBegin() routine. So we know that the pMaskSet->ix[]
+** array will never overflow.
+*/
+static void createMask(WhereMaskSet *pMaskSet, int iCursor){
+ assert( pMaskSet->n < ArraySize(pMaskSet->ix) );
+ pMaskSet->ix[pMaskSet->n++] = iCursor;
+}
+
+/*
+** Advance to the next WhereTerm that matches according to the criteria
+** established when the pScan object was initialized by whereScanInit().
+** Return NULL if there are no more matching WhereTerms.
+*/
+static WhereTerm *whereScanNext(WhereScan *pScan){
+ int iCur; /* The cursor on the LHS of the term */
+ i16 iColumn; /* The column on the LHS of the term. -1 for IPK */
+ Expr *pX; /* An expression being tested */
+ WhereClause *pWC; /* Shorthand for pScan->pWC */
+ WhereTerm *pTerm; /* The term being tested */
+ int k = pScan->k; /* Where to start scanning */
+
+ while( pScan->iEquiv<=pScan->nEquiv ){
+ iCur = pScan->aiCur[pScan->iEquiv-1];
+ iColumn = pScan->aiColumn[pScan->iEquiv-1];
+ if( iColumn==XN_EXPR && pScan->pIdxExpr==0 ) return 0;
+ while( (pWC = pScan->pWC)!=0 ){
+ for(pTerm=pWC->a+k; k<pWC->nTerm; k++, pTerm++){
+ if( pTerm->leftCursor==iCur
+ && pTerm->u.leftColumn==iColumn
+ && (iColumn!=XN_EXPR
+ || sqlite3ExprCompare(pTerm->pExpr->pLeft,pScan->pIdxExpr,iCur)==0)
+ && (pScan->iEquiv<=1 || !ExprHasProperty(pTerm->pExpr, EP_FromJoin))
+ ){
+ if( (pTerm->eOperator & WO_EQUIV)!=0
+ && pScan->nEquiv<ArraySize(pScan->aiCur)
+ && (pX = sqlite3ExprSkipCollate(pTerm->pExpr->pRight))->op==TK_COLUMN
+ ){
+ int j;
+ for(j=0; j<pScan->nEquiv; j++){
+ if( pScan->aiCur[j]==pX->iTable
+ && pScan->aiColumn[j]==pX->iColumn ){
+ break;
+ }
+ }
+ if( j==pScan->nEquiv ){
+ pScan->aiCur[j] = pX->iTable;
+ pScan->aiColumn[j] = pX->iColumn;
+ pScan->nEquiv++;
+ }
+ }
+ if( (pTerm->eOperator & pScan->opMask)!=0 ){
+ /* Verify the affinity and collating sequence match */
+ if( pScan->zCollName && (pTerm->eOperator & WO_ISNULL)==0 ){
+ CollSeq *pColl;
+ Parse *pParse = pWC->pWInfo->pParse;
+ pX = pTerm->pExpr;
+ if( !sqlite3IndexAffinityOk(pX, pScan->idxaff) ){
+ continue;
+ }
+ assert(pX->pLeft);
+ pColl = sqlite3BinaryCompareCollSeq(pParse,
+ pX->pLeft, pX->pRight);
+ if( pColl==0 ) pColl = pParse->db->pDfltColl;
+ if( sqlite3StrICmp(pColl->zName, pScan->zCollName) ){
+ continue;
+ }
+ }
+ if( (pTerm->eOperator & (WO_EQ|WO_IS))!=0
+ && (pX = pTerm->pExpr->pRight)->op==TK_COLUMN
+ && pX->iTable==pScan->aiCur[0]
+ && pX->iColumn==pScan->aiColumn[0]
+ ){
+ testcase( pTerm->eOperator & WO_IS );
+ continue;
+ }
+ pScan->k = k+1;
+ return pTerm;
+ }
+ }
+ }
+ pScan->pWC = pScan->pWC->pOuter;
+ k = 0;
+ }
+ pScan->pWC = pScan->pOrigWC;
+ k = 0;
+ pScan->iEquiv++;
+ }
+ return 0;
+}
+
+/*
+** Initialize a WHERE clause scanner object. Return a pointer to the
+** first match. Return NULL if there are no matches.
+**
+** The scanner will be searching the WHERE clause pWC. It will look
+** for terms of the form "X <op> <expr>" where X is column iColumn of table
+** iCur. Or if pIdx!=0 then X is column iColumn of index pIdx. pIdx
+** must be one of the indexes of table iCur.
+**
+** The <op> must be one of the operators described by opMask.
+**
+** If the search is for X and the WHERE clause contains terms of the
+** form X=Y then this routine might also return terms of the form
+** "Y <op> <expr>". The number of levels of transitivity is limited,
+** but is enough to handle most commonly occurring SQL statements.
+**
+** If X is not the INTEGER PRIMARY KEY then X must be compatible with
+** index pIdx.
+*/
+static WhereTerm *whereScanInit(
+ WhereScan *pScan, /* The WhereScan object being initialized */
+ WhereClause *pWC, /* The WHERE clause to be scanned */
+ int iCur, /* Cursor to scan for */
+ int iColumn, /* Column to scan for */
+ u32 opMask, /* Operator(s) to scan for */
+ Index *pIdx /* Must be compatible with this index */
+){
+ int j = 0;
+
+ /* memset(pScan, 0, sizeof(*pScan)); */
+ pScan->pOrigWC = pWC;
+ pScan->pWC = pWC;
+ pScan->pIdxExpr = 0;
+ if( pIdx ){
+ j = iColumn;
+ iColumn = pIdx->aiColumn[j];
+ if( iColumn==XN_EXPR ) pScan->pIdxExpr = pIdx->aColExpr->a[j].pExpr;
+ if( iColumn==pIdx->pTable->iPKey ) iColumn = XN_ROWID;
+ }
+ if( pIdx && iColumn>=0 ){
+ pScan->idxaff = pIdx->pTable->aCol[iColumn].affinity;
+ pScan->zCollName = pIdx->azColl[j];
+ }else{
+ pScan->idxaff = 0;
+ pScan->zCollName = 0;
+ }
+ pScan->opMask = opMask;
+ pScan->k = 0;
+ pScan->aiCur[0] = iCur;
+ pScan->aiColumn[0] = iColumn;
+ pScan->nEquiv = 1;
+ pScan->iEquiv = 1;
+ return whereScanNext(pScan);
+}
+
+/*
+** Search for a term in the WHERE clause that is of the form "X <op> <expr>"
+** where X is a reference to the iColumn of table iCur or of index pIdx
+** if pIdx!=0 and <op> is one of the WO_xx operator codes specified by
+** the op parameter. Return a pointer to the term. Return 0 if not found.
+**
+** If pIdx!=0 then it must be one of the indexes of table iCur.
+** Search for terms matching the iColumn-th column of pIdx
+** rather than the iColumn-th column of table iCur.
+**
+** The term returned might by Y=<expr> if there is another constraint in
+** the WHERE clause that specifies that X=Y. Any such constraints will be
+** identified by the WO_EQUIV bit in the pTerm->eOperator field. The
+** aiCur[]/iaColumn[] arrays hold X and all its equivalents. There are 11
+** slots in aiCur[]/aiColumn[] so that means we can look for X plus up to 10
+** other equivalent values. Hence a search for X will return <expr> if X=A1
+** and A1=A2 and A2=A3 and ... and A9=A10 and A10=<expr>.
+**
+** If there are multiple terms in the WHERE clause of the form "X <op> <expr>"
+** then try for the one with no dependencies on <expr> - in other words where
+** <expr> is a constant expression of some kind. Only return entries of
+** the form "X <op> Y" where Y is a column in another table if no terms of
+** the form "X <op> <const-expr>" exist. If no terms with a constant RHS
+** exist, try to return a term that does not use WO_EQUIV.
+*/
+SQLITE_PRIVATE WhereTerm *sqlite3WhereFindTerm(
+ WhereClause *pWC, /* The WHERE clause to be searched */
+ int iCur, /* Cursor number of LHS */
+ int iColumn, /* Column number of LHS */
+ Bitmask notReady, /* RHS must not overlap with this mask */
+ u32 op, /* Mask of WO_xx values describing operator */
+ Index *pIdx /* Must be compatible with this index, if not NULL */
+){
+ WhereTerm *pResult = 0;
+ WhereTerm *p;
+ WhereScan scan;
+
+ p = whereScanInit(&scan, pWC, iCur, iColumn, op, pIdx);
+ op &= WO_EQ|WO_IS;
+ while( p ){
+ if( (p->prereqRight & notReady)==0 ){
+ if( p->prereqRight==0 && (p->eOperator&op)!=0 ){
+ testcase( p->eOperator & WO_IS );
+ return p;
+ }
+ if( pResult==0 ) pResult = p;
+ }
+ p = whereScanNext(&scan);
+ }
+ return pResult;
+}
+
/*
** This function searches pList for an entry that matches the iCol-th column
** of index pIdx.
@@ -117717,11 +126362,30 @@ static int findIndexCol(
}
/*
+** Return TRUE if the iCol-th column of index pIdx is NOT NULL
+*/
+static int indexColumnNotNull(Index *pIdx, int iCol){
+ int j;
+ assert( pIdx!=0 );
+ assert( iCol>=0 && iCol<pIdx->nColumn );
+ j = pIdx->aiColumn[iCol];
+ if( j>=0 ){
+ return pIdx->pTable->aCol[j].notNull;
+ }else if( j==(-1) ){
+ return 1;
+ }else{
+ assert( j==(-2) );
+ return 0; /* Assume an indexed expression can always yield a NULL */
+
+ }
+}
+
+/*
** Return true if the DISTINCT expression-list passed as the third argument
** is redundant.
**
-** A DISTINCT list is redundant if the database contains some subset of
-** columns that are unique and non-null.
+** A DISTINCT list is redundant if any subset of the columns in the
+** DISTINCT list are collectively unique and individually non-null.
*/
static int isDistinctRedundant(
Parse *pParse, /* Parsing context */
@@ -117766,12 +126430,9 @@ static int isDistinctRedundant(
for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
if( !IsUniqueIndex(pIdx) ) continue;
for(i=0; i<pIdx->nKeyCol; i++){
- i16 iCol = pIdx->aiColumn[i];
- if( 0==findTerm(pWC, iBase, iCol, ~(Bitmask)0, WO_EQ, pIdx) ){
- int iIdxCol = findIndexCol(pParse, pDistinct, iBase, pIdx, i);
- if( iIdxCol<0 || pTab->aCol[iCol].notNull==0 ){
- break;
- }
+ if( 0==sqlite3WhereFindTerm(pWC, iBase, i, ~(Bitmask)0, WO_EQ, pIdx) ){
+ if( findIndexCol(pParse, pDistinct, iBase, pIdx, i)<0 ) break;
+ if( indexColumnNotNull(pIdx, i)==0 ) break;
}
}
if( i==pIdx->nKeyCol ){
@@ -117792,6 +126453,49 @@ static LogEst estLog(LogEst N){
}
/*
+** Convert OP_Column opcodes to OP_Copy in previously generated code.
+**
+** This routine runs over generated VDBE code and translates OP_Column
+** opcodes into OP_Copy when the table is being accessed via co-routine
+** instead of via table lookup.
+**
+** If the bIncrRowid parameter is 0, then any OP_Rowid instructions on
+** cursor iTabCur are transformed into OP_Null. Or, if bIncrRowid is non-zero,
+** then each OP_Rowid is transformed into an instruction to increment the
+** value stored in its output register.
+*/
+static void translateColumnToCopy(
+ Vdbe *v, /* The VDBE containing code to translate */
+ int iStart, /* Translate from this opcode to the end */
+ int iTabCur, /* OP_Column/OP_Rowid references to this table */
+ int iRegister, /* The first column is in this register */
+ int bIncrRowid /* If non-zero, transform OP_rowid to OP_AddImm(1) */
+){
+ VdbeOp *pOp = sqlite3VdbeGetOp(v, iStart);
+ int iEnd = sqlite3VdbeCurrentAddr(v);
+ for(; iStart<iEnd; iStart++, pOp++){
+ if( pOp->p1!=iTabCur ) continue;
+ if( pOp->opcode==OP_Column ){
+ pOp->opcode = OP_Copy;
+ pOp->p1 = pOp->p2 + iRegister;
+ pOp->p2 = pOp->p3;
+ pOp->p3 = 0;
+ }else if( pOp->opcode==OP_Rowid ){
+ if( bIncrRowid ){
+ /* Increment the value stored in the P2 operand of the OP_Rowid. */
+ pOp->opcode = OP_AddImm;
+ pOp->p1 = pOp->p2;
+ pOp->p2 = 1;
+ }else{
+ pOp->opcode = OP_Null;
+ pOp->p1 = 0;
+ pOp->p3 = 0;
+ }
+ }
+ }
+}
+
+/*
** Two routines for printing the content of an sqlite3_index_info
** structure. Used for testing and debugging only. If neither
** SQLITE_TEST or SQLITE_DEBUG are defined, then these routines
@@ -117849,11 +126553,12 @@ static int termCanDriveIndex(
){
char aff;
if( pTerm->leftCursor!=pSrc->iCursor ) return 0;
- if( (pTerm->eOperator & WO_EQ)==0 ) return 0;
+ if( (pTerm->eOperator & (WO_EQ|WO_IS))==0 ) return 0;
if( (pTerm->prereqRight & notReady)!=0 ) return 0;
if( pTerm->u.leftColumn<0 ) return 0;
aff = pSrc->pTab->aCol[pTerm->u.leftColumn].affinity;
if( !sqlite3IndexAffinityOk(pTerm->pExpr, aff) ) return 0;
+ testcase( pTerm->pExpr->op==TK_IS );
return 1;
}
#endif
@@ -117892,6 +126597,9 @@ static void constructAutomaticIndex(
u8 sentWarning = 0; /* True if a warnning has been issued */
Expr *pPartial = 0; /* Partial Index Expression */
int iContinue = 0; /* Jump here to skip excluded rows */
+ struct SrcList_item *pTabItem; /* FROM clause term being indexed */
+ int addrCounter = 0; /* Address where integer counter is initialized */
+ int regBase; /* Array of registers where record is assembled */
/* Generate code to skip over the creation and initialization of the
** transient index on 2nd and subsequent iterations of the loop. */
@@ -117981,7 +126689,7 @@ static void constructAutomaticIndex(
idxCols |= cMask;
pIdx->aiColumn[n] = pTerm->u.leftColumn;
pColl = sqlite3BinaryCompareCollSeq(pParse, pX->pLeft, pX->pRight);
- pIdx->azColl[n] = pColl ? pColl->zName : "BINARY";
+ pIdx->azColl[n] = pColl ? pColl->zName : sqlite3StrBINARY;
n++;
}
}
@@ -117993,20 +126701,20 @@ static void constructAutomaticIndex(
for(i=0; i<mxBitCol; i++){
if( extraCols & MASKBIT(i) ){
pIdx->aiColumn[n] = i;
- pIdx->azColl[n] = "BINARY";
+ pIdx->azColl[n] = sqlite3StrBINARY;
n++;
}
}
if( pSrc->colUsed & MASKBIT(BMS-1) ){
for(i=BMS-1; i<pTable->nCol; i++){
pIdx->aiColumn[n] = i;
- pIdx->azColl[n] = "BINARY";
+ pIdx->azColl[n] = sqlite3StrBINARY;
n++;
}
}
assert( n==nKeyCol );
- pIdx->aiColumn[n] = -1;
- pIdx->azColl[n] = "BINARY";
+ pIdx->aiColumn[n] = XN_ROWID;
+ pIdx->azColl[n] = sqlite3StrBINARY;
/* Create the automatic index */
assert( pLevel->iIdxCur>=0 );
@@ -118017,18 +126725,37 @@ static void constructAutomaticIndex(
/* Fill the automatic index with content */
sqlite3ExprCachePush(pParse);
- addrTop = sqlite3VdbeAddOp1(v, OP_Rewind, pLevel->iTabCur); VdbeCoverage(v);
+ pTabItem = &pWC->pWInfo->pTabList->a[pLevel->iFrom];
+ if( pTabItem->fg.viaCoroutine ){
+ int regYield = pTabItem->regReturn;
+ addrCounter = sqlite3VdbeAddOp2(v, OP_Integer, 0, 0);
+ sqlite3VdbeAddOp3(v, OP_InitCoroutine, regYield, 0, pTabItem->addrFillSub);
+ addrTop = sqlite3VdbeAddOp1(v, OP_Yield, regYield);
+ VdbeCoverage(v);
+ VdbeComment((v, "next row of \"%s\"", pTabItem->pTab->zName));
+ }else{
+ addrTop = sqlite3VdbeAddOp1(v, OP_Rewind, pLevel->iTabCur); VdbeCoverage(v);
+ }
if( pPartial ){
iContinue = sqlite3VdbeMakeLabel(v);
sqlite3ExprIfFalse(pParse, pPartial, iContinue, SQLITE_JUMPIFNULL);
pLoop->wsFlags |= WHERE_PARTIALIDX;
}
regRecord = sqlite3GetTempReg(pParse);
- sqlite3GenerateIndexKey(pParse, pIdx, pLevel->iTabCur, regRecord, 0, 0, 0, 0);
+ regBase = sqlite3GenerateIndexKey(
+ pParse, pIdx, pLevel->iTabCur, regRecord, 0, 0, 0, 0
+ );
sqlite3VdbeAddOp2(v, OP_IdxInsert, pLevel->iIdxCur, regRecord);
sqlite3VdbeChangeP5(v, OPFLAG_USESEEKRESULT);
if( pPartial ) sqlite3VdbeResolveLabel(v, iContinue);
- sqlite3VdbeAddOp2(v, OP_Next, pLevel->iTabCur, addrTop+1); VdbeCoverage(v);
+ if( pTabItem->fg.viaCoroutine ){
+ sqlite3VdbeChangeP2(v, addrCounter, regBase+n);
+ translateColumnToCopy(v, addrTop, pLevel->iTabCur, pTabItem->regResult, 1);
+ sqlite3VdbeGoto(v, addrTop);
+ pTabItem->fg.viaCoroutine = 0;
+ }else{
+ sqlite3VdbeAddOp2(v, OP_Next, pLevel->iTabCur, addrTop+1); VdbeCoverage(v);
+ }
sqlite3VdbeChangeP5(v, SQLITE_STMTSTATUS_AUTOINDEX);
sqlite3VdbeJumpHere(v, addrTop);
sqlite3ReleaseTempReg(pParse, regRecord);
@@ -118051,6 +126778,7 @@ end_auto_index_create:
static sqlite3_index_info *allocateIndexInfo(
Parse *pParse,
WhereClause *pWC,
+ Bitmask mUnusable, /* Ignore terms with these prereqs */
struct SrcList_item *pSrc,
ExprList *pOrderBy
){
@@ -118067,12 +126795,15 @@ static sqlite3_index_info *allocateIndexInfo(
** to this virtual table */
for(i=nTerm=0, pTerm=pWC->a; i<pWC->nTerm; i++, pTerm++){
if( pTerm->leftCursor != pSrc->iCursor ) continue;
+ if( pTerm->prereqRight & mUnusable ) continue;
assert( IsPowerOfTwo(pTerm->eOperator & ~WO_EQUIV) );
testcase( pTerm->eOperator & WO_IN );
testcase( pTerm->eOperator & WO_ISNULL );
+ testcase( pTerm->eOperator & WO_IS );
testcase( pTerm->eOperator & WO_ALL );
- if( (pTerm->eOperator & ~(WO_ISNULL|WO_EQUIV))==0 ) continue;
+ if( (pTerm->eOperator & ~(WO_ISNULL|WO_EQUIV|WO_IS))==0 ) continue;
if( pTerm->wtFlags & TERM_VNULL ) continue;
+ assert( pTerm->u.leftColumn>=(-1) );
nTerm++;
}
@@ -118120,16 +126851,22 @@ static sqlite3_index_info *allocateIndexInfo(
for(i=j=0, pTerm=pWC->a; i<pWC->nTerm; i++, pTerm++){
u8 op;
if( pTerm->leftCursor != pSrc->iCursor ) continue;
+ if( pTerm->prereqRight & mUnusable ) continue;
assert( IsPowerOfTwo(pTerm->eOperator & ~WO_EQUIV) );
testcase( pTerm->eOperator & WO_IN );
+ testcase( pTerm->eOperator & WO_IS );
testcase( pTerm->eOperator & WO_ISNULL );
testcase( pTerm->eOperator & WO_ALL );
- if( (pTerm->eOperator & ~(WO_ISNULL|WO_EQUIV))==0 ) continue;
+ if( (pTerm->eOperator & ~(WO_ISNULL|WO_EQUIV|WO_IS))==0 ) continue;
if( pTerm->wtFlags & TERM_VNULL ) continue;
+ assert( pTerm->u.leftColumn>=(-1) );
pIdxCons[j].iColumn = pTerm->u.leftColumn;
pIdxCons[j].iTermOffset = i;
op = (u8)pTerm->eOperator & WO_ALL;
if( op==WO_IN ) op = WO_EQ;
+ if( op==WO_MATCH ){
+ op = pTerm->eMatchOp;
+ }
pIdxCons[j].op = op;
/* The direct assignment in the previous line is possible only because
** the WO_ and SQLITE_INDEX_CONSTRAINT_ codes are identical. The
@@ -118168,7 +126905,6 @@ static sqlite3_index_info *allocateIndexInfo(
*/
static int vtabBestIndex(Parse *pParse, Table *pTab, sqlite3_index_info *p){
sqlite3_vtab *pVtab = sqlite3GetVTable(pParse->db, pTab)->pVtab;
- int i;
int rc;
TRACE_IDX_INPUTS(p);
@@ -118177,7 +126913,7 @@ static int vtabBestIndex(Parse *pParse, Table *pTab, sqlite3_index_info *p){
if( rc!=SQLITE_OK ){
if( rc==SQLITE_NOMEM ){
- pParse->db->mallocFailed = 1;
+ sqlite3OomFault(pParse->db);
}else if( !pVtab->zErrMsg ){
sqlite3ErrorMsg(pParse, "%s", sqlite3ErrStr(rc));
}else{
@@ -118187,12 +126923,16 @@ static int vtabBestIndex(Parse *pParse, Table *pTab, sqlite3_index_info *p){
sqlite3_free(pVtab->zErrMsg);
pVtab->zErrMsg = 0;
+#if 0
+ /* This error is now caught by the caller.
+ ** Search for "xBestIndex malfunction" below */
for(i=0; i<p->nConstraint; i++){
if( !p->aConstraint[i].usable && p->aConstraintUsage[i].argvIndex>0 ){
sqlite3ErrorMsg(pParse,
"table %s: xBestIndex returned an invalid plan", pTab->zName);
}
}
+#endif
return pParse->nErr;
}
@@ -118416,6 +127156,21 @@ static LogEst whereRangeAdjust(WhereTerm *pTerm, LogEst nNew){
return nRet;
}
+
+#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+/*
+** Return the affinity for a single column of an index.
+*/
+static char sqlite3IndexColumnAffinity(sqlite3 *db, Index *pIdx, int iCol){
+ assert( iCol>=0 && iCol<pIdx->nColumn );
+ if( !pIdx->zColAff ){
+ if( sqlite3IndexAffinityStr(db, pIdx)==0 ) return SQLITE_AFF_BLOB;
+ }
+ return pIdx->zColAff[iCol];
+}
+#endif
+
+
#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
/*
** This function is called to estimate the number of rows visited by a
@@ -118465,8 +127220,7 @@ static int whereRangeSkipScanEst(
int nLower = -1;
int nUpper = p->nSample+1;
int rc = SQLITE_OK;
- int iCol = p->aiColumn[nEq];
- u8 aff = iCol>=0 ? p->pTable->aCol[iCol].affinity : SQLITE_AFF_INTEGER;
+ u8 aff = sqlite3IndexColumnAffinity(db, p, nEq);
CollSeq *pColl;
sqlite3_value *p1 = 0; /* Value extracted from pLower */
@@ -118614,11 +127368,8 @@ static int whereRangeScanEst(
testcase( pRec->nField!=pBuilder->nRecValid );
pRec->nField = pBuilder->nRecValid;
}
- if( nEq==p->nKeyCol ){
- aff = SQLITE_AFF_INTEGER;
- }else{
- aff = p->pTable->aCol[p->aiColumn[nEq]].affinity;
- }
+ aff = sqlite3IndexColumnAffinity(pParse->db, p, nEq);
+ assert( nEq!=p->nKeyCol || aff==SQLITE_AFF_INTEGER );
/* Determine iLower and iUpper using ($P) only. */
if( nEq==0 ){
iLower = 0;
@@ -118776,7 +127527,7 @@ static int whereEqualScanEst(
return SQLITE_OK;
}
- aff = p->pTable->aCol[p->aiColumn[nEq-1]].affinity;
+ aff = sqlite3IndexColumnAffinity(pParse->db, p, nEq-1);
rc = sqlite3Stat4ProbeSetValue(pParse, p, &pRec, pExpr, aff, nEq-1, &bOk);
pBuilder->pRec = pRec;
if( rc!=SQLITE_OK ) return rc;
@@ -118784,7 +127535,8 @@ static int whereEqualScanEst(
pBuilder->nRecValid = nEq;
whereKeyStats(pParse, p, pRec, 0, a);
- WHERETRACE(0x10,("equality scan regions: %d\n", (int)a[1]));
+ WHERETRACE(0x10,("equality scan regions %s(%d): %d\n",
+ p->zName, nEq-1, (int)a[1]));
*pnRow = a[1];
return rc;
@@ -118840,1484 +127592,6 @@ static int whereInScanEst(
}
#endif /* SQLITE_ENABLE_STAT3_OR_STAT4 */
-/*
-** Disable a term in the WHERE clause. Except, do not disable the term
-** if it controls a LEFT OUTER JOIN and it did not originate in the ON
-** or USING clause of that join.
-**
-** Consider the term t2.z='ok' in the following queries:
-**
-** (1) SELECT * FROM t1 LEFT JOIN t2 ON t1.a=t2.x WHERE t2.z='ok'
-** (2) SELECT * FROM t1 LEFT JOIN t2 ON t1.a=t2.x AND t2.z='ok'
-** (3) SELECT * FROM t1, t2 WHERE t1.a=t2.x AND t2.z='ok'
-**
-** The t2.z='ok' is disabled in the in (2) because it originates
-** in the ON clause. The term is disabled in (3) because it is not part
-** of a LEFT OUTER JOIN. In (1), the term is not disabled.
-**
-** Disabling a term causes that term to not be tested in the inner loop
-** of the join. Disabling is an optimization. When terms are satisfied
-** by indices, we disable them to prevent redundant tests in the inner
-** loop. We would get the correct results if nothing were ever disabled,
-** 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){
- int nLoop = 0;
- while( pTerm
- && (pTerm->wtFlags & TERM_CODED)==0
- && (pLevel->iLeftJoin==0 || ExprHasProperty(pTerm->pExpr, EP_FromJoin))
- && (pLevel->notReady & pTerm->prereqAll)==0
- ){
- 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++;
- }
-}
-
-/*
-** Code an OP_Affinity opcode to apply the column affinity string zAff
-** to the n registers starting at base.
-**
-** As an optimization, SQLITE_AFF_NONE entries (which are no-ops) at the
-** beginning and end of zAff are ignored. If all entries in zAff are
-** SQLITE_AFF_NONE, then no code gets generated.
-**
-** This routine makes its own copy of zAff so that the caller is free
-** to modify zAff after this routine returns.
-*/
-static void codeApplyAffinity(Parse *pParse, int base, int n, char *zAff){
- Vdbe *v = pParse->pVdbe;
- if( zAff==0 ){
- assert( pParse->db->mallocFailed );
- return;
- }
- assert( v!=0 );
-
- /* Adjust base and n to skip over SQLITE_AFF_NONE entries at the beginning
- ** and end of the affinity string.
- */
- while( n>0 && zAff[0]==SQLITE_AFF_NONE ){
- n--;
- base++;
- zAff++;
- }
- while( n>1 && zAff[n-1]==SQLITE_AFF_NONE ){
- n--;
- }
-
- /* Code the OP_Affinity opcode if there is anything left to do. */
- if( n>0 ){
- sqlite3VdbeAddOp2(v, OP_Affinity, base, n);
- sqlite3VdbeChangeP4(v, -1, zAff, n);
- sqlite3ExprCacheAffinityChange(pParse, base, n);
- }
-}
-
-
-/*
-** Generate code for a single equality term of the WHERE clause. An equality
-** term can be either X=expr or X IN (...). pTerm is the term to be
-** coded.
-**
-** The current value for the constraint is left in register iReg.
-**
-** For a constraint of the form X=expr, the expression is evaluated and its
-** result is left on the stack. For constraints of the form X IN (...)
-** this routine sets up a loop that will iterate over all values of X.
-*/
-static int codeEqualityTerm(
- Parse *pParse, /* The parsing context */
- WhereTerm *pTerm, /* The term of the WHERE clause to be coded */
- WhereLevel *pLevel, /* The level of the FROM clause we are working on */
- int iEq, /* Index of the equality term within this level */
- int bRev, /* True for reverse-order IN operations */
- int iTarget /* Attempt to leave results in this register */
-){
- Expr *pX = pTerm->pExpr;
- Vdbe *v = pParse->pVdbe;
- int iReg; /* Register holding results */
-
- assert( iTarget>0 );
- if( pX->op==TK_EQ ){
- iReg = sqlite3ExprCodeTarget(pParse, pX->pRight, iTarget);
- }else if( pX->op==TK_ISNULL ){
- iReg = iTarget;
- sqlite3VdbeAddOp2(v, OP_Null, 0, iReg);
-#ifndef SQLITE_OMIT_SUBQUERY
- }else{
- int eType;
- int iTab;
- struct InLoop *pIn;
- WhereLoop *pLoop = pLevel->pWLoop;
-
- if( (pLoop->wsFlags & WHERE_VIRTUALTABLE)==0
- && pLoop->u.btree.pIndex!=0
- && pLoop->u.btree.pIndex->aSortOrder[iEq]
- ){
- testcase( iEq==0 );
- testcase( bRev );
- bRev = !bRev;
- }
- assert( pX->op==TK_IN );
- iReg = iTarget;
- eType = sqlite3FindInIndex(pParse, pX, IN_INDEX_LOOP, 0);
- if( eType==IN_INDEX_INDEX_DESC ){
- testcase( bRev );
- bRev = !bRev;
- }
- iTab = pX->iTable;
- sqlite3VdbeAddOp2(v, bRev ? OP_Last : OP_Rewind, iTab, 0);
- VdbeCoverageIf(v, bRev);
- VdbeCoverageIf(v, !bRev);
- assert( (pLoop->wsFlags & WHERE_MULTI_OR)==0 );
- pLoop->wsFlags |= WHERE_IN_ABLE;
- if( pLevel->u.in.nIn==0 ){
- pLevel->addrNxt = sqlite3VdbeMakeLabel(v);
- }
- pLevel->u.in.nIn++;
- pLevel->u.in.aInLoop =
- sqlite3DbReallocOrFree(pParse->db, pLevel->u.in.aInLoop,
- sizeof(pLevel->u.in.aInLoop[0])*pLevel->u.in.nIn);
- pIn = pLevel->u.in.aInLoop;
- if( pIn ){
- pIn += pLevel->u.in.nIn - 1;
- pIn->iCur = iTab;
- if( eType==IN_INDEX_ROWID ){
- pIn->addrInTop = sqlite3VdbeAddOp2(v, OP_Rowid, iTab, iReg);
- }else{
- pIn->addrInTop = sqlite3VdbeAddOp3(v, OP_Column, iTab, 0, iReg);
- }
- pIn->eEndLoopOp = bRev ? OP_PrevIfOpen : OP_NextIfOpen;
- sqlite3VdbeAddOp1(v, OP_IsNull, iReg); VdbeCoverage(v);
- }else{
- pLevel->u.in.nIn = 0;
- }
-#endif
- }
- disableTerm(pLevel, pTerm);
- return iReg;
-}
-
-/*
-** Generate code that will evaluate all == and IN constraints for an
-** index scan.
-**
-** For example, consider table t1(a,b,c,d,e,f) with index i1(a,b,c).
-** Suppose the WHERE clause is this: a==5 AND b IN (1,2,3) AND c>5 AND c<10
-** The index has as many as three equality constraints, but in this
-** example, the third "c" value is an inequality. So only two
-** constraints are coded. This routine will generate code to evaluate
-** a==5 and b IN (1,2,3). The current values for a and b will be stored
-** in consecutive registers and the index of the first register is returned.
-**
-** In the example above nEq==2. But this subroutine works for any value
-** of nEq including 0. If nEq==0, this routine is nearly a no-op.
-** The only thing it does is allocate the pLevel->iMem memory cell and
-** compute the affinity string.
-**
-** The nExtraReg parameter is 0 or 1. It is 0 if all WHERE clause constraints
-** are == or IN and are covered by the nEq. nExtraReg is 1 if there is
-** an inequality constraint (such as the "c>=5 AND c<10" in the example) that
-** occurs after the nEq quality constraints.
-**
-** This routine allocates a range of nEq+nExtraReg memory cells and returns
-** the index of the first memory cell in that range. The code that
-** calls this routine will use that memory range to store keys for
-** start and termination conditions of the loop.
-** key value of the loop. If one or more IN operators appear, then
-** this routine allocates an additional nEq memory cells for internal
-** use.
-**
-** Before returning, *pzAff is set to point to a buffer containing a
-** copy of the column affinity string of the index allocated using
-** sqlite3DbMalloc(). Except, entries in the copy of the string associated
-** with equality constraints that use NONE affinity are set to
-** SQLITE_AFF_NONE. This is to deal with SQL such as the following:
-**
-** CREATE TABLE t1(a TEXT PRIMARY KEY, b);
-** SELECT ... FROM t1 AS t2, t1 WHERE t1.a = t2.b;
-**
-** In the example above, the index on t1(a) has TEXT affinity. But since
-** the right hand side of the equality constraint (t2.b) has NONE affinity,
-** no conversion should be attempted before using a t2.b value as part of
-** a key to search the index. Hence the first byte in the returned affinity
-** string in this example would be set to SQLITE_AFF_NONE.
-*/
-static int codeAllEqualityTerms(
- Parse *pParse, /* Parsing context */
- WhereLevel *pLevel, /* Which nested loop of the FROM we are coding */
- int bRev, /* Reverse the order of IN operators */
- int nExtraReg, /* Number of extra registers to allocate */
- char **pzAff /* OUT: Set to point to affinity string */
-){
- u16 nEq; /* The number of == or IN constraints to code */
- u16 nSkip; /* Number of left-most columns to skip */
- Vdbe *v = pParse->pVdbe; /* The vm under construction */
- Index *pIdx; /* The index being used for this loop */
- WhereTerm *pTerm; /* A single constraint term */
- WhereLoop *pLoop; /* The WhereLoop object */
- int j; /* Loop counter */
- int regBase; /* Base register */
- int nReg; /* Number of registers to allocate */
- char *zAff; /* Affinity string to return */
-
- /* This module is only called on query plans that use an index. */
- pLoop = pLevel->pWLoop;
- assert( (pLoop->wsFlags & WHERE_VIRTUALTABLE)==0 );
- nEq = pLoop->u.btree.nEq;
- nSkip = pLoop->nSkip;
- pIdx = pLoop->u.btree.pIndex;
- assert( pIdx!=0 );
-
- /* Figure out how many memory cells we will need then allocate them.
- */
- regBase = pParse->nMem + 1;
- nReg = pLoop->u.btree.nEq + nExtraReg;
- pParse->nMem += nReg;
-
- zAff = sqlite3DbStrDup(pParse->db, sqlite3IndexAffinityStr(v, pIdx));
- if( !zAff ){
- pParse->db->mallocFailed = 1;
- }
-
- if( nSkip ){
- int iIdxCur = pLevel->iIdxCur;
- sqlite3VdbeAddOp1(v, (bRev?OP_Last:OP_Rewind), iIdxCur);
- VdbeCoverageIf(v, bRev==0);
- VdbeCoverageIf(v, bRev!=0);
- VdbeComment((v, "begin skip-scan on %s", pIdx->zName));
- j = sqlite3VdbeAddOp0(v, OP_Goto);
- pLevel->addrSkip = sqlite3VdbeAddOp4Int(v, (bRev?OP_SeekLT:OP_SeekGT),
- iIdxCur, 0, regBase, nSkip);
- VdbeCoverageIf(v, bRev==0);
- VdbeCoverageIf(v, bRev!=0);
- sqlite3VdbeJumpHere(v, j);
- for(j=0; j<nSkip; j++){
- sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, j, regBase+j);
- assert( pIdx->aiColumn[j]>=0 );
- VdbeComment((v, "%s", pIdx->pTable->aCol[pIdx->aiColumn[j]].zName));
- }
- }
-
- /* Evaluate the equality constraints
- */
- assert( zAff==0 || (int)strlen(zAff)>=nEq );
- for(j=nSkip; j<nEq; j++){
- int r1;
- pTerm = pLoop->aLTerm[j];
- assert( pTerm!=0 );
- /* The following testcase is true for indices with redundant columns.
- ** Ex: CREATE INDEX i1 ON t1(a,b,a); SELECT * FROM t1 WHERE a=0 AND b=0; */
- testcase( (pTerm->wtFlags & TERM_CODED)!=0 );
- testcase( pTerm->wtFlags & TERM_VIRTUAL );
- r1 = codeEqualityTerm(pParse, pTerm, pLevel, j, bRev, regBase+j);
- if( r1!=regBase+j ){
- if( nReg==1 ){
- sqlite3ReleaseTempReg(pParse, regBase);
- regBase = r1;
- }else{
- sqlite3VdbeAddOp2(v, OP_SCopy, r1, regBase+j);
- }
- }
- testcase( pTerm->eOperator & WO_ISNULL );
- testcase( pTerm->eOperator & WO_IN );
- if( (pTerm->eOperator & (WO_ISNULL|WO_IN))==0 ){
- Expr *pRight = pTerm->pExpr->pRight;
- if( sqlite3ExprCanBeNull(pRight) ){
- sqlite3VdbeAddOp2(v, OP_IsNull, regBase+j, pLevel->addrBrk);
- VdbeCoverage(v);
- }
- if( zAff ){
- if( sqlite3CompareAffinity(pRight, zAff[j])==SQLITE_AFF_NONE ){
- zAff[j] = SQLITE_AFF_NONE;
- }
- if( sqlite3ExprNeedsNoAffinityChange(pRight, zAff[j]) ){
- zAff[j] = SQLITE_AFF_NONE;
- }
- }
- }
- }
- *pzAff = zAff;
- return regBase;
-}
-
-#ifndef SQLITE_OMIT_EXPLAIN
-/*
-** This routine is a helper for explainIndexRange() below
-**
-** pStr holds the text of an expression that we are building up one term
-** at a time. This routine adds a new term to the end of the expression.
-** Terms are separated by AND so add the "AND" text for second and subsequent
-** terms only.
-*/
-static void explainAppendTerm(
- StrAccum *pStr, /* The text expression being built */
- int iTerm, /* Index of this term. First is zero */
- const char *zColumn, /* Name of the column */
- const char *zOp /* Name of the operator */
-){
- if( iTerm ) sqlite3StrAccumAppend(pStr, " AND ", 5);
- sqlite3StrAccumAppendAll(pStr, zColumn);
- sqlite3StrAccumAppend(pStr, zOp, 1);
- sqlite3StrAccumAppend(pStr, "?", 1);
-}
-
-/*
-** Argument pLevel describes a strategy for scanning table pTab. This
-** function appends text to pStr that describes the subset of table
-** rows scanned by the strategy in the form of an SQL expression.
-**
-** For example, if the query:
-**
-** SELECT * FROM t1 WHERE a=1 AND b>2;
-**
-** is run and there is an index on (a, b), then this function returns a
-** string similar to:
-**
-** "a=? AND b>?"
-*/
-static void explainIndexRange(StrAccum *pStr, WhereLoop *pLoop, Table *pTab){
- Index *pIndex = pLoop->u.btree.pIndex;
- u16 nEq = pLoop->u.btree.nEq;
- u16 nSkip = pLoop->nSkip;
- int i, j;
- Column *aCol = pTab->aCol;
- i16 *aiColumn = pIndex->aiColumn;
-
- if( nEq==0 && (pLoop->wsFlags&(WHERE_BTM_LIMIT|WHERE_TOP_LIMIT))==0 ) return;
- sqlite3StrAccumAppend(pStr, " (", 2);
- for(i=0; i<nEq; i++){
- char *z = aiColumn[i] < 0 ? "rowid" : aCol[aiColumn[i]].zName;
- if( i>=nSkip ){
- explainAppendTerm(pStr, i, z, "=");
- }else{
- if( i ) sqlite3StrAccumAppend(pStr, " AND ", 5);
- sqlite3XPrintf(pStr, 0, "ANY(%s)", z);
- }
- }
-
- j = i;
- if( pLoop->wsFlags&WHERE_BTM_LIMIT ){
- char *z = aiColumn[j] < 0 ? "rowid" : aCol[aiColumn[j]].zName;
- explainAppendTerm(pStr, i++, z, ">");
- }
- if( pLoop->wsFlags&WHERE_TOP_LIMIT ){
- char *z = aiColumn[j] < 0 ? "rowid" : aCol[aiColumn[j]].zName;
- explainAppendTerm(pStr, i, z, "<");
- }
- sqlite3StrAccumAppend(pStr, ")", 1);
-}
-
-/*
-** This function is a no-op unless currently processing an EXPLAIN QUERY PLAN
-** command, or if either SQLITE_DEBUG or SQLITE_ENABLE_STMT_SCANSTATUS was
-** defined at compile-time. If it is not a no-op, a single OP_Explain opcode
-** is added to the output to describe the table scan strategy in pLevel.
-**
-** If an OP_Explain opcode is added to the VM, its address is returned.
-** Otherwise, if no OP_Explain is coded, zero is returned.
-*/
-static int explainOneScan(
- Parse *pParse, /* Parse context */
- SrcList *pTabList, /* Table list this loop refers to */
- WhereLevel *pLevel, /* Scan to write OP_Explain opcode for */
- int iLevel, /* Value for "level" column of output */
- int iFrom, /* Value for "from" column of output */
- u16 wctrlFlags /* Flags passed to sqlite3WhereBegin() */
-){
- int ret = 0;
-#if !defined(SQLITE_DEBUG) && !defined(SQLITE_ENABLE_STMT_SCANSTATUS)
- if( pParse->explain==2 )
-#endif
- {
- struct SrcList_item *pItem = &pTabList->a[pLevel->iFrom];
- Vdbe *v = pParse->pVdbe; /* VM being constructed */
- sqlite3 *db = pParse->db; /* Database handle */
- int iId = pParse->iSelectId; /* Select id (left-most output column) */
- int isSearch; /* True for a SEARCH. False for SCAN. */
- WhereLoop *pLoop; /* The controlling WhereLoop object */
- u32 flags; /* Flags that describe this loop */
- char *zMsg; /* Text to add to EQP output */
- StrAccum str; /* EQP output string */
- char zBuf[100]; /* Initial space for EQP output string */
-
- pLoop = pLevel->pWLoop;
- flags = pLoop->wsFlags;
- if( (flags&WHERE_MULTI_OR) || (wctrlFlags&WHERE_ONETABLE_ONLY) ) return 0;
-
- isSearch = (flags&(WHERE_BTM_LIMIT|WHERE_TOP_LIMIT))!=0
- || ((flags&WHERE_VIRTUALTABLE)==0 && (pLoop->u.btree.nEq>0))
- || (wctrlFlags&(WHERE_ORDERBY_MIN|WHERE_ORDERBY_MAX));
-
- sqlite3StrAccumInit(&str, db, zBuf, sizeof(zBuf), SQLITE_MAX_LENGTH);
- sqlite3StrAccumAppendAll(&str, isSearch ? "SEARCH" : "SCAN");
- if( pItem->pSelect ){
- sqlite3XPrintf(&str, 0, " SUBQUERY %d", pItem->iSelectId);
- }else{
- sqlite3XPrintf(&str, 0, " TABLE %s", pItem->zName);
- }
-
- if( pItem->zAlias ){
- sqlite3XPrintf(&str, 0, " AS %s", pItem->zAlias);
- }
- if( (flags & (WHERE_IPK|WHERE_VIRTUALTABLE))==0 ){
- const char *zFmt = 0;
- Index *pIdx;
-
- assert( pLoop->u.btree.pIndex!=0 );
- pIdx = pLoop->u.btree.pIndex;
- assert( !(flags&WHERE_AUTO_INDEX) || (flags&WHERE_IDX_ONLY) );
- if( !HasRowid(pItem->pTab) && IsPrimaryKeyIndex(pIdx) ){
- if( isSearch ){
- zFmt = "PRIMARY KEY";
- }
- }else if( flags & WHERE_PARTIALIDX ){
- zFmt = "AUTOMATIC PARTIAL COVERING INDEX";
- }else if( flags & WHERE_AUTO_INDEX ){
- zFmt = "AUTOMATIC COVERING INDEX";
- }else if( flags & WHERE_IDX_ONLY ){
- zFmt = "COVERING INDEX %s";
- }else{
- zFmt = "INDEX %s";
- }
- if( zFmt ){
- sqlite3StrAccumAppend(&str, " USING ", 7);
- sqlite3XPrintf(&str, 0, zFmt, pIdx->zName);
- explainIndexRange(&str, pLoop, pItem->pTab);
- }
- }else if( (flags & WHERE_IPK)!=0 && (flags & WHERE_CONSTRAINT)!=0 ){
- const char *zRange;
- if( flags&(WHERE_COLUMN_EQ|WHERE_COLUMN_IN) ){
- zRange = "(rowid=?)";
- }else if( (flags&WHERE_BOTH_LIMIT)==WHERE_BOTH_LIMIT ){
- zRange = "(rowid>? AND rowid<?)";
- }else if( flags&WHERE_BTM_LIMIT ){
- zRange = "(rowid>?)";
- }else{
- assert( flags&WHERE_TOP_LIMIT);
- zRange = "(rowid<?)";
- }
- sqlite3StrAccumAppendAll(&str, " USING INTEGER PRIMARY KEY ");
- sqlite3StrAccumAppendAll(&str, zRange);
- }
-#ifndef SQLITE_OMIT_VIRTUALTABLE
- else if( (flags & WHERE_VIRTUALTABLE)!=0 ){
- sqlite3XPrintf(&str, 0, " VIRTUAL TABLE INDEX %d:%s",
- pLoop->u.vtab.idxNum, pLoop->u.vtab.idxStr);
- }
-#endif
-#ifdef SQLITE_EXPLAIN_ESTIMATED_ROWS
- if( pLoop->nOut>=10 ){
- sqlite3XPrintf(&str, 0, " (~%llu rows)", sqlite3LogEstToInt(pLoop->nOut));
- }else{
- sqlite3StrAccumAppend(&str, " (~1 row)", 9);
- }
-#endif
- zMsg = sqlite3StrAccumFinish(&str);
- ret = sqlite3VdbeAddOp4(v, OP_Explain, iId, iLevel, iFrom, zMsg,P4_DYNAMIC);
- }
- return ret;
-}
-#else
-# define explainOneScan(u,v,w,x,y,z) 0
-#endif /* SQLITE_OMIT_EXPLAIN */
-
-#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
-/*
-** Configure the VM passed as the first argument with an
-** sqlite3_stmt_scanstatus() entry corresponding to the scan used to
-** implement level pLvl. Argument pSrclist is a pointer to the FROM
-** clause that the scan reads data from.
-**
-** If argument addrExplain is not 0, it must be the address of an
-** OP_Explain instruction that describes the same loop.
-*/
-static void addScanStatus(
- Vdbe *v, /* Vdbe to add scanstatus entry to */
- SrcList *pSrclist, /* FROM clause pLvl reads data from */
- WhereLevel *pLvl, /* Level to add scanstatus() entry for */
- int addrExplain /* Address of OP_Explain (or 0) */
-){
- const char *zObj = 0;
- WhereLoop *pLoop = pLvl->pWLoop;
- if( (pLoop->wsFlags & WHERE_VIRTUALTABLE)==0 && pLoop->u.btree.pIndex!=0 ){
- zObj = pLoop->u.btree.pIndex->zName;
- }else{
- zObj = pSrclist->a[pLvl->iFrom].zName;
- }
- sqlite3VdbeScanStatus(
- v, addrExplain, pLvl->addrBody, pLvl->addrVisit, pLoop->nOut, zObj
- );
-}
-#else
-# 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
-** implementation described by pWInfo.
-*/
-static Bitmask codeOneLoopStart(
- WhereInfo *pWInfo, /* Complete information about the WHERE clause */
- int iLevel, /* Which level of pWInfo->a[] should be coded */
- Bitmask notReady /* Which tables are currently available */
-){
- int j, k; /* Loop counters */
- int iCur; /* The VDBE cursor for the table */
- int addrNxt; /* Where to jump to continue with the next IN case */
- int omitTable; /* True if we use the index only */
- int bRev; /* True if we need to scan in reverse order */
- WhereLevel *pLevel; /* The where level to be coded */
- WhereLoop *pLoop; /* The WhereLoop object being coded */
- WhereClause *pWC; /* Decomposition of the entire WHERE clause */
- WhereTerm *pTerm; /* A WHERE clause term */
- Parse *pParse; /* Parsing context */
- sqlite3 *db; /* Database connection */
- Vdbe *v; /* The prepared stmt under constructions */
- struct SrcList_item *pTabItem; /* FROM clause term being coded */
- int addrBrk; /* Jump here to break out of the loop */
- int addrCont; /* Jump here to continue with next cycle */
- int iRowidReg = 0; /* Rowid is stored in this register, if not zero */
- int iReleaseReg = 0; /* Temp register to free before returning */
-
- pParse = pWInfo->pParse;
- v = pParse->pVdbe;
- pWC = &pWInfo->sWC;
- db = pParse->db;
- pLevel = &pWInfo->a[iLevel];
- pLoop = pLevel->pWLoop;
- pTabItem = &pWInfo->pTabList->a[pLevel->iFrom];
- iCur = pTabItem->iCursor;
- pLevel->notReady = notReady & ~getMask(&pWInfo->sMaskSet, iCur);
- bRev = (pWInfo->revMask>>iLevel)&1;
- omitTable = (pLoop->wsFlags & WHERE_IDX_ONLY)!=0
- && (pWInfo->wctrlFlags & WHERE_FORCE_TABLE)==0;
- VdbeModuleComment((v, "Begin WHERE-loop%d: %s",iLevel,pTabItem->pTab->zName));
-
- /* Create labels for the "break" and "continue" instructions
- ** for the current loop. Jump to addrBrk to break out of a loop.
- ** Jump to cont to go immediately to the next iteration of the
- ** loop.
- **
- ** When there is an IN operator, we also have a "addrNxt" label that
- ** means to continue with the next IN value combination. When
- ** there are no IN operators in the constraints, the "addrNxt" label
- ** is the same as "addrBrk".
- */
- addrBrk = pLevel->addrBrk = pLevel->addrNxt = sqlite3VdbeMakeLabel(v);
- addrCont = pLevel->addrCont = sqlite3VdbeMakeLabel(v);
-
- /* If this is the right table of a LEFT OUTER JOIN, allocate and
- ** initialize a memory cell that records if this table matches any
- ** row of the left table of the join.
- */
- if( pLevel->iFrom>0 && (pTabItem[0].jointype & JT_LEFT)!=0 ){
- pLevel->iLeftJoin = ++pParse->nMem;
- sqlite3VdbeAddOp2(v, OP_Integer, 0, pLevel->iLeftJoin);
- VdbeComment((v, "init LEFT JOIN no-match flag"));
- }
-
- /* Special case of a FROM clause subquery implemented as a co-routine */
- if( pTabItem->viaCoroutine ){
- int regYield = pTabItem->regReturn;
- sqlite3VdbeAddOp3(v, OP_InitCoroutine, regYield, 0, pTabItem->addrFillSub);
- pLevel->p2 = sqlite3VdbeAddOp2(v, OP_Yield, regYield, addrBrk);
- VdbeCoverage(v);
- VdbeComment((v, "next row of \"%s\"", pTabItem->pTab->zName));
- pLevel->op = OP_Goto;
- }else
-
-#ifndef SQLITE_OMIT_VIRTUALTABLE
- if( (pLoop->wsFlags & WHERE_VIRTUALTABLE)!=0 ){
- /* Case 1: The table is a virtual-table. Use the VFilter and VNext
- ** to access the data.
- */
- int iReg; /* P3 Value for OP_VFilter */
- int addrNotFound;
- int nConstraint = pLoop->nLTerm;
-
- sqlite3ExprCachePush(pParse);
- iReg = sqlite3GetTempRange(pParse, nConstraint+2);
- addrNotFound = pLevel->addrBrk;
- for(j=0; j<nConstraint; j++){
- int iTarget = iReg+j+2;
- pTerm = pLoop->aLTerm[j];
- if( pTerm==0 ) continue;
- if( pTerm->eOperator & WO_IN ){
- codeEqualityTerm(pParse, pTerm, pLevel, j, bRev, iTarget);
- addrNotFound = pLevel->addrNxt;
- }else{
- sqlite3ExprCode(pParse, pTerm->pExpr->pRight, iTarget);
- }
- }
- sqlite3VdbeAddOp2(v, OP_Integer, pLoop->u.vtab.idxNum, iReg);
- sqlite3VdbeAddOp2(v, OP_Integer, nConstraint, iReg+1);
- sqlite3VdbeAddOp4(v, OP_VFilter, iCur, addrNotFound, iReg,
- pLoop->u.vtab.idxStr,
- pLoop->u.vtab.needFree ? P4_MPRINTF : P4_STATIC);
- VdbeCoverage(v);
- pLoop->u.vtab.needFree = 0;
- for(j=0; j<nConstraint && j<16; j++){
- if( (pLoop->u.vtab.omitMask>>j)&1 ){
- disableTerm(pLevel, pLoop->aLTerm[j]);
- }
- }
- pLevel->op = OP_VNext;
- pLevel->p1 = iCur;
- pLevel->p2 = sqlite3VdbeCurrentAddr(v);
- sqlite3ReleaseTempRange(pParse, iReg, nConstraint+2);
- sqlite3ExprCachePop(pParse);
- }else
-#endif /* SQLITE_OMIT_VIRTUALTABLE */
-
- if( (pLoop->wsFlags & WHERE_IPK)!=0
- && (pLoop->wsFlags & (WHERE_COLUMN_IN|WHERE_COLUMN_EQ))!=0
- ){
- /* Case 2: We can directly reference a single row using an
- ** equality comparison against the ROWID field. Or
- ** we reference multiple rows using a "rowid IN (...)"
- ** construct.
- */
- assert( pLoop->u.btree.nEq==1 );
- pTerm = pLoop->aLTerm[0];
- assert( pTerm!=0 );
- assert( pTerm->pExpr!=0 );
- assert( omitTable==0 );
- testcase( pTerm->wtFlags & TERM_VIRTUAL );
- iReleaseReg = ++pParse->nMem;
- iRowidReg = codeEqualityTerm(pParse, pTerm, pLevel, 0, bRev, iReleaseReg);
- if( iRowidReg!=iReleaseReg ) sqlite3ReleaseTempReg(pParse, iReleaseReg);
- addrNxt = pLevel->addrNxt;
- sqlite3VdbeAddOp2(v, OP_MustBeInt, iRowidReg, addrNxt); VdbeCoverage(v);
- sqlite3VdbeAddOp3(v, OP_NotExists, iCur, addrNxt, iRowidReg);
- VdbeCoverage(v);
- sqlite3ExprCacheAffinityChange(pParse, iRowidReg, 1);
- sqlite3ExprCacheStore(pParse, iCur, -1, iRowidReg);
- VdbeComment((v, "pk"));
- pLevel->op = OP_Noop;
- }else if( (pLoop->wsFlags & WHERE_IPK)!=0
- && (pLoop->wsFlags & WHERE_COLUMN_RANGE)!=0
- ){
- /* Case 3: We have an inequality comparison against the ROWID field.
- */
- int testOp = OP_Noop;
- int start;
- int memEndValue = 0;
- WhereTerm *pStart, *pEnd;
-
- assert( omitTable==0 );
- j = 0;
- pStart = pEnd = 0;
- if( pLoop->wsFlags & WHERE_BTM_LIMIT ) pStart = pLoop->aLTerm[j++];
- if( pLoop->wsFlags & WHERE_TOP_LIMIT ) pEnd = pLoop->aLTerm[j++];
- assert( pStart!=0 || pEnd!=0 );
- if( bRev ){
- pTerm = pStart;
- pStart = pEnd;
- pEnd = pTerm;
- }
- if( pStart ){
- Expr *pX; /* The expression that defines the start bound */
- int r1, rTemp; /* Registers for holding the start boundary */
-
- /* The following constant maps TK_xx codes into corresponding
- ** seek opcodes. It depends on a particular ordering of TK_xx
- */
- const u8 aMoveOp[] = {
- /* TK_GT */ OP_SeekGT,
- /* TK_LE */ OP_SeekLE,
- /* TK_LT */ OP_SeekLT,
- /* TK_GE */ OP_SeekGE
- };
- assert( TK_LE==TK_GT+1 ); /* Make sure the ordering.. */
- assert( TK_LT==TK_GT+2 ); /* ... of the TK_xx values... */
- assert( TK_GE==TK_GT+3 ); /* ... is correcct. */
-
- assert( (pStart->wtFlags & TERM_VNULL)==0 );
- testcase( pStart->wtFlags & TERM_VIRTUAL );
- pX = pStart->pExpr;
- assert( pX!=0 );
- testcase( pStart->leftCursor!=iCur ); /* transitive constraints */
- r1 = sqlite3ExprCodeTemp(pParse, pX->pRight, &rTemp);
- sqlite3VdbeAddOp3(v, aMoveOp[pX->op-TK_GT], iCur, addrBrk, r1);
- VdbeComment((v, "pk"));
- VdbeCoverageIf(v, pX->op==TK_GT);
- VdbeCoverageIf(v, pX->op==TK_LE);
- VdbeCoverageIf(v, pX->op==TK_LT);
- VdbeCoverageIf(v, pX->op==TK_GE);
- sqlite3ExprCacheAffinityChange(pParse, r1, 1);
- sqlite3ReleaseTempReg(pParse, rTemp);
- disableTerm(pLevel, pStart);
- }else{
- sqlite3VdbeAddOp2(v, bRev ? OP_Last : OP_Rewind, iCur, addrBrk);
- VdbeCoverageIf(v, bRev==0);
- VdbeCoverageIf(v, bRev!=0);
- }
- if( pEnd ){
- Expr *pX;
- pX = pEnd->pExpr;
- assert( pX!=0 );
- assert( (pEnd->wtFlags & TERM_VNULL)==0 );
- testcase( pEnd->leftCursor!=iCur ); /* Transitive constraints */
- testcase( pEnd->wtFlags & TERM_VIRTUAL );
- memEndValue = ++pParse->nMem;
- sqlite3ExprCode(pParse, pX->pRight, memEndValue);
- if( pX->op==TK_LT || pX->op==TK_GT ){
- testOp = bRev ? OP_Le : OP_Ge;
- }else{
- testOp = bRev ? OP_Lt : OP_Gt;
- }
- disableTerm(pLevel, pEnd);
- }
- start = sqlite3VdbeCurrentAddr(v);
- pLevel->op = bRev ? OP_Prev : OP_Next;
- pLevel->p1 = iCur;
- pLevel->p2 = start;
- assert( pLevel->p5==0 );
- if( testOp!=OP_Noop ){
- iRowidReg = ++pParse->nMem;
- sqlite3VdbeAddOp2(v, OP_Rowid, iCur, iRowidReg);
- sqlite3ExprCacheStore(pParse, iCur, -1, iRowidReg);
- sqlite3VdbeAddOp3(v, testOp, memEndValue, addrBrk, iRowidReg);
- VdbeCoverageIf(v, testOp==OP_Le);
- VdbeCoverageIf(v, testOp==OP_Lt);
- VdbeCoverageIf(v, testOp==OP_Ge);
- VdbeCoverageIf(v, testOp==OP_Gt);
- sqlite3VdbeChangeP5(v, SQLITE_AFF_NUMERIC | SQLITE_JUMPIFNULL);
- }
- }else if( pLoop->wsFlags & WHERE_INDEXED ){
- /* Case 4: A scan using an index.
- **
- ** The WHERE clause may contain zero or more equality
- ** terms ("==" or "IN" operators) that refer to the N
- ** left-most columns of the index. It may also contain
- ** inequality constraints (>, <, >= or <=) on the indexed
- ** column that immediately follows the N equalities. Only
- ** the right-most column can be an inequality - the rest must
- ** use the "==" and "IN" operators. For example, if the
- ** index is on (x,y,z), then the following clauses are all
- ** optimized:
- **
- ** x=5
- ** x=5 AND y=10
- ** x=5 AND y<10
- ** x=5 AND y>5 AND y<10
- ** x=5 AND y=5 AND z<=10
- **
- ** The z<10 term of the following cannot be used, only
- ** the x=5 term:
- **
- ** x=5 AND z<10
- **
- ** N may be zero if there are inequality constraints.
- ** If there are no inequality constraints, then N is at
- ** least one.
- **
- ** This case is also used when there are no WHERE clause
- ** constraints but an index is selected anyway, in order
- ** to force the output order to conform to an ORDER BY.
- */
- static const u8 aStartOp[] = {
- 0,
- 0,
- OP_Rewind, /* 2: (!start_constraints && startEq && !bRev) */
- OP_Last, /* 3: (!start_constraints && startEq && bRev) */
- OP_SeekGT, /* 4: (start_constraints && !startEq && !bRev) */
- OP_SeekLT, /* 5: (start_constraints && !startEq && bRev) */
- OP_SeekGE, /* 6: (start_constraints && startEq && !bRev) */
- OP_SeekLE /* 7: (start_constraints && startEq && bRev) */
- };
- static const u8 aEndOp[] = {
- OP_IdxGE, /* 0: (end_constraints && !bRev && !endEq) */
- OP_IdxGT, /* 1: (end_constraints && !bRev && endEq) */
- OP_IdxLE, /* 2: (end_constraints && bRev && !endEq) */
- OP_IdxLT, /* 3: (end_constraints && bRev && endEq) */
- };
- u16 nEq = pLoop->u.btree.nEq; /* Number of == or IN terms */
- int regBase; /* Base register holding constraint values */
- WhereTerm *pRangeStart = 0; /* Inequality constraint at range start */
- WhereTerm *pRangeEnd = 0; /* Inequality constraint at range end */
- int startEq; /* True if range start uses ==, >= or <= */
- int endEq; /* True if range end uses ==, >= or <= */
- int start_constraints; /* Start of range is constrained */
- int nConstraint; /* Number of constraint terms */
- Index *pIdx; /* The index we will be using */
- int iIdxCur; /* The VDBE cursor for the index */
- int nExtraReg = 0; /* Number of extra registers needed */
- int op; /* Instruction opcode */
- char *zStartAff; /* Affinity for start of range constraint */
- char cEndAff = 0; /* Affinity for end of range constraint */
- u8 bSeekPastNull = 0; /* True to seek past initial nulls */
- u8 bStopAtNull = 0; /* Add condition to terminate at NULLs */
-
- pIdx = pLoop->u.btree.pIndex;
- iIdxCur = pLevel->iIdxCur;
- assert( nEq>=pLoop->nSkip );
-
- /* If this loop satisfies a sort order (pOrderBy) request that
- ** was passed to this function to implement a "SELECT min(x) ..."
- ** query, then the caller will only allow the loop to run for
- ** a single iteration. This means that the first row returned
- ** should not have a NULL value stored in 'x'. If column 'x' is
- ** the first one after the nEq equality constraints in the index,
- ** this requires some special handling.
- */
- assert( pWInfo->pOrderBy==0
- || pWInfo->pOrderBy->nExpr==1
- || (pWInfo->wctrlFlags&WHERE_ORDERBY_MIN)==0 );
- if( (pWInfo->wctrlFlags&WHERE_ORDERBY_MIN)!=0
- && pWInfo->nOBSat>0
- && (pIdx->nKeyCol>nEq)
- ){
- assert( pLoop->nSkip==0 );
- bSeekPastNull = 1;
- nExtraReg = 1;
- }
-
- /* Find any inequality constraint terms for the start and end
- ** of the range.
- */
- j = nEq;
- 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
- ){
- bSeekPastNull = 1;
- }
- }
- assert( pRangeEnd==0 || (pRangeEnd->wtFlags & TERM_VNULL)==0 );
-
- /* Generate code to evaluate all constraint terms using == or IN
- ** and store the values of those terms in an array of registers
- ** starting at regBase.
- */
- regBase = codeAllEqualityTerms(pParse,pLevel,bRev,nExtraReg,&zStartAff);
- assert( zStartAff==0 || sqlite3Strlen30(zStartAff)>=nEq );
- if( zStartAff ) cEndAff = zStartAff[nEq];
- addrNxt = pLevel->addrNxt;
-
- /* If we are doing a reverse order scan on an ascending index, or
- ** a forward order scan on a descending index, interchange the
- ** start and end terms (pRangeStart and pRangeEnd).
- */
- if( (nEq<pIdx->nKeyCol && bRev==(pIdx->aSortOrder[nEq]==SQLITE_SO_ASC))
- || (bRev && pIdx->nKeyCol==nEq)
- ){
- SWAP(WhereTerm *, pRangeEnd, pRangeStart);
- SWAP(u8, bSeekPastNull, bStopAtNull);
- }
-
- testcase( pRangeStart && (pRangeStart->eOperator & WO_LE)!=0 );
- testcase( pRangeStart && (pRangeStart->eOperator & WO_GE)!=0 );
- testcase( pRangeEnd && (pRangeEnd->eOperator & WO_LE)!=0 );
- testcase( pRangeEnd && (pRangeEnd->eOperator & WO_GE)!=0 );
- startEq = !pRangeStart || pRangeStart->eOperator & (WO_LE|WO_GE);
- endEq = !pRangeEnd || pRangeEnd->eOperator & (WO_LE|WO_GE);
- start_constraints = pRangeStart || nEq>0;
-
- /* Seek the index cursor to the start of the range. */
- nConstraint = nEq;
- if( pRangeStart ){
- Expr *pRight = pRangeStart->pExpr->pRight;
- sqlite3ExprCode(pParse, pRight, regBase+nEq);
- whereLikeOptimizationStringFixup(v, pLevel, pRangeStart);
- if( (pRangeStart->wtFlags & TERM_VNULL)==0
- && sqlite3ExprCanBeNull(pRight)
- ){
- sqlite3VdbeAddOp2(v, OP_IsNull, regBase+nEq, addrNxt);
- VdbeCoverage(v);
- }
- if( zStartAff ){
- if( sqlite3CompareAffinity(pRight, zStartAff[nEq])==SQLITE_AFF_NONE){
- /* Since the comparison is to be performed with no conversions
- ** applied to the operands, set the affinity to apply to pRight to
- ** SQLITE_AFF_NONE. */
- zStartAff[nEq] = SQLITE_AFF_NONE;
- }
- if( sqlite3ExprNeedsNoAffinityChange(pRight, zStartAff[nEq]) ){
- zStartAff[nEq] = SQLITE_AFF_NONE;
- }
- }
- nConstraint++;
- testcase( pRangeStart->wtFlags & TERM_VIRTUAL );
- }else if( bSeekPastNull ){
- sqlite3VdbeAddOp2(v, OP_Null, 0, regBase+nEq);
- nConstraint++;
- startEq = 0;
- start_constraints = 1;
- }
- codeApplyAffinity(pParse, regBase, nConstraint - bSeekPastNull, zStartAff);
- op = aStartOp[(start_constraints<<2) + (startEq<<1) + bRev];
- assert( op!=0 );
- sqlite3VdbeAddOp4Int(v, op, iIdxCur, addrNxt, regBase, nConstraint);
- VdbeCoverage(v);
- VdbeCoverageIf(v, op==OP_Rewind); testcase( op==OP_Rewind );
- VdbeCoverageIf(v, op==OP_Last); testcase( op==OP_Last );
- VdbeCoverageIf(v, op==OP_SeekGT); testcase( op==OP_SeekGT );
- VdbeCoverageIf(v, op==OP_SeekGE); testcase( op==OP_SeekGE );
- VdbeCoverageIf(v, op==OP_SeekLE); testcase( op==OP_SeekLE );
- VdbeCoverageIf(v, op==OP_SeekLT); testcase( op==OP_SeekLT );
-
- /* Load the value for the inequality constraint at the end of the
- ** range (if any).
- */
- nConstraint = nEq;
- if( pRangeEnd ){
- 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)
- ){
- sqlite3VdbeAddOp2(v, OP_IsNull, regBase+nEq, addrNxt);
- VdbeCoverage(v);
- }
- if( sqlite3CompareAffinity(pRight, cEndAff)!=SQLITE_AFF_NONE
- && !sqlite3ExprNeedsNoAffinityChange(pRight, cEndAff)
- ){
- codeApplyAffinity(pParse, regBase+nEq, 1, &cEndAff);
- }
- nConstraint++;
- testcase( pRangeEnd->wtFlags & TERM_VIRTUAL );
- }else if( bStopAtNull ){
- sqlite3VdbeAddOp2(v, OP_Null, 0, regBase+nEq);
- endEq = 0;
- nConstraint++;
- }
- sqlite3DbFree(db, zStartAff);
-
- /* Top of the loop body */
- pLevel->p2 = sqlite3VdbeCurrentAddr(v);
-
- /* Check if the index cursor is past the end of the range. */
- if( nConstraint ){
- op = aEndOp[bRev*2 + endEq];
- sqlite3VdbeAddOp4Int(v, op, iIdxCur, addrNxt, regBase, nConstraint);
- testcase( op==OP_IdxGT ); VdbeCoverageIf(v, op==OP_IdxGT );
- testcase( op==OP_IdxGE ); VdbeCoverageIf(v, op==OP_IdxGE );
- testcase( op==OP_IdxLT ); VdbeCoverageIf(v, op==OP_IdxLT );
- testcase( op==OP_IdxLE ); VdbeCoverageIf(v, op==OP_IdxLE );
- }
-
- /* Seek the table cursor, if required */
- disableTerm(pLevel, pRangeStart);
- disableTerm(pLevel, pRangeEnd);
- if( omitTable ){
- /* pIdx is a covering index. No need to access the main table. */
- }else if( HasRowid(pIdx->pTable) ){
- iRowidReg = ++pParse->nMem;
- sqlite3VdbeAddOp2(v, OP_IdxRowid, iIdxCur, iRowidReg);
- sqlite3ExprCacheStore(pParse, iCur, -1, iRowidReg);
- sqlite3VdbeAddOp2(v, OP_Seek, iCur, iRowidReg); /* Deferred seek */
- }else if( iCur!=iIdxCur ){
- Index *pPk = sqlite3PrimaryKeyIndex(pIdx->pTable);
- iRowidReg = sqlite3GetTempRange(pParse, pPk->nKeyCol);
- for(j=0; j<pPk->nKeyCol; j++){
- k = sqlite3ColumnOfIndex(pIdx, pPk->aiColumn[j]);
- sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, k, iRowidReg+j);
- }
- sqlite3VdbeAddOp4Int(v, OP_NotFound, iCur, addrCont,
- iRowidReg, pPk->nKeyCol); VdbeCoverage(v);
- }
-
- /* Record the instruction used to terminate the loop. Disable
- ** WHERE clause terms made redundant by the index range scan.
- */
- if( pLoop->wsFlags & WHERE_ONEROW ){
- pLevel->op = OP_Noop;
- }else if( bRev ){
- pLevel->op = OP_Prev;
- }else{
- pLevel->op = OP_Next;
- }
- pLevel->p1 = iIdxCur;
- pLevel->p3 = (pLoop->wsFlags&WHERE_UNQ_WANTED)!=0 ? 1:0;
- if( (pLoop->wsFlags & WHERE_CONSTRAINT)==0 ){
- pLevel->p5 = SQLITE_STMTSTATUS_FULLSCAN_STEP;
- }else{
- assert( pLevel->p5==0 );
- }
- }else
-
-#ifndef SQLITE_OMIT_OR_OPTIMIZATION
- if( pLoop->wsFlags & WHERE_MULTI_OR ){
- /* Case 5: Two or more separately indexed terms connected by OR
- **
- ** Example:
- **
- ** CREATE TABLE t1(a,b,c,d);
- ** CREATE INDEX i1 ON t1(a);
- ** CREATE INDEX i2 ON t1(b);
- ** CREATE INDEX i3 ON t1(c);
- **
- ** SELECT * FROM t1 WHERE a=5 OR b=7 OR (c=11 AND d=13)
- **
- ** In the example, there are three indexed terms connected by OR.
- ** The top of the loop looks like this:
- **
- ** Null 1 # Zero the rowset in reg 1
- **
- ** Then, for each indexed term, the following. The arguments to
- ** RowSetTest are such that the rowid of the current row is inserted
- ** into the RowSet. If it is already present, control skips the
- ** Gosub opcode and jumps straight to the code generated by WhereEnd().
- **
- ** sqlite3WhereBegin(<term>)
- ** RowSetTest # Insert rowid into rowset
- ** Gosub 2 A
- ** sqlite3WhereEnd()
- **
- ** Following the above, code to terminate the loop. Label A, the target
- ** of the Gosub above, jumps to the instruction right after the Goto.
- **
- ** Null 1 # Zero the rowset in reg 1
- ** Goto B # The loop is finished.
- **
- ** A: <loop body> # Return data, whatever.
- **
- ** Return 2 # Jump back to the Gosub
- **
- ** B: <after the loop>
- **
- ** Added 2014-05-26: If the table is a WITHOUT ROWID table, then
- ** use an ephemeral index instead of a RowSet to record the primary
- ** keys of the rows we have already seen.
- **
- */
- WhereClause *pOrWc; /* The OR-clause broken out into subterms */
- SrcList *pOrTab; /* Shortened table list or OR-clause generation */
- Index *pCov = 0; /* Potential covering index (or NULL) */
- int iCovCur = pParse->nTab++; /* Cursor used for index scans (if any) */
-
- int regReturn = ++pParse->nMem; /* Register used with OP_Gosub */
- int regRowset = 0; /* Register for RowSet object */
- int regRowid = 0; /* Register holding rowid */
- int iLoopBody = sqlite3VdbeMakeLabel(v); /* Start of loop body */
- int iRetInit; /* Address of regReturn init */
- int untestedTerms = 0; /* Some terms not completely tested */
- int ii; /* Loop counter */
- u16 wctrlFlags; /* Flags for sub-WHERE clause */
- Expr *pAndExpr = 0; /* An ".. AND (...)" expression */
- Table *pTab = pTabItem->pTab;
-
- pTerm = pLoop->aLTerm[0];
- assert( pTerm!=0 );
- assert( pTerm->eOperator & WO_OR );
- assert( (pTerm->wtFlags & TERM_ORINFO)!=0 );
- pOrWc = &pTerm->u.pOrInfo->wc;
- pLevel->op = OP_Return;
- pLevel->p1 = regReturn;
-
- /* Set up a new SrcList in pOrTab containing the table being scanned
- ** by this loop in the a[0] slot and all notReady tables in a[1..] slots.
- ** This becomes the SrcList in the recursive call to sqlite3WhereBegin().
- */
- if( pWInfo->nLevel>1 ){
- int nNotReady; /* The number of notReady tables */
- struct SrcList_item *origSrc; /* Original list of tables */
- nNotReady = pWInfo->nLevel - iLevel - 1;
- pOrTab = sqlite3StackAllocRaw(db,
- sizeof(*pOrTab)+ nNotReady*sizeof(pOrTab->a[0]));
- if( pOrTab==0 ) return notReady;
- pOrTab->nAlloc = (u8)(nNotReady + 1);
- pOrTab->nSrc = pOrTab->nAlloc;
- memcpy(pOrTab->a, pTabItem, sizeof(*pTabItem));
- origSrc = pWInfo->pTabList->a;
- for(k=1; k<=nNotReady; k++){
- memcpy(&pOrTab->a[k], &origSrc[pLevel[k].iFrom], sizeof(pOrTab->a[k]));
- }
- }else{
- pOrTab = pWInfo->pTabList;
- }
-
- /* Initialize the rowset register to contain NULL. An SQL NULL is
- ** equivalent to an empty rowset. Or, create an ephemeral index
- ** capable of holding primary keys in the case of a WITHOUT ROWID.
- **
- ** Also initialize regReturn to contain the address of the instruction
- ** immediately following the OP_Return at the bottom of the loop. This
- ** is required in a few obscure LEFT JOIN cases where control jumps
- ** over the top of the loop into the body of it. In this case the
- ** correct response for the end-of-loop code (the OP_Return) is to
- ** fall through to the next instruction, just as an OP_Next does if
- ** called on an uninitialized cursor.
- */
- if( (pWInfo->wctrlFlags & WHERE_DUPLICATES_OK)==0 ){
- if( HasRowid(pTab) ){
- regRowset = ++pParse->nMem;
- sqlite3VdbeAddOp2(v, OP_Null, 0, regRowset);
- }else{
- Index *pPk = sqlite3PrimaryKeyIndex(pTab);
- regRowset = pParse->nTab++;
- sqlite3VdbeAddOp2(v, OP_OpenEphemeral, regRowset, pPk->nKeyCol);
- sqlite3VdbeSetP4KeyInfo(pParse, pPk);
- }
- regRowid = ++pParse->nMem;
- }
- iRetInit = sqlite3VdbeAddOp2(v, OP_Integer, 0, regReturn);
-
- /* If the original WHERE clause is z of the form: (x1 OR x2 OR ...) AND y
- ** Then for every term xN, evaluate as the subexpression: xN AND z
- ** That way, terms in y that are factored into the disjunction will
- ** be picked up by the recursive calls to sqlite3WhereBegin() below.
- **
- ** Actually, each subexpression is converted to "xN AND w" where w is
- ** the "interesting" terms of z - terms that did not originate in the
- ** ON or USING clause of a LEFT JOIN, and terms that are usable as
- ** indices.
- **
- ** This optimization also only applies if the (x1 OR x2 OR ...) term
- ** is not contained in the ON clause of a LEFT JOIN.
- ** See ticket http://www.sqlite.org/src/info/f2369304e4
- */
- if( pWC->nTerm>1 ){
- int iTerm;
- for(iTerm=0; iTerm<pWC->nTerm; iTerm++){
- Expr *pExpr = pWC->a[iTerm].pExpr;
- if( &pWC->a[iTerm] == pTerm ) continue;
- if( ExprHasProperty(pExpr, EP_FromJoin) ) continue;
- if( (pWC->a[iTerm].wtFlags & TERM_VIRTUAL)!=0 ) continue;
- if( (pWC->a[iTerm].eOperator & WO_ALL)==0 ) continue;
- testcase( pWC->a[iTerm].wtFlags & TERM_ORINFO );
- pExpr = sqlite3ExprDup(db, pExpr, 0);
- pAndExpr = sqlite3ExprAnd(db, pAndExpr, pExpr);
- }
- if( pAndExpr ){
- pAndExpr = sqlite3PExpr(pParse, TK_AND, 0, pAndExpr, 0);
- }
- }
-
- /* Run a separate WHERE clause for each term of the OR clause. After
- ** eliminating duplicates from other WHERE clauses, the action for each
- ** sub-WHERE clause is to to invoke the main loop body as a subroutine.
- */
- wctrlFlags = WHERE_OMIT_OPEN_CLOSE
- | WHERE_FORCE_TABLE
- | 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 ){
- WhereInfo *pSubWInfo; /* Info for single OR-term scan */
- Expr *pOrExpr = pOrTerm->pExpr; /* Current OR clause term */
- int j1 = 0; /* Address of jump operation */
- if( pAndExpr && !ExprHasProperty(pOrExpr, EP_FromJoin) ){
- pAndExpr->pLeft = pOrExpr;
- pOrExpr = pAndExpr;
- }
- /* Loop through table entries that match term pOrTerm. */
- WHERETRACE(0xffff, ("Subplan for OR-clause:\n"));
- pSubWInfo = sqlite3WhereBegin(pParse, pOrTab, pOrExpr, 0, 0,
- wctrlFlags, iCovCur);
- assert( pSubWInfo || pParse->nErr || db->mallocFailed );
- if( pSubWInfo ){
- WhereLoop *pSubLoop;
- int addrExplain = explainOneScan(
- pParse, pOrTab, &pSubWInfo->a[0], iLevel, pLevel->iFrom, 0
- );
- addScanStatus(v, pOrTab, &pSubWInfo->a[0], addrExplain);
-
- /* This is the sub-WHERE clause body. First skip over
- ** duplicate rows from prior sub-WHERE clauses, and record the
- ** rowid (or PRIMARY KEY) for the current row so that the same
- ** row will be skipped in subsequent sub-WHERE clauses.
- */
- if( (pWInfo->wctrlFlags & WHERE_DUPLICATES_OK)==0 ){
- int r;
- int iSet = ((ii==pOrWc->nTerm-1)?-1:ii);
- if( HasRowid(pTab) ){
- r = sqlite3ExprCodeGetColumn(pParse, pTab, -1, iCur, regRowid, 0);
- j1 = sqlite3VdbeAddOp4Int(v, OP_RowSetTest, regRowset, 0, r,iSet);
- VdbeCoverage(v);
- }else{
- Index *pPk = sqlite3PrimaryKeyIndex(pTab);
- int nPk = pPk->nKeyCol;
- int iPk;
-
- /* Read the PK into an array of temp registers. */
- r = sqlite3GetTempRange(pParse, nPk);
- for(iPk=0; iPk<nPk; iPk++){
- int iCol = pPk->aiColumn[iPk];
- sqlite3ExprCodeGetColumn(pParse, pTab, iCol, iCur, r+iPk, 0);
- }
-
- /* Check if the temp table already contains this key. If so,
- ** the row has already been included in the result set and
- ** can be ignored (by jumping past the Gosub below). Otherwise,
- ** insert the key into the temp table and proceed with processing
- ** the row.
- **
- ** Use some of the same optimizations as OP_RowSetTest: If iSet
- ** is zero, assume that the key cannot already be present in
- ** the temp table. And if iSet is -1, assume that there is no
- ** need to insert the key into the temp table, as it will never
- ** be tested for. */
- if( iSet ){
- j1 = sqlite3VdbeAddOp4Int(v, OP_Found, regRowset, 0, r, nPk);
- VdbeCoverage(v);
- }
- if( iSet>=0 ){
- sqlite3VdbeAddOp3(v, OP_MakeRecord, r, nPk, regRowid);
- sqlite3VdbeAddOp3(v, OP_IdxInsert, regRowset, regRowid, 0);
- if( iSet ) sqlite3VdbeChangeP5(v, OPFLAG_USESEEKRESULT);
- }
-
- /* Release the array of temp registers */
- sqlite3ReleaseTempRange(pParse, r, nPk);
- }
- }
-
- /* Invoke the main loop body as a subroutine */
- sqlite3VdbeAddOp2(v, OP_Gosub, regReturn, iLoopBody);
-
- /* Jump here (skipping the main loop body subroutine) if the
- ** current sub-WHERE row is a duplicate from prior sub-WHEREs. */
- if( j1 ) sqlite3VdbeJumpHere(v, j1);
-
- /* The pSubWInfo->untestedTerms flag means that this OR term
- ** contained one or more AND term from a notReady table. The
- ** terms from the notReady table could not be tested and will
- ** need to be tested later.
- */
- if( pSubWInfo->untestedTerms ) untestedTerms = 1;
-
- /* If all of the OR-connected terms are optimized using the same
- ** index, and the index is opened using the same cursor number
- ** by each call to sqlite3WhereBegin() made by this loop, it may
- ** be possible to use that index as a covering index.
- **
- ** If the call to sqlite3WhereBegin() above resulted in a scan that
- ** uses an index, and this is either the first OR-connected term
- ** processed or the index is the same as that used by all previous
- ** terms, set pCov to the candidate covering index. Otherwise, set
- ** pCov to NULL to indicate that no candidate covering index will
- ** be available.
- */
- pSubLoop = pSubWInfo->a[0].pWLoop;
- assert( (pSubLoop->wsFlags & WHERE_AUTO_INDEX)==0 );
- if( (pSubLoop->wsFlags & WHERE_INDEXED)!=0
- && (ii==0 || pSubLoop->u.btree.pIndex==pCov)
- && (HasRowid(pTab) || !IsPrimaryKeyIndex(pSubLoop->u.btree.pIndex))
- ){
- assert( pSubWInfo->a[0].iIdxCur==iCovCur );
- pCov = pSubLoop->u.btree.pIndex;
- wctrlFlags |= WHERE_REOPEN_IDX;
- }else{
- pCov = 0;
- }
-
- /* Finish the loop through table entries that match term pOrTerm. */
- sqlite3WhereEnd(pSubWInfo);
- }
- }
- }
- pLevel->u.pCovidx = pCov;
- if( pCov ) pLevel->iIdxCur = iCovCur;
- if( pAndExpr ){
- pAndExpr->pLeft = 0;
- sqlite3ExprDelete(db, pAndExpr);
- }
- sqlite3VdbeChangeP1(v, iRetInit, sqlite3VdbeCurrentAddr(v));
- sqlite3VdbeAddOp2(v, OP_Goto, 0, pLevel->addrBrk);
- sqlite3VdbeResolveLabel(v, iLoopBody);
-
- if( pWInfo->nLevel>1 ) sqlite3StackFree(db, pOrTab);
- if( !untestedTerms ) disableTerm(pLevel, pTerm);
- }else
-#endif /* SQLITE_OMIT_OR_OPTIMIZATION */
-
- {
- /* Case 6: There is no usable index. We must do a complete
- ** scan of the entire table.
- */
- static const u8 aStep[] = { OP_Next, OP_Prev };
- static const u8 aStart[] = { OP_Rewind, OP_Last };
- assert( bRev==0 || bRev==1 );
- if( pTabItem->isRecursive ){
- /* Tables marked isRecursive have only a single row that is stored in
- ** a pseudo-cursor. No need to Rewind or Next such cursors. */
- pLevel->op = OP_Noop;
- }else{
- pLevel->op = aStep[bRev];
- pLevel->p1 = iCur;
- pLevel->p2 = 1 + sqlite3VdbeAddOp2(v, aStart[bRev], iCur, addrBrk);
- VdbeCoverageIf(v, bRev==0);
- VdbeCoverageIf(v, bRev!=0);
- pLevel->p5 = SQLITE_STMTSTATUS_FULLSCAN_STEP;
- }
- }
-
-#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
- pLevel->addrVisit = sqlite3VdbeCurrentAddr(v);
-#endif
-
- /* Insert code to test every subexpression that can be completely
- ** computed using the current set of tables.
- */
- 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;
- if( (pTerm->prereqAll & pLevel->notReady)!=0 ){
- testcase( pWInfo->untestedTerms==0
- && (pWInfo->wctrlFlags & WHERE_ONETABLE_ONLY)!=0 );
- pWInfo->untestedTerms = 1;
- continue;
- }
- pE = pTerm->pExpr;
- assert( pE!=0 );
- 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;
- }
-
- /* Insert code to test for implied constraints based on transitivity
- ** of the "==" operator.
- **
- ** Example: If the WHERE clause contains "t1.a=t2.b" and "t2.b=123"
- ** and we are coding the t1 loop and the t2 loop has not yet coded,
- ** then we cannot use the "t1.a=t2.b" constraint, but we can code
- ** the implied "t1.a=123" constraint.
- */
- for(pTerm=pWC->a, j=pWC->nTerm; j>0; j--, pTerm++){
- Expr *pE, *pEAlt;
- WhereTerm *pAlt;
- if( pTerm->wtFlags & (TERM_VIRTUAL|TERM_CODED) ) continue;
- if( pTerm->eOperator!=(WO_EQUIV|WO_EQ) ) continue;
- if( pTerm->leftCursor!=iCur ) continue;
- if( pLevel->iLeftJoin ) continue;
- pE = pTerm->pExpr;
- assert( !ExprHasProperty(pE, EP_FromJoin) );
- assert( (pTerm->prereqRight & pLevel->notReady)!=0 );
- pAlt = findTerm(pWC, iCur, pTerm->u.leftColumn, notReady, WO_EQ|WO_IN, 0);
- if( pAlt==0 ) continue;
- if( pAlt->wtFlags & (TERM_CODED) ) continue;
- testcase( pAlt->eOperator & WO_EQ );
- testcase( pAlt->eOperator & WO_IN );
- VdbeModuleComment((v, "begin transitive constraint"));
- pEAlt = sqlite3StackAllocRaw(db, sizeof(*pEAlt));
- if( pEAlt ){
- *pEAlt = *pAlt->pExpr;
- pEAlt->pLeft = pE->pLeft;
- sqlite3ExprIfFalse(pParse, pEAlt, addrCont, SQLITE_JUMPIFNULL);
- sqlite3StackFree(db, pEAlt);
- }
- }
-
- /* For a LEFT OUTER JOIN, generate code that will record the fact that
- ** at least one row of the right table has matched the left table.
- */
- if( pLevel->iLeftJoin ){
- pLevel->addrFirst = sqlite3VdbeCurrentAddr(v);
- sqlite3VdbeAddOp2(v, OP_Integer, 1, pLevel->iLeftJoin);
- VdbeComment((v, "record LEFT JOIN hit"));
- sqlite3ExprCacheClear(pParse);
- for(pTerm=pWC->a, j=0; j<pWC->nTerm; j++, pTerm++){
- testcase( pTerm->wtFlags & TERM_VIRTUAL );
- testcase( pTerm->wtFlags & TERM_CODED );
- if( pTerm->wtFlags & (TERM_VIRTUAL|TERM_CODED) ) continue;
- if( (pTerm->prereqAll & pLevel->notReady)!=0 ){
- assert( pWInfo->untestedTerms );
- continue;
- }
- assert( pTerm->pExpr );
- sqlite3ExprIfFalse(pParse, pTerm->pExpr, addrCont, SQLITE_JUMPIFNULL);
- pTerm->wtFlags |= TERM_CODED;
- }
- }
-
- return pLevel->notReady;
-}
#ifdef WHERETRACE_ENABLED
/*
@@ -120332,9 +127606,10 @@ static void whereTermPrint(WhereTerm *pTerm, int iTerm){
if( pTerm->wtFlags & TERM_VIRTUAL ) zType[0] = 'V';
if( pTerm->eOperator & WO_EQUIV ) zType[1] = 'E';
if( ExprHasProperty(pTerm->pExpr, EP_FromJoin) ) zType[2] = 'L';
- sqlite3DebugPrintf("TERM-%-3d %p %s cursor=%-3d prob=%-3d op=0x%03x\n",
- iTerm, pTerm, zType, pTerm->leftCursor, pTerm->truthProb,
- pTerm->eOperator);
+ sqlite3DebugPrintf(
+ "TERM-%-3d %p %s cursor=%-3d prob=%-3d op=0x%03x wtFlags=0x%04x\n",
+ iTerm, pTerm, zType, pTerm->leftCursor, pTerm->truthProb,
+ pTerm->eOperator, pTerm->wtFlags);
sqlite3TreeViewExpr(0, pTerm->pExpr, 0);
}
}
@@ -120346,11 +127621,12 @@ static void whereTermPrint(WhereTerm *pTerm, int iTerm){
*/
static void whereLoopPrint(WhereLoop *p, WhereClause *pWC){
WhereInfo *pWInfo = pWC->pWInfo;
- int nb = 1+(pWInfo->pTabList->nSrc+7)/8;
+ int nb = 1+(pWInfo->pTabList->nSrc+3)/4;
struct SrcList_item *pItem = pWInfo->pTabList->a + p->iTab;
Table *pTab = pItem->pTab;
+ Bitmask mAll = (((Bitmask)1)<<(nb*4)) - 1;
sqlite3DebugPrintf("%c%2d.%0*llx.%0*llx", p->cId,
- p->iTab, nb, p->maskSelf, nb, p->prereq);
+ p->iTab, nb, p->maskSelf, nb, p->prereq & mAll);
sqlite3DebugPrintf(" %12s",
pItem->zAlias ? pItem->zAlias : pTab->zName);
if( (p->wsFlags & WHERE_VIRTUALTABLE)==0 ){
@@ -120435,8 +127711,8 @@ static int whereLoopResize(sqlite3 *db, WhereLoop *p, int n){
WhereTerm **paNew;
if( p->nLSlot>=n ) return SQLITE_OK;
n = (n+7)&~7;
- paNew = sqlite3DbMallocRaw(db, sizeof(p->aLTerm[0])*n);
- if( paNew==0 ) return SQLITE_NOMEM;
+ paNew = sqlite3DbMallocRawNN(db, sizeof(p->aLTerm[0])*n);
+ if( paNew==0 ) return SQLITE_NOMEM_BKPT;
memcpy(paNew, p->aLTerm, sizeof(p->aLTerm[0])*p->nLSlot);
if( p->aLTerm!=p->aLTermSpace ) sqlite3DbFree(db, p->aLTerm);
p->aLTerm = paNew;
@@ -120451,7 +127727,7 @@ static int whereLoopXfer(sqlite3 *db, WhereLoop *pTo, WhereLoop *pFrom){
whereLoopClearUnion(db, pTo);
if( whereLoopResize(db, pTo, pFrom->nLTerm) ){
memset(&pTo->u, 0, sizeof(pTo->u));
- return SQLITE_NOMEM;
+ return SQLITE_NOMEM_BKPT;
}
memcpy(pTo, pFrom, WHERE_LOOP_XFER_SZ);
memcpy(pTo->aLTerm, pFrom->aLTerm, pTo->nLTerm*sizeof(pTo->aLTerm[0]));
@@ -120483,7 +127759,7 @@ static void whereInfoFree(sqlite3 *db, WhereInfo *pWInfo){
sqlite3DbFree(db, pLevel->u.in.aInLoop);
}
}
- whereClauseClear(&pWInfo->sWC);
+ sqlite3WhereClauseClear(&pWInfo->sWC);
while( pWInfo->pLoops ){
WhereLoop *p = pWInfo->pLoops;
pWInfo->pLoops = p->pNextLoop;
@@ -120675,23 +127951,26 @@ static int whereLoopInsert(WhereLoopBuilder *pBuilder, WhereLoop *pTemplate){
WhereLoop **ppPrev, *p;
WhereInfo *pWInfo = pBuilder->pWInfo;
sqlite3 *db = pWInfo->pParse->db;
+ int rc;
/* If pBuilder->pOrSet is defined, then only keep track of the costs
** and prereqs.
*/
if( pBuilder->pOrSet!=0 ){
+ if( pTemplate->nLTerm ){
#if WHERETRACE_ENABLED
- u16 n = pBuilder->pOrSet->n;
- int x =
+ u16 n = pBuilder->pOrSet->n;
+ int x =
#endif
- whereOrInsert(pBuilder->pOrSet, pTemplate->prereq, pTemplate->rRun,
+ whereOrInsert(pBuilder->pOrSet, pTemplate->prereq, pTemplate->rRun,
pTemplate->nOut);
#if WHERETRACE_ENABLED /* 0x8 */
- if( sqlite3WhereTrace & 0x8 ){
- sqlite3DebugPrintf(x?" or-%d: ":" or-X: ", n);
- whereLoopPrint(pTemplate, pBuilder->pWC);
- }
+ if( sqlite3WhereTrace & 0x8 ){
+ sqlite3DebugPrintf(x?" or-%d: ":" or-X: ", n);
+ whereLoopPrint(pTemplate, pBuilder->pWC);
+ }
#endif
+ }
return SQLITE_OK;
}
@@ -120730,8 +128009,8 @@ static int whereLoopInsert(WhereLoopBuilder *pBuilder, WhereLoop *pTemplate){
#endif
if( p==0 ){
/* Allocate a new WhereLoop to add to the end of the list */
- *ppPrev = p = sqlite3DbMallocRaw(db, sizeof(WhereLoop));
- if( p==0 ) return SQLITE_NOMEM;
+ *ppPrev = p = sqlite3DbMallocRawNN(db, sizeof(WhereLoop));
+ if( p==0 ) return SQLITE_NOMEM_BKPT;
whereLoopInit(p);
p->pNextLoop = 0;
}else{
@@ -120755,14 +128034,14 @@ static int whereLoopInsert(WhereLoopBuilder *pBuilder, WhereLoop *pTemplate){
whereLoopDelete(db, pToDel);
}
}
- whereLoopXfer(db, p, pTemplate);
+ rc = whereLoopXfer(db, p, pTemplate);
if( (p->wsFlags & WHERE_VIRTUALTABLE)==0 ){
Index *pIndex = p->u.btree.pIndex;
if( pIndex && pIndex->tnum==0 ){
p->u.btree.pIndex = 0;
}
}
- return SQLITE_OK;
+ return rc;
}
/*
@@ -120824,8 +128103,9 @@ static void whereLoopOutputAdjust(
/* In the absence of explicit truth probabilities, use heuristics to
** guess a reasonable truth probability. */
pLoop->nOut--;
- if( pTerm->eOperator&WO_EQ ){
+ if( pTerm->eOperator&(WO_EQ|WO_IS) ){
Expr *pRight = pTerm->pExpr->pRight;
+ testcase( pTerm->pExpr->op==TK_IS );
if( sqlite3ExprIsInteger(pRight, &k) && k>=(-1) && k<=1 ){
k = 10;
}else{
@@ -120880,37 +128160,33 @@ static int whereLoopAddBtreeIndex(
u16 saved_nSkip; /* Original value of pNew->nSkip */
u32 saved_wsFlags; /* Original value of pNew->wsFlags */
LogEst saved_nOut; /* Original value of pNew->nOut */
- int iCol; /* Index of the column in the table */
int rc = SQLITE_OK; /* Return code */
LogEst rSize; /* Number of rows in the table */
LogEst rLogSize; /* Logarithm of table size */
WhereTerm *pTop = 0, *pBtm = 0; /* Top and bottom range constraints */
pNew = pBuilder->pNew;
- if( db->mallocFailed ) return SQLITE_NOMEM;
+ if( db->mallocFailed ) return SQLITE_NOMEM_BKPT;
assert( (pNew->wsFlags & WHERE_VIRTUALTABLE)==0 );
assert( (pNew->wsFlags & WHERE_TOP_LIMIT)==0 );
if( pNew->wsFlags & WHERE_BTM_LIMIT ){
opMask = WO_LT|WO_LE;
- }else if( pProbe->tnum<=0 || (pSrc->jointype & JT_LEFT)!=0 ){
- opMask = WO_EQ|WO_IN|WO_GT|WO_GE|WO_LT|WO_LE;
}else{
- opMask = WO_EQ|WO_IN|WO_ISNULL|WO_GT|WO_GE|WO_LT|WO_LE;
+ opMask = WO_EQ|WO_IN|WO_GT|WO_GE|WO_LT|WO_LE|WO_ISNULL|WO_IS;
}
if( pProbe->bUnordered ) opMask &= ~(WO_GT|WO_GE|WO_LT|WO_LE);
assert( pNew->u.btree.nEq<pProbe->nColumn );
- iCol = pProbe->aiColumn[pNew->u.btree.nEq];
- pTerm = whereScanInit(&scan, pBuilder->pWC, pSrc->iCursor, iCol,
- opMask, pProbe);
saved_nEq = pNew->u.btree.nEq;
saved_nSkip = pNew->nSkip;
saved_nLTerm = pNew->nLTerm;
saved_wsFlags = pNew->wsFlags;
saved_prereq = pNew->prereq;
saved_nOut = pNew->nOut;
+ pTerm = whereScanInit(&scan, pBuilder->pWC, pSrc->iCursor, saved_nEq,
+ opMask, pProbe);
pNew->rSetup = 0;
rSize = pProbe->aiRowLogEst[0];
rLogSize = estLog(rSize);
@@ -120923,7 +128199,7 @@ static int whereLoopAddBtreeIndex(
int nRecValid = pBuilder->nRecValid;
#endif
if( (eOp==WO_ISNULL || (pTerm->wtFlags&TERM_VNULL)!=0)
- && (iCol<0 || pSrc->pTab->aCol[iCol].notNull)
+ && indexColumnNotNull(pProbe, saved_nEq)
){
continue; /* ignore IS [NOT] NULL constraints on NOT NULL columns */
}
@@ -120933,6 +128209,18 @@ static int whereLoopAddBtreeIndex(
** to mix with a lower range bound from some other source */
if( pTerm->wtFlags & TERM_LIKEOPT && pTerm->eOperator==WO_LT ) continue;
+ /* Do not allow IS constraints from the WHERE clause to be used by the
+ ** right table of a LEFT JOIN. Only constraints in the ON clause are
+ ** allowed */
+ if( (pSrc->fg.jointype & JT_LEFT)!=0
+ && !ExprHasProperty(pTerm->pExpr, EP_FromJoin)
+ && (eOp & (WO_IS|WO_ISNULL))!=0
+ ){
+ testcase( eOp & WO_IS );
+ testcase( eOp & WO_ISNULL );
+ continue;
+ }
+
pNew->wsFlags = saved_wsFlags;
pNew->u.btree.nEq = saved_nEq;
pNew->nLTerm = saved_nLTerm;
@@ -120959,9 +128247,13 @@ static int whereLoopAddBtreeIndex(
assert( nIn>0 ); /* RHS always has 2 or more terms... The parser
** changes "x IN (?)" into "x=?". */
- }else if( eOp & (WO_EQ) ){
+ }else if( eOp & (WO_EQ|WO_IS) ){
+ int iCol = pProbe->aiColumn[saved_nEq];
pNew->wsFlags |= WHERE_COLUMN_EQ;
- if( iCol<0 || (nInMul==0 && pNew->u.btree.nEq==pProbe->nKeyCol-1) ){
+ assert( saved_nEq==pNew->u.btree.nEq );
+ if( iCol==XN_ROWID
+ || (iCol>0 && nInMul==0 && saved_nEq==pProbe->nKeyCol-1)
+ ){
if( iCol>=0 && pProbe->uniqNotNull==0 ){
pNew->wsFlags |= WHERE_UNQ_WANTED;
}else{
@@ -121009,10 +128301,10 @@ static int whereLoopAddBtreeIndex(
whereRangeScanEst(pParse, pBuilder, pBtm, pTop, pNew);
}else{
int nEq = ++pNew->u.btree.nEq;
- assert( eOp & (WO_ISNULL|WO_EQ|WO_IN) );
+ assert( eOp & (WO_ISNULL|WO_EQ|WO_IN|WO_IS) );
assert( pNew->nOut==saved_nOut );
- if( pTerm->truthProb<=0 && iCol>=0 ){
+ if( pTerm->truthProb<=0 && pProbe->aiColumn[saved_nEq]>=0 ){
assert( (eOp & WO_IN) || nIn==0 );
testcase( eOp & WO_IN );
pNew->nOut += pTerm->truthProb;
@@ -121026,8 +128318,9 @@ static int whereLoopAddBtreeIndex(
&& ((eOp & WO_IN)==0 || !ExprHasProperty(pTerm->pExpr, EP_xIsSelect))
){
Expr *pExpr = pTerm->pExpr;
- if( (eOp & (WO_EQ|WO_ISNULL))!=0 ){
+ if( (eOp & (WO_EQ|WO_ISNULL|WO_IS))!=0 ){
testcase( eOp & WO_EQ );
+ testcase( eOp & WO_IS );
testcase( eOp & WO_ISNULL );
rc = whereEqualScanEst(pParse, pBuilder, pExpr->pRight, &nOut);
}else{
@@ -121146,18 +128439,25 @@ static int indexMightHelpWithOrderBy(
int iCursor
){
ExprList *pOB;
+ ExprList *aColExpr;
int ii, jj;
if( pIndex->bUnordered ) return 0;
if( (pOB = pBuilder->pWInfo->pOrderBy)==0 ) return 0;
for(ii=0; ii<pOB->nExpr; ii++){
Expr *pExpr = sqlite3ExprSkipCollate(pOB->a[ii].pExpr);
- if( pExpr->op!=TK_COLUMN ) return 0;
- if( pExpr->iTable==iCursor ){
+ if( pExpr->op==TK_COLUMN && pExpr->iTable==iCursor ){
if( pExpr->iColumn<0 ) return 1;
for(jj=0; jj<pIndex->nKeyCol; jj++){
if( pExpr->iColumn==pIndex->aiColumn[jj] ) return 1;
}
+ }else if( (aColExpr = pIndex->aColExpr)!=0 ){
+ for(jj=0; jj<pIndex->nKeyCol; jj++){
+ if( pIndex->aiColumn[jj]!=XN_EXPR ) continue;
+ if( sqlite3ExprCompare(pExpr,aColExpr->a[jj].pExpr,iCursor)==0 ){
+ return 1;
+ }
+ }
}
}
return 0;
@@ -121187,6 +128487,10 @@ static Bitmask columnsInIndex(Index *pIdx){
static int whereUsablePartialIndex(int iTab, WhereClause *pWC, Expr *pWhere){
int i;
WhereTerm *pTerm;
+ while( pWhere->op==TK_AND ){
+ if( !whereUsablePartialIndex(iTab,pWC,pWhere->pLeft) ) return 0;
+ pWhere = pWhere->pRight;
+ }
for(i=0, pTerm=pWC->a; i<pWC->nTerm; i++, pTerm++){
Expr *pExpr = pTerm->pExpr;
if( sqlite3ExprImpliesExpr(pExpr, pWhere, iTab)
@@ -121236,7 +128540,7 @@ static int whereUsablePartialIndex(int iTab, WhereClause *pWC, Expr *pWhere){
*/
static int whereLoopAddBtree(
WhereLoopBuilder *pBuilder, /* WHERE clause information */
- Bitmask mExtra /* Extra prerequesites for using this table */
+ Bitmask mPrereq /* Extra prerequesites for using this table */
){
WhereInfo *pWInfo; /* WHERE analysis context */
Index *pProbe; /* An index we are evaluating */
@@ -121262,9 +128566,9 @@ static int whereLoopAddBtree(
pWC = pBuilder->pWC;
assert( !IsVirtual(pSrc->pTab) );
- if( pSrc->pIndex ){
+ if( pSrc->pIBIndex ){
/* An INDEXED BY clause specifies a particular index to use */
- pProbe = pSrc->pIndex;
+ pProbe = pSrc->pIBIndex;
}else if( !HasRowid(pTab) ){
pProbe = pTab->pIndex;
}else{
@@ -121284,7 +128588,7 @@ static int whereLoopAddBtree(
aiRowEstPk[0] = pTab->nRowLogEst;
aiRowEstPk[1] = 0;
pFirst = pSrc->pTab->pIndex;
- if( pSrc->notIndexed==0 ){
+ if( pSrc->fg.notIndexed==0 ){
/* The real indices of the table are only considered if the
** NOT INDEXED qualifier is omitted from the FROM clause */
sPk.pNext = pFirst;
@@ -121296,15 +128600,14 @@ static int whereLoopAddBtree(
#ifndef SQLITE_OMIT_AUTOMATIC_INDEX
/* Automatic indexes */
- if( !pBuilder->pOrSet
+ if( !pBuilder->pOrSet /* Not part of an OR optimization */
&& (pWInfo->wctrlFlags & WHERE_NO_AUTOINDEX)==0
&& (pWInfo->pParse->db->flags & SQLITE_AutoIndex)!=0
- && pSrc->pIndex==0
- && !pSrc->viaCoroutine
- && !pSrc->notIndexed
- && HasRowid(pTab)
- && !pSrc->isCorrelated
- && !pSrc->isRecursive
+ && pSrc->pIBIndex==0 /* Has no INDEXED BY clause */
+ && !pSrc->fg.notIndexed /* Has no NOT INDEXED clause */
+ && HasRowid(pTab) /* Not WITHOUT ROWID table. (FIXME: Why not?) */
+ && !pSrc->fg.isCorrelated /* Not a correlated subquery */
+ && !pSrc->fg.isRecursive /* Not a recursive common table expression. */
){
/* Generate auto-index WhereLoops */
WhereTerm *pTerm;
@@ -121337,7 +128640,7 @@ static int whereLoopAddBtree(
pNew->nOut = 43; assert( 43==sqlite3LogEst(20) );
pNew->rRun = sqlite3LogEstAdd(rLogSize,pNew->nOut);
pNew->wsFlags = WHERE_AUTO_INDEX;
- pNew->prereq = mExtra | pTerm->prereqRight;
+ pNew->prereq = mPrereq | pTerm->prereqRight;
rc = whereLoopInsert(pBuilder, pNew);
}
}
@@ -121358,7 +128661,7 @@ static int whereLoopAddBtree(
pNew->nLTerm = 0;
pNew->iSortIdx = 0;
pNew->rSetup = 0;
- pNew->prereq = mExtra;
+ pNew->prereq = mPrereq;
pNew->nOut = rSize;
pNew->u.btree.pIndex = pProbe;
b = indexMightHelpWithOrderBy(pBuilder, pProbe, pSrc->iCursor);
@@ -121425,176 +128728,292 @@ static int whereLoopAddBtree(
/* If there was an INDEXED BY clause, then only that one index is
** considered. */
- if( pSrc->pIndex ) break;
+ if( pSrc->pIBIndex ) break;
}
return rc;
}
#ifndef SQLITE_OMIT_VIRTUALTABLE
+
+/*
+** Argument pIdxInfo is already populated with all constraints that may
+** be used by the virtual table identified by pBuilder->pNew->iTab. This
+** function marks a subset of those constraints usable, invokes the
+** xBestIndex method and adds the returned plan to pBuilder.
+**
+** A constraint is marked usable if:
+**
+** * Argument mUsable indicates that its prerequisites are available, and
+**
+** * It is not one of the operators specified in the mExclude mask passed
+** as the fourth argument (which in practice is either WO_IN or 0).
+**
+** Argument mPrereq is a mask of tables that must be scanned before the
+** virtual table in question. These are added to the plans prerequisites
+** before it is added to pBuilder.
+**
+** Output parameter *pbIn is set to true if the plan added to pBuilder
+** uses one or more WO_IN terms, or false otherwise.
+*/
+static int whereLoopAddVirtualOne(
+ WhereLoopBuilder *pBuilder,
+ Bitmask mPrereq, /* Mask of tables that must be used. */
+ Bitmask mUsable, /* Mask of usable tables */
+ u16 mExclude, /* Exclude terms using these operators */
+ sqlite3_index_info *pIdxInfo, /* Populated object for xBestIndex */
+ int *pbIn /* OUT: True if plan uses an IN(...) op */
+){
+ WhereClause *pWC = pBuilder->pWC;
+ struct sqlite3_index_constraint *pIdxCons;
+ struct sqlite3_index_constraint_usage *pUsage = pIdxInfo->aConstraintUsage;
+ int i;
+ int mxTerm;
+ int rc = SQLITE_OK;
+ WhereLoop *pNew = pBuilder->pNew;
+ Parse *pParse = pBuilder->pWInfo->pParse;
+ struct SrcList_item *pSrc = &pBuilder->pWInfo->pTabList->a[pNew->iTab];
+ int nConstraint = pIdxInfo->nConstraint;
+
+ assert( (mUsable & mPrereq)==mPrereq );
+ *pbIn = 0;
+ pNew->prereq = mPrereq;
+
+ /* Set the usable flag on the subset of constraints identified by
+ ** arguments mUsable and mExclude. */
+ pIdxCons = *(struct sqlite3_index_constraint**)&pIdxInfo->aConstraint;
+ for(i=0; i<nConstraint; i++, pIdxCons++){
+ WhereTerm *pTerm = &pWC->a[pIdxCons->iTermOffset];
+ pIdxCons->usable = 0;
+ if( (pTerm->prereqRight & mUsable)==pTerm->prereqRight
+ && (pTerm->eOperator & mExclude)==0
+ ){
+ pIdxCons->usable = 1;
+ }
+ }
+
+ /* Initialize the output fields of the sqlite3_index_info structure */
+ memset(pUsage, 0, sizeof(pUsage[0])*nConstraint);
+ assert( pIdxInfo->needToFreeIdxStr==0 );
+ pIdxInfo->idxStr = 0;
+ pIdxInfo->idxNum = 0;
+ pIdxInfo->orderByConsumed = 0;
+ pIdxInfo->estimatedCost = SQLITE_BIG_DBL / (double)2;
+ pIdxInfo->estimatedRows = 25;
+ pIdxInfo->idxFlags = 0;
+ pIdxInfo->colUsed = (sqlite3_int64)pSrc->colUsed;
+
+ /* Invoke the virtual table xBestIndex() method */
+ rc = vtabBestIndex(pParse, pSrc->pTab, pIdxInfo);
+ if( rc ) return rc;
+
+ mxTerm = -1;
+ assert( pNew->nLSlot>=nConstraint );
+ for(i=0; i<nConstraint; i++) pNew->aLTerm[i] = 0;
+ pNew->u.vtab.omitMask = 0;
+ pIdxCons = *(struct sqlite3_index_constraint**)&pIdxInfo->aConstraint;
+ for(i=0; i<nConstraint; i++, pIdxCons++){
+ int iTerm;
+ if( (iTerm = pUsage[i].argvIndex - 1)>=0 ){
+ WhereTerm *pTerm;
+ int j = pIdxCons->iTermOffset;
+ if( iTerm>=nConstraint
+ || j<0
+ || j>=pWC->nTerm
+ || pNew->aLTerm[iTerm]!=0
+ || pIdxCons->usable==0
+ ){
+ rc = SQLITE_ERROR;
+ sqlite3ErrorMsg(pParse,"%s.xBestIndex malfunction",pSrc->pTab->zName);
+ return rc;
+ }
+ testcase( iTerm==nConstraint-1 );
+ testcase( j==0 );
+ testcase( j==pWC->nTerm-1 );
+ pTerm = &pWC->a[j];
+ pNew->prereq |= pTerm->prereqRight;
+ assert( iTerm<pNew->nLSlot );
+ pNew->aLTerm[iTerm] = pTerm;
+ if( iTerm>mxTerm ) mxTerm = iTerm;
+ testcase( iTerm==15 );
+ testcase( iTerm==16 );
+ if( iTerm<16 && pUsage[i].omit ) pNew->u.vtab.omitMask |= 1<<iTerm;
+ if( (pTerm->eOperator & WO_IN)!=0 ){
+ /* A virtual table that is constrained by an IN clause may not
+ ** consume the ORDER BY clause because (1) the order of IN terms
+ ** is not necessarily related to the order of output terms and
+ ** (2) Multiple outputs from a single IN value will not merge
+ ** together. */
+ pIdxInfo->orderByConsumed = 0;
+ pIdxInfo->idxFlags &= ~SQLITE_INDEX_SCAN_UNIQUE;
+ *pbIn = 1; assert( (mExclude & WO_IN)==0 );
+ }
+ }
+ }
+
+ pNew->nLTerm = mxTerm+1;
+ assert( pNew->nLTerm<=pNew->nLSlot );
+ pNew->u.vtab.idxNum = pIdxInfo->idxNum;
+ pNew->u.vtab.needFree = pIdxInfo->needToFreeIdxStr;
+ pIdxInfo->needToFreeIdxStr = 0;
+ pNew->u.vtab.idxStr = pIdxInfo->idxStr;
+ pNew->u.vtab.isOrdered = (i8)(pIdxInfo->orderByConsumed ?
+ pIdxInfo->nOrderBy : 0);
+ pNew->rSetup = 0;
+ pNew->rRun = sqlite3LogEstFromDouble(pIdxInfo->estimatedCost);
+ pNew->nOut = sqlite3LogEst(pIdxInfo->estimatedRows);
+
+ /* Set the WHERE_ONEROW flag if the xBestIndex() method indicated
+ ** that the scan will visit at most one row. Clear it otherwise. */
+ if( pIdxInfo->idxFlags & SQLITE_INDEX_SCAN_UNIQUE ){
+ pNew->wsFlags |= WHERE_ONEROW;
+ }else{
+ pNew->wsFlags &= ~WHERE_ONEROW;
+ }
+ rc = whereLoopInsert(pBuilder, pNew);
+ if( pNew->u.vtab.needFree ){
+ sqlite3_free(pNew->u.vtab.idxStr);
+ pNew->u.vtab.needFree = 0;
+ }
+ WHERETRACE(0xffff, (" bIn=%d prereqIn=%04llx prereqOut=%04llx\n",
+ *pbIn, (sqlite3_uint64)mPrereq,
+ (sqlite3_uint64)(pNew->prereq & ~mPrereq)));
+
+ return rc;
+}
+
+
/*
** Add all WhereLoop objects for a table of the join identified by
** pBuilder->pNew->iTab. That table is guaranteed to be a virtual table.
+**
+** If there are no LEFT or CROSS JOIN joins in the query, both mPrereq and
+** mUnusable are set to 0. Otherwise, mPrereq is a mask of all FROM clause
+** entries that occur before the virtual table in the FROM clause and are
+** separated from it by at least one LEFT or CROSS JOIN. Similarly, the
+** mUnusable mask contains all FROM clause entries that occur after the
+** virtual table and are separated from it by at least one LEFT or
+** CROSS JOIN.
+**
+** For example, if the query were:
+**
+** ... FROM t1, t2 LEFT JOIN t3, t4, vt CROSS JOIN t5, t6;
+**
+** then mPrereq corresponds to (t1, t2) and mUnusable to (t5, t6).
+**
+** All the tables in mPrereq must be scanned before the current virtual
+** table. So any terms for which all prerequisites are satisfied by
+** mPrereq may be specified as "usable" in all calls to xBestIndex.
+** Conversely, all tables in mUnusable must be scanned after the current
+** virtual table, so any terms for which the prerequisites overlap with
+** mUnusable should always be configured as "not-usable" for xBestIndex.
*/
static int whereLoopAddVirtual(
WhereLoopBuilder *pBuilder, /* WHERE clause information */
- Bitmask mExtra
+ Bitmask mPrereq, /* Tables that must be scanned before this one */
+ Bitmask mUnusable /* Tables that must be scanned after this one */
){
+ int rc = SQLITE_OK; /* Return code */
WhereInfo *pWInfo; /* WHERE analysis context */
Parse *pParse; /* The parsing context */
WhereClause *pWC; /* The WHERE clause */
struct SrcList_item *pSrc; /* The FROM clause term to search */
- Table *pTab;
- sqlite3 *db;
- sqlite3_index_info *pIdxInfo;
- struct sqlite3_index_constraint *pIdxCons;
- struct sqlite3_index_constraint_usage *pUsage;
- WhereTerm *pTerm;
- int i, j;
- int iTerm, mxTerm;
- int nConstraint;
- int seenIn = 0; /* True if an IN operator is seen */
- int seenVar = 0; /* True if a non-constant constraint is seen */
- int iPhase; /* 0: const w/o IN, 1: const, 2: no IN, 2: IN */
+ sqlite3_index_info *p; /* Object to pass to xBestIndex() */
+ int nConstraint; /* Number of constraints in p */
+ int bIn; /* True if plan uses IN(...) operator */
WhereLoop *pNew;
- int rc = SQLITE_OK;
+ Bitmask mBest; /* Tables used by best possible plan */
+ assert( (mPrereq & mUnusable)==0 );
pWInfo = pBuilder->pWInfo;
pParse = pWInfo->pParse;
- db = pParse->db;
pWC = pBuilder->pWC;
pNew = pBuilder->pNew;
pSrc = &pWInfo->pTabList->a[pNew->iTab];
- pTab = pSrc->pTab;
- assert( IsVirtual(pTab) );
- pIdxInfo = allocateIndexInfo(pParse, pWC, pSrc, pBuilder->pOrderBy);
- if( pIdxInfo==0 ) return SQLITE_NOMEM;
- pNew->prereq = 0;
+ assert( IsVirtual(pSrc->pTab) );
+ p = allocateIndexInfo(pParse, pWC, mUnusable, pSrc, pBuilder->pOrderBy);
+ if( p==0 ) return SQLITE_NOMEM_BKPT;
pNew->rSetup = 0;
pNew->wsFlags = WHERE_VIRTUALTABLE;
pNew->nLTerm = 0;
pNew->u.vtab.needFree = 0;
- pUsage = pIdxInfo->aConstraintUsage;
- nConstraint = pIdxInfo->nConstraint;
- if( whereLoopResize(db, pNew, nConstraint) ){
- sqlite3DbFree(db, pIdxInfo);
- return SQLITE_NOMEM;
- }
-
- for(iPhase=0; iPhase<=3; iPhase++){
- if( !seenIn && (iPhase&1)!=0 ){
- iPhase++;
- if( iPhase>3 ) break;
- }
- if( !seenVar && iPhase>1 ) break;
- pIdxCons = *(struct sqlite3_index_constraint**)&pIdxInfo->aConstraint;
- for(i=0; i<pIdxInfo->nConstraint; i++, pIdxCons++){
- j = pIdxCons->iTermOffset;
- pTerm = &pWC->a[j];
- switch( iPhase ){
- case 0: /* Constants without IN operator */
- pIdxCons->usable = 0;
- if( (pTerm->eOperator & WO_IN)!=0 ){
- seenIn = 1;
- }
- if( pTerm->prereqRight!=0 ){
- seenVar = 1;
- }else if( (pTerm->eOperator & WO_IN)==0 ){
- pIdxCons->usable = 1;
- }
- break;
- case 1: /* Constants with IN operators */
- assert( seenIn );
- pIdxCons->usable = (pTerm->prereqRight==0);
- break;
- case 2: /* Variables without IN */
- assert( seenVar );
- pIdxCons->usable = (pTerm->eOperator & WO_IN)==0;
- break;
- default: /* Variables with IN */
- assert( seenVar && seenIn );
- pIdxCons->usable = 1;
- break;
+ nConstraint = p->nConstraint;
+ if( whereLoopResize(pParse->db, pNew, nConstraint) ){
+ sqlite3DbFree(pParse->db, p);
+ return SQLITE_NOMEM_BKPT;
+ }
+
+ /* First call xBestIndex() with all constraints usable. */
+ WHERETRACE(0x40, (" VirtualOne: all usable\n"));
+ rc = whereLoopAddVirtualOne(pBuilder, mPrereq, ALLBITS, 0, p, &bIn);
+
+ /* If the call to xBestIndex() with all terms enabled produced a plan
+ ** that does not require any source tables (IOW: a plan with mBest==0),
+ ** then there is no point in making any further calls to xBestIndex()
+ ** since they will all return the same result (if the xBestIndex()
+ ** implementation is sane). */
+ if( rc==SQLITE_OK && (mBest = (pNew->prereq & ~mPrereq))!=0 ){
+ int seenZero = 0; /* True if a plan with no prereqs seen */
+ int seenZeroNoIN = 0; /* Plan with no prereqs and no IN(...) seen */
+ Bitmask mPrev = 0;
+ Bitmask mBestNoIn = 0;
+
+ /* If the plan produced by the earlier call uses an IN(...) term, call
+ ** xBestIndex again, this time with IN(...) terms disabled. */
+ if( bIn ){
+ WHERETRACE(0x40, (" VirtualOne: all usable w/o IN\n"));
+ rc = whereLoopAddVirtualOne(pBuilder, mPrereq, ALLBITS, WO_IN, p, &bIn);
+ assert( bIn==0 );
+ mBestNoIn = pNew->prereq & ~mPrereq;
+ if( mBestNoIn==0 ){
+ seenZero = 1;
+ seenZeroNoIN = 1;
+ }
+ }
+
+ /* Call xBestIndex once for each distinct value of (prereqRight & ~mPrereq)
+ ** in the set of terms that apply to the current virtual table. */
+ while( rc==SQLITE_OK ){
+ int i;
+ Bitmask mNext = ALLBITS;
+ assert( mNext>0 );
+ for(i=0; i<nConstraint; i++){
+ Bitmask mThis = (
+ pWC->a[p->aConstraint[i].iTermOffset].prereqRight & ~mPrereq
+ );
+ if( mThis>mPrev && mThis<mNext ) mNext = mThis;
}
- }
- memset(pUsage, 0, sizeof(pUsage[0])*pIdxInfo->nConstraint);
- if( pIdxInfo->needToFreeIdxStr ) sqlite3_free(pIdxInfo->idxStr);
- pIdxInfo->idxStr = 0;
- pIdxInfo->idxNum = 0;
- pIdxInfo->needToFreeIdxStr = 0;
- pIdxInfo->orderByConsumed = 0;
- pIdxInfo->estimatedCost = SQLITE_BIG_DBL / (double)2;
- pIdxInfo->estimatedRows = 25;
- rc = vtabBestIndex(pParse, pTab, pIdxInfo);
- if( rc ) goto whereLoopAddVtab_exit;
- pIdxCons = *(struct sqlite3_index_constraint**)&pIdxInfo->aConstraint;
- pNew->prereq = mExtra;
- mxTerm = -1;
- assert( pNew->nLSlot>=nConstraint );
- for(i=0; i<nConstraint; i++) pNew->aLTerm[i] = 0;
- pNew->u.vtab.omitMask = 0;
- for(i=0; i<nConstraint; i++, pIdxCons++){
- if( (iTerm = pUsage[i].argvIndex - 1)>=0 ){
- j = pIdxCons->iTermOffset;
- if( iTerm>=nConstraint
- || j<0
- || j>=pWC->nTerm
- || pNew->aLTerm[iTerm]!=0
- ){
- rc = SQLITE_ERROR;
- sqlite3ErrorMsg(pParse, "%s.xBestIndex() malfunction", pTab->zName);
- goto whereLoopAddVtab_exit;
- }
- testcase( iTerm==nConstraint-1 );
- testcase( j==0 );
- testcase( j==pWC->nTerm-1 );
- pTerm = &pWC->a[j];
- pNew->prereq |= pTerm->prereqRight;
- assert( iTerm<pNew->nLSlot );
- pNew->aLTerm[iTerm] = pTerm;
- if( iTerm>mxTerm ) mxTerm = iTerm;
- testcase( iTerm==15 );
- testcase( iTerm==16 );
- if( iTerm<16 && pUsage[i].omit ) pNew->u.vtab.omitMask |= 1<<iTerm;
- if( (pTerm->eOperator & WO_IN)!=0 ){
- if( pUsage[i].omit==0 ){
- /* Do not attempt to use an IN constraint if the virtual table
- ** says that the equivalent EQ constraint cannot be safely omitted.
- ** If we do attempt to use such a constraint, some rows might be
- ** repeated in the output. */
- break;
- }
- /* A virtual table that is constrained by an IN clause may not
- ** consume the ORDER BY clause because (1) the order of IN terms
- ** is not necessarily related to the order of output terms and
- ** (2) Multiple outputs from a single IN value will not merge
- ** together. */
- pIdxInfo->orderByConsumed = 0;
- }
- }
- }
- if( i>=nConstraint ){
- pNew->nLTerm = mxTerm+1;
- assert( pNew->nLTerm<=pNew->nLSlot );
- pNew->u.vtab.idxNum = pIdxInfo->idxNum;
- pNew->u.vtab.needFree = pIdxInfo->needToFreeIdxStr;
- pIdxInfo->needToFreeIdxStr = 0;
- pNew->u.vtab.idxStr = pIdxInfo->idxStr;
- pNew->u.vtab.isOrdered = (i8)(pIdxInfo->orderByConsumed ?
- pIdxInfo->nOrderBy : 0);
- pNew->rSetup = 0;
- pNew->rRun = sqlite3LogEstFromDouble(pIdxInfo->estimatedCost);
- pNew->nOut = sqlite3LogEst(pIdxInfo->estimatedRows);
- whereLoopInsert(pBuilder, pNew);
- if( pNew->u.vtab.needFree ){
- sqlite3_free(pNew->u.vtab.idxStr);
- pNew->u.vtab.needFree = 0;
+ mPrev = mNext;
+ if( mNext==ALLBITS ) break;
+ if( mNext==mBest || mNext==mBestNoIn ) continue;
+ WHERETRACE(0x40, (" VirtualOne: mPrev=%04llx mNext=%04llx\n",
+ (sqlite3_uint64)mPrev, (sqlite3_uint64)mNext));
+ rc = whereLoopAddVirtualOne(pBuilder, mPrereq, mNext|mPrereq, 0, p, &bIn);
+ if( pNew->prereq==mPrereq ){
+ seenZero = 1;
+ if( bIn==0 ) seenZeroNoIN = 1;
}
}
- }
-whereLoopAddVtab_exit:
- if( pIdxInfo->needToFreeIdxStr ) sqlite3_free(pIdxInfo->idxStr);
- sqlite3DbFree(db, pIdxInfo);
+ /* If the calls to xBestIndex() in the above loop did not find a plan
+ ** that requires no source tables at all (i.e. one guaranteed to be
+ ** usable), make a call here with all source tables disabled */
+ if( rc==SQLITE_OK && seenZero==0 ){
+ WHERETRACE(0x40, (" VirtualOne: all disabled\n"));
+ rc = whereLoopAddVirtualOne(pBuilder, mPrereq, mPrereq, 0, p, &bIn);
+ if( bIn==0 ) seenZeroNoIN = 1;
+ }
+
+ /* If the calls to xBestIndex() have so far failed to find a plan
+ ** that requires no source tables at all and does not use an IN(...)
+ ** operator, make a final call to obtain one here. */
+ if( rc==SQLITE_OK && seenZeroNoIN==0 ){
+ WHERETRACE(0x40, (" VirtualOne: all disabled and w/o IN\n"));
+ rc = whereLoopAddVirtualOne(pBuilder, mPrereq, mPrereq, WO_IN, p, &bIn);
+ }
+ }
+
+ if( p->needToFreeIdxStr ) sqlite3_free(p->idxStr);
+ sqlite3DbFree(pParse->db, p);
return rc;
}
#endif /* SQLITE_OMIT_VIRTUALTABLE */
@@ -121603,7 +129022,11 @@ whereLoopAddVtab_exit:
** Add WhereLoop entries to handle OR terms. This works for either
** btrees or virtual tables.
*/
-static int whereLoopAddOr(WhereLoopBuilder *pBuilder, Bitmask mExtra){
+static int whereLoopAddOr(
+ WhereLoopBuilder *pBuilder,
+ Bitmask mPrereq,
+ Bitmask mUnusable
+){
WhereInfo *pWInfo = pBuilder->pWInfo;
WhereClause *pWC;
WhereLoop *pNew;
@@ -121662,14 +129085,14 @@ static int whereLoopAddOr(WhereLoopBuilder *pBuilder, Bitmask mExtra){
#endif
#ifndef SQLITE_OMIT_VIRTUALTABLE
if( IsVirtual(pItem->pTab) ){
- rc = whereLoopAddVirtual(&sSubBuild, mExtra);
+ rc = whereLoopAddVirtual(&sSubBuild, mPrereq, mUnusable);
}else
#endif
{
- rc = whereLoopAddBtree(&sSubBuild, mExtra);
+ rc = whereLoopAddBtree(&sSubBuild, mPrereq);
}
if( rc==SQLITE_OK ){
- rc = whereLoopAddOr(&sSubBuild, mExtra);
+ rc = whereLoopAddOr(&sSubBuild, mPrereq, mUnusable);
}
assert( rc==SQLITE_OK || sCur.n==0 );
if( sCur.n==0 ){
@@ -121726,38 +129149,48 @@ static int whereLoopAddOr(WhereLoopBuilder *pBuilder, Bitmask mExtra){
*/
static int whereLoopAddAll(WhereLoopBuilder *pBuilder){
WhereInfo *pWInfo = pBuilder->pWInfo;
- Bitmask mExtra = 0;
+ Bitmask mPrereq = 0;
Bitmask mPrior = 0;
int iTab;
SrcList *pTabList = pWInfo->pTabList;
struct SrcList_item *pItem;
+ struct SrcList_item *pEnd = &pTabList->a[pWInfo->nLevel];
sqlite3 *db = pWInfo->pParse->db;
- int nTabList = pWInfo->nLevel;
int rc = SQLITE_OK;
- u8 priorJoinType = 0;
WhereLoop *pNew;
+ u8 priorJointype = 0;
/* Loop over the tables in the join, from left to right */
pNew = pBuilder->pNew;
whereLoopInit(pNew);
- for(iTab=0, pItem=pTabList->a; iTab<nTabList; iTab++, pItem++){
+ for(iTab=0, pItem=pTabList->a; pItem<pEnd; iTab++, pItem++){
+ Bitmask mUnusable = 0;
pNew->iTab = iTab;
- pNew->maskSelf = getMask(&pWInfo->sMaskSet, pItem->iCursor);
- if( ((pItem->jointype|priorJoinType) & (JT_LEFT|JT_CROSS))!=0 ){
- mExtra = mPrior;
+ pNew->maskSelf = sqlite3WhereGetMask(&pWInfo->sMaskSet, pItem->iCursor);
+ if( ((pItem->fg.jointype|priorJointype) & (JT_LEFT|JT_CROSS))!=0 ){
+ /* This condition is true when pItem is the FROM clause term on the
+ ** right-hand-side of a LEFT or CROSS JOIN. */
+ mPrereq = mPrior;
}
- priorJoinType = pItem->jointype;
+ priorJointype = pItem->fg.jointype;
if( IsVirtual(pItem->pTab) ){
- rc = whereLoopAddVirtual(pBuilder, mExtra);
+ struct SrcList_item *p;
+ for(p=&pItem[1]; p<pEnd; p++){
+ if( mUnusable || (p->fg.jointype & (JT_LEFT|JT_CROSS)) ){
+ mUnusable |= sqlite3WhereGetMask(&pWInfo->sMaskSet, p->iCursor);
+ }
+ }
+ rc = whereLoopAddVirtual(pBuilder, mPrereq, mUnusable);
}else{
- rc = whereLoopAddBtree(pBuilder, mExtra);
+ rc = whereLoopAddBtree(pBuilder, mPrereq);
}
if( rc==SQLITE_OK ){
- rc = whereLoopAddOr(pBuilder, mExtra);
+ rc = whereLoopAddOr(pBuilder, mPrereq, mUnusable);
}
mPrior |= pNew->maskSelf;
if( rc || db->mallocFailed ) break;
}
+
whereLoopClear(db, pNew);
return rc;
}
@@ -121863,10 +129296,10 @@ static i8 wherePathSatisfiesOrderBy(
pOBExpr = sqlite3ExprSkipCollate(pOrderBy->a[i].pExpr);
if( pOBExpr->op!=TK_COLUMN ) continue;
if( pOBExpr->iTable!=iCur ) continue;
- pTerm = findTerm(&pWInfo->sWC, iCur, pOBExpr->iColumn,
- ~ready, WO_EQ|WO_ISNULL, 0);
+ pTerm = sqlite3WhereFindTerm(&pWInfo->sWC, iCur, pOBExpr->iColumn,
+ ~ready, WO_EQ|WO_ISNULL|WO_IS, 0);
if( pTerm==0 ) continue;
- if( (pTerm->eOperator&WO_EQ)!=0 && pOBExpr->iColumn>=0 ){
+ if( (pTerm->eOperator&(WO_EQ|WO_IS))!=0 && pOBExpr->iColumn>=0 ){
const char *z1, *z2;
pColl = sqlite3ExprCollSeq(pWInfo->pParse, pOrderBy->a[i].pExpr);
if( !pColl ) pColl = db->pDfltColl;
@@ -121875,6 +129308,7 @@ static i8 wherePathSatisfiesOrderBy(
if( !pColl ) pColl = db->pDfltColl;
z2 = pColl->zName;
if( sqlite3StrICmp(z1, z2)!=0 ) continue;
+ testcase( pTerm->pExpr->op==TK_IS );
}
obSat |= MASKBIT(i);
}
@@ -121890,7 +129324,8 @@ static i8 wherePathSatisfiesOrderBy(
nKeyCol = pIndex->nKeyCol;
nColumn = pIndex->nColumn;
assert( nColumn==nKeyCol+1 || !HasRowid(pIndex->pTable) );
- assert( pIndex->aiColumn[nColumn-1]==(-1) || !HasRowid(pIndex->pTable));
+ assert( pIndex->aiColumn[nColumn-1]==XN_ROWID
+ || !HasRowid(pIndex->pTable));
isOrderDistinct = IsUniqueIndex(pIndex);
}
@@ -121905,7 +129340,7 @@ static i8 wherePathSatisfiesOrderBy(
/* Skip over == and IS NULL terms */
if( j<pLoop->u.btree.nEq
&& pLoop->nSkip==0
- && ((i = pLoop->aLTerm[j]->eOperator) & (WO_EQ|WO_ISNULL))!=0
+ && ((i = pLoop->aLTerm[j]->eOperator) & (WO_EQ|WO_ISNULL|WO_IS))!=0
){
if( i & WO_ISNULL ){
testcase( isOrderDistinct );
@@ -121922,7 +129357,7 @@ static i8 wherePathSatisfiesOrderBy(
revIdx = pIndex->aSortOrder[j];
if( iColumn==pIndex->pTable->iPKey ) iColumn = -1;
}else{
- iColumn = -1;
+ iColumn = XN_ROWID;
revIdx = 0;
}
@@ -121948,9 +129383,15 @@ static i8 wherePathSatisfiesOrderBy(
testcase( wctrlFlags & WHERE_GROUPBY );
testcase( wctrlFlags & WHERE_DISTINCTBY );
if( (wctrlFlags & (WHERE_GROUPBY|WHERE_DISTINCTBY))==0 ) bOnce = 0;
- if( pOBExpr->op!=TK_COLUMN ) continue;
- if( pOBExpr->iTable!=iCur ) continue;
- if( pOBExpr->iColumn!=iColumn ) continue;
+ if( iColumn>=(-1) ){
+ if( pOBExpr->op!=TK_COLUMN ) continue;
+ if( pOBExpr->iTable!=iCur ) continue;
+ if( pOBExpr->iColumn!=iColumn ) continue;
+ }else{
+ if( sqlite3ExprCompare(pOBExpr,pIndex->aColExpr->a[j].pExpr,iCur) ){
+ continue;
+ }
+ }
if( iColumn>=0 ){
pColl = sqlite3ExprCollSeq(pWInfo->pParse, pOrderBy->a[i].pExpr);
if( !pColl ) pColl = db->pDfltColl;
@@ -121999,7 +129440,7 @@ static i8 wherePathSatisfiesOrderBy(
Bitmask mTerm;
if( MASKBIT(i) & obSat ) continue;
p = pOrderBy->a[i].pExpr;
- mTerm = exprTableUsage(&pWInfo->sMaskSet,p);
+ mTerm = sqlite3WhereExprUsage(&pWInfo->sMaskSet,p);
if( mTerm==0 && !sqlite3ExprIsConstant(p) ) continue;
if( (mTerm&~orderDistinctMask)==0 ){
obSat |= MASKBIT(i);
@@ -122087,15 +129528,14 @@ static LogEst whereSortingCost(
LogEst rScale, rSortCost;
assert( nOrderBy>0 && 66==sqlite3LogEst(100) );
rScale = sqlite3LogEst((nOrderBy-nSorted)*100/nOrderBy) - 66;
- rSortCost = nRow + estLog(nRow) + rScale + 16;
+ rSortCost = nRow + rScale + 16;
- /* TUNING: The cost of implementing DISTINCT using a B-TREE is
- ** similar but with a larger constant of proportionality.
- ** Multiply by an additional factor of 3.0. */
- if( pWInfo->wctrlFlags & WHERE_WANT_DISTINCT ){
- rSortCost += 16;
+ /* Multiple by log(M) where M is the number of output rows.
+ ** Use the LIMIT for M if it is smaller */
+ if( (pWInfo->wctrlFlags & WHERE_USE_LIMIT)!=0 && pWInfo->iLimit<nRow ){
+ nRow = pWInfo->iLimit;
}
-
+ rSortCost += estLog(nRow);
return rSortCost;
}
@@ -122157,8 +129597,8 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){
/* Allocate and initialize space for aTo, aFrom and aSortCost[] */
nSpace = (sizeof(WherePath)+sizeof(WhereLoop*)*nLoop)*mxChoice*2;
nSpace += sizeof(LogEst) * nOrderBy;
- pSpace = sqlite3DbMallocRaw(db, nSpace);
- if( pSpace==0 ) return SQLITE_NOMEM;
+ pSpace = sqlite3DbMallocRawNN(db, nSpace);
+ if( pSpace==0 ) return SQLITE_NOMEM_BKPT;
aTo = (WherePath*)pSpace;
aFrom = aTo+mxChoice;
memset(aFrom, 0, sizeof(aFrom[0]));
@@ -122213,6 +129653,12 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){
if( (pWLoop->prereq & ~pFrom->maskLoop)!=0 ) continue;
if( (pWLoop->maskSelf & pFrom->maskLoop)!=0 ) continue;
+ if( (pWLoop->wsFlags & WHERE_AUTO_INDEX)!=0 && pFrom->nRow<10 ){
+ /* Do not use an automatic index if the this loop is expected
+ ** to run less than 2 times. */
+ assert( 10==sqlite3LogEst(2) );
+ continue;
+ }
/* At this point, pWLoop is a candidate to be the next loop.
** Compute its cost */
rUnsorted = sqlite3LogEstAdd(pWLoop->rSetup,pWLoop->rRun + pFrom->nRow);
@@ -122405,9 +129851,9 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){
&& nRowEst
){
Bitmask notUsed;
- int rc = wherePathSatisfiesOrderBy(pWInfo, pWInfo->pResultSet, pFrom,
+ int rc = wherePathSatisfiesOrderBy(pWInfo, pWInfo->pDistinctSet, pFrom,
WHERE_DISTINCTBY, nLoop-1, pFrom->aLoop[nLoop-1], &notUsed);
- if( rc==pWInfo->pResultSet->nExpr ){
+ if( rc==pWInfo->pDistinctSet->nExpr ){
pWInfo->eDistinct = WHERE_DISTINCT_ORDERED;
}
}
@@ -122465,21 +129911,22 @@ static int whereShortCut(WhereLoopBuilder *pBuilder){
int j;
Table *pTab;
Index *pIdx;
-
+
pWInfo = pBuilder->pWInfo;
if( pWInfo->wctrlFlags & WHERE_FORCE_TABLE ) return 0;
assert( pWInfo->pTabList->nSrc>=1 );
pItem = pWInfo->pTabList->a;
pTab = pItem->pTab;
if( IsVirtual(pTab) ) return 0;
- if( pItem->zIndex ) return 0;
+ if( pItem->fg.isIndexedBy ) return 0;
iCur = pItem->iCursor;
pWC = &pWInfo->sWC;
pLoop = pBuilder->pNew;
pLoop->wsFlags = 0;
pLoop->nSkip = 0;
- pTerm = findTerm(pWC, iCur, -1, 0, WO_EQ, 0);
+ pTerm = sqlite3WhereFindTerm(pWC, iCur, -1, 0, WO_EQ|WO_IS, 0);
if( pTerm ){
+ testcase( pTerm->eOperator & WO_IS );
pLoop->wsFlags = WHERE_COLUMN_EQ|WHERE_IPK|WHERE_ONEROW;
pLoop->aLTerm[0] = pTerm;
pLoop->nLTerm = 1;
@@ -122488,14 +129935,17 @@ static int whereShortCut(WhereLoopBuilder *pBuilder){
pLoop->rRun = 33; /* 33==sqlite3LogEst(10) */
}else{
for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
+ int opMask;
assert( pLoop->aLTermSpace==pLoop->aLTerm );
if( !IsUniqueIndex(pIdx)
|| pIdx->pPartIdxWhere!=0
|| pIdx->nKeyCol>ArraySize(pLoop->aLTermSpace)
) continue;
+ opMask = pIdx->uniqNotNull ? (WO_EQ|WO_IS) : WO_EQ;
for(j=0; j<pIdx->nKeyCol; j++){
- pTerm = findTerm(pWC, iCur, pIdx->aiColumn[j], 0, WO_EQ, pIdx);
+ pTerm = sqlite3WhereFindTerm(pWC, iCur, j, 0, opMask, pIdx);
if( pTerm==0 ) break;
+ testcase( pTerm->eOperator & WO_IS );
pLoop->aLTerm[j] = pTerm;
}
if( j!=pIdx->nKeyCol ) continue;
@@ -122514,7 +129964,7 @@ static int whereShortCut(WhereLoopBuilder *pBuilder){
if( pLoop->wsFlags ){
pLoop->nOut = (LogEst)1;
pWInfo->a[0].pWLoop = pLoop;
- pLoop->maskSelf = getMask(&pWInfo->sMaskSet, iCur);
+ pLoop->maskSelf = sqlite3WhereGetMask(&pWInfo->sMaskSet, iCur);
pWInfo->a[0].iTabCur = iCur;
pWInfo->nRowOut = 1;
if( pWInfo->pOrderBy ) pWInfo->nOBSat = pWInfo->pOrderBy->nExpr;
@@ -122618,13 +130068,14 @@ static int whereShortCut(WhereLoopBuilder *pBuilder){
** used.
*/
SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin(
- Parse *pParse, /* The parser context */
- SrcList *pTabList, /* FROM clause: A list of all tables to be scanned */
- Expr *pWhere, /* The WHERE clause */
- ExprList *pOrderBy, /* An ORDER BY (or GROUP BY) clause, or NULL */
- ExprList *pResultSet, /* Result set of the query */
- u16 wctrlFlags, /* One of the WHERE_* flags defined in sqliteInt.h */
- int iIdxCur /* If WHERE_ONETABLE_ONLY is set, index cursor number */
+ Parse *pParse, /* The parser context */
+ SrcList *pTabList, /* FROM clause: A list of all tables to be scanned */
+ Expr *pWhere, /* The WHERE clause */
+ ExprList *pOrderBy, /* An ORDER BY (or GROUP BY) clause, or NULL */
+ ExprList *pDistinctSet, /* Try not to output two rows that duplicate these */
+ u16 wctrlFlags, /* The WHERE_* flags defined in sqliteInt.h */
+ int iAuxArg /* If WHERE_ONETABLE_ONLY is set, index cursor number
+ ** If WHERE_USE_LIMIT, then the limit amount */
){
int nByteWInfo; /* Num. bytes allocated for WhereInfo struct */
int nTabList; /* Number of elements in pTabList */
@@ -122638,7 +130089,16 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin(
int ii; /* Loop counter */
sqlite3 *db; /* Database connection */
int rc; /* Return code */
+ u8 bFordelete = 0; /* OPFLAG_FORDELETE or zero, as appropriate */
+ assert( (wctrlFlags & WHERE_ONEPASS_MULTIROW)==0 || (
+ (wctrlFlags & WHERE_ONEPASS_DESIRED)!=0
+ && (wctrlFlags & WHERE_OMIT_OPEN_CLOSE)==0
+ ));
+
+ /* Only one of WHERE_ONETABLE_ONLY or WHERE_USE_LIMIT */
+ assert( (wctrlFlags & WHERE_ONETABLE_ONLY)==0
+ || (wctrlFlags & WHERE_USE_LIMIT)==0 );
/* Variable initialization */
db = pParse->db;
@@ -122690,10 +130150,12 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin(
pWInfo->pParse = pParse;
pWInfo->pTabList = pTabList;
pWInfo->pOrderBy = pOrderBy;
- pWInfo->pResultSet = pResultSet;
+ pWInfo->pDistinctSet = pDistinctSet;
pWInfo->iBreak = pWInfo->iContinue = sqlite3VdbeMakeLabel(v);
pWInfo->wctrlFlags = wctrlFlags;
+ pWInfo->iLimit = iAuxArg;
pWInfo->savedNQueryLoop = pParse->nQueryLoop;
+ assert( pWInfo->eOnePass==ONEPASS_OFF ); /* ONEPASS defaults to OFF */
pMaskSet = &pWInfo->sMaskSet;
sWLB.pWInfo = pWInfo;
sWLB.pWC = &pWInfo->sWC;
@@ -122708,8 +130170,8 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin(
** subexpression is separated by an AND operator.
*/
initMaskSet(pMaskSet);
- whereClauseInit(&pWInfo->sWC, pWInfo);
- whereSplit(&pWInfo->sWC, pWhere, TK_AND);
+ sqlite3WhereClauseInit(&pWInfo->sWC, pWInfo);
+ sqlite3WhereSplit(&pWInfo->sWC, pWhere, TK_AND);
/* Special case: a WHERE clause that is constant. Evaluate the
** expression and either jump over all of the code or fall thru.
@@ -122733,14 +130195,12 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin(
/* Assign a bit from the bitmask to every term in the FROM clause.
**
- ** When assigning bitmask values to FROM clause cursors, it must be
- ** the case that if X is the bitmask for the N-th FROM clause term then
- ** the bitmask for all FROM clause terms to the left of the N-th term
- ** is (X-1). An expression from the ON clause of a LEFT JOIN can use
- ** its Expr.iRightJoinTable value to find the bitmask of the right table
- ** of the join. Subtracting one from the right table bitmask gives a
- ** bitmask for all tables to the left of the join. Knowing the bitmask
- ** for all tables to the left of a left join is important. Ticket #3015.
+ ** The N-th term of the FROM clause is assigned a bitmask of 1<<N.
+ **
+ ** The rule of the previous sentence ensures thta if X is the bitmask for
+ ** a table T, then X-1 is the bitmask for all other tables to the left of T.
+ ** Knowing the bitmask for all tables to the left of a left join is
+ ** important. Ticket #3015.
**
** Note that bitmasks are created for all pTabList->nSrc tables in
** pTabList, not just the first nTabList tables. nTabList is normally
@@ -122749,44 +130209,40 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin(
*/
for(ii=0; ii<pTabList->nSrc; ii++){
createMask(pMaskSet, pTabList->a[ii].iCursor);
+ sqlite3WhereTabFuncArgs(pParse, &pTabList->a[ii], &pWInfo->sWC);
}
-#ifndef NDEBUG
- {
- Bitmask toTheLeft = 0;
- for(ii=0; ii<pTabList->nSrc; ii++){
- Bitmask m = getMask(pMaskSet, pTabList->a[ii].iCursor);
- assert( (m-1)==toTheLeft );
- toTheLeft |= m;
- }
+#ifdef SQLITE_DEBUG
+ for(ii=0; ii<pTabList->nSrc; ii++){
+ Bitmask m = sqlite3WhereGetMask(pMaskSet, pTabList->a[ii].iCursor);
+ assert( m==MASKBIT(ii) );
}
#endif
- /* Analyze all of the subexpressions. Note that exprAnalyze() might
- ** add new virtual terms onto the end of the WHERE clause. We do not
- ** want to analyze these virtual terms, so start analyzing at the end
- ** and work forward so that the added virtual terms are never processed.
- */
- exprAnalyzeAll(pTabList, &pWInfo->sWC);
- if( db->mallocFailed ){
- goto whereBeginError;
- }
+ /* Analyze all of the subexpressions. */
+ sqlite3WhereExprAnalyze(pTabList, &pWInfo->sWC);
+ if( db->mallocFailed ) goto whereBeginError;
if( wctrlFlags & WHERE_WANT_DISTINCT ){
- if( isDistinctRedundant(pParse, pTabList, &pWInfo->sWC, pResultSet) ){
+ if( isDistinctRedundant(pParse, pTabList, &pWInfo->sWC, pDistinctSet) ){
/* The DISTINCT marking is pointless. Ignore it. */
pWInfo->eDistinct = WHERE_DISTINCT_UNIQUE;
}else if( pOrderBy==0 ){
/* Try to ORDER BY the result set to make distinct processing easier */
pWInfo->wctrlFlags |= WHERE_DISTINCTBY;
- pWInfo->pOrderBy = pResultSet;
+ pWInfo->pOrderBy = pDistinctSet;
}
}
/* Construct the WhereLoop objects */
- WHERETRACE(0xffff,("*** Optimizer Start ***\n"));
#if defined(WHERETRACE_ENABLED)
- /* Display all terms of the WHERE clause */
- if( sqlite3WhereTrace & 0x100 ){
+ if( sqlite3WhereTrace & 0xffff ){
+ sqlite3DebugPrintf("*** Optimizer Start *** (wctrlFlags: 0x%x",wctrlFlags);
+ if( wctrlFlags & WHERE_USE_LIMIT ){
+ sqlite3DebugPrintf(", limit: %d", iAuxArg);
+ }
+ sqlite3DebugPrintf(")\n");
+ }
+ if( sqlite3WhereTrace & 0x100 ){ /* Display all terms of the WHERE clause */
int i;
for(i=0; i<sWLB.pWC->nTerm; i++){
whereTermPrint(&sWLB.pWC->a[i], i);
@@ -122798,13 +130254,12 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin(
rc = whereLoopAddAll(&sWLB);
if( rc ) goto whereBeginError;
- /* Display all of the WhereLoop objects if wheretrace is enabled */
-#ifdef WHERETRACE_ENABLED /* !=0 */
- if( sqlite3WhereTrace ){
+#ifdef WHERETRACE_ENABLED
+ if( sqlite3WhereTrace ){ /* Display all of the WhereLoop objects */
WhereLoop *p;
int i;
- static char zLabel[] = "0123456789abcdefghijklmnopqrstuvwyxz"
- "ABCDEFGHIJKLMNOPQRSTUVWYXZ";
+ static const char zLabel[] = "0123456789abcdefghijklmnopqrstuvwyxz"
+ "ABCDEFGHIJKLMNOPQRSTUVWYXZ";
for(p=pWInfo->pLoops, i=0; p; p=p->pNextLoop, i++){
p->cId = zLabel[i%sizeof(zLabel)];
whereLoopPrint(p, sWLB.pWC);
@@ -122820,12 +130275,12 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin(
}
}
if( pWInfo->pOrderBy==0 && (db->flags & SQLITE_ReverseOrder)!=0 ){
- pWInfo->revMask = (Bitmask)(-1);
+ pWInfo->revMask = ALLBITS;
}
if( pParse->nErr || NEVER(db->mallocFailed) ){
goto whereBeginError;
}
-#ifdef WHERETRACE_ENABLED /* !=0 */
+#ifdef WHERETRACE_ENABLED
if( sqlite3WhereTrace ){
sqlite3DebugPrintf("---- Solution nRow=%d", pWInfo->nRowOut);
if( pWInfo->nOBSat>0 ){
@@ -122853,15 +130308,17 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin(
#endif
/* Attempt to omit tables from the join that do not effect the result */
if( pWInfo->nLevel>=2
- && pResultSet!=0
+ && pDistinctSet!=0
&& OptimizationEnabled(db, SQLITE_OmitNoopJoin)
){
- Bitmask tabUsed = exprListTableUsage(pMaskSet, pResultSet);
- if( sWLB.pOrderBy ) tabUsed |= exprListTableUsage(pMaskSet, sWLB.pOrderBy);
+ Bitmask tabUsed = sqlite3WhereExprListUsage(pMaskSet, pDistinctSet);
+ if( sWLB.pOrderBy ){
+ tabUsed |= sqlite3WhereExprListUsage(pMaskSet, sWLB.pOrderBy);
+ }
while( pWInfo->nLevel>=2 ){
WhereTerm *pTerm, *pEnd;
pLoop = pWInfo->a[pWInfo->nLevel-1].pWLoop;
- if( (pWInfo->pTabList->a[pLoop->iTab].jointype & JT_LEFT)==0 ) break;
+ if( (pWInfo->pTabList->a[pLoop->iTab].fg.jointype & JT_LEFT)==0 ) break;
if( (wctrlFlags & WHERE_WANT_DISTINCT)==0
&& (pLoop->wsFlags & WHERE_ONEROW)==0
){
@@ -122887,22 +130344,28 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin(
/* If the caller is an UPDATE or DELETE statement that is requesting
** to use a one-pass algorithm, determine if this is appropriate.
- ** The one-pass algorithm only works if the WHERE clause constrains
- ** the statement to update a single row.
*/
assert( (wctrlFlags & WHERE_ONEPASS_DESIRED)==0 || pWInfo->nLevel==1 );
- if( (wctrlFlags & WHERE_ONEPASS_DESIRED)!=0
- && (pWInfo->a[0].pWLoop->wsFlags & WHERE_ONEROW)!=0 ){
- pWInfo->okOnePass = 1;
- if( HasRowid(pTabList->a[0].pTab) ){
- pWInfo->a[0].pWLoop->wsFlags &= ~WHERE_IDX_ONLY;
+ if( (wctrlFlags & WHERE_ONEPASS_DESIRED)!=0 ){
+ int wsFlags = pWInfo->a[0].pWLoop->wsFlags;
+ int bOnerow = (wsFlags & WHERE_ONEROW)!=0;
+ if( bOnerow
+ || ((wctrlFlags & WHERE_ONEPASS_MULTIROW)!=0
+ && 0==(wsFlags & WHERE_VIRTUALTABLE))
+ ){
+ pWInfo->eOnePass = bOnerow ? ONEPASS_SINGLE : ONEPASS_MULTI;
+ if( HasRowid(pTabList->a[0].pTab) && (wsFlags & WHERE_IDX_ONLY) ){
+ if( wctrlFlags & WHERE_ONEPASS_MULTIROW ){
+ bFordelete = OPFLAG_FORDELETE;
+ }
+ pWInfo->a[0].pWLoop->wsFlags = (wsFlags & ~WHERE_IDX_ONLY);
+ }
}
}
/* Open all tables in the pTabList and any indices selected for
** searching those tables.
*/
- notReady = ~(Bitmask)0;
for(ii=0, pLevel=pWInfo->a; ii<nTabList; ii++, pLevel++){
Table *pTab; /* Table to open */
int iDb; /* Index of database containing table/index */
@@ -122927,22 +130390,33 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin(
if( (pLoop->wsFlags & WHERE_IDX_ONLY)==0
&& (wctrlFlags & WHERE_OMIT_OPEN_CLOSE)==0 ){
int op = OP_OpenRead;
- if( pWInfo->okOnePass ){
+ if( pWInfo->eOnePass!=ONEPASS_OFF ){
op = OP_OpenWrite;
pWInfo->aiCurOnePass[0] = pTabItem->iCursor;
};
sqlite3OpenTable(pParse, pTabItem->iCursor, iDb, pTab, op);
assert( pTabItem->iCursor==pLevel->iTabCur );
- testcase( !pWInfo->okOnePass && pTab->nCol==BMS-1 );
- testcase( !pWInfo->okOnePass && pTab->nCol==BMS );
- if( !pWInfo->okOnePass && pTab->nCol<BMS && HasRowid(pTab) ){
+ testcase( pWInfo->eOnePass==ONEPASS_OFF && pTab->nCol==BMS-1 );
+ testcase( pWInfo->eOnePass==ONEPASS_OFF && pTab->nCol==BMS );
+ if( pWInfo->eOnePass==ONEPASS_OFF && pTab->nCol<BMS && HasRowid(pTab) ){
Bitmask b = pTabItem->colUsed;
int n = 0;
for(; b; b=b>>1, n++){}
- sqlite3VdbeChangeP4(v, sqlite3VdbeCurrentAddr(v)-1,
- SQLITE_INT_TO_PTR(n), P4_INT32);
+ sqlite3VdbeChangeP4(v, -1, SQLITE_INT_TO_PTR(n), P4_INT32);
assert( n<=pTab->nCol );
}
+#ifdef SQLITE_ENABLE_CURSOR_HINTS
+ if( pLoop->u.btree.pIndex!=0 ){
+ sqlite3VdbeChangeP5(v, OPFLAG_SEEKEQ|bFordelete);
+ }else
+#endif
+ {
+ sqlite3VdbeChangeP5(v, bFordelete);
+ }
+#ifdef SQLITE_ENABLE_COLUMN_USED_MASK
+ sqlite3VdbeAddOp4Dup8(v, OP_ColumnsUsed, pTabItem->iCursor, 0, 0,
+ (const u8*)&pTabItem->colUsed, P4_INT64);
+#endif
}else{
sqlite3TableLock(pParse, iDb, pTab->tnum, 0, pTab->zName);
}
@@ -122950,8 +130424,8 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin(
Index *pIx = pLoop->u.btree.pIndex;
int iIndexCur;
int op = OP_OpenRead;
- /* iIdxCur is always set if to a positive value if ONEPASS is possible */
- assert( iIdxCur!=0 || (pWInfo->wctrlFlags & WHERE_ONEPASS_DESIRED)==0 );
+ /* iAuxArg is always set if to a positive value if ONEPASS is possible */
+ assert( iAuxArg!=0 || (pWInfo->wctrlFlags & WHERE_ONEPASS_DESIRED)==0 );
if( !HasRowid(pTab) && IsPrimaryKeyIndex(pIx)
&& (wctrlFlags & WHERE_ONETABLE_ONLY)!=0
){
@@ -122959,9 +130433,9 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin(
** WITHOUT ROWID table. No need for a separate index */
iIndexCur = pLevel->iTabCur;
op = 0;
- }else if( pWInfo->okOnePass ){
+ }else if( pWInfo->eOnePass!=ONEPASS_OFF ){
Index *pJ = pTabItem->pTab->pIndex;
- iIndexCur = iIdxCur;
+ iIndexCur = iAuxArg;
assert( wctrlFlags & WHERE_ONEPASS_DESIRED );
while( ALWAYS(pJ) && pJ!=pIx ){
iIndexCur++;
@@ -122969,8 +130443,8 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin(
}
op = OP_OpenWrite;
pWInfo->aiCurOnePass[1] = iIndexCur;
- }else if( iIdxCur && (wctrlFlags & WHERE_ONETABLE_ONLY)!=0 ){
- iIndexCur = iIdxCur;
+ }else if( iAuxArg && (wctrlFlags & WHERE_ONETABLE_ONLY)!=0 ){
+ iIndexCur = iAuxArg;
if( wctrlFlags & WHERE_REOPEN_IDX ) op = OP_ReopenIdx;
}else{
iIndexCur = pParse->nTab++;
@@ -122988,10 +130462,24 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin(
sqlite3VdbeChangeP5(v, OPFLAG_SEEKEQ); /* Hint to COMDB2 */
}
VdbeComment((v, "%s", pIx->zName));
+#ifdef SQLITE_ENABLE_COLUMN_USED_MASK
+ {
+ u64 colUsed = 0;
+ int ii, jj;
+ for(ii=0; ii<pIx->nColumn; ii++){
+ jj = pIx->aiColumn[ii];
+ if( jj<0 ) continue;
+ if( jj>63 ) jj = 63;
+ if( (pTabItem->colUsed & MASKBIT(jj))==0 ) continue;
+ colUsed |= ((u64)1)<<(ii<63 ? ii : 63);
+ }
+ sqlite3VdbeAddOp4Dup8(v, OP_ColumnsUsed, iIndexCur, 0, 0,
+ (u8*)&colUsed, P4_INT64);
+ }
+#endif /* SQLITE_ENABLE_COLUMN_USED_MASK */
}
}
if( iDb>=0 ) sqlite3CodeVerifySchema(pParse, iDb);
- notReady &= ~getMask(&pWInfo->sMaskSet, pTabItem->iCursor);
}
pWInfo->iTop = sqlite3VdbeCurrentAddr(v);
if( db->mallocFailed ) goto whereBeginError;
@@ -123013,14 +130501,14 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin(
if( db->mallocFailed ) goto whereBeginError;
}
#endif
- addrExplain = explainOneScan(
+ addrExplain = sqlite3WhereExplainOneScan(
pParse, pTabList, pLevel, ii, pLevel->iFrom, wctrlFlags
);
pLevel->addrBody = sqlite3VdbeCurrentAddr(v);
- notReady = codeOneLoopStart(pWInfo, ii, notReady);
+ notReady = sqlite3WhereCodeOneLoopStart(pWInfo, ii, notReady);
pWInfo->iContinue = pLevel->addrCont;
if( (wsFlags&WHERE_MULTI_OR)==0 && (wctrlFlags&WHERE_ONETABLE_ONLY)==0 ){
- addScanStatus(v, pTabList, pLevel, addrExplain);
+ sqlite3WhereAddScanStatus(v, pTabList, pLevel, addrExplain);
}
}
@@ -123082,21 +130570,18 @@ SQLITE_PRIVATE void sqlite3WhereEnd(WhereInfo *pWInfo){
}
sqlite3VdbeResolveLabel(v, pLevel->addrBrk);
if( pLevel->addrSkip ){
- sqlite3VdbeAddOp2(v, OP_Goto, 0, pLevel->addrSkip);
+ sqlite3VdbeGoto(v, pLevel->addrSkip);
VdbeComment((v, "next skip-scan on %s", pLoop->u.btree.pIndex->zName));
sqlite3VdbeJumpHere(v, pLevel->addrSkip);
sqlite3VdbeJumpHere(v, pLevel->addrSkip-2);
}
+#ifndef SQLITE_LIKE_DOESNT_MATCH_BLOBS
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);
+ sqlite3VdbeAddOp2(v, OP_DecrJumpZero, (int)(pLevel->iLikeRepCntr>>1),
+ pLevel->addrLikeRep);
VdbeCoverage(v);
}
+#endif
if( pLevel->iLeftJoin ){
addr = sqlite3VdbeAddOp1(v, OP_IfPos, pLevel->iLeftJoin); VdbeCoverage(v);
assert( (pLoop->wsFlags & WHERE_IDX_ONLY)==0
@@ -123110,7 +130595,7 @@ SQLITE_PRIVATE void sqlite3WhereEnd(WhereInfo *pWInfo){
if( pLevel->op==OP_Return ){
sqlite3VdbeAddOp2(v, OP_Gosub, pLevel->p1, pLevel->addrFirst);
}else{
- sqlite3VdbeAddOp2(v, OP_Goto, 0, pLevel->addrFirst);
+ sqlite3VdbeGoto(v, pLevel->addrFirst);
}
sqlite3VdbeJumpHere(v, addr);
}
@@ -123134,26 +130619,12 @@ SQLITE_PRIVATE void sqlite3WhereEnd(WhereInfo *pWInfo){
pLoop = pLevel->pWLoop;
/* For a co-routine, change all OP_Column references to the table of
- ** the co-routine into OP_SCopy of result contained in a register.
+ ** the co-routine into OP_Copy of result contained in a register.
** OP_Rowid becomes OP_Null.
*/
- if( pTabItem->viaCoroutine && !db->mallocFailed ){
- last = sqlite3VdbeCurrentAddr(v);
- k = pLevel->addrBody;
- pOp = sqlite3VdbeGetOp(v, k);
- for(; k<last; k++, pOp++){
- if( pOp->p1!=pLevel->iTabCur ) continue;
- if( pOp->opcode==OP_Column ){
- pOp->opcode = OP_Copy;
- pOp->p1 = pOp->p2 + pTabItem->regResult;
- pOp->p2 = pOp->p3;
- pOp->p3 = 0;
- }else if( pOp->opcode==OP_Rowid ){
- pOp->opcode = OP_Null;
- pOp->p1 = 0;
- pOp->p3 = 0;
- }
- }
+ if( pTabItem->fg.viaCoroutine && !db->mallocFailed ){
+ translateColumnToCopy(v, pLevel->addrBody, pLevel->iTabCur,
+ pTabItem->regResult, 0);
continue;
}
@@ -123167,7 +130638,7 @@ SQLITE_PRIVATE void sqlite3WhereEnd(WhereInfo *pWInfo){
&& (pWInfo->wctrlFlags & WHERE_OMIT_OPEN_CLOSE)==0
){
int ws = pLoop->wsFlags;
- if( !pWInfo->okOnePass && (ws & WHERE_IDX_ONLY)==0 ){
+ if( pWInfo->eOnePass==ONEPASS_OFF && (ws & WHERE_IDX_ONLY)==0 ){
sqlite3VdbeAddOp1(v, OP_Close, pTabItem->iCursor);
}
if( (ws & WHERE_INDEXED)!=0
@@ -123194,7 +130665,10 @@ SQLITE_PRIVATE void sqlite3WhereEnd(WhereInfo *pWInfo){
}else if( pLoop->wsFlags & WHERE_MULTI_OR ){
pIdx = pLevel->u.pCovidx;
}
- if( pIdx && !db->mallocFailed ){
+ if( pIdx
+ && (pWInfo->eOnePass==ONEPASS_OFF || !HasRowid(pIdx->pTable))
+ && !db->mallocFailed
+ ){
last = sqlite3VdbeCurrentAddr(v);
k = pLevel->addrBody;
pOp = sqlite3VdbeGetOp(v, k);
@@ -123206,6 +130680,7 @@ SQLITE_PRIVATE void sqlite3WhereEnd(WhereInfo *pWInfo){
if( !HasRowid(pTab) ){
Index *pPk = sqlite3PrimaryKeyIndex(pTab);
x = pPk->aiColumn[x];
+ assert( x>=0 );
}
x = sqlite3ColumnOfIndex(pIdx, x);
if( x>=0 ){
@@ -123230,19 +130705,34 @@ SQLITE_PRIVATE void sqlite3WhereEnd(WhereInfo *pWInfo){
/************** End of where.c ***********************************************/
/************** Begin file parse.c *******************************************/
-/* Driver template for the LEMON parser generator.
-** The author disclaims copyright to this source code.
+/*
+** 2000-05-29
+**
+** 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 version of "lempar.c" is modified, slightly, for use by SQLite.
-** The only modifications are the addition of a couple of NEVER()
-** macros to disable tests that are needed in the case of a general
-** LALR(1) grammar but which are always false in the
-** specific grammar used by SQLite.
+*************************************************************************
+** Driver template for the LEMON parser generator.
+**
+** The "lemon" program processes an LALR(1) input grammar file, then uses
+** this template to construct a parser. The "lemon" program inserts text
+** at each "%%" line. Also, any "P-a-r-s-e" identifer prefix (without the
+** interstitial "-" characters) contained in this template is changed into
+** the value of the %name directive from the grammar. Otherwise, the content
+** of this template is copied straight through into the generate parser
+** source file.
+**
+** The following is the concatenation of all %include directives from the
+** input grammar file:
*/
-/* First off, code is included that follows the "include" declaration
-** in the input grammar file. */
/* #include <stdio.h> */
+/************ Begin %include sections from the grammar ************************/
+/* #include "sqliteInt.h" */
/*
** Disable all error recovery processing in the parser push-down
@@ -123256,6 +130746,18 @@ SQLITE_PRIVATE void sqlite3WhereEnd(WhereInfo *pWInfo){
#define yytestcase(X) testcase(X)
/*
+** Indicate that sqlite3ParserFree() will never be called with a null
+** pointer.
+*/
+#define YYPARSEFREENEVERNULL 1
+
+/*
+** Alternative datatype for the argument to the malloc() routine passed
+** into sqlite3ParserAlloc(). The default is size_t.
+*/
+#define YYMALLOCARGTYPE u64
+
+/*
** An instance of this structure holds information about the
** LIMIT clause of a SELECT statement.
*/
@@ -123289,6 +130791,15 @@ struct TrigEvent { int a; IdList * b; };
*/
struct AttachKey { int type; Token key; };
+/*
+** Disable lookaside memory allocation for objects that might be
+** shared across database connections.
+*/
+static void disableLookaside(Parse *pParse){
+ pParse->disableLookaside++;
+ pParse->db->lookaside.bDisable++;
+}
+
/*
** For a compound SELECT statement, make sure p->pPrior->pNext==p for
@@ -123325,46 +130836,51 @@ struct AttachKey { int type; Token key; };
** new Expr to populate pOut. Set the span of pOut to be the identifier
** that created the expression.
*/
- static void spanExpr(ExprSpan *pOut, Parse *pParse, int op, Token *pValue){
- pOut->pExpr = sqlite3PExpr(pParse, op, 0, 0, pValue);
- pOut->zStart = pValue->z;
- pOut->zEnd = &pValue->z[pValue->n];
+ static void spanExpr(ExprSpan *pOut, Parse *pParse, int op, Token t){
+ pOut->pExpr = sqlite3PExpr(pParse, op, 0, 0, &t);
+ pOut->zStart = t.z;
+ pOut->zEnd = &t.z[t.n];
}
/* This routine constructs a binary expression node out of two ExprSpan
** objects and uses the result to populate a new ExprSpan object.
*/
static void spanBinaryExpr(
- ExprSpan *pOut, /* Write the result here */
Parse *pParse, /* The parsing context. Errors accumulate here */
int op, /* The binary operation */
- ExprSpan *pLeft, /* The left operand */
+ ExprSpan *pLeft, /* The left operand, and output */
ExprSpan *pRight /* The right operand */
){
- pOut->pExpr = sqlite3PExpr(pParse, op, pLeft->pExpr, pRight->pExpr, 0);
- pOut->zStart = pLeft->zStart;
- pOut->zEnd = pRight->zEnd;
+ pLeft->pExpr = sqlite3PExpr(pParse, op, pLeft->pExpr, pRight->pExpr, 0);
+ pLeft->zEnd = pRight->zEnd;
+ }
+
+ /* If doNot is true, then add a TK_NOT Expr-node wrapper around the
+ ** outside of *ppExpr.
+ */
+ static void exprNot(Parse *pParse, int doNot, ExprSpan *pSpan){
+ if( doNot ){
+ pSpan->pExpr = sqlite3PExpr(pParse, TK_NOT, pSpan->pExpr, 0, 0);
+ }
}
/* Construct an expression node for a unary postfix operator
*/
static void spanUnaryPostfix(
- ExprSpan *pOut, /* Write the new expression node here */
Parse *pParse, /* Parsing context to record errors */
int op, /* The operator */
- ExprSpan *pOperand, /* The operand */
+ ExprSpan *pOperand, /* The operand, and output */
Token *pPostOp /* The operand token for setting the span */
){
- pOut->pExpr = sqlite3PExpr(pParse, op, pOperand->pExpr, 0, 0);
- pOut->zStart = pOperand->zStart;
- pOut->zEnd = &pPostOp->z[pPostOp->n];
+ pOperand->pExpr = sqlite3PExpr(pParse, op, pOperand->pExpr, 0, 0);
+ pOperand->zEnd = &pPostOp->z[pPostOp->n];
}
/* A routine to convert a binary TK_IS or TK_ISNOT expression into a
** unary TK_ISNULL or TK_NOTNULL expression. */
static void binaryToUnaryIfNull(Parse *pParse, Expr *pY, Expr *pA, int op){
sqlite3 *db = pParse->db;
- if( pY && pA && pY->op==TK_NULL ){
+ if( pA && pY && pY->op==TK_NULL ){
pA->op = (u8)op;
sqlite3ExprDelete(db, pA->pRight);
pA->pRight = 0;
@@ -123380,82 +130896,112 @@ struct AttachKey { int type; Token key; };
ExprSpan *pOperand, /* The operand */
Token *pPreOp /* The operand token for setting the span */
){
- pOut->pExpr = sqlite3PExpr(pParse, op, pOperand->pExpr, 0, 0);
pOut->zStart = pPreOp->z;
+ pOut->pExpr = sqlite3PExpr(pParse, op, pOperand->pExpr, 0, 0);
pOut->zEnd = pOperand->zEnd;
}
-/* Next is all token values, in a form suitable for use by makeheaders.
-** This section will be null unless lemon is run with the -m switch.
-*/
-/*
-** These constants (all generated automatically by the parser generator)
-** specify the various kinds of tokens (terminals) that the parser
-** understands.
-**
-** Each symbol here is a terminal symbol in the grammar.
-*/
-/* Make sure the INTERFACE macro is defined.
-*/
-#ifndef INTERFACE
-# define INTERFACE 1
-#endif
-/* The next thing included is series of defines which control
+
+ /* Add a single new term to an ExprList that is used to store a
+ ** list of identifiers. Report an error if the ID list contains
+ ** a COLLATE clause or an ASC or DESC keyword, except ignore the
+ ** error while parsing a legacy schema.
+ */
+ static ExprList *parserAddExprIdListTerm(
+ Parse *pParse,
+ ExprList *pPrior,
+ Token *pIdToken,
+ int hasCollate,
+ int sortOrder
+ ){
+ ExprList *p = sqlite3ExprListAppend(pParse, pPrior, 0);
+ if( (hasCollate || sortOrder!=SQLITE_SO_UNDEFINED)
+ && pParse->db->init.busy==0
+ ){
+ sqlite3ErrorMsg(pParse, "syntax error after column name \"%.*s\"",
+ pIdToken->n, pIdToken->z);
+ }
+ sqlite3ExprListSetName(pParse, p, pIdToken, 1);
+ return p;
+ }
+/**************** End of %include directives **********************************/
+/* These constants specify the various numeric values for terminal symbols
+** in a format understandable to "makeheaders". This section is blank unless
+** "lemon" is run with the "-m" command-line option.
+***************** Begin makeheaders token definitions *************************/
+/**************** End makeheaders token definitions ***************************/
+
+/* The next sections is a series of control #defines.
** various aspects of the generated parser.
-** YYCODETYPE is the data type used for storing terminal
-** and nonterminal numbers. "unsigned char" is
-** used if there are fewer than 250 terminals
-** and nonterminals. "int" is used otherwise.
-** YYNOCODE is a number of type YYCODETYPE which corresponds
-** to no legal terminal or nonterminal number. This
-** number is used to fill in empty slots of the hash
-** table.
+** YYCODETYPE is the data type used to store the integer codes
+** that represent terminal and non-terminal symbols.
+** "unsigned char" is used if there are fewer than
+** 256 symbols. Larger types otherwise.
+** YYNOCODE is a number of type YYCODETYPE that is not used for
+** any terminal or nonterminal symbol.
** YYFALLBACK If defined, this indicates that one or more tokens
-** have fall-back values which should be used if the
-** original value of the token will not parse.
-** YYACTIONTYPE is the data type used for storing terminal
-** and nonterminal numbers. "unsigned char" is
-** used if there are fewer than 250 rules and
-** states combined. "int" is used otherwise.
-** sqlite3ParserTOKENTYPE is the data type used for minor tokens given
-** directly to the parser from the tokenizer.
-** YYMINORTYPE is the data type used for all minor tokens.
+** (also known as: "terminal symbols") have fall-back
+** values which should be used if the original symbol
+** would not parse. This permits keywords to sometimes
+** be used as identifiers, for example.
+** YYACTIONTYPE is the data type used for "action codes" - numbers
+** that indicate what to do in response to the next
+** token.
+** sqlite3ParserTOKENTYPE is the data type used for minor type for terminal
+** symbols. Background: A "minor type" is a semantic
+** value associated with a terminal or non-terminal
+** symbols. For example, for an "ID" terminal symbol,
+** the minor type might be the name of the identifier.
+** Each non-terminal can have a different minor type.
+** Terminal symbols all have the same minor type, though.
+** This macros defines the minor type for terminal
+** symbols.
+** YYMINORTYPE is the data type used for all minor types.
** This is typically a union of many types, one of
** which is sqlite3ParserTOKENTYPE. The entry in the union
-** for base tokens is called "yy0".
+** for terminal symbols is called "yy0".
** YYSTACKDEPTH is the maximum depth of the parser's stack. If
** zero the stack is dynamically sized using realloc()
** sqlite3ParserARG_SDECL A static variable declaration for the %extra_argument
** sqlite3ParserARG_PDECL A parameter declaration for the %extra_argument
** sqlite3ParserARG_STORE Code to store %extra_argument into yypParser
** sqlite3ParserARG_FETCH Code to extract %extra_argument from yypParser
-** YYNSTATE the combined number of states.
-** YYNRULE the number of rules in the grammar
** YYERRORSYMBOL is the code number of the error symbol. If not
** defined, then do no error processing.
+** YYNSTATE the combined number of states.
+** YYNRULE the number of rules in the grammar
+** YY_MAX_SHIFT Maximum value for shift actions
+** YY_MIN_SHIFTREDUCE Minimum value for shift-reduce actions
+** YY_MAX_SHIFTREDUCE Maximum value for shift-reduce actions
+** YY_MIN_REDUCE Maximum value for reduce actions
+** YY_ERROR_ACTION The yy_action[] code for syntax error
+** YY_ACCEPT_ACTION The yy_action[] code for accept
+** YY_NO_ACTION The yy_action[] code for no-op
*/
+#ifndef INTERFACE
+# define INTERFACE 1
+#endif
+/************* Begin control #defines *****************************************/
#define YYCODETYPE unsigned char
-#define YYNOCODE 254
+#define YYNOCODE 251
#define YYACTIONTYPE unsigned short int
-#define YYWILDCARD 70
+#define YYWILDCARD 96
#define sqlite3ParserTOKENTYPE Token
typedef union {
int yyinit;
sqlite3ParserTOKENTYPE yy0;
- Select* yy3;
- ExprList* yy14;
- With* yy59;
- SrcList* yy65;
- struct LikeOp yy96;
- Expr* yy132;
- u8 yy186;
- int yy328;
- ExprSpan yy346;
- struct TrigEvent yy378;
- u16 yy381;
- IdList* yy408;
- struct {int value; int mask;} yy429;
- TriggerStep* yy473;
- struct LimitVal yy476;
+ struct LimitVal yy64;
+ Expr* yy122;
+ Select* yy159;
+ IdList* yy180;
+ struct {int value; int mask;} yy207;
+ struct LikeOp yy318;
+ TriggerStep* yy327;
+ With* yy331;
+ ExprSpan yy342;
+ SrcList* yy347;
+ int yy392;
+ struct TrigEvent yy410;
+ ExprList* yy442;
} YYMINORTYPE;
#ifndef YYSTACKDEPTH
#define YYSTACKDEPTH 100
@@ -123464,16 +131010,18 @@ typedef union {
#define sqlite3ParserARG_PDECL ,Parse *pParse
#define sqlite3ParserARG_FETCH Parse *pParse = yypParser->pParse
#define sqlite3ParserARG_STORE yypParser->pParse = pParse
-#define YYNSTATE 642
-#define YYNRULE 327
#define YYFALLBACK 1
-#define YY_NO_ACTION (YYNSTATE+YYNRULE+2)
-#define YY_ACCEPT_ACTION (YYNSTATE+YYNRULE+1)
-#define YY_ERROR_ACTION (YYNSTATE+YYNRULE)
-
-/* The yyzerominor constant is used to initialize instances of
-** YYMINORTYPE objects to zero. */
-static const YYMINORTYPE yyzerominor = { 0 };
+#define YYNSTATE 440
+#define YYNRULE 326
+#define YY_MAX_SHIFT 439
+#define YY_MIN_SHIFTREDUCE 649
+#define YY_MAX_SHIFTREDUCE 974
+#define YY_MIN_REDUCE 975
+#define YY_MAX_REDUCE 1300
+#define YY_ERROR_ACTION 1301
+#define YY_ACCEPT_ACTION 1302
+#define YY_NO_ACTION 1303
+/************* End control #defines *******************************************/
/* Define the yytestcase() macro to be a no-op if is not already defined
** otherwise.
@@ -123496,16 +131044,20 @@ static const YYMINORTYPE yyzerominor = { 0 };
** Suppose the action integer is N. Then the action is determined as
** follows
**
-** 0 <= N < YYNSTATE Shift N. That is, push the lookahead
+** 0 <= N <= YY_MAX_SHIFT Shift N. That is, push the lookahead
** token onto the stack and goto state N.
**
-** YYNSTATE <= N < YYNSTATE+YYNRULE Reduce by rule N-YYNSTATE.
+** N between YY_MIN_SHIFTREDUCE Shift to an arbitrary state then
+** and YY_MAX_SHIFTREDUCE reduce by rule N-YY_MIN_SHIFTREDUCE.
**
-** N == YYNSTATE+YYNRULE A syntax error has occurred.
+** N between YY_MIN_REDUCE Reduce by rule N-YY_MIN_REDUCE
+** and YY_MAX_REDUCE
+
+** N == YY_ERROR_ACTION A syntax error has occurred.
**
-** N == YYNSTATE+YYNRULE+1 The parser accepts its input.
+** N == YY_ACCEPT_ACTION The parser accepts its input.
**
-** N == YYNSTATE+YYNRULE+2 No such action. Denotes unused
+** N == YY_NO_ACTION No such action. Denotes unused
** slots in the yy_action[] table.
**
** The action table is constructed as a single large table named yy_action[].
@@ -123534,468 +131086,453 @@ static const YYMINORTYPE yyzerominor = { 0 };
** yy_reduce_ofst[] For each state, the offset into yy_action for
** shifting non-terminals after a reduce.
** yy_default[] Default action for each state.
-*/
-#define YY_ACTTAB_COUNT (1497)
+**
+*********** Begin parsing tables **********************************************/
+#define YY_ACTTAB_COUNT (1501)
static const YYACTIONTYPE yy_action[] = {
- /* 0 */ 306, 212, 432, 955, 639, 191, 955, 295, 559, 88,
- /* 10 */ 88, 88, 88, 81, 86, 86, 86, 86, 85, 85,
- /* 20 */ 84, 84, 84, 83, 330, 185, 184, 183, 635, 635,
- /* 30 */ 292, 606, 606, 88, 88, 88, 88, 683, 86, 86,
- /* 40 */ 86, 86, 85, 85, 84, 84, 84, 83, 330, 16,
- /* 50 */ 436, 597, 89, 90, 80, 600, 599, 601, 601, 87,
- /* 60 */ 87, 88, 88, 88, 88, 684, 86, 86, 86, 86,
- /* 70 */ 85, 85, 84, 84, 84, 83, 330, 306, 559, 84,
- /* 80 */ 84, 84, 83, 330, 65, 86, 86, 86, 86, 85,
- /* 90 */ 85, 84, 84, 84, 83, 330, 635, 635, 634, 633,
- /* 100 */ 182, 682, 550, 379, 376, 375, 17, 322, 606, 606,
- /* 110 */ 371, 198, 479, 91, 374, 82, 79, 165, 85, 85,
- /* 120 */ 84, 84, 84, 83, 330, 598, 635, 635, 107, 89,
- /* 130 */ 90, 80, 600, 599, 601, 601, 87, 87, 88, 88,
- /* 140 */ 88, 88, 186, 86, 86, 86, 86, 85, 85, 84,
- /* 150 */ 84, 84, 83, 330, 306, 594, 594, 142, 328, 327,
- /* 160 */ 484, 249, 344, 238, 635, 635, 634, 633, 585, 448,
- /* 170 */ 526, 525, 229, 388, 1, 394, 450, 584, 449, 635,
- /* 180 */ 635, 635, 635, 319, 395, 606, 606, 199, 157, 273,
- /* 190 */ 382, 268, 381, 187, 635, 635, 634, 633, 311, 555,
- /* 200 */ 266, 593, 593, 266, 347, 588, 89, 90, 80, 600,
- /* 210 */ 599, 601, 601, 87, 87, 88, 88, 88, 88, 478,
- /* 220 */ 86, 86, 86, 86, 85, 85, 84, 84, 84, 83,
- /* 230 */ 330, 306, 272, 536, 634, 633, 146, 610, 197, 310,
- /* 240 */ 575, 182, 482, 271, 379, 376, 375, 506, 21, 634,
- /* 250 */ 633, 634, 633, 635, 635, 374, 611, 574, 548, 440,
- /* 260 */ 111, 563, 606, 606, 634, 633, 324, 479, 608, 608,
- /* 270 */ 608, 300, 435, 573, 119, 407, 210, 162, 562, 883,
- /* 280 */ 592, 592, 306, 89, 90, 80, 600, 599, 601, 601,
- /* 290 */ 87, 87, 88, 88, 88, 88, 506, 86, 86, 86,
- /* 300 */ 86, 85, 85, 84, 84, 84, 83, 330, 620, 111,
- /* 310 */ 635, 635, 361, 606, 606, 358, 249, 349, 248, 433,
- /* 320 */ 243, 479, 586, 634, 633, 195, 611, 93, 119, 221,
- /* 330 */ 575, 497, 534, 534, 89, 90, 80, 600, 599, 601,
- /* 340 */ 601, 87, 87, 88, 88, 88, 88, 574, 86, 86,
- /* 350 */ 86, 86, 85, 85, 84, 84, 84, 83, 330, 306,
- /* 360 */ 77, 429, 638, 573, 589, 530, 240, 230, 242, 105,
- /* 370 */ 249, 349, 248, 515, 588, 208, 460, 529, 564, 173,
- /* 380 */ 634, 633, 970, 144, 430, 2, 424, 228, 380, 557,
- /* 390 */ 606, 606, 190, 153, 159, 158, 514, 51, 632, 631,
- /* 400 */ 630, 71, 536, 432, 954, 196, 610, 954, 614, 45,
- /* 410 */ 18, 89, 90, 80, 600, 599, 601, 601, 87, 87,
- /* 420 */ 88, 88, 88, 88, 261, 86, 86, 86, 86, 85,
- /* 430 */ 85, 84, 84, 84, 83, 330, 306, 608, 608, 608,
- /* 440 */ 542, 424, 402, 385, 241, 506, 451, 320, 211, 543,
- /* 450 */ 164, 436, 386, 293, 451, 587, 108, 496, 111, 334,
- /* 460 */ 391, 591, 424, 614, 27, 452, 453, 606, 606, 72,
- /* 470 */ 257, 70, 259, 452, 339, 342, 564, 582, 68, 415,
- /* 480 */ 469, 328, 327, 62, 614, 45, 110, 393, 89, 90,
- /* 490 */ 80, 600, 599, 601, 601, 87, 87, 88, 88, 88,
- /* 500 */ 88, 152, 86, 86, 86, 86, 85, 85, 84, 84,
- /* 510 */ 84, 83, 330, 306, 110, 499, 520, 538, 402, 389,
- /* 520 */ 424, 110, 566, 500, 593, 593, 454, 82, 79, 165,
- /* 530 */ 424, 591, 384, 564, 340, 615, 188, 162, 424, 350,
- /* 540 */ 616, 424, 614, 44, 606, 606, 445, 582, 300, 434,
- /* 550 */ 151, 19, 614, 9, 568, 580, 348, 615, 469, 567,
- /* 560 */ 614, 26, 616, 614, 45, 89, 90, 80, 600, 599,
- /* 570 */ 601, 601, 87, 87, 88, 88, 88, 88, 411, 86,
- /* 580 */ 86, 86, 86, 85, 85, 84, 84, 84, 83, 330,
- /* 590 */ 306, 579, 110, 578, 521, 282, 433, 398, 400, 255,
- /* 600 */ 486, 82, 79, 165, 487, 164, 82, 79, 165, 488,
- /* 610 */ 488, 364, 387, 424, 544, 544, 509, 350, 362, 155,
- /* 620 */ 191, 606, 606, 559, 642, 640, 333, 82, 79, 165,
- /* 630 */ 305, 564, 507, 312, 357, 614, 45, 329, 596, 595,
- /* 640 */ 194, 337, 89, 90, 80, 600, 599, 601, 601, 87,
- /* 650 */ 87, 88, 88, 88, 88, 424, 86, 86, 86, 86,
- /* 660 */ 85, 85, 84, 84, 84, 83, 330, 306, 20, 323,
- /* 670 */ 150, 263, 211, 543, 421, 596, 595, 614, 22, 424,
- /* 680 */ 193, 424, 284, 424, 391, 424, 509, 424, 577, 424,
- /* 690 */ 186, 335, 424, 559, 424, 313, 120, 546, 606, 606,
- /* 700 */ 67, 614, 47, 614, 50, 614, 48, 614, 100, 614,
- /* 710 */ 99, 614, 101, 576, 614, 102, 614, 109, 326, 89,
- /* 720 */ 90, 80, 600, 599, 601, 601, 87, 87, 88, 88,
- /* 730 */ 88, 88, 424, 86, 86, 86, 86, 85, 85, 84,
- /* 740 */ 84, 84, 83, 330, 306, 424, 311, 424, 585, 54,
- /* 750 */ 424, 516, 517, 590, 614, 112, 424, 584, 424, 572,
- /* 760 */ 424, 195, 424, 571, 424, 67, 424, 614, 94, 614,
- /* 770 */ 98, 424, 614, 97, 264, 606, 606, 195, 614, 46,
- /* 780 */ 614, 96, 614, 30, 614, 49, 614, 115, 614, 114,
- /* 790 */ 418, 229, 388, 614, 113, 306, 89, 90, 80, 600,
- /* 800 */ 599, 601, 601, 87, 87, 88, 88, 88, 88, 424,
- /* 810 */ 86, 86, 86, 86, 85, 85, 84, 84, 84, 83,
- /* 820 */ 330, 119, 424, 590, 110, 372, 606, 606, 195, 53,
- /* 830 */ 250, 614, 29, 195, 472, 438, 729, 190, 302, 498,
- /* 840 */ 14, 523, 641, 2, 614, 43, 306, 89, 90, 80,
- /* 850 */ 600, 599, 601, 601, 87, 87, 88, 88, 88, 88,
- /* 860 */ 424, 86, 86, 86, 86, 85, 85, 84, 84, 84,
- /* 870 */ 83, 330, 424, 613, 964, 964, 354, 606, 606, 420,
- /* 880 */ 312, 64, 614, 42, 391, 355, 283, 437, 301, 255,
- /* 890 */ 414, 410, 495, 492, 614, 28, 471, 306, 89, 90,
- /* 900 */ 80, 600, 599, 601, 601, 87, 87, 88, 88, 88,
- /* 910 */ 88, 424, 86, 86, 86, 86, 85, 85, 84, 84,
- /* 920 */ 84, 83, 330, 424, 110, 110, 110, 110, 606, 606,
- /* 930 */ 110, 254, 13, 614, 41, 532, 531, 283, 481, 531,
- /* 940 */ 457, 284, 119, 561, 356, 614, 40, 284, 306, 89,
- /* 950 */ 78, 80, 600, 599, 601, 601, 87, 87, 88, 88,
- /* 960 */ 88, 88, 424, 86, 86, 86, 86, 85, 85, 84,
- /* 970 */ 84, 84, 83, 330, 110, 424, 341, 220, 555, 606,
- /* 980 */ 606, 351, 555, 318, 614, 95, 413, 255, 83, 330,
- /* 990 */ 284, 284, 255, 640, 333, 356, 255, 614, 39, 306,
- /* 1000 */ 356, 90, 80, 600, 599, 601, 601, 87, 87, 88,
- /* 1010 */ 88, 88, 88, 424, 86, 86, 86, 86, 85, 85,
- /* 1020 */ 84, 84, 84, 83, 330, 424, 317, 316, 141, 465,
- /* 1030 */ 606, 606, 219, 619, 463, 614, 10, 417, 462, 255,
- /* 1040 */ 189, 510, 553, 351, 207, 363, 161, 614, 38, 315,
- /* 1050 */ 218, 255, 255, 80, 600, 599, 601, 601, 87, 87,
- /* 1060 */ 88, 88, 88, 88, 424, 86, 86, 86, 86, 85,
- /* 1070 */ 85, 84, 84, 84, 83, 330, 76, 419, 255, 3,
- /* 1080 */ 878, 461, 424, 247, 331, 331, 614, 37, 217, 76,
- /* 1090 */ 419, 390, 3, 216, 215, 422, 4, 331, 331, 424,
- /* 1100 */ 547, 12, 424, 545, 614, 36, 424, 541, 422, 424,
- /* 1110 */ 540, 424, 214, 424, 408, 424, 539, 403, 605, 605,
- /* 1120 */ 237, 614, 25, 119, 614, 24, 588, 408, 614, 45,
- /* 1130 */ 118, 614, 35, 614, 34, 614, 33, 614, 23, 588,
- /* 1140 */ 60, 223, 603, 602, 513, 378, 73, 74, 140, 139,
- /* 1150 */ 424, 110, 265, 75, 426, 425, 59, 424, 610, 73,
- /* 1160 */ 74, 549, 402, 404, 424, 373, 75, 426, 425, 604,
- /* 1170 */ 138, 610, 614, 11, 392, 76, 419, 181, 3, 614,
- /* 1180 */ 32, 271, 369, 331, 331, 493, 614, 31, 149, 608,
- /* 1190 */ 608, 608, 607, 15, 422, 365, 614, 8, 137, 489,
- /* 1200 */ 136, 190, 608, 608, 608, 607, 15, 485, 176, 135,
- /* 1210 */ 7, 252, 477, 408, 174, 133, 175, 474, 57, 56,
- /* 1220 */ 132, 130, 119, 76, 419, 588, 3, 468, 245, 464,
- /* 1230 */ 171, 331, 331, 125, 123, 456, 447, 122, 446, 104,
- /* 1240 */ 336, 231, 422, 166, 154, 73, 74, 332, 116, 431,
- /* 1250 */ 121, 309, 75, 426, 425, 222, 106, 610, 308, 637,
- /* 1260 */ 204, 408, 629, 627, 628, 6, 200, 428, 427, 290,
- /* 1270 */ 203, 622, 201, 588, 62, 63, 289, 66, 419, 399,
- /* 1280 */ 3, 401, 288, 92, 143, 331, 331, 287, 608, 608,
- /* 1290 */ 608, 607, 15, 73, 74, 227, 422, 325, 69, 416,
- /* 1300 */ 75, 426, 425, 612, 412, 610, 192, 61, 569, 209,
- /* 1310 */ 396, 226, 278, 225, 383, 408, 527, 558, 276, 533,
- /* 1320 */ 552, 528, 321, 523, 370, 508, 180, 588, 494, 179,
- /* 1330 */ 366, 117, 253, 269, 522, 503, 608, 608, 608, 607,
- /* 1340 */ 15, 551, 502, 58, 274, 524, 178, 73, 74, 304,
- /* 1350 */ 501, 368, 303, 206, 75, 426, 425, 491, 360, 610,
- /* 1360 */ 213, 177, 483, 131, 345, 298, 297, 296, 202, 294,
- /* 1370 */ 480, 490, 466, 134, 172, 129, 444, 346, 470, 128,
- /* 1380 */ 314, 459, 103, 127, 126, 148, 124, 167, 443, 235,
- /* 1390 */ 608, 608, 608, 607, 15, 442, 439, 623, 234, 299,
- /* 1400 */ 145, 583, 291, 377, 581, 160, 119, 156, 270, 636,
- /* 1410 */ 971, 169, 279, 626, 520, 625, 473, 624, 170, 621,
- /* 1420 */ 618, 119, 168, 55, 409, 423, 537, 609, 286, 285,
- /* 1430 */ 405, 570, 560, 556, 5, 52, 458, 554, 147, 267,
- /* 1440 */ 519, 504, 518, 406, 262, 239, 260, 512, 343, 511,
- /* 1450 */ 258, 353, 565, 256, 224, 251, 359, 277, 275, 476,
- /* 1460 */ 475, 246, 352, 244, 467, 455, 236, 233, 232, 307,
- /* 1470 */ 441, 281, 205, 163, 397, 280, 535, 505, 330, 617,
- /* 1480 */ 971, 971, 971, 971, 367, 971, 971, 971, 971, 971,
- /* 1490 */ 971, 971, 971, 971, 971, 971, 338,
+ /* 0 */ 315, 810, 339, 804, 5, 194, 194, 798, 92, 93,
+ /* 10 */ 83, 819, 819, 831, 834, 823, 823, 90, 90, 91,
+ /* 20 */ 91, 91, 91, 290, 89, 89, 89, 89, 88, 88,
+ /* 30 */ 87, 87, 87, 86, 339, 315, 952, 952, 803, 803,
+ /* 40 */ 803, 922, 342, 92, 93, 83, 819, 819, 831, 834,
+ /* 50 */ 823, 823, 90, 90, 91, 91, 91, 91, 123, 89,
+ /* 60 */ 89, 89, 89, 88, 88, 87, 87, 87, 86, 339,
+ /* 70 */ 88, 88, 87, 87, 87, 86, 339, 772, 952, 952,
+ /* 80 */ 315, 87, 87, 87, 86, 339, 773, 68, 92, 93,
+ /* 90 */ 83, 819, 819, 831, 834, 823, 823, 90, 90, 91,
+ /* 100 */ 91, 91, 91, 434, 89, 89, 89, 89, 88, 88,
+ /* 110 */ 87, 87, 87, 86, 339, 1302, 146, 921, 2, 315,
+ /* 120 */ 427, 24, 679, 953, 48, 86, 339, 92, 93, 83,
+ /* 130 */ 819, 819, 831, 834, 823, 823, 90, 90, 91, 91,
+ /* 140 */ 91, 91, 94, 89, 89, 89, 89, 88, 88, 87,
+ /* 150 */ 87, 87, 86, 339, 933, 933, 315, 259, 412, 398,
+ /* 160 */ 396, 57, 733, 733, 92, 93, 83, 819, 819, 831,
+ /* 170 */ 834, 823, 823, 90, 90, 91, 91, 91, 91, 56,
+ /* 180 */ 89, 89, 89, 89, 88, 88, 87, 87, 87, 86,
+ /* 190 */ 339, 315, 1245, 922, 342, 268, 934, 935, 241, 92,
+ /* 200 */ 93, 83, 819, 819, 831, 834, 823, 823, 90, 90,
+ /* 210 */ 91, 91, 91, 91, 291, 89, 89, 89, 89, 88,
+ /* 220 */ 88, 87, 87, 87, 86, 339, 315, 913, 1295, 682,
+ /* 230 */ 687, 1295, 233, 397, 92, 93, 83, 819, 819, 831,
+ /* 240 */ 834, 823, 823, 90, 90, 91, 91, 91, 91, 326,
+ /* 250 */ 89, 89, 89, 89, 88, 88, 87, 87, 87, 86,
+ /* 260 */ 339, 315, 85, 82, 168, 680, 431, 938, 939, 92,
+ /* 270 */ 93, 83, 819, 819, 831, 834, 823, 823, 90, 90,
+ /* 280 */ 91, 91, 91, 91, 291, 89, 89, 89, 89, 88,
+ /* 290 */ 88, 87, 87, 87, 86, 339, 315, 319, 913, 1296,
+ /* 300 */ 797, 911, 1296, 681, 92, 93, 83, 819, 819, 831,
+ /* 310 */ 834, 823, 823, 90, 90, 91, 91, 91, 91, 335,
+ /* 320 */ 89, 89, 89, 89, 88, 88, 87, 87, 87, 86,
+ /* 330 */ 339, 315, 876, 876, 373, 85, 82, 168, 944, 92,
+ /* 340 */ 93, 83, 819, 819, 831, 834, 823, 823, 90, 90,
+ /* 350 */ 91, 91, 91, 91, 896, 89, 89, 89, 89, 88,
+ /* 360 */ 88, 87, 87, 87, 86, 339, 315, 370, 307, 973,
+ /* 370 */ 367, 1, 911, 433, 92, 93, 83, 819, 819, 831,
+ /* 380 */ 834, 823, 823, 90, 90, 91, 91, 91, 91, 189,
+ /* 390 */ 89, 89, 89, 89, 88, 88, 87, 87, 87, 86,
+ /* 400 */ 339, 315, 720, 948, 933, 933, 149, 718, 948, 92,
+ /* 410 */ 93, 83, 819, 819, 831, 834, 823, 823, 90, 90,
+ /* 420 */ 91, 91, 91, 91, 434, 89, 89, 89, 89, 88,
+ /* 430 */ 88, 87, 87, 87, 86, 339, 338, 938, 939, 947,
+ /* 440 */ 694, 940, 974, 315, 953, 48, 934, 935, 715, 689,
+ /* 450 */ 71, 92, 93, 83, 819, 819, 831, 834, 823, 823,
+ /* 460 */ 90, 90, 91, 91, 91, 91, 320, 89, 89, 89,
+ /* 470 */ 89, 88, 88, 87, 87, 87, 86, 339, 315, 412,
+ /* 480 */ 403, 820, 820, 832, 835, 74, 92, 81, 83, 819,
+ /* 490 */ 819, 831, 834, 823, 823, 90, 90, 91, 91, 91,
+ /* 500 */ 91, 698, 89, 89, 89, 89, 88, 88, 87, 87,
+ /* 510 */ 87, 86, 339, 315, 259, 654, 655, 656, 393, 111,
+ /* 520 */ 331, 153, 93, 83, 819, 819, 831, 834, 823, 823,
+ /* 530 */ 90, 90, 91, 91, 91, 91, 434, 89, 89, 89,
+ /* 540 */ 89, 88, 88, 87, 87, 87, 86, 339, 315, 188,
+ /* 550 */ 187, 186, 824, 937, 328, 219, 953, 48, 83, 819,
+ /* 560 */ 819, 831, 834, 823, 823, 90, 90, 91, 91, 91,
+ /* 570 */ 91, 956, 89, 89, 89, 89, 88, 88, 87, 87,
+ /* 580 */ 87, 86, 339, 79, 429, 738, 3, 1174, 955, 348,
+ /* 590 */ 737, 332, 792, 933, 933, 937, 79, 429, 730, 3,
+ /* 600 */ 203, 160, 278, 391, 273, 390, 190, 892, 434, 400,
+ /* 610 */ 741, 76, 77, 271, 287, 253, 353, 242, 78, 340,
+ /* 620 */ 340, 85, 82, 168, 76, 77, 233, 397, 953, 48,
+ /* 630 */ 432, 78, 340, 340, 277, 934, 935, 185, 439, 651,
+ /* 640 */ 388, 385, 384, 432, 234, 276, 107, 418, 349, 337,
+ /* 650 */ 336, 383, 893, 728, 215, 949, 123, 971, 308, 810,
+ /* 660 */ 418, 436, 435, 412, 394, 798, 400, 873, 894, 123,
+ /* 670 */ 721, 872, 810, 889, 436, 435, 215, 949, 798, 351,
+ /* 680 */ 722, 697, 380, 434, 771, 371, 22, 434, 400, 79,
+ /* 690 */ 429, 232, 3, 189, 413, 870, 803, 803, 803, 805,
+ /* 700 */ 18, 54, 148, 953, 48, 956, 113, 953, 9, 803,
+ /* 710 */ 803, 803, 805, 18, 310, 123, 748, 76, 77, 742,
+ /* 720 */ 123, 325, 955, 866, 78, 340, 340, 113, 350, 359,
+ /* 730 */ 85, 82, 168, 343, 960, 960, 432, 770, 412, 414,
+ /* 740 */ 407, 23, 1240, 1240, 79, 429, 357, 3, 166, 91,
+ /* 750 */ 91, 91, 91, 418, 89, 89, 89, 89, 88, 88,
+ /* 760 */ 87, 87, 87, 86, 339, 810, 434, 436, 435, 792,
+ /* 770 */ 320, 798, 76, 77, 789, 271, 123, 434, 360, 78,
+ /* 780 */ 340, 340, 864, 85, 82, 168, 953, 9, 395, 743,
+ /* 790 */ 360, 432, 253, 358, 252, 933, 933, 953, 30, 889,
+ /* 800 */ 327, 216, 803, 803, 803, 805, 18, 113, 418, 89,
+ /* 810 */ 89, 89, 89, 88, 88, 87, 87, 87, 86, 339,
+ /* 820 */ 810, 113, 436, 435, 792, 185, 798, 288, 388, 385,
+ /* 830 */ 384, 123, 113, 920, 2, 796, 696, 934, 935, 383,
+ /* 840 */ 69, 429, 434, 3, 218, 110, 738, 253, 358, 252,
+ /* 850 */ 434, 737, 933, 933, 892, 359, 222, 803, 803, 803,
+ /* 860 */ 805, 18, 953, 47, 933, 933, 933, 933, 76, 77,
+ /* 870 */ 953, 9, 366, 904, 217, 78, 340, 340, 677, 305,
+ /* 880 */ 304, 303, 206, 301, 224, 259, 664, 432, 337, 336,
+ /* 890 */ 434, 228, 247, 144, 934, 935, 933, 933, 667, 893,
+ /* 900 */ 324, 1259, 96, 434, 418, 796, 934, 935, 934, 935,
+ /* 910 */ 953, 48, 401, 148, 289, 894, 810, 417, 436, 435,
+ /* 920 */ 677, 759, 798, 953, 9, 314, 220, 162, 161, 170,
+ /* 930 */ 402, 239, 953, 8, 194, 683, 683, 410, 934, 935,
+ /* 940 */ 238, 959, 933, 933, 225, 408, 945, 365, 957, 212,
+ /* 950 */ 958, 172, 757, 803, 803, 803, 805, 18, 173, 365,
+ /* 960 */ 176, 123, 171, 113, 244, 952, 246, 434, 356, 796,
+ /* 970 */ 372, 365, 236, 960, 960, 810, 290, 804, 191, 165,
+ /* 980 */ 852, 798, 259, 316, 934, 935, 237, 953, 34, 404,
+ /* 990 */ 91, 91, 91, 91, 84, 89, 89, 89, 89, 88,
+ /* 1000 */ 88, 87, 87, 87, 86, 339, 701, 952, 434, 240,
+ /* 1010 */ 347, 758, 803, 803, 803, 434, 245, 1179, 434, 389,
+ /* 1020 */ 434, 376, 434, 895, 167, 434, 405, 702, 953, 35,
+ /* 1030 */ 673, 321, 221, 434, 333, 953, 11, 434, 953, 26,
+ /* 1040 */ 953, 36, 953, 37, 251, 953, 38, 434, 259, 434,
+ /* 1050 */ 757, 434, 329, 953, 27, 434, 223, 953, 28, 434,
+ /* 1060 */ 690, 434, 67, 434, 65, 434, 862, 953, 39, 953,
+ /* 1070 */ 40, 953, 41, 423, 434, 953, 10, 434, 772, 953,
+ /* 1080 */ 42, 953, 98, 953, 43, 953, 44, 773, 434, 346,
+ /* 1090 */ 434, 75, 434, 73, 953, 31, 434, 953, 45, 434,
+ /* 1100 */ 259, 434, 690, 434, 757, 434, 887, 434, 953, 46,
+ /* 1110 */ 953, 32, 953, 115, 434, 266, 953, 116, 951, 953,
+ /* 1120 */ 117, 953, 52, 953, 33, 953, 99, 953, 49, 726,
+ /* 1130 */ 434, 909, 434, 19, 953, 100, 434, 344, 434, 113,
+ /* 1140 */ 434, 258, 692, 434, 259, 434, 670, 434, 20, 434,
+ /* 1150 */ 953, 101, 953, 97, 434, 259, 953, 114, 953, 112,
+ /* 1160 */ 953, 105, 113, 953, 104, 953, 102, 953, 103, 953,
+ /* 1170 */ 51, 434, 148, 434, 953, 53, 167, 434, 259, 113,
+ /* 1180 */ 300, 307, 912, 363, 311, 860, 248, 261, 209, 264,
+ /* 1190 */ 416, 953, 50, 953, 25, 420, 727, 953, 29, 430,
+ /* 1200 */ 321, 424, 757, 428, 322, 124, 1269, 214, 165, 710,
+ /* 1210 */ 859, 908, 806, 794, 309, 158, 193, 361, 254, 723,
+ /* 1220 */ 364, 67, 381, 269, 735, 199, 67, 70, 113, 700,
+ /* 1230 */ 699, 707, 708, 884, 113, 766, 113, 855, 193, 883,
+ /* 1240 */ 199, 869, 869, 675, 868, 868, 109, 368, 255, 260,
+ /* 1250 */ 263, 280, 859, 265, 806, 974, 267, 711, 695, 272,
+ /* 1260 */ 764, 282, 795, 284, 150, 744, 755, 415, 292, 293,
+ /* 1270 */ 802, 678, 672, 661, 660, 662, 927, 6, 306, 386,
+ /* 1280 */ 352, 786, 243, 250, 886, 362, 163, 286, 419, 298,
+ /* 1290 */ 930, 159, 968, 196, 126, 903, 901, 965, 55, 58,
+ /* 1300 */ 323, 275, 857, 136, 147, 694, 856, 121, 65, 354,
+ /* 1310 */ 355, 379, 175, 61, 151, 369, 180, 871, 375, 129,
+ /* 1320 */ 257, 756, 210, 181, 145, 131, 132, 377, 262, 663,
+ /* 1330 */ 133, 134, 139, 783, 791, 182, 392, 183, 312, 330,
+ /* 1340 */ 714, 888, 713, 851, 692, 195, 712, 406, 686, 705,
+ /* 1350 */ 313, 685, 64, 839, 274, 72, 684, 334, 942, 95,
+ /* 1360 */ 752, 279, 281, 704, 753, 751, 422, 283, 411, 750,
+ /* 1370 */ 426, 66, 204, 409, 21, 285, 928, 669, 437, 205,
+ /* 1380 */ 207, 208, 438, 658, 657, 652, 118, 108, 119, 226,
+ /* 1390 */ 650, 341, 157, 235, 169, 345, 106, 734, 790, 296,
+ /* 1400 */ 294, 295, 120, 297, 867, 865, 127, 128, 130, 724,
+ /* 1410 */ 229, 174, 249, 882, 137, 230, 138, 135, 885, 231,
+ /* 1420 */ 59, 60, 177, 881, 7, 178, 12, 179, 256, 874,
+ /* 1430 */ 140, 193, 962, 374, 141, 152, 666, 378, 276, 184,
+ /* 1440 */ 270, 122, 142, 382, 387, 62, 13, 14, 703, 63,
+ /* 1450 */ 125, 317, 318, 227, 809, 808, 837, 732, 15, 164,
+ /* 1460 */ 736, 4, 765, 211, 399, 213, 192, 143, 760, 70,
+ /* 1470 */ 67, 16, 17, 838, 836, 891, 841, 890, 198, 197,
+ /* 1480 */ 917, 154, 421, 923, 918, 155, 200, 977, 425, 840,
+ /* 1490 */ 156, 201, 807, 676, 80, 302, 299, 977, 202, 1261,
+ /* 1500 */ 1260,
};
static const YYCODETYPE yy_lookahead[] = {
- /* 0 */ 19, 22, 22, 23, 1, 24, 26, 15, 27, 80,
- /* 10 */ 81, 82, 83, 84, 85, 86, 87, 88, 89, 90,
- /* 20 */ 91, 92, 93, 94, 95, 108, 109, 110, 27, 28,
- /* 30 */ 23, 50, 51, 80, 81, 82, 83, 122, 85, 86,
- /* 40 */ 87, 88, 89, 90, 91, 92, 93, 94, 95, 22,
- /* 50 */ 70, 23, 71, 72, 73, 74, 75, 76, 77, 78,
- /* 60 */ 79, 80, 81, 82, 83, 122, 85, 86, 87, 88,
- /* 70 */ 89, 90, 91, 92, 93, 94, 95, 19, 97, 91,
- /* 80 */ 92, 93, 94, 95, 26, 85, 86, 87, 88, 89,
- /* 90 */ 90, 91, 92, 93, 94, 95, 27, 28, 97, 98,
- /* 100 */ 99, 122, 211, 102, 103, 104, 79, 19, 50, 51,
- /* 110 */ 19, 122, 59, 55, 113, 224, 225, 226, 89, 90,
- /* 120 */ 91, 92, 93, 94, 95, 23, 27, 28, 26, 71,
- /* 130 */ 72, 73, 74, 75, 76, 77, 78, 79, 80, 81,
- /* 140 */ 82, 83, 51, 85, 86, 87, 88, 89, 90, 91,
- /* 150 */ 92, 93, 94, 95, 19, 132, 133, 58, 89, 90,
- /* 160 */ 21, 108, 109, 110, 27, 28, 97, 98, 33, 100,
- /* 170 */ 7, 8, 119, 120, 22, 19, 107, 42, 109, 27,
- /* 180 */ 28, 27, 28, 95, 28, 50, 51, 99, 100, 101,
- /* 190 */ 102, 103, 104, 105, 27, 28, 97, 98, 107, 152,
- /* 200 */ 112, 132, 133, 112, 65, 69, 71, 72, 73, 74,
- /* 210 */ 75, 76, 77, 78, 79, 80, 81, 82, 83, 11,
- /* 220 */ 85, 86, 87, 88, 89, 90, 91, 92, 93, 94,
- /* 230 */ 95, 19, 101, 97, 97, 98, 24, 101, 122, 157,
- /* 240 */ 12, 99, 103, 112, 102, 103, 104, 152, 22, 97,
- /* 250 */ 98, 97, 98, 27, 28, 113, 27, 29, 91, 164,
- /* 260 */ 165, 124, 50, 51, 97, 98, 219, 59, 132, 133,
- /* 270 */ 134, 22, 23, 45, 66, 47, 212, 213, 124, 140,
- /* 280 */ 132, 133, 19, 71, 72, 73, 74, 75, 76, 77,
- /* 290 */ 78, 79, 80, 81, 82, 83, 152, 85, 86, 87,
- /* 300 */ 88, 89, 90, 91, 92, 93, 94, 95, 164, 165,
- /* 310 */ 27, 28, 230, 50, 51, 233, 108, 109, 110, 70,
- /* 320 */ 16, 59, 23, 97, 98, 26, 97, 22, 66, 185,
- /* 330 */ 12, 187, 27, 28, 71, 72, 73, 74, 75, 76,
- /* 340 */ 77, 78, 79, 80, 81, 82, 83, 29, 85, 86,
- /* 350 */ 87, 88, 89, 90, 91, 92, 93, 94, 95, 19,
- /* 360 */ 22, 148, 149, 45, 23, 47, 62, 154, 64, 156,
- /* 370 */ 108, 109, 110, 37, 69, 23, 163, 59, 26, 26,
- /* 380 */ 97, 98, 144, 145, 146, 147, 152, 200, 52, 23,
- /* 390 */ 50, 51, 26, 22, 89, 90, 60, 210, 7, 8,
- /* 400 */ 9, 138, 97, 22, 23, 26, 101, 26, 174, 175,
- /* 410 */ 197, 71, 72, 73, 74, 75, 76, 77, 78, 79,
- /* 420 */ 80, 81, 82, 83, 16, 85, 86, 87, 88, 89,
- /* 430 */ 90, 91, 92, 93, 94, 95, 19, 132, 133, 134,
- /* 440 */ 23, 152, 208, 209, 140, 152, 152, 111, 195, 196,
- /* 450 */ 98, 70, 163, 160, 152, 23, 22, 164, 165, 246,
- /* 460 */ 207, 27, 152, 174, 175, 171, 172, 50, 51, 137,
- /* 470 */ 62, 139, 64, 171, 172, 222, 124, 27, 138, 24,
- /* 480 */ 163, 89, 90, 130, 174, 175, 197, 163, 71, 72,
- /* 490 */ 73, 74, 75, 76, 77, 78, 79, 80, 81, 82,
- /* 500 */ 83, 22, 85, 86, 87, 88, 89, 90, 91, 92,
- /* 510 */ 93, 94, 95, 19, 197, 181, 182, 23, 208, 209,
- /* 520 */ 152, 197, 26, 189, 132, 133, 232, 224, 225, 226,
- /* 530 */ 152, 97, 91, 26, 232, 116, 212, 213, 152, 222,
- /* 540 */ 121, 152, 174, 175, 50, 51, 243, 97, 22, 23,
- /* 550 */ 22, 234, 174, 175, 177, 23, 239, 116, 163, 177,
- /* 560 */ 174, 175, 121, 174, 175, 71, 72, 73, 74, 75,
- /* 570 */ 76, 77, 78, 79, 80, 81, 82, 83, 24, 85,
- /* 580 */ 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
- /* 590 */ 19, 23, 197, 11, 23, 227, 70, 208, 220, 152,
- /* 600 */ 31, 224, 225, 226, 35, 98, 224, 225, 226, 108,
- /* 610 */ 109, 110, 115, 152, 117, 118, 27, 222, 49, 123,
- /* 620 */ 24, 50, 51, 27, 0, 1, 2, 224, 225, 226,
- /* 630 */ 166, 124, 168, 169, 239, 174, 175, 170, 171, 172,
- /* 640 */ 22, 194, 71, 72, 73, 74, 75, 76, 77, 78,
- /* 650 */ 79, 80, 81, 82, 83, 152, 85, 86, 87, 88,
- /* 660 */ 89, 90, 91, 92, 93, 94, 95, 19, 22, 208,
- /* 670 */ 24, 23, 195, 196, 170, 171, 172, 174, 175, 152,
- /* 680 */ 26, 152, 152, 152, 207, 152, 97, 152, 23, 152,
- /* 690 */ 51, 244, 152, 97, 152, 247, 248, 23, 50, 51,
- /* 700 */ 26, 174, 175, 174, 175, 174, 175, 174, 175, 174,
- /* 710 */ 175, 174, 175, 23, 174, 175, 174, 175, 188, 71,
- /* 720 */ 72, 73, 74, 75, 76, 77, 78, 79, 80, 81,
- /* 730 */ 82, 83, 152, 85, 86, 87, 88, 89, 90, 91,
- /* 740 */ 92, 93, 94, 95, 19, 152, 107, 152, 33, 24,
- /* 750 */ 152, 100, 101, 27, 174, 175, 152, 42, 152, 23,
- /* 760 */ 152, 26, 152, 23, 152, 26, 152, 174, 175, 174,
- /* 770 */ 175, 152, 174, 175, 23, 50, 51, 26, 174, 175,
- /* 780 */ 174, 175, 174, 175, 174, 175, 174, 175, 174, 175,
- /* 790 */ 163, 119, 120, 174, 175, 19, 71, 72, 73, 74,
- /* 800 */ 75, 76, 77, 78, 79, 80, 81, 82, 83, 152,
- /* 810 */ 85, 86, 87, 88, 89, 90, 91, 92, 93, 94,
- /* 820 */ 95, 66, 152, 97, 197, 23, 50, 51, 26, 53,
- /* 830 */ 23, 174, 175, 26, 23, 23, 23, 26, 26, 26,
- /* 840 */ 36, 106, 146, 147, 174, 175, 19, 71, 72, 73,
- /* 850 */ 74, 75, 76, 77, 78, 79, 80, 81, 82, 83,
- /* 860 */ 152, 85, 86, 87, 88, 89, 90, 91, 92, 93,
- /* 870 */ 94, 95, 152, 196, 119, 120, 19, 50, 51, 168,
- /* 880 */ 169, 26, 174, 175, 207, 28, 152, 249, 250, 152,
- /* 890 */ 163, 163, 163, 163, 174, 175, 163, 19, 71, 72,
- /* 900 */ 73, 74, 75, 76, 77, 78, 79, 80, 81, 82,
- /* 910 */ 83, 152, 85, 86, 87, 88, 89, 90, 91, 92,
- /* 920 */ 93, 94, 95, 152, 197, 197, 197, 197, 50, 51,
- /* 930 */ 197, 194, 36, 174, 175, 191, 192, 152, 191, 192,
- /* 940 */ 163, 152, 66, 124, 152, 174, 175, 152, 19, 71,
- /* 950 */ 72, 73, 74, 75, 76, 77, 78, 79, 80, 81,
- /* 960 */ 82, 83, 152, 85, 86, 87, 88, 89, 90, 91,
- /* 970 */ 92, 93, 94, 95, 197, 152, 100, 188, 152, 50,
- /* 980 */ 51, 152, 152, 188, 174, 175, 252, 152, 94, 95,
- /* 990 */ 152, 152, 152, 1, 2, 152, 152, 174, 175, 19,
- /* 1000 */ 152, 72, 73, 74, 75, 76, 77, 78, 79, 80,
- /* 1010 */ 81, 82, 83, 152, 85, 86, 87, 88, 89, 90,
- /* 1020 */ 91, 92, 93, 94, 95, 152, 188, 188, 22, 194,
- /* 1030 */ 50, 51, 240, 173, 194, 174, 175, 252, 194, 152,
- /* 1040 */ 36, 181, 28, 152, 23, 219, 122, 174, 175, 219,
- /* 1050 */ 221, 152, 152, 73, 74, 75, 76, 77, 78, 79,
- /* 1060 */ 80, 81, 82, 83, 152, 85, 86, 87, 88, 89,
- /* 1070 */ 90, 91, 92, 93, 94, 95, 19, 20, 152, 22,
- /* 1080 */ 23, 194, 152, 240, 27, 28, 174, 175, 240, 19,
- /* 1090 */ 20, 26, 22, 194, 194, 38, 22, 27, 28, 152,
- /* 1100 */ 23, 22, 152, 116, 174, 175, 152, 23, 38, 152,
- /* 1110 */ 23, 152, 221, 152, 57, 152, 23, 163, 50, 51,
- /* 1120 */ 194, 174, 175, 66, 174, 175, 69, 57, 174, 175,
- /* 1130 */ 40, 174, 175, 174, 175, 174, 175, 174, 175, 69,
- /* 1140 */ 22, 53, 74, 75, 30, 53, 89, 90, 22, 22,
- /* 1150 */ 152, 197, 23, 96, 97, 98, 22, 152, 101, 89,
- /* 1160 */ 90, 91, 208, 209, 152, 53, 96, 97, 98, 101,
- /* 1170 */ 22, 101, 174, 175, 152, 19, 20, 105, 22, 174,
- /* 1180 */ 175, 112, 19, 27, 28, 20, 174, 175, 24, 132,
- /* 1190 */ 133, 134, 135, 136, 38, 44, 174, 175, 107, 61,
- /* 1200 */ 54, 26, 132, 133, 134, 135, 136, 54, 107, 22,
- /* 1210 */ 5, 140, 1, 57, 36, 111, 122, 28, 79, 79,
- /* 1220 */ 131, 123, 66, 19, 20, 69, 22, 1, 16, 20,
- /* 1230 */ 125, 27, 28, 123, 111, 120, 23, 131, 23, 16,
- /* 1240 */ 68, 142, 38, 15, 22, 89, 90, 3, 167, 4,
- /* 1250 */ 248, 251, 96, 97, 98, 180, 180, 101, 251, 151,
- /* 1260 */ 6, 57, 151, 13, 151, 26, 25, 151, 161, 202,
- /* 1270 */ 153, 162, 153, 69, 130, 128, 203, 19, 20, 127,
- /* 1280 */ 22, 126, 204, 129, 22, 27, 28, 205, 132, 133,
- /* 1290 */ 134, 135, 136, 89, 90, 231, 38, 95, 137, 179,
- /* 1300 */ 96, 97, 98, 206, 179, 101, 122, 107, 159, 159,
- /* 1310 */ 125, 231, 216, 228, 107, 57, 184, 217, 216, 176,
- /* 1320 */ 217, 176, 48, 106, 18, 184, 158, 69, 159, 158,
- /* 1330 */ 46, 71, 237, 176, 176, 176, 132, 133, 134, 135,
- /* 1340 */ 136, 217, 176, 137, 216, 178, 158, 89, 90, 179,
- /* 1350 */ 176, 159, 179, 159, 96, 97, 98, 159, 159, 101,
- /* 1360 */ 5, 158, 202, 22, 18, 10, 11, 12, 13, 14,
- /* 1370 */ 190, 238, 17, 190, 158, 193, 41, 159, 202, 193,
- /* 1380 */ 159, 202, 245, 193, 193, 223, 190, 32, 159, 34,
- /* 1390 */ 132, 133, 134, 135, 136, 159, 39, 155, 43, 150,
- /* 1400 */ 223, 177, 201, 178, 177, 186, 66, 199, 177, 152,
- /* 1410 */ 253, 56, 215, 152, 182, 152, 202, 152, 63, 152,
- /* 1420 */ 152, 66, 67, 242, 229, 152, 174, 152, 152, 152,
- /* 1430 */ 152, 152, 152, 152, 199, 242, 202, 152, 198, 152,
- /* 1440 */ 152, 152, 183, 192, 152, 215, 152, 183, 215, 183,
- /* 1450 */ 152, 241, 214, 152, 211, 152, 152, 211, 211, 152,
- /* 1460 */ 152, 241, 152, 152, 152, 152, 152, 152, 152, 114,
- /* 1470 */ 152, 152, 235, 152, 152, 152, 174, 187, 95, 174,
- /* 1480 */ 253, 253, 253, 253, 236, 253, 253, 253, 253, 253,
- /* 1490 */ 253, 253, 253, 253, 253, 253, 141,
+ /* 0 */ 19, 95, 53, 97, 22, 24, 24, 101, 27, 28,
+ /* 10 */ 29, 30, 31, 32, 33, 34, 35, 36, 37, 38,
+ /* 20 */ 39, 40, 41, 152, 43, 44, 45, 46, 47, 48,
+ /* 30 */ 49, 50, 51, 52, 53, 19, 55, 55, 132, 133,
+ /* 40 */ 134, 1, 2, 27, 28, 29, 30, 31, 32, 33,
+ /* 50 */ 34, 35, 36, 37, 38, 39, 40, 41, 92, 43,
+ /* 60 */ 44, 45, 46, 47, 48, 49, 50, 51, 52, 53,
+ /* 70 */ 47, 48, 49, 50, 51, 52, 53, 61, 97, 97,
+ /* 80 */ 19, 49, 50, 51, 52, 53, 70, 26, 27, 28,
+ /* 90 */ 29, 30, 31, 32, 33, 34, 35, 36, 37, 38,
+ /* 100 */ 39, 40, 41, 152, 43, 44, 45, 46, 47, 48,
+ /* 110 */ 49, 50, 51, 52, 53, 144, 145, 146, 147, 19,
+ /* 120 */ 249, 22, 172, 172, 173, 52, 53, 27, 28, 29,
+ /* 130 */ 30, 31, 32, 33, 34, 35, 36, 37, 38, 39,
+ /* 140 */ 40, 41, 81, 43, 44, 45, 46, 47, 48, 49,
+ /* 150 */ 50, 51, 52, 53, 55, 56, 19, 152, 207, 208,
+ /* 160 */ 115, 24, 117, 118, 27, 28, 29, 30, 31, 32,
+ /* 170 */ 33, 34, 35, 36, 37, 38, 39, 40, 41, 79,
+ /* 180 */ 43, 44, 45, 46, 47, 48, 49, 50, 51, 52,
+ /* 190 */ 53, 19, 0, 1, 2, 23, 97, 98, 193, 27,
+ /* 200 */ 28, 29, 30, 31, 32, 33, 34, 35, 36, 37,
+ /* 210 */ 38, 39, 40, 41, 152, 43, 44, 45, 46, 47,
+ /* 220 */ 48, 49, 50, 51, 52, 53, 19, 22, 23, 172,
+ /* 230 */ 23, 26, 119, 120, 27, 28, 29, 30, 31, 32,
+ /* 240 */ 33, 34, 35, 36, 37, 38, 39, 40, 41, 187,
+ /* 250 */ 43, 44, 45, 46, 47, 48, 49, 50, 51, 52,
+ /* 260 */ 53, 19, 221, 222, 223, 23, 168, 169, 170, 27,
+ /* 270 */ 28, 29, 30, 31, 32, 33, 34, 35, 36, 37,
+ /* 280 */ 38, 39, 40, 41, 152, 43, 44, 45, 46, 47,
+ /* 290 */ 48, 49, 50, 51, 52, 53, 19, 157, 22, 23,
+ /* 300 */ 23, 96, 26, 172, 27, 28, 29, 30, 31, 32,
+ /* 310 */ 33, 34, 35, 36, 37, 38, 39, 40, 41, 187,
+ /* 320 */ 43, 44, 45, 46, 47, 48, 49, 50, 51, 52,
+ /* 330 */ 53, 19, 108, 109, 110, 221, 222, 223, 185, 27,
+ /* 340 */ 28, 29, 30, 31, 32, 33, 34, 35, 36, 37,
+ /* 350 */ 38, 39, 40, 41, 240, 43, 44, 45, 46, 47,
+ /* 360 */ 48, 49, 50, 51, 52, 53, 19, 227, 22, 23,
+ /* 370 */ 230, 22, 96, 152, 27, 28, 29, 30, 31, 32,
+ /* 380 */ 33, 34, 35, 36, 37, 38, 39, 40, 41, 30,
+ /* 390 */ 43, 44, 45, 46, 47, 48, 49, 50, 51, 52,
+ /* 400 */ 53, 19, 190, 191, 55, 56, 24, 190, 191, 27,
+ /* 410 */ 28, 29, 30, 31, 32, 33, 34, 35, 36, 37,
+ /* 420 */ 38, 39, 40, 41, 152, 43, 44, 45, 46, 47,
+ /* 430 */ 48, 49, 50, 51, 52, 53, 168, 169, 170, 179,
+ /* 440 */ 180, 171, 96, 19, 172, 173, 97, 98, 188, 179,
+ /* 450 */ 138, 27, 28, 29, 30, 31, 32, 33, 34, 35,
+ /* 460 */ 36, 37, 38, 39, 40, 41, 107, 43, 44, 45,
+ /* 470 */ 46, 47, 48, 49, 50, 51, 52, 53, 19, 207,
+ /* 480 */ 208, 30, 31, 32, 33, 138, 27, 28, 29, 30,
+ /* 490 */ 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
+ /* 500 */ 41, 181, 43, 44, 45, 46, 47, 48, 49, 50,
+ /* 510 */ 51, 52, 53, 19, 152, 7, 8, 9, 49, 22,
+ /* 520 */ 19, 24, 28, 29, 30, 31, 32, 33, 34, 35,
+ /* 530 */ 36, 37, 38, 39, 40, 41, 152, 43, 44, 45,
+ /* 540 */ 46, 47, 48, 49, 50, 51, 52, 53, 19, 108,
+ /* 550 */ 109, 110, 101, 55, 53, 193, 172, 173, 29, 30,
+ /* 560 */ 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
+ /* 570 */ 41, 152, 43, 44, 45, 46, 47, 48, 49, 50,
+ /* 580 */ 51, 52, 53, 19, 20, 116, 22, 23, 169, 170,
+ /* 590 */ 121, 207, 85, 55, 56, 97, 19, 20, 195, 22,
+ /* 600 */ 99, 100, 101, 102, 103, 104, 105, 12, 152, 206,
+ /* 610 */ 210, 47, 48, 112, 152, 108, 109, 110, 54, 55,
+ /* 620 */ 56, 221, 222, 223, 47, 48, 119, 120, 172, 173,
+ /* 630 */ 66, 54, 55, 56, 101, 97, 98, 99, 148, 149,
+ /* 640 */ 102, 103, 104, 66, 154, 112, 156, 83, 229, 47,
+ /* 650 */ 48, 113, 57, 163, 194, 195, 92, 246, 247, 95,
+ /* 660 */ 83, 97, 98, 207, 208, 101, 206, 59, 73, 92,
+ /* 670 */ 75, 63, 95, 163, 97, 98, 194, 195, 101, 219,
+ /* 680 */ 85, 181, 19, 152, 175, 77, 196, 152, 206, 19,
+ /* 690 */ 20, 199, 22, 30, 163, 11, 132, 133, 134, 135,
+ /* 700 */ 136, 209, 152, 172, 173, 152, 196, 172, 173, 132,
+ /* 710 */ 133, 134, 135, 136, 164, 92, 213, 47, 48, 49,
+ /* 720 */ 92, 186, 169, 170, 54, 55, 56, 196, 100, 219,
+ /* 730 */ 221, 222, 223, 243, 132, 133, 66, 175, 207, 208,
+ /* 740 */ 152, 231, 119, 120, 19, 20, 236, 22, 152, 38,
+ /* 750 */ 39, 40, 41, 83, 43, 44, 45, 46, 47, 48,
+ /* 760 */ 49, 50, 51, 52, 53, 95, 152, 97, 98, 85,
+ /* 770 */ 107, 101, 47, 48, 163, 112, 92, 152, 152, 54,
+ /* 780 */ 55, 56, 229, 221, 222, 223, 172, 173, 163, 49,
+ /* 790 */ 152, 66, 108, 109, 110, 55, 56, 172, 173, 163,
+ /* 800 */ 186, 22, 132, 133, 134, 135, 136, 196, 83, 43,
+ /* 810 */ 44, 45, 46, 47, 48, 49, 50, 51, 52, 53,
+ /* 820 */ 95, 196, 97, 98, 85, 99, 101, 152, 102, 103,
+ /* 830 */ 104, 92, 196, 146, 147, 152, 181, 97, 98, 113,
+ /* 840 */ 19, 20, 152, 22, 218, 22, 116, 108, 109, 110,
+ /* 850 */ 152, 121, 55, 56, 12, 219, 218, 132, 133, 134,
+ /* 860 */ 135, 136, 172, 173, 55, 56, 55, 56, 47, 48,
+ /* 870 */ 172, 173, 236, 152, 5, 54, 55, 56, 55, 10,
+ /* 880 */ 11, 12, 13, 14, 186, 152, 17, 66, 47, 48,
+ /* 890 */ 152, 210, 16, 84, 97, 98, 55, 56, 21, 57,
+ /* 900 */ 217, 122, 22, 152, 83, 152, 97, 98, 97, 98,
+ /* 910 */ 172, 173, 152, 152, 224, 73, 95, 75, 97, 98,
+ /* 920 */ 97, 124, 101, 172, 173, 164, 193, 47, 48, 60,
+ /* 930 */ 163, 62, 172, 173, 24, 55, 56, 186, 97, 98,
+ /* 940 */ 71, 100, 55, 56, 183, 207, 185, 152, 107, 23,
+ /* 950 */ 109, 82, 26, 132, 133, 134, 135, 136, 89, 152,
+ /* 960 */ 26, 92, 93, 196, 88, 55, 90, 152, 91, 152,
+ /* 970 */ 217, 152, 152, 132, 133, 95, 152, 97, 211, 212,
+ /* 980 */ 103, 101, 152, 114, 97, 98, 152, 172, 173, 19,
+ /* 990 */ 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
+ /* 1000 */ 48, 49, 50, 51, 52, 53, 65, 97, 152, 152,
+ /* 1010 */ 141, 124, 132, 133, 134, 152, 140, 140, 152, 78,
+ /* 1020 */ 152, 233, 152, 193, 98, 152, 56, 86, 172, 173,
+ /* 1030 */ 166, 167, 237, 152, 217, 172, 173, 152, 172, 173,
+ /* 1040 */ 172, 173, 172, 173, 237, 172, 173, 152, 152, 152,
+ /* 1050 */ 124, 152, 111, 172, 173, 152, 237, 172, 173, 152,
+ /* 1060 */ 55, 152, 26, 152, 130, 152, 152, 172, 173, 172,
+ /* 1070 */ 173, 172, 173, 249, 152, 172, 173, 152, 61, 172,
+ /* 1080 */ 173, 172, 173, 172, 173, 172, 173, 70, 152, 193,
+ /* 1090 */ 152, 137, 152, 139, 172, 173, 152, 172, 173, 152,
+ /* 1100 */ 152, 152, 97, 152, 26, 152, 163, 152, 172, 173,
+ /* 1110 */ 172, 173, 172, 173, 152, 16, 172, 173, 26, 172,
+ /* 1120 */ 173, 172, 173, 172, 173, 172, 173, 172, 173, 163,
+ /* 1130 */ 152, 152, 152, 22, 172, 173, 152, 241, 152, 196,
+ /* 1140 */ 152, 193, 106, 152, 152, 152, 163, 152, 37, 152,
+ /* 1150 */ 172, 173, 172, 173, 152, 152, 172, 173, 172, 173,
+ /* 1160 */ 172, 173, 196, 172, 173, 172, 173, 172, 173, 172,
+ /* 1170 */ 173, 152, 152, 152, 172, 173, 98, 152, 152, 196,
+ /* 1180 */ 160, 22, 23, 19, 164, 193, 152, 88, 232, 90,
+ /* 1190 */ 191, 172, 173, 172, 173, 163, 193, 172, 173, 166,
+ /* 1200 */ 167, 163, 124, 163, 244, 245, 23, 211, 212, 26,
+ /* 1210 */ 55, 23, 55, 23, 26, 123, 26, 152, 23, 193,
+ /* 1220 */ 56, 26, 23, 23, 23, 26, 26, 26, 196, 100,
+ /* 1230 */ 101, 7, 8, 152, 196, 23, 196, 23, 26, 152,
+ /* 1240 */ 26, 132, 133, 23, 132, 133, 26, 152, 152, 152,
+ /* 1250 */ 152, 210, 97, 152, 97, 96, 152, 152, 152, 152,
+ /* 1260 */ 152, 210, 152, 210, 197, 152, 152, 152, 152, 152,
+ /* 1270 */ 152, 152, 152, 152, 152, 152, 152, 198, 150, 176,
+ /* 1280 */ 214, 201, 214, 238, 201, 238, 184, 214, 226, 200,
+ /* 1290 */ 155, 198, 67, 122, 242, 159, 159, 69, 239, 239,
+ /* 1300 */ 159, 175, 175, 22, 220, 180, 175, 27, 130, 18,
+ /* 1310 */ 159, 18, 158, 137, 220, 159, 158, 235, 74, 189,
+ /* 1320 */ 234, 159, 159, 158, 22, 192, 192, 177, 159, 159,
+ /* 1330 */ 192, 192, 189, 201, 189, 158, 107, 158, 177, 76,
+ /* 1340 */ 174, 201, 174, 201, 106, 159, 174, 125, 174, 182,
+ /* 1350 */ 177, 176, 107, 159, 174, 137, 174, 53, 174, 129,
+ /* 1360 */ 216, 215, 215, 182, 216, 216, 177, 215, 126, 216,
+ /* 1370 */ 177, 128, 25, 127, 26, 215, 13, 162, 161, 153,
+ /* 1380 */ 153, 6, 151, 151, 151, 151, 165, 178, 165, 178,
+ /* 1390 */ 4, 3, 22, 142, 15, 94, 16, 205, 120, 202,
+ /* 1400 */ 204, 203, 165, 201, 23, 23, 131, 111, 123, 20,
+ /* 1410 */ 225, 125, 16, 1, 131, 228, 111, 123, 56, 228,
+ /* 1420 */ 37, 37, 64, 1, 5, 122, 22, 107, 140, 80,
+ /* 1430 */ 80, 26, 87, 72, 107, 24, 20, 19, 112, 105,
+ /* 1440 */ 23, 68, 22, 79, 79, 22, 22, 22, 58, 22,
+ /* 1450 */ 245, 248, 248, 79, 23, 23, 23, 116, 22, 122,
+ /* 1460 */ 23, 22, 56, 23, 26, 23, 64, 22, 124, 26,
+ /* 1470 */ 26, 64, 64, 23, 23, 23, 11, 23, 22, 26,
+ /* 1480 */ 23, 22, 24, 1, 23, 22, 26, 250, 24, 23,
+ /* 1490 */ 22, 122, 23, 23, 22, 15, 23, 250, 122, 122,
+ /* 1500 */ 122,
};
-#define YY_SHIFT_USE_DFLT (-86)
-#define YY_SHIFT_COUNT (429)
-#define YY_SHIFT_MIN (-85)
-#define YY_SHIFT_MAX (1383)
+#define YY_SHIFT_USE_DFLT (-95)
+#define YY_SHIFT_COUNT (439)
+#define YY_SHIFT_MIN (-94)
+#define YY_SHIFT_MAX (1482)
static const short yy_shift_ofst[] = {
- /* 0 */ 992, 1057, 1355, 1156, 1204, 1204, 1, 262, -19, 135,
- /* 10 */ 135, 776, 1204, 1204, 1204, 1204, 69, 69, 53, 208,
- /* 20 */ 283, 755, 58, 725, 648, 571, 494, 417, 340, 263,
- /* 30 */ 212, 827, 827, 827, 827, 827, 827, 827, 827, 827,
- /* 40 */ 827, 827, 827, 827, 827, 827, 878, 827, 929, 980,
- /* 50 */ 980, 1070, 1204, 1204, 1204, 1204, 1204, 1204, 1204, 1204,
- /* 60 */ 1204, 1204, 1204, 1204, 1204, 1204, 1204, 1204, 1204, 1204,
- /* 70 */ 1204, 1204, 1204, 1204, 1204, 1204, 1204, 1204, 1204, 1204,
- /* 80 */ 1258, 1204, 1204, 1204, 1204, 1204, 1204, 1204, 1204, 1204,
- /* 90 */ 1204, 1204, 1204, 1204, -71, -47, -47, -47, -47, -47,
- /* 100 */ 0, 29, -12, 283, 283, 139, 91, 392, 392, 894,
- /* 110 */ 672, 726, 1383, -86, -86, -86, 88, 318, 318, 99,
- /* 120 */ 381, -20, 283, 283, 283, 283, 283, 283, 283, 283,
- /* 130 */ 283, 283, 283, 283, 283, 283, 283, 283, 283, 283,
- /* 140 */ 283, 283, 283, 283, 624, 876, 726, 672, 1340, 1340,
- /* 150 */ 1340, 1340, 1340, 1340, -86, -86, -86, 305, 136, 136,
- /* 160 */ 142, 167, 226, 154, 137, 152, 283, 283, 283, 283,
- /* 170 */ 283, 283, 283, 283, 283, 283, 283, 283, 283, 283,
- /* 180 */ 283, 283, 283, 336, 336, 336, 283, 283, 352, 283,
- /* 190 */ 283, 283, 283, 283, 228, 283, 283, 283, 283, 283,
- /* 200 */ 283, 283, 283, 283, 283, 501, 569, 596, 596, 596,
- /* 210 */ 507, 497, 441, 391, 353, 156, 156, 857, 353, 857,
- /* 220 */ 735, 813, 639, 715, 156, 332, 715, 715, 496, 419,
- /* 230 */ 646, 1357, 1184, 1184, 1335, 1335, 1184, 1341, 1260, 1144,
- /* 240 */ 1346, 1346, 1346, 1346, 1184, 1306, 1144, 1341, 1260, 1260,
- /* 250 */ 1144, 1184, 1306, 1206, 1284, 1184, 1184, 1306, 1184, 1306,
- /* 260 */ 1184, 1306, 1262, 1207, 1207, 1207, 1274, 1262, 1207, 1217,
- /* 270 */ 1207, 1274, 1207, 1207, 1185, 1200, 1185, 1200, 1185, 1200,
- /* 280 */ 1184, 1184, 1161, 1262, 1202, 1202, 1262, 1154, 1155, 1147,
- /* 290 */ 1152, 1144, 1241, 1239, 1250, 1250, 1254, 1254, 1254, 1254,
- /* 300 */ -86, -86, -86, -86, -86, -86, 1068, 304, 526, 249,
- /* 310 */ 408, -83, 434, 812, 27, 811, 807, 802, 751, 589,
- /* 320 */ 651, 163, 131, 674, 366, 450, 299, 148, 23, 102,
- /* 330 */ 229, -21, 1245, 1244, 1222, 1099, 1228, 1172, 1223, 1215,
- /* 340 */ 1213, 1115, 1106, 1123, 1110, 1209, 1105, 1212, 1226, 1098,
- /* 350 */ 1089, 1140, 1139, 1104, 1189, 1178, 1094, 1211, 1205, 1187,
- /* 360 */ 1101, 1071, 1153, 1175, 1146, 1138, 1151, 1091, 1164, 1165,
- /* 370 */ 1163, 1069, 1072, 1148, 1112, 1134, 1127, 1129, 1126, 1092,
- /* 380 */ 1114, 1118, 1088, 1090, 1093, 1087, 1084, 987, 1079, 1077,
- /* 390 */ 1074, 1065, 924, 1021, 1014, 1004, 1006, 819, 739, 896,
- /* 400 */ 855, 804, 739, 740, 736, 690, 654, 665, 618, 582,
- /* 410 */ 568, 528, 554, 379, 532, 479, 455, 379, 432, 371,
- /* 420 */ 341, 28, 338, 116, -11, -57, -85, 7, -8, 3,
+ /* 0 */ 40, 564, 869, 577, 725, 725, 725, 739, -19, 16,
+ /* 10 */ 16, 100, 725, 725, 725, 725, 725, 725, 725, 841,
+ /* 20 */ 841, 538, 507, 684, 623, 61, 137, 172, 207, 242,
+ /* 30 */ 277, 312, 347, 382, 424, 424, 424, 424, 424, 424,
+ /* 40 */ 424, 424, 424, 424, 424, 424, 424, 424, 424, 459,
+ /* 50 */ 424, 494, 529, 529, 670, 725, 725, 725, 725, 725,
+ /* 60 */ 725, 725, 725, 725, 725, 725, 725, 725, 725, 725,
+ /* 70 */ 725, 725, 725, 725, 725, 725, 725, 725, 725, 725,
+ /* 80 */ 725, 725, 725, 821, 725, 725, 725, 725, 725, 725,
+ /* 90 */ 725, 725, 725, 725, 725, 725, 725, 952, 711, 711,
+ /* 100 */ 711, 711, 711, 766, 23, 32, 811, 877, 663, 602,
+ /* 110 */ 602, 811, 73, 113, -51, -95, -95, -95, 501, 501,
+ /* 120 */ 501, 595, 595, 809, 205, 276, 811, 811, 811, 811,
+ /* 130 */ 811, 811, 811, 811, 811, 811, 811, 811, 811, 811,
+ /* 140 */ 811, 811, 811, 811, 811, 811, 192, 628, 498, 498,
+ /* 150 */ 113, -34, -34, -34, -34, -34, -34, -95, -95, -95,
+ /* 160 */ 880, -94, -94, 726, 740, 99, 797, 887, 349, 811,
+ /* 170 */ 811, 811, 811, 811, 811, 811, 811, 811, 811, 811,
+ /* 180 */ 811, 811, 811, 811, 811, 811, 941, 941, 941, 811,
+ /* 190 */ 811, 926, 811, 811, 811, -18, 811, 811, 842, 811,
+ /* 200 */ 811, 811, 811, 811, 811, 811, 811, 811, 811, 224,
+ /* 210 */ 608, 910, 910, 910, 1078, 45, 469, 508, 934, 970,
+ /* 220 */ 970, 1164, 934, 1164, 1036, 1183, 359, 1017, 970, 954,
+ /* 230 */ 1017, 1017, 1092, 730, 497, 1225, 1171, 1171, 1228, 1228,
+ /* 240 */ 1171, 1281, 1280, 1178, 1291, 1291, 1291, 1291, 1171, 1293,
+ /* 250 */ 1178, 1281, 1280, 1280, 1178, 1171, 1293, 1176, 1244, 1171,
+ /* 260 */ 1171, 1293, 1302, 1171, 1293, 1171, 1293, 1302, 1229, 1229,
+ /* 270 */ 1229, 1263, 1302, 1229, 1238, 1229, 1263, 1229, 1229, 1222,
+ /* 280 */ 1245, 1222, 1245, 1222, 1245, 1222, 1245, 1171, 1171, 1218,
+ /* 290 */ 1302, 1304, 1304, 1302, 1230, 1242, 1243, 1246, 1178, 1347,
+ /* 300 */ 1348, 1363, 1363, 1375, 1375, 1375, 1375, -95, -95, -95,
+ /* 310 */ -95, -95, -95, -95, -95, 451, 876, 346, 1159, 1099,
+ /* 320 */ 441, 823, 1188, 1111, 1190, 1195, 1199, 1200, 1005, 1129,
+ /* 330 */ 1224, 533, 1201, 1212, 1155, 1214, 1109, 1112, 1220, 1157,
+ /* 340 */ 779, 1386, 1388, 1370, 1251, 1379, 1301, 1380, 1381, 1382,
+ /* 350 */ 1278, 1275, 1296, 1285, 1389, 1286, 1396, 1412, 1294, 1283,
+ /* 360 */ 1383, 1384, 1305, 1362, 1358, 1303, 1422, 1419, 1404, 1320,
+ /* 370 */ 1288, 1349, 1405, 1350, 1345, 1361, 1327, 1411, 1416, 1418,
+ /* 380 */ 1326, 1334, 1420, 1364, 1423, 1424, 1417, 1425, 1365, 1390,
+ /* 390 */ 1427, 1374, 1373, 1431, 1432, 1433, 1341, 1436, 1437, 1439,
+ /* 400 */ 1438, 1337, 1440, 1442, 1406, 1402, 1445, 1344, 1443, 1407,
+ /* 410 */ 1444, 1408, 1443, 1450, 1451, 1452, 1453, 1454, 1456, 1465,
+ /* 420 */ 1457, 1459, 1458, 1460, 1461, 1463, 1464, 1460, 1466, 1468,
+ /* 430 */ 1469, 1470, 1472, 1369, 1376, 1377, 1378, 1473, 1480, 1482,
};
-#define YY_REDUCE_USE_DFLT (-110)
-#define YY_REDUCE_COUNT (305)
-#define YY_REDUCE_MIN (-109)
-#define YY_REDUCE_MAX (1323)
+#define YY_REDUCE_USE_DFLT (-130)
+#define YY_REDUCE_COUNT (314)
+#define YY_REDUCE_MIN (-129)
+#define YY_REDUCE_MAX (1237)
static const short yy_reduce_ofst[] = {
- /* 0 */ 238, 954, 213, 289, 310, 234, 144, 317, -109, 382,
- /* 10 */ 377, 303, 461, 389, 378, 368, 302, 294, 253, 395,
- /* 20 */ 293, 324, 403, 403, 403, 403, 403, 403, 403, 403,
- /* 30 */ 403, 403, 403, 403, 403, 403, 403, 403, 403, 403,
- /* 40 */ 403, 403, 403, 403, 403, 403, 403, 403, 403, 403,
- /* 50 */ 403, 1022, 1012, 1005, 998, 963, 961, 959, 957, 950,
- /* 60 */ 947, 930, 912, 873, 861, 823, 810, 771, 759, 720,
- /* 70 */ 708, 670, 657, 619, 614, 612, 610, 608, 606, 604,
- /* 80 */ 598, 595, 593, 580, 542, 540, 537, 535, 533, 531,
- /* 90 */ 529, 527, 503, 386, 403, 403, 403, 403, 403, 403,
- /* 100 */ 403, 403, 403, 95, 447, 82, 334, 504, 467, 403,
- /* 110 */ 477, 464, 403, 403, 403, 403, 860, 747, 744, 785,
- /* 120 */ 638, 638, 926, 891, 900, 899, 887, 844, 840, 835,
- /* 130 */ 848, 830, 843, 829, 792, 839, 826, 737, 838, 795,
- /* 140 */ 789, 47, 734, 530, 696, 777, 711, 677, 733, 730,
- /* 150 */ 729, 728, 727, 627, 448, 64, 187, 1305, 1302, 1252,
- /* 160 */ 1290, 1273, 1323, 1322, 1321, 1319, 1318, 1316, 1315, 1314,
- /* 170 */ 1313, 1312, 1311, 1310, 1308, 1307, 1304, 1303, 1301, 1298,
- /* 180 */ 1294, 1292, 1289, 1266, 1264, 1259, 1288, 1287, 1238, 1285,
- /* 190 */ 1281, 1280, 1279, 1278, 1251, 1277, 1276, 1275, 1273, 1268,
- /* 200 */ 1267, 1265, 1263, 1261, 1257, 1248, 1237, 1247, 1246, 1243,
- /* 210 */ 1238, 1240, 1235, 1249, 1234, 1233, 1230, 1220, 1214, 1210,
- /* 220 */ 1225, 1219, 1232, 1231, 1197, 1195, 1227, 1224, 1201, 1208,
- /* 230 */ 1242, 1137, 1236, 1229, 1193, 1181, 1221, 1177, 1196, 1179,
- /* 240 */ 1191, 1190, 1186, 1182, 1218, 1216, 1176, 1162, 1183, 1180,
- /* 250 */ 1160, 1199, 1203, 1133, 1095, 1198, 1194, 1188, 1192, 1171,
- /* 260 */ 1169, 1168, 1173, 1174, 1166, 1159, 1141, 1170, 1158, 1167,
- /* 270 */ 1157, 1132, 1145, 1143, 1124, 1128, 1103, 1102, 1100, 1096,
- /* 280 */ 1150, 1149, 1085, 1125, 1080, 1064, 1120, 1097, 1082, 1078,
- /* 290 */ 1073, 1067, 1109, 1107, 1119, 1117, 1116, 1113, 1111, 1108,
- /* 300 */ 1007, 1000, 1002, 1076, 1075, 1081,
+ /* 0 */ -29, 531, 490, 625, -49, 272, 456, 510, 400, 509,
+ /* 10 */ 562, 114, 535, 614, 698, 384, 738, 751, 690, 419,
+ /* 20 */ 553, 761, 460, 636, 767, 41, 41, 41, 41, 41,
+ /* 30 */ 41, 41, 41, 41, 41, 41, 41, 41, 41, 41,
+ /* 40 */ 41, 41, 41, 41, 41, 41, 41, 41, 41, 41,
+ /* 50 */ 41, 41, 41, 41, 760, 815, 856, 863, 866, 868,
+ /* 60 */ 870, 873, 881, 885, 895, 897, 899, 903, 907, 909,
+ /* 70 */ 911, 913, 922, 925, 936, 938, 940, 944, 947, 949,
+ /* 80 */ 951, 953, 955, 962, 978, 980, 984, 986, 988, 991,
+ /* 90 */ 993, 995, 997, 1002, 1019, 1021, 1025, 41, 41, 41,
+ /* 100 */ 41, 41, 41, 41, 41, 41, 896, 140, 260, 98,
+ /* 110 */ 268, 1020, 41, 482, 41, 41, 41, 41, 270, 270,
+ /* 120 */ 270, 212, 217, -129, 411, 411, 550, 5, 626, 362,
+ /* 130 */ 733, 830, 992, 1003, 1026, 795, 683, 807, 638, 819,
+ /* 140 */ 753, 948, 62, 817, 824, 132, 687, 611, 864, 1033,
+ /* 150 */ 403, 943, 966, 983, 1032, 1038, 1040, 960, 996, 492,
+ /* 160 */ -50, 57, 131, 153, 221, 462, 588, 596, 675, 721,
+ /* 170 */ 820, 834, 857, 914, 979, 1034, 1065, 1081, 1087, 1095,
+ /* 180 */ 1096, 1097, 1098, 1101, 1104, 1105, 320, 500, 655, 1106,
+ /* 190 */ 1107, 503, 1108, 1110, 1113, 681, 1114, 1115, 999, 1116,
+ /* 200 */ 1117, 1118, 221, 1119, 1120, 1121, 1122, 1123, 1124, 788,
+ /* 210 */ 956, 1041, 1051, 1053, 503, 1067, 1079, 1128, 1080, 1066,
+ /* 220 */ 1068, 1045, 1083, 1047, 1103, 1102, 1125, 1126, 1073, 1062,
+ /* 230 */ 1127, 1131, 1089, 1093, 1135, 1052, 1136, 1137, 1059, 1060,
+ /* 240 */ 1141, 1084, 1130, 1132, 1133, 1134, 1138, 1139, 1151, 1154,
+ /* 250 */ 1140, 1094, 1143, 1145, 1142, 1156, 1158, 1082, 1086, 1162,
+ /* 260 */ 1163, 1165, 1150, 1169, 1177, 1170, 1179, 1161, 1166, 1168,
+ /* 270 */ 1172, 1167, 1173, 1174, 1175, 1180, 1181, 1182, 1184, 1144,
+ /* 280 */ 1146, 1148, 1147, 1149, 1152, 1153, 1160, 1186, 1194, 1185,
+ /* 290 */ 1189, 1187, 1191, 1193, 1192, 1196, 1198, 1197, 1202, 1215,
+ /* 300 */ 1217, 1226, 1227, 1231, 1232, 1233, 1234, 1203, 1204, 1205,
+ /* 310 */ 1221, 1223, 1209, 1211, 1237,
};
static const YYACTIONTYPE yy_default[] = {
- /* 0 */ 647, 964, 964, 964, 878, 878, 969, 964, 774, 802,
- /* 10 */ 802, 938, 969, 969, 969, 876, 969, 969, 969, 964,
- /* 20 */ 969, 778, 808, 969, 969, 969, 969, 969, 969, 969,
- /* 30 */ 969, 937, 939, 816, 815, 918, 789, 813, 806, 810,
- /* 40 */ 879, 872, 873, 871, 875, 880, 969, 809, 841, 856,
- /* 50 */ 840, 969, 969, 969, 969, 969, 969, 969, 969, 969,
- /* 60 */ 969, 969, 969, 969, 969, 969, 969, 969, 969, 969,
- /* 70 */ 969, 969, 969, 969, 969, 969, 969, 969, 969, 969,
- /* 80 */ 969, 969, 969, 969, 969, 969, 969, 969, 969, 969,
- /* 90 */ 969, 969, 969, 969, 850, 855, 862, 854, 851, 843,
- /* 100 */ 842, 844, 845, 969, 969, 673, 739, 969, 969, 846,
- /* 110 */ 969, 685, 847, 859, 858, 857, 680, 969, 969, 969,
- /* 120 */ 969, 969, 969, 969, 969, 969, 969, 969, 969, 969,
- /* 130 */ 969, 969, 969, 969, 969, 969, 969, 969, 969, 969,
- /* 140 */ 969, 969, 969, 969, 647, 964, 969, 969, 964, 964,
- /* 150 */ 964, 964, 964, 964, 956, 778, 768, 969, 969, 969,
- /* 160 */ 969, 969, 969, 969, 969, 969, 969, 944, 942, 969,
- /* 170 */ 891, 969, 969, 969, 969, 969, 969, 969, 969, 969,
- /* 180 */ 969, 969, 969, 969, 969, 969, 969, 969, 969, 969,
- /* 190 */ 969, 969, 969, 969, 969, 969, 969, 969, 969, 969,
- /* 200 */ 969, 969, 969, 969, 653, 969, 911, 774, 774, 774,
- /* 210 */ 776, 754, 766, 655, 812, 791, 791, 923, 812, 923,
- /* 220 */ 710, 733, 707, 802, 791, 874, 802, 802, 775, 766,
- /* 230 */ 969, 949, 782, 782, 941, 941, 782, 821, 743, 812,
- /* 240 */ 750, 750, 750, 750, 782, 670, 812, 821, 743, 743,
- /* 250 */ 812, 782, 670, 917, 915, 782, 782, 670, 782, 670,
- /* 260 */ 782, 670, 884, 741, 741, 741, 725, 884, 741, 710,
- /* 270 */ 741, 725, 741, 741, 795, 790, 795, 790, 795, 790,
- /* 280 */ 782, 782, 969, 884, 888, 888, 884, 807, 796, 805,
- /* 290 */ 803, 812, 676, 728, 663, 663, 652, 652, 652, 652,
- /* 300 */ 961, 961, 956, 712, 712, 695, 969, 969, 969, 969,
- /* 310 */ 969, 969, 687, 969, 893, 969, 969, 969, 969, 969,
- /* 320 */ 969, 969, 969, 969, 969, 969, 969, 969, 969, 969,
- /* 330 */ 969, 828, 969, 648, 951, 969, 969, 948, 969, 969,
- /* 340 */ 969, 969, 969, 969, 969, 969, 969, 969, 969, 969,
- /* 350 */ 969, 969, 969, 969, 969, 969, 921, 969, 969, 969,
- /* 360 */ 969, 969, 969, 914, 913, 969, 969, 969, 969, 969,
- /* 370 */ 969, 969, 969, 969, 969, 969, 969, 969, 969, 969,
- /* 380 */ 969, 969, 969, 969, 969, 969, 969, 757, 969, 969,
- /* 390 */ 969, 761, 969, 969, 969, 969, 969, 969, 804, 969,
- /* 400 */ 797, 969, 877, 969, 969, 969, 969, 969, 969, 969,
- /* 410 */ 969, 969, 969, 966, 969, 969, 969, 965, 969, 969,
- /* 420 */ 969, 969, 969, 830, 969, 829, 833, 969, 661, 969,
- /* 430 */ 644, 649, 960, 963, 962, 959, 958, 957, 952, 950,
- /* 440 */ 947, 946, 945, 943, 940, 936, 897, 895, 902, 901,
- /* 450 */ 900, 899, 898, 896, 894, 892, 818, 817, 814, 811,
- /* 460 */ 753, 935, 890, 752, 749, 748, 669, 953, 920, 929,
- /* 470 */ 928, 927, 822, 926, 925, 924, 922, 919, 906, 820,
- /* 480 */ 819, 744, 882, 881, 672, 910, 909, 908, 912, 916,
- /* 490 */ 907, 784, 751, 671, 668, 675, 679, 731, 732, 740,
- /* 500 */ 738, 737, 736, 735, 734, 730, 681, 686, 724, 709,
- /* 510 */ 708, 717, 716, 722, 721, 720, 719, 718, 715, 714,
- /* 520 */ 713, 706, 705, 711, 704, 727, 726, 723, 703, 747,
- /* 530 */ 746, 745, 742, 702, 701, 700, 833, 699, 698, 838,
- /* 540 */ 837, 866, 826, 755, 759, 758, 762, 763, 771, 770,
- /* 550 */ 769, 780, 781, 793, 792, 824, 823, 794, 779, 773,
- /* 560 */ 772, 788, 787, 786, 785, 777, 767, 799, 798, 868,
- /* 570 */ 783, 867, 865, 934, 933, 932, 931, 930, 870, 967,
- /* 580 */ 968, 887, 889, 886, 801, 800, 885, 869, 839, 836,
- /* 590 */ 690, 691, 905, 904, 903, 693, 692, 689, 688, 863,
- /* 600 */ 860, 852, 864, 861, 853, 849, 848, 834, 832, 831,
- /* 610 */ 827, 835, 760, 756, 825, 765, 764, 697, 696, 694,
- /* 620 */ 678, 677, 674, 667, 665, 664, 666, 662, 660, 659,
- /* 630 */ 658, 657, 656, 684, 683, 682, 654, 651, 650, 646,
- /* 640 */ 645, 643,
+ /* 0 */ 1250, 1240, 1240, 1240, 1174, 1174, 1174, 1240, 1071, 1100,
+ /* 10 */ 1100, 1224, 1301, 1301, 1301, 1301, 1301, 1301, 1173, 1301,
+ /* 20 */ 1301, 1301, 1301, 1240, 1075, 1106, 1301, 1301, 1301, 1301,
+ /* 30 */ 1301, 1301, 1301, 1301, 1223, 1225, 1114, 1113, 1206, 1087,
+ /* 40 */ 1111, 1104, 1108, 1175, 1169, 1170, 1168, 1172, 1176, 1301,
+ /* 50 */ 1107, 1138, 1153, 1137, 1301, 1301, 1301, 1301, 1301, 1301,
+ /* 60 */ 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301,
+ /* 70 */ 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301,
+ /* 80 */ 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301,
+ /* 90 */ 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1147, 1152, 1159,
+ /* 100 */ 1151, 1148, 1140, 1139, 1141, 1142, 1301, 994, 1042, 1301,
+ /* 110 */ 1301, 1301, 1143, 1301, 1144, 1156, 1155, 1154, 1231, 1258,
+ /* 120 */ 1257, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301,
+ /* 130 */ 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301,
+ /* 140 */ 1301, 1301, 1301, 1301, 1301, 1301, 1250, 1240, 1000, 1000,
+ /* 150 */ 1301, 1240, 1240, 1240, 1240, 1240, 1240, 1236, 1075, 1066,
+ /* 160 */ 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301,
+ /* 170 */ 1228, 1226, 1301, 1187, 1301, 1301, 1301, 1301, 1301, 1301,
+ /* 180 */ 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301,
+ /* 190 */ 1301, 1301, 1301, 1301, 1301, 1071, 1301, 1301, 1301, 1301,
+ /* 200 */ 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1252, 1301,
+ /* 210 */ 1201, 1071, 1071, 1071, 1073, 1055, 1065, 979, 1110, 1089,
+ /* 220 */ 1089, 1290, 1110, 1290, 1017, 1272, 1014, 1100, 1089, 1171,
+ /* 230 */ 1100, 1100, 1072, 1065, 1301, 1293, 1080, 1080, 1292, 1292,
+ /* 240 */ 1080, 1119, 1045, 1110, 1051, 1051, 1051, 1051, 1080, 991,
+ /* 250 */ 1110, 1119, 1045, 1045, 1110, 1080, 991, 1205, 1287, 1080,
+ /* 260 */ 1080, 991, 1180, 1080, 991, 1080, 991, 1180, 1043, 1043,
+ /* 270 */ 1043, 1032, 1180, 1043, 1017, 1043, 1032, 1043, 1043, 1093,
+ /* 280 */ 1088, 1093, 1088, 1093, 1088, 1093, 1088, 1080, 1080, 1301,
+ /* 290 */ 1180, 1184, 1184, 1180, 1105, 1094, 1103, 1101, 1110, 997,
+ /* 300 */ 1035, 1255, 1255, 1251, 1251, 1251, 1251, 1298, 1298, 1236,
+ /* 310 */ 1267, 1267, 1019, 1019, 1267, 1301, 1301, 1301, 1301, 1301,
+ /* 320 */ 1301, 1262, 1301, 1189, 1301, 1301, 1301, 1301, 1301, 1301,
+ /* 330 */ 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301,
+ /* 340 */ 1125, 1301, 975, 1233, 1301, 1301, 1232, 1301, 1301, 1301,
+ /* 350 */ 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301,
+ /* 360 */ 1301, 1301, 1301, 1301, 1301, 1289, 1301, 1301, 1301, 1301,
+ /* 370 */ 1301, 1301, 1204, 1203, 1301, 1301, 1301, 1301, 1301, 1301,
+ /* 380 */ 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301,
+ /* 390 */ 1301, 1301, 1301, 1301, 1301, 1301, 1057, 1301, 1301, 1301,
+ /* 400 */ 1276, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1102, 1301,
+ /* 410 */ 1095, 1301, 1280, 1301, 1301, 1301, 1301, 1301, 1301, 1301,
+ /* 420 */ 1301, 1301, 1301, 1242, 1301, 1301, 1301, 1241, 1301, 1301,
+ /* 430 */ 1301, 1301, 1301, 1127, 1301, 1126, 1130, 1301, 985, 1301,
};
+/********** End of lemon-generated parsing tables *****************************/
-/* The next table maps tokens into fallback tokens. If a construct
-** like the following:
+/* The next table maps tokens (terminal symbols) into fallback tokens.
+** If a construct like the following:
**
** %fallback ID X Y Z.
**
@@ -124003,79 +131540,109 @@ static const YYACTIONTYPE yy_default[] = {
** and Z. Whenever one of the tokens X, Y, or Z is input to the parser
** but it does not parse, the type of the token is changed to ID and
** the parse is retried before an error is thrown.
+**
+** This feature can be used, for example, to cause some keywords in a language
+** to revert to identifiers if they keyword does not apply in the context where
+** it appears.
*/
#ifdef YYFALLBACK
static const YYCODETYPE yyFallback[] = {
0, /* $ => nothing */
0, /* SEMI => nothing */
- 27, /* EXPLAIN => ID */
- 27, /* QUERY => ID */
- 27, /* PLAN => ID */
- 27, /* BEGIN => ID */
+ 55, /* EXPLAIN => ID */
+ 55, /* QUERY => ID */
+ 55, /* PLAN => ID */
+ 55, /* BEGIN => ID */
0, /* TRANSACTION => nothing */
- 27, /* DEFERRED => ID */
- 27, /* IMMEDIATE => ID */
- 27, /* EXCLUSIVE => ID */
+ 55, /* DEFERRED => ID */
+ 55, /* IMMEDIATE => ID */
+ 55, /* EXCLUSIVE => ID */
0, /* COMMIT => nothing */
- 27, /* END => ID */
- 27, /* ROLLBACK => ID */
- 27, /* SAVEPOINT => ID */
- 27, /* RELEASE => ID */
+ 55, /* END => ID */
+ 55, /* ROLLBACK => ID */
+ 55, /* SAVEPOINT => ID */
+ 55, /* RELEASE => ID */
0, /* TO => nothing */
0, /* TABLE => nothing */
0, /* CREATE => nothing */
- 27, /* IF => ID */
+ 55, /* IF => ID */
0, /* NOT => nothing */
0, /* EXISTS => nothing */
- 27, /* TEMP => ID */
+ 55, /* TEMP => ID */
0, /* LP => nothing */
0, /* RP => nothing */
0, /* AS => nothing */
- 27, /* WITHOUT => ID */
+ 55, /* WITHOUT => ID */
0, /* COMMA => nothing */
+ 0, /* OR => nothing */
+ 0, /* AND => nothing */
+ 0, /* IS => nothing */
+ 55, /* MATCH => ID */
+ 55, /* LIKE_KW => ID */
+ 0, /* BETWEEN => nothing */
+ 0, /* IN => nothing */
+ 0, /* ISNULL => nothing */
+ 0, /* NOTNULL => nothing */
+ 0, /* NE => nothing */
+ 0, /* EQ => nothing */
+ 0, /* GT => nothing */
+ 0, /* LE => nothing */
+ 0, /* LT => nothing */
+ 0, /* GE => nothing */
+ 0, /* ESCAPE => nothing */
+ 0, /* BITAND => nothing */
+ 0, /* BITOR => nothing */
+ 0, /* LSHIFT => nothing */
+ 0, /* RSHIFT => nothing */
+ 0, /* PLUS => nothing */
+ 0, /* MINUS => nothing */
+ 0, /* STAR => nothing */
+ 0, /* SLASH => nothing */
+ 0, /* REM => nothing */
+ 0, /* CONCAT => nothing */
+ 0, /* COLLATE => nothing */
+ 0, /* BITNOT => nothing */
0, /* ID => nothing */
0, /* INDEXED => nothing */
- 27, /* ABORT => ID */
- 27, /* ACTION => ID */
- 27, /* AFTER => ID */
- 27, /* ANALYZE => ID */
- 27, /* ASC => ID */
- 27, /* ATTACH => ID */
- 27, /* BEFORE => ID */
- 27, /* BY => ID */
- 27, /* CASCADE => ID */
- 27, /* CAST => ID */
- 27, /* COLUMNKW => ID */
- 27, /* CONFLICT => ID */
- 27, /* DATABASE => ID */
- 27, /* DESC => ID */
- 27, /* DETACH => ID */
- 27, /* EACH => ID */
- 27, /* FAIL => ID */
- 27, /* FOR => ID */
- 27, /* IGNORE => ID */
- 27, /* INITIALLY => ID */
- 27, /* INSTEAD => ID */
- 27, /* LIKE_KW => ID */
- 27, /* MATCH => ID */
- 27, /* NO => ID */
- 27, /* KEY => ID */
- 27, /* OF => ID */
- 27, /* OFFSET => ID */
- 27, /* PRAGMA => ID */
- 27, /* RAISE => ID */
- 27, /* RECURSIVE => ID */
- 27, /* REPLACE => ID */
- 27, /* RESTRICT => ID */
- 27, /* ROW => ID */
- 27, /* TRIGGER => ID */
- 27, /* VACUUM => ID */
- 27, /* VIEW => ID */
- 27, /* VIRTUAL => ID */
- 27, /* WITH => ID */
- 27, /* REINDEX => ID */
- 27, /* RENAME => ID */
- 27, /* CTIME_KW => ID */
+ 55, /* ABORT => ID */
+ 55, /* ACTION => ID */
+ 55, /* AFTER => ID */
+ 55, /* ANALYZE => ID */
+ 55, /* ASC => ID */
+ 55, /* ATTACH => ID */
+ 55, /* BEFORE => ID */
+ 55, /* BY => ID */
+ 55, /* CASCADE => ID */
+ 55, /* CAST => ID */
+ 55, /* COLUMNKW => ID */
+ 55, /* CONFLICT => ID */
+ 55, /* DATABASE => ID */
+ 55, /* DESC => ID */
+ 55, /* DETACH => ID */
+ 55, /* EACH => ID */
+ 55, /* FAIL => ID */
+ 55, /* FOR => ID */
+ 55, /* IGNORE => ID */
+ 55, /* INITIALLY => ID */
+ 55, /* INSTEAD => ID */
+ 55, /* NO => ID */
+ 55, /* KEY => ID */
+ 55, /* OF => ID */
+ 55, /* OFFSET => ID */
+ 55, /* PRAGMA => ID */
+ 55, /* RAISE => ID */
+ 55, /* RECURSIVE => ID */
+ 55, /* REPLACE => ID */
+ 55, /* RESTRICT => ID */
+ 55, /* ROW => ID */
+ 55, /* TRIGGER => ID */
+ 55, /* VACUUM => ID */
+ 55, /* VIEW => ID */
+ 55, /* VIRTUAL => ID */
+ 55, /* WITH => ID */
+ 55, /* REINDEX => ID */
+ 55, /* RENAME => ID */
+ 55, /* CTIME_KW => ID */
};
#endif /* YYFALLBACK */
@@ -124090,9 +131657,13 @@ static const YYCODETYPE yyFallback[] = {
** + The semantic value stored at this level of the stack. This is
** the information used by the action routines in the grammar.
** It is sometimes called the "minor" token.
+**
+** After the "shift" half of a SHIFTREDUCE action, the stateno field
+** actually contains the reduce action for the second half of the
+** SHIFTREDUCE.
*/
struct yyStackEntry {
- YYACTIONTYPE stateno; /* The state-number */
+ YYACTIONTYPE stateno; /* The state-number, or reduce action in SHIFTREDUCE */
YYCODETYPE major; /* The major token value. This is the code
** number for the token at this stack level */
YYMINORTYPE minor; /* The user-supplied minor token value. This
@@ -124107,7 +131678,9 @@ struct yyParser {
#ifdef YYTRACKMAXSTACKDEPTH
int yyidxMax; /* Maximum value of yyidx */
#endif
+#ifndef YYNOERRORRECOVERY
int yyerrcnt; /* Shifts left before out of the error */
+#endif
sqlite3ParserARG_SDECL /* A place to hold %extra_argument */
#if YYSTACKDEPTH<=0
int yystksz; /* Current side of the stack */
@@ -124160,25 +131733,25 @@ static const char *const yyTokenName[] = {
"ROLLBACK", "SAVEPOINT", "RELEASE", "TO",
"TABLE", "CREATE", "IF", "NOT",
"EXISTS", "TEMP", "LP", "RP",
- "AS", "WITHOUT", "COMMA", "ID",
+ "AS", "WITHOUT", "COMMA", "OR",
+ "AND", "IS", "MATCH", "LIKE_KW",
+ "BETWEEN", "IN", "ISNULL", "NOTNULL",
+ "NE", "EQ", "GT", "LE",
+ "LT", "GE", "ESCAPE", "BITAND",
+ "BITOR", "LSHIFT", "RSHIFT", "PLUS",
+ "MINUS", "STAR", "SLASH", "REM",
+ "CONCAT", "COLLATE", "BITNOT", "ID",
"INDEXED", "ABORT", "ACTION", "AFTER",
"ANALYZE", "ASC", "ATTACH", "BEFORE",
"BY", "CASCADE", "CAST", "COLUMNKW",
"CONFLICT", "DATABASE", "DESC", "DETACH",
"EACH", "FAIL", "FOR", "IGNORE",
- "INITIALLY", "INSTEAD", "LIKE_KW", "MATCH",
- "NO", "KEY", "OF", "OFFSET",
- "PRAGMA", "RAISE", "RECURSIVE", "REPLACE",
- "RESTRICT", "ROW", "TRIGGER", "VACUUM",
- "VIEW", "VIRTUAL", "WITH", "REINDEX",
- "RENAME", "CTIME_KW", "ANY", "OR",
- "AND", "IS", "BETWEEN", "IN",
- "ISNULL", "NOTNULL", "NE", "EQ",
- "GT", "LE", "LT", "GE",
- "ESCAPE", "BITAND", "BITOR", "LSHIFT",
- "RSHIFT", "PLUS", "MINUS", "STAR",
- "SLASH", "REM", "CONCAT", "COLLATE",
- "BITNOT", "STRING", "JOIN_KW", "CONSTRAINT",
+ "INITIALLY", "INSTEAD", "NO", "KEY",
+ "OF", "OFFSET", "PRAGMA", "RAISE",
+ "RECURSIVE", "REPLACE", "RESTRICT", "ROW",
+ "TRIGGER", "VACUUM", "VIEW", "VIRTUAL",
+ "WITH", "REINDEX", "RENAME", "CTIME_KW",
+ "ANY", "STRING", "JOIN_KW", "CONSTRAINT",
"DEFAULT", "NULL", "PRIMARY", "UNIQUE",
"CHECK", "REFERENCES", "AUTOINCR", "ON",
"INSERT", "DELETE", "UPDATE", "SET",
@@ -124195,29 +131768,28 @@ static const char *const yyTokenName[] = {
"nm", "savepoint_opt", "create_table", "create_table_args",
"createkw", "temp", "ifnotexists", "dbnm",
"columnlist", "conslist_opt", "table_options", "select",
- "column", "columnid", "type", "carglist",
- "typetoken", "typename", "signed", "plus_num",
- "minus_num", "ccons", "term", "expr",
- "onconf", "sortorder", "autoinc", "idxlist_opt",
- "refargs", "defer_subclause", "refarg", "refact",
- "init_deferred_pred_opt", "conslist", "tconscomma", "tcons",
- "idxlist", "defer_subclause_opt", "orconf", "resolvetype",
- "raisetype", "ifexists", "fullname", "selectnowith",
- "oneselect", "with", "multiselect_op", "distinct",
- "selcollist", "from", "where_opt", "groupby_opt",
- "having_opt", "orderby_opt", "limit_opt", "values",
- "nexprlist", "exprlist", "sclp", "as",
- "seltablist", "stl_prefix", "joinop", "indexed_opt",
- "on_opt", "using_opt", "joinop2", "idlist",
- "sortlist", "setlist", "insert_cmd", "inscollist_opt",
- "likeop", "between_op", "in_op", "case_operand",
- "case_exprlist", "case_else", "uniqueflag", "collate",
- "nmnum", "trigger_decl", "trigger_cmd_list", "trigger_time",
- "trigger_event", "foreach_clause", "when_clause", "trigger_cmd",
- "trnm", "tridxby", "database_kw_opt", "key_opt",
- "add_column_fullname", "kwcolumn_opt", "create_vtab", "vtabarglist",
- "vtabarg", "vtabargtoken", "lp", "anylist",
- "wqlist",
+ "columnname", "carglist", "typetoken", "typename",
+ "signed", "plus_num", "minus_num", "ccons",
+ "term", "expr", "onconf", "sortorder",
+ "autoinc", "eidlist_opt", "refargs", "defer_subclause",
+ "refarg", "refact", "init_deferred_pred_opt", "conslist",
+ "tconscomma", "tcons", "sortlist", "eidlist",
+ "defer_subclause_opt", "orconf", "resolvetype", "raisetype",
+ "ifexists", "fullname", "selectnowith", "oneselect",
+ "with", "multiselect_op", "distinct", "selcollist",
+ "from", "where_opt", "groupby_opt", "having_opt",
+ "orderby_opt", "limit_opt", "values", "nexprlist",
+ "exprlist", "sclp", "as", "seltablist",
+ "stl_prefix", "joinop", "indexed_opt", "on_opt",
+ "using_opt", "idlist", "setlist", "insert_cmd",
+ "idlist_opt", "likeop", "between_op", "in_op",
+ "case_operand", "case_exprlist", "case_else", "uniqueflag",
+ "collate", "nmnum", "trigger_decl", "trigger_cmd_list",
+ "trigger_time", "trigger_event", "foreach_clause", "when_clause",
+ "trigger_cmd", "trnm", "tridxby", "database_kw_opt",
+ "key_opt", "add_column_fullname", "kwcolumn_opt", "create_vtab",
+ "vtabarglist", "vtabarg", "vtabargtoken", "lp",
+ "anylist", "wqlist",
};
#endif /* NDEBUG */
@@ -124225,333 +131797,332 @@ static const char *const yyTokenName[] = {
/* For tracing reduce actions, the names of all rules are required.
*/
static const char *const yyRuleName[] = {
- /* 0 */ "input ::= cmdlist",
- /* 1 */ "cmdlist ::= cmdlist ecmd",
- /* 2 */ "cmdlist ::= ecmd",
- /* 3 */ "ecmd ::= SEMI",
- /* 4 */ "ecmd ::= explain cmdx SEMI",
- /* 5 */ "explain ::=",
- /* 6 */ "explain ::= EXPLAIN",
- /* 7 */ "explain ::= EXPLAIN QUERY PLAN",
- /* 8 */ "cmdx ::= cmd",
- /* 9 */ "cmd ::= BEGIN transtype trans_opt",
- /* 10 */ "trans_opt ::=",
- /* 11 */ "trans_opt ::= TRANSACTION",
- /* 12 */ "trans_opt ::= TRANSACTION nm",
- /* 13 */ "transtype ::=",
- /* 14 */ "transtype ::= DEFERRED",
- /* 15 */ "transtype ::= IMMEDIATE",
- /* 16 */ "transtype ::= EXCLUSIVE",
- /* 17 */ "cmd ::= COMMIT trans_opt",
- /* 18 */ "cmd ::= END trans_opt",
- /* 19 */ "cmd ::= ROLLBACK trans_opt",
- /* 20 */ "savepoint_opt ::= SAVEPOINT",
- /* 21 */ "savepoint_opt ::=",
- /* 22 */ "cmd ::= SAVEPOINT nm",
- /* 23 */ "cmd ::= RELEASE savepoint_opt nm",
- /* 24 */ "cmd ::= ROLLBACK trans_opt TO savepoint_opt nm",
- /* 25 */ "cmd ::= create_table create_table_args",
- /* 26 */ "create_table ::= createkw temp TABLE ifnotexists nm dbnm",
- /* 27 */ "createkw ::= CREATE",
- /* 28 */ "ifnotexists ::=",
- /* 29 */ "ifnotexists ::= IF NOT EXISTS",
- /* 30 */ "temp ::= TEMP",
- /* 31 */ "temp ::=",
- /* 32 */ "create_table_args ::= LP columnlist conslist_opt RP table_options",
- /* 33 */ "create_table_args ::= AS select",
- /* 34 */ "table_options ::=",
- /* 35 */ "table_options ::= WITHOUT nm",
- /* 36 */ "columnlist ::= columnlist COMMA column",
- /* 37 */ "columnlist ::= column",
- /* 38 */ "column ::= columnid type carglist",
- /* 39 */ "columnid ::= nm",
- /* 40 */ "nm ::= ID|INDEXED",
- /* 41 */ "nm ::= STRING",
- /* 42 */ "nm ::= JOIN_KW",
- /* 43 */ "type ::=",
- /* 44 */ "type ::= typetoken",
- /* 45 */ "typetoken ::= typename",
- /* 46 */ "typetoken ::= typename LP signed RP",
- /* 47 */ "typetoken ::= typename LP signed COMMA signed RP",
- /* 48 */ "typename ::= ID|STRING",
- /* 49 */ "typename ::= typename ID|STRING",
- /* 50 */ "signed ::= plus_num",
- /* 51 */ "signed ::= minus_num",
- /* 52 */ "carglist ::= carglist ccons",
- /* 53 */ "carglist ::=",
- /* 54 */ "ccons ::= CONSTRAINT nm",
- /* 55 */ "ccons ::= DEFAULT term",
- /* 56 */ "ccons ::= DEFAULT LP expr RP",
- /* 57 */ "ccons ::= DEFAULT PLUS term",
- /* 58 */ "ccons ::= DEFAULT MINUS term",
- /* 59 */ "ccons ::= DEFAULT ID|INDEXED",
- /* 60 */ "ccons ::= NULL onconf",
- /* 61 */ "ccons ::= NOT NULL onconf",
- /* 62 */ "ccons ::= PRIMARY KEY sortorder onconf autoinc",
- /* 63 */ "ccons ::= UNIQUE onconf",
- /* 64 */ "ccons ::= CHECK LP expr RP",
- /* 65 */ "ccons ::= REFERENCES nm idxlist_opt refargs",
- /* 66 */ "ccons ::= defer_subclause",
- /* 67 */ "ccons ::= COLLATE ID|STRING",
- /* 68 */ "autoinc ::=",
- /* 69 */ "autoinc ::= AUTOINCR",
- /* 70 */ "refargs ::=",
- /* 71 */ "refargs ::= refargs refarg",
- /* 72 */ "refarg ::= MATCH nm",
- /* 73 */ "refarg ::= ON INSERT refact",
- /* 74 */ "refarg ::= ON DELETE refact",
- /* 75 */ "refarg ::= ON UPDATE refact",
- /* 76 */ "refact ::= SET NULL",
- /* 77 */ "refact ::= SET DEFAULT",
- /* 78 */ "refact ::= CASCADE",
- /* 79 */ "refact ::= RESTRICT",
- /* 80 */ "refact ::= NO ACTION",
- /* 81 */ "defer_subclause ::= NOT DEFERRABLE init_deferred_pred_opt",
- /* 82 */ "defer_subclause ::= DEFERRABLE init_deferred_pred_opt",
- /* 83 */ "init_deferred_pred_opt ::=",
- /* 84 */ "init_deferred_pred_opt ::= INITIALLY DEFERRED",
- /* 85 */ "init_deferred_pred_opt ::= INITIALLY IMMEDIATE",
- /* 86 */ "conslist_opt ::=",
- /* 87 */ "conslist_opt ::= COMMA conslist",
- /* 88 */ "conslist ::= conslist tconscomma tcons",
- /* 89 */ "conslist ::= tcons",
- /* 90 */ "tconscomma ::= COMMA",
- /* 91 */ "tconscomma ::=",
- /* 92 */ "tcons ::= CONSTRAINT nm",
- /* 93 */ "tcons ::= PRIMARY KEY LP idxlist autoinc RP onconf",
- /* 94 */ "tcons ::= UNIQUE LP idxlist RP onconf",
- /* 95 */ "tcons ::= CHECK LP expr RP onconf",
- /* 96 */ "tcons ::= FOREIGN KEY LP idxlist RP REFERENCES nm idxlist_opt refargs defer_subclause_opt",
- /* 97 */ "defer_subclause_opt ::=",
- /* 98 */ "defer_subclause_opt ::= defer_subclause",
- /* 99 */ "onconf ::=",
- /* 100 */ "onconf ::= ON CONFLICT resolvetype",
- /* 101 */ "orconf ::=",
- /* 102 */ "orconf ::= OR resolvetype",
- /* 103 */ "resolvetype ::= raisetype",
- /* 104 */ "resolvetype ::= IGNORE",
- /* 105 */ "resolvetype ::= REPLACE",
- /* 106 */ "cmd ::= DROP TABLE ifexists fullname",
- /* 107 */ "ifexists ::= IF EXISTS",
- /* 108 */ "ifexists ::=",
- /* 109 */ "cmd ::= createkw temp VIEW ifnotexists nm dbnm AS select",
- /* 110 */ "cmd ::= DROP VIEW ifexists fullname",
- /* 111 */ "cmd ::= select",
- /* 112 */ "select ::= with selectnowith",
- /* 113 */ "selectnowith ::= oneselect",
- /* 114 */ "selectnowith ::= selectnowith multiselect_op oneselect",
- /* 115 */ "multiselect_op ::= UNION",
- /* 116 */ "multiselect_op ::= UNION ALL",
- /* 117 */ "multiselect_op ::= EXCEPT|INTERSECT",
- /* 118 */ "oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt orderby_opt limit_opt",
- /* 119 */ "oneselect ::= values",
- /* 120 */ "values ::= VALUES LP nexprlist RP",
- /* 121 */ "values ::= values COMMA LP exprlist RP",
- /* 122 */ "distinct ::= DISTINCT",
- /* 123 */ "distinct ::= ALL",
- /* 124 */ "distinct ::=",
- /* 125 */ "sclp ::= selcollist COMMA",
- /* 126 */ "sclp ::=",
- /* 127 */ "selcollist ::= sclp expr as",
- /* 128 */ "selcollist ::= sclp STAR",
- /* 129 */ "selcollist ::= sclp nm DOT STAR",
- /* 130 */ "as ::= AS nm",
- /* 131 */ "as ::= ID|STRING",
- /* 132 */ "as ::=",
- /* 133 */ "from ::=",
- /* 134 */ "from ::= FROM seltablist",
- /* 135 */ "stl_prefix ::= seltablist joinop",
- /* 136 */ "stl_prefix ::=",
- /* 137 */ "seltablist ::= stl_prefix nm dbnm as indexed_opt on_opt using_opt",
- /* 138 */ "seltablist ::= stl_prefix LP select RP as on_opt using_opt",
- /* 139 */ "seltablist ::= stl_prefix LP seltablist RP as on_opt using_opt",
- /* 140 */ "dbnm ::=",
- /* 141 */ "dbnm ::= DOT nm",
- /* 142 */ "fullname ::= nm dbnm",
- /* 143 */ "joinop ::= COMMA|JOIN",
- /* 144 */ "joinop ::= JOIN_KW JOIN",
- /* 145 */ "joinop ::= JOIN_KW nm JOIN",
- /* 146 */ "joinop ::= JOIN_KW nm nm JOIN",
- /* 147 */ "on_opt ::= ON expr",
- /* 148 */ "on_opt ::=",
- /* 149 */ "indexed_opt ::=",
- /* 150 */ "indexed_opt ::= INDEXED BY nm",
- /* 151 */ "indexed_opt ::= NOT INDEXED",
- /* 152 */ "using_opt ::= USING LP idlist RP",
- /* 153 */ "using_opt ::=",
- /* 154 */ "orderby_opt ::=",
- /* 155 */ "orderby_opt ::= ORDER BY sortlist",
- /* 156 */ "sortlist ::= sortlist COMMA expr sortorder",
- /* 157 */ "sortlist ::= expr sortorder",
- /* 158 */ "sortorder ::= ASC",
- /* 159 */ "sortorder ::= DESC",
- /* 160 */ "sortorder ::=",
- /* 161 */ "groupby_opt ::=",
- /* 162 */ "groupby_opt ::= GROUP BY nexprlist",
- /* 163 */ "having_opt ::=",
- /* 164 */ "having_opt ::= HAVING expr",
- /* 165 */ "limit_opt ::=",
- /* 166 */ "limit_opt ::= LIMIT expr",
- /* 167 */ "limit_opt ::= LIMIT expr OFFSET expr",
- /* 168 */ "limit_opt ::= LIMIT expr COMMA expr",
- /* 169 */ "cmd ::= with DELETE FROM fullname indexed_opt where_opt",
- /* 170 */ "where_opt ::=",
- /* 171 */ "where_opt ::= WHERE expr",
- /* 172 */ "cmd ::= with UPDATE orconf fullname indexed_opt SET setlist where_opt",
- /* 173 */ "setlist ::= setlist COMMA nm EQ expr",
- /* 174 */ "setlist ::= nm EQ expr",
- /* 175 */ "cmd ::= with insert_cmd INTO fullname inscollist_opt select",
- /* 176 */ "cmd ::= with insert_cmd INTO fullname inscollist_opt DEFAULT VALUES",
- /* 177 */ "insert_cmd ::= INSERT orconf",
- /* 178 */ "insert_cmd ::= REPLACE",
- /* 179 */ "inscollist_opt ::=",
- /* 180 */ "inscollist_opt ::= LP idlist RP",
- /* 181 */ "idlist ::= idlist COMMA nm",
- /* 182 */ "idlist ::= nm",
- /* 183 */ "expr ::= term",
- /* 184 */ "expr ::= LP expr RP",
- /* 185 */ "term ::= NULL",
- /* 186 */ "expr ::= ID|INDEXED",
- /* 187 */ "expr ::= JOIN_KW",
- /* 188 */ "expr ::= nm DOT nm",
- /* 189 */ "expr ::= nm DOT nm DOT nm",
- /* 190 */ "term ::= INTEGER|FLOAT|BLOB",
- /* 191 */ "term ::= STRING",
- /* 192 */ "expr ::= VARIABLE",
- /* 193 */ "expr ::= expr COLLATE ID|STRING",
- /* 194 */ "expr ::= CAST LP expr AS typetoken RP",
- /* 195 */ "expr ::= ID|INDEXED LP distinct exprlist RP",
- /* 196 */ "expr ::= ID|INDEXED LP STAR RP",
- /* 197 */ "term ::= CTIME_KW",
- /* 198 */ "expr ::= expr AND expr",
- /* 199 */ "expr ::= expr OR expr",
- /* 200 */ "expr ::= expr LT|GT|GE|LE expr",
- /* 201 */ "expr ::= expr EQ|NE expr",
- /* 202 */ "expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr",
- /* 203 */ "expr ::= expr PLUS|MINUS expr",
- /* 204 */ "expr ::= expr STAR|SLASH|REM expr",
- /* 205 */ "expr ::= expr CONCAT expr",
- /* 206 */ "likeop ::= LIKE_KW|MATCH",
- /* 207 */ "likeop ::= NOT LIKE_KW|MATCH",
- /* 208 */ "expr ::= expr likeop expr",
- /* 209 */ "expr ::= expr likeop expr ESCAPE expr",
- /* 210 */ "expr ::= expr ISNULL|NOTNULL",
- /* 211 */ "expr ::= expr NOT NULL",
- /* 212 */ "expr ::= expr IS expr",
- /* 213 */ "expr ::= expr IS NOT expr",
- /* 214 */ "expr ::= NOT expr",
- /* 215 */ "expr ::= BITNOT expr",
- /* 216 */ "expr ::= MINUS expr",
- /* 217 */ "expr ::= PLUS expr",
- /* 218 */ "between_op ::= BETWEEN",
- /* 219 */ "between_op ::= NOT BETWEEN",
- /* 220 */ "expr ::= expr between_op expr AND expr",
- /* 221 */ "in_op ::= IN",
- /* 222 */ "in_op ::= NOT IN",
- /* 223 */ "expr ::= expr in_op LP exprlist RP",
- /* 224 */ "expr ::= LP select RP",
- /* 225 */ "expr ::= expr in_op LP select RP",
- /* 226 */ "expr ::= expr in_op nm dbnm",
- /* 227 */ "expr ::= EXISTS LP select RP",
- /* 228 */ "expr ::= CASE case_operand case_exprlist case_else END",
- /* 229 */ "case_exprlist ::= case_exprlist WHEN expr THEN expr",
- /* 230 */ "case_exprlist ::= WHEN expr THEN expr",
- /* 231 */ "case_else ::= ELSE expr",
- /* 232 */ "case_else ::=",
- /* 233 */ "case_operand ::= expr",
- /* 234 */ "case_operand ::=",
- /* 235 */ "exprlist ::= nexprlist",
- /* 236 */ "exprlist ::=",
- /* 237 */ "nexprlist ::= nexprlist COMMA expr",
- /* 238 */ "nexprlist ::= expr",
- /* 239 */ "cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP idxlist RP where_opt",
- /* 240 */ "uniqueflag ::= UNIQUE",
- /* 241 */ "uniqueflag ::=",
- /* 242 */ "idxlist_opt ::=",
- /* 243 */ "idxlist_opt ::= LP idxlist RP",
- /* 244 */ "idxlist ::= idxlist COMMA nm collate sortorder",
- /* 245 */ "idxlist ::= nm collate sortorder",
- /* 246 */ "collate ::=",
- /* 247 */ "collate ::= COLLATE ID|STRING",
- /* 248 */ "cmd ::= DROP INDEX ifexists fullname",
- /* 249 */ "cmd ::= VACUUM",
- /* 250 */ "cmd ::= VACUUM nm",
- /* 251 */ "cmd ::= PRAGMA nm dbnm",
- /* 252 */ "cmd ::= PRAGMA nm dbnm EQ nmnum",
- /* 253 */ "cmd ::= PRAGMA nm dbnm LP nmnum RP",
- /* 254 */ "cmd ::= PRAGMA nm dbnm EQ minus_num",
- /* 255 */ "cmd ::= PRAGMA nm dbnm LP minus_num RP",
- /* 256 */ "nmnum ::= plus_num",
- /* 257 */ "nmnum ::= nm",
- /* 258 */ "nmnum ::= ON",
- /* 259 */ "nmnum ::= DELETE",
- /* 260 */ "nmnum ::= DEFAULT",
- /* 261 */ "plus_num ::= PLUS INTEGER|FLOAT",
- /* 262 */ "plus_num ::= INTEGER|FLOAT",
- /* 263 */ "minus_num ::= MINUS INTEGER|FLOAT",
- /* 264 */ "cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END",
- /* 265 */ "trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause",
- /* 266 */ "trigger_time ::= BEFORE",
- /* 267 */ "trigger_time ::= AFTER",
- /* 268 */ "trigger_time ::= INSTEAD OF",
- /* 269 */ "trigger_time ::=",
- /* 270 */ "trigger_event ::= DELETE|INSERT",
- /* 271 */ "trigger_event ::= UPDATE",
- /* 272 */ "trigger_event ::= UPDATE OF idlist",
- /* 273 */ "foreach_clause ::=",
- /* 274 */ "foreach_clause ::= FOR EACH ROW",
- /* 275 */ "when_clause ::=",
- /* 276 */ "when_clause ::= WHEN expr",
- /* 277 */ "trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI",
- /* 278 */ "trigger_cmd_list ::= trigger_cmd SEMI",
- /* 279 */ "trnm ::= nm",
- /* 280 */ "trnm ::= nm DOT nm",
- /* 281 */ "tridxby ::=",
- /* 282 */ "tridxby ::= INDEXED BY nm",
- /* 283 */ "tridxby ::= NOT INDEXED",
- /* 284 */ "trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist where_opt",
- /* 285 */ "trigger_cmd ::= insert_cmd INTO trnm inscollist_opt select",
- /* 286 */ "trigger_cmd ::= DELETE FROM trnm tridxby where_opt",
- /* 287 */ "trigger_cmd ::= select",
- /* 288 */ "expr ::= RAISE LP IGNORE RP",
- /* 289 */ "expr ::= RAISE LP raisetype COMMA nm RP",
- /* 290 */ "raisetype ::= ROLLBACK",
- /* 291 */ "raisetype ::= ABORT",
- /* 292 */ "raisetype ::= FAIL",
- /* 293 */ "cmd ::= DROP TRIGGER ifexists fullname",
- /* 294 */ "cmd ::= ATTACH database_kw_opt expr AS expr key_opt",
- /* 295 */ "cmd ::= DETACH database_kw_opt expr",
- /* 296 */ "key_opt ::=",
- /* 297 */ "key_opt ::= KEY expr",
- /* 298 */ "database_kw_opt ::= DATABASE",
- /* 299 */ "database_kw_opt ::=",
- /* 300 */ "cmd ::= REINDEX",
- /* 301 */ "cmd ::= REINDEX nm dbnm",
- /* 302 */ "cmd ::= ANALYZE",
- /* 303 */ "cmd ::= ANALYZE nm dbnm",
- /* 304 */ "cmd ::= ALTER TABLE fullname RENAME TO nm",
- /* 305 */ "cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt column",
- /* 306 */ "add_column_fullname ::= fullname",
- /* 307 */ "kwcolumn_opt ::=",
- /* 308 */ "kwcolumn_opt ::= COLUMNKW",
- /* 309 */ "cmd ::= create_vtab",
- /* 310 */ "cmd ::= create_vtab LP vtabarglist RP",
- /* 311 */ "create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm",
- /* 312 */ "vtabarglist ::= vtabarg",
- /* 313 */ "vtabarglist ::= vtabarglist COMMA vtabarg",
- /* 314 */ "vtabarg ::=",
- /* 315 */ "vtabarg ::= vtabarg vtabargtoken",
- /* 316 */ "vtabargtoken ::= ANY",
- /* 317 */ "vtabargtoken ::= lp anylist RP",
- /* 318 */ "lp ::= LP",
- /* 319 */ "anylist ::=",
- /* 320 */ "anylist ::= anylist LP anylist RP",
- /* 321 */ "anylist ::= anylist ANY",
- /* 322 */ "with ::=",
- /* 323 */ "with ::= WITH wqlist",
- /* 324 */ "with ::= WITH RECURSIVE wqlist",
- /* 325 */ "wqlist ::= nm idxlist_opt AS LP select RP",
- /* 326 */ "wqlist ::= wqlist COMMA nm idxlist_opt AS LP select RP",
+ /* 0 */ "explain ::= EXPLAIN",
+ /* 1 */ "explain ::= EXPLAIN QUERY PLAN",
+ /* 2 */ "cmdx ::= cmd",
+ /* 3 */ "cmd ::= BEGIN transtype trans_opt",
+ /* 4 */ "transtype ::=",
+ /* 5 */ "transtype ::= DEFERRED",
+ /* 6 */ "transtype ::= IMMEDIATE",
+ /* 7 */ "transtype ::= EXCLUSIVE",
+ /* 8 */ "cmd ::= COMMIT trans_opt",
+ /* 9 */ "cmd ::= END trans_opt",
+ /* 10 */ "cmd ::= ROLLBACK trans_opt",
+ /* 11 */ "cmd ::= SAVEPOINT nm",
+ /* 12 */ "cmd ::= RELEASE savepoint_opt nm",
+ /* 13 */ "cmd ::= ROLLBACK trans_opt TO savepoint_opt nm",
+ /* 14 */ "create_table ::= createkw temp TABLE ifnotexists nm dbnm",
+ /* 15 */ "createkw ::= CREATE",
+ /* 16 */ "ifnotexists ::=",
+ /* 17 */ "ifnotexists ::= IF NOT EXISTS",
+ /* 18 */ "temp ::= TEMP",
+ /* 19 */ "temp ::=",
+ /* 20 */ "create_table_args ::= LP columnlist conslist_opt RP table_options",
+ /* 21 */ "create_table_args ::= AS select",
+ /* 22 */ "table_options ::=",
+ /* 23 */ "table_options ::= WITHOUT nm",
+ /* 24 */ "columnname ::= nm typetoken",
+ /* 25 */ "typetoken ::=",
+ /* 26 */ "typetoken ::= typename LP signed RP",
+ /* 27 */ "typetoken ::= typename LP signed COMMA signed RP",
+ /* 28 */ "typename ::= typename ID|STRING",
+ /* 29 */ "ccons ::= CONSTRAINT nm",
+ /* 30 */ "ccons ::= DEFAULT term",
+ /* 31 */ "ccons ::= DEFAULT LP expr RP",
+ /* 32 */ "ccons ::= DEFAULT PLUS term",
+ /* 33 */ "ccons ::= DEFAULT MINUS term",
+ /* 34 */ "ccons ::= DEFAULT ID|INDEXED",
+ /* 35 */ "ccons ::= NOT NULL onconf",
+ /* 36 */ "ccons ::= PRIMARY KEY sortorder onconf autoinc",
+ /* 37 */ "ccons ::= UNIQUE onconf",
+ /* 38 */ "ccons ::= CHECK LP expr RP",
+ /* 39 */ "ccons ::= REFERENCES nm eidlist_opt refargs",
+ /* 40 */ "ccons ::= defer_subclause",
+ /* 41 */ "ccons ::= COLLATE ID|STRING",
+ /* 42 */ "autoinc ::=",
+ /* 43 */ "autoinc ::= AUTOINCR",
+ /* 44 */ "refargs ::=",
+ /* 45 */ "refargs ::= refargs refarg",
+ /* 46 */ "refarg ::= MATCH nm",
+ /* 47 */ "refarg ::= ON INSERT refact",
+ /* 48 */ "refarg ::= ON DELETE refact",
+ /* 49 */ "refarg ::= ON UPDATE refact",
+ /* 50 */ "refact ::= SET NULL",
+ /* 51 */ "refact ::= SET DEFAULT",
+ /* 52 */ "refact ::= CASCADE",
+ /* 53 */ "refact ::= RESTRICT",
+ /* 54 */ "refact ::= NO ACTION",
+ /* 55 */ "defer_subclause ::= NOT DEFERRABLE init_deferred_pred_opt",
+ /* 56 */ "defer_subclause ::= DEFERRABLE init_deferred_pred_opt",
+ /* 57 */ "init_deferred_pred_opt ::=",
+ /* 58 */ "init_deferred_pred_opt ::= INITIALLY DEFERRED",
+ /* 59 */ "init_deferred_pred_opt ::= INITIALLY IMMEDIATE",
+ /* 60 */ "conslist_opt ::=",
+ /* 61 */ "tconscomma ::= COMMA",
+ /* 62 */ "tcons ::= CONSTRAINT nm",
+ /* 63 */ "tcons ::= PRIMARY KEY LP sortlist autoinc RP onconf",
+ /* 64 */ "tcons ::= UNIQUE LP sortlist RP onconf",
+ /* 65 */ "tcons ::= CHECK LP expr RP onconf",
+ /* 66 */ "tcons ::= FOREIGN KEY LP eidlist RP REFERENCES nm eidlist_opt refargs defer_subclause_opt",
+ /* 67 */ "defer_subclause_opt ::=",
+ /* 68 */ "onconf ::=",
+ /* 69 */ "onconf ::= ON CONFLICT resolvetype",
+ /* 70 */ "orconf ::=",
+ /* 71 */ "orconf ::= OR resolvetype",
+ /* 72 */ "resolvetype ::= IGNORE",
+ /* 73 */ "resolvetype ::= REPLACE",
+ /* 74 */ "cmd ::= DROP TABLE ifexists fullname",
+ /* 75 */ "ifexists ::= IF EXISTS",
+ /* 76 */ "ifexists ::=",
+ /* 77 */ "cmd ::= createkw temp VIEW ifnotexists nm dbnm eidlist_opt AS select",
+ /* 78 */ "cmd ::= DROP VIEW ifexists fullname",
+ /* 79 */ "cmd ::= select",
+ /* 80 */ "select ::= with selectnowith",
+ /* 81 */ "selectnowith ::= selectnowith multiselect_op oneselect",
+ /* 82 */ "multiselect_op ::= UNION",
+ /* 83 */ "multiselect_op ::= UNION ALL",
+ /* 84 */ "multiselect_op ::= EXCEPT|INTERSECT",
+ /* 85 */ "oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt orderby_opt limit_opt",
+ /* 86 */ "values ::= VALUES LP nexprlist RP",
+ /* 87 */ "values ::= values COMMA LP exprlist RP",
+ /* 88 */ "distinct ::= DISTINCT",
+ /* 89 */ "distinct ::= ALL",
+ /* 90 */ "distinct ::=",
+ /* 91 */ "sclp ::=",
+ /* 92 */ "selcollist ::= sclp expr as",
+ /* 93 */ "selcollist ::= sclp STAR",
+ /* 94 */ "selcollist ::= sclp nm DOT STAR",
+ /* 95 */ "as ::= AS nm",
+ /* 96 */ "as ::=",
+ /* 97 */ "from ::=",
+ /* 98 */ "from ::= FROM seltablist",
+ /* 99 */ "stl_prefix ::= seltablist joinop",
+ /* 100 */ "stl_prefix ::=",
+ /* 101 */ "seltablist ::= stl_prefix nm dbnm as indexed_opt on_opt using_opt",
+ /* 102 */ "seltablist ::= stl_prefix nm dbnm LP exprlist RP as on_opt using_opt",
+ /* 103 */ "seltablist ::= stl_prefix LP select RP as on_opt using_opt",
+ /* 104 */ "seltablist ::= stl_prefix LP seltablist RP as on_opt using_opt",
+ /* 105 */ "dbnm ::=",
+ /* 106 */ "dbnm ::= DOT nm",
+ /* 107 */ "fullname ::= nm dbnm",
+ /* 108 */ "joinop ::= COMMA|JOIN",
+ /* 109 */ "joinop ::= JOIN_KW JOIN",
+ /* 110 */ "joinop ::= JOIN_KW nm JOIN",
+ /* 111 */ "joinop ::= JOIN_KW nm nm JOIN",
+ /* 112 */ "on_opt ::= ON expr",
+ /* 113 */ "on_opt ::=",
+ /* 114 */ "indexed_opt ::=",
+ /* 115 */ "indexed_opt ::= INDEXED BY nm",
+ /* 116 */ "indexed_opt ::= NOT INDEXED",
+ /* 117 */ "using_opt ::= USING LP idlist RP",
+ /* 118 */ "using_opt ::=",
+ /* 119 */ "orderby_opt ::=",
+ /* 120 */ "orderby_opt ::= ORDER BY sortlist",
+ /* 121 */ "sortlist ::= sortlist COMMA expr sortorder",
+ /* 122 */ "sortlist ::= expr sortorder",
+ /* 123 */ "sortorder ::= ASC",
+ /* 124 */ "sortorder ::= DESC",
+ /* 125 */ "sortorder ::=",
+ /* 126 */ "groupby_opt ::=",
+ /* 127 */ "groupby_opt ::= GROUP BY nexprlist",
+ /* 128 */ "having_opt ::=",
+ /* 129 */ "having_opt ::= HAVING expr",
+ /* 130 */ "limit_opt ::=",
+ /* 131 */ "limit_opt ::= LIMIT expr",
+ /* 132 */ "limit_opt ::= LIMIT expr OFFSET expr",
+ /* 133 */ "limit_opt ::= LIMIT expr COMMA expr",
+ /* 134 */ "cmd ::= with DELETE FROM fullname indexed_opt where_opt",
+ /* 135 */ "where_opt ::=",
+ /* 136 */ "where_opt ::= WHERE expr",
+ /* 137 */ "cmd ::= with UPDATE orconf fullname indexed_opt SET setlist where_opt",
+ /* 138 */ "setlist ::= setlist COMMA nm EQ expr",
+ /* 139 */ "setlist ::= nm EQ expr",
+ /* 140 */ "cmd ::= with insert_cmd INTO fullname idlist_opt select",
+ /* 141 */ "cmd ::= with insert_cmd INTO fullname idlist_opt DEFAULT VALUES",
+ /* 142 */ "insert_cmd ::= INSERT orconf",
+ /* 143 */ "insert_cmd ::= REPLACE",
+ /* 144 */ "idlist_opt ::=",
+ /* 145 */ "idlist_opt ::= LP idlist RP",
+ /* 146 */ "idlist ::= idlist COMMA nm",
+ /* 147 */ "idlist ::= nm",
+ /* 148 */ "expr ::= LP expr RP",
+ /* 149 */ "term ::= NULL",
+ /* 150 */ "expr ::= ID|INDEXED",
+ /* 151 */ "expr ::= JOIN_KW",
+ /* 152 */ "expr ::= nm DOT nm",
+ /* 153 */ "expr ::= nm DOT nm DOT nm",
+ /* 154 */ "term ::= INTEGER|FLOAT|BLOB",
+ /* 155 */ "term ::= STRING",
+ /* 156 */ "expr ::= VARIABLE",
+ /* 157 */ "expr ::= expr COLLATE ID|STRING",
+ /* 158 */ "expr ::= CAST LP expr AS typetoken RP",
+ /* 159 */ "expr ::= ID|INDEXED LP distinct exprlist RP",
+ /* 160 */ "expr ::= ID|INDEXED LP STAR RP",
+ /* 161 */ "term ::= CTIME_KW",
+ /* 162 */ "expr ::= expr AND expr",
+ /* 163 */ "expr ::= expr OR expr",
+ /* 164 */ "expr ::= expr LT|GT|GE|LE expr",
+ /* 165 */ "expr ::= expr EQ|NE expr",
+ /* 166 */ "expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr",
+ /* 167 */ "expr ::= expr PLUS|MINUS expr",
+ /* 168 */ "expr ::= expr STAR|SLASH|REM expr",
+ /* 169 */ "expr ::= expr CONCAT expr",
+ /* 170 */ "likeop ::= LIKE_KW|MATCH",
+ /* 171 */ "likeop ::= NOT LIKE_KW|MATCH",
+ /* 172 */ "expr ::= expr likeop expr",
+ /* 173 */ "expr ::= expr likeop expr ESCAPE expr",
+ /* 174 */ "expr ::= expr ISNULL|NOTNULL",
+ /* 175 */ "expr ::= expr NOT NULL",
+ /* 176 */ "expr ::= expr IS expr",
+ /* 177 */ "expr ::= expr IS NOT expr",
+ /* 178 */ "expr ::= NOT expr",
+ /* 179 */ "expr ::= BITNOT expr",
+ /* 180 */ "expr ::= MINUS expr",
+ /* 181 */ "expr ::= PLUS expr",
+ /* 182 */ "between_op ::= BETWEEN",
+ /* 183 */ "between_op ::= NOT BETWEEN",
+ /* 184 */ "expr ::= expr between_op expr AND expr",
+ /* 185 */ "in_op ::= IN",
+ /* 186 */ "in_op ::= NOT IN",
+ /* 187 */ "expr ::= expr in_op LP exprlist RP",
+ /* 188 */ "expr ::= LP select RP",
+ /* 189 */ "expr ::= expr in_op LP select RP",
+ /* 190 */ "expr ::= expr in_op nm dbnm",
+ /* 191 */ "expr ::= EXISTS LP select RP",
+ /* 192 */ "expr ::= CASE case_operand case_exprlist case_else END",
+ /* 193 */ "case_exprlist ::= case_exprlist WHEN expr THEN expr",
+ /* 194 */ "case_exprlist ::= WHEN expr THEN expr",
+ /* 195 */ "case_else ::= ELSE expr",
+ /* 196 */ "case_else ::=",
+ /* 197 */ "case_operand ::= expr",
+ /* 198 */ "case_operand ::=",
+ /* 199 */ "exprlist ::=",
+ /* 200 */ "nexprlist ::= nexprlist COMMA expr",
+ /* 201 */ "nexprlist ::= expr",
+ /* 202 */ "cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt",
+ /* 203 */ "uniqueflag ::= UNIQUE",
+ /* 204 */ "uniqueflag ::=",
+ /* 205 */ "eidlist_opt ::=",
+ /* 206 */ "eidlist_opt ::= LP eidlist RP",
+ /* 207 */ "eidlist ::= eidlist COMMA nm collate sortorder",
+ /* 208 */ "eidlist ::= nm collate sortorder",
+ /* 209 */ "collate ::=",
+ /* 210 */ "collate ::= COLLATE ID|STRING",
+ /* 211 */ "cmd ::= DROP INDEX ifexists fullname",
+ /* 212 */ "cmd ::= VACUUM",
+ /* 213 */ "cmd ::= VACUUM nm",
+ /* 214 */ "cmd ::= PRAGMA nm dbnm",
+ /* 215 */ "cmd ::= PRAGMA nm dbnm EQ nmnum",
+ /* 216 */ "cmd ::= PRAGMA nm dbnm LP nmnum RP",
+ /* 217 */ "cmd ::= PRAGMA nm dbnm EQ minus_num",
+ /* 218 */ "cmd ::= PRAGMA nm dbnm LP minus_num RP",
+ /* 219 */ "plus_num ::= PLUS INTEGER|FLOAT",
+ /* 220 */ "minus_num ::= MINUS INTEGER|FLOAT",
+ /* 221 */ "cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END",
+ /* 222 */ "trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause",
+ /* 223 */ "trigger_time ::= BEFORE",
+ /* 224 */ "trigger_time ::= AFTER",
+ /* 225 */ "trigger_time ::= INSTEAD OF",
+ /* 226 */ "trigger_time ::=",
+ /* 227 */ "trigger_event ::= DELETE|INSERT",
+ /* 228 */ "trigger_event ::= UPDATE",
+ /* 229 */ "trigger_event ::= UPDATE OF idlist",
+ /* 230 */ "when_clause ::=",
+ /* 231 */ "when_clause ::= WHEN expr",
+ /* 232 */ "trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI",
+ /* 233 */ "trigger_cmd_list ::= trigger_cmd SEMI",
+ /* 234 */ "trnm ::= nm DOT nm",
+ /* 235 */ "tridxby ::= INDEXED BY nm",
+ /* 236 */ "tridxby ::= NOT INDEXED",
+ /* 237 */ "trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist where_opt",
+ /* 238 */ "trigger_cmd ::= insert_cmd INTO trnm idlist_opt select",
+ /* 239 */ "trigger_cmd ::= DELETE FROM trnm tridxby where_opt",
+ /* 240 */ "trigger_cmd ::= select",
+ /* 241 */ "expr ::= RAISE LP IGNORE RP",
+ /* 242 */ "expr ::= RAISE LP raisetype COMMA nm RP",
+ /* 243 */ "raisetype ::= ROLLBACK",
+ /* 244 */ "raisetype ::= ABORT",
+ /* 245 */ "raisetype ::= FAIL",
+ /* 246 */ "cmd ::= DROP TRIGGER ifexists fullname",
+ /* 247 */ "cmd ::= ATTACH database_kw_opt expr AS expr key_opt",
+ /* 248 */ "cmd ::= DETACH database_kw_opt expr",
+ /* 249 */ "key_opt ::=",
+ /* 250 */ "key_opt ::= KEY expr",
+ /* 251 */ "cmd ::= REINDEX",
+ /* 252 */ "cmd ::= REINDEX nm dbnm",
+ /* 253 */ "cmd ::= ANALYZE",
+ /* 254 */ "cmd ::= ANALYZE nm dbnm",
+ /* 255 */ "cmd ::= ALTER TABLE fullname RENAME TO nm",
+ /* 256 */ "cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist",
+ /* 257 */ "add_column_fullname ::= fullname",
+ /* 258 */ "cmd ::= create_vtab",
+ /* 259 */ "cmd ::= create_vtab LP vtabarglist RP",
+ /* 260 */ "create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm",
+ /* 261 */ "vtabarg ::=",
+ /* 262 */ "vtabargtoken ::= ANY",
+ /* 263 */ "vtabargtoken ::= lp anylist RP",
+ /* 264 */ "lp ::= LP",
+ /* 265 */ "with ::=",
+ /* 266 */ "with ::= WITH wqlist",
+ /* 267 */ "with ::= WITH RECURSIVE wqlist",
+ /* 268 */ "wqlist ::= nm eidlist_opt AS LP select RP",
+ /* 269 */ "wqlist ::= wqlist COMMA nm eidlist_opt AS LP select RP",
+ /* 270 */ "input ::= cmdlist",
+ /* 271 */ "cmdlist ::= cmdlist ecmd",
+ /* 272 */ "cmdlist ::= ecmd",
+ /* 273 */ "ecmd ::= SEMI",
+ /* 274 */ "ecmd ::= explain cmdx SEMI",
+ /* 275 */ "explain ::=",
+ /* 276 */ "trans_opt ::=",
+ /* 277 */ "trans_opt ::= TRANSACTION",
+ /* 278 */ "trans_opt ::= TRANSACTION nm",
+ /* 279 */ "savepoint_opt ::= SAVEPOINT",
+ /* 280 */ "savepoint_opt ::=",
+ /* 281 */ "cmd ::= create_table create_table_args",
+ /* 282 */ "columnlist ::= columnlist COMMA columnname carglist",
+ /* 283 */ "columnlist ::= columnname carglist",
+ /* 284 */ "nm ::= ID|INDEXED",
+ /* 285 */ "nm ::= STRING",
+ /* 286 */ "nm ::= JOIN_KW",
+ /* 287 */ "typetoken ::= typename",
+ /* 288 */ "typename ::= ID|STRING",
+ /* 289 */ "signed ::= plus_num",
+ /* 290 */ "signed ::= minus_num",
+ /* 291 */ "carglist ::= carglist ccons",
+ /* 292 */ "carglist ::=",
+ /* 293 */ "ccons ::= NULL onconf",
+ /* 294 */ "conslist_opt ::= COMMA conslist",
+ /* 295 */ "conslist ::= conslist tconscomma tcons",
+ /* 296 */ "conslist ::= tcons",
+ /* 297 */ "tconscomma ::=",
+ /* 298 */ "defer_subclause_opt ::= defer_subclause",
+ /* 299 */ "resolvetype ::= raisetype",
+ /* 300 */ "selectnowith ::= oneselect",
+ /* 301 */ "oneselect ::= values",
+ /* 302 */ "sclp ::= selcollist COMMA",
+ /* 303 */ "as ::= ID|STRING",
+ /* 304 */ "expr ::= term",
+ /* 305 */ "exprlist ::= nexprlist",
+ /* 306 */ "nmnum ::= plus_num",
+ /* 307 */ "nmnum ::= nm",
+ /* 308 */ "nmnum ::= ON",
+ /* 309 */ "nmnum ::= DELETE",
+ /* 310 */ "nmnum ::= DEFAULT",
+ /* 311 */ "plus_num ::= INTEGER|FLOAT",
+ /* 312 */ "foreach_clause ::=",
+ /* 313 */ "foreach_clause ::= FOR EACH ROW",
+ /* 314 */ "trnm ::= nm",
+ /* 315 */ "tridxby ::=",
+ /* 316 */ "database_kw_opt ::= DATABASE",
+ /* 317 */ "database_kw_opt ::=",
+ /* 318 */ "kwcolumn_opt ::=",
+ /* 319 */ "kwcolumn_opt ::= COLUMNKW",
+ /* 320 */ "vtabarglist ::= vtabarg",
+ /* 321 */ "vtabarglist ::= vtabarglist COMMA vtabarg",
+ /* 322 */ "vtabarg ::= vtabarg vtabargtoken",
+ /* 323 */ "anylist ::=",
+ /* 324 */ "anylist ::= anylist LP anylist RP",
+ /* 325 */ "anylist ::= anylist ANY",
};
#endif /* NDEBUG */
@@ -124579,6 +132150,15 @@ static void yyGrowStack(yyParser *p){
}
#endif
+/* Datatype of the argument to the memory allocated passed as the
+** second argument to sqlite3ParserAlloc() below. This can be changed by
+** putting an appropriate #define in the %include section of the input
+** grammar.
+*/
+#ifndef YYMALLOCARGTYPE
+# define YYMALLOCARGTYPE size_t
+#endif
+
/*
** This function allocates a new parser.
** The only argument is a pointer to a function which works like
@@ -124591,9 +132171,9 @@ static void yyGrowStack(yyParser *p){
** A pointer to a parser. This pointer is used in subsequent calls
** to sqlite3Parser and sqlite3ParserFree.
*/
-SQLITE_PRIVATE void *sqlite3ParserAlloc(void *(*mallocProc)(u64)){
+SQLITE_PRIVATE void *sqlite3ParserAlloc(void *(*mallocProc)(YYMALLOCARGTYPE)){
yyParser *pParser;
- pParser = (yyParser*)(*mallocProc)( (u64)sizeof(yyParser) );
+ pParser = (yyParser*)(*mallocProc)( (YYMALLOCARGTYPE)sizeof(yyParser) );
if( pParser ){
pParser->yyidx = -1;
#ifdef YYTRACKMAXSTACKDEPTH
@@ -124608,10 +132188,12 @@ SQLITE_PRIVATE void *sqlite3ParserAlloc(void *(*mallocProc)(u64)){
return pParser;
}
-/* The following function deletes the value associated with a
-** symbol. The symbol can be either a terminal or nonterminal.
-** "yymajor" is the symbol code, and "yypminor" is a pointer to
-** the value.
+/* The following function deletes the "minor type" or semantic value
+** associated with a symbol. The symbol can be either a terminal
+** or nonterminal. "yymajor" is the symbol code, and "yypminor" is
+** a pointer to the value to be deleted. The code used to do the
+** deletions is derived from the %destructor and/or %token_destructor
+** directives of the input grammar.
*/
static void yy_destructor(
yyParser *yypParser, /* The parser */
@@ -124627,81 +132209,83 @@ static void yy_destructor(
** being destroyed before it is finished parsing.
**
** Note: during a reduce, the only symbols destroyed are those
- ** which appear on the RHS of the rule, but which are not used
+ ** which appear on the RHS of the rule, but which are *not* used
** inside the C code.
*/
+/********* Begin destructor definitions ***************************************/
case 163: /* select */
- case 195: /* selectnowith */
- case 196: /* oneselect */
- case 207: /* values */
+ case 194: /* selectnowith */
+ case 195: /* oneselect */
+ case 206: /* values */
{
-sqlite3SelectDelete(pParse->db, (yypminor->yy3));
+sqlite3SelectDelete(pParse->db, (yypminor->yy159));
}
break;
- case 174: /* term */
- case 175: /* expr */
+ case 172: /* term */
+ case 173: /* expr */
{
-sqlite3ExprDelete(pParse->db, (yypminor->yy346).pExpr);
+sqlite3ExprDelete(pParse->db, (yypminor->yy342).pExpr);
}
break;
- case 179: /* idxlist_opt */
- case 188: /* idxlist */
- case 200: /* selcollist */
- case 203: /* groupby_opt */
- case 205: /* orderby_opt */
- case 208: /* nexprlist */
- case 209: /* exprlist */
- case 210: /* sclp */
- case 220: /* sortlist */
- case 221: /* setlist */
- case 228: /* case_exprlist */
+ case 177: /* eidlist_opt */
+ case 186: /* sortlist */
+ case 187: /* eidlist */
+ case 199: /* selcollist */
+ case 202: /* groupby_opt */
+ case 204: /* orderby_opt */
+ case 207: /* nexprlist */
+ case 208: /* exprlist */
+ case 209: /* sclp */
+ case 218: /* setlist */
+ case 225: /* case_exprlist */
{
-sqlite3ExprListDelete(pParse->db, (yypminor->yy14));
+sqlite3ExprListDelete(pParse->db, (yypminor->yy442));
}
break;
- case 194: /* fullname */
- case 201: /* from */
- case 212: /* seltablist */
- case 213: /* stl_prefix */
+ case 193: /* fullname */
+ case 200: /* from */
+ case 211: /* seltablist */
+ case 212: /* stl_prefix */
{
-sqlite3SrcListDelete(pParse->db, (yypminor->yy65));
+sqlite3SrcListDelete(pParse->db, (yypminor->yy347));
}
break;
- case 197: /* with */
- case 252: /* wqlist */
+ case 196: /* with */
+ case 249: /* wqlist */
{
-sqlite3WithDelete(pParse->db, (yypminor->yy59));
+sqlite3WithDelete(pParse->db, (yypminor->yy331));
}
break;
- case 202: /* where_opt */
- case 204: /* having_opt */
- case 216: /* on_opt */
- case 227: /* case_operand */
- case 229: /* case_else */
- case 238: /* when_clause */
- case 243: /* key_opt */
+ case 201: /* where_opt */
+ case 203: /* having_opt */
+ case 215: /* on_opt */
+ case 224: /* case_operand */
+ case 226: /* case_else */
+ case 235: /* when_clause */
+ case 240: /* key_opt */
{
-sqlite3ExprDelete(pParse->db, (yypminor->yy132));
+sqlite3ExprDelete(pParse->db, (yypminor->yy122));
}
break;
- case 217: /* using_opt */
- case 219: /* idlist */
- case 223: /* inscollist_opt */
+ case 216: /* using_opt */
+ case 217: /* idlist */
+ case 220: /* idlist_opt */
{
-sqlite3IdListDelete(pParse->db, (yypminor->yy408));
+sqlite3IdListDelete(pParse->db, (yypminor->yy180));
}
break;
- case 234: /* trigger_cmd_list */
- case 239: /* trigger_cmd */
+ case 231: /* trigger_cmd_list */
+ case 236: /* trigger_cmd */
{
-sqlite3DeleteTriggerStep(pParse->db, (yypminor->yy473));
+sqlite3DeleteTriggerStep(pParse->db, (yypminor->yy327));
}
break;
- case 236: /* trigger_event */
+ case 233: /* trigger_event */
{
-sqlite3IdListDelete(pParse->db, (yypminor->yy378).b);
+sqlite3IdListDelete(pParse->db, (yypminor->yy410).b);
}
break;
+/********* End destructor definitions *****************************************/
default: break; /* If no destructor action specified: do nothing */
}
}
@@ -124711,49 +132295,37 @@ sqlite3IdListDelete(pParse->db, (yypminor->yy378).b);
**
** If there is a destructor routine associated with the token which
** is popped from the stack, then call it.
-**
-** Return the major token number for the symbol popped.
*/
-static int yy_pop_parser_stack(yyParser *pParser){
- YYCODETYPE yymajor;
- yyStackEntry *yytos = &pParser->yystack[pParser->yyidx];
-
- /* There is no mechanism by which the parser stack can be popped below
- ** empty in SQLite. */
- if( NEVER(pParser->yyidx<0) ) return 0;
+static void yy_pop_parser_stack(yyParser *pParser){
+ yyStackEntry *yytos;
+ assert( pParser->yyidx>=0 );
+ yytos = &pParser->yystack[pParser->yyidx--];
#ifndef NDEBUG
- if( yyTraceFILE && pParser->yyidx>=0 ){
+ if( yyTraceFILE ){
fprintf(yyTraceFILE,"%sPopping %s\n",
yyTracePrompt,
yyTokenName[yytos->major]);
}
#endif
- yymajor = yytos->major;
- yy_destructor(pParser, yymajor, &yytos->minor);
- pParser->yyidx--;
- return yymajor;
+ yy_destructor(pParser, yytos->major, &yytos->minor);
}
/*
-** Deallocate and destroy a parser. Destructors are all called for
+** Deallocate and destroy a parser. Destructors are called for
** all stack elements before shutting the parser down.
**
-** Inputs:
-** <ul>
-** <li> A pointer to the parser. This should be a pointer
-** obtained from sqlite3ParserAlloc.
-** <li> A pointer to a function used to reclaim memory obtained
-** from malloc.
-** </ul>
+** If the YYPARSEFREENEVERNULL macro exists (for example because it
+** is defined in a %include section of the input grammar) then it is
+** assumed that the input pointer is never NULL.
*/
SQLITE_PRIVATE void sqlite3ParserFree(
void *p, /* The parser to be deleted */
void (*freeProc)(void*) /* Function used to reclaim memory */
){
yyParser *pParser = (yyParser*)p;
- /* In SQLite, we never try to destroy a parser that was not successfully
- ** created in the first place. */
- if( NEVER(pParser==0) ) return;
+#ifndef YYPARSEFREENEVERNULL
+ if( pParser==0 ) return;
+#endif
while( pParser->yyidx>=0 ) yy_pop_parser_stack(pParser);
#if YYSTACKDEPTH<=0
free(pParser->yystack);
@@ -124774,75 +132346,72 @@ SQLITE_PRIVATE int sqlite3ParserStackPeak(void *p){
/*
** Find the appropriate action for a parser given the terminal
** look-ahead token iLookAhead.
-**
-** If the look-ahead token is YYNOCODE, then check to see if the action is
-** independent of the look-ahead. If it is, return the action, otherwise
-** return YY_NO_ACTION.
*/
-static int yy_find_shift_action(
+static unsigned int yy_find_shift_action(
yyParser *pParser, /* The parser */
YYCODETYPE iLookAhead /* The look-ahead token */
){
int i;
int stateno = pParser->yystack[pParser->yyidx].stateno;
- if( stateno>YY_SHIFT_COUNT
- || (i = yy_shift_ofst[stateno])==YY_SHIFT_USE_DFLT ){
- return yy_default[stateno];
- }
- assert( iLookAhead!=YYNOCODE );
- i += iLookAhead;
- if( i<0 || i>=YY_ACTTAB_COUNT || yy_lookahead[i]!=iLookAhead ){
- if( iLookAhead>0 ){
+ if( stateno>=YY_MIN_REDUCE ) return stateno;
+ assert( stateno <= YY_SHIFT_COUNT );
+ do{
+ i = yy_shift_ofst[stateno];
+ if( i==YY_SHIFT_USE_DFLT ) return yy_default[stateno];
+ assert( iLookAhead!=YYNOCODE );
+ i += iLookAhead;
+ if( i<0 || i>=YY_ACTTAB_COUNT || yy_lookahead[i]!=iLookAhead ){
+ if( iLookAhead>0 ){
#ifdef YYFALLBACK
- YYCODETYPE iFallback; /* Fallback token */
- if( iLookAhead<sizeof(yyFallback)/sizeof(yyFallback[0])
- && (iFallback = yyFallback[iLookAhead])!=0 ){
+ YYCODETYPE iFallback; /* Fallback token */
+ if( iLookAhead<sizeof(yyFallback)/sizeof(yyFallback[0])
+ && (iFallback = yyFallback[iLookAhead])!=0 ){
#ifndef NDEBUG
- if( yyTraceFILE ){
- fprintf(yyTraceFILE, "%sFALLBACK %s => %s\n",
- yyTracePrompt, yyTokenName[iLookAhead], yyTokenName[iFallback]);
- }
+ if( yyTraceFILE ){
+ fprintf(yyTraceFILE, "%sFALLBACK %s => %s\n",
+ yyTracePrompt, yyTokenName[iLookAhead], yyTokenName[iFallback]);
+ }
#endif
- return yy_find_shift_action(pParser, iFallback);
- }
+ assert( yyFallback[iFallback]==0 ); /* Fallback loop must terminate */
+ iLookAhead = iFallback;
+ continue;
+ }
#endif
#ifdef YYWILDCARD
- {
- int j = i - iLookAhead + YYWILDCARD;
- if(
+ {
+ int j = i - iLookAhead + YYWILDCARD;
+ if(
#if YY_SHIFT_MIN+YYWILDCARD<0
- j>=0 &&
+ j>=0 &&
#endif
#if YY_SHIFT_MAX+YYWILDCARD>=YY_ACTTAB_COUNT
- j<YY_ACTTAB_COUNT &&
+ j<YY_ACTTAB_COUNT &&
#endif
- yy_lookahead[j]==YYWILDCARD
- ){
+ yy_lookahead[j]==YYWILDCARD
+ ){
#ifndef NDEBUG
- if( yyTraceFILE ){
- fprintf(yyTraceFILE, "%sWILDCARD %s => %s\n",
- yyTracePrompt, yyTokenName[iLookAhead], yyTokenName[YYWILDCARD]);
- }
+ if( yyTraceFILE ){
+ fprintf(yyTraceFILE, "%sWILDCARD %s => %s\n",
+ yyTracePrompt, yyTokenName[iLookAhead],
+ yyTokenName[YYWILDCARD]);
+ }
#endif /* NDEBUG */
- return yy_action[j];
+ return yy_action[j];
+ }
}
- }
#endif /* YYWILDCARD */
+ }
+ return yy_default[stateno];
+ }else{
+ return yy_action[i];
}
- return yy_default[stateno];
- }else{
- return yy_action[i];
- }
+ }while(1);
}
/*
** Find the appropriate action for a parser given the non-terminal
** look-ahead token iLookAhead.
-**
-** If the look-ahead token is YYNOCODE, then check to see if the action is
-** independent of the look-ahead. If it is, return the action, otherwise
-** return YY_NO_ACTION.
*/
static int yy_find_reduce_action(
int stateno, /* Current state number */
@@ -124874,7 +132443,7 @@ static int yy_find_reduce_action(
/*
** The following routine is called if the stack overflows.
*/
-static void yyStackOverflow(yyParser *yypParser, YYMINORTYPE *yypMinor){
+static void yyStackOverflow(yyParser *yypParser){
sqlite3ParserARG_FETCH;
yypParser->yyidx--;
#ifndef NDEBUG
@@ -124885,20 +132454,41 @@ static void yyStackOverflow(yyParser *yypParser, YYMINORTYPE *yypMinor){
while( yypParser->yyidx>=0 ) yy_pop_parser_stack(yypParser);
/* Here code is inserted which will execute if the parser
** stack every overflows */
+/******** Begin %stack_overflow code ******************************************/
- UNUSED_PARAMETER(yypMinor); /* Silence some compiler warnings */
sqlite3ErrorMsg(pParse, "parser stack overflow");
+/******** End %stack_overflow code ********************************************/
sqlite3ParserARG_STORE; /* Suppress warning about unused %extra_argument var */
}
/*
+** Print tracing information for a SHIFT action
+*/
+#ifndef NDEBUG
+static void yyTraceShift(yyParser *yypParser, int yyNewState){
+ if( yyTraceFILE ){
+ if( yyNewState<YYNSTATE ){
+ fprintf(yyTraceFILE,"%sShift '%s', go to state %d\n",
+ yyTracePrompt,yyTokenName[yypParser->yystack[yypParser->yyidx].major],
+ yyNewState);
+ }else{
+ fprintf(yyTraceFILE,"%sShift '%s'\n",
+ yyTracePrompt,yyTokenName[yypParser->yystack[yypParser->yyidx].major]);
+ }
+ }
+}
+#else
+# define yyTraceShift(X,Y)
+#endif
+
+/*
** Perform a shift action.
*/
static void yy_shift(
yyParser *yypParser, /* The parser to be shifted */
int yyNewState, /* The new state to shift in */
int yyMajor, /* The major token to shift in */
- YYMINORTYPE *yypMinor /* Pointer to the minor token to shift in */
+ sqlite3ParserTOKENTYPE yyMinor /* The minor token to shift in */
){
yyStackEntry *yytos;
yypParser->yyidx++;
@@ -124909,14 +132499,14 @@ static void yy_shift(
#endif
#if YYSTACKDEPTH>0
if( yypParser->yyidx>=YYSTACKDEPTH ){
- yyStackOverflow(yypParser, yypMinor);
+ yyStackOverflow(yypParser);
return;
}
#else
if( yypParser->yyidx>=yypParser->yystksz ){
yyGrowStack(yypParser);
if( yypParser->yyidx>=yypParser->yystksz ){
- yyStackOverflow(yypParser, yypMinor);
+ yyStackOverflow(yypParser);
return;
}
}
@@ -124924,17 +132514,8 @@ static void yy_shift(
yytos = &yypParser->yystack[yypParser->yyidx];
yytos->stateno = (YYACTIONTYPE)yyNewState;
yytos->major = (YYCODETYPE)yyMajor;
- yytos->minor = *yypMinor;
-#ifndef NDEBUG
- if( yyTraceFILE && yypParser->yyidx>0 ){
- int i;
- fprintf(yyTraceFILE,"%sShift %d\n",yyTracePrompt,yyNewState);
- fprintf(yyTraceFILE,"%sStack:",yyTracePrompt);
- for(i=1; i<=yypParser->yyidx; i++)
- fprintf(yyTraceFILE," %s",yyTokenName[yypParser->yystack[i].major]);
- fprintf(yyTraceFILE,"\n");
- }
-#endif
+ yytos->minor.yy0 = yyMinor;
+ yyTraceShift(yypParser, yyNewState);
}
/* The following table contains information about every rule that
@@ -124944,19 +132525,10 @@ static const struct {
YYCODETYPE lhs; /* Symbol on the left-hand side of the rule */
unsigned char nrhs; /* Number of right-hand side symbols in the rule */
} yyRuleInfo[] = {
- { 144, 1 },
- { 145, 2 },
- { 145, 1 },
- { 146, 1 },
- { 146, 3 },
- { 147, 0 },
{ 147, 1 },
{ 147, 3 },
{ 148, 1 },
{ 149, 3 },
- { 151, 0 },
- { 151, 1 },
- { 151, 2 },
{ 150, 0 },
{ 150, 1 },
{ 150, 1 },
@@ -124964,12 +132536,9 @@ static const struct {
{ 149, 2 },
{ 149, 2 },
{ 149, 2 },
- { 153, 1 },
- { 153, 0 },
{ 149, 2 },
{ 149, 3 },
{ 149, 5 },
- { 149, 2 },
{ 154, 6 },
{ 156, 1 },
{ 158, 0 },
@@ -124980,218 +132549,193 @@ static const struct {
{ 155, 2 },
{ 162, 0 },
{ 162, 2 },
- { 160, 3 },
- { 160, 1 },
- { 164, 3 },
- { 165, 1 },
- { 152, 1 },
- { 152, 1 },
- { 152, 1 },
+ { 164, 2 },
{ 166, 0 },
- { 166, 1 },
- { 168, 1 },
- { 168, 4 },
- { 168, 6 },
- { 169, 1 },
- { 169, 2 },
- { 170, 1 },
- { 170, 1 },
+ { 166, 4 },
+ { 166, 6 },
{ 167, 2 },
- { 167, 0 },
- { 173, 2 },
- { 173, 2 },
- { 173, 4 },
- { 173, 3 },
- { 173, 3 },
- { 173, 2 },
- { 173, 2 },
- { 173, 3 },
- { 173, 5 },
- { 173, 2 },
- { 173, 4 },
- { 173, 4 },
- { 173, 1 },
- { 173, 2 },
+ { 171, 2 },
+ { 171, 2 },
+ { 171, 4 },
+ { 171, 3 },
+ { 171, 3 },
+ { 171, 2 },
+ { 171, 3 },
+ { 171, 5 },
+ { 171, 2 },
+ { 171, 4 },
+ { 171, 4 },
+ { 171, 1 },
+ { 171, 2 },
+ { 176, 0 },
+ { 176, 1 },
{ 178, 0 },
- { 178, 1 },
- { 180, 0 },
+ { 178, 2 },
{ 180, 2 },
- { 182, 2 },
- { 182, 3 },
- { 182, 3 },
- { 182, 3 },
- { 183, 2 },
- { 183, 2 },
- { 183, 1 },
- { 183, 1 },
- { 183, 2 },
- { 181, 3 },
+ { 180, 3 },
+ { 180, 3 },
+ { 180, 3 },
{ 181, 2 },
- { 184, 0 },
- { 184, 2 },
- { 184, 2 },
+ { 181, 2 },
+ { 181, 1 },
+ { 181, 1 },
+ { 181, 2 },
+ { 179, 3 },
+ { 179, 2 },
+ { 182, 0 },
+ { 182, 2 },
+ { 182, 2 },
{ 161, 0 },
- { 161, 2 },
- { 185, 3 },
- { 185, 1 },
- { 186, 1 },
- { 186, 0 },
- { 187, 2 },
- { 187, 7 },
- { 187, 5 },
- { 187, 5 },
- { 187, 10 },
+ { 184, 1 },
+ { 185, 2 },
+ { 185, 7 },
+ { 185, 5 },
+ { 185, 5 },
+ { 185, 10 },
+ { 188, 0 },
+ { 174, 0 },
+ { 174, 3 },
{ 189, 0 },
- { 189, 1 },
- { 176, 0 },
- { 176, 3 },
- { 190, 0 },
- { 190, 2 },
- { 191, 1 },
- { 191, 1 },
- { 191, 1 },
+ { 189, 2 },
+ { 190, 1 },
+ { 190, 1 },
{ 149, 4 },
- { 193, 2 },
- { 193, 0 },
- { 149, 8 },
+ { 192, 2 },
+ { 192, 0 },
+ { 149, 9 },
{ 149, 4 },
{ 149, 1 },
{ 163, 2 },
- { 195, 1 },
- { 195, 3 },
+ { 194, 3 },
+ { 197, 1 },
+ { 197, 2 },
+ { 197, 1 },
+ { 195, 9 },
+ { 206, 4 },
+ { 206, 5 },
{ 198, 1 },
- { 198, 2 },
{ 198, 1 },
- { 196, 9 },
- { 196, 1 },
- { 207, 4 },
- { 207, 5 },
- { 199, 1 },
- { 199, 1 },
- { 199, 0 },
+ { 198, 0 },
+ { 209, 0 },
+ { 199, 3 },
+ { 199, 2 },
+ { 199, 4 },
{ 210, 2 },
{ 210, 0 },
- { 200, 3 },
+ { 200, 0 },
{ 200, 2 },
- { 200, 4 },
- { 211, 2 },
- { 211, 1 },
- { 211, 0 },
- { 201, 0 },
- { 201, 2 },
- { 213, 2 },
- { 213, 0 },
- { 212, 7 },
- { 212, 7 },
- { 212, 7 },
+ { 212, 2 },
+ { 212, 0 },
+ { 211, 7 },
+ { 211, 9 },
+ { 211, 7 },
+ { 211, 7 },
{ 159, 0 },
{ 159, 2 },
- { 194, 2 },
- { 214, 1 },
- { 214, 2 },
+ { 193, 2 },
+ { 213, 1 },
+ { 213, 2 },
+ { 213, 3 },
+ { 213, 4 },
+ { 215, 2 },
+ { 215, 0 },
+ { 214, 0 },
{ 214, 3 },
- { 214, 4 },
- { 216, 2 },
+ { 214, 2 },
+ { 216, 4 },
{ 216, 0 },
- { 215, 0 },
- { 215, 3 },
- { 215, 2 },
- { 217, 4 },
- { 217, 0 },
- { 205, 0 },
- { 205, 3 },
- { 220, 4 },
- { 220, 2 },
- { 177, 1 },
- { 177, 1 },
- { 177, 0 },
- { 203, 0 },
- { 203, 3 },
{ 204, 0 },
- { 204, 2 },
- { 206, 0 },
- { 206, 2 },
- { 206, 4 },
- { 206, 4 },
- { 149, 6 },
+ { 204, 3 },
+ { 186, 4 },
+ { 186, 2 },
+ { 175, 1 },
+ { 175, 1 },
+ { 175, 0 },
{ 202, 0 },
- { 202, 2 },
+ { 202, 3 },
+ { 203, 0 },
+ { 203, 2 },
+ { 205, 0 },
+ { 205, 2 },
+ { 205, 4 },
+ { 205, 4 },
+ { 149, 6 },
+ { 201, 0 },
+ { 201, 2 },
{ 149, 8 },
- { 221, 5 },
- { 221, 3 },
+ { 218, 5 },
+ { 218, 3 },
{ 149, 6 },
{ 149, 7 },
- { 222, 2 },
- { 222, 1 },
- { 223, 0 },
- { 223, 3 },
- { 219, 3 },
+ { 219, 2 },
{ 219, 1 },
- { 175, 1 },
- { 175, 3 },
- { 174, 1 },
- { 175, 1 },
- { 175, 1 },
- { 175, 3 },
- { 175, 5 },
- { 174, 1 },
- { 174, 1 },
- { 175, 1 },
- { 175, 3 },
- { 175, 6 },
- { 175, 5 },
- { 175, 4 },
- { 174, 1 },
- { 175, 3 },
- { 175, 3 },
- { 175, 3 },
- { 175, 3 },
- { 175, 3 },
- { 175, 3 },
- { 175, 3 },
- { 175, 3 },
- { 224, 1 },
- { 224, 2 },
- { 175, 3 },
- { 175, 5 },
- { 175, 2 },
- { 175, 3 },
- { 175, 3 },
- { 175, 4 },
- { 175, 2 },
- { 175, 2 },
- { 175, 2 },
- { 175, 2 },
- { 225, 1 },
- { 225, 2 },
- { 175, 5 },
- { 226, 1 },
+ { 220, 0 },
+ { 220, 3 },
+ { 217, 3 },
+ { 217, 1 },
+ { 173, 3 },
+ { 172, 1 },
+ { 173, 1 },
+ { 173, 1 },
+ { 173, 3 },
+ { 173, 5 },
+ { 172, 1 },
+ { 172, 1 },
+ { 173, 1 },
+ { 173, 3 },
+ { 173, 6 },
+ { 173, 5 },
+ { 173, 4 },
+ { 172, 1 },
+ { 173, 3 },
+ { 173, 3 },
+ { 173, 3 },
+ { 173, 3 },
+ { 173, 3 },
+ { 173, 3 },
+ { 173, 3 },
+ { 173, 3 },
+ { 221, 1 },
+ { 221, 2 },
+ { 173, 3 },
+ { 173, 5 },
+ { 173, 2 },
+ { 173, 3 },
+ { 173, 3 },
+ { 173, 4 },
+ { 173, 2 },
+ { 173, 2 },
+ { 173, 2 },
+ { 173, 2 },
+ { 222, 1 },
+ { 222, 2 },
+ { 173, 5 },
+ { 223, 1 },
+ { 223, 2 },
+ { 173, 5 },
+ { 173, 3 },
+ { 173, 5 },
+ { 173, 4 },
+ { 173, 4 },
+ { 173, 5 },
+ { 225, 5 },
+ { 225, 4 },
{ 226, 2 },
- { 175, 5 },
- { 175, 3 },
- { 175, 5 },
- { 175, 4 },
- { 175, 4 },
- { 175, 5 },
- { 228, 5 },
- { 228, 4 },
- { 229, 2 },
- { 229, 0 },
+ { 226, 0 },
+ { 224, 1 },
+ { 224, 0 },
+ { 208, 0 },
+ { 207, 3 },
+ { 207, 1 },
+ { 149, 12 },
{ 227, 1 },
{ 227, 0 },
- { 209, 1 },
- { 209, 0 },
- { 208, 3 },
- { 208, 1 },
- { 149, 12 },
- { 230, 1 },
- { 230, 0 },
- { 179, 0 },
- { 179, 3 },
- { 188, 5 },
- { 188, 3 },
- { 231, 0 },
- { 231, 2 },
+ { 177, 0 },
+ { 177, 3 },
+ { 187, 5 },
+ { 187, 3 },
+ { 228, 0 },
+ { 228, 2 },
{ 149, 4 },
{ 149, 1 },
{ 149, 2 },
@@ -125200,77 +132744,113 @@ static const struct {
{ 149, 6 },
{ 149, 5 },
{ 149, 6 },
+ { 169, 2 },
+ { 170, 2 },
+ { 149, 5 },
+ { 230, 11 },
{ 232, 1 },
{ 232, 1 },
- { 232, 1 },
- { 232, 1 },
- { 232, 1 },
- { 171, 2 },
- { 171, 1 },
- { 172, 2 },
- { 149, 5 },
- { 233, 11 },
- { 235, 1 },
- { 235, 1 },
- { 235, 2 },
+ { 232, 2 },
+ { 232, 0 },
+ { 233, 1 },
+ { 233, 1 },
+ { 233, 3 },
{ 235, 0 },
- { 236, 1 },
- { 236, 1 },
- { 236, 3 },
- { 237, 0 },
+ { 235, 2 },
+ { 231, 3 },
+ { 231, 2 },
{ 237, 3 },
- { 238, 0 },
+ { 238, 3 },
{ 238, 2 },
- { 234, 3 },
- { 234, 2 },
- { 240, 1 },
- { 240, 3 },
- { 241, 0 },
- { 241, 3 },
- { 241, 2 },
- { 239, 7 },
- { 239, 5 },
- { 239, 5 },
- { 239, 1 },
- { 175, 4 },
- { 175, 6 },
- { 192, 1 },
- { 192, 1 },
- { 192, 1 },
+ { 236, 7 },
+ { 236, 5 },
+ { 236, 5 },
+ { 236, 1 },
+ { 173, 4 },
+ { 173, 6 },
+ { 191, 1 },
+ { 191, 1 },
+ { 191, 1 },
{ 149, 4 },
{ 149, 6 },
{ 149, 3 },
- { 243, 0 },
- { 243, 2 },
- { 242, 1 },
- { 242, 0 },
+ { 240, 0 },
+ { 240, 2 },
{ 149, 1 },
{ 149, 3 },
{ 149, 1 },
{ 149, 3 },
{ 149, 6 },
- { 149, 6 },
- { 244, 1 },
- { 245, 0 },
- { 245, 1 },
+ { 149, 7 },
+ { 241, 1 },
{ 149, 1 },
{ 149, 4 },
- { 246, 8 },
+ { 243, 8 },
+ { 245, 0 },
+ { 246, 1 },
+ { 246, 3 },
{ 247, 1 },
- { 247, 3 },
+ { 196, 0 },
+ { 196, 2 },
+ { 196, 3 },
+ { 249, 6 },
+ { 249, 8 },
+ { 144, 1 },
+ { 145, 2 },
+ { 145, 1 },
+ { 146, 1 },
+ { 146, 3 },
+ { 147, 0 },
+ { 151, 0 },
+ { 151, 1 },
+ { 151, 2 },
+ { 153, 1 },
+ { 153, 0 },
+ { 149, 2 },
+ { 160, 4 },
+ { 160, 2 },
+ { 152, 1 },
+ { 152, 1 },
+ { 152, 1 },
+ { 166, 1 },
+ { 167, 1 },
+ { 168, 1 },
+ { 168, 1 },
+ { 165, 2 },
+ { 165, 0 },
+ { 171, 2 },
+ { 161, 2 },
+ { 183, 3 },
+ { 183, 1 },
+ { 184, 0 },
+ { 188, 1 },
+ { 190, 1 },
+ { 194, 1 },
+ { 195, 1 },
+ { 209, 2 },
+ { 210, 1 },
+ { 173, 1 },
+ { 208, 1 },
+ { 229, 1 },
+ { 229, 1 },
+ { 229, 1 },
+ { 229, 1 },
+ { 229, 1 },
+ { 169, 1 },
+ { 234, 0 },
+ { 234, 3 },
+ { 237, 1 },
+ { 238, 0 },
+ { 239, 1 },
+ { 239, 0 },
+ { 242, 0 },
+ { 242, 1 },
+ { 244, 1 },
+ { 244, 3 },
+ { 245, 2 },
{ 248, 0 },
+ { 248, 4 },
{ 248, 2 },
- { 249, 1 },
- { 249, 3 },
- { 250, 1 },
- { 251, 0 },
- { 251, 4 },
- { 251, 2 },
- { 197, 0 },
- { 197, 2 },
- { 197, 3 },
- { 252, 6 },
- { 252, 8 },
};
static void yy_accept(yyParser*); /* Forward Declaration */
@@ -125281,40 +132861,46 @@ static void yy_accept(yyParser*); /* Forward Declaration */
*/
static void yy_reduce(
yyParser *yypParser, /* The parser */
- int yyruleno /* Number of the rule by which to reduce */
+ unsigned int yyruleno /* Number of the rule by which to reduce */
){
int yygoto; /* The next state */
int yyact; /* The next action */
- YYMINORTYPE yygotominor; /* The LHS of the rule reduced */
yyStackEntry *yymsp; /* The top of the parser's stack */
int yysize; /* Amount to pop the stack */
sqlite3ParserARG_FETCH;
yymsp = &yypParser->yystack[yypParser->yyidx];
#ifndef NDEBUG
- if( yyTraceFILE && yyruleno>=0
- && yyruleno<(int)(sizeof(yyRuleName)/sizeof(yyRuleName[0])) ){
- fprintf(yyTraceFILE, "%sReduce [%s].\n", yyTracePrompt,
- yyRuleName[yyruleno]);
+ if( yyTraceFILE && yyruleno<(int)(sizeof(yyRuleName)/sizeof(yyRuleName[0])) ){
+ yysize = yyRuleInfo[yyruleno].nrhs;
+ fprintf(yyTraceFILE, "%sReduce [%s], go to state %d.\n", yyTracePrompt,
+ yyRuleName[yyruleno], yymsp[-yysize].stateno);
}
#endif /* NDEBUG */
- /* Silence complaints from purify about yygotominor being uninitialized
- ** in some cases when it is copied into the stack after the following
- ** switch. yygotominor is uninitialized when a rule reduces that does
- ** not set the value of its left-hand side nonterminal. Leaving the
- ** value of the nonterminal uninitialized is utterly harmless as long
- ** as the value is never used. So really the only thing this code
- ** accomplishes is to quieten purify.
- **
- ** 2007-01-16: The wireshark project (www.wireshark.org) reports that
- ** without this code, their parser segfaults. I'm not sure what there
- ** parser is doing to make this happen. This is the second bug report
- ** from wireshark this week. Clearly they are stressing Lemon in ways
- ** that it has not been previously stressed... (SQLite ticket #2172)
- */
- /*memset(&yygotominor, 0, sizeof(yygotominor));*/
- yygotominor = yyzerominor;
-
+ /* Check that the stack is large enough to grow by a single entry
+ ** if the RHS of the rule is empty. This ensures that there is room
+ ** enough on the stack to push the LHS value */
+ if( yyRuleInfo[yyruleno].nrhs==0 ){
+#ifdef YYTRACKMAXSTACKDEPTH
+ if( yypParser->yyidx>yypParser->yyidxMax ){
+ yypParser->yyidxMax = yypParser->yyidx;
+ }
+#endif
+#if YYSTACKDEPTH>0
+ if( yypParser->yyidx>=YYSTACKDEPTH-1 ){
+ yyStackOverflow(yypParser);
+ return;
+ }
+#else
+ if( yypParser->yyidx>=yypParser->yystksz-1 ){
+ yyGrowStack(yypParser);
+ if( yypParser->yyidx>=yypParser->yystksz-1 ){
+ yyStackOverflow(yypParser);
+ return;
+ }
+ }
+#endif
+ }
switch( yyruleno ){
/* Beginning here are the reduction cases. A typical example
@@ -125325,326 +132911,287 @@ static void yy_reduce(
** #line <lineno> <thisfile>
** break;
*/
- case 5: /* explain ::= */
-{ sqlite3BeginParse(pParse, 0); }
- break;
- case 6: /* explain ::= EXPLAIN */
-{ sqlite3BeginParse(pParse, 1); }
+/********** Begin reduce actions **********************************************/
+ YYMINORTYPE yylhsminor;
+ case 0: /* explain ::= EXPLAIN */
+{ pParse->explain = 1; }
break;
- case 7: /* explain ::= EXPLAIN QUERY PLAN */
-{ sqlite3BeginParse(pParse, 2); }
+ case 1: /* explain ::= EXPLAIN QUERY PLAN */
+{ pParse->explain = 2; }
break;
- case 8: /* cmdx ::= cmd */
+ case 2: /* cmdx ::= cmd */
{ sqlite3FinishCoding(pParse); }
break;
- case 9: /* cmd ::= BEGIN transtype trans_opt */
-{sqlite3BeginTransaction(pParse, yymsp[-1].minor.yy328);}
+ case 3: /* cmd ::= BEGIN transtype trans_opt */
+{sqlite3BeginTransaction(pParse, yymsp[-1].minor.yy392);}
break;
- case 13: /* transtype ::= */
-{yygotominor.yy328 = TK_DEFERRED;}
+ case 4: /* transtype ::= */
+{yymsp[1].minor.yy392 = TK_DEFERRED;}
break;
- case 14: /* transtype ::= DEFERRED */
- case 15: /* transtype ::= IMMEDIATE */ yytestcase(yyruleno==15);
- case 16: /* transtype ::= EXCLUSIVE */ yytestcase(yyruleno==16);
- case 115: /* multiselect_op ::= UNION */ yytestcase(yyruleno==115);
- case 117: /* multiselect_op ::= EXCEPT|INTERSECT */ yytestcase(yyruleno==117);
-{yygotominor.yy328 = yymsp[0].major;}
+ case 5: /* transtype ::= DEFERRED */
+ case 6: /* transtype ::= IMMEDIATE */ yytestcase(yyruleno==6);
+ case 7: /* transtype ::= EXCLUSIVE */ yytestcase(yyruleno==7);
+{yymsp[0].minor.yy392 = yymsp[0].major; /*A-overwrites-X*/}
break;
- case 17: /* cmd ::= COMMIT trans_opt */
- case 18: /* cmd ::= END trans_opt */ yytestcase(yyruleno==18);
+ case 8: /* cmd ::= COMMIT trans_opt */
+ case 9: /* cmd ::= END trans_opt */ yytestcase(yyruleno==9);
{sqlite3CommitTransaction(pParse);}
break;
- case 19: /* cmd ::= ROLLBACK trans_opt */
+ case 10: /* cmd ::= ROLLBACK trans_opt */
{sqlite3RollbackTransaction(pParse);}
break;
- case 22: /* cmd ::= SAVEPOINT nm */
+ case 11: /* cmd ::= SAVEPOINT nm */
{
sqlite3Savepoint(pParse, SAVEPOINT_BEGIN, &yymsp[0].minor.yy0);
}
break;
- case 23: /* cmd ::= RELEASE savepoint_opt nm */
+ case 12: /* cmd ::= RELEASE savepoint_opt nm */
{
sqlite3Savepoint(pParse, SAVEPOINT_RELEASE, &yymsp[0].minor.yy0);
}
break;
- case 24: /* cmd ::= ROLLBACK trans_opt TO savepoint_opt nm */
+ case 13: /* cmd ::= ROLLBACK trans_opt TO savepoint_opt nm */
{
sqlite3Savepoint(pParse, SAVEPOINT_ROLLBACK, &yymsp[0].minor.yy0);
}
break;
- case 26: /* create_table ::= createkw temp TABLE ifnotexists nm dbnm */
+ case 14: /* create_table ::= createkw temp TABLE ifnotexists nm dbnm */
{
- sqlite3StartTable(pParse,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0,yymsp[-4].minor.yy328,0,0,yymsp[-2].minor.yy328);
+ sqlite3StartTable(pParse,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0,yymsp[-4].minor.yy392,0,0,yymsp[-2].minor.yy392);
}
break;
- case 27: /* createkw ::= CREATE */
-{
- pParse->db->lookaside.bEnabled = 0;
- yygotominor.yy0 = yymsp[0].minor.yy0;
-}
+ case 15: /* createkw ::= CREATE */
+{disableLookaside(pParse);}
+ break;
+ case 16: /* ifnotexists ::= */
+ case 19: /* temp ::= */ yytestcase(yyruleno==19);
+ case 22: /* table_options ::= */ yytestcase(yyruleno==22);
+ case 42: /* autoinc ::= */ yytestcase(yyruleno==42);
+ case 57: /* init_deferred_pred_opt ::= */ yytestcase(yyruleno==57);
+ case 67: /* defer_subclause_opt ::= */ yytestcase(yyruleno==67);
+ case 76: /* ifexists ::= */ yytestcase(yyruleno==76);
+ case 90: /* distinct ::= */ yytestcase(yyruleno==90);
+ case 209: /* collate ::= */ yytestcase(yyruleno==209);
+{yymsp[1].minor.yy392 = 0;}
break;
- case 28: /* ifnotexists ::= */
- case 31: /* temp ::= */ yytestcase(yyruleno==31);
- case 68: /* autoinc ::= */ yytestcase(yyruleno==68);
- case 81: /* defer_subclause ::= NOT DEFERRABLE init_deferred_pred_opt */ yytestcase(yyruleno==81);
- case 83: /* init_deferred_pred_opt ::= */ yytestcase(yyruleno==83);
- case 85: /* init_deferred_pred_opt ::= INITIALLY IMMEDIATE */ yytestcase(yyruleno==85);
- case 97: /* defer_subclause_opt ::= */ yytestcase(yyruleno==97);
- case 108: /* ifexists ::= */ yytestcase(yyruleno==108);
- case 218: /* between_op ::= BETWEEN */ yytestcase(yyruleno==218);
- case 221: /* in_op ::= IN */ yytestcase(yyruleno==221);
-{yygotominor.yy328 = 0;}
+ case 17: /* ifnotexists ::= IF NOT EXISTS */
+{yymsp[-2].minor.yy392 = 1;}
break;
- case 29: /* ifnotexists ::= IF NOT EXISTS */
- case 30: /* temp ::= TEMP */ yytestcase(yyruleno==30);
- case 69: /* autoinc ::= AUTOINCR */ yytestcase(yyruleno==69);
- case 84: /* init_deferred_pred_opt ::= INITIALLY DEFERRED */ yytestcase(yyruleno==84);
- case 107: /* ifexists ::= IF EXISTS */ yytestcase(yyruleno==107);
- case 219: /* between_op ::= NOT BETWEEN */ yytestcase(yyruleno==219);
- case 222: /* in_op ::= NOT IN */ yytestcase(yyruleno==222);
-{yygotominor.yy328 = 1;}
+ case 18: /* temp ::= TEMP */
+ case 43: /* autoinc ::= AUTOINCR */ yytestcase(yyruleno==43);
+{yymsp[0].minor.yy392 = 1;}
break;
- case 32: /* create_table_args ::= LP columnlist conslist_opt RP table_options */
+ case 20: /* create_table_args ::= LP columnlist conslist_opt RP table_options */
{
- sqlite3EndTable(pParse,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0,yymsp[0].minor.yy186,0);
+ sqlite3EndTable(pParse,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0,yymsp[0].minor.yy392,0);
}
break;
- case 33: /* create_table_args ::= AS select */
+ case 21: /* create_table_args ::= AS select */
{
- sqlite3EndTable(pParse,0,0,0,yymsp[0].minor.yy3);
- sqlite3SelectDelete(pParse->db, yymsp[0].minor.yy3);
+ sqlite3EndTable(pParse,0,0,0,yymsp[0].minor.yy159);
+ sqlite3SelectDelete(pParse->db, yymsp[0].minor.yy159);
}
break;
- case 34: /* table_options ::= */
-{yygotominor.yy186 = 0;}
- break;
- case 35: /* table_options ::= WITHOUT nm */
+ case 23: /* table_options ::= WITHOUT nm */
{
if( yymsp[0].minor.yy0.n==5 && sqlite3_strnicmp(yymsp[0].minor.yy0.z,"rowid",5)==0 ){
- yygotominor.yy186 = TF_WithoutRowid;
+ yymsp[-1].minor.yy392 = TF_WithoutRowid | TF_NoVisibleRowid;
}else{
- yygotominor.yy186 = 0;
+ yymsp[-1].minor.yy392 = 0;
sqlite3ErrorMsg(pParse, "unknown table option: %.*s", yymsp[0].minor.yy0.n, yymsp[0].minor.yy0.z);
}
}
break;
- case 38: /* column ::= columnid type carglist */
-{
- yygotominor.yy0.z = yymsp[-2].minor.yy0.z;
- yygotominor.yy0.n = (int)(pParse->sLastToken.z-yymsp[-2].minor.yy0.z) + pParse->sLastToken.n;
-}
+ case 24: /* columnname ::= nm typetoken */
+{sqlite3AddColumn(pParse,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0);}
break;
- case 39: /* columnid ::= nm */
-{
- sqlite3AddColumn(pParse,&yymsp[0].minor.yy0);
- yygotominor.yy0 = yymsp[0].minor.yy0;
- pParse->constraintName.n = 0;
-}
+ case 25: /* typetoken ::= */
+ case 60: /* conslist_opt ::= */ yytestcase(yyruleno==60);
+ case 96: /* as ::= */ yytestcase(yyruleno==96);
+{yymsp[1].minor.yy0.n = 0; yymsp[1].minor.yy0.z = 0;}
break;
- case 40: /* nm ::= ID|INDEXED */
- case 41: /* nm ::= STRING */ yytestcase(yyruleno==41);
- case 42: /* nm ::= JOIN_KW */ yytestcase(yyruleno==42);
- case 45: /* typetoken ::= typename */ yytestcase(yyruleno==45);
- case 48: /* typename ::= ID|STRING */ yytestcase(yyruleno==48);
- case 130: /* as ::= AS nm */ yytestcase(yyruleno==130);
- case 131: /* as ::= ID|STRING */ yytestcase(yyruleno==131);
- case 141: /* dbnm ::= DOT nm */ yytestcase(yyruleno==141);
- case 150: /* indexed_opt ::= INDEXED BY nm */ yytestcase(yyruleno==150);
- case 247: /* collate ::= COLLATE ID|STRING */ yytestcase(yyruleno==247);
- case 256: /* nmnum ::= plus_num */ yytestcase(yyruleno==256);
- case 257: /* nmnum ::= nm */ yytestcase(yyruleno==257);
- case 258: /* nmnum ::= ON */ yytestcase(yyruleno==258);
- case 259: /* nmnum ::= DELETE */ yytestcase(yyruleno==259);
- case 260: /* nmnum ::= DEFAULT */ yytestcase(yyruleno==260);
- case 261: /* plus_num ::= PLUS INTEGER|FLOAT */ yytestcase(yyruleno==261);
- case 262: /* plus_num ::= INTEGER|FLOAT */ yytestcase(yyruleno==262);
- case 263: /* minus_num ::= MINUS INTEGER|FLOAT */ yytestcase(yyruleno==263);
- case 279: /* trnm ::= nm */ yytestcase(yyruleno==279);
-{yygotominor.yy0 = yymsp[0].minor.yy0;}
- break;
- case 44: /* type ::= typetoken */
-{sqlite3AddColumnType(pParse,&yymsp[0].minor.yy0);}
- break;
- case 46: /* typetoken ::= typename LP signed RP */
+ case 26: /* typetoken ::= typename LP signed RP */
{
- yygotominor.yy0.z = yymsp[-3].minor.yy0.z;
- yygotominor.yy0.n = (int)(&yymsp[0].minor.yy0.z[yymsp[0].minor.yy0.n] - yymsp[-3].minor.yy0.z);
+ yymsp[-3].minor.yy0.n = (int)(&yymsp[0].minor.yy0.z[yymsp[0].minor.yy0.n] - yymsp[-3].minor.yy0.z);
}
break;
- case 47: /* typetoken ::= typename LP signed COMMA signed RP */
+ case 27: /* typetoken ::= typename LP signed COMMA signed RP */
{
- yygotominor.yy0.z = yymsp[-5].minor.yy0.z;
- yygotominor.yy0.n = (int)(&yymsp[0].minor.yy0.z[yymsp[0].minor.yy0.n] - yymsp[-5].minor.yy0.z);
+ yymsp[-5].minor.yy0.n = (int)(&yymsp[0].minor.yy0.z[yymsp[0].minor.yy0.n] - yymsp[-5].minor.yy0.z);
}
break;
- case 49: /* typename ::= typename ID|STRING */
-{yygotominor.yy0.z=yymsp[-1].minor.yy0.z; yygotominor.yy0.n=yymsp[0].minor.yy0.n+(int)(yymsp[0].minor.yy0.z-yymsp[-1].minor.yy0.z);}
+ case 28: /* typename ::= typename ID|STRING */
+{yymsp[-1].minor.yy0.n=yymsp[0].minor.yy0.n+(int)(yymsp[0].minor.yy0.z-yymsp[-1].minor.yy0.z);}
break;
- case 54: /* ccons ::= CONSTRAINT nm */
- case 92: /* tcons ::= CONSTRAINT nm */ yytestcase(yyruleno==92);
+ case 29: /* ccons ::= CONSTRAINT nm */
+ case 62: /* tcons ::= CONSTRAINT nm */ yytestcase(yyruleno==62);
{pParse->constraintName = yymsp[0].minor.yy0;}
break;
- case 55: /* ccons ::= DEFAULT term */
- case 57: /* ccons ::= DEFAULT PLUS term */ yytestcase(yyruleno==57);
-{sqlite3AddDefaultValue(pParse,&yymsp[0].minor.yy346);}
+ case 30: /* ccons ::= DEFAULT term */
+ case 32: /* ccons ::= DEFAULT PLUS term */ yytestcase(yyruleno==32);
+{sqlite3AddDefaultValue(pParse,&yymsp[0].minor.yy342);}
break;
- case 56: /* ccons ::= DEFAULT LP expr RP */
-{sqlite3AddDefaultValue(pParse,&yymsp[-1].minor.yy346);}
+ case 31: /* ccons ::= DEFAULT LP expr RP */
+{sqlite3AddDefaultValue(pParse,&yymsp[-1].minor.yy342);}
break;
- case 58: /* ccons ::= DEFAULT MINUS term */
+ case 33: /* ccons ::= DEFAULT MINUS term */
{
ExprSpan v;
- v.pExpr = sqlite3PExpr(pParse, TK_UMINUS, yymsp[0].minor.yy346.pExpr, 0, 0);
+ v.pExpr = sqlite3PExpr(pParse, TK_UMINUS, yymsp[0].minor.yy342.pExpr, 0, 0);
v.zStart = yymsp[-1].minor.yy0.z;
- v.zEnd = yymsp[0].minor.yy346.zEnd;
+ v.zEnd = yymsp[0].minor.yy342.zEnd;
sqlite3AddDefaultValue(pParse,&v);
}
break;
- case 59: /* ccons ::= DEFAULT ID|INDEXED */
+ case 34: /* ccons ::= DEFAULT ID|INDEXED */
{
ExprSpan v;
- spanExpr(&v, pParse, TK_STRING, &yymsp[0].minor.yy0);
+ spanExpr(&v, pParse, TK_STRING, yymsp[0].minor.yy0);
sqlite3AddDefaultValue(pParse,&v);
}
break;
- case 61: /* ccons ::= NOT NULL onconf */
-{sqlite3AddNotNull(pParse, yymsp[0].minor.yy328);}
+ case 35: /* ccons ::= NOT NULL onconf */
+{sqlite3AddNotNull(pParse, yymsp[0].minor.yy392);}
break;
- case 62: /* ccons ::= PRIMARY KEY sortorder onconf autoinc */
-{sqlite3AddPrimaryKey(pParse,0,yymsp[-1].minor.yy328,yymsp[0].minor.yy328,yymsp[-2].minor.yy328);}
+ case 36: /* ccons ::= PRIMARY KEY sortorder onconf autoinc */
+{sqlite3AddPrimaryKey(pParse,0,yymsp[-1].minor.yy392,yymsp[0].minor.yy392,yymsp[-2].minor.yy392);}
break;
- case 63: /* ccons ::= UNIQUE onconf */
-{sqlite3CreateIndex(pParse,0,0,0,0,yymsp[0].minor.yy328,0,0,0,0);}
+ case 37: /* ccons ::= UNIQUE onconf */
+{sqlite3CreateIndex(pParse,0,0,0,0,yymsp[0].minor.yy392,0,0,0,0);}
break;
- case 64: /* ccons ::= CHECK LP expr RP */
-{sqlite3AddCheckConstraint(pParse,yymsp[-1].minor.yy346.pExpr);}
+ case 38: /* ccons ::= CHECK LP expr RP */
+{sqlite3AddCheckConstraint(pParse,yymsp[-1].minor.yy342.pExpr);}
break;
- case 65: /* ccons ::= REFERENCES nm idxlist_opt refargs */
-{sqlite3CreateForeignKey(pParse,0,&yymsp[-2].minor.yy0,yymsp[-1].minor.yy14,yymsp[0].minor.yy328);}
+ case 39: /* ccons ::= REFERENCES nm eidlist_opt refargs */
+{sqlite3CreateForeignKey(pParse,0,&yymsp[-2].minor.yy0,yymsp[-1].minor.yy442,yymsp[0].minor.yy392);}
break;
- case 66: /* ccons ::= defer_subclause */
-{sqlite3DeferForeignKey(pParse,yymsp[0].minor.yy328);}
+ case 40: /* ccons ::= defer_subclause */
+{sqlite3DeferForeignKey(pParse,yymsp[0].minor.yy392);}
break;
- case 67: /* ccons ::= COLLATE ID|STRING */
+ case 41: /* ccons ::= COLLATE ID|STRING */
{sqlite3AddCollateType(pParse, &yymsp[0].minor.yy0);}
break;
- case 70: /* refargs ::= */
-{ yygotominor.yy328 = OE_None*0x0101; /* EV: R-19803-45884 */}
+ case 44: /* refargs ::= */
+{ yymsp[1].minor.yy392 = OE_None*0x0101; /* EV: R-19803-45884 */}
+ break;
+ case 45: /* refargs ::= refargs refarg */
+{ yymsp[-1].minor.yy392 = (yymsp[-1].minor.yy392 & ~yymsp[0].minor.yy207.mask) | yymsp[0].minor.yy207.value; }
break;
- case 71: /* refargs ::= refargs refarg */
-{ yygotominor.yy328 = (yymsp[-1].minor.yy328 & ~yymsp[0].minor.yy429.mask) | yymsp[0].minor.yy429.value; }
+ case 46: /* refarg ::= MATCH nm */
+{ yymsp[-1].minor.yy207.value = 0; yymsp[-1].minor.yy207.mask = 0x000000; }
break;
- case 72: /* refarg ::= MATCH nm */
- case 73: /* refarg ::= ON INSERT refact */ yytestcase(yyruleno==73);
-{ yygotominor.yy429.value = 0; yygotominor.yy429.mask = 0x000000; }
+ case 47: /* refarg ::= ON INSERT refact */
+{ yymsp[-2].minor.yy207.value = 0; yymsp[-2].minor.yy207.mask = 0x000000; }
break;
- case 74: /* refarg ::= ON DELETE refact */
-{ yygotominor.yy429.value = yymsp[0].minor.yy328; yygotominor.yy429.mask = 0x0000ff; }
+ case 48: /* refarg ::= ON DELETE refact */
+{ yymsp[-2].minor.yy207.value = yymsp[0].minor.yy392; yymsp[-2].minor.yy207.mask = 0x0000ff; }
break;
- case 75: /* refarg ::= ON UPDATE refact */
-{ yygotominor.yy429.value = yymsp[0].minor.yy328<<8; yygotominor.yy429.mask = 0x00ff00; }
+ case 49: /* refarg ::= ON UPDATE refact */
+{ yymsp[-2].minor.yy207.value = yymsp[0].minor.yy392<<8; yymsp[-2].minor.yy207.mask = 0x00ff00; }
break;
- case 76: /* refact ::= SET NULL */
-{ yygotominor.yy328 = OE_SetNull; /* EV: R-33326-45252 */}
+ case 50: /* refact ::= SET NULL */
+{ yymsp[-1].minor.yy392 = OE_SetNull; /* EV: R-33326-45252 */}
break;
- case 77: /* refact ::= SET DEFAULT */
-{ yygotominor.yy328 = OE_SetDflt; /* EV: R-33326-45252 */}
+ case 51: /* refact ::= SET DEFAULT */
+{ yymsp[-1].minor.yy392 = OE_SetDflt; /* EV: R-33326-45252 */}
break;
- case 78: /* refact ::= CASCADE */
-{ yygotominor.yy328 = OE_Cascade; /* EV: R-33326-45252 */}
+ case 52: /* refact ::= CASCADE */
+{ yymsp[0].minor.yy392 = OE_Cascade; /* EV: R-33326-45252 */}
break;
- case 79: /* refact ::= RESTRICT */
-{ yygotominor.yy328 = OE_Restrict; /* EV: R-33326-45252 */}
+ case 53: /* refact ::= RESTRICT */
+{ yymsp[0].minor.yy392 = OE_Restrict; /* EV: R-33326-45252 */}
break;
- case 80: /* refact ::= NO ACTION */
-{ yygotominor.yy328 = OE_None; /* EV: R-33326-45252 */}
+ case 54: /* refact ::= NO ACTION */
+{ yymsp[-1].minor.yy392 = OE_None; /* EV: R-33326-45252 */}
break;
- case 82: /* defer_subclause ::= DEFERRABLE init_deferred_pred_opt */
- case 98: /* defer_subclause_opt ::= defer_subclause */ yytestcase(yyruleno==98);
- case 100: /* onconf ::= ON CONFLICT resolvetype */ yytestcase(yyruleno==100);
- case 103: /* resolvetype ::= raisetype */ yytestcase(yyruleno==103);
-{yygotominor.yy328 = yymsp[0].minor.yy328;}
+ case 55: /* defer_subclause ::= NOT DEFERRABLE init_deferred_pred_opt */
+{yymsp[-2].minor.yy392 = 0;}
break;
- case 86: /* conslist_opt ::= */
-{yygotominor.yy0.n = 0; yygotominor.yy0.z = 0;}
+ case 56: /* defer_subclause ::= DEFERRABLE init_deferred_pred_opt */
+ case 71: /* orconf ::= OR resolvetype */ yytestcase(yyruleno==71);
+ case 142: /* insert_cmd ::= INSERT orconf */ yytestcase(yyruleno==142);
+{yymsp[-1].minor.yy392 = yymsp[0].minor.yy392;}
break;
- case 87: /* conslist_opt ::= COMMA conslist */
-{yygotominor.yy0 = yymsp[-1].minor.yy0;}
+ case 58: /* init_deferred_pred_opt ::= INITIALLY DEFERRED */
+ case 75: /* ifexists ::= IF EXISTS */ yytestcase(yyruleno==75);
+ case 183: /* between_op ::= NOT BETWEEN */ yytestcase(yyruleno==183);
+ case 186: /* in_op ::= NOT IN */ yytestcase(yyruleno==186);
+ case 210: /* collate ::= COLLATE ID|STRING */ yytestcase(yyruleno==210);
+{yymsp[-1].minor.yy392 = 1;}
break;
- case 90: /* tconscomma ::= COMMA */
+ case 59: /* init_deferred_pred_opt ::= INITIALLY IMMEDIATE */
+{yymsp[-1].minor.yy392 = 0;}
+ break;
+ case 61: /* tconscomma ::= COMMA */
{pParse->constraintName.n = 0;}
break;
- case 93: /* tcons ::= PRIMARY KEY LP idxlist autoinc RP onconf */
-{sqlite3AddPrimaryKey(pParse,yymsp[-3].minor.yy14,yymsp[0].minor.yy328,yymsp[-2].minor.yy328,0);}
+ case 63: /* tcons ::= PRIMARY KEY LP sortlist autoinc RP onconf */
+{sqlite3AddPrimaryKey(pParse,yymsp[-3].minor.yy442,yymsp[0].minor.yy392,yymsp[-2].minor.yy392,0);}
break;
- case 94: /* tcons ::= UNIQUE LP idxlist RP onconf */
-{sqlite3CreateIndex(pParse,0,0,0,yymsp[-2].minor.yy14,yymsp[0].minor.yy328,0,0,0,0);}
+ case 64: /* tcons ::= UNIQUE LP sortlist RP onconf */
+{sqlite3CreateIndex(pParse,0,0,0,yymsp[-2].minor.yy442,yymsp[0].minor.yy392,0,0,0,0);}
break;
- case 95: /* tcons ::= CHECK LP expr RP onconf */
-{sqlite3AddCheckConstraint(pParse,yymsp[-2].minor.yy346.pExpr);}
+ case 65: /* tcons ::= CHECK LP expr RP onconf */
+{sqlite3AddCheckConstraint(pParse,yymsp[-2].minor.yy342.pExpr);}
break;
- case 96: /* tcons ::= FOREIGN KEY LP idxlist RP REFERENCES nm idxlist_opt refargs defer_subclause_opt */
+ case 66: /* tcons ::= FOREIGN KEY LP eidlist RP REFERENCES nm eidlist_opt refargs defer_subclause_opt */
{
- sqlite3CreateForeignKey(pParse, yymsp[-6].minor.yy14, &yymsp[-3].minor.yy0, yymsp[-2].minor.yy14, yymsp[-1].minor.yy328);
- sqlite3DeferForeignKey(pParse, yymsp[0].minor.yy328);
+ sqlite3CreateForeignKey(pParse, yymsp[-6].minor.yy442, &yymsp[-3].minor.yy0, yymsp[-2].minor.yy442, yymsp[-1].minor.yy392);
+ sqlite3DeferForeignKey(pParse, yymsp[0].minor.yy392);
}
break;
- case 99: /* onconf ::= */
-{yygotominor.yy328 = OE_Default;}
- break;
- case 101: /* orconf ::= */
-{yygotominor.yy186 = OE_Default;}
+ case 68: /* onconf ::= */
+ case 70: /* orconf ::= */ yytestcase(yyruleno==70);
+{yymsp[1].minor.yy392 = OE_Default;}
break;
- case 102: /* orconf ::= OR resolvetype */
-{yygotominor.yy186 = (u8)yymsp[0].minor.yy328;}
+ case 69: /* onconf ::= ON CONFLICT resolvetype */
+{yymsp[-2].minor.yy392 = yymsp[0].minor.yy392;}
break;
- case 104: /* resolvetype ::= IGNORE */
-{yygotominor.yy328 = OE_Ignore;}
+ case 72: /* resolvetype ::= IGNORE */
+{yymsp[0].minor.yy392 = OE_Ignore;}
break;
- case 105: /* resolvetype ::= REPLACE */
-{yygotominor.yy328 = OE_Replace;}
+ case 73: /* resolvetype ::= REPLACE */
+ case 143: /* insert_cmd ::= REPLACE */ yytestcase(yyruleno==143);
+{yymsp[0].minor.yy392 = OE_Replace;}
break;
- case 106: /* cmd ::= DROP TABLE ifexists fullname */
+ case 74: /* cmd ::= DROP TABLE ifexists fullname */
{
- sqlite3DropTable(pParse, yymsp[0].minor.yy65, 0, yymsp[-1].minor.yy328);
+ sqlite3DropTable(pParse, yymsp[0].minor.yy347, 0, yymsp[-1].minor.yy392);
}
break;
- case 109: /* cmd ::= createkw temp VIEW ifnotexists nm dbnm AS select */
+ case 77: /* cmd ::= createkw temp VIEW ifnotexists nm dbnm eidlist_opt AS select */
{
- sqlite3CreateView(pParse, &yymsp[-7].minor.yy0, &yymsp[-3].minor.yy0, &yymsp[-2].minor.yy0, yymsp[0].minor.yy3, yymsp[-6].minor.yy328, yymsp[-4].minor.yy328);
+ sqlite3CreateView(pParse, &yymsp[-8].minor.yy0, &yymsp[-4].minor.yy0, &yymsp[-3].minor.yy0, yymsp[-2].minor.yy442, yymsp[0].minor.yy159, yymsp[-7].minor.yy392, yymsp[-5].minor.yy392);
}
break;
- case 110: /* cmd ::= DROP VIEW ifexists fullname */
+ case 78: /* cmd ::= DROP VIEW ifexists fullname */
{
- sqlite3DropTable(pParse, yymsp[0].minor.yy65, 1, yymsp[-1].minor.yy328);
+ sqlite3DropTable(pParse, yymsp[0].minor.yy347, 1, yymsp[-1].minor.yy392);
}
break;
- case 111: /* cmd ::= select */
+ case 79: /* cmd ::= select */
{
SelectDest dest = {SRT_Output, 0, 0, 0, 0, 0};
- sqlite3Select(pParse, yymsp[0].minor.yy3, &dest);
- sqlite3SelectDelete(pParse->db, yymsp[0].minor.yy3);
+ sqlite3Select(pParse, yymsp[0].minor.yy159, &dest);
+ sqlite3SelectDelete(pParse->db, yymsp[0].minor.yy159);
}
break;
- case 112: /* select ::= with selectnowith */
+ case 80: /* select ::= with selectnowith */
{
- Select *p = yymsp[0].minor.yy3;
+ Select *p = yymsp[0].minor.yy159;
if( p ){
- p->pWith = yymsp[-1].minor.yy59;
+ p->pWith = yymsp[-1].minor.yy331;
parserDoubleLinkSelect(pParse, p);
}else{
- sqlite3WithDelete(pParse->db, yymsp[-1].minor.yy59);
+ sqlite3WithDelete(pParse->db, yymsp[-1].minor.yy331);
}
- yygotominor.yy3 = p;
+ yymsp[-1].minor.yy159 = p; /*A-overwrites-W*/
}
break;
- case 113: /* selectnowith ::= oneselect */
- case 119: /* oneselect ::= values */ yytestcase(yyruleno==119);
-{yygotominor.yy3 = yymsp[0].minor.yy3;}
- break;
- case 114: /* selectnowith ::= selectnowith multiselect_op oneselect */
+ case 81: /* selectnowith ::= selectnowith multiselect_op oneselect */
{
- Select *pRhs = yymsp[0].minor.yy3;
+ Select *pRhs = yymsp[0].minor.yy159;
+ Select *pLhs = yymsp[-2].minor.yy159;
if( pRhs && pRhs->pPrior ){
SrcList *pFrom;
Token x;
@@ -125654,22 +133201,30 @@ static void yy_reduce(
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->op = (u8)yymsp[-1].minor.yy392;
+ pRhs->pPrior = pLhs;
+ if( ALWAYS(pLhs) ) pLhs->selFlags &= ~SF_MultiValue;
pRhs->selFlags &= ~SF_MultiValue;
- if( yymsp[-1].minor.yy328!=TK_ALL ) pParse->hasCompound = 1;
+ if( yymsp[-1].minor.yy392!=TK_ALL ) pParse->hasCompound = 1;
}else{
- sqlite3SelectDelete(pParse->db, yymsp[-2].minor.yy3);
+ sqlite3SelectDelete(pParse->db, pLhs);
}
- yygotominor.yy3 = pRhs;
+ yymsp[-2].minor.yy159 = pRhs;
}
break;
- case 116: /* multiselect_op ::= UNION ALL */
-{yygotominor.yy328 = TK_ALL;}
+ case 82: /* multiselect_op ::= UNION */
+ case 84: /* multiselect_op ::= EXCEPT|INTERSECT */ yytestcase(yyruleno==84);
+{yymsp[0].minor.yy392 = yymsp[0].major; /*A-overwrites-OP*/}
break;
- case 118: /* oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt orderby_opt limit_opt */
+ case 83: /* multiselect_op ::= UNION ALL */
+{yymsp[-1].minor.yy392 = TK_ALL;}
+ break;
+ case 85: /* oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt orderby_opt limit_opt */
{
- yygotominor.yy3 = sqlite3SelectNew(pParse,yymsp[-6].minor.yy14,yymsp[-5].minor.yy65,yymsp[-4].minor.yy132,yymsp[-3].minor.yy14,yymsp[-2].minor.yy132,yymsp[-1].minor.yy14,yymsp[-7].minor.yy381,yymsp[0].minor.yy476.pLimit,yymsp[0].minor.yy476.pOffset);
+#if SELECTTRACE_ENABLED
+ Token s = yymsp[-8].minor.yy0; /*A-overwrites-S*/
+#endif
+ yymsp[-8].minor.yy159 = sqlite3SelectNew(pParse,yymsp[-6].minor.yy442,yymsp[-5].minor.yy347,yymsp[-4].minor.yy122,yymsp[-3].minor.yy442,yymsp[-2].minor.yy122,yymsp[-1].minor.yy442,yymsp[-7].minor.yy392,yymsp[0].minor.yy64.pLimit,yymsp[0].minor.yy64.pOffset);
#if SELECTTRACE_ENABLED
/* Populate the Select.zSelName[] string that is used to help with
** query planner debugging, to differentiate between multiple Select
@@ -125680,445 +133235,449 @@ static void yy_reduce(
** comment to be the zSelName value. Otherwise, the label is #N where
** is an integer that is incremented with each SELECT statement seen.
*/
- if( yygotominor.yy3!=0 ){
- const char *z = yymsp[-8].minor.yy0.z+6;
+ if( yymsp[-8].minor.yy159!=0 ){
+ const char *z = s.z+6;
int i;
- sqlite3_snprintf(sizeof(yygotominor.yy3->zSelName), yygotominor.yy3->zSelName, "#%d",
+ sqlite3_snprintf(sizeof(yymsp[-8].minor.yy159->zSelName), yymsp[-8].minor.yy159->zSelName, "#%d",
++pParse->nSelect);
while( z[0]==' ' ) z++;
if( z[0]=='/' && z[1]=='*' ){
z += 2;
while( z[0]==' ' ) z++;
for(i=0; sqlite3Isalnum(z[i]); i++){}
- sqlite3_snprintf(sizeof(yygotominor.yy3->zSelName), yygotominor.yy3->zSelName, "%.*s", i, z);
+ sqlite3_snprintf(sizeof(yymsp[-8].minor.yy159->zSelName), yymsp[-8].minor.yy159->zSelName, "%.*s", i, z);
}
}
#endif /* SELECTRACE_ENABLED */
}
break;
- case 120: /* values ::= VALUES LP nexprlist RP */
+ case 86: /* values ::= VALUES LP nexprlist RP */
{
- yygotominor.yy3 = sqlite3SelectNew(pParse,yymsp[-1].minor.yy14,0,0,0,0,0,SF_Values,0,0);
+ yymsp[-3].minor.yy159 = sqlite3SelectNew(pParse,yymsp[-1].minor.yy442,0,0,0,0,0,SF_Values,0,0);
}
break;
- case 121: /* values ::= values COMMA LP exprlist RP */
+ case 87: /* values ::= values COMMA LP exprlist RP */
{
- 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);
+ Select *pRight, *pLeft = yymsp[-4].minor.yy159;
+ pRight = sqlite3SelectNew(pParse,yymsp[-1].minor.yy442,0,0,0,0,0,SF_Values|SF_MultiValue,0,0);
if( ALWAYS(pLeft) ) pLeft->selFlags &= ~SF_MultiValue;
if( pRight ){
pRight->op = TK_ALL;
- pLeft = yymsp[-4].minor.yy3;
pRight->pPrior = pLeft;
- yygotominor.yy3 = pRight;
+ yymsp[-4].minor.yy159 = pRight;
}else{
- yygotominor.yy3 = pLeft;
+ yymsp[-4].minor.yy159 = pLeft;
}
}
break;
- case 122: /* distinct ::= DISTINCT */
-{yygotominor.yy381 = SF_Distinct;}
+ case 88: /* distinct ::= DISTINCT */
+{yymsp[0].minor.yy392 = SF_Distinct;}
break;
- case 123: /* distinct ::= ALL */
- case 124: /* distinct ::= */ yytestcase(yyruleno==124);
-{yygotominor.yy381 = 0;}
+ case 89: /* distinct ::= ALL */
+{yymsp[0].minor.yy392 = SF_All;}
break;
- case 125: /* sclp ::= selcollist COMMA */
- case 243: /* idxlist_opt ::= LP idxlist RP */ yytestcase(yyruleno==243);
-{yygotominor.yy14 = yymsp[-1].minor.yy14;}
+ case 91: /* sclp ::= */
+ case 119: /* orderby_opt ::= */ yytestcase(yyruleno==119);
+ case 126: /* groupby_opt ::= */ yytestcase(yyruleno==126);
+ case 199: /* exprlist ::= */ yytestcase(yyruleno==199);
+ case 205: /* eidlist_opt ::= */ yytestcase(yyruleno==205);
+{yymsp[1].minor.yy442 = 0;}
break;
- case 126: /* sclp ::= */
- case 154: /* orderby_opt ::= */ yytestcase(yyruleno==154);
- case 161: /* groupby_opt ::= */ yytestcase(yyruleno==161);
- case 236: /* exprlist ::= */ yytestcase(yyruleno==236);
- case 242: /* idxlist_opt ::= */ yytestcase(yyruleno==242);
-{yygotominor.yy14 = 0;}
- break;
- case 127: /* selcollist ::= sclp expr as */
+ case 92: /* selcollist ::= sclp expr as */
{
- yygotominor.yy14 = sqlite3ExprListAppend(pParse, yymsp[-2].minor.yy14, yymsp[-1].minor.yy346.pExpr);
- if( yymsp[0].minor.yy0.n>0 ) sqlite3ExprListSetName(pParse, yygotominor.yy14, &yymsp[0].minor.yy0, 1);
- sqlite3ExprListSetSpan(pParse,yygotominor.yy14,&yymsp[-1].minor.yy346);
+ yymsp[-2].minor.yy442 = sqlite3ExprListAppend(pParse, yymsp[-2].minor.yy442, yymsp[-1].minor.yy342.pExpr);
+ if( yymsp[0].minor.yy0.n>0 ) sqlite3ExprListSetName(pParse, yymsp[-2].minor.yy442, &yymsp[0].minor.yy0, 1);
+ sqlite3ExprListSetSpan(pParse,yymsp[-2].minor.yy442,&yymsp[-1].minor.yy342);
}
break;
- case 128: /* selcollist ::= sclp STAR */
+ case 93: /* selcollist ::= sclp STAR */
{
- Expr *p = sqlite3Expr(pParse->db, TK_ALL, 0);
- yygotominor.yy14 = sqlite3ExprListAppend(pParse, yymsp[-1].minor.yy14, p);
+ Expr *p = sqlite3Expr(pParse->db, TK_ASTERISK, 0);
+ yymsp[-1].minor.yy442 = sqlite3ExprListAppend(pParse, yymsp[-1].minor.yy442, p);
}
break;
- case 129: /* selcollist ::= sclp nm DOT STAR */
+ case 94: /* selcollist ::= sclp nm DOT STAR */
{
- Expr *pRight = sqlite3PExpr(pParse, TK_ALL, 0, 0, &yymsp[0].minor.yy0);
+ Expr *pRight = sqlite3PExpr(pParse, TK_ASTERISK, 0, 0, &yymsp[0].minor.yy0);
Expr *pLeft = sqlite3PExpr(pParse, TK_ID, 0, 0, &yymsp[-2].minor.yy0);
Expr *pDot = sqlite3PExpr(pParse, TK_DOT, pLeft, pRight, 0);
- yygotominor.yy14 = sqlite3ExprListAppend(pParse,yymsp[-3].minor.yy14, pDot);
+ yymsp[-3].minor.yy442 = sqlite3ExprListAppend(pParse,yymsp[-3].minor.yy442, pDot);
}
break;
- case 132: /* as ::= */
-{yygotominor.yy0.n = 0;}
+ case 95: /* as ::= AS nm */
+ case 106: /* dbnm ::= DOT nm */ yytestcase(yyruleno==106);
+ case 219: /* plus_num ::= PLUS INTEGER|FLOAT */ yytestcase(yyruleno==219);
+ case 220: /* minus_num ::= MINUS INTEGER|FLOAT */ yytestcase(yyruleno==220);
+{yymsp[-1].minor.yy0 = yymsp[0].minor.yy0;}
break;
- case 133: /* from ::= */
-{yygotominor.yy65 = sqlite3DbMallocZero(pParse->db, sizeof(*yygotominor.yy65));}
+ case 97: /* from ::= */
+{yymsp[1].minor.yy347 = sqlite3DbMallocZero(pParse->db, sizeof(*yymsp[1].minor.yy347));}
break;
- case 134: /* from ::= FROM seltablist */
+ case 98: /* from ::= FROM seltablist */
{
- yygotominor.yy65 = yymsp[0].minor.yy65;
- sqlite3SrcListShiftJoinType(yygotominor.yy65);
+ yymsp[-1].minor.yy347 = yymsp[0].minor.yy347;
+ sqlite3SrcListShiftJoinType(yymsp[-1].minor.yy347);
}
break;
- case 135: /* stl_prefix ::= seltablist joinop */
+ case 99: /* stl_prefix ::= seltablist joinop */
{
- yygotominor.yy65 = yymsp[-1].minor.yy65;
- if( ALWAYS(yygotominor.yy65 && yygotominor.yy65->nSrc>0) ) yygotominor.yy65->a[yygotominor.yy65->nSrc-1].jointype = (u8)yymsp[0].minor.yy328;
+ if( ALWAYS(yymsp[-1].minor.yy347 && yymsp[-1].minor.yy347->nSrc>0) ) yymsp[-1].minor.yy347->a[yymsp[-1].minor.yy347->nSrc-1].fg.jointype = (u8)yymsp[0].minor.yy392;
}
break;
- case 136: /* stl_prefix ::= */
-{yygotominor.yy65 = 0;}
+ case 100: /* stl_prefix ::= */
+{yymsp[1].minor.yy347 = 0;}
break;
- case 137: /* seltablist ::= stl_prefix nm dbnm as indexed_opt on_opt using_opt */
+ case 101: /* seltablist ::= stl_prefix nm dbnm as indexed_opt on_opt using_opt */
{
- yygotominor.yy65 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-6].minor.yy65,&yymsp[-5].minor.yy0,&yymsp[-4].minor.yy0,&yymsp[-3].minor.yy0,0,yymsp[-1].minor.yy132,yymsp[0].minor.yy408);
- sqlite3SrcListIndexedBy(pParse, yygotominor.yy65, &yymsp[-2].minor.yy0);
+ yymsp[-6].minor.yy347 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-6].minor.yy347,&yymsp[-5].minor.yy0,&yymsp[-4].minor.yy0,&yymsp[-3].minor.yy0,0,yymsp[-1].minor.yy122,yymsp[0].minor.yy180);
+ sqlite3SrcListIndexedBy(pParse, yymsp[-6].minor.yy347, &yymsp[-2].minor.yy0);
}
break;
- case 138: /* seltablist ::= stl_prefix LP select RP as on_opt using_opt */
+ case 102: /* seltablist ::= stl_prefix nm dbnm LP exprlist RP as on_opt using_opt */
{
- yygotominor.yy65 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-6].minor.yy65,0,0,&yymsp[-2].minor.yy0,yymsp[-4].minor.yy3,yymsp[-1].minor.yy132,yymsp[0].minor.yy408);
+ yymsp[-8].minor.yy347 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-8].minor.yy347,&yymsp[-7].minor.yy0,&yymsp[-6].minor.yy0,&yymsp[-2].minor.yy0,0,yymsp[-1].minor.yy122,yymsp[0].minor.yy180);
+ sqlite3SrcListFuncArgs(pParse, yymsp[-8].minor.yy347, yymsp[-4].minor.yy442);
+}
+ break;
+ case 103: /* seltablist ::= stl_prefix LP select RP as on_opt using_opt */
+{
+ yymsp[-6].minor.yy347 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-6].minor.yy347,0,0,&yymsp[-2].minor.yy0,yymsp[-4].minor.yy159,yymsp[-1].minor.yy122,yymsp[0].minor.yy180);
}
break;
- case 139: /* seltablist ::= stl_prefix LP seltablist RP as on_opt using_opt */
+ case 104: /* seltablist ::= stl_prefix LP seltablist RP as on_opt using_opt */
{
- if( yymsp[-6].minor.yy65==0 && yymsp[-2].minor.yy0.n==0 && yymsp[-1].minor.yy132==0 && yymsp[0].minor.yy408==0 ){
- yygotominor.yy65 = yymsp[-4].minor.yy65;
- }else if( yymsp[-4].minor.yy65->nSrc==1 ){
- yygotominor.yy65 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-6].minor.yy65,0,0,&yymsp[-2].minor.yy0,0,yymsp[-1].minor.yy132,yymsp[0].minor.yy408);
- if( yygotominor.yy65 ){
- struct SrcList_item *pNew = &yygotominor.yy65->a[yygotominor.yy65->nSrc-1];
- struct SrcList_item *pOld = yymsp[-4].minor.yy65->a;
+ if( yymsp[-6].minor.yy347==0 && yymsp[-2].minor.yy0.n==0 && yymsp[-1].minor.yy122==0 && yymsp[0].minor.yy180==0 ){
+ yymsp[-6].minor.yy347 = yymsp[-4].minor.yy347;
+ }else if( yymsp[-4].minor.yy347->nSrc==1 ){
+ yymsp[-6].minor.yy347 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-6].minor.yy347,0,0,&yymsp[-2].minor.yy0,0,yymsp[-1].minor.yy122,yymsp[0].minor.yy180);
+ if( yymsp[-6].minor.yy347 ){
+ struct SrcList_item *pNew = &yymsp[-6].minor.yy347->a[yymsp[-6].minor.yy347->nSrc-1];
+ struct SrcList_item *pOld = yymsp[-4].minor.yy347->a;
pNew->zName = pOld->zName;
pNew->zDatabase = pOld->zDatabase;
pNew->pSelect = pOld->pSelect;
pOld->zName = pOld->zDatabase = 0;
pOld->pSelect = 0;
}
- sqlite3SrcListDelete(pParse->db, yymsp[-4].minor.yy65);
+ sqlite3SrcListDelete(pParse->db, yymsp[-4].minor.yy347);
}else{
Select *pSubquery;
- sqlite3SrcListShiftJoinType(yymsp[-4].minor.yy65);
- pSubquery = sqlite3SelectNew(pParse,0,yymsp[-4].minor.yy65,0,0,0,0,SF_NestedFrom,0,0);
- yygotominor.yy65 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-6].minor.yy65,0,0,&yymsp[-2].minor.yy0,pSubquery,yymsp[-1].minor.yy132,yymsp[0].minor.yy408);
+ sqlite3SrcListShiftJoinType(yymsp[-4].minor.yy347);
+ pSubquery = sqlite3SelectNew(pParse,0,yymsp[-4].minor.yy347,0,0,0,0,SF_NestedFrom,0,0);
+ yymsp[-6].minor.yy347 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-6].minor.yy347,0,0,&yymsp[-2].minor.yy0,pSubquery,yymsp[-1].minor.yy122,yymsp[0].minor.yy180);
}
}
break;
- case 140: /* dbnm ::= */
- case 149: /* indexed_opt ::= */ yytestcase(yyruleno==149);
-{yygotominor.yy0.z=0; yygotominor.yy0.n=0;}
+ case 105: /* dbnm ::= */
+ case 114: /* indexed_opt ::= */ yytestcase(yyruleno==114);
+{yymsp[1].minor.yy0.z=0; yymsp[1].minor.yy0.n=0;}
+ break;
+ case 107: /* fullname ::= nm dbnm */
+{yymsp[-1].minor.yy347 = sqlite3SrcListAppend(pParse->db,0,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0); /*A-overwrites-X*/}
break;
- case 142: /* fullname ::= nm dbnm */
-{yygotominor.yy65 = sqlite3SrcListAppend(pParse->db,0,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0);}
+ case 108: /* joinop ::= COMMA|JOIN */
+{ yymsp[0].minor.yy392 = JT_INNER; }
break;
- case 143: /* joinop ::= COMMA|JOIN */
-{ yygotominor.yy328 = JT_INNER; }
+ case 109: /* joinop ::= JOIN_KW JOIN */
+{yymsp[-1].minor.yy392 = sqlite3JoinType(pParse,&yymsp[-1].minor.yy0,0,0); /*X-overwrites-A*/}
break;
- case 144: /* joinop ::= JOIN_KW JOIN */
-{ yygotominor.yy328 = sqlite3JoinType(pParse,&yymsp[-1].minor.yy0,0,0); }
+ case 110: /* joinop ::= JOIN_KW nm JOIN */
+{yymsp[-2].minor.yy392 = sqlite3JoinType(pParse,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0,0); /*X-overwrites-A*/}
break;
- case 145: /* joinop ::= JOIN_KW nm JOIN */
-{ yygotominor.yy328 = sqlite3JoinType(pParse,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0,0); }
+ case 111: /* joinop ::= JOIN_KW nm nm JOIN */
+{yymsp[-3].minor.yy392 = sqlite3JoinType(pParse,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0);/*X-overwrites-A*/}
break;
- case 146: /* joinop ::= JOIN_KW nm nm JOIN */
-{ yygotominor.yy328 = sqlite3JoinType(pParse,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0); }
+ case 112: /* on_opt ::= ON expr */
+ case 129: /* having_opt ::= HAVING expr */ yytestcase(yyruleno==129);
+ case 136: /* where_opt ::= WHERE expr */ yytestcase(yyruleno==136);
+ case 195: /* case_else ::= ELSE expr */ yytestcase(yyruleno==195);
+{yymsp[-1].minor.yy122 = yymsp[0].minor.yy342.pExpr;}
break;
- case 147: /* on_opt ::= ON expr */
- case 164: /* having_opt ::= HAVING expr */ yytestcase(yyruleno==164);
- case 171: /* where_opt ::= WHERE expr */ yytestcase(yyruleno==171);
- case 231: /* case_else ::= ELSE expr */ yytestcase(yyruleno==231);
- case 233: /* case_operand ::= expr */ yytestcase(yyruleno==233);
-{yygotominor.yy132 = yymsp[0].minor.yy346.pExpr;}
+ case 113: /* on_opt ::= */
+ case 128: /* having_opt ::= */ yytestcase(yyruleno==128);
+ case 135: /* where_opt ::= */ yytestcase(yyruleno==135);
+ case 196: /* case_else ::= */ yytestcase(yyruleno==196);
+ case 198: /* case_operand ::= */ yytestcase(yyruleno==198);
+{yymsp[1].minor.yy122 = 0;}
break;
- case 148: /* on_opt ::= */
- case 163: /* having_opt ::= */ yytestcase(yyruleno==163);
- case 170: /* where_opt ::= */ yytestcase(yyruleno==170);
- case 232: /* case_else ::= */ yytestcase(yyruleno==232);
- case 234: /* case_operand ::= */ yytestcase(yyruleno==234);
-{yygotominor.yy132 = 0;}
+ case 115: /* indexed_opt ::= INDEXED BY nm */
+{yymsp[-2].minor.yy0 = yymsp[0].minor.yy0;}
break;
- case 151: /* indexed_opt ::= NOT INDEXED */
-{yygotominor.yy0.z=0; yygotominor.yy0.n=1;}
+ case 116: /* indexed_opt ::= NOT INDEXED */
+{yymsp[-1].minor.yy0.z=0; yymsp[-1].minor.yy0.n=1;}
break;
- case 152: /* using_opt ::= USING LP idlist RP */
- case 180: /* inscollist_opt ::= LP idlist RP */ yytestcase(yyruleno==180);
-{yygotominor.yy408 = yymsp[-1].minor.yy408;}
+ case 117: /* using_opt ::= USING LP idlist RP */
+{yymsp[-3].minor.yy180 = yymsp[-1].minor.yy180;}
break;
- case 153: /* using_opt ::= */
- case 179: /* inscollist_opt ::= */ yytestcase(yyruleno==179);
-{yygotominor.yy408 = 0;}
+ case 118: /* using_opt ::= */
+ case 144: /* idlist_opt ::= */ yytestcase(yyruleno==144);
+{yymsp[1].minor.yy180 = 0;}
break;
- case 155: /* orderby_opt ::= ORDER BY sortlist */
- case 162: /* groupby_opt ::= GROUP BY nexprlist */ yytestcase(yyruleno==162);
- case 235: /* exprlist ::= nexprlist */ yytestcase(yyruleno==235);
-{yygotominor.yy14 = yymsp[0].minor.yy14;}
+ case 120: /* orderby_opt ::= ORDER BY sortlist */
+ case 127: /* groupby_opt ::= GROUP BY nexprlist */ yytestcase(yyruleno==127);
+{yymsp[-2].minor.yy442 = yymsp[0].minor.yy442;}
break;
- case 156: /* sortlist ::= sortlist COMMA expr sortorder */
+ case 121: /* sortlist ::= sortlist COMMA expr sortorder */
{
- yygotominor.yy14 = sqlite3ExprListAppend(pParse,yymsp[-3].minor.yy14,yymsp[-1].minor.yy346.pExpr);
- if( yygotominor.yy14 ) yygotominor.yy14->a[yygotominor.yy14->nExpr-1].sortOrder = (u8)yymsp[0].minor.yy328;
+ yymsp[-3].minor.yy442 = sqlite3ExprListAppend(pParse,yymsp[-3].minor.yy442,yymsp[-1].minor.yy342.pExpr);
+ sqlite3ExprListSetSortOrder(yymsp[-3].minor.yy442,yymsp[0].minor.yy392);
}
break;
- case 157: /* sortlist ::= expr sortorder */
+ case 122: /* sortlist ::= expr sortorder */
{
- yygotominor.yy14 = sqlite3ExprListAppend(pParse,0,yymsp[-1].minor.yy346.pExpr);
- if( yygotominor.yy14 && ALWAYS(yygotominor.yy14->a) ) yygotominor.yy14->a[0].sortOrder = (u8)yymsp[0].minor.yy328;
+ yymsp[-1].minor.yy442 = sqlite3ExprListAppend(pParse,0,yymsp[-1].minor.yy342.pExpr); /*A-overwrites-Y*/
+ sqlite3ExprListSetSortOrder(yymsp[-1].minor.yy442,yymsp[0].minor.yy392);
}
break;
- case 158: /* sortorder ::= ASC */
- case 160: /* sortorder ::= */ yytestcase(yyruleno==160);
-{yygotominor.yy328 = SQLITE_SO_ASC;}
+ case 123: /* sortorder ::= ASC */
+{yymsp[0].minor.yy392 = SQLITE_SO_ASC;}
+ break;
+ case 124: /* sortorder ::= DESC */
+{yymsp[0].minor.yy392 = SQLITE_SO_DESC;}
break;
- case 159: /* sortorder ::= DESC */
-{yygotominor.yy328 = SQLITE_SO_DESC;}
+ case 125: /* sortorder ::= */
+{yymsp[1].minor.yy392 = SQLITE_SO_UNDEFINED;}
break;
- case 165: /* limit_opt ::= */
-{yygotominor.yy476.pLimit = 0; yygotominor.yy476.pOffset = 0;}
+ case 130: /* limit_opt ::= */
+{yymsp[1].minor.yy64.pLimit = 0; yymsp[1].minor.yy64.pOffset = 0;}
break;
- case 166: /* limit_opt ::= LIMIT expr */
-{yygotominor.yy476.pLimit = yymsp[0].minor.yy346.pExpr; yygotominor.yy476.pOffset = 0;}
+ case 131: /* limit_opt ::= LIMIT expr */
+{yymsp[-1].minor.yy64.pLimit = yymsp[0].minor.yy342.pExpr; yymsp[-1].minor.yy64.pOffset = 0;}
break;
- case 167: /* limit_opt ::= LIMIT expr OFFSET expr */
-{yygotominor.yy476.pLimit = yymsp[-2].minor.yy346.pExpr; yygotominor.yy476.pOffset = yymsp[0].minor.yy346.pExpr;}
+ case 132: /* limit_opt ::= LIMIT expr OFFSET expr */
+{yymsp[-3].minor.yy64.pLimit = yymsp[-2].minor.yy342.pExpr; yymsp[-3].minor.yy64.pOffset = yymsp[0].minor.yy342.pExpr;}
break;
- case 168: /* limit_opt ::= LIMIT expr COMMA expr */
-{yygotominor.yy476.pOffset = yymsp[-2].minor.yy346.pExpr; yygotominor.yy476.pLimit = yymsp[0].minor.yy346.pExpr;}
+ case 133: /* limit_opt ::= LIMIT expr COMMA expr */
+{yymsp[-3].minor.yy64.pOffset = yymsp[-2].minor.yy342.pExpr; yymsp[-3].minor.yy64.pLimit = yymsp[0].minor.yy342.pExpr;}
break;
- case 169: /* cmd ::= with DELETE FROM fullname indexed_opt where_opt */
+ case 134: /* cmd ::= with DELETE FROM fullname indexed_opt where_opt */
{
- sqlite3WithPush(pParse, yymsp[-5].minor.yy59, 1);
- sqlite3SrcListIndexedBy(pParse, yymsp[-2].minor.yy65, &yymsp[-1].minor.yy0);
- sqlite3DeleteFrom(pParse,yymsp[-2].minor.yy65,yymsp[0].minor.yy132);
+ sqlite3WithPush(pParse, yymsp[-5].minor.yy331, 1);
+ sqlite3SrcListIndexedBy(pParse, yymsp[-2].minor.yy347, &yymsp[-1].minor.yy0);
+ sqlite3DeleteFrom(pParse,yymsp[-2].minor.yy347,yymsp[0].minor.yy122);
}
break;
- case 172: /* cmd ::= with UPDATE orconf fullname indexed_opt SET setlist where_opt */
+ case 137: /* cmd ::= with UPDATE orconf fullname indexed_opt SET setlist where_opt */
{
- sqlite3WithPush(pParse, yymsp[-7].minor.yy59, 1);
- sqlite3SrcListIndexedBy(pParse, yymsp[-4].minor.yy65, &yymsp[-3].minor.yy0);
- sqlite3ExprListCheckLength(pParse,yymsp[-1].minor.yy14,"set list");
- sqlite3Update(pParse,yymsp[-4].minor.yy65,yymsp[-1].minor.yy14,yymsp[0].minor.yy132,yymsp[-5].minor.yy186);
+ sqlite3WithPush(pParse, yymsp[-7].minor.yy331, 1);
+ sqlite3SrcListIndexedBy(pParse, yymsp[-4].minor.yy347, &yymsp[-3].minor.yy0);
+ sqlite3ExprListCheckLength(pParse,yymsp[-1].minor.yy442,"set list");
+ sqlite3Update(pParse,yymsp[-4].minor.yy347,yymsp[-1].minor.yy442,yymsp[0].minor.yy122,yymsp[-5].minor.yy392);
}
break;
- case 173: /* setlist ::= setlist COMMA nm EQ expr */
+ case 138: /* setlist ::= setlist COMMA nm EQ expr */
{
- yygotominor.yy14 = sqlite3ExprListAppend(pParse, yymsp[-4].minor.yy14, yymsp[0].minor.yy346.pExpr);
- sqlite3ExprListSetName(pParse, yygotominor.yy14, &yymsp[-2].minor.yy0, 1);
+ yymsp[-4].minor.yy442 = sqlite3ExprListAppend(pParse, yymsp[-4].minor.yy442, yymsp[0].minor.yy342.pExpr);
+ sqlite3ExprListSetName(pParse, yymsp[-4].minor.yy442, &yymsp[-2].minor.yy0, 1);
}
break;
- case 174: /* setlist ::= nm EQ expr */
+ case 139: /* setlist ::= nm EQ expr */
{
- yygotominor.yy14 = sqlite3ExprListAppend(pParse, 0, yymsp[0].minor.yy346.pExpr);
- sqlite3ExprListSetName(pParse, yygotominor.yy14, &yymsp[-2].minor.yy0, 1);
+ yylhsminor.yy442 = sqlite3ExprListAppend(pParse, 0, yymsp[0].minor.yy342.pExpr);
+ sqlite3ExprListSetName(pParse, yylhsminor.yy442, &yymsp[-2].minor.yy0, 1);
}
+ yymsp[-2].minor.yy442 = yylhsminor.yy442;
break;
- case 175: /* cmd ::= with insert_cmd INTO fullname inscollist_opt select */
+ case 140: /* cmd ::= with insert_cmd INTO fullname idlist_opt select */
{
- sqlite3WithPush(pParse, yymsp[-5].minor.yy59, 1);
- sqlite3Insert(pParse, yymsp[-2].minor.yy65, yymsp[0].minor.yy3, yymsp[-1].minor.yy408, yymsp[-4].minor.yy186);
+ sqlite3WithPush(pParse, yymsp[-5].minor.yy331, 1);
+ sqlite3Insert(pParse, yymsp[-2].minor.yy347, yymsp[0].minor.yy159, yymsp[-1].minor.yy180, yymsp[-4].minor.yy392);
}
break;
- case 176: /* cmd ::= with insert_cmd INTO fullname inscollist_opt DEFAULT VALUES */
+ case 141: /* cmd ::= with insert_cmd INTO fullname idlist_opt DEFAULT VALUES */
{
- sqlite3WithPush(pParse, yymsp[-6].minor.yy59, 1);
- sqlite3Insert(pParse, yymsp[-3].minor.yy65, 0, yymsp[-2].minor.yy408, yymsp[-5].minor.yy186);
+ sqlite3WithPush(pParse, yymsp[-6].minor.yy331, 1);
+ sqlite3Insert(pParse, yymsp[-3].minor.yy347, 0, yymsp[-2].minor.yy180, yymsp[-5].minor.yy392);
}
break;
- case 177: /* insert_cmd ::= INSERT orconf */
-{yygotominor.yy186 = yymsp[0].minor.yy186;}
+ case 145: /* idlist_opt ::= LP idlist RP */
+{yymsp[-2].minor.yy180 = yymsp[-1].minor.yy180;}
break;
- case 178: /* insert_cmd ::= REPLACE */
-{yygotominor.yy186 = OE_Replace;}
+ case 146: /* idlist ::= idlist COMMA nm */
+{yymsp[-2].minor.yy180 = sqlite3IdListAppend(pParse->db,yymsp[-2].minor.yy180,&yymsp[0].minor.yy0);}
break;
- case 181: /* idlist ::= idlist COMMA nm */
-{yygotominor.yy408 = sqlite3IdListAppend(pParse->db,yymsp[-2].minor.yy408,&yymsp[0].minor.yy0);}
+ case 147: /* idlist ::= nm */
+{yymsp[0].minor.yy180 = sqlite3IdListAppend(pParse->db,0,&yymsp[0].minor.yy0); /*A-overwrites-Y*/}
break;
- case 182: /* idlist ::= nm */
-{yygotominor.yy408 = sqlite3IdListAppend(pParse->db,0,&yymsp[0].minor.yy0);}
+ case 148: /* expr ::= LP expr RP */
+{spanSet(&yymsp[-2].minor.yy342,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0); /*A-overwrites-B*/ yymsp[-2].minor.yy342.pExpr = yymsp[-1].minor.yy342.pExpr;}
break;
- case 183: /* expr ::= term */
-{yygotominor.yy346 = yymsp[0].minor.yy346;}
+ case 149: /* term ::= NULL */
+ case 154: /* term ::= INTEGER|FLOAT|BLOB */ yytestcase(yyruleno==154);
+ case 155: /* term ::= STRING */ yytestcase(yyruleno==155);
+{spanExpr(&yymsp[0].minor.yy342,pParse,yymsp[0].major,yymsp[0].minor.yy0);/*A-overwrites-X*/}
break;
- case 184: /* expr ::= LP expr RP */
-{yygotominor.yy346.pExpr = yymsp[-1].minor.yy346.pExpr; spanSet(&yygotominor.yy346,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0);}
+ case 150: /* expr ::= ID|INDEXED */
+ case 151: /* expr ::= JOIN_KW */ yytestcase(yyruleno==151);
+{spanExpr(&yymsp[0].minor.yy342,pParse,TK_ID,yymsp[0].minor.yy0); /*A-overwrites-X*/}
break;
- case 185: /* term ::= NULL */
- case 190: /* term ::= INTEGER|FLOAT|BLOB */ yytestcase(yyruleno==190);
- case 191: /* term ::= STRING */ yytestcase(yyruleno==191);
-{spanExpr(&yygotominor.yy346, pParse, yymsp[0].major, &yymsp[0].minor.yy0);}
- break;
- case 186: /* expr ::= ID|INDEXED */
- case 187: /* expr ::= JOIN_KW */ yytestcase(yyruleno==187);
-{spanExpr(&yygotominor.yy346, pParse, TK_ID, &yymsp[0].minor.yy0);}
- break;
- case 188: /* expr ::= nm DOT nm */
+ case 152: /* expr ::= nm DOT nm */
{
Expr *temp1 = sqlite3PExpr(pParse, TK_ID, 0, 0, &yymsp[-2].minor.yy0);
Expr *temp2 = sqlite3PExpr(pParse, TK_ID, 0, 0, &yymsp[0].minor.yy0);
- yygotominor.yy346.pExpr = sqlite3PExpr(pParse, TK_DOT, temp1, temp2, 0);
- spanSet(&yygotominor.yy346,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0);
+ spanSet(&yymsp[-2].minor.yy342,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0); /*A-overwrites-X*/
+ yymsp[-2].minor.yy342.pExpr = sqlite3PExpr(pParse, TK_DOT, temp1, temp2, 0);
}
break;
- case 189: /* expr ::= nm DOT nm DOT nm */
+ case 153: /* expr ::= nm DOT nm DOT nm */
{
Expr *temp1 = sqlite3PExpr(pParse, TK_ID, 0, 0, &yymsp[-4].minor.yy0);
Expr *temp2 = sqlite3PExpr(pParse, TK_ID, 0, 0, &yymsp[-2].minor.yy0);
Expr *temp3 = sqlite3PExpr(pParse, TK_ID, 0, 0, &yymsp[0].minor.yy0);
Expr *temp4 = sqlite3PExpr(pParse, TK_DOT, temp2, temp3, 0);
- yygotominor.yy346.pExpr = sqlite3PExpr(pParse, TK_DOT, temp1, temp4, 0);
- spanSet(&yygotominor.yy346,&yymsp[-4].minor.yy0,&yymsp[0].minor.yy0);
+ spanSet(&yymsp[-4].minor.yy342,&yymsp[-4].minor.yy0,&yymsp[0].minor.yy0); /*A-overwrites-X*/
+ yymsp[-4].minor.yy342.pExpr = sqlite3PExpr(pParse, TK_DOT, temp1, temp4, 0);
}
break;
- case 192: /* expr ::= VARIABLE */
+ case 156: /* expr ::= VARIABLE */
{
- if( yymsp[0].minor.yy0.n>=2 && yymsp[0].minor.yy0.z[0]=='#' && sqlite3Isdigit(yymsp[0].minor.yy0.z[1]) ){
+ if( !(yymsp[0].minor.yy0.z[0]=='#' && sqlite3Isdigit(yymsp[0].minor.yy0.z[1])) ){
+ spanExpr(&yymsp[0].minor.yy342, pParse, TK_VARIABLE, yymsp[0].minor.yy0);
+ sqlite3ExprAssignVarNumber(pParse, yymsp[0].minor.yy342.pExpr);
+ }else{
/* When doing a nested parse, one can include terms in an expression
** that look like this: #1 #2 ... These terms refer to registers
** in the virtual machine. #N is the N-th register. */
+ Token t = yymsp[0].minor.yy0; /*A-overwrites-X*/
+ assert( t.n>=2 );
+ spanSet(&yymsp[0].minor.yy342, &t, &t);
if( pParse->nested==0 ){
- sqlite3ErrorMsg(pParse, "near \"%T\": syntax error", &yymsp[0].minor.yy0);
- yygotominor.yy346.pExpr = 0;
+ sqlite3ErrorMsg(pParse, "near \"%T\": syntax error", &t);
+ yymsp[0].minor.yy342.pExpr = 0;
}else{
- yygotominor.yy346.pExpr = sqlite3PExpr(pParse, TK_REGISTER, 0, 0, &yymsp[0].minor.yy0);
- if( yygotominor.yy346.pExpr ) sqlite3GetInt32(&yymsp[0].minor.yy0.z[1], &yygotominor.yy346.pExpr->iTable);
+ yymsp[0].minor.yy342.pExpr = sqlite3PExpr(pParse, TK_REGISTER, 0, 0, &t);
+ if( yymsp[0].minor.yy342.pExpr ) sqlite3GetInt32(&t.z[1], &yymsp[0].minor.yy342.pExpr->iTable);
}
- }else{
- spanExpr(&yygotominor.yy346, pParse, TK_VARIABLE, &yymsp[0].minor.yy0);
- sqlite3ExprAssignVarNumber(pParse, yygotominor.yy346.pExpr);
}
- spanSet(&yygotominor.yy346, &yymsp[0].minor.yy0, &yymsp[0].minor.yy0);
}
break;
- case 193: /* expr ::= expr COLLATE ID|STRING */
+ case 157: /* expr ::= expr COLLATE ID|STRING */
{
- 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];
+ yymsp[-2].minor.yy342.pExpr = sqlite3ExprAddCollateToken(pParse, yymsp[-2].minor.yy342.pExpr, &yymsp[0].minor.yy0, 1);
+ yymsp[-2].minor.yy342.zEnd = &yymsp[0].minor.yy0.z[yymsp[0].minor.yy0.n];
}
break;
- case 194: /* expr ::= CAST LP expr AS typetoken RP */
+ case 158: /* expr ::= CAST LP expr AS typetoken RP */
{
- yygotominor.yy346.pExpr = sqlite3PExpr(pParse, TK_CAST, yymsp[-3].minor.yy346.pExpr, 0, &yymsp[-1].minor.yy0);
- spanSet(&yygotominor.yy346,&yymsp[-5].minor.yy0,&yymsp[0].minor.yy0);
+ spanSet(&yymsp[-5].minor.yy342,&yymsp[-5].minor.yy0,&yymsp[0].minor.yy0); /*A-overwrites-X*/
+ yymsp[-5].minor.yy342.pExpr = sqlite3PExpr(pParse, TK_CAST, yymsp[-3].minor.yy342.pExpr, 0, &yymsp[-1].minor.yy0);
}
break;
- case 195: /* expr ::= ID|INDEXED LP distinct exprlist RP */
+ case 159: /* expr ::= ID|INDEXED LP distinct exprlist RP */
{
- if( yymsp[-1].minor.yy14 && yymsp[-1].minor.yy14->nExpr>pParse->db->aLimit[SQLITE_LIMIT_FUNCTION_ARG] ){
+ if( yymsp[-1].minor.yy442 && yymsp[-1].minor.yy442->nExpr>pParse->db->aLimit[SQLITE_LIMIT_FUNCTION_ARG] ){
sqlite3ErrorMsg(pParse, "too many arguments on function %T", &yymsp[-4].minor.yy0);
}
- yygotominor.yy346.pExpr = sqlite3ExprFunction(pParse, yymsp[-1].minor.yy14, &yymsp[-4].minor.yy0);
- spanSet(&yygotominor.yy346,&yymsp[-4].minor.yy0,&yymsp[0].minor.yy0);
- if( yymsp[-2].minor.yy381 && yygotominor.yy346.pExpr ){
- yygotominor.yy346.pExpr->flags |= EP_Distinct;
+ yylhsminor.yy342.pExpr = sqlite3ExprFunction(pParse, yymsp[-1].minor.yy442, &yymsp[-4].minor.yy0);
+ spanSet(&yylhsminor.yy342,&yymsp[-4].minor.yy0,&yymsp[0].minor.yy0);
+ if( yymsp[-2].minor.yy392==SF_Distinct && yylhsminor.yy342.pExpr ){
+ yylhsminor.yy342.pExpr->flags |= EP_Distinct;
}
}
+ yymsp[-4].minor.yy342 = yylhsminor.yy342;
break;
- case 196: /* expr ::= ID|INDEXED LP STAR RP */
+ case 160: /* expr ::= ID|INDEXED LP STAR RP */
{
- yygotominor.yy346.pExpr = sqlite3ExprFunction(pParse, 0, &yymsp[-3].minor.yy0);
- spanSet(&yygotominor.yy346,&yymsp[-3].minor.yy0,&yymsp[0].minor.yy0);
+ yylhsminor.yy342.pExpr = sqlite3ExprFunction(pParse, 0, &yymsp[-3].minor.yy0);
+ spanSet(&yylhsminor.yy342,&yymsp[-3].minor.yy0,&yymsp[0].minor.yy0);
}
+ yymsp[-3].minor.yy342 = yylhsminor.yy342;
break;
- case 197: /* term ::= CTIME_KW */
+ case 161: /* term ::= CTIME_KW */
{
- yygotominor.yy346.pExpr = sqlite3ExprFunction(pParse, 0, &yymsp[0].minor.yy0);
- spanSet(&yygotominor.yy346, &yymsp[0].minor.yy0, &yymsp[0].minor.yy0);
+ yylhsminor.yy342.pExpr = sqlite3ExprFunction(pParse, 0, &yymsp[0].minor.yy0);
+ spanSet(&yylhsminor.yy342, &yymsp[0].minor.yy0, &yymsp[0].minor.yy0);
}
+ yymsp[0].minor.yy342 = yylhsminor.yy342;
break;
- case 198: /* expr ::= expr AND expr */
- case 199: /* expr ::= expr OR expr */ yytestcase(yyruleno==199);
- case 200: /* expr ::= expr LT|GT|GE|LE expr */ yytestcase(yyruleno==200);
- case 201: /* expr ::= expr EQ|NE expr */ yytestcase(yyruleno==201);
- case 202: /* expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr */ yytestcase(yyruleno==202);
- case 203: /* expr ::= expr PLUS|MINUS expr */ yytestcase(yyruleno==203);
- case 204: /* expr ::= expr STAR|SLASH|REM expr */ yytestcase(yyruleno==204);
- case 205: /* expr ::= expr CONCAT expr */ yytestcase(yyruleno==205);
-{spanBinaryExpr(&yygotominor.yy346,pParse,yymsp[-1].major,&yymsp[-2].minor.yy346,&yymsp[0].minor.yy346);}
+ case 162: /* expr ::= expr AND expr */
+ case 163: /* expr ::= expr OR expr */ yytestcase(yyruleno==163);
+ case 164: /* expr ::= expr LT|GT|GE|LE expr */ yytestcase(yyruleno==164);
+ case 165: /* expr ::= expr EQ|NE expr */ yytestcase(yyruleno==165);
+ case 166: /* expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr */ yytestcase(yyruleno==166);
+ case 167: /* expr ::= expr PLUS|MINUS expr */ yytestcase(yyruleno==167);
+ case 168: /* expr ::= expr STAR|SLASH|REM expr */ yytestcase(yyruleno==168);
+ case 169: /* expr ::= expr CONCAT expr */ yytestcase(yyruleno==169);
+{spanBinaryExpr(pParse,yymsp[-1].major,&yymsp[-2].minor.yy342,&yymsp[0].minor.yy342);}
break;
- case 206: /* likeop ::= LIKE_KW|MATCH */
-{yygotominor.yy96.eOperator = yymsp[0].minor.yy0; yygotominor.yy96.bNot = 0;}
+ case 170: /* likeop ::= LIKE_KW|MATCH */
+{yymsp[0].minor.yy318.eOperator = yymsp[0].minor.yy0; yymsp[0].minor.yy318.bNot = 0;/*A-overwrites-X*/}
break;
- case 207: /* likeop ::= NOT LIKE_KW|MATCH */
-{yygotominor.yy96.eOperator = yymsp[0].minor.yy0; yygotominor.yy96.bNot = 1;}
+ case 171: /* likeop ::= NOT LIKE_KW|MATCH */
+{yymsp[-1].minor.yy318.eOperator = yymsp[0].minor.yy0; yymsp[-1].minor.yy318.bNot = 1;}
break;
- case 208: /* expr ::= expr likeop expr */
+ case 172: /* expr ::= expr likeop expr */
{
ExprList *pList;
- pList = sqlite3ExprListAppend(pParse,0, yymsp[0].minor.yy346.pExpr);
- pList = sqlite3ExprListAppend(pParse,pList, yymsp[-2].minor.yy346.pExpr);
- yygotominor.yy346.pExpr = sqlite3ExprFunction(pParse, pList, &yymsp[-1].minor.yy96.eOperator);
- if( yymsp[-1].minor.yy96.bNot ) yygotominor.yy346.pExpr = sqlite3PExpr(pParse, TK_NOT, yygotominor.yy346.pExpr, 0, 0);
- yygotominor.yy346.zStart = yymsp[-2].minor.yy346.zStart;
- yygotominor.yy346.zEnd = yymsp[0].minor.yy346.zEnd;
- if( yygotominor.yy346.pExpr ) yygotominor.yy346.pExpr->flags |= EP_InfixFunc;
+ pList = sqlite3ExprListAppend(pParse,0, yymsp[0].minor.yy342.pExpr);
+ pList = sqlite3ExprListAppend(pParse,pList, yymsp[-2].minor.yy342.pExpr);
+ yymsp[-2].minor.yy342.pExpr = sqlite3ExprFunction(pParse, pList, &yymsp[-1].minor.yy318.eOperator);
+ exprNot(pParse, yymsp[-1].minor.yy318.bNot, &yymsp[-2].minor.yy342);
+ yymsp[-2].minor.yy342.zEnd = yymsp[0].minor.yy342.zEnd;
+ if( yymsp[-2].minor.yy342.pExpr ) yymsp[-2].minor.yy342.pExpr->flags |= EP_InfixFunc;
}
break;
- case 209: /* expr ::= expr likeop expr ESCAPE expr */
+ case 173: /* expr ::= expr likeop expr ESCAPE expr */
{
ExprList *pList;
- pList = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy346.pExpr);
- pList = sqlite3ExprListAppend(pParse,pList, yymsp[-4].minor.yy346.pExpr);
- pList = sqlite3ExprListAppend(pParse,pList, yymsp[0].minor.yy346.pExpr);
- yygotominor.yy346.pExpr = sqlite3ExprFunction(pParse, pList, &yymsp[-3].minor.yy96.eOperator);
- if( yymsp[-3].minor.yy96.bNot ) yygotominor.yy346.pExpr = sqlite3PExpr(pParse, TK_NOT, yygotominor.yy346.pExpr, 0, 0);
- yygotominor.yy346.zStart = yymsp[-4].minor.yy346.zStart;
- yygotominor.yy346.zEnd = yymsp[0].minor.yy346.zEnd;
- if( yygotominor.yy346.pExpr ) yygotominor.yy346.pExpr->flags |= EP_InfixFunc;
+ pList = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy342.pExpr);
+ pList = sqlite3ExprListAppend(pParse,pList, yymsp[-4].minor.yy342.pExpr);
+ pList = sqlite3ExprListAppend(pParse,pList, yymsp[0].minor.yy342.pExpr);
+ yymsp[-4].minor.yy342.pExpr = sqlite3ExprFunction(pParse, pList, &yymsp[-3].minor.yy318.eOperator);
+ exprNot(pParse, yymsp[-3].minor.yy318.bNot, &yymsp[-4].minor.yy342);
+ yymsp[-4].minor.yy342.zEnd = yymsp[0].minor.yy342.zEnd;
+ if( yymsp[-4].minor.yy342.pExpr ) yymsp[-4].minor.yy342.pExpr->flags |= EP_InfixFunc;
}
break;
- case 210: /* expr ::= expr ISNULL|NOTNULL */
-{spanUnaryPostfix(&yygotominor.yy346,pParse,yymsp[0].major,&yymsp[-1].minor.yy346,&yymsp[0].minor.yy0);}
+ case 174: /* expr ::= expr ISNULL|NOTNULL */
+{spanUnaryPostfix(pParse,yymsp[0].major,&yymsp[-1].minor.yy342,&yymsp[0].minor.yy0);}
break;
- case 211: /* expr ::= expr NOT NULL */
-{spanUnaryPostfix(&yygotominor.yy346,pParse,TK_NOTNULL,&yymsp[-2].minor.yy346,&yymsp[0].minor.yy0);}
+ case 175: /* expr ::= expr NOT NULL */
+{spanUnaryPostfix(pParse,TK_NOTNULL,&yymsp[-2].minor.yy342,&yymsp[0].minor.yy0);}
break;
- case 212: /* expr ::= expr IS expr */
+ case 176: /* expr ::= expr IS expr */
{
- spanBinaryExpr(&yygotominor.yy346,pParse,TK_IS,&yymsp[-2].minor.yy346,&yymsp[0].minor.yy346);
- binaryToUnaryIfNull(pParse, yymsp[0].minor.yy346.pExpr, yygotominor.yy346.pExpr, TK_ISNULL);
+ spanBinaryExpr(pParse,TK_IS,&yymsp[-2].minor.yy342,&yymsp[0].minor.yy342);
+ binaryToUnaryIfNull(pParse, yymsp[0].minor.yy342.pExpr, yymsp[-2].minor.yy342.pExpr, TK_ISNULL);
}
break;
- case 213: /* expr ::= expr IS NOT expr */
+ case 177: /* expr ::= expr IS NOT expr */
{
- spanBinaryExpr(&yygotominor.yy346,pParse,TK_ISNOT,&yymsp[-3].minor.yy346,&yymsp[0].minor.yy346);
- binaryToUnaryIfNull(pParse, yymsp[0].minor.yy346.pExpr, yygotominor.yy346.pExpr, TK_NOTNULL);
+ spanBinaryExpr(pParse,TK_ISNOT,&yymsp[-3].minor.yy342,&yymsp[0].minor.yy342);
+ binaryToUnaryIfNull(pParse, yymsp[0].minor.yy342.pExpr, yymsp[-3].minor.yy342.pExpr, TK_NOTNULL);
}
break;
- case 214: /* expr ::= NOT expr */
- case 215: /* expr ::= BITNOT expr */ yytestcase(yyruleno==215);
-{spanUnaryPrefix(&yygotominor.yy346,pParse,yymsp[-1].major,&yymsp[0].minor.yy346,&yymsp[-1].minor.yy0);}
+ case 178: /* expr ::= NOT expr */
+ case 179: /* expr ::= BITNOT expr */ yytestcase(yyruleno==179);
+{spanUnaryPrefix(&yymsp[-1].minor.yy342,pParse,yymsp[-1].major,&yymsp[0].minor.yy342,&yymsp[-1].minor.yy0);/*A-overwrites-B*/}
+ break;
+ case 180: /* expr ::= MINUS expr */
+{spanUnaryPrefix(&yymsp[-1].minor.yy342,pParse,TK_UMINUS,&yymsp[0].minor.yy342,&yymsp[-1].minor.yy0);/*A-overwrites-B*/}
break;
- case 216: /* expr ::= MINUS expr */
-{spanUnaryPrefix(&yygotominor.yy346,pParse,TK_UMINUS,&yymsp[0].minor.yy346,&yymsp[-1].minor.yy0);}
+ case 181: /* expr ::= PLUS expr */
+{spanUnaryPrefix(&yymsp[-1].minor.yy342,pParse,TK_UPLUS,&yymsp[0].minor.yy342,&yymsp[-1].minor.yy0);/*A-overwrites-B*/}
break;
- case 217: /* expr ::= PLUS expr */
-{spanUnaryPrefix(&yygotominor.yy346,pParse,TK_UPLUS,&yymsp[0].minor.yy346,&yymsp[-1].minor.yy0);}
+ case 182: /* between_op ::= BETWEEN */
+ case 185: /* in_op ::= IN */ yytestcase(yyruleno==185);
+{yymsp[0].minor.yy392 = 0;}
break;
- case 220: /* expr ::= expr between_op expr AND expr */
+ case 184: /* expr ::= expr between_op expr AND expr */
{
- ExprList *pList = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy346.pExpr);
- pList = sqlite3ExprListAppend(pParse,pList, yymsp[0].minor.yy346.pExpr);
- yygotominor.yy346.pExpr = sqlite3PExpr(pParse, TK_BETWEEN, yymsp[-4].minor.yy346.pExpr, 0, 0);
- if( yygotominor.yy346.pExpr ){
- yygotominor.yy346.pExpr->x.pList = pList;
+ ExprList *pList = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy342.pExpr);
+ pList = sqlite3ExprListAppend(pParse,pList, yymsp[0].minor.yy342.pExpr);
+ yymsp[-4].minor.yy342.pExpr = sqlite3PExpr(pParse, TK_BETWEEN, yymsp[-4].minor.yy342.pExpr, 0, 0);
+ if( yymsp[-4].minor.yy342.pExpr ){
+ yymsp[-4].minor.yy342.pExpr->x.pList = pList;
}else{
sqlite3ExprListDelete(pParse->db, pList);
}
- if( yymsp[-3].minor.yy328 ) yygotominor.yy346.pExpr = sqlite3PExpr(pParse, TK_NOT, yygotominor.yy346.pExpr, 0, 0);
- yygotominor.yy346.zStart = yymsp[-4].minor.yy346.zStart;
- yygotominor.yy346.zEnd = yymsp[0].minor.yy346.zEnd;
+ exprNot(pParse, yymsp[-3].minor.yy392, &yymsp[-4].minor.yy342);
+ yymsp[-4].minor.yy342.zEnd = yymsp[0].minor.yy342.zEnd;
}
break;
- case 223: /* expr ::= expr in_op LP exprlist RP */
+ case 187: /* expr ::= expr in_op LP exprlist RP */
{
- if( yymsp[-1].minor.yy14==0 ){
+ if( yymsp[-1].minor.yy442==0 ){
/* Expressions of the form
**
** expr1 IN ()
@@ -126127,9 +133686,9 @@ static void yy_reduce(
** simplify to constants 0 (false) and 1 (true), respectively,
** regardless of the value of expr1.
*/
- yygotominor.yy346.pExpr = sqlite3PExpr(pParse, TK_INTEGER, 0, 0, &sqlite3IntTokens[yymsp[-3].minor.yy328]);
- sqlite3ExprDelete(pParse->db, yymsp[-4].minor.yy346.pExpr);
- }else if( yymsp[-1].minor.yy14->nExpr==1 ){
+ sqlite3ExprDelete(pParse->db, yymsp[-4].minor.yy342.pExpr);
+ yymsp[-4].minor.yy342.pExpr = sqlite3PExpr(pParse, TK_INTEGER, 0, 0, &sqlite3IntTokens[yymsp[-3].minor.yy392]);
+ }else if( yymsp[-1].minor.yy442->nExpr==1 ){
/* Expressions of the form:
**
** expr1 IN (?1)
@@ -126146,433 +133705,413 @@ static void yy_reduce(
** affinity or the collating sequence to use for comparison. Otherwise,
** the semantics would be subtly different from IN or NOT IN.
*/
- Expr *pRHS = yymsp[-1].minor.yy14->a[0].pExpr;
- yymsp[-1].minor.yy14->a[0].pExpr = 0;
- sqlite3ExprListDelete(pParse->db, yymsp[-1].minor.yy14);
+ Expr *pRHS = yymsp[-1].minor.yy442->a[0].pExpr;
+ yymsp[-1].minor.yy442->a[0].pExpr = 0;
+ sqlite3ExprListDelete(pParse->db, yymsp[-1].minor.yy442);
/* pRHS cannot be NULL because a malloc error would have been detected
** before now and control would have never reached this point */
if( ALWAYS(pRHS) ){
pRHS->flags &= ~EP_Collate;
pRHS->flags |= EP_Generic;
}
- yygotominor.yy346.pExpr = sqlite3PExpr(pParse, yymsp[-3].minor.yy328 ? TK_NE : TK_EQ, yymsp[-4].minor.yy346.pExpr, pRHS, 0);
+ yymsp[-4].minor.yy342.pExpr = sqlite3PExpr(pParse, yymsp[-3].minor.yy392 ? TK_NE : TK_EQ, yymsp[-4].minor.yy342.pExpr, pRHS, 0);
}else{
- 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;
- sqlite3ExprSetHeightAndFlags(pParse, yygotominor.yy346.pExpr);
+ yymsp[-4].minor.yy342.pExpr = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy342.pExpr, 0, 0);
+ if( yymsp[-4].minor.yy342.pExpr ){
+ yymsp[-4].minor.yy342.pExpr->x.pList = yymsp[-1].minor.yy442;
+ sqlite3ExprSetHeightAndFlags(pParse, yymsp[-4].minor.yy342.pExpr);
}else{
- sqlite3ExprListDelete(pParse->db, yymsp[-1].minor.yy14);
+ sqlite3ExprListDelete(pParse->db, yymsp[-1].minor.yy442);
}
- if( yymsp[-3].minor.yy328 ) yygotominor.yy346.pExpr = sqlite3PExpr(pParse, TK_NOT, yygotominor.yy346.pExpr, 0, 0);
+ exprNot(pParse, yymsp[-3].minor.yy392, &yymsp[-4].minor.yy342);
}
- yygotominor.yy346.zStart = yymsp[-4].minor.yy346.zStart;
- yygotominor.yy346.zEnd = &yymsp[0].minor.yy0.z[yymsp[0].minor.yy0.n];
+ yymsp[-4].minor.yy342.zEnd = &yymsp[0].minor.yy0.z[yymsp[0].minor.yy0.n];
}
break;
- case 224: /* expr ::= LP select RP */
+ case 188: /* expr ::= LP select RP */
{
- 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|EP_Subquery);
- sqlite3ExprSetHeightAndFlags(pParse, yygotominor.yy346.pExpr);
- }else{
- sqlite3SelectDelete(pParse->db, yymsp[-1].minor.yy3);
- }
- yygotominor.yy346.zStart = yymsp[-2].minor.yy0.z;
- yygotominor.yy346.zEnd = &yymsp[0].minor.yy0.z[yymsp[0].minor.yy0.n];
+ spanSet(&yymsp[-2].minor.yy342,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0); /*A-overwrites-B*/
+ yymsp[-2].minor.yy342.pExpr = sqlite3PExpr(pParse, TK_SELECT, 0, 0, 0);
+ sqlite3PExprAddSelect(pParse, yymsp[-2].minor.yy342.pExpr, yymsp[-1].minor.yy159);
}
break;
- case 225: /* expr ::= expr in_op LP select RP */
+ case 189: /* expr ::= expr in_op LP select RP */
{
- 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|EP_Subquery);
- sqlite3ExprSetHeightAndFlags(pParse, yygotominor.yy346.pExpr);
- }else{
- sqlite3SelectDelete(pParse->db, yymsp[-1].minor.yy3);
- }
- if( yymsp[-3].minor.yy328 ) yygotominor.yy346.pExpr = sqlite3PExpr(pParse, TK_NOT, yygotominor.yy346.pExpr, 0, 0);
- yygotominor.yy346.zStart = yymsp[-4].minor.yy346.zStart;
- yygotominor.yy346.zEnd = &yymsp[0].minor.yy0.z[yymsp[0].minor.yy0.n];
+ yymsp[-4].minor.yy342.pExpr = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy342.pExpr, 0, 0);
+ sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy342.pExpr, yymsp[-1].minor.yy159);
+ exprNot(pParse, yymsp[-3].minor.yy392, &yymsp[-4].minor.yy342);
+ yymsp[-4].minor.yy342.zEnd = &yymsp[0].minor.yy0.z[yymsp[0].minor.yy0.n];
}
break;
- case 226: /* expr ::= expr in_op nm dbnm */
+ case 190: /* expr ::= expr in_op nm dbnm */
{
SrcList *pSrc = sqlite3SrcListAppend(pParse->db, 0,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0);
- 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|EP_Subquery);
- sqlite3ExprSetHeightAndFlags(pParse, yygotominor.yy346.pExpr);
- }else{
- sqlite3SrcListDelete(pParse->db, pSrc);
- }
- if( yymsp[-2].minor.yy328 ) yygotominor.yy346.pExpr = sqlite3PExpr(pParse, TK_NOT, yygotominor.yy346.pExpr, 0, 0);
- yygotominor.yy346.zStart = yymsp[-3].minor.yy346.zStart;
- yygotominor.yy346.zEnd = yymsp[0].minor.yy0.z ? &yymsp[0].minor.yy0.z[yymsp[0].minor.yy0.n] : &yymsp[-1].minor.yy0.z[yymsp[-1].minor.yy0.n];
+ Select *pSelect = sqlite3SelectNew(pParse, 0,pSrc,0,0,0,0,0,0,0);
+ yymsp[-3].minor.yy342.pExpr = sqlite3PExpr(pParse, TK_IN, yymsp[-3].minor.yy342.pExpr, 0, 0);
+ sqlite3PExprAddSelect(pParse, yymsp[-3].minor.yy342.pExpr, pSelect);
+ exprNot(pParse, yymsp[-2].minor.yy392, &yymsp[-3].minor.yy342);
+ yymsp[-3].minor.yy342.zEnd = yymsp[0].minor.yy0.z ? &yymsp[0].minor.yy0.z[yymsp[0].minor.yy0.n] : &yymsp[-1].minor.yy0.z[yymsp[-1].minor.yy0.n];
}
break;
- case 227: /* expr ::= EXISTS LP select RP */
+ case 191: /* expr ::= EXISTS LP select RP */
{
- 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|EP_Subquery);
- sqlite3ExprSetHeightAndFlags(pParse, p);
- }else{
- sqlite3SelectDelete(pParse->db, yymsp[-1].minor.yy3);
- }
- yygotominor.yy346.zStart = yymsp[-3].minor.yy0.z;
- yygotominor.yy346.zEnd = &yymsp[0].minor.yy0.z[yymsp[0].minor.yy0.n];
+ Expr *p;
+ spanSet(&yymsp[-3].minor.yy342,&yymsp[-3].minor.yy0,&yymsp[0].minor.yy0); /*A-overwrites-B*/
+ p = yymsp[-3].minor.yy342.pExpr = sqlite3PExpr(pParse, TK_EXISTS, 0, 0, 0);
+ sqlite3PExprAddSelect(pParse, p, yymsp[-1].minor.yy159);
}
break;
- case 228: /* expr ::= CASE case_operand case_exprlist case_else END */
+ case 192: /* expr ::= CASE case_operand case_exprlist case_else END */
{
- 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;
- sqlite3ExprSetHeightAndFlags(pParse, yygotominor.yy346.pExpr);
+ spanSet(&yymsp[-4].minor.yy342,&yymsp[-4].minor.yy0,&yymsp[0].minor.yy0); /*A-overwrites-C*/
+ yymsp[-4].minor.yy342.pExpr = sqlite3PExpr(pParse, TK_CASE, yymsp[-3].minor.yy122, 0, 0);
+ if( yymsp[-4].minor.yy342.pExpr ){
+ yymsp[-4].minor.yy342.pExpr->x.pList = yymsp[-1].minor.yy122 ? sqlite3ExprListAppend(pParse,yymsp[-2].minor.yy442,yymsp[-1].minor.yy122) : yymsp[-2].minor.yy442;
+ sqlite3ExprSetHeightAndFlags(pParse, yymsp[-4].minor.yy342.pExpr);
}else{
- sqlite3ExprListDelete(pParse->db, yymsp[-2].minor.yy14);
- sqlite3ExprDelete(pParse->db, yymsp[-1].minor.yy132);
+ sqlite3ExprListDelete(pParse->db, yymsp[-2].minor.yy442);
+ sqlite3ExprDelete(pParse->db, yymsp[-1].minor.yy122);
}
- yygotominor.yy346.zStart = yymsp[-4].minor.yy0.z;
- yygotominor.yy346.zEnd = &yymsp[0].minor.yy0.z[yymsp[0].minor.yy0.n];
}
break;
- case 229: /* case_exprlist ::= case_exprlist WHEN expr THEN expr */
+ case 193: /* case_exprlist ::= case_exprlist WHEN expr THEN expr */
{
- yygotominor.yy14 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy14, yymsp[-2].minor.yy346.pExpr);
- yygotominor.yy14 = sqlite3ExprListAppend(pParse,yygotominor.yy14, yymsp[0].minor.yy346.pExpr);
+ yymsp[-4].minor.yy442 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy442, yymsp[-2].minor.yy342.pExpr);
+ yymsp[-4].minor.yy442 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy442, yymsp[0].minor.yy342.pExpr);
}
break;
- case 230: /* case_exprlist ::= WHEN expr THEN expr */
+ case 194: /* case_exprlist ::= WHEN expr THEN expr */
{
- yygotominor.yy14 = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy346.pExpr);
- yygotominor.yy14 = sqlite3ExprListAppend(pParse,yygotominor.yy14, yymsp[0].minor.yy346.pExpr);
+ yymsp[-3].minor.yy442 = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy342.pExpr);
+ yymsp[-3].minor.yy442 = sqlite3ExprListAppend(pParse,yymsp[-3].minor.yy442, yymsp[0].minor.yy342.pExpr);
}
break;
- case 237: /* nexprlist ::= nexprlist COMMA expr */
-{yygotominor.yy14 = sqlite3ExprListAppend(pParse,yymsp[-2].minor.yy14,yymsp[0].minor.yy346.pExpr);}
+ case 197: /* case_operand ::= expr */
+{yymsp[0].minor.yy122 = yymsp[0].minor.yy342.pExpr; /*A-overwrites-X*/}
+ break;
+ case 200: /* nexprlist ::= nexprlist COMMA expr */
+{yymsp[-2].minor.yy442 = sqlite3ExprListAppend(pParse,yymsp[-2].minor.yy442,yymsp[0].minor.yy342.pExpr);}
break;
- case 238: /* nexprlist ::= expr */
-{yygotominor.yy14 = sqlite3ExprListAppend(pParse,0,yymsp[0].minor.yy346.pExpr);}
+ case 201: /* nexprlist ::= expr */
+{yymsp[0].minor.yy442 = sqlite3ExprListAppend(pParse,0,yymsp[0].minor.yy342.pExpr); /*A-overwrites-Y*/}
break;
- case 239: /* cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP idxlist RP where_opt */
+ case 202: /* cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */
{
sqlite3CreateIndex(pParse, &yymsp[-7].minor.yy0, &yymsp[-6].minor.yy0,
- sqlite3SrcListAppend(pParse->db,0,&yymsp[-4].minor.yy0,0), yymsp[-2].minor.yy14, yymsp[-10].minor.yy328,
- &yymsp[-11].minor.yy0, yymsp[0].minor.yy132, SQLITE_SO_ASC, yymsp[-8].minor.yy328);
+ sqlite3SrcListAppend(pParse->db,0,&yymsp[-4].minor.yy0,0), yymsp[-2].minor.yy442, yymsp[-10].minor.yy392,
+ &yymsp[-11].minor.yy0, yymsp[0].minor.yy122, SQLITE_SO_ASC, yymsp[-8].minor.yy392);
}
break;
- case 240: /* uniqueflag ::= UNIQUE */
- case 291: /* raisetype ::= ABORT */ yytestcase(yyruleno==291);
-{yygotominor.yy328 = OE_Abort;}
+ case 203: /* uniqueflag ::= UNIQUE */
+ case 244: /* raisetype ::= ABORT */ yytestcase(yyruleno==244);
+{yymsp[0].minor.yy392 = OE_Abort;}
+ break;
+ case 204: /* uniqueflag ::= */
+{yymsp[1].minor.yy392 = OE_None;}
break;
- case 241: /* uniqueflag ::= */
-{yygotominor.yy328 = OE_None;}
+ case 206: /* eidlist_opt ::= LP eidlist RP */
+{yymsp[-2].minor.yy442 = yymsp[-1].minor.yy442;}
break;
- case 244: /* idxlist ::= idxlist COMMA nm collate sortorder */
+ case 207: /* eidlist ::= eidlist COMMA nm collate sortorder */
{
- 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");
- if( yygotominor.yy14 ) yygotominor.yy14->a[yygotominor.yy14->nExpr-1].sortOrder = (u8)yymsp[0].minor.yy328;
+ yymsp[-4].minor.yy442 = parserAddExprIdListTerm(pParse, yymsp[-4].minor.yy442, &yymsp[-2].minor.yy0, yymsp[-1].minor.yy392, yymsp[0].minor.yy392);
}
break;
- case 245: /* idxlist ::= nm collate sortorder */
+ case 208: /* eidlist ::= nm collate sortorder */
{
- 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");
- if( yygotominor.yy14 ) yygotominor.yy14->a[yygotominor.yy14->nExpr-1].sortOrder = (u8)yymsp[0].minor.yy328;
+ yymsp[-2].minor.yy442 = parserAddExprIdListTerm(pParse, 0, &yymsp[-2].minor.yy0, yymsp[-1].minor.yy392, yymsp[0].minor.yy392); /*A-overwrites-Y*/
}
break;
- case 246: /* collate ::= */
-{yygotominor.yy0.z = 0; yygotominor.yy0.n = 0;}
+ case 211: /* cmd ::= DROP INDEX ifexists fullname */
+{sqlite3DropIndex(pParse, yymsp[0].minor.yy347, yymsp[-1].minor.yy392);}
break;
- case 248: /* cmd ::= DROP INDEX ifexists fullname */
-{sqlite3DropIndex(pParse, yymsp[0].minor.yy65, yymsp[-1].minor.yy328);}
- break;
- case 249: /* cmd ::= VACUUM */
- case 250: /* cmd ::= VACUUM nm */ yytestcase(yyruleno==250);
+ case 212: /* cmd ::= VACUUM */
+ case 213: /* cmd ::= VACUUM nm */ yytestcase(yyruleno==213);
{sqlite3Vacuum(pParse);}
break;
- case 251: /* cmd ::= PRAGMA nm dbnm */
+ case 214: /* cmd ::= PRAGMA nm dbnm */
{sqlite3Pragma(pParse,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0,0,0);}
break;
- case 252: /* cmd ::= PRAGMA nm dbnm EQ nmnum */
+ case 215: /* cmd ::= PRAGMA nm dbnm EQ nmnum */
{sqlite3Pragma(pParse,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0,0);}
break;
- case 253: /* cmd ::= PRAGMA nm dbnm LP nmnum RP */
+ case 216: /* cmd ::= PRAGMA nm dbnm LP nmnum RP */
{sqlite3Pragma(pParse,&yymsp[-4].minor.yy0,&yymsp[-3].minor.yy0,&yymsp[-1].minor.yy0,0);}
break;
- case 254: /* cmd ::= PRAGMA nm dbnm EQ minus_num */
+ case 217: /* cmd ::= PRAGMA nm dbnm EQ minus_num */
{sqlite3Pragma(pParse,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0,1);}
break;
- case 255: /* cmd ::= PRAGMA nm dbnm LP minus_num RP */
+ case 218: /* cmd ::= PRAGMA nm dbnm LP minus_num RP */
{sqlite3Pragma(pParse,&yymsp[-4].minor.yy0,&yymsp[-3].minor.yy0,&yymsp[-1].minor.yy0,1);}
break;
- case 264: /* cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */
+ case 221: /* cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */
{
Token all;
all.z = yymsp[-3].minor.yy0.z;
all.n = (int)(yymsp[0].minor.yy0.z - yymsp[-3].minor.yy0.z) + yymsp[0].minor.yy0.n;
- sqlite3FinishTrigger(pParse, yymsp[-1].minor.yy473, &all);
+ sqlite3FinishTrigger(pParse, yymsp[-1].minor.yy327, &all);
}
break;
- case 265: /* trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */
+ case 222: /* trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */
{
- sqlite3BeginTrigger(pParse, &yymsp[-7].minor.yy0, &yymsp[-6].minor.yy0, yymsp[-5].minor.yy328, yymsp[-4].minor.yy378.a, yymsp[-4].minor.yy378.b, yymsp[-2].minor.yy65, yymsp[0].minor.yy132, yymsp[-10].minor.yy328, yymsp[-8].minor.yy328);
- yygotominor.yy0 = (yymsp[-6].minor.yy0.n==0?yymsp[-7].minor.yy0:yymsp[-6].minor.yy0);
+ sqlite3BeginTrigger(pParse, &yymsp[-7].minor.yy0, &yymsp[-6].minor.yy0, yymsp[-5].minor.yy392, yymsp[-4].minor.yy410.a, yymsp[-4].minor.yy410.b, yymsp[-2].minor.yy347, yymsp[0].minor.yy122, yymsp[-10].minor.yy392, yymsp[-8].minor.yy392);
+ yymsp[-10].minor.yy0 = (yymsp[-6].minor.yy0.n==0?yymsp[-7].minor.yy0:yymsp[-6].minor.yy0); /*A-overwrites-T*/
}
break;
- case 266: /* trigger_time ::= BEFORE */
- case 269: /* trigger_time ::= */ yytestcase(yyruleno==269);
-{ yygotominor.yy328 = TK_BEFORE; }
+ case 223: /* trigger_time ::= BEFORE */
+{ yymsp[0].minor.yy392 = TK_BEFORE; }
+ break;
+ case 224: /* trigger_time ::= AFTER */
+{ yymsp[0].minor.yy392 = TK_AFTER; }
break;
- case 267: /* trigger_time ::= AFTER */
-{ yygotominor.yy328 = TK_AFTER; }
+ case 225: /* trigger_time ::= INSTEAD OF */
+{ yymsp[-1].minor.yy392 = TK_INSTEAD;}
break;
- case 268: /* trigger_time ::= INSTEAD OF */
-{ yygotominor.yy328 = TK_INSTEAD;}
+ case 226: /* trigger_time ::= */
+{ yymsp[1].minor.yy392 = TK_BEFORE; }
break;
- case 270: /* trigger_event ::= DELETE|INSERT */
- case 271: /* trigger_event ::= UPDATE */ yytestcase(yyruleno==271);
-{yygotominor.yy378.a = yymsp[0].major; yygotominor.yy378.b = 0;}
+ case 227: /* trigger_event ::= DELETE|INSERT */
+ case 228: /* trigger_event ::= UPDATE */ yytestcase(yyruleno==228);
+{yymsp[0].minor.yy410.a = yymsp[0].major; /*A-overwrites-X*/ yymsp[0].minor.yy410.b = 0;}
break;
- case 272: /* trigger_event ::= UPDATE OF idlist */
-{yygotominor.yy378.a = TK_UPDATE; yygotominor.yy378.b = yymsp[0].minor.yy408;}
+ case 229: /* trigger_event ::= UPDATE OF idlist */
+{yymsp[-2].minor.yy410.a = TK_UPDATE; yymsp[-2].minor.yy410.b = yymsp[0].minor.yy180;}
break;
- case 275: /* when_clause ::= */
- case 296: /* key_opt ::= */ yytestcase(yyruleno==296);
-{ yygotominor.yy132 = 0; }
+ case 230: /* when_clause ::= */
+ case 249: /* key_opt ::= */ yytestcase(yyruleno==249);
+{ yymsp[1].minor.yy122 = 0; }
break;
- case 276: /* when_clause ::= WHEN expr */
- case 297: /* key_opt ::= KEY expr */ yytestcase(yyruleno==297);
-{ yygotominor.yy132 = yymsp[0].minor.yy346.pExpr; }
+ case 231: /* when_clause ::= WHEN expr */
+ case 250: /* key_opt ::= KEY expr */ yytestcase(yyruleno==250);
+{ yymsp[-1].minor.yy122 = yymsp[0].minor.yy342.pExpr; }
break;
- case 277: /* trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */
+ case 232: /* trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */
{
- assert( yymsp[-2].minor.yy473!=0 );
- yymsp[-2].minor.yy473->pLast->pNext = yymsp[-1].minor.yy473;
- yymsp[-2].minor.yy473->pLast = yymsp[-1].minor.yy473;
- yygotominor.yy473 = yymsp[-2].minor.yy473;
+ assert( yymsp[-2].minor.yy327!=0 );
+ yymsp[-2].minor.yy327->pLast->pNext = yymsp[-1].minor.yy327;
+ yymsp[-2].minor.yy327->pLast = yymsp[-1].minor.yy327;
}
break;
- case 278: /* trigger_cmd_list ::= trigger_cmd SEMI */
+ case 233: /* trigger_cmd_list ::= trigger_cmd SEMI */
{
- assert( yymsp[-1].minor.yy473!=0 );
- yymsp[-1].minor.yy473->pLast = yymsp[-1].minor.yy473;
- yygotominor.yy473 = yymsp[-1].minor.yy473;
+ assert( yymsp[-1].minor.yy327!=0 );
+ yymsp[-1].minor.yy327->pLast = yymsp[-1].minor.yy327;
}
break;
- case 280: /* trnm ::= nm DOT nm */
+ case 234: /* trnm ::= nm DOT nm */
{
- yygotominor.yy0 = yymsp[0].minor.yy0;
+ yymsp[-2].minor.yy0 = yymsp[0].minor.yy0;
sqlite3ErrorMsg(pParse,
"qualified table names are not allowed on INSERT, UPDATE, and DELETE "
"statements within triggers");
}
break;
- case 282: /* tridxby ::= INDEXED BY nm */
+ case 235: /* tridxby ::= INDEXED BY nm */
{
sqlite3ErrorMsg(pParse,
"the INDEXED BY clause is not allowed on UPDATE or DELETE statements "
"within triggers");
}
break;
- case 283: /* tridxby ::= NOT INDEXED */
+ case 236: /* tridxby ::= NOT INDEXED */
{
sqlite3ErrorMsg(pParse,
"the NOT INDEXED clause is not allowed on UPDATE or DELETE statements "
"within triggers");
}
break;
- case 284: /* trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist where_opt */
-{ yygotominor.yy473 = sqlite3TriggerUpdateStep(pParse->db, &yymsp[-4].minor.yy0, yymsp[-1].minor.yy14, yymsp[0].minor.yy132, yymsp[-5].minor.yy186); }
+ case 237: /* trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist where_opt */
+{yymsp[-6].minor.yy327 = sqlite3TriggerUpdateStep(pParse->db, &yymsp[-4].minor.yy0, yymsp[-1].minor.yy442, yymsp[0].minor.yy122, yymsp[-5].minor.yy392);}
break;
- case 285: /* trigger_cmd ::= insert_cmd INTO trnm inscollist_opt select */
-{yygotominor.yy473 = sqlite3TriggerInsertStep(pParse->db, &yymsp[-2].minor.yy0, yymsp[-1].minor.yy408, yymsp[0].minor.yy3, yymsp[-4].minor.yy186);}
+ case 238: /* trigger_cmd ::= insert_cmd INTO trnm idlist_opt select */
+{yymsp[-4].minor.yy327 = sqlite3TriggerInsertStep(pParse->db, &yymsp[-2].minor.yy0, yymsp[-1].minor.yy180, yymsp[0].minor.yy159, yymsp[-4].minor.yy392);/*A-overwrites-R*/}
break;
- case 286: /* trigger_cmd ::= DELETE FROM trnm tridxby where_opt */
-{yygotominor.yy473 = sqlite3TriggerDeleteStep(pParse->db, &yymsp[-2].minor.yy0, yymsp[0].minor.yy132);}
+ case 239: /* trigger_cmd ::= DELETE FROM trnm tridxby where_opt */
+{yymsp[-4].minor.yy327 = sqlite3TriggerDeleteStep(pParse->db, &yymsp[-2].minor.yy0, yymsp[0].minor.yy122);}
break;
- case 287: /* trigger_cmd ::= select */
-{yygotominor.yy473 = sqlite3TriggerSelectStep(pParse->db, yymsp[0].minor.yy3); }
+ case 240: /* trigger_cmd ::= select */
+{yymsp[0].minor.yy327 = sqlite3TriggerSelectStep(pParse->db, yymsp[0].minor.yy159); /*A-overwrites-X*/}
break;
- case 288: /* expr ::= RAISE LP IGNORE RP */
+ case 241: /* expr ::= RAISE LP IGNORE RP */
{
- yygotominor.yy346.pExpr = sqlite3PExpr(pParse, TK_RAISE, 0, 0, 0);
- if( yygotominor.yy346.pExpr ){
- yygotominor.yy346.pExpr->affinity = OE_Ignore;
+ spanSet(&yymsp[-3].minor.yy342,&yymsp[-3].minor.yy0,&yymsp[0].minor.yy0); /*A-overwrites-X*/
+ yymsp[-3].minor.yy342.pExpr = sqlite3PExpr(pParse, TK_RAISE, 0, 0, 0);
+ if( yymsp[-3].minor.yy342.pExpr ){
+ yymsp[-3].minor.yy342.pExpr->affinity = OE_Ignore;
}
- yygotominor.yy346.zStart = yymsp[-3].minor.yy0.z;
- yygotominor.yy346.zEnd = &yymsp[0].minor.yy0.z[yymsp[0].minor.yy0.n];
}
break;
- case 289: /* expr ::= RAISE LP raisetype COMMA nm RP */
+ case 242: /* expr ::= RAISE LP raisetype COMMA nm RP */
{
- yygotominor.yy346.pExpr = sqlite3PExpr(pParse, TK_RAISE, 0, 0, &yymsp[-1].minor.yy0);
- if( yygotominor.yy346.pExpr ) {
- yygotominor.yy346.pExpr->affinity = (char)yymsp[-3].minor.yy328;
+ spanSet(&yymsp[-5].minor.yy342,&yymsp[-5].minor.yy0,&yymsp[0].minor.yy0); /*A-overwrites-X*/
+ yymsp[-5].minor.yy342.pExpr = sqlite3PExpr(pParse, TK_RAISE, 0, 0, &yymsp[-1].minor.yy0);
+ if( yymsp[-5].minor.yy342.pExpr ) {
+ yymsp[-5].minor.yy342.pExpr->affinity = (char)yymsp[-3].minor.yy392;
}
- yygotominor.yy346.zStart = yymsp[-5].minor.yy0.z;
- yygotominor.yy346.zEnd = &yymsp[0].minor.yy0.z[yymsp[0].minor.yy0.n];
}
break;
- case 290: /* raisetype ::= ROLLBACK */
-{yygotominor.yy328 = OE_Rollback;}
+ case 243: /* raisetype ::= ROLLBACK */
+{yymsp[0].minor.yy392 = OE_Rollback;}
break;
- case 292: /* raisetype ::= FAIL */
-{yygotominor.yy328 = OE_Fail;}
+ case 245: /* raisetype ::= FAIL */
+{yymsp[0].minor.yy392 = OE_Fail;}
break;
- case 293: /* cmd ::= DROP TRIGGER ifexists fullname */
+ case 246: /* cmd ::= DROP TRIGGER ifexists fullname */
{
- sqlite3DropTrigger(pParse,yymsp[0].minor.yy65,yymsp[-1].minor.yy328);
+ sqlite3DropTrigger(pParse,yymsp[0].minor.yy347,yymsp[-1].minor.yy392);
}
break;
- case 294: /* cmd ::= ATTACH database_kw_opt expr AS expr key_opt */
+ case 247: /* cmd ::= ATTACH database_kw_opt expr AS expr key_opt */
{
- sqlite3Attach(pParse, yymsp[-3].minor.yy346.pExpr, yymsp[-1].minor.yy346.pExpr, yymsp[0].minor.yy132);
+ sqlite3Attach(pParse, yymsp[-3].minor.yy342.pExpr, yymsp[-1].minor.yy342.pExpr, yymsp[0].minor.yy122);
}
break;
- case 295: /* cmd ::= DETACH database_kw_opt expr */
+ case 248: /* cmd ::= DETACH database_kw_opt expr */
{
- sqlite3Detach(pParse, yymsp[0].minor.yy346.pExpr);
+ sqlite3Detach(pParse, yymsp[0].minor.yy342.pExpr);
}
break;
- case 300: /* cmd ::= REINDEX */
+ case 251: /* cmd ::= REINDEX */
{sqlite3Reindex(pParse, 0, 0);}
break;
- case 301: /* cmd ::= REINDEX nm dbnm */
+ case 252: /* cmd ::= REINDEX nm dbnm */
{sqlite3Reindex(pParse, &yymsp[-1].minor.yy0, &yymsp[0].minor.yy0);}
break;
- case 302: /* cmd ::= ANALYZE */
+ case 253: /* cmd ::= ANALYZE */
{sqlite3Analyze(pParse, 0, 0);}
break;
- case 303: /* cmd ::= ANALYZE nm dbnm */
+ case 254: /* cmd ::= ANALYZE nm dbnm */
{sqlite3Analyze(pParse, &yymsp[-1].minor.yy0, &yymsp[0].minor.yy0);}
break;
- case 304: /* cmd ::= ALTER TABLE fullname RENAME TO nm */
+ case 255: /* cmd ::= ALTER TABLE fullname RENAME TO nm */
{
- sqlite3AlterRenameTable(pParse,yymsp[-3].minor.yy65,&yymsp[0].minor.yy0);
+ sqlite3AlterRenameTable(pParse,yymsp[-3].minor.yy347,&yymsp[0].minor.yy0);
}
break;
- case 305: /* cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt column */
+ case 256: /* cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */
{
- sqlite3AlterFinishAddColumn(pParse, &yymsp[0].minor.yy0);
+ yymsp[-1].minor.yy0.n = (int)(pParse->sLastToken.z-yymsp[-1].minor.yy0.z) + pParse->sLastToken.n;
+ sqlite3AlterFinishAddColumn(pParse, &yymsp[-1].minor.yy0);
}
break;
- case 306: /* add_column_fullname ::= fullname */
+ case 257: /* add_column_fullname ::= fullname */
{
- pParse->db->lookaside.bEnabled = 0;
- sqlite3AlterBeginAddColumn(pParse, yymsp[0].minor.yy65);
+ disableLookaside(pParse);
+ sqlite3AlterBeginAddColumn(pParse, yymsp[0].minor.yy347);
}
break;
- case 309: /* cmd ::= create_vtab */
+ case 258: /* cmd ::= create_vtab */
{sqlite3VtabFinishParse(pParse,0);}
break;
- case 310: /* cmd ::= create_vtab LP vtabarglist RP */
+ case 259: /* cmd ::= create_vtab LP vtabarglist RP */
{sqlite3VtabFinishParse(pParse,&yymsp[0].minor.yy0);}
break;
- case 311: /* create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */
+ case 260: /* create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */
{
- sqlite3VtabBeginParse(pParse, &yymsp[-3].minor.yy0, &yymsp[-2].minor.yy0, &yymsp[0].minor.yy0, yymsp[-4].minor.yy328);
+ sqlite3VtabBeginParse(pParse, &yymsp[-3].minor.yy0, &yymsp[-2].minor.yy0, &yymsp[0].minor.yy0, yymsp[-4].minor.yy392);
}
break;
- case 314: /* vtabarg ::= */
+ case 261: /* vtabarg ::= */
{sqlite3VtabArgInit(pParse);}
break;
- case 316: /* vtabargtoken ::= ANY */
- case 317: /* vtabargtoken ::= lp anylist RP */ yytestcase(yyruleno==317);
- case 318: /* lp ::= LP */ yytestcase(yyruleno==318);
+ case 262: /* vtabargtoken ::= ANY */
+ case 263: /* vtabargtoken ::= lp anylist RP */ yytestcase(yyruleno==263);
+ case 264: /* lp ::= LP */ yytestcase(yyruleno==264);
{sqlite3VtabArgExtend(pParse,&yymsp[0].minor.yy0);}
break;
- case 322: /* with ::= */
-{yygotominor.yy59 = 0;}
+ case 265: /* with ::= */
+{yymsp[1].minor.yy331 = 0;}
+ break;
+ case 266: /* with ::= WITH wqlist */
+{ yymsp[-1].minor.yy331 = yymsp[0].minor.yy331; }
break;
- case 323: /* with ::= WITH wqlist */
- case 324: /* with ::= WITH RECURSIVE wqlist */ yytestcase(yyruleno==324);
-{ yygotominor.yy59 = yymsp[0].minor.yy59; }
+ case 267: /* with ::= WITH RECURSIVE wqlist */
+{ yymsp[-2].minor.yy331 = yymsp[0].minor.yy331; }
break;
- case 325: /* wqlist ::= nm idxlist_opt AS LP select RP */
+ case 268: /* wqlist ::= nm eidlist_opt AS LP select RP */
{
- yygotominor.yy59 = sqlite3WithAdd(pParse, 0, &yymsp[-5].minor.yy0, yymsp[-4].minor.yy14, yymsp[-1].minor.yy3);
+ yymsp[-5].minor.yy331 = sqlite3WithAdd(pParse, 0, &yymsp[-5].minor.yy0, yymsp[-4].minor.yy442, yymsp[-1].minor.yy159); /*A-overwrites-X*/
}
break;
- case 326: /* wqlist ::= wqlist COMMA nm idxlist_opt AS LP select RP */
+ case 269: /* wqlist ::= wqlist COMMA nm eidlist_opt AS LP select RP */
{
- yygotominor.yy59 = sqlite3WithAdd(pParse, yymsp[-7].minor.yy59, &yymsp[-5].minor.yy0, yymsp[-4].minor.yy14, yymsp[-1].minor.yy3);
+ yymsp[-7].minor.yy331 = sqlite3WithAdd(pParse, yymsp[-7].minor.yy331, &yymsp[-5].minor.yy0, yymsp[-4].minor.yy442, yymsp[-1].minor.yy159);
}
break;
default:
- /* (0) input ::= cmdlist */ yytestcase(yyruleno==0);
- /* (1) cmdlist ::= cmdlist ecmd */ yytestcase(yyruleno==1);
- /* (2) cmdlist ::= ecmd */ yytestcase(yyruleno==2);
- /* (3) ecmd ::= SEMI */ yytestcase(yyruleno==3);
- /* (4) ecmd ::= explain cmdx SEMI */ yytestcase(yyruleno==4);
- /* (10) trans_opt ::= */ yytestcase(yyruleno==10);
- /* (11) trans_opt ::= TRANSACTION */ yytestcase(yyruleno==11);
- /* (12) trans_opt ::= TRANSACTION nm */ yytestcase(yyruleno==12);
- /* (20) savepoint_opt ::= SAVEPOINT */ yytestcase(yyruleno==20);
- /* (21) savepoint_opt ::= */ yytestcase(yyruleno==21);
- /* (25) cmd ::= create_table create_table_args */ yytestcase(yyruleno==25);
- /* (36) columnlist ::= columnlist COMMA column */ yytestcase(yyruleno==36);
- /* (37) columnlist ::= column */ yytestcase(yyruleno==37);
- /* (43) type ::= */ yytestcase(yyruleno==43);
- /* (50) signed ::= plus_num */ yytestcase(yyruleno==50);
- /* (51) signed ::= minus_num */ yytestcase(yyruleno==51);
- /* (52) carglist ::= carglist ccons */ yytestcase(yyruleno==52);
- /* (53) carglist ::= */ yytestcase(yyruleno==53);
- /* (60) ccons ::= NULL onconf */ yytestcase(yyruleno==60);
- /* (88) conslist ::= conslist tconscomma tcons */ yytestcase(yyruleno==88);
- /* (89) conslist ::= tcons */ yytestcase(yyruleno==89);
- /* (91) tconscomma ::= */ yytestcase(yyruleno==91);
- /* (273) foreach_clause ::= */ yytestcase(yyruleno==273);
- /* (274) foreach_clause ::= FOR EACH ROW */ yytestcase(yyruleno==274);
- /* (281) tridxby ::= */ yytestcase(yyruleno==281);
- /* (298) database_kw_opt ::= DATABASE */ yytestcase(yyruleno==298);
- /* (299) database_kw_opt ::= */ yytestcase(yyruleno==299);
- /* (307) kwcolumn_opt ::= */ yytestcase(yyruleno==307);
- /* (308) kwcolumn_opt ::= COLUMNKW */ yytestcase(yyruleno==308);
- /* (312) vtabarglist ::= vtabarg */ yytestcase(yyruleno==312);
- /* (313) vtabarglist ::= vtabarglist COMMA vtabarg */ yytestcase(yyruleno==313);
- /* (315) vtabarg ::= vtabarg vtabargtoken */ yytestcase(yyruleno==315);
- /* (319) anylist ::= */ yytestcase(yyruleno==319);
- /* (320) anylist ::= anylist LP anylist RP */ yytestcase(yyruleno==320);
- /* (321) anylist ::= anylist ANY */ yytestcase(yyruleno==321);
+ /* (270) input ::= cmdlist */ yytestcase(yyruleno==270);
+ /* (271) cmdlist ::= cmdlist ecmd */ yytestcase(yyruleno==271);
+ /* (272) cmdlist ::= ecmd */ yytestcase(yyruleno==272);
+ /* (273) ecmd ::= SEMI */ yytestcase(yyruleno==273);
+ /* (274) ecmd ::= explain cmdx SEMI */ yytestcase(yyruleno==274);
+ /* (275) explain ::= */ yytestcase(yyruleno==275);
+ /* (276) trans_opt ::= */ yytestcase(yyruleno==276);
+ /* (277) trans_opt ::= TRANSACTION */ yytestcase(yyruleno==277);
+ /* (278) trans_opt ::= TRANSACTION nm */ yytestcase(yyruleno==278);
+ /* (279) savepoint_opt ::= SAVEPOINT */ yytestcase(yyruleno==279);
+ /* (280) savepoint_opt ::= */ yytestcase(yyruleno==280);
+ /* (281) cmd ::= create_table create_table_args */ yytestcase(yyruleno==281);
+ /* (282) columnlist ::= columnlist COMMA columnname carglist */ yytestcase(yyruleno==282);
+ /* (283) columnlist ::= columnname carglist */ yytestcase(yyruleno==283);
+ /* (284) nm ::= ID|INDEXED */ yytestcase(yyruleno==284);
+ /* (285) nm ::= STRING */ yytestcase(yyruleno==285);
+ /* (286) nm ::= JOIN_KW */ yytestcase(yyruleno==286);
+ /* (287) typetoken ::= typename */ yytestcase(yyruleno==287);
+ /* (288) typename ::= ID|STRING */ yytestcase(yyruleno==288);
+ /* (289) signed ::= plus_num */ yytestcase(yyruleno==289);
+ /* (290) signed ::= minus_num */ yytestcase(yyruleno==290);
+ /* (291) carglist ::= carglist ccons */ yytestcase(yyruleno==291);
+ /* (292) carglist ::= */ yytestcase(yyruleno==292);
+ /* (293) ccons ::= NULL onconf */ yytestcase(yyruleno==293);
+ /* (294) conslist_opt ::= COMMA conslist */ yytestcase(yyruleno==294);
+ /* (295) conslist ::= conslist tconscomma tcons */ yytestcase(yyruleno==295);
+ /* (296) conslist ::= tcons */ yytestcase(yyruleno==296);
+ /* (297) tconscomma ::= */ yytestcase(yyruleno==297);
+ /* (298) defer_subclause_opt ::= defer_subclause */ yytestcase(yyruleno==298);
+ /* (299) resolvetype ::= raisetype */ yytestcase(yyruleno==299);
+ /* (300) selectnowith ::= oneselect */ yytestcase(yyruleno==300);
+ /* (301) oneselect ::= values */ yytestcase(yyruleno==301);
+ /* (302) sclp ::= selcollist COMMA */ yytestcase(yyruleno==302);
+ /* (303) as ::= ID|STRING */ yytestcase(yyruleno==303);
+ /* (304) expr ::= term */ yytestcase(yyruleno==304);
+ /* (305) exprlist ::= nexprlist */ yytestcase(yyruleno==305);
+ /* (306) nmnum ::= plus_num */ yytestcase(yyruleno==306);
+ /* (307) nmnum ::= nm */ yytestcase(yyruleno==307);
+ /* (308) nmnum ::= ON */ yytestcase(yyruleno==308);
+ /* (309) nmnum ::= DELETE */ yytestcase(yyruleno==309);
+ /* (310) nmnum ::= DEFAULT */ yytestcase(yyruleno==310);
+ /* (311) plus_num ::= INTEGER|FLOAT */ yytestcase(yyruleno==311);
+ /* (312) foreach_clause ::= */ yytestcase(yyruleno==312);
+ /* (313) foreach_clause ::= FOR EACH ROW */ yytestcase(yyruleno==313);
+ /* (314) trnm ::= nm */ yytestcase(yyruleno==314);
+ /* (315) tridxby ::= */ yytestcase(yyruleno==315);
+ /* (316) database_kw_opt ::= DATABASE */ yytestcase(yyruleno==316);
+ /* (317) database_kw_opt ::= */ yytestcase(yyruleno==317);
+ /* (318) kwcolumn_opt ::= */ yytestcase(yyruleno==318);
+ /* (319) kwcolumn_opt ::= COLUMNKW */ yytestcase(yyruleno==319);
+ /* (320) vtabarglist ::= vtabarg */ yytestcase(yyruleno==320);
+ /* (321) vtabarglist ::= vtabarglist COMMA vtabarg */ yytestcase(yyruleno==321);
+ /* (322) vtabarg ::= vtabarg vtabargtoken */ yytestcase(yyruleno==322);
+ /* (323) anylist ::= */ yytestcase(yyruleno==323);
+ /* (324) anylist ::= anylist LP anylist RP */ yytestcase(yyruleno==324);
+ /* (325) anylist ::= anylist ANY */ yytestcase(yyruleno==325);
break;
+/********** End reduce actions ************************************************/
};
- assert( yyruleno>=0 && yyruleno<sizeof(yyRuleInfo)/sizeof(yyRuleInfo[0]) );
+ assert( yyruleno<sizeof(yyRuleInfo)/sizeof(yyRuleInfo[0]) );
yygoto = yyRuleInfo[yyruleno].lhs;
yysize = yyRuleInfo[yyruleno].nrhs;
- yypParser->yyidx -= yysize;
yyact = yy_find_reduce_action(yymsp[-yysize].stateno,(YYCODETYPE)yygoto);
- if( yyact < YYNSTATE ){
-#ifdef NDEBUG
- /* If we are not debugging and the reduce action popped at least
- ** one element off the stack, then we can push the new element back
- ** onto the stack here, and skip the stack overflow test in yy_shift().
- ** That gives a significant speed improvement. */
- if( yysize ){
- yypParser->yyidx++;
- yymsp -= yysize-1;
- yymsp->stateno = (YYACTIONTYPE)yyact;
- yymsp->major = (YYCODETYPE)yygoto;
- yymsp->minor = yygotominor;
- }else
-#endif
- {
- yy_shift(yypParser,yyact,yygoto,&yygotominor);
- }
- }else{
- assert( yyact == YYNSTATE + YYNRULE + 1 );
+ if( yyact <= YY_MAX_SHIFTREDUCE ){
+ if( yyact>YY_MAX_SHIFT ) yyact += YY_MIN_REDUCE - YY_MIN_SHIFTREDUCE;
+ yypParser->yyidx -= yysize - 1;
+ yymsp -= yysize-1;
+ yymsp->stateno = (YYACTIONTYPE)yyact;
+ yymsp->major = (YYCODETYPE)yygoto;
+ yyTraceShift(yypParser, yyact);
+ }else{
+ assert( yyact == YY_ACCEPT_ACTION );
+ yypParser->yyidx -= yysize;
yy_accept(yypParser);
}
}
@@ -126593,6 +134132,8 @@ static void yy_parse_failed(
while( yypParser->yyidx>=0 ) yy_pop_parser_stack(yypParser);
/* Here code is inserted which will be executed whenever the
** parser fails */
+/************ Begin %parse_failure code ***************************************/
+/************ End %parse_failure code *****************************************/
sqlite3ParserARG_STORE; /* Suppress warning about unused %extra_argument variable */
}
#endif /* YYNOERRORRECOVERY */
@@ -126603,14 +134144,16 @@ static void yy_parse_failed(
static void yy_syntax_error(
yyParser *yypParser, /* The parser */
int yymajor, /* The major type of the error token */
- YYMINORTYPE yyminor /* The minor type of the error token */
+ sqlite3ParserTOKENTYPE yyminor /* The minor type of the error token */
){
sqlite3ParserARG_FETCH;
-#define TOKEN (yyminor.yy0)
+#define TOKEN yyminor
+/************ Begin %syntax_error code ****************************************/
UNUSED_PARAMETER(yymajor); /* Silence some compiler warnings */
assert( TOKEN.z[0] ); /* The tokenizer always gives us a token */
sqlite3ErrorMsg(pParse, "near \"%T\": syntax error", &TOKEN);
+/************ End %syntax_error code ******************************************/
sqlite3ParserARG_STORE; /* Suppress warning about unused %extra_argument variable */
}
@@ -126629,6 +134172,8 @@ static void yy_accept(
while( yypParser->yyidx>=0 ) yy_pop_parser_stack(yypParser);
/* Here code is inserted which will be executed whenever the
** parser accepts */
+/*********** Begin %parse_accept code *****************************************/
+/*********** End %parse_accept code *******************************************/
sqlite3ParserARG_STORE; /* Suppress warning about unused %extra_argument variable */
}
@@ -126658,7 +134203,7 @@ SQLITE_PRIVATE void sqlite3Parser(
sqlite3ParserARG_PDECL /* Optional %extra_argument parameter */
){
YYMINORTYPE yyminorunion;
- int yyact; /* The parser action. */
+ unsigned int yyact; /* The parser action. */
#if !defined(YYERRORSYMBOL) && !defined(YYNOERRORRECOVERY)
int yyendofinput; /* True if we are at the end of input */
#endif
@@ -126672,18 +134217,23 @@ SQLITE_PRIVATE void sqlite3Parser(
if( yypParser->yyidx<0 ){
#if YYSTACKDEPTH<=0
if( yypParser->yystksz <=0 ){
- /*memset(&yyminorunion, 0, sizeof(yyminorunion));*/
- yyminorunion = yyzerominor;
- yyStackOverflow(yypParser, &yyminorunion);
+ yyStackOverflow(yypParser);
return;
}
#endif
yypParser->yyidx = 0;
+#ifndef YYNOERRORRECOVERY
yypParser->yyerrcnt = -1;
+#endif
yypParser->yystack[0].stateno = 0;
yypParser->yystack[0].major = 0;
+#ifndef NDEBUG
+ if( yyTraceFILE ){
+ fprintf(yyTraceFILE,"%sInitialize. Empty stack. State 0\n",
+ yyTracePrompt);
+ }
+#endif
}
- yyminorunion.yy0 = yyminor;
#if !defined(YYERRORSYMBOL) && !defined(YYNOERRORRECOVERY)
yyendofinput = (yymajor==0);
#endif
@@ -126691,20 +134241,24 @@ SQLITE_PRIVATE void sqlite3Parser(
#ifndef NDEBUG
if( yyTraceFILE ){
- fprintf(yyTraceFILE,"%sInput %s\n",yyTracePrompt,yyTokenName[yymajor]);
+ fprintf(yyTraceFILE,"%sInput '%s'\n",yyTracePrompt,yyTokenName[yymajor]);
}
#endif
do{
yyact = yy_find_shift_action(yypParser,(YYCODETYPE)yymajor);
- if( yyact<YYNSTATE ){
- yy_shift(yypParser,yyact,yymajor,&yyminorunion);
+ if( yyact <= YY_MAX_SHIFTREDUCE ){
+ if( yyact > YY_MAX_SHIFT ) yyact += YY_MIN_REDUCE - YY_MIN_SHIFTREDUCE;
+ yy_shift(yypParser,yyact,yymajor,yyminor);
+#ifndef YYNOERRORRECOVERY
yypParser->yyerrcnt--;
+#endif
yymajor = YYNOCODE;
- }else if( yyact < YYNSTATE + YYNRULE ){
- yy_reduce(yypParser,yyact-YYNSTATE);
+ }else if( yyact <= YY_MAX_REDUCE ){
+ yy_reduce(yypParser,yyact-YY_MIN_REDUCE);
}else{
assert( yyact == YY_ERROR_ACTION );
+ yyminorunion.yy0 = yyminor;
#ifdef YYERRORSYMBOL
int yymx;
#endif
@@ -126734,7 +134288,7 @@ SQLITE_PRIVATE void sqlite3Parser(
**
*/
if( yypParser->yyerrcnt<0 ){
- yy_syntax_error(yypParser,yymajor,yyminorunion);
+ yy_syntax_error(yypParser,yymajor,yyminor);
}
yymx = yypParser->yystack[yypParser->yyidx].major;
if( yymx==YYERRORSYMBOL || yyerrorhit ){
@@ -126744,15 +134298,15 @@ SQLITE_PRIVATE void sqlite3Parser(
yyTracePrompt,yyTokenName[yymajor]);
}
#endif
- yy_destructor(yypParser, (YYCODETYPE)yymajor,&yyminorunion);
+ yy_destructor(yypParser, (YYCODETYPE)yymajor, &yyminorunion);
yymajor = YYNOCODE;
}else{
- while(
+ while(
yypParser->yyidx >= 0 &&
yymx != YYERRORSYMBOL &&
(yyact = yy_find_reduce_action(
yypParser->yystack[yypParser->yyidx].stateno,
- YYERRORSYMBOL)) >= YYNSTATE
+ YYERRORSYMBOL)) >= YY_MIN_REDUCE
){
yy_pop_parser_stack(yypParser);
}
@@ -126761,9 +134315,7 @@ SQLITE_PRIVATE void sqlite3Parser(
yy_parse_failed(yypParser);
yymajor = YYNOCODE;
}else if( yymx!=YYERRORSYMBOL ){
- YYMINORTYPE u2;
- u2.YYERRSYMDT = 0;
- yy_shift(yypParser,yyact,YYERRORSYMBOL,&u2);
+ yy_shift(yypParser,yyact,YYERRORSYMBOL,yyminor);
}
}
yypParser->yyerrcnt = 3;
@@ -126776,7 +134328,7 @@ SQLITE_PRIVATE void sqlite3Parser(
** Applications can set this macro (for example inside %include) if
** they intend to abandon the parse upon the first syntax error seen.
*/
- yy_syntax_error(yypParser,yymajor,yyminorunion);
+ yy_syntax_error(yypParser,yymajor, yyminor);
yy_destructor(yypParser,(YYCODETYPE)yymajor,&yyminorunion);
yymajor = YYNOCODE;
@@ -126791,7 +134343,7 @@ SQLITE_PRIVATE void sqlite3Parser(
** three input tokens have been successfully shifted.
*/
if( yypParser->yyerrcnt<=0 ){
- yy_syntax_error(yypParser,yymajor,yyminorunion);
+ yy_syntax_error(yypParser,yymajor, yyminor);
}
yypParser->yyerrcnt = 3;
yy_destructor(yypParser,(YYCODETYPE)yymajor,&yyminorunion);
@@ -126802,6 +134354,16 @@ SQLITE_PRIVATE void sqlite3Parser(
#endif
}
}while( yymajor!=YYNOCODE && yypParser->yyidx>=0 );
+#ifndef NDEBUG
+ if( yyTraceFILE ){
+ int i;
+ fprintf(yyTraceFILE,"%sReturn. Stack=",yyTracePrompt);
+ for(i=1; i<=yypParser->yyidx; i++)
+ fprintf(yyTraceFILE,"%c%s", i==1 ? '[' : ' ',
+ yyTokenName[yypParser->yystack[i].major]);
+ fprintf(yyTraceFILE,"]\n");
+ }
+#endif
return;
}
@@ -126824,14 +134386,95 @@ SQLITE_PRIVATE void sqlite3Parser(
** individual tokens and sends those tokens one-by-one over to the
** parser for analysis.
*/
+/* #include "sqliteInt.h" */
/* #include <stdlib.h> */
+/* Character classes for tokenizing
+**
+** In the sqlite3GetToken() function, a switch() on aiClass[c] is implemented
+** using a lookup table, whereas a switch() directly on c uses a binary search.
+** The lookup table is much faster. To maximize speed, and to ensure that
+** a lookup table is used, all of the classes need to be small integers and
+** all of them need to be used within the switch.
+*/
+#define CC_X 0 /* The letter 'x', or start of BLOB literal */
+#define CC_KYWD 1 /* Alphabetics or '_'. Usable in a keyword */
+#define CC_ID 2 /* unicode characters usable in IDs */
+#define CC_DIGIT 3 /* Digits */
+#define CC_DOLLAR 4 /* '$' */
+#define CC_VARALPHA 5 /* '@', '#', ':'. Alphabetic SQL variables */
+#define CC_VARNUM 6 /* '?'. Numeric SQL variables */
+#define CC_SPACE 7 /* Space characters */
+#define CC_QUOTE 8 /* '"', '\'', or '`'. String literals, quoted ids */
+#define CC_QUOTE2 9 /* '['. [...] style quoted ids */
+#define CC_PIPE 10 /* '|'. Bitwise OR or concatenate */
+#define CC_MINUS 11 /* '-'. Minus or SQL-style comment */
+#define CC_LT 12 /* '<'. Part of < or <= or <> */
+#define CC_GT 13 /* '>'. Part of > or >= */
+#define CC_EQ 14 /* '='. Part of = or == */
+#define CC_BANG 15 /* '!'. Part of != */
+#define CC_SLASH 16 /* '/'. / or c-style comment */
+#define CC_LP 17 /* '(' */
+#define CC_RP 18 /* ')' */
+#define CC_SEMI 19 /* ';' */
+#define CC_PLUS 20 /* '+' */
+#define CC_STAR 21 /* '*' */
+#define CC_PERCENT 22 /* '%' */
+#define CC_COMMA 23 /* ',' */
+#define CC_AND 24 /* '&' */
+#define CC_TILDA 25 /* '~' */
+#define CC_DOT 26 /* '.' */
+#define CC_ILLEGAL 27 /* Illegal character */
+
+static const unsigned char aiClass[] = {
+#ifdef SQLITE_ASCII
+/* x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xa xb xc xd xe xf */
+/* 0x */ 27, 27, 27, 27, 27, 27, 27, 27, 27, 7, 7, 27, 7, 7, 27, 27,
+/* 1x */ 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27,
+/* 2x */ 7, 15, 8, 5, 4, 22, 24, 8, 17, 18, 21, 20, 23, 11, 26, 16,
+/* 3x */ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 5, 19, 12, 14, 13, 6,
+/* 4x */ 5, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+/* 5x */ 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 9, 27, 27, 27, 1,
+/* 6x */ 8, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+/* 7x */ 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 27, 10, 27, 25, 27,
+/* 8x */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+/* 9x */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+/* Ax */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+/* Bx */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+/* Cx */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+/* Dx */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+/* Ex */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+/* Fx */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2
+#endif
+#ifdef SQLITE_EBCDIC
+/* x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xa xb xc xd xe xf */
+/* 0x */ 27, 27, 27, 27, 27, 7, 27, 27, 27, 27, 27, 27, 7, 7, 27, 27,
+/* 1x */ 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27,
+/* 2x */ 27, 27, 27, 27, 27, 7, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27,
+/* 3x */ 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27,
+/* 4x */ 7, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 12, 17, 20, 10,
+/* 5x */ 24, 27, 27, 27, 27, 27, 27, 27, 27, 27, 15, 4, 21, 18, 19, 27,
+/* 6x */ 11, 16, 27, 27, 27, 27, 27, 27, 27, 27, 27, 23, 22, 1, 13, 7,
+/* 7x */ 27, 27, 27, 27, 27, 27, 27, 27, 27, 8, 5, 5, 5, 8, 14, 8,
+/* 8x */ 27, 1, 1, 1, 1, 1, 1, 1, 1, 1, 27, 27, 27, 27, 27, 27,
+/* 9x */ 27, 1, 1, 1, 1, 1, 1, 1, 1, 1, 27, 27, 27, 27, 27, 27,
+/* 9x */ 25, 1, 1, 1, 1, 1, 1, 0, 1, 1, 27, 27, 27, 27, 27, 27,
+/* Bx */ 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 9, 27, 27, 27, 27, 27,
+/* Cx */ 27, 1, 1, 1, 1, 1, 1, 1, 1, 1, 27, 27, 27, 27, 27, 27,
+/* Dx */ 27, 1, 1, 1, 1, 1, 1, 1, 1, 1, 27, 27, 27, 27, 27, 27,
+/* Ex */ 27, 27, 1, 1, 1, 1, 1, 0, 1, 1, 27, 27, 27, 27, 27, 27,
+/* Fx */ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 27, 27, 27, 27, 27, 27,
+#endif
+};
+
/*
-** The charMap() macro maps alphabetic characters into their
+** The charMap() macro maps alphabetic characters (only) into their
** lower-case ASCII equivalent. On ASCII machines, this is just
** an upper-to-lower case map. On EBCDIC machines we also need
-** to adjust the encoding. Only alphabetic characters and underscores
-** need to be translated.
+** to adjust the encoding. The mapping is only valid for alphabetics
+** which are the only characters for which this feature is used.
+**
+** Used by keywordhash.h
*/
#ifdef SQLITE_ASCII
# define charMap(X) sqlite3UpperToLower[(unsigned char)X]
@@ -126865,7 +134508,7 @@ const unsigned char ebcdicToAscii[] = {
** returned. If the input is not a keyword, TK_ID is returned.
**
** The implementation of this routine was generated by a program,
-** mkkeywordhash.h, located in the tool subdirectory of the distribution.
+** mkkeywordhash.c, located in the tool subdirectory of the distribution.
** The output of the mkkeywordhash.c program is written into a file
** named keywordhash.h and then included into this source file by
** the #include below.
@@ -126886,7 +134529,7 @@ const unsigned char ebcdicToAscii[] = {
** on platforms with limited memory.
*/
/* Hash score: 182 */
-static int keywordCode(const char *z, int n){
+static int keywordCode(const char *z, int n, int *pType){
/* zText[] encodes 834 bytes of keywords in 554 bytes */
/* REINDEXEDESCAPEACHECKEYBEFOREIGNOREGEXPLAINSTEADDATABASELECT */
/* ABLEFTHENDEFERRABLELSEXCEPTRANSACTIONATURALTERAISEXCLUSIVE */
@@ -127006,13 +134649,21 @@ static int keywordCode(const char *z, int n){
TK_JOIN_KW, TK_ROLLBACK, TK_ROW, TK_UNION, TK_USING,
TK_VACUUM, TK_VIEW, TK_INITIALLY, TK_ALL,
};
- int h, i;
- if( n<2 ) return TK_ID;
- h = ((charMap(z[0])*4) ^
- (charMap(z[n-1])*3) ^
- n) % 127;
- for(i=((int)aHash[h])-1; i>=0; i=((int)aNext[i])-1){
- if( aLen[i]==n && sqlite3StrNICmp(&zText[aOffset[i]],z,n)==0 ){
+ int i, j;
+ const char *zKW;
+ if( n>=2 ){
+ i = ((charMap(z[0])*4) ^ (charMap(z[n-1])*3) ^ n) % 127;
+ for(i=((int)aHash[i])-1; i>=0; i=((int)aNext[i])-1){
+ if( aLen[i]!=n ) continue;
+ j = 0;
+ zKW = &zText[aOffset[i]];
+#ifdef SQLITE_ASCII
+ while( j<n && (z[j]&~0x20)==zKW[j] ){ j++; }
+#endif
+#ifdef SQLITE_EBCDIC
+ while( j<n && toupper(z[j])==zKW[j] ){ j++; }
+#endif
+ if( j<n ) continue;
testcase( i==0 ); /* REINDEX */
testcase( i==1 ); /* INDEXED */
testcase( i==2 ); /* INDEX */
@@ -127137,13 +134788,16 @@ static int keywordCode(const char *z, int n){
testcase( i==121 ); /* VIEW */
testcase( i==122 ); /* INITIALLY */
testcase( i==123 ); /* ALL */
- return aCode[i];
+ *pType = aCode[i];
+ break;
}
}
- return TK_ID;
+ return n;
}
SQLITE_PRIVATE int sqlite3KeywordCode(const unsigned char *z, int n){
- return keywordCode((char*)z, n);
+ int id = TK_ID;
+ keywordCode((char*)z, n, &id);
+ return id;
}
#define SQLITE_N_KEYWORD 124
@@ -127188,17 +134842,23 @@ SQLITE_PRIVATE const char sqlite3IsEbcdicIdChar[] = {
};
#define IdChar(C) (((c=C)>=0x42 && sqlite3IsEbcdicIdChar[c-0x40]))
#endif
+
+/* Make the IdChar function accessible from ctime.c */
+#ifndef SQLITE_OMIT_COMPILEOPTION_DIAGS
SQLITE_PRIVATE int sqlite3IsIdChar(u8 c){ return IdChar(c); }
+#endif
/*
-** Return the length of the token that begins at z[0].
+** Return the length (in bytes) of the token that begins at z[0].
** Store the token type in *tokenType before returning.
*/
SQLITE_PRIVATE int sqlite3GetToken(const unsigned char *z, int *tokenType){
int i, c;
- switch( *z ){
- case ' ': case '\t': case '\n': case '\f': case '\r': {
+ switch( aiClass[*z] ){ /* Switch on the character-class of the first byte
+ ** of the token. See the comment on the CC_ defines
+ ** above. */
+ case CC_SPACE: {
testcase( z[0]==' ' );
testcase( z[0]=='\t' );
testcase( z[0]=='\n' );
@@ -127208,7 +134868,7 @@ SQLITE_PRIVATE int sqlite3GetToken(const unsigned char *z, int *tokenType){
*tokenType = TK_SPACE;
return i;
}
- case '-': {
+ case CC_MINUS: {
if( z[1]=='-' ){
for(i=2; (c=z[i])!=0 && c!='\n'; i++){}
*tokenType = TK_SPACE; /* IMP: R-22934-25134 */
@@ -127217,27 +134877,27 @@ SQLITE_PRIVATE int sqlite3GetToken(const unsigned char *z, int *tokenType){
*tokenType = TK_MINUS;
return 1;
}
- case '(': {
+ case CC_LP: {
*tokenType = TK_LP;
return 1;
}
- case ')': {
+ case CC_RP: {
*tokenType = TK_RP;
return 1;
}
- case ';': {
+ case CC_SEMI: {
*tokenType = TK_SEMI;
return 1;
}
- case '+': {
+ case CC_PLUS: {
*tokenType = TK_PLUS;
return 1;
}
- case '*': {
+ case CC_STAR: {
*tokenType = TK_STAR;
return 1;
}
- case '/': {
+ case CC_SLASH: {
if( z[1]!='*' || z[2]==0 ){
*tokenType = TK_SLASH;
return 1;
@@ -127247,15 +134907,15 @@ SQLITE_PRIVATE int sqlite3GetToken(const unsigned char *z, int *tokenType){
*tokenType = TK_SPACE; /* IMP: R-22934-25134 */
return i;
}
- case '%': {
+ case CC_PERCENT: {
*tokenType = TK_REM;
return 1;
}
- case '=': {
+ case CC_EQ: {
*tokenType = TK_EQ;
return 1 + (z[1]=='=');
}
- case '<': {
+ case CC_LT: {
if( (c=z[1])=='=' ){
*tokenType = TK_LE;
return 2;
@@ -127270,7 +134930,7 @@ SQLITE_PRIVATE int sqlite3GetToken(const unsigned char *z, int *tokenType){
return 1;
}
}
- case '>': {
+ case CC_GT: {
if( (c=z[1])=='=' ){
*tokenType = TK_GE;
return 2;
@@ -127282,16 +134942,16 @@ SQLITE_PRIVATE int sqlite3GetToken(const unsigned char *z, int *tokenType){
return 1;
}
}
- case '!': {
+ case CC_BANG: {
if( z[1]!='=' ){
*tokenType = TK_ILLEGAL;
- return 2;
+ return 1;
}else{
*tokenType = TK_NE;
return 2;
}
}
- case '|': {
+ case CC_PIPE: {
if( z[1]!='|' ){
*tokenType = TK_BITOR;
return 1;
@@ -127300,21 +134960,19 @@ SQLITE_PRIVATE int sqlite3GetToken(const unsigned char *z, int *tokenType){
return 2;
}
}
- case ',': {
+ case CC_COMMA: {
*tokenType = TK_COMMA;
return 1;
}
- case '&': {
+ case CC_AND: {
*tokenType = TK_BITAND;
return 1;
}
- case '~': {
+ case CC_TILDA: {
*tokenType = TK_BITNOT;
return 1;
}
- case '`':
- case '\'':
- case '"': {
+ case CC_QUOTE: {
int delim = z[0];
testcase( delim=='`' );
testcase( delim=='\'' );
@@ -127339,7 +134997,7 @@ SQLITE_PRIVATE int sqlite3GetToken(const unsigned char *z, int *tokenType){
return i;
}
}
- case '.': {
+ case CC_DOT: {
#ifndef SQLITE_OMIT_FLOATING_POINT
if( !sqlite3Isdigit(z[1]) )
#endif
@@ -127350,8 +135008,7 @@ SQLITE_PRIVATE int sqlite3GetToken(const unsigned char *z, int *tokenType){
/* If the next character is a digit, this is a floating point
** number that begins with ".". Fall thru into the next case */
}
- case '0': case '1': case '2': case '3': case '4':
- case '5': case '6': case '7': case '8': case '9': {
+ case CC_DIGIT: {
testcase( z[0]=='0' ); testcase( z[0]=='1' ); testcase( z[0]=='2' );
testcase( z[0]=='3' ); testcase( z[0]=='4' ); testcase( z[0]=='5' );
testcase( z[0]=='6' ); testcase( z[0]=='7' ); testcase( z[0]=='8' );
@@ -127386,22 +135043,18 @@ SQLITE_PRIVATE int sqlite3GetToken(const unsigned char *z, int *tokenType){
}
return i;
}
- case '[': {
+ case CC_QUOTE2: {
for(i=1, c=z[0]; c!=']' && (c=z[i])!=0; i++){}
*tokenType = c==']' ? TK_ID : TK_ILLEGAL;
return i;
}
- case '?': {
+ case CC_VARNUM: {
*tokenType = TK_VARIABLE;
for(i=1; sqlite3Isdigit(z[i]); i++){}
return i;
}
-#ifndef SQLITE_OMIT_TCL_VARIABLE
- case '$':
-#endif
- case '@': /* For compatibility with MS SQL Server */
- case '#':
- case ':': {
+ case CC_DOLLAR:
+ case CC_VARALPHA: {
int n = 0;
testcase( z[0]=='$' ); testcase( z[0]=='@' );
testcase( z[0]==':' ); testcase( z[0]=='#' );
@@ -127430,8 +135083,20 @@ SQLITE_PRIVATE int sqlite3GetToken(const unsigned char *z, int *tokenType){
if( n==0 ) *tokenType = TK_ILLEGAL;
return i;
}
+ case CC_KYWD: {
+ for(i=1; aiClass[z[i]]<=CC_KYWD; i++){}
+ if( IdChar(z[i]) ){
+ /* This token started out using characters that can appear in keywords,
+ ** but z[i] is a character not allowed within keywords, so this must
+ ** be an identifier instead */
+ i++;
+ break;
+ }
+ *tokenType = TK_ID;
+ return keywordCode((char*)z, i, tokenType);
+ }
+ case CC_X: {
#ifndef SQLITE_OMIT_BLOB_LITERAL
- case 'x': case 'X': {
testcase( z[0]=='x' ); testcase( z[0]=='X' );
if( z[1]=='\'' ){
*tokenType = TK_BLOB;
@@ -127443,20 +135108,22 @@ SQLITE_PRIVATE int sqlite3GetToken(const unsigned char *z, int *tokenType){
if( z[i] ) i++;
return i;
}
- /* Otherwise fall through to the next case */
- }
#endif
+ /* If it is not a BLOB literal, then it must be an ID, since no
+ ** SQL keywords start with the letter 'x'. Fall through */
+ }
+ case CC_ID: {
+ i = 1;
+ break;
+ }
default: {
- if( !IdChar(*z) ){
- break;
- }
- for(i=1; IdChar(z[i]); i++){}
- *tokenType = keywordCode((char*)z, i);
- return i;
+ *tokenType = TK_ILLEGAL;
+ return 1;
}
}
- *tokenType = TK_ILLEGAL;
- return 1;
+ while( IdChar(z[i]) ){ i++; }
+ *tokenType = TK_ID;
+ return i;
}
/*
@@ -127472,7 +135139,6 @@ SQLITE_PRIVATE int sqlite3RunParser(Parse *pParse, const char *zSql, char **pzEr
void *pEngine; /* The LEMON-generated LALR(1) parser */
int tokenType; /* type of the next token */
int lastTokenParsed = -1; /* type of the previous token */
- u8 enableLookaside; /* Saved value of db->lookaside.bEnabled */
sqlite3 *db = pParse->db; /* The database connection */
int mxSqlLen; /* Max length of an SQL string */
@@ -127485,19 +135151,18 @@ SQLITE_PRIVATE int sqlite3RunParser(Parse *pParse, const char *zSql, char **pzEr
pParse->zTail = zSql;
i = 0;
assert( pzErrMsg!=0 );
+ /* sqlite3ParserTrace(stdout, "parser: "); */
pEngine = sqlite3ParserAlloc(sqlite3Malloc);
if( pEngine==0 ){
- db->mallocFailed = 1;
- return SQLITE_NOMEM;
+ sqlite3OomFault(db);
+ return SQLITE_NOMEM_BKPT;
}
assert( pParse->pNewTable==0 );
assert( pParse->pNewTrigger==0 );
assert( pParse->nVar==0 );
assert( pParse->nzVar==0 );
assert( pParse->azVar==0 );
- enableLookaside = db->lookaside.bEnabled;
- if( db->lookaside.pStart ) db->lookaside.bEnabled = 1;
- while( !db->mallocFailed && zSql[i]!=0 ){
+ while( zSql[i]!=0 ){
assert( i>=0 );
pParse->sLastToken.z = &zSql[i];
pParse->sLastToken.n = sqlite3GetToken((unsigned char*)&zSql[i],&tokenType);
@@ -127506,40 +135171,29 @@ SQLITE_PRIVATE int sqlite3RunParser(Parse *pParse, const char *zSql, char **pzEr
pParse->rc = SQLITE_TOOBIG;
break;
}
- switch( tokenType ){
- case TK_SPACE: {
- if( db->u1.isInterrupted ){
- sqlite3ErrorMsg(pParse, "interrupt");
- pParse->rc = SQLITE_INTERRUPT;
- goto abort_parse;
- }
+ if( tokenType>=TK_SPACE ){
+ assert( tokenType==TK_SPACE || tokenType==TK_ILLEGAL );
+ if( db->u1.isInterrupted ){
+ pParse->rc = SQLITE_INTERRUPT;
break;
}
- case TK_ILLEGAL: {
+ if( tokenType==TK_ILLEGAL ){
sqlite3ErrorMsg(pParse, "unrecognized token: \"%T\"",
&pParse->sLastToken);
- goto abort_parse;
- }
- case TK_SEMI: {
- pParse->zTail = &zSql[i];
- /* Fall thru into the default case */
- }
- default: {
- sqlite3Parser(pEngine, tokenType, pParse->sLastToken, pParse);
- lastTokenParsed = tokenType;
- if( pParse->rc!=SQLITE_OK ){
- goto abort_parse;
- }
break;
}
+ }else{
+ sqlite3Parser(pEngine, tokenType, pParse->sLastToken, pParse);
+ lastTokenParsed = tokenType;
+ if( pParse->rc!=SQLITE_OK || db->mallocFailed ) break;
}
}
-abort_parse:
assert( nErr==0 );
- if( zSql[i]==0 && pParse->rc==SQLITE_OK && db->mallocFailed==0 ){
+ pParse->zTail = &zSql[i];
+ if( pParse->rc==SQLITE_OK && db->mallocFailed==0 ){
+ assert( zSql[i]==0 );
if( lastTokenParsed!=TK_SEMI ){
sqlite3Parser(pEngine, TK_SEMI, pParse->sLastToken, pParse);
- pParse->zTail = &zSql[i];
}
if( pParse->rc==SQLITE_OK && db->mallocFailed==0 ){
sqlite3Parser(pEngine, 0, pParse->sLastToken, pParse);
@@ -127547,18 +135201,17 @@ abort_parse:
}
#ifdef YYTRACKMAXSTACKDEPTH
sqlite3_mutex_enter(sqlite3MallocMutex());
- sqlite3StatusSet(SQLITE_STATUS_PARSER_STACK,
+ sqlite3StatusHighwater(SQLITE_STATUS_PARSER_STACK,
sqlite3ParserStackPeak(pEngine)
);
sqlite3_mutex_leave(sqlite3MallocMutex());
#endif /* YYDEBUG */
sqlite3ParserFree(pEngine, sqlite3_free);
- db->lookaside.bEnabled = enableLookaside;
if( db->mallocFailed ){
- pParse->rc = SQLITE_NOMEM;
+ pParse->rc = SQLITE_NOMEM_BKPT;
}
if( pParse->rc!=SQLITE_OK && pParse->rc!=SQLITE_DONE && pParse->zErrMsg==0 ){
- sqlite3SetString(&pParse->zErrMsg, db, "%s", sqlite3ErrStr(pParse->rc));
+ pParse->zErrMsg = sqlite3MPrintf(db, "%s", sqlite3ErrStr(pParse->rc));
}
assert( pzErrMsg!=0 );
if( pParse->zErrMsg ){
@@ -127590,7 +135243,7 @@ abort_parse:
sqlite3DeleteTable(db, pParse->pNewTable);
}
- if( pParse->bFreeWith ) sqlite3WithDelete(db, pParse->pWith);
+ if( pParse->pWithToFree ) sqlite3WithDelete(db, pParse->pWithToFree);
sqlite3DeleteTrigger(db, pParse->pNewTrigger);
for(i=pParse->nzVar-1; i>=0; i--) sqlite3DbFree(db, pParse->azVar[i]);
sqlite3DbFree(db, pParse->azVar);
@@ -127628,6 +135281,7 @@ abort_parse:
** separating it out, the code will be automatically omitted from
** static links that do not use it.
*/
+/* #include "sqliteInt.h" */
#ifndef SQLITE_OMIT_COMPLETE
/*
@@ -127892,10 +135546,10 @@ SQLITE_API int SQLITE_STDCALL sqlite3_complete16(const void *zSql){
if( zSql8 ){
rc = sqlite3_complete(zSql8);
}else{
- rc = SQLITE_NOMEM;
+ rc = SQLITE_NOMEM_BKPT;
}
sqlite3ValueFree(pVal);
- return sqlite3ApiExit(0, rc);
+ return rc & 0xff;
}
#endif /* SQLITE_OMIT_UTF16 */
#endif /* SQLITE_OMIT_COMPLETE */
@@ -127918,6 +135572,7 @@ SQLITE_API int SQLITE_STDCALL sqlite3_complete16(const void *zSql){
** other files are for internal use by SQLite and should not be
** accessed by users of the library.
*/
+/* #include "sqliteInt.h" */
#ifdef SQLITE_ENABLE_FTS3
/************** Include fts3.h in the middle of main.c ***********************/
@@ -127937,6 +135592,7 @@ SQLITE_API int SQLITE_STDCALL sqlite3_complete16(const void *zSql){
** This header file is used by programs that want to link against the
** FTS3 library. All it does is declare the sqlite3Fts3Init() interface.
*/
+/* #include "sqlite3.h" */
#if 0
extern "C" {
@@ -127969,6 +135625,7 @@ SQLITE_PRIVATE int sqlite3Fts3Init(sqlite3 *db);
** This header file is used by programs that want to link against the
** RTREE library. All it does is declare the sqlite3RtreeInit() interface.
*/
+/* #include "sqlite3.h" */
#if 0
extern "C" {
@@ -128001,6 +135658,7 @@ SQLITE_PRIVATE int sqlite3RtreeInit(sqlite3 *db);
** This header file is used by programs that want to link against the
** ICU extension. All it does is declare the sqlite3IcuInit() interface.
*/
+/* #include "sqlite3.h" */
#if 0
extern "C" {
@@ -128016,6 +135674,12 @@ SQLITE_PRIVATE int sqlite3IcuInit(sqlite3 *db);
/************** End of sqliteicu.h *******************************************/
/************** Continuing where we left off in main.c ***********************/
#endif
+#ifdef SQLITE_ENABLE_JSON1
+SQLITE_PRIVATE int sqlite3Json1Init(sqlite3*);
+#endif
+#ifdef SQLITE_ENABLE_FTS5
+SQLITE_PRIVATE int sqlite3Fts5Init(sqlite3*);
+#endif
#ifndef SQLITE_AMALGAMATION
/* IMPLEMENTATION-OF: R-46656-45156 The sqlite3_version[] string constant
@@ -128172,7 +135836,7 @@ SQLITE_API int SQLITE_STDCALL sqlite3_initialize(void){
sqlite3GlobalConfig.pInitMutex =
sqlite3MutexAlloc(SQLITE_MUTEX_RECURSIVE);
if( sqlite3GlobalConfig.bCoreMutex && !sqlite3GlobalConfig.pInitMutex ){
- rc = SQLITE_NOMEM;
+ rc = SQLITE_NOMEM_BKPT;
}
}
}
@@ -128203,10 +135867,15 @@ SQLITE_API int SQLITE_STDCALL sqlite3_initialize(void){
*/
sqlite3_mutex_enter(sqlite3GlobalConfig.pInitMutex);
if( sqlite3GlobalConfig.isInit==0 && sqlite3GlobalConfig.inProgress==0 ){
- FuncDefHash *pHash = &GLOBAL(FuncDefHash, sqlite3GlobalFunctions);
sqlite3GlobalConfig.inProgress = 1;
- memset(pHash, 0, sizeof(sqlite3GlobalFunctions));
- sqlite3RegisterGlobalFunctions();
+#ifdef SQLITE_ENABLE_SQLLOG
+ {
+ extern void sqlite3_init_sqllog(void);
+ sqlite3_init_sqllog();
+ }
+#endif
+ memset(&sqlite3BuiltinFunctions, 0, sizeof(sqlite3BuiltinFunctions));
+ sqlite3RegisterBuiltinFunctions();
if( sqlite3GlobalConfig.isPCacheInit==0 ){
rc = sqlite3PcacheInitialize();
}
@@ -128424,9 +136093,10 @@ SQLITE_API int SQLITE_CDECL sqlite3_config(int op, ...){
break;
}
case SQLITE_CONFIG_PAGECACHE: {
- /* EVIDENCE-OF: R-31408-40510 There are three arguments to
- ** SQLITE_CONFIG_PAGECACHE: A pointer to 8-byte aligned memory, the size
- ** of each page buffer (sz), and the number of pages (N). */
+ /* EVIDENCE-OF: R-18761-36601 There are three arguments to
+ ** SQLITE_CONFIG_PAGECACHE: A pointer to 8-byte aligned memory (pMem),
+ ** the size of each page cache line (sz), and the number of cache lines
+ ** (N). */
sqlite3GlobalConfig.pPage = va_arg(ap, void*);
sqlite3GlobalConfig.szPage = va_arg(ap, int);
sqlite3GlobalConfig.nPage = va_arg(ap, int);
@@ -128612,6 +136282,11 @@ SQLITE_API int SQLITE_CDECL sqlite3_config(int op, ...){
break;
}
+ case SQLITE_CONFIG_STMTJRNL_SPILL: {
+ sqlite3GlobalConfig.nStmtSpill = va_arg(ap, int);
+ break;
+ }
+
default: {
rc = SQLITE_ERROR;
break;
@@ -128633,6 +136308,7 @@ SQLITE_API int SQLITE_CDECL sqlite3_config(int op, ...){
** the lookaside memory.
*/
static int setupLookaside(sqlite3 *db, void *pBuf, int sz, int cnt){
+#ifndef SQLITE_OMIT_LOOKASIDE
void *pStart;
if( db->lookaside.nOut ){
return SQLITE_BUSY;
@@ -128675,14 +136351,15 @@ static int setupLookaside(sqlite3 *db, void *pBuf, int sz, int cnt){
p = (LookasideSlot*)&((u8*)p)[sz];
}
db->lookaside.pEnd = p;
- db->lookaside.bEnabled = 1;
+ db->lookaside.bDisable = 0;
db->lookaside.bMalloced = pBuf==0 ?1:0;
}else{
db->lookaside.pStart = db;
db->lookaside.pEnd = db;
- db->lookaside.bEnabled = 0;
+ db->lookaside.bDisable = 1;
db->lookaside.bMalloced = 0;
}
+#endif /* SQLITE_OMIT_LOOKASIDE */
return SQLITE_OK;
}
@@ -128724,6 +136401,36 @@ SQLITE_API int SQLITE_STDCALL sqlite3_db_release_memory(sqlite3 *db){
}
/*
+** Flush any dirty pages in the pager-cache for any attached database
+** to disk.
+*/
+SQLITE_API int SQLITE_STDCALL sqlite3_db_cacheflush(sqlite3 *db){
+ int i;
+ int rc = SQLITE_OK;
+ int bSeenBusy = 0;
+
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( !sqlite3SafetyCheckOk(db) ) return SQLITE_MISUSE_BKPT;
+#endif
+ sqlite3_mutex_enter(db->mutex);
+ sqlite3BtreeEnterAll(db);
+ for(i=0; rc==SQLITE_OK && i<db->nDb; i++){
+ Btree *pBt = db->aDb[i].pBt;
+ if( pBt && sqlite3BtreeIsInTrans(pBt) ){
+ Pager *pPager = sqlite3BtreePager(pBt);
+ rc = sqlite3PagerFlush(pPager);
+ if( rc==SQLITE_BUSY ){
+ bSeenBusy = 1;
+ rc = SQLITE_OK;
+ }
+ }
+ }
+ sqlite3BtreeLeaveAll(db);
+ sqlite3_mutex_leave(db->mutex);
+ return ((rc==SQLITE_OK && bSeenBusy) ? SQLITE_BUSY : rc);
+}
+
+/*
** Configuration settings for an individual database connection
*/
SQLITE_API int SQLITE_CDECL sqlite3_db_config(sqlite3 *db, int op, ...){
@@ -128743,8 +136450,10 @@ SQLITE_API int SQLITE_CDECL sqlite3_db_config(sqlite3 *db, int op, ...){
int op; /* The opcode */
u32 mask; /* Mask of the bit in sqlite3.flags to set/clear */
} aFlagOp[] = {
- { SQLITE_DBCONFIG_ENABLE_FKEY, SQLITE_ForeignKeys },
- { SQLITE_DBCONFIG_ENABLE_TRIGGER, SQLITE_EnableTrigger },
+ { SQLITE_DBCONFIG_ENABLE_FKEY, SQLITE_ForeignKeys },
+ { SQLITE_DBCONFIG_ENABLE_TRIGGER, SQLITE_EnableTrigger },
+ { SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER, SQLITE_Fts3Tokenizer },
+ { SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION, SQLITE_LoadExtension },
};
unsigned int i;
rc = SQLITE_ERROR; /* IMP: R-42790-23372 */
@@ -128904,7 +136613,7 @@ SQLITE_PRIVATE void sqlite3CloseSavepoints(sqlite3 *db){
** with SQLITE_ANY as the encoding.
*/
static void functionDestroy(sqlite3 *db, FuncDef *p){
- FuncDestructor *pDestructor = p->pDestructor;
+ FuncDestructor *pDestructor = p->u.pDestructor;
if( pDestructor ){
pDestructor->nRef--;
if( pDestructor->nRef==0 ){
@@ -128921,17 +136630,23 @@ static void functionDestroy(sqlite3 *db, FuncDef *p){
static void disconnectAllVtab(sqlite3 *db){
#ifndef SQLITE_OMIT_VIRTUALTABLE
int i;
+ HashElem *p;
sqlite3BtreeEnterAll(db);
for(i=0; i<db->nDb; i++){
Schema *pSchema = db->aDb[i].pSchema;
if( db->aDb[i].pSchema ){
- HashElem *p;
for(p=sqliteHashFirst(&pSchema->tblHash); p; p=sqliteHashNext(p)){
Table *pTab = (Table *)sqliteHashData(p);
if( IsVirtual(pTab) ) sqlite3VtabDisconnect(db, pTab);
}
}
}
+ for(p=sqliteHashFirst(&db->aModule); p; p=sqliteHashNext(p)){
+ Module *pMod = (Module *)sqliteHashData(p);
+ if( pMod->pEpoTab ){
+ sqlite3VtabDisconnect(db, pMod->pEpoTab);
+ }
+ }
sqlite3VtabUnlockList(db);
sqlite3BtreeLeaveAll(db);
#else
@@ -129080,18 +136795,17 @@ SQLITE_PRIVATE void sqlite3LeaveMutexAndCloseZombie(sqlite3 *db){
*/
sqlite3ConnectionClosed(db);
- for(j=0; j<ArraySize(db->aFunc.a); j++){
- FuncDef *pNext, *pHash, *p;
- for(p=db->aFunc.a[j]; p; p=pHash){
- pHash = p->pHash;
- while( p ){
- functionDestroy(db, p);
- pNext = p->pNext;
- sqlite3DbFree(db, p);
- p = pNext;
- }
- }
+ for(i=sqliteHashFirst(&db->aFunc); i; i=sqliteHashNext(i)){
+ FuncDef *pNext, *p;
+ p = sqliteHashData(i);
+ do{
+ functionDestroy(db, p);
+ pNext = p->pNext;
+ sqlite3DbFree(db, p);
+ p = pNext;
+ }while( p );
}
+ sqlite3HashClear(&db->aFunc);
for(i=sqliteHashFirst(&db->aCollSeq); i; i=sqliteHashNext(i)){
CollSeq *pColl = (CollSeq *)sqliteHashData(i);
/* Invoke any destructors registered for collation sequence user data. */
@@ -129109,6 +136823,7 @@ SQLITE_PRIVATE void sqlite3LeaveMutexAndCloseZombie(sqlite3 *db){
if( pMod->xDestroy ){
pMod->xDestroy(pMod->pAux);
}
+ sqlite3VtabEponymousTableClear(db, pMod);
sqlite3DbFree(db, pMod);
}
sqlite3HashClear(&db->aModule);
@@ -129514,7 +137229,7 @@ SQLITE_PRIVATE int sqlite3CreateFunc(
int nArg,
int enc,
void *pUserData,
- void (*xFunc)(sqlite3_context*,int,sqlite3_value **),
+ void (*xSFunc)(sqlite3_context*,int,sqlite3_value **),
void (*xStep)(sqlite3_context*,int,sqlite3_value **),
void (*xFinal)(sqlite3_context*),
FuncDestructor *pDestructor
@@ -129525,9 +137240,9 @@ SQLITE_PRIVATE int sqlite3CreateFunc(
assert( sqlite3_mutex_held(db->mutex) );
if( zFunctionName==0 ||
- (xFunc && (xFinal || xStep)) ||
- (!xFunc && (xFinal && !xStep)) ||
- (!xFunc && (!xFinal && xStep)) ||
+ (xSFunc && (xFinal || xStep)) ||
+ (!xSFunc && (xFinal && !xStep)) ||
+ (!xSFunc && (!xFinal && xStep)) ||
(nArg<-1 || nArg>SQLITE_MAX_FUNCTION_ARG) ||
(255<(nName = sqlite3Strlen30( zFunctionName))) ){
return SQLITE_MISUSE_BKPT;
@@ -129550,10 +137265,10 @@ SQLITE_PRIVATE int sqlite3CreateFunc(
}else if( enc==SQLITE_ANY ){
int rc;
rc = sqlite3CreateFunc(db, zFunctionName, nArg, SQLITE_UTF8|extraFlags,
- pUserData, xFunc, xStep, xFinal, pDestructor);
+ pUserData, xSFunc, xStep, xFinal, pDestructor);
if( rc==SQLITE_OK ){
rc = sqlite3CreateFunc(db, zFunctionName, nArg, SQLITE_UTF16LE|extraFlags,
- pUserData, xFunc, xStep, xFinal, pDestructor);
+ pUserData, xSFunc, xStep, xFinal, pDestructor);
}
if( rc!=SQLITE_OK ){
return rc;
@@ -129569,7 +137284,7 @@ SQLITE_PRIVATE int sqlite3CreateFunc(
** is being overridden/deleted but there are no active VMs, allow the
** operation to continue but invalidate all precompiled statements.
*/
- p = sqlite3FindFunction(db, zFunctionName, nName, nArg, (u8)enc, 0);
+ p = sqlite3FindFunction(db, zFunctionName, nArg, (u8)enc, 0);
if( p && (p->funcFlags & SQLITE_FUNC_ENCMASK)==enc && p->nArg==nArg ){
if( db->nVdbeActive ){
sqlite3ErrorWithMsg(db, SQLITE_BUSY,
@@ -129581,10 +137296,10 @@ SQLITE_PRIVATE int sqlite3CreateFunc(
}
}
- p = sqlite3FindFunction(db, zFunctionName, nName, nArg, (u8)enc, 1);
+ p = sqlite3FindFunction(db, zFunctionName, nArg, (u8)enc, 1);
assert(p || db->mallocFailed);
if( !p ){
- return SQLITE_NOMEM;
+ return SQLITE_NOMEM_BKPT;
}
/* If an older version of the function with a configured destructor is
@@ -129594,11 +137309,10 @@ SQLITE_PRIVATE int sqlite3CreateFunc(
if( pDestructor ){
pDestructor->nRef++;
}
- p->pDestructor = pDestructor;
+ p->u.pDestructor = pDestructor;
p->funcFlags = (p->funcFlags & SQLITE_FUNC_ENCMASK) | extraFlags;
testcase( p->funcFlags & SQLITE_DETERMINISTIC );
- p->xFunc = xFunc;
- p->xStep = xStep;
+ p->xSFunc = xSFunc ? xSFunc : xStep;
p->xFinalize = xFinal;
p->pUserData = pUserData;
p->nArg = (u16)nArg;
@@ -129614,11 +137328,11 @@ SQLITE_API int SQLITE_STDCALL sqlite3_create_function(
int nArg,
int enc,
void *p,
- void (*xFunc)(sqlite3_context*,int,sqlite3_value **),
+ void (*xSFunc)(sqlite3_context*,int,sqlite3_value **),
void (*xStep)(sqlite3_context*,int,sqlite3_value **),
void (*xFinal)(sqlite3_context*)
){
- return sqlite3_create_function_v2(db, zFunc, nArg, enc, p, xFunc, xStep,
+ return sqlite3_create_function_v2(db, zFunc, nArg, enc, p, xSFunc, xStep,
xFinal, 0);
}
@@ -129628,7 +137342,7 @@ SQLITE_API int SQLITE_STDCALL sqlite3_create_function_v2(
int nArg,
int enc,
void *p,
- void (*xFunc)(sqlite3_context*,int,sqlite3_value **),
+ void (*xSFunc)(sqlite3_context*,int,sqlite3_value **),
void (*xStep)(sqlite3_context*,int,sqlite3_value **),
void (*xFinal)(sqlite3_context*),
void (*xDestroy)(void *)
@@ -129651,7 +137365,7 @@ SQLITE_API int SQLITE_STDCALL sqlite3_create_function_v2(
pArg->xDestroy = xDestroy;
pArg->pUserData = p;
}
- rc = sqlite3CreateFunc(db, zFunc, nArg, enc, p, xFunc, xStep, xFinal, pArg);
+ rc = sqlite3CreateFunc(db, zFunc, nArg, enc, p, xSFunc, xStep, xFinal, pArg);
if( pArg && pArg->nRef==0 ){
assert( rc!=SQLITE_OK );
xDestroy(p);
@@ -129671,7 +137385,7 @@ SQLITE_API int SQLITE_STDCALL sqlite3_create_function16(
int nArg,
int eTextRep,
void *p,
- void (*xFunc)(sqlite3_context*,int,sqlite3_value**),
+ void (*xSFunc)(sqlite3_context*,int,sqlite3_value**),
void (*xStep)(sqlite3_context*,int,sqlite3_value**),
void (*xFinal)(sqlite3_context*)
){
@@ -129684,7 +137398,7 @@ SQLITE_API int SQLITE_STDCALL sqlite3_create_function16(
sqlite3_mutex_enter(db->mutex);
assert( !db->mallocFailed );
zFunc8 = sqlite3Utf16to8(db, zFunctionName, -1, SQLITE_UTF16NATIVE);
- rc = sqlite3CreateFunc(db, zFunc8, nArg, eTextRep, p, xFunc, xStep, xFinal,0);
+ rc = sqlite3CreateFunc(db, zFunc8, nArg, eTextRep, p, xSFunc,xStep,xFinal,0);
sqlite3DbFree(db, zFunc8);
rc = sqlite3ApiExit(db, rc);
sqlite3_mutex_leave(db->mutex);
@@ -129710,7 +137424,6 @@ SQLITE_API int SQLITE_STDCALL sqlite3_overload_function(
const char *zName,
int nArg
){
- int nName = sqlite3Strlen30(zName);
int rc = SQLITE_OK;
#ifdef SQLITE_ENABLE_API_ARMOR
@@ -129719,7 +137432,7 @@ SQLITE_API int SQLITE_STDCALL sqlite3_overload_function(
}
#endif
sqlite3_mutex_enter(db->mutex);
- if( sqlite3FindFunction(db, zName, nName, nArg, SQLITE_UTF8, 0)==0 ){
+ if( sqlite3FindFunction(db, zName, nArg, SQLITE_UTF8, 0)==0 ){
rc = sqlite3CreateFunc(db, zName, nArg, SQLITE_UTF8,
0, sqlite3InvalidFunction, 0, 0, 0);
}
@@ -129859,6 +137572,27 @@ SQLITE_API void *SQLITE_STDCALL sqlite3_rollback_hook(
return pRet;
}
+#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
+/*
+** Register a callback to be invoked each time a row is updated,
+** inserted or deleted using this database connection.
+*/
+SQLITE_API void *SQLITE_STDCALL sqlite3_preupdate_hook(
+ sqlite3 *db, /* Attach the hook to this database */
+ void(*xCallback)( /* Callback function */
+ void*,sqlite3*,int,char const*,char const*,sqlite3_int64,sqlite3_int64),
+ void *pArg /* First callback argument */
+){
+ void *pRet;
+ sqlite3_mutex_enter(db->mutex);
+ pRet = db->pPreUpdateArg;
+ db->xPreUpdateCallback = xCallback;
+ db->pPreUpdateArg = pArg;
+ sqlite3_mutex_leave(db->mutex);
+ return pRet;
+}
+#endif /* SQLITE_ENABLE_PREUPDATE_HOOK */
+
#ifndef SQLITE_OMIT_WAL
/*
** The sqlite3_wal_hook() callback registered by sqlite3_wal_autocheckpoint().
@@ -130073,9 +137807,11 @@ SQLITE_PRIVATE int sqlite3TempInMemory(const sqlite3 *db){
return ( db->temp_store!=1 );
#endif
#if SQLITE_TEMP_STORE==3
+ UNUSED_PARAMETER(db);
return 1;
#endif
#if SQLITE_TEMP_STORE<1 || SQLITE_TEMP_STORE>3
+ UNUSED_PARAMETER(db);
return 0;
#endif
}
@@ -130087,14 +137823,14 @@ SQLITE_PRIVATE int sqlite3TempInMemory(const sqlite3 *db){
SQLITE_API const char *SQLITE_STDCALL sqlite3_errmsg(sqlite3 *db){
const char *z;
if( !db ){
- return sqlite3ErrStr(SQLITE_NOMEM);
+ return sqlite3ErrStr(SQLITE_NOMEM_BKPT);
}
if( !sqlite3SafetyCheckSickOrOk(db) ){
return sqlite3ErrStr(SQLITE_MISUSE_BKPT);
}
sqlite3_mutex_enter(db->mutex);
if( db->mallocFailed ){
- z = sqlite3ErrStr(SQLITE_NOMEM);
+ z = sqlite3ErrStr(SQLITE_NOMEM_BKPT);
}else{
testcase( db->pErr==0 );
z = (char*)sqlite3_value_text(db->pErr);
@@ -130146,7 +137882,7 @@ SQLITE_API const void *SQLITE_STDCALL sqlite3_errmsg16(sqlite3 *db){
** be cleared before returning. Do this directly, instead of via
** sqlite3ApiExit(), to avoid setting the database handle error message.
*/
- db->mallocFailed = 0;
+ sqlite3OomClear(db);
}
sqlite3_mutex_leave(db->mutex);
return z;
@@ -130162,7 +137898,7 @@ SQLITE_API int SQLITE_STDCALL sqlite3_errcode(sqlite3 *db){
return SQLITE_MISUSE_BKPT;
}
if( !db || db->mallocFailed ){
- return SQLITE_NOMEM;
+ return SQLITE_NOMEM_BKPT;
}
return db->errCode & db->errMask;
}
@@ -130171,10 +137907,13 @@ SQLITE_API int SQLITE_STDCALL sqlite3_extended_errcode(sqlite3 *db){
return SQLITE_MISUSE_BKPT;
}
if( !db || db->mallocFailed ){
- return SQLITE_NOMEM;
+ return SQLITE_NOMEM_BKPT;
}
return db->errCode;
}
+SQLITE_API int SQLITE_STDCALL sqlite3_system_errno(sqlite3 *db){
+ return db ? db->iSysErrno : 0;
+}
/*
** Return a string that describes the kind of error specified in the
@@ -130251,7 +137990,7 @@ static int createCollation(
}
pColl = sqlite3FindCollSeq(db, (u8)enc2, zName, 1);
- if( pColl==0 ) return SQLITE_NOMEM;
+ if( pColl==0 ) return SQLITE_NOMEM_BKPT;
pColl->xCmp = xCompare;
pColl->pUser = pCtx;
pColl->xDel = xDel;
@@ -130299,8 +138038,8 @@ static const int aHardLimit[] = {
#if SQLITE_MAX_VDBE_OP<40
# error SQLITE_MAX_VDBE_OP must be at least 40
#endif
-#if SQLITE_MAX_FUNCTION_ARG<0 || SQLITE_MAX_FUNCTION_ARG>1000
-# error SQLITE_MAX_FUNCTION_ARG must be between 0 and 1000
+#if SQLITE_MAX_FUNCTION_ARG<0 || SQLITE_MAX_FUNCTION_ARG>127
+# error SQLITE_MAX_FUNCTION_ARG must be between 0 and 127
#endif
#if SQLITE_MAX_ATTACHED<0 || SQLITE_MAX_ATTACHED>125
# error SQLITE_MAX_ATTACHED must be between 0 and 125
@@ -130430,7 +138169,7 @@ SQLITE_PRIVATE int sqlite3ParseUri(
for(iIn=0; iIn<nUri; iIn++) nByte += (zUri[iIn]=='&');
zFile = sqlite3_malloc64(nByte);
- if( !zFile ) return SQLITE_NOMEM;
+ if( !zFile ) return SQLITE_NOMEM_BKPT;
iIn = 5;
#ifdef SQLITE_ALLOW_URI_AUTHORITY
@@ -130596,7 +138335,7 @@ SQLITE_PRIVATE int sqlite3ParseUri(
}else{
zFile = sqlite3_malloc64(nUri+2);
- if( !zFile ) return SQLITE_NOMEM;
+ if( !zFile ) return SQLITE_NOMEM_BKPT;
memcpy(zFile, zUri, nUri);
zFile[nUri] = '\0';
zFile[nUri+1] = '\0';
@@ -130750,6 +138489,12 @@ static int openDatabase(
#if defined(SQLITE_REVERSE_UNORDERED_SELECTS)
| SQLITE_ReverseOrder
#endif
+#if defined(SQLITE_ENABLE_OVERSIZE_CELL_CHECK)
+ | SQLITE_CellSizeCk
+#endif
+#if defined(SQLITE_ENABLE_FTS3_TOKENIZER)
+ | SQLITE_Fts3Tokenizer
+#endif
;
sqlite3HashInit(&db->aCollSeq);
#ifndef SQLITE_OMIT_VIRTUALTABLE
@@ -130763,9 +138508,9 @@ static int openDatabase(
** EVIDENCE-OF: R-52786-44878 SQLite defines three built-in collating
** functions:
*/
- createCollation(db, "BINARY", SQLITE_UTF8, 0, binCollFunc, 0);
- createCollation(db, "BINARY", SQLITE_UTF16BE, 0, binCollFunc, 0);
- createCollation(db, "BINARY", SQLITE_UTF16LE, 0, binCollFunc, 0);
+ createCollation(db, sqlite3StrBINARY, SQLITE_UTF8, 0, binCollFunc, 0);
+ createCollation(db, sqlite3StrBINARY, SQLITE_UTF16BE, 0, binCollFunc, 0);
+ createCollation(db, sqlite3StrBINARY, SQLITE_UTF16LE, 0, binCollFunc, 0);
createCollation(db, "NOCASE", SQLITE_UTF8, 0, nocaseCollatingFunc, 0);
createCollation(db, "RTRIM", SQLITE_UTF8, (void*)1, binCollFunc, 0);
if( db->mallocFailed ){
@@ -130774,14 +138519,14 @@ static int openDatabase(
/* EVIDENCE-OF: R-08308-17224 The default collating function for all
** strings is BINARY.
*/
- db->pDfltColl = sqlite3FindCollSeq(db, SQLITE_UTF8, "BINARY", 0);
+ db->pDfltColl = sqlite3FindCollSeq(db, SQLITE_UTF8, sqlite3StrBINARY, 0);
assert( db->pDfltColl!=0 );
/* Parse the filename/URI argument. */
db->openFlags = flags;
rc = sqlite3ParseUri(zVfs, zFilename, &flags, &db->pVfs, &zOpen, &zErrMsg);
if( rc!=SQLITE_OK ){
- if( rc==SQLITE_NOMEM ) db->mallocFailed = 1;
+ if( rc==SQLITE_NOMEM ) sqlite3OomFault(db);
sqlite3ErrorWithMsg(db, rc, zErrMsg ? "%s" : 0, zErrMsg);
sqlite3_free(zErrMsg);
goto opendb_out;
@@ -130792,7 +138537,7 @@ static int openDatabase(
flags | SQLITE_OPEN_MAIN_DB);
if( rc!=SQLITE_OK ){
if( rc==SQLITE_IOERR_NOMEM ){
- rc = SQLITE_NOMEM;
+ rc = SQLITE_NOMEM_BKPT;
}
sqlite3Error(db, rc);
goto opendb_out;
@@ -130803,13 +138548,13 @@ static int openDatabase(
sqlite3BtreeLeave(db->aDb[0].pBt);
db->aDb[1].pSchema = sqlite3SchemaGet(db, 0);
- /* The default safety_level for the main database is 'full'; for the temp
- ** database it is 'NONE'. This matches the pager layer defaults.
+ /* The default safety_level for the main database is FULL; for the temp
+ ** database it is OFF. This matches the pager layer defaults.
*/
db->aDb[0].zName = "main";
- db->aDb[0].safety_level = 3;
+ db->aDb[0].safety_level = SQLITE_DEFAULT_SYNCHRONOUS+1;
db->aDb[1].zName = "temp";
- db->aDb[1].safety_level = 1;
+ db->aDb[1].safety_level = PAGER_SYNCHRONOUS_OFF;
db->magic = SQLITE_MAGIC_OPEN;
if( db->mallocFailed ){
@@ -130821,7 +138566,7 @@ static int openDatabase(
** is accessed.
*/
sqlite3Error(db, SQLITE_OK);
- sqlite3RegisterBuiltinFunctions(db);
+ sqlite3RegisterPerConnectionBuiltinFunctions(db);
/* Load automatic extensions - extensions that have been registered
** using the sqlite3_automatic_extension() API.
@@ -130849,12 +138594,18 @@ static int openDatabase(
}
#endif
-#ifdef SQLITE_ENABLE_FTS3
+#ifdef SQLITE_ENABLE_FTS3 /* automatically defined by SQLITE_ENABLE_FTS4 */
if( !db->mallocFailed && rc==SQLITE_OK ){
rc = sqlite3Fts3Init(db);
}
#endif
+#ifdef SQLITE_ENABLE_FTS5
+ if( !db->mallocFailed && rc==SQLITE_OK ){
+ rc = sqlite3Fts5Init(db);
+ }
+#endif
+
#ifdef SQLITE_ENABLE_ICU
if( !db->mallocFailed && rc==SQLITE_OK ){
rc = sqlite3IcuInit(db);
@@ -130869,8 +138620,13 @@ static int openDatabase(
#ifdef SQLITE_ENABLE_DBSTAT_VTAB
if( !db->mallocFailed && rc==SQLITE_OK){
- int sqlite3_dbstat_register(sqlite3*);
- rc = sqlite3_dbstat_register(db);
+ rc = sqlite3DbstatRegister(db);
+ }
+#endif
+
+#ifdef SQLITE_ENABLE_JSON1
+ if( !db->mallocFailed && rc==SQLITE_OK){
+ rc = sqlite3Json1Init(db);
}
#endif
@@ -130893,7 +138649,6 @@ static int openDatabase(
sqlite3_wal_autocheckpoint(db, SQLITE_DEFAULT_WAL_AUTOCHECKPOINT);
opendb_out:
- sqlite3_free(zOpen);
if( db ){
assert( db->mutex!=0 || isThreadsafe==0
|| sqlite3GlobalConfig.bFullMutex==0 );
@@ -130915,7 +138670,23 @@ opendb_out:
sqlite3GlobalConfig.xSqllog(pArg, db, zFilename, 0);
}
#endif
- return sqlite3ApiExit(0, rc);
+#if defined(SQLITE_HAS_CODEC)
+ if( rc==SQLITE_OK ){
+ const char *zHexKey = sqlite3_uri_parameter(zOpen, "hexkey");
+ if( zHexKey && zHexKey[0] ){
+ u8 iByte;
+ int i;
+ char zKey[40];
+ for(i=0, iByte=0; i<sizeof(zKey)*2 && sqlite3Isxdigit(zHexKey[i]); i++){
+ iByte = (iByte<<4) + sqlite3HexToInt(zHexKey[i]);
+ if( (i&1)!=0 ) zKey[i/2] = iByte;
+ }
+ sqlite3_key_v2(db, 0, zKey, i/2);
+ }
+ }
+#endif
+ sqlite3_free(zOpen);
+ return rc & 0xff;
}
/*
@@ -130969,11 +138740,11 @@ SQLITE_API int SQLITE_STDCALL sqlite3_open16(
SCHEMA_ENC(*ppDb) = ENC(*ppDb) = SQLITE_UTF16NATIVE;
}
}else{
- rc = SQLITE_NOMEM;
+ rc = SQLITE_NOMEM_BKPT;
}
sqlite3ValueFree(pVal);
- return sqlite3ApiExit(0, rc);
+ return rc & 0xff;
}
#endif /* SQLITE_OMIT_UTF16 */
@@ -131114,7 +138885,7 @@ SQLITE_API int SQLITE_STDCALL sqlite3_get_autocommit(sqlite3 *db){
/*
** The following routines are substitutes for constants SQLITE_CORRUPT,
-** SQLITE_MISUSE, SQLITE_CANTOPEN, SQLITE_IOERR and possibly other error
+** SQLITE_MISUSE, SQLITE_CANTOPEN, SQLITE_NOMEM and possibly other error
** constants. They serve two purposes:
**
** 1. Serve as a convenient place to set a breakpoint in a debugger
@@ -131123,28 +138894,33 @@ SQLITE_API int SQLITE_STDCALL sqlite3_get_autocommit(sqlite3 *db){
** 2. Invoke sqlite3_log() to provide the source code location where
** a low-level error is first detected.
*/
+static int reportError(int iErr, int lineno, const char *zType){
+ sqlite3_log(iErr, "%s at line %d of [%.10s]",
+ zType, lineno, 20+sqlite3_sourceid());
+ return iErr;
+}
SQLITE_PRIVATE int sqlite3CorruptError(int lineno){
testcase( sqlite3GlobalConfig.xLog!=0 );
- sqlite3_log(SQLITE_CORRUPT,
- "database corruption at line %d of [%.10s]",
- lineno, 20+sqlite3_sourceid());
- return SQLITE_CORRUPT;
+ return reportError(SQLITE_CORRUPT, lineno, "database corruption");
}
SQLITE_PRIVATE int sqlite3MisuseError(int lineno){
testcase( sqlite3GlobalConfig.xLog!=0 );
- sqlite3_log(SQLITE_MISUSE,
- "misuse at line %d of [%.10s]",
- lineno, 20+sqlite3_sourceid());
- return SQLITE_MISUSE;
+ return reportError(SQLITE_MISUSE, lineno, "misuse");
}
SQLITE_PRIVATE int sqlite3CantopenError(int lineno){
testcase( sqlite3GlobalConfig.xLog!=0 );
- sqlite3_log(SQLITE_CANTOPEN,
- "cannot open file at line %d of [%.10s]",
- lineno, 20+sqlite3_sourceid());
- return SQLITE_CANTOPEN;
+ return reportError(SQLITE_CANTOPEN, lineno, "cannot open file");
}
-
+#ifdef SQLITE_DEBUG
+SQLITE_PRIVATE int sqlite3NomemError(int lineno){
+ testcase( sqlite3GlobalConfig.xLog!=0 );
+ return reportError(SQLITE_NOMEM, lineno, "OOM");
+}
+SQLITE_PRIVATE int sqlite3IoerrnomemError(int lineno){
+ testcase( sqlite3GlobalConfig.xLog!=0 );
+ return reportError(SQLITE_IOERR_NOMEM, lineno, "I/O OOM error");
+}
+#endif
#ifndef SQLITE_OMIT_DEPRECATED
/*
@@ -131238,7 +139014,7 @@ SQLITE_API int SQLITE_STDCALL sqlite3_table_column_metadata(
** explicitly declared column. Copy meta information from *pCol.
*/
if( pCol ){
- zDataType = pCol->zType;
+ zDataType = sqlite3ColumnType(pCol,0);
zCollSeq = pCol->zColl;
notnull = pCol->notNull!=0;
primarykey = (pCol->colFlags & COLFLAG_PRIMKEY)!=0;
@@ -131248,7 +139024,7 @@ SQLITE_API int SQLITE_STDCALL sqlite3_table_column_metadata(
primarykey = 1;
}
if( !zCollSeq ){
- zCollSeq = "BINARY";
+ zCollSeq = sqlite3StrBINARY;
}
error_out:
@@ -131329,6 +139105,12 @@ SQLITE_API int SQLITE_STDCALL sqlite3_file_control(sqlite3 *db, const char *zDbN
if( op==SQLITE_FCNTL_FILE_POINTER ){
*(sqlite3_file**)pArg = fd;
rc = SQLITE_OK;
+ }else if( op==SQLITE_FCNTL_VFS_POINTER ){
+ *(sqlite3_vfs**)pArg = sqlite3PagerVfs(pPager);
+ rc = SQLITE_OK;
+ }else if( op==SQLITE_FCNTL_JOURNAL_POINTER ){
+ *(sqlite3_file**)pArg = sqlite3PagerJrnlFile(pPager);
+ rc = SQLITE_OK;
}else if( fd->pMethods ){
rc = sqlite3OsFileControl(fd, op, pArg);
}else{
@@ -131345,7 +139127,9 @@ SQLITE_API int SQLITE_STDCALL sqlite3_file_control(sqlite3 *db, const char *zDbN
*/
SQLITE_API int SQLITE_CDECL sqlite3_test_control(int op, ...){
int rc = 0;
-#ifndef SQLITE_OMIT_BUILTIN_TEST
+#ifdef SQLITE_OMIT_BUILTIN_TEST
+ UNUSED_PARAMETER(op);
+#else
va_list ap;
va_start(ap, op);
switch( op ){
@@ -131467,7 +139251,7 @@ SQLITE_API int SQLITE_CDECL sqlite3_test_control(int op, ...){
*/
case SQLITE_TESTCTRL_ASSERT: {
volatile int x = 0;
- assert( (x = va_arg(ap,int))!=0 );
+ assert( /*side-effects-ok*/ (x = va_arg(ap,int))!=0 );
rc = x;
break;
}
@@ -131770,6 +139554,88 @@ SQLITE_API int SQLITE_STDCALL sqlite3_db_readonly(sqlite3 *db, const char *zDbNa
return pBt ? sqlite3BtreeIsReadonly(pBt) : -1;
}
+#ifdef SQLITE_ENABLE_SNAPSHOT
+/*
+** Obtain a snapshot handle for the snapshot of database zDb currently
+** being read by handle db.
+*/
+SQLITE_API int SQLITE_STDCALL sqlite3_snapshot_get(
+ sqlite3 *db,
+ const char *zDb,
+ sqlite3_snapshot **ppSnapshot
+){
+ int rc = SQLITE_ERROR;
+#ifndef SQLITE_OMIT_WAL
+ int iDb;
+
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( !sqlite3SafetyCheckOk(db) ){
+ return SQLITE_MISUSE_BKPT;
+ }
+#endif
+ sqlite3_mutex_enter(db->mutex);
+
+ iDb = sqlite3FindDbName(db, zDb);
+ if( iDb==0 || iDb>1 ){
+ Btree *pBt = db->aDb[iDb].pBt;
+ if( 0==sqlite3BtreeIsInTrans(pBt) ){
+ rc = sqlite3BtreeBeginTrans(pBt, 0);
+ if( rc==SQLITE_OK ){
+ rc = sqlite3PagerSnapshotGet(sqlite3BtreePager(pBt), ppSnapshot);
+ }
+ }
+ }
+
+ sqlite3_mutex_leave(db->mutex);
+#endif /* SQLITE_OMIT_WAL */
+ return rc;
+}
+
+/*
+** Open a read-transaction on the snapshot idendified by pSnapshot.
+*/
+SQLITE_API int SQLITE_STDCALL sqlite3_snapshot_open(
+ sqlite3 *db,
+ const char *zDb,
+ sqlite3_snapshot *pSnapshot
+){
+ int rc = SQLITE_ERROR;
+#ifndef SQLITE_OMIT_WAL
+
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( !sqlite3SafetyCheckOk(db) ){
+ return SQLITE_MISUSE_BKPT;
+ }
+#endif
+ sqlite3_mutex_enter(db->mutex);
+ if( db->autoCommit==0 ){
+ int iDb;
+ iDb = sqlite3FindDbName(db, zDb);
+ if( iDb==0 || iDb>1 ){
+ Btree *pBt = db->aDb[iDb].pBt;
+ if( 0==sqlite3BtreeIsInReadTrans(pBt) ){
+ rc = sqlite3PagerSnapshotOpen(sqlite3BtreePager(pBt), pSnapshot);
+ if( rc==SQLITE_OK ){
+ rc = sqlite3BtreeBeginTrans(pBt, 0);
+ sqlite3PagerSnapshotOpen(sqlite3BtreePager(pBt), 0);
+ }
+ }
+ }
+ }
+
+ sqlite3_mutex_leave(db->mutex);
+#endif /* SQLITE_OMIT_WAL */
+ return rc;
+}
+
+/*
+** Free a snapshot handle obtained from sqlite3_snapshot_get().
+*/
+SQLITE_API void SQLITE_STDCALL sqlite3_snapshot_free(sqlite3_snapshot *pSnapshot){
+ sqlite3_free(pSnapshot);
+}
+#endif /* SQLITE_ENABLE_SNAPSHOT */
+
/************** End of main.c ************************************************/
/************** Begin file notify.c ******************************************/
/*
@@ -131787,6 +139653,8 @@ SQLITE_API int SQLITE_STDCALL sqlite3_db_readonly(sqlite3 *db, const char *zDbNa
** This file contains the implementation of the sqlite3_unlock_notify()
** API method and its associated functionality.
*/
+/* #include "sqliteInt.h" */
+/* #include "btreeInt.h" */
/* Omit this entire file if SQLITE_ENABLE_UNLOCK_NOTIFY is not defined. */
#ifdef SQLITE_ENABLE_UNLOCK_NOTIFY
@@ -132417,6 +140285,12 @@ SQLITE_PRIVATE void sqlite3ConnectionClosed(sqlite3 *db){
# define NDEBUG 1
#endif
+/* FTS3/FTS4 require virtual tables */
+#ifdef SQLITE_OMIT_VIRTUALTABLE
+# undef SQLITE_ENABLE_FTS3
+# undef SQLITE_ENABLE_FTS4
+#endif
+
/*
** FTS4 is really an extension for FTS3. It is enabled using the
** SQLITE_ENABLE_FTS3 macro. But to avoid confusion we also all
@@ -132430,9 +140304,11 @@ SQLITE_PRIVATE void sqlite3ConnectionClosed(sqlite3 *db){
/* If not building as part of the core, include sqlite3ext.h. */
#ifndef SQLITE_CORE
+/* # include "sqlite3ext.h" */
SQLITE_EXTENSION_INIT3
#endif
+/* #include "sqlite3.h" */
/************** Include fts3_tokenizer.h in the middle of fts3Int.h **********/
/************** Begin file fts3_tokenizer.h **********************************/
/*
@@ -132461,6 +140337,7 @@ SQLITE_EXTENSION_INIT3
** If tokenizers are to be allowed to call sqlite3_*() functions, then
** we will need a way to register the API consistently.
*/
+/* #include "sqlite3.h" */
/*
** Structures used by the tokenizer interface. When a new tokenizer
@@ -132874,6 +140751,8 @@ typedef struct Fts3DeferredToken Fts3DeferredToken;
typedef struct Fts3SegReader Fts3SegReader;
typedef struct Fts3MultiSegReader Fts3MultiSegReader;
+typedef struct MatchinfoBuffer MatchinfoBuffer;
+
/*
** A connection to a fulltext index is an instance of the following
** structure. The xCreate and xConnect methods create an instance
@@ -132939,6 +140818,7 @@ struct Fts3Table {
int nPendingData; /* Current bytes of pending data */
sqlite_int64 iPrevDocid; /* Docid of most recently inserted document */
int iPrevLangid; /* Langid of recently inserted document */
+ int bPrevDelete; /* True if last operation was a delete */
#if defined(SQLITE_DEBUG) || defined(SQLITE_COVERAGE_TEST)
/* State variables used for validating that the transaction control
@@ -132983,9 +140863,7 @@ struct Fts3Cursor {
i64 iMinDocid; /* Minimum docid to return */
i64 iMaxDocid; /* Maximum docid to return */
int isMatchinfoNeeded; /* True when aMatchinfo[] needs filling in */
- u32 *aMatchinfo; /* Information about most recent match */
- int nMatchinfo; /* Number of elements in aMatchinfo[] */
- char *zMatchinfo; /* Matchinfo specification */
+ MatchinfoBuffer *pMIBuffer; /* Buffer for matchinfo data */
};
#define FTS3_EVAL_FILTER 0
@@ -133105,7 +140983,9 @@ struct Fts3Expr {
u8 bStart; /* True if iDocid is valid */
u8 bDeferred; /* True if this expression is entirely deferred */
- u32 *aMI;
+ /* The following are used by the fts3_snippet.c module. */
+ int iPhrase; /* Index of this phrase in matchinfo() results */
+ u32 *aMI; /* See above */
};
/*
@@ -133226,6 +141106,7 @@ SQLITE_PRIVATE void sqlite3Fts3DoclistPrev(int,char*,int,char**,sqlite3_int64*,i
SQLITE_PRIVATE int sqlite3Fts3EvalPhraseStats(Fts3Cursor *, Fts3Expr *, u32 *);
SQLITE_PRIVATE int sqlite3Fts3FirstFilter(sqlite3_int64, char *, int, char *);
SQLITE_PRIVATE void sqlite3Fts3CreateStatTable(int*, Fts3Table*);
+SQLITE_PRIVATE int sqlite3Fts3EvalTestDeferred(Fts3Cursor *pCsr, int *pRc);
/* fts3_tokenizer.c */
SQLITE_PRIVATE const char *sqlite3Fts3NextToken(const char *, int *);
@@ -133241,6 +141122,7 @@ SQLITE_PRIVATE void sqlite3Fts3Snippet(sqlite3_context *, Fts3Cursor *, const ch
const char *, const char *, int, int
);
SQLITE_PRIVATE void sqlite3Fts3Matchinfo(sqlite3_context *, Fts3Cursor *, const char *);
+SQLITE_PRIVATE void sqlite3Fts3MIBufferFree(MatchinfoBuffer *p);
/* fts3_expr.c */
SQLITE_PRIVATE int sqlite3Fts3ExprParse(sqlite3_tokenizer *, int,
@@ -133297,7 +141179,9 @@ SQLITE_PRIVATE int sqlite3FtsUnicodeIsdiacritic(int);
/* #include <string.h> */
/* #include <stdarg.h> */
+/* #include "fts3.h" */
#ifndef SQLITE_CORE
+/* # include "sqlite3ext.h" */
SQLITE_EXTENSION_INIT1
#endif
@@ -134510,6 +142394,19 @@ static void fts3SetEstimatedRows(sqlite3_index_info *pIdxInfo, i64 nRow){
#endif
}
+/*
+** Set the SQLITE_INDEX_SCAN_UNIQUE flag in pIdxInfo->flags. Unless this
+** extension is currently being used by a version of SQLite too old to
+** support index-info flags. In that case this function is a no-op.
+*/
+static void fts3SetUniqueFlag(sqlite3_index_info *pIdxInfo){
+#if SQLITE_VERSION_NUMBER>=3008012
+ if( sqlite3_libversion_number()>=3008012 ){
+ pIdxInfo->idxFlags |= SQLITE_INDEX_SCAN_UNIQUE;
+ }
+#endif
+}
+
/*
** Implementation of the xBestIndex method for FTS3 tables. There
** are three possible strategies, in order of preference:
@@ -134600,6 +142497,9 @@ static int fts3BestIndexMethod(sqlite3_vtab *pVTab, sqlite3_index_info *pInfo){
}
}
+ /* If using a docid=? or rowid=? strategy, set the UNIQUE flag. */
+ if( pInfo->idxNum==FTS3_DOCID_SEARCH ) fts3SetUniqueFlag(pInfo);
+
iIdx = 1;
if( iCons>=0 ){
pInfo->aConstraintUsage[iCons].argvIndex = iIdx++;
@@ -134668,7 +142568,7 @@ static int fts3CloseMethod(sqlite3_vtab_cursor *pCursor){
sqlite3Fts3ExprFree(pCsr->pExpr);
sqlite3Fts3FreeDeferredTokens(pCsr);
sqlite3_free(pCsr->aDoclist);
- sqlite3_free(pCsr->aMatchinfo);
+ sqlite3Fts3MIBufferFree(pCsr->pMIBuffer);
assert( ((Fts3Table *)pCsr->base.pVtab)->pSegments==0 );
sqlite3_free(pCsr);
return SQLITE_OK;
@@ -136169,7 +144069,7 @@ static int fts3FilterMethod(
/* In case the cursor has been used before, clear it now. */
sqlite3_finalize(pCsr->pStmt);
sqlite3_free(pCsr->aDoclist);
- sqlite3_free(pCsr->aMatchinfo);
+ sqlite3Fts3MIBufferFree(pCsr->pMIBuffer);
sqlite3Fts3ExprFree(pCsr->pExpr);
memset(&pCursor[1], 0, sizeof(Fts3Cursor)-sizeof(sqlite3_vtab_cursor));
@@ -137224,7 +145124,6 @@ static int fts3EvalPhraseStart(Fts3Cursor *pCsr, int bOptOk, Fts3Phrase *p){
int bIncrOk = (bOptOk
&& pCsr->bDesc==pTab->bDescIdx
&& p->nToken<=MAX_INCR_PHRASE_TOKENS && p->nToken>0
- && p->nToken<=MAX_INCR_PHRASE_TOKENS && p->nToken>0
#ifdef SQLITE_TEST
&& pTab->bNoIncrDoclist==0
#endif
@@ -137344,6 +145243,7 @@ SQLITE_PRIVATE void sqlite3Fts3DoclistNext(
p += sqlite3Fts3GetVarint(p, piDocid);
}else{
fts3PoslistCopy(0, &p);
+ while( p<&aDoclist[nDoclist] && *p==0 ) p++;
if( p>=&aDoclist[nDoclist] ){
*pbEof = 1;
}else{
@@ -138067,7 +145967,7 @@ static int fts3EvalNearTrim(
** 2. NEAR is treated as AND. If the expression is "x NEAR y", it is
** advanced to point to the next row that matches "x AND y".
**
-** See fts3EvalTestDeferredAndNear() for details on testing if a row is
+** See sqlite3Fts3EvalTestDeferred() for details on testing if a row is
** really a match, taking into account deferred tokens and NEAR operators.
*/
static void fts3EvalNextRow(
@@ -138287,7 +146187,7 @@ static int fts3EvalNearTest(Fts3Expr *pExpr, int *pRc){
}
/*
-** This function is a helper function for fts3EvalTestDeferredAndNear().
+** This function is a helper function for sqlite3Fts3EvalTestDeferred().
** Assuming no error occurs or has occurred, It returns non-zero if the
** expression passed as the second argument matches the row that pCsr
** currently points to, or zero if it does not.
@@ -138408,7 +146308,7 @@ static int fts3EvalTestExpr(
** Or, if no error occurs and it seems the current row does match the FTS
** query, return 0.
*/
-static int fts3EvalTestDeferredAndNear(Fts3Cursor *pCsr, int *pRc){
+SQLITE_PRIVATE int sqlite3Fts3EvalTestDeferred(Fts3Cursor *pCsr, int *pRc){
int rc = *pRc;
int bMiss = 0;
if( rc==SQLITE_OK ){
@@ -138455,7 +146355,7 @@ static int fts3EvalNext(Fts3Cursor *pCsr){
pCsr->isRequireSeek = 1;
pCsr->isMatchinfoNeeded = 1;
pCsr->iPrevId = pExpr->iDocid;
- }while( pCsr->isEof==0 && fts3EvalTestDeferredAndNear(pCsr, &rc) );
+ }while( pCsr->isEof==0 && sqlite3Fts3EvalTestDeferred(pCsr, &rc) );
}
/* Check if the cursor is past the end of the docid range specified
@@ -138616,7 +146516,7 @@ static int fts3EvalGatherStats(
pCsr->iPrevId = pRoot->iDocid;
}while( pCsr->isEof==0
&& pRoot->eType==FTSQUERY_NEAR
- && fts3EvalTestDeferredAndNear(pCsr, &rc)
+ && sqlite3Fts3EvalTestDeferred(pCsr, &rc)
);
if( rc==SQLITE_OK && pCsr->isEof==0 ){
@@ -138641,7 +146541,6 @@ static int fts3EvalGatherStats(
fts3EvalNextRow(pCsr, pRoot, &rc);
assert( pRoot->bEof==0 );
}while( pRoot->iDocid!=iDocid && rc==SQLITE_OK );
- fts3EvalTestDeferredAndNear(pCsr, &rc);
}
}
return rc;
@@ -138751,10 +146650,10 @@ SQLITE_PRIVATE int sqlite3Fts3EvalPhrasePoslist(
int rc = SQLITE_OK;
int bDescDoclist = pTab->bDescIdx; /* For DOCID_CMP macro */
int bOr = 0;
- u8 bEof = 0;
u8 bTreeEof = 0;
Fts3Expr *p; /* Used to iterate from pExpr to root */
Fts3Expr *pNear; /* Most senior NEAR ancestor (or pExpr) */
+ int bMatch;
/* Check if this phrase descends from an OR expression node. If not,
** return NULL. Otherwise, the entry that corresponds to docid
@@ -138788,31 +146687,47 @@ SQLITE_PRIVATE int sqlite3Fts3EvalPhrasePoslist(
}
if( rc!=SQLITE_OK ) return rc;
- pIter = pPhrase->pOrPoslist;
- iDocid = pPhrase->iOrDocid;
- if( pCsr->bDesc==bDescDoclist ){
- 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,
- &pIter, &iDocid, &bEof
- );
- }
- }else{
- bEof = !pPhrase->doclist.nAll || (pIter && pIter<=pPhrase->doclist.aAll);
- while( (pIter==0 || DOCID_CMP(iDocid, pCsr->iPrevId)>0 ) && bEof==0 ){
- int dummy;
- sqlite3Fts3DoclistPrev(
- bDescDoclist, pPhrase->doclist.aAll, pPhrase->doclist.nAll,
- &pIter, &iDocid, &dummy, &bEof
- );
+ bMatch = 1;
+ for(p=pNear; p; p=p->pLeft){
+ u8 bEof = 0;
+ Fts3Expr *pTest = p;
+ Fts3Phrase *pPh;
+ assert( pTest->eType==FTSQUERY_NEAR || pTest->eType==FTSQUERY_PHRASE );
+ if( pTest->eType==FTSQUERY_NEAR ) pTest = pTest->pRight;
+ assert( pTest->eType==FTSQUERY_PHRASE );
+ pPh = pTest->pPhrase;
+
+ pIter = pPh->pOrPoslist;
+ iDocid = pPh->iOrDocid;
+ if( pCsr->bDesc==bDescDoclist ){
+ bEof = !pPh->doclist.nAll ||
+ (pIter >= (pPh->doclist.aAll + pPh->doclist.nAll));
+ while( (pIter==0 || DOCID_CMP(iDocid, pCsr->iPrevId)<0 ) && bEof==0 ){
+ sqlite3Fts3DoclistNext(
+ bDescDoclist, pPh->doclist.aAll, pPh->doclist.nAll,
+ &pIter, &iDocid, &bEof
+ );
+ }
+ }else{
+ bEof = !pPh->doclist.nAll || (pIter && pIter<=pPh->doclist.aAll);
+ while( (pIter==0 || DOCID_CMP(iDocid, pCsr->iPrevId)>0 ) && bEof==0 ){
+ int dummy;
+ sqlite3Fts3DoclistPrev(
+ bDescDoclist, pPh->doclist.aAll, pPh->doclist.nAll,
+ &pIter, &iDocid, &dummy, &bEof
+ );
+ }
}
+ pPh->pOrPoslist = pIter;
+ pPh->iOrDocid = iDocid;
+ if( bEof || iDocid!=pCsr->iPrevId ) bMatch = 0;
}
- pPhrase->pOrPoslist = pIter;
- pPhrase->iOrDocid = iDocid;
- if( bEof || iDocid!=pCsr->iPrevId ) pIter = 0;
+ if( bMatch ){
+ pIter = pPhrase->pOrPoslist;
+ }else{
+ pIter = 0;
+ }
}
if( pIter==0 ) return SQLITE_OK;
@@ -138900,6 +146815,7 @@ SQLITE_API int SQLITE_STDCALL sqlite3_fts3_init(
******************************************************************************
**
*/
+/* #include "fts3Int.h" */
#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3)
/* #include <string.h> */
@@ -139456,6 +147372,7 @@ SQLITE_PRIVATE int sqlite3Fts3InitAux(sqlite3 *db){
** syntax is relatively simple, the whole tokenizer/parser system is
** hand-coded.
*/
+/* #include "fts3Int.h" */
#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3)
/*
@@ -140233,125 +148150,151 @@ static int fts3ExprBalance(Fts3Expr **pp, int nMaxDepth){
rc = SQLITE_ERROR;
}
- if( rc==SQLITE_OK && (eType==FTSQUERY_AND || eType==FTSQUERY_OR) ){
- Fts3Expr **apLeaf;
- apLeaf = (Fts3Expr **)sqlite3_malloc(sizeof(Fts3Expr *) * nMaxDepth);
- if( 0==apLeaf ){
- rc = SQLITE_NOMEM;
- }else{
- memset(apLeaf, 0, sizeof(Fts3Expr *) * nMaxDepth);
- }
-
- if( rc==SQLITE_OK ){
- int i;
- Fts3Expr *p;
-
- /* Set $p to point to the left-most leaf in the tree of eType nodes. */
- for(p=pRoot; p->eType==eType; p=p->pLeft){
- assert( p->pParent==0 || p->pParent->pLeft==p );
- assert( p->pLeft && p->pRight );
+ if( rc==SQLITE_OK ){
+ if( (eType==FTSQUERY_AND || eType==FTSQUERY_OR) ){
+ Fts3Expr **apLeaf;
+ apLeaf = (Fts3Expr **)sqlite3_malloc(sizeof(Fts3Expr *) * nMaxDepth);
+ if( 0==apLeaf ){
+ rc = SQLITE_NOMEM;
+ }else{
+ memset(apLeaf, 0, sizeof(Fts3Expr *) * nMaxDepth);
}
- /* This loop runs once for each leaf in the tree of eType nodes. */
- while( 1 ){
- int iLvl;
- Fts3Expr *pParent = p->pParent; /* Current parent of p */
+ if( rc==SQLITE_OK ){
+ int i;
+ Fts3Expr *p;
- assert( pParent==0 || pParent->pLeft==p );
- p->pParent = 0;
- if( pParent ){
- pParent->pLeft = 0;
- }else{
- pRoot = 0;
+ /* Set $p to point to the left-most leaf in the tree of eType nodes. */
+ for(p=pRoot; p->eType==eType; p=p->pLeft){
+ assert( p->pParent==0 || p->pParent->pLeft==p );
+ assert( p->pLeft && p->pRight );
}
- rc = fts3ExprBalance(&p, nMaxDepth-1);
- if( rc!=SQLITE_OK ) break;
- for(iLvl=0; p && iLvl<nMaxDepth; iLvl++){
- if( apLeaf[iLvl]==0 ){
- apLeaf[iLvl] = p;
- p = 0;
+ /* This loop runs once for each leaf in the tree of eType nodes. */
+ while( 1 ){
+ int iLvl;
+ Fts3Expr *pParent = p->pParent; /* Current parent of p */
+
+ assert( pParent==0 || pParent->pLeft==p );
+ p->pParent = 0;
+ if( pParent ){
+ pParent->pLeft = 0;
}else{
- assert( pFree );
- pFree->pLeft = apLeaf[iLvl];
- pFree->pRight = p;
- pFree->pLeft->pParent = pFree;
- pFree->pRight->pParent = pFree;
-
- p = pFree;
- pFree = pFree->pParent;
- p->pParent = 0;
- apLeaf[iLvl] = 0;
+ pRoot = 0;
}
- }
- if( p ){
- sqlite3Fts3ExprFree(p);
- rc = SQLITE_TOOBIG;
- break;
- }
-
- /* If that was the last leaf node, break out of the loop */
- if( pParent==0 ) break;
-
- /* Set $p to point to the next leaf in the tree of eType nodes */
- for(p=pParent->pRight; p->eType==eType; p=p->pLeft);
-
- /* Remove pParent from the original tree. */
- assert( pParent->pParent==0 || pParent->pParent->pLeft==pParent );
- pParent->pRight->pParent = pParent->pParent;
- if( pParent->pParent ){
- pParent->pParent->pLeft = pParent->pRight;
- }else{
- assert( pParent==pRoot );
- pRoot = pParent->pRight;
- }
-
- /* Link pParent into the free node list. It will be used as an
- ** internal node of the new tree. */
- pParent->pParent = pFree;
- pFree = pParent;
- }
+ rc = fts3ExprBalance(&p, nMaxDepth-1);
+ if( rc!=SQLITE_OK ) break;
- if( rc==SQLITE_OK ){
- p = 0;
- for(i=0; i<nMaxDepth; i++){
- if( apLeaf[i] ){
- if( p==0 ){
- p = apLeaf[i];
- p->pParent = 0;
+ for(iLvl=0; p && iLvl<nMaxDepth; iLvl++){
+ if( apLeaf[iLvl]==0 ){
+ apLeaf[iLvl] = p;
+ p = 0;
}else{
- assert( pFree!=0 );
+ assert( pFree );
+ pFree->pLeft = apLeaf[iLvl];
pFree->pRight = p;
- pFree->pLeft = apLeaf[i];
pFree->pLeft->pParent = pFree;
pFree->pRight->pParent = pFree;
p = pFree;
pFree = pFree->pParent;
p->pParent = 0;
+ apLeaf[iLvl] = 0;
}
}
+ if( p ){
+ sqlite3Fts3ExprFree(p);
+ rc = SQLITE_TOOBIG;
+ break;
+ }
+
+ /* If that was the last leaf node, break out of the loop */
+ if( pParent==0 ) break;
+
+ /* Set $p to point to the next leaf in the tree of eType nodes */
+ for(p=pParent->pRight; p->eType==eType; p=p->pLeft);
+
+ /* Remove pParent from the original tree. */
+ assert( pParent->pParent==0 || pParent->pParent->pLeft==pParent );
+ pParent->pRight->pParent = pParent->pParent;
+ if( pParent->pParent ){
+ pParent->pParent->pLeft = pParent->pRight;
+ }else{
+ assert( pParent==pRoot );
+ pRoot = pParent->pRight;
+ }
+
+ /* Link pParent into the free node list. It will be used as an
+ ** internal node of the new tree. */
+ pParent->pParent = pFree;
+ pFree = pParent;
}
- pRoot = p;
- }else{
- /* An error occurred. Delete the contents of the apLeaf[] array
- ** and pFree list. Everything else is cleaned up by the call to
- ** sqlite3Fts3ExprFree(pRoot) below. */
- Fts3Expr *pDel;
- for(i=0; i<nMaxDepth; i++){
- sqlite3Fts3ExprFree(apLeaf[i]);
- }
- while( (pDel=pFree)!=0 ){
- pFree = pDel->pParent;
- sqlite3_free(pDel);
+
+ if( rc==SQLITE_OK ){
+ p = 0;
+ for(i=0; i<nMaxDepth; i++){
+ if( apLeaf[i] ){
+ if( p==0 ){
+ p = apLeaf[i];
+ p->pParent = 0;
+ }else{
+ assert( pFree!=0 );
+ pFree->pRight = p;
+ pFree->pLeft = apLeaf[i];
+ pFree->pLeft->pParent = pFree;
+ pFree->pRight->pParent = pFree;
+
+ p = pFree;
+ pFree = pFree->pParent;
+ p->pParent = 0;
+ }
+ }
+ }
+ pRoot = p;
+ }else{
+ /* An error occurred. Delete the contents of the apLeaf[] array
+ ** and pFree list. Everything else is cleaned up by the call to
+ ** sqlite3Fts3ExprFree(pRoot) below. */
+ Fts3Expr *pDel;
+ for(i=0; i<nMaxDepth; i++){
+ sqlite3Fts3ExprFree(apLeaf[i]);
+ }
+ while( (pDel=pFree)!=0 ){
+ pFree = pDel->pParent;
+ sqlite3_free(pDel);
+ }
}
+
+ assert( pFree==0 );
+ sqlite3_free( apLeaf );
}
+ }else if( eType==FTSQUERY_NOT ){
+ Fts3Expr *pLeft = pRoot->pLeft;
+ Fts3Expr *pRight = pRoot->pRight;
- assert( pFree==0 );
- sqlite3_free( apLeaf );
+ pRoot->pLeft = 0;
+ pRoot->pRight = 0;
+ pLeft->pParent = 0;
+ pRight->pParent = 0;
+
+ rc = fts3ExprBalance(&pLeft, nMaxDepth-1);
+ if( rc==SQLITE_OK ){
+ rc = fts3ExprBalance(&pRight, nMaxDepth-1);
+ }
+
+ if( rc!=SQLITE_OK ){
+ sqlite3Fts3ExprFree(pRight);
+ sqlite3Fts3ExprFree(pLeft);
+ }else{
+ assert( pLeft && pRight );
+ pRoot->pLeft = pLeft;
+ pLeft->pParent = pRoot;
+ pRoot->pRight = pRight;
+ pRight->pParent = pRoot;
+ }
}
}
-
+
if( rc!=SQLITE_OK ){
sqlite3Fts3ExprFree(pRoot);
pRoot = 0;
@@ -140749,12 +148692,14 @@ SQLITE_PRIVATE int sqlite3Fts3ExprInitTestInterface(sqlite3* db){
** * The FTS3 module is being built into the core of
** SQLite (in which case SQLITE_ENABLE_FTS3 is defined).
*/
+/* #include "fts3Int.h" */
#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3)
/* #include <assert.h> */
/* #include <stdlib.h> */
/* #include <string.h> */
+/* #include "fts3_hash.h" */
/*
** Malloc and Free functions
@@ -141132,6 +149077,7 @@ SQLITE_PRIVATE void *sqlite3Fts3HashInsert(
** * The FTS3 module is being built into the core of
** SQLite (in which case SQLITE_ENABLE_FTS3 is defined).
*/
+/* #include "fts3Int.h" */
#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3)
/* #include <assert.h> */
@@ -141139,6 +149085,7 @@ SQLITE_PRIVATE void *sqlite3Fts3HashInsert(
/* #include <stdio.h> */
/* #include <string.h> */
+/* #include "fts3_tokenizer.h" */
/*
** Class derived from sqlite3_tokenizer
@@ -141796,12 +149743,25 @@ SQLITE_PRIVATE void sqlite3Fts3PorterTokenizerModule(
** * The FTS3 module is being built into the core of
** SQLite (in which case SQLITE_ENABLE_FTS3 is defined).
*/
+/* #include "fts3Int.h" */
#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3)
/* #include <assert.h> */
/* #include <string.h> */
/*
+** Return true if the two-argument version of fts3_tokenizer()
+** has been activated via a prior call to sqlite3_db_config(db,
+** SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER, 1, 0);
+*/
+static int fts3TokenizerEnabled(sqlite3_context *context){
+ sqlite3 *db = sqlite3_context_db_handle(context);
+ int isEnabled = 0;
+ sqlite3_db_config(db,SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER,-1,&isEnabled);
+ return isEnabled;
+}
+
+/*
** Implementation of the SQL scalar function for accessing the underlying
** hash table. This function may be called as follows:
**
@@ -141821,7 +149781,7 @@ SQLITE_PRIVATE void sqlite3Fts3PorterTokenizerModule(
** is a blob containing the pointer stored as the hash data corresponding
** to string <key-name> (after the hash-table is updated, if applicable).
*/
-static void scalarFunc(
+static void fts3TokenizerFunc(
sqlite3_context *context,
int argc,
sqlite3_value **argv
@@ -141839,16 +149799,20 @@ static void scalarFunc(
nName = sqlite3_value_bytes(argv[0])+1;
if( argc==2 ){
- void *pOld;
- int n = sqlite3_value_bytes(argv[1]);
- if( zName==0 || n!=sizeof(pPtr) ){
- sqlite3_result_error(context, "argument type mismatch", -1);
- return;
- }
- pPtr = *(void **)sqlite3_value_blob(argv[1]);
- pOld = sqlite3Fts3HashInsert(pHash, (void *)zName, nName, pPtr);
- if( pOld==pPtr ){
- sqlite3_result_error(context, "out of memory", -1);
+ if( fts3TokenizerEnabled(context) ){
+ void *pOld;
+ int n = sqlite3_value_bytes(argv[1]);
+ if( zName==0 || n!=sizeof(pPtr) ){
+ sqlite3_result_error(context, "argument type mismatch", -1);
+ return;
+ }
+ pPtr = *(void **)sqlite3_value_blob(argv[1]);
+ pOld = sqlite3Fts3HashInsert(pHash, (void *)zName, nName, pPtr);
+ if( pOld==pPtr ){
+ sqlite3_result_error(context, "out of memory", -1);
+ }
+ }else{
+ sqlite3_result_error(context, "fts3tokenize disabled", -1);
return;
}
}else{
@@ -141862,7 +149826,6 @@ static void scalarFunc(
return;
}
}
-
sqlite3_result_blob(context, (void *)&pPtr, sizeof(pPtr), SQLITE_TRANSIENT);
}
@@ -142122,6 +150085,7 @@ int registerTokenizer(
return sqlite3_finalize(pStmt);
}
+
static
int queryTokenizer(
sqlite3 *db,
@@ -142192,11 +150156,13 @@ static void intTestFunc(
assert( 0==strcmp(sqlite3_errmsg(db), "unknown tokenizer: nosuchtokenizer") );
/* Test the storage function */
- rc = registerTokenizer(db, "nosuchtokenizer", p1);
- assert( rc==SQLITE_OK );
- rc = queryTokenizer(db, "nosuchtokenizer", &p2);
- assert( rc==SQLITE_OK );
- assert( p2==p1 );
+ if( fts3TokenizerEnabled(context) ){
+ rc = registerTokenizer(db, "nosuchtokenizer", p1);
+ assert( rc==SQLITE_OK );
+ rc = queryTokenizer(db, "nosuchtokenizer", &p2);
+ assert( rc==SQLITE_OK );
+ assert( p2==p1 );
+ }
sqlite3_result_text(context, "ok", -1, SQLITE_STATIC);
}
@@ -142212,7 +150178,7 @@ static void intTestFunc(
** sqlite3Fts3HashInit(pHash, FTS3_HASH_STRING, 1);
**
** This function adds a scalar function (see header comment above
-** scalarFunc() in this file for details) and, if ENABLE_TABLE is
+** fts3TokenizerFunc() in this file for details) and, if ENABLE_TABLE is
** defined at compilation time, a temporary virtual table (see header
** comment above struct HashTableVtab) to the database schema. Both
** provide read/write access to the contents of *pHash.
@@ -142241,10 +150207,10 @@ SQLITE_PRIVATE int sqlite3Fts3InitHashTable(
#endif
if( SQLITE_OK==rc ){
- rc = sqlite3_create_function(db, zName, 1, any, p, scalarFunc, 0, 0);
+ rc = sqlite3_create_function(db, zName, 1, any, p, fts3TokenizerFunc, 0, 0);
}
if( SQLITE_OK==rc ){
- rc = sqlite3_create_function(db, zName, 2, any, p, scalarFunc, 0, 0);
+ rc = sqlite3_create_function(db, zName, 2, any, p, fts3TokenizerFunc, 0, 0);
}
#ifdef SQLITE_TEST
if( SQLITE_OK==rc ){
@@ -142291,6 +150257,7 @@ SQLITE_PRIVATE int sqlite3Fts3InitHashTable(
** * The FTS3 module is being built into the core of
** SQLite (in which case SQLITE_ENABLE_FTS3 is defined).
*/
+/* #include "fts3Int.h" */
#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3)
/* #include <assert.h> */
@@ -142298,6 +150265,7 @@ SQLITE_PRIVATE int sqlite3Fts3InitHashTable(
/* #include <stdio.h> */
/* #include <string.h> */
+/* #include "fts3_tokenizer.h" */
typedef struct simple_tokenizer {
sqlite3_tokenizer base;
@@ -142542,6 +150510,7 @@ SQLITE_PRIVATE void sqlite3Fts3SimpleTokenizerModule(
** pos: Token offset of token within input.
**
*/
+/* #include "fts3Int.h" */
#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3)
/* #include <string.h> */
@@ -142977,6 +150946,7 @@ SQLITE_PRIVATE int sqlite3Fts3InitTok(sqlite3 *db, Fts3Hash *pHash){
** code in fts3.c.
*/
+/* #include "fts3Int.h" */
#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3)
/* #include <string.h> */
@@ -143292,7 +151262,8 @@ static int fts3SqlStmt(
** of the oldest level in the db that contains at least ? segments. Or,
** if no level in the FTS index contains more than ? segments, the statement
** returns zero rows. */
-/* 28 */ "SELECT level FROM %Q.'%q_segdir' GROUP BY level HAVING count(*)>=?"
+/* 28 */ "SELECT level, count(*) AS cnt FROM %Q.'%q_segdir' "
+ " GROUP BY level HAVING cnt>=?"
" ORDER BY (level %% 1024) ASC LIMIT 1",
/* Estimate the upper limit on the number of leaf nodes in a new segment
@@ -143819,10 +151790,12 @@ static int fts3PendingTermsAdd(
*/
static int fts3PendingTermsDocid(
Fts3Table *p, /* Full-text table handle */
+ int bDelete, /* True if this op is a delete */
int iLangid, /* Language id of row being written */
sqlite_int64 iDocid /* Docid of row being written */
){
assert( iLangid>=0 );
+ assert( bDelete==1 || bDelete==0 );
/* TODO(shess) Explore whether partially flushing the buffer on
** forced-flush would provide better performance. I suspect that if
@@ -143830,7 +151803,8 @@ static int fts3PendingTermsDocid(
** buffer was half empty, that would let the less frequent terms
** generate longer doclists.
*/
- if( iDocid<=p->iPrevDocid
+ if( iDocid<p->iPrevDocid
+ || (iDocid==p->iPrevDocid && p->bPrevDelete==0)
|| p->iPrevLangid!=iLangid
|| p->nPendingData>p->nMaxPendingData
){
@@ -143839,6 +151813,7 @@ static int fts3PendingTermsDocid(
}
p->iPrevDocid = iDocid;
p->iPrevLangid = iLangid;
+ p->bPrevDelete = bDelete;
return SQLITE_OK;
}
@@ -144028,7 +152003,8 @@ static void fts3DeleteTerms(
if( SQLITE_ROW==sqlite3_step(pSelect) ){
int i;
int iLangid = langidFromSelect(p, pSelect);
- rc = fts3PendingTermsDocid(p, iLangid, sqlite3_column_int64(pSelect, 0));
+ i64 iDocid = sqlite3_column_int64(pSelect, 0);
+ rc = fts3PendingTermsDocid(p, 1, iLangid, iDocid);
for(i=1; rc==SQLITE_OK && i<=p->nColumn; i++){
int iCol = i-1;
if( p->abNotindexed[iCol]==0 ){
@@ -144276,14 +152252,19 @@ static int fts3SegReaderNext(
if( fts3SegReaderIsPending(pReader) ){
Fts3HashElem *pElem = *(pReader->ppNextElem);
- if( pElem==0 ){
- pReader->aNode = 0;
- }else{
+ sqlite3_free(pReader->aNode);
+ pReader->aNode = 0;
+ if( pElem ){
+ char *aCopy;
PendingList *pList = (PendingList *)fts3HashData(pElem);
+ int nCopy = pList->nData+1;
pReader->zTerm = (char *)fts3HashKey(pElem);
pReader->nTerm = fts3HashKeysize(pElem);
- pReader->nNode = pReader->nDoclist = pList->nData + 1;
- pReader->aNode = pReader->aDoclist = pList->aData;
+ aCopy = (char*)sqlite3_malloc(nCopy);
+ if( !aCopy ) return SQLITE_NOMEM;
+ memcpy(aCopy, pList->aData, nCopy);
+ pReader->nNode = pReader->nDoclist = nCopy;
+ pReader->aNode = pReader->aDoclist = aCopy;
pReader->ppNextElem++;
assert( pReader->aNode );
}
@@ -144523,12 +152504,14 @@ SQLITE_PRIVATE int sqlite3Fts3MsrOvfl(
** second argument.
*/
SQLITE_PRIVATE void sqlite3Fts3SegReaderFree(Fts3SegReader *pReader){
- if( pReader && !fts3SegReaderIsPending(pReader) ){
- sqlite3_free(pReader->zTerm);
+ if( pReader ){
+ if( !fts3SegReaderIsPending(pReader) ){
+ sqlite3_free(pReader->zTerm);
+ }
if( !fts3SegReaderIsRootOnly(pReader) ){
sqlite3_free(pReader->aNode);
- sqlite3_blob_close(pReader->pBlob);
}
+ sqlite3_blob_close(pReader->pBlob);
}
sqlite3_free(pReader);
}
@@ -146141,7 +154124,7 @@ static int fts3SegmentMerge(
** segment. The level of the new segment is equal to the numerically
** greatest segment level currently present in the database for this
** index. The idx of the new segment is always 0. */
- if( csr.nSegment==1 ){
+ if( csr.nSegment==1 && 0==fts3SegReaderIsPending(csr.apSegment[0]) ){
rc = SQLITE_DONE;
goto finished;
}
@@ -146471,7 +154454,7 @@ static int fts3DoRebuild(Fts3Table *p){
while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){
int iCol;
int iLangid = langidFromSelect(p, pStmt);
- rc = fts3PendingTermsDocid(p, iLangid, sqlite3_column_int64(pStmt, 0));
+ rc = fts3PendingTermsDocid(p, 0, iLangid, sqlite3_column_int64(pStmt, 0));
memset(aSz, 0, sizeof(aSz[0]) * (p->nColumn+1));
for(iCol=0; rc==SQLITE_OK && iCol<p->nColumn; iCol++){
if( p->abNotindexed[iCol]==0 ){
@@ -147783,10 +155766,11 @@ SQLITE_PRIVATE int sqlite3Fts3Incrmerge(Fts3Table *p, int nMerge, int nMin){
** set nSeg to -1.
*/
rc = fts3SqlStmt(p, SQL_FIND_MERGE_LEVEL, &pFindLevel, 0);
- sqlite3_bind_int(pFindLevel, 1, nMin);
+ sqlite3_bind_int(pFindLevel, 1, MAX(2, nMin));
if( sqlite3_step(pFindLevel)==SQLITE_ROW ){
iAbsLevel = sqlite3_column_int64(pFindLevel, 0);
- nSeg = nMin;
+ nSeg = sqlite3_column_int(pFindLevel, 1);
+ assert( nSeg>=2 );
}else{
nSeg = -1;
}
@@ -148576,7 +156560,7 @@ SQLITE_PRIVATE int sqlite3Fts3UpdateMethod(
}
}
if( rc==SQLITE_OK && (!isRemove || *pRowid!=p->iPrevDocid ) ){
- rc = fts3PendingTermsDocid(p, iLangid, *pRowid);
+ rc = fts3PendingTermsDocid(p, 0, iLangid, *pRowid);
}
if( rc==SQLITE_OK ){
assert( p->iPrevDocid==*pRowid );
@@ -148637,6 +156621,7 @@ SQLITE_PRIVATE int sqlite3Fts3Optimize(Fts3Table *p){
******************************************************************************
*/
+/* #include "fts3Int.h" */
#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3)
/* #include <string.h> */
@@ -148653,6 +156638,7 @@ SQLITE_PRIVATE int sqlite3Fts3Optimize(Fts3Table *p){
#define FTS3_MATCHINFO_LCS 's' /* nCol values */
#define FTS3_MATCHINFO_HITS 'x' /* 3*nCol*nPhrase values */
#define FTS3_MATCHINFO_LHITS 'y' /* nCol*nPhrase values */
+#define FTS3_MATCHINFO_LHITS_BM 'b' /* nCol*nPhrase values */
/*
** The default value for the second argument to matchinfo().
@@ -148714,9 +156700,22 @@ struct MatchInfo {
int nCol; /* Number of columns in table */
int nPhrase; /* Number of matchable phrases in query */
sqlite3_int64 nDoc; /* Number of docs in database */
+ char flag;
u32 *aMatchinfo; /* Pre-allocated buffer */
};
+/*
+** An instance of this structure is used to manage a pair of buffers, each
+** (nElem * sizeof(u32)) bytes in size. See the MatchinfoBuffer code below
+** for details.
+*/
+struct MatchinfoBuffer {
+ u8 aRef[3];
+ int nElem;
+ int bGlobal; /* Set if global data is loaded */
+ char *zMatchinfo;
+ u32 aMatchinfo[1];
+};
/*
@@ -148732,6 +156731,97 @@ struct StrBuffer {
};
+/*************************************************************************
+** Start of MatchinfoBuffer code.
+*/
+
+/*
+** Allocate a two-slot MatchinfoBuffer object.
+*/
+static MatchinfoBuffer *fts3MIBufferNew(int nElem, const char *zMatchinfo){
+ MatchinfoBuffer *pRet;
+ int nByte = sizeof(u32) * (2*nElem + 1) + sizeof(MatchinfoBuffer);
+ int nStr = (int)strlen(zMatchinfo);
+
+ pRet = sqlite3_malloc(nByte + nStr+1);
+ if( pRet ){
+ memset(pRet, 0, nByte);
+ pRet->aMatchinfo[0] = (u8*)(&pRet->aMatchinfo[1]) - (u8*)pRet;
+ pRet->aMatchinfo[1+nElem] = pRet->aMatchinfo[0] + sizeof(u32)*(nElem+1);
+ pRet->nElem = nElem;
+ pRet->zMatchinfo = ((char*)pRet) + nByte;
+ memcpy(pRet->zMatchinfo, zMatchinfo, nStr+1);
+ pRet->aRef[0] = 1;
+ }
+
+ return pRet;
+}
+
+static void fts3MIBufferFree(void *p){
+ MatchinfoBuffer *pBuf = (MatchinfoBuffer*)((u8*)p - ((u32*)p)[-1]);
+
+ assert( (u32*)p==&pBuf->aMatchinfo[1]
+ || (u32*)p==&pBuf->aMatchinfo[pBuf->nElem+2]
+ );
+ if( (u32*)p==&pBuf->aMatchinfo[1] ){
+ pBuf->aRef[1] = 0;
+ }else{
+ pBuf->aRef[2] = 0;
+ }
+
+ if( pBuf->aRef[0]==0 && pBuf->aRef[1]==0 && pBuf->aRef[2]==0 ){
+ sqlite3_free(pBuf);
+ }
+}
+
+static void (*fts3MIBufferAlloc(MatchinfoBuffer *p, u32 **paOut))(void*){
+ void (*xRet)(void*) = 0;
+ u32 *aOut = 0;
+
+ if( p->aRef[1]==0 ){
+ p->aRef[1] = 1;
+ aOut = &p->aMatchinfo[1];
+ xRet = fts3MIBufferFree;
+ }
+ else if( p->aRef[2]==0 ){
+ p->aRef[2] = 1;
+ aOut = &p->aMatchinfo[p->nElem+2];
+ xRet = fts3MIBufferFree;
+ }else{
+ aOut = (u32*)sqlite3_malloc(p->nElem * sizeof(u32));
+ if( aOut ){
+ xRet = sqlite3_free;
+ if( p->bGlobal ) memcpy(aOut, &p->aMatchinfo[1], p->nElem*sizeof(u32));
+ }
+ }
+
+ *paOut = aOut;
+ return xRet;
+}
+
+static void fts3MIBufferSetGlobal(MatchinfoBuffer *p){
+ p->bGlobal = 1;
+ memcpy(&p->aMatchinfo[2+p->nElem], &p->aMatchinfo[1], p->nElem*sizeof(u32));
+}
+
+/*
+** Free a MatchinfoBuffer object allocated using fts3MIBufferNew()
+*/
+SQLITE_PRIVATE void sqlite3Fts3MIBufferFree(MatchinfoBuffer *p){
+ if( p ){
+ assert( p->aRef[0]==1 );
+ p->aRef[0] = 0;
+ if( p->aRef[0]==0 && p->aRef[1]==0 && p->aRef[2]==0 ){
+ sqlite3_free(p);
+ }
+ }
+}
+
+/*
+** End of MatchinfoBuffer code.
+*************************************************************************/
+
+
/*
** This function is used to help iterate through a position-list. A position
** list is a list of unique integers, sorted from smallest to largest. Each
@@ -148768,7 +156858,7 @@ static int fts3ExprIterate2(
void *pCtx /* Second argument to pass to callback */
){
int rc; /* Return code */
- int eType = pExpr->eType; /* Type of expression node pExpr */
+ int eType = pExpr->eType; /* Type of expression node pExpr */
if( eType!=FTSQUERY_PHRASE ){
assert( pExpr->pLeft && pExpr->pRight );
@@ -148802,6 +156892,7 @@ static int fts3ExprIterate(
return fts3ExprIterate2(pExpr, &iPhrase, x, pCtx);
}
+
/*
** This is an fts3ExprIterate() callback used while loading the doclists
** for each phrase into Fts3Expr.aDoclist[]/nDoclist. See also
@@ -148846,8 +156937,7 @@ static int fts3ExprLoadDoclists(
static int fts3ExprPhraseCountCb(Fts3Expr *pExpr, int iPhrase, void *ctx){
(*(int *)ctx)++;
- UNUSED_PARAMETER(pExpr);
- UNUSED_PARAMETER(iPhrase);
+ pExpr->iPhrase = iPhrase;
return SQLITE_OK;
}
static int fts3ExprPhraseCount(Fts3Expr *pExpr){
@@ -149068,7 +157158,7 @@ static int fts3BestSnippet(
sIter.nSnippet = nSnippet;
sIter.nPhrase = nList;
sIter.iCurrent = -1;
- rc = fts3ExprIterate(pCsr->pExpr, fts3SnippetFindPositions, (void *)&sIter);
+ rc = fts3ExprIterate(pCsr->pExpr, fts3SnippetFindPositions, (void*)&sIter);
if( rc==SQLITE_OK ){
/* Set the *pmSeen output variable. */
@@ -149370,6 +157460,60 @@ static int fts3ColumnlistCount(char **ppCollist){
}
/*
+** This function gathers 'y' or 'b' data for a single phrase.
+*/
+static void fts3ExprLHits(
+ Fts3Expr *pExpr, /* Phrase expression node */
+ MatchInfo *p /* Matchinfo context */
+){
+ Fts3Table *pTab = (Fts3Table *)p->pCursor->base.pVtab;
+ int iStart;
+ Fts3Phrase *pPhrase = pExpr->pPhrase;
+ char *pIter = pPhrase->doclist.pList;
+ int iCol = 0;
+
+ assert( p->flag==FTS3_MATCHINFO_LHITS_BM || p->flag==FTS3_MATCHINFO_LHITS );
+ if( p->flag==FTS3_MATCHINFO_LHITS ){
+ iStart = pExpr->iPhrase * p->nCol;
+ }else{
+ iStart = pExpr->iPhrase * ((p->nCol + 31) / 32);
+ }
+
+ while( 1 ){
+ int nHit = fts3ColumnlistCount(&pIter);
+ if( (pPhrase->iColumn>=pTab->nColumn || pPhrase->iColumn==iCol) ){
+ if( p->flag==FTS3_MATCHINFO_LHITS ){
+ p->aMatchinfo[iStart + iCol] = (u32)nHit;
+ }else if( nHit ){
+ p->aMatchinfo[iStart + (iCol+1)/32] |= (1 << (iCol&0x1F));
+ }
+ }
+ assert( *pIter==0x00 || *pIter==0x01 );
+ if( *pIter!=0x01 ) break;
+ pIter++;
+ pIter += fts3GetVarint32(pIter, &iCol);
+ }
+}
+
+/*
+** Gather the results for matchinfo directives 'y' and 'b'.
+*/
+static void fts3ExprLHitGather(
+ Fts3Expr *pExpr,
+ MatchInfo *p
+){
+ assert( (pExpr->pLeft==0)==(pExpr->pRight==0) );
+ if( pExpr->bEof==0 && pExpr->iDocid==p->pCursor->iPrevId ){
+ if( pExpr->pLeft ){
+ fts3ExprLHitGather(pExpr->pLeft, p);
+ fts3ExprLHitGather(pExpr->pRight, p);
+ }else{
+ fts3ExprLHits(pExpr, p);
+ }
+ }
+}
+
+/*
** fts3ExprIterate() callback used to collect the "global" matchinfo stats
** for a single query.
**
@@ -149435,51 +157579,6 @@ 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,
@@ -149493,6 +157592,7 @@ static int fts3MatchinfoCheck(
|| (cArg==FTS3_MATCHINFO_LCS)
|| (cArg==FTS3_MATCHINFO_HITS)
|| (cArg==FTS3_MATCHINFO_LHITS)
+ || (cArg==FTS3_MATCHINFO_LHITS_BM)
){
return SQLITE_OK;
}
@@ -149520,6 +157620,10 @@ static int fts3MatchinfoSize(MatchInfo *pInfo, char cArg){
nVal = pInfo->nCol * pInfo->nPhrase;
break;
+ case FTS3_MATCHINFO_LHITS_BM:
+ nVal = pInfo->nPhrase * ((pInfo->nCol + 31) / 32);
+ break;
+
default:
assert( cArg==FTS3_MATCHINFO_HITS );
nVal = pInfo->nCol * pInfo->nPhrase * 3;
@@ -149714,7 +157818,7 @@ static int fts3MatchinfoValues(
sqlite3_stmt *pSelect = 0;
for(i=0; rc==SQLITE_OK && zArg[i]; i++){
-
+ pInfo->flag = zArg[i];
switch( zArg[i] ){
case FTS3_MATCHINFO_NPHRASE:
if( bGlobal ) pInfo->aMatchinfo[0] = pInfo->nPhrase;
@@ -149774,9 +157878,13 @@ static int fts3MatchinfoValues(
}
break;
- case FTS3_MATCHINFO_LHITS:
- (void)fts3ExprIterate(pCsr->pExpr, fts3ExprLHitsCb, (void*)pInfo);
+ case FTS3_MATCHINFO_LHITS_BM:
+ case FTS3_MATCHINFO_LHITS: {
+ int nZero = fts3MatchinfoSize(pInfo, zArg[i]) * sizeof(u32);
+ memset(pInfo->aMatchinfo, 0, nZero);
+ fts3ExprLHitGather(pCsr->pExpr, pInfo);
break;
+ }
default: {
Fts3Expr *pExpr;
@@ -149790,6 +157898,7 @@ static int fts3MatchinfoValues(
if( rc!=SQLITE_OK ) break;
}
rc = fts3ExprIterate(pExpr, fts3ExprGlobalHitsCb,(void*)pInfo);
+ sqlite3Fts3EvalTestDeferred(pCsr, &rc);
if( rc!=SQLITE_OK ) break;
}
(void)fts3ExprIterate(pExpr, fts3ExprLocalHitsCb,(void*)pInfo);
@@ -149809,7 +157918,8 @@ static int fts3MatchinfoValues(
** Populate pCsr->aMatchinfo[] with data for the current row. The
** 'matchinfo' data is an array of 32-bit unsigned integers (C type u32).
*/
-static int fts3GetMatchinfo(
+static void fts3GetMatchinfo(
+ sqlite3_context *pCtx, /* Return results here */
Fts3Cursor *pCsr, /* FTS3 Cursor object */
const char *zArg /* Second argument to matchinfo() function */
){
@@ -149818,6 +157928,9 @@ static int fts3GetMatchinfo(
int rc = SQLITE_OK;
int bGlobal = 0; /* Collect 'global' stats as well as local */
+ u32 *aOut = 0;
+ void (*xDestroyOut)(void*) = 0;
+
memset(&sInfo, 0, sizeof(MatchInfo));
sInfo.pCursor = pCsr;
sInfo.nCol = pTab->nColumn;
@@ -149825,21 +157938,18 @@ static int fts3GetMatchinfo(
/* If there is cached matchinfo() data, but the format string for the
** cache does not match the format string for this request, discard
** the cached data. */
- if( pCsr->zMatchinfo && strcmp(pCsr->zMatchinfo, zArg) ){
- assert( pCsr->aMatchinfo );
- sqlite3_free(pCsr->aMatchinfo);
- pCsr->zMatchinfo = 0;
- pCsr->aMatchinfo = 0;
+ if( pCsr->pMIBuffer && strcmp(pCsr->pMIBuffer->zMatchinfo, zArg) ){
+ sqlite3Fts3MIBufferFree(pCsr->pMIBuffer);
+ pCsr->pMIBuffer = 0;
}
- /* If Fts3Cursor.aMatchinfo[] is NULL, then this is the first time the
+ /* If Fts3Cursor.pMIBuffer is NULL, then this is the first time the
** matchinfo function has been called for this query. In this case
** allocate the array used to accumulate the matchinfo data and
** initialize those elements that are constant for every row.
*/
- if( pCsr->aMatchinfo==0 ){
+ if( pCsr->pMIBuffer==0 ){
int nMatchinfo = 0; /* Number of u32 elements in match-info */
- int nArg; /* Bytes in zArg */
int i; /* Used to iterate through zArg */
/* Determine the number of phrases in the query */
@@ -149848,30 +157958,46 @@ static int fts3GetMatchinfo(
/* Determine the number of integers in the buffer returned by this call. */
for(i=0; zArg[i]; i++){
+ char *zErr = 0;
+ if( fts3MatchinfoCheck(pTab, zArg[i], &zErr) ){
+ sqlite3_result_error(pCtx, zErr, -1);
+ sqlite3_free(zErr);
+ return;
+ }
nMatchinfo += fts3MatchinfoSize(&sInfo, zArg[i]);
}
/* Allocate space for Fts3Cursor.aMatchinfo[] and Fts3Cursor.zMatchinfo. */
- nArg = (int)strlen(zArg);
- pCsr->aMatchinfo = (u32 *)sqlite3_malloc(sizeof(u32)*nMatchinfo + nArg + 1);
- if( !pCsr->aMatchinfo ) return SQLITE_NOMEM;
-
- pCsr->zMatchinfo = (char *)&pCsr->aMatchinfo[nMatchinfo];
- pCsr->nMatchinfo = nMatchinfo;
- memcpy(pCsr->zMatchinfo, zArg, nArg+1);
- memset(pCsr->aMatchinfo, 0, sizeof(u32)*nMatchinfo);
+ pCsr->pMIBuffer = fts3MIBufferNew(nMatchinfo, zArg);
+ if( !pCsr->pMIBuffer ) rc = SQLITE_NOMEM;
+
pCsr->isMatchinfoNeeded = 1;
bGlobal = 1;
}
- sInfo.aMatchinfo = pCsr->aMatchinfo;
- sInfo.nPhrase = pCsr->nPhrase;
- if( pCsr->isMatchinfoNeeded ){
+ if( rc==SQLITE_OK ){
+ xDestroyOut = fts3MIBufferAlloc(pCsr->pMIBuffer, &aOut);
+ if( xDestroyOut==0 ){
+ rc = SQLITE_NOMEM;
+ }
+ }
+
+ if( rc==SQLITE_OK ){
+ sInfo.aMatchinfo = aOut;
+ sInfo.nPhrase = pCsr->nPhrase;
rc = fts3MatchinfoValues(pCsr, bGlobal, &sInfo, zArg);
- pCsr->isMatchinfoNeeded = 0;
+ if( bGlobal ){
+ fts3MIBufferSetGlobal(pCsr->pMIBuffer);
+ }
}
- return rc;
+ if( rc!=SQLITE_OK ){
+ sqlite3_result_error_code(pCtx, rc);
+ if( xDestroyOut ) xDestroyOut(aOut);
+ }else{
+ int n = pCsr->pMIBuffer->nElem * sizeof(u32);
+ sqlite3_result_blob(pCtx, aOut, n, xDestroyOut);
+ }
}
/*
@@ -150077,7 +158203,7 @@ SQLITE_PRIVATE void sqlite3Fts3Offsets(
*/
sCtx.iCol = iCol;
sCtx.iTerm = 0;
- (void)fts3ExprIterate(pCsr->pExpr, fts3ExprTermOffsetInit, (void *)&sCtx);
+ (void)fts3ExprIterate(pCsr->pExpr, fts3ExprTermOffsetInit, (void*)&sCtx);
/* Retreive the text stored in column iCol. If an SQL NULL is stored
** in column iCol, jump immediately to the next iteration of the loop.
@@ -150169,19 +158295,9 @@ SQLITE_PRIVATE void sqlite3Fts3Matchinfo(
const char *zArg /* Second arg to matchinfo() function */
){
Fts3Table *pTab = (Fts3Table *)pCsr->base.pVtab;
- int rc;
- int i;
const char *zFormat;
if( zArg ){
- for(i=0; zArg[i]; i++){
- char *zErr = 0;
- if( fts3MatchinfoCheck(pTab, zArg[i], &zErr) ){
- sqlite3_result_error(pContext, zErr, -1);
- sqlite3_free(zErr);
- return;
- }
- }
zFormat = zArg;
}else{
zFormat = FTS3_MATCHINFO_DEFAULT;
@@ -150190,17 +158306,10 @@ SQLITE_PRIVATE void sqlite3Fts3Matchinfo(
if( !pCsr->pExpr ){
sqlite3_result_blob(pContext, "", 0, SQLITE_STATIC);
return;
- }
-
- /* Retrieve matchinfo() data. */
- rc = fts3GetMatchinfo(pCsr, zFormat);
- sqlite3Fts3SegmentsClose(pTab);
-
- if( rc!=SQLITE_OK ){
- sqlite3_result_error_code(pContext, rc);
}else{
- int n = pCsr->nMatchinfo * sizeof(u32);
- sqlite3_result_blob(pContext, pCsr->aMatchinfo, n, SQLITE_TRANSIENT);
+ /* Retrieve matchinfo() data. */
+ fts3GetMatchinfo(pContext, pCsr, zFormat);
+ sqlite3Fts3SegmentsClose(pTab);
}
}
@@ -150225,6 +158334,7 @@ SQLITE_PRIVATE void sqlite3Fts3Matchinfo(
#ifndef SQLITE_DISABLE_FTS3_UNICODE
+/* #include "fts3Int.h" */
#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3)
/* #include <assert.h> */
@@ -150232,6 +158342,7 @@ SQLITE_PRIVATE void sqlite3Fts3Matchinfo(
/* #include <stdio.h> */
/* #include <string.h> */
+/* #include "fts3_tokenizer.h" */
/*
** The following two macros - READ_UTF8 and WRITE_UTF8 - have been copied
@@ -151027,8 +159138,10 @@ SQLITE_PRIVATE int sqlite3FtsUnicodeFold(int c, int bRemoveDiacritic){
#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_RTREE)
#ifndef SQLITE_CORE
+/* #include "sqlite3ext.h" */
SQLITE_EXTENSION_INIT1
#else
+/* #include "sqlite3.h" */
#endif
/* #include <string.h> */
@@ -151321,6 +159434,7 @@ struct RtreeMatchArg {
u32 magic; /* Always RTREE_GEOMETRY_MAGIC */
RtreeGeomCallback cb; /* Info about the callback functions */
int nParam; /* Number of parameters to the SQL function */
+ sqlite3_value **apSqlParam; /* Original SQL parameter values */
RtreeDValue aParam[1]; /* Values for parameters to the SQL function */
};
@@ -152452,9 +160566,7 @@ static int deserializeGeometry(sqlite3_value *pValue, RtreeConstraint *pCons){
/* Check that the blob is roughly the right size. */
nBlob = sqlite3_value_bytes(pValue);
- if( nBlob<(int)sizeof(RtreeMatchArg)
- || ((nBlob-sizeof(RtreeMatchArg))%sizeof(RtreeDValue))!=0
- ){
+ if( nBlob<(int)sizeof(RtreeMatchArg) ){
return SQLITE_ERROR;
}
@@ -152465,6 +160577,7 @@ static int deserializeGeometry(sqlite3_value *pValue, RtreeConstraint *pCons){
memcpy(pBlob, sqlite3_value_blob(pValue), nBlob);
nExpected = (int)(sizeof(RtreeMatchArg) +
+ pBlob->nParam*sizeof(sqlite3_value*) +
(pBlob->nParam-1)*sizeof(RtreeDValue));
if( pBlob->magic!=RTREE_GEOMETRY_MAGIC || nBlob!=nExpected ){
sqlite3_free(pInfo);
@@ -152473,6 +160586,7 @@ static int deserializeGeometry(sqlite3_value *pValue, RtreeConstraint *pCons){
pInfo->pContext = pBlob->cb.pContext;
pInfo->nParam = pBlob->nParam;
pInfo->aParam = pBlob->aParam;
+ pInfo->apSqlParam = pBlob->apSqlParam;
if( pBlob->cb.xGeom ){
pCons->u.xGeom = pBlob->cb.xGeom;
@@ -152511,7 +160625,7 @@ static int rtreeFilter(
if( idxNum==1 ){
/* Special case - lookup by rowid. */
RtreeNode *pLeaf; /* Leaf on which the required cell resides */
- RtreeSearchPoint *p; /* Search point for the leaf */
+ RtreeSearchPoint *p; /* Search point for the the leaf */
i64 iRowid = sqlite3_value_int64(argv[0]);
i64 iNode = 0;
rc = findLeafNode(pRtree, iRowid, &pLeaf, &iNode);
@@ -152639,17 +160753,30 @@ static int rtreeBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
Rtree *pRtree = (Rtree*)tab;
int rc = SQLITE_OK;
int ii;
+ int bMatch = 0; /* True if there exists a MATCH constraint */
i64 nRow; /* Estimated rows returned by this scan */
int iIdx = 0;
char zIdxStr[RTREE_MAX_DIMENSIONS*8+1];
memset(zIdxStr, 0, sizeof(zIdxStr));
+ /* Check if there exists a MATCH constraint - even an unusable one. If there
+ ** is, do not consider the lookup-by-rowid plan as using such a plan would
+ ** require the VDBE to evaluate the MATCH constraint, which is not currently
+ ** possible. */
+ for(ii=0; ii<pIdxInfo->nConstraint; ii++){
+ if( pIdxInfo->aConstraint[ii].op==SQLITE_INDEX_CONSTRAINT_MATCH ){
+ bMatch = 1;
+ }
+ }
+
assert( pIdxInfo->idxStr==0 );
for(ii=0; ii<pIdxInfo->nConstraint && iIdx<(int)(sizeof(zIdxStr)-1); ii++){
struct sqlite3_index_constraint *p = &pIdxInfo->aConstraint[ii];
- if( p->usable && p->iColumn==0 && p->op==SQLITE_INDEX_CONSTRAINT_EQ ){
+ if( bMatch==0 && p->usable
+ && p->iColumn==0 && p->op==SQLITE_INDEX_CONSTRAINT_EQ
+ ){
/* We have an equality constraint on the rowid. Use strategy 1. */
int jj;
for(jj=0; jj<ii; jj++){
@@ -152697,7 +160824,7 @@ static int rtreeBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
return SQLITE_NOMEM;
}
- nRow = pRtree->nRowEst / (iIdx + 1);
+ nRow = pRtree->nRowEst >> (iIdx/2);
pIdxInfo->estimatedCost = (double)6.0 * (double)nRow;
setEstimatedRows(pIdxInfo, nRow);
@@ -154343,6 +162470,18 @@ static void rtreeFreeCallback(void *p){
}
/*
+** This routine frees the BLOB that is returned by geomCallback().
+*/
+static void rtreeMatchArgFree(void *pArg){
+ int i;
+ RtreeMatchArg *p = (RtreeMatchArg*)pArg;
+ for(i=0; i<p->nParam; i++){
+ sqlite3_value_free(p->apSqlParam[i]);
+ }
+ sqlite3_free(p);
+}
+
+/*
** Each call to sqlite3_rtree_geometry_callback() or
** sqlite3_rtree_query_callback() creates an ordinary SQLite
** scalar function that is implemented by this routine.
@@ -154360,8 +162499,10 @@ static void geomCallback(sqlite3_context *ctx, int nArg, sqlite3_value **aArg){
RtreeGeomCallback *pGeomCtx = (RtreeGeomCallback *)sqlite3_user_data(ctx);
RtreeMatchArg *pBlob;
int nBlob;
+ int memErr = 0;
- nBlob = sizeof(RtreeMatchArg) + (nArg-1)*sizeof(RtreeDValue);
+ nBlob = sizeof(RtreeMatchArg) + (nArg-1)*sizeof(RtreeDValue)
+ + nArg*sizeof(sqlite3_value*);
pBlob = (RtreeMatchArg *)sqlite3_malloc(nBlob);
if( !pBlob ){
sqlite3_result_error_nomem(ctx);
@@ -154369,15 +162510,23 @@ static void geomCallback(sqlite3_context *ctx, int nArg, sqlite3_value **aArg){
int i;
pBlob->magic = RTREE_GEOMETRY_MAGIC;
pBlob->cb = pGeomCtx[0];
+ pBlob->apSqlParam = (sqlite3_value**)&pBlob->aParam[nArg];
pBlob->nParam = nArg;
for(i=0; i<nArg; i++){
+ pBlob->apSqlParam[i] = sqlite3_value_dup(aArg[i]);
+ if( pBlob->apSqlParam[i]==0 ) memErr = 1;
#ifdef SQLITE_RTREE_INT_ONLY
pBlob->aParam[i] = sqlite3_value_int64(aArg[i]);
#else
pBlob->aParam[i] = sqlite3_value_double(aArg[i]);
#endif
}
- sqlite3_result_blob(ctx, pBlob, nBlob, sqlite3_free);
+ if( memErr ){
+ sqlite3_result_error_nomem(ctx);
+ rtreeMatchArgFree(pBlob);
+ }else{
+ sqlite3_result_blob(ctx, pBlob, nBlob, rtreeMatchArgFree);
+ }
}
}
@@ -154488,8 +162637,10 @@ SQLITE_API int SQLITE_STDCALL sqlite3_rtree_init(
/* #include <assert.h> */
#ifndef SQLITE_CORE
+/* #include "sqlite3ext.h" */
SQLITE_EXTENSION_INIT1
#else
+/* #include "sqlite3.h" */
#endif
/*
@@ -154508,6 +162659,38 @@ static void xFree(void *p){
}
/*
+** This lookup table is used to help decode the first byte of
+** a multi-byte UTF8 character. It is copied here from SQLite source
+** code file utf8.c.
+*/
+static const unsigned char icuUtf8Trans1[] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x00, 0x01, 0x02, 0x03, 0x00, 0x01, 0x00, 0x00,
+};
+
+#define SQLITE_ICU_READ_UTF8(zIn, c) \
+ c = *(zIn++); \
+ if( c>=0xc0 ){ \
+ c = icuUtf8Trans1[c-0xc0]; \
+ while( (*zIn & 0xc0)==0x80 ){ \
+ c = (c<<6) + (0x3f & *(zIn++)); \
+ } \
+ }
+
+#define SQLITE_ICU_SKIP_UTF8(zIn) \
+ assert( *zIn ); \
+ if( *(zIn++)>=0xc0 ){ \
+ while( (*zIn & 0xc0)==0x80 ){zIn++;} \
+ }
+
+
+/*
** Compare two UTF-8 strings for equality where the first string is
** a "LIKE" expression. Return true (1) if they are the same and
** false (0) if they are different.
@@ -154520,17 +162703,14 @@ static int icuLikeCompare(
static const int MATCH_ONE = (UChar32)'_';
static const int MATCH_ALL = (UChar32)'%';
- int iPattern = 0; /* Current byte index in zPattern */
- int iString = 0; /* Current byte index in zString */
-
int prevEscape = 0; /* True if the previous character was uEsc */
- while( zPattern[iPattern]!=0 ){
+ while( 1 ){
/* Read (and consume) the next character from the input pattern. */
UChar32 uPattern;
- U8_NEXT_UNSAFE(zPattern, iPattern, uPattern);
- assert(uPattern!=0);
+ SQLITE_ICU_READ_UTF8(zPattern, uPattern);
+ if( uPattern==0 ) break;
/* There are now 4 possibilities:
**
@@ -154547,28 +162727,28 @@ static int icuLikeCompare(
** MATCH_ALL. For each MATCH_ONE, skip one character in the
** test string.
*/
- while( (c=zPattern[iPattern]) == MATCH_ALL || c == MATCH_ONE ){
+ while( (c=*zPattern) == MATCH_ALL || c == MATCH_ONE ){
if( c==MATCH_ONE ){
- if( zString[iString]==0 ) return 0;
- U8_FWD_1_UNSAFE(zString, iString);
+ if( *zString==0 ) return 0;
+ SQLITE_ICU_SKIP_UTF8(zString);
}
- iPattern++;
+ zPattern++;
}
- if( zPattern[iPattern]==0 ) return 1;
+ if( *zPattern==0 ) return 1;
- while( zString[iString] ){
- if( icuLikeCompare(&zPattern[iPattern], &zString[iString], uEsc) ){
+ while( *zString ){
+ if( icuLikeCompare(zPattern, zString, uEsc) ){
return 1;
}
- U8_FWD_1_UNSAFE(zString, iString);
+ SQLITE_ICU_SKIP_UTF8(zString);
}
return 0;
}else if( !prevEscape && uPattern==MATCH_ONE ){
/* Case 2. */
- if( zString[iString]==0 ) return 0;
- U8_FWD_1_UNSAFE(zString, iString);
+ if( *zString==0 ) return 0;
+ SQLITE_ICU_SKIP_UTF8(zString);
}else if( !prevEscape && uPattern==uEsc){
/* Case 3. */
@@ -154577,7 +162757,7 @@ static int icuLikeCompare(
}else{
/* Case 4. */
UChar32 uString;
- U8_NEXT_UNSAFE(zString, iString, uString);
+ SQLITE_ICU_READ_UTF8(zString, uString);
uString = u_foldCase(uString, U_FOLD_CASE_DEFAULT);
uPattern = u_foldCase(uPattern, U_FOLD_CASE_DEFAULT);
if( uString!=uPattern ){
@@ -154587,7 +162767,7 @@ static int icuLikeCompare(
}
}
- return zString[iString]==0;
+ return *zString==0;
}
/*
@@ -154772,15 +162952,17 @@ static void icuRegexpFunc(sqlite3_context *p, int nArg, sqlite3_value **apArg){
** http://www.icu-project.org/userguide/posix.html#case_mappings
*/
static void icuCaseFunc16(sqlite3_context *p, int nArg, sqlite3_value **apArg){
- const UChar *zInput;
- UChar *zOutput;
- int nInput;
- int nOutput;
-
- UErrorCode status = U_ZERO_ERROR;
+ const UChar *zInput; /* Pointer to input string */
+ UChar *zOutput = 0; /* Pointer to output buffer */
+ int nInput; /* Size of utf-16 input string in bytes */
+ int nOut; /* Size of output buffer in bytes */
+ int cnt;
+ int bToUpper; /* True for toupper(), false for tolower() */
+ UErrorCode status;
const char *zLocale = 0;
assert(nArg==1 || nArg==2);
+ bToUpper = (sqlite3_user_data(p)!=0);
if( nArg==2 ){
zLocale = (const char *)sqlite3_value_text(apArg[1]);
}
@@ -154789,26 +162971,38 @@ static void icuCaseFunc16(sqlite3_context *p, int nArg, sqlite3_value **apArg){
if( !zInput ){
return;
}
- nInput = sqlite3_value_bytes16(apArg[0]);
-
- nOutput = nInput * 2 + 2;
- zOutput = sqlite3_malloc(nOutput);
- if( !zOutput ){
+ nOut = nInput = sqlite3_value_bytes16(apArg[0]);
+ if( nOut==0 ){
+ sqlite3_result_text16(p, "", 0, SQLITE_STATIC);
return;
}
- if( sqlite3_user_data(p) ){
- u_strToUpper(zOutput, nOutput/2, zInput, nInput/2, zLocale, &status);
- }else{
- u_strToLower(zOutput, nOutput/2, zInput, nInput/2, zLocale, &status);
- }
+ for(cnt=0; cnt<2; cnt++){
+ UChar *zNew = sqlite3_realloc(zOutput, nOut);
+ if( zNew==0 ){
+ sqlite3_free(zOutput);
+ sqlite3_result_error_nomem(p);
+ return;
+ }
+ zOutput = zNew;
+ status = U_ZERO_ERROR;
+ if( bToUpper ){
+ nOut = 2*u_strToUpper(zOutput,nOut/2,zInput,nInput/2,zLocale,&status);
+ }else{
+ nOut = 2*u_strToLower(zOutput,nOut/2,zInput,nInput/2,zLocale,&status);
+ }
- if( !U_SUCCESS(status) ){
- icuFunctionError(p, "u_strToLower()/u_strToUpper", status);
+ if( U_SUCCESS(status) ){
+ sqlite3_result_text16(p, zOutput, nOut, xFree);
+ }else if( status==U_BUFFER_OVERFLOW_ERROR ){
+ assert( cnt==0 );
+ continue;
+ }else{
+ icuFunctionError(p, bToUpper ? "u_strToUpper" : "u_strToLower", status);
+ }
return;
}
-
- sqlite3_result_text16(p, zOutput, -1, xFree);
+ assert( 0 ); /* Unreachable */
}
/*
@@ -154869,6 +163063,7 @@ static void icuLoadCollation(
int rc; /* Return code from sqlite3_create_collation_x() */
assert(nArg==2);
+ (void)nArg; /* Unused parameter */
zLocale = (const char *)sqlite3_value_text(apArg[0]);
zName = (const char *)sqlite3_value_text(apArg[1]);
@@ -154965,11 +163160,13 @@ SQLITE_API int SQLITE_STDCALL sqlite3_icu_init(
*************************************************************************
** This file implements a tokenizer for fts3 based on the ICU library.
*/
+/* #include "fts3Int.h" */
#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3)
#ifdef SQLITE_ENABLE_ICU
/* #include <assert.h> */
/* #include <string.h> */
+/* #include "fts3_tokenizer.h" */
#include <unicode/ubrk.h>
/* #include <unicode/ucol.h> */
@@ -155192,12 +163389,13 @@ static int icuNext(
** The set of routines that implement the simple tokenizer
*/
static const sqlite3_tokenizer_module icuTokenizerModule = {
- 0, /* iVersion */
- icuCreate, /* xCreate */
- icuDestroy, /* xCreate */
- icuOpen, /* xOpen */
- icuClose, /* xClose */
- icuNext, /* xNext */
+ 0, /* iVersion */
+ icuCreate, /* xCreate */
+ icuDestroy, /* xCreate */
+ icuOpen, /* xOpen */
+ icuClose, /* xClose */
+ icuNext, /* xNext */
+ 0, /* xLanguageid */
};
/*
@@ -155213,6 +163411,5230 @@ SQLITE_PRIVATE void sqlite3Fts3IcuTokenizerModule(
#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) */
/************** End of fts3_icu.c ********************************************/
+/************** Begin file sqlite3rbu.c **************************************/
+/*
+** 2014 August 30
+**
+** 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.
+**
+*************************************************************************
+**
+**
+** OVERVIEW
+**
+** The RBU extension requires that the RBU update be packaged as an
+** SQLite database. The tables it expects to find are described in
+** sqlite3rbu.h. Essentially, for each table xyz in the target database
+** that the user wishes to write to, a corresponding data_xyz table is
+** created in the RBU database and populated with one row for each row to
+** update, insert or delete from the target table.
+**
+** The update proceeds in three stages:
+**
+** 1) The database is updated. The modified database pages are written
+** to a *-oal file. A *-oal file is just like a *-wal file, except
+** that it is named "<database>-oal" instead of "<database>-wal".
+** Because regular SQLite clients do not look for file named
+** "<database>-oal", they go on using the original database in
+** rollback mode while the *-oal file is being generated.
+**
+** During this stage RBU does not update the database by writing
+** directly to the target tables. Instead it creates "imposter"
+** tables using the SQLITE_TESTCTRL_IMPOSTER interface that it uses
+** to update each b-tree individually. All updates required by each
+** b-tree are completed before moving on to the next, and all
+** updates are done in sorted key order.
+**
+** 2) The "<database>-oal" file is moved to the equivalent "<database>-wal"
+** location using a call to rename(2). Before doing this the RBU
+** module takes an EXCLUSIVE lock on the database file, ensuring
+** that there are no other active readers.
+**
+** Once the EXCLUSIVE lock is released, any other database readers
+** detect the new *-wal file and read the database in wal mode. At
+** this point they see the new version of the database - including
+** the updates made as part of the RBU update.
+**
+** 3) The new *-wal file is checkpointed. This proceeds in the same way
+** as a regular database checkpoint, except that a single frame is
+** checkpointed each time sqlite3rbu_step() is called. If the RBU
+** handle is closed before the entire *-wal file is checkpointed,
+** the checkpoint progress is saved in the RBU database and the
+** checkpoint can be resumed by another RBU client at some point in
+** the future.
+**
+** POTENTIAL PROBLEMS
+**
+** The rename() call might not be portable. And RBU is not currently
+** syncing the directory after renaming the file.
+**
+** When state is saved, any commit to the *-oal file and the commit to
+** the RBU update database are not atomic. So if the power fails at the
+** wrong moment they might get out of sync. As the main database will be
+** committed before the RBU update database this will likely either just
+** pass unnoticed, or result in SQLITE_CONSTRAINT errors (due to UNIQUE
+** constraint violations).
+**
+** If some client does modify the target database mid RBU update, or some
+** other error occurs, the RBU extension will keep throwing errors. It's
+** not really clear how to get out of this state. The system could just
+** by delete the RBU update database and *-oal file and have the device
+** download the update again and start over.
+**
+** At present, for an UPDATE, both the new.* and old.* records are
+** collected in the rbu_xyz table. And for both UPDATEs and DELETEs all
+** fields are collected. This means we're probably writing a lot more
+** data to disk when saving the state of an ongoing update to the RBU
+** update database than is strictly necessary.
+**
+*/
+
+/* #include <assert.h> */
+/* #include <string.h> */
+/* #include <stdio.h> */
+
+/* #include "sqlite3.h" */
+
+#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_RBU)
+/************** Include sqlite3rbu.h in the middle of sqlite3rbu.c ***********/
+/************** Begin file sqlite3rbu.h **************************************/
+/*
+** 2014 August 30
+**
+** 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 the public interface for the RBU extension.
+*/
+
+/*
+** SUMMARY
+**
+** Writing a transaction containing a large number of operations on
+** b-tree indexes that are collectively larger than the available cache
+** memory can be very inefficient.
+**
+** The problem is that in order to update a b-tree, the leaf page (at least)
+** containing the entry being inserted or deleted must be modified. If the
+** working set of leaves is larger than the available cache memory, then a
+** single leaf that is modified more than once as part of the transaction
+** may be loaded from or written to the persistent media multiple times.
+** Additionally, because the index updates are likely to be applied in
+** random order, access to pages within the database is also likely to be in
+** random order, which is itself quite inefficient.
+**
+** One way to improve the situation is to sort the operations on each index
+** by index key before applying them to the b-tree. This leads to an IO
+** pattern that resembles a single linear scan through the index b-tree,
+** and all but guarantees each modified leaf page is loaded and stored
+** exactly once. SQLite uses this trick to improve the performance of
+** CREATE INDEX commands. This extension allows it to be used to improve
+** the performance of large transactions on existing databases.
+**
+** Additionally, this extension allows the work involved in writing the
+** large transaction to be broken down into sub-transactions performed
+** sequentially by separate processes. This is useful if the system cannot
+** guarantee that a single update process will run for long enough to apply
+** the entire update, for example because the update is being applied on a
+** mobile device that is frequently rebooted. Even after the writer process
+** has committed one or more sub-transactions, other database clients continue
+** to read from the original database snapshot. In other words, partially
+** applied transactions are not visible to other clients.
+**
+** "RBU" stands for "Resumable Bulk Update". As in a large database update
+** transmitted via a wireless network to a mobile device. A transaction
+** applied using this extension is hence refered to as an "RBU update".
+**
+**
+** LIMITATIONS
+**
+** An "RBU update" transaction is subject to the following limitations:
+**
+** * The transaction must consist of INSERT, UPDATE and DELETE operations
+** only.
+**
+** * INSERT statements may not use any default values.
+**
+** * UPDATE and DELETE statements must identify their target rows by
+** non-NULL PRIMARY KEY values. Rows with NULL values stored in PRIMARY
+** KEY fields may not be updated or deleted. If the table being written
+** has no PRIMARY KEY, affected rows must be identified by rowid.
+**
+** * UPDATE statements may not modify PRIMARY KEY columns.
+**
+** * No triggers will be fired.
+**
+** * No foreign key violations are detected or reported.
+**
+** * CHECK constraints are not enforced.
+**
+** * No constraint handling mode except for "OR ROLLBACK" is supported.
+**
+**
+** PREPARATION
+**
+** An "RBU update" is stored as a separate SQLite database. A database
+** containing an RBU update is an "RBU database". For each table in the
+** target database to be updated, the RBU database should contain a table
+** named "data_<target name>" containing the same set of columns as the
+** target table, and one more - "rbu_control". The data_% table should
+** have no PRIMARY KEY or UNIQUE constraints, but each column should have
+** the same type as the corresponding column in the target database.
+** The "rbu_control" column should have no type at all. For example, if
+** the target database contains:
+**
+** CREATE TABLE t1(a INTEGER PRIMARY KEY, b TEXT, c UNIQUE);
+**
+** Then the RBU database should contain:
+**
+** CREATE TABLE data_t1(a INTEGER, b TEXT, c, rbu_control);
+**
+** The order of the columns in the data_% table does not matter.
+**
+** Instead of a regular table, the RBU database may also contain virtual
+** tables or view named using the data_<target> naming scheme.
+**
+** Instead of the plain data_<target> naming scheme, RBU database tables
+** may also be named data<integer>_<target>, where <integer> is any sequence
+** of zero or more numeric characters (0-9). This can be significant because
+** tables within the RBU database are always processed in order sorted by
+** name. By judicious selection of the the <integer> portion of the names
+** of the RBU tables the user can therefore control the order in which they
+** are processed. This can be useful, for example, to ensure that "external
+** content" FTS4 tables are updated before their underlying content tables.
+**
+** If the target database table is a virtual table or a table that has no
+** PRIMARY KEY declaration, the data_% table must also contain a column
+** named "rbu_rowid". This column is mapped to the tables implicit primary
+** key column - "rowid". Virtual tables for which the "rowid" column does
+** not function like a primary key value cannot be updated using RBU. For
+** example, if the target db contains either of the following:
+**
+** CREATE VIRTUAL TABLE x1 USING fts3(a, b);
+** CREATE TABLE x1(a, b)
+**
+** then the RBU database should contain:
+**
+** CREATE TABLE data_x1(a, b, rbu_rowid, rbu_control);
+**
+** All non-hidden columns (i.e. all columns matched by "SELECT *") of the
+** target table must be present in the input table. For virtual tables,
+** hidden columns are optional - they are updated by RBU if present in
+** the input table, or not otherwise. For example, to write to an fts4
+** table with a hidden languageid column such as:
+**
+** CREATE VIRTUAL TABLE ft1 USING fts4(a, b, languageid='langid');
+**
+** Either of the following input table schemas may be used:
+**
+** CREATE TABLE data_ft1(a, b, langid, rbu_rowid, rbu_control);
+** CREATE TABLE data_ft1(a, b, rbu_rowid, rbu_control);
+**
+** For each row to INSERT into the target database as part of the RBU
+** update, the corresponding data_% table should contain a single record
+** with the "rbu_control" column set to contain integer value 0. The
+** other columns should be set to the values that make up the new record
+** to insert.
+**
+** If the target database table has an INTEGER PRIMARY KEY, it is not
+** possible to insert a NULL value into the IPK column. Attempting to
+** do so results in an SQLITE_MISMATCH error.
+**
+** For each row to DELETE from the target database as part of the RBU
+** update, the corresponding data_% table should contain a single record
+** with the "rbu_control" column set to contain integer value 1. The
+** real primary key values of the row to delete should be stored in the
+** corresponding columns of the data_% table. The values stored in the
+** other columns are not used.
+**
+** For each row to UPDATE from the target database as part of the RBU
+** update, the corresponding data_% table should contain a single record
+** with the "rbu_control" column set to contain a value of type text.
+** The real primary key values identifying the row to update should be
+** stored in the corresponding columns of the data_% table row, as should
+** the new values of all columns being update. The text value in the
+** "rbu_control" column must contain the same number of characters as
+** there are columns in the target database table, and must consist entirely
+** of 'x' and '.' characters (or in some special cases 'd' - see below). For
+** each column that is being updated, the corresponding character is set to
+** 'x'. For those that remain as they are, the corresponding character of the
+** rbu_control value should be set to '.'. For example, given the tables
+** above, the update statement:
+**
+** UPDATE t1 SET c = 'usa' WHERE a = 4;
+**
+** is represented by the data_t1 row created by:
+**
+** INSERT INTO data_t1(a, b, c, rbu_control) VALUES(4, NULL, 'usa', '..x');
+**
+** Instead of an 'x' character, characters of the rbu_control value specified
+** for UPDATEs may also be set to 'd'. In this case, instead of updating the
+** target table with the value stored in the corresponding data_% column, the
+** user-defined SQL function "rbu_delta()" is invoked and the result stored in
+** the target table column. rbu_delta() is invoked with two arguments - the
+** original value currently stored in the target table column and the
+** value specified in the data_xxx table.
+**
+** For example, this row:
+**
+** INSERT INTO data_t1(a, b, c, rbu_control) VALUES(4, NULL, 'usa', '..d');
+**
+** is similar to an UPDATE statement such as:
+**
+** UPDATE t1 SET c = rbu_delta(c, 'usa') WHERE a = 4;
+**
+** Finally, if an 'f' character appears in place of a 'd' or 's' in an
+** ota_control string, the contents of the data_xxx table column is assumed
+** to be a "fossil delta" - a patch to be applied to a blob value in the
+** format used by the fossil source-code management system. In this case
+** the existing value within the target database table must be of type BLOB.
+** It is replaced by the result of applying the specified fossil delta to
+** itself.
+**
+** If the target database table is a virtual table or a table with no PRIMARY
+** KEY, the rbu_control value should not include a character corresponding
+** to the rbu_rowid value. For example, this:
+**
+** INSERT INTO data_ft1(a, b, rbu_rowid, rbu_control)
+** VALUES(NULL, 'usa', 12, '.x');
+**
+** causes a result similar to:
+**
+** UPDATE ft1 SET b = 'usa' WHERE rowid = 12;
+**
+** The data_xxx tables themselves should have no PRIMARY KEY declarations.
+** However, RBU is more efficient if reading the rows in from each data_xxx
+** table in "rowid" order is roughly the same as reading them sorted by
+** the PRIMARY KEY of the corresponding target database table. In other
+** words, rows should be sorted using the destination table PRIMARY KEY
+** fields before they are inserted into the data_xxx tables.
+**
+** USAGE
+**
+** The API declared below allows an application to apply an RBU update
+** stored on disk to an existing target database. Essentially, the
+** application:
+**
+** 1) Opens an RBU handle using the sqlite3rbu_open() function.
+**
+** 2) Registers any required virtual table modules with the database
+** handle returned by sqlite3rbu_db(). Also, if required, register
+** the rbu_delta() implementation.
+**
+** 3) Calls the sqlite3rbu_step() function one or more times on
+** the new handle. Each call to sqlite3rbu_step() performs a single
+** b-tree operation, so thousands of calls may be required to apply
+** a complete update.
+**
+** 4) Calls sqlite3rbu_close() to close the RBU update handle. If
+** sqlite3rbu_step() has been called enough times to completely
+** apply the update to the target database, then the RBU database
+** is marked as fully applied. Otherwise, the state of the RBU
+** update application is saved in the RBU database for later
+** resumption.
+**
+** See comments below for more detail on APIs.
+**
+** If an update is only partially applied to the target database by the
+** time sqlite3rbu_close() is called, various state information is saved
+** within the RBU database. This allows subsequent processes to automatically
+** resume the RBU update from where it left off.
+**
+** To remove all RBU extension state information, returning an RBU database
+** to its original contents, it is sufficient to drop all tables that begin
+** with the prefix "rbu_"
+**
+** DATABASE LOCKING
+**
+** An RBU update may not be applied to a database in WAL mode. Attempting
+** to do so is an error (SQLITE_ERROR).
+**
+** While an RBU handle is open, a SHARED lock may be held on the target
+** database file. This means it is possible for other clients to read the
+** database, but not to write it.
+**
+** If an RBU update is started and then suspended before it is completed,
+** then an external client writes to the database, then attempting to resume
+** the suspended RBU update is also an error (SQLITE_BUSY).
+*/
+
+#ifndef _SQLITE3RBU_H
+#define _SQLITE3RBU_H
+
+/* #include "sqlite3.h" ** Required for error code definitions ** */
+
+#if 0
+extern "C" {
+#endif
+
+typedef struct sqlite3rbu sqlite3rbu;
+
+/*
+** Open an RBU handle.
+**
+** Argument zTarget is the path to the target database. Argument zRbu is
+** the path to the RBU database. Each call to this function must be matched
+** by a call to sqlite3rbu_close(). When opening the databases, RBU passes
+** the SQLITE_CONFIG_URI flag to sqlite3_open_v2(). So if either zTarget
+** or zRbu begin with "file:", it will be interpreted as an SQLite
+** database URI, not a regular file name.
+**
+** If the zState argument is passed a NULL value, the RBU extension stores
+** the current state of the update (how many rows have been updated, which
+** indexes are yet to be updated etc.) within the RBU database itself. This
+** can be convenient, as it means that the RBU application does not need to
+** organize removing a separate state file after the update is concluded.
+** Or, if zState is non-NULL, it must be a path to a database file in which
+** the RBU extension can store the state of the update.
+**
+** When resuming an RBU update, the zState argument must be passed the same
+** value as when the RBU update was started.
+**
+** Once the RBU update is finished, the RBU extension does not
+** automatically remove any zState database file, even if it created it.
+**
+** By default, RBU uses the default VFS to access the files on disk. To
+** use a VFS other than the default, an SQLite "file:" URI containing a
+** "vfs=..." option may be passed as the zTarget option.
+**
+** IMPORTANT NOTE FOR ZIPVFS USERS: The RBU extension works with all of
+** SQLite's built-in VFSs, including the multiplexor VFS. However it does
+** not work out of the box with zipvfs. Refer to the comment describing
+** the zipvfs_create_vfs() API below for details on using RBU with zipvfs.
+*/
+SQLITE_API sqlite3rbu *SQLITE_STDCALL sqlite3rbu_open(
+ const char *zTarget,
+ const char *zRbu,
+ const char *zState
+);
+
+/*
+** Open an RBU handle to perform an RBU vacuum on database file zTarget.
+** An RBU vacuum is similar to SQLite's built-in VACUUM command, except
+** that it can be suspended and resumed like an RBU update.
+**
+** The second argument to this function, which may not be NULL, identifies
+** a database in which to store the state of the RBU vacuum operation if
+** it is suspended. The first time sqlite3rbu_vacuum() is called, to start
+** an RBU vacuum operation, the state database should either not exist or
+** be empty (contain no tables). If an RBU vacuum is suspended by calling
+** sqlite3rbu_close() on the RBU handle before sqlite3rbu_step() has
+** returned SQLITE_DONE, the vacuum state is stored in the state database.
+** The vacuum can be resumed by calling this function to open a new RBU
+** handle specifying the same target and state databases.
+**
+** This function does not delete the state database after an RBU vacuum
+** is completed, even if it created it. However, if the call to
+** sqlite3rbu_close() returns any value other than SQLITE_OK, the contents
+** of the state tables within the state database are zeroed. This way,
+** the next call to sqlite3rbu_vacuum() opens a handle that starts a
+** new RBU vacuum operation.
+**
+** As with sqlite3rbu_open(), Zipvfs users should rever to the comment
+** describing the sqlite3rbu_create_vfs() API function below for
+** a description of the complications associated with using RBU with
+** zipvfs databases.
+*/
+SQLITE_API sqlite3rbu *SQLITE_STDCALL sqlite3rbu_vacuum(
+ const char *zTarget,
+ const char *zState
+);
+
+/*
+** Internally, each RBU connection uses a separate SQLite database
+** connection to access the target and rbu update databases. This
+** API allows the application direct access to these database handles.
+**
+** The first argument passed to this function must be a valid, open, RBU
+** handle. The second argument should be passed zero to access the target
+** database handle, or non-zero to access the rbu update database handle.
+** Accessing the underlying database handles may be useful in the
+** following scenarios:
+**
+** * If any target tables are virtual tables, it may be necessary to
+** call sqlite3_create_module() on the target database handle to
+** register the required virtual table implementations.
+**
+** * If the data_xxx tables in the RBU source database are virtual
+** tables, the application may need to call sqlite3_create_module() on
+** the rbu update db handle to any required virtual table
+** implementations.
+**
+** * If the application uses the "rbu_delta()" feature described above,
+** it must use sqlite3_create_function() or similar to register the
+** rbu_delta() implementation with the target database handle.
+**
+** If an error has occurred, either while opening or stepping the RBU object,
+** this function may return NULL. The error code and message may be collected
+** when sqlite3rbu_close() is called.
+**
+** Database handles returned by this function remain valid until the next
+** call to any sqlite3rbu_xxx() function other than sqlite3rbu_db().
+*/
+SQLITE_API sqlite3 *SQLITE_STDCALL sqlite3rbu_db(sqlite3rbu*, int bRbu);
+
+/*
+** Do some work towards applying the RBU update to the target db.
+**
+** Return SQLITE_DONE if the update has been completely applied, or
+** SQLITE_OK if no error occurs but there remains work to do to apply
+** the RBU update. If an error does occur, some other error code is
+** returned.
+**
+** Once a call to sqlite3rbu_step() has returned a value other than
+** SQLITE_OK, all subsequent calls on the same RBU handle are no-ops
+** that immediately return the same value.
+*/
+SQLITE_API int SQLITE_STDCALL sqlite3rbu_step(sqlite3rbu *pRbu);
+
+/*
+** Force RBU to save its state to disk.
+**
+** If a power failure or application crash occurs during an update, following
+** system recovery RBU may resume the update from the point at which the state
+** was last saved. In other words, from the most recent successful call to
+** sqlite3rbu_close() or this function.
+**
+** SQLITE_OK is returned if successful, or an SQLite error code otherwise.
+*/
+SQLITE_API int SQLITE_STDCALL sqlite3rbu_savestate(sqlite3rbu *pRbu);
+
+/*
+** Close an RBU handle.
+**
+** If the RBU update has been completely applied, mark the RBU database
+** as fully applied. Otherwise, assuming no error has occurred, save the
+** current state of the RBU update appliation to the RBU database.
+**
+** If an error has already occurred as part of an sqlite3rbu_step()
+** or sqlite3rbu_open() call, or if one occurs within this function, an
+** SQLite error code is returned. Additionally, *pzErrmsg may be set to
+** point to a buffer containing a utf-8 formatted English language error
+** message. It is the responsibility of the caller to eventually free any
+** such buffer using sqlite3_free().
+**
+** Otherwise, if no error occurs, this function returns SQLITE_OK if the
+** update has been partially applied, or SQLITE_DONE if it has been
+** completely applied.
+*/
+SQLITE_API int SQLITE_STDCALL sqlite3rbu_close(sqlite3rbu *pRbu, char **pzErrmsg);
+
+/*
+** Return the total number of key-value operations (inserts, deletes or
+** updates) that have been performed on the target database since the
+** current RBU update was started.
+*/
+SQLITE_API sqlite3_int64 SQLITE_STDCALL sqlite3rbu_progress(sqlite3rbu *pRbu);
+
+/*
+** Obtain permyriadage (permyriadage is to 10000 as percentage is to 100)
+** progress indications for the two stages of an RBU update. This API may
+** be useful for driving GUI progress indicators and similar.
+**
+** An RBU update is divided into two stages:
+**
+** * Stage 1, in which changes are accumulated in an oal/wal file, and
+** * Stage 2, in which the contents of the wal file are copied into the
+** main database.
+**
+** The update is visible to non-RBU clients during stage 2. During stage 1
+** non-RBU reader clients may see the original database.
+**
+** If this API is called during stage 2 of the update, output variable
+** (*pnOne) is set to 10000 to indicate that stage 1 has finished and (*pnTwo)
+** to a value between 0 and 10000 to indicate the permyriadage progress of
+** stage 2. A value of 5000 indicates that stage 2 is half finished,
+** 9000 indicates that it is 90% finished, and so on.
+**
+** If this API is called during stage 1 of the update, output variable
+** (*pnTwo) is set to 0 to indicate that stage 2 has not yet started. The
+** value to which (*pnOne) is set depends on whether or not the RBU
+** database contains an "rbu_count" table. The rbu_count table, if it
+** exists, must contain the same columns as the following:
+**
+** CREATE TABLE rbu_count(tbl TEXT PRIMARY KEY, cnt INTEGER) WITHOUT ROWID;
+**
+** There must be one row in the table for each source (data_xxx) table within
+** the RBU database. The 'tbl' column should contain the name of the source
+** table. The 'cnt' column should contain the number of rows within the
+** source table.
+**
+** If the rbu_count table is present and populated correctly and this
+** API is called during stage 1, the *pnOne output variable is set to the
+** permyriadage progress of the same stage. If the rbu_count table does
+** not exist, then (*pnOne) is set to -1 during stage 1. If the rbu_count
+** table exists but is not correctly populated, the value of the *pnOne
+** output variable during stage 1 is undefined.
+*/
+SQLITE_API void SQLITE_STDCALL sqlite3rbu_bp_progress(sqlite3rbu *pRbu, int *pnOne, int *pnTwo);
+
+/*
+** Create an RBU VFS named zName that accesses the underlying file-system
+** via existing VFS zParent. Or, if the zParent parameter is passed NULL,
+** then the new RBU VFS uses the default system VFS to access the file-system.
+** The new object is registered as a non-default VFS with SQLite before
+** returning.
+**
+** Part of the RBU implementation uses a custom VFS object. Usually, this
+** object is created and deleted automatically by RBU.
+**
+** The exception is for applications that also use zipvfs. In this case,
+** the custom VFS must be explicitly created by the user before the RBU
+** handle is opened. The RBU VFS should be installed so that the zipvfs
+** VFS uses the RBU VFS, which in turn uses any other VFS layers in use
+** (for example multiplexor) to access the file-system. For example,
+** to assemble an RBU enabled VFS stack that uses both zipvfs and
+** multiplexor (error checking omitted):
+**
+** // Create a VFS named "multiplex" (not the default).
+** sqlite3_multiplex_initialize(0, 0);
+**
+** // Create an rbu VFS named "rbu" that uses multiplexor. If the
+** // second argument were replaced with NULL, the "rbu" VFS would
+** // access the file-system via the system default VFS, bypassing the
+** // multiplexor.
+** sqlite3rbu_create_vfs("rbu", "multiplex");
+**
+** // Create a zipvfs VFS named "zipvfs" that uses rbu.
+** zipvfs_create_vfs_v3("zipvfs", "rbu", 0, xCompressorAlgorithmDetector);
+**
+** // Make zipvfs the default VFS.
+** sqlite3_vfs_register(sqlite3_vfs_find("zipvfs"), 1);
+**
+** Because the default VFS created above includes a RBU functionality, it
+** may be used by RBU clients. Attempting to use RBU with a zipvfs VFS stack
+** that does not include the RBU layer results in an error.
+**
+** The overhead of adding the "rbu" VFS to the system is negligible for
+** non-RBU users. There is no harm in an application accessing the
+** file-system via "rbu" all the time, even if it only uses RBU functionality
+** occasionally.
+*/
+SQLITE_API int SQLITE_STDCALL sqlite3rbu_create_vfs(const char *zName, const char *zParent);
+
+/*
+** Deregister and destroy an RBU vfs created by an earlier call to
+** sqlite3rbu_create_vfs().
+**
+** VFS objects are not reference counted. If a VFS object is destroyed
+** before all database handles that use it have been closed, the results
+** are undefined.
+*/
+SQLITE_API void SQLITE_STDCALL sqlite3rbu_destroy_vfs(const char *zName);
+
+#if 0
+} /* end of the 'extern "C"' block */
+#endif
+
+#endif /* _SQLITE3RBU_H */
+
+/************** End of sqlite3rbu.h ******************************************/
+/************** Continuing where we left off in sqlite3rbu.c *****************/
+
+#if defined(_WIN32_WCE)
+/* #include "windows.h" */
+#endif
+
+/* Maximum number of prepared UPDATE statements held by this module */
+#define SQLITE_RBU_UPDATE_CACHESIZE 16
+
+/*
+** Swap two objects of type TYPE.
+*/
+#if !defined(SQLITE_AMALGAMATION)
+# define SWAP(TYPE,A,B) {TYPE t=A; A=B; B=t;}
+#endif
+
+/*
+** The rbu_state table is used to save the state of a partially applied
+** update so that it can be resumed later. The table consists of integer
+** keys mapped to values as follows:
+**
+** RBU_STATE_STAGE:
+** May be set to integer values 1, 2, 4 or 5. As follows:
+** 1: the *-rbu file is currently under construction.
+** 2: the *-rbu file has been constructed, but not yet moved
+** to the *-wal path.
+** 4: the checkpoint is underway.
+** 5: the rbu update has been checkpointed.
+**
+** RBU_STATE_TBL:
+** Only valid if STAGE==1. The target database name of the table
+** currently being written.
+**
+** RBU_STATE_IDX:
+** Only valid if STAGE==1. The target database name of the index
+** currently being written, or NULL if the main table is currently being
+** updated.
+**
+** RBU_STATE_ROW:
+** Only valid if STAGE==1. Number of rows already processed for the current
+** table/index.
+**
+** RBU_STATE_PROGRESS:
+** Trbul number of sqlite3rbu_step() calls made so far as part of this
+** rbu update.
+**
+** RBU_STATE_CKPT:
+** Valid if STAGE==4. The 64-bit checksum associated with the wal-index
+** header created by recovering the *-wal file. This is used to detect
+** cases when another client appends frames to the *-wal file in the
+** middle of an incremental checkpoint (an incremental checkpoint cannot
+** be continued if this happens).
+**
+** RBU_STATE_COOKIE:
+** Valid if STAGE==1. The current change-counter cookie value in the
+** target db file.
+**
+** RBU_STATE_OALSZ:
+** Valid if STAGE==1. The size in bytes of the *-oal file.
+*/
+#define RBU_STATE_STAGE 1
+#define RBU_STATE_TBL 2
+#define RBU_STATE_IDX 3
+#define RBU_STATE_ROW 4
+#define RBU_STATE_PROGRESS 5
+#define RBU_STATE_CKPT 6
+#define RBU_STATE_COOKIE 7
+#define RBU_STATE_OALSZ 8
+#define RBU_STATE_PHASEONESTEP 9
+
+#define RBU_STAGE_OAL 1
+#define RBU_STAGE_MOVE 2
+#define RBU_STAGE_CAPTURE 3
+#define RBU_STAGE_CKPT 4
+#define RBU_STAGE_DONE 5
+
+
+#define RBU_CREATE_STATE \
+ "CREATE TABLE IF NOT EXISTS %s.rbu_state(k INTEGER PRIMARY KEY, v)"
+
+typedef struct RbuFrame RbuFrame;
+typedef struct RbuObjIter RbuObjIter;
+typedef struct RbuState RbuState;
+typedef struct rbu_vfs rbu_vfs;
+typedef struct rbu_file rbu_file;
+typedef struct RbuUpdateStmt RbuUpdateStmt;
+
+#if !defined(SQLITE_AMALGAMATION)
+typedef unsigned int u32;
+typedef unsigned short u16;
+typedef unsigned char u8;
+typedef sqlite3_int64 i64;
+#endif
+
+/*
+** These values must match the values defined in wal.c for the equivalent
+** locks. These are not magic numbers as they are part of the SQLite file
+** format.
+*/
+#define WAL_LOCK_WRITE 0
+#define WAL_LOCK_CKPT 1
+#define WAL_LOCK_READ0 3
+
+#define SQLITE_FCNTL_RBUCNT 5149216
+
+/*
+** A structure to store values read from the rbu_state table in memory.
+*/
+struct RbuState {
+ int eStage;
+ char *zTbl;
+ char *zIdx;
+ i64 iWalCksum;
+ int nRow;
+ i64 nProgress;
+ u32 iCookie;
+ i64 iOalSz;
+ i64 nPhaseOneStep;
+};
+
+struct RbuUpdateStmt {
+ char *zMask; /* Copy of update mask used with pUpdate */
+ sqlite3_stmt *pUpdate; /* Last update statement (or NULL) */
+ RbuUpdateStmt *pNext;
+};
+
+/*
+** An iterator of this type is used to iterate through all objects in
+** the target database that require updating. For each such table, the
+** iterator visits, in order:
+**
+** * the table itself,
+** * each index of the table (zero or more points to visit), and
+** * a special "cleanup table" state.
+**
+** abIndexed:
+** If the table has no indexes on it, abIndexed is set to NULL. Otherwise,
+** it points to an array of flags nTblCol elements in size. The flag is
+** set for each column that is either a part of the PK or a part of an
+** index. Or clear otherwise.
+**
+*/
+struct RbuObjIter {
+ sqlite3_stmt *pTblIter; /* Iterate through tables */
+ sqlite3_stmt *pIdxIter; /* Index iterator */
+ int nTblCol; /* Size of azTblCol[] array */
+ char **azTblCol; /* Array of unquoted target column names */
+ char **azTblType; /* Array of target column types */
+ int *aiSrcOrder; /* src table col -> target table col */
+ u8 *abTblPk; /* Array of flags, set on target PK columns */
+ u8 *abNotNull; /* Array of flags, set on NOT NULL columns */
+ u8 *abIndexed; /* Array of flags, set on indexed & PK cols */
+ int eType; /* Table type - an RBU_PK_XXX value */
+
+ /* Output variables. zTbl==0 implies EOF. */
+ int bCleanup; /* True in "cleanup" state */
+ const char *zTbl; /* Name of target db table */
+ const char *zDataTbl; /* Name of rbu db table (or null) */
+ const char *zIdx; /* Name of target db index (or null) */
+ int iTnum; /* Root page of current object */
+ int iPkTnum; /* If eType==EXTERNAL, root of PK index */
+ int bUnique; /* Current index is unique */
+ int nIndex; /* Number of aux. indexes on table zTbl */
+
+ /* Statements created by rbuObjIterPrepareAll() */
+ int nCol; /* Number of columns in current object */
+ sqlite3_stmt *pSelect; /* Source data */
+ sqlite3_stmt *pInsert; /* Statement for INSERT operations */
+ sqlite3_stmt *pDelete; /* Statement for DELETE ops */
+ sqlite3_stmt *pTmpInsert; /* Insert into rbu_tmp_$zDataTbl */
+
+ /* Last UPDATE used (for PK b-tree updates only), or NULL. */
+ RbuUpdateStmt *pRbuUpdate;
+};
+
+/*
+** Values for RbuObjIter.eType
+**
+** 0: Table does not exist (error)
+** 1: Table has an implicit rowid.
+** 2: Table has an explicit IPK column.
+** 3: Table has an external PK index.
+** 4: Table is WITHOUT ROWID.
+** 5: Table is a virtual table.
+*/
+#define RBU_PK_NOTABLE 0
+#define RBU_PK_NONE 1
+#define RBU_PK_IPK 2
+#define RBU_PK_EXTERNAL 3
+#define RBU_PK_WITHOUT_ROWID 4
+#define RBU_PK_VTAB 5
+
+
+/*
+** Within the RBU_STAGE_OAL stage, each call to sqlite3rbu_step() performs
+** one of the following operations.
+*/
+#define RBU_INSERT 1 /* Insert on a main table b-tree */
+#define RBU_DELETE 2 /* Delete a row from a main table b-tree */
+#define RBU_REPLACE 3 /* Delete and then insert a row */
+#define RBU_IDX_DELETE 4 /* Delete a row from an aux. index b-tree */
+#define RBU_IDX_INSERT 5 /* Insert on an aux. index b-tree */
+
+#define RBU_UPDATE 6 /* Update a row in a main table b-tree */
+
+/*
+** A single step of an incremental checkpoint - frame iWalFrame of the wal
+** file should be copied to page iDbPage of the database file.
+*/
+struct RbuFrame {
+ u32 iDbPage;
+ u32 iWalFrame;
+};
+
+/*
+** RBU handle.
+**
+** nPhaseOneStep:
+** If the RBU database contains an rbu_count table, this value is set to
+** a running estimate of the number of b-tree operations required to
+** finish populating the *-oal file. This allows the sqlite3_bp_progress()
+** API to calculate the permyriadage progress of populating the *-oal file
+** using the formula:
+**
+** permyriadage = (10000 * nProgress) / nPhaseOneStep
+**
+** nPhaseOneStep is initialized to the sum of:
+**
+** nRow * (nIndex + 1)
+**
+** for all source tables in the RBU database, where nRow is the number
+** of rows in the source table and nIndex the number of indexes on the
+** corresponding target database table.
+**
+** This estimate is accurate if the RBU update consists entirely of
+** INSERT operations. However, it is inaccurate if:
+**
+** * the RBU update contains any UPDATE operations. If the PK specified
+** for an UPDATE operation does not exist in the target table, then
+** no b-tree operations are required on index b-trees. Or if the
+** specified PK does exist, then (nIndex*2) such operations are
+** required (one delete and one insert on each index b-tree).
+**
+** * the RBU update contains any DELETE operations for which the specified
+** PK does not exist. In this case no operations are required on index
+** b-trees.
+**
+** * the RBU update contains REPLACE operations. These are similar to
+** UPDATE operations.
+**
+** nPhaseOneStep is updated to account for the conditions above during the
+** first pass of each source table. The updated nPhaseOneStep value is
+** stored in the rbu_state table if the RBU update is suspended.
+*/
+struct sqlite3rbu {
+ int eStage; /* Value of RBU_STATE_STAGE field */
+ sqlite3 *dbMain; /* target database handle */
+ sqlite3 *dbRbu; /* rbu database handle */
+ char *zTarget; /* Path to target db */
+ char *zRbu; /* Path to rbu db */
+ char *zState; /* Path to state db (or NULL if zRbu) */
+ char zStateDb[5]; /* Db name for state ("stat" or "main") */
+ int rc; /* Value returned by last rbu_step() call */
+ char *zErrmsg; /* Error message if rc!=SQLITE_OK */
+ int nStep; /* Rows processed for current object */
+ int nProgress; /* Rows processed for all objects */
+ RbuObjIter objiter; /* Iterator for skipping through tbl/idx */
+ const char *zVfsName; /* Name of automatically created rbu vfs */
+ rbu_file *pTargetFd; /* File handle open on target db */
+ i64 iOalSz;
+ i64 nPhaseOneStep;
+
+ /* The following state variables are used as part of the incremental
+ ** checkpoint stage (eStage==RBU_STAGE_CKPT). See comments surrounding
+ ** function rbuSetupCheckpoint() for details. */
+ u32 iMaxFrame; /* Largest iWalFrame value in aFrame[] */
+ u32 mLock;
+ int nFrame; /* Entries in aFrame[] array */
+ int nFrameAlloc; /* Allocated size of aFrame[] array */
+ RbuFrame *aFrame;
+ int pgsz;
+ u8 *aBuf;
+ i64 iWalCksum;
+
+ /* Used in RBU vacuum mode only */
+ int nRbu; /* Number of RBU VFS in the stack */
+ rbu_file *pRbuFd; /* Fd for main db of dbRbu */
+};
+
+/*
+** An rbu VFS is implemented using an instance of this structure.
+*/
+struct rbu_vfs {
+ sqlite3_vfs base; /* rbu VFS shim methods */
+ sqlite3_vfs *pRealVfs; /* Underlying VFS */
+ sqlite3_mutex *mutex; /* Mutex to protect pMain */
+ rbu_file *pMain; /* Linked list of main db files */
+};
+
+/*
+** Each file opened by an rbu VFS is represented by an instance of
+** the following structure.
+*/
+struct rbu_file {
+ sqlite3_file base; /* sqlite3_file methods */
+ sqlite3_file *pReal; /* Underlying file handle */
+ rbu_vfs *pRbuVfs; /* Pointer to the rbu_vfs object */
+ sqlite3rbu *pRbu; /* Pointer to rbu object (rbu target only) */
+
+ int openFlags; /* Flags this file was opened with */
+ u32 iCookie; /* Cookie value for main db files */
+ u8 iWriteVer; /* "write-version" value for main db files */
+ u8 bNolock; /* True to fail EXCLUSIVE locks */
+
+ int nShm; /* Number of entries in apShm[] array */
+ char **apShm; /* Array of mmap'd *-shm regions */
+ char *zDel; /* Delete this when closing file */
+
+ const char *zWal; /* Wal filename for this main db file */
+ rbu_file *pWalFd; /* Wal file descriptor for this main db */
+ rbu_file *pMainNext; /* Next MAIN_DB file */
+};
+
+/*
+** True for an RBU vacuum handle, or false otherwise.
+*/
+#define rbuIsVacuum(p) ((p)->zTarget==0)
+
+
+/*************************************************************************
+** The following three functions, found below:
+**
+** rbuDeltaGetInt()
+** rbuDeltaChecksum()
+** rbuDeltaApply()
+**
+** are lifted from the fossil source code (http://fossil-scm.org). They
+** are used to implement the scalar SQL function rbu_fossil_delta().
+*/
+
+/*
+** Read bytes from *pz and convert them into a positive integer. When
+** finished, leave *pz pointing to the first character past the end of
+** the integer. The *pLen parameter holds the length of the string
+** in *pz and is decremented once for each character in the integer.
+*/
+static unsigned int rbuDeltaGetInt(const char **pz, int *pLen){
+ static const signed char zValue[] = {
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1,
+ -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
+ 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, 36,
+ -1, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,
+ 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, -1, -1, -1, 63, -1,
+ };
+ unsigned int v = 0;
+ int c;
+ unsigned char *z = (unsigned char*)*pz;
+ unsigned char *zStart = z;
+ while( (c = zValue[0x7f&*(z++)])>=0 ){
+ v = (v<<6) + c;
+ }
+ z--;
+ *pLen -= z - zStart;
+ *pz = (char*)z;
+ return v;
+}
+
+/*
+** Compute a 32-bit checksum on the N-byte buffer. Return the result.
+*/
+static unsigned int rbuDeltaChecksum(const char *zIn, size_t N){
+ const unsigned char *z = (const unsigned char *)zIn;
+ unsigned sum0 = 0;
+ unsigned sum1 = 0;
+ unsigned sum2 = 0;
+ unsigned sum3 = 0;
+ while(N >= 16){
+ sum0 += ((unsigned)z[0] + z[4] + z[8] + z[12]);
+ sum1 += ((unsigned)z[1] + z[5] + z[9] + z[13]);
+ sum2 += ((unsigned)z[2] + z[6] + z[10]+ z[14]);
+ sum3 += ((unsigned)z[3] + z[7] + z[11]+ z[15]);
+ z += 16;
+ N -= 16;
+ }
+ while(N >= 4){
+ sum0 += z[0];
+ sum1 += z[1];
+ sum2 += z[2];
+ sum3 += z[3];
+ z += 4;
+ N -= 4;
+ }
+ sum3 += (sum2 << 8) + (sum1 << 16) + (sum0 << 24);
+ switch(N){
+ case 3: sum3 += (z[2] << 8);
+ case 2: sum3 += (z[1] << 16);
+ case 1: sum3 += (z[0] << 24);
+ default: ;
+ }
+ return sum3;
+}
+
+/*
+** Apply a delta.
+**
+** The output buffer should be big enough to hold the whole output
+** file and a NUL terminator at the end. The delta_output_size()
+** routine will determine this size for you.
+**
+** The delta string should be null-terminated. But the delta string
+** may contain embedded NUL characters (if the input and output are
+** binary files) so we also have to pass in the length of the delta in
+** the lenDelta parameter.
+**
+** This function returns the size of the output file in bytes (excluding
+** the final NUL terminator character). Except, if the delta string is
+** malformed or intended for use with a source file other than zSrc,
+** then this routine returns -1.
+**
+** Refer to the delta_create() documentation above for a description
+** of the delta file format.
+*/
+static int rbuDeltaApply(
+ const char *zSrc, /* The source or pattern file */
+ int lenSrc, /* Length of the source file */
+ const char *zDelta, /* Delta to apply to the pattern */
+ int lenDelta, /* Length of the delta */
+ char *zOut /* Write the output into this preallocated buffer */
+){
+ unsigned int limit;
+ unsigned int total = 0;
+#ifndef FOSSIL_OMIT_DELTA_CKSUM_TEST
+ char *zOrigOut = zOut;
+#endif
+
+ limit = rbuDeltaGetInt(&zDelta, &lenDelta);
+ if( *zDelta!='\n' ){
+ /* ERROR: size integer not terminated by "\n" */
+ return -1;
+ }
+ zDelta++; lenDelta--;
+ while( *zDelta && lenDelta>0 ){
+ unsigned int cnt, ofst;
+ cnt = rbuDeltaGetInt(&zDelta, &lenDelta);
+ switch( zDelta[0] ){
+ case '@': {
+ zDelta++; lenDelta--;
+ ofst = rbuDeltaGetInt(&zDelta, &lenDelta);
+ if( lenDelta>0 && zDelta[0]!=',' ){
+ /* ERROR: copy command not terminated by ',' */
+ return -1;
+ }
+ zDelta++; lenDelta--;
+ total += cnt;
+ if( total>limit ){
+ /* ERROR: copy exceeds output file size */
+ return -1;
+ }
+ if( (int)(ofst+cnt) > lenSrc ){
+ /* ERROR: copy extends past end of input */
+ return -1;
+ }
+ memcpy(zOut, &zSrc[ofst], cnt);
+ zOut += cnt;
+ break;
+ }
+ case ':': {
+ zDelta++; lenDelta--;
+ total += cnt;
+ if( total>limit ){
+ /* ERROR: insert command gives an output larger than predicted */
+ return -1;
+ }
+ if( (int)cnt>lenDelta ){
+ /* ERROR: insert count exceeds size of delta */
+ return -1;
+ }
+ memcpy(zOut, zDelta, cnt);
+ zOut += cnt;
+ zDelta += cnt;
+ lenDelta -= cnt;
+ break;
+ }
+ case ';': {
+ zDelta++; lenDelta--;
+ zOut[0] = 0;
+#ifndef FOSSIL_OMIT_DELTA_CKSUM_TEST
+ if( cnt!=rbuDeltaChecksum(zOrigOut, total) ){
+ /* ERROR: bad checksum */
+ return -1;
+ }
+#endif
+ if( total!=limit ){
+ /* ERROR: generated size does not match predicted size */
+ return -1;
+ }
+ return total;
+ }
+ default: {
+ /* ERROR: unknown delta operator */
+ return -1;
+ }
+ }
+ }
+ /* ERROR: unterminated delta */
+ return -1;
+}
+
+static int rbuDeltaOutputSize(const char *zDelta, int lenDelta){
+ int size;
+ size = rbuDeltaGetInt(&zDelta, &lenDelta);
+ if( *zDelta!='\n' ){
+ /* ERROR: size integer not terminated by "\n" */
+ return -1;
+ }
+ return size;
+}
+
+/*
+** End of code taken from fossil.
+*************************************************************************/
+
+/*
+** Implementation of SQL scalar function rbu_fossil_delta().
+**
+** This function applies a fossil delta patch to a blob. Exactly two
+** arguments must be passed to this function. The first is the blob to
+** patch and the second the patch to apply. If no error occurs, this
+** function returns the patched blob.
+*/
+static void rbuFossilDeltaFunc(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ const char *aDelta;
+ int nDelta;
+ const char *aOrig;
+ int nOrig;
+
+ int nOut;
+ int nOut2;
+ char *aOut;
+
+ assert( argc==2 );
+
+ nOrig = sqlite3_value_bytes(argv[0]);
+ aOrig = (const char*)sqlite3_value_blob(argv[0]);
+ nDelta = sqlite3_value_bytes(argv[1]);
+ aDelta = (const char*)sqlite3_value_blob(argv[1]);
+
+ /* Figure out the size of the output */
+ nOut = rbuDeltaOutputSize(aDelta, nDelta);
+ if( nOut<0 ){
+ sqlite3_result_error(context, "corrupt fossil delta", -1);
+ return;
+ }
+
+ aOut = sqlite3_malloc(nOut+1);
+ if( aOut==0 ){
+ sqlite3_result_error_nomem(context);
+ }else{
+ nOut2 = rbuDeltaApply(aOrig, nOrig, aDelta, nDelta, aOut);
+ if( nOut2!=nOut ){
+ sqlite3_result_error(context, "corrupt fossil delta", -1);
+ }else{
+ sqlite3_result_blob(context, aOut, nOut, sqlite3_free);
+ }
+ }
+}
+
+
+/*
+** Prepare the SQL statement in buffer zSql against database handle db.
+** If successful, set *ppStmt to point to the new statement and return
+** SQLITE_OK.
+**
+** Otherwise, if an error does occur, set *ppStmt to NULL and return
+** an SQLite error code. Additionally, set output variable *pzErrmsg to
+** point to a buffer containing an error message. It is the responsibility
+** of the caller to (eventually) free this buffer using sqlite3_free().
+*/
+static int prepareAndCollectError(
+ sqlite3 *db,
+ sqlite3_stmt **ppStmt,
+ char **pzErrmsg,
+ const char *zSql
+){
+ int rc = sqlite3_prepare_v2(db, zSql, -1, ppStmt, 0);
+ if( rc!=SQLITE_OK ){
+ *pzErrmsg = sqlite3_mprintf("%s", sqlite3_errmsg(db));
+ *ppStmt = 0;
+ }
+ return rc;
+}
+
+/*
+** Reset the SQL statement passed as the first argument. Return a copy
+** of the value returned by sqlite3_reset().
+**
+** If an error has occurred, then set *pzErrmsg to point to a buffer
+** containing an error message. It is the responsibility of the caller
+** to eventually free this buffer using sqlite3_free().
+*/
+static int resetAndCollectError(sqlite3_stmt *pStmt, char **pzErrmsg){
+ int rc = sqlite3_reset(pStmt);
+ if( rc!=SQLITE_OK ){
+ *pzErrmsg = sqlite3_mprintf("%s", sqlite3_errmsg(sqlite3_db_handle(pStmt)));
+ }
+ return rc;
+}
+
+/*
+** Unless it is NULL, argument zSql points to a buffer allocated using
+** sqlite3_malloc containing an SQL statement. This function prepares the SQL
+** statement against database db and frees the buffer. If statement
+** compilation is successful, *ppStmt is set to point to the new statement
+** handle and SQLITE_OK is returned.
+**
+** Otherwise, if an error occurs, *ppStmt is set to NULL and an error code
+** returned. In this case, *pzErrmsg may also be set to point to an error
+** message. It is the responsibility of the caller to free this error message
+** buffer using sqlite3_free().
+**
+** If argument zSql is NULL, this function assumes that an OOM has occurred.
+** In this case SQLITE_NOMEM is returned and *ppStmt set to NULL.
+*/
+static int prepareFreeAndCollectError(
+ sqlite3 *db,
+ sqlite3_stmt **ppStmt,
+ char **pzErrmsg,
+ char *zSql
+){
+ int rc;
+ assert( *pzErrmsg==0 );
+ if( zSql==0 ){
+ rc = SQLITE_NOMEM;
+ *ppStmt = 0;
+ }else{
+ rc = prepareAndCollectError(db, ppStmt, pzErrmsg, zSql);
+ sqlite3_free(zSql);
+ }
+ return rc;
+}
+
+/*
+** Free the RbuObjIter.azTblCol[] and RbuObjIter.abTblPk[] arrays allocated
+** by an earlier call to rbuObjIterCacheTableInfo().
+*/
+static void rbuObjIterFreeCols(RbuObjIter *pIter){
+ int i;
+ for(i=0; i<pIter->nTblCol; i++){
+ sqlite3_free(pIter->azTblCol[i]);
+ sqlite3_free(pIter->azTblType[i]);
+ }
+ sqlite3_free(pIter->azTblCol);
+ pIter->azTblCol = 0;
+ pIter->azTblType = 0;
+ pIter->aiSrcOrder = 0;
+ pIter->abTblPk = 0;
+ pIter->abNotNull = 0;
+ pIter->nTblCol = 0;
+ pIter->eType = 0; /* Invalid value */
+}
+
+/*
+** Finalize all statements and free all allocations that are specific to
+** the current object (table/index pair).
+*/
+static void rbuObjIterClearStatements(RbuObjIter *pIter){
+ RbuUpdateStmt *pUp;
+
+ sqlite3_finalize(pIter->pSelect);
+ sqlite3_finalize(pIter->pInsert);
+ sqlite3_finalize(pIter->pDelete);
+ sqlite3_finalize(pIter->pTmpInsert);
+ pUp = pIter->pRbuUpdate;
+ while( pUp ){
+ RbuUpdateStmt *pTmp = pUp->pNext;
+ sqlite3_finalize(pUp->pUpdate);
+ sqlite3_free(pUp);
+ pUp = pTmp;
+ }
+
+ pIter->pSelect = 0;
+ pIter->pInsert = 0;
+ pIter->pDelete = 0;
+ pIter->pRbuUpdate = 0;
+ pIter->pTmpInsert = 0;
+ pIter->nCol = 0;
+}
+
+/*
+** Clean up any resources allocated as part of the iterator object passed
+** as the only argument.
+*/
+static void rbuObjIterFinalize(RbuObjIter *pIter){
+ rbuObjIterClearStatements(pIter);
+ sqlite3_finalize(pIter->pTblIter);
+ sqlite3_finalize(pIter->pIdxIter);
+ rbuObjIterFreeCols(pIter);
+ memset(pIter, 0, sizeof(RbuObjIter));
+}
+
+/*
+** Advance the iterator to the next position.
+**
+** If no error occurs, SQLITE_OK is returned and the iterator is left
+** pointing to the next entry. Otherwise, an error code and message is
+** left in the RBU handle passed as the first argument. A copy of the
+** error code is returned.
+*/
+static int rbuObjIterNext(sqlite3rbu *p, RbuObjIter *pIter){
+ int rc = p->rc;
+ if( rc==SQLITE_OK ){
+
+ /* Free any SQLite statements used while processing the previous object */
+ rbuObjIterClearStatements(pIter);
+ if( pIter->zIdx==0 ){
+ rc = sqlite3_exec(p->dbMain,
+ "DROP TRIGGER IF EXISTS temp.rbu_insert_tr;"
+ "DROP TRIGGER IF EXISTS temp.rbu_update1_tr;"
+ "DROP TRIGGER IF EXISTS temp.rbu_update2_tr;"
+ "DROP TRIGGER IF EXISTS temp.rbu_delete_tr;"
+ , 0, 0, &p->zErrmsg
+ );
+ }
+
+ if( rc==SQLITE_OK ){
+ if( pIter->bCleanup ){
+ rbuObjIterFreeCols(pIter);
+ pIter->bCleanup = 0;
+ rc = sqlite3_step(pIter->pTblIter);
+ if( rc!=SQLITE_ROW ){
+ rc = resetAndCollectError(pIter->pTblIter, &p->zErrmsg);
+ pIter->zTbl = 0;
+ }else{
+ pIter->zTbl = (const char*)sqlite3_column_text(pIter->pTblIter, 0);
+ pIter->zDataTbl = (const char*)sqlite3_column_text(pIter->pTblIter,1);
+ rc = (pIter->zDataTbl && pIter->zTbl) ? SQLITE_OK : SQLITE_NOMEM;
+ }
+ }else{
+ if( pIter->zIdx==0 ){
+ sqlite3_stmt *pIdx = pIter->pIdxIter;
+ rc = sqlite3_bind_text(pIdx, 1, pIter->zTbl, -1, SQLITE_STATIC);
+ }
+ if( rc==SQLITE_OK ){
+ rc = sqlite3_step(pIter->pIdxIter);
+ if( rc!=SQLITE_ROW ){
+ rc = resetAndCollectError(pIter->pIdxIter, &p->zErrmsg);
+ pIter->bCleanup = 1;
+ pIter->zIdx = 0;
+ }else{
+ pIter->zIdx = (const char*)sqlite3_column_text(pIter->pIdxIter, 0);
+ pIter->iTnum = sqlite3_column_int(pIter->pIdxIter, 1);
+ pIter->bUnique = sqlite3_column_int(pIter->pIdxIter, 2);
+ rc = pIter->zIdx ? SQLITE_OK : SQLITE_NOMEM;
+ }
+ }
+ }
+ }
+ }
+
+ if( rc!=SQLITE_OK ){
+ rbuObjIterFinalize(pIter);
+ p->rc = rc;
+ }
+ return rc;
+}
+
+
+/*
+** The implementation of the rbu_target_name() SQL function. This function
+** accepts one or two arguments. The first argument is the name of a table -
+** the name of a table in the RBU database. The second, if it is present, is 1
+** for a view or 0 for a table.
+**
+** For a non-vacuum RBU handle, if the table name matches the pattern:
+**
+** data[0-9]_<name>
+**
+** where <name> is any sequence of 1 or more characters, <name> is returned.
+** Otherwise, if the only argument does not match the above pattern, an SQL
+** NULL is returned.
+**
+** "data_t1" -> "t1"
+** "data0123_t2" -> "t2"
+** "dataAB_t3" -> NULL
+**
+** For an rbu vacuum handle, a copy of the first argument is returned if
+** the second argument is either missing or 0 (not a view).
+*/
+static void rbuTargetNameFunc(
+ sqlite3_context *pCtx,
+ int argc,
+ sqlite3_value **argv
+){
+ sqlite3rbu *p = sqlite3_user_data(pCtx);
+ const char *zIn;
+ assert( argc==1 || argc==2 );
+
+ zIn = (const char*)sqlite3_value_text(argv[0]);
+ if( zIn ){
+ if( rbuIsVacuum(p) ){
+ if( argc==1 || 0==sqlite3_value_int(argv[1]) ){
+ sqlite3_result_text(pCtx, zIn, -1, SQLITE_STATIC);
+ }
+ }else{
+ if( strlen(zIn)>4 && memcmp("data", zIn, 4)==0 ){
+ int i;
+ for(i=4; zIn[i]>='0' && zIn[i]<='9'; i++);
+ if( zIn[i]=='_' && zIn[i+1] ){
+ sqlite3_result_text(pCtx, &zIn[i+1], -1, SQLITE_STATIC);
+ }
+ }
+ }
+ }
+}
+
+/*
+** Initialize the iterator structure passed as the second argument.
+**
+** If no error occurs, SQLITE_OK is returned and the iterator is left
+** pointing to the first entry. Otherwise, an error code and message is
+** left in the RBU handle passed as the first argument. A copy of the
+** error code is returned.
+*/
+static int rbuObjIterFirst(sqlite3rbu *p, RbuObjIter *pIter){
+ int rc;
+ memset(pIter, 0, sizeof(RbuObjIter));
+
+ rc = prepareAndCollectError(p->dbRbu, &pIter->pTblIter, &p->zErrmsg,
+ "SELECT rbu_target_name(name, type='view') AS target, name "
+ "FROM sqlite_master "
+ "WHERE type IN ('table', 'view') AND target IS NOT NULL "
+ "ORDER BY name"
+ );
+
+ if( rc==SQLITE_OK ){
+ rc = prepareAndCollectError(p->dbMain, &pIter->pIdxIter, &p->zErrmsg,
+ "SELECT name, rootpage, sql IS NULL OR substr(8, 6)=='UNIQUE' "
+ " FROM main.sqlite_master "
+ " WHERE type='index' AND tbl_name = ?"
+ );
+ }
+
+ pIter->bCleanup = 1;
+ p->rc = rc;
+ return rbuObjIterNext(p, pIter);
+}
+
+/*
+** This is a wrapper around "sqlite3_mprintf(zFmt, ...)". If an OOM occurs,
+** an error code is stored in the RBU handle passed as the first argument.
+**
+** If an error has already occurred (p->rc is already set to something other
+** than SQLITE_OK), then this function returns NULL without modifying the
+** stored error code. In this case it still calls sqlite3_free() on any
+** printf() parameters associated with %z conversions.
+*/
+static char *rbuMPrintf(sqlite3rbu *p, const char *zFmt, ...){
+ char *zSql = 0;
+ va_list ap;
+ va_start(ap, zFmt);
+ zSql = sqlite3_vmprintf(zFmt, ap);
+ if( p->rc==SQLITE_OK ){
+ if( zSql==0 ) p->rc = SQLITE_NOMEM;
+ }else{
+ sqlite3_free(zSql);
+ zSql = 0;
+ }
+ va_end(ap);
+ return zSql;
+}
+
+/*
+** Argument zFmt is a sqlite3_mprintf() style format string. The trailing
+** arguments are the usual subsitution values. This function performs
+** the printf() style substitutions and executes the result as an SQL
+** statement on the RBU handles database.
+**
+** If an error occurs, an error code and error message is stored in the
+** RBU handle. If an error has already occurred when this function is
+** called, it is a no-op.
+*/
+static int rbuMPrintfExec(sqlite3rbu *p, sqlite3 *db, const char *zFmt, ...){
+ va_list ap;
+ char *zSql;
+ va_start(ap, zFmt);
+ zSql = sqlite3_vmprintf(zFmt, ap);
+ if( p->rc==SQLITE_OK ){
+ if( zSql==0 ){
+ p->rc = SQLITE_NOMEM;
+ }else{
+ p->rc = sqlite3_exec(db, zSql, 0, 0, &p->zErrmsg);
+ }
+ }
+ sqlite3_free(zSql);
+ va_end(ap);
+ return p->rc;
+}
+
+/*
+** Attempt to allocate and return a pointer to a zeroed block of nByte
+** bytes.
+**
+** If an error (i.e. an OOM condition) occurs, return NULL and leave an
+** error code in the rbu handle passed as the first argument. Or, if an
+** error has already occurred when this function is called, return NULL
+** immediately without attempting the allocation or modifying the stored
+** error code.
+*/
+static void *rbuMalloc(sqlite3rbu *p, int nByte){
+ void *pRet = 0;
+ if( p->rc==SQLITE_OK ){
+ assert( nByte>0 );
+ pRet = sqlite3_malloc64(nByte);
+ if( pRet==0 ){
+ p->rc = SQLITE_NOMEM;
+ }else{
+ memset(pRet, 0, nByte);
+ }
+ }
+ return pRet;
+}
+
+
+/*
+** Allocate and zero the pIter->azTblCol[] and abTblPk[] arrays so that
+** there is room for at least nCol elements. If an OOM occurs, store an
+** error code in the RBU handle passed as the first argument.
+*/
+static void rbuAllocateIterArrays(sqlite3rbu *p, RbuObjIter *pIter, int nCol){
+ int nByte = (2*sizeof(char*) + sizeof(int) + 3*sizeof(u8)) * nCol;
+ char **azNew;
+
+ azNew = (char**)rbuMalloc(p, nByte);
+ if( azNew ){
+ pIter->azTblCol = azNew;
+ pIter->azTblType = &azNew[nCol];
+ pIter->aiSrcOrder = (int*)&pIter->azTblType[nCol];
+ pIter->abTblPk = (u8*)&pIter->aiSrcOrder[nCol];
+ pIter->abNotNull = (u8*)&pIter->abTblPk[nCol];
+ pIter->abIndexed = (u8*)&pIter->abNotNull[nCol];
+ }
+}
+
+/*
+** The first argument must be a nul-terminated string. This function
+** returns a copy of the string in memory obtained from sqlite3_malloc().
+** It is the responsibility of the caller to eventually free this memory
+** using sqlite3_free().
+**
+** If an OOM condition is encountered when attempting to allocate memory,
+** output variable (*pRc) is set to SQLITE_NOMEM before returning. Otherwise,
+** if the allocation succeeds, (*pRc) is left unchanged.
+*/
+static char *rbuStrndup(const char *zStr, int *pRc){
+ char *zRet = 0;
+
+ assert( *pRc==SQLITE_OK );
+ if( zStr ){
+ size_t nCopy = strlen(zStr) + 1;
+ zRet = (char*)sqlite3_malloc64(nCopy);
+ if( zRet ){
+ memcpy(zRet, zStr, nCopy);
+ }else{
+ *pRc = SQLITE_NOMEM;
+ }
+ }
+
+ return zRet;
+}
+
+/*
+** Finalize the statement passed as the second argument.
+**
+** If the sqlite3_finalize() call indicates that an error occurs, and the
+** rbu handle error code is not already set, set the error code and error
+** message accordingly.
+*/
+static void rbuFinalize(sqlite3rbu *p, sqlite3_stmt *pStmt){
+ sqlite3 *db = sqlite3_db_handle(pStmt);
+ int rc = sqlite3_finalize(pStmt);
+ if( p->rc==SQLITE_OK && rc!=SQLITE_OK ){
+ p->rc = rc;
+ p->zErrmsg = sqlite3_mprintf("%s", sqlite3_errmsg(db));
+ }
+}
+
+/* Determine the type of a table.
+**
+** peType is of type (int*), a pointer to an output parameter of type
+** (int). This call sets the output parameter as follows, depending
+** on the type of the table specified by parameters dbName and zTbl.
+**
+** RBU_PK_NOTABLE: No such table.
+** RBU_PK_NONE: Table has an implicit rowid.
+** RBU_PK_IPK: Table has an explicit IPK column.
+** RBU_PK_EXTERNAL: Table has an external PK index.
+** RBU_PK_WITHOUT_ROWID: Table is WITHOUT ROWID.
+** RBU_PK_VTAB: Table is a virtual table.
+**
+** Argument *piPk is also of type (int*), and also points to an output
+** parameter. Unless the table has an external primary key index
+** (i.e. unless *peType is set to 3), then *piPk is set to zero. Or,
+** if the table does have an external primary key index, then *piPk
+** is set to the root page number of the primary key index before
+** returning.
+**
+** ALGORITHM:
+**
+** if( no entry exists in sqlite_master ){
+** return RBU_PK_NOTABLE
+** }else if( sql for the entry starts with "CREATE VIRTUAL" ){
+** return RBU_PK_VTAB
+** }else if( "PRAGMA index_list()" for the table contains a "pk" index ){
+** if( the index that is the pk exists in sqlite_master ){
+** *piPK = rootpage of that index.
+** return RBU_PK_EXTERNAL
+** }else{
+** return RBU_PK_WITHOUT_ROWID
+** }
+** }else if( "PRAGMA table_info()" lists one or more "pk" columns ){
+** return RBU_PK_IPK
+** }else{
+** return RBU_PK_NONE
+** }
+*/
+static void rbuTableType(
+ sqlite3rbu *p,
+ const char *zTab,
+ int *peType,
+ int *piTnum,
+ int *piPk
+){
+ /*
+ ** 0) SELECT count(*) FROM sqlite_master where name=%Q AND IsVirtual(%Q)
+ ** 1) PRAGMA index_list = ?
+ ** 2) SELECT count(*) FROM sqlite_master where name=%Q
+ ** 3) PRAGMA table_info = ?
+ */
+ sqlite3_stmt *aStmt[4] = {0, 0, 0, 0};
+
+ *peType = RBU_PK_NOTABLE;
+ *piPk = 0;
+
+ assert( p->rc==SQLITE_OK );
+ p->rc = prepareFreeAndCollectError(p->dbMain, &aStmt[0], &p->zErrmsg,
+ sqlite3_mprintf(
+ "SELECT (sql LIKE 'create virtual%%'), rootpage"
+ " FROM sqlite_master"
+ " WHERE name=%Q", zTab
+ ));
+ if( p->rc!=SQLITE_OK || sqlite3_step(aStmt[0])!=SQLITE_ROW ){
+ /* Either an error, or no such table. */
+ goto rbuTableType_end;
+ }
+ if( sqlite3_column_int(aStmt[0], 0) ){
+ *peType = RBU_PK_VTAB; /* virtual table */
+ goto rbuTableType_end;
+ }
+ *piTnum = sqlite3_column_int(aStmt[0], 1);
+
+ p->rc = prepareFreeAndCollectError(p->dbMain, &aStmt[1], &p->zErrmsg,
+ sqlite3_mprintf("PRAGMA index_list=%Q",zTab)
+ );
+ if( p->rc ) goto rbuTableType_end;
+ while( sqlite3_step(aStmt[1])==SQLITE_ROW ){
+ const u8 *zOrig = sqlite3_column_text(aStmt[1], 3);
+ const u8 *zIdx = sqlite3_column_text(aStmt[1], 1);
+ if( zOrig && zIdx && zOrig[0]=='p' ){
+ p->rc = prepareFreeAndCollectError(p->dbMain, &aStmt[2], &p->zErrmsg,
+ sqlite3_mprintf(
+ "SELECT rootpage FROM sqlite_master WHERE name = %Q", zIdx
+ ));
+ if( p->rc==SQLITE_OK ){
+ if( sqlite3_step(aStmt[2])==SQLITE_ROW ){
+ *piPk = sqlite3_column_int(aStmt[2], 0);
+ *peType = RBU_PK_EXTERNAL;
+ }else{
+ *peType = RBU_PK_WITHOUT_ROWID;
+ }
+ }
+ goto rbuTableType_end;
+ }
+ }
+
+ p->rc = prepareFreeAndCollectError(p->dbMain, &aStmt[3], &p->zErrmsg,
+ sqlite3_mprintf("PRAGMA table_info=%Q",zTab)
+ );
+ if( p->rc==SQLITE_OK ){
+ while( sqlite3_step(aStmt[3])==SQLITE_ROW ){
+ if( sqlite3_column_int(aStmt[3],5)>0 ){
+ *peType = RBU_PK_IPK; /* explicit IPK column */
+ goto rbuTableType_end;
+ }
+ }
+ *peType = RBU_PK_NONE;
+ }
+
+rbuTableType_end: {
+ unsigned int i;
+ for(i=0; i<sizeof(aStmt)/sizeof(aStmt[0]); i++){
+ rbuFinalize(p, aStmt[i]);
+ }
+ }
+}
+
+/*
+** This is a helper function for rbuObjIterCacheTableInfo(). It populates
+** the pIter->abIndexed[] array.
+*/
+static void rbuObjIterCacheIndexedCols(sqlite3rbu *p, RbuObjIter *pIter){
+ sqlite3_stmt *pList = 0;
+ int bIndex = 0;
+
+ if( p->rc==SQLITE_OK ){
+ memcpy(pIter->abIndexed, pIter->abTblPk, sizeof(u8)*pIter->nTblCol);
+ p->rc = prepareFreeAndCollectError(p->dbMain, &pList, &p->zErrmsg,
+ sqlite3_mprintf("PRAGMA main.index_list = %Q", pIter->zTbl)
+ );
+ }
+
+ pIter->nIndex = 0;
+ while( p->rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pList) ){
+ const char *zIdx = (const char*)sqlite3_column_text(pList, 1);
+ sqlite3_stmt *pXInfo = 0;
+ if( zIdx==0 ) break;
+ p->rc = prepareFreeAndCollectError(p->dbMain, &pXInfo, &p->zErrmsg,
+ sqlite3_mprintf("PRAGMA main.index_xinfo = %Q", zIdx)
+ );
+ while( p->rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pXInfo) ){
+ int iCid = sqlite3_column_int(pXInfo, 1);
+ if( iCid>=0 ) pIter->abIndexed[iCid] = 1;
+ }
+ rbuFinalize(p, pXInfo);
+ bIndex = 1;
+ pIter->nIndex++;
+ }
+
+ if( pIter->eType==RBU_PK_WITHOUT_ROWID ){
+ /* "PRAGMA index_list" includes the main PK b-tree */
+ pIter->nIndex--;
+ }
+
+ rbuFinalize(p, pList);
+ if( bIndex==0 ) pIter->abIndexed = 0;
+}
+
+
+/*
+** If they are not already populated, populate the pIter->azTblCol[],
+** pIter->abTblPk[], pIter->nTblCol and pIter->bRowid variables according to
+** the table (not index) that the iterator currently points to.
+**
+** Return SQLITE_OK if successful, or an SQLite error code otherwise. If
+** an error does occur, an error code and error message are also left in
+** the RBU handle.
+*/
+static int rbuObjIterCacheTableInfo(sqlite3rbu *p, RbuObjIter *pIter){
+ if( pIter->azTblCol==0 ){
+ sqlite3_stmt *pStmt = 0;
+ int nCol = 0;
+ int i; /* for() loop iterator variable */
+ int bRbuRowid = 0; /* If input table has column "rbu_rowid" */
+ int iOrder = 0;
+ int iTnum = 0;
+
+ /* Figure out the type of table this step will deal with. */
+ assert( pIter->eType==0 );
+ rbuTableType(p, pIter->zTbl, &pIter->eType, &iTnum, &pIter->iPkTnum);
+ if( p->rc==SQLITE_OK && pIter->eType==RBU_PK_NOTABLE ){
+ p->rc = SQLITE_ERROR;
+ p->zErrmsg = sqlite3_mprintf("no such table: %s", pIter->zTbl);
+ }
+ if( p->rc ) return p->rc;
+ if( pIter->zIdx==0 ) pIter->iTnum = iTnum;
+
+ assert( pIter->eType==RBU_PK_NONE || pIter->eType==RBU_PK_IPK
+ || pIter->eType==RBU_PK_EXTERNAL || pIter->eType==RBU_PK_WITHOUT_ROWID
+ || pIter->eType==RBU_PK_VTAB
+ );
+
+ /* Populate the azTblCol[] and nTblCol variables based on the columns
+ ** of the input table. Ignore any input table columns that begin with
+ ** "rbu_". */
+ p->rc = prepareFreeAndCollectError(p->dbRbu, &pStmt, &p->zErrmsg,
+ sqlite3_mprintf("SELECT * FROM '%q'", pIter->zDataTbl)
+ );
+ if( p->rc==SQLITE_OK ){
+ nCol = sqlite3_column_count(pStmt);
+ rbuAllocateIterArrays(p, pIter, nCol);
+ }
+ for(i=0; p->rc==SQLITE_OK && i<nCol; i++){
+ const char *zName = (const char*)sqlite3_column_name(pStmt, i);
+ if( sqlite3_strnicmp("rbu_", zName, 4) ){
+ char *zCopy = rbuStrndup(zName, &p->rc);
+ pIter->aiSrcOrder[pIter->nTblCol] = pIter->nTblCol;
+ pIter->azTblCol[pIter->nTblCol++] = zCopy;
+ }
+ else if( 0==sqlite3_stricmp("rbu_rowid", zName) ){
+ bRbuRowid = 1;
+ }
+ }
+ sqlite3_finalize(pStmt);
+ pStmt = 0;
+
+ if( p->rc==SQLITE_OK
+ && rbuIsVacuum(p)==0
+ && bRbuRowid!=(pIter->eType==RBU_PK_VTAB || pIter->eType==RBU_PK_NONE)
+ ){
+ p->rc = SQLITE_ERROR;
+ p->zErrmsg = sqlite3_mprintf(
+ "table %q %s rbu_rowid column", pIter->zDataTbl,
+ (bRbuRowid ? "may not have" : "requires")
+ );
+ }
+
+ /* Check that all non-HIDDEN columns in the destination table are also
+ ** present in the input table. Populate the abTblPk[], azTblType[] and
+ ** aiTblOrder[] arrays at the same time. */
+ if( p->rc==SQLITE_OK ){
+ p->rc = prepareFreeAndCollectError(p->dbMain, &pStmt, &p->zErrmsg,
+ sqlite3_mprintf("PRAGMA table_info(%Q)", pIter->zTbl)
+ );
+ }
+ while( p->rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){
+ const char *zName = (const char*)sqlite3_column_text(pStmt, 1);
+ if( zName==0 ) break; /* An OOM - finalize() below returns S_NOMEM */
+ for(i=iOrder; i<pIter->nTblCol; i++){
+ if( 0==strcmp(zName, pIter->azTblCol[i]) ) break;
+ }
+ if( i==pIter->nTblCol ){
+ p->rc = SQLITE_ERROR;
+ p->zErrmsg = sqlite3_mprintf("column missing from %q: %s",
+ pIter->zDataTbl, zName
+ );
+ }else{
+ int iPk = sqlite3_column_int(pStmt, 5);
+ int bNotNull = sqlite3_column_int(pStmt, 3);
+ const char *zType = (const char*)sqlite3_column_text(pStmt, 2);
+
+ if( i!=iOrder ){
+ SWAP(int, pIter->aiSrcOrder[i], pIter->aiSrcOrder[iOrder]);
+ SWAP(char*, pIter->azTblCol[i], pIter->azTblCol[iOrder]);
+ }
+
+ pIter->azTblType[iOrder] = rbuStrndup(zType, &p->rc);
+ pIter->abTblPk[iOrder] = (iPk!=0);
+ pIter->abNotNull[iOrder] = (u8)bNotNull || (iPk!=0);
+ iOrder++;
+ }
+ }
+
+ rbuFinalize(p, pStmt);
+ rbuObjIterCacheIndexedCols(p, pIter);
+ assert( pIter->eType!=RBU_PK_VTAB || pIter->abIndexed==0 );
+ assert( pIter->eType!=RBU_PK_VTAB || pIter->nIndex==0 );
+ }
+
+ return p->rc;
+}
+
+/*
+** This function constructs and returns a pointer to a nul-terminated
+** string containing some SQL clause or list based on one or more of the
+** column names currently stored in the pIter->azTblCol[] array.
+*/
+static char *rbuObjIterGetCollist(
+ sqlite3rbu *p, /* RBU object */
+ RbuObjIter *pIter /* Object iterator for column names */
+){
+ char *zList = 0;
+ const char *zSep = "";
+ int i;
+ for(i=0; i<pIter->nTblCol; i++){
+ const char *z = pIter->azTblCol[i];
+ zList = rbuMPrintf(p, "%z%s\"%w\"", zList, zSep, z);
+ zSep = ", ";
+ }
+ return zList;
+}
+
+/*
+** This function is used to create a SELECT list (the list of SQL
+** expressions that follows a SELECT keyword) for a SELECT statement
+** used to read from an data_xxx or rbu_tmp_xxx table while updating the
+** index object currently indicated by the iterator object passed as the
+** second argument. A "PRAGMA index_xinfo = <idxname>" statement is used
+** to obtain the required information.
+**
+** If the index is of the following form:
+**
+** CREATE INDEX i1 ON t1(c, b COLLATE nocase);
+**
+** and "t1" is a table with an explicit INTEGER PRIMARY KEY column
+** "ipk", the returned string is:
+**
+** "`c` COLLATE 'BINARY', `b` COLLATE 'NOCASE', `ipk` COLLATE 'BINARY'"
+**
+** As well as the returned string, three other malloc'd strings are
+** returned via output parameters. As follows:
+**
+** pzImposterCols: ...
+** pzImposterPk: ...
+** pzWhere: ...
+*/
+static char *rbuObjIterGetIndexCols(
+ sqlite3rbu *p, /* RBU object */
+ RbuObjIter *pIter, /* Object iterator for column names */
+ char **pzImposterCols, /* OUT: Columns for imposter table */
+ char **pzImposterPk, /* OUT: Imposter PK clause */
+ char **pzWhere, /* OUT: WHERE clause */
+ int *pnBind /* OUT: Trbul number of columns */
+){
+ int rc = p->rc; /* Error code */
+ int rc2; /* sqlite3_finalize() return code */
+ char *zRet = 0; /* String to return */
+ char *zImpCols = 0; /* String to return via *pzImposterCols */
+ char *zImpPK = 0; /* String to return via *pzImposterPK */
+ char *zWhere = 0; /* String to return via *pzWhere */
+ int nBind = 0; /* Value to return via *pnBind */
+ const char *zCom = ""; /* Set to ", " later on */
+ const char *zAnd = ""; /* Set to " AND " later on */
+ sqlite3_stmt *pXInfo = 0; /* PRAGMA index_xinfo = ? */
+
+ if( rc==SQLITE_OK ){
+ assert( p->zErrmsg==0 );
+ rc = prepareFreeAndCollectError(p->dbMain, &pXInfo, &p->zErrmsg,
+ sqlite3_mprintf("PRAGMA main.index_xinfo = %Q", pIter->zIdx)
+ );
+ }
+
+ while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pXInfo) ){
+ int iCid = sqlite3_column_int(pXInfo, 1);
+ int bDesc = sqlite3_column_int(pXInfo, 3);
+ const char *zCollate = (const char*)sqlite3_column_text(pXInfo, 4);
+ const char *zCol;
+ const char *zType;
+
+ if( iCid<0 ){
+ /* An integer primary key. If the table has an explicit IPK, use
+ ** its name. Otherwise, use "rbu_rowid". */
+ if( pIter->eType==RBU_PK_IPK ){
+ int i;
+ for(i=0; pIter->abTblPk[i]==0; i++);
+ assert( i<pIter->nTblCol );
+ zCol = pIter->azTblCol[i];
+ }else if( rbuIsVacuum(p) ){
+ zCol = "_rowid_";
+ }else{
+ zCol = "rbu_rowid";
+ }
+ zType = "INTEGER";
+ }else{
+ zCol = pIter->azTblCol[iCid];
+ zType = pIter->azTblType[iCid];
+ }
+
+ zRet = sqlite3_mprintf("%z%s\"%w\" COLLATE %Q", zRet, zCom, zCol, zCollate);
+ if( pIter->bUnique==0 || sqlite3_column_int(pXInfo, 5) ){
+ const char *zOrder = (bDesc ? " DESC" : "");
+ zImpPK = sqlite3_mprintf("%z%s\"rbu_imp_%d%w\"%s",
+ zImpPK, zCom, nBind, zCol, zOrder
+ );
+ }
+ zImpCols = sqlite3_mprintf("%z%s\"rbu_imp_%d%w\" %s COLLATE %Q",
+ zImpCols, zCom, nBind, zCol, zType, zCollate
+ );
+ zWhere = sqlite3_mprintf(
+ "%z%s\"rbu_imp_%d%w\" IS ?", zWhere, zAnd, nBind, zCol
+ );
+ if( zRet==0 || zImpPK==0 || zImpCols==0 || zWhere==0 ) rc = SQLITE_NOMEM;
+ zCom = ", ";
+ zAnd = " AND ";
+ nBind++;
+ }
+
+ rc2 = sqlite3_finalize(pXInfo);
+ if( rc==SQLITE_OK ) rc = rc2;
+
+ if( rc!=SQLITE_OK ){
+ sqlite3_free(zRet);
+ sqlite3_free(zImpCols);
+ sqlite3_free(zImpPK);
+ sqlite3_free(zWhere);
+ zRet = 0;
+ zImpCols = 0;
+ zImpPK = 0;
+ zWhere = 0;
+ p->rc = rc;
+ }
+
+ *pzImposterCols = zImpCols;
+ *pzImposterPk = zImpPK;
+ *pzWhere = zWhere;
+ *pnBind = nBind;
+ return zRet;
+}
+
+/*
+** Assuming the current table columns are "a", "b" and "c", and the zObj
+** paramter is passed "old", return a string of the form:
+**
+** "old.a, old.b, old.b"
+**
+** With the column names escaped.
+**
+** For tables with implicit rowids - RBU_PK_EXTERNAL and RBU_PK_NONE, append
+** the text ", old._rowid_" to the returned value.
+*/
+static char *rbuObjIterGetOldlist(
+ sqlite3rbu *p,
+ RbuObjIter *pIter,
+ const char *zObj
+){
+ char *zList = 0;
+ if( p->rc==SQLITE_OK && pIter->abIndexed ){
+ const char *zS = "";
+ int i;
+ for(i=0; i<pIter->nTblCol; i++){
+ if( pIter->abIndexed[i] ){
+ const char *zCol = pIter->azTblCol[i];
+ zList = sqlite3_mprintf("%z%s%s.\"%w\"", zList, zS, zObj, zCol);
+ }else{
+ zList = sqlite3_mprintf("%z%sNULL", zList, zS);
+ }
+ zS = ", ";
+ if( zList==0 ){
+ p->rc = SQLITE_NOMEM;
+ break;
+ }
+ }
+
+ /* For a table with implicit rowids, append "old._rowid_" to the list. */
+ if( pIter->eType==RBU_PK_EXTERNAL || pIter->eType==RBU_PK_NONE ){
+ zList = rbuMPrintf(p, "%z, %s._rowid_", zList, zObj);
+ }
+ }
+ return zList;
+}
+
+/*
+** Return an expression that can be used in a WHERE clause to match the
+** primary key of the current table. For example, if the table is:
+**
+** CREATE TABLE t1(a, b, c, PRIMARY KEY(b, c));
+**
+** Return the string:
+**
+** "b = ?1 AND c = ?2"
+*/
+static char *rbuObjIterGetWhere(
+ sqlite3rbu *p,
+ RbuObjIter *pIter
+){
+ char *zList = 0;
+ if( pIter->eType==RBU_PK_VTAB || pIter->eType==RBU_PK_NONE ){
+ zList = rbuMPrintf(p, "_rowid_ = ?%d", pIter->nTblCol+1);
+ }else if( pIter->eType==RBU_PK_EXTERNAL ){
+ const char *zSep = "";
+ int i;
+ for(i=0; i<pIter->nTblCol; i++){
+ if( pIter->abTblPk[i] ){
+ zList = rbuMPrintf(p, "%z%sc%d=?%d", zList, zSep, i, i+1);
+ zSep = " AND ";
+ }
+ }
+ zList = rbuMPrintf(p,
+ "_rowid_ = (SELECT id FROM rbu_imposter2 WHERE %z)", zList
+ );
+
+ }else{
+ const char *zSep = "";
+ int i;
+ for(i=0; i<pIter->nTblCol; i++){
+ if( pIter->abTblPk[i] ){
+ const char *zCol = pIter->azTblCol[i];
+ zList = rbuMPrintf(p, "%z%s\"%w\"=?%d", zList, zSep, zCol, i+1);
+ zSep = " AND ";
+ }
+ }
+ }
+ return zList;
+}
+
+/*
+** The SELECT statement iterating through the keys for the current object
+** (p->objiter.pSelect) currently points to a valid row. However, there
+** is something wrong with the rbu_control value in the rbu_control value
+** stored in the (p->nCol+1)'th column. Set the error code and error message
+** of the RBU handle to something reflecting this.
+*/
+static void rbuBadControlError(sqlite3rbu *p){
+ p->rc = SQLITE_ERROR;
+ p->zErrmsg = sqlite3_mprintf("invalid rbu_control value");
+}
+
+
+/*
+** Return a nul-terminated string containing the comma separated list of
+** assignments that should be included following the "SET" keyword of
+** an UPDATE statement used to update the table object that the iterator
+** passed as the second argument currently points to if the rbu_control
+** column of the data_xxx table entry is set to zMask.
+**
+** The memory for the returned string is obtained from sqlite3_malloc().
+** It is the responsibility of the caller to eventually free it using
+** sqlite3_free().
+**
+** If an OOM error is encountered when allocating space for the new
+** string, an error code is left in the rbu handle passed as the first
+** argument and NULL is returned. Or, if an error has already occurred
+** when this function is called, NULL is returned immediately, without
+** attempting the allocation or modifying the stored error code.
+*/
+static char *rbuObjIterGetSetlist(
+ sqlite3rbu *p,
+ RbuObjIter *pIter,
+ const char *zMask
+){
+ char *zList = 0;
+ if( p->rc==SQLITE_OK ){
+ int i;
+
+ if( (int)strlen(zMask)!=pIter->nTblCol ){
+ rbuBadControlError(p);
+ }else{
+ const char *zSep = "";
+ for(i=0; i<pIter->nTblCol; i++){
+ char c = zMask[pIter->aiSrcOrder[i]];
+ if( c=='x' ){
+ zList = rbuMPrintf(p, "%z%s\"%w\"=?%d",
+ zList, zSep, pIter->azTblCol[i], i+1
+ );
+ zSep = ", ";
+ }
+ else if( c=='d' ){
+ zList = rbuMPrintf(p, "%z%s\"%w\"=rbu_delta(\"%w\", ?%d)",
+ zList, zSep, pIter->azTblCol[i], pIter->azTblCol[i], i+1
+ );
+ zSep = ", ";
+ }
+ else if( c=='f' ){
+ zList = rbuMPrintf(p, "%z%s\"%w\"=rbu_fossil_delta(\"%w\", ?%d)",
+ zList, zSep, pIter->azTblCol[i], pIter->azTblCol[i], i+1
+ );
+ zSep = ", ";
+ }
+ }
+ }
+ }
+ return zList;
+}
+
+/*
+** Return a nul-terminated string consisting of nByte comma separated
+** "?" expressions. For example, if nByte is 3, return a pointer to
+** a buffer containing the string "?,?,?".
+**
+** The memory for the returned string is obtained from sqlite3_malloc().
+** It is the responsibility of the caller to eventually free it using
+** sqlite3_free().
+**
+** If an OOM error is encountered when allocating space for the new
+** string, an error code is left in the rbu handle passed as the first
+** argument and NULL is returned. Or, if an error has already occurred
+** when this function is called, NULL is returned immediately, without
+** attempting the allocation or modifying the stored error code.
+*/
+static char *rbuObjIterGetBindlist(sqlite3rbu *p, int nBind){
+ char *zRet = 0;
+ int nByte = nBind*2 + 1;
+
+ zRet = (char*)rbuMalloc(p, nByte);
+ if( zRet ){
+ int i;
+ for(i=0; i<nBind; i++){
+ zRet[i*2] = '?';
+ zRet[i*2+1] = (i+1==nBind) ? '\0' : ',';
+ }
+ }
+ return zRet;
+}
+
+/*
+** The iterator currently points to a table (not index) of type
+** RBU_PK_WITHOUT_ROWID. This function creates the PRIMARY KEY
+** declaration for the corresponding imposter table. For example,
+** if the iterator points to a table created as:
+**
+** CREATE TABLE t1(a, b, c, PRIMARY KEY(b, a DESC)) WITHOUT ROWID
+**
+** this function returns:
+**
+** PRIMARY KEY("b", "a" DESC)
+*/
+static char *rbuWithoutRowidPK(sqlite3rbu *p, RbuObjIter *pIter){
+ char *z = 0;
+ assert( pIter->zIdx==0 );
+ if( p->rc==SQLITE_OK ){
+ const char *zSep = "PRIMARY KEY(";
+ sqlite3_stmt *pXList = 0; /* PRAGMA index_list = (pIter->zTbl) */
+ sqlite3_stmt *pXInfo = 0; /* PRAGMA index_xinfo = <pk-index> */
+
+ p->rc = prepareFreeAndCollectError(p->dbMain, &pXList, &p->zErrmsg,
+ sqlite3_mprintf("PRAGMA main.index_list = %Q", pIter->zTbl)
+ );
+ while( p->rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pXList) ){
+ const char *zOrig = (const char*)sqlite3_column_text(pXList,3);
+ if( zOrig && strcmp(zOrig, "pk")==0 ){
+ const char *zIdx = (const char*)sqlite3_column_text(pXList,1);
+ if( zIdx ){
+ p->rc = prepareFreeAndCollectError(p->dbMain, &pXInfo, &p->zErrmsg,
+ sqlite3_mprintf("PRAGMA main.index_xinfo = %Q", zIdx)
+ );
+ }
+ break;
+ }
+ }
+ rbuFinalize(p, pXList);
+
+ while( p->rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pXInfo) ){
+ if( sqlite3_column_int(pXInfo, 5) ){
+ /* int iCid = sqlite3_column_int(pXInfo, 0); */
+ const char *zCol = (const char*)sqlite3_column_text(pXInfo, 2);
+ const char *zDesc = sqlite3_column_int(pXInfo, 3) ? " DESC" : "";
+ z = rbuMPrintf(p, "%z%s\"%w\"%s", z, zSep, zCol, zDesc);
+ zSep = ", ";
+ }
+ }
+ z = rbuMPrintf(p, "%z)", z);
+ rbuFinalize(p, pXInfo);
+ }
+ return z;
+}
+
+/*
+** This function creates the second imposter table used when writing to
+** a table b-tree where the table has an external primary key. If the
+** iterator passed as the second argument does not currently point to
+** a table (not index) with an external primary key, this function is a
+** no-op.
+**
+** Assuming the iterator does point to a table with an external PK, this
+** function creates a WITHOUT ROWID imposter table named "rbu_imposter2"
+** used to access that PK index. For example, if the target table is
+** declared as follows:
+**
+** CREATE TABLE t1(a, b TEXT, c REAL, PRIMARY KEY(b, c));
+**
+** then the imposter table schema is:
+**
+** CREATE TABLE rbu_imposter2(c1 TEXT, c2 REAL, id INTEGER) WITHOUT ROWID;
+**
+*/
+static void rbuCreateImposterTable2(sqlite3rbu *p, RbuObjIter *pIter){
+ if( p->rc==SQLITE_OK && pIter->eType==RBU_PK_EXTERNAL ){
+ int tnum = pIter->iPkTnum; /* Root page of PK index */
+ sqlite3_stmt *pQuery = 0; /* SELECT name ... WHERE rootpage = $tnum */
+ const char *zIdx = 0; /* Name of PK index */
+ sqlite3_stmt *pXInfo = 0; /* PRAGMA main.index_xinfo = $zIdx */
+ const char *zComma = "";
+ char *zCols = 0; /* Used to build up list of table cols */
+ char *zPk = 0; /* Used to build up table PK declaration */
+
+ /* Figure out the name of the primary key index for the current table.
+ ** This is needed for the argument to "PRAGMA index_xinfo". Set
+ ** zIdx to point to a nul-terminated string containing this name. */
+ p->rc = prepareAndCollectError(p->dbMain, &pQuery, &p->zErrmsg,
+ "SELECT name FROM sqlite_master WHERE rootpage = ?"
+ );
+ if( p->rc==SQLITE_OK ){
+ sqlite3_bind_int(pQuery, 1, tnum);
+ if( SQLITE_ROW==sqlite3_step(pQuery) ){
+ zIdx = (const char*)sqlite3_column_text(pQuery, 0);
+ }
+ }
+ if( zIdx ){
+ p->rc = prepareFreeAndCollectError(p->dbMain, &pXInfo, &p->zErrmsg,
+ sqlite3_mprintf("PRAGMA main.index_xinfo = %Q", zIdx)
+ );
+ }
+ rbuFinalize(p, pQuery);
+
+ while( p->rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pXInfo) ){
+ int bKey = sqlite3_column_int(pXInfo, 5);
+ if( bKey ){
+ int iCid = sqlite3_column_int(pXInfo, 1);
+ int bDesc = sqlite3_column_int(pXInfo, 3);
+ const char *zCollate = (const char*)sqlite3_column_text(pXInfo, 4);
+ zCols = rbuMPrintf(p, "%z%sc%d %s COLLATE %s", zCols, zComma,
+ iCid, pIter->azTblType[iCid], zCollate
+ );
+ zPk = rbuMPrintf(p, "%z%sc%d%s", zPk, zComma, iCid, bDesc?" DESC":"");
+ zComma = ", ";
+ }
+ }
+ zCols = rbuMPrintf(p, "%z, id INTEGER", zCols);
+ rbuFinalize(p, pXInfo);
+
+ sqlite3_test_control(SQLITE_TESTCTRL_IMPOSTER, p->dbMain, "main", 1, tnum);
+ rbuMPrintfExec(p, p->dbMain,
+ "CREATE TABLE rbu_imposter2(%z, PRIMARY KEY(%z)) WITHOUT ROWID",
+ zCols, zPk
+ );
+ sqlite3_test_control(SQLITE_TESTCTRL_IMPOSTER, p->dbMain, "main", 0, 0);
+ }
+}
+
+/*
+** If an error has already occurred when this function is called, it
+** immediately returns zero (without doing any work). Or, if an error
+** occurs during the execution of this function, it sets the error code
+** in the sqlite3rbu object indicated by the first argument and returns
+** zero.
+**
+** The iterator passed as the second argument is guaranteed to point to
+** a table (not an index) when this function is called. This function
+** attempts to create any imposter table required to write to the main
+** table b-tree of the table before returning. Non-zero is returned if
+** an imposter table are created, or zero otherwise.
+**
+** An imposter table is required in all cases except RBU_PK_VTAB. Only
+** virtual tables are written to directly. The imposter table has the
+** same schema as the actual target table (less any UNIQUE constraints).
+** More precisely, the "same schema" means the same columns, types,
+** collation sequences. For tables that do not have an external PRIMARY
+** KEY, it also means the same PRIMARY KEY declaration.
+*/
+static void rbuCreateImposterTable(sqlite3rbu *p, RbuObjIter *pIter){
+ if( p->rc==SQLITE_OK && pIter->eType!=RBU_PK_VTAB ){
+ int tnum = pIter->iTnum;
+ const char *zComma = "";
+ char *zSql = 0;
+ int iCol;
+ sqlite3_test_control(SQLITE_TESTCTRL_IMPOSTER, p->dbMain, "main", 0, 1);
+
+ for(iCol=0; p->rc==SQLITE_OK && iCol<pIter->nTblCol; iCol++){
+ const char *zPk = "";
+ const char *zCol = pIter->azTblCol[iCol];
+ const char *zColl = 0;
+
+ p->rc = sqlite3_table_column_metadata(
+ p->dbMain, "main", pIter->zTbl, zCol, 0, &zColl, 0, 0, 0
+ );
+
+ if( pIter->eType==RBU_PK_IPK && pIter->abTblPk[iCol] ){
+ /* If the target table column is an "INTEGER PRIMARY KEY", add
+ ** "PRIMARY KEY" to the imposter table column declaration. */
+ zPk = "PRIMARY KEY ";
+ }
+ zSql = rbuMPrintf(p, "%z%s\"%w\" %s %sCOLLATE %s%s",
+ zSql, zComma, zCol, pIter->azTblType[iCol], zPk, zColl,
+ (pIter->abNotNull[iCol] ? " NOT NULL" : "")
+ );
+ zComma = ", ";
+ }
+
+ if( pIter->eType==RBU_PK_WITHOUT_ROWID ){
+ char *zPk = rbuWithoutRowidPK(p, pIter);
+ if( zPk ){
+ zSql = rbuMPrintf(p, "%z, %z", zSql, zPk);
+ }
+ }
+
+ sqlite3_test_control(SQLITE_TESTCTRL_IMPOSTER, p->dbMain, "main", 1, tnum);
+ rbuMPrintfExec(p, p->dbMain, "CREATE TABLE \"rbu_imp_%w\"(%z)%s",
+ pIter->zTbl, zSql,
+ (pIter->eType==RBU_PK_WITHOUT_ROWID ? " WITHOUT ROWID" : "")
+ );
+ sqlite3_test_control(SQLITE_TESTCTRL_IMPOSTER, p->dbMain, "main", 0, 0);
+ }
+}
+
+/*
+** Prepare a statement used to insert rows into the "rbu_tmp_xxx" table.
+** Specifically a statement of the form:
+**
+** INSERT INTO rbu_tmp_xxx VALUES(?, ?, ? ...);
+**
+** The number of bound variables is equal to the number of columns in
+** the target table, plus one (for the rbu_control column), plus one more
+** (for the rbu_rowid column) if the target table is an implicit IPK or
+** virtual table.
+*/
+static void rbuObjIterPrepareTmpInsert(
+ sqlite3rbu *p,
+ RbuObjIter *pIter,
+ const char *zCollist,
+ const char *zRbuRowid
+){
+ int bRbuRowid = (pIter->eType==RBU_PK_EXTERNAL || pIter->eType==RBU_PK_NONE);
+ char *zBind = rbuObjIterGetBindlist(p, pIter->nTblCol + 1 + bRbuRowid);
+ if( zBind ){
+ assert( pIter->pTmpInsert==0 );
+ p->rc = prepareFreeAndCollectError(
+ p->dbRbu, &pIter->pTmpInsert, &p->zErrmsg, sqlite3_mprintf(
+ "INSERT INTO %s.'rbu_tmp_%q'(rbu_control,%s%s) VALUES(%z)",
+ p->zStateDb, pIter->zDataTbl, zCollist, zRbuRowid, zBind
+ ));
+ }
+}
+
+static void rbuTmpInsertFunc(
+ sqlite3_context *pCtx,
+ int nVal,
+ sqlite3_value **apVal
+){
+ sqlite3rbu *p = sqlite3_user_data(pCtx);
+ int rc = SQLITE_OK;
+ int i;
+
+ assert( sqlite3_value_int(apVal[0])!=0
+ || p->objiter.eType==RBU_PK_EXTERNAL
+ || p->objiter.eType==RBU_PK_NONE
+ );
+ if( sqlite3_value_int(apVal[0])!=0 ){
+ p->nPhaseOneStep += p->objiter.nIndex;
+ }
+
+ for(i=0; rc==SQLITE_OK && i<nVal; i++){
+ rc = sqlite3_bind_value(p->objiter.pTmpInsert, i+1, apVal[i]);
+ }
+ if( rc==SQLITE_OK ){
+ sqlite3_step(p->objiter.pTmpInsert);
+ rc = sqlite3_reset(p->objiter.pTmpInsert);
+ }
+
+ if( rc!=SQLITE_OK ){
+ sqlite3_result_error_code(pCtx, rc);
+ }
+}
+
+/*
+** Ensure that the SQLite statement handles required to update the
+** target database object currently indicated by the iterator passed
+** as the second argument are available.
+*/
+static int rbuObjIterPrepareAll(
+ sqlite3rbu *p,
+ RbuObjIter *pIter,
+ int nOffset /* Add "LIMIT -1 OFFSET $nOffset" to SELECT */
+){
+ assert( pIter->bCleanup==0 );
+ if( pIter->pSelect==0 && rbuObjIterCacheTableInfo(p, pIter)==SQLITE_OK ){
+ const int tnum = pIter->iTnum;
+ char *zCollist = 0; /* List of indexed columns */
+ char **pz = &p->zErrmsg;
+ const char *zIdx = pIter->zIdx;
+ char *zLimit = 0;
+
+ if( nOffset ){
+ zLimit = sqlite3_mprintf(" LIMIT -1 OFFSET %d", nOffset);
+ if( !zLimit ) p->rc = SQLITE_NOMEM;
+ }
+
+ if( zIdx ){
+ const char *zTbl = pIter->zTbl;
+ char *zImposterCols = 0; /* Columns for imposter table */
+ char *zImposterPK = 0; /* Primary key declaration for imposter */
+ char *zWhere = 0; /* WHERE clause on PK columns */
+ char *zBind = 0;
+ int nBind = 0;
+
+ assert( pIter->eType!=RBU_PK_VTAB );
+ zCollist = rbuObjIterGetIndexCols(
+ p, pIter, &zImposterCols, &zImposterPK, &zWhere, &nBind
+ );
+ zBind = rbuObjIterGetBindlist(p, nBind);
+
+ /* Create the imposter table used to write to this index. */
+ sqlite3_test_control(SQLITE_TESTCTRL_IMPOSTER, p->dbMain, "main", 0, 1);
+ sqlite3_test_control(SQLITE_TESTCTRL_IMPOSTER, p->dbMain, "main", 1,tnum);
+ rbuMPrintfExec(p, p->dbMain,
+ "CREATE TABLE \"rbu_imp_%w\"( %s, PRIMARY KEY( %s ) ) WITHOUT ROWID",
+ zTbl, zImposterCols, zImposterPK
+ );
+ sqlite3_test_control(SQLITE_TESTCTRL_IMPOSTER, p->dbMain, "main", 0, 0);
+
+ /* Create the statement to insert index entries */
+ pIter->nCol = nBind;
+ if( p->rc==SQLITE_OK ){
+ p->rc = prepareFreeAndCollectError(
+ p->dbMain, &pIter->pInsert, &p->zErrmsg,
+ sqlite3_mprintf("INSERT INTO \"rbu_imp_%w\" VALUES(%s)", zTbl, zBind)
+ );
+ }
+
+ /* And to delete index entries */
+ if( rbuIsVacuum(p)==0 && p->rc==SQLITE_OK ){
+ p->rc = prepareFreeAndCollectError(
+ p->dbMain, &pIter->pDelete, &p->zErrmsg,
+ sqlite3_mprintf("DELETE FROM \"rbu_imp_%w\" WHERE %s", zTbl, zWhere)
+ );
+ }
+
+ /* Create the SELECT statement to read keys in sorted order */
+ if( p->rc==SQLITE_OK ){
+ char *zSql;
+ if( rbuIsVacuum(p) ){
+ zSql = sqlite3_mprintf(
+ "SELECT %s, 0 AS rbu_control FROM '%q' ORDER BY %s%s",
+ zCollist,
+ pIter->zDataTbl,
+ zCollist, zLimit
+ );
+ }else
+
+ if( pIter->eType==RBU_PK_EXTERNAL || pIter->eType==RBU_PK_NONE ){
+ zSql = sqlite3_mprintf(
+ "SELECT %s, rbu_control FROM %s.'rbu_tmp_%q' ORDER BY %s%s",
+ zCollist, p->zStateDb, pIter->zDataTbl,
+ zCollist, zLimit
+ );
+ }else{
+ zSql = sqlite3_mprintf(
+ "SELECT %s, rbu_control FROM %s.'rbu_tmp_%q' "
+ "UNION ALL "
+ "SELECT %s, rbu_control FROM '%q' "
+ "WHERE typeof(rbu_control)='integer' AND rbu_control!=1 "
+ "ORDER BY %s%s",
+ zCollist, p->zStateDb, pIter->zDataTbl,
+ zCollist, pIter->zDataTbl,
+ zCollist, zLimit
+ );
+ }
+ p->rc = prepareFreeAndCollectError(p->dbRbu, &pIter->pSelect, pz, zSql);
+ }
+
+ sqlite3_free(zImposterCols);
+ sqlite3_free(zImposterPK);
+ sqlite3_free(zWhere);
+ sqlite3_free(zBind);
+ }else{
+ int bRbuRowid = (pIter->eType==RBU_PK_VTAB)
+ ||(pIter->eType==RBU_PK_NONE)
+ ||(pIter->eType==RBU_PK_EXTERNAL && rbuIsVacuum(p));
+ const char *zTbl = pIter->zTbl; /* Table this step applies to */
+ const char *zWrite; /* Imposter table name */
+
+ char *zBindings = rbuObjIterGetBindlist(p, pIter->nTblCol + bRbuRowid);
+ char *zWhere = rbuObjIterGetWhere(p, pIter);
+ char *zOldlist = rbuObjIterGetOldlist(p, pIter, "old");
+ char *zNewlist = rbuObjIterGetOldlist(p, pIter, "new");
+
+ zCollist = rbuObjIterGetCollist(p, pIter);
+ pIter->nCol = pIter->nTblCol;
+
+ /* Create the imposter table or tables (if required). */
+ rbuCreateImposterTable(p, pIter);
+ rbuCreateImposterTable2(p, pIter);
+ zWrite = (pIter->eType==RBU_PK_VTAB ? "" : "rbu_imp_");
+
+ /* Create the INSERT statement to write to the target PK b-tree */
+ if( p->rc==SQLITE_OK ){
+ p->rc = prepareFreeAndCollectError(p->dbMain, &pIter->pInsert, pz,
+ sqlite3_mprintf(
+ "INSERT INTO \"%s%w\"(%s%s) VALUES(%s)",
+ zWrite, zTbl, zCollist, (bRbuRowid ? ", _rowid_" : ""), zBindings
+ )
+ );
+ }
+
+ /* Create the DELETE statement to write to the target PK b-tree.
+ ** Because it only performs INSERT operations, this is not required for
+ ** an rbu vacuum handle. */
+ if( rbuIsVacuum(p)==0 && p->rc==SQLITE_OK ){
+ p->rc = prepareFreeAndCollectError(p->dbMain, &pIter->pDelete, pz,
+ sqlite3_mprintf(
+ "DELETE FROM \"%s%w\" WHERE %s", zWrite, zTbl, zWhere
+ )
+ );
+ }
+
+ if( rbuIsVacuum(p)==0 && pIter->abIndexed ){
+ const char *zRbuRowid = "";
+ if( pIter->eType==RBU_PK_EXTERNAL || pIter->eType==RBU_PK_NONE ){
+ zRbuRowid = ", rbu_rowid";
+ }
+
+ /* Create the rbu_tmp_xxx table and the triggers to populate it. */
+ rbuMPrintfExec(p, p->dbRbu,
+ "CREATE TABLE IF NOT EXISTS %s.'rbu_tmp_%q' AS "
+ "SELECT *%s FROM '%q' WHERE 0;"
+ , p->zStateDb, pIter->zDataTbl
+ , (pIter->eType==RBU_PK_EXTERNAL ? ", 0 AS rbu_rowid" : "")
+ , pIter->zDataTbl
+ );
+
+ rbuMPrintfExec(p, p->dbMain,
+ "CREATE TEMP TRIGGER rbu_delete_tr BEFORE DELETE ON \"%s%w\" "
+ "BEGIN "
+ " SELECT rbu_tmp_insert(3, %s);"
+ "END;"
+
+ "CREATE TEMP TRIGGER rbu_update1_tr BEFORE UPDATE ON \"%s%w\" "
+ "BEGIN "
+ " SELECT rbu_tmp_insert(3, %s);"
+ "END;"
+
+ "CREATE TEMP TRIGGER rbu_update2_tr AFTER UPDATE ON \"%s%w\" "
+ "BEGIN "
+ " SELECT rbu_tmp_insert(4, %s);"
+ "END;",
+ zWrite, zTbl, zOldlist,
+ zWrite, zTbl, zOldlist,
+ zWrite, zTbl, zNewlist
+ );
+
+ if( pIter->eType==RBU_PK_EXTERNAL || pIter->eType==RBU_PK_NONE ){
+ rbuMPrintfExec(p, p->dbMain,
+ "CREATE TEMP TRIGGER rbu_insert_tr AFTER INSERT ON \"%s%w\" "
+ "BEGIN "
+ " SELECT rbu_tmp_insert(0, %s);"
+ "END;",
+ zWrite, zTbl, zNewlist
+ );
+ }
+
+ rbuObjIterPrepareTmpInsert(p, pIter, zCollist, zRbuRowid);
+ }
+
+ /* Create the SELECT statement to read keys from data_xxx */
+ if( p->rc==SQLITE_OK ){
+ const char *zRbuRowid = "";
+ if( bRbuRowid ){
+ zRbuRowid = rbuIsVacuum(p) ? ",_rowid_ " : ",rbu_rowid";
+ }
+ p->rc = prepareFreeAndCollectError(p->dbRbu, &pIter->pSelect, pz,
+ sqlite3_mprintf(
+ "SELECT %s,%s rbu_control%s FROM '%q'%s",
+ zCollist,
+ (rbuIsVacuum(p) ? "0 AS " : ""),
+ zRbuRowid,
+ pIter->zDataTbl, zLimit
+ )
+ );
+ }
+
+ sqlite3_free(zWhere);
+ sqlite3_free(zOldlist);
+ sqlite3_free(zNewlist);
+ sqlite3_free(zBindings);
+ }
+ sqlite3_free(zCollist);
+ sqlite3_free(zLimit);
+ }
+
+ return p->rc;
+}
+
+/*
+** Set output variable *ppStmt to point to an UPDATE statement that may
+** be used to update the imposter table for the main table b-tree of the
+** table object that pIter currently points to, assuming that the
+** rbu_control column of the data_xyz table contains zMask.
+**
+** If the zMask string does not specify any columns to update, then this
+** is not an error. Output variable *ppStmt is set to NULL in this case.
+*/
+static int rbuGetUpdateStmt(
+ sqlite3rbu *p, /* RBU handle */
+ RbuObjIter *pIter, /* Object iterator */
+ const char *zMask, /* rbu_control value ('x.x.') */
+ sqlite3_stmt **ppStmt /* OUT: UPDATE statement handle */
+){
+ RbuUpdateStmt **pp;
+ RbuUpdateStmt *pUp = 0;
+ int nUp = 0;
+
+ /* In case an error occurs */
+ *ppStmt = 0;
+
+ /* Search for an existing statement. If one is found, shift it to the front
+ ** of the LRU queue and return immediately. Otherwise, leave nUp pointing
+ ** to the number of statements currently in the cache and pUp to the
+ ** last object in the list. */
+ for(pp=&pIter->pRbuUpdate; *pp; pp=&((*pp)->pNext)){
+ pUp = *pp;
+ if( strcmp(pUp->zMask, zMask)==0 ){
+ *pp = pUp->pNext;
+ pUp->pNext = pIter->pRbuUpdate;
+ pIter->pRbuUpdate = pUp;
+ *ppStmt = pUp->pUpdate;
+ return SQLITE_OK;
+ }
+ nUp++;
+ }
+ assert( pUp==0 || pUp->pNext==0 );
+
+ if( nUp>=SQLITE_RBU_UPDATE_CACHESIZE ){
+ for(pp=&pIter->pRbuUpdate; *pp!=pUp; pp=&((*pp)->pNext));
+ *pp = 0;
+ sqlite3_finalize(pUp->pUpdate);
+ pUp->pUpdate = 0;
+ }else{
+ pUp = (RbuUpdateStmt*)rbuMalloc(p, sizeof(RbuUpdateStmt)+pIter->nTblCol+1);
+ }
+
+ if( pUp ){
+ char *zWhere = rbuObjIterGetWhere(p, pIter);
+ char *zSet = rbuObjIterGetSetlist(p, pIter, zMask);
+ char *zUpdate = 0;
+
+ pUp->zMask = (char*)&pUp[1];
+ memcpy(pUp->zMask, zMask, pIter->nTblCol);
+ pUp->pNext = pIter->pRbuUpdate;
+ pIter->pRbuUpdate = pUp;
+
+ if( zSet ){
+ const char *zPrefix = "";
+
+ if( pIter->eType!=RBU_PK_VTAB ) zPrefix = "rbu_imp_";
+ zUpdate = sqlite3_mprintf("UPDATE \"%s%w\" SET %s WHERE %s",
+ zPrefix, pIter->zTbl, zSet, zWhere
+ );
+ p->rc = prepareFreeAndCollectError(
+ p->dbMain, &pUp->pUpdate, &p->zErrmsg, zUpdate
+ );
+ *ppStmt = pUp->pUpdate;
+ }
+ sqlite3_free(zWhere);
+ sqlite3_free(zSet);
+ }
+
+ return p->rc;
+}
+
+static sqlite3 *rbuOpenDbhandle(
+ sqlite3rbu *p,
+ const char *zName,
+ int bUseVfs
+){
+ sqlite3 *db = 0;
+ if( p->rc==SQLITE_OK ){
+ const int flags = SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE|SQLITE_OPEN_URI;
+ p->rc = sqlite3_open_v2(zName, &db, flags, bUseVfs ? p->zVfsName : 0);
+ if( p->rc ){
+ p->zErrmsg = sqlite3_mprintf("%s", sqlite3_errmsg(db));
+ sqlite3_close(db);
+ db = 0;
+ }
+ }
+ return db;
+}
+
+/*
+** Free an RbuState object allocated by rbuLoadState().
+*/
+static void rbuFreeState(RbuState *p){
+ if( p ){
+ sqlite3_free(p->zTbl);
+ sqlite3_free(p->zIdx);
+ sqlite3_free(p);
+ }
+}
+
+/*
+** Allocate an RbuState object and load the contents of the rbu_state
+** table into it. Return a pointer to the new object. It is the
+** responsibility of the caller to eventually free the object using
+** sqlite3_free().
+**
+** If an error occurs, leave an error code and message in the rbu handle
+** and return NULL.
+*/
+static RbuState *rbuLoadState(sqlite3rbu *p){
+ RbuState *pRet = 0;
+ sqlite3_stmt *pStmt = 0;
+ int rc;
+ int rc2;
+
+ pRet = (RbuState*)rbuMalloc(p, sizeof(RbuState));
+ if( pRet==0 ) return 0;
+
+ rc = prepareFreeAndCollectError(p->dbRbu, &pStmt, &p->zErrmsg,
+ sqlite3_mprintf("SELECT k, v FROM %s.rbu_state", p->zStateDb)
+ );
+ while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){
+ switch( sqlite3_column_int(pStmt, 0) ){
+ case RBU_STATE_STAGE:
+ pRet->eStage = sqlite3_column_int(pStmt, 1);
+ if( pRet->eStage!=RBU_STAGE_OAL
+ && pRet->eStage!=RBU_STAGE_MOVE
+ && pRet->eStage!=RBU_STAGE_CKPT
+ ){
+ p->rc = SQLITE_CORRUPT;
+ }
+ break;
+
+ case RBU_STATE_TBL:
+ pRet->zTbl = rbuStrndup((char*)sqlite3_column_text(pStmt, 1), &rc);
+ break;
+
+ case RBU_STATE_IDX:
+ pRet->zIdx = rbuStrndup((char*)sqlite3_column_text(pStmt, 1), &rc);
+ break;
+
+ case RBU_STATE_ROW:
+ pRet->nRow = sqlite3_column_int(pStmt, 1);
+ break;
+
+ case RBU_STATE_PROGRESS:
+ pRet->nProgress = sqlite3_column_int64(pStmt, 1);
+ break;
+
+ case RBU_STATE_CKPT:
+ pRet->iWalCksum = sqlite3_column_int64(pStmt, 1);
+ break;
+
+ case RBU_STATE_COOKIE:
+ pRet->iCookie = (u32)sqlite3_column_int64(pStmt, 1);
+ break;
+
+ case RBU_STATE_OALSZ:
+ pRet->iOalSz = (u32)sqlite3_column_int64(pStmt, 1);
+ break;
+
+ case RBU_STATE_PHASEONESTEP:
+ pRet->nPhaseOneStep = sqlite3_column_int64(pStmt, 1);
+ break;
+
+ default:
+ rc = SQLITE_CORRUPT;
+ break;
+ }
+ }
+ rc2 = sqlite3_finalize(pStmt);
+ if( rc==SQLITE_OK ) rc = rc2;
+
+ p->rc = rc;
+ return pRet;
+}
+
+
+/*
+** Open the database handle and attach the RBU database as "rbu". If an
+** error occurs, leave an error code and message in the RBU handle.
+*/
+static void rbuOpenDatabase(sqlite3rbu *p){
+ assert( p->rc==SQLITE_OK );
+ assert( p->dbMain==0 && p->dbRbu==0 );
+ assert( rbuIsVacuum(p) || p->zTarget!=0 );
+
+ /* Open the RBU database */
+ p->dbRbu = rbuOpenDbhandle(p, p->zRbu, 1);
+
+ if( p->rc==SQLITE_OK && rbuIsVacuum(p) ){
+ sqlite3_file_control(p->dbRbu, "main", SQLITE_FCNTL_RBUCNT, (void*)p);
+ }
+
+ /* If using separate RBU and state databases, attach the state database to
+ ** the RBU db handle now. */
+ if( p->zState ){
+ rbuMPrintfExec(p, p->dbRbu, "ATTACH %Q AS stat", p->zState);
+ memcpy(p->zStateDb, "stat", 4);
+ }else{
+ memcpy(p->zStateDb, "main", 4);
+ }
+
+#if 0
+ if( p->rc==SQLITE_OK && rbuIsVacuum(p) ){
+ p->rc = sqlite3_exec(p->dbRbu, "BEGIN", 0, 0, 0);
+ }
+#endif
+
+ /* If it has not already been created, create the rbu_state table */
+ rbuMPrintfExec(p, p->dbRbu, RBU_CREATE_STATE, p->zStateDb);
+
+#if 0
+ if( rbuIsVacuum(p) ){
+ if( p->rc==SQLITE_OK ){
+ int rc2;
+ int bOk = 0;
+ sqlite3_stmt *pCnt = 0;
+ p->rc = prepareAndCollectError(p->dbRbu, &pCnt, &p->zErrmsg,
+ "SELECT count(*) FROM stat.sqlite_master"
+ );
+ if( p->rc==SQLITE_OK
+ && sqlite3_step(pCnt)==SQLITE_ROW
+ && 1==sqlite3_column_int(pCnt, 0)
+ ){
+ bOk = 1;
+ }
+ rc2 = sqlite3_finalize(pCnt);
+ if( p->rc==SQLITE_OK ) p->rc = rc2;
+
+ if( p->rc==SQLITE_OK && bOk==0 ){
+ p->rc = SQLITE_ERROR;
+ p->zErrmsg = sqlite3_mprintf("invalid state database");
+ }
+
+ if( p->rc==SQLITE_OK ){
+ p->rc = sqlite3_exec(p->dbRbu, "COMMIT", 0, 0, 0);
+ }
+ }
+ }
+#endif
+
+ if( p->rc==SQLITE_OK && rbuIsVacuum(p) ){
+ int bOpen = 0;
+ int rc;
+ p->nRbu = 0;
+ p->pRbuFd = 0;
+ rc = sqlite3_file_control(p->dbRbu, "main", SQLITE_FCNTL_RBUCNT, (void*)p);
+ if( rc!=SQLITE_NOTFOUND ) p->rc = rc;
+ if( p->eStage>=RBU_STAGE_MOVE ){
+ bOpen = 1;
+ }else{
+ RbuState *pState = rbuLoadState(p);
+ if( pState ){
+ bOpen = (pState->eStage>RBU_STAGE_MOVE);
+ rbuFreeState(pState);
+ }
+ }
+ if( bOpen ) p->dbMain = rbuOpenDbhandle(p, p->zRbu, p->nRbu<=1);
+ }
+
+ p->eStage = 0;
+ if( p->rc==SQLITE_OK && p->dbMain==0 ){
+ if( !rbuIsVacuum(p) ){
+ p->dbMain = rbuOpenDbhandle(p, p->zTarget, 1);
+ }else if( p->pRbuFd->pWalFd ){
+ p->rc = SQLITE_ERROR;
+ p->zErrmsg = sqlite3_mprintf("cannot vacuum wal mode database");
+ }else{
+ char *zTarget;
+ char *zExtra = 0;
+ if( strlen(p->zRbu)>=5 && 0==memcmp("file:", p->zRbu, 5) ){
+ zExtra = &p->zRbu[5];
+ while( *zExtra ){
+ if( *zExtra++=='?' ) break;
+ }
+ if( *zExtra=='\0' ) zExtra = 0;
+ }
+
+ zTarget = sqlite3_mprintf("file:%s-vacuum?rbu_memory=1%s%s",
+ sqlite3_db_filename(p->dbRbu, "main"),
+ (zExtra==0 ? "" : "&"), (zExtra==0 ? "" : zExtra)
+ );
+
+ if( zTarget==0 ){
+ p->rc = SQLITE_NOMEM;
+ return;
+ }
+ p->dbMain = rbuOpenDbhandle(p, zTarget, p->nRbu<=1);
+ sqlite3_free(zTarget);
+ }
+ }
+
+ if( p->rc==SQLITE_OK ){
+ p->rc = sqlite3_create_function(p->dbMain,
+ "rbu_tmp_insert", -1, SQLITE_UTF8, (void*)p, rbuTmpInsertFunc, 0, 0
+ );
+ }
+
+ if( p->rc==SQLITE_OK ){
+ p->rc = sqlite3_create_function(p->dbMain,
+ "rbu_fossil_delta", 2, SQLITE_UTF8, 0, rbuFossilDeltaFunc, 0, 0
+ );
+ }
+
+ if( p->rc==SQLITE_OK ){
+ p->rc = sqlite3_create_function(p->dbRbu,
+ "rbu_target_name", -1, SQLITE_UTF8, (void*)p, rbuTargetNameFunc, 0, 0
+ );
+ }
+
+ if( p->rc==SQLITE_OK ){
+ p->rc = sqlite3_file_control(p->dbMain, "main", SQLITE_FCNTL_RBU, (void*)p);
+ }
+ rbuMPrintfExec(p, p->dbMain, "SELECT * FROM sqlite_master");
+
+ /* Mark the database file just opened as an RBU target database. If
+ ** this call returns SQLITE_NOTFOUND, then the RBU vfs is not in use.
+ ** This is an error. */
+ if( p->rc==SQLITE_OK ){
+ p->rc = sqlite3_file_control(p->dbMain, "main", SQLITE_FCNTL_RBU, (void*)p);
+ }
+
+ if( p->rc==SQLITE_NOTFOUND ){
+ p->rc = SQLITE_ERROR;
+ p->zErrmsg = sqlite3_mprintf("rbu vfs not found");
+ }
+}
+
+/*
+** This routine is a copy of the sqlite3FileSuffix3() routine from the core.
+** It is a no-op unless SQLITE_ENABLE_8_3_NAMES is defined.
+**
+** If SQLITE_ENABLE_8_3_NAMES is set at compile-time and if the database
+** filename in zBaseFilename is a URI with the "8_3_names=1" parameter and
+** if filename in z[] has a suffix (a.k.a. "extension") that is longer than
+** three characters, then shorten the suffix on z[] to be the last three
+** characters of the original suffix.
+**
+** If SQLITE_ENABLE_8_3_NAMES is set to 2 at compile-time, then always
+** do the suffix shortening regardless of URI parameter.
+**
+** Examples:
+**
+** test.db-journal => test.nal
+** test.db-wal => test.wal
+** test.db-shm => test.shm
+** test.db-mj7f3319fa => test.9fa
+*/
+static void rbuFileSuffix3(const char *zBase, char *z){
+#ifdef SQLITE_ENABLE_8_3_NAMES
+#if SQLITE_ENABLE_8_3_NAMES<2
+ if( sqlite3_uri_boolean(zBase, "8_3_names", 0) )
+#endif
+ {
+ int i, sz;
+ sz = sqlite3Strlen30(z);
+ for(i=sz-1; i>0 && z[i]!='/' && z[i]!='.'; i--){}
+ if( z[i]=='.' && ALWAYS(sz>i+4) ) memmove(&z[i+1], &z[sz-3], 4);
+ }
+#endif
+}
+
+/*
+** Return the current wal-index header checksum for the target database
+** as a 64-bit integer.
+**
+** The checksum is store in the first page of xShmMap memory as an 8-byte
+** blob starting at byte offset 40.
+*/
+static i64 rbuShmChecksum(sqlite3rbu *p){
+ i64 iRet = 0;
+ if( p->rc==SQLITE_OK ){
+ sqlite3_file *pDb = p->pTargetFd->pReal;
+ u32 volatile *ptr;
+ p->rc = pDb->pMethods->xShmMap(pDb, 0, 32*1024, 0, (void volatile**)&ptr);
+ if( p->rc==SQLITE_OK ){
+ iRet = ((i64)ptr[10] << 32) + ptr[11];
+ }
+ }
+ return iRet;
+}
+
+/*
+** This function is called as part of initializing or reinitializing an
+** incremental checkpoint.
+**
+** It populates the sqlite3rbu.aFrame[] array with the set of
+** (wal frame -> db page) copy operations required to checkpoint the
+** current wal file, and obtains the set of shm locks required to safely
+** perform the copy operations directly on the file-system.
+**
+** If argument pState is not NULL, then the incremental checkpoint is
+** being resumed. In this case, if the checksum of the wal-index-header
+** following recovery is not the same as the checksum saved in the RbuState
+** object, then the rbu handle is set to DONE state. This occurs if some
+** other client appends a transaction to the wal file in the middle of
+** an incremental checkpoint.
+*/
+static void rbuSetupCheckpoint(sqlite3rbu *p, RbuState *pState){
+
+ /* If pState is NULL, then the wal file may not have been opened and
+ ** recovered. Running a read-statement here to ensure that doing so
+ ** does not interfere with the "capture" process below. */
+ if( pState==0 ){
+ p->eStage = 0;
+ if( p->rc==SQLITE_OK ){
+ p->rc = sqlite3_exec(p->dbMain, "SELECT * FROM sqlite_master", 0, 0, 0);
+ }
+ }
+
+ /* Assuming no error has occurred, run a "restart" checkpoint with the
+ ** sqlite3rbu.eStage variable set to CAPTURE. This turns on the following
+ ** special behaviour in the rbu VFS:
+ **
+ ** * If the exclusive shm WRITER or READ0 lock cannot be obtained,
+ ** the checkpoint fails with SQLITE_BUSY (normally SQLite would
+ ** proceed with running a passive checkpoint instead of failing).
+ **
+ ** * Attempts to read from the *-wal file or write to the database file
+ ** do not perform any IO. Instead, the frame/page combinations that
+ ** would be read/written are recorded in the sqlite3rbu.aFrame[]
+ ** array.
+ **
+ ** * Calls to xShmLock(UNLOCK) to release the exclusive shm WRITER,
+ ** READ0 and CHECKPOINT locks taken as part of the checkpoint are
+ ** no-ops. These locks will not be released until the connection
+ ** is closed.
+ **
+ ** * Attempting to xSync() the database file causes an SQLITE_INTERNAL
+ ** error.
+ **
+ ** As a result, unless an error (i.e. OOM or SQLITE_BUSY) occurs, the
+ ** checkpoint below fails with SQLITE_INTERNAL, and leaves the aFrame[]
+ ** array populated with a set of (frame -> page) mappings. Because the
+ ** WRITER, CHECKPOINT and READ0 locks are still held, it is safe to copy
+ ** data from the wal file into the database file according to the
+ ** contents of aFrame[].
+ */
+ if( p->rc==SQLITE_OK ){
+ int rc2;
+ p->eStage = RBU_STAGE_CAPTURE;
+ rc2 = sqlite3_exec(p->dbMain, "PRAGMA main.wal_checkpoint=restart", 0, 0,0);
+ if( rc2!=SQLITE_INTERNAL ) p->rc = rc2;
+ }
+
+ if( p->rc==SQLITE_OK ){
+ p->eStage = RBU_STAGE_CKPT;
+ p->nStep = (pState ? pState->nRow : 0);
+ p->aBuf = rbuMalloc(p, p->pgsz);
+ p->iWalCksum = rbuShmChecksum(p);
+ }
+
+ if( p->rc==SQLITE_OK && pState && pState->iWalCksum!=p->iWalCksum ){
+ p->rc = SQLITE_DONE;
+ p->eStage = RBU_STAGE_DONE;
+ }
+}
+
+/*
+** Called when iAmt bytes are read from offset iOff of the wal file while
+** the rbu object is in capture mode. Record the frame number of the frame
+** being read in the aFrame[] array.
+*/
+static int rbuCaptureWalRead(sqlite3rbu *pRbu, i64 iOff, int iAmt){
+ const u32 mReq = (1<<WAL_LOCK_WRITE)|(1<<WAL_LOCK_CKPT)|(1<<WAL_LOCK_READ0);
+ u32 iFrame;
+
+ if( pRbu->mLock!=mReq ){
+ pRbu->rc = SQLITE_BUSY;
+ return SQLITE_INTERNAL;
+ }
+
+ pRbu->pgsz = iAmt;
+ if( pRbu->nFrame==pRbu->nFrameAlloc ){
+ int nNew = (pRbu->nFrameAlloc ? pRbu->nFrameAlloc : 64) * 2;
+ RbuFrame *aNew;
+ aNew = (RbuFrame*)sqlite3_realloc64(pRbu->aFrame, nNew * sizeof(RbuFrame));
+ if( aNew==0 ) return SQLITE_NOMEM;
+ pRbu->aFrame = aNew;
+ pRbu->nFrameAlloc = nNew;
+ }
+
+ iFrame = (u32)((iOff-32) / (i64)(iAmt+24)) + 1;
+ if( pRbu->iMaxFrame<iFrame ) pRbu->iMaxFrame = iFrame;
+ pRbu->aFrame[pRbu->nFrame].iWalFrame = iFrame;
+ pRbu->aFrame[pRbu->nFrame].iDbPage = 0;
+ pRbu->nFrame++;
+ return SQLITE_OK;
+}
+
+/*
+** Called when a page of data is written to offset iOff of the database
+** file while the rbu handle is in capture mode. Record the page number
+** of the page being written in the aFrame[] array.
+*/
+static int rbuCaptureDbWrite(sqlite3rbu *pRbu, i64 iOff){
+ pRbu->aFrame[pRbu->nFrame-1].iDbPage = (u32)(iOff / pRbu->pgsz) + 1;
+ return SQLITE_OK;
+}
+
+/*
+** This is called as part of an incremental checkpoint operation. Copy
+** a single frame of data from the wal file into the database file, as
+** indicated by the RbuFrame object.
+*/
+static void rbuCheckpointFrame(sqlite3rbu *p, RbuFrame *pFrame){
+ sqlite3_file *pWal = p->pTargetFd->pWalFd->pReal;
+ sqlite3_file *pDb = p->pTargetFd->pReal;
+ i64 iOff;
+
+ assert( p->rc==SQLITE_OK );
+ iOff = (i64)(pFrame->iWalFrame-1) * (p->pgsz + 24) + 32 + 24;
+ p->rc = pWal->pMethods->xRead(pWal, p->aBuf, p->pgsz, iOff);
+ if( p->rc ) return;
+
+ iOff = (i64)(pFrame->iDbPage-1) * p->pgsz;
+ p->rc = pDb->pMethods->xWrite(pDb, p->aBuf, p->pgsz, iOff);
+}
+
+
+/*
+** Take an EXCLUSIVE lock on the database file.
+*/
+static void rbuLockDatabase(sqlite3rbu *p){
+ sqlite3_file *pReal = p->pTargetFd->pReal;
+ assert( p->rc==SQLITE_OK );
+ p->rc = pReal->pMethods->xLock(pReal, SQLITE_LOCK_SHARED);
+ if( p->rc==SQLITE_OK ){
+ p->rc = pReal->pMethods->xLock(pReal, SQLITE_LOCK_EXCLUSIVE);
+ }
+}
+
+#if defined(_WIN32_WCE)
+static LPWSTR rbuWinUtf8ToUnicode(const char *zFilename){
+ int nChar;
+ LPWSTR zWideFilename;
+
+ nChar = MultiByteToWideChar(CP_UTF8, 0, zFilename, -1, NULL, 0);
+ if( nChar==0 ){
+ return 0;
+ }
+ zWideFilename = sqlite3_malloc64( nChar*sizeof(zWideFilename[0]) );
+ if( zWideFilename==0 ){
+ return 0;
+ }
+ memset(zWideFilename, 0, nChar*sizeof(zWideFilename[0]));
+ nChar = MultiByteToWideChar(CP_UTF8, 0, zFilename, -1, zWideFilename,
+ nChar);
+ if( nChar==0 ){
+ sqlite3_free(zWideFilename);
+ zWideFilename = 0;
+ }
+ return zWideFilename;
+}
+#endif
+
+/*
+** The RBU handle is currently in RBU_STAGE_OAL state, with a SHARED lock
+** on the database file. This proc moves the *-oal file to the *-wal path,
+** then reopens the database file (this time in vanilla, non-oal, WAL mode).
+** If an error occurs, leave an error code and error message in the rbu
+** handle.
+*/
+static void rbuMoveOalFile(sqlite3rbu *p){
+ const char *zBase = sqlite3_db_filename(p->dbMain, "main");
+ const char *zMove = zBase;
+ char *zOal;
+ char *zWal;
+
+ if( rbuIsVacuum(p) ){
+ zMove = sqlite3_db_filename(p->dbRbu, "main");
+ }
+ zOal = sqlite3_mprintf("%s-oal", zMove);
+ zWal = sqlite3_mprintf("%s-wal", zMove);
+
+ assert( p->eStage==RBU_STAGE_MOVE );
+ assert( p->rc==SQLITE_OK && p->zErrmsg==0 );
+ if( zWal==0 || zOal==0 ){
+ p->rc = SQLITE_NOMEM;
+ }else{
+ /* Move the *-oal file to *-wal. At this point connection p->db is
+ ** holding a SHARED lock on the target database file (because it is
+ ** in WAL mode). So no other connection may be writing the db.
+ **
+ ** In order to ensure that there are no database readers, an EXCLUSIVE
+ ** lock is obtained here before the *-oal is moved to *-wal.
+ */
+ rbuLockDatabase(p);
+ if( p->rc==SQLITE_OK ){
+ rbuFileSuffix3(zBase, zWal);
+ rbuFileSuffix3(zBase, zOal);
+
+ /* Re-open the databases. */
+ rbuObjIterFinalize(&p->objiter);
+ sqlite3_close(p->dbRbu);
+ sqlite3_close(p->dbMain);
+ p->dbMain = 0;
+ p->dbRbu = 0;
+
+#if defined(_WIN32_WCE)
+ {
+ LPWSTR zWideOal;
+ LPWSTR zWideWal;
+
+ zWideOal = rbuWinUtf8ToUnicode(zOal);
+ if( zWideOal ){
+ zWideWal = rbuWinUtf8ToUnicode(zWal);
+ if( zWideWal ){
+ if( MoveFileW(zWideOal, zWideWal) ){
+ p->rc = SQLITE_OK;
+ }else{
+ p->rc = SQLITE_IOERR;
+ }
+ sqlite3_free(zWideWal);
+ }else{
+ p->rc = SQLITE_IOERR_NOMEM;
+ }
+ sqlite3_free(zWideOal);
+ }else{
+ p->rc = SQLITE_IOERR_NOMEM;
+ }
+ }
+#else
+ p->rc = rename(zOal, zWal) ? SQLITE_IOERR : SQLITE_OK;
+#endif
+
+ if( p->rc==SQLITE_OK ){
+ rbuOpenDatabase(p);
+ rbuSetupCheckpoint(p, 0);
+ }
+ }
+ }
+
+ sqlite3_free(zWal);
+ sqlite3_free(zOal);
+}
+
+/*
+** The SELECT statement iterating through the keys for the current object
+** (p->objiter.pSelect) currently points to a valid row. This function
+** determines the type of operation requested by this row and returns
+** one of the following values to indicate the result:
+**
+** * RBU_INSERT
+** * RBU_DELETE
+** * RBU_IDX_DELETE
+** * RBU_UPDATE
+**
+** If RBU_UPDATE is returned, then output variable *pzMask is set to
+** point to the text value indicating the columns to update.
+**
+** If the rbu_control field contains an invalid value, an error code and
+** message are left in the RBU handle and zero returned.
+*/
+static int rbuStepType(sqlite3rbu *p, const char **pzMask){
+ int iCol = p->objiter.nCol; /* Index of rbu_control column */
+ int res = 0; /* Return value */
+
+ switch( sqlite3_column_type(p->objiter.pSelect, iCol) ){
+ case SQLITE_INTEGER: {
+ int iVal = sqlite3_column_int(p->objiter.pSelect, iCol);
+ switch( iVal ){
+ case 0: res = RBU_INSERT; break;
+ case 1: res = RBU_DELETE; break;
+ case 2: res = RBU_REPLACE; break;
+ case 3: res = RBU_IDX_DELETE; break;
+ case 4: res = RBU_IDX_INSERT; break;
+ }
+ break;
+ }
+
+ case SQLITE_TEXT: {
+ const unsigned char *z = sqlite3_column_text(p->objiter.pSelect, iCol);
+ if( z==0 ){
+ p->rc = SQLITE_NOMEM;
+ }else{
+ *pzMask = (const char*)z;
+ }
+ res = RBU_UPDATE;
+
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ if( res==0 ){
+ rbuBadControlError(p);
+ }
+ return res;
+}
+
+#ifdef SQLITE_DEBUG
+/*
+** Assert that column iCol of statement pStmt is named zName.
+*/
+static void assertColumnName(sqlite3_stmt *pStmt, int iCol, const char *zName){
+ const char *zCol = sqlite3_column_name(pStmt, iCol);
+ assert( 0==sqlite3_stricmp(zName, zCol) );
+}
+#else
+# define assertColumnName(x,y,z)
+#endif
+
+/*
+** Argument eType must be one of RBU_INSERT, RBU_DELETE, RBU_IDX_INSERT or
+** RBU_IDX_DELETE. This function performs the work of a single
+** sqlite3rbu_step() call for the type of operation specified by eType.
+*/
+static void rbuStepOneOp(sqlite3rbu *p, int eType){
+ RbuObjIter *pIter = &p->objiter;
+ sqlite3_value *pVal;
+ sqlite3_stmt *pWriter;
+ int i;
+
+ assert( p->rc==SQLITE_OK );
+ assert( eType!=RBU_DELETE || pIter->zIdx==0 );
+ assert( eType==RBU_DELETE || eType==RBU_IDX_DELETE
+ || eType==RBU_INSERT || eType==RBU_IDX_INSERT
+ );
+
+ /* If this is a delete, decrement nPhaseOneStep by nIndex. If the DELETE
+ ** statement below does actually delete a row, nPhaseOneStep will be
+ ** incremented by the same amount when SQL function rbu_tmp_insert()
+ ** is invoked by the trigger. */
+ if( eType==RBU_DELETE ){
+ p->nPhaseOneStep -= p->objiter.nIndex;
+ }
+
+ if( eType==RBU_IDX_DELETE || eType==RBU_DELETE ){
+ pWriter = pIter->pDelete;
+ }else{
+ pWriter = pIter->pInsert;
+ }
+
+ for(i=0; i<pIter->nCol; i++){
+ /* If this is an INSERT into a table b-tree and the table has an
+ ** explicit INTEGER PRIMARY KEY, check that this is not an attempt
+ ** to write a NULL into the IPK column. That is not permitted. */
+ if( eType==RBU_INSERT
+ && pIter->zIdx==0 && pIter->eType==RBU_PK_IPK && pIter->abTblPk[i]
+ && sqlite3_column_type(pIter->pSelect, i)==SQLITE_NULL
+ ){
+ p->rc = SQLITE_MISMATCH;
+ p->zErrmsg = sqlite3_mprintf("datatype mismatch");
+ return;
+ }
+
+ if( eType==RBU_DELETE && pIter->abTblPk[i]==0 ){
+ continue;
+ }
+
+ pVal = sqlite3_column_value(pIter->pSelect, i);
+ p->rc = sqlite3_bind_value(pWriter, i+1, pVal);
+ if( p->rc ) return;
+ }
+ if( pIter->zIdx==0 ){
+ if( pIter->eType==RBU_PK_VTAB
+ || pIter->eType==RBU_PK_NONE
+ || (pIter->eType==RBU_PK_EXTERNAL && rbuIsVacuum(p))
+ ){
+ /* For a virtual table, or a table with no primary key, the
+ ** SELECT statement is:
+ **
+ ** SELECT <cols>, rbu_control, rbu_rowid FROM ....
+ **
+ ** Hence column_value(pIter->nCol+1).
+ */
+ assertColumnName(pIter->pSelect, pIter->nCol+1,
+ rbuIsVacuum(p) ? "rowid" : "rbu_rowid"
+ );
+ pVal = sqlite3_column_value(pIter->pSelect, pIter->nCol+1);
+ p->rc = sqlite3_bind_value(pWriter, pIter->nCol+1, pVal);
+ }
+ }
+ if( p->rc==SQLITE_OK ){
+ sqlite3_step(pWriter);
+ p->rc = resetAndCollectError(pWriter, &p->zErrmsg);
+ }
+}
+
+/*
+** This function does the work for an sqlite3rbu_step() call.
+**
+** The object-iterator (p->objiter) currently points to a valid object,
+** and the input cursor (p->objiter.pSelect) currently points to a valid
+** input row. Perform whatever processing is required and return.
+**
+** If no error occurs, SQLITE_OK is returned. Otherwise, an error code
+** and message is left in the RBU handle and a copy of the error code
+** returned.
+*/
+static int rbuStep(sqlite3rbu *p){
+ RbuObjIter *pIter = &p->objiter;
+ const char *zMask = 0;
+ int eType = rbuStepType(p, &zMask);
+
+ if( eType ){
+ assert( eType==RBU_INSERT || eType==RBU_DELETE
+ || eType==RBU_REPLACE || eType==RBU_IDX_DELETE
+ || eType==RBU_IDX_INSERT || eType==RBU_UPDATE
+ );
+ assert( eType!=RBU_UPDATE || pIter->zIdx==0 );
+
+ if( pIter->zIdx==0 && (eType==RBU_IDX_DELETE || eType==RBU_IDX_INSERT) ){
+ rbuBadControlError(p);
+ }
+ else if( eType==RBU_REPLACE ){
+ if( pIter->zIdx==0 ){
+ p->nPhaseOneStep += p->objiter.nIndex;
+ rbuStepOneOp(p, RBU_DELETE);
+ }
+ if( p->rc==SQLITE_OK ) rbuStepOneOp(p, RBU_INSERT);
+ }
+ else if( eType!=RBU_UPDATE ){
+ rbuStepOneOp(p, eType);
+ }
+ else{
+ sqlite3_value *pVal;
+ sqlite3_stmt *pUpdate = 0;
+ assert( eType==RBU_UPDATE );
+ p->nPhaseOneStep -= p->objiter.nIndex;
+ rbuGetUpdateStmt(p, pIter, zMask, &pUpdate);
+ if( pUpdate ){
+ int i;
+ for(i=0; p->rc==SQLITE_OK && i<pIter->nCol; i++){
+ char c = zMask[pIter->aiSrcOrder[i]];
+ pVal = sqlite3_column_value(pIter->pSelect, i);
+ if( pIter->abTblPk[i] || c!='.' ){
+ p->rc = sqlite3_bind_value(pUpdate, i+1, pVal);
+ }
+ }
+ if( p->rc==SQLITE_OK
+ && (pIter->eType==RBU_PK_VTAB || pIter->eType==RBU_PK_NONE)
+ ){
+ /* Bind the rbu_rowid value to column _rowid_ */
+ assertColumnName(pIter->pSelect, pIter->nCol+1, "rbu_rowid");
+ pVal = sqlite3_column_value(pIter->pSelect, pIter->nCol+1);
+ p->rc = sqlite3_bind_value(pUpdate, pIter->nCol+1, pVal);
+ }
+ if( p->rc==SQLITE_OK ){
+ sqlite3_step(pUpdate);
+ p->rc = resetAndCollectError(pUpdate, &p->zErrmsg);
+ }
+ }
+ }
+ }
+ return p->rc;
+}
+
+/*
+** Increment the schema cookie of the main database opened by p->dbMain.
+**
+** Or, if this is an RBU vacuum, set the schema cookie of the main db
+** opened by p->dbMain to one more than the schema cookie of the main
+** db opened by p->dbRbu.
+*/
+static void rbuIncrSchemaCookie(sqlite3rbu *p){
+ if( p->rc==SQLITE_OK ){
+ sqlite3 *dbread = (rbuIsVacuum(p) ? p->dbRbu : p->dbMain);
+ int iCookie = 1000000;
+ sqlite3_stmt *pStmt;
+
+ p->rc = prepareAndCollectError(dbread, &pStmt, &p->zErrmsg,
+ "PRAGMA schema_version"
+ );
+ if( p->rc==SQLITE_OK ){
+ /* Coverage: it may be that this sqlite3_step() cannot fail. There
+ ** is already a transaction open, so the prepared statement cannot
+ ** throw an SQLITE_SCHEMA exception. The only database page the
+ ** statement reads is page 1, which is guaranteed to be in the cache.
+ ** And no memory allocations are required. */
+ if( SQLITE_ROW==sqlite3_step(pStmt) ){
+ iCookie = sqlite3_column_int(pStmt, 0);
+ }
+ rbuFinalize(p, pStmt);
+ }
+ if( p->rc==SQLITE_OK ){
+ rbuMPrintfExec(p, p->dbMain, "PRAGMA schema_version = %d", iCookie+1);
+ }
+ }
+}
+
+/*
+** Update the contents of the rbu_state table within the rbu database. The
+** value stored in the RBU_STATE_STAGE column is eStage. All other values
+** are determined by inspecting the rbu handle passed as the first argument.
+*/
+static void rbuSaveState(sqlite3rbu *p, int eStage){
+ if( p->rc==SQLITE_OK || p->rc==SQLITE_DONE ){
+ sqlite3_stmt *pInsert = 0;
+ rbu_file *pFd = (rbuIsVacuum(p) ? p->pRbuFd : p->pTargetFd);
+ int rc;
+
+ assert( p->zErrmsg==0 );
+ rc = prepareFreeAndCollectError(p->dbRbu, &pInsert, &p->zErrmsg,
+ sqlite3_mprintf(
+ "INSERT OR REPLACE INTO %s.rbu_state(k, v) VALUES "
+ "(%d, %d), "
+ "(%d, %Q), "
+ "(%d, %Q), "
+ "(%d, %d), "
+ "(%d, %d), "
+ "(%d, %lld), "
+ "(%d, %lld), "
+ "(%d, %lld), "
+ "(%d, %lld) ",
+ p->zStateDb,
+ RBU_STATE_STAGE, eStage,
+ RBU_STATE_TBL, p->objiter.zTbl,
+ RBU_STATE_IDX, p->objiter.zIdx,
+ RBU_STATE_ROW, p->nStep,
+ RBU_STATE_PROGRESS, p->nProgress,
+ RBU_STATE_CKPT, p->iWalCksum,
+ RBU_STATE_COOKIE, (i64)pFd->iCookie,
+ RBU_STATE_OALSZ, p->iOalSz,
+ RBU_STATE_PHASEONESTEP, p->nPhaseOneStep
+ )
+ );
+ assert( pInsert==0 || rc==SQLITE_OK );
+
+ if( rc==SQLITE_OK ){
+ sqlite3_step(pInsert);
+ rc = sqlite3_finalize(pInsert);
+ }
+ if( rc!=SQLITE_OK ) p->rc = rc;
+ }
+}
+
+
+/*
+** The second argument passed to this function is the name of a PRAGMA
+** setting - "page_size", "auto_vacuum", "user_version" or "application_id".
+** This function executes the following on sqlite3rbu.dbRbu:
+**
+** "PRAGMA main.$zPragma"
+**
+** where $zPragma is the string passed as the second argument, then
+** on sqlite3rbu.dbMain:
+**
+** "PRAGMA main.$zPragma = $val"
+**
+** where $val is the value returned by the first PRAGMA invocation.
+**
+** In short, it copies the value of the specified PRAGMA setting from
+** dbRbu to dbMain.
+*/
+static void rbuCopyPragma(sqlite3rbu *p, const char *zPragma){
+ if( p->rc==SQLITE_OK ){
+ sqlite3_stmt *pPragma = 0;
+ p->rc = prepareFreeAndCollectError(p->dbRbu, &pPragma, &p->zErrmsg,
+ sqlite3_mprintf("PRAGMA main.%s", zPragma)
+ );
+ if( p->rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pPragma) ){
+ p->rc = rbuMPrintfExec(p, p->dbMain, "PRAGMA main.%s = %d",
+ zPragma, sqlite3_column_int(pPragma, 0)
+ );
+ }
+ rbuFinalize(p, pPragma);
+ }
+}
+
+/*
+** The RBU handle passed as the only argument has just been opened and
+** the state database is empty. If this RBU handle was opened for an
+** RBU vacuum operation, create the schema in the target db.
+*/
+static void rbuCreateTargetSchema(sqlite3rbu *p){
+ sqlite3_stmt *pSql = 0;
+ sqlite3_stmt *pInsert = 0;
+
+ assert( rbuIsVacuum(p) );
+ p->rc = sqlite3_exec(p->dbMain, "PRAGMA writable_schema=1", 0,0, &p->zErrmsg);
+ if( p->rc==SQLITE_OK ){
+ p->rc = prepareAndCollectError(p->dbRbu, &pSql, &p->zErrmsg,
+ "SELECT sql FROM sqlite_master WHERE sql!='' AND rootpage!=0"
+ " AND name!='sqlite_sequence' "
+ " ORDER BY type DESC"
+ );
+ }
+
+ while( p->rc==SQLITE_OK && sqlite3_step(pSql)==SQLITE_ROW ){
+ const char *zSql = (const char*)sqlite3_column_text(pSql, 0);
+ p->rc = sqlite3_exec(p->dbMain, zSql, 0, 0, &p->zErrmsg);
+ }
+ rbuFinalize(p, pSql);
+ if( p->rc!=SQLITE_OK ) return;
+
+ if( p->rc==SQLITE_OK ){
+ p->rc = prepareAndCollectError(p->dbRbu, &pSql, &p->zErrmsg,
+ "SELECT * FROM sqlite_master WHERE rootpage=0 OR rootpage IS NULL"
+ );
+ }
+
+ if( p->rc==SQLITE_OK ){
+ p->rc = prepareAndCollectError(p->dbMain, &pInsert, &p->zErrmsg,
+ "INSERT INTO sqlite_master VALUES(?,?,?,?,?)"
+ );
+ }
+
+ while( p->rc==SQLITE_OK && sqlite3_step(pSql)==SQLITE_ROW ){
+ int i;
+ for(i=0; i<5; i++){
+ sqlite3_bind_value(pInsert, i+1, sqlite3_column_value(pSql, i));
+ }
+ sqlite3_step(pInsert);
+ p->rc = sqlite3_reset(pInsert);
+ }
+ if( p->rc==SQLITE_OK ){
+ p->rc = sqlite3_exec(p->dbMain, "PRAGMA writable_schema=0",0,0,&p->zErrmsg);
+ }
+
+ rbuFinalize(p, pSql);
+ rbuFinalize(p, pInsert);
+}
+
+/*
+** Step the RBU object.
+*/
+SQLITE_API int SQLITE_STDCALL sqlite3rbu_step(sqlite3rbu *p){
+ if( p ){
+ switch( p->eStage ){
+ case RBU_STAGE_OAL: {
+ RbuObjIter *pIter = &p->objiter;
+
+ /* If this is an RBU vacuum operation and the state table was empty
+ ** when this handle was opened, create the target database schema. */
+ if( rbuIsVacuum(p) && p->nProgress==0 && p->rc==SQLITE_OK ){
+ rbuCreateTargetSchema(p);
+ rbuCopyPragma(p, "user_version");
+ rbuCopyPragma(p, "application_id");
+ }
+
+ while( p->rc==SQLITE_OK && pIter->zTbl ){
+
+ if( pIter->bCleanup ){
+ /* Clean up the rbu_tmp_xxx table for the previous table. It
+ ** cannot be dropped as there are currently active SQL statements.
+ ** But the contents can be deleted. */
+ if( rbuIsVacuum(p)==0 && pIter->abIndexed ){
+ rbuMPrintfExec(p, p->dbRbu,
+ "DELETE FROM %s.'rbu_tmp_%q'", p->zStateDb, pIter->zDataTbl
+ );
+ }
+ }else{
+ rbuObjIterPrepareAll(p, pIter, 0);
+
+ /* Advance to the next row to process. */
+ if( p->rc==SQLITE_OK ){
+ int rc = sqlite3_step(pIter->pSelect);
+ if( rc==SQLITE_ROW ){
+ p->nProgress++;
+ p->nStep++;
+ return rbuStep(p);
+ }
+ p->rc = sqlite3_reset(pIter->pSelect);
+ p->nStep = 0;
+ }
+ }
+
+ rbuObjIterNext(p, pIter);
+ }
+
+ if( p->rc==SQLITE_OK ){
+ assert( pIter->zTbl==0 );
+ rbuSaveState(p, RBU_STAGE_MOVE);
+ rbuIncrSchemaCookie(p);
+ if( p->rc==SQLITE_OK ){
+ p->rc = sqlite3_exec(p->dbMain, "COMMIT", 0, 0, &p->zErrmsg);
+ }
+ if( p->rc==SQLITE_OK ){
+ p->rc = sqlite3_exec(p->dbRbu, "COMMIT", 0, 0, &p->zErrmsg);
+ }
+ p->eStage = RBU_STAGE_MOVE;
+ }
+ break;
+ }
+
+ case RBU_STAGE_MOVE: {
+ if( p->rc==SQLITE_OK ){
+ rbuMoveOalFile(p);
+ p->nProgress++;
+ }
+ break;
+ }
+
+ case RBU_STAGE_CKPT: {
+ if( p->rc==SQLITE_OK ){
+ if( p->nStep>=p->nFrame ){
+ sqlite3_file *pDb = p->pTargetFd->pReal;
+
+ /* Sync the db file */
+ p->rc = pDb->pMethods->xSync(pDb, SQLITE_SYNC_NORMAL);
+
+ /* Update nBackfill */
+ if( p->rc==SQLITE_OK ){
+ void volatile *ptr;
+ p->rc = pDb->pMethods->xShmMap(pDb, 0, 32*1024, 0, &ptr);
+ if( p->rc==SQLITE_OK ){
+ ((u32 volatile*)ptr)[24] = p->iMaxFrame;
+ }
+ }
+
+ if( p->rc==SQLITE_OK ){
+ p->eStage = RBU_STAGE_DONE;
+ p->rc = SQLITE_DONE;
+ }
+ }else{
+ RbuFrame *pFrame = &p->aFrame[p->nStep];
+ rbuCheckpointFrame(p, pFrame);
+ p->nStep++;
+ }
+ p->nProgress++;
+ }
+ break;
+ }
+
+ default:
+ break;
+ }
+ return p->rc;
+ }else{
+ return SQLITE_NOMEM;
+ }
+}
+
+/*
+** Compare strings z1 and z2, returning 0 if they are identical, or non-zero
+** otherwise. Either or both argument may be NULL. Two NULL values are
+** considered equal, and NULL is considered distinct from all other values.
+*/
+static int rbuStrCompare(const char *z1, const char *z2){
+ if( z1==0 && z2==0 ) return 0;
+ if( z1==0 || z2==0 ) return 1;
+ return (sqlite3_stricmp(z1, z2)!=0);
+}
+
+/*
+** This function is called as part of sqlite3rbu_open() when initializing
+** an rbu handle in OAL stage. If the rbu update has not started (i.e.
+** the rbu_state table was empty) it is a no-op. Otherwise, it arranges
+** things so that the next call to sqlite3rbu_step() continues on from
+** where the previous rbu handle left off.
+**
+** If an error occurs, an error code and error message are left in the
+** rbu handle passed as the first argument.
+*/
+static void rbuSetupOal(sqlite3rbu *p, RbuState *pState){
+ assert( p->rc==SQLITE_OK );
+ if( pState->zTbl ){
+ RbuObjIter *pIter = &p->objiter;
+ int rc = SQLITE_OK;
+
+ while( rc==SQLITE_OK && pIter->zTbl && (pIter->bCleanup
+ || rbuStrCompare(pIter->zIdx, pState->zIdx)
+ || rbuStrCompare(pIter->zTbl, pState->zTbl)
+ )){
+ rc = rbuObjIterNext(p, pIter);
+ }
+
+ if( rc==SQLITE_OK && !pIter->zTbl ){
+ rc = SQLITE_ERROR;
+ p->zErrmsg = sqlite3_mprintf("rbu_state mismatch error");
+ }
+
+ if( rc==SQLITE_OK ){
+ p->nStep = pState->nRow;
+ rc = rbuObjIterPrepareAll(p, &p->objiter, p->nStep);
+ }
+
+ p->rc = rc;
+ }
+}
+
+/*
+** If there is a "*-oal" file in the file-system corresponding to the
+** target database in the file-system, delete it. If an error occurs,
+** leave an error code and error message in the rbu handle.
+*/
+static void rbuDeleteOalFile(sqlite3rbu *p){
+ char *zOal = rbuMPrintf(p, "%s-oal", p->zTarget);
+ if( zOal ){
+ sqlite3_vfs *pVfs = sqlite3_vfs_find(0);
+ assert( pVfs && p->rc==SQLITE_OK && p->zErrmsg==0 );
+ pVfs->xDelete(pVfs, zOal, 0);
+ sqlite3_free(zOal);
+ }
+}
+
+/*
+** Allocate a private rbu VFS for the rbu handle passed as the only
+** argument. This VFS will be used unless the call to sqlite3rbu_open()
+** specified a URI with a vfs=? option in place of a target database
+** file name.
+*/
+static void rbuCreateVfs(sqlite3rbu *p){
+ int rnd;
+ char zRnd[64];
+
+ assert( p->rc==SQLITE_OK );
+ sqlite3_randomness(sizeof(int), (void*)&rnd);
+ sqlite3_snprintf(sizeof(zRnd), zRnd, "rbu_vfs_%d", rnd);
+ p->rc = sqlite3rbu_create_vfs(zRnd, 0);
+ if( p->rc==SQLITE_OK ){
+ sqlite3_vfs *pVfs = sqlite3_vfs_find(zRnd);
+ assert( pVfs );
+ p->zVfsName = pVfs->zName;
+ }
+}
+
+/*
+** Destroy the private VFS created for the rbu handle passed as the only
+** argument by an earlier call to rbuCreateVfs().
+*/
+static void rbuDeleteVfs(sqlite3rbu *p){
+ if( p->zVfsName ){
+ sqlite3rbu_destroy_vfs(p->zVfsName);
+ p->zVfsName = 0;
+ }
+}
+
+/*
+** This user-defined SQL function is invoked with a single argument - the
+** name of a table expected to appear in the target database. It returns
+** the number of auxilliary indexes on the table.
+*/
+static void rbuIndexCntFunc(
+ sqlite3_context *pCtx,
+ int nVal,
+ sqlite3_value **apVal
+){
+ sqlite3rbu *p = (sqlite3rbu*)sqlite3_user_data(pCtx);
+ sqlite3_stmt *pStmt = 0;
+ char *zErrmsg = 0;
+ int rc;
+
+ assert( nVal==1 );
+
+ rc = prepareFreeAndCollectError(p->dbMain, &pStmt, &zErrmsg,
+ sqlite3_mprintf("SELECT count(*) FROM sqlite_master "
+ "WHERE type='index' AND tbl_name = %Q", sqlite3_value_text(apVal[0]))
+ );
+ if( rc!=SQLITE_OK ){
+ sqlite3_result_error(pCtx, zErrmsg, -1);
+ }else{
+ int nIndex = 0;
+ if( SQLITE_ROW==sqlite3_step(pStmt) ){
+ nIndex = sqlite3_column_int(pStmt, 0);
+ }
+ rc = sqlite3_finalize(pStmt);
+ if( rc==SQLITE_OK ){
+ sqlite3_result_int(pCtx, nIndex);
+ }else{
+ sqlite3_result_error(pCtx, sqlite3_errmsg(p->dbMain), -1);
+ }
+ }
+
+ sqlite3_free(zErrmsg);
+}
+
+/*
+** If the RBU database contains the rbu_count table, use it to initialize
+** the sqlite3rbu.nPhaseOneStep variable. The schema of the rbu_count table
+** is assumed to contain the same columns as:
+**
+** CREATE TABLE rbu_count(tbl TEXT PRIMARY KEY, cnt INTEGER) WITHOUT ROWID;
+**
+** There should be one row in the table for each data_xxx table in the
+** database. The 'tbl' column should contain the name of a data_xxx table,
+** and the cnt column the number of rows it contains.
+**
+** sqlite3rbu.nPhaseOneStep is initialized to the sum of (1 + nIndex) * cnt
+** for all rows in the rbu_count table, where nIndex is the number of
+** indexes on the corresponding target database table.
+*/
+static void rbuInitPhaseOneSteps(sqlite3rbu *p){
+ if( p->rc==SQLITE_OK ){
+ sqlite3_stmt *pStmt = 0;
+ int bExists = 0; /* True if rbu_count exists */
+
+ p->nPhaseOneStep = -1;
+
+ p->rc = sqlite3_create_function(p->dbRbu,
+ "rbu_index_cnt", 1, SQLITE_UTF8, (void*)p, rbuIndexCntFunc, 0, 0
+ );
+
+ /* Check for the rbu_count table. If it does not exist, or if an error
+ ** occurs, nPhaseOneStep will be left set to -1. */
+ if( p->rc==SQLITE_OK ){
+ p->rc = prepareAndCollectError(p->dbRbu, &pStmt, &p->zErrmsg,
+ "SELECT 1 FROM sqlite_master WHERE tbl_name = 'rbu_count'"
+ );
+ }
+ if( p->rc==SQLITE_OK ){
+ if( SQLITE_ROW==sqlite3_step(pStmt) ){
+ bExists = 1;
+ }
+ p->rc = sqlite3_finalize(pStmt);
+ }
+
+ if( p->rc==SQLITE_OK && bExists ){
+ p->rc = prepareAndCollectError(p->dbRbu, &pStmt, &p->zErrmsg,
+ "SELECT sum(cnt * (1 + rbu_index_cnt(rbu_target_name(tbl))))"
+ "FROM rbu_count"
+ );
+ if( p->rc==SQLITE_OK ){
+ if( SQLITE_ROW==sqlite3_step(pStmt) ){
+ p->nPhaseOneStep = sqlite3_column_int64(pStmt, 0);
+ }
+ p->rc = sqlite3_finalize(pStmt);
+ }
+ }
+ }
+}
+
+
+static sqlite3rbu *openRbuHandle(
+ const char *zTarget,
+ const char *zRbu,
+ const char *zState
+){
+ sqlite3rbu *p;
+ size_t nTarget = zTarget ? strlen(zTarget) : 0;
+ size_t nRbu = strlen(zRbu);
+ size_t nState = zState ? strlen(zState) : 0;
+ size_t nByte = sizeof(sqlite3rbu) + nTarget+1 + nRbu+1+ nState+1;
+
+ p = (sqlite3rbu*)sqlite3_malloc64(nByte);
+ if( p ){
+ RbuState *pState = 0;
+
+ /* Create the custom VFS. */
+ memset(p, 0, sizeof(sqlite3rbu));
+ rbuCreateVfs(p);
+
+ /* Open the target, RBU and state databases */
+ if( p->rc==SQLITE_OK ){
+ char *pCsr = (char*)&p[1];
+ if( zTarget ){
+ p->zTarget = pCsr;
+ memcpy(p->zTarget, zTarget, nTarget+1);
+ pCsr += nTarget+1;
+ }
+ p->zRbu = pCsr;
+ memcpy(p->zRbu, zRbu, nRbu+1);
+ pCsr += nRbu+1;
+ if( zState ){
+ p->zState = pCsr;
+ memcpy(p->zState, zState, nState+1);
+ }
+ rbuOpenDatabase(p);
+ }
+
+ if( p->rc==SQLITE_OK ){
+ pState = rbuLoadState(p);
+ assert( pState || p->rc!=SQLITE_OK );
+ if( p->rc==SQLITE_OK ){
+
+ if( pState->eStage==0 ){
+ rbuDeleteOalFile(p);
+ rbuInitPhaseOneSteps(p);
+ p->eStage = RBU_STAGE_OAL;
+ }else{
+ p->eStage = pState->eStage;
+ p->nPhaseOneStep = pState->nPhaseOneStep;
+ }
+ p->nProgress = pState->nProgress;
+ p->iOalSz = pState->iOalSz;
+ }
+ }
+ assert( p->rc!=SQLITE_OK || p->eStage!=0 );
+
+ if( p->rc==SQLITE_OK && p->pTargetFd->pWalFd ){
+ if( p->eStage==RBU_STAGE_OAL ){
+ p->rc = SQLITE_ERROR;
+ p->zErrmsg = sqlite3_mprintf("cannot update wal mode database");
+ }else if( p->eStage==RBU_STAGE_MOVE ){
+ p->eStage = RBU_STAGE_CKPT;
+ p->nStep = 0;
+ }
+ }
+
+ if( p->rc==SQLITE_OK
+ && (p->eStage==RBU_STAGE_OAL || p->eStage==RBU_STAGE_MOVE)
+ && pState->eStage!=0
+ ){
+ rbu_file *pFd = (rbuIsVacuum(p) ? p->pRbuFd : p->pTargetFd);
+ if( pFd->iCookie!=pState->iCookie ){
+ /* At this point (pTargetFd->iCookie) contains the value of the
+ ** change-counter cookie (the thing that gets incremented when a
+ ** transaction is committed in rollback mode) currently stored on
+ ** page 1 of the database file. */
+ p->rc = SQLITE_BUSY;
+ p->zErrmsg = sqlite3_mprintf("database modified during rbu %s",
+ (rbuIsVacuum(p) ? "vacuum" : "update")
+ );
+ }
+ }
+
+ if( p->rc==SQLITE_OK ){
+ if( p->eStage==RBU_STAGE_OAL ){
+ sqlite3 *db = p->dbMain;
+
+ if( pState->eStage==0 && rbuIsVacuum(p) ){
+ rbuCopyPragma(p, "page_size");
+ rbuCopyPragma(p, "auto_vacuum");
+ }
+
+ /* Open transactions both databases. The *-oal file is opened or
+ ** created at this point. */
+ if( p->rc==SQLITE_OK ){
+ p->rc = sqlite3_exec(db, "BEGIN IMMEDIATE", 0, 0, &p->zErrmsg);
+ }
+ if( p->rc==SQLITE_OK ){
+ p->rc = sqlite3_exec(p->dbRbu, "BEGIN", 0, 0, &p->zErrmsg);
+ }
+
+ /* Check if the main database is a zipvfs db. If it is, set the upper
+ ** level pager to use "journal_mode=off". This prevents it from
+ ** generating a large journal using a temp file. */
+ if( p->rc==SQLITE_OK ){
+ int frc = sqlite3_file_control(db, "main", SQLITE_FCNTL_ZIPVFS, 0);
+ if( frc==SQLITE_OK ){
+ p->rc = sqlite3_exec(db, "PRAGMA journal_mode=off",0,0,&p->zErrmsg);
+ }
+ }
+
+ /* Point the object iterator at the first object */
+ if( p->rc==SQLITE_OK ){
+ p->rc = rbuObjIterFirst(p, &p->objiter);
+ }
+
+ /* If the RBU database contains no data_xxx tables, declare the RBU
+ ** update finished. */
+ if( p->rc==SQLITE_OK && p->objiter.zTbl==0 ){
+ p->rc = SQLITE_DONE;
+ }
+
+ if( p->rc==SQLITE_OK ){
+ rbuSetupOal(p, pState);
+ }
+
+ }else if( p->eStage==RBU_STAGE_MOVE ){
+ /* no-op */
+ }else if( p->eStage==RBU_STAGE_CKPT ){
+ rbuSetupCheckpoint(p, pState);
+ }else if( p->eStage==RBU_STAGE_DONE ){
+ p->rc = SQLITE_DONE;
+ }else{
+ p->rc = SQLITE_CORRUPT;
+ }
+ }
+
+ rbuFreeState(pState);
+ }
+
+ return p;
+}
+
+/*
+** Open and return a new RBU handle.
+*/
+SQLITE_API sqlite3rbu *SQLITE_STDCALL sqlite3rbu_open(
+ const char *zTarget,
+ const char *zRbu,
+ const char *zState
+){
+ /* TODO: Check that zTarget and zRbu are non-NULL */
+ return openRbuHandle(zTarget, zRbu, zState);
+}
+
+/*
+** Open a handle to begin or resume an RBU VACUUM operation.
+*/
+SQLITE_API sqlite3rbu *SQLITE_STDCALL sqlite3rbu_vacuum(
+ const char *zTarget,
+ const char *zState
+){
+ /* TODO: Check that both arguments are non-NULL */
+ return openRbuHandle(0, zTarget, zState);
+}
+
+/*
+** Return the database handle used by pRbu.
+*/
+SQLITE_API sqlite3 *SQLITE_STDCALL sqlite3rbu_db(sqlite3rbu *pRbu, int bRbu){
+ sqlite3 *db = 0;
+ if( pRbu ){
+ db = (bRbu ? pRbu->dbRbu : pRbu->dbMain);
+ }
+ return db;
+}
+
+
+/*
+** If the error code currently stored in the RBU handle is SQLITE_CONSTRAINT,
+** then edit any error message string so as to remove all occurrences of
+** the pattern "rbu_imp_[0-9]*".
+*/
+static void rbuEditErrmsg(sqlite3rbu *p){
+ if( p->rc==SQLITE_CONSTRAINT && p->zErrmsg ){
+ unsigned int i;
+ size_t nErrmsg = strlen(p->zErrmsg);
+ for(i=0; i<(nErrmsg-8); i++){
+ if( memcmp(&p->zErrmsg[i], "rbu_imp_", 8)==0 ){
+ int nDel = 8;
+ while( p->zErrmsg[i+nDel]>='0' && p->zErrmsg[i+nDel]<='9' ) nDel++;
+ memmove(&p->zErrmsg[i], &p->zErrmsg[i+nDel], nErrmsg + 1 - i - nDel);
+ nErrmsg -= nDel;
+ }
+ }
+ }
+}
+
+/*
+** Close the RBU handle.
+*/
+SQLITE_API int SQLITE_STDCALL sqlite3rbu_close(sqlite3rbu *p, char **pzErrmsg){
+ int rc;
+ if( p ){
+
+ /* Commit the transaction to the *-oal file. */
+ if( p->rc==SQLITE_OK && p->eStage==RBU_STAGE_OAL ){
+ p->rc = sqlite3_exec(p->dbMain, "COMMIT", 0, 0, &p->zErrmsg);
+ }
+
+ rbuSaveState(p, p->eStage);
+
+ if( p->rc==SQLITE_OK && p->eStage==RBU_STAGE_OAL ){
+ p->rc = sqlite3_exec(p->dbRbu, "COMMIT", 0, 0, &p->zErrmsg);
+ }
+
+ /* Close any open statement handles. */
+ rbuObjIterFinalize(&p->objiter);
+
+ /* If this is an RBU vacuum handle and the vacuum has either finished
+ ** successfully or encountered an error, delete the contents of the
+ ** state table. This causes the next call to sqlite3rbu_vacuum()
+ ** specifying the current target and state databases to start a new
+ ** vacuum from scratch. */
+ if( rbuIsVacuum(p) && p->rc!=SQLITE_OK && p->dbRbu ){
+ int rc2 = sqlite3_exec(p->dbRbu, "DELETE FROM stat.rbu_state", 0, 0, 0);
+ if( p->rc==SQLITE_DONE && rc2!=SQLITE_OK ) p->rc = rc2;
+ }
+
+ /* Close the open database handle and VFS object. */
+ sqlite3_close(p->dbRbu);
+ sqlite3_close(p->dbMain);
+ rbuDeleteVfs(p);
+ sqlite3_free(p->aBuf);
+ sqlite3_free(p->aFrame);
+
+ rbuEditErrmsg(p);
+ rc = p->rc;
+ *pzErrmsg = p->zErrmsg;
+ sqlite3_free(p);
+ }else{
+ rc = SQLITE_NOMEM;
+ *pzErrmsg = 0;
+ }
+ return rc;
+}
+
+/*
+** Return the total number of key-value operations (inserts, deletes or
+** updates) that have been performed on the target database since the
+** current RBU update was started.
+*/
+SQLITE_API sqlite3_int64 SQLITE_STDCALL sqlite3rbu_progress(sqlite3rbu *pRbu){
+ return pRbu->nProgress;
+}
+
+/*
+** Return permyriadage progress indications for the two main stages of
+** an RBU update.
+*/
+SQLITE_API void SQLITE_STDCALL sqlite3rbu_bp_progress(sqlite3rbu *p, int *pnOne, int *pnTwo){
+ const int MAX_PROGRESS = 10000;
+ switch( p->eStage ){
+ case RBU_STAGE_OAL:
+ if( p->nPhaseOneStep>0 ){
+ *pnOne = (int)(MAX_PROGRESS * (i64)p->nProgress/(i64)p->nPhaseOneStep);
+ }else{
+ *pnOne = -1;
+ }
+ *pnTwo = 0;
+ break;
+
+ case RBU_STAGE_MOVE:
+ *pnOne = MAX_PROGRESS;
+ *pnTwo = 0;
+ break;
+
+ case RBU_STAGE_CKPT:
+ *pnOne = MAX_PROGRESS;
+ *pnTwo = (int)(MAX_PROGRESS * (i64)p->nStep / (i64)p->nFrame);
+ break;
+
+ case RBU_STAGE_DONE:
+ *pnOne = MAX_PROGRESS;
+ *pnTwo = MAX_PROGRESS;
+ break;
+
+ default:
+ assert( 0 );
+ }
+}
+
+SQLITE_API int SQLITE_STDCALL sqlite3rbu_savestate(sqlite3rbu *p){
+ int rc = p->rc;
+
+ if( rc==SQLITE_DONE ) return SQLITE_OK;
+
+ assert( p->eStage>=RBU_STAGE_OAL && p->eStage<=RBU_STAGE_DONE );
+ if( p->eStage==RBU_STAGE_OAL ){
+ assert( rc!=SQLITE_DONE );
+ if( rc==SQLITE_OK ) rc = sqlite3_exec(p->dbMain, "COMMIT", 0, 0, 0);
+ }
+
+ p->rc = rc;
+ rbuSaveState(p, p->eStage);
+ rc = p->rc;
+
+ if( p->eStage==RBU_STAGE_OAL ){
+ assert( rc!=SQLITE_DONE );
+ if( rc==SQLITE_OK ) rc = sqlite3_exec(p->dbRbu, "COMMIT", 0, 0, 0);
+ if( rc==SQLITE_OK ) rc = sqlite3_exec(p->dbRbu, "BEGIN IMMEDIATE", 0, 0, 0);
+ if( rc==SQLITE_OK ) rc = sqlite3_exec(p->dbMain, "BEGIN IMMEDIATE", 0, 0,0);
+ }
+
+ p->rc = rc;
+ return rc;
+}
+
+/**************************************************************************
+** Beginning of RBU VFS shim methods. The VFS shim modifies the behaviour
+** of a standard VFS in the following ways:
+**
+** 1. Whenever the first page of a main database file is read or
+** written, the value of the change-counter cookie is stored in
+** rbu_file.iCookie. Similarly, the value of the "write-version"
+** database header field is stored in rbu_file.iWriteVer. This ensures
+** that the values are always trustworthy within an open transaction.
+**
+** 2. Whenever an SQLITE_OPEN_WAL file is opened, the (rbu_file.pWalFd)
+** member variable of the associated database file descriptor is set
+** to point to the new file. A mutex protected linked list of all main
+** db fds opened using a particular RBU VFS is maintained at
+** rbu_vfs.pMain to facilitate this.
+**
+** 3. Using a new file-control "SQLITE_FCNTL_RBU", a main db rbu_file
+** object can be marked as the target database of an RBU update. This
+** turns on the following extra special behaviour:
+**
+** 3a. If xAccess() is called to check if there exists a *-wal file
+** associated with an RBU target database currently in RBU_STAGE_OAL
+** stage (preparing the *-oal file), the following special handling
+** applies:
+**
+** * if the *-wal file does exist, return SQLITE_CANTOPEN. An RBU
+** target database may not be in wal mode already.
+**
+** * if the *-wal file does not exist, set the output parameter to
+** non-zero (to tell SQLite that it does exist) anyway.
+**
+** Then, when xOpen() is called to open the *-wal file associated with
+** the RBU target in RBU_STAGE_OAL stage, instead of opening the *-wal
+** file, the rbu vfs opens the corresponding *-oal file instead.
+**
+** 3b. The *-shm pages returned by xShmMap() for a target db file in
+** RBU_STAGE_OAL mode are actually stored in heap memory. This is to
+** avoid creating a *-shm file on disk. Additionally, xShmLock() calls
+** are no-ops on target database files in RBU_STAGE_OAL mode. This is
+** because assert() statements in some VFS implementations fail if
+** xShmLock() is called before xShmMap().
+**
+** 3c. If an EXCLUSIVE lock is attempted on a target database file in any
+** mode except RBU_STAGE_DONE (all work completed and checkpointed), it
+** fails with an SQLITE_BUSY error. This is to stop RBU connections
+** from automatically checkpointing a *-wal (or *-oal) file from within
+** sqlite3_close().
+**
+** 3d. In RBU_STAGE_CAPTURE mode, all xRead() calls on the wal file, and
+** all xWrite() calls on the target database file perform no IO.
+** Instead the frame and page numbers that would be read and written
+** are recorded. Additionally, successful attempts to obtain exclusive
+** xShmLock() WRITER, CHECKPOINTER and READ0 locks on the target
+** database file are recorded. xShmLock() calls to unlock the same
+** locks are no-ops (so that once obtained, these locks are never
+** relinquished). Finally, calls to xSync() on the target database
+** file fail with SQLITE_INTERNAL errors.
+*/
+
+static void rbuUnlockShm(rbu_file *p){
+ if( p->pRbu ){
+ int (*xShmLock)(sqlite3_file*,int,int,int) = p->pReal->pMethods->xShmLock;
+ int i;
+ for(i=0; i<SQLITE_SHM_NLOCK;i++){
+ if( (1<<i) & p->pRbu->mLock ){
+ xShmLock(p->pReal, i, 1, SQLITE_SHM_UNLOCK|SQLITE_SHM_EXCLUSIVE);
+ }
+ }
+ p->pRbu->mLock = 0;
+ }
+}
+
+/*
+** Close an rbu file.
+*/
+static int rbuVfsClose(sqlite3_file *pFile){
+ rbu_file *p = (rbu_file*)pFile;
+ int rc;
+ int i;
+
+ /* Free the contents of the apShm[] array. And the array itself. */
+ for(i=0; i<p->nShm; i++){
+ sqlite3_free(p->apShm[i]);
+ }
+ sqlite3_free(p->apShm);
+ p->apShm = 0;
+ sqlite3_free(p->zDel);
+
+ if( p->openFlags & SQLITE_OPEN_MAIN_DB ){
+ rbu_file **pp;
+ sqlite3_mutex_enter(p->pRbuVfs->mutex);
+ for(pp=&p->pRbuVfs->pMain; *pp!=p; pp=&((*pp)->pMainNext));
+ *pp = p->pMainNext;
+ sqlite3_mutex_leave(p->pRbuVfs->mutex);
+ rbuUnlockShm(p);
+ p->pReal->pMethods->xShmUnmap(p->pReal, 0);
+ }
+
+ /* Close the underlying file handle */
+ rc = p->pReal->pMethods->xClose(p->pReal);
+ return rc;
+}
+
+
+/*
+** Read and return an unsigned 32-bit big-endian integer from the buffer
+** passed as the only argument.
+*/
+static u32 rbuGetU32(u8 *aBuf){
+ return ((u32)aBuf[0] << 24)
+ + ((u32)aBuf[1] << 16)
+ + ((u32)aBuf[2] << 8)
+ + ((u32)aBuf[3]);
+}
+
+/*
+** Write an unsigned 32-bit value in big-endian format to the supplied
+** buffer.
+*/
+static void rbuPutU32(u8 *aBuf, u32 iVal){
+ aBuf[0] = (iVal >> 24) & 0xFF;
+ aBuf[1] = (iVal >> 16) & 0xFF;
+ aBuf[2] = (iVal >> 8) & 0xFF;
+ aBuf[3] = (iVal >> 0) & 0xFF;
+}
+
+static void rbuPutU16(u8 *aBuf, u16 iVal){
+ aBuf[0] = (iVal >> 8) & 0xFF;
+ aBuf[1] = (iVal >> 0) & 0xFF;
+}
+
+/*
+** Read data from an rbuVfs-file.
+*/
+static int rbuVfsRead(
+ sqlite3_file *pFile,
+ void *zBuf,
+ int iAmt,
+ sqlite_int64 iOfst
+){
+ rbu_file *p = (rbu_file*)pFile;
+ sqlite3rbu *pRbu = p->pRbu;
+ int rc;
+
+ if( pRbu && pRbu->eStage==RBU_STAGE_CAPTURE ){
+ assert( p->openFlags & SQLITE_OPEN_WAL );
+ rc = rbuCaptureWalRead(p->pRbu, iOfst, iAmt);
+ }else{
+ if( pRbu && pRbu->eStage==RBU_STAGE_OAL
+ && (p->openFlags & SQLITE_OPEN_WAL)
+ && iOfst>=pRbu->iOalSz
+ ){
+ rc = SQLITE_OK;
+ memset(zBuf, 0, iAmt);
+ }else{
+ rc = p->pReal->pMethods->xRead(p->pReal, zBuf, iAmt, iOfst);
+#if 1
+ /* If this is being called to read the first page of the target
+ ** database as part of an rbu vacuum operation, synthesize the
+ ** contents of the first page if it does not yet exist. Otherwise,
+ ** SQLite will not check for a *-wal file. */
+ if( pRbu && rbuIsVacuum(pRbu)
+ && rc==SQLITE_IOERR_SHORT_READ && iOfst==0
+ && (p->openFlags & SQLITE_OPEN_MAIN_DB)
+ && pRbu->rc==SQLITE_OK
+ ){
+ sqlite3_file *pFd = (sqlite3_file*)pRbu->pRbuFd;
+ rc = pFd->pMethods->xRead(pFd, zBuf, iAmt, iOfst);
+ if( rc==SQLITE_OK ){
+ u8 *aBuf = (u8*)zBuf;
+ u32 iRoot = rbuGetU32(&aBuf[52]) ? 1 : 0;
+ rbuPutU32(&aBuf[52], iRoot); /* largest root page number */
+ rbuPutU32(&aBuf[36], 0); /* number of free pages */
+ rbuPutU32(&aBuf[32], 0); /* first page on free list trunk */
+ rbuPutU32(&aBuf[28], 1); /* size of db file in pages */
+ rbuPutU32(&aBuf[24], pRbu->pRbuFd->iCookie+1); /* Change counter */
+
+ if( iAmt>100 ){
+ memset(&aBuf[100], 0, iAmt-100);
+ rbuPutU16(&aBuf[105], iAmt & 0xFFFF);
+ aBuf[100] = 0x0D;
+ }
+ }
+ }
+#endif
+ }
+ if( rc==SQLITE_OK && iOfst==0 && (p->openFlags & SQLITE_OPEN_MAIN_DB) ){
+ /* These look like magic numbers. But they are stable, as they are part
+ ** of the definition of the SQLite file format, which may not change. */
+ u8 *pBuf = (u8*)zBuf;
+ p->iCookie = rbuGetU32(&pBuf[24]);
+ p->iWriteVer = pBuf[19];
+ }
+ }
+ return rc;
+}
+
+/*
+** Write data to an rbuVfs-file.
+*/
+static int rbuVfsWrite(
+ sqlite3_file *pFile,
+ const void *zBuf,
+ int iAmt,
+ sqlite_int64 iOfst
+){
+ rbu_file *p = (rbu_file*)pFile;
+ sqlite3rbu *pRbu = p->pRbu;
+ int rc;
+
+ if( pRbu && pRbu->eStage==RBU_STAGE_CAPTURE ){
+ assert( p->openFlags & SQLITE_OPEN_MAIN_DB );
+ rc = rbuCaptureDbWrite(p->pRbu, iOfst);
+ }else{
+ if( pRbu && pRbu->eStage==RBU_STAGE_OAL
+ && (p->openFlags & SQLITE_OPEN_WAL)
+ && iOfst>=pRbu->iOalSz
+ ){
+ pRbu->iOalSz = iAmt + iOfst;
+ }
+ rc = p->pReal->pMethods->xWrite(p->pReal, zBuf, iAmt, iOfst);
+ if( rc==SQLITE_OK && iOfst==0 && (p->openFlags & SQLITE_OPEN_MAIN_DB) ){
+ /* These look like magic numbers. But they are stable, as they are part
+ ** of the definition of the SQLite file format, which may not change. */
+ u8 *pBuf = (u8*)zBuf;
+ p->iCookie = rbuGetU32(&pBuf[24]);
+ p->iWriteVer = pBuf[19];
+ }
+ }
+ return rc;
+}
+
+/*
+** Truncate an rbuVfs-file.
+*/
+static int rbuVfsTruncate(sqlite3_file *pFile, sqlite_int64 size){
+ rbu_file *p = (rbu_file*)pFile;
+ return p->pReal->pMethods->xTruncate(p->pReal, size);
+}
+
+/*
+** Sync an rbuVfs-file.
+*/
+static int rbuVfsSync(sqlite3_file *pFile, int flags){
+ rbu_file *p = (rbu_file *)pFile;
+ if( p->pRbu && p->pRbu->eStage==RBU_STAGE_CAPTURE ){
+ if( p->openFlags & SQLITE_OPEN_MAIN_DB ){
+ return SQLITE_INTERNAL;
+ }
+ return SQLITE_OK;
+ }
+ return p->pReal->pMethods->xSync(p->pReal, flags);
+}
+
+/*
+** Return the current file-size of an rbuVfs-file.
+*/
+static int rbuVfsFileSize(sqlite3_file *pFile, sqlite_int64 *pSize){
+ rbu_file *p = (rbu_file *)pFile;
+ int rc;
+ rc = p->pReal->pMethods->xFileSize(p->pReal, pSize);
+
+ /* If this is an RBU vacuum operation and this is the target database,
+ ** pretend that it has at least one page. Otherwise, SQLite will not
+ ** check for the existance of a *-wal file. rbuVfsRead() contains
+ ** similar logic. */
+ if( rc==SQLITE_OK && *pSize==0
+ && p->pRbu && rbuIsVacuum(p->pRbu)
+ && (p->openFlags & SQLITE_OPEN_MAIN_DB)
+ ){
+ *pSize = 1024;
+ }
+ return rc;
+}
+
+/*
+** Lock an rbuVfs-file.
+*/
+static int rbuVfsLock(sqlite3_file *pFile, int eLock){
+ rbu_file *p = (rbu_file*)pFile;
+ sqlite3rbu *pRbu = p->pRbu;
+ int rc = SQLITE_OK;
+
+ assert( p->openFlags & (SQLITE_OPEN_MAIN_DB|SQLITE_OPEN_TEMP_DB) );
+ if( eLock==SQLITE_LOCK_EXCLUSIVE
+ && (p->bNolock || (pRbu && pRbu->eStage!=RBU_STAGE_DONE))
+ ){
+ /* Do not allow EXCLUSIVE locks. Preventing SQLite from taking this
+ ** prevents it from checkpointing the database from sqlite3_close(). */
+ rc = SQLITE_BUSY;
+ }else{
+ rc = p->pReal->pMethods->xLock(p->pReal, eLock);
+ }
+
+ return rc;
+}
+
+/*
+** Unlock an rbuVfs-file.
+*/
+static int rbuVfsUnlock(sqlite3_file *pFile, int eLock){
+ rbu_file *p = (rbu_file *)pFile;
+ return p->pReal->pMethods->xUnlock(p->pReal, eLock);
+}
+
+/*
+** Check if another file-handle holds a RESERVED lock on an rbuVfs-file.
+*/
+static int rbuVfsCheckReservedLock(sqlite3_file *pFile, int *pResOut){
+ rbu_file *p = (rbu_file *)pFile;
+ return p->pReal->pMethods->xCheckReservedLock(p->pReal, pResOut);
+}
+
+/*
+** File control method. For custom operations on an rbuVfs-file.
+*/
+static int rbuVfsFileControl(sqlite3_file *pFile, int op, void *pArg){
+ rbu_file *p = (rbu_file *)pFile;
+ int (*xControl)(sqlite3_file*,int,void*) = p->pReal->pMethods->xFileControl;
+ int rc;
+
+ assert( p->openFlags & (SQLITE_OPEN_MAIN_DB|SQLITE_OPEN_TEMP_DB)
+ || p->openFlags & (SQLITE_OPEN_TRANSIENT_DB|SQLITE_OPEN_TEMP_JOURNAL)
+ );
+ if( op==SQLITE_FCNTL_RBU ){
+ sqlite3rbu *pRbu = (sqlite3rbu*)pArg;
+
+ /* First try to find another RBU vfs lower down in the vfs stack. If
+ ** one is found, this vfs will operate in pass-through mode. The lower
+ ** level vfs will do the special RBU handling. */
+ rc = xControl(p->pReal, op, pArg);
+
+ if( rc==SQLITE_NOTFOUND ){
+ /* Now search for a zipvfs instance lower down in the VFS stack. If
+ ** one is found, this is an error. */
+ void *dummy = 0;
+ rc = xControl(p->pReal, SQLITE_FCNTL_ZIPVFS, &dummy);
+ if( rc==SQLITE_OK ){
+ rc = SQLITE_ERROR;
+ pRbu->zErrmsg = sqlite3_mprintf("rbu/zipvfs setup error");
+ }else if( rc==SQLITE_NOTFOUND ){
+ pRbu->pTargetFd = p;
+ p->pRbu = pRbu;
+ if( p->pWalFd ) p->pWalFd->pRbu = pRbu;
+ rc = SQLITE_OK;
+ }
+ }
+ return rc;
+ }
+ else if( op==SQLITE_FCNTL_RBUCNT ){
+ sqlite3rbu *pRbu = (sqlite3rbu*)pArg;
+ pRbu->nRbu++;
+ pRbu->pRbuFd = p;
+ p->bNolock = 1;
+ }
+
+ rc = xControl(p->pReal, op, pArg);
+ if( rc==SQLITE_OK && op==SQLITE_FCNTL_VFSNAME ){
+ rbu_vfs *pRbuVfs = p->pRbuVfs;
+ char *zIn = *(char**)pArg;
+ char *zOut = sqlite3_mprintf("rbu(%s)/%z", pRbuVfs->base.zName, zIn);
+ *(char**)pArg = zOut;
+ if( zOut==0 ) rc = SQLITE_NOMEM;
+ }
+
+ return rc;
+}
+
+/*
+** Return the sector-size in bytes for an rbuVfs-file.
+*/
+static int rbuVfsSectorSize(sqlite3_file *pFile){
+ rbu_file *p = (rbu_file *)pFile;
+ return p->pReal->pMethods->xSectorSize(p->pReal);
+}
+
+/*
+** Return the device characteristic flags supported by an rbuVfs-file.
+*/
+static int rbuVfsDeviceCharacteristics(sqlite3_file *pFile){
+ rbu_file *p = (rbu_file *)pFile;
+ return p->pReal->pMethods->xDeviceCharacteristics(p->pReal);
+}
+
+/*
+** Take or release a shared-memory lock.
+*/
+static int rbuVfsShmLock(sqlite3_file *pFile, int ofst, int n, int flags){
+ rbu_file *p = (rbu_file*)pFile;
+ sqlite3rbu *pRbu = p->pRbu;
+ int rc = SQLITE_OK;
+
+#ifdef SQLITE_AMALGAMATION
+ assert( WAL_CKPT_LOCK==1 );
+#endif
+
+ assert( p->openFlags & (SQLITE_OPEN_MAIN_DB|SQLITE_OPEN_TEMP_DB) );
+ if( pRbu && (pRbu->eStage==RBU_STAGE_OAL || pRbu->eStage==RBU_STAGE_MOVE) ){
+ /* Magic number 1 is the WAL_CKPT_LOCK lock. Preventing SQLite from
+ ** taking this lock also prevents any checkpoints from occurring.
+ ** todo: really, it's not clear why this might occur, as
+ ** wal_autocheckpoint ought to be turned off. */
+ if( ofst==WAL_LOCK_CKPT && n==1 ) rc = SQLITE_BUSY;
+ }else{
+ int bCapture = 0;
+ if( n==1 && (flags & SQLITE_SHM_EXCLUSIVE)
+ && pRbu && pRbu->eStage==RBU_STAGE_CAPTURE
+ && (ofst==WAL_LOCK_WRITE || ofst==WAL_LOCK_CKPT || ofst==WAL_LOCK_READ0)
+ ){
+ bCapture = 1;
+ }
+
+ if( bCapture==0 || 0==(flags & SQLITE_SHM_UNLOCK) ){
+ rc = p->pReal->pMethods->xShmLock(p->pReal, ofst, n, flags);
+ if( bCapture && rc==SQLITE_OK ){
+ pRbu->mLock |= (1 << ofst);
+ }
+ }
+ }
+
+ return rc;
+}
+
+/*
+** Obtain a pointer to a mapping of a single 32KiB page of the *-shm file.
+*/
+static int rbuVfsShmMap(
+ sqlite3_file *pFile,
+ int iRegion,
+ int szRegion,
+ int isWrite,
+ void volatile **pp
+){
+ rbu_file *p = (rbu_file*)pFile;
+ int rc = SQLITE_OK;
+ int eStage = (p->pRbu ? p->pRbu->eStage : 0);
+
+ /* If not in RBU_STAGE_OAL, allow this call to pass through. Or, if this
+ ** rbu is in the RBU_STAGE_OAL state, use heap memory for *-shm space
+ ** instead of a file on disk. */
+ assert( p->openFlags & (SQLITE_OPEN_MAIN_DB|SQLITE_OPEN_TEMP_DB) );
+ if( eStage==RBU_STAGE_OAL || eStage==RBU_STAGE_MOVE ){
+ if( iRegion<=p->nShm ){
+ int nByte = (iRegion+1) * sizeof(char*);
+ char **apNew = (char**)sqlite3_realloc64(p->apShm, nByte);
+ if( apNew==0 ){
+ rc = SQLITE_NOMEM;
+ }else{
+ memset(&apNew[p->nShm], 0, sizeof(char*) * (1 + iRegion - p->nShm));
+ p->apShm = apNew;
+ p->nShm = iRegion+1;
+ }
+ }
+
+ if( rc==SQLITE_OK && p->apShm[iRegion]==0 ){
+ char *pNew = (char*)sqlite3_malloc64(szRegion);
+ if( pNew==0 ){
+ rc = SQLITE_NOMEM;
+ }else{
+ memset(pNew, 0, szRegion);
+ p->apShm[iRegion] = pNew;
+ }
+ }
+
+ if( rc==SQLITE_OK ){
+ *pp = p->apShm[iRegion];
+ }else{
+ *pp = 0;
+ }
+ }else{
+ assert( p->apShm==0 );
+ rc = p->pReal->pMethods->xShmMap(p->pReal, iRegion, szRegion, isWrite, pp);
+ }
+
+ return rc;
+}
+
+/*
+** Memory barrier.
+*/
+static void rbuVfsShmBarrier(sqlite3_file *pFile){
+ rbu_file *p = (rbu_file *)pFile;
+ p->pReal->pMethods->xShmBarrier(p->pReal);
+}
+
+/*
+** The xShmUnmap method.
+*/
+static int rbuVfsShmUnmap(sqlite3_file *pFile, int delFlag){
+ rbu_file *p = (rbu_file*)pFile;
+ int rc = SQLITE_OK;
+ int eStage = (p->pRbu ? p->pRbu->eStage : 0);
+
+ assert( p->openFlags & (SQLITE_OPEN_MAIN_DB|SQLITE_OPEN_TEMP_DB) );
+ if( eStage==RBU_STAGE_OAL || eStage==RBU_STAGE_MOVE ){
+ /* no-op */
+ }else{
+ /* Release the checkpointer and writer locks */
+ rbuUnlockShm(p);
+ rc = p->pReal->pMethods->xShmUnmap(p->pReal, delFlag);
+ }
+ return rc;
+}
+
+/*
+** Given that zWal points to a buffer containing a wal file name passed to
+** either the xOpen() or xAccess() VFS method, return a pointer to the
+** file-handle opened by the same database connection on the corresponding
+** database file.
+*/
+static rbu_file *rbuFindMaindb(rbu_vfs *pRbuVfs, const char *zWal){
+ rbu_file *pDb;
+ sqlite3_mutex_enter(pRbuVfs->mutex);
+ for(pDb=pRbuVfs->pMain; pDb && pDb->zWal!=zWal; pDb=pDb->pMainNext){}
+ sqlite3_mutex_leave(pRbuVfs->mutex);
+ return pDb;
+}
+
+/*
+** A main database named zName has just been opened. The following
+** function returns a pointer to a buffer owned by SQLite that contains
+** the name of the *-wal file this db connection will use. SQLite
+** happens to pass a pointer to this buffer when using xAccess()
+** or xOpen() to operate on the *-wal file.
+*/
+static const char *rbuMainToWal(const char *zName, int flags){
+ int n = (int)strlen(zName);
+ const char *z = &zName[n];
+ if( flags & SQLITE_OPEN_URI ){
+ int odd = 0;
+ while( 1 ){
+ if( z[0]==0 ){
+ odd = 1 - odd;
+ if( odd && z[1]==0 ) break;
+ }
+ z++;
+ }
+ z += 2;
+ }else{
+ while( *z==0 ) z++;
+ }
+ z += (n + 8 + 1);
+ return z;
+}
+
+/*
+** Open an rbu file handle.
+*/
+static int rbuVfsOpen(
+ sqlite3_vfs *pVfs,
+ const char *zName,
+ sqlite3_file *pFile,
+ int flags,
+ int *pOutFlags
+){
+ static sqlite3_io_methods rbuvfs_io_methods = {
+ 2, /* iVersion */
+ rbuVfsClose, /* xClose */
+ rbuVfsRead, /* xRead */
+ rbuVfsWrite, /* xWrite */
+ rbuVfsTruncate, /* xTruncate */
+ rbuVfsSync, /* xSync */
+ rbuVfsFileSize, /* xFileSize */
+ rbuVfsLock, /* xLock */
+ rbuVfsUnlock, /* xUnlock */
+ rbuVfsCheckReservedLock, /* xCheckReservedLock */
+ rbuVfsFileControl, /* xFileControl */
+ rbuVfsSectorSize, /* xSectorSize */
+ rbuVfsDeviceCharacteristics, /* xDeviceCharacteristics */
+ rbuVfsShmMap, /* xShmMap */
+ rbuVfsShmLock, /* xShmLock */
+ rbuVfsShmBarrier, /* xShmBarrier */
+ rbuVfsShmUnmap, /* xShmUnmap */
+ 0, 0 /* xFetch, xUnfetch */
+ };
+ rbu_vfs *pRbuVfs = (rbu_vfs*)pVfs;
+ sqlite3_vfs *pRealVfs = pRbuVfs->pRealVfs;
+ rbu_file *pFd = (rbu_file *)pFile;
+ int rc = SQLITE_OK;
+ const char *zOpen = zName;
+ int oflags = flags;
+
+ memset(pFd, 0, sizeof(rbu_file));
+ pFd->pReal = (sqlite3_file*)&pFd[1];
+ pFd->pRbuVfs = pRbuVfs;
+ pFd->openFlags = flags;
+ if( zName ){
+ if( flags & SQLITE_OPEN_MAIN_DB ){
+ /* A main database has just been opened. The following block sets
+ ** (pFd->zWal) to point to a buffer owned by SQLite that contains
+ ** the name of the *-wal file this db connection will use. SQLite
+ ** happens to pass a pointer to this buffer when using xAccess()
+ ** or xOpen() to operate on the *-wal file. */
+ pFd->zWal = rbuMainToWal(zName, flags);
+ }
+ else if( flags & SQLITE_OPEN_WAL ){
+ rbu_file *pDb = rbuFindMaindb(pRbuVfs, zName);
+ if( pDb ){
+ if( pDb->pRbu && pDb->pRbu->eStage==RBU_STAGE_OAL ){
+ /* This call is to open a *-wal file. Intead, open the *-oal. This
+ ** code ensures that the string passed to xOpen() is terminated by a
+ ** pair of '\0' bytes in case the VFS attempts to extract a URI
+ ** parameter from it. */
+ const char *zBase = zName;
+ size_t nCopy;
+ char *zCopy;
+ if( rbuIsVacuum(pDb->pRbu) ){
+ zBase = sqlite3_db_filename(pDb->pRbu->dbRbu, "main");
+ zBase = rbuMainToWal(zBase, SQLITE_OPEN_URI);
+ }
+ nCopy = strlen(zBase);
+ zCopy = sqlite3_malloc64(nCopy+2);
+ if( zCopy ){
+ memcpy(zCopy, zBase, nCopy);
+ zCopy[nCopy-3] = 'o';
+ zCopy[nCopy] = '\0';
+ zCopy[nCopy+1] = '\0';
+ zOpen = (const char*)(pFd->zDel = zCopy);
+ }else{
+ rc = SQLITE_NOMEM;
+ }
+ pFd->pRbu = pDb->pRbu;
+ }
+ pDb->pWalFd = pFd;
+ }
+ }
+ }
+
+ if( oflags & SQLITE_OPEN_MAIN_DB
+ && sqlite3_uri_boolean(zName, "rbu_memory", 0)
+ ){
+ assert( oflags & SQLITE_OPEN_MAIN_DB );
+ oflags = SQLITE_OPEN_TEMP_DB | SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE |
+ SQLITE_OPEN_EXCLUSIVE | SQLITE_OPEN_DELETEONCLOSE;
+ zOpen = 0;
+ }
+
+ if( rc==SQLITE_OK ){
+ rc = pRealVfs->xOpen(pRealVfs, zOpen, pFd->pReal, oflags, pOutFlags);
+ }
+ if( pFd->pReal->pMethods ){
+ /* The xOpen() operation has succeeded. Set the sqlite3_file.pMethods
+ ** pointer and, if the file is a main database file, link it into the
+ ** mutex protected linked list of all such files. */
+ pFile->pMethods = &rbuvfs_io_methods;
+ if( flags & SQLITE_OPEN_MAIN_DB ){
+ sqlite3_mutex_enter(pRbuVfs->mutex);
+ pFd->pMainNext = pRbuVfs->pMain;
+ pRbuVfs->pMain = pFd;
+ sqlite3_mutex_leave(pRbuVfs->mutex);
+ }
+ }else{
+ sqlite3_free(pFd->zDel);
+ }
+
+ return rc;
+}
+
+/*
+** Delete the file located at zPath.
+*/
+static int rbuVfsDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){
+ sqlite3_vfs *pRealVfs = ((rbu_vfs*)pVfs)->pRealVfs;
+ return pRealVfs->xDelete(pRealVfs, zPath, dirSync);
+}
+
+/*
+** Test for access permissions. Return true if the requested permission
+** is available, or false otherwise.
+*/
+static int rbuVfsAccess(
+ sqlite3_vfs *pVfs,
+ const char *zPath,
+ int flags,
+ int *pResOut
+){
+ rbu_vfs *pRbuVfs = (rbu_vfs*)pVfs;
+ sqlite3_vfs *pRealVfs = pRbuVfs->pRealVfs;
+ int rc;
+
+ rc = pRealVfs->xAccess(pRealVfs, zPath, flags, pResOut);
+
+ /* If this call is to check if a *-wal file associated with an RBU target
+ ** database connection exists, and the RBU update is in RBU_STAGE_OAL,
+ ** the following special handling is activated:
+ **
+ ** a) if the *-wal file does exist, return SQLITE_CANTOPEN. This
+ ** ensures that the RBU extension never tries to update a database
+ ** in wal mode, even if the first page of the database file has
+ ** been damaged.
+ **
+ ** b) if the *-wal file does not exist, claim that it does anyway,
+ ** causing SQLite to call xOpen() to open it. This call will also
+ ** be intercepted (see the rbuVfsOpen() function) and the *-oal
+ ** file opened instead.
+ */
+ if( rc==SQLITE_OK && flags==SQLITE_ACCESS_EXISTS ){
+ rbu_file *pDb = rbuFindMaindb(pRbuVfs, zPath);
+ if( pDb && pDb->pRbu && pDb->pRbu->eStage==RBU_STAGE_OAL ){
+ if( *pResOut ){
+ rc = SQLITE_CANTOPEN;
+ }else{
+ *pResOut = 1;
+ }
+ }
+ }
+
+ return rc;
+}
+
+/*
+** Populate buffer zOut with the full canonical pathname corresponding
+** to the pathname in zPath. zOut is guaranteed to point to a buffer
+** of at least (DEVSYM_MAX_PATHNAME+1) bytes.
+*/
+static int rbuVfsFullPathname(
+ sqlite3_vfs *pVfs,
+ const char *zPath,
+ int nOut,
+ char *zOut
+){
+ sqlite3_vfs *pRealVfs = ((rbu_vfs*)pVfs)->pRealVfs;
+ return pRealVfs->xFullPathname(pRealVfs, zPath, nOut, zOut);
+}
+
+#ifndef SQLITE_OMIT_LOAD_EXTENSION
+/*
+** Open the dynamic library located at zPath and return a handle.
+*/
+static void *rbuVfsDlOpen(sqlite3_vfs *pVfs, const char *zPath){
+ sqlite3_vfs *pRealVfs = ((rbu_vfs*)pVfs)->pRealVfs;
+ return pRealVfs->xDlOpen(pRealVfs, zPath);
+}
+
+/*
+** Populate the buffer zErrMsg (size nByte bytes) with a human readable
+** utf-8 string describing the most recent error encountered associated
+** with dynamic libraries.
+*/
+static void rbuVfsDlError(sqlite3_vfs *pVfs, int nByte, char *zErrMsg){
+ sqlite3_vfs *pRealVfs = ((rbu_vfs*)pVfs)->pRealVfs;
+ pRealVfs->xDlError(pRealVfs, nByte, zErrMsg);
+}
+
+/*
+** Return a pointer to the symbol zSymbol in the dynamic library pHandle.
+*/
+static void (*rbuVfsDlSym(
+ sqlite3_vfs *pVfs,
+ void *pArg,
+ const char *zSym
+))(void){
+ sqlite3_vfs *pRealVfs = ((rbu_vfs*)pVfs)->pRealVfs;
+ return pRealVfs->xDlSym(pRealVfs, pArg, zSym);
+}
+
+/*
+** Close the dynamic library handle pHandle.
+*/
+static void rbuVfsDlClose(sqlite3_vfs *pVfs, void *pHandle){
+ sqlite3_vfs *pRealVfs = ((rbu_vfs*)pVfs)->pRealVfs;
+ pRealVfs->xDlClose(pRealVfs, pHandle);
+}
+#endif /* SQLITE_OMIT_LOAD_EXTENSION */
+
+/*
+** Populate the buffer pointed to by zBufOut with nByte bytes of
+** random data.
+*/
+static int rbuVfsRandomness(sqlite3_vfs *pVfs, int nByte, char *zBufOut){
+ sqlite3_vfs *pRealVfs = ((rbu_vfs*)pVfs)->pRealVfs;
+ return pRealVfs->xRandomness(pRealVfs, nByte, zBufOut);
+}
+
+/*
+** Sleep for nMicro microseconds. Return the number of microseconds
+** actually slept.
+*/
+static int rbuVfsSleep(sqlite3_vfs *pVfs, int nMicro){
+ sqlite3_vfs *pRealVfs = ((rbu_vfs*)pVfs)->pRealVfs;
+ return pRealVfs->xSleep(pRealVfs, nMicro);
+}
+
+/*
+** Return the current time as a Julian Day number in *pTimeOut.
+*/
+static int rbuVfsCurrentTime(sqlite3_vfs *pVfs, double *pTimeOut){
+ sqlite3_vfs *pRealVfs = ((rbu_vfs*)pVfs)->pRealVfs;
+ return pRealVfs->xCurrentTime(pRealVfs, pTimeOut);
+}
+
+/*
+** No-op.
+*/
+static int rbuVfsGetLastError(sqlite3_vfs *pVfs, int a, char *b){
+ return 0;
+}
+
+/*
+** Deregister and destroy an RBU vfs created by an earlier call to
+** sqlite3rbu_create_vfs().
+*/
+SQLITE_API void SQLITE_STDCALL sqlite3rbu_destroy_vfs(const char *zName){
+ sqlite3_vfs *pVfs = sqlite3_vfs_find(zName);
+ if( pVfs && pVfs->xOpen==rbuVfsOpen ){
+ sqlite3_mutex_free(((rbu_vfs*)pVfs)->mutex);
+ sqlite3_vfs_unregister(pVfs);
+ sqlite3_free(pVfs);
+ }
+}
+
+/*
+** Create an RBU VFS named zName that accesses the underlying file-system
+** via existing VFS zParent. The new object is registered as a non-default
+** VFS with SQLite before returning.
+*/
+SQLITE_API int SQLITE_STDCALL sqlite3rbu_create_vfs(const char *zName, const char *zParent){
+
+ /* Template for VFS */
+ static sqlite3_vfs vfs_template = {
+ 1, /* iVersion */
+ 0, /* szOsFile */
+ 0, /* mxPathname */
+ 0, /* pNext */
+ 0, /* zName */
+ 0, /* pAppData */
+ rbuVfsOpen, /* xOpen */
+ rbuVfsDelete, /* xDelete */
+ rbuVfsAccess, /* xAccess */
+ rbuVfsFullPathname, /* xFullPathname */
+
+#ifndef SQLITE_OMIT_LOAD_EXTENSION
+ rbuVfsDlOpen, /* xDlOpen */
+ rbuVfsDlError, /* xDlError */
+ rbuVfsDlSym, /* xDlSym */
+ rbuVfsDlClose, /* xDlClose */
+#else
+ 0, 0, 0, 0,
+#endif
+
+ rbuVfsRandomness, /* xRandomness */
+ rbuVfsSleep, /* xSleep */
+ rbuVfsCurrentTime, /* xCurrentTime */
+ rbuVfsGetLastError, /* xGetLastError */
+ 0, /* xCurrentTimeInt64 (version 2) */
+ 0, 0, 0 /* Unimplemented version 3 methods */
+ };
+
+ rbu_vfs *pNew = 0; /* Newly allocated VFS */
+ int rc = SQLITE_OK;
+ size_t nName;
+ size_t nByte;
+
+ nName = strlen(zName);
+ nByte = sizeof(rbu_vfs) + nName + 1;
+ pNew = (rbu_vfs*)sqlite3_malloc64(nByte);
+ if( pNew==0 ){
+ rc = SQLITE_NOMEM;
+ }else{
+ sqlite3_vfs *pParent; /* Parent VFS */
+ memset(pNew, 0, nByte);
+ pParent = sqlite3_vfs_find(zParent);
+ if( pParent==0 ){
+ rc = SQLITE_NOTFOUND;
+ }else{
+ char *zSpace;
+ memcpy(&pNew->base, &vfs_template, sizeof(sqlite3_vfs));
+ pNew->base.mxPathname = pParent->mxPathname;
+ pNew->base.szOsFile = sizeof(rbu_file) + pParent->szOsFile;
+ pNew->pRealVfs = pParent;
+ pNew->base.zName = (const char*)(zSpace = (char*)&pNew[1]);
+ memcpy(zSpace, zName, nName);
+
+ /* Allocate the mutex and register the new VFS (not as the default) */
+ pNew->mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_RECURSIVE);
+ if( pNew->mutex==0 ){
+ rc = SQLITE_NOMEM;
+ }else{
+ rc = sqlite3_vfs_register(&pNew->base, 0);
+ }
+ }
+
+ if( rc!=SQLITE_OK ){
+ sqlite3_mutex_free(pNew->mutex);
+ sqlite3_free(pNew);
+ }
+ }
+
+ return rc;
+}
+
+
+/**************************************************************************/
+
+#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_RBU) */
+
+/************** End of sqlite3rbu.c ******************************************/
/************** Begin file dbstat.c ******************************************/
/*
** 2010 July 12
@@ -155232,8 +168654,12 @@ SQLITE_PRIVATE void sqlite3Fts3IcuTokenizerModule(
** information from an SQLite database in order to implement the
** "sqlite3_analyzer" utility. See the ../tool/spaceanal.tcl script
** for an example implementation.
+**
+** Additional information is available on the "dbstat.html" page of the
+** official SQLite documentation.
*/
+/* #include "sqliteInt.h" ** Requires access to internal data structures ** */
#if (defined(SQLITE_ENABLE_DBSTAT_VTAB) || defined(SQLITE_TEST)) \
&& !defined(SQLITE_OMIT_VIRTUALTABLE)
@@ -155279,7 +168705,8 @@ SQLITE_PRIVATE void sqlite3Fts3IcuTokenizerModule(
" 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 */" \
+ " pgsize INTEGER, /* Size of the page */" \
+ " schema TEXT HIDDEN /* Database schema being analyzed */" \
");"
@@ -155317,6 +168744,7 @@ struct StatCursor {
sqlite3_vtab_cursor base;
sqlite3_stmt *pStmt; /* Iterates through set of root pages */
int isEof; /* After pStmt has returned SQLITE_DONE */
+ int iDb; /* Schema used for this query */
StatPage aPage[32];
int iPage; /* Current entry in aPage[] */
@@ -155359,7 +168787,9 @@ static int statConnect(
int iDb;
if( argc>=4 ){
- iDb = sqlite3FindDbName(db, argv[3]);
+ Token nm;
+ sqlite3TokenInit(&nm, (char*)argv[3]);
+ iDb = sqlite3FindDb(db, &nm);
if( iDb<0 ){
*pzErr = sqlite3_mprintf("no such database: %s", argv[3]);
return SQLITE_ERROR;
@@ -155370,7 +168800,7 @@ static int statConnect(
rc = sqlite3_declare_vtab(db, VTAB_SCHEMA);
if( rc==SQLITE_OK ){
pTab = (StatTable *)sqlite3_malloc64(sizeof(StatTable));
- if( pTab==0 ) rc = SQLITE_NOMEM;
+ if( pTab==0 ) rc = SQLITE_NOMEM_BKPT;
}
assert( rc==SQLITE_OK || pTab==0 );
@@ -155394,9 +168824,32 @@ static int statDisconnect(sqlite3_vtab *pVtab){
/*
** There is no "best-index". This virtual table always does a linear
-** scan of the binary VFS log file.
+** scan. However, a schema=? constraint should cause this table to
+** operate on a different database schema, so check for it.
+**
+** idxNum is normally 0, but will be 1 if a schema=? constraint exists.
*/
static int statBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
+ int i;
+
+ pIdxInfo->estimatedCost = 1.0e6; /* Initial cost estimate */
+
+ /* Look for a valid schema=? constraint. If found, change the idxNum to
+ ** 1 and request the value of that constraint be sent to xFilter. And
+ ** lower the cost estimate to encourage the constrained version to be
+ ** used.
+ */
+ for(i=0; i<pIdxInfo->nConstraint; i++){
+ if( pIdxInfo->aConstraint[i].usable==0 ) continue;
+ if( pIdxInfo->aConstraint[i].op!=SQLITE_INDEX_CONSTRAINT_EQ ) continue;
+ if( pIdxInfo->aConstraint[i].iColumn!=10 ) continue;
+ pIdxInfo->idxNum = 1;
+ pIdxInfo->estimatedCost = 1.0;
+ pIdxInfo->aConstraintUsage[i].argvIndex = 1;
+ pIdxInfo->aConstraintUsage[i].omit = 1;
+ break;
+ }
+
/* Records are always returned in ascending order of (name, path).
** If this will satisfy the client, set the orderByConsumed flag so that
@@ -155416,7 +168869,6 @@ static int statBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
pIdxInfo->orderByConsumed = 1;
}
- pIdxInfo->estimatedCost = 10.0;
return SQLITE_OK;
}
@@ -155426,36 +168878,18 @@ static int statBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
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;
+ return SQLITE_NOMEM_BKPT;
}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;
- }
+ pCsr->iDb = pTab->iDb;
}
*ppCursor = (sqlite3_vtab_cursor *)pCsr;
- return rc;
+ return SQLITE_OK;
}
static void statClearPage(StatPage *p){
@@ -155480,6 +168914,7 @@ static void statResetCsr(StatCursor *pCsr){
pCsr->iPage = 0;
sqlite3_free(pCsr->zPath);
pCsr->zPath = 0;
+ pCsr->isEof = 0;
}
/*
@@ -155552,7 +168987,7 @@ static int statDecodePage(Btree *pBt, StatPage *p){
nUsable = szPage - sqlite3BtreeGetReserveNoMutex(pBt);
sqlite3BtreeLeave(pBt);
p->aCell = sqlite3_malloc64((p->nCell+1) * sizeof(StatCell));
- if( p->aCell==0 ) return SQLITE_NOMEM;
+ if( p->aCell==0 ) return SQLITE_NOMEM_BKPT;
memset(p->aCell, 0, (p->nCell+1) * sizeof(StatCell));
for(i=0; i<p->nCell; i++){
@@ -155585,13 +169020,13 @@ static int statDecodePage(Btree *pBt, StatPage *p){
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;
+ if( pCell->aOvfl==0 ) return SQLITE_NOMEM_BKPT;
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);
+ rc = sqlite3PagerGet(sqlite3BtreePager(pBt), iPrev, &pPg, 0);
if( rc!=SQLITE_OK ){
assert( pPg==0 );
return rc;
@@ -155642,7 +169077,7 @@ static int statNext(sqlite3_vtab_cursor *pCursor){
char *z;
StatCursor *pCsr = (StatCursor *)pCursor;
StatTable *pTab = (StatTable *)pCursor->pVtab;
- Btree *pBt = pTab->db->aDb[pTab->iDb].pBt;
+ Btree *pBt = pTab->db->aDb[pCsr->iDb].pBt;
Pager *pPager = sqlite3BtreePager(pBt);
sqlite3_free(pCsr->zPath);
@@ -155659,12 +169094,12 @@ statNextRestart:
pCsr->isEof = 1;
return sqlite3_reset(pCsr->pStmt);
}
- rc = sqlite3PagerGet(pPager, iRoot, &pCsr->aPage[0].pPg);
+ rc = sqlite3PagerGet(pPager, iRoot, &pCsr->aPage[0].pPg, 0);
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;
+ if( z==0 ) rc = SQLITE_NOMEM_BKPT;
}else{
pCsr->isEof = 1;
return sqlite3_reset(pCsr->pStmt);
@@ -155699,7 +169134,7 @@ statNextRestart:
}
pCell->iOvfl++;
statSizeAndOffset(pCsr);
- return z==0 ? SQLITE_NOMEM : SQLITE_OK;
+ return z==0 ? SQLITE_NOMEM_BKPT : SQLITE_OK;
}
if( p->iRightChildPg ) break;
p->iCell++;
@@ -155719,11 +169154,11 @@ statNextRestart:
}else{
p[1].iPgno = p->aCell[p->iCell].iChildPg;
}
- rc = sqlite3PagerGet(pPager, p[1].iPgno, &p[1].pPg);
+ rc = sqlite3PagerGet(pPager, p[1].iPgno, &p[1].pPg, 0);
p[1].iCell = 0;
p[1].zPath = z = sqlite3_mprintf("%s%.3x/", p->zPath, p->iCell);
p->iCell++;
- if( z==0 ) rc = SQLITE_NOMEM;
+ if( z==0 ) rc = SQLITE_NOMEM_BKPT;
}
@@ -155757,7 +169192,7 @@ statNextRestart:
pCsr->nUnused = p->nUnused;
pCsr->nMxPayload = p->nMxPayload;
pCsr->zPath = z = sqlite3_mprintf("%s", p->zPath);
- if( z==0 ) rc = SQLITE_NOMEM;
+ if( z==0 ) rc = SQLITE_NOMEM_BKPT;
nPayload = 0;
for(i=0; i<p->nCell; i++){
nPayload += p->aCell[i].nLocal;
@@ -155780,9 +169215,43 @@ static int statFilter(
int argc, sqlite3_value **argv
){
StatCursor *pCsr = (StatCursor *)pCursor;
+ StatTable *pTab = (StatTable*)(pCursor->pVtab);
+ char *zSql;
+ int rc = SQLITE_OK;
+ char *zMaster;
+ if( idxNum==1 ){
+ const char *zDbase = (const char*)sqlite3_value_text(argv[0]);
+ pCsr->iDb = sqlite3FindDbName(pTab->db, zDbase);
+ if( pCsr->iDb<0 ){
+ sqlite3_free(pCursor->pVtab->zErrMsg);
+ pCursor->pVtab->zErrMsg = sqlite3_mprintf("no such schema: %s", zDbase);
+ return pCursor->pVtab->zErrMsg ? SQLITE_ERROR : SQLITE_NOMEM_BKPT;
+ }
+ }else{
+ pCsr->iDb = pTab->iDb;
+ }
statResetCsr(pCsr);
- return statNext(pCursor);
+ sqlite3_finalize(pCsr->pStmt);
+ pCsr->pStmt = 0;
+ zMaster = pCsr->iDb==1 ? "sqlite_temp_master" : "sqlite_master";
+ zSql = sqlite3_mprintf(
+ "SELECT 'sqlite_master' AS name, 1 AS rootpage, 'table' AS type"
+ " UNION ALL "
+ "SELECT name, rootpage, type"
+ " FROM \"%w\".%s WHERE rootpage!=0"
+ " ORDER BY name", pTab->db->aDb[pCsr->iDb].zName, zMaster);
+ if( zSql==0 ){
+ return SQLITE_NOMEM_BKPT;
+ }else{
+ rc = sqlite3_prepare_v2(pTab->db, zSql, -1, &pCsr->pStmt, 0);
+ sqlite3_free(zSql);
+ }
+
+ if( rc==SQLITE_OK ){
+ rc = statNext(pCursor);
+ }
+ return rc;
}
static int statColumn(
@@ -155819,10 +169288,15 @@ static int statColumn(
case 8: /* pgoffset */
sqlite3_result_int64(ctx, pCsr->iOffset);
break;
- default: /* pgsize */
- assert( i==9 );
+ case 9: /* pgsize */
sqlite3_result_int(ctx, pCsr->szPage);
break;
+ default: { /* schema */
+ sqlite3 *db = sqlite3_context_db_handle(ctx);
+ int iDb = pCsr->iDb;
+ sqlite3_result_text(ctx, db->aDb[iDb].zName, -1, SQLITE_STATIC);
+ break;
+ }
}
return SQLITE_OK;
}
@@ -155836,7 +169310,7 @@ static int statRowid(sqlite3_vtab_cursor *pCursor, sqlite_int64 *pRowid){
/*
** Invoke this routine to register the "dbstat" virtual table module
*/
-SQLITE_API int SQLITE_STDCALL sqlite3_dbstat_register(sqlite3 *db){
+SQLITE_PRIVATE int sqlite3DbstatRegister(sqlite3 *db){
static sqlite3_module dbstat_module = {
0, /* iVersion */
statConnect, /* xCreate */
@@ -155861,6 +169335,27508 @@ SQLITE_API int SQLITE_STDCALL sqlite3_dbstat_register(sqlite3 *db){
};
return sqlite3_create_module(db, "dbstat", &dbstat_module, 0);
}
+#elif defined(SQLITE_ENABLE_DBSTAT_VTAB)
+SQLITE_PRIVATE int sqlite3DbstatRegister(sqlite3 *db){ return SQLITE_OK; }
#endif /* SQLITE_ENABLE_DBSTAT_VTAB */
/************** End of dbstat.c **********************************************/
+/************** Begin file sqlite3session.c **********************************/
+
+#if defined(SQLITE_ENABLE_SESSION) && defined(SQLITE_ENABLE_PREUPDATE_HOOK)
+/* #include "sqlite3session.h" */
+/* #include <assert.h> */
+/* #include <string.h> */
+
+#ifndef SQLITE_AMALGAMATION
+/* # include "sqliteInt.h" */
+/* # include "vdbeInt.h" */
+#endif
+
+typedef struct SessionTable SessionTable;
+typedef struct SessionChange SessionChange;
+typedef struct SessionBuffer SessionBuffer;
+typedef struct SessionInput SessionInput;
+
+/*
+** Minimum chunk size used by streaming versions of functions.
+*/
+#ifndef SESSIONS_STRM_CHUNK_SIZE
+# ifdef SQLITE_TEST
+# define SESSIONS_STRM_CHUNK_SIZE 64
+# else
+# define SESSIONS_STRM_CHUNK_SIZE 1024
+# endif
+#endif
+
+typedef struct SessionHook SessionHook;
+struct SessionHook {
+ void *pCtx;
+ int (*xOld)(void*,int,sqlite3_value**);
+ int (*xNew)(void*,int,sqlite3_value**);
+ int (*xCount)(void*);
+ int (*xDepth)(void*);
+};
+
+/*
+** Session handle structure.
+*/
+struct sqlite3_session {
+ sqlite3 *db; /* Database handle session is attached to */
+ char *zDb; /* Name of database session is attached to */
+ int bEnable; /* True if currently recording */
+ int bIndirect; /* True if all changes are indirect */
+ int bAutoAttach; /* True to auto-attach tables */
+ int rc; /* Non-zero if an error has occurred */
+ void *pFilterCtx; /* First argument to pass to xTableFilter */
+ int (*xTableFilter)(void *pCtx, const char *zTab);
+ sqlite3_session *pNext; /* Next session object on same db. */
+ SessionTable *pTable; /* List of attached tables */
+ SessionHook hook; /* APIs to grab new and old data with */
+};
+
+/*
+** Instances of this structure are used to build strings or binary records.
+*/
+struct SessionBuffer {
+ u8 *aBuf; /* Pointer to changeset buffer */
+ int nBuf; /* Size of buffer aBuf */
+ int nAlloc; /* Size of allocation containing aBuf */
+};
+
+/*
+** An object of this type is used internally as an abstraction for
+** input data. Input data may be supplied either as a single large buffer
+** (e.g. sqlite3changeset_start()) or using a stream function (e.g.
+** sqlite3changeset_start_strm()).
+*/
+struct SessionInput {
+ int bNoDiscard; /* If true, discard no data */
+ int iCurrent; /* Offset in aData[] of current change */
+ int iNext; /* Offset in aData[] of next change */
+ u8 *aData; /* Pointer to buffer containing changeset */
+ int nData; /* Number of bytes in aData */
+
+ SessionBuffer buf; /* Current read buffer */
+ int (*xInput)(void*, void*, int*); /* Input stream call (or NULL) */
+ void *pIn; /* First argument to xInput */
+ int bEof; /* Set to true after xInput finished */
+};
+
+/*
+** Structure for changeset iterators.
+*/
+struct sqlite3_changeset_iter {
+ SessionInput in; /* Input buffer or stream */
+ SessionBuffer tblhdr; /* Buffer to hold apValue/zTab/abPK/ */
+ int bPatchset; /* True if this is a patchset */
+ int rc; /* Iterator error code */
+ sqlite3_stmt *pConflict; /* Points to conflicting row, if any */
+ char *zTab; /* Current table */
+ int nCol; /* Number of columns in zTab */
+ int op; /* Current operation */
+ int bIndirect; /* True if current change was indirect */
+ u8 *abPK; /* Primary key array */
+ sqlite3_value **apValue; /* old.* and new.* values */
+};
+
+/*
+** Each session object maintains a set of the following structures, one
+** for each table the session object is monitoring. The structures are
+** stored in a linked list starting at sqlite3_session.pTable.
+**
+** The keys of the SessionTable.aChange[] hash table are all rows that have
+** been modified in any way since the session object was attached to the
+** table.
+**
+** The data associated with each hash-table entry is a structure containing
+** a subset of the initial values that the modified row contained at the
+** start of the session. Or no initial values if the row was inserted.
+*/
+struct SessionTable {
+ SessionTable *pNext;
+ char *zName; /* Local name of table */
+ int nCol; /* Number of columns in table zName */
+ const char **azCol; /* Column names */
+ u8 *abPK; /* Array of primary key flags */
+ int nEntry; /* Total number of entries in hash table */
+ int nChange; /* Size of apChange[] array */
+ SessionChange **apChange; /* Hash table buckets */
+};
+
+/*
+** RECORD FORMAT:
+**
+** The following record format is similar to (but not compatible with) that
+** used in SQLite database files. This format is used as part of the
+** change-set binary format, and so must be architecture independent.
+**
+** Unlike the SQLite database record format, each field is self-contained -
+** there is no separation of header and data. Each field begins with a
+** single byte describing its type, as follows:
+**
+** 0x00: Undefined value.
+** 0x01: Integer value.
+** 0x02: Real value.
+** 0x03: Text value.
+** 0x04: Blob value.
+** 0x05: SQL NULL value.
+**
+** Note that the above match the definitions of SQLITE_INTEGER, SQLITE_TEXT
+** and so on in sqlite3.h. For undefined and NULL values, the field consists
+** only of the single type byte. For other types of values, the type byte
+** is followed by:
+**
+** Text values:
+** A varint containing the number of bytes in the value (encoded using
+** UTF-8). Followed by a buffer containing the UTF-8 representation
+** of the text value. There is no nul terminator.
+**
+** Blob values:
+** A varint containing the number of bytes in the value, followed by
+** a buffer containing the value itself.
+**
+** Integer values:
+** An 8-byte big-endian integer value.
+**
+** Real values:
+** An 8-byte big-endian IEEE 754-2008 real value.
+**
+** Varint values are encoded in the same way as varints in the SQLite
+** record format.
+**
+** CHANGESET FORMAT:
+**
+** A changeset is a collection of DELETE, UPDATE and INSERT operations on
+** one or more tables. Operations on a single table are grouped together,
+** but may occur in any order (i.e. deletes, updates and inserts are all
+** mixed together).
+**
+** Each group of changes begins with a table header:
+**
+** 1 byte: Constant 0x54 (capital 'T')
+** Varint: Number of columns in the table.
+** nCol bytes: 0x01 for PK columns, 0x00 otherwise.
+** N bytes: Unqualified table name (encoded using UTF-8). Nul-terminated.
+**
+** Followed by one or more changes to the table.
+**
+** 1 byte: Either SQLITE_INSERT (0x12), UPDATE (0x17) or DELETE (0x09).
+** 1 byte: The "indirect-change" flag.
+** old.* record: (delete and update only)
+** new.* record: (insert and update only)
+**
+** The "old.*" and "new.*" records, if present, are N field records in the
+** format described above under "RECORD FORMAT", where N is the number of
+** columns in the table. The i'th field of each record is associated with
+** the i'th column of the table, counting from left to right in the order
+** in which columns were declared in the CREATE TABLE statement.
+**
+** The new.* record that is part of each INSERT change contains the values
+** that make up the new row. Similarly, the old.* record that is part of each
+** DELETE change contains the values that made up the row that was deleted
+** from the database. In the changeset format, the records that are part
+** of INSERT or DELETE changes never contain any undefined (type byte 0x00)
+** fields.
+**
+** Within the old.* record associated with an UPDATE change, all fields
+** associated with table columns that are not PRIMARY KEY columns and are
+** not modified by the UPDATE change are set to "undefined". Other fields
+** are set to the values that made up the row before the UPDATE that the
+** change records took place. Within the new.* record, fields associated
+** with table columns modified by the UPDATE change contain the new
+** values. Fields associated with table columns that are not modified
+** are set to "undefined".
+**
+** PATCHSET FORMAT:
+**
+** A patchset is also a collection of changes. It is similar to a changeset,
+** but leaves undefined those fields that are not useful if no conflict
+** resolution is required when applying the changeset.
+**
+** Each group of changes begins with a table header:
+**
+** 1 byte: Constant 0x50 (capital 'P')
+** Varint: Number of columns in the table.
+** nCol bytes: 0x01 for PK columns, 0x00 otherwise.
+** N bytes: Unqualified table name (encoded using UTF-8). Nul-terminated.
+**
+** Followed by one or more changes to the table.
+**
+** 1 byte: Either SQLITE_INSERT (0x12), UPDATE (0x17) or DELETE (0x09).
+** 1 byte: The "indirect-change" flag.
+** single record: (PK fields for DELETE, PK and modified fields for UPDATE,
+** full record for INSERT).
+**
+** As in the changeset format, each field of the single record that is part
+** of a patchset change is associated with the correspondingly positioned
+** table column, counting from left to right within the CREATE TABLE
+** statement.
+**
+** For a DELETE change, all fields within the record except those associated
+** with PRIMARY KEY columns are set to "undefined". The PRIMARY KEY fields
+** contain the values identifying the row to delete.
+**
+** For an UPDATE change, all fields except those associated with PRIMARY KEY
+** columns and columns that are modified by the UPDATE are set to "undefined".
+** PRIMARY KEY fields contain the values identifying the table row to update,
+** and fields associated with modified columns contain the new column values.
+**
+** The records associated with INSERT changes are in the same format as for
+** changesets. It is not possible for a record associated with an INSERT
+** change to contain a field set to "undefined".
+*/
+
+/*
+** For each row modified during a session, there exists a single instance of
+** this structure stored in a SessionTable.aChange[] hash table.
+*/
+struct SessionChange {
+ int op; /* One of UPDATE, DELETE, INSERT */
+ int bIndirect; /* True if this change is "indirect" */
+ int nRecord; /* Number of bytes in buffer aRecord[] */
+ u8 *aRecord; /* Buffer containing old.* record */
+ SessionChange *pNext; /* For hash-table collisions */
+};
+
+/*
+** Write a varint with value iVal into the buffer at aBuf. Return the
+** number of bytes written.
+*/
+static int sessionVarintPut(u8 *aBuf, int iVal){
+ return putVarint32(aBuf, iVal);
+}
+
+/*
+** Return the number of bytes required to store value iVal as a varint.
+*/
+static int sessionVarintLen(int iVal){
+ return sqlite3VarintLen(iVal);
+}
+
+/*
+** Read a varint value from aBuf[] into *piVal. Return the number of
+** bytes read.
+*/
+static int sessionVarintGet(u8 *aBuf, int *piVal){
+ return getVarint32(aBuf, *piVal);
+}
+
+/* Load an unaligned and unsigned 32-bit integer */
+#define SESSION_UINT32(x) (((u32)(x)[0]<<24)|((x)[1]<<16)|((x)[2]<<8)|(x)[3])
+
+/*
+** Read a 64-bit big-endian integer value from buffer aRec[]. Return
+** the value read.
+*/
+static sqlite3_int64 sessionGetI64(u8 *aRec){
+ u64 x = SESSION_UINT32(aRec);
+ u32 y = SESSION_UINT32(aRec+4);
+ x = (x<<32) + y;
+ return (sqlite3_int64)x;
+}
+
+/*
+** Write a 64-bit big-endian integer value to the buffer aBuf[].
+*/
+static void sessionPutI64(u8 *aBuf, sqlite3_int64 i){
+ aBuf[0] = (i>>56) & 0xFF;
+ aBuf[1] = (i>>48) & 0xFF;
+ aBuf[2] = (i>>40) & 0xFF;
+ aBuf[3] = (i>>32) & 0xFF;
+ aBuf[4] = (i>>24) & 0xFF;
+ aBuf[5] = (i>>16) & 0xFF;
+ aBuf[6] = (i>> 8) & 0xFF;
+ aBuf[7] = (i>> 0) & 0xFF;
+}
+
+/*
+** This function is used to serialize the contents of value pValue (see
+** comment titled "RECORD FORMAT" above).
+**
+** If it is non-NULL, the serialized form of the value is written to
+** buffer aBuf. *pnWrite is set to the number of bytes written before
+** returning. Or, if aBuf is NULL, the only thing this function does is
+** set *pnWrite.
+**
+** If no error occurs, SQLITE_OK is returned. Or, if an OOM error occurs
+** within a call to sqlite3_value_text() (may fail if the db is utf-16))
+** SQLITE_NOMEM is returned.
+*/
+static int sessionSerializeValue(
+ u8 *aBuf, /* If non-NULL, write serialized value here */
+ sqlite3_value *pValue, /* Value to serialize */
+ int *pnWrite /* IN/OUT: Increment by bytes written */
+){
+ int nByte; /* Size of serialized value in bytes */
+
+ if( pValue ){
+ int eType; /* Value type (SQLITE_NULL, TEXT etc.) */
+
+ eType = sqlite3_value_type(pValue);
+ if( aBuf ) aBuf[0] = eType;
+
+ switch( eType ){
+ case SQLITE_NULL:
+ nByte = 1;
+ break;
+
+ case SQLITE_INTEGER:
+ case SQLITE_FLOAT:
+ if( aBuf ){
+ /* TODO: SQLite does something special to deal with mixed-endian
+ ** floating point values (e.g. ARM7). This code probably should
+ ** too. */
+ u64 i;
+ if( eType==SQLITE_INTEGER ){
+ i = (u64)sqlite3_value_int64(pValue);
+ }else{
+ double r;
+ assert( sizeof(double)==8 && sizeof(u64)==8 );
+ r = sqlite3_value_double(pValue);
+ memcpy(&i, &r, 8);
+ }
+ sessionPutI64(&aBuf[1], i);
+ }
+ nByte = 9;
+ break;
+
+ default: {
+ u8 *z;
+ int n;
+ int nVarint;
+
+ assert( eType==SQLITE_TEXT || eType==SQLITE_BLOB );
+ if( eType==SQLITE_TEXT ){
+ z = (u8 *)sqlite3_value_text(pValue);
+ }else{
+ z = (u8 *)sqlite3_value_blob(pValue);
+ }
+ n = sqlite3_value_bytes(pValue);
+ if( z==0 && (eType!=SQLITE_BLOB || n>0) ) return SQLITE_NOMEM;
+ nVarint = sessionVarintLen(n);
+
+ if( aBuf ){
+ sessionVarintPut(&aBuf[1], n);
+ memcpy(&aBuf[nVarint + 1], eType==SQLITE_TEXT ?
+ sqlite3_value_text(pValue) : sqlite3_value_blob(pValue), n
+ );
+ }
+
+ nByte = 1 + nVarint + n;
+ break;
+ }
+ }
+ }else{
+ nByte = 1;
+ if( aBuf ) aBuf[0] = '\0';
+ }
+
+ if( pnWrite ) *pnWrite += nByte;
+ return SQLITE_OK;
+}
+
+
+/*
+** This macro is used to calculate hash key values for data structures. In
+** order to use this macro, the entire data structure must be represented
+** as a series of unsigned integers. In order to calculate a hash-key value
+** for a data structure represented as three such integers, the macro may
+** then be used as follows:
+**
+** int hash_key_value;
+** hash_key_value = HASH_APPEND(0, <value 1>);
+** hash_key_value = HASH_APPEND(hash_key_value, <value 2>);
+** hash_key_value = HASH_APPEND(hash_key_value, <value 3>);
+**
+** In practice, the data structures this macro is used for are the primary
+** key values of modified rows.
+*/
+#define HASH_APPEND(hash, add) ((hash) << 3) ^ (hash) ^ (unsigned int)(add)
+
+/*
+** Append the hash of the 64-bit integer passed as the second argument to the
+** hash-key value passed as the first. Return the new hash-key value.
+*/
+static unsigned int sessionHashAppendI64(unsigned int h, i64 i){
+ h = HASH_APPEND(h, i & 0xFFFFFFFF);
+ return HASH_APPEND(h, (i>>32)&0xFFFFFFFF);
+}
+
+/*
+** Append the hash of the blob passed via the second and third arguments to
+** the hash-key value passed as the first. Return the new hash-key value.
+*/
+static unsigned int sessionHashAppendBlob(unsigned int h, int n, const u8 *z){
+ int i;
+ for(i=0; i<n; i++) h = HASH_APPEND(h, z[i]);
+ return h;
+}
+
+/*
+** Append the hash of the data type passed as the second argument to the
+** hash-key value passed as the first. Return the new hash-key value.
+*/
+static unsigned int sessionHashAppendType(unsigned int h, int eType){
+ return HASH_APPEND(h, eType);
+}
+
+/*
+** This function may only be called from within a pre-update callback.
+** It calculates a hash based on the primary key values of the old.* or
+** new.* row currently available and, assuming no error occurs, writes it to
+** *piHash before returning. If the primary key contains one or more NULL
+** values, *pbNullPK is set to true before returning.
+**
+** If an error occurs, an SQLite error code is returned and the final values
+** of *piHash asn *pbNullPK are undefined. Otherwise, SQLITE_OK is returned
+** and the output variables are set as described above.
+*/
+static int sessionPreupdateHash(
+ sqlite3_session *pSession, /* Session object that owns pTab */
+ SessionTable *pTab, /* Session table handle */
+ int bNew, /* True to hash the new.* PK */
+ int *piHash, /* OUT: Hash value */
+ int *pbNullPK /* OUT: True if there are NULL values in PK */
+){
+ unsigned int h = 0; /* Hash value to return */
+ int i; /* Used to iterate through columns */
+
+ assert( *pbNullPK==0 );
+ assert( pTab->nCol==pSession->hook.xCount(pSession->hook.pCtx) );
+ for(i=0; i<pTab->nCol; i++){
+ if( pTab->abPK[i] ){
+ int rc;
+ int eType;
+ sqlite3_value *pVal;
+
+ if( bNew ){
+ rc = pSession->hook.xNew(pSession->hook.pCtx, i, &pVal);
+ }else{
+ rc = pSession->hook.xOld(pSession->hook.pCtx, i, &pVal);
+ }
+ if( rc!=SQLITE_OK ) return rc;
+
+ eType = sqlite3_value_type(pVal);
+ h = sessionHashAppendType(h, eType);
+ if( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT ){
+ i64 iVal;
+ if( eType==SQLITE_INTEGER ){
+ iVal = sqlite3_value_int64(pVal);
+ }else{
+ double rVal = sqlite3_value_double(pVal);
+ assert( sizeof(iVal)==8 && sizeof(rVal)==8 );
+ memcpy(&iVal, &rVal, 8);
+ }
+ h = sessionHashAppendI64(h, iVal);
+ }else if( eType==SQLITE_TEXT || eType==SQLITE_BLOB ){
+ const u8 *z;
+ int n;
+ if( eType==SQLITE_TEXT ){
+ z = (const u8 *)sqlite3_value_text(pVal);
+ }else{
+ z = (const u8 *)sqlite3_value_blob(pVal);
+ }
+ n = sqlite3_value_bytes(pVal);
+ if( !z && (eType!=SQLITE_BLOB || n>0) ) return SQLITE_NOMEM;
+ h = sessionHashAppendBlob(h, n, z);
+ }else{
+ assert( eType==SQLITE_NULL );
+ *pbNullPK = 1;
+ }
+ }
+ }
+
+ *piHash = (h % pTab->nChange);
+ return SQLITE_OK;
+}
+
+/*
+** The buffer that the argument points to contains a serialized SQL value.
+** Return the number of bytes of space occupied by the value (including
+** the type byte).
+*/
+static int sessionSerialLen(u8 *a){
+ int e = *a;
+ int n;
+ if( e==0 ) return 1;
+ if( e==SQLITE_NULL ) return 1;
+ if( e==SQLITE_INTEGER || e==SQLITE_FLOAT ) return 9;
+ return sessionVarintGet(&a[1], &n) + 1 + n;
+}
+
+/*
+** Based on the primary key values stored in change aRecord, calculate a
+** hash key. Assume the has table has nBucket buckets. The hash keys
+** calculated by this function are compatible with those calculated by
+** sessionPreupdateHash().
+**
+** The bPkOnly argument is non-zero if the record at aRecord[] is from
+** a patchset DELETE. In this case the non-PK fields are omitted entirely.
+*/
+static unsigned int sessionChangeHash(
+ SessionTable *pTab, /* Table handle */
+ int bPkOnly, /* Record consists of PK fields only */
+ u8 *aRecord, /* Change record */
+ int nBucket /* Assume this many buckets in hash table */
+){
+ unsigned int h = 0; /* Value to return */
+ int i; /* Used to iterate through columns */
+ u8 *a = aRecord; /* Used to iterate through change record */
+
+ for(i=0; i<pTab->nCol; i++){
+ int eType = *a;
+ int isPK = pTab->abPK[i];
+ if( bPkOnly && isPK==0 ) continue;
+
+ /* It is not possible for eType to be SQLITE_NULL here. The session
+ ** module does not record changes for rows with NULL values stored in
+ ** primary key columns. */
+ assert( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT
+ || eType==SQLITE_TEXT || eType==SQLITE_BLOB
+ || eType==SQLITE_NULL || eType==0
+ );
+ assert( !isPK || (eType!=0 && eType!=SQLITE_NULL) );
+
+ if( isPK ){
+ a++;
+ h = sessionHashAppendType(h, eType);
+ if( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT ){
+ h = sessionHashAppendI64(h, sessionGetI64(a));
+ a += 8;
+ }else{
+ int n;
+ a += sessionVarintGet(a, &n);
+ h = sessionHashAppendBlob(h, n, a);
+ a += n;
+ }
+ }else{
+ a += sessionSerialLen(a);
+ }
+ }
+ return (h % nBucket);
+}
+
+/*
+** Arguments aLeft and aRight are pointers to change records for table pTab.
+** This function returns true if the two records apply to the same row (i.e.
+** have the same values stored in the primary key columns), or false
+** otherwise.
+*/
+static int sessionChangeEqual(
+ SessionTable *pTab, /* Table used for PK definition */
+ int bLeftPkOnly, /* True if aLeft[] contains PK fields only */
+ u8 *aLeft, /* Change record */
+ int bRightPkOnly, /* True if aRight[] contains PK fields only */
+ u8 *aRight /* Change record */
+){
+ u8 *a1 = aLeft; /* Cursor to iterate through aLeft */
+ u8 *a2 = aRight; /* Cursor to iterate through aRight */
+ int iCol; /* Used to iterate through table columns */
+
+ for(iCol=0; iCol<pTab->nCol; iCol++){
+ if( pTab->abPK[iCol] ){
+ int n1 = sessionSerialLen(a1);
+ int n2 = sessionSerialLen(a2);
+
+ if( pTab->abPK[iCol] && (n1!=n2 || memcmp(a1, a2, n1)) ){
+ return 0;
+ }
+ a1 += n1;
+ a2 += n2;
+ }else{
+ if( bLeftPkOnly==0 ) a1 += sessionSerialLen(a1);
+ if( bRightPkOnly==0 ) a2 += sessionSerialLen(a2);
+ }
+ }
+
+ return 1;
+}
+
+/*
+** Arguments aLeft and aRight both point to buffers containing change
+** records with nCol columns. This function "merges" the two records into
+** a single records which is written to the buffer at *paOut. *paOut is
+** then set to point to one byte after the last byte written before
+** returning.
+**
+** The merging of records is done as follows: For each column, if the
+** aRight record contains a value for the column, copy the value from
+** their. Otherwise, if aLeft contains a value, copy it. If neither
+** record contains a value for a given column, then neither does the
+** output record.
+*/
+static void sessionMergeRecord(
+ u8 **paOut,
+ int nCol,
+ u8 *aLeft,
+ u8 *aRight
+){
+ u8 *a1 = aLeft; /* Cursor used to iterate through aLeft */
+ u8 *a2 = aRight; /* Cursor used to iterate through aRight */
+ u8 *aOut = *paOut; /* Output cursor */
+ int iCol; /* Used to iterate from 0 to nCol */
+
+ for(iCol=0; iCol<nCol; iCol++){
+ int n1 = sessionSerialLen(a1);
+ int n2 = sessionSerialLen(a2);
+ if( *a2 ){
+ memcpy(aOut, a2, n2);
+ aOut += n2;
+ }else{
+ memcpy(aOut, a1, n1);
+ aOut += n1;
+ }
+ a1 += n1;
+ a2 += n2;
+ }
+
+ *paOut = aOut;
+}
+
+/*
+** This is a helper function used by sessionMergeUpdate().
+**
+** When this function is called, both *paOne and *paTwo point to a value
+** within a change record. Before it returns, both have been advanced so
+** as to point to the next value in the record.
+**
+** If, when this function is called, *paTwo points to a valid value (i.e.
+** *paTwo[0] is not 0x00 - the "no value" placeholder), a copy of the *paTwo
+** pointer is returned and *pnVal is set to the number of bytes in the
+** serialized value. Otherwise, a copy of *paOne is returned and *pnVal
+** set to the number of bytes in the value at *paOne. If *paOne points
+** to the "no value" placeholder, *pnVal is set to 1. In other words:
+**
+** if( *paTwo is valid ) return *paTwo;
+** return *paOne;
+**
+*/
+static u8 *sessionMergeValue(
+ u8 **paOne, /* IN/OUT: Left-hand buffer pointer */
+ u8 **paTwo, /* IN/OUT: Right-hand buffer pointer */
+ int *pnVal /* OUT: Bytes in returned value */
+){
+ u8 *a1 = *paOne;
+ u8 *a2 = *paTwo;
+ u8 *pRet = 0;
+ int n1;
+
+ assert( a1 );
+ if( a2 ){
+ int n2 = sessionSerialLen(a2);
+ if( *a2 ){
+ *pnVal = n2;
+ pRet = a2;
+ }
+ *paTwo = &a2[n2];
+ }
+
+ n1 = sessionSerialLen(a1);
+ if( pRet==0 ){
+ *pnVal = n1;
+ pRet = a1;
+ }
+ *paOne = &a1[n1];
+
+ return pRet;
+}
+
+/*
+** This function is used by changeset_concat() to merge two UPDATE changes
+** on the same row.
+*/
+static int sessionMergeUpdate(
+ u8 **paOut, /* IN/OUT: Pointer to output buffer */
+ SessionTable *pTab, /* Table change pertains to */
+ int bPatchset, /* True if records are patchset records */
+ u8 *aOldRecord1, /* old.* record for first change */
+ u8 *aOldRecord2, /* old.* record for second change */
+ u8 *aNewRecord1, /* new.* record for first change */
+ u8 *aNewRecord2 /* new.* record for second change */
+){
+ u8 *aOld1 = aOldRecord1;
+ u8 *aOld2 = aOldRecord2;
+ u8 *aNew1 = aNewRecord1;
+ u8 *aNew2 = aNewRecord2;
+
+ u8 *aOut = *paOut;
+ int i;
+
+ if( bPatchset==0 ){
+ int bRequired = 0;
+
+ assert( aOldRecord1 && aNewRecord1 );
+
+ /* Write the old.* vector first. */
+ for(i=0; i<pTab->nCol; i++){
+ int nOld;
+ u8 *aOld;
+ int nNew;
+ u8 *aNew;
+
+ aOld = sessionMergeValue(&aOld1, &aOld2, &nOld);
+ aNew = sessionMergeValue(&aNew1, &aNew2, &nNew);
+ if( pTab->abPK[i] || nOld!=nNew || memcmp(aOld, aNew, nNew) ){
+ if( pTab->abPK[i]==0 ) bRequired = 1;
+ memcpy(aOut, aOld, nOld);
+ aOut += nOld;
+ }else{
+ *(aOut++) = '\0';
+ }
+ }
+
+ if( !bRequired ) return 0;
+ }
+
+ /* Write the new.* vector */
+ aOld1 = aOldRecord1;
+ aOld2 = aOldRecord2;
+ aNew1 = aNewRecord1;
+ aNew2 = aNewRecord2;
+ for(i=0; i<pTab->nCol; i++){
+ int nOld;
+ u8 *aOld;
+ int nNew;
+ u8 *aNew;
+
+ aOld = sessionMergeValue(&aOld1, &aOld2, &nOld);
+ aNew = sessionMergeValue(&aNew1, &aNew2, &nNew);
+ if( bPatchset==0
+ && (pTab->abPK[i] || (nOld==nNew && 0==memcmp(aOld, aNew, nNew)))
+ ){
+ *(aOut++) = '\0';
+ }else{
+ memcpy(aOut, aNew, nNew);
+ aOut += nNew;
+ }
+ }
+
+ *paOut = aOut;
+ return 1;
+}
+
+/*
+** This function is only called from within a pre-update-hook callback.
+** It determines if the current pre-update-hook change affects the same row
+** as the change stored in argument pChange. If so, it returns true. Otherwise
+** if the pre-update-hook does not affect the same row as pChange, it returns
+** false.
+*/
+static int sessionPreupdateEqual(
+ sqlite3_session *pSession, /* Session object that owns SessionTable */
+ SessionTable *pTab, /* Table associated with change */
+ SessionChange *pChange, /* Change to compare to */
+ int op /* Current pre-update operation */
+){
+ int iCol; /* Used to iterate through columns */
+ u8 *a = pChange->aRecord; /* Cursor used to scan change record */
+
+ assert( op==SQLITE_INSERT || op==SQLITE_UPDATE || op==SQLITE_DELETE );
+ for(iCol=0; iCol<pTab->nCol; iCol++){
+ if( !pTab->abPK[iCol] ){
+ a += sessionSerialLen(a);
+ }else{
+ sqlite3_value *pVal; /* Value returned by preupdate_new/old */
+ int rc; /* Error code from preupdate_new/old */
+ int eType = *a++; /* Type of value from change record */
+
+ /* The following calls to preupdate_new() and preupdate_old() can not
+ ** fail. This is because they cache their return values, and by the
+ ** time control flows to here they have already been called once from
+ ** within sessionPreupdateHash(). The first two asserts below verify
+ ** this (that the method has already been called). */
+ if( op==SQLITE_INSERT ){
+ /* assert( db->pPreUpdate->pNewUnpacked || db->pPreUpdate->aNew ); */
+ rc = pSession->hook.xNew(pSession->hook.pCtx, iCol, &pVal);
+ }else{
+ /* assert( db->pPreUpdate->pUnpacked ); */
+ rc = pSession->hook.xOld(pSession->hook.pCtx, iCol, &pVal);
+ }
+ assert( rc==SQLITE_OK );
+ if( sqlite3_value_type(pVal)!=eType ) return 0;
+
+ /* A SessionChange object never has a NULL value in a PK column */
+ assert( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT
+ || eType==SQLITE_BLOB || eType==SQLITE_TEXT
+ );
+
+ if( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT ){
+ i64 iVal = sessionGetI64(a);
+ a += 8;
+ if( eType==SQLITE_INTEGER ){
+ if( sqlite3_value_int64(pVal)!=iVal ) return 0;
+ }else{
+ double rVal;
+ assert( sizeof(iVal)==8 && sizeof(rVal)==8 );
+ memcpy(&rVal, &iVal, 8);
+ if( sqlite3_value_double(pVal)!=rVal ) return 0;
+ }
+ }else{
+ int n;
+ const u8 *z;
+ a += sessionVarintGet(a, &n);
+ if( sqlite3_value_bytes(pVal)!=n ) return 0;
+ if( eType==SQLITE_TEXT ){
+ z = sqlite3_value_text(pVal);
+ }else{
+ z = sqlite3_value_blob(pVal);
+ }
+ if( memcmp(a, z, n) ) return 0;
+ a += n;
+ break;
+ }
+ }
+ }
+
+ return 1;
+}
+
+/*
+** If required, grow the hash table used to store changes on table pTab
+** (part of the session pSession). If a fatal OOM error occurs, set the
+** session object to failed and return SQLITE_ERROR. Otherwise, return
+** SQLITE_OK.
+**
+** It is possible that a non-fatal OOM error occurs in this function. In
+** that case the hash-table does not grow, but SQLITE_OK is returned anyway.
+** Growing the hash table in this case is a performance optimization only,
+** it is not required for correct operation.
+*/
+static int sessionGrowHash(int bPatchset, SessionTable *pTab){
+ if( pTab->nChange==0 || pTab->nEntry>=(pTab->nChange/2) ){
+ int i;
+ SessionChange **apNew;
+ int nNew = (pTab->nChange ? pTab->nChange : 128) * 2;
+
+ apNew = (SessionChange **)sqlite3_malloc(sizeof(SessionChange *) * nNew);
+ if( apNew==0 ){
+ if( pTab->nChange==0 ){
+ return SQLITE_ERROR;
+ }
+ return SQLITE_OK;
+ }
+ memset(apNew, 0, sizeof(SessionChange *) * nNew);
+
+ for(i=0; i<pTab->nChange; i++){
+ SessionChange *p;
+ SessionChange *pNext;
+ for(p=pTab->apChange[i]; p; p=pNext){
+ int bPkOnly = (p->op==SQLITE_DELETE && bPatchset);
+ int iHash = sessionChangeHash(pTab, bPkOnly, p->aRecord, nNew);
+ pNext = p->pNext;
+ p->pNext = apNew[iHash];
+ apNew[iHash] = p;
+ }
+ }
+
+ sqlite3_free(pTab->apChange);
+ pTab->nChange = nNew;
+ pTab->apChange = apNew;
+ }
+
+ return SQLITE_OK;
+}
+
+/*
+** This function queries the database for the names of the columns of table
+** zThis, in schema zDb. It is expected that the table has nCol columns. If
+** not, SQLITE_SCHEMA is returned and none of the output variables are
+** populated.
+**
+** Otherwise, if they are not NULL, variable *pnCol is set to the number
+** of columns in the database table and variable *pzTab is set to point to a
+** nul-terminated copy of the table name. *pazCol (if not NULL) is set to
+** point to an array of pointers to column names. And *pabPK (again, if not
+** NULL) is set to point to an array of booleans - true if the corresponding
+** column is part of the primary key.
+**
+** For example, if the table is declared as:
+**
+** CREATE TABLE tbl1(w, x, y, z, PRIMARY KEY(w, z));
+**
+** Then the four output variables are populated as follows:
+**
+** *pnCol = 4
+** *pzTab = "tbl1"
+** *pazCol = {"w", "x", "y", "z"}
+** *pabPK = {1, 0, 0, 1}
+**
+** All returned buffers are part of the same single allocation, which must
+** be freed using sqlite3_free() by the caller. If pazCol was not NULL, then
+** pointer *pazCol should be freed to release all memory. Otherwise, pointer
+** *pabPK. It is illegal for both pazCol and pabPK to be NULL.
+*/
+static int sessionTableInfo(
+ sqlite3 *db, /* Database connection */
+ const char *zDb, /* Name of attached database (e.g. "main") */
+ const char *zThis, /* Table name */
+ int *pnCol, /* OUT: number of columns */
+ const char **pzTab, /* OUT: Copy of zThis */
+ const char ***pazCol, /* OUT: Array of column names for table */
+ u8 **pabPK /* OUT: Array of booleans - true for PK col */
+){
+ char *zPragma;
+ sqlite3_stmt *pStmt;
+ int rc;
+ int nByte;
+ int nDbCol = 0;
+ int nThis;
+ int i;
+ u8 *pAlloc = 0;
+ char **azCol = 0;
+ u8 *abPK = 0;
+
+ assert( pazCol && pabPK );
+
+ nThis = sqlite3Strlen30(zThis);
+ zPragma = sqlite3_mprintf("PRAGMA '%q'.table_info('%q')", zDb, zThis);
+ if( !zPragma ) return SQLITE_NOMEM;
+
+ rc = sqlite3_prepare_v2(db, zPragma, -1, &pStmt, 0);
+ sqlite3_free(zPragma);
+ if( rc!=SQLITE_OK ) return rc;
+
+ nByte = nThis + 1;
+ while( SQLITE_ROW==sqlite3_step(pStmt) ){
+ nByte += sqlite3_column_bytes(pStmt, 1);
+ nDbCol++;
+ }
+ rc = sqlite3_reset(pStmt);
+
+ if( rc==SQLITE_OK ){
+ nByte += nDbCol * (sizeof(const char *) + sizeof(u8) + 1);
+ pAlloc = sqlite3_malloc(nByte);
+ if( pAlloc==0 ){
+ rc = SQLITE_NOMEM;
+ }
+ }
+ if( rc==SQLITE_OK ){
+ azCol = (char **)pAlloc;
+ pAlloc = (u8 *)&azCol[nDbCol];
+ abPK = (u8 *)pAlloc;
+ pAlloc = &abPK[nDbCol];
+ if( pzTab ){
+ memcpy(pAlloc, zThis, nThis+1);
+ *pzTab = (char *)pAlloc;
+ pAlloc += nThis+1;
+ }
+
+ i = 0;
+ while( SQLITE_ROW==sqlite3_step(pStmt) ){
+ int nName = sqlite3_column_bytes(pStmt, 1);
+ const unsigned char *zName = sqlite3_column_text(pStmt, 1);
+ if( zName==0 ) break;
+ memcpy(pAlloc, zName, nName+1);
+ azCol[i] = (char *)pAlloc;
+ pAlloc += nName+1;
+ abPK[i] = sqlite3_column_int(pStmt, 5);
+ i++;
+ }
+ rc = sqlite3_reset(pStmt);
+
+ }
+
+ /* If successful, populate the output variables. Otherwise, zero them and
+ ** free any allocation made. An error code will be returned in this case.
+ */
+ if( rc==SQLITE_OK ){
+ *pazCol = (const char **)azCol;
+ *pabPK = abPK;
+ *pnCol = nDbCol;
+ }else{
+ *pazCol = 0;
+ *pabPK = 0;
+ *pnCol = 0;
+ if( pzTab ) *pzTab = 0;
+ sqlite3_free(azCol);
+ }
+ sqlite3_finalize(pStmt);
+ return rc;
+}
+
+/*
+** This function is only called from within a pre-update handler for a
+** write to table pTab, part of session pSession. If this is the first
+** write to this table, initalize the SessionTable.nCol, azCol[] and
+** abPK[] arrays accordingly.
+**
+** If an error occurs, an error code is stored in sqlite3_session.rc and
+** non-zero returned. Or, if no error occurs but the table has no primary
+** key, sqlite3_session.rc is left set to SQLITE_OK and non-zero returned to
+** indicate that updates on this table should be ignored. SessionTable.abPK
+** is set to NULL in this case.
+*/
+static int sessionInitTable(sqlite3_session *pSession, SessionTable *pTab){
+ if( pTab->nCol==0 ){
+ u8 *abPK;
+ assert( pTab->azCol==0 || pTab->abPK==0 );
+ pSession->rc = sessionTableInfo(pSession->db, pSession->zDb,
+ pTab->zName, &pTab->nCol, 0, &pTab->azCol, &abPK
+ );
+ if( pSession->rc==SQLITE_OK ){
+ int i;
+ for(i=0; i<pTab->nCol; i++){
+ if( abPK[i] ){
+ pTab->abPK = abPK;
+ break;
+ }
+ }
+ }
+ }
+ return (pSession->rc || pTab->abPK==0);
+}
+
+/*
+** This function is only called from with a pre-update-hook reporting a
+** change on table pTab (attached to session pSession). The type of change
+** (UPDATE, INSERT, DELETE) is specified by the first argument.
+**
+** Unless one is already present or an error occurs, an entry is added
+** to the changed-rows hash table associated with table pTab.
+*/
+static void sessionPreupdateOneChange(
+ int op, /* One of SQLITE_UPDATE, INSERT, DELETE */
+ sqlite3_session *pSession, /* Session object pTab is attached to */
+ SessionTable *pTab /* Table that change applies to */
+){
+ int iHash;
+ int bNull = 0;
+ int rc = SQLITE_OK;
+
+ if( pSession->rc ) return;
+
+ /* Load table details if required */
+ if( sessionInitTable(pSession, pTab) ) return;
+
+ /* Check the number of columns in this xPreUpdate call matches the
+ ** number of columns in the table. */
+ if( pTab->nCol!=pSession->hook.xCount(pSession->hook.pCtx) ){
+ pSession->rc = SQLITE_SCHEMA;
+ return;
+ }
+
+ /* Grow the hash table if required */
+ if( sessionGrowHash(0, pTab) ){
+ pSession->rc = SQLITE_NOMEM;
+ return;
+ }
+
+ /* Calculate the hash-key for this change. If the primary key of the row
+ ** includes a NULL value, exit early. Such changes are ignored by the
+ ** session module. */
+ rc = sessionPreupdateHash(pSession, pTab, op==SQLITE_INSERT, &iHash, &bNull);
+ if( rc!=SQLITE_OK ) goto error_out;
+
+ if( bNull==0 ){
+ /* Search the hash table for an existing record for this row. */
+ SessionChange *pC;
+ for(pC=pTab->apChange[iHash]; pC; pC=pC->pNext){
+ if( sessionPreupdateEqual(pSession, pTab, pC, op) ) break;
+ }
+
+ if( pC==0 ){
+ /* Create a new change object containing all the old values (if
+ ** this is an SQLITE_UPDATE or SQLITE_DELETE), or just the PK
+ ** values (if this is an INSERT). */
+ SessionChange *pChange; /* New change object */
+ int nByte; /* Number of bytes to allocate */
+ int i; /* Used to iterate through columns */
+
+ assert( rc==SQLITE_OK );
+ pTab->nEntry++;
+
+ /* Figure out how large an allocation is required */
+ nByte = sizeof(SessionChange);
+ for(i=0; i<pTab->nCol; i++){
+ sqlite3_value *p = 0;
+ if( op!=SQLITE_INSERT ){
+ TESTONLY(int trc = ) pSession->hook.xOld(pSession->hook.pCtx, i, &p);
+ assert( trc==SQLITE_OK );
+ }else if( pTab->abPK[i] ){
+ TESTONLY(int trc = ) pSession->hook.xNew(pSession->hook.pCtx, i, &p);
+ assert( trc==SQLITE_OK );
+ }
+
+ /* This may fail if SQLite value p contains a utf-16 string that must
+ ** be converted to utf-8 and an OOM error occurs while doing so. */
+ rc = sessionSerializeValue(0, p, &nByte);
+ if( rc!=SQLITE_OK ) goto error_out;
+ }
+
+ /* Allocate the change object */
+ pChange = (SessionChange *)sqlite3_malloc(nByte);
+ if( !pChange ){
+ rc = SQLITE_NOMEM;
+ goto error_out;
+ }else{
+ memset(pChange, 0, sizeof(SessionChange));
+ pChange->aRecord = (u8 *)&pChange[1];
+ }
+
+ /* Populate the change object. None of the preupdate_old(),
+ ** preupdate_new() or SerializeValue() calls below may fail as all
+ ** required values and encodings have already been cached in memory.
+ ** It is not possible for an OOM to occur in this block. */
+ nByte = 0;
+ for(i=0; i<pTab->nCol; i++){
+ sqlite3_value *p = 0;
+ if( op!=SQLITE_INSERT ){
+ pSession->hook.xOld(pSession->hook.pCtx, i, &p);
+ }else if( pTab->abPK[i] ){
+ pSession->hook.xNew(pSession->hook.pCtx, i, &p);
+ }
+ sessionSerializeValue(&pChange->aRecord[nByte], p, &nByte);
+ }
+
+ /* Add the change to the hash-table */
+ if( pSession->bIndirect || pSession->hook.xDepth(pSession->hook.pCtx) ){
+ pChange->bIndirect = 1;
+ }
+ pChange->nRecord = nByte;
+ pChange->op = op;
+ pChange->pNext = pTab->apChange[iHash];
+ pTab->apChange[iHash] = pChange;
+
+ }else if( pC->bIndirect ){
+ /* If the existing change is considered "indirect", but this current
+ ** change is "direct", mark the change object as direct. */
+ if( pSession->hook.xDepth(pSession->hook.pCtx)==0
+ && pSession->bIndirect==0
+ ){
+ pC->bIndirect = 0;
+ }
+ }
+ }
+
+ /* If an error has occurred, mark the session object as failed. */
+ error_out:
+ if( rc!=SQLITE_OK ){
+ pSession->rc = rc;
+ }
+}
+
+static int sessionFindTable(
+ sqlite3_session *pSession,
+ const char *zName,
+ SessionTable **ppTab
+){
+ int rc = SQLITE_OK;
+ int nName = sqlite3Strlen30(zName);
+ SessionTable *pRet;
+
+ /* Search for an existing table */
+ for(pRet=pSession->pTable; pRet; pRet=pRet->pNext){
+ if( 0==sqlite3_strnicmp(pRet->zName, zName, nName+1) ) break;
+ }
+
+ if( pRet==0 && pSession->bAutoAttach ){
+ /* If there is a table-filter configured, invoke it. If it returns 0,
+ ** do not automatically add the new table. */
+ if( pSession->xTableFilter==0
+ || pSession->xTableFilter(pSession->pFilterCtx, zName)
+ ){
+ rc = sqlite3session_attach(pSession, zName);
+ if( rc==SQLITE_OK ){
+ for(pRet=pSession->pTable; pRet->pNext; pRet=pRet->pNext);
+ assert( 0==sqlite3_strnicmp(pRet->zName, zName, nName+1) );
+ }
+ }
+ }
+
+ assert( rc==SQLITE_OK || pRet==0 );
+ *ppTab = pRet;
+ return rc;
+}
+
+/*
+** The 'pre-update' hook registered by this module with SQLite databases.
+*/
+static void xPreUpdate(
+ void *pCtx, /* Copy of third arg to preupdate_hook() */
+ sqlite3 *db, /* Database handle */
+ int op, /* SQLITE_UPDATE, DELETE or INSERT */
+ char const *zDb, /* Database name */
+ char const *zName, /* Table name */
+ sqlite3_int64 iKey1, /* Rowid of row about to be deleted/updated */
+ sqlite3_int64 iKey2 /* New rowid value (for a rowid UPDATE) */
+){
+ sqlite3_session *pSession;
+ int nDb = sqlite3Strlen30(zDb);
+
+ assert( sqlite3_mutex_held(db->mutex) );
+
+ for(pSession=(sqlite3_session *)pCtx; pSession; pSession=pSession->pNext){
+ SessionTable *pTab;
+
+ /* If this session is attached to a different database ("main", "temp"
+ ** etc.), or if it is not currently enabled, there is nothing to do. Skip
+ ** to the next session object attached to this database. */
+ if( pSession->bEnable==0 ) continue;
+ if( pSession->rc ) continue;
+ if( sqlite3_strnicmp(zDb, pSession->zDb, nDb+1) ) continue;
+
+ pSession->rc = sessionFindTable(pSession, zName, &pTab);
+ if( pTab ){
+ assert( pSession->rc==SQLITE_OK );
+ sessionPreupdateOneChange(op, pSession, pTab);
+ if( op==SQLITE_UPDATE ){
+ sessionPreupdateOneChange(SQLITE_INSERT, pSession, pTab);
+ }
+ }
+ }
+}
+
+/*
+** The pre-update hook implementations.
+*/
+static int sessionPreupdateOld(void *pCtx, int iVal, sqlite3_value **ppVal){
+ return sqlite3_preupdate_old((sqlite3*)pCtx, iVal, ppVal);
+}
+static int sessionPreupdateNew(void *pCtx, int iVal, sqlite3_value **ppVal){
+ return sqlite3_preupdate_new((sqlite3*)pCtx, iVal, ppVal);
+}
+static int sessionPreupdateCount(void *pCtx){
+ return sqlite3_preupdate_count((sqlite3*)pCtx);
+}
+static int sessionPreupdateDepth(void *pCtx){
+ return sqlite3_preupdate_depth((sqlite3*)pCtx);
+}
+
+/*
+** Install the pre-update hooks on the session object passed as the only
+** argument.
+*/
+static void sessionPreupdateHooks(
+ sqlite3_session *pSession
+){
+ pSession->hook.pCtx = (void*)pSession->db;
+ pSession->hook.xOld = sessionPreupdateOld;
+ pSession->hook.xNew = sessionPreupdateNew;
+ pSession->hook.xCount = sessionPreupdateCount;
+ pSession->hook.xDepth = sessionPreupdateDepth;
+}
+
+typedef struct SessionDiffCtx SessionDiffCtx;
+struct SessionDiffCtx {
+ sqlite3_stmt *pStmt;
+ int nOldOff;
+};
+
+/*
+** The diff hook implementations.
+*/
+static int sessionDiffOld(void *pCtx, int iVal, sqlite3_value **ppVal){
+ SessionDiffCtx *p = (SessionDiffCtx*)pCtx;
+ *ppVal = sqlite3_column_value(p->pStmt, iVal+p->nOldOff);
+ return SQLITE_OK;
+}
+static int sessionDiffNew(void *pCtx, int iVal, sqlite3_value **ppVal){
+ SessionDiffCtx *p = (SessionDiffCtx*)pCtx;
+ *ppVal = sqlite3_column_value(p->pStmt, iVal);
+ return SQLITE_OK;
+}
+static int sessionDiffCount(void *pCtx){
+ SessionDiffCtx *p = (SessionDiffCtx*)pCtx;
+ return p->nOldOff ? p->nOldOff : sqlite3_column_count(p->pStmt);
+}
+static int sessionDiffDepth(void *pCtx){
+ return 0;
+}
+
+/*
+** Install the diff hooks on the session object passed as the only
+** argument.
+*/
+static void sessionDiffHooks(
+ sqlite3_session *pSession,
+ SessionDiffCtx *pDiffCtx
+){
+ pSession->hook.pCtx = (void*)pDiffCtx;
+ pSession->hook.xOld = sessionDiffOld;
+ pSession->hook.xNew = sessionDiffNew;
+ pSession->hook.xCount = sessionDiffCount;
+ pSession->hook.xDepth = sessionDiffDepth;
+}
+
+static char *sessionExprComparePK(
+ int nCol,
+ const char *zDb1, const char *zDb2,
+ const char *zTab,
+ const char **azCol, u8 *abPK
+){
+ int i;
+ const char *zSep = "";
+ char *zRet = 0;
+
+ for(i=0; i<nCol; i++){
+ if( abPK[i] ){
+ zRet = sqlite3_mprintf("%z%s\"%w\".\"%w\".\"%w\"=\"%w\".\"%w\".\"%w\"",
+ zRet, zSep, zDb1, zTab, azCol[i], zDb2, zTab, azCol[i]
+ );
+ zSep = " AND ";
+ if( zRet==0 ) break;
+ }
+ }
+
+ return zRet;
+}
+
+static char *sessionExprCompareOther(
+ int nCol,
+ const char *zDb1, const char *zDb2,
+ const char *zTab,
+ const char **azCol, u8 *abPK
+){
+ int i;
+ const char *zSep = "";
+ char *zRet = 0;
+ int bHave = 0;
+
+ for(i=0; i<nCol; i++){
+ if( abPK[i]==0 ){
+ bHave = 1;
+ zRet = sqlite3_mprintf(
+ "%z%s\"%w\".\"%w\".\"%w\" IS NOT \"%w\".\"%w\".\"%w\"",
+ zRet, zSep, zDb1, zTab, azCol[i], zDb2, zTab, azCol[i]
+ );
+ zSep = " OR ";
+ if( zRet==0 ) break;
+ }
+ }
+
+ if( bHave==0 ){
+ assert( zRet==0 );
+ zRet = sqlite3_mprintf("0");
+ }
+
+ return zRet;
+}
+
+static char *sessionSelectFindNew(
+ int nCol,
+ const char *zDb1, /* Pick rows in this db only */
+ const char *zDb2, /* But not in this one */
+ const char *zTbl, /* Table name */
+ const char *zExpr
+){
+ char *zRet = sqlite3_mprintf(
+ "SELECT * FROM \"%w\".\"%w\" WHERE NOT EXISTS ("
+ " SELECT 1 FROM \"%w\".\"%w\" WHERE %s"
+ ")",
+ zDb1, zTbl, zDb2, zTbl, zExpr
+ );
+ return zRet;
+}
+
+static int sessionDiffFindNew(
+ int op,
+ sqlite3_session *pSession,
+ SessionTable *pTab,
+ const char *zDb1,
+ const char *zDb2,
+ char *zExpr
+){
+ int rc = SQLITE_OK;
+ char *zStmt = sessionSelectFindNew(pTab->nCol, zDb1, zDb2, pTab->zName,zExpr);
+
+ if( zStmt==0 ){
+ rc = SQLITE_NOMEM;
+ }else{
+ sqlite3_stmt *pStmt;
+ rc = sqlite3_prepare(pSession->db, zStmt, -1, &pStmt, 0);
+ if( rc==SQLITE_OK ){
+ SessionDiffCtx *pDiffCtx = (SessionDiffCtx*)pSession->hook.pCtx;
+ pDiffCtx->pStmt = pStmt;
+ pDiffCtx->nOldOff = 0;
+ while( SQLITE_ROW==sqlite3_step(pStmt) ){
+ sessionPreupdateOneChange(op, pSession, pTab);
+ }
+ rc = sqlite3_finalize(pStmt);
+ }
+ sqlite3_free(zStmt);
+ }
+
+ return rc;
+}
+
+static int sessionDiffFindModified(
+ sqlite3_session *pSession,
+ SessionTable *pTab,
+ const char *zFrom,
+ const char *zExpr
+){
+ int rc = SQLITE_OK;
+
+ char *zExpr2 = sessionExprCompareOther(pTab->nCol,
+ pSession->zDb, zFrom, pTab->zName, pTab->azCol, pTab->abPK
+ );
+ if( zExpr2==0 ){
+ rc = SQLITE_NOMEM;
+ }else{
+ char *zStmt = sqlite3_mprintf(
+ "SELECT * FROM \"%w\".\"%w\", \"%w\".\"%w\" WHERE %s AND (%z)",
+ pSession->zDb, pTab->zName, zFrom, pTab->zName, zExpr, zExpr2
+ );
+ if( zStmt==0 ){
+ rc = SQLITE_NOMEM;
+ }else{
+ sqlite3_stmt *pStmt;
+ rc = sqlite3_prepare(pSession->db, zStmt, -1, &pStmt, 0);
+
+ if( rc==SQLITE_OK ){
+ SessionDiffCtx *pDiffCtx = (SessionDiffCtx*)pSession->hook.pCtx;
+ pDiffCtx->pStmt = pStmt;
+ pDiffCtx->nOldOff = pTab->nCol;
+ while( SQLITE_ROW==sqlite3_step(pStmt) ){
+ sessionPreupdateOneChange(SQLITE_UPDATE, pSession, pTab);
+ }
+ rc = sqlite3_finalize(pStmt);
+ }
+ sqlite3_free(zStmt);
+ }
+ }
+
+ return rc;
+}
+
+SQLITE_API int SQLITE_STDCALL sqlite3session_diff(
+ sqlite3_session *pSession,
+ const char *zFrom,
+ const char *zTbl,
+ char **pzErrMsg
+){
+ const char *zDb = pSession->zDb;
+ int rc = pSession->rc;
+ SessionDiffCtx d;
+
+ memset(&d, 0, sizeof(d));
+ sessionDiffHooks(pSession, &d);
+
+ sqlite3_mutex_enter(sqlite3_db_mutex(pSession->db));
+ if( pzErrMsg ) *pzErrMsg = 0;
+ if( rc==SQLITE_OK ){
+ char *zExpr = 0;
+ sqlite3 *db = pSession->db;
+ SessionTable *pTo; /* Table zTbl */
+
+ /* Locate and if necessary initialize the target table object */
+ rc = sessionFindTable(pSession, zTbl, &pTo);
+ if( pTo==0 ) goto diff_out;
+ if( sessionInitTable(pSession, pTo) ){
+ rc = pSession->rc;
+ goto diff_out;
+ }
+
+ /* Check the table schemas match */
+ if( rc==SQLITE_OK ){
+ int bHasPk = 0;
+ int bMismatch = 0;
+ int nCol; /* Columns in zFrom.zTbl */
+ u8 *abPK;
+ const char **azCol = 0;
+ rc = sessionTableInfo(db, zFrom, zTbl, &nCol, 0, &azCol, &abPK);
+ if( rc==SQLITE_OK ){
+ if( pTo->nCol!=nCol ){
+ bMismatch = 1;
+ }else{
+ int i;
+ for(i=0; i<nCol; i++){
+ if( pTo->abPK[i]!=abPK[i] ) bMismatch = 1;
+ if( sqlite3_stricmp(azCol[i], pTo->azCol[i]) ) bMismatch = 1;
+ if( abPK[i] ) bHasPk = 1;
+ }
+ }
+
+ }
+ sqlite3_free((char*)azCol);
+ if( bMismatch ){
+ *pzErrMsg = sqlite3_mprintf("table schemas do not match");
+ rc = SQLITE_SCHEMA;
+ }
+ if( bHasPk==0 ){
+ /* Ignore tables with no primary keys */
+ goto diff_out;
+ }
+ }
+
+ if( rc==SQLITE_OK ){
+ zExpr = sessionExprComparePK(pTo->nCol,
+ zDb, zFrom, pTo->zName, pTo->azCol, pTo->abPK
+ );
+ }
+
+ /* Find new rows */
+ if( rc==SQLITE_OK ){
+ rc = sessionDiffFindNew(SQLITE_INSERT, pSession, pTo, zDb, zFrom, zExpr);
+ }
+
+ /* Find old rows */
+ if( rc==SQLITE_OK ){
+ rc = sessionDiffFindNew(SQLITE_DELETE, pSession, pTo, zFrom, zDb, zExpr);
+ }
+
+ /* Find modified rows */
+ if( rc==SQLITE_OK ){
+ rc = sessionDiffFindModified(pSession, pTo, zFrom, zExpr);
+ }
+
+ sqlite3_free(zExpr);
+ }
+
+ diff_out:
+ sessionPreupdateHooks(pSession);
+ sqlite3_mutex_leave(sqlite3_db_mutex(pSession->db));
+ return rc;
+}
+
+/*
+** Create a session object. This session object will record changes to
+** database zDb attached to connection db.
+*/
+SQLITE_API int SQLITE_STDCALL sqlite3session_create(
+ sqlite3 *db, /* Database handle */
+ const char *zDb, /* Name of db (e.g. "main") */
+ sqlite3_session **ppSession /* OUT: New session object */
+){
+ sqlite3_session *pNew; /* Newly allocated session object */
+ sqlite3_session *pOld; /* Session object already attached to db */
+ int nDb = sqlite3Strlen30(zDb); /* Length of zDb in bytes */
+
+ /* Zero the output value in case an error occurs. */
+ *ppSession = 0;
+
+ /* Allocate and populate the new session object. */
+ pNew = (sqlite3_session *)sqlite3_malloc(sizeof(sqlite3_session) + nDb + 1);
+ if( !pNew ) return SQLITE_NOMEM;
+ memset(pNew, 0, sizeof(sqlite3_session));
+ pNew->db = db;
+ pNew->zDb = (char *)&pNew[1];
+ pNew->bEnable = 1;
+ memcpy(pNew->zDb, zDb, nDb+1);
+ sessionPreupdateHooks(pNew);
+
+ /* Add the new session object to the linked list of session objects
+ ** attached to database handle $db. Do this under the cover of the db
+ ** handle mutex. */
+ sqlite3_mutex_enter(sqlite3_db_mutex(db));
+ pOld = (sqlite3_session*)sqlite3_preupdate_hook(db, xPreUpdate, (void*)pNew);
+ pNew->pNext = pOld;
+ sqlite3_mutex_leave(sqlite3_db_mutex(db));
+
+ *ppSession = pNew;
+ return SQLITE_OK;
+}
+
+/*
+** Free the list of table objects passed as the first argument. The contents
+** of the changed-rows hash tables are also deleted.
+*/
+static void sessionDeleteTable(SessionTable *pList){
+ SessionTable *pNext;
+ SessionTable *pTab;
+
+ for(pTab=pList; pTab; pTab=pNext){
+ int i;
+ pNext = pTab->pNext;
+ for(i=0; i<pTab->nChange; i++){
+ SessionChange *p;
+ SessionChange *pNextChange;
+ for(p=pTab->apChange[i]; p; p=pNextChange){
+ pNextChange = p->pNext;
+ sqlite3_free(p);
+ }
+ }
+ sqlite3_free((char*)pTab->azCol); /* cast works around VC++ bug */
+ sqlite3_free(pTab->apChange);
+ sqlite3_free(pTab);
+ }
+}
+
+/*
+** Delete a session object previously allocated using sqlite3session_create().
+*/
+SQLITE_API void SQLITE_STDCALL sqlite3session_delete(sqlite3_session *pSession){
+ sqlite3 *db = pSession->db;
+ sqlite3_session *pHead;
+ sqlite3_session **pp;
+
+ /* Unlink the session from the linked list of sessions attached to the
+ ** database handle. Hold the db mutex while doing so. */
+ sqlite3_mutex_enter(sqlite3_db_mutex(db));
+ pHead = (sqlite3_session*)sqlite3_preupdate_hook(db, 0, 0);
+ for(pp=&pHead; ALWAYS((*pp)!=0); pp=&((*pp)->pNext)){
+ if( (*pp)==pSession ){
+ *pp = (*pp)->pNext;
+ if( pHead ) sqlite3_preupdate_hook(db, xPreUpdate, (void*)pHead);
+ break;
+ }
+ }
+ sqlite3_mutex_leave(sqlite3_db_mutex(db));
+
+ /* Delete all attached table objects. And the contents of their
+ ** associated hash-tables. */
+ sessionDeleteTable(pSession->pTable);
+
+ /* Free the session object itself. */
+ sqlite3_free(pSession);
+}
+
+/*
+** Set a table filter on a Session Object.
+*/
+SQLITE_API void SQLITE_STDCALL sqlite3session_table_filter(
+ sqlite3_session *pSession,
+ int(*xFilter)(void*, const char*),
+ void *pCtx /* First argument passed to xFilter */
+){
+ pSession->bAutoAttach = 1;
+ pSession->pFilterCtx = pCtx;
+ pSession->xTableFilter = xFilter;
+}
+
+/*
+** Attach a table to a session. All subsequent changes made to the table
+** while the session object is enabled will be recorded.
+**
+** Only tables that have a PRIMARY KEY defined may be attached. It does
+** not matter if the PRIMARY KEY is an "INTEGER PRIMARY KEY" (rowid alias)
+** or not.
+*/
+SQLITE_API int SQLITE_STDCALL sqlite3session_attach(
+ sqlite3_session *pSession, /* Session object */
+ const char *zName /* Table name */
+){
+ int rc = SQLITE_OK;
+ sqlite3_mutex_enter(sqlite3_db_mutex(pSession->db));
+
+ if( !zName ){
+ pSession->bAutoAttach = 1;
+ }else{
+ SessionTable *pTab; /* New table object (if required) */
+ int nName; /* Number of bytes in string zName */
+
+ /* First search for an existing entry. If one is found, this call is
+ ** a no-op. Return early. */
+ nName = sqlite3Strlen30(zName);
+ for(pTab=pSession->pTable; pTab; pTab=pTab->pNext){
+ if( 0==sqlite3_strnicmp(pTab->zName, zName, nName+1) ) break;
+ }
+
+ if( !pTab ){
+ /* Allocate new SessionTable object. */
+ pTab = (SessionTable *)sqlite3_malloc(sizeof(SessionTable) + nName + 1);
+ if( !pTab ){
+ rc = SQLITE_NOMEM;
+ }else{
+ /* Populate the new SessionTable object and link it into the list.
+ ** The new object must be linked onto the end of the list, not
+ ** simply added to the start of it in order to ensure that tables
+ ** appear in the correct order when a changeset or patchset is
+ ** eventually generated. */
+ SessionTable **ppTab;
+ memset(pTab, 0, sizeof(SessionTable));
+ pTab->zName = (char *)&pTab[1];
+ memcpy(pTab->zName, zName, nName+1);
+ for(ppTab=&pSession->pTable; *ppTab; ppTab=&(*ppTab)->pNext);
+ *ppTab = pTab;
+ }
+ }
+ }
+
+ sqlite3_mutex_leave(sqlite3_db_mutex(pSession->db));
+ return rc;
+}
+
+/*
+** Ensure that there is room in the buffer to append nByte bytes of data.
+** If not, use sqlite3_realloc() to grow the buffer so that there is.
+**
+** If successful, return zero. Otherwise, if an OOM condition is encountered,
+** set *pRc to SQLITE_NOMEM and return non-zero.
+*/
+static int sessionBufferGrow(SessionBuffer *p, int nByte, int *pRc){
+ if( *pRc==SQLITE_OK && p->nAlloc-p->nBuf<nByte ){
+ u8 *aNew;
+ int nNew = p->nAlloc ? p->nAlloc : 128;
+ do {
+ nNew = nNew*2;
+ }while( nNew<(p->nBuf+nByte) );
+
+ aNew = (u8 *)sqlite3_realloc(p->aBuf, nNew);
+ if( 0==aNew ){
+ *pRc = SQLITE_NOMEM;
+ }else{
+ p->aBuf = aNew;
+ p->nAlloc = nNew;
+ }
+ }
+ return (*pRc!=SQLITE_OK);
+}
+
+/*
+** Append the value passed as the second argument to the buffer passed
+** as the first.
+**
+** This function is a no-op if *pRc is non-zero when it is called.
+** Otherwise, if an error occurs, *pRc is set to an SQLite error code
+** before returning.
+*/
+static void sessionAppendValue(SessionBuffer *p, sqlite3_value *pVal, int *pRc){
+ int rc = *pRc;
+ if( rc==SQLITE_OK ){
+ int nByte = 0;
+ rc = sessionSerializeValue(0, pVal, &nByte);
+ sessionBufferGrow(p, nByte, &rc);
+ if( rc==SQLITE_OK ){
+ rc = sessionSerializeValue(&p->aBuf[p->nBuf], pVal, 0);
+ p->nBuf += nByte;
+ }else{
+ *pRc = rc;
+ }
+ }
+}
+
+/*
+** This function is a no-op if *pRc is other than SQLITE_OK when it is
+** called. Otherwise, append a single byte to the buffer.
+**
+** If an OOM condition is encountered, set *pRc to SQLITE_NOMEM before
+** returning.
+*/
+static void sessionAppendByte(SessionBuffer *p, u8 v, int *pRc){
+ if( 0==sessionBufferGrow(p, 1, pRc) ){
+ p->aBuf[p->nBuf++] = v;
+ }
+}
+
+/*
+** This function is a no-op if *pRc is other than SQLITE_OK when it is
+** called. Otherwise, append a single varint to the buffer.
+**
+** If an OOM condition is encountered, set *pRc to SQLITE_NOMEM before
+** returning.
+*/
+static void sessionAppendVarint(SessionBuffer *p, int v, int *pRc){
+ if( 0==sessionBufferGrow(p, 9, pRc) ){
+ p->nBuf += sessionVarintPut(&p->aBuf[p->nBuf], v);
+ }
+}
+
+/*
+** This function is a no-op if *pRc is other than SQLITE_OK when it is
+** called. Otherwise, append a blob of data to the buffer.
+**
+** If an OOM condition is encountered, set *pRc to SQLITE_NOMEM before
+** returning.
+*/
+static void sessionAppendBlob(
+ SessionBuffer *p,
+ const u8 *aBlob,
+ int nBlob,
+ int *pRc
+){
+ if( 0==sessionBufferGrow(p, nBlob, pRc) ){
+ memcpy(&p->aBuf[p->nBuf], aBlob, nBlob);
+ p->nBuf += nBlob;
+ }
+}
+
+/*
+** This function is a no-op if *pRc is other than SQLITE_OK when it is
+** called. Otherwise, append a string to the buffer. All bytes in the string
+** up to (but not including) the nul-terminator are written to the buffer.
+**
+** If an OOM condition is encountered, set *pRc to SQLITE_NOMEM before
+** returning.
+*/
+static void sessionAppendStr(
+ SessionBuffer *p,
+ const char *zStr,
+ int *pRc
+){
+ int nStr = sqlite3Strlen30(zStr);
+ if( 0==sessionBufferGrow(p, nStr, pRc) ){
+ memcpy(&p->aBuf[p->nBuf], zStr, nStr);
+ p->nBuf += nStr;
+ }
+}
+
+/*
+** This function is a no-op if *pRc is other than SQLITE_OK when it is
+** called. Otherwise, append the string representation of integer iVal
+** to the buffer. No nul-terminator is written.
+**
+** If an OOM condition is encountered, set *pRc to SQLITE_NOMEM before
+** returning.
+*/
+static void sessionAppendInteger(
+ SessionBuffer *p, /* Buffer to append to */
+ int iVal, /* Value to write the string rep. of */
+ int *pRc /* IN/OUT: Error code */
+){
+ char aBuf[24];
+ sqlite3_snprintf(sizeof(aBuf)-1, aBuf, "%d", iVal);
+ sessionAppendStr(p, aBuf, pRc);
+}
+
+/*
+** This function is a no-op if *pRc is other than SQLITE_OK when it is
+** called. Otherwise, append the string zStr enclosed in quotes (") and
+** with any embedded quote characters escaped to the buffer. No
+** nul-terminator byte is written.
+**
+** If an OOM condition is encountered, set *pRc to SQLITE_NOMEM before
+** returning.
+*/
+static void sessionAppendIdent(
+ SessionBuffer *p, /* Buffer to a append to */
+ const char *zStr, /* String to quote, escape and append */
+ int *pRc /* IN/OUT: Error code */
+){
+ int nStr = sqlite3Strlen30(zStr)*2 + 2 + 1;
+ if( 0==sessionBufferGrow(p, nStr, pRc) ){
+ char *zOut = (char *)&p->aBuf[p->nBuf];
+ const char *zIn = zStr;
+ *zOut++ = '"';
+ while( *zIn ){
+ if( *zIn=='"' ) *zOut++ = '"';
+ *zOut++ = *(zIn++);
+ }
+ *zOut++ = '"';
+ p->nBuf = (int)((u8 *)zOut - p->aBuf);
+ }
+}
+
+/*
+** This function is a no-op if *pRc is other than SQLITE_OK when it is
+** called. Otherwse, it appends the serialized version of the value stored
+** in column iCol of the row that SQL statement pStmt currently points
+** to to the buffer.
+*/
+static void sessionAppendCol(
+ SessionBuffer *p, /* Buffer to append to */
+ sqlite3_stmt *pStmt, /* Handle pointing to row containing value */
+ int iCol, /* Column to read value from */
+ int *pRc /* IN/OUT: Error code */
+){
+ if( *pRc==SQLITE_OK ){
+ int eType = sqlite3_column_type(pStmt, iCol);
+ sessionAppendByte(p, (u8)eType, pRc);
+ if( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT ){
+ sqlite3_int64 i;
+ u8 aBuf[8];
+ if( eType==SQLITE_INTEGER ){
+ i = sqlite3_column_int64(pStmt, iCol);
+ }else{
+ double r = sqlite3_column_double(pStmt, iCol);
+ memcpy(&i, &r, 8);
+ }
+ sessionPutI64(aBuf, i);
+ sessionAppendBlob(p, aBuf, 8, pRc);
+ }
+ if( eType==SQLITE_BLOB || eType==SQLITE_TEXT ){
+ u8 *z;
+ int nByte;
+ if( eType==SQLITE_BLOB ){
+ z = (u8 *)sqlite3_column_blob(pStmt, iCol);
+ }else{
+ z = (u8 *)sqlite3_column_text(pStmt, iCol);
+ }
+ nByte = sqlite3_column_bytes(pStmt, iCol);
+ if( z || (eType==SQLITE_BLOB && nByte==0) ){
+ sessionAppendVarint(p, nByte, pRc);
+ sessionAppendBlob(p, z, nByte, pRc);
+ }else{
+ *pRc = SQLITE_NOMEM;
+ }
+ }
+ }
+}
+
+/*
+**
+** This function appends an update change to the buffer (see the comments
+** under "CHANGESET FORMAT" at the top of the file). An update change
+** consists of:
+**
+** 1 byte: SQLITE_UPDATE (0x17)
+** n bytes: old.* record (see RECORD FORMAT)
+** m bytes: new.* record (see RECORD FORMAT)
+**
+** The SessionChange object passed as the third argument contains the
+** values that were stored in the row when the session began (the old.*
+** values). The statement handle passed as the second argument points
+** at the current version of the row (the new.* values).
+**
+** If all of the old.* values are equal to their corresponding new.* value
+** (i.e. nothing has changed), then no data at all is appended to the buffer.
+**
+** Otherwise, the old.* record contains all primary key values and the
+** original values of any fields that have been modified. The new.* record
+** contains the new values of only those fields that have been modified.
+*/
+static int sessionAppendUpdate(
+ SessionBuffer *pBuf, /* Buffer to append to */
+ int bPatchset, /* True for "patchset", 0 for "changeset" */
+ sqlite3_stmt *pStmt, /* Statement handle pointing at new row */
+ SessionChange *p, /* Object containing old values */
+ u8 *abPK /* Boolean array - true for PK columns */
+){
+ int rc = SQLITE_OK;
+ SessionBuffer buf2 = {0,0,0}; /* Buffer to accumulate new.* record in */
+ int bNoop = 1; /* Set to zero if any values are modified */
+ int nRewind = pBuf->nBuf; /* Set to zero if any values are modified */
+ int i; /* Used to iterate through columns */
+ u8 *pCsr = p->aRecord; /* Used to iterate through old.* values */
+
+ sessionAppendByte(pBuf, SQLITE_UPDATE, &rc);
+ sessionAppendByte(pBuf, p->bIndirect, &rc);
+ for(i=0; i<sqlite3_column_count(pStmt); i++){
+ int bChanged = 0;
+ int nAdvance;
+ int eType = *pCsr;
+ switch( eType ){
+ case SQLITE_NULL:
+ nAdvance = 1;
+ if( sqlite3_column_type(pStmt, i)!=SQLITE_NULL ){
+ bChanged = 1;
+ }
+ break;
+
+ case SQLITE_FLOAT:
+ case SQLITE_INTEGER: {
+ nAdvance = 9;
+ if( eType==sqlite3_column_type(pStmt, i) ){
+ sqlite3_int64 iVal = sessionGetI64(&pCsr[1]);
+ if( eType==SQLITE_INTEGER ){
+ if( iVal==sqlite3_column_int64(pStmt, i) ) break;
+ }else{
+ double dVal;
+ memcpy(&dVal, &iVal, 8);
+ if( dVal==sqlite3_column_double(pStmt, i) ) break;
+ }
+ }
+ bChanged = 1;
+ break;
+ }
+
+ default: {
+ int nByte;
+ int nHdr = 1 + sessionVarintGet(&pCsr[1], &nByte);
+ assert( eType==SQLITE_TEXT || eType==SQLITE_BLOB );
+ nAdvance = nHdr + nByte;
+ if( eType==sqlite3_column_type(pStmt, i)
+ && nByte==sqlite3_column_bytes(pStmt, i)
+ && 0==memcmp(&pCsr[nHdr], sqlite3_column_blob(pStmt, i), nByte)
+ ){
+ break;
+ }
+ bChanged = 1;
+ }
+ }
+
+ /* If at least one field has been modified, this is not a no-op. */
+ if( bChanged ) bNoop = 0;
+
+ /* Add a field to the old.* record. This is omitted if this modules is
+ ** currently generating a patchset. */
+ if( bPatchset==0 ){
+ if( bChanged || abPK[i] ){
+ sessionAppendBlob(pBuf, pCsr, nAdvance, &rc);
+ }else{
+ sessionAppendByte(pBuf, 0, &rc);
+ }
+ }
+
+ /* Add a field to the new.* record. Or the only record if currently
+ ** generating a patchset. */
+ if( bChanged || (bPatchset && abPK[i]) ){
+ sessionAppendCol(&buf2, pStmt, i, &rc);
+ }else{
+ sessionAppendByte(&buf2, 0, &rc);
+ }
+
+ pCsr += nAdvance;
+ }
+
+ if( bNoop ){
+ pBuf->nBuf = nRewind;
+ }else{
+ sessionAppendBlob(pBuf, buf2.aBuf, buf2.nBuf, &rc);
+ }
+ sqlite3_free(buf2.aBuf);
+
+ return rc;
+}
+
+/*
+** Append a DELETE change to the buffer passed as the first argument. Use
+** the changeset format if argument bPatchset is zero, or the patchset
+** format otherwise.
+*/
+static int sessionAppendDelete(
+ SessionBuffer *pBuf, /* Buffer to append to */
+ int bPatchset, /* True for "patchset", 0 for "changeset" */
+ SessionChange *p, /* Object containing old values */
+ int nCol, /* Number of columns in table */
+ u8 *abPK /* Boolean array - true for PK columns */
+){
+ int rc = SQLITE_OK;
+
+ sessionAppendByte(pBuf, SQLITE_DELETE, &rc);
+ sessionAppendByte(pBuf, p->bIndirect, &rc);
+
+ if( bPatchset==0 ){
+ sessionAppendBlob(pBuf, p->aRecord, p->nRecord, &rc);
+ }else{
+ int i;
+ u8 *a = p->aRecord;
+ for(i=0; i<nCol; i++){
+ u8 *pStart = a;
+ int eType = *a++;
+
+ switch( eType ){
+ case 0:
+ case SQLITE_NULL:
+ assert( abPK[i]==0 );
+ break;
+
+ case SQLITE_FLOAT:
+ case SQLITE_INTEGER:
+ a += 8;
+ break;
+
+ default: {
+ int n;
+ a += sessionVarintGet(a, &n);
+ a += n;
+ break;
+ }
+ }
+ if( abPK[i] ){
+ sessionAppendBlob(pBuf, pStart, (int)(a-pStart), &rc);
+ }
+ }
+ assert( (a - p->aRecord)==p->nRecord );
+ }
+
+ return rc;
+}
+
+/*
+** Formulate and prepare a SELECT statement to retrieve a row from table
+** zTab in database zDb based on its primary key. i.e.
+**
+** SELECT * FROM zDb.zTab WHERE pk1 = ? AND pk2 = ? AND ...
+*/
+static int sessionSelectStmt(
+ sqlite3 *db, /* Database handle */
+ const char *zDb, /* Database name */
+ const char *zTab, /* Table name */
+ int nCol, /* Number of columns in table */
+ const char **azCol, /* Names of table columns */
+ u8 *abPK, /* PRIMARY KEY array */
+ sqlite3_stmt **ppStmt /* OUT: Prepared SELECT statement */
+){
+ int rc = SQLITE_OK;
+ int i;
+ const char *zSep = "";
+ SessionBuffer buf = {0, 0, 0};
+
+ sessionAppendStr(&buf, "SELECT * FROM ", &rc);
+ sessionAppendIdent(&buf, zDb, &rc);
+ sessionAppendStr(&buf, ".", &rc);
+ sessionAppendIdent(&buf, zTab, &rc);
+ sessionAppendStr(&buf, " WHERE ", &rc);
+ for(i=0; i<nCol; i++){
+ if( abPK[i] ){
+ sessionAppendStr(&buf, zSep, &rc);
+ sessionAppendIdent(&buf, azCol[i], &rc);
+ sessionAppendStr(&buf, " = ?", &rc);
+ sessionAppendInteger(&buf, i+1, &rc);
+ zSep = " AND ";
+ }
+ }
+ if( rc==SQLITE_OK ){
+ rc = sqlite3_prepare_v2(db, (char *)buf.aBuf, buf.nBuf, ppStmt, 0);
+ }
+ sqlite3_free(buf.aBuf);
+ return rc;
+}
+
+/*
+** Bind the PRIMARY KEY values from the change passed in argument pChange
+** to the SELECT statement passed as the first argument. The SELECT statement
+** is as prepared by function sessionSelectStmt().
+**
+** Return SQLITE_OK if all PK values are successfully bound, or an SQLite
+** error code (e.g. SQLITE_NOMEM) otherwise.
+*/
+static int sessionSelectBind(
+ sqlite3_stmt *pSelect, /* SELECT from sessionSelectStmt() */
+ int nCol, /* Number of columns in table */
+ u8 *abPK, /* PRIMARY KEY array */
+ SessionChange *pChange /* Change structure */
+){
+ int i;
+ int rc = SQLITE_OK;
+ u8 *a = pChange->aRecord;
+
+ for(i=0; i<nCol && rc==SQLITE_OK; i++){
+ int eType = *a++;
+
+ switch( eType ){
+ case 0:
+ case SQLITE_NULL:
+ assert( abPK[i]==0 );
+ break;
+
+ case SQLITE_INTEGER: {
+ if( abPK[i] ){
+ i64 iVal = sessionGetI64(a);
+ rc = sqlite3_bind_int64(pSelect, i+1, iVal);
+ }
+ a += 8;
+ break;
+ }
+
+ case SQLITE_FLOAT: {
+ if( abPK[i] ){
+ double rVal;
+ i64 iVal = sessionGetI64(a);
+ memcpy(&rVal, &iVal, 8);
+ rc = sqlite3_bind_double(pSelect, i+1, rVal);
+ }
+ a += 8;
+ break;
+ }
+
+ case SQLITE_TEXT: {
+ int n;
+ a += sessionVarintGet(a, &n);
+ if( abPK[i] ){
+ rc = sqlite3_bind_text(pSelect, i+1, (char *)a, n, SQLITE_TRANSIENT);
+ }
+ a += n;
+ break;
+ }
+
+ default: {
+ int n;
+ assert( eType==SQLITE_BLOB );
+ a += sessionVarintGet(a, &n);
+ if( abPK[i] ){
+ rc = sqlite3_bind_blob(pSelect, i+1, a, n, SQLITE_TRANSIENT);
+ }
+ a += n;
+ break;
+ }
+ }
+ }
+
+ return rc;
+}
+
+/*
+** This function is a no-op if *pRc is set to other than SQLITE_OK when it
+** is called. Otherwise, append a serialized table header (part of the binary
+** changeset format) to buffer *pBuf. If an error occurs, set *pRc to an
+** SQLite error code before returning.
+*/
+static void sessionAppendTableHdr(
+ SessionBuffer *pBuf, /* Append header to this buffer */
+ int bPatchset, /* Use the patchset format if true */
+ SessionTable *pTab, /* Table object to append header for */
+ int *pRc /* IN/OUT: Error code */
+){
+ /* Write a table header */
+ sessionAppendByte(pBuf, (bPatchset ? 'P' : 'T'), pRc);
+ sessionAppendVarint(pBuf, pTab->nCol, pRc);
+ sessionAppendBlob(pBuf, pTab->abPK, pTab->nCol, pRc);
+ sessionAppendBlob(pBuf, (u8 *)pTab->zName, (int)strlen(pTab->zName)+1, pRc);
+}
+
+/*
+** Generate either a changeset (if argument bPatchset is zero) or a patchset
+** (if it is non-zero) based on the current contents of the session object
+** passed as the first argument.
+**
+** If no error occurs, SQLITE_OK is returned and the new changeset/patchset
+** stored in output variables *pnChangeset and *ppChangeset. Or, if an error
+** occurs, an SQLite error code is returned and both output variables set
+** to 0.
+*/
+static int sessionGenerateChangeset(
+ sqlite3_session *pSession, /* Session object */
+ int bPatchset, /* True for patchset, false for changeset */
+ int (*xOutput)(void *pOut, const void *pData, int nData),
+ void *pOut, /* First argument for xOutput */
+ int *pnChangeset, /* OUT: Size of buffer at *ppChangeset */
+ void **ppChangeset /* OUT: Buffer containing changeset */
+){
+ sqlite3 *db = pSession->db; /* Source database handle */
+ SessionTable *pTab; /* Used to iterate through attached tables */
+ SessionBuffer buf = {0,0,0}; /* Buffer in which to accumlate changeset */
+ int rc; /* Return code */
+
+ assert( xOutput==0 || (pnChangeset==0 && ppChangeset==0 ) );
+
+ /* Zero the output variables in case an error occurs. If this session
+ ** object is already in the error state (sqlite3_session.rc != SQLITE_OK),
+ ** this call will be a no-op. */
+ if( xOutput==0 ){
+ *pnChangeset = 0;
+ *ppChangeset = 0;
+ }
+
+ if( pSession->rc ) return pSession->rc;
+ rc = sqlite3_exec(pSession->db, "SAVEPOINT changeset", 0, 0, 0);
+ if( rc!=SQLITE_OK ) return rc;
+
+ sqlite3_mutex_enter(sqlite3_db_mutex(db));
+
+ for(pTab=pSession->pTable; rc==SQLITE_OK && pTab; pTab=pTab->pNext){
+ if( pTab->nEntry ){
+ const char *zName = pTab->zName;
+ int nCol; /* Number of columns in table */
+ u8 *abPK; /* Primary key array */
+ const char **azCol = 0; /* Table columns */
+ int i; /* Used to iterate through hash buckets */
+ sqlite3_stmt *pSel = 0; /* SELECT statement to query table pTab */
+ int nRewind = buf.nBuf; /* Initial size of write buffer */
+ int nNoop; /* Size of buffer after writing tbl header */
+
+ /* Check the table schema is still Ok. */
+ rc = sessionTableInfo(db, pSession->zDb, zName, &nCol, 0, &azCol, &abPK);
+ if( !rc && (pTab->nCol!=nCol || memcmp(abPK, pTab->abPK, nCol)) ){
+ rc = SQLITE_SCHEMA;
+ }
+
+ /* Write a table header */
+ sessionAppendTableHdr(&buf, bPatchset, pTab, &rc);
+
+ /* Build and compile a statement to execute: */
+ if( rc==SQLITE_OK ){
+ rc = sessionSelectStmt(
+ db, pSession->zDb, zName, nCol, azCol, abPK, &pSel);
+ }
+
+ nNoop = buf.nBuf;
+ for(i=0; i<pTab->nChange && rc==SQLITE_OK; i++){
+ SessionChange *p; /* Used to iterate through changes */
+
+ for(p=pTab->apChange[i]; rc==SQLITE_OK && p; p=p->pNext){
+ rc = sessionSelectBind(pSel, nCol, abPK, p);
+ if( rc!=SQLITE_OK ) continue;
+ if( sqlite3_step(pSel)==SQLITE_ROW ){
+ if( p->op==SQLITE_INSERT ){
+ int iCol;
+ sessionAppendByte(&buf, SQLITE_INSERT, &rc);
+ sessionAppendByte(&buf, p->bIndirect, &rc);
+ for(iCol=0; iCol<nCol; iCol++){
+ sessionAppendCol(&buf, pSel, iCol, &rc);
+ }
+ }else{
+ rc = sessionAppendUpdate(&buf, bPatchset, pSel, p, abPK);
+ }
+ }else if( p->op!=SQLITE_INSERT ){
+ rc = sessionAppendDelete(&buf, bPatchset, p, nCol, abPK);
+ }
+ if( rc==SQLITE_OK ){
+ rc = sqlite3_reset(pSel);
+ }
+
+ /* If the buffer is now larger than SESSIONS_STRM_CHUNK_SIZE, pass
+ ** its contents to the xOutput() callback. */
+ if( xOutput
+ && rc==SQLITE_OK
+ && buf.nBuf>nNoop
+ && buf.nBuf>SESSIONS_STRM_CHUNK_SIZE
+ ){
+ rc = xOutput(pOut, (void*)buf.aBuf, buf.nBuf);
+ nNoop = -1;
+ buf.nBuf = 0;
+ }
+
+ }
+ }
+
+ sqlite3_finalize(pSel);
+ if( buf.nBuf==nNoop ){
+ buf.nBuf = nRewind;
+ }
+ sqlite3_free((char*)azCol); /* cast works around VC++ bug */
+ }
+ }
+
+ if( rc==SQLITE_OK ){
+ if( xOutput==0 ){
+ *pnChangeset = buf.nBuf;
+ *ppChangeset = buf.aBuf;
+ buf.aBuf = 0;
+ }else if( buf.nBuf>0 ){
+ rc = xOutput(pOut, (void*)buf.aBuf, buf.nBuf);
+ }
+ }
+
+ sqlite3_free(buf.aBuf);
+ sqlite3_exec(db, "RELEASE changeset", 0, 0, 0);
+ sqlite3_mutex_leave(sqlite3_db_mutex(db));
+ return rc;
+}
+
+/*
+** Obtain a changeset object containing all changes recorded by the
+** session object passed as the first argument.
+**
+** It is the responsibility of the caller to eventually free the buffer
+** using sqlite3_free().
+*/
+SQLITE_API int SQLITE_STDCALL sqlite3session_changeset(
+ sqlite3_session *pSession, /* Session object */
+ int *pnChangeset, /* OUT: Size of buffer at *ppChangeset */
+ void **ppChangeset /* OUT: Buffer containing changeset */
+){
+ return sessionGenerateChangeset(pSession, 0, 0, 0, pnChangeset, ppChangeset);
+}
+
+/*
+** Streaming version of sqlite3session_changeset().
+*/
+SQLITE_API int SQLITE_STDCALL sqlite3session_changeset_strm(
+ sqlite3_session *pSession,
+ int (*xOutput)(void *pOut, const void *pData, int nData),
+ void *pOut
+){
+ return sessionGenerateChangeset(pSession, 0, xOutput, pOut, 0, 0);
+}
+
+/*
+** Streaming version of sqlite3session_patchset().
+*/
+SQLITE_API int SQLITE_STDCALL sqlite3session_patchset_strm(
+ sqlite3_session *pSession,
+ int (*xOutput)(void *pOut, const void *pData, int nData),
+ void *pOut
+){
+ return sessionGenerateChangeset(pSession, 1, xOutput, pOut, 0, 0);
+}
+
+/*
+** Obtain a patchset object containing all changes recorded by the
+** session object passed as the first argument.
+**
+** It is the responsibility of the caller to eventually free the buffer
+** using sqlite3_free().
+*/
+SQLITE_API int SQLITE_STDCALL sqlite3session_patchset(
+ sqlite3_session *pSession, /* Session object */
+ int *pnPatchset, /* OUT: Size of buffer at *ppChangeset */
+ void **ppPatchset /* OUT: Buffer containing changeset */
+){
+ return sessionGenerateChangeset(pSession, 1, 0, 0, pnPatchset, ppPatchset);
+}
+
+/*
+** Enable or disable the session object passed as the first argument.
+*/
+SQLITE_API int SQLITE_STDCALL sqlite3session_enable(sqlite3_session *pSession, int bEnable){
+ int ret;
+ sqlite3_mutex_enter(sqlite3_db_mutex(pSession->db));
+ if( bEnable>=0 ){
+ pSession->bEnable = bEnable;
+ }
+ ret = pSession->bEnable;
+ sqlite3_mutex_leave(sqlite3_db_mutex(pSession->db));
+ return ret;
+}
+
+/*
+** Enable or disable the session object passed as the first argument.
+*/
+SQLITE_API int SQLITE_STDCALL sqlite3session_indirect(sqlite3_session *pSession, int bIndirect){
+ int ret;
+ sqlite3_mutex_enter(sqlite3_db_mutex(pSession->db));
+ if( bIndirect>=0 ){
+ pSession->bIndirect = bIndirect;
+ }
+ ret = pSession->bIndirect;
+ sqlite3_mutex_leave(sqlite3_db_mutex(pSession->db));
+ return ret;
+}
+
+/*
+** Return true if there have been no changes to monitored tables recorded
+** by the session object passed as the only argument.
+*/
+SQLITE_API int SQLITE_STDCALL sqlite3session_isempty(sqlite3_session *pSession){
+ int ret = 0;
+ SessionTable *pTab;
+
+ sqlite3_mutex_enter(sqlite3_db_mutex(pSession->db));
+ for(pTab=pSession->pTable; pTab && ret==0; pTab=pTab->pNext){
+ ret = (pTab->nEntry>0);
+ }
+ sqlite3_mutex_leave(sqlite3_db_mutex(pSession->db));
+
+ return (ret==0);
+}
+
+/*
+** Do the work for either sqlite3changeset_start() or start_strm().
+*/
+static int sessionChangesetStart(
+ sqlite3_changeset_iter **pp, /* OUT: Changeset iterator handle */
+ int (*xInput)(void *pIn, void *pData, int *pnData),
+ void *pIn,
+ int nChangeset, /* Size of buffer pChangeset in bytes */
+ void *pChangeset /* Pointer to buffer containing changeset */
+){
+ sqlite3_changeset_iter *pRet; /* Iterator to return */
+ int nByte; /* Number of bytes to allocate for iterator */
+
+ assert( xInput==0 || (pChangeset==0 && nChangeset==0) );
+
+ /* Zero the output variable in case an error occurs. */
+ *pp = 0;
+
+ /* Allocate and initialize the iterator structure. */
+ nByte = sizeof(sqlite3_changeset_iter);
+ pRet = (sqlite3_changeset_iter *)sqlite3_malloc(nByte);
+ if( !pRet ) return SQLITE_NOMEM;
+ memset(pRet, 0, sizeof(sqlite3_changeset_iter));
+ pRet->in.aData = (u8 *)pChangeset;
+ pRet->in.nData = nChangeset;
+ pRet->in.xInput = xInput;
+ pRet->in.pIn = pIn;
+ pRet->in.bEof = (xInput ? 0 : 1);
+
+ /* Populate the output variable and return success. */
+ *pp = pRet;
+ return SQLITE_OK;
+}
+
+/*
+** Create an iterator used to iterate through the contents of a changeset.
+*/
+SQLITE_API int SQLITE_STDCALL sqlite3changeset_start(
+ sqlite3_changeset_iter **pp, /* OUT: Changeset iterator handle */
+ int nChangeset, /* Size of buffer pChangeset in bytes */
+ void *pChangeset /* Pointer to buffer containing changeset */
+){
+ return sessionChangesetStart(pp, 0, 0, nChangeset, pChangeset);
+}
+
+/*
+** Streaming version of sqlite3changeset_start().
+*/
+SQLITE_API int SQLITE_STDCALL sqlite3changeset_start_strm(
+ sqlite3_changeset_iter **pp, /* OUT: Changeset iterator handle */
+ int (*xInput)(void *pIn, void *pData, int *pnData),
+ void *pIn
+){
+ return sessionChangesetStart(pp, xInput, pIn, 0, 0);
+}
+
+/*
+** If the SessionInput object passed as the only argument is a streaming
+** object and the buffer is full, discard some data to free up space.
+*/
+static void sessionDiscardData(SessionInput *pIn){
+ if( pIn->bEof && pIn->xInput && pIn->iNext>=SESSIONS_STRM_CHUNK_SIZE ){
+ int nMove = pIn->buf.nBuf - pIn->iNext;
+ assert( nMove>=0 );
+ if( nMove>0 ){
+ memmove(pIn->buf.aBuf, &pIn->buf.aBuf[pIn->iNext], nMove);
+ }
+ pIn->buf.nBuf -= pIn->iNext;
+ pIn->iNext = 0;
+ pIn->nData = pIn->buf.nBuf;
+ }
+}
+
+/*
+** Ensure that there are at least nByte bytes available in the buffer. Or,
+** if there are not nByte bytes remaining in the input, that all available
+** data is in the buffer.
+**
+** Return an SQLite error code if an error occurs, or SQLITE_OK otherwise.
+*/
+static int sessionInputBuffer(SessionInput *pIn, int nByte){
+ int rc = SQLITE_OK;
+ if( pIn->xInput ){
+ while( !pIn->bEof && (pIn->iNext+nByte)>=pIn->nData && rc==SQLITE_OK ){
+ int nNew = SESSIONS_STRM_CHUNK_SIZE;
+
+ if( pIn->bNoDiscard==0 ) sessionDiscardData(pIn);
+ if( SQLITE_OK==sessionBufferGrow(&pIn->buf, nNew, &rc) ){
+ rc = pIn->xInput(pIn->pIn, &pIn->buf.aBuf[pIn->buf.nBuf], &nNew);
+ if( nNew==0 ){
+ pIn->bEof = 1;
+ }else{
+ pIn->buf.nBuf += nNew;
+ }
+ }
+
+ pIn->aData = pIn->buf.aBuf;
+ pIn->nData = pIn->buf.nBuf;
+ }
+ }
+ return rc;
+}
+
+/*
+** When this function is called, *ppRec points to the start of a record
+** that contains nCol values. This function advances the pointer *ppRec
+** until it points to the byte immediately following that record.
+*/
+static void sessionSkipRecord(
+ u8 **ppRec, /* IN/OUT: Record pointer */
+ int nCol /* Number of values in record */
+){
+ u8 *aRec = *ppRec;
+ int i;
+ for(i=0; i<nCol; i++){
+ int eType = *aRec++;
+ if( eType==SQLITE_TEXT || eType==SQLITE_BLOB ){
+ int nByte;
+ aRec += sessionVarintGet((u8*)aRec, &nByte);
+ aRec += nByte;
+ }else if( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT ){
+ aRec += 8;
+ }
+ }
+
+ *ppRec = aRec;
+}
+
+/*
+** This function sets the value of the sqlite3_value object passed as the
+** first argument to a copy of the string or blob held in the aData[]
+** buffer. SQLITE_OK is returned if successful, or SQLITE_NOMEM if an OOM
+** error occurs.
+*/
+static int sessionValueSetStr(
+ sqlite3_value *pVal, /* Set the value of this object */
+ u8 *aData, /* Buffer containing string or blob data */
+ int nData, /* Size of buffer aData[] in bytes */
+ u8 enc /* String encoding (0 for blobs) */
+){
+ /* In theory this code could just pass SQLITE_TRANSIENT as the final
+ ** argument to sqlite3ValueSetStr() and have the copy created
+ ** automatically. But doing so makes it difficult to detect any OOM
+ ** error. Hence the code to create the copy externally. */
+ u8 *aCopy = sqlite3_malloc(nData+1);
+ if( aCopy==0 ) return SQLITE_NOMEM;
+ memcpy(aCopy, aData, nData);
+ sqlite3ValueSetStr(pVal, nData, (char*)aCopy, enc, sqlite3_free);
+ return SQLITE_OK;
+}
+
+/*
+** Deserialize a single record from a buffer in memory. See "RECORD FORMAT"
+** for details.
+**
+** When this function is called, *paChange points to the start of the record
+** to deserialize. Assuming no error occurs, *paChange is set to point to
+** one byte after the end of the same record before this function returns.
+** If the argument abPK is NULL, then the record contains nCol values. Or,
+** if abPK is other than NULL, then the record contains only the PK fields
+** (in other words, it is a patchset DELETE record).
+**
+** If successful, each element of the apOut[] array (allocated by the caller)
+** is set to point to an sqlite3_value object containing the value read
+** from the corresponding position in the record. If that value is not
+** included in the record (i.e. because the record is part of an UPDATE change
+** and the field was not modified), the corresponding element of apOut[] is
+** set to NULL.
+**
+** It is the responsibility of the caller to free all sqlite_value structures
+** using sqlite3_free().
+**
+** If an error occurs, an SQLite error code (e.g. SQLITE_NOMEM) is returned.
+** The apOut[] array may have been partially populated in this case.
+*/
+static int sessionReadRecord(
+ SessionInput *pIn, /* Input data */
+ int nCol, /* Number of values in record */
+ u8 *abPK, /* Array of primary key flags, or NULL */
+ sqlite3_value **apOut /* Write values to this array */
+){
+ int i; /* Used to iterate through columns */
+ int rc = SQLITE_OK;
+
+ for(i=0; i<nCol && rc==SQLITE_OK; i++){
+ int eType = 0; /* Type of value (SQLITE_NULL, TEXT etc.) */
+ if( abPK && abPK[i]==0 ) continue;
+ rc = sessionInputBuffer(pIn, 9);
+ if( rc==SQLITE_OK ){
+ eType = pIn->aData[pIn->iNext++];
+ }
+
+ assert( apOut[i]==0 );
+ if( eType ){
+ apOut[i] = sqlite3ValueNew(0);
+ if( !apOut[i] ) rc = SQLITE_NOMEM;
+ }
+
+ if( rc==SQLITE_OK ){
+ u8 *aVal = &pIn->aData[pIn->iNext];
+ if( eType==SQLITE_TEXT || eType==SQLITE_BLOB ){
+ int nByte;
+ pIn->iNext += sessionVarintGet(aVal, &nByte);
+ rc = sessionInputBuffer(pIn, nByte);
+ if( rc==SQLITE_OK ){
+ u8 enc = (eType==SQLITE_TEXT ? SQLITE_UTF8 : 0);
+ rc = sessionValueSetStr(apOut[i],&pIn->aData[pIn->iNext],nByte,enc);
+ }
+ pIn->iNext += nByte;
+ }
+ if( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT ){
+ sqlite3_int64 v = sessionGetI64(aVal);
+ if( eType==SQLITE_INTEGER ){
+ sqlite3VdbeMemSetInt64(apOut[i], v);
+ }else{
+ double d;
+ memcpy(&d, &v, 8);
+ sqlite3VdbeMemSetDouble(apOut[i], d);
+ }
+ pIn->iNext += 8;
+ }
+ }
+ }
+
+ return rc;
+}
+
+/*
+** The input pointer currently points to the second byte of a table-header.
+** Specifically, to the following:
+**
+** + number of columns in table (varint)
+** + array of PK flags (1 byte per column),
+** + table name (nul terminated).
+**
+** This function ensures that all of the above is present in the input
+** buffer (i.e. that it can be accessed without any calls to xInput()).
+** If successful, SQLITE_OK is returned. Otherwise, an SQLite error code.
+** The input pointer is not moved.
+*/
+static int sessionChangesetBufferTblhdr(SessionInput *pIn, int *pnByte){
+ int rc = SQLITE_OK;
+ int nCol = 0;
+ int nRead = 0;
+
+ rc = sessionInputBuffer(pIn, 9);
+ if( rc==SQLITE_OK ){
+ nRead += sessionVarintGet(&pIn->aData[pIn->iNext + nRead], &nCol);
+ rc = sessionInputBuffer(pIn, nRead+nCol+100);
+ nRead += nCol;
+ }
+
+ while( rc==SQLITE_OK ){
+ while( (pIn->iNext + nRead)<pIn->nData && pIn->aData[pIn->iNext + nRead] ){
+ nRead++;
+ }
+ if( (pIn->iNext + nRead)<pIn->nData ) break;
+ rc = sessionInputBuffer(pIn, nRead + 100);
+ }
+ *pnByte = nRead+1;
+ return rc;
+}
+
+/*
+** The input pointer currently points to the first byte of the first field
+** of a record consisting of nCol columns. This function ensures the entire
+** record is buffered. It does not move the input pointer.
+**
+** If successful, SQLITE_OK is returned and *pnByte is set to the size of
+** the record in bytes. Otherwise, an SQLite error code is returned. The
+** final value of *pnByte is undefined in this case.
+*/
+static int sessionChangesetBufferRecord(
+ SessionInput *pIn, /* Input data */
+ int nCol, /* Number of columns in record */
+ int *pnByte /* OUT: Size of record in bytes */
+){
+ int rc = SQLITE_OK;
+ int nByte = 0;
+ int i;
+ for(i=0; rc==SQLITE_OK && i<nCol; i++){
+ int eType;
+ rc = sessionInputBuffer(pIn, nByte + 10);
+ if( rc==SQLITE_OK ){
+ eType = pIn->aData[pIn->iNext + nByte++];
+ if( eType==SQLITE_TEXT || eType==SQLITE_BLOB ){
+ int n;
+ nByte += sessionVarintGet(&pIn->aData[pIn->iNext+nByte], &n);
+ nByte += n;
+ rc = sessionInputBuffer(pIn, nByte);
+ }else if( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT ){
+ nByte += 8;
+ }
+ }
+ }
+ *pnByte = nByte;
+ return rc;
+}
+
+/*
+** The input pointer currently points to the second byte of a table-header.
+** Specifically, to the following:
+**
+** + number of columns in table (varint)
+** + array of PK flags (1 byte per column),
+** + table name (nul terminated).
+**
+** This function decodes the table-header and populates the p->nCol,
+** p->zTab and p->abPK[] variables accordingly. The p->apValue[] array is
+** also allocated or resized according to the new value of p->nCol. The
+** input pointer is left pointing to the byte following the table header.
+**
+** If successful, SQLITE_OK is returned. Otherwise, an SQLite error code
+** is returned and the final values of the various fields enumerated above
+** are undefined.
+*/
+static int sessionChangesetReadTblhdr(sqlite3_changeset_iter *p){
+ int rc;
+ int nCopy;
+ assert( p->rc==SQLITE_OK );
+
+ rc = sessionChangesetBufferTblhdr(&p->in, &nCopy);
+ if( rc==SQLITE_OK ){
+ int nByte;
+ int nVarint;
+ nVarint = sessionVarintGet(&p->in.aData[p->in.iNext], &p->nCol);
+ nCopy -= nVarint;
+ p->in.iNext += nVarint;
+ nByte = p->nCol * sizeof(sqlite3_value*) * 2 + nCopy;
+ p->tblhdr.nBuf = 0;
+ sessionBufferGrow(&p->tblhdr, nByte, &rc);
+ }
+
+ if( rc==SQLITE_OK ){
+ int iPK = sizeof(sqlite3_value*)*p->nCol*2;
+ memset(p->tblhdr.aBuf, 0, iPK);
+ memcpy(&p->tblhdr.aBuf[iPK], &p->in.aData[p->in.iNext], nCopy);
+ p->in.iNext += nCopy;
+ }
+
+ p->apValue = (sqlite3_value**)p->tblhdr.aBuf;
+ p->abPK = (u8*)&p->apValue[p->nCol*2];
+ p->zTab = (char*)&p->abPK[p->nCol];
+ return (p->rc = rc);
+}
+
+/*
+** Advance the changeset iterator to the next change.
+**
+** If both paRec and pnRec are NULL, then this function works like the public
+** API sqlite3changeset_next(). If SQLITE_ROW is returned, then the
+** sqlite3changeset_new() and old() APIs may be used to query for values.
+**
+** Otherwise, if paRec and pnRec are not NULL, then a pointer to the change
+** record is written to *paRec before returning and the number of bytes in
+** the record to *pnRec.
+**
+** Either way, this function returns SQLITE_ROW if the iterator is
+** successfully advanced to the next change in the changeset, an SQLite
+** error code if an error occurs, or SQLITE_DONE if there are no further
+** changes in the changeset.
+*/
+static int sessionChangesetNext(
+ sqlite3_changeset_iter *p, /* Changeset iterator */
+ u8 **paRec, /* If non-NULL, store record pointer here */
+ int *pnRec /* If non-NULL, store size of record here */
+){
+ int i;
+ u8 op;
+
+ assert( (paRec==0 && pnRec==0) || (paRec && pnRec) );
+
+ /* If the iterator is in the error-state, return immediately. */
+ if( p->rc!=SQLITE_OK ) return p->rc;
+
+ /* Free the current contents of p->apValue[], if any. */
+ if( p->apValue ){
+ for(i=0; i<p->nCol*2; i++){
+ sqlite3ValueFree(p->apValue[i]);
+ }
+ memset(p->apValue, 0, sizeof(sqlite3_value*)*p->nCol*2);
+ }
+
+ /* Make sure the buffer contains at least 10 bytes of input data, or all
+ ** remaining data if there are less than 10 bytes available. This is
+ ** sufficient either for the 'T' or 'P' byte and the varint that follows
+ ** it, or for the two single byte values otherwise. */
+ p->rc = sessionInputBuffer(&p->in, 2);
+ if( p->rc!=SQLITE_OK ) return p->rc;
+
+ /* If the iterator is already at the end of the changeset, return DONE. */
+ if( p->in.iNext>=p->in.nData ){
+ return SQLITE_DONE;
+ }
+
+ sessionDiscardData(&p->in);
+ p->in.iCurrent = p->in.iNext;
+
+ op = p->in.aData[p->in.iNext++];
+ if( op=='T' || op=='P' ){
+ p->bPatchset = (op=='P');
+ if( sessionChangesetReadTblhdr(p) ) return p->rc;
+ if( (p->rc = sessionInputBuffer(&p->in, 2)) ) return p->rc;
+ p->in.iCurrent = p->in.iNext;
+ op = p->in.aData[p->in.iNext++];
+ }
+
+ p->op = op;
+ p->bIndirect = p->in.aData[p->in.iNext++];
+ if( p->op!=SQLITE_UPDATE && p->op!=SQLITE_DELETE && p->op!=SQLITE_INSERT ){
+ return (p->rc = SQLITE_CORRUPT_BKPT);
+ }
+
+ if( paRec ){
+ int nVal; /* Number of values to buffer */
+ if( p->bPatchset==0 && op==SQLITE_UPDATE ){
+ nVal = p->nCol * 2;
+ }else if( p->bPatchset && op==SQLITE_DELETE ){
+ nVal = 0;
+ for(i=0; i<p->nCol; i++) if( p->abPK[i] ) nVal++;
+ }else{
+ nVal = p->nCol;
+ }
+ p->rc = sessionChangesetBufferRecord(&p->in, nVal, pnRec);
+ if( p->rc!=SQLITE_OK ) return p->rc;
+ *paRec = &p->in.aData[p->in.iNext];
+ p->in.iNext += *pnRec;
+ }else{
+
+ /* If this is an UPDATE or DELETE, read the old.* record. */
+ if( p->op!=SQLITE_INSERT && (p->bPatchset==0 || p->op==SQLITE_DELETE) ){
+ u8 *abPK = p->bPatchset ? p->abPK : 0;
+ p->rc = sessionReadRecord(&p->in, p->nCol, abPK, p->apValue);
+ if( p->rc!=SQLITE_OK ) return p->rc;
+ }
+
+ /* If this is an INSERT or UPDATE, read the new.* record. */
+ if( p->op!=SQLITE_DELETE ){
+ p->rc = sessionReadRecord(&p->in, p->nCol, 0, &p->apValue[p->nCol]);
+ if( p->rc!=SQLITE_OK ) return p->rc;
+ }
+
+ if( p->bPatchset && p->op==SQLITE_UPDATE ){
+ /* If this is an UPDATE that is part of a patchset, then all PK and
+ ** modified fields are present in the new.* record. The old.* record
+ ** is currently completely empty. This block shifts the PK fields from
+ ** new.* to old.*, to accommodate the code that reads these arrays. */
+ for(i=0; i<p->nCol; i++){
+ assert( p->apValue[i]==0 );
+ assert( p->abPK[i]==0 || p->apValue[i+p->nCol] );
+ if( p->abPK[i] ){
+ p->apValue[i] = p->apValue[i+p->nCol];
+ p->apValue[i+p->nCol] = 0;
+ }
+ }
+ }
+ }
+
+ return SQLITE_ROW;
+}
+
+/*
+** Advance an iterator created by sqlite3changeset_start() to the next
+** change in the changeset. This function may return SQLITE_ROW, SQLITE_DONE
+** or SQLITE_CORRUPT.
+**
+** This function may not be called on iterators passed to a conflict handler
+** callback by changeset_apply().
+*/
+SQLITE_API int SQLITE_STDCALL sqlite3changeset_next(sqlite3_changeset_iter *p){
+ return sessionChangesetNext(p, 0, 0);
+}
+
+/*
+** The following function extracts information on the current change
+** from a changeset iterator. It may only be called after changeset_next()
+** has returned SQLITE_ROW.
+*/
+SQLITE_API int SQLITE_STDCALL sqlite3changeset_op(
+ sqlite3_changeset_iter *pIter, /* Iterator handle */
+ const char **pzTab, /* OUT: Pointer to table name */
+ int *pnCol, /* OUT: Number of columns in table */
+ int *pOp, /* OUT: SQLITE_INSERT, DELETE or UPDATE */
+ int *pbIndirect /* OUT: True if change is indirect */
+){
+ *pOp = pIter->op;
+ *pnCol = pIter->nCol;
+ *pzTab = pIter->zTab;
+ if( pbIndirect ) *pbIndirect = pIter->bIndirect;
+ return SQLITE_OK;
+}
+
+/*
+** Return information regarding the PRIMARY KEY and number of columns in
+** the database table affected by the change that pIter currently points
+** to. This function may only be called after changeset_next() returns
+** SQLITE_ROW.
+*/
+SQLITE_API int SQLITE_STDCALL sqlite3changeset_pk(
+ sqlite3_changeset_iter *pIter, /* Iterator object */
+ unsigned char **pabPK, /* OUT: Array of boolean - true for PK cols */
+ int *pnCol /* OUT: Number of entries in output array */
+){
+ *pabPK = pIter->abPK;
+ if( pnCol ) *pnCol = pIter->nCol;
+ return SQLITE_OK;
+}
+
+/*
+** This function may only be called while the iterator is pointing to an
+** SQLITE_UPDATE or SQLITE_DELETE change (see sqlite3changeset_op()).
+** Otherwise, SQLITE_MISUSE is returned.
+**
+** It sets *ppValue to point to an sqlite3_value structure containing the
+** iVal'th value in the old.* record. Or, if that particular value is not
+** included in the record (because the change is an UPDATE and the field
+** was not modified and is not a PK column), set *ppValue to NULL.
+**
+** If value iVal is out-of-range, SQLITE_RANGE is returned and *ppValue is
+** not modified. Otherwise, SQLITE_OK.
+*/
+SQLITE_API int SQLITE_STDCALL sqlite3changeset_old(
+ sqlite3_changeset_iter *pIter, /* Changeset iterator */
+ int iVal, /* Index of old.* value to retrieve */
+ sqlite3_value **ppValue /* OUT: Old value (or NULL pointer) */
+){
+ if( pIter->op!=SQLITE_UPDATE && pIter->op!=SQLITE_DELETE ){
+ return SQLITE_MISUSE;
+ }
+ if( iVal<0 || iVal>=pIter->nCol ){
+ return SQLITE_RANGE;
+ }
+ *ppValue = pIter->apValue[iVal];
+ return SQLITE_OK;
+}
+
+/*
+** This function may only be called while the iterator is pointing to an
+** SQLITE_UPDATE or SQLITE_INSERT change (see sqlite3changeset_op()).
+** Otherwise, SQLITE_MISUSE is returned.
+**
+** It sets *ppValue to point to an sqlite3_value structure containing the
+** iVal'th value in the new.* record. Or, if that particular value is not
+** included in the record (because the change is an UPDATE and the field
+** was not modified), set *ppValue to NULL.
+**
+** If value iVal is out-of-range, SQLITE_RANGE is returned and *ppValue is
+** not modified. Otherwise, SQLITE_OK.
+*/
+SQLITE_API int SQLITE_STDCALL sqlite3changeset_new(
+ sqlite3_changeset_iter *pIter, /* Changeset iterator */
+ int iVal, /* Index of new.* value to retrieve */
+ sqlite3_value **ppValue /* OUT: New value (or NULL pointer) */
+){
+ if( pIter->op!=SQLITE_UPDATE && pIter->op!=SQLITE_INSERT ){
+ return SQLITE_MISUSE;
+ }
+ if( iVal<0 || iVal>=pIter->nCol ){
+ return SQLITE_RANGE;
+ }
+ *ppValue = pIter->apValue[pIter->nCol+iVal];
+ return SQLITE_OK;
+}
+
+/*
+** The following two macros are used internally. They are similar to the
+** sqlite3changeset_new() and sqlite3changeset_old() functions, except that
+** they omit all error checking and return a pointer to the requested value.
+*/
+#define sessionChangesetNew(pIter, iVal) (pIter)->apValue[(pIter)->nCol+(iVal)]
+#define sessionChangesetOld(pIter, iVal) (pIter)->apValue[(iVal)]
+
+/*
+** This function may only be called with a changeset iterator that has been
+** passed to an SQLITE_CHANGESET_DATA or SQLITE_CHANGESET_CONFLICT
+** conflict-handler function. Otherwise, SQLITE_MISUSE is returned.
+**
+** If successful, *ppValue is set to point to an sqlite3_value structure
+** containing the iVal'th value of the conflicting record.
+**
+** If value iVal is out-of-range or some other error occurs, an SQLite error
+** code is returned. Otherwise, SQLITE_OK.
+*/
+SQLITE_API int SQLITE_STDCALL sqlite3changeset_conflict(
+ sqlite3_changeset_iter *pIter, /* Changeset iterator */
+ int iVal, /* Index of conflict record value to fetch */
+ sqlite3_value **ppValue /* OUT: Value from conflicting row */
+){
+ if( !pIter->pConflict ){
+ return SQLITE_MISUSE;
+ }
+ if( iVal<0 || iVal>=sqlite3_column_count(pIter->pConflict) ){
+ return SQLITE_RANGE;
+ }
+ *ppValue = sqlite3_column_value(pIter->pConflict, iVal);
+ return SQLITE_OK;
+}
+
+/*
+** This function may only be called with an iterator passed to an
+** SQLITE_CHANGESET_FOREIGN_KEY conflict handler callback. In this case
+** it sets the output variable to the total number of known foreign key
+** violations in the destination database and returns SQLITE_OK.
+**
+** In all other cases this function returns SQLITE_MISUSE.
+*/
+SQLITE_API int SQLITE_STDCALL sqlite3changeset_fk_conflicts(
+ sqlite3_changeset_iter *pIter, /* Changeset iterator */
+ int *pnOut /* OUT: Number of FK violations */
+){
+ if( pIter->pConflict || pIter->apValue ){
+ return SQLITE_MISUSE;
+ }
+ *pnOut = pIter->nCol;
+ return SQLITE_OK;
+}
+
+
+/*
+** Finalize an iterator allocated with sqlite3changeset_start().
+**
+** This function may not be called on iterators passed to a conflict handler
+** callback by changeset_apply().
+*/
+SQLITE_API int SQLITE_STDCALL sqlite3changeset_finalize(sqlite3_changeset_iter *p){
+ int rc = SQLITE_OK;
+ if( p ){
+ int i; /* Used to iterate through p->apValue[] */
+ rc = p->rc;
+ if( p->apValue ){
+ for(i=0; i<p->nCol*2; i++) sqlite3ValueFree(p->apValue[i]);
+ }
+ sqlite3_free(p->tblhdr.aBuf);
+ sqlite3_free(p->in.buf.aBuf);
+ sqlite3_free(p);
+ }
+ return rc;
+}
+
+static int sessionChangesetInvert(
+ SessionInput *pInput, /* Input changeset */
+ int (*xOutput)(void *pOut, const void *pData, int nData),
+ void *pOut,
+ int *pnInverted, /* OUT: Number of bytes in output changeset */
+ void **ppInverted /* OUT: Inverse of pChangeset */
+){
+ int rc = SQLITE_OK; /* Return value */
+ SessionBuffer sOut; /* Output buffer */
+ int nCol = 0; /* Number of cols in current table */
+ u8 *abPK = 0; /* PK array for current table */
+ sqlite3_value **apVal = 0; /* Space for values for UPDATE inversion */
+ SessionBuffer sPK = {0, 0, 0}; /* PK array for current table */
+
+ /* Initialize the output buffer */
+ memset(&sOut, 0, sizeof(SessionBuffer));
+
+ /* Zero the output variables in case an error occurs. */
+ if( ppInverted ){
+ *ppInverted = 0;
+ *pnInverted = 0;
+ }
+
+ while( 1 ){
+ u8 eType;
+
+ /* Test for EOF. */
+ if( (rc = sessionInputBuffer(pInput, 2)) ) goto finished_invert;
+ if( pInput->iNext>=pInput->nData ) break;
+ eType = pInput->aData[pInput->iNext];
+
+ switch( eType ){
+ case 'T': {
+ /* A 'table' record consists of:
+ **
+ ** * A constant 'T' character,
+ ** * Number of columns in said table (a varint),
+ ** * An array of nCol bytes (sPK),
+ ** * A nul-terminated table name.
+ */
+ int nByte;
+ int nVar;
+ pInput->iNext++;
+ if( (rc = sessionChangesetBufferTblhdr(pInput, &nByte)) ){
+ goto finished_invert;
+ }
+ nVar = sessionVarintGet(&pInput->aData[pInput->iNext], &nCol);
+ sPK.nBuf = 0;
+ sessionAppendBlob(&sPK, &pInput->aData[pInput->iNext+nVar], nCol, &rc);
+ sessionAppendByte(&sOut, eType, &rc);
+ sessionAppendBlob(&sOut, &pInput->aData[pInput->iNext], nByte, &rc);
+ if( rc ) goto finished_invert;
+
+ pInput->iNext += nByte;
+ sqlite3_free(apVal);
+ apVal = 0;
+ abPK = sPK.aBuf;
+ break;
+ }
+
+ case SQLITE_INSERT:
+ case SQLITE_DELETE: {
+ int nByte;
+ int bIndirect = pInput->aData[pInput->iNext+1];
+ int eType2 = (eType==SQLITE_DELETE ? SQLITE_INSERT : SQLITE_DELETE);
+ pInput->iNext += 2;
+ assert( rc==SQLITE_OK );
+ rc = sessionChangesetBufferRecord(pInput, nCol, &nByte);
+ sessionAppendByte(&sOut, eType2, &rc);
+ sessionAppendByte(&sOut, bIndirect, &rc);
+ sessionAppendBlob(&sOut, &pInput->aData[pInput->iNext], nByte, &rc);
+ pInput->iNext += nByte;
+ if( rc ) goto finished_invert;
+ break;
+ }
+
+ case SQLITE_UPDATE: {
+ int iCol;
+
+ if( 0==apVal ){
+ apVal = (sqlite3_value **)sqlite3_malloc(sizeof(apVal[0])*nCol*2);
+ if( 0==apVal ){
+ rc = SQLITE_NOMEM;
+ goto finished_invert;
+ }
+ memset(apVal, 0, sizeof(apVal[0])*nCol*2);
+ }
+
+ /* Write the header for the new UPDATE change. Same as the original. */
+ sessionAppendByte(&sOut, eType, &rc);
+ sessionAppendByte(&sOut, pInput->aData[pInput->iNext+1], &rc);
+
+ /* Read the old.* and new.* records for the update change. */
+ pInput->iNext += 2;
+ rc = sessionReadRecord(pInput, nCol, 0, &apVal[0]);
+ if( rc==SQLITE_OK ){
+ rc = sessionReadRecord(pInput, nCol, 0, &apVal[nCol]);
+ }
+
+ /* Write the new old.* record. Consists of the PK columns from the
+ ** original old.* record, and the other values from the original
+ ** new.* record. */
+ for(iCol=0; iCol<nCol; iCol++){
+ sqlite3_value *pVal = apVal[iCol + (abPK[iCol] ? 0 : nCol)];
+ sessionAppendValue(&sOut, pVal, &rc);
+ }
+
+ /* Write the new new.* record. Consists of a copy of all values
+ ** from the original old.* record, except for the PK columns, which
+ ** are set to "undefined". */
+ for(iCol=0; iCol<nCol; iCol++){
+ sqlite3_value *pVal = (abPK[iCol] ? 0 : apVal[iCol]);
+ sessionAppendValue(&sOut, pVal, &rc);
+ }
+
+ for(iCol=0; iCol<nCol*2; iCol++){
+ sqlite3ValueFree(apVal[iCol]);
+ }
+ memset(apVal, 0, sizeof(apVal[0])*nCol*2);
+ if( rc!=SQLITE_OK ){
+ goto finished_invert;
+ }
+
+ break;
+ }
+
+ default:
+ rc = SQLITE_CORRUPT_BKPT;
+ goto finished_invert;
+ }
+
+ assert( rc==SQLITE_OK );
+ if( xOutput && sOut.nBuf>=SESSIONS_STRM_CHUNK_SIZE ){
+ rc = xOutput(pOut, sOut.aBuf, sOut.nBuf);
+ sOut.nBuf = 0;
+ if( rc!=SQLITE_OK ) goto finished_invert;
+ }
+ }
+
+ assert( rc==SQLITE_OK );
+ if( pnInverted ){
+ *pnInverted = sOut.nBuf;
+ *ppInverted = sOut.aBuf;
+ sOut.aBuf = 0;
+ }else if( sOut.nBuf>0 ){
+ rc = xOutput(pOut, sOut.aBuf, sOut.nBuf);
+ }
+
+ finished_invert:
+ sqlite3_free(sOut.aBuf);
+ sqlite3_free(apVal);
+ sqlite3_free(sPK.aBuf);
+ return rc;
+}
+
+
+/*
+** Invert a changeset object.
+*/
+SQLITE_API int SQLITE_STDCALL sqlite3changeset_invert(
+ int nChangeset, /* Number of bytes in input */
+ const void *pChangeset, /* Input changeset */
+ int *pnInverted, /* OUT: Number of bytes in output changeset */
+ void **ppInverted /* OUT: Inverse of pChangeset */
+){
+ SessionInput sInput;
+
+ /* Set up the input stream */
+ memset(&sInput, 0, sizeof(SessionInput));
+ sInput.nData = nChangeset;
+ sInput.aData = (u8*)pChangeset;
+
+ return sessionChangesetInvert(&sInput, 0, 0, pnInverted, ppInverted);
+}
+
+/*
+** Streaming version of sqlite3changeset_invert().
+*/
+SQLITE_API int SQLITE_STDCALL sqlite3changeset_invert_strm(
+ int (*xInput)(void *pIn, void *pData, int *pnData),
+ void *pIn,
+ int (*xOutput)(void *pOut, const void *pData, int nData),
+ void *pOut
+){
+ SessionInput sInput;
+ int rc;
+
+ /* Set up the input stream */
+ memset(&sInput, 0, sizeof(SessionInput));
+ sInput.xInput = xInput;
+ sInput.pIn = pIn;
+
+ rc = sessionChangesetInvert(&sInput, xOutput, pOut, 0, 0);
+ sqlite3_free(sInput.buf.aBuf);
+ return rc;
+}
+
+typedef struct SessionApplyCtx SessionApplyCtx;
+struct SessionApplyCtx {
+ sqlite3 *db;
+ sqlite3_stmt *pDelete; /* DELETE statement */
+ sqlite3_stmt *pUpdate; /* UPDATE statement */
+ sqlite3_stmt *pInsert; /* INSERT statement */
+ sqlite3_stmt *pSelect; /* SELECT statement */
+ int nCol; /* Size of azCol[] and abPK[] arrays */
+ const char **azCol; /* Array of column names */
+ u8 *abPK; /* Boolean array - true if column is in PK */
+
+ int bDeferConstraints; /* True to defer constraints */
+ SessionBuffer constraints; /* Deferred constraints are stored here */
+};
+
+/*
+** Formulate a statement to DELETE a row from database db. Assuming a table
+** structure like this:
+**
+** CREATE TABLE x(a, b, c, d, PRIMARY KEY(a, c));
+**
+** The DELETE statement looks like this:
+**
+** DELETE FROM x WHERE a = :1 AND c = :3 AND (:5 OR b IS :2 AND d IS :4)
+**
+** Variable :5 (nCol+1) is a boolean. It should be set to 0 if we require
+** matching b and d values, or 1 otherwise. The second case comes up if the
+** conflict handler is invoked with NOTFOUND and returns CHANGESET_REPLACE.
+**
+** If successful, SQLITE_OK is returned and SessionApplyCtx.pDelete is left
+** pointing to the prepared version of the SQL statement.
+*/
+static int sessionDeleteRow(
+ sqlite3 *db, /* Database handle */
+ const char *zTab, /* Table name */
+ SessionApplyCtx *p /* Session changeset-apply context */
+){
+ int i;
+ const char *zSep = "";
+ int rc = SQLITE_OK;
+ SessionBuffer buf = {0, 0, 0};
+ int nPk = 0;
+
+ sessionAppendStr(&buf, "DELETE FROM ", &rc);
+ sessionAppendIdent(&buf, zTab, &rc);
+ sessionAppendStr(&buf, " WHERE ", &rc);
+
+ for(i=0; i<p->nCol; i++){
+ if( p->abPK[i] ){
+ nPk++;
+ sessionAppendStr(&buf, zSep, &rc);
+ sessionAppendIdent(&buf, p->azCol[i], &rc);
+ sessionAppendStr(&buf, " = ?", &rc);
+ sessionAppendInteger(&buf, i+1, &rc);
+ zSep = " AND ";
+ }
+ }
+
+ if( nPk<p->nCol ){
+ sessionAppendStr(&buf, " AND (?", &rc);
+ sessionAppendInteger(&buf, p->nCol+1, &rc);
+ sessionAppendStr(&buf, " OR ", &rc);
+
+ zSep = "";
+ for(i=0; i<p->nCol; i++){
+ if( !p->abPK[i] ){
+ sessionAppendStr(&buf, zSep, &rc);
+ sessionAppendIdent(&buf, p->azCol[i], &rc);
+ sessionAppendStr(&buf, " IS ?", &rc);
+ sessionAppendInteger(&buf, i+1, &rc);
+ zSep = "AND ";
+ }
+ }
+ sessionAppendStr(&buf, ")", &rc);
+ }
+
+ if( rc==SQLITE_OK ){
+ rc = sqlite3_prepare_v2(db, (char *)buf.aBuf, buf.nBuf, &p->pDelete, 0);
+ }
+ sqlite3_free(buf.aBuf);
+
+ return rc;
+}
+
+/*
+** Formulate and prepare a statement to UPDATE a row from database db.
+** Assuming a table structure like this:
+**
+** CREATE TABLE x(a, b, c, d, PRIMARY KEY(a, c));
+**
+** The UPDATE statement looks like this:
+**
+** UPDATE x SET
+** a = CASE WHEN ?2 THEN ?3 ELSE a END,
+** b = CASE WHEN ?5 THEN ?6 ELSE b END,
+** c = CASE WHEN ?8 THEN ?9 ELSE c END,
+** d = CASE WHEN ?11 THEN ?12 ELSE d END
+** WHERE a = ?1 AND c = ?7 AND (?13 OR
+** (?5==0 OR b IS ?4) AND (?11==0 OR d IS ?10) AND
+** )
+**
+** For each column in the table, there are three variables to bind:
+**
+** ?(i*3+1) The old.* value of the column, if any.
+** ?(i*3+2) A boolean flag indicating that the value is being modified.
+** ?(i*3+3) The new.* value of the column, if any.
+**
+** Also, a boolean flag that, if set to true, causes the statement to update
+** a row even if the non-PK values do not match. This is required if the
+** conflict-handler is invoked with CHANGESET_DATA and returns
+** CHANGESET_REPLACE. This is variable "?(nCol*3+1)".
+**
+** If successful, SQLITE_OK is returned and SessionApplyCtx.pUpdate is left
+** pointing to the prepared version of the SQL statement.
+*/
+static int sessionUpdateRow(
+ sqlite3 *db, /* Database handle */
+ const char *zTab, /* Table name */
+ SessionApplyCtx *p /* Session changeset-apply context */
+){
+ int rc = SQLITE_OK;
+ int i;
+ const char *zSep = "";
+ SessionBuffer buf = {0, 0, 0};
+
+ /* Append "UPDATE tbl SET " */
+ sessionAppendStr(&buf, "UPDATE ", &rc);
+ sessionAppendIdent(&buf, zTab, &rc);
+ sessionAppendStr(&buf, " SET ", &rc);
+
+ /* Append the assignments */
+ for(i=0; i<p->nCol; i++){
+ sessionAppendStr(&buf, zSep, &rc);
+ sessionAppendIdent(&buf, p->azCol[i], &rc);
+ sessionAppendStr(&buf, " = CASE WHEN ?", &rc);
+ sessionAppendInteger(&buf, i*3+2, &rc);
+ sessionAppendStr(&buf, " THEN ?", &rc);
+ sessionAppendInteger(&buf, i*3+3, &rc);
+ sessionAppendStr(&buf, " ELSE ", &rc);
+ sessionAppendIdent(&buf, p->azCol[i], &rc);
+ sessionAppendStr(&buf, " END", &rc);
+ zSep = ", ";
+ }
+
+ /* Append the PK part of the WHERE clause */
+ sessionAppendStr(&buf, " WHERE ", &rc);
+ for(i=0; i<p->nCol; i++){
+ if( p->abPK[i] ){
+ sessionAppendIdent(&buf, p->azCol[i], &rc);
+ sessionAppendStr(&buf, " = ?", &rc);
+ sessionAppendInteger(&buf, i*3+1, &rc);
+ sessionAppendStr(&buf, " AND ", &rc);
+ }
+ }
+
+ /* Append the non-PK part of the WHERE clause */
+ sessionAppendStr(&buf, " (?", &rc);
+ sessionAppendInteger(&buf, p->nCol*3+1, &rc);
+ sessionAppendStr(&buf, " OR 1", &rc);
+ for(i=0; i<p->nCol; i++){
+ if( !p->abPK[i] ){
+ sessionAppendStr(&buf, " AND (?", &rc);
+ sessionAppendInteger(&buf, i*3+2, &rc);
+ sessionAppendStr(&buf, "=0 OR ", &rc);
+ sessionAppendIdent(&buf, p->azCol[i], &rc);
+ sessionAppendStr(&buf, " IS ?", &rc);
+ sessionAppendInteger(&buf, i*3+1, &rc);
+ sessionAppendStr(&buf, ")", &rc);
+ }
+ }
+ sessionAppendStr(&buf, ")", &rc);
+
+ if( rc==SQLITE_OK ){
+ rc = sqlite3_prepare_v2(db, (char *)buf.aBuf, buf.nBuf, &p->pUpdate, 0);
+ }
+ sqlite3_free(buf.aBuf);
+
+ return rc;
+}
+
+/*
+** Formulate and prepare an SQL statement to query table zTab by primary
+** key. Assuming the following table structure:
+**
+** CREATE TABLE x(a, b, c, d, PRIMARY KEY(a, c));
+**
+** The SELECT statement looks like this:
+**
+** SELECT * FROM x WHERE a = ?1 AND c = ?3
+**
+** If successful, SQLITE_OK is returned and SessionApplyCtx.pSelect is left
+** pointing to the prepared version of the SQL statement.
+*/
+static int sessionSelectRow(
+ sqlite3 *db, /* Database handle */
+ const char *zTab, /* Table name */
+ SessionApplyCtx *p /* Session changeset-apply context */
+){
+ return sessionSelectStmt(
+ db, "main", zTab, p->nCol, p->azCol, p->abPK, &p->pSelect);
+}
+
+/*
+** Formulate and prepare an INSERT statement to add a record to table zTab.
+** For example:
+**
+** INSERT INTO main."zTab" VALUES(?1, ?2, ?3 ...);
+**
+** If successful, SQLITE_OK is returned and SessionApplyCtx.pInsert is left
+** pointing to the prepared version of the SQL statement.
+*/
+static int sessionInsertRow(
+ sqlite3 *db, /* Database handle */
+ const char *zTab, /* Table name */
+ SessionApplyCtx *p /* Session changeset-apply context */
+){
+ int rc = SQLITE_OK;
+ int i;
+ SessionBuffer buf = {0, 0, 0};
+
+ sessionAppendStr(&buf, "INSERT INTO main.", &rc);
+ sessionAppendIdent(&buf, zTab, &rc);
+ sessionAppendStr(&buf, " VALUES(?", &rc);
+ for(i=1; i<p->nCol; i++){
+ sessionAppendStr(&buf, ", ?", &rc);
+ }
+ sessionAppendStr(&buf, ")", &rc);
+
+ if( rc==SQLITE_OK ){
+ rc = sqlite3_prepare_v2(db, (char *)buf.aBuf, buf.nBuf, &p->pInsert, 0);
+ }
+ sqlite3_free(buf.aBuf);
+ return rc;
+}
+
+/*
+** A wrapper around sqlite3_bind_value() that detects an extra problem.
+** See comments in the body of this function for details.
+*/
+static int sessionBindValue(
+ sqlite3_stmt *pStmt, /* Statement to bind value to */
+ int i, /* Parameter number to bind to */
+ sqlite3_value *pVal /* Value to bind */
+){
+ int eType = sqlite3_value_type(pVal);
+ /* COVERAGE: The (pVal->z==0) branch is never true using current versions
+ ** of SQLite. If a malloc fails in an sqlite3_value_xxx() function, either
+ ** the (pVal->z) variable remains as it was or the type of the value is
+ ** set to SQLITE_NULL. */
+ if( (eType==SQLITE_TEXT || eType==SQLITE_BLOB) && pVal->z==0 ){
+ /* This condition occurs when an earlier OOM in a call to
+ ** sqlite3_value_text() or sqlite3_value_blob() (perhaps from within
+ ** a conflict-handler) has zeroed the pVal->z pointer. Return NOMEM. */
+ return SQLITE_NOMEM;
+ }
+ return sqlite3_bind_value(pStmt, i, pVal);
+}
+
+/*
+** Iterator pIter must point to an SQLITE_INSERT entry. This function
+** transfers new.* values from the current iterator entry to statement
+** pStmt. The table being inserted into has nCol columns.
+**
+** New.* value $i from the iterator is bound to variable ($i+1) of
+** statement pStmt. If parameter abPK is NULL, all values from 0 to (nCol-1)
+** are transfered to the statement. Otherwise, if abPK is not NULL, it points
+** to an array nCol elements in size. In this case only those values for
+** which abPK[$i] is true are read from the iterator and bound to the
+** statement.
+**
+** An SQLite error code is returned if an error occurs. Otherwise, SQLITE_OK.
+*/
+static int sessionBindRow(
+ sqlite3_changeset_iter *pIter, /* Iterator to read values from */
+ int(*xValue)(sqlite3_changeset_iter *, int, sqlite3_value **),
+ int nCol, /* Number of columns */
+ u8 *abPK, /* If not NULL, bind only if true */
+ sqlite3_stmt *pStmt /* Bind values to this statement */
+){
+ int i;
+ int rc = SQLITE_OK;
+
+ /* Neither sqlite3changeset_old or sqlite3changeset_new can fail if the
+ ** argument iterator points to a suitable entry. Make sure that xValue
+ ** is one of these to guarantee that it is safe to ignore the return
+ ** in the code below. */
+ assert( xValue==sqlite3changeset_old || xValue==sqlite3changeset_new );
+
+ for(i=0; rc==SQLITE_OK && i<nCol; i++){
+ if( !abPK || abPK[i] ){
+ sqlite3_value *pVal;
+ (void)xValue(pIter, i, &pVal);
+ rc = sessionBindValue(pStmt, i+1, pVal);
+ }
+ }
+ return rc;
+}
+
+/*
+** SQL statement pSelect is as generated by the sessionSelectRow() function.
+** This function binds the primary key values from the change that changeset
+** iterator pIter points to to the SELECT and attempts to seek to the table
+** entry. If a row is found, the SELECT statement left pointing at the row
+** and SQLITE_ROW is returned. Otherwise, if no row is found and no error
+** has occured, the statement is reset and SQLITE_OK is returned. If an
+** error occurs, the statement is reset and an SQLite error code is returned.
+**
+** If this function returns SQLITE_ROW, the caller must eventually reset()
+** statement pSelect. If any other value is returned, the statement does
+** not require a reset().
+**
+** If the iterator currently points to an INSERT record, bind values from the
+** new.* record to the SELECT statement. Or, if it points to a DELETE or
+** UPDATE, bind values from the old.* record.
+*/
+static int sessionSeekToRow(
+ sqlite3 *db, /* Database handle */
+ sqlite3_changeset_iter *pIter, /* Changeset iterator */
+ u8 *abPK, /* Primary key flags array */
+ sqlite3_stmt *pSelect /* SELECT statement from sessionSelectRow() */
+){
+ int rc; /* Return code */
+ int nCol; /* Number of columns in table */
+ int op; /* Changset operation (SQLITE_UPDATE etc.) */
+ const char *zDummy; /* Unused */
+
+ sqlite3changeset_op(pIter, &zDummy, &nCol, &op, 0);
+ rc = sessionBindRow(pIter,
+ op==SQLITE_INSERT ? sqlite3changeset_new : sqlite3changeset_old,
+ nCol, abPK, pSelect
+ );
+
+ if( rc==SQLITE_OK ){
+ rc = sqlite3_step(pSelect);
+ if( rc!=SQLITE_ROW ) rc = sqlite3_reset(pSelect);
+ }
+
+ return rc;
+}
+
+/*
+** Invoke the conflict handler for the change that the changeset iterator
+** currently points to.
+**
+** Argument eType must be either CHANGESET_DATA or CHANGESET_CONFLICT.
+** If argument pbReplace is NULL, then the type of conflict handler invoked
+** depends solely on eType, as follows:
+**
+** eType value Value passed to xConflict
+** -------------------------------------------------
+** CHANGESET_DATA CHANGESET_NOTFOUND
+** CHANGESET_CONFLICT CHANGESET_CONSTRAINT
+**
+** Or, if pbReplace is not NULL, then an attempt is made to find an existing
+** record with the same primary key as the record about to be deleted, updated
+** or inserted. If such a record can be found, it is available to the conflict
+** handler as the "conflicting" record. In this case the type of conflict
+** handler invoked is as follows:
+**
+** eType value PK Record found? Value passed to xConflict
+** ----------------------------------------------------------------
+** CHANGESET_DATA Yes CHANGESET_DATA
+** CHANGESET_DATA No CHANGESET_NOTFOUND
+** CHANGESET_CONFLICT Yes CHANGESET_CONFLICT
+** CHANGESET_CONFLICT No CHANGESET_CONSTRAINT
+**
+** If pbReplace is not NULL, and a record with a matching PK is found, and
+** the conflict handler function returns SQLITE_CHANGESET_REPLACE, *pbReplace
+** is set to non-zero before returning SQLITE_OK.
+**
+** If the conflict handler returns SQLITE_CHANGESET_ABORT, SQLITE_ABORT is
+** returned. Or, if the conflict handler returns an invalid value,
+** SQLITE_MISUSE. If the conflict handler returns SQLITE_CHANGESET_OMIT,
+** this function returns SQLITE_OK.
+*/
+static int sessionConflictHandler(
+ int eType, /* Either CHANGESET_DATA or CONFLICT */
+ SessionApplyCtx *p, /* changeset_apply() context */
+ sqlite3_changeset_iter *pIter, /* Changeset iterator */
+ int(*xConflict)(void *, int, sqlite3_changeset_iter*),
+ void *pCtx, /* First argument for conflict handler */
+ int *pbReplace /* OUT: Set to true if PK row is found */
+){
+ int res = 0; /* Value returned by conflict handler */
+ int rc;
+ int nCol;
+ int op;
+ const char *zDummy;
+
+ sqlite3changeset_op(pIter, &zDummy, &nCol, &op, 0);
+
+ assert( eType==SQLITE_CHANGESET_CONFLICT || eType==SQLITE_CHANGESET_DATA );
+ assert( SQLITE_CHANGESET_CONFLICT+1==SQLITE_CHANGESET_CONSTRAINT );
+ assert( SQLITE_CHANGESET_DATA+1==SQLITE_CHANGESET_NOTFOUND );
+
+ /* Bind the new.* PRIMARY KEY values to the SELECT statement. */
+ if( pbReplace ){
+ rc = sessionSeekToRow(p->db, pIter, p->abPK, p->pSelect);
+ }else{
+ rc = SQLITE_OK;
+ }
+
+ if( rc==SQLITE_ROW ){
+ /* There exists another row with the new.* primary key. */
+ pIter->pConflict = p->pSelect;
+ res = xConflict(pCtx, eType, pIter);
+ pIter->pConflict = 0;
+ rc = sqlite3_reset(p->pSelect);
+ }else if( rc==SQLITE_OK ){
+ if( p->bDeferConstraints && eType==SQLITE_CHANGESET_CONFLICT ){
+ /* Instead of invoking the conflict handler, append the change blob
+ ** to the SessionApplyCtx.constraints buffer. */
+ u8 *aBlob = &pIter->in.aData[pIter->in.iCurrent];
+ int nBlob = pIter->in.iNext - pIter->in.iCurrent;
+ sessionAppendBlob(&p->constraints, aBlob, nBlob, &rc);
+ res = SQLITE_CHANGESET_OMIT;
+ }else{
+ /* No other row with the new.* primary key. */
+ res = xConflict(pCtx, eType+1, pIter);
+ if( res==SQLITE_CHANGESET_REPLACE ) rc = SQLITE_MISUSE;
+ }
+ }
+
+ if( rc==SQLITE_OK ){
+ switch( res ){
+ case SQLITE_CHANGESET_REPLACE:
+ assert( pbReplace );
+ *pbReplace = 1;
+ break;
+
+ case SQLITE_CHANGESET_OMIT:
+ break;
+
+ case SQLITE_CHANGESET_ABORT:
+ rc = SQLITE_ABORT;
+ break;
+
+ default:
+ rc = SQLITE_MISUSE;
+ break;
+ }
+ }
+
+ return rc;
+}
+
+/*
+** Attempt to apply the change that the iterator passed as the first argument
+** currently points to to the database. If a conflict is encountered, invoke
+** the conflict handler callback.
+**
+** If argument pbRetry is NULL, then ignore any CHANGESET_DATA conflict. If
+** one is encountered, update or delete the row with the matching primary key
+** instead. Or, if pbRetry is not NULL and a CHANGESET_DATA conflict occurs,
+** invoke the conflict handler. If it returns CHANGESET_REPLACE, set *pbRetry
+** to true before returning. In this case the caller will invoke this function
+** again, this time with pbRetry set to NULL.
+**
+** If argument pbReplace is NULL and a CHANGESET_CONFLICT conflict is
+** encountered invoke the conflict handler with CHANGESET_CONSTRAINT instead.
+** Or, if pbReplace is not NULL, invoke it with CHANGESET_CONFLICT. If such
+** an invocation returns SQLITE_CHANGESET_REPLACE, set *pbReplace to true
+** before retrying. In this case the caller attempts to remove the conflicting
+** row before invoking this function again, this time with pbReplace set
+** to NULL.
+**
+** If any conflict handler returns SQLITE_CHANGESET_ABORT, this function
+** returns SQLITE_ABORT. Otherwise, if no error occurs, SQLITE_OK is
+** returned.
+*/
+static int sessionApplyOneOp(
+ sqlite3_changeset_iter *pIter, /* Changeset iterator */
+ SessionApplyCtx *p, /* changeset_apply() context */
+ int(*xConflict)(void *, int, sqlite3_changeset_iter *),
+ void *pCtx, /* First argument for the conflict handler */
+ int *pbReplace, /* OUT: True to remove PK row and retry */
+ int *pbRetry /* OUT: True to retry. */
+){
+ const char *zDummy;
+ int op;
+ int nCol;
+ int rc = SQLITE_OK;
+
+ assert( p->pDelete && p->pUpdate && p->pInsert && p->pSelect );
+ assert( p->azCol && p->abPK );
+ assert( !pbReplace || *pbReplace==0 );
+
+ sqlite3changeset_op(pIter, &zDummy, &nCol, &op, 0);
+
+ if( op==SQLITE_DELETE ){
+
+ /* Bind values to the DELETE statement. If conflict handling is required,
+ ** bind values for all columns and set bound variable (nCol+1) to true.
+ ** Or, if conflict handling is not required, bind just the PK column
+ ** values and, if it exists, set (nCol+1) to false. Conflict handling
+ ** is not required if:
+ **
+ ** * this is a patchset, or
+ ** * (pbRetry==0), or
+ ** * all columns of the table are PK columns (in this case there is
+ ** no (nCol+1) variable to bind to).
+ */
+ u8 *abPK = (pIter->bPatchset ? p->abPK : 0);
+ rc = sessionBindRow(pIter, sqlite3changeset_old, nCol, abPK, p->pDelete);
+ if( rc==SQLITE_OK && sqlite3_bind_parameter_count(p->pDelete)>nCol ){
+ rc = sqlite3_bind_int(p->pDelete, nCol+1, (pbRetry==0 || abPK));
+ }
+ if( rc!=SQLITE_OK ) return rc;
+
+ sqlite3_step(p->pDelete);
+ rc = sqlite3_reset(p->pDelete);
+ if( rc==SQLITE_OK && sqlite3_changes(p->db)==0 ){
+ rc = sessionConflictHandler(
+ SQLITE_CHANGESET_DATA, p, pIter, xConflict, pCtx, pbRetry
+ );
+ }else if( (rc&0xff)==SQLITE_CONSTRAINT ){
+ rc = sessionConflictHandler(
+ SQLITE_CHANGESET_CONFLICT, p, pIter, xConflict, pCtx, 0
+ );
+ }
+
+ }else if( op==SQLITE_UPDATE ){
+ int i;
+
+ /* Bind values to the UPDATE statement. */
+ for(i=0; rc==SQLITE_OK && i<nCol; i++){
+ sqlite3_value *pOld = sessionChangesetOld(pIter, i);
+ sqlite3_value *pNew = sessionChangesetNew(pIter, i);
+
+ sqlite3_bind_int(p->pUpdate, i*3+2, !!pNew);
+ if( pOld ){
+ rc = sessionBindValue(p->pUpdate, i*3+1, pOld);
+ }
+ if( rc==SQLITE_OK && pNew ){
+ rc = sessionBindValue(p->pUpdate, i*3+3, pNew);
+ }
+ }
+ if( rc==SQLITE_OK ){
+ sqlite3_bind_int(p->pUpdate, nCol*3+1, pbRetry==0 || pIter->bPatchset);
+ }
+ if( rc!=SQLITE_OK ) return rc;
+
+ /* Attempt the UPDATE. In the case of a NOTFOUND or DATA conflict,
+ ** the result will be SQLITE_OK with 0 rows modified. */
+ sqlite3_step(p->pUpdate);
+ rc = sqlite3_reset(p->pUpdate);
+
+ if( rc==SQLITE_OK && sqlite3_changes(p->db)==0 ){
+ /* A NOTFOUND or DATA error. Search the table to see if it contains
+ ** a row with a matching primary key. If so, this is a DATA conflict.
+ ** Otherwise, if there is no primary key match, it is a NOTFOUND. */
+
+ rc = sessionConflictHandler(
+ SQLITE_CHANGESET_DATA, p, pIter, xConflict, pCtx, pbRetry
+ );
+
+ }else if( (rc&0xff)==SQLITE_CONSTRAINT ){
+ /* This is always a CONSTRAINT conflict. */
+ rc = sessionConflictHandler(
+ SQLITE_CHANGESET_CONFLICT, p, pIter, xConflict, pCtx, 0
+ );
+ }
+
+ }else{
+ assert( op==SQLITE_INSERT );
+ rc = sessionBindRow(pIter, sqlite3changeset_new, nCol, 0, p->pInsert);
+ if( rc!=SQLITE_OK ) return rc;
+
+ sqlite3_step(p->pInsert);
+ rc = sqlite3_reset(p->pInsert);
+ if( (rc&0xff)==SQLITE_CONSTRAINT ){
+ rc = sessionConflictHandler(
+ SQLITE_CHANGESET_CONFLICT, p, pIter, xConflict, pCtx, pbReplace
+ );
+ }
+ }
+
+ return rc;
+}
+
+/*
+** Attempt to apply the change that the iterator passed as the first argument
+** currently points to to the database. If a conflict is encountered, invoke
+** the conflict handler callback.
+**
+** The difference between this function and sessionApplyOne() is that this
+** function handles the case where the conflict-handler is invoked and
+** returns SQLITE_CHANGESET_REPLACE - indicating that the change should be
+** retried in some manner.
+*/
+static int sessionApplyOneWithRetry(
+ sqlite3 *db, /* Apply change to "main" db of this handle */
+ sqlite3_changeset_iter *pIter, /* Changeset iterator to read change from */
+ SessionApplyCtx *pApply, /* Apply context */
+ int(*xConflict)(void*, int, sqlite3_changeset_iter*),
+ void *pCtx /* First argument passed to xConflict */
+){
+ int bReplace = 0;
+ int bRetry = 0;
+ int rc;
+
+ rc = sessionApplyOneOp(pIter, pApply, xConflict, pCtx, &bReplace, &bRetry);
+ assert( rc==SQLITE_OK || (bRetry==0 && bReplace==0) );
+
+ /* If the bRetry flag is set, the change has not been applied due to an
+ ** SQLITE_CHANGESET_DATA problem (i.e. this is an UPDATE or DELETE and
+ ** a row with the correct PK is present in the db, but one or more other
+ ** fields do not contain the expected values) and the conflict handler
+ ** returned SQLITE_CHANGESET_REPLACE. In this case retry the operation,
+ ** but pass NULL as the final argument so that sessionApplyOneOp() ignores
+ ** the SQLITE_CHANGESET_DATA problem. */
+ if( bRetry ){
+ assert( pIter->op==SQLITE_UPDATE || pIter->op==SQLITE_DELETE );
+ rc = sessionApplyOneOp(pIter, pApply, xConflict, pCtx, 0, 0);
+ }
+
+ /* If the bReplace flag is set, the change is an INSERT that has not
+ ** been performed because the database already contains a row with the
+ ** specified primary key and the conflict handler returned
+ ** SQLITE_CHANGESET_REPLACE. In this case remove the conflicting row
+ ** before reattempting the INSERT. */
+ else if( bReplace ){
+ assert( pIter->op==SQLITE_INSERT );
+ rc = sqlite3_exec(db, "SAVEPOINT replace_op", 0, 0, 0);
+ if( rc==SQLITE_OK ){
+ rc = sessionBindRow(pIter,
+ sqlite3changeset_new, pApply->nCol, pApply->abPK, pApply->pDelete);
+ sqlite3_bind_int(pApply->pDelete, pApply->nCol+1, 1);
+ }
+ if( rc==SQLITE_OK ){
+ sqlite3_step(pApply->pDelete);
+ rc = sqlite3_reset(pApply->pDelete);
+ }
+ if( rc==SQLITE_OK ){
+ rc = sessionApplyOneOp(pIter, pApply, xConflict, pCtx, 0, 0);
+ }
+ if( rc==SQLITE_OK ){
+ rc = sqlite3_exec(db, "RELEASE replace_op", 0, 0, 0);
+ }
+ }
+
+ return rc;
+}
+
+/*
+** Retry the changes accumulated in the pApply->constraints buffer.
+*/
+static int sessionRetryConstraints(
+ sqlite3 *db,
+ int bPatchset,
+ const char *zTab,
+ SessionApplyCtx *pApply,
+ int(*xConflict)(void*, int, sqlite3_changeset_iter*),
+ void *pCtx /* First argument passed to xConflict */
+){
+ int rc = SQLITE_OK;
+
+ while( pApply->constraints.nBuf ){
+ sqlite3_changeset_iter *pIter2 = 0;
+ SessionBuffer cons = pApply->constraints;
+ memset(&pApply->constraints, 0, sizeof(SessionBuffer));
+
+ rc = sessionChangesetStart(&pIter2, 0, 0, cons.nBuf, cons.aBuf);
+ if( rc==SQLITE_OK ){
+ int nByte = 2*pApply->nCol*sizeof(sqlite3_value*);
+ int rc2;
+ pIter2->bPatchset = bPatchset;
+ pIter2->zTab = (char*)zTab;
+ pIter2->nCol = pApply->nCol;
+ pIter2->abPK = pApply->abPK;
+ sessionBufferGrow(&pIter2->tblhdr, nByte, &rc);
+ pIter2->apValue = (sqlite3_value**)pIter2->tblhdr.aBuf;
+ if( rc==SQLITE_OK ) memset(pIter2->apValue, 0, nByte);
+
+ while( rc==SQLITE_OK && SQLITE_ROW==sqlite3changeset_next(pIter2) ){
+ rc = sessionApplyOneWithRetry(db, pIter2, pApply, xConflict, pCtx);
+ }
+
+ rc2 = sqlite3changeset_finalize(pIter2);
+ if( rc==SQLITE_OK ) rc = rc2;
+ }
+ assert( pApply->bDeferConstraints || pApply->constraints.nBuf==0 );
+
+ sqlite3_free(cons.aBuf);
+ if( rc!=SQLITE_OK ) break;
+ if( pApply->constraints.nBuf>=cons.nBuf ){
+ /* No progress was made on the last round. */
+ pApply->bDeferConstraints = 0;
+ }
+ }
+
+ return rc;
+}
+
+/*
+** Argument pIter is a changeset iterator that has been initialized, but
+** not yet passed to sqlite3changeset_next(). This function applies the
+** changeset to the main database attached to handle "db". The supplied
+** conflict handler callback is invoked to resolve any conflicts encountered
+** while applying the change.
+*/
+static int sessionChangesetApply(
+ sqlite3 *db, /* Apply change to "main" db of this handle */
+ sqlite3_changeset_iter *pIter, /* Changeset to apply */
+ int(*xFilter)(
+ void *pCtx, /* Copy of sixth arg to _apply() */
+ const char *zTab /* Table name */
+ ),
+ int(*xConflict)(
+ void *pCtx, /* Copy of fifth arg to _apply() */
+ int eConflict, /* DATA, MISSING, CONFLICT, CONSTRAINT */
+ sqlite3_changeset_iter *p /* Handle describing change and conflict */
+ ),
+ void *pCtx /* First argument passed to xConflict */
+){
+ int schemaMismatch = 0;
+ int rc; /* Return code */
+ const char *zTab = 0; /* Name of current table */
+ int nTab = 0; /* Result of sqlite3Strlen30(zTab) */
+ SessionApplyCtx sApply; /* changeset_apply() context object */
+ int bPatchset;
+
+ assert( xConflict!=0 );
+
+ pIter->in.bNoDiscard = 1;
+ memset(&sApply, 0, sizeof(sApply));
+ sqlite3_mutex_enter(sqlite3_db_mutex(db));
+ rc = sqlite3_exec(db, "SAVEPOINT changeset_apply", 0, 0, 0);
+ if( rc==SQLITE_OK ){
+ rc = sqlite3_exec(db, "PRAGMA defer_foreign_keys = 1", 0, 0, 0);
+ }
+ while( rc==SQLITE_OK && SQLITE_ROW==sqlite3changeset_next(pIter) ){
+ int nCol;
+ int op;
+ const char *zNew;
+
+ sqlite3changeset_op(pIter, &zNew, &nCol, &op, 0);
+
+ if( zTab==0 || sqlite3_strnicmp(zNew, zTab, nTab+1) ){
+ u8 *abPK;
+
+ rc = sessionRetryConstraints(
+ db, pIter->bPatchset, zTab, &sApply, xConflict, pCtx
+ );
+ if( rc!=SQLITE_OK ) break;
+
+ sqlite3_free((char*)sApply.azCol); /* cast works around VC++ bug */
+ sqlite3_finalize(sApply.pDelete);
+ sqlite3_finalize(sApply.pUpdate);
+ sqlite3_finalize(sApply.pInsert);
+ sqlite3_finalize(sApply.pSelect);
+ memset(&sApply, 0, sizeof(sApply));
+ sApply.db = db;
+ sApply.bDeferConstraints = 1;
+
+ /* If an xFilter() callback was specified, invoke it now. If the
+ ** xFilter callback returns zero, skip this table. If it returns
+ ** non-zero, proceed. */
+ schemaMismatch = (xFilter && (0==xFilter(pCtx, zNew)));
+ if( schemaMismatch ){
+ zTab = sqlite3_mprintf("%s", zNew);
+ if( zTab==0 ){
+ rc = SQLITE_NOMEM;
+ break;
+ }
+ nTab = (int)strlen(zTab);
+ sApply.azCol = (const char **)zTab;
+ }else{
+ sqlite3changeset_pk(pIter, &abPK, 0);
+ rc = sessionTableInfo(
+ db, "main", zNew, &sApply.nCol, &zTab, &sApply.azCol, &sApply.abPK
+ );
+ if( rc!=SQLITE_OK ) break;
+
+ if( sApply.nCol==0 ){
+ schemaMismatch = 1;
+ sqlite3_log(SQLITE_SCHEMA,
+ "sqlite3changeset_apply(): no such table: %s", zTab
+ );
+ }
+ else if( sApply.nCol!=nCol ){
+ schemaMismatch = 1;
+ sqlite3_log(SQLITE_SCHEMA,
+ "sqlite3changeset_apply(): table %s has %d columns, expected %d",
+ zTab, sApply.nCol, nCol
+ );
+ }
+ else if( memcmp(sApply.abPK, abPK, nCol)!=0 ){
+ schemaMismatch = 1;
+ sqlite3_log(SQLITE_SCHEMA, "sqlite3changeset_apply(): "
+ "primary key mismatch for table %s", zTab
+ );
+ }
+ else if(
+ (rc = sessionSelectRow(db, zTab, &sApply))
+ || (rc = sessionUpdateRow(db, zTab, &sApply))
+ || (rc = sessionDeleteRow(db, zTab, &sApply))
+ || (rc = sessionInsertRow(db, zTab, &sApply))
+ ){
+ break;
+ }
+ nTab = sqlite3Strlen30(zTab);
+ }
+ }
+
+ /* If there is a schema mismatch on the current table, proceed to the
+ ** next change. A log message has already been issued. */
+ if( schemaMismatch ) continue;
+
+ rc = sessionApplyOneWithRetry(db, pIter, &sApply, xConflict, pCtx);
+ }
+
+ bPatchset = pIter->bPatchset;
+ if( rc==SQLITE_OK ){
+ rc = sqlite3changeset_finalize(pIter);
+ }else{
+ sqlite3changeset_finalize(pIter);
+ }
+
+ if( rc==SQLITE_OK ){
+ rc = sessionRetryConstraints(db, bPatchset, zTab, &sApply, xConflict, pCtx);
+ }
+
+ if( rc==SQLITE_OK ){
+ int nFk, notUsed;
+ sqlite3_db_status(db, SQLITE_DBSTATUS_DEFERRED_FKS, &nFk, &notUsed, 0);
+ if( nFk!=0 ){
+ int res = SQLITE_CHANGESET_ABORT;
+ sqlite3_changeset_iter sIter;
+ memset(&sIter, 0, sizeof(sIter));
+ sIter.nCol = nFk;
+ res = xConflict(pCtx, SQLITE_CHANGESET_FOREIGN_KEY, &sIter);
+ if( res!=SQLITE_CHANGESET_OMIT ){
+ rc = SQLITE_CONSTRAINT;
+ }
+ }
+ }
+ sqlite3_exec(db, "PRAGMA defer_foreign_keys = 0", 0, 0, 0);
+
+ if( rc==SQLITE_OK ){
+ rc = sqlite3_exec(db, "RELEASE changeset_apply", 0, 0, 0);
+ }else{
+ sqlite3_exec(db, "ROLLBACK TO changeset_apply", 0, 0, 0);
+ sqlite3_exec(db, "RELEASE changeset_apply", 0, 0, 0);
+ }
+
+ sqlite3_finalize(sApply.pInsert);
+ sqlite3_finalize(sApply.pDelete);
+ sqlite3_finalize(sApply.pUpdate);
+ sqlite3_finalize(sApply.pSelect);
+ sqlite3_free((char*)sApply.azCol); /* cast works around VC++ bug */
+ sqlite3_free((char*)sApply.constraints.aBuf);
+ sqlite3_mutex_leave(sqlite3_db_mutex(db));
+ return rc;
+}
+
+/*
+** Apply the changeset passed via pChangeset/nChangeset to the main database
+** attached to handle "db". Invoke the supplied conflict handler callback
+** to resolve any conflicts encountered while applying the change.
+*/
+SQLITE_API int SQLITE_STDCALL sqlite3changeset_apply(
+ sqlite3 *db, /* Apply change to "main" db of this handle */
+ int nChangeset, /* Size of changeset in bytes */
+ void *pChangeset, /* Changeset blob */
+ int(*xFilter)(
+ void *pCtx, /* Copy of sixth arg to _apply() */
+ const char *zTab /* Table name */
+ ),
+ int(*xConflict)(
+ void *pCtx, /* Copy of fifth arg to _apply() */
+ int eConflict, /* DATA, MISSING, CONFLICT, CONSTRAINT */
+ sqlite3_changeset_iter *p /* Handle describing change and conflict */
+ ),
+ void *pCtx /* First argument passed to xConflict */
+){
+ sqlite3_changeset_iter *pIter; /* Iterator to skip through changeset */
+ int rc = sqlite3changeset_start(&pIter, nChangeset, pChangeset);
+ if( rc==SQLITE_OK ){
+ rc = sessionChangesetApply(db, pIter, xFilter, xConflict, pCtx);
+ }
+ return rc;
+}
+
+/*
+** Apply the changeset passed via xInput/pIn to the main database
+** attached to handle "db". Invoke the supplied conflict handler callback
+** to resolve any conflicts encountered while applying the change.
+*/
+SQLITE_API int SQLITE_STDCALL sqlite3changeset_apply_strm(
+ sqlite3 *db, /* Apply change to "main" db of this handle */
+ int (*xInput)(void *pIn, void *pData, int *pnData), /* Input function */
+ void *pIn, /* First arg for xInput */
+ int(*xFilter)(
+ void *pCtx, /* Copy of sixth arg to _apply() */
+ const char *zTab /* Table name */
+ ),
+ int(*xConflict)(
+ void *pCtx, /* Copy of sixth arg to _apply() */
+ int eConflict, /* DATA, MISSING, CONFLICT, CONSTRAINT */
+ sqlite3_changeset_iter *p /* Handle describing change and conflict */
+ ),
+ void *pCtx /* First argument passed to xConflict */
+){
+ sqlite3_changeset_iter *pIter; /* Iterator to skip through changeset */
+ int rc = sqlite3changeset_start_strm(&pIter, xInput, pIn);
+ if( rc==SQLITE_OK ){
+ rc = sessionChangesetApply(db, pIter, xFilter, xConflict, pCtx);
+ }
+ return rc;
+}
+
+/*
+** sqlite3_changegroup handle.
+*/
+struct sqlite3_changegroup {
+ int rc; /* Error code */
+ int bPatch; /* True to accumulate patchsets */
+ SessionTable *pList; /* List of tables in current patch */
+};
+
+/*
+** This function is called to merge two changes to the same row together as
+** part of an sqlite3changeset_concat() operation. A new change object is
+** allocated and a pointer to it stored in *ppNew.
+*/
+static int sessionChangeMerge(
+ SessionTable *pTab, /* Table structure */
+ int bPatchset, /* True for patchsets */
+ SessionChange *pExist, /* Existing change */
+ int op2, /* Second change operation */
+ int bIndirect, /* True if second change is indirect */
+ u8 *aRec, /* Second change record */
+ int nRec, /* Number of bytes in aRec */
+ SessionChange **ppNew /* OUT: Merged change */
+){
+ SessionChange *pNew = 0;
+
+ if( !pExist ){
+ pNew = (SessionChange *)sqlite3_malloc(sizeof(SessionChange) + nRec);
+ if( !pNew ){
+ return SQLITE_NOMEM;
+ }
+ memset(pNew, 0, sizeof(SessionChange));
+ pNew->op = op2;
+ pNew->bIndirect = bIndirect;
+ pNew->nRecord = nRec;
+ pNew->aRecord = (u8*)&pNew[1];
+ memcpy(pNew->aRecord, aRec, nRec);
+ }else{
+ int op1 = pExist->op;
+
+ /*
+ ** op1=INSERT, op2=INSERT -> Unsupported. Discard op2.
+ ** op1=INSERT, op2=UPDATE -> INSERT.
+ ** op1=INSERT, op2=DELETE -> (none)
+ **
+ ** op1=UPDATE, op2=INSERT -> Unsupported. Discard op2.
+ ** op1=UPDATE, op2=UPDATE -> UPDATE.
+ ** op1=UPDATE, op2=DELETE -> DELETE.
+ **
+ ** op1=DELETE, op2=INSERT -> UPDATE.
+ ** op1=DELETE, op2=UPDATE -> Unsupported. Discard op2.
+ ** op1=DELETE, op2=DELETE -> Unsupported. Discard op2.
+ */
+ if( (op1==SQLITE_INSERT && op2==SQLITE_INSERT)
+ || (op1==SQLITE_UPDATE && op2==SQLITE_INSERT)
+ || (op1==SQLITE_DELETE && op2==SQLITE_UPDATE)
+ || (op1==SQLITE_DELETE && op2==SQLITE_DELETE)
+ ){
+ pNew = pExist;
+ }else if( op1==SQLITE_INSERT && op2==SQLITE_DELETE ){
+ sqlite3_free(pExist);
+ assert( pNew==0 );
+ }else{
+ u8 *aExist = pExist->aRecord;
+ int nByte;
+ u8 *aCsr;
+
+ /* Allocate a new SessionChange object. Ensure that the aRecord[]
+ ** buffer of the new object is large enough to hold any record that
+ ** may be generated by combining the input records. */
+ nByte = sizeof(SessionChange) + pExist->nRecord + nRec;
+ pNew = (SessionChange *)sqlite3_malloc(nByte);
+ if( !pNew ){
+ sqlite3_free(pExist);
+ return SQLITE_NOMEM;
+ }
+ memset(pNew, 0, sizeof(SessionChange));
+ pNew->bIndirect = (bIndirect && pExist->bIndirect);
+ aCsr = pNew->aRecord = (u8 *)&pNew[1];
+
+ if( op1==SQLITE_INSERT ){ /* INSERT + UPDATE */
+ u8 *a1 = aRec;
+ assert( op2==SQLITE_UPDATE );
+ pNew->op = SQLITE_INSERT;
+ if( bPatchset==0 ) sessionSkipRecord(&a1, pTab->nCol);
+ sessionMergeRecord(&aCsr, pTab->nCol, aExist, a1);
+ }else if( op1==SQLITE_DELETE ){ /* DELETE + INSERT */
+ assert( op2==SQLITE_INSERT );
+ pNew->op = SQLITE_UPDATE;
+ if( bPatchset ){
+ memcpy(aCsr, aRec, nRec);
+ aCsr += nRec;
+ }else{
+ if( 0==sessionMergeUpdate(&aCsr, pTab, bPatchset, aExist, 0,aRec,0) ){
+ sqlite3_free(pNew);
+ pNew = 0;
+ }
+ }
+ }else if( op2==SQLITE_UPDATE ){ /* UPDATE + UPDATE */
+ u8 *a1 = aExist;
+ u8 *a2 = aRec;
+ assert( op1==SQLITE_UPDATE );
+ if( bPatchset==0 ){
+ sessionSkipRecord(&a1, pTab->nCol);
+ sessionSkipRecord(&a2, pTab->nCol);
+ }
+ pNew->op = SQLITE_UPDATE;
+ if( 0==sessionMergeUpdate(&aCsr, pTab, bPatchset, aRec, aExist,a1,a2) ){
+ sqlite3_free(pNew);
+ pNew = 0;
+ }
+ }else{ /* UPDATE + DELETE */
+ assert( op1==SQLITE_UPDATE && op2==SQLITE_DELETE );
+ pNew->op = SQLITE_DELETE;
+ if( bPatchset ){
+ memcpy(aCsr, aRec, nRec);
+ aCsr += nRec;
+ }else{
+ sessionMergeRecord(&aCsr, pTab->nCol, aRec, aExist);
+ }
+ }
+
+ if( pNew ){
+ pNew->nRecord = (int)(aCsr - pNew->aRecord);
+ }
+ sqlite3_free(pExist);
+ }
+ }
+
+ *ppNew = pNew;
+ return SQLITE_OK;
+}
+
+/*
+** Add all changes in the changeset traversed by the iterator passed as
+** the first argument to the changegroup hash tables.
+*/
+static int sessionChangesetToHash(
+ sqlite3_changeset_iter *pIter, /* Iterator to read from */
+ sqlite3_changegroup *pGrp /* Changegroup object to add changeset to */
+){
+ u8 *aRec;
+ int nRec;
+ int rc = SQLITE_OK;
+ SessionTable *pTab = 0;
+
+
+ while( SQLITE_ROW==sessionChangesetNext(pIter, &aRec, &nRec) ){
+ const char *zNew;
+ int nCol;
+ int op;
+ int iHash;
+ int bIndirect;
+ SessionChange *pChange;
+ SessionChange *pExist = 0;
+ SessionChange **pp;
+
+ if( pGrp->pList==0 ){
+ pGrp->bPatch = pIter->bPatchset;
+ }else if( pIter->bPatchset!=pGrp->bPatch ){
+ rc = SQLITE_ERROR;
+ break;
+ }
+
+ sqlite3changeset_op(pIter, &zNew, &nCol, &op, &bIndirect);
+ if( !pTab || sqlite3_stricmp(zNew, pTab->zName) ){
+ /* Search the list for a matching table */
+ int nNew = (int)strlen(zNew);
+ u8 *abPK;
+
+ sqlite3changeset_pk(pIter, &abPK, 0);
+ for(pTab = pGrp->pList; pTab; pTab=pTab->pNext){
+ if( 0==sqlite3_strnicmp(pTab->zName, zNew, nNew+1) ) break;
+ }
+ if( !pTab ){
+ SessionTable **ppTab;
+
+ pTab = sqlite3_malloc(sizeof(SessionTable) + nCol + nNew+1);
+ if( !pTab ){
+ rc = SQLITE_NOMEM;
+ break;
+ }
+ memset(pTab, 0, sizeof(SessionTable));
+ pTab->nCol = nCol;
+ pTab->abPK = (u8*)&pTab[1];
+ memcpy(pTab->abPK, abPK, nCol);
+ pTab->zName = (char*)&pTab->abPK[nCol];
+ memcpy(pTab->zName, zNew, nNew+1);
+
+ /* The new object must be linked on to the end of the list, not
+ ** simply added to the start of it. This is to ensure that the
+ ** tables within the output of sqlite3changegroup_output() are in
+ ** the right order. */
+ for(ppTab=&pGrp->pList; *ppTab; ppTab=&(*ppTab)->pNext);
+ *ppTab = pTab;
+ }else if( pTab->nCol!=nCol || memcmp(pTab->abPK, abPK, nCol) ){
+ rc = SQLITE_SCHEMA;
+ break;
+ }
+ }
+
+ if( sessionGrowHash(pIter->bPatchset, pTab) ){
+ rc = SQLITE_NOMEM;
+ break;
+ }
+ iHash = sessionChangeHash(
+ pTab, (pIter->bPatchset && op==SQLITE_DELETE), aRec, pTab->nChange
+ );
+
+ /* Search for existing entry. If found, remove it from the hash table.
+ ** Code below may link it back in.
+ */
+ for(pp=&pTab->apChange[iHash]; *pp; pp=&(*pp)->pNext){
+ int bPkOnly1 = 0;
+ int bPkOnly2 = 0;
+ if( pIter->bPatchset ){
+ bPkOnly1 = (*pp)->op==SQLITE_DELETE;
+ bPkOnly2 = op==SQLITE_DELETE;
+ }
+ if( sessionChangeEqual(pTab, bPkOnly1, (*pp)->aRecord, bPkOnly2, aRec) ){
+ pExist = *pp;
+ *pp = (*pp)->pNext;
+ pTab->nEntry--;
+ break;
+ }
+ }
+
+ rc = sessionChangeMerge(pTab,
+ pIter->bPatchset, pExist, op, bIndirect, aRec, nRec, &pChange
+ );
+ if( rc ) break;
+ if( pChange ){
+ pChange->pNext = pTab->apChange[iHash];
+ pTab->apChange[iHash] = pChange;
+ pTab->nEntry++;
+ }
+ }
+
+ if( rc==SQLITE_OK ) rc = pIter->rc;
+ return rc;
+}
+
+/*
+** Serialize a changeset (or patchset) based on all changesets (or patchsets)
+** added to the changegroup object passed as the first argument.
+**
+** If xOutput is not NULL, then the changeset/patchset is returned to the
+** user via one or more calls to xOutput, as with the other streaming
+** interfaces.
+**
+** Or, if xOutput is NULL, then (*ppOut) is populated with a pointer to a
+** buffer containing the output changeset before this function returns. In
+** this case (*pnOut) is set to the size of the output buffer in bytes. It
+** is the responsibility of the caller to free the output buffer using
+** sqlite3_free() when it is no longer required.
+**
+** If successful, SQLITE_OK is returned. Or, if an error occurs, an SQLite
+** error code. If an error occurs and xOutput is NULL, (*ppOut) and (*pnOut)
+** are both set to 0 before returning.
+*/
+static int sessionChangegroupOutput(
+ sqlite3_changegroup *pGrp,
+ int (*xOutput)(void *pOut, const void *pData, int nData),
+ void *pOut,
+ int *pnOut,
+ void **ppOut
+){
+ int rc = SQLITE_OK;
+ SessionBuffer buf = {0, 0, 0};
+ SessionTable *pTab;
+ assert( xOutput==0 || (ppOut==0 && pnOut==0) );
+
+ /* Create the serialized output changeset based on the contents of the
+ ** hash tables attached to the SessionTable objects in list p->pList.
+ */
+ for(pTab=pGrp->pList; rc==SQLITE_OK && pTab; pTab=pTab->pNext){
+ int i;
+ if( pTab->nEntry==0 ) continue;
+
+ sessionAppendTableHdr(&buf, pGrp->bPatch, pTab, &rc);
+ for(i=0; i<pTab->nChange; i++){
+ SessionChange *p;
+ for(p=pTab->apChange[i]; p; p=p->pNext){
+ sessionAppendByte(&buf, p->op, &rc);
+ sessionAppendByte(&buf, p->bIndirect, &rc);
+ sessionAppendBlob(&buf, p->aRecord, p->nRecord, &rc);
+ }
+ }
+
+ if( rc==SQLITE_OK && xOutput && buf.nBuf>=SESSIONS_STRM_CHUNK_SIZE ){
+ rc = xOutput(pOut, buf.aBuf, buf.nBuf);
+ buf.nBuf = 0;
+ }
+ }
+
+ if( rc==SQLITE_OK ){
+ if( xOutput ){
+ if( buf.nBuf>0 ) rc = xOutput(pOut, buf.aBuf, buf.nBuf);
+ }else{
+ *ppOut = buf.aBuf;
+ *pnOut = buf.nBuf;
+ buf.aBuf = 0;
+ }
+ }
+ sqlite3_free(buf.aBuf);
+
+ return rc;
+}
+
+/*
+** Allocate a new, empty, sqlite3_changegroup.
+*/
+SQLITE_API int SQLITE_STDCALL sqlite3changegroup_new(sqlite3_changegroup **pp){
+ int rc = SQLITE_OK; /* Return code */
+ sqlite3_changegroup *p; /* New object */
+ p = (sqlite3_changegroup*)sqlite3_malloc(sizeof(sqlite3_changegroup));
+ if( p==0 ){
+ rc = SQLITE_NOMEM;
+ }else{
+ memset(p, 0, sizeof(sqlite3_changegroup));
+ }
+ *pp = p;
+ return rc;
+}
+
+/*
+** Add the changeset currently stored in buffer pData, size nData bytes,
+** to changeset-group p.
+*/
+SQLITE_API int SQLITE_STDCALL sqlite3changegroup_add(sqlite3_changegroup *pGrp, int nData, void *pData){
+ sqlite3_changeset_iter *pIter; /* Iterator opened on pData/nData */
+ int rc; /* Return code */
+
+ rc = sqlite3changeset_start(&pIter, nData, pData);
+ if( rc==SQLITE_OK ){
+ rc = sessionChangesetToHash(pIter, pGrp);
+ }
+ sqlite3changeset_finalize(pIter);
+ return rc;
+}
+
+/*
+** Obtain a buffer containing a changeset representing the concatenation
+** of all changesets added to the group so far.
+*/
+SQLITE_API int SQLITE_STDCALL sqlite3changegroup_output(
+ sqlite3_changegroup *pGrp,
+ int *pnData,
+ void **ppData
+){
+ return sessionChangegroupOutput(pGrp, 0, 0, pnData, ppData);
+}
+
+/*
+** Streaming versions of changegroup_add().
+*/
+SQLITE_API int SQLITE_STDCALL sqlite3changegroup_add_strm(
+ sqlite3_changegroup *pGrp,
+ int (*xInput)(void *pIn, void *pData, int *pnData),
+ void *pIn
+){
+ sqlite3_changeset_iter *pIter; /* Iterator opened on pData/nData */
+ int rc; /* Return code */
+
+ rc = sqlite3changeset_start_strm(&pIter, xInput, pIn);
+ if( rc==SQLITE_OK ){
+ rc = sessionChangesetToHash(pIter, pGrp);
+ }
+ sqlite3changeset_finalize(pIter);
+ return rc;
+}
+
+/*
+** Streaming versions of changegroup_output().
+*/
+SQLITE_API int SQLITE_STDCALL sqlite3changegroup_output_strm(
+ sqlite3_changegroup *pGrp,
+ int (*xOutput)(void *pOut, const void *pData, int nData),
+ void *pOut
+){
+ return sessionChangegroupOutput(pGrp, xOutput, pOut, 0, 0);
+}
+
+/*
+** Delete a changegroup object.
+*/
+SQLITE_API void SQLITE_STDCALL sqlite3changegroup_delete(sqlite3_changegroup *pGrp){
+ if( pGrp ){
+ sessionDeleteTable(pGrp->pList);
+ sqlite3_free(pGrp);
+ }
+}
+
+/*
+** Combine two changesets together.
+*/
+SQLITE_API int SQLITE_STDCALL sqlite3changeset_concat(
+ int nLeft, /* Number of bytes in lhs input */
+ void *pLeft, /* Lhs input changeset */
+ int nRight /* Number of bytes in rhs input */,
+ void *pRight, /* Rhs input changeset */
+ int *pnOut, /* OUT: Number of bytes in output changeset */
+ void **ppOut /* OUT: changeset (left <concat> right) */
+){
+ sqlite3_changegroup *pGrp;
+ int rc;
+
+ rc = sqlite3changegroup_new(&pGrp);
+ if( rc==SQLITE_OK ){
+ rc = sqlite3changegroup_add(pGrp, nLeft, pLeft);
+ }
+ if( rc==SQLITE_OK ){
+ rc = sqlite3changegroup_add(pGrp, nRight, pRight);
+ }
+ if( rc==SQLITE_OK ){
+ rc = sqlite3changegroup_output(pGrp, pnOut, ppOut);
+ }
+ sqlite3changegroup_delete(pGrp);
+
+ return rc;
+}
+
+/*
+** Streaming version of sqlite3changeset_concat().
+*/
+SQLITE_API int SQLITE_STDCALL sqlite3changeset_concat_strm(
+ int (*xInputA)(void *pIn, void *pData, int *pnData),
+ void *pInA,
+ int (*xInputB)(void *pIn, void *pData, int *pnData),
+ void *pInB,
+ int (*xOutput)(void *pOut, const void *pData, int nData),
+ void *pOut
+){
+ sqlite3_changegroup *pGrp;
+ int rc;
+
+ rc = sqlite3changegroup_new(&pGrp);
+ if( rc==SQLITE_OK ){
+ rc = sqlite3changegroup_add_strm(pGrp, xInputA, pInA);
+ }
+ if( rc==SQLITE_OK ){
+ rc = sqlite3changegroup_add_strm(pGrp, xInputB, pInB);
+ }
+ if( rc==SQLITE_OK ){
+ rc = sqlite3changegroup_output_strm(pGrp, xOutput, pOut);
+ }
+ sqlite3changegroup_delete(pGrp);
+
+ return rc;
+}
+
+#endif /* SQLITE_ENABLE_SESSION && SQLITE_ENABLE_PREUPDATE_HOOK */
+
+/************** End of sqlite3session.c **************************************/
+/************** Begin file json1.c *******************************************/
+/*
+** 2015-08-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 SQLite extension implements JSON functions. The interface is
+** modeled after MySQL JSON functions:
+**
+** https://dev.mysql.com/doc/refman/5.7/en/json.html
+**
+** For the time being, all JSON is stored as pure text. (We might add
+** a JSONB type in the future which stores a binary encoding of JSON in
+** a BLOB, but there is no support for JSONB in the current implementation.
+** This implementation parses JSON text at 250 MB/s, so it is hard to see
+** how JSONB might improve on that.)
+*/
+#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_JSON1)
+#if !defined(_SQLITEINT_H_)
+/* #include "sqlite3ext.h" */
+#endif
+SQLITE_EXTENSION_INIT1
+/* #include <assert.h> */
+/* #include <string.h> */
+/* #include <stdlib.h> */
+/* #include <stdarg.h> */
+
+/* Mark a function parameter as unused, to suppress nuisance compiler
+** warnings. */
+#ifndef UNUSED_PARAM
+# define UNUSED_PARAM(X) (void)(X)
+#endif
+
+#ifndef LARGEST_INT64
+# define LARGEST_INT64 (0xffffffff|(((sqlite3_int64)0x7fffffff)<<32))
+# define SMALLEST_INT64 (((sqlite3_int64)-1) - LARGEST_INT64)
+#endif
+
+/*
+** Versions of isspace(), isalnum() and isdigit() to which it is safe
+** to pass signed char values.
+*/
+#ifdef sqlite3Isdigit
+ /* Use the SQLite core versions if this routine is part of the
+ ** SQLite amalgamation */
+# define safe_isdigit(x) sqlite3Isdigit(x)
+# define safe_isalnum(x) sqlite3Isalnum(x)
+#else
+ /* Use the standard library for separate compilation */
+#include <ctype.h> /* amalgamator: keep */
+# define safe_isdigit(x) isdigit((unsigned char)(x))
+# define safe_isalnum(x) isalnum((unsigned char)(x))
+#endif
+
+/*
+** Growing our own isspace() routine this way is twice as fast as
+** the library isspace() function, resulting in a 7% overall performance
+** increase for the parser. (Ubuntu14.10 gcc 4.8.4 x64 with -Os).
+*/
+static const char jsonIsSpace[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+};
+#define safe_isspace(x) (jsonIsSpace[(unsigned char)x])
+
+#ifndef SQLITE_AMALGAMATION
+ /* Unsigned integer types. These are already defined in the sqliteInt.h,
+ ** but the definitions need to be repeated for separate compilation. */
+ typedef sqlite3_uint64 u64;
+ typedef unsigned int u32;
+ typedef unsigned char u8;
+#endif
+
+/* Objects */
+typedef struct JsonString JsonString;
+typedef struct JsonNode JsonNode;
+typedef struct JsonParse JsonParse;
+
+/* An instance of this object represents a JSON string
+** under construction. Really, this is a generic string accumulator
+** that can be and is used to create strings other than JSON.
+*/
+struct JsonString {
+ sqlite3_context *pCtx; /* Function context - put error messages here */
+ char *zBuf; /* Append JSON content here */
+ u64 nAlloc; /* Bytes of storage available in zBuf[] */
+ u64 nUsed; /* Bytes of zBuf[] currently used */
+ u8 bStatic; /* True if zBuf is static space */
+ u8 bErr; /* True if an error has been encountered */
+ char zSpace[100]; /* Initial static space */
+};
+
+/* JSON type values
+*/
+#define JSON_NULL 0
+#define JSON_TRUE 1
+#define JSON_FALSE 2
+#define JSON_INT 3
+#define JSON_REAL 4
+#define JSON_STRING 5
+#define JSON_ARRAY 6
+#define JSON_OBJECT 7
+
+/* The "subtype" set for JSON values */
+#define JSON_SUBTYPE 74 /* Ascii for "J" */
+
+/*
+** Names of the various JSON types:
+*/
+static const char * const jsonType[] = {
+ "null", "true", "false", "integer", "real", "text", "array", "object"
+};
+
+/* Bit values for the JsonNode.jnFlag field
+*/
+#define JNODE_RAW 0x01 /* Content is raw, not JSON encoded */
+#define JNODE_ESCAPE 0x02 /* Content is text with \ escapes */
+#define JNODE_REMOVE 0x04 /* Do not output */
+#define JNODE_REPLACE 0x08 /* Replace with JsonNode.iVal */
+#define JNODE_APPEND 0x10 /* More ARRAY/OBJECT entries at u.iAppend */
+#define JNODE_LABEL 0x20 /* Is a label of an object */
+
+
+/* A single node of parsed JSON
+*/
+struct JsonNode {
+ u8 eType; /* One of the JSON_ type values */
+ u8 jnFlags; /* JNODE flags */
+ u8 iVal; /* Replacement value when JNODE_REPLACE */
+ u32 n; /* Bytes of content, or number of sub-nodes */
+ union {
+ const char *zJContent; /* Content for INT, REAL, and STRING */
+ u32 iAppend; /* More terms for ARRAY and OBJECT */
+ u32 iKey; /* Key for ARRAY objects in json_tree() */
+ } u;
+};
+
+/* A completely parsed JSON string
+*/
+struct JsonParse {
+ u32 nNode; /* Number of slots of aNode[] used */
+ u32 nAlloc; /* Number of slots of aNode[] allocated */
+ JsonNode *aNode; /* Array of nodes containing the parse */
+ const char *zJson; /* Original JSON string */
+ u32 *aUp; /* Index of parent of each node */
+ u8 oom; /* Set to true if out of memory */
+ u8 nErr; /* Number of errors seen */
+};
+
+/**************************************************************************
+** Utility routines for dealing with JsonString objects
+**************************************************************************/
+
+/* Set the JsonString object to an empty string
+*/
+static void jsonZero(JsonString *p){
+ p->zBuf = p->zSpace;
+ p->nAlloc = sizeof(p->zSpace);
+ p->nUsed = 0;
+ p->bStatic = 1;
+}
+
+/* Initialize the JsonString object
+*/
+static void jsonInit(JsonString *p, sqlite3_context *pCtx){
+ p->pCtx = pCtx;
+ p->bErr = 0;
+ jsonZero(p);
+}
+
+
+/* Free all allocated memory and reset the JsonString object back to its
+** initial state.
+*/
+static void jsonReset(JsonString *p){
+ if( !p->bStatic ) sqlite3_free(p->zBuf);
+ jsonZero(p);
+}
+
+
+/* Report an out-of-memory (OOM) condition
+*/
+static void jsonOom(JsonString *p){
+ p->bErr = 1;
+ sqlite3_result_error_nomem(p->pCtx);
+ jsonReset(p);
+}
+
+/* Enlarge pJson->zBuf so that it can hold at least N more bytes.
+** Return zero on success. Return non-zero on an OOM error
+*/
+static int jsonGrow(JsonString *p, u32 N){
+ u64 nTotal = N<p->nAlloc ? p->nAlloc*2 : p->nAlloc+N+10;
+ char *zNew;
+ if( p->bStatic ){
+ if( p->bErr ) return 1;
+ zNew = sqlite3_malloc64(nTotal);
+ if( zNew==0 ){
+ jsonOom(p);
+ return SQLITE_NOMEM;
+ }
+ memcpy(zNew, p->zBuf, (size_t)p->nUsed);
+ p->zBuf = zNew;
+ p->bStatic = 0;
+ }else{
+ zNew = sqlite3_realloc64(p->zBuf, nTotal);
+ if( zNew==0 ){
+ jsonOom(p);
+ return SQLITE_NOMEM;
+ }
+ p->zBuf = zNew;
+ }
+ p->nAlloc = nTotal;
+ return SQLITE_OK;
+}
+
+/* Append N bytes from zIn onto the end of the JsonString string.
+*/
+static void jsonAppendRaw(JsonString *p, const char *zIn, u32 N){
+ if( (N+p->nUsed >= p->nAlloc) && jsonGrow(p,N)!=0 ) return;
+ memcpy(p->zBuf+p->nUsed, zIn, N);
+ p->nUsed += N;
+}
+
+/* Append formatted text (not to exceed N bytes) to the JsonString.
+*/
+static void jsonPrintf(int N, JsonString *p, const char *zFormat, ...){
+ va_list ap;
+ if( (p->nUsed + N >= p->nAlloc) && jsonGrow(p, N) ) return;
+ va_start(ap, zFormat);
+ sqlite3_vsnprintf(N, p->zBuf+p->nUsed, zFormat, ap);
+ va_end(ap);
+ p->nUsed += (int)strlen(p->zBuf+p->nUsed);
+}
+
+/* Append a single character
+*/
+static void jsonAppendChar(JsonString *p, char c){
+ if( p->nUsed>=p->nAlloc && jsonGrow(p,1)!=0 ) return;
+ p->zBuf[p->nUsed++] = c;
+}
+
+/* Append a comma separator to the output buffer, if the previous
+** character is not '[' or '{'.
+*/
+static void jsonAppendSeparator(JsonString *p){
+ char c;
+ if( p->nUsed==0 ) return;
+ c = p->zBuf[p->nUsed-1];
+ if( c!='[' && c!='{' ) jsonAppendChar(p, ',');
+}
+
+/* Append the N-byte string in zIn to the end of the JsonString string
+** under construction. Enclose the string in "..." and escape
+** any double-quotes or backslash characters contained within the
+** string.
+*/
+static void jsonAppendString(JsonString *p, const char *zIn, u32 N){
+ u32 i;
+ if( (N+p->nUsed+2 >= p->nAlloc) && jsonGrow(p,N+2)!=0 ) return;
+ p->zBuf[p->nUsed++] = '"';
+ for(i=0; i<N; i++){
+ unsigned char c = ((unsigned const char*)zIn)[i];
+ if( c=='"' || c=='\\' ){
+ json_simple_escape:
+ if( (p->nUsed+N+3-i > p->nAlloc) && jsonGrow(p,N+3-i)!=0 ) return;
+ p->zBuf[p->nUsed++] = '\\';
+ }else if( c<=0x1f ){
+ static const char aSpecial[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 'b', 't', 'n', 0, 'f', 'r', 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ };
+ assert( sizeof(aSpecial)==32 );
+ assert( aSpecial['\b']=='b' );
+ assert( aSpecial['\f']=='f' );
+ assert( aSpecial['\n']=='n' );
+ assert( aSpecial['\r']=='r' );
+ assert( aSpecial['\t']=='t' );
+ if( aSpecial[c] ){
+ c = aSpecial[c];
+ goto json_simple_escape;
+ }
+ if( (p->nUsed+N+7+i > p->nAlloc) && jsonGrow(p,N+7-i)!=0 ) return;
+ p->zBuf[p->nUsed++] = '\\';
+ p->zBuf[p->nUsed++] = 'u';
+ p->zBuf[p->nUsed++] = '0';
+ p->zBuf[p->nUsed++] = '0';
+ p->zBuf[p->nUsed++] = '0' + (c>>4);
+ c = "0123456789abcdef"[c&0xf];
+ }
+ p->zBuf[p->nUsed++] = c;
+ }
+ p->zBuf[p->nUsed++] = '"';
+ assert( p->nUsed<p->nAlloc );
+}
+
+/*
+** Append a function parameter value to the JSON string under
+** construction.
+*/
+static void jsonAppendValue(
+ JsonString *p, /* Append to this JSON string */
+ sqlite3_value *pValue /* Value to append */
+){
+ switch( sqlite3_value_type(pValue) ){
+ case SQLITE_NULL: {
+ jsonAppendRaw(p, "null", 4);
+ break;
+ }
+ case SQLITE_INTEGER:
+ case SQLITE_FLOAT: {
+ const char *z = (const char*)sqlite3_value_text(pValue);
+ u32 n = (u32)sqlite3_value_bytes(pValue);
+ jsonAppendRaw(p, z, n);
+ break;
+ }
+ case SQLITE_TEXT: {
+ const char *z = (const char*)sqlite3_value_text(pValue);
+ u32 n = (u32)sqlite3_value_bytes(pValue);
+ if( sqlite3_value_subtype(pValue)==JSON_SUBTYPE ){
+ jsonAppendRaw(p, z, n);
+ }else{
+ jsonAppendString(p, z, n);
+ }
+ break;
+ }
+ default: {
+ if( p->bErr==0 ){
+ sqlite3_result_error(p->pCtx, "JSON cannot hold BLOB values", -1);
+ p->bErr = 2;
+ jsonReset(p);
+ }
+ break;
+ }
+ }
+}
+
+
+/* Make the JSON in p the result of the SQL function.
+*/
+static void jsonResult(JsonString *p){
+ if( p->bErr==0 ){
+ sqlite3_result_text64(p->pCtx, p->zBuf, p->nUsed,
+ p->bStatic ? SQLITE_TRANSIENT : sqlite3_free,
+ SQLITE_UTF8);
+ jsonZero(p);
+ }
+ assert( p->bStatic );
+}
+
+/**************************************************************************
+** Utility routines for dealing with JsonNode and JsonParse objects
+**************************************************************************/
+
+/*
+** Return the number of consecutive JsonNode slots need to represent
+** the parsed JSON at pNode. The minimum answer is 1. For ARRAY and
+** OBJECT types, the number might be larger.
+**
+** Appended elements are not counted. The value returned is the number
+** by which the JsonNode counter should increment in order to go to the
+** next peer value.
+*/
+static u32 jsonNodeSize(JsonNode *pNode){
+ return pNode->eType>=JSON_ARRAY ? pNode->n+1 : 1;
+}
+
+/*
+** Reclaim all memory allocated by a JsonParse object. But do not
+** delete the JsonParse object itself.
+*/
+static void jsonParseReset(JsonParse *pParse){
+ sqlite3_free(pParse->aNode);
+ pParse->aNode = 0;
+ pParse->nNode = 0;
+ pParse->nAlloc = 0;
+ sqlite3_free(pParse->aUp);
+ pParse->aUp = 0;
+}
+
+/*
+** Convert the JsonNode pNode into a pure JSON string and
+** append to pOut. Subsubstructure is also included. Return
+** the number of JsonNode objects that are encoded.
+*/
+static void jsonRenderNode(
+ JsonNode *pNode, /* The node to render */
+ JsonString *pOut, /* Write JSON here */
+ sqlite3_value **aReplace /* Replacement values */
+){
+ switch( pNode->eType ){
+ default: {
+ assert( pNode->eType==JSON_NULL );
+ jsonAppendRaw(pOut, "null", 4);
+ break;
+ }
+ case JSON_TRUE: {
+ jsonAppendRaw(pOut, "true", 4);
+ break;
+ }
+ case JSON_FALSE: {
+ jsonAppendRaw(pOut, "false", 5);
+ break;
+ }
+ case JSON_STRING: {
+ if( pNode->jnFlags & JNODE_RAW ){
+ jsonAppendString(pOut, pNode->u.zJContent, pNode->n);
+ break;
+ }
+ /* Fall through into the next case */
+ }
+ case JSON_REAL:
+ case JSON_INT: {
+ jsonAppendRaw(pOut, pNode->u.zJContent, pNode->n);
+ break;
+ }
+ case JSON_ARRAY: {
+ u32 j = 1;
+ jsonAppendChar(pOut, '[');
+ for(;;){
+ while( j<=pNode->n ){
+ if( pNode[j].jnFlags & (JNODE_REMOVE|JNODE_REPLACE) ){
+ if( pNode[j].jnFlags & JNODE_REPLACE ){
+ jsonAppendSeparator(pOut);
+ jsonAppendValue(pOut, aReplace[pNode[j].iVal]);
+ }
+ }else{
+ jsonAppendSeparator(pOut);
+ jsonRenderNode(&pNode[j], pOut, aReplace);
+ }
+ j += jsonNodeSize(&pNode[j]);
+ }
+ if( (pNode->jnFlags & JNODE_APPEND)==0 ) break;
+ pNode = &pNode[pNode->u.iAppend];
+ j = 1;
+ }
+ jsonAppendChar(pOut, ']');
+ break;
+ }
+ case JSON_OBJECT: {
+ u32 j = 1;
+ jsonAppendChar(pOut, '{');
+ for(;;){
+ while( j<=pNode->n ){
+ if( (pNode[j+1].jnFlags & JNODE_REMOVE)==0 ){
+ jsonAppendSeparator(pOut);
+ jsonRenderNode(&pNode[j], pOut, aReplace);
+ jsonAppendChar(pOut, ':');
+ if( pNode[j+1].jnFlags & JNODE_REPLACE ){
+ jsonAppendValue(pOut, aReplace[pNode[j+1].iVal]);
+ }else{
+ jsonRenderNode(&pNode[j+1], pOut, aReplace);
+ }
+ }
+ j += 1 + jsonNodeSize(&pNode[j+1]);
+ }
+ if( (pNode->jnFlags & JNODE_APPEND)==0 ) break;
+ pNode = &pNode[pNode->u.iAppend];
+ j = 1;
+ }
+ jsonAppendChar(pOut, '}');
+ break;
+ }
+ }
+}
+
+/*
+** Return a JsonNode and all its descendents as a JSON string.
+*/
+static void jsonReturnJson(
+ JsonNode *pNode, /* Node to return */
+ sqlite3_context *pCtx, /* Return value for this function */
+ sqlite3_value **aReplace /* Array of replacement values */
+){
+ JsonString s;
+ jsonInit(&s, pCtx);
+ jsonRenderNode(pNode, &s, aReplace);
+ jsonResult(&s);
+ sqlite3_result_subtype(pCtx, JSON_SUBTYPE);
+}
+
+/*
+** Make the JsonNode the return value of the function.
+*/
+static void jsonReturn(
+ JsonNode *pNode, /* Node to return */
+ sqlite3_context *pCtx, /* Return value for this function */
+ sqlite3_value **aReplace /* Array of replacement values */
+){
+ switch( pNode->eType ){
+ default: {
+ assert( pNode->eType==JSON_NULL );
+ sqlite3_result_null(pCtx);
+ break;
+ }
+ case JSON_TRUE: {
+ sqlite3_result_int(pCtx, 1);
+ break;
+ }
+ case JSON_FALSE: {
+ sqlite3_result_int(pCtx, 0);
+ break;
+ }
+ case JSON_INT: {
+ sqlite3_int64 i = 0;
+ const char *z = pNode->u.zJContent;
+ if( z[0]=='-' ){ z++; }
+ while( z[0]>='0' && z[0]<='9' ){
+ unsigned v = *(z++) - '0';
+ if( i>=LARGEST_INT64/10 ){
+ if( i>LARGEST_INT64/10 ) goto int_as_real;
+ if( z[0]>='0' && z[0]<='9' ) goto int_as_real;
+ if( v==9 ) goto int_as_real;
+ if( v==8 ){
+ if( pNode->u.zJContent[0]=='-' ){
+ sqlite3_result_int64(pCtx, SMALLEST_INT64);
+ goto int_done;
+ }else{
+ goto int_as_real;
+ }
+ }
+ }
+ i = i*10 + v;
+ }
+ if( pNode->u.zJContent[0]=='-' ){ i = -i; }
+ sqlite3_result_int64(pCtx, i);
+ int_done:
+ break;
+ int_as_real: /* fall through to real */;
+ }
+ case JSON_REAL: {
+ double r;
+#ifdef SQLITE_AMALGAMATION
+ const char *z = pNode->u.zJContent;
+ sqlite3AtoF(z, &r, sqlite3Strlen30(z), SQLITE_UTF8);
+#else
+ r = strtod(pNode->u.zJContent, 0);
+#endif
+ sqlite3_result_double(pCtx, r);
+ break;
+ }
+ case JSON_STRING: {
+#if 0 /* Never happens because JNODE_RAW is only set by json_set(),
+ ** json_insert() and json_replace() and those routines do not
+ ** call jsonReturn() */
+ if( pNode->jnFlags & JNODE_RAW ){
+ sqlite3_result_text(pCtx, pNode->u.zJContent, pNode->n,
+ SQLITE_TRANSIENT);
+ }else
+#endif
+ assert( (pNode->jnFlags & JNODE_RAW)==0 );
+ if( (pNode->jnFlags & JNODE_ESCAPE)==0 ){
+ /* JSON formatted without any backslash-escapes */
+ sqlite3_result_text(pCtx, pNode->u.zJContent+1, pNode->n-2,
+ SQLITE_TRANSIENT);
+ }else{
+ /* Translate JSON formatted string into raw text */
+ u32 i;
+ u32 n = pNode->n;
+ const char *z = pNode->u.zJContent;
+ char *zOut;
+ u32 j;
+ zOut = sqlite3_malloc( n+1 );
+ if( zOut==0 ){
+ sqlite3_result_error_nomem(pCtx);
+ break;
+ }
+ for(i=1, j=0; i<n-1; i++){
+ char c = z[i];
+ if( c!='\\' ){
+ zOut[j++] = c;
+ }else{
+ c = z[++i];
+ if( c=='u' ){
+ u32 v = 0, k;
+ for(k=0; k<4 && i<n-2; i++, k++){
+ c = z[i+1];
+ if( c>='0' && c<='9' ) v = v*16 + c - '0';
+ else if( c>='A' && c<='F' ) v = v*16 + c - 'A' + 10;
+ else if( c>='a' && c<='f' ) v = v*16 + c - 'a' + 10;
+ else break;
+ }
+ if( v==0 ) break;
+ if( v<=0x7f ){
+ zOut[j++] = (char)v;
+ }else if( v<=0x7ff ){
+ zOut[j++] = (char)(0xc0 | (v>>6));
+ zOut[j++] = 0x80 | (v&0x3f);
+ }else{
+ zOut[j++] = (char)(0xe0 | (v>>12));
+ zOut[j++] = 0x80 | ((v>>6)&0x3f);
+ zOut[j++] = 0x80 | (v&0x3f);
+ }
+ }else{
+ if( c=='b' ){
+ c = '\b';
+ }else if( c=='f' ){
+ c = '\f';
+ }else if( c=='n' ){
+ c = '\n';
+ }else if( c=='r' ){
+ c = '\r';
+ }else if( c=='t' ){
+ c = '\t';
+ }
+ zOut[j++] = c;
+ }
+ }
+ }
+ zOut[j] = 0;
+ sqlite3_result_text(pCtx, zOut, j, sqlite3_free);
+ }
+ break;
+ }
+ case JSON_ARRAY:
+ case JSON_OBJECT: {
+ jsonReturnJson(pNode, pCtx, aReplace);
+ break;
+ }
+ }
+}
+
+/* Forward reference */
+static int jsonParseAddNode(JsonParse*,u32,u32,const char*);
+
+/*
+** A macro to hint to the compiler that a function should not be
+** inlined.
+*/
+#if defined(__GNUC__)
+# define JSON_NOINLINE __attribute__((noinline))
+#elif defined(_MSC_VER) && _MSC_VER>=1310
+# define JSON_NOINLINE __declspec(noinline)
+#else
+# define JSON_NOINLINE
+#endif
+
+
+static JSON_NOINLINE int jsonParseAddNodeExpand(
+ JsonParse *pParse, /* Append the node to this object */
+ u32 eType, /* Node type */
+ u32 n, /* Content size or sub-node count */
+ const char *zContent /* Content */
+){
+ u32 nNew;
+ JsonNode *pNew;
+ assert( pParse->nNode>=pParse->nAlloc );
+ if( pParse->oom ) return -1;
+ nNew = pParse->nAlloc*2 + 10;
+ pNew = sqlite3_realloc(pParse->aNode, sizeof(JsonNode)*nNew);
+ if( pNew==0 ){
+ pParse->oom = 1;
+ return -1;
+ }
+ pParse->nAlloc = nNew;
+ pParse->aNode = pNew;
+ assert( pParse->nNode<pParse->nAlloc );
+ return jsonParseAddNode(pParse, eType, n, zContent);
+}
+
+/*
+** Create a new JsonNode instance based on the arguments and append that
+** instance to the JsonParse. Return the index in pParse->aNode[] of the
+** new node, or -1 if a memory allocation fails.
+*/
+static int jsonParseAddNode(
+ JsonParse *pParse, /* Append the node to this object */
+ u32 eType, /* Node type */
+ u32 n, /* Content size or sub-node count */
+ const char *zContent /* Content */
+){
+ JsonNode *p;
+ if( pParse->nNode>=pParse->nAlloc ){
+ return jsonParseAddNodeExpand(pParse, eType, n, zContent);
+ }
+ p = &pParse->aNode[pParse->nNode];
+ p->eType = (u8)eType;
+ p->jnFlags = 0;
+ p->iVal = 0;
+ p->n = n;
+ p->u.zJContent = zContent;
+ return pParse->nNode++;
+}
+
+/*
+** Parse a single JSON value which begins at pParse->zJson[i]. Return the
+** index of the first character past the end of the value parsed.
+**
+** Return negative for a syntax error. Special cases: return -2 if the
+** first non-whitespace character is '}' and return -3 if the first
+** non-whitespace character is ']'.
+*/
+static int jsonParseValue(JsonParse *pParse, u32 i){
+ char c;
+ u32 j;
+ int iThis;
+ int x;
+ JsonNode *pNode;
+ while( safe_isspace(pParse->zJson[i]) ){ i++; }
+ if( (c = pParse->zJson[i])=='{' ){
+ /* Parse object */
+ iThis = jsonParseAddNode(pParse, JSON_OBJECT, 0, 0);
+ if( iThis<0 ) return -1;
+ for(j=i+1;;j++){
+ while( safe_isspace(pParse->zJson[j]) ){ j++; }
+ x = jsonParseValue(pParse, j);
+ if( x<0 ){
+ if( x==(-2) && pParse->nNode==(u32)iThis+1 ) return j+1;
+ return -1;
+ }
+ if( pParse->oom ) return -1;
+ pNode = &pParse->aNode[pParse->nNode-1];
+ if( pNode->eType!=JSON_STRING ) return -1;
+ pNode->jnFlags |= JNODE_LABEL;
+ j = x;
+ while( safe_isspace(pParse->zJson[j]) ){ j++; }
+ if( pParse->zJson[j]!=':' ) return -1;
+ j++;
+ x = jsonParseValue(pParse, j);
+ if( x<0 ) return -1;
+ j = x;
+ while( safe_isspace(pParse->zJson[j]) ){ j++; }
+ c = pParse->zJson[j];
+ if( c==',' ) continue;
+ if( c!='}' ) return -1;
+ break;
+ }
+ pParse->aNode[iThis].n = pParse->nNode - (u32)iThis - 1;
+ return j+1;
+ }else if( c=='[' ){
+ /* Parse array */
+ iThis = jsonParseAddNode(pParse, JSON_ARRAY, 0, 0);
+ if( iThis<0 ) return -1;
+ for(j=i+1;;j++){
+ while( safe_isspace(pParse->zJson[j]) ){ j++; }
+ x = jsonParseValue(pParse, j);
+ if( x<0 ){
+ if( x==(-3) && pParse->nNode==(u32)iThis+1 ) return j+1;
+ return -1;
+ }
+ j = x;
+ while( safe_isspace(pParse->zJson[j]) ){ j++; }
+ c = pParse->zJson[j];
+ if( c==',' ) continue;
+ if( c!=']' ) return -1;
+ break;
+ }
+ pParse->aNode[iThis].n = pParse->nNode - (u32)iThis - 1;
+ return j+1;
+ }else if( c=='"' ){
+ /* Parse string */
+ u8 jnFlags = 0;
+ j = i+1;
+ for(;;){
+ c = pParse->zJson[j];
+ if( c==0 ) return -1;
+ if( c=='\\' ){
+ c = pParse->zJson[++j];
+ if( c==0 ) return -1;
+ jnFlags = JNODE_ESCAPE;
+ }else if( c=='"' ){
+ break;
+ }
+ j++;
+ }
+ jsonParseAddNode(pParse, JSON_STRING, j+1-i, &pParse->zJson[i]);
+ if( !pParse->oom ) pParse->aNode[pParse->nNode-1].jnFlags = jnFlags;
+ return j+1;
+ }else if( c=='n'
+ && strncmp(pParse->zJson+i,"null",4)==0
+ && !safe_isalnum(pParse->zJson[i+4]) ){
+ jsonParseAddNode(pParse, JSON_NULL, 0, 0);
+ return i+4;
+ }else if( c=='t'
+ && strncmp(pParse->zJson+i,"true",4)==0
+ && !safe_isalnum(pParse->zJson[i+4]) ){
+ jsonParseAddNode(pParse, JSON_TRUE, 0, 0);
+ return i+4;
+ }else if( c=='f'
+ && strncmp(pParse->zJson+i,"false",5)==0
+ && !safe_isalnum(pParse->zJson[i+5]) ){
+ jsonParseAddNode(pParse, JSON_FALSE, 0, 0);
+ return i+5;
+ }else if( c=='-' || (c>='0' && c<='9') ){
+ /* Parse number */
+ u8 seenDP = 0;
+ u8 seenE = 0;
+ j = i+1;
+ for(;; j++){
+ c = pParse->zJson[j];
+ if( c>='0' && c<='9' ) continue;
+ if( c=='.' ){
+ if( pParse->zJson[j-1]=='-' ) return -1;
+ if( seenDP ) return -1;
+ seenDP = 1;
+ continue;
+ }
+ if( c=='e' || c=='E' ){
+ if( pParse->zJson[j-1]<'0' ) return -1;
+ if( seenE ) return -1;
+ seenDP = seenE = 1;
+ c = pParse->zJson[j+1];
+ if( c=='+' || c=='-' ){
+ j++;
+ c = pParse->zJson[j+1];
+ }
+ if( c<'0' || c>'9' ) return -1;
+ continue;
+ }
+ break;
+ }
+ if( pParse->zJson[j-1]<'0' ) return -1;
+ jsonParseAddNode(pParse, seenDP ? JSON_REAL : JSON_INT,
+ j - i, &pParse->zJson[i]);
+ return j;
+ }else if( c=='}' ){
+ return -2; /* End of {...} */
+ }else if( c==']' ){
+ return -3; /* End of [...] */
+ }else if( c==0 ){
+ return 0; /* End of file */
+ }else{
+ return -1; /* Syntax error */
+ }
+}
+
+/*
+** Parse a complete JSON string. Return 0 on success or non-zero if there
+** are any errors. If an error occurs, free all memory associated with
+** pParse.
+**
+** pParse is uninitialized when this routine is called.
+*/
+static int jsonParse(
+ JsonParse *pParse, /* Initialize and fill this JsonParse object */
+ sqlite3_context *pCtx, /* Report errors here */
+ const char *zJson /* Input JSON text to be parsed */
+){
+ int i;
+ memset(pParse, 0, sizeof(*pParse));
+ if( zJson==0 ) return 1;
+ pParse->zJson = zJson;
+ i = jsonParseValue(pParse, 0);
+ if( pParse->oom ) i = -1;
+ if( i>0 ){
+ while( safe_isspace(zJson[i]) ) i++;
+ if( zJson[i] ) i = -1;
+ }
+ if( i<=0 ){
+ if( pCtx!=0 ){
+ if( pParse->oom ){
+ sqlite3_result_error_nomem(pCtx);
+ }else{
+ sqlite3_result_error(pCtx, "malformed JSON", -1);
+ }
+ }
+ jsonParseReset(pParse);
+ return 1;
+ }
+ return 0;
+}
+
+/* Mark node i of pParse as being a child of iParent. Call recursively
+** to fill in all the descendants of node i.
+*/
+static void jsonParseFillInParentage(JsonParse *pParse, u32 i, u32 iParent){
+ JsonNode *pNode = &pParse->aNode[i];
+ u32 j;
+ pParse->aUp[i] = iParent;
+ switch( pNode->eType ){
+ case JSON_ARRAY: {
+ for(j=1; j<=pNode->n; j += jsonNodeSize(pNode+j)){
+ jsonParseFillInParentage(pParse, i+j, i);
+ }
+ break;
+ }
+ case JSON_OBJECT: {
+ for(j=1; j<=pNode->n; j += jsonNodeSize(pNode+j+1)+1){
+ pParse->aUp[i+j] = i;
+ jsonParseFillInParentage(pParse, i+j+1, i);
+ }
+ break;
+ }
+ default: {
+ break;
+ }
+ }
+}
+
+/*
+** Compute the parentage of all nodes in a completed parse.
+*/
+static int jsonParseFindParents(JsonParse *pParse){
+ u32 *aUp;
+ assert( pParse->aUp==0 );
+ aUp = pParse->aUp = sqlite3_malloc( sizeof(u32)*pParse->nNode );
+ if( aUp==0 ){
+ pParse->oom = 1;
+ return SQLITE_NOMEM;
+ }
+ jsonParseFillInParentage(pParse, 0, 0);
+ return SQLITE_OK;
+}
+
+/*
+** Compare the OBJECT label at pNode against zKey,nKey. Return true on
+** a match.
+*/
+static int jsonLabelCompare(JsonNode *pNode, const char *zKey, u32 nKey){
+ if( pNode->jnFlags & JNODE_RAW ){
+ if( pNode->n!=nKey ) return 0;
+ return strncmp(pNode->u.zJContent, zKey, nKey)==0;
+ }else{
+ if( pNode->n!=nKey+2 ) return 0;
+ return strncmp(pNode->u.zJContent+1, zKey, nKey)==0;
+ }
+}
+
+/* forward declaration */
+static JsonNode *jsonLookupAppend(JsonParse*,const char*,int*,const char**);
+
+/*
+** Search along zPath to find the node specified. Return a pointer
+** to that node, or NULL if zPath is malformed or if there is no such
+** node.
+**
+** If pApnd!=0, then try to append new nodes to complete zPath if it is
+** possible to do so and if no existing node corresponds to zPath. If
+** new nodes are appended *pApnd is set to 1.
+*/
+static JsonNode *jsonLookupStep(
+ JsonParse *pParse, /* The JSON to search */
+ u32 iRoot, /* Begin the search at this node */
+ const char *zPath, /* The path to search */
+ int *pApnd, /* Append nodes to complete path if not NULL */
+ const char **pzErr /* Make *pzErr point to any syntax error in zPath */
+){
+ u32 i, j, nKey;
+ const char *zKey;
+ JsonNode *pRoot = &pParse->aNode[iRoot];
+ if( zPath[0]==0 ) return pRoot;
+ if( zPath[0]=='.' ){
+ if( pRoot->eType!=JSON_OBJECT ) return 0;
+ zPath++;
+ if( zPath[0]=='"' ){
+ zKey = zPath + 1;
+ for(i=1; zPath[i] && zPath[i]!='"'; i++){}
+ nKey = i-1;
+ if( zPath[i] ){
+ i++;
+ }else{
+ *pzErr = zPath;
+ return 0;
+ }
+ }else{
+ zKey = zPath;
+ for(i=0; zPath[i] && zPath[i]!='.' && zPath[i]!='['; i++){}
+ nKey = i;
+ }
+ if( nKey==0 ){
+ *pzErr = zPath;
+ return 0;
+ }
+ j = 1;
+ for(;;){
+ while( j<=pRoot->n ){
+ if( jsonLabelCompare(pRoot+j, zKey, nKey) ){
+ return jsonLookupStep(pParse, iRoot+j+1, &zPath[i], pApnd, pzErr);
+ }
+ j++;
+ j += jsonNodeSize(&pRoot[j]);
+ }
+ if( (pRoot->jnFlags & JNODE_APPEND)==0 ) break;
+ iRoot += pRoot->u.iAppend;
+ pRoot = &pParse->aNode[iRoot];
+ j = 1;
+ }
+ if( pApnd ){
+ u32 iStart, iLabel;
+ JsonNode *pNode;
+ iStart = jsonParseAddNode(pParse, JSON_OBJECT, 2, 0);
+ iLabel = jsonParseAddNode(pParse, JSON_STRING, i, zPath);
+ zPath += i;
+ pNode = jsonLookupAppend(pParse, zPath, pApnd, pzErr);
+ if( pParse->oom ) return 0;
+ if( pNode ){
+ pRoot = &pParse->aNode[iRoot];
+ pRoot->u.iAppend = iStart - iRoot;
+ pRoot->jnFlags |= JNODE_APPEND;
+ pParse->aNode[iLabel].jnFlags |= JNODE_RAW;
+ }
+ return pNode;
+ }
+ }else if( zPath[0]=='[' && safe_isdigit(zPath[1]) ){
+ if( pRoot->eType!=JSON_ARRAY ) return 0;
+ i = 0;
+ j = 1;
+ while( safe_isdigit(zPath[j]) ){
+ i = i*10 + zPath[j] - '0';
+ j++;
+ }
+ if( zPath[j]!=']' ){
+ *pzErr = zPath;
+ return 0;
+ }
+ zPath += j + 1;
+ j = 1;
+ for(;;){
+ while( j<=pRoot->n && (i>0 || (pRoot[j].jnFlags & JNODE_REMOVE)!=0) ){
+ if( (pRoot[j].jnFlags & JNODE_REMOVE)==0 ) i--;
+ j += jsonNodeSize(&pRoot[j]);
+ }
+ if( (pRoot->jnFlags & JNODE_APPEND)==0 ) break;
+ iRoot += pRoot->u.iAppend;
+ pRoot = &pParse->aNode[iRoot];
+ j = 1;
+ }
+ if( j<=pRoot->n ){
+ return jsonLookupStep(pParse, iRoot+j, zPath, pApnd, pzErr);
+ }
+ if( i==0 && pApnd ){
+ u32 iStart;
+ JsonNode *pNode;
+ iStart = jsonParseAddNode(pParse, JSON_ARRAY, 1, 0);
+ pNode = jsonLookupAppend(pParse, zPath, pApnd, pzErr);
+ if( pParse->oom ) return 0;
+ if( pNode ){
+ pRoot = &pParse->aNode[iRoot];
+ pRoot->u.iAppend = iStart - iRoot;
+ pRoot->jnFlags |= JNODE_APPEND;
+ }
+ return pNode;
+ }
+ }else{
+ *pzErr = zPath;
+ }
+ return 0;
+}
+
+/*
+** Append content to pParse that will complete zPath. Return a pointer
+** to the inserted node, or return NULL if the append fails.
+*/
+static JsonNode *jsonLookupAppend(
+ JsonParse *pParse, /* Append content to the JSON parse */
+ const char *zPath, /* Description of content to append */
+ int *pApnd, /* Set this flag to 1 */
+ const char **pzErr /* Make this point to any syntax error */
+){
+ *pApnd = 1;
+ if( zPath[0]==0 ){
+ jsonParseAddNode(pParse, JSON_NULL, 0, 0);
+ return pParse->oom ? 0 : &pParse->aNode[pParse->nNode-1];
+ }
+ if( zPath[0]=='.' ){
+ jsonParseAddNode(pParse, JSON_OBJECT, 0, 0);
+ }else if( strncmp(zPath,"[0]",3)==0 ){
+ jsonParseAddNode(pParse, JSON_ARRAY, 0, 0);
+ }else{
+ return 0;
+ }
+ if( pParse->oom ) return 0;
+ return jsonLookupStep(pParse, pParse->nNode-1, zPath, pApnd, pzErr);
+}
+
+/*
+** Return the text of a syntax error message on a JSON path. Space is
+** obtained from sqlite3_malloc().
+*/
+static char *jsonPathSyntaxError(const char *zErr){
+ return sqlite3_mprintf("JSON path error near '%q'", zErr);
+}
+
+/*
+** Do a node lookup using zPath. Return a pointer to the node on success.
+** Return NULL if not found or if there is an error.
+**
+** On an error, write an error message into pCtx and increment the
+** pParse->nErr counter.
+**
+** If pApnd!=NULL then try to append missing nodes and set *pApnd = 1 if
+** nodes are appended.
+*/
+static JsonNode *jsonLookup(
+ JsonParse *pParse, /* The JSON to search */
+ const char *zPath, /* The path to search */
+ int *pApnd, /* Append nodes to complete path if not NULL */
+ sqlite3_context *pCtx /* Report errors here, if not NULL */
+){
+ const char *zErr = 0;
+ JsonNode *pNode = 0;
+ char *zMsg;
+
+ if( zPath==0 ) return 0;
+ if( zPath[0]!='$' ){
+ zErr = zPath;
+ goto lookup_err;
+ }
+ zPath++;
+ pNode = jsonLookupStep(pParse, 0, zPath, pApnd, &zErr);
+ if( zErr==0 ) return pNode;
+
+lookup_err:
+ pParse->nErr++;
+ assert( zErr!=0 && pCtx!=0 );
+ zMsg = jsonPathSyntaxError(zErr);
+ if( zMsg ){
+ sqlite3_result_error(pCtx, zMsg, -1);
+ sqlite3_free(zMsg);
+ }else{
+ sqlite3_result_error_nomem(pCtx);
+ }
+ return 0;
+}
+
+
+/*
+** Report the wrong number of arguments for json_insert(), json_replace()
+** or json_set().
+*/
+static void jsonWrongNumArgs(
+ sqlite3_context *pCtx,
+ const char *zFuncName
+){
+ char *zMsg = sqlite3_mprintf("json_%s() needs an odd number of arguments",
+ zFuncName);
+ sqlite3_result_error(pCtx, zMsg, -1);
+ sqlite3_free(zMsg);
+}
+
+
+/****************************************************************************
+** SQL functions used for testing and debugging
+****************************************************************************/
+
+#ifdef SQLITE_DEBUG
+/*
+** The json_parse(JSON) function returns a string which describes
+** a parse of the JSON provided. Or it returns NULL if JSON is not
+** well-formed.
+*/
+static void jsonParseFunc(
+ sqlite3_context *ctx,
+ int argc,
+ sqlite3_value **argv
+){
+ JsonString s; /* Output string - not real JSON */
+ JsonParse x; /* The parse */
+ u32 i;
+
+ assert( argc==1 );
+ if( jsonParse(&x, ctx, (const char*)sqlite3_value_text(argv[0])) ) return;
+ jsonParseFindParents(&x);
+ jsonInit(&s, ctx);
+ for(i=0; i<x.nNode; i++){
+ const char *zType;
+ if( x.aNode[i].jnFlags & JNODE_LABEL ){
+ assert( x.aNode[i].eType==JSON_STRING );
+ zType = "label";
+ }else{
+ zType = jsonType[x.aNode[i].eType];
+ }
+ jsonPrintf(100, &s,"node %3u: %7s n=%-4d up=%-4d",
+ i, zType, x.aNode[i].n, x.aUp[i]);
+ if( x.aNode[i].u.zJContent!=0 ){
+ jsonAppendRaw(&s, " ", 1);
+ jsonAppendRaw(&s, x.aNode[i].u.zJContent, x.aNode[i].n);
+ }
+ jsonAppendRaw(&s, "\n", 1);
+ }
+ jsonParseReset(&x);
+ jsonResult(&s);
+}
+
+/*
+** The json_test1(JSON) function return true (1) if the input is JSON
+** text generated by another json function. It returns (0) if the input
+** is not known to be JSON.
+*/
+static void jsonTest1Func(
+ sqlite3_context *ctx,
+ int argc,
+ sqlite3_value **argv
+){
+ UNUSED_PARAM(argc);
+ sqlite3_result_int(ctx, sqlite3_value_subtype(argv[0])==JSON_SUBTYPE);
+}
+#endif /* SQLITE_DEBUG */
+
+/****************************************************************************
+** Scalar SQL function implementations
+****************************************************************************/
+
+/*
+** Implementation of the json_array(VALUE,...) function. Return a JSON
+** array that contains all values given in arguments. Or if any argument
+** is a BLOB, throw an error.
+*/
+static void jsonArrayFunc(
+ sqlite3_context *ctx,
+ int argc,
+ sqlite3_value **argv
+){
+ int i;
+ JsonString jx;
+
+ jsonInit(&jx, ctx);
+ jsonAppendChar(&jx, '[');
+ for(i=0; i<argc; i++){
+ jsonAppendSeparator(&jx);
+ jsonAppendValue(&jx, argv[i]);
+ }
+ jsonAppendChar(&jx, ']');
+ jsonResult(&jx);
+ sqlite3_result_subtype(ctx, JSON_SUBTYPE);
+}
+
+
+/*
+** json_array_length(JSON)
+** json_array_length(JSON, PATH)
+**
+** Return the number of elements in the top-level JSON array.
+** Return 0 if the input is not a well-formed JSON array.
+*/
+static void jsonArrayLengthFunc(
+ sqlite3_context *ctx,
+ int argc,
+ sqlite3_value **argv
+){
+ JsonParse x; /* The parse */
+ sqlite3_int64 n = 0;
+ u32 i;
+ JsonNode *pNode;
+
+ if( jsonParse(&x, ctx, (const char*)sqlite3_value_text(argv[0])) ) return;
+ assert( x.nNode );
+ if( argc==2 ){
+ const char *zPath = (const char*)sqlite3_value_text(argv[1]);
+ pNode = jsonLookup(&x, zPath, 0, ctx);
+ }else{
+ pNode = x.aNode;
+ }
+ if( pNode==0 ){
+ x.nErr = 1;
+ }else if( pNode->eType==JSON_ARRAY ){
+ assert( (pNode->jnFlags & JNODE_APPEND)==0 );
+ for(i=1; i<=pNode->n; n++){
+ i += jsonNodeSize(&pNode[i]);
+ }
+ }
+ if( x.nErr==0 ) sqlite3_result_int64(ctx, n);
+ jsonParseReset(&x);
+}
+
+/*
+** json_extract(JSON, PATH, ...)
+**
+** Return the element described by PATH. Return NULL if there is no
+** PATH element. If there are multiple PATHs, then return a JSON array
+** with the result from each path. Throw an error if the JSON or any PATH
+** is malformed.
+*/
+static void jsonExtractFunc(
+ sqlite3_context *ctx,
+ int argc,
+ sqlite3_value **argv
+){
+ JsonParse x; /* The parse */
+ JsonNode *pNode;
+ const char *zPath;
+ JsonString jx;
+ int i;
+
+ if( argc<2 ) return;
+ if( jsonParse(&x, ctx, (const char*)sqlite3_value_text(argv[0])) ) return;
+ jsonInit(&jx, ctx);
+ jsonAppendChar(&jx, '[');
+ for(i=1; i<argc; i++){
+ zPath = (const char*)sqlite3_value_text(argv[i]);
+ pNode = jsonLookup(&x, zPath, 0, ctx);
+ if( x.nErr ) break;
+ if( argc>2 ){
+ jsonAppendSeparator(&jx);
+ if( pNode ){
+ jsonRenderNode(pNode, &jx, 0);
+ }else{
+ jsonAppendRaw(&jx, "null", 4);
+ }
+ }else if( pNode ){
+ jsonReturn(pNode, ctx, 0);
+ }
+ }
+ if( argc>2 && i==argc ){
+ jsonAppendChar(&jx, ']');
+ jsonResult(&jx);
+ sqlite3_result_subtype(ctx, JSON_SUBTYPE);
+ }
+ jsonReset(&jx);
+ jsonParseReset(&x);
+}
+
+/*
+** Implementation of the json_object(NAME,VALUE,...) function. Return a JSON
+** object that contains all name/value given in arguments. Or if any name
+** is not a string or if any value is a BLOB, throw an error.
+*/
+static void jsonObjectFunc(
+ sqlite3_context *ctx,
+ int argc,
+ sqlite3_value **argv
+){
+ int i;
+ JsonString jx;
+ const char *z;
+ u32 n;
+
+ if( argc&1 ){
+ sqlite3_result_error(ctx, "json_object() requires an even number "
+ "of arguments", -1);
+ return;
+ }
+ jsonInit(&jx, ctx);
+ jsonAppendChar(&jx, '{');
+ for(i=0; i<argc; i+=2){
+ if( sqlite3_value_type(argv[i])!=SQLITE_TEXT ){
+ sqlite3_result_error(ctx, "json_object() labels must be TEXT", -1);
+ jsonReset(&jx);
+ return;
+ }
+ jsonAppendSeparator(&jx);
+ z = (const char*)sqlite3_value_text(argv[i]);
+ n = (u32)sqlite3_value_bytes(argv[i]);
+ jsonAppendString(&jx, z, n);
+ jsonAppendChar(&jx, ':');
+ jsonAppendValue(&jx, argv[i+1]);
+ }
+ jsonAppendChar(&jx, '}');
+ jsonResult(&jx);
+ sqlite3_result_subtype(ctx, JSON_SUBTYPE);
+}
+
+
+/*
+** json_remove(JSON, PATH, ...)
+**
+** Remove the named elements from JSON and return the result. malformed
+** JSON or PATH arguments result in an error.
+*/
+static void jsonRemoveFunc(
+ sqlite3_context *ctx,
+ int argc,
+ sqlite3_value **argv
+){
+ JsonParse x; /* The parse */
+ JsonNode *pNode;
+ const char *zPath;
+ u32 i;
+
+ if( argc<1 ) return;
+ if( jsonParse(&x, ctx, (const char*)sqlite3_value_text(argv[0])) ) return;
+ assert( x.nNode );
+ for(i=1; i<(u32)argc; i++){
+ zPath = (const char*)sqlite3_value_text(argv[i]);
+ if( zPath==0 ) goto remove_done;
+ pNode = jsonLookup(&x, zPath, 0, ctx);
+ if( x.nErr ) goto remove_done;
+ if( pNode ) pNode->jnFlags |= JNODE_REMOVE;
+ }
+ if( (x.aNode[0].jnFlags & JNODE_REMOVE)==0 ){
+ jsonReturnJson(x.aNode, ctx, 0);
+ }
+remove_done:
+ jsonParseReset(&x);
+}
+
+/*
+** json_replace(JSON, PATH, VALUE, ...)
+**
+** Replace the value at PATH with VALUE. If PATH does not already exist,
+** this routine is a no-op. If JSON or PATH is malformed, throw an error.
+*/
+static void jsonReplaceFunc(
+ sqlite3_context *ctx,
+ int argc,
+ sqlite3_value **argv
+){
+ JsonParse x; /* The parse */
+ JsonNode *pNode;
+ const char *zPath;
+ u32 i;
+
+ if( argc<1 ) return;
+ if( (argc&1)==0 ) {
+ jsonWrongNumArgs(ctx, "replace");
+ return;
+ }
+ if( jsonParse(&x, ctx, (const char*)sqlite3_value_text(argv[0])) ) return;
+ assert( x.nNode );
+ for(i=1; i<(u32)argc; i+=2){
+ zPath = (const char*)sqlite3_value_text(argv[i]);
+ pNode = jsonLookup(&x, zPath, 0, ctx);
+ if( x.nErr ) goto replace_err;
+ if( pNode ){
+ pNode->jnFlags |= (u8)JNODE_REPLACE;
+ pNode->iVal = (u8)(i+1);
+ }
+ }
+ if( x.aNode[0].jnFlags & JNODE_REPLACE ){
+ sqlite3_result_value(ctx, argv[x.aNode[0].iVal]);
+ }else{
+ jsonReturnJson(x.aNode, ctx, argv);
+ }
+replace_err:
+ jsonParseReset(&x);
+}
+
+/*
+** json_set(JSON, PATH, VALUE, ...)
+**
+** Set the value at PATH to VALUE. Create the PATH if it does not already
+** exist. Overwrite existing values that do exist.
+** If JSON or PATH is malformed, throw an error.
+**
+** json_insert(JSON, PATH, VALUE, ...)
+**
+** Create PATH and initialize it to VALUE. If PATH already exists, this
+** routine is a no-op. If JSON or PATH is malformed, throw an error.
+*/
+static void jsonSetFunc(
+ sqlite3_context *ctx,
+ int argc,
+ sqlite3_value **argv
+){
+ JsonParse x; /* The parse */
+ JsonNode *pNode;
+ const char *zPath;
+ u32 i;
+ int bApnd;
+ int bIsSet = *(int*)sqlite3_user_data(ctx);
+
+ if( argc<1 ) return;
+ if( (argc&1)==0 ) {
+ jsonWrongNumArgs(ctx, bIsSet ? "set" : "insert");
+ return;
+ }
+ if( jsonParse(&x, ctx, (const char*)sqlite3_value_text(argv[0])) ) return;
+ assert( x.nNode );
+ for(i=1; i<(u32)argc; i+=2){
+ zPath = (const char*)sqlite3_value_text(argv[i]);
+ bApnd = 0;
+ pNode = jsonLookup(&x, zPath, &bApnd, ctx);
+ if( x.oom ){
+ sqlite3_result_error_nomem(ctx);
+ goto jsonSetDone;
+ }else if( x.nErr ){
+ goto jsonSetDone;
+ }else if( pNode && (bApnd || bIsSet) ){
+ pNode->jnFlags |= (u8)JNODE_REPLACE;
+ pNode->iVal = (u8)(i+1);
+ }
+ }
+ if( x.aNode[0].jnFlags & JNODE_REPLACE ){
+ sqlite3_result_value(ctx, argv[x.aNode[0].iVal]);
+ }else{
+ jsonReturnJson(x.aNode, ctx, argv);
+ }
+jsonSetDone:
+ jsonParseReset(&x);
+}
+
+/*
+** json_type(JSON)
+** json_type(JSON, PATH)
+**
+** Return the top-level "type" of a JSON string. Throw an error if
+** either the JSON or PATH inputs are not well-formed.
+*/
+static void jsonTypeFunc(
+ sqlite3_context *ctx,
+ int argc,
+ sqlite3_value **argv
+){
+ JsonParse x; /* The parse */
+ const char *zPath;
+ JsonNode *pNode;
+
+ if( jsonParse(&x, ctx, (const char*)sqlite3_value_text(argv[0])) ) return;
+ assert( x.nNode );
+ if( argc==2 ){
+ zPath = (const char*)sqlite3_value_text(argv[1]);
+ pNode = jsonLookup(&x, zPath, 0, ctx);
+ }else{
+ pNode = x.aNode;
+ }
+ if( pNode ){
+ sqlite3_result_text(ctx, jsonType[pNode->eType], -1, SQLITE_STATIC);
+ }
+ jsonParseReset(&x);
+}
+
+/*
+** json_valid(JSON)
+**
+** Return 1 if JSON is a well-formed JSON string according to RFC-7159.
+** Return 0 otherwise.
+*/
+static void jsonValidFunc(
+ sqlite3_context *ctx,
+ int argc,
+ sqlite3_value **argv
+){
+ JsonParse x; /* The parse */
+ int rc = 0;
+
+ UNUSED_PARAM(argc);
+ if( jsonParse(&x, 0, (const char*)sqlite3_value_text(argv[0]))==0 ){
+ rc = 1;
+ }
+ jsonParseReset(&x);
+ sqlite3_result_int(ctx, rc);
+}
+
+
+/****************************************************************************
+** Aggregate SQL function implementations
+****************************************************************************/
+/*
+** json_group_array(VALUE)
+**
+** Return a JSON array composed of all values in the aggregate.
+*/
+static void jsonArrayStep(
+ sqlite3_context *ctx,
+ int argc,
+ sqlite3_value **argv
+){
+ JsonString *pStr;
+ UNUSED_PARAM(argc);
+ pStr = (JsonString*)sqlite3_aggregate_context(ctx, sizeof(*pStr));
+ if( pStr ){
+ if( pStr->zBuf==0 ){
+ jsonInit(pStr, ctx);
+ jsonAppendChar(pStr, '[');
+ }else{
+ jsonAppendChar(pStr, ',');
+ pStr->pCtx = ctx;
+ }
+ jsonAppendValue(pStr, argv[0]);
+ }
+}
+static void jsonArrayFinal(sqlite3_context *ctx){
+ JsonString *pStr;
+ pStr = (JsonString*)sqlite3_aggregate_context(ctx, 0);
+ if( pStr ){
+ pStr->pCtx = ctx;
+ jsonAppendChar(pStr, ']');
+ if( pStr->bErr ){
+ if( pStr->bErr==1 ) sqlite3_result_error_nomem(ctx);
+ assert( pStr->bStatic );
+ }else{
+ sqlite3_result_text(ctx, pStr->zBuf, pStr->nUsed,
+ pStr->bStatic ? SQLITE_TRANSIENT : sqlite3_free);
+ pStr->bStatic = 1;
+ }
+ }else{
+ sqlite3_result_text(ctx, "[]", 2, SQLITE_STATIC);
+ }
+ sqlite3_result_subtype(ctx, JSON_SUBTYPE);
+}
+
+/*
+** json_group_obj(NAME,VALUE)
+**
+** Return a JSON object composed of all names and values in the aggregate.
+*/
+static void jsonObjectStep(
+ sqlite3_context *ctx,
+ int argc,
+ sqlite3_value **argv
+){
+ JsonString *pStr;
+ const char *z;
+ u32 n;
+ UNUSED_PARAM(argc);
+ pStr = (JsonString*)sqlite3_aggregate_context(ctx, sizeof(*pStr));
+ if( pStr ){
+ if( pStr->zBuf==0 ){
+ jsonInit(pStr, ctx);
+ jsonAppendChar(pStr, '{');
+ }else{
+ jsonAppendChar(pStr, ',');
+ pStr->pCtx = ctx;
+ }
+ z = (const char*)sqlite3_value_text(argv[0]);
+ n = (u32)sqlite3_value_bytes(argv[0]);
+ jsonAppendString(pStr, z, n);
+ jsonAppendChar(pStr, ':');
+ jsonAppendValue(pStr, argv[1]);
+ }
+}
+static void jsonObjectFinal(sqlite3_context *ctx){
+ JsonString *pStr;
+ pStr = (JsonString*)sqlite3_aggregate_context(ctx, 0);
+ if( pStr ){
+ jsonAppendChar(pStr, '}');
+ if( pStr->bErr ){
+ if( pStr->bErr==0 ) sqlite3_result_error_nomem(ctx);
+ assert( pStr->bStatic );
+ }else{
+ sqlite3_result_text(ctx, pStr->zBuf, pStr->nUsed,
+ pStr->bStatic ? SQLITE_TRANSIENT : sqlite3_free);
+ pStr->bStatic = 1;
+ }
+ }else{
+ sqlite3_result_text(ctx, "{}", 2, SQLITE_STATIC);
+ }
+ sqlite3_result_subtype(ctx, JSON_SUBTYPE);
+}
+
+
+#ifndef SQLITE_OMIT_VIRTUALTABLE
+/****************************************************************************
+** The json_each virtual table
+****************************************************************************/
+typedef struct JsonEachCursor JsonEachCursor;
+struct JsonEachCursor {
+ sqlite3_vtab_cursor base; /* Base class - must be first */
+ u32 iRowid; /* The rowid */
+ u32 iBegin; /* The first node of the scan */
+ u32 i; /* Index in sParse.aNode[] of current row */
+ u32 iEnd; /* EOF when i equals or exceeds this value */
+ u8 eType; /* Type of top-level element */
+ u8 bRecursive; /* True for json_tree(). False for json_each() */
+ char *zJson; /* Input JSON */
+ char *zRoot; /* Path by which to filter zJson */
+ JsonParse sParse; /* Parse of the input JSON */
+};
+
+/* Constructor for the json_each virtual table */
+static int jsonEachConnect(
+ sqlite3 *db,
+ void *pAux,
+ int argc, const char *const*argv,
+ sqlite3_vtab **ppVtab,
+ char **pzErr
+){
+ sqlite3_vtab *pNew;
+ int rc;
+
+/* Column numbers */
+#define JEACH_KEY 0
+#define JEACH_VALUE 1
+#define JEACH_TYPE 2
+#define JEACH_ATOM 3
+#define JEACH_ID 4
+#define JEACH_PARENT 5
+#define JEACH_FULLKEY 6
+#define JEACH_PATH 7
+#define JEACH_JSON 8
+#define JEACH_ROOT 9
+
+ UNUSED_PARAM(pzErr);
+ UNUSED_PARAM(argv);
+ UNUSED_PARAM(argc);
+ UNUSED_PARAM(pAux);
+ rc = sqlite3_declare_vtab(db,
+ "CREATE TABLE x(key,value,type,atom,id,parent,fullkey,path,"
+ "json HIDDEN,root HIDDEN)");
+ if( rc==SQLITE_OK ){
+ pNew = *ppVtab = sqlite3_malloc( sizeof(*pNew) );
+ if( pNew==0 ) return SQLITE_NOMEM;
+ memset(pNew, 0, sizeof(*pNew));
+ }
+ return rc;
+}
+
+/* destructor for json_each virtual table */
+static int jsonEachDisconnect(sqlite3_vtab *pVtab){
+ sqlite3_free(pVtab);
+ return SQLITE_OK;
+}
+
+/* constructor for a JsonEachCursor object for json_each(). */
+static int jsonEachOpenEach(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
+ JsonEachCursor *pCur;
+
+ UNUSED_PARAM(p);
+ pCur = sqlite3_malloc( sizeof(*pCur) );
+ if( pCur==0 ) return SQLITE_NOMEM;
+ memset(pCur, 0, sizeof(*pCur));
+ *ppCursor = &pCur->base;
+ return SQLITE_OK;
+}
+
+/* constructor for a JsonEachCursor object for json_tree(). */
+static int jsonEachOpenTree(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
+ int rc = jsonEachOpenEach(p, ppCursor);
+ if( rc==SQLITE_OK ){
+ JsonEachCursor *pCur = (JsonEachCursor*)*ppCursor;
+ pCur->bRecursive = 1;
+ }
+ return rc;
+}
+
+/* Reset a JsonEachCursor back to its original state. Free any memory
+** held. */
+static void jsonEachCursorReset(JsonEachCursor *p){
+ sqlite3_free(p->zJson);
+ sqlite3_free(p->zRoot);
+ jsonParseReset(&p->sParse);
+ p->iRowid = 0;
+ p->i = 0;
+ p->iEnd = 0;
+ p->eType = 0;
+ p->zJson = 0;
+ p->zRoot = 0;
+}
+
+/* Destructor for a jsonEachCursor object */
+static int jsonEachClose(sqlite3_vtab_cursor *cur){
+ JsonEachCursor *p = (JsonEachCursor*)cur;
+ jsonEachCursorReset(p);
+ sqlite3_free(cur);
+ return SQLITE_OK;
+}
+
+/* Return TRUE if the jsonEachCursor object has been advanced off the end
+** of the JSON object */
+static int jsonEachEof(sqlite3_vtab_cursor *cur){
+ JsonEachCursor *p = (JsonEachCursor*)cur;
+ return p->i >= p->iEnd;
+}
+
+/* Advance the cursor to the next element for json_tree() */
+static int jsonEachNext(sqlite3_vtab_cursor *cur){
+ JsonEachCursor *p = (JsonEachCursor*)cur;
+ if( p->bRecursive ){
+ if( p->sParse.aNode[p->i].jnFlags & JNODE_LABEL ) p->i++;
+ p->i++;
+ p->iRowid++;
+ if( p->i<p->iEnd ){
+ u32 iUp = p->sParse.aUp[p->i];
+ JsonNode *pUp = &p->sParse.aNode[iUp];
+ p->eType = pUp->eType;
+ if( pUp->eType==JSON_ARRAY ){
+ if( iUp==p->i-1 ){
+ pUp->u.iKey = 0;
+ }else{
+ pUp->u.iKey++;
+ }
+ }
+ }
+ }else{
+ switch( p->eType ){
+ case JSON_ARRAY: {
+ p->i += jsonNodeSize(&p->sParse.aNode[p->i]);
+ p->iRowid++;
+ break;
+ }
+ case JSON_OBJECT: {
+ p->i += 1 + jsonNodeSize(&p->sParse.aNode[p->i+1]);
+ p->iRowid++;
+ break;
+ }
+ default: {
+ p->i = p->iEnd;
+ break;
+ }
+ }
+ }
+ return SQLITE_OK;
+}
+
+/* Append the name of the path for element i to pStr
+*/
+static void jsonEachComputePath(
+ JsonEachCursor *p, /* The cursor */
+ JsonString *pStr, /* Write the path here */
+ u32 i /* Path to this element */
+){
+ JsonNode *pNode, *pUp;
+ u32 iUp;
+ if( i==0 ){
+ jsonAppendChar(pStr, '$');
+ return;
+ }
+ iUp = p->sParse.aUp[i];
+ jsonEachComputePath(p, pStr, iUp);
+ pNode = &p->sParse.aNode[i];
+ pUp = &p->sParse.aNode[iUp];
+ if( pUp->eType==JSON_ARRAY ){
+ jsonPrintf(30, pStr, "[%d]", pUp->u.iKey);
+ }else{
+ assert( pUp->eType==JSON_OBJECT );
+ if( (pNode->jnFlags & JNODE_LABEL)==0 ) pNode--;
+ assert( pNode->eType==JSON_STRING );
+ assert( pNode->jnFlags & JNODE_LABEL );
+ jsonPrintf(pNode->n+1, pStr, ".%.*s", pNode->n-2, pNode->u.zJContent+1);
+ }
+}
+
+/* Return the value of a column */
+static int jsonEachColumn(
+ sqlite3_vtab_cursor *cur, /* The cursor */
+ sqlite3_context *ctx, /* First argument to sqlite3_result_...() */
+ int i /* Which column to return */
+){
+ JsonEachCursor *p = (JsonEachCursor*)cur;
+ JsonNode *pThis = &p->sParse.aNode[p->i];
+ switch( i ){
+ case JEACH_KEY: {
+ if( p->i==0 ) break;
+ if( p->eType==JSON_OBJECT ){
+ jsonReturn(pThis, ctx, 0);
+ }else if( p->eType==JSON_ARRAY ){
+ u32 iKey;
+ if( p->bRecursive ){
+ if( p->iRowid==0 ) break;
+ iKey = p->sParse.aNode[p->sParse.aUp[p->i]].u.iKey;
+ }else{
+ iKey = p->iRowid;
+ }
+ sqlite3_result_int64(ctx, (sqlite3_int64)iKey);
+ }
+ break;
+ }
+ case JEACH_VALUE: {
+ if( pThis->jnFlags & JNODE_LABEL ) pThis++;
+ jsonReturn(pThis, ctx, 0);
+ break;
+ }
+ case JEACH_TYPE: {
+ if( pThis->jnFlags & JNODE_LABEL ) pThis++;
+ sqlite3_result_text(ctx, jsonType[pThis->eType], -1, SQLITE_STATIC);
+ break;
+ }
+ case JEACH_ATOM: {
+ if( pThis->jnFlags & JNODE_LABEL ) pThis++;
+ if( pThis->eType>=JSON_ARRAY ) break;
+ jsonReturn(pThis, ctx, 0);
+ break;
+ }
+ case JEACH_ID: {
+ sqlite3_result_int64(ctx,
+ (sqlite3_int64)p->i + ((pThis->jnFlags & JNODE_LABEL)!=0));
+ break;
+ }
+ case JEACH_PARENT: {
+ if( p->i>p->iBegin && p->bRecursive ){
+ sqlite3_result_int64(ctx, (sqlite3_int64)p->sParse.aUp[p->i]);
+ }
+ break;
+ }
+ case JEACH_FULLKEY: {
+ JsonString x;
+ jsonInit(&x, ctx);
+ if( p->bRecursive ){
+ jsonEachComputePath(p, &x, p->i);
+ }else{
+ if( p->zRoot ){
+ jsonAppendRaw(&x, p->zRoot, (int)strlen(p->zRoot));
+ }else{
+ jsonAppendChar(&x, '$');
+ }
+ if( p->eType==JSON_ARRAY ){
+ jsonPrintf(30, &x, "[%d]", p->iRowid);
+ }else{
+ jsonPrintf(pThis->n, &x, ".%.*s", pThis->n-2, pThis->u.zJContent+1);
+ }
+ }
+ jsonResult(&x);
+ break;
+ }
+ case JEACH_PATH: {
+ if( p->bRecursive ){
+ JsonString x;
+ jsonInit(&x, ctx);
+ jsonEachComputePath(p, &x, p->sParse.aUp[p->i]);
+ jsonResult(&x);
+ break;
+ }
+ /* For json_each() path and root are the same so fall through
+ ** into the root case */
+ }
+ case JEACH_ROOT: {
+ const char *zRoot = p->zRoot;
+ if( zRoot==0 ) zRoot = "$";
+ sqlite3_result_text(ctx, zRoot, -1, SQLITE_STATIC);
+ break;
+ }
+ case JEACH_JSON: {
+ assert( i==JEACH_JSON );
+ sqlite3_result_text(ctx, p->sParse.zJson, -1, SQLITE_STATIC);
+ break;
+ }
+ }
+ return SQLITE_OK;
+}
+
+/* Return the current rowid value */
+static int jsonEachRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
+ JsonEachCursor *p = (JsonEachCursor*)cur;
+ *pRowid = p->iRowid;
+ return SQLITE_OK;
+}
+
+/* The query strategy is to look for an equality constraint on the json
+** column. Without such a constraint, the table cannot operate. idxNum is
+** 1 if the constraint is found, 3 if the constraint and zRoot are found,
+** and 0 otherwise.
+*/
+static int jsonEachBestIndex(
+ sqlite3_vtab *tab,
+ sqlite3_index_info *pIdxInfo
+){
+ int i;
+ int jsonIdx = -1;
+ int rootIdx = -1;
+ const struct sqlite3_index_constraint *pConstraint;
+
+ UNUSED_PARAM(tab);
+ pConstraint = pIdxInfo->aConstraint;
+ for(i=0; i<pIdxInfo->nConstraint; i++, pConstraint++){
+ if( pConstraint->usable==0 ) continue;
+ if( pConstraint->op!=SQLITE_INDEX_CONSTRAINT_EQ ) continue;
+ switch( pConstraint->iColumn ){
+ case JEACH_JSON: jsonIdx = i; break;
+ case JEACH_ROOT: rootIdx = i; break;
+ default: /* no-op */ break;
+ }
+ }
+ if( jsonIdx<0 ){
+ pIdxInfo->idxNum = 0;
+ pIdxInfo->estimatedCost = 1e99;
+ }else{
+ pIdxInfo->estimatedCost = 1.0;
+ pIdxInfo->aConstraintUsage[jsonIdx].argvIndex = 1;
+ pIdxInfo->aConstraintUsage[jsonIdx].omit = 1;
+ if( rootIdx<0 ){
+ pIdxInfo->idxNum = 1;
+ }else{
+ pIdxInfo->aConstraintUsage[rootIdx].argvIndex = 2;
+ pIdxInfo->aConstraintUsage[rootIdx].omit = 1;
+ pIdxInfo->idxNum = 3;
+ }
+ }
+ return SQLITE_OK;
+}
+
+/* Start a search on a new JSON string */
+static int jsonEachFilter(
+ sqlite3_vtab_cursor *cur,
+ int idxNum, const char *idxStr,
+ int argc, sqlite3_value **argv
+){
+ JsonEachCursor *p = (JsonEachCursor*)cur;
+ const char *z;
+ const char *zRoot = 0;
+ sqlite3_int64 n;
+
+ UNUSED_PARAM(idxStr);
+ UNUSED_PARAM(argc);
+ jsonEachCursorReset(p);
+ if( idxNum==0 ) return SQLITE_OK;
+ z = (const char*)sqlite3_value_text(argv[0]);
+ if( z==0 ) return SQLITE_OK;
+ n = sqlite3_value_bytes(argv[0]);
+ p->zJson = sqlite3_malloc64( n+1 );
+ if( p->zJson==0 ) return SQLITE_NOMEM;
+ memcpy(p->zJson, z, (size_t)n+1);
+ if( jsonParse(&p->sParse, 0, p->zJson) ){
+ int rc = SQLITE_NOMEM;
+ if( p->sParse.oom==0 ){
+ sqlite3_free(cur->pVtab->zErrMsg);
+ cur->pVtab->zErrMsg = sqlite3_mprintf("malformed JSON");
+ if( cur->pVtab->zErrMsg ) rc = SQLITE_ERROR;
+ }
+ jsonEachCursorReset(p);
+ return rc;
+ }else if( p->bRecursive && jsonParseFindParents(&p->sParse) ){
+ jsonEachCursorReset(p);
+ return SQLITE_NOMEM;
+ }else{
+ JsonNode *pNode = 0;
+ if( idxNum==3 ){
+ const char *zErr = 0;
+ zRoot = (const char*)sqlite3_value_text(argv[1]);
+ if( zRoot==0 ) return SQLITE_OK;
+ n = sqlite3_value_bytes(argv[1]);
+ p->zRoot = sqlite3_malloc64( n+1 );
+ if( p->zRoot==0 ) return SQLITE_NOMEM;
+ memcpy(p->zRoot, zRoot, (size_t)n+1);
+ if( zRoot[0]!='$' ){
+ zErr = zRoot;
+ }else{
+ pNode = jsonLookupStep(&p->sParse, 0, p->zRoot+1, 0, &zErr);
+ }
+ if( zErr ){
+ sqlite3_free(cur->pVtab->zErrMsg);
+ cur->pVtab->zErrMsg = jsonPathSyntaxError(zErr);
+ jsonEachCursorReset(p);
+ return cur->pVtab->zErrMsg ? SQLITE_ERROR : SQLITE_NOMEM;
+ }else if( pNode==0 ){
+ return SQLITE_OK;
+ }
+ }else{
+ pNode = p->sParse.aNode;
+ }
+ p->iBegin = p->i = (int)(pNode - p->sParse.aNode);
+ p->eType = pNode->eType;
+ if( p->eType>=JSON_ARRAY ){
+ pNode->u.iKey = 0;
+ p->iEnd = p->i + pNode->n + 1;
+ if( p->bRecursive ){
+ p->eType = p->sParse.aNode[p->sParse.aUp[p->i]].eType;
+ if( p->i>0 && (p->sParse.aNode[p->i-1].jnFlags & JNODE_LABEL)!=0 ){
+ p->i--;
+ }
+ }else{
+ p->i++;
+ }
+ }else{
+ p->iEnd = p->i+1;
+ }
+ }
+ return SQLITE_OK;
+}
+
+/* The methods of the json_each virtual table */
+static sqlite3_module jsonEachModule = {
+ 0, /* iVersion */
+ 0, /* xCreate */
+ jsonEachConnect, /* xConnect */
+ jsonEachBestIndex, /* xBestIndex */
+ jsonEachDisconnect, /* xDisconnect */
+ 0, /* xDestroy */
+ jsonEachOpenEach, /* xOpen - open a cursor */
+ jsonEachClose, /* xClose - close a cursor */
+ jsonEachFilter, /* xFilter - configure scan constraints */
+ jsonEachNext, /* xNext - advance a cursor */
+ jsonEachEof, /* xEof - check for end of scan */
+ jsonEachColumn, /* xColumn - read data */
+ jsonEachRowid, /* xRowid - read data */
+ 0, /* xUpdate */
+ 0, /* xBegin */
+ 0, /* xSync */
+ 0, /* xCommit */
+ 0, /* xRollback */
+ 0, /* xFindMethod */
+ 0, /* xRename */
+ 0, /* xSavepoint */
+ 0, /* xRelease */
+ 0 /* xRollbackTo */
+};
+
+/* The methods of the json_tree virtual table. */
+static sqlite3_module jsonTreeModule = {
+ 0, /* iVersion */
+ 0, /* xCreate */
+ jsonEachConnect, /* xConnect */
+ jsonEachBestIndex, /* xBestIndex */
+ jsonEachDisconnect, /* xDisconnect */
+ 0, /* xDestroy */
+ jsonEachOpenTree, /* xOpen - open a cursor */
+ jsonEachClose, /* xClose - close a cursor */
+ jsonEachFilter, /* xFilter - configure scan constraints */
+ jsonEachNext, /* xNext - advance a cursor */
+ jsonEachEof, /* xEof - check for end of scan */
+ jsonEachColumn, /* xColumn - read data */
+ jsonEachRowid, /* xRowid - read data */
+ 0, /* xUpdate */
+ 0, /* xBegin */
+ 0, /* xSync */
+ 0, /* xCommit */
+ 0, /* xRollback */
+ 0, /* xFindMethod */
+ 0, /* xRename */
+ 0, /* xSavepoint */
+ 0, /* xRelease */
+ 0 /* xRollbackTo */
+};
+#endif /* SQLITE_OMIT_VIRTUALTABLE */
+
+/****************************************************************************
+** The following routines are the only publically visible identifiers in this
+** file. Call the following routines in order to register the various SQL
+** functions and the virtual table implemented by this file.
+****************************************************************************/
+
+SQLITE_PRIVATE int sqlite3Json1Init(sqlite3 *db){
+ int rc = SQLITE_OK;
+ unsigned int i;
+ static const struct {
+ const char *zName;
+ int nArg;
+ int flag;
+ void (*xFunc)(sqlite3_context*,int,sqlite3_value**);
+ } aFunc[] = {
+ { "json", 1, 0, jsonRemoveFunc },
+ { "json_array", -1, 0, jsonArrayFunc },
+ { "json_array_length", 1, 0, jsonArrayLengthFunc },
+ { "json_array_length", 2, 0, jsonArrayLengthFunc },
+ { "json_extract", -1, 0, jsonExtractFunc },
+ { "json_insert", -1, 0, jsonSetFunc },
+ { "json_object", -1, 0, jsonObjectFunc },
+ { "json_remove", -1, 0, jsonRemoveFunc },
+ { "json_replace", -1, 0, jsonReplaceFunc },
+ { "json_set", -1, 1, jsonSetFunc },
+ { "json_type", 1, 0, jsonTypeFunc },
+ { "json_type", 2, 0, jsonTypeFunc },
+ { "json_valid", 1, 0, jsonValidFunc },
+
+#if SQLITE_DEBUG
+ /* DEBUG and TESTING functions */
+ { "json_parse", 1, 0, jsonParseFunc },
+ { "json_test1", 1, 0, jsonTest1Func },
+#endif
+ };
+ static const struct {
+ const char *zName;
+ int nArg;
+ void (*xStep)(sqlite3_context*,int,sqlite3_value**);
+ void (*xFinal)(sqlite3_context*);
+ } aAgg[] = {
+ { "json_group_array", 1, jsonArrayStep, jsonArrayFinal },
+ { "json_group_object", 2, jsonObjectStep, jsonObjectFinal },
+ };
+#ifndef SQLITE_OMIT_VIRTUALTABLE
+ static const struct {
+ const char *zName;
+ sqlite3_module *pModule;
+ } aMod[] = {
+ { "json_each", &jsonEachModule },
+ { "json_tree", &jsonTreeModule },
+ };
+#endif
+ for(i=0; i<sizeof(aFunc)/sizeof(aFunc[0]) && rc==SQLITE_OK; i++){
+ rc = sqlite3_create_function(db, aFunc[i].zName, aFunc[i].nArg,
+ SQLITE_UTF8 | SQLITE_DETERMINISTIC,
+ (void*)&aFunc[i].flag,
+ aFunc[i].xFunc, 0, 0);
+ }
+ for(i=0; i<sizeof(aAgg)/sizeof(aAgg[0]) && rc==SQLITE_OK; i++){
+ rc = sqlite3_create_function(db, aAgg[i].zName, aAgg[i].nArg,
+ SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
+ 0, aAgg[i].xStep, aAgg[i].xFinal);
+ }
+#ifndef SQLITE_OMIT_VIRTUALTABLE
+ for(i=0; i<sizeof(aMod)/sizeof(aMod[0]) && rc==SQLITE_OK; i++){
+ rc = sqlite3_create_module(db, aMod[i].zName, aMod[i].pModule, 0);
+ }
+#endif
+ return rc;
+}
+
+
+#ifndef SQLITE_CORE
+#ifdef _WIN32
+__declspec(dllexport)
+#endif
+SQLITE_API int SQLITE_STDCALL sqlite3_json_init(
+ sqlite3 *db,
+ char **pzErrMsg,
+ const sqlite3_api_routines *pApi
+){
+ SQLITE_EXTENSION_INIT2(pApi);
+ (void)pzErrMsg; /* Unused parameter */
+ return sqlite3Json1Init(db);
+}
+#endif
+#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_JSON1) */
+
+/************** End of json1.c ***********************************************/
+/************** Begin file fts5.c ********************************************/
+
+
+#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS5)
+
+#if !defined(NDEBUG) && !defined(SQLITE_DEBUG)
+# define NDEBUG 1
+#endif
+#if defined(NDEBUG) && defined(SQLITE_DEBUG)
+# undef NDEBUG
+#endif
+
+/*
+** 2014 May 31
+**
+** 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.
+**
+******************************************************************************
+**
+** Interfaces to extend FTS5. Using the interfaces defined in this file,
+** FTS5 may be extended with:
+**
+** * custom tokenizers, and
+** * custom auxiliary functions.
+*/
+
+
+#ifndef _FTS5_H
+#define _FTS5_H
+
+/* #include "sqlite3.h" */
+
+#if 0
+extern "C" {
+#endif
+
+/*************************************************************************
+** CUSTOM AUXILIARY FUNCTIONS
+**
+** Virtual table implementations may overload SQL functions by implementing
+** the sqlite3_module.xFindFunction() method.
+*/
+
+typedef struct Fts5ExtensionApi Fts5ExtensionApi;
+typedef struct Fts5Context Fts5Context;
+typedef struct Fts5PhraseIter Fts5PhraseIter;
+
+typedef void (*fts5_extension_function)(
+ const Fts5ExtensionApi *pApi, /* API offered by current FTS version */
+ Fts5Context *pFts, /* First arg to pass to pApi functions */
+ sqlite3_context *pCtx, /* Context for returning result/error */
+ int nVal, /* Number of values in apVal[] array */
+ sqlite3_value **apVal /* Array of trailing arguments */
+);
+
+struct Fts5PhraseIter {
+ const unsigned char *a;
+ const unsigned char *b;
+};
+
+/*
+** EXTENSION API FUNCTIONS
+**
+** xUserData(pFts):
+** Return a copy of the context pointer the extension function was
+** registered with.
+**
+** xColumnTotalSize(pFts, iCol, pnToken):
+** If parameter iCol is less than zero, set output variable *pnToken
+** to the total number of tokens in the FTS5 table. Or, if iCol is
+** non-negative but less than the number of columns in the table, return
+** the total number of tokens in column iCol, considering all rows in
+** the FTS5 table.
+**
+** If parameter iCol is greater than or equal to the number of columns
+** in the table, SQLITE_RANGE is returned. Or, if an error occurs (e.g.
+** an OOM condition or IO error), an appropriate SQLite error code is
+** returned.
+**
+** xColumnCount(pFts):
+** Return the number of columns in the table.
+**
+** xColumnSize(pFts, iCol, pnToken):
+** If parameter iCol is less than zero, set output variable *pnToken
+** to the total number of tokens in the current row. Or, if iCol is
+** non-negative but less than the number of columns in the table, set
+** *pnToken to the number of tokens in column iCol of the current row.
+**
+** If parameter iCol is greater than or equal to the number of columns
+** in the table, SQLITE_RANGE is returned. Or, if an error occurs (e.g.
+** an OOM condition or IO error), an appropriate SQLite error code is
+** returned.
+**
+** This function may be quite inefficient if used with an FTS5 table
+** created with the "columnsize=0" option.
+**
+** xColumnText:
+** This function attempts to retrieve the text of column iCol of the
+** current document. If successful, (*pz) is set to point to a buffer
+** containing the text in utf-8 encoding, (*pn) is set to the size in bytes
+** (not characters) of the buffer and SQLITE_OK is returned. Otherwise,
+** if an error occurs, an SQLite error code is returned and the final values
+** of (*pz) and (*pn) are undefined.
+**
+** xPhraseCount:
+** Returns the number of phrases in the current query expression.
+**
+** xPhraseSize:
+** Returns the number of tokens in phrase iPhrase of the query. Phrases
+** are numbered starting from zero.
+**
+** xInstCount:
+** Set *pnInst to the total number of occurrences of all phrases within
+** the query within the current row. Return SQLITE_OK if successful, or
+** an error code (i.e. SQLITE_NOMEM) if an error occurs.
+**
+** This API can be quite slow if used with an FTS5 table created with the
+** "detail=none" or "detail=column" option. If the FTS5 table is created
+** with either "detail=none" or "detail=column" and "content=" option
+** (i.e. if it is a contentless table), then this API always returns 0.
+**
+** xInst:
+** Query for the details of phrase match iIdx within the current row.
+** Phrase matches are numbered starting from zero, so the iIdx argument
+** should be greater than or equal to zero and smaller than the value
+** output by xInstCount().
+**
+** Usually, output parameter *piPhrase is set to the phrase number, *piCol
+** to the column in which it occurs and *piOff the token offset of the
+** first token of the phrase. The exception is if the table was created
+** with the offsets=0 option specified. In this case *piOff is always
+** set to -1.
+**
+** Returns SQLITE_OK if successful, or an error code (i.e. SQLITE_NOMEM)
+** if an error occurs.
+**
+** This API can be quite slow if used with an FTS5 table created with the
+** "detail=none" or "detail=column" option.
+**
+** xRowid:
+** Returns the rowid of the current row.
+**
+** xTokenize:
+** Tokenize text using the tokenizer belonging to the FTS5 table.
+**
+** xQueryPhrase(pFts5, iPhrase, pUserData, xCallback):
+** This API function is used to query the FTS table for phrase iPhrase
+** of the current query. Specifically, a query equivalent to:
+**
+** ... FROM ftstable WHERE ftstable MATCH $p ORDER BY rowid
+**
+** with $p set to a phrase equivalent to the phrase iPhrase of the
+** current query is executed. Any column filter that applies to
+** phrase iPhrase of the current query is included in $p. For each
+** row visited, the callback function passed as the fourth argument
+** is invoked. The context and API objects passed to the callback
+** function may be used to access the properties of each matched row.
+** Invoking Api.xUserData() returns a copy of the pointer passed as
+** the third argument to pUserData.
+**
+** If the callback function returns any value other than SQLITE_OK, the
+** query is abandoned and the xQueryPhrase function returns immediately.
+** If the returned value is SQLITE_DONE, xQueryPhrase returns SQLITE_OK.
+** Otherwise, the error code is propagated upwards.
+**
+** If the query runs to completion without incident, SQLITE_OK is returned.
+** Or, if some error occurs before the query completes or is aborted by
+** the callback, an SQLite error code is returned.
+**
+**
+** xSetAuxdata(pFts5, pAux, xDelete)
+**
+** Save the pointer passed as the second argument as the extension functions
+** "auxiliary data". The pointer may then be retrieved by the current or any
+** future invocation of the same fts5 extension function made as part of
+** of the same MATCH query using the xGetAuxdata() API.
+**
+** Each extension function is allocated a single auxiliary data slot for
+** each FTS query (MATCH expression). If the extension function is invoked
+** more than once for a single FTS query, then all invocations share a
+** single auxiliary data context.
+**
+** If there is already an auxiliary data pointer when this function is
+** invoked, then it is replaced by the new pointer. If an xDelete callback
+** was specified along with the original pointer, it is invoked at this
+** point.
+**
+** The xDelete callback, if one is specified, is also invoked on the
+** auxiliary data pointer after the FTS5 query has finished.
+**
+** If an error (e.g. an OOM condition) occurs within this function, an
+** the auxiliary data is set to NULL and an error code returned. If the
+** xDelete parameter was not NULL, it is invoked on the auxiliary data
+** pointer before returning.
+**
+**
+** xGetAuxdata(pFts5, bClear)
+**
+** Returns the current auxiliary data pointer for the fts5 extension
+** function. See the xSetAuxdata() method for details.
+**
+** If the bClear argument is non-zero, then the auxiliary data is cleared
+** (set to NULL) before this function returns. In this case the xDelete,
+** if any, is not invoked.
+**
+**
+** xRowCount(pFts5, pnRow)
+**
+** This function is used to retrieve the total number of rows in the table.
+** In other words, the same value that would be returned by:
+**
+** SELECT count(*) FROM ftstable;
+**
+** xPhraseFirst()
+** This function is used, along with type Fts5PhraseIter and the xPhraseNext
+** method, to iterate through all instances of a single query phrase within
+** the current row. This is the same information as is accessible via the
+** xInstCount/xInst APIs. While the xInstCount/xInst APIs are more convenient
+** to use, this API may be faster under some circumstances. To iterate
+** through instances of phrase iPhrase, use the following code:
+**
+** Fts5PhraseIter iter;
+** int iCol, iOff;
+** for(pApi->xPhraseFirst(pFts, iPhrase, &iter, &iCol, &iOff);
+** iCol>=0;
+** pApi->xPhraseNext(pFts, &iter, &iCol, &iOff)
+** ){
+** // An instance of phrase iPhrase at offset iOff of column iCol
+** }
+**
+** The Fts5PhraseIter structure is defined above. Applications should not
+** modify this structure directly - it should only be used as shown above
+** with the xPhraseFirst() and xPhraseNext() API methods (and by
+** xPhraseFirstColumn() and xPhraseNextColumn() as illustrated below).
+**
+** This API can be quite slow if used with an FTS5 table created with the
+** "detail=none" or "detail=column" option. If the FTS5 table is created
+** with either "detail=none" or "detail=column" and "content=" option
+** (i.e. if it is a contentless table), then this API always iterates
+** through an empty set (all calls to xPhraseFirst() set iCol to -1).
+**
+** xPhraseNext()
+** See xPhraseFirst above.
+**
+** xPhraseFirstColumn()
+** This function and xPhraseNextColumn() are similar to the xPhraseFirst()
+** and xPhraseNext() APIs described above. The difference is that instead
+** of iterating through all instances of a phrase in the current row, these
+** APIs are used to iterate through the set of columns in the current row
+** that contain one or more instances of a specified phrase. For example:
+**
+** Fts5PhraseIter iter;
+** int iCol;
+** for(pApi->xPhraseFirstColumn(pFts, iPhrase, &iter, &iCol);
+** iCol>=0;
+** pApi->xPhraseNextColumn(pFts, &iter, &iCol)
+** ){
+** // Column iCol contains at least one instance of phrase iPhrase
+** }
+**
+** This API can be quite slow if used with an FTS5 table created with the
+** "detail=none" option. If the FTS5 table is created with either
+** "detail=none" "content=" option (i.e. if it is a contentless table),
+** then this API always iterates through an empty set (all calls to
+** xPhraseFirstColumn() set iCol to -1).
+**
+** The information accessed using this API and its companion
+** xPhraseFirstColumn() may also be obtained using xPhraseFirst/xPhraseNext
+** (or xInst/xInstCount). The chief advantage of this API is that it is
+** significantly more efficient than those alternatives when used with
+** "detail=column" tables.
+**
+** xPhraseNextColumn()
+** See xPhraseFirstColumn above.
+*/
+struct Fts5ExtensionApi {
+ int iVersion; /* Currently always set to 3 */
+
+ void *(*xUserData)(Fts5Context*);
+
+ int (*xColumnCount)(Fts5Context*);
+ int (*xRowCount)(Fts5Context*, sqlite3_int64 *pnRow);
+ int (*xColumnTotalSize)(Fts5Context*, int iCol, sqlite3_int64 *pnToken);
+
+ int (*xTokenize)(Fts5Context*,
+ const char *pText, int nText, /* Text to tokenize */
+ void *pCtx, /* Context passed to xToken() */
+ int (*xToken)(void*, int, const char*, int, int, int) /* Callback */
+ );
+
+ int (*xPhraseCount)(Fts5Context*);
+ int (*xPhraseSize)(Fts5Context*, int iPhrase);
+
+ int (*xInstCount)(Fts5Context*, int *pnInst);
+ int (*xInst)(Fts5Context*, int iIdx, int *piPhrase, int *piCol, int *piOff);
+
+ sqlite3_int64 (*xRowid)(Fts5Context*);
+ int (*xColumnText)(Fts5Context*, int iCol, const char **pz, int *pn);
+ int (*xColumnSize)(Fts5Context*, int iCol, int *pnToken);
+
+ int (*xQueryPhrase)(Fts5Context*, int iPhrase, void *pUserData,
+ int(*)(const Fts5ExtensionApi*,Fts5Context*,void*)
+ );
+ int (*xSetAuxdata)(Fts5Context*, void *pAux, void(*xDelete)(void*));
+ void *(*xGetAuxdata)(Fts5Context*, int bClear);
+
+ int (*xPhraseFirst)(Fts5Context*, int iPhrase, Fts5PhraseIter*, int*, int*);
+ void (*xPhraseNext)(Fts5Context*, Fts5PhraseIter*, int *piCol, int *piOff);
+
+ int (*xPhraseFirstColumn)(Fts5Context*, int iPhrase, Fts5PhraseIter*, int*);
+ void (*xPhraseNextColumn)(Fts5Context*, Fts5PhraseIter*, int *piCol);
+};
+
+/*
+** CUSTOM AUXILIARY FUNCTIONS
+*************************************************************************/
+
+/*************************************************************************
+** CUSTOM TOKENIZERS
+**
+** Applications may also register custom tokenizer types. A tokenizer
+** is registered by providing fts5 with a populated instance of the
+** following structure. All structure methods must be defined, setting
+** any member of the fts5_tokenizer struct to NULL leads to undefined
+** behaviour. The structure methods are expected to function as follows:
+**
+** xCreate:
+** This function is used to allocate and inititalize a tokenizer instance.
+** A tokenizer instance is required to actually tokenize text.
+**
+** The first argument passed to this function is a copy of the (void*)
+** pointer provided by the application when the fts5_tokenizer object
+** was registered with FTS5 (the third argument to xCreateTokenizer()).
+** The second and third arguments are an array of nul-terminated strings
+** containing the tokenizer arguments, if any, specified following the
+** tokenizer name as part of the CREATE VIRTUAL TABLE statement used
+** to create the FTS5 table.
+**
+** The final argument is an output variable. If successful, (*ppOut)
+** should be set to point to the new tokenizer handle and SQLITE_OK
+** returned. If an error occurs, some value other than SQLITE_OK should
+** be returned. In this case, fts5 assumes that the final value of *ppOut
+** is undefined.
+**
+** xDelete:
+** This function is invoked to delete a tokenizer handle previously
+** allocated using xCreate(). Fts5 guarantees that this function will
+** be invoked exactly once for each successful call to xCreate().
+**
+** xTokenize:
+** This function is expected to tokenize the nText byte string indicated
+** by argument pText. pText may or may not be nul-terminated. The first
+** argument passed to this function is a pointer to an Fts5Tokenizer object
+** returned by an earlier call to xCreate().
+**
+** The second argument indicates the reason that FTS5 is requesting
+** tokenization of the supplied text. This is always one of the following
+** four values:
+**
+** <ul><li> <b>FTS5_TOKENIZE_DOCUMENT</b> - A document is being inserted into
+** or removed from the FTS table. The tokenizer is being invoked to
+** determine the set of tokens to add to (or delete from) the
+** FTS index.
+**
+** <li> <b>FTS5_TOKENIZE_QUERY</b> - A MATCH query is being executed
+** against the FTS index. The tokenizer is being called to tokenize
+** a bareword or quoted string specified as part of the query.
+**
+** <li> <b>(FTS5_TOKENIZE_QUERY | FTS5_TOKENIZE_PREFIX)</b> - Same as
+** FTS5_TOKENIZE_QUERY, except that the bareword or quoted string is
+** followed by a "*" character, indicating that the last token
+** returned by the tokenizer will be treated as a token prefix.
+**
+** <li> <b>FTS5_TOKENIZE_AUX</b> - The tokenizer is being invoked to
+** satisfy an fts5_api.xTokenize() request made by an auxiliary
+** function. Or an fts5_api.xColumnSize() request made by the same
+** on a columnsize=0 database.
+** </ul>
+**
+** For each token in the input string, the supplied callback xToken() must
+** be invoked. The first argument to it should be a copy of the pointer
+** passed as the second argument to xTokenize(). The third and fourth
+** arguments are a pointer to a buffer containing the token text, and the
+** size of the token in bytes. The 4th and 5th arguments are the byte offsets
+** of the first byte of and first byte immediately following the text from
+** which the token is derived within the input.
+**
+** The second argument passed to the xToken() callback ("tflags") should
+** normally be set to 0. The exception is if the tokenizer supports
+** synonyms. In this case see the discussion below for details.
+**
+** FTS5 assumes the xToken() callback is invoked for each token in the
+** order that they occur within the input text.
+**
+** If an xToken() callback returns any value other than SQLITE_OK, then
+** the tokenization should be abandoned and the xTokenize() method should
+** immediately return a copy of the xToken() return value. Or, if the
+** input buffer is exhausted, xTokenize() should return SQLITE_OK. Finally,
+** if an error occurs with the xTokenize() implementation itself, it
+** may abandon the tokenization and return any error code other than
+** SQLITE_OK or SQLITE_DONE.
+**
+** SYNONYM SUPPORT
+**
+** Custom tokenizers may also support synonyms. Consider a case in which a
+** user wishes to query for a phrase such as "first place". Using the
+** built-in tokenizers, the FTS5 query 'first + place' will match instances
+** of "first place" within the document set, but not alternative forms
+** such as "1st place". In some applications, it would be better to match
+** all instances of "first place" or "1st place" regardless of which form
+** the user specified in the MATCH query text.
+**
+** There are several ways to approach this in FTS5:
+**
+** <ol><li> By mapping all synonyms to a single token. In this case, the
+** In the above example, this means that the tokenizer returns the
+** same token for inputs "first" and "1st". Say that token is in
+** fact "first", so that when the user inserts the document "I won
+** 1st place" entries are added to the index for tokens "i", "won",
+** "first" and "place". If the user then queries for '1st + place',
+** the tokenizer substitutes "first" for "1st" and the query works
+** as expected.
+**
+** <li> By adding multiple synonyms for a single term to the FTS index.
+** In this case, when tokenizing query text, the tokenizer may
+** provide multiple synonyms for a single term within the document.
+** FTS5 then queries the index for each synonym individually. For
+** example, faced with the query:
+**
+** <codeblock>
+** ... MATCH 'first place'</codeblock>
+**
+** the tokenizer offers both "1st" and "first" as synonyms for the
+** first token in the MATCH query and FTS5 effectively runs a query
+** similar to:
+**
+** <codeblock>
+** ... MATCH '(first OR 1st) place'</codeblock>
+**
+** except that, for the purposes of auxiliary functions, the query
+** still appears to contain just two phrases - "(first OR 1st)"
+** being treated as a single phrase.
+**
+** <li> By adding multiple synonyms for a single term to the FTS index.
+** Using this method, when tokenizing document text, the tokenizer
+** provides multiple synonyms for each token. So that when a
+** document such as "I won first place" is tokenized, entries are
+** added to the FTS index for "i", "won", "first", "1st" and
+** "place".
+**
+** This way, even if the tokenizer does not provide synonyms
+** when tokenizing query text (it should not - to do would be
+** inefficient), it doesn't matter if the user queries for
+** 'first + place' or '1st + place', as there are entires in the
+** FTS index corresponding to both forms of the first token.
+** </ol>
+**
+** Whether it is parsing document or query text, any call to xToken that
+** specifies a <i>tflags</i> argument with the FTS5_TOKEN_COLOCATED bit
+** is considered to supply a synonym for the previous token. For example,
+** when parsing the document "I won first place", a tokenizer that supports
+** synonyms would call xToken() 5 times, as follows:
+**
+** <codeblock>
+** xToken(pCtx, 0, "i", 1, 0, 1);
+** xToken(pCtx, 0, "won", 3, 2, 5);
+** xToken(pCtx, 0, "first", 5, 6, 11);
+** xToken(pCtx, FTS5_TOKEN_COLOCATED, "1st", 3, 6, 11);
+** xToken(pCtx, 0, "place", 5, 12, 17);
+**</codeblock>
+**
+** It is an error to specify the FTS5_TOKEN_COLOCATED flag the first time
+** xToken() is called. Multiple synonyms may be specified for a single token
+** by making multiple calls to xToken(FTS5_TOKEN_COLOCATED) in sequence.
+** There is no limit to the number of synonyms that may be provided for a
+** single token.
+**
+** In many cases, method (1) above is the best approach. It does not add
+** extra data to the FTS index or require FTS5 to query for multiple terms,
+** so it is efficient in terms of disk space and query speed. However, it
+** does not support prefix queries very well. If, as suggested above, the
+** token "first" is subsituted for "1st" by the tokenizer, then the query:
+**
+** <codeblock>
+** ... MATCH '1s*'</codeblock>
+**
+** will not match documents that contain the token "1st" (as the tokenizer
+** will probably not map "1s" to any prefix of "first").
+**
+** For full prefix support, method (3) may be preferred. In this case,
+** because the index contains entries for both "first" and "1st", prefix
+** queries such as 'fi*' or '1s*' will match correctly. However, because
+** extra entries are added to the FTS index, this method uses more space
+** within the database.
+**
+** Method (2) offers a midpoint between (1) and (3). Using this method,
+** a query such as '1s*' will match documents that contain the literal
+** token "1st", but not "first" (assuming the tokenizer is not able to
+** provide synonyms for prefixes). However, a non-prefix query like '1st'
+** will match against "1st" and "first". This method does not require
+** extra disk space, as no extra entries are added to the FTS index.
+** On the other hand, it may require more CPU cycles to run MATCH queries,
+** as separate queries of the FTS index are required for each synonym.
+**
+** When using methods (2) or (3), it is important that the tokenizer only
+** provide synonyms when tokenizing document text (method (2)) or query
+** text (method (3)), not both. Doing so will not cause any errors, but is
+** inefficient.
+*/
+typedef struct Fts5Tokenizer Fts5Tokenizer;
+typedef struct fts5_tokenizer fts5_tokenizer;
+struct fts5_tokenizer {
+ int (*xCreate)(void*, const char **azArg, int nArg, Fts5Tokenizer **ppOut);
+ void (*xDelete)(Fts5Tokenizer*);
+ int (*xTokenize)(Fts5Tokenizer*,
+ void *pCtx,
+ int flags, /* Mask of FTS5_TOKENIZE_* flags */
+ const char *pText, int nText,
+ int (*xToken)(
+ void *pCtx, /* Copy of 2nd argument to xTokenize() */
+ int tflags, /* Mask of FTS5_TOKEN_* flags */
+ const char *pToken, /* Pointer to buffer containing token */
+ int nToken, /* Size of token in bytes */
+ int iStart, /* Byte offset of token within input text */
+ int iEnd /* Byte offset of end of token within input text */
+ )
+ );
+};
+
+/* Flags that may be passed as the third argument to xTokenize() */
+#define FTS5_TOKENIZE_QUERY 0x0001
+#define FTS5_TOKENIZE_PREFIX 0x0002
+#define FTS5_TOKENIZE_DOCUMENT 0x0004
+#define FTS5_TOKENIZE_AUX 0x0008
+
+/* Flags that may be passed by the tokenizer implementation back to FTS5
+** as the third argument to the supplied xToken callback. */
+#define FTS5_TOKEN_COLOCATED 0x0001 /* Same position as prev. token */
+
+/*
+** END OF CUSTOM TOKENIZERS
+*************************************************************************/
+
+/*************************************************************************
+** FTS5 EXTENSION REGISTRATION API
+*/
+typedef struct fts5_api fts5_api;
+struct fts5_api {
+ int iVersion; /* Currently always set to 2 */
+
+ /* Create a new tokenizer */
+ int (*xCreateTokenizer)(
+ fts5_api *pApi,
+ const char *zName,
+ void *pContext,
+ fts5_tokenizer *pTokenizer,
+ void (*xDestroy)(void*)
+ );
+
+ /* Find an existing tokenizer */
+ int (*xFindTokenizer)(
+ fts5_api *pApi,
+ const char *zName,
+ void **ppContext,
+ fts5_tokenizer *pTokenizer
+ );
+
+ /* Create a new auxiliary function */
+ int (*xCreateFunction)(
+ fts5_api *pApi,
+ const char *zName,
+ void *pContext,
+ fts5_extension_function xFunction,
+ void (*xDestroy)(void*)
+ );
+};
+
+/*
+** END OF REGISTRATION API
+*************************************************************************/
+
+#if 0
+} /* end of the 'extern "C"' block */
+#endif
+
+#endif /* _FTS5_H */
+
+
+/*
+** 2014 May 31
+**
+** 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.
+**
+******************************************************************************
+**
+*/
+#ifndef _FTS5INT_H
+#define _FTS5INT_H
+
+/* #include "fts5.h" */
+/* #include "sqlite3ext.h" */
+SQLITE_EXTENSION_INIT1
+
+/* #include <string.h> */
+/* #include <assert.h> */
+
+#ifndef SQLITE_AMALGAMATION
+
+typedef unsigned char u8;
+typedef unsigned int u32;
+typedef unsigned short u16;
+typedef short i16;
+typedef sqlite3_int64 i64;
+typedef sqlite3_uint64 u64;
+
+#define ArraySize(x) ((int)(sizeof(x) / sizeof(x[0])))
+
+#define testcase(x)
+#define ALWAYS(x) 1
+#define NEVER(x) 0
+
+#define MIN(x,y) (((x) < (y)) ? (x) : (y))
+#define MAX(x,y) (((x) > (y)) ? (x) : (y))
+
+/*
+** Constants for the largest and smallest possible 64-bit signed integers.
+*/
+# define LARGEST_INT64 (0xffffffff|(((i64)0x7fffffff)<<32))
+# define SMALLEST_INT64 (((i64)-1) - LARGEST_INT64)
+
+#endif
+
+/* Truncate very long tokens to this many bytes. Hard limit is
+** (65536-1-1-4-9)==65521 bytes. The limiting factor is the 16-bit offset
+** field that occurs at the start of each leaf page (see fts5_index.c). */
+#define FTS5_MAX_TOKEN_SIZE 32768
+
+/*
+** Maximum number of prefix indexes on single FTS5 table. This must be
+** less than 32. If it is set to anything large than that, an #error
+** directive in fts5_index.c will cause the build to fail.
+*/
+#define FTS5_MAX_PREFIX_INDEXES 31
+
+#define FTS5_DEFAULT_NEARDIST 10
+#define FTS5_DEFAULT_RANK "bm25"
+
+/* Name of rank and rowid columns */
+#define FTS5_RANK_NAME "rank"
+#define FTS5_ROWID_NAME "rowid"
+
+#ifdef SQLITE_DEBUG
+# define FTS5_CORRUPT sqlite3Fts5Corrupt()
+static int sqlite3Fts5Corrupt(void);
+#else
+# define FTS5_CORRUPT SQLITE_CORRUPT_VTAB
+#endif
+
+/*
+** The assert_nc() macro is similar to the assert() macro, except that it
+** is used for assert() conditions that are true only if it can be
+** guranteed that the database is not corrupt.
+*/
+#ifdef SQLITE_DEBUG
+SQLITE_API extern int sqlite3_fts5_may_be_corrupt;
+# define assert_nc(x) assert(sqlite3_fts5_may_be_corrupt || (x))
+#else
+# define assert_nc(x) assert(x)
+#endif
+
+/* Mark a function parameter as unused, to suppress nuisance compiler
+** warnings. */
+#ifndef UNUSED_PARAM
+# define UNUSED_PARAM(X) (void)(X)
+#endif
+
+#ifndef UNUSED_PARAM2
+# define UNUSED_PARAM2(X, Y) (void)(X), (void)(Y)
+#endif
+
+typedef struct Fts5Global Fts5Global;
+typedef struct Fts5Colset Fts5Colset;
+
+/* If a NEAR() clump or phrase may only match a specific set of columns,
+** then an object of the following type is used to record the set of columns.
+** Each entry in the aiCol[] array is a column that may be matched.
+**
+** This object is used by fts5_expr.c and fts5_index.c.
+*/
+struct Fts5Colset {
+ int nCol;
+ int aiCol[1];
+};
+
+
+
+/**************************************************************************
+** Interface to code in fts5_config.c. fts5_config.c contains contains code
+** to parse the arguments passed to the CREATE VIRTUAL TABLE statement.
+*/
+
+typedef struct Fts5Config Fts5Config;
+
+/*
+** An instance of the following structure encodes all information that can
+** be gleaned from the CREATE VIRTUAL TABLE statement.
+**
+** And all information loaded from the %_config table.
+**
+** nAutomerge:
+** The minimum number of segments that an auto-merge operation should
+** attempt to merge together. A value of 1 sets the object to use the
+** compile time default. Zero disables auto-merge altogether.
+**
+** zContent:
+**
+** zContentRowid:
+** The value of the content_rowid= option, if one was specified. Or
+** the string "rowid" otherwise. This text is not quoted - if it is
+** used as part of an SQL statement it needs to be quoted appropriately.
+**
+** zContentExprlist:
+**
+** pzErrmsg:
+** This exists in order to allow the fts5_index.c module to return a
+** decent error message if it encounters a file-format version it does
+** not understand.
+**
+** bColumnsize:
+** True if the %_docsize table is created.
+**
+** bPrefixIndex:
+** This is only used for debugging. If set to false, any prefix indexes
+** are ignored. This value is configured using:
+**
+** INSERT INTO tbl(tbl, rank) VALUES('prefix-index', $bPrefixIndex);
+**
+*/
+struct Fts5Config {
+ sqlite3 *db; /* Database handle */
+ char *zDb; /* Database holding FTS index (e.g. "main") */
+ char *zName; /* Name of FTS index */
+ int nCol; /* Number of columns */
+ char **azCol; /* Column names */
+ u8 *abUnindexed; /* True for unindexed columns */
+ int nPrefix; /* Number of prefix indexes */
+ int *aPrefix; /* Sizes in bytes of nPrefix prefix indexes */
+ int eContent; /* An FTS5_CONTENT value */
+ char *zContent; /* content table */
+ char *zContentRowid; /* "content_rowid=" option value */
+ int bColumnsize; /* "columnsize=" option value (dflt==1) */
+ int eDetail; /* FTS5_DETAIL_XXX value */
+ char *zContentExprlist;
+ Fts5Tokenizer *pTok;
+ fts5_tokenizer *pTokApi;
+
+ /* Values loaded from the %_config table */
+ int iCookie; /* Incremented when %_config is modified */
+ int pgsz; /* Approximate page size used in %_data */
+ int nAutomerge; /* 'automerge' setting */
+ int nCrisisMerge; /* Maximum allowed segments per level */
+ int nUsermerge; /* 'usermerge' setting */
+ int nHashSize; /* Bytes of memory for in-memory hash */
+ char *zRank; /* Name of rank function */
+ char *zRankArgs; /* Arguments to rank function */
+
+ /* If non-NULL, points to sqlite3_vtab.base.zErrmsg. Often NULL. */
+ char **pzErrmsg;
+
+#ifdef SQLITE_DEBUG
+ int bPrefixIndex; /* True to use prefix-indexes */
+#endif
+};
+
+/* Current expected value of %_config table 'version' field */
+#define FTS5_CURRENT_VERSION 4
+
+#define FTS5_CONTENT_NORMAL 0
+#define FTS5_CONTENT_NONE 1
+#define FTS5_CONTENT_EXTERNAL 2
+
+#define FTS5_DETAIL_FULL 0
+#define FTS5_DETAIL_NONE 1
+#define FTS5_DETAIL_COLUMNS 2
+
+
+
+static int sqlite3Fts5ConfigParse(
+ Fts5Global*, sqlite3*, int, const char **, Fts5Config**, char**
+);
+static void sqlite3Fts5ConfigFree(Fts5Config*);
+
+static int sqlite3Fts5ConfigDeclareVtab(Fts5Config *pConfig);
+
+static int sqlite3Fts5Tokenize(
+ Fts5Config *pConfig, /* FTS5 Configuration object */
+ int flags, /* FTS5_TOKENIZE_* flags */
+ const char *pText, int nText, /* Text to tokenize */
+ void *pCtx, /* Context passed to xToken() */
+ int (*xToken)(void*, int, const char*, int, int, int) /* Callback */
+);
+
+static void sqlite3Fts5Dequote(char *z);
+
+/* Load the contents of the %_config table */
+static int sqlite3Fts5ConfigLoad(Fts5Config*, int);
+
+/* Set the value of a single config attribute */
+static int sqlite3Fts5ConfigSetValue(Fts5Config*, const char*, sqlite3_value*, int*);
+
+static int sqlite3Fts5ConfigParseRank(const char*, char**, char**);
+
+/*
+** End of interface to code in fts5_config.c.
+**************************************************************************/
+
+/**************************************************************************
+** Interface to code in fts5_buffer.c.
+*/
+
+/*
+** Buffer object for the incremental building of string data.
+*/
+typedef struct Fts5Buffer Fts5Buffer;
+struct Fts5Buffer {
+ u8 *p;
+ int n;
+ int nSpace;
+};
+
+static int sqlite3Fts5BufferSize(int*, Fts5Buffer*, u32);
+static void sqlite3Fts5BufferAppendVarint(int*, Fts5Buffer*, i64);
+static void sqlite3Fts5BufferAppendBlob(int*, Fts5Buffer*, u32, const u8*);
+static void sqlite3Fts5BufferAppendString(int *, Fts5Buffer*, const char*);
+static void sqlite3Fts5BufferFree(Fts5Buffer*);
+static void sqlite3Fts5BufferZero(Fts5Buffer*);
+static void sqlite3Fts5BufferSet(int*, Fts5Buffer*, int, const u8*);
+static void sqlite3Fts5BufferAppendPrintf(int *, Fts5Buffer*, char *zFmt, ...);
+
+static char *sqlite3Fts5Mprintf(int *pRc, const char *zFmt, ...);
+
+#define fts5BufferZero(x) sqlite3Fts5BufferZero(x)
+#define fts5BufferAppendVarint(a,b,c) sqlite3Fts5BufferAppendVarint(a,b,c)
+#define fts5BufferFree(a) sqlite3Fts5BufferFree(a)
+#define fts5BufferAppendBlob(a,b,c,d) sqlite3Fts5BufferAppendBlob(a,b,c,d)
+#define fts5BufferSet(a,b,c,d) sqlite3Fts5BufferSet(a,b,c,d)
+
+#define fts5BufferGrow(pRc,pBuf,nn) ( \
+ (u32)((pBuf)->n) + (u32)(nn) <= (u32)((pBuf)->nSpace) ? 0 : \
+ sqlite3Fts5BufferSize((pRc),(pBuf),(nn)+(pBuf)->n) \
+)
+
+/* Write and decode big-endian 32-bit integer values */
+static void sqlite3Fts5Put32(u8*, int);
+static int sqlite3Fts5Get32(const u8*);
+
+#define FTS5_POS2COLUMN(iPos) (int)(iPos >> 32)
+#define FTS5_POS2OFFSET(iPos) (int)(iPos & 0xFFFFFFFF)
+
+typedef struct Fts5PoslistReader Fts5PoslistReader;
+struct Fts5PoslistReader {
+ /* Variables used only by sqlite3Fts5PoslistIterXXX() functions. */
+ const u8 *a; /* Position list to iterate through */
+ int n; /* Size of buffer at a[] in bytes */
+ int i; /* Current offset in a[] */
+
+ u8 bFlag; /* For client use (any custom purpose) */
+
+ /* Output variables */
+ u8 bEof; /* Set to true at EOF */
+ i64 iPos; /* (iCol<<32) + iPos */
+};
+static int sqlite3Fts5PoslistReaderInit(
+ const u8 *a, int n, /* Poslist buffer to iterate through */
+ Fts5PoslistReader *pIter /* Iterator object to initialize */
+);
+static int sqlite3Fts5PoslistReaderNext(Fts5PoslistReader*);
+
+typedef struct Fts5PoslistWriter Fts5PoslistWriter;
+struct Fts5PoslistWriter {
+ i64 iPrev;
+};
+static int sqlite3Fts5PoslistWriterAppend(Fts5Buffer*, Fts5PoslistWriter*, i64);
+static void sqlite3Fts5PoslistSafeAppend(Fts5Buffer*, i64*, i64);
+
+static int sqlite3Fts5PoslistNext64(
+ const u8 *a, int n, /* Buffer containing poslist */
+ int *pi, /* IN/OUT: Offset within a[] */
+ i64 *piOff /* IN/OUT: Current offset */
+);
+
+/* Malloc utility */
+static void *sqlite3Fts5MallocZero(int *pRc, int nByte);
+static char *sqlite3Fts5Strndup(int *pRc, const char *pIn, int nIn);
+
+/* Character set tests (like isspace(), isalpha() etc.) */
+static int sqlite3Fts5IsBareword(char t);
+
+
+/* Bucket of terms object used by the integrity-check in offsets=0 mode. */
+typedef struct Fts5Termset Fts5Termset;
+static int sqlite3Fts5TermsetNew(Fts5Termset**);
+static int sqlite3Fts5TermsetAdd(Fts5Termset*, int, const char*, int, int *pbPresent);
+static void sqlite3Fts5TermsetFree(Fts5Termset*);
+
+/*
+** End of interface to code in fts5_buffer.c.
+**************************************************************************/
+
+/**************************************************************************
+** Interface to code in fts5_index.c. fts5_index.c contains contains code
+** to access the data stored in the %_data table.
+*/
+
+typedef struct Fts5Index Fts5Index;
+typedef struct Fts5IndexIter Fts5IndexIter;
+
+struct Fts5IndexIter {
+ i64 iRowid;
+ const u8 *pData;
+ int nData;
+ u8 bEof;
+};
+
+#define sqlite3Fts5IterEof(x) ((x)->bEof)
+
+/*
+** Values used as part of the flags argument passed to IndexQuery().
+*/
+#define FTS5INDEX_QUERY_PREFIX 0x0001 /* Prefix query */
+#define FTS5INDEX_QUERY_DESC 0x0002 /* Docs in descending rowid order */
+#define FTS5INDEX_QUERY_TEST_NOIDX 0x0004 /* Do not use prefix index */
+#define FTS5INDEX_QUERY_SCAN 0x0008 /* Scan query (fts5vocab) */
+
+/* The following are used internally by the fts5_index.c module. They are
+** defined here only to make it easier to avoid clashes with the flags
+** above. */
+#define FTS5INDEX_QUERY_SKIPEMPTY 0x0010
+#define FTS5INDEX_QUERY_NOOUTPUT 0x0020
+
+/*
+** Create/destroy an Fts5Index object.
+*/
+static int sqlite3Fts5IndexOpen(Fts5Config *pConfig, int bCreate, Fts5Index**, char**);
+static int sqlite3Fts5IndexClose(Fts5Index *p);
+
+/*
+** Return a simple checksum value based on the arguments.
+*/
+static u64 sqlite3Fts5IndexEntryCksum(
+ i64 iRowid,
+ int iCol,
+ int iPos,
+ int iIdx,
+ const char *pTerm,
+ int nTerm
+);
+
+/*
+** Argument p points to a buffer containing utf-8 text that is n bytes in
+** size. Return the number of bytes in the nChar character prefix of the
+** buffer, or 0 if there are less than nChar characters in total.
+*/
+static int sqlite3Fts5IndexCharlenToBytelen(
+ const char *p,
+ int nByte,
+ int nChar
+);
+
+/*
+** Open a new iterator to iterate though all rowids that match the
+** specified token or token prefix.
+*/
+static int sqlite3Fts5IndexQuery(
+ Fts5Index *p, /* FTS index to query */
+ const char *pToken, int nToken, /* Token (or prefix) to query for */
+ int flags, /* Mask of FTS5INDEX_QUERY_X flags */
+ Fts5Colset *pColset, /* Match these columns only */
+ Fts5IndexIter **ppIter /* OUT: New iterator object */
+);
+
+/*
+** The various operations on open token or token prefix iterators opened
+** using sqlite3Fts5IndexQuery().
+*/
+static int sqlite3Fts5IterNext(Fts5IndexIter*);
+static int sqlite3Fts5IterNextFrom(Fts5IndexIter*, i64 iMatch);
+
+/*
+** Close an iterator opened by sqlite3Fts5IndexQuery().
+*/
+static void sqlite3Fts5IterClose(Fts5IndexIter*);
+
+/*
+** This interface is used by the fts5vocab module.
+*/
+static const char *sqlite3Fts5IterTerm(Fts5IndexIter*, int*);
+static int sqlite3Fts5IterNextScan(Fts5IndexIter*);
+
+
+/*
+** Insert or remove data to or from the index. Each time a document is
+** added to or removed from the index, this function is called one or more
+** times.
+**
+** For an insert, it must be called once for each token in the new document.
+** If the operation is a delete, it must be called (at least) once for each
+** unique token in the document with an iCol value less than zero. The iPos
+** argument is ignored for a delete.
+*/
+static int sqlite3Fts5IndexWrite(
+ Fts5Index *p, /* Index to write to */
+ int iCol, /* Column token appears in (-ve -> delete) */
+ int iPos, /* Position of token within column */
+ const char *pToken, int nToken /* Token to add or remove to or from index */
+);
+
+/*
+** Indicate that subsequent calls to sqlite3Fts5IndexWrite() pertain to
+** document iDocid.
+*/
+static int sqlite3Fts5IndexBeginWrite(
+ Fts5Index *p, /* Index to write to */
+ int bDelete, /* True if current operation is a delete */
+ i64 iDocid /* Docid to add or remove data from */
+);
+
+/*
+** Flush any data stored in the in-memory hash tables to the database.
+** If the bCommit flag is true, also close any open blob handles.
+*/
+static int sqlite3Fts5IndexSync(Fts5Index *p, int bCommit);
+
+/*
+** Discard any data stored in the in-memory hash tables. Do not write it
+** to the database. Additionally, assume that the contents of the %_data
+** table may have changed on disk. So any in-memory caches of %_data
+** records must be invalidated.
+*/
+static int sqlite3Fts5IndexRollback(Fts5Index *p);
+
+/*
+** Get or set the "averages" values.
+*/
+static int sqlite3Fts5IndexGetAverages(Fts5Index *p, i64 *pnRow, i64 *anSize);
+static int sqlite3Fts5IndexSetAverages(Fts5Index *p, const u8*, int);
+
+/*
+** Functions called by the storage module as part of integrity-check.
+*/
+static int sqlite3Fts5IndexIntegrityCheck(Fts5Index*, u64 cksum);
+
+/*
+** Called during virtual module initialization to register UDF
+** fts5_decode() with SQLite
+*/
+static int sqlite3Fts5IndexInit(sqlite3*);
+
+static int sqlite3Fts5IndexSetCookie(Fts5Index*, int);
+
+/*
+** Return the total number of entries read from the %_data table by
+** this connection since it was created.
+*/
+static int sqlite3Fts5IndexReads(Fts5Index *p);
+
+static int sqlite3Fts5IndexReinit(Fts5Index *p);
+static int sqlite3Fts5IndexOptimize(Fts5Index *p);
+static int sqlite3Fts5IndexMerge(Fts5Index *p, int nMerge);
+static int sqlite3Fts5IndexReset(Fts5Index *p);
+
+static int sqlite3Fts5IndexLoadConfig(Fts5Index *p);
+
+/*
+** End of interface to code in fts5_index.c.
+**************************************************************************/
+
+/**************************************************************************
+** Interface to code in fts5_varint.c.
+*/
+static int sqlite3Fts5GetVarint32(const unsigned char *p, u32 *v);
+static int sqlite3Fts5GetVarintLen(u32 iVal);
+static u8 sqlite3Fts5GetVarint(const unsigned char*, u64*);
+static int sqlite3Fts5PutVarint(unsigned char *p, u64 v);
+
+#define fts5GetVarint32(a,b) sqlite3Fts5GetVarint32(a,(u32*)&b)
+#define fts5GetVarint sqlite3Fts5GetVarint
+
+#define fts5FastGetVarint32(a, iOff, nVal) { \
+ nVal = (a)[iOff++]; \
+ if( nVal & 0x80 ){ \
+ iOff--; \
+ iOff += fts5GetVarint32(&(a)[iOff], nVal); \
+ } \
+}
+
+
+/*
+** End of interface to code in fts5_varint.c.
+**************************************************************************/
+
+
+/**************************************************************************
+** Interface to code in fts5.c.
+*/
+
+static int sqlite3Fts5GetTokenizer(
+ Fts5Global*,
+ const char **azArg,
+ int nArg,
+ Fts5Tokenizer**,
+ fts5_tokenizer**,
+ char **pzErr
+);
+
+static Fts5Index *sqlite3Fts5IndexFromCsrid(Fts5Global*, i64, Fts5Config **);
+
+/*
+** End of interface to code in fts5.c.
+**************************************************************************/
+
+/**************************************************************************
+** Interface to code in fts5_hash.c.
+*/
+typedef struct Fts5Hash Fts5Hash;
+
+/*
+** Create a hash table, free a hash table.
+*/
+static int sqlite3Fts5HashNew(Fts5Config*, Fts5Hash**, int *pnSize);
+static void sqlite3Fts5HashFree(Fts5Hash*);
+
+static int sqlite3Fts5HashWrite(
+ Fts5Hash*,
+ i64 iRowid, /* Rowid for this entry */
+ int iCol, /* Column token appears in (-ve -> delete) */
+ int iPos, /* Position of token within column */
+ char bByte,
+ const char *pToken, int nToken /* Token to add or remove to or from index */
+);
+
+/*
+** Empty (but do not delete) a hash table.
+*/
+static void sqlite3Fts5HashClear(Fts5Hash*);
+
+static int sqlite3Fts5HashQuery(
+ Fts5Hash*, /* Hash table to query */
+ const char *pTerm, int nTerm, /* Query term */
+ const u8 **ppDoclist, /* OUT: Pointer to doclist for pTerm */
+ int *pnDoclist /* OUT: Size of doclist in bytes */
+);
+
+static int sqlite3Fts5HashScanInit(
+ Fts5Hash*, /* Hash table to query */
+ const char *pTerm, int nTerm /* Query prefix */
+);
+static void sqlite3Fts5HashScanNext(Fts5Hash*);
+static int sqlite3Fts5HashScanEof(Fts5Hash*);
+static void sqlite3Fts5HashScanEntry(Fts5Hash *,
+ const char **pzTerm, /* OUT: term (nul-terminated) */
+ const u8 **ppDoclist, /* OUT: pointer to doclist */
+ int *pnDoclist /* OUT: size of doclist in bytes */
+);
+
+
+/*
+** End of interface to code in fts5_hash.c.
+**************************************************************************/
+
+/**************************************************************************
+** Interface to code in fts5_storage.c. fts5_storage.c contains contains
+** code to access the data stored in the %_content and %_docsize tables.
+*/
+
+#define FTS5_STMT_SCAN_ASC 0 /* SELECT rowid, * FROM ... ORDER BY 1 ASC */
+#define FTS5_STMT_SCAN_DESC 1 /* SELECT rowid, * FROM ... ORDER BY 1 DESC */
+#define FTS5_STMT_LOOKUP 2 /* SELECT rowid, * FROM ... WHERE rowid=? */
+
+typedef struct Fts5Storage Fts5Storage;
+
+static int sqlite3Fts5StorageOpen(Fts5Config*, Fts5Index*, int, Fts5Storage**, char**);
+static int sqlite3Fts5StorageClose(Fts5Storage *p);
+static int sqlite3Fts5StorageRename(Fts5Storage*, const char *zName);
+
+static int sqlite3Fts5DropAll(Fts5Config*);
+static int sqlite3Fts5CreateTable(Fts5Config*, const char*, const char*, int, char **);
+
+static int sqlite3Fts5StorageDelete(Fts5Storage *p, i64, sqlite3_value**);
+static int sqlite3Fts5StorageContentInsert(Fts5Storage *p, sqlite3_value**, i64*);
+static int sqlite3Fts5StorageIndexInsert(Fts5Storage *p, sqlite3_value**, i64);
+
+static int sqlite3Fts5StorageIntegrity(Fts5Storage *p);
+
+static int sqlite3Fts5StorageStmt(Fts5Storage *p, int eStmt, sqlite3_stmt**, char**);
+static void sqlite3Fts5StorageStmtRelease(Fts5Storage *p, int eStmt, sqlite3_stmt*);
+
+static int sqlite3Fts5StorageDocsize(Fts5Storage *p, i64 iRowid, int *aCol);
+static int sqlite3Fts5StorageSize(Fts5Storage *p, int iCol, i64 *pnAvg);
+static int sqlite3Fts5StorageRowCount(Fts5Storage *p, i64 *pnRow);
+
+static int sqlite3Fts5StorageSync(Fts5Storage *p, int bCommit);
+static int sqlite3Fts5StorageRollback(Fts5Storage *p);
+
+static int sqlite3Fts5StorageConfigValue(
+ Fts5Storage *p, const char*, sqlite3_value*, int
+);
+
+static int sqlite3Fts5StorageDeleteAll(Fts5Storage *p);
+static int sqlite3Fts5StorageRebuild(Fts5Storage *p);
+static int sqlite3Fts5StorageOptimize(Fts5Storage *p);
+static int sqlite3Fts5StorageMerge(Fts5Storage *p, int nMerge);
+static int sqlite3Fts5StorageReset(Fts5Storage *p);
+
+/*
+** End of interface to code in fts5_storage.c.
+**************************************************************************/
+
+
+/**************************************************************************
+** Interface to code in fts5_expr.c.
+*/
+typedef struct Fts5Expr Fts5Expr;
+typedef struct Fts5ExprNode Fts5ExprNode;
+typedef struct Fts5Parse Fts5Parse;
+typedef struct Fts5Token Fts5Token;
+typedef struct Fts5ExprPhrase Fts5ExprPhrase;
+typedef struct Fts5ExprNearset Fts5ExprNearset;
+
+struct Fts5Token {
+ const char *p; /* Token text (not NULL terminated) */
+ int n; /* Size of buffer p in bytes */
+};
+
+/* Parse a MATCH expression. */
+static int sqlite3Fts5ExprNew(
+ Fts5Config *pConfig,
+ const char *zExpr,
+ Fts5Expr **ppNew,
+ char **pzErr
+);
+
+/*
+** for(rc = sqlite3Fts5ExprFirst(pExpr, pIdx, bDesc);
+** rc==SQLITE_OK && 0==sqlite3Fts5ExprEof(pExpr);
+** rc = sqlite3Fts5ExprNext(pExpr)
+** ){
+** // The document with rowid iRowid matches the expression!
+** i64 iRowid = sqlite3Fts5ExprRowid(pExpr);
+** }
+*/
+static int sqlite3Fts5ExprFirst(Fts5Expr*, Fts5Index *pIdx, i64 iMin, int bDesc);
+static int sqlite3Fts5ExprNext(Fts5Expr*, i64 iMax);
+static int sqlite3Fts5ExprEof(Fts5Expr*);
+static i64 sqlite3Fts5ExprRowid(Fts5Expr*);
+
+static void sqlite3Fts5ExprFree(Fts5Expr*);
+
+/* Called during startup to register a UDF with SQLite */
+static int sqlite3Fts5ExprInit(Fts5Global*, sqlite3*);
+
+static int sqlite3Fts5ExprPhraseCount(Fts5Expr*);
+static int sqlite3Fts5ExprPhraseSize(Fts5Expr*, int iPhrase);
+static int sqlite3Fts5ExprPoslist(Fts5Expr*, int, const u8 **);
+
+typedef struct Fts5PoslistPopulator Fts5PoslistPopulator;
+static Fts5PoslistPopulator *sqlite3Fts5ExprClearPoslists(Fts5Expr*, int);
+static int sqlite3Fts5ExprPopulatePoslists(
+ Fts5Config*, Fts5Expr*, Fts5PoslistPopulator*, int, const char*, int
+);
+static void sqlite3Fts5ExprCheckPoslists(Fts5Expr*, i64);
+static void sqlite3Fts5ExprClearEof(Fts5Expr*);
+
+static int sqlite3Fts5ExprClonePhrase(Fts5Expr*, int, Fts5Expr**);
+
+static int sqlite3Fts5ExprPhraseCollist(Fts5Expr *, int, const u8 **, int *);
+
+/*******************************************
+** The fts5_expr.c API above this point is used by the other hand-written
+** C code in this module. The interfaces below this point are called by
+** the parser code in fts5parse.y. */
+
+static void sqlite3Fts5ParseError(Fts5Parse *pParse, const char *zFmt, ...);
+
+static Fts5ExprNode *sqlite3Fts5ParseNode(
+ Fts5Parse *pParse,
+ int eType,
+ Fts5ExprNode *pLeft,
+ Fts5ExprNode *pRight,
+ Fts5ExprNearset *pNear
+);
+
+static Fts5ExprNode *sqlite3Fts5ParseImplicitAnd(
+ Fts5Parse *pParse,
+ Fts5ExprNode *pLeft,
+ Fts5ExprNode *pRight
+);
+
+static Fts5ExprPhrase *sqlite3Fts5ParseTerm(
+ Fts5Parse *pParse,
+ Fts5ExprPhrase *pPhrase,
+ Fts5Token *pToken,
+ int bPrefix
+);
+
+static Fts5ExprNearset *sqlite3Fts5ParseNearset(
+ Fts5Parse*,
+ Fts5ExprNearset*,
+ Fts5ExprPhrase*
+);
+
+static Fts5Colset *sqlite3Fts5ParseColset(
+ Fts5Parse*,
+ Fts5Colset*,
+ Fts5Token *
+);
+
+static void sqlite3Fts5ParsePhraseFree(Fts5ExprPhrase*);
+static void sqlite3Fts5ParseNearsetFree(Fts5ExprNearset*);
+static void sqlite3Fts5ParseNodeFree(Fts5ExprNode*);
+
+static void sqlite3Fts5ParseSetDistance(Fts5Parse*, Fts5ExprNearset*, Fts5Token*);
+static void sqlite3Fts5ParseSetColset(Fts5Parse*, Fts5ExprNearset*, Fts5Colset*);
+static void sqlite3Fts5ParseFinished(Fts5Parse *pParse, Fts5ExprNode *p);
+static void sqlite3Fts5ParseNear(Fts5Parse *pParse, Fts5Token*);
+
+/*
+** End of interface to code in fts5_expr.c.
+**************************************************************************/
+
+
+
+/**************************************************************************
+** Interface to code in fts5_aux.c.
+*/
+
+static int sqlite3Fts5AuxInit(fts5_api*);
+/*
+** End of interface to code in fts5_aux.c.
+**************************************************************************/
+
+/**************************************************************************
+** Interface to code in fts5_tokenizer.c.
+*/
+
+static int sqlite3Fts5TokenizerInit(fts5_api*);
+/*
+** End of interface to code in fts5_tokenizer.c.
+**************************************************************************/
+
+/**************************************************************************
+** Interface to code in fts5_vocab.c.
+*/
+
+static int sqlite3Fts5VocabInit(Fts5Global*, sqlite3*);
+
+/*
+** End of interface to code in fts5_vocab.c.
+**************************************************************************/
+
+
+/**************************************************************************
+** Interface to automatically generated code in fts5_unicode2.c.
+*/
+static int sqlite3Fts5UnicodeIsalnum(int c);
+static int sqlite3Fts5UnicodeIsdiacritic(int c);
+static int sqlite3Fts5UnicodeFold(int c, int bRemoveDiacritic);
+/*
+** End of interface to code in fts5_unicode2.c.
+**************************************************************************/
+
+#endif
+
+#define FTS5_OR 1
+#define FTS5_AND 2
+#define FTS5_NOT 3
+#define FTS5_TERM 4
+#define FTS5_COLON 5
+#define FTS5_LP 6
+#define FTS5_RP 7
+#define FTS5_LCP 8
+#define FTS5_RCP 9
+#define FTS5_STRING 10
+#define FTS5_COMMA 11
+#define FTS5_PLUS 12
+#define FTS5_STAR 13
+
+/*
+** 2000-05-29
+**
+** 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.
+**
+*************************************************************************
+** Driver template for the LEMON parser generator.
+**
+** The "lemon" program processes an LALR(1) input grammar file, then uses
+** this template to construct a parser. The "lemon" program inserts text
+** at each "%%" line. Also, any "P-a-r-s-e" identifer prefix (without the
+** interstitial "-" characters) contained in this template is changed into
+** the value of the %name directive from the grammar. Otherwise, the content
+** of this template is copied straight through into the generate parser
+** source file.
+**
+** The following is the concatenation of all %include directives from the
+** input grammar file:
+*/
+/* #include <stdio.h> */
+/************ Begin %include sections from the grammar ************************/
+
+/* #include "fts5Int.h" */
+/* #include "fts5parse.h" */
+
+/*
+** Disable all error recovery processing in the parser push-down
+** automaton.
+*/
+#define fts5YYNOERRORRECOVERY 1
+
+/*
+** Make fts5yytestcase() the same as testcase()
+*/
+#define fts5yytestcase(X) testcase(X)
+
+/*
+** Indicate that sqlite3ParserFree() will never be called with a null
+** pointer.
+*/
+#define fts5YYPARSEFREENOTNULL 1
+
+/*
+** Alternative datatype for the argument to the malloc() routine passed
+** into sqlite3ParserAlloc(). The default is size_t.
+*/
+#define fts5YYMALLOCARGTYPE u64
+
+/**************** End of %include directives **********************************/
+/* These constants specify the various numeric values for terminal symbols
+** in a format understandable to "makeheaders". This section is blank unless
+** "lemon" is run with the "-m" command-line option.
+***************** Begin makeheaders token definitions *************************/
+/**************** End makeheaders token definitions ***************************/
+
+/* The next sections is a series of control #defines.
+** various aspects of the generated parser.
+** fts5YYCODETYPE is the data type used to store the integer codes
+** that represent terminal and non-terminal symbols.
+** "unsigned char" is used if there are fewer than
+** 256 symbols. Larger types otherwise.
+** fts5YYNOCODE is a number of type fts5YYCODETYPE that is not used for
+** any terminal or nonterminal symbol.
+** fts5YYFALLBACK If defined, this indicates that one or more tokens
+** (also known as: "terminal symbols") have fall-back
+** values which should be used if the original symbol
+** would not parse. This permits keywords to sometimes
+** be used as identifiers, for example.
+** fts5YYACTIONTYPE is the data type used for "action codes" - numbers
+** that indicate what to do in response to the next
+** token.
+** sqlite3Fts5ParserFTS5TOKENTYPE is the data type used for minor type for terminal
+** symbols. Background: A "minor type" is a semantic
+** value associated with a terminal or non-terminal
+** symbols. For example, for an "ID" terminal symbol,
+** the minor type might be the name of the identifier.
+** Each non-terminal can have a different minor type.
+** Terminal symbols all have the same minor type, though.
+** This macros defines the minor type for terminal
+** symbols.
+** fts5YYMINORTYPE is the data type used for all minor types.
+** This is typically a union of many types, one of
+** which is sqlite3Fts5ParserFTS5TOKENTYPE. The entry in the union
+** for terminal symbols is called "fts5yy0".
+** fts5YYSTACKDEPTH is the maximum depth of the parser's stack. If
+** zero the stack is dynamically sized using realloc()
+** sqlite3Fts5ParserARG_SDECL A static variable declaration for the %extra_argument
+** sqlite3Fts5ParserARG_PDECL A parameter declaration for the %extra_argument
+** sqlite3Fts5ParserARG_STORE Code to store %extra_argument into fts5yypParser
+** sqlite3Fts5ParserARG_FETCH Code to extract %extra_argument from fts5yypParser
+** fts5YYERRORSYMBOL is the code number of the error symbol. If not
+** defined, then do no error processing.
+** fts5YYNSTATE the combined number of states.
+** fts5YYNRULE the number of rules in the grammar
+** fts5YY_MAX_SHIFT Maximum value for shift actions
+** fts5YY_MIN_SHIFTREDUCE Minimum value for shift-reduce actions
+** fts5YY_MAX_SHIFTREDUCE Maximum value for shift-reduce actions
+** fts5YY_MIN_REDUCE Maximum value for reduce actions
+** fts5YY_ERROR_ACTION The fts5yy_action[] code for syntax error
+** fts5YY_ACCEPT_ACTION The fts5yy_action[] code for accept
+** fts5YY_NO_ACTION The fts5yy_action[] code for no-op
+*/
+#ifndef INTERFACE
+# define INTERFACE 1
+#endif
+/************* Begin control #defines *****************************************/
+#define fts5YYCODETYPE unsigned char
+#define fts5YYNOCODE 27
+#define fts5YYACTIONTYPE unsigned char
+#define sqlite3Fts5ParserFTS5TOKENTYPE Fts5Token
+typedef union {
+ int fts5yyinit;
+ sqlite3Fts5ParserFTS5TOKENTYPE fts5yy0;
+ Fts5Colset* fts5yy3;
+ Fts5ExprPhrase* fts5yy11;
+ Fts5ExprNode* fts5yy18;
+ int fts5yy20;
+ Fts5ExprNearset* fts5yy26;
+} fts5YYMINORTYPE;
+#ifndef fts5YYSTACKDEPTH
+#define fts5YYSTACKDEPTH 100
+#endif
+#define sqlite3Fts5ParserARG_SDECL Fts5Parse *pParse;
+#define sqlite3Fts5ParserARG_PDECL ,Fts5Parse *pParse
+#define sqlite3Fts5ParserARG_FETCH Fts5Parse *pParse = fts5yypParser->pParse
+#define sqlite3Fts5ParserARG_STORE fts5yypParser->pParse = pParse
+#define fts5YYNSTATE 26
+#define fts5YYNRULE 24
+#define fts5YY_MAX_SHIFT 25
+#define fts5YY_MIN_SHIFTREDUCE 40
+#define fts5YY_MAX_SHIFTREDUCE 63
+#define fts5YY_MIN_REDUCE 64
+#define fts5YY_MAX_REDUCE 87
+#define fts5YY_ERROR_ACTION 88
+#define fts5YY_ACCEPT_ACTION 89
+#define fts5YY_NO_ACTION 90
+/************* End control #defines *******************************************/
+
+/* Define the fts5yytestcase() macro to be a no-op if is not already defined
+** otherwise.
+**
+** Applications can choose to define fts5yytestcase() in the %include section
+** to a macro that can assist in verifying code coverage. For production
+** code the fts5yytestcase() macro should be turned off. But it is useful
+** for testing.
+*/
+#ifndef fts5yytestcase
+# define fts5yytestcase(X)
+#endif
+
+
+/* Next are the tables used to determine what action to take based on the
+** current state and lookahead token. These tables are used to implement
+** functions that take a state number and lookahead value and return an
+** action integer.
+**
+** Suppose the action integer is N. Then the action is determined as
+** follows
+**
+** 0 <= N <= fts5YY_MAX_SHIFT Shift N. That is, push the lookahead
+** token onto the stack and goto state N.
+**
+** N between fts5YY_MIN_SHIFTREDUCE Shift to an arbitrary state then
+** and fts5YY_MAX_SHIFTREDUCE reduce by rule N-fts5YY_MIN_SHIFTREDUCE.
+**
+** N between fts5YY_MIN_REDUCE Reduce by rule N-fts5YY_MIN_REDUCE
+** and fts5YY_MAX_REDUCE
+
+** N == fts5YY_ERROR_ACTION A syntax error has occurred.
+**
+** N == fts5YY_ACCEPT_ACTION The parser accepts its input.
+**
+** N == fts5YY_NO_ACTION No such action. Denotes unused
+** slots in the fts5yy_action[] table.
+**
+** The action table is constructed as a single large table named fts5yy_action[].
+** Given state S and lookahead X, the action is computed as
+**
+** fts5yy_action[ fts5yy_shift_ofst[S] + X ]
+**
+** If the index value fts5yy_shift_ofst[S]+X is out of range or if the value
+** fts5yy_lookahead[fts5yy_shift_ofst[S]+X] is not equal to X or if fts5yy_shift_ofst[S]
+** is equal to fts5YY_SHIFT_USE_DFLT, it means that the action is not in the table
+** and that fts5yy_default[S] should be used instead.
+**
+** The formula above is for computing the action when the lookahead is
+** a terminal symbol. If the lookahead is a non-terminal (as occurs after
+** a reduce action) then the fts5yy_reduce_ofst[] array is used in place of
+** the fts5yy_shift_ofst[] array and fts5YY_REDUCE_USE_DFLT is used in place of
+** fts5YY_SHIFT_USE_DFLT.
+**
+** The following are the tables generated in this section:
+**
+** fts5yy_action[] A single table containing all actions.
+** fts5yy_lookahead[] A table containing the lookahead for each entry in
+** fts5yy_action. Used to detect hash collisions.
+** fts5yy_shift_ofst[] For each state, the offset into fts5yy_action for
+** shifting terminals.
+** fts5yy_reduce_ofst[] For each state, the offset into fts5yy_action for
+** shifting non-terminals after a reduce.
+** fts5yy_default[] Default action for each state.
+**
+*********** Begin parsing tables **********************************************/
+#define fts5YY_ACTTAB_COUNT (78)
+static const fts5YYACTIONTYPE fts5yy_action[] = {
+ /* 0 */ 89, 15, 46, 5, 48, 24, 12, 19, 23, 14,
+ /* 10 */ 46, 5, 48, 24, 20, 21, 23, 43, 46, 5,
+ /* 20 */ 48, 24, 6, 18, 23, 17, 46, 5, 48, 24,
+ /* 30 */ 75, 7, 23, 25, 46, 5, 48, 24, 62, 47,
+ /* 40 */ 23, 48, 24, 7, 11, 23, 9, 3, 4, 2,
+ /* 50 */ 62, 50, 52, 44, 64, 3, 4, 2, 49, 4,
+ /* 60 */ 2, 1, 23, 11, 16, 9, 12, 2, 10, 61,
+ /* 70 */ 53, 59, 62, 60, 22, 13, 55, 8,
+};
+static const fts5YYCODETYPE fts5yy_lookahead[] = {
+ /* 0 */ 15, 16, 17, 18, 19, 20, 10, 11, 23, 16,
+ /* 10 */ 17, 18, 19, 20, 23, 24, 23, 16, 17, 18,
+ /* 20 */ 19, 20, 22, 23, 23, 16, 17, 18, 19, 20,
+ /* 30 */ 5, 6, 23, 16, 17, 18, 19, 20, 13, 17,
+ /* 40 */ 23, 19, 20, 6, 8, 23, 10, 1, 2, 3,
+ /* 50 */ 13, 9, 10, 7, 0, 1, 2, 3, 19, 2,
+ /* 60 */ 3, 6, 23, 8, 21, 10, 10, 3, 10, 25,
+ /* 70 */ 10, 10, 13, 25, 12, 10, 7, 5,
+};
+#define fts5YY_SHIFT_USE_DFLT (-5)
+#define fts5YY_SHIFT_COUNT (25)
+#define fts5YY_SHIFT_MIN (-4)
+#define fts5YY_SHIFT_MAX (72)
+static const signed char fts5yy_shift_ofst[] = {
+ /* 0 */ 55, 55, 55, 55, 55, 36, -4, 56, 58, 25,
+ /* 10 */ 37, 60, 59, 59, 46, 54, 42, 57, 62, 61,
+ /* 20 */ 62, 69, 65, 62, 72, 64,
+};
+#define fts5YY_REDUCE_USE_DFLT (-16)
+#define fts5YY_REDUCE_COUNT (13)
+#define fts5YY_REDUCE_MIN (-15)
+#define fts5YY_REDUCE_MAX (48)
+static const signed char fts5yy_reduce_ofst[] = {
+ /* 0 */ -15, -7, 1, 9, 17, 22, -9, 0, 39, 44,
+ /* 10 */ 44, 43, 44, 48,
+};
+static const fts5YYACTIONTYPE fts5yy_default[] = {
+ /* 0 */ 88, 88, 88, 88, 88, 69, 82, 88, 88, 87,
+ /* 10 */ 87, 88, 87, 87, 88, 88, 88, 66, 80, 88,
+ /* 20 */ 81, 88, 88, 78, 88, 65,
+};
+/********** End of lemon-generated parsing tables *****************************/
+
+/* The next table maps tokens (terminal symbols) into fallback tokens.
+** If a construct like the following:
+**
+** %fallback ID X Y Z.
+**
+** appears in the grammar, then ID becomes a fallback token for X, Y,
+** and Z. Whenever one of the tokens X, Y, or Z is input to the parser
+** but it does not parse, the type of the token is changed to ID and
+** the parse is retried before an error is thrown.
+**
+** This feature can be used, for example, to cause some keywords in a language
+** to revert to identifiers if they keyword does not apply in the context where
+** it appears.
+*/
+#ifdef fts5YYFALLBACK
+static const fts5YYCODETYPE fts5yyFallback[] = {
+};
+#endif /* fts5YYFALLBACK */
+
+/* The following structure represents a single element of the
+** parser's stack. Information stored includes:
+**
+** + The state number for the parser at this level of the stack.
+**
+** + The value of the token stored at this level of the stack.
+** (In other words, the "major" token.)
+**
+** + The semantic value stored at this level of the stack. This is
+** the information used by the action routines in the grammar.
+** It is sometimes called the "minor" token.
+**
+** After the "shift" half of a SHIFTREDUCE action, the stateno field
+** actually contains the reduce action for the second half of the
+** SHIFTREDUCE.
+*/
+struct fts5yyStackEntry {
+ fts5YYACTIONTYPE stateno; /* The state-number, or reduce action in SHIFTREDUCE */
+ fts5YYCODETYPE major; /* The major token value. This is the code
+ ** number for the token at this stack level */
+ fts5YYMINORTYPE minor; /* The user-supplied minor token value. This
+ ** is the value of the token */
+};
+typedef struct fts5yyStackEntry fts5yyStackEntry;
+
+/* The state of the parser is completely contained in an instance of
+** the following structure */
+struct fts5yyParser {
+ int fts5yyidx; /* Index of top element in stack */
+#ifdef fts5YYTRACKMAXSTACKDEPTH
+ int fts5yyidxMax; /* Maximum value of fts5yyidx */
+#endif
+#ifndef fts5YYNOERRORRECOVERY
+ int fts5yyerrcnt; /* Shifts left before out of the error */
+#endif
+ sqlite3Fts5ParserARG_SDECL /* A place to hold %extra_argument */
+#if fts5YYSTACKDEPTH<=0
+ int fts5yystksz; /* Current side of the stack */
+ fts5yyStackEntry *fts5yystack; /* The parser's stack */
+#else
+ fts5yyStackEntry fts5yystack[fts5YYSTACKDEPTH]; /* The parser's stack */
+#endif
+};
+typedef struct fts5yyParser fts5yyParser;
+
+#ifndef NDEBUG
+/* #include <stdio.h> */
+static FILE *fts5yyTraceFILE = 0;
+static char *fts5yyTracePrompt = 0;
+#endif /* NDEBUG */
+
+#ifndef NDEBUG
+/*
+** Turn parser tracing on by giving a stream to which to write the trace
+** and a prompt to preface each trace message. Tracing is turned off
+** by making either argument NULL
+**
+** Inputs:
+** <ul>
+** <li> A FILE* to which trace output should be written.
+** If NULL, then tracing is turned off.
+** <li> A prefix string written at the beginning of every
+** line of trace output. If NULL, then tracing is
+** turned off.
+** </ul>
+**
+** Outputs:
+** None.
+*/
+static void sqlite3Fts5ParserTrace(FILE *TraceFILE, char *zTracePrompt){
+ fts5yyTraceFILE = TraceFILE;
+ fts5yyTracePrompt = zTracePrompt;
+ if( fts5yyTraceFILE==0 ) fts5yyTracePrompt = 0;
+ else if( fts5yyTracePrompt==0 ) fts5yyTraceFILE = 0;
+}
+#endif /* NDEBUG */
+
+#ifndef NDEBUG
+/* For tracing shifts, the names of all terminals and nonterminals
+** are required. The following table supplies these names */
+static const char *const fts5yyTokenName[] = {
+ "$", "OR", "AND", "NOT",
+ "TERM", "COLON", "LP", "RP",
+ "LCP", "RCP", "STRING", "COMMA",
+ "PLUS", "STAR", "error", "input",
+ "expr", "cnearset", "exprlist", "nearset",
+ "colset", "colsetlist", "nearphrases", "phrase",
+ "neardist_opt", "star_opt",
+};
+#endif /* NDEBUG */
+
+#ifndef NDEBUG
+/* For tracing reduce actions, the names of all rules are required.
+*/
+static const char *const fts5yyRuleName[] = {
+ /* 0 */ "input ::= expr",
+ /* 1 */ "expr ::= expr AND expr",
+ /* 2 */ "expr ::= expr OR expr",
+ /* 3 */ "expr ::= expr NOT expr",
+ /* 4 */ "expr ::= LP expr RP",
+ /* 5 */ "expr ::= exprlist",
+ /* 6 */ "exprlist ::= cnearset",
+ /* 7 */ "exprlist ::= exprlist cnearset",
+ /* 8 */ "cnearset ::= nearset",
+ /* 9 */ "cnearset ::= colset COLON nearset",
+ /* 10 */ "colset ::= LCP colsetlist RCP",
+ /* 11 */ "colset ::= STRING",
+ /* 12 */ "colsetlist ::= colsetlist STRING",
+ /* 13 */ "colsetlist ::= STRING",
+ /* 14 */ "nearset ::= phrase",
+ /* 15 */ "nearset ::= STRING LP nearphrases neardist_opt RP",
+ /* 16 */ "nearphrases ::= phrase",
+ /* 17 */ "nearphrases ::= nearphrases phrase",
+ /* 18 */ "neardist_opt ::=",
+ /* 19 */ "neardist_opt ::= COMMA STRING",
+ /* 20 */ "phrase ::= phrase PLUS STRING star_opt",
+ /* 21 */ "phrase ::= STRING star_opt",
+ /* 22 */ "star_opt ::= STAR",
+ /* 23 */ "star_opt ::=",
+};
+#endif /* NDEBUG */
+
+
+#if fts5YYSTACKDEPTH<=0
+/*
+** Try to increase the size of the parser stack.
+*/
+static void fts5yyGrowStack(fts5yyParser *p){
+ int newSize;
+ fts5yyStackEntry *pNew;
+
+ newSize = p->fts5yystksz*2 + 100;
+ pNew = realloc(p->fts5yystack, newSize*sizeof(pNew[0]));
+ if( pNew ){
+ p->fts5yystack = pNew;
+ p->fts5yystksz = newSize;
+#ifndef NDEBUG
+ if( fts5yyTraceFILE ){
+ fprintf(fts5yyTraceFILE,"%sStack grows to %d entries!\n",
+ fts5yyTracePrompt, p->fts5yystksz);
+ }
+#endif
+ }
+}
+#endif
+
+/* Datatype of the argument to the memory allocated passed as the
+** second argument to sqlite3Fts5ParserAlloc() below. This can be changed by
+** putting an appropriate #define in the %include section of the input
+** grammar.
+*/
+#ifndef fts5YYMALLOCARGTYPE
+# define fts5YYMALLOCARGTYPE size_t
+#endif
+
+/*
+** This function allocates a new parser.
+** The only argument is a pointer to a function which works like
+** malloc.
+**
+** Inputs:
+** A pointer to the function used to allocate memory.
+**
+** Outputs:
+** A pointer to a parser. This pointer is used in subsequent calls
+** to sqlite3Fts5Parser and sqlite3Fts5ParserFree.
+*/
+static void *sqlite3Fts5ParserAlloc(void *(*mallocProc)(fts5YYMALLOCARGTYPE)){
+ fts5yyParser *pParser;
+ pParser = (fts5yyParser*)(*mallocProc)( (fts5YYMALLOCARGTYPE)sizeof(fts5yyParser) );
+ if( pParser ){
+ pParser->fts5yyidx = -1;
+#ifdef fts5YYTRACKMAXSTACKDEPTH
+ pParser->fts5yyidxMax = 0;
+#endif
+#if fts5YYSTACKDEPTH<=0
+ pParser->fts5yystack = NULL;
+ pParser->fts5yystksz = 0;
+ fts5yyGrowStack(pParser);
+#endif
+ }
+ return pParser;
+}
+
+/* The following function deletes the "minor type" or semantic value
+** associated with a symbol. The symbol can be either a terminal
+** or nonterminal. "fts5yymajor" is the symbol code, and "fts5yypminor" is
+** a pointer to the value to be deleted. The code used to do the
+** deletions is derived from the %destructor and/or %token_destructor
+** directives of the input grammar.
+*/
+static void fts5yy_destructor(
+ fts5yyParser *fts5yypParser, /* The parser */
+ fts5YYCODETYPE fts5yymajor, /* Type code for object to destroy */
+ fts5YYMINORTYPE *fts5yypminor /* The object to be destroyed */
+){
+ sqlite3Fts5ParserARG_FETCH;
+ switch( fts5yymajor ){
+ /* Here is inserted the actions which take place when a
+ ** terminal or non-terminal is destroyed. This can happen
+ ** when the symbol is popped from the stack during a
+ ** reduce or during error processing or when a parser is
+ ** being destroyed before it is finished parsing.
+ **
+ ** Note: during a reduce, the only symbols destroyed are those
+ ** which appear on the RHS of the rule, but which are *not* used
+ ** inside the C code.
+ */
+/********* Begin destructor definitions ***************************************/
+ case 15: /* input */
+{
+ (void)pParse;
+}
+ break;
+ case 16: /* expr */
+ case 17: /* cnearset */
+ case 18: /* exprlist */
+{
+ sqlite3Fts5ParseNodeFree((fts5yypminor->fts5yy18));
+}
+ break;
+ case 19: /* nearset */
+ case 22: /* nearphrases */
+{
+ sqlite3Fts5ParseNearsetFree((fts5yypminor->fts5yy26));
+}
+ break;
+ case 20: /* colset */
+ case 21: /* colsetlist */
+{
+ sqlite3_free((fts5yypminor->fts5yy3));
+}
+ break;
+ case 23: /* phrase */
+{
+ sqlite3Fts5ParsePhraseFree((fts5yypminor->fts5yy11));
+}
+ break;
+/********* End destructor definitions *****************************************/
+ default: break; /* If no destructor action specified: do nothing */
+ }
+}
+
+/*
+** Pop the parser's stack once.
+**
+** If there is a destructor routine associated with the token which
+** is popped from the stack, then call it.
+*/
+static void fts5yy_pop_parser_stack(fts5yyParser *pParser){
+ fts5yyStackEntry *fts5yytos;
+ assert( pParser->fts5yyidx>=0 );
+ fts5yytos = &pParser->fts5yystack[pParser->fts5yyidx--];
+#ifndef NDEBUG
+ if( fts5yyTraceFILE ){
+ fprintf(fts5yyTraceFILE,"%sPopping %s\n",
+ fts5yyTracePrompt,
+ fts5yyTokenName[fts5yytos->major]);
+ }
+#endif
+ fts5yy_destructor(pParser, fts5yytos->major, &fts5yytos->minor);
+}
+
+/*
+** Deallocate and destroy a parser. Destructors are called for
+** all stack elements before shutting the parser down.
+**
+** If the fts5YYPARSEFREENEVERNULL macro exists (for example because it
+** is defined in a %include section of the input grammar) then it is
+** assumed that the input pointer is never NULL.
+*/
+static void sqlite3Fts5ParserFree(
+ void *p, /* The parser to be deleted */
+ void (*freeProc)(void*) /* Function used to reclaim memory */
+){
+ fts5yyParser *pParser = (fts5yyParser*)p;
+#ifndef fts5YYPARSEFREENEVERNULL
+ if( pParser==0 ) return;
+#endif
+ while( pParser->fts5yyidx>=0 ) fts5yy_pop_parser_stack(pParser);
+#if fts5YYSTACKDEPTH<=0
+ free(pParser->fts5yystack);
+#endif
+ (*freeProc)((void*)pParser);
+}
+
+/*
+** Return the peak depth of the stack for a parser.
+*/
+#ifdef fts5YYTRACKMAXSTACKDEPTH
+static int sqlite3Fts5ParserStackPeak(void *p){
+ fts5yyParser *pParser = (fts5yyParser*)p;
+ return pParser->fts5yyidxMax;
+}
+#endif
+
+/*
+** Find the appropriate action for a parser given the terminal
+** look-ahead token iLookAhead.
+*/
+static unsigned int fts5yy_find_shift_action(
+ fts5yyParser *pParser, /* The parser */
+ fts5YYCODETYPE iLookAhead /* The look-ahead token */
+){
+ int i;
+ int stateno = pParser->fts5yystack[pParser->fts5yyidx].stateno;
+
+ if( stateno>=fts5YY_MIN_REDUCE ) return stateno;
+ assert( stateno <= fts5YY_SHIFT_COUNT );
+ do{
+ i = fts5yy_shift_ofst[stateno];
+ if( i==fts5YY_SHIFT_USE_DFLT ) return fts5yy_default[stateno];
+ assert( iLookAhead!=fts5YYNOCODE );
+ i += iLookAhead;
+ if( i<0 || i>=fts5YY_ACTTAB_COUNT || fts5yy_lookahead[i]!=iLookAhead ){
+ if( iLookAhead>0 ){
+#ifdef fts5YYFALLBACK
+ fts5YYCODETYPE iFallback; /* Fallback token */
+ if( iLookAhead<sizeof(fts5yyFallback)/sizeof(fts5yyFallback[0])
+ && (iFallback = fts5yyFallback[iLookAhead])!=0 ){
+#ifndef NDEBUG
+ if( fts5yyTraceFILE ){
+ fprintf(fts5yyTraceFILE, "%sFALLBACK %s => %s\n",
+ fts5yyTracePrompt, fts5yyTokenName[iLookAhead], fts5yyTokenName[iFallback]);
+ }
+#endif
+ assert( fts5yyFallback[iFallback]==0 ); /* Fallback loop must terminate */
+ iLookAhead = iFallback;
+ continue;
+ }
+#endif
+#ifdef fts5YYWILDCARD
+ {
+ int j = i - iLookAhead + fts5YYWILDCARD;
+ if(
+#if fts5YY_SHIFT_MIN+fts5YYWILDCARD<0
+ j>=0 &&
+#endif
+#if fts5YY_SHIFT_MAX+fts5YYWILDCARD>=fts5YY_ACTTAB_COUNT
+ j<fts5YY_ACTTAB_COUNT &&
+#endif
+ fts5yy_lookahead[j]==fts5YYWILDCARD
+ ){
+#ifndef NDEBUG
+ if( fts5yyTraceFILE ){
+ fprintf(fts5yyTraceFILE, "%sWILDCARD %s => %s\n",
+ fts5yyTracePrompt, fts5yyTokenName[iLookAhead],
+ fts5yyTokenName[fts5YYWILDCARD]);
+ }
+#endif /* NDEBUG */
+ return fts5yy_action[j];
+ }
+ }
+#endif /* fts5YYWILDCARD */
+ }
+ return fts5yy_default[stateno];
+ }else{
+ return fts5yy_action[i];
+ }
+ }while(1);
+}
+
+/*
+** Find the appropriate action for a parser given the non-terminal
+** look-ahead token iLookAhead.
+*/
+static int fts5yy_find_reduce_action(
+ int stateno, /* Current state number */
+ fts5YYCODETYPE iLookAhead /* The look-ahead token */
+){
+ int i;
+#ifdef fts5YYERRORSYMBOL
+ if( stateno>fts5YY_REDUCE_COUNT ){
+ return fts5yy_default[stateno];
+ }
+#else
+ assert( stateno<=fts5YY_REDUCE_COUNT );
+#endif
+ i = fts5yy_reduce_ofst[stateno];
+ assert( i!=fts5YY_REDUCE_USE_DFLT );
+ assert( iLookAhead!=fts5YYNOCODE );
+ i += iLookAhead;
+#ifdef fts5YYERRORSYMBOL
+ if( i<0 || i>=fts5YY_ACTTAB_COUNT || fts5yy_lookahead[i]!=iLookAhead ){
+ return fts5yy_default[stateno];
+ }
+#else
+ assert( i>=0 && i<fts5YY_ACTTAB_COUNT );
+ assert( fts5yy_lookahead[i]==iLookAhead );
+#endif
+ return fts5yy_action[i];
+}
+
+/*
+** The following routine is called if the stack overflows.
+*/
+static void fts5yyStackOverflow(fts5yyParser *fts5yypParser){
+ sqlite3Fts5ParserARG_FETCH;
+ fts5yypParser->fts5yyidx--;
+#ifndef NDEBUG
+ if( fts5yyTraceFILE ){
+ fprintf(fts5yyTraceFILE,"%sStack Overflow!\n",fts5yyTracePrompt);
+ }
+#endif
+ while( fts5yypParser->fts5yyidx>=0 ) fts5yy_pop_parser_stack(fts5yypParser);
+ /* Here code is inserted which will execute if the parser
+ ** stack every overflows */
+/******** Begin %stack_overflow code ******************************************/
+
+ sqlite3Fts5ParseError(pParse, "fts5: parser stack overflow");
+/******** End %stack_overflow code ********************************************/
+ sqlite3Fts5ParserARG_STORE; /* Suppress warning about unused %extra_argument var */
+}
+
+/*
+** Print tracing information for a SHIFT action
+*/
+#ifndef NDEBUG
+static void fts5yyTraceShift(fts5yyParser *fts5yypParser, int fts5yyNewState){
+ if( fts5yyTraceFILE ){
+ if( fts5yyNewState<fts5YYNSTATE ){
+ fprintf(fts5yyTraceFILE,"%sShift '%s', go to state %d\n",
+ fts5yyTracePrompt,fts5yyTokenName[fts5yypParser->fts5yystack[fts5yypParser->fts5yyidx].major],
+ fts5yyNewState);
+ }else{
+ fprintf(fts5yyTraceFILE,"%sShift '%s'\n",
+ fts5yyTracePrompt,fts5yyTokenName[fts5yypParser->fts5yystack[fts5yypParser->fts5yyidx].major]);
+ }
+ }
+}
+#else
+# define fts5yyTraceShift(X,Y)
+#endif
+
+/*
+** Perform a shift action.
+*/
+static void fts5yy_shift(
+ fts5yyParser *fts5yypParser, /* The parser to be shifted */
+ int fts5yyNewState, /* The new state to shift in */
+ int fts5yyMajor, /* The major token to shift in */
+ sqlite3Fts5ParserFTS5TOKENTYPE fts5yyMinor /* The minor token to shift in */
+){
+ fts5yyStackEntry *fts5yytos;
+ fts5yypParser->fts5yyidx++;
+#ifdef fts5YYTRACKMAXSTACKDEPTH
+ if( fts5yypParser->fts5yyidx>fts5yypParser->fts5yyidxMax ){
+ fts5yypParser->fts5yyidxMax = fts5yypParser->fts5yyidx;
+ }
+#endif
+#if fts5YYSTACKDEPTH>0
+ if( fts5yypParser->fts5yyidx>=fts5YYSTACKDEPTH ){
+ fts5yyStackOverflow(fts5yypParser);
+ return;
+ }
+#else
+ if( fts5yypParser->fts5yyidx>=fts5yypParser->fts5yystksz ){
+ fts5yyGrowStack(fts5yypParser);
+ if( fts5yypParser->fts5yyidx>=fts5yypParser->fts5yystksz ){
+ fts5yyStackOverflow(fts5yypParser);
+ return;
+ }
+ }
+#endif
+ fts5yytos = &fts5yypParser->fts5yystack[fts5yypParser->fts5yyidx];
+ fts5yytos->stateno = (fts5YYACTIONTYPE)fts5yyNewState;
+ fts5yytos->major = (fts5YYCODETYPE)fts5yyMajor;
+ fts5yytos->minor.fts5yy0 = fts5yyMinor;
+ fts5yyTraceShift(fts5yypParser, fts5yyNewState);
+}
+
+/* The following table contains information about every rule that
+** is used during the reduce.
+*/
+static const struct {
+ fts5YYCODETYPE lhs; /* Symbol on the left-hand side of the rule */
+ unsigned char nrhs; /* Number of right-hand side symbols in the rule */
+} fts5yyRuleInfo[] = {
+ { 15, 1 },
+ { 16, 3 },
+ { 16, 3 },
+ { 16, 3 },
+ { 16, 3 },
+ { 16, 1 },
+ { 18, 1 },
+ { 18, 2 },
+ { 17, 1 },
+ { 17, 3 },
+ { 20, 3 },
+ { 20, 1 },
+ { 21, 2 },
+ { 21, 1 },
+ { 19, 1 },
+ { 19, 5 },
+ { 22, 1 },
+ { 22, 2 },
+ { 24, 0 },
+ { 24, 2 },
+ { 23, 4 },
+ { 23, 2 },
+ { 25, 1 },
+ { 25, 0 },
+};
+
+static void fts5yy_accept(fts5yyParser*); /* Forward Declaration */
+
+/*
+** Perform a reduce action and the shift that must immediately
+** follow the reduce.
+*/
+static void fts5yy_reduce(
+ fts5yyParser *fts5yypParser, /* The parser */
+ unsigned int fts5yyruleno /* Number of the rule by which to reduce */
+){
+ int fts5yygoto; /* The next state */
+ int fts5yyact; /* The next action */
+ fts5yyStackEntry *fts5yymsp; /* The top of the parser's stack */
+ int fts5yysize; /* Amount to pop the stack */
+ sqlite3Fts5ParserARG_FETCH;
+ fts5yymsp = &fts5yypParser->fts5yystack[fts5yypParser->fts5yyidx];
+#ifndef NDEBUG
+ if( fts5yyTraceFILE && fts5yyruleno<(int)(sizeof(fts5yyRuleName)/sizeof(fts5yyRuleName[0])) ){
+ fts5yysize = fts5yyRuleInfo[fts5yyruleno].nrhs;
+ fprintf(fts5yyTraceFILE, "%sReduce [%s], go to state %d.\n", fts5yyTracePrompt,
+ fts5yyRuleName[fts5yyruleno], fts5yymsp[-fts5yysize].stateno);
+ }
+#endif /* NDEBUG */
+
+ /* Check that the stack is large enough to grow by a single entry
+ ** if the RHS of the rule is empty. This ensures that there is room
+ ** enough on the stack to push the LHS value */
+ if( fts5yyRuleInfo[fts5yyruleno].nrhs==0 ){
+#ifdef fts5YYTRACKMAXSTACKDEPTH
+ if( fts5yypParser->fts5yyidx>fts5yypParser->fts5yyidxMax ){
+ fts5yypParser->fts5yyidxMax = fts5yypParser->fts5yyidx;
+ }
+#endif
+#if fts5YYSTACKDEPTH>0
+ if( fts5yypParser->fts5yyidx>=fts5YYSTACKDEPTH-1 ){
+ fts5yyStackOverflow(fts5yypParser);
+ return;
+ }
+#else
+ if( fts5yypParser->fts5yyidx>=fts5yypParser->fts5yystksz-1 ){
+ fts5yyGrowStack(fts5yypParser);
+ if( fts5yypParser->fts5yyidx>=fts5yypParser->fts5yystksz-1 ){
+ fts5yyStackOverflow(fts5yypParser);
+ return;
+ }
+ }
+#endif
+ }
+
+ switch( fts5yyruleno ){
+ /* Beginning here are the reduction cases. A typical example
+ ** follows:
+ ** case 0:
+ ** #line <lineno> <grammarfile>
+ ** { ... } // User supplied code
+ ** #line <lineno> <thisfile>
+ ** break;
+ */
+/********** Begin reduce actions **********************************************/
+ fts5YYMINORTYPE fts5yylhsminor;
+ case 0: /* input ::= expr */
+{ sqlite3Fts5ParseFinished(pParse, fts5yymsp[0].minor.fts5yy18); }
+ break;
+ case 1: /* expr ::= expr AND expr */
+{
+ fts5yylhsminor.fts5yy18 = sqlite3Fts5ParseNode(pParse, FTS5_AND, fts5yymsp[-2].minor.fts5yy18, fts5yymsp[0].minor.fts5yy18, 0);
+}
+ fts5yymsp[-2].minor.fts5yy18 = fts5yylhsminor.fts5yy18;
+ break;
+ case 2: /* expr ::= expr OR expr */
+{
+ fts5yylhsminor.fts5yy18 = sqlite3Fts5ParseNode(pParse, FTS5_OR, fts5yymsp[-2].minor.fts5yy18, fts5yymsp[0].minor.fts5yy18, 0);
+}
+ fts5yymsp[-2].minor.fts5yy18 = fts5yylhsminor.fts5yy18;
+ break;
+ case 3: /* expr ::= expr NOT expr */
+{
+ fts5yylhsminor.fts5yy18 = sqlite3Fts5ParseNode(pParse, FTS5_NOT, fts5yymsp[-2].minor.fts5yy18, fts5yymsp[0].minor.fts5yy18, 0);
+}
+ fts5yymsp[-2].minor.fts5yy18 = fts5yylhsminor.fts5yy18;
+ break;
+ case 4: /* expr ::= LP expr RP */
+{fts5yymsp[-2].minor.fts5yy18 = fts5yymsp[-1].minor.fts5yy18;}
+ break;
+ case 5: /* expr ::= exprlist */
+ case 6: /* exprlist ::= cnearset */ fts5yytestcase(fts5yyruleno==6);
+{fts5yylhsminor.fts5yy18 = fts5yymsp[0].minor.fts5yy18;}
+ fts5yymsp[0].minor.fts5yy18 = fts5yylhsminor.fts5yy18;
+ break;
+ case 7: /* exprlist ::= exprlist cnearset */
+{
+ fts5yylhsminor.fts5yy18 = sqlite3Fts5ParseImplicitAnd(pParse, fts5yymsp[-1].minor.fts5yy18, fts5yymsp[0].minor.fts5yy18);
+}
+ fts5yymsp[-1].minor.fts5yy18 = fts5yylhsminor.fts5yy18;
+ break;
+ case 8: /* cnearset ::= nearset */
+{
+ fts5yylhsminor.fts5yy18 = sqlite3Fts5ParseNode(pParse, FTS5_STRING, 0, 0, fts5yymsp[0].minor.fts5yy26);
+}
+ fts5yymsp[0].minor.fts5yy18 = fts5yylhsminor.fts5yy18;
+ break;
+ case 9: /* cnearset ::= colset COLON nearset */
+{
+ sqlite3Fts5ParseSetColset(pParse, fts5yymsp[0].minor.fts5yy26, fts5yymsp[-2].minor.fts5yy3);
+ fts5yylhsminor.fts5yy18 = sqlite3Fts5ParseNode(pParse, FTS5_STRING, 0, 0, fts5yymsp[0].minor.fts5yy26);
+}
+ fts5yymsp[-2].minor.fts5yy18 = fts5yylhsminor.fts5yy18;
+ break;
+ case 10: /* colset ::= LCP colsetlist RCP */
+{ fts5yymsp[-2].minor.fts5yy3 = fts5yymsp[-1].minor.fts5yy3; }
+ break;
+ case 11: /* colset ::= STRING */
+{
+ fts5yylhsminor.fts5yy3 = sqlite3Fts5ParseColset(pParse, 0, &fts5yymsp[0].minor.fts5yy0);
+}
+ fts5yymsp[0].minor.fts5yy3 = fts5yylhsminor.fts5yy3;
+ break;
+ case 12: /* colsetlist ::= colsetlist STRING */
+{
+ fts5yylhsminor.fts5yy3 = sqlite3Fts5ParseColset(pParse, fts5yymsp[-1].minor.fts5yy3, &fts5yymsp[0].minor.fts5yy0); }
+ fts5yymsp[-1].minor.fts5yy3 = fts5yylhsminor.fts5yy3;
+ break;
+ case 13: /* colsetlist ::= STRING */
+{
+ fts5yylhsminor.fts5yy3 = sqlite3Fts5ParseColset(pParse, 0, &fts5yymsp[0].minor.fts5yy0);
+}
+ fts5yymsp[0].minor.fts5yy3 = fts5yylhsminor.fts5yy3;
+ break;
+ case 14: /* nearset ::= phrase */
+{ fts5yylhsminor.fts5yy26 = sqlite3Fts5ParseNearset(pParse, 0, fts5yymsp[0].minor.fts5yy11); }
+ fts5yymsp[0].minor.fts5yy26 = fts5yylhsminor.fts5yy26;
+ break;
+ case 15: /* nearset ::= STRING LP nearphrases neardist_opt RP */
+{
+ sqlite3Fts5ParseNear(pParse, &fts5yymsp[-4].minor.fts5yy0);
+ sqlite3Fts5ParseSetDistance(pParse, fts5yymsp[-2].minor.fts5yy26, &fts5yymsp[-1].minor.fts5yy0);
+ fts5yylhsminor.fts5yy26 = fts5yymsp[-2].minor.fts5yy26;
+}
+ fts5yymsp[-4].minor.fts5yy26 = fts5yylhsminor.fts5yy26;
+ break;
+ case 16: /* nearphrases ::= phrase */
+{
+ fts5yylhsminor.fts5yy26 = sqlite3Fts5ParseNearset(pParse, 0, fts5yymsp[0].minor.fts5yy11);
+}
+ fts5yymsp[0].minor.fts5yy26 = fts5yylhsminor.fts5yy26;
+ break;
+ case 17: /* nearphrases ::= nearphrases phrase */
+{
+ fts5yylhsminor.fts5yy26 = sqlite3Fts5ParseNearset(pParse, fts5yymsp[-1].minor.fts5yy26, fts5yymsp[0].minor.fts5yy11);
+}
+ fts5yymsp[-1].minor.fts5yy26 = fts5yylhsminor.fts5yy26;
+ break;
+ case 18: /* neardist_opt ::= */
+{ fts5yymsp[1].minor.fts5yy0.p = 0; fts5yymsp[1].minor.fts5yy0.n = 0; }
+ break;
+ case 19: /* neardist_opt ::= COMMA STRING */
+{ fts5yymsp[-1].minor.fts5yy0 = fts5yymsp[0].minor.fts5yy0; }
+ break;
+ case 20: /* phrase ::= phrase PLUS STRING star_opt */
+{
+ fts5yylhsminor.fts5yy11 = sqlite3Fts5ParseTerm(pParse, fts5yymsp[-3].minor.fts5yy11, &fts5yymsp[-1].minor.fts5yy0, fts5yymsp[0].minor.fts5yy20);
+}
+ fts5yymsp[-3].minor.fts5yy11 = fts5yylhsminor.fts5yy11;
+ break;
+ case 21: /* phrase ::= STRING star_opt */
+{
+ fts5yylhsminor.fts5yy11 = sqlite3Fts5ParseTerm(pParse, 0, &fts5yymsp[-1].minor.fts5yy0, fts5yymsp[0].minor.fts5yy20);
+}
+ fts5yymsp[-1].minor.fts5yy11 = fts5yylhsminor.fts5yy11;
+ break;
+ case 22: /* star_opt ::= STAR */
+{ fts5yymsp[0].minor.fts5yy20 = 1; }
+ break;
+ case 23: /* star_opt ::= */
+{ fts5yymsp[1].minor.fts5yy20 = 0; }
+ break;
+ default:
+ break;
+/********** End reduce actions ************************************************/
+ };
+ assert( fts5yyruleno<sizeof(fts5yyRuleInfo)/sizeof(fts5yyRuleInfo[0]) );
+ fts5yygoto = fts5yyRuleInfo[fts5yyruleno].lhs;
+ fts5yysize = fts5yyRuleInfo[fts5yyruleno].nrhs;
+ fts5yyact = fts5yy_find_reduce_action(fts5yymsp[-fts5yysize].stateno,(fts5YYCODETYPE)fts5yygoto);
+ if( fts5yyact <= fts5YY_MAX_SHIFTREDUCE ){
+ if( fts5yyact>fts5YY_MAX_SHIFT ) fts5yyact += fts5YY_MIN_REDUCE - fts5YY_MIN_SHIFTREDUCE;
+ fts5yypParser->fts5yyidx -= fts5yysize - 1;
+ fts5yymsp -= fts5yysize-1;
+ fts5yymsp->stateno = (fts5YYACTIONTYPE)fts5yyact;
+ fts5yymsp->major = (fts5YYCODETYPE)fts5yygoto;
+ fts5yyTraceShift(fts5yypParser, fts5yyact);
+ }else{
+ assert( fts5yyact == fts5YY_ACCEPT_ACTION );
+ fts5yypParser->fts5yyidx -= fts5yysize;
+ fts5yy_accept(fts5yypParser);
+ }
+}
+
+/*
+** The following code executes when the parse fails
+*/
+#ifndef fts5YYNOERRORRECOVERY
+static void fts5yy_parse_failed(
+ fts5yyParser *fts5yypParser /* The parser */
+){
+ sqlite3Fts5ParserARG_FETCH;
+#ifndef NDEBUG
+ if( fts5yyTraceFILE ){
+ fprintf(fts5yyTraceFILE,"%sFail!\n",fts5yyTracePrompt);
+ }
+#endif
+ while( fts5yypParser->fts5yyidx>=0 ) fts5yy_pop_parser_stack(fts5yypParser);
+ /* Here code is inserted which will be executed whenever the
+ ** parser fails */
+/************ Begin %parse_failure code ***************************************/
+/************ End %parse_failure code *****************************************/
+ sqlite3Fts5ParserARG_STORE; /* Suppress warning about unused %extra_argument variable */
+}
+#endif /* fts5YYNOERRORRECOVERY */
+
+/*
+** The following code executes when a syntax error first occurs.
+*/
+static void fts5yy_syntax_error(
+ fts5yyParser *fts5yypParser, /* The parser */
+ int fts5yymajor, /* The major type of the error token */
+ sqlite3Fts5ParserFTS5TOKENTYPE fts5yyminor /* The minor type of the error token */
+){
+ sqlite3Fts5ParserARG_FETCH;
+#define FTS5TOKEN fts5yyminor
+/************ Begin %syntax_error code ****************************************/
+
+ UNUSED_PARAM(fts5yymajor); /* Silence a compiler warning */
+ sqlite3Fts5ParseError(
+ pParse, "fts5: syntax error near \"%.*s\"",FTS5TOKEN.n,FTS5TOKEN.p
+ );
+/************ End %syntax_error code ******************************************/
+ sqlite3Fts5ParserARG_STORE; /* Suppress warning about unused %extra_argument variable */
+}
+
+/*
+** The following is executed when the parser accepts
+*/
+static void fts5yy_accept(
+ fts5yyParser *fts5yypParser /* The parser */
+){
+ sqlite3Fts5ParserARG_FETCH;
+#ifndef NDEBUG
+ if( fts5yyTraceFILE ){
+ fprintf(fts5yyTraceFILE,"%sAccept!\n",fts5yyTracePrompt);
+ }
+#endif
+ while( fts5yypParser->fts5yyidx>=0 ) fts5yy_pop_parser_stack(fts5yypParser);
+ /* Here code is inserted which will be executed whenever the
+ ** parser accepts */
+/*********** Begin %parse_accept code *****************************************/
+/*********** End %parse_accept code *******************************************/
+ sqlite3Fts5ParserARG_STORE; /* Suppress warning about unused %extra_argument variable */
+}
+
+/* The main parser program.
+** The first argument is a pointer to a structure obtained from
+** "sqlite3Fts5ParserAlloc" which describes the current state of the parser.
+** The second argument is the major token number. The third is
+** the minor token. The fourth optional argument is whatever the
+** user wants (and specified in the grammar) and is available for
+** use by the action routines.
+**
+** Inputs:
+** <ul>
+** <li> A pointer to the parser (an opaque structure.)
+** <li> The major token number.
+** <li> The minor token number.
+** <li> An option argument of a grammar-specified type.
+** </ul>
+**
+** Outputs:
+** None.
+*/
+static void sqlite3Fts5Parser(
+ void *fts5yyp, /* The parser */
+ int fts5yymajor, /* The major token code number */
+ sqlite3Fts5ParserFTS5TOKENTYPE fts5yyminor /* The value for the token */
+ sqlite3Fts5ParserARG_PDECL /* Optional %extra_argument parameter */
+){
+ fts5YYMINORTYPE fts5yyminorunion;
+ unsigned int fts5yyact; /* The parser action. */
+#if !defined(fts5YYERRORSYMBOL) && !defined(fts5YYNOERRORRECOVERY)
+ int fts5yyendofinput; /* True if we are at the end of input */
+#endif
+#ifdef fts5YYERRORSYMBOL
+ int fts5yyerrorhit = 0; /* True if fts5yymajor has invoked an error */
+#endif
+ fts5yyParser *fts5yypParser; /* The parser */
+
+ /* (re)initialize the parser, if necessary */
+ fts5yypParser = (fts5yyParser*)fts5yyp;
+ if( fts5yypParser->fts5yyidx<0 ){
+#if fts5YYSTACKDEPTH<=0
+ if( fts5yypParser->fts5yystksz <=0 ){
+ fts5yyStackOverflow(fts5yypParser);
+ return;
+ }
+#endif
+ fts5yypParser->fts5yyidx = 0;
+#ifndef fts5YYNOERRORRECOVERY
+ fts5yypParser->fts5yyerrcnt = -1;
+#endif
+ fts5yypParser->fts5yystack[0].stateno = 0;
+ fts5yypParser->fts5yystack[0].major = 0;
+#ifndef NDEBUG
+ if( fts5yyTraceFILE ){
+ fprintf(fts5yyTraceFILE,"%sInitialize. Empty stack. State 0\n",
+ fts5yyTracePrompt);
+ }
+#endif
+ }
+#if !defined(fts5YYERRORSYMBOL) && !defined(fts5YYNOERRORRECOVERY)
+ fts5yyendofinput = (fts5yymajor==0);
+#endif
+ sqlite3Fts5ParserARG_STORE;
+
+#ifndef NDEBUG
+ if( fts5yyTraceFILE ){
+ fprintf(fts5yyTraceFILE,"%sInput '%s'\n",fts5yyTracePrompt,fts5yyTokenName[fts5yymajor]);
+ }
+#endif
+
+ do{
+ fts5yyact = fts5yy_find_shift_action(fts5yypParser,(fts5YYCODETYPE)fts5yymajor);
+ if( fts5yyact <= fts5YY_MAX_SHIFTREDUCE ){
+ if( fts5yyact > fts5YY_MAX_SHIFT ) fts5yyact += fts5YY_MIN_REDUCE - fts5YY_MIN_SHIFTREDUCE;
+ fts5yy_shift(fts5yypParser,fts5yyact,fts5yymajor,fts5yyminor);
+#ifndef fts5YYNOERRORRECOVERY
+ fts5yypParser->fts5yyerrcnt--;
+#endif
+ fts5yymajor = fts5YYNOCODE;
+ }else if( fts5yyact <= fts5YY_MAX_REDUCE ){
+ fts5yy_reduce(fts5yypParser,fts5yyact-fts5YY_MIN_REDUCE);
+ }else{
+ assert( fts5yyact == fts5YY_ERROR_ACTION );
+ fts5yyminorunion.fts5yy0 = fts5yyminor;
+#ifdef fts5YYERRORSYMBOL
+ int fts5yymx;
+#endif
+#ifndef NDEBUG
+ if( fts5yyTraceFILE ){
+ fprintf(fts5yyTraceFILE,"%sSyntax Error!\n",fts5yyTracePrompt);
+ }
+#endif
+#ifdef fts5YYERRORSYMBOL
+ /* A syntax error has occurred.
+ ** The response to an error depends upon whether or not the
+ ** grammar defines an error token "ERROR".
+ **
+ ** This is what we do if the grammar does define ERROR:
+ **
+ ** * Call the %syntax_error function.
+ **
+ ** * Begin popping the stack until we enter a state where
+ ** it is legal to shift the error symbol, then shift
+ ** the error symbol.
+ **
+ ** * Set the error count to three.
+ **
+ ** * Begin accepting and shifting new tokens. No new error
+ ** processing will occur until three tokens have been
+ ** shifted successfully.
+ **
+ */
+ if( fts5yypParser->fts5yyerrcnt<0 ){
+ fts5yy_syntax_error(fts5yypParser,fts5yymajor,fts5yyminor);
+ }
+ fts5yymx = fts5yypParser->fts5yystack[fts5yypParser->fts5yyidx].major;
+ if( fts5yymx==fts5YYERRORSYMBOL || fts5yyerrorhit ){
+#ifndef NDEBUG
+ if( fts5yyTraceFILE ){
+ fprintf(fts5yyTraceFILE,"%sDiscard input token %s\n",
+ fts5yyTracePrompt,fts5yyTokenName[fts5yymajor]);
+ }
+#endif
+ fts5yy_destructor(fts5yypParser, (fts5YYCODETYPE)fts5yymajor, &fts5yyminorunion);
+ fts5yymajor = fts5YYNOCODE;
+ }else{
+ while(
+ fts5yypParser->fts5yyidx >= 0 &&
+ fts5yymx != fts5YYERRORSYMBOL &&
+ (fts5yyact = fts5yy_find_reduce_action(
+ fts5yypParser->fts5yystack[fts5yypParser->fts5yyidx].stateno,
+ fts5YYERRORSYMBOL)) >= fts5YY_MIN_REDUCE
+ ){
+ fts5yy_pop_parser_stack(fts5yypParser);
+ }
+ if( fts5yypParser->fts5yyidx < 0 || fts5yymajor==0 ){
+ fts5yy_destructor(fts5yypParser,(fts5YYCODETYPE)fts5yymajor,&fts5yyminorunion);
+ fts5yy_parse_failed(fts5yypParser);
+ fts5yymajor = fts5YYNOCODE;
+ }else if( fts5yymx!=fts5YYERRORSYMBOL ){
+ fts5yy_shift(fts5yypParser,fts5yyact,fts5YYERRORSYMBOL,fts5yyminor);
+ }
+ }
+ fts5yypParser->fts5yyerrcnt = 3;
+ fts5yyerrorhit = 1;
+#elif defined(fts5YYNOERRORRECOVERY)
+ /* If the fts5YYNOERRORRECOVERY macro is defined, then do not attempt to
+ ** do any kind of error recovery. Instead, simply invoke the syntax
+ ** error routine and continue going as if nothing had happened.
+ **
+ ** Applications can set this macro (for example inside %include) if
+ ** they intend to abandon the parse upon the first syntax error seen.
+ */
+ fts5yy_syntax_error(fts5yypParser,fts5yymajor, fts5yyminor);
+ fts5yy_destructor(fts5yypParser,(fts5YYCODETYPE)fts5yymajor,&fts5yyminorunion);
+ fts5yymajor = fts5YYNOCODE;
+
+#else /* fts5YYERRORSYMBOL is not defined */
+ /* This is what we do if the grammar does not define ERROR:
+ **
+ ** * Report an error message, and throw away the input token.
+ **
+ ** * If the input token is $, then fail the parse.
+ **
+ ** As before, subsequent error messages are suppressed until
+ ** three input tokens have been successfully shifted.
+ */
+ if( fts5yypParser->fts5yyerrcnt<=0 ){
+ fts5yy_syntax_error(fts5yypParser,fts5yymajor, fts5yyminor);
+ }
+ fts5yypParser->fts5yyerrcnt = 3;
+ fts5yy_destructor(fts5yypParser,(fts5YYCODETYPE)fts5yymajor,&fts5yyminorunion);
+ if( fts5yyendofinput ){
+ fts5yy_parse_failed(fts5yypParser);
+ }
+ fts5yymajor = fts5YYNOCODE;
+#endif
+ }
+ }while( fts5yymajor!=fts5YYNOCODE && fts5yypParser->fts5yyidx>=0 );
+#ifndef NDEBUG
+ if( fts5yyTraceFILE ){
+ int i;
+ fprintf(fts5yyTraceFILE,"%sReturn. Stack=",fts5yyTracePrompt);
+ for(i=1; i<=fts5yypParser->fts5yyidx; i++)
+ fprintf(fts5yyTraceFILE,"%c%s", i==1 ? '[' : ' ',
+ fts5yyTokenName[fts5yypParser->fts5yystack[i].major]);
+ fprintf(fts5yyTraceFILE,"]\n");
+ }
+#endif
+ return;
+}
+
+/*
+** 2014 May 31
+**
+** 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.
+**
+******************************************************************************
+*/
+
+
+/* #include "fts5Int.h" */
+#include <math.h> /* amalgamator: keep */
+
+/*
+** Object used to iterate through all "coalesced phrase instances" in
+** a single column of the current row. If the phrase instances in the
+** column being considered do not overlap, this object simply iterates
+** through them. Or, if they do overlap (share one or more tokens in
+** common), each set of overlapping instances is treated as a single
+** match. See documentation for the highlight() auxiliary function for
+** details.
+**
+** Usage is:
+**
+** for(rc = fts5CInstIterNext(pApi, pFts, iCol, &iter);
+** (rc==SQLITE_OK && 0==fts5CInstIterEof(&iter);
+** rc = fts5CInstIterNext(&iter)
+** ){
+** printf("instance starts at %d, ends at %d\n", iter.iStart, iter.iEnd);
+** }
+**
+*/
+typedef struct CInstIter CInstIter;
+struct CInstIter {
+ const Fts5ExtensionApi *pApi; /* API offered by current FTS version */
+ Fts5Context *pFts; /* First arg to pass to pApi functions */
+ int iCol; /* Column to search */
+ int iInst; /* Next phrase instance index */
+ int nInst; /* Total number of phrase instances */
+
+ /* Output variables */
+ int iStart; /* First token in coalesced phrase instance */
+ int iEnd; /* Last token in coalesced phrase instance */
+};
+
+/*
+** Advance the iterator to the next coalesced phrase instance. Return
+** an SQLite error code if an error occurs, or SQLITE_OK otherwise.
+*/
+static int fts5CInstIterNext(CInstIter *pIter){
+ int rc = SQLITE_OK;
+ pIter->iStart = -1;
+ pIter->iEnd = -1;
+
+ while( rc==SQLITE_OK && pIter->iInst<pIter->nInst ){
+ int ip; int ic; int io;
+ rc = pIter->pApi->xInst(pIter->pFts, pIter->iInst, &ip, &ic, &io);
+ if( rc==SQLITE_OK ){
+ if( ic==pIter->iCol ){
+ int iEnd = io - 1 + pIter->pApi->xPhraseSize(pIter->pFts, ip);
+ if( pIter->iStart<0 ){
+ pIter->iStart = io;
+ pIter->iEnd = iEnd;
+ }else if( io<=pIter->iEnd ){
+ if( iEnd>pIter->iEnd ) pIter->iEnd = iEnd;
+ }else{
+ break;
+ }
+ }
+ pIter->iInst++;
+ }
+ }
+
+ return rc;
+}
+
+/*
+** Initialize the iterator object indicated by the final parameter to
+** iterate through coalesced phrase instances in column iCol.
+*/
+static int fts5CInstIterInit(
+ const Fts5ExtensionApi *pApi,
+ Fts5Context *pFts,
+ int iCol,
+ CInstIter *pIter
+){
+ int rc;
+
+ memset(pIter, 0, sizeof(CInstIter));
+ pIter->pApi = pApi;
+ pIter->pFts = pFts;
+ pIter->iCol = iCol;
+ rc = pApi->xInstCount(pFts, &pIter->nInst);
+
+ if( rc==SQLITE_OK ){
+ rc = fts5CInstIterNext(pIter);
+ }
+
+ return rc;
+}
+
+
+
+/*************************************************************************
+** Start of highlight() implementation.
+*/
+typedef struct HighlightContext HighlightContext;
+struct HighlightContext {
+ CInstIter iter; /* Coalesced Instance Iterator */
+ int iPos; /* Current token offset in zIn[] */
+ int iRangeStart; /* First token to include */
+ int iRangeEnd; /* If non-zero, last token to include */
+ const char *zOpen; /* Opening highlight */
+ const char *zClose; /* Closing highlight */
+ const char *zIn; /* Input text */
+ int nIn; /* Size of input text in bytes */
+ int iOff; /* Current offset within zIn[] */
+ char *zOut; /* Output value */
+};
+
+/*
+** Append text to the HighlightContext output string - p->zOut. Argument
+** z points to a buffer containing n bytes of text to append. If n is
+** negative, everything up until the first '\0' is appended to the output.
+**
+** If *pRc is set to any value other than SQLITE_OK when this function is
+** called, it is a no-op. If an error (i.e. an OOM condition) is encountered,
+** *pRc is set to an error code before returning.
+*/
+static void fts5HighlightAppend(
+ int *pRc,
+ HighlightContext *p,
+ const char *z, int n
+){
+ if( *pRc==SQLITE_OK ){
+ if( n<0 ) n = (int)strlen(z);
+ p->zOut = sqlite3_mprintf("%z%.*s", p->zOut, n, z);
+ if( p->zOut==0 ) *pRc = SQLITE_NOMEM;
+ }
+}
+
+/*
+** Tokenizer callback used by implementation of highlight() function.
+*/
+static int fts5HighlightCb(
+ void *pContext, /* Pointer to HighlightContext object */
+ int tflags, /* Mask of FTS5_TOKEN_* flags */
+ const char *pToken, /* Buffer containing token */
+ int nToken, /* Size of token in bytes */
+ int iStartOff, /* Start offset of token */
+ int iEndOff /* End offset of token */
+){
+ HighlightContext *p = (HighlightContext*)pContext;
+ int rc = SQLITE_OK;
+ int iPos;
+
+ UNUSED_PARAM2(pToken, nToken);
+
+ if( tflags & FTS5_TOKEN_COLOCATED ) return SQLITE_OK;
+ iPos = p->iPos++;
+
+ if( p->iRangeEnd>0 ){
+ if( iPos<p->iRangeStart || iPos>p->iRangeEnd ) return SQLITE_OK;
+ if( p->iRangeStart && iPos==p->iRangeStart ) p->iOff = iStartOff;
+ }
+
+ if( iPos==p->iter.iStart ){
+ fts5HighlightAppend(&rc, p, &p->zIn[p->iOff], iStartOff - p->iOff);
+ fts5HighlightAppend(&rc, p, p->zOpen, -1);
+ p->iOff = iStartOff;
+ }
+
+ if( iPos==p->iter.iEnd ){
+ if( p->iRangeEnd && p->iter.iStart<p->iRangeStart ){
+ fts5HighlightAppend(&rc, p, p->zOpen, -1);
+ }
+ fts5HighlightAppend(&rc, p, &p->zIn[p->iOff], iEndOff - p->iOff);
+ fts5HighlightAppend(&rc, p, p->zClose, -1);
+ p->iOff = iEndOff;
+ if( rc==SQLITE_OK ){
+ rc = fts5CInstIterNext(&p->iter);
+ }
+ }
+
+ if( p->iRangeEnd>0 && iPos==p->iRangeEnd ){
+ fts5HighlightAppend(&rc, p, &p->zIn[p->iOff], iEndOff - p->iOff);
+ p->iOff = iEndOff;
+ if( iPos<p->iter.iEnd ){
+ fts5HighlightAppend(&rc, p, p->zClose, -1);
+ }
+ }
+
+ return rc;
+}
+
+/*
+** Implementation of highlight() function.
+*/
+static void fts5HighlightFunction(
+ const Fts5ExtensionApi *pApi, /* API offered by current FTS version */
+ Fts5Context *pFts, /* First arg to pass to pApi functions */
+ sqlite3_context *pCtx, /* Context for returning result/error */
+ int nVal, /* Number of values in apVal[] array */
+ sqlite3_value **apVal /* Array of trailing arguments */
+){
+ HighlightContext ctx;
+ int rc;
+ int iCol;
+
+ if( nVal!=3 ){
+ const char *zErr = "wrong number of arguments to function highlight()";
+ sqlite3_result_error(pCtx, zErr, -1);
+ return;
+ }
+
+ iCol = sqlite3_value_int(apVal[0]);
+ memset(&ctx, 0, sizeof(HighlightContext));
+ ctx.zOpen = (const char*)sqlite3_value_text(apVal[1]);
+ ctx.zClose = (const char*)sqlite3_value_text(apVal[2]);
+ rc = pApi->xColumnText(pFts, iCol, &ctx.zIn, &ctx.nIn);
+
+ if( ctx.zIn ){
+ if( rc==SQLITE_OK ){
+ rc = fts5CInstIterInit(pApi, pFts, iCol, &ctx.iter);
+ }
+
+ if( rc==SQLITE_OK ){
+ rc = pApi->xTokenize(pFts, ctx.zIn, ctx.nIn, (void*)&ctx,fts5HighlightCb);
+ }
+ fts5HighlightAppend(&rc, &ctx, &ctx.zIn[ctx.iOff], ctx.nIn - ctx.iOff);
+
+ if( rc==SQLITE_OK ){
+ sqlite3_result_text(pCtx, (const char*)ctx.zOut, -1, SQLITE_TRANSIENT);
+ }
+ sqlite3_free(ctx.zOut);
+ }
+ if( rc!=SQLITE_OK ){
+ sqlite3_result_error_code(pCtx, rc);
+ }
+}
+/*
+** End of highlight() implementation.
+**************************************************************************/
+
+/*
+** Implementation of snippet() function.
+*/
+static void fts5SnippetFunction(
+ const Fts5ExtensionApi *pApi, /* API offered by current FTS version */
+ Fts5Context *pFts, /* First arg to pass to pApi functions */
+ sqlite3_context *pCtx, /* Context for returning result/error */
+ int nVal, /* Number of values in apVal[] array */
+ sqlite3_value **apVal /* Array of trailing arguments */
+){
+ HighlightContext ctx;
+ int rc = SQLITE_OK; /* Return code */
+ int iCol; /* 1st argument to snippet() */
+ const char *zEllips; /* 4th argument to snippet() */
+ int nToken; /* 5th argument to snippet() */
+ int nInst = 0; /* Number of instance matches this row */
+ int i; /* Used to iterate through instances */
+ int nPhrase; /* Number of phrases in query */
+ unsigned char *aSeen; /* Array of "seen instance" flags */
+ int iBestCol; /* Column containing best snippet */
+ int iBestStart = 0; /* First token of best snippet */
+ int iBestLast; /* Last token of best snippet */
+ int nBestScore = 0; /* Score of best snippet */
+ int nColSize = 0; /* Total size of iBestCol in tokens */
+
+ if( nVal!=5 ){
+ const char *zErr = "wrong number of arguments to function snippet()";
+ sqlite3_result_error(pCtx, zErr, -1);
+ return;
+ }
+
+ memset(&ctx, 0, sizeof(HighlightContext));
+ iCol = sqlite3_value_int(apVal[0]);
+ ctx.zOpen = (const char*)sqlite3_value_text(apVal[1]);
+ ctx.zClose = (const char*)sqlite3_value_text(apVal[2]);
+ zEllips = (const char*)sqlite3_value_text(apVal[3]);
+ nToken = sqlite3_value_int(apVal[4]);
+ iBestLast = nToken-1;
+
+ iBestCol = (iCol>=0 ? iCol : 0);
+ nPhrase = pApi->xPhraseCount(pFts);
+ aSeen = sqlite3_malloc(nPhrase);
+ if( aSeen==0 ){
+ rc = SQLITE_NOMEM;
+ }
+
+ if( rc==SQLITE_OK ){
+ rc = pApi->xInstCount(pFts, &nInst);
+ }
+ for(i=0; rc==SQLITE_OK && i<nInst; i++){
+ int ip, iSnippetCol, iStart;
+ memset(aSeen, 0, nPhrase);
+ rc = pApi->xInst(pFts, i, &ip, &iSnippetCol, &iStart);
+ if( rc==SQLITE_OK && (iCol<0 || iSnippetCol==iCol) ){
+ int nScore = 1000;
+ int iLast = iStart - 1 + pApi->xPhraseSize(pFts, ip);
+ int j;
+ aSeen[ip] = 1;
+
+ for(j=i+1; rc==SQLITE_OK && j<nInst; j++){
+ int ic; int io; int iFinal;
+ rc = pApi->xInst(pFts, j, &ip, &ic, &io);
+ iFinal = io + pApi->xPhraseSize(pFts, ip) - 1;
+ if( rc==SQLITE_OK && ic==iSnippetCol && iLast<iStart+nToken ){
+ nScore += aSeen[ip] ? 1000 : 1;
+ aSeen[ip] = 1;
+ if( iFinal>iLast ) iLast = iFinal;
+ }
+ }
+
+ if( rc==SQLITE_OK && nScore>nBestScore ){
+ iBestCol = iSnippetCol;
+ iBestStart = iStart;
+ iBestLast = iLast;
+ nBestScore = nScore;
+ }
+ }
+ }
+
+ if( rc==SQLITE_OK ){
+ rc = pApi->xColumnSize(pFts, iBestCol, &nColSize);
+ }
+ if( rc==SQLITE_OK ){
+ rc = pApi->xColumnText(pFts, iBestCol, &ctx.zIn, &ctx.nIn);
+ }
+ if( ctx.zIn ){
+ if( rc==SQLITE_OK ){
+ rc = fts5CInstIterInit(pApi, pFts, iBestCol, &ctx.iter);
+ }
+
+ if( (iBestStart+nToken-1)>iBestLast ){
+ iBestStart -= (iBestStart+nToken-1-iBestLast) / 2;
+ }
+ if( iBestStart+nToken>nColSize ){
+ iBestStart = nColSize - nToken;
+ }
+ if( iBestStart<0 ) iBestStart = 0;
+
+ ctx.iRangeStart = iBestStart;
+ ctx.iRangeEnd = iBestStart + nToken - 1;
+
+ if( iBestStart>0 ){
+ fts5HighlightAppend(&rc, &ctx, zEllips, -1);
+ }
+ if( rc==SQLITE_OK ){
+ rc = pApi->xTokenize(pFts, ctx.zIn, ctx.nIn, (void*)&ctx,fts5HighlightCb);
+ }
+ if( ctx.iRangeEnd>=(nColSize-1) ){
+ fts5HighlightAppend(&rc, &ctx, &ctx.zIn[ctx.iOff], ctx.nIn - ctx.iOff);
+ }else{
+ fts5HighlightAppend(&rc, &ctx, zEllips, -1);
+ }
+
+ if( rc==SQLITE_OK ){
+ sqlite3_result_text(pCtx, (const char*)ctx.zOut, -1, SQLITE_TRANSIENT);
+ }else{
+ sqlite3_result_error_code(pCtx, rc);
+ }
+ sqlite3_free(ctx.zOut);
+ }
+ sqlite3_free(aSeen);
+}
+
+/************************************************************************/
+
+/*
+** The first time the bm25() function is called for a query, an instance
+** of the following structure is allocated and populated.
+*/
+typedef struct Fts5Bm25Data Fts5Bm25Data;
+struct Fts5Bm25Data {
+ int nPhrase; /* Number of phrases in query */
+ double avgdl; /* Average number of tokens in each row */
+ double *aIDF; /* IDF for each phrase */
+ double *aFreq; /* Array used to calculate phrase freq. */
+};
+
+/*
+** Callback used by fts5Bm25GetData() to count the number of rows in the
+** table matched by each individual phrase within the query.
+*/
+static int fts5CountCb(
+ const Fts5ExtensionApi *pApi,
+ Fts5Context *pFts,
+ void *pUserData /* Pointer to sqlite3_int64 variable */
+){
+ sqlite3_int64 *pn = (sqlite3_int64*)pUserData;
+ UNUSED_PARAM2(pApi, pFts);
+ (*pn)++;
+ return SQLITE_OK;
+}
+
+/*
+** Set *ppData to point to the Fts5Bm25Data object for the current query.
+** If the object has not already been allocated, allocate and populate it
+** now.
+*/
+static int fts5Bm25GetData(
+ const Fts5ExtensionApi *pApi,
+ Fts5Context *pFts,
+ Fts5Bm25Data **ppData /* OUT: bm25-data object for this query */
+){
+ int rc = SQLITE_OK; /* Return code */
+ Fts5Bm25Data *p; /* Object to return */
+
+ p = pApi->xGetAuxdata(pFts, 0);
+ if( p==0 ){
+ int nPhrase; /* Number of phrases in query */
+ sqlite3_int64 nRow = 0; /* Number of rows in table */
+ sqlite3_int64 nToken = 0; /* Number of tokens in table */
+ int nByte; /* Bytes of space to allocate */
+ int i;
+
+ /* Allocate the Fts5Bm25Data object */
+ nPhrase = pApi->xPhraseCount(pFts);
+ nByte = sizeof(Fts5Bm25Data) + nPhrase*2*sizeof(double);
+ p = (Fts5Bm25Data*)sqlite3_malloc(nByte);
+ if( p==0 ){
+ rc = SQLITE_NOMEM;
+ }else{
+ memset(p, 0, nByte);
+ p->nPhrase = nPhrase;
+ p->aIDF = (double*)&p[1];
+ p->aFreq = &p->aIDF[nPhrase];
+ }
+
+ /* Calculate the average document length for this FTS5 table */
+ if( rc==SQLITE_OK ) rc = pApi->xRowCount(pFts, &nRow);
+ if( rc==SQLITE_OK ) rc = pApi->xColumnTotalSize(pFts, -1, &nToken);
+ if( rc==SQLITE_OK ) p->avgdl = (double)nToken / (double)nRow;
+
+ /* Calculate an IDF for each phrase in the query */
+ for(i=0; rc==SQLITE_OK && i<nPhrase; i++){
+ sqlite3_int64 nHit = 0;
+ rc = pApi->xQueryPhrase(pFts, i, (void*)&nHit, fts5CountCb);
+ if( rc==SQLITE_OK ){
+ /* Calculate the IDF (Inverse Document Frequency) for phrase i.
+ ** This is done using the standard BM25 formula as found on wikipedia:
+ **
+ ** IDF = log( (N - nHit + 0.5) / (nHit + 0.5) )
+ **
+ ** where "N" is the total number of documents in the set and nHit
+ ** is the number that contain at least one instance of the phrase
+ ** under consideration.
+ **
+ ** The problem with this is that if (N < 2*nHit), the IDF is
+ ** negative. Which is undesirable. So the mimimum allowable IDF is
+ ** (1e-6) - roughly the same as a term that appears in just over
+ ** half of set of 5,000,000 documents. */
+ double idf = log( (nRow - nHit + 0.5) / (nHit + 0.5) );
+ if( idf<=0.0 ) idf = 1e-6;
+ p->aIDF[i] = idf;
+ }
+ }
+
+ if( rc!=SQLITE_OK ){
+ sqlite3_free(p);
+ }else{
+ rc = pApi->xSetAuxdata(pFts, p, sqlite3_free);
+ }
+ if( rc!=SQLITE_OK ) p = 0;
+ }
+ *ppData = p;
+ return rc;
+}
+
+/*
+** Implementation of bm25() function.
+*/
+static void fts5Bm25Function(
+ const Fts5ExtensionApi *pApi, /* API offered by current FTS version */
+ Fts5Context *pFts, /* First arg to pass to pApi functions */
+ sqlite3_context *pCtx, /* Context for returning result/error */
+ int nVal, /* Number of values in apVal[] array */
+ sqlite3_value **apVal /* Array of trailing arguments */
+){
+ const double k1 = 1.2; /* Constant "k1" from BM25 formula */
+ const double b = 0.75; /* Constant "b" from BM25 formula */
+ int rc = SQLITE_OK; /* Error code */
+ double score = 0.0; /* SQL function return value */
+ Fts5Bm25Data *pData; /* Values allocated/calculated once only */
+ int i; /* Iterator variable */
+ int nInst = 0; /* Value returned by xInstCount() */
+ double D = 0.0; /* Total number of tokens in row */
+ double *aFreq = 0; /* Array of phrase freq. for current row */
+
+ /* Calculate the phrase frequency (symbol "f(qi,D)" in the documentation)
+ ** for each phrase in the query for the current row. */
+ rc = fts5Bm25GetData(pApi, pFts, &pData);
+ if( rc==SQLITE_OK ){
+ aFreq = pData->aFreq;
+ memset(aFreq, 0, sizeof(double) * pData->nPhrase);
+ rc = pApi->xInstCount(pFts, &nInst);
+ }
+ for(i=0; rc==SQLITE_OK && i<nInst; i++){
+ int ip; int ic; int io;
+ rc = pApi->xInst(pFts, i, &ip, &ic, &io);
+ if( rc==SQLITE_OK ){
+ double w = (nVal > ic) ? sqlite3_value_double(apVal[ic]) : 1.0;
+ aFreq[ip] += w;
+ }
+ }
+
+ /* Figure out the total size of the current row in tokens. */
+ if( rc==SQLITE_OK ){
+ int nTok;
+ rc = pApi->xColumnSize(pFts, -1, &nTok);
+ D = (double)nTok;
+ }
+
+ /* Determine the BM25 score for the current row. */
+ for(i=0; rc==SQLITE_OK && i<pData->nPhrase; i++){
+ score += pData->aIDF[i] * (
+ ( aFreq[i] * (k1 + 1.0) ) /
+ ( aFreq[i] + k1 * (1 - b + b * D / pData->avgdl) )
+ );
+ }
+
+ /* If no error has occurred, return the calculated score. Otherwise,
+ ** throw an SQL exception. */
+ if( rc==SQLITE_OK ){
+ sqlite3_result_double(pCtx, -1.0 * score);
+ }else{
+ sqlite3_result_error_code(pCtx, rc);
+ }
+}
+
+static int sqlite3Fts5AuxInit(fts5_api *pApi){
+ struct Builtin {
+ const char *zFunc; /* Function name (nul-terminated) */
+ void *pUserData; /* User-data pointer */
+ fts5_extension_function xFunc;/* Callback function */
+ void (*xDestroy)(void*); /* Destructor function */
+ } aBuiltin [] = {
+ { "snippet", 0, fts5SnippetFunction, 0 },
+ { "highlight", 0, fts5HighlightFunction, 0 },
+ { "bm25", 0, fts5Bm25Function, 0 },
+ };
+ int rc = SQLITE_OK; /* Return code */
+ int i; /* To iterate through builtin functions */
+
+ for(i=0; rc==SQLITE_OK && i<ArraySize(aBuiltin); i++){
+ rc = pApi->xCreateFunction(pApi,
+ aBuiltin[i].zFunc,
+ aBuiltin[i].pUserData,
+ aBuiltin[i].xFunc,
+ aBuiltin[i].xDestroy
+ );
+ }
+
+ return rc;
+}
+
+
+
+/*
+** 2014 May 31
+**
+** 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.
+**
+******************************************************************************
+*/
+
+
+
+/* #include "fts5Int.h" */
+
+static int sqlite3Fts5BufferSize(int *pRc, Fts5Buffer *pBuf, u32 nByte){
+ if( (u32)pBuf->nSpace<nByte ){
+ u32 nNew = pBuf->nSpace ? pBuf->nSpace : 64;
+ u8 *pNew;
+ while( nNew<nByte ){
+ nNew = nNew * 2;
+ }
+ pNew = sqlite3_realloc(pBuf->p, nNew);
+ if( pNew==0 ){
+ *pRc = SQLITE_NOMEM;
+ return 1;
+ }else{
+ pBuf->nSpace = nNew;
+ pBuf->p = pNew;
+ }
+ }
+ return 0;
+}
+
+
+/*
+** Encode value iVal as an SQLite varint and append it to the buffer object
+** pBuf. If an OOM error occurs, set the error code in p.
+*/
+static void sqlite3Fts5BufferAppendVarint(int *pRc, Fts5Buffer *pBuf, i64 iVal){
+ if( fts5BufferGrow(pRc, pBuf, 9) ) return;
+ pBuf->n += sqlite3Fts5PutVarint(&pBuf->p[pBuf->n], iVal);
+}
+
+static void sqlite3Fts5Put32(u8 *aBuf, int iVal){
+ aBuf[0] = (iVal>>24) & 0x00FF;
+ aBuf[1] = (iVal>>16) & 0x00FF;
+ aBuf[2] = (iVal>> 8) & 0x00FF;
+ aBuf[3] = (iVal>> 0) & 0x00FF;
+}
+
+static int sqlite3Fts5Get32(const u8 *aBuf){
+ return (aBuf[0] << 24) + (aBuf[1] << 16) + (aBuf[2] << 8) + aBuf[3];
+}
+
+/*
+** Append buffer nData/pData to buffer pBuf. If an OOM error occurs, set
+** the error code in p. If an error has already occurred when this function
+** is called, it is a no-op.
+*/
+static void sqlite3Fts5BufferAppendBlob(
+ int *pRc,
+ Fts5Buffer *pBuf,
+ u32 nData,
+ const u8 *pData
+){
+ assert_nc( *pRc || nData>=0 );
+ if( fts5BufferGrow(pRc, pBuf, nData) ) return;
+ memcpy(&pBuf->p[pBuf->n], pData, nData);
+ pBuf->n += nData;
+}
+
+/*
+** Append the nul-terminated string zStr to the buffer pBuf. This function
+** ensures that the byte following the buffer data is set to 0x00, even
+** though this byte is not included in the pBuf->n count.
+*/
+static void sqlite3Fts5BufferAppendString(
+ int *pRc,
+ Fts5Buffer *pBuf,
+ const char *zStr
+){
+ int nStr = (int)strlen(zStr);
+ sqlite3Fts5BufferAppendBlob(pRc, pBuf, nStr+1, (const u8*)zStr);
+ pBuf->n--;
+}
+
+/*
+** Argument zFmt is a printf() style format string. This function performs
+** the printf() style processing, then appends the results to buffer pBuf.
+**
+** Like sqlite3Fts5BufferAppendString(), this function ensures that the byte
+** following the buffer data is set to 0x00, even though this byte is not
+** included in the pBuf->n count.
+*/
+static void sqlite3Fts5BufferAppendPrintf(
+ int *pRc,
+ Fts5Buffer *pBuf,
+ char *zFmt, ...
+){
+ if( *pRc==SQLITE_OK ){
+ char *zTmp;
+ va_list ap;
+ va_start(ap, zFmt);
+ zTmp = sqlite3_vmprintf(zFmt, ap);
+ va_end(ap);
+
+ if( zTmp==0 ){
+ *pRc = SQLITE_NOMEM;
+ }else{
+ sqlite3Fts5BufferAppendString(pRc, pBuf, zTmp);
+ sqlite3_free(zTmp);
+ }
+ }
+}
+
+static char *sqlite3Fts5Mprintf(int *pRc, const char *zFmt, ...){
+ char *zRet = 0;
+ if( *pRc==SQLITE_OK ){
+ va_list ap;
+ va_start(ap, zFmt);
+ zRet = sqlite3_vmprintf(zFmt, ap);
+ va_end(ap);
+ if( zRet==0 ){
+ *pRc = SQLITE_NOMEM;
+ }
+ }
+ return zRet;
+}
+
+
+/*
+** Free any buffer allocated by pBuf. Zero the structure before returning.
+*/
+static void sqlite3Fts5BufferFree(Fts5Buffer *pBuf){
+ sqlite3_free(pBuf->p);
+ memset(pBuf, 0, sizeof(Fts5Buffer));
+}
+
+/*
+** Zero the contents of the buffer object. But do not free the associated
+** memory allocation.
+*/
+static void sqlite3Fts5BufferZero(Fts5Buffer *pBuf){
+ pBuf->n = 0;
+}
+
+/*
+** Set the buffer to contain nData/pData. If an OOM error occurs, leave an
+** the error code in p. If an error has already occurred when this function
+** is called, it is a no-op.
+*/
+static void sqlite3Fts5BufferSet(
+ int *pRc,
+ Fts5Buffer *pBuf,
+ int nData,
+ const u8 *pData
+){
+ pBuf->n = 0;
+ sqlite3Fts5BufferAppendBlob(pRc, pBuf, nData, pData);
+}
+
+static int sqlite3Fts5PoslistNext64(
+ const u8 *a, int n, /* Buffer containing poslist */
+ int *pi, /* IN/OUT: Offset within a[] */
+ i64 *piOff /* IN/OUT: Current offset */
+){
+ int i = *pi;
+ if( i>=n ){
+ /* EOF */
+ *piOff = -1;
+ return 1;
+ }else{
+ i64 iOff = *piOff;
+ int iVal;
+ fts5FastGetVarint32(a, i, iVal);
+ if( iVal==1 ){
+ fts5FastGetVarint32(a, i, iVal);
+ iOff = ((i64)iVal) << 32;
+ fts5FastGetVarint32(a, i, iVal);
+ }
+ *piOff = iOff + (iVal-2);
+ *pi = i;
+ return 0;
+ }
+}
+
+
+/*
+** Advance the iterator object passed as the only argument. Return true
+** if the iterator reaches EOF, or false otherwise.
+*/
+static int sqlite3Fts5PoslistReaderNext(Fts5PoslistReader *pIter){
+ if( sqlite3Fts5PoslistNext64(pIter->a, pIter->n, &pIter->i, &pIter->iPos) ){
+ pIter->bEof = 1;
+ }
+ return pIter->bEof;
+}
+
+static int sqlite3Fts5PoslistReaderInit(
+ const u8 *a, int n, /* Poslist buffer to iterate through */
+ Fts5PoslistReader *pIter /* Iterator object to initialize */
+){
+ memset(pIter, 0, sizeof(*pIter));
+ pIter->a = a;
+ pIter->n = n;
+ sqlite3Fts5PoslistReaderNext(pIter);
+ return pIter->bEof;
+}
+
+/*
+** Append position iPos to the position list being accumulated in buffer
+** pBuf, which must be already be large enough to hold the new data.
+** The previous position written to this list is *piPrev. *piPrev is set
+** to iPos before returning.
+*/
+static void sqlite3Fts5PoslistSafeAppend(
+ Fts5Buffer *pBuf,
+ i64 *piPrev,
+ i64 iPos
+){
+ static const i64 colmask = ((i64)(0x7FFFFFFF)) << 32;
+ if( (iPos & colmask) != (*piPrev & colmask) ){
+ pBuf->p[pBuf->n++] = 1;
+ pBuf->n += sqlite3Fts5PutVarint(&pBuf->p[pBuf->n], (iPos>>32));
+ *piPrev = (iPos & colmask);
+ }
+ pBuf->n += sqlite3Fts5PutVarint(&pBuf->p[pBuf->n], (iPos-*piPrev)+2);
+ *piPrev = iPos;
+}
+
+static int sqlite3Fts5PoslistWriterAppend(
+ Fts5Buffer *pBuf,
+ Fts5PoslistWriter *pWriter,
+ i64 iPos
+){
+ int rc = 0; /* Initialized only to suppress erroneous warning from Clang */
+ if( fts5BufferGrow(&rc, pBuf, 5+5+5) ) return rc;
+ sqlite3Fts5PoslistSafeAppend(pBuf, &pWriter->iPrev, iPos);
+ return SQLITE_OK;
+}
+
+static void *sqlite3Fts5MallocZero(int *pRc, int nByte){
+ void *pRet = 0;
+ if( *pRc==SQLITE_OK ){
+ pRet = sqlite3_malloc(nByte);
+ if( pRet==0 && nByte>0 ){
+ *pRc = SQLITE_NOMEM;
+ }else{
+ memset(pRet, 0, nByte);
+ }
+ }
+ return pRet;
+}
+
+/*
+** Return a nul-terminated copy of the string indicated by pIn. If nIn
+** is non-negative, then it is the length of the string in bytes. Otherwise,
+** the length of the string is determined using strlen().
+**
+** It is the responsibility of the caller to eventually free the returned
+** buffer using sqlite3_free(). If an OOM error occurs, NULL is returned.
+*/
+static char *sqlite3Fts5Strndup(int *pRc, const char *pIn, int nIn){
+ char *zRet = 0;
+ if( *pRc==SQLITE_OK ){
+ if( nIn<0 ){
+ nIn = (int)strlen(pIn);
+ }
+ zRet = (char*)sqlite3_malloc(nIn+1);
+ if( zRet ){
+ memcpy(zRet, pIn, nIn);
+ zRet[nIn] = '\0';
+ }else{
+ *pRc = SQLITE_NOMEM;
+ }
+ }
+ return zRet;
+}
+
+
+/*
+** Return true if character 't' may be part of an FTS5 bareword, or false
+** otherwise. Characters that may be part of barewords:
+**
+** * All non-ASCII characters,
+** * The 52 upper and lower case ASCII characters, and
+** * The 10 integer ASCII characters.
+** * The underscore character "_" (0x5F).
+** * The unicode "subsitute" character (0x1A).
+*/
+static int sqlite3Fts5IsBareword(char t){
+ u8 aBareword[128] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x00 .. 0x0F */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, /* 0x10 .. 0x1F */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x20 .. 0x2F */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, /* 0x30 .. 0x3F */
+ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x40 .. 0x4F */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, /* 0x50 .. 0x5F */
+ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x60 .. 0x6F */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0 /* 0x70 .. 0x7F */
+ };
+
+ return (t & 0x80) || aBareword[(int)t];
+}
+
+
+/*************************************************************************
+*/
+typedef struct Fts5TermsetEntry Fts5TermsetEntry;
+struct Fts5TermsetEntry {
+ char *pTerm;
+ int nTerm;
+ int iIdx; /* Index (main or aPrefix[] entry) */
+ Fts5TermsetEntry *pNext;
+};
+
+struct Fts5Termset {
+ Fts5TermsetEntry *apHash[512];
+};
+
+static int sqlite3Fts5TermsetNew(Fts5Termset **pp){
+ int rc = SQLITE_OK;
+ *pp = sqlite3Fts5MallocZero(&rc, sizeof(Fts5Termset));
+ return rc;
+}
+
+static int sqlite3Fts5TermsetAdd(
+ Fts5Termset *p,
+ int iIdx,
+ const char *pTerm, int nTerm,
+ int *pbPresent
+){
+ int rc = SQLITE_OK;
+ *pbPresent = 0;
+ if( p ){
+ int i;
+ u32 hash = 13;
+ Fts5TermsetEntry *pEntry;
+
+ /* Calculate a hash value for this term. This is the same hash checksum
+ ** used by the fts5_hash.c module. This is not important for correct
+ ** operation of the module, but is necessary to ensure that some tests
+ ** designed to produce hash table collisions really do work. */
+ for(i=nTerm-1; i>=0; i--){
+ hash = (hash << 3) ^ hash ^ pTerm[i];
+ }
+ hash = (hash << 3) ^ hash ^ iIdx;
+ hash = hash % ArraySize(p->apHash);
+
+ for(pEntry=p->apHash[hash]; pEntry; pEntry=pEntry->pNext){
+ if( pEntry->iIdx==iIdx
+ && pEntry->nTerm==nTerm
+ && memcmp(pEntry->pTerm, pTerm, nTerm)==0
+ ){
+ *pbPresent = 1;
+ break;
+ }
+ }
+
+ if( pEntry==0 ){
+ pEntry = sqlite3Fts5MallocZero(&rc, sizeof(Fts5TermsetEntry) + nTerm);
+ if( pEntry ){
+ pEntry->pTerm = (char*)&pEntry[1];
+ pEntry->nTerm = nTerm;
+ pEntry->iIdx = iIdx;
+ memcpy(pEntry->pTerm, pTerm, nTerm);
+ pEntry->pNext = p->apHash[hash];
+ p->apHash[hash] = pEntry;
+ }
+ }
+ }
+
+ return rc;
+}
+
+static void sqlite3Fts5TermsetFree(Fts5Termset *p){
+ if( p ){
+ u32 i;
+ for(i=0; i<ArraySize(p->apHash); i++){
+ Fts5TermsetEntry *pEntry = p->apHash[i];
+ while( pEntry ){
+ Fts5TermsetEntry *pDel = pEntry;
+ pEntry = pEntry->pNext;
+ sqlite3_free(pDel);
+ }
+ }
+ sqlite3_free(p);
+ }
+}
+
+/*
+** 2014 Jun 09
+**
+** 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 is an SQLite module implementing full-text search.
+*/
+
+
+/* #include "fts5Int.h" */
+
+#define FTS5_DEFAULT_PAGE_SIZE 4050
+#define FTS5_DEFAULT_AUTOMERGE 4
+#define FTS5_DEFAULT_USERMERGE 4
+#define FTS5_DEFAULT_CRISISMERGE 16
+#define FTS5_DEFAULT_HASHSIZE (1024*1024)
+
+/* Maximum allowed page size */
+#define FTS5_MAX_PAGE_SIZE (128*1024)
+
+static int fts5_iswhitespace(char x){
+ return (x==' ');
+}
+
+static int fts5_isopenquote(char x){
+ return (x=='"' || x=='\'' || x=='[' || x=='`');
+}
+
+/*
+** Argument pIn points to a character that is part of a nul-terminated
+** string. Return a pointer to the first character following *pIn in
+** the string that is not a white-space character.
+*/
+static const char *fts5ConfigSkipWhitespace(const char *pIn){
+ const char *p = pIn;
+ if( p ){
+ while( fts5_iswhitespace(*p) ){ p++; }
+ }
+ return p;
+}
+
+/*
+** Argument pIn points to a character that is part of a nul-terminated
+** string. Return a pointer to the first character following *pIn in
+** the string that is not a "bareword" character.
+*/
+static const char *fts5ConfigSkipBareword(const char *pIn){
+ const char *p = pIn;
+ while ( sqlite3Fts5IsBareword(*p) ) p++;
+ if( p==pIn ) p = 0;
+ return p;
+}
+
+static int fts5_isdigit(char a){
+ return (a>='0' && a<='9');
+}
+
+
+
+static const char *fts5ConfigSkipLiteral(const char *pIn){
+ const char *p = pIn;
+ switch( *p ){
+ case 'n': case 'N':
+ if( sqlite3_strnicmp("null", p, 4)==0 ){
+ p = &p[4];
+ }else{
+ p = 0;
+ }
+ break;
+
+ case 'x': case 'X':
+ p++;
+ if( *p=='\'' ){
+ p++;
+ while( (*p>='a' && *p<='f')
+ || (*p>='A' && *p<='F')
+ || (*p>='0' && *p<='9')
+ ){
+ p++;
+ }
+ if( *p=='\'' && 0==((p-pIn)%2) ){
+ p++;
+ }else{
+ p = 0;
+ }
+ }else{
+ p = 0;
+ }
+ break;
+
+ case '\'':
+ p++;
+ while( p ){
+ if( *p=='\'' ){
+ p++;
+ if( *p!='\'' ) break;
+ }
+ p++;
+ if( *p==0 ) p = 0;
+ }
+ break;
+
+ default:
+ /* maybe a number */
+ if( *p=='+' || *p=='-' ) p++;
+ while( fts5_isdigit(*p) ) p++;
+
+ /* At this point, if the literal was an integer, the parse is
+ ** finished. Or, if it is a floating point value, it may continue
+ ** with either a decimal point or an 'E' character. */
+ if( *p=='.' && fts5_isdigit(p[1]) ){
+ p += 2;
+ while( fts5_isdigit(*p) ) p++;
+ }
+ if( p==pIn ) p = 0;
+
+ break;
+ }
+
+ return p;
+}
+
+/*
+** The first character of the string pointed to by argument z is guaranteed
+** to be an open-quote character (see function fts5_isopenquote()).
+**
+** This function searches for the corresponding close-quote character within
+** the string and, if found, dequotes the string in place and adds a new
+** nul-terminator byte.
+**
+** If the close-quote is found, the value returned is the byte offset of
+** the character immediately following it. Or, if the close-quote is not
+** found, -1 is returned. If -1 is returned, the buffer is left in an
+** undefined state.
+*/
+static int fts5Dequote(char *z){
+ char q;
+ int iIn = 1;
+ int iOut = 0;
+ q = z[0];
+
+ /* Set stack variable q to the close-quote character */
+ assert( q=='[' || q=='\'' || q=='"' || q=='`' );
+ if( q=='[' ) q = ']';
+
+ while( ALWAYS(z[iIn]) ){
+ if( z[iIn]==q ){
+ if( z[iIn+1]!=q ){
+ /* Character iIn was the close quote. */
+ iIn++;
+ break;
+ }else{
+ /* Character iIn and iIn+1 form an escaped quote character. Skip
+ ** the input cursor past both and copy a single quote character
+ ** to the output buffer. */
+ iIn += 2;
+ z[iOut++] = q;
+ }
+ }else{
+ z[iOut++] = z[iIn++];
+ }
+ }
+
+ z[iOut] = '\0';
+ return iIn;
+}
+
+/*
+** Convert an SQL-style quoted string into a normal string by removing
+** the quote characters. The conversion is done in-place. If the
+** input does not begin with a quote character, then this routine
+** is a no-op.
+**
+** Examples:
+**
+** "abc" becomes abc
+** 'xyz' becomes xyz
+** [pqr] becomes pqr
+** `mno` becomes mno
+*/
+static void sqlite3Fts5Dequote(char *z){
+ char quote; /* Quote character (if any ) */
+
+ assert( 0==fts5_iswhitespace(z[0]) );
+ quote = z[0];
+ if( quote=='[' || quote=='\'' || quote=='"' || quote=='`' ){
+ fts5Dequote(z);
+ }
+}
+
+
+struct Fts5Enum {
+ const char *zName;
+ int eVal;
+};
+typedef struct Fts5Enum Fts5Enum;
+
+static int fts5ConfigSetEnum(
+ const Fts5Enum *aEnum,
+ const char *zEnum,
+ int *peVal
+){
+ int nEnum = (int)strlen(zEnum);
+ int i;
+ int iVal = -1;
+
+ for(i=0; aEnum[i].zName; i++){
+ if( sqlite3_strnicmp(aEnum[i].zName, zEnum, nEnum)==0 ){
+ if( iVal>=0 ) return SQLITE_ERROR;
+ iVal = aEnum[i].eVal;
+ }
+ }
+
+ *peVal = iVal;
+ return iVal<0 ? SQLITE_ERROR : SQLITE_OK;
+}
+
+/*
+** Parse a "special" CREATE VIRTUAL TABLE directive and update
+** configuration object pConfig as appropriate.
+**
+** If successful, object pConfig is updated and SQLITE_OK returned. If
+** an error occurs, an SQLite error code is returned and an error message
+** may be left in *pzErr. It is the responsibility of the caller to
+** eventually free any such error message using sqlite3_free().
+*/
+static int fts5ConfigParseSpecial(
+ Fts5Global *pGlobal,
+ Fts5Config *pConfig, /* Configuration object to update */
+ const char *zCmd, /* Special command to parse */
+ const char *zArg, /* Argument to parse */
+ char **pzErr /* OUT: Error message */
+){
+ int rc = SQLITE_OK;
+ int nCmd = (int)strlen(zCmd);
+ if( sqlite3_strnicmp("prefix", zCmd, nCmd)==0 ){
+ const int nByte = sizeof(int) * FTS5_MAX_PREFIX_INDEXES;
+ const char *p;
+ int bFirst = 1;
+ if( pConfig->aPrefix==0 ){
+ pConfig->aPrefix = sqlite3Fts5MallocZero(&rc, nByte);
+ if( rc ) return rc;
+ }
+
+ p = zArg;
+ while( 1 ){
+ int nPre = 0;
+
+ while( p[0]==' ' ) p++;
+ if( bFirst==0 && p[0]==',' ){
+ p++;
+ while( p[0]==' ' ) p++;
+ }else if( p[0]=='\0' ){
+ break;
+ }
+ if( p[0]<'0' || p[0]>'9' ){
+ *pzErr = sqlite3_mprintf("malformed prefix=... directive");
+ rc = SQLITE_ERROR;
+ break;
+ }
+
+ if( pConfig->nPrefix==FTS5_MAX_PREFIX_INDEXES ){
+ *pzErr = sqlite3_mprintf(
+ "too many prefix indexes (max %d)", FTS5_MAX_PREFIX_INDEXES
+ );
+ rc = SQLITE_ERROR;
+ break;
+ }
+
+ while( p[0]>='0' && p[0]<='9' && nPre<1000 ){
+ nPre = nPre*10 + (p[0] - '0');
+ p++;
+ }
+
+ if( nPre<=0 || nPre>=1000 ){
+ *pzErr = sqlite3_mprintf("prefix length out of range (max 999)");
+ rc = SQLITE_ERROR;
+ break;
+ }
+
+ pConfig->aPrefix[pConfig->nPrefix] = nPre;
+ pConfig->nPrefix++;
+ bFirst = 0;
+ }
+ assert( pConfig->nPrefix<=FTS5_MAX_PREFIX_INDEXES );
+ return rc;
+ }
+
+ if( sqlite3_strnicmp("tokenize", zCmd, nCmd)==0 ){
+ const char *p = (const char*)zArg;
+ int nArg = (int)strlen(zArg) + 1;
+ char **azArg = sqlite3Fts5MallocZero(&rc, sizeof(char*) * nArg);
+ char *pDel = sqlite3Fts5MallocZero(&rc, nArg * 2);
+ char *pSpace = pDel;
+
+ if( azArg && pSpace ){
+ if( pConfig->pTok ){
+ *pzErr = sqlite3_mprintf("multiple tokenize=... directives");
+ rc = SQLITE_ERROR;
+ }else{
+ for(nArg=0; p && *p; nArg++){
+ const char *p2 = fts5ConfigSkipWhitespace(p);
+ if( *p2=='\'' ){
+ p = fts5ConfigSkipLiteral(p2);
+ }else{
+ p = fts5ConfigSkipBareword(p2);
+ }
+ if( p ){
+ memcpy(pSpace, p2, p-p2);
+ azArg[nArg] = pSpace;
+ sqlite3Fts5Dequote(pSpace);
+ pSpace += (p - p2) + 1;
+ p = fts5ConfigSkipWhitespace(p);
+ }
+ }
+ if( p==0 ){
+ *pzErr = sqlite3_mprintf("parse error in tokenize directive");
+ rc = SQLITE_ERROR;
+ }else{
+ rc = sqlite3Fts5GetTokenizer(pGlobal,
+ (const char**)azArg, nArg, &pConfig->pTok, &pConfig->pTokApi,
+ pzErr
+ );
+ }
+ }
+ }
+
+ sqlite3_free(azArg);
+ sqlite3_free(pDel);
+ return rc;
+ }
+
+ if( sqlite3_strnicmp("content", zCmd, nCmd)==0 ){
+ if( pConfig->eContent!=FTS5_CONTENT_NORMAL ){
+ *pzErr = sqlite3_mprintf("multiple content=... directives");
+ rc = SQLITE_ERROR;
+ }else{
+ if( zArg[0] ){
+ pConfig->eContent = FTS5_CONTENT_EXTERNAL;
+ pConfig->zContent = sqlite3Fts5Mprintf(&rc, "%Q.%Q", pConfig->zDb,zArg);
+ }else{
+ pConfig->eContent = FTS5_CONTENT_NONE;
+ }
+ }
+ return rc;
+ }
+
+ if( sqlite3_strnicmp("content_rowid", zCmd, nCmd)==0 ){
+ if( pConfig->zContentRowid ){
+ *pzErr = sqlite3_mprintf("multiple content_rowid=... directives");
+ rc = SQLITE_ERROR;
+ }else{
+ pConfig->zContentRowid = sqlite3Fts5Strndup(&rc, zArg, -1);
+ }
+ return rc;
+ }
+
+ if( sqlite3_strnicmp("columnsize", zCmd, nCmd)==0 ){
+ if( (zArg[0]!='0' && zArg[0]!='1') || zArg[1]!='\0' ){
+ *pzErr = sqlite3_mprintf("malformed columnsize=... directive");
+ rc = SQLITE_ERROR;
+ }else{
+ pConfig->bColumnsize = (zArg[0]=='1');
+ }
+ return rc;
+ }
+
+ if( sqlite3_strnicmp("detail", zCmd, nCmd)==0 ){
+ const Fts5Enum aDetail[] = {
+ { "none", FTS5_DETAIL_NONE },
+ { "full", FTS5_DETAIL_FULL },
+ { "columns", FTS5_DETAIL_COLUMNS },
+ { 0, 0 }
+ };
+
+ if( (rc = fts5ConfigSetEnum(aDetail, zArg, &pConfig->eDetail)) ){
+ *pzErr = sqlite3_mprintf("malformed detail=... directive");
+ }
+ return rc;
+ }
+
+ *pzErr = sqlite3_mprintf("unrecognized option: \"%.*s\"", nCmd, zCmd);
+ return SQLITE_ERROR;
+}
+
+/*
+** Allocate an instance of the default tokenizer ("simple") at
+** Fts5Config.pTokenizer. Return SQLITE_OK if successful, or an SQLite error
+** code if an error occurs.
+*/
+static int fts5ConfigDefaultTokenizer(Fts5Global *pGlobal, Fts5Config *pConfig){
+ assert( pConfig->pTok==0 && pConfig->pTokApi==0 );
+ return sqlite3Fts5GetTokenizer(
+ pGlobal, 0, 0, &pConfig->pTok, &pConfig->pTokApi, 0
+ );
+}
+
+/*
+** Gobble up the first bareword or quoted word from the input buffer zIn.
+** Return a pointer to the character immediately following the last in
+** the gobbled word if successful, or a NULL pointer otherwise (failed
+** to find close-quote character).
+**
+** Before returning, set pzOut to point to a new buffer containing a
+** nul-terminated, dequoted copy of the gobbled word. If the word was
+** quoted, *pbQuoted is also set to 1 before returning.
+**
+** If *pRc is other than SQLITE_OK when this function is called, it is
+** a no-op (NULL is returned). Otherwise, if an OOM occurs within this
+** function, *pRc is set to SQLITE_NOMEM before returning. *pRc is *not*
+** set if a parse error (failed to find close quote) occurs.
+*/
+static const char *fts5ConfigGobbleWord(
+ int *pRc, /* IN/OUT: Error code */
+ const char *zIn, /* Buffer to gobble string/bareword from */
+ char **pzOut, /* OUT: malloc'd buffer containing str/bw */
+ int *pbQuoted /* OUT: Set to true if dequoting required */
+){
+ const char *zRet = 0;
+
+ int nIn = (int)strlen(zIn);
+ char *zOut = sqlite3_malloc(nIn+1);
+
+ assert( *pRc==SQLITE_OK );
+ *pbQuoted = 0;
+ *pzOut = 0;
+
+ if( zOut==0 ){
+ *pRc = SQLITE_NOMEM;
+ }else{
+ memcpy(zOut, zIn, nIn+1);
+ if( fts5_isopenquote(zOut[0]) ){
+ int ii = fts5Dequote(zOut);
+ zRet = &zIn[ii];
+ *pbQuoted = 1;
+ }else{
+ zRet = fts5ConfigSkipBareword(zIn);
+ if( zRet ){
+ zOut[zRet-zIn] = '\0';
+ }
+ }
+ }
+
+ if( zRet==0 ){
+ sqlite3_free(zOut);
+ }else{
+ *pzOut = zOut;
+ }
+
+ return zRet;
+}
+
+static int fts5ConfigParseColumn(
+ Fts5Config *p,
+ char *zCol,
+ char *zArg,
+ char **pzErr
+){
+ int rc = SQLITE_OK;
+ if( 0==sqlite3_stricmp(zCol, FTS5_RANK_NAME)
+ || 0==sqlite3_stricmp(zCol, FTS5_ROWID_NAME)
+ ){
+ *pzErr = sqlite3_mprintf("reserved fts5 column name: %s", zCol);
+ rc = SQLITE_ERROR;
+ }else if( zArg ){
+ if( 0==sqlite3_stricmp(zArg, "unindexed") ){
+ p->abUnindexed[p->nCol] = 1;
+ }else{
+ *pzErr = sqlite3_mprintf("unrecognized column option: %s", zArg);
+ rc = SQLITE_ERROR;
+ }
+ }
+
+ p->azCol[p->nCol++] = zCol;
+ return rc;
+}
+
+/*
+** Populate the Fts5Config.zContentExprlist string.
+*/
+static int fts5ConfigMakeExprlist(Fts5Config *p){
+ int i;
+ int rc = SQLITE_OK;
+ Fts5Buffer buf = {0, 0, 0};
+
+ sqlite3Fts5BufferAppendPrintf(&rc, &buf, "T.%Q", p->zContentRowid);
+ if( p->eContent!=FTS5_CONTENT_NONE ){
+ for(i=0; i<p->nCol; i++){
+ if( p->eContent==FTS5_CONTENT_EXTERNAL ){
+ sqlite3Fts5BufferAppendPrintf(&rc, &buf, ", T.%Q", p->azCol[i]);
+ }else{
+ sqlite3Fts5BufferAppendPrintf(&rc, &buf, ", T.c%d", i);
+ }
+ }
+ }
+
+ assert( p->zContentExprlist==0 );
+ p->zContentExprlist = (char*)buf.p;
+ return rc;
+}
+
+/*
+** Arguments nArg/azArg contain the string arguments passed to the xCreate
+** or xConnect method of the virtual table. This function attempts to
+** allocate an instance of Fts5Config containing the results of parsing
+** those arguments.
+**
+** If successful, SQLITE_OK is returned and *ppOut is set to point to the
+** new Fts5Config object. If an error occurs, an SQLite error code is
+** returned, *ppOut is set to NULL and an error message may be left in
+** *pzErr. It is the responsibility of the caller to eventually free any
+** such error message using sqlite3_free().
+*/
+static int sqlite3Fts5ConfigParse(
+ Fts5Global *pGlobal,
+ sqlite3 *db,
+ int nArg, /* Number of arguments */
+ const char **azArg, /* Array of nArg CREATE VIRTUAL TABLE args */
+ Fts5Config **ppOut, /* OUT: Results of parse */
+ char **pzErr /* OUT: Error message */
+){
+ int rc = SQLITE_OK; /* Return code */
+ Fts5Config *pRet; /* New object to return */
+ int i;
+ int nByte;
+
+ *ppOut = pRet = (Fts5Config*)sqlite3_malloc(sizeof(Fts5Config));
+ if( pRet==0 ) return SQLITE_NOMEM;
+ memset(pRet, 0, sizeof(Fts5Config));
+ pRet->db = db;
+ pRet->iCookie = -1;
+
+ nByte = nArg * (sizeof(char*) + sizeof(u8));
+ pRet->azCol = (char**)sqlite3Fts5MallocZero(&rc, nByte);
+ pRet->abUnindexed = (u8*)&pRet->azCol[nArg];
+ pRet->zDb = sqlite3Fts5Strndup(&rc, azArg[1], -1);
+ pRet->zName = sqlite3Fts5Strndup(&rc, azArg[2], -1);
+ pRet->bColumnsize = 1;
+ pRet->eDetail = FTS5_DETAIL_FULL;
+#ifdef SQLITE_DEBUG
+ pRet->bPrefixIndex = 1;
+#endif
+ if( rc==SQLITE_OK && sqlite3_stricmp(pRet->zName, FTS5_RANK_NAME)==0 ){
+ *pzErr = sqlite3_mprintf("reserved fts5 table name: %s", pRet->zName);
+ rc = SQLITE_ERROR;
+ }
+
+ for(i=3; rc==SQLITE_OK && i<nArg; i++){
+ const char *zOrig = azArg[i];
+ const char *z;
+ char *zOne = 0;
+ char *zTwo = 0;
+ int bOption = 0;
+ int bMustBeCol = 0;
+
+ z = fts5ConfigGobbleWord(&rc, zOrig, &zOne, &bMustBeCol);
+ z = fts5ConfigSkipWhitespace(z);
+ if( z && *z=='=' ){
+ bOption = 1;
+ z++;
+ if( bMustBeCol ) z = 0;
+ }
+ z = fts5ConfigSkipWhitespace(z);
+ if( z && z[0] ){
+ int bDummy;
+ z = fts5ConfigGobbleWord(&rc, z, &zTwo, &bDummy);
+ if( z && z[0] ) z = 0;
+ }
+
+ if( rc==SQLITE_OK ){
+ if( z==0 ){
+ *pzErr = sqlite3_mprintf("parse error in \"%s\"", zOrig);
+ rc = SQLITE_ERROR;
+ }else{
+ if( bOption ){
+ rc = fts5ConfigParseSpecial(pGlobal, pRet, zOne, zTwo?zTwo:"", pzErr);
+ }else{
+ rc = fts5ConfigParseColumn(pRet, zOne, zTwo, pzErr);
+ zOne = 0;
+ }
+ }
+ }
+
+ sqlite3_free(zOne);
+ sqlite3_free(zTwo);
+ }
+
+ /* If a tokenizer= option was successfully parsed, the tokenizer has
+ ** already been allocated. Otherwise, allocate an instance of the default
+ ** tokenizer (unicode61) now. */
+ if( rc==SQLITE_OK && pRet->pTok==0 ){
+ rc = fts5ConfigDefaultTokenizer(pGlobal, pRet);
+ }
+
+ /* If no zContent option was specified, fill in the default values. */
+ if( rc==SQLITE_OK && pRet->zContent==0 ){
+ const char *zTail = 0;
+ assert( pRet->eContent==FTS5_CONTENT_NORMAL
+ || pRet->eContent==FTS5_CONTENT_NONE
+ );
+ if( pRet->eContent==FTS5_CONTENT_NORMAL ){
+ zTail = "content";
+ }else if( pRet->bColumnsize ){
+ zTail = "docsize";
+ }
+
+ if( zTail ){
+ pRet->zContent = sqlite3Fts5Mprintf(
+ &rc, "%Q.'%q_%s'", pRet->zDb, pRet->zName, zTail
+ );
+ }
+ }
+
+ if( rc==SQLITE_OK && pRet->zContentRowid==0 ){
+ pRet->zContentRowid = sqlite3Fts5Strndup(&rc, "rowid", -1);
+ }
+
+ /* Formulate the zContentExprlist text */
+ if( rc==SQLITE_OK ){
+ rc = fts5ConfigMakeExprlist(pRet);
+ }
+
+ if( rc!=SQLITE_OK ){
+ sqlite3Fts5ConfigFree(pRet);
+ *ppOut = 0;
+ }
+ return rc;
+}
+
+/*
+** Free the configuration object passed as the only argument.
+*/
+static void sqlite3Fts5ConfigFree(Fts5Config *pConfig){
+ if( pConfig ){
+ int i;
+ if( pConfig->pTok ){
+ pConfig->pTokApi->xDelete(pConfig->pTok);
+ }
+ sqlite3_free(pConfig->zDb);
+ sqlite3_free(pConfig->zName);
+ for(i=0; i<pConfig->nCol; i++){
+ sqlite3_free(pConfig->azCol[i]);
+ }
+ sqlite3_free(pConfig->azCol);
+ sqlite3_free(pConfig->aPrefix);
+ sqlite3_free(pConfig->zRank);
+ sqlite3_free(pConfig->zRankArgs);
+ sqlite3_free(pConfig->zContent);
+ sqlite3_free(pConfig->zContentRowid);
+ sqlite3_free(pConfig->zContentExprlist);
+ sqlite3_free(pConfig);
+ }
+}
+
+/*
+** Call sqlite3_declare_vtab() based on the contents of the configuration
+** object passed as the only argument. Return SQLITE_OK if successful, or
+** an SQLite error code if an error occurs.
+*/
+static int sqlite3Fts5ConfigDeclareVtab(Fts5Config *pConfig){
+ int i;
+ int rc = SQLITE_OK;
+ char *zSql;
+
+ zSql = sqlite3Fts5Mprintf(&rc, "CREATE TABLE x(");
+ for(i=0; zSql && i<pConfig->nCol; i++){
+ const char *zSep = (i==0?"":", ");
+ zSql = sqlite3Fts5Mprintf(&rc, "%z%s%Q", zSql, zSep, pConfig->azCol[i]);
+ }
+ zSql = sqlite3Fts5Mprintf(&rc, "%z, %Q HIDDEN, %s HIDDEN)",
+ zSql, pConfig->zName, FTS5_RANK_NAME
+ );
+
+ assert( zSql || rc==SQLITE_NOMEM );
+ if( zSql ){
+ rc = sqlite3_declare_vtab(pConfig->db, zSql);
+ sqlite3_free(zSql);
+ }
+
+ return rc;
+}
+
+/*
+** Tokenize the text passed via the second and third arguments.
+**
+** The callback is invoked once for each token in the input text. The
+** arguments passed to it are, in order:
+**
+** void *pCtx // Copy of 4th argument to sqlite3Fts5Tokenize()
+** const char *pToken // Pointer to buffer containing token
+** int nToken // Size of token in bytes
+** int iStart // Byte offset of start of token within input text
+** int iEnd // Byte offset of end of token within input text
+** int iPos // Position of token in input (first token is 0)
+**
+** If the callback returns a non-zero value the tokenization is abandoned
+** and no further callbacks are issued.
+**
+** This function returns SQLITE_OK if successful or an SQLite error code
+** if an error occurs. If the tokenization was abandoned early because
+** the callback returned SQLITE_DONE, this is not an error and this function
+** still returns SQLITE_OK. Or, if the tokenization was abandoned early
+** because the callback returned another non-zero value, it is assumed
+** to be an SQLite error code and returned to the caller.
+*/
+static int sqlite3Fts5Tokenize(
+ Fts5Config *pConfig, /* FTS5 Configuration object */
+ int flags, /* FTS5_TOKENIZE_* flags */
+ const char *pText, int nText, /* Text to tokenize */
+ void *pCtx, /* Context passed to xToken() */
+ int (*xToken)(void*, int, const char*, int, int, int) /* Callback */
+){
+ if( pText==0 ) return SQLITE_OK;
+ return pConfig->pTokApi->xTokenize(
+ pConfig->pTok, pCtx, flags, pText, nText, xToken
+ );
+}
+
+/*
+** Argument pIn points to the first character in what is expected to be
+** a comma-separated list of SQL literals followed by a ')' character.
+** If it actually is this, return a pointer to the ')'. Otherwise, return
+** NULL to indicate a parse error.
+*/
+static const char *fts5ConfigSkipArgs(const char *pIn){
+ const char *p = pIn;
+
+ while( 1 ){
+ p = fts5ConfigSkipWhitespace(p);
+ p = fts5ConfigSkipLiteral(p);
+ p = fts5ConfigSkipWhitespace(p);
+ if( p==0 || *p==')' ) break;
+ if( *p!=',' ){
+ p = 0;
+ break;
+ }
+ p++;
+ }
+
+ return p;
+}
+
+/*
+** Parameter zIn contains a rank() function specification. The format of
+** this is:
+**
+** + Bareword (function name)
+** + Open parenthesis - "("
+** + Zero or more SQL literals in a comma separated list
+** + Close parenthesis - ")"
+*/
+static int sqlite3Fts5ConfigParseRank(
+ const char *zIn, /* Input string */
+ char **pzRank, /* OUT: Rank function name */
+ char **pzRankArgs /* OUT: Rank function arguments */
+){
+ const char *p = zIn;
+ const char *pRank;
+ char *zRank = 0;
+ char *zRankArgs = 0;
+ int rc = SQLITE_OK;
+
+ *pzRank = 0;
+ *pzRankArgs = 0;
+
+ if( p==0 ){
+ rc = SQLITE_ERROR;
+ }else{
+ p = fts5ConfigSkipWhitespace(p);
+ pRank = p;
+ p = fts5ConfigSkipBareword(p);
+
+ if( p ){
+ zRank = sqlite3Fts5MallocZero(&rc, 1 + p - pRank);
+ if( zRank ) memcpy(zRank, pRank, p-pRank);
+ }else{
+ rc = SQLITE_ERROR;
+ }
+
+ if( rc==SQLITE_OK ){
+ p = fts5ConfigSkipWhitespace(p);
+ if( *p!='(' ) rc = SQLITE_ERROR;
+ p++;
+ }
+ if( rc==SQLITE_OK ){
+ const char *pArgs;
+ p = fts5ConfigSkipWhitespace(p);
+ pArgs = p;
+ if( *p!=')' ){
+ p = fts5ConfigSkipArgs(p);
+ if( p==0 ){
+ rc = SQLITE_ERROR;
+ }else{
+ zRankArgs = sqlite3Fts5MallocZero(&rc, 1 + p - pArgs);
+ if( zRankArgs ) memcpy(zRankArgs, pArgs, p-pArgs);
+ }
+ }
+ }
+ }
+
+ if( rc!=SQLITE_OK ){
+ sqlite3_free(zRank);
+ assert( zRankArgs==0 );
+ }else{
+ *pzRank = zRank;
+ *pzRankArgs = zRankArgs;
+ }
+ return rc;
+}
+
+static int sqlite3Fts5ConfigSetValue(
+ Fts5Config *pConfig,
+ const char *zKey,
+ sqlite3_value *pVal,
+ int *pbBadkey
+){
+ int rc = SQLITE_OK;
+
+ if( 0==sqlite3_stricmp(zKey, "pgsz") ){
+ int pgsz = 0;
+ if( SQLITE_INTEGER==sqlite3_value_numeric_type(pVal) ){
+ pgsz = sqlite3_value_int(pVal);
+ }
+ if( pgsz<=0 || pgsz>FTS5_MAX_PAGE_SIZE ){
+ *pbBadkey = 1;
+ }else{
+ pConfig->pgsz = pgsz;
+ }
+ }
+
+ else if( 0==sqlite3_stricmp(zKey, "hashsize") ){
+ int nHashSize = -1;
+ if( SQLITE_INTEGER==sqlite3_value_numeric_type(pVal) ){
+ nHashSize = sqlite3_value_int(pVal);
+ }
+ if( nHashSize<=0 ){
+ *pbBadkey = 1;
+ }else{
+ pConfig->nHashSize = nHashSize;
+ }
+ }
+
+ else if( 0==sqlite3_stricmp(zKey, "automerge") ){
+ int nAutomerge = -1;
+ if( SQLITE_INTEGER==sqlite3_value_numeric_type(pVal) ){
+ nAutomerge = sqlite3_value_int(pVal);
+ }
+ if( nAutomerge<0 || nAutomerge>64 ){
+ *pbBadkey = 1;
+ }else{
+ if( nAutomerge==1 ) nAutomerge = FTS5_DEFAULT_AUTOMERGE;
+ pConfig->nAutomerge = nAutomerge;
+ }
+ }
+
+ else if( 0==sqlite3_stricmp(zKey, "usermerge") ){
+ int nUsermerge = -1;
+ if( SQLITE_INTEGER==sqlite3_value_numeric_type(pVal) ){
+ nUsermerge = sqlite3_value_int(pVal);
+ }
+ if( nUsermerge<2 || nUsermerge>16 ){
+ *pbBadkey = 1;
+ }else{
+ pConfig->nUsermerge = nUsermerge;
+ }
+ }
+
+ else if( 0==sqlite3_stricmp(zKey, "crisismerge") ){
+ int nCrisisMerge = -1;
+ if( SQLITE_INTEGER==sqlite3_value_numeric_type(pVal) ){
+ nCrisisMerge = sqlite3_value_int(pVal);
+ }
+ if( nCrisisMerge<0 ){
+ *pbBadkey = 1;
+ }else{
+ if( nCrisisMerge<=1 ) nCrisisMerge = FTS5_DEFAULT_CRISISMERGE;
+ pConfig->nCrisisMerge = nCrisisMerge;
+ }
+ }
+
+ else if( 0==sqlite3_stricmp(zKey, "rank") ){
+ const char *zIn = (const char*)sqlite3_value_text(pVal);
+ char *zRank;
+ char *zRankArgs;
+ rc = sqlite3Fts5ConfigParseRank(zIn, &zRank, &zRankArgs);
+ if( rc==SQLITE_OK ){
+ sqlite3_free(pConfig->zRank);
+ sqlite3_free(pConfig->zRankArgs);
+ pConfig->zRank = zRank;
+ pConfig->zRankArgs = zRankArgs;
+ }else if( rc==SQLITE_ERROR ){
+ rc = SQLITE_OK;
+ *pbBadkey = 1;
+ }
+ }else{
+ *pbBadkey = 1;
+ }
+ return rc;
+}
+
+/*
+** Load the contents of the %_config table into memory.
+*/
+static int sqlite3Fts5ConfigLoad(Fts5Config *pConfig, int iCookie){
+ const char *zSelect = "SELECT k, v FROM %Q.'%q_config'";
+ char *zSql;
+ sqlite3_stmt *p = 0;
+ int rc = SQLITE_OK;
+ int iVersion = 0;
+
+ /* Set default values */
+ pConfig->pgsz = FTS5_DEFAULT_PAGE_SIZE;
+ pConfig->nAutomerge = FTS5_DEFAULT_AUTOMERGE;
+ pConfig->nUsermerge = FTS5_DEFAULT_USERMERGE;
+ pConfig->nCrisisMerge = FTS5_DEFAULT_CRISISMERGE;
+ pConfig->nHashSize = FTS5_DEFAULT_HASHSIZE;
+
+ zSql = sqlite3Fts5Mprintf(&rc, zSelect, pConfig->zDb, pConfig->zName);
+ if( zSql ){
+ rc = sqlite3_prepare_v2(pConfig->db, zSql, -1, &p, 0);
+ sqlite3_free(zSql);
+ }
+
+ assert( rc==SQLITE_OK || p==0 );
+ if( rc==SQLITE_OK ){
+ while( SQLITE_ROW==sqlite3_step(p) ){
+ const char *zK = (const char*)sqlite3_column_text(p, 0);
+ sqlite3_value *pVal = sqlite3_column_value(p, 1);
+ if( 0==sqlite3_stricmp(zK, "version") ){
+ iVersion = sqlite3_value_int(pVal);
+ }else{
+ int bDummy = 0;
+ sqlite3Fts5ConfigSetValue(pConfig, zK, pVal, &bDummy);
+ }
+ }
+ rc = sqlite3_finalize(p);
+ }
+
+ if( rc==SQLITE_OK && iVersion!=FTS5_CURRENT_VERSION ){
+ rc = SQLITE_ERROR;
+ if( pConfig->pzErrmsg ){
+ assert( 0==*pConfig->pzErrmsg );
+ *pConfig->pzErrmsg = sqlite3_mprintf(
+ "invalid fts5 file format (found %d, expected %d) - run 'rebuild'",
+ iVersion, FTS5_CURRENT_VERSION
+ );
+ }
+ }
+
+ if( rc==SQLITE_OK ){
+ pConfig->iCookie = iCookie;
+ }
+ return rc;
+}
+
+/*
+** 2014 May 31
+**
+** 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.
+**
+******************************************************************************
+**
+*/
+
+
+
+/* #include "fts5Int.h" */
+/* #include "fts5parse.h" */
+
+/*
+** All token types in the generated fts5parse.h file are greater than 0.
+*/
+#define FTS5_EOF 0
+
+#define FTS5_LARGEST_INT64 (0xffffffff|(((i64)0x7fffffff)<<32))
+
+typedef struct Fts5ExprTerm Fts5ExprTerm;
+
+/*
+** Functions generated by lemon from fts5parse.y.
+*/
+static void *sqlite3Fts5ParserAlloc(void *(*mallocProc)(u64));
+static void sqlite3Fts5ParserFree(void*, void (*freeProc)(void*));
+static void sqlite3Fts5Parser(void*, int, Fts5Token, Fts5Parse*);
+#ifndef NDEBUG
+/* #include <stdio.h> */
+static void sqlite3Fts5ParserTrace(FILE*, char*);
+#endif
+
+
+struct Fts5Expr {
+ Fts5Index *pIndex;
+ Fts5Config *pConfig;
+ Fts5ExprNode *pRoot;
+ int bDesc; /* Iterate in descending rowid order */
+ int nPhrase; /* Number of phrases in expression */
+ Fts5ExprPhrase **apExprPhrase; /* Pointers to phrase objects */
+};
+
+/*
+** eType:
+** Expression node type. Always one of:
+**
+** FTS5_AND (nChild, apChild valid)
+** FTS5_OR (nChild, apChild valid)
+** FTS5_NOT (nChild, apChild valid)
+** FTS5_STRING (pNear valid)
+** FTS5_TERM (pNear valid)
+*/
+struct Fts5ExprNode {
+ int eType; /* Node type */
+ int bEof; /* True at EOF */
+ int bNomatch; /* True if entry is not a match */
+
+ /* Next method for this node. */
+ int (*xNext)(Fts5Expr*, Fts5ExprNode*, int, i64);
+
+ i64 iRowid; /* Current rowid */
+ Fts5ExprNearset *pNear; /* For FTS5_STRING - cluster of phrases */
+
+ /* Child nodes. For a NOT node, this array always contains 2 entries. For
+ ** AND or OR nodes, it contains 2 or more entries. */
+ int nChild; /* Number of child nodes */
+ Fts5ExprNode *apChild[1]; /* Array of child nodes */
+};
+
+#define Fts5NodeIsString(p) ((p)->eType==FTS5_TERM || (p)->eType==FTS5_STRING)
+
+/*
+** Invoke the xNext method of an Fts5ExprNode object. This macro should be
+** used as if it has the same signature as the xNext() methods themselves.
+*/
+#define fts5ExprNodeNext(a,b,c,d) (b)->xNext((a), (b), (c), (d))
+
+/*
+** An instance of the following structure represents a single search term
+** or term prefix.
+*/
+struct Fts5ExprTerm {
+ int bPrefix; /* True for a prefix term */
+ char *zTerm; /* nul-terminated term */
+ Fts5IndexIter *pIter; /* Iterator for this term */
+ Fts5ExprTerm *pSynonym; /* Pointer to first in list of synonyms */
+};
+
+/*
+** A phrase. One or more terms that must appear in a contiguous sequence
+** within a document for it to match.
+*/
+struct Fts5ExprPhrase {
+ Fts5ExprNode *pNode; /* FTS5_STRING node this phrase is part of */
+ Fts5Buffer poslist; /* Current position list */
+ int nTerm; /* Number of entries in aTerm[] */
+ Fts5ExprTerm aTerm[1]; /* Terms that make up this phrase */
+};
+
+/*
+** One or more phrases that must appear within a certain token distance of
+** each other within each matching document.
+*/
+struct Fts5ExprNearset {
+ int nNear; /* NEAR parameter */
+ Fts5Colset *pColset; /* Columns to search (NULL -> all columns) */
+ int nPhrase; /* Number of entries in aPhrase[] array */
+ Fts5ExprPhrase *apPhrase[1]; /* Array of phrase pointers */
+};
+
+
+/*
+** Parse context.
+*/
+struct Fts5Parse {
+ Fts5Config *pConfig;
+ char *zErr;
+ int rc;
+ int nPhrase; /* Size of apPhrase array */
+ Fts5ExprPhrase **apPhrase; /* Array of all phrases */
+ Fts5ExprNode *pExpr; /* Result of a successful parse */
+};
+
+static void sqlite3Fts5ParseError(Fts5Parse *pParse, const char *zFmt, ...){
+ va_list ap;
+ va_start(ap, zFmt);
+ if( pParse->rc==SQLITE_OK ){
+ pParse->zErr = sqlite3_vmprintf(zFmt, ap);
+ pParse->rc = SQLITE_ERROR;
+ }
+ va_end(ap);
+}
+
+static int fts5ExprIsspace(char t){
+ return t==' ' || t=='\t' || t=='\n' || t=='\r';
+}
+
+/*
+** Read the first token from the nul-terminated string at *pz.
+*/
+static int fts5ExprGetToken(
+ Fts5Parse *pParse,
+ const char **pz, /* IN/OUT: Pointer into buffer */
+ Fts5Token *pToken
+){
+ const char *z = *pz;
+ int tok;
+
+ /* Skip past any whitespace */
+ while( fts5ExprIsspace(*z) ) z++;
+
+ pToken->p = z;
+ pToken->n = 1;
+ switch( *z ){
+ case '(': tok = FTS5_LP; break;
+ case ')': tok = FTS5_RP; break;
+ case '{': tok = FTS5_LCP; break;
+ case '}': tok = FTS5_RCP; break;
+ case ':': tok = FTS5_COLON; break;
+ case ',': tok = FTS5_COMMA; break;
+ case '+': tok = FTS5_PLUS; break;
+ case '*': tok = FTS5_STAR; break;
+ case '\0': tok = FTS5_EOF; break;
+
+ case '"': {
+ const char *z2;
+ tok = FTS5_STRING;
+
+ for(z2=&z[1]; 1; z2++){
+ if( z2[0]=='"' ){
+ z2++;
+ if( z2[0]!='"' ) break;
+ }
+ if( z2[0]=='\0' ){
+ sqlite3Fts5ParseError(pParse, "unterminated string");
+ return FTS5_EOF;
+ }
+ }
+ pToken->n = (z2 - z);
+ break;
+ }
+
+ default: {
+ const char *z2;
+ if( sqlite3Fts5IsBareword(z[0])==0 ){
+ sqlite3Fts5ParseError(pParse, "fts5: syntax error near \"%.1s\"", z);
+ return FTS5_EOF;
+ }
+ tok = FTS5_STRING;
+ for(z2=&z[1]; sqlite3Fts5IsBareword(*z2); z2++);
+ pToken->n = (z2 - z);
+ if( pToken->n==2 && memcmp(pToken->p, "OR", 2)==0 ) tok = FTS5_OR;
+ if( pToken->n==3 && memcmp(pToken->p, "NOT", 3)==0 ) tok = FTS5_NOT;
+ if( pToken->n==3 && memcmp(pToken->p, "AND", 3)==0 ) tok = FTS5_AND;
+ break;
+ }
+ }
+
+ *pz = &pToken->p[pToken->n];
+ return tok;
+}
+
+static void *fts5ParseAlloc(u64 t){ return sqlite3_malloc((int)t); }
+static void fts5ParseFree(void *p){ sqlite3_free(p); }
+
+static int sqlite3Fts5ExprNew(
+ Fts5Config *pConfig, /* FTS5 Configuration */
+ const char *zExpr, /* Expression text */
+ Fts5Expr **ppNew,
+ char **pzErr
+){
+ Fts5Parse sParse;
+ Fts5Token token;
+ const char *z = zExpr;
+ int t; /* Next token type */
+ void *pEngine;
+ Fts5Expr *pNew;
+
+ *ppNew = 0;
+ *pzErr = 0;
+ memset(&sParse, 0, sizeof(sParse));
+ pEngine = sqlite3Fts5ParserAlloc(fts5ParseAlloc);
+ if( pEngine==0 ){ return SQLITE_NOMEM; }
+ sParse.pConfig = pConfig;
+
+ do {
+ t = fts5ExprGetToken(&sParse, &z, &token);
+ sqlite3Fts5Parser(pEngine, t, token, &sParse);
+ }while( sParse.rc==SQLITE_OK && t!=FTS5_EOF );
+ sqlite3Fts5ParserFree(pEngine, fts5ParseFree);
+
+ assert( sParse.rc!=SQLITE_OK || sParse.zErr==0 );
+ if( sParse.rc==SQLITE_OK ){
+ *ppNew = pNew = sqlite3_malloc(sizeof(Fts5Expr));
+ if( pNew==0 ){
+ sParse.rc = SQLITE_NOMEM;
+ sqlite3Fts5ParseNodeFree(sParse.pExpr);
+ }else{
+ if( !sParse.pExpr ){
+ const int nByte = sizeof(Fts5ExprNode);
+ pNew->pRoot = (Fts5ExprNode*)sqlite3Fts5MallocZero(&sParse.rc, nByte);
+ if( pNew->pRoot ){
+ pNew->pRoot->bEof = 1;
+ }
+ }else{
+ pNew->pRoot = sParse.pExpr;
+ }
+ pNew->pIndex = 0;
+ pNew->pConfig = pConfig;
+ pNew->apExprPhrase = sParse.apPhrase;
+ pNew->nPhrase = sParse.nPhrase;
+ sParse.apPhrase = 0;
+ }
+ }else{
+ sqlite3Fts5ParseNodeFree(sParse.pExpr);
+ }
+
+ sqlite3_free(sParse.apPhrase);
+ *pzErr = sParse.zErr;
+ return sParse.rc;
+}
+
+/*
+** Free the expression node object passed as the only argument.
+*/
+static void sqlite3Fts5ParseNodeFree(Fts5ExprNode *p){
+ if( p ){
+ int i;
+ for(i=0; i<p->nChild; i++){
+ sqlite3Fts5ParseNodeFree(p->apChild[i]);
+ }
+ sqlite3Fts5ParseNearsetFree(p->pNear);
+ sqlite3_free(p);
+ }
+}
+
+/*
+** Free the expression object passed as the only argument.
+*/
+static void sqlite3Fts5ExprFree(Fts5Expr *p){
+ if( p ){
+ sqlite3Fts5ParseNodeFree(p->pRoot);
+ sqlite3_free(p->apExprPhrase);
+ sqlite3_free(p);
+ }
+}
+
+/*
+** Argument pTerm must be a synonym iterator. Return the current rowid
+** that it points to.
+*/
+static i64 fts5ExprSynonymRowid(Fts5ExprTerm *pTerm, int bDesc, int *pbEof){
+ i64 iRet = 0;
+ int bRetValid = 0;
+ Fts5ExprTerm *p;
+
+ assert( pTerm->pSynonym );
+ assert( bDesc==0 || bDesc==1 );
+ for(p=pTerm; p; p=p->pSynonym){
+ if( 0==sqlite3Fts5IterEof(p->pIter) ){
+ i64 iRowid = p->pIter->iRowid;
+ if( bRetValid==0 || (bDesc!=(iRowid<iRet)) ){
+ iRet = iRowid;
+ bRetValid = 1;
+ }
+ }
+ }
+
+ if( pbEof && bRetValid==0 ) *pbEof = 1;
+ return iRet;
+}
+
+/*
+** Argument pTerm must be a synonym iterator.
+*/
+static int fts5ExprSynonymList(
+ Fts5ExprTerm *pTerm,
+ i64 iRowid,
+ Fts5Buffer *pBuf, /* Use this buffer for space if required */
+ u8 **pa, int *pn
+){
+ Fts5PoslistReader aStatic[4];
+ Fts5PoslistReader *aIter = aStatic;
+ int nIter = 0;
+ int nAlloc = 4;
+ int rc = SQLITE_OK;
+ Fts5ExprTerm *p;
+
+ assert( pTerm->pSynonym );
+ for(p=pTerm; p; p=p->pSynonym){
+ Fts5IndexIter *pIter = p->pIter;
+ if( sqlite3Fts5IterEof(pIter)==0 && pIter->iRowid==iRowid ){
+ if( pIter->nData==0 ) continue;
+ if( nIter==nAlloc ){
+ int nByte = sizeof(Fts5PoslistReader) * nAlloc * 2;
+ Fts5PoslistReader *aNew = (Fts5PoslistReader*)sqlite3_malloc(nByte);
+ if( aNew==0 ){
+ rc = SQLITE_NOMEM;
+ goto synonym_poslist_out;
+ }
+ memcpy(aNew, aIter, sizeof(Fts5PoslistReader) * nIter);
+ nAlloc = nAlloc*2;
+ if( aIter!=aStatic ) sqlite3_free(aIter);
+ aIter = aNew;
+ }
+ sqlite3Fts5PoslistReaderInit(pIter->pData, pIter->nData, &aIter[nIter]);
+ assert( aIter[nIter].bEof==0 );
+ nIter++;
+ }
+ }
+
+ if( nIter==1 ){
+ *pa = (u8*)aIter[0].a;
+ *pn = aIter[0].n;
+ }else{
+ Fts5PoslistWriter writer = {0};
+ i64 iPrev = -1;
+ fts5BufferZero(pBuf);
+ while( 1 ){
+ int i;
+ i64 iMin = FTS5_LARGEST_INT64;
+ for(i=0; i<nIter; i++){
+ if( aIter[i].bEof==0 ){
+ if( aIter[i].iPos==iPrev ){
+ if( sqlite3Fts5PoslistReaderNext(&aIter[i]) ) continue;
+ }
+ if( aIter[i].iPos<iMin ){
+ iMin = aIter[i].iPos;
+ }
+ }
+ }
+ if( iMin==FTS5_LARGEST_INT64 || rc!=SQLITE_OK ) break;
+ rc = sqlite3Fts5PoslistWriterAppend(pBuf, &writer, iMin);
+ iPrev = iMin;
+ }
+ if( rc==SQLITE_OK ){
+ *pa = pBuf->p;
+ *pn = pBuf->n;
+ }
+ }
+
+ synonym_poslist_out:
+ if( aIter!=aStatic ) sqlite3_free(aIter);
+ return rc;
+}
+
+
+/*
+** All individual term iterators in pPhrase are guaranteed to be valid and
+** pointing to the same rowid when this function is called. This function
+** checks if the current rowid really is a match, and if so populates
+** the pPhrase->poslist buffer accordingly. Output parameter *pbMatch
+** is set to true if this is really a match, or false otherwise.
+**
+** SQLITE_OK is returned if an error occurs, or an SQLite error code
+** otherwise. It is not considered an error code if the current rowid is
+** not a match.
+*/
+static int fts5ExprPhraseIsMatch(
+ Fts5ExprNode *pNode, /* Node pPhrase belongs to */
+ Fts5ExprPhrase *pPhrase, /* Phrase object to initialize */
+ int *pbMatch /* OUT: Set to true if really a match */
+){
+ Fts5PoslistWriter writer = {0};
+ Fts5PoslistReader aStatic[4];
+ Fts5PoslistReader *aIter = aStatic;
+ int i;
+ int rc = SQLITE_OK;
+
+ fts5BufferZero(&pPhrase->poslist);
+
+ /* If the aStatic[] array is not large enough, allocate a large array
+ ** using sqlite3_malloc(). This approach could be improved upon. */
+ if( pPhrase->nTerm>ArraySize(aStatic) ){
+ int nByte = sizeof(Fts5PoslistReader) * pPhrase->nTerm;
+ aIter = (Fts5PoslistReader*)sqlite3_malloc(nByte);
+ if( !aIter ) return SQLITE_NOMEM;
+ }
+ memset(aIter, 0, sizeof(Fts5PoslistReader) * pPhrase->nTerm);
+
+ /* Initialize a term iterator for each term in the phrase */
+ for(i=0; i<pPhrase->nTerm; i++){
+ Fts5ExprTerm *pTerm = &pPhrase->aTerm[i];
+ int n = 0;
+ int bFlag = 0;
+ u8 *a = 0;
+ if( pTerm->pSynonym ){
+ Fts5Buffer buf = {0, 0, 0};
+ rc = fts5ExprSynonymList(pTerm, pNode->iRowid, &buf, &a, &n);
+ if( rc ){
+ sqlite3_free(a);
+ goto ismatch_out;
+ }
+ if( a==buf.p ) bFlag = 1;
+ }else{
+ a = (u8*)pTerm->pIter->pData;
+ n = pTerm->pIter->nData;
+ }
+ sqlite3Fts5PoslistReaderInit(a, n, &aIter[i]);
+ aIter[i].bFlag = (u8)bFlag;
+ if( aIter[i].bEof ) goto ismatch_out;
+ }
+
+ while( 1 ){
+ int bMatch;
+ i64 iPos = aIter[0].iPos;
+ do {
+ bMatch = 1;
+ for(i=0; i<pPhrase->nTerm; i++){
+ Fts5PoslistReader *pPos = &aIter[i];
+ i64 iAdj = iPos + i;
+ if( pPos->iPos!=iAdj ){
+ bMatch = 0;
+ while( pPos->iPos<iAdj ){
+ if( sqlite3Fts5PoslistReaderNext(pPos) ) goto ismatch_out;
+ }
+ if( pPos->iPos>iAdj ) iPos = pPos->iPos-i;
+ }
+ }
+ }while( bMatch==0 );
+
+ /* Append position iPos to the output */
+ rc = sqlite3Fts5PoslistWriterAppend(&pPhrase->poslist, &writer, iPos);
+ if( rc!=SQLITE_OK ) goto ismatch_out;
+
+ for(i=0; i<pPhrase->nTerm; i++){
+ if( sqlite3Fts5PoslistReaderNext(&aIter[i]) ) goto ismatch_out;
+ }
+ }
+
+ ismatch_out:
+ *pbMatch = (pPhrase->poslist.n>0);
+ for(i=0; i<pPhrase->nTerm; i++){
+ if( aIter[i].bFlag ) sqlite3_free((u8*)aIter[i].a);
+ }
+ if( aIter!=aStatic ) sqlite3_free(aIter);
+ return rc;
+}
+
+typedef struct Fts5LookaheadReader Fts5LookaheadReader;
+struct Fts5LookaheadReader {
+ const u8 *a; /* Buffer containing position list */
+ int n; /* Size of buffer a[] in bytes */
+ int i; /* Current offset in position list */
+ i64 iPos; /* Current position */
+ i64 iLookahead; /* Next position */
+};
+
+#define FTS5_LOOKAHEAD_EOF (((i64)1) << 62)
+
+static int fts5LookaheadReaderNext(Fts5LookaheadReader *p){
+ p->iPos = p->iLookahead;
+ if( sqlite3Fts5PoslistNext64(p->a, p->n, &p->i, &p->iLookahead) ){
+ p->iLookahead = FTS5_LOOKAHEAD_EOF;
+ }
+ return (p->iPos==FTS5_LOOKAHEAD_EOF);
+}
+
+static int fts5LookaheadReaderInit(
+ const u8 *a, int n, /* Buffer to read position list from */
+ Fts5LookaheadReader *p /* Iterator object to initialize */
+){
+ memset(p, 0, sizeof(Fts5LookaheadReader));
+ p->a = a;
+ p->n = n;
+ fts5LookaheadReaderNext(p);
+ return fts5LookaheadReaderNext(p);
+}
+
+typedef struct Fts5NearTrimmer Fts5NearTrimmer;
+struct Fts5NearTrimmer {
+ Fts5LookaheadReader reader; /* Input iterator */
+ Fts5PoslistWriter writer; /* Writer context */
+ Fts5Buffer *pOut; /* Output poslist */
+};
+
+/*
+** The near-set object passed as the first argument contains more than
+** one phrase. All phrases currently point to the same row. The
+** Fts5ExprPhrase.poslist buffers are populated accordingly. This function
+** tests if the current row contains instances of each phrase sufficiently
+** close together to meet the NEAR constraint. Non-zero is returned if it
+** does, or zero otherwise.
+**
+** If in/out parameter (*pRc) is set to other than SQLITE_OK when this
+** function is called, it is a no-op. Or, if an error (e.g. SQLITE_NOMEM)
+** occurs within this function (*pRc) is set accordingly before returning.
+** The return value is undefined in both these cases.
+**
+** If no error occurs and non-zero (a match) is returned, the position-list
+** of each phrase object is edited to contain only those entries that
+** meet the constraint before returning.
+*/
+static int fts5ExprNearIsMatch(int *pRc, Fts5ExprNearset *pNear){
+ Fts5NearTrimmer aStatic[4];
+ Fts5NearTrimmer *a = aStatic;
+ Fts5ExprPhrase **apPhrase = pNear->apPhrase;
+
+ int i;
+ int rc = *pRc;
+ int bMatch;
+
+ assert( pNear->nPhrase>1 );
+
+ /* If the aStatic[] array is not large enough, allocate a large array
+ ** using sqlite3_malloc(). This approach could be improved upon. */
+ if( pNear->nPhrase>ArraySize(aStatic) ){
+ int nByte = sizeof(Fts5NearTrimmer) * pNear->nPhrase;
+ a = (Fts5NearTrimmer*)sqlite3Fts5MallocZero(&rc, nByte);
+ }else{
+ memset(aStatic, 0, sizeof(aStatic));
+ }
+ if( rc!=SQLITE_OK ){
+ *pRc = rc;
+ return 0;
+ }
+
+ /* Initialize a lookahead iterator for each phrase. After passing the
+ ** buffer and buffer size to the lookaside-reader init function, zero
+ ** the phrase poslist buffer. The new poslist for the phrase (containing
+ ** the same entries as the original with some entries removed on account
+ ** of the NEAR constraint) is written over the original even as it is
+ ** being read. This is safe as the entries for the new poslist are a
+ ** subset of the old, so it is not possible for data yet to be read to
+ ** be overwritten. */
+ for(i=0; i<pNear->nPhrase; i++){
+ Fts5Buffer *pPoslist = &apPhrase[i]->poslist;
+ fts5LookaheadReaderInit(pPoslist->p, pPoslist->n, &a[i].reader);
+ pPoslist->n = 0;
+ a[i].pOut = pPoslist;
+ }
+
+ while( 1 ){
+ int iAdv;
+ i64 iMin;
+ i64 iMax;
+
+ /* This block advances the phrase iterators until they point to a set of
+ ** entries that together comprise a match. */
+ iMax = a[0].reader.iPos;
+ do {
+ bMatch = 1;
+ for(i=0; i<pNear->nPhrase; i++){
+ Fts5LookaheadReader *pPos = &a[i].reader;
+ iMin = iMax - pNear->apPhrase[i]->nTerm - pNear->nNear;
+ if( pPos->iPos<iMin || pPos->iPos>iMax ){
+ bMatch = 0;
+ while( pPos->iPos<iMin ){
+ if( fts5LookaheadReaderNext(pPos) ) goto ismatch_out;
+ }
+ if( pPos->iPos>iMax ) iMax = pPos->iPos;
+ }
+ }
+ }while( bMatch==0 );
+
+ /* Add an entry to each output position list */
+ for(i=0; i<pNear->nPhrase; i++){
+ i64 iPos = a[i].reader.iPos;
+ Fts5PoslistWriter *pWriter = &a[i].writer;
+ if( a[i].pOut->n==0 || iPos!=pWriter->iPrev ){
+ sqlite3Fts5PoslistWriterAppend(a[i].pOut, pWriter, iPos);
+ }
+ }
+
+ iAdv = 0;
+ iMin = a[0].reader.iLookahead;
+ for(i=0; i<pNear->nPhrase; i++){
+ if( a[i].reader.iLookahead < iMin ){
+ iMin = a[i].reader.iLookahead;
+ iAdv = i;
+ }
+ }
+ if( fts5LookaheadReaderNext(&a[iAdv].reader) ) goto ismatch_out;
+ }
+
+ ismatch_out: {
+ int bRet = a[0].pOut->n>0;
+ *pRc = rc;
+ if( a!=aStatic ) sqlite3_free(a);
+ return bRet;
+ }
+}
+
+/*
+** Advance iterator pIter until it points to a value equal to or laster
+** than the initial value of *piLast. If this means the iterator points
+** to a value laster than *piLast, update *piLast to the new lastest value.
+**
+** If the iterator reaches EOF, set *pbEof to true before returning. If
+** an error occurs, set *pRc to an error code. If either *pbEof or *pRc
+** are set, return a non-zero value. Otherwise, return zero.
+*/
+static int fts5ExprAdvanceto(
+ Fts5IndexIter *pIter, /* Iterator to advance */
+ int bDesc, /* True if iterator is "rowid DESC" */
+ i64 *piLast, /* IN/OUT: Lastest rowid seen so far */
+ int *pRc, /* OUT: Error code */
+ int *pbEof /* OUT: Set to true if EOF */
+){
+ i64 iLast = *piLast;
+ i64 iRowid;
+
+ iRowid = pIter->iRowid;
+ if( (bDesc==0 && iLast>iRowid) || (bDesc && iLast<iRowid) ){
+ int rc = sqlite3Fts5IterNextFrom(pIter, iLast);
+ if( rc || sqlite3Fts5IterEof(pIter) ){
+ *pRc = rc;
+ *pbEof = 1;
+ return 1;
+ }
+ iRowid = pIter->iRowid;
+ assert( (bDesc==0 && iRowid>=iLast) || (bDesc==1 && iRowid<=iLast) );
+ }
+ *piLast = iRowid;
+
+ return 0;
+}
+
+static int fts5ExprSynonymAdvanceto(
+ Fts5ExprTerm *pTerm, /* Term iterator to advance */
+ int bDesc, /* True if iterator is "rowid DESC" */
+ i64 *piLast, /* IN/OUT: Lastest rowid seen so far */
+ int *pRc /* OUT: Error code */
+){
+ int rc = SQLITE_OK;
+ i64 iLast = *piLast;
+ Fts5ExprTerm *p;
+ int bEof = 0;
+
+ for(p=pTerm; rc==SQLITE_OK && p; p=p->pSynonym){
+ if( sqlite3Fts5IterEof(p->pIter)==0 ){
+ i64 iRowid = p->pIter->iRowid;
+ if( (bDesc==0 && iLast>iRowid) || (bDesc && iLast<iRowid) ){
+ rc = sqlite3Fts5IterNextFrom(p->pIter, iLast);
+ }
+ }
+ }
+
+ if( rc!=SQLITE_OK ){
+ *pRc = rc;
+ bEof = 1;
+ }else{
+ *piLast = fts5ExprSynonymRowid(pTerm, bDesc, &bEof);
+ }
+ return bEof;
+}
+
+
+static int fts5ExprNearTest(
+ int *pRc,
+ Fts5Expr *pExpr, /* Expression that pNear is a part of */
+ Fts5ExprNode *pNode /* The "NEAR" node (FTS5_STRING) */
+){
+ Fts5ExprNearset *pNear = pNode->pNear;
+ int rc = *pRc;
+
+ if( pExpr->pConfig->eDetail!=FTS5_DETAIL_FULL ){
+ Fts5ExprTerm *pTerm;
+ Fts5ExprPhrase *pPhrase = pNear->apPhrase[0];
+ pPhrase->poslist.n = 0;
+ for(pTerm=&pPhrase->aTerm[0]; pTerm; pTerm=pTerm->pSynonym){
+ Fts5IndexIter *pIter = pTerm->pIter;
+ if( sqlite3Fts5IterEof(pIter)==0 ){
+ if( pIter->iRowid==pNode->iRowid && pIter->nData>0 ){
+ pPhrase->poslist.n = 1;
+ }
+ }
+ }
+ return pPhrase->poslist.n;
+ }else{
+ int i;
+
+ /* Check that each phrase in the nearset matches the current row.
+ ** Populate the pPhrase->poslist buffers at the same time. If any
+ ** phrase is not a match, break out of the loop early. */
+ for(i=0; rc==SQLITE_OK && i<pNear->nPhrase; i++){
+ Fts5ExprPhrase *pPhrase = pNear->apPhrase[i];
+ if( pPhrase->nTerm>1 || pPhrase->aTerm[0].pSynonym || pNear->pColset ){
+ int bMatch = 0;
+ rc = fts5ExprPhraseIsMatch(pNode, pPhrase, &bMatch);
+ if( bMatch==0 ) break;
+ }else{
+ Fts5IndexIter *pIter = pPhrase->aTerm[0].pIter;
+ fts5BufferSet(&rc, &pPhrase->poslist, pIter->nData, pIter->pData);
+ }
+ }
+
+ *pRc = rc;
+ if( i==pNear->nPhrase && (i==1 || fts5ExprNearIsMatch(pRc, pNear)) ){
+ return 1;
+ }
+ return 0;
+ }
+}
+
+
+/*
+** Initialize all term iterators in the pNear object. If any term is found
+** to match no documents at all, return immediately without initializing any
+** further iterators.
+*/
+static int fts5ExprNearInitAll(
+ Fts5Expr *pExpr,
+ Fts5ExprNode *pNode
+){
+ Fts5ExprNearset *pNear = pNode->pNear;
+ int i, j;
+ int rc = SQLITE_OK;
+
+ assert( pNode->bNomatch==0 );
+ for(i=0; rc==SQLITE_OK && i<pNear->nPhrase; i++){
+ Fts5ExprPhrase *pPhrase = pNear->apPhrase[i];
+ for(j=0; j<pPhrase->nTerm; j++){
+ Fts5ExprTerm *pTerm = &pPhrase->aTerm[j];
+ Fts5ExprTerm *p;
+ int bEof = 1;
+
+ for(p=pTerm; p && rc==SQLITE_OK; p=p->pSynonym){
+ if( p->pIter ){
+ sqlite3Fts5IterClose(p->pIter);
+ p->pIter = 0;
+ }
+ rc = sqlite3Fts5IndexQuery(
+ pExpr->pIndex, p->zTerm, (int)strlen(p->zTerm),
+ (pTerm->bPrefix ? FTS5INDEX_QUERY_PREFIX : 0) |
+ (pExpr->bDesc ? FTS5INDEX_QUERY_DESC : 0),
+ pNear->pColset,
+ &p->pIter
+ );
+ assert( rc==SQLITE_OK || p->pIter==0 );
+ if( p->pIter && 0==sqlite3Fts5IterEof(p->pIter) ){
+ bEof = 0;
+ }
+ }
+
+ if( bEof ){
+ pNode->bEof = 1;
+ return rc;
+ }
+ }
+ }
+
+ return rc;
+}
+
+/*
+** If pExpr is an ASC iterator, this function returns a value with the
+** same sign as:
+**
+** (iLhs - iRhs)
+**
+** Otherwise, if this is a DESC iterator, the opposite is returned:
+**
+** (iRhs - iLhs)
+*/
+static int fts5RowidCmp(
+ Fts5Expr *pExpr,
+ i64 iLhs,
+ i64 iRhs
+){
+ assert( pExpr->bDesc==0 || pExpr->bDesc==1 );
+ if( pExpr->bDesc==0 ){
+ if( iLhs<iRhs ) return -1;
+ return (iLhs > iRhs);
+ }else{
+ if( iLhs>iRhs ) return -1;
+ return (iLhs < iRhs);
+ }
+}
+
+static void fts5ExprSetEof(Fts5ExprNode *pNode){
+ int i;
+ pNode->bEof = 1;
+ pNode->bNomatch = 0;
+ for(i=0; i<pNode->nChild; i++){
+ fts5ExprSetEof(pNode->apChild[i]);
+ }
+}
+
+static void fts5ExprNodeZeroPoslist(Fts5ExprNode *pNode){
+ if( pNode->eType==FTS5_STRING || pNode->eType==FTS5_TERM ){
+ Fts5ExprNearset *pNear = pNode->pNear;
+ int i;
+ for(i=0; i<pNear->nPhrase; i++){
+ Fts5ExprPhrase *pPhrase = pNear->apPhrase[i];
+ pPhrase->poslist.n = 0;
+ }
+ }else{
+ int i;
+ for(i=0; i<pNode->nChild; i++){
+ fts5ExprNodeZeroPoslist(pNode->apChild[i]);
+ }
+ }
+}
+
+
+
+/*
+** Compare the values currently indicated by the two nodes as follows:
+**
+** res = (*p1) - (*p2)
+**
+** Nodes that point to values that come later in the iteration order are
+** considered to be larger. Nodes at EOF are the largest of all.
+**
+** This means that if the iteration order is ASC, then numerically larger
+** rowids are considered larger. Or if it is the default DESC, numerically
+** smaller rowids are larger.
+*/
+static int fts5NodeCompare(
+ Fts5Expr *pExpr,
+ Fts5ExprNode *p1,
+ Fts5ExprNode *p2
+){
+ if( p2->bEof ) return -1;
+ if( p1->bEof ) return +1;
+ return fts5RowidCmp(pExpr, p1->iRowid, p2->iRowid);
+}
+
+/*
+** All individual term iterators in pNear are guaranteed to be valid when
+** this function is called. This function checks if all term iterators
+** point to the same rowid, and if not, advances them until they do.
+** If an EOF is reached before this happens, *pbEof is set to true before
+** returning.
+**
+** SQLITE_OK is returned if an error occurs, or an SQLite error code
+** otherwise. It is not considered an error code if an iterator reaches
+** EOF.
+*/
+static int fts5ExprNodeTest_STRING(
+ Fts5Expr *pExpr, /* Expression pPhrase belongs to */
+ Fts5ExprNode *pNode
+){
+ Fts5ExprNearset *pNear = pNode->pNear;
+ Fts5ExprPhrase *pLeft = pNear->apPhrase[0];
+ int rc = SQLITE_OK;
+ i64 iLast; /* Lastest rowid any iterator points to */
+ int i, j; /* Phrase and token index, respectively */
+ int bMatch; /* True if all terms are at the same rowid */
+ const int bDesc = pExpr->bDesc;
+
+ /* Check that this node should not be FTS5_TERM */
+ assert( pNear->nPhrase>1
+ || pNear->apPhrase[0]->nTerm>1
+ || pNear->apPhrase[0]->aTerm[0].pSynonym
+ );
+
+ /* Initialize iLast, the "lastest" rowid any iterator points to. If the
+ ** iterator skips through rowids in the default ascending order, this means
+ ** the maximum rowid. Or, if the iterator is "ORDER BY rowid DESC", then it
+ ** means the minimum rowid. */
+ if( pLeft->aTerm[0].pSynonym ){
+ iLast = fts5ExprSynonymRowid(&pLeft->aTerm[0], bDesc, 0);
+ }else{
+ iLast = pLeft->aTerm[0].pIter->iRowid;
+ }
+
+ do {
+ bMatch = 1;
+ for(i=0; i<pNear->nPhrase; i++){
+ Fts5ExprPhrase *pPhrase = pNear->apPhrase[i];
+ for(j=0; j<pPhrase->nTerm; j++){
+ Fts5ExprTerm *pTerm = &pPhrase->aTerm[j];
+ if( pTerm->pSynonym ){
+ i64 iRowid = fts5ExprSynonymRowid(pTerm, bDesc, 0);
+ if( iRowid==iLast ) continue;
+ bMatch = 0;
+ if( fts5ExprSynonymAdvanceto(pTerm, bDesc, &iLast, &rc) ){
+ pNode->bNomatch = 0;
+ pNode->bEof = 1;
+ return rc;
+ }
+ }else{
+ Fts5IndexIter *pIter = pPhrase->aTerm[j].pIter;
+ if( pIter->iRowid==iLast ) continue;
+ bMatch = 0;
+ if( fts5ExprAdvanceto(pIter, bDesc, &iLast, &rc, &pNode->bEof) ){
+ return rc;
+ }
+ }
+ }
+ }
+ }while( bMatch==0 );
+
+ pNode->iRowid = iLast;
+ pNode->bNomatch = ((0==fts5ExprNearTest(&rc, pExpr, pNode)) && rc==SQLITE_OK);
+ assert( pNode->bEof==0 || pNode->bNomatch==0 );
+
+ return rc;
+}
+
+/*
+** Advance the first term iterator in the first phrase of pNear. Set output
+** variable *pbEof to true if it reaches EOF or if an error occurs.
+**
+** Return SQLITE_OK if successful, or an SQLite error code if an error
+** occurs.
+*/
+static int fts5ExprNodeNext_STRING(
+ Fts5Expr *pExpr, /* Expression pPhrase belongs to */
+ Fts5ExprNode *pNode, /* FTS5_STRING or FTS5_TERM node */
+ int bFromValid,
+ i64 iFrom
+){
+ Fts5ExprTerm *pTerm = &pNode->pNear->apPhrase[0]->aTerm[0];
+ int rc = SQLITE_OK;
+
+ pNode->bNomatch = 0;
+ if( pTerm->pSynonym ){
+ int bEof = 1;
+ Fts5ExprTerm *p;
+
+ /* Find the firstest rowid any synonym points to. */
+ i64 iRowid = fts5ExprSynonymRowid(pTerm, pExpr->bDesc, 0);
+
+ /* Advance each iterator that currently points to iRowid. Or, if iFrom
+ ** is valid - each iterator that points to a rowid before iFrom. */
+ for(p=pTerm; p; p=p->pSynonym){
+ if( sqlite3Fts5IterEof(p->pIter)==0 ){
+ i64 ii = p->pIter->iRowid;
+ if( ii==iRowid
+ || (bFromValid && ii!=iFrom && (ii>iFrom)==pExpr->bDesc)
+ ){
+ if( bFromValid ){
+ rc = sqlite3Fts5IterNextFrom(p->pIter, iFrom);
+ }else{
+ rc = sqlite3Fts5IterNext(p->pIter);
+ }
+ if( rc!=SQLITE_OK ) break;
+ if( sqlite3Fts5IterEof(p->pIter)==0 ){
+ bEof = 0;
+ }
+ }else{
+ bEof = 0;
+ }
+ }
+ }
+
+ /* Set the EOF flag if either all synonym iterators are at EOF or an
+ ** error has occurred. */
+ pNode->bEof = (rc || bEof);
+ }else{
+ Fts5IndexIter *pIter = pTerm->pIter;
+
+ assert( Fts5NodeIsString(pNode) );
+ if( bFromValid ){
+ rc = sqlite3Fts5IterNextFrom(pIter, iFrom);
+ }else{
+ rc = sqlite3Fts5IterNext(pIter);
+ }
+
+ pNode->bEof = (rc || sqlite3Fts5IterEof(pIter));
+ }
+
+ if( pNode->bEof==0 ){
+ assert( rc==SQLITE_OK );
+ rc = fts5ExprNodeTest_STRING(pExpr, pNode);
+ }
+
+ return rc;
+}
+
+
+static int fts5ExprNodeTest_TERM(
+ Fts5Expr *pExpr, /* Expression that pNear is a part of */
+ Fts5ExprNode *pNode /* The "NEAR" node (FTS5_TERM) */
+){
+ /* As this "NEAR" object is actually a single phrase that consists
+ ** of a single term only, grab pointers into the poslist managed by the
+ ** fts5_index.c iterator object. This is much faster than synthesizing
+ ** a new poslist the way we have to for more complicated phrase or NEAR
+ ** expressions. */
+ Fts5ExprPhrase *pPhrase = pNode->pNear->apPhrase[0];
+ Fts5IndexIter *pIter = pPhrase->aTerm[0].pIter;
+
+ assert( pNode->eType==FTS5_TERM );
+ assert( pNode->pNear->nPhrase==1 && pPhrase->nTerm==1 );
+ assert( pPhrase->aTerm[0].pSynonym==0 );
+
+ pPhrase->poslist.n = pIter->nData;
+ if( pExpr->pConfig->eDetail==FTS5_DETAIL_FULL ){
+ pPhrase->poslist.p = (u8*)pIter->pData;
+ }
+ pNode->iRowid = pIter->iRowid;
+ pNode->bNomatch = (pPhrase->poslist.n==0);
+ return SQLITE_OK;
+}
+
+/*
+** xNext() method for a node of type FTS5_TERM.
+*/
+static int fts5ExprNodeNext_TERM(
+ Fts5Expr *pExpr,
+ Fts5ExprNode *pNode,
+ int bFromValid,
+ i64 iFrom
+){
+ int rc;
+ Fts5IndexIter *pIter = pNode->pNear->apPhrase[0]->aTerm[0].pIter;
+
+ assert( pNode->bEof==0 );
+ if( bFromValid ){
+ rc = sqlite3Fts5IterNextFrom(pIter, iFrom);
+ }else{
+ rc = sqlite3Fts5IterNext(pIter);
+ }
+ if( rc==SQLITE_OK && sqlite3Fts5IterEof(pIter)==0 ){
+ rc = fts5ExprNodeTest_TERM(pExpr, pNode);
+ }else{
+ pNode->bEof = 1;
+ pNode->bNomatch = 0;
+ }
+ return rc;
+}
+
+static void fts5ExprNodeTest_OR(
+ Fts5Expr *pExpr, /* Expression of which pNode is a part */
+ Fts5ExprNode *pNode /* Expression node to test */
+){
+ Fts5ExprNode *pNext = pNode->apChild[0];
+ int i;
+
+ for(i=1; i<pNode->nChild; i++){
+ Fts5ExprNode *pChild = pNode->apChild[i];
+ int cmp = fts5NodeCompare(pExpr, pNext, pChild);
+ if( cmp>0 || (cmp==0 && pChild->bNomatch==0) ){
+ pNext = pChild;
+ }
+ }
+ pNode->iRowid = pNext->iRowid;
+ pNode->bEof = pNext->bEof;
+ pNode->bNomatch = pNext->bNomatch;
+}
+
+static int fts5ExprNodeNext_OR(
+ Fts5Expr *pExpr,
+ Fts5ExprNode *pNode,
+ int bFromValid,
+ i64 iFrom
+){
+ int i;
+ i64 iLast = pNode->iRowid;
+
+ for(i=0; i<pNode->nChild; i++){
+ Fts5ExprNode *p1 = pNode->apChild[i];
+ assert( p1->bEof || fts5RowidCmp(pExpr, p1->iRowid, iLast)>=0 );
+ if( p1->bEof==0 ){
+ if( (p1->iRowid==iLast)
+ || (bFromValid && fts5RowidCmp(pExpr, p1->iRowid, iFrom)<0)
+ ){
+ int rc = fts5ExprNodeNext(pExpr, p1, bFromValid, iFrom);
+ if( rc!=SQLITE_OK ) return rc;
+ }
+ }
+ }
+
+ fts5ExprNodeTest_OR(pExpr, pNode);
+ return SQLITE_OK;
+}
+
+/*
+** Argument pNode is an FTS5_AND node.
+*/
+static int fts5ExprNodeTest_AND(
+ Fts5Expr *pExpr, /* Expression pPhrase belongs to */
+ Fts5ExprNode *pAnd /* FTS5_AND node to advance */
+){
+ int iChild;
+ i64 iLast = pAnd->iRowid;
+ int rc = SQLITE_OK;
+ int bMatch;
+
+ assert( pAnd->bEof==0 );
+ do {
+ pAnd->bNomatch = 0;
+ bMatch = 1;
+ for(iChild=0; iChild<pAnd->nChild; iChild++){
+ Fts5ExprNode *pChild = pAnd->apChild[iChild];
+ int cmp = fts5RowidCmp(pExpr, iLast, pChild->iRowid);
+ if( cmp>0 ){
+ /* Advance pChild until it points to iLast or laster */
+ rc = fts5ExprNodeNext(pExpr, pChild, 1, iLast);
+ if( rc!=SQLITE_OK ) return rc;
+ }
+
+ /* If the child node is now at EOF, so is the parent AND node. Otherwise,
+ ** the child node is guaranteed to have advanced at least as far as
+ ** rowid iLast. So if it is not at exactly iLast, pChild->iRowid is the
+ ** new lastest rowid seen so far. */
+ assert( pChild->bEof || fts5RowidCmp(pExpr, iLast, pChild->iRowid)<=0 );
+ if( pChild->bEof ){
+ fts5ExprSetEof(pAnd);
+ bMatch = 1;
+ break;
+ }else if( iLast!=pChild->iRowid ){
+ bMatch = 0;
+ iLast = pChild->iRowid;
+ }
+
+ if( pChild->bNomatch ){
+ pAnd->bNomatch = 1;
+ }
+ }
+ }while( bMatch==0 );
+
+ if( pAnd->bNomatch && pAnd!=pExpr->pRoot ){
+ fts5ExprNodeZeroPoslist(pAnd);
+ }
+ pAnd->iRowid = iLast;
+ return SQLITE_OK;
+}
+
+static int fts5ExprNodeNext_AND(
+ Fts5Expr *pExpr,
+ Fts5ExprNode *pNode,
+ int bFromValid,
+ i64 iFrom
+){
+ int rc = fts5ExprNodeNext(pExpr, pNode->apChild[0], bFromValid, iFrom);
+ if( rc==SQLITE_OK ){
+ rc = fts5ExprNodeTest_AND(pExpr, pNode);
+ }
+ return rc;
+}
+
+static int fts5ExprNodeTest_NOT(
+ Fts5Expr *pExpr, /* Expression pPhrase belongs to */
+ Fts5ExprNode *pNode /* FTS5_NOT node to advance */
+){
+ int rc = SQLITE_OK;
+ Fts5ExprNode *p1 = pNode->apChild[0];
+ Fts5ExprNode *p2 = pNode->apChild[1];
+ assert( pNode->nChild==2 );
+
+ while( rc==SQLITE_OK && p1->bEof==0 ){
+ int cmp = fts5NodeCompare(pExpr, p1, p2);
+ if( cmp>0 ){
+ rc = fts5ExprNodeNext(pExpr, p2, 1, p1->iRowid);
+ cmp = fts5NodeCompare(pExpr, p1, p2);
+ }
+ assert( rc!=SQLITE_OK || cmp<=0 );
+ if( cmp || p2->bNomatch ) break;
+ rc = fts5ExprNodeNext(pExpr, p1, 0, 0);
+ }
+ pNode->bEof = p1->bEof;
+ pNode->bNomatch = p1->bNomatch;
+ pNode->iRowid = p1->iRowid;
+ if( p1->bEof ){
+ fts5ExprNodeZeroPoslist(p2);
+ }
+ return rc;
+}
+
+static int fts5ExprNodeNext_NOT(
+ Fts5Expr *pExpr,
+ Fts5ExprNode *pNode,
+ int bFromValid,
+ i64 iFrom
+){
+ int rc = fts5ExprNodeNext(pExpr, pNode->apChild[0], bFromValid, iFrom);
+ if( rc==SQLITE_OK ){
+ rc = fts5ExprNodeTest_NOT(pExpr, pNode);
+ }
+ return rc;
+}
+
+/*
+** If pNode currently points to a match, this function returns SQLITE_OK
+** without modifying it. Otherwise, pNode is advanced until it does point
+** to a match or EOF is reached.
+*/
+static int fts5ExprNodeTest(
+ Fts5Expr *pExpr, /* Expression of which pNode is a part */
+ Fts5ExprNode *pNode /* Expression node to test */
+){
+ int rc = SQLITE_OK;
+ if( pNode->bEof==0 ){
+ switch( pNode->eType ){
+
+ case FTS5_STRING: {
+ rc = fts5ExprNodeTest_STRING(pExpr, pNode);
+ break;
+ }
+
+ case FTS5_TERM: {
+ rc = fts5ExprNodeTest_TERM(pExpr, pNode);
+ break;
+ }
+
+ case FTS5_AND: {
+ rc = fts5ExprNodeTest_AND(pExpr, pNode);
+ break;
+ }
+
+ case FTS5_OR: {
+ fts5ExprNodeTest_OR(pExpr, pNode);
+ break;
+ }
+
+ default: assert( pNode->eType==FTS5_NOT ); {
+ rc = fts5ExprNodeTest_NOT(pExpr, pNode);
+ break;
+ }
+ }
+ }
+ return rc;
+}
+
+
+/*
+** Set node pNode, which is part of expression pExpr, to point to the first
+** match. If there are no matches, set the Node.bEof flag to indicate EOF.
+**
+** Return an SQLite error code if an error occurs, or SQLITE_OK otherwise.
+** It is not an error if there are no matches.
+*/
+static int fts5ExprNodeFirst(Fts5Expr *pExpr, Fts5ExprNode *pNode){
+ int rc = SQLITE_OK;
+ pNode->bEof = 0;
+ pNode->bNomatch = 0;
+
+ if( Fts5NodeIsString(pNode) ){
+ /* Initialize all term iterators in the NEAR object. */
+ rc = fts5ExprNearInitAll(pExpr, pNode);
+ }else if( pNode->xNext==0 ){
+ pNode->bEof = 1;
+ }else{
+ int i;
+ int nEof = 0;
+ for(i=0; i<pNode->nChild && rc==SQLITE_OK; i++){
+ Fts5ExprNode *pChild = pNode->apChild[i];
+ rc = fts5ExprNodeFirst(pExpr, pNode->apChild[i]);
+ assert( pChild->bEof==0 || pChild->bEof==1 );
+ nEof += pChild->bEof;
+ }
+ pNode->iRowid = pNode->apChild[0]->iRowid;
+
+ switch( pNode->eType ){
+ case FTS5_AND:
+ if( nEof>0 ) fts5ExprSetEof(pNode);
+ break;
+
+ case FTS5_OR:
+ if( pNode->nChild==nEof ) fts5ExprSetEof(pNode);
+ break;
+
+ default:
+ assert( pNode->eType==FTS5_NOT );
+ pNode->bEof = pNode->apChild[0]->bEof;
+ break;
+ }
+ }
+
+ if( rc==SQLITE_OK ){
+ rc = fts5ExprNodeTest(pExpr, pNode);
+ }
+ return rc;
+}
+
+
+/*
+** Begin iterating through the set of documents in index pIdx matched by
+** the MATCH expression passed as the first argument. If the "bDesc"
+** parameter is passed a non-zero value, iteration is in descending rowid
+** order. Or, if it is zero, in ascending order.
+**
+** If iterating in ascending rowid order (bDesc==0), the first document
+** visited is that with the smallest rowid that is larger than or equal
+** to parameter iFirst. Or, if iterating in ascending order (bDesc==1),
+** then the first document visited must have a rowid smaller than or
+** equal to iFirst.
+**
+** Return SQLITE_OK if successful, or an SQLite error code otherwise. It
+** is not considered an error if the query does not match any documents.
+*/
+static int sqlite3Fts5ExprFirst(Fts5Expr *p, Fts5Index *pIdx, i64 iFirst, int bDesc){
+ Fts5ExprNode *pRoot = p->pRoot;
+ int rc; /* Return code */
+
+ p->pIndex = pIdx;
+ p->bDesc = bDesc;
+ rc = fts5ExprNodeFirst(p, pRoot);
+
+ /* If not at EOF but the current rowid occurs earlier than iFirst in
+ ** the iteration order, move to document iFirst or later. */
+ if( pRoot->bEof==0 && fts5RowidCmp(p, pRoot->iRowid, iFirst)<0 ){
+ rc = fts5ExprNodeNext(p, pRoot, 1, iFirst);
+ }
+
+ /* If the iterator is not at a real match, skip forward until it is. */
+ while( pRoot->bNomatch ){
+ assert( pRoot->bEof==0 && rc==SQLITE_OK );
+ rc = fts5ExprNodeNext(p, pRoot, 0, 0);
+ }
+ return rc;
+}
+
+/*
+** Move to the next document
+**
+** Return SQLITE_OK if successful, or an SQLite error code otherwise. It
+** is not considered an error if the query does not match any documents.
+*/
+static int sqlite3Fts5ExprNext(Fts5Expr *p, i64 iLast){
+ int rc;
+ Fts5ExprNode *pRoot = p->pRoot;
+ assert( pRoot->bEof==0 && pRoot->bNomatch==0 );
+ do {
+ rc = fts5ExprNodeNext(p, pRoot, 0, 0);
+ assert( pRoot->bNomatch==0 || (rc==SQLITE_OK && pRoot->bEof==0) );
+ }while( pRoot->bNomatch );
+ if( fts5RowidCmp(p, pRoot->iRowid, iLast)>0 ){
+ pRoot->bEof = 1;
+ }
+ return rc;
+}
+
+static int sqlite3Fts5ExprEof(Fts5Expr *p){
+ return p->pRoot->bEof;
+}
+
+static i64 sqlite3Fts5ExprRowid(Fts5Expr *p){
+ return p->pRoot->iRowid;
+}
+
+static int fts5ParseStringFromToken(Fts5Token *pToken, char **pz){
+ int rc = SQLITE_OK;
+ *pz = sqlite3Fts5Strndup(&rc, pToken->p, pToken->n);
+ return rc;
+}
+
+/*
+** Free the phrase object passed as the only argument.
+*/
+static void fts5ExprPhraseFree(Fts5ExprPhrase *pPhrase){
+ if( pPhrase ){
+ int i;
+ for(i=0; i<pPhrase->nTerm; i++){
+ Fts5ExprTerm *pSyn;
+ Fts5ExprTerm *pNext;
+ Fts5ExprTerm *pTerm = &pPhrase->aTerm[i];
+ sqlite3_free(pTerm->zTerm);
+ sqlite3Fts5IterClose(pTerm->pIter);
+ for(pSyn=pTerm->pSynonym; pSyn; pSyn=pNext){
+ pNext = pSyn->pSynonym;
+ sqlite3Fts5IterClose(pSyn->pIter);
+ fts5BufferFree((Fts5Buffer*)&pSyn[1]);
+ sqlite3_free(pSyn);
+ }
+ }
+ if( pPhrase->poslist.nSpace>0 ) fts5BufferFree(&pPhrase->poslist);
+ sqlite3_free(pPhrase);
+ }
+}
+
+/*
+** If argument pNear is NULL, then a new Fts5ExprNearset object is allocated
+** and populated with pPhrase. Or, if pNear is not NULL, phrase pPhrase is
+** appended to it and the results returned.
+**
+** If an OOM error occurs, both the pNear and pPhrase objects are freed and
+** NULL returned.
+*/
+static Fts5ExprNearset *sqlite3Fts5ParseNearset(
+ Fts5Parse *pParse, /* Parse context */
+ Fts5ExprNearset *pNear, /* Existing nearset, or NULL */
+ Fts5ExprPhrase *pPhrase /* Recently parsed phrase */
+){
+ const int SZALLOC = 8;
+ Fts5ExprNearset *pRet = 0;
+
+ if( pParse->rc==SQLITE_OK ){
+ if( pPhrase==0 ){
+ return pNear;
+ }
+ if( pNear==0 ){
+ int nByte = sizeof(Fts5ExprNearset) + SZALLOC * sizeof(Fts5ExprPhrase*);
+ pRet = sqlite3_malloc(nByte);
+ if( pRet==0 ){
+ pParse->rc = SQLITE_NOMEM;
+ }else{
+ memset(pRet, 0, nByte);
+ }
+ }else if( (pNear->nPhrase % SZALLOC)==0 ){
+ int nNew = pNear->nPhrase + SZALLOC;
+ int nByte = sizeof(Fts5ExprNearset) + nNew * sizeof(Fts5ExprPhrase*);
+
+ pRet = (Fts5ExprNearset*)sqlite3_realloc(pNear, nByte);
+ if( pRet==0 ){
+ pParse->rc = SQLITE_NOMEM;
+ }
+ }else{
+ pRet = pNear;
+ }
+ }
+
+ if( pRet==0 ){
+ assert( pParse->rc!=SQLITE_OK );
+ sqlite3Fts5ParseNearsetFree(pNear);
+ sqlite3Fts5ParsePhraseFree(pPhrase);
+ }else{
+ if( pRet->nPhrase>0 ){
+ Fts5ExprPhrase *pLast = pRet->apPhrase[pRet->nPhrase-1];
+ assert( pLast==pParse->apPhrase[pParse->nPhrase-2] );
+ if( pPhrase->nTerm==0 ){
+ fts5ExprPhraseFree(pPhrase);
+ pRet->nPhrase--;
+ pParse->nPhrase--;
+ pPhrase = pLast;
+ }else if( pLast->nTerm==0 ){
+ fts5ExprPhraseFree(pLast);
+ pParse->apPhrase[pParse->nPhrase-2] = pPhrase;
+ pParse->nPhrase--;
+ pRet->nPhrase--;
+ }
+ }
+ pRet->apPhrase[pRet->nPhrase++] = pPhrase;
+ }
+ return pRet;
+}
+
+typedef struct TokenCtx TokenCtx;
+struct TokenCtx {
+ Fts5ExprPhrase *pPhrase;
+ int rc;
+};
+
+/*
+** Callback for tokenizing terms used by ParseTerm().
+*/
+static int fts5ParseTokenize(
+ void *pContext, /* Pointer to Fts5InsertCtx object */
+ int tflags, /* Mask of FTS5_TOKEN_* flags */
+ const char *pToken, /* Buffer containing token */
+ int nToken, /* Size of token in bytes */
+ int iUnused1, /* Start offset of token */
+ int iUnused2 /* End offset of token */
+){
+ int rc = SQLITE_OK;
+ const int SZALLOC = 8;
+ TokenCtx *pCtx = (TokenCtx*)pContext;
+ Fts5ExprPhrase *pPhrase = pCtx->pPhrase;
+
+ UNUSED_PARAM2(iUnused1, iUnused2);
+
+ /* If an error has already occurred, this is a no-op */
+ if( pCtx->rc!=SQLITE_OK ) return pCtx->rc;
+ if( nToken>FTS5_MAX_TOKEN_SIZE ) nToken = FTS5_MAX_TOKEN_SIZE;
+
+ if( pPhrase && pPhrase->nTerm>0 && (tflags & FTS5_TOKEN_COLOCATED) ){
+ Fts5ExprTerm *pSyn;
+ int nByte = sizeof(Fts5ExprTerm) + sizeof(Fts5Buffer) + nToken+1;
+ pSyn = (Fts5ExprTerm*)sqlite3_malloc(nByte);
+ if( pSyn==0 ){
+ rc = SQLITE_NOMEM;
+ }else{
+ memset(pSyn, 0, nByte);
+ pSyn->zTerm = ((char*)pSyn) + sizeof(Fts5ExprTerm) + sizeof(Fts5Buffer);
+ memcpy(pSyn->zTerm, pToken, nToken);
+ pSyn->pSynonym = pPhrase->aTerm[pPhrase->nTerm-1].pSynonym;
+ pPhrase->aTerm[pPhrase->nTerm-1].pSynonym = pSyn;
+ }
+ }else{
+ Fts5ExprTerm *pTerm;
+ if( pPhrase==0 || (pPhrase->nTerm % SZALLOC)==0 ){
+ Fts5ExprPhrase *pNew;
+ int nNew = SZALLOC + (pPhrase ? pPhrase->nTerm : 0);
+
+ pNew = (Fts5ExprPhrase*)sqlite3_realloc(pPhrase,
+ sizeof(Fts5ExprPhrase) + sizeof(Fts5ExprTerm) * nNew
+ );
+ if( pNew==0 ){
+ rc = SQLITE_NOMEM;
+ }else{
+ if( pPhrase==0 ) memset(pNew, 0, sizeof(Fts5ExprPhrase));
+ pCtx->pPhrase = pPhrase = pNew;
+ pNew->nTerm = nNew - SZALLOC;
+ }
+ }
+
+ if( rc==SQLITE_OK ){
+ pTerm = &pPhrase->aTerm[pPhrase->nTerm++];
+ memset(pTerm, 0, sizeof(Fts5ExprTerm));
+ pTerm->zTerm = sqlite3Fts5Strndup(&rc, pToken, nToken);
+ }
+ }
+
+ pCtx->rc = rc;
+ return rc;
+}
+
+
+/*
+** Free the phrase object passed as the only argument.
+*/
+static void sqlite3Fts5ParsePhraseFree(Fts5ExprPhrase *pPhrase){
+ fts5ExprPhraseFree(pPhrase);
+}
+
+/*
+** Free the phrase object passed as the second argument.
+*/
+static void sqlite3Fts5ParseNearsetFree(Fts5ExprNearset *pNear){
+ if( pNear ){
+ int i;
+ for(i=0; i<pNear->nPhrase; i++){
+ fts5ExprPhraseFree(pNear->apPhrase[i]);
+ }
+ sqlite3_free(pNear->pColset);
+ sqlite3_free(pNear);
+ }
+}
+
+static void sqlite3Fts5ParseFinished(Fts5Parse *pParse, Fts5ExprNode *p){
+ assert( pParse->pExpr==0 );
+ pParse->pExpr = p;
+}
+
+/*
+** This function is called by the parser to process a string token. The
+** string may or may not be quoted. In any case it is tokenized and a
+** phrase object consisting of all tokens returned.
+*/
+static Fts5ExprPhrase *sqlite3Fts5ParseTerm(
+ Fts5Parse *pParse, /* Parse context */
+ Fts5ExprPhrase *pAppend, /* Phrase to append to */
+ Fts5Token *pToken, /* String to tokenize */
+ int bPrefix /* True if there is a trailing "*" */
+){
+ Fts5Config *pConfig = pParse->pConfig;
+ TokenCtx sCtx; /* Context object passed to callback */
+ int rc; /* Tokenize return code */
+ char *z = 0;
+
+ memset(&sCtx, 0, sizeof(TokenCtx));
+ sCtx.pPhrase = pAppend;
+
+ rc = fts5ParseStringFromToken(pToken, &z);
+ if( rc==SQLITE_OK ){
+ int flags = FTS5_TOKENIZE_QUERY | (bPrefix ? FTS5_TOKENIZE_QUERY : 0);
+ int n;
+ sqlite3Fts5Dequote(z);
+ n = (int)strlen(z);
+ rc = sqlite3Fts5Tokenize(pConfig, flags, z, n, &sCtx, fts5ParseTokenize);
+ }
+ sqlite3_free(z);
+ if( rc || (rc = sCtx.rc) ){
+ pParse->rc = rc;
+ fts5ExprPhraseFree(sCtx.pPhrase);
+ sCtx.pPhrase = 0;
+ }else{
+
+ if( pAppend==0 ){
+ if( (pParse->nPhrase % 8)==0 ){
+ int nByte = sizeof(Fts5ExprPhrase*) * (pParse->nPhrase + 8);
+ Fts5ExprPhrase **apNew;
+ apNew = (Fts5ExprPhrase**)sqlite3_realloc(pParse->apPhrase, nByte);
+ if( apNew==0 ){
+ pParse->rc = SQLITE_NOMEM;
+ fts5ExprPhraseFree(sCtx.pPhrase);
+ return 0;
+ }
+ pParse->apPhrase = apNew;
+ }
+ pParse->nPhrase++;
+ }
+
+ if( sCtx.pPhrase==0 ){
+ /* This happens when parsing a token or quoted phrase that contains
+ ** no token characters at all. (e.g ... MATCH '""'). */
+ sCtx.pPhrase = sqlite3Fts5MallocZero(&pParse->rc, sizeof(Fts5ExprPhrase));
+ }else if( sCtx.pPhrase->nTerm ){
+ sCtx.pPhrase->aTerm[sCtx.pPhrase->nTerm-1].bPrefix = bPrefix;
+ }
+ pParse->apPhrase[pParse->nPhrase-1] = sCtx.pPhrase;
+ }
+
+ return sCtx.pPhrase;
+}
+
+/*
+** Create a new FTS5 expression by cloning phrase iPhrase of the
+** expression passed as the second argument.
+*/
+static int sqlite3Fts5ExprClonePhrase(
+ Fts5Expr *pExpr,
+ int iPhrase,
+ Fts5Expr **ppNew
+){
+ int rc = SQLITE_OK; /* Return code */
+ Fts5ExprPhrase *pOrig; /* The phrase extracted from pExpr */
+ int i; /* Used to iterate through phrase terms */
+ Fts5Expr *pNew = 0; /* Expression to return via *ppNew */
+ TokenCtx sCtx = {0,0}; /* Context object for fts5ParseTokenize */
+
+ pOrig = pExpr->apExprPhrase[iPhrase];
+ pNew = (Fts5Expr*)sqlite3Fts5MallocZero(&rc, sizeof(Fts5Expr));
+ if( rc==SQLITE_OK ){
+ pNew->apExprPhrase = (Fts5ExprPhrase**)sqlite3Fts5MallocZero(&rc,
+ sizeof(Fts5ExprPhrase*));
+ }
+ if( rc==SQLITE_OK ){
+ pNew->pRoot = (Fts5ExprNode*)sqlite3Fts5MallocZero(&rc,
+ sizeof(Fts5ExprNode));
+ }
+ if( rc==SQLITE_OK ){
+ pNew->pRoot->pNear = (Fts5ExprNearset*)sqlite3Fts5MallocZero(&rc,
+ sizeof(Fts5ExprNearset) + sizeof(Fts5ExprPhrase*));
+ }
+ if( rc==SQLITE_OK ){
+ Fts5Colset *pColsetOrig = pOrig->pNode->pNear->pColset;
+ if( pColsetOrig ){
+ int nByte = sizeof(Fts5Colset) + pColsetOrig->nCol * sizeof(int);
+ Fts5Colset *pColset = (Fts5Colset*)sqlite3Fts5MallocZero(&rc, nByte);
+ if( pColset ){
+ memcpy(pColset, pColsetOrig, nByte);
+ }
+ pNew->pRoot->pNear->pColset = pColset;
+ }
+ }
+
+ for(i=0; rc==SQLITE_OK && i<pOrig->nTerm; i++){
+ int tflags = 0;
+ Fts5ExprTerm *p;
+ for(p=&pOrig->aTerm[i]; p && rc==SQLITE_OK; p=p->pSynonym){
+ const char *zTerm = p->zTerm;
+ rc = fts5ParseTokenize((void*)&sCtx, tflags, zTerm, (int)strlen(zTerm),
+ 0, 0);
+ tflags = FTS5_TOKEN_COLOCATED;
+ }
+ if( rc==SQLITE_OK ){
+ sCtx.pPhrase->aTerm[i].bPrefix = pOrig->aTerm[i].bPrefix;
+ }
+ }
+
+ if( rc==SQLITE_OK ){
+ /* All the allocations succeeded. Put the expression object together. */
+ pNew->pIndex = pExpr->pIndex;
+ pNew->pConfig = pExpr->pConfig;
+ pNew->nPhrase = 1;
+ pNew->apExprPhrase[0] = sCtx.pPhrase;
+ pNew->pRoot->pNear->apPhrase[0] = sCtx.pPhrase;
+ pNew->pRoot->pNear->nPhrase = 1;
+ sCtx.pPhrase->pNode = pNew->pRoot;
+
+ if( pOrig->nTerm==1 && pOrig->aTerm[0].pSynonym==0 ){
+ pNew->pRoot->eType = FTS5_TERM;
+ pNew->pRoot->xNext = fts5ExprNodeNext_TERM;
+ }else{
+ pNew->pRoot->eType = FTS5_STRING;
+ pNew->pRoot->xNext = fts5ExprNodeNext_STRING;
+ }
+ }else{
+ sqlite3Fts5ExprFree(pNew);
+ fts5ExprPhraseFree(sCtx.pPhrase);
+ pNew = 0;
+ }
+
+ *ppNew = pNew;
+ return rc;
+}
+
+
+/*
+** Token pTok has appeared in a MATCH expression where the NEAR operator
+** is expected. If token pTok does not contain "NEAR", store an error
+** in the pParse object.
+*/
+static void sqlite3Fts5ParseNear(Fts5Parse *pParse, Fts5Token *pTok){
+ if( pTok->n!=4 || memcmp("NEAR", pTok->p, 4) ){
+ sqlite3Fts5ParseError(
+ pParse, "fts5: syntax error near \"%.*s\"", pTok->n, pTok->p
+ );
+ }
+}
+
+static void sqlite3Fts5ParseSetDistance(
+ Fts5Parse *pParse,
+ Fts5ExprNearset *pNear,
+ Fts5Token *p
+){
+ if( pNear ){
+ int nNear = 0;
+ int i;
+ if( p->n ){
+ for(i=0; i<p->n; i++){
+ char c = (char)p->p[i];
+ if( c<'0' || c>'9' ){
+ sqlite3Fts5ParseError(
+ pParse, "expected integer, got \"%.*s\"", p->n, p->p
+ );
+ return;
+ }
+ nNear = nNear * 10 + (p->p[i] - '0');
+ }
+ }else{
+ nNear = FTS5_DEFAULT_NEARDIST;
+ }
+ pNear->nNear = nNear;
+ }
+}
+
+/*
+** The second argument passed to this function may be NULL, or it may be
+** an existing Fts5Colset object. This function returns a pointer to
+** a new colset object containing the contents of (p) with new value column
+** number iCol appended.
+**
+** If an OOM error occurs, store an error code in pParse and return NULL.
+** The old colset object (if any) is not freed in this case.
+*/
+static Fts5Colset *fts5ParseColset(
+ Fts5Parse *pParse, /* Store SQLITE_NOMEM here if required */
+ Fts5Colset *p, /* Existing colset object */
+ int iCol /* New column to add to colset object */
+){
+ int nCol = p ? p->nCol : 0; /* Num. columns already in colset object */
+ Fts5Colset *pNew; /* New colset object to return */
+
+ assert( pParse->rc==SQLITE_OK );
+ assert( iCol>=0 && iCol<pParse->pConfig->nCol );
+
+ pNew = sqlite3_realloc(p, sizeof(Fts5Colset) + sizeof(int)*nCol);
+ if( pNew==0 ){
+ pParse->rc = SQLITE_NOMEM;
+ }else{
+ int *aiCol = pNew->aiCol;
+ int i, j;
+ for(i=0; i<nCol; i++){
+ if( aiCol[i]==iCol ) return pNew;
+ if( aiCol[i]>iCol ) break;
+ }
+ for(j=nCol; j>i; j--){
+ aiCol[j] = aiCol[j-1];
+ }
+ aiCol[i] = iCol;
+ pNew->nCol = nCol+1;
+
+#ifndef NDEBUG
+ /* Check that the array is in order and contains no duplicate entries. */
+ for(i=1; i<pNew->nCol; i++) assert( pNew->aiCol[i]>pNew->aiCol[i-1] );
+#endif
+ }
+
+ return pNew;
+}
+
+static Fts5Colset *sqlite3Fts5ParseColset(
+ Fts5Parse *pParse, /* Store SQLITE_NOMEM here if required */
+ Fts5Colset *pColset, /* Existing colset object */
+ Fts5Token *p
+){
+ Fts5Colset *pRet = 0;
+ int iCol;
+ char *z; /* Dequoted copy of token p */
+
+ z = sqlite3Fts5Strndup(&pParse->rc, p->p, p->n);
+ if( pParse->rc==SQLITE_OK ){
+ Fts5Config *pConfig = pParse->pConfig;
+ sqlite3Fts5Dequote(z);
+ for(iCol=0; iCol<pConfig->nCol; iCol++){
+ if( 0==sqlite3_stricmp(pConfig->azCol[iCol], z) ) break;
+ }
+ if( iCol==pConfig->nCol ){
+ sqlite3Fts5ParseError(pParse, "no such column: %s", z);
+ }else{
+ pRet = fts5ParseColset(pParse, pColset, iCol);
+ }
+ sqlite3_free(z);
+ }
+
+ if( pRet==0 ){
+ assert( pParse->rc!=SQLITE_OK );
+ sqlite3_free(pColset);
+ }
+
+ return pRet;
+}
+
+static void sqlite3Fts5ParseSetColset(
+ Fts5Parse *pParse,
+ Fts5ExprNearset *pNear,
+ Fts5Colset *pColset
+){
+ if( pParse->pConfig->eDetail==FTS5_DETAIL_NONE ){
+ pParse->rc = SQLITE_ERROR;
+ pParse->zErr = sqlite3_mprintf(
+ "fts5: column queries are not supported (detail=none)"
+ );
+ sqlite3_free(pColset);
+ return;
+ }
+
+ if( pNear ){
+ pNear->pColset = pColset;
+ }else{
+ sqlite3_free(pColset);
+ }
+}
+
+static void fts5ExprAssignXNext(Fts5ExprNode *pNode){
+ switch( pNode->eType ){
+ case FTS5_STRING: {
+ Fts5ExprNearset *pNear = pNode->pNear;
+ if( pNear->nPhrase==1 && pNear->apPhrase[0]->nTerm==1
+ && pNear->apPhrase[0]->aTerm[0].pSynonym==0
+ ){
+ pNode->eType = FTS5_TERM;
+ pNode->xNext = fts5ExprNodeNext_TERM;
+ }else{
+ pNode->xNext = fts5ExprNodeNext_STRING;
+ }
+ break;
+ };
+
+ case FTS5_OR: {
+ pNode->xNext = fts5ExprNodeNext_OR;
+ break;
+ };
+
+ case FTS5_AND: {
+ pNode->xNext = fts5ExprNodeNext_AND;
+ break;
+ };
+
+ default: assert( pNode->eType==FTS5_NOT ); {
+ pNode->xNext = fts5ExprNodeNext_NOT;
+ break;
+ };
+ }
+}
+
+static void fts5ExprAddChildren(Fts5ExprNode *p, Fts5ExprNode *pSub){
+ if( p->eType!=FTS5_NOT && pSub->eType==p->eType ){
+ int nByte = sizeof(Fts5ExprNode*) * pSub->nChild;
+ memcpy(&p->apChild[p->nChild], pSub->apChild, nByte);
+ p->nChild += pSub->nChild;
+ sqlite3_free(pSub);
+ }else{
+ p->apChild[p->nChild++] = pSub;
+ }
+}
+
+/*
+** Allocate and return a new expression object. If anything goes wrong (i.e.
+** OOM error), leave an error code in pParse and return NULL.
+*/
+static Fts5ExprNode *sqlite3Fts5ParseNode(
+ Fts5Parse *pParse, /* Parse context */
+ int eType, /* FTS5_STRING, AND, OR or NOT */
+ Fts5ExprNode *pLeft, /* Left hand child expression */
+ Fts5ExprNode *pRight, /* Right hand child expression */
+ Fts5ExprNearset *pNear /* For STRING expressions, the near cluster */
+){
+ Fts5ExprNode *pRet = 0;
+
+ if( pParse->rc==SQLITE_OK ){
+ int nChild = 0; /* Number of children of returned node */
+ int nByte; /* Bytes of space to allocate for this node */
+
+ assert( (eType!=FTS5_STRING && !pNear)
+ || (eType==FTS5_STRING && !pLeft && !pRight)
+ );
+ if( eType==FTS5_STRING && pNear==0 ) return 0;
+ if( eType!=FTS5_STRING && pLeft==0 ) return pRight;
+ if( eType!=FTS5_STRING && pRight==0 ) return pLeft;
+
+ if( eType==FTS5_NOT ){
+ nChild = 2;
+ }else if( eType==FTS5_AND || eType==FTS5_OR ){
+ nChild = 2;
+ if( pLeft->eType==eType ) nChild += pLeft->nChild-1;
+ if( pRight->eType==eType ) nChild += pRight->nChild-1;
+ }
+
+ nByte = sizeof(Fts5ExprNode) + sizeof(Fts5ExprNode*)*(nChild-1);
+ pRet = (Fts5ExprNode*)sqlite3Fts5MallocZero(&pParse->rc, nByte);
+
+ if( pRet ){
+ pRet->eType = eType;
+ pRet->pNear = pNear;
+ fts5ExprAssignXNext(pRet);
+ if( eType==FTS5_STRING ){
+ int iPhrase;
+ for(iPhrase=0; iPhrase<pNear->nPhrase; iPhrase++){
+ pNear->apPhrase[iPhrase]->pNode = pRet;
+ if( pNear->apPhrase[iPhrase]->nTerm==0 ){
+ pRet->xNext = 0;
+ pRet->eType = FTS5_EOF;
+ }
+ }
+
+ if( pParse->pConfig->eDetail!=FTS5_DETAIL_FULL
+ && (pNear->nPhrase!=1 || pNear->apPhrase[0]->nTerm>1)
+ ){
+ assert( pParse->rc==SQLITE_OK );
+ pParse->rc = SQLITE_ERROR;
+ assert( pParse->zErr==0 );
+ pParse->zErr = sqlite3_mprintf(
+ "fts5: %s queries are not supported (detail!=full)",
+ pNear->nPhrase==1 ? "phrase": "NEAR"
+ );
+ sqlite3_free(pRet);
+ pRet = 0;
+ }
+
+ }else{
+ fts5ExprAddChildren(pRet, pLeft);
+ fts5ExprAddChildren(pRet, pRight);
+ }
+ }
+ }
+
+ if( pRet==0 ){
+ assert( pParse->rc!=SQLITE_OK );
+ sqlite3Fts5ParseNodeFree(pLeft);
+ sqlite3Fts5ParseNodeFree(pRight);
+ sqlite3Fts5ParseNearsetFree(pNear);
+ }
+ return pRet;
+}
+
+static Fts5ExprNode *sqlite3Fts5ParseImplicitAnd(
+ Fts5Parse *pParse, /* Parse context */
+ Fts5ExprNode *pLeft, /* Left hand child expression */
+ Fts5ExprNode *pRight /* Right hand child expression */
+){
+ Fts5ExprNode *pRet = 0;
+ Fts5ExprNode *pPrev;
+
+ if( pParse->rc ){
+ sqlite3Fts5ParseNodeFree(pLeft);
+ sqlite3Fts5ParseNodeFree(pRight);
+ }else{
+
+ assert( pLeft->eType==FTS5_STRING
+ || pLeft->eType==FTS5_TERM
+ || pLeft->eType==FTS5_EOF
+ || pLeft->eType==FTS5_AND
+ );
+ assert( pRight->eType==FTS5_STRING
+ || pRight->eType==FTS5_TERM
+ || pRight->eType==FTS5_EOF
+ );
+
+ if( pLeft->eType==FTS5_AND ){
+ pPrev = pLeft->apChild[pLeft->nChild-1];
+ }else{
+ pPrev = pLeft;
+ }
+ assert( pPrev->eType==FTS5_STRING
+ || pPrev->eType==FTS5_TERM
+ || pPrev->eType==FTS5_EOF
+ );
+
+ if( pRight->eType==FTS5_EOF ){
+ assert( pParse->apPhrase[pParse->nPhrase-1]==pRight->pNear->apPhrase[0] );
+ sqlite3Fts5ParseNodeFree(pRight);
+ pRet = pLeft;
+ pParse->nPhrase--;
+ }
+ else if( pPrev->eType==FTS5_EOF ){
+ Fts5ExprPhrase **ap;
+
+ if( pPrev==pLeft ){
+ pRet = pRight;
+ }else{
+ pLeft->apChild[pLeft->nChild-1] = pRight;
+ pRet = pLeft;
+ }
+
+ ap = &pParse->apPhrase[pParse->nPhrase-1-pRight->pNear->nPhrase];
+ assert( ap[0]==pPrev->pNear->apPhrase[0] );
+ memmove(ap, &ap[1], sizeof(Fts5ExprPhrase*)*pRight->pNear->nPhrase);
+ pParse->nPhrase--;
+
+ sqlite3Fts5ParseNodeFree(pPrev);
+ }
+ else{
+ pRet = sqlite3Fts5ParseNode(pParse, FTS5_AND, pLeft, pRight, 0);
+ }
+ }
+
+ return pRet;
+}
+
+static char *fts5ExprTermPrint(Fts5ExprTerm *pTerm){
+ int nByte = 0;
+ Fts5ExprTerm *p;
+ char *zQuoted;
+
+ /* Determine the maximum amount of space required. */
+ for(p=pTerm; p; p=p->pSynonym){
+ nByte += (int)strlen(pTerm->zTerm) * 2 + 3 + 2;
+ }
+ zQuoted = sqlite3_malloc(nByte);
+
+ if( zQuoted ){
+ int i = 0;
+ for(p=pTerm; p; p=p->pSynonym){
+ char *zIn = p->zTerm;
+ zQuoted[i++] = '"';
+ while( *zIn ){
+ if( *zIn=='"' ) zQuoted[i++] = '"';
+ zQuoted[i++] = *zIn++;
+ }
+ zQuoted[i++] = '"';
+ if( p->pSynonym ) zQuoted[i++] = '|';
+ }
+ if( pTerm->bPrefix ){
+ zQuoted[i++] = ' ';
+ zQuoted[i++] = '*';
+ }
+ zQuoted[i++] = '\0';
+ }
+ return zQuoted;
+}
+
+static char *fts5PrintfAppend(char *zApp, const char *zFmt, ...){
+ char *zNew;
+ va_list ap;
+ va_start(ap, zFmt);
+ zNew = sqlite3_vmprintf(zFmt, ap);
+ va_end(ap);
+ if( zApp && zNew ){
+ char *zNew2 = sqlite3_mprintf("%s%s", zApp, zNew);
+ sqlite3_free(zNew);
+ zNew = zNew2;
+ }
+ sqlite3_free(zApp);
+ return zNew;
+}
+
+/*
+** Compose a tcl-readable representation of expression pExpr. Return a
+** pointer to a buffer containing that representation. It is the
+** responsibility of the caller to at some point free the buffer using
+** sqlite3_free().
+*/
+static char *fts5ExprPrintTcl(
+ Fts5Config *pConfig,
+ const char *zNearsetCmd,
+ Fts5ExprNode *pExpr
+){
+ char *zRet = 0;
+ if( pExpr->eType==FTS5_STRING || pExpr->eType==FTS5_TERM ){
+ Fts5ExprNearset *pNear = pExpr->pNear;
+ int i;
+ int iTerm;
+
+ zRet = fts5PrintfAppend(zRet, "%s ", zNearsetCmd);
+ if( zRet==0 ) return 0;
+ if( pNear->pColset ){
+ int *aiCol = pNear->pColset->aiCol;
+ int nCol = pNear->pColset->nCol;
+ if( nCol==1 ){
+ zRet = fts5PrintfAppend(zRet, "-col %d ", aiCol[0]);
+ }else{
+ zRet = fts5PrintfAppend(zRet, "-col {%d", aiCol[0]);
+ for(i=1; i<pNear->pColset->nCol; i++){
+ zRet = fts5PrintfAppend(zRet, " %d", aiCol[i]);
+ }
+ zRet = fts5PrintfAppend(zRet, "} ");
+ }
+ if( zRet==0 ) return 0;
+ }
+
+ if( pNear->nPhrase>1 ){
+ zRet = fts5PrintfAppend(zRet, "-near %d ", pNear->nNear);
+ if( zRet==0 ) return 0;
+ }
+
+ zRet = fts5PrintfAppend(zRet, "--");
+ if( zRet==0 ) return 0;
+
+ for(i=0; i<pNear->nPhrase; i++){
+ Fts5ExprPhrase *pPhrase = pNear->apPhrase[i];
+
+ zRet = fts5PrintfAppend(zRet, " {");
+ for(iTerm=0; zRet && iTerm<pPhrase->nTerm; iTerm++){
+ char *zTerm = pPhrase->aTerm[iTerm].zTerm;
+ zRet = fts5PrintfAppend(zRet, "%s%s", iTerm==0?"":" ", zTerm);
+ if( pPhrase->aTerm[iTerm].bPrefix ){
+ zRet = fts5PrintfAppend(zRet, "*");
+ }
+ }
+
+ if( zRet ) zRet = fts5PrintfAppend(zRet, "}");
+ if( zRet==0 ) return 0;
+ }
+
+ }else{
+ char const *zOp = 0;
+ int i;
+ switch( pExpr->eType ){
+ case FTS5_AND: zOp = "AND"; break;
+ case FTS5_NOT: zOp = "NOT"; break;
+ default:
+ assert( pExpr->eType==FTS5_OR );
+ zOp = "OR";
+ break;
+ }
+
+ zRet = sqlite3_mprintf("%s", zOp);
+ for(i=0; zRet && i<pExpr->nChild; i++){
+ char *z = fts5ExprPrintTcl(pConfig, zNearsetCmd, pExpr->apChild[i]);
+ if( !z ){
+ sqlite3_free(zRet);
+ zRet = 0;
+ }else{
+ zRet = fts5PrintfAppend(zRet, " [%z]", z);
+ }
+ }
+ }
+
+ return zRet;
+}
+
+static char *fts5ExprPrint(Fts5Config *pConfig, Fts5ExprNode *pExpr){
+ char *zRet = 0;
+ if( pExpr->eType==0 ){
+ return sqlite3_mprintf("\"\"");
+ }else
+ if( pExpr->eType==FTS5_STRING || pExpr->eType==FTS5_TERM ){
+ Fts5ExprNearset *pNear = pExpr->pNear;
+ int i;
+ int iTerm;
+
+ if( pNear->pColset ){
+ int iCol = pNear->pColset->aiCol[0];
+ zRet = fts5PrintfAppend(zRet, "%s : ", pConfig->azCol[iCol]);
+ if( zRet==0 ) return 0;
+ }
+
+ if( pNear->nPhrase>1 ){
+ zRet = fts5PrintfAppend(zRet, "NEAR(");
+ if( zRet==0 ) return 0;
+ }
+
+ for(i=0; i<pNear->nPhrase; i++){
+ Fts5ExprPhrase *pPhrase = pNear->apPhrase[i];
+ if( i!=0 ){
+ zRet = fts5PrintfAppend(zRet, " ");
+ if( zRet==0 ) return 0;
+ }
+ for(iTerm=0; iTerm<pPhrase->nTerm; iTerm++){
+ char *zTerm = fts5ExprTermPrint(&pPhrase->aTerm[iTerm]);
+ if( zTerm ){
+ zRet = fts5PrintfAppend(zRet, "%s%s", iTerm==0?"":" + ", zTerm);
+ sqlite3_free(zTerm);
+ }
+ if( zTerm==0 || zRet==0 ){
+ sqlite3_free(zRet);
+ return 0;
+ }
+ }
+ }
+
+ if( pNear->nPhrase>1 ){
+ zRet = fts5PrintfAppend(zRet, ", %d)", pNear->nNear);
+ if( zRet==0 ) return 0;
+ }
+
+ }else{
+ char const *zOp = 0;
+ int i;
+
+ switch( pExpr->eType ){
+ case FTS5_AND: zOp = " AND "; break;
+ case FTS5_NOT: zOp = " NOT "; break;
+ default:
+ assert( pExpr->eType==FTS5_OR );
+ zOp = " OR ";
+ break;
+ }
+
+ for(i=0; i<pExpr->nChild; i++){
+ char *z = fts5ExprPrint(pConfig, pExpr->apChild[i]);
+ if( z==0 ){
+ sqlite3_free(zRet);
+ zRet = 0;
+ }else{
+ int e = pExpr->apChild[i]->eType;
+ int b = (e!=FTS5_STRING && e!=FTS5_TERM && e!=FTS5_EOF);
+ zRet = fts5PrintfAppend(zRet, "%s%s%z%s",
+ (i==0 ? "" : zOp),
+ (b?"(":""), z, (b?")":"")
+ );
+ }
+ if( zRet==0 ) break;
+ }
+ }
+
+ return zRet;
+}
+
+/*
+** The implementation of user-defined scalar functions fts5_expr() (bTcl==0)
+** and fts5_expr_tcl() (bTcl!=0).
+*/
+static void fts5ExprFunction(
+ sqlite3_context *pCtx, /* Function call context */
+ int nArg, /* Number of args */
+ sqlite3_value **apVal, /* Function arguments */
+ int bTcl
+){
+ Fts5Global *pGlobal = (Fts5Global*)sqlite3_user_data(pCtx);
+ sqlite3 *db = sqlite3_context_db_handle(pCtx);
+ const char *zExpr = 0;
+ char *zErr = 0;
+ Fts5Expr *pExpr = 0;
+ int rc;
+ int i;
+
+ const char **azConfig; /* Array of arguments for Fts5Config */
+ const char *zNearsetCmd = "nearset";
+ int nConfig; /* Size of azConfig[] */
+ Fts5Config *pConfig = 0;
+ int iArg = 1;
+
+ if( nArg<1 ){
+ zErr = sqlite3_mprintf("wrong number of arguments to function %s",
+ bTcl ? "fts5_expr_tcl" : "fts5_expr"
+ );
+ sqlite3_result_error(pCtx, zErr, -1);
+ sqlite3_free(zErr);
+ return;
+ }
+
+ if( bTcl && nArg>1 ){
+ zNearsetCmd = (const char*)sqlite3_value_text(apVal[1]);
+ iArg = 2;
+ }
+
+ nConfig = 3 + (nArg-iArg);
+ azConfig = (const char**)sqlite3_malloc(sizeof(char*) * nConfig);
+ if( azConfig==0 ){
+ sqlite3_result_error_nomem(pCtx);
+ return;
+ }
+ azConfig[0] = 0;
+ azConfig[1] = "main";
+ azConfig[2] = "tbl";
+ for(i=3; iArg<nArg; iArg++){
+ azConfig[i++] = (const char*)sqlite3_value_text(apVal[iArg]);
+ }
+
+ zExpr = (const char*)sqlite3_value_text(apVal[0]);
+
+ rc = sqlite3Fts5ConfigParse(pGlobal, db, nConfig, azConfig, &pConfig, &zErr);
+ if( rc==SQLITE_OK ){
+ rc = sqlite3Fts5ExprNew(pConfig, zExpr, &pExpr, &zErr);
+ }
+ if( rc==SQLITE_OK ){
+ char *zText;
+ if( pExpr->pRoot->xNext==0 ){
+ zText = sqlite3_mprintf("");
+ }else if( bTcl ){
+ zText = fts5ExprPrintTcl(pConfig, zNearsetCmd, pExpr->pRoot);
+ }else{
+ zText = fts5ExprPrint(pConfig, pExpr->pRoot);
+ }
+ if( zText==0 ){
+ rc = SQLITE_NOMEM;
+ }else{
+ sqlite3_result_text(pCtx, zText, -1, SQLITE_TRANSIENT);
+ sqlite3_free(zText);
+ }
+ }
+
+ if( rc!=SQLITE_OK ){
+ if( zErr ){
+ sqlite3_result_error(pCtx, zErr, -1);
+ sqlite3_free(zErr);
+ }else{
+ sqlite3_result_error_code(pCtx, rc);
+ }
+ }
+ sqlite3_free((void *)azConfig);
+ sqlite3Fts5ConfigFree(pConfig);
+ sqlite3Fts5ExprFree(pExpr);
+}
+
+static void fts5ExprFunctionHr(
+ sqlite3_context *pCtx, /* Function call context */
+ int nArg, /* Number of args */
+ sqlite3_value **apVal /* Function arguments */
+){
+ fts5ExprFunction(pCtx, nArg, apVal, 0);
+}
+static void fts5ExprFunctionTcl(
+ sqlite3_context *pCtx, /* Function call context */
+ int nArg, /* Number of args */
+ sqlite3_value **apVal /* Function arguments */
+){
+ fts5ExprFunction(pCtx, nArg, apVal, 1);
+}
+
+/*
+** The implementation of an SQLite user-defined-function that accepts a
+** single integer as an argument. If the integer is an alpha-numeric
+** unicode code point, 1 is returned. Otherwise 0.
+*/
+static void fts5ExprIsAlnum(
+ sqlite3_context *pCtx, /* Function call context */
+ int nArg, /* Number of args */
+ sqlite3_value **apVal /* Function arguments */
+){
+ int iCode;
+ if( nArg!=1 ){
+ sqlite3_result_error(pCtx,
+ "wrong number of arguments to function fts5_isalnum", -1
+ );
+ return;
+ }
+ iCode = sqlite3_value_int(apVal[0]);
+ sqlite3_result_int(pCtx, sqlite3Fts5UnicodeIsalnum(iCode));
+}
+
+static void fts5ExprFold(
+ sqlite3_context *pCtx, /* Function call context */
+ int nArg, /* Number of args */
+ sqlite3_value **apVal /* Function arguments */
+){
+ if( nArg!=1 && nArg!=2 ){
+ sqlite3_result_error(pCtx,
+ "wrong number of arguments to function fts5_fold", -1
+ );
+ }else{
+ int iCode;
+ int bRemoveDiacritics = 0;
+ iCode = sqlite3_value_int(apVal[0]);
+ if( nArg==2 ) bRemoveDiacritics = sqlite3_value_int(apVal[1]);
+ sqlite3_result_int(pCtx, sqlite3Fts5UnicodeFold(iCode, bRemoveDiacritics));
+ }
+}
+
+/*
+** This is called during initialization to register the fts5_expr() scalar
+** UDF with the SQLite handle passed as the only argument.
+*/
+static int sqlite3Fts5ExprInit(Fts5Global *pGlobal, sqlite3 *db){
+ struct Fts5ExprFunc {
+ const char *z;
+ void (*x)(sqlite3_context*,int,sqlite3_value**);
+ } aFunc[] = {
+ { "fts5_expr", fts5ExprFunctionHr },
+ { "fts5_expr_tcl", fts5ExprFunctionTcl },
+ { "fts5_isalnum", fts5ExprIsAlnum },
+ { "fts5_fold", fts5ExprFold },
+ };
+ int i;
+ int rc = SQLITE_OK;
+ void *pCtx = (void*)pGlobal;
+
+ for(i=0; rc==SQLITE_OK && i<ArraySize(aFunc); i++){
+ struct Fts5ExprFunc *p = &aFunc[i];
+ rc = sqlite3_create_function(db, p->z, -1, SQLITE_UTF8, pCtx, p->x, 0, 0);
+ }
+
+ /* Avoid a warning indicating that sqlite3Fts5ParserTrace() is unused */
+#ifndef NDEBUG
+ (void)sqlite3Fts5ParserTrace;
+#endif
+
+ return rc;
+}
+
+/*
+** Return the number of phrases in expression pExpr.
+*/
+static int sqlite3Fts5ExprPhraseCount(Fts5Expr *pExpr){
+ return (pExpr ? pExpr->nPhrase : 0);
+}
+
+/*
+** Return the number of terms in the iPhrase'th phrase in pExpr.
+*/
+static int sqlite3Fts5ExprPhraseSize(Fts5Expr *pExpr, int iPhrase){
+ if( iPhrase<0 || iPhrase>=pExpr->nPhrase ) return 0;
+ return pExpr->apExprPhrase[iPhrase]->nTerm;
+}
+
+/*
+** This function is used to access the current position list for phrase
+** iPhrase.
+*/
+static int sqlite3Fts5ExprPoslist(Fts5Expr *pExpr, int iPhrase, const u8 **pa){
+ int nRet;
+ Fts5ExprPhrase *pPhrase = pExpr->apExprPhrase[iPhrase];
+ Fts5ExprNode *pNode = pPhrase->pNode;
+ if( pNode->bEof==0 && pNode->iRowid==pExpr->pRoot->iRowid ){
+ *pa = pPhrase->poslist.p;
+ nRet = pPhrase->poslist.n;
+ }else{
+ *pa = 0;
+ nRet = 0;
+ }
+ return nRet;
+}
+
+struct Fts5PoslistPopulator {
+ Fts5PoslistWriter writer;
+ int bOk; /* True if ok to populate */
+ int bMiss;
+};
+
+static Fts5PoslistPopulator *sqlite3Fts5ExprClearPoslists(Fts5Expr *pExpr, int bLive){
+ Fts5PoslistPopulator *pRet;
+ pRet = sqlite3_malloc(sizeof(Fts5PoslistPopulator)*pExpr->nPhrase);
+ if( pRet ){
+ int i;
+ memset(pRet, 0, sizeof(Fts5PoslistPopulator)*pExpr->nPhrase);
+ for(i=0; i<pExpr->nPhrase; i++){
+ Fts5Buffer *pBuf = &pExpr->apExprPhrase[i]->poslist;
+ Fts5ExprNode *pNode = pExpr->apExprPhrase[i]->pNode;
+ assert( pExpr->apExprPhrase[i]->nTerm==1 );
+ if( bLive &&
+ (pBuf->n==0 || pNode->iRowid!=pExpr->pRoot->iRowid || pNode->bEof)
+ ){
+ pRet[i].bMiss = 1;
+ }else{
+ pBuf->n = 0;
+ }
+ }
+ }
+ return pRet;
+}
+
+struct Fts5ExprCtx {
+ Fts5Expr *pExpr;
+ Fts5PoslistPopulator *aPopulator;
+ i64 iOff;
+};
+typedef struct Fts5ExprCtx Fts5ExprCtx;
+
+/*
+** TODO: Make this more efficient!
+*/
+static int fts5ExprColsetTest(Fts5Colset *pColset, int iCol){
+ int i;
+ for(i=0; i<pColset->nCol; i++){
+ if( pColset->aiCol[i]==iCol ) return 1;
+ }
+ return 0;
+}
+
+static int fts5ExprPopulatePoslistsCb(
+ void *pCtx, /* Copy of 2nd argument to xTokenize() */
+ int tflags, /* Mask of FTS5_TOKEN_* flags */
+ const char *pToken, /* Pointer to buffer containing token */
+ int nToken, /* Size of token in bytes */
+ int iUnused1, /* Byte offset of token within input text */
+ int iUnused2 /* Byte offset of end of token within input text */
+){
+ Fts5ExprCtx *p = (Fts5ExprCtx*)pCtx;
+ Fts5Expr *pExpr = p->pExpr;
+ int i;
+
+ UNUSED_PARAM2(iUnused1, iUnused2);
+
+ if( nToken>FTS5_MAX_TOKEN_SIZE ) nToken = FTS5_MAX_TOKEN_SIZE;
+ if( (tflags & FTS5_TOKEN_COLOCATED)==0 ) p->iOff++;
+ for(i=0; i<pExpr->nPhrase; i++){
+ Fts5ExprTerm *pTerm;
+ if( p->aPopulator[i].bOk==0 ) continue;
+ for(pTerm=&pExpr->apExprPhrase[i]->aTerm[0]; pTerm; pTerm=pTerm->pSynonym){
+ int nTerm = (int)strlen(pTerm->zTerm);
+ if( (nTerm==nToken || (nTerm<nToken && pTerm->bPrefix))
+ && memcmp(pTerm->zTerm, pToken, nTerm)==0
+ ){
+ int rc = sqlite3Fts5PoslistWriterAppend(
+ &pExpr->apExprPhrase[i]->poslist, &p->aPopulator[i].writer, p->iOff
+ );
+ if( rc ) return rc;
+ break;
+ }
+ }
+ }
+ return SQLITE_OK;
+}
+
+static int sqlite3Fts5ExprPopulatePoslists(
+ Fts5Config *pConfig,
+ Fts5Expr *pExpr,
+ Fts5PoslistPopulator *aPopulator,
+ int iCol,
+ const char *z, int n
+){
+ int i;
+ Fts5ExprCtx sCtx;
+ sCtx.pExpr = pExpr;
+ sCtx.aPopulator = aPopulator;
+ sCtx.iOff = (((i64)iCol) << 32) - 1;
+
+ for(i=0; i<pExpr->nPhrase; i++){
+ Fts5ExprNode *pNode = pExpr->apExprPhrase[i]->pNode;
+ Fts5Colset *pColset = pNode->pNear->pColset;
+ if( (pColset && 0==fts5ExprColsetTest(pColset, iCol))
+ || aPopulator[i].bMiss
+ ){
+ aPopulator[i].bOk = 0;
+ }else{
+ aPopulator[i].bOk = 1;
+ }
+ }
+
+ return sqlite3Fts5Tokenize(pConfig,
+ FTS5_TOKENIZE_DOCUMENT, z, n, (void*)&sCtx, fts5ExprPopulatePoslistsCb
+ );
+}
+
+static void fts5ExprClearPoslists(Fts5ExprNode *pNode){
+ if( pNode->eType==FTS5_TERM || pNode->eType==FTS5_STRING ){
+ pNode->pNear->apPhrase[0]->poslist.n = 0;
+ }else{
+ int i;
+ for(i=0; i<pNode->nChild; i++){
+ fts5ExprClearPoslists(pNode->apChild[i]);
+ }
+ }
+}
+
+static int fts5ExprCheckPoslists(Fts5ExprNode *pNode, i64 iRowid){
+ pNode->iRowid = iRowid;
+ pNode->bEof = 0;
+ switch( pNode->eType ){
+ case FTS5_TERM:
+ case FTS5_STRING:
+ return (pNode->pNear->apPhrase[0]->poslist.n>0);
+
+ case FTS5_AND: {
+ int i;
+ for(i=0; i<pNode->nChild; i++){
+ if( fts5ExprCheckPoslists(pNode->apChild[i], iRowid)==0 ){
+ fts5ExprClearPoslists(pNode);
+ return 0;
+ }
+ }
+ break;
+ }
+
+ case FTS5_OR: {
+ int i;
+ int bRet = 0;
+ for(i=0; i<pNode->nChild; i++){
+ if( fts5ExprCheckPoslists(pNode->apChild[i], iRowid) ){
+ bRet = 1;
+ }
+ }
+ return bRet;
+ }
+
+ default: {
+ assert( pNode->eType==FTS5_NOT );
+ if( 0==fts5ExprCheckPoslists(pNode->apChild[0], iRowid)
+ || 0!=fts5ExprCheckPoslists(pNode->apChild[1], iRowid)
+ ){
+ fts5ExprClearPoslists(pNode);
+ return 0;
+ }
+ break;
+ }
+ }
+ return 1;
+}
+
+static void sqlite3Fts5ExprCheckPoslists(Fts5Expr *pExpr, i64 iRowid){
+ fts5ExprCheckPoslists(pExpr->pRoot, iRowid);
+}
+
+static void fts5ExprClearEof(Fts5ExprNode *pNode){
+ int i;
+ for(i=0; i<pNode->nChild; i++){
+ fts5ExprClearEof(pNode->apChild[i]);
+ }
+ pNode->bEof = 0;
+}
+static void sqlite3Fts5ExprClearEof(Fts5Expr *pExpr){
+ fts5ExprClearEof(pExpr->pRoot);
+}
+
+/*
+** This function is only called for detail=columns tables.
+*/
+static int sqlite3Fts5ExprPhraseCollist(
+ Fts5Expr *pExpr,
+ int iPhrase,
+ const u8 **ppCollist,
+ int *pnCollist
+){
+ Fts5ExprPhrase *pPhrase = pExpr->apExprPhrase[iPhrase];
+ Fts5ExprNode *pNode = pPhrase->pNode;
+ int rc = SQLITE_OK;
+
+ assert( iPhrase>=0 && iPhrase<pExpr->nPhrase );
+ assert( pExpr->pConfig->eDetail==FTS5_DETAIL_COLUMNS );
+
+ if( pNode->bEof==0
+ && pNode->iRowid==pExpr->pRoot->iRowid
+ && pPhrase->poslist.n>0
+ ){
+ Fts5ExprTerm *pTerm = &pPhrase->aTerm[0];
+ if( pTerm->pSynonym ){
+ Fts5Buffer *pBuf = (Fts5Buffer*)&pTerm->pSynonym[1];
+ rc = fts5ExprSynonymList(
+ pTerm, pNode->iRowid, pBuf, (u8**)ppCollist, pnCollist
+ );
+ }else{
+ *ppCollist = pPhrase->aTerm[0].pIter->pData;
+ *pnCollist = pPhrase->aTerm[0].pIter->nData;
+ }
+ }else{
+ *ppCollist = 0;
+ *pnCollist = 0;
+ }
+
+ return rc;
+}
+
+
+/*
+** 2014 August 11
+**
+** 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.
+**
+******************************************************************************
+**
+*/
+
+
+
+/* #include "fts5Int.h" */
+
+typedef struct Fts5HashEntry Fts5HashEntry;
+
+/*
+** This file contains the implementation of an in-memory hash table used
+** to accumuluate "term -> doclist" content before it is flused to a level-0
+** segment.
+*/
+
+
+struct Fts5Hash {
+ int eDetail; /* Copy of Fts5Config.eDetail */
+ int *pnByte; /* Pointer to bytes counter */
+ int nEntry; /* Number of entries currently in hash */
+ int nSlot; /* Size of aSlot[] array */
+ Fts5HashEntry *pScan; /* Current ordered scan item */
+ Fts5HashEntry **aSlot; /* Array of hash slots */
+};
+
+/*
+** Each entry in the hash table is represented by an object of the
+** following type. Each object, its key (zKey[]) and its current data
+** are stored in a single memory allocation. The position list data
+** immediately follows the key data in memory.
+**
+** The data that follows the key is in a similar, but not identical format
+** to the doclist data stored in the database. It is:
+**
+** * Rowid, as a varint
+** * Position list, without 0x00 terminator.
+** * Size of previous position list and rowid, as a 4 byte
+** big-endian integer.
+**
+** iRowidOff:
+** Offset of last rowid written to data area. Relative to first byte of
+** structure.
+**
+** nData:
+** Bytes of data written since iRowidOff.
+*/
+struct Fts5HashEntry {
+ Fts5HashEntry *pHashNext; /* Next hash entry with same hash-key */
+ Fts5HashEntry *pScanNext; /* Next entry in sorted order */
+
+ int nAlloc; /* Total size of allocation */
+ int iSzPoslist; /* Offset of space for 4-byte poslist size */
+ int nData; /* Total bytes of data (incl. structure) */
+ int nKey; /* Length of zKey[] in bytes */
+ u8 bDel; /* Set delete-flag @ iSzPoslist */
+ u8 bContent; /* Set content-flag (detail=none mode) */
+ i16 iCol; /* Column of last value written */
+ int iPos; /* Position of last value written */
+ i64 iRowid; /* Rowid of last value written */
+ char zKey[8]; /* Nul-terminated entry key */
+};
+
+/*
+** Size of Fts5HashEntry without the zKey[] array.
+*/
+#define FTS5_HASHENTRYSIZE (sizeof(Fts5HashEntry)-8)
+
+
+
+/*
+** Allocate a new hash table.
+*/
+static int sqlite3Fts5HashNew(Fts5Config *pConfig, Fts5Hash **ppNew, int *pnByte){
+ int rc = SQLITE_OK;
+ Fts5Hash *pNew;
+
+ *ppNew = pNew = (Fts5Hash*)sqlite3_malloc(sizeof(Fts5Hash));
+ if( pNew==0 ){
+ rc = SQLITE_NOMEM;
+ }else{
+ int nByte;
+ memset(pNew, 0, sizeof(Fts5Hash));
+ pNew->pnByte = pnByte;
+ pNew->eDetail = pConfig->eDetail;
+
+ pNew->nSlot = 1024;
+ nByte = sizeof(Fts5HashEntry*) * pNew->nSlot;
+ pNew->aSlot = (Fts5HashEntry**)sqlite3_malloc(nByte);
+ if( pNew->aSlot==0 ){
+ sqlite3_free(pNew);
+ *ppNew = 0;
+ rc = SQLITE_NOMEM;
+ }else{
+ memset(pNew->aSlot, 0, nByte);
+ }
+ }
+ return rc;
+}
+
+/*
+** Free a hash table object.
+*/
+static void sqlite3Fts5HashFree(Fts5Hash *pHash){
+ if( pHash ){
+ sqlite3Fts5HashClear(pHash);
+ sqlite3_free(pHash->aSlot);
+ sqlite3_free(pHash);
+ }
+}
+
+/*
+** Empty (but do not delete) a hash table.
+*/
+static void sqlite3Fts5HashClear(Fts5Hash *pHash){
+ int i;
+ for(i=0; i<pHash->nSlot; i++){
+ Fts5HashEntry *pNext;
+ Fts5HashEntry *pSlot;
+ for(pSlot=pHash->aSlot[i]; pSlot; pSlot=pNext){
+ pNext = pSlot->pHashNext;
+ sqlite3_free(pSlot);
+ }
+ }
+ memset(pHash->aSlot, 0, pHash->nSlot * sizeof(Fts5HashEntry*));
+ pHash->nEntry = 0;
+}
+
+static unsigned int fts5HashKey(int nSlot, const u8 *p, int n){
+ int i;
+ unsigned int h = 13;
+ for(i=n-1; i>=0; i--){
+ h = (h << 3) ^ h ^ p[i];
+ }
+ return (h % nSlot);
+}
+
+static unsigned int fts5HashKey2(int nSlot, u8 b, const u8 *p, int n){
+ int i;
+ unsigned int h = 13;
+ for(i=n-1; i>=0; i--){
+ h = (h << 3) ^ h ^ p[i];
+ }
+ h = (h << 3) ^ h ^ b;
+ return (h % nSlot);
+}
+
+/*
+** Resize the hash table by doubling the number of slots.
+*/
+static int fts5HashResize(Fts5Hash *pHash){
+ int nNew = pHash->nSlot*2;
+ int i;
+ Fts5HashEntry **apNew;
+ Fts5HashEntry **apOld = pHash->aSlot;
+
+ apNew = (Fts5HashEntry**)sqlite3_malloc(nNew*sizeof(Fts5HashEntry*));
+ if( !apNew ) return SQLITE_NOMEM;
+ memset(apNew, 0, nNew*sizeof(Fts5HashEntry*));
+
+ for(i=0; i<pHash->nSlot; i++){
+ while( apOld[i] ){
+ int iHash;
+ Fts5HashEntry *p = apOld[i];
+ apOld[i] = p->pHashNext;
+ iHash = fts5HashKey(nNew, (u8*)p->zKey, (int)strlen(p->zKey));
+ p->pHashNext = apNew[iHash];
+ apNew[iHash] = p;
+ }
+ }
+
+ sqlite3_free(apOld);
+ pHash->nSlot = nNew;
+ pHash->aSlot = apNew;
+ return SQLITE_OK;
+}
+
+static void fts5HashAddPoslistSize(Fts5Hash *pHash, Fts5HashEntry *p){
+ if( p->iSzPoslist ){
+ u8 *pPtr = (u8*)p;
+ if( pHash->eDetail==FTS5_DETAIL_NONE ){
+ assert( p->nData==p->iSzPoslist );
+ if( p->bDel ){
+ pPtr[p->nData++] = 0x00;
+ if( p->bContent ){
+ pPtr[p->nData++] = 0x00;
+ }
+ }
+ }else{
+ int nSz = (p->nData - p->iSzPoslist - 1); /* Size in bytes */
+ int nPos = nSz*2 + p->bDel; /* Value of nPos field */
+
+ assert( p->bDel==0 || p->bDel==1 );
+ if( nPos<=127 ){
+ pPtr[p->iSzPoslist] = (u8)nPos;
+ }else{
+ int nByte = sqlite3Fts5GetVarintLen((u32)nPos);
+ memmove(&pPtr[p->iSzPoslist + nByte], &pPtr[p->iSzPoslist + 1], nSz);
+ sqlite3Fts5PutVarint(&pPtr[p->iSzPoslist], nPos);
+ p->nData += (nByte-1);
+ }
+ }
+
+ p->iSzPoslist = 0;
+ p->bDel = 0;
+ p->bContent = 0;
+ }
+}
+
+/*
+** Add an entry to the in-memory hash table. The key is the concatenation
+** of bByte and (pToken/nToken). The value is (iRowid/iCol/iPos).
+**
+** (bByte || pToken) -> (iRowid,iCol,iPos)
+**
+** Or, if iCol is negative, then the value is a delete marker.
+*/
+static int sqlite3Fts5HashWrite(
+ Fts5Hash *pHash,
+ i64 iRowid, /* Rowid for this entry */
+ int iCol, /* Column token appears in (-ve -> delete) */
+ int iPos, /* Position of token within column */
+ char bByte, /* First byte of token */
+ const char *pToken, int nToken /* Token to add or remove to or from index */
+){
+ unsigned int iHash;
+ Fts5HashEntry *p;
+ u8 *pPtr;
+ int nIncr = 0; /* Amount to increment (*pHash->pnByte) by */
+ int bNew; /* If non-delete entry should be written */
+
+ bNew = (pHash->eDetail==FTS5_DETAIL_FULL);
+
+ /* Attempt to locate an existing hash entry */
+ iHash = fts5HashKey2(pHash->nSlot, (u8)bByte, (const u8*)pToken, nToken);
+ for(p=pHash->aSlot[iHash]; p; p=p->pHashNext){
+ if( p->zKey[0]==bByte
+ && p->nKey==nToken
+ && memcmp(&p->zKey[1], pToken, nToken)==0
+ ){
+ break;
+ }
+ }
+
+ /* If an existing hash entry cannot be found, create a new one. */
+ if( p==0 ){
+ /* Figure out how much space to allocate */
+ int nByte = FTS5_HASHENTRYSIZE + (nToken+1) + 1 + 64;
+ if( nByte<128 ) nByte = 128;
+
+ /* Grow the Fts5Hash.aSlot[] array if necessary. */
+ if( (pHash->nEntry*2)>=pHash->nSlot ){
+ int rc = fts5HashResize(pHash);
+ if( rc!=SQLITE_OK ) return rc;
+ iHash = fts5HashKey2(pHash->nSlot, (u8)bByte, (const u8*)pToken, nToken);
+ }
+
+ /* Allocate new Fts5HashEntry and add it to the hash table. */
+ p = (Fts5HashEntry*)sqlite3_malloc(nByte);
+ if( !p ) return SQLITE_NOMEM;
+ memset(p, 0, FTS5_HASHENTRYSIZE);
+ p->nAlloc = nByte;
+ p->zKey[0] = bByte;
+ memcpy(&p->zKey[1], pToken, nToken);
+ assert( iHash==fts5HashKey(pHash->nSlot, (u8*)p->zKey, nToken+1) );
+ p->nKey = nToken;
+ p->zKey[nToken+1] = '\0';
+ p->nData = nToken+1 + 1 + FTS5_HASHENTRYSIZE;
+ p->pHashNext = pHash->aSlot[iHash];
+ pHash->aSlot[iHash] = p;
+ pHash->nEntry++;
+
+ /* Add the first rowid field to the hash-entry */
+ p->nData += sqlite3Fts5PutVarint(&((u8*)p)[p->nData], iRowid);
+ p->iRowid = iRowid;
+
+ p->iSzPoslist = p->nData;
+ if( pHash->eDetail!=FTS5_DETAIL_NONE ){
+ p->nData += 1;
+ p->iCol = (pHash->eDetail==FTS5_DETAIL_FULL ? 0 : -1);
+ }
+
+ nIncr += p->nData;
+ }else{
+
+ /* Appending to an existing hash-entry. Check that there is enough
+ ** space to append the largest possible new entry. Worst case scenario
+ ** is:
+ **
+ ** + 9 bytes for a new rowid,
+ ** + 4 byte reserved for the "poslist size" varint.
+ ** + 1 byte for a "new column" byte,
+ ** + 3 bytes for a new column number (16-bit max) as a varint,
+ ** + 5 bytes for the new position offset (32-bit max).
+ */
+ if( (p->nAlloc - p->nData) < (9 + 4 + 1 + 3 + 5) ){
+ int nNew = p->nAlloc * 2;
+ Fts5HashEntry *pNew;
+ Fts5HashEntry **pp;
+ pNew = (Fts5HashEntry*)sqlite3_realloc(p, nNew);
+ if( pNew==0 ) return SQLITE_NOMEM;
+ pNew->nAlloc = nNew;
+ for(pp=&pHash->aSlot[iHash]; *pp!=p; pp=&(*pp)->pHashNext);
+ *pp = pNew;
+ p = pNew;
+ }
+ nIncr -= p->nData;
+ }
+ assert( (p->nAlloc - p->nData) >= (9 + 4 + 1 + 3 + 5) );
+
+ pPtr = (u8*)p;
+
+ /* If this is a new rowid, append the 4-byte size field for the previous
+ ** entry, and the new rowid for this entry. */
+ if( iRowid!=p->iRowid ){
+ fts5HashAddPoslistSize(pHash, p);
+ p->nData += sqlite3Fts5PutVarint(&pPtr[p->nData], iRowid - p->iRowid);
+ p->iRowid = iRowid;
+ bNew = 1;
+ p->iSzPoslist = p->nData;
+ if( pHash->eDetail!=FTS5_DETAIL_NONE ){
+ p->nData += 1;
+ p->iCol = (pHash->eDetail==FTS5_DETAIL_FULL ? 0 : -1);
+ p->iPos = 0;
+ }
+ }
+
+ if( iCol>=0 ){
+ if( pHash->eDetail==FTS5_DETAIL_NONE ){
+ p->bContent = 1;
+ }else{
+ /* Append a new column value, if necessary */
+ assert( iCol>=p->iCol );
+ if( iCol!=p->iCol ){
+ if( pHash->eDetail==FTS5_DETAIL_FULL ){
+ pPtr[p->nData++] = 0x01;
+ p->nData += sqlite3Fts5PutVarint(&pPtr[p->nData], iCol);
+ p->iCol = (i16)iCol;
+ p->iPos = 0;
+ }else{
+ bNew = 1;
+ p->iCol = (i16)(iPos = iCol);
+ }
+ }
+
+ /* Append the new position offset, if necessary */
+ if( bNew ){
+ p->nData += sqlite3Fts5PutVarint(&pPtr[p->nData], iPos - p->iPos + 2);
+ p->iPos = iPos;
+ }
+ }
+ }else{
+ /* This is a delete. Set the delete flag. */
+ p->bDel = 1;
+ }
+
+ nIncr += p->nData;
+ *pHash->pnByte += nIncr;
+ return SQLITE_OK;
+}
+
+
+/*
+** Arguments pLeft and pRight point to linked-lists of hash-entry objects,
+** each sorted in key order. This function merges the two lists into a
+** single list and returns a pointer to its first element.
+*/
+static Fts5HashEntry *fts5HashEntryMerge(
+ Fts5HashEntry *pLeft,
+ Fts5HashEntry *pRight
+){
+ Fts5HashEntry *p1 = pLeft;
+ Fts5HashEntry *p2 = pRight;
+ Fts5HashEntry *pRet = 0;
+ Fts5HashEntry **ppOut = &pRet;
+
+ while( p1 || p2 ){
+ if( p1==0 ){
+ *ppOut = p2;
+ p2 = 0;
+ }else if( p2==0 ){
+ *ppOut = p1;
+ p1 = 0;
+ }else{
+ int i = 0;
+ while( p1->zKey[i]==p2->zKey[i] ) i++;
+
+ if( ((u8)p1->zKey[i])>((u8)p2->zKey[i]) ){
+ /* p2 is smaller */
+ *ppOut = p2;
+ ppOut = &p2->pScanNext;
+ p2 = p2->pScanNext;
+ }else{
+ /* p1 is smaller */
+ *ppOut = p1;
+ ppOut = &p1->pScanNext;
+ p1 = p1->pScanNext;
+ }
+ *ppOut = 0;
+ }
+ }
+
+ return pRet;
+}
+
+/*
+** Extract all tokens from hash table iHash and link them into a list
+** in sorted order. The hash table is cleared before returning. It is
+** the responsibility of the caller to free the elements of the returned
+** list.
+*/
+static int fts5HashEntrySort(
+ Fts5Hash *pHash,
+ const char *pTerm, int nTerm, /* Query prefix, if any */
+ Fts5HashEntry **ppSorted
+){
+ const int nMergeSlot = 32;
+ Fts5HashEntry **ap;
+ Fts5HashEntry *pList;
+ int iSlot;
+ int i;
+
+ *ppSorted = 0;
+ ap = sqlite3_malloc(sizeof(Fts5HashEntry*) * nMergeSlot);
+ if( !ap ) return SQLITE_NOMEM;
+ memset(ap, 0, sizeof(Fts5HashEntry*) * nMergeSlot);
+
+ for(iSlot=0; iSlot<pHash->nSlot; iSlot++){
+ Fts5HashEntry *pIter;
+ for(pIter=pHash->aSlot[iSlot]; pIter; pIter=pIter->pHashNext){
+ if( pTerm==0 || 0==memcmp(pIter->zKey, pTerm, nTerm) ){
+ Fts5HashEntry *pEntry = pIter;
+ pEntry->pScanNext = 0;
+ for(i=0; ap[i]; i++){
+ pEntry = fts5HashEntryMerge(pEntry, ap[i]);
+ ap[i] = 0;
+ }
+ ap[i] = pEntry;
+ }
+ }
+ }
+
+ pList = 0;
+ for(i=0; i<nMergeSlot; i++){
+ pList = fts5HashEntryMerge(pList, ap[i]);
+ }
+
+ pHash->nEntry = 0;
+ sqlite3_free(ap);
+ *ppSorted = pList;
+ return SQLITE_OK;
+}
+
+/*
+** Query the hash table for a doclist associated with term pTerm/nTerm.
+*/
+static int sqlite3Fts5HashQuery(
+ Fts5Hash *pHash, /* Hash table to query */
+ const char *pTerm, int nTerm, /* Query term */
+ const u8 **ppDoclist, /* OUT: Pointer to doclist for pTerm */
+ int *pnDoclist /* OUT: Size of doclist in bytes */
+){
+ unsigned int iHash = fts5HashKey(pHash->nSlot, (const u8*)pTerm, nTerm);
+ Fts5HashEntry *p;
+
+ for(p=pHash->aSlot[iHash]; p; p=p->pHashNext){
+ if( memcmp(p->zKey, pTerm, nTerm)==0 && p->zKey[nTerm]==0 ) break;
+ }
+
+ if( p ){
+ fts5HashAddPoslistSize(pHash, p);
+ *ppDoclist = (const u8*)&p->zKey[nTerm+1];
+ *pnDoclist = p->nData - (FTS5_HASHENTRYSIZE + nTerm + 1);
+ }else{
+ *ppDoclist = 0;
+ *pnDoclist = 0;
+ }
+
+ return SQLITE_OK;
+}
+
+static int sqlite3Fts5HashScanInit(
+ Fts5Hash *p, /* Hash table to query */
+ const char *pTerm, int nTerm /* Query prefix */
+){
+ return fts5HashEntrySort(p, pTerm, nTerm, &p->pScan);
+}
+
+static void sqlite3Fts5HashScanNext(Fts5Hash *p){
+ assert( !sqlite3Fts5HashScanEof(p) );
+ p->pScan = p->pScan->pScanNext;
+}
+
+static int sqlite3Fts5HashScanEof(Fts5Hash *p){
+ return (p->pScan==0);
+}
+
+static void sqlite3Fts5HashScanEntry(
+ Fts5Hash *pHash,
+ const char **pzTerm, /* OUT: term (nul-terminated) */
+ const u8 **ppDoclist, /* OUT: pointer to doclist */
+ int *pnDoclist /* OUT: size of doclist in bytes */
+){
+ Fts5HashEntry *p;
+ if( (p = pHash->pScan) ){
+ int nTerm = (int)strlen(p->zKey);
+ fts5HashAddPoslistSize(pHash, p);
+ *pzTerm = p->zKey;
+ *ppDoclist = (const u8*)&p->zKey[nTerm+1];
+ *pnDoclist = p->nData - (FTS5_HASHENTRYSIZE + nTerm + 1);
+ }else{
+ *pzTerm = 0;
+ *ppDoclist = 0;
+ *pnDoclist = 0;
+ }
+}
+
+
+/*
+** 2014 May 31
+**
+** 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.
+**
+******************************************************************************
+**
+** Low level access to the FTS index stored in the database file. The
+** routines in this file file implement all read and write access to the
+** %_data table. Other parts of the system access this functionality via
+** the interface defined in fts5Int.h.
+*/
+
+
+/* #include "fts5Int.h" */
+
+/*
+** Overview:
+**
+** The %_data table contains all the FTS indexes for an FTS5 virtual table.
+** As well as the main term index, there may be up to 31 prefix indexes.
+** The format is similar to FTS3/4, except that:
+**
+** * all segment b-tree leaf data is stored in fixed size page records
+** (e.g. 1000 bytes). A single doclist may span multiple pages. Care is
+** taken to ensure it is possible to iterate in either direction through
+** the entries in a doclist, or to seek to a specific entry within a
+** doclist, without loading it into memory.
+**
+** * large doclists that span many pages have associated "doclist index"
+** records that contain a copy of the first rowid on each page spanned by
+** the doclist. This is used to speed up seek operations, and merges of
+** large doclists with very small doclists.
+**
+** * extra fields in the "structure record" record the state of ongoing
+** incremental merge operations.
+**
+*/
+
+
+#define FTS5_OPT_WORK_UNIT 1000 /* Number of leaf pages per optimize step */
+#define FTS5_WORK_UNIT 64 /* Number of leaf pages in unit of work */
+
+#define FTS5_MIN_DLIDX_SIZE 4 /* Add dlidx if this many empty pages */
+
+#define FTS5_MAIN_PREFIX '0'
+
+#if FTS5_MAX_PREFIX_INDEXES > 31
+# error "FTS5_MAX_PREFIX_INDEXES is too large"
+#endif
+
+/*
+** Details:
+**
+** The %_data table managed by this module,
+**
+** CREATE TABLE %_data(id INTEGER PRIMARY KEY, block BLOB);
+**
+** , contains the following 5 types of records. See the comments surrounding
+** the FTS5_*_ROWID macros below for a description of how %_data rowids are
+** assigned to each fo them.
+**
+** 1. Structure Records:
+**
+** The set of segments that make up an index - the index structure - are
+** recorded in a single record within the %_data table. The record consists
+** of a single 32-bit configuration cookie value followed by a list of
+** SQLite varints. If the FTS table features more than one index (because
+** there are one or more prefix indexes), it is guaranteed that all share
+** the same cookie value.
+**
+** Immediately following the configuration cookie, the record begins with
+** three varints:
+**
+** + number of levels,
+** + total number of segments on all levels,
+** + value of write counter.
+**
+** Then, for each level from 0 to nMax:
+**
+** + number of input segments in ongoing merge.
+** + total number of segments in level.
+** + for each segment from oldest to newest:
+** + segment id (always > 0)
+** + first leaf page number (often 1, always greater than 0)
+** + final leaf page number
+**
+** 2. The Averages Record:
+**
+** A single record within the %_data table. The data is a list of varints.
+** The first value is the number of rows in the index. Then, for each column
+** from left to right, the total number of tokens in the column for all
+** rows of the table.
+**
+** 3. Segment leaves:
+**
+** TERM/DOCLIST FORMAT:
+**
+** Most of each segment leaf is taken up by term/doclist data. The
+** general format of term/doclist, starting with the first term
+** on the leaf page, is:
+**
+** varint : size of first term
+** blob: first term data
+** doclist: first doclist
+** zero-or-more {
+** varint: number of bytes in common with previous term
+** varint: number of bytes of new term data (nNew)
+** blob: nNew bytes of new term data
+** doclist: next doclist
+** }
+**
+** doclist format:
+**
+** varint: first rowid
+** poslist: first poslist
+** zero-or-more {
+** varint: rowid delta (always > 0)
+** poslist: next poslist
+** }
+**
+** poslist format:
+**
+** varint: size of poslist in bytes multiplied by 2, not including
+** this field. Plus 1 if this entry carries the "delete" flag.
+** collist: collist for column 0
+** zero-or-more {
+** 0x01 byte
+** varint: column number (I)
+** collist: collist for column I
+** }
+**
+** collist format:
+**
+** varint: first offset + 2
+** zero-or-more {
+** varint: offset delta + 2
+** }
+**
+** PAGE FORMAT
+**
+** Each leaf page begins with a 4-byte header containing 2 16-bit
+** unsigned integer fields in big-endian format. They are:
+**
+** * The byte offset of the first rowid on the page, if it exists
+** and occurs before the first term (otherwise 0).
+**
+** * The byte offset of the start of the page footer. If the page
+** footer is 0 bytes in size, then this field is the same as the
+** size of the leaf page in bytes.
+**
+** The page footer consists of a single varint for each term located
+** on the page. Each varint is the byte offset of the current term
+** within the page, delta-compressed against the previous value. In
+** other words, the first varint in the footer is the byte offset of
+** the first term, the second is the byte offset of the second less that
+** of the first, and so on.
+**
+** The term/doclist format described above is accurate if the entire
+** term/doclist data fits on a single leaf page. If this is not the case,
+** the format is changed in two ways:
+**
+** + if the first rowid on a page occurs before the first term, it
+** is stored as a literal value:
+**
+** varint: first rowid
+**
+** + the first term on each page is stored in the same way as the
+** very first term of the segment:
+**
+** varint : size of first term
+** blob: first term data
+**
+** 5. Segment doclist indexes:
+**
+** Doclist indexes are themselves b-trees, however they usually consist of
+** a single leaf record only. The format of each doclist index leaf page
+** is:
+**
+** * Flags byte. Bits are:
+** 0x01: Clear if leaf is also the root page, otherwise set.
+**
+** * Page number of fts index leaf page. As a varint.
+**
+** * First rowid on page indicated by previous field. As a varint.
+**
+** * A list of varints, one for each subsequent termless page. A
+** positive delta if the termless page contains at least one rowid,
+** or an 0x00 byte otherwise.
+**
+** Internal doclist index nodes are:
+**
+** * Flags byte. Bits are:
+** 0x01: Clear for root page, otherwise set.
+**
+** * Page number of first child page. As a varint.
+**
+** * Copy of first rowid on page indicated by previous field. As a varint.
+**
+** * A list of delta-encoded varints - the first rowid on each subsequent
+** child page.
+**
+*/
+
+/*
+** Rowids for the averages and structure records in the %_data table.
+*/
+#define FTS5_AVERAGES_ROWID 1 /* Rowid used for the averages record */
+#define FTS5_STRUCTURE_ROWID 10 /* The structure record */
+
+/*
+** Macros determining the rowids used by segment leaves and dlidx leaves
+** and nodes. All nodes and leaves are stored in the %_data table with large
+** positive rowids.
+**
+** Each segment has a unique non-zero 16-bit id.
+**
+** The rowid for each segment leaf is found by passing the segment id and
+** the leaf page number to the FTS5_SEGMENT_ROWID macro. Leaves are numbered
+** sequentially starting from 1.
+*/
+#define FTS5_DATA_ID_B 16 /* Max seg id number 65535 */
+#define FTS5_DATA_DLI_B 1 /* Doclist-index flag (1 bit) */
+#define FTS5_DATA_HEIGHT_B 5 /* Max dlidx tree height of 32 */
+#define FTS5_DATA_PAGE_B 31 /* Max page number of 2147483648 */
+
+#define fts5_dri(segid, dlidx, height, pgno) ( \
+ ((i64)(segid) << (FTS5_DATA_PAGE_B+FTS5_DATA_HEIGHT_B+FTS5_DATA_DLI_B)) + \
+ ((i64)(dlidx) << (FTS5_DATA_PAGE_B + FTS5_DATA_HEIGHT_B)) + \
+ ((i64)(height) << (FTS5_DATA_PAGE_B)) + \
+ ((i64)(pgno)) \
+)
+
+#define FTS5_SEGMENT_ROWID(segid, pgno) fts5_dri(segid, 0, 0, pgno)
+#define FTS5_DLIDX_ROWID(segid, height, pgno) fts5_dri(segid, 1, height, pgno)
+
+/*
+** Maximum segments permitted in a single index
+*/
+#define FTS5_MAX_SEGMENT 2000
+
+#ifdef SQLITE_DEBUG
+static int sqlite3Fts5Corrupt() { return SQLITE_CORRUPT_VTAB; }
+#endif
+
+
+/*
+** Each time a blob is read from the %_data table, it is padded with this
+** many zero bytes. This makes it easier to decode the various record formats
+** without overreading if the records are corrupt.
+*/
+#define FTS5_DATA_ZERO_PADDING 8
+#define FTS5_DATA_PADDING 20
+
+typedef struct Fts5Data Fts5Data;
+typedef struct Fts5DlidxIter Fts5DlidxIter;
+typedef struct Fts5DlidxLvl Fts5DlidxLvl;
+typedef struct Fts5DlidxWriter Fts5DlidxWriter;
+typedef struct Fts5Iter Fts5Iter;
+typedef struct Fts5PageWriter Fts5PageWriter;
+typedef struct Fts5SegIter Fts5SegIter;
+typedef struct Fts5DoclistIter Fts5DoclistIter;
+typedef struct Fts5SegWriter Fts5SegWriter;
+typedef struct Fts5Structure Fts5Structure;
+typedef struct Fts5StructureLevel Fts5StructureLevel;
+typedef struct Fts5StructureSegment Fts5StructureSegment;
+
+struct Fts5Data {
+ u8 *p; /* Pointer to buffer containing record */
+ int nn; /* Size of record in bytes */
+ int szLeaf; /* Size of leaf without page-index */
+};
+
+/*
+** One object per %_data table.
+*/
+struct Fts5Index {
+ Fts5Config *pConfig; /* Virtual table configuration */
+ char *zDataTbl; /* Name of %_data table */
+ int nWorkUnit; /* Leaf pages in a "unit" of work */
+
+ /*
+ ** Variables related to the accumulation of tokens and doclists within the
+ ** in-memory hash tables before they are flushed to disk.
+ */
+ Fts5Hash *pHash; /* Hash table for in-memory data */
+ int nPendingData; /* Current bytes of pending data */
+ i64 iWriteRowid; /* Rowid for current doc being written */
+ int bDelete; /* Current write is a delete */
+
+ /* Error state. */
+ int rc; /* Current error code */
+
+ /* State used by the fts5DataXXX() functions. */
+ sqlite3_blob *pReader; /* RO incr-blob open on %_data table */
+ sqlite3_stmt *pWriter; /* "INSERT ... %_data VALUES(?,?)" */
+ sqlite3_stmt *pDeleter; /* "DELETE FROM %_data ... id>=? AND id<=?" */
+ sqlite3_stmt *pIdxWriter; /* "INSERT ... %_idx VALUES(?,?,?,?)" */
+ sqlite3_stmt *pIdxDeleter; /* "DELETE FROM %_idx WHERE segid=? */
+ sqlite3_stmt *pIdxSelect;
+ int nRead; /* Total number of blocks read */
+
+ sqlite3_stmt *pDataVersion;
+ i64 iStructVersion; /* data_version when pStruct read */
+ Fts5Structure *pStruct; /* Current db structure (or NULL) */
+};
+
+struct Fts5DoclistIter {
+ u8 *aEof; /* Pointer to 1 byte past end of doclist */
+
+ /* Output variables. aPoslist==0 at EOF */
+ i64 iRowid;
+ u8 *aPoslist;
+ int nPoslist;
+ int nSize;
+};
+
+/*
+** The contents of the "structure" record for each index are represented
+** using an Fts5Structure record in memory. Which uses instances of the
+** other Fts5StructureXXX types as components.
+*/
+struct Fts5StructureSegment {
+ int iSegid; /* Segment id */
+ int pgnoFirst; /* First leaf page number in segment */
+ int pgnoLast; /* Last leaf page number in segment */
+};
+struct Fts5StructureLevel {
+ int nMerge; /* Number of segments in incr-merge */
+ int nSeg; /* Total number of segments on level */
+ Fts5StructureSegment *aSeg; /* Array of segments. aSeg[0] is oldest. */
+};
+struct Fts5Structure {
+ int nRef; /* Object reference count */
+ u64 nWriteCounter; /* Total leaves written to level 0 */
+ int nSegment; /* Total segments in this structure */
+ int nLevel; /* Number of levels in this index */
+ Fts5StructureLevel aLevel[1]; /* Array of nLevel level objects */
+};
+
+/*
+** An object of type Fts5SegWriter is used to write to segments.
+*/
+struct Fts5PageWriter {
+ int pgno; /* Page number for this page */
+ int iPrevPgidx; /* Previous value written into pgidx */
+ Fts5Buffer buf; /* Buffer containing leaf data */
+ Fts5Buffer pgidx; /* Buffer containing page-index */
+ Fts5Buffer term; /* Buffer containing previous term on page */
+};
+struct Fts5DlidxWriter {
+ int pgno; /* Page number for this page */
+ int bPrevValid; /* True if iPrev is valid */
+ i64 iPrev; /* Previous rowid value written to page */
+ Fts5Buffer buf; /* Buffer containing page data */
+};
+struct Fts5SegWriter {
+ int iSegid; /* Segid to write to */
+ Fts5PageWriter writer; /* PageWriter object */
+ i64 iPrevRowid; /* Previous rowid written to current leaf */
+ u8 bFirstRowidInDoclist; /* True if next rowid is first in doclist */
+ u8 bFirstRowidInPage; /* True if next rowid is first in page */
+ /* TODO1: Can use (writer.pgidx.n==0) instead of bFirstTermInPage */
+ u8 bFirstTermInPage; /* True if next term will be first in leaf */
+ int nLeafWritten; /* Number of leaf pages written */
+ int nEmpty; /* Number of contiguous term-less nodes */
+
+ int nDlidx; /* Allocated size of aDlidx[] array */
+ Fts5DlidxWriter *aDlidx; /* Array of Fts5DlidxWriter objects */
+
+ /* Values to insert into the %_idx table */
+ Fts5Buffer btterm; /* Next term to insert into %_idx table */
+ int iBtPage; /* Page number corresponding to btterm */
+};
+
+typedef struct Fts5CResult Fts5CResult;
+struct Fts5CResult {
+ u16 iFirst; /* aSeg[] index of firstest iterator */
+ u8 bTermEq; /* True if the terms are equal */
+};
+
+/*
+** Object for iterating through a single segment, visiting each term/rowid
+** pair in the segment.
+**
+** pSeg:
+** The segment to iterate through.
+**
+** iLeafPgno:
+** Current leaf page number within segment.
+**
+** iLeafOffset:
+** Byte offset within the current leaf that is the first byte of the
+** position list data (one byte passed the position-list size field).
+** rowid field of the current entry. Usually this is the size field of the
+** position list data. The exception is if the rowid for the current entry
+** is the last thing on the leaf page.
+**
+** pLeaf:
+** Buffer containing current leaf page data. Set to NULL at EOF.
+**
+** iTermLeafPgno, iTermLeafOffset:
+** Leaf page number containing the last term read from the segment. And
+** the offset immediately following the term data.
+**
+** flags:
+** Mask of FTS5_SEGITER_XXX values. Interpreted as follows:
+**
+** FTS5_SEGITER_ONETERM:
+** If set, set the iterator to point to EOF after the current doclist
+** has been exhausted. Do not proceed to the next term in the segment.
+**
+** FTS5_SEGITER_REVERSE:
+** This flag is only ever set if FTS5_SEGITER_ONETERM is also set. If
+** it is set, iterate through rowid in descending order instead of the
+** default ascending order.
+**
+** iRowidOffset/nRowidOffset/aRowidOffset:
+** These are used if the FTS5_SEGITER_REVERSE flag is set.
+**
+** For each rowid on the page corresponding to the current term, the
+** corresponding aRowidOffset[] entry is set to the byte offset of the
+** start of the "position-list-size" field within the page.
+**
+** iTermIdx:
+** Index of current term on iTermLeafPgno.
+*/
+struct Fts5SegIter {
+ Fts5StructureSegment *pSeg; /* Segment to iterate through */
+ int flags; /* Mask of configuration flags */
+ int iLeafPgno; /* Current leaf page number */
+ Fts5Data *pLeaf; /* Current leaf data */
+ Fts5Data *pNextLeaf; /* Leaf page (iLeafPgno+1) */
+ int iLeafOffset; /* Byte offset within current leaf */
+
+ /* Next method */
+ void (*xNext)(Fts5Index*, Fts5SegIter*, int*);
+
+ /* The page and offset from which the current term was read. The offset
+ ** is the offset of the first rowid in the current doclist. */
+ int iTermLeafPgno;
+ int iTermLeafOffset;
+
+ int iPgidxOff; /* Next offset in pgidx */
+ int iEndofDoclist;
+
+ /* The following are only used if the FTS5_SEGITER_REVERSE flag is set. */
+ int iRowidOffset; /* Current entry in aRowidOffset[] */
+ int nRowidOffset; /* Allocated size of aRowidOffset[] array */
+ int *aRowidOffset; /* Array of offset to rowid fields */
+
+ Fts5DlidxIter *pDlidx; /* If there is a doclist-index */
+
+ /* Variables populated based on current entry. */
+ Fts5Buffer term; /* Current term */
+ i64 iRowid; /* Current rowid */
+ int nPos; /* Number of bytes in current position list */
+ u8 bDel; /* True if the delete flag is set */
+};
+
+/*
+** Argument is a pointer to an Fts5Data structure that contains a
+** leaf page.
+*/
+#define ASSERT_SZLEAF_OK(x) assert( \
+ (x)->szLeaf==(x)->nn || (x)->szLeaf==fts5GetU16(&(x)->p[2]) \
+)
+
+#define FTS5_SEGITER_ONETERM 0x01
+#define FTS5_SEGITER_REVERSE 0x02
+
+/*
+** Argument is a pointer to an Fts5Data structure that contains a leaf
+** page. This macro evaluates to true if the leaf contains no terms, or
+** false if it contains at least one term.
+*/
+#define fts5LeafIsTermless(x) ((x)->szLeaf >= (x)->nn)
+
+#define fts5LeafTermOff(x, i) (fts5GetU16(&(x)->p[(x)->szLeaf + (i)*2]))
+
+#define fts5LeafFirstRowidOff(x) (fts5GetU16((x)->p))
+
+/*
+** Object for iterating through the merged results of one or more segments,
+** visiting each term/rowid pair in the merged data.
+**
+** nSeg is always a power of two greater than or equal to the number of
+** segments that this object is merging data from. Both the aSeg[] and
+** aFirst[] arrays are sized at nSeg entries. The aSeg[] array is padded
+** with zeroed objects - these are handled as if they were iterators opened
+** on empty segments.
+**
+** The results of comparing segments aSeg[N] and aSeg[N+1], where N is an
+** even number, is stored in aFirst[(nSeg+N)/2]. The "result" of the
+** comparison in this context is the index of the iterator that currently
+** points to the smaller term/rowid combination. Iterators at EOF are
+** considered to be greater than all other iterators.
+**
+** aFirst[1] contains the index in aSeg[] of the iterator that points to
+** the smallest key overall. aFirst[0] is unused.
+**
+** poslist:
+** Used by sqlite3Fts5IterPoslist() when the poslist needs to be buffered.
+** There is no way to tell if this is populated or not.
+*/
+struct Fts5Iter {
+ Fts5IndexIter base; /* Base class containing output vars */
+
+ Fts5Index *pIndex; /* Index that owns this iterator */
+ Fts5Structure *pStruct; /* Database structure for this iterator */
+ Fts5Buffer poslist; /* Buffer containing current poslist */
+ Fts5Colset *pColset; /* Restrict matches to these columns */
+
+ /* Invoked to set output variables. */
+ void (*xSetOutputs)(Fts5Iter*, Fts5SegIter*);
+
+ int nSeg; /* Size of aSeg[] array */
+ int bRev; /* True to iterate in reverse order */
+ u8 bSkipEmpty; /* True to skip deleted entries */
+
+ i64 iSwitchRowid; /* Firstest rowid of other than aFirst[1] */
+ Fts5CResult *aFirst; /* Current merge state (see above) */
+ Fts5SegIter aSeg[1]; /* Array of segment iterators */
+};
+
+
+/*
+** An instance of the following type is used to iterate through the contents
+** of a doclist-index record.
+**
+** pData:
+** Record containing the doclist-index data.
+**
+** bEof:
+** Set to true once iterator has reached EOF.
+**
+** iOff:
+** Set to the current offset within record pData.
+*/
+struct Fts5DlidxLvl {
+ Fts5Data *pData; /* Data for current page of this level */
+ int iOff; /* Current offset into pData */
+ int bEof; /* At EOF already */
+ int iFirstOff; /* Used by reverse iterators */
+
+ /* Output variables */
+ int iLeafPgno; /* Page number of current leaf page */
+ i64 iRowid; /* First rowid on leaf iLeafPgno */
+};
+struct Fts5DlidxIter {
+ int nLvl;
+ int iSegid;
+ Fts5DlidxLvl aLvl[1];
+};
+
+static void fts5PutU16(u8 *aOut, u16 iVal){
+ aOut[0] = (iVal>>8);
+ aOut[1] = (iVal&0xFF);
+}
+
+static u16 fts5GetU16(const u8 *aIn){
+ return ((u16)aIn[0] << 8) + aIn[1];
+}
+
+/*
+** Allocate and return a buffer at least nByte bytes in size.
+**
+** If an OOM error is encountered, return NULL and set the error code in
+** the Fts5Index handle passed as the first argument.
+*/
+static void *fts5IdxMalloc(Fts5Index *p, int nByte){
+ return sqlite3Fts5MallocZero(&p->rc, nByte);
+}
+
+/*
+** Compare the contents of the pLeft buffer with the pRight/nRight blob.
+**
+** Return -ve if pLeft is smaller than pRight, 0 if they are equal or
+** +ve if pRight is smaller than pLeft. In other words:
+**
+** res = *pLeft - *pRight
+*/
+#ifdef SQLITE_DEBUG
+static int fts5BufferCompareBlob(
+ Fts5Buffer *pLeft, /* Left hand side of comparison */
+ const u8 *pRight, int nRight /* Right hand side of comparison */
+){
+ int nCmp = MIN(pLeft->n, nRight);
+ int res = memcmp(pLeft->p, pRight, nCmp);
+ return (res==0 ? (pLeft->n - nRight) : res);
+}
+#endif
+
+/*
+** Compare the contents of the two buffers using memcmp(). If one buffer
+** is a prefix of the other, it is considered the lesser.
+**
+** Return -ve if pLeft is smaller than pRight, 0 if they are equal or
+** +ve if pRight is smaller than pLeft. In other words:
+**
+** res = *pLeft - *pRight
+*/
+static int fts5BufferCompare(Fts5Buffer *pLeft, Fts5Buffer *pRight){
+ int nCmp = MIN(pLeft->n, pRight->n);
+ int res = memcmp(pLeft->p, pRight->p, nCmp);
+ return (res==0 ? (pLeft->n - pRight->n) : res);
+}
+
+static int fts5LeafFirstTermOff(Fts5Data *pLeaf){
+ int ret;
+ fts5GetVarint32(&pLeaf->p[pLeaf->szLeaf], ret);
+ return ret;
+}
+
+/*
+** Close the read-only blob handle, if it is open.
+*/
+static void fts5CloseReader(Fts5Index *p){
+ if( p->pReader ){
+ sqlite3_blob *pReader = p->pReader;
+ p->pReader = 0;
+ sqlite3_blob_close(pReader);
+ }
+}
+
+
+/*
+** Retrieve a record from the %_data table.
+**
+** If an error occurs, NULL is returned and an error left in the
+** Fts5Index object.
+*/
+static Fts5Data *fts5DataRead(Fts5Index *p, i64 iRowid){
+ Fts5Data *pRet = 0;
+ if( p->rc==SQLITE_OK ){
+ int rc = SQLITE_OK;
+
+ if( p->pReader ){
+ /* This call may return SQLITE_ABORT if there has been a savepoint
+ ** rollback since it was last used. In this case a new blob handle
+ ** is required. */
+ sqlite3_blob *pBlob = p->pReader;
+ p->pReader = 0;
+ rc = sqlite3_blob_reopen(pBlob, iRowid);
+ assert( p->pReader==0 );
+ p->pReader = pBlob;
+ if( rc!=SQLITE_OK ){
+ fts5CloseReader(p);
+ }
+ if( rc==SQLITE_ABORT ) rc = SQLITE_OK;
+ }
+
+ /* If the blob handle is not open at this point, open it and seek
+ ** to the requested entry. */
+ if( p->pReader==0 && rc==SQLITE_OK ){
+ Fts5Config *pConfig = p->pConfig;
+ rc = sqlite3_blob_open(pConfig->db,
+ pConfig->zDb, p->zDataTbl, "block", iRowid, 0, &p->pReader
+ );
+ }
+
+ /* If either of the sqlite3_blob_open() or sqlite3_blob_reopen() calls
+ ** above returned SQLITE_ERROR, return SQLITE_CORRUPT_VTAB instead.
+ ** All the reasons those functions might return SQLITE_ERROR - missing
+ ** table, missing row, non-blob/text in block column - indicate
+ ** backing store corruption. */
+ if( rc==SQLITE_ERROR ) rc = FTS5_CORRUPT;
+
+ if( rc==SQLITE_OK ){
+ u8 *aOut = 0; /* Read blob data into this buffer */
+ int nByte = sqlite3_blob_bytes(p->pReader);
+ int nAlloc = sizeof(Fts5Data) + nByte + FTS5_DATA_PADDING;
+ pRet = (Fts5Data*)sqlite3_malloc(nAlloc);
+ if( pRet ){
+ pRet->nn = nByte;
+ aOut = pRet->p = (u8*)&pRet[1];
+ }else{
+ rc = SQLITE_NOMEM;
+ }
+
+ if( rc==SQLITE_OK ){
+ rc = sqlite3_blob_read(p->pReader, aOut, nByte, 0);
+ }
+ if( rc!=SQLITE_OK ){
+ sqlite3_free(pRet);
+ pRet = 0;
+ }else{
+ /* TODO1: Fix this */
+ pRet->szLeaf = fts5GetU16(&pRet->p[2]);
+ }
+ }
+ p->rc = rc;
+ p->nRead++;
+ }
+
+ assert( (pRet==0)==(p->rc!=SQLITE_OK) );
+ return pRet;
+}
+
+
+/*
+** Release a reference to data record returned by an earlier call to
+** fts5DataRead().
+*/
+static void fts5DataRelease(Fts5Data *pData){
+ sqlite3_free(pData);
+}
+
+static int fts5IndexPrepareStmt(
+ Fts5Index *p,
+ sqlite3_stmt **ppStmt,
+ char *zSql
+){
+ if( p->rc==SQLITE_OK ){
+ if( zSql ){
+ p->rc = sqlite3_prepare_v2(p->pConfig->db, zSql, -1, ppStmt, 0);
+ }else{
+ p->rc = SQLITE_NOMEM;
+ }
+ }
+ sqlite3_free(zSql);
+ return p->rc;
+}
+
+
+/*
+** INSERT OR REPLACE a record into the %_data table.
+*/
+static void fts5DataWrite(Fts5Index *p, i64 iRowid, const u8 *pData, int nData){
+ if( p->rc!=SQLITE_OK ) return;
+
+ if( p->pWriter==0 ){
+ Fts5Config *pConfig = p->pConfig;
+ fts5IndexPrepareStmt(p, &p->pWriter, sqlite3_mprintf(
+ "REPLACE INTO '%q'.'%q_data'(id, block) VALUES(?,?)",
+ pConfig->zDb, pConfig->zName
+ ));
+ if( p->rc ) return;
+ }
+
+ sqlite3_bind_int64(p->pWriter, 1, iRowid);
+ sqlite3_bind_blob(p->pWriter, 2, pData, nData, SQLITE_STATIC);
+ sqlite3_step(p->pWriter);
+ p->rc = sqlite3_reset(p->pWriter);
+}
+
+/*
+** Execute the following SQL:
+**
+** DELETE FROM %_data WHERE id BETWEEN $iFirst AND $iLast
+*/
+static void fts5DataDelete(Fts5Index *p, i64 iFirst, i64 iLast){
+ if( p->rc!=SQLITE_OK ) return;
+
+ if( p->pDeleter==0 ){
+ int rc;
+ Fts5Config *pConfig = p->pConfig;
+ char *zSql = sqlite3_mprintf(
+ "DELETE FROM '%q'.'%q_data' WHERE id>=? AND id<=?",
+ pConfig->zDb, pConfig->zName
+ );
+ if( zSql==0 ){
+ rc = SQLITE_NOMEM;
+ }else{
+ rc = sqlite3_prepare_v2(pConfig->db, zSql, -1, &p->pDeleter, 0);
+ sqlite3_free(zSql);
+ }
+ if( rc!=SQLITE_OK ){
+ p->rc = rc;
+ return;
+ }
+ }
+
+ sqlite3_bind_int64(p->pDeleter, 1, iFirst);
+ sqlite3_bind_int64(p->pDeleter, 2, iLast);
+ sqlite3_step(p->pDeleter);
+ p->rc = sqlite3_reset(p->pDeleter);
+}
+
+/*
+** Remove all records associated with segment iSegid.
+*/
+static void fts5DataRemoveSegment(Fts5Index *p, int iSegid){
+ i64 iFirst = FTS5_SEGMENT_ROWID(iSegid, 0);
+ i64 iLast = FTS5_SEGMENT_ROWID(iSegid+1, 0)-1;
+ fts5DataDelete(p, iFirst, iLast);
+ if( p->pIdxDeleter==0 ){
+ Fts5Config *pConfig = p->pConfig;
+ fts5IndexPrepareStmt(p, &p->pIdxDeleter, sqlite3_mprintf(
+ "DELETE FROM '%q'.'%q_idx' WHERE segid=?",
+ pConfig->zDb, pConfig->zName
+ ));
+ }
+ if( p->rc==SQLITE_OK ){
+ sqlite3_bind_int(p->pIdxDeleter, 1, iSegid);
+ sqlite3_step(p->pIdxDeleter);
+ p->rc = sqlite3_reset(p->pIdxDeleter);
+ }
+}
+
+/*
+** Release a reference to an Fts5Structure object returned by an earlier
+** call to fts5StructureRead() or fts5StructureDecode().
+*/
+static void fts5StructureRelease(Fts5Structure *pStruct){
+ if( pStruct && 0>=(--pStruct->nRef) ){
+ int i;
+ assert( pStruct->nRef==0 );
+ for(i=0; i<pStruct->nLevel; i++){
+ sqlite3_free(pStruct->aLevel[i].aSeg);
+ }
+ sqlite3_free(pStruct);
+ }
+}
+
+static void fts5StructureRef(Fts5Structure *pStruct){
+ pStruct->nRef++;
+}
+
+/*
+** Deserialize and return the structure record currently stored in serialized
+** form within buffer pData/nData.
+**
+** The Fts5Structure.aLevel[] and each Fts5StructureLevel.aSeg[] array
+** are over-allocated by one slot. This allows the structure contents
+** to be more easily edited.
+**
+** If an error occurs, *ppOut is set to NULL and an SQLite error code
+** returned. Otherwise, *ppOut is set to point to the new object and
+** SQLITE_OK returned.
+*/
+static int fts5StructureDecode(
+ const u8 *pData, /* Buffer containing serialized structure */
+ int nData, /* Size of buffer pData in bytes */
+ int *piCookie, /* Configuration cookie value */
+ Fts5Structure **ppOut /* OUT: Deserialized object */
+){
+ int rc = SQLITE_OK;
+ int i = 0;
+ int iLvl;
+ int nLevel = 0;
+ int nSegment = 0;
+ int nByte; /* Bytes of space to allocate at pRet */
+ Fts5Structure *pRet = 0; /* Structure object to return */
+
+ /* Grab the cookie value */
+ if( piCookie ) *piCookie = sqlite3Fts5Get32(pData);
+ i = 4;
+
+ /* Read the total number of levels and segments from the start of the
+ ** structure record. */
+ i += fts5GetVarint32(&pData[i], nLevel);
+ i += fts5GetVarint32(&pData[i], nSegment);
+ nByte = (
+ sizeof(Fts5Structure) + /* Main structure */
+ sizeof(Fts5StructureLevel) * (nLevel-1) /* aLevel[] array */
+ );
+ pRet = (Fts5Structure*)sqlite3Fts5MallocZero(&rc, nByte);
+
+ if( pRet ){
+ pRet->nRef = 1;
+ pRet->nLevel = nLevel;
+ pRet->nSegment = nSegment;
+ i += sqlite3Fts5GetVarint(&pData[i], &pRet->nWriteCounter);
+
+ for(iLvl=0; rc==SQLITE_OK && iLvl<nLevel; iLvl++){
+ Fts5StructureLevel *pLvl = &pRet->aLevel[iLvl];
+ int nTotal = 0;
+ int iSeg;
+
+ if( i>=nData ){
+ rc = FTS5_CORRUPT;
+ }else{
+ i += fts5GetVarint32(&pData[i], pLvl->nMerge);
+ i += fts5GetVarint32(&pData[i], nTotal);
+ assert( nTotal>=pLvl->nMerge );
+ pLvl->aSeg = (Fts5StructureSegment*)sqlite3Fts5MallocZero(&rc,
+ nTotal * sizeof(Fts5StructureSegment)
+ );
+ }
+
+ if( rc==SQLITE_OK ){
+ pLvl->nSeg = nTotal;
+ for(iSeg=0; iSeg<nTotal; iSeg++){
+ if( i>=nData ){
+ rc = FTS5_CORRUPT;
+ break;
+ }
+ i += fts5GetVarint32(&pData[i], pLvl->aSeg[iSeg].iSegid);
+ i += fts5GetVarint32(&pData[i], pLvl->aSeg[iSeg].pgnoFirst);
+ i += fts5GetVarint32(&pData[i], pLvl->aSeg[iSeg].pgnoLast);
+ }
+ }
+ }
+ if( rc!=SQLITE_OK ){
+ fts5StructureRelease(pRet);
+ pRet = 0;
+ }
+ }
+
+ *ppOut = pRet;
+ return rc;
+}
+
+/*
+**
+*/
+static void fts5StructureAddLevel(int *pRc, Fts5Structure **ppStruct){
+ if( *pRc==SQLITE_OK ){
+ Fts5Structure *pStruct = *ppStruct;
+ int nLevel = pStruct->nLevel;
+ int nByte = (
+ sizeof(Fts5Structure) + /* Main structure */
+ sizeof(Fts5StructureLevel) * (nLevel+1) /* aLevel[] array */
+ );
+
+ pStruct = sqlite3_realloc(pStruct, nByte);
+ if( pStruct ){
+ memset(&pStruct->aLevel[nLevel], 0, sizeof(Fts5StructureLevel));
+ pStruct->nLevel++;
+ *ppStruct = pStruct;
+ }else{
+ *pRc = SQLITE_NOMEM;
+ }
+ }
+}
+
+/*
+** Extend level iLvl so that there is room for at least nExtra more
+** segments.
+*/
+static void fts5StructureExtendLevel(
+ int *pRc,
+ Fts5Structure *pStruct,
+ int iLvl,
+ int nExtra,
+ int bInsert
+){
+ if( *pRc==SQLITE_OK ){
+ Fts5StructureLevel *pLvl = &pStruct->aLevel[iLvl];
+ Fts5StructureSegment *aNew;
+ int nByte;
+
+ nByte = (pLvl->nSeg + nExtra) * sizeof(Fts5StructureSegment);
+ aNew = sqlite3_realloc(pLvl->aSeg, nByte);
+ if( aNew ){
+ if( bInsert==0 ){
+ memset(&aNew[pLvl->nSeg], 0, sizeof(Fts5StructureSegment) * nExtra);
+ }else{
+ int nMove = pLvl->nSeg * sizeof(Fts5StructureSegment);
+ memmove(&aNew[nExtra], aNew, nMove);
+ memset(aNew, 0, sizeof(Fts5StructureSegment) * nExtra);
+ }
+ pLvl->aSeg = aNew;
+ }else{
+ *pRc = SQLITE_NOMEM;
+ }
+ }
+}
+
+static Fts5Structure *fts5StructureReadUncached(Fts5Index *p){
+ Fts5Structure *pRet = 0;
+ Fts5Config *pConfig = p->pConfig;
+ int iCookie; /* Configuration cookie */
+ Fts5Data *pData;
+
+ pData = fts5DataRead(p, FTS5_STRUCTURE_ROWID);
+ if( p->rc==SQLITE_OK ){
+ /* TODO: Do we need this if the leaf-index is appended? Probably... */
+ memset(&pData->p[pData->nn], 0, FTS5_DATA_PADDING);
+ p->rc = fts5StructureDecode(pData->p, pData->nn, &iCookie, &pRet);
+ if( p->rc==SQLITE_OK && pConfig->iCookie!=iCookie ){
+ p->rc = sqlite3Fts5ConfigLoad(pConfig, iCookie);
+ }
+ fts5DataRelease(pData);
+ if( p->rc!=SQLITE_OK ){
+ fts5StructureRelease(pRet);
+ pRet = 0;
+ }
+ }
+
+ return pRet;
+}
+
+static i64 fts5IndexDataVersion(Fts5Index *p){
+ i64 iVersion = 0;
+
+ if( p->rc==SQLITE_OK ){
+ if( p->pDataVersion==0 ){
+ p->rc = fts5IndexPrepareStmt(p, &p->pDataVersion,
+ sqlite3_mprintf("PRAGMA %Q.data_version", p->pConfig->zDb)
+ );
+ if( p->rc ) return 0;
+ }
+
+ if( SQLITE_ROW==sqlite3_step(p->pDataVersion) ){
+ iVersion = sqlite3_column_int64(p->pDataVersion, 0);
+ }
+ p->rc = sqlite3_reset(p->pDataVersion);
+ }
+
+ return iVersion;
+}
+
+/*
+** Read, deserialize and return the structure record.
+**
+** The Fts5Structure.aLevel[] and each Fts5StructureLevel.aSeg[] array
+** are over-allocated as described for function fts5StructureDecode()
+** above.
+**
+** If an error occurs, NULL is returned and an error code left in the
+** Fts5Index handle. If an error has already occurred when this function
+** is called, it is a no-op.
+*/
+static Fts5Structure *fts5StructureRead(Fts5Index *p){
+
+ if( p->pStruct==0 ){
+ p->iStructVersion = fts5IndexDataVersion(p);
+ if( p->rc==SQLITE_OK ){
+ p->pStruct = fts5StructureReadUncached(p);
+ }
+ }
+
+#if 0
+ else{
+ Fts5Structure *pTest = fts5StructureReadUncached(p);
+ if( pTest ){
+ int i, j;
+ assert_nc( p->pStruct->nSegment==pTest->nSegment );
+ assert_nc( p->pStruct->nLevel==pTest->nLevel );
+ for(i=0; i<pTest->nLevel; i++){
+ assert_nc( p->pStruct->aLevel[i].nMerge==pTest->aLevel[i].nMerge );
+ assert_nc( p->pStruct->aLevel[i].nSeg==pTest->aLevel[i].nSeg );
+ for(j=0; j<pTest->aLevel[i].nSeg; j++){
+ Fts5StructureSegment *p1 = &pTest->aLevel[i].aSeg[j];
+ Fts5StructureSegment *p2 = &p->pStruct->aLevel[i].aSeg[j];
+ assert_nc( p1->iSegid==p2->iSegid );
+ assert_nc( p1->pgnoFirst==p2->pgnoFirst );
+ assert_nc( p1->pgnoLast==p2->pgnoLast );
+ }
+ }
+ fts5StructureRelease(pTest);
+ }
+ }
+#endif
+
+ if( p->rc!=SQLITE_OK ) return 0;
+ assert( p->iStructVersion!=0 );
+ assert( p->pStruct!=0 );
+ fts5StructureRef(p->pStruct);
+ return p->pStruct;
+}
+
+static void fts5StructureInvalidate(Fts5Index *p){
+ if( p->pStruct ){
+ fts5StructureRelease(p->pStruct);
+ p->pStruct = 0;
+ }
+}
+
+/*
+** Return the total number of segments in index structure pStruct. This
+** function is only ever used as part of assert() conditions.
+*/
+#ifdef SQLITE_DEBUG
+static int fts5StructureCountSegments(Fts5Structure *pStruct){
+ int nSegment = 0; /* Total number of segments */
+ if( pStruct ){
+ int iLvl; /* Used to iterate through levels */
+ for(iLvl=0; iLvl<pStruct->nLevel; iLvl++){
+ nSegment += pStruct->aLevel[iLvl].nSeg;
+ }
+ }
+
+ return nSegment;
+}
+#endif
+
+#define fts5BufferSafeAppendBlob(pBuf, pBlob, nBlob) { \
+ assert( (pBuf)->nSpace>=((pBuf)->n+nBlob) ); \
+ memcpy(&(pBuf)->p[(pBuf)->n], pBlob, nBlob); \
+ (pBuf)->n += nBlob; \
+}
+
+#define fts5BufferSafeAppendVarint(pBuf, iVal) { \
+ (pBuf)->n += sqlite3Fts5PutVarint(&(pBuf)->p[(pBuf)->n], (iVal)); \
+ assert( (pBuf)->nSpace>=(pBuf)->n ); \
+}
+
+
+/*
+** Serialize and store the "structure" record.
+**
+** If an error occurs, leave an error code in the Fts5Index object. If an
+** error has already occurred, this function is a no-op.
+*/
+static void fts5StructureWrite(Fts5Index *p, Fts5Structure *pStruct){
+ if( p->rc==SQLITE_OK ){
+ Fts5Buffer buf; /* Buffer to serialize record into */
+ int iLvl; /* Used to iterate through levels */
+ int iCookie; /* Cookie value to store */
+
+ assert( pStruct->nSegment==fts5StructureCountSegments(pStruct) );
+ memset(&buf, 0, sizeof(Fts5Buffer));
+
+ /* Append the current configuration cookie */
+ iCookie = p->pConfig->iCookie;
+ if( iCookie<0 ) iCookie = 0;
+
+ if( 0==sqlite3Fts5BufferSize(&p->rc, &buf, 4+9+9+9) ){
+ sqlite3Fts5Put32(buf.p, iCookie);
+ buf.n = 4;
+ fts5BufferSafeAppendVarint(&buf, pStruct->nLevel);
+ fts5BufferSafeAppendVarint(&buf, pStruct->nSegment);
+ fts5BufferSafeAppendVarint(&buf, (i64)pStruct->nWriteCounter);
+ }
+
+ for(iLvl=0; iLvl<pStruct->nLevel; iLvl++){
+ int iSeg; /* Used to iterate through segments */
+ Fts5StructureLevel *pLvl = &pStruct->aLevel[iLvl];
+ fts5BufferAppendVarint(&p->rc, &buf, pLvl->nMerge);
+ fts5BufferAppendVarint(&p->rc, &buf, pLvl->nSeg);
+ assert( pLvl->nMerge<=pLvl->nSeg );
+
+ for(iSeg=0; iSeg<pLvl->nSeg; iSeg++){
+ fts5BufferAppendVarint(&p->rc, &buf, pLvl->aSeg[iSeg].iSegid);
+ fts5BufferAppendVarint(&p->rc, &buf, pLvl->aSeg[iSeg].pgnoFirst);
+ fts5BufferAppendVarint(&p->rc, &buf, pLvl->aSeg[iSeg].pgnoLast);
+ }
+ }
+
+ fts5DataWrite(p, FTS5_STRUCTURE_ROWID, buf.p, buf.n);
+ fts5BufferFree(&buf);
+ }
+}
+
+#if 0
+static void fts5DebugStructure(int*,Fts5Buffer*,Fts5Structure*);
+static void fts5PrintStructure(const char *zCaption, Fts5Structure *pStruct){
+ int rc = SQLITE_OK;
+ Fts5Buffer buf;
+ memset(&buf, 0, sizeof(buf));
+ fts5DebugStructure(&rc, &buf, pStruct);
+ fprintf(stdout, "%s: %s\n", zCaption, buf.p);
+ fflush(stdout);
+ fts5BufferFree(&buf);
+}
+#else
+# define fts5PrintStructure(x,y)
+#endif
+
+static int fts5SegmentSize(Fts5StructureSegment *pSeg){
+ return 1 + pSeg->pgnoLast - pSeg->pgnoFirst;
+}
+
+/*
+** Return a copy of index structure pStruct. Except, promote as many
+** segments as possible to level iPromote. If an OOM occurs, NULL is
+** returned.
+*/
+static void fts5StructurePromoteTo(
+ Fts5Index *p,
+ int iPromote,
+ int szPromote,
+ Fts5Structure *pStruct
+){
+ int il, is;
+ Fts5StructureLevel *pOut = &pStruct->aLevel[iPromote];
+
+ if( pOut->nMerge==0 ){
+ for(il=iPromote+1; il<pStruct->nLevel; il++){
+ Fts5StructureLevel *pLvl = &pStruct->aLevel[il];
+ if( pLvl->nMerge ) return;
+ for(is=pLvl->nSeg-1; is>=0; is--){
+ int sz = fts5SegmentSize(&pLvl->aSeg[is]);
+ if( sz>szPromote ) return;
+ fts5StructureExtendLevel(&p->rc, pStruct, iPromote, 1, 1);
+ if( p->rc ) return;
+ memcpy(pOut->aSeg, &pLvl->aSeg[is], sizeof(Fts5StructureSegment));
+ pOut->nSeg++;
+ pLvl->nSeg--;
+ }
+ }
+ }
+}
+
+/*
+** A new segment has just been written to level iLvl of index structure
+** pStruct. This function determines if any segments should be promoted
+** as a result. Segments are promoted in two scenarios:
+**
+** a) If the segment just written is smaller than one or more segments
+** within the previous populated level, it is promoted to the previous
+** populated level.
+**
+** b) If the segment just written is larger than the newest segment on
+** the next populated level, then that segment, and any other adjacent
+** segments that are also smaller than the one just written, are
+** promoted.
+**
+** If one or more segments are promoted, the structure object is updated
+** to reflect this.
+*/
+static void fts5StructurePromote(
+ Fts5Index *p, /* FTS5 backend object */
+ int iLvl, /* Index level just updated */
+ Fts5Structure *pStruct /* Index structure */
+){
+ if( p->rc==SQLITE_OK ){
+ int iTst;
+ int iPromote = -1;
+ int szPromote = 0; /* Promote anything this size or smaller */
+ Fts5StructureSegment *pSeg; /* Segment just written */
+ int szSeg; /* Size of segment just written */
+ int nSeg = pStruct->aLevel[iLvl].nSeg;
+
+ if( nSeg==0 ) return;
+ pSeg = &pStruct->aLevel[iLvl].aSeg[pStruct->aLevel[iLvl].nSeg-1];
+ szSeg = (1 + pSeg->pgnoLast - pSeg->pgnoFirst);
+
+ /* Check for condition (a) */
+ for(iTst=iLvl-1; iTst>=0 && pStruct->aLevel[iTst].nSeg==0; iTst--);
+ if( iTst>=0 ){
+ int i;
+ int szMax = 0;
+ Fts5StructureLevel *pTst = &pStruct->aLevel[iTst];
+ assert( pTst->nMerge==0 );
+ for(i=0; i<pTst->nSeg; i++){
+ int sz = pTst->aSeg[i].pgnoLast - pTst->aSeg[i].pgnoFirst + 1;
+ if( sz>szMax ) szMax = sz;
+ }
+ if( szMax>=szSeg ){
+ /* Condition (a) is true. Promote the newest segment on level
+ ** iLvl to level iTst. */
+ iPromote = iTst;
+ szPromote = szMax;
+ }
+ }
+
+ /* If condition (a) is not met, assume (b) is true. StructurePromoteTo()
+ ** is a no-op if it is not. */
+ if( iPromote<0 ){
+ iPromote = iLvl;
+ szPromote = szSeg;
+ }
+ fts5StructurePromoteTo(p, iPromote, szPromote, pStruct);
+ }
+}
+
+
+/*
+** Advance the iterator passed as the only argument. If the end of the
+** doclist-index page is reached, return non-zero.
+*/
+static int fts5DlidxLvlNext(Fts5DlidxLvl *pLvl){
+ Fts5Data *pData = pLvl->pData;
+
+ if( pLvl->iOff==0 ){
+ assert( pLvl->bEof==0 );
+ pLvl->iOff = 1;
+ pLvl->iOff += fts5GetVarint32(&pData->p[1], pLvl->iLeafPgno);
+ pLvl->iOff += fts5GetVarint(&pData->p[pLvl->iOff], (u64*)&pLvl->iRowid);
+ pLvl->iFirstOff = pLvl->iOff;
+ }else{
+ int iOff;
+ for(iOff=pLvl->iOff; iOff<pData->nn; iOff++){
+ if( pData->p[iOff] ) break;
+ }
+
+ if( iOff<pData->nn ){
+ i64 iVal;
+ pLvl->iLeafPgno += (iOff - pLvl->iOff) + 1;
+ iOff += fts5GetVarint(&pData->p[iOff], (u64*)&iVal);
+ pLvl->iRowid += iVal;
+ pLvl->iOff = iOff;
+ }else{
+ pLvl->bEof = 1;
+ }
+ }
+
+ return pLvl->bEof;
+}
+
+/*
+** Advance the iterator passed as the only argument.
+*/
+static int fts5DlidxIterNextR(Fts5Index *p, Fts5DlidxIter *pIter, int iLvl){
+ Fts5DlidxLvl *pLvl = &pIter->aLvl[iLvl];
+
+ assert( iLvl<pIter->nLvl );
+ if( fts5DlidxLvlNext(pLvl) ){
+ if( (iLvl+1) < pIter->nLvl ){
+ fts5DlidxIterNextR(p, pIter, iLvl+1);
+ if( pLvl[1].bEof==0 ){
+ fts5DataRelease(pLvl->pData);
+ memset(pLvl, 0, sizeof(Fts5DlidxLvl));
+ pLvl->pData = fts5DataRead(p,
+ FTS5_DLIDX_ROWID(pIter->iSegid, iLvl, pLvl[1].iLeafPgno)
+ );
+ if( pLvl->pData ) fts5DlidxLvlNext(pLvl);
+ }
+ }
+ }
+
+ return pIter->aLvl[0].bEof;
+}
+static int fts5DlidxIterNext(Fts5Index *p, Fts5DlidxIter *pIter){
+ return fts5DlidxIterNextR(p, pIter, 0);
+}
+
+/*
+** The iterator passed as the first argument has the following fields set
+** as follows. This function sets up the rest of the iterator so that it
+** points to the first rowid in the doclist-index.
+**
+** pData:
+** pointer to doclist-index record,
+**
+** When this function is called pIter->iLeafPgno is the page number the
+** doclist is associated with (the one featuring the term).
+*/
+static int fts5DlidxIterFirst(Fts5DlidxIter *pIter){
+ int i;
+ for(i=0; i<pIter->nLvl; i++){
+ fts5DlidxLvlNext(&pIter->aLvl[i]);
+ }
+ return pIter->aLvl[0].bEof;
+}
+
+
+static int fts5DlidxIterEof(Fts5Index *p, Fts5DlidxIter *pIter){
+ return p->rc!=SQLITE_OK || pIter->aLvl[0].bEof;
+}
+
+static void fts5DlidxIterLast(Fts5Index *p, Fts5DlidxIter *pIter){
+ int i;
+
+ /* Advance each level to the last entry on the last page */
+ for(i=pIter->nLvl-1; p->rc==SQLITE_OK && i>=0; i--){
+ Fts5DlidxLvl *pLvl = &pIter->aLvl[i];
+ while( fts5DlidxLvlNext(pLvl)==0 );
+ pLvl->bEof = 0;
+
+ if( i>0 ){
+ Fts5DlidxLvl *pChild = &pLvl[-1];
+ fts5DataRelease(pChild->pData);
+ memset(pChild, 0, sizeof(Fts5DlidxLvl));
+ pChild->pData = fts5DataRead(p,
+ FTS5_DLIDX_ROWID(pIter->iSegid, i-1, pLvl->iLeafPgno)
+ );
+ }
+ }
+}
+
+/*
+** Move the iterator passed as the only argument to the previous entry.
+*/
+static int fts5DlidxLvlPrev(Fts5DlidxLvl *pLvl){
+ int iOff = pLvl->iOff;
+
+ assert( pLvl->bEof==0 );
+ if( iOff<=pLvl->iFirstOff ){
+ pLvl->bEof = 1;
+ }else{
+ u8 *a = pLvl->pData->p;
+ i64 iVal;
+ int iLimit;
+ int ii;
+ int nZero = 0;
+
+ /* Currently iOff points to the first byte of a varint. This block
+ ** decrements iOff until it points to the first byte of the previous
+ ** varint. Taking care not to read any memory locations that occur
+ ** before the buffer in memory. */
+ iLimit = (iOff>9 ? iOff-9 : 0);
+ for(iOff--; iOff>iLimit; iOff--){
+ if( (a[iOff-1] & 0x80)==0 ) break;
+ }
+
+ fts5GetVarint(&a[iOff], (u64*)&iVal);
+ pLvl->iRowid -= iVal;
+ pLvl->iLeafPgno--;
+
+ /* Skip backwards past any 0x00 varints. */
+ for(ii=iOff-1; ii>=pLvl->iFirstOff && a[ii]==0x00; ii--){
+ nZero++;
+ }
+ if( ii>=pLvl->iFirstOff && (a[ii] & 0x80) ){
+ /* The byte immediately before the last 0x00 byte has the 0x80 bit
+ ** set. So the last 0x00 is only a varint 0 if there are 8 more 0x80
+ ** bytes before a[ii]. */
+ int bZero = 0; /* True if last 0x00 counts */
+ if( (ii-8)>=pLvl->iFirstOff ){
+ int j;
+ for(j=1; j<=8 && (a[ii-j] & 0x80); j++);
+ bZero = (j>8);
+ }
+ if( bZero==0 ) nZero--;
+ }
+ pLvl->iLeafPgno -= nZero;
+ pLvl->iOff = iOff - nZero;
+ }
+
+ return pLvl->bEof;
+}
+
+static int fts5DlidxIterPrevR(Fts5Index *p, Fts5DlidxIter *pIter, int iLvl){
+ Fts5DlidxLvl *pLvl = &pIter->aLvl[iLvl];
+
+ assert( iLvl<pIter->nLvl );
+ if( fts5DlidxLvlPrev(pLvl) ){
+ if( (iLvl+1) < pIter->nLvl ){
+ fts5DlidxIterPrevR(p, pIter, iLvl+1);
+ if( pLvl[1].bEof==0 ){
+ fts5DataRelease(pLvl->pData);
+ memset(pLvl, 0, sizeof(Fts5DlidxLvl));
+ pLvl->pData = fts5DataRead(p,
+ FTS5_DLIDX_ROWID(pIter->iSegid, iLvl, pLvl[1].iLeafPgno)
+ );
+ if( pLvl->pData ){
+ while( fts5DlidxLvlNext(pLvl)==0 );
+ pLvl->bEof = 0;
+ }
+ }
+ }
+ }
+
+ return pIter->aLvl[0].bEof;
+}
+static int fts5DlidxIterPrev(Fts5Index *p, Fts5DlidxIter *pIter){
+ return fts5DlidxIterPrevR(p, pIter, 0);
+}
+
+/*
+** Free a doclist-index iterator object allocated by fts5DlidxIterInit().
+*/
+static void fts5DlidxIterFree(Fts5DlidxIter *pIter){
+ if( pIter ){
+ int i;
+ for(i=0; i<pIter->nLvl; i++){
+ fts5DataRelease(pIter->aLvl[i].pData);
+ }
+ sqlite3_free(pIter);
+ }
+}
+
+static Fts5DlidxIter *fts5DlidxIterInit(
+ Fts5Index *p, /* Fts5 Backend to iterate within */
+ int bRev, /* True for ORDER BY ASC */
+ int iSegid, /* Segment id */
+ int iLeafPg /* Leaf page number to load dlidx for */
+){
+ Fts5DlidxIter *pIter = 0;
+ int i;
+ int bDone = 0;
+
+ for(i=0; p->rc==SQLITE_OK && bDone==0; i++){
+ int nByte = sizeof(Fts5DlidxIter) + i * sizeof(Fts5DlidxLvl);
+ Fts5DlidxIter *pNew;
+
+ pNew = (Fts5DlidxIter*)sqlite3_realloc(pIter, nByte);
+ if( pNew==0 ){
+ p->rc = SQLITE_NOMEM;
+ }else{
+ i64 iRowid = FTS5_DLIDX_ROWID(iSegid, i, iLeafPg);
+ Fts5DlidxLvl *pLvl = &pNew->aLvl[i];
+ pIter = pNew;
+ memset(pLvl, 0, sizeof(Fts5DlidxLvl));
+ pLvl->pData = fts5DataRead(p, iRowid);
+ if( pLvl->pData && (pLvl->pData->p[0] & 0x0001)==0 ){
+ bDone = 1;
+ }
+ pIter->nLvl = i+1;
+ }
+ }
+
+ if( p->rc==SQLITE_OK ){
+ pIter->iSegid = iSegid;
+ if( bRev==0 ){
+ fts5DlidxIterFirst(pIter);
+ }else{
+ fts5DlidxIterLast(p, pIter);
+ }
+ }
+
+ if( p->rc!=SQLITE_OK ){
+ fts5DlidxIterFree(pIter);
+ pIter = 0;
+ }
+
+ return pIter;
+}
+
+static i64 fts5DlidxIterRowid(Fts5DlidxIter *pIter){
+ return pIter->aLvl[0].iRowid;
+}
+static int fts5DlidxIterPgno(Fts5DlidxIter *pIter){
+ return pIter->aLvl[0].iLeafPgno;
+}
+
+/*
+** Load the next leaf page into the segment iterator.
+*/
+static void fts5SegIterNextPage(
+ Fts5Index *p, /* FTS5 backend object */
+ Fts5SegIter *pIter /* Iterator to advance to next page */
+){
+ Fts5Data *pLeaf;
+ Fts5StructureSegment *pSeg = pIter->pSeg;
+ fts5DataRelease(pIter->pLeaf);
+ pIter->iLeafPgno++;
+ if( pIter->pNextLeaf ){
+ pIter->pLeaf = pIter->pNextLeaf;
+ pIter->pNextLeaf = 0;
+ }else if( pIter->iLeafPgno<=pSeg->pgnoLast ){
+ pIter->pLeaf = fts5DataRead(p,
+ FTS5_SEGMENT_ROWID(pSeg->iSegid, pIter->iLeafPgno)
+ );
+ }else{
+ pIter->pLeaf = 0;
+ }
+ pLeaf = pIter->pLeaf;
+
+ if( pLeaf ){
+ pIter->iPgidxOff = pLeaf->szLeaf;
+ if( fts5LeafIsTermless(pLeaf) ){
+ pIter->iEndofDoclist = pLeaf->nn+1;
+ }else{
+ pIter->iPgidxOff += fts5GetVarint32(&pLeaf->p[pIter->iPgidxOff],
+ pIter->iEndofDoclist
+ );
+ }
+ }
+}
+
+/*
+** Argument p points to a buffer containing a varint to be interpreted as a
+** position list size field. Read the varint and return the number of bytes
+** read. Before returning, set *pnSz to the number of bytes in the position
+** list, and *pbDel to true if the delete flag is set, or false otherwise.
+*/
+static int fts5GetPoslistSize(const u8 *p, int *pnSz, int *pbDel){
+ int nSz;
+ int n = 0;
+ fts5FastGetVarint32(p, n, nSz);
+ assert_nc( nSz>=0 );
+ *pnSz = nSz/2;
+ *pbDel = nSz & 0x0001;
+ return n;
+}
+
+/*
+** Fts5SegIter.iLeafOffset currently points to the first byte of a
+** position-list size field. Read the value of the field and store it
+** in the following variables:
+**
+** Fts5SegIter.nPos
+** Fts5SegIter.bDel
+**
+** Leave Fts5SegIter.iLeafOffset pointing to the first byte of the
+** position list content (if any).
+*/
+static void fts5SegIterLoadNPos(Fts5Index *p, Fts5SegIter *pIter){
+ if( p->rc==SQLITE_OK ){
+ int iOff = pIter->iLeafOffset; /* Offset to read at */
+ ASSERT_SZLEAF_OK(pIter->pLeaf);
+ if( p->pConfig->eDetail==FTS5_DETAIL_NONE ){
+ int iEod = MIN(pIter->iEndofDoclist, pIter->pLeaf->szLeaf);
+ pIter->bDel = 0;
+ pIter->nPos = 1;
+ if( iOff<iEod && pIter->pLeaf->p[iOff]==0 ){
+ pIter->bDel = 1;
+ iOff++;
+ if( iOff<iEod && pIter->pLeaf->p[iOff]==0 ){
+ pIter->nPos = 1;
+ iOff++;
+ }else{
+ pIter->nPos = 0;
+ }
+ }
+ }else{
+ int nSz;
+ fts5FastGetVarint32(pIter->pLeaf->p, iOff, nSz);
+ pIter->bDel = (nSz & 0x0001);
+ pIter->nPos = nSz>>1;
+ assert_nc( pIter->nPos>=0 );
+ }
+ pIter->iLeafOffset = iOff;
+ }
+}
+
+static void fts5SegIterLoadRowid(Fts5Index *p, Fts5SegIter *pIter){
+ u8 *a = pIter->pLeaf->p; /* Buffer to read data from */
+ int iOff = pIter->iLeafOffset;
+
+ ASSERT_SZLEAF_OK(pIter->pLeaf);
+ if( iOff>=pIter->pLeaf->szLeaf ){
+ fts5SegIterNextPage(p, pIter);
+ if( pIter->pLeaf==0 ){
+ if( p->rc==SQLITE_OK ) p->rc = FTS5_CORRUPT;
+ return;
+ }
+ iOff = 4;
+ a = pIter->pLeaf->p;
+ }
+ iOff += sqlite3Fts5GetVarint(&a[iOff], (u64*)&pIter->iRowid);
+ pIter->iLeafOffset = iOff;
+}
+
+/*
+** Fts5SegIter.iLeafOffset currently points to the first byte of the
+** "nSuffix" field of a term. Function parameter nKeep contains the value
+** of the "nPrefix" field (if there was one - it is passed 0 if this is
+** the first term in the segment).
+**
+** This function populates:
+**
+** Fts5SegIter.term
+** Fts5SegIter.rowid
+**
+** accordingly and leaves (Fts5SegIter.iLeafOffset) set to the content of
+** the first position list. The position list belonging to document
+** (Fts5SegIter.iRowid).
+*/
+static void fts5SegIterLoadTerm(Fts5Index *p, Fts5SegIter *pIter, int nKeep){
+ u8 *a = pIter->pLeaf->p; /* Buffer to read data from */
+ int iOff = pIter->iLeafOffset; /* Offset to read at */
+ int nNew; /* Bytes of new data */
+
+ iOff += fts5GetVarint32(&a[iOff], nNew);
+ if( iOff+nNew>pIter->pLeaf->nn ){
+ p->rc = FTS5_CORRUPT;
+ return;
+ }
+ pIter->term.n = nKeep;
+ fts5BufferAppendBlob(&p->rc, &pIter->term, nNew, &a[iOff]);
+ iOff += nNew;
+ pIter->iTermLeafOffset = iOff;
+ pIter->iTermLeafPgno = pIter->iLeafPgno;
+ pIter->iLeafOffset = iOff;
+
+ if( pIter->iPgidxOff>=pIter->pLeaf->nn ){
+ pIter->iEndofDoclist = pIter->pLeaf->nn+1;
+ }else{
+ int nExtra;
+ pIter->iPgidxOff += fts5GetVarint32(&a[pIter->iPgidxOff], nExtra);
+ pIter->iEndofDoclist += nExtra;
+ }
+
+ fts5SegIterLoadRowid(p, pIter);
+}
+
+static void fts5SegIterNext(Fts5Index*, Fts5SegIter*, int*);
+static void fts5SegIterNext_Reverse(Fts5Index*, Fts5SegIter*, int*);
+static void fts5SegIterNext_None(Fts5Index*, Fts5SegIter*, int*);
+
+static void fts5SegIterSetNext(Fts5Index *p, Fts5SegIter *pIter){
+ if( pIter->flags & FTS5_SEGITER_REVERSE ){
+ pIter->xNext = fts5SegIterNext_Reverse;
+ }else if( p->pConfig->eDetail==FTS5_DETAIL_NONE ){
+ pIter->xNext = fts5SegIterNext_None;
+ }else{
+ pIter->xNext = fts5SegIterNext;
+ }
+}
+
+/*
+** Initialize the iterator object pIter to iterate through the entries in
+** segment pSeg. The iterator is left pointing to the first entry when
+** this function returns.
+**
+** If an error occurs, Fts5Index.rc is set to an appropriate error code. If
+** an error has already occurred when this function is called, it is a no-op.
+*/
+static void fts5SegIterInit(
+ Fts5Index *p, /* FTS index object */
+ Fts5StructureSegment *pSeg, /* Description of segment */
+ Fts5SegIter *pIter /* Object to populate */
+){
+ if( pSeg->pgnoFirst==0 ){
+ /* This happens if the segment is being used as an input to an incremental
+ ** merge and all data has already been "trimmed". See function
+ ** fts5TrimSegments() for details. In this case leave the iterator empty.
+ ** The caller will see the (pIter->pLeaf==0) and assume the iterator is
+ ** at EOF already. */
+ assert( pIter->pLeaf==0 );
+ return;
+ }
+
+ if( p->rc==SQLITE_OK ){
+ memset(pIter, 0, sizeof(*pIter));
+ fts5SegIterSetNext(p, pIter);
+ pIter->pSeg = pSeg;
+ pIter->iLeafPgno = pSeg->pgnoFirst-1;
+ fts5SegIterNextPage(p, pIter);
+ }
+
+ if( p->rc==SQLITE_OK ){
+ pIter->iLeafOffset = 4;
+ assert_nc( pIter->pLeaf->nn>4 );
+ assert( fts5LeafFirstTermOff(pIter->pLeaf)==4 );
+ pIter->iPgidxOff = pIter->pLeaf->szLeaf+1;
+ fts5SegIterLoadTerm(p, pIter, 0);
+ fts5SegIterLoadNPos(p, pIter);
+ }
+}
+
+/*
+** This function is only ever called on iterators created by calls to
+** Fts5IndexQuery() with the FTS5INDEX_QUERY_DESC flag set.
+**
+** The iterator is in an unusual state when this function is called: the
+** Fts5SegIter.iLeafOffset variable is set to the offset of the start of
+** the position-list size field for the first relevant rowid on the page.
+** Fts5SegIter.rowid is set, but nPos and bDel are not.
+**
+** This function advances the iterator so that it points to the last
+** relevant rowid on the page and, if necessary, initializes the
+** aRowidOffset[] and iRowidOffset variables. At this point the iterator
+** is in its regular state - Fts5SegIter.iLeafOffset points to the first
+** byte of the position list content associated with said rowid.
+*/
+static void fts5SegIterReverseInitPage(Fts5Index *p, Fts5SegIter *pIter){
+ int eDetail = p->pConfig->eDetail;
+ int n = pIter->pLeaf->szLeaf;
+ int i = pIter->iLeafOffset;
+ u8 *a = pIter->pLeaf->p;
+ int iRowidOffset = 0;
+
+ if( n>pIter->iEndofDoclist ){
+ n = pIter->iEndofDoclist;
+ }
+
+ ASSERT_SZLEAF_OK(pIter->pLeaf);
+ while( 1 ){
+ i64 iDelta = 0;
+
+ if( eDetail==FTS5_DETAIL_NONE ){
+ /* todo */
+ if( i<n && a[i]==0 ){
+ i++;
+ if( i<n && a[i]==0 ) i++;
+ }
+ }else{
+ int nPos;
+ int bDummy;
+ i += fts5GetPoslistSize(&a[i], &nPos, &bDummy);
+ i += nPos;
+ }
+ if( i>=n ) break;
+ i += fts5GetVarint(&a[i], (u64*)&iDelta);
+ pIter->iRowid += iDelta;
+
+ /* If necessary, grow the pIter->aRowidOffset[] array. */
+ if( iRowidOffset>=pIter->nRowidOffset ){
+ int nNew = pIter->nRowidOffset + 8;
+ int *aNew = (int*)sqlite3_realloc(pIter->aRowidOffset, nNew*sizeof(int));
+ if( aNew==0 ){
+ p->rc = SQLITE_NOMEM;
+ break;
+ }
+ pIter->aRowidOffset = aNew;
+ pIter->nRowidOffset = nNew;
+ }
+
+ pIter->aRowidOffset[iRowidOffset++] = pIter->iLeafOffset;
+ pIter->iLeafOffset = i;
+ }
+ pIter->iRowidOffset = iRowidOffset;
+ fts5SegIterLoadNPos(p, pIter);
+}
+
+/*
+**
+*/
+static void fts5SegIterReverseNewPage(Fts5Index *p, Fts5SegIter *pIter){
+ assert( pIter->flags & FTS5_SEGITER_REVERSE );
+ assert( pIter->flags & FTS5_SEGITER_ONETERM );
+
+ fts5DataRelease(pIter->pLeaf);
+ pIter->pLeaf = 0;
+ while( p->rc==SQLITE_OK && pIter->iLeafPgno>pIter->iTermLeafPgno ){
+ Fts5Data *pNew;
+ pIter->iLeafPgno--;
+ pNew = fts5DataRead(p, FTS5_SEGMENT_ROWID(
+ pIter->pSeg->iSegid, pIter->iLeafPgno
+ ));
+ if( pNew ){
+ /* iTermLeafOffset may be equal to szLeaf if the term is the last
+ ** thing on the page - i.e. the first rowid is on the following page.
+ ** In this case leave pIter->pLeaf==0, this iterator is at EOF. */
+ if( pIter->iLeafPgno==pIter->iTermLeafPgno ){
+ assert( pIter->pLeaf==0 );
+ if( pIter->iTermLeafOffset<pNew->szLeaf ){
+ pIter->pLeaf = pNew;
+ pIter->iLeafOffset = pIter->iTermLeafOffset;
+ }
+ }else{
+ int iRowidOff;
+ iRowidOff = fts5LeafFirstRowidOff(pNew);
+ if( iRowidOff ){
+ pIter->pLeaf = pNew;
+ pIter->iLeafOffset = iRowidOff;
+ }
+ }
+
+ if( pIter->pLeaf ){
+ u8 *a = &pIter->pLeaf->p[pIter->iLeafOffset];
+ pIter->iLeafOffset += fts5GetVarint(a, (u64*)&pIter->iRowid);
+ break;
+ }else{
+ fts5DataRelease(pNew);
+ }
+ }
+ }
+
+ if( pIter->pLeaf ){
+ pIter->iEndofDoclist = pIter->pLeaf->nn+1;
+ fts5SegIterReverseInitPage(p, pIter);
+ }
+}
+
+/*
+** Return true if the iterator passed as the second argument currently
+** points to a delete marker. A delete marker is an entry with a 0 byte
+** position-list.
+*/
+static int fts5MultiIterIsEmpty(Fts5Index *p, Fts5Iter *pIter){
+ Fts5SegIter *pSeg = &pIter->aSeg[pIter->aFirst[1].iFirst];
+ return (p->rc==SQLITE_OK && pSeg->pLeaf && pSeg->nPos==0);
+}
+
+/*
+** Advance iterator pIter to the next entry.
+**
+** This version of fts5SegIterNext() is only used by reverse iterators.
+*/
+static void fts5SegIterNext_Reverse(
+ Fts5Index *p, /* FTS5 backend object */
+ Fts5SegIter *pIter, /* Iterator to advance */
+ int *pbUnused /* Unused */
+){
+ assert( pIter->flags & FTS5_SEGITER_REVERSE );
+ assert( pIter->pNextLeaf==0 );
+ UNUSED_PARAM(pbUnused);
+
+ if( pIter->iRowidOffset>0 ){
+ u8 *a = pIter->pLeaf->p;
+ int iOff;
+ i64 iDelta;
+
+ pIter->iRowidOffset--;
+ pIter->iLeafOffset = pIter->aRowidOffset[pIter->iRowidOffset];
+ fts5SegIterLoadNPos(p, pIter);
+ iOff = pIter->iLeafOffset;
+ if( p->pConfig->eDetail!=FTS5_DETAIL_NONE ){
+ iOff += pIter->nPos;
+ }
+ fts5GetVarint(&a[iOff], (u64*)&iDelta);
+ pIter->iRowid -= iDelta;
+ }else{
+ fts5SegIterReverseNewPage(p, pIter);
+ }
+}
+
+/*
+** Advance iterator pIter to the next entry.
+**
+** This version of fts5SegIterNext() is only used if detail=none and the
+** iterator is not a reverse direction iterator.
+*/
+static void fts5SegIterNext_None(
+ Fts5Index *p, /* FTS5 backend object */
+ Fts5SegIter *pIter, /* Iterator to advance */
+ int *pbNewTerm /* OUT: Set for new term */
+){
+ int iOff;
+
+ assert( p->rc==SQLITE_OK );
+ assert( (pIter->flags & FTS5_SEGITER_REVERSE)==0 );
+ assert( p->pConfig->eDetail==FTS5_DETAIL_NONE );
+
+ ASSERT_SZLEAF_OK(pIter->pLeaf);
+ iOff = pIter->iLeafOffset;
+
+ /* Next entry is on the next page */
+ if( pIter->pSeg && iOff>=pIter->pLeaf->szLeaf ){
+ fts5SegIterNextPage(p, pIter);
+ if( p->rc || pIter->pLeaf==0 ) return;
+ pIter->iRowid = 0;
+ iOff = 4;
+ }
+
+ if( iOff<pIter->iEndofDoclist ){
+ /* Next entry is on the current page */
+ i64 iDelta;
+ iOff += sqlite3Fts5GetVarint(&pIter->pLeaf->p[iOff], (u64*)&iDelta);
+ pIter->iLeafOffset = iOff;
+ pIter->iRowid += iDelta;
+ }else if( (pIter->flags & FTS5_SEGITER_ONETERM)==0 ){
+ if( pIter->pSeg ){
+ int nKeep = 0;
+ if( iOff!=fts5LeafFirstTermOff(pIter->pLeaf) ){
+ iOff += fts5GetVarint32(&pIter->pLeaf->p[iOff], nKeep);
+ }
+ pIter->iLeafOffset = iOff;
+ fts5SegIterLoadTerm(p, pIter, nKeep);
+ }else{
+ const u8 *pList = 0;
+ const char *zTerm = 0;
+ int nList;
+ sqlite3Fts5HashScanNext(p->pHash);
+ sqlite3Fts5HashScanEntry(p->pHash, &zTerm, &pList, &nList);
+ if( pList==0 ) goto next_none_eof;
+ pIter->pLeaf->p = (u8*)pList;
+ pIter->pLeaf->nn = nList;
+ pIter->pLeaf->szLeaf = nList;
+ pIter->iEndofDoclist = nList;
+ sqlite3Fts5BufferSet(&p->rc,&pIter->term, (int)strlen(zTerm), (u8*)zTerm);
+ pIter->iLeafOffset = fts5GetVarint(pList, (u64*)&pIter->iRowid);
+ }
+
+ if( pbNewTerm ) *pbNewTerm = 1;
+ }else{
+ goto next_none_eof;
+ }
+
+ fts5SegIterLoadNPos(p, pIter);
+
+ return;
+ next_none_eof:
+ fts5DataRelease(pIter->pLeaf);
+ pIter->pLeaf = 0;
+}
+
+
+/*
+** Advance iterator pIter to the next entry.
+**
+** If an error occurs, Fts5Index.rc is set to an appropriate error code. It
+** is not considered an error if the iterator reaches EOF. If an error has
+** already occurred when this function is called, it is a no-op.
+*/
+static void fts5SegIterNext(
+ Fts5Index *p, /* FTS5 backend object */
+ Fts5SegIter *pIter, /* Iterator to advance */
+ int *pbNewTerm /* OUT: Set for new term */
+){
+ Fts5Data *pLeaf = pIter->pLeaf;
+ int iOff;
+ int bNewTerm = 0;
+ int nKeep = 0;
+ u8 *a;
+ int n;
+
+ assert( pbNewTerm==0 || *pbNewTerm==0 );
+ assert( p->pConfig->eDetail!=FTS5_DETAIL_NONE );
+
+ /* Search for the end of the position list within the current page. */
+ a = pLeaf->p;
+ n = pLeaf->szLeaf;
+
+ ASSERT_SZLEAF_OK(pLeaf);
+ iOff = pIter->iLeafOffset + pIter->nPos;
+
+ if( iOff<n ){
+ /* The next entry is on the current page. */
+ assert_nc( iOff<=pIter->iEndofDoclist );
+ if( iOff>=pIter->iEndofDoclist ){
+ bNewTerm = 1;
+ if( iOff!=fts5LeafFirstTermOff(pLeaf) ){
+ iOff += fts5GetVarint32(&a[iOff], nKeep);
+ }
+ }else{
+ u64 iDelta;
+ iOff += sqlite3Fts5GetVarint(&a[iOff], &iDelta);
+ pIter->iRowid += iDelta;
+ assert_nc( iDelta>0 );
+ }
+ pIter->iLeafOffset = iOff;
+
+ }else if( pIter->pSeg==0 ){
+ const u8 *pList = 0;
+ const char *zTerm = 0;
+ int nList = 0;
+ assert( (pIter->flags & FTS5_SEGITER_ONETERM) || pbNewTerm );
+ if( 0==(pIter->flags & FTS5_SEGITER_ONETERM) ){
+ sqlite3Fts5HashScanNext(p->pHash);
+ sqlite3Fts5HashScanEntry(p->pHash, &zTerm, &pList, &nList);
+ }
+ if( pList==0 ){
+ fts5DataRelease(pIter->pLeaf);
+ pIter->pLeaf = 0;
+ }else{
+ pIter->pLeaf->p = (u8*)pList;
+ pIter->pLeaf->nn = nList;
+ pIter->pLeaf->szLeaf = nList;
+ pIter->iEndofDoclist = nList+1;
+ sqlite3Fts5BufferSet(&p->rc, &pIter->term, (int)strlen(zTerm),
+ (u8*)zTerm);
+ pIter->iLeafOffset = fts5GetVarint(pList, (u64*)&pIter->iRowid);
+ *pbNewTerm = 1;
+ }
+ }else{
+ iOff = 0;
+ /* Next entry is not on the current page */
+ while( iOff==0 ){
+ fts5SegIterNextPage(p, pIter);
+ pLeaf = pIter->pLeaf;
+ if( pLeaf==0 ) break;
+ ASSERT_SZLEAF_OK(pLeaf);
+ if( (iOff = fts5LeafFirstRowidOff(pLeaf)) && iOff<pLeaf->szLeaf ){
+ iOff += sqlite3Fts5GetVarint(&pLeaf->p[iOff], (u64*)&pIter->iRowid);
+ pIter->iLeafOffset = iOff;
+
+ if( pLeaf->nn>pLeaf->szLeaf ){
+ pIter->iPgidxOff = pLeaf->szLeaf + fts5GetVarint32(
+ &pLeaf->p[pLeaf->szLeaf], pIter->iEndofDoclist
+ );
+ }
+
+ }
+ else if( pLeaf->nn>pLeaf->szLeaf ){
+ pIter->iPgidxOff = pLeaf->szLeaf + fts5GetVarint32(
+ &pLeaf->p[pLeaf->szLeaf], iOff
+ );
+ pIter->iLeafOffset = iOff;
+ pIter->iEndofDoclist = iOff;
+ bNewTerm = 1;
+ }
+ assert_nc( iOff<pLeaf->szLeaf );
+ if( iOff>pLeaf->szLeaf ){
+ p->rc = FTS5_CORRUPT;
+ return;
+ }
+ }
+ }
+
+ /* Check if the iterator is now at EOF. If so, return early. */
+ if( pIter->pLeaf ){
+ if( bNewTerm ){
+ if( pIter->flags & FTS5_SEGITER_ONETERM ){
+ fts5DataRelease(pIter->pLeaf);
+ pIter->pLeaf = 0;
+ }else{
+ fts5SegIterLoadTerm(p, pIter, nKeep);
+ fts5SegIterLoadNPos(p, pIter);
+ if( pbNewTerm ) *pbNewTerm = 1;
+ }
+ }else{
+ /* The following could be done by calling fts5SegIterLoadNPos(). But
+ ** this block is particularly performance critical, so equivalent
+ ** code is inlined.
+ **
+ ** Later: Switched back to fts5SegIterLoadNPos() because it supports
+ ** detail=none mode. Not ideal.
+ */
+ int nSz;
+ assert( p->rc==SQLITE_OK );
+ fts5FastGetVarint32(pIter->pLeaf->p, pIter->iLeafOffset, nSz);
+ pIter->bDel = (nSz & 0x0001);
+ pIter->nPos = nSz>>1;
+ assert_nc( pIter->nPos>=0 );
+ }
+ }
+}
+
+#define SWAPVAL(T, a, b) { T tmp; tmp=a; a=b; b=tmp; }
+
+#define fts5IndexSkipVarint(a, iOff) { \
+ int iEnd = iOff+9; \
+ while( (a[iOff++] & 0x80) && iOff<iEnd ); \
+}
+
+/*
+** Iterator pIter currently points to the first rowid in a doclist. This
+** function sets the iterator up so that iterates in reverse order through
+** the doclist.
+*/
+static void fts5SegIterReverse(Fts5Index *p, Fts5SegIter *pIter){
+ Fts5DlidxIter *pDlidx = pIter->pDlidx;
+ Fts5Data *pLast = 0;
+ int pgnoLast = 0;
+
+ if( pDlidx ){
+ int iSegid = pIter->pSeg->iSegid;
+ pgnoLast = fts5DlidxIterPgno(pDlidx);
+ pLast = fts5DataRead(p, FTS5_SEGMENT_ROWID(iSegid, pgnoLast));
+ }else{
+ Fts5Data *pLeaf = pIter->pLeaf; /* Current leaf data */
+
+ /* Currently, Fts5SegIter.iLeafOffset points to the first byte of
+ ** position-list content for the current rowid. Back it up so that it
+ ** points to the start of the position-list size field. */
+ int iPoslist;
+ if( pIter->iTermLeafPgno==pIter->iLeafPgno ){
+ iPoslist = pIter->iTermLeafOffset;
+ }else{
+ iPoslist = 4;
+ }
+ fts5IndexSkipVarint(pLeaf->p, iPoslist);
+ pIter->iLeafOffset = iPoslist;
+
+ /* If this condition is true then the largest rowid for the current
+ ** term may not be stored on the current page. So search forward to
+ ** see where said rowid really is. */
+ if( pIter->iEndofDoclist>=pLeaf->szLeaf ){
+ int pgno;
+ Fts5StructureSegment *pSeg = pIter->pSeg;
+
+ /* The last rowid in the doclist may not be on the current page. Search
+ ** forward to find the page containing the last rowid. */
+ for(pgno=pIter->iLeafPgno+1; !p->rc && pgno<=pSeg->pgnoLast; pgno++){
+ i64 iAbs = FTS5_SEGMENT_ROWID(pSeg->iSegid, pgno);
+ Fts5Data *pNew = fts5DataRead(p, iAbs);
+ if( pNew ){
+ int iRowid, bTermless;
+ iRowid = fts5LeafFirstRowidOff(pNew);
+ bTermless = fts5LeafIsTermless(pNew);
+ if( iRowid ){
+ SWAPVAL(Fts5Data*, pNew, pLast);
+ pgnoLast = pgno;
+ }
+ fts5DataRelease(pNew);
+ if( bTermless==0 ) break;
+ }
+ }
+ }
+ }
+
+ /* If pLast is NULL at this point, then the last rowid for this doclist
+ ** lies on the page currently indicated by the iterator. In this case
+ ** pIter->iLeafOffset is already set to point to the position-list size
+ ** field associated with the first relevant rowid on the page.
+ **
+ ** Or, if pLast is non-NULL, then it is the page that contains the last
+ ** rowid. In this case configure the iterator so that it points to the
+ ** first rowid on this page.
+ */
+ if( pLast ){
+ int iOff;
+ fts5DataRelease(pIter->pLeaf);
+ pIter->pLeaf = pLast;
+ pIter->iLeafPgno = pgnoLast;
+ iOff = fts5LeafFirstRowidOff(pLast);
+ iOff += fts5GetVarint(&pLast->p[iOff], (u64*)&pIter->iRowid);
+ pIter->iLeafOffset = iOff;
+
+ if( fts5LeafIsTermless(pLast) ){
+ pIter->iEndofDoclist = pLast->nn+1;
+ }else{
+ pIter->iEndofDoclist = fts5LeafFirstTermOff(pLast);
+ }
+
+ }
+
+ fts5SegIterReverseInitPage(p, pIter);
+}
+
+/*
+** Iterator pIter currently points to the first rowid of a doclist.
+** There is a doclist-index associated with the final term on the current
+** page. If the current term is the last term on the page, load the
+** doclist-index from disk and initialize an iterator at (pIter->pDlidx).
+*/
+static void fts5SegIterLoadDlidx(Fts5Index *p, Fts5SegIter *pIter){
+ int iSeg = pIter->pSeg->iSegid;
+ int bRev = (pIter->flags & FTS5_SEGITER_REVERSE);
+ Fts5Data *pLeaf = pIter->pLeaf; /* Current leaf data */
+
+ assert( pIter->flags & FTS5_SEGITER_ONETERM );
+ assert( pIter->pDlidx==0 );
+
+ /* Check if the current doclist ends on this page. If it does, return
+ ** early without loading the doclist-index (as it belongs to a different
+ ** term. */
+ if( pIter->iTermLeafPgno==pIter->iLeafPgno
+ && pIter->iEndofDoclist<pLeaf->szLeaf
+ ){
+ return;
+ }
+
+ pIter->pDlidx = fts5DlidxIterInit(p, bRev, iSeg, pIter->iTermLeafPgno);
+}
+
+/*
+** The iterator object passed as the second argument currently contains
+** no valid values except for the Fts5SegIter.pLeaf member variable. This
+** function searches the leaf page for a term matching (pTerm/nTerm).
+**
+** If the specified term is found on the page, then the iterator is left
+** pointing to it. If argument bGe is zero and the term is not found,
+** the iterator is left pointing at EOF.
+**
+** If bGe is non-zero and the specified term is not found, then the
+** iterator is left pointing to the smallest term in the segment that
+** is larger than the specified term, even if this term is not on the
+** current page.
+*/
+static void fts5LeafSeek(
+ Fts5Index *p, /* Leave any error code here */
+ int bGe, /* True for a >= search */
+ Fts5SegIter *pIter, /* Iterator to seek */
+ const u8 *pTerm, int nTerm /* Term to search for */
+){
+ int iOff;
+ const u8 *a = pIter->pLeaf->p;
+ int szLeaf = pIter->pLeaf->szLeaf;
+ int n = pIter->pLeaf->nn;
+
+ int nMatch = 0;
+ int nKeep = 0;
+ int nNew = 0;
+ int iTermOff;
+ int iPgidx; /* Current offset in pgidx */
+ int bEndOfPage = 0;
+
+ assert( p->rc==SQLITE_OK );
+
+ iPgidx = szLeaf;
+ iPgidx += fts5GetVarint32(&a[iPgidx], iTermOff);
+ iOff = iTermOff;
+ if( iOff>n ){
+ p->rc = FTS5_CORRUPT;
+ return;
+ }
+
+ while( 1 ){
+
+ /* Figure out how many new bytes are in this term */
+ fts5FastGetVarint32(a, iOff, nNew);
+ if( nKeep<nMatch ){
+ goto search_failed;
+ }
+
+ assert( nKeep>=nMatch );
+ if( nKeep==nMatch ){
+ int nCmp;
+ int i;
+ nCmp = MIN(nNew, nTerm-nMatch);
+ for(i=0; i<nCmp; i++){
+ if( a[iOff+i]!=pTerm[nMatch+i] ) break;
+ }
+ nMatch += i;
+
+ if( nTerm==nMatch ){
+ if( i==nNew ){
+ goto search_success;
+ }else{
+ goto search_failed;
+ }
+ }else if( i<nNew && a[iOff+i]>pTerm[nMatch] ){
+ goto search_failed;
+ }
+ }
+
+ if( iPgidx>=n ){
+ bEndOfPage = 1;
+ break;
+ }
+
+ iPgidx += fts5GetVarint32(&a[iPgidx], nKeep);
+ iTermOff += nKeep;
+ iOff = iTermOff;
+
+ /* Read the nKeep field of the next term. */
+ fts5FastGetVarint32(a, iOff, nKeep);
+ }
+
+ search_failed:
+ if( bGe==0 ){
+ fts5DataRelease(pIter->pLeaf);
+ pIter->pLeaf = 0;
+ return;
+ }else if( bEndOfPage ){
+ do {
+ fts5SegIterNextPage(p, pIter);
+ if( pIter->pLeaf==0 ) return;
+ a = pIter->pLeaf->p;
+ if( fts5LeafIsTermless(pIter->pLeaf)==0 ){
+ iPgidx = pIter->pLeaf->szLeaf;
+ iPgidx += fts5GetVarint32(&pIter->pLeaf->p[iPgidx], iOff);
+ if( iOff<4 || iOff>=pIter->pLeaf->szLeaf ){
+ p->rc = FTS5_CORRUPT;
+ }else{
+ nKeep = 0;
+ iTermOff = iOff;
+ n = pIter->pLeaf->nn;
+ iOff += fts5GetVarint32(&a[iOff], nNew);
+ break;
+ }
+ }
+ }while( 1 );
+ }
+
+ search_success:
+
+ pIter->iLeafOffset = iOff + nNew;
+ pIter->iTermLeafOffset = pIter->iLeafOffset;
+ pIter->iTermLeafPgno = pIter->iLeafPgno;
+
+ fts5BufferSet(&p->rc, &pIter->term, nKeep, pTerm);
+ fts5BufferAppendBlob(&p->rc, &pIter->term, nNew, &a[iOff]);
+
+ if( iPgidx>=n ){
+ pIter->iEndofDoclist = pIter->pLeaf->nn+1;
+ }else{
+ int nExtra;
+ iPgidx += fts5GetVarint32(&a[iPgidx], nExtra);
+ pIter->iEndofDoclist = iTermOff + nExtra;
+ }
+ pIter->iPgidxOff = iPgidx;
+
+ fts5SegIterLoadRowid(p, pIter);
+ fts5SegIterLoadNPos(p, pIter);
+}
+
+static sqlite3_stmt *fts5IdxSelectStmt(Fts5Index *p){
+ if( p->pIdxSelect==0 ){
+ Fts5Config *pConfig = p->pConfig;
+ fts5IndexPrepareStmt(p, &p->pIdxSelect, sqlite3_mprintf(
+ "SELECT pgno FROM '%q'.'%q_idx' WHERE "
+ "segid=? AND term<=? ORDER BY term DESC LIMIT 1",
+ pConfig->zDb, pConfig->zName
+ ));
+ }
+ return p->pIdxSelect;
+}
+
+/*
+** Initialize the object pIter to point to term pTerm/nTerm within segment
+** pSeg. If there is no such term in the index, the iterator is set to EOF.
+**
+** If an error occurs, Fts5Index.rc is set to an appropriate error code. If
+** an error has already occurred when this function is called, it is a no-op.
+*/
+static void fts5SegIterSeekInit(
+ Fts5Index *p, /* FTS5 backend */
+ const u8 *pTerm, int nTerm, /* Term to seek to */
+ int flags, /* Mask of FTS5INDEX_XXX flags */
+ Fts5StructureSegment *pSeg, /* Description of segment */
+ Fts5SegIter *pIter /* Object to populate */
+){
+ int iPg = 1;
+ int bGe = (flags & FTS5INDEX_QUERY_SCAN);
+ int bDlidx = 0; /* True if there is a doclist-index */
+ sqlite3_stmt *pIdxSelect = 0;
+
+ assert( bGe==0 || (flags & FTS5INDEX_QUERY_DESC)==0 );
+ assert( pTerm && nTerm );
+ memset(pIter, 0, sizeof(*pIter));
+ pIter->pSeg = pSeg;
+
+ /* This block sets stack variable iPg to the leaf page number that may
+ ** contain term (pTerm/nTerm), if it is present in the segment. */
+ pIdxSelect = fts5IdxSelectStmt(p);
+ if( p->rc ) return;
+ sqlite3_bind_int(pIdxSelect, 1, pSeg->iSegid);
+ sqlite3_bind_blob(pIdxSelect, 2, pTerm, nTerm, SQLITE_STATIC);
+ if( SQLITE_ROW==sqlite3_step(pIdxSelect) ){
+ i64 val = sqlite3_column_int(pIdxSelect, 0);
+ iPg = (int)(val>>1);
+ bDlidx = (val & 0x0001);
+ }
+ p->rc = sqlite3_reset(pIdxSelect);
+
+ if( iPg<pSeg->pgnoFirst ){
+ iPg = pSeg->pgnoFirst;
+ bDlidx = 0;
+ }
+
+ pIter->iLeafPgno = iPg - 1;
+ fts5SegIterNextPage(p, pIter);
+
+ if( pIter->pLeaf ){
+ fts5LeafSeek(p, bGe, pIter, pTerm, nTerm);
+ }
+
+ if( p->rc==SQLITE_OK && bGe==0 ){
+ pIter->flags |= FTS5_SEGITER_ONETERM;
+ if( pIter->pLeaf ){
+ if( flags & FTS5INDEX_QUERY_DESC ){
+ pIter->flags |= FTS5_SEGITER_REVERSE;
+ }
+ if( bDlidx ){
+ fts5SegIterLoadDlidx(p, pIter);
+ }
+ if( flags & FTS5INDEX_QUERY_DESC ){
+ fts5SegIterReverse(p, pIter);
+ }
+ }
+ }
+
+ fts5SegIterSetNext(p, pIter);
+
+ /* Either:
+ **
+ ** 1) an error has occurred, or
+ ** 2) the iterator points to EOF, or
+ ** 3) the iterator points to an entry with term (pTerm/nTerm), or
+ ** 4) the FTS5INDEX_QUERY_SCAN flag was set and the iterator points
+ ** to an entry with a term greater than or equal to (pTerm/nTerm).
+ */
+ assert( p->rc!=SQLITE_OK /* 1 */
+ || pIter->pLeaf==0 /* 2 */
+ || fts5BufferCompareBlob(&pIter->term, pTerm, nTerm)==0 /* 3 */
+ || (bGe && fts5BufferCompareBlob(&pIter->term, pTerm, nTerm)>0) /* 4 */
+ );
+}
+
+/*
+** Initialize the object pIter to point to term pTerm/nTerm within the
+** in-memory hash table. If there is no such term in the hash-table, the
+** iterator is set to EOF.
+**
+** If an error occurs, Fts5Index.rc is set to an appropriate error code. If
+** an error has already occurred when this function is called, it is a no-op.
+*/
+static void fts5SegIterHashInit(
+ Fts5Index *p, /* FTS5 backend */
+ const u8 *pTerm, int nTerm, /* Term to seek to */
+ int flags, /* Mask of FTS5INDEX_XXX flags */
+ Fts5SegIter *pIter /* Object to populate */
+){
+ const u8 *pList = 0;
+ int nList = 0;
+ const u8 *z = 0;
+ int n = 0;
+
+ assert( p->pHash );
+ assert( p->rc==SQLITE_OK );
+
+ if( pTerm==0 || (flags & FTS5INDEX_QUERY_SCAN) ){
+ p->rc = sqlite3Fts5HashScanInit(p->pHash, (const char*)pTerm, nTerm);
+ sqlite3Fts5HashScanEntry(p->pHash, (const char**)&z, &pList, &nList);
+ n = (z ? (int)strlen((const char*)z) : 0);
+ }else{
+ pIter->flags |= FTS5_SEGITER_ONETERM;
+ sqlite3Fts5HashQuery(p->pHash, (const char*)pTerm, nTerm, &pList, &nList);
+ z = pTerm;
+ n = nTerm;
+ }
+
+ if( pList ){
+ Fts5Data *pLeaf;
+ sqlite3Fts5BufferSet(&p->rc, &pIter->term, n, z);
+ pLeaf = fts5IdxMalloc(p, sizeof(Fts5Data));
+ if( pLeaf==0 ) return;
+ pLeaf->p = (u8*)pList;
+ pLeaf->nn = pLeaf->szLeaf = nList;
+ pIter->pLeaf = pLeaf;
+ pIter->iLeafOffset = fts5GetVarint(pLeaf->p, (u64*)&pIter->iRowid);
+ pIter->iEndofDoclist = pLeaf->nn;
+
+ if( flags & FTS5INDEX_QUERY_DESC ){
+ pIter->flags |= FTS5_SEGITER_REVERSE;
+ fts5SegIterReverseInitPage(p, pIter);
+ }else{
+ fts5SegIterLoadNPos(p, pIter);
+ }
+ }
+
+ fts5SegIterSetNext(p, pIter);
+}
+
+/*
+** Zero the iterator passed as the only argument.
+*/
+static void fts5SegIterClear(Fts5SegIter *pIter){
+ fts5BufferFree(&pIter->term);
+ fts5DataRelease(pIter->pLeaf);
+ fts5DataRelease(pIter->pNextLeaf);
+ fts5DlidxIterFree(pIter->pDlidx);
+ sqlite3_free(pIter->aRowidOffset);
+ memset(pIter, 0, sizeof(Fts5SegIter));
+}
+
+#ifdef SQLITE_DEBUG
+
+/*
+** This function is used as part of the big assert() procedure implemented by
+** fts5AssertMultiIterSetup(). It ensures that the result currently stored
+** in *pRes is the correct result of comparing the current positions of the
+** two iterators.
+*/
+static void fts5AssertComparisonResult(
+ Fts5Iter *pIter,
+ Fts5SegIter *p1,
+ Fts5SegIter *p2,
+ Fts5CResult *pRes
+){
+ int i1 = p1 - pIter->aSeg;
+ int i2 = p2 - pIter->aSeg;
+
+ if( p1->pLeaf || p2->pLeaf ){
+ if( p1->pLeaf==0 ){
+ assert( pRes->iFirst==i2 );
+ }else if( p2->pLeaf==0 ){
+ assert( pRes->iFirst==i1 );
+ }else{
+ int nMin = MIN(p1->term.n, p2->term.n);
+ int res = memcmp(p1->term.p, p2->term.p, nMin);
+ if( res==0 ) res = p1->term.n - p2->term.n;
+
+ if( res==0 ){
+ assert( pRes->bTermEq==1 );
+ assert( p1->iRowid!=p2->iRowid );
+ res = ((p1->iRowid > p2->iRowid)==pIter->bRev) ? -1 : 1;
+ }else{
+ assert( pRes->bTermEq==0 );
+ }
+
+ if( res<0 ){
+ assert( pRes->iFirst==i1 );
+ }else{
+ assert( pRes->iFirst==i2 );
+ }
+ }
+ }
+}
+
+/*
+** This function is a no-op unless SQLITE_DEBUG is defined when this module
+** is compiled. In that case, this function is essentially an assert()
+** statement used to verify that the contents of the pIter->aFirst[] array
+** are correct.
+*/
+static void fts5AssertMultiIterSetup(Fts5Index *p, Fts5Iter *pIter){
+ if( p->rc==SQLITE_OK ){
+ Fts5SegIter *pFirst = &pIter->aSeg[ pIter->aFirst[1].iFirst ];
+ int i;
+
+ assert( (pFirst->pLeaf==0)==pIter->base.bEof );
+
+ /* Check that pIter->iSwitchRowid is set correctly. */
+ for(i=0; i<pIter->nSeg; i++){
+ Fts5SegIter *p1 = &pIter->aSeg[i];
+ assert( p1==pFirst
+ || p1->pLeaf==0
+ || fts5BufferCompare(&pFirst->term, &p1->term)
+ || p1->iRowid==pIter->iSwitchRowid
+ || (p1->iRowid<pIter->iSwitchRowid)==pIter->bRev
+ );
+ }
+
+ for(i=0; i<pIter->nSeg; i+=2){
+ Fts5SegIter *p1 = &pIter->aSeg[i];
+ Fts5SegIter *p2 = &pIter->aSeg[i+1];
+ Fts5CResult *pRes = &pIter->aFirst[(pIter->nSeg + i) / 2];
+ fts5AssertComparisonResult(pIter, p1, p2, pRes);
+ }
+
+ for(i=1; i<(pIter->nSeg / 2); i+=2){
+ Fts5SegIter *p1 = &pIter->aSeg[ pIter->aFirst[i*2].iFirst ];
+ Fts5SegIter *p2 = &pIter->aSeg[ pIter->aFirst[i*2+1].iFirst ];
+ Fts5CResult *pRes = &pIter->aFirst[i];
+ fts5AssertComparisonResult(pIter, p1, p2, pRes);
+ }
+ }
+}
+#else
+# define fts5AssertMultiIterSetup(x,y)
+#endif
+
+/*
+** Do the comparison necessary to populate pIter->aFirst[iOut].
+**
+** If the returned value is non-zero, then it is the index of an entry
+** in the pIter->aSeg[] array that is (a) not at EOF, and (b) pointing
+** to a key that is a duplicate of another, higher priority,
+** segment-iterator in the pSeg->aSeg[] array.
+*/
+static int fts5MultiIterDoCompare(Fts5Iter *pIter, int iOut){
+ int i1; /* Index of left-hand Fts5SegIter */
+ int i2; /* Index of right-hand Fts5SegIter */
+ int iRes;
+ Fts5SegIter *p1; /* Left-hand Fts5SegIter */
+ Fts5SegIter *p2; /* Right-hand Fts5SegIter */
+ Fts5CResult *pRes = &pIter->aFirst[iOut];
+
+ assert( iOut<pIter->nSeg && iOut>0 );
+ assert( pIter->bRev==0 || pIter->bRev==1 );
+
+ if( iOut>=(pIter->nSeg/2) ){
+ i1 = (iOut - pIter->nSeg/2) * 2;
+ i2 = i1 + 1;
+ }else{
+ i1 = pIter->aFirst[iOut*2].iFirst;
+ i2 = pIter->aFirst[iOut*2+1].iFirst;
+ }
+ p1 = &pIter->aSeg[i1];
+ p2 = &pIter->aSeg[i2];
+
+ pRes->bTermEq = 0;
+ if( p1->pLeaf==0 ){ /* If p1 is at EOF */
+ iRes = i2;
+ }else if( p2->pLeaf==0 ){ /* If p2 is at EOF */
+ iRes = i1;
+ }else{
+ int res = fts5BufferCompare(&p1->term, &p2->term);
+ if( res==0 ){
+ assert( i2>i1 );
+ assert( i2!=0 );
+ pRes->bTermEq = 1;
+ if( p1->iRowid==p2->iRowid ){
+ p1->bDel = p2->bDel;
+ return i2;
+ }
+ res = ((p1->iRowid > p2->iRowid)==pIter->bRev) ? -1 : +1;
+ }
+ assert( res!=0 );
+ if( res<0 ){
+ iRes = i1;
+ }else{
+ iRes = i2;
+ }
+ }
+
+ pRes->iFirst = (u16)iRes;
+ return 0;
+}
+
+/*
+** Move the seg-iter so that it points to the first rowid on page iLeafPgno.
+** It is an error if leaf iLeafPgno does not exist or contains no rowids.
+*/
+static void fts5SegIterGotoPage(
+ Fts5Index *p, /* FTS5 backend object */
+ Fts5SegIter *pIter, /* Iterator to advance */
+ int iLeafPgno
+){
+ assert( iLeafPgno>pIter->iLeafPgno );
+
+ if( iLeafPgno>pIter->pSeg->pgnoLast ){
+ p->rc = FTS5_CORRUPT;
+ }else{
+ fts5DataRelease(pIter->pNextLeaf);
+ pIter->pNextLeaf = 0;
+ pIter->iLeafPgno = iLeafPgno-1;
+ fts5SegIterNextPage(p, pIter);
+ assert( p->rc!=SQLITE_OK || pIter->iLeafPgno==iLeafPgno );
+
+ if( p->rc==SQLITE_OK ){
+ int iOff;
+ u8 *a = pIter->pLeaf->p;
+ int n = pIter->pLeaf->szLeaf;
+
+ iOff = fts5LeafFirstRowidOff(pIter->pLeaf);
+ if( iOff<4 || iOff>=n ){
+ p->rc = FTS5_CORRUPT;
+ }else{
+ iOff += fts5GetVarint(&a[iOff], (u64*)&pIter->iRowid);
+ pIter->iLeafOffset = iOff;
+ fts5SegIterLoadNPos(p, pIter);
+ }
+ }
+ }
+}
+
+/*
+** Advance the iterator passed as the second argument until it is at or
+** past rowid iFrom. Regardless of the value of iFrom, the iterator is
+** always advanced at least once.
+*/
+static void fts5SegIterNextFrom(
+ Fts5Index *p, /* FTS5 backend object */
+ Fts5SegIter *pIter, /* Iterator to advance */
+ i64 iMatch /* Advance iterator at least this far */
+){
+ int bRev = (pIter->flags & FTS5_SEGITER_REVERSE);
+ Fts5DlidxIter *pDlidx = pIter->pDlidx;
+ int iLeafPgno = pIter->iLeafPgno;
+ int bMove = 1;
+
+ assert( pIter->flags & FTS5_SEGITER_ONETERM );
+ assert( pIter->pDlidx );
+ assert( pIter->pLeaf );
+
+ if( bRev==0 ){
+ while( !fts5DlidxIterEof(p, pDlidx) && iMatch>fts5DlidxIterRowid(pDlidx) ){
+ iLeafPgno = fts5DlidxIterPgno(pDlidx);
+ fts5DlidxIterNext(p, pDlidx);
+ }
+ assert_nc( iLeafPgno>=pIter->iLeafPgno || p->rc );
+ if( iLeafPgno>pIter->iLeafPgno ){
+ fts5SegIterGotoPage(p, pIter, iLeafPgno);
+ bMove = 0;
+ }
+ }else{
+ assert( pIter->pNextLeaf==0 );
+ assert( iMatch<pIter->iRowid );
+ while( !fts5DlidxIterEof(p, pDlidx) && iMatch<fts5DlidxIterRowid(pDlidx) ){
+ fts5DlidxIterPrev(p, pDlidx);
+ }
+ iLeafPgno = fts5DlidxIterPgno(pDlidx);
+
+ assert( fts5DlidxIterEof(p, pDlidx) || iLeafPgno<=pIter->iLeafPgno );
+
+ if( iLeafPgno<pIter->iLeafPgno ){
+ pIter->iLeafPgno = iLeafPgno+1;
+ fts5SegIterReverseNewPage(p, pIter);
+ bMove = 0;
+ }
+ }
+
+ do{
+ if( bMove && p->rc==SQLITE_OK ) pIter->xNext(p, pIter, 0);
+ if( pIter->pLeaf==0 ) break;
+ if( bRev==0 && pIter->iRowid>=iMatch ) break;
+ if( bRev!=0 && pIter->iRowid<=iMatch ) break;
+ bMove = 1;
+ }while( p->rc==SQLITE_OK );
+}
+
+
+/*
+** Free the iterator object passed as the second argument.
+*/
+static void fts5MultiIterFree(Fts5Iter *pIter){
+ if( pIter ){
+ int i;
+ for(i=0; i<pIter->nSeg; i++){
+ fts5SegIterClear(&pIter->aSeg[i]);
+ }
+ fts5StructureRelease(pIter->pStruct);
+ fts5BufferFree(&pIter->poslist);
+ sqlite3_free(pIter);
+ }
+}
+
+static void fts5MultiIterAdvanced(
+ Fts5Index *p, /* FTS5 backend to iterate within */
+ Fts5Iter *pIter, /* Iterator to update aFirst[] array for */
+ int iChanged, /* Index of sub-iterator just advanced */
+ int iMinset /* Minimum entry in aFirst[] to set */
+){
+ int i;
+ for(i=(pIter->nSeg+iChanged)/2; i>=iMinset && p->rc==SQLITE_OK; i=i/2){
+ int iEq;
+ if( (iEq = fts5MultiIterDoCompare(pIter, i)) ){
+ Fts5SegIter *pSeg = &pIter->aSeg[iEq];
+ assert( p->rc==SQLITE_OK );
+ pSeg->xNext(p, pSeg, 0);
+ i = pIter->nSeg + iEq;
+ }
+ }
+}
+
+/*
+** Sub-iterator iChanged of iterator pIter has just been advanced. It still
+** points to the same term though - just a different rowid. This function
+** attempts to update the contents of the pIter->aFirst[] accordingly.
+** If it does so successfully, 0 is returned. Otherwise 1.
+**
+** If non-zero is returned, the caller should call fts5MultiIterAdvanced()
+** on the iterator instead. That function does the same as this one, except
+** that it deals with more complicated cases as well.
+*/
+static int fts5MultiIterAdvanceRowid(
+ Fts5Iter *pIter, /* Iterator to update aFirst[] array for */
+ int iChanged, /* Index of sub-iterator just advanced */
+ Fts5SegIter **ppFirst
+){
+ Fts5SegIter *pNew = &pIter->aSeg[iChanged];
+
+ if( pNew->iRowid==pIter->iSwitchRowid
+ || (pNew->iRowid<pIter->iSwitchRowid)==pIter->bRev
+ ){
+ int i;
+ Fts5SegIter *pOther = &pIter->aSeg[iChanged ^ 0x0001];
+ pIter->iSwitchRowid = pIter->bRev ? SMALLEST_INT64 : LARGEST_INT64;
+ for(i=(pIter->nSeg+iChanged)/2; 1; i=i/2){
+ Fts5CResult *pRes = &pIter->aFirst[i];
+
+ assert( pNew->pLeaf );
+ assert( pRes->bTermEq==0 || pOther->pLeaf );
+
+ if( pRes->bTermEq ){
+ if( pNew->iRowid==pOther->iRowid ){
+ return 1;
+ }else if( (pOther->iRowid>pNew->iRowid)==pIter->bRev ){
+ pIter->iSwitchRowid = pOther->iRowid;
+ pNew = pOther;
+ }else if( (pOther->iRowid>pIter->iSwitchRowid)==pIter->bRev ){
+ pIter->iSwitchRowid = pOther->iRowid;
+ }
+ }
+ pRes->iFirst = (u16)(pNew - pIter->aSeg);
+ if( i==1 ) break;
+
+ pOther = &pIter->aSeg[ pIter->aFirst[i ^ 0x0001].iFirst ];
+ }
+ }
+
+ *ppFirst = pNew;
+ return 0;
+}
+
+/*
+** Set the pIter->bEof variable based on the state of the sub-iterators.
+*/
+static void fts5MultiIterSetEof(Fts5Iter *pIter){
+ Fts5SegIter *pSeg = &pIter->aSeg[ pIter->aFirst[1].iFirst ];
+ pIter->base.bEof = pSeg->pLeaf==0;
+ pIter->iSwitchRowid = pSeg->iRowid;
+}
+
+/*
+** Move the iterator to the next entry.
+**
+** If an error occurs, an error code is left in Fts5Index.rc. It is not
+** considered an error if the iterator reaches EOF, or if it is already at
+** EOF when this function is called.
+*/
+static void fts5MultiIterNext(
+ Fts5Index *p,
+ Fts5Iter *pIter,
+ int bFrom, /* True if argument iFrom is valid */
+ i64 iFrom /* Advance at least as far as this */
+){
+ int bUseFrom = bFrom;
+ while( p->rc==SQLITE_OK ){
+ int iFirst = pIter->aFirst[1].iFirst;
+ int bNewTerm = 0;
+ Fts5SegIter *pSeg = &pIter->aSeg[iFirst];
+ assert( p->rc==SQLITE_OK );
+ if( bUseFrom && pSeg->pDlidx ){
+ fts5SegIterNextFrom(p, pSeg, iFrom);
+ }else{
+ pSeg->xNext(p, pSeg, &bNewTerm);
+ }
+
+ if( pSeg->pLeaf==0 || bNewTerm
+ || fts5MultiIterAdvanceRowid(pIter, iFirst, &pSeg)
+ ){
+ fts5MultiIterAdvanced(p, pIter, iFirst, 1);
+ fts5MultiIterSetEof(pIter);
+ pSeg = &pIter->aSeg[pIter->aFirst[1].iFirst];
+ if( pSeg->pLeaf==0 ) return;
+ }
+
+ fts5AssertMultiIterSetup(p, pIter);
+ assert( pSeg==&pIter->aSeg[pIter->aFirst[1].iFirst] && pSeg->pLeaf );
+ if( pIter->bSkipEmpty==0 || pSeg->nPos ){
+ pIter->xSetOutputs(pIter, pSeg);
+ return;
+ }
+ bUseFrom = 0;
+ }
+}
+
+static void fts5MultiIterNext2(
+ Fts5Index *p,
+ Fts5Iter *pIter,
+ int *pbNewTerm /* OUT: True if *might* be new term */
+){
+ assert( pIter->bSkipEmpty );
+ if( p->rc==SQLITE_OK ){
+ do {
+ int iFirst = pIter->aFirst[1].iFirst;
+ Fts5SegIter *pSeg = &pIter->aSeg[iFirst];
+ int bNewTerm = 0;
+
+ assert( p->rc==SQLITE_OK );
+ pSeg->xNext(p, pSeg, &bNewTerm);
+ if( pSeg->pLeaf==0 || bNewTerm
+ || fts5MultiIterAdvanceRowid(pIter, iFirst, &pSeg)
+ ){
+ fts5MultiIterAdvanced(p, pIter, iFirst, 1);
+ fts5MultiIterSetEof(pIter);
+ *pbNewTerm = 1;
+ }else{
+ *pbNewTerm = 0;
+ }
+ fts5AssertMultiIterSetup(p, pIter);
+
+ }while( fts5MultiIterIsEmpty(p, pIter) );
+ }
+}
+
+static void fts5IterSetOutputs_Noop(Fts5Iter *pUnused1, Fts5SegIter *pUnused2){
+ UNUSED_PARAM2(pUnused1, pUnused2);
+}
+
+static Fts5Iter *fts5MultiIterAlloc(
+ Fts5Index *p, /* FTS5 backend to iterate within */
+ int nSeg
+){
+ Fts5Iter *pNew;
+ int nSlot; /* Power of two >= nSeg */
+
+ for(nSlot=2; nSlot<nSeg; nSlot=nSlot*2);
+ pNew = fts5IdxMalloc(p,
+ sizeof(Fts5Iter) + /* pNew */
+ sizeof(Fts5SegIter) * (nSlot-1) + /* pNew->aSeg[] */
+ sizeof(Fts5CResult) * nSlot /* pNew->aFirst[] */
+ );
+ if( pNew ){
+ pNew->nSeg = nSlot;
+ pNew->aFirst = (Fts5CResult*)&pNew->aSeg[nSlot];
+ pNew->pIndex = p;
+ pNew->xSetOutputs = fts5IterSetOutputs_Noop;
+ }
+ return pNew;
+}
+
+static void fts5PoslistCallback(
+ Fts5Index *pUnused,
+ void *pContext,
+ const u8 *pChunk, int nChunk
+){
+ UNUSED_PARAM(pUnused);
+ assert_nc( nChunk>=0 );
+ if( nChunk>0 ){
+ fts5BufferSafeAppendBlob((Fts5Buffer*)pContext, pChunk, nChunk);
+ }
+}
+
+typedef struct PoslistCallbackCtx PoslistCallbackCtx;
+struct PoslistCallbackCtx {
+ Fts5Buffer *pBuf; /* Append to this buffer */
+ Fts5Colset *pColset; /* Restrict matches to this column */
+ int eState; /* See above */
+};
+
+typedef struct PoslistOffsetsCtx PoslistOffsetsCtx;
+struct PoslistOffsetsCtx {
+ Fts5Buffer *pBuf; /* Append to this buffer */
+ Fts5Colset *pColset; /* Restrict matches to this column */
+ int iRead;
+ int iWrite;
+};
+
+/*
+** TODO: Make this more efficient!
+*/
+static int fts5IndexColsetTest(Fts5Colset *pColset, int iCol){
+ int i;
+ for(i=0; i<pColset->nCol; i++){
+ if( pColset->aiCol[i]==iCol ) return 1;
+ }
+ return 0;
+}
+
+static void fts5PoslistOffsetsCallback(
+ Fts5Index *pUnused,
+ void *pContext,
+ const u8 *pChunk, int nChunk
+){
+ PoslistOffsetsCtx *pCtx = (PoslistOffsetsCtx*)pContext;
+ UNUSED_PARAM(pUnused);
+ assert_nc( nChunk>=0 );
+ if( nChunk>0 ){
+ int i = 0;
+ while( i<nChunk ){
+ int iVal;
+ i += fts5GetVarint32(&pChunk[i], iVal);
+ iVal += pCtx->iRead - 2;
+ pCtx->iRead = iVal;
+ if( fts5IndexColsetTest(pCtx->pColset, iVal) ){
+ fts5BufferSafeAppendVarint(pCtx->pBuf, iVal + 2 - pCtx->iWrite);
+ pCtx->iWrite = iVal;
+ }
+ }
+ }
+}
+
+static void fts5PoslistFilterCallback(
+ Fts5Index *pUnused,
+ void *pContext,
+ const u8 *pChunk, int nChunk
+){
+ PoslistCallbackCtx *pCtx = (PoslistCallbackCtx*)pContext;
+ UNUSED_PARAM(pUnused);
+ assert_nc( nChunk>=0 );
+ if( nChunk>0 ){
+ /* Search through to find the first varint with value 1. This is the
+ ** start of the next columns hits. */
+ int i = 0;
+ int iStart = 0;
+
+ if( pCtx->eState==2 ){
+ int iCol;
+ fts5FastGetVarint32(pChunk, i, iCol);
+ if( fts5IndexColsetTest(pCtx->pColset, iCol) ){
+ pCtx->eState = 1;
+ fts5BufferSafeAppendVarint(pCtx->pBuf, 1);
+ }else{
+ pCtx->eState = 0;
+ }
+ }
+
+ do {
+ while( i<nChunk && pChunk[i]!=0x01 ){
+ while( pChunk[i] & 0x80 ) i++;
+ i++;
+ }
+ if( pCtx->eState ){
+ fts5BufferSafeAppendBlob(pCtx->pBuf, &pChunk[iStart], i-iStart);
+ }
+ if( i<nChunk ){
+ int iCol;
+ iStart = i;
+ i++;
+ if( i>=nChunk ){
+ pCtx->eState = 2;
+ }else{
+ fts5FastGetVarint32(pChunk, i, iCol);
+ pCtx->eState = fts5IndexColsetTest(pCtx->pColset, iCol);
+ if( pCtx->eState ){
+ fts5BufferSafeAppendBlob(pCtx->pBuf, &pChunk[iStart], i-iStart);
+ iStart = i;
+ }
+ }
+ }
+ }while( i<nChunk );
+ }
+}
+
+static void fts5ChunkIterate(
+ Fts5Index *p, /* Index object */
+ Fts5SegIter *pSeg, /* Poslist of this iterator */
+ void *pCtx, /* Context pointer for xChunk callback */
+ void (*xChunk)(Fts5Index*, void*, const u8*, int)
+){
+ int nRem = pSeg->nPos; /* Number of bytes still to come */
+ Fts5Data *pData = 0;
+ u8 *pChunk = &pSeg->pLeaf->p[pSeg->iLeafOffset];
+ int nChunk = MIN(nRem, pSeg->pLeaf->szLeaf - pSeg->iLeafOffset);
+ int pgno = pSeg->iLeafPgno;
+ int pgnoSave = 0;
+
+ /* This function does notmwork with detail=none databases. */
+ assert( p->pConfig->eDetail!=FTS5_DETAIL_NONE );
+
+ if( (pSeg->flags & FTS5_SEGITER_REVERSE)==0 ){
+ pgnoSave = pgno+1;
+ }
+
+ while( 1 ){
+ xChunk(p, pCtx, pChunk, nChunk);
+ nRem -= nChunk;
+ fts5DataRelease(pData);
+ if( nRem<=0 ){
+ break;
+ }else{
+ pgno++;
+ pData = fts5DataRead(p, FTS5_SEGMENT_ROWID(pSeg->pSeg->iSegid, pgno));
+ if( pData==0 ) break;
+ pChunk = &pData->p[4];
+ nChunk = MIN(nRem, pData->szLeaf - 4);
+ if( pgno==pgnoSave ){
+ assert( pSeg->pNextLeaf==0 );
+ pSeg->pNextLeaf = pData;
+ pData = 0;
+ }
+ }
+ }
+}
+
+/*
+** Iterator pIter currently points to a valid entry (not EOF). This
+** function appends the position list data for the current entry to
+** buffer pBuf. It does not make a copy of the position-list size
+** field.
+*/
+static void fts5SegiterPoslist(
+ Fts5Index *p,
+ Fts5SegIter *pSeg,
+ Fts5Colset *pColset,
+ Fts5Buffer *pBuf
+){
+ if( 0==fts5BufferGrow(&p->rc, pBuf, pSeg->nPos) ){
+ if( pColset==0 ){
+ fts5ChunkIterate(p, pSeg, (void*)pBuf, fts5PoslistCallback);
+ }else{
+ if( p->pConfig->eDetail==FTS5_DETAIL_FULL ){
+ PoslistCallbackCtx sCtx;
+ sCtx.pBuf = pBuf;
+ sCtx.pColset = pColset;
+ sCtx.eState = fts5IndexColsetTest(pColset, 0);
+ assert( sCtx.eState==0 || sCtx.eState==1 );
+ fts5ChunkIterate(p, pSeg, (void*)&sCtx, fts5PoslistFilterCallback);
+ }else{
+ PoslistOffsetsCtx sCtx;
+ memset(&sCtx, 0, sizeof(sCtx));
+ sCtx.pBuf = pBuf;
+ sCtx.pColset = pColset;
+ fts5ChunkIterate(p, pSeg, (void*)&sCtx, fts5PoslistOffsetsCallback);
+ }
+ }
+ }
+}
+
+/*
+** IN/OUT parameter (*pa) points to a position list n bytes in size. If
+** the position list contains entries for column iCol, then (*pa) is set
+** to point to the sub-position-list for that column and the number of
+** bytes in it returned. Or, if the argument position list does not
+** contain any entries for column iCol, return 0.
+*/
+static int fts5IndexExtractCol(
+ const u8 **pa, /* IN/OUT: Pointer to poslist */
+ int n, /* IN: Size of poslist in bytes */
+ int iCol /* Column to extract from poslist */
+){
+ int iCurrent = 0; /* Anything before the first 0x01 is col 0 */
+ const u8 *p = *pa;
+ const u8 *pEnd = &p[n]; /* One byte past end of position list */
+
+ while( iCol>iCurrent ){
+ /* Advance pointer p until it points to pEnd or an 0x01 byte that is
+ ** not part of a varint. Note that it is not possible for a negative
+ ** or extremely large varint to occur within an uncorrupted position
+ ** list. So the last byte of each varint may be assumed to have a clear
+ ** 0x80 bit. */
+ while( *p!=0x01 ){
+ while( *p++ & 0x80 );
+ if( p>=pEnd ) return 0;
+ }
+ *pa = p++;
+ iCurrent = *p++;
+ if( iCurrent & 0x80 ){
+ p--;
+ p += fts5GetVarint32(p, iCurrent);
+ }
+ }
+ if( iCol!=iCurrent ) return 0;
+
+ /* Advance pointer p until it points to pEnd or an 0x01 byte that is
+ ** not part of a varint */
+ while( p<pEnd && *p!=0x01 ){
+ while( *p++ & 0x80 );
+ }
+
+ return p - (*pa);
+}
+
+static int fts5IndexExtractColset (
+ Fts5Colset *pColset, /* Colset to filter on */
+ const u8 *pPos, int nPos, /* Position list */
+ Fts5Buffer *pBuf /* Output buffer */
+){
+ int rc = SQLITE_OK;
+ int i;
+
+ fts5BufferZero(pBuf);
+ for(i=0; i<pColset->nCol; i++){
+ const u8 *pSub = pPos;
+ int nSub = fts5IndexExtractCol(&pSub, nPos, pColset->aiCol[i]);
+ if( nSub ){
+ fts5BufferAppendBlob(&rc, pBuf, nSub, pSub);
+ }
+ }
+ return rc;
+}
+
+/*
+** xSetOutputs callback used by detail=none tables.
+*/
+static void fts5IterSetOutputs_None(Fts5Iter *pIter, Fts5SegIter *pSeg){
+ assert( pIter->pIndex->pConfig->eDetail==FTS5_DETAIL_NONE );
+ pIter->base.iRowid = pSeg->iRowid;
+ pIter->base.nData = pSeg->nPos;
+}
+
+/*
+** xSetOutputs callback used by detail=full and detail=col tables when no
+** column filters are specified.
+*/
+static void fts5IterSetOutputs_Nocolset(Fts5Iter *pIter, Fts5SegIter *pSeg){
+ pIter->base.iRowid = pSeg->iRowid;
+ pIter->base.nData = pSeg->nPos;
+
+ assert( pIter->pIndex->pConfig->eDetail!=FTS5_DETAIL_NONE );
+ assert( pIter->pColset==0 );
+
+ if( pSeg->iLeafOffset+pSeg->nPos<=pSeg->pLeaf->szLeaf ){
+ /* All data is stored on the current page. Populate the output
+ ** variables to point into the body of the page object. */
+ pIter->base.pData = &pSeg->pLeaf->p[pSeg->iLeafOffset];
+ }else{
+ /* The data is distributed over two or more pages. Copy it into the
+ ** Fts5Iter.poslist buffer and then set the output pointer to point
+ ** to this buffer. */
+ fts5BufferZero(&pIter->poslist);
+ fts5SegiterPoslist(pIter->pIndex, pSeg, 0, &pIter->poslist);
+ pIter->base.pData = pIter->poslist.p;
+ }
+}
+
+/*
+** xSetOutputs callback used by detail=col when there is a column filter
+** and there are 100 or more columns. Also called as a fallback from
+** fts5IterSetOutputs_Col100 if the column-list spans more than one page.
+*/
+static void fts5IterSetOutputs_Col(Fts5Iter *pIter, Fts5SegIter *pSeg){
+ fts5BufferZero(&pIter->poslist);
+ fts5SegiterPoslist(pIter->pIndex, pSeg, pIter->pColset, &pIter->poslist);
+ pIter->base.iRowid = pSeg->iRowid;
+ pIter->base.pData = pIter->poslist.p;
+ pIter->base.nData = pIter->poslist.n;
+}
+
+/*
+** xSetOutputs callback used when:
+**
+** * detail=col,
+** * there is a column filter, and
+** * the table contains 100 or fewer columns.
+**
+** The last point is to ensure all column numbers are stored as
+** single-byte varints.
+*/
+static void fts5IterSetOutputs_Col100(Fts5Iter *pIter, Fts5SegIter *pSeg){
+
+ assert( pIter->pIndex->pConfig->eDetail==FTS5_DETAIL_COLUMNS );
+ assert( pIter->pColset );
+
+ if( pSeg->iLeafOffset+pSeg->nPos>pSeg->pLeaf->szLeaf ){
+ fts5IterSetOutputs_Col(pIter, pSeg);
+ }else{
+ u8 *a = (u8*)&pSeg->pLeaf->p[pSeg->iLeafOffset];
+ u8 *pEnd = (u8*)&a[pSeg->nPos];
+ int iPrev = 0;
+ int *aiCol = pIter->pColset->aiCol;
+ int *aiColEnd = &aiCol[pIter->pColset->nCol];
+
+ u8 *aOut = pIter->poslist.p;
+ int iPrevOut = 0;
+
+ pIter->base.iRowid = pSeg->iRowid;
+
+ while( a<pEnd ){
+ iPrev += (int)a++[0] - 2;
+ while( *aiCol<iPrev ){
+ aiCol++;
+ if( aiCol==aiColEnd ) goto setoutputs_col_out;
+ }
+ if( *aiCol==iPrev ){
+ *aOut++ = (u8)((iPrev - iPrevOut) + 2);
+ iPrevOut = iPrev;
+ }
+ }
+
+setoutputs_col_out:
+ pIter->base.pData = pIter->poslist.p;
+ pIter->base.nData = aOut - pIter->poslist.p;
+ }
+}
+
+/*
+** xSetOutputs callback used by detail=full when there is a column filter.
+*/
+static void fts5IterSetOutputs_Full(Fts5Iter *pIter, Fts5SegIter *pSeg){
+ Fts5Colset *pColset = pIter->pColset;
+ pIter->base.iRowid = pSeg->iRowid;
+
+ assert( pIter->pIndex->pConfig->eDetail==FTS5_DETAIL_FULL );
+ assert( pColset );
+
+ if( pSeg->iLeafOffset+pSeg->nPos<=pSeg->pLeaf->szLeaf ){
+ /* All data is stored on the current page. Populate the output
+ ** variables to point into the body of the page object. */
+ const u8 *a = &pSeg->pLeaf->p[pSeg->iLeafOffset];
+ if( pColset->nCol==1 ){
+ pIter->base.nData = fts5IndexExtractCol(&a, pSeg->nPos,pColset->aiCol[0]);
+ pIter->base.pData = a;
+ }else{
+ fts5BufferZero(&pIter->poslist);
+ fts5IndexExtractColset(pColset, a, pSeg->nPos, &pIter->poslist);
+ pIter->base.pData = pIter->poslist.p;
+ pIter->base.nData = pIter->poslist.n;
+ }
+ }else{
+ /* The data is distributed over two or more pages. Copy it into the
+ ** Fts5Iter.poslist buffer and then set the output pointer to point
+ ** to this buffer. */
+ fts5BufferZero(&pIter->poslist);
+ fts5SegiterPoslist(pIter->pIndex, pSeg, pColset, &pIter->poslist);
+ pIter->base.pData = pIter->poslist.p;
+ pIter->base.nData = pIter->poslist.n;
+ }
+}
+
+static void fts5IterSetOutputCb(int *pRc, Fts5Iter *pIter){
+ if( *pRc==SQLITE_OK ){
+ Fts5Config *pConfig = pIter->pIndex->pConfig;
+ if( pConfig->eDetail==FTS5_DETAIL_NONE ){
+ pIter->xSetOutputs = fts5IterSetOutputs_None;
+ }
+
+ else if( pIter->pColset==0 ){
+ pIter->xSetOutputs = fts5IterSetOutputs_Nocolset;
+ }
+
+ else if( pConfig->eDetail==FTS5_DETAIL_FULL ){
+ pIter->xSetOutputs = fts5IterSetOutputs_Full;
+ }
+
+ else{
+ assert( pConfig->eDetail==FTS5_DETAIL_COLUMNS );
+ if( pConfig->nCol<=100 ){
+ pIter->xSetOutputs = fts5IterSetOutputs_Col100;
+ sqlite3Fts5BufferSize(pRc, &pIter->poslist, pConfig->nCol);
+ }else{
+ pIter->xSetOutputs = fts5IterSetOutputs_Col;
+ }
+ }
+ }
+}
+
+
+/*
+** Allocate a new Fts5Iter object.
+**
+** The new object will be used to iterate through data in structure pStruct.
+** If iLevel is -ve, then all data in all segments is merged. Or, if iLevel
+** is zero or greater, data from the first nSegment segments on level iLevel
+** is merged.
+**
+** The iterator initially points to the first term/rowid entry in the
+** iterated data.
+*/
+static void fts5MultiIterNew(
+ Fts5Index *p, /* FTS5 backend to iterate within */
+ Fts5Structure *pStruct, /* Structure of specific index */
+ int flags, /* FTS5INDEX_QUERY_XXX flags */
+ Fts5Colset *pColset, /* Colset to filter on (or NULL) */
+ const u8 *pTerm, int nTerm, /* Term to seek to (or NULL/0) */
+ int iLevel, /* Level to iterate (-1 for all) */
+ int nSegment, /* Number of segments to merge (iLevel>=0) */
+ Fts5Iter **ppOut /* New object */
+){
+ int nSeg = 0; /* Number of segment-iters in use */
+ int iIter = 0; /* */
+ int iSeg; /* Used to iterate through segments */
+ Fts5StructureLevel *pLvl;
+ Fts5Iter *pNew;
+
+ assert( (pTerm==0 && nTerm==0) || iLevel<0 );
+
+ /* Allocate space for the new multi-seg-iterator. */
+ if( p->rc==SQLITE_OK ){
+ if( iLevel<0 ){
+ assert( pStruct->nSegment==fts5StructureCountSegments(pStruct) );
+ nSeg = pStruct->nSegment;
+ nSeg += (p->pHash ? 1 : 0);
+ }else{
+ nSeg = MIN(pStruct->aLevel[iLevel].nSeg, nSegment);
+ }
+ }
+ *ppOut = pNew = fts5MultiIterAlloc(p, nSeg);
+ if( pNew==0 ) return;
+ pNew->bRev = (0!=(flags & FTS5INDEX_QUERY_DESC));
+ pNew->bSkipEmpty = (0!=(flags & FTS5INDEX_QUERY_SKIPEMPTY));
+ pNew->pStruct = pStruct;
+ pNew->pColset = pColset;
+ fts5StructureRef(pStruct);
+ if( (flags & FTS5INDEX_QUERY_NOOUTPUT)==0 ){
+ fts5IterSetOutputCb(&p->rc, pNew);
+ }
+
+ /* Initialize each of the component segment iterators. */
+ if( p->rc==SQLITE_OK ){
+ if( iLevel<0 ){
+ Fts5StructureLevel *pEnd = &pStruct->aLevel[pStruct->nLevel];
+ if( p->pHash ){
+ /* Add a segment iterator for the current contents of the hash table. */
+ Fts5SegIter *pIter = &pNew->aSeg[iIter++];
+ fts5SegIterHashInit(p, pTerm, nTerm, flags, pIter);
+ }
+ for(pLvl=&pStruct->aLevel[0]; pLvl<pEnd; pLvl++){
+ for(iSeg=pLvl->nSeg-1; iSeg>=0; iSeg--){
+ Fts5StructureSegment *pSeg = &pLvl->aSeg[iSeg];
+ Fts5SegIter *pIter = &pNew->aSeg[iIter++];
+ if( pTerm==0 ){
+ fts5SegIterInit(p, pSeg, pIter);
+ }else{
+ fts5SegIterSeekInit(p, pTerm, nTerm, flags, pSeg, pIter);
+ }
+ }
+ }
+ }else{
+ pLvl = &pStruct->aLevel[iLevel];
+ for(iSeg=nSeg-1; iSeg>=0; iSeg--){
+ fts5SegIterInit(p, &pLvl->aSeg[iSeg], &pNew->aSeg[iIter++]);
+ }
+ }
+ assert( iIter==nSeg );
+ }
+
+ /* If the above was successful, each component iterators now points
+ ** to the first entry in its segment. In this case initialize the
+ ** aFirst[] array. Or, if an error has occurred, free the iterator
+ ** object and set the output variable to NULL. */
+ if( p->rc==SQLITE_OK ){
+ for(iIter=pNew->nSeg-1; iIter>0; iIter--){
+ int iEq;
+ if( (iEq = fts5MultiIterDoCompare(pNew, iIter)) ){
+ Fts5SegIter *pSeg = &pNew->aSeg[iEq];
+ if( p->rc==SQLITE_OK ) pSeg->xNext(p, pSeg, 0);
+ fts5MultiIterAdvanced(p, pNew, iEq, iIter);
+ }
+ }
+ fts5MultiIterSetEof(pNew);
+ fts5AssertMultiIterSetup(p, pNew);
+
+ if( pNew->bSkipEmpty && fts5MultiIterIsEmpty(p, pNew) ){
+ fts5MultiIterNext(p, pNew, 0, 0);
+ }else if( pNew->base.bEof==0 ){
+ Fts5SegIter *pSeg = &pNew->aSeg[pNew->aFirst[1].iFirst];
+ pNew->xSetOutputs(pNew, pSeg);
+ }
+
+ }else{
+ fts5MultiIterFree(pNew);
+ *ppOut = 0;
+ }
+}
+
+/*
+** Create an Fts5Iter that iterates through the doclist provided
+** as the second argument.
+*/
+static void fts5MultiIterNew2(
+ Fts5Index *p, /* FTS5 backend to iterate within */
+ Fts5Data *pData, /* Doclist to iterate through */
+ int bDesc, /* True for descending rowid order */
+ Fts5Iter **ppOut /* New object */
+){
+ Fts5Iter *pNew;
+ pNew = fts5MultiIterAlloc(p, 2);
+ if( pNew ){
+ Fts5SegIter *pIter = &pNew->aSeg[1];
+
+ pIter->flags = FTS5_SEGITER_ONETERM;
+ if( pData->szLeaf>0 ){
+ pIter->pLeaf = pData;
+ pIter->iLeafOffset = fts5GetVarint(pData->p, (u64*)&pIter->iRowid);
+ pIter->iEndofDoclist = pData->nn;
+ pNew->aFirst[1].iFirst = 1;
+ if( bDesc ){
+ pNew->bRev = 1;
+ pIter->flags |= FTS5_SEGITER_REVERSE;
+ fts5SegIterReverseInitPage(p, pIter);
+ }else{
+ fts5SegIterLoadNPos(p, pIter);
+ }
+ pData = 0;
+ }else{
+ pNew->base.bEof = 1;
+ }
+ fts5SegIterSetNext(p, pIter);
+
+ *ppOut = pNew;
+ }
+
+ fts5DataRelease(pData);
+}
+
+/*
+** Return true if the iterator is at EOF or if an error has occurred.
+** False otherwise.
+*/
+static int fts5MultiIterEof(Fts5Index *p, Fts5Iter *pIter){
+ assert( p->rc
+ || (pIter->aSeg[ pIter->aFirst[1].iFirst ].pLeaf==0)==pIter->base.bEof
+ );
+ return (p->rc || pIter->base.bEof);
+}
+
+/*
+** Return the rowid of the entry that the iterator currently points
+** to. If the iterator points to EOF when this function is called the
+** results are undefined.
+*/
+static i64 fts5MultiIterRowid(Fts5Iter *pIter){
+ assert( pIter->aSeg[ pIter->aFirst[1].iFirst ].pLeaf );
+ return pIter->aSeg[ pIter->aFirst[1].iFirst ].iRowid;
+}
+
+/*
+** Move the iterator to the next entry at or following iMatch.
+*/
+static void fts5MultiIterNextFrom(
+ Fts5Index *p,
+ Fts5Iter *pIter,
+ i64 iMatch
+){
+ while( 1 ){
+ i64 iRowid;
+ fts5MultiIterNext(p, pIter, 1, iMatch);
+ if( fts5MultiIterEof(p, pIter) ) break;
+ iRowid = fts5MultiIterRowid(pIter);
+ if( pIter->bRev==0 && iRowid>=iMatch ) break;
+ if( pIter->bRev!=0 && iRowid<=iMatch ) break;
+ }
+}
+
+/*
+** Return a pointer to a buffer containing the term associated with the
+** entry that the iterator currently points to.
+*/
+static const u8 *fts5MultiIterTerm(Fts5Iter *pIter, int *pn){
+ Fts5SegIter *p = &pIter->aSeg[ pIter->aFirst[1].iFirst ];
+ *pn = p->term.n;
+ return p->term.p;
+}
+
+/*
+** Allocate a new segment-id for the structure pStruct. The new segment
+** id must be between 1 and 65335 inclusive, and must not be used by
+** any currently existing segment. If a free segment id cannot be found,
+** SQLITE_FULL is returned.
+**
+** If an error has already occurred, this function is a no-op. 0 is
+** returned in this case.
+*/
+static int fts5AllocateSegid(Fts5Index *p, Fts5Structure *pStruct){
+ int iSegid = 0;
+
+ if( p->rc==SQLITE_OK ){
+ if( pStruct->nSegment>=FTS5_MAX_SEGMENT ){
+ p->rc = SQLITE_FULL;
+ }else{
+ /* FTS5_MAX_SEGMENT is currently defined as 2000. So the following
+ ** array is 63 elements, or 252 bytes, in size. */
+ u32 aUsed[(FTS5_MAX_SEGMENT+31) / 32];
+ int iLvl, iSeg;
+ int i;
+ u32 mask;
+ memset(aUsed, 0, sizeof(aUsed));
+ for(iLvl=0; iLvl<pStruct->nLevel; iLvl++){
+ for(iSeg=0; iSeg<pStruct->aLevel[iLvl].nSeg; iSeg++){
+ int iId = pStruct->aLevel[iLvl].aSeg[iSeg].iSegid;
+ if( iId<=FTS5_MAX_SEGMENT ){
+ aUsed[(iId-1) / 32] |= 1 << ((iId-1) % 32);
+ }
+ }
+ }
+
+ for(i=0; aUsed[i]==0xFFFFFFFF; i++);
+ mask = aUsed[i];
+ for(iSegid=0; mask & (1 << iSegid); iSegid++);
+ iSegid += 1 + i*32;
+
+#ifdef SQLITE_DEBUG
+ for(iLvl=0; iLvl<pStruct->nLevel; iLvl++){
+ for(iSeg=0; iSeg<pStruct->aLevel[iLvl].nSeg; iSeg++){
+ assert( iSegid!=pStruct->aLevel[iLvl].aSeg[iSeg].iSegid );
+ }
+ }
+ assert( iSegid>0 && iSegid<=FTS5_MAX_SEGMENT );
+
+ {
+ sqlite3_stmt *pIdxSelect = fts5IdxSelectStmt(p);
+ if( p->rc==SQLITE_OK ){
+ u8 aBlob[2] = {0xff, 0xff};
+ sqlite3_bind_int(pIdxSelect, 1, iSegid);
+ sqlite3_bind_blob(pIdxSelect, 2, aBlob, 2, SQLITE_STATIC);
+ assert( sqlite3_step(pIdxSelect)!=SQLITE_ROW );
+ p->rc = sqlite3_reset(pIdxSelect);
+ }
+ }
+#endif
+ }
+ }
+
+ return iSegid;
+}
+
+/*
+** Discard all data currently cached in the hash-tables.
+*/
+static void fts5IndexDiscardData(Fts5Index *p){
+ assert( p->pHash || p->nPendingData==0 );
+ if( p->pHash ){
+ sqlite3Fts5HashClear(p->pHash);
+ p->nPendingData = 0;
+ }
+}
+
+/*
+** Return the size of the prefix, in bytes, that buffer
+** (pNew/<length-unknown>) shares with buffer (pOld/nOld).
+**
+** Buffer (pNew/<length-unknown>) is guaranteed to be greater
+** than buffer (pOld/nOld).
+*/
+static int fts5PrefixCompress(int nOld, const u8 *pOld, const u8 *pNew){
+ int i;
+ for(i=0; i<nOld; i++){
+ if( pOld[i]!=pNew[i] ) break;
+ }
+ return i;
+}
+
+static void fts5WriteDlidxClear(
+ Fts5Index *p,
+ Fts5SegWriter *pWriter,
+ int bFlush /* If true, write dlidx to disk */
+){
+ int i;
+ assert( bFlush==0 || (pWriter->nDlidx>0 && pWriter->aDlidx[0].buf.n>0) );
+ for(i=0; i<pWriter->nDlidx; i++){
+ Fts5DlidxWriter *pDlidx = &pWriter->aDlidx[i];
+ if( pDlidx->buf.n==0 ) break;
+ if( bFlush ){
+ assert( pDlidx->pgno!=0 );
+ fts5DataWrite(p,
+ FTS5_DLIDX_ROWID(pWriter->iSegid, i, pDlidx->pgno),
+ pDlidx->buf.p, pDlidx->buf.n
+ );
+ }
+ sqlite3Fts5BufferZero(&pDlidx->buf);
+ pDlidx->bPrevValid = 0;
+ }
+}
+
+/*
+** Grow the pWriter->aDlidx[] array to at least nLvl elements in size.
+** Any new array elements are zeroed before returning.
+*/
+static int fts5WriteDlidxGrow(
+ Fts5Index *p,
+ Fts5SegWriter *pWriter,
+ int nLvl
+){
+ if( p->rc==SQLITE_OK && nLvl>=pWriter->nDlidx ){
+ Fts5DlidxWriter *aDlidx = (Fts5DlidxWriter*)sqlite3_realloc(
+ pWriter->aDlidx, sizeof(Fts5DlidxWriter) * nLvl
+ );
+ if( aDlidx==0 ){
+ p->rc = SQLITE_NOMEM;
+ }else{
+ int nByte = sizeof(Fts5DlidxWriter) * (nLvl - pWriter->nDlidx);
+ memset(&aDlidx[pWriter->nDlidx], 0, nByte);
+ pWriter->aDlidx = aDlidx;
+ pWriter->nDlidx = nLvl;
+ }
+ }
+ return p->rc;
+}
+
+/*
+** If the current doclist-index accumulating in pWriter->aDlidx[] is large
+** enough, flush it to disk and return 1. Otherwise discard it and return
+** zero.
+*/
+static int fts5WriteFlushDlidx(Fts5Index *p, Fts5SegWriter *pWriter){
+ int bFlag = 0;
+
+ /* If there were FTS5_MIN_DLIDX_SIZE or more empty leaf pages written
+ ** to the database, also write the doclist-index to disk. */
+ if( pWriter->aDlidx[0].buf.n>0 && pWriter->nEmpty>=FTS5_MIN_DLIDX_SIZE ){
+ bFlag = 1;
+ }
+ fts5WriteDlidxClear(p, pWriter, bFlag);
+ pWriter->nEmpty = 0;
+ return bFlag;
+}
+
+/*
+** This function is called whenever processing of the doclist for the
+** last term on leaf page (pWriter->iBtPage) is completed.
+**
+** The doclist-index for that term is currently stored in-memory within the
+** Fts5SegWriter.aDlidx[] array. If it is large enough, this function
+** writes it out to disk. Or, if it is too small to bother with, discards
+** it.
+**
+** Fts5SegWriter.btterm currently contains the first term on page iBtPage.
+*/
+static void fts5WriteFlushBtree(Fts5Index *p, Fts5SegWriter *pWriter){
+ int bFlag;
+
+ assert( pWriter->iBtPage || pWriter->nEmpty==0 );
+ if( pWriter->iBtPage==0 ) return;
+ bFlag = fts5WriteFlushDlidx(p, pWriter);
+
+ if( p->rc==SQLITE_OK ){
+ const char *z = (pWriter->btterm.n>0?(const char*)pWriter->btterm.p:"");
+ /* The following was already done in fts5WriteInit(): */
+ /* sqlite3_bind_int(p->pIdxWriter, 1, pWriter->iSegid); */
+ sqlite3_bind_blob(p->pIdxWriter, 2, z, pWriter->btterm.n, SQLITE_STATIC);
+ sqlite3_bind_int64(p->pIdxWriter, 3, bFlag + ((i64)pWriter->iBtPage<<1));
+ sqlite3_step(p->pIdxWriter);
+ p->rc = sqlite3_reset(p->pIdxWriter);
+ }
+ pWriter->iBtPage = 0;
+}
+
+/*
+** This is called once for each leaf page except the first that contains
+** at least one term. Argument (nTerm/pTerm) is the split-key - a term that
+** is larger than all terms written to earlier leaves, and equal to or
+** smaller than the first term on the new leaf.
+**
+** If an error occurs, an error code is left in Fts5Index.rc. If an error
+** has already occurred when this function is called, it is a no-op.
+*/
+static void fts5WriteBtreeTerm(
+ Fts5Index *p, /* FTS5 backend object */
+ Fts5SegWriter *pWriter, /* Writer object */
+ int nTerm, const u8 *pTerm /* First term on new page */
+){
+ fts5WriteFlushBtree(p, pWriter);
+ fts5BufferSet(&p->rc, &pWriter->btterm, nTerm, pTerm);
+ pWriter->iBtPage = pWriter->writer.pgno;
+}
+
+/*
+** This function is called when flushing a leaf page that contains no
+** terms at all to disk.
+*/
+static void fts5WriteBtreeNoTerm(
+ Fts5Index *p, /* FTS5 backend object */
+ Fts5SegWriter *pWriter /* Writer object */
+){
+ /* If there were no rowids on the leaf page either and the doclist-index
+ ** has already been started, append an 0x00 byte to it. */
+ if( pWriter->bFirstRowidInPage && pWriter->aDlidx[0].buf.n>0 ){
+ Fts5DlidxWriter *pDlidx = &pWriter->aDlidx[0];
+ assert( pDlidx->bPrevValid );
+ sqlite3Fts5BufferAppendVarint(&p->rc, &pDlidx->buf, 0);
+ }
+
+ /* Increment the "number of sequential leaves without a term" counter. */
+ pWriter->nEmpty++;
+}
+
+static i64 fts5DlidxExtractFirstRowid(Fts5Buffer *pBuf){
+ i64 iRowid;
+ int iOff;
+
+ iOff = 1 + fts5GetVarint(&pBuf->p[1], (u64*)&iRowid);
+ fts5GetVarint(&pBuf->p[iOff], (u64*)&iRowid);
+ return iRowid;
+}
+
+/*
+** Rowid iRowid has just been appended to the current leaf page. It is the
+** first on the page. This function appends an appropriate entry to the current
+** doclist-index.
+*/
+static void fts5WriteDlidxAppend(
+ Fts5Index *p,
+ Fts5SegWriter *pWriter,
+ i64 iRowid
+){
+ int i;
+ int bDone = 0;
+
+ for(i=0; p->rc==SQLITE_OK && bDone==0; i++){
+ i64 iVal;
+ Fts5DlidxWriter *pDlidx = &pWriter->aDlidx[i];
+
+ if( pDlidx->buf.n>=p->pConfig->pgsz ){
+ /* The current doclist-index page is full. Write it to disk and push
+ ** a copy of iRowid (which will become the first rowid on the next
+ ** doclist-index leaf page) up into the next level of the b-tree
+ ** hierarchy. If the node being flushed is currently the root node,
+ ** also push its first rowid upwards. */
+ pDlidx->buf.p[0] = 0x01; /* Not the root node */
+ fts5DataWrite(p,
+ FTS5_DLIDX_ROWID(pWriter->iSegid, i, pDlidx->pgno),
+ pDlidx->buf.p, pDlidx->buf.n
+ );
+ fts5WriteDlidxGrow(p, pWriter, i+2);
+ pDlidx = &pWriter->aDlidx[i];
+ if( p->rc==SQLITE_OK && pDlidx[1].buf.n==0 ){
+ i64 iFirst = fts5DlidxExtractFirstRowid(&pDlidx->buf);
+
+ /* This was the root node. Push its first rowid up to the new root. */
+ pDlidx[1].pgno = pDlidx->pgno;
+ sqlite3Fts5BufferAppendVarint(&p->rc, &pDlidx[1].buf, 0);
+ sqlite3Fts5BufferAppendVarint(&p->rc, &pDlidx[1].buf, pDlidx->pgno);
+ sqlite3Fts5BufferAppendVarint(&p->rc, &pDlidx[1].buf, iFirst);
+ pDlidx[1].bPrevValid = 1;
+ pDlidx[1].iPrev = iFirst;
+ }
+
+ sqlite3Fts5BufferZero(&pDlidx->buf);
+ pDlidx->bPrevValid = 0;
+ pDlidx->pgno++;
+ }else{
+ bDone = 1;
+ }
+
+ if( pDlidx->bPrevValid ){
+ iVal = iRowid - pDlidx->iPrev;
+ }else{
+ i64 iPgno = (i==0 ? pWriter->writer.pgno : pDlidx[-1].pgno);
+ assert( pDlidx->buf.n==0 );
+ sqlite3Fts5BufferAppendVarint(&p->rc, &pDlidx->buf, !bDone);
+ sqlite3Fts5BufferAppendVarint(&p->rc, &pDlidx->buf, iPgno);
+ iVal = iRowid;
+ }
+
+ sqlite3Fts5BufferAppendVarint(&p->rc, &pDlidx->buf, iVal);
+ pDlidx->bPrevValid = 1;
+ pDlidx->iPrev = iRowid;
+ }
+}
+
+static void fts5WriteFlushLeaf(Fts5Index *p, Fts5SegWriter *pWriter){
+ static const u8 zero[] = { 0x00, 0x00, 0x00, 0x00 };
+ Fts5PageWriter *pPage = &pWriter->writer;
+ i64 iRowid;
+
+static int nCall = 0;
+nCall++;
+
+ assert( (pPage->pgidx.n==0)==(pWriter->bFirstTermInPage) );
+
+ /* Set the szLeaf header field. */
+ assert( 0==fts5GetU16(&pPage->buf.p[2]) );
+ fts5PutU16(&pPage->buf.p[2], (u16)pPage->buf.n);
+
+ if( pWriter->bFirstTermInPage ){
+ /* No term was written to this page. */
+ assert( pPage->pgidx.n==0 );
+ fts5WriteBtreeNoTerm(p, pWriter);
+ }else{
+ /* Append the pgidx to the page buffer. Set the szLeaf header field. */
+ fts5BufferAppendBlob(&p->rc, &pPage->buf, pPage->pgidx.n, pPage->pgidx.p);
+ }
+
+ /* Write the page out to disk */
+ iRowid = FTS5_SEGMENT_ROWID(pWriter->iSegid, pPage->pgno);
+ fts5DataWrite(p, iRowid, pPage->buf.p, pPage->buf.n);
+
+ /* Initialize the next page. */
+ fts5BufferZero(&pPage->buf);
+ fts5BufferZero(&pPage->pgidx);
+ fts5BufferAppendBlob(&p->rc, &pPage->buf, 4, zero);
+ pPage->iPrevPgidx = 0;
+ pPage->pgno++;
+
+ /* Increase the leaves written counter */
+ pWriter->nLeafWritten++;
+
+ /* The new leaf holds no terms or rowids */
+ pWriter->bFirstTermInPage = 1;
+ pWriter->bFirstRowidInPage = 1;
+}
+
+/*
+** Append term pTerm/nTerm to the segment being written by the writer passed
+** as the second argument.
+**
+** If an error occurs, set the Fts5Index.rc error code. If an error has
+** already occurred, this function is a no-op.
+*/
+static void fts5WriteAppendTerm(
+ Fts5Index *p,
+ Fts5SegWriter *pWriter,
+ int nTerm, const u8 *pTerm
+){
+ int nPrefix; /* Bytes of prefix compression for term */
+ Fts5PageWriter *pPage = &pWriter->writer;
+ Fts5Buffer *pPgidx = &pWriter->writer.pgidx;
+
+ assert( p->rc==SQLITE_OK );
+ assert( pPage->buf.n>=4 );
+ assert( pPage->buf.n>4 || pWriter->bFirstTermInPage );
+
+ /* If the current leaf page is full, flush it to disk. */
+ if( (pPage->buf.n + pPgidx->n + nTerm + 2)>=p->pConfig->pgsz ){
+ if( pPage->buf.n>4 ){
+ fts5WriteFlushLeaf(p, pWriter);
+ }
+ fts5BufferGrow(&p->rc, &pPage->buf, nTerm+FTS5_DATA_PADDING);
+ }
+
+ /* TODO1: Updating pgidx here. */
+ pPgidx->n += sqlite3Fts5PutVarint(
+ &pPgidx->p[pPgidx->n], pPage->buf.n - pPage->iPrevPgidx
+ );
+ pPage->iPrevPgidx = pPage->buf.n;
+#if 0
+ fts5PutU16(&pPgidx->p[pPgidx->n], pPage->buf.n);
+ pPgidx->n += 2;
+#endif
+
+ if( pWriter->bFirstTermInPage ){
+ nPrefix = 0;
+ if( pPage->pgno!=1 ){
+ /* This is the first term on a leaf that is not the leftmost leaf in
+ ** the segment b-tree. In this case it is necessary to add a term to
+ ** the b-tree hierarchy that is (a) larger than the largest term
+ ** already written to the segment and (b) smaller than or equal to
+ ** this term. In other words, a prefix of (pTerm/nTerm) that is one
+ ** byte longer than the longest prefix (pTerm/nTerm) shares with the
+ ** previous term.
+ **
+ ** Usually, the previous term is available in pPage->term. The exception
+ ** is if this is the first term written in an incremental-merge step.
+ ** In this case the previous term is not available, so just write a
+ ** copy of (pTerm/nTerm) into the parent node. This is slightly
+ ** inefficient, but still correct. */
+ int n = nTerm;
+ if( pPage->term.n ){
+ n = 1 + fts5PrefixCompress(pPage->term.n, pPage->term.p, pTerm);
+ }
+ fts5WriteBtreeTerm(p, pWriter, n, pTerm);
+ pPage = &pWriter->writer;
+ }
+ }else{
+ nPrefix = fts5PrefixCompress(pPage->term.n, pPage->term.p, pTerm);
+ fts5BufferAppendVarint(&p->rc, &pPage->buf, nPrefix);
+ }
+
+ /* Append the number of bytes of new data, then the term data itself
+ ** to the page. */
+ fts5BufferAppendVarint(&p->rc, &pPage->buf, nTerm - nPrefix);
+ fts5BufferAppendBlob(&p->rc, &pPage->buf, nTerm - nPrefix, &pTerm[nPrefix]);
+
+ /* Update the Fts5PageWriter.term field. */
+ fts5BufferSet(&p->rc, &pPage->term, nTerm, pTerm);
+ pWriter->bFirstTermInPage = 0;
+
+ pWriter->bFirstRowidInPage = 0;
+ pWriter->bFirstRowidInDoclist = 1;
+
+ assert( p->rc || (pWriter->nDlidx>0 && pWriter->aDlidx[0].buf.n==0) );
+ pWriter->aDlidx[0].pgno = pPage->pgno;
+}
+
+/*
+** Append a rowid and position-list size field to the writers output.
+*/
+static void fts5WriteAppendRowid(
+ Fts5Index *p,
+ Fts5SegWriter *pWriter,
+ i64 iRowid
+){
+ if( p->rc==SQLITE_OK ){
+ Fts5PageWriter *pPage = &pWriter->writer;
+
+ if( (pPage->buf.n + pPage->pgidx.n)>=p->pConfig->pgsz ){
+ fts5WriteFlushLeaf(p, pWriter);
+ }
+
+ /* If this is to be the first rowid written to the page, set the
+ ** rowid-pointer in the page-header. Also append a value to the dlidx
+ ** buffer, in case a doclist-index is required. */
+ if( pWriter->bFirstRowidInPage ){
+ fts5PutU16(pPage->buf.p, (u16)pPage->buf.n);
+ fts5WriteDlidxAppend(p, pWriter, iRowid);
+ }
+
+ /* Write the rowid. */
+ if( pWriter->bFirstRowidInDoclist || pWriter->bFirstRowidInPage ){
+ fts5BufferAppendVarint(&p->rc, &pPage->buf, iRowid);
+ }else{
+ assert( p->rc || iRowid>pWriter->iPrevRowid );
+ fts5BufferAppendVarint(&p->rc, &pPage->buf, iRowid - pWriter->iPrevRowid);
+ }
+ pWriter->iPrevRowid = iRowid;
+ pWriter->bFirstRowidInDoclist = 0;
+ pWriter->bFirstRowidInPage = 0;
+ }
+}
+
+static void fts5WriteAppendPoslistData(
+ Fts5Index *p,
+ Fts5SegWriter *pWriter,
+ const u8 *aData,
+ int nData
+){
+ Fts5PageWriter *pPage = &pWriter->writer;
+ const u8 *a = aData;
+ int n = nData;
+
+ assert( p->pConfig->pgsz>0 );
+ while( p->rc==SQLITE_OK
+ && (pPage->buf.n + pPage->pgidx.n + n)>=p->pConfig->pgsz
+ ){
+ int nReq = p->pConfig->pgsz - pPage->buf.n - pPage->pgidx.n;
+ int nCopy = 0;
+ while( nCopy<nReq ){
+ i64 dummy;
+ nCopy += fts5GetVarint(&a[nCopy], (u64*)&dummy);
+ }
+ fts5BufferAppendBlob(&p->rc, &pPage->buf, nCopy, a);
+ a += nCopy;
+ n -= nCopy;
+ fts5WriteFlushLeaf(p, pWriter);
+ }
+ if( n>0 ){
+ fts5BufferAppendBlob(&p->rc, &pPage->buf, n, a);
+ }
+}
+
+/*
+** Flush any data cached by the writer object to the database. Free any
+** allocations associated with the writer.
+*/
+static void fts5WriteFinish(
+ Fts5Index *p,
+ Fts5SegWriter *pWriter, /* Writer object */
+ int *pnLeaf /* OUT: Number of leaf pages in b-tree */
+){
+ int i;
+ Fts5PageWriter *pLeaf = &pWriter->writer;
+ if( p->rc==SQLITE_OK ){
+ assert( pLeaf->pgno>=1 );
+ if( pLeaf->buf.n>4 ){
+ fts5WriteFlushLeaf(p, pWriter);
+ }
+ *pnLeaf = pLeaf->pgno-1;
+ if( pLeaf->pgno>1 ){
+ fts5WriteFlushBtree(p, pWriter);
+ }
+ }
+ fts5BufferFree(&pLeaf->term);
+ fts5BufferFree(&pLeaf->buf);
+ fts5BufferFree(&pLeaf->pgidx);
+ fts5BufferFree(&pWriter->btterm);
+
+ for(i=0; i<pWriter->nDlidx; i++){
+ sqlite3Fts5BufferFree(&pWriter->aDlidx[i].buf);
+ }
+ sqlite3_free(pWriter->aDlidx);
+}
+
+static void fts5WriteInit(
+ Fts5Index *p,
+ Fts5SegWriter *pWriter,
+ int iSegid
+){
+ const int nBuffer = p->pConfig->pgsz + FTS5_DATA_PADDING;
+
+ memset(pWriter, 0, sizeof(Fts5SegWriter));
+ pWriter->iSegid = iSegid;
+
+ fts5WriteDlidxGrow(p, pWriter, 1);
+ pWriter->writer.pgno = 1;
+ pWriter->bFirstTermInPage = 1;
+ pWriter->iBtPage = 1;
+
+ assert( pWriter->writer.buf.n==0 );
+ assert( pWriter->writer.pgidx.n==0 );
+
+ /* Grow the two buffers to pgsz + padding bytes in size. */
+ sqlite3Fts5BufferSize(&p->rc, &pWriter->writer.pgidx, nBuffer);
+ sqlite3Fts5BufferSize(&p->rc, &pWriter->writer.buf, nBuffer);
+
+ if( p->pIdxWriter==0 ){
+ Fts5Config *pConfig = p->pConfig;
+ fts5IndexPrepareStmt(p, &p->pIdxWriter, sqlite3_mprintf(
+ "INSERT INTO '%q'.'%q_idx'(segid,term,pgno) VALUES(?,?,?)",
+ pConfig->zDb, pConfig->zName
+ ));
+ }
+
+ if( p->rc==SQLITE_OK ){
+ /* Initialize the 4-byte leaf-page header to 0x00. */
+ memset(pWriter->writer.buf.p, 0, 4);
+ pWriter->writer.buf.n = 4;
+
+ /* Bind the current output segment id to the index-writer. This is an
+ ** optimization over binding the same value over and over as rows are
+ ** inserted into %_idx by the current writer. */
+ sqlite3_bind_int(p->pIdxWriter, 1, pWriter->iSegid);
+ }
+}
+
+/*
+** Iterator pIter was used to iterate through the input segments of on an
+** incremental merge operation. This function is called if the incremental
+** merge step has finished but the input has not been completely exhausted.
+*/
+static void fts5TrimSegments(Fts5Index *p, Fts5Iter *pIter){
+ int i;
+ Fts5Buffer buf;
+ memset(&buf, 0, sizeof(Fts5Buffer));
+ for(i=0; i<pIter->nSeg; i++){
+ Fts5SegIter *pSeg = &pIter->aSeg[i];
+ if( pSeg->pSeg==0 ){
+ /* no-op */
+ }else if( pSeg->pLeaf==0 ){
+ /* All keys from this input segment have been transfered to the output.
+ ** Set both the first and last page-numbers to 0 to indicate that the
+ ** segment is now empty. */
+ pSeg->pSeg->pgnoLast = 0;
+ pSeg->pSeg->pgnoFirst = 0;
+ }else{
+ int iOff = pSeg->iTermLeafOffset; /* Offset on new first leaf page */
+ i64 iLeafRowid;
+ Fts5Data *pData;
+ int iId = pSeg->pSeg->iSegid;
+ u8 aHdr[4] = {0x00, 0x00, 0x00, 0x00};
+
+ iLeafRowid = FTS5_SEGMENT_ROWID(iId, pSeg->iTermLeafPgno);
+ pData = fts5DataRead(p, iLeafRowid);
+ if( pData ){
+ fts5BufferZero(&buf);
+ fts5BufferGrow(&p->rc, &buf, pData->nn);
+ fts5BufferAppendBlob(&p->rc, &buf, sizeof(aHdr), aHdr);
+ fts5BufferAppendVarint(&p->rc, &buf, pSeg->term.n);
+ fts5BufferAppendBlob(&p->rc, &buf, pSeg->term.n, pSeg->term.p);
+ fts5BufferAppendBlob(&p->rc, &buf, pData->szLeaf-iOff, &pData->p[iOff]);
+ if( p->rc==SQLITE_OK ){
+ /* Set the szLeaf field */
+ fts5PutU16(&buf.p[2], (u16)buf.n);
+ }
+
+ /* Set up the new page-index array */
+ fts5BufferAppendVarint(&p->rc, &buf, 4);
+ if( pSeg->iLeafPgno==pSeg->iTermLeafPgno
+ && pSeg->iEndofDoclist<pData->szLeaf
+ ){
+ int nDiff = pData->szLeaf - pSeg->iEndofDoclist;
+ fts5BufferAppendVarint(&p->rc, &buf, buf.n - 1 - nDiff - 4);
+ fts5BufferAppendBlob(&p->rc, &buf,
+ pData->nn - pSeg->iPgidxOff, &pData->p[pSeg->iPgidxOff]
+ );
+ }
+
+ fts5DataRelease(pData);
+ pSeg->pSeg->pgnoFirst = pSeg->iTermLeafPgno;
+ fts5DataDelete(p, FTS5_SEGMENT_ROWID(iId, 1), iLeafRowid);
+ fts5DataWrite(p, iLeafRowid, buf.p, buf.n);
+ }
+ }
+ }
+ fts5BufferFree(&buf);
+}
+
+static void fts5MergeChunkCallback(
+ Fts5Index *p,
+ void *pCtx,
+ const u8 *pChunk, int nChunk
+){
+ Fts5SegWriter *pWriter = (Fts5SegWriter*)pCtx;
+ fts5WriteAppendPoslistData(p, pWriter, pChunk, nChunk);
+}
+
+/*
+**
+*/
+static void fts5IndexMergeLevel(
+ Fts5Index *p, /* FTS5 backend object */
+ Fts5Structure **ppStruct, /* IN/OUT: Stucture of index */
+ int iLvl, /* Level to read input from */
+ int *pnRem /* Write up to this many output leaves */
+){
+ Fts5Structure *pStruct = *ppStruct;
+ Fts5StructureLevel *pLvl = &pStruct->aLevel[iLvl];
+ Fts5StructureLevel *pLvlOut;
+ Fts5Iter *pIter = 0; /* Iterator to read input data */
+ int nRem = pnRem ? *pnRem : 0; /* Output leaf pages left to write */
+ int nInput; /* Number of input segments */
+ Fts5SegWriter writer; /* Writer object */
+ Fts5StructureSegment *pSeg; /* Output segment */
+ Fts5Buffer term;
+ int bOldest; /* True if the output segment is the oldest */
+ int eDetail = p->pConfig->eDetail;
+ const int flags = FTS5INDEX_QUERY_NOOUTPUT;
+
+ assert( iLvl<pStruct->nLevel );
+ assert( pLvl->nMerge<=pLvl->nSeg );
+
+ memset(&writer, 0, sizeof(Fts5SegWriter));
+ memset(&term, 0, sizeof(Fts5Buffer));
+ if( pLvl->nMerge ){
+ pLvlOut = &pStruct->aLevel[iLvl+1];
+ assert( pLvlOut->nSeg>0 );
+ nInput = pLvl->nMerge;
+ pSeg = &pLvlOut->aSeg[pLvlOut->nSeg-1];
+
+ fts5WriteInit(p, &writer, pSeg->iSegid);
+ writer.writer.pgno = pSeg->pgnoLast+1;
+ writer.iBtPage = 0;
+ }else{
+ int iSegid = fts5AllocateSegid(p, pStruct);
+
+ /* Extend the Fts5Structure object as required to ensure the output
+ ** segment exists. */
+ if( iLvl==pStruct->nLevel-1 ){
+ fts5StructureAddLevel(&p->rc, ppStruct);
+ pStruct = *ppStruct;
+ }
+ fts5StructureExtendLevel(&p->rc, pStruct, iLvl+1, 1, 0);
+ if( p->rc ) return;
+ pLvl = &pStruct->aLevel[iLvl];
+ pLvlOut = &pStruct->aLevel[iLvl+1];
+
+ fts5WriteInit(p, &writer, iSegid);
+
+ /* Add the new segment to the output level */
+ pSeg = &pLvlOut->aSeg[pLvlOut->nSeg];
+ pLvlOut->nSeg++;
+ pSeg->pgnoFirst = 1;
+ pSeg->iSegid = iSegid;
+ pStruct->nSegment++;
+
+ /* Read input from all segments in the input level */
+ nInput = pLvl->nSeg;
+ }
+ bOldest = (pLvlOut->nSeg==1 && pStruct->nLevel==iLvl+2);
+
+ assert( iLvl>=0 );
+ for(fts5MultiIterNew(p, pStruct, flags, 0, 0, 0, iLvl, nInput, &pIter);
+ fts5MultiIterEof(p, pIter)==0;
+ fts5MultiIterNext(p, pIter, 0, 0)
+ ){
+ Fts5SegIter *pSegIter = &pIter->aSeg[ pIter->aFirst[1].iFirst ];
+ int nPos; /* position-list size field value */
+ int nTerm;
+ const u8 *pTerm;
+
+ /* Check for key annihilation. */
+ if( pSegIter->nPos==0 && (bOldest || pSegIter->bDel==0) ) continue;
+
+ pTerm = fts5MultiIterTerm(pIter, &nTerm);
+ if( nTerm!=term.n || memcmp(pTerm, term.p, nTerm) ){
+ if( pnRem && writer.nLeafWritten>nRem ){
+ break;
+ }
+
+ /* This is a new term. Append a term to the output segment. */
+ fts5WriteAppendTerm(p, &writer, nTerm, pTerm);
+ fts5BufferSet(&p->rc, &term, nTerm, pTerm);
+ }
+
+ /* Append the rowid to the output */
+ /* WRITEPOSLISTSIZE */
+ fts5WriteAppendRowid(p, &writer, fts5MultiIterRowid(pIter));
+
+ if( eDetail==FTS5_DETAIL_NONE ){
+ if( pSegIter->bDel ){
+ fts5BufferAppendVarint(&p->rc, &writer.writer.buf, 0);
+ if( pSegIter->nPos>0 ){
+ fts5BufferAppendVarint(&p->rc, &writer.writer.buf, 0);
+ }
+ }
+ }else{
+ /* Append the position-list data to the output */
+ nPos = pSegIter->nPos*2 + pSegIter->bDel;
+ fts5BufferAppendVarint(&p->rc, &writer.writer.buf, nPos);
+ fts5ChunkIterate(p, pSegIter, (void*)&writer, fts5MergeChunkCallback);
+ }
+ }
+
+ /* Flush the last leaf page to disk. Set the output segment b-tree height
+ ** and last leaf page number at the same time. */
+ fts5WriteFinish(p, &writer, &pSeg->pgnoLast);
+
+ if( fts5MultiIterEof(p, pIter) ){
+ int i;
+
+ /* Remove the redundant segments from the %_data table */
+ for(i=0; i<nInput; i++){
+ fts5DataRemoveSegment(p, pLvl->aSeg[i].iSegid);
+ }
+
+ /* Remove the redundant segments from the input level */
+ if( pLvl->nSeg!=nInput ){
+ int nMove = (pLvl->nSeg - nInput) * sizeof(Fts5StructureSegment);
+ memmove(pLvl->aSeg, &pLvl->aSeg[nInput], nMove);
+ }
+ pStruct->nSegment -= nInput;
+ pLvl->nSeg -= nInput;
+ pLvl->nMerge = 0;
+ if( pSeg->pgnoLast==0 ){
+ pLvlOut->nSeg--;
+ pStruct->nSegment--;
+ }
+ }else{
+ assert( pSeg->pgnoLast>0 );
+ fts5TrimSegments(p, pIter);
+ pLvl->nMerge = nInput;
+ }
+
+ fts5MultiIterFree(pIter);
+ fts5BufferFree(&term);
+ if( pnRem ) *pnRem -= writer.nLeafWritten;
+}
+
+/*
+** Do up to nPg pages of automerge work on the index.
+**
+** Return true if any changes were actually made, or false otherwise.
+*/
+static int fts5IndexMerge(
+ Fts5Index *p, /* FTS5 backend object */
+ Fts5Structure **ppStruct, /* IN/OUT: Current structure of index */
+ int nPg, /* Pages of work to do */
+ int nMin /* Minimum number of segments to merge */
+){
+ int nRem = nPg;
+ int bRet = 0;
+ Fts5Structure *pStruct = *ppStruct;
+ while( nRem>0 && p->rc==SQLITE_OK ){
+ int iLvl; /* To iterate through levels */
+ int iBestLvl = 0; /* Level offering the most input segments */
+ int nBest = 0; /* Number of input segments on best level */
+
+ /* Set iBestLvl to the level to read input segments from. */
+ assert( pStruct->nLevel>0 );
+ for(iLvl=0; iLvl<pStruct->nLevel; iLvl++){
+ Fts5StructureLevel *pLvl = &pStruct->aLevel[iLvl];
+ if( pLvl->nMerge ){
+ if( pLvl->nMerge>nBest ){
+ iBestLvl = iLvl;
+ nBest = pLvl->nMerge;
+ }
+ break;
+ }
+ if( pLvl->nSeg>nBest ){
+ nBest = pLvl->nSeg;
+ iBestLvl = iLvl;
+ }
+ }
+
+ /* If nBest is still 0, then the index must be empty. */
+#ifdef SQLITE_DEBUG
+ for(iLvl=0; nBest==0 && iLvl<pStruct->nLevel; iLvl++){
+ assert( pStruct->aLevel[iLvl].nSeg==0 );
+ }
+#endif
+
+ if( nBest<nMin && pStruct->aLevel[iBestLvl].nMerge==0 ){
+ break;
+ }
+ bRet = 1;
+ fts5IndexMergeLevel(p, &pStruct, iBestLvl, &nRem);
+ if( p->rc==SQLITE_OK && pStruct->aLevel[iBestLvl].nMerge==0 ){
+ fts5StructurePromote(p, iBestLvl+1, pStruct);
+ }
+ }
+ *ppStruct = pStruct;
+ return bRet;
+}
+
+/*
+** A total of nLeaf leaf pages of data has just been flushed to a level-0
+** segment. This function updates the write-counter accordingly and, if
+** necessary, performs incremental merge work.
+**
+** If an error occurs, set the Fts5Index.rc error code. If an error has
+** already occurred, this function is a no-op.
+*/
+static void fts5IndexAutomerge(
+ Fts5Index *p, /* FTS5 backend object */
+ Fts5Structure **ppStruct, /* IN/OUT: Current structure of index */
+ int nLeaf /* Number of output leaves just written */
+){
+ if( p->rc==SQLITE_OK && p->pConfig->nAutomerge>0 ){
+ Fts5Structure *pStruct = *ppStruct;
+ u64 nWrite; /* Initial value of write-counter */
+ int nWork; /* Number of work-quanta to perform */
+ int nRem; /* Number of leaf pages left to write */
+
+ /* Update the write-counter. While doing so, set nWork. */
+ nWrite = pStruct->nWriteCounter;
+ nWork = (int)(((nWrite + nLeaf) / p->nWorkUnit) - (nWrite / p->nWorkUnit));
+ pStruct->nWriteCounter += nLeaf;
+ nRem = (int)(p->nWorkUnit * nWork * pStruct->nLevel);
+
+ fts5IndexMerge(p, ppStruct, nRem, p->pConfig->nAutomerge);
+ }
+}
+
+static void fts5IndexCrisismerge(
+ Fts5Index *p, /* FTS5 backend object */
+ Fts5Structure **ppStruct /* IN/OUT: Current structure of index */
+){
+ const int nCrisis = p->pConfig->nCrisisMerge;
+ Fts5Structure *pStruct = *ppStruct;
+ int iLvl = 0;
+
+ assert( p->rc!=SQLITE_OK || pStruct->nLevel>0 );
+ while( p->rc==SQLITE_OK && pStruct->aLevel[iLvl].nSeg>=nCrisis ){
+ fts5IndexMergeLevel(p, &pStruct, iLvl, 0);
+ assert( p->rc!=SQLITE_OK || pStruct->nLevel>(iLvl+1) );
+ fts5StructurePromote(p, iLvl+1, pStruct);
+ iLvl++;
+ }
+ *ppStruct = pStruct;
+}
+
+static int fts5IndexReturn(Fts5Index *p){
+ int rc = p->rc;
+ p->rc = SQLITE_OK;
+ return rc;
+}
+
+typedef struct Fts5FlushCtx Fts5FlushCtx;
+struct Fts5FlushCtx {
+ Fts5Index *pIdx;
+ Fts5SegWriter writer;
+};
+
+/*
+** Buffer aBuf[] contains a list of varints, all small enough to fit
+** in a 32-bit integer. Return the size of the largest prefix of this
+** list nMax bytes or less in size.
+*/
+static int fts5PoslistPrefix(const u8 *aBuf, int nMax){
+ int ret;
+ u32 dummy;
+ ret = fts5GetVarint32(aBuf, dummy);
+ if( ret<nMax ){
+ while( 1 ){
+ int i = fts5GetVarint32(&aBuf[ret], dummy);
+ if( (ret + i) > nMax ) break;
+ ret += i;
+ }
+ }
+ return ret;
+}
+
+/*
+** Flush the contents of in-memory hash table iHash to a new level-0
+** segment on disk. Also update the corresponding structure record.
+**
+** If an error occurs, set the Fts5Index.rc error code. If an error has
+** already occurred, this function is a no-op.
+*/
+static void fts5FlushOneHash(Fts5Index *p){
+ Fts5Hash *pHash = p->pHash;
+ Fts5Structure *pStruct;
+ int iSegid;
+ int pgnoLast = 0; /* Last leaf page number in segment */
+
+ /* Obtain a reference to the index structure and allocate a new segment-id
+ ** for the new level-0 segment. */
+ pStruct = fts5StructureRead(p);
+ iSegid = fts5AllocateSegid(p, pStruct);
+ fts5StructureInvalidate(p);
+
+ if( iSegid ){
+ const int pgsz = p->pConfig->pgsz;
+ int eDetail = p->pConfig->eDetail;
+ Fts5StructureSegment *pSeg; /* New segment within pStruct */
+ Fts5Buffer *pBuf; /* Buffer in which to assemble leaf page */
+ Fts5Buffer *pPgidx; /* Buffer in which to assemble pgidx */
+
+ Fts5SegWriter writer;
+ fts5WriteInit(p, &writer, iSegid);
+
+ pBuf = &writer.writer.buf;
+ pPgidx = &writer.writer.pgidx;
+
+ /* fts5WriteInit() should have initialized the buffers to (most likely)
+ ** the maximum space required. */
+ assert( p->rc || pBuf->nSpace>=(pgsz + FTS5_DATA_PADDING) );
+ assert( p->rc || pPgidx->nSpace>=(pgsz + FTS5_DATA_PADDING) );
+
+ /* Begin scanning through hash table entries. This loop runs once for each
+ ** term/doclist currently stored within the hash table. */
+ if( p->rc==SQLITE_OK ){
+ p->rc = sqlite3Fts5HashScanInit(pHash, 0, 0);
+ }
+ while( p->rc==SQLITE_OK && 0==sqlite3Fts5HashScanEof(pHash) ){
+ const char *zTerm; /* Buffer containing term */
+ const u8 *pDoclist; /* Pointer to doclist for this term */
+ int nDoclist; /* Size of doclist in bytes */
+
+ /* Write the term for this entry to disk. */
+ sqlite3Fts5HashScanEntry(pHash, &zTerm, &pDoclist, &nDoclist);
+ fts5WriteAppendTerm(p, &writer, (int)strlen(zTerm), (const u8*)zTerm);
+
+ assert( writer.bFirstRowidInPage==0 );
+ if( pgsz>=(pBuf->n + pPgidx->n + nDoclist + 1) ){
+ /* The entire doclist will fit on the current leaf. */
+ fts5BufferSafeAppendBlob(pBuf, pDoclist, nDoclist);
+ }else{
+ i64 iRowid = 0;
+ i64 iDelta = 0;
+ int iOff = 0;
+
+ /* The entire doclist will not fit on this leaf. The following
+ ** loop iterates through the poslists that make up the current
+ ** doclist. */
+ while( p->rc==SQLITE_OK && iOff<nDoclist ){
+ iOff += fts5GetVarint(&pDoclist[iOff], (u64*)&iDelta);
+ iRowid += iDelta;
+
+ if( writer.bFirstRowidInPage ){
+ fts5PutU16(&pBuf->p[0], (u16)pBuf->n); /* first rowid on page */
+ pBuf->n += sqlite3Fts5PutVarint(&pBuf->p[pBuf->n], iRowid);
+ writer.bFirstRowidInPage = 0;
+ fts5WriteDlidxAppend(p, &writer, iRowid);
+ }else{
+ pBuf->n += sqlite3Fts5PutVarint(&pBuf->p[pBuf->n], iDelta);
+ }
+ assert( pBuf->n<=pBuf->nSpace );
+
+ if( eDetail==FTS5_DETAIL_NONE ){
+ if( iOff<nDoclist && pDoclist[iOff]==0 ){
+ pBuf->p[pBuf->n++] = 0;
+ iOff++;
+ if( iOff<nDoclist && pDoclist[iOff]==0 ){
+ pBuf->p[pBuf->n++] = 0;
+ iOff++;
+ }
+ }
+ if( (pBuf->n + pPgidx->n)>=pgsz ){
+ fts5WriteFlushLeaf(p, &writer);
+ }
+ }else{
+ int bDummy;
+ int nPos;
+ int nCopy = fts5GetPoslistSize(&pDoclist[iOff], &nPos, &bDummy);
+ nCopy += nPos;
+ if( (pBuf->n + pPgidx->n + nCopy) <= pgsz ){
+ /* The entire poslist will fit on the current leaf. So copy
+ ** it in one go. */
+ fts5BufferSafeAppendBlob(pBuf, &pDoclist[iOff], nCopy);
+ }else{
+ /* The entire poslist will not fit on this leaf. So it needs
+ ** to be broken into sections. The only qualification being
+ ** that each varint must be stored contiguously. */
+ const u8 *pPoslist = &pDoclist[iOff];
+ int iPos = 0;
+ while( p->rc==SQLITE_OK ){
+ int nSpace = pgsz - pBuf->n - pPgidx->n;
+ int n = 0;
+ if( (nCopy - iPos)<=nSpace ){
+ n = nCopy - iPos;
+ }else{
+ n = fts5PoslistPrefix(&pPoslist[iPos], nSpace);
+ }
+ assert( n>0 );
+ fts5BufferSafeAppendBlob(pBuf, &pPoslist[iPos], n);
+ iPos += n;
+ if( (pBuf->n + pPgidx->n)>=pgsz ){
+ fts5WriteFlushLeaf(p, &writer);
+ }
+ if( iPos>=nCopy ) break;
+ }
+ }
+ iOff += nCopy;
+ }
+ }
+ }
+
+ /* TODO2: Doclist terminator written here. */
+ /* pBuf->p[pBuf->n++] = '\0'; */
+ assert( pBuf->n<=pBuf->nSpace );
+ sqlite3Fts5HashScanNext(pHash);
+ }
+ sqlite3Fts5HashClear(pHash);
+ fts5WriteFinish(p, &writer, &pgnoLast);
+
+ /* Update the Fts5Structure. It is written back to the database by the
+ ** fts5StructureRelease() call below. */
+ if( pStruct->nLevel==0 ){
+ fts5StructureAddLevel(&p->rc, &pStruct);
+ }
+ fts5StructureExtendLevel(&p->rc, pStruct, 0, 1, 0);
+ if( p->rc==SQLITE_OK ){
+ pSeg = &pStruct->aLevel[0].aSeg[ pStruct->aLevel[0].nSeg++ ];
+ pSeg->iSegid = iSegid;
+ pSeg->pgnoFirst = 1;
+ pSeg->pgnoLast = pgnoLast;
+ pStruct->nSegment++;
+ }
+ fts5StructurePromote(p, 0, pStruct);
+ }
+
+ fts5IndexAutomerge(p, &pStruct, pgnoLast);
+ fts5IndexCrisismerge(p, &pStruct);
+ fts5StructureWrite(p, pStruct);
+ fts5StructureRelease(pStruct);
+}
+
+/*
+** Flush any data stored in the in-memory hash tables to the database.
+*/
+static void fts5IndexFlush(Fts5Index *p){
+ /* Unless it is empty, flush the hash table to disk */
+ if( p->nPendingData ){
+ assert( p->pHash );
+ p->nPendingData = 0;
+ fts5FlushOneHash(p);
+ }
+}
+
+static Fts5Structure *fts5IndexOptimizeStruct(
+ Fts5Index *p,
+ Fts5Structure *pStruct
+){
+ Fts5Structure *pNew = 0;
+ int nByte = sizeof(Fts5Structure);
+ int nSeg = pStruct->nSegment;
+ int i;
+
+ /* Figure out if this structure requires optimization. A structure does
+ ** not require optimization if either:
+ **
+ ** + it consists of fewer than two segments, or
+ ** + all segments are on the same level, or
+ ** + all segments except one are currently inputs to a merge operation.
+ **
+ ** In the first case, return NULL. In the second, increment the ref-count
+ ** on *pStruct and return a copy of the pointer to it.
+ */
+ if( nSeg<2 ) return 0;
+ for(i=0; i<pStruct->nLevel; i++){
+ int nThis = pStruct->aLevel[i].nSeg;
+ if( nThis==nSeg || (nThis==nSeg-1 && pStruct->aLevel[i].nMerge==nThis) ){
+ fts5StructureRef(pStruct);
+ return pStruct;
+ }
+ assert( pStruct->aLevel[i].nMerge<=nThis );
+ }
+
+ nByte += (pStruct->nLevel+1) * sizeof(Fts5StructureLevel);
+ pNew = (Fts5Structure*)sqlite3Fts5MallocZero(&p->rc, nByte);
+
+ if( pNew ){
+ Fts5StructureLevel *pLvl;
+ nByte = nSeg * sizeof(Fts5StructureSegment);
+ pNew->nLevel = pStruct->nLevel+1;
+ pNew->nRef = 1;
+ pNew->nWriteCounter = pStruct->nWriteCounter;
+ pLvl = &pNew->aLevel[pStruct->nLevel];
+ pLvl->aSeg = (Fts5StructureSegment*)sqlite3Fts5MallocZero(&p->rc, nByte);
+ if( pLvl->aSeg ){
+ int iLvl, iSeg;
+ int iSegOut = 0;
+ /* Iterate through all segments, from oldest to newest. Add them to
+ ** the new Fts5Level object so that pLvl->aSeg[0] is the oldest
+ ** segment in the data structure. */
+ for(iLvl=pStruct->nLevel-1; iLvl>=0; iLvl--){
+ for(iSeg=0; iSeg<pStruct->aLevel[iLvl].nSeg; iSeg++){
+ pLvl->aSeg[iSegOut] = pStruct->aLevel[iLvl].aSeg[iSeg];
+ iSegOut++;
+ }
+ }
+ pNew->nSegment = pLvl->nSeg = nSeg;
+ }else{
+ sqlite3_free(pNew);
+ pNew = 0;
+ }
+ }
+
+ return pNew;
+}
+
+static int sqlite3Fts5IndexOptimize(Fts5Index *p){
+ Fts5Structure *pStruct;
+ Fts5Structure *pNew = 0;
+
+ assert( p->rc==SQLITE_OK );
+ fts5IndexFlush(p);
+ pStruct = fts5StructureRead(p);
+ fts5StructureInvalidate(p);
+
+ if( pStruct ){
+ pNew = fts5IndexOptimizeStruct(p, pStruct);
+ }
+ fts5StructureRelease(pStruct);
+
+ assert( pNew==0 || pNew->nSegment>0 );
+ if( pNew ){
+ int iLvl;
+ for(iLvl=0; pNew->aLevel[iLvl].nSeg==0; iLvl++){}
+ while( p->rc==SQLITE_OK && pNew->aLevel[iLvl].nSeg>0 ){
+ int nRem = FTS5_OPT_WORK_UNIT;
+ fts5IndexMergeLevel(p, &pNew, iLvl, &nRem);
+ }
+
+ fts5StructureWrite(p, pNew);
+ fts5StructureRelease(pNew);
+ }
+
+ return fts5IndexReturn(p);
+}
+
+/*
+** This is called to implement the special "VALUES('merge', $nMerge)"
+** INSERT command.
+*/
+static int sqlite3Fts5IndexMerge(Fts5Index *p, int nMerge){
+ Fts5Structure *pStruct = fts5StructureRead(p);
+ if( pStruct ){
+ int nMin = p->pConfig->nUsermerge;
+ fts5StructureInvalidate(p);
+ if( nMerge<0 ){
+ Fts5Structure *pNew = fts5IndexOptimizeStruct(p, pStruct);
+ fts5StructureRelease(pStruct);
+ pStruct = pNew;
+ nMin = 2;
+ nMerge = nMerge*-1;
+ }
+ if( pStruct && pStruct->nLevel ){
+ if( fts5IndexMerge(p, &pStruct, nMerge, nMin) ){
+ fts5StructureWrite(p, pStruct);
+ }
+ }
+ fts5StructureRelease(pStruct);
+ }
+ return fts5IndexReturn(p);
+}
+
+static void fts5AppendRowid(
+ Fts5Index *p,
+ i64 iDelta,
+ Fts5Iter *pUnused,
+ Fts5Buffer *pBuf
+){
+ UNUSED_PARAM(pUnused);
+ fts5BufferAppendVarint(&p->rc, pBuf, iDelta);
+}
+
+static void fts5AppendPoslist(
+ Fts5Index *p,
+ i64 iDelta,
+ Fts5Iter *pMulti,
+ Fts5Buffer *pBuf
+){
+ int nData = pMulti->base.nData;
+ assert( nData>0 );
+ if( p->rc==SQLITE_OK && 0==fts5BufferGrow(&p->rc, pBuf, nData+9+9) ){
+ fts5BufferSafeAppendVarint(pBuf, iDelta);
+ fts5BufferSafeAppendVarint(pBuf, nData*2);
+ fts5BufferSafeAppendBlob(pBuf, pMulti->base.pData, nData);
+ }
+}
+
+
+static void fts5DoclistIterNext(Fts5DoclistIter *pIter){
+ u8 *p = pIter->aPoslist + pIter->nSize + pIter->nPoslist;
+
+ assert( pIter->aPoslist );
+ if( p>=pIter->aEof ){
+ pIter->aPoslist = 0;
+ }else{
+ i64 iDelta;
+
+ p += fts5GetVarint(p, (u64*)&iDelta);
+ pIter->iRowid += iDelta;
+
+ /* Read position list size */
+ if( p[0] & 0x80 ){
+ int nPos;
+ pIter->nSize = fts5GetVarint32(p, nPos);
+ pIter->nPoslist = (nPos>>1);
+ }else{
+ pIter->nPoslist = ((int)(p[0])) >> 1;
+ pIter->nSize = 1;
+ }
+
+ pIter->aPoslist = p;
+ }
+}
+
+static void fts5DoclistIterInit(
+ Fts5Buffer *pBuf,
+ Fts5DoclistIter *pIter
+){
+ memset(pIter, 0, sizeof(*pIter));
+ pIter->aPoslist = pBuf->p;
+ pIter->aEof = &pBuf->p[pBuf->n];
+ fts5DoclistIterNext(pIter);
+}
+
+#if 0
+/*
+** Append a doclist to buffer pBuf.
+**
+** This function assumes that space within the buffer has already been
+** allocated.
+*/
+static void fts5MergeAppendDocid(
+ Fts5Buffer *pBuf, /* Buffer to write to */
+ i64 *piLastRowid, /* IN/OUT: Previous rowid written (if any) */
+ i64 iRowid /* Rowid to append */
+){
+ assert( pBuf->n!=0 || (*piLastRowid)==0 );
+ fts5BufferSafeAppendVarint(pBuf, iRowid - *piLastRowid);
+ *piLastRowid = iRowid;
+}
+#endif
+
+#define fts5MergeAppendDocid(pBuf, iLastRowid, iRowid) { \
+ assert( (pBuf)->n!=0 || (iLastRowid)==0 ); \
+ fts5BufferSafeAppendVarint((pBuf), (iRowid) - (iLastRowid)); \
+ (iLastRowid) = (iRowid); \
+}
+
+/*
+** Swap the contents of buffer *p1 with that of *p2.
+*/
+static void fts5BufferSwap(Fts5Buffer *p1, Fts5Buffer *p2){
+ Fts5Buffer tmp = *p1;
+ *p1 = *p2;
+ *p2 = tmp;
+}
+
+static void fts5NextRowid(Fts5Buffer *pBuf, int *piOff, i64 *piRowid){
+ int i = *piOff;
+ if( i>=pBuf->n ){
+ *piOff = -1;
+ }else{
+ u64 iVal;
+ *piOff = i + sqlite3Fts5GetVarint(&pBuf->p[i], &iVal);
+ *piRowid += iVal;
+ }
+}
+
+/*
+** This is the equivalent of fts5MergePrefixLists() for detail=none mode.
+** In this case the buffers consist of a delta-encoded list of rowids only.
+*/
+static void fts5MergeRowidLists(
+ Fts5Index *p, /* FTS5 backend object */
+ Fts5Buffer *p1, /* First list to merge */
+ Fts5Buffer *p2 /* Second list to merge */
+){
+ int i1 = 0;
+ int i2 = 0;
+ i64 iRowid1 = 0;
+ i64 iRowid2 = 0;
+ i64 iOut = 0;
+
+ Fts5Buffer out;
+ memset(&out, 0, sizeof(out));
+ sqlite3Fts5BufferSize(&p->rc, &out, p1->n + p2->n);
+ if( p->rc ) return;
+
+ fts5NextRowid(p1, &i1, &iRowid1);
+ fts5NextRowid(p2, &i2, &iRowid2);
+ while( i1>=0 || i2>=0 ){
+ if( i1>=0 && (i2<0 || iRowid1<iRowid2) ){
+ assert( iOut==0 || iRowid1>iOut );
+ fts5BufferSafeAppendVarint(&out, iRowid1 - iOut);
+ iOut = iRowid1;
+ fts5NextRowid(p1, &i1, &iRowid1);
+ }else{
+ assert( iOut==0 || iRowid2>iOut );
+ fts5BufferSafeAppendVarint(&out, iRowid2 - iOut);
+ iOut = iRowid2;
+ if( i1>=0 && iRowid1==iRowid2 ){
+ fts5NextRowid(p1, &i1, &iRowid1);
+ }
+ fts5NextRowid(p2, &i2, &iRowid2);
+ }
+ }
+
+ fts5BufferSwap(&out, p1);
+ fts5BufferFree(&out);
+}
+
+/*
+** Buffers p1 and p2 contain doclists. This function merges the content
+** of the two doclists together and sets buffer p1 to the result before
+** returning.
+**
+** If an error occurs, an error code is left in p->rc. If an error has
+** already occurred, this function is a no-op.
+*/
+static void fts5MergePrefixLists(
+ Fts5Index *p, /* FTS5 backend object */
+ Fts5Buffer *p1, /* First list to merge */
+ Fts5Buffer *p2 /* Second list to merge */
+){
+ if( p2->n ){
+ i64 iLastRowid = 0;
+ Fts5DoclistIter i1;
+ Fts5DoclistIter i2;
+ Fts5Buffer out = {0, 0, 0};
+ Fts5Buffer tmp = {0, 0, 0};
+
+ if( sqlite3Fts5BufferSize(&p->rc, &out, p1->n + p2->n) ) return;
+ fts5DoclistIterInit(p1, &i1);
+ fts5DoclistIterInit(p2, &i2);
+
+ while( 1 ){
+ if( i1.iRowid<i2.iRowid ){
+ /* Copy entry from i1 */
+ fts5MergeAppendDocid(&out, iLastRowid, i1.iRowid);
+ fts5BufferSafeAppendBlob(&out, i1.aPoslist, i1.nPoslist+i1.nSize);
+ fts5DoclistIterNext(&i1);
+ if( i1.aPoslist==0 ) break;
+ }
+ else if( i2.iRowid!=i1.iRowid ){
+ /* Copy entry from i2 */
+ fts5MergeAppendDocid(&out, iLastRowid, i2.iRowid);
+ fts5BufferSafeAppendBlob(&out, i2.aPoslist, i2.nPoslist+i2.nSize);
+ fts5DoclistIterNext(&i2);
+ if( i2.aPoslist==0 ) break;
+ }
+ else{
+ /* Merge the two position lists. */
+ i64 iPos1 = 0;
+ i64 iPos2 = 0;
+ int iOff1 = 0;
+ int iOff2 = 0;
+ u8 *a1 = &i1.aPoslist[i1.nSize];
+ u8 *a2 = &i2.aPoslist[i2.nSize];
+
+ i64 iPrev = 0;
+ Fts5PoslistWriter writer;
+ memset(&writer, 0, sizeof(writer));
+
+ fts5MergeAppendDocid(&out, iLastRowid, i2.iRowid);
+ fts5BufferZero(&tmp);
+ sqlite3Fts5BufferSize(&p->rc, &tmp, i1.nPoslist + i2.nPoslist);
+ if( p->rc ) break;
+
+ sqlite3Fts5PoslistNext64(a1, i1.nPoslist, &iOff1, &iPos1);
+ sqlite3Fts5PoslistNext64(a2, i2.nPoslist, &iOff2, &iPos2);
+ assert( iPos1>=0 && iPos2>=0 );
+
+ if( iPos1<iPos2 ){
+ sqlite3Fts5PoslistSafeAppend(&tmp, &iPrev, iPos1);
+ sqlite3Fts5PoslistNext64(a1, i1.nPoslist, &iOff1, &iPos1);
+ }else{
+ sqlite3Fts5PoslistSafeAppend(&tmp, &iPrev, iPos2);
+ sqlite3Fts5PoslistNext64(a2, i2.nPoslist, &iOff2, &iPos2);
+ }
+
+ if( iPos1>=0 && iPos2>=0 ){
+ while( 1 ){
+ if( iPos1<iPos2 ){
+ if( iPos1!=iPrev ){
+ sqlite3Fts5PoslistSafeAppend(&tmp, &iPrev, iPos1);
+ }
+ sqlite3Fts5PoslistNext64(a1, i1.nPoslist, &iOff1, &iPos1);
+ if( iPos1<0 ) break;
+ }else{
+ assert( iPos2!=iPrev );
+ sqlite3Fts5PoslistSafeAppend(&tmp, &iPrev, iPos2);
+ sqlite3Fts5PoslistNext64(a2, i2.nPoslist, &iOff2, &iPos2);
+ if( iPos2<0 ) break;
+ }
+ }
+ }
+
+ if( iPos1>=0 ){
+ if( iPos1!=iPrev ){
+ sqlite3Fts5PoslistSafeAppend(&tmp, &iPrev, iPos1);
+ }
+ fts5BufferSafeAppendBlob(&tmp, &a1[iOff1], i1.nPoslist-iOff1);
+ }else{
+ assert( iPos2>=0 && iPos2!=iPrev );
+ sqlite3Fts5PoslistSafeAppend(&tmp, &iPrev, iPos2);
+ fts5BufferSafeAppendBlob(&tmp, &a2[iOff2], i2.nPoslist-iOff2);
+ }
+
+ /* WRITEPOSLISTSIZE */
+ fts5BufferSafeAppendVarint(&out, tmp.n * 2);
+ fts5BufferSafeAppendBlob(&out, tmp.p, tmp.n);
+ fts5DoclistIterNext(&i1);
+ fts5DoclistIterNext(&i2);
+ if( i1.aPoslist==0 || i2.aPoslist==0 ) break;
+ }
+ }
+
+ if( i1.aPoslist ){
+ fts5MergeAppendDocid(&out, iLastRowid, i1.iRowid);
+ fts5BufferSafeAppendBlob(&out, i1.aPoslist, i1.aEof - i1.aPoslist);
+ }
+ else if( i2.aPoslist ){
+ fts5MergeAppendDocid(&out, iLastRowid, i2.iRowid);
+ fts5BufferSafeAppendBlob(&out, i2.aPoslist, i2.aEof - i2.aPoslist);
+ }
+
+ fts5BufferSet(&p->rc, p1, out.n, out.p);
+ fts5BufferFree(&tmp);
+ fts5BufferFree(&out);
+ }
+}
+
+static void fts5SetupPrefixIter(
+ Fts5Index *p, /* Index to read from */
+ int bDesc, /* True for "ORDER BY rowid DESC" */
+ const u8 *pToken, /* Buffer containing prefix to match */
+ int nToken, /* Size of buffer pToken in bytes */
+ Fts5Colset *pColset, /* Restrict matches to these columns */
+ Fts5Iter **ppIter /* OUT: New iterator */
+){
+ Fts5Structure *pStruct;
+ Fts5Buffer *aBuf;
+ const int nBuf = 32;
+
+ void (*xMerge)(Fts5Index*, Fts5Buffer*, Fts5Buffer*);
+ void (*xAppend)(Fts5Index*, i64, Fts5Iter*, Fts5Buffer*);
+ if( p->pConfig->eDetail==FTS5_DETAIL_NONE ){
+ xMerge = fts5MergeRowidLists;
+ xAppend = fts5AppendRowid;
+ }else{
+ xMerge = fts5MergePrefixLists;
+ xAppend = fts5AppendPoslist;
+ }
+
+ aBuf = (Fts5Buffer*)fts5IdxMalloc(p, sizeof(Fts5Buffer)*nBuf);
+ pStruct = fts5StructureRead(p);
+
+ if( aBuf && pStruct ){
+ const int flags = FTS5INDEX_QUERY_SCAN
+ | FTS5INDEX_QUERY_SKIPEMPTY
+ | FTS5INDEX_QUERY_NOOUTPUT;
+ int i;
+ i64 iLastRowid = 0;
+ Fts5Iter *p1 = 0; /* Iterator used to gather data from index */
+ Fts5Data *pData;
+ Fts5Buffer doclist;
+ int bNewTerm = 1;
+
+ memset(&doclist, 0, sizeof(doclist));
+ fts5MultiIterNew(p, pStruct, flags, pColset, pToken, nToken, -1, 0, &p1);
+ fts5IterSetOutputCb(&p->rc, p1);
+ for( /* no-op */ ;
+ fts5MultiIterEof(p, p1)==0;
+ fts5MultiIterNext2(p, p1, &bNewTerm)
+ ){
+ Fts5SegIter *pSeg = &p1->aSeg[ p1->aFirst[1].iFirst ];
+ int nTerm = pSeg->term.n;
+ const u8 *pTerm = pSeg->term.p;
+ p1->xSetOutputs(p1, pSeg);
+
+ assert_nc( memcmp(pToken, pTerm, MIN(nToken, nTerm))<=0 );
+ if( bNewTerm ){
+ if( nTerm<nToken || memcmp(pToken, pTerm, nToken) ) break;
+ }
+
+ if( p1->base.nData==0 ) continue;
+
+ if( p1->base.iRowid<=iLastRowid && doclist.n>0 ){
+ for(i=0; p->rc==SQLITE_OK && doclist.n; i++){
+ assert( i<nBuf );
+ if( aBuf[i].n==0 ){
+ fts5BufferSwap(&doclist, &aBuf[i]);
+ fts5BufferZero(&doclist);
+ }else{
+ xMerge(p, &doclist, &aBuf[i]);
+ fts5BufferZero(&aBuf[i]);
+ }
+ }
+ iLastRowid = 0;
+ }
+
+ xAppend(p, p1->base.iRowid-iLastRowid, p1, &doclist);
+ iLastRowid = p1->base.iRowid;
+ }
+
+ for(i=0; i<nBuf; i++){
+ if( p->rc==SQLITE_OK ){
+ xMerge(p, &doclist, &aBuf[i]);
+ }
+ fts5BufferFree(&aBuf[i]);
+ }
+ fts5MultiIterFree(p1);
+
+ pData = fts5IdxMalloc(p, sizeof(Fts5Data) + doclist.n);
+ if( pData ){
+ pData->p = (u8*)&pData[1];
+ pData->nn = pData->szLeaf = doclist.n;
+ memcpy(pData->p, doclist.p, doclist.n);
+ fts5MultiIterNew2(p, pData, bDesc, ppIter);
+ }
+ fts5BufferFree(&doclist);
+ }
+
+ fts5StructureRelease(pStruct);
+ sqlite3_free(aBuf);
+}
+
+
+/*
+** Indicate that all subsequent calls to sqlite3Fts5IndexWrite() pertain
+** to the document with rowid iRowid.
+*/
+static int sqlite3Fts5IndexBeginWrite(Fts5Index *p, int bDelete, i64 iRowid){
+ assert( p->rc==SQLITE_OK );
+
+ /* Allocate the hash table if it has not already been allocated */
+ if( p->pHash==0 ){
+ p->rc = sqlite3Fts5HashNew(p->pConfig, &p->pHash, &p->nPendingData);
+ }
+
+ /* Flush the hash table to disk if required */
+ if( iRowid<p->iWriteRowid
+ || (iRowid==p->iWriteRowid && p->bDelete==0)
+ || (p->nPendingData > p->pConfig->nHashSize)
+ ){
+ fts5IndexFlush(p);
+ }
+
+ p->iWriteRowid = iRowid;
+ p->bDelete = bDelete;
+ return fts5IndexReturn(p);
+}
+
+/*
+** Commit data to disk.
+*/
+static int sqlite3Fts5IndexSync(Fts5Index *p, int bCommit){
+ assert( p->rc==SQLITE_OK );
+ fts5IndexFlush(p);
+ if( bCommit ) fts5CloseReader(p);
+ return fts5IndexReturn(p);
+}
+
+/*
+** Discard any data stored in the in-memory hash tables. Do not write it
+** to the database. Additionally, assume that the contents of the %_data
+** table may have changed on disk. So any in-memory caches of %_data
+** records must be invalidated.
+*/
+static int sqlite3Fts5IndexRollback(Fts5Index *p){
+ fts5CloseReader(p);
+ fts5IndexDiscardData(p);
+ fts5StructureInvalidate(p);
+ /* assert( p->rc==SQLITE_OK ); */
+ return SQLITE_OK;
+}
+
+/*
+** The %_data table is completely empty when this function is called. This
+** function populates it with the initial structure objects for each index,
+** and the initial version of the "averages" record (a zero-byte blob).
+*/
+static int sqlite3Fts5IndexReinit(Fts5Index *p){
+ Fts5Structure s;
+ fts5StructureInvalidate(p);
+ memset(&s, 0, sizeof(Fts5Structure));
+ fts5DataWrite(p, FTS5_AVERAGES_ROWID, (const u8*)"", 0);
+ fts5StructureWrite(p, &s);
+ return fts5IndexReturn(p);
+}
+
+/*
+** Open a new Fts5Index handle. If the bCreate argument is true, create
+** and initialize the underlying %_data table.
+**
+** If successful, set *pp to point to the new object and return SQLITE_OK.
+** Otherwise, set *pp to NULL and return an SQLite error code.
+*/
+static int sqlite3Fts5IndexOpen(
+ Fts5Config *pConfig,
+ int bCreate,
+ Fts5Index **pp,
+ char **pzErr
+){
+ int rc = SQLITE_OK;
+ Fts5Index *p; /* New object */
+
+ *pp = p = (Fts5Index*)sqlite3Fts5MallocZero(&rc, sizeof(Fts5Index));
+ if( rc==SQLITE_OK ){
+ p->pConfig = pConfig;
+ p->nWorkUnit = FTS5_WORK_UNIT;
+ p->zDataTbl = sqlite3Fts5Mprintf(&rc, "%s_data", pConfig->zName);
+ if( p->zDataTbl && bCreate ){
+ rc = sqlite3Fts5CreateTable(
+ pConfig, "data", "id INTEGER PRIMARY KEY, block BLOB", 0, pzErr
+ );
+ if( rc==SQLITE_OK ){
+ rc = sqlite3Fts5CreateTable(pConfig, "idx",
+ "segid, term, pgno, PRIMARY KEY(segid, term)",
+ 1, pzErr
+ );
+ }
+ if( rc==SQLITE_OK ){
+ rc = sqlite3Fts5IndexReinit(p);
+ }
+ }
+ }
+
+ assert( rc!=SQLITE_OK || p->rc==SQLITE_OK );
+ if( rc ){
+ sqlite3Fts5IndexClose(p);
+ *pp = 0;
+ }
+ return rc;
+}
+
+/*
+** Close a handle opened by an earlier call to sqlite3Fts5IndexOpen().
+*/
+static int sqlite3Fts5IndexClose(Fts5Index *p){
+ int rc = SQLITE_OK;
+ if( p ){
+ assert( p->pReader==0 );
+ fts5StructureInvalidate(p);
+ sqlite3_finalize(p->pWriter);
+ sqlite3_finalize(p->pDeleter);
+ sqlite3_finalize(p->pIdxWriter);
+ sqlite3_finalize(p->pIdxDeleter);
+ sqlite3_finalize(p->pIdxSelect);
+ sqlite3_finalize(p->pDataVersion);
+ sqlite3Fts5HashFree(p->pHash);
+ sqlite3_free(p->zDataTbl);
+ sqlite3_free(p);
+ }
+ return rc;
+}
+
+/*
+** Argument p points to a buffer containing utf-8 text that is n bytes in
+** size. Return the number of bytes in the nChar character prefix of the
+** buffer, or 0 if there are less than nChar characters in total.
+*/
+static int sqlite3Fts5IndexCharlenToBytelen(
+ const char *p,
+ int nByte,
+ int nChar
+){
+ int n = 0;
+ int i;
+ for(i=0; i<nChar; i++){
+ if( n>=nByte ) return 0; /* Input contains fewer than nChar chars */
+ if( (unsigned char)p[n++]>=0xc0 ){
+ while( (p[n] & 0xc0)==0x80 ) n++;
+ }
+ }
+ return n;
+}
+
+/*
+** pIn is a UTF-8 encoded string, nIn bytes in size. Return the number of
+** unicode characters in the string.
+*/
+static int fts5IndexCharlen(const char *pIn, int nIn){
+ int nChar = 0;
+ int i = 0;
+ while( i<nIn ){
+ if( (unsigned char)pIn[i++]>=0xc0 ){
+ while( i<nIn && (pIn[i] & 0xc0)==0x80 ) i++;
+ }
+ nChar++;
+ }
+ return nChar;
+}
+
+/*
+** Insert or remove data to or from the index. Each time a document is
+** added to or removed from the index, this function is called one or more
+** times.
+**
+** For an insert, it must be called once for each token in the new document.
+** If the operation is a delete, it must be called (at least) once for each
+** unique token in the document with an iCol value less than zero. The iPos
+** argument is ignored for a delete.
+*/
+static int sqlite3Fts5IndexWrite(
+ Fts5Index *p, /* Index to write to */
+ int iCol, /* Column token appears in (-ve -> delete) */
+ int iPos, /* Position of token within column */
+ const char *pToken, int nToken /* Token to add or remove to or from index */
+){
+ int i; /* Used to iterate through indexes */
+ int rc = SQLITE_OK; /* Return code */
+ Fts5Config *pConfig = p->pConfig;
+
+ assert( p->rc==SQLITE_OK );
+ assert( (iCol<0)==p->bDelete );
+
+ /* Add the entry to the main terms index. */
+ rc = sqlite3Fts5HashWrite(
+ p->pHash, p->iWriteRowid, iCol, iPos, FTS5_MAIN_PREFIX, pToken, nToken
+ );
+
+ for(i=0; i<pConfig->nPrefix && rc==SQLITE_OK; i++){
+ const int nChar = pConfig->aPrefix[i];
+ int nByte = sqlite3Fts5IndexCharlenToBytelen(pToken, nToken, nChar);
+ if( nByte ){
+ rc = sqlite3Fts5HashWrite(p->pHash,
+ p->iWriteRowid, iCol, iPos, (char)(FTS5_MAIN_PREFIX+i+1), pToken,
+ nByte
+ );
+ }
+ }
+
+ return rc;
+}
+
+/*
+** Open a new iterator to iterate though all rowid that match the
+** specified token or token prefix.
+*/
+static int sqlite3Fts5IndexQuery(
+ Fts5Index *p, /* FTS index to query */
+ const char *pToken, int nToken, /* Token (or prefix) to query for */
+ int flags, /* Mask of FTS5INDEX_QUERY_X flags */
+ Fts5Colset *pColset, /* Match these columns only */
+ Fts5IndexIter **ppIter /* OUT: New iterator object */
+){
+ Fts5Config *pConfig = p->pConfig;
+ Fts5Iter *pRet = 0;
+ Fts5Buffer buf = {0, 0, 0};
+
+ /* If the QUERY_SCAN flag is set, all other flags must be clear. */
+ assert( (flags & FTS5INDEX_QUERY_SCAN)==0 || flags==FTS5INDEX_QUERY_SCAN );
+
+ if( sqlite3Fts5BufferSize(&p->rc, &buf, nToken+1)==0 ){
+ int iIdx = 0; /* Index to search */
+ memcpy(&buf.p[1], pToken, nToken);
+
+ /* Figure out which index to search and set iIdx accordingly. If this
+ ** is a prefix query for which there is no prefix index, set iIdx to
+ ** greater than pConfig->nPrefix to indicate that the query will be
+ ** satisfied by scanning multiple terms in the main index.
+ **
+ ** If the QUERY_TEST_NOIDX flag was specified, then this must be a
+ ** prefix-query. Instead of using a prefix-index (if one exists),
+ ** evaluate the prefix query using the main FTS index. This is used
+ ** for internal sanity checking by the integrity-check in debug
+ ** mode only. */
+#ifdef SQLITE_DEBUG
+ if( pConfig->bPrefixIndex==0 || (flags & FTS5INDEX_QUERY_TEST_NOIDX) ){
+ assert( flags & FTS5INDEX_QUERY_PREFIX );
+ iIdx = 1+pConfig->nPrefix;
+ }else
+#endif
+ if( flags & FTS5INDEX_QUERY_PREFIX ){
+ int nChar = fts5IndexCharlen(pToken, nToken);
+ for(iIdx=1; iIdx<=pConfig->nPrefix; iIdx++){
+ if( pConfig->aPrefix[iIdx-1]==nChar ) break;
+ }
+ }
+
+ if( iIdx<=pConfig->nPrefix ){
+ /* Straight index lookup */
+ Fts5Structure *pStruct = fts5StructureRead(p);
+ buf.p[0] = (u8)(FTS5_MAIN_PREFIX + iIdx);
+ if( pStruct ){
+ fts5MultiIterNew(p, pStruct, flags | FTS5INDEX_QUERY_SKIPEMPTY,
+ pColset, buf.p, nToken+1, -1, 0, &pRet
+ );
+ fts5StructureRelease(pStruct);
+ }
+ }else{
+ /* Scan multiple terms in the main index */
+ int bDesc = (flags & FTS5INDEX_QUERY_DESC)!=0;
+ buf.p[0] = FTS5_MAIN_PREFIX;
+ fts5SetupPrefixIter(p, bDesc, buf.p, nToken+1, pColset, &pRet);
+ assert( p->rc!=SQLITE_OK || pRet->pColset==0 );
+ fts5IterSetOutputCb(&p->rc, pRet);
+ if( p->rc==SQLITE_OK ){
+ Fts5SegIter *pSeg = &pRet->aSeg[pRet->aFirst[1].iFirst];
+ if( pSeg->pLeaf ) pRet->xSetOutputs(pRet, pSeg);
+ }
+ }
+
+ if( p->rc ){
+ sqlite3Fts5IterClose(&pRet->base);
+ pRet = 0;
+ fts5CloseReader(p);
+ }
+
+ *ppIter = &pRet->base;
+ sqlite3Fts5BufferFree(&buf);
+ }
+ return fts5IndexReturn(p);
+}
+
+/*
+** Return true if the iterator passed as the only argument is at EOF.
+*/
+/*
+** Move to the next matching rowid.
+*/
+static int sqlite3Fts5IterNext(Fts5IndexIter *pIndexIter){
+ Fts5Iter *pIter = (Fts5Iter*)pIndexIter;
+ assert( pIter->pIndex->rc==SQLITE_OK );
+ fts5MultiIterNext(pIter->pIndex, pIter, 0, 0);
+ return fts5IndexReturn(pIter->pIndex);
+}
+
+/*
+** Move to the next matching term/rowid. Used by the fts5vocab module.
+*/
+static int sqlite3Fts5IterNextScan(Fts5IndexIter *pIndexIter){
+ Fts5Iter *pIter = (Fts5Iter*)pIndexIter;
+ Fts5Index *p = pIter->pIndex;
+
+ assert( pIter->pIndex->rc==SQLITE_OK );
+
+ fts5MultiIterNext(p, pIter, 0, 0);
+ if( p->rc==SQLITE_OK ){
+ Fts5SegIter *pSeg = &pIter->aSeg[ pIter->aFirst[1].iFirst ];
+ if( pSeg->pLeaf && pSeg->term.p[0]!=FTS5_MAIN_PREFIX ){
+ fts5DataRelease(pSeg->pLeaf);
+ pSeg->pLeaf = 0;
+ pIter->base.bEof = 1;
+ }
+ }
+
+ return fts5IndexReturn(pIter->pIndex);
+}
+
+/*
+** Move to the next matching rowid that occurs at or after iMatch. The
+** definition of "at or after" depends on whether this iterator iterates
+** in ascending or descending rowid order.
+*/
+static int sqlite3Fts5IterNextFrom(Fts5IndexIter *pIndexIter, i64 iMatch){
+ Fts5Iter *pIter = (Fts5Iter*)pIndexIter;
+ fts5MultiIterNextFrom(pIter->pIndex, pIter, iMatch);
+ return fts5IndexReturn(pIter->pIndex);
+}
+
+/*
+** Return the current term.
+*/
+static const char *sqlite3Fts5IterTerm(Fts5IndexIter *pIndexIter, int *pn){
+ int n;
+ const char *z = (const char*)fts5MultiIterTerm((Fts5Iter*)pIndexIter, &n);
+ *pn = n-1;
+ return &z[1];
+}
+
+/*
+** Close an iterator opened by an earlier call to sqlite3Fts5IndexQuery().
+*/
+static void sqlite3Fts5IterClose(Fts5IndexIter *pIndexIter){
+ if( pIndexIter ){
+ Fts5Iter *pIter = (Fts5Iter*)pIndexIter;
+ Fts5Index *pIndex = pIter->pIndex;
+ fts5MultiIterFree(pIter);
+ fts5CloseReader(pIndex);
+ }
+}
+
+/*
+** Read and decode the "averages" record from the database.
+**
+** Parameter anSize must point to an array of size nCol, where nCol is
+** the number of user defined columns in the FTS table.
+*/
+static int sqlite3Fts5IndexGetAverages(Fts5Index *p, i64 *pnRow, i64 *anSize){
+ int nCol = p->pConfig->nCol;
+ Fts5Data *pData;
+
+ *pnRow = 0;
+ memset(anSize, 0, sizeof(i64) * nCol);
+ pData = fts5DataRead(p, FTS5_AVERAGES_ROWID);
+ if( p->rc==SQLITE_OK && pData->nn ){
+ int i = 0;
+ int iCol;
+ i += fts5GetVarint(&pData->p[i], (u64*)pnRow);
+ for(iCol=0; i<pData->nn && iCol<nCol; iCol++){
+ i += fts5GetVarint(&pData->p[i], (u64*)&anSize[iCol]);
+ }
+ }
+
+ fts5DataRelease(pData);
+ return fts5IndexReturn(p);
+}
+
+/*
+** Replace the current "averages" record with the contents of the buffer
+** supplied as the second argument.
+*/
+static int sqlite3Fts5IndexSetAverages(Fts5Index *p, const u8 *pData, int nData){
+ assert( p->rc==SQLITE_OK );
+ fts5DataWrite(p, FTS5_AVERAGES_ROWID, pData, nData);
+ return fts5IndexReturn(p);
+}
+
+/*
+** Return the total number of blocks this module has read from the %_data
+** table since it was created.
+*/
+static int sqlite3Fts5IndexReads(Fts5Index *p){
+ return p->nRead;
+}
+
+/*
+** Set the 32-bit cookie value stored at the start of all structure
+** records to the value passed as the second argument.
+**
+** Return SQLITE_OK if successful, or an SQLite error code if an error
+** occurs.
+*/
+static int sqlite3Fts5IndexSetCookie(Fts5Index *p, int iNew){
+ int rc; /* Return code */
+ Fts5Config *pConfig = p->pConfig; /* Configuration object */
+ u8 aCookie[4]; /* Binary representation of iNew */
+ sqlite3_blob *pBlob = 0;
+
+ assert( p->rc==SQLITE_OK );
+ sqlite3Fts5Put32(aCookie, iNew);
+
+ rc = sqlite3_blob_open(pConfig->db, pConfig->zDb, p->zDataTbl,
+ "block", FTS5_STRUCTURE_ROWID, 1, &pBlob
+ );
+ if( rc==SQLITE_OK ){
+ sqlite3_blob_write(pBlob, aCookie, 4, 0);
+ rc = sqlite3_blob_close(pBlob);
+ }
+
+ return rc;
+}
+
+static int sqlite3Fts5IndexLoadConfig(Fts5Index *p){
+ Fts5Structure *pStruct;
+ pStruct = fts5StructureRead(p);
+ fts5StructureRelease(pStruct);
+ return fts5IndexReturn(p);
+}
+
+
+/*************************************************************************
+**************************************************************************
+** Below this point is the implementation of the integrity-check
+** functionality.
+*/
+
+/*
+** Return a simple checksum value based on the arguments.
+*/
+static u64 sqlite3Fts5IndexEntryCksum(
+ i64 iRowid,
+ int iCol,
+ int iPos,
+ int iIdx,
+ const char *pTerm,
+ int nTerm
+){
+ int i;
+ u64 ret = iRowid;
+ ret += (ret<<3) + iCol;
+ ret += (ret<<3) + iPos;
+ if( iIdx>=0 ) ret += (ret<<3) + (FTS5_MAIN_PREFIX + iIdx);
+ for(i=0; i<nTerm; i++) ret += (ret<<3) + pTerm[i];
+ return ret;
+}
+
+#ifdef SQLITE_DEBUG
+/*
+** This function is purely an internal test. It does not contribute to
+** FTS functionality, or even the integrity-check, in any way.
+**
+** Instead, it tests that the same set of pgno/rowid combinations are
+** visited regardless of whether the doclist-index identified by parameters
+** iSegid/iLeaf is iterated in forwards or reverse order.
+*/
+static void fts5TestDlidxReverse(
+ Fts5Index *p,
+ int iSegid, /* Segment id to load from */
+ int iLeaf /* Load doclist-index for this leaf */
+){
+ Fts5DlidxIter *pDlidx = 0;
+ u64 cksum1 = 13;
+ u64 cksum2 = 13;
+
+ for(pDlidx=fts5DlidxIterInit(p, 0, iSegid, iLeaf);
+ fts5DlidxIterEof(p, pDlidx)==0;
+ fts5DlidxIterNext(p, pDlidx)
+ ){
+ i64 iRowid = fts5DlidxIterRowid(pDlidx);
+ int pgno = fts5DlidxIterPgno(pDlidx);
+ assert( pgno>iLeaf );
+ cksum1 += iRowid + ((i64)pgno<<32);
+ }
+ fts5DlidxIterFree(pDlidx);
+ pDlidx = 0;
+
+ for(pDlidx=fts5DlidxIterInit(p, 1, iSegid, iLeaf);
+ fts5DlidxIterEof(p, pDlidx)==0;
+ fts5DlidxIterPrev(p, pDlidx)
+ ){
+ i64 iRowid = fts5DlidxIterRowid(pDlidx);
+ int pgno = fts5DlidxIterPgno(pDlidx);
+ assert( fts5DlidxIterPgno(pDlidx)>iLeaf );
+ cksum2 += iRowid + ((i64)pgno<<32);
+ }
+ fts5DlidxIterFree(pDlidx);
+ pDlidx = 0;
+
+ if( p->rc==SQLITE_OK && cksum1!=cksum2 ) p->rc = FTS5_CORRUPT;
+}
+
+static int fts5QueryCksum(
+ Fts5Index *p, /* Fts5 index object */
+ int iIdx,
+ const char *z, /* Index key to query for */
+ int n, /* Size of index key in bytes */
+ int flags, /* Flags for Fts5IndexQuery */
+ u64 *pCksum /* IN/OUT: Checksum value */
+){
+ int eDetail = p->pConfig->eDetail;
+ u64 cksum = *pCksum;
+ Fts5IndexIter *pIter = 0;
+ int rc = sqlite3Fts5IndexQuery(p, z, n, flags, 0, &pIter);
+
+ while( rc==SQLITE_OK && 0==sqlite3Fts5IterEof(pIter) ){
+ i64 rowid = pIter->iRowid;
+
+ if( eDetail==FTS5_DETAIL_NONE ){
+ cksum ^= sqlite3Fts5IndexEntryCksum(rowid, 0, 0, iIdx, z, n);
+ }else{
+ Fts5PoslistReader sReader;
+ for(sqlite3Fts5PoslistReaderInit(pIter->pData, pIter->nData, &sReader);
+ sReader.bEof==0;
+ sqlite3Fts5PoslistReaderNext(&sReader)
+ ){
+ int iCol = FTS5_POS2COLUMN(sReader.iPos);
+ int iOff = FTS5_POS2OFFSET(sReader.iPos);
+ cksum ^= sqlite3Fts5IndexEntryCksum(rowid, iCol, iOff, iIdx, z, n);
+ }
+ }
+ if( rc==SQLITE_OK ){
+ rc = sqlite3Fts5IterNext(pIter);
+ }
+ }
+ sqlite3Fts5IterClose(pIter);
+
+ *pCksum = cksum;
+ return rc;
+}
+
+
+/*
+** This function is also purely an internal test. It does not contribute to
+** FTS functionality, or even the integrity-check, in any way.
+*/
+static void fts5TestTerm(
+ Fts5Index *p,
+ Fts5Buffer *pPrev, /* Previous term */
+ const char *z, int n, /* Possibly new term to test */
+ u64 expected,
+ u64 *pCksum
+){
+ int rc = p->rc;
+ if( pPrev->n==0 ){
+ fts5BufferSet(&rc, pPrev, n, (const u8*)z);
+ }else
+ if( rc==SQLITE_OK && (pPrev->n!=n || memcmp(pPrev->p, z, n)) ){
+ u64 cksum3 = *pCksum;
+ const char *zTerm = (const char*)&pPrev->p[1]; /* term sans prefix-byte */
+ int nTerm = pPrev->n-1; /* Size of zTerm in bytes */
+ int iIdx = (pPrev->p[0] - FTS5_MAIN_PREFIX);
+ int flags = (iIdx==0 ? 0 : FTS5INDEX_QUERY_PREFIX);
+ u64 ck1 = 0;
+ u64 ck2 = 0;
+
+ /* Check that the results returned for ASC and DESC queries are
+ ** the same. If not, call this corruption. */
+ rc = fts5QueryCksum(p, iIdx, zTerm, nTerm, flags, &ck1);
+ if( rc==SQLITE_OK ){
+ int f = flags|FTS5INDEX_QUERY_DESC;
+ rc = fts5QueryCksum(p, iIdx, zTerm, nTerm, f, &ck2);
+ }
+ if( rc==SQLITE_OK && ck1!=ck2 ) rc = FTS5_CORRUPT;
+
+ /* If this is a prefix query, check that the results returned if the
+ ** the index is disabled are the same. In both ASC and DESC order.
+ **
+ ** This check may only be performed if the hash table is empty. This
+ ** is because the hash table only supports a single scan query at
+ ** a time, and the multi-iter loop from which this function is called
+ ** is already performing such a scan. */
+ if( p->nPendingData==0 ){
+ if( iIdx>0 && rc==SQLITE_OK ){
+ int f = flags|FTS5INDEX_QUERY_TEST_NOIDX;
+ ck2 = 0;
+ rc = fts5QueryCksum(p, iIdx, zTerm, nTerm, f, &ck2);
+ if( rc==SQLITE_OK && ck1!=ck2 ) rc = FTS5_CORRUPT;
+ }
+ if( iIdx>0 && rc==SQLITE_OK ){
+ int f = flags|FTS5INDEX_QUERY_TEST_NOIDX|FTS5INDEX_QUERY_DESC;
+ ck2 = 0;
+ rc = fts5QueryCksum(p, iIdx, zTerm, nTerm, f, &ck2);
+ if( rc==SQLITE_OK && ck1!=ck2 ) rc = FTS5_CORRUPT;
+ }
+ }
+
+ cksum3 ^= ck1;
+ fts5BufferSet(&rc, pPrev, n, (const u8*)z);
+
+ if( rc==SQLITE_OK && cksum3!=expected ){
+ rc = FTS5_CORRUPT;
+ }
+ *pCksum = cksum3;
+ }
+ p->rc = rc;
+}
+
+#else
+# define fts5TestDlidxReverse(x,y,z)
+# define fts5TestTerm(u,v,w,x,y,z)
+#endif
+
+/*
+** Check that:
+**
+** 1) All leaves of pSeg between iFirst and iLast (inclusive) exist and
+** contain zero terms.
+** 2) All leaves of pSeg between iNoRowid and iLast (inclusive) exist and
+** contain zero rowids.
+*/
+static void fts5IndexIntegrityCheckEmpty(
+ Fts5Index *p,
+ Fts5StructureSegment *pSeg, /* Segment to check internal consistency */
+ int iFirst,
+ int iNoRowid,
+ int iLast
+){
+ int i;
+
+ /* Now check that the iter.nEmpty leaves following the current leaf
+ ** (a) exist and (b) contain no terms. */
+ for(i=iFirst; p->rc==SQLITE_OK && i<=iLast; i++){
+ Fts5Data *pLeaf = fts5DataRead(p, FTS5_SEGMENT_ROWID(pSeg->iSegid, i));
+ if( pLeaf ){
+ if( !fts5LeafIsTermless(pLeaf) ) p->rc = FTS5_CORRUPT;
+ if( i>=iNoRowid && 0!=fts5LeafFirstRowidOff(pLeaf) ) p->rc = FTS5_CORRUPT;
+ }
+ fts5DataRelease(pLeaf);
+ }
+}
+
+static void fts5IntegrityCheckPgidx(Fts5Index *p, Fts5Data *pLeaf){
+ int iTermOff = 0;
+ int ii;
+
+ Fts5Buffer buf1 = {0,0,0};
+ Fts5Buffer buf2 = {0,0,0};
+
+ ii = pLeaf->szLeaf;
+ while( ii<pLeaf->nn && p->rc==SQLITE_OK ){
+ int res;
+ int iOff;
+ int nIncr;
+
+ ii += fts5GetVarint32(&pLeaf->p[ii], nIncr);
+ iTermOff += nIncr;
+ iOff = iTermOff;
+
+ if( iOff>=pLeaf->szLeaf ){
+ p->rc = FTS5_CORRUPT;
+ }else if( iTermOff==nIncr ){
+ int nByte;
+ iOff += fts5GetVarint32(&pLeaf->p[iOff], nByte);
+ if( (iOff+nByte)>pLeaf->szLeaf ){
+ p->rc = FTS5_CORRUPT;
+ }else{
+ fts5BufferSet(&p->rc, &buf1, nByte, &pLeaf->p[iOff]);
+ }
+ }else{
+ int nKeep, nByte;
+ iOff += fts5GetVarint32(&pLeaf->p[iOff], nKeep);
+ iOff += fts5GetVarint32(&pLeaf->p[iOff], nByte);
+ if( nKeep>buf1.n || (iOff+nByte)>pLeaf->szLeaf ){
+ p->rc = FTS5_CORRUPT;
+ }else{
+ buf1.n = nKeep;
+ fts5BufferAppendBlob(&p->rc, &buf1, nByte, &pLeaf->p[iOff]);
+ }
+
+ if( p->rc==SQLITE_OK ){
+ res = fts5BufferCompare(&buf1, &buf2);
+ if( res<=0 ) p->rc = FTS5_CORRUPT;
+ }
+ }
+ fts5BufferSet(&p->rc, &buf2, buf1.n, buf1.p);
+ }
+
+ fts5BufferFree(&buf1);
+ fts5BufferFree(&buf2);
+}
+
+static void fts5IndexIntegrityCheckSegment(
+ Fts5Index *p, /* FTS5 backend object */
+ Fts5StructureSegment *pSeg /* Segment to check internal consistency */
+){
+ Fts5Config *pConfig = p->pConfig;
+ sqlite3_stmt *pStmt = 0;
+ int rc2;
+ int iIdxPrevLeaf = pSeg->pgnoFirst-1;
+ int iDlidxPrevLeaf = pSeg->pgnoLast;
+
+ if( pSeg->pgnoFirst==0 ) return;
+
+ fts5IndexPrepareStmt(p, &pStmt, sqlite3_mprintf(
+ "SELECT segid, term, (pgno>>1), (pgno&1) FROM %Q.'%q_idx' WHERE segid=%d",
+ pConfig->zDb, pConfig->zName, pSeg->iSegid
+ ));
+
+ /* Iterate through the b-tree hierarchy. */
+ while( p->rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){
+ i64 iRow; /* Rowid for this leaf */
+ Fts5Data *pLeaf; /* Data for this leaf */
+
+ int nIdxTerm = sqlite3_column_bytes(pStmt, 1);
+ const char *zIdxTerm = (const char*)sqlite3_column_text(pStmt, 1);
+ int iIdxLeaf = sqlite3_column_int(pStmt, 2);
+ int bIdxDlidx = sqlite3_column_int(pStmt, 3);
+
+ /* If the leaf in question has already been trimmed from the segment,
+ ** ignore this b-tree entry. Otherwise, load it into memory. */
+ if( iIdxLeaf<pSeg->pgnoFirst ) continue;
+ iRow = FTS5_SEGMENT_ROWID(pSeg->iSegid, iIdxLeaf);
+ pLeaf = fts5DataRead(p, iRow);
+ if( pLeaf==0 ) break;
+
+ /* Check that the leaf contains at least one term, and that it is equal
+ ** to or larger than the split-key in zIdxTerm. Also check that if there
+ ** is also a rowid pointer within the leaf page header, it points to a
+ ** location before the term. */
+ if( pLeaf->nn<=pLeaf->szLeaf ){
+ p->rc = FTS5_CORRUPT;
+ }else{
+ int iOff; /* Offset of first term on leaf */
+ int iRowidOff; /* Offset of first rowid on leaf */
+ int nTerm; /* Size of term on leaf in bytes */
+ int res; /* Comparison of term and split-key */
+
+ iOff = fts5LeafFirstTermOff(pLeaf);
+ iRowidOff = fts5LeafFirstRowidOff(pLeaf);
+ if( iRowidOff>=iOff ){
+ p->rc = FTS5_CORRUPT;
+ }else{
+ iOff += fts5GetVarint32(&pLeaf->p[iOff], nTerm);
+ res = memcmp(&pLeaf->p[iOff], zIdxTerm, MIN(nTerm, nIdxTerm));
+ if( res==0 ) res = nTerm - nIdxTerm;
+ if( res<0 ) p->rc = FTS5_CORRUPT;
+ }
+
+ fts5IntegrityCheckPgidx(p, pLeaf);
+ }
+ fts5DataRelease(pLeaf);
+ if( p->rc ) break;
+
+ /* Now check that the iter.nEmpty leaves following the current leaf
+ ** (a) exist and (b) contain no terms. */
+ fts5IndexIntegrityCheckEmpty(
+ p, pSeg, iIdxPrevLeaf+1, iDlidxPrevLeaf+1, iIdxLeaf-1
+ );
+ if( p->rc ) break;
+
+ /* If there is a doclist-index, check that it looks right. */
+ if( bIdxDlidx ){
+ Fts5DlidxIter *pDlidx = 0; /* For iterating through doclist index */
+ int iPrevLeaf = iIdxLeaf;
+ int iSegid = pSeg->iSegid;
+ int iPg = 0;
+ i64 iKey;
+
+ for(pDlidx=fts5DlidxIterInit(p, 0, iSegid, iIdxLeaf);
+ fts5DlidxIterEof(p, pDlidx)==0;
+ fts5DlidxIterNext(p, pDlidx)
+ ){
+
+ /* Check any rowid-less pages that occur before the current leaf. */
+ for(iPg=iPrevLeaf+1; iPg<fts5DlidxIterPgno(pDlidx); iPg++){
+ iKey = FTS5_SEGMENT_ROWID(iSegid, iPg);
+ pLeaf = fts5DataRead(p, iKey);
+ if( pLeaf ){
+ if( fts5LeafFirstRowidOff(pLeaf)!=0 ) p->rc = FTS5_CORRUPT;
+ fts5DataRelease(pLeaf);
+ }
+ }
+ iPrevLeaf = fts5DlidxIterPgno(pDlidx);
+
+ /* Check that the leaf page indicated by the iterator really does
+ ** contain the rowid suggested by the same. */
+ iKey = FTS5_SEGMENT_ROWID(iSegid, iPrevLeaf);
+ pLeaf = fts5DataRead(p, iKey);
+ if( pLeaf ){
+ i64 iRowid;
+ int iRowidOff = fts5LeafFirstRowidOff(pLeaf);
+ ASSERT_SZLEAF_OK(pLeaf);
+ if( iRowidOff>=pLeaf->szLeaf ){
+ p->rc = FTS5_CORRUPT;
+ }else{
+ fts5GetVarint(&pLeaf->p[iRowidOff], (u64*)&iRowid);
+ if( iRowid!=fts5DlidxIterRowid(pDlidx) ) p->rc = FTS5_CORRUPT;
+ }
+ fts5DataRelease(pLeaf);
+ }
+ }
+
+ iDlidxPrevLeaf = iPg;
+ fts5DlidxIterFree(pDlidx);
+ fts5TestDlidxReverse(p, iSegid, iIdxLeaf);
+ }else{
+ iDlidxPrevLeaf = pSeg->pgnoLast;
+ /* TODO: Check there is no doclist index */
+ }
+
+ iIdxPrevLeaf = iIdxLeaf;
+ }
+
+ rc2 = sqlite3_finalize(pStmt);
+ if( p->rc==SQLITE_OK ) p->rc = rc2;
+
+ /* Page iter.iLeaf must now be the rightmost leaf-page in the segment */
+#if 0
+ if( p->rc==SQLITE_OK && iter.iLeaf!=pSeg->pgnoLast ){
+ p->rc = FTS5_CORRUPT;
+ }
+#endif
+}
+
+
+/*
+** Run internal checks to ensure that the FTS index (a) is internally
+** consistent and (b) contains entries for which the XOR of the checksums
+** as calculated by sqlite3Fts5IndexEntryCksum() is cksum.
+**
+** Return SQLITE_CORRUPT if any of the internal checks fail, or if the
+** checksum does not match. Return SQLITE_OK if all checks pass without
+** error, or some other SQLite error code if another error (e.g. OOM)
+** occurs.
+*/
+static int sqlite3Fts5IndexIntegrityCheck(Fts5Index *p, u64 cksum){
+ int eDetail = p->pConfig->eDetail;
+ u64 cksum2 = 0; /* Checksum based on contents of indexes */
+ Fts5Buffer poslist = {0,0,0}; /* Buffer used to hold a poslist */
+ Fts5Iter *pIter; /* Used to iterate through entire index */
+ Fts5Structure *pStruct; /* Index structure */
+
+#ifdef SQLITE_DEBUG
+ /* Used by extra internal tests only run if NDEBUG is not defined */
+ u64 cksum3 = 0; /* Checksum based on contents of indexes */
+ Fts5Buffer term = {0,0,0}; /* Buffer used to hold most recent term */
+#endif
+ const int flags = FTS5INDEX_QUERY_NOOUTPUT;
+
+ /* Load the FTS index structure */
+ pStruct = fts5StructureRead(p);
+
+ /* Check that the internal nodes of each segment match the leaves */
+ if( pStruct ){
+ int iLvl, iSeg;
+ for(iLvl=0; iLvl<pStruct->nLevel; iLvl++){
+ for(iSeg=0; iSeg<pStruct->aLevel[iLvl].nSeg; iSeg++){
+ Fts5StructureSegment *pSeg = &pStruct->aLevel[iLvl].aSeg[iSeg];
+ fts5IndexIntegrityCheckSegment(p, pSeg);
+ }
+ }
+ }
+
+ /* The cksum argument passed to this function is a checksum calculated
+ ** based on all expected entries in the FTS index (including prefix index
+ ** entries). This block checks that a checksum calculated based on the
+ ** actual contents of FTS index is identical.
+ **
+ ** Two versions of the same checksum are calculated. The first (stack
+ ** variable cksum2) based on entries extracted from the full-text index
+ ** while doing a linear scan of each individual index in turn.
+ **
+ ** As each term visited by the linear scans, a separate query for the
+ ** same term is performed. cksum3 is calculated based on the entries
+ ** extracted by these queries.
+ */
+ for(fts5MultiIterNew(p, pStruct, flags, 0, 0, 0, -1, 0, &pIter);
+ fts5MultiIterEof(p, pIter)==0;
+ fts5MultiIterNext(p, pIter, 0, 0)
+ ){
+ int n; /* Size of term in bytes */
+ i64 iPos = 0; /* Position read from poslist */
+ int iOff = 0; /* Offset within poslist */
+ i64 iRowid = fts5MultiIterRowid(pIter);
+ char *z = (char*)fts5MultiIterTerm(pIter, &n);
+
+ /* If this is a new term, query for it. Update cksum3 with the results. */
+ fts5TestTerm(p, &term, z, n, cksum2, &cksum3);
+
+ if( eDetail==FTS5_DETAIL_NONE ){
+ if( 0==fts5MultiIterIsEmpty(p, pIter) ){
+ cksum2 ^= sqlite3Fts5IndexEntryCksum(iRowid, 0, 0, -1, z, n);
+ }
+ }else{
+ poslist.n = 0;
+ fts5SegiterPoslist(p, &pIter->aSeg[pIter->aFirst[1].iFirst], 0, &poslist);
+ while( 0==sqlite3Fts5PoslistNext64(poslist.p, poslist.n, &iOff, &iPos) ){
+ int iCol = FTS5_POS2COLUMN(iPos);
+ int iTokOff = FTS5_POS2OFFSET(iPos);
+ cksum2 ^= sqlite3Fts5IndexEntryCksum(iRowid, iCol, iTokOff, -1, z, n);
+ }
+ }
+ }
+ fts5TestTerm(p, &term, 0, 0, cksum2, &cksum3);
+
+ fts5MultiIterFree(pIter);
+ if( p->rc==SQLITE_OK && cksum!=cksum2 ) p->rc = FTS5_CORRUPT;
+
+ fts5StructureRelease(pStruct);
+#ifdef SQLITE_DEBUG
+ fts5BufferFree(&term);
+#endif
+ fts5BufferFree(&poslist);
+ return fts5IndexReturn(p);
+}
+
+/*************************************************************************
+**************************************************************************
+** Below this point is the implementation of the fts5_decode() scalar
+** function only.
+*/
+
+/*
+** Decode a segment-data rowid from the %_data table. This function is
+** the opposite of macro FTS5_SEGMENT_ROWID().
+*/
+static void fts5DecodeRowid(
+ i64 iRowid, /* Rowid from %_data table */
+ int *piSegid, /* OUT: Segment id */
+ int *pbDlidx, /* OUT: Dlidx flag */
+ int *piHeight, /* OUT: Height */
+ int *piPgno /* OUT: Page number */
+){
+ *piPgno = (int)(iRowid & (((i64)1 << FTS5_DATA_PAGE_B) - 1));
+ iRowid >>= FTS5_DATA_PAGE_B;
+
+ *piHeight = (int)(iRowid & (((i64)1 << FTS5_DATA_HEIGHT_B) - 1));
+ iRowid >>= FTS5_DATA_HEIGHT_B;
+
+ *pbDlidx = (int)(iRowid & 0x0001);
+ iRowid >>= FTS5_DATA_DLI_B;
+
+ *piSegid = (int)(iRowid & (((i64)1 << FTS5_DATA_ID_B) - 1));
+}
+
+static void fts5DebugRowid(int *pRc, Fts5Buffer *pBuf, i64 iKey){
+ int iSegid, iHeight, iPgno, bDlidx; /* Rowid compenents */
+ fts5DecodeRowid(iKey, &iSegid, &bDlidx, &iHeight, &iPgno);
+
+ if( iSegid==0 ){
+ if( iKey==FTS5_AVERAGES_ROWID ){
+ sqlite3Fts5BufferAppendPrintf(pRc, pBuf, "{averages} ");
+ }else{
+ sqlite3Fts5BufferAppendPrintf(pRc, pBuf, "{structure}");
+ }
+ }
+ else{
+ sqlite3Fts5BufferAppendPrintf(pRc, pBuf, "{%ssegid=%d h=%d pgno=%d}",
+ bDlidx ? "dlidx " : "", iSegid, iHeight, iPgno
+ );
+ }
+}
+
+static void fts5DebugStructure(
+ int *pRc, /* IN/OUT: error code */
+ Fts5Buffer *pBuf,
+ Fts5Structure *p
+){
+ int iLvl, iSeg; /* Iterate through levels, segments */
+
+ for(iLvl=0; iLvl<p->nLevel; iLvl++){
+ Fts5StructureLevel *pLvl = &p->aLevel[iLvl];
+ sqlite3Fts5BufferAppendPrintf(pRc, pBuf,
+ " {lvl=%d nMerge=%d nSeg=%d", iLvl, pLvl->nMerge, pLvl->nSeg
+ );
+ for(iSeg=0; iSeg<pLvl->nSeg; iSeg++){
+ Fts5StructureSegment *pSeg = &pLvl->aSeg[iSeg];
+ sqlite3Fts5BufferAppendPrintf(pRc, pBuf, " {id=%d leaves=%d..%d}",
+ pSeg->iSegid, pSeg->pgnoFirst, pSeg->pgnoLast
+ );
+ }
+ sqlite3Fts5BufferAppendPrintf(pRc, pBuf, "}");
+ }
+}
+
+/*
+** This is part of the fts5_decode() debugging aid.
+**
+** Arguments pBlob/nBlob contain a serialized Fts5Structure object. This
+** function appends a human-readable representation of the same object
+** to the buffer passed as the second argument.
+*/
+static void fts5DecodeStructure(
+ int *pRc, /* IN/OUT: error code */
+ Fts5Buffer *pBuf,
+ const u8 *pBlob, int nBlob
+){
+ int rc; /* Return code */
+ Fts5Structure *p = 0; /* Decoded structure object */
+
+ rc = fts5StructureDecode(pBlob, nBlob, 0, &p);
+ if( rc!=SQLITE_OK ){
+ *pRc = rc;
+ return;
+ }
+
+ fts5DebugStructure(pRc, pBuf, p);
+ fts5StructureRelease(p);
+}
+
+/*
+** This is part of the fts5_decode() debugging aid.
+**
+** Arguments pBlob/nBlob contain an "averages" record. This function
+** appends a human-readable representation of record to the buffer passed
+** as the second argument.
+*/
+static void fts5DecodeAverages(
+ int *pRc, /* IN/OUT: error code */
+ Fts5Buffer *pBuf,
+ const u8 *pBlob, int nBlob
+){
+ int i = 0;
+ const char *zSpace = "";
+
+ while( i<nBlob ){
+ u64 iVal;
+ i += sqlite3Fts5GetVarint(&pBlob[i], &iVal);
+ sqlite3Fts5BufferAppendPrintf(pRc, pBuf, "%s%d", zSpace, (int)iVal);
+ zSpace = " ";
+ }
+}
+
+/*
+** Buffer (a/n) is assumed to contain a list of serialized varints. Read
+** each varint and append its string representation to buffer pBuf. Return
+** after either the input buffer is exhausted or a 0 value is read.
+**
+** The return value is the number of bytes read from the input buffer.
+*/
+static int fts5DecodePoslist(int *pRc, Fts5Buffer *pBuf, const u8 *a, int n){
+ int iOff = 0;
+ while( iOff<n ){
+ int iVal;
+ iOff += fts5GetVarint32(&a[iOff], iVal);
+ sqlite3Fts5BufferAppendPrintf(pRc, pBuf, " %d", iVal);
+ }
+ return iOff;
+}
+
+/*
+** The start of buffer (a/n) contains the start of a doclist. The doclist
+** may or may not finish within the buffer. This function appends a text
+** representation of the part of the doclist that is present to buffer
+** pBuf.
+**
+** The return value is the number of bytes read from the input buffer.
+*/
+static int fts5DecodeDoclist(int *pRc, Fts5Buffer *pBuf, const u8 *a, int n){
+ i64 iDocid = 0;
+ int iOff = 0;
+
+ if( n>0 ){
+ iOff = sqlite3Fts5GetVarint(a, (u64*)&iDocid);
+ sqlite3Fts5BufferAppendPrintf(pRc, pBuf, " id=%lld", iDocid);
+ }
+ while( iOff<n ){
+ int nPos;
+ int bDel;
+ iOff += fts5GetPoslistSize(&a[iOff], &nPos, &bDel);
+ sqlite3Fts5BufferAppendPrintf(pRc, pBuf, " nPos=%d%s", nPos, bDel?"*":"");
+ iOff += fts5DecodePoslist(pRc, pBuf, &a[iOff], MIN(n-iOff, nPos));
+ if( iOff<n ){
+ i64 iDelta;
+ iOff += sqlite3Fts5GetVarint(&a[iOff], (u64*)&iDelta);
+ iDocid += iDelta;
+ sqlite3Fts5BufferAppendPrintf(pRc, pBuf, " id=%lld", iDocid);
+ }
+ }
+
+ return iOff;
+}
+
+/*
+** This function is part of the fts5_decode() debugging function. It is
+** only ever used with detail=none tables.
+**
+** Buffer (pData/nData) contains a doclist in the format used by detail=none
+** tables. This function appends a human-readable version of that list to
+** buffer pBuf.
+**
+** If *pRc is other than SQLITE_OK when this function is called, it is a
+** no-op. If an OOM or other error occurs within this function, *pRc is
+** set to an SQLite error code before returning. The final state of buffer
+** pBuf is undefined in this case.
+*/
+static void fts5DecodeRowidList(
+ int *pRc, /* IN/OUT: Error code */
+ Fts5Buffer *pBuf, /* Buffer to append text to */
+ const u8 *pData, int nData /* Data to decode list-of-rowids from */
+){
+ int i = 0;
+ i64 iRowid = 0;
+
+ while( i<nData ){
+ const char *zApp = "";
+ u64 iVal;
+ i += sqlite3Fts5GetVarint(&pData[i], &iVal);
+ iRowid += iVal;
+
+ if( i<nData && pData[i]==0x00 ){
+ i++;
+ if( i<nData && pData[i]==0x00 ){
+ i++;
+ zApp = "+";
+ }else{
+ zApp = "*";
+ }
+ }
+
+ sqlite3Fts5BufferAppendPrintf(pRc, pBuf, " %lld%s", iRowid, zApp);
+ }
+}
+
+/*
+** The implementation of user-defined scalar function fts5_decode().
+*/
+static void fts5DecodeFunction(
+ sqlite3_context *pCtx, /* Function call context */
+ int nArg, /* Number of args (always 2) */
+ sqlite3_value **apVal /* Function arguments */
+){
+ i64 iRowid; /* Rowid for record being decoded */
+ int iSegid,iHeight,iPgno,bDlidx;/* Rowid components */
+ const u8 *aBlob; int n; /* Record to decode */
+ u8 *a = 0;
+ Fts5Buffer s; /* Build up text to return here */
+ int rc = SQLITE_OK; /* Return code */
+ int nSpace = 0;
+ int eDetailNone = (sqlite3_user_data(pCtx)!=0);
+
+ assert( nArg==2 );
+ UNUSED_PARAM(nArg);
+ memset(&s, 0, sizeof(Fts5Buffer));
+ iRowid = sqlite3_value_int64(apVal[0]);
+
+ /* Make a copy of the second argument (a blob) in aBlob[]. The aBlob[]
+ ** copy is followed by FTS5_DATA_ZERO_PADDING 0x00 bytes, which prevents
+ ** buffer overreads even if the record is corrupt. */
+ n = sqlite3_value_bytes(apVal[1]);
+ aBlob = sqlite3_value_blob(apVal[1]);
+ nSpace = n + FTS5_DATA_ZERO_PADDING;
+ a = (u8*)sqlite3Fts5MallocZero(&rc, nSpace);
+ if( a==0 ) goto decode_out;
+ memcpy(a, aBlob, n);
+
+
+ fts5DecodeRowid(iRowid, &iSegid, &bDlidx, &iHeight, &iPgno);
+
+ fts5DebugRowid(&rc, &s, iRowid);
+ if( bDlidx ){
+ Fts5Data dlidx;
+ Fts5DlidxLvl lvl;
+
+ dlidx.p = a;
+ dlidx.nn = n;
+
+ memset(&lvl, 0, sizeof(Fts5DlidxLvl));
+ lvl.pData = &dlidx;
+ lvl.iLeafPgno = iPgno;
+
+ for(fts5DlidxLvlNext(&lvl); lvl.bEof==0; fts5DlidxLvlNext(&lvl)){
+ sqlite3Fts5BufferAppendPrintf(&rc, &s,
+ " %d(%lld)", lvl.iLeafPgno, lvl.iRowid
+ );
+ }
+ }else if( iSegid==0 ){
+ if( iRowid==FTS5_AVERAGES_ROWID ){
+ fts5DecodeAverages(&rc, &s, a, n);
+ }else{
+ fts5DecodeStructure(&rc, &s, a, n);
+ }
+ }else if( eDetailNone ){
+ Fts5Buffer term; /* Current term read from page */
+ int szLeaf;
+ int iPgidxOff = szLeaf = fts5GetU16(&a[2]);
+ int iTermOff;
+ int nKeep = 0;
+ int iOff;
+
+ memset(&term, 0, sizeof(Fts5Buffer));
+
+ /* Decode any entries that occur before the first term. */
+ if( szLeaf<n ){
+ iPgidxOff += fts5GetVarint32(&a[iPgidxOff], iTermOff);
+ }else{
+ iTermOff = szLeaf;
+ }
+ fts5DecodeRowidList(&rc, &s, &a[4], iTermOff-4);
+
+ iOff = iTermOff;
+ while( iOff<szLeaf ){
+ int nAppend;
+
+ /* Read the term data for the next term*/
+ iOff += fts5GetVarint32(&a[iOff], nAppend);
+ term.n = nKeep;
+ fts5BufferAppendBlob(&rc, &term, nAppend, &a[iOff]);
+ sqlite3Fts5BufferAppendPrintf(
+ &rc, &s, " term=%.*s", term.n, (const char*)term.p
+ );
+ iOff += nAppend;
+
+ /* Figure out where the doclist for this term ends */
+ if( iPgidxOff<n ){
+ int nIncr;
+ iPgidxOff += fts5GetVarint32(&a[iPgidxOff], nIncr);
+ iTermOff += nIncr;
+ }else{
+ iTermOff = szLeaf;
+ }
+
+ fts5DecodeRowidList(&rc, &s, &a[iOff], iTermOff-iOff);
+ iOff = iTermOff;
+ if( iOff<szLeaf ){
+ iOff += fts5GetVarint32(&a[iOff], nKeep);
+ }
+ }
+
+ fts5BufferFree(&term);
+ }else{
+ Fts5Buffer term; /* Current term read from page */
+ int szLeaf; /* Offset of pgidx in a[] */
+ int iPgidxOff;
+ int iPgidxPrev = 0; /* Previous value read from pgidx */
+ int iTermOff = 0;
+ int iRowidOff = 0;
+ int iOff;
+ int nDoclist;
+
+ memset(&term, 0, sizeof(Fts5Buffer));
+
+ if( n<4 ){
+ sqlite3Fts5BufferSet(&rc, &s, 7, (const u8*)"corrupt");
+ goto decode_out;
+ }else{
+ iRowidOff = fts5GetU16(&a[0]);
+ iPgidxOff = szLeaf = fts5GetU16(&a[2]);
+ if( iPgidxOff<n ){
+ fts5GetVarint32(&a[iPgidxOff], iTermOff);
+ }
+ }
+
+ /* Decode the position list tail at the start of the page */
+ if( iRowidOff!=0 ){
+ iOff = iRowidOff;
+ }else if( iTermOff!=0 ){
+ iOff = iTermOff;
+ }else{
+ iOff = szLeaf;
+ }
+ fts5DecodePoslist(&rc, &s, &a[4], iOff-4);
+
+ /* Decode any more doclist data that appears on the page before the
+ ** first term. */
+ nDoclist = (iTermOff ? iTermOff : szLeaf) - iOff;
+ fts5DecodeDoclist(&rc, &s, &a[iOff], nDoclist);
+
+ while( iPgidxOff<n ){
+ int bFirst = (iPgidxOff==szLeaf); /* True for first term on page */
+ int nByte; /* Bytes of data */
+ int iEnd;
+
+ iPgidxOff += fts5GetVarint32(&a[iPgidxOff], nByte);
+ iPgidxPrev += nByte;
+ iOff = iPgidxPrev;
+
+ if( iPgidxOff<n ){
+ fts5GetVarint32(&a[iPgidxOff], nByte);
+ iEnd = iPgidxPrev + nByte;
+ }else{
+ iEnd = szLeaf;
+ }
+
+ if( bFirst==0 ){
+ iOff += fts5GetVarint32(&a[iOff], nByte);
+ term.n = nByte;
+ }
+ iOff += fts5GetVarint32(&a[iOff], nByte);
+ fts5BufferAppendBlob(&rc, &term, nByte, &a[iOff]);
+ iOff += nByte;
+
+ sqlite3Fts5BufferAppendPrintf(
+ &rc, &s, " term=%.*s", term.n, (const char*)term.p
+ );
+ iOff += fts5DecodeDoclist(&rc, &s, &a[iOff], iEnd-iOff);
+ }
+
+ fts5BufferFree(&term);
+ }
+
+ decode_out:
+ sqlite3_free(a);
+ if( rc==SQLITE_OK ){
+ sqlite3_result_text(pCtx, (const char*)s.p, s.n, SQLITE_TRANSIENT);
+ }else{
+ sqlite3_result_error_code(pCtx, rc);
+ }
+ fts5BufferFree(&s);
+}
+
+/*
+** The implementation of user-defined scalar function fts5_rowid().
+*/
+static void fts5RowidFunction(
+ sqlite3_context *pCtx, /* Function call context */
+ int nArg, /* Number of args (always 2) */
+ sqlite3_value **apVal /* Function arguments */
+){
+ const char *zArg;
+ if( nArg==0 ){
+ sqlite3_result_error(pCtx, "should be: fts5_rowid(subject, ....)", -1);
+ }else{
+ zArg = (const char*)sqlite3_value_text(apVal[0]);
+ if( 0==sqlite3_stricmp(zArg, "segment") ){
+ i64 iRowid;
+ int segid, pgno;
+ if( nArg!=3 ){
+ sqlite3_result_error(pCtx,
+ "should be: fts5_rowid('segment', segid, pgno))", -1
+ );
+ }else{
+ segid = sqlite3_value_int(apVal[1]);
+ pgno = sqlite3_value_int(apVal[2]);
+ iRowid = FTS5_SEGMENT_ROWID(segid, pgno);
+ sqlite3_result_int64(pCtx, iRowid);
+ }
+ }else{
+ sqlite3_result_error(pCtx,
+ "first arg to fts5_rowid() must be 'segment'" , -1
+ );
+ }
+ }
+}
+
+/*
+** This is called as part of registering the FTS5 module with database
+** connection db. It registers several user-defined scalar functions useful
+** with FTS5.
+**
+** If successful, SQLITE_OK is returned. If an error occurs, some other
+** SQLite error code is returned instead.
+*/
+static int sqlite3Fts5IndexInit(sqlite3 *db){
+ int rc = sqlite3_create_function(
+ db, "fts5_decode", 2, SQLITE_UTF8, 0, fts5DecodeFunction, 0, 0
+ );
+
+ if( rc==SQLITE_OK ){
+ rc = sqlite3_create_function(
+ db, "fts5_decode_none", 2,
+ SQLITE_UTF8, (void*)db, fts5DecodeFunction, 0, 0
+ );
+ }
+
+ if( rc==SQLITE_OK ){
+ rc = sqlite3_create_function(
+ db, "fts5_rowid", -1, SQLITE_UTF8, 0, fts5RowidFunction, 0, 0
+ );
+ }
+ return rc;
+}
+
+
+static int sqlite3Fts5IndexReset(Fts5Index *p){
+ assert( p->pStruct==0 || p->iStructVersion!=0 );
+ if( fts5IndexDataVersion(p)!=p->iStructVersion ){
+ fts5StructureInvalidate(p);
+ }
+ return fts5IndexReturn(p);
+}
+
+/*
+** 2014 Jun 09
+**
+** 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 is an SQLite module implementing full-text search.
+*/
+
+
+/* #include "fts5Int.h" */
+
+/*
+** This variable is set to false when running tests for which the on disk
+** structures should not be corrupt. Otherwise, true. If it is false, extra
+** assert() conditions in the fts5 code are activated - conditions that are
+** only true if it is guaranteed that the fts5 database is not corrupt.
+*/
+SQLITE_API int sqlite3_fts5_may_be_corrupt = 1;
+
+
+typedef struct Fts5Auxdata Fts5Auxdata;
+typedef struct Fts5Auxiliary Fts5Auxiliary;
+typedef struct Fts5Cursor Fts5Cursor;
+typedef struct Fts5Sorter Fts5Sorter;
+typedef struct Fts5Table Fts5Table;
+typedef struct Fts5TokenizerModule Fts5TokenizerModule;
+
+/*
+** NOTES ON TRANSACTIONS:
+**
+** SQLite invokes the following virtual table methods as transactions are
+** opened and closed by the user:
+**
+** xBegin(): Start of a new transaction.
+** xSync(): Initial part of two-phase commit.
+** xCommit(): Final part of two-phase commit.
+** xRollback(): Rollback the transaction.
+**
+** Anything that is required as part of a commit that may fail is performed
+** in the xSync() callback. Current versions of SQLite ignore any errors
+** returned by xCommit().
+**
+** And as sub-transactions are opened/closed:
+**
+** xSavepoint(int S): Open savepoint S.
+** xRelease(int S): Commit and close savepoint S.
+** xRollbackTo(int S): Rollback to start of savepoint S.
+**
+** During a write-transaction the fts5_index.c module may cache some data
+** in-memory. It is flushed to disk whenever xSync(), xRelease() or
+** xSavepoint() is called. And discarded whenever xRollback() or xRollbackTo()
+** is called.
+**
+** Additionally, if SQLITE_DEBUG is defined, an instance of the following
+** structure is used to record the current transaction state. This information
+** is not required, but it is used in the assert() statements executed by
+** function fts5CheckTransactionState() (see below).
+*/
+struct Fts5TransactionState {
+ int eState; /* 0==closed, 1==open, 2==synced */
+ int iSavepoint; /* Number of open savepoints (0 -> none) */
+};
+
+/*
+** A single object of this type is allocated when the FTS5 module is
+** registered with a database handle. It is used to store pointers to
+** all registered FTS5 extensions - tokenizers and auxiliary functions.
+*/
+struct Fts5Global {
+ fts5_api api; /* User visible part of object (see fts5.h) */
+ sqlite3 *db; /* Associated database connection */
+ i64 iNextId; /* Used to allocate unique cursor ids */
+ Fts5Auxiliary *pAux; /* First in list of all aux. functions */
+ Fts5TokenizerModule *pTok; /* First in list of all tokenizer modules */
+ Fts5TokenizerModule *pDfltTok; /* Default tokenizer module */
+ Fts5Cursor *pCsr; /* First in list of all open cursors */
+};
+
+/*
+** Each auxiliary function registered with the FTS5 module is represented
+** by an object of the following type. All such objects are stored as part
+** of the Fts5Global.pAux list.
+*/
+struct Fts5Auxiliary {
+ Fts5Global *pGlobal; /* Global context for this function */
+ char *zFunc; /* Function name (nul-terminated) */
+ void *pUserData; /* User-data pointer */
+ fts5_extension_function xFunc; /* Callback function */
+ void (*xDestroy)(void*); /* Destructor function */
+ Fts5Auxiliary *pNext; /* Next registered auxiliary function */
+};
+
+/*
+** Each tokenizer module registered with the FTS5 module is represented
+** by an object of the following type. All such objects are stored as part
+** of the Fts5Global.pTok list.
+*/
+struct Fts5TokenizerModule {
+ char *zName; /* Name of tokenizer */
+ void *pUserData; /* User pointer passed to xCreate() */
+ fts5_tokenizer x; /* Tokenizer functions */
+ void (*xDestroy)(void*); /* Destructor function */
+ Fts5TokenizerModule *pNext; /* Next registered tokenizer module */
+};
+
+/*
+** Virtual-table object.
+*/
+struct Fts5Table {
+ sqlite3_vtab base; /* Base class used by SQLite core */
+ Fts5Config *pConfig; /* Virtual table configuration */
+ Fts5Index *pIndex; /* Full-text index */
+ Fts5Storage *pStorage; /* Document store */
+ Fts5Global *pGlobal; /* Global (connection wide) data */
+ Fts5Cursor *pSortCsr; /* Sort data from this cursor */
+#ifdef SQLITE_DEBUG
+ struct Fts5TransactionState ts;
+#endif
+};
+
+struct Fts5MatchPhrase {
+ Fts5Buffer *pPoslist; /* Pointer to current poslist */
+ int nTerm; /* Size of phrase in terms */
+};
+
+/*
+** pStmt:
+** SELECT rowid, <fts> FROM <fts> ORDER BY +rank;
+**
+** aIdx[]:
+** There is one entry in the aIdx[] array for each phrase in the query,
+** the value of which is the offset within aPoslist[] following the last
+** byte of the position list for the corresponding phrase.
+*/
+struct Fts5Sorter {
+ sqlite3_stmt *pStmt;
+ i64 iRowid; /* Current rowid */
+ const u8 *aPoslist; /* Position lists for current row */
+ int nIdx; /* Number of entries in aIdx[] */
+ int aIdx[1]; /* Offsets into aPoslist for current row */
+};
+
+
+/*
+** Virtual-table cursor object.
+**
+** iSpecial:
+** If this is a 'special' query (refer to function fts5SpecialMatch()),
+** then this variable contains the result of the query.
+**
+** iFirstRowid, iLastRowid:
+** These variables are only used for FTS5_PLAN_MATCH cursors. Assuming the
+** cursor iterates in ascending order of rowids, iFirstRowid is the lower
+** limit of rowids to return, and iLastRowid the upper. In other words, the
+** WHERE clause in the user's query might have been:
+**
+** <tbl> MATCH <expr> AND rowid BETWEEN $iFirstRowid AND $iLastRowid
+**
+** If the cursor iterates in descending order of rowid, iFirstRowid
+** is the upper limit (i.e. the "first" rowid visited) and iLastRowid
+** the lower.
+*/
+struct Fts5Cursor {
+ sqlite3_vtab_cursor base; /* Base class used by SQLite core */
+ Fts5Cursor *pNext; /* Next cursor in Fts5Cursor.pCsr list */
+ int *aColumnSize; /* Values for xColumnSize() */
+ i64 iCsrId; /* Cursor id */
+
+ /* Zero from this point onwards on cursor reset */
+ int ePlan; /* FTS5_PLAN_XXX value */
+ int bDesc; /* True for "ORDER BY rowid DESC" queries */
+ i64 iFirstRowid; /* Return no rowids earlier than this */
+ i64 iLastRowid; /* Return no rowids later than this */
+ sqlite3_stmt *pStmt; /* Statement used to read %_content */
+ Fts5Expr *pExpr; /* Expression for MATCH queries */
+ Fts5Sorter *pSorter; /* Sorter for "ORDER BY rank" queries */
+ int csrflags; /* Mask of cursor flags (see below) */
+ i64 iSpecial; /* Result of special query */
+
+ /* "rank" function. Populated on demand from vtab.xColumn(). */
+ char *zRank; /* Custom rank function */
+ char *zRankArgs; /* Custom rank function args */
+ Fts5Auxiliary *pRank; /* Rank callback (or NULL) */
+ int nRankArg; /* Number of trailing arguments for rank() */
+ sqlite3_value **apRankArg; /* Array of trailing arguments */
+ sqlite3_stmt *pRankArgStmt; /* Origin of objects in apRankArg[] */
+
+ /* Auxiliary data storage */
+ Fts5Auxiliary *pAux; /* Currently executing extension function */
+ Fts5Auxdata *pAuxdata; /* First in linked list of saved aux-data */
+
+ /* Cache used by auxiliary functions xInst() and xInstCount() */
+ Fts5PoslistReader *aInstIter; /* One for each phrase */
+ int nInstAlloc; /* Size of aInst[] array (entries / 3) */
+ int nInstCount; /* Number of phrase instances */
+ int *aInst; /* 3 integers per phrase instance */
+};
+
+/*
+** Bits that make up the "idxNum" parameter passed indirectly by
+** xBestIndex() to xFilter().
+*/
+#define FTS5_BI_MATCH 0x0001 /* <tbl> MATCH ? */
+#define FTS5_BI_RANK 0x0002 /* rank MATCH ? */
+#define FTS5_BI_ROWID_EQ 0x0004 /* rowid == ? */
+#define FTS5_BI_ROWID_LE 0x0008 /* rowid <= ? */
+#define FTS5_BI_ROWID_GE 0x0010 /* rowid >= ? */
+
+#define FTS5_BI_ORDER_RANK 0x0020
+#define FTS5_BI_ORDER_ROWID 0x0040
+#define FTS5_BI_ORDER_DESC 0x0080
+
+/*
+** Values for Fts5Cursor.csrflags
+*/
+#define FTS5CSR_EOF 0x01
+#define FTS5CSR_REQUIRE_CONTENT 0x02
+#define FTS5CSR_REQUIRE_DOCSIZE 0x04
+#define FTS5CSR_REQUIRE_INST 0x08
+#define FTS5CSR_FREE_ZRANK 0x10
+#define FTS5CSR_REQUIRE_RESEEK 0x20
+#define FTS5CSR_REQUIRE_POSLIST 0x40
+
+#define BitFlagAllTest(x,y) (((x) & (y))==(y))
+#define BitFlagTest(x,y) (((x) & (y))!=0)
+
+
+/*
+** Macros to Set(), Clear() and Test() cursor flags.
+*/
+#define CsrFlagSet(pCsr, flag) ((pCsr)->csrflags |= (flag))
+#define CsrFlagClear(pCsr, flag) ((pCsr)->csrflags &= ~(flag))
+#define CsrFlagTest(pCsr, flag) ((pCsr)->csrflags & (flag))
+
+struct Fts5Auxdata {
+ Fts5Auxiliary *pAux; /* Extension to which this belongs */
+ void *pPtr; /* Pointer value */
+ void(*xDelete)(void*); /* Destructor */
+ Fts5Auxdata *pNext; /* Next object in linked list */
+};
+
+#ifdef SQLITE_DEBUG
+#define FTS5_BEGIN 1
+#define FTS5_SYNC 2
+#define FTS5_COMMIT 3
+#define FTS5_ROLLBACK 4
+#define FTS5_SAVEPOINT 5
+#define FTS5_RELEASE 6
+#define FTS5_ROLLBACKTO 7
+static void fts5CheckTransactionState(Fts5Table *p, int op, int iSavepoint){
+ switch( op ){
+ case FTS5_BEGIN:
+ assert( p->ts.eState==0 );
+ p->ts.eState = 1;
+ p->ts.iSavepoint = -1;
+ break;
+
+ case FTS5_SYNC:
+ assert( p->ts.eState==1 );
+ p->ts.eState = 2;
+ break;
+
+ case FTS5_COMMIT:
+ assert( p->ts.eState==2 );
+ p->ts.eState = 0;
+ break;
+
+ case FTS5_ROLLBACK:
+ assert( p->ts.eState==1 || p->ts.eState==2 || p->ts.eState==0 );
+ p->ts.eState = 0;
+ break;
+
+ case FTS5_SAVEPOINT:
+ assert( p->ts.eState==1 );
+ assert( iSavepoint>=0 );
+ assert( iSavepoint>p->ts.iSavepoint );
+ p->ts.iSavepoint = iSavepoint;
+ break;
+
+ case FTS5_RELEASE:
+ assert( p->ts.eState==1 );
+ assert( iSavepoint>=0 );
+ assert( iSavepoint<=p->ts.iSavepoint );
+ p->ts.iSavepoint = iSavepoint-1;
+ break;
+
+ case FTS5_ROLLBACKTO:
+ assert( p->ts.eState==1 );
+ assert( iSavepoint>=0 );
+ assert( iSavepoint<=p->ts.iSavepoint );
+ p->ts.iSavepoint = iSavepoint;
+ break;
+ }
+}
+#else
+# define fts5CheckTransactionState(x,y,z)
+#endif
+
+/*
+** Return true if pTab is a contentless table.
+*/
+static int fts5IsContentless(Fts5Table *pTab){
+ return pTab->pConfig->eContent==FTS5_CONTENT_NONE;
+}
+
+/*
+** Delete a virtual table handle allocated by fts5InitVtab().
+*/
+static void fts5FreeVtab(Fts5Table *pTab){
+ if( pTab ){
+ sqlite3Fts5IndexClose(pTab->pIndex);
+ sqlite3Fts5StorageClose(pTab->pStorage);
+ sqlite3Fts5ConfigFree(pTab->pConfig);
+ sqlite3_free(pTab);
+ }
+}
+
+/*
+** The xDisconnect() virtual table method.
+*/
+static int fts5DisconnectMethod(sqlite3_vtab *pVtab){
+ fts5FreeVtab((Fts5Table*)pVtab);
+ return SQLITE_OK;
+}
+
+/*
+** The xDestroy() virtual table method.
+*/
+static int fts5DestroyMethod(sqlite3_vtab *pVtab){
+ Fts5Table *pTab = (Fts5Table*)pVtab;
+ int rc = sqlite3Fts5DropAll(pTab->pConfig);
+ if( rc==SQLITE_OK ){
+ fts5FreeVtab((Fts5Table*)pVtab);
+ }
+ return rc;
+}
+
+/*
+** This function is the implementation of both the xConnect and xCreate
+** methods of the FTS3 virtual table.
+**
+** The argv[] array contains the following:
+**
+** argv[0] -> module name ("fts5")
+** argv[1] -> database name
+** argv[2] -> table name
+** argv[...] -> "column name" and other module argument fields.
+*/
+static int fts5InitVtab(
+ int bCreate, /* True for xCreate, false for xConnect */
+ sqlite3 *db, /* The SQLite database connection */
+ void *pAux, /* Hash table containing tokenizers */
+ int argc, /* Number of elements in argv array */
+ const char * const *argv, /* xCreate/xConnect argument array */
+ sqlite3_vtab **ppVTab, /* Write the resulting vtab structure here */
+ char **pzErr /* Write any error message here */
+){
+ Fts5Global *pGlobal = (Fts5Global*)pAux;
+ const char **azConfig = (const char**)argv;
+ int rc = SQLITE_OK; /* Return code */
+ Fts5Config *pConfig = 0; /* Results of parsing argc/argv */
+ Fts5Table *pTab = 0; /* New virtual table object */
+
+ /* Allocate the new vtab object and parse the configuration */
+ pTab = (Fts5Table*)sqlite3Fts5MallocZero(&rc, sizeof(Fts5Table));
+ if( rc==SQLITE_OK ){
+ rc = sqlite3Fts5ConfigParse(pGlobal, db, argc, azConfig, &pConfig, pzErr);
+ assert( (rc==SQLITE_OK && *pzErr==0) || pConfig==0 );
+ }
+ if( rc==SQLITE_OK ){
+ pTab->pConfig = pConfig;
+ pTab->pGlobal = pGlobal;
+ }
+
+ /* Open the index sub-system */
+ if( rc==SQLITE_OK ){
+ rc = sqlite3Fts5IndexOpen(pConfig, bCreate, &pTab->pIndex, pzErr);
+ }
+
+ /* Open the storage sub-system */
+ if( rc==SQLITE_OK ){
+ rc = sqlite3Fts5StorageOpen(
+ pConfig, pTab->pIndex, bCreate, &pTab->pStorage, pzErr
+ );
+ }
+
+ /* Call sqlite3_declare_vtab() */
+ if( rc==SQLITE_OK ){
+ rc = sqlite3Fts5ConfigDeclareVtab(pConfig);
+ }
+
+ /* Load the initial configuration */
+ if( rc==SQLITE_OK ){
+ assert( pConfig->pzErrmsg==0 );
+ pConfig->pzErrmsg = pzErr;
+ rc = sqlite3Fts5IndexLoadConfig(pTab->pIndex);
+ sqlite3Fts5IndexRollback(pTab->pIndex);
+ pConfig->pzErrmsg = 0;
+ }
+
+ if( rc!=SQLITE_OK ){
+ fts5FreeVtab(pTab);
+ pTab = 0;
+ }else if( bCreate ){
+ fts5CheckTransactionState(pTab, FTS5_BEGIN, 0);
+ }
+ *ppVTab = (sqlite3_vtab*)pTab;
+ return rc;
+}
+
+/*
+** The xConnect() and xCreate() methods for the virtual table. All the
+** work is done in function fts5InitVtab().
+*/
+static int fts5ConnectMethod(
+ sqlite3 *db, /* Database connection */
+ void *pAux, /* Pointer to tokenizer hash table */
+ int argc, /* Number of elements in argv array */
+ const char * const *argv, /* xCreate/xConnect argument array */
+ sqlite3_vtab **ppVtab, /* OUT: New sqlite3_vtab object */
+ char **pzErr /* OUT: sqlite3_malloc'd error message */
+){
+ return fts5InitVtab(0, db, pAux, argc, argv, ppVtab, pzErr);
+}
+static int fts5CreateMethod(
+ sqlite3 *db, /* Database connection */
+ void *pAux, /* Pointer to tokenizer hash table */
+ int argc, /* Number of elements in argv array */
+ const char * const *argv, /* xCreate/xConnect argument array */
+ sqlite3_vtab **ppVtab, /* OUT: New sqlite3_vtab object */
+ char **pzErr /* OUT: sqlite3_malloc'd error message */
+){
+ return fts5InitVtab(1, db, pAux, argc, argv, ppVtab, pzErr);
+}
+
+/*
+** The different query plans.
+*/
+#define FTS5_PLAN_MATCH 1 /* (<tbl> MATCH ?) */
+#define FTS5_PLAN_SOURCE 2 /* A source cursor for SORTED_MATCH */
+#define FTS5_PLAN_SPECIAL 3 /* An internal query */
+#define FTS5_PLAN_SORTED_MATCH 4 /* (<tbl> MATCH ? ORDER BY rank) */
+#define FTS5_PLAN_SCAN 5 /* No usable constraint */
+#define FTS5_PLAN_ROWID 6 /* (rowid = ?) */
+
+/*
+** Set the SQLITE_INDEX_SCAN_UNIQUE flag in pIdxInfo->flags. Unless this
+** extension is currently being used by a version of SQLite too old to
+** support index-info flags. In that case this function is a no-op.
+*/
+static void fts5SetUniqueFlag(sqlite3_index_info *pIdxInfo){
+#if SQLITE_VERSION_NUMBER>=3008012
+#ifndef SQLITE_CORE
+ if( sqlite3_libversion_number()>=3008012 )
+#endif
+ {
+ pIdxInfo->idxFlags |= SQLITE_INDEX_SCAN_UNIQUE;
+ }
+#endif
+}
+
+/*
+** Implementation of the xBestIndex method for FTS5 tables. Within the
+** WHERE constraint, it searches for the following:
+**
+** 1. A MATCH constraint against the special column.
+** 2. A MATCH constraint against the "rank" column.
+** 3. An == constraint against the rowid column.
+** 4. A < or <= constraint against the rowid column.
+** 5. A > or >= constraint against the rowid column.
+**
+** Within the ORDER BY, either:
+**
+** 5. ORDER BY rank [ASC|DESC]
+** 6. ORDER BY rowid [ASC|DESC]
+**
+** Costs are assigned as follows:
+**
+** a) If an unusable MATCH operator is present in the WHERE clause, the
+** cost is unconditionally set to 1e50 (a really big number).
+**
+** a) If a MATCH operator is present, the cost depends on the other
+** constraints also present. As follows:
+**
+** * No other constraints: cost=1000.0
+** * One rowid range constraint: cost=750.0
+** * Both rowid range constraints: cost=500.0
+** * An == rowid constraint: cost=100.0
+**
+** b) Otherwise, if there is no MATCH:
+**
+** * No other constraints: cost=1000000.0
+** * One rowid range constraint: cost=750000.0
+** * Both rowid range constraints: cost=250000.0
+** * An == rowid constraint: cost=10.0
+**
+** Costs are not modified by the ORDER BY clause.
+*/
+static int fts5BestIndexMethod(sqlite3_vtab *pVTab, sqlite3_index_info *pInfo){
+ Fts5Table *pTab = (Fts5Table*)pVTab;
+ Fts5Config *pConfig = pTab->pConfig;
+ int idxFlags = 0; /* Parameter passed through to xFilter() */
+ int bHasMatch;
+ int iNext;
+ int i;
+
+ struct Constraint {
+ int op; /* Mask against sqlite3_index_constraint.op */
+ int fts5op; /* FTS5 mask for idxFlags */
+ int iCol; /* 0==rowid, 1==tbl, 2==rank */
+ int omit; /* True to omit this if found */
+ int iConsIndex; /* Index in pInfo->aConstraint[] */
+ } aConstraint[] = {
+ {SQLITE_INDEX_CONSTRAINT_MATCH|SQLITE_INDEX_CONSTRAINT_EQ,
+ FTS5_BI_MATCH, 1, 1, -1},
+ {SQLITE_INDEX_CONSTRAINT_MATCH|SQLITE_INDEX_CONSTRAINT_EQ,
+ FTS5_BI_RANK, 2, 1, -1},
+ {SQLITE_INDEX_CONSTRAINT_EQ, FTS5_BI_ROWID_EQ, 0, 0, -1},
+ {SQLITE_INDEX_CONSTRAINT_LT|SQLITE_INDEX_CONSTRAINT_LE,
+ FTS5_BI_ROWID_LE, 0, 0, -1},
+ {SQLITE_INDEX_CONSTRAINT_GT|SQLITE_INDEX_CONSTRAINT_GE,
+ FTS5_BI_ROWID_GE, 0, 0, -1},
+ };
+
+ int aColMap[3];
+ aColMap[0] = -1;
+ aColMap[1] = pConfig->nCol;
+ aColMap[2] = pConfig->nCol+1;
+
+ /* Set idxFlags flags for all WHERE clause terms that will be used. */
+ for(i=0; i<pInfo->nConstraint; i++){
+ struct sqlite3_index_constraint *p = &pInfo->aConstraint[i];
+ int j;
+ for(j=0; j<ArraySize(aConstraint); j++){
+ struct Constraint *pC = &aConstraint[j];
+ if( p->iColumn==aColMap[pC->iCol] && p->op & pC->op ){
+ if( p->usable ){
+ pC->iConsIndex = i;
+ idxFlags |= pC->fts5op;
+ }else if( j==0 ){
+ /* As there exists an unusable MATCH constraint this is an
+ ** unusable plan. Set a prohibitively high cost. */
+ pInfo->estimatedCost = 1e50;
+ return SQLITE_OK;
+ }
+ }
+ }
+ }
+
+ /* Set idxFlags flags for the ORDER BY clause */
+ if( pInfo->nOrderBy==1 ){
+ int iSort = pInfo->aOrderBy[0].iColumn;
+ if( iSort==(pConfig->nCol+1) && BitFlagTest(idxFlags, FTS5_BI_MATCH) ){
+ idxFlags |= FTS5_BI_ORDER_RANK;
+ }else if( iSort==-1 ){
+ idxFlags |= FTS5_BI_ORDER_ROWID;
+ }
+ if( BitFlagTest(idxFlags, FTS5_BI_ORDER_RANK|FTS5_BI_ORDER_ROWID) ){
+ pInfo->orderByConsumed = 1;
+ if( pInfo->aOrderBy[0].desc ){
+ idxFlags |= FTS5_BI_ORDER_DESC;
+ }
+ }
+ }
+
+ /* Calculate the estimated cost based on the flags set in idxFlags. */
+ bHasMatch = BitFlagTest(idxFlags, FTS5_BI_MATCH);
+ if( BitFlagTest(idxFlags, FTS5_BI_ROWID_EQ) ){
+ pInfo->estimatedCost = bHasMatch ? 100.0 : 10.0;
+ if( bHasMatch==0 ) fts5SetUniqueFlag(pInfo);
+ }else if( BitFlagAllTest(idxFlags, FTS5_BI_ROWID_LE|FTS5_BI_ROWID_GE) ){
+ pInfo->estimatedCost = bHasMatch ? 500.0 : 250000.0;
+ }else if( BitFlagTest(idxFlags, FTS5_BI_ROWID_LE|FTS5_BI_ROWID_GE) ){
+ pInfo->estimatedCost = bHasMatch ? 750.0 : 750000.0;
+ }else{
+ pInfo->estimatedCost = bHasMatch ? 1000.0 : 1000000.0;
+ }
+
+ /* Assign argvIndex values to each constraint in use. */
+ iNext = 1;
+ for(i=0; i<ArraySize(aConstraint); i++){
+ struct Constraint *pC = &aConstraint[i];
+ if( pC->iConsIndex>=0 ){
+ pInfo->aConstraintUsage[pC->iConsIndex].argvIndex = iNext++;
+ pInfo->aConstraintUsage[pC->iConsIndex].omit = (unsigned char)pC->omit;
+ }
+ }
+
+ pInfo->idxNum = idxFlags;
+ return SQLITE_OK;
+}
+
+static int fts5NewTransaction(Fts5Table *pTab){
+ Fts5Cursor *pCsr;
+ for(pCsr=pTab->pGlobal->pCsr; pCsr; pCsr=pCsr->pNext){
+ if( pCsr->base.pVtab==(sqlite3_vtab*)pTab ) return SQLITE_OK;
+ }
+ return sqlite3Fts5StorageReset(pTab->pStorage);
+}
+
+/*
+** Implementation of xOpen method.
+*/
+static int fts5OpenMethod(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCsr){
+ Fts5Table *pTab = (Fts5Table*)pVTab;
+ Fts5Config *pConfig = pTab->pConfig;
+ Fts5Cursor *pCsr = 0; /* New cursor object */
+ int nByte; /* Bytes of space to allocate */
+ int rc; /* Return code */
+
+ rc = fts5NewTransaction(pTab);
+ if( rc==SQLITE_OK ){
+ nByte = sizeof(Fts5Cursor) + pConfig->nCol * sizeof(int);
+ pCsr = (Fts5Cursor*)sqlite3_malloc(nByte);
+ if( pCsr ){
+ Fts5Global *pGlobal = pTab->pGlobal;
+ memset(pCsr, 0, nByte);
+ pCsr->aColumnSize = (int*)&pCsr[1];
+ pCsr->pNext = pGlobal->pCsr;
+ pGlobal->pCsr = pCsr;
+ pCsr->iCsrId = ++pGlobal->iNextId;
+ }else{
+ rc = SQLITE_NOMEM;
+ }
+ }
+ *ppCsr = (sqlite3_vtab_cursor*)pCsr;
+ return rc;
+}
+
+static int fts5StmtType(Fts5Cursor *pCsr){
+ if( pCsr->ePlan==FTS5_PLAN_SCAN ){
+ return (pCsr->bDesc) ? FTS5_STMT_SCAN_DESC : FTS5_STMT_SCAN_ASC;
+ }
+ return FTS5_STMT_LOOKUP;
+}
+
+/*
+** This function is called after the cursor passed as the only argument
+** is moved to point at a different row. It clears all cached data
+** specific to the previous row stored by the cursor object.
+*/
+static void fts5CsrNewrow(Fts5Cursor *pCsr){
+ CsrFlagSet(pCsr,
+ FTS5CSR_REQUIRE_CONTENT
+ | FTS5CSR_REQUIRE_DOCSIZE
+ | FTS5CSR_REQUIRE_INST
+ | FTS5CSR_REQUIRE_POSLIST
+ );
+}
+
+static void fts5FreeCursorComponents(Fts5Cursor *pCsr){
+ Fts5Table *pTab = (Fts5Table*)(pCsr->base.pVtab);
+ Fts5Auxdata *pData;
+ Fts5Auxdata *pNext;
+
+ sqlite3_free(pCsr->aInstIter);
+ sqlite3_free(pCsr->aInst);
+ if( pCsr->pStmt ){
+ int eStmt = fts5StmtType(pCsr);
+ sqlite3Fts5StorageStmtRelease(pTab->pStorage, eStmt, pCsr->pStmt);
+ }
+ if( pCsr->pSorter ){
+ Fts5Sorter *pSorter = pCsr->pSorter;
+ sqlite3_finalize(pSorter->pStmt);
+ sqlite3_free(pSorter);
+ }
+
+ if( pCsr->ePlan!=FTS5_PLAN_SOURCE ){
+ sqlite3Fts5ExprFree(pCsr->pExpr);
+ }
+
+ for(pData=pCsr->pAuxdata; pData; pData=pNext){
+ pNext = pData->pNext;
+ if( pData->xDelete ) pData->xDelete(pData->pPtr);
+ sqlite3_free(pData);
+ }
+
+ sqlite3_finalize(pCsr->pRankArgStmt);
+ sqlite3_free(pCsr->apRankArg);
+
+ if( CsrFlagTest(pCsr, FTS5CSR_FREE_ZRANK) ){
+ sqlite3_free(pCsr->zRank);
+ sqlite3_free(pCsr->zRankArgs);
+ }
+
+ memset(&pCsr->ePlan, 0, sizeof(Fts5Cursor) - ((u8*)&pCsr->ePlan - (u8*)pCsr));
+}
+
+
+/*
+** Close the cursor. For additional information see the documentation
+** on the xClose method of the virtual table interface.
+*/
+static int fts5CloseMethod(sqlite3_vtab_cursor *pCursor){
+ if( pCursor ){
+ Fts5Table *pTab = (Fts5Table*)(pCursor->pVtab);
+ Fts5Cursor *pCsr = (Fts5Cursor*)pCursor;
+ Fts5Cursor **pp;
+
+ fts5FreeCursorComponents(pCsr);
+ /* Remove the cursor from the Fts5Global.pCsr list */
+ for(pp=&pTab->pGlobal->pCsr; (*pp)!=pCsr; pp=&(*pp)->pNext);
+ *pp = pCsr->pNext;
+
+ sqlite3_free(pCsr);
+ }
+ return SQLITE_OK;
+}
+
+static int fts5SorterNext(Fts5Cursor *pCsr){
+ Fts5Sorter *pSorter = pCsr->pSorter;
+ int rc;
+
+ rc = sqlite3_step(pSorter->pStmt);
+ if( rc==SQLITE_DONE ){
+ rc = SQLITE_OK;
+ CsrFlagSet(pCsr, FTS5CSR_EOF);
+ }else if( rc==SQLITE_ROW ){
+ const u8 *a;
+ const u8 *aBlob;
+ int nBlob;
+ int i;
+ int iOff = 0;
+ rc = SQLITE_OK;
+
+ pSorter->iRowid = sqlite3_column_int64(pSorter->pStmt, 0);
+ nBlob = sqlite3_column_bytes(pSorter->pStmt, 1);
+ aBlob = a = sqlite3_column_blob(pSorter->pStmt, 1);
+
+ /* nBlob==0 in detail=none mode. */
+ if( nBlob>0 ){
+ for(i=0; i<(pSorter->nIdx-1); i++){
+ int iVal;
+ a += fts5GetVarint32(a, iVal);
+ iOff += iVal;
+ pSorter->aIdx[i] = iOff;
+ }
+ pSorter->aIdx[i] = &aBlob[nBlob] - a;
+ pSorter->aPoslist = a;
+ }
+
+ fts5CsrNewrow(pCsr);
+ }
+
+ return rc;
+}
+
+
+/*
+** Set the FTS5CSR_REQUIRE_RESEEK flag on all FTS5_PLAN_MATCH cursors
+** open on table pTab.
+*/
+static void fts5TripCursors(Fts5Table *pTab){
+ Fts5Cursor *pCsr;
+ for(pCsr=pTab->pGlobal->pCsr; pCsr; pCsr=pCsr->pNext){
+ if( pCsr->ePlan==FTS5_PLAN_MATCH
+ && pCsr->base.pVtab==(sqlite3_vtab*)pTab
+ ){
+ CsrFlagSet(pCsr, FTS5CSR_REQUIRE_RESEEK);
+ }
+ }
+}
+
+/*
+** If the REQUIRE_RESEEK flag is set on the cursor passed as the first
+** argument, close and reopen all Fts5IndexIter iterators that the cursor
+** is using. Then attempt to move the cursor to a rowid equal to or laster
+** (in the cursors sort order - ASC or DESC) than the current rowid.
+**
+** If the new rowid is not equal to the old, set output parameter *pbSkip
+** to 1 before returning. Otherwise, leave it unchanged.
+**
+** Return SQLITE_OK if successful or if no reseek was required, or an
+** error code if an error occurred.
+*/
+static int fts5CursorReseek(Fts5Cursor *pCsr, int *pbSkip){
+ int rc = SQLITE_OK;
+ assert( *pbSkip==0 );
+ if( CsrFlagTest(pCsr, FTS5CSR_REQUIRE_RESEEK) ){
+ Fts5Table *pTab = (Fts5Table*)(pCsr->base.pVtab);
+ int bDesc = pCsr->bDesc;
+ i64 iRowid = sqlite3Fts5ExprRowid(pCsr->pExpr);
+
+ rc = sqlite3Fts5ExprFirst(pCsr->pExpr, pTab->pIndex, iRowid, bDesc);
+ if( rc==SQLITE_OK && iRowid!=sqlite3Fts5ExprRowid(pCsr->pExpr) ){
+ *pbSkip = 1;
+ }
+
+ CsrFlagClear(pCsr, FTS5CSR_REQUIRE_RESEEK);
+ fts5CsrNewrow(pCsr);
+ if( sqlite3Fts5ExprEof(pCsr->pExpr) ){
+ CsrFlagSet(pCsr, FTS5CSR_EOF);
+ *pbSkip = 1;
+ }
+ }
+ return rc;
+}
+
+
+/*
+** Advance the cursor to the next row in the table that matches the
+** search criteria.
+**
+** Return SQLITE_OK if nothing goes wrong. SQLITE_OK is returned
+** even if we reach end-of-file. The fts5EofMethod() will be called
+** subsequently to determine whether or not an EOF was hit.
+*/
+static int fts5NextMethod(sqlite3_vtab_cursor *pCursor){
+ Fts5Cursor *pCsr = (Fts5Cursor*)pCursor;
+ int rc;
+
+ assert( (pCsr->ePlan<3)==
+ (pCsr->ePlan==FTS5_PLAN_MATCH || pCsr->ePlan==FTS5_PLAN_SOURCE)
+ );
+ assert( !CsrFlagTest(pCsr, FTS5CSR_EOF) );
+
+ if( pCsr->ePlan<3 ){
+ int bSkip = 0;
+ if( (rc = fts5CursorReseek(pCsr, &bSkip)) || bSkip ) return rc;
+ rc = sqlite3Fts5ExprNext(pCsr->pExpr, pCsr->iLastRowid);
+ CsrFlagSet(pCsr, sqlite3Fts5ExprEof(pCsr->pExpr));
+ fts5CsrNewrow(pCsr);
+ }else{
+ switch( pCsr->ePlan ){
+ case FTS5_PLAN_SPECIAL: {
+ CsrFlagSet(pCsr, FTS5CSR_EOF);
+ rc = SQLITE_OK;
+ break;
+ }
+
+ case FTS5_PLAN_SORTED_MATCH: {
+ rc = fts5SorterNext(pCsr);
+ break;
+ }
+
+ default:
+ rc = sqlite3_step(pCsr->pStmt);
+ if( rc!=SQLITE_ROW ){
+ CsrFlagSet(pCsr, FTS5CSR_EOF);
+ rc = sqlite3_reset(pCsr->pStmt);
+ }else{
+ rc = SQLITE_OK;
+ }
+ break;
+ }
+ }
+
+ return rc;
+}
+
+
+static int fts5PrepareStatement(
+ sqlite3_stmt **ppStmt,
+ Fts5Config *pConfig,
+ const char *zFmt,
+ ...
+){
+ sqlite3_stmt *pRet = 0;
+ int rc;
+ char *zSql;
+ va_list ap;
+
+ va_start(ap, zFmt);
+ zSql = sqlite3_vmprintf(zFmt, ap);
+ if( zSql==0 ){
+ rc = SQLITE_NOMEM;
+ }else{
+ rc = sqlite3_prepare_v2(pConfig->db, zSql, -1, &pRet, 0);
+ if( rc!=SQLITE_OK ){
+ *pConfig->pzErrmsg = sqlite3_mprintf("%s", sqlite3_errmsg(pConfig->db));
+ }
+ sqlite3_free(zSql);
+ }
+
+ va_end(ap);
+ *ppStmt = pRet;
+ return rc;
+}
+
+static int fts5CursorFirstSorted(Fts5Table *pTab, Fts5Cursor *pCsr, int bDesc){
+ Fts5Config *pConfig = pTab->pConfig;
+ Fts5Sorter *pSorter;
+ int nPhrase;
+ int nByte;
+ int rc;
+ const char *zRank = pCsr->zRank;
+ const char *zRankArgs = pCsr->zRankArgs;
+
+ nPhrase = sqlite3Fts5ExprPhraseCount(pCsr->pExpr);
+ nByte = sizeof(Fts5Sorter) + sizeof(int) * (nPhrase-1);
+ pSorter = (Fts5Sorter*)sqlite3_malloc(nByte);
+ if( pSorter==0 ) return SQLITE_NOMEM;
+ memset(pSorter, 0, nByte);
+ pSorter->nIdx = nPhrase;
+
+ /* TODO: It would be better to have some system for reusing statement
+ ** handles here, rather than preparing a new one for each query. But that
+ ** is not possible as SQLite reference counts the virtual table objects.
+ ** And since the statement required here reads from this very virtual
+ ** table, saving it creates a circular reference.
+ **
+ ** If SQLite a built-in statement cache, this wouldn't be a problem. */
+ rc = fts5PrepareStatement(&pSorter->pStmt, pConfig,
+ "SELECT rowid, rank FROM %Q.%Q ORDER BY %s(%s%s%s) %s",
+ pConfig->zDb, pConfig->zName, zRank, pConfig->zName,
+ (zRankArgs ? ", " : ""),
+ (zRankArgs ? zRankArgs : ""),
+ bDesc ? "DESC" : "ASC"
+ );
+
+ pCsr->pSorter = pSorter;
+ if( rc==SQLITE_OK ){
+ assert( pTab->pSortCsr==0 );
+ pTab->pSortCsr = pCsr;
+ rc = fts5SorterNext(pCsr);
+ pTab->pSortCsr = 0;
+ }
+
+ if( rc!=SQLITE_OK ){
+ sqlite3_finalize(pSorter->pStmt);
+ sqlite3_free(pSorter);
+ pCsr->pSorter = 0;
+ }
+
+ return rc;
+}
+
+static int fts5CursorFirst(Fts5Table *pTab, Fts5Cursor *pCsr, int bDesc){
+ int rc;
+ Fts5Expr *pExpr = pCsr->pExpr;
+ rc = sqlite3Fts5ExprFirst(pExpr, pTab->pIndex, pCsr->iFirstRowid, bDesc);
+ if( sqlite3Fts5ExprEof(pExpr) ){
+ CsrFlagSet(pCsr, FTS5CSR_EOF);
+ }
+ fts5CsrNewrow(pCsr);
+ return rc;
+}
+
+/*
+** Process a "special" query. A special query is identified as one with a
+** MATCH expression that begins with a '*' character. The remainder of
+** the text passed to the MATCH operator are used as the special query
+** parameters.
+*/
+static int fts5SpecialMatch(
+ Fts5Table *pTab,
+ Fts5Cursor *pCsr,
+ const char *zQuery
+){
+ int rc = SQLITE_OK; /* Return code */
+ const char *z = zQuery; /* Special query text */
+ int n; /* Number of bytes in text at z */
+
+ while( z[0]==' ' ) z++;
+ for(n=0; z[n] && z[n]!=' '; n++);
+
+ assert( pTab->base.zErrMsg==0 );
+ pCsr->ePlan = FTS5_PLAN_SPECIAL;
+
+ if( 0==sqlite3_strnicmp("reads", z, n) ){
+ pCsr->iSpecial = sqlite3Fts5IndexReads(pTab->pIndex);
+ }
+ else if( 0==sqlite3_strnicmp("id", z, n) ){
+ pCsr->iSpecial = pCsr->iCsrId;
+ }
+ else{
+ /* An unrecognized directive. Return an error message. */
+ pTab->base.zErrMsg = sqlite3_mprintf("unknown special query: %.*s", n, z);
+ rc = SQLITE_ERROR;
+ }
+
+ return rc;
+}
+
+/*
+** Search for an auxiliary function named zName that can be used with table
+** pTab. If one is found, return a pointer to the corresponding Fts5Auxiliary
+** structure. Otherwise, if no such function exists, return NULL.
+*/
+static Fts5Auxiliary *fts5FindAuxiliary(Fts5Table *pTab, const char *zName){
+ Fts5Auxiliary *pAux;
+
+ for(pAux=pTab->pGlobal->pAux; pAux; pAux=pAux->pNext){
+ if( sqlite3_stricmp(zName, pAux->zFunc)==0 ) return pAux;
+ }
+
+ /* No function of the specified name was found. Return 0. */
+ return 0;
+}
+
+
+static int fts5FindRankFunction(Fts5Cursor *pCsr){
+ Fts5Table *pTab = (Fts5Table*)(pCsr->base.pVtab);
+ Fts5Config *pConfig = pTab->pConfig;
+ int rc = SQLITE_OK;
+ Fts5Auxiliary *pAux = 0;
+ const char *zRank = pCsr->zRank;
+ const char *zRankArgs = pCsr->zRankArgs;
+
+ if( zRankArgs ){
+ char *zSql = sqlite3Fts5Mprintf(&rc, "SELECT %s", zRankArgs);
+ if( zSql ){
+ sqlite3_stmt *pStmt = 0;
+ rc = sqlite3_prepare_v2(pConfig->db, zSql, -1, &pStmt, 0);
+ sqlite3_free(zSql);
+ assert( rc==SQLITE_OK || pCsr->pRankArgStmt==0 );
+ if( rc==SQLITE_OK ){
+ if( SQLITE_ROW==sqlite3_step(pStmt) ){
+ int nByte;
+ pCsr->nRankArg = sqlite3_column_count(pStmt);
+ nByte = sizeof(sqlite3_value*)*pCsr->nRankArg;
+ pCsr->apRankArg = (sqlite3_value**)sqlite3Fts5MallocZero(&rc, nByte);
+ if( rc==SQLITE_OK ){
+ int i;
+ for(i=0; i<pCsr->nRankArg; i++){
+ pCsr->apRankArg[i] = sqlite3_column_value(pStmt, i);
+ }
+ }
+ pCsr->pRankArgStmt = pStmt;
+ }else{
+ rc = sqlite3_finalize(pStmt);
+ assert( rc!=SQLITE_OK );
+ }
+ }
+ }
+ }
+
+ if( rc==SQLITE_OK ){
+ pAux = fts5FindAuxiliary(pTab, zRank);
+ if( pAux==0 ){
+ assert( pTab->base.zErrMsg==0 );
+ pTab->base.zErrMsg = sqlite3_mprintf("no such function: %s", zRank);
+ rc = SQLITE_ERROR;
+ }
+ }
+
+ pCsr->pRank = pAux;
+ return rc;
+}
+
+
+static int fts5CursorParseRank(
+ Fts5Config *pConfig,
+ Fts5Cursor *pCsr,
+ sqlite3_value *pRank
+){
+ int rc = SQLITE_OK;
+ if( pRank ){
+ const char *z = (const char*)sqlite3_value_text(pRank);
+ char *zRank = 0;
+ char *zRankArgs = 0;
+
+ if( z==0 ){
+ if( sqlite3_value_type(pRank)==SQLITE_NULL ) rc = SQLITE_ERROR;
+ }else{
+ rc = sqlite3Fts5ConfigParseRank(z, &zRank, &zRankArgs);
+ }
+ if( rc==SQLITE_OK ){
+ pCsr->zRank = zRank;
+ pCsr->zRankArgs = zRankArgs;
+ CsrFlagSet(pCsr, FTS5CSR_FREE_ZRANK);
+ }else if( rc==SQLITE_ERROR ){
+ pCsr->base.pVtab->zErrMsg = sqlite3_mprintf(
+ "parse error in rank function: %s", z
+ );
+ }
+ }else{
+ if( pConfig->zRank ){
+ pCsr->zRank = (char*)pConfig->zRank;
+ pCsr->zRankArgs = (char*)pConfig->zRankArgs;
+ }else{
+ pCsr->zRank = (char*)FTS5_DEFAULT_RANK;
+ pCsr->zRankArgs = 0;
+ }
+ }
+ return rc;
+}
+
+static i64 fts5GetRowidLimit(sqlite3_value *pVal, i64 iDefault){
+ if( pVal ){
+ int eType = sqlite3_value_numeric_type(pVal);
+ if( eType==SQLITE_INTEGER ){
+ return sqlite3_value_int64(pVal);
+ }
+ }
+ return iDefault;
+}
+
+/*
+** This is the xFilter interface for the virtual table. See
+** the virtual table xFilter method documentation for additional
+** information.
+**
+** There are three possible query strategies:
+**
+** 1. Full-text search using a MATCH operator.
+** 2. A by-rowid lookup.
+** 3. A full-table scan.
+*/
+static int fts5FilterMethod(
+ sqlite3_vtab_cursor *pCursor, /* The cursor used for this query */
+ int idxNum, /* Strategy index */
+ const char *zUnused, /* Unused */
+ int nVal, /* Number of elements in apVal */
+ sqlite3_value **apVal /* Arguments for the indexing scheme */
+){
+ Fts5Table *pTab = (Fts5Table*)(pCursor->pVtab);
+ Fts5Config *pConfig = pTab->pConfig;
+ Fts5Cursor *pCsr = (Fts5Cursor*)pCursor;
+ int rc = SQLITE_OK; /* Error code */
+ int iVal = 0; /* Counter for apVal[] */
+ int bDesc; /* True if ORDER BY [rank|rowid] DESC */
+ int bOrderByRank; /* True if ORDER BY rank */
+ sqlite3_value *pMatch = 0; /* <tbl> MATCH ? expression (or NULL) */
+ sqlite3_value *pRank = 0; /* rank MATCH ? expression (or NULL) */
+ sqlite3_value *pRowidEq = 0; /* rowid = ? expression (or NULL) */
+ sqlite3_value *pRowidLe = 0; /* rowid <= ? expression (or NULL) */
+ sqlite3_value *pRowidGe = 0; /* rowid >= ? expression (or NULL) */
+ char **pzErrmsg = pConfig->pzErrmsg;
+
+ UNUSED_PARAM(zUnused);
+ UNUSED_PARAM(nVal);
+
+ if( pCsr->ePlan ){
+ fts5FreeCursorComponents(pCsr);
+ memset(&pCsr->ePlan, 0, sizeof(Fts5Cursor) - ((u8*)&pCsr->ePlan-(u8*)pCsr));
+ }
+
+ assert( pCsr->pStmt==0 );
+ assert( pCsr->pExpr==0 );
+ assert( pCsr->csrflags==0 );
+ assert( pCsr->pRank==0 );
+ assert( pCsr->zRank==0 );
+ assert( pCsr->zRankArgs==0 );
+
+ assert( pzErrmsg==0 || pzErrmsg==&pTab->base.zErrMsg );
+ pConfig->pzErrmsg = &pTab->base.zErrMsg;
+
+ /* Decode the arguments passed through to this function.
+ **
+ ** Note: The following set of if(...) statements must be in the same
+ ** order as the corresponding entries in the struct at the top of
+ ** fts5BestIndexMethod(). */
+ if( BitFlagTest(idxNum, FTS5_BI_MATCH) ) pMatch = apVal[iVal++];
+ if( BitFlagTest(idxNum, FTS5_BI_RANK) ) pRank = apVal[iVal++];
+ if( BitFlagTest(idxNum, FTS5_BI_ROWID_EQ) ) pRowidEq = apVal[iVal++];
+ if( BitFlagTest(idxNum, FTS5_BI_ROWID_LE) ) pRowidLe = apVal[iVal++];
+ if( BitFlagTest(idxNum, FTS5_BI_ROWID_GE) ) pRowidGe = apVal[iVal++];
+ assert( iVal==nVal );
+ bOrderByRank = ((idxNum & FTS5_BI_ORDER_RANK) ? 1 : 0);
+ pCsr->bDesc = bDesc = ((idxNum & FTS5_BI_ORDER_DESC) ? 1 : 0);
+
+ /* Set the cursor upper and lower rowid limits. Only some strategies
+ ** actually use them. This is ok, as the xBestIndex() method leaves the
+ ** sqlite3_index_constraint.omit flag clear for range constraints
+ ** on the rowid field. */
+ if( pRowidEq ){
+ pRowidLe = pRowidGe = pRowidEq;
+ }
+ if( bDesc ){
+ pCsr->iFirstRowid = fts5GetRowidLimit(pRowidLe, LARGEST_INT64);
+ pCsr->iLastRowid = fts5GetRowidLimit(pRowidGe, SMALLEST_INT64);
+ }else{
+ pCsr->iLastRowid = fts5GetRowidLimit(pRowidLe, LARGEST_INT64);
+ pCsr->iFirstRowid = fts5GetRowidLimit(pRowidGe, SMALLEST_INT64);
+ }
+
+ if( pTab->pSortCsr ){
+ /* If pSortCsr is non-NULL, then this call is being made as part of
+ ** processing for a "... MATCH <expr> ORDER BY rank" query (ePlan is
+ ** set to FTS5_PLAN_SORTED_MATCH). pSortCsr is the cursor that will
+ ** return results to the user for this query. The current cursor
+ ** (pCursor) is used to execute the query issued by function
+ ** fts5CursorFirstSorted() above. */
+ assert( pRowidEq==0 && pRowidLe==0 && pRowidGe==0 && pRank==0 );
+ assert( nVal==0 && pMatch==0 && bOrderByRank==0 && bDesc==0 );
+ assert( pCsr->iLastRowid==LARGEST_INT64 );
+ assert( pCsr->iFirstRowid==SMALLEST_INT64 );
+ pCsr->ePlan = FTS5_PLAN_SOURCE;
+ pCsr->pExpr = pTab->pSortCsr->pExpr;
+ rc = fts5CursorFirst(pTab, pCsr, bDesc);
+ sqlite3Fts5ExprClearEof(pCsr->pExpr);
+ }else if( pMatch ){
+ const char *zExpr = (const char*)sqlite3_value_text(apVal[0]);
+ if( zExpr==0 ) zExpr = "";
+
+ rc = fts5CursorParseRank(pConfig, pCsr, pRank);
+ if( rc==SQLITE_OK ){
+ if( zExpr[0]=='*' ){
+ /* The user has issued a query of the form "MATCH '*...'". This
+ ** indicates that the MATCH expression is not a full text query,
+ ** but a request for an internal parameter. */
+ rc = fts5SpecialMatch(pTab, pCsr, &zExpr[1]);
+ }else{
+ char **pzErr = &pTab->base.zErrMsg;
+ rc = sqlite3Fts5ExprNew(pConfig, zExpr, &pCsr->pExpr, pzErr);
+ if( rc==SQLITE_OK ){
+ if( bOrderByRank ){
+ pCsr->ePlan = FTS5_PLAN_SORTED_MATCH;
+ rc = fts5CursorFirstSorted(pTab, pCsr, bDesc);
+ }else{
+ pCsr->ePlan = FTS5_PLAN_MATCH;
+ rc = fts5CursorFirst(pTab, pCsr, bDesc);
+ }
+ }
+ }
+ }
+ }else if( pConfig->zContent==0 ){
+ *pConfig->pzErrmsg = sqlite3_mprintf(
+ "%s: table does not support scanning", pConfig->zName
+ );
+ rc = SQLITE_ERROR;
+ }else{
+ /* This is either a full-table scan (ePlan==FTS5_PLAN_SCAN) or a lookup
+ ** by rowid (ePlan==FTS5_PLAN_ROWID). */
+ pCsr->ePlan = (pRowidEq ? FTS5_PLAN_ROWID : FTS5_PLAN_SCAN);
+ rc = sqlite3Fts5StorageStmt(
+ pTab->pStorage, fts5StmtType(pCsr), &pCsr->pStmt, &pTab->base.zErrMsg
+ );
+ if( rc==SQLITE_OK ){
+ if( pCsr->ePlan==FTS5_PLAN_ROWID ){
+ sqlite3_bind_value(pCsr->pStmt, 1, apVal[0]);
+ }else{
+ sqlite3_bind_int64(pCsr->pStmt, 1, pCsr->iFirstRowid);
+ sqlite3_bind_int64(pCsr->pStmt, 2, pCsr->iLastRowid);
+ }
+ rc = fts5NextMethod(pCursor);
+ }
+ }
+
+ pConfig->pzErrmsg = pzErrmsg;
+ return rc;
+}
+
+/*
+** This is the xEof method of the virtual table. SQLite calls this
+** routine to find out if it has reached the end of a result set.
+*/
+static int fts5EofMethod(sqlite3_vtab_cursor *pCursor){
+ Fts5Cursor *pCsr = (Fts5Cursor*)pCursor;
+ return (CsrFlagTest(pCsr, FTS5CSR_EOF) ? 1 : 0);
+}
+
+/*
+** Return the rowid that the cursor currently points to.
+*/
+static i64 fts5CursorRowid(Fts5Cursor *pCsr){
+ assert( pCsr->ePlan==FTS5_PLAN_MATCH
+ || pCsr->ePlan==FTS5_PLAN_SORTED_MATCH
+ || pCsr->ePlan==FTS5_PLAN_SOURCE
+ );
+ if( pCsr->pSorter ){
+ return pCsr->pSorter->iRowid;
+ }else{
+ return sqlite3Fts5ExprRowid(pCsr->pExpr);
+ }
+}
+
+/*
+** This is the xRowid method. The SQLite core calls this routine to
+** retrieve the rowid for the current row of the result set. fts5
+** exposes %_content.rowid as the rowid for the virtual table. The
+** rowid should be written to *pRowid.
+*/
+static int fts5RowidMethod(sqlite3_vtab_cursor *pCursor, sqlite_int64 *pRowid){
+ Fts5Cursor *pCsr = (Fts5Cursor*)pCursor;
+ int ePlan = pCsr->ePlan;
+
+ assert( CsrFlagTest(pCsr, FTS5CSR_EOF)==0 );
+ switch( ePlan ){
+ case FTS5_PLAN_SPECIAL:
+ *pRowid = 0;
+ break;
+
+ case FTS5_PLAN_SOURCE:
+ case FTS5_PLAN_MATCH:
+ case FTS5_PLAN_SORTED_MATCH:
+ *pRowid = fts5CursorRowid(pCsr);
+ break;
+
+ default:
+ *pRowid = sqlite3_column_int64(pCsr->pStmt, 0);
+ break;
+ }
+
+ return SQLITE_OK;
+}
+
+/*
+** If the cursor requires seeking (bSeekRequired flag is set), seek it.
+** Return SQLITE_OK if no error occurs, or an SQLite error code otherwise.
+**
+** If argument bErrormsg is true and an error occurs, an error message may
+** be left in sqlite3_vtab.zErrMsg.
+*/
+static int fts5SeekCursor(Fts5Cursor *pCsr, int bErrormsg){
+ int rc = SQLITE_OK;
+
+ /* If the cursor does not yet have a statement handle, obtain one now. */
+ if( pCsr->pStmt==0 ){
+ Fts5Table *pTab = (Fts5Table*)(pCsr->base.pVtab);
+ int eStmt = fts5StmtType(pCsr);
+ rc = sqlite3Fts5StorageStmt(
+ pTab->pStorage, eStmt, &pCsr->pStmt, (bErrormsg?&pTab->base.zErrMsg:0)
+ );
+ assert( rc!=SQLITE_OK || pTab->base.zErrMsg==0 );
+ assert( CsrFlagTest(pCsr, FTS5CSR_REQUIRE_CONTENT) );
+ }
+
+ if( rc==SQLITE_OK && CsrFlagTest(pCsr, FTS5CSR_REQUIRE_CONTENT) ){
+ assert( pCsr->pExpr );
+ sqlite3_reset(pCsr->pStmt);
+ sqlite3_bind_int64(pCsr->pStmt, 1, fts5CursorRowid(pCsr));
+ rc = sqlite3_step(pCsr->pStmt);
+ if( rc==SQLITE_ROW ){
+ rc = SQLITE_OK;
+ CsrFlagClear(pCsr, FTS5CSR_REQUIRE_CONTENT);
+ }else{
+ rc = sqlite3_reset(pCsr->pStmt);
+ if( rc==SQLITE_OK ){
+ rc = FTS5_CORRUPT;
+ }
+ }
+ }
+ return rc;
+}
+
+static void fts5SetVtabError(Fts5Table *p, const char *zFormat, ...){
+ va_list ap; /* ... printf arguments */
+ va_start(ap, zFormat);
+ assert( p->base.zErrMsg==0 );
+ p->base.zErrMsg = sqlite3_vmprintf(zFormat, ap);
+ va_end(ap);
+}
+
+/*
+** This function is called to handle an FTS INSERT command. In other words,
+** an INSERT statement of the form:
+**
+** INSERT INTO fts(fts) VALUES($pCmd)
+** INSERT INTO fts(fts, rank) VALUES($pCmd, $pVal)
+**
+** Argument pVal is the value assigned to column "fts" by the INSERT
+** statement. This function returns SQLITE_OK if successful, or an SQLite
+** error code if an error occurs.
+**
+** The commands implemented by this function are documented in the "Special
+** INSERT Directives" section of the documentation. It should be updated if
+** more commands are added to this function.
+*/
+static int fts5SpecialInsert(
+ Fts5Table *pTab, /* Fts5 table object */
+ const char *zCmd, /* Text inserted into table-name column */
+ sqlite3_value *pVal /* Value inserted into rank column */
+){
+ Fts5Config *pConfig = pTab->pConfig;
+ int rc = SQLITE_OK;
+ int bError = 0;
+
+ if( 0==sqlite3_stricmp("delete-all", zCmd) ){
+ if( pConfig->eContent==FTS5_CONTENT_NORMAL ){
+ fts5SetVtabError(pTab,
+ "'delete-all' may only be used with a "
+ "contentless or external content fts5 table"
+ );
+ rc = SQLITE_ERROR;
+ }else{
+ rc = sqlite3Fts5StorageDeleteAll(pTab->pStorage);
+ }
+ }else if( 0==sqlite3_stricmp("rebuild", zCmd) ){
+ if( pConfig->eContent==FTS5_CONTENT_NONE ){
+ fts5SetVtabError(pTab,
+ "'rebuild' may not be used with a contentless fts5 table"
+ );
+ rc = SQLITE_ERROR;
+ }else{
+ rc = sqlite3Fts5StorageRebuild(pTab->pStorage);
+ }
+ }else if( 0==sqlite3_stricmp("optimize", zCmd) ){
+ rc = sqlite3Fts5StorageOptimize(pTab->pStorage);
+ }else if( 0==sqlite3_stricmp("merge", zCmd) ){
+ int nMerge = sqlite3_value_int(pVal);
+ rc = sqlite3Fts5StorageMerge(pTab->pStorage, nMerge);
+ }else if( 0==sqlite3_stricmp("integrity-check", zCmd) ){
+ rc = sqlite3Fts5StorageIntegrity(pTab->pStorage);
+#ifdef SQLITE_DEBUG
+ }else if( 0==sqlite3_stricmp("prefix-index", zCmd) ){
+ pConfig->bPrefixIndex = sqlite3_value_int(pVal);
+#endif
+ }else{
+ rc = sqlite3Fts5IndexLoadConfig(pTab->pIndex);
+ if( rc==SQLITE_OK ){
+ rc = sqlite3Fts5ConfigSetValue(pTab->pConfig, zCmd, pVal, &bError);
+ }
+ if( rc==SQLITE_OK ){
+ if( bError ){
+ rc = SQLITE_ERROR;
+ }else{
+ rc = sqlite3Fts5StorageConfigValue(pTab->pStorage, zCmd, pVal, 0);
+ }
+ }
+ }
+ return rc;
+}
+
+static int fts5SpecialDelete(
+ Fts5Table *pTab,
+ sqlite3_value **apVal
+){
+ int rc = SQLITE_OK;
+ int eType1 = sqlite3_value_type(apVal[1]);
+ if( eType1==SQLITE_INTEGER ){
+ sqlite3_int64 iDel = sqlite3_value_int64(apVal[1]);
+ rc = sqlite3Fts5StorageDelete(pTab->pStorage, iDel, &apVal[2]);
+ }
+ return rc;
+}
+
+static void fts5StorageInsert(
+ int *pRc,
+ Fts5Table *pTab,
+ sqlite3_value **apVal,
+ i64 *piRowid
+){
+ int rc = *pRc;
+ if( rc==SQLITE_OK ){
+ rc = sqlite3Fts5StorageContentInsert(pTab->pStorage, apVal, piRowid);
+ }
+ if( rc==SQLITE_OK ){
+ rc = sqlite3Fts5StorageIndexInsert(pTab->pStorage, apVal, *piRowid);
+ }
+ *pRc = rc;
+}
+
+/*
+** This function is the implementation of the xUpdate callback used by
+** FTS3 virtual tables. It is invoked by SQLite each time a row is to be
+** inserted, updated or deleted.
+**
+** A delete specifies a single argument - the rowid of the row to remove.
+**
+** Update and insert operations pass:
+**
+** 1. The "old" rowid, or NULL.
+** 2. The "new" rowid.
+** 3. Values for each of the nCol matchable columns.
+** 4. Values for the two hidden columns (<tablename> and "rank").
+*/
+static int fts5UpdateMethod(
+ sqlite3_vtab *pVtab, /* Virtual table handle */
+ int nArg, /* Size of argument array */
+ sqlite3_value **apVal, /* Array of arguments */
+ sqlite_int64 *pRowid /* OUT: The affected (or effected) rowid */
+){
+ Fts5Table *pTab = (Fts5Table*)pVtab;
+ Fts5Config *pConfig = pTab->pConfig;
+ int eType0; /* value_type() of apVal[0] */
+ int rc = SQLITE_OK; /* Return code */
+
+ /* A transaction must be open when this is called. */
+ assert( pTab->ts.eState==1 );
+
+ assert( pVtab->zErrMsg==0 );
+ assert( nArg==1 || nArg==(2+pConfig->nCol+2) );
+ assert( nArg==1
+ || sqlite3_value_type(apVal[1])==SQLITE_INTEGER
+ || sqlite3_value_type(apVal[1])==SQLITE_NULL
+ );
+ assert( pTab->pConfig->pzErrmsg==0 );
+ pTab->pConfig->pzErrmsg = &pTab->base.zErrMsg;
+
+ /* Put any active cursors into REQUIRE_SEEK state. */
+ fts5TripCursors(pTab);
+
+ eType0 = sqlite3_value_type(apVal[0]);
+ if( eType0==SQLITE_NULL
+ && sqlite3_value_type(apVal[2+pConfig->nCol])!=SQLITE_NULL
+ ){
+ /* A "special" INSERT op. These are handled separately. */
+ const char *z = (const char*)sqlite3_value_text(apVal[2+pConfig->nCol]);
+ if( pConfig->eContent!=FTS5_CONTENT_NORMAL
+ && 0==sqlite3_stricmp("delete", z)
+ ){
+ rc = fts5SpecialDelete(pTab, apVal);
+ }else{
+ rc = fts5SpecialInsert(pTab, z, apVal[2 + pConfig->nCol + 1]);
+ }
+ }else{
+ /* A regular INSERT, UPDATE or DELETE statement. The trick here is that
+ ** any conflict on the rowid value must be detected before any
+ ** modifications are made to the database file. There are 4 cases:
+ **
+ ** 1) DELETE
+ ** 2) UPDATE (rowid not modified)
+ ** 3) UPDATE (rowid modified)
+ ** 4) INSERT
+ **
+ ** Cases 3 and 4 may violate the rowid constraint.
+ */
+ int eConflict = SQLITE_ABORT;
+ if( pConfig->eContent==FTS5_CONTENT_NORMAL ){
+ eConflict = sqlite3_vtab_on_conflict(pConfig->db);
+ }
+
+ assert( eType0==SQLITE_INTEGER || eType0==SQLITE_NULL );
+ assert( nArg!=1 || eType0==SQLITE_INTEGER );
+
+ /* Filter out attempts to run UPDATE or DELETE on contentless tables.
+ ** This is not suported. */
+ if( eType0==SQLITE_INTEGER && fts5IsContentless(pTab) ){
+ pTab->base.zErrMsg = sqlite3_mprintf(
+ "cannot %s contentless fts5 table: %s",
+ (nArg>1 ? "UPDATE" : "DELETE from"), pConfig->zName
+ );
+ rc = SQLITE_ERROR;
+ }
+
+ /* DELETE */
+ else if( nArg==1 ){
+ i64 iDel = sqlite3_value_int64(apVal[0]); /* Rowid to delete */
+ rc = sqlite3Fts5StorageDelete(pTab->pStorage, iDel, 0);
+ }
+
+ /* INSERT */
+ else if( eType0!=SQLITE_INTEGER ){
+ /* If this is a REPLACE, first remove the current entry (if any) */
+ if( eConflict==SQLITE_REPLACE
+ && sqlite3_value_type(apVal[1])==SQLITE_INTEGER
+ ){
+ i64 iNew = sqlite3_value_int64(apVal[1]); /* Rowid to delete */
+ rc = sqlite3Fts5StorageDelete(pTab->pStorage, iNew, 0);
+ }
+ fts5StorageInsert(&rc, pTab, apVal, pRowid);
+ }
+
+ /* UPDATE */
+ else{
+ i64 iOld = sqlite3_value_int64(apVal[0]); /* Old rowid */
+ i64 iNew = sqlite3_value_int64(apVal[1]); /* New rowid */
+ if( iOld!=iNew ){
+ if( eConflict==SQLITE_REPLACE ){
+ rc = sqlite3Fts5StorageDelete(pTab->pStorage, iOld, 0);
+ if( rc==SQLITE_OK ){
+ rc = sqlite3Fts5StorageDelete(pTab->pStorage, iNew, 0);
+ }
+ fts5StorageInsert(&rc, pTab, apVal, pRowid);
+ }else{
+ rc = sqlite3Fts5StorageContentInsert(pTab->pStorage, apVal, pRowid);
+ if( rc==SQLITE_OK ){
+ rc = sqlite3Fts5StorageDelete(pTab->pStorage, iOld, 0);
+ }
+ if( rc==SQLITE_OK ){
+ rc = sqlite3Fts5StorageIndexInsert(pTab->pStorage, apVal, *pRowid);
+ }
+ }
+ }else{
+ rc = sqlite3Fts5StorageDelete(pTab->pStorage, iOld, 0);
+ fts5StorageInsert(&rc, pTab, apVal, pRowid);
+ }
+ }
+ }
+
+ pTab->pConfig->pzErrmsg = 0;
+ return rc;
+}
+
+/*
+** Implementation of xSync() method.
+*/
+static int fts5SyncMethod(sqlite3_vtab *pVtab){
+ int rc;
+ Fts5Table *pTab = (Fts5Table*)pVtab;
+ fts5CheckTransactionState(pTab, FTS5_SYNC, 0);
+ pTab->pConfig->pzErrmsg = &pTab->base.zErrMsg;
+ fts5TripCursors(pTab);
+ rc = sqlite3Fts5StorageSync(pTab->pStorage, 1);
+ pTab->pConfig->pzErrmsg = 0;
+ return rc;
+}
+
+/*
+** Implementation of xBegin() method.
+*/
+static int fts5BeginMethod(sqlite3_vtab *pVtab){
+ fts5CheckTransactionState((Fts5Table*)pVtab, FTS5_BEGIN, 0);
+ fts5NewTransaction((Fts5Table*)pVtab);
+ return SQLITE_OK;
+}
+
+/*
+** Implementation of xCommit() method. This is a no-op. The contents of
+** the pending-terms hash-table have already been flushed into the database
+** by fts5SyncMethod().
+*/
+static int fts5CommitMethod(sqlite3_vtab *pVtab){
+ UNUSED_PARAM(pVtab); /* Call below is a no-op for NDEBUG builds */
+ fts5CheckTransactionState((Fts5Table*)pVtab, FTS5_COMMIT, 0);
+ return SQLITE_OK;
+}
+
+/*
+** Implementation of xRollback(). Discard the contents of the pending-terms
+** hash-table. Any changes made to the database are reverted by SQLite.
+*/
+static int fts5RollbackMethod(sqlite3_vtab *pVtab){
+ int rc;
+ Fts5Table *pTab = (Fts5Table*)pVtab;
+ fts5CheckTransactionState(pTab, FTS5_ROLLBACK, 0);
+ rc = sqlite3Fts5StorageRollback(pTab->pStorage);
+ return rc;
+}
+
+static int fts5CsrPoslist(Fts5Cursor*, int, const u8**, int*);
+
+static void *fts5ApiUserData(Fts5Context *pCtx){
+ Fts5Cursor *pCsr = (Fts5Cursor*)pCtx;
+ return pCsr->pAux->pUserData;
+}
+
+static int fts5ApiColumnCount(Fts5Context *pCtx){
+ Fts5Cursor *pCsr = (Fts5Cursor*)pCtx;
+ return ((Fts5Table*)(pCsr->base.pVtab))->pConfig->nCol;
+}
+
+static int fts5ApiColumnTotalSize(
+ Fts5Context *pCtx,
+ int iCol,
+ sqlite3_int64 *pnToken
+){
+ Fts5Cursor *pCsr = (Fts5Cursor*)pCtx;
+ Fts5Table *pTab = (Fts5Table*)(pCsr->base.pVtab);
+ return sqlite3Fts5StorageSize(pTab->pStorage, iCol, pnToken);
+}
+
+static int fts5ApiRowCount(Fts5Context *pCtx, i64 *pnRow){
+ Fts5Cursor *pCsr = (Fts5Cursor*)pCtx;
+ Fts5Table *pTab = (Fts5Table*)(pCsr->base.pVtab);
+ return sqlite3Fts5StorageRowCount(pTab->pStorage, pnRow);
+}
+
+static int fts5ApiTokenize(
+ Fts5Context *pCtx,
+ const char *pText, int nText,
+ void *pUserData,
+ int (*xToken)(void*, int, const char*, int, int, int)
+){
+ Fts5Cursor *pCsr = (Fts5Cursor*)pCtx;
+ Fts5Table *pTab = (Fts5Table*)(pCsr->base.pVtab);
+ return sqlite3Fts5Tokenize(
+ pTab->pConfig, FTS5_TOKENIZE_AUX, pText, nText, pUserData, xToken
+ );
+}
+
+static int fts5ApiPhraseCount(Fts5Context *pCtx){
+ Fts5Cursor *pCsr = (Fts5Cursor*)pCtx;
+ return sqlite3Fts5ExprPhraseCount(pCsr->pExpr);
+}
+
+static int fts5ApiPhraseSize(Fts5Context *pCtx, int iPhrase){
+ Fts5Cursor *pCsr = (Fts5Cursor*)pCtx;
+ return sqlite3Fts5ExprPhraseSize(pCsr->pExpr, iPhrase);
+}
+
+static int fts5ApiColumnText(
+ Fts5Context *pCtx,
+ int iCol,
+ const char **pz,
+ int *pn
+){
+ int rc = SQLITE_OK;
+ Fts5Cursor *pCsr = (Fts5Cursor*)pCtx;
+ if( fts5IsContentless((Fts5Table*)(pCsr->base.pVtab)) ){
+ *pz = 0;
+ *pn = 0;
+ }else{
+ rc = fts5SeekCursor(pCsr, 0);
+ if( rc==SQLITE_OK ){
+ *pz = (const char*)sqlite3_column_text(pCsr->pStmt, iCol+1);
+ *pn = sqlite3_column_bytes(pCsr->pStmt, iCol+1);
+ }
+ }
+ return rc;
+}
+
+static int fts5CsrPoslist(
+ Fts5Cursor *pCsr,
+ int iPhrase,
+ const u8 **pa,
+ int *pn
+){
+ Fts5Config *pConfig = ((Fts5Table*)(pCsr->base.pVtab))->pConfig;
+ int rc = SQLITE_OK;
+ int bLive = (pCsr->pSorter==0);
+
+ if( CsrFlagTest(pCsr, FTS5CSR_REQUIRE_POSLIST) ){
+
+ if( pConfig->eDetail!=FTS5_DETAIL_FULL ){
+ Fts5PoslistPopulator *aPopulator;
+ int i;
+ aPopulator = sqlite3Fts5ExprClearPoslists(pCsr->pExpr, bLive);
+ if( aPopulator==0 ) rc = SQLITE_NOMEM;
+ for(i=0; i<pConfig->nCol && rc==SQLITE_OK; i++){
+ int n; const char *z;
+ rc = fts5ApiColumnText((Fts5Context*)pCsr, i, &z, &n);
+ if( rc==SQLITE_OK ){
+ rc = sqlite3Fts5ExprPopulatePoslists(
+ pConfig, pCsr->pExpr, aPopulator, i, z, n
+ );
+ }
+ }
+ sqlite3_free(aPopulator);
+
+ if( pCsr->pSorter ){
+ sqlite3Fts5ExprCheckPoslists(pCsr->pExpr, pCsr->pSorter->iRowid);
+ }
+ }
+ CsrFlagClear(pCsr, FTS5CSR_REQUIRE_POSLIST);
+ }
+
+ if( pCsr->pSorter && pConfig->eDetail==FTS5_DETAIL_FULL ){
+ Fts5Sorter *pSorter = pCsr->pSorter;
+ int i1 = (iPhrase==0 ? 0 : pSorter->aIdx[iPhrase-1]);
+ *pn = pSorter->aIdx[iPhrase] - i1;
+ *pa = &pSorter->aPoslist[i1];
+ }else{
+ *pn = sqlite3Fts5ExprPoslist(pCsr->pExpr, iPhrase, pa);
+ }
+
+ return rc;
+}
+
+/*
+** Ensure that the Fts5Cursor.nInstCount and aInst[] variables are populated
+** correctly for the current view. Return SQLITE_OK if successful, or an
+** SQLite error code otherwise.
+*/
+static int fts5CacheInstArray(Fts5Cursor *pCsr){
+ int rc = SQLITE_OK;
+ Fts5PoslistReader *aIter; /* One iterator for each phrase */
+ int nIter; /* Number of iterators/phrases */
+
+ nIter = sqlite3Fts5ExprPhraseCount(pCsr->pExpr);
+ if( pCsr->aInstIter==0 ){
+ int nByte = sizeof(Fts5PoslistReader) * nIter;
+ pCsr->aInstIter = (Fts5PoslistReader*)sqlite3Fts5MallocZero(&rc, nByte);
+ }
+ aIter = pCsr->aInstIter;
+
+ if( aIter ){
+ int nInst = 0; /* Number instances seen so far */
+ int i;
+
+ /* Initialize all iterators */
+ for(i=0; i<nIter && rc==SQLITE_OK; i++){
+ const u8 *a;
+ int n;
+ rc = fts5CsrPoslist(pCsr, i, &a, &n);
+ if( rc==SQLITE_OK ){
+ sqlite3Fts5PoslistReaderInit(a, n, &aIter[i]);
+ }
+ }
+
+ if( rc==SQLITE_OK ){
+ while( 1 ){
+ int *aInst;
+ int iBest = -1;
+ for(i=0; i<nIter; i++){
+ if( (aIter[i].bEof==0)
+ && (iBest<0 || aIter[i].iPos<aIter[iBest].iPos)
+ ){
+ iBest = i;
+ }
+ }
+ if( iBest<0 ) break;
+
+ nInst++;
+ if( nInst>=pCsr->nInstAlloc ){
+ pCsr->nInstAlloc = pCsr->nInstAlloc ? pCsr->nInstAlloc*2 : 32;
+ aInst = (int*)sqlite3_realloc(
+ pCsr->aInst, pCsr->nInstAlloc*sizeof(int)*3
+ );
+ if( aInst ){
+ pCsr->aInst = aInst;
+ }else{
+ rc = SQLITE_NOMEM;
+ break;
+ }
+ }
+
+ aInst = &pCsr->aInst[3 * (nInst-1)];
+ aInst[0] = iBest;
+ aInst[1] = FTS5_POS2COLUMN(aIter[iBest].iPos);
+ aInst[2] = FTS5_POS2OFFSET(aIter[iBest].iPos);
+ sqlite3Fts5PoslistReaderNext(&aIter[iBest]);
+ }
+ }
+
+ pCsr->nInstCount = nInst;
+ CsrFlagClear(pCsr, FTS5CSR_REQUIRE_INST);
+ }
+ return rc;
+}
+
+static int fts5ApiInstCount(Fts5Context *pCtx, int *pnInst){
+ Fts5Cursor *pCsr = (Fts5Cursor*)pCtx;
+ int rc = SQLITE_OK;
+ if( CsrFlagTest(pCsr, FTS5CSR_REQUIRE_INST)==0
+ || SQLITE_OK==(rc = fts5CacheInstArray(pCsr)) ){
+ *pnInst = pCsr->nInstCount;
+ }
+ return rc;
+}
+
+static int fts5ApiInst(
+ Fts5Context *pCtx,
+ int iIdx,
+ int *piPhrase,
+ int *piCol,
+ int *piOff
+){
+ Fts5Cursor *pCsr = (Fts5Cursor*)pCtx;
+ int rc = SQLITE_OK;
+ if( CsrFlagTest(pCsr, FTS5CSR_REQUIRE_INST)==0
+ || SQLITE_OK==(rc = fts5CacheInstArray(pCsr))
+ ){
+ if( iIdx<0 || iIdx>=pCsr->nInstCount ){
+ rc = SQLITE_RANGE;
+#if 0
+ }else if( fts5IsOffsetless((Fts5Table*)pCsr->base.pVtab) ){
+ *piPhrase = pCsr->aInst[iIdx*3];
+ *piCol = pCsr->aInst[iIdx*3 + 2];
+ *piOff = -1;
+#endif
+ }else{
+ *piPhrase = pCsr->aInst[iIdx*3];
+ *piCol = pCsr->aInst[iIdx*3 + 1];
+ *piOff = pCsr->aInst[iIdx*3 + 2];
+ }
+ }
+ return rc;
+}
+
+static sqlite3_int64 fts5ApiRowid(Fts5Context *pCtx){
+ return fts5CursorRowid((Fts5Cursor*)pCtx);
+}
+
+static int fts5ColumnSizeCb(
+ void *pContext, /* Pointer to int */
+ int tflags,
+ const char *pUnused, /* Buffer containing token */
+ int nUnused, /* Size of token in bytes */
+ int iUnused1, /* Start offset of token */
+ int iUnused2 /* End offset of token */
+){
+ int *pCnt = (int*)pContext;
+ UNUSED_PARAM2(pUnused, nUnused);
+ UNUSED_PARAM2(iUnused1, iUnused2);
+ if( (tflags & FTS5_TOKEN_COLOCATED)==0 ){
+ (*pCnt)++;
+ }
+ return SQLITE_OK;
+}
+
+static int fts5ApiColumnSize(Fts5Context *pCtx, int iCol, int *pnToken){
+ Fts5Cursor *pCsr = (Fts5Cursor*)pCtx;
+ Fts5Table *pTab = (Fts5Table*)(pCsr->base.pVtab);
+ Fts5Config *pConfig = pTab->pConfig;
+ int rc = SQLITE_OK;
+
+ if( CsrFlagTest(pCsr, FTS5CSR_REQUIRE_DOCSIZE) ){
+ if( pConfig->bColumnsize ){
+ i64 iRowid = fts5CursorRowid(pCsr);
+ rc = sqlite3Fts5StorageDocsize(pTab->pStorage, iRowid, pCsr->aColumnSize);
+ }else if( pConfig->zContent==0 ){
+ int i;
+ for(i=0; i<pConfig->nCol; i++){
+ if( pConfig->abUnindexed[i]==0 ){
+ pCsr->aColumnSize[i] = -1;
+ }
+ }
+ }else{
+ int i;
+ for(i=0; rc==SQLITE_OK && i<pConfig->nCol; i++){
+ if( pConfig->abUnindexed[i]==0 ){
+ const char *z; int n;
+ void *p = (void*)(&pCsr->aColumnSize[i]);
+ pCsr->aColumnSize[i] = 0;
+ rc = fts5ApiColumnText(pCtx, i, &z, &n);
+ if( rc==SQLITE_OK ){
+ rc = sqlite3Fts5Tokenize(
+ pConfig, FTS5_TOKENIZE_AUX, z, n, p, fts5ColumnSizeCb
+ );
+ }
+ }
+ }
+ }
+ CsrFlagClear(pCsr, FTS5CSR_REQUIRE_DOCSIZE);
+ }
+ if( iCol<0 ){
+ int i;
+ *pnToken = 0;
+ for(i=0; i<pConfig->nCol; i++){
+ *pnToken += pCsr->aColumnSize[i];
+ }
+ }else if( iCol<pConfig->nCol ){
+ *pnToken = pCsr->aColumnSize[iCol];
+ }else{
+ *pnToken = 0;
+ rc = SQLITE_RANGE;
+ }
+ return rc;
+}
+
+/*
+** Implementation of the xSetAuxdata() method.
+*/
+static int fts5ApiSetAuxdata(
+ Fts5Context *pCtx, /* Fts5 context */
+ void *pPtr, /* Pointer to save as auxdata */
+ void(*xDelete)(void*) /* Destructor for pPtr (or NULL) */
+){
+ Fts5Cursor *pCsr = (Fts5Cursor*)pCtx;
+ Fts5Auxdata *pData;
+
+ /* Search through the cursors list of Fts5Auxdata objects for one that
+ ** corresponds to the currently executing auxiliary function. */
+ for(pData=pCsr->pAuxdata; pData; pData=pData->pNext){
+ if( pData->pAux==pCsr->pAux ) break;
+ }
+
+ if( pData ){
+ if( pData->xDelete ){
+ pData->xDelete(pData->pPtr);
+ }
+ }else{
+ int rc = SQLITE_OK;
+ pData = (Fts5Auxdata*)sqlite3Fts5MallocZero(&rc, sizeof(Fts5Auxdata));
+ if( pData==0 ){
+ if( xDelete ) xDelete(pPtr);
+ return rc;
+ }
+ pData->pAux = pCsr->pAux;
+ pData->pNext = pCsr->pAuxdata;
+ pCsr->pAuxdata = pData;
+ }
+
+ pData->xDelete = xDelete;
+ pData->pPtr = pPtr;
+ return SQLITE_OK;
+}
+
+static void *fts5ApiGetAuxdata(Fts5Context *pCtx, int bClear){
+ Fts5Cursor *pCsr = (Fts5Cursor*)pCtx;
+ Fts5Auxdata *pData;
+ void *pRet = 0;
+
+ for(pData=pCsr->pAuxdata; pData; pData=pData->pNext){
+ if( pData->pAux==pCsr->pAux ) break;
+ }
+
+ if( pData ){
+ pRet = pData->pPtr;
+ if( bClear ){
+ pData->pPtr = 0;
+ pData->xDelete = 0;
+ }
+ }
+
+ return pRet;
+}
+
+static void fts5ApiPhraseNext(
+ Fts5Context *pUnused,
+ Fts5PhraseIter *pIter,
+ int *piCol, int *piOff
+){
+ UNUSED_PARAM(pUnused);
+ if( pIter->a>=pIter->b ){
+ *piCol = -1;
+ *piOff = -1;
+ }else{
+ int iVal;
+ pIter->a += fts5GetVarint32(pIter->a, iVal);
+ if( iVal==1 ){
+ pIter->a += fts5GetVarint32(pIter->a, iVal);
+ *piCol = iVal;
+ *piOff = 0;
+ pIter->a += fts5GetVarint32(pIter->a, iVal);
+ }
+ *piOff += (iVal-2);
+ }
+}
+
+static int fts5ApiPhraseFirst(
+ Fts5Context *pCtx,
+ int iPhrase,
+ Fts5PhraseIter *pIter,
+ int *piCol, int *piOff
+){
+ Fts5Cursor *pCsr = (Fts5Cursor*)pCtx;
+ int n;
+ int rc = fts5CsrPoslist(pCsr, iPhrase, &pIter->a, &n);
+ if( rc==SQLITE_OK ){
+ pIter->b = &pIter->a[n];
+ *piCol = 0;
+ *piOff = 0;
+ fts5ApiPhraseNext(pCtx, pIter, piCol, piOff);
+ }
+ return rc;
+}
+
+static void fts5ApiPhraseNextColumn(
+ Fts5Context *pCtx,
+ Fts5PhraseIter *pIter,
+ int *piCol
+){
+ Fts5Cursor *pCsr = (Fts5Cursor*)pCtx;
+ Fts5Config *pConfig = ((Fts5Table*)(pCsr->base.pVtab))->pConfig;
+
+ if( pConfig->eDetail==FTS5_DETAIL_COLUMNS ){
+ if( pIter->a>=pIter->b ){
+ *piCol = -1;
+ }else{
+ int iIncr;
+ pIter->a += fts5GetVarint32(&pIter->a[0], iIncr);
+ *piCol += (iIncr-2);
+ }
+ }else{
+ while( 1 ){
+ int dummy;
+ if( pIter->a>=pIter->b ){
+ *piCol = -1;
+ return;
+ }
+ if( pIter->a[0]==0x01 ) break;
+ pIter->a += fts5GetVarint32(pIter->a, dummy);
+ }
+ pIter->a += 1 + fts5GetVarint32(&pIter->a[1], *piCol);
+ }
+}
+
+static int fts5ApiPhraseFirstColumn(
+ Fts5Context *pCtx,
+ int iPhrase,
+ Fts5PhraseIter *pIter,
+ int *piCol
+){
+ int rc = SQLITE_OK;
+ Fts5Cursor *pCsr = (Fts5Cursor*)pCtx;
+ Fts5Config *pConfig = ((Fts5Table*)(pCsr->base.pVtab))->pConfig;
+
+ if( pConfig->eDetail==FTS5_DETAIL_COLUMNS ){
+ Fts5Sorter *pSorter = pCsr->pSorter;
+ int n;
+ if( pSorter ){
+ int i1 = (iPhrase==0 ? 0 : pSorter->aIdx[iPhrase-1]);
+ n = pSorter->aIdx[iPhrase] - i1;
+ pIter->a = &pSorter->aPoslist[i1];
+ }else{
+ rc = sqlite3Fts5ExprPhraseCollist(pCsr->pExpr, iPhrase, &pIter->a, &n);
+ }
+ if( rc==SQLITE_OK ){
+ pIter->b = &pIter->a[n];
+ *piCol = 0;
+ fts5ApiPhraseNextColumn(pCtx, pIter, piCol);
+ }
+ }else{
+ int n;
+ rc = fts5CsrPoslist(pCsr, iPhrase, &pIter->a, &n);
+ if( rc==SQLITE_OK ){
+ pIter->b = &pIter->a[n];
+ if( n<=0 ){
+ *piCol = -1;
+ }else if( pIter->a[0]==0x01 ){
+ pIter->a += 1 + fts5GetVarint32(&pIter->a[1], *piCol);
+ }else{
+ *piCol = 0;
+ }
+ }
+ }
+
+ return rc;
+}
+
+
+static int fts5ApiQueryPhrase(Fts5Context*, int, void*,
+ int(*)(const Fts5ExtensionApi*, Fts5Context*, void*)
+);
+
+static const Fts5ExtensionApi sFts5Api = {
+ 2, /* iVersion */
+ fts5ApiUserData,
+ fts5ApiColumnCount,
+ fts5ApiRowCount,
+ fts5ApiColumnTotalSize,
+ fts5ApiTokenize,
+ fts5ApiPhraseCount,
+ fts5ApiPhraseSize,
+ fts5ApiInstCount,
+ fts5ApiInst,
+ fts5ApiRowid,
+ fts5ApiColumnText,
+ fts5ApiColumnSize,
+ fts5ApiQueryPhrase,
+ fts5ApiSetAuxdata,
+ fts5ApiGetAuxdata,
+ fts5ApiPhraseFirst,
+ fts5ApiPhraseNext,
+ fts5ApiPhraseFirstColumn,
+ fts5ApiPhraseNextColumn,
+};
+
+/*
+** Implementation of API function xQueryPhrase().
+*/
+static int fts5ApiQueryPhrase(
+ Fts5Context *pCtx,
+ int iPhrase,
+ void *pUserData,
+ int(*xCallback)(const Fts5ExtensionApi*, Fts5Context*, void*)
+){
+ Fts5Cursor *pCsr = (Fts5Cursor*)pCtx;
+ Fts5Table *pTab = (Fts5Table*)(pCsr->base.pVtab);
+ int rc;
+ Fts5Cursor *pNew = 0;
+
+ rc = fts5OpenMethod(pCsr->base.pVtab, (sqlite3_vtab_cursor**)&pNew);
+ if( rc==SQLITE_OK ){
+ pNew->ePlan = FTS5_PLAN_MATCH;
+ pNew->iFirstRowid = SMALLEST_INT64;
+ pNew->iLastRowid = LARGEST_INT64;
+ pNew->base.pVtab = (sqlite3_vtab*)pTab;
+ rc = sqlite3Fts5ExprClonePhrase(pCsr->pExpr, iPhrase, &pNew->pExpr);
+ }
+
+ if( rc==SQLITE_OK ){
+ for(rc = fts5CursorFirst(pTab, pNew, 0);
+ rc==SQLITE_OK && CsrFlagTest(pNew, FTS5CSR_EOF)==0;
+ rc = fts5NextMethod((sqlite3_vtab_cursor*)pNew)
+ ){
+ rc = xCallback(&sFts5Api, (Fts5Context*)pNew, pUserData);
+ if( rc!=SQLITE_OK ){
+ if( rc==SQLITE_DONE ) rc = SQLITE_OK;
+ break;
+ }
+ }
+ }
+
+ fts5CloseMethod((sqlite3_vtab_cursor*)pNew);
+ return rc;
+}
+
+static void fts5ApiInvoke(
+ Fts5Auxiliary *pAux,
+ Fts5Cursor *pCsr,
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ assert( pCsr->pAux==0 );
+ pCsr->pAux = pAux;
+ pAux->xFunc(&sFts5Api, (Fts5Context*)pCsr, context, argc, argv);
+ pCsr->pAux = 0;
+}
+
+static Fts5Cursor *fts5CursorFromCsrid(Fts5Global *pGlobal, i64 iCsrId){
+ Fts5Cursor *pCsr;
+ for(pCsr=pGlobal->pCsr; pCsr; pCsr=pCsr->pNext){
+ if( pCsr->iCsrId==iCsrId ) break;
+ }
+ return pCsr;
+}
+
+static void fts5ApiCallback(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+
+ Fts5Auxiliary *pAux;
+ Fts5Cursor *pCsr;
+ i64 iCsrId;
+
+ assert( argc>=1 );
+ pAux = (Fts5Auxiliary*)sqlite3_user_data(context);
+ iCsrId = sqlite3_value_int64(argv[0]);
+
+ pCsr = fts5CursorFromCsrid(pAux->pGlobal, iCsrId);
+ if( pCsr==0 ){
+ char *zErr = sqlite3_mprintf("no such cursor: %lld", iCsrId);
+ sqlite3_result_error(context, zErr, -1);
+ sqlite3_free(zErr);
+ }else{
+ fts5ApiInvoke(pAux, pCsr, context, argc-1, &argv[1]);
+ }
+}
+
+
+/*
+** Given cursor id iId, return a pointer to the corresponding Fts5Index
+** object. Or NULL If the cursor id does not exist.
+**
+** If successful, set *ppConfig to point to the associated config object
+** before returning.
+*/
+static Fts5Index *sqlite3Fts5IndexFromCsrid(
+ Fts5Global *pGlobal, /* FTS5 global context for db handle */
+ i64 iCsrId, /* Id of cursor to find */
+ Fts5Config **ppConfig /* OUT: Configuration object */
+){
+ Fts5Cursor *pCsr;
+ Fts5Table *pTab;
+
+ pCsr = fts5CursorFromCsrid(pGlobal, iCsrId);
+ pTab = (Fts5Table*)pCsr->base.pVtab;
+ *ppConfig = pTab->pConfig;
+
+ return pTab->pIndex;
+}
+
+/*
+** Return a "position-list blob" corresponding to the current position of
+** cursor pCsr via sqlite3_result_blob(). A position-list blob contains
+** the current position-list for each phrase in the query associated with
+** cursor pCsr.
+**
+** A position-list blob begins with (nPhrase-1) varints, where nPhrase is
+** the number of phrases in the query. Following the varints are the
+** concatenated position lists for each phrase, in order.
+**
+** The first varint (if it exists) contains the size of the position list
+** for phrase 0. The second (same disclaimer) contains the size of position
+** list 1. And so on. There is no size field for the final position list,
+** as it can be derived from the total size of the blob.
+*/
+static int fts5PoslistBlob(sqlite3_context *pCtx, Fts5Cursor *pCsr){
+ int i;
+ int rc = SQLITE_OK;
+ int nPhrase = sqlite3Fts5ExprPhraseCount(pCsr->pExpr);
+ Fts5Buffer val;
+
+ memset(&val, 0, sizeof(Fts5Buffer));
+ switch( ((Fts5Table*)(pCsr->base.pVtab))->pConfig->eDetail ){
+ case FTS5_DETAIL_FULL:
+
+ /* Append the varints */
+ for(i=0; i<(nPhrase-1); i++){
+ const u8 *dummy;
+ int nByte = sqlite3Fts5ExprPoslist(pCsr->pExpr, i, &dummy);
+ sqlite3Fts5BufferAppendVarint(&rc, &val, nByte);
+ }
+
+ /* Append the position lists */
+ for(i=0; i<nPhrase; i++){
+ const u8 *pPoslist;
+ int nPoslist;
+ nPoslist = sqlite3Fts5ExprPoslist(pCsr->pExpr, i, &pPoslist);
+ sqlite3Fts5BufferAppendBlob(&rc, &val, nPoslist, pPoslist);
+ }
+ break;
+
+ case FTS5_DETAIL_COLUMNS:
+
+ /* Append the varints */
+ for(i=0; rc==SQLITE_OK && i<(nPhrase-1); i++){
+ const u8 *dummy;
+ int nByte;
+ rc = sqlite3Fts5ExprPhraseCollist(pCsr->pExpr, i, &dummy, &nByte);
+ sqlite3Fts5BufferAppendVarint(&rc, &val, nByte);
+ }
+
+ /* Append the position lists */
+ for(i=0; rc==SQLITE_OK && i<nPhrase; i++){
+ const u8 *pPoslist;
+ int nPoslist;
+ rc = sqlite3Fts5ExprPhraseCollist(pCsr->pExpr, i, &pPoslist, &nPoslist);
+ sqlite3Fts5BufferAppendBlob(&rc, &val, nPoslist, pPoslist);
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ sqlite3_result_blob(pCtx, val.p, val.n, sqlite3_free);
+ return rc;
+}
+
+/*
+** This is the xColumn method, called by SQLite to request a value from
+** the row that the supplied cursor currently points to.
+*/
+static int fts5ColumnMethod(
+ sqlite3_vtab_cursor *pCursor, /* Cursor to retrieve value from */
+ sqlite3_context *pCtx, /* Context for sqlite3_result_xxx() calls */
+ int iCol /* Index of column to read value from */
+){
+ Fts5Table *pTab = (Fts5Table*)(pCursor->pVtab);
+ Fts5Config *pConfig = pTab->pConfig;
+ Fts5Cursor *pCsr = (Fts5Cursor*)pCursor;
+ int rc = SQLITE_OK;
+
+ assert( CsrFlagTest(pCsr, FTS5CSR_EOF)==0 );
+
+ if( pCsr->ePlan==FTS5_PLAN_SPECIAL ){
+ if( iCol==pConfig->nCol ){
+ sqlite3_result_int64(pCtx, pCsr->iSpecial);
+ }
+ }else
+
+ if( iCol==pConfig->nCol ){
+ /* User is requesting the value of the special column with the same name
+ ** as the table. Return the cursor integer id number. This value is only
+ ** useful in that it may be passed as the first argument to an FTS5
+ ** auxiliary function. */
+ sqlite3_result_int64(pCtx, pCsr->iCsrId);
+ }else if( iCol==pConfig->nCol+1 ){
+
+ /* The value of the "rank" column. */
+ if( pCsr->ePlan==FTS5_PLAN_SOURCE ){
+ fts5PoslistBlob(pCtx, pCsr);
+ }else if(
+ pCsr->ePlan==FTS5_PLAN_MATCH
+ || pCsr->ePlan==FTS5_PLAN_SORTED_MATCH
+ ){
+ if( pCsr->pRank || SQLITE_OK==(rc = fts5FindRankFunction(pCsr)) ){
+ fts5ApiInvoke(pCsr->pRank, pCsr, pCtx, pCsr->nRankArg, pCsr->apRankArg);
+ }
+ }
+ }else if( !fts5IsContentless(pTab) ){
+ rc = fts5SeekCursor(pCsr, 1);
+ if( rc==SQLITE_OK ){
+ sqlite3_result_value(pCtx, sqlite3_column_value(pCsr->pStmt, iCol+1));
+ }
+ }
+ return rc;
+}
+
+
+/*
+** This routine implements the xFindFunction method for the FTS3
+** virtual table.
+*/
+static int fts5FindFunctionMethod(
+ sqlite3_vtab *pVtab, /* Virtual table handle */
+ int nUnused, /* Number of SQL function arguments */
+ const char *zName, /* Name of SQL function */
+ void (**pxFunc)(sqlite3_context*,int,sqlite3_value**), /* OUT: Result */
+ void **ppArg /* OUT: User data for *pxFunc */
+){
+ Fts5Table *pTab = (Fts5Table*)pVtab;
+ Fts5Auxiliary *pAux;
+
+ UNUSED_PARAM(nUnused);
+ pAux = fts5FindAuxiliary(pTab, zName);
+ if( pAux ){
+ *pxFunc = fts5ApiCallback;
+ *ppArg = (void*)pAux;
+ return 1;
+ }
+
+ /* No function of the specified name was found. Return 0. */
+ return 0;
+}
+
+/*
+** Implementation of FTS5 xRename method. Rename an fts5 table.
+*/
+static int fts5RenameMethod(
+ sqlite3_vtab *pVtab, /* Virtual table handle */
+ const char *zName /* New name of table */
+){
+ Fts5Table *pTab = (Fts5Table*)pVtab;
+ return sqlite3Fts5StorageRename(pTab->pStorage, zName);
+}
+
+/*
+** The xSavepoint() method.
+**
+** Flush the contents of the pending-terms table to disk.
+*/
+static int fts5SavepointMethod(sqlite3_vtab *pVtab, int iSavepoint){
+ Fts5Table *pTab = (Fts5Table*)pVtab;
+ UNUSED_PARAM(iSavepoint); /* Call below is a no-op for NDEBUG builds */
+ fts5CheckTransactionState(pTab, FTS5_SAVEPOINT, iSavepoint);
+ fts5TripCursors(pTab);
+ return sqlite3Fts5StorageSync(pTab->pStorage, 0);
+}
+
+/*
+** The xRelease() method.
+**
+** This is a no-op.
+*/
+static int fts5ReleaseMethod(sqlite3_vtab *pVtab, int iSavepoint){
+ Fts5Table *pTab = (Fts5Table*)pVtab;
+ UNUSED_PARAM(iSavepoint); /* Call below is a no-op for NDEBUG builds */
+ fts5CheckTransactionState(pTab, FTS5_RELEASE, iSavepoint);
+ fts5TripCursors(pTab);
+ return sqlite3Fts5StorageSync(pTab->pStorage, 0);
+}
+
+/*
+** The xRollbackTo() method.
+**
+** Discard the contents of the pending terms table.
+*/
+static int fts5RollbackToMethod(sqlite3_vtab *pVtab, int iSavepoint){
+ Fts5Table *pTab = (Fts5Table*)pVtab;
+ UNUSED_PARAM(iSavepoint); /* Call below is a no-op for NDEBUG builds */
+ fts5CheckTransactionState(pTab, FTS5_ROLLBACKTO, iSavepoint);
+ fts5TripCursors(pTab);
+ return sqlite3Fts5StorageRollback(pTab->pStorage);
+}
+
+/*
+** Register a new auxiliary function with global context pGlobal.
+*/
+static int fts5CreateAux(
+ fts5_api *pApi, /* Global context (one per db handle) */
+ const char *zName, /* Name of new function */
+ void *pUserData, /* User data for aux. function */
+ fts5_extension_function xFunc, /* Aux. function implementation */
+ void(*xDestroy)(void*) /* Destructor for pUserData */
+){
+ Fts5Global *pGlobal = (Fts5Global*)pApi;
+ int rc = sqlite3_overload_function(pGlobal->db, zName, -1);
+ if( rc==SQLITE_OK ){
+ Fts5Auxiliary *pAux;
+ int nName; /* Size of zName in bytes, including \0 */
+ int nByte; /* Bytes of space to allocate */
+
+ nName = (int)strlen(zName) + 1;
+ nByte = sizeof(Fts5Auxiliary) + nName;
+ pAux = (Fts5Auxiliary*)sqlite3_malloc(nByte);
+ if( pAux ){
+ memset(pAux, 0, nByte);
+ pAux->zFunc = (char*)&pAux[1];
+ memcpy(pAux->zFunc, zName, nName);
+ pAux->pGlobal = pGlobal;
+ pAux->pUserData = pUserData;
+ pAux->xFunc = xFunc;
+ pAux->xDestroy = xDestroy;
+ pAux->pNext = pGlobal->pAux;
+ pGlobal->pAux = pAux;
+ }else{
+ rc = SQLITE_NOMEM;
+ }
+ }
+
+ return rc;
+}
+
+/*
+** Register a new tokenizer. This is the implementation of the
+** fts5_api.xCreateTokenizer() method.
+*/
+static int fts5CreateTokenizer(
+ fts5_api *pApi, /* Global context (one per db handle) */
+ const char *zName, /* Name of new function */
+ void *pUserData, /* User data for aux. function */
+ fts5_tokenizer *pTokenizer, /* Tokenizer implementation */
+ void(*xDestroy)(void*) /* Destructor for pUserData */
+){
+ Fts5Global *pGlobal = (Fts5Global*)pApi;
+ Fts5TokenizerModule *pNew;
+ int nName; /* Size of zName and its \0 terminator */
+ int nByte; /* Bytes of space to allocate */
+ int rc = SQLITE_OK;
+
+ nName = (int)strlen(zName) + 1;
+ nByte = sizeof(Fts5TokenizerModule) + nName;
+ pNew = (Fts5TokenizerModule*)sqlite3_malloc(nByte);
+ if( pNew ){
+ memset(pNew, 0, nByte);
+ pNew->zName = (char*)&pNew[1];
+ memcpy(pNew->zName, zName, nName);
+ pNew->pUserData = pUserData;
+ pNew->x = *pTokenizer;
+ pNew->xDestroy = xDestroy;
+ pNew->pNext = pGlobal->pTok;
+ pGlobal->pTok = pNew;
+ if( pNew->pNext==0 ){
+ pGlobal->pDfltTok = pNew;
+ }
+ }else{
+ rc = SQLITE_NOMEM;
+ }
+
+ return rc;
+}
+
+static Fts5TokenizerModule *fts5LocateTokenizer(
+ Fts5Global *pGlobal,
+ const char *zName
+){
+ Fts5TokenizerModule *pMod = 0;
+
+ if( zName==0 ){
+ pMod = pGlobal->pDfltTok;
+ }else{
+ for(pMod=pGlobal->pTok; pMod; pMod=pMod->pNext){
+ if( sqlite3_stricmp(zName, pMod->zName)==0 ) break;
+ }
+ }
+
+ return pMod;
+}
+
+/*
+** Find a tokenizer. This is the implementation of the
+** fts5_api.xFindTokenizer() method.
+*/
+static int fts5FindTokenizer(
+ fts5_api *pApi, /* Global context (one per db handle) */
+ const char *zName, /* Name of new function */
+ void **ppUserData,
+ fts5_tokenizer *pTokenizer /* Populate this object */
+){
+ int rc = SQLITE_OK;
+ Fts5TokenizerModule *pMod;
+
+ pMod = fts5LocateTokenizer((Fts5Global*)pApi, zName);
+ if( pMod ){
+ *pTokenizer = pMod->x;
+ *ppUserData = pMod->pUserData;
+ }else{
+ memset(pTokenizer, 0, sizeof(fts5_tokenizer));
+ rc = SQLITE_ERROR;
+ }
+
+ return rc;
+}
+
+static int sqlite3Fts5GetTokenizer(
+ Fts5Global *pGlobal,
+ const char **azArg,
+ int nArg,
+ Fts5Tokenizer **ppTok,
+ fts5_tokenizer **ppTokApi,
+ char **pzErr
+){
+ Fts5TokenizerModule *pMod;
+ int rc = SQLITE_OK;
+
+ pMod = fts5LocateTokenizer(pGlobal, nArg==0 ? 0 : azArg[0]);
+ if( pMod==0 ){
+ assert( nArg>0 );
+ rc = SQLITE_ERROR;
+ *pzErr = sqlite3_mprintf("no such tokenizer: %s", azArg[0]);
+ }else{
+ rc = pMod->x.xCreate(pMod->pUserData, &azArg[1], (nArg?nArg-1:0), ppTok);
+ *ppTokApi = &pMod->x;
+ if( rc!=SQLITE_OK && pzErr ){
+ *pzErr = sqlite3_mprintf("error in tokenizer constructor");
+ }
+ }
+
+ if( rc!=SQLITE_OK ){
+ *ppTokApi = 0;
+ *ppTok = 0;
+ }
+
+ return rc;
+}
+
+static void fts5ModuleDestroy(void *pCtx){
+ Fts5TokenizerModule *pTok, *pNextTok;
+ Fts5Auxiliary *pAux, *pNextAux;
+ Fts5Global *pGlobal = (Fts5Global*)pCtx;
+
+ for(pAux=pGlobal->pAux; pAux; pAux=pNextAux){
+ pNextAux = pAux->pNext;
+ if( pAux->xDestroy ) pAux->xDestroy(pAux->pUserData);
+ sqlite3_free(pAux);
+ }
+
+ for(pTok=pGlobal->pTok; pTok; pTok=pNextTok){
+ pNextTok = pTok->pNext;
+ if( pTok->xDestroy ) pTok->xDestroy(pTok->pUserData);
+ sqlite3_free(pTok);
+ }
+
+ sqlite3_free(pGlobal);
+}
+
+static void fts5Fts5Func(
+ sqlite3_context *pCtx, /* Function call context */
+ int nArg, /* Number of args */
+ sqlite3_value **apUnused /* Function arguments */
+){
+ Fts5Global *pGlobal = (Fts5Global*)sqlite3_user_data(pCtx);
+ char buf[8];
+ UNUSED_PARAM2(nArg, apUnused);
+ assert( nArg==0 );
+ assert( sizeof(buf)>=sizeof(pGlobal) );
+ memcpy(buf, (void*)&pGlobal, sizeof(pGlobal));
+ sqlite3_result_blob(pCtx, buf, sizeof(pGlobal), SQLITE_TRANSIENT);
+}
+
+/*
+** Implementation of fts5_source_id() function.
+*/
+static void fts5SourceIdFunc(
+ sqlite3_context *pCtx, /* Function call context */
+ int nArg, /* Number of args */
+ sqlite3_value **apUnused /* Function arguments */
+){
+ assert( nArg==0 );
+ UNUSED_PARAM2(nArg, apUnused);
+ sqlite3_result_text(pCtx, "fts5: 2016-05-18 10:57:30 fc49f556e48970561d7ab6a2f24fdd7d9eb81ff2", -1, SQLITE_TRANSIENT);
+}
+
+static int fts5Init(sqlite3 *db){
+ static const sqlite3_module fts5Mod = {
+ /* iVersion */ 2,
+ /* xCreate */ fts5CreateMethod,
+ /* xConnect */ fts5ConnectMethod,
+ /* xBestIndex */ fts5BestIndexMethod,
+ /* xDisconnect */ fts5DisconnectMethod,
+ /* xDestroy */ fts5DestroyMethod,
+ /* xOpen */ fts5OpenMethod,
+ /* xClose */ fts5CloseMethod,
+ /* xFilter */ fts5FilterMethod,
+ /* xNext */ fts5NextMethod,
+ /* xEof */ fts5EofMethod,
+ /* xColumn */ fts5ColumnMethod,
+ /* xRowid */ fts5RowidMethod,
+ /* xUpdate */ fts5UpdateMethod,
+ /* xBegin */ fts5BeginMethod,
+ /* xSync */ fts5SyncMethod,
+ /* xCommit */ fts5CommitMethod,
+ /* xRollback */ fts5RollbackMethod,
+ /* xFindFunction */ fts5FindFunctionMethod,
+ /* xRename */ fts5RenameMethod,
+ /* xSavepoint */ fts5SavepointMethod,
+ /* xRelease */ fts5ReleaseMethod,
+ /* xRollbackTo */ fts5RollbackToMethod,
+ };
+
+ int rc;
+ Fts5Global *pGlobal = 0;
+
+ pGlobal = (Fts5Global*)sqlite3_malloc(sizeof(Fts5Global));
+ if( pGlobal==0 ){
+ rc = SQLITE_NOMEM;
+ }else{
+ void *p = (void*)pGlobal;
+ memset(pGlobal, 0, sizeof(Fts5Global));
+ pGlobal->db = db;
+ pGlobal->api.iVersion = 2;
+ pGlobal->api.xCreateFunction = fts5CreateAux;
+ pGlobal->api.xCreateTokenizer = fts5CreateTokenizer;
+ pGlobal->api.xFindTokenizer = fts5FindTokenizer;
+ rc = sqlite3_create_module_v2(db, "fts5", &fts5Mod, p, fts5ModuleDestroy);
+ if( rc==SQLITE_OK ) rc = sqlite3Fts5IndexInit(db);
+ if( rc==SQLITE_OK ) rc = sqlite3Fts5ExprInit(pGlobal, db);
+ if( rc==SQLITE_OK ) rc = sqlite3Fts5AuxInit(&pGlobal->api);
+ if( rc==SQLITE_OK ) rc = sqlite3Fts5TokenizerInit(&pGlobal->api);
+ if( rc==SQLITE_OK ) rc = sqlite3Fts5VocabInit(pGlobal, db);
+ if( rc==SQLITE_OK ){
+ rc = sqlite3_create_function(
+ db, "fts5", 0, SQLITE_UTF8, p, fts5Fts5Func, 0, 0
+ );
+ }
+ if( rc==SQLITE_OK ){
+ rc = sqlite3_create_function(
+ db, "fts5_source_id", 0, SQLITE_UTF8, p, fts5SourceIdFunc, 0, 0
+ );
+ }
+ }
+
+ /* If SQLITE_FTS5_ENABLE_TEST_MI is defined, assume that the file
+ ** fts5_test_mi.c is compiled and linked into the executable. And call
+ ** its entry point to enable the matchinfo() demo. */
+#ifdef SQLITE_FTS5_ENABLE_TEST_MI
+ if( rc==SQLITE_OK ){
+ extern int sqlite3Fts5TestRegisterMatchinfo(sqlite3*);
+ rc = sqlite3Fts5TestRegisterMatchinfo(db);
+ }
+#endif
+
+ return rc;
+}
+
+/*
+** The following functions are used to register the module with SQLite. If
+** this module is being built as part of the SQLite core (SQLITE_CORE is
+** defined), then sqlite3_open() will call sqlite3Fts5Init() directly.
+**
+** Or, if this module is being built as a loadable extension,
+** sqlite3Fts5Init() is omitted and the two standard entry points
+** sqlite3_fts_init() and sqlite3_fts5_init() defined instead.
+*/
+#ifndef SQLITE_CORE
+#ifdef _WIN32
+__declspec(dllexport)
+#endif
+SQLITE_API int SQLITE_STDCALL sqlite3_fts_init(
+ sqlite3 *db,
+ char **pzErrMsg,
+ const sqlite3_api_routines *pApi
+){
+ SQLITE_EXTENSION_INIT2(pApi);
+ (void)pzErrMsg; /* Unused parameter */
+ return fts5Init(db);
+}
+
+#ifdef _WIN32
+__declspec(dllexport)
+#endif
+SQLITE_API int SQLITE_STDCALL sqlite3_fts5_init(
+ sqlite3 *db,
+ char **pzErrMsg,
+ const sqlite3_api_routines *pApi
+){
+ SQLITE_EXTENSION_INIT2(pApi);
+ (void)pzErrMsg; /* Unused parameter */
+ return fts5Init(db);
+}
+#else
+SQLITE_PRIVATE int sqlite3Fts5Init(sqlite3 *db){
+ return fts5Init(db);
+}
+#endif
+
+/*
+** 2014 May 31
+**
+** 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.
+**
+******************************************************************************
+**
+*/
+
+
+
+/* #include "fts5Int.h" */
+
+struct Fts5Storage {
+ Fts5Config *pConfig;
+ Fts5Index *pIndex;
+ int bTotalsValid; /* True if nTotalRow/aTotalSize[] are valid */
+ i64 nTotalRow; /* Total number of rows in FTS table */
+ i64 *aTotalSize; /* Total sizes of each column */
+ sqlite3_stmt *aStmt[11];
+};
+
+
+#if FTS5_STMT_SCAN_ASC!=0
+# error "FTS5_STMT_SCAN_ASC mismatch"
+#endif
+#if FTS5_STMT_SCAN_DESC!=1
+# error "FTS5_STMT_SCAN_DESC mismatch"
+#endif
+#if FTS5_STMT_LOOKUP!=2
+# error "FTS5_STMT_LOOKUP mismatch"
+#endif
+
+#define FTS5_STMT_INSERT_CONTENT 3
+#define FTS5_STMT_REPLACE_CONTENT 4
+#define FTS5_STMT_DELETE_CONTENT 5
+#define FTS5_STMT_REPLACE_DOCSIZE 6
+#define FTS5_STMT_DELETE_DOCSIZE 7
+#define FTS5_STMT_LOOKUP_DOCSIZE 8
+#define FTS5_STMT_REPLACE_CONFIG 9
+#define FTS5_STMT_SCAN 10
+
+/*
+** Prepare the two insert statements - Fts5Storage.pInsertContent and
+** Fts5Storage.pInsertDocsize - if they have not already been prepared.
+** Return SQLITE_OK if successful, or an SQLite error code if an error
+** occurs.
+*/
+static int fts5StorageGetStmt(
+ Fts5Storage *p, /* Storage handle */
+ int eStmt, /* FTS5_STMT_XXX constant */
+ sqlite3_stmt **ppStmt, /* OUT: Prepared statement handle */
+ char **pzErrMsg /* OUT: Error message (if any) */
+){
+ int rc = SQLITE_OK;
+
+ /* If there is no %_docsize table, there should be no requests for
+ ** statements to operate on it. */
+ assert( p->pConfig->bColumnsize || (
+ eStmt!=FTS5_STMT_REPLACE_DOCSIZE
+ && eStmt!=FTS5_STMT_DELETE_DOCSIZE
+ && eStmt!=FTS5_STMT_LOOKUP_DOCSIZE
+ ));
+
+ assert( eStmt>=0 && eStmt<ArraySize(p->aStmt) );
+ if( p->aStmt[eStmt]==0 ){
+ const char *azStmt[] = {
+ "SELECT %s FROM %s T WHERE T.%Q >= ? AND T.%Q <= ? ORDER BY T.%Q ASC",
+ "SELECT %s FROM %s T WHERE T.%Q <= ? AND T.%Q >= ? ORDER BY T.%Q DESC",
+ "SELECT %s FROM %s T WHERE T.%Q=?", /* LOOKUP */
+
+ "INSERT INTO %Q.'%q_content' VALUES(%s)", /* INSERT_CONTENT */
+ "REPLACE INTO %Q.'%q_content' VALUES(%s)", /* REPLACE_CONTENT */
+ "DELETE FROM %Q.'%q_content' WHERE id=?", /* DELETE_CONTENT */
+ "REPLACE INTO %Q.'%q_docsize' VALUES(?,?)", /* REPLACE_DOCSIZE */
+ "DELETE FROM %Q.'%q_docsize' WHERE id=?", /* DELETE_DOCSIZE */
+
+ "SELECT sz FROM %Q.'%q_docsize' WHERE id=?", /* LOOKUP_DOCSIZE */
+
+ "REPLACE INTO %Q.'%q_config' VALUES(?,?)", /* REPLACE_CONFIG */
+ "SELECT %s FROM %s AS T", /* SCAN */
+ };
+ Fts5Config *pC = p->pConfig;
+ char *zSql = 0;
+
+ switch( eStmt ){
+ case FTS5_STMT_SCAN:
+ zSql = sqlite3_mprintf(azStmt[eStmt],
+ pC->zContentExprlist, pC->zContent
+ );
+ break;
+
+ case FTS5_STMT_SCAN_ASC:
+ case FTS5_STMT_SCAN_DESC:
+ zSql = sqlite3_mprintf(azStmt[eStmt], pC->zContentExprlist,
+ pC->zContent, pC->zContentRowid, pC->zContentRowid,
+ pC->zContentRowid
+ );
+ break;
+
+ case FTS5_STMT_LOOKUP:
+ zSql = sqlite3_mprintf(azStmt[eStmt],
+ pC->zContentExprlist, pC->zContent, pC->zContentRowid
+ );
+ break;
+
+ case FTS5_STMT_INSERT_CONTENT:
+ case FTS5_STMT_REPLACE_CONTENT: {
+ int nCol = pC->nCol + 1;
+ char *zBind;
+ int i;
+
+ zBind = sqlite3_malloc(1 + nCol*2);
+ if( zBind ){
+ for(i=0; i<nCol; i++){
+ zBind[i*2] = '?';
+ zBind[i*2 + 1] = ',';
+ }
+ zBind[i*2-1] = '\0';
+ zSql = sqlite3_mprintf(azStmt[eStmt], pC->zDb, pC->zName, zBind);
+ sqlite3_free(zBind);
+ }
+ break;
+ }
+
+ default:
+ zSql = sqlite3_mprintf(azStmt[eStmt], pC->zDb, pC->zName);
+ break;
+ }
+
+ if( zSql==0 ){
+ rc = SQLITE_NOMEM;
+ }else{
+ rc = sqlite3_prepare_v2(pC->db, zSql, -1, &p->aStmt[eStmt], 0);
+ sqlite3_free(zSql);
+ if( rc!=SQLITE_OK && pzErrMsg ){
+ *pzErrMsg = sqlite3_mprintf("%s", sqlite3_errmsg(pC->db));
+ }
+ }
+ }
+
+ *ppStmt = p->aStmt[eStmt];
+ sqlite3_reset(*ppStmt);
+ return rc;
+}
+
+
+static int fts5ExecPrintf(
+ sqlite3 *db,
+ char **pzErr,
+ const char *zFormat,
+ ...
+){
+ int rc;
+ va_list ap; /* ... printf arguments */
+ char *zSql;
+
+ va_start(ap, zFormat);
+ zSql = sqlite3_vmprintf(zFormat, ap);
+
+ if( zSql==0 ){
+ rc = SQLITE_NOMEM;
+ }else{
+ rc = sqlite3_exec(db, zSql, 0, 0, pzErr);
+ sqlite3_free(zSql);
+ }
+
+ va_end(ap);
+ return rc;
+}
+
+/*
+** Drop all shadow tables. Return SQLITE_OK if successful or an SQLite error
+** code otherwise.
+*/
+static int sqlite3Fts5DropAll(Fts5Config *pConfig){
+ int rc = fts5ExecPrintf(pConfig->db, 0,
+ "DROP TABLE IF EXISTS %Q.'%q_data';"
+ "DROP TABLE IF EXISTS %Q.'%q_idx';"
+ "DROP TABLE IF EXISTS %Q.'%q_config';",
+ pConfig->zDb, pConfig->zName,
+ pConfig->zDb, pConfig->zName,
+ pConfig->zDb, pConfig->zName
+ );
+ if( rc==SQLITE_OK && pConfig->bColumnsize ){
+ rc = fts5ExecPrintf(pConfig->db, 0,
+ "DROP TABLE IF EXISTS %Q.'%q_docsize';",
+ pConfig->zDb, pConfig->zName
+ );
+ }
+ if( rc==SQLITE_OK && pConfig->eContent==FTS5_CONTENT_NORMAL ){
+ rc = fts5ExecPrintf(pConfig->db, 0,
+ "DROP TABLE IF EXISTS %Q.'%q_content';",
+ pConfig->zDb, pConfig->zName
+ );
+ }
+ return rc;
+}
+
+static void fts5StorageRenameOne(
+ Fts5Config *pConfig, /* Current FTS5 configuration */
+ int *pRc, /* IN/OUT: Error code */
+ const char *zTail, /* Tail of table name e.g. "data", "config" */
+ const char *zName /* New name of FTS5 table */
+){
+ if( *pRc==SQLITE_OK ){
+ *pRc = fts5ExecPrintf(pConfig->db, 0,
+ "ALTER TABLE %Q.'%q_%s' RENAME TO '%q_%s';",
+ pConfig->zDb, pConfig->zName, zTail, zName, zTail
+ );
+ }
+}
+
+static int sqlite3Fts5StorageRename(Fts5Storage *pStorage, const char *zName){
+ Fts5Config *pConfig = pStorage->pConfig;
+ int rc = sqlite3Fts5StorageSync(pStorage, 1);
+
+ fts5StorageRenameOne(pConfig, &rc, "data", zName);
+ fts5StorageRenameOne(pConfig, &rc, "idx", zName);
+ fts5StorageRenameOne(pConfig, &rc, "config", zName);
+ if( pConfig->bColumnsize ){
+ fts5StorageRenameOne(pConfig, &rc, "docsize", zName);
+ }
+ if( pConfig->eContent==FTS5_CONTENT_NORMAL ){
+ fts5StorageRenameOne(pConfig, &rc, "content", zName);
+ }
+ return rc;
+}
+
+/*
+** Create the shadow table named zPost, with definition zDefn. Return
+** SQLITE_OK if successful, or an SQLite error code otherwise.
+*/
+static int sqlite3Fts5CreateTable(
+ Fts5Config *pConfig, /* FTS5 configuration */
+ const char *zPost, /* Shadow table to create (e.g. "content") */
+ const char *zDefn, /* Columns etc. for shadow table */
+ int bWithout, /* True for without rowid */
+ char **pzErr /* OUT: Error message */
+){
+ int rc;
+ char *zErr = 0;
+
+ rc = fts5ExecPrintf(pConfig->db, &zErr, "CREATE TABLE %Q.'%q_%q'(%s)%s",
+ pConfig->zDb, pConfig->zName, zPost, zDefn, bWithout?" WITHOUT ROWID":""
+ );
+ if( zErr ){
+ *pzErr = sqlite3_mprintf(
+ "fts5: error creating shadow table %q_%s: %s",
+ pConfig->zName, zPost, zErr
+ );
+ sqlite3_free(zErr);
+ }
+
+ return rc;
+}
+
+/*
+** Open a new Fts5Index handle. If the bCreate argument is true, create
+** and initialize the underlying tables
+**
+** If successful, set *pp to point to the new object and return SQLITE_OK.
+** Otherwise, set *pp to NULL and return an SQLite error code.
+*/
+static int sqlite3Fts5StorageOpen(
+ Fts5Config *pConfig,
+ Fts5Index *pIndex,
+ int bCreate,
+ Fts5Storage **pp,
+ char **pzErr /* OUT: Error message */
+){
+ int rc = SQLITE_OK;
+ Fts5Storage *p; /* New object */
+ int nByte; /* Bytes of space to allocate */
+
+ nByte = sizeof(Fts5Storage) /* Fts5Storage object */
+ + pConfig->nCol * sizeof(i64); /* Fts5Storage.aTotalSize[] */
+ *pp = p = (Fts5Storage*)sqlite3_malloc(nByte);
+ if( !p ) return SQLITE_NOMEM;
+
+ memset(p, 0, nByte);
+ p->aTotalSize = (i64*)&p[1];
+ p->pConfig = pConfig;
+ p->pIndex = pIndex;
+
+ if( bCreate ){
+ if( pConfig->eContent==FTS5_CONTENT_NORMAL ){
+ int nDefn = 32 + pConfig->nCol*10;
+ char *zDefn = sqlite3_malloc(32 + pConfig->nCol * 10);
+ if( zDefn==0 ){
+ rc = SQLITE_NOMEM;
+ }else{
+ int i;
+ int iOff;
+ sqlite3_snprintf(nDefn, zDefn, "id INTEGER PRIMARY KEY");
+ iOff = (int)strlen(zDefn);
+ for(i=0; i<pConfig->nCol; i++){
+ sqlite3_snprintf(nDefn-iOff, &zDefn[iOff], ", c%d", i);
+ iOff += (int)strlen(&zDefn[iOff]);
+ }
+ rc = sqlite3Fts5CreateTable(pConfig, "content", zDefn, 0, pzErr);
+ }
+ sqlite3_free(zDefn);
+ }
+
+ if( rc==SQLITE_OK && pConfig->bColumnsize ){
+ rc = sqlite3Fts5CreateTable(
+ pConfig, "docsize", "id INTEGER PRIMARY KEY, sz BLOB", 0, pzErr
+ );
+ }
+ if( rc==SQLITE_OK ){
+ rc = sqlite3Fts5CreateTable(
+ pConfig, "config", "k PRIMARY KEY, v", 1, pzErr
+ );
+ }
+ if( rc==SQLITE_OK ){
+ rc = sqlite3Fts5StorageConfigValue(p, "version", 0, FTS5_CURRENT_VERSION);
+ }
+ }
+
+ if( rc ){
+ sqlite3Fts5StorageClose(p);
+ *pp = 0;
+ }
+ return rc;
+}
+
+/*
+** Close a handle opened by an earlier call to sqlite3Fts5StorageOpen().
+*/
+static int sqlite3Fts5StorageClose(Fts5Storage *p){
+ int rc = SQLITE_OK;
+ if( p ){
+ int i;
+
+ /* Finalize all SQL statements */
+ for(i=0; i<ArraySize(p->aStmt); i++){
+ sqlite3_finalize(p->aStmt[i]);
+ }
+
+ sqlite3_free(p);
+ }
+ return rc;
+}
+
+typedef struct Fts5InsertCtx Fts5InsertCtx;
+struct Fts5InsertCtx {
+ Fts5Storage *pStorage;
+ int iCol;
+ int szCol; /* Size of column value in tokens */
+};
+
+/*
+** Tokenization callback used when inserting tokens into the FTS index.
+*/
+static int fts5StorageInsertCallback(
+ void *pContext, /* Pointer to Fts5InsertCtx object */
+ int tflags,
+ const char *pToken, /* Buffer containing token */
+ int nToken, /* Size of token in bytes */
+ int iUnused1, /* Start offset of token */
+ int iUnused2 /* End offset of token */
+){
+ Fts5InsertCtx *pCtx = (Fts5InsertCtx*)pContext;
+ Fts5Index *pIdx = pCtx->pStorage->pIndex;
+ UNUSED_PARAM2(iUnused1, iUnused2);
+ if( nToken>FTS5_MAX_TOKEN_SIZE ) nToken = FTS5_MAX_TOKEN_SIZE;
+ if( (tflags & FTS5_TOKEN_COLOCATED)==0 || pCtx->szCol==0 ){
+ pCtx->szCol++;
+ }
+ return sqlite3Fts5IndexWrite(pIdx, pCtx->iCol, pCtx->szCol-1, pToken, nToken);
+}
+
+/*
+** If a row with rowid iDel is present in the %_content table, add the
+** delete-markers to the FTS index necessary to delete it. Do not actually
+** remove the %_content row at this time though.
+*/
+static int fts5StorageDeleteFromIndex(
+ Fts5Storage *p,
+ i64 iDel,
+ sqlite3_value **apVal
+){
+ Fts5Config *pConfig = p->pConfig;
+ sqlite3_stmt *pSeek = 0; /* SELECT to read row iDel from %_data */
+ int rc; /* Return code */
+ int rc2; /* sqlite3_reset() return code */
+ int iCol;
+ Fts5InsertCtx ctx;
+
+ if( apVal==0 ){
+ rc = fts5StorageGetStmt(p, FTS5_STMT_LOOKUP, &pSeek, 0);
+ if( rc!=SQLITE_OK ) return rc;
+ sqlite3_bind_int64(pSeek, 1, iDel);
+ if( sqlite3_step(pSeek)!=SQLITE_ROW ){
+ return sqlite3_reset(pSeek);
+ }
+ }
+
+ ctx.pStorage = p;
+ ctx.iCol = -1;
+ rc = sqlite3Fts5IndexBeginWrite(p->pIndex, 1, iDel);
+ for(iCol=1; rc==SQLITE_OK && iCol<=pConfig->nCol; iCol++){
+ if( pConfig->abUnindexed[iCol-1]==0 ){
+ const char *zText;
+ int nText;
+ if( pSeek ){
+ zText = (const char*)sqlite3_column_text(pSeek, iCol);
+ nText = sqlite3_column_bytes(pSeek, iCol);
+ }else{
+ zText = (const char*)sqlite3_value_text(apVal[iCol-1]);
+ nText = sqlite3_value_bytes(apVal[iCol-1]);
+ }
+ ctx.szCol = 0;
+ rc = sqlite3Fts5Tokenize(pConfig, FTS5_TOKENIZE_DOCUMENT,
+ zText, nText, (void*)&ctx, fts5StorageInsertCallback
+ );
+ p->aTotalSize[iCol-1] -= (i64)ctx.szCol;
+ }
+ }
+ p->nTotalRow--;
+
+ rc2 = sqlite3_reset(pSeek);
+ if( rc==SQLITE_OK ) rc = rc2;
+ return rc;
+}
+
+
+/*
+** Insert a record into the %_docsize table. Specifically, do:
+**
+** INSERT OR REPLACE INTO %_docsize(id, sz) VALUES(iRowid, pBuf);
+**
+** If there is no %_docsize table (as happens if the columnsize=0 option
+** is specified when the FTS5 table is created), this function is a no-op.
+*/
+static int fts5StorageInsertDocsize(
+ Fts5Storage *p, /* Storage module to write to */
+ i64 iRowid, /* id value */
+ Fts5Buffer *pBuf /* sz value */
+){
+ int rc = SQLITE_OK;
+ if( p->pConfig->bColumnsize ){
+ sqlite3_stmt *pReplace = 0;
+ rc = fts5StorageGetStmt(p, FTS5_STMT_REPLACE_DOCSIZE, &pReplace, 0);
+ if( rc==SQLITE_OK ){
+ sqlite3_bind_int64(pReplace, 1, iRowid);
+ sqlite3_bind_blob(pReplace, 2, pBuf->p, pBuf->n, SQLITE_STATIC);
+ sqlite3_step(pReplace);
+ rc = sqlite3_reset(pReplace);
+ }
+ }
+ return rc;
+}
+
+/*
+** Load the contents of the "averages" record from disk into the
+** p->nTotalRow and p->aTotalSize[] variables. If successful, and if
+** argument bCache is true, set the p->bTotalsValid flag to indicate
+** that the contents of aTotalSize[] and nTotalRow are valid until
+** further notice.
+**
+** Return SQLITE_OK if successful, or an SQLite error code if an error
+** occurs.
+*/
+static int fts5StorageLoadTotals(Fts5Storage *p, int bCache){
+ int rc = SQLITE_OK;
+ if( p->bTotalsValid==0 ){
+ rc = sqlite3Fts5IndexGetAverages(p->pIndex, &p->nTotalRow, p->aTotalSize);
+ p->bTotalsValid = bCache;
+ }
+ return rc;
+}
+
+/*
+** Store the current contents of the p->nTotalRow and p->aTotalSize[]
+** variables in the "averages" record on disk.
+**
+** Return SQLITE_OK if successful, or an SQLite error code if an error
+** occurs.
+*/
+static int fts5StorageSaveTotals(Fts5Storage *p){
+ int nCol = p->pConfig->nCol;
+ int i;
+ Fts5Buffer buf;
+ int rc = SQLITE_OK;
+ memset(&buf, 0, sizeof(buf));
+
+ sqlite3Fts5BufferAppendVarint(&rc, &buf, p->nTotalRow);
+ for(i=0; i<nCol; i++){
+ sqlite3Fts5BufferAppendVarint(&rc, &buf, p->aTotalSize[i]);
+ }
+ if( rc==SQLITE_OK ){
+ rc = sqlite3Fts5IndexSetAverages(p->pIndex, buf.p, buf.n);
+ }
+ sqlite3_free(buf.p);
+
+ return rc;
+}
+
+/*
+** Remove a row from the FTS table.
+*/
+static int sqlite3Fts5StorageDelete(Fts5Storage *p, i64 iDel, sqlite3_value **apVal){
+ Fts5Config *pConfig = p->pConfig;
+ int rc;
+ sqlite3_stmt *pDel = 0;
+
+ assert( pConfig->eContent!=FTS5_CONTENT_NORMAL || apVal==0 );
+ rc = fts5StorageLoadTotals(p, 1);
+
+ /* Delete the index records */
+ if( rc==SQLITE_OK ){
+ rc = fts5StorageDeleteFromIndex(p, iDel, apVal);
+ }
+
+ /* Delete the %_docsize record */
+ if( rc==SQLITE_OK && pConfig->bColumnsize ){
+ rc = fts5StorageGetStmt(p, FTS5_STMT_DELETE_DOCSIZE, &pDel, 0);
+ if( rc==SQLITE_OK ){
+ sqlite3_bind_int64(pDel, 1, iDel);
+ sqlite3_step(pDel);
+ rc = sqlite3_reset(pDel);
+ }
+ }
+
+ /* Delete the %_content record */
+ if( pConfig->eContent==FTS5_CONTENT_NORMAL ){
+ if( rc==SQLITE_OK ){
+ rc = fts5StorageGetStmt(p, FTS5_STMT_DELETE_CONTENT, &pDel, 0);
+ }
+ if( rc==SQLITE_OK ){
+ sqlite3_bind_int64(pDel, 1, iDel);
+ sqlite3_step(pDel);
+ rc = sqlite3_reset(pDel);
+ }
+ }
+
+ /* Write the averages record */
+ if( rc==SQLITE_OK ){
+ rc = fts5StorageSaveTotals(p);
+ }
+
+ return rc;
+}
+
+/*
+** Delete all entries in the FTS5 index.
+*/
+static int sqlite3Fts5StorageDeleteAll(Fts5Storage *p){
+ Fts5Config *pConfig = p->pConfig;
+ int rc;
+
+ /* Delete the contents of the %_data and %_docsize tables. */
+ rc = fts5ExecPrintf(pConfig->db, 0,
+ "DELETE FROM %Q.'%q_data';"
+ "DELETE FROM %Q.'%q_idx';",
+ pConfig->zDb, pConfig->zName,
+ pConfig->zDb, pConfig->zName
+ );
+ if( rc==SQLITE_OK && pConfig->bColumnsize ){
+ rc = fts5ExecPrintf(pConfig->db, 0,
+ "DELETE FROM %Q.'%q_docsize';",
+ pConfig->zDb, pConfig->zName
+ );
+ }
+
+ /* Reinitialize the %_data table. This call creates the initial structure
+ ** and averages records. */
+ if( rc==SQLITE_OK ){
+ rc = sqlite3Fts5IndexReinit(p->pIndex);
+ }
+ if( rc==SQLITE_OK ){
+ rc = sqlite3Fts5StorageConfigValue(p, "version", 0, FTS5_CURRENT_VERSION);
+ }
+ return rc;
+}
+
+static int sqlite3Fts5StorageRebuild(Fts5Storage *p){
+ Fts5Buffer buf = {0,0,0};
+ Fts5Config *pConfig = p->pConfig;
+ sqlite3_stmt *pScan = 0;
+ Fts5InsertCtx ctx;
+ int rc;
+
+ memset(&ctx, 0, sizeof(Fts5InsertCtx));
+ ctx.pStorage = p;
+ rc = sqlite3Fts5StorageDeleteAll(p);
+ if( rc==SQLITE_OK ){
+ rc = fts5StorageLoadTotals(p, 1);
+ }
+
+ if( rc==SQLITE_OK ){
+ rc = fts5StorageGetStmt(p, FTS5_STMT_SCAN, &pScan, 0);
+ }
+
+ while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pScan) ){
+ i64 iRowid = sqlite3_column_int64(pScan, 0);
+
+ sqlite3Fts5BufferZero(&buf);
+ rc = sqlite3Fts5IndexBeginWrite(p->pIndex, 0, iRowid);
+ for(ctx.iCol=0; rc==SQLITE_OK && ctx.iCol<pConfig->nCol; ctx.iCol++){
+ ctx.szCol = 0;
+ if( pConfig->abUnindexed[ctx.iCol]==0 ){
+ rc = sqlite3Fts5Tokenize(pConfig,
+ FTS5_TOKENIZE_DOCUMENT,
+ (const char*)sqlite3_column_text(pScan, ctx.iCol+1),
+ sqlite3_column_bytes(pScan, ctx.iCol+1),
+ (void*)&ctx,
+ fts5StorageInsertCallback
+ );
+ }
+ sqlite3Fts5BufferAppendVarint(&rc, &buf, ctx.szCol);
+ p->aTotalSize[ctx.iCol] += (i64)ctx.szCol;
+ }
+ p->nTotalRow++;
+
+ if( rc==SQLITE_OK ){
+ rc = fts5StorageInsertDocsize(p, iRowid, &buf);
+ }
+ }
+ sqlite3_free(buf.p);
+
+ /* Write the averages record */
+ if( rc==SQLITE_OK ){
+ rc = fts5StorageSaveTotals(p);
+ }
+ return rc;
+}
+
+static int sqlite3Fts5StorageOptimize(Fts5Storage *p){
+ return sqlite3Fts5IndexOptimize(p->pIndex);
+}
+
+static int sqlite3Fts5StorageMerge(Fts5Storage *p, int nMerge){
+ return sqlite3Fts5IndexMerge(p->pIndex, nMerge);
+}
+
+static int sqlite3Fts5StorageReset(Fts5Storage *p){
+ return sqlite3Fts5IndexReset(p->pIndex);
+}
+
+/*
+** Allocate a new rowid. This is used for "external content" tables when
+** a NULL value is inserted into the rowid column. The new rowid is allocated
+** by inserting a dummy row into the %_docsize table. The dummy will be
+** overwritten later.
+**
+** If the %_docsize table does not exist, SQLITE_MISMATCH is returned. In
+** this case the user is required to provide a rowid explicitly.
+*/
+static int fts5StorageNewRowid(Fts5Storage *p, i64 *piRowid){
+ int rc = SQLITE_MISMATCH;
+ if( p->pConfig->bColumnsize ){
+ sqlite3_stmt *pReplace = 0;
+ rc = fts5StorageGetStmt(p, FTS5_STMT_REPLACE_DOCSIZE, &pReplace, 0);
+ if( rc==SQLITE_OK ){
+ sqlite3_bind_null(pReplace, 1);
+ sqlite3_bind_null(pReplace, 2);
+ sqlite3_step(pReplace);
+ rc = sqlite3_reset(pReplace);
+ }
+ if( rc==SQLITE_OK ){
+ *piRowid = sqlite3_last_insert_rowid(p->pConfig->db);
+ }
+ }
+ return rc;
+}
+
+/*
+** Insert a new row into the FTS content table.
+*/
+static int sqlite3Fts5StorageContentInsert(
+ Fts5Storage *p,
+ sqlite3_value **apVal,
+ i64 *piRowid
+){
+ Fts5Config *pConfig = p->pConfig;
+ int rc = SQLITE_OK;
+
+ /* Insert the new row into the %_content table. */
+ if( pConfig->eContent!=FTS5_CONTENT_NORMAL ){
+ if( sqlite3_value_type(apVal[1])==SQLITE_INTEGER ){
+ *piRowid = sqlite3_value_int64(apVal[1]);
+ }else{
+ rc = fts5StorageNewRowid(p, piRowid);
+ }
+ }else{
+ sqlite3_stmt *pInsert = 0; /* Statement to write %_content table */
+ int i; /* Counter variable */
+ rc = fts5StorageGetStmt(p, FTS5_STMT_INSERT_CONTENT, &pInsert, 0);
+ for(i=1; rc==SQLITE_OK && i<=pConfig->nCol+1; i++){
+ rc = sqlite3_bind_value(pInsert, i, apVal[i]);
+ }
+ if( rc==SQLITE_OK ){
+ sqlite3_step(pInsert);
+ rc = sqlite3_reset(pInsert);
+ }
+ *piRowid = sqlite3_last_insert_rowid(pConfig->db);
+ }
+
+ return rc;
+}
+
+/*
+** Insert new entries into the FTS index and %_docsize table.
+*/
+static int sqlite3Fts5StorageIndexInsert(
+ Fts5Storage *p,
+ sqlite3_value **apVal,
+ i64 iRowid
+){
+ Fts5Config *pConfig = p->pConfig;
+ int rc = SQLITE_OK; /* Return code */
+ Fts5InsertCtx ctx; /* Tokenization callback context object */
+ Fts5Buffer buf; /* Buffer used to build up %_docsize blob */
+
+ memset(&buf, 0, sizeof(Fts5Buffer));
+ ctx.pStorage = p;
+ rc = fts5StorageLoadTotals(p, 1);
+
+ if( rc==SQLITE_OK ){
+ rc = sqlite3Fts5IndexBeginWrite(p->pIndex, 0, iRowid);
+ }
+ for(ctx.iCol=0; rc==SQLITE_OK && ctx.iCol<pConfig->nCol; ctx.iCol++){
+ ctx.szCol = 0;
+ if( pConfig->abUnindexed[ctx.iCol]==0 ){
+ rc = sqlite3Fts5Tokenize(pConfig,
+ FTS5_TOKENIZE_DOCUMENT,
+ (const char*)sqlite3_value_text(apVal[ctx.iCol+2]),
+ sqlite3_value_bytes(apVal[ctx.iCol+2]),
+ (void*)&ctx,
+ fts5StorageInsertCallback
+ );
+ }
+ sqlite3Fts5BufferAppendVarint(&rc, &buf, ctx.szCol);
+ p->aTotalSize[ctx.iCol] += (i64)ctx.szCol;
+ }
+ p->nTotalRow++;
+
+ /* Write the %_docsize record */
+ if( rc==SQLITE_OK ){
+ rc = fts5StorageInsertDocsize(p, iRowid, &buf);
+ }
+ sqlite3_free(buf.p);
+
+ /* Write the averages record */
+ if( rc==SQLITE_OK ){
+ rc = fts5StorageSaveTotals(p);
+ }
+
+ return rc;
+}
+
+static int fts5StorageCount(Fts5Storage *p, const char *zSuffix, i64 *pnRow){
+ Fts5Config *pConfig = p->pConfig;
+ char *zSql;
+ int rc;
+
+ zSql = sqlite3_mprintf("SELECT count(*) FROM %Q.'%q_%s'",
+ pConfig->zDb, pConfig->zName, zSuffix
+ );
+ if( zSql==0 ){
+ rc = SQLITE_NOMEM;
+ }else{
+ sqlite3_stmt *pCnt = 0;
+ rc = sqlite3_prepare_v2(pConfig->db, zSql, -1, &pCnt, 0);
+ if( rc==SQLITE_OK ){
+ if( SQLITE_ROW==sqlite3_step(pCnt) ){
+ *pnRow = sqlite3_column_int64(pCnt, 0);
+ }
+ rc = sqlite3_finalize(pCnt);
+ }
+ }
+
+ sqlite3_free(zSql);
+ return rc;
+}
+
+/*
+** Context object used by sqlite3Fts5StorageIntegrity().
+*/
+typedef struct Fts5IntegrityCtx Fts5IntegrityCtx;
+struct Fts5IntegrityCtx {
+ i64 iRowid;
+ int iCol;
+ int szCol;
+ u64 cksum;
+ Fts5Termset *pTermset;
+ Fts5Config *pConfig;
+};
+
+
+/*
+** Tokenization callback used by integrity check.
+*/
+static int fts5StorageIntegrityCallback(
+ void *pContext, /* Pointer to Fts5IntegrityCtx object */
+ int tflags,
+ const char *pToken, /* Buffer containing token */
+ int nToken, /* Size of token in bytes */
+ int iUnused1, /* Start offset of token */
+ int iUnused2 /* End offset of token */
+){
+ Fts5IntegrityCtx *pCtx = (Fts5IntegrityCtx*)pContext;
+ Fts5Termset *pTermset = pCtx->pTermset;
+ int bPresent;
+ int ii;
+ int rc = SQLITE_OK;
+ int iPos;
+ int iCol;
+
+ UNUSED_PARAM2(iUnused1, iUnused2);
+ if( nToken>FTS5_MAX_TOKEN_SIZE ) nToken = FTS5_MAX_TOKEN_SIZE;
+
+ if( (tflags & FTS5_TOKEN_COLOCATED)==0 || pCtx->szCol==0 ){
+ pCtx->szCol++;
+ }
+
+ switch( pCtx->pConfig->eDetail ){
+ case FTS5_DETAIL_FULL:
+ iPos = pCtx->szCol-1;
+ iCol = pCtx->iCol;
+ break;
+
+ case FTS5_DETAIL_COLUMNS:
+ iPos = pCtx->iCol;
+ iCol = 0;
+ break;
+
+ default:
+ assert( pCtx->pConfig->eDetail==FTS5_DETAIL_NONE );
+ iPos = 0;
+ iCol = 0;
+ break;
+ }
+
+ rc = sqlite3Fts5TermsetAdd(pTermset, 0, pToken, nToken, &bPresent);
+ if( rc==SQLITE_OK && bPresent==0 ){
+ pCtx->cksum ^= sqlite3Fts5IndexEntryCksum(
+ pCtx->iRowid, iCol, iPos, 0, pToken, nToken
+ );
+ }
+
+ for(ii=0; rc==SQLITE_OK && ii<pCtx->pConfig->nPrefix; ii++){
+ const int nChar = pCtx->pConfig->aPrefix[ii];
+ int nByte = sqlite3Fts5IndexCharlenToBytelen(pToken, nToken, nChar);
+ if( nByte ){
+ rc = sqlite3Fts5TermsetAdd(pTermset, ii+1, pToken, nByte, &bPresent);
+ if( bPresent==0 ){
+ pCtx->cksum ^= sqlite3Fts5IndexEntryCksum(
+ pCtx->iRowid, iCol, iPos, ii+1, pToken, nByte
+ );
+ }
+ }
+ }
+
+ return rc;
+}
+
+/*
+** Check that the contents of the FTS index match that of the %_content
+** table. Return SQLITE_OK if they do, or SQLITE_CORRUPT if not. Return
+** some other SQLite error code if an error occurs while attempting to
+** determine this.
+*/
+static int sqlite3Fts5StorageIntegrity(Fts5Storage *p){
+ Fts5Config *pConfig = p->pConfig;
+ int rc; /* Return code */
+ int *aColSize; /* Array of size pConfig->nCol */
+ i64 *aTotalSize; /* Array of size pConfig->nCol */
+ Fts5IntegrityCtx ctx;
+ sqlite3_stmt *pScan;
+
+ memset(&ctx, 0, sizeof(Fts5IntegrityCtx));
+ ctx.pConfig = p->pConfig;
+ aTotalSize = (i64*)sqlite3_malloc(pConfig->nCol * (sizeof(int)+sizeof(i64)));
+ if( !aTotalSize ) return SQLITE_NOMEM;
+ aColSize = (int*)&aTotalSize[pConfig->nCol];
+ memset(aTotalSize, 0, sizeof(i64) * pConfig->nCol);
+
+ /* Generate the expected index checksum based on the contents of the
+ ** %_content table. This block stores the checksum in ctx.cksum. */
+ rc = fts5StorageGetStmt(p, FTS5_STMT_SCAN, &pScan, 0);
+ if( rc==SQLITE_OK ){
+ int rc2;
+ while( SQLITE_ROW==sqlite3_step(pScan) ){
+ int i;
+ ctx.iRowid = sqlite3_column_int64(pScan, 0);
+ ctx.szCol = 0;
+ if( pConfig->bColumnsize ){
+ rc = sqlite3Fts5StorageDocsize(p, ctx.iRowid, aColSize);
+ }
+ if( rc==SQLITE_OK && pConfig->eDetail==FTS5_DETAIL_NONE ){
+ rc = sqlite3Fts5TermsetNew(&ctx.pTermset);
+ }
+ for(i=0; rc==SQLITE_OK && i<pConfig->nCol; i++){
+ if( pConfig->abUnindexed[i] ) continue;
+ ctx.iCol = i;
+ ctx.szCol = 0;
+ if( pConfig->eDetail==FTS5_DETAIL_COLUMNS ){
+ rc = sqlite3Fts5TermsetNew(&ctx.pTermset);
+ }
+ if( rc==SQLITE_OK ){
+ rc = sqlite3Fts5Tokenize(pConfig,
+ FTS5_TOKENIZE_DOCUMENT,
+ (const char*)sqlite3_column_text(pScan, i+1),
+ sqlite3_column_bytes(pScan, i+1),
+ (void*)&ctx,
+ fts5StorageIntegrityCallback
+ );
+ }
+ if( rc==SQLITE_OK && pConfig->bColumnsize && ctx.szCol!=aColSize[i] ){
+ rc = FTS5_CORRUPT;
+ }
+ aTotalSize[i] += ctx.szCol;
+ if( pConfig->eDetail==FTS5_DETAIL_COLUMNS ){
+ sqlite3Fts5TermsetFree(ctx.pTermset);
+ ctx.pTermset = 0;
+ }
+ }
+ sqlite3Fts5TermsetFree(ctx.pTermset);
+ ctx.pTermset = 0;
+
+ if( rc!=SQLITE_OK ) break;
+ }
+ rc2 = sqlite3_reset(pScan);
+ if( rc==SQLITE_OK ) rc = rc2;
+ }
+
+ /* Test that the "totals" (sometimes called "averages") record looks Ok */
+ if( rc==SQLITE_OK ){
+ int i;
+ rc = fts5StorageLoadTotals(p, 0);
+ for(i=0; rc==SQLITE_OK && i<pConfig->nCol; i++){
+ if( p->aTotalSize[i]!=aTotalSize[i] ) rc = FTS5_CORRUPT;
+ }
+ }
+
+ /* Check that the %_docsize and %_content tables contain the expected
+ ** number of rows. */
+ if( rc==SQLITE_OK && pConfig->eContent==FTS5_CONTENT_NORMAL ){
+ i64 nRow = 0;
+ rc = fts5StorageCount(p, "content", &nRow);
+ if( rc==SQLITE_OK && nRow!=p->nTotalRow ) rc = FTS5_CORRUPT;
+ }
+ if( rc==SQLITE_OK && pConfig->bColumnsize ){
+ i64 nRow = 0;
+ rc = fts5StorageCount(p, "docsize", &nRow);
+ if( rc==SQLITE_OK && nRow!=p->nTotalRow ) rc = FTS5_CORRUPT;
+ }
+
+ /* Pass the expected checksum down to the FTS index module. It will
+ ** verify, amongst other things, that it matches the checksum generated by
+ ** inspecting the index itself. */
+ if( rc==SQLITE_OK ){
+ rc = sqlite3Fts5IndexIntegrityCheck(p->pIndex, ctx.cksum);
+ }
+
+ sqlite3_free(aTotalSize);
+ return rc;
+}
+
+/*
+** Obtain an SQLite statement handle that may be used to read data from the
+** %_content table.
+*/
+static int sqlite3Fts5StorageStmt(
+ Fts5Storage *p,
+ int eStmt,
+ sqlite3_stmt **pp,
+ char **pzErrMsg
+){
+ int rc;
+ assert( eStmt==FTS5_STMT_SCAN_ASC
+ || eStmt==FTS5_STMT_SCAN_DESC
+ || eStmt==FTS5_STMT_LOOKUP
+ );
+ rc = fts5StorageGetStmt(p, eStmt, pp, pzErrMsg);
+ if( rc==SQLITE_OK ){
+ assert( p->aStmt[eStmt]==*pp );
+ p->aStmt[eStmt] = 0;
+ }
+ return rc;
+}
+
+/*
+** Release an SQLite statement handle obtained via an earlier call to
+** sqlite3Fts5StorageStmt(). The eStmt parameter passed to this function
+** must match that passed to the sqlite3Fts5StorageStmt() call.
+*/
+static void sqlite3Fts5StorageStmtRelease(
+ Fts5Storage *p,
+ int eStmt,
+ sqlite3_stmt *pStmt
+){
+ assert( eStmt==FTS5_STMT_SCAN_ASC
+ || eStmt==FTS5_STMT_SCAN_DESC
+ || eStmt==FTS5_STMT_LOOKUP
+ );
+ if( p->aStmt[eStmt]==0 ){
+ sqlite3_reset(pStmt);
+ p->aStmt[eStmt] = pStmt;
+ }else{
+ sqlite3_finalize(pStmt);
+ }
+}
+
+static int fts5StorageDecodeSizeArray(
+ int *aCol, int nCol, /* Array to populate */
+ const u8 *aBlob, int nBlob /* Record to read varints from */
+){
+ int i;
+ int iOff = 0;
+ for(i=0; i<nCol; i++){
+ if( iOff>=nBlob ) return 1;
+ iOff += fts5GetVarint32(&aBlob[iOff], aCol[i]);
+ }
+ return (iOff!=nBlob);
+}
+
+/*
+** Argument aCol points to an array of integers containing one entry for
+** each table column. This function reads the %_docsize record for the
+** specified rowid and populates aCol[] with the results.
+**
+** An SQLite error code is returned if an error occurs, or SQLITE_OK
+** otherwise.
+*/
+static int sqlite3Fts5StorageDocsize(Fts5Storage *p, i64 iRowid, int *aCol){
+ int nCol = p->pConfig->nCol; /* Number of user columns in table */
+ sqlite3_stmt *pLookup = 0; /* Statement to query %_docsize */
+ int rc; /* Return Code */
+
+ assert( p->pConfig->bColumnsize );
+ rc = fts5StorageGetStmt(p, FTS5_STMT_LOOKUP_DOCSIZE, &pLookup, 0);
+ if( rc==SQLITE_OK ){
+ int bCorrupt = 1;
+ sqlite3_bind_int64(pLookup, 1, iRowid);
+ if( SQLITE_ROW==sqlite3_step(pLookup) ){
+ const u8 *aBlob = sqlite3_column_blob(pLookup, 0);
+ int nBlob = sqlite3_column_bytes(pLookup, 0);
+ if( 0==fts5StorageDecodeSizeArray(aCol, nCol, aBlob, nBlob) ){
+ bCorrupt = 0;
+ }
+ }
+ rc = sqlite3_reset(pLookup);
+ if( bCorrupt && rc==SQLITE_OK ){
+ rc = FTS5_CORRUPT;
+ }
+ }
+
+ return rc;
+}
+
+static int sqlite3Fts5StorageSize(Fts5Storage *p, int iCol, i64 *pnToken){
+ int rc = fts5StorageLoadTotals(p, 0);
+ if( rc==SQLITE_OK ){
+ *pnToken = 0;
+ if( iCol<0 ){
+ int i;
+ for(i=0; i<p->pConfig->nCol; i++){
+ *pnToken += p->aTotalSize[i];
+ }
+ }else if( iCol<p->pConfig->nCol ){
+ *pnToken = p->aTotalSize[iCol];
+ }else{
+ rc = SQLITE_RANGE;
+ }
+ }
+ return rc;
+}
+
+static int sqlite3Fts5StorageRowCount(Fts5Storage *p, i64 *pnRow){
+ int rc = fts5StorageLoadTotals(p, 0);
+ if( rc==SQLITE_OK ){
+ *pnRow = p->nTotalRow;
+ }
+ return rc;
+}
+
+/*
+** Flush any data currently held in-memory to disk.
+*/
+static int sqlite3Fts5StorageSync(Fts5Storage *p, int bCommit){
+ if( bCommit && p->bTotalsValid ){
+ int rc = fts5StorageSaveTotals(p);
+ p->bTotalsValid = 0;
+ if( rc!=SQLITE_OK ) return rc;
+ }
+ return sqlite3Fts5IndexSync(p->pIndex, bCommit);
+}
+
+static int sqlite3Fts5StorageRollback(Fts5Storage *p){
+ p->bTotalsValid = 0;
+ return sqlite3Fts5IndexRollback(p->pIndex);
+}
+
+static int sqlite3Fts5StorageConfigValue(
+ Fts5Storage *p,
+ const char *z,
+ sqlite3_value *pVal,
+ int iVal
+){
+ sqlite3_stmt *pReplace = 0;
+ int rc = fts5StorageGetStmt(p, FTS5_STMT_REPLACE_CONFIG, &pReplace, 0);
+ if( rc==SQLITE_OK ){
+ sqlite3_bind_text(pReplace, 1, z, -1, SQLITE_STATIC);
+ if( pVal ){
+ sqlite3_bind_value(pReplace, 2, pVal);
+ }else{
+ sqlite3_bind_int(pReplace, 2, iVal);
+ }
+ sqlite3_step(pReplace);
+ rc = sqlite3_reset(pReplace);
+ }
+ if( rc==SQLITE_OK && pVal ){
+ int iNew = p->pConfig->iCookie + 1;
+ rc = sqlite3Fts5IndexSetCookie(p->pIndex, iNew);
+ if( rc==SQLITE_OK ){
+ p->pConfig->iCookie = iNew;
+ }
+ }
+ return rc;
+}
+
+/*
+** 2014 May 31
+**
+** 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.
+**
+******************************************************************************
+*/
+
+
+/* #include "fts5Int.h" */
+
+/**************************************************************************
+** Start of ascii tokenizer implementation.
+*/
+
+/*
+** For tokenizers with no "unicode" modifier, the set of token characters
+** is the same as the set of ASCII range alphanumeric characters.
+*/
+static unsigned char aAsciiTokenChar[128] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x00..0x0F */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x10..0x1F */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x20..0x2F */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, /* 0x30..0x3F */
+ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x40..0x4F */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 0x50..0x5F */
+ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x60..0x6F */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 0x70..0x7F */
+};
+
+typedef struct AsciiTokenizer AsciiTokenizer;
+struct AsciiTokenizer {
+ unsigned char aTokenChar[128];
+};
+
+static void fts5AsciiAddExceptions(
+ AsciiTokenizer *p,
+ const char *zArg,
+ int bTokenChars
+){
+ int i;
+ for(i=0; zArg[i]; i++){
+ if( (zArg[i] & 0x80)==0 ){
+ p->aTokenChar[(int)zArg[i]] = (unsigned char)bTokenChars;
+ }
+ }
+}
+
+/*
+** Delete a "ascii" tokenizer.
+*/
+static void fts5AsciiDelete(Fts5Tokenizer *p){
+ sqlite3_free(p);
+}
+
+/*
+** Create an "ascii" tokenizer.
+*/
+static int fts5AsciiCreate(
+ void *pUnused,
+ const char **azArg, int nArg,
+ Fts5Tokenizer **ppOut
+){
+ int rc = SQLITE_OK;
+ AsciiTokenizer *p = 0;
+ UNUSED_PARAM(pUnused);
+ if( nArg%2 ){
+ rc = SQLITE_ERROR;
+ }else{
+ p = sqlite3_malloc(sizeof(AsciiTokenizer));
+ if( p==0 ){
+ rc = SQLITE_NOMEM;
+ }else{
+ int i;
+ memset(p, 0, sizeof(AsciiTokenizer));
+ memcpy(p->aTokenChar, aAsciiTokenChar, sizeof(aAsciiTokenChar));
+ for(i=0; rc==SQLITE_OK && i<nArg; i+=2){
+ const char *zArg = azArg[i+1];
+ if( 0==sqlite3_stricmp(azArg[i], "tokenchars") ){
+ fts5AsciiAddExceptions(p, zArg, 1);
+ }else
+ if( 0==sqlite3_stricmp(azArg[i], "separators") ){
+ fts5AsciiAddExceptions(p, zArg, 0);
+ }else{
+ rc = SQLITE_ERROR;
+ }
+ }
+ if( rc!=SQLITE_OK ){
+ fts5AsciiDelete((Fts5Tokenizer*)p);
+ p = 0;
+ }
+ }
+ }
+
+ *ppOut = (Fts5Tokenizer*)p;
+ return rc;
+}
+
+
+static void asciiFold(char *aOut, const char *aIn, int nByte){
+ int i;
+ for(i=0; i<nByte; i++){
+ char c = aIn[i];
+ if( c>='A' && c<='Z' ) c += 32;
+ aOut[i] = c;
+ }
+}
+
+/*
+** Tokenize some text using the ascii tokenizer.
+*/
+static int fts5AsciiTokenize(
+ Fts5Tokenizer *pTokenizer,
+ void *pCtx,
+ int iUnused,
+ const char *pText, int nText,
+ int (*xToken)(void*, int, const char*, int nToken, int iStart, int iEnd)
+){
+ AsciiTokenizer *p = (AsciiTokenizer*)pTokenizer;
+ int rc = SQLITE_OK;
+ int ie;
+ int is = 0;
+
+ char aFold[64];
+ int nFold = sizeof(aFold);
+ char *pFold = aFold;
+ unsigned char *a = p->aTokenChar;
+
+ UNUSED_PARAM(iUnused);
+
+ while( is<nText && rc==SQLITE_OK ){
+ int nByte;
+
+ /* Skip any leading divider characters. */
+ while( is<nText && ((pText[is]&0x80)==0 && a[(int)pText[is]]==0) ){
+ is++;
+ }
+ if( is==nText ) break;
+
+ /* Count the token characters */
+ ie = is+1;
+ while( ie<nText && ((pText[ie]&0x80) || a[(int)pText[ie]] ) ){
+ ie++;
+ }
+
+ /* Fold to lower case */
+ nByte = ie-is;
+ if( nByte>nFold ){
+ if( pFold!=aFold ) sqlite3_free(pFold);
+ pFold = sqlite3_malloc(nByte*2);
+ if( pFold==0 ){
+ rc = SQLITE_NOMEM;
+ break;
+ }
+ nFold = nByte*2;
+ }
+ asciiFold(pFold, &pText[is], nByte);
+
+ /* Invoke the token callback */
+ rc = xToken(pCtx, 0, pFold, nByte, is, ie);
+ is = ie+1;
+ }
+
+ if( pFold!=aFold ) sqlite3_free(pFold);
+ if( rc==SQLITE_DONE ) rc = SQLITE_OK;
+ return rc;
+}
+
+/**************************************************************************
+** Start of unicode61 tokenizer implementation.
+*/
+
+
+/*
+** The following two macros - READ_UTF8 and WRITE_UTF8 - have been copied
+** from the sqlite3 source file utf.c. If this file is compiled as part
+** of the amalgamation, they are not required.
+*/
+#ifndef SQLITE_AMALGAMATION
+
+static const unsigned char sqlite3Utf8Trans1[] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x00, 0x01, 0x02, 0x03, 0x00, 0x01, 0x00, 0x00,
+};
+
+#define READ_UTF8(zIn, zTerm, c) \
+ c = *(zIn++); \
+ if( c>=0xc0 ){ \
+ c = sqlite3Utf8Trans1[c-0xc0]; \
+ while( zIn!=zTerm && (*zIn & 0xc0)==0x80 ){ \
+ c = (c<<6) + (0x3f & *(zIn++)); \
+ } \
+ if( c<0x80 \
+ || (c&0xFFFFF800)==0xD800 \
+ || (c&0xFFFFFFFE)==0xFFFE ){ c = 0xFFFD; } \
+ }
+
+
+#define WRITE_UTF8(zOut, c) { \
+ if( c<0x00080 ){ \
+ *zOut++ = (unsigned char)(c&0xFF); \
+ } \
+ else if( c<0x00800 ){ \
+ *zOut++ = 0xC0 + (unsigned char)((c>>6)&0x1F); \
+ *zOut++ = 0x80 + (unsigned char)(c & 0x3F); \
+ } \
+ else if( c<0x10000 ){ \
+ *zOut++ = 0xE0 + (unsigned char)((c>>12)&0x0F); \
+ *zOut++ = 0x80 + (unsigned char)((c>>6) & 0x3F); \
+ *zOut++ = 0x80 + (unsigned char)(c & 0x3F); \
+ }else{ \
+ *zOut++ = 0xF0 + (unsigned char)((c>>18) & 0x07); \
+ *zOut++ = 0x80 + (unsigned char)((c>>12) & 0x3F); \
+ *zOut++ = 0x80 + (unsigned char)((c>>6) & 0x3F); \
+ *zOut++ = 0x80 + (unsigned char)(c & 0x3F); \
+ } \
+}
+
+#endif /* ifndef SQLITE_AMALGAMATION */
+
+typedef struct Unicode61Tokenizer Unicode61Tokenizer;
+struct Unicode61Tokenizer {
+ unsigned char aTokenChar[128]; /* ASCII range token characters */
+ char *aFold; /* Buffer to fold text into */
+ int nFold; /* Size of aFold[] in bytes */
+ int bRemoveDiacritic; /* True if remove_diacritics=1 is set */
+ int nException;
+ int *aiException;
+};
+
+static int fts5UnicodeAddExceptions(
+ Unicode61Tokenizer *p, /* Tokenizer object */
+ const char *z, /* Characters to treat as exceptions */
+ int bTokenChars /* 1 for 'tokenchars', 0 for 'separators' */
+){
+ int rc = SQLITE_OK;
+ int n = (int)strlen(z);
+ int *aNew;
+
+ if( n>0 ){
+ aNew = (int*)sqlite3_realloc(p->aiException, (n+p->nException)*sizeof(int));
+ if( aNew ){
+ int nNew = p->nException;
+ const unsigned char *zCsr = (const unsigned char*)z;
+ const unsigned char *zTerm = (const unsigned char*)&z[n];
+ while( zCsr<zTerm ){
+ int iCode;
+ int bToken;
+ READ_UTF8(zCsr, zTerm, iCode);
+ if( iCode<128 ){
+ p->aTokenChar[iCode] = (unsigned char)bTokenChars;
+ }else{
+ bToken = sqlite3Fts5UnicodeIsalnum(iCode);
+ assert( (bToken==0 || bToken==1) );
+ assert( (bTokenChars==0 || bTokenChars==1) );
+ if( bToken!=bTokenChars && sqlite3Fts5UnicodeIsdiacritic(iCode)==0 ){
+ int i;
+ for(i=0; i<nNew; i++){
+ if( aNew[i]>iCode ) break;
+ }
+ memmove(&aNew[i+1], &aNew[i], (nNew-i)*sizeof(int));
+ aNew[i] = iCode;
+ nNew++;
+ }
+ }
+ }
+ p->aiException = aNew;
+ p->nException = nNew;
+ }else{
+ rc = SQLITE_NOMEM;
+ }
+ }
+
+ return rc;
+}
+
+/*
+** Return true if the p->aiException[] array contains the value iCode.
+*/
+static int fts5UnicodeIsException(Unicode61Tokenizer *p, int iCode){
+ if( p->nException>0 ){
+ int *a = p->aiException;
+ int iLo = 0;
+ int iHi = p->nException-1;
+
+ while( iHi>=iLo ){
+ int iTest = (iHi + iLo) / 2;
+ if( iCode==a[iTest] ){
+ return 1;
+ }else if( iCode>a[iTest] ){
+ iLo = iTest+1;
+ }else{
+ iHi = iTest-1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+/*
+** Delete a "unicode61" tokenizer.
+*/
+static void fts5UnicodeDelete(Fts5Tokenizer *pTok){
+ if( pTok ){
+ Unicode61Tokenizer *p = (Unicode61Tokenizer*)pTok;
+ sqlite3_free(p->aiException);
+ sqlite3_free(p->aFold);
+ sqlite3_free(p);
+ }
+ return;
+}
+
+/*
+** Create a "unicode61" tokenizer.
+*/
+static int fts5UnicodeCreate(
+ void *pUnused,
+ const char **azArg, int nArg,
+ Fts5Tokenizer **ppOut
+){
+ int rc = SQLITE_OK; /* Return code */
+ Unicode61Tokenizer *p = 0; /* New tokenizer object */
+
+ UNUSED_PARAM(pUnused);
+
+ if( nArg%2 ){
+ rc = SQLITE_ERROR;
+ }else{
+ p = (Unicode61Tokenizer*)sqlite3_malloc(sizeof(Unicode61Tokenizer));
+ if( p ){
+ int i;
+ memset(p, 0, sizeof(Unicode61Tokenizer));
+ memcpy(p->aTokenChar, aAsciiTokenChar, sizeof(aAsciiTokenChar));
+ p->bRemoveDiacritic = 1;
+ p->nFold = 64;
+ p->aFold = sqlite3_malloc(p->nFold * sizeof(char));
+ if( p->aFold==0 ){
+ rc = SQLITE_NOMEM;
+ }
+ for(i=0; rc==SQLITE_OK && i<nArg; i+=2){
+ const char *zArg = azArg[i+1];
+ if( 0==sqlite3_stricmp(azArg[i], "remove_diacritics") ){
+ if( (zArg[0]!='0' && zArg[0]!='1') || zArg[1] ){
+ rc = SQLITE_ERROR;
+ }
+ p->bRemoveDiacritic = (zArg[0]=='1');
+ }else
+ if( 0==sqlite3_stricmp(azArg[i], "tokenchars") ){
+ rc = fts5UnicodeAddExceptions(p, zArg, 1);
+ }else
+ if( 0==sqlite3_stricmp(azArg[i], "separators") ){
+ rc = fts5UnicodeAddExceptions(p, zArg, 0);
+ }else{
+ rc = SQLITE_ERROR;
+ }
+ }
+ }else{
+ rc = SQLITE_NOMEM;
+ }
+ if( rc!=SQLITE_OK ){
+ fts5UnicodeDelete((Fts5Tokenizer*)p);
+ p = 0;
+ }
+ *ppOut = (Fts5Tokenizer*)p;
+ }
+ return rc;
+}
+
+/*
+** Return true if, for the purposes of tokenizing with the tokenizer
+** passed as the first argument, codepoint iCode is considered a token
+** character (not a separator).
+*/
+static int fts5UnicodeIsAlnum(Unicode61Tokenizer *p, int iCode){
+ assert( (sqlite3Fts5UnicodeIsalnum(iCode) & 0xFFFFFFFE)==0 );
+ return sqlite3Fts5UnicodeIsalnum(iCode) ^ fts5UnicodeIsException(p, iCode);
+}
+
+static int fts5UnicodeTokenize(
+ Fts5Tokenizer *pTokenizer,
+ void *pCtx,
+ int iUnused,
+ const char *pText, int nText,
+ int (*xToken)(void*, int, const char*, int nToken, int iStart, int iEnd)
+){
+ Unicode61Tokenizer *p = (Unicode61Tokenizer*)pTokenizer;
+ int rc = SQLITE_OK;
+ unsigned char *a = p->aTokenChar;
+
+ unsigned char *zTerm = (unsigned char*)&pText[nText];
+ unsigned char *zCsr = (unsigned char *)pText;
+
+ /* Output buffer */
+ char *aFold = p->aFold;
+ int nFold = p->nFold;
+ const char *pEnd = &aFold[nFold-6];
+
+ UNUSED_PARAM(iUnused);
+
+ /* Each iteration of this loop gobbles up a contiguous run of separators,
+ ** then the next token. */
+ while( rc==SQLITE_OK ){
+ int iCode; /* non-ASCII codepoint read from input */
+ char *zOut = aFold;
+ int is;
+ int ie;
+
+ /* Skip any separator characters. */
+ while( 1 ){
+ if( zCsr>=zTerm ) goto tokenize_done;
+ if( *zCsr & 0x80 ) {
+ /* A character outside of the ascii range. Skip past it if it is
+ ** a separator character. Or break out of the loop if it is not. */
+ is = zCsr - (unsigned char*)pText;
+ READ_UTF8(zCsr, zTerm, iCode);
+ if( fts5UnicodeIsAlnum(p, iCode) ){
+ goto non_ascii_tokenchar;
+ }
+ }else{
+ if( a[*zCsr] ){
+ is = zCsr - (unsigned char*)pText;
+ goto ascii_tokenchar;
+ }
+ zCsr++;
+ }
+ }
+
+ /* Run through the tokenchars. Fold them into the output buffer along
+ ** the way. */
+ while( zCsr<zTerm ){
+
+ /* Grow the output buffer so that there is sufficient space to fit the
+ ** largest possible utf-8 character. */
+ if( zOut>pEnd ){
+ aFold = sqlite3_malloc(nFold*2);
+ if( aFold==0 ){
+ rc = SQLITE_NOMEM;
+ goto tokenize_done;
+ }
+ zOut = &aFold[zOut - p->aFold];
+ memcpy(aFold, p->aFold, nFold);
+ sqlite3_free(p->aFold);
+ p->aFold = aFold;
+ p->nFold = nFold = nFold*2;
+ pEnd = &aFold[nFold-6];
+ }
+
+ if( *zCsr & 0x80 ){
+ /* An non-ascii-range character. Fold it into the output buffer if
+ ** it is a token character, or break out of the loop if it is not. */
+ READ_UTF8(zCsr, zTerm, iCode);
+ if( fts5UnicodeIsAlnum(p,iCode)||sqlite3Fts5UnicodeIsdiacritic(iCode) ){
+ non_ascii_tokenchar:
+ iCode = sqlite3Fts5UnicodeFold(iCode, p->bRemoveDiacritic);
+ if( iCode ) WRITE_UTF8(zOut, iCode);
+ }else{
+ break;
+ }
+ }else if( a[*zCsr]==0 ){
+ /* An ascii-range separator character. End of token. */
+ break;
+ }else{
+ ascii_tokenchar:
+ if( *zCsr>='A' && *zCsr<='Z' ){
+ *zOut++ = *zCsr + 32;
+ }else{
+ *zOut++ = *zCsr;
+ }
+ zCsr++;
+ }
+ ie = zCsr - (unsigned char*)pText;
+ }
+
+ /* Invoke the token callback */
+ rc = xToken(pCtx, 0, aFold, zOut-aFold, is, ie);
+ }
+
+ tokenize_done:
+ if( rc==SQLITE_DONE ) rc = SQLITE_OK;
+ return rc;
+}
+
+/**************************************************************************
+** Start of porter stemmer implementation.
+*/
+
+/* Any tokens larger than this (in bytes) are passed through without
+** stemming. */
+#define FTS5_PORTER_MAX_TOKEN 64
+
+typedef struct PorterTokenizer PorterTokenizer;
+struct PorterTokenizer {
+ fts5_tokenizer tokenizer; /* Parent tokenizer module */
+ Fts5Tokenizer *pTokenizer; /* Parent tokenizer instance */
+ char aBuf[FTS5_PORTER_MAX_TOKEN + 64];
+};
+
+/*
+** Delete a "porter" tokenizer.
+*/
+static void fts5PorterDelete(Fts5Tokenizer *pTok){
+ if( pTok ){
+ PorterTokenizer *p = (PorterTokenizer*)pTok;
+ if( p->pTokenizer ){
+ p->tokenizer.xDelete(p->pTokenizer);
+ }
+ sqlite3_free(p);
+ }
+}
+
+/*
+** Create a "porter" tokenizer.
+*/
+static int fts5PorterCreate(
+ void *pCtx,
+ const char **azArg, int nArg,
+ Fts5Tokenizer **ppOut
+){
+ fts5_api *pApi = (fts5_api*)pCtx;
+ int rc = SQLITE_OK;
+ PorterTokenizer *pRet;
+ void *pUserdata = 0;
+ const char *zBase = "unicode61";
+
+ if( nArg>0 ){
+ zBase = azArg[0];
+ }
+
+ pRet = (PorterTokenizer*)sqlite3_malloc(sizeof(PorterTokenizer));
+ if( pRet ){
+ memset(pRet, 0, sizeof(PorterTokenizer));
+ rc = pApi->xFindTokenizer(pApi, zBase, &pUserdata, &pRet->tokenizer);
+ }else{
+ rc = SQLITE_NOMEM;
+ }
+ if( rc==SQLITE_OK ){
+ int nArg2 = (nArg>0 ? nArg-1 : 0);
+ const char **azArg2 = (nArg2 ? &azArg[1] : 0);
+ rc = pRet->tokenizer.xCreate(pUserdata, azArg2, nArg2, &pRet->pTokenizer);
+ }
+
+ if( rc!=SQLITE_OK ){
+ fts5PorterDelete((Fts5Tokenizer*)pRet);
+ pRet = 0;
+ }
+ *ppOut = (Fts5Tokenizer*)pRet;
+ return rc;
+}
+
+typedef struct PorterContext PorterContext;
+struct PorterContext {
+ void *pCtx;
+ int (*xToken)(void*, int, const char*, int, int, int);
+ char *aBuf;
+};
+
+typedef struct PorterRule PorterRule;
+struct PorterRule {
+ const char *zSuffix;
+ int nSuffix;
+ int (*xCond)(char *zStem, int nStem);
+ const char *zOutput;
+ int nOutput;
+};
+
+#if 0
+static int fts5PorterApply(char *aBuf, int *pnBuf, PorterRule *aRule){
+ int ret = -1;
+ int nBuf = *pnBuf;
+ PorterRule *p;
+
+ for(p=aRule; p->zSuffix; p++){
+ assert( strlen(p->zSuffix)==p->nSuffix );
+ assert( strlen(p->zOutput)==p->nOutput );
+ if( nBuf<p->nSuffix ) continue;
+ if( 0==memcmp(&aBuf[nBuf - p->nSuffix], p->zSuffix, p->nSuffix) ) break;
+ }
+
+ if( p->zSuffix ){
+ int nStem = nBuf - p->nSuffix;
+ if( p->xCond==0 || p->xCond(aBuf, nStem) ){
+ memcpy(&aBuf[nStem], p->zOutput, p->nOutput);
+ *pnBuf = nStem + p->nOutput;
+ ret = p - aRule;
+ }
+ }
+
+ return ret;
+}
+#endif
+
+static int fts5PorterIsVowel(char c, int bYIsVowel){
+ return (
+ c=='a' || c=='e' || c=='i' || c=='o' || c=='u' || (bYIsVowel && c=='y')
+ );
+}
+
+static int fts5PorterGobbleVC(char *zStem, int nStem, int bPrevCons){
+ int i;
+ int bCons = bPrevCons;
+
+ /* Scan for a vowel */
+ for(i=0; i<nStem; i++){
+ if( 0==(bCons = !fts5PorterIsVowel(zStem[i], bCons)) ) break;
+ }
+
+ /* Scan for a consonent */
+ for(i++; i<nStem; i++){
+ if( (bCons = !fts5PorterIsVowel(zStem[i], bCons)) ) return i+1;
+ }
+ return 0;
+}
+
+/* porter rule condition: (m > 0) */
+static int fts5Porter_MGt0(char *zStem, int nStem){
+ return !!fts5PorterGobbleVC(zStem, nStem, 0);
+}
+
+/* porter rule condition: (m > 1) */
+static int fts5Porter_MGt1(char *zStem, int nStem){
+ int n;
+ n = fts5PorterGobbleVC(zStem, nStem, 0);
+ if( n && fts5PorterGobbleVC(&zStem[n], nStem-n, 1) ){
+ return 1;
+ }
+ return 0;
+}
+
+/* porter rule condition: (m = 1) */
+static int fts5Porter_MEq1(char *zStem, int nStem){
+ int n;
+ n = fts5PorterGobbleVC(zStem, nStem, 0);
+ if( n && 0==fts5PorterGobbleVC(&zStem[n], nStem-n, 1) ){
+ return 1;
+ }
+ return 0;
+}
+
+/* porter rule condition: (*o) */
+static int fts5Porter_Ostar(char *zStem, int nStem){
+ if( zStem[nStem-1]=='w' || zStem[nStem-1]=='x' || zStem[nStem-1]=='y' ){
+ return 0;
+ }else{
+ int i;
+ int mask = 0;
+ int bCons = 0;
+ for(i=0; i<nStem; i++){
+ bCons = !fts5PorterIsVowel(zStem[i], bCons);
+ assert( bCons==0 || bCons==1 );
+ mask = (mask << 1) + bCons;
+ }
+ return ((mask & 0x0007)==0x0005);
+ }
+}
+
+/* porter rule condition: (m > 1 and (*S or *T)) */
+static int fts5Porter_MGt1_and_S_or_T(char *zStem, int nStem){
+ assert( nStem>0 );
+ return (zStem[nStem-1]=='s' || zStem[nStem-1]=='t')
+ && fts5Porter_MGt1(zStem, nStem);
+}
+
+/* porter rule condition: (*v*) */
+static int fts5Porter_Vowel(char *zStem, int nStem){
+ int i;
+ for(i=0; i<nStem; i++){
+ if( fts5PorterIsVowel(zStem[i], i>0) ){
+ return 1;
+ }
+ }
+ return 0;
+}
+
+
+/**************************************************************************
+***************************************************************************
+** GENERATED CODE STARTS HERE (mkportersteps.tcl)
+*/
+
+static int fts5PorterStep4(char *aBuf, int *pnBuf){
+ int ret = 0;
+ int nBuf = *pnBuf;
+ switch( aBuf[nBuf-2] ){
+
+ case 'a':
+ if( nBuf>2 && 0==memcmp("al", &aBuf[nBuf-2], 2) ){
+ if( fts5Porter_MGt1(aBuf, nBuf-2) ){
+ *pnBuf = nBuf - 2;
+ }
+ }
+ break;
+
+ case 'c':
+ if( nBuf>4 && 0==memcmp("ance", &aBuf[nBuf-4], 4) ){
+ if( fts5Porter_MGt1(aBuf, nBuf-4) ){
+ *pnBuf = nBuf - 4;
+ }
+ }else if( nBuf>4 && 0==memcmp("ence", &aBuf[nBuf-4], 4) ){
+ if( fts5Porter_MGt1(aBuf, nBuf-4) ){
+ *pnBuf = nBuf - 4;
+ }
+ }
+ break;
+
+ case 'e':
+ if( nBuf>2 && 0==memcmp("er", &aBuf[nBuf-2], 2) ){
+ if( fts5Porter_MGt1(aBuf, nBuf-2) ){
+ *pnBuf = nBuf - 2;
+ }
+ }
+ break;
+
+ case 'i':
+ if( nBuf>2 && 0==memcmp("ic", &aBuf[nBuf-2], 2) ){
+ if( fts5Porter_MGt1(aBuf, nBuf-2) ){
+ *pnBuf = nBuf - 2;
+ }
+ }
+ break;
+
+ case 'l':
+ if( nBuf>4 && 0==memcmp("able", &aBuf[nBuf-4], 4) ){
+ if( fts5Porter_MGt1(aBuf, nBuf-4) ){
+ *pnBuf = nBuf - 4;
+ }
+ }else if( nBuf>4 && 0==memcmp("ible", &aBuf[nBuf-4], 4) ){
+ if( fts5Porter_MGt1(aBuf, nBuf-4) ){
+ *pnBuf = nBuf - 4;
+ }
+ }
+ break;
+
+ case 'n':
+ if( nBuf>3 && 0==memcmp("ant", &aBuf[nBuf-3], 3) ){
+ if( fts5Porter_MGt1(aBuf, nBuf-3) ){
+ *pnBuf = nBuf - 3;
+ }
+ }else if( nBuf>5 && 0==memcmp("ement", &aBuf[nBuf-5], 5) ){
+ if( fts5Porter_MGt1(aBuf, nBuf-5) ){
+ *pnBuf = nBuf - 5;
+ }
+ }else if( nBuf>4 && 0==memcmp("ment", &aBuf[nBuf-4], 4) ){
+ if( fts5Porter_MGt1(aBuf, nBuf-4) ){
+ *pnBuf = nBuf - 4;
+ }
+ }else if( nBuf>3 && 0==memcmp("ent", &aBuf[nBuf-3], 3) ){
+ if( fts5Porter_MGt1(aBuf, nBuf-3) ){
+ *pnBuf = nBuf - 3;
+ }
+ }
+ break;
+
+ case 'o':
+ if( nBuf>3 && 0==memcmp("ion", &aBuf[nBuf-3], 3) ){
+ if( fts5Porter_MGt1_and_S_or_T(aBuf, nBuf-3) ){
+ *pnBuf = nBuf - 3;
+ }
+ }else if( nBuf>2 && 0==memcmp("ou", &aBuf[nBuf-2], 2) ){
+ if( fts5Porter_MGt1(aBuf, nBuf-2) ){
+ *pnBuf = nBuf - 2;
+ }
+ }
+ break;
+
+ case 's':
+ if( nBuf>3 && 0==memcmp("ism", &aBuf[nBuf-3], 3) ){
+ if( fts5Porter_MGt1(aBuf, nBuf-3) ){
+ *pnBuf = nBuf - 3;
+ }
+ }
+ break;
+
+ case 't':
+ if( nBuf>3 && 0==memcmp("ate", &aBuf[nBuf-3], 3) ){
+ if( fts5Porter_MGt1(aBuf, nBuf-3) ){
+ *pnBuf = nBuf - 3;
+ }
+ }else if( nBuf>3 && 0==memcmp("iti", &aBuf[nBuf-3], 3) ){
+ if( fts5Porter_MGt1(aBuf, nBuf-3) ){
+ *pnBuf = nBuf - 3;
+ }
+ }
+ break;
+
+ case 'u':
+ if( nBuf>3 && 0==memcmp("ous", &aBuf[nBuf-3], 3) ){
+ if( fts5Porter_MGt1(aBuf, nBuf-3) ){
+ *pnBuf = nBuf - 3;
+ }
+ }
+ break;
+
+ case 'v':
+ if( nBuf>3 && 0==memcmp("ive", &aBuf[nBuf-3], 3) ){
+ if( fts5Porter_MGt1(aBuf, nBuf-3) ){
+ *pnBuf = nBuf - 3;
+ }
+ }
+ break;
+
+ case 'z':
+ if( nBuf>3 && 0==memcmp("ize", &aBuf[nBuf-3], 3) ){
+ if( fts5Porter_MGt1(aBuf, nBuf-3) ){
+ *pnBuf = nBuf - 3;
+ }
+ }
+ break;
+
+ }
+ return ret;
+}
+
+
+static int fts5PorterStep1B2(char *aBuf, int *pnBuf){
+ int ret = 0;
+ int nBuf = *pnBuf;
+ switch( aBuf[nBuf-2] ){
+
+ case 'a':
+ if( nBuf>2 && 0==memcmp("at", &aBuf[nBuf-2], 2) ){
+ memcpy(&aBuf[nBuf-2], "ate", 3);
+ *pnBuf = nBuf - 2 + 3;
+ ret = 1;
+ }
+ break;
+
+ case 'b':
+ if( nBuf>2 && 0==memcmp("bl", &aBuf[nBuf-2], 2) ){
+ memcpy(&aBuf[nBuf-2], "ble", 3);
+ *pnBuf = nBuf - 2 + 3;
+ ret = 1;
+ }
+ break;
+
+ case 'i':
+ if( nBuf>2 && 0==memcmp("iz", &aBuf[nBuf-2], 2) ){
+ memcpy(&aBuf[nBuf-2], "ize", 3);
+ *pnBuf = nBuf - 2 + 3;
+ ret = 1;
+ }
+ break;
+
+ }
+ return ret;
+}
+
+
+static int fts5PorterStep2(char *aBuf, int *pnBuf){
+ int ret = 0;
+ int nBuf = *pnBuf;
+ switch( aBuf[nBuf-2] ){
+
+ case 'a':
+ if( nBuf>7 && 0==memcmp("ational", &aBuf[nBuf-7], 7) ){
+ if( fts5Porter_MGt0(aBuf, nBuf-7) ){
+ memcpy(&aBuf[nBuf-7], "ate", 3);
+ *pnBuf = nBuf - 7 + 3;
+ }
+ }else if( nBuf>6 && 0==memcmp("tional", &aBuf[nBuf-6], 6) ){
+ if( fts5Porter_MGt0(aBuf, nBuf-6) ){
+ memcpy(&aBuf[nBuf-6], "tion", 4);
+ *pnBuf = nBuf - 6 + 4;
+ }
+ }
+ break;
+
+ case 'c':
+ if( nBuf>4 && 0==memcmp("enci", &aBuf[nBuf-4], 4) ){
+ if( fts5Porter_MGt0(aBuf, nBuf-4) ){
+ memcpy(&aBuf[nBuf-4], "ence", 4);
+ *pnBuf = nBuf - 4 + 4;
+ }
+ }else if( nBuf>4 && 0==memcmp("anci", &aBuf[nBuf-4], 4) ){
+ if( fts5Porter_MGt0(aBuf, nBuf-4) ){
+ memcpy(&aBuf[nBuf-4], "ance", 4);
+ *pnBuf = nBuf - 4 + 4;
+ }
+ }
+ break;
+
+ case 'e':
+ if( nBuf>4 && 0==memcmp("izer", &aBuf[nBuf-4], 4) ){
+ if( fts5Porter_MGt0(aBuf, nBuf-4) ){
+ memcpy(&aBuf[nBuf-4], "ize", 3);
+ *pnBuf = nBuf - 4 + 3;
+ }
+ }
+ break;
+
+ case 'g':
+ if( nBuf>4 && 0==memcmp("logi", &aBuf[nBuf-4], 4) ){
+ if( fts5Porter_MGt0(aBuf, nBuf-4) ){
+ memcpy(&aBuf[nBuf-4], "log", 3);
+ *pnBuf = nBuf - 4 + 3;
+ }
+ }
+ break;
+
+ case 'l':
+ if( nBuf>3 && 0==memcmp("bli", &aBuf[nBuf-3], 3) ){
+ if( fts5Porter_MGt0(aBuf, nBuf-3) ){
+ memcpy(&aBuf[nBuf-3], "ble", 3);
+ *pnBuf = nBuf - 3 + 3;
+ }
+ }else if( nBuf>4 && 0==memcmp("alli", &aBuf[nBuf-4], 4) ){
+ if( fts5Porter_MGt0(aBuf, nBuf-4) ){
+ memcpy(&aBuf[nBuf-4], "al", 2);
+ *pnBuf = nBuf - 4 + 2;
+ }
+ }else if( nBuf>5 && 0==memcmp("entli", &aBuf[nBuf-5], 5) ){
+ if( fts5Porter_MGt0(aBuf, nBuf-5) ){
+ memcpy(&aBuf[nBuf-5], "ent", 3);
+ *pnBuf = nBuf - 5 + 3;
+ }
+ }else if( nBuf>3 && 0==memcmp("eli", &aBuf[nBuf-3], 3) ){
+ if( fts5Porter_MGt0(aBuf, nBuf-3) ){
+ memcpy(&aBuf[nBuf-3], "e", 1);
+ *pnBuf = nBuf - 3 + 1;
+ }
+ }else if( nBuf>5 && 0==memcmp("ousli", &aBuf[nBuf-5], 5) ){
+ if( fts5Porter_MGt0(aBuf, nBuf-5) ){
+ memcpy(&aBuf[nBuf-5], "ous", 3);
+ *pnBuf = nBuf - 5 + 3;
+ }
+ }
+ break;
+
+ case 'o':
+ if( nBuf>7 && 0==memcmp("ization", &aBuf[nBuf-7], 7) ){
+ if( fts5Porter_MGt0(aBuf, nBuf-7) ){
+ memcpy(&aBuf[nBuf-7], "ize", 3);
+ *pnBuf = nBuf - 7 + 3;
+ }
+ }else if( nBuf>5 && 0==memcmp("ation", &aBuf[nBuf-5], 5) ){
+ if( fts5Porter_MGt0(aBuf, nBuf-5) ){
+ memcpy(&aBuf[nBuf-5], "ate", 3);
+ *pnBuf = nBuf - 5 + 3;
+ }
+ }else if( nBuf>4 && 0==memcmp("ator", &aBuf[nBuf-4], 4) ){
+ if( fts5Porter_MGt0(aBuf, nBuf-4) ){
+ memcpy(&aBuf[nBuf-4], "ate", 3);
+ *pnBuf = nBuf - 4 + 3;
+ }
+ }
+ break;
+
+ case 's':
+ if( nBuf>5 && 0==memcmp("alism", &aBuf[nBuf-5], 5) ){
+ if( fts5Porter_MGt0(aBuf, nBuf-5) ){
+ memcpy(&aBuf[nBuf-5], "al", 2);
+ *pnBuf = nBuf - 5 + 2;
+ }
+ }else if( nBuf>7 && 0==memcmp("iveness", &aBuf[nBuf-7], 7) ){
+ if( fts5Porter_MGt0(aBuf, nBuf-7) ){
+ memcpy(&aBuf[nBuf-7], "ive", 3);
+ *pnBuf = nBuf - 7 + 3;
+ }
+ }else if( nBuf>7 && 0==memcmp("fulness", &aBuf[nBuf-7], 7) ){
+ if( fts5Porter_MGt0(aBuf, nBuf-7) ){
+ memcpy(&aBuf[nBuf-7], "ful", 3);
+ *pnBuf = nBuf - 7 + 3;
+ }
+ }else if( nBuf>7 && 0==memcmp("ousness", &aBuf[nBuf-7], 7) ){
+ if( fts5Porter_MGt0(aBuf, nBuf-7) ){
+ memcpy(&aBuf[nBuf-7], "ous", 3);
+ *pnBuf = nBuf - 7 + 3;
+ }
+ }
+ break;
+
+ case 't':
+ if( nBuf>5 && 0==memcmp("aliti", &aBuf[nBuf-5], 5) ){
+ if( fts5Porter_MGt0(aBuf, nBuf-5) ){
+ memcpy(&aBuf[nBuf-5], "al", 2);
+ *pnBuf = nBuf - 5 + 2;
+ }
+ }else if( nBuf>5 && 0==memcmp("iviti", &aBuf[nBuf-5], 5) ){
+ if( fts5Porter_MGt0(aBuf, nBuf-5) ){
+ memcpy(&aBuf[nBuf-5], "ive", 3);
+ *pnBuf = nBuf - 5 + 3;
+ }
+ }else if( nBuf>6 && 0==memcmp("biliti", &aBuf[nBuf-6], 6) ){
+ if( fts5Porter_MGt0(aBuf, nBuf-6) ){
+ memcpy(&aBuf[nBuf-6], "ble", 3);
+ *pnBuf = nBuf - 6 + 3;
+ }
+ }
+ break;
+
+ }
+ return ret;
+}
+
+
+static int fts5PorterStep3(char *aBuf, int *pnBuf){
+ int ret = 0;
+ int nBuf = *pnBuf;
+ switch( aBuf[nBuf-2] ){
+
+ case 'a':
+ if( nBuf>4 && 0==memcmp("ical", &aBuf[nBuf-4], 4) ){
+ if( fts5Porter_MGt0(aBuf, nBuf-4) ){
+ memcpy(&aBuf[nBuf-4], "ic", 2);
+ *pnBuf = nBuf - 4 + 2;
+ }
+ }
+ break;
+
+ case 's':
+ if( nBuf>4 && 0==memcmp("ness", &aBuf[nBuf-4], 4) ){
+ if( fts5Porter_MGt0(aBuf, nBuf-4) ){
+ *pnBuf = nBuf - 4;
+ }
+ }
+ break;
+
+ case 't':
+ if( nBuf>5 && 0==memcmp("icate", &aBuf[nBuf-5], 5) ){
+ if( fts5Porter_MGt0(aBuf, nBuf-5) ){
+ memcpy(&aBuf[nBuf-5], "ic", 2);
+ *pnBuf = nBuf - 5 + 2;
+ }
+ }else if( nBuf>5 && 0==memcmp("iciti", &aBuf[nBuf-5], 5) ){
+ if( fts5Porter_MGt0(aBuf, nBuf-5) ){
+ memcpy(&aBuf[nBuf-5], "ic", 2);
+ *pnBuf = nBuf - 5 + 2;
+ }
+ }
+ break;
+
+ case 'u':
+ if( nBuf>3 && 0==memcmp("ful", &aBuf[nBuf-3], 3) ){
+ if( fts5Porter_MGt0(aBuf, nBuf-3) ){
+ *pnBuf = nBuf - 3;
+ }
+ }
+ break;
+
+ case 'v':
+ if( nBuf>5 && 0==memcmp("ative", &aBuf[nBuf-5], 5) ){
+ if( fts5Porter_MGt0(aBuf, nBuf-5) ){
+ *pnBuf = nBuf - 5;
+ }
+ }
+ break;
+
+ case 'z':
+ if( nBuf>5 && 0==memcmp("alize", &aBuf[nBuf-5], 5) ){
+ if( fts5Porter_MGt0(aBuf, nBuf-5) ){
+ memcpy(&aBuf[nBuf-5], "al", 2);
+ *pnBuf = nBuf - 5 + 2;
+ }
+ }
+ break;
+
+ }
+ return ret;
+}
+
+
+static int fts5PorterStep1B(char *aBuf, int *pnBuf){
+ int ret = 0;
+ int nBuf = *pnBuf;
+ switch( aBuf[nBuf-2] ){
+
+ case 'e':
+ if( nBuf>3 && 0==memcmp("eed", &aBuf[nBuf-3], 3) ){
+ if( fts5Porter_MGt0(aBuf, nBuf-3) ){
+ memcpy(&aBuf[nBuf-3], "ee", 2);
+ *pnBuf = nBuf - 3 + 2;
+ }
+ }else if( nBuf>2 && 0==memcmp("ed", &aBuf[nBuf-2], 2) ){
+ if( fts5Porter_Vowel(aBuf, nBuf-2) ){
+ *pnBuf = nBuf - 2;
+ ret = 1;
+ }
+ }
+ break;
+
+ case 'n':
+ if( nBuf>3 && 0==memcmp("ing", &aBuf[nBuf-3], 3) ){
+ if( fts5Porter_Vowel(aBuf, nBuf-3) ){
+ *pnBuf = nBuf - 3;
+ ret = 1;
+ }
+ }
+ break;
+
+ }
+ return ret;
+}
+
+/*
+** GENERATED CODE ENDS HERE (mkportersteps.tcl)
+***************************************************************************
+**************************************************************************/
+
+static void fts5PorterStep1A(char *aBuf, int *pnBuf){
+ int nBuf = *pnBuf;
+ if( aBuf[nBuf-1]=='s' ){
+ if( aBuf[nBuf-2]=='e' ){
+ if( (nBuf>4 && aBuf[nBuf-4]=='s' && aBuf[nBuf-3]=='s')
+ || (nBuf>3 && aBuf[nBuf-3]=='i' )
+ ){
+ *pnBuf = nBuf-2;
+ }else{
+ *pnBuf = nBuf-1;
+ }
+ }
+ else if( aBuf[nBuf-2]!='s' ){
+ *pnBuf = nBuf-1;
+ }
+ }
+}
+
+static int fts5PorterCb(
+ void *pCtx,
+ int tflags,
+ const char *pToken,
+ int nToken,
+ int iStart,
+ int iEnd
+){
+ PorterContext *p = (PorterContext*)pCtx;
+
+ char *aBuf;
+ int nBuf;
+
+ if( nToken>FTS5_PORTER_MAX_TOKEN || nToken<3 ) goto pass_through;
+ aBuf = p->aBuf;
+ nBuf = nToken;
+ memcpy(aBuf, pToken, nBuf);
+
+ /* Step 1. */
+ fts5PorterStep1A(aBuf, &nBuf);
+ if( fts5PorterStep1B(aBuf, &nBuf) ){
+ if( fts5PorterStep1B2(aBuf, &nBuf)==0 ){
+ char c = aBuf[nBuf-1];
+ if( fts5PorterIsVowel(c, 0)==0
+ && c!='l' && c!='s' && c!='z' && c==aBuf[nBuf-2]
+ ){
+ nBuf--;
+ }else if( fts5Porter_MEq1(aBuf, nBuf) && fts5Porter_Ostar(aBuf, nBuf) ){
+ aBuf[nBuf++] = 'e';
+ }
+ }
+ }
+
+ /* Step 1C. */
+ if( aBuf[nBuf-1]=='y' && fts5Porter_Vowel(aBuf, nBuf-1) ){
+ aBuf[nBuf-1] = 'i';
+ }
+
+ /* Steps 2 through 4. */
+ fts5PorterStep2(aBuf, &nBuf);
+ fts5PorterStep3(aBuf, &nBuf);
+ fts5PorterStep4(aBuf, &nBuf);
+
+ /* Step 5a. */
+ assert( nBuf>0 );
+ if( aBuf[nBuf-1]=='e' ){
+ if( fts5Porter_MGt1(aBuf, nBuf-1)
+ || (fts5Porter_MEq1(aBuf, nBuf-1) && !fts5Porter_Ostar(aBuf, nBuf-1))
+ ){
+ nBuf--;
+ }
+ }
+
+ /* Step 5b. */
+ if( nBuf>1 && aBuf[nBuf-1]=='l'
+ && aBuf[nBuf-2]=='l' && fts5Porter_MGt1(aBuf, nBuf-1)
+ ){
+ nBuf--;
+ }
+
+ return p->xToken(p->pCtx, tflags, aBuf, nBuf, iStart, iEnd);
+
+ pass_through:
+ return p->xToken(p->pCtx, tflags, pToken, nToken, iStart, iEnd);
+}
+
+/*
+** Tokenize using the porter tokenizer.
+*/
+static int fts5PorterTokenize(
+ Fts5Tokenizer *pTokenizer,
+ void *pCtx,
+ int flags,
+ const char *pText, int nText,
+ int (*xToken)(void*, int, const char*, int nToken, int iStart, int iEnd)
+){
+ PorterTokenizer *p = (PorterTokenizer*)pTokenizer;
+ PorterContext sCtx;
+ sCtx.xToken = xToken;
+ sCtx.pCtx = pCtx;
+ sCtx.aBuf = p->aBuf;
+ return p->tokenizer.xTokenize(
+ p->pTokenizer, (void*)&sCtx, flags, pText, nText, fts5PorterCb
+ );
+}
+
+/*
+** Register all built-in tokenizers with FTS5.
+*/
+static int sqlite3Fts5TokenizerInit(fts5_api *pApi){
+ struct BuiltinTokenizer {
+ const char *zName;
+ fts5_tokenizer x;
+ } aBuiltin[] = {
+ { "unicode61", {fts5UnicodeCreate, fts5UnicodeDelete, fts5UnicodeTokenize}},
+ { "ascii", {fts5AsciiCreate, fts5AsciiDelete, fts5AsciiTokenize }},
+ { "porter", {fts5PorterCreate, fts5PorterDelete, fts5PorterTokenize }},
+ };
+
+ int rc = SQLITE_OK; /* Return code */
+ int i; /* To iterate through builtin functions */
+
+ for(i=0; rc==SQLITE_OK && i<ArraySize(aBuiltin); i++){
+ rc = pApi->xCreateTokenizer(pApi,
+ aBuiltin[i].zName,
+ (void*)pApi,
+ &aBuiltin[i].x,
+ 0
+ );
+ }
+
+ return rc;
+}
+
+
+
+/*
+** 2012 May 25
+**
+** 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.
+**
+******************************************************************************
+*/
+
+/*
+** DO NOT EDIT THIS MACHINE GENERATED FILE.
+*/
+
+
+/* #include <assert.h> */
+
+/*
+** Return true if the argument corresponds to a unicode codepoint
+** classified as either a letter or a number. Otherwise false.
+**
+** The results are undefined if the value passed to this function
+** is less than zero.
+*/
+static int sqlite3Fts5UnicodeIsalnum(int c){
+ /* Each unsigned integer in the following array corresponds to a contiguous
+ ** range of unicode codepoints that are not either letters or numbers (i.e.
+ ** codepoints for which this function should return 0).
+ **
+ ** The most significant 22 bits in each 32-bit value contain the first
+ ** codepoint in the range. The least significant 10 bits are used to store
+ ** the size of the range (always at least 1). In other words, the value
+ ** ((C<<22) + N) represents a range of N codepoints starting with codepoint
+ ** C. It is not possible to represent a range larger than 1023 codepoints
+ ** using this format.
+ */
+ static const unsigned int aEntry[] = {
+ 0x00000030, 0x0000E807, 0x00016C06, 0x0001EC2F, 0x0002AC07,
+ 0x0002D001, 0x0002D803, 0x0002EC01, 0x0002FC01, 0x00035C01,
+ 0x0003DC01, 0x000B0804, 0x000B480E, 0x000B9407, 0x000BB401,
+ 0x000BBC81, 0x000DD401, 0x000DF801, 0x000E1002, 0x000E1C01,
+ 0x000FD801, 0x00120808, 0x00156806, 0x00162402, 0x00163C01,
+ 0x00164437, 0x0017CC02, 0x00180005, 0x00181816, 0x00187802,
+ 0x00192C15, 0x0019A804, 0x0019C001, 0x001B5001, 0x001B580F,
+ 0x001B9C07, 0x001BF402, 0x001C000E, 0x001C3C01, 0x001C4401,
+ 0x001CC01B, 0x001E980B, 0x001FAC09, 0x001FD804, 0x00205804,
+ 0x00206C09, 0x00209403, 0x0020A405, 0x0020C00F, 0x00216403,
+ 0x00217801, 0x0023901B, 0x00240004, 0x0024E803, 0x0024F812,
+ 0x00254407, 0x00258804, 0x0025C001, 0x00260403, 0x0026F001,
+ 0x0026F807, 0x00271C02, 0x00272C03, 0x00275C01, 0x00278802,
+ 0x0027C802, 0x0027E802, 0x00280403, 0x0028F001, 0x0028F805,
+ 0x00291C02, 0x00292C03, 0x00294401, 0x0029C002, 0x0029D401,
+ 0x002A0403, 0x002AF001, 0x002AF808, 0x002B1C03, 0x002B2C03,
+ 0x002B8802, 0x002BC002, 0x002C0403, 0x002CF001, 0x002CF807,
+ 0x002D1C02, 0x002D2C03, 0x002D5802, 0x002D8802, 0x002DC001,
+ 0x002E0801, 0x002EF805, 0x002F1803, 0x002F2804, 0x002F5C01,
+ 0x002FCC08, 0x00300403, 0x0030F807, 0x00311803, 0x00312804,
+ 0x00315402, 0x00318802, 0x0031FC01, 0x00320802, 0x0032F001,
+ 0x0032F807, 0x00331803, 0x00332804, 0x00335402, 0x00338802,
+ 0x00340802, 0x0034F807, 0x00351803, 0x00352804, 0x00355C01,
+ 0x00358802, 0x0035E401, 0x00360802, 0x00372801, 0x00373C06,
+ 0x00375801, 0x00376008, 0x0037C803, 0x0038C401, 0x0038D007,
+ 0x0038FC01, 0x00391C09, 0x00396802, 0x003AC401, 0x003AD006,
+ 0x003AEC02, 0x003B2006, 0x003C041F, 0x003CD00C, 0x003DC417,
+ 0x003E340B, 0x003E6424, 0x003EF80F, 0x003F380D, 0x0040AC14,
+ 0x00412806, 0x00415804, 0x00417803, 0x00418803, 0x00419C07,
+ 0x0041C404, 0x0042080C, 0x00423C01, 0x00426806, 0x0043EC01,
+ 0x004D740C, 0x004E400A, 0x00500001, 0x0059B402, 0x005A0001,
+ 0x005A6C02, 0x005BAC03, 0x005C4803, 0x005CC805, 0x005D4802,
+ 0x005DC802, 0x005ED023, 0x005F6004, 0x005F7401, 0x0060000F,
+ 0x0062A401, 0x0064800C, 0x0064C00C, 0x00650001, 0x00651002,
+ 0x0066C011, 0x00672002, 0x00677822, 0x00685C05, 0x00687802,
+ 0x0069540A, 0x0069801D, 0x0069FC01, 0x006A8007, 0x006AA006,
+ 0x006C0005, 0x006CD011, 0x006D6823, 0x006E0003, 0x006E840D,
+ 0x006F980E, 0x006FF004, 0x00709014, 0x0070EC05, 0x0071F802,
+ 0x00730008, 0x00734019, 0x0073B401, 0x0073C803, 0x00770027,
+ 0x0077F004, 0x007EF401, 0x007EFC03, 0x007F3403, 0x007F7403,
+ 0x007FB403, 0x007FF402, 0x00800065, 0x0081A806, 0x0081E805,
+ 0x00822805, 0x0082801A, 0x00834021, 0x00840002, 0x00840C04,
+ 0x00842002, 0x00845001, 0x00845803, 0x00847806, 0x00849401,
+ 0x00849C01, 0x0084A401, 0x0084B801, 0x0084E802, 0x00850005,
+ 0x00852804, 0x00853C01, 0x00864264, 0x00900027, 0x0091000B,
+ 0x0092704E, 0x00940200, 0x009C0475, 0x009E53B9, 0x00AD400A,
+ 0x00B39406, 0x00B3BC03, 0x00B3E404, 0x00B3F802, 0x00B5C001,
+ 0x00B5FC01, 0x00B7804F, 0x00B8C00C, 0x00BA001A, 0x00BA6C59,
+ 0x00BC00D6, 0x00BFC00C, 0x00C00005, 0x00C02019, 0x00C0A807,
+ 0x00C0D802, 0x00C0F403, 0x00C26404, 0x00C28001, 0x00C3EC01,
+ 0x00C64002, 0x00C6580A, 0x00C70024, 0x00C8001F, 0x00C8A81E,
+ 0x00C94001, 0x00C98020, 0x00CA2827, 0x00CB003F, 0x00CC0100,
+ 0x01370040, 0x02924037, 0x0293F802, 0x02983403, 0x0299BC10,
+ 0x029A7C01, 0x029BC008, 0x029C0017, 0x029C8002, 0x029E2402,
+ 0x02A00801, 0x02A01801, 0x02A02C01, 0x02A08C09, 0x02A0D804,
+ 0x02A1D004, 0x02A20002, 0x02A2D011, 0x02A33802, 0x02A38012,
+ 0x02A3E003, 0x02A4980A, 0x02A51C0D, 0x02A57C01, 0x02A60004,
+ 0x02A6CC1B, 0x02A77802, 0x02A8A40E, 0x02A90C01, 0x02A93002,
+ 0x02A97004, 0x02A9DC03, 0x02A9EC01, 0x02AAC001, 0x02AAC803,
+ 0x02AADC02, 0x02AAF802, 0x02AB0401, 0x02AB7802, 0x02ABAC07,
+ 0x02ABD402, 0x02AF8C0B, 0x03600001, 0x036DFC02, 0x036FFC02,
+ 0x037FFC01, 0x03EC7801, 0x03ECA401, 0x03EEC810, 0x03F4F802,
+ 0x03F7F002, 0x03F8001A, 0x03F88007, 0x03F8C023, 0x03F95013,
+ 0x03F9A004, 0x03FBFC01, 0x03FC040F, 0x03FC6807, 0x03FCEC06,
+ 0x03FD6C0B, 0x03FF8007, 0x03FFA007, 0x03FFE405, 0x04040003,
+ 0x0404DC09, 0x0405E411, 0x0406400C, 0x0407402E, 0x040E7C01,
+ 0x040F4001, 0x04215C01, 0x04247C01, 0x0424FC01, 0x04280403,
+ 0x04281402, 0x04283004, 0x0428E003, 0x0428FC01, 0x04294009,
+ 0x0429FC01, 0x042CE407, 0x04400003, 0x0440E016, 0x04420003,
+ 0x0442C012, 0x04440003, 0x04449C0E, 0x04450004, 0x04460003,
+ 0x0446CC0E, 0x04471404, 0x045AAC0D, 0x0491C004, 0x05BD442E,
+ 0x05BE3C04, 0x074000F6, 0x07440027, 0x0744A4B5, 0x07480046,
+ 0x074C0057, 0x075B0401, 0x075B6C01, 0x075BEC01, 0x075C5401,
+ 0x075CD401, 0x075D3C01, 0x075DBC01, 0x075E2401, 0x075EA401,
+ 0x075F0C01, 0x07BBC002, 0x07C0002C, 0x07C0C064, 0x07C2800F,
+ 0x07C2C40E, 0x07C3040F, 0x07C3440F, 0x07C4401F, 0x07C4C03C,
+ 0x07C5C02B, 0x07C7981D, 0x07C8402B, 0x07C90009, 0x07C94002,
+ 0x07CC0021, 0x07CCC006, 0x07CCDC46, 0x07CE0014, 0x07CE8025,
+ 0x07CF1805, 0x07CF8011, 0x07D0003F, 0x07D10001, 0x07D108B6,
+ 0x07D3E404, 0x07D4003E, 0x07D50004, 0x07D54018, 0x07D7EC46,
+ 0x07D9140B, 0x07DA0046, 0x07DC0074, 0x38000401, 0x38008060,
+ 0x380400F0,
+ };
+ static const unsigned int aAscii[4] = {
+ 0xFFFFFFFF, 0xFC00FFFF, 0xF8000001, 0xF8000001,
+ };
+
+ if( (unsigned int)c<128 ){
+ return ( (aAscii[c >> 5] & (1 << (c & 0x001F)))==0 );
+ }else if( (unsigned int)c<(1<<22) ){
+ unsigned int key = (((unsigned int)c)<<10) | 0x000003FF;
+ int iRes = 0;
+ int iHi = sizeof(aEntry)/sizeof(aEntry[0]) - 1;
+ int iLo = 0;
+ while( iHi>=iLo ){
+ int iTest = (iHi + iLo) / 2;
+ if( key >= aEntry[iTest] ){
+ iRes = iTest;
+ iLo = iTest+1;
+ }else{
+ iHi = iTest-1;
+ }
+ }
+ assert( aEntry[0]<key );
+ assert( key>=aEntry[iRes] );
+ return (((unsigned int)c) >= ((aEntry[iRes]>>10) + (aEntry[iRes]&0x3FF)));
+ }
+ return 1;
+}
+
+
+/*
+** If the argument is a codepoint corresponding to a lowercase letter
+** in the ASCII range with a diacritic added, return the codepoint
+** of the ASCII letter only. For example, if passed 235 - "LATIN
+** SMALL LETTER E WITH DIAERESIS" - return 65 ("LATIN SMALL LETTER
+** E"). The resuls of passing a codepoint that corresponds to an
+** uppercase letter are undefined.
+*/
+static int fts5_remove_diacritic(int c){
+ unsigned short aDia[] = {
+ 0, 1797, 1848, 1859, 1891, 1928, 1940, 1995,
+ 2024, 2040, 2060, 2110, 2168, 2206, 2264, 2286,
+ 2344, 2383, 2472, 2488, 2516, 2596, 2668, 2732,
+ 2782, 2842, 2894, 2954, 2984, 3000, 3028, 3336,
+ 3456, 3696, 3712, 3728, 3744, 3896, 3912, 3928,
+ 3968, 4008, 4040, 4106, 4138, 4170, 4202, 4234,
+ 4266, 4296, 4312, 4344, 4408, 4424, 4472, 4504,
+ 6148, 6198, 6264, 6280, 6360, 6429, 6505, 6529,
+ 61448, 61468, 61534, 61592, 61642, 61688, 61704, 61726,
+ 61784, 61800, 61836, 61880, 61914, 61948, 61998, 62122,
+ 62154, 62200, 62218, 62302, 62364, 62442, 62478, 62536,
+ 62554, 62584, 62604, 62640, 62648, 62656, 62664, 62730,
+ 62924, 63050, 63082, 63274, 63390,
+ };
+ char aChar[] = {
+ '\0', 'a', 'c', 'e', 'i', 'n', 'o', 'u', 'y', 'y', 'a', 'c',
+ 'd', 'e', 'e', 'g', 'h', 'i', 'j', 'k', 'l', 'n', 'o', 'r',
+ 's', 't', 'u', 'u', 'w', 'y', 'z', 'o', 'u', 'a', 'i', 'o',
+ 'u', 'g', 'k', 'o', 'j', 'g', 'n', 'a', 'e', 'i', 'o', 'r',
+ 'u', 's', 't', 'h', 'a', 'e', 'o', 'y', '\0', '\0', '\0', '\0',
+ '\0', '\0', '\0', '\0', 'a', 'b', 'd', 'd', 'e', 'f', 'g', 'h',
+ 'h', 'i', 'k', 'l', 'l', 'm', 'n', 'p', 'r', 'r', 's', 't',
+ 'u', 'v', 'w', 'w', 'x', 'y', 'z', 'h', 't', 'w', 'y', 'a',
+ 'e', 'i', 'o', 'u', 'y',
+ };
+
+ unsigned int key = (((unsigned int)c)<<3) | 0x00000007;
+ int iRes = 0;
+ int iHi = sizeof(aDia)/sizeof(aDia[0]) - 1;
+ int iLo = 0;
+ while( iHi>=iLo ){
+ int iTest = (iHi + iLo) / 2;
+ if( key >= aDia[iTest] ){
+ iRes = iTest;
+ iLo = iTest+1;
+ }else{
+ iHi = iTest-1;
+ }
+ }
+ assert( key>=aDia[iRes] );
+ return ((c > (aDia[iRes]>>3) + (aDia[iRes]&0x07)) ? c : (int)aChar[iRes]);
+}
+
+
+/*
+** Return true if the argument interpreted as a unicode codepoint
+** is a diacritical modifier character.
+*/
+static int sqlite3Fts5UnicodeIsdiacritic(int c){
+ unsigned int mask0 = 0x08029FDF;
+ unsigned int mask1 = 0x000361F8;
+ if( c<768 || c>817 ) return 0;
+ return (c < 768+32) ?
+ (mask0 & (1 << (c-768))) :
+ (mask1 & (1 << (c-768-32)));
+}
+
+
+/*
+** Interpret the argument as a unicode codepoint. If the codepoint
+** is an upper case character that has a lower case equivalent,
+** return the codepoint corresponding to the lower case version.
+** Otherwise, return a copy of the argument.
+**
+** The results are undefined if the value passed to this function
+** is less than zero.
+*/
+static int sqlite3Fts5UnicodeFold(int c, int bRemoveDiacritic){
+ /* Each entry in the following array defines a rule for folding a range
+ ** of codepoints to lower case. The rule applies to a range of nRange
+ ** codepoints starting at codepoint iCode.
+ **
+ ** If the least significant bit in flags is clear, then the rule applies
+ ** to all nRange codepoints (i.e. all nRange codepoints are upper case and
+ ** need to be folded). Or, if it is set, then the rule only applies to
+ ** every second codepoint in the range, starting with codepoint C.
+ **
+ ** The 7 most significant bits in flags are an index into the aiOff[]
+ ** array. If a specific codepoint C does require folding, then its lower
+ ** case equivalent is ((C + aiOff[flags>>1]) & 0xFFFF).
+ **
+ ** The contents of this array are generated by parsing the CaseFolding.txt
+ ** file distributed as part of the "Unicode Character Database". See
+ ** http://www.unicode.org for details.
+ */
+ static const struct TableEntry {
+ unsigned short iCode;
+ unsigned char flags;
+ unsigned char nRange;
+ } aEntry[] = {
+ {65, 14, 26}, {181, 64, 1}, {192, 14, 23},
+ {216, 14, 7}, {256, 1, 48}, {306, 1, 6},
+ {313, 1, 16}, {330, 1, 46}, {376, 116, 1},
+ {377, 1, 6}, {383, 104, 1}, {385, 50, 1},
+ {386, 1, 4}, {390, 44, 1}, {391, 0, 1},
+ {393, 42, 2}, {395, 0, 1}, {398, 32, 1},
+ {399, 38, 1}, {400, 40, 1}, {401, 0, 1},
+ {403, 42, 1}, {404, 46, 1}, {406, 52, 1},
+ {407, 48, 1}, {408, 0, 1}, {412, 52, 1},
+ {413, 54, 1}, {415, 56, 1}, {416, 1, 6},
+ {422, 60, 1}, {423, 0, 1}, {425, 60, 1},
+ {428, 0, 1}, {430, 60, 1}, {431, 0, 1},
+ {433, 58, 2}, {435, 1, 4}, {439, 62, 1},
+ {440, 0, 1}, {444, 0, 1}, {452, 2, 1},
+ {453, 0, 1}, {455, 2, 1}, {456, 0, 1},
+ {458, 2, 1}, {459, 1, 18}, {478, 1, 18},
+ {497, 2, 1}, {498, 1, 4}, {502, 122, 1},
+ {503, 134, 1}, {504, 1, 40}, {544, 110, 1},
+ {546, 1, 18}, {570, 70, 1}, {571, 0, 1},
+ {573, 108, 1}, {574, 68, 1}, {577, 0, 1},
+ {579, 106, 1}, {580, 28, 1}, {581, 30, 1},
+ {582, 1, 10}, {837, 36, 1}, {880, 1, 4},
+ {886, 0, 1}, {902, 18, 1}, {904, 16, 3},
+ {908, 26, 1}, {910, 24, 2}, {913, 14, 17},
+ {931, 14, 9}, {962, 0, 1}, {975, 4, 1},
+ {976, 140, 1}, {977, 142, 1}, {981, 146, 1},
+ {982, 144, 1}, {984, 1, 24}, {1008, 136, 1},
+ {1009, 138, 1}, {1012, 130, 1}, {1013, 128, 1},
+ {1015, 0, 1}, {1017, 152, 1}, {1018, 0, 1},
+ {1021, 110, 3}, {1024, 34, 16}, {1040, 14, 32},
+ {1120, 1, 34}, {1162, 1, 54}, {1216, 6, 1},
+ {1217, 1, 14}, {1232, 1, 88}, {1329, 22, 38},
+ {4256, 66, 38}, {4295, 66, 1}, {4301, 66, 1},
+ {7680, 1, 150}, {7835, 132, 1}, {7838, 96, 1},
+ {7840, 1, 96}, {7944, 150, 8}, {7960, 150, 6},
+ {7976, 150, 8}, {7992, 150, 8}, {8008, 150, 6},
+ {8025, 151, 8}, {8040, 150, 8}, {8072, 150, 8},
+ {8088, 150, 8}, {8104, 150, 8}, {8120, 150, 2},
+ {8122, 126, 2}, {8124, 148, 1}, {8126, 100, 1},
+ {8136, 124, 4}, {8140, 148, 1}, {8152, 150, 2},
+ {8154, 120, 2}, {8168, 150, 2}, {8170, 118, 2},
+ {8172, 152, 1}, {8184, 112, 2}, {8186, 114, 2},
+ {8188, 148, 1}, {8486, 98, 1}, {8490, 92, 1},
+ {8491, 94, 1}, {8498, 12, 1}, {8544, 8, 16},
+ {8579, 0, 1}, {9398, 10, 26}, {11264, 22, 47},
+ {11360, 0, 1}, {11362, 88, 1}, {11363, 102, 1},
+ {11364, 90, 1}, {11367, 1, 6}, {11373, 84, 1},
+ {11374, 86, 1}, {11375, 80, 1}, {11376, 82, 1},
+ {11378, 0, 1}, {11381, 0, 1}, {11390, 78, 2},
+ {11392, 1, 100}, {11499, 1, 4}, {11506, 0, 1},
+ {42560, 1, 46}, {42624, 1, 24}, {42786, 1, 14},
+ {42802, 1, 62}, {42873, 1, 4}, {42877, 76, 1},
+ {42878, 1, 10}, {42891, 0, 1}, {42893, 74, 1},
+ {42896, 1, 4}, {42912, 1, 10}, {42922, 72, 1},
+ {65313, 14, 26},
+ };
+ static const unsigned short aiOff[] = {
+ 1, 2, 8, 15, 16, 26, 28, 32,
+ 37, 38, 40, 48, 63, 64, 69, 71,
+ 79, 80, 116, 202, 203, 205, 206, 207,
+ 209, 210, 211, 213, 214, 217, 218, 219,
+ 775, 7264, 10792, 10795, 23228, 23256, 30204, 54721,
+ 54753, 54754, 54756, 54787, 54793, 54809, 57153, 57274,
+ 57921, 58019, 58363, 61722, 65268, 65341, 65373, 65406,
+ 65408, 65410, 65415, 65424, 65436, 65439, 65450, 65462,
+ 65472, 65476, 65478, 65480, 65482, 65488, 65506, 65511,
+ 65514, 65521, 65527, 65528, 65529,
+ };
+
+ int ret = c;
+
+ assert( sizeof(unsigned short)==2 && sizeof(unsigned char)==1 );
+
+ if( c<128 ){
+ if( c>='A' && c<='Z' ) ret = c + ('a' - 'A');
+ }else if( c<65536 ){
+ const struct TableEntry *p;
+ int iHi = sizeof(aEntry)/sizeof(aEntry[0]) - 1;
+ int iLo = 0;
+ int iRes = -1;
+
+ assert( c>aEntry[0].iCode );
+ while( iHi>=iLo ){
+ int iTest = (iHi + iLo) / 2;
+ int cmp = (c - aEntry[iTest].iCode);
+ if( cmp>=0 ){
+ iRes = iTest;
+ iLo = iTest+1;
+ }else{
+ iHi = iTest-1;
+ }
+ }
+
+ assert( iRes>=0 && c>=aEntry[iRes].iCode );
+ p = &aEntry[iRes];
+ if( c<(p->iCode + p->nRange) && 0==(0x01 & p->flags & (p->iCode ^ c)) ){
+ ret = (c + (aiOff[p->flags>>1])) & 0x0000FFFF;
+ assert( ret>0 );
+ }
+
+ if( bRemoveDiacritic ) ret = fts5_remove_diacritic(ret);
+ }
+
+ else if( c>=66560 && c<66600 ){
+ ret = c + 40;
+ }
+
+ return ret;
+}
+
+/*
+** 2015 May 30
+**
+** 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.
+**
+******************************************************************************
+**
+** Routines for varint serialization and deserialization.
+*/
+
+
+/* #include "fts5Int.h" */
+
+/*
+** This is a copy of the sqlite3GetVarint32() routine from the SQLite core.
+** Except, this version does handle the single byte case that the core
+** version depends on being handled before its function is called.
+*/
+static int sqlite3Fts5GetVarint32(const unsigned char *p, u32 *v){
+ u32 a,b;
+
+ /* The 1-byte case. Overwhelmingly the most common. */
+ a = *p;
+ /* a: p0 (unmasked) */
+ if (!(a&0x80))
+ {
+ /* Values between 0 and 127 */
+ *v = a;
+ return 1;
+ }
+
+ /* The 2-byte case */
+ p++;
+ b = *p;
+ /* b: p1 (unmasked) */
+ if (!(b&0x80))
+ {
+ /* Values between 128 and 16383 */
+ a &= 0x7f;
+ a = a<<7;
+ *v = a | b;
+ return 2;
+ }
+
+ /* The 3-byte case */
+ p++;
+ a = a<<14;
+ a |= *p;
+ /* a: p0<<14 | p2 (unmasked) */
+ if (!(a&0x80))
+ {
+ /* Values between 16384 and 2097151 */
+ a &= (0x7f<<14)|(0x7f);
+ b &= 0x7f;
+ b = b<<7;
+ *v = a | b;
+ return 3;
+ }
+
+ /* A 32-bit varint is used to store size information in btrees.
+ ** Objects are rarely larger than 2MiB limit of a 3-byte varint.
+ ** A 3-byte varint is sufficient, for example, to record the size
+ ** of a 1048569-byte BLOB or string.
+ **
+ ** We only unroll the first 1-, 2-, and 3- byte cases. The very
+ ** rare larger cases can be handled by the slower 64-bit varint
+ ** routine.
+ */
+ {
+ u64 v64;
+ u8 n;
+ p -= 2;
+ n = sqlite3Fts5GetVarint(p, &v64);
+ *v = (u32)v64;
+ assert( n>3 && n<=9 );
+ return n;
+ }
+}
+
+
+/*
+** Bitmasks used by sqlite3GetVarint(). These precomputed constants
+** are defined here rather than simply putting the constant expressions
+** inline in order to work around bugs in the RVT compiler.
+**
+** SLOT_2_0 A mask for (0x7f<<14) | 0x7f
+**
+** SLOT_4_2_0 A mask for (0x7f<<28) | SLOT_2_0
+*/
+#define SLOT_2_0 0x001fc07f
+#define SLOT_4_2_0 0xf01fc07f
+
+/*
+** Read a 64-bit variable-length integer from memory starting at p[0].
+** Return the number of bytes read. The value is stored in *v.
+*/
+static u8 sqlite3Fts5GetVarint(const unsigned char *p, u64 *v){
+ u32 a,b,s;
+
+ a = *p;
+ /* a: p0 (unmasked) */
+ if (!(a&0x80))
+ {
+ *v = a;
+ return 1;
+ }
+
+ p++;
+ b = *p;
+ /* b: p1 (unmasked) */
+ if (!(b&0x80))
+ {
+ a &= 0x7f;
+ a = a<<7;
+ a |= b;
+ *v = a;
+ return 2;
+ }
+
+ /* Verify that constants are precomputed correctly */
+ assert( SLOT_2_0 == ((0x7f<<14) | (0x7f)) );
+ assert( SLOT_4_2_0 == ((0xfU<<28) | (0x7f<<14) | (0x7f)) );
+
+ p++;
+ a = a<<14;
+ a |= *p;
+ /* a: p0<<14 | p2 (unmasked) */
+ if (!(a&0x80))
+ {
+ a &= SLOT_2_0;
+ b &= 0x7f;
+ b = b<<7;
+ a |= b;
+ *v = a;
+ return 3;
+ }
+
+ /* CSE1 from below */
+ a &= SLOT_2_0;
+ p++;
+ b = b<<14;
+ b |= *p;
+ /* b: p1<<14 | p3 (unmasked) */
+ if (!(b&0x80))
+ {
+ b &= SLOT_2_0;
+ /* moved CSE1 up */
+ /* a &= (0x7f<<14)|(0x7f); */
+ a = a<<7;
+ a |= b;
+ *v = a;
+ return 4;
+ }
+
+ /* a: p0<<14 | p2 (masked) */
+ /* b: p1<<14 | p3 (unmasked) */
+ /* 1:save off p0<<21 | p1<<14 | p2<<7 | p3 (masked) */
+ /* moved CSE1 up */
+ /* a &= (0x7f<<14)|(0x7f); */
+ b &= SLOT_2_0;
+ s = a;
+ /* s: p0<<14 | p2 (masked) */
+
+ p++;
+ a = a<<14;
+ a |= *p;
+ /* a: p0<<28 | p2<<14 | p4 (unmasked) */
+ if (!(a&0x80))
+ {
+ /* we can skip these cause they were (effectively) done above in calc'ing s */
+ /* a &= (0x7f<<28)|(0x7f<<14)|(0x7f); */
+ /* b &= (0x7f<<14)|(0x7f); */
+ b = b<<7;
+ a |= b;
+ s = s>>18;
+ *v = ((u64)s)<<32 | a;
+ return 5;
+ }
+
+ /* 2:save off p0<<21 | p1<<14 | p2<<7 | p3 (masked) */
+ s = s<<7;
+ s |= b;
+ /* s: p0<<21 | p1<<14 | p2<<7 | p3 (masked) */
+
+ p++;
+ b = b<<14;
+ b |= *p;
+ /* b: p1<<28 | p3<<14 | p5 (unmasked) */
+ if (!(b&0x80))
+ {
+ /* we can skip this cause it was (effectively) done above in calc'ing s */
+ /* b &= (0x7f<<28)|(0x7f<<14)|(0x7f); */
+ a &= SLOT_2_0;
+ a = a<<7;
+ a |= b;
+ s = s>>18;
+ *v = ((u64)s)<<32 | a;
+ return 6;
+ }
+
+ p++;
+ a = a<<14;
+ a |= *p;
+ /* a: p2<<28 | p4<<14 | p6 (unmasked) */
+ if (!(a&0x80))
+ {
+ a &= SLOT_4_2_0;
+ b &= SLOT_2_0;
+ b = b<<7;
+ a |= b;
+ s = s>>11;
+ *v = ((u64)s)<<32 | a;
+ return 7;
+ }
+
+ /* CSE2 from below */
+ a &= SLOT_2_0;
+ p++;
+ b = b<<14;
+ b |= *p;
+ /* b: p3<<28 | p5<<14 | p7 (unmasked) */
+ if (!(b&0x80))
+ {
+ b &= SLOT_4_2_0;
+ /* moved CSE2 up */
+ /* a &= (0x7f<<14)|(0x7f); */
+ a = a<<7;
+ a |= b;
+ s = s>>4;
+ *v = ((u64)s)<<32 | a;
+ return 8;
+ }
+
+ p++;
+ a = a<<15;
+ a |= *p;
+ /* a: p4<<29 | p6<<15 | p8 (unmasked) */
+
+ /* moved CSE2 up */
+ /* a &= (0x7f<<29)|(0x7f<<15)|(0xff); */
+ b &= SLOT_2_0;
+ b = b<<8;
+ a |= b;
+
+ s = s<<4;
+ b = p[-4];
+ b &= 0x7f;
+ b = b>>3;
+ s |= b;
+
+ *v = ((u64)s)<<32 | a;
+
+ return 9;
+}
+
+/*
+** The variable-length integer encoding is as follows:
+**
+** KEY:
+** A = 0xxxxxxx 7 bits of data and one flag bit
+** B = 1xxxxxxx 7 bits of data and one flag bit
+** C = xxxxxxxx 8 bits of data
+**
+** 7 bits - A
+** 14 bits - BA
+** 21 bits - BBA
+** 28 bits - BBBA
+** 35 bits - BBBBA
+** 42 bits - BBBBBA
+** 49 bits - BBBBBBA
+** 56 bits - BBBBBBBA
+** 64 bits - BBBBBBBBC
+*/
+
+#ifdef SQLITE_NOINLINE
+# define FTS5_NOINLINE SQLITE_NOINLINE
+#else
+# define FTS5_NOINLINE
+#endif
+
+/*
+** Write a 64-bit variable-length integer to memory starting at p[0].
+** The length of data write will be between 1 and 9 bytes. The number
+** of bytes written is returned.
+**
+** A variable-length integer consists of the lower 7 bits of each byte
+** for all bytes that have the 8th bit set and one byte with the 8th
+** bit clear. Except, if we get to the 9th byte, it stores the full
+** 8 bits and is the last byte.
+*/
+static int FTS5_NOINLINE fts5PutVarint64(unsigned char *p, u64 v){
+ int i, j, n;
+ u8 buf[10];
+ if( v & (((u64)0xff000000)<<32) ){
+ p[8] = (u8)v;
+ v >>= 8;
+ for(i=7; i>=0; i--){
+ p[i] = (u8)((v & 0x7f) | 0x80);
+ v >>= 7;
+ }
+ return 9;
+ }
+ n = 0;
+ do{
+ buf[n++] = (u8)((v & 0x7f) | 0x80);
+ v >>= 7;
+ }while( v!=0 );
+ buf[0] &= 0x7f;
+ assert( n<=9 );
+ for(i=0, j=n-1; j>=0; j--, i++){
+ p[i] = buf[j];
+ }
+ return n;
+}
+
+static int sqlite3Fts5PutVarint(unsigned char *p, u64 v){
+ if( v<=0x7f ){
+ p[0] = v&0x7f;
+ return 1;
+ }
+ if( v<=0x3fff ){
+ p[0] = ((v>>7)&0x7f)|0x80;
+ p[1] = v&0x7f;
+ return 2;
+ }
+ return fts5PutVarint64(p,v);
+}
+
+
+static int sqlite3Fts5GetVarintLen(u32 iVal){
+#if 0
+ if( iVal<(1 << 7 ) ) return 1;
+#endif
+ assert( iVal>=(1 << 7) );
+ if( iVal<(1 << 14) ) return 2;
+ if( iVal<(1 << 21) ) return 3;
+ if( iVal<(1 << 28) ) return 4;
+ return 5;
+}
+
+
+/*
+** 2015 May 08
+**
+** 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 is an SQLite virtual table module implementing direct access to an
+** existing FTS5 index. The module may create several different types of
+** tables:
+**
+** col:
+** CREATE TABLE vocab(term, col, doc, cnt, PRIMARY KEY(term, col));
+**
+** One row for each term/column combination. The value of $doc is set to
+** the number of fts5 rows that contain at least one instance of term
+** $term within column $col. Field $cnt is set to the total number of
+** instances of term $term in column $col (in any row of the fts5 table).
+**
+** row:
+** CREATE TABLE vocab(term, doc, cnt, PRIMARY KEY(term));
+**
+** One row for each term in the database. The value of $doc is set to
+** the number of fts5 rows that contain at least one instance of term
+** $term. Field $cnt is set to the total number of instances of term
+** $term in the database.
+*/
+
+
+/* #include "fts5Int.h" */
+
+
+typedef struct Fts5VocabTable Fts5VocabTable;
+typedef struct Fts5VocabCursor Fts5VocabCursor;
+
+struct Fts5VocabTable {
+ sqlite3_vtab base;
+ char *zFts5Tbl; /* Name of fts5 table */
+ char *zFts5Db; /* Db containing fts5 table */
+ sqlite3 *db; /* Database handle */
+ Fts5Global *pGlobal; /* FTS5 global object for this database */
+ int eType; /* FTS5_VOCAB_COL or ROW */
+};
+
+struct Fts5VocabCursor {
+ sqlite3_vtab_cursor base;
+ sqlite3_stmt *pStmt; /* Statement holding lock on pIndex */
+ Fts5Index *pIndex; /* Associated FTS5 index */
+
+ int bEof; /* True if this cursor is at EOF */
+ Fts5IndexIter *pIter; /* Term/rowid iterator object */
+
+ int nLeTerm; /* Size of zLeTerm in bytes */
+ char *zLeTerm; /* (term <= $zLeTerm) paramater, or NULL */
+
+ /* These are used by 'col' tables only */
+ Fts5Config *pConfig; /* Fts5 table configuration */
+ int iCol;
+ i64 *aCnt;
+ i64 *aDoc;
+
+ /* Output values used by 'row' and 'col' tables */
+ i64 rowid; /* This table's current rowid value */
+ Fts5Buffer term; /* Current value of 'term' column */
+};
+
+#define FTS5_VOCAB_COL 0
+#define FTS5_VOCAB_ROW 1
+
+#define FTS5_VOCAB_COL_SCHEMA "term, col, doc, cnt"
+#define FTS5_VOCAB_ROW_SCHEMA "term, doc, cnt"
+
+/*
+** Bits for the mask used as the idxNum value by xBestIndex/xFilter.
+*/
+#define FTS5_VOCAB_TERM_EQ 0x01
+#define FTS5_VOCAB_TERM_GE 0x02
+#define FTS5_VOCAB_TERM_LE 0x04
+
+
+/*
+** Translate a string containing an fts5vocab table type to an
+** FTS5_VOCAB_XXX constant. If successful, set *peType to the output
+** value and return SQLITE_OK. Otherwise, set *pzErr to an error message
+** and return SQLITE_ERROR.
+*/
+static int fts5VocabTableType(const char *zType, char **pzErr, int *peType){
+ int rc = SQLITE_OK;
+ char *zCopy = sqlite3Fts5Strndup(&rc, zType, -1);
+ if( rc==SQLITE_OK ){
+ sqlite3Fts5Dequote(zCopy);
+ if( sqlite3_stricmp(zCopy, "col")==0 ){
+ *peType = FTS5_VOCAB_COL;
+ }else
+
+ if( sqlite3_stricmp(zCopy, "row")==0 ){
+ *peType = FTS5_VOCAB_ROW;
+ }else
+ {
+ *pzErr = sqlite3_mprintf("fts5vocab: unknown table type: %Q", zCopy);
+ rc = SQLITE_ERROR;
+ }
+ sqlite3_free(zCopy);
+ }
+
+ return rc;
+}
+
+
+/*
+** The xDisconnect() virtual table method.
+*/
+static int fts5VocabDisconnectMethod(sqlite3_vtab *pVtab){
+ Fts5VocabTable *pTab = (Fts5VocabTable*)pVtab;
+ sqlite3_free(pTab);
+ return SQLITE_OK;
+}
+
+/*
+** The xDestroy() virtual table method.
+*/
+static int fts5VocabDestroyMethod(sqlite3_vtab *pVtab){
+ Fts5VocabTable *pTab = (Fts5VocabTable*)pVtab;
+ sqlite3_free(pTab);
+ return SQLITE_OK;
+}
+
+/*
+** This function is the implementation of both the xConnect and xCreate
+** methods of the FTS3 virtual table.
+**
+** The argv[] array contains the following:
+**
+** argv[0] -> module name ("fts5vocab")
+** argv[1] -> database name
+** argv[2] -> table name
+**
+** then:
+**
+** argv[3] -> name of fts5 table
+** argv[4] -> type of fts5vocab table
+**
+** or, for tables in the TEMP schema only.
+**
+** argv[3] -> name of fts5 tables database
+** argv[4] -> name of fts5 table
+** argv[5] -> type of fts5vocab table
+*/
+static int fts5VocabInitVtab(
+ sqlite3 *db, /* The SQLite database connection */
+ void *pAux, /* Pointer to Fts5Global object */
+ int argc, /* Number of elements in argv array */
+ const char * const *argv, /* xCreate/xConnect argument array */
+ sqlite3_vtab **ppVTab, /* Write the resulting vtab structure here */
+ char **pzErr /* Write any error message here */
+){
+ const char *azSchema[] = {
+ "CREATE TABlE vocab(" FTS5_VOCAB_COL_SCHEMA ")",
+ "CREATE TABlE vocab(" FTS5_VOCAB_ROW_SCHEMA ")"
+ };
+
+ Fts5VocabTable *pRet = 0;
+ int rc = SQLITE_OK; /* Return code */
+ int bDb;
+
+ bDb = (argc==6 && strlen(argv[1])==4 && memcmp("temp", argv[1], 4)==0);
+
+ if( argc!=5 && bDb==0 ){
+ *pzErr = sqlite3_mprintf("wrong number of vtable arguments");
+ rc = SQLITE_ERROR;
+ }else{
+ int nByte; /* Bytes of space to allocate */
+ const char *zDb = bDb ? argv[3] : argv[1];
+ const char *zTab = bDb ? argv[4] : argv[3];
+ const char *zType = bDb ? argv[5] : argv[4];
+ int nDb = (int)strlen(zDb)+1;
+ int nTab = (int)strlen(zTab)+1;
+ int eType = 0;
+
+ rc = fts5VocabTableType(zType, pzErr, &eType);
+ if( rc==SQLITE_OK ){
+ assert( eType>=0 && eType<ArraySize(azSchema) );
+ rc = sqlite3_declare_vtab(db, azSchema[eType]);
+ }
+
+ nByte = sizeof(Fts5VocabTable) + nDb + nTab;
+ pRet = sqlite3Fts5MallocZero(&rc, nByte);
+ if( pRet ){
+ pRet->pGlobal = (Fts5Global*)pAux;
+ pRet->eType = eType;
+ pRet->db = db;
+ pRet->zFts5Tbl = (char*)&pRet[1];
+ pRet->zFts5Db = &pRet->zFts5Tbl[nTab];
+ memcpy(pRet->zFts5Tbl, zTab, nTab);
+ memcpy(pRet->zFts5Db, zDb, nDb);
+ sqlite3Fts5Dequote(pRet->zFts5Tbl);
+ sqlite3Fts5Dequote(pRet->zFts5Db);
+ }
+ }
+
+ *ppVTab = (sqlite3_vtab*)pRet;
+ return rc;
+}
+
+
+/*
+** The xConnect() and xCreate() methods for the virtual table. All the
+** work is done in function fts5VocabInitVtab().
+*/
+static int fts5VocabConnectMethod(
+ sqlite3 *db, /* Database connection */
+ void *pAux, /* Pointer to tokenizer hash table */
+ int argc, /* Number of elements in argv array */
+ const char * const *argv, /* xCreate/xConnect argument array */
+ sqlite3_vtab **ppVtab, /* OUT: New sqlite3_vtab object */
+ char **pzErr /* OUT: sqlite3_malloc'd error message */
+){
+ return fts5VocabInitVtab(db, pAux, argc, argv, ppVtab, pzErr);
+}
+static int fts5VocabCreateMethod(
+ sqlite3 *db, /* Database connection */
+ void *pAux, /* Pointer to tokenizer hash table */
+ int argc, /* Number of elements in argv array */
+ const char * const *argv, /* xCreate/xConnect argument array */
+ sqlite3_vtab **ppVtab, /* OUT: New sqlite3_vtab object */
+ char **pzErr /* OUT: sqlite3_malloc'd error message */
+){
+ return fts5VocabInitVtab(db, pAux, argc, argv, ppVtab, pzErr);
+}
+
+/*
+** Implementation of the xBestIndex method.
+*/
+static int fts5VocabBestIndexMethod(
+ sqlite3_vtab *pUnused,
+ sqlite3_index_info *pInfo
+){
+ int i;
+ int iTermEq = -1;
+ int iTermGe = -1;
+ int iTermLe = -1;
+ int idxNum = 0;
+ int nArg = 0;
+
+ UNUSED_PARAM(pUnused);
+
+ for(i=0; i<pInfo->nConstraint; i++){
+ struct sqlite3_index_constraint *p = &pInfo->aConstraint[i];
+ if( p->usable==0 ) continue;
+ if( p->iColumn==0 ){ /* term column */
+ if( p->op==SQLITE_INDEX_CONSTRAINT_EQ ) iTermEq = i;
+ if( p->op==SQLITE_INDEX_CONSTRAINT_LE ) iTermLe = i;
+ if( p->op==SQLITE_INDEX_CONSTRAINT_LT ) iTermLe = i;
+ if( p->op==SQLITE_INDEX_CONSTRAINT_GE ) iTermGe = i;
+ if( p->op==SQLITE_INDEX_CONSTRAINT_GT ) iTermGe = i;
+ }
+ }
+
+ if( iTermEq>=0 ){
+ idxNum |= FTS5_VOCAB_TERM_EQ;
+ pInfo->aConstraintUsage[iTermEq].argvIndex = ++nArg;
+ pInfo->estimatedCost = 100;
+ }else{
+ pInfo->estimatedCost = 1000000;
+ if( iTermGe>=0 ){
+ idxNum |= FTS5_VOCAB_TERM_GE;
+ pInfo->aConstraintUsage[iTermGe].argvIndex = ++nArg;
+ pInfo->estimatedCost = pInfo->estimatedCost / 2;
+ }
+ if( iTermLe>=0 ){
+ idxNum |= FTS5_VOCAB_TERM_LE;
+ pInfo->aConstraintUsage[iTermLe].argvIndex = ++nArg;
+ pInfo->estimatedCost = pInfo->estimatedCost / 2;
+ }
+ }
+
+ pInfo->idxNum = idxNum;
+
+ return SQLITE_OK;
+}
+
+/*
+** Implementation of xOpen method.
+*/
+static int fts5VocabOpenMethod(
+ sqlite3_vtab *pVTab,
+ sqlite3_vtab_cursor **ppCsr
+){
+ Fts5VocabTable *pTab = (Fts5VocabTable*)pVTab;
+ Fts5Index *pIndex = 0;
+ Fts5Config *pConfig = 0;
+ Fts5VocabCursor *pCsr = 0;
+ int rc = SQLITE_OK;
+ sqlite3_stmt *pStmt = 0;
+ char *zSql = 0;
+
+ zSql = sqlite3Fts5Mprintf(&rc,
+ "SELECT t.%Q FROM %Q.%Q AS t WHERE t.%Q MATCH '*id'",
+ pTab->zFts5Tbl, pTab->zFts5Db, pTab->zFts5Tbl, pTab->zFts5Tbl
+ );
+ if( zSql ){
+ rc = sqlite3_prepare_v2(pTab->db, zSql, -1, &pStmt, 0);
+ }
+ sqlite3_free(zSql);
+ assert( rc==SQLITE_OK || pStmt==0 );
+ if( rc==SQLITE_ERROR ) rc = SQLITE_OK;
+
+ if( pStmt && sqlite3_step(pStmt)==SQLITE_ROW ){
+ i64 iId = sqlite3_column_int64(pStmt, 0);
+ pIndex = sqlite3Fts5IndexFromCsrid(pTab->pGlobal, iId, &pConfig);
+ }
+
+ if( rc==SQLITE_OK && pIndex==0 ){
+ rc = sqlite3_finalize(pStmt);
+ pStmt = 0;
+ if( rc==SQLITE_OK ){
+ pVTab->zErrMsg = sqlite3_mprintf(
+ "no such fts5 table: %s.%s", pTab->zFts5Db, pTab->zFts5Tbl
+ );
+ rc = SQLITE_ERROR;
+ }
+ }
+
+ if( rc==SQLITE_OK ){
+ int nByte = pConfig->nCol * sizeof(i64) * 2 + sizeof(Fts5VocabCursor);
+ pCsr = (Fts5VocabCursor*)sqlite3Fts5MallocZero(&rc, nByte);
+ }
+
+ if( pCsr ){
+ pCsr->pIndex = pIndex;
+ pCsr->pStmt = pStmt;
+ pCsr->pConfig = pConfig;
+ pCsr->aCnt = (i64*)&pCsr[1];
+ pCsr->aDoc = &pCsr->aCnt[pConfig->nCol];
+ }else{
+ sqlite3_finalize(pStmt);
+ }
+
+ *ppCsr = (sqlite3_vtab_cursor*)pCsr;
+ return rc;
+}
+
+static void fts5VocabResetCursor(Fts5VocabCursor *pCsr){
+ pCsr->rowid = 0;
+ sqlite3Fts5IterClose(pCsr->pIter);
+ pCsr->pIter = 0;
+ sqlite3_free(pCsr->zLeTerm);
+ pCsr->nLeTerm = -1;
+ pCsr->zLeTerm = 0;
+}
+
+/*
+** Close the cursor. For additional information see the documentation
+** on the xClose method of the virtual table interface.
+*/
+static int fts5VocabCloseMethod(sqlite3_vtab_cursor *pCursor){
+ Fts5VocabCursor *pCsr = (Fts5VocabCursor*)pCursor;
+ fts5VocabResetCursor(pCsr);
+ sqlite3Fts5BufferFree(&pCsr->term);
+ sqlite3_finalize(pCsr->pStmt);
+ sqlite3_free(pCsr);
+ return SQLITE_OK;
+}
+
+
+/*
+** Advance the cursor to the next row in the table.
+*/
+static int fts5VocabNextMethod(sqlite3_vtab_cursor *pCursor){
+ Fts5VocabCursor *pCsr = (Fts5VocabCursor*)pCursor;
+ Fts5VocabTable *pTab = (Fts5VocabTable*)pCursor->pVtab;
+ int rc = SQLITE_OK;
+ int nCol = pCsr->pConfig->nCol;
+
+ pCsr->rowid++;
+
+ if( pTab->eType==FTS5_VOCAB_COL ){
+ for(pCsr->iCol++; pCsr->iCol<nCol; pCsr->iCol++){
+ if( pCsr->aDoc[pCsr->iCol] ) break;
+ }
+ }
+
+ if( pTab->eType==FTS5_VOCAB_ROW || pCsr->iCol>=nCol ){
+ if( sqlite3Fts5IterEof(pCsr->pIter) ){
+ pCsr->bEof = 1;
+ }else{
+ const char *zTerm;
+ int nTerm;
+
+ zTerm = sqlite3Fts5IterTerm(pCsr->pIter, &nTerm);
+ if( pCsr->nLeTerm>=0 ){
+ int nCmp = MIN(nTerm, pCsr->nLeTerm);
+ int bCmp = memcmp(pCsr->zLeTerm, zTerm, nCmp);
+ if( bCmp<0 || (bCmp==0 && pCsr->nLeTerm<nTerm) ){
+ pCsr->bEof = 1;
+ return SQLITE_OK;
+ }
+ }
+
+ sqlite3Fts5BufferSet(&rc, &pCsr->term, nTerm, (const u8*)zTerm);
+ memset(pCsr->aCnt, 0, nCol * sizeof(i64));
+ memset(pCsr->aDoc, 0, nCol * sizeof(i64));
+ pCsr->iCol = 0;
+
+ assert( pTab->eType==FTS5_VOCAB_COL || pTab->eType==FTS5_VOCAB_ROW );
+ while( rc==SQLITE_OK ){
+ const u8 *pPos; int nPos; /* Position list */
+ i64 iPos = 0; /* 64-bit position read from poslist */
+ int iOff = 0; /* Current offset within position list */
+
+ pPos = pCsr->pIter->pData;
+ nPos = pCsr->pIter->nData;
+ switch( pCsr->pConfig->eDetail ){
+ case FTS5_DETAIL_FULL:
+ pPos = pCsr->pIter->pData;
+ nPos = pCsr->pIter->nData;
+ if( pTab->eType==FTS5_VOCAB_ROW ){
+ while( 0==sqlite3Fts5PoslistNext64(pPos, nPos, &iOff, &iPos) ){
+ pCsr->aCnt[0]++;
+ }
+ pCsr->aDoc[0]++;
+ }else{
+ int iCol = -1;
+ while( 0==sqlite3Fts5PoslistNext64(pPos, nPos, &iOff, &iPos) ){
+ int ii = FTS5_POS2COLUMN(iPos);
+ pCsr->aCnt[ii]++;
+ if( iCol!=ii ){
+ if( ii>=nCol ){
+ rc = FTS5_CORRUPT;
+ break;
+ }
+ pCsr->aDoc[ii]++;
+ iCol = ii;
+ }
+ }
+ }
+ break;
+
+ case FTS5_DETAIL_COLUMNS:
+ if( pTab->eType==FTS5_VOCAB_ROW ){
+ pCsr->aDoc[0]++;
+ }else{
+ while( 0==sqlite3Fts5PoslistNext64(pPos, nPos, &iOff,&iPos) ){
+ assert_nc( iPos>=0 && iPos<nCol );
+ if( iPos>=nCol ){
+ rc = FTS5_CORRUPT;
+ break;
+ }
+ pCsr->aDoc[iPos]++;
+ }
+ }
+ break;
+
+ default:
+ assert( pCsr->pConfig->eDetail==FTS5_DETAIL_NONE );
+ pCsr->aDoc[0]++;
+ break;
+ }
+
+ if( rc==SQLITE_OK ){
+ rc = sqlite3Fts5IterNextScan(pCsr->pIter);
+ }
+
+ if( rc==SQLITE_OK ){
+ zTerm = sqlite3Fts5IterTerm(pCsr->pIter, &nTerm);
+ if( nTerm!=pCsr->term.n || memcmp(zTerm, pCsr->term.p, nTerm) ){
+ break;
+ }
+ if( sqlite3Fts5IterEof(pCsr->pIter) ) break;
+ }
+ }
+ }
+ }
+
+ if( rc==SQLITE_OK && pCsr->bEof==0 && pTab->eType==FTS5_VOCAB_COL ){
+ while( pCsr->aDoc[pCsr->iCol]==0 ) pCsr->iCol++;
+ assert( pCsr->iCol<pCsr->pConfig->nCol );
+ }
+ return rc;
+}
+
+/*
+** This is the xFilter implementation for the virtual table.
+*/
+static int fts5VocabFilterMethod(
+ sqlite3_vtab_cursor *pCursor, /* The cursor used for this query */
+ int idxNum, /* Strategy index */
+ const char *zUnused, /* Unused */
+ int nUnused, /* Number of elements in apVal */
+ sqlite3_value **apVal /* Arguments for the indexing scheme */
+){
+ Fts5VocabCursor *pCsr = (Fts5VocabCursor*)pCursor;
+ int rc = SQLITE_OK;
+
+ int iVal = 0;
+ int f = FTS5INDEX_QUERY_SCAN;
+ const char *zTerm = 0;
+ int nTerm = 0;
+
+ sqlite3_value *pEq = 0;
+ sqlite3_value *pGe = 0;
+ sqlite3_value *pLe = 0;
+
+ UNUSED_PARAM2(zUnused, nUnused);
+
+ fts5VocabResetCursor(pCsr);
+ if( idxNum & FTS5_VOCAB_TERM_EQ ) pEq = apVal[iVal++];
+ if( idxNum & FTS5_VOCAB_TERM_GE ) pGe = apVal[iVal++];
+ if( idxNum & FTS5_VOCAB_TERM_LE ) pLe = apVal[iVal++];
+
+ if( pEq ){
+ zTerm = (const char *)sqlite3_value_text(pEq);
+ nTerm = sqlite3_value_bytes(pEq);
+ f = 0;
+ }else{
+ if( pGe ){
+ zTerm = (const char *)sqlite3_value_text(pGe);
+ nTerm = sqlite3_value_bytes(pGe);
+ }
+ if( pLe ){
+ const char *zCopy = (const char *)sqlite3_value_text(pLe);
+ pCsr->nLeTerm = sqlite3_value_bytes(pLe);
+ pCsr->zLeTerm = sqlite3_malloc(pCsr->nLeTerm+1);
+ if( pCsr->zLeTerm==0 ){
+ rc = SQLITE_NOMEM;
+ }else{
+ memcpy(pCsr->zLeTerm, zCopy, pCsr->nLeTerm+1);
+ }
+ }
+ }
+
+
+ if( rc==SQLITE_OK ){
+ rc = sqlite3Fts5IndexQuery(pCsr->pIndex, zTerm, nTerm, f, 0, &pCsr->pIter);
+ }
+ if( rc==SQLITE_OK ){
+ rc = fts5VocabNextMethod(pCursor);
+ }
+
+ return rc;
+}
+
+/*
+** This is the xEof method of the virtual table. SQLite calls this
+** routine to find out if it has reached the end of a result set.
+*/
+static int fts5VocabEofMethod(sqlite3_vtab_cursor *pCursor){
+ Fts5VocabCursor *pCsr = (Fts5VocabCursor*)pCursor;
+ return pCsr->bEof;
+}
+
+static int fts5VocabColumnMethod(
+ sqlite3_vtab_cursor *pCursor, /* Cursor to retrieve value from */
+ sqlite3_context *pCtx, /* Context for sqlite3_result_xxx() calls */
+ int iCol /* Index of column to read value from */
+){
+ Fts5VocabCursor *pCsr = (Fts5VocabCursor*)pCursor;
+ int eDetail = pCsr->pConfig->eDetail;
+ int eType = ((Fts5VocabTable*)(pCursor->pVtab))->eType;
+ i64 iVal = 0;
+
+ if( iCol==0 ){
+ sqlite3_result_text(
+ pCtx, (const char*)pCsr->term.p, pCsr->term.n, SQLITE_TRANSIENT
+ );
+ }else if( eType==FTS5_VOCAB_COL ){
+ assert( iCol==1 || iCol==2 || iCol==3 );
+ if( iCol==1 ){
+ if( eDetail!=FTS5_DETAIL_NONE ){
+ const char *z = pCsr->pConfig->azCol[pCsr->iCol];
+ sqlite3_result_text(pCtx, z, -1, SQLITE_STATIC);
+ }
+ }else if( iCol==2 ){
+ iVal = pCsr->aDoc[pCsr->iCol];
+ }else{
+ iVal = pCsr->aCnt[pCsr->iCol];
+ }
+ }else{
+ assert( iCol==1 || iCol==2 );
+ if( iCol==1 ){
+ iVal = pCsr->aDoc[0];
+ }else{
+ iVal = pCsr->aCnt[0];
+ }
+ }
+
+ if( iVal>0 ) sqlite3_result_int64(pCtx, iVal);
+ return SQLITE_OK;
+}
+
+/*
+** This is the xRowid method. The SQLite core calls this routine to
+** retrieve the rowid for the current row of the result set. The
+** rowid should be written to *pRowid.
+*/
+static int fts5VocabRowidMethod(
+ sqlite3_vtab_cursor *pCursor,
+ sqlite_int64 *pRowid
+){
+ Fts5VocabCursor *pCsr = (Fts5VocabCursor*)pCursor;
+ *pRowid = pCsr->rowid;
+ return SQLITE_OK;
+}
+
+static int sqlite3Fts5VocabInit(Fts5Global *pGlobal, sqlite3 *db){
+ static const sqlite3_module fts5Vocab = {
+ /* iVersion */ 2,
+ /* xCreate */ fts5VocabCreateMethod,
+ /* xConnect */ fts5VocabConnectMethod,
+ /* xBestIndex */ fts5VocabBestIndexMethod,
+ /* xDisconnect */ fts5VocabDisconnectMethod,
+ /* xDestroy */ fts5VocabDestroyMethod,
+ /* xOpen */ fts5VocabOpenMethod,
+ /* xClose */ fts5VocabCloseMethod,
+ /* xFilter */ fts5VocabFilterMethod,
+ /* xNext */ fts5VocabNextMethod,
+ /* xEof */ fts5VocabEofMethod,
+ /* xColumn */ fts5VocabColumnMethod,
+ /* xRowid */ fts5VocabRowidMethod,
+ /* xUpdate */ 0,
+ /* xBegin */ 0,
+ /* xSync */ 0,
+ /* xCommit */ 0,
+ /* xRollback */ 0,
+ /* xFindFunction */ 0,
+ /* xRename */ 0,
+ /* xSavepoint */ 0,
+ /* xRelease */ 0,
+ /* xRollbackTo */ 0,
+ };
+ void *p = (void*)pGlobal;
+
+ return sqlite3_create_module_v2(db, "fts5vocab", &fts5Vocab, p, 0);
+}
+
+
+
+
+
+#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS5) */
+
+/************** End of fts5.c ************************************************/
diff --git a/ext/sqlite3/libsqlite/sqlite3.h b/ext/sqlite3/libsqlite/sqlite3.h
index d43b63c107..7cca0accfa 100644
--- a/ext/sqlite3/libsqlite/sqlite3.h
+++ b/ext/sqlite3/libsqlite/sqlite3.h
@@ -23,7 +23,7 @@
**
** The official C-language API documentation for SQLite is derived
** from comments in this file. This file is the authoritative source
-** on how SQLite interfaces are suppose to operate.
+** on how SQLite interfaces are supposed to operate.
**
** The name of this file under configuration management is "sqlite.h.in".
** The makefile makes some minor changes to this file (such as inserting
@@ -111,9 +111,9 @@ extern "C" {
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
** [sqlite_version()] and [sqlite_source_id()].
*/
-#define SQLITE_VERSION "3.8.10.2"
-#define SQLITE_VERSION_NUMBER 3008010
-#define SQLITE_SOURCE_ID "2015-05-20 18:17:19 2ef4f3a5b1d1d0c4338f8243d40a2452cc1f7fe4"
+#define SQLITE_VERSION "3.9.2"
+#define SQLITE_VERSION_NUMBER 3009002
+#define SQLITE_SOURCE_ID "2015-11-02 18:31:45 bda77dda9697c463c3d0704014d51627fceee328"
/*
** CAPI3REF: Run-Time Library Version Numbers
@@ -124,7 +124,7 @@ extern "C" {
** but are associated with the library instead of the header file. ^(Cautious
** programmers might include assert() statements in their application to
** verify that values returned by these interfaces match the macros in
-** the header, and thus insure that the application is
+** the header, and thus ensure that the application is
** compiled with matching library and header files.
**
** <blockquote><pre>
@@ -374,7 +374,7 @@ typedef int (*sqlite3_callback)(void*,int,char**, char**);
** Restrictions:
**
** <ul>
-** <li> The application must insure that the 1st parameter to sqlite3_exec()
+** <li> The application must ensure that the 1st parameter to sqlite3_exec()
** is a valid and open [database connection].
** <li> The application must not close the [database connection] specified by
** the 1st parameter to sqlite3_exec() while sqlite3_exec() is running.
@@ -477,6 +477,7 @@ SQLITE_API int SQLITE_STDCALL sqlite3_exec(
#define SQLITE_IOERR_MMAP (SQLITE_IOERR | (24<<8))
#define SQLITE_IOERR_GETTEMPPATH (SQLITE_IOERR | (25<<8))
#define SQLITE_IOERR_CONVPATH (SQLITE_IOERR | (26<<8))
+#define SQLITE_IOERR_VNODE (SQLITE_IOERR | (27<<8))
#define SQLITE_LOCKED_SHAREDCACHE (SQLITE_LOCKED | (1<<8))
#define SQLITE_BUSY_RECOVERY (SQLITE_BUSY | (1<<8))
#define SQLITE_BUSY_SNAPSHOT (SQLITE_BUSY | (2<<8))
@@ -963,6 +964,14 @@ struct sqlite3_io_methods {
** circumstances in order to fix a problem with priority inversion.
** Applications should <em>not</em> use this file-control.
**
+** <li>[[SQLITE_FCNTL_ZIPVFS]]
+** The [SQLITE_FCNTL_ZIPVFS] opcode is implemented by zipvfs only. All other
+** VFS should return SQLITE_NOTFOUND for this opcode.
+**
+** <li>[[SQLITE_FCNTL_RBU]]
+** The [SQLITE_FCNTL_RBU] opcode is implemented by the special VFS used by
+** the RBU extension only. All other VFS should return SQLITE_NOTFOUND for
+** this opcode.
** </ul>
*/
#define SQLITE_FCNTL_LOCKSTATE 1
@@ -988,6 +997,8 @@ struct sqlite3_io_methods {
#define SQLITE_FCNTL_COMMIT_PHASETWO 22
#define SQLITE_FCNTL_WIN32_SET_HANDLE 23
#define SQLITE_FCNTL_WAL_BLOCK 24
+#define SQLITE_FCNTL_ZIPVFS 25
+#define SQLITE_FCNTL_RBU 26
/* deprecated names */
#define SQLITE_GET_LOCKPROXYFILE SQLITE_FCNTL_GET_LOCKPROXYFILE
@@ -1356,9 +1367,11 @@ SQLITE_API int SQLITE_STDCALL sqlite3_os_end(void);
** applications and so this routine is usually not necessary. It is
** provided to support rare applications with unusual needs.
**
-** The sqlite3_config() interface is not threadsafe. The application
-** must insure that no other SQLite interfaces are invoked by other
-** threads while sqlite3_config() is running. Furthermore, sqlite3_config()
+** <b>The sqlite3_config() interface is not threadsafe. The application
+** must ensure that no other SQLite interfaces are invoked by other
+** threads while sqlite3_config() is running.</b>
+**
+** The sqlite3_config() interface
** may only be invoked prior to library initialization using
** [sqlite3_initialize()] or after shutdown by [sqlite3_shutdown()].
** ^If sqlite3_config() is called after [sqlite3_initialize()] and before
@@ -3363,7 +3376,8 @@ SQLITE_API int SQLITE_STDCALL sqlite3_stmt_readonly(sqlite3_stmt *pStmt);
**
** ^The sqlite3_stmt_busy(S) interface returns true (non-zero) if the
** [prepared statement] S has been stepped at least once using
-** [sqlite3_step(S)] but has not run to completion and/or has not
+** [sqlite3_step(S)] but has neither run to completion (returned
+** [SQLITE_DONE] from [sqlite3_step(S)]) nor
** been reset using [sqlite3_reset(S)]. ^The sqlite3_stmt_busy(S)
** interface returns false if S is a NULL pointer. If S is not a
** NULL pointer and is not a pointer to a valid [prepared statement]
@@ -3390,7 +3404,9 @@ SQLITE_API int SQLITE_STDCALL sqlite3_stmt_busy(sqlite3_stmt*);
** Some interfaces require a protected sqlite3_value. Other interfaces
** will accept either a protected or an unprotected sqlite3_value.
** Every interface that accepts sqlite3_value arguments specifies
-** whether or not it requires a protected sqlite3_value.
+** whether or not it requires a protected sqlite3_value. The
+** [sqlite3_value_dup()] interface can be used to construct a new
+** protected sqlite3_value from an unprotected sqlite3_value.
**
** The terms "protected" and "unprotected" refer to whether or not
** a mutex is held. An internal mutex is held for a protected
@@ -3550,6 +3566,7 @@ SQLITE_API int SQLITE_STDCALL sqlite3_bind_text64(sqlite3_stmt*, int, const char
void(*)(void*), unsigned char encoding);
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);
+SQLITE_API int SQLITE_STDCALL sqlite3_bind_zeroblob64(sqlite3_stmt*, int, sqlite3_uint64);
/*
** CAPI3REF: Number Of SQL Parameters
@@ -3613,7 +3630,7 @@ SQLITE_API const char *SQLITE_STDCALL sqlite3_bind_parameter_name(sqlite3_stmt*,
**
** See also: [sqlite3_bind_blob|sqlite3_bind()],
** [sqlite3_bind_parameter_count()], and
-** [sqlite3_bind_parameter_index()].
+** [sqlite3_bind_parameter_name()].
*/
SQLITE_API int SQLITE_STDCALL sqlite3_bind_parameter_index(sqlite3_stmt*, const char *zName);
@@ -3893,8 +3910,6 @@ SQLITE_API int SQLITE_STDCALL sqlite3_data_count(sqlite3_stmt *pStmt);
** KEYWORDS: {column access functions}
** METHOD: sqlite3_stmt
**
-** These routines form the "result set" interface.
-**
** ^These routines return information about a single column of the current
** result row of a query. ^In every case the first argument is a pointer
** to the [prepared statement] that is being evaluated (the [sqlite3_stmt*]
@@ -3954,13 +3969,14 @@ SQLITE_API int SQLITE_STDCALL sqlite3_data_count(sqlite3_stmt *pStmt);
** even empty strings, are always zero-terminated. ^The return
** value from sqlite3_column_blob() for a zero-length BLOB is a NULL pointer.
**
-** ^The object returned by [sqlite3_column_value()] is an
-** [unprotected sqlite3_value] object. An unprotected sqlite3_value object
-** may only be used with [sqlite3_bind_value()] and [sqlite3_result_value()].
+** <b>Warning:</b> ^The object returned by [sqlite3_column_value()] is an
+** [unprotected sqlite3_value] object. In a multithreaded environment,
+** an unprotected sqlite3_value object may only be used safely with
+** [sqlite3_bind_value()] and [sqlite3_result_value()].
** If the [unprotected sqlite3_value] object returned by
** [sqlite3_column_value()] is used in any other way, including calls
** to routines like [sqlite3_value_int()], [sqlite3_value_text()],
-** or [sqlite3_value_bytes()], then the behavior is undefined.
+** or [sqlite3_value_bytes()], the behavior is not threadsafe.
**
** These routines attempt to convert the value where appropriate. ^For
** example, if the internal representation is FLOAT and a text result
@@ -3991,12 +4007,6 @@ SQLITE_API int SQLITE_STDCALL sqlite3_data_count(sqlite3_stmt *pStmt);
** </table>
** </blockquote>)^
**
-** The table above makes reference to standard C library functions atoi()
-** and atof(). SQLite does not really use these functions. It has its
-** own equivalent internal routines. The atoi() and atof() names are
-** used in the table for brevity and because they are familiar to most
-** C programmers.
-**
** Note that when type conversions occur, pointers returned by prior
** calls to sqlite3_column_blob(), sqlite3_column_text(), and/or
** sqlite3_column_text16() may be invalidated.
@@ -4021,7 +4031,7 @@ SQLITE_API int SQLITE_STDCALL sqlite3_data_count(sqlite3_stmt *pStmt);
** of conversion are done in place when it is possible, but sometimes they
** are not possible and in those cases prior pointers are invalidated.
**
-** The safest and easiest to remember policy is to invoke these routines
+** The safest policy is to invoke these routines
** in one of the following ways:
**
** <ul>
@@ -4041,7 +4051,7 @@ SQLITE_API int SQLITE_STDCALL sqlite3_data_count(sqlite3_stmt *pStmt);
** ^The pointers returned are valid until a type conversion occurs as
** described above, or until [sqlite3_step()] or [sqlite3_reset()] or
** [sqlite3_finalize()] is called. ^The memory space used to hold strings
-** and BLOBs is freed automatically. Do <b>not</b> pass the pointers returned
+** and BLOBs is freed automatically. Do <em>not</em> pass the pointers returned
** from [sqlite3_column_blob()], [sqlite3_column_text()], etc. into
** [sqlite3_free()].
**
@@ -4291,12 +4301,12 @@ SQLITE_API SQLITE_DEPRECATED int SQLITE_STDCALL sqlite3_memory_alarm(void(*)(voi
#endif
/*
-** CAPI3REF: Obtaining SQL Function Parameter Values
+** CAPI3REF: Obtaining SQL 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
-** the function or aggregate.
+** the function or aggregate.
**
** The xFunc (for scalar functions) or xStep (for aggregates) parameters
** to [sqlite3_create_function()] and [sqlite3_create_function16()]
@@ -4350,6 +4360,39 @@ SQLITE_API int SQLITE_STDCALL sqlite3_value_type(sqlite3_value*);
SQLITE_API int SQLITE_STDCALL sqlite3_value_numeric_type(sqlite3_value*);
/*
+** CAPI3REF: Finding The Subtype Of SQL Values
+** METHOD: sqlite3_value
+**
+** The sqlite3_value_subtype(V) function returns the subtype for
+** an [application-defined SQL function] argument V. The subtype
+** information can be used to pass a limited amount of context from
+** one SQL function to another. Use the [sqlite3_result_subtype()]
+** routine to set the subtype for the return value of an SQL function.
+**
+** SQLite makes no use of subtype itself. It merely passes the subtype
+** from the result of one [application-defined SQL function] into the
+** input of another.
+*/
+SQLITE_API unsigned int SQLITE_STDCALL sqlite3_value_subtype(sqlite3_value*);
+
+/*
+** CAPI3REF: Copy And Free SQL Values
+** METHOD: sqlite3_value
+**
+** ^The sqlite3_value_dup(V) interface makes a copy of the [sqlite3_value]
+** object D and returns a pointer to that copy. ^The [sqlite3_value] returned
+** is a [protected sqlite3_value] object even if the input is not.
+** ^The sqlite3_value_dup(V) interface returns NULL if V is NULL or if a
+** memory allocation fails.
+**
+** ^The sqlite3_value_free(V) interface frees an [sqlite3_value] object
+** previously obtained from [sqlite3_value_dup()]. ^If V is a NULL pointer
+** then sqlite3_value_free(V) is a harmless no-op.
+*/
+SQLITE_API SQLITE_EXPERIMENTAL sqlite3_value *SQLITE_STDCALL sqlite3_value_dup(const sqlite3_value*);
+SQLITE_API SQLITE_EXPERIMENTAL void SQLITE_STDCALL sqlite3_value_free(sqlite3_value*);
+
+/*
** CAPI3REF: Obtain Aggregate Function Context
** METHOD: sqlite3_context
**
@@ -4512,9 +4555,9 @@ typedef void (*sqlite3_destructor_type)(void*);
** to by the second parameter and which is N bytes long where N is the
** third parameter.
**
-** ^The sqlite3_result_zeroblob() interfaces set the result of
-** the application-defined function to be a BLOB containing all zero
-** bytes and N bytes in size, where N is the value of the 2nd parameter.
+** ^The sqlite3_result_zeroblob(C,N) and sqlite3_result_zeroblob64(C,N)
+** interfaces set the result of the application-defined function to be
+** a BLOB containing all zero bytes and N bytes in size.
**
** ^The sqlite3_result_double() interface sets the result from
** an application-defined function to be a floating point value specified
@@ -4596,7 +4639,7 @@ typedef void (*sqlite3_destructor_type)(void*);
** from [sqlite3_malloc()] before it returns.
**
** ^The sqlite3_result_value() interface sets the result of
-** the application-defined function to be a copy the
+** the application-defined function to be a copy of the
** [unprotected sqlite3_value] object specified by the 2nd parameter. ^The
** sqlite3_result_value() interface makes a copy of the [sqlite3_value]
** so that the [sqlite3_value] specified in the parameter may change or
@@ -4629,6 +4672,22 @@ SQLITE_API void SQLITE_STDCALL sqlite3_result_text16le(sqlite3_context*, const v
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);
+SQLITE_API int SQLITE_STDCALL sqlite3_result_zeroblob64(sqlite3_context*, sqlite3_uint64 n);
+
+
+/*
+** CAPI3REF: Setting The Subtype Of An SQL Function
+** METHOD: sqlite3_context
+**
+** The sqlite3_result_subtype(C,T) function causes the subtype of
+** the result from the [application-defined SQL function] with
+** [sqlite3_context] C to be the value T. Only the lower 8 bits
+** of the subtype T are preserved in current versions of SQLite;
+** higher order bits are discarded.
+** The number of subtype bytes preserved by SQLite might increase
+** in future releases of SQLite.
+*/
+SQLITE_API void SQLITE_STDCALL sqlite3_result_subtype(sqlite3_context*,unsigned int);
/*
** CAPI3REF: Define New Collating Sequences
@@ -5575,13 +5634,31 @@ struct sqlite3_module {
** ^The estimatedRows value is an estimate of the number of rows that
** will be returned by the strategy.
**
+** The xBestIndex method may optionally populate the idxFlags field with a
+** mask of SQLITE_INDEX_SCAN_* flags. Currently there is only one such flag -
+** SQLITE_INDEX_SCAN_UNIQUE. If the xBestIndex method sets this flag, SQLite
+** assumes that the strategy may visit at most one row.
+**
+** Additionally, if xBestIndex sets the SQLITE_INDEX_SCAN_UNIQUE flag, then
+** SQLite also assumes that if a call to the xUpdate() method is made as
+** part of the same statement to delete or update a virtual table row and the
+** implementation returns SQLITE_CONSTRAINT, then there is no need to rollback
+** any database changes. In other words, if the xUpdate() returns
+** SQLITE_CONSTRAINT, the database contents must be exactly as they were
+** before xUpdate was called. By contrast, if SQLITE_INDEX_SCAN_UNIQUE is not
+** set and xUpdate returns SQLITE_CONSTRAINT, any database changes made by
+** the xUpdate method are automatically rolled back by SQLite.
+**
** IMPORTANT: The estimatedRows field was added to the sqlite3_index_info
** structure for SQLite version 3.8.2. If a virtual table extension is
** used with an SQLite version earlier than 3.8.2, the results of attempting
** to read or write the estimatedRows field are undefined (but are likely
** to included crashing the application). The estimatedRows field should
** therefore only be used if [sqlite3_libversion_number()] returns a
-** value greater than or equal to 3008002.
+** value greater than or equal to 3008002. Similarly, the idxFlags field
+** was added for version 3.9.0. It may therefore only be used if
+** sqlite3_libversion_number() returns a value greater than or equal to
+** 3009000.
*/
struct sqlite3_index_info {
/* Inputs */
@@ -5609,9 +5686,16 @@ struct sqlite3_index_info {
double estimatedCost; /* Estimated cost of using this index */
/* Fields below are only available in SQLite 3.8.2 and later */
sqlite3_int64 estimatedRows; /* Estimated number of rows returned */
+ /* Fields below are only available in SQLite 3.9.0 and later */
+ int idxFlags; /* Mask of SQLITE_INDEX_SCAN_* flags */
};
/*
+** CAPI3REF: Virtual Table Scan Flags
+*/
+#define SQLITE_INDEX_SCAN_UNIQUE 1 /* Scan visits at most 1 row */
+
+/*
** CAPI3REF: Virtual Table Constraint Operator Codes
**
** These macros defined the allowed values for the
@@ -5872,7 +5956,7 @@ SQLITE_API int SQLITE_STDCALL sqlite3_blob_open(
**
** ^This function sets the database handle error code and message.
*/
-SQLITE_API SQLITE_EXPERIMENTAL int SQLITE_STDCALL sqlite3_blob_reopen(sqlite3_blob *, sqlite3_int64);
+SQLITE_API int SQLITE_STDCALL sqlite3_blob_reopen(sqlite3_blob *, sqlite3_int64);
/*
** CAPI3REF: Close A BLOB Handle
@@ -6068,6 +6152,9 @@ SQLITE_API int SQLITE_STDCALL sqlite3_vfs_unregister(sqlite3_vfs*);
** <li> SQLITE_MUTEX_STATIC_APP1
** <li> SQLITE_MUTEX_STATIC_APP2
** <li> SQLITE_MUTEX_STATIC_APP3
+** <li> SQLITE_MUTEX_STATIC_VFS1
+** <li> SQLITE_MUTEX_STATIC_VFS2
+** <li> SQLITE_MUTEX_STATIC_VFS3
** </ul>
**
** ^The first two constants (SQLITE_MUTEX_FAST and SQLITE_MUTEX_RECURSIVE)
@@ -6269,6 +6356,9 @@ SQLITE_API int SQLITE_STDCALL sqlite3_mutex_notheld(sqlite3_mutex*);
#define SQLITE_MUTEX_STATIC_APP1 8 /* For use by application */
#define SQLITE_MUTEX_STATIC_APP2 9 /* For use by application */
#define SQLITE_MUTEX_STATIC_APP3 10 /* For use by application */
+#define SQLITE_MUTEX_STATIC_VFS1 11 /* For use by built-in VFS */
+#define SQLITE_MUTEX_STATIC_VFS2 12 /* For use by extension VFS */
+#define SQLITE_MUTEX_STATIC_VFS3 13 /* For use by application VFS */
/*
** CAPI3REF: Retrieve the mutex for a database connection
@@ -7682,7 +7772,7 @@ SQLITE_API int SQLITE_STDCALL sqlite3_vtab_on_conflict(sqlite3 *);
**
** See also: [sqlite3_stmt_scanstatus_reset()]
*/
-SQLITE_API SQLITE_EXPERIMENTAL int SQLITE_STDCALL sqlite3_stmt_scanstatus(
+SQLITE_API 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_* */
@@ -7698,7 +7788,7 @@ SQLITE_API SQLITE_EXPERIMENTAL int SQLITE_STDCALL sqlite3_stmt_scanstatus(
** This API is only available if the library is built with pre-processor
** symbol [SQLITE_ENABLE_STMT_SCANSTATUS] defined.
*/
-SQLITE_API SQLITE_EXPERIMENTAL void SQLITE_STDCALL sqlite3_stmt_scanstatus_reset(sqlite3_stmt*);
+SQLITE_API void SQLITE_STDCALL sqlite3_stmt_scanstatus_reset(sqlite3_stmt*);
/*
@@ -7813,6 +7903,8 @@ struct sqlite3_rtree_query_info {
int eParentWithin; /* Visibility of parent node */
int eWithin; /* OUT: Visiblity */
sqlite3_rtree_dbl rScore; /* OUT: Write the score here */
+ /* The following fields are only available in 3.8.11 and later */
+ sqlite3_value **apSqlParam; /* Original SQL values of parameters */
};
/*
@@ -7829,3 +7921,523 @@ struct sqlite3_rtree_query_info {
#endif /* ifndef _SQLITE3RTREE_H_ */
+/*
+** 2014 May 31
+**
+** 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.
+**
+******************************************************************************
+**
+** Interfaces to extend FTS5. Using the interfaces defined in this file,
+** FTS5 may be extended with:
+**
+** * custom tokenizers, and
+** * custom auxiliary functions.
+*/
+
+
+#ifndef _FTS5_H
+#define _FTS5_H
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*************************************************************************
+** CUSTOM AUXILIARY FUNCTIONS
+**
+** Virtual table implementations may overload SQL functions by implementing
+** the sqlite3_module.xFindFunction() method.
+*/
+
+typedef struct Fts5ExtensionApi Fts5ExtensionApi;
+typedef struct Fts5Context Fts5Context;
+typedef struct Fts5PhraseIter Fts5PhraseIter;
+
+typedef void (*fts5_extension_function)(
+ const Fts5ExtensionApi *pApi, /* API offered by current FTS version */
+ Fts5Context *pFts, /* First arg to pass to pApi functions */
+ sqlite3_context *pCtx, /* Context for returning result/error */
+ int nVal, /* Number of values in apVal[] array */
+ sqlite3_value **apVal /* Array of trailing arguments */
+);
+
+struct Fts5PhraseIter {
+ const unsigned char *a;
+ const unsigned char *b;
+};
+
+/*
+** EXTENSION API FUNCTIONS
+**
+** xUserData(pFts):
+** Return a copy of the context pointer the extension function was
+** registered with.
+**
+** xColumnTotalSize(pFts, iCol, pnToken):
+** If parameter iCol is less than zero, set output variable *pnToken
+** to the total number of tokens in the FTS5 table. Or, if iCol is
+** non-negative but less than the number of columns in the table, return
+** the total number of tokens in column iCol, considering all rows in
+** the FTS5 table.
+**
+** If parameter iCol is greater than or equal to the number of columns
+** in the table, SQLITE_RANGE is returned. Or, if an error occurs (e.g.
+** an OOM condition or IO error), an appropriate SQLite error code is
+** returned.
+**
+** xColumnCount(pFts):
+** Return the number of columns in the table.
+**
+** xColumnSize(pFts, iCol, pnToken):
+** If parameter iCol is less than zero, set output variable *pnToken
+** to the total number of tokens in the current row. Or, if iCol is
+** non-negative but less than the number of columns in the table, set
+** *pnToken to the number of tokens in column iCol of the current row.
+**
+** If parameter iCol is greater than or equal to the number of columns
+** in the table, SQLITE_RANGE is returned. Or, if an error occurs (e.g.
+** an OOM condition or IO error), an appropriate SQLite error code is
+** returned.
+**
+** xColumnText:
+** This function attempts to retrieve the text of column iCol of the
+** current document. If successful, (*pz) is set to point to a buffer
+** containing the text in utf-8 encoding, (*pn) is set to the size in bytes
+** (not characters) of the buffer and SQLITE_OK is returned. Otherwise,
+** if an error occurs, an SQLite error code is returned and the final values
+** of (*pz) and (*pn) are undefined.
+**
+** xPhraseCount:
+** Returns the number of phrases in the current query expression.
+**
+** xPhraseSize:
+** Returns the number of tokens in phrase iPhrase of the query. Phrases
+** are numbered starting from zero.
+**
+** xInstCount:
+** Set *pnInst to the total number of occurrences of all phrases within
+** the query within the current row. Return SQLITE_OK if successful, or
+** an error code (i.e. SQLITE_NOMEM) if an error occurs.
+**
+** xInst:
+** Query for the details of phrase match iIdx within the current row.
+** Phrase matches are numbered starting from zero, so the iIdx argument
+** should be greater than or equal to zero and smaller than the value
+** output by xInstCount().
+**
+** Returns SQLITE_OK if successful, or an error code (i.e. SQLITE_NOMEM)
+** if an error occurs.
+**
+** xRowid:
+** Returns the rowid of the current row.
+**
+** xTokenize:
+** Tokenize text using the tokenizer belonging to the FTS5 table.
+**
+** xQueryPhrase(pFts5, iPhrase, pUserData, xCallback):
+** This API function is used to query the FTS table for phrase iPhrase
+** of the current query. Specifically, a query equivalent to:
+**
+** ... FROM ftstable WHERE ftstable MATCH $p ORDER BY rowid
+**
+** with $p set to a phrase equivalent to the phrase iPhrase of the
+** current query is executed. For each row visited, the callback function
+** passed as the fourth argument is invoked. The context and API objects
+** passed to the callback function may be used to access the properties of
+** each matched row. Invoking Api.xUserData() returns a copy of the pointer
+** passed as the third argument to pUserData.
+**
+** If the callback function returns any value other than SQLITE_OK, the
+** query is abandoned and the xQueryPhrase function returns immediately.
+** If the returned value is SQLITE_DONE, xQueryPhrase returns SQLITE_OK.
+** Otherwise, the error code is propagated upwards.
+**
+** If the query runs to completion without incident, SQLITE_OK is returned.
+** Or, if some error occurs before the query completes or is aborted by
+** the callback, an SQLite error code is returned.
+**
+**
+** xSetAuxdata(pFts5, pAux, xDelete)
+**
+** Save the pointer passed as the second argument as the extension functions
+** "auxiliary data". The pointer may then be retrieved by the current or any
+** future invocation of the same fts5 extension function made as part of
+** of the same MATCH query using the xGetAuxdata() API.
+**
+** Each extension function is allocated a single auxiliary data slot for
+** each FTS query (MATCH expression). If the extension function is invoked
+** more than once for a single FTS query, then all invocations share a
+** single auxiliary data context.
+**
+** If there is already an auxiliary data pointer when this function is
+** invoked, then it is replaced by the new pointer. If an xDelete callback
+** was specified along with the original pointer, it is invoked at this
+** point.
+**
+** The xDelete callback, if one is specified, is also invoked on the
+** auxiliary data pointer after the FTS5 query has finished.
+**
+** If an error (e.g. an OOM condition) occurs within this function, an
+** the auxiliary data is set to NULL and an error code returned. If the
+** xDelete parameter was not NULL, it is invoked on the auxiliary data
+** pointer before returning.
+**
+**
+** xGetAuxdata(pFts5, bClear)
+**
+** Returns the current auxiliary data pointer for the fts5 extension
+** function. See the xSetAuxdata() method for details.
+**
+** If the bClear argument is non-zero, then the auxiliary data is cleared
+** (set to NULL) before this function returns. In this case the xDelete,
+** if any, is not invoked.
+**
+**
+** xRowCount(pFts5, pnRow)
+**
+** This function is used to retrieve the total number of rows in the table.
+** In other words, the same value that would be returned by:
+**
+** SELECT count(*) FROM ftstable;
+**
+** xPhraseFirst()
+** This function is used, along with type Fts5PhraseIter and the xPhraseNext
+** method, to iterate through all instances of a single query phrase within
+** the current row. This is the same information as is accessible via the
+** xInstCount/xInst APIs. While the xInstCount/xInst APIs are more convenient
+** to use, this API may be faster under some circumstances. To iterate
+** through instances of phrase iPhrase, use the following code:
+**
+** Fts5PhraseIter iter;
+** int iCol, iOff;
+** for(pApi->xPhraseFirst(pFts, iPhrase, &iter, &iCol, &iOff);
+** iOff>=0;
+** pApi->xPhraseNext(pFts, &iter, &iCol, &iOff)
+** ){
+** // An instance of phrase iPhrase at offset iOff of column iCol
+** }
+**
+** The Fts5PhraseIter structure is defined above. Applications should not
+** modify this structure directly - it should only be used as shown above
+** with the xPhraseFirst() and xPhraseNext() API methods.
+**
+** xPhraseNext()
+** See xPhraseFirst above.
+*/
+struct Fts5ExtensionApi {
+ int iVersion; /* Currently always set to 1 */
+
+ void *(*xUserData)(Fts5Context*);
+
+ int (*xColumnCount)(Fts5Context*);
+ int (*xRowCount)(Fts5Context*, sqlite3_int64 *pnRow);
+ int (*xColumnTotalSize)(Fts5Context*, int iCol, sqlite3_int64 *pnToken);
+
+ int (*xTokenize)(Fts5Context*,
+ const char *pText, int nText, /* Text to tokenize */
+ void *pCtx, /* Context passed to xToken() */
+ int (*xToken)(void*, int, const char*, int, int, int) /* Callback */
+ );
+
+ int (*xPhraseCount)(Fts5Context*);
+ int (*xPhraseSize)(Fts5Context*, int iPhrase);
+
+ int (*xInstCount)(Fts5Context*, int *pnInst);
+ int (*xInst)(Fts5Context*, int iIdx, int *piPhrase, int *piCol, int *piOff);
+
+ sqlite3_int64 (*xRowid)(Fts5Context*);
+ int (*xColumnText)(Fts5Context*, int iCol, const char **pz, int *pn);
+ int (*xColumnSize)(Fts5Context*, int iCol, int *pnToken);
+
+ int (*xQueryPhrase)(Fts5Context*, int iPhrase, void *pUserData,
+ int(*)(const Fts5ExtensionApi*,Fts5Context*,void*)
+ );
+ int (*xSetAuxdata)(Fts5Context*, void *pAux, void(*xDelete)(void*));
+ void *(*xGetAuxdata)(Fts5Context*, int bClear);
+
+ void (*xPhraseFirst)(Fts5Context*, int iPhrase, Fts5PhraseIter*, int*, int*);
+ void (*xPhraseNext)(Fts5Context*, Fts5PhraseIter*, int *piCol, int *piOff);
+};
+
+/*
+** CUSTOM AUXILIARY FUNCTIONS
+*************************************************************************/
+
+/*************************************************************************
+** CUSTOM TOKENIZERS
+**
+** Applications may also register custom tokenizer types. A tokenizer
+** is registered by providing fts5 with a populated instance of the
+** following structure. All structure methods must be defined, setting
+** any member of the fts5_tokenizer struct to NULL leads to undefined
+** behaviour. The structure methods are expected to function as follows:
+**
+** xCreate:
+** This function is used to allocate and inititalize a tokenizer instance.
+** A tokenizer instance is required to actually tokenize text.
+**
+** The first argument passed to this function is a copy of the (void*)
+** pointer provided by the application when the fts5_tokenizer object
+** was registered with FTS5 (the third argument to xCreateTokenizer()).
+** The second and third arguments are an array of nul-terminated strings
+** containing the tokenizer arguments, if any, specified following the
+** tokenizer name as part of the CREATE VIRTUAL TABLE statement used
+** to create the FTS5 table.
+**
+** The final argument is an output variable. If successful, (*ppOut)
+** should be set to point to the new tokenizer handle and SQLITE_OK
+** returned. If an error occurs, some value other than SQLITE_OK should
+** be returned. In this case, fts5 assumes that the final value of *ppOut
+** is undefined.
+**
+** xDelete:
+** This function is invoked to delete a tokenizer handle previously
+** allocated using xCreate(). Fts5 guarantees that this function will
+** be invoked exactly once for each successful call to xCreate().
+**
+** xTokenize:
+** This function is expected to tokenize the nText byte string indicated
+** by argument pText. pText may or may not be nul-terminated. The first
+** argument passed to this function is a pointer to an Fts5Tokenizer object
+** returned by an earlier call to xCreate().
+**
+** The second argument indicates the reason that FTS5 is requesting
+** tokenization of the supplied text. This is always one of the following
+** four values:
+**
+** <ul><li> <b>FTS5_TOKENIZE_DOCUMENT</b> - A document is being inserted into
+** or removed from the FTS table. The tokenizer is being invoked to
+** determine the set of tokens to add to (or delete from) the
+** FTS index.
+**
+** <li> <b>FTS5_TOKENIZE_QUERY</b> - A MATCH query is being executed
+** against the FTS index. The tokenizer is being called to tokenize
+** a bareword or quoted string specified as part of the query.
+**
+** <li> <b>(FTS5_TOKENIZE_QUERY | FTS5_TOKENIZE_PREFIX)</b> - Same as
+** FTS5_TOKENIZE_QUERY, except that the bareword or quoted string is
+** followed by a "*" character, indicating that the last token
+** returned by the tokenizer will be treated as a token prefix.
+**
+** <li> <b>FTS5_TOKENIZE_AUX</b> - The tokenizer is being invoked to
+** satisfy an fts5_api.xTokenize() request made by an auxiliary
+** function. Or an fts5_api.xColumnSize() request made by the same
+** on a columnsize=0 database.
+** </ul>
+**
+** For each token in the input string, the supplied callback xToken() must
+** be invoked. The first argument to it should be a copy of the pointer
+** passed as the second argument to xTokenize(). The third and fourth
+** arguments are a pointer to a buffer containing the token text, and the
+** size of the token in bytes. The 4th and 5th arguments are the byte offsets
+** of the first byte of and first byte immediately following the text from
+** which the token is derived within the input.
+**
+** The second argument passed to the xToken() callback ("tflags") should
+** normally be set to 0. The exception is if the tokenizer supports
+** synonyms. In this case see the discussion below for details.
+**
+** FTS5 assumes the xToken() callback is invoked for each token in the
+** order that they occur within the input text.
+**
+** If an xToken() callback returns any value other than SQLITE_OK, then
+** the tokenization should be abandoned and the xTokenize() method should
+** immediately return a copy of the xToken() return value. Or, if the
+** input buffer is exhausted, xTokenize() should return SQLITE_OK. Finally,
+** if an error occurs with the xTokenize() implementation itself, it
+** may abandon the tokenization and return any error code other than
+** SQLITE_OK or SQLITE_DONE.
+**
+** SYNONYM SUPPORT
+**
+** Custom tokenizers may also support synonyms. Consider a case in which a
+** user wishes to query for a phrase such as "first place". Using the
+** built-in tokenizers, the FTS5 query 'first + place' will match instances
+** of "first place" within the document set, but not alternative forms
+** such as "1st place". In some applications, it would be better to match
+** all instances of "first place" or "1st place" regardless of which form
+** the user specified in the MATCH query text.
+**
+** There are several ways to approach this in FTS5:
+**
+** <ol><li> By mapping all synonyms to a single token. In this case, the
+** In the above example, this means that the tokenizer returns the
+** same token for inputs "first" and "1st". Say that token is in
+** fact "first", so that when the user inserts the document "I won
+** 1st place" entries are added to the index for tokens "i", "won",
+** "first" and "place". If the user then queries for '1st + place',
+** the tokenizer substitutes "first" for "1st" and the query works
+** as expected.
+**
+** <li> By adding multiple synonyms for a single term to the FTS index.
+** In this case, when tokenizing query text, the tokenizer may
+** provide multiple synonyms for a single term within the document.
+** FTS5 then queries the index for each synonym individually. For
+** example, faced with the query:
+**
+** <codeblock>
+** ... MATCH 'first place'</codeblock>
+**
+** the tokenizer offers both "1st" and "first" as synonyms for the
+** first token in the MATCH query and FTS5 effectively runs a query
+** similar to:
+**
+** <codeblock>
+** ... MATCH '(first OR 1st) place'</codeblock>
+**
+** except that, for the purposes of auxiliary functions, the query
+** still appears to contain just two phrases - "(first OR 1st)"
+** being treated as a single phrase.
+**
+** <li> By adding multiple synonyms for a single term to the FTS index.
+** Using this method, when tokenizing document text, the tokenizer
+** provides multiple synonyms for each token. So that when a
+** document such as "I won first place" is tokenized, entries are
+** added to the FTS index for "i", "won", "first", "1st" and
+** "place".
+**
+** This way, even if the tokenizer does not provide synonyms
+** when tokenizing query text (it should not - to do would be
+** inefficient), it doesn't matter if the user queries for
+** 'first + place' or '1st + place', as there are entires in the
+** FTS index corresponding to both forms of the first token.
+** </ol>
+**
+** Whether it is parsing document or query text, any call to xToken that
+** specifies a <i>tflags</i> argument with the FTS5_TOKEN_COLOCATED bit
+** is considered to supply a synonym for the previous token. For example,
+** when parsing the document "I won first place", a tokenizer that supports
+** synonyms would call xToken() 5 times, as follows:
+**
+** <codeblock>
+** xToken(pCtx, 0, "i", 1, 0, 1);
+** xToken(pCtx, 0, "won", 3, 2, 5);
+** xToken(pCtx, 0, "first", 5, 6, 11);
+** xToken(pCtx, FTS5_TOKEN_COLOCATED, "1st", 3, 6, 11);
+** xToken(pCtx, 0, "place", 5, 12, 17);
+**</codeblock>
+**
+** It is an error to specify the FTS5_TOKEN_COLOCATED flag the first time
+** xToken() is called. Multiple synonyms may be specified for a single token
+** by making multiple calls to xToken(FTS5_TOKEN_COLOCATED) in sequence.
+** There is no limit to the number of synonyms that may be provided for a
+** single token.
+**
+** In many cases, method (1) above is the best approach. It does not add
+** extra data to the FTS index or require FTS5 to query for multiple terms,
+** so it is efficient in terms of disk space and query speed. However, it
+** does not support prefix queries very well. If, as suggested above, the
+** token "first" is subsituted for "1st" by the tokenizer, then the query:
+**
+** <codeblock>
+** ... MATCH '1s*'</codeblock>
+**
+** will not match documents that contain the token "1st" (as the tokenizer
+** will probably not map "1s" to any prefix of "first").
+**
+** For full prefix support, method (3) may be preferred. In this case,
+** because the index contains entries for both "first" and "1st", prefix
+** queries such as 'fi*' or '1s*' will match correctly. However, because
+** extra entries are added to the FTS index, this method uses more space
+** within the database.
+**
+** Method (2) offers a midpoint between (1) and (3). Using this method,
+** a query such as '1s*' will match documents that contain the literal
+** token "1st", but not "first" (assuming the tokenizer is not able to
+** provide synonyms for prefixes). However, a non-prefix query like '1st'
+** will match against "1st" and "first". This method does not require
+** extra disk space, as no extra entries are added to the FTS index.
+** On the other hand, it may require more CPU cycles to run MATCH queries,
+** as separate queries of the FTS index are required for each synonym.
+**
+** When using methods (2) or (3), it is important that the tokenizer only
+** provide synonyms when tokenizing document text (method (2)) or query
+** text (method (3)), not both. Doing so will not cause any errors, but is
+** inefficient.
+*/
+typedef struct Fts5Tokenizer Fts5Tokenizer;
+typedef struct fts5_tokenizer fts5_tokenizer;
+struct fts5_tokenizer {
+ int (*xCreate)(void*, const char **azArg, int nArg, Fts5Tokenizer **ppOut);
+ void (*xDelete)(Fts5Tokenizer*);
+ int (*xTokenize)(Fts5Tokenizer*,
+ void *pCtx,
+ int flags, /* Mask of FTS5_TOKENIZE_* flags */
+ const char *pText, int nText,
+ int (*xToken)(
+ void *pCtx, /* Copy of 2nd argument to xTokenize() */
+ int tflags, /* Mask of FTS5_TOKEN_* flags */
+ const char *pToken, /* Pointer to buffer containing token */
+ int nToken, /* Size of token in bytes */
+ int iStart, /* Byte offset of token within input text */
+ int iEnd /* Byte offset of end of token within input text */
+ )
+ );
+};
+
+/* Flags that may be passed as the third argument to xTokenize() */
+#define FTS5_TOKENIZE_QUERY 0x0001
+#define FTS5_TOKENIZE_PREFIX 0x0002
+#define FTS5_TOKENIZE_DOCUMENT 0x0004
+#define FTS5_TOKENIZE_AUX 0x0008
+
+/* Flags that may be passed by the tokenizer implementation back to FTS5
+** as the third argument to the supplied xToken callback. */
+#define FTS5_TOKEN_COLOCATED 0x0001 /* Same position as prev. token */
+
+/*
+** END OF CUSTOM TOKENIZERS
+*************************************************************************/
+
+/*************************************************************************
+** FTS5 EXTENSION REGISTRATION API
+*/
+typedef struct fts5_api fts5_api;
+struct fts5_api {
+ int iVersion; /* Currently always set to 2 */
+
+ /* Create a new tokenizer */
+ int (*xCreateTokenizer)(
+ fts5_api *pApi,
+ const char *zName,
+ void *pContext,
+ fts5_tokenizer *pTokenizer,
+ void (*xDestroy)(void*)
+ );
+
+ /* Find an existing tokenizer */
+ int (*xFindTokenizer)(
+ fts5_api *pApi,
+ const char *zName,
+ void **ppContext,
+ fts5_tokenizer *pTokenizer
+ );
+
+ /* Create a new auxiliary function */
+ int (*xCreateFunction)(
+ fts5_api *pApi,
+ const char *zName,
+ void *pContext,
+ fts5_extension_function xFunction,
+ void (*xDestroy)(void*)
+ );
+};
+
+/*
+** END OF REGISTRATION API
+*************************************************************************/
+
+#ifdef __cplusplus
+} /* end of the 'extern "C"' block */
+#endif
+
+#endif /* _FTS5_H */
+
+
diff --git a/ext/sqlite3/libsqlite/sqlite3ext.h b/ext/sqlite3/libsqlite/sqlite3ext.h
index f9a066592d..017ea308b1 100644
--- a/ext/sqlite3/libsqlite/sqlite3ext.h
+++ b/ext/sqlite3/libsqlite/sqlite3ext.h
@@ -267,6 +267,14 @@ struct sqlite3_api_routines {
void (*result_text64)(sqlite3_context*,const char*,sqlite3_uint64,
void(*)(void*), unsigned char);
int (*strglob)(const char*,const char*);
+ /* Version 3.8.11 and later */
+ sqlite3_value *(*value_dup)(const sqlite3_value*);
+ void (*value_free)(sqlite3_value*);
+ int (*result_zeroblob64)(sqlite3_context*,sqlite3_uint64);
+ int (*bind_zeroblob64)(sqlite3_stmt*, int, sqlite3_uint64);
+ /* Version 3.9.0 and later */
+ unsigned int (*value_subtype)(sqlite3_value*);
+ void (*result_subtype)(sqlite3_context*,unsigned int);
};
/*
@@ -280,7 +288,7 @@ struct sqlite3_api_routines {
** the API. So the redefinition macros are only valid if the
** SQLITE_CORE macros is undefined.
*/
-#ifndef SQLITE_CORE
+#if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION)
#define sqlite3_aggregate_context sqlite3_api->aggregate_context
#ifndef SQLITE_OMIT_DEPRECATED
#define sqlite3_aggregate_count sqlite3_api->aggregate_count
@@ -407,6 +415,7 @@ struct sqlite3_api_routines {
#define sqlite3_value_text16le sqlite3_api->value_text16le
#define sqlite3_value_type sqlite3_api->value_type
#define sqlite3_vmprintf sqlite3_api->vmprintf
+#define sqlite3_vsnprintf sqlite3_api->vsnprintf
#define sqlite3_overload_function sqlite3_api->overload_function
#define sqlite3_prepare_v2 sqlite3_api->prepare_v2
#define sqlite3_prepare16_v2 sqlite3_api->prepare16_v2
@@ -497,9 +506,17 @@ struct sqlite3_api_routines {
#define sqlite3_result_blob64 sqlite3_api->result_blob64
#define sqlite3_result_text64 sqlite3_api->result_text64
#define sqlite3_strglob sqlite3_api->strglob
-#endif /* SQLITE_CORE */
+/* Version 3.8.11 and later */
+#define sqlite3_value_dup sqlite3_api->value_dup
+#define sqlite3_value_free sqlite3_api->value_free
+#define sqlite3_result_zeroblob64 sqlite3_api->result_zeroblob64
+#define sqlite3_bind_zeroblob64 sqlite3_api->bind_zeroblob64
+/* Version 3.9.0 and later */
+#define sqlite3_value_subtype sqlite3_api->value_subtype
+#define sqlite3_result_subtype sqlite3_api->result_subtype
+#endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */
-#ifndef SQLITE_CORE
+#if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION)
/* This case when the file really is being compiled as a loadable
** extension */
# define SQLITE_EXTENSION_INIT1 const sqlite3_api_routines *sqlite3_api=0;
diff --git a/ext/sqlite3/sqlite3.c b/ext/sqlite3/sqlite3.c
index 741a6ad0c2..d7f7722959 100644
--- a/ext/sqlite3/sqlite3.c
+++ b/ext/sqlite3/sqlite3.c
@@ -124,14 +124,6 @@ PHP_METHOD(sqlite3, open)
return;
}
-#if PHP_API_VERSION < 20100412
- if (PG(safe_mode) && (!php_checkuid(fullpath, NULL, CHECKUID_CHECK_FILE_AND_DIR))) {
- zend_throw_exception_ex(zend_ce_exception, 0, "safe_mode prohibits opening %s", fullpath);
- efree(fullpath);
- return;
- }
-#endif
-
if (php_check_open_basedir(fullpath)) {
zend_throw_exception_ex(zend_ce_exception, 0, "open_basedir prohibits opening %s", fullpath);
efree(fullpath);
@@ -164,11 +156,7 @@ PHP_METHOD(sqlite3, open)
db_obj->initialised = 1;
-#if PHP_API_VERSION < 20100412
- if (PG(safe_mode) || (PG(open_basedir) && *PG(open_basedir))) {
-#else
if (PG(open_basedir) && *PG(open_basedir)) {
-#endif
sqlite3_set_authorizer(db_obj->db, php_sqlite3_authorizer, NULL);
}
@@ -695,9 +683,7 @@ static int sqlite3_do_callback(struct php_sqlite3_fci *fc, zval *cb, int argc, s
fake_argc = argc + is_agg;
fc->fci.size = sizeof(fc->fci);
- fc->fci.function_table = EG(function_table);
ZVAL_COPY_VALUE(&fc->fci.function_name, cb);
- fc->fci.symbol_table = NULL;
fc->fci.object = NULL;
fc->fci.retval = &retval;
fc->fci.param_count = fake_argc;
@@ -854,9 +840,7 @@ static int php_sqlite3_callback_compare(void *coll, int a_len, const void *a, in
int ret;
collation->fci.fci.size = (sizeof(collation->fci.fci));
- collation->fci.fci.function_table = EG(function_table);
ZVAL_COPY_VALUE(&collation->fci.fci.function_name, &collation->cmp_func);
- collation->fci.fci.symbol_table = NULL;
collation->fci.fci.object = NULL;
collation->fci.fci.retval = &retval;
collation->fci.fci.param_count = 2;
@@ -2011,13 +1995,6 @@ static int php_sqlite3_authorizer(void *autharg, int access_type, const char *ar
case SQLITE_ATTACH:
{
if (memcmp(arg3, ":memory:", sizeof(":memory:")) && *arg3) {
-
-#if PHP_API_VERSION < 20100412
- if (PG(safe_mode) && (!php_checkuid(arg3, NULL, CHECKUID_CHECK_FILE_AND_DIR))) {
- return SQLITE_DENY;
- }
-#endif
-
if (php_check_open_basedir(arg3)) {
return SQLITE_DENY;
}
diff --git a/ext/standard/array.c b/ext/standard/array.c
index 6c58e3aa74..67aec057b8 100644
--- a/ext/standard/array.c
+++ b/ext/standard/array.c
@@ -46,9 +46,7 @@
#include "php_string.h"
#include "php_rand.h"
#include "zend_smart_str.h"
-#ifdef HAVE_SPL
#include "ext/spl/spl_array.h"
-#endif
/* {{{ defines */
#define EXTR_OVERWRITE 0
@@ -821,9 +819,7 @@ PHP_FUNCTION(count)
RETURN_LONG(cnt);
break;
case IS_OBJECT: {
-#ifdef HAVE_SPL
zval retval;
-#endif
/* first, we check if the handler is defined */
if (Z_OBJ_HT_P(array)->count_elements) {
RETVAL_LONG(1);
@@ -831,7 +827,6 @@ PHP_FUNCTION(count)
return;
}
}
-#ifdef HAVE_SPL
/* if not and the object implements Countable we call its count() method */
if (instanceof_function(Z_OBJCE_P(array), spl_ce_Countable)) {
zend_call_method_with_0_params(array, NULL, NULL, "count", &retval);
@@ -841,7 +836,6 @@ PHP_FUNCTION(count)
}
return;
}
-#endif
}
default:
RETURN_LONG(1);
@@ -1619,7 +1613,6 @@ static inline void php_search_array(INTERNAL_FUNCTION_PARAMETERS, int behavior)
}
} ZEND_HASH_FOREACH_END();
} else {
- ZVAL_DEREF(value);
if (Z_TYPE_P(value) == IS_LONG) {
ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(array), num_idx, str_idx, entry) {
if (fast_equal_check_long(value, entry)) {
@@ -1771,6 +1764,7 @@ PHP_FUNCTION(extract)
zend_ulong num_key;
int var_exists, count = 0;
int extract_refs = 0;
+ int exception_thrown = 0;
zend_array *symbol_table;
zval var_array;
@@ -1811,6 +1805,10 @@ PHP_FUNCTION(extract)
}
}
+ if (zend_forbid_dynamic_call("extract()") == FAILURE) {
+ return;
+ }
+
symbol_table = zend_rebuild_symbol_table();
#if 0
if (!symbol_table) {
@@ -1849,9 +1847,6 @@ PHP_FUNCTION(extract)
if (var_exists && ZSTR_LEN(var_name) == sizeof("GLOBALS")-1 && !strcmp(ZSTR_VAL(var_name), "GLOBALS")) {
break;
}
- if (var_exists && ZSTR_LEN(var_name) == sizeof("this")-1 && !strcmp(ZSTR_VAL(var_name), "this") && EG(scope) && ZSTR_LEN(EG(scope)->name) != 0) {
- break;
- }
ZVAL_STR_COPY(&final_name, var_name);
break;
@@ -1892,6 +1887,15 @@ PHP_FUNCTION(extract)
if (Z_TYPE(final_name) == IS_STRING && php_valid_var_name(Z_STRVAL(final_name), Z_STRLEN(final_name))) {
zval *orig_var;
+
+ if (Z_STRLEN(final_name) == sizeof("this")-1 && !strcmp(Z_STRVAL(final_name), "this")) {
+ if (!exception_thrown) {
+ exception_thrown = 1;
+ zend_throw_error(NULL, "Cannot re-assign $this");
+ }
+ zval_dtor(&final_name);
+ continue;
+ }
if (extract_refs) {
ZVAL_MAKE_REF(entry);
@@ -1972,8 +1976,11 @@ PHP_FUNCTION(compact)
return;
}
- symbol_table = zend_rebuild_symbol_table();
+ if (zend_forbid_dynamic_call("compact()") == FAILURE) {
+ return;
+ }
+ symbol_table = zend_rebuild_symbol_table();
if (UNEXPECTED(symbol_table == NULL)) {
return;
}
@@ -3235,18 +3242,19 @@ static inline void php_array_merge_or_replace_wrapper(INTERNAL_FUNCTION_PARAMETE
src = Z_ARRVAL_P(arg);
dest = Z_ARRVAL_P(return_value);
ZEND_HASH_FOREACH_KEY_VAL(src, idx, string_key, src_entry) {
- if (UNEXPECTED(Z_ISREF_P(src_entry) && Z_REFCOUNT_P(src_entry) == 1)) {
- src_entry = Z_REFVAL_P(src_entry);
- }
- if (string_key) {
- if (Z_REFCOUNTED_P(src_entry)) {
+ if (Z_REFCOUNTED_P(src_entry)) {
+ if (UNEXPECTED(Z_ISREF_P(src_entry) && Z_REFCOUNT_P(src_entry) == 1)) {
+ src_entry = Z_REFVAL_P(src_entry);
+ if (Z_REFCOUNTED_P(src_entry)) {
+ Z_ADDREF_P(src_entry);
+ }
+ } else {
Z_ADDREF_P(src_entry);
}
+ }
+ if (string_key) {
zend_hash_add_new(dest, string_key, src_entry);
} else {
- if (Z_REFCOUNTED_P(src_entry)) {
- Z_ADDREF_P(src_entry);
- }
zend_hash_index_add_new(dest, idx, src_entry);
}
} ZEND_HASH_FOREACH_END();
@@ -3275,18 +3283,19 @@ static inline void php_array_merge_or_replace_wrapper(INTERNAL_FUNCTION_PARAMETE
src = Z_ARRVAL_P(arg);
dest = Z_ARRVAL_P(return_value);
ZEND_HASH_FOREACH_STR_KEY_VAL(src, string_key, src_entry) {
- if (UNEXPECTED(Z_ISREF_P(src_entry) && Z_REFCOUNT_P(src_entry) == 1)) {
- src_entry = Z_REFVAL_P(src_entry);
- }
- if (string_key) {
- if (Z_REFCOUNTED_P(src_entry)) {
+ if (Z_REFCOUNTED_P(src_entry)) {
+ if (UNEXPECTED(Z_ISREF_P(src_entry) && Z_REFCOUNT_P(src_entry) == 1)) {
+ src_entry = Z_REFVAL_P(src_entry);
+ if (Z_REFCOUNTED_P(src_entry)) {
+ Z_ADDREF_P(src_entry);
+ }
+ } else {
Z_ADDREF_P(src_entry);
}
+ }
+ if (string_key) {
zend_hash_add_new(dest, string_key, src_entry);
} else {
- if (Z_REFCOUNTED_P(src_entry)) {
- Z_ADDREF_P(src_entry);
- }
zend_hash_next_index_insert_new(dest, src_entry);
}
} ZEND_HASH_FOREACH_END();
diff --git a/ext/standard/assert.c b/ext/standard/assert.c
index 109ba3ba22..016c90e02e 100644
--- a/ext/standard/assert.c
+++ b/ext/standard/assert.c
@@ -164,7 +164,10 @@ PHP_FUNCTION(assert)
if (Z_TYPE_P(assertion) == IS_STRING) {
zval retval;
int old_error_reporting = 0; /* shut up gcc! */
- zend_class_entry *orig_scope = EG(scope);
+
+ if (zend_forbid_dynamic_call("assert() with string argument") == FAILURE) {
+ RETURN_FALSE;
+ }
myeval = Z_STRVAL_P(assertion);
@@ -194,8 +197,6 @@ PHP_FUNCTION(assert)
EG(error_reporting) = old_error_reporting;
}
- EG(scope) = orig_scope;
-
convert_to_boolean(&retval);
val = Z_TYPE(retval) == IS_TRUE;
} else {
diff --git a/ext/standard/basic_functions.c b/ext/standard/basic_functions.c
index 452c565d75..69663d983d 100644
--- a/ext/standard/basic_functions.c
+++ b/ext/standard/basic_functions.c
@@ -23,6 +23,7 @@
#include "php_streams.h"
#include "php_main.h"
#include "php_globals.h"
+#include "php_variables.h"
#include "php_ini.h"
#include "php_standard.h"
#include "php_math.h"
@@ -632,7 +633,7 @@ ZEND_BEGIN_ARG_INFO(arginfo_long2ip, 0)
ZEND_ARG_INFO(0, proper_address)
ZEND_END_ARG_INFO()
-ZEND_BEGIN_ARG_INFO(arginfo_getenv, 0)
+ZEND_BEGIN_ARG_INFO_EX(arginfo_getenv, 0, 0, 0)
ZEND_ARG_INFO(0, varname)
ZEND_END_ARG_INFO()
@@ -871,12 +872,10 @@ ZEND_END_ARG_INFO()
/* }}} */
/* {{{ crypt.c */
-#if HAVE_CRYPT
ZEND_BEGIN_ARG_INFO_EX(arginfo_crypt, 0, 0, 1)
ZEND_ARG_INFO(0, str)
ZEND_ARG_INFO(0, salt)
ZEND_END_ARG_INFO()
-#endif
/* }}} */
/* {{{ cyr_convert.c */
ZEND_BEGIN_ARG_INFO(arginfo_convert_cyr_string, 0)
@@ -2598,6 +2597,7 @@ ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_get_headers, 0, 0, 1)
ZEND_ARG_INFO(0, url)
ZEND_ARG_INFO(0, format)
+ ZEND_ARG_INFO(0, context)
ZEND_END_ARG_INFO()
/* }}} */
/* {{{ user_filters.c */
@@ -3168,10 +3168,8 @@ const zend_function_entry basic_functions[] = { /* {{{ */
/* functions from browscap.c */
PHP_FE(get_browser, arginfo_get_browser)
-#if HAVE_CRYPT
/* functions from crypt.c */
PHP_FE(crypt, arginfo_crypt)
-#endif
/* functions from dir.c */
PHP_FE(opendir, arginfo_opendir)
@@ -3491,8 +3489,8 @@ PHPAPI double php_get_nan(void) /* {{{ */
return HUGE_VAL + -HUGE_VAL;
#elif defined(__i386__) || defined(_X86_) || defined(ALPHA) || defined(_ALPHA) || defined(__alpha)
double val = 0.0;
- ((php_uint32*)&val)[1] = PHP_DOUBLE_QUIET_NAN_HIGH;
- ((php_uint32*)&val)[0] = 0;
+ ((uint32_t*)&val)[1] = PHP_DOUBLE_QUIET_NAN_HIGH;
+ ((uint32_t*)&val)[0] = 0;
return val;
#elif HAVE_ATOF_ACCEPTS_NAN
return atof("NAN");
@@ -3508,8 +3506,8 @@ PHPAPI double php_get_inf(void) /* {{{ */
return HUGE_VAL;
#elif defined(__i386__) || defined(_X86_) || defined(ALPHA) || defined(_ALPHA) || defined(__alpha)
double val = 0.0;
- ((php_uint32*)&val)[1] = PHP_DOUBLE_INFINITY_HIGH;
- ((php_uint32*)&val)[0] = 0;
+ ((uint32_t*)&val)[1] = PHP_DOUBLE_INFINITY_HIGH;
+ ((uint32_t*)&val)[0] = 0;
return val;
#elif HAVE_ATOF_ACCEPTS_INF
return atof("INF");
@@ -3641,10 +3639,7 @@ PHP_MINIT_FUNCTION(basic) /* {{{ */
BASIC_MINIT_SUBMODULE(nl_langinfo)
#endif
-#if HAVE_CRYPT
BASIC_MINIT_SUBMODULE(crypt)
-#endif
-
BASIC_MINIT_SUBMODULE(lcg)
BASIC_MINIT_SUBMODULE(dir)
@@ -3713,10 +3708,7 @@ PHP_MSHUTDOWN_FUNCTION(basic) /* {{{ */
#if defined(HAVE_LOCALECONV) && defined(ZTS)
BASIC_MSHUTDOWN_SUBMODULE(localeconv)
#endif
-#if HAVE_CRYPT
BASIC_MSHUTDOWN_SUBMODULE(crypt)
-#endif
-
BASIC_MSHUTDOWN_SUBMODULE(random)
zend_hash_destroy(&basic_submodules);
@@ -3842,20 +3834,21 @@ PHP_FUNCTION(constant)
{
zend_string *const_name;
zval *c;
+ zend_class_entry *scope;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &const_name) == FAILURE) {
return;
}
- c = zend_get_constant_ex(const_name, NULL, ZEND_FETCH_CLASS_SILENT);
+ scope = zend_get_executed_scope();
+ c = zend_get_constant_ex(const_name, scope, ZEND_FETCH_CLASS_SILENT);
if (c) {
- ZVAL_COPY_VALUE(return_value, c);
+ ZVAL_DUP(return_value, c);
if (Z_CONSTANT_P(return_value)) {
- if (UNEXPECTED(zval_update_constant_ex(return_value, 1, NULL) != SUCCESS)) {
+ if (UNEXPECTED(zval_update_constant_ex(return_value, scope) != SUCCESS)) {
return;
}
}
- zval_copy_ctor(return_value);
} else {
php_error_docref(NULL, E_WARNING, "Couldn't find constant %s", ZSTR_VAL(const_name));
RETURN_NULL();
@@ -3977,22 +3970,17 @@ PHP_FUNCTION(ip2long)
Converts an (IPv4) Internet network address into a string in Internet standard dotted format */
PHP_FUNCTION(long2ip)
{
- /* "It's a long but it's not, PHP ints are signed */
- char *ip;
- size_t ip_len;
- uint32_t n;
+ zend_ulong ip;
struct in_addr myaddr;
#ifdef HAVE_INET_PTON
char str[40];
#endif
- if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &ip, &ip_len) == FAILURE) {
+ if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &ip) == FAILURE) {
return;
}
- n = strtoul(ip, NULL, 0);
-
- myaddr.s_addr = htonl(n);
+ myaddr.s_addr = htonl(ip);
#ifdef HAVE_INET_PTON
if (inet_ntop(AF_INET, &myaddr, str, sizeof(str))) {
RETURN_STRING(str);
@@ -4009,17 +3997,24 @@ PHP_FUNCTION(long2ip)
* System Functions *
********************/
-/* {{{ proto string getenv(string varname)
- Get the value of an environment variable */
+/* {{{ proto string getenv([string varname])
+ Get the value of an environment variable or every available environment variable
+ if no varname is present */
PHP_FUNCTION(getenv)
{
- char *ptr, *str;
+ char *ptr, *str = NULL;
size_t str_len;
- if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &str, &str_len) == FAILURE) {
+ if (zend_parse_parameters(ZEND_NUM_ARGS(), "|s", &str, &str_len) == FAILURE) {
RETURN_FALSE;
}
+ if (!str) {
+ array_init(return_value);
+ php_import_environment_variables(return_value);
+ return;
+ }
+
/* SAPI method returns an emalloc()'d string */
ptr = sapi_getenv(str, str_len);
if (ptr) {
@@ -4709,7 +4704,7 @@ PHPAPI int _php_error_log_ex(int opt_err, char *message, size_t message_len, cha
break;
default:
- php_log_err(message);
+ php_log_err_with_severity(message, LOG_NOTICE);
break;
}
return SUCCESS;
@@ -5482,15 +5477,9 @@ PHP_FUNCTION(print_r)
}
if (do_return) {
- php_output_start_default();
- }
-
- zend_print_zval_r(var, 0);
-
- if (do_return) {
- php_output_get_contents(return_value);
- php_output_discard();
+ RETURN_STR(zend_print_zval_r_to_str(var, 0));
} else {
+ zend_print_zval_r(var, 0);
RETURN_TRUE;
}
}
diff --git a/ext/standard/basic_functions.h b/ext/standard/basic_functions.h
index 774f85dd1a..e37bca31d6 100644
--- a/ext/standard/basic_functions.h
+++ b/ext/standard/basic_functions.h
@@ -149,20 +149,12 @@ PHPAPI int _php_error_log(int opt_err, char *message, char *opt, char *headers);
PHPAPI int _php_error_log_ex(int opt_err, char *message, size_t message_len, char *opt, char *headers);
PHPAPI int php_prefix_varname(zval *result, zval *prefix, char *var_name, size_t var_name_len, zend_bool add_underscore);
-#if SIZEOF_INT == 4
-/* Most 32-bit and 64-bit systems have 32-bit ints */
-typedef unsigned int php_uint32;
-typedef signed int php_int32;
-#elif SIZEOF_LONG == 4
-/* 16-bit systems? */
-typedef unsigned long php_uint32;
-typedef signed long php_int32;
-#else
-#error Need type which holds 32 bits
-#endif
-
#define MT_N (624)
+/* Deprecated type aliases -- use the standard types instead */
+typedef uint32_t php_uint32;
+typedef int32_t php_int32;
+
typedef struct _php_basic_globals {
HashTable *user_shutdown_function_names;
HashTable putenv_ht;
@@ -193,8 +185,8 @@ typedef struct _php_basic_globals {
php_stream_statbuf ssb, lssb;
/* rand.c */
- php_uint32 state[MT_N+1]; /* state vector + 1 extra to not violate ANSI C */
- php_uint32 *next; /* next random value is computed from here */
+ uint32_t state[MT_N+1]; /* state vector + 1 extra to not violate ANSI C */
+ uint32_t *next; /* next random value is computed from here */
int left; /* can *next++ this many times before reloading */
unsigned int rand_seed; /* Seed for rand(), in ts version */
diff --git a/ext/standard/config.m4 b/ext/standard/config.m4
index f9afff30e6..26eb056321 100644
--- a/ext/standard/config.m4
+++ b/ext/standard/config.m4
@@ -242,7 +242,7 @@ int main() {
dnl
dnl If one of them is missing, use our own implementation, portable code is then possible
dnl
-if test "$ac_cv_crypt_blowfish" = "no" || test "$ac_cv_crypt_des" = "no" || test "$ac_cv_crypt_ext_des" = "no" || test "x$php_crypt_r" = "x0"; then
+if test "$ac_cv_crypt_blowfish" = "no" || test "$ac_cv_crypt_des" = "no" || test "$ac_cv_crypt_ext_des" = "no" || test "$ac_cv_crypt_md5" = "no" || test "$ac_cv_crypt_sha512" = "no" || test "$ac_cv_crypt_sha256" = "no" || test "x$php_crypt_r" = "x0"; then
dnl
dnl Check for __alignof__ support in the compiler
@@ -260,90 +260,29 @@ if test "$ac_cv_crypt_blowfish" = "no" || test "$ac_cv_crypt_des" = "no" || test
AC_DEFINE([HAVE_ALIGNOF], 1, [whether the compiler supports __alignof__])
fi
- dnl
- dnl Check for __attribute__ ((__aligned__)) support in the compiler
- dnl
- AC_CACHE_CHECK(whether the compiler supports aligned attribute, ac_cv_attribute_aligned,[
- AC_TRY_COMPILE([
- ],[
- unsigned char test[32] __attribute__ ((__aligned__ (__alignof__ (int))));
- ],[
- ac_cv_attribute_aligned=yes
- ],[
- ac_cv_attribute_aligned=no
- ])])
- if test "$ac_cv_attribute_aligned" = "yes"; then
- AC_DEFINE([HAVE_ATTRIBUTE_ALIGNED], 1, [whether the compiler supports __attribute__ ((__aligned__))])
- fi
-
-
AC_DEFINE_UNQUOTED(PHP_USE_PHP_CRYPT_R, 1, [Whether PHP has to use its own crypt_r for blowfish, des, ext des and md5])
- AC_DEFINE_UNQUOTED(PHP_STD_DES_CRYPT, 1, [Whether the system supports standard DES salt])
- AC_DEFINE_UNQUOTED(PHP_BLOWFISH_CRYPT, 1, [Whether the system supports BlowFish salt])
- AC_DEFINE_UNQUOTED(PHP_EXT_DES_CRYPT, 1, [Whether the system supports extended DES salt])
- AC_DEFINE_UNQUOTED(PHP_MD5_CRYPT, 1, [Whether the system supports MD5 salt])
- AC_DEFINE_UNQUOTED(PHP_SHA512_CRYPT, 1, [Whether the system supports SHA512 salt])
- AC_DEFINE_UNQUOTED(PHP_SHA256_CRYPT, 1, [Whether the system supports SHA256 salt])
PHP_ADD_SOURCES(PHP_EXT_DIR(standard), crypt_freesec.c crypt_blowfish.c crypt_sha512.c crypt_sha256.c php_crypt_r.c)
else
- if test "$ac_cv_crypt_des" = "yes"; then
- ac_result=1
- ac_crypt_des=1
- else
- ac_result=0
- ac_crypt_des=0
- fi
- AC_DEFINE_UNQUOTED(PHP_STD_DES_CRYPT, $ac_result, [Whether the system supports standard DES salt])
-
- if test "$ac_cv_crypt_blowfish" = "yes"; then
- ac_result=1
- ac_crypt_blowfish=1
- else
- ac_result=0
- ac_crypt_blowfish=0
- fi
- AC_DEFINE_UNQUOTED(PHP_BLOWFISH_CRYPT, $ac_result, [Whether the system supports BlowFish salt])
-
- if test "$ac_cv_crypt_ext_des" = "yes"; then
- ac_result=1
- ac_crypt_edes=1
- else
- ac_result=0
- ac_crypt_edes=0
- fi
- AC_DEFINE_UNQUOTED(PHP_EXT_DES_CRYPT, $ac_result, [Whether the system supports extended DES salt])
-
- if test "$ac_cv_crypt_md5" = "yes"; then
- ac_result=1
- ac_crypt_md5=1
- else
- ac_result=0
- ac_crypt_md5=0
- fi
- AC_DEFINE_UNQUOTED(PHP_MD5_CRYPT, $ac_result, [Whether the system supports MD5 salt])
-
- if test "$ac_cv_crypt_sha512" = "yes"; then
- ac_result=1
- ac_crypt_sha512=1
- else
- ac_result=0
- ac_crypt_sha512=0
- fi
- AC_DEFINE_UNQUOTED(PHP_SHA512_CRYPT, $ac_result, [Whether the system supports SHA512 salt])
-
- if test "$ac_cv_crypt_sha256" = "yes"; then
- ac_result=1
- ac_crypt_sha256=1
- else
- ac_result=0
- ac_crypt_sha256=0
- fi
- AC_DEFINE_UNQUOTED(PHP_SHA256_CRYPT, $ac_result, [Whether the system supports SHA256 salt])
-
AC_DEFINE_UNQUOTED(PHP_USE_PHP_CRYPT_R, 0, [Whether PHP has to use its own crypt_r for blowfish, des and ext des])
fi
+dnl
+dnl Check for __attribute__ ((__aligned__)) support in the compiler
+dnl
+AC_CACHE_CHECK(whether the compiler supports aligned attribute, ac_cv_attribute_aligned,[
+AC_TRY_COMPILE([
+],[
+ unsigned char test[32] __attribute__ ((__aligned__ (__alignof__ (int))));
+],[
+ ac_cv_attribute_aligned=yes
+],[
+ ac_cv_attribute_aligned=no
+])])
+if test "$ac_cv_attribute_aligned" = "yes"; then
+ AC_DEFINE([HAVE_ATTRIBUTE_ALIGNED], 1, [whether the compiler supports __attribute__ ((__aligned__))])
+fi
+
dnl
dnl Check for available functions
dnl
diff --git a/ext/standard/crc32.c b/ext/standard/crc32.c
index 45a2eb0667..e6024dcf11 100644
--- a/ext/standard/crc32.c
+++ b/ext/standard/crc32.c
@@ -28,8 +28,8 @@ PHP_NAMED_FUNCTION(php_if_crc32)
{
char *p;
size_t nr;
- php_uint32 crcinit = 0;
- register php_uint32 crc;
+ uint32_t crcinit = 0;
+ register uint32_t crc;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &p, &nr) == FAILURE) {
return;
diff --git a/ext/standard/crypt.c b/ext/standard/crypt.c
index 66b37eb79e..9249d9d96b 100644
--- a/ext/standard/crypt.c
+++ b/ext/standard/crypt.c
@@ -24,7 +24,6 @@
#include <stdlib.h>
#include "php.h"
-#if HAVE_CRYPT
#if HAVE_UNISTD_H
#include <unistd.h>
@@ -59,44 +58,8 @@
#include "php_crypt.h"
#include "php_rand.h"
-/* The capabilities of the crypt() function is determined by the test programs
- * run by configure from aclocal.m4. They will set PHP_STD_DES_CRYPT,
- * PHP_EXT_DES_CRYPT, PHP_MD5_CRYPT and PHP_BLOWFISH_CRYPT as appropriate
- * for the target platform. */
-
-#if PHP_STD_DES_CRYPT
-#define PHP_MAX_SALT_LEN 2
-#endif
-
-#if PHP_EXT_DES_CRYPT
-#undef PHP_MAX_SALT_LEN
-#define PHP_MAX_SALT_LEN 9
-#endif
-
-#if PHP_MD5_CRYPT
-#undef PHP_MAX_SALT_LEN
-#define PHP_MAX_SALT_LEN 12
-#endif
-
-#if PHP_BLOWFISH_CRYPT
-#undef PHP_MAX_SALT_LEN
-#define PHP_MAX_SALT_LEN 60
-#endif
-
-#if PHP_SHA512_CRYPT
-#undef PHP_MAX_SALT_LEN
+/* sha512 crypt has the maximal salt length of 123 characters */
#define PHP_MAX_SALT_LEN 123
-#endif
-
-
-/* If the configure-time checks fail, we provide DES.
- * XXX: This is a hack. Fix the real problem! */
-
-#ifndef PHP_MAX_SALT_LEN
-#define PHP_MAX_SALT_LEN 2
-#undef PHP_STD_DES_CRYPT
-#define PHP_STD_DES_CRYPT 1
-#endif
#define PHP_CRYPT_RAND php_rand()
@@ -109,18 +72,12 @@
PHP_MINIT_FUNCTION(crypt) /* {{{ */
{
REGISTER_LONG_CONSTANT("CRYPT_SALT_LENGTH", PHP_MAX_SALT_LEN, CONST_CS | CONST_PERSISTENT);
- REGISTER_LONG_CONSTANT("CRYPT_STD_DES", PHP_STD_DES_CRYPT, CONST_CS | CONST_PERSISTENT);
- REGISTER_LONG_CONSTANT("CRYPT_EXT_DES", PHP_EXT_DES_CRYPT, CONST_CS | CONST_PERSISTENT);
- REGISTER_LONG_CONSTANT("CRYPT_MD5", PHP_MD5_CRYPT, CONST_CS | CONST_PERSISTENT);
- REGISTER_LONG_CONSTANT("CRYPT_BLOWFISH", PHP_BLOWFISH_CRYPT, CONST_CS | CONST_PERSISTENT);
-
-#ifdef PHP_SHA256_CRYPT
- REGISTER_LONG_CONSTANT("CRYPT_SHA256", PHP_SHA256_CRYPT, CONST_CS | CONST_PERSISTENT);
-#endif
-
-#ifdef PHP_SHA512_CRYPT
- REGISTER_LONG_CONSTANT("CRYPT_SHA512", PHP_SHA512_CRYPT, CONST_CS | CONST_PERSISTENT);
-#endif
+ REGISTER_LONG_CONSTANT("CRYPT_STD_DES", 1, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("CRYPT_EXT_DES", 1, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("CRYPT_MD5", 1, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("CRYPT_BLOWFISH", 1, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("CRYPT_SHA256", 1, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("CRYPT_SHA512", 1, CONST_CS | CONST_PERSISTENT);
#if PHP_USE_PHP_CRYPT_R
php_init_crypt_r();
@@ -309,15 +266,10 @@ PHP_FUNCTION(crypt)
/* The automatic salt generation covers standard DES, md5-crypt and Blowfish (simple) */
if (!*salt) {
-#if PHP_MD5_CRYPT
strncpy(salt, "$1$", PHP_MAX_SALT_LEN);
php_to64(&salt[3], PHP_CRYPT_RAND, 4);
php_to64(&salt[7], PHP_CRYPT_RAND, 4);
strncpy(&salt[11], "$", PHP_MAX_SALT_LEN - 11);
-#elif PHP_STD_DES_CRYPT
- php_to64(&salt[0], PHP_CRYPT_RAND, 2);
- salt[2] = '\0';
-#endif
salt_in_len = strlen(salt);
} else {
salt_in_len = MIN(PHP_MAX_SALT_LEN, salt_in_len);
@@ -334,7 +286,6 @@ PHP_FUNCTION(crypt)
RETURN_STR(result);
}
/* }}} */
-#endif
/*
* Local variables:
diff --git a/ext/standard/file.c b/ext/standard/file.c
index cefdc4aecb..eef1287b0f 100644
--- a/ext/standard/file.c
+++ b/ext/standard/file.c
@@ -262,19 +262,19 @@ PHP_MINIT_FUNCTION(file)
REGISTER_LONG_CONSTANT("STREAM_IPPROTO_IP", IPPROTO_IP, CONST_CS|CONST_PERSISTENT);
#endif
-#ifdef IPPROTO_TCP
+#if defined(IPPROTO_TCP) || defined(PHP_WIN32)
REGISTER_LONG_CONSTANT("STREAM_IPPROTO_TCP", IPPROTO_TCP, CONST_CS|CONST_PERSISTENT);
#endif
-#ifdef IPPROTO_UDP
+#if defined(IPPROTO_UDP) || defined(PHP_WIN32)
REGISTER_LONG_CONSTANT("STREAM_IPPROTO_UDP", IPPROTO_UDP, CONST_CS|CONST_PERSISTENT);
#endif
-#ifdef IPPROTO_ICMP
+#if defined(IPPROTO_ICMP) || defined(PHP_WIN32)
REGISTER_LONG_CONSTANT("STREAM_IPPROTO_ICMP", IPPROTO_ICMP, CONST_CS|CONST_PERSISTENT);
#endif
-#ifdef IPPROTO_RAW
+#if defined(IPPROTO_RAW) || defined(PHP_WIN32)
REGISTER_LONG_CONSTANT("STREAM_IPPROTO_RAW", IPPROTO_RAW, CONST_CS|CONST_PERSISTENT);
#endif
@@ -523,7 +523,7 @@ PHP_FUNCTION(file_get_contents)
size_t filename_len;
zend_bool use_include_path = 0;
php_stream *stream;
- zend_long offset = -1;
+ zend_long offset = 0;
zend_long maxlen = (ssize_t) PHP_STREAM_COPY_ALL;
zval *zcontext = NULL;
php_stream_context *context = NULL;
@@ -548,7 +548,7 @@ PHP_FUNCTION(file_get_contents)
RETURN_FALSE;
}
- if (offset > 0 && php_stream_seek(stream, offset, SEEK_SET) < 0) {
+ if (offset != 0 && php_stream_seek(stream, offset, ((offset > 0) ? SEEK_SET : SEEK_END)) < 0) {
php_error_docref(NULL, E_WARNING, "Failed to seek to position " ZEND_LONG_FMT " in the stream", offset);
php_stream_close(stream);
RETURN_FALSE;
diff --git a/ext/standard/ftok.c b/ext/standard/ftok.c
index e9ec6b5eaa..02cf61daf6 100644
--- a/ext/standard/ftok.c
+++ b/ext/standard/ftok.c
@@ -26,6 +26,10 @@
#include <sys/ipc.h>
#endif
+#ifdef PHP_WIN32
+#include "win32/ipc.h"
+#endif
+
#if HAVE_FTOK
/* {{{ proto int ftok(string pathname, string proj)
Convert a pathname and a project identifier to a System V IPC key */
diff --git a/ext/standard/incomplete_class.c b/ext/standard/incomplete_class.c
index 887f209299..d327602e75 100644
--- a/ext/standard/incomplete_class.c
+++ b/ext/standard/incomplete_class.c
@@ -54,7 +54,8 @@ static zval *incomplete_class_get_property(zval *object, zval *member, int type,
incomplete_class_message(object, E_NOTICE);
if (type == BP_VAR_W || type == BP_VAR_RW) {
- return &EG(error_zval);
+ ZVAL_ERROR(rv);
+ return rv;
} else {
return &EG(uninitialized_zval);
}
diff --git a/ext/standard/info.c b/ext/standard/info.c
index c0d8ea91d5..982d4e461b 100644
--- a/ext/standard/info.c
+++ b/ext/standard/info.c
@@ -229,9 +229,11 @@ static void php_print_gpcse_array(char *name, uint name_length)
}
if (Z_TYPE_P(tmp) == IS_ARRAY) {
if (!sapi_module.phpinfo_as_text) {
+ zend_string *str = zend_print_zval_r_to_str(tmp, 0);
php_info_print("<pre>");
- zend_print_zval_r_ex((zend_write_func_t) php_info_print_html_esc, tmp, 0);
+ php_info_print_html_esc(ZSTR_VAL(str), ZSTR_LEN(str));
php_info_print("</pre>");
+ zend_string_release(str);
} else {
zend_print_zval_r(tmp, 0);
}
diff --git a/ext/standard/lcg.c b/ext/standard/lcg.c
index 9f40aba3e2..74b46eadd2 100644
--- a/ext/standard/lcg.c
+++ b/ext/standard/lcg.c
@@ -54,8 +54,8 @@ static void lcg_seed(void);
PHPAPI double php_combined_lcg(void) /* {{{ */
{
- php_int32 q;
- php_int32 z;
+ int32_t q;
+ int32_t z;
if (!LCG(seeded)) {
lcg_seed();
diff --git a/ext/standard/math.c b/ext/standard/math.c
index ebfee8ead8..688ae9f6d1 100644
--- a/ext/standard/math.c
+++ b/ext/standard/math.c
@@ -93,18 +93,6 @@ static inline double php_intpow10(int power) {
}
/* }}} */
-/* {{{ php_math_is_finite */
-static inline int php_math_is_finite(double value) {
-#if defined(PHP_WIN32)
- return _finite(value);
-#elif defined(isfinite)
- return isfinite(value);
-#else
- return value == value && (value == 0. || value * 2. != value);
-#endif
-}
-/* }}} */
-
/* {{{ php_round_helper
Actually performs the rounding of a value to integer in a certain mode */
static inline double php_round_helper(double value, int mode) {
@@ -142,7 +130,7 @@ PHPAPI double _php_math_round(double value, int places, int mode) {
double tmp_value;
int precision_places;
- if (!php_math_is_finite(value)) {
+ if (!zend_finite(value)) {
return value;
}
diff --git a/ext/standard/md5.c b/ext/standard/md5.c
index 2489b9971a..825df21c69 100644
--- a/ext/standard/md5.c
+++ b/ext/standard/md5.c
@@ -169,16 +169,16 @@ PHP_NAMED_FUNCTION(php_if_md5_file)
*/
#if defined(__i386__) || defined(__x86_64__) || defined(__vax__)
# define SET(n) \
- (*(php_uint32 *)&ptr[(n) * 4])
+ (*(uint32_t *)&ptr[(n) * 4])
# define GET(n) \
SET(n)
#else
# define SET(n) \
(ctx->block[(n)] = \
- (php_uint32)ptr[(n) * 4] | \
- ((php_uint32)ptr[(n) * 4 + 1] << 8) | \
- ((php_uint32)ptr[(n) * 4 + 2] << 16) | \
- ((php_uint32)ptr[(n) * 4 + 3] << 24))
+ (uint32_t)ptr[(n) * 4] | \
+ ((uint32_t)ptr[(n) * 4 + 1] << 8) | \
+ ((uint32_t)ptr[(n) * 4 + 2] << 16) | \
+ ((uint32_t)ptr[(n) * 4 + 3] << 24))
# define GET(n) \
(ctx->block[(n)])
#endif
@@ -190,8 +190,8 @@ PHP_NAMED_FUNCTION(php_if_md5_file)
static const void *body(PHP_MD5_CTX *ctx, const void *data, size_t size)
{
const unsigned char *ptr;
- php_uint32 a, b, c, d;
- php_uint32 saved_a, saved_b, saved_c, saved_d;
+ uint32_t a, b, c, d;
+ uint32_t saved_a, saved_b, saved_c, saved_d;
ptr = data;
@@ -307,8 +307,8 @@ PHPAPI void PHP_MD5Init(PHP_MD5_CTX *ctx)
PHPAPI void PHP_MD5Update(PHP_MD5_CTX *ctx, const void *data, size_t size)
{
- php_uint32 saved_lo;
- php_uint32 used, free;
+ uint32_t saved_lo;
+ uint32_t used, free;
saved_lo = ctx->lo;
if ((ctx->lo = (saved_lo + size) & 0x1fffffff) < saved_lo) {
@@ -342,7 +342,7 @@ PHPAPI void PHP_MD5Update(PHP_MD5_CTX *ctx, const void *data, size_t size)
PHPAPI void PHP_MD5Final(unsigned char *result, PHP_MD5_CTX *ctx)
{
- php_uint32 used, free;
+ uint32_t used, free;
used = ctx->lo & 0x3f;
diff --git a/ext/standard/md5.h b/ext/standard/md5.h
index 4210483864..5acd83ecbe 100644
--- a/ext/standard/md5.h
+++ b/ext/standard/md5.h
@@ -42,10 +42,10 @@ PHP_NAMED_FUNCTION(php_if_md5_file);
/* MD5 context. */
typedef struct {
- php_uint32 lo, hi;
- php_uint32 a, b, c, d;
+ uint32_t lo, hi;
+ uint32_t a, b, c, d;
unsigned char buffer[64];
- php_uint32 block[16];
+ uint32_t block[16];
} PHP_MD5_CTX;
PHPAPI void PHP_MD5Init(PHP_MD5_CTX *ctx);
diff --git a/ext/standard/pack.c b/ext/standard/pack.c
index a24ee69ad2..1d353d1743 100644
--- a/ext/standard/pack.c
+++ b/ext/standard/pack.c
@@ -551,9 +551,10 @@ PHP_FUNCTION(unpack)
zend_string *formatarg, *inputarg;
zend_long formatlen, inputpos, inputlen;
int i;
+ zend_long offset = 0;
- if (zend_parse_parameters(ZEND_NUM_ARGS(), "SS", &formatarg,
- &inputarg) == FAILURE) {
+ if (zend_parse_parameters(ZEND_NUM_ARGS(), "SS|l", &formatarg,
+ &inputarg, &offset) == FAILURE) {
return;
}
@@ -563,6 +564,14 @@ PHP_FUNCTION(unpack)
inputlen = ZSTR_LEN(inputarg);
inputpos = 0;
+
+ if (offset < 0 || offset > inputlen) {
+ php_error_docref(NULL, E_WARNING, "Offset " ZEND_LONG_FMT " is out of input range" , offset);
+ RETURN_FALSE;
+ }
+ input += offset;
+ inputlen -= offset;
+
array_init(return_value);
while (formatlen-- > 0) {
diff --git a/ext/standard/password.c b/ext/standard/password.c
index a46f4889e5..2a5cec3e93 100644
--- a/ext/standard/password.c
+++ b/ext/standard/password.c
@@ -21,7 +21,6 @@
#include <stdlib.h>
#include "php.h"
-#if HAVE_CRYPT
#include "fcntl.h"
#include "php_password.h"
@@ -384,7 +383,6 @@ PHP_FUNCTION(password_hash)
}
/* }}} */
-#endif /* HAVE_CRYPT */
/*
* Local variables:
* tab-width: 4
diff --git a/ext/standard/php_crypt.h b/ext/standard/php_crypt.h
index bf7a13333f..cff71d867b 100644
--- a/ext/standard/php_crypt.h
+++ b/ext/standard/php_crypt.h
@@ -25,11 +25,9 @@
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);
PHP_MSHUTDOWN_FUNCTION(crypt);
PHP_RINIT_FUNCTION(crypt);
-#endif
#endif
diff --git a/ext/standard/php_crypt_r.c b/ext/standard/php_crypt_r.c
index a8ca39b08e..8b97d4a496 100644
--- a/ext/standard/php_crypt_r.c
+++ b/ext/standard/php_crypt_r.c
@@ -323,7 +323,7 @@ char * php_md5_crypt_r(const char *pw, const char *salt, char *out)
unsigned char final[16];
unsigned int i, sl, pwl;
PHP_MD5_CTX ctx, ctx1;
- php_uint32 l;
+ uint32_t l;
int pl;
pwl = strlen(pw);
diff --git a/ext/standard/php_lcg.h b/ext/standard/php_lcg.h
index 60850d6c7c..2c24756012 100644
--- a/ext/standard/php_lcg.h
+++ b/ext/standard/php_lcg.h
@@ -24,8 +24,8 @@
#include "ext/standard/basic_functions.h"
typedef struct {
- php_int32 s1;
- php_int32 s2;
+ int32_t s1;
+ int32_t s2;
int seeded;
} php_lcg_globals;
diff --git a/ext/standard/php_rand.h b/ext/standard/php_rand.h
index 44a40a7c3b..39ba00eb56 100644
--- a/ext/standard/php_rand.h
+++ b/ext/standard/php_rand.h
@@ -55,7 +55,7 @@
PHPAPI void php_srand(zend_long seed);
PHPAPI zend_long php_rand(void);
-PHPAPI void php_mt_srand(php_uint32 seed);
-PHPAPI php_uint32 php_mt_rand(void);
+PHPAPI void php_mt_srand(uint32_t seed);
+PHPAPI uint32_t php_mt_rand(void);
#endif /* PHP_RAND_H */
diff --git a/ext/standard/php_smart_string.h b/ext/standard/php_smart_string.h
index a832376064..58c319a5a5 100644
--- a/ext/standard/php_smart_string.h
+++ b/ext/standard/php_smart_string.h
@@ -93,7 +93,7 @@
smart_string_append_unsigned_ex((dest), (val), 0)
#define smart_string_appendc_ex(dest, ch, what) do { \
- register size_t __nl; \
+ size_t __nl; \
smart_string_alloc4((dest), 1, (what), __nl); \
(dest)->len = __nl; \
((unsigned char *) (dest)->c)[(dest)->len - 1] = (ch); \
@@ -109,7 +109,7 @@
} while (0)
#define smart_string_appendl_ex(dest, src, nlen, what) do { \
- register size_t __nl; \
+ size_t __nl; \
smart_string *__dest = (smart_string *) (dest); \
\
smart_string_alloc4(__dest, (nlen), (what), __nl); \
diff --git a/ext/standard/proc_open.c b/ext/standard/proc_open.c
index 700d4e862c..6969faebe9 100644
--- a/ext/standard/proc_open.c
+++ b/ext/standard/proc_open.c
@@ -77,13 +77,13 @@ static php_process_env_t _php_array_to_envp(zval *environment, int is_persistent
{
zval *element;
php_process_env_t env;
- zend_string *string_key;
+ zend_string *key, *str;
#ifndef PHP_WIN32
char **ep;
#endif
char *p;
- size_t cnt, l, sizeenv=0;
- HashTable *target_hash;
+ size_t cnt, l, sizeenv = 0;
+ HashTable *env_hash;
memset(&env, 0, sizeof(env));
@@ -101,28 +101,25 @@ static php_process_env_t _php_array_to_envp(zval *environment, int is_persistent
return env;
}
- target_hash = Z_ARRVAL_P(environment);
- if (!target_hash) {
- return env;
- }
+ ALLOC_HASHTABLE(env_hash);
+ zend_hash_init(env_hash, cnt, NULL, NULL, 0);
/* first, we have to get the size of all the elements in the hash */
- ZEND_HASH_FOREACH_STR_KEY_VAL(target_hash, string_key, element) {
- zend_string *str = zval_get_string(element);
- size_t el_len = ZSTR_LEN(str);
- zend_string_release(str);
+ ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(environment), key, element) {
+ str = zval_get_string(element);
- if (el_len == 0) {
+ if (ZSTR_LEN(str) == 0) {
+ zend_string_release(str);
continue;
}
- sizeenv += el_len + 1;
+ sizeenv += ZSTR_LEN(str) + 1;
- if (string_key) {
- if (ZSTR_LEN(string_key) == 0) {
- continue;
- }
- sizeenv += ZSTR_LEN(string_key) + 1;
+ if (key && ZSTR_LEN(key)) {
+ sizeenv += ZSTR_LEN(key) + 1;
+ zend_hash_add_ptr(env_hash, key, str);
+ } else {
+ zend_hash_next_index_insert_ptr(env_hash, str);
}
} ZEND_HASH_FOREACH_END();
@@ -131,20 +128,10 @@ static php_process_env_t _php_array_to_envp(zval *environment, int is_persistent
#endif
p = env.envp = (char *) pecalloc(sizeenv + 4, 1, is_persistent);
- ZEND_HASH_FOREACH_STR_KEY_VAL(target_hash, string_key, element) {
- zend_string *str = zval_get_string(element);
-
- if (ZSTR_LEN(str) == 0) {
- goto next_element;
- }
-
- if (string_key) {
- if (ZSTR_LEN(string_key) == 0) {
- goto next_element;
- }
-
- l = ZSTR_LEN(string_key) + ZSTR_LEN(str) + 2;
- memcpy(p, ZSTR_VAL(string_key), ZSTR_LEN(string_key));
+ ZEND_HASH_FOREACH_STR_KEY_PTR(env_hash, key, str) {
+ if (key) {
+ l = ZSTR_LEN(key) + ZSTR_LEN(str) + 2;
+ memcpy(p, ZSTR_VAL(key), ZSTR_LEN(key));
strncat(p, "=", 1);
strncat(p, ZSTR_VAL(str), ZSTR_LEN(str));
@@ -161,12 +148,14 @@ static php_process_env_t _php_array_to_envp(zval *environment, int is_persistent
#endif
p += ZSTR_LEN(str) + 1;
}
-next_element:
zend_string_release(str);
} ZEND_HASH_FOREACH_END();
assert((uint)(p - env.envp) <= sizeenv);
+ zend_hash_destroy(env_hash);
+ FREE_HASHTABLE(env_hash);
+
return env;
}
/* }}} */
diff --git a/ext/standard/rand.c b/ext/standard/rand.c
index 2dd05e70bb..50729f2418 100644
--- a/ext/standard/rand.c
+++ b/ext/standard/rand.c
@@ -146,19 +146,19 @@ PHPAPI zend_long php_rand(void)
#define loBits(u) ((u) & 0x7FFFFFFFU) /* mask the highest bit of u */
#define mixBits(u, v) (hiBit(u)|loBits(v)) /* move hi bit of u to hi bit of v */
-#define twist(m,u,v) (m ^ (mixBits(u,v)>>1) ^ ((php_uint32)(-(php_int32)(loBit(u))) & 0x9908b0dfU))
+#define twist(m,u,v) (m ^ (mixBits(u,v)>>1) ^ ((uint32_t)(-(int32_t)(loBit(u))) & 0x9908b0dfU))
/* {{{ php_mt_initialize
*/
-static inline void php_mt_initialize(php_uint32 seed, php_uint32 *state)
+static inline void php_mt_initialize(uint32_t seed, uint32_t *state)
{
/* Initialize generator state with seed
See Knuth TAOCP Vol 2, 3rd Ed, p.106 for multiplier.
In previous versions, most significant bits (MSBs) of the seed affect
only MSBs of the state array. Modified 9 Jan 2002 by Makoto Matsumoto. */
- register php_uint32 *s = state;
- register php_uint32 *r = state;
+ register uint32_t *s = state;
+ register uint32_t *r = state;
register int i = 1;
*s++ = seed & 0xffffffffU;
@@ -176,8 +176,8 @@ static inline void php_mt_reload(void)
/* Generate N new values in state
Made clearer and faster by Matthew Bellew (matthew.bellew@home.com) */
- register php_uint32 *state = BG(state);
- register php_uint32 *p = state;
+ register uint32_t *state = BG(state);
+ register uint32_t *p = state;
register int i;
for (i = N - M; i--; ++p)
@@ -192,7 +192,7 @@ static inline void php_mt_reload(void)
/* {{{ php_mt_srand
*/
-PHPAPI void php_mt_srand(php_uint32 seed)
+PHPAPI void php_mt_srand(uint32_t seed)
{
/* Seed the generator with a simple uint32 */
php_mt_initialize(seed, BG(state));
@@ -205,12 +205,12 @@ PHPAPI void php_mt_srand(php_uint32 seed)
/* {{{ php_mt_rand
*/
-PHPAPI php_uint32 php_mt_rand(void)
+PHPAPI uint32_t php_mt_rand(void)
{
/* Pull a 32-bit integer from the generator state
Every other access function simply transforms the numbers extracted here */
- register php_uint32 s1;
+ register uint32_t s1;
if (BG(left) == 0) {
php_mt_reload();
diff --git a/ext/standard/sha1.c b/ext/standard/sha1.c
index ad4f4a5603..c4bed51abc 100644
--- a/ext/standard/sha1.c
+++ b/ext/standard/sha1.c
@@ -103,9 +103,9 @@ PHP_FUNCTION(sha1_file)
/* }}} */
-static void SHA1Transform(php_uint32[5], const unsigned char[64]);
-static void SHA1Encode(unsigned char *, php_uint32 *, unsigned int);
-static void SHA1Decode(php_uint32 *, const unsigned char *, unsigned int);
+static void SHA1Transform(uint32_t[5], const unsigned char[64]);
+static void SHA1Encode(unsigned char *, uint32_t *, unsigned int);
+static void SHA1Decode(uint32_t *, const unsigned char *, unsigned int);
static unsigned char PADDING[64] =
{
@@ -133,22 +133,22 @@ static unsigned char PADDING[64] =
/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4.
*/
#define FF(a, b, c, d, e, w) { \
- (e) += F ((b), (c), (d)) + (w) + (php_uint32)(0x5A827999); \
+ (e) += F ((b), (c), (d)) + (w) + (uint32_t)(0x5A827999); \
(e) += ROTATE_LEFT ((a), 5); \
(b) = ROTATE_LEFT((b), 30); \
}
#define GG(a, b, c, d, e, w) { \
- (e) += G ((b), (c), (d)) + (w) + (php_uint32)(0x6ED9EBA1); \
+ (e) += G ((b), (c), (d)) + (w) + (uint32_t)(0x6ED9EBA1); \
(e) += ROTATE_LEFT ((a), 5); \
(b) = ROTATE_LEFT((b), 30); \
}
#define HH(a, b, c, d, e, w) { \
- (e) += H ((b), (c), (d)) + (w) + (php_uint32)(0x8F1BBCDC); \
+ (e) += H ((b), (c), (d)) + (w) + (uint32_t)(0x8F1BBCDC); \
(e) += ROTATE_LEFT ((a), 5); \
(b) = ROTATE_LEFT((b), 30); \
}
#define II(a, b, c, d, e, w) { \
- (e) += I ((b), (c), (d)) + (w) + (php_uint32)(0xCA62C1D6); \
+ (e) += I ((b), (c), (d)) + (w) + (uint32_t)(0xCA62C1D6); \
(e) += ROTATE_LEFT ((a), 5); \
(b) = ROTATE_LEFT((b), 30); \
}
@@ -184,10 +184,10 @@ PHPAPI void PHP_SHA1Update(PHP_SHA1_CTX * context, const unsigned char *input,
index = (unsigned int) ((context->count[0] >> 3) & 0x3F);
/* Update number of bits */
- if ((context->count[0] += ((php_uint32) inputLen << 3))
- < ((php_uint32) inputLen << 3))
+ if ((context->count[0] += ((uint32_t) inputLen << 3))
+ < ((uint32_t) inputLen << 3))
context->count[1]++;
- context->count[1] += ((php_uint32) inputLen >> 29);
+ context->count[1] += ((uint32_t) inputLen >> 29);
partLen = 64 - index;
@@ -253,11 +253,11 @@ PHPAPI void PHP_SHA1Final(unsigned char digest[20], PHP_SHA1_CTX * context)
* SHA1 basic transformation. Transforms state based on block.
*/
static void SHA1Transform(state, block)
-php_uint32 state[5];
+uint32_t state[5];
const unsigned char block[64];
{
- php_uint32 a = state[0], b = state[1], c = state[2];
- php_uint32 d = state[3], e = state[4], x[16], tmp;
+ uint32_t a = state[0], b = state[1], c = state[2];
+ uint32_t d = state[3], e = state[4], x[16], tmp;
SHA1Decode(x, block, 64);
@@ -361,12 +361,12 @@ const unsigned char block[64];
/* }}} */
/* {{{ SHA1Encode
- Encodes input (php_uint32) into output (unsigned char). Assumes len is
+ Encodes input (uint32_t) into output (unsigned char). Assumes len is
a multiple of 4.
*/
static void SHA1Encode(output, input, len)
unsigned char *output;
-php_uint32 *input;
+uint32_t *input;
unsigned int len;
{
unsigned int i, j;
@@ -381,19 +381,19 @@ unsigned int len;
/* }}} */
/* {{{ SHA1Decode
- Decodes input (unsigned char) into output (php_uint32). Assumes len is
+ Decodes input (unsigned char) into output (uint32_t). Assumes len is
a multiple of 4.
*/
static void SHA1Decode(output, input, len)
-php_uint32 *output;
+uint32_t *output;
const unsigned char *input;
unsigned int len;
{
unsigned int i, j;
for (i = 0, j = 0; j < len; i++, j += 4)
- output[i] = ((php_uint32) input[j + 3]) | (((php_uint32) input[j + 2]) << 8) |
- (((php_uint32) input[j + 1]) << 16) | (((php_uint32) input[j]) << 24);
+ output[i] = ((uint32_t) input[j + 3]) | (((uint32_t) input[j + 2]) << 8) |
+ (((uint32_t) input[j + 1]) << 16) | (((uint32_t) input[j]) << 24);
}
/* }}} */
diff --git a/ext/standard/sha1.h b/ext/standard/sha1.h
index d2cc1b70b9..de338fad38 100644
--- a/ext/standard/sha1.h
+++ b/ext/standard/sha1.h
@@ -25,8 +25,8 @@
/* SHA1 context. */
typedef struct {
- php_uint32 state[5]; /* state (ABCD) */
- php_uint32 count[2]; /* number of bits, modulo 2^64 (lsb first) */
+ uint32_t state[5]; /* state (ABCD) */
+ uint32_t count[2]; /* number of bits, modulo 2^64 (lsb first) */
unsigned char buffer[64]; /* input buffer */
} PHP_SHA1_CTX;
diff --git a/ext/standard/string.c b/ext/standard/string.c
index 5219df915d..2b753727d7 100644
--- a/ext/standard/string.c
+++ b/ext/standard/string.c
@@ -1439,7 +1439,7 @@ PHPAPI zend_string *php_string_toupper(zend_string *s)
e = c + ZSTR_LEN(s);
while (c < e) {
- if (!isupper(*c)) {
+ if (islower(*c)) {
register unsigned char *r;
zend_string *res = zend_string_alloc(ZSTR_LEN(s), 0);
@@ -1508,7 +1508,7 @@ PHPAPI zend_string *php_string_tolower(zend_string *s)
e = c + ZSTR_LEN(s);
while (c < e) {
- if (!islower(*c)) {
+ if (isupper(*c)) {
register unsigned char *r;
zend_string *res = zend_string_alloc(ZSTR_LEN(s), 0);
@@ -1968,6 +1968,9 @@ PHP_FUNCTION(strpos)
ZEND_PARSE_PARAMETERS_END();
#endif
+ if (offset < 0) {
+ offset += (zend_long)ZSTR_LEN(haystack);
+ }
if (offset < 0 || (size_t)offset > ZSTR_LEN(haystack)) {
php_error_docref(NULL, E_WARNING, "Offset not contained in string");
RETURN_FALSE;
@@ -2018,6 +2021,9 @@ PHP_FUNCTION(stripos)
return;
}
+ if (offset < 0) {
+ offset += (zend_long)ZSTR_LEN(haystack);
+ }
if (offset < 0 || (size_t)offset > ZSTR_LEN(haystack)) {
php_error_docref(NULL, E_WARNING, "Offset not contained in string");
RETURN_FALSE;
@@ -2426,10 +2432,6 @@ PHP_FUNCTION(substr)
}
}
- if (f > (zend_long)ZSTR_LEN(str)) {
- RETURN_FALSE;
- }
-
if ((f + l) > (zend_long)ZSTR_LEN(str)) {
l = ZSTR_LEN(str) - f;
}
@@ -2745,7 +2747,8 @@ PHP_FUNCTION(quotemeta)
/* }}} */
/* {{{ proto int ord(string character)
- Returns ASCII value of character */
+ Returns ASCII value of character
+ Warning: This function is special-cased by zend_compile.c and so is bypassed for constant string argument */
PHP_FUNCTION(ord)
{
char *str;
@@ -2766,7 +2769,8 @@ PHP_FUNCTION(ord)
/* }}} */
/* {{{ proto string chr(int ascii)
- Converts ASCII code to a character */
+ Converts ASCII code to a character
+ Warning: This function is special-cased by zend_compile.c and so is bypassed for constant integer argument */
PHP_FUNCTION(chr)
{
zend_long c;
@@ -4601,10 +4605,18 @@ PHP_FUNCTION(parse_str)
if (arrayArg == NULL) {
zval tmp;
- zend_array *symbol_table = zend_rebuild_symbol_table();
+ zend_array *symbol_table;
+ if (zend_forbid_dynamic_call("parse_str() with a single argument") == FAILURE) {
+ efree(res);
+ return;
+ }
+ symbol_table = zend_rebuild_symbol_table();
ZVAL_ARR(&tmp, symbol_table);
sapi_module.treat_data(PARSE_STRING, res, &tmp);
+ if (UNEXPECTED(zend_hash_del(symbol_table, CG(known_strings)[ZEND_STR_THIS]) == SUCCESS)) {
+ zend_throw_error(NULL, "Cannot re-assign $this");
+ }
} else {
zval ret;
@@ -5291,12 +5303,10 @@ PHP_FUNCTION(substr_count)
endp = p + haystack_len;
if (offset < 0) {
- php_error_docref(NULL, E_WARNING, "Offset should be greater than or equal to 0");
- RETURN_FALSE;
+ offset += (zend_long)haystack_len;
}
-
- if ((size_t)offset > haystack_len) {
- php_error_docref(NULL, E_WARNING, "Offset value " ZEND_LONG_FMT " exceeds string length", offset);
+ if ((offset < 0) || ((size_t)offset > haystack_len)) {
+ php_error_docref(NULL, E_WARNING, "Offset not contained in string");
RETURN_FALSE;
}
p += offset;
@@ -5304,11 +5314,10 @@ PHP_FUNCTION(substr_count)
if (ac == 4) {
if (length <= 0) {
- php_error_docref(NULL, E_WARNING, "Length should be greater than 0");
- RETURN_FALSE;
+ length += (haystack_len - offset);
}
- if (length > (haystack_len - offset)) {
- php_error_docref(NULL, E_WARNING, "Length value " ZEND_LONG_FMT " exceeds string length", length);
+ if ((length <= 0) || (length > (haystack_len - offset))) {
+ php_error_docref(NULL, E_WARNING, "Invalid length value");
RETURN_FALSE;
}
endp = p + length;
diff --git a/ext/standard/tests/array/array_filter_variation10.phpt b/ext/standard/tests/array/array_filter_variation10.phpt
index f0a6115f79..b23618794e 100644
--- a/ext/standard/tests/array/array_filter_variation10.phpt
+++ b/ext/standard/tests/array/array_filter_variation10.phpt
@@ -34,7 +34,11 @@ var_dump( array_filter($input, 'dump2', true) );
echo "*** Testing array_filter() : usage variations - 'callback' expecting second argument ***\n";
-var_dump( array_filter($small, 'dump', false) );
+try {
+ var_dump( array_filter($small, 'dump', false) );
+} catch (Throwable $e) {
+ echo "Exception: " . $e->getMessage() . "\n";
+}
echo "*** Testing array_filter() with various use types ***\n";
@@ -70,13 +74,7 @@ array(3) {
NULL
}
*** Testing array_filter() : usage variations - 'callback' expecting second argument ***
-
-Warning: Missing argument 2 for dump() in %s on line %d
-
-Notice: Undefined variable: key in %s on line %d
- = 123
-array(0) {
-}
+Exception: Too few arguments to function dump(), 1 passed and exactly 2 expected
*** Testing array_filter() with various use types ***
array(2) {
[1]=>
@@ -91,13 +89,13 @@ array(2) {
int(2)
}
-Warning: is_numeric() expects exactly 1 parameter, 2 given in %s on line 44
+Warning: is_numeric() expects exactly 1 parameter, 2 given in %s on line 48
-Warning: is_numeric() expects exactly 1 parameter, 2 given in %s on line 44
+Warning: is_numeric() expects exactly 1 parameter, 2 given in %s on line 48
-Warning: is_numeric() expects exactly 1 parameter, 2 given in %s on line 44
+Warning: is_numeric() expects exactly 1 parameter, 2 given in %s on line 48
-Warning: is_numeric() expects exactly 1 parameter, 2 given in %s on line 44
+Warning: is_numeric() expects exactly 1 parameter, 2 given in %s on line 48
array(0) {
}
Done
diff --git a/ext/standard/tests/array/array_map_error.phpt b/ext/standard/tests/array/array_map_error.phpt
index 7c623ec4ea..56dd033521 100644
--- a/ext/standard/tests/array/array_map_error.phpt
+++ b/ext/standard/tests/array/array_map_error.phpt
@@ -18,14 +18,22 @@ echo "\n-- Testing array_map() function with one less than expected no. of argum
function callback1() {
return 1;
}
-var_dump( array_map('callback1') );
+try {
+ var_dump( array_map('callback1') );
+} catch (Throwable $e) {
+ echo "Exception: " . $e->getMessage() . "\n";
+}
echo "\n-- Testing array_map() function with less no. of arrays than callback function arguments --\n";
$arr1 = array(1, 2);
function callback2($p, $q) {
return $p * $q;
}
-var_dump( array_map('callback2', $arr1) );
+try {
+ var_dump( array_map('callback2', $arr1) );
+} catch (Throwable $e) {
+ echo "Exception: " . $e->getMessage() . "\n";
+}
echo "\n-- Testing array_map() function with more no. of arrays than callback function arguments --\n";
$arr2 = array(3, 4);
@@ -48,20 +56,7 @@ Warning: array_map() expects at least 2 parameters, 1 given in %s on line %d%d
NULL
-- Testing array_map() function with less no. of arrays than callback function arguments --
-
-Warning: Missing argument 2 for callback2() in %s on line %d%d
-
-Notice: Undefined variable: q in %s on line %d%d
-
-Warning: Missing argument 2 for callback2() in %s on line %d%d
-
-Notice: Undefined variable: q in %s on line %d%d
-array(2) {
- [0]=>
- int(0)
- [1]=>
- int(0)
-}
+Exception: Too few arguments to function callback2(), 1 passed and exactly 2 expected
-- Testing array_map() function with more no. of arrays than callback function arguments --
array(2) {
diff --git a/ext/standard/tests/array/array_map_variation10.phpt b/ext/standard/tests/array/array_map_variation10.phpt
index cc75436999..ecf9157620 100644
--- a/ext/standard/tests/array/array_map_variation10.phpt
+++ b/ext/standard/tests/array/array_map_variation10.phpt
@@ -20,7 +20,11 @@ echo "-- anonymous function with all parameters and body --\n";
var_dump( array_map( create_function('$a, $b', 'return array($a, $b);'), $array1, $array2));
echo "-- anonymous function with two parameters and passing one array --\n";
-var_dump( array_map( create_function('$a, $b', 'return array($a, $b);'), $array1));
+try {
+ var_dump( array_map( create_function('$a, $b', 'return array($a, $b);'), $array1));
+} catch (Throwable $e) {
+ echo "Exception: " . $e->getMessage() . "\n";
+}
echo "-- anonymous function with NULL parameter --\n";
var_dump( array_map( create_function(NULL, 'return NULL;'), $array1));
@@ -60,41 +64,7 @@ array(3) {
}
}
-- anonymous function with two parameters and passing one array --
-
-Warning: Missing argument 2 for __lambda_func() in %s(20) : runtime-created function on line %d
-
-Notice: Undefined variable: b in %s(20) : runtime-created function on line %d
-
-Warning: Missing argument 2 for __lambda_func() in %s(20) : runtime-created function on line %d
-
-Notice: Undefined variable: b in %s(20) : runtime-created function on line %d
-
-Warning: Missing argument 2 for __lambda_func() in %s(20) : runtime-created function on line %d
-
-Notice: Undefined variable: b in %s(20) : runtime-created function on line %d
-array(3) {
- [0]=>
- array(2) {
- [0]=>
- int(1)
- [1]=>
- NULL
- }
- [1]=>
- array(2) {
- [0]=>
- int(2)
- [1]=>
- NULL
- }
- [2]=>
- array(2) {
- [0]=>
- int(3)
- [1]=>
- NULL
- }
-}
+Exception: Too few arguments to function __lambda_func(), 1 passed and exactly 2 expected
-- anonymous function with NULL parameter --
array(3) {
[0]=>
diff --git a/ext/standard/tests/array/array_map_variation9.phpt b/ext/standard/tests/array/array_map_variation9.phpt
index f029beccd6..f33b717c6c 100644
--- a/ext/standard/tests/array/array_map_variation9.phpt
+++ b/ext/standard/tests/array/array_map_variation9.phpt
@@ -29,7 +29,11 @@ echo "-- checking binary safe array with one parameter callback function --\n";
var_dump( array_map('callback1', $arr1) );
echo "-- checking binary safe array with two parameter callback function --\n";
-var_dump( array_map(b"callback2", $arr1) );
+try {
+ var_dump( array_map(b"callback2", $arr1) );
+} catch (Throwable $e) {
+ echo "Exception: " . $e->getMessage() . "\n";
+}
echo "Done";
?>
@@ -47,42 +51,5 @@ array(4) {
string(5) "22.22"
}
-- checking binary safe array with two parameter callback function --
-
-Warning: Missing argument 2 for callback2() in %s on line %d%d
-
-Notice: Undefined variable: b in %s on line %d%d
-
-Warning: Missing argument 2 for callback2() in %s on line %d%d
-
-Notice: Undefined variable: b in %s on line %d%d
-
-Warning: Missing argument 2 for callback2() in %s on line %d%d
-
-Notice: Undefined variable: b in %s on line %d%d
-
-Warning: Missing argument 2 for callback2() in %s on line %d%d
-
-Notice: Undefined variable: b in %s on line %d%d
-array(4) {
- [0]=>
- array(1) {
- ["hello"]=>
- NULL
- }
- [1]=>
- array(1) {
- ["world"]=>
- NULL
- }
- [2]=>
- array(1) {
- [1]=>
- NULL
- }
- [3]=>
- array(1) {
- ["22.22"]=>
- NULL
- }
-}
+Exception: Too few arguments to function callback2(), 1 passed and exactly 2 expected
Done
diff --git a/ext/standard/tests/array/array_reduce_variation1.phpt b/ext/standard/tests/array/array_reduce_variation1.phpt
index b02a82a7ca..adffeb53d4 100644
--- a/ext/standard/tests/array/array_reduce_variation1.phpt
+++ b/ext/standard/tests/array/array_reduce_variation1.phpt
@@ -25,7 +25,11 @@ echo "\n--- Testing with a callback with too few parameters ---\n";
var_dump(array_reduce($array, "oneArg", 2));
echo "\n--- Testing with a callback with too many parameters ---\n";
-var_dump(array_reduce($array, "threeArgs", 2));
+try {
+ var_dump(array_reduce($array, "threeArgs", 2));
+} catch (Throwable $e) {
+ echo "Exception: " . $e->getMessage() . "\n";
+}
?>
===DONE===
@@ -36,9 +40,5 @@ var_dump(array_reduce($array, "threeArgs", 2));
int(2)
--- Testing with a callback with too many parameters ---
-
-Warning: Missing argument 3 for threeArgs() in %sarray_reduce_variation1.php on line %d
-
-Notice: Undefined variable: x in %sarray_reduce_variation1.php on line %d
-int(3)
-===DONE=== \ No newline at end of file
+Exception: Too few arguments to function threeArgs(), 2 passed and exactly 3 expected
+===DONE===
diff --git a/ext/standard/tests/array/array_udiff_assoc_variation5.phpt b/ext/standard/tests/array/array_udiff_assoc_variation5.phpt
index 69380767bb..6b7f014467 100644
--- a/ext/standard/tests/array/array_udiff_assoc_variation5.phpt
+++ b/ext/standard/tests/array/array_udiff_assoc_variation5.phpt
@@ -24,7 +24,11 @@ echo "\n-- comparison function taking too many parameters --\n";
function too_many_parameters ($val1, $val2, $val3) {
return 1;
}
-var_dump(array_udiff_assoc($arr1, $arr2, 'too_many_parameters'));
+try {
+ var_dump(array_udiff_assoc($arr1, $arr2, 'too_many_parameters'));
+} catch (Throwable $e) {
+ echo "Exception: " . $e->getMessage() . "\n";
+}
echo "\n-- comparison function taking too few parameters --\n";
function too_few_parameters ($val1) {
@@ -32,7 +36,6 @@ function too_few_parameters ($val1) {
}
var_dump(array_udiff_assoc($arr1, $arr2, 'too_few_parameters'));
-
?>
===DONE===
--EXPECTF--
@@ -45,12 +48,7 @@ array(1) {
}
-- comparison function taking too many parameters --
-
-Warning: Missing argument 3 for too_many_parameters() in %sarray_udiff_assoc_variation5.php on line %d
-array(1) {
- [0]=>
- int(1)
-}
+Exception: Too few arguments to function too_many_parameters(), 2 passed and exactly 3 expected
-- comparison function taking too few parameters --
array(1) {
diff --git a/ext/standard/tests/array/array_udiff_uassoc_variation6.phpt b/ext/standard/tests/array/array_udiff_uassoc_variation6.phpt
index ec752a31bb..bf2c0f3d2d 100644
--- a/ext/standard/tests/array/array_udiff_uassoc_variation6.phpt
+++ b/ext/standard/tests/array/array_udiff_uassoc_variation6.phpt
@@ -23,7 +23,11 @@ echo "\n-- comparison function taking too many parameters --\n";
function too_many_parameters ($val1, $val2, $val3) {
return 1;
}
-var_dump(array_udiff_uassoc($arr1, $arr2, 'too_many_parameters', 'too_many_parameters'));
+try {
+ var_dump(array_udiff_uassoc($arr1, $arr2, 'too_many_parameters', 'too_many_parameters'));
+} catch (Throwable $e) {
+ echo "Exception: " . $e->getMessage() . "\n";
+}
echo "\n-- comparison function taking too few parameters --\n";
function too_few_parameters ($val1) {
@@ -43,12 +47,7 @@ array(1) {
}
-- comparison function taking too many parameters --
-
-Warning: Missing argument 3 for too_many_parameters() in %sarray_udiff_uassoc_variation6.php on line %d
-array(1) {
- [0]=>
- int(1)
-}
+Exception: Too few arguments to function too_many_parameters(), 2 passed and exactly 3 expected
-- comparison function taking too few parameters --
array(1) {
diff --git a/ext/standard/tests/array/array_udiff_variation5.phpt b/ext/standard/tests/array/array_udiff_variation5.phpt
index 49405d40be..ce6362fc08 100644
--- a/ext/standard/tests/array/array_udiff_variation5.phpt
+++ b/ext/standard/tests/array/array_udiff_variation5.phpt
@@ -24,7 +24,11 @@ echo "\n-- comparison function taking too many parameters --\n";
function too_many_parameters ($val1, $val2, $val3) {
return 0;
}
-var_dump(array_udiff($arr1, $arr2, 'too_many_parameters'));
+try {
+ var_dump(array_udiff($arr1, $arr2, 'too_many_parameters'));
+} catch (Throwable $e) {
+ echo "Exception: " . $e->getMessage() . "\n";
+}
echo "\n-- comparison function taking too few parameters --\n";
function too_few_parameters ($val1) {
@@ -44,10 +48,7 @@ array(1) {
}
-- comparison function taking too many parameters --
-
-Warning: Missing argument 3 for too_many_parameters() in %sarray_udiff_variation5.php on line %d
-array(0) {
-}
+Exception: Too few arguments to function too_many_parameters(), 2 passed and exactly 3 expected
-- comparison function taking too few parameters --
array(0) {
diff --git a/ext/standard/tests/array/array_uintersect_assoc_variation5.phpt b/ext/standard/tests/array/array_uintersect_assoc_variation5.phpt
index e2d7bd03a6..bebe5b52c9 100644
--- a/ext/standard/tests/array/array_uintersect_assoc_variation5.phpt
+++ b/ext/standard/tests/array/array_uintersect_assoc_variation5.phpt
@@ -23,7 +23,11 @@ echo "\n-- comparison function taking too many parameters --\n";
function too_many_parameters ($val1, $val2, $val3) {
return 1;
}
-var_dump(array_uintersect_assoc($arr1, $arr2, 'too_many_parameters'));
+try {
+ var_dump(array_uintersect_assoc($arr1, $arr2, 'too_many_parameters'));
+} catch (Throwable $e) {
+ echo "Exception: " . $e->getMessage() . "\n";
+}
echo "\n-- comparison function taking too few parameters --\n";
function too_few_parameters ($val1) {
@@ -42,10 +46,7 @@ array(0) {
}
-- comparison function taking too many parameters --
-
-Warning: Missing argument 3 for too_many_parameters() in %sarray_uintersect_assoc_variation5.php on line %d
-array(0) {
-}
+Exception: Too few arguments to function too_many_parameters(), 2 passed and exactly 3 expected
-- comparison function taking too few parameters --
array(0) {
diff --git a/ext/standard/tests/array/array_uintersect_uassoc_variation6.phpt b/ext/standard/tests/array/array_uintersect_uassoc_variation6.phpt
index 6ed86f8417..a5317c1c0d 100644
--- a/ext/standard/tests/array/array_uintersect_uassoc_variation6.phpt
+++ b/ext/standard/tests/array/array_uintersect_uassoc_variation6.phpt
@@ -23,7 +23,11 @@ echo "\n-- comparison function taking too many parameters --\n";
function too_many_parameters ($val1, $val2, $val3) {
return 1;
}
-var_dump(array_uintersect_uassoc($arr1, $arr2, 'too_many_parameters', 'too_many_parameters'));
+try {
+ var_dump(array_uintersect_uassoc($arr1, $arr2, 'too_many_parameters', 'too_many_parameters'));
+} catch (Throwable $e) {
+ echo "Exception: " . $e->getMessage() . "\n";
+}
echo "\n-- comparison function taking too few parameters --\n";
function too_few_parameters ($val1) {
@@ -41,14 +45,7 @@ array(0) {
}
-- comparison function taking too many parameters --
-
-Warning: Missing argument 3 for too_many_parameters() in %sarray_uintersect_uassoc_variation6.php on line %d
-
-Warning: Missing argument 3 for too_many_parameters() in %sarray_uintersect_uassoc_variation6.php on line %d
-
-Warning: Missing argument 3 for too_many_parameters() in %sarray_uintersect_uassoc_variation6.php on line %d
-array(0) {
-}
+Exception: Too few arguments to function too_many_parameters(), 2 passed and exactly 3 expected
-- comparison function taking too few parameters --
array(0) {
diff --git a/ext/standard/tests/array/array_uintersect_variation5.phpt b/ext/standard/tests/array/array_uintersect_variation5.phpt
index 75cf08e270..642ebc0077 100644
--- a/ext/standard/tests/array/array_uintersect_variation5.phpt
+++ b/ext/standard/tests/array/array_uintersect_variation5.phpt
@@ -23,7 +23,11 @@ echo "\n-- comparison function taking too many parameters --\n";
function too_many_parameters ($val1, $val2, $val3) {
return 1;
}
-var_dump(array_uintersect($arr1, $arr2, 'too_many_parameters'));
+try {
+ var_dump(array_uintersect($arr1, $arr2, 'too_many_parameters'));
+} catch (Throwable $e) {
+ echo "Exception: " . $e->getMessage() . "\n";
+}
echo "\n-- comparison function taking too few parameters --\n";
function too_few_parameters ($val1) {
@@ -42,14 +46,7 @@ array(0) {
}
-- comparison function taking too many parameters --
-
-Warning: Missing argument 3 for too_many_parameters() in %sarray_uintersect_variation5.php on line %d
-
-Warning: Missing argument 3 for too_many_parameters() in %sarray_uintersect_variation5.php on line %d
-
-Warning: Missing argument 3 for too_many_parameters() in %sarray_uintersect_variation5.php on line %d
-array(0) {
-}
+Exception: Too few arguments to function too_many_parameters(), 2 passed and exactly 3 expected
-- comparison function taking too few parameters --
array(0) {
diff --git a/ext/standard/tests/array/array_walk_error2.phpt b/ext/standard/tests/array/array_walk_error2.phpt
index 63c5f51ee0..fd3cbf5037 100644
--- a/ext/standard/tests/array/array_walk_error2.phpt
+++ b/ext/standard/tests/array/array_walk_error2.phpt
@@ -22,36 +22,44 @@ function callback2($value, $key, $user_data1, $user_data2) {
echo "*** Testing array_walk() : error conditions - callback parameters ***\n";
// expected: Missing argument Warning
-var_dump( array_walk($input, "callback1") );
-var_dump( array_walk($input, "callback2", 4) );
+try {
+ var_dump( array_walk($input, "callback1") );
+} catch (Throwable $e) {
+ echo "Exception: " . $e->getMessage() . "\n";
+}
+try {
+ var_dump( array_walk($input, "callback2", 4) );
+} catch (Throwable $e) {
+ echo "Exception: " . $e->getMessage() . "\n";
+}
// expected: Warning is suppressed
-var_dump( @array_walk($input, "callback1") );
-var_dump( @array_walk($input, "callback2", 4) );
+try {
+ var_dump( @array_walk($input, "callback1") );
+} catch (Throwable $e) {
+ echo "Exception: " . $e->getMessage() . "\n";
+}
+try {
+ var_dump( @array_walk($input, "callback2", 4) );
+} catch (Throwable $e) {
+ echo "Exception: " . $e->getMessage() . "\n";
+}
echo "-- Testing array_walk() function with too many callback parameters --\n";
-var_dump( array_walk($input, "callback1", 20, 10) );
+try {
+ var_dump( array_walk($input, "callback1", 20, 10) );
+} catch (Throwable $e) {
+ echo "Exception: " . $e->getMessage() . "\n";
+}
echo "Done";
?>
--EXPECTF--
*** Testing array_walk() : error conditions - callback parameters ***
-
-Warning: Missing argument 3 for callback1() in %s on line %d
-
-callback1() invoked
-bool(true)
-
-Warning: Missing argument 4 for callback2() in %s on line %d
-
-callback2() invoked
-bool(true)
-
-callback1() invoked
-bool(true)
-
-callback2() invoked
-bool(true)
+Exception: Too few arguments to function callback1(), 2 passed and exactly 3 expected
+Exception: Too few arguments to function callback2(), 3 passed and exactly 4 expected
+Exception: Too few arguments to function callback1(), 2 passed and exactly 3 expected
+Exception: Too few arguments to function callback2(), 3 passed and exactly 4 expected
-- Testing array_walk() function with too many callback parameters --
Warning: array_walk() expects at most 3 parameters, 4 given in %s on line %d
diff --git a/ext/standard/tests/array/array_walk_recursive_error2.phpt b/ext/standard/tests/array/array_walk_recursive_error2.phpt
index 8e0c8829e6..5a077c6886 100644
--- a/ext/standard/tests/array/array_walk_recursive_error2.phpt
+++ b/ext/standard/tests/array/array_walk_recursive_error2.phpt
@@ -22,36 +22,44 @@ function callback2($value, $key, $user_data1, $user_data2) {
echo "*** Testing array_walk_recursive() : error conditions - callback parameters ***\n";
// expected: Missing argument Warning
-var_dump( array_walk_recursive($input, "callback1") );
-var_dump( array_walk_recursive($input, "callback2", 4) );
+try {
+ var_dump( array_walk_recursive($input, "callback1") );
+} catch (Throwable $e) {
+ echo "Exception: " . $e->getMessage() . "\n";
+}
+try {
+ var_dump( array_walk_recursive($input, "callback2", 4) );
+} catch (Throwable $e) {
+ echo "Exception: " . $e->getMessage() . "\n";
+}
// expected: Warning is suppressed
-var_dump( @array_walk_recursive($input, "callback1") );
-var_dump( @array_walk_recursive($input, "callback2", 4) );
+try {
+ var_dump( @array_walk_recursive($input, "callback1") );
+} catch (Throwable $e) {
+ echo "Exception: " . $e->getMessage() . "\n";
+}
+try {
+ var_dump( @array_walk_recursive($input, "callback2", 4) );
+} catch (Throwable $e) {
+ echo "Exception: " . $e->getMessage() . "\n";
+}
echo "-- Testing array_walk_recursive() function with too many callback parameters --\n";
-var_dump( array_walk_recursive($input, "callback1", 20, 10) );
+try {
+ var_dump( array_walk_recursive($input, "callback1", 20, 10) );
+} catch (Throwable $e) {
+ echo "Exception: " . $e->getMessage() . "\n";
+}
echo "Done";
?>
--EXPECTF--
*** Testing array_walk_recursive() : error conditions - callback parameters ***
-
-Warning: Missing argument 3 for callback1() in %s on line %d
-
-callback1() invoked
-bool(true)
-
-Warning: Missing argument 4 for callback2() in %s on line %d
-
-callback2() invoked
-bool(true)
-
-callback1() invoked
-bool(true)
-
-callback2() invoked
-bool(true)
+Exception: Too few arguments to function callback1(), 2 passed and exactly 3 expected
+Exception: Too few arguments to function callback2(), 3 passed and exactly 4 expected
+Exception: Too few arguments to function callback1(), 2 passed and exactly 3 expected
+Exception: Too few arguments to function callback2(), 3 passed and exactly 4 expected
-- Testing array_walk_recursive() function with too many callback parameters --
Warning: array_walk_recursive() expects at most 3 parameters, 4 given in %s on line %d
diff --git a/ext/standard/tests/assert/assert_error2.phpt b/ext/standard/tests/assert/assert_error2.phpt
index 41a7197535..7861d435c8 100644
--- a/ext/standard/tests/assert/assert_error2.phpt
+++ b/ext/standard/tests/assert/assert_error2.phpt
@@ -22,8 +22,11 @@ echo "If this is printed BAIL hasn't worked";
--EXPECTF--
int(0)
-Warning: Missing argument 4 for f1() in %s on line 2
-f1 called
-
Warning: assert(): Assertion "0 != 0" failed in %s on line 9
+Fatal error: Uncaught Error: Too few arguments to function f1(), 3 passed and exactly 4 expected in %sassert_error2.php:2
+Stack trace:
+#0 [internal function]: f1('%s', 9, '0 != 0')
+#1 %sassert_error2.php(9): assert('0 != 0')
+#2 {main}
+ thrown in %sassert_error2.php on line 2
diff --git a/ext/standard/tests/file/bug38450.phpt b/ext/standard/tests/file/bug38450.phpt
index 07e413b92b..4a2953ea79 100644
--- a/ext/standard/tests/file/bug38450.phpt
+++ b/ext/standard/tests/file/bug38450.phpt
@@ -7,7 +7,7 @@ class VariableStream {
var $position;
var $varname;
- function __construct($var) {
+ function __construct($var=null) {
var_dump("constructor!");
}
@@ -102,7 +102,6 @@ var_dump($myvar);
echo "Done\n";
?>
--EXPECTF--
-Warning: Missing argument 1 for VariableStream::__construct() in %s on line %d
string(12) "constructor!"
line1
line2
diff --git a/ext/standard/tests/file/bug38450_1.phpt b/ext/standard/tests/file/bug38450_1.phpt
index 07e413b92b..d0682186f9 100644
--- a/ext/standard/tests/file/bug38450_1.phpt
+++ b/ext/standard/tests/file/bug38450_1.phpt
@@ -7,7 +7,7 @@ class VariableStream {
var $position;
var $varname;
- function __construct($var) {
+ function __construct($var = null) {
var_dump("constructor!");
}
@@ -102,7 +102,6 @@ var_dump($myvar);
echo "Done\n";
?>
--EXPECTF--
-Warning: Missing argument 1 for VariableStream::__construct() in %s on line %d
string(12) "constructor!"
line1
line2
diff --git a/ext/standard/tests/file/bug38450_2.phpt b/ext/standard/tests/file/bug38450_2.phpt
index 7934bb40f5..64c9f8d94a 100644
--- a/ext/standard/tests/file/bug38450_2.phpt
+++ b/ext/standard/tests/file/bug38450_2.phpt
@@ -7,7 +7,7 @@ class VariableStream {
var $position;
var $varname;
- function __construct($var) {
+ function __construct($var = null) {
throw new Exception("constructor");
}
@@ -102,7 +102,6 @@ var_dump($myvar);
echo "Done\n";
?>
--EXPECTF--
-Warning: Missing argument 1 for VariableStream::__construct() in %s on line %d
Warning: fopen(var://myvar): failed to open stream: "VariableStream::stream_open" call failed in %s on line %d
diff --git a/ext/standard/tests/file/bug38450_3.phpt b/ext/standard/tests/file/bug38450_3.phpt
index f2c4643ef3..07c5958ab4 100644
--- a/ext/standard/tests/file/bug38450_3.phpt
+++ b/ext/standard/tests/file/bug38450_3.phpt
@@ -104,7 +104,7 @@ echo "Done\n";
--EXPECTF--
Warning: fopen(var://myvar): failed to open stream: "VariableStream::stream_open" call failed in %sbug38450_3.php on line %d
-Fatal error: Uncaught TypeError: Argument 1 passed to VariableStream::__construct() must be of the type array, none given in %s:%d
+Fatal error: Uncaught Error: Too few arguments to function VariableStream::__construct(), 0 passed and exactly 1 expected in %sbug38450_3.php:7
Stack trace:
#0 [internal function]: VariableStream->__construct()
#1 %s(%d): fopen('var://myvar', 'r+')
diff --git a/ext/standard/tests/file/bug39673.phpt b/ext/standard/tests/file/bug39673.phpt
index 3836f2103d..00c29b28da 100644
--- a/ext/standard/tests/file/bug39673.phpt
+++ b/ext/standard/tests/file/bug39673.phpt
@@ -22,14 +22,14 @@ $offsets = array(
foreach ($offsets as $offset) {
$r = file_get_contents($filename, false, null, $offset);
- var_dump(strlen($r));
+ if ($r !== false) var_dump(strlen($r));
}
@unlink($filename);
echo "Done\n";
?>
---EXPECTF--
-int(13824)
+--EXPECTF--
+int(1)
int(13824)
int(0)
int(1)
diff --git a/ext/standard/tests/file/bug47517.phpt b/ext/standard/tests/file/bug47517.phpt
new file mode 100644
index 0000000000..97f387c865
--- /dev/null
+++ b/ext/standard/tests/file/bug47517.phpt
@@ -0,0 +1,17 @@
+--TEST--
+Bug #47517 test registry virtualization disabled
+--SKIPIF--
+<?php
+if (substr(PHP_OS, 0, 3) != 'WIN') {
+ die('skip only for Windows');
+}
+?>
+--FILE--
+<?php
+/* This has to behave same way on both 64- and 32-bits. */
+file_put_contents('C:\Program Files\myfile.txt', 'hello');
+?>
+==DONE==
+--EXPECTF--
+Warning: file_put_contents(C:\Program Files\myfile.txt): failed to open stream: Permission denied in %sbug47517.php on line %d
+==DONE==
diff --git a/ext/standard/tests/file/bug52624.phpt b/ext/standard/tests/file/bug52624.phpt
index ee61eb90e6..c5e7a9cb0e 100644
--- a/ext/standard/tests/file/bug52624.phpt
+++ b/ext/standard/tests/file/bug52624.phpt
@@ -9,4 +9,6 @@ echo tempnam("directory_that_not_exists", "prefix_");
?>
--EXPECTF--
+Notice: tempnam(): file created in the system's temporary directory in %sbug52624.php on line %d
+
Warning: tempnam(): open_basedir restriction in effect. File(%s) is not within the allowed path(s): (%s) in %s on line %d
diff --git a/ext/standard/tests/file/file_get_contents_variation5_32bit.phpt b/ext/standard/tests/file/file_get_contents_variation5_32bit.phpt
new file mode 100644
index 0000000000..3afc3dc180
--- /dev/null
+++ b/ext/standard/tests/file/file_get_contents_variation5_32bit.phpt
@@ -0,0 +1,236 @@
+--TEST--
+Test file_get_contents() function : usage variation
+--CREDITS--
+Dave Kelsey <d_kelsey@uk.ibm.com>
+--SKIPIF--
+<?php if (PHP_INT_SIZE != 4) die("skip this test is for 32-bit only");
+--FILE--
+<?php
+/* Prototype : string file_get_contents(string filename [, bool use_include_path [, resource context [, long offset [, long maxlen]]]])
+ * Description: Read the entire file into a string
+ * Source code: ext/standard/file.c
+ * Alias to functions:
+ */
+
+echo "*** Testing file_get_contents() : usage variation ***\n";
+
+// Define error handler
+function test_error_handler($err_no, $err_msg, $filename, $linenum, $vars) {
+ if (error_reporting() != 0) {
+ // report non-silenced errors
+ echo "Error: $err_no - $err_msg, $filename($linenum)\n";
+ }
+}
+set_error_handler('test_error_handler');
+
+// Initialise function arguments not being substituted (if any)
+$filename = 'FileGetContentsVar5.tmp';
+$absFile = dirname(__FILE__).'/'.$filename;
+$h = fopen($absFile,"w");
+fwrite($h, b"contents read");
+fclose($h);
+
+
+//get an unset variable
+$unset_var = 10;
+unset ($unset_var);
+
+// define some classes
+class classWithToString
+{
+ public function __toString() {
+ return "Class A object";
+ }
+}
+
+class classWithoutToString
+{
+}
+
+// heredoc string
+$heredoc = <<<EOT
+hello world
+EOT;
+
+// add arrays
+$index_array = array (1, 2, 3);
+$assoc_array = array ('one' => 1, 'two' => 2);
+
+//array of values to iterate over
+$inputs = array(
+
+ // int data
+ 'int 0' => 0,
+ 'int 1' => 1,
+ 'int 12345' => 12345,
+ 'int -12345' => -12345,
+ 'int -10' => -10,
+
+ // float data
+ 'float 10.5' => 10.5,
+ 'float -10.5' => -10.5,
+ 'float -22.5' => -22.5,
+ 'float 12.3456789000e10' => 12.3456789000e10,
+ 'float -12.3456789000e10' => -12.3456789000e10,
+ 'float .5' => .5,
+
+ // array data
+ 'empty array' => array(),
+ 'int indexed array' => $index_array,
+ 'associative array' => $assoc_array,
+ 'nested arrays' => array('foo', $index_array, $assoc_array),
+
+ // null data
+ 'uppercase NULL' => NULL,
+ 'lowercase null' => null,
+
+ // boolean data
+ 'lowercase true' => true,
+ 'lowercase false' =>false,
+ 'uppercase TRUE' =>TRUE,
+ 'uppercase FALSE' =>FALSE,
+
+ // empty data
+ 'empty string DQ' => "",
+ 'empty string SQ' => '',
+
+ // string data
+ 'string DQ' => "string",
+ 'string SQ' => 'string',
+ 'mixed case string' => "sTrInG",
+ 'heredoc' => $heredoc,
+
+ // object data
+ 'instance of classWithToString' => new classWithToString(),
+ 'instance of classWithoutToString' => new classWithoutToString(),
+
+ // undefined data
+ 'undefined var' => @$undefined_var,
+
+ // unset data
+ 'unset var' => @$unset_var,
+);
+
+// loop through each element of the array for offset
+
+foreach($inputs as $key =>$value) {
+ echo "\n--$key--\n";
+ var_dump( file_get_contents($absFile, false, null, $value) );
+};
+
+unlink($absFile);
+
+?>
+===DONE===
+--EXPECTF--
+*** Testing file_get_contents() : usage variation ***
+
+--int 0--
+string(%d) "contents read"
+
+--int 1--
+string(%d) "ontents read"
+
+--int 12345--
+string(%d) ""
+
+--int -12345--
+Error: 2 - file_get_contents(): Failed to seek to position -12345 in the stream, %s(%d)
+bool(false)
+
+--int -10--
+string(10) "tents read"
+
+--float 10.5--
+string(3) "ead"
+
+--float -10.5--
+string(10) "tents read"
+
+--float -22.5--
+Error: 2 - file_get_contents(): Failed to seek to position -22 in the stream, %s(%d)
+bool(false)
+
+--float 12.3456789000e10--
+Error: 2 - file_get_contents() expects parameter 4 to be integer, float given, %s(%d)
+NULL
+
+--float -12.3456789000e10--
+Error: 2 - file_get_contents() expects parameter 4 to be integer, float given, %s(%d)
+NULL
+
+--float .5--
+string(%d) "contents read"
+
+--empty array--
+Error: 2 - file_get_contents() expects parameter 4 to be integer, array given, %s(%d)
+NULL
+
+--int indexed array--
+Error: 2 - file_get_contents() expects parameter 4 to be integer, array given, %s(%d)
+NULL
+
+--associative array--
+Error: 2 - file_get_contents() expects parameter 4 to be integer, array given, %s(%d)
+NULL
+
+--nested arrays--
+Error: 2 - file_get_contents() expects parameter 4 to be integer, array given, %s(%d)
+NULL
+
+--uppercase NULL--
+string(%d) "contents read"
+
+--lowercase null--
+string(%d) "contents read"
+
+--lowercase true--
+string(12) "ontents read"
+
+--lowercase false--
+string(%d) "contents read"
+
+--uppercase TRUE--
+string(12) "ontents read"
+
+--uppercase FALSE--
+string(%d) "contents read"
+
+--empty string DQ--
+Error: 2 - file_get_contents() expects parameter 4 to be integer, %unicode_string_optional% given, %s(%d)
+NULL
+
+--empty string SQ--
+Error: 2 - file_get_contents() expects parameter 4 to be integer, %unicode_string_optional% given, %s(%d)
+NULL
+
+--string DQ--
+Error: 2 - file_get_contents() expects parameter 4 to be integer, %unicode_string_optional% given, %s(%d)
+NULL
+
+--string SQ--
+Error: 2 - file_get_contents() expects parameter 4 to be integer, %unicode_string_optional% given, %s(%d)
+NULL
+
+--mixed case string--
+Error: 2 - file_get_contents() expects parameter 4 to be integer, %unicode_string_optional% given, %s(%d)
+NULL
+
+--heredoc--
+Error: 2 - file_get_contents() expects parameter 4 to be integer, %unicode_string_optional% given, %s(%d)
+NULL
+
+--instance of classWithToString--
+Error: 2 - file_get_contents() expects parameter 4 to be integer, object given, %s(%d)
+NULL
+
+--instance of classWithoutToString--
+Error: 2 - file_get_contents() expects parameter 4 to be integer, object given, %s(%d)
+NULL
+
+--undefined var--
+string(%d) "contents read"
+
+--unset var--
+string(%d) "contents read"
+===DONE===
diff --git a/ext/standard/tests/file/file_get_contents_variation5.phpt b/ext/standard/tests/file/file_get_contents_variation5_64bit.phpt
index aa13d69eb8..df33059f30 100644
--- a/ext/standard/tests/file/file_get_contents_variation5.phpt
+++ b/ext/standard/tests/file/file_get_contents_variation5_64bit.phpt
@@ -63,11 +63,13 @@ $inputs = array(
'int 0' => 0,
'int 1' => 1,
'int 12345' => 12345,
- 'int -12345' => -2345,
+ 'int -12345' => -12345,
+ 'int -10' => -10,
// float data
'float 10.5' => 10.5,
'float -10.5' => -10.5,
+ 'float -22.5' => -22.5,
'float 12.3456789000e10' => 12.3456789000e10,
'float -12.3456789000e10' => -12.3456789000e10,
'float .5' => .5,
@@ -133,19 +135,28 @@ string(%d) "ontents read"
string(%d) ""
--int -12345--
-string(%d) "contents read"
+Error: 2 - file_get_contents(): Failed to seek to position -12345 in the stream, %s(%d)
+bool(false)
+
+--int -10--
+string(10) "tents read"
--float 10.5--
string(3) "ead"
--float -10.5--
-string(%d) "contents read"
+string(10) "tents read"
+
+--float -22.5--
+Error: 2 - file_get_contents(): Failed to seek to position -22 in the stream, %s(%d)
+bool(false)
--float 12.3456789000e10--
string(%d) %s
--float -12.3456789000e10--
-string(%d) %s
+Error: 2 - file_get_contents(): Failed to seek to position -123456789000 in the stream, %s(%d)
+bool(false)
--float .5--
string(%d) "contents read"
diff --git a/ext/standard/tests/file/file_put_contents_variation7-win32.phpt b/ext/standard/tests/file/file_put_contents_variation7-win32.phpt
index d7bfdf9233..e1a94a2043 100644
--- a/ext/standard/tests/file/file_put_contents_variation7-win32.phpt
+++ b/ext/standard/tests/file/file_put_contents_variation7-win32.phpt
@@ -59,10 +59,10 @@ for($i = 0; $i<count($allDirs); $i++) {
$j = $i+1;
$dir = $allDirs[$i];
echo "\n-- Iteration $j --\n";
- $res = file_put_contents($dir."\\".$filename, ($data + $i));
+ $res = file_put_contents($dir."\\".$filename, ($data . $i));
if ($res !== false) {
$in = file_get_contents($absFile);
- if ($in == ($data + $i)) {
+ if ($in == ($data . $i)) {
echo "Data written correctly\n";
}
else {
@@ -127,4 +127,4 @@ No data written
-- Iteration 12 --
Data written correctly
-*** Done *** \ No newline at end of file
+*** Done ***
diff --git a/ext/standard/tests/file/file_put_contents_variation7.phpt b/ext/standard/tests/file/file_put_contents_variation7.phpt
index 5c8e5f3602..b1b2face60 100644
--- a/ext/standard/tests/file/file_put_contents_variation7.phpt
+++ b/ext/standard/tests/file/file_put_contents_variation7.phpt
@@ -51,10 +51,10 @@ for($i = 0; $i<count($allDirs); $i++) {
$j = $i+1;
$dir = $allDirs[$i];
echo "\n-- Iteration $j --\n";
- $res = file_put_contents($dir."/".$filename, ($data + $i));
+ $res = file_put_contents($dir."/".$filename, ($data . $i));
if ($res !== false) {
$in = file_get_contents($absFile);
- if ($in == ($data + $i)) {
+ if ($in == ($data . $i)) {
echo "Data written correctly\n";
}
else {
@@ -116,4 +116,4 @@ Data written correctly
Warning: file_put_contents(BADDIR/FileGetContentsVar7.tmp): failed to open stream: %s in %s on line %d
No data written
-*** Done *** \ No newline at end of file
+*** Done ***
diff --git a/ext/standard/tests/file/tempnam_variation2-win32.phpt b/ext/standard/tests/file/tempnam_variation2-win32.phpt
index 4224966daf..15d59614ff 100644
--- a/ext/standard/tests/file/tempnam_variation2-win32.phpt
+++ b/ext/standard/tests/file/tempnam_variation2-win32.phpt
@@ -121,6 +121,8 @@ File permissions are => 100666
File created in => directory specified
-- Iteration 7 --
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation2-win32.php on line %d
File name is => %s\t%s
File permissions are => 100666
File created in => temp dir
@@ -131,11 +133,15 @@ File permissions are => 100666
File created in => directory specified
-- Iteration 9 --
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation2-win32.php on line %d
File name is => %s\t%s
File permissions are => 100666
File created in => temp dir
-- Iteration 10 --
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation2-win32.php on line %d
File name is => %s\t%s
File permissions are => 100666
File created in => temp dir
@@ -156,8 +162,10 @@ File permissions are => 100666
File created in => directory specified
-- Iteration 14 --
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation2-win32.php on line %d
File name is => %s\t%s
File permissions are => 100666
File created in => temp dir
-*** Done *** \ No newline at end of file
+*** Done ***
diff --git a/ext/standard/tests/file/tempnam_variation2.phpt b/ext/standard/tests/file/tempnam_variation2.phpt
index b7e5cdc058..1a2de1885b 100644
--- a/ext/standard/tests/file/tempnam_variation2.phpt
+++ b/ext/standard/tests/file/tempnam_variation2.phpt
@@ -121,6 +121,8 @@ File permissions are => 100600
File created in => directory specified
-- Iteration 7 --
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation2.php on line %d
File name is => %s/tempnam_variation2.tmp%s
File permissions are => 100600
File created in => temp dir
@@ -131,11 +133,15 @@ File permissions are => 100600
File created in => directory specified
-- Iteration 9 --
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation2.php on line %d
File name is => %s/tempnam_variation2.tmp%s
File permissions are => 100600
File created in => temp dir
-- Iteration 10 --
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation2.php on line %d
File name is => %s/tempnam_variation2.tmp%s
File permissions are => 100600
File created in => temp dir
@@ -156,8 +162,10 @@ File permissions are => 100600
File created in => directory specified
-- Iteration 14 --
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation2.php on line %d
File name is => %s/tempnam_variation2.tmp%s
File permissions are => 100600
File created in => temp dir
-*** Done *** \ No newline at end of file
+*** Done ***
diff --git a/ext/standard/tests/file/tempnam_variation3-win32.phpt b/ext/standard/tests/file/tempnam_variation3-win32.phpt
index df0d88feda..6d5071b5e5 100644
--- a/ext/standard/tests/file/tempnam_variation3-win32.phpt
+++ b/ext/standard/tests/file/tempnam_variation3-win32.phpt
@@ -99,6 +99,8 @@ OK
-- Iteration 4 --
OK
-- Iteration 5 --
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation3-win32.php on line %d
Failed, not created in the correct directory %s vs %s
0
-- Iteration 6 --
diff --git a/ext/standard/tests/file/tempnam_variation4-0.phpt b/ext/standard/tests/file/tempnam_variation4-0.phpt
new file mode 100644
index 0000000000..e96846ff2c
--- /dev/null
+++ b/ext/standard/tests/file/tempnam_variation4-0.phpt
@@ -0,0 +1,638 @@
+--TEST--
+Test tempnam() function: usage variations - permissions(0000 to 0350) of dir
+--SKIPIF--
+<?php
+if (substr(PHP_OS, 0, 3) == 'WIN') {
+ die('skip Not valid for Windows');
+}
+// Skip if being run by root
+$filename = dirname(__FILE__)."/is_readable_root_check.tmp";
+$fp = fopen($filename, 'w');
+fclose($fp);
+if(fileowner($filename) == 0) {
+ unlink ($filename);
+ die('skip cannot be run as root');
+}
+unlink($filename);
+?>
+--FILE--
+<?php
+/* Prototype: string tempnam ( string $dir, string $prefix );
+ Description: Create file with unique file name.
+*/
+
+/* Trying to create the file in a dir with permissions from 0000 to 0350,
+ Allowable permissions: files are expected to be created in the input dir
+ Non-allowable permissions: files are expected to be created in '/tmp' dir
+*/
+
+echo "*** Testing tempnam() with dir of permissions from 0000 to 0350 ***\n";
+$file_path = dirname(__FILE__);
+$dir_name = $file_path."/tempnam_variation4";
+$prefix = "tempnamVar4.";
+
+mkdir($dir_name);
+
+for($mode = 0000; $mode <= 0350; $mode++) {
+ chmod($dir_name, $mode);
+ $file_name = tempnam($dir_name, $prefix);
+
+ if(file_exists($file_name) ) {
+ if (dirname($file_name) != $dir_name) {
+ /* Either there's a notice or error */
+ printf("%o\n", $mode);
+
+ if (realpath(dirname($file_name)) != realpath(sys_get_temp_dir())) {
+ echo " created in unexpected dir\n";
+ }
+ }
+ unlink($file_name);
+ }
+ else {
+ print("FAILED: File is not created\n");
+ }
+}
+
+rmdir($dir_name);
+
+echo "*** Done ***\n";
+?>
+--EXPECTF--
+*** Testing tempnam() with dir of permissions from 0000 to 0350 ***
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+0
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+1
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+2
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+3
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+4
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+5
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+6
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+7
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+10
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+11
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+12
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+13
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+14
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+15
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+16
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+17
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+20
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+21
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+22
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+23
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+24
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+25
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+26
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+27
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+30
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+31
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+32
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+33
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+34
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+35
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+36
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+37
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+40
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+41
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+42
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+43
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+44
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+45
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+46
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+47
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+50
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+51
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+52
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+53
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+54
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+55
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+56
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+57
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+60
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+61
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+62
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+63
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+64
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+65
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+66
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+67
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+70
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+71
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+72
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+73
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+74
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+75
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+76
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+77
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+100
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+101
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+102
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+103
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+104
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+105
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+106
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+107
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+110
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+111
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+112
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+113
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+114
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+115
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+116
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+117
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+120
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+121
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+122
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+123
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+124
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+125
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+126
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+127
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+130
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+131
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+132
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+133
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+134
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+135
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+136
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+137
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+140
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+141
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+142
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+143
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+144
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+145
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+146
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+147
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+150
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+151
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+152
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+153
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+154
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+155
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+156
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+157
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+160
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+161
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+162
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+163
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+164
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+165
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+166
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+167
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+170
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+171
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+172
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+173
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+174
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+175
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+176
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+177
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+200
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+201
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+202
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+203
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+204
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+205
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+206
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+207
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+210
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+211
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+212
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+213
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+214
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+215
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+216
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+217
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+220
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+221
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+222
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+223
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+224
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+225
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+226
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+227
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+230
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+231
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+232
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+233
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+234
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+235
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+236
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+237
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+240
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+241
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+242
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+243
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+244
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+245
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+246
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+247
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+250
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+251
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+252
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+253
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+254
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+255
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+256
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+257
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+260
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+261
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+262
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+263
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+264
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+265
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+266
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+267
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+270
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+271
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+272
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+273
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+274
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+275
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+276
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-0.php on line 20
+277
+*** Done ***
diff --git a/ext/standard/tests/file/tempnam_variation4-1.phpt b/ext/standard/tests/file/tempnam_variation4-1.phpt
new file mode 100644
index 0000000000..667d3ecda6
--- /dev/null
+++ b/ext/standard/tests/file/tempnam_variation4-1.phpt
@@ -0,0 +1,638 @@
+--TEST--
+Test tempnam() function: usage variations - permissions(0351 to 0777) of dir
+--SKIPIF--
+<?php
+if (substr(PHP_OS, 0, 3) == 'WIN') {
+ die('skip Not valid for Windows');
+}
+// Skip if being run by root
+$filename = dirname(__FILE__)."/is_readable_root_check.tmp";
+$fp = fopen($filename, 'w');
+fclose($fp);
+if(fileowner($filename) == 0) {
+ unlink ($filename);
+ die('skip cannot be run as root');
+}
+unlink($filename);
+?>
+--FILE--
+<?php
+/* Prototype: string tempnam ( string $dir, string $prefix );
+ Description: Create file with unique file name.
+*/
+
+/* Trying to create the file in a dir with permissions from 0351 to 0777,
+ Allowable permissions: files are expected to be created in the input dir
+ Non-allowable permissions: files are expected to be created in '/tmp' dir
+*/
+
+echo "*** Testing tempnam() with dir of permissions from 0351 to 0777 ***\n";
+$file_path = dirname(__FILE__);
+$dir_name = $file_path."/tempnam_variation4";
+$prefix = "tempnamVar4.";
+
+mkdir($dir_name);
+
+for($mode = 0351; $mode <= 0777; $mode++) {
+ chmod($dir_name, $mode);
+ $file_name = tempnam($dir_name, $prefix);
+
+ if(file_exists($file_name) ) {
+ if (dirname($file_name) != $dir_name) {
+ /* Either there's a notice or error */
+ printf("%o\n", $mode);
+
+ if (realpath(dirname($file_name)) != realpath(sys_get_temp_dir())) {
+ echo " created in unexpected dir\n";
+ }
+ }
+ unlink($file_name);
+ }
+ else {
+ print("FAILED: File is not created\n");
+ }
+}
+
+rmdir($dir_name);
+
+echo "*** Done ***\n";
+?>
+--EXPECTF--
+*** Testing tempnam() with dir of permissions from 0351 to 0777 ***
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+400
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+401
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+402
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+403
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+404
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+405
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+406
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+407
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+410
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+411
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+412
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+413
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+414
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+415
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+416
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+417
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+420
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+421
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+422
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+423
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+424
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+425
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+426
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+427
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+430
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+431
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+432
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+433
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+434
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+435
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+436
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+437
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+440
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+441
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+442
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+443
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+444
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+445
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+446
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+447
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+450
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+451
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+452
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+453
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+454
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+455
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+456
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+457
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+460
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+461
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+462
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+463
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+464
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+465
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+466
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+467
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+470
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+471
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+472
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+473
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+474
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+475
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+476
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+477
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+500
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+501
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+502
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+503
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+504
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+505
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+506
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+507
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+510
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+511
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+512
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+513
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+514
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+515
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+516
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+517
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+520
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+521
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+522
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+523
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+524
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+525
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+526
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+527
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+530
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+531
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+532
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+533
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+534
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+535
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+536
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+537
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+540
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+541
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+542
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+543
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+544
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+545
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+546
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+547
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+550
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+551
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+552
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+553
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+554
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+555
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+556
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+557
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+560
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+561
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+562
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+563
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+564
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+565
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+566
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+567
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+570
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+571
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+572
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+573
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+574
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+575
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+576
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+577
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+600
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+601
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+602
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+603
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+604
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+605
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+606
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+607
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+610
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+611
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+612
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+613
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+614
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+615
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+616
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+617
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+620
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+621
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+622
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+623
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+624
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+625
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+626
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+627
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+630
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+631
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+632
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+633
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+634
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+635
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+636
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+637
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+640
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+641
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+642
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+643
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+644
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+645
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+646
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+647
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+650
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+651
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+652
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+653
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+654
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+655
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+656
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+657
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+660
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+661
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+662
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+663
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+664
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+665
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+666
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+667
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+670
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+671
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+672
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+673
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+674
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+675
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+676
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation4-1.php on line 20
+677
+*** Done ***
diff --git a/ext/standard/tests/file/tempnam_variation4.phpt b/ext/standard/tests/file/tempnam_variation4.phpt
deleted file mode 100644
index 4ce1c7ec24..0000000000
--- a/ext/standard/tests/file/tempnam_variation4.phpt
+++ /dev/null
@@ -1,1092 +0,0 @@
---TEST--
-Test tempnam() function: usage variations - permissions(0000 to 0777) of dir
---SKIPIF--
-<?php
-if (substr(PHP_OS, 0, 3) == 'WIN') {
- die('skip Not valid for Windows');
-}
-// Skip if being run by root
-$filename = dirname(__FILE__)."/is_readable_root_check.tmp";
-$fp = fopen($filename, 'w');
-fclose($fp);
-if(fileowner($filename) == 0) {
- unlink ($filename);
- die('skip cannot be run as root');
-}
-unlink($filename);
-?>
---FILE--
-<?php
-/* Prototype: string tempnam ( string $dir, string $prefix );
- Description: Create file with unique file name.
-*/
-
-/* Trying to create the file in a dir with permissions from 0000 to 0777,
- Allowable permissions: files are expected to be created in the input dir
- Non-allowable permissions: files are expected to be created in '/tmp' dir
-*/
-
-echo "*** Testing tempnam() with dir of permissions from 0000 to 0777 ***\n";
-$file_path = dirname(__FILE__);
-$dir_name = $file_path."/tempnam_variation4";
-$prefix = "tempnamVar4.";
-
-mkdir($dir_name);
-
-for($mode = 0000; $mode <= 0777; $mode++) {
- echo "-- dir perms ";
- printf("%o", $mode);
- echo " --\n";
- chmod($dir_name, $mode);
- $file_name = tempnam($dir_name, $prefix);
-
- if(file_exists($file_name) ) {
- if (realpath(dirname($file_name)) == realpath(sys_get_temp_dir())) {
- $msg = " created in temp dir ";
- }
- else if (dirname($file_name) == $dir_name) {
- $msg = " created in requested dir";
- }
- else {
- $msg = " created in unexpected dir";
- }
-
- echo $msg."\n";
- unlink($file_name);
- }
- else {
- print("FAILED: File is not created\n");
- }
-}
-
-rmdir($dir_name);
-
-echo "*** Done ***\n";
-?>
---EXPECTF--
-*** Testing tempnam() with dir of permissions from 0000 to 0777 ***
--- dir perms 0 --
- created in temp dir
--- dir perms 1 --
- created in temp dir
--- dir perms 2 --
- created in temp dir
--- dir perms 3 --
- created in temp dir
--- dir perms 4 --
- created in temp dir
--- dir perms 5 --
- created in temp dir
--- dir perms 6 --
- created in temp dir
--- dir perms 7 --
- created in temp dir
--- dir perms 10 --
- created in temp dir
--- dir perms 11 --
- created in temp dir
--- dir perms 12 --
- created in temp dir
--- dir perms 13 --
- created in temp dir
--- dir perms 14 --
- created in temp dir
--- dir perms 15 --
- created in temp dir
--- dir perms 16 --
- created in temp dir
--- dir perms 17 --
- created in temp dir
--- dir perms 20 --
- created in temp dir
--- dir perms 21 --
- created in temp dir
--- dir perms 22 --
- created in temp dir
--- dir perms 23 --
- created in temp dir
--- dir perms 24 --
- created in temp dir
--- dir perms 25 --
- created in temp dir
--- dir perms 26 --
- created in temp dir
--- dir perms 27 --
- created in temp dir
--- dir perms 30 --
- created in temp dir
--- dir perms 31 --
- created in temp dir
--- dir perms 32 --
- created in temp dir
--- dir perms 33 --
- created in temp dir
--- dir perms 34 --
- created in temp dir
--- dir perms 35 --
- created in temp dir
--- dir perms 36 --
- created in temp dir
--- dir perms 37 --
- created in temp dir
--- dir perms 40 --
- created in temp dir
--- dir perms 41 --
- created in temp dir
--- dir perms 42 --
- created in temp dir
--- dir perms 43 --
- created in temp dir
--- dir perms 44 --
- created in temp dir
--- dir perms 45 --
- created in temp dir
--- dir perms 46 --
- created in temp dir
--- dir perms 47 --
- created in temp dir
--- dir perms 50 --
- created in temp dir
--- dir perms 51 --
- created in temp dir
--- dir perms 52 --
- created in temp dir
--- dir perms 53 --
- created in temp dir
--- dir perms 54 --
- created in temp dir
--- dir perms 55 --
- created in temp dir
--- dir perms 56 --
- created in temp dir
--- dir perms 57 --
- created in temp dir
--- dir perms 60 --
- created in temp dir
--- dir perms 61 --
- created in temp dir
--- dir perms 62 --
- created in temp dir
--- dir perms 63 --
- created in temp dir
--- dir perms 64 --
- created in temp dir
--- dir perms 65 --
- created in temp dir
--- dir perms 66 --
- created in temp dir
--- dir perms 67 --
- created in temp dir
--- dir perms 70 --
- created in temp dir
--- dir perms 71 --
- created in temp dir
--- dir perms 72 --
- created in temp dir
--- dir perms 73 --
- created in temp dir
--- dir perms 74 --
- created in temp dir
--- dir perms 75 --
- created in temp dir
--- dir perms 76 --
- created in temp dir
--- dir perms 77 --
- created in temp dir
--- dir perms 100 --
- created in temp dir
--- dir perms 101 --
- created in temp dir
--- dir perms 102 --
- created in temp dir
--- dir perms 103 --
- created in temp dir
--- dir perms 104 --
- created in temp dir
--- dir perms 105 --
- created in temp dir
--- dir perms 106 --
- created in temp dir
--- dir perms 107 --
- created in temp dir
--- dir perms 110 --
- created in temp dir
--- dir perms 111 --
- created in temp dir
--- dir perms 112 --
- created in temp dir
--- dir perms 113 --
- created in temp dir
--- dir perms 114 --
- created in temp dir
--- dir perms 115 --
- created in temp dir
--- dir perms 116 --
- created in temp dir
--- dir perms 117 --
- created in temp dir
--- dir perms 120 --
- created in temp dir
--- dir perms 121 --
- created in temp dir
--- dir perms 122 --
- created in temp dir
--- dir perms 123 --
- created in temp dir
--- dir perms 124 --
- created in temp dir
--- dir perms 125 --
- created in temp dir
--- dir perms 126 --
- created in temp dir
--- dir perms 127 --
- created in temp dir
--- dir perms 130 --
- created in temp dir
--- dir perms 131 --
- created in temp dir
--- dir perms 132 --
- created in temp dir
--- dir perms 133 --
- created in temp dir
--- dir perms 134 --
- created in temp dir
--- dir perms 135 --
- created in temp dir
--- dir perms 136 --
- created in temp dir
--- dir perms 137 --
- created in temp dir
--- dir perms 140 --
- created in temp dir
--- dir perms 141 --
- created in temp dir
--- dir perms 142 --
- created in temp dir
--- dir perms 143 --
- created in temp dir
--- dir perms 144 --
- created in temp dir
--- dir perms 145 --
- created in temp dir
--- dir perms 146 --
- created in temp dir
--- dir perms 147 --
- created in temp dir
--- dir perms 150 --
- created in temp dir
--- dir perms 151 --
- created in temp dir
--- dir perms 152 --
- created in temp dir
--- dir perms 153 --
- created in temp dir
--- dir perms 154 --
- created in temp dir
--- dir perms 155 --
- created in temp dir
--- dir perms 156 --
- created in temp dir
--- dir perms 157 --
- created in temp dir
--- dir perms 160 --
- created in temp dir
--- dir perms 161 --
- created in temp dir
--- dir perms 162 --
- created in temp dir
--- dir perms 163 --
- created in temp dir
--- dir perms 164 --
- created in temp dir
--- dir perms 165 --
- created in temp dir
--- dir perms 166 --
- created in temp dir
--- dir perms 167 --
- created in temp dir
--- dir perms 170 --
- created in temp dir
--- dir perms 171 --
- created in temp dir
--- dir perms 172 --
- created in temp dir
--- dir perms 173 --
- created in temp dir
--- dir perms 174 --
- created in temp dir
--- dir perms 175 --
- created in temp dir
--- dir perms 176 --
- created in temp dir
--- dir perms 177 --
- created in temp dir
--- dir perms 200 --
- created in temp dir
--- dir perms 201 --
- created in temp dir
--- dir perms 202 --
- created in temp dir
--- dir perms 203 --
- created in temp dir
--- dir perms 204 --
- created in temp dir
--- dir perms 205 --
- created in temp dir
--- dir perms 206 --
- created in temp dir
--- dir perms 207 --
- created in temp dir
--- dir perms 210 --
- created in temp dir
--- dir perms 211 --
- created in temp dir
--- dir perms 212 --
- created in temp dir
--- dir perms 213 --
- created in temp dir
--- dir perms 214 --
- created in temp dir
--- dir perms 215 --
- created in temp dir
--- dir perms 216 --
- created in temp dir
--- dir perms 217 --
- created in temp dir
--- dir perms 220 --
- created in temp dir
--- dir perms 221 --
- created in temp dir
--- dir perms 222 --
- created in temp dir
--- dir perms 223 --
- created in temp dir
--- dir perms 224 --
- created in temp dir
--- dir perms 225 --
- created in temp dir
--- dir perms 226 --
- created in temp dir
--- dir perms 227 --
- created in temp dir
--- dir perms 230 --
- created in temp dir
--- dir perms 231 --
- created in temp dir
--- dir perms 232 --
- created in temp dir
--- dir perms 233 --
- created in temp dir
--- dir perms 234 --
- created in temp dir
--- dir perms 235 --
- created in temp dir
--- dir perms 236 --
- created in temp dir
--- dir perms 237 --
- created in temp dir
--- dir perms 240 --
- created in temp dir
--- dir perms 241 --
- created in temp dir
--- dir perms 242 --
- created in temp dir
--- dir perms 243 --
- created in temp dir
--- dir perms 244 --
- created in temp dir
--- dir perms 245 --
- created in temp dir
--- dir perms 246 --
- created in temp dir
--- dir perms 247 --
- created in temp dir
--- dir perms 250 --
- created in temp dir
--- dir perms 251 --
- created in temp dir
--- dir perms 252 --
- created in temp dir
--- dir perms 253 --
- created in temp dir
--- dir perms 254 --
- created in temp dir
--- dir perms 255 --
- created in temp dir
--- dir perms 256 --
- created in temp dir
--- dir perms 257 --
- created in temp dir
--- dir perms 260 --
- created in temp dir
--- dir perms 261 --
- created in temp dir
--- dir perms 262 --
- created in temp dir
--- dir perms 263 --
- created in temp dir
--- dir perms 264 --
- created in temp dir
--- dir perms 265 --
- created in temp dir
--- dir perms 266 --
- created in temp dir
--- dir perms 267 --
- created in temp dir
--- dir perms 270 --
- created in temp dir
--- dir perms 271 --
- created in temp dir
--- dir perms 272 --
- created in temp dir
--- dir perms 273 --
- created in temp dir
--- dir perms 274 --
- created in temp dir
--- dir perms 275 --
- created in temp dir
--- dir perms 276 --
- created in temp dir
--- dir perms 277 --
- created in temp dir
--- dir perms 300 --
- created in requested dir
--- dir perms 301 --
- created in requested dir
--- dir perms 302 --
- created in requested dir
--- dir perms 303 --
- created in requested dir
--- dir perms 304 --
- created in requested dir
--- dir perms 305 --
- created in requested dir
--- dir perms 306 --
- created in requested dir
--- dir perms 307 --
- created in requested dir
--- dir perms 310 --
- created in requested dir
--- dir perms 311 --
- created in requested dir
--- dir perms 312 --
- created in requested dir
--- dir perms 313 --
- created in requested dir
--- dir perms 314 --
- created in requested dir
--- dir perms 315 --
- created in requested dir
--- dir perms 316 --
- created in requested dir
--- dir perms 317 --
- created in requested dir
--- dir perms 320 --
- created in requested dir
--- dir perms 321 --
- created in requested dir
--- dir perms 322 --
- created in requested dir
--- dir perms 323 --
- created in requested dir
--- dir perms 324 --
- created in requested dir
--- dir perms 325 --
- created in requested dir
--- dir perms 326 --
- created in requested dir
--- dir perms 327 --
- created in requested dir
--- dir perms 330 --
- created in requested dir
--- dir perms 331 --
- created in requested dir
--- dir perms 332 --
- created in requested dir
--- dir perms 333 --
- created in requested dir
--- dir perms 334 --
- created in requested dir
--- dir perms 335 --
- created in requested dir
--- dir perms 336 --
- created in requested dir
--- dir perms 337 --
- created in requested dir
--- dir perms 340 --
- created in requested dir
--- dir perms 341 --
- created in requested dir
--- dir perms 342 --
- created in requested dir
--- dir perms 343 --
- created in requested dir
--- dir perms 344 --
- created in requested dir
--- dir perms 345 --
- created in requested dir
--- dir perms 346 --
- created in requested dir
--- dir perms 347 --
- created in requested dir
--- dir perms 350 --
- created in requested dir
--- dir perms 351 --
- created in requested dir
--- dir perms 352 --
- created in requested dir
--- dir perms 353 --
- created in requested dir
--- dir perms 354 --
- created in requested dir
--- dir perms 355 --
- created in requested dir
--- dir perms 356 --
- created in requested dir
--- dir perms 357 --
- created in requested dir
--- dir perms 360 --
- created in requested dir
--- dir perms 361 --
- created in requested dir
--- dir perms 362 --
- created in requested dir
--- dir perms 363 --
- created in requested dir
--- dir perms 364 --
- created in requested dir
--- dir perms 365 --
- created in requested dir
--- dir perms 366 --
- created in requested dir
--- dir perms 367 --
- created in requested dir
--- dir perms 370 --
- created in requested dir
--- dir perms 371 --
- created in requested dir
--- dir perms 372 --
- created in requested dir
--- dir perms 373 --
- created in requested dir
--- dir perms 374 --
- created in requested dir
--- dir perms 375 --
- created in requested dir
--- dir perms 376 --
- created in requested dir
--- dir perms 377 --
- created in requested dir
--- dir perms 400 --
- created in temp dir
--- dir perms 401 --
- created in temp dir
--- dir perms 402 --
- created in temp dir
--- dir perms 403 --
- created in temp dir
--- dir perms 404 --
- created in temp dir
--- dir perms 405 --
- created in temp dir
--- dir perms 406 --
- created in temp dir
--- dir perms 407 --
- created in temp dir
--- dir perms 410 --
- created in temp dir
--- dir perms 411 --
- created in temp dir
--- dir perms 412 --
- created in temp dir
--- dir perms 413 --
- created in temp dir
--- dir perms 414 --
- created in temp dir
--- dir perms 415 --
- created in temp dir
--- dir perms 416 --
- created in temp dir
--- dir perms 417 --
- created in temp dir
--- dir perms 420 --
- created in temp dir
--- dir perms 421 --
- created in temp dir
--- dir perms 422 --
- created in temp dir
--- dir perms 423 --
- created in temp dir
--- dir perms 424 --
- created in temp dir
--- dir perms 425 --
- created in temp dir
--- dir perms 426 --
- created in temp dir
--- dir perms 427 --
- created in temp dir
--- dir perms 430 --
- created in temp dir
--- dir perms 431 --
- created in temp dir
--- dir perms 432 --
- created in temp dir
--- dir perms 433 --
- created in temp dir
--- dir perms 434 --
- created in temp dir
--- dir perms 435 --
- created in temp dir
--- dir perms 436 --
- created in temp dir
--- dir perms 437 --
- created in temp dir
--- dir perms 440 --
- created in temp dir
--- dir perms 441 --
- created in temp dir
--- dir perms 442 --
- created in temp dir
--- dir perms 443 --
- created in temp dir
--- dir perms 444 --
- created in temp dir
--- dir perms 445 --
- created in temp dir
--- dir perms 446 --
- created in temp dir
--- dir perms 447 --
- created in temp dir
--- dir perms 450 --
- created in temp dir
--- dir perms 451 --
- created in temp dir
--- dir perms 452 --
- created in temp dir
--- dir perms 453 --
- created in temp dir
--- dir perms 454 --
- created in temp dir
--- dir perms 455 --
- created in temp dir
--- dir perms 456 --
- created in temp dir
--- dir perms 457 --
- created in temp dir
--- dir perms 460 --
- created in temp dir
--- dir perms 461 --
- created in temp dir
--- dir perms 462 --
- created in temp dir
--- dir perms 463 --
- created in temp dir
--- dir perms 464 --
- created in temp dir
--- dir perms 465 --
- created in temp dir
--- dir perms 466 --
- created in temp dir
--- dir perms 467 --
- created in temp dir
--- dir perms 470 --
- created in temp dir
--- dir perms 471 --
- created in temp dir
--- dir perms 472 --
- created in temp dir
--- dir perms 473 --
- created in temp dir
--- dir perms 474 --
- created in temp dir
--- dir perms 475 --
- created in temp dir
--- dir perms 476 --
- created in temp dir
--- dir perms 477 --
- created in temp dir
--- dir perms 500 --
- created in temp dir
--- dir perms 501 --
- created in temp dir
--- dir perms 502 --
- created in temp dir
--- dir perms 503 --
- created in temp dir
--- dir perms 504 --
- created in temp dir
--- dir perms 505 --
- created in temp dir
--- dir perms 506 --
- created in temp dir
--- dir perms 507 --
- created in temp dir
--- dir perms 510 --
- created in temp dir
--- dir perms 511 --
- created in temp dir
--- dir perms 512 --
- created in temp dir
--- dir perms 513 --
- created in temp dir
--- dir perms 514 --
- created in temp dir
--- dir perms 515 --
- created in temp dir
--- dir perms 516 --
- created in temp dir
--- dir perms 517 --
- created in temp dir
--- dir perms 520 --
- created in temp dir
--- dir perms 521 --
- created in temp dir
--- dir perms 522 --
- created in temp dir
--- dir perms 523 --
- created in temp dir
--- dir perms 524 --
- created in temp dir
--- dir perms 525 --
- created in temp dir
--- dir perms 526 --
- created in temp dir
--- dir perms 527 --
- created in temp dir
--- dir perms 530 --
- created in temp dir
--- dir perms 531 --
- created in temp dir
--- dir perms 532 --
- created in temp dir
--- dir perms 533 --
- created in temp dir
--- dir perms 534 --
- created in temp dir
--- dir perms 535 --
- created in temp dir
--- dir perms 536 --
- created in temp dir
--- dir perms 537 --
- created in temp dir
--- dir perms 540 --
- created in temp dir
--- dir perms 541 --
- created in temp dir
--- dir perms 542 --
- created in temp dir
--- dir perms 543 --
- created in temp dir
--- dir perms 544 --
- created in temp dir
--- dir perms 545 --
- created in temp dir
--- dir perms 546 --
- created in temp dir
--- dir perms 547 --
- created in temp dir
--- dir perms 550 --
- created in temp dir
--- dir perms 551 --
- created in temp dir
--- dir perms 552 --
- created in temp dir
--- dir perms 553 --
- created in temp dir
--- dir perms 554 --
- created in temp dir
--- dir perms 555 --
- created in temp dir
--- dir perms 556 --
- created in temp dir
--- dir perms 557 --
- created in temp dir
--- dir perms 560 --
- created in temp dir
--- dir perms 561 --
- created in temp dir
--- dir perms 562 --
- created in temp dir
--- dir perms 563 --
- created in temp dir
--- dir perms 564 --
- created in temp dir
--- dir perms 565 --
- created in temp dir
--- dir perms 566 --
- created in temp dir
--- dir perms 567 --
- created in temp dir
--- dir perms 570 --
- created in temp dir
--- dir perms 571 --
- created in temp dir
--- dir perms 572 --
- created in temp dir
--- dir perms 573 --
- created in temp dir
--- dir perms 574 --
- created in temp dir
--- dir perms 575 --
- created in temp dir
--- dir perms 576 --
- created in temp dir
--- dir perms 577 --
- created in temp dir
--- dir perms 600 --
- created in temp dir
--- dir perms 601 --
- created in temp dir
--- dir perms 602 --
- created in temp dir
--- dir perms 603 --
- created in temp dir
--- dir perms 604 --
- created in temp dir
--- dir perms 605 --
- created in temp dir
--- dir perms 606 --
- created in temp dir
--- dir perms 607 --
- created in temp dir
--- dir perms 610 --
- created in temp dir
--- dir perms 611 --
- created in temp dir
--- dir perms 612 --
- created in temp dir
--- dir perms 613 --
- created in temp dir
--- dir perms 614 --
- created in temp dir
--- dir perms 615 --
- created in temp dir
--- dir perms 616 --
- created in temp dir
--- dir perms 617 --
- created in temp dir
--- dir perms 620 --
- created in temp dir
--- dir perms 621 --
- created in temp dir
--- dir perms 622 --
- created in temp dir
--- dir perms 623 --
- created in temp dir
--- dir perms 624 --
- created in temp dir
--- dir perms 625 --
- created in temp dir
--- dir perms 626 --
- created in temp dir
--- dir perms 627 --
- created in temp dir
--- dir perms 630 --
- created in temp dir
--- dir perms 631 --
- created in temp dir
--- dir perms 632 --
- created in temp dir
--- dir perms 633 --
- created in temp dir
--- dir perms 634 --
- created in temp dir
--- dir perms 635 --
- created in temp dir
--- dir perms 636 --
- created in temp dir
--- dir perms 637 --
- created in temp dir
--- dir perms 640 --
- created in temp dir
--- dir perms 641 --
- created in temp dir
--- dir perms 642 --
- created in temp dir
--- dir perms 643 --
- created in temp dir
--- dir perms 644 --
- created in temp dir
--- dir perms 645 --
- created in temp dir
--- dir perms 646 --
- created in temp dir
--- dir perms 647 --
- created in temp dir
--- dir perms 650 --
- created in temp dir
--- dir perms 651 --
- created in temp dir
--- dir perms 652 --
- created in temp dir
--- dir perms 653 --
- created in temp dir
--- dir perms 654 --
- created in temp dir
--- dir perms 655 --
- created in temp dir
--- dir perms 656 --
- created in temp dir
--- dir perms 657 --
- created in temp dir
--- dir perms 660 --
- created in temp dir
--- dir perms 661 --
- created in temp dir
--- dir perms 662 --
- created in temp dir
--- dir perms 663 --
- created in temp dir
--- dir perms 664 --
- created in temp dir
--- dir perms 665 --
- created in temp dir
--- dir perms 666 --
- created in temp dir
--- dir perms 667 --
- created in temp dir
--- dir perms 670 --
- created in temp dir
--- dir perms 671 --
- created in temp dir
--- dir perms 672 --
- created in temp dir
--- dir perms 673 --
- created in temp dir
--- dir perms 674 --
- created in temp dir
--- dir perms 675 --
- created in temp dir
--- dir perms 676 --
- created in temp dir
--- dir perms 677 --
- created in temp dir
--- dir perms 700 --
- created in requested dir
--- dir perms 701 --
- created in requested dir
--- dir perms 702 --
- created in requested dir
--- dir perms 703 --
- created in requested dir
--- dir perms 704 --
- created in requested dir
--- dir perms 705 --
- created in requested dir
--- dir perms 706 --
- created in requested dir
--- dir perms 707 --
- created in requested dir
--- dir perms 710 --
- created in requested dir
--- dir perms 711 --
- created in requested dir
--- dir perms 712 --
- created in requested dir
--- dir perms 713 --
- created in requested dir
--- dir perms 714 --
- created in requested dir
--- dir perms 715 --
- created in requested dir
--- dir perms 716 --
- created in requested dir
--- dir perms 717 --
- created in requested dir
--- dir perms 720 --
- created in requested dir
--- dir perms 721 --
- created in requested dir
--- dir perms 722 --
- created in requested dir
--- dir perms 723 --
- created in requested dir
--- dir perms 724 --
- created in requested dir
--- dir perms 725 --
- created in requested dir
--- dir perms 726 --
- created in requested dir
--- dir perms 727 --
- created in requested dir
--- dir perms 730 --
- created in requested dir
--- dir perms 731 --
- created in requested dir
--- dir perms 732 --
- created in requested dir
--- dir perms 733 --
- created in requested dir
--- dir perms 734 --
- created in requested dir
--- dir perms 735 --
- created in requested dir
--- dir perms 736 --
- created in requested dir
--- dir perms 737 --
- created in requested dir
--- dir perms 740 --
- created in requested dir
--- dir perms 741 --
- created in requested dir
--- dir perms 742 --
- created in requested dir
--- dir perms 743 --
- created in requested dir
--- dir perms 744 --
- created in requested dir
--- dir perms 745 --
- created in requested dir
--- dir perms 746 --
- created in requested dir
--- dir perms 747 --
- created in requested dir
--- dir perms 750 --
- created in requested dir
--- dir perms 751 --
- created in requested dir
--- dir perms 752 --
- created in requested dir
--- dir perms 753 --
- created in requested dir
--- dir perms 754 --
- created in requested dir
--- dir perms 755 --
- created in requested dir
--- dir perms 756 --
- created in requested dir
--- dir perms 757 --
- created in requested dir
--- dir perms 760 --
- created in requested dir
--- dir perms 761 --
- created in requested dir
--- dir perms 762 --
- created in requested dir
--- dir perms 763 --
- created in requested dir
--- dir perms 764 --
- created in requested dir
--- dir perms 765 --
- created in requested dir
--- dir perms 766 --
- created in requested dir
--- dir perms 767 --
- created in requested dir
--- dir perms 770 --
- created in requested dir
--- dir perms 771 --
- created in requested dir
--- dir perms 772 --
- created in requested dir
--- dir perms 773 --
- created in requested dir
--- dir perms 774 --
- created in requested dir
--- dir perms 775 --
- created in requested dir
--- dir perms 776 --
- created in requested dir
--- dir perms 777 --
- created in requested dir
-*** Done ***
diff --git a/ext/standard/tests/file/tempnam_variation7-win32.phpt b/ext/standard/tests/file/tempnam_variation7-win32.phpt
index 0c8caca95c..918ef36588 100644
--- a/ext/standard/tests/file/tempnam_variation7-win32.phpt
+++ b/ext/standard/tests/file/tempnam_variation7-win32.phpt
@@ -65,10 +65,14 @@ echo "\n*** Done ***\n";
--EXPECTF--
*** Testing tempnam() with invalid/non-existing directory names ***
-- Iteration 0 --
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation7-win32.php on line %d
File name is => %s%et%s
File permissions are => 100666
File created in => temp dir
-- Iteration 1 --
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation7-win32.php on line %d
File name is => %s%et%s
File permissions are => 100666
File created in => temp dir
@@ -85,6 +89,8 @@ File name is => %s%et%s
File permissions are => 100666
File created in => temp dir
-- Iteration 5 --
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation7-win32.php on line %d
File name is => %s%et%s
File permissions are => 100666
File created in => temp dir
@@ -101,10 +107,14 @@ Warning: tempnam() expects parameter 1 to be a valid path, array given in %s on
Warning: unlink(): %r(Invalid argument|No such file or directory)%r in %s on line %d
-- Iteration 8 --
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation7-win32.php on line %d
File name is => %s%et%s
File permissions are => 100666
File created in => temp dir
-- Iteration 9 --
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation7-win32.php on line %d
File name is => %s%et%s
File permissions are => 100666
File created in => temp dir
diff --git a/ext/standard/tests/file/tempnam_variation7.phpt b/ext/standard/tests/file/tempnam_variation7.phpt
index b6f81caabc..d24c1d8974 100644
--- a/ext/standard/tests/file/tempnam_variation7.phpt
+++ b/ext/standard/tests/file/tempnam_variation7.phpt
@@ -70,10 +70,14 @@ echo "\n*** Done ***\n";
--EXPECTF--
*** Testing tempnam() with invalid/non-existing directory names ***
-- Iteration 0 --
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation7.php on line %d
File name is => %s%etempnam_variation3.tmp%s
File permissions are => 100600
File created in => temp dir
-- Iteration 1 --
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation7.php on line %d
File name is => %s%etempnam_variation3.tmp%s
File permissions are => 100600
File created in => temp dir
@@ -90,6 +94,8 @@ File name is => %s%etempnam_variation3.tmp%s
File permissions are => 100600
File created in => temp dir
-- Iteration 5 --
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation7.php on line %d
File name is => %s%etempnam_variation3.tmp%s
File permissions are => 100600
File created in => temp dir
@@ -106,10 +112,14 @@ Warning: tempnam() expects parameter 1 to be a valid path, array given in %s on
Warning: unlink(): %s in %s on line %d
-- Iteration 8 --
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation7.php on line %d
File name is => %s/tempnam_variation3.tmp%s
File permissions are => 100600
File created in => temp dir
-- Iteration 9 --
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation7.php on line %d
File name is => %s/tempnam_variation3.tmp%s
File permissions are => 100600
File created in => temp dir
diff --git a/ext/standard/tests/file/tempnam_variation8-win32.phpt b/ext/standard/tests/file/tempnam_variation8-win32.phpt
index 8df67b609e..52ff7b9daa 100644
--- a/ext/standard/tests/file/tempnam_variation8-win32.phpt
+++ b/ext/standard/tests/file/tempnam_variation8-win32.phpt
@@ -115,11 +115,15 @@ File permissions are => 100666
File created in => directory specified
-- Iteration 6 --
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation8-win32.php on line %d
File name is => %s\t%s
File permissions are => 100666
File created in => temp dir
-- Iteration 7 --
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation8-win32.php on line %d
File name is => %s\t%s
File permissions are => 100666
File created in => temp dir
@@ -140,6 +144,8 @@ File permissions are => 100666
File created in => directory specified
-- Iteration 11 --
+
+Notice: tempnam(): file created in the system's temporary directory in %stempnam_variation8-win32.php on line %d
File name is => %s\t%s
File permissions are => 100666
File created in => temp dir
diff --git a/ext/standard/tests/general_functions/bug72306.phpt b/ext/standard/tests/general_functions/bug72306.phpt
new file mode 100644
index 0000000000..05c25e6f1e
--- /dev/null
+++ b/ext/standard/tests/general_functions/bug72306.phpt
@@ -0,0 +1,23 @@
+--TEST--
+Bug #72306 (Heap overflow through proc_open and $env parameter)
+--FILE--
+<?php
+class moo {
+ function __construct() { $this->a = 0; }
+ function __toString() { return $this->a++ ? str_repeat("a", 0x8000) : "a"; }
+}
+
+$env = array('some_option' => new moo());
+$pipes = array();
+$description = array(
+ 0 => array("pipe", "r"),
+ 1 => array("pipe", "w"),
+ 2 => array("pipe", "r")
+);
+
+$process = proc_open('nothing', $description, $pipes, NULL, $env);
+
+?>
+okey
+--EXPECT--
+okey
diff --git a/ext/standard/tests/general_functions/floatval.phpt b/ext/standard/tests/general_functions/floatval.phpt
index 9b7a3281e4..d7bdffd6ae 100644
--- a/ext/standard/tests/general_functions/floatval.phpt
+++ b/ext/standard/tests/general_functions/floatval.phpt
@@ -155,6 +155,10 @@ float(5000000)
float(-5000000)
*** Testing floatval() on non floating types ***
+
+Notice: A non well formed numeric value encountered in %s on line 69
+
+Notice: A non well formed numeric value encountered in %s on line 70
float(-2147483648)
float(2147483648)
float(%d)
diff --git a/ext/standard/tests/general_functions/floatval_variation1.phpt b/ext/standard/tests/general_functions/floatval_variation1.phpt
index 83925b89b0..aa808cfba1 100644
--- a/ext/standard/tests/general_functions/floatval_variation1.phpt
+++ b/ext/standard/tests/general_functions/floatval_variation1.phpt
@@ -52,6 +52,11 @@ foreach ($not_float_types as $key => $type ) {
?>
===DONE===
--EXPECTF--
+
+Notice: A non well formed numeric value encountered in %s on line %d
+
+Notice: A non well formed numeric value encountered in %s on line %d
+
*** Testing floatval() on non floating types ***
-- Iteration : -2147483648 --
@@ -151,4 +156,4 @@ float(0)
-- Iteration : null --
float(0)
-===DONE=== \ No newline at end of file
+===DONE===
diff --git a/ext/standard/tests/general_functions/getenv.phpt b/ext/standard/tests/general_functions/getenv.phpt
new file mode 100644
index 0000000000..006378a337
--- /dev/null
+++ b/ext/standard/tests/general_functions/getenv.phpt
@@ -0,0 +1,16 @@
+--TEST--
+getenv() basic tests
+--ENV--
+FOO=bar
+--FILE--
+<?php
+
+var_dump(getenv("FOO"));
+var_dump(getenv()["FOO"]);
+
+echo "Done\n";
+?>
+--EXPECTF--
+string(3) "bar"
+string(3) "bar"
+Done
diff --git a/ext/standard/tests/general_functions/gettype_settype_variation2.phpt b/ext/standard/tests/general_functions/gettype_settype_variation2.phpt
index 2242952769..18b05b6a97 100644
--- a/ext/standard/tests/general_functions/gettype_settype_variation2.phpt
+++ b/ext/standard/tests/general_functions/gettype_settype_variation2.phpt
@@ -282,7 +282,7 @@ string(7) "integer"
-- Iteration 18 --
string(6) "string"
bool(true)
-int(1)
+int(100)
string(7) "integer"
-- Iteration 19 --
string(6) "string"
@@ -297,7 +297,7 @@ string(7) "integer"
-- Iteration 21 --
string(6) "string"
bool(true)
-int(-1)
+int(0)
string(7) "integer"
-- Iteration 22 --
string(6) "string"
@@ -312,7 +312,7 @@ string(7) "integer"
-- Iteration 24 --
string(6) "string"
bool(true)
-int(1)
+int(100)
string(7) "integer"
-- Iteration 25 --
string(6) "string"
@@ -327,7 +327,7 @@ string(7) "integer"
-- Iteration 27 --
string(6) "string"
bool(true)
-int(-1)
+int(0)
string(7) "integer"
-- Iteration 28 --
string(6) "string"
@@ -687,7 +687,7 @@ string(7) "integer"
-- Iteration 18 --
string(6) "string"
bool(true)
-int(1)
+int(100)
string(7) "integer"
-- Iteration 19 --
string(6) "string"
@@ -702,7 +702,7 @@ string(7) "integer"
-- Iteration 21 --
string(6) "string"
bool(true)
-int(-1)
+int(0)
string(7) "integer"
-- Iteration 22 --
string(6) "string"
@@ -717,7 +717,7 @@ string(7) "integer"
-- Iteration 24 --
string(6) "string"
bool(true)
-int(1)
+int(100)
string(7) "integer"
-- Iteration 25 --
string(6) "string"
@@ -732,7 +732,7 @@ string(7) "integer"
-- Iteration 27 --
string(6) "string"
bool(true)
-int(-1)
+int(0)
string(7) "integer"
-- Iteration 28 --
string(6) "string"
diff --git a/ext/standard/tests/http/http_response_header_01.phpt b/ext/standard/tests/http/http_response_header_01.phpt
new file mode 100644
index 0000000000..2facf4fae5
--- /dev/null
+++ b/ext/standard/tests/http/http_response_header_01.phpt
@@ -0,0 +1,38 @@
+--TEST--
+$http_reponse_header (no redirect)
+--SKIPIF--
+<?php require 'server.inc'; http_server_skipif('tcp://127.0.0.1:22346'); ?>
+--INI--
+allow_url_fopen=1
+allow_url_include=1
+--FILE--
+<?php
+require 'server.inc';
+
+$responses = array(
+ "data://text/plain,HTTP/1.0 200 Ok\r\nSome: Header\r\nSome: Header\r\n\r\nBody",
+);
+
+$pid = http_server("tcp://127.0.0.1:22346", $responses, $output);
+
+function test() {
+ $f = file_get_contents('http://127.0.0.1:22346/');
+ var_dump($f);
+ var_dump($http_response_header);
+}
+test();
+
+http_server_kill($pid);
+?>
+==DONE==
+--EXPECT--
+string(4) "Body"
+array(3) {
+ [0]=>
+ string(15) "HTTP/1.0 200 Ok"
+ [1]=>
+ string(12) "Some: Header"
+ [2]=>
+ string(12) "Some: Header"
+}
+==DONE==
diff --git a/ext/standard/tests/http/http_response_header_02.phpt b/ext/standard/tests/http/http_response_header_02.phpt
new file mode 100644
index 0000000000..0d4da1af14
--- /dev/null
+++ b/ext/standard/tests/http/http_response_header_02.phpt
@@ -0,0 +1,44 @@
+--TEST--
+$http_reponse_header (redirect)
+--SKIPIF--
+<?php require 'server.inc'; http_server_skipif('tcp://127.0.0.1:22347'); ?>
+--INI--
+allow_url_fopen=1
+allow_url_include=1
+--FILE--
+<?php
+require 'server.inc';
+
+$responses = array(
+ "data://text/plain,HTTP/1.0 302 Found\r\n"
+ . "Some: Header\r\nLocation: http://127.0.0.1:22347/try-again\r\n\r\n",
+ "data://test/plain,HTTP/1.0 200 Ok\r\nSome: Header\r\n\r\nBody",
+);
+
+$pid = http_server("tcp://127.0.0.1:22347", $responses, $output);
+
+function test() {
+ $f = file_get_contents('http://127.0.0.1:22347/');
+ var_dump($f);
+ var_dump($http_response_header);
+}
+test();
+
+http_server_kill($pid);
+?>
+==DONE==
+--EXPECT--
+string(4) "Body"
+array(5) {
+ [0]=>
+ string(18) "HTTP/1.0 302 Found"
+ [1]=>
+ string(12) "Some: Header"
+ [2]=>
+ string(42) "Location: http://127.0.0.1:22347/try-again"
+ [3]=>
+ string(15) "HTTP/1.0 200 Ok"
+ [4]=>
+ string(12) "Some: Header"
+}
+==DONE==
diff --git a/ext/standard/tests/http/http_response_header_03.phpt b/ext/standard/tests/http/http_response_header_03.phpt
new file mode 100644
index 0000000000..866a9b3ba5
--- /dev/null
+++ b/ext/standard/tests/http/http_response_header_03.phpt
@@ -0,0 +1,45 @@
+--TEST--
+$http_reponse_header (redirect + not found)
+--SKIPIF--
+<?php require 'server.inc'; http_server_skipif('tcp://127.0.0.1:22348'); ?>
+--INI--
+allow_url_fopen=1
+allow_url_include=1
+--FILE--
+<?php
+require 'server.inc';
+
+$responses = array(
+ "data://text/plain,HTTP/1.0 302 Found\r\n"
+ . "Some: Header\r\nLocation: http://127.0.0.1:22348/try-again\r\n\r\n",
+ "data://test/plain,HTTP/1.0 404 Not Found\r\nSome: Header\r\n\r\nBody",
+);
+
+$pid = http_server("tcp://127.0.0.1:22348", $responses, $output);
+
+function test() {
+ $f = file_get_contents('http://127.0.0.1:22348/');
+ var_dump($f);
+ var_dump($http_response_header);
+}
+test();
+
+http_server_kill($pid);
+?>
+==DONE==
+--EXPECTF--
+Warning: file_get_contents(http://127.0.0.1:22348/): failed to open stream: HTTP request failed! HTTP/1.0 404 Not Found%a
+bool(false)
+array(5) {
+ [0]=>
+ string(18) "HTTP/1.0 302 Found"
+ [1]=>
+ string(12) "Some: Header"
+ [2]=>
+ string(42) "Location: http://127.0.0.1:22348/try-again"
+ [3]=>
+ string(22) "HTTP/1.0 404 Not Found"
+ [4]=>
+ string(12) "Some: Header"
+}
+==DONE==
diff --git a/ext/standard/tests/math/decbin_basic.phpt b/ext/standard/tests/math/decbin_basic.phpt
index b4389956f4..572a04245e 100644
--- a/ext/standard/tests/math/decbin_basic.phpt
+++ b/ext/standard/tests/math/decbin_basic.phpt
@@ -31,7 +31,7 @@ string(2) "11"
string(7) "1011111"
string(4) "1010"
string(12) "111101101110"
-string(2) "11"
+string(12) "111101101110"
string(6) "100111"
string(1) "0"
string(1) "1"
diff --git a/ext/standard/tests/math/dechex_basic.phpt b/ext/standard/tests/math/dechex_basic.phpt
index 2423d8e748..ac53a97b34 100644
--- a/ext/standard/tests/math/dechex_basic.phpt
+++ b/ext/standard/tests/math/dechex_basic.phpt
@@ -30,7 +30,7 @@ string(1) "3"
string(2) "5f"
string(1) "a"
string(3) "f6e"
-string(1) "3"
+string(3) "f6e"
string(2) "27"
string(1) "0"
string(1) "1"
diff --git a/ext/standard/tests/math/decoct_basic.phpt b/ext/standard/tests/math/decoct_basic.phpt
index cc1f0a899a..3a5011b973 100644
--- a/ext/standard/tests/math/decoct_basic.phpt
+++ b/ext/standard/tests/math/decoct_basic.phpt
@@ -30,7 +30,7 @@ string(1) "3"
string(3) "137"
string(2) "12"
string(4) "7556"
-string(1) "3"
+string(4) "7556"
string(2) "47"
string(1) "0"
string(1) "1"
diff --git a/ext/standard/tests/math/pow_variation1.phpt b/ext/standard/tests/math/pow_variation1.phpt
index 5576e5b493..c744c4eb9d 100644
--- a/ext/standard/tests/math/pow_variation1.phpt
+++ b/ext/standard/tests/math/pow_variation1.phpt
@@ -143,21 +143,31 @@ int(1)
int(0)
-- Iteration 17 --
+
+Warning: A non-numeric value encountered in %s on line %d
int(0)
-- Iteration 18 --
+
+Warning: A non-numeric value encountered in %s on line %d
int(0)
-- Iteration 19 --
int(0)
-- Iteration 20 --
+
+Warning: A non-numeric value encountered in %s on line %d
int(0)
-- Iteration 21 --
+
+Warning: A non-numeric value encountered in %s on line %d
int(0)
-- Iteration 22 --
+
+Warning: A non-numeric value encountered in %s on line %d
int(0)
-- Iteration 23 --
diff --git a/ext/standard/tests/math/pow_variation1_64bit.phpt b/ext/standard/tests/math/pow_variation1_64bit.phpt
index e1986ba858..ea2ae45d18 100644
--- a/ext/standard/tests/math/pow_variation1_64bit.phpt
+++ b/ext/standard/tests/math/pow_variation1_64bit.phpt
@@ -143,21 +143,31 @@ int(1)
int(0)
-- Iteration 17 --
+
+Warning: A non-numeric value encountered in %s on line %d
int(0)
-- Iteration 18 --
+
+Warning: A non-numeric value encountered in %s on line %d
int(0)
-- Iteration 19 --
int(0)
-- Iteration 20 --
+
+Warning: A non-numeric value encountered in %s on line %d
int(0)
-- Iteration 21 --
+
+Warning: A non-numeric value encountered in %s on line %d
int(0)
-- Iteration 22 --
+
+Warning: A non-numeric value encountered in %s on line %d
int(0)
-- Iteration 23 --
diff --git a/ext/standard/tests/math/pow_variation2.phpt b/ext/standard/tests/math/pow_variation2.phpt
index f571936727..36b085b647 100644
--- a/ext/standard/tests/math/pow_variation2.phpt
+++ b/ext/standard/tests/math/pow_variation2.phpt
@@ -139,21 +139,31 @@ float(20.3)
float(1)
-- Iteration 17 --
+
+Warning: A non-numeric value encountered in %s on line %d
float(1)
-- Iteration 18 --
+
+Warning: A non-numeric value encountered in %s on line %d
float(1)
-- Iteration 19 --
int(1)
-- Iteration 20 --
+
+Warning: A non-numeric value encountered in %s on line %d
float(1)
-- Iteration 21 --
+
+Warning: A non-numeric value encountered in %s on line %d
float(1)
-- Iteration 22 --
+
+Warning: A non-numeric value encountered in %s on line %d
float(1)
-- Iteration 23 --
diff --git a/ext/standard/tests/network/ip.phpt b/ext/standard/tests/network/ip.phpt
index 3fc1b9dcf4..abe50e7192 100644
--- a/ext/standard/tests/network/ip.phpt
+++ b/ext/standard/tests/network/ip.phpt
@@ -48,20 +48,22 @@ string(7) "0.0.0.0"
int(1118019956)
string(14) "66.163.161.116"
-Warning: ip2long() expects exactly 1 parameter, 0 given in %s on line %d
+Warning: ip2long() expects exactly 1 parameter, 0 given in %sip.php on line %d
NULL
bool(false)
bool(false)
int(1869573999)
-Warning: ip2long() expects parameter 1 to be string, array given in %s on line %d
+Warning: ip2long() expects parameter 1 to be string, array given in %sip.php on line %d
NULL
-Warning: long2ip() expects exactly 1 parameter, 0 given in %s on line %d
+Warning: long2ip() expects exactly 1 parameter, 0 given in %sip.php on line %d
NULL
string(13) "255.254.82.80"
-string(7) "0.0.0.0"
-Warning: long2ip() expects parameter 1 to be string, array given in %s on line %d
+Warning: long2ip() expects parameter 1 to be integer, string given in %sip.php on line %d
+NULL
+
+Warning: long2ip() expects parameter 1 to be integer, array given in %sip.php on line %d
NULL
Done
diff --git a/ext/standard/tests/network/ip_x86_64.phpt b/ext/standard/tests/network/ip_x86_64.phpt
index 1fcb8b2043..45a48ca9df 100644
--- a/ext/standard/tests/network/ip_x86_64.phpt
+++ b/ext/standard/tests/network/ip_x86_64.phpt
@@ -48,20 +48,22 @@ string(7) "0.0.0.0"
int(1118019956)
string(14) "66.163.161.116"
-Warning: ip2long() expects exactly 1 parameter, 0 given in %s on line %d
+Warning: ip2long() expects exactly 1 parameter, 0 given in %sip_x86_64.php on line %d
NULL
bool(false)
bool(false)
int(1869573999)
-Warning: ip2long() expects parameter 1 to be string, array given in %s on line %d
+Warning: ip2long() expects parameter 1 to be string, array given in %sip_x86_64.php on line %d
NULL
-Warning: long2ip() expects exactly 1 parameter, 0 given in %s on line %d
+Warning: long2ip() expects exactly 1 parameter, 0 given in %sip_x86_64.php on line %d
NULL
string(13) "255.254.82.80"
-string(7) "0.0.0.0"
-Warning: long2ip() expects parameter 1 to be string, array given in %s on line %d
+Warning: long2ip() expects parameter 1 to be integer, string given in %sip_x86_64.php on line %d
+NULL
+
+Warning: long2ip() expects parameter 1 to be integer, array given in %sip_x86_64.php on line %d
NULL
Done
diff --git a/ext/standard/tests/network/long2ip_variation1.phpt b/ext/standard/tests/network/long2ip_variation1.phpt
index 2dc6fb123b..e7336712be 100644
--- a/ext/standard/tests/network/long2ip_variation1.phpt
+++ b/ext/standard/tests/network/long2ip_variation1.phpt
@@ -126,19 +126,19 @@ string(15) "255.255.255.246"
string(7) "0.0.0.0"
--empty array--
-Error: 2 - long2ip() expects parameter 1 to be string, array given, %s(%d)
+Error: 2 - long2ip() expects parameter 1 to be integer, array given, %slong2ip_variation1.php(%d)
NULL
--int indexed array--
-Error: 2 - long2ip() expects parameter 1 to be string, array given, %s(%d)
+Error: 2 - long2ip() expects parameter 1 to be integer, array given, %slong2ip_variation1.php(%d)
NULL
--associative array--
-Error: 2 - long2ip() expects parameter 1 to be string, array given, %s(%d)
+Error: 2 - long2ip() expects parameter 1 to be integer, array given, %slong2ip_variation1.php(%d)
NULL
--nested arrays--
-Error: 2 - long2ip() expects parameter 1 to be string, array given, %s(%d)
+Error: 2 - long2ip() expects parameter 1 to be integer, array given, %slong2ip_variation1.php(%d)
NULL
--uppercase NULL--
@@ -160,28 +160,35 @@ string(7) "0.0.0.1"
string(7) "0.0.0.0"
--empty string DQ--
-string(7) "0.0.0.0"
+Error: 2 - long2ip() expects parameter 1 to be integer, string given, %slong2ip_variation1.php(%d)
+NULL
--empty string SQ--
-string(7) "0.0.0.0"
+Error: 2 - long2ip() expects parameter 1 to be integer, string given, %slong2ip_variation1.php(%d)
+NULL
--string DQ--
-string(7) "0.0.0.0"
+Error: 2 - long2ip() expects parameter 1 to be integer, string given, %slong2ip_variation1.php(%d)
+NULL
--string SQ--
-string(7) "0.0.0.0"
+Error: 2 - long2ip() expects parameter 1 to be integer, string given, %slong2ip_variation1.php(%d)
+NULL
--mixed case string--
-string(7) "0.0.0.0"
+Error: 2 - long2ip() expects parameter 1 to be integer, string given, %slong2ip_variation1.php(%d)
+NULL
--heredoc--
-string(7) "0.0.0.0"
+Error: 2 - long2ip() expects parameter 1 to be integer, string given, %slong2ip_variation1.php(%d)
+NULL
--instance of classWithToString--
-string(7) "0.0.0.0"
+Error: 2 - long2ip() expects parameter 1 to be integer, object given, %slong2ip_variation1.php(%d)
+NULL
--instance of classWithoutToString--
-Error: 2 - long2ip() expects parameter 1 to be string, object given, %s(%d)
+Error: 2 - long2ip() expects parameter 1 to be integer, object given, %slong2ip_variation1.php(%d)
NULL
--undefined var--
@@ -191,6 +198,6 @@ string(7) "0.0.0.0"
string(7) "0.0.0.0"
--resource--
-Error: 2 - long2ip() expects parameter 1 to be string, resource given, %s(%d)
+Error: 2 - long2ip() expects parameter 1 to be integer, resource given, %slong2ip_variation1.php(%d)
NULL
===DONE===
diff --git a/ext/standard/tests/streams/bug60602.phpt b/ext/standard/tests/streams/bug60602.phpt
index 2c08ce87b7..f0a3cf83e8 100644
--- a/ext/standard/tests/streams/bug60602.phpt
+++ b/ext/standard/tests/streams/bug60602.phpt
@@ -49,8 +49,6 @@ if (is_resource($p)) {
==DONE==
--EXPECTF--
Notice: Array to string conversion in %s on line %d
-
-Notice: Array to string conversion in %s on line %d
int(%d)
int(0)
bool(true)
diff --git a/ext/standard/tests/streams/stream_context_tcp_nodelay.phpt b/ext/standard/tests/streams/stream_context_tcp_nodelay.phpt
new file mode 100644
index 0000000000..ca3289fbc2
--- /dev/null
+++ b/ext/standard/tests/streams/stream_context_tcp_nodelay.phpt
@@ -0,0 +1,22 @@
+--TEST--
+stream context tcp_nodelay
+--SKIPIF--
+<?php if (!extension_loaded("sockets")) die("skip: need sockets") ?>
+--FILE--
+<?php
+$ctxt = stream_context_create([
+ "socket" => [
+ "tcp_nodelay" => true
+ ]
+]);
+
+$stream = stream_socket_client(
+ "tcp://www.php.net:80", $errno, $errstr, 10, STREAM_CLIENT_CONNECT, $ctxt);
+
+$socket =
+ socket_import_stream($stream);
+
+var_dump(socket_get_option($socket, SOL_TCP, TCP_NODELAY) > 0);
+?>
+--EXPECT--
+bool(true)
diff --git a/ext/standard/tests/streams/stream_context_tcp_nodelay_fopen.phpt b/ext/standard/tests/streams/stream_context_tcp_nodelay_fopen.phpt
new file mode 100644
index 0000000000..08ad4c2d2e
--- /dev/null
+++ b/ext/standard/tests/streams/stream_context_tcp_nodelay_fopen.phpt
@@ -0,0 +1,21 @@
+--TEST--
+stream context tcp_nodelay fopen
+--SKIPIF--
+<?php if (!extension_loaded("sockets")) die("skip: need sockets") ?>
+--FILE--
+<?php
+$ctxt = stream_context_create([
+ "socket" => [
+ "tcp_nodelay" => true
+ ]
+]);
+
+$stream = fopen("http://www.php.net", "r", false, $ctxt);
+
+$socket =
+ @socket_import_stream($stream);
+
+var_dump(socket_get_option($socket, STREAM_IPPROTO_TCP, TCP_NODELAY) > 0);
+?>
+--EXPECT--
+bool(true)
diff --git a/ext/standard/tests/streams/stream_context_tcp_nodelay_server.phpt b/ext/standard/tests/streams/stream_context_tcp_nodelay_server.phpt
new file mode 100644
index 0000000000..6606a15052
--- /dev/null
+++ b/ext/standard/tests/streams/stream_context_tcp_nodelay_server.phpt
@@ -0,0 +1,47 @@
+--TEST--
+stream context tcp_nodelay server
+--SKIPIF--
+<?php if (!extension_loaded("sockets")) die("skip: need sockets") ?>
+--FILE--
+<?php
+$serverCode = <<<'CODE'
+ $ctxt = stream_context_create([
+ "socket" => [
+ "tcp_nodelay" => true
+ ]
+ ]);
+
+ $server = stream_socket_server(
+ "tcp://127.0.0.1:9099", $errno, $errstr, STREAM_SERVER_BIND | STREAM_SERVER_LISTEN, $ctxt);
+
+ $client = stream_socket_accept($server);
+
+ var_dump(socket_get_option(
+ socket_import_stream($server),
+ SOL_TCP, TCP_NODELAY) > 0);
+
+ var_dump(socket_get_option(
+ socket_import_stream($client),
+ SOL_TCP, TCP_NODELAY) > 0);
+
+ fclose($client);
+ fclose($server);
+CODE;
+
+$clientCode = <<<'CODE'
+ $test = stream_socket_client(
+ "tcp://127.0.0.1:9099", $errno, $errstr, 10);
+
+ sleep(1);
+
+ fclose($test);
+CODE;
+
+include sprintf(
+ "%s/../../../openssl/tests/ServerClientTestCase.inc",
+ dirname(__FILE__));
+ServerClientTestCase::getInstance()->run($serverCode, $clientCode);
+?>
+--EXPECT--
+bool(false)
+bool(true)
diff --git a/ext/standard/tests/strings/bug40754.phpt b/ext/standard/tests/strings/bug40754.phpt
index 6cfe47056b..84e4337463 100644
--- a/ext/standard/tests/strings/bug40754.phpt
+++ b/ext/standard/tests/strings/bug40754.phpt
@@ -32,7 +32,7 @@ var_dump(substr("abcde", $v, $v));
bool(false)
bool(false)
-Warning: substr_count(): Offset value 2147483647 exceeds string length in %s on line %d
+Warning: substr_count(): Offset not contained in string in %s on line %d
bool(false)
Warning: substr_compare(): The start position cannot exceed initial string length in %s on line %d
@@ -41,10 +41,10 @@ bool(false)
Warning: stripos(): Offset not contained in string in %s on line %d
bool(false)
-Warning: substr_count(): Offset value 2147483647 exceeds string length in %s on line %d
+Warning: substr_count(): Offset not contained in string in %s on line %d
bool(false)
-Warning: substr_count(): Length value 2147483647 exceeds string length in %s on line %d
+Warning: substr_count(): Invalid length value in %s on line %d
bool(false)
Warning: strpos(): Offset not contained in string in %s on line %d
diff --git a/ext/standard/tests/strings/bug53021.phpt b/ext/standard/tests/strings/bug53021.phpt
index 4a8fbe4f76..38d904761d 100644
--- a/ext/standard/tests/strings/bug53021.phpt
+++ b/ext/standard/tests/strings/bug53021.phpt
@@ -10,11 +10,14 @@ echo html_entity_decode("&quot;", ENT_QUOTES, 'UTF-8'), "\n";
echo html_entity_decode("&#34;", ENT_QUOTES, 'UTF-8'), "\n";
echo html_entity_decode("&quot;", ENT_COMPAT, 'UTF-8'), "\n";
echo html_entity_decode("&#34;", ENT_COMPAT, 'UTF-8'), "\n";
+echo html_entity_decode("&quot;"), "\n";
+echo html_entity_decode("&#34;"), "\n";
echo "\nsingle quotes variations:", "\n";
echo html_entity_decode("&#39;", ENT_NOQUOTES, 'UTF-8'), "\n";
echo html_entity_decode("&#39;", ENT_QUOTES, 'UTF-8'), "\n";
echo html_entity_decode("&#39;", ENT_COMPAT, 'UTF-8'), "\n";
+echo html_entity_decode("&#39;"), "\n";
--EXPECT--
array(1) {
[1]=>
@@ -27,8 +30,11 @@ double quotes variations:
"
"
"
+"
+"
single quotes variations:
&#39;
'
&#39;
+&#39;
diff --git a/ext/standard/tests/strings/bug55871.phpt b/ext/standard/tests/strings/bug55871.phpt
index 249d1bd3a3..0044f50ce7 100644
--- a/ext/standard/tests/strings/bug55871.phpt
+++ b/ext/standard/tests/strings/bug55871.phpt
@@ -41,6 +41,8 @@ array(1) {
[0]=>
string(0) ""
}
+
+Warning: A non-numeric value encountered in %s on line %d
array(1) {
[0]=>
string(40) "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
diff --git a/ext/standard/tests/strings/html_entity_decode3.phpt b/ext/standard/tests/strings/html_entity_decode3.phpt
index fcf2710679..48cff17bda 100644
--- a/ext/standard/tests/strings/html_entity_decode3.phpt
+++ b/ext/standard/tests/strings/html_entity_decode3.phpt
@@ -14,6 +14,7 @@ $tests = array(
"&#x0E;",
"&#x1F;",
"&#x20;", //allowed always
+ "&#x27;", //single quote, depends on flags
"&#x7F;", //DEL
"&#x80;", //C1
"&#x9F;",
@@ -76,6 +77,17 @@ foreach ($tests as $t) {
}
}
+echo "\n*** Default options ***\n";
+
+foreach ($tests as $t) {
+ $dec = html_entity_decode($t);
+ if ($t == $dec) {
+ echo "$t\tNOT DECODED\n";
+ } else {
+ echo "$t\tDECODED\n";
+ }
+}
+
echo "\nDone.\n";
--EXPECT--
*** HTML 4.01 ***
@@ -89,6 +101,7 @@ echo "\nDone.\n";
&#x0E; NOT DECODED
&#x1F; NOT DECODED
&#x20; DECODED
+&#x27; DECODED
&#x7F; NOT DECODED
&#x80; NOT DECODED
&#x9F; NOT DECODED
@@ -117,6 +130,7 @@ echo "\nDone.\n";
&#x0E; NOT DECODED
&#x1F; NOT DECODED
&#x20; DECODED
+&#x27; DECODED
&#x7F; DECODED
&#x80; DECODED
&#x9F; DECODED
@@ -145,6 +159,7 @@ echo "\nDone.\n";
&#x0E; NOT DECODED
&#x1F; NOT DECODED
&#x20; DECODED
+&#x27; DECODED
&#x7F; NOT DECODED
&#x80; NOT DECODED
&#x9F; NOT DECODED
@@ -173,6 +188,7 @@ echo "\nDone.\n";
&#x0E; NOT DECODED
&#x1F; NOT DECODED
&#x20; DECODED
+&#x27; DECODED
&#x7F; DECODED
&#x80; DECODED
&#x9F; DECODED
@@ -190,4 +206,33 @@ echo "\nDone.\n";
&#x2FFFE; DECODED
&#x2FFFF; DECODED
+*** Default options ***
+&#0; NOT DECODED
+&#1; NOT DECODED
+&#x09; DECODED
+&#x0A; DECODED
+&#x0B; NOT DECODED
+&#x0C; NOT DECODED
+&#x0D; DECODED
+&#x0E; NOT DECODED
+&#x1F; NOT DECODED
+&#x20; DECODED
+&#x27; NOT DECODED
+&#x7F; NOT DECODED
+&#x80; NOT DECODED
+&#x9F; NOT DECODED
+&#xA0; DECODED
+&#xD7FF; DECODED
+&#xD800; NOT DECODED
+&#xDFFF; NOT DECODED
+&#xE000; DECODED
+&#xFFFE; DECODED
+&#xFFFF; DECODED
+&#xFDCF; DECODED
+&#xFDD0; DECODED
+&#xFDEF; DECODED
+&#xFDF0; DECODED
+&#x2FFFE; DECODED
+&#x2FFFF; DECODED
+
Done.
diff --git a/ext/standard/tests/strings/stripos_basic2.phpt b/ext/standard/tests/strings/stripos_basic2.phpt
index 3022bae168..226d3f61d2 100644
--- a/ext/standard/tests/strings/stripos_basic2.phpt
+++ b/ext/standard/tests/strings/stripos_basic2.phpt
@@ -18,6 +18,9 @@ var_dump( stripos("Hello, World", "Hello", 0) );
var_dump( stripos("Hello, World", 'Hello', 1) );
var_dump( stripos('Hello, World', 'WORLD', 1) );
var_dump( stripos('Hello, World', "WoRld", 5) );
+var_dump( stripos('Hello, World', "WoRld", -6) );
+var_dump( stripos('Hello, World', "WoRld", -3) );
+var_dump( stripos('Hello, World', "WoRld", -12) );
//heredoc string for haystack & needle, with various offsets
var_dump( stripos($heredoc_str, "Hello, World", 0) );
@@ -25,12 +28,19 @@ var_dump( stripos($heredoc_str, 'Hello', 0) );
var_dump( stripos($heredoc_str, 'Hello', 1) );
var_dump( stripos($heredoc_str, $heredoc_str, 0) );
var_dump( stripos($heredoc_str, $heredoc_str, 1) );
+var_dump( stripos($heredoc_str, $heredoc_str, -strlen($heredoc_str)) );
+var_dump( stripos($heredoc_str, $heredoc_str, -strlen($heredoc_str)+1) );
//various offsets
var_dump( stripos("Hello, World", "o", 3) );
var_dump( stripos("Hello, World", "O", 5) );
var_dump( stripos("Hello, World", "o", 6) );
var_dump( stripos("Hello, World", "o", 10) );
+var_dump( stripos("Hello, World", "o", -7) );
+var_dump( stripos("Hello, World", "o", -8) );
+var_dump( stripos("Hello, World", "o", -10) );
+var_dump( stripos("Hello, World", "o", -4) );
+var_dump( stripos("Hello, World", "o", -3) );
echo "*** Done ***";
?>
--EXPECTF--
@@ -40,13 +50,23 @@ int(0)
bool(false)
int(7)
int(7)
+int(7)
+bool(false)
+int(7)
int(0)
int(0)
bool(false)
int(0)
bool(false)
+int(0)
+bool(false)
int(4)
int(8)
int(8)
bool(false)
+int(8)
+int(4)
+int(4)
+int(8)
+bool(false)
*** Done ***
diff --git a/ext/standard/tests/strings/stripos_error.phpt b/ext/standard/tests/strings/stripos_error.phpt
index ef6ad9e6ec..c59473046d 100644
--- a/ext/standard/tests/strings/stripos_error.phpt
+++ b/ext/standard/tests/strings/stripos_error.phpt
@@ -16,6 +16,13 @@ var_dump( stripos("String") );
echo "\n-- With more than expected number of arguments --";
var_dump( stripos("string", "String", 1, 'extra_arg') );
+
+echo "\n-- Offset beyond the end of the string --";
+var_dump( stripos("Hello World", "o", 12) );
+
+echo "\n-- Offset before the start of the string --";
+var_dump( stripos("Hello World", "o", -12) );
+
echo "*** Done ***";
?>
--EXPECTF--
@@ -32,4 +39,12 @@ NULL
-- With more than expected number of arguments --
Warning: stripos() expects at most 3 parameters, 4 given in %s on line %d
NULL
+
+-- Offset beyond the end of the string --
+Warning: stripos(): Offset not contained in string in %s on line %d
+bool(false)
+
+-- Offset before the start of the string --
+Warning: stripos(): Offset not contained in string in %s on line %d
+bool(false)
*** Done ***
diff --git a/ext/standard/tests/strings/stripos_variation14.phpt b/ext/standard/tests/strings/stripos_variation14.phpt
index aabf0e62ea..3339e2f27e 100644
--- a/ext/standard/tests/strings/stripos_variation14.phpt
+++ b/ext/standard/tests/strings/stripos_variation14.phpt
@@ -1,7 +1,5 @@
--TEST--
Test stripos() function : usage variations - unexpected inputs for 'offset' argument
---SKIPIF--
-<?php if (PHP_INT_SIZE != 8) die("skip this test is for 64-bit only");
--FILE--
<?php
/* Prototype : int stripos ( string $haystack, string $needle [, int $offset] );
@@ -37,7 +35,7 @@ $offsets = array (
// float values
1.5,
-1.5,
- 1.5e10,
+ 1.5e6,
1.6E-10,
.5,
@@ -91,8 +89,6 @@ echo "*** Done ***";
-- Iteration 1 --
int(6)
-- Iteration 2 --
-
-Warning: stripos(): Offset not contained in string in %s on line %d
bool(false)
-- Iteration 3 --
diff --git a/ext/standard/tests/strings/strpos.phpt b/ext/standard/tests/strings/strpos.phpt
index 36854d1b37..f5d60a69b5 100644
--- a/ext/standard/tests/strings/strpos.phpt
+++ b/ext/standard/tests/strings/strpos.phpt
Binary files differ
diff --git a/ext/standard/tests/strings/substr_count_basic.phpt b/ext/standard/tests/strings/substr_count_basic.phpt
index f880e9481e..c7c96fd55b 100644
--- a/ext/standard/tests/strings/substr_count_basic.phpt
+++ b/ext/standard/tests/strings/substr_count_basic.phpt
@@ -9,12 +9,17 @@ var_dump(@substr_count("a", ""));
var_dump(@substr_count("", "a"));
var_dump(@substr_count("", "a"));
var_dump(@substr_count("", chr(0)));
+
$a = str_repeat("abcacba", 100);
var_dump(@substr_count($a, "bca"));
+
$a = str_repeat("abcacbabca", 100);
var_dump(@substr_count($a, "bca"));
var_dump(substr_count($a, "bca", 200));
var_dump(substr_count($a, "bca", 200, 50));
+var_dump(substr_count($a, "bca", -200));
+var_dump(substr_count($a, "bca", -200, 50));
+var_dump(substr_count($a, "bca", -200, -50));
echo "Done\n";
@@ -30,4 +35,7 @@ int(100)
int(200)
int(160)
int(10)
+int(40)
+int(10)
+int(30)
Done
diff --git a/ext/standard/tests/strings/substr_count_error.phpt b/ext/standard/tests/strings/substr_count_error.phpt
index f6924217b9..881da391e6 100644
--- a/ext/standard/tests/strings/substr_count_error.phpt
+++ b/ext/standard/tests/strings/substr_count_error.phpt
@@ -4,27 +4,31 @@ Test substr_count() function (error conditions)
<?php
echo "\n*** Testing error conditions ***\n";
+$str = 'abcdefghik';
+
/* Zero argument */
var_dump( substr_count() );
/* more than expected no. of args */
var_dump( substr_count($str, "t", 0, 15, 30) );
-/* offset as negative value */
-var_dump(substr_count($str, "t", -5));
+/* offset before start */
+var_dump(substr_count($str, "t", -20));
/* offset > size of the string */
var_dump(substr_count($str, "t", 25));
/* Using offset and length to go beyond the size of the string:
Warning message expected, as length+offset > length of string */
-var_dump( substr_count($str, "i", 5, 15) );
+var_dump( substr_count($str, "i", 5, 7) );
-/* length as Null */
-var_dump( substr_count($str, "t", "", "") );
-var_dump( substr_count($str, "i", NULL, NULL) );
-
-echo "Done\n";
+/* Invalid offset argument */
+var_dump( substr_count($str, "t", "") );
+
+/* length too small */
+var_dump( substr_count($str, "t", 2, -20) );
+
+echo "Done\n";
?>
--EXPECTF--
@@ -33,33 +37,21 @@ echo "Done\n";
Warning: substr_count() expects at least 2 parameters, 0 given in %s on line %d
NULL
-Notice: Undefined variable: str in %s on line %d
-
Warning: substr_count() expects at most 4 parameters, 5 given in %s on line %d
NULL
-Notice: Undefined variable: str in %s on line %d
-
-Warning: substr_count(): Offset should be greater than or equal to 0 in %s on line %d
+Warning: substr_count(): Offset not contained in string in %s on line %d
bool(false)
-Notice: Undefined variable: str in %s on line %d
-
-Warning: substr_count(): Offset value 25 exceeds string length in %s on line %d
+Warning: substr_count(): Offset not contained in string in %s on line %d
bool(false)
-Notice: Undefined variable: str in %s on line %d
-
-Warning: substr_count(): Offset value 5 exceeds string length in %s on line %d
+Warning: substr_count(): Invalid length value in %s on line %d
bool(false)
-Notice: Undefined variable: str in %s on line %d
-
Warning: substr_count() expects parameter 3 to be integer, string given in %s on line %d
NULL
-Notice: Undefined variable: str in %s on line %d
-
-Warning: substr_count(): Length should be greater than 0 in %s on line %d
+Warning: substr_count(): Invalid length value in %s on line %d
bool(false)
Done
diff --git a/ext/standard/tests/strings/unpack_error.phpt b/ext/standard/tests/strings/unpack_error.phpt
index 1ef97ccbaf..3a4f334c3b 100644
--- a/ext/standard/tests/strings/unpack_error.phpt
+++ b/ext/standard/tests/strings/unpack_error.phpt
@@ -15,7 +15,7 @@ var_dump( unpack() );
echo "\n-- Testing unpack() function with more than expected no. of arguments --\n";
$extra_arg = 10;
-var_dump(unpack("I", pack("I", 65534), $extra_arg));
+var_dump(unpack("I", pack("I", 65534), 0, $extra_arg));
echo "\n-- Testing unpack() function with invalid format character --\n";
$extra_arg = 10;
@@ -27,12 +27,12 @@ var_dump(unpack("G", pack("I", 65534)));
-- Testing unpack() function with no arguments --
-Warning: unpack() expects exactly 2 parameters, 0 given in %s on line %d
+Warning: unpack() expects at least 2 parameters, 0 given in %s on line %d
NULL
-- Testing unpack() function with more than expected no. of arguments --
-Warning: unpack() expects exactly 2 parameters, 3 given in %s on line %d
+Warning: unpack() expects at most 3 parameters, 4 given in %s on line %d
NULL
-- Testing unpack() function with invalid format character --
diff --git a/ext/standard/tests/strings/unpack_offset.phpt b/ext/standard/tests/strings/unpack_offset.phpt
new file mode 100644
index 0000000000..c8c08e74f2
--- /dev/null
+++ b/ext/standard/tests/strings/unpack_offset.phpt
@@ -0,0 +1,17 @@
+--TEST--
+unpack() with offset
+--FILE--
+<?php
+$data = "pad" . pack("ll", 0x01020304, 0x05060708);
+
+$a = unpack("l2", $data, 3);
+printf("0x%08x 0x%08x\n", $a[1], $a[2]);
+
+printf("0x%08x 0x%08x\n",
+ unpack("l", $data, 3)[1],
+ unpack("@4/l", $data, 3)[1]);
+?>
+--EXPECT--
+0x01020304 0x05060708
+0x01020304 0x05060708
+
diff --git a/ext/standard/tests/strings/url_t.phpt b/ext/standard/tests/strings/url_t.phpt
index e172061ec2..8fdddcb9f5 100644
--- a/ext/standard/tests/strings/url_t.phpt
+++ b/ext/standard/tests/strings/url_t.phpt
@@ -575,22 +575,7 @@ $sample_urls = array (
string(16) "some_page_ref123"
}
---> http://secret@hideout@www.php.net:80/index.php?test=1&test2=char&test3=mixesCI#some_page_ref123: array(7) {
- ["scheme"]=>
- string(4) "http"
- ["host"]=>
- string(11) "www.php.net"
- ["port"]=>
- int(80)
- ["user"]=>
- string(14) "secret@hideout"
- ["path"]=>
- string(10) "/index.php"
- ["query"]=>
- string(31) "test=1&test2=char&test3=mixesCI"
- ["fragment"]=>
- string(16) "some_page_ref123"
-}
+--> http://secret@hideout@www.php.net:80/index.php?test=1&test2=char&test3=mixesCI#some_page_ref123: bool(false)
--> http://secret:hid:out@www.php.net:80/index.php?test=1&test2=char&test3=mixesCI#some_page_ref123: array(8) {
["scheme"]=>
@@ -759,22 +744,7 @@ $sample_urls = array (
string(7) "9130731"
}
---> http://user:@pass@host/path?argument?value#etc: array(7) {
- ["scheme"]=>
- string(4) "http"
- ["host"]=>
- string(4) "host"
- ["user"]=>
- string(4) "user"
- ["pass"]=>
- string(5) "@pass"
- ["path"]=>
- string(5) "/path"
- ["query"]=>
- string(14) "argument?value"
- ["fragment"]=>
- string(3) "etc"
-}
+--> http://user:@pass@host/path?argument?value#etc: bool(false)
string(4) "http"
string(11) "www.php.net"
int(80)
diff --git a/ext/standard/tests/url/get_headers_error_001.phpt b/ext/standard/tests/url/get_headers_error_001.phpt
index 8d5fd11f60..270c8350c0 100644
--- a/ext/standard/tests/url/get_headers_error_001.phpt
+++ b/ext/standard/tests/url/get_headers_error_001.phpt
@@ -5,7 +5,7 @@ June Henriksen <juneih@redpill-linpro.com>
#PHPTestFest2009 Norway 2009-06-09 \o/
--FILE--
<?php
-/* Prototype : proto array get_headers(string url[, int format])
+/* Prototype : proto array get_headers(string url[, int format[, resource $context]])
* Description: Fetches all the headers sent by the server in response to a HTTP request
* Source code: ext/standard/url.c
* Alias to functions:
@@ -21,8 +21,9 @@ var_dump( get_headers() );
echo "\n-- Testing get_headers() function with more than expected no. of arguments --\n";
$url = 'string_val';
$format = 1;
+$context = stream_context_get_default();
$extra_arg = 10;
-var_dump( get_headers($url, $format, $extra_arg) );
+var_dump( get_headers($url, $format, $context, $extra_arg) );
echo "Done";
?>
@@ -36,7 +37,7 @@ NULL
-- Testing get_headers() function with more than expected no. of arguments --
-Warning: get_headers() expects at most 2 parameters, 3 given in %s on line 19
+Warning: get_headers() expects at most 3 parameters, 4 given in %s on line 20
NULL
Done
diff --git a/ext/standard/tests/url/get_headers_error_003.phpt b/ext/standard/tests/url/get_headers_error_003.phpt
new file mode 100644
index 0000000000..6c8878513c
--- /dev/null
+++ b/ext/standard/tests/url/get_headers_error_003.phpt
@@ -0,0 +1,31 @@
+--TEST--
+Test get_headers() function : test with context
+--FILE--
+<?php
+
+include dirname(__FILE__)."/../../../../sapi/cli/tests/php_cli_server.inc";
+php_cli_server_start('header("X-Request-Method: ".$_SERVER["REQUEST_METHOD"]);');
+
+$opts = array(
+ 'http' => array(
+ 'method' => 'HEAD'
+ )
+);
+
+$context = stream_context_create($opts);
+$headers = get_headers("http://".PHP_CLI_SERVER_ADDRESS, 1, $context);
+echo $headers["X-Request-Method"]."\n";
+
+stream_context_set_default($opts);
+$headers = get_headers("http://".PHP_CLI_SERVER_ADDRESS, 1);
+echo $headers["X-Request-Method"]."\n";
+
+echo "Done";
+?>
+--EXPECTF--
+HEAD
+HEAD
+Done
+
+
+
diff --git a/ext/standard/tests/url/parse_url_basic_001.phpt b/ext/standard/tests/url/parse_url_basic_001.phpt
index 0708691fe3..e482566b88 100644
--- a/ext/standard/tests/url/parse_url_basic_001.phpt
+++ b/ext/standard/tests/url/parse_url_basic_001.phpt
@@ -507,23 +507,6 @@ echo "Done";
string(16) "some_page_ref123"
}
---> http://secret@hideout@www.php.net:80/index.php?test=1&test2=char&test3=mixesCI#some_page_ref123: array(7) {
- ["scheme"]=>
- string(4) "http"
- ["host"]=>
- string(11) "www.php.net"
- ["port"]=>
- int(80)
- ["user"]=>
- string(14) "secret@hideout"
- ["path"]=>
- string(10) "/index.php"
- ["query"]=>
- string(31) "test=1&test2=char&test3=mixesCI"
- ["fragment"]=>
- string(16) "some_page_ref123"
-}
-
--> http://secret:hid:out@www.php.net:80/index.php?test=1&test2=char&test3=mixesCI#some_page_ref123: array(8) {
["scheme"]=>
string(4) "http"
@@ -691,23 +674,6 @@ echo "Done";
string(7) "9130731"
}
---> http://user:@pass@host/path?argument?value#etc: array(7) {
- ["scheme"]=>
- string(4) "http"
- ["host"]=>
- string(4) "host"
- ["user"]=>
- string(4) "user"
- ["pass"]=>
- string(5) "@pass"
- ["path"]=>
- string(5) "/path"
- ["query"]=>
- string(14) "argument?value"
- ["fragment"]=>
- string(3) "etc"
-}
-
--> http://10.10.10.10/:80: array(3) {
["scheme"]=>
string(4) "http"
@@ -883,4 +849,10 @@ echo "Done";
--> http://blah.com:123456: bool(false)
--> http://blah.com:abcdef: bool(false)
+
+--> http://secret@hideout@www.php.net:80/index.php?test=1&test2=char&test3=mixesCI#some_page_ref123: bool(false)
+
+--> http://user:@pass@host/path?argument?value#etc: bool(false)
+
+--> http://foo.com\@bar.com: bool(false)
Done
diff --git a/ext/standard/tests/url/parse_url_basic_002.phpt b/ext/standard/tests/url/parse_url_basic_002.phpt
index c05d1f487a..b68a82f4a9 100644
--- a/ext/standard/tests/url/parse_url_basic_002.phpt
+++ b/ext/standard/tests/url/parse_url_basic_002.phpt
@@ -69,7 +69,6 @@ echo "Done";
--> http://secret:@www.php.net/index.php?test=1&test2=char&test3=mixesCI#some_page_ref123 : string(4) "http"
--> http://:hideout@www.php.net:80/index.php?test=1&test2=char&test3=mixesCI#some_page_ref123 : string(4) "http"
--> http://secret:hideout@www.php.net/index.php?test=1&test2=char&test3=mixesCI#some_page_ref123 : string(4) "http"
---> http://secret@hideout@www.php.net:80/index.php?test=1&test2=char&test3=mixesCI#some_page_ref123 : string(4) "http"
--> http://secret:hid:out@www.php.net:80/index.php?test=1&test2=char&test3=mixesCI#some_page_ref123 : string(4) "http"
--> nntp://news.php.net : string(4) "nntp"
--> ftp://ftp.gnu.org/gnu/glic/glibc.tar.gz : string(3) "ftp"
@@ -89,7 +88,6 @@ echo "Done";
--> scheme: : string(6) "scheme"
--> foo+bar://baz@bang/bla : string(7) "foo+bar"
--> gg:9130731 : string(2) "gg"
---> http://user:@pass@host/path?argument?value#etc : string(4) "http"
--> http://10.10.10.10/:80 : string(4) "http"
--> http://x:? : string(4) "http"
--> x:blah.com : string(1) "x"
@@ -125,4 +123,7 @@ echo "Done";
--> http://:? : bool(false)
--> http://blah.com:123456 : bool(false)
--> http://blah.com:abcdef : bool(false)
+--> http://secret@hideout@www.php.net:80/index.php?test=1&test2=char&test3=mixesCI#some_page_ref123 : bool(false)
+--> http://user:@pass@host/path?argument?value#etc : bool(false)
+--> http://foo.com\@bar.com : bool(false)
Done
diff --git a/ext/standard/tests/url/parse_url_basic_003.phpt b/ext/standard/tests/url/parse_url_basic_003.phpt
index 88eda504d5..19ee322feb 100644
--- a/ext/standard/tests/url/parse_url_basic_003.phpt
+++ b/ext/standard/tests/url/parse_url_basic_003.phpt
@@ -68,7 +68,6 @@ echo "Done";
--> http://secret:@www.php.net/index.php?test=1&test2=char&test3=mixesCI#some_page_ref123 : string(11) "www.php.net"
--> http://:hideout@www.php.net:80/index.php?test=1&test2=char&test3=mixesCI#some_page_ref123 : string(11) "www.php.net"
--> http://secret:hideout@www.php.net/index.php?test=1&test2=char&test3=mixesCI#some_page_ref123 : string(11) "www.php.net"
---> http://secret@hideout@www.php.net:80/index.php?test=1&test2=char&test3=mixesCI#some_page_ref123 : string(11) "www.php.net"
--> http://secret:hid:out@www.php.net:80/index.php?test=1&test2=char&test3=mixesCI#some_page_ref123 : string(11) "www.php.net"
--> nntp://news.php.net : string(12) "news.php.net"
--> ftp://ftp.gnu.org/gnu/glic/glibc.tar.gz : string(11) "ftp.gnu.org"
@@ -88,7 +87,6 @@ echo "Done";
--> scheme: : NULL
--> foo+bar://baz@bang/bla : string(4) "bang"
--> gg:9130731 : NULL
---> http://user:@pass@host/path?argument?value#etc : string(4) "host"
--> http://10.10.10.10/:80 : string(11) "10.10.10.10"
--> http://x:? : string(1) "x"
--> x:blah.com : NULL
@@ -124,4 +122,7 @@ echo "Done";
--> http://:? : bool(false)
--> http://blah.com:123456 : bool(false)
--> http://blah.com:abcdef : bool(false)
+--> http://secret@hideout@www.php.net:80/index.php?test=1&test2=char&test3=mixesCI#some_page_ref123 : bool(false)
+--> http://user:@pass@host/path?argument?value#etc : bool(false)
+--> http://foo.com\@bar.com : bool(false)
Done
diff --git a/ext/standard/tests/url/parse_url_basic_004.phpt b/ext/standard/tests/url/parse_url_basic_004.phpt
index e3b9abd91c..e26b3976fc 100644
--- a/ext/standard/tests/url/parse_url_basic_004.phpt
+++ b/ext/standard/tests/url/parse_url_basic_004.phpt
@@ -68,7 +68,6 @@ echo "Done";
--> http://secret:@www.php.net/index.php?test=1&test2=char&test3=mixesCI#some_page_ref123 : NULL
--> http://:hideout@www.php.net:80/index.php?test=1&test2=char&test3=mixesCI#some_page_ref123 : int(80)
--> http://secret:hideout@www.php.net/index.php?test=1&test2=char&test3=mixesCI#some_page_ref123 : NULL
---> http://secret@hideout@www.php.net:80/index.php?test=1&test2=char&test3=mixesCI#some_page_ref123 : int(80)
--> http://secret:hid:out@www.php.net:80/index.php?test=1&test2=char&test3=mixesCI#some_page_ref123 : int(80)
--> nntp://news.php.net : NULL
--> ftp://ftp.gnu.org/gnu/glic/glibc.tar.gz : NULL
@@ -88,7 +87,6 @@ echo "Done";
--> scheme: : NULL
--> foo+bar://baz@bang/bla : NULL
--> gg:9130731 : NULL
---> http://user:@pass@host/path?argument?value#etc : NULL
--> http://10.10.10.10/:80 : NULL
--> http://x:? : NULL
--> x:blah.com : NULL
@@ -124,4 +122,7 @@ echo "Done";
--> http://:? : bool(false)
--> http://blah.com:123456 : bool(false)
--> http://blah.com:abcdef : bool(false)
+--> http://secret@hideout@www.php.net:80/index.php?test=1&test2=char&test3=mixesCI#some_page_ref123 : bool(false)
+--> http://user:@pass@host/path?argument?value#etc : bool(false)
+--> http://foo.com\@bar.com : bool(false)
Done
diff --git a/ext/standard/tests/url/parse_url_basic_005.phpt b/ext/standard/tests/url/parse_url_basic_005.phpt
index 5b2cb98f8b..df2095a949 100644
--- a/ext/standard/tests/url/parse_url_basic_005.phpt
+++ b/ext/standard/tests/url/parse_url_basic_005.phpt
@@ -68,7 +68,6 @@ echo "Done";
--> http://secret:@www.php.net/index.php?test=1&test2=char&test3=mixesCI#some_page_ref123 : string(6) "secret"
--> http://:hideout@www.php.net:80/index.php?test=1&test2=char&test3=mixesCI#some_page_ref123 : string(0) ""
--> http://secret:hideout@www.php.net/index.php?test=1&test2=char&test3=mixesCI#some_page_ref123 : string(6) "secret"
---> http://secret@hideout@www.php.net:80/index.php?test=1&test2=char&test3=mixesCI#some_page_ref123 : string(14) "secret@hideout"
--> http://secret:hid:out@www.php.net:80/index.php?test=1&test2=char&test3=mixesCI#some_page_ref123 : string(6) "secret"
--> nntp://news.php.net : NULL
--> ftp://ftp.gnu.org/gnu/glic/glibc.tar.gz : NULL
@@ -88,7 +87,6 @@ echo "Done";
--> scheme: : NULL
--> foo+bar://baz@bang/bla : string(3) "baz"
--> gg:9130731 : NULL
---> http://user:@pass@host/path?argument?value#etc : string(4) "user"
--> http://10.10.10.10/:80 : NULL
--> http://x:? : NULL
--> x:blah.com : NULL
@@ -124,4 +122,7 @@ echo "Done";
--> http://:? : bool(false)
--> http://blah.com:123456 : bool(false)
--> http://blah.com:abcdef : bool(false)
+--> http://secret@hideout@www.php.net:80/index.php?test=1&test2=char&test3=mixesCI#some_page_ref123 : bool(false)
+--> http://user:@pass@host/path?argument?value#etc : bool(false)
+--> http://foo.com\@bar.com : bool(false)
Done
diff --git a/ext/standard/tests/url/parse_url_basic_006.phpt b/ext/standard/tests/url/parse_url_basic_006.phpt
index 79af6b8b62..4c79e8dcb2 100644
--- a/ext/standard/tests/url/parse_url_basic_006.phpt
+++ b/ext/standard/tests/url/parse_url_basic_006.phpt
@@ -68,7 +68,6 @@ echo "Done";
--> http://secret:@www.php.net/index.php?test=1&test2=char&test3=mixesCI#some_page_ref123 : string(0) ""
--> http://:hideout@www.php.net:80/index.php?test=1&test2=char&test3=mixesCI#some_page_ref123 : string(7) "hideout"
--> http://secret:hideout@www.php.net/index.php?test=1&test2=char&test3=mixesCI#some_page_ref123 : string(7) "hideout"
---> http://secret@hideout@www.php.net:80/index.php?test=1&test2=char&test3=mixesCI#some_page_ref123 : NULL
--> http://secret:hid:out@www.php.net:80/index.php?test=1&test2=char&test3=mixesCI#some_page_ref123 : string(7) "hid:out"
--> nntp://news.php.net : NULL
--> ftp://ftp.gnu.org/gnu/glic/glibc.tar.gz : NULL
@@ -88,7 +87,6 @@ echo "Done";
--> scheme: : NULL
--> foo+bar://baz@bang/bla : NULL
--> gg:9130731 : NULL
---> http://user:@pass@host/path?argument?value#etc : string(5) "@pass"
--> http://10.10.10.10/:80 : NULL
--> http://x:? : NULL
--> x:blah.com : NULL
@@ -124,4 +122,7 @@ echo "Done";
--> http://:? : bool(false)
--> http://blah.com:123456 : bool(false)
--> http://blah.com:abcdef : bool(false)
+--> http://secret@hideout@www.php.net:80/index.php?test=1&test2=char&test3=mixesCI#some_page_ref123 : bool(false)
+--> http://user:@pass@host/path?argument?value#etc : bool(false)
+--> http://foo.com\@bar.com : bool(false)
Done
diff --git a/ext/standard/tests/url/parse_url_basic_007.phpt b/ext/standard/tests/url/parse_url_basic_007.phpt
index 8e04553983..52f3a92add 100644
--- a/ext/standard/tests/url/parse_url_basic_007.phpt
+++ b/ext/standard/tests/url/parse_url_basic_007.phpt
@@ -68,7 +68,6 @@ echo "Done";
--> http://secret:@www.php.net/index.php?test=1&test2=char&test3=mixesCI#some_page_ref123 : string(10) "/index.php"
--> http://:hideout@www.php.net:80/index.php?test=1&test2=char&test3=mixesCI#some_page_ref123 : string(10) "/index.php"
--> http://secret:hideout@www.php.net/index.php?test=1&test2=char&test3=mixesCI#some_page_ref123 : string(10) "/index.php"
---> http://secret@hideout@www.php.net:80/index.php?test=1&test2=char&test3=mixesCI#some_page_ref123 : string(10) "/index.php"
--> http://secret:hid:out@www.php.net:80/index.php?test=1&test2=char&test3=mixesCI#some_page_ref123 : string(10) "/index.php"
--> nntp://news.php.net : NULL
--> ftp://ftp.gnu.org/gnu/glic/glibc.tar.gz : string(22) "/gnu/glic/glibc.tar.gz"
@@ -88,7 +87,6 @@ echo "Done";
--> scheme: : NULL
--> foo+bar://baz@bang/bla : string(4) "/bla"
--> gg:9130731 : string(7) "9130731"
---> http://user:@pass@host/path?argument?value#etc : string(5) "/path"
--> http://10.10.10.10/:80 : string(4) "/:80"
--> http://x:? : NULL
--> x:blah.com : string(8) "blah.com"
@@ -124,4 +122,7 @@ echo "Done";
--> http://:? : bool(false)
--> http://blah.com:123456 : bool(false)
--> http://blah.com:abcdef : bool(false)
+--> http://secret@hideout@www.php.net:80/index.php?test=1&test2=char&test3=mixesCI#some_page_ref123 : bool(false)
+--> http://user:@pass@host/path?argument?value#etc : bool(false)
+--> http://foo.com\@bar.com : bool(false)
Done
diff --git a/ext/standard/tests/url/parse_url_basic_008.phpt b/ext/standard/tests/url/parse_url_basic_008.phpt
index 0c77221465..874c901076 100644
--- a/ext/standard/tests/url/parse_url_basic_008.phpt
+++ b/ext/standard/tests/url/parse_url_basic_008.phpt
@@ -68,7 +68,6 @@ echo "Done";
--> http://secret:@www.php.net/index.php?test=1&test2=char&test3=mixesCI#some_page_ref123 : string(31) "test=1&test2=char&test3=mixesCI"
--> http://:hideout@www.php.net:80/index.php?test=1&test2=char&test3=mixesCI#some_page_ref123 : string(31) "test=1&test2=char&test3=mixesCI"
--> http://secret:hideout@www.php.net/index.php?test=1&test2=char&test3=mixesCI#some_page_ref123 : string(31) "test=1&test2=char&test3=mixesCI"
---> http://secret@hideout@www.php.net:80/index.php?test=1&test2=char&test3=mixesCI#some_page_ref123 : string(31) "test=1&test2=char&test3=mixesCI"
--> http://secret:hid:out@www.php.net:80/index.php?test=1&test2=char&test3=mixesCI#some_page_ref123 : string(31) "test=1&test2=char&test3=mixesCI"
--> nntp://news.php.net : NULL
--> ftp://ftp.gnu.org/gnu/glic/glibc.tar.gz : NULL
@@ -88,7 +87,6 @@ echo "Done";
--> scheme: : NULL
--> foo+bar://baz@bang/bla : NULL
--> gg:9130731 : NULL
---> http://user:@pass@host/path?argument?value#etc : string(14) "argument?value"
--> http://10.10.10.10/:80 : NULL
--> http://x:? : NULL
--> x:blah.com : NULL
@@ -124,4 +122,7 @@ echo "Done";
--> http://:? : bool(false)
--> http://blah.com:123456 : bool(false)
--> http://blah.com:abcdef : bool(false)
+--> http://secret@hideout@www.php.net:80/index.php?test=1&test2=char&test3=mixesCI#some_page_ref123 : bool(false)
+--> http://user:@pass@host/path?argument?value#etc : bool(false)
+--> http://foo.com\@bar.com : bool(false)
Done
diff --git a/ext/standard/tests/url/parse_url_basic_009.phpt b/ext/standard/tests/url/parse_url_basic_009.phpt
index 487b271149..ea0b257751 100644
--- a/ext/standard/tests/url/parse_url_basic_009.phpt
+++ b/ext/standard/tests/url/parse_url_basic_009.phpt
@@ -68,7 +68,6 @@ echo "Done";
--> http://secret:@www.php.net/index.php?test=1&test2=char&test3=mixesCI#some_page_ref123 : string(16) "some_page_ref123"
--> http://:hideout@www.php.net:80/index.php?test=1&test2=char&test3=mixesCI#some_page_ref123 : string(16) "some_page_ref123"
--> http://secret:hideout@www.php.net/index.php?test=1&test2=char&test3=mixesCI#some_page_ref123 : string(16) "some_page_ref123"
---> http://secret@hideout@www.php.net:80/index.php?test=1&test2=char&test3=mixesCI#some_page_ref123 : string(16) "some_page_ref123"
--> http://secret:hid:out@www.php.net:80/index.php?test=1&test2=char&test3=mixesCI#some_page_ref123 : string(16) "some_page_ref123"
--> nntp://news.php.net : NULL
--> ftp://ftp.gnu.org/gnu/glic/glibc.tar.gz : NULL
@@ -88,7 +87,6 @@ echo "Done";
--> scheme: : NULL
--> foo+bar://baz@bang/bla : NULL
--> gg:9130731 : NULL
---> http://user:@pass@host/path?argument?value#etc : string(3) "etc"
--> http://10.10.10.10/:80 : NULL
--> http://x:? : NULL
--> x:blah.com : NULL
@@ -124,4 +122,7 @@ echo "Done";
--> http://:? : bool(false)
--> http://blah.com:123456 : bool(false)
--> http://blah.com:abcdef : bool(false)
+--> http://secret@hideout@www.php.net:80/index.php?test=1&test2=char&test3=mixesCI#some_page_ref123 : bool(false)
+--> http://user:@pass@host/path?argument?value#etc : bool(false)
+--> http://foo.com\@bar.com : bool(false)
Done
diff --git a/ext/standard/tests/url/urls.inc b/ext/standard/tests/url/urls.inc
index d8ffe91378..6228bd8b7d 100644
--- a/ext/standard/tests/url/urls.inc
+++ b/ext/standard/tests/url/urls.inc
@@ -48,7 +48,6 @@ $urls = array(
'http://secret:@www.php.net/index.php?test=1&test2=char&test3=mixesCI#some_page_ref123',
'http://:hideout@www.php.net:80/index.php?test=1&test2=char&test3=mixesCI#some_page_ref123',
'http://secret:hideout@www.php.net/index.php?test=1&test2=char&test3=mixesCI#some_page_ref123',
-'http://secret@hideout@www.php.net:80/index.php?test=1&test2=char&test3=mixesCI#some_page_ref123',
'http://secret:hid:out@www.php.net:80/index.php?test=1&test2=char&test3=mixesCI#some_page_ref123',
'nntp://news.php.net',
'ftp://ftp.gnu.org/gnu/glic/glibc.tar.gz',
@@ -68,7 +67,6 @@ $urls = array(
'scheme:',
'foo+bar://baz@bang/bla',
'gg:9130731',
-'http://user:@pass@host/path?argument?value#etc',
'http://10.10.10.10/:80',
'http://x:?',
'x:blah.com',
@@ -106,6 +104,9 @@ $urls = array(
'http://:?',
'http://blah.com:123456',
'http://blah.com:abcdef',
+'http://secret@hideout@www.php.net:80/index.php?test=1&test2=char&test3=mixesCI#some_page_ref123',
+'http://user:@pass@host/path?argument?value#etc',
+'http://foo.com\\@bar.com'
);
diff --git a/ext/standard/type.c b/ext/standard/type.c
index d6a412f4ec..529d666d02 100644
--- a/ext/standard/type.c
+++ b/ext/standard/type.c
@@ -154,7 +154,7 @@ PHP_FUNCTION(intval)
ZEND_PARSE_PARAMETERS_END();
#endif
- if (Z_TYPE_P(num) != IS_STRING) {
+ if (Z_TYPE_P(num) != IS_STRING || base == 10) {
RETVAL_LONG(zval_get_long(num));
} else {
RETVAL_LONG(ZEND_STRTOL(Z_STRVAL_P(num), NULL, base));
diff --git a/ext/standard/url.c b/ext/standard/url.c
index 78ca472a2b..d69c11f1c1 100644
--- a/ext/standard/url.c
+++ b/ext/standard/url.c
@@ -242,6 +242,19 @@ PHPAPI php_url *php_url_parse_ex(char const *str, size_t length)
/* check for login and password */
if ((p = zend_memrchr(s, '@', (e-s)))) {
+ /* check for invalid chars inside login/pass */
+ pp = s;
+ while (pp < p) {
+ if (!isalnum(*pp) && *pp != ':' && *pp != ';' && *pp != '=' && !(*pp >= '!' && *pp <= ',')) {
+ if (ret->scheme) {
+ efree(ret->scheme);
+ }
+ efree(ret);
+ return NULL;
+ }
+ pp++;
+ }
+
if ((pp = memchr(s, ':', (p-s)))) {
ret->user = estrndup(s, (pp-s));
php_replace_controlchars_ex(ret->user, (pp - s));
@@ -708,22 +721,24 @@ PHPAPI size_t php_raw_url_decode(char *str, size_t len)
}
/* }}} */
-/* {{{ proto array get_headers(string url[, int format])
+/* {{{ proto array get_headers(string url[, int format[, resource context]])
fetches all the headers sent by the server in response to a HTTP request */
PHP_FUNCTION(get_headers)
{
char *url;
size_t url_len;
- php_stream_context *context;
php_stream *stream;
zval *prev_val, *hdr = NULL, *h;
HashTable *hashT;
zend_long format = 0;
+ zval *zcontext = NULL;
+ php_stream_context *context;
- if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|l", &url, &url_len, &format) == FAILURE) {
+ if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|lr!", &url, &url_len, &format, &zcontext) == FAILURE) {
return;
}
- context = FG(default_context) ? FG(default_context) : (FG(default_context) = php_stream_context_alloc());
+
+ context = php_stream_context_from_zval(zcontext, 0);
if (!(stream = php_stream_open_wrapper_ex(url, "r", REPORT_ERRORS | STREAM_USE_URL | STREAM_ONLY_GET_HEADERS, NULL, context))) {
RETURN_FALSE;
diff --git a/ext/standard/var.c b/ext/standard/var.c
index 472cd62d8f..16b9fffba1 100644
--- a/ext/standard/var.c
+++ b/ext/standard/var.c
@@ -1084,6 +1084,11 @@ PHP_FUNCTION(unserialize)
in case nesting calls to unserialize */
var_push_dtor(&var_hash, return_value);
+ /* Ensure return value is a value */
+ if (Z_ISREF_P(return_value)) {
+ zend_unwrap_reference(return_value);
+ }
+
PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
if (class_hash) {
zend_hash_destroy(class_hash);
diff --git a/ext/sysvshm/php_sysvshm.h b/ext/sysvshm/php_sysvshm.h
index 6d53171f11..8bd261992f 100644
--- a/ext/sysvshm/php_sysvshm.h
+++ b/ext/sysvshm/php_sysvshm.h
@@ -33,7 +33,7 @@ extern zend_module_entry sysvshm_module_entry;
#ifdef PHP_WIN32
# include <TSRM/tsrm_win32.h>
-typedef int key_t;
+# include "win32/ipc.h"
# ifndef THREAD_LS
# define THREAD_LS
# endif
diff --git a/ext/wddx/wddx.c b/ext/wddx/wddx.c
index 330cb49f08..e848afdeb7 100644
--- a/ext/wddx/wddx.c
+++ b/ext/wddx/wddx.c
@@ -954,12 +954,8 @@ static void php_wddx_pop_element(void *user_data, const XML_Char *name)
/* Clean up class name var entry */
zval_ptr_dtor(&ent1->data);
} else if (Z_TYPE(ent2->data) == IS_OBJECT) {
- zend_class_entry *old_scope = EG(scope);
-
- EG(scope) = Z_OBJCE(ent2->data);
- add_property_zval(&ent2->data, ent1->varname, &ent1->data);
+ zend_update_property(Z_OBJCE(ent2->data), &ent2->data, ent1->varname, strlen(ent1->varname), &ent1->data);
if Z_REFCOUNTED(ent1->data) Z_DELREF(ent1->data);
- EG(scope) = old_scope;
} else {
zend_symtable_str_update(target_hash, ent1->varname, strlen(ent1->varname), &ent1->data);
}
diff --git a/ext/xml/xml.c b/ext/xml/xml.c
index d788b9b0b8..fe29f2891d 100644
--- a/ext/xml/xml.c
+++ b/ext/xml/xml.c
@@ -111,7 +111,7 @@ ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_xml_set_object, 0, 0, 2)
ZEND_ARG_INFO(0, parser)
- ZEND_ARG_INFO(1, obj)
+ ZEND_ARG_INFO(0, obj)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_xml_set_element_handler, 0, 0, 3)
@@ -286,7 +286,7 @@ xml_encoding xml_encodings[] = {
static XML_Memory_Handling_Suite php_xml_mem_hdlrs;
/* True globals, no need for thread safety */
-static int le_xml_parser;
+static int le_xml_parser;
/* }}} */
@@ -349,7 +349,7 @@ PHP_MINIT_FUNCTION(xml)
REGISTER_LONG_CONSTANT("XML_OPTION_SKIP_WHITE", PHP_XML_OPTION_SKIP_WHITE, CONST_CS|CONST_PERSISTENT);
/* this object should not be pre-initialised at compile time,
- as the order of members may vary */
+ as the order of members may vary */
php_xml_mem_hdlrs.malloc_fcn = php_xml_malloc_wrapper;
php_xml_mem_hdlrs.realloc_fcn = php_xml_realloc_wrapper;
@@ -397,7 +397,7 @@ static void _xml_xmlchar_zval(const XML_Char *s, int len, const XML_Char *encodi
static void xml_parser_dtor(zend_resource *rsrc)
{
xml_parser *parser = (xml_parser *)rsrc->ptr;
-
+
if (parser->parser) {
XML_ParserFree(parser->parser);
}
@@ -483,9 +483,7 @@ static void xml_call_handler(xml_parser *parser, zval *handler, zend_function *f
zend_fcall_info fci;
fci.size = sizeof(fci);
- fci.function_table = EG(function_table);
ZVAL_COPY_VALUE(&fci.function_name, handler);
- fci.symbol_table = NULL;
fci.object = Z_OBJ(parser->object);
fci.retval = retval;
fci.param_count = argc;
@@ -899,7 +897,7 @@ void _xml_characterDataHandler(void *userData, const XML_Char *s, int len)
} else {
add_assoc_str(parser->ctag, "value", decoded_value);
}
-
+
} else {
zval tag;
zval *curtag, *mytype, *myval;
@@ -920,7 +918,7 @@ void _xml_characterDataHandler(void *userData, const XML_Char *s, int len)
break;
} ZEND_HASH_FOREACH_END();
- if (parser->level <= XML_MAXLEVEL && parser->level > 0) {
+ if (parser->level <= XML_MAXLEVEL) {
array_init(&tag);
_xml_add_to_info(parser,parser->ltags[parser->level-1] + parser->toffset);
@@ -977,8 +975,8 @@ void _xml_defaultHandler(void *userData, const XML_Char *s, int len)
/* }}} */
/* {{{ _xml_unparsedEntityDeclHandler() */
-void _xml_unparsedEntityDeclHandler(void *userData,
- const XML_Char *entityName,
+void _xml_unparsedEntityDeclHandler(void *userData,
+ const XML_Char *entityName,
const XML_Char *base,
const XML_Char *systemId,
const XML_Char *publicId,
@@ -1146,15 +1144,15 @@ static void php_xml_parser_create_impl(INTERNAL_FUNCTION_PARAMETERS, int ns_supp
}
/* }}} */
-/* {{{ proto resource xml_parser_create([string encoding])
+/* {{{ proto resource xml_parser_create([string encoding])
Create an XML parser */
PHP_FUNCTION(xml_parser_create)
{
- php_xml_parser_create_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
+ php_xml_parser_create_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
}
/* }}} */
-/* {{{ proto resource xml_parser_create_ns([string encoding [, string sep]])
+/* {{{ proto resource xml_parser_create_ns([string encoding [, string sep]])
Create an XML parser */
PHP_FUNCTION(xml_parser_create_ns)
{
@@ -1162,14 +1160,14 @@ PHP_FUNCTION(xml_parser_create_ns)
}
/* }}} */
-/* {{{ proto int xml_set_object(resource parser, object &obj)
+/* {{{ proto int xml_set_object(resource parser, object &obj)
Set up object which should be used for callbacks */
PHP_FUNCTION(xml_set_object)
{
xml_parser *parser;
zval *pind, *mythis;
- if (zend_parse_parameters(ZEND_NUM_ARGS(), "ro/", &pind, &mythis) == FAILURE) {
+ if (zend_parse_parameters(ZEND_NUM_ARGS(), "ro", &pind, &mythis) == FAILURE) {
return;
}
@@ -1183,9 +1181,7 @@ PHP_FUNCTION(xml_set_object)
}
/* please leave this commented - or ask thies@thieso.net before doing it (again) */
-/* #ifdef ZEND_ENGINE_2
- zval_add_ref(&parser->object);
-#endif */
+ /* zval_add_ref(&parser->object); */
ZVAL_COPY(&parser->object, mythis);
@@ -1193,7 +1189,7 @@ PHP_FUNCTION(xml_set_object)
}
/* }}} */
-/* {{{ proto int xml_set_element_handler(resource parser, string shdl, string ehdl)
+/* {{{ proto int xml_set_element_handler(resource parser, string shdl, string ehdl)
Set up start and end element handlers */
PHP_FUNCTION(xml_set_element_handler)
{
@@ -1215,7 +1211,7 @@ PHP_FUNCTION(xml_set_element_handler)
}
/* }}} */
-/* {{{ proto int xml_set_character_data_handler(resource parser, string hdl)
+/* {{{ proto int xml_set_character_data_handler(resource parser, string hdl)
Set up character data handler */
PHP_FUNCTION(xml_set_character_data_handler)
{
@@ -1236,7 +1232,7 @@ PHP_FUNCTION(xml_set_character_data_handler)
}
/* }}} */
-/* {{{ proto int xml_set_processing_instruction_handler(resource parser, string hdl)
+/* {{{ proto int xml_set_processing_instruction_handler(resource parser, string hdl)
Set up processing instruction (PI) handler */
PHP_FUNCTION(xml_set_processing_instruction_handler)
{
@@ -1257,7 +1253,7 @@ PHP_FUNCTION(xml_set_processing_instruction_handler)
}
/* }}} */
-/* {{{ proto int xml_set_default_handler(resource parser, string hdl)
+/* {{{ proto int xml_set_default_handler(resource parser, string hdl)
Set up default handler */
PHP_FUNCTION(xml_set_default_handler)
{
@@ -1278,7 +1274,7 @@ PHP_FUNCTION(xml_set_default_handler)
}
/* }}} */
-/* {{{ proto int xml_set_unparsed_entity_decl_handler(resource parser, string hdl)
+/* {{{ proto int xml_set_unparsed_entity_decl_handler(resource parser, string hdl)
Set up unparsed entity declaration handler */
PHP_FUNCTION(xml_set_unparsed_entity_decl_handler)
{
@@ -1299,7 +1295,7 @@ PHP_FUNCTION(xml_set_unparsed_entity_decl_handler)
}
/* }}} */
-/* {{{ proto int xml_set_notation_decl_handler(resource parser, string hdl)
+/* {{{ proto int xml_set_notation_decl_handler(resource parser, string hdl)
Set up notation declaration handler */
PHP_FUNCTION(xml_set_notation_decl_handler)
{
@@ -1320,7 +1316,7 @@ PHP_FUNCTION(xml_set_notation_decl_handler)
}
/* }}} */
-/* {{{ proto int xml_set_external_entity_ref_handler(resource parser, string hdl)
+/* {{{ proto int xml_set_external_entity_ref_handler(resource parser, string hdl)
Set up external entity reference handler */
PHP_FUNCTION(xml_set_external_entity_ref_handler)
{
@@ -1341,7 +1337,7 @@ PHP_FUNCTION(xml_set_external_entity_ref_handler)
}
/* }}} */
-/* {{{ proto int xml_set_start_namespace_decl_handler(resource parser, string hdl)
+/* {{{ proto int xml_set_start_namespace_decl_handler(resource parser, string hdl)
Set up character data handler */
PHP_FUNCTION(xml_set_start_namespace_decl_handler)
{
@@ -1362,7 +1358,7 @@ PHP_FUNCTION(xml_set_start_namespace_decl_handler)
}
/* }}} */
-/* {{{ proto int xml_set_end_namespace_decl_handler(resource parser, string hdl)
+/* {{{ proto int xml_set_end_namespace_decl_handler(resource parser, string hdl)
Set up character data handler */
PHP_FUNCTION(xml_set_end_namespace_decl_handler)
{
@@ -1442,7 +1438,7 @@ PHP_FUNCTION(xml_parse_into_struct)
if (info) {
ZVAL_COPY_VALUE(&parser->info, info);
}
-
+
parser->level = 0;
parser->ltags = safe_emalloc(XML_MAXLEVEL, sizeof(char *), 0);
@@ -1458,7 +1454,7 @@ PHP_FUNCTION(xml_parse_into_struct)
}
/* }}} */
-/* {{{ proto int xml_get_error_code(resource parser)
+/* {{{ proto int xml_get_error_code(resource parser)
Get XML parser error code */
PHP_FUNCTION(xml_get_error_code)
{
@@ -1495,7 +1491,7 @@ PHP_FUNCTION(xml_error_string)
}
/* }}} */
-/* {{{ proto int xml_get_current_line_number(resource parser)
+/* {{{ proto int xml_get_current_line_number(resource parser)
Get current line number for an XML parser */
PHP_FUNCTION(xml_get_current_line_number)
{
@@ -1533,7 +1529,7 @@ PHP_FUNCTION(xml_get_current_column_number)
}
/* }}} */
-/* {{{ proto int xml_get_current_byte_index(resource parser)
+/* {{{ proto int xml_get_current_byte_index(resource parser)
Get current byte index for an XML parser */
PHP_FUNCTION(xml_get_current_byte_index)
{
@@ -1552,13 +1548,12 @@ PHP_FUNCTION(xml_get_current_byte_index)
}
/* }}} */
-/* {{{ proto int xml_parser_free(resource parser)
+/* {{{ proto int xml_parser_free(resource parser)
Free an XML parser */
PHP_FUNCTION(xml_parser_free)
{
zval *pind;
xml_parser *parser;
- zend_resource *res;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &pind) == FAILURE) {
return;
@@ -1581,7 +1576,7 @@ PHP_FUNCTION(xml_parser_free)
}
/* }}} */
-/* {{{ proto int xml_parser_set_option(resource parser, int option, mixed value)
+/* {{{ proto int xml_parser_set_option(resource parser, int option, mixed value)
Set options in an XML parser */
PHP_FUNCTION(xml_parser_set_option)
{
@@ -1630,7 +1625,7 @@ PHP_FUNCTION(xml_parser_set_option)
}
/* }}} */
-/* {{{ proto int xml_parser_get_option(resource parser, int option)
+/* {{{ proto int xml_parser_get_option(resource parser, int option)
Get options from an XML parser */
PHP_FUNCTION(xml_parser_get_option)
{
@@ -1663,7 +1658,7 @@ PHP_FUNCTION(xml_parser_get_option)
}
/* }}} */
-/* {{{ proto string utf8_encode(string data)
+/* {{{ proto string utf8_encode(string data)
Encodes an ISO-8859-1 string to UTF-8 */
PHP_FUNCTION(utf8_encode)
{
@@ -1683,7 +1678,7 @@ PHP_FUNCTION(utf8_encode)
}
/* }}} */
-/* {{{ proto string utf8_decode(string data)
+/* {{{ proto string utf8_decode(string data)
Converts a UTF-8 encoded string to ISO-8859-1 */
PHP_FUNCTION(utf8_decode)
{
diff --git a/ext/xsl/php_xsl.h b/ext/xsl/php_xsl.h
index 75ede969a8..985b743734 100644
--- a/ext/xsl/php_xsl.h
+++ b/ext/xsl/php_xsl.h
@@ -115,16 +115,6 @@ ZEND_BEGIN_MODULE_GLOBALS(xsl)
ZEND_END_MODULE_GLOBALS(xsl)
*/
-/* In every utility function you add that needs to use variables
- in php_xsl_globals, call TSRM_FETCH(); after declaring other
- variables used by that function, or better yet, pass in
- after the last function argument and declare your utility function
- with after the last declared argument. Always refer to
- the globals in your function as XSL_G(variable). You are
- encouraged to rename these macros something shorter, see
- examples in any other php module directory.
-*/
-
#ifdef ZTS
#define XSL_G(v) TSRMG(xsl_globals_id, zend_xsl_globals *, v)
#else
diff --git a/ext/xsl/xsltprocessor.c b/ext/xsl/xsltprocessor.c
index 600c7cddb0..5339812253 100644
--- a/ext/xsl/xsltprocessor.c
+++ b/ext/xsl/xsltprocessor.c
@@ -295,7 +295,6 @@ static void xsl_ext_function_php(xmlXPathParserContextPtr ctxt, int nargs, int t
}
fci.size = sizeof(fci);
- fci.function_table = EG(function_table);
if (fci.param_count > 0) {
fci.params = args;
} else {
@@ -320,7 +319,6 @@ static void xsl_ext_function_php(xmlXPathParserContextPtr ctxt, int nargs, int t
xmlXPathFreeObject(obj);
ZVAL_COPY_VALUE(&fci.function_name, &handler);
- fci.symbol_table = NULL;
fci.object = NULL;
fci.retval = &retval;
fci.no_separation = 0;
diff --git a/ext/zip/php_zip.c b/ext/zip/php_zip.c
index 88bb518442..be35c2d2b1 100644
--- a/ext/zip/php_zip.c
+++ b/ext/zip/php_zip.c
@@ -3038,7 +3038,6 @@ static PHP_MINIT_FUNCTION(zip)
REGISTER_ZIP_CLASS_CONST_LONG("FL_NODIR", ZIP_FL_NODIR);
REGISTER_ZIP_CLASS_CONST_LONG("FL_COMPRESSED", ZIP_FL_COMPRESSED);
REGISTER_ZIP_CLASS_CONST_LONG("FL_UNCHANGED", ZIP_FL_UNCHANGED);
-
#ifdef ZIP_FL_ENC_GUESS
/* Default filename encoding policy. */
REGISTER_ZIP_CLASS_CONST_LONG("FL_ENC_GUESS", ZIP_FL_ENC_GUESS);
@@ -3056,6 +3055,20 @@ static PHP_MINIT_FUNCTION(zip)
REGISTER_ZIP_CLASS_CONST_LONG("FL_ENC_CP437", ZIP_FL_ENC_CP437);
#endif
+/* XXX The below are rather not implemented or to check whether makes sense to expose. */
+/*#ifdef ZIP_FL_RECOMPRESS
+ REGISTER_ZIP_CLASS_CONST_LONG("FL_RECOMPRESS", ZIP_FL_RECOMPRESS);
+#endif
+#ifdef ZIP_FL_ENCRYPTED
+ REGISTER_ZIP_CLASS_CONST_LONG("FL_ENCRYPTED", ZIP_FL_ENCRYPTED);
+#endif
+#ifdef ZIP_FL_LOCAL
+ REGISTER_ZIP_CLASS_CONST_LONG("FL_LOCAL", ZIP_FL_LOCAL);
+#endif
+#ifdef ZIP_FL_CENTRAL
+ REGISTER_ZIP_CLASS_CONST_LONG("FL_CENTRAL", ZIP_FL_CENTRAL);
+#endif */
+
REGISTER_ZIP_CLASS_CONST_LONG("CM_DEFAULT", ZIP_CM_DEFAULT);
REGISTER_ZIP_CLASS_CONST_LONG("CM_STORE", ZIP_CM_STORE);
REGISTER_ZIP_CLASS_CONST_LONG("CM_SHRINK", ZIP_CM_SHRINK);
diff --git a/ext/zip/php_zip.h b/ext/zip/php_zip.h
index f5df19e79a..ad65fdeb9d 100644
--- a/ext/zip/php_zip.h
+++ b/ext/zip/php_zip.h
@@ -39,22 +39,7 @@ extern zend_module_entry zip_module_entry;
#define PHP_ZIP_VERSION "1.13.2"
-#ifndef Z_SET_REFCOUNT_P
-# if PHP_MAJOR_VERSION < 6 && (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION < 3)
-# define Z_SET_REFCOUNT_P(pz, rc) pz->refcount = rc
-# define Z_UNSET_ISREF_P(pz) pz->is_ref = 0
-# endif
-#endif
-
-/* {{{ ZIP_OPENBASEDIR_CHECKPATH(filename) */
-#if PHP_API_VERSION < 20100412
-# define ZIP_OPENBASEDIR_CHECKPATH(filename) \
- (PG(safe_mode) && (!php_checkuid(filename, NULL, CHECKUID_CHECK_FILE_AND_DIR))) || php_check_open_basedir(filename)
-#else
-#define ZIP_OPENBASEDIR_CHECKPATH(filename) \
- php_check_open_basedir(filename)
-#endif
-/* }}} */
+#define ZIP_OPENBASEDIR_CHECKPATH(filename) php_check_open_basedir(filename)
typedef struct _ze_zip_rsrc {
struct zip *za;
diff --git a/ext/zip/zip_stream.c b/ext/zip/zip_stream.c
index a1f620182c..a088985198 100644
--- a/ext/zip/zip_stream.c
+++ b/ext/zip/zip_stream.c
@@ -21,7 +21,6 @@
#endif
#include "php.h"
#if HAVE_ZIP
-#if defined(ZEND_ENGINE_2) || defined(ZEND_ENGINE_3)
#include "php_streams.h"
#include "ext/standard/file.h"
@@ -356,5 +355,4 @@ php_stream_wrapper php_stream_zip_wrapper = {
NULL,
0 /* is_url */
};
-#endif /* defined(ZEND_ENGINE_2) || defined(ZEND_ENGINE_3) */
#endif /* HAVE_ZIP */
diff --git a/ext/zlib/tests/gzfilegzreadfile.phpt b/ext/zlib/tests/gzfilegzreadfile.phpt
index 2d6843ddd4..ef6378de25 100644
--- a/ext/zlib/tests/gzfilegzreadfile.phpt
+++ b/ext/zlib/tests/gzfilegzreadfile.phpt
@@ -25,7 +25,7 @@ blah blah blah blah blah blah blah
EOD;
-$filename = tempnam("/tmp", "phpt");
+$filename = tempnam(sys_get_temp_dir(), "phpt");
$fp = gzopen($filename, "wb");
gzwrite($fp, $original);
diff --git a/ext/zlib/tests/gzreadgzwrite.phpt b/ext/zlib/tests/gzreadgzwrite.phpt
index 71d728b6ed..d52f12a4ac 100644
--- a/ext/zlib/tests/gzreadgzwrite.phpt
+++ b/ext/zlib/tests/gzreadgzwrite.phpt
@@ -6,7 +6,7 @@ if (!extension_loaded("zlib")) print "skip"; ?>
--FILE--
<?php
$original = str_repeat(b"hallo php",4096);
-$filename = tempnam("/tmp", "phpt");
+$filename = tempnam(sys_get_temp_dir(), "phpt");
$fp = gzopen($filename, "wb");
gzwrite($fp, $original);
diff --git a/ext/zlib/tests/gzreadgzwriteplain.phpt b/ext/zlib/tests/gzreadgzwriteplain.phpt
index 6a752b6212..7a5a5025aa 100644
--- a/ext/zlib/tests/gzreadgzwriteplain.phpt
+++ b/ext/zlib/tests/gzreadgzwriteplain.phpt
@@ -6,7 +6,7 @@ if (!extension_loaded("zlib")) print "skip"; ?>
--FILE--
<?php
$original = str_repeat(b"hallo php",4096);
-$filename = tempnam("/tmp", "phpt");
+$filename = tempnam(sys_get_temp_dir(), "phpt");
$fp = fopen($filename, "wb");
fwrite($fp, $original);
diff --git a/main/fopen_wrappers.h b/main/fopen_wrappers.h
index ce13d4a9b2..e9f5159fcd 100644
--- a/main/fopen_wrappers.h
+++ b/main/fopen_wrappers.h
@@ -34,15 +34,8 @@ PHPAPI int php_check_open_basedir(const char *path);
PHPAPI int php_check_open_basedir_ex(const char *path, int warn);
PHPAPI int php_check_specific_open_basedir(const char *basedir, const char *path);
-/* {{{ OPENBASEDIR_CHECKPATH(filename) to ease merge between 6.x and 5.x */
-#if PHP_API_VERSION < 20100412
-# define OPENBASEDIR_CHECKPATH(filename) \
- (PG(safe_mode) && (!php_checkuid(filename, NULL, CHECKUID_CHECK_FILE_AND_DIR))) || php_check_open_basedir(filename)
-#else
-#define OPENBASEDIR_CHECKPATH(filename) \
- php_check_open_basedir(filename)
-#endif
-/* }}} */
+/* OPENBASEDIR_CHECKPATH(filename) to ease merge between 6.x and 5.x */
+#define OPENBASEDIR_CHECKPATH(filename) php_check_open_basedir(filename)
PHPAPI int php_check_safe_mode_include_dir(const char *path);
diff --git a/main/main.c b/main/main.c
index fcb42b43b4..cc4b30b462 100644
--- a/main/main.c
+++ b/main/main.c
@@ -572,7 +572,7 @@ PHP_INI_BEGIN()
STD_PHP_INI_ENTRY("user_ini.filename", ".user.ini", PHP_INI_SYSTEM, OnUpdateString, user_ini_filename, php_core_globals, core_globals)
STD_PHP_INI_ENTRY("user_ini.cache_ttl", "300", PHP_INI_SYSTEM, OnUpdateLong, user_ini_cache_ttl, php_core_globals, core_globals)
- STD_PHP_INI_BOOLEAN("exit_on_timeout", "0", PHP_INI_ALL, OnUpdateBool, exit_on_timeout, php_core_globals, core_globals)
+ STD_PHP_INI_ENTRY("hard_timeout", "2", PHP_INI_SYSTEM, OnUpdateLong, hard_timeout, zend_executor_globals, executor_globals)
#ifdef PHP_WIN32
STD_PHP_INI_BOOLEAN("windows.show_crt_warning", "0", PHP_INI_ALL, OnUpdateBool, windows_show_crt_warning, php_core_globals, core_globals)
#endif
@@ -607,9 +607,9 @@ PHPAPI int php_get_module_initialized(void)
}
/* }}} */
-/* {{{ php_log_err
+/* {{{ php_log_err_with_severity
*/
-PHPAPI ZEND_COLD void php_log_err(char *log_message)
+PHPAPI ZEND_COLD void php_log_err_with_severity(char *log_message, int syslog_type_int)
{
int fd = -1;
time_t error_time;
@@ -624,7 +624,7 @@ PHPAPI ZEND_COLD void php_log_err(char *log_message)
if (PG(error_log) != NULL) {
#ifdef HAVE_SYSLOG_H
if (!strcmp(PG(error_log), "syslog")) {
- php_syslog(LOG_NOTICE, "%s", log_message);
+ php_syslog(syslog_type_int, "%s", log_message);
PG(in_error_log) = 0;
return;
}
@@ -1049,6 +1049,7 @@ static ZEND_COLD void php_error_cb(int type, const char *error_filename, const u
if (display && (EG(error_reporting) & type || (type & E_CORE))
&& (PG(log_errors) || PG(display_errors) || (!module_initialized))) {
char *error_type_str;
+ int syslog_type_int = LOG_NOTICE;
switch (type) {
case E_ERROR:
@@ -1056,29 +1057,36 @@ static ZEND_COLD void php_error_cb(int type, const char *error_filename, const u
case E_COMPILE_ERROR:
case E_USER_ERROR:
error_type_str = "Fatal error";
+ syslog_type_int = LOG_ERR;
break;
case E_RECOVERABLE_ERROR:
error_type_str = "Catchable fatal error";
+ syslog_type_int = LOG_ERR;
break;
case E_WARNING:
case E_CORE_WARNING:
case E_COMPILE_WARNING:
case E_USER_WARNING:
error_type_str = "Warning";
+ syslog_type_int = LOG_WARNING;
break;
case E_PARSE:
error_type_str = "Parse error";
+ syslog_type_int = LOG_EMERG;
break;
case E_NOTICE:
case E_USER_NOTICE:
error_type_str = "Notice";
+ syslog_type_int = LOG_NOTICE;
break;
case E_STRICT:
error_type_str = "Strict Standards";
+ syslog_type_int = LOG_INFO;
break;
case E_DEPRECATED:
case E_USER_DEPRECATED:
error_type_str = "Deprecated";
+ syslog_type_int = LOG_INFO;
break;
default:
error_type_str = "Unknown error";
@@ -1093,7 +1101,7 @@ static ZEND_COLD void php_error_cb(int type, const char *error_filename, const u
}
#endif
spprintf(&log_buffer, 0, "PHP %s: %s in %s on line %d", error_type_str, buffer, error_filename, error_lineno);
- php_log_err(log_buffer);
+ php_log_err_with_severity(log_buffer, syslog_type_int);
efree(log_buffer);
}
@@ -1505,8 +1513,6 @@ static ZEND_COLD void php_message_handler_for_zend(zend_long message, const void
void php_on_timeout(int seconds)
{
PG(connection_status) |= PHP_CONNECTION_TIMEOUT;
- zend_set_timeout(EG(timeout_seconds), 1);
- if(PG(exit_on_timeout)) sapi_terminate_process();
}
#if PHP_SIGCHILD
diff --git a/main/network.c b/main/network.c
index ab6a8ca715..aedff7e207 100644
--- a/main/network.c
+++ b/main/network.c
@@ -487,6 +487,11 @@ php_socket_t php_network_bind_socket_to_local_addr(const char *host, unsigned po
setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (char*)&sockoptval, sizeof(sockoptval));
}
#endif
+#ifdef TCP_NODELAY
+ if (sockopts & STREAM_SOCKOP_TCP_NODELAY) {
+ setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (char*)&sockoptval, sizeof(sockoptval));
+ }
+#endif
n = bind(sock, sa, socklen);
@@ -726,7 +731,8 @@ PHPAPI php_socket_t php_network_accept_incoming(php_socket_t srvsock,
socklen_t *addrlen,
struct timeval *timeout,
zend_string **error_string,
- int *error_code
+ int *error_code,
+ int tcp_nodelay
)
{
php_socket_t clisock = -1;
@@ -750,6 +756,11 @@ PHPAPI php_socket_t php_network_accept_incoming(php_socket_t srvsock,
textaddr,
addr, addrlen
);
+ if (tcp_nodelay) {
+#ifdef TCP_NODELAY
+ setsockopt(clisock, IPPROTO_TCP, TCP_NODELAY, (char*)&tcp_nodelay, sizeof(tcp_nodelay));
+#endif
+ }
} else {
error = php_socket_errno();
}
@@ -767,7 +778,6 @@ PHPAPI php_socket_t php_network_accept_incoming(php_socket_t srvsock,
/* }}} */
-
/* Connect to a remote host using an interruptible connect with optional timeout.
* Optionally, the connect can be made asynchronously, which will implicitly
* enable non-blocking mode on the socket.
@@ -902,6 +912,15 @@ skip_bind:
}
}
#endif
+
+#ifdef TCP_NODELAY
+ {
+ int val = 1;
+ if (sockopts & STREAM_SOCKOP_TCP_NODELAY) {
+ setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (char*)&val, sizeof(val));
+ }
+ }
+#endif
n = php_network_connect_socket(sock, sa, socklen, asynchronous,
timeout ? &working_timeout : NULL,
error_string, error_code);
diff --git a/main/php.h b/main/php.h
index bb06a3edac..ed6a4d7189 100644
--- a/main/php.h
+++ b/main/php.h
@@ -280,7 +280,13 @@ PHPAPI size_t php_write(void *buf, size_t size);
PHPAPI size_t php_printf(const char *format, ...) PHP_ATTRIBUTE_FORMAT(printf, 1,
2);
PHPAPI int php_get_module_initialized(void);
-PHPAPI ZEND_COLD void php_log_err(char *log_message);
+#ifdef HAVE_SYSLOG_H
+#include "php_syslog.h"
+#define php_log_err(msg) php_log_err_with_severity(msg, LOG_NOTICE)
+#else
+#define php_log_err(msg) php_log_err_with_severity(msg, 5)
+#endif
+PHPAPI ZEND_COLD void php_log_err_with_severity(char *log_message, int syslog_type_int);
int Debug(char *format, ...) PHP_ATTRIBUTE_FORMAT(printf, 1, 2);
int cfgparse(void);
END_EXTERN_C()
@@ -324,7 +330,7 @@ END_EXTERN_C()
BEGIN_EXTERN_C()
PHPAPI extern int (*php_register_internal_extensions_func)(void);
PHPAPI int php_register_internal_extensions(void);
-PHPAPI int php_mergesort(void *base, size_t nmemb, register size_t size, int (*cmp)(const void *, const void *));
+PHPAPI int php_mergesort(void *base, size_t nmemb, size_t size, int (*cmp)(const void *, const void *));
PHPAPI void php_register_pre_request_shutdown(void (*func)(void *), void *userdata);
PHPAPI void php_com_initialize(void);
PHPAPI char *php_get_current_user(void);
diff --git a/main/php_globals.h b/main/php_globals.h
index 057ec6679f..e50ea2ebfb 100644
--- a/main/php_globals.h
+++ b/main/php_globals.h
@@ -147,7 +147,6 @@ struct _php_core_globals {
char *disable_functions;
char *disable_classes;
zend_bool allow_url_include;
- zend_bool exit_on_timeout;
#ifdef PHP_WIN32
zend_bool com_initialized;
#endif
diff --git a/main/php_ini.c b/main/php_ini.c
index 5f9eb61907..d056f51edd 100644
--- a/main/php_ini.c
+++ b/main/php_ini.c
@@ -638,7 +638,7 @@ int php_init_config(void)
}
if (!debpath[0]) {
/* empty string means default builtin value
- to allow "/foo/phd.d:" or ":/foo/php.d" */
+ to allow "/foo/php.d:" or ":/foo/php.d" */
debpath = PHP_CONFIG_FILE_SCAN_DIR;
}
lenpath = (int)strlen(debpath);
diff --git a/main/php_network.h b/main/php_network.h
index df56685dc4..16e0e60255 100644
--- a/main/php_network.h
+++ b/main/php_network.h
@@ -28,6 +28,7 @@
#else
# undef closesocket
# define closesocket close
+# include <netinet/tcp.h>
#endif
#ifndef HAVE_SHUTDOWN
@@ -115,6 +116,7 @@ typedef int php_socket_t;
#define STREAM_SOCKOP_SO_BROADCAST (1 << 2)
#define STREAM_SOCKOP_IPV6_V6ONLY (1 << 3)
#define STREAM_SOCKOP_IPV6_V6ONLY_ENABLED (1 << 4)
+#define STREAM_SOCKOP_TCP_NODELAY (1 << 5)
/* uncomment this to debug poll(2) emulation on systems that have poll(2) */
@@ -265,7 +267,8 @@ PHPAPI php_socket_t php_network_accept_incoming(php_socket_t srvsock,
socklen_t *addrlen,
struct timeval *timeout,
zend_string **error_string,
- int *error_code
+ int *error_code,
+ int tcp_nodelay
);
PHPAPI int php_network_get_sock_name(php_socket_t sock,
diff --git a/main/php_open_temporary_file.c b/main/php_open_temporary_file.c
index b21f3ab10d..caddf1b390 100644
--- a/main/php_open_temporary_file.c
+++ b/main/php_open_temporary_file.c
@@ -19,6 +19,7 @@
/* $Id$ */
#include "php.h"
+#include "php_open_temporary_file.h"
#include <errno.h>
#include <sys/types.h>
@@ -249,7 +250,7 @@ PHPAPI const char* php_get_temporary_directory(void)
* This function should do its best to return a file pointer to a newly created
* unique file, on every platform.
*/
-PHPAPI int php_open_temporary_fd_ex(const char *dir, const char *pfx, zend_string **opened_path_p, zend_bool open_basedir_check)
+PHPAPI int php_open_temporary_fd_ex(const char *dir, const char *pfx, zend_string **opened_path_p, uint32_t flags)
{
int fd;
const char *temp_dir;
@@ -265,7 +266,7 @@ PHPAPI int php_open_temporary_fd_ex(const char *dir, const char *pfx, zend_strin
def_tmp:
temp_dir = php_get_temporary_directory();
- if (temp_dir && *temp_dir != '\0' && (!open_basedir_check || !php_check_open_basedir(temp_dir))) {
+ if (temp_dir && *temp_dir != '\0' && (!(flags & PHP_TMP_FILE_OPEN_BASEDIR_CHECK) || !php_check_open_basedir(temp_dir))) {
return php_do_open_temporary_file(temp_dir, pfx, opened_path_p);
} else {
return -1;
@@ -276,6 +277,9 @@ def_tmp:
fd = php_do_open_temporary_file(dir, pfx, opened_path_p);
if (fd == -1) {
/* Use default temporary directory. */
+ if (!(flags & PHP_TMP_FILE_SILENT)) {
+ php_error_docref(NULL, E_NOTICE, "file created in the system's temporary directory");
+ }
goto def_tmp;
}
return fd;
diff --git a/main/php_open_temporary_file.h b/main/php_open_temporary_file.h
index fa8649aa70..f82f577c75 100644
--- a/main/php_open_temporary_file.h
+++ b/main/php_open_temporary_file.h
@@ -21,9 +21,12 @@
#ifndef PHP_OPEN_TEMPORARY_FILE_H
#define PHP_OPEN_TEMPORARY_FILE_H
+#define PHP_TMP_FILE_OPEN_BASEDIR_CHECK (1<<0)
+#define PHP_TMP_FILE_SILENT (1<<1)
+
BEGIN_EXTERN_C()
PHPAPI FILE *php_open_temporary_file(const char *dir, const char *pfx, zend_string **opened_path_p);
-PHPAPI int php_open_temporary_fd_ex(const char *dir, const char *pfx, zend_string **opened_path_p, zend_bool open_basedir_check);
+PHPAPI int php_open_temporary_fd_ex(const char *dir, const char *pfx, zend_string **opened_path_p, uint32_t flags);
PHPAPI int php_open_temporary_fd(const char *dir, const char *pfx, zend_string **opened_path_p);
PHPAPI const char *php_get_temporary_directory(void);
END_EXTERN_C()
diff --git a/main/php_variables.c b/main/php_variables.c
index 73274d7695..f55f7c8195 100644
--- a/main/php_variables.c
+++ b/main/php_variables.c
@@ -109,6 +109,25 @@ PHPAPI void php_register_variable_ex(char *var_name, zval *val, zval *track_vars
return;
}
+ if (var_len == sizeof("this")-1 && EG(current_execute_data)) {
+ zend_execute_data *ex = EG(current_execute_data);
+
+ while (ex) {
+ if (ex->func && ZEND_USER_CODE(ex->func->common.type)) {
+ if (ex->symbol_table == symtable1) {
+ if (memcmp(var, "this", sizeof("this")-1) == 0) {
+ zend_throw_error(NULL, "Cannot re-assign $this");
+ zval_dtor(val);
+ free_alloca(var_orig, use_heap);
+ return;
+ }
+ }
+ break;
+ }
+ ex = ex->prev_execute_data;
+ }
+ }
+
/* GLOBALS hijack attempt, reject parameter */
if (symtable1 == &EG(symbol_table) &&
var_len == sizeof("GLOBALS")-1 &&
diff --git a/main/php_version.h b/main/php_version.h
index f88ce5e69e..4714e5dc4b 100644
--- a/main/php_version.h
+++ b/main/php_version.h
@@ -1,8 +1,8 @@
/* automatically generated by configure */
/* edit configure.in to change version number */
#define PHP_MAJOR_VERSION 7
-#define PHP_MINOR_VERSION 0
-#define PHP_RELEASE_VERSION 9
+#define PHP_MINOR_VERSION 1
+#define PHP_RELEASE_VERSION 0
#define PHP_EXTRA_VERSION "-dev"
-#define PHP_VERSION "7.0.9-dev"
-#define PHP_VERSION_ID 70009
+#define PHP_VERSION "7.1.0-dev"
+#define PHP_VERSION_ID 70100
diff --git a/main/snprintf.h b/main/snprintf.h
index 1fcffb016a..918cb60152 100644
--- a/main/snprintf.h
+++ b/main/snprintf.h
@@ -86,7 +86,7 @@ PHPAPI int ap_php_vasprintf(char **buf, const char *format, va_list ap);
PHPAPI int ap_php_asprintf(char **buf, const char *format, ...);
PHPAPI int php_sprintf (char* s, const char* format, ...) PHP_ATTRIBUTE_FORMAT(printf, 2, 3);
PHPAPI char * php_gcvt(double value, int ndigit, char dec_point, char exponent, char *buf);
-PHPAPI char * php_conv_fp(register char format, register double num,
+PHPAPI char * php_conv_fp(char format, double num,
boolean_e add_dp, int precision, char dec_point, bool_int * is_negative, char *buf, size_t *len);
END_EXTERN_C()
@@ -153,11 +153,11 @@ typedef enum {
typedef WIDE_INT wide_int;
typedef unsigned WIDE_INT u_wide_int;
-PHPAPI char * ap_php_conv_10(register wide_int num, register bool_int is_unsigned,
- register bool_int * is_negative, char *buf_end, register size_t *len);
+PHPAPI char * ap_php_conv_10(wide_int num, bool_int is_unsigned,
+ bool_int * is_negative, char *buf_end, size_t *len);
-PHPAPI char * ap_php_conv_p2(register u_wide_int num, register int nbits,
- char format, char *buf_end, register size_t *len);
+PHPAPI char * ap_php_conv_p2(u_wide_int num, int nbits,
+ char format, char *buf_end, size_t *len);
/* The maximum precision that's allowed for float conversion. Does not include
* decimal separator, exponent, sign, terminator. Currently does not affect
diff --git a/main/streams/userspace.c b/main/streams/userspace.c
index 63a351a59b..eb8b32eed9 100644
--- a/main/streams/userspace.c
+++ b/main/streams/userspace.c
@@ -299,9 +299,7 @@ static void user_stream_create_object(struct php_user_stream_wrapper *uwrap, php
zval retval;
fci.size = sizeof(fci);
- fci.function_table = &uwrap->ce->function_table;
ZVAL_UNDEF(&fci.function_name);
- fci.symbol_table = NULL;
fci.object = Z_OBJ_P(object);
fci.retval = &retval;
fci.param_count = 0;
@@ -310,7 +308,7 @@ static void user_stream_create_object(struct php_user_stream_wrapper *uwrap, php
fcc.initialized = 1;
fcc.function_handler = uwrap->ce->constructor;
- fcc.calling_scope = EG(scope);
+ fcc.calling_scope = zend_get_executed_scope();
fcc.called_scope = Z_OBJCE_P(object);
fcc.object = Z_OBJ_P(object);
diff --git a/main/streams/xp_socket.c b/main/streams/xp_socket.c
index 7a21fbef44..8dc8ef8980 100644
--- a/main/streams/xp_socket.c
+++ b/main/streams/xp_socket.c
@@ -266,16 +266,26 @@ static inline int sock_recvfrom(php_netstream_data_t *sock, char *buf, size_t bu
struct sockaddr **addr, socklen_t *addrlen
)
{
- php_sockaddr_storage sa;
- socklen_t sl = sizeof(sa);
int ret;
int want_addr = textaddr || addr;
if (want_addr) {
+ php_sockaddr_storage sa;
+ socklen_t sl = sizeof(sa);
ret = recvfrom(sock->socket, buf, XP_SOCK_BUF_SIZE(buflen), flags, (struct sockaddr*)&sa, &sl);
ret = (ret == SOCK_CONN_ERR) ? -1 : ret;
- php_network_populate_name_from_sockaddr((struct sockaddr*)&sa, sl,
- textaddr, addr, addrlen);
+ if (sl) {
+ php_network_populate_name_from_sockaddr((struct sockaddr*)&sa, sl,
+ textaddr, addr, addrlen);
+ } else {
+ if (textaddr) {
+ *textaddr = ZSTR_EMPTY_ALLOC();
+ }
+ if (addr) {
+ *addr = NULL;
+ *addrlen = 0;
+ }
+ }
} else {
ret = recv(sock->socket, buf, XP_SOCK_BUF_SIZE(buflen), flags);
ret = (ret == SOCK_CONN_ERR) ? -1 : ret;
@@ -742,6 +752,18 @@ static inline int php_tcp_sockop_connect(php_stream *stream, php_netstream_data_
}
#endif
+ if (stream->ops != &php_stream_udp_socket_ops /* TCP_NODELAY is only applicable for TCP */
+#ifdef AF_UNIX
+ && stream->ops != &php_stream_unix_socket_ops
+ && stream->ops != &php_stream_unixdg_socket_ops
+#endif
+ && PHP_STREAM_CONTEXT(stream)
+ && (tmpzval = php_stream_context_get_option(PHP_STREAM_CONTEXT(stream), "socket", "tcp_nodelay")) != NULL
+ && zend_is_true(tmpzval)
+ ) {
+ sockopts |= STREAM_SOCKOP_TCP_NODELAY;
+ }
+
/* Note: the test here for php_stream_udp_socket_ops is important, because we
* want the default to be TCP sockets so that the openssl extension can
* re-use this code. */
@@ -783,36 +805,37 @@ static inline int php_tcp_sockop_accept(php_stream *stream, php_netstream_data_t
php_stream_xport_param *xparam STREAMS_DC)
{
int clisock;
+ zend_bool nodelay = 0;
+ zval *tmpzval = NULL;
xparam->outputs.client = NULL;
+ if ((NULL != PHP_STREAM_CONTEXT(stream)) &&
+ (tmpzval = php_stream_context_get_option(PHP_STREAM_CONTEXT(stream), "socket", "tcp_nodelay")) != NULL &&
+ zend_is_true(tmpzval)) {
+ nodelay = 1;
+ }
+
clisock = php_network_accept_incoming(sock->socket,
- xparam->want_textaddr ? &xparam->outputs.textaddr : NULL,
- xparam->want_addr ? &xparam->outputs.addr : NULL,
- xparam->want_addr ? &xparam->outputs.addrlen : NULL,
- xparam->inputs.timeout,
- xparam->want_errortext ? &xparam->outputs.error_text : NULL,
- &xparam->outputs.error_code
- );
+ xparam->want_textaddr ? &xparam->outputs.textaddr : NULL,
+ xparam->want_addr ? &xparam->outputs.addr : NULL,
+ xparam->want_addr ? &xparam->outputs.addrlen : NULL,
+ xparam->inputs.timeout,
+ xparam->want_errortext ? &xparam->outputs.error_text : NULL,
+ &xparam->outputs.error_code,
+ nodelay);
if (clisock >= 0) {
- php_netstream_data_t *clisockdata;
+ php_netstream_data_t *clisockdata = (php_netstream_data_t*) emalloc(sizeof(*clisockdata));
- clisockdata = emalloc(sizeof(*clisockdata));
+ memcpy(clisockdata, sock, sizeof(*clisockdata));
+ clisockdata->socket = clisock;
- if (clisockdata == NULL) {
- close(clisock);
- /* technically a fatal error */
- } else {
- memcpy(clisockdata, sock, sizeof(*clisockdata));
- clisockdata->socket = clisock;
-
- xparam->outputs.client = php_stream_alloc_rel(stream->ops, clisockdata, NULL, "r+");
- if (xparam->outputs.client) {
- xparam->outputs.client->ctx = stream->ctx;
- if (stream->ctx) {
- GC_REFCOUNT(stream->ctx)++;
- }
+ xparam->outputs.client = php_stream_alloc_rel(stream->ops, clisockdata, NULL, "r+");
+ if (xparam->outputs.client) {
+ xparam->outputs.client->ctx = stream->ctx;
+ if (stream->ctx) {
+ GC_REFCOUNT(stream->ctx)++;
}
}
}
diff --git a/run-tests.php b/run-tests.php
index 66fcdd7fb3..fd4e12081f 100755
--- a/run-tests.php
+++ b/run-tests.php
@@ -59,27 +59,6 @@ NO_PROC_OPEN_ERROR;
exit;
}
-// Version constants only available as of 5.2.8
-if (!defined("PHP_VERSION_ID")) {
- list($major, $minor, $bug) = explode(".", phpversion(), 3);
- $bug = (int)$bug; // Many distros make up their own versions
- if ($bug < 10) {
- $bug = "0$bug";
- }
-
- define("PHP_VERSION_ID", "{$major}0{$minor}$bug");
- define("PHP_MAJOR_VERSION", $major);
-}
-
-// __DIR__ is available from 5.3.0
-if (PHP_VERSION_ID < 50300) {
- define('__DIR__', realpath(dirname(__FILE__)));
- // FILE_BINARY is available from 5.2.7
- if (PHP_VERSION_ID < 50207) {
- define('FILE_BINARY', 0);
- }
-}
-
// If timezone is not set, use UTC.
if (ini_get('date.timezone') == '') {
date_default_timezone_set('UTC');
@@ -113,22 +92,6 @@ while(@ob_end_clean());
if (ob_get_level()) echo "Not all buffers were deleted.\n";
error_reporting(E_ALL);
-if (PHP_MAJOR_VERSION < 6) {
- if (ini_get('safe_mode')) {
- echo <<< SAFE_MODE_WARNING
-
-+-----------------------------------------------------------+
-| ! WARNING ! |
-| You are running the test-suite with "safe_mode" ENABLED ! |
-| |
-| Chances are high that no test will work at all, |
-| depending on how you configured "safe_mode" ! |
-+-----------------------------------------------------------+
-
-
-SAFE_MODE_WARNING;
- }
-}
$environment = isset($_ENV) ? $_ENV : array();
if ((substr(PHP_OS, 0, 3) == "WIN") && empty($environment["SystemRoot"])) {
@@ -240,7 +203,6 @@ $exts_to_test = array();
$ini_overwrites = array(
'output_handler=',
'open_basedir=',
- 'safe_mode=0',
'disable_functions=',
'output_buffering=Off',
'error_reporting=' . (E_ALL | E_STRICT),
@@ -2834,7 +2796,7 @@ function junit_mark_test_as($type, $file_name, $test_name, $time = null, $messag
} elseif ('WARN' == $type) {
junit_suite_record($suite, 'test_warn');
$JUNIT['files'][$file_name]['xml'] .= "<warning>$escaped_message</warning>\n";
- } elseif('FAIL' == $type) {
+ } elseif ('FAIL' == $type) {
junit_suite_record($suite, 'test_fail');
$JUNIT['files'][$file_name]['xml'] .= "<failure type='$output_type' message='$escaped_message'>$escaped_details</failure>\n";
} else {
diff --git a/sapi/apache2handler/php_apache.h b/sapi/apache2handler/php_apache.h
index c1c52f5697..69c01bbe02 100644
--- a/sapi/apache2handler/php_apache.h
+++ b/sapi/apache2handler/php_apache.h
@@ -24,10 +24,16 @@
#include "httpd.h"
#include "http_config.h"
#include "http_core.h"
+#include "http_log.h"
#include "php.h"
#include "main/php_streams.h"
+/* Enable per-module logging in Apache 2.4+ */
+#ifdef APLOG_USE_MODULE
+APLOG_USE_MODULE(php7);
+#endif
+
/* Declare this so we can get to it from outside the sapi_apache2.c file */
extern module AP_MODULE_DECLARE_DATA php7_module;
diff --git a/sapi/cgi/cgi_main.c b/sapi/cgi/cgi_main.c
index 8983b53dc2..e758b33eeb 100644
--- a/sapi/cgi/cgi_main.c
+++ b/sapi/cgi/cgi_main.c
@@ -35,6 +35,7 @@
#ifdef PHP_WIN32
# include "win32/time.h"
# include "win32/signal.h"
+# include "win32/winutil.h"
# include <process.h>
#endif
@@ -102,7 +103,6 @@ struct sigaction act, old_term, old_quit, old_int;
static void (*php_php_import_environment_variables)(zval *array_ptr);
-#ifndef PHP_WIN32
/* these globals used for forking children on unix systems */
/**
* Number of child processes that will get created to service requests
@@ -115,6 +115,7 @@ static int children = 0;
*/
static int parent = 1;
+#ifndef PHP_WIN32
/* Did parent received exit signals SIG_TERM/SIG_INT/SIG_QUIT */
static int exit_signal = 0;
@@ -222,6 +223,14 @@ static php_cgi_globals_struct php_cgi_globals;
#define TRANSLATE_SLASHES(path)
#endif
+#ifdef PHP_WIN32
+#define WIN32_MAX_SPAWN_CHILDREN 64
+HANDLE kid_cgi_ps[WIN32_MAX_SPAWN_CHILDREN];
+int kids;
+HANDLE job = NULL;
+JOBOBJECT_EXTENDED_LIMIT_INFORMATION job_info = { 0 };
+#endif
+
#ifndef HAVE_ATTRIBUTE_WEAK
static void fcgi_log(int type, const char *format, ...) {
va_list ap;
@@ -354,9 +363,7 @@ static void sapi_fcgi_flush(void *server_context)
fcgi_request *request = (fcgi_request*) server_context;
if (
-#ifndef PHP_WIN32
!parent &&
-#endif
request && !fcgi_flush(request, 0)) {
php_handle_aborted_connection();
@@ -900,9 +907,7 @@ static int sapi_cgi_deactivate(void)
if (SG(sapi_started)) {
if (fcgi_is_fastcgi()) {
if (
-#ifndef PHP_WIN32
!parent &&
-#endif
!fcgi_finish_request((fcgi_request*)SG(server_context), 0)) {
php_handle_aborted_connection();
}
@@ -1431,6 +1436,29 @@ void fastcgi_cleanup(int signal)
exit(0);
}
}
+#else
+BOOL WINAPI fastcgi_cleanup(DWORD sig)
+{
+ int i = kids;
+
+ while (0 < i--) {
+ if (NULL == kid_cgi_ps[i]) {
+ continue;
+ }
+
+ TerminateProcess(kid_cgi_ps[i], 0);
+ CloseHandle(kid_cgi_ps[i]);
+ kid_cgi_ps[i] = NULL;
+ }
+
+ if (job) {
+ CloseHandle(job);
+ }
+
+ parent = 0;
+
+ return TRUE;
+}
#endif
PHP_INI_BEGIN()
@@ -1979,8 +2007,7 @@ consult the installation file that came with this distribution, or visit \n\
/* library is already initialized, now init our request */
request = fcgi_init_request(fcgi_fd, NULL, NULL, NULL);
-#ifndef PHP_WIN32
- /* Pre-fork, if required */
+ /* Pre-fork or spawn, if required */
if (getenv("PHP_FCGI_CHILDREN")) {
char * children_str = getenv("PHP_FCGI_CHILDREN");
children = atoi(children_str);
@@ -1996,6 +2023,7 @@ consult the installation file that came with this distribution, or visit \n\
fcgi_set_mgmt_var("FCGI_MAX_REQS", sizeof("FCGI_MAX_REQS")-1, "1", sizeof("1")-1);
}
+#ifndef PHP_WIN32
if (children) {
int running = 0;
pid_t pid;
@@ -2081,6 +2109,103 @@ consult the installation file that came with this distribution, or visit \n\
parent = 0;
}
+#else
+ if (children) {
+ char *cmd_line;
+ char kid_buf[16];
+ char my_name[MAX_PATH] = {0};
+ int i;
+
+ ZeroMemory(&kid_cgi_ps, sizeof(kid_cgi_ps));
+ kids = children < WIN32_MAX_SPAWN_CHILDREN ? children : WIN32_MAX_SPAWN_CHILDREN;
+
+ SetConsoleCtrlHandler(fastcgi_cleanup, TRUE);
+
+ /* kids will inherit the env, don't let them spawn */
+ SetEnvironmentVariable("PHP_FCGI_CHILDREN", NULL);
+
+ GetModuleFileName(NULL, my_name, MAX_PATH);
+ cmd_line = my_name;
+
+ job = CreateJobObject(NULL, NULL);
+ if (!job) {
+ DWORD err = GetLastError();
+ char *err_text = php_win32_error_to_msg(err);
+
+ fprintf(stderr, "unable to create job object: [0x%08lx]: %s\n", err, err_text);
+
+ goto parent_out;
+ }
+
+ job_info.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
+ if (!SetInformationJobObject(job, JobObjectExtendedLimitInformation, &job_info, sizeof(job_info))) {
+ DWORD err = GetLastError();
+ char *err_text = php_win32_error_to_msg(err);
+
+ fprintf(stderr, "unable to configure job object: [0x%08lx]: %s\n", err, err_text);
+ }
+
+ while (parent) {
+ i = kids;
+ while (0 < i--) {
+ DWORD status;
+
+ if (NULL != kid_cgi_ps[i]) {
+ if(!GetExitCodeProcess(kid_cgi_ps[i], &status) || status != STILL_ACTIVE) {
+ CloseHandle(kid_cgi_ps[i]);
+ kid_cgi_ps[i] = NULL;
+ }
+ }
+ }
+
+ i = kids;
+ while (0 < i--) {
+ PROCESS_INFORMATION pi;
+ STARTUPINFO si;
+
+ if (NULL != kid_cgi_ps[i]) {
+ continue;
+ }
+
+ ZeroMemory(&si, sizeof(si));
+ si.cb = sizeof(si);
+ ZeroMemory(&pi, sizeof(pi));
+
+ si.dwFlags = STARTF_USESTDHANDLES;
+ si.hStdOutput = INVALID_HANDLE_VALUE;
+ si.hStdInput = (HANDLE)_get_osfhandle(fcgi_fd);
+ si.hStdError = INVALID_HANDLE_VALUE;
+
+ if (CreateProcess(NULL, cmd_line, NULL, NULL, TRUE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi)) {
+ kid_cgi_ps[i] = pi.hProcess;
+ if (!AssignProcessToJobObject(job, pi.hProcess)) {
+ DWORD err = GetLastError();
+ char *err_text = php_win32_error_to_msg(err);
+
+ fprintf(stderr, "unable to assign child process to job object: [0x%08lx]: %s\n", err, err_text);
+ }
+ CloseHandle(pi.hThread);
+ } else {
+ DWORD err = GetLastError();
+ char *err_text = php_win32_error_to_msg(err);
+
+ kid_cgi_ps[i] = NULL;
+
+ fprintf(stderr, "unable to spawn: [0x%08lx]: %s\n", err, err_text);
+ }
+ }
+
+ WaitForMultipleObjects(kids, kid_cgi_ps, FALSE, INFINITE);
+ }
+
+ snprintf(kid_buf, 16, "%d", children);
+ /* restore my env */
+ SetEnvironmentVariable("PHP_FCGI_CHILDREN", kid_buf);
+
+ goto parent_out;
+ } else {
+ parent = 0;
+ }
#endif /* WIN32 */
}
@@ -2584,9 +2709,7 @@ out:
#endif
}
-#ifndef PHP_WIN32
parent_out:
-#endif
SG(server_context) = NULL;
php_module_shutdown();
diff --git a/sapi/cli/config.w32 b/sapi/cli/config.w32
index 664394c8a6..c6409b59df 100644
--- a/sapi/cli/config.w32
+++ b/sapi/cli/config.w32
@@ -12,6 +12,11 @@ if (PHP_CLI == "yes") {
ADD_FLAG("CFLAGS_CLI", "/D PHP_WIN32_DEBUG_HEAP");
}
ADD_FLAG("LDFLAGS_CLI", "/stack:67108864");
+
+ if (CHECK_LIB("edit_a.lib;edit.lib", "cli", PHP_CLI) &&
+ CHECK_HEADER_ADD_INCLUDE("editline/readline.h", "CFLAGS_CLI")) {
+ ADD_FLAG("CFLAGS_CLI", "/D HAVE_LIBEDIT");
+ }
}
if (PHP_CLI_WIN32 == "yes") {
diff --git a/sapi/cli/php_cli.c b/sapi/cli/php_cli.c
index 978c8b332e..09ce00f9e7 100644
--- a/sapi/cli/php_cli.c
+++ b/sapi/cli/php_cli.c
@@ -689,6 +689,14 @@ static int do_cli(int argc, char **argv) /* {{{ */
#else
"NTS "
#endif
+#ifdef COMPILER
+ COMPILER
+ " "
+#endif
+#ifdef ARCHITECTURE
+ ARCHITECTURE
+ " "
+#endif
#if ZEND_DEBUG
"DEBUG "
#endif
diff --git a/sapi/cli/php_cli_server.c b/sapi/cli/php_cli_server.c
index f94ac74741..a9d4a533b3 100644
--- a/sapi/cli/php_cli_server.c
+++ b/sapi/cli/php_cli_server.c
@@ -96,6 +96,7 @@
#include "ext/standard/html.h"
#include "ext/standard/url.h" /* for php_raw_url_decode() */
#include "ext/standard/php_string.h" /* for php_dirname() */
+#include "ext/date/php_date.h" /* for php_format_date() */
#include "php_network.h"
#include "php_http_parser.h"
@@ -339,15 +340,24 @@ static void append_http_status_line(smart_str *buffer, int protocol_version, int
static void append_essential_headers(smart_str* buffer, php_cli_server_client *client, int persistent) /* {{{ */
{
- {
- char *val;
- if (NULL != (val = zend_hash_str_find_ptr(&client->request.headers, "host", sizeof("host")-1))) {
- smart_str_appendl_ex(buffer, "Host", sizeof("Host") - 1, persistent);
- smart_str_appendl_ex(buffer, ": ", sizeof(": ") - 1, persistent);
- smart_str_appends_ex(buffer, val, persistent);
- smart_str_appendl_ex(buffer, "\r\n", 2, persistent);
- }
+ char *val;
+ struct timeval tv = {0};
+
+ if (NULL != (val = zend_hash_str_find_ptr(&client->request.headers, "host", sizeof("host")-1))) {
+ smart_str_appendl_ex(buffer, "Host", sizeof("Host") - 1, persistent);
+ smart_str_appendl_ex(buffer, ": ", sizeof(": ") - 1, persistent);
+ smart_str_appends_ex(buffer, val, persistent);
+ smart_str_appendl_ex(buffer, "\r\n", 2, persistent);
}
+
+ if (!gettimeofday(&tv, NULL)) {
+ zend_string *dt = php_format_date("r", 1, tv.tv_sec, 1);
+ smart_str_appendl_ex(buffer, "Date: ", 6, persistent);
+ smart_str_appends_ex(buffer, dt->val, persistent);
+ smart_str_appendl_ex(buffer, "\r\n", 2, persistent);
+ zend_string_release(dt);
+ }
+
smart_str_appendl_ex(buffer, "Connection: close\r\n", sizeof("Connection: close\r\n") - 1, persistent);
} /* }}} */
diff --git a/sapi/cli/tests/bug43177.phpt b/sapi/cli/tests/bug43177.phpt
index 23af545908..e475fb09b4 100644
--- a/sapi/cli/tests/bug43177.phpt
+++ b/sapi/cli/tests/bug43177.phpt
@@ -61,6 +61,7 @@ HEADER
--EXPECTF--
HTTP/1.1 200 OK
Host: localhost
+Date: %s
Connection: close
X-Powered-By: %s
Content-type: text/html; charset=UTF-8
@@ -68,18 +69,21 @@ Content-type: text/html; charset=UTF-8
OK
HTTP/1.0 500 Internal Server Error
Host: localhost
+Date: %s
Connection: close
X-Powered-By: %s
Content-type: text/html; charset=UTF-8
HTTP/1.0 500 Internal Server Error
Host: localhost
+Date: %s
Connection: close
X-Powered-By: %s
Content-type: text/html; charset=UTF-8
HTTP/1.0 500 Internal Server Error
Host: localhost
+Date: %s
Connection: close
X-Powered-By: %s
Content-type: text/html; charset=UTF-8
diff --git a/sapi/cli/tests/bug65066_100.phpt b/sapi/cli/tests/bug65066_100.phpt
index 901ba188fd..34381d8e96 100644
--- a/sapi/cli/tests/bug65066_100.phpt
+++ b/sapi/cli/tests/bug65066_100.phpt
@@ -34,6 +34,7 @@ HEADER
--EXPECTF--
HTTP/1.1 100 Continue
Host: %s
+Date: %s
Connection: close
X-Powered-By: PHP/%s
Content-type: text/html; charset=UTF-8
diff --git a/sapi/cli/tests/bug65066_422.phpt b/sapi/cli/tests/bug65066_422.phpt
index 4e5d31c7a7..f25ddfbca6 100644
--- a/sapi/cli/tests/bug65066_422.phpt
+++ b/sapi/cli/tests/bug65066_422.phpt
@@ -34,6 +34,7 @@ HEADER
--EXPECTF--
HTTP/1.1 422 Unknown Status Code
Host: %s
+Date: %s
Connection: close
X-Powered-By: PHP/%s
Content-type: text/html; charset=UTF-8
diff --git a/sapi/cli/tests/bug65066_511.phpt b/sapi/cli/tests/bug65066_511.phpt
index a0b4eae393..27c9f9755a 100644
--- a/sapi/cli/tests/bug65066_511.phpt
+++ b/sapi/cli/tests/bug65066_511.phpt
@@ -34,6 +34,7 @@ HEADER
--EXPECTF--
HTTP/1.1 511 Network Authentication Required
Host: %s
+Date: %s
Connection: close
X-Powered-By: PHP/%s
Content-type: text/html; charset=UTF-8
diff --git a/sapi/cli/tests/bug65633.phpt b/sapi/cli/tests/bug65633.phpt
index 456436b1f7..2e9f2796e8 100644
--- a/sapi/cli/tests/bug65633.phpt
+++ b/sapi/cli/tests/bug65633.phpt
@@ -37,6 +37,7 @@ fclose($fp);
?>
--EXPECTF--
HTTP/1.1 200 OK
+Date: %s
Connection: close
X-Powered-By: %s
Content-type: text/html; charset=UTF-8
diff --git a/sapi/cli/tests/bug66606_2.phpt b/sapi/cli/tests/bug66606_2.phpt
index 9f539873e1..c47b7736c0 100644
--- a/sapi/cli/tests/bug66606_2.phpt
+++ b/sapi/cli/tests/bug66606_2.phpt
@@ -38,6 +38,7 @@ fclose($fp);
--EXPECTF--
HTTP/1.1 200 OK
Host: %s
+Date: %s
Connection: close
X-Powered-By: PHP/%s
Content-type: text/html; charset=UTF-8
diff --git a/sapi/cli/tests/bug66830.phpt b/sapi/cli/tests/bug66830.phpt
index 58c07e031a..b21b33627e 100644
--- a/sapi/cli/tests/bug66830.phpt
+++ b/sapi/cli/tests/bug66830.phpt
@@ -37,6 +37,7 @@ fclose($fp);
--EXPECTF--
HTTP/1.1 200 OK
Host: %s
+Date: %s
Connection: close
X-Powered-By: %s
Content-type: text/html; charset=UTF-8
diff --git a/sapi/cli/tests/bug67429.phpt b/sapi/cli/tests/bug67429.phpt
index 856946b29d..2ad4a27f07 100644
--- a/sapi/cli/tests/bug67429.phpt
+++ b/sapi/cli/tests/bug67429.phpt
@@ -38,11 +38,13 @@ HEADER
?>
--EXPECTF--
HTTP/1.1 308 Permanent Redirect
+Date: %s
Connection: close
X-Powered-By: %s
Content-type: text/html; charset=UTF-8
HTTP/1.1 426 Upgrade Required
+Date: %s
Connection: close
X-Powered-By: %s
Content-type: text/html; charset=UTF-8
diff --git a/sapi/cli/tests/bug68745.phpt b/sapi/cli/tests/bug68745.phpt
index 733d7d0900..4cdfe0ae4e 100644
--- a/sapi/cli/tests/bug68745.phpt
+++ b/sapi/cli/tests/bug68745.phpt
@@ -27,6 +27,7 @@ fclose($fp);
?>
--EXPECTF--
HTTP/1.1 200 OK
+Date: %s
Connection: close
X-Powered-By: %s
Content-type: text/html; charset=UTF-8
diff --git a/sapi/cli/tests/bug71005.phpt b/sapi/cli/tests/bug71005.phpt
index 3a60dcf42f..f02f261fbf 100644
--- a/sapi/cli/tests/bug71005.phpt
+++ b/sapi/cli/tests/bug71005.phpt
@@ -39,6 +39,7 @@ HEADER
--EXPECTF--
HTTP/1.1 200 OK
Host: %s
+Date: %s
Connection: close
X-Powered-By: PHP/%s
Content-type: text/html; charset=UTF-8
diff --git a/sapi/cli/tests/php_cli_server_004.phpt b/sapi/cli/tests/php_cli_server_004.phpt
index 8b913f6596..5e3bbec18f 100644
--- a/sapi/cli/tests/php_cli_server_004.phpt
+++ b/sapi/cli/tests/php_cli_server_004.phpt
@@ -38,6 +38,7 @@ HEADER
--EXPECTF--
HTTP/1.1 200 OK
Host: %s
+Date: %s
Connection: close
X-Powered-By: PHP/%s
Content-type: text/html; charset=UTF-8
diff --git a/sapi/cli/tests/php_cli_server_005.phpt b/sapi/cli/tests/php_cli_server_005.phpt
index cdd0ae902f..3f9078c8ae 100644
--- a/sapi/cli/tests/php_cli_server_005.phpt
+++ b/sapi/cli/tests/php_cli_server_005.phpt
@@ -50,6 +50,7 @@ HEADER
--EXPECTF--
HTTP/1.1 200 OK
Host: %s
+Date: %s
Connection: close
X-Powered-By: PHP/%s
Content-type: text/html; charset=UTF-8
diff --git a/sapi/cli/tests/php_cli_server_006.phpt b/sapi/cli/tests/php_cli_server_006.phpt
index ad6d6c9598..c68f1e51b0 100644
--- a/sapi/cli/tests/php_cli_server_006.phpt
+++ b/sapi/cli/tests/php_cli_server_006.phpt
@@ -34,6 +34,7 @@ HEADER
--EXPECTF--
HTTP/1.1 200 OK
Host: %s
+Date: %s
Connection: close
X-Powered-By: PHP/%s
Content-type: text/html; charset=UTF-8
diff --git a/sapi/cli/tests/php_cli_server_007.phpt b/sapi/cli/tests/php_cli_server_007.phpt
index 6420ff5a41..b588b9ee2e 100644
--- a/sapi/cli/tests/php_cli_server_007.phpt
+++ b/sapi/cli/tests/php_cli_server_007.phpt
@@ -34,6 +34,7 @@ HEADER
--EXPECTF--
HTTP/1.1 401 Unauthorized
Host: %s
+Date: %s
Connection: close
X-Powered-By: PHP/%s
WWW-Authenticate: Digest realm="foo",qop="auth",nonce="XXXXX",opaque="acbd18db4cc2f85cedef654fccc4a4d8"
diff --git a/sapi/cli/tests/php_cli_server_008.phpt b/sapi/cli/tests/php_cli_server_008.phpt
index 01f825a746..58b2485db5 100644
--- a/sapi/cli/tests/php_cli_server_008.phpt
+++ b/sapi/cli/tests/php_cli_server_008.phpt
@@ -54,6 +54,7 @@ fclose($fp);
--EXPECTF--
HTTP/1.1 200 OK
Host: %s
+Date: %s
Connection: close
X-Powered-By: PHP/%s
Content-type: text/html; charset=UTF-8
@@ -61,6 +62,7 @@ Content-type: text/html; charset=UTF-8
string(8) "HTTP/1.1"
HTTP/1.0 200 OK
Host: %s
+Date: %s
Connection: close
X-Powered-By: PHP/%s
Content-type: text/html; charset=UTF-8
diff --git a/sapi/cli/tests/php_cli_server_009.phpt b/sapi/cli/tests/php_cli_server_009.phpt
index 7f3009b9bd..d63c6abf28 100644
--- a/sapi/cli/tests/php_cli_server_009.phpt
+++ b/sapi/cli/tests/php_cli_server_009.phpt
@@ -78,6 +78,7 @@ fclose($fp);
--EXPECTF--
HTTP/1.1 200 OK
Host: %s
+Date: %s
Connection: close
X-Powered-By: PHP/%s
Content-type: text/html; charset=UTF-8
@@ -85,6 +86,7 @@ Content-type: text/html; charset=UTF-8
string(8) "/foo/bar"
HTTP/1.0 200 OK
Host: %s
+Date: %s
Connection: close
X-Powered-By: PHP/%s
Content-type: text/html; charset=UTF-8
diff --git a/sapi/cli/tests/php_cli_server_010.phpt b/sapi/cli/tests/php_cli_server_010.phpt
index ce3abeb501..fe8f1a98ca 100644
--- a/sapi/cli/tests/php_cli_server_010.phpt
+++ b/sapi/cli/tests/php_cli_server_010.phpt
@@ -55,6 +55,7 @@ fclose($fp);
--EXPECTF--
HTTP/1.1 200 OK
Host: %s
+Date: %s
Connection: close
X-Powered-By: PHP/%s
Content-type: text/html; charset=UTF-8
@@ -65,6 +66,7 @@ string(8) "/foo/bar"
string(7) "foo=bar"
HTTP/1.0 200 OK
Host: %s
+Date: %s
Connection: close
X-Powered-By: PHP/%s
Content-type: text/html; charset=UTF-8
diff --git a/sapi/cli/tests/php_cli_server_012.phpt b/sapi/cli/tests/php_cli_server_012.phpt
index 302540f7e6..10b9cdbfe9 100644
--- a/sapi/cli/tests/php_cli_server_012.phpt
+++ b/sapi/cli/tests/php_cli_server_012.phpt
@@ -40,6 +40,7 @@ fclose($fp);
--EXPECTF--
HTTP/1.1 200 OK
Host: %s
+Date: %s
Connection: close
X-Powered-By: PHP/%s
Content-type: text/html; charset=UTF-8
diff --git a/sapi/cli/tests/php_cli_server_013.phpt b/sapi/cli/tests/php_cli_server_013.phpt
index 3ea3ea9cad..23269580fb 100644
--- a/sapi/cli/tests/php_cli_server_013.phpt
+++ b/sapi/cli/tests/php_cli_server_013.phpt
@@ -83,6 +83,7 @@ fclose($fp);
HTTP/1.1 404 Not Found
Host: %s
+Date: %s
Connection: close
Content-Type: text/html; charset=UTF-8
Content-Length: %d
@@ -91,6 +92,7 @@ Content-Length: %d
</head><body><h1>Not Found</h1><p>The requested resource <code class="url">/</code> was not found on this server.</p></body></html>
HTTP/1.1 404 Not Found
Host: %s
+Date: %s
Connection: close
Content-Type: text/html; charset=UTF-8
Content-Length: %d
@@ -99,6 +101,7 @@ Content-Length: %d
</head><body><h1>Not Found</h1><p>The requested resource <code class="url">/main/style.css</code> was not found on this server.</p></body></html>
HTTP/1.1 404 Not Found
Host: %s
+Date: %s
Connection: close
Content-Type: text/html; charset=UTF-8
Content-Length: %d
diff --git a/sapi/cli/tests/php_cli_server_014.phpt b/sapi/cli/tests/php_cli_server_014.phpt
index 4f812e2f63..7c50a5e30a 100644
--- a/sapi/cli/tests/php_cli_server_014.phpt
+++ b/sapi/cli/tests/php_cli_server_014.phpt
@@ -65,6 +65,7 @@ fclose($fp);
HTTP/1.1 200 OK
Host: %s
+Date: %s
Connection: close
X-Powered-By: %s
Content-type: %s
@@ -72,6 +73,7 @@ Content-type: %s
done
HTTP/1.1 404 Not Found
Host: %s
+Date: %s
Connection: close
Content-Type: %s
Content-Length: %d
diff --git a/sapi/cli/tests/php_cli_server_015.phpt b/sapi/cli/tests/php_cli_server_015.phpt
index af0a3f65a8..663268aea6 100644
--- a/sapi/cli/tests/php_cli_server_015.phpt
+++ b/sapi/cli/tests/php_cli_server_015.phpt
@@ -41,6 +41,7 @@ fclose($fp);
--EXPECTF--
HTTP/1.1 200 OK
Host: %s
+Date: %s
Connection: close
X-Powered-By: PHP/%s
Content-type: text/html; charset=UTF-8
diff --git a/sapi/cli/tests/php_cli_server_017.phpt b/sapi/cli/tests/php_cli_server_017.phpt
index 34e7d5e289..28876dc52b 100644
--- a/sapi/cli/tests/php_cli_server_017.phpt
+++ b/sapi/cli/tests/php_cli_server_017.phpt
@@ -37,6 +37,7 @@ fclose($fp);
--EXPECTF--
HTTP/1.1 200 OK
Host: %s
+Date: %s
Connection: close
X-Powered-By: %s
Content-type: text/html; charset=UTF-8
diff --git a/sapi/cli/tests/php_cli_server_018.phpt b/sapi/cli/tests/php_cli_server_018.phpt
index 44e1292934..acfb0f2ce3 100644
--- a/sapi/cli/tests/php_cli_server_018.phpt
+++ b/sapi/cli/tests/php_cli_server_018.phpt
@@ -37,6 +37,7 @@ fclose($fp);
--EXPECTF--
HTTP/1.1 200 OK
Host: %s
+Date: %s
Connection: close
X-Powered-By: %s
Content-type: text/html; charset=UTF-8
diff --git a/sapi/cli/tests/php_cli_server_019.phpt b/sapi/cli/tests/php_cli_server_019.phpt
index aeb7a9f891..7be87a86c9 100644
--- a/sapi/cli/tests/php_cli_server_019.phpt
+++ b/sapi/cli/tests/php_cli_server_019.phpt
@@ -41,6 +41,7 @@ fclose($fp);
--EXPECTF--
HTTP/1.1 200 OK
Host: %s
+Date: %s
Connection: close
X-Powered-By: %s
Bar-Foo: Foo
diff --git a/sapi/cli/tests/php_cli_server_020.phpt b/sapi/cli/tests/php_cli_server_020.phpt
index 4ed673d380..766c666911 100644
--- a/sapi/cli/tests/php_cli_server_020.phpt
+++ b/sapi/cli/tests/php_cli_server_020.phpt
@@ -33,6 +33,7 @@ HEADER
--EXPECTF--
HTTP/1.1 200 OK
Host: %s
+Date: %s
Connection: close
X-Powered-By: PHP/%s
Content-type: text/html; charset=UTF-8
diff --git a/sapi/cli/tests/upload_2G.phpt b/sapi/cli/tests/upload_2G.phpt
index bd36ad6cda..21dacb439a 100644
--- a/sapi/cli/tests/upload_2G.phpt
+++ b/sapi/cli/tests/upload_2G.phpt
@@ -81,6 +81,7 @@ Test
HTTP/1.1 200 OK
Host: %s
+Date: %s
Connection: close
X-Powered-By: PHP/%s
Content-type: text/html; charset=UTF-8
diff --git a/sapi/fpm/fpm/fpm_conf.c b/sapi/fpm/fpm/fpm_conf.c
index 9a619ce887..2a92fb25a7 100644
--- a/sapi/fpm/fpm/fpm_conf.c
+++ b/sapi/fpm/fpm/fpm_conf.c
@@ -1573,7 +1573,7 @@ static void fpm_conf_dump() /* {{{ */
/*
* Please keep the same order as in fpm_conf.h and in php-fpm.conf.in
*/
- zlog(ZLOG_NOTICE, "[General]");
+ zlog(ZLOG_NOTICE, "[global]");
zlog(ZLOG_NOTICE, "\tpid = %s", STR2STR(fpm_global_config.pid_file));
zlog(ZLOG_NOTICE, "\terror_log = %s", STR2STR(fpm_global_config.error_log));
#ifdef HAVE_SYSLOG_H
diff --git a/sapi/fpm/fpm/fpm_events.c b/sapi/fpm/fpm/fpm_events.c
index ca45cb1665..2b8e8cf13a 100644
--- a/sapi/fpm/fpm/fpm_events.c
+++ b/sapi/fpm/fpm/fpm_events.c
@@ -291,7 +291,7 @@ int fpm_event_pre_init(char *machanism) /* {{{ */
}
return -1;
}
-/* }} */
+/* }}} */
const char *fpm_event_machanism_name() /* {{{ */
{
@@ -538,4 +538,3 @@ int fpm_event_del(struct fpm_event_s *ev) /* {{{ */
}
/* }}} */
-/* }}} */
diff --git a/sapi/litespeed/lsapilib.c b/sapi/litespeed/lsapilib.c
index 4fc4c6e2e6..41a1715d07 100644
--- a/sapi/litespeed/lsapilib.c
+++ b/sapi/litespeed/lsapilib.c
@@ -1499,7 +1499,7 @@ int LSAPI_ReqBodyGetLine_r( LSAPI_Request * pReq, char * pBuf, size_t bufLen, in
char * pBufCur = pBuf;
char * pCur;
char * p;
- if (!pReq || (pReq->m_fd ==-1) ||( !pBuf )|| !getLF )
+ if (!pReq || (pReq->m_fd ==-1) ||( !pBuf )||(bufLen < 0 )|| !getLF )
return -1;
*getLF = 0;
while( (left = pBufEnd - pBufCur ) > 0 )
@@ -1543,7 +1543,7 @@ ssize_t LSAPI_ReadReqBody_r( LSAPI_Request * pReq, char * pBuf, size_t bufLen )
ssize_t len;
off_t total;
/* char *pOldBuf = pBuf; */
- if (!pReq || (pReq->m_fd ==-1) || ( !pBuf ))
+ if (!pReq || (pReq->m_fd ==-1) || ( !pBuf )||(bufLen < 0 ))
return -1;
total = pReq->m_reqBodyLen - pReq->m_reqBodyRead;
diff --git a/sapi/phpdbg/phpdbg_frame.c b/sapi/phpdbg/phpdbg_frame.c
index d6256a84af..f64d18b4bb 100644
--- a/sapi/phpdbg/phpdbg_frame.c
+++ b/sapi/phpdbg/phpdbg_frame.c
@@ -36,8 +36,6 @@ void phpdbg_restore_frame(void) /* {{{ */
/* move things back */
EG(current_execute_data) = PHPDBG_FRAME(execute_data);
-
- EG(scope) = PHPDBG_EX(func)->op_array.scope;
} /* }}} */
void phpdbg_switch_frame(int frame) /* {{{ */
@@ -78,8 +76,6 @@ void phpdbg_switch_frame(int frame) /* {{{ */
/* backup things and jump back */
PHPDBG_FRAME(execute_data) = EG(current_execute_data);
EG(current_execute_data) = execute_data;
-
- EG(scope) = PHPDBG_EX(func)->op_array.scope;
}
phpdbg_notice("frame", "id=\"%d\"", "Switched to frame #%d", frame);
diff --git a/sapi/phpdbg/phpdbg_list.c b/sapi/phpdbg/phpdbg_list.c
index 6895bea43e..ca73209114 100644
--- a/sapi/phpdbg/phpdbg_list.c
+++ b/sapi/phpdbg/phpdbg_list.c
@@ -200,11 +200,12 @@ void phpdbg_list_function_byname(const char *str, size_t len) /* {{{ */
/* search active scope if begins with period */
if (func_name[0] == '.') {
- if (EG(scope)) {
+ zend_class_entry *scope = zend_get_executed_scope();
+ if (scope) {
func_name++;
func_name_len--;
- func_table = &EG(scope)->function_table;
+ func_table = &scope->function_table;
} else {
phpdbg_error("inactive", "type=\"noclasses\"", "No active class");
return;
diff --git a/sapi/phpdbg/phpdbg_opcode.c b/sapi/phpdbg/phpdbg_opcode.c
index e64ab0d636..44119c9e51 100644
--- a/sapi/phpdbg/phpdbg_opcode.c
+++ b/sapi/phpdbg/phpdbg_opcode.c
@@ -36,7 +36,8 @@ static inline const char *phpdbg_decode_opcode(zend_uchar opcode) /* {{{ */
return "UNKNOWN";
} /* }}} */
-static inline char *phpdbg_decode_op(zend_op_array *ops, znode_op *op, uint32_t type) /* {{{ */
+static inline char *phpdbg_decode_op(
+ zend_op_array *ops, const znode_op *op, uint32_t type) /* {{{ */
{
char *decode = NULL;
@@ -62,91 +63,57 @@ static inline char *phpdbg_decode_op(zend_op_array *ops, znode_op *op, uint32_t
return decode;
} /* }}} */
-char *phpdbg_decode_opline(zend_op_array *ops, zend_op *op) /*{{{ */
-{
- const char *opcode_name = phpdbg_decode_opcode(op->opcode);
- char *result, *decode[4] = {NULL, NULL, NULL, NULL};
-
- /* EX */
- switch (op->opcode) {
- case ZEND_FAST_CALL:
- if (op->extended_value == ZEND_FAST_CALL_FROM_FINALLY) {
- decode[0] = estrdup("FAST_CALL<FROM_FINALLY>");
+char *phpdbg_decode_input_op(
+ zend_op_array *ops, const zend_op *opline, znode_op op, zend_uchar op_type,
+ uint32_t flags) {
+ char *result = NULL;
+ if (op_type != IS_UNUSED) {
+ result = phpdbg_decode_op(ops, &op, op_type);
+ } else if (ZEND_VM_OP_JMP_ADDR == (flags & ZEND_VM_OP_MASK)) {
+ spprintf(&result, 0, "J%td", OP_JMP_ADDR(opline, op) - ops->opcodes);
+ } else if (ZEND_VM_OP_NUM == (flags & ZEND_VM_OP_MASK)) {
+ spprintf(&result, 0, "%" PRIu32, op.num);
+ } else if (ZEND_VM_OP_TRY_CATCH == (flags & ZEND_VM_OP_MASK)) {
+ if (op.num != (uint32_t)-1) {
+ spprintf(&result, 0, "try-catch(%" PRIu32 ")", op.num);
}
- break;
- case ZEND_FAST_RET:
- if (op->extended_value != 0) {
- spprintf(&decode[0], 0, "FAST_RET<%s>",
- op->extended_value == ZEND_FAST_RET_TO_CATCH ? "TO_CATCH" : "TO_FINALLY");
+ } else if (ZEND_VM_OP_LIVE_RANGE == (flags & ZEND_VM_OP_MASK)) {
+ if (opline->extended_value & ZEND_FREE_ON_RETURN) {
+ spprintf(&result, 0, "live-range(%" PRIu32 ")", op.num);
}
- break;
+ } else if (ZEND_VM_OP_THIS == (flags & ZEND_VM_OP_MASK)) {
+ result = estrdup("THIS");
+ } else if (ZEND_VM_OP_NEXT == (flags & ZEND_VM_OP_MASK)) {
+ result = estrdup("NEXT");
+ } else if (ZEND_VM_OP_CLASS_FETCH == (flags & ZEND_VM_OP_MASK)) {
+ //zend_dump_class_fetch_type(op.num);
+ } else if (ZEND_VM_OP_CONSTRUCTOR == (flags & ZEND_VM_OP_MASK)) {
+ result = estrdup("CONSTRUCTOR");
}
+ return result;
+}
- /* OP1 */
- switch (op->opcode) {
- case ZEND_JMP:
- case ZEND_FAST_CALL:
- spprintf(&decode[1], 0, "J%td", OP_JMP_ADDR(op, op->op1) - ops->opcodes);
- break;
-
- case ZEND_INIT_FCALL:
- case ZEND_RECV:
- case ZEND_RECV_INIT:
- case ZEND_RECV_VARIADIC:
- spprintf(&decode[1], 0, "%" PRIu32, op->op1.num);
- break;
+char *phpdbg_decode_opline(zend_op_array *ops, zend_op *opline) /*{{{ */
+{
+ const char *opcode_name = phpdbg_decode_opcode(opline->opcode);
+ uint32_t flags = zend_get_opcode_flags(opline->opcode);
+ char *result, *decode[4] = {NULL, NULL, NULL, NULL};
- default:
- decode[1] = phpdbg_decode_op(ops, &op->op1, op->op1_type);
- break;
- }
+ /* OP1 */
+ decode[1] = phpdbg_decode_input_op(
+ ops, opline, opline->op1, opline->op1_type, ZEND_VM_OP1_FLAGS(flags));
/* OP2 */
- switch (op->opcode) {
- case ZEND_JMPZNZ:
- spprintf(&decode[2], 0, "J%td or J%td",
- OP_JMP_ADDR(op, op->op2) - ops->opcodes,
- ZEND_OFFSET_TO_OPLINE(op, op->extended_value) - ops->opcodes);
- break;
-
- case ZEND_JMPZ:
- case ZEND_JMPNZ:
- case ZEND_JMPZ_EX:
- case ZEND_JMPNZ_EX:
- case ZEND_JMP_SET:
- case ZEND_ASSERT_CHECK:
- spprintf(&decode[2], 0, "J%td", OP_JMP_ADDR(op, op->op2) - ops->opcodes);
- break;
-
- case ZEND_FAST_CALL:
- case ZEND_FAST_RET:
- if (op->extended_value != 0) {
- spprintf(&decode[2], 0, "J%" PRIu32, op->op2.opline_num);
- }
- break;
-
- 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:
- spprintf(&decode[2], 0, "%" PRIu32, op->op2.num);
- break;
-
- default:
- decode[2] = phpdbg_decode_op(ops, &op->op2, op->op2_type);
- break;
- }
+ decode[2] = phpdbg_decode_input_op(
+ ops, opline, opline->op2, opline->op2_type, ZEND_VM_OP2_FLAGS(flags));
/* RESULT */
- switch (op->opcode) {
+ switch (opline->opcode) {
case ZEND_CATCH:
- spprintf(&decode[3], 0, "%" PRIu32, op->result.num);
+ spprintf(&decode[3], 0, "%" PRIu32, opline->result.num);
break;
default:
- decode[3] = phpdbg_decode_op(ops, &op->result, op->result_type);
+ decode[3] = phpdbg_decode_op(ops, &opline->result, opline->result_type);
break;
}
diff --git a/sapi/phpdbg/phpdbg_print.c b/sapi/phpdbg/phpdbg_print.c
index 3725bf6083..034354a9e7 100644
--- a/sapi/phpdbg/phpdbg_print.c
+++ b/sapi/phpdbg/phpdbg_print.c
@@ -217,11 +217,13 @@ PHPDBG_PRINT(func) /* {{{ */
zend_string *lcname;
/* search active scope if begins with period */
if (func_name[0] == '.') {
- if (EG(scope)) {
+ zend_class_entry *scope = zend_get_executed_scope();
+
+ if (scope) {
func_name++;
func_name_len--;
- func_table = &EG(scope)->function_table;
+ func_table = &scope->function_table;
} else {
phpdbg_error("inactive", "type=\"noclasses\"", "No active class");
return SUCCESS;
diff --git a/sapi/phpdbg/phpdbg_prompt.c b/sapi/phpdbg/phpdbg_prompt.c
index 6f73d3087a..2ca1368751 100644
--- a/sapi/phpdbg/phpdbg_prompt.c
+++ b/sapi/phpdbg/phpdbg_prompt.c
@@ -120,9 +120,7 @@ static inline int phpdbg_call_register(phpdbg_param_t *stack) /* {{{ */
ZVAL_STRINGL(&fci.function_name, lc_name, name->len);
fci.size = sizeof(zend_fcall_info);
- fci.function_table = &PHPDBG_G(registered);
//???fci.symbol_table = zend_rebuild_symbol_table();
- fci.symbol_table = NULL;
fci.object = NULL;
fci.retval = &fretval;
fci.no_separation = 1;
@@ -654,7 +652,7 @@ static inline void phpdbg_handle_exception(void) /* {{{ */
phpdbg_error("exception", "name=\"%s\" file=\"%s\" line=\"" ZEND_LONG_FMT "\"", "Uncaught %s in %s on line " ZEND_LONG_FMT, ZSTR_VAL(ex->ce->name), ZSTR_VAL(file), line);
zend_string_release(file);
- phpdbg_writeln("exceptionmsg", "msg=\"%s\"", ZSTR_VAL(msg));
+ phpdbg_writeln("exceptionmsg", "msg=\"%s\"", "%s", ZSTR_VAL(msg));
zend_string_release(msg);
if (EG(prev_exception)) {
@@ -689,7 +687,7 @@ PHPDBG_COMMAND(run) /* {{{ */
}
/* clean up from last execution */
- if (ex && ex->symbol_table) {
+ if (ex && (ZEND_CALL_INFO(ex) & ZEND_CALL_HAS_SYMBOL_TABLE)) {
zend_hash_clean(ex->symbol_table);
} else {
zend_rebuild_symbol_table();
@@ -799,7 +797,6 @@ PHPDBG_COMMAND(ev) /* {{{ */
zval retval;
zend_execute_data *original_execute_data = EG(current_execute_data);
- zend_class_entry *original_scope = EG(scope);
zend_vm_stack original_stack = EG(vm_stack);
zend_object *ex = NULL;
@@ -847,7 +844,6 @@ PHPDBG_COMMAND(ev) /* {{{ */
OBJ_RELEASE(ex);
}
EG(current_execute_data) = original_execute_data;
- EG(scope) = original_scope;
EG(vm_stack_top) = original_stack->top;
EG(vm_stack_end) = original_stack->end;
EG(vm_stack) = original_stack;
@@ -1634,7 +1630,7 @@ next:
execute_data->call->func->type == ZEND_USER_FUNCTION) {
zend_execute_ex = execute_ex;
}
- PHPDBG_G(vmret) = zend_vm_call_opcode_handler(execute_data);
+ PHPDBG_G(vmret) = zend_vm_call_opcode_handler(execute_data);
zend_execute_ex = phpdbg_execute_ex;
if (PHPDBG_G(vmret) != 0) {
diff --git a/sapi/phpdbg/phpdbg_utils.c b/sapi/phpdbg/phpdbg_utils.c
index 7158f3ba6c..6f42665809 100644
--- a/sapi/phpdbg/phpdbg_utils.c
+++ b/sapi/phpdbg/phpdbg_utils.c
@@ -804,6 +804,18 @@ char *phpdbg_short_zval_print(zval *zv, int maxlen) /* {{{ */
break;
case IS_DOUBLE:
spprintf(&decode, 0, "%.*G", 14, Z_DVAL_P(zv));
+
+ /* Make sure it looks like a float */
+ if (zend_finite(Z_DVAL_P(zv)) && !strchr(decode, '.')) {
+ size_t len = strlen(decode);
+ char *decode2 = emalloc(len + strlen(".0") + 1);
+ memcpy(decode2, decode, len);
+ decode2[len] = '.';
+ decode2[len+1] = '0';
+ decode2[len+2] = '\0';
+ efree(decode);
+ decode = decode2;
+ }
break;
case IS_STRING: {
int i;
diff --git a/sapi/phpdbg/phpdbg_watch.c b/sapi/phpdbg/phpdbg_watch.c
index 2c324aa5dc..0224ff4fd1 100644
--- a/sapi/phpdbg/phpdbg_watch.c
+++ b/sapi/phpdbg/phpdbg_watch.c
@@ -680,7 +680,9 @@ static int phpdbg_watchpoint_parse_step(char *name, size_t namelen, char *key, s
}
static int phpdbg_watchpoint_parse_symtables(char *input, size_t len, int (*callback)(phpdbg_watchpoint_t *)) {
- if (EG(scope) && len >= 5 && !memcmp("$this", input, 5)) {
+ zend_class_entry *scope = zend_get_executed_scope();
+
+ if (scope && len >= 5 && !memcmp("$this", input, 5)) {
zend_hash_str_add(EG(current_execute_data)->symbol_table, ZEND_STRL("this"), &EG(current_execute_data)->This);
}
diff --git a/sapi/phpdbg/tests/breakpoints_001.phpt b/sapi/phpdbg/tests/breakpoints_001.phpt
index 934f0d3554..17e7c65cbf 100644
--- a/sapi/phpdbg/tests/breakpoints_001.phpt
+++ b/sapi/phpdbg/tests/breakpoints_001.phpt
@@ -11,23 +11,23 @@ q
[Successful compilation of %s]
prompt> [Breakpoint #0 added at %s:3]
prompt> [Breakpoint #0 at %s:3, hits: 1]
->00003: echo 1;
- 00004: echo 2;
- 00005: echo 3;
+>00003: echo $i++;
+ 00004: echo $i++;
+ 00005: echo $i++;
prompt> [Breakpoint #1 added at %s:4]
prompt> 1
[Breakpoint #1 at %s:4, hits: 1]
->00004: echo 2;
- 00005: echo 3;
- 00006: echo 4;
+>00004: echo $i++;
+ 00005: echo $i++;
+ 00006: echo $i++;
prompt> 234
[Script ended normally]
prompt>
--FILE--
<?php
-
-echo 1;
-echo 2;
-echo 3;
-echo 4;
+$i = 1;
+echo $i++;
+echo $i++;
+echo $i++;
+echo $i++;
diff --git a/sapi/phpdbg/tests/breakpoints_002.phpt b/sapi/phpdbg/tests/breakpoints_002.phpt
index 18aaef1f36..96c98194b1 100644
--- a/sapi/phpdbg/tests/breakpoints_002.phpt
+++ b/sapi/phpdbg/tests/breakpoints_002.phpt
@@ -14,27 +14,27 @@ q
prompt> [Breakpoint #0 added at %s:4]
prompt> 1
[Breakpoint #0 at %s:4, hits: 1]
->00004: echo 2;
- 00005: echo 3;
- 00006: echo 4;
+>00004: echo $i++;
+ 00005: echo $i++;
+ 00006: echo $i++;
prompt> [Breakpoint #1 added at %s:3]
prompt> Do you really want to restart execution? (type y or n): [Breakpoint #1 at %s:3, hits: 1]
->00003: echo 1;
- 00004: echo 2;
- 00005: echo 3;
+>00003: echo $i++;
+ 00004: echo $i++;
+ 00005: echo $i++;
prompt> 1
[Breakpoint #0 at %s:4, hits: 1]
->00004: echo 2;
- 00005: echo 3;
- 00006: echo 4;
+>00004: echo $i++;
+ 00005: echo $i++;
+ 00006: echo $i++;
prompt> 234
[Script ended normally]
prompt>
--FILE--
<?php
-
-echo 1;
-echo 2;
-echo 3;
-echo 4;
+$i = 1;
+echo $i++;
+echo $i++;
+echo $i++;
+echo $i++;
diff --git a/sapi/phpdbg/tests/breakpoints_003.phpt b/sapi/phpdbg/tests/breakpoints_003.phpt
index 8caa64632b..12b2504487 100644
--- a/sapi/phpdbg/tests/breakpoints_003.phpt
+++ b/sapi/phpdbg/tests/breakpoints_003.phpt
@@ -16,8 +16,8 @@ prompt> [Deleted breakpoint #0]
prompt> [Breakpoint #1 added at %s:5]
prompt> 12
[Breakpoint #1 at %s:5, hits: 1]
->00005: echo 3;
- 00006: echo 4;
+>00005: echo $i++;
+ 00006: echo $i++;
00007:
prompt> [Deleted breakpoint #1]
prompt> Do you really want to restart execution? (type y or n): 1234
@@ -25,9 +25,9 @@ prompt> Do you really want to restart execution? (type y or n): 1234
prompt>
--FILE--
<?php
-
-echo 1;
-echo 2;
-echo 3;
-echo 4;
+$i = 1;
+echo $i++;
+echo $i++;
+echo $i++;
+echo $i++;
diff --git a/sapi/phpdbg/tests/breakpoints_004.phpt b/sapi/phpdbg/tests/breakpoints_004.phpt
index 27ebd0bea2..917e908efb 100644
--- a/sapi/phpdbg/tests/breakpoints_004.phpt
+++ b/sapi/phpdbg/tests/breakpoints_004.phpt
@@ -12,30 +12,30 @@ q
[Successful compilation of %s]
prompt> [Breakpoint #0 added at ZEND_ECHO]
prompt> [Breakpoint #0 in ZEND_ECHO at %s:3, hits: 1]
->00003: echo 1;
- 00004: echo 2;
- 00005: echo 3;
+>00003: echo $i++;
+ 00004: echo $i++;
+ 00005: echo $i++;
prompt> 1
[Breakpoint #0 in ZEND_ECHO at %s:4, hits: 2]
->00004: echo 2;
- 00005: echo 3;
- 00006: echo 4;
+>00004: echo $i++;
+ 00005: echo $i++;
+ 00006: echo $i++;
prompt> 2
[Breakpoint #0 in ZEND_ECHO at %s:5, hits: 3]
->00005: echo 3;
- 00006: echo 4;
+>00005: echo $i++;
+ 00006: echo $i++;
00007:
prompt> 3
[Breakpoint #0 in ZEND_ECHO at %s:6, hits: 4]
->00006: echo 4;
+>00006: echo $i++;
00007:
prompt> 4
[Script ended normally]
prompt>
--FILE--
<?php
-
-echo 1;
-echo 2;
-echo 3;
-echo 4;
+$i = 1;
+echo $i++;
+echo $i++;
+echo $i++;
+echo $i++;
diff --git a/sapi/phpdbg/tests/exceptions_003.phpt b/sapi/phpdbg/tests/exceptions_003.phpt
index fffe7a9296..2a982bcbe3 100644
--- a/sapi/phpdbg/tests/exceptions_003.phpt
+++ b/sapi/phpdbg/tests/exceptions_003.phpt
@@ -25,7 +25,7 @@ prompt> [L7 %s ECHO "ok "
00008: }
00009: } catch (Error $e) {
prompt> ok
-[L7 %s FAST_RET<TO_CATCH> ~%d J7 %s]
+[L7 %s FAST_RET ~%d try-catch(0) %s]
[L9 %s CATCH "Error" $e 1 %s]
>00005: x();
00006: } finally {
diff --git a/sapi/phpdbg/tests/stepping_001.phpt b/sapi/phpdbg/tests/stepping_001.phpt
index 5c92ba2f0b..9366550f56 100644
--- a/sapi/phpdbg/tests/stepping_001.phpt
+++ b/sapi/phpdbg/tests/stepping_001.phpt
@@ -1,5 +1,7 @@
--TEST--
Stepping with exceptions must not be stuck at CATCH
+--INI--
+opcache.enable=0
--PHPDBG--
b ZEND_THROW
r
diff --git a/server-tests.php b/server-tests.php
index af4ce018e8..6971d679f1 100755
--- a/server-tests.php
+++ b/server-tests.php
@@ -48,14 +48,6 @@ define('PCRE_MISSING_ERROR',
| with --with-pcre-regex or if you have compiled pcre as a |
| shared module load it via php.ini. |
+-----------------------------------------------------------+');
-define('SAFE_MODE_WARNING',
-'+-----------------------------------------------------------+
-| ! WARNING ! |
-| You are running the test-suite with "safe_mode" ENABLED ! |
-| |
-| Chances are high that no test will work at all, |
-| depending on how you configured "safe_mode" ! |
-+-----------------------------------------------------------+');
define('TMP_MISSING',
'+-----------------------------------------------------------+
| ! ERROR ! |
@@ -456,7 +448,6 @@ class testHarness {
'output_handler'=>'',
'zlib.output_compression'=>'Off',
'open_basedir'=>'',
- 'safe_mode'=>'0',
'disable_functions'=>'',
'output_buffering'=>'Off',
'error_reporting'=>'4095',
@@ -736,7 +727,7 @@ class testHarness {
}
function checkRequirements() {
- if (version_compare(phpversion(), "5.0") < 0) {
+ if (version_compare(phpversion(), "5.4") < 0) {
$this->writemsg(REQ_PHP_VERSION);
exit;
}
@@ -753,9 +744,6 @@ class testHarness {
$this->writemsg(PCRE_MISSING_ERROR);
exit;
}
- if (ini_get('safe_mode')) {
- $this->writemsg(SAFE_MODE_WARNING);
- }
}
//
diff --git a/tests/classes/__call_007.phpt b/tests/classes/__call_007.phpt
index b061d17d85..cc7a2773bd 100644
--- a/tests/classes/__call_007.phpt
+++ b/tests/classes/__call_007.phpt
@@ -54,21 +54,25 @@ try {
--EXPECTF--
Warning: The magic method __call() must have public visibility and cannot be static in %s on line 3
---> Invoke __call via simple method call.
-NULL
+object(A)#1 (0) {
+}
Exception caught OK; continuing.
---> Invoke __call via scope resolution operator within instance.
-NULL
+object(A)#1 (0) {
+}
Exception caught OK; continuing.
---> Invoke __call via scope resolution operator within child instance.
-NULL
+object(B)#2 (0) {
+}
Exception caught OK; continuing.
---> Invoke __call via callback.
-NULL
+object(B)#2 (0) {
+}
Exception caught OK; continuing.
==DONE==
diff --git a/tests/classes/constants_comments_001.phpt b/tests/classes/constants_comments_001.phpt
new file mode 100644
index 0000000000..dbdd67c332
--- /dev/null
+++ b/tests/classes/constants_comments_001.phpt
@@ -0,0 +1,34 @@
+--TEST--
+Class constants and doc comments
+--INI--
+opcache.save_comments=1
+--FILE--
+<?php
+class X {
+ /** comment X1 */
+ const X1 = 1;
+ const X2 = 2;
+ /** comment X3 */
+ const X3 = 3;
+}
+class Y extends X {
+ /** comment Y1 */
+ const Y1 = 1;
+ const Y2 = 2;
+ /** comment Y3 */
+ const Y3 = 3;
+}
+$r = new ReflectionClass('Y');
+foreach ($r->getReflectionConstants() as $rc) {
+ echo $rc->getName() . " : " . $rc->getDocComment() . "\n";
+}
+
+
+?>
+--EXPECT--
+Y1 : /** comment Y1 */
+Y2 :
+Y3 : /** comment Y3 */
+X1 : /** comment X1 */
+X2 :
+X3 : /** comment X3 */
diff --git a/tests/classes/constants_visibility_001.phpt b/tests/classes/constants_visibility_001.phpt
new file mode 100644
index 0000000000..37a0154d92
--- /dev/null
+++ b/tests/classes/constants_visibility_001.phpt
@@ -0,0 +1,23 @@
+--TEST--
+Class public constant visibility
+--FILE--
+<?php
+class A {
+ public const publicConst = 'publicConst';
+ static function staticConstDump() {
+ var_dump(self::publicConst);
+ }
+ function constDump() {
+ var_dump(self::publicConst);
+ }
+}
+
+var_dump(A::publicConst);
+A::staticConstDump();
+(new A())->constDump();
+
+?>
+--EXPECTF--
+string(11) "publicConst"
+string(11) "publicConst"
+string(11) "publicConst"
diff --git a/tests/classes/constants_visibility_002.phpt b/tests/classes/constants_visibility_002.phpt
new file mode 100644
index 0000000000..82bdd6df1f
--- /dev/null
+++ b/tests/classes/constants_visibility_002.phpt
@@ -0,0 +1,30 @@
+--TEST--
+Class protected constant visibility
+--FILE--
+<?php
+class A {
+ protected const protectedConst = 'protectedConst';
+ static function staticConstDump() {
+ var_dump(self::protectedConst);
+ }
+ function constDump() {
+ var_dump(self::protectedConst);
+ }
+}
+
+A::staticConstDump();
+(new A())->constDump();
+constant('A::protectedConst');
+
+?>
+--EXPECTF--
+string(14) "protectedConst"
+string(14) "protectedConst"
+
+Warning: constant(): Couldn't find constant A::protectedConst in %s on line 14
+
+Fatal error: Uncaught Error: Cannot access protected const A::protectedConst in %s:14
+Stack trace:
+#0 %s(14): constant('A::protectedCon...')
+#1 {main}
+ thrown in %s on line 14
diff --git a/tests/classes/constants_visibility_003.phpt b/tests/classes/constants_visibility_003.phpt
new file mode 100644
index 0000000000..7f049abffb
--- /dev/null
+++ b/tests/classes/constants_visibility_003.phpt
@@ -0,0 +1,30 @@
+--TEST--
+Class private constant visibility
+--FILE--
+<?php
+class A {
+ private const privateConst = 'privateConst';
+ static function staticConstDump() {
+ var_dump(self::privateConst);
+ }
+ function constDump() {
+ var_dump(self::privateConst);
+ }
+}
+
+A::staticConstDump();
+(new A())->constDump();
+constant('A::privateConst');
+
+?>
+--EXPECTF--
+string(12) "privateConst"
+string(12) "privateConst"
+
+Warning: constant(): Couldn't find constant A::privateConst in %s on line 14
+
+Fatal error: Uncaught Error: Cannot access private const A::privateConst in %s:14
+Stack trace:
+#0 %s(14): constant('A::privateConst')
+#1 {main}
+ thrown in %s on line 14
diff --git a/tests/classes/constants_visibility_004.phpt b/tests/classes/constants_visibility_004.phpt
new file mode 100644
index 0000000000..93acacf3c9
--- /dev/null
+++ b/tests/classes/constants_visibility_004.phpt
@@ -0,0 +1,28 @@
+--TEST--
+Only public and protected class constants should be inherited
+--FILE--
+<?php
+class A {
+ public const X = 1;
+ protected const Y = 2;
+ private const Z = 3;
+}
+class B extends A {
+ static public function checkConstants() {
+ var_dump(self::X);
+ var_dump(self::Y);
+ var_dump(self::Z);
+ }
+}
+
+B::checkConstants();
+?>
+--EXPECTF--
+int(1)
+int(2)
+
+Fatal error: Uncaught Error: Undefined class constant 'Z' in %s:11
+Stack trace:
+#0 %s(15): B::checkConstants()
+#1 {main}
+ thrown in %s on line 11
diff --git a/tests/classes/constants_visibility_005.phpt b/tests/classes/constants_visibility_005.phpt
new file mode 100644
index 0000000000..813009c675
--- /dev/null
+++ b/tests/classes/constants_visibility_005.phpt
@@ -0,0 +1,10 @@
+--TEST--
+Static constants are not allowed
+--FILE--
+<?php
+class A {
+ static const X = 1;
+}
+?>
+--EXPECTF--
+Fatal error: Cannot use 'static' as constant modifier in %s on line 3
diff --git a/tests/classes/constants_visibility_006.phpt b/tests/classes/constants_visibility_006.phpt
new file mode 100644
index 0000000000..537c8eac0f
--- /dev/null
+++ b/tests/classes/constants_visibility_006.phpt
@@ -0,0 +1,11 @@
+--TEST--
+Abstract constants are not allowed
+--FILE--
+<?php
+class A {
+ abstract const X = 1;
+}
+?>
+--EXPECTF--
+Fatal error: Cannot use 'abstract' as constant modifier in %s on line 3
+
diff --git a/tests/classes/constants_visibility_007.phpt b/tests/classes/constants_visibility_007.phpt
new file mode 100644
index 0000000000..f1b040c5c3
--- /dev/null
+++ b/tests/classes/constants_visibility_007.phpt
@@ -0,0 +1,10 @@
+--TEST--
+Final constants are not allowed
+--FILE--
+<?php
+class A {
+ final const X = 1;
+}
+?>
+--EXPECTF--
+Fatal error: Cannot use 'final' as constant modifier in %s on line 3
diff --git a/tests/classes/constants_visibility_error_001.phpt b/tests/classes/constants_visibility_error_001.phpt
new file mode 100644
index 0000000000..397dd24882
--- /dev/null
+++ b/tests/classes/constants_visibility_error_001.phpt
@@ -0,0 +1,16 @@
+--TEST--
+Class private constant visibility error
+--FILE--
+<?php
+class A {
+ private const privateConst = 'privateConst';
+}
+
+var_dump(A::privateConst);
+
+?>
+--EXPECTF--
+Fatal error: Uncaught Error: Cannot access private const A::privateConst in %s:6
+Stack trace:
+#0 {main}
+ thrown in %s on line 6
diff --git a/tests/classes/constants_visibility_error_002.phpt b/tests/classes/constants_visibility_error_002.phpt
new file mode 100644
index 0000000000..2980b52c37
--- /dev/null
+++ b/tests/classes/constants_visibility_error_002.phpt
@@ -0,0 +1,16 @@
+--TEST--
+Class protected constant visibility error
+--FILE--
+<?php
+class A {
+ protected const protectedConst = 'protectedConst';
+}
+
+var_dump(A::protectedConst);
+
+?>
+--EXPECTF--
+Fatal error: Uncaught Error: Cannot access protected const A::protectedConst in %s:6
+Stack trace:
+#0 {main}
+ thrown in %s on line 6
diff --git a/tests/classes/constants_visibility_error_003.phpt b/tests/classes/constants_visibility_error_003.phpt
new file mode 100644
index 0000000000..c385bbd300
--- /dev/null
+++ b/tests/classes/constants_visibility_error_003.phpt
@@ -0,0 +1,16 @@
+--TEST--
+A redeclared class constant must have the same or higher visibility
+--FILE--
+<?php
+
+class A {
+ public const publicConst = 0;
+}
+
+class B extends A {
+ protected const publicConst = 1;
+}
+
+
+--EXPECTF--
+Fatal error: Access level to B::publicConst must be public (as in class A) in %s on line 9
diff --git a/tests/classes/constants_visibility_error_004.phpt b/tests/classes/constants_visibility_error_004.phpt
new file mode 100644
index 0000000000..fe37b0691f
--- /dev/null
+++ b/tests/classes/constants_visibility_error_004.phpt
@@ -0,0 +1,16 @@
+--TEST--
+A redeclared class constant must have the same or higher visibility
+--FILE--
+<?php
+
+class A {
+ protected const protectedConst = 0;
+}
+
+class B extends A {
+ private const protectedConst = 1;
+}
+
+
+--EXPECTF--
+Fatal error: Access level to B::protectedConst must be protected (as in class A) or weaker in %s on line 9
diff --git a/tests/classes/interface_constant_inheritance_005.phpt b/tests/classes/interface_constant_inheritance_005.phpt
new file mode 100644
index 0000000000..60bf222e85
--- /dev/null
+++ b/tests/classes/interface_constant_inheritance_005.phpt
@@ -0,0 +1,12 @@
+--TEST--
+Ensure a interface can have public constants
+--FILE--
+<?php
+interface IA {
+ public const FOO = 10;
+}
+
+echo "Done\n";
+?>
+--EXPECT--
+Done \ No newline at end of file
diff --git a/tests/classes/interface_constant_inheritance_006.phpt b/tests/classes/interface_constant_inheritance_006.phpt
new file mode 100644
index 0000000000..125326b224
--- /dev/null
+++ b/tests/classes/interface_constant_inheritance_006.phpt
@@ -0,0 +1,10 @@
+--TEST--
+Ensure a interface can not have protected constants
+
+--FILE--
+<?php
+interface A {
+ protected const FOO = 10;
+}
+--EXPECTF--
+Fatal error: Access type for interface constant A::FOO must be public in %s on line 3
diff --git a/tests/classes/interface_constant_inheritance_007.phpt b/tests/classes/interface_constant_inheritance_007.phpt
new file mode 100644
index 0000000000..52695343e1
--- /dev/null
+++ b/tests/classes/interface_constant_inheritance_007.phpt
@@ -0,0 +1,10 @@
+--TEST--
+Ensure a interface can not have private constants
+
+--FILE--
+<?php
+interface A {
+ private const FOO = 10;
+}
+--EXPECTF--
+Fatal error: Access type for interface constant A::FOO must be public in %s on line 3
diff --git a/tests/classes/interfaces_003.phpt b/tests/classes/interfaces_003.phpt
index e1cbfdaf54..e66a86491c 100644
--- a/tests/classes/interfaces_003.phpt
+++ b/tests/classes/interfaces_003.phpt
@@ -23,7 +23,7 @@ $obj = new MyTestClass;
===DONE===
--EXPECTF--
-Fatal error: Uncaught TypeError: Argument 1 passed to MyTestClass::__construct() must be an instance of MyObject, none given, called in %sinterfaces_003.php:%d
+Fatal error: Uncaught Error: Too few arguments to function MyTestClass::__construct(), 0 passed in %sinterfaces_003.php on line 17 and exactly 1 expected in %sinterfaces_003.php:12
Stack trace:
#0 %s(%d): MyTestClass->__construct()
#1 {main}
diff --git a/tests/classes/static_this.phpt b/tests/classes/static_this.phpt
index 91b0287195..f7a11f5481 100644
--- a/tests/classes/static_this.phpt
+++ b/tests/classes/static_this.phpt
@@ -28,12 +28,4 @@ TestClass::Test2(new stdClass);
?>
===DONE===
--EXPECTF--
-
-Notice: Undefined variable: this in %sstatic_this.php on line %d
-NULL
-
-Notice: Undefined variable: this in %sstatic_this.php on line %d
-NULL
-object(stdClass)#%d (0) {
-}
-===DONE===
+Fatal error: Cannot use $this as parameter in %sstatic_this.php on line 16
diff --git a/tests/lang/045.phpt b/tests/lang/045.phpt
index 11598cf035..44fb801410 100644
--- a/tests/lang/045.phpt
+++ b/tests/lang/045.phpt
@@ -3,7 +3,10 @@ Timeout again inside register_shutdown_function
--SKIPIF--
<?php
if (getenv("SKIP_SLOW_TESTS")) die("skip slow test");
+if (PHP_ZTS) die("skip hard_timeout works only on no-zts builds");
?>
+--INI--
+hard_timeout=1
--FILE--
<?php
set_time_limit(1);
diff --git a/tests/lang/bug22592.phpt b/tests/lang/bug22592.phpt
index 270584185f..4614efc169 100644
--- a/tests/lang/bug22592.phpt
+++ b/tests/lang/bug22592.phpt
@@ -32,7 +32,8 @@ var_dump($result);
$e = $result[1] = $result[6];
var_dump($result);
var_dump($a, $b, $c, $d, $e);
-$result[-1] = 'a';
+$result[0] = $result[-4] = $result[-1] = 'a';
+var_dump($result);
?>
--EXPECT--
string(5) "* *-*"
@@ -50,4 +51,4 @@ string(1) "s"
string(1) "4"
string(1) "5"
string(1) "5"
-[Illegal string offset: -1]
+string(9) "a54s4a50a"
diff --git a/tests/lang/bug28800.phpt b/tests/lang/bug28800.phpt
index f81ad7fec9..8bd2c306e1 100644
--- a/tests/lang/bug28800.phpt
+++ b/tests/lang/bug28800.phpt
@@ -7,11 +7,23 @@ Bug #28800 (Incorrect string to number conversion for strings starting with 'inf
echo ($v+0)."\n";
}
?>
---EXPECT--
+--EXPECTF--
+
+Warning: A non-numeric value encountered in %s on line %d
0
+
+Warning: A non-numeric value encountered in %s on line %d
0
+
+Warning: A non-numeric value encountered in %s on line %d
0
+
+Warning: A non-numeric value encountered in %s on line %d
0
+
+Warning: A non-numeric value encountered in %s on line %d
0
+
+Warning: A non-numeric value encountered in %s on line %d
0
diff --git a/tests/lang/bug71897.phpt b/tests/lang/bug71897.phpt
new file mode 100644
index 0000000000..bd76921af6
--- /dev/null
+++ b/tests/lang/bug71897.phpt
@@ -0,0 +1,16 @@
+--TEST--
+Bug #71897 (ASCII 0x7F Delete control character permitted in identifiers)
+--FILE--
+<?php
+
+eval("
+ \$a\x7Fb = 3;
+ var_dump(\$a\x7Fb);
+");
+
+?>
+--EXPECTF--
+
+Warning: Unexpected character in input: '%s' (ASCII=127) state=0 in %s(%d) : eval()'d code on line %d
+
+Parse error: syntax error, unexpected 'b' (T_STRING) in %s(%d) : eval()'d code on line %d
diff --git a/tests/lang/operators/bitwiseShiftLeft_variationStr_64bit.phpt b/tests/lang/operators/bitwiseShiftLeft_variationStr_64bit.phpt
index d5888d837f..69fd90f1c8 100644
--- a/tests/lang/operators/bitwiseShiftLeft_variationStr_64bit.phpt
+++ b/tests/lang/operators/bitwiseShiftLeft_variationStr_64bit.phpt
@@ -226,17 +226,17 @@ int(984)
--- testing: '123abc' << 'a5.9' ---
int(123)
--- testing: '123e5' << '0' ---
-int(123)
+int(12300000)
--- testing: '123e5' << '65' ---
int(0)
--- testing: '123e5' << '-44' ---
Exception: Bit shift by negative number
--- testing: '123e5' << '1.2' ---
-int(246)
+int(24600000)
--- testing: '123e5' << '-7.7' ---
Exception: Bit shift by negative number
--- testing: '123e5' << 'abc' ---
-int(123)
+int(12300000)
--- testing: '123e5' << '123abc' ---
int(0)
--- testing: '123e5' << '123e5' ---
@@ -250,21 +250,21 @@ int(0)
--- testing: '123e5' << '123abc ' ---
int(0)
--- testing: '123e5' << '3.4a' ---
-int(984)
+int(98400000)
--- testing: '123e5' << 'a5.9' ---
-int(123)
+int(12300000)
--- testing: '123e5xyz' << '0' ---
-int(123)
+int(12300000)
--- testing: '123e5xyz' << '65' ---
int(0)
--- testing: '123e5xyz' << '-44' ---
Exception: Bit shift by negative number
--- testing: '123e5xyz' << '1.2' ---
-int(246)
+int(24600000)
--- testing: '123e5xyz' << '-7.7' ---
Exception: Bit shift by negative number
--- testing: '123e5xyz' << 'abc' ---
-int(123)
+int(12300000)
--- testing: '123e5xyz' << '123abc' ---
int(0)
--- testing: '123e5xyz' << '123e5' ---
@@ -278,9 +278,9 @@ int(0)
--- testing: '123e5xyz' << '123abc ' ---
int(0)
--- testing: '123e5xyz' << '3.4a' ---
-int(984)
+int(98400000)
--- testing: '123e5xyz' << 'a5.9' ---
-int(123)
+int(12300000)
--- testing: ' 123abc' << '0' ---
int(123)
--- testing: ' 123abc' << '65' ---
diff --git a/tests/lang/operators/bitwiseShiftRight_variationStr.phpt b/tests/lang/operators/bitwiseShiftRight_variationStr.phpt
index a86d0cfddb..a4c425aab3 100644
--- a/tests/lang/operators/bitwiseShiftRight_variationStr.phpt
+++ b/tests/lang/operators/bitwiseShiftRight_variationStr.phpt
@@ -222,17 +222,17 @@ int(15)
--- testing: '123abc' >> 'a5.9' ---
int(123)
--- testing: '123e5' >> '0' ---
-int(123)
+int(12300000)
--- testing: '123e5' >> '65' ---
int(0)
--- testing: '123e5' >> '-44' ---
Exception: Bit shift by negative number
--- testing: '123e5' >> '1.2' ---
-int(61)
+int(6150000)
--- testing: '123e5' >> '-7.7' ---
Exception: Bit shift by negative number
--- testing: '123e5' >> 'abc' ---
-int(123)
+int(12300000)
--- testing: '123e5' >> '123abc' ---
int(0)
--- testing: '123e5' >> '123e5' ---
@@ -246,21 +246,21 @@ int(0)
--- testing: '123e5' >> '123abc ' ---
int(0)
--- testing: '123e5' >> '3.4a' ---
-int(15)
+int(1537500)
--- testing: '123e5' >> 'a5.9' ---
-int(123)
+int(12300000)
--- testing: '123e5xyz' >> '0' ---
-int(123)
+int(12300000)
--- testing: '123e5xyz' >> '65' ---
int(0)
--- testing: '123e5xyz' >> '-44' ---
Exception: Bit shift by negative number
--- testing: '123e5xyz' >> '1.2' ---
-int(61)
+int(6150000)
--- testing: '123e5xyz' >> '-7.7' ---
Exception: Bit shift by negative number
--- testing: '123e5xyz' >> 'abc' ---
-int(123)
+int(12300000)
--- testing: '123e5xyz' >> '123abc' ---
int(0)
--- testing: '123e5xyz' >> '123e5' ---
@@ -274,9 +274,9 @@ int(0)
--- testing: '123e5xyz' >> '123abc ' ---
int(0)
--- testing: '123e5xyz' >> '3.4a' ---
-int(15)
+int(1537500)
--- testing: '123e5xyz' >> 'a5.9' ---
-int(123)
+int(12300000)
--- testing: ' 123abc' >> '0' ---
int(123)
--- testing: ' 123abc' >> '65' ---
diff --git a/tests/lang/operators/modulus_variationStr.phpt b/tests/lang/operators/modulus_variationStr.phpt
index c647ecd380..4cfd7768ff 100644
--- a/tests/lang/operators/modulus_variationStr.phpt
+++ b/tests/lang/operators/modulus_variationStr.phpt
@@ -208,9 +208,9 @@ Exception: Modulo by zero
--- testing: '123abc' % '123abc' ---
int(0)
--- testing: '123abc' % '123e5' ---
-int(0)
+int(123)
--- testing: '123abc' % '123e5xyz' ---
-int(0)
+int(123)
--- testing: '123abc' % ' 123abc' ---
int(0)
--- testing: '123abc' % '123 abc' ---
@@ -224,13 +224,13 @@ Exception: Modulo by zero
--- testing: '123e5' % '0' ---
Exception: Modulo by zero
--- testing: '123e5' % '65' ---
-int(58)
+int(50)
--- testing: '123e5' % '-44' ---
-int(35)
+int(20)
--- testing: '123e5' % '1.2' ---
int(0)
--- testing: '123e5' % '-7.7' ---
-int(4)
+int(6)
--- testing: '123e5' % 'abc' ---
Exception: Modulo by zero
--- testing: '123e5' % '123abc' ---
@@ -252,13 +252,13 @@ Exception: Modulo by zero
--- testing: '123e5xyz' % '0' ---
Exception: Modulo by zero
--- testing: '123e5xyz' % '65' ---
-int(58)
+int(50)
--- testing: '123e5xyz' % '-44' ---
-int(35)
+int(20)
--- testing: '123e5xyz' % '1.2' ---
int(0)
--- testing: '123e5xyz' % '-7.7' ---
-int(4)
+int(6)
--- testing: '123e5xyz' % 'abc' ---
Exception: Modulo by zero
--- testing: '123e5xyz' % '123abc' ---
@@ -292,9 +292,9 @@ Exception: Modulo by zero
--- testing: ' 123abc' % '123abc' ---
int(0)
--- testing: ' 123abc' % '123e5' ---
-int(0)
+int(123)
--- testing: ' 123abc' % '123e5xyz' ---
-int(0)
+int(123)
--- testing: ' 123abc' % ' 123abc' ---
int(0)
--- testing: ' 123abc' % '123 abc' ---
@@ -320,9 +320,9 @@ Exception: Modulo by zero
--- testing: '123 abc' % '123abc' ---
int(0)
--- testing: '123 abc' % '123e5' ---
-int(0)
+int(123)
--- testing: '123 abc' % '123e5xyz' ---
-int(0)
+int(123)
--- testing: '123 abc' % ' 123abc' ---
int(0)
--- testing: '123 abc' % '123 abc' ---
@@ -348,9 +348,9 @@ Exception: Modulo by zero
--- testing: '123abc ' % '123abc' ---
int(0)
--- testing: '123abc ' % '123e5' ---
-int(0)
+int(123)
--- testing: '123abc ' % '123e5xyz' ---
-int(0)
+int(123)
--- testing: '123abc ' % ' 123abc' ---
int(0)
--- testing: '123abc ' % '123 abc' ---
diff --git a/tests/lang/operators/negate_variationStr.phpt b/tests/lang/operators/negate_variationStr.phpt
index a25bdda7f5..7405d42882 100644
--- a/tests/lang/operators/negate_variationStr.phpt
+++ b/tests/lang/operators/negate_variationStr.phpt
@@ -16,7 +16,7 @@ foreach ($strVals as $strVal) {
?>
===DONE===
---EXPECT--
+--EXPECTF--
--- testing: '0' ---
int(0)
--- testing: '65' ---
@@ -28,21 +28,37 @@ float(-1.2)
--- testing: '-7.7' ---
float(7.7)
--- testing: 'abc' ---
+
+Warning: A non-numeric value encountered in %s on line %d
int(0)
--- testing: '123abc' ---
+
+Notice: A non well formed numeric value encountered in %s on line %d
int(-123)
--- testing: '123e5' ---
float(-12300000)
--- testing: '123e5xyz' ---
+
+Notice: A non well formed numeric value encountered in %s on line %d
float(-12300000)
--- testing: ' 123abc' ---
+
+Notice: A non well formed numeric value encountered in %s on line %d
int(-123)
--- testing: '123 abc' ---
+
+Notice: A non well formed numeric value encountered in %s on line %d
int(-123)
--- testing: '123abc ' ---
+
+Notice: A non well formed numeric value encountered in %s on line %d
int(-123)
--- testing: '3.4a' ---
+
+Notice: A non well formed numeric value encountered in %s on line %d
float(-3.4)
--- testing: 'a5.9' ---
+
+Warning: A non-numeric value encountered in %s on line %d
int(0)
===DONE===
diff --git a/tests/output/ob_010.phpt b/tests/output/ob_010.phpt
index 24d650c50d..7e362cabe1 100644
--- a/tests/output/ob_010.phpt
+++ b/tests/output/ob_010.phpt
@@ -4,10 +4,10 @@ output buffering - fatalism
<?php
function obh($s)
{
- print_r($s, 1);
+ return print_r($s, 1);
}
ob_start("obh");
echo "foo\n";
?>
--EXPECTF--
-Fatal error: print_r(): Cannot use output buffering in output buffering display handlers in %sob_010.php on line %d
+foo
diff --git a/win32/build/buildconf.js b/win32/build/buildconf.js
index a3a4f1e9aa..d7975d0139 100644
--- a/win32/build/buildconf.js
+++ b/win32/build/buildconf.js
@@ -244,6 +244,8 @@ for (i = 0; i < calls.length; i++) {
}
C.WriteBlankLines(1);
+C.WriteLine("STDOUT.WriteLine(\"PHP Version: \" + PHP_VERSION_STRING);");
+C.WriteLine("STDOUT.WriteBlankLines(1);");
C.WriteLine("conf_process_args();");
C.WriteBlankLines(1);
diff --git a/win32/build/config.w32 b/win32/build/config.w32
index b7e5e49220..609f5c5d46 100644
--- a/win32/build/config.w32
+++ b/win32/build/config.w32
@@ -160,7 +160,8 @@ ADD_SOURCES("main/streams", "streams.c cast.c memory.c filter.c plain_wrapper.c
ADD_FLAG("CFLAGS_BD_MAIN_STREAMS", "/D ZEND_ENABLE_STATIC_TSRMLS_CACHE=1");
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");
+ registry.c select.c sendmail.c time.c winutil.c wsyslog.c globals.c \
+ getrusage.c ftok.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 fd02cc7878..2a8297a144 100644
--- a/win32/build/config.w32.h.in
+++ b/win32/build/config.w32.h.in
@@ -22,15 +22,6 @@
#define PHP_PREFIX "@PREFIX@"
#define PHP_SYSCONFDIR "@PREFIX@"
-/* Enable / Disable crypt() function (default: enabled) */
-#define HAVE_CRYPT 1
-#define PHP_STD_DES_CRYPT 1
-#define PHP_EXT_DES_CRYPT 1
-#define PHP_MD5_CRYPT 1
-#define PHP_BLOWFISH_CRYPT 1
-#define PHP_SHA512_CRYPT 1
-#define PHP_SHA256_CRYPT 1
-
/* PHP Runtime Configuration */
#define PHP_URL_FOPEN 1
#define USE_CONFIG_FILE 1
@@ -183,3 +174,4 @@
#define HAVE_GETRUSAGE
+#define HAVE_FTOK 1
diff --git a/win32/build/confutils.js b/win32/build/confutils.js
index dd9bddd57a..eb3658b68c 100644
--- a/win32/build/confutils.js
+++ b/win32/build/confutils.js
@@ -100,10 +100,10 @@ if (typeof(CWD) == "undefined") {
/* defaults; we pick up the precise versions from configure.in */
var PHP_VERSION = 7;
-var PHP_MINOR_VERSION = 0;
+var PHP_MINOR_VERSION = 1;
var PHP_RELEASE_VERSION = 0;
var PHP_EXTRA_VERSION = "";
-var PHP_VERSION_STRING = "7.0.0";
+var PHP_VERSION_STRING = "7.1.0";
/* Get version numbers and DEFINE as a string */
function get_version_numbers()
@@ -2693,6 +2693,15 @@ function toolset_setup_common_cflags()
if (VCVERS >= 1900) {
ADD_FLAG('CFLAGS', "/guard:cf");
}
+ if (VCVERS >= 1800) {
+ if (PHP_PGI != "yes" && PHP_PGO != "yes") {
+ ADD_FLAG('CFLAGS', "/Zc:inline");
+ }
+ /* We enable /opt:icf only with the debug pack, so /Gw only makes sense there, too. */
+ if (PHP_DEBUG_PACK == "yes") {
+ ADD_FLAG('CFLAGS', "/Gw");
+ }
+ }
}
} else if (CLANG_TOOLSET) {
diff --git a/win32/build/default.manifest b/win32/build/default.manifest
index 77b2a2165a..992f41c738 100644
--- a/win32/build/default.manifest
+++ b/win32/build/default.manifest
@@ -1,6 +1,13 @@
<?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">
+ <trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
+ <security>
+ <requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
+ <requestedExecutionLevel level="asInvoker" uiAccess="false" />
+ </requestedPrivileges>
+ </security>
+ </trustInfo>
+ <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<!-- Windows Vista -->
<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/>
diff --git a/win32/ftok.c b/win32/ftok.c
new file mode 100644
index 0000000000..842da78192
--- /dev/null
+++ b/win32/ftok.c
@@ -0,0 +1,52 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 7 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2016 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: Anatol Belski <ab@php.net> |
+ +----------------------------------------------------------------------+
+ */
+
+#include "ipc.h"
+
+#include <windows.h>
+#include <sys/stat.h>
+
+
+PHPAPI key_t
+ftok(const char *pathname, int proj_id)
+{
+ HANDLE fh;
+ struct stat st;
+ BY_HANDLE_FILE_INFORMATION bhfi;
+ key_t ret;
+
+ if (stat(pathname, &st) < 0) {
+ return (key_t)-1;
+ }
+
+ if ((fh = CreateFile(pathname, GENERIC_READ, 0, 0, OPEN_EXISTING, 0, 0)) == INVALID_HANDLE_VALUE) {
+ return (key_t)-1;
+ }
+
+ if (!GetFileInformationByHandle(fh, &bhfi)) {
+ CloseHandle(fh);
+ return (key_t)-1;
+ }
+
+ ret = (key_t) ((proj_id & 0xff) << 24 | (st.st_dev & 0xff) << 16 | ((bhfi.nFileIndexLow | (__int64)bhfi.nFileIndexHigh << 32) & 0xffff));
+
+ CloseHandle(fh);
+
+ return ret;
+}
+
diff --git a/win32/ipc.h b/win32/ipc.h
new file mode 100644
index 0000000000..cafcf4f85e
--- /dev/null
+++ b/win32/ipc.h
@@ -0,0 +1,29 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 7 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2016 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: Anatol Belski <ab@php.net> |
+ +----------------------------------------------------------------------+
+ */
+
+#ifndef PHP_WIN32_IPC_H
+#define PHP_WIN32_IPC_H 1
+
+#include "php.h"
+
+typedef int key_t;
+
+PHPAPI key_t ftok(const char *path, int id);
+
+
+#endif /* PHP_WIN32_IPC_H */