2 * Copyright (c) 2015, Facebook, Inc.
5 * This source code is licensed under the MIT license found in the
6 * LICENSE file in the "hack" directory of this source tree.
11 * Parses and gathers information from the .hhconfig in the repo.
15 open Config_file.Getters
16 open Reordered_argument_collections
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
47 let make_gc_control config
=
48 let { Gc.Control.minor_heap_size
; space_overhead
; _
} = GlobalConfig.gc_control
in
50 int_
"gc_minor_heap_size" ~default
:minor_heap_size config
in
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
=
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
",")
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
82 Ai.modify_shared_mem_sizes
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
112 let sl = Str.split
config_list_regexp s
in
113 process_experimental sl
115 let process_migration_flags sl =
117 | ["false"] -> SSet.empty
118 | ["true"] -> TypecheckerOptions.migration_flags_all
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
->
139 | Hh_json.JSON_String path
-> Some path
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!!! *)
151 if Filename.is_relative fn
152 then Relative_path.(to_absolute
(from_root 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
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
171 (* out of tree file access*)
173 (* potential resource abuse *)
174 "language_feature_logging";
176 let prefix_blacklist = [
177 (* potential resource abuse *)
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
)
188 match exact_match, prefix_match with
189 | (None
, None
) -> false
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"
197 let extract_auto_namespace_element ns_map element
=
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
=
212 (SMap.get config
"auto_namespace_map")
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
229 if ServerArgs.ai_mode options
<> None
then
232 use_watchman
= false;
233 watchman_subscribe
= false;
234 interrupt_on_watchman
= false;
235 interrupt_on_client
= false;
236 trace_parsing
= false;
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);
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;
310 (* useful in testing code *)
311 let default_config = {
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
;
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