From 3cd8e8109ba89970042cb35fd3f66a2aa0e0aedd Mon Sep 17 00:00:00 2001 From: Martyna Siejba Date: Thu, 19 Sep 2019 09:19:58 -0700 Subject: [PATCH] Allow function pointer builtins in constant initializers Summary: Allow use of function pointers and class method pointers in constant expressions. They are translated to `ResolveFunc` and `ResolveClsMethod` in bytecode, which protects them from being evaluated too early and to a wrong value. Reviewed By: periodic1236 Differential Revision: D16925419 fbshipit-source-id: 78ddf9891ead0a810e75def9b9bfaba435a523c1 --- .../src/client/ide_service/clientIdeIncremental.ml | 1 + hphp/hack/src/facts/facts_parser.ml | 6 +- hphp/hack/src/facts/facts_parser.mli | 2 + hphp/hack/src/facts/facts_parser.rs | 2 + hphp/hack/src/facts/rust_facts_ffi.rs | 3 + hphp/hack/src/facts/symbols/indexBuilder.ml | 1 + hphp/hack/src/hh_parse.ml | 16 ++++- hphp/hack/src/hh_single_compile.ml | 8 ++- hphp/hack/src/hh_single_type_check.ml | 6 ++ hphp/hack/src/hhbc/hhbc_options.ml | 14 +++++ hphp/hack/src/naming/naming.ml | 6 +- hphp/hack/src/options/globalOptions.ml | 7 +++ hphp/hack/src/options/globalOptions.mli | 5 ++ hphp/hack/src/options/parserOptions.ml | 10 +++- hphp/hack/src/oxidized/gen/global_options.rs | 3 +- hphp/hack/src/oxidized/gen/typing_defs.rs | 3 +- .../src/oxidized/manual/global_options_impl.rs | 1 + .../hack/src/parser/full_fidelity_parser_errors.ml | 70 +++++++++++++++------- hphp/hack/src/parser/lowerer_ffi.rs | 1 + hphp/hack/src/parser/parser_env.rs | 1 + .../src/parser/ppl/rust_ppl_class_rewriter_ffi.rs | 1 + hphp/hack/src/parser/rust_parser_ffi.rs | 2 + hphp/hack/src/server/serverConfig.ml | 2 + hphp/hack/test/rust/facts_parse.rs | 1 + .../allowed_func_ptr_builtins.php | 21 +++++++ .../allowed_func_ptr_builtins.php.exp | 1 + .../func_ptr_in_constants/custom_function.php | 11 ++++ .../func_ptr_in_constants/custom_function.php.exp | 6 ++ .../func_ptr_in_constants/disallowed/HH_FLAGS | 1 + .../disallowed/allowed_func_ptr_builtins.php | 21 +++++++ .../disallowed/allowed_func_ptr_builtins.php.exp | 8 +++ .../func_ptr_in_constants/meth_caller.php | 15 +++++ .../func_ptr_in_constants/meth_caller.php.exp | 6 ++ hphp/runtime/base/runtime-option.h | 1 + hphp/runtime/base/type-variant.cpp | 4 +- .../func-ptr/const-func-ptr-dynamic-include.php | 35 +++++++++++ .../const-func-ptr-dynamic-include.php.expect | 10 ++++ .../const-func-ptr-dynamic-include.php.norepo | 0 .../const-func-ptr-dynamic-include.php.opts | 1 + .../test/slow/func-ptr/const-func-ptr-include1.inc | 9 +++ .../test/slow/func-ptr/const-func-ptr-include2.inc | 9 +++ hphp/test/slow/func-ptr/const-func-ptr.php | 29 +++++++++ hphp/test/slow/func-ptr/const-func-ptr.php.expect | 4 ++ .../slow/func-ptr/const-func-ptr.php.hphp_opts | 1 + hphp/test/slow/func-ptr/const-func-ptr.php.opts | 1 + .../slow/func-ptr/func-ptr-in-const-disallowed.php | 13 ++++ .../func-ptr-in-const-disallowed.php.expectf | 12 ++++ .../func-ptr-in-const-disallowed.php.norepo | 0 .../func-ptr/func-ptr-in-const-disallowed.php.opts | 1 + .../func-ptr/func-ptr-in-const-disallowed1.php.inc | 9 +++ .../func-ptr/func-ptr-in-const-disallowed2.php.inc | 9 +++ .../func-ptr/func-ptr-in-const-disallowed3.php.inc | 11 ++++ .../func-ptr/func-ptr-in-const-disallowed4.php.inc | 11 ++++ hphp/test/slow/func-ptr/func-ptr-in-const.php | 28 +++++++++ .../slow/func-ptr/func-ptr-in-const.php.expect | 19 ++++++ .../slow/func-ptr/func-ptr-in-const.php.hphp_opts | 1 + hphp/test/slow/func-ptr/func-ptr-in-const.php.opts | 1 + 57 files changed, 447 insertions(+), 34 deletions(-) create mode 100644 hphp/hack/test/typecheck/func_ptr_in_constants/allowed_func_ptr_builtins.php create mode 100644 hphp/hack/test/typecheck/func_ptr_in_constants/allowed_func_ptr_builtins.php.exp create mode 100644 hphp/hack/test/typecheck/func_ptr_in_constants/custom_function.php create mode 100644 hphp/hack/test/typecheck/func_ptr_in_constants/custom_function.php.exp create mode 100644 hphp/hack/test/typecheck/func_ptr_in_constants/disallowed/HH_FLAGS create mode 100644 hphp/hack/test/typecheck/func_ptr_in_constants/disallowed/allowed_func_ptr_builtins.php create mode 100644 hphp/hack/test/typecheck/func_ptr_in_constants/disallowed/allowed_func_ptr_builtins.php.exp create mode 100644 hphp/hack/test/typecheck/func_ptr_in_constants/meth_caller.php create mode 100644 hphp/hack/test/typecheck/func_ptr_in_constants/meth_caller.php.exp create mode 100644 hphp/test/slow/func-ptr/const-func-ptr-dynamic-include.php create mode 100644 hphp/test/slow/func-ptr/const-func-ptr-dynamic-include.php.expect create mode 100644 hphp/test/slow/func-ptr/const-func-ptr-dynamic-include.php.norepo create mode 100644 hphp/test/slow/func-ptr/const-func-ptr-dynamic-include.php.opts create mode 100644 hphp/test/slow/func-ptr/const-func-ptr-include1.inc create mode 100644 hphp/test/slow/func-ptr/const-func-ptr-include2.inc create mode 100644 hphp/test/slow/func-ptr/const-func-ptr.php create mode 100644 hphp/test/slow/func-ptr/const-func-ptr.php.expect create mode 100644 hphp/test/slow/func-ptr/const-func-ptr.php.hphp_opts create mode 100644 hphp/test/slow/func-ptr/const-func-ptr.php.opts create mode 100644 hphp/test/slow/func-ptr/func-ptr-in-const-disallowed.php create mode 100644 hphp/test/slow/func-ptr/func-ptr-in-const-disallowed.php.expectf create mode 100644 hphp/test/slow/func-ptr/func-ptr-in-const-disallowed.php.norepo create mode 100644 hphp/test/slow/func-ptr/func-ptr-in-const-disallowed.php.opts create mode 100644 hphp/test/slow/func-ptr/func-ptr-in-const-disallowed1.php.inc create mode 100644 hphp/test/slow/func-ptr/func-ptr-in-const-disallowed2.php.inc create mode 100644 hphp/test/slow/func-ptr/func-ptr-in-const-disallowed3.php.inc create mode 100644 hphp/test/slow/func-ptr/func-ptr-in-const-disallowed4.php.inc create mode 100644 hphp/test/slow/func-ptr/func-ptr-in-const.php create mode 100644 hphp/test/slow/func-ptr/func-ptr-in-const.php.expect create mode 100644 hphp/test/slow/func-ptr/func-ptr-in-const.php.hphp_opts create mode 100644 hphp/test/slow/func-ptr/func-ptr-in-const.php.opts diff --git a/hphp/hack/src/client/ide_service/clientIdeIncremental.ml b/hphp/hack/src/client/ide_service/clientIdeIncremental.ml index 727dd0b8472..8409cec6768 100644 --- a/hphp/hack/src/client/ide_service/clientIdeIncremental.ml +++ b/hphp/hack/src/client/ide_service/clientIdeIncremental.ml @@ -117,6 +117,7 @@ let compute_fileinfo_for_path (env : ServerEnv.env) (path : Relative_path.t) : ~disable_legacy_soft_typehints:false ~allow_new_attribute_syntax:false ~disable_legacy_attribute_syntax:false + ~disallow_func_ptrs_in_constants:false ~filename:path ~text:contents in diff --git a/hphp/hack/src/facts/facts_parser.ml b/hphp/hack/src/facts/facts_parser.ml index b444f43cd7d..95f064a14cb 100644 --- a/hphp/hack/src/facts/facts_parser.ml +++ b/hphp/hack/src/facts/facts_parser.ml @@ -23,6 +23,7 @@ let extract_as_json_string ~(disable_legacy_soft_typehints : bool) ~(allow_new_attribute_syntax : bool) ~(disable_legacy_attribute_syntax : bool) + ~(disallow_func_ptrs_in_constants : bool) ~(filename : Relative_path.t) ~(text : string) = (* return empty string if file has syntax errors *) @@ -39,7 +40,8 @@ let extract_as_json_string Rust_facts_ffi.extract_as_json_ffi ( (bool2int php5_compat_mode lsl 0) lor (bool2int hhvm_compat_mode lsl 1) - lor (bool2int allow_new_attribute_syntax lsl 2) ) + lor (bool2int allow_new_attribute_syntax lsl 2) + lor (bool2int disallow_func_ptrs_in_constants lsl 3) ) filename text !mangle_xhp_mode @@ -58,6 +60,7 @@ let from_text ~(disable_legacy_soft_typehints : bool) ~(allow_new_attribute_syntax : bool) ~(disable_legacy_attribute_syntax : bool) + ~(disallow_func_ptrs_in_constants : bool) ~(filename : Relative_path.t) ~(text : string) = Option.bind @@ -68,6 +71,7 @@ let from_text ~disable_legacy_soft_typehints ~allow_new_attribute_syntax ~disable_legacy_attribute_syntax + ~disallow_func_ptrs_in_constants ~filename ~text |> Option.map ~f:Hh_json.json_of_string ) diff --git a/hphp/hack/src/facts/facts_parser.mli b/hphp/hack/src/facts/facts_parser.mli index 10b3ca7f4aa..a45d0cbf026 100644 --- a/hphp/hack/src/facts/facts_parser.mli +++ b/hphp/hack/src/facts/facts_parser.mli @@ -19,6 +19,7 @@ val extract_as_json_string : disable_legacy_soft_typehints:bool -> allow_new_attribute_syntax:bool -> disable_legacy_attribute_syntax:bool -> + disallow_func_ptrs_in_constants:bool -> filename:Relative_path.t -> text:string -> string option @@ -30,6 +31,7 @@ val from_text : disable_legacy_soft_typehints:bool -> allow_new_attribute_syntax:bool -> disable_legacy_attribute_syntax:bool -> + disallow_func_ptrs_in_constants:bool -> filename:Relative_path.t -> text:string -> Facts.facts option diff --git a/hphp/hack/src/facts/facts_parser.rs b/hphp/hack/src/facts/facts_parser.rs index 28c8bd35c12..470b5141d03 100644 --- a/hphp/hack/src/facts/facts_parser.rs +++ b/hphp/hack/src/facts/facts_parser.rs @@ -22,6 +22,7 @@ pub struct ExtractAsJsonOpts { pub php5_compat_mode: bool, pub hhvm_compat_mode: bool, pub allow_new_attribute_syntax: bool, + pub disallow_func_ptrs_in_constants: bool, pub filename: RelativePath, } @@ -40,6 +41,7 @@ pub fn from_text(text: &str, opts: ExtractAsJsonOpts) -> Option { hhvm_compat_mode: opts.hhvm_compat_mode, is_experimental_mode: is_experimental, allow_new_attribute_syntax: opts.allow_new_attribute_syntax, + disallow_func_ptrs_in_constants: opts.disallow_func_ptrs_in_constants, ..ParserEnv::default() }; let mut parser = FactsParser::make(&text, env); diff --git a/hphp/hack/src/facts/rust_facts_ffi.rs b/hphp/hack/src/facts/rust_facts_ffi.rs index 10c7645af99..604f0dccf5e 100644 --- a/hphp/hack/src/facts/rust_facts_ffi.rs +++ b/hphp/hack/src/facts/rust_facts_ffi.rs @@ -22,6 +22,7 @@ caml!(extract_as_json_ffi( ((1 << 0) & flags) != 0, // php5_compat_mode ((1 << 1) & flags) != 0, // hhvm_compat_mode ((1 << 2) & flags) != 0, // allow_new_attribute_syntax + ((1 << 3) & flags) != 0, // disallow_func_ptrs_in_constants RelativePath::from_ocamlvalue(&filename), ocaml::Str::from(text).as_str(), mangle_xhp.i32_val() != 0, @@ -32,6 +33,7 @@ fn extract_as_json_ffi0( php5_compat_mode: bool, hhvm_compat_mode: bool, allow_new_attribute_syntax: bool, + disallow_func_ptrs_in_constants: bool, filename: RelativePath, text: &str, mangle_xhp: bool, @@ -40,6 +42,7 @@ fn extract_as_json_ffi0( php5_compat_mode, hhvm_compat_mode, allow_new_attribute_syntax, + disallow_func_ptrs_in_constants, filename, }; // return empty string in case of failure (ambiguous because "" is not a valid JSON) diff --git a/hphp/hack/src/facts/symbols/indexBuilder.ml b/hphp/hack/src/facts/symbols/indexBuilder.ml index ab777538cb1..d7fa30922dc 100644 --- a/hphp/hack/src/facts/symbols/indexBuilder.ml +++ b/hphp/hack/src/facts/symbols/indexBuilder.ml @@ -56,6 +56,7 @@ let parse_one_file ~(path : Relative_path.t) : si_capture = ~disable_legacy_soft_typehints:false ~allow_new_attribute_syntax:false ~disable_legacy_attribute_syntax:false + ~disallow_func_ptrs_in_constants:false ~filename:path ~text in diff --git a/hphp/hack/src/hh_parse.ml b/hphp/hack/src/hh_parse.ml index 34a7657ecd9..965f316e909 100644 --- a/hphp/hack/src/hh_parse.ml +++ b/hphp/hack/src/hh_parse.ml @@ -72,6 +72,7 @@ module FullFidelityParseArgs = struct const_static_props: bool; abstract_static_props: bool; disable_halt_compiler: bool; + disallow_func_ptrs_in_constants: bool; } let make @@ -108,7 +109,8 @@ module FullFidelityParseArgs = struct const_default_func_args const_static_props abstract_static_props - disable_halt_compiler = + disable_halt_compiler + disallow_func_ptrs_in_constants = { full_fidelity_json; full_fidelity_dot; @@ -144,6 +146,7 @@ module FullFidelityParseArgs = struct const_static_props; abstract_static_props; disable_halt_compiler; + disallow_func_ptrs_in_constants; } let parse_args () = @@ -196,6 +199,7 @@ module FullFidelityParseArgs = struct let const_static_props = ref false in let abstract_static_props = ref false in let disable_halt_compiler = ref false in + let disallow_func_ptrs_in_constants = ref false in let options = [ (* modes *) @@ -333,6 +337,10 @@ No errors are filtered out." ( "--disable-halt-compiler", Arg.Set disable_halt_compiler, "Disable using PHP __halt_compiler()" ); + ( "--disallow-func-ptrs-in-constants", + Arg.Set disallow_func_ptrs_in_constants, + "Disallow use of HH\\fun and HH\\class_meth in constants and constant initializers" + ); ] in Arg.parse options push_file usage; @@ -388,6 +396,7 @@ No errors are filtered out." !const_static_props !abstract_static_props !disable_halt_compiler + !disallow_func_ptrs_in_constants end open FullFidelityParseArgs @@ -450,6 +459,11 @@ let handle_existing_file args filename = let popt = ParserOptions.with_disable_halt_compiler popt args.disable_halt_compiler in + let popt = + ParserOptions.with_disallow_func_ptrs_in_constants + popt + args.disallow_func_ptrs_in_constants + in (* Parse with the full fidelity parser *) let file = Relative_path.create Relative_path.Dummy filename in let source_text = SourceText.from_file file in diff --git a/hphp/hack/src/hh_single_compile.ml b/hphp/hack/src/hh_single_compile.ml index ca5b01f7d9f..fb5ada80377 100644 --- a/hphp/hack/src/hh_single_compile.ml +++ b/hphp/hack/src/hh_single_compile.ml @@ -379,10 +379,10 @@ let convert_to_tast ast = (* Ignore these errors to match legacy AST behavior *) | 2086 (* Naming.MethodNeedsVisibility *) - + | 2102 (* Naming.UnsupportedTraitUseAs *) - + | 2103 (* Naming.UnsupportedInsteadOf *) -> acc | _ (* Emit fatal parse otherwise *) -> @@ -464,6 +464,7 @@ let extract_facts ~filename text = ~disable_legacy_soft_typehints:(disable_legacy_soft_typehints co) ~allow_new_attribute_syntax:(allow_new_attribute_syntax co) ~disable_legacy_attribute_syntax:(disable_legacy_attribute_syntax co) + ~disallow_func_ptrs_in_constants:(disallow_func_ptrs_in_constants co) ~filename ~text |> Option.value ~default:""); @@ -518,7 +519,8 @@ let make_popt () = ~disallow_silence:false ~const_static_props:(const_static_props co) ~abstract_static_props:(abstract_static_props co) - ~disable_unset_class_const:(disable_unset_class_const co)) + ~disable_unset_class_const:(disable_unset_class_const co) + ~disallow_func_ptrs_in_constants:(disallow_func_ptrs_in_constants co)) let process_single_source_unit compiler_options handle_output handle_exception filename source_text = diff --git a/hphp/hack/src/hh_single_type_check.ml b/hphp/hack/src/hh_single_type_check.ml index 71e43938a67..e3cf1a3d572 100644 --- a/hphp/hack/src/hh_single_type_check.ml +++ b/hphp/hack/src/hh_single_type_check.ml @@ -225,6 +225,7 @@ let parse_options () = let glean_hostname = ref (GleanOptions.hostname GlobalOptions.default) in let glean_port = ref (GleanOptions.port GlobalOptions.default) in let glean_reponame = ref (GleanOptions.reponame GlobalOptions.default) in + let disallow_func_ptrs_in_constants = ref false in let options = [ ("--ai", Arg.String set_ai, " Run the abstract interpreter (Zoncolan)"); @@ -506,6 +507,10 @@ let parse_options () = ( "--glean-reponame", Arg.String (fun str -> glean_reponame := str), "glean repo name" ); + ( "--disallow-func-ptrs-in-constants", + Arg.Set disallow_func_ptrs_in_constants, + "Disallow use of HH\\fun and HH\\class_meth in constants and constant initializers" + ); ] in let options = Arg.align ~limit:25 options in @@ -569,6 +574,7 @@ let parse_options () = ~po_abstract_static_props:!abstract_static_props ~po_disable_unset_class_const:!disable_unset_class_const ~po_disable_halt_compiler:!disable_halt_compiler + ~po_disallow_func_ptrs_in_constants:!disallow_func_ptrs_in_constants ~tco_check_attribute_locations:true ~glean_service:!glean_service ~glean_hostname:!glean_hostname diff --git a/hphp/hack/src/hhbc/hhbc_options.ml b/hphp/hack/src/hhbc/hhbc_options.ml index 088e51b0531..6a0ca81ff8a 100644 --- a/hphp/hack/src/hhbc/hhbc_options.ml +++ b/hphp/hack/src/hhbc/hhbc_options.ml @@ -57,6 +57,7 @@ type t = { option_const_static_props: bool; option_abstract_static_props: bool; option_disable_unset_class_const: bool; + option_disallow_func_ptrs_in_constants: bool; } let default = @@ -109,6 +110,7 @@ let default = option_const_static_props = false; option_abstract_static_props = false; option_disable_unset_class_const = false; + option_disallow_func_ptrs_in_constants = false; } let constant_folding o = o.option_constant_folding @@ -209,6 +211,9 @@ let abstract_static_props o = o.option_abstract_static_props let disable_unset_class_const o = o.option_disable_unset_class_const +let disallow_func_ptrs_in_constants o = + o.option_disallow_func_ptrs_in_constants + let to_string o = let dynamic_invokes = String.concat ~sep:", " (SSet.elements (dynamic_invoke_functions o)) @@ -283,6 +288,8 @@ let to_string o = Printf.sprintf "abstract_static_props: %B" @@ abstract_static_props o; Printf.sprintf "disable_unset_class_const: %B" @@ disable_unset_class_const o; + Printf.sprintf "disallow_func_ptrs_in_constants: %B" + @@ disallow_func_ptrs_in_constants o; ] let as_bool s = @@ -390,6 +397,8 @@ let set_option options name value = { options with option_abstract_static_props = as_bool value } | "hhvm.lang.disableunsetclassconst" -> { options with option_disable_unset_class_const = as_bool value } + | "hhvm.lang.disallow_func_ptrs_in_constants" -> + { options with option_disallow_func_ptrs_in_constants = as_bool value } | _ -> options let get_value_from_config_ config key = @@ -592,6 +601,11 @@ let value_setters = get_value_from_config_int @@ (fun opts v -> { opts with option_disable_unset_class_const = v = 1 }) ); + ( set_value + "hhvm.hack.lang.disallow_func_ptrs_in_constants" + get_value_from_config_int + @@ fun opts v -> + { opts with option_disallow_func_ptrs_in_constants = v = 1 } ); ] let extract_config_options_from_json ~init config_json = diff --git a/hphp/hack/src/naming/naming.ml b/hphp/hack/src/naming/naming.ml index 5fa631b74aa..e65fe764ef3 100644 --- a/hphp/hack/src/naming/naming.ml +++ b/hphp/hack/src/naming/naming.ml @@ -1687,8 +1687,10 @@ module Make (GetLocals : GetLocals) = struct (* Only check the values because shape field names are always legal *) List.iter fdl ~f:(fun (_, e) -> check_constant_expr env e) | Aast.Call (_, (_, Aast.Id (_, cn)), _, el, uel) - when cn = SN.SpecialFunctions.tuple -> - (* Tuples are not really function calls, they are just parsed that way*) + when cn = SN.SpecialFunctions.fun_ + || cn = SN.SpecialFunctions.class_meth + (* Tuples are not really function calls, they are just parsed that way*) + || cn = SN.SpecialFunctions.tuple -> arg_unpack_unexpected uel; List.iter el ~f:(check_constant_expr env) | Aast.Collection (id, _, l) -> diff --git a/hphp/hack/src/options/globalOptions.ml b/hphp/hack/src/options/globalOptions.ml index 0ca84cb61cb..20d61c9f14a 100644 --- a/hphp/hack/src/options/globalOptions.ml +++ b/hphp/hack/src/options/globalOptions.ml @@ -119,6 +119,7 @@ type t = { glean_hostname: string; glean_port: int; glean_reponame: string; + po_disallow_func_ptrs_in_constants: bool; } [@@deriving show] @@ -278,6 +279,7 @@ let default = glean_hostname = ""; glean_port = 0; glean_reponame = "www.autocomplete"; + po_disallow_func_ptrs_in_constants = false; } let make @@ -374,6 +376,8 @@ let make ?(glean_hostname = default.glean_hostname) ?(glean_port = default.glean_port) ?(glean_reponame = default.glean_reponame) + ?(po_disallow_func_ptrs_in_constants = + default.po_disallow_func_ptrs_in_constants) () = { tco_safe_array; @@ -456,6 +460,7 @@ let make glean_hostname; glean_port; glean_reponame; + po_disallow_func_ptrs_in_constants; } let tco_safe_array t = t.tco_safe_array @@ -632,3 +637,5 @@ let glean_reponame t = t.glean_reponame let set_infer_missing t w = { t with tco_infer_missing = w } let po_parser_errors_only t = t.po_parser_errors_only + +let po_disallow_func_ptrs_in_constants t = t.po_disallow_func_ptrs_in_constants diff --git a/hphp/hack/src/options/globalOptions.mli b/hphp/hack/src/options/globalOptions.mli index 8c751663450..d71ce40b4a2 100644 --- a/hphp/hack/src/options/globalOptions.mli +++ b/hphp/hack/src/options/globalOptions.mli @@ -279,6 +279,8 @@ type t = { glean_port: int; (* Reponame used for glean connection, default to "www.autocomplete" *) glean_reponame: string; + (* Flag to disallow HH\fun and HH\class_meth in constants and constant initializers *) + po_disallow_func_ptrs_in_constants: bool; } [@@deriving show] @@ -362,6 +364,7 @@ val make : ?glean_hostname:string -> ?glean_port:int -> ?glean_reponame:string -> + ?po_disallow_func_ptrs_in_constants:bool -> unit -> t @@ -552,3 +555,5 @@ val glean_hostname : t -> string val glean_port : t -> int val glean_reponame : t -> string + +val po_disallow_func_ptrs_in_constants : t -> bool diff --git a/hphp/hack/src/options/parserOptions.ml b/hphp/hack/src/options/parserOptions.ml index a9314c8dd9f..632ce0d8717 100644 --- a/hphp/hack/src/options/parserOptions.ml +++ b/hphp/hack/src/options/parserOptions.ml @@ -92,6 +92,12 @@ let disable_halt_compiler = GlobalOptions.po_disable_halt_compiler let with_disable_halt_compiler po b = { po with GlobalOptions.po_disable_halt_compiler = b } +let disallow_func_ptrs_in_constants = + GlobalOptions.po_disallow_func_ptrs_in_constants + +let with_disallow_func_ptrs_in_constants po b = + { po with GlobalOptions.po_disallow_func_ptrs_in_constants = b } + let make ~auto_namespace_map ~codegen @@ -109,7 +115,8 @@ let make ~const_static_props ~abstract_static_props ~disable_unset_class_const - ~disable_halt_compiler = + ~disable_halt_compiler + ~disallow_func_ptrs_in_constants = GlobalOptions. { default with @@ -131,4 +138,5 @@ let make po_abstract_static_props = abstract_static_props; po_disable_unset_class_const = disable_unset_class_const; po_disable_halt_compiler = disable_halt_compiler; + po_disallow_func_ptrs_in_constants = disallow_func_ptrs_in_constants; } diff --git a/hphp/hack/src/oxidized/gen/global_options.rs b/hphp/hack/src/oxidized/gen/global_options.rs index 7bfeff5e5e8..bf6623c7d30 100644 --- a/hphp/hack/src/oxidized/gen/global_options.rs +++ b/hphp/hack/src/oxidized/gen/global_options.rs @@ -3,7 +3,7 @@ // This source code is licensed under the MIT license found in the // LICENSE file in the "hack" directory of this source tree. // -// @generated SignedSource<<49418e10683f23be7c52e944d5fbc9dc>> +// @generated SignedSource<<1476d8690b710111a6ae0fec71007718>> // // To regenerate this file, run: // hphp/hack/src/oxidized/regen.sh @@ -95,4 +95,5 @@ pub struct GlobalOptions { pub po_disable_unset_class_const: bool, pub po_parser_errors_only: bool, pub tco_check_attribute_locations: bool, + pub po_disallow_func_ptrs_in_constants: bool, } diff --git a/hphp/hack/src/oxidized/gen/typing_defs.rs b/hphp/hack/src/oxidized/gen/typing_defs.rs index e9d749172dd..0ca6f1020c9 100644 --- a/hphp/hack/src/oxidized/gen/typing_defs.rs +++ b/hphp/hack/src/oxidized/gen/typing_defs.rs @@ -3,7 +3,7 @@ // This source code is licensed under the MIT license found in the // LICENSE file in the "hack" directory of this source tree. // -// @generated SignedSource<<4cd424616336ab71964ca8d198b899ef>> +// @generated SignedSource<> // // To regenerate this file, run: // hphp/hack/src/oxidized/regen.sh @@ -130,7 +130,6 @@ pub enum FunTparamsKind { pub struct FunType { pub pos: pos::Pos, pub deprecated: Option, - pub abstract_: bool, pub is_coroutine: bool, pub arity: FunArity, pub tparams: (Vec>, FunTparamsKind), diff --git a/hphp/hack/src/oxidized/manual/global_options_impl.rs b/hphp/hack/src/oxidized/manual/global_options_impl.rs index 2d1dec1e3f8..69bea1f7e1b 100644 --- a/hphp/hack/src/oxidized/manual/global_options_impl.rs +++ b/hphp/hack/src/oxidized/manual/global_options_impl.rs @@ -84,6 +84,7 @@ impl Default for GlobalOptions { po_disable_unset_class_const: false, po_parser_errors_only: false, tco_check_attribute_locations: false, + po_disallow_func_ptrs_in_constants: false, } } } diff --git a/hphp/hack/src/parser/full_fidelity_parser_errors.ml b/hphp/hack/src/parser/full_fidelity_parser_errors.ml index 45d52770a32..7ef619e20ed 100644 --- a/hphp/hack/src/parser/full_fidelity_parser_errors.ml +++ b/hphp/hack/src/parser/full_fidelity_parser_errors.ml @@ -4490,13 +4490,20 @@ module WithSyntax (Syntax : Syntax_sig.Syntax_S) = struct (syntax_to_list_no_separators clauses) | _ -> (names, errors) - let rec check_constant_expression errors node = + let rec check_constant_expression env errors node = (* __FUNTION_CREDENTIAL__ emits an object, so it cannot be used in a constant expression*) let not_function_credential token = let to_upper = String.uppercase (Token.text token) in String.compare to_upper "__FUNCTION_CREDENTIAL__" <> 0 in + let is_whitelisted_function receiver_token = + (not (ParserOptions.disallow_func_ptrs_in_constants env.parser_options)) + && SSet.mem + (Token.text receiver_token) + (SSet.of_list + [SN.SpecialFunctions.fun_; SN.SpecialFunctions.class_meth]) + in let is_namey token = match Token.kind token with | TokenKind.Name -> not_function_credential token @@ -4586,7 +4593,7 @@ module WithSyntax (Syntax : Syntax_sig.Syntax_S) = struct | Tilde -> true | _ -> false) -> - check_constant_expression errors prefix_unary_operand + check_constant_expression env errors prefix_unary_operand | BinaryExpression { binary_left_operand; @@ -4622,8 +4629,12 @@ module WithSyntax (Syntax : Syntax_sig.Syntax_S) = struct | QuestionColon -> true | _ -> false) -> - let errors = check_constant_expression errors binary_left_operand in - let errors = check_constant_expression errors binary_right_operand in + let errors = + check_constant_expression env errors binary_left_operand + in + let errors = + check_constant_expression env errors binary_right_operand + in errors | ConditionalExpression { @@ -4632,12 +4643,12 @@ module WithSyntax (Syntax : Syntax_sig.Syntax_S) = struct conditional_alternative; _; } -> - let errors = check_constant_expression errors conditional_test in + let errors = check_constant_expression env errors conditional_test in let errors = - check_constant_expression errors conditional_consequence + check_constant_expression env errors conditional_consequence in let errors = - check_constant_expression errors conditional_alternative + check_constant_expression env errors conditional_alternative in errors | SimpleInitializer @@ -4656,7 +4667,7 @@ module WithSyntax (Syntax : Syntax_sig.Syntax_S) = struct | SimpleInitializer { simple_initializer_value = e; _ } | ParenthesizedExpression { parenthesized_expression_expression = e; _ } -> - check_constant_expression errors e + check_constant_expression env errors e | CollectionLiteralExpression { collection_literal_name = @@ -4673,7 +4684,7 @@ module WithSyntax (Syntax : Syntax_sig.Syntax_S) = struct } when is_namey token -> syntax_to_list_no_separators lst - |> List.fold_left ~init:errors ~f:check_constant_expression + |> List.fold_left ~init:errors ~f:(check_constant_expression env) | TupleExpression { tuple_expression_items = lst; _ } | KeysetIntrinsicExpression { keyset_intrinsic_members = lst; _ } | VarrayIntrinsicExpression { varray_intrinsic_members = lst; _ } @@ -4684,12 +4695,12 @@ module WithSyntax (Syntax : Syntax_sig.Syntax_S) = struct | ArrayCreationExpression { array_creation_members = lst; _ } | ShapeExpression { shape_expression_fields = lst; _ } -> syntax_to_list_no_separators lst - |> List.fold_left ~init:errors ~f:check_constant_expression + |> List.fold_left ~init:errors ~f:(check_constant_expression env) | ElementInitializer { element_key = n; element_value = v; _ } | FieldInitializer { field_initializer_name = n; field_initializer_value = v; _ } -> - let errors = check_constant_expression errors n in - let errors = check_constant_expression errors v in + let errors = check_constant_expression env errors n in + let errors = check_constant_expression env errors v in errors | ScopeResolutionExpression { scope_resolution_qualifier; scope_resolution_name; _ } @@ -4702,7 +4713,7 @@ module WithSyntax (Syntax : Syntax_sig.Syntax_S) = struct as_right_operand = { syntax = LikeTypeSpecifier _; _ }; _; } -> - check_constant_expression errors e + check_constant_expression env errors e | AsExpression { as_left_operand = e; @@ -4715,7 +4726,21 @@ module WithSyntax (Syntax : Syntax_sig.Syntax_S) = struct } when text s = SN.FB.cIncorrectType || text s = Utils.strip_ns SN.FB.cIncorrectType -> - check_constant_expression errors e + check_constant_expression env errors e + | FunctionCallExpression + { + function_call_receiver = { syntax = Token receiver_token; _ }; + function_call_argument_list; + _; + } -> + TokenKind.( + (match Token.kind receiver_token with + | Name when is_whitelisted_function receiver_token -> + syntax_to_list_no_separators function_call_argument_list + |> List.fold_left ~init:errors ~f:(check_constant_expression env) + | _ -> + make_error_from_node node SyntaxError.invalid_constant_initializer + :: errors)) | _ -> make_error_from_node node SyntaxError.invalid_constant_initializer :: errors @@ -4776,7 +4801,10 @@ module WithSyntax (Syntax : Syntax_sig.Syntax_S) = struct cd.constant_declarator_initializer in let errors = - check_constant_expression errors cd.constant_declarator_initializer + check_constant_expression + env + errors + cd.constant_declarator_initializer in let errors = produce_error @@ -5026,7 +5054,7 @@ module WithSyntax (Syntax : Syntax_sig.Syntax_S) = struct errors | _ -> errors - let enumerator_errors node errors = + let enumerator_errors env node errors = match syntax node with | Enumerator { enumerator_name = name; enumerator_value = value; _ } -> let errors = @@ -5036,7 +5064,7 @@ module WithSyntax (Syntax : Syntax_sig.Syntax_S) = struct else errors in - let errors = check_constant_expression errors value in + let errors = check_constant_expression env errors value in errors | _ -> errors @@ -5259,7 +5287,7 @@ module WithSyntax (Syntax : Syntax_sig.Syntax_S) = struct match syntax node with | ParameterDeclaration { parameter_default_value; _ } when ParserOptions.const_default_func_args env.parser_options -> - check_constant_expression errors parameter_default_value + check_constant_expression env errors parameter_default_value | _ -> errors let find_syntax_errors env = @@ -5454,7 +5482,7 @@ module WithSyntax (Syntax : Syntax_sig.Syntax_S) = struct let errors = enum_decl_errors node errors in (trait_require_clauses, names, errors) | Enumerator _ -> - let errors = enumerator_errors node errors in + let errors = enumerator_errors env node errors in (trait_require_clauses, names, errors) | PostfixUnaryExpression _ | BinaryExpression _ @@ -5474,10 +5502,10 @@ module WithSyntax (Syntax : Syntax_sig.Syntax_S) = struct SyntaxError.parent_static_prop_decl init in - let errors = check_constant_expression errors init in + let errors = check_constant_expression env errors init in (trait_require_clauses, names, errors) | XHPClassAttribute { xhp_attribute_decl_initializer = init; _ } -> - let errors = check_constant_expression errors init in + let errors = check_constant_expression env errors init in (trait_require_clauses, names, errors) | SoftTypeSpecifier _ -> let errors = diff --git a/hphp/hack/src/parser/lowerer_ffi.rs b/hphp/hack/src/parser/lowerer_ffi.rs index 2798de849a5..73be039c9b0 100644 --- a/hphp/hack/src/parser/lowerer_ffi.rs +++ b/hphp/hack/src/parser/lowerer_ffi.rs @@ -52,6 +52,7 @@ caml_raise!(parse_and_lower_from_text, |ocaml_source_text|, , { php5_compat_mode : false, codegen : false, allow_new_attribute_syntax : false, + disallow_func_ptrs_in_constants: false, }; let mut parser = PositionedSyntaxParser::make(&source_text, env); diff --git a/hphp/hack/src/parser/parser_env.rs b/hphp/hack/src/parser/parser_env.rs index 572bdc9b843..b1568023a69 100644 --- a/hphp/hack/src/parser/parser_env.rs +++ b/hphp/hack/src/parser/parser_env.rs @@ -11,4 +11,5 @@ pub struct ParserEnv { pub hhvm_compat_mode: bool, pub php5_compat_mode: bool, pub allow_new_attribute_syntax: bool, + pub disallow_func_ptrs_in_constants: bool, } diff --git a/hphp/hack/src/parser/ppl/rust_ppl_class_rewriter_ffi.rs b/hphp/hack/src/parser/ppl/rust_ppl_class_rewriter_ffi.rs index 8ff503173b4..58ea2b5ed99 100644 --- a/hphp/hack/src/parser/ppl/rust_ppl_class_rewriter_ffi.rs +++ b/hphp/hack/src/parser/ppl/rust_ppl_class_rewriter_ffi.rs @@ -42,6 +42,7 @@ caml!(parse_and_rewrite_ppl_classes, |ocaml_source_text|, , { php5_compat_mode : false, codegen : false, allow_new_attribute_syntax : false, + disallow_func_ptrs_in_constants : false, }; let mut parser = PositionedSyntaxParser::make(&source_text, env); diff --git a/hphp/hack/src/parser/rust_parser_ffi.rs b/hphp/hack/src/parser/rust_parser_ffi.rs index fbbb8b195f9..b3c3f9834ae 100644 --- a/hphp/hack/src/parser/rust_parser_ffi.rs +++ b/hphp/hack/src/parser/rust_parser_ffi.rs @@ -90,12 +90,14 @@ macro_rules! parse { let codegen = bool_field(&opts, 3); let allow_new_attribute_syntax = bool_field(&opts, 4); let leak_rust_tree = bool_field(&opts, 5); + let disallow_func_ptrs_in_constants = bool_field(&opts, 6); let env = ParserEnv { is_experimental_mode, hhvm_compat_mode, php5_compat_mode, codegen, allow_new_attribute_syntax, + disallow_func_ptrs_in_constants, }; // Note: Determining the current thread size cannot be done portably, diff --git a/hphp/hack/src/server/serverConfig.ml b/hphp/hack/src/server/serverConfig.ml index e5713433c5d..5c1961d0765 100644 --- a/hphp/hack/src/server/serverConfig.ml +++ b/hphp/hack/src/server/serverConfig.ml @@ -396,6 +396,8 @@ let load config_filename options = ?glean_hostname:(string_opt "glean_hostname" config) ?glean_port:(int_opt "glean_port" config) ?glean_reponame:(string_opt "glean_reponame" config) + ?po_disallow_func_ptrs_in_constants: + (bool_opt "disallow_func_ptrs_in_constants" config) () in Errors.ignored_fixme_codes := GlobalOptions.ignored_fixme_codes global_opts; diff --git a/hphp/hack/test/rust/facts_parse.rs b/hphp/hack/test/rust/facts_parse.rs index a7c1845d884..1e9f98aa448 100644 --- a/hphp/hack/test/rust/facts_parse.rs +++ b/hphp/hack/test/rust/facts_parse.rs @@ -38,6 +38,7 @@ fn parse(file_path: String, parse_only: bool) { php5_compat_mode: true, hhvm_compat_mode: true, allow_new_attribute_syntax: false, + disallow_func_ptrs_in_constants: false, filename: path, }; diff --git a/hphp/hack/test/typecheck/func_ptr_in_constants/allowed_func_ptr_builtins.php b/hphp/hack/test/typecheck/func_ptr_in_constants/allowed_func_ptr_builtins.php new file mode 100644 index 00000000000..9d79268e98c --- /dev/null +++ b/hphp/hack/test/typecheck/func_ptr_in_constants/allowed_func_ptr_builtins.php @@ -0,0 +1,21 @@ + fun('foo'), + 'meth' => class_meth(Cls::class, 'meth'), + ]; +} + +abstract class B { + const const_arr = dict[ + 'foo' => fun('foo'), + 'meth' => class_meth(Cls::class, 'meth'), + ]; +} diff --git a/hphp/hack/test/typecheck/func_ptr_in_constants/allowed_func_ptr_builtins.php.exp b/hphp/hack/test/typecheck/func_ptr_in_constants/allowed_func_ptr_builtins.php.exp new file mode 100644 index 00000000000..4269126fceb --- /dev/null +++ b/hphp/hack/test/typecheck/func_ptr_in_constants/allowed_func_ptr_builtins.php.exp @@ -0,0 +1 @@ +No errors diff --git a/hphp/hack/test/typecheck/func_ptr_in_constants/custom_function.php b/hphp/hack/test/typecheck/func_ptr_in_constants/custom_function.php new file mode 100644 index 00000000000..6e667de835a --- /dev/null +++ b/hphp/hack/test/typecheck/func_ptr_in_constants/custom_function.php @@ -0,0 +1,11 @@ + fun('foo'), + 'meth' => class_meth(Cls::class, 'meth'), + ]; +} + +abstract class B { + const const_arr = dict[ + 'foo' => fun('foo'), + 'meth' => class_meth(Cls::class, 'meth'), + ]; +} diff --git a/hphp/hack/test/typecheck/func_ptr_in_constants/disallowed/allowed_func_ptr_builtins.php.exp b/hphp/hack/test/typecheck/func_ptr_in_constants/disallowed/allowed_func_ptr_builtins.php.exp new file mode 100644 index 00000000000..d4bff350135 --- /dev/null +++ b/hphp/hack/test/typecheck/func_ptr_in_constants/disallowed/allowed_func_ptr_builtins.php.exp @@ -0,0 +1,8 @@ +File "allowed_func_ptr_builtins.php", line 11, characters 14-22: +Invalid expression in constant initializer (Parsing[1002]) +File "allowed_func_ptr_builtins.php", line 12, characters 15-43: +Invalid expression in constant initializer (Parsing[1002]) +File "allowed_func_ptr_builtins.php", line 18, characters 14-22: +Invalid expression in constant initializer (Parsing[1002]) +File "allowed_func_ptr_builtins.php", line 19, characters 15-43: +Invalid expression in constant initializer (Parsing[1002]) diff --git a/hphp/hack/test/typecheck/func_ptr_in_constants/meth_caller.php b/hphp/hack/test/typecheck/func_ptr_in_constants/meth_caller.php new file mode 100644 index 00000000000..c34a8699462 --- /dev/null +++ b/hphp/hack/test/typecheck/func_ptr_in_constants/meth_caller.php @@ -0,0 +1,15 @@ + fun('foo'), + 'meth' => class_meth(Cls::class, 'meth'), + ]; + + const arr2 = dict[ + 'foo' => fun('foo'), + 'meth' => class_meth(Cls::class, 'meth'), + ]; +} + +<<__EntryPoint>> +function main_constant_functions() { + $count = __hhvm_intrinsics\apc_fetch_no_check('count'); + if ($count === false) { + $count = 0; + } + if ($count < 2) { + ++$count; + apc_store('count', $count); + + $path = __DIR__.'/const-func-ptr-include'.$count.'.inc'; + include $path; + + echo "===== run with include$count ======\n"; + + var_dump((A::$arr['foo'])(5)); + var_dump((A::$arr['meth'])(5)); + var_dump((A::arr2['foo'])(5)); + var_dump((A::arr2['meth'])(5)); + } +} diff --git a/hphp/test/slow/func-ptr/const-func-ptr-dynamic-include.php.expect b/hphp/test/slow/func-ptr/const-func-ptr-dynamic-include.php.expect new file mode 100644 index 00000000000..7b03db7b46a --- /dev/null +++ b/hphp/test/slow/func-ptr/const-func-ptr-dynamic-include.php.expect @@ -0,0 +1,10 @@ +===== run with include1 ====== +string(22) "function from include1" +string(20) "method from include1" +string(22) "function from include1" +string(20) "method from include1" +===== run with include2 ====== +string(22) "function from include2" +string(20) "method from include2" +string(22) "function from include2" +string(20) "method from include2" diff --git a/hphp/test/slow/func-ptr/const-func-ptr-dynamic-include.php.norepo b/hphp/test/slow/func-ptr/const-func-ptr-dynamic-include.php.norepo new file mode 100644 index 00000000000..e69de29bb2d diff --git a/hphp/test/slow/func-ptr/const-func-ptr-dynamic-include.php.opts b/hphp/test/slow/func-ptr/const-func-ptr-dynamic-include.php.opts new file mode 100644 index 00000000000..99f86337720 --- /dev/null +++ b/hphp/test/slow/func-ptr/const-func-ptr-dynamic-include.php.opts @@ -0,0 +1 @@ +--count=2 -vEval.EmitFuncPointers=1 -vEval.EmitClsMethPointers=1 diff --git a/hphp/test/slow/func-ptr/const-func-ptr-include1.inc b/hphp/test/slow/func-ptr/const-func-ptr-include1.inc new file mode 100644 index 00000000000..7bdbcdb4722 --- /dev/null +++ b/hphp/test/slow/func-ptr/const-func-ptr-include1.inc @@ -0,0 +1,9 @@ + fun('foo'), + 'meth' => class_meth(Cls::class, 'meth'), + ]; + + const arr2 = dict[ + 'foo' => fun('foo'), + 'meth' => class_meth(Cls::class, 'meth'), + ]; +} + +<<__EntryPoint>> +function main_constant_functions() { + var_dump((A::$arr['foo'])(5)); + var_dump((A::$arr['meth'])(5)); + var_dump((A::arr2['foo'])(5)); + var_dump((A::arr2['meth'])(5)); +} diff --git a/hphp/test/slow/func-ptr/const-func-ptr.php.expect b/hphp/test/slow/func-ptr/const-func-ptr.php.expect new file mode 100644 index 00000000000..c2720780196 --- /dev/null +++ b/hphp/test/slow/func-ptr/const-func-ptr.php.expect @@ -0,0 +1,4 @@ +string(3) "foo" +string(4) "meth" +string(3) "foo" +string(4) "meth" diff --git a/hphp/test/slow/func-ptr/const-func-ptr.php.hphp_opts b/hphp/test/slow/func-ptr/const-func-ptr.php.hphp_opts new file mode 100644 index 00000000000..5dd8dc46d39 --- /dev/null +++ b/hphp/test/slow/func-ptr/const-func-ptr.php.hphp_opts @@ -0,0 +1 @@ +-vRuntime.Eval.EmitFuncPointers=1 -vRuntime.Eval.EmitClsMethPointers=1 diff --git a/hphp/test/slow/func-ptr/const-func-ptr.php.opts b/hphp/test/slow/func-ptr/const-func-ptr.php.opts new file mode 100644 index 00000000000..822e36e6117 --- /dev/null +++ b/hphp/test/slow/func-ptr/const-func-ptr.php.opts @@ -0,0 +1 @@ +-vEval.EmitFuncPointers=1 -vEval.EmitClsMethPointers=1 diff --git a/hphp/test/slow/func-ptr/func-ptr-in-const-disallowed.php b/hphp/test/slow/func-ptr/func-ptr-in-const-disallowed.php new file mode 100644 index 00000000000..6911772ea32 --- /dev/null +++ b/hphp/test/slow/func-ptr/func-ptr-in-const-disallowed.php @@ -0,0 +1,13 @@ +> +function main() { + $count = __hhvm_intrinsics\apc_fetch_no_check('count'); + if ($count === false) $count = 0; + if ($count < 4) { + ++$count; + apc_store('count', $count); + echo "====================== $count =======================\n"; + require_once(__DIR__.'/func-ptr-in-const-disallowed'.$count.'.php.inc'); + } +} diff --git a/hphp/test/slow/func-ptr/func-ptr-in-const-disallowed.php.expectf b/hphp/test/slow/func-ptr/func-ptr-in-const-disallowed.php.expectf new file mode 100644 index 00000000000..54f118084dc --- /dev/null +++ b/hphp/test/slow/func-ptr/func-ptr-in-const-disallowed.php.expectf @@ -0,0 +1,12 @@ +====================== 1 ======================= + +Fatal error: Invalid expression in constant initializer in %s/func-ptr-in-const-disallowed1.php.inc on line 7 +====================== 2 ======================= + +Fatal error: Invalid expression in constant initializer in %s/func-ptr-in-const-disallowed2.php.inc on line 7 +====================== 3 ======================= + +Fatal error: Invalid expression in constant initializer in %s/func-ptr-in-const-disallowed3.php.inc on line 9 +====================== 4 ======================= + +Fatal error: Invalid expression in constant initializer in %s/func-ptr-in-const-disallowed4.php.inc on line 9 diff --git a/hphp/test/slow/func-ptr/func-ptr-in-const-disallowed.php.norepo b/hphp/test/slow/func-ptr/func-ptr-in-const-disallowed.php.norepo new file mode 100644 index 00000000000..e69de29bb2d diff --git a/hphp/test/slow/func-ptr/func-ptr-in-const-disallowed.php.opts b/hphp/test/slow/func-ptr/func-ptr-in-const-disallowed.php.opts new file mode 100644 index 00000000000..abd170d6069 --- /dev/null +++ b/hphp/test/slow/func-ptr/func-ptr-in-const-disallowed.php.opts @@ -0,0 +1 @@ +--count=4 -vEval.EmitFuncPointers=1 -vEval.EmitClsMethPointers=1 -vHack.Lang.DisallowFuncPtrsInConstants=true diff --git a/hphp/test/slow/func-ptr/func-ptr-in-const-disallowed1.php.inc b/hphp/test/slow/func-ptr/func-ptr-in-const-disallowed1.php.inc new file mode 100644 index 00000000000..a5ce4a897df --- /dev/null +++ b/hphp/test/slow/func-ptr/func-ptr-in-const-disallowed1.php.inc @@ -0,0 +1,9 @@ + fun('foo'), + ]; +} diff --git a/hphp/test/slow/func-ptr/func-ptr-in-const-disallowed2.php.inc b/hphp/test/slow/func-ptr/func-ptr-in-const-disallowed2.php.inc new file mode 100644 index 00000000000..c7998da0600 --- /dev/null +++ b/hphp/test/slow/func-ptr/func-ptr-in-const-disallowed2.php.inc @@ -0,0 +1,9 @@ + fun('foo'), + ]; +} diff --git a/hphp/test/slow/func-ptr/func-ptr-in-const-disallowed3.php.inc b/hphp/test/slow/func-ptr/func-ptr-in-const-disallowed3.php.inc new file mode 100644 index 00000000000..b7dca62f56f --- /dev/null +++ b/hphp/test/slow/func-ptr/func-ptr-in-const-disallowed3.php.inc @@ -0,0 +1,11 @@ + class_meth(Cls::class, 'meth'), + ]; +} diff --git a/hphp/test/slow/func-ptr/func-ptr-in-const-disallowed4.php.inc b/hphp/test/slow/func-ptr/func-ptr-in-const-disallowed4.php.inc new file mode 100644 index 00000000000..8d1f8bf1bc6 --- /dev/null +++ b/hphp/test/slow/func-ptr/func-ptr-in-const-disallowed4.php.inc @@ -0,0 +1,11 @@ + class_meth(Cls::class, 'meth'), + ]; +} diff --git a/hphp/test/slow/func-ptr/func-ptr-in-const.php b/hphp/test/slow/func-ptr/func-ptr-in-const.php new file mode 100644 index 00000000000..7ea1baadcd1 --- /dev/null +++ b/hphp/test/slow/func-ptr/func-ptr-in-const.php @@ -0,0 +1,28 @@ + fun('foo'), + 'meth' => class_meth(Cls::class, 'meth'), + ]; +} + +class B { + const f = dict[ + 'foo' => fun('foo'), + 'meth' => class_meth(Cls::class, 'meth'), + ]; +} + +<<__EntryPoint>> +function main() { + var_dump(A::$arr); + var_dump(B::f); + var_dump(A::$arr === B::f); +} diff --git a/hphp/test/slow/func-ptr/func-ptr-in-const.php.expect b/hphp/test/slow/func-ptr/func-ptr-in-const.php.expect new file mode 100644 index 00000000000..6420c907778 --- /dev/null +++ b/hphp/test/slow/func-ptr/func-ptr-in-const.php.expect @@ -0,0 +1,19 @@ +dict(2) { + ["foo"]=> + function(foo) + ["meth"]=> +classMeth{ + class(Cls) + function(meth) +} +} +dict(2) { + ["foo"]=> + function(foo) + ["meth"]=> +classMeth{ + class(Cls) + function(meth) +} +} +bool(true) diff --git a/hphp/test/slow/func-ptr/func-ptr-in-const.php.hphp_opts b/hphp/test/slow/func-ptr/func-ptr-in-const.php.hphp_opts new file mode 100644 index 00000000000..5dd8dc46d39 --- /dev/null +++ b/hphp/test/slow/func-ptr/func-ptr-in-const.php.hphp_opts @@ -0,0 +1 @@ +-vRuntime.Eval.EmitFuncPointers=1 -vRuntime.Eval.EmitClsMethPointers=1 diff --git a/hphp/test/slow/func-ptr/func-ptr-in-const.php.opts b/hphp/test/slow/func-ptr/func-ptr-in-const.php.opts new file mode 100644 index 00000000000..822e36e6117 --- /dev/null +++ b/hphp/test/slow/func-ptr/func-ptr-in-const.php.opts @@ -0,0 +1 @@ +-vEval.EmitFuncPointers=1 -vEval.EmitClsMethPointers=1 -- 2.11.4.GIT