Default `disable_unset_class_const` to `true` and remove
[hiphop-php.git] / hphp / hack / src / hackc / compile / compile.rs
blobf526bb9cf9fa2205ddf9855832ce1d47b60c5f73
1 // Copyright (c) Facebook, Inc. and its affiliates.
2 //
3 // This source code is licensed under the MIT license found in the
4 // LICENSE file in the "hack" directory of this source tree.
6 pub mod dump_expr_tree;
8 use aast_parser::{
9     rust_aast_parser_types::{Env as AastEnv, Result as AastResult},
10     AastParser, Error as AastError,
12 use anyhow::anyhow;
13 use bitflags::bitflags;
14 use bytecode_printer::{print_unit, Context};
15 use decl_provider::{DeclProvider, NoDeclProvider};
16 use emit_unit::{emit_unit, FromAstFlags};
17 use env::emitter::Emitter;
18 use hackc_unit::HackCUnit;
19 use hhbc_ast::FatalOp;
20 use instruction_sequence::Error;
21 use ocamlrep::{rc::RcOc, FromError, FromOcamlRep, Value};
22 use ocamlrep_derive::{FromOcamlRep, ToOcamlRep};
23 use options::{Arg, HackLang, Hhvm, HhvmFlags, LangFlags, Options, Php7Flags, RepoFlags};
24 use oxidized::{
25     ast, namespace_env::Env as NamespaceEnv, parser_options::ParserOptions, pos::Pos,
26     relative_path::RelativePath,
28 use parser_core_types::{
29     indexed_source_text::IndexedSourceText, source_text::SourceText, syntax_error::ErrorType,
31 use rewrite_program::rewrite_program;
32 use stack_limit::StackLimit;
33 use thiserror::Error;
35 /// Common input needed for compilation.  Extra care is taken
36 /// so that everything is easily serializable at the FFI boundary
37 /// until the migration from OCaml is fully complete
38 #[derive(Debug, FromOcamlRep)]
39 pub struct Env<S> {
40     pub filepath: RelativePath,
41     pub config_jsons: Vec<S>,
42     pub config_list: Vec<S>,
43     pub flags: EnvFlags,
46 #[derive(Debug)]
47 pub struct NativeEnv<S> {
48     pub filepath: RelativePath,
49     pub aliased_namespaces: S,
50     pub include_roots: S,
51     pub emit_class_pointers: i32,
52     pub check_int_overflow: i32,
53     pub hhbc_flags: HHBCFlags,
54     pub parser_flags: ParserFlags,
55     pub flags: EnvFlags,
58 bitflags! {
59     // Note: these flags are intentionally packed into bits to overcome
60     // the limitation of to-OCaml FFI functions having at most 5 parameters
61     pub struct EnvFlags: u8 {
62         const IS_SYSTEMLIB = 1 << 0;
63         const IS_EVALED = 1 << 1;
64         const FOR_DEBUGGER_EVAL = 1 << 2;
65         const DUMP_SYMBOL_REFS = 1 << 3;
66         const DISABLE_TOPLEVEL_ELABORATION = 1 << 4;
67         const ENABLE_DECL = 1 << 5;
68     }
71 // Keep in sync with compiler_ffi.rs
72 bitflags! {
73       pub struct HHBCFlags: u32 {
74         const LTR_ASSIGN=1 << 0;
75         const UVS=1 << 1;
76         // No longer using bit 3.
77         const AUTHORITATIVE=1 << 4;
78         const JIT_ENABLE_RENAME_FUNCTION=1 << 5;
79         const LOG_EXTERN_COMPILER_PERF=1 << 6;
80         const ENABLE_INTRINSICS_EXTENSION=1 << 7;
81         // No longer using bit 8.
82         // No longer using bit 9.
83         const EMIT_CLS_METH_POINTERS=1 << 10;
84         const EMIT_METH_CALLER_FUNC_POINTERS=1 << 11;
85         const ENABLE_IMPLICIT_CONTEXT=1 << 12;
86         const ARRAY_PROVENANCE=1 << 13;
87         // No longer using bit 14.
88         const FOLD_LAZY_CLASS_KEYS=1 << 15;
89         // No longer using bit 16.
90     }
93 // Mapping must match getParserFlags() in runtime-option.cpp and compiler_ffi.rs
94 bitflags! {
95     pub struct ParserFlags: u32 {
96         const ABSTRACT_STATIC_PROPS=1 << 0;
97         const ALLOW_NEW_ATTRIBUTE_SYNTAX=1 << 1;
98         const ALLOW_UNSTABLE_FEATURES=1 << 2;
99         const CONST_DEFAULT_FUNC_ARGS=1 << 3;
100         const CONST_STATIC_PROPS=1 << 4;
101         const DISABLE_ARRAY=1 << 5;
102         // No longer using bit 6
103         const DISABLE_ARRAY_TYPEHINT=1 << 7;
104         const DISABLE_LVAL_AS_AN_EXPRESSION=1 << 8;
105         // No longer using bit 9
106         const DISALLOW_INST_METH=1 << 10;
107         const DISABLE_XHP_ELEMENT_MANGLING=1 << 11;
108         const DISALLOW_FUN_AND_CLS_METH_PSEUDO_FUNCS=1 << 12;
109         const DISALLOW_FUNC_PTRS_IN_CONSTANTS=1 << 13;
110         // No longer using bit 14.
111         // No longer using bit 15.
112         const ENABLE_ENUM_CLASSES=1 << 16;
113         const ENABLE_XHP_CLASS_MODIFIER=1 << 17;
114         // No longer using bits 18-19.
115         const ENABLE_CLASS_LEVEL_WHERE_CLAUSES=1 << 20;
116   }
119 impl FromOcamlRep for EnvFlags {
120     fn from_ocamlrep(value: Value<'_>) -> Result<Self, FromError> {
121         Ok(EnvFlags::from_bits(value.as_int().unwrap() as u8).unwrap())
122     }
125 impl HHBCFlags {
126     fn to_php7_flags(self) -> Php7Flags {
127         let mut f = Php7Flags::empty();
128         if self.contains(HHBCFlags::UVS) {
129             f |= Php7Flags::UVS;
130         }
131         if self.contains(HHBCFlags::LTR_ASSIGN) {
132             f |= Php7Flags::LTR_ASSIGN;
133         }
134         f
135     }
137     fn to_hhvm_flags(self) -> HhvmFlags {
138         let mut f = HhvmFlags::empty();
139         if self.contains(HHBCFlags::ARRAY_PROVENANCE) {
140             f |= HhvmFlags::ARRAY_PROVENANCE;
141         }
142         if self.contains(HHBCFlags::EMIT_CLS_METH_POINTERS) {
143             f |= HhvmFlags::EMIT_CLS_METH_POINTERS;
144         }
145         if self.contains(HHBCFlags::EMIT_METH_CALLER_FUNC_POINTERS) {
146             f |= HhvmFlags::EMIT_METH_CALLER_FUNC_POINTERS;
147         }
148         if self.contains(HHBCFlags::ENABLE_INTRINSICS_EXTENSION) {
149             f |= HhvmFlags::ENABLE_INTRINSICS_EXTENSION;
150         }
151         if self.contains(HHBCFlags::FOLD_LAZY_CLASS_KEYS) {
152             f |= HhvmFlags::FOLD_LAZY_CLASS_KEYS;
153         }
154         if self.contains(HHBCFlags::JIT_ENABLE_RENAME_FUNCTION) {
155             f |= HhvmFlags::JIT_ENABLE_RENAME_FUNCTION;
156         }
157         if self.contains(HHBCFlags::LOG_EXTERN_COMPILER_PERF) {
158             f |= HhvmFlags::LOG_EXTERN_COMPILER_PERF;
159         }
160         if self.contains(HHBCFlags::ENABLE_IMPLICIT_CONTEXT) {
161             f |= HhvmFlags::ENABLE_IMPLICIT_CONTEXT;
162         }
163         f
164     }
166     fn to_repo_flags(self) -> RepoFlags {
167         let mut f = RepoFlags::empty();
168         if self.contains(HHBCFlags::AUTHORITATIVE) {
169             f |= RepoFlags::AUTHORITATIVE;
170         }
171         f
172     }
175 impl ParserFlags {
176     fn to_lang_flags(self) -> LangFlags {
177         let mut f = LangFlags::empty();
178         if self.contains(ParserFlags::ABSTRACT_STATIC_PROPS) {
179             f |= LangFlags::ABSTRACT_STATIC_PROPS;
180         }
181         if self.contains(ParserFlags::ALLOW_NEW_ATTRIBUTE_SYNTAX) {
182             f |= LangFlags::ALLOW_NEW_ATTRIBUTE_SYNTAX;
183         }
184         if self.contains(ParserFlags::ALLOW_UNSTABLE_FEATURES) {
185             f |= LangFlags::ALLOW_UNSTABLE_FEATURES;
186         }
187         if self.contains(ParserFlags::CONST_DEFAULT_FUNC_ARGS) {
188             f |= LangFlags::CONST_DEFAULT_FUNC_ARGS;
189         }
190         if self.contains(ParserFlags::CONST_STATIC_PROPS) {
191             f |= LangFlags::CONST_STATIC_PROPS;
192         }
193         if self.contains(ParserFlags::DISABLE_ARRAY) {
194             f |= LangFlags::DISABLE_ARRAY;
195         }
196         if self.contains(ParserFlags::DISABLE_ARRAY_TYPEHINT) {
197             f |= LangFlags::DISABLE_ARRAY_TYPEHINT;
198         }
199         if self.contains(ParserFlags::DISABLE_LVAL_AS_AN_EXPRESSION) {
200             f |= LangFlags::DISABLE_LVAL_AS_AN_EXPRESSION;
201         }
202         if self.contains(ParserFlags::DISALLOW_INST_METH) {
203             f |= LangFlags::DISALLOW_INST_METH;
204         }
205         if self.contains(ParserFlags::DISABLE_XHP_ELEMENT_MANGLING) {
206             f |= LangFlags::DISABLE_XHP_ELEMENT_MANGLING;
207         }
208         if self.contains(ParserFlags::DISALLOW_FUN_AND_CLS_METH_PSEUDO_FUNCS) {
209             f |= LangFlags::DISALLOW_FUN_AND_CLS_METH_PSEUDO_FUNCS;
210         }
211         if self.contains(ParserFlags::DISALLOW_FUNC_PTRS_IN_CONSTANTS) {
212             f |= LangFlags::DISALLOW_FUNC_PTRS_IN_CONSTANTS;
213         }
214         if self.contains(ParserFlags::ENABLE_ENUM_CLASSES) {
215             f |= LangFlags::ENABLE_ENUM_CLASSES;
216         }
217         if self.contains(ParserFlags::ENABLE_XHP_CLASS_MODIFIER) {
218             f |= LangFlags::ENABLE_XHP_CLASS_MODIFIER;
219         }
220         if self.contains(ParserFlags::ENABLE_CLASS_LEVEL_WHERE_CLAUSES) {
221             f |= LangFlags::ENABLE_CLASS_LEVEL_WHERE_CLAUSES;
222         }
223         f
224     }
227 impl<S: AsRef<str>> NativeEnv<S> {
228     pub fn to_options(native_env: &NativeEnv<S>) -> Options {
229         let hhbc_flags = native_env.hhbc_flags;
230         let config = [&native_env.aliased_namespaces, &native_env.include_roots];
231         let opts = Options::from_configs(&config, &[]).unwrap();
232         let hhvm = Hhvm {
233             aliased_namespaces: opts.hhvm.aliased_namespaces,
234             include_roots: opts.hhvm.include_roots,
235             flags: hhbc_flags.to_hhvm_flags(),
236             emit_class_pointers: Arg::new(native_env.emit_class_pointers.to_string()),
237             hack_lang: HackLang {
238                 flags: native_env.parser_flags.to_lang_flags(),
239                 check_int_overflow: Arg::new(native_env.check_int_overflow.to_string()),
240             },
241         };
242         Options {
243             hhvm,
244             php7_flags: hhbc_flags.to_php7_flags(),
245             repo_flags: hhbc_flags.to_repo_flags(),
246             ..Default::default()
247         }
248     }
251 /// Compilation profile. All times are in seconds,
252 /// except when they are ignored and should not be reported,
253 /// such as in the case hhvm.log_extern_compiler_perf is false
254 /// (this avoids the need to read Options from OCaml, as
255 /// they can be simply returned as NaNs to signal that
256 /// they should _not_ be passed back as JSON to HHVM process)
257 #[derive(Debug, Default, ToOcamlRep)]
258 pub struct Profile {
259     pub parsing_t: f64,
260     pub codegen_t: f64,
261     pub printing_t: f64,
264 pub fn emit_fatal_unit<S: AsRef<str>>(
265     env: &Env<S>,
266     writer: &mut dyn std::io::Write,
267     err_msg: &str,
268 ) -> anyhow::Result<()> {
269     let is_systemlib = env.flags.contains(EnvFlags::IS_SYSTEMLIB);
270     let opts =
271         Options::from_configs(&env.config_jsons, &env.config_list).map_err(anyhow::Error::msg)?;
272     let alloc = bumpalo::Bump::new();
273     let emitter = Emitter::new(
274         opts,
275         is_systemlib,
276         env.flags.contains(EnvFlags::FOR_DEBUGGER_EVAL),
277         env.flags.contains(EnvFlags::ENABLE_DECL),
278         &alloc,
279         &NoDeclProvider,
280     );
282     let prog = emit_unit::emit_fatal_unit(&alloc, FatalOp::Parse, &Pos::make_none(), err_msg);
283     let prog = prog.map_err(|e| anyhow!("Unhandled Emitter error: {}", e))?;
284     print_unit(
285         &Context::new(
286             &emitter,
287             Some(&env.filepath),
288             env.flags.contains(EnvFlags::DUMP_SYMBOL_REFS),
289         ),
290         writer,
291         &prog,
292     )?;
293     Ok(())
296 pub fn from_text<'arena, 'decl, S: AsRef<str>>(
297     alloc: &'arena bumpalo::Bump,
298     env: &Env<S>,
299     stack_limit: &StackLimit,
300     writer: &mut dyn std::io::Write,
301     source_text: SourceText<'_>,
302     native_env: Option<&NativeEnv<S>>,
303     decl_provider: &'decl dyn DeclProvider<'decl>,
304 ) -> anyhow::Result<Option<Profile>> {
305     let mut emitter = create_emitter(env, native_env, decl_provider, alloc)?;
306     let (unit, profile) = emit_unit_from_text(&mut emitter, env, stack_limit, source_text)?;
308     let (print_result, printing_t) = time(|| {
309         print_unit(
310             &Context::new(
311                 &emitter,
312                 Some(&env.filepath),
313                 env.flags.contains(EnvFlags::DUMP_SYMBOL_REFS),
314             ),
315             writer,
316             &unit,
317         )
318     });
319     print_result?;
321     Ok(profile.map(|mut prof| {
322         prof.printing_t = printing_t;
323         prof
324     }))
327 fn rewrite_and_emit<'p, 'arena, 'decl, S: AsRef<str>>(
328     emitter: &mut Emitter<'arena, 'decl>,
329     env: &Env<S>,
330     namespace_env: RcOc<NamespaceEnv>,
331     ast: &'p mut ast::Program,
332 ) -> Result<HackCUnit<'arena>, Error> {
333     // First rewrite.
334     let result = rewrite(emitter, ast, RcOc::clone(&namespace_env)); // Modifies `ast` in place.
335     match result {
336         Ok(()) => {
337             // Rewrite ok, now emit.
338             emit_unit_from_ast(emitter, env, namespace_env, ast)
339         }
340         Err(Error::IncludeTimeFatalException(op, pos, msg)) => {
341             emit_unit::emit_fatal_unit(emitter.alloc, op, &pos, msg)
342         }
343         Err(e) => Err(e),
344     }
347 fn rewrite<'p, 'arena, 'decl>(
348     emitter: &mut Emitter<'arena, 'decl>,
349     ast: &'p mut ast::Program,
350     namespace_env: RcOc<NamespaceEnv>,
351 ) -> Result<(), Error> {
352     rewrite_program(emitter, ast, namespace_env)
355 pub fn unit_from_text<'arena, 'decl, S: AsRef<str>>(
356     alloc: &'arena bumpalo::Bump,
357     env: &Env<S>,
358     stack_limit: &StackLimit,
359     source_text: SourceText<'_>,
360     native_env: Option<&NativeEnv<S>>,
361     decl_provider: &'decl dyn DeclProvider<'decl>,
362 ) -> anyhow::Result<(HackCUnit<'arena>, Option<Profile>)> {
363     let mut emitter = create_emitter(env, native_env, decl_provider, alloc)?;
364     emit_unit_from_text(&mut emitter, env, stack_limit, source_text)
367 pub fn unit_to_string<W: std::io::Write, S: AsRef<str>>(
368     env: &Env<S>,
369     native_env: Option<&NativeEnv<S>>,
370     writer: &mut W,
371     program: &HackCUnit<'_>,
372 ) -> anyhow::Result<()> {
373     let alloc = bumpalo::Bump::new();
374     let emitter = create_emitter(env, native_env, &NoDeclProvider, &alloc)?;
375     let (print_result, _) = time(|| {
376         print_unit(
377             &Context::new(
378                 &emitter,
379                 Some(&env.filepath),
380                 env.flags.contains(EnvFlags::DUMP_SYMBOL_REFS),
381             ),
382             writer,
383             program,
384         )
385     });
386     print_result.map_err(|e| anyhow!("{}", e))
389 fn emit_unit_from_ast<'p, 'arena, 'decl, S: AsRef<str>>(
390     emitter: &mut Emitter<'arena, 'decl>,
391     env: &Env<S>,
392     namespace: RcOc<NamespaceEnv>,
393     ast: &'p mut ast::Program,
394 ) -> Result<HackCUnit<'arena>, Error> {
395     let mut flags = FromAstFlags::empty();
396     if env.flags.contains(EnvFlags::IS_EVALED) {
397         flags |= FromAstFlags::IS_EVALED;
398     }
399     if env.flags.contains(EnvFlags::FOR_DEBUGGER_EVAL) {
400         flags |= FromAstFlags::FOR_DEBUGGER_EVAL;
401     }
402     if env.flags.contains(EnvFlags::IS_SYSTEMLIB) {
403         flags |= FromAstFlags::IS_SYSTEMLIB;
404     }
406     emit_unit(emitter, flags, namespace, ast)
409 fn emit_unit_from_text<'arena, 'decl, S: AsRef<str>>(
410     emitter: &mut Emitter<'arena, 'decl>,
411     env: &Env<S>,
412     stack_limit: &StackLimit,
413     source_text: SourceText<'_>,
414 ) -> anyhow::Result<(HackCUnit<'arena>, Option<Profile>)> {
415     let log_extern_compiler_perf = emitter.options().log_extern_compiler_perf();
417     let namespace_env = RcOc::new(NamespaceEnv::empty(
418         emitter.options().hhvm.aliased_namespaces_cloned().collect(),
419         true, /* is_codegen */
420         emitter
421             .options()
422             .hhvm
423             .hack_lang
424             .flags
425             .contains(LangFlags::DISABLE_XHP_ELEMENT_MANGLING),
426     ));
428     let (parse_result, parsing_t) = time(|| {
429         parse_file(
430             emitter.options(),
431             stack_limit,
432             source_text,
433             !env.flags.contains(EnvFlags::DISABLE_TOPLEVEL_ELABORATION),
434             RcOc::clone(&namespace_env),
435             env.flags.contains(EnvFlags::IS_SYSTEMLIB),
436         )
437     });
439     let (program, codegen_t) = match parse_result {
440         Ok(mut ast) => {
441             elaborate_namespaces_visitor::elaborate_program(RcOc::clone(&namespace_env), &mut ast);
442             time(move || rewrite_and_emit(emitter, env, namespace_env, &mut ast))
443         }
444         Err(ParseError(pos, msg, is_runtime_error)) => {
445             time(|| emit_fatal(emitter.alloc, is_runtime_error, &pos, msg))
446         }
447     };
448     let profile = if log_extern_compiler_perf {
449         Some(Profile {
450             parsing_t,
451             codegen_t,
452             ..Default::default()
453         })
454     } else {
455         None
456     };
457     match program {
458         Ok(prog) => Ok((prog, profile)),
459         Err(e) => Err(anyhow!("Unhandled Emitter error: {}", e)),
460     }
463 fn emit_fatal<'arena>(
464     alloc: &'arena bumpalo::Bump,
465     is_runtime_error: bool,
466     pos: &Pos,
467     msg: impl AsRef<str> + 'arena,
468 ) -> Result<HackCUnit<'arena>, Error> {
469     let op = if is_runtime_error {
470         FatalOp::Runtime
471     } else {
472         FatalOp::Parse
473     };
474     emit_unit::emit_fatal_unit(alloc, op, pos, msg)
477 fn create_emitter<'arena, 'decl, S: AsRef<str>>(
478     env: &Env<S>,
479     native_env: Option<&NativeEnv<S>>,
480     decl_provider: &'decl dyn DeclProvider<'decl>,
481     alloc: &'arena bumpalo::Bump,
482 ) -> anyhow::Result<Emitter<'arena, 'decl>> {
483     let opts = match native_env {
484         None => Options::from_configs(&env.config_jsons, &env.config_list)
485             .map_err(anyhow::Error::msg)?,
486         Some(native_env) => NativeEnv::to_options(native_env),
487     };
488     Ok(Emitter::new(
489         opts,
490         env.flags.contains(EnvFlags::IS_SYSTEMLIB),
491         env.flags.contains(EnvFlags::FOR_DEBUGGER_EVAL),
492         env.flags.contains(EnvFlags::ENABLE_DECL),
493         alloc,
494         decl_provider,
495     ))
498 fn create_parser_options(opts: &Options) -> ParserOptions {
499     let hack_lang_flags = |flag| opts.hhvm.hack_lang.flags.contains(flag);
500     ParserOptions {
501         po_auto_namespace_map: opts.hhvm.aliased_namespaces_cloned().collect(),
502         po_codegen: true,
503         po_disallow_silence: false,
504         po_disable_lval_as_an_expression: hack_lang_flags(LangFlags::DISABLE_LVAL_AS_AN_EXPRESSION),
505         po_enable_class_level_where_clauses: hack_lang_flags(
506             LangFlags::ENABLE_CLASS_LEVEL_WHERE_CLAUSES,
507         ),
508         po_disable_legacy_soft_typehints: hack_lang_flags(LangFlags::DISABLE_LEGACY_SOFT_TYPEHINTS),
509         po_allow_new_attribute_syntax: hack_lang_flags(LangFlags::ALLOW_NEW_ATTRIBUTE_SYNTAX),
510         po_disable_legacy_attribute_syntax: hack_lang_flags(
511             LangFlags::DISABLE_LEGACY_ATTRIBUTE_SYNTAX,
512         ),
513         po_const_default_func_args: hack_lang_flags(LangFlags::CONST_DEFAULT_FUNC_ARGS),
514         po_const_default_lambda_args: hack_lang_flags(LangFlags::CONST_DEFAULT_LAMBDA_ARGS),
515         tco_const_static_props: hack_lang_flags(LangFlags::CONST_STATIC_PROPS),
516         po_abstract_static_props: hack_lang_flags(LangFlags::ABSTRACT_STATIC_PROPS),
517         po_disallow_func_ptrs_in_constants: hack_lang_flags(
518             LangFlags::DISALLOW_FUNC_PTRS_IN_CONSTANTS,
519         ),
520         po_enable_xhp_class_modifier: hack_lang_flags(LangFlags::ENABLE_XHP_CLASS_MODIFIER),
521         po_disable_xhp_element_mangling: hack_lang_flags(LangFlags::DISABLE_XHP_ELEMENT_MANGLING),
522         po_enable_enum_classes: hack_lang_flags(LangFlags::ENABLE_ENUM_CLASSES),
523         po_disable_array: hack_lang_flags(LangFlags::DISABLE_ARRAY),
524         po_disable_array_typehint: hack_lang_flags(LangFlags::DISABLE_ARRAY_TYPEHINT),
525         po_allow_unstable_features: hack_lang_flags(LangFlags::ALLOW_UNSTABLE_FEATURES),
526         po_disallow_fun_and_cls_meth_pseudo_funcs: hack_lang_flags(
527             LangFlags::DISALLOW_FUN_AND_CLS_METH_PSEUDO_FUNCS,
528         ),
529         po_disallow_inst_meth: hack_lang_flags(LangFlags::DISALLOW_INST_METH),
530         ..Default::default()
531     }
534 #[derive(Error, Debug)]
535 #[error("{0}: {1}")]
536 pub(crate) struct ParseError(Pos, String, bool);
538 /// parse_file returns either error(Left) or ast(Right)
539 /// - Left((Position, message, is_runtime_error))
540 /// - Right(ast)
541 fn parse_file(
542     opts: &Options,
543     stack_limit: &StackLimit,
544     source_text: SourceText<'_>,
545     elaborate_namespaces: bool,
546     namespace_env: RcOc<NamespaceEnv>,
547     is_systemlib: bool,
548 ) -> Result<ast::Program, ParseError> {
549     let aast_env = AastEnv {
550         codegen: true,
551         fail_open: false,
552         // Ocaml's implementation
553         // let enable_uniform_variable_syntax o = o.option_php7_uvs in
554         // php5_compat_mode:
555         //   (not (Hhbc_options.enable_uniform_variable_syntax hhbc_options))
556         php5_compat_mode: !opts.php7_flags.contains(Php7Flags::UVS),
557         keep_errors: false,
558         is_systemlib,
559         elaborate_namespaces,
560         parser_options: create_parser_options(opts),
561         ..AastEnv::default()
562     };
564     let indexed_source_text = IndexedSourceText::new(source_text);
565     let ast_result = AastParser::from_text_with_namespace_env(
566         &aast_env,
567         namespace_env,
568         &indexed_source_text,
569         Some(stack_limit),
570     );
571     match ast_result {
572         Err(AastError::Other(msg)) => Err(ParseError(Pos::make_none(), msg, false)),
573         Err(AastError::NotAHackFile()) => Err(ParseError(
574             Pos::make_none(),
575             "Not a Hack file".to_string(),
576             false,
577         )),
578         Err(AastError::ParserFatal(syntax_error, pos)) => {
579             Err(ParseError(pos, syntax_error.message.to_string(), false))
580         }
581         Ok(ast) => match ast {
582             AastResult { syntax_errors, .. } if !syntax_errors.is_empty() => {
583                 let error = syntax_errors
584                     .iter()
585                     .find(|e| e.error_type == ErrorType::RuntimeError)
586                     .unwrap_or(&syntax_errors[0]);
587                 let pos = indexed_source_text.relative_pos(error.start_offset, error.end_offset);
588                 Err(ParseError(
589                     pos,
590                     error.message.to_string(),
591                     error.error_type == ErrorType::RuntimeError,
592                 ))
593             }
594             AastResult { lowpri_errors, .. } if !lowpri_errors.is_empty() => {
595                 let (pos, msg) = lowpri_errors.into_iter().next().unwrap();
596                 Err(ParseError(pos, msg, false))
597             }
598             AastResult {
599                 errors,
600                 aast,
601                 scoured_comments,
602                 ..
603             } => {
604                 let mut errors = errors.iter().filter(|e| {
605                     scoured_comments.get_fixme(e.pos(), e.code()).is_none()
606                         /* Ignore these errors to match legacy AST behavior */
607                         && e.code() != 2086
608                         /* Naming.MethodNeedsVisibility */
609                         && e.code() != 2102
610                         /* Naming.UnsupportedTraitUseAs */
611                         && e.code() != 2103
612                 });
613                 if errors.next().is_some() {
614                     Err(ParseError(Pos::make_none(), String::new(), false))
615                 } else {
616                     match aast {
617                         Ok(aast) => Ok(aast),
618                         Err(msg) => Err(ParseError(Pos::make_none(), msg, false)),
619                     }
620                 }
621             }
622         },
623     }
626 fn time<T>(f: impl FnOnce() -> T) -> (T, f64) {
627     let (r, t) = profile_rust::time(f);
628     (r, t.as_secs_f64())
631 pub fn expr_to_string_lossy<S: AsRef<str>>(env: &Env<S>, expr: &ast::Expr) -> String {
632     let opts =
633         Options::from_configs(&env.config_jsons, &env.config_list).expect("Malformed options");
635     let alloc = bumpalo::Bump::new();
636     let emitter = Emitter::new(
637         opts,
638         env.flags.contains(EnvFlags::IS_SYSTEMLIB),
639         env.flags.contains(EnvFlags::FOR_DEBUGGER_EVAL),
640         env.flags.contains(EnvFlags::ENABLE_DECL),
641         &alloc,
642         &NoDeclProvider,
643     );
644     let ctx = Context::new(
645         &emitter,
646         Some(&env.filepath),
647         env.flags.contains(EnvFlags::DUMP_SYMBOL_REFS),
648     );
650     bytecode_printer::expr_to_string_lossy(ctx, expr)