Remove support for <<__UNSAFE_Construct>> in the type checker
[hiphop-php.git] / hphp / hack / src / server / serverConfig.ml
blobd8a7e0858c2daa273e9bca231462ea36291d5c0d
1 (**
2 * Copyright (c) 2015, Facebook, Inc.
3 * All rights reserved.
5 * This source code is licensed under the MIT license found in the
6 * LICENSE file in the "hack" directory of this source tree.
8 *)
10 (**
11 * Parses and gathers information from the .hhconfig in the repo.
14 open Core_kernel
15 open Config_file.Getters
16 open Reordered_argument_collections
18 type t = {
19 version : string option;
21 load_script_timeout : int; (* in seconds *)
23 (* Configures only the workers. Workers can have more relaxed GC configs as
24 * they are short-lived processes *)
25 gc_control : Gc.control;
26 sharedmem_config : SharedMem.config;
27 tc_options : TypecheckerOptions.t;
28 parser_options : ParserOptions.t;
29 formatter_override : Path.t option;
30 config_hash : string option;
31 (* A list of regexps for paths to ignore *)
32 ignored_paths : string list;
33 (* A list of extra paths to search for declarations *)
34 extra_paths : Path.t list;
35 (* A list of regexps for paths to ignore for typechecking coroutines *)
36 coroutine_whitelist_paths : string list;
39 let filename = Relative_path.from_root Config_file.file_path_relative_to_repo_root
41 let is_compatible c1 c2 =
42 (* This comparison can eventually be made more complex; we may not always
43 * need to restart hh_server, e.g. changing the path to the load script
44 * is immaterial*)
45 c1 = c2
47 let make_gc_control config =
48 let { Gc.Control.minor_heap_size; space_overhead; _} = GlobalConfig.gc_control in
49 let minor_heap_size =
50 int_ "gc_minor_heap_size" ~default:minor_heap_size config in
51 let space_overhead =
52 int_ "gc_space_overhead" ~default:space_overhead config in
53 { GlobalConfig.gc_control with Gc.Control.minor_heap_size; space_overhead; }
55 let make_sharedmem_config config options local_config =
56 let { SharedMem.
57 global_size;
58 heap_size;
59 shm_min_avail;
61 } = GlobalConfig.default_sharedmem_config in
62 let shm_dirs = local_config.ServerLocalConfig.shm_dirs in
64 let global_size = int_ "sharedmem_global_size" ~default:global_size config in
65 let heap_size = int_ "sharedmem_heap_size" ~default:heap_size config in
66 let dep_table_pow = int_ "sharedmem_dep_table_pow" ~default:17 config in
67 let hash_table_pow = int_ "sharedmem_hash_table_pow" ~default:18 config in
68 let log_level = int_ "sharedmem_log_level" ~default:0 config in
69 let sample_rate = float_ "sharedmem_sample_rate" ~default:0.0 config in
70 let shm_dirs = string_list
71 ~delim:(Str.regexp ",")
72 "sharedmem_dirs"
73 ~default:shm_dirs
74 config in
75 let shm_min_avail =
76 int_ "sharedmem_minimum_available" ~default:shm_min_avail config in
78 let global_size, heap_size, dep_table_pow, hash_table_pow =
79 match ServerArgs.ai_mode options with
80 | None -> global_size, heap_size, dep_table_pow, hash_table_pow
81 | Some ai_options ->
82 Ai.modify_shared_mem_sizes
83 global_size
84 heap_size
85 dep_table_pow
86 hash_table_pow
87 ai_options in
89 { SharedMem.
90 global_size;
91 heap_size;
92 dep_table_pow;
93 hash_table_pow;
94 log_level;
95 sample_rate;
96 shm_dirs;
97 shm_min_avail;
100 let config_list_regexp = (Str.regexp "[, \t]+")
102 let process_experimental sl =
103 match List.map sl String.lowercase with
104 | ["false"] -> SSet.empty
105 | ["true"] -> TypecheckerOptions.experimental_all
106 | features -> List.fold_left features ~f:SSet.add ~init:SSet.empty
108 let config_experimental_tc_features config =
109 match SMap.get config "enable_experimental_tc_features" with
110 | None -> SSet.empty
111 | Some s ->
112 let sl = Str.split config_list_regexp s in
113 process_experimental sl
115 let process_migration_flags sl =
116 match sl with
117 | ["false"] -> SSet.empty
118 | ["true"] -> TypecheckerOptions.migration_flags_all
119 | flags ->
120 begin
121 List.iter flags ~f:(fun s ->
122 if not (SSet.mem TypecheckerOptions.migration_flags_all s)
123 then failwith ("invalid migration flag: " ^ s));
124 List.fold_left flags ~f:SSet.add ~init:SSet.empty
127 let config_tc_migration_flags config =
128 SMap.get config "enable_tc_migration_flags"
129 |> Option.value_map ~f:(Str.split config_list_regexp) ~default:[]
130 |> List.map ~f:String.lowercase
131 |> process_migration_flags
134 let convert_paths str =
135 let json = Hh_json.json_of_string ~strict:true str in
136 let l = Hh_json.get_array_exn json in
137 List.filter_map ~f:(fun s ->
138 match s with
139 | Hh_json.JSON_String path -> Some path
140 | _ -> None
143 let process_ignored_paths config =
144 SMap.get config "ignored_paths"
145 |> Option.value_map ~f:convert_paths ~default:[]
147 let maybe_relative_path fn =
148 (* Note: this is not the same as calling realpath; the cwd is not
149 * necessarily the same as hh_server's root!!! *)
150 Path.make begin
151 if Filename.is_relative fn
152 then Relative_path.(to_absolute (from_root fn))
153 else fn
156 let process_extra_paths config =
157 match SMap.get config "extra_paths" with
158 | Some s -> Str.split config_list_regexp s |> List.map ~f:maybe_relative_path
159 | _ -> []
161 let process_coroutine_whitelist_paths config =
162 SMap.get config "coroutine_whitelist_paths"
163 |> Option.value_map ~f:convert_paths ~default:[]
165 let process_untrusted_mode config =
166 match SMap.get config "untrusted_mode" with
167 | Some s ->
168 if bool_of_string s
169 then
170 let blacklist = [
171 (* out of tree file access*)
172 "extra_paths";
173 (* potential resource abuse *)
174 "language_feature_logging";
175 ] in
176 let prefix_blacklist = [
177 (* potential resource abuse *)
178 "gc_";
179 "sharedmem_";
180 ] in
181 let invalid_keys = SMap.filter ~f:(fun ck _ ->
182 let ck = String.lowercase ck in
183 let exact_match = List.find ~f:(fun bli -> bli = ck) blacklist in
184 let prefix_match = List.find
185 ~f:(fun blp -> String_utils.string_starts_with ck blp)
186 prefix_blacklist
188 match exact_match, prefix_match with
189 | (None, None) -> false
190 | _ -> true
191 ) config |> SMap.keys in
192 if not (List.is_empty invalid_keys)
193 then failwith ("option not permitted in untrusted_mode: "^(String.concat ~sep:", " invalid_keys))
194 else failwith "untrusted_mode can only be enabled, not disabled"
195 | _ -> ()
197 let extract_auto_namespace_element ns_map element =
198 match element with
199 | (source, Hh_json.JSON_String target) ->
200 (source, target)::ns_map
201 | _ -> ns_map (* This means the JSON we received is incorrect *)
203 let convert_auto_namespace_to_map map =
204 let json = Hh_json.json_of_string ~strict:true map in
205 let pairs = Hh_json.get_object_exn json in
206 (* We do a fold instead of a map to filter
207 * out the incorrect entrie as we look at each item *)
208 List.fold_left ~init:[] ~f:extract_auto_namespace_element pairs
210 let prepare_auto_namespace_map config =
211 Option.value_map
212 (SMap.get config "auto_namespace_map")
213 ~default:[]
214 ~f:convert_auto_namespace_to_map
216 let prepare_ignored_fixme_codes config =
217 SMap.get config "ignored_fixme_codes"
218 |> Option.value_map ~f:(Str.split config_list_regexp) ~default:[]
219 |> List.map ~f:int_of_string
220 |> List.fold_right ~init:Errors.default_ignored_fixme_codes ~f:ISet.add
222 let load config_filename options =
223 let config_hash, config = Config_file.parse (Relative_path.to_absolute config_filename) in
224 let config_overrides = SMap.of_list @@ ServerArgs.config options in
225 let config = SMap.union config config_overrides in
226 process_untrusted_mode config;
227 let local_config = ServerLocalConfig.load ~silent:false in
228 let local_config =
229 if ServerArgs.ai_mode options <> None then
230 ServerLocalConfig.{
231 local_config with
232 use_watchman = false;
233 watchman_subscribe = false;
234 interrupt_on_watchman = false;
235 interrupt_on_client = false;
236 trace_parsing = false;
238 else
239 local_config
241 let version = SMap.get config "version" in
242 let ignored_paths = process_ignored_paths config in
243 let extra_paths = process_extra_paths config in
244 let coroutine_whitelist_paths = process_coroutine_whitelist_paths config in
245 (* Since we use the unix alarm() for our timeouts, a timeout value of 0 means
246 * to wait indefinitely *)
247 let load_script_timeout = int_ "load_script_timeout" ~default:0 config in
248 let formatter_override =
249 Option.map (SMap.get config "formatter_override") maybe_relative_path in
250 let global_opts = GlobalOptions.make
251 ?tco_safe_array:(bool_opt "safe_array" config)
252 ?tco_safe_vector_array:(bool_opt "safe_vector_array" config)
253 ?po_deregister_php_stdlib:(bool_opt "deregister_php_stdlib" config)
254 ?po_enable_concurrent:(bool_opt "enable_concurrent" config)
255 ?po_enable_await_as_an_expression:(bool_opt "enable_await_as_an_expression" config)
256 ?po_allow_goto:(Option.map ~f:not (bool_opt "disallow_goto" config))
257 ?po_disable_static_closures:(bool_opt "disable_static_closures" config)
258 ?tco_disallow_array_as_tuple:(bool_opt "disallow_array_as_tuple" config)
259 ?tco_disallow_ambiguous_lambda:(bool_opt "disallow_ambiguous_lambda" config)
260 ?tco_disallow_array_typehint:(bool_opt "disallow_array_typehint" config)
261 ?tco_disallow_array_literal:(bool_opt "disallow_array_literal" config)
262 ?tco_language_feature_logging:(bool_opt "language_feature_logging" config)
263 ?tco_unsafe_rx:(bool_opt "unsafe_rx" config)
264 ?tco_disallow_implicit_returns_in_non_void_functions:
265 (bool_opt "disallow_implicit_returns_in_non_void_functions" config)
266 ?tco_disallow_unset_on_varray:(bool_opt "disallow_unset_on_varray" config)
267 ?tco_disallow_scrutinee_case_value_type_mismatch:
268 (bool_opt "disallow_scrutinee_case_value_type_mismatch" config)
269 ?tco_disallow_stringish_magic:(bool_opt "disallow_stringish_magic" config)
270 ?tco_disallow_anon_use_capture_by_ref:
271 (bool_opt "disallow_anon_use_capture_by_ref" config)
272 ?tco_new_inference:(float_opt "new_inference" config)
273 ?tco_new_inference_no_eager_solve:(bool_opt "new_inference_no_eager_solve" config)
274 ?tco_timeout:(int_opt "timeout" config)
275 ?tco_disallow_invalid_arraykey:(bool_opt "disallow_invalid_arraykey" config)
276 ?tco_disable_instanceof_refinement:(bool_opt "disable_instanceof_refinement" config)
277 ?tco_disallow_ref_param_on_constructor:(bool_opt "disallow_ref_param_on_constructor" config)
278 ?tco_disallow_byref_dynamic_calls:(bool_opt "disallow_byref_dynamic_calls" config)
279 ?po_enable_stronger_await_binding:(bool_opt "stronger_await_binding" config)
280 ?po_disable_lval_as_an_expression:(bool_opt "disable_lval_as_an_expression" config)
281 ?po_disable_unsafe_expr:(bool_opt "disable_unsafe_expr" config)
282 ?po_disable_unsafe_block:(bool_opt "disable_unsafe_block" config)
283 ?tco_typecheck_xhp_cvars:(bool_opt "typecheck_xhp_cvars" config)
284 ?tco_ignore_collection_expr_type_arguments:(bool_opt "ignore_collection_expr_type_arguments" config)
285 ~ignored_fixme_codes:(prepare_ignored_fixme_codes config)
286 ?ignored_fixme_regex:(string_opt "ignored_fixme_regex" config)
287 ~po_auto_namespace_map:(prepare_auto_namespace_map config)
288 ~tco_experimental_features:(config_experimental_tc_features config)
289 ~tco_log_inference_constraints:(ServerArgs.log_inference_constraints options)
290 ~tco_migration_flags:(config_tc_migration_flags config)
291 ?po_disallow_byref_prop_args:(bool_opt "disallow_byref_prop_args" config)
294 Errors.ignored_fixme_codes :=
295 (GlobalOptions.ignored_fixme_codes global_opts);
297 version = version;
298 load_script_timeout = load_script_timeout;
299 gc_control = make_gc_control config;
300 sharedmem_config = make_sharedmem_config config options local_config;
301 tc_options = global_opts;
302 parser_options = global_opts;
303 formatter_override = formatter_override;
304 config_hash = Some config_hash;
305 ignored_paths = ignored_paths;
306 extra_paths = extra_paths;
307 coroutine_whitelist_paths = coroutine_whitelist_paths;
308 }, local_config
310 (* useful in testing code *)
311 let default_config = {
312 version = None;
313 load_script_timeout = 0;
314 gc_control = GlobalConfig.gc_control;
315 sharedmem_config = GlobalConfig.default_sharedmem_config;
316 tc_options = TypecheckerOptions.default;
317 parser_options = ParserOptions.default;
318 formatter_override = None;
319 config_hash = None;
320 ignored_paths = [];
321 extra_paths = [];
322 coroutine_whitelist_paths = [];
325 let set_parser_options config popt = { config with parser_options = popt }
326 let set_tc_options config tcopt = { config with tc_options = tcopt }
327 let gc_control config = config.gc_control
328 let sharedmem_config config = config.sharedmem_config
329 let typechecker_options config = config.tc_options
330 let parser_options config = config.parser_options
331 let formatter_override config = config.formatter_override
332 let config_hash config = config.config_hash
333 let ignored_paths config = config.ignored_paths
334 let extra_paths config = config.extra_paths
335 let coroutine_whitelist_paths config = config.coroutine_whitelist_paths
336 let version config = config.version