Stop emitting pseudomains for fatal units in HackC
[hiphop-php.git] / hphp / hack / src / hhbc / print.rs
blob4f34262507df5ee044c3e83a5fa675970b2a692e
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.
5 mod print_env;
6 mod write;
8 use indexmap::IndexSet;
9 use itertools::Itertools;
10 pub use write::{Error, IoWrite, Result, Write};
12 use ast_class_expr_rust::ClassExpr;
13 use ast_scope_rust as ast_scope;
14 use context::Context;
15 use core_utils_rust::add_ns;
16 use emit_type_hint_rust as emit_type_hint;
17 use env::{iterator::Id as IterId, local::Type as Local, Env as BodyEnv};
18 use escaper::{escape, escape_by, is_lit_printable};
19 use hhas_adata_rust::HhasAdata;
20 use hhas_adata_rust::{
21     DARRAY_PREFIX, DICT_PREFIX, KEYSET_PREFIX, LEGACY_DICT_PREFIX, LEGACY_VEC_PREFIX,
22     VARRAY_PREFIX, VEC_PREFIX,
24 use hhas_attribute_rust::{self as hhas_attribute, HhasAttribute};
25 use hhas_body_rust::HhasBody;
26 use hhas_class_rust::{self as hhas_class, HhasClass};
27 use hhas_constant_rust::HhasConstant;
28 use hhas_function_rust::HhasFunction;
29 use hhas_method_rust::{HhasMethod, HhasMethodFlags};
30 use hhas_param_rust::HhasParam;
31 use hhas_pos_rust::Span;
32 use hhas_program_rust::HhasProgram;
33 use hhas_property_rust::HhasProperty;
34 use hhas_record_def_rust::{Field, HhasRecord};
35 use hhas_symbol_refs_rust::{HhasSymbolRefs, IncludePath};
36 use hhas_type::{constraint, Info as HhasTypeInfo};
37 use hhas_type_const::HhasTypeConstant;
38 use hhas_typedef_rust::Typedef as HhasTypedef;
39 use hhbc_ast_rust::*;
40 use hhbc_id_rust::{class, Id};
41 use hhbc_string_utils_rust::{
42     float, integer, is_class, is_parent, is_self, is_static, is_xhp, lstrip, mangle, quote_string,
43     quote_string_with_escape, strip_global_ns, strip_ns, triple_quote_string, types,
45 use instruction_sequence_rust::{Error::Unrecoverable, InstrSeq};
46 use label_rust::Label;
47 use lazy_static::lazy_static;
48 use naming_special_names_rust::classes;
49 use ocaml_helper::escaped;
50 use oxidized::{ast, ast_defs, doc_comment::DocComment, local_id, pos::Pos};
51 use regex::Regex;
52 use runtime::TypedValue;
53 use write::*;
55 use std::{
56     borrow::Cow, collections::BTreeSet, convert::TryInto, io::Write as _, path::Path, write,
59 pub mod context {
60     use crate::write::*;
61     use env::emitter::Emitter;
62     use oxidized::relative_path::RelativePath;
64     /// Indent is an abstraction of indentation. Configurable indentation
65     /// and perf tweaking will be easier.
66     struct Indent(usize);
68     impl Indent {
69         pub fn new() -> Self {
70             Self(0)
71         }
73         pub fn inc(&mut self) {
74             self.0 += 1;
75         }
77         pub fn dec(&mut self) {
78             self.0 -= 1;
79         }
81         pub fn write<W: Write>(&self, w: &mut W) -> Result<(), W::Error> {
82             Ok(for _ in 0..self.0 {
83                 w.write("  ")?;
84             })
85         }
86     }
88     pub struct Context<'a> {
89         pub emitter: &'a mut Emitter,
90         pub path: Option<&'a RelativePath>,
92         dump_symbol_refs: bool,
93         indent: Indent,
94         is_system_lib: bool,
95     }
97     impl<'a> Context<'a> {
98         pub fn new(
99             emitter: &'a mut Emitter,
100             path: Option<&'a RelativePath>,
101             dump_symbol_refs: bool,
102             is_system_lib: bool,
103         ) -> Self {
104             Self {
105                 emitter,
106                 path,
107                 dump_symbol_refs,
108                 indent: Indent::new(),
109                 is_system_lib,
110             }
111         }
113         pub fn dump_symbol_refs(&self) -> bool {
114             self.dump_symbol_refs
115         }
117         /// Insert a newline with indentation
118         pub fn newline<W: Write>(&self, w: &mut W) -> Result<(), W::Error> {
119             newline(w)?;
120             self.indent.write(w)
121         }
123         /// Start a new indented block
124         pub fn block<W, F>(&mut self, w: &mut W, f: F) -> Result<(), W::Error>
125         where
126             W: Write,
127             F: FnOnce(&mut Self, &mut W) -> Result<(), W::Error>,
128         {
129             self.indent.inc();
130             let r = f(self, w);
131             self.indent.dec();
132             r
133         }
135         pub fn unblock<W, F>(&mut self, w: &mut W, f: F) -> Result<(), W::Error>
136         where
137             W: Write,
138             F: FnOnce(&mut Self, &mut W) -> Result<(), W::Error>,
139         {
140             self.indent.dec();
141             let r = f(self, w);
142             self.indent.inc();
143             r
144         }
146         /// Printing instruction list requies manually control indentation,
147         /// where indent_inc/indent_dec are called
148         pub fn indent_inc(&mut self) {
149             self.indent.inc();
150         }
152         pub fn indent_dec(&mut self) {
153             self.indent.dec();
154         }
156         pub fn is_system_lib(&self) -> bool {
157             self.is_system_lib
158         }
159     }
162 struct ExprEnv<'e> {
163     pub codegen_env: Option<&'e BodyEnv<'e>>,
164     pub is_xhp: bool,
167 pub fn print_program<W: Write>(
168     ctx: &mut Context,
169     w: &mut W,
170     prog: &HhasProgram,
171 ) -> Result<(), W::Error> {
172     match ctx.path {
173         Some(p) => {
174             let abs = p.to_absolute();
175             let p = escape(abs.to_str().ok_or(Error::InvalidUTF8)?);
177             concat_str_by(w, " ", ["#", p.as_ref(), "starts here"])?;
179             newline(w)?;
181             newline(w)?;
182             concat_str(w, [".filepath ", format!("\"{}\"", p).as_str(), ";"])?;
184             newline(w)?;
185             handle_not_impl(|| print_program_(ctx, w, prog))?;
187             newline(w)?;
188             concat_str_by(w, " ", ["#", p.as_ref(), "ends here"])?;
190             newline(w)
191         }
192         None => {
193             w.write("#starts here")?;
195             newline(w)?;
196             handle_not_impl(|| print_program_(ctx, w, prog))?;
198             newline(w)?;
199             w.write("#ends here")?;
201             newline(w)
202         }
203     }
206 fn get_fatal_op(f: &FatalOp) -> &str {
207     return match f {
208         FatalOp::Parse => "Parse",
209         FatalOp::Runtime => "Runtime",
210         FatalOp::RuntimeOmitFrame => "RuntimeOmitFrame",
211     };
214 fn print_program_<W: Write>(
215     ctx: &mut Context,
216     w: &mut W,
217     prog: &HhasProgram,
218 ) -> Result<(), W::Error> {
219     let is_hh = if prog.is_hh { "1" } else { "0" };
220     newline(w)?;
221     concat_str(w, [".hh_file ", is_hh, ";"])?;
223     if let Some((fop, p, msg)) = &prog.fatal {
224         newline(w)?;
225         let (line_begin, line_end, col_begin, col_end) = if p.is_none() {
226             (1, 1, 0, 0)
227         } else {
228             p.info_pos_extended()
229         };
230         let pos = format!("{}:{},{}:{}", line_begin, col_begin, line_end, col_end);
231         concat_str(
232             w,
233             [
234                 ".fatal ",
235                 pos.as_ref(),
236                 " ",
237                 get_fatal_op(fop),
238                 " \"",
239                 escape(msg).as_ref(),
240                 "\";",
241             ],
242         )?;
243     }
245     newline(w)?;
246     concat(w, &prog.adata, |w, a| print_adata_region(ctx, w, a))?;
247     newline(w)?;
248     print_main(ctx, w, &prog.main)?;
249     concat(w, &prog.functions, |w, f| print_fun_def(ctx, w, f))?;
250     concat(w, &prog.classes, |w, cd| print_class_def(ctx, w, cd))?;
251     concat(w, &prog.record_defs, |w, rd| print_record_def(ctx, w, rd))?;
252     concat(w, &prog.constants, |w, c| print_constant(ctx, w, c))?;
253     concat(w, &prog.typedefs, |w, td| print_typedef(ctx, w, td))?;
254     print_file_attributes(ctx, w, &prog.file_attributes)?;
256     if ctx.dump_symbol_refs() {
257         print_include_region(ctx, w, &prog.symbol_refs.includes)?;
258         print_symbol_ref_regions(ctx, w, &prog.symbol_refs)?;
259     }
260     Ok(())
263 fn print_include_region<W: Write>(
264     ctx: &mut Context,
265     w: &mut W,
266     includes: &BTreeSet<IncludePath>,
267 ) -> Result<(), W::Error> {
268     fn print_path<W: Write>(w: &mut W, p: &Path) -> Result<(), W::Error> {
269         option(w, p.to_str(), |w, p: &str| write!(w, "\n  {}", p))
270     };
271     fn print_if_exists<W: Write>(w: &mut W, p: &Path) -> Result<(), W::Error> {
272         if p.exists() {
273             print_path(w, p)
274         } else {
275             Ok(())
276         }
277     };
278     fn print_include<W: Write>(
279         ctx: &mut Context,
280         w: &mut W,
281         inc: IncludePath,
282     ) -> Result<(), W::Error> {
283         let include_roots = ctx.emitter.options().hhvm.include_roots.get();
284         match inc.into_doc_root_relative(include_roots) {
285             IncludePath::Absolute(p) => print_if_exists(w, Path::new(&p)),
286             IncludePath::SearchPathRelative(p) => {
287                 let path_from_cur_dirname = ctx
288                     .path
289                     .and_then(|p| p.path().parent())
290                     .unwrap_or(Path::new(""))
291                     .join(&p);
292                 if path_from_cur_dirname.exists() {
293                     print_path(w, &path_from_cur_dirname)
294                 } else {
295                     let search_paths = ctx.emitter.options().server.include_search_paths.get();
296                     for prefix in search_paths.iter() {
297                         let path = Path::new(prefix).join(&p);
298                         if path.exists() {
299                             return print_path(w, &path);
300                         }
301                     }
302                     Ok(())
303                 }
304             }
305             IncludePath::IncludeRootRelative(v, p) => {
306                 if !p.is_empty() {
307                     include_roots
308                         .get(&v)
309                         .iter()
310                         .map(|ir| {
311                             let doc_root = ctx.emitter.options().doc_root.get();
312                             let resolved = Path::new(doc_root).join(ir).join(&p);
313                             print_if_exists(w, &resolved)
314                         })
315                         .collect::<Result<_, _>>()?
316                 }
317                 Ok(())
318             }
319             IncludePath::DocRootRelative(p) => {
320                 let doc_root = ctx.emitter.options().doc_root.get();
321                 let resolved = Path::new(doc_root).join(&p);
322                 print_if_exists(w, &resolved)
323             }
324         }
325     };
326     if !includes.is_empty() {
327         w.write("\n.includes {")?;
328         for inc in includes.into_iter() {
329             // TODO(hrust): avoid clone. Rethink onwership of inc in
330             // hhas_symbol_refs_rust::IncludePath::into_doc_root_relative
331             print_include(ctx, w, inc.clone())?;
332         }
333         w.write("\n}\n")
334     } else {
335         Ok(())
336     }
339 fn print_symbol_ref_regions<W: Write>(
340     ctx: &mut Context,
341     w: &mut W,
342     symbol_refs: &HhasSymbolRefs,
343 ) -> Result<(), W::Error> {
344     let mut print_region = |name, refs: &BTreeSet<String>| {
345         if !refs.is_empty() {
346             ctx.newline(w)?;
347             write!(w, ".{} {{", name)?;
348             ctx.block(w, |c, w| {
349                 for s in refs.iter() {
350                     c.newline(w)?;
351                     w.write(s)?;
352                 }
353                 Ok(())
354             })?;
355             w.write("\n}\n")
356         } else {
357             Ok(())
358         }
359     };
360     print_region("constant_refs", &symbol_refs.constants)?;
361     print_region("function_refs", &symbol_refs.functions)?;
362     print_region("class_refs", &symbol_refs.classes)
365 fn print_adata_region<W: Write>(
366     ctx: &mut Context,
367     w: &mut W,
368     adata: &HhasAdata,
369 ) -> Result<(), W::Error> {
370     concat_str_by(w, " ", [".adata", adata.id.as_str(), "= "])?;
371     triple_quotes(w, |w| print_adata(ctx, w, &adata.value))?;
372     w.write(";")?;
373     ctx.newline(w)
376 fn print_typedef<W: Write>(ctx: &mut Context, w: &mut W, td: &HhasTypedef) -> Result<(), W::Error> {
377     newline(w)?;
378     w.write(".alias ")?;
379     print_typedef_attributes(ctx, w, td)?;
380     w.write(td.name.to_raw_string())?;
381     w.write(" = ")?;
382     print_typedef_info(w, &td.type_info)?;
383     w.write(" ")?;
384     triple_quotes(w, |w| print_adata(ctx, w, &td.type_structure))?;
385     w.write(";")
388 fn print_typedef_attributes<W: Write>(
389     ctx: &mut Context,
390     w: &mut W,
391     td: &HhasTypedef,
392 ) -> Result<(), W::Error> {
393     let mut specials = vec![];
394     if ctx.is_system_lib() {
395         specials.push("persistent");
396     }
397     print_special_and_user_attrs(ctx, w, &specials[..], td.attributes.as_slice())
400 fn handle_not_impl<E: std::fmt::Debug, F: FnOnce() -> Result<(), E>>(f: F) -> Result<(), E> {
401     let r = f();
402     match &r {
403         Err(Error::NotImpl(msg)) => {
404             println!("#### NotImpl: {}", msg);
405             eprintln!("NotImpl: {}", msg);
406             Ok(())
407         }
408         _ => r,
409     }
412 fn print_fun_def<W: Write>(
413     ctx: &mut Context,
414     w: &mut W,
415     fun_def: &HhasFunction,
416 ) -> Result<(), W::Error> {
417     let body = &fun_def.body;
418     newline(w)?;
419     w.write(".function ")?;
420     print_upper_bounds(w, &body.upper_bounds)?;
421     w.write(" ")?;
422     print_fun_attrs(ctx, w, fun_def)?;
423     print_span(w, &fun_def.span)?;
424     w.write(" ")?;
425     option(w, &body.return_type_info, |w, ti| {
426         print_type_info(w, ti)?;
427         w.write(" ")
428     })?;
429     w.write(fun_def.name.to_raw_string())?;
430     print_params(ctx, w, fun_def.body.env.as_ref(), fun_def.params())?;
431     if fun_def.is_generator() {
432         w.write(" isGenerator")?;
433     }
434     if fun_def.is_async() {
435         w.write(" isAsync")?;
436     }
437     if fun_def.is_pair_generator() {
438         w.write(" isPairGenerator")?;
439     }
440     if fun_def.rx_disabled() {
441         w.write(" isRxDisabled")?;
442     }
443     w.write(" ")?;
444     braces(w, |w| {
445         ctx.block(w, |c, w| print_body(c, w, body))?;
446         newline(w)
447     })?;
448     newline(w)
451 fn print_requirement<W: Write>(
452     ctx: &mut Context,
453     w: &mut W,
454     r: &(class::Type<'_>, hhas_class::TraitReqKind),
455 ) -> Result<(), W::Error> {
456     ctx.newline(w)?;
457     w.write(".require ")?;
458     match r {
459         (name, hhas_class::TraitReqKind::MustExtend) => {
460             write!(w, "extends <{}>;", name.to_raw_string())
461         }
462         (name, hhas_class::TraitReqKind::MustImplement) => {
463             write!(w, "implements <{}>;", name.to_raw_string())
464         }
465     }
468 fn print_type_constant<W: Write>(
469     ctx: &mut Context,
470     w: &mut W,
471     c: &HhasTypeConstant,
472 ) -> Result<(), W::Error> {
473     ctx.newline(w)?;
474     concat_str_by(w, " ", [".const", &c.name, "isType"])?;
475     option(w, &c.initializer, |w, init| {
476         w.write(" = ")?;
477         triple_quotes(w, |w| print_adata(ctx, w, init))
478     })?;
479     w.write(";")
482 fn print_property_doc_comment<W: Write>(w: &mut W, p: &HhasProperty) -> Result<(), W::Error> {
483     if let Some(s) = p.doc_comment.as_ref() {
484         w.write(triple_quote_string(&s.0))?;
485         w.write(" ")?;
486     }
487     Ok(())
490 fn print_property_attributes<W: Write>(
491     ctx: &mut Context,
492     w: &mut W,
493     property: &HhasProperty,
494 ) -> Result<(), W::Error> {
495     let mut special_attributes = vec![];
496     if property.is_late_init() {
497         special_attributes.push("late_init")
498     };
499     if property.is_no_bad_redeclare() {
500         special_attributes.push("no_bad_redeclare")
501     };
502     if property.initial_satisfies_tc() {
503         special_attributes.push("initial_satisfies_tc")
504     }
505     if property.no_implicit_null() {
506         special_attributes.push("no_implicit_null")
507     }
508     if property.has_system_initial() {
509         special_attributes.push("sys_initial_val")
510     }
511     if property.is_const() {
512         special_attributes.push("is_const")
513     }
514     if property.is_deep_init() {
515         special_attributes.push("deep_init")
516     }
517     if property.is_lsb() {
518         special_attributes.push("lsb")
519     }
520     if property.is_static() {
521         special_attributes.push("static")
522     }
523     special_attributes.push(property.visibility.as_ref());
524     special_attributes.reverse();
526     w.write("[")?;
527     concat_by(w, " ", &special_attributes, |w, a| w.write(a))?;
528     if !special_attributes.is_empty() && !property.attributes.is_empty() {
529         w.write(" ")?;
530     }
531     print_attributes(ctx, w, &property.attributes)?;
532     w.write("] ")
535 fn print_property_type_info<W: Write>(w: &mut W, p: &HhasProperty) -> Result<(), W::Error> {
536     print_type_info(w, &p.type_info)?;
537     w.write(" ")
540 fn print_property<W: Write>(
541     ctx: &mut Context,
542     w: &mut W,
543     class_def: &HhasClass,
544     property: &HhasProperty,
545 ) -> Result<(), W::Error> {
546     newline(w)?;
547     w.write("  .property ")?;
548     print_property_attributes(ctx, w, property)?;
549     print_property_doc_comment(w, property)?;
550     print_property_type_info(w, property)?;
551     w.write(property.name.to_raw_string())?;
552     w.write(" =\n    ")?;
553     let initial_value = property.initial_value.as_ref();
554     if class_def.is_closure() || initial_value == Some(&TypedValue::Uninit) {
555         w.write("uninit;")
556     } else {
557         triple_quotes(w, |w| match initial_value {
558             None => w.write("N;"),
559             Some(value) => print_adata(ctx, w, &value),
560         })?;
561         w.write(";")
562     }
565 fn print_constant<W: Write>(
566     ctx: &mut Context,
567     w: &mut W,
568     c: &HhasConstant,
569 ) -> Result<(), W::Error> {
570     ctx.newline(w)?;
571     w.write(".const ")?;
572     w.write(c.name.to_raw_string())?;
573     match c.value.as_ref() {
574         Some(TypedValue::Uninit) => w.write(" = uninit")?,
575         Some(value) => {
576             w.write(" = ")?;
577             triple_quotes(w, |w| print_adata(ctx, w, value))?;
578         }
579         None => (),
580     }
581     w.write(";")
584 fn print_enum_ty<W: Write>(ctx: &mut Context, w: &mut W, c: &HhasClass) -> Result<(), W::Error> {
585     if let Some(et) = c.enum_type.as_ref() {
586         ctx.newline(w)?;
587         w.write(".enum_ty ")?;
588         print_type_info_(w, true, et)?;
589         w.write(";")?;
590     }
591     Ok(())
594 fn print_doc_comment<W: Write>(
595     ctx: &mut Context,
596     w: &mut W,
597     doc_comment: &Option<DocComment>,
598 ) -> Result<(), W::Error> {
599     if let Some(cmt) = doc_comment {
600         ctx.newline(w)?;
601         write!(w, ".doc {};", triple_quote_string(&cmt.0))?;
602     }
603     Ok(())
606 fn print_use_precedence<W: Write>(
607     ctx: &mut Context,
608     w: &mut W,
609     (id1, id2, ids): &(class::Type, class::Type, Vec<class::Type>),
610 ) -> Result<(), W::Error> {
611     ctx.newline(w)?;
612     concat_str(w, [id1.to_raw_string(), "::", id2.to_raw_string()])?;
613     w.write(" insteadof ")?;
614     let unique_ids: IndexSet<&str> = ids.iter().map(|i| i.to_raw_string()).collect();
615     concat_str_by(w, " ", unique_ids.iter().collect::<Vec<_>>())?;
616     w.write(";")
619 fn print_use_as_visibility<W: Write>(w: &mut W, u: ast::UseAsVisibility) -> Result<(), W::Error> {
620     w.write(match u {
621         ast::UseAsVisibility::UseAsPublic => "public",
622         ast::UseAsVisibility::UseAsPrivate => "private",
623         ast::UseAsVisibility::UseAsProtected => "protected",
624         ast::UseAsVisibility::UseAsFinal => "final",
625     })
628 fn print_use_alias<W: Write>(
629     ctx: &mut Context,
630     w: &mut W,
631     (ido1, id, ido2, kindl): &(
632         Option<class::Type>,
633         class::Type,
634         Option<class::Type>,
635         &Vec<ast::UseAsVisibility>,
636     ),
637 ) -> Result<(), W::Error> {
638     ctx.newline(w)?;
639     let id = id.to_raw_string();
640     option_or(
641         w,
642         ido1,
643         |w, i: &class::Type| concat_str(w, [i.to_raw_string(), "::", id]),
644         id,
645     )?;
646     w.write(" as ")?;
647     if !kindl.is_empty() {
648         square(w, |w| {
649             concat_by(w, " ", kindl, |w, k| print_use_as_visibility(w, *k))
650         })?;
651     }
652     w.write_if(!kindl.is_empty() && ido2.is_some(), " ")?;
653     option(w, ido2, |w, i: &class::Type| w.write(i.to_raw_string()))?;
654     w.write(";")
657 fn print_method_trait_resolutions<W: Write>(
658     ctx: &mut Context,
659     w: &mut W,
660     (mtr, kind_as_tring): &(&ast::MethodRedeclaration, class::Type),
661 ) -> Result<(), W::Error> {
662     ctx.newline(w)?;
663     write!(
664         w,
665         "{}::{} as strict ",
666         kind_as_tring.to_raw_string(),
667         mtr.method.1
668     )?;
669     w.write_if(mtr.fun_kind.is_async(), "async ")?;
670     square(w, |w| {
671         w.write_if(mtr.final_, "final ")?;
672         w.write(mtr.visibility.to_string())?;
673         w.write_if(mtr.abstract_, " abstract")?;
674         w.write_if(mtr.static_, " static")
675     })?;
676     write!(w, " {};", mtr.name.1)
679 fn print_uses<W: Write>(ctx: &mut Context, w: &mut W, c: &HhasClass) -> Result<(), W::Error> {
680     if c.uses.is_empty() && c.method_trait_resolutions.is_empty() {
681         Ok(())
682     } else {
683         let unique_ids: IndexSet<&str> = c.uses.iter().map(|e| strip_global_ns(e)).collect();
684         let unique_ids: Vec<_> = unique_ids.into_iter().collect();
686         newline(w)?;
687         w.write("  .use ")?;
688         concat_by(w, " ", unique_ids, |w, id| w.write(id))?;
690         if c.use_aliases.is_empty()
691             && c.use_precedences.is_empty()
692             && c.method_trait_resolutions.is_empty()
693         {
694             w.write(";")
695         } else {
696             w.write(" {")?;
697             ctx.block(w, |ctx, w| {
698                 for x in &c.use_precedences {
699                     print_use_precedence(ctx, w, x)?;
700                 }
701                 for x in &c.use_aliases {
702                     print_use_alias(ctx, w, x)?;
703                 }
704                 for x in &c.method_trait_resolutions {
705                     print_method_trait_resolutions(ctx, w, x)?;
706                 }
707                 Ok(())
708             })?;
709             newline(w)?;
710             w.write("  }")
711         }
712     }
715 fn print_class_special_attributes<W: Write>(
716     ctx: &mut Context,
717     w: &mut W,
718     c: &HhasClass,
719 ) -> Result<(), W::Error> {
720     let user_attrs = &c.attributes;
721     let is_system_lib = ctx.is_system_lib();
723     let mut special_attributes: Vec<&str> = vec![];
724     if c.needs_no_reifiedinit() {
725         special_attributes.push("noreifiedinit")
726     }
727     if c.no_dynamic_props() {
728         special_attributes.push("no_dynamic_props")
729     }
730     if c.is_const() {
731         special_attributes.push("is_const")
732     }
733     if hhas_attribute::has_foldable(user_attrs) {
734         special_attributes.push("foldable")
735     }
736     if is_system_lib {
737         special_attributes.extend(&["persistent", "builtin", "unique"])
738     }
739     if hhas_attribute::has_dynamically_constructible(user_attrs) {
740         special_attributes.push("dyn_constructible");
741     }
742     if !c.is_top() {
743         special_attributes.push("nontop");
744     }
745     if c.is_closure() && !is_system_lib {
746         special_attributes.push("unique");
747     }
748     if c.is_closure() {
749         special_attributes.push("no_override");
750     }
751     if c.is_trait() {
752         special_attributes.push("trait");
753     }
754     if c.is_interface() {
755         special_attributes.push("interface");
756     }
757     if c.is_final() {
758         special_attributes.push("final");
759     }
760     if c.is_sealed() {
761         special_attributes.push("sealed");
762     }
763     if c.enum_type.is_some() {
764         special_attributes.push("enum");
765     }
766     if c.is_abstract() {
767         special_attributes.push("abstract");
768     }
769     if special_attributes.is_empty() && user_attrs.is_empty() {
770         return Ok(());
771     }
773     special_attributes.reverse();
774     wrap_by_(w, "[", "] ", |w| {
775         concat_by(w, " ", &special_attributes, |w, a| w.write(a))?;
776         w.write_if(
777             !special_attributes.is_empty() && !user_attrs.is_empty(),
778             " ",
779         )?;
780         print_attributes(ctx, w, &user_attrs)
781     })
784 fn print_implements<W: Write>(
785     w: &mut W,
786     implements: &Vec<class::Type<'_>>,
787 ) -> Result<(), W::Error> {
788     if implements.is_empty() {
789         return Ok(());
790     }
791     w.write(" implements (")?;
792     concat_str_by(
793         w,
794         " ",
795         implements
796             .iter()
797             .map(|x| x.to_raw_string())
798             .collect::<Vec<_>>(),
799     )?;
800     w.write(")")
803 fn print_shadowed_tparams<W: Write>(
804     w: &mut W,
805     shadowed_tparams: &[String],
806 ) -> Result<(), W::Error> {
807     braces(w, |w| concat_str_by(w, ", ", shadowed_tparams))
810 fn print_method_def<W: Write>(
811     ctx: &mut Context,
812     w: &mut W,
813     method_def: &HhasMethod,
814 ) -> Result<(), W::Error> {
815     let body = &method_def.body;
816     newline(w)?;
817     w.write("  .method ")?;
818     print_shadowed_tparams(w, &body.shadowed_tparams)?;
819     print_upper_bounds(w, &body.upper_bounds)?;
820     w.write(" ")?;
821     print_method_attrs(ctx, w, method_def)?;
822     print_span(w, &method_def.span)?;
823     w.write(" ")?;
824     option(w, &body.return_type_info, |w, t| {
825         print_type_info(w, t)?;
826         w.write(" ")
827     })?;
828     w.write(method_def.name.to_raw_string())?;
829     print_params(ctx, w, body.env.as_ref(), &body.params)?;
830     if method_def.flags.contains(HhasMethodFlags::IS_GENERATOR) {
831         w.write(" isGenerator")?;
832     }
833     if method_def.flags.contains(HhasMethodFlags::IS_ASYNC) {
834         w.write(" isAsync")?;
835     }
836     if method_def
837         .flags
838         .contains(HhasMethodFlags::IS_PAIR_GENERATOR)
839     {
840         w.write(" isPairGenerator")?;
841     }
842     if method_def.flags.contains(HhasMethodFlags::IS_CLOSURE_BODY) {
843         w.write(" isClosureBody")?;
844     }
845     if method_def.flags.contains(HhasMethodFlags::RX_DISABLED) {
846         w.write(" isRxDisabled")?;
847     }
848     w.write(" ")?;
849     braces(w, |w| {
850         ctx.block(w, |c, w| print_body(c, w, body))?;
851         newline(w)?;
852         w.write("  ")
853     })
856 fn print_method_attrs<W: Write>(
857     ctx: &mut Context,
858     w: &mut W,
859     m: &HhasMethod,
860 ) -> Result<(), W::Error> {
861     use hhas_attribute::*;
862     let user_attrs = &m.attributes;
863     let mut special_attrs = vec![];
864     if let Ok(attr) = m.rx_level.try_into() {
865         special_attrs.push(attr);
866     }
867     if has_provenance_skip_frame(user_attrs) {
868         special_attrs.push("prov_skip_frame")
869     }
870     if m.is_interceptable() {
871         special_attrs.push("interceptable");
872     }
873     let visibility = m.visibility.to_string();
874     special_attrs.push(&visibility);
875     if m.flags.contains(HhasMethodFlags::IS_STATIC) {
876         special_attrs.push("static");
877     }
878     if m.flags.contains(HhasMethodFlags::IS_FINAL) {
879         special_attrs.push("final");
880     }
881     if m.flags.contains(HhasMethodFlags::IS_ABSTRACT) {
882         special_attrs.push("abstract");
883     }
884     if has_foldable(user_attrs) {
885         special_attrs.push("foldable");
886     }
887     if m.is_no_injection() {
888         special_attrs.push("no_injection");
889     }
890     if ctx.is_system_lib() && has_native(user_attrs) && !is_native_opcode_impl(user_attrs) {
891         special_attrs.push("unique");
892     }
893     if ctx.is_system_lib() {
894         special_attrs.push("builtin");
895     }
896     if ctx.is_system_lib() && has_native(user_attrs) && !is_native_opcode_impl(user_attrs) {
897         special_attrs.push("persistent");
898     }
899     if ctx.is_system_lib() || (has_dynamically_callable(user_attrs) && !m.is_memoize_impl()) {
900         special_attrs.push("dyn_callable")
901     }
902     print_special_and_user_attrs(ctx, w, &special_attrs, user_attrs)
905 fn print_class_def<W: Write>(
906     ctx: &mut Context,
907     w: &mut W,
908     class_def: &HhasClass,
909 ) -> Result<(), W::Error> {
910     newline(w)?;
911     w.write(".class ")?;
912     print_upper_bounds(w, &class_def.upper_bounds)?;
913     w.write(" ")?;
914     print_class_special_attributes(ctx, w, class_def)?;
915     w.write(class_def.name.to_raw_string())?;
916     w.write(" ")?;
917     print_span(w, &class_def.span)?;
918     print_extends(w, class_def.base.as_ref().map(|x| x.to_raw_string()))?;
919     print_implements(w, &class_def.implements)?;
920     w.write(" {")?;
921     ctx.block(w, |c, w| {
922         print_doc_comment(c, w, &class_def.doc_comment)?;
923         print_uses(c, w, class_def)?;
924         print_enum_ty(c, w, class_def)?;
925         for x in &class_def.requirements {
926             print_requirement(c, w, x)?;
927         }
928         for x in &class_def.constants {
929             print_constant(c, w, x)?;
930         }
931         for x in &class_def.type_constants {
932             print_type_constant(c, w, x)?;
933         }
934         for x in &class_def.properties {
935             print_property(c, w, class_def, x)?;
936         }
937         for m in &class_def.methods {
938             print_method_def(c, w, m)?;
939         }
940         Ok(())
941     })?;
942     newline(w)?;
943     w.write("}")?;
944     newline(w)
947 fn print_pos_as_prov_tag<W: Write>(
948     ctx: &Context,
949     w: &mut W,
950     loc: &Option<ast_defs::Pos>,
951 ) -> Result<(), W::Error> {
952     match loc {
953         Some(l) if ctx.emitter.options().array_provenance() => {
954             let (line, ..) = l.info_pos();
955             let filename = l.filename().to_absolute();
956             let filename = match filename.to_str().unwrap() {
957                 "" => "(unknown hackc filename)",
958                 x => x,
959             };
960             write!(
961                 w,
962                 "p:i:{};s:{}:{};",
963                 line,
964                 filename.len(),
965                 quote_string_with_escape(filename)
966             )
967         }
968         _ => Ok(()),
969     }
972 fn print_hhbc_id<'a, W: Write>(w: &mut W, id: &impl Id<'a>) -> Result<(), W::Error> {
973     quotes(w, |w| w.write(escape(id.to_raw_string())))
976 fn print_function_id<W: Write>(w: &mut W, id: &FunctionId) -> Result<(), W::Error> {
977     print_hhbc_id(w, id)
980 fn print_class_id<W: Write>(w: &mut W, id: &ClassId) -> Result<(), W::Error> {
981     print_hhbc_id(w, id)
984 fn print_method_id<W: Write>(w: &mut W, id: &MethodId) -> Result<(), W::Error> {
985     print_hhbc_id(w, id)
988 fn print_const_id<W: Write>(w: &mut W, id: &ConstId) -> Result<(), W::Error> {
989     print_hhbc_id(w, id)
992 fn print_prop_id<W: Write>(w: &mut W, id: &PropId) -> Result<(), W::Error> {
993     print_hhbc_id(w, id)
996 fn print_adata_id<W: Write>(w: &mut W, id: &AdataId) -> Result<(), W::Error> {
997     concat_str(w, ["@", id.as_str()])
1000 fn print_adata_mapped_argument<W: Write, F, V>(
1001     ctx: &mut Context,
1002     w: &mut W,
1003     col_type: &str,
1004     loc: &Option<ast_defs::Pos>,
1005     values: &Vec<V>,
1006     f: F,
1007 ) -> Result<(), W::Error>
1008 where
1009     F: Fn(&mut Context, &mut W, &V) -> Result<(), W::Error>,
1011     write!(w, "{}:{}:{{", col_type, values.len(),)?;
1012     print_pos_as_prov_tag(ctx, w, loc)?;
1013     for v in values {
1014         f(ctx, w, v)?
1015     }
1016     write!(w, "}}")
1019 fn print_adata_collection_argument<W: Write>(
1020     ctx: &mut Context,
1021     w: &mut W,
1022     col_type: &str,
1023     loc: &Option<ast_defs::Pos>,
1024     values: &Vec<TypedValue>,
1025 ) -> Result<(), W::Error> {
1026     print_adata_mapped_argument(ctx, w, col_type, loc, values, &print_adata)
1029 fn print_adata_dict_collection_argument<W: Write>(
1030     ctx: &mut Context,
1031     w: &mut W,
1032     col_type: &str,
1033     loc: &Option<ast_defs::Pos>,
1034     pairs: &Vec<(TypedValue, TypedValue)>,
1035 ) -> Result<(), W::Error> {
1036     print_adata_mapped_argument(ctx, w, col_type, loc, pairs, |ctx, w, (v1, v2)| {
1037         print_adata(ctx, w, v1)?;
1038         print_adata(ctx, w, v2)
1039     })
1042 fn print_adata<W: Write>(ctx: &mut Context, w: &mut W, tv: &TypedValue) -> Result<(), W::Error> {
1043     match tv {
1044         TypedValue::Uninit => w.write("uninit"),
1045         TypedValue::Null => w.write("N;"),
1046         TypedValue::String(s) => write!(w, "s:{}:{};", s.len(), quote_string_with_escape(s)),
1047         TypedValue::Float(f) => write!(w, "d:{};", float::to_string(*f)),
1048         TypedValue::Int(i) => write!(w, "i:{};", i),
1049         // TODO: The False case seems to sometimes be b:0 and sometimes i:0.  Why?
1050         TypedValue::Bool(false) => w.write("b:0;"),
1051         TypedValue::Bool(true) => w.write("b:1;"),
1052         TypedValue::Dict((pairs, loc, is_legacy)) => {
1053             let prefix = if *is_legacy {
1054                 LEGACY_DICT_PREFIX
1055             } else {
1056                 DICT_PREFIX
1057             };
1058             print_adata_dict_collection_argument(ctx, w, prefix, loc, pairs)
1059         }
1060         TypedValue::Vec((values, loc, is_legacy)) => {
1061             let prefix = if *is_legacy {
1062                 LEGACY_VEC_PREFIX
1063             } else {
1064                 VEC_PREFIX
1065             };
1066             print_adata_collection_argument(ctx, w, prefix, loc, values)
1067         }
1068         TypedValue::DArray((pairs, loc)) => {
1069             print_adata_dict_collection_argument(ctx, w, DARRAY_PREFIX, loc, pairs)
1070         }
1071         TypedValue::Keyset(values) => {
1072             print_adata_collection_argument(ctx, w, KEYSET_PREFIX, &None, values)
1073         }
1074         TypedValue::VArray((values, loc)) => {
1075             print_adata_collection_argument(ctx, w, VARRAY_PREFIX, loc, values)
1076         }
1077         TypedValue::HhasAdata(s) => w.write(escaped(s)),
1078     }
1081 fn print_attribute<W: Write>(
1082     ctx: &mut Context,
1083     w: &mut W,
1084     a: &HhasAttribute,
1085 ) -> Result<(), W::Error> {
1086     write!(
1087         w,
1088         "\"{}\"(\"\"\"{}:{}:{{",
1089         a.name,
1090         VARRAY_PREFIX,
1091         a.arguments.len()
1092     )?;
1093     concat(w, &a.arguments, |w, arg| print_adata(ctx, w, arg))?;
1094     w.write("}\"\"\")")
1097 fn print_attributes<W: Write>(
1098     ctx: &mut Context,
1099     w: &mut W,
1100     al: impl AsRef<[HhasAttribute]>,
1101 ) -> Result<(), W::Error> {
1102     // Adjust for underscore coming before alphabet
1103     let al: Vec<&HhasAttribute> = al
1104         .as_ref()
1105         .iter()
1106         .sorted_by_key(|a| (!a.name.starts_with("__"), &a.name))
1107         .collect();
1108     concat_by(w, " ", &al, |w, a| print_attribute(ctx, w, a))
1111 fn print_file_attributes<W: Write>(
1112     ctx: &mut Context,
1113     w: &mut W,
1114     al: &Vec<HhasAttribute>,
1115 ) -> Result<(), W::Error> {
1116     if al.is_empty() {
1117         return Ok(());
1118     }
1119     newline(w)?;
1120     w.write(".file_attributes [")?;
1121     print_attributes(ctx, w, al)?;
1122     w.write("] ;")?;
1123     newline(w)
1126 fn print_main<W: Write>(ctx: &mut Context, w: &mut W, body: &HhasBody) -> Result<(), W::Error> {
1127     w.write(".main ")?;
1128     w.write("(1,1) ")?;
1129     braces(w, |w| {
1130         ctx.block(w, |c, w| print_body(c, w, body))?;
1131         newline(w)
1132     })?;
1133     newline(w)
1136 fn is_bareword_char(c: &u8) -> bool {
1137     match *c {
1138         b'_' | b'.' | b'$' | b'\\' => true,
1139         c => (c >= b'0' && c <= b'9') || (c >= b'a' && c <= b'z') || (c >= b'A' && c <= b'Z'),
1140     }
1143 fn print_body<W: Write>(ctx: &mut Context, w: &mut W, body: &HhasBody) -> Result<(), W::Error> {
1144     print_doc_comment(ctx, w, &body.doc_comment)?;
1145     if body.is_memoize_wrapper {
1146         ctx.newline(w)?;
1147         w.write(".ismemoizewrapper;")?;
1148     }
1149     if body.is_memoize_wrapper_lsb {
1150         ctx.newline(w)?;
1151         w.write(".ismemoizewrapperlsb;")?;
1152     }
1153     if body.num_iters > 0 {
1154         ctx.newline(w)?;
1155         write!(w, ".numiters {};", body.num_iters)?;
1156     }
1157     if !body.decl_vars.is_empty() {
1158         ctx.newline(w)?;
1159         w.write(".declvars ")?;
1160         concat_by(w, " ", &body.decl_vars, |w, var| {
1161             if var.as_bytes().iter().all(is_bareword_char) {
1162                 w.write(var)
1163             } else {
1164                 quotes(w, |w| w.write(escaper::escape(var)))
1165             }
1166         })?;
1167         w.write(";")?;
1168     }
1169     print_instructions(ctx, w, &body.body_instrs)
1172 fn print_instructions<W: Write>(
1173     ctx: &mut Context,
1174     w: &mut W,
1175     instr_seq: &InstrSeq,
1176 ) -> Result<(), W::Error> {
1177     use Instruct::*;
1178     use InstructTry::*;
1179     for instr in instr_seq.compact_iter() {
1180         match instr {
1181             ISpecialFlow(_) => return Err(Error::fail("Cannot break/continue 1 level")),
1182             IComment(_) => {
1183                 // indetation = 0
1184                 newline(w)?;
1185                 print_instr(w, instr)?;
1186             }
1187             ILabel(_) => ctx.unblock(w, |c, w| {
1188                 c.newline(w)?;
1189                 print_instr(w, instr)
1190             })?,
1191             ITry(TryCatchBegin) => {
1192                 ctx.newline(w)?;
1193                 print_instr(w, instr)?;
1194                 ctx.indent_inc();
1195             }
1196             ITry(TryCatchMiddle) => ctx.unblock(w, |c, w| {
1197                 c.newline(w)?;
1198                 print_instr(w, instr)
1199             })?,
1200             ITry(TryCatchEnd) => {
1201                 ctx.indent_dec();
1202                 ctx.newline(w)?;
1203                 print_instr(w, instr)?;
1204             }
1205             _ => {
1206                 ctx.newline(w)?;
1207                 print_instr(w, instr)?;
1208             }
1209         }
1210     }
1211     Ok(())
1214 fn if_then<F: FnOnce() -> R, R>(cond: bool, f: F) -> Option<R> {
1215     if cond {
1216         Some(f())
1217     } else {
1218         None
1219     }
1222 fn print_fcall_args<W: Write>(
1223     w: &mut W,
1224     FcallArgs(fls, num_args, num_rets, inouts, async_eager_label, context): &FcallArgs,
1225 ) -> Result<(), W::Error> {
1226     use FcallFlags as F;
1227     let mut flags = vec![];
1228     if_then(fls.contains(F::HAS_UNPACK), || flags.push("Unpack"));
1229     if_then(fls.contains(F::HAS_GENERICS), || flags.push("Generics"));
1230     if_then(fls.contains(F::SUPPORTS_ASYNC_EAGER_RETURN), || {
1231         flags.push("SupportsAER")
1232     });
1233     if_then(fls.contains(F::LOCK_WHILE_UNWINDING), || {
1234         flags.push("LockWhileUnwinding")
1235     });
1236     angle(w, |w| concat_str_by(w, " ", flags))?;
1237     w.write(" ")?;
1238     print_int(w, num_args)?;
1239     w.write(" ")?;
1240     print_int(w, num_rets)?;
1241     w.write(" ")?;
1242     quotes(w, |w| {
1243         concat_by(w, "", inouts, |w, i| w.write(if *i { "1" } else { "0" }))
1244     })?;
1245     w.write(" ")?;
1246     option_or(w, async_eager_label, print_label, "-")?;
1247     w.write(" ")?;
1248     match context {
1249         Some(s) => quotes(w, |w| w.write(s)),
1250         None => w.write("\"\""),
1251     }
1254 fn print_special_cls_ref<W: Write>(w: &mut W, cls_ref: &SpecialClsRef) -> Result<(), W::Error> {
1255     w.write(match cls_ref {
1256         SpecialClsRef::Static => "Static",
1257         SpecialClsRef::Self_ => "Self",
1258         SpecialClsRef::Parent => "Parent",
1259     })
1262 fn print_null_flavor<W: Write>(w: &mut W, f: &ObjNullFlavor) -> Result<(), W::Error> {
1263     w.write(match f {
1264         ObjNullFlavor::NullThrows => "NullThrows",
1265         ObjNullFlavor::NullSafe => "NullSafe",
1266     })
1269 fn print_instr<W: Write>(w: &mut W, instr: &Instruct) -> Result<(), W::Error> {
1270     fn print_call<W: Write>(w: &mut W, call: &InstructCall) -> Result<(), W::Error> {
1271         use InstructCall as I;
1272         match call {
1273             I::NewObj => w.write("NewObj"),
1274             I::NewObjR => w.write("NewObjR"),
1275             I::NewObjD(cid) => {
1276                 w.write("NewObjD ")?;
1277                 print_class_id(w, cid)
1278             }
1279             I::NewObjRD(cid) => {
1280                 w.write("NewObjRD ")?;
1281                 print_class_id(w, cid)
1282             }
1283             I::NewObjS(r) => {
1284                 w.write("NewObjS ")?;
1285                 print_special_cls_ref(w, r)
1286             }
1287             I::FCall(fcall_args) => {
1288                 w.write("FCall ")?;
1289                 print_fcall_args(w, &fcall_args)?;
1290                 w.write(r#" "" """#)
1291             }
1292             I::FCallClsMethod(fcall_args, is_log_as_dynamic_call) => {
1293                 w.write("FCallClsMethod ")?;
1294                 print_fcall_args(w, fcall_args)?;
1295                 w.write(r#" "" "#)?;
1296                 w.write(match is_log_as_dynamic_call {
1297                     IsLogAsDynamicCallOp::LogAsDynamicCall => "LogAsDynamicCall",
1298                     IsLogAsDynamicCallOp::DontLogAsDynamicCall => "DontLogAsDynamicCall",
1299                 })
1300             }
1301             I::FCallClsMethodD(fcall_args, cid, mid) => {
1302                 w.write("FCallClsMethodD ")?;
1303                 print_fcall_args(w, fcall_args)?;
1304                 w.write(r#" "" "#)?;
1305                 print_class_id(w, cid)?;
1306                 w.write(" ")?;
1307                 print_method_id(w, mid)
1308             }
1309             I::FCallClsMethodS(fcall_args, r) => {
1310                 w.write("FCallClsMethodS ")?;
1311                 print_fcall_args(w, fcall_args)?;
1312                 w.write(r#" "" "#)?;
1313                 print_special_cls_ref(w, r)
1314             }
1315             I::FCallClsMethodSD(fcall_args, r, mid) => {
1316                 w.write("FCallClsMethodSD ")?;
1317                 print_fcall_args(w, fcall_args)?;
1318                 w.write(r#" "" "#)?;
1319                 print_special_cls_ref(w, r)?;
1320                 w.write(" ")?;
1321                 print_method_id(w, mid)
1322             }
1323             I::FCallCtor(fcall_args) => {
1324                 w.write("FCallCtor ")?;
1325                 print_fcall_args(w, fcall_args)?;
1326                 w.write(r#" """#)
1327             }
1328             I::FCallFunc(fcall_args) => {
1329                 w.write("FCallFunc ")?;
1330                 print_fcall_args(w, fcall_args)
1331             }
1332             I::FCallFuncD(fcall_args, id) => {
1333                 w.write("FCallFuncD ")?;
1334                 print_fcall_args(w, fcall_args)?;
1335                 w.write(" ")?;
1336                 print_function_id(w, id)
1337             }
1338             I::FCallObjMethod(fcall_args, nf) => {
1339                 w.write("FCallObjMethod ")?;
1340                 print_fcall_args(w, fcall_args)?;
1341                 w.write(r#" "" "#)?;
1342                 print_null_flavor(w, nf)
1343             }
1344             I::FCallObjMethodD(fcall_args, nf, id) => {
1345                 w.write("FCallObjMethodD ")?;
1346                 print_fcall_args(w, fcall_args)?;
1347                 w.write(r#" "" "#)?;
1348                 print_null_flavor(w, nf)?;
1349                 w.write(" ")?;
1350                 print_method_id(w, id)
1351             }
1352         }
1353     }
1355     fn print_get<W: Write>(w: &mut W, get: &InstructGet) -> Result<(), W::Error> {
1356         use InstructGet as IG;
1357         match get {
1358             IG::CGetL(id) => {
1359                 w.write("CGetL ")?;
1360                 print_local(w, id)
1361             }
1362             IG::CGetQuietL(id) => {
1363                 w.write("CGetQuietL ")?;
1364                 print_local(w, id)
1365             }
1366             IG::CGetL2(id) => {
1367                 w.write("CGetL2 ")?;
1368                 print_local(w, id)
1369             }
1370             IG::CUGetL(id) => {
1371                 w.write("CUGetL ")?;
1372                 print_local(w, id)
1373             }
1374             IG::PushL(id) => {
1375                 w.write("PushL ")?;
1376                 print_local(w, id)
1377             }
1378             IG::CGetG => w.write("CGetG"),
1379             IG::CGetS => w.write("CGetS"),
1380             IG::ClassGetC => w.write("ClassGetC"),
1381             IG::ClassGetTS => w.write("ClassGetTS"),
1382         }
1383     }
1385     use Instruct::*;
1386     use InstructBasic as IB;
1387     match instr {
1388         IIterator(i) => print_iterator(w, i),
1389         IBasic(b) => w.write(match b {
1390             IB::Nop => "Nop",
1391             IB::EntryNop => "EntryNop",
1392             IB::PopC => "PopC",
1393             IB::PopU => "PopU",
1394             IB::Dup => "Dup",
1395         }),
1396         ILitConst(lit) => print_lit_const(w, lit),
1397         IOp(op) => print_op(w, op),
1398         IContFlow(cf) => print_control_flow(w, cf),
1399         ICall(c) => print_call(w, c),
1400         IMisc(misc) => print_misc(w, misc),
1401         IGet(get) => print_get(w, get),
1402         IMutator(mutator) => print_mutator(w, mutator),
1403         ILabel(l) => {
1404             print_label(w, l)?;
1405             w.write(":")
1406         }
1407         IIsset(i) => print_isset(w, i),
1408         IBase(i) => print_base(w, i),
1409         IFinal(i) => print_final(w, i),
1410         ITry(itry) => print_try(w, itry),
1411         IComment(s) => concat_str_by(w, " ", ["#", s.as_str()]),
1412         ISrcLoc(p) => write!(
1413             w,
1414             ".srcloc {}:{},{}:{};",
1415             p.line_begin, p.col_begin, p.line_end, p.col_end
1416         ),
1417         IAsync(a) => print_async(w, a),
1418         IGenerator(gen) => print_gen_creation_execution(w, gen),
1419         IIncludeEvalDefine(ed) => print_include_eval_define(w, ed),
1420         _ => Err(Error::fail("invalid instruction")),
1421     }
1424 fn print_base<W: Write>(w: &mut W, i: &InstructBase) -> Result<(), W::Error> {
1425     use InstructBase as I;
1426     match i {
1427         I::BaseGC(si, m) => {
1428             w.write("BaseGC ")?;
1429             print_stack_index(w, si)?;
1430             w.write(" ")?;
1431             print_member_opmode(w, m)
1432         }
1433         I::BaseGL(id, m) => {
1434             w.write("BaseGL ")?;
1435             print_local(w, id)?;
1436             w.write(" ")?;
1437             print_member_opmode(w, m)
1438         }
1439         I::BaseSC(si1, si2, m) => {
1440             w.write("BaseSC ")?;
1441             print_stack_index(w, si1)?;
1442             w.write(" ")?;
1443             print_stack_index(w, si2)?;
1444             w.write(" ")?;
1445             print_member_opmode(w, m)
1446         }
1447         I::BaseL(id, m) => {
1448             w.write("BaseL ")?;
1449             print_local(w, id)?;
1450             w.write(" ")?;
1451             print_member_opmode(w, m)
1452         }
1453         I::BaseC(si, m) => {
1454             w.write("BaseC ")?;
1455             print_stack_index(w, si)?;
1456             w.write(" ")?;
1457             print_member_opmode(w, m)
1458         }
1459         I::BaseH => w.write("BaseH"),
1460         I::Dim(m, mk) => {
1461             w.write("Dim ")?;
1462             print_member_opmode(w, m)?;
1463             w.write(" ")?;
1464             print_member_key(w, mk)
1465         }
1466     }
1469 fn print_stack_index<W: Write>(w: &mut W, si: &StackIndex) -> Result<(), W::Error> {
1470     w.write(si.to_string())
1473 fn print_member_opmode<W: Write>(w: &mut W, m: &MemberOpMode) -> Result<(), W::Error> {
1474     use MemberOpMode as M;
1475     w.write(match m {
1476         M::ModeNone => "None",
1477         M::Warn => "Warn",
1478         M::Define => "Define",
1479         M::Unset => "Unset",
1480         M::InOut => "InOut",
1481     })
1484 fn print_member_key<W: Write>(w: &mut W, mk: &MemberKey) -> Result<(), W::Error> {
1485     use MemberKey as M;
1486     match mk {
1487         M::EC(si) => {
1488             w.write("EC:")?;
1489             print_stack_index(w, si)
1490         }
1491         M::EL(local) => {
1492             w.write("EL:")?;
1493             print_local(w, local)
1494         }
1495         M::ET(s) => {
1496             w.write("ET:")?;
1497             quotes(w, |w| w.write(escape(s)))
1498         }
1499         M::EI(i) => concat_str(w, ["EI:", i.to_string().as_ref()]),
1500         M::PC(si) => {
1501             w.write("PC:")?;
1502             print_stack_index(w, si)
1503         }
1504         M::PL(local) => {
1505             w.write("PL:")?;
1506             print_local(w, local)
1507         }
1508         M::PT(id) => {
1509             w.write("PT:")?;
1510             print_prop_id(w, id)
1511         }
1512         M::QT(id) => {
1513             w.write("QT:")?;
1514             print_prop_id(w, id)
1515         }
1516         M::W => w.write("W"),
1517     }
1520 fn print_iterator<W: Write>(w: &mut W, i: &InstructIterator) -> Result<(), W::Error> {
1521     use InstructIterator as I;
1522     match i {
1523         I::IterInit(iter_args, label) => {
1524             w.write("IterInit ")?;
1525             print_iter_args(w, iter_args)?;
1526             w.write(" ")?;
1527             print_label(w, label)
1528         }
1529         I::IterNext(iter_args, label) => {
1530             w.write("IterNext ")?;
1531             print_iter_args(w, iter_args)?;
1532             w.write(" ")?;
1533             print_label(w, label)
1534         }
1535         I::IterFree(id) => {
1536             w.write("IterFree ")?;
1537             print_iterator_id(w, id)
1538         }
1539     }
1542 fn print_iter_args<W: Write>(w: &mut W, iter_args: &IterArgs) -> Result<(), W::Error> {
1543     print_iterator_id(w, &iter_args.iter_id)?;
1544     w.write(" ")?;
1545     match &iter_args.key_id {
1546         None => w.write("NK")?,
1547         Some(k) => {
1548             w.write("K:")?;
1549             print_local(w, &k)?;
1550         }
1551     };
1552     w.write(" ")?;
1553     w.write("V:")?;
1554     print_local(w, &iter_args.val_id)
1557 fn print_iterator_id<W: Write>(w: &mut W, i: &IterId) -> Result<(), W::Error> {
1558     write!(w, "{}", i)
1561 fn print_async<W: Write>(w: &mut W, a: &AsyncFunctions) -> Result<(), W::Error> {
1562     use AsyncFunctions as A;
1563     match a {
1564         A::WHResult => w.write("WHResult"),
1565         A::Await => w.write("Await"),
1566         A::AwaitAll(Some((Local::Unnamed(id), count))) => write!(w, "AwaitAll L:{}+{}", id, count),
1567         A::AwaitAll(None) => w.write("AwaitAll L:0+0"),
1568         _ => Err(Error::fail("AwaitAll needs an unnamed local")),
1569     }
1572 fn print_query_op<W: Write>(w: &mut W, q: QueryOp) -> Result<(), W::Error> {
1573     w.write(match q {
1574         QueryOp::CGet => "CGet",
1575         QueryOp::CGetQuiet => "CGetQuiet",
1576         QueryOp::Isset => "Isset",
1577         QueryOp::InOut => "InOut",
1578     })
1581 fn print_final<W: Write>(w: &mut W, f: &InstructFinal) -> Result<(), W::Error> {
1582     use InstructFinal as F;
1583     match f {
1584         F::QueryM(n, op, mk) => {
1585             w.write("QueryM ")?;
1586             print_int(w, n)?;
1587             w.write(" ")?;
1588             print_query_op(w, *op)?;
1589             w.write(" ")?;
1590             print_member_key(w, mk)
1591         }
1592         F::UnsetM(n, mk) => {
1593             w.write("UnsetM ")?;
1594             print_int(w, n)?;
1595             w.write(" ")?;
1596             print_member_key(w, mk)
1597         }
1598         F::SetM(i, mk) => {
1599             w.write("SetM ")?;
1600             print_int(w, i)?;
1601             w.write(" ")?;
1602             print_member_key(w, mk)
1603         }
1604         F::SetOpM(i, op, mk) => {
1605             w.write("SetOpM ")?;
1606             print_int(w, i)?;
1607             w.write(" ")?;
1608             print_eq_op(w, &op)?;
1609             w.write(" ")?;
1610             print_member_key(w, mk)
1611         }
1612         F::IncDecM(i, op, mk) => {
1613             w.write("IncDecM ")?;
1614             print_int(w, i)?;
1615             w.write(" ")?;
1616             print_incdec_op(w, &op)?;
1617             w.write(" ")?;
1618             print_member_key(w, mk)
1619         }
1620         F::SetRangeM(i, op, s) => {
1621             w.write("SetRangeM ")?;
1622             print_int(w, i)?;
1623             w.write(" ")?;
1624             w.write(match op {
1625                 SetrangeOp::Forward => "Forward",
1626                 SetrangeOp::Reverse => "Reverse",
1627             })?;
1628             w.write(" ")?;
1629             print_int(w, &(*s as usize))
1630         }
1631     }
1634 fn print_isset<W: Write>(w: &mut W, isset: &InstructIsset) -> Result<(), W::Error> {
1635     use InstructIsset as I;
1636     match isset {
1637         I::IssetC => w.write("IssetC"),
1638         I::IssetL(local) => {
1639             w.write("IssetL ")?;
1640             print_local(w, local)
1641         }
1642         I::IsUnsetL(local) => {
1643             w.write("IsUnsetL ")?;
1644             print_local(w, local)
1645         }
1646         I::IssetG => w.write("IssetG"),
1647         I::IssetS => w.write("IssetS"),
1648         I::IsTypeC(op) => {
1649             w.write("IsTypeC ")?;
1650             print_istype_op(w, op)
1651         }
1652         I::IsTypeL(local, op) => {
1653             w.write("IsTypeL ")?;
1654             print_local(w, local)?;
1655             w.write(" ")?;
1656             print_istype_op(w, op)
1657         }
1658     }
1661 fn print_istype_op<W: Write>(w: &mut W, op: &IstypeOp) -> Result<(), W::Error> {
1662     use IstypeOp as Op;
1663     match op {
1664         Op::OpNull => w.write("Null"),
1665         Op::OpBool => w.write("Bool"),
1666         Op::OpInt => w.write("Int"),
1667         Op::OpDbl => w.write("Dbl"),
1668         Op::OpStr => w.write("Str"),
1669         Op::OpArr => w.write("Arr"),
1670         Op::OpObj => w.write("Obj"),
1671         Op::OpRes => w.write("Res"),
1672         Op::OpScalar => w.write("Scalar"),
1673         Op::OpKeyset => w.write("Keyset"),
1674         Op::OpDict => w.write("Dict"),
1675         Op::OpVec => w.write("Vec"),
1676         Op::OpArrLike => w.write("ArrLike"),
1677         Op::OpVArray => w.write("VArray"),
1678         Op::OpDArray => w.write("DArray"),
1679         Op::OpClsMeth => w.write("ClsMeth"),
1680         Op::OpFunc => w.write("Func"),
1681         Op::OpPHPArr => w.write("PHPArr"),
1682     }
1685 fn print_try<W: Write>(w: &mut W, itry: &InstructTry) -> Result<(), W::Error> {
1686     use InstructTry as T;
1687     match itry {
1688         T::TryCatchBegin => w.write(".try {"),
1689         T::TryCatchMiddle => w.write("} .catch {"),
1690         T::TryCatchEnd => w.write("}"),
1691     }
1694 fn print_mutator<W: Write>(w: &mut W, mutator: &InstructMutator) -> Result<(), W::Error> {
1695     use InstructMutator as M;
1696     match mutator {
1697         M::SetL(local) => {
1698             w.write("SetL ")?;
1699             print_local(w, local)
1700         }
1701         M::PopL(id) => {
1702             w.write("PopL ")?;
1703             print_local(w, id)
1704         }
1705         M::SetG => w.write("SetG"),
1706         M::SetS => w.write("SetS"),
1707         M::SetOpL(id, op) => {
1708             w.write("SetOpL ")?;
1709             print_local(w, id)?;
1710             w.write(" ")?;
1711             print_eq_op(w, op)
1712         }
1713         M::SetOpG(op) => {
1714             w.write("SetOpG ")?;
1715             print_eq_op(w, op)
1716         }
1717         M::SetOpS(op) => {
1718             w.write("SetOpS ")?;
1719             print_eq_op(w, op)
1720         }
1721         M::IncDecL(id, op) => {
1722             w.write("IncDecL ")?;
1723             print_local(w, id)?;
1724             w.write(" ")?;
1725             print_incdec_op(w, op)
1726         }
1727         M::IncDecG(op) => {
1728             w.write("IncDecG ")?;
1729             print_incdec_op(w, op)
1730         }
1731         M::IncDecS(op) => {
1732             w.write("IncDecS ")?;
1733             print_incdec_op(w, op)
1734         }
1735         M::UnsetL(id) => {
1736             w.write("UnsetL ")?;
1737             print_local(w, id)
1738         }
1739         M::UnsetG => w.write("UnsetG"),
1740         M::CheckProp(id) => {
1741             w.write("CheckProp ")?;
1742             print_prop_id(w, id)
1743         }
1744         M::InitProp(id, op) => {
1745             w.write("InitProp ")?;
1746             print_prop_id(w, id)?;
1747             w.write(" ")?;
1748             match op {
1749                 InitpropOp::Static => w.write("Static"),
1750                 InitpropOp::NonStatic => w.write("NonStatic"),
1751             }
1752         }
1753     }
1756 fn print_eq_op<W: Write>(w: &mut W, op: &EqOp) -> Result<(), W::Error> {
1757     w.write(match op {
1758         EqOp::PlusEqual => "PlusEqual",
1759         EqOp::MinusEqual => "MinusEqual",
1760         EqOp::MulEqual => "MulEqual",
1761         EqOp::ConcatEqual => "ConcatEqual",
1762         EqOp::DivEqual => "DivEqual",
1763         EqOp::PowEqual => "PowEqual",
1764         EqOp::ModEqual => "ModEqual",
1765         EqOp::AndEqual => "AndEqual",
1766         EqOp::OrEqual => "OrEqual",
1767         EqOp::XorEqual => "XorEqual",
1768         EqOp::SlEqual => "SlEqual",
1769         EqOp::SrEqual => "SrEqual",
1770         EqOp::PlusEqualO => "PlusEqualO",
1771         EqOp::MinusEqualO => "MinusEqualO",
1772         EqOp::MulEqualO => "MulEqualO",
1773     })
1776 fn print_incdec_op<W: Write>(w: &mut W, op: &IncdecOp) -> Result<(), W::Error> {
1777     w.write(match op {
1778         IncdecOp::PreInc => "PreInc",
1779         IncdecOp::PostInc => "PostInc",
1780         IncdecOp::PreDec => "PreDec",
1781         IncdecOp::PostDec => "PostDec",
1782         IncdecOp::PreIncO => "PreIncO",
1783         IncdecOp::PostIncO => "PostIncO",
1784         IncdecOp::PreDecO => "PreDecO",
1785         IncdecOp::PostDecO => "PostDecO",
1786     })
1789 fn print_gen_creation_execution<W: Write>(
1790     w: &mut W,
1791     gen: &GenCreationExecution,
1792 ) -> Result<(), W::Error> {
1793     use GenCreationExecution as G;
1794     match gen {
1795         G::CreateCont => w.write("CreateCont"),
1796         G::ContEnter => w.write("ContEnter"),
1797         G::ContRaise => w.write("ContRaise"),
1798         G::Yield => w.write("Yield"),
1799         G::YieldK => w.write("YieldK"),
1800         G::ContCheck(CheckStarted::IgnoreStarted) => w.write("ContCheck IgnoreStarted"),
1801         G::ContCheck(CheckStarted::CheckStarted) => w.write("ContCheck CheckStarted"),
1802         G::ContValid => w.write("ContValid"),
1803         G::ContKey => w.write("ContKey"),
1804         G::ContGetReturn => w.write("ContGetReturn"),
1805         G::ContCurrent => w.write("ContCurrent"),
1806     }
1809 fn print_misc<W: Write>(w: &mut W, misc: &InstructMisc) -> Result<(), W::Error> {
1810     use InstructMisc as M;
1811     match misc {
1812         M::This => w.write("This"),
1813         M::CheckThis => w.write("CheckThis"),
1814         M::FuncNumArgs => w.write("FuncNumArgs"),
1815         M::ChainFaults => w.write("ChainFaults"),
1816         M::VerifyRetTypeC => w.write("VerifyRetTypeC"),
1817         M::VerifyRetTypeTS => w.write("VerifyRetTypeTS"),
1818         M::Self_ => w.write("Self"),
1819         M::Parent => w.write("Parent"),
1820         M::LateBoundCls => w.write("LateBoundCls"),
1821         M::ClassName => w.write("ClassName"),
1822         M::RecordReifiedGeneric => w.write("RecordReifiedGeneric"),
1823         M::CheckReifiedGenericMismatch => w.write("CheckReifiedGenericMismatch"),
1824         M::NativeImpl => w.write("NativeImpl"),
1825         M::AKExists => w.write("AKExists"),
1826         M::Idx => w.write("Idx"),
1827         M::ArrayIdx => w.write("ArrayIdx"),
1828         M::BreakTraceHint => w.write("BreakTraceHint"),
1829         M::CGetCUNop => w.write("CGetCUNop"),
1830         M::UGetCUNop => w.write("UGetCUNop"),
1831         M::LockObj => w.write("LockObj"),
1832         M::ThrowNonExhaustiveSwitch => w.write("ThrowNonExhaustiveSwitch"),
1833         M::VerifyParamType(id) => {
1834             w.write("VerifyParamType ")?;
1835             print_param_id(w, id)
1836         }
1837         M::VerifyParamTypeTS(id) => {
1838             w.write("VerifyParamTypeTS ")?;
1839             print_param_id(w, id)
1840         }
1841         M::Silence(local, op) => {
1842             w.write("Silence ")?;
1843             print_local(w, local)?;
1844             w.write(" ")?;
1845             match op {
1846                 OpSilence::Start => w.write("Start"),
1847                 OpSilence::End => w.write("End"),
1848             }
1849         }
1850         M::VerifyOutType(id) => {
1851             w.write("VerifyOutType ")?;
1852             print_param_id(w, id)
1853         }
1854         M::CreateCl(n, cid) => concat_str_by(
1855             w,
1856             " ",
1857             ["CreateCl", n.to_string().as_str(), cid.to_string().as_str()],
1858         ),
1859         M::BareThis(op) => concat_str_by(
1860             w,
1861             " ",
1862             [
1863                 "BareThis",
1864                 match op {
1865                     BareThisOp::Notice => "Notice",
1866                     BareThisOp::NoNotice => "NoNotice",
1867                     BareThisOp::NeverNull => "NeverNull",
1868                 },
1869             ],
1870         ),
1872         M::MemoGet(label, Some((Local::Unnamed(first), local_count))) => {
1873             w.write("MemoGet ")?;
1874             print_label(w, label)?;
1875             write!(w, " L:{}+{}", first, local_count)
1876         }
1877         M::MemoGet(label, None) => {
1878             w.write("MemoGet ")?;
1879             print_label(w, label)?;
1880             w.write(" L:0+0")
1881         }
1882         M::MemoGet(_, _) => Err(Error::fail("MemoGet needs an unnamed local")),
1884         M::MemoSet(Some((Local::Unnamed(first), local_count))) => {
1885             write!(w, "MemoSet L:{}+{}", first, local_count)
1886         }
1887         M::MemoSet(None) => w.write("MemoSet L:0+0"),
1888         M::MemoSet(_) => Err(Error::fail("MemoSet needs an unnamed local")),
1890         M::MemoGetEager(label1, label2, Some((Local::Unnamed(first), local_count))) => {
1891             w.write("MemoGetEager ")?;
1892             print_label(w, label1)?;
1893             w.write(" ")?;
1894             print_label(w, label2)?;
1895             write!(w, " L:{}+{}", first, local_count)
1896         }
1897         M::MemoGetEager(label1, label2, None) => {
1898             w.write("MemoGetEager ")?;
1899             print_label(w, label1)?;
1900             w.write(" ")?;
1901             print_label(w, label2)?;
1902             w.write(" L:0+0")
1903         }
1904         M::MemoGetEager(_, _, _) => Err(Error::fail("MemoGetEager needs an unnamed local")),
1906         M::MemoSetEager(Some((Local::Unnamed(first), local_count))) => {
1907             write!(w, "MemoSetEager L:{}+{}", first, local_count)
1908         }
1909         M::MemoSetEager(None) => w.write("MemoSetEager L:0+0"),
1910         M::MemoSetEager(_) => Err(Error::fail("MemoSetEager needs an unnamed local")),
1912         M::InitThisLoc(id) => {
1913             w.write("InitThisLoc ")?;
1914             print_local(w, id)
1915         }
1916         M::OODeclExists(k) => concat_str_by(
1917             w,
1918             " ",
1919             [
1920                 "OODeclExists",
1921                 match k {
1922                     ClassKind::Class => "Class",
1923                     ClassKind::Interface => "Interface",
1924                     ClassKind::Trait => "Trait",
1925                 },
1926             ],
1927         ),
1928         M::AssertRATL(local, s) => {
1929             w.write("AssertRATL ")?;
1930             print_local(w, local)?;
1931             w.write(" ")?;
1932             w.write(s)
1933         }
1934         M::AssertRATStk(n, s) => {
1935             concat_str_by(w, " ", ["AssertRATStk", n.to_string().as_str(), s.as_str()])
1936         }
1937         M::GetMemoKeyL(local) => {
1938             w.write("GetMemoKeyL ")?;
1939             print_local(w, local)
1940         }
1941     }
1944 fn print_include_eval_define<W: Write>(
1945     w: &mut W,
1946     ed: &InstructIncludeEvalDefine,
1947 ) -> Result<(), W::Error> {
1948     use InstructIncludeEvalDefine::*;
1949     match ed {
1950         Incl => w.write("Incl"),
1951         InclOnce => w.write("InclOnce"),
1952         Req => w.write("Req"),
1953         ReqOnce => w.write("ReqOnce"),
1954         ReqDoc => w.write("ReqDoc"),
1955         Eval => w.write("Eval"),
1956         DefCls(n) => concat_str_by(w, " ", ["DefCls", n.to_string().as_str()]),
1957         DefClsNop(n) => concat_str_by(w, " ", ["DefClsNop", n.to_string().as_str()]),
1958         DefRecord(n) => concat_str_by(w, " ", ["DefRecord", n.to_string().as_str()]),
1959         DefCns(n) => concat_str_by(w, " ", ["DefCns", n.to_string().as_str()]),
1960         DefTypeAlias(id) => write!(w, "DefTypeAlias {}", id),
1961     }
1964 fn print_control_flow<W: Write>(w: &mut W, cf: &InstructControlFlow) -> Result<(), W::Error> {
1965     use InstructControlFlow as CF;
1966     match cf {
1967         CF::Jmp(l) => {
1968             w.write("Jmp ")?;
1969             print_label(w, l)
1970         }
1971         CF::JmpNS(l) => {
1972             w.write("JmpNS ")?;
1973             print_label(w, l)
1974         }
1975         CF::JmpZ(l) => {
1976             w.write("JmpZ ")?;
1977             print_label(w, l)
1978         }
1979         CF::JmpNZ(l) => {
1980             w.write("JmpNZ ")?;
1981             print_label(w, l)
1982         }
1983         CF::RetC => w.write("RetC"),
1984         CF::RetCSuspended => w.write("RetCSuspended"),
1985         CF::RetM(p) => concat_str_by(w, " ", ["RetM", p.to_string().as_str()]),
1986         CF::Throw => w.write("Throw"),
1987         CF::Switch(kind, base, labels) => print_switch(w, kind, base, labels),
1988         CF::SSwitch(cases) => match &cases[..] {
1989             [] => Err(Error::fail("sswitch should have at least one case")),
1990             [rest @ .., (_, lastlabel)] => {
1991                 w.write("SSwitch ")?;
1992                 angle(w, |w| {
1993                     concat_by(w, " ", rest, |w, (s, l)| {
1994                         concat_str(w, [quote_string(s).as_str(), ":"])?;
1995                         print_label(w, l)
1996                     })?;
1997                     w.write(" -:")?;
1998                     print_label(w, lastlabel)
1999                 })
2000             }
2001         },
2002     }
2005 fn print_switch<W: Write>(
2006     w: &mut W,
2007     kind: &Switchkind,
2008     base: &isize,
2009     labels: &[Label],
2010 ) -> Result<(), W::Error> {
2011     w.write("Switch ")?;
2012     w.write(match kind {
2013         Switchkind::Bounded => "Bounded ",
2014         Switchkind::Unbounded => "Unbounded ",
2015     })?;
2016     w.write(base.to_string())?;
2017     w.write(" ")?;
2018     angle(w, |w| concat_by(w, " ", labels, print_label))
2021 fn print_lit_const<W: Write>(w: &mut W, lit: &InstructLitConst) -> Result<(), W::Error> {
2022     use InstructLitConst as LC;
2023     match lit {
2024         LC::Null => w.write("Null"),
2025         LC::Int(i) => concat_str_by(w, " ", ["Int", i.to_string().as_str()]),
2026         LC::String(s) => {
2027             w.write("String ")?;
2028             quotes(w, |w| w.write(escape(s)))
2029         }
2030         LC::True => w.write("True"),
2031         LC::False => w.write("False"),
2032         LC::Double(d) => concat_str_by(w, " ", ["Double", d.as_str()]),
2033         LC::AddElemC => w.write("AddElemC"),
2034         LC::AddNewElemC => w.write("AddNewElemC"),
2035         LC::NewPair => w.write("NewPair"),
2036         LC::File => w.write("File"),
2037         LC::Dir => w.write("Dir"),
2038         LC::Method => w.write("Method"),
2039         LC::FuncCred => w.write("FuncCred"),
2040         LC::Array(id) => {
2041             w.write("Array ")?;
2042             print_adata_id(w, id)
2043         }
2044         LC::Dict(id) => {
2045             w.write("Dict ")?;
2046             print_adata_id(w, id)
2047         }
2048         LC::Keyset(id) => {
2049             w.write("Keyset ")?;
2050             print_adata_id(w, id)
2051         }
2052         LC::Vec(id) => {
2053             w.write("Vec ")?;
2054             print_adata_id(w, id)
2055         }
2056         LC::NewDictArray(i) => concat_str_by(w, " ", ["NewDictArray", i.to_string().as_str()]),
2057         LC::NewDArray(i) => concat_str_by(w, " ", ["NewDArray", i.to_string().as_str()]),
2058         LC::NewVArray(i) => concat_str_by(w, " ", ["NewVArray", i.to_string().as_str()]),
2059         LC::NewVec(i) => concat_str_by(w, " ", ["NewVec", i.to_string().as_str()]),
2060         LC::NewKeysetArray(i) => concat_str_by(w, " ", ["NewKeysetArray", i.to_string().as_str()]),
2061         LC::NewStructDArray(l) => {
2062             w.write("NewStructDArray ")?;
2063             angle(w, |w| print_shape_fields(w, l))
2064         }
2065         LC::NewStructDict(l) => {
2066             w.write("NewStructDict ")?;
2067             angle(w, |w| print_shape_fields(w, l))
2068         }
2069         LC::NewRecord(cid, l) => {
2070             w.write("NewRecord ")?;
2071             print_class_id(w, cid)?;
2072             w.write(" ")?;
2073             angle(w, |w| print_shape_fields(w, l))
2074         }
2075         LC::CnsE(id) => {
2076             w.write("CnsE ")?;
2077             print_const_id(w, id)
2078         }
2079         LC::ClsCns(id) => {
2080             w.write("ClsCns ")?;
2081             print_const_id(w, id)
2082         }
2083         LC::ClsCnsD(const_id, cid) => {
2084             w.write("ClsCnsD ")?;
2085             print_const_id(w, const_id)?;
2086             w.write(" ")?;
2087             print_class_id(w, cid)
2088         }
2089         LC::NewCol(ct) => {
2090             w.write("NewCol ")?;
2091             print_collection_type(w, ct)
2092         }
2093         LC::ColFromArray(ct) => {
2094             w.write("ColFromArray ")?;
2095             print_collection_type(w, ct)
2096         }
2097         LC::NullUninit => w.write("NullUninit"),
2098         LC::TypedValue(_) => Err(Error::fail("print_lit_const: TypedValue")),
2099     }
2102 fn print_collection_type<W: Write>(w: &mut W, ct: &CollectionType) -> Result<(), W::Error> {
2103     use CollectionType as CT;
2104     match ct {
2105         CT::Vector => w.write("Vector"),
2106         CT::Map => w.write("Map"),
2107         CT::Set => w.write("Set"),
2108         CT::Pair => w.write("Pair"),
2109         CT::ImmVector => w.write("ImmVector"),
2110         CT::ImmMap => w.write("ImmMap"),
2111         CT::ImmSet => w.write("ImmSet"),
2112         CT::Dict => w.write("dict"),
2113         CT::Array => w.write("array"),
2114         CT::Keyset => w.write("keyset"),
2115         CT::Vec => w.write("vec"),
2116     }
2119 fn print_shape_fields<W: Write>(w: &mut W, sf: &Vec<String>) -> Result<(), W::Error> {
2120     concat_by(w, " ", sf, |w, f| quotes(w, |w| w.write(escape(f))))
2123 fn print_op<W: Write>(w: &mut W, op: &InstructOperator) -> Result<(), W::Error> {
2124     use InstructOperator as I;
2125     match op {
2126         I::Concat => w.write("Concat"),
2127         I::ConcatN(n) => concat_str_by(w, " ", ["ConcatN", n.to_string().as_str()]),
2128         I::Abs => w.write("Abs"),
2129         I::Add => w.write("Add"),
2130         I::Sub => w.write("Sub"),
2131         I::Mul => w.write("Mul"),
2132         I::AddO => w.write("AddO"),
2133         I::SubO => w.write("SubO"),
2134         I::MulO => w.write("MulO"),
2135         I::Div => w.write("Div"),
2136         I::Mod => w.write("Mod"),
2137         I::Pow => w.write("Pow"),
2138         I::Sqrt => w.write("Sqrt"),
2139         I::Xor => w.write("Xor"),
2140         I::Not => w.write("Not"),
2141         I::Same => w.write("Same"),
2142         I::NSame => w.write("NSame"),
2143         I::Eq => w.write("Eq"),
2144         I::Neq => w.write("Neq"),
2145         I::Lt => w.write("Lt"),
2146         I::Lte => w.write("Lte"),
2147         I::Gt => w.write("Gt"),
2148         I::Gte => w.write("Gte"),
2149         I::Cmp => w.write("Cmp"),
2150         I::BitAnd => w.write("BitAnd"),
2151         I::BitOr => w.write("BitOr"),
2152         I::BitXor => w.write("BitXor"),
2153         I::BitNot => w.write("BitNot"),
2154         I::Shl => w.write("Shl"),
2155         I::Shr => w.write("Shr"),
2156         I::Floor => w.write("Floor"),
2157         I::Ceil => w.write("Ceil"),
2158         I::CastBool => w.write("CastBool"),
2159         I::CastInt => w.write("CastInt"),
2160         I::CastDouble => w.write("CastDouble"),
2161         I::CastString => w.write("CastString"),
2162         I::CastVec => w.write("CastVec"),
2163         I::CastDict => w.write("CastDict"),
2164         I::CastKeyset => w.write("CastKeyset"),
2165         I::CastVArray => w.write("CastVArray"),
2166         I::CastDArray => w.write("CastDArray"),
2167         I::InstanceOf => w.write("InstanceOf"),
2168         I::InstanceOfD(id) => {
2169             w.write("InstanceOfD ")?;
2170             print_class_id(w, id)
2171         }
2172         I::IsLateBoundCls => w.write("IsLateBoundCls"),
2173         I::IsTypeStructC(op) => concat_str_by(
2174             w,
2175             " ",
2176             [
2177                 "IsTypeStructC",
2178                 match op {
2179                     TypestructResolveOp::Resolve => "Resolve",
2180                     TypestructResolveOp::DontResolve => "DontResolve",
2181                 },
2182             ],
2183         ),
2184         I::ThrowAsTypeStructException => w.write("ThrowAsTypeStructException"),
2185         I::CombineAndResolveTypeStruct(n) => concat_str_by(
2186             w,
2187             " ",
2188             ["CombineAndResolveTypeStruct", n.to_string().as_str()],
2189         ),
2190         I::Print => w.write("Print"),
2191         I::Clone => w.write("Clone"),
2192         I::Exit => w.write("Exit"),
2193         I::ResolveFunc(id) => {
2194             w.write("ResolveFunc ")?;
2195             print_function_id(w, id)
2196         }
2197         I::ResolveRFunc(id) => {
2198             w.write("ResolveRFunc ")?;
2199             print_function_id(w, id)
2200         }
2201         I::ResolveMethCaller(id) => {
2202             w.write("ResolveMethCaller ")?;
2203             print_function_id(w, id)
2204         }
2205         I::ResolveObjMethod => w.write("ResolveObjMethod"),
2206         I::ResolveClsMethod(mid) => {
2207             w.write("ResolveClsMethod ")?;
2208             print_method_id(w, mid)
2209         }
2210         I::ResolveClsMethodD(cid, mid) => {
2211             w.write("ResolveClsMethodD ")?;
2212             print_class_id(w, cid)?;
2213             w.write(" ")?;
2214             print_method_id(w, mid)
2215         }
2216         I::ResolveClsMethodS(r, mid) => {
2217             w.write("ResolveClsMethodS ")?;
2218             print_special_cls_ref(w, r)?;
2219             w.write(" ")?;
2220             print_method_id(w, mid)
2221         }
2222         I::ResolveRClsMethod(mid) => {
2223             w.write("ResolveRClsMethod ")?;
2224             print_method_id(w, mid)
2225         }
2226         I::ResolveRClsMethodD(cid, mid) => {
2227             w.write("ResolveRClsMethodD ")?;
2228             print_class_id(w, cid)?;
2229             w.write(" ")?;
2230             print_method_id(w, mid)
2231         }
2232         I::ResolveRClsMethodS(r, mid) => {
2233             w.write("ResolveRClsMethodS ")?;
2234             print_special_cls_ref(w, r)?;
2235             w.write(" ")?;
2236             print_method_id(w, mid)
2237         }
2238         I::Fatal(fatal_op) => print_fatal_op(w, fatal_op),
2239     }
2242 fn print_fatal_op<W: Write>(w: &mut W, f: &FatalOp) -> Result<(), W::Error> {
2243     match f {
2244         FatalOp::Parse => w.write("Fatal Parse"),
2245         FatalOp::Runtime => w.write("Fatal Runtime"),
2246         FatalOp::RuntimeOmitFrame => w.write("Fatal RuntimeOmitFrame"),
2247     }
2250 fn print_params<W: Write>(
2251     ctx: &mut Context,
2252     w: &mut W,
2253     body_env: Option<&BodyEnv>,
2254     params: &[HhasParam],
2255 ) -> Result<(), W::Error> {
2256     paren(w, |w| {
2257         concat_by(w, ", ", params, |w, i| print_param(ctx, w, body_env, i))
2258     })
2261 fn print_param<W: Write>(
2262     ctx: &mut Context,
2263     w: &mut W,
2264     body_env: Option<&BodyEnv>,
2265     param: &HhasParam,
2266 ) -> Result<(), W::Error> {
2267     print_param_user_attributes(ctx, w, param)?;
2268     w.write_if(param.is_inout, "inout ")?;
2269     w.write_if(param.is_variadic, "...")?;
2270     option(w, &param.type_info, |w, ty| {
2271         print_type_info(w, ty)?;
2272         w.write(" ")
2273     })?;
2274     w.write(&param.name)?;
2275     option(w, &param.default_value, |w, i| {
2276         print_param_default_value(ctx, w, body_env, i)
2277     })
2280 fn print_param_id<W: Write>(w: &mut W, param_id: &ParamId) -> Result<(), W::Error> {
2281     match param_id {
2282         ParamId::ParamUnnamed(i) => w.write(i.to_string()),
2283         ParamId::ParamNamed(s) => w.write(s),
2284     }
2287 fn print_param_default_value<W: Write>(
2288     ctx: &mut Context,
2289     w: &mut W,
2290     body_env: Option<&BodyEnv>,
2291     default_val: &(Label, ast::Expr),
2292 ) -> Result<(), W::Error> {
2293     let expr_env = ExprEnv {
2294         codegen_env: body_env,
2295         is_xhp: false,
2296     };
2297     w.write(" = ")?;
2298     print_label(w, &default_val.0)?;
2299     paren(w, |w| {
2300         triple_quotes(w, |w| print_expr(ctx, w, &expr_env, &default_val.1))
2301     })
2304 fn print_label<W: Write>(w: &mut W, label: &Label) -> Result<(), W::Error> {
2305     match label {
2306         Label::Regular(id) => {
2307             w.write("L")?;
2308             print_int(w, id)
2309         }
2310         Label::DefaultArg(id) => {
2311             w.write("DV")?;
2312             print_int(w, id)
2313         }
2314         Label::Named(id) => w.write(id),
2315     }
2318 fn print_local<W: Write>(w: &mut W, local: &Local) -> Result<(), W::Error> {
2319     match local {
2320         Local::Unnamed(id) => {
2321             w.write("_")?;
2322             print_int(w, id)
2323         }
2324         Local::Named(id) => w.write(id),
2325     }
2328 fn print_int<W: Write>(w: &mut W, i: &usize) -> Result<(), W::Error> {
2329     write!(w, "{}", i)
2332 fn print_key_value<W: Write>(
2333     ctx: &mut Context,
2334     w: &mut W,
2335     env: &ExprEnv,
2336     k: &ast::Expr,
2337     v: &ast::Expr,
2338 ) -> Result<(), W::Error> {
2339     print_key_value_(ctx, w, env, k, print_expr, v)
2342 fn print_key_value_<W: Write, K, KeyPrinter>(
2343     ctx: &mut Context,
2344     w: &mut W,
2345     env: &ExprEnv,
2346     k: K,
2347     mut kp: KeyPrinter,
2348     v: &ast::Expr,
2349 ) -> Result<(), W::Error>
2350 where
2351     KeyPrinter: FnMut(&mut Context, &mut W, &ExprEnv, K) -> Result<(), W::Error>,
2353     kp(ctx, w, env, k)?;
2354     w.write(" => ")?;
2355     print_expr(ctx, w, env, v)
2358 fn print_afield<W: Write>(
2359     ctx: &mut Context,
2360     w: &mut W,
2361     env: &ExprEnv,
2362     afield: &ast::Afield,
2363 ) -> Result<(), W::Error> {
2364     use ast::Afield as A;
2365     match afield {
2366         A::AFvalue(e) => print_expr(ctx, w, env, &e),
2367         A::AFkvalue(k, v) => print_key_value(ctx, w, env, &k, &v),
2368     }
2371 fn print_afields<W: Write>(
2372     ctx: &mut Context,
2373     w: &mut W,
2374     env: &ExprEnv,
2375     afields: impl AsRef<[ast::Afield]>,
2376 ) -> Result<(), W::Error> {
2377     concat_by(w, ", ", afields, |w, i| print_afield(ctx, w, env, i))
2380 fn print_uop<W: Write>(w: &mut W, op: ast::Uop) -> Result<(), W::Error> {
2381     use ast::Uop as U;
2382     w.write(match op {
2383         U::Utild => "~",
2384         U::Unot => "!",
2385         U::Uplus => "+",
2386         U::Uminus => "-",
2387         U::Uincr => "++",
2388         U::Udecr => "--",
2389         U::Usilence => "@",
2390         U::Upincr | U::Updecr => {
2391             return Err(Error::fail(
2392                 "string_of_uop - should have been captures earlier",
2393             ))
2394         }
2395     })
2398 fn print_key_values<W: Write>(
2399     ctx: &mut Context,
2400     w: &mut W,
2401     env: &ExprEnv,
2402     kvs: impl AsRef<[(ast::Expr, ast::Expr)]>,
2403 ) -> Result<(), W::Error> {
2404     print_key_values_(ctx, w, env, print_expr, kvs)
2407 fn print_key_values_<W: Write, K, KeyPrinter>(
2408     ctx: &mut Context,
2409     w: &mut W,
2410     env: &ExprEnv,
2411     mut kp: KeyPrinter,
2412     kvs: impl AsRef<[(K, ast::Expr)]>,
2413 ) -> Result<(), W::Error>
2414 where
2415     KeyPrinter: Fn(&mut Context, &mut W, &ExprEnv, &K) -> Result<(), W::Error>,
2417     concat_by(w, ", ", kvs, |w, (k, v)| {
2418         print_key_value_(ctx, w, env, k, &mut kp, v)
2419     })
2422 fn print_expr_darray<W: Write, K, KeyPrinter>(
2423     ctx: &mut Context,
2424     w: &mut W,
2425     env: &ExprEnv,
2426     kp: KeyPrinter,
2427     kvs: impl AsRef<[(K, ast::Expr)]>,
2428 ) -> Result<(), W::Error>
2429 where
2430     KeyPrinter: Fn(&mut Context, &mut W, &ExprEnv, &K) -> Result<(), W::Error>,
2432     wrap_by_(w, "darray[", "]", |w| {
2433         print_key_values_(ctx, w, env, kp, kvs)
2434     })
2437 fn print_expr_varray<W: Write>(
2438     ctx: &mut Context,
2439     w: &mut W,
2440     env: &ExprEnv,
2441     varray: &[ast::Expr],
2442 ) -> Result<(), W::Error> {
2443     wrap_by_(w, "varray[", "]", |w| {
2444         concat_by(w, ", ", varray, |w, e| print_expr(ctx, w, env, e))
2445     })
2448 fn print_shape_field_name<W: Write>(
2449     _: &mut Context,
2450     w: &mut W,
2451     _: &ExprEnv,
2452     field: &ast::ShapeFieldName,
2453 ) -> Result<(), W::Error> {
2454     use ast::ShapeFieldName as S;
2455     match field {
2456         S::SFlitInt((_, s)) => print_expr_int(w, s),
2457         S::SFlitStr((_, s)) | S::SFclassConst(_, (_, s)) => print_expr_string(w, s),
2458     }
2461 fn print_expr_int<W: Write>(w: &mut W, i: &String) -> Result<(), W::Error> {
2462     match integer::to_decimal(i.as_str()) {
2463         Some(s) => w.write(s),
2464         None => Err(Error::fail("ParseIntError")),
2465     }
2468 fn print_expr_string<W: Write>(w: &mut W, s: &String) -> Result<(), W::Error> {
2469     fn escape_char(c: u8) -> Option<Cow<'static, [u8]>> {
2470         match c {
2471             b'\n' => Some((&b"\\\\n"[..]).into()),
2472             b'\r' => Some((&b"\\\\r"[..]).into()),
2473             b'\t' => Some((&b"\\\\t"[..]).into()),
2474             b'\\' => Some((&b"\\\\\\\\"[..]).into()),
2475             b'"' => Some((&b"\\\\\\\""[..]).into()),
2476             b'$' => Some((&b"\\\\$"[..]).into()),
2477             b'?' => Some((&b"\\?"[..]).into()),
2478             c if is_lit_printable(c) => None,
2479             c => {
2480                 let mut r = vec![];
2481                 write!(r, "\\\\{:03o}", c).unwrap();
2482                 Some(r.into())
2483             }
2484         }
2485     }
2486     wrap_by(w, "\\\"", |w| w.write(escape_by(s.into(), escape_char)))
2489 fn print_expr_to_string<W: Write>(
2490     ctx: &mut Context,
2491     env: &ExprEnv,
2492     expr: &ast::Expr,
2493 ) -> Result<String, W::Error> {
2494     let mut buf = String::new();
2495     print_expr(ctx, &mut buf, env, expr).map_err(|e| match e {
2496         Error::NotImpl(m) => Error::NotImpl(m),
2497         _ => Error::Fail(format!("Failed: {}", e)),
2498     })?;
2499     Ok(buf)
2502 fn print_expr<W: Write>(
2503     ctx: &mut Context,
2504     w: &mut W,
2505     env: &ExprEnv,
2506     ast::Expr(_, expr): &ast::Expr,
2507 ) -> Result<(), W::Error> {
2508     fn adjust_id<'a>(env: &ExprEnv, id: &'a String) -> Cow<'a, str> {
2509         let s: Cow<'a, str> = match env.codegen_env {
2510             Some(env) => {
2511                 if env.namespace.name.is_none()
2512                     && id
2513                         .as_bytes()
2514                         .iter()
2515                         .rposition(|c| *c == b'\\')
2516                         .map_or(true, |i| i < 1)
2517                 {
2518                     strip_global_ns(id).into()
2519                 } else {
2520                     add_ns(id)
2521                 }
2522             }
2523             _ => id.into(),
2524         };
2525         escaper::escape(s)
2526     }
2527     fn print_expr_id<'a, W: Write>(
2528         w: &mut W,
2529         env: &ExprEnv,
2530         s: &'a String,
2531     ) -> Result<(), W::Error> {
2532         w.write(adjust_id(env, s))
2533     }
2534     fn fmt_class_name<'a>(is_class_constant: bool, id: Cow<'a, str>) -> Cow<'a, str> {
2535         let cn: Cow<'a, str> = if is_xhp(strip_global_ns(&id)) {
2536             escaper::escape(strip_global_ns(&mangle(id.into())))
2537                 .to_string()
2538                 .into()
2539         } else {
2540             escaper::escape(strip_global_ns(&id)).to_string().into()
2541         };
2542         if is_class_constant {
2543             format!("\\\\{}", cn).into()
2544         } else {
2545             cn
2546         }
2547     }
2548     fn get_class_name_from_id<'e>(
2549         ctx: &mut Context,
2550         env: Option<&'e BodyEnv<'e>>,
2551         should_format: bool,
2552         is_class_constant: bool,
2553         id: &'e str,
2554     ) -> Cow<'e, str> {
2555         if id == classes::SELF || id == classes::PARENT || id == classes::STATIC {
2556             return get_special_class_name(ctx, env, is_class_constant, id);
2557         }
2558         fn get<'a>(should_format: bool, is_class_constant: bool, id: &'a str) -> Cow<'a, str> {
2559             if should_format {
2560                 fmt_class_name(is_class_constant, id.into())
2561             } else {
2562                 id.into()
2563             }
2564         };
2566         if env.is_some() {
2567             let class_id = class::Type::from_ast_name(id);
2568             let id = class_id.to_raw_string();
2569             get(should_format, is_class_constant, id)
2570                 .into_owned()
2571                 .into()
2572         } else {
2573             get(should_format, is_class_constant, id)
2574         }
2575     }
2576     fn get_special_class_name<'e>(
2577         ctx: &mut Context,
2578         env: Option<&'e BodyEnv<'e>>,
2579         is_class_constant: bool,
2580         id: &'e str,
2581     ) -> Cow<'e, str> {
2582         let class_expr = match env {
2583             None => ClassExpr::expr_to_class_expr(
2584                 ctx.emitter,
2585                 true,
2586                 true,
2587                 &ast_scope::Scope::toplevel(),
2588                 ast::Expr(
2589                     Pos::make_none(),
2590                     ast::Expr_::mk_id(ast_defs::Id(Pos::make_none(), id.into())),
2591                 ),
2592             ),
2593             Some(body_env) => ClassExpr::expr_to_class_expr(
2594                 ctx.emitter,
2595                 true,
2596                 true,
2597                 &body_env.scope,
2598                 ast::Expr(
2599                     Pos::make_none(),
2600                     ast::Expr_::mk_id(ast_defs::Id(Pos::make_none(), id.into())),
2601                 ),
2602             ),
2603         };
2604         let name = match class_expr {
2605             ClassExpr::Id(ast_defs::Id(_, name)) => Cow::Owned(name),
2606             _ => Cow::Borrowed(id),
2607         };
2608         fmt_class_name(is_class_constant, name)
2609     }
2610     fn handle_possible_colon_colon_class_expr<W: Write>(
2611         ctx: &mut Context,
2612         w: &mut W,
2613         env: &ExprEnv,
2614         is_array_get: bool,
2615         e_: &ast::Expr_,
2616     ) -> Result<Option<()>, W::Error> {
2617         match e_.as_class_const() {
2618             Some((
2619                 ast::ClassId(_, ast::ClassId_::CIexpr(ast::Expr(_, ast::Expr_::Id(id)))),
2620                 (_, s2),
2621             )) if is_class(&s2) && !(is_self(&id.1) || is_parent(&id.1) || is_static(&id.1)) => {
2622                 Ok(Some({
2623                     let s1 =
2624                         &get_class_name_from_id(ctx, env.codegen_env, false, false, &id.1).into();
2625                     if is_array_get {
2626                         print_expr_id(w, env, s1)?
2627                     } else {
2628                         print_expr_string(w, s1)?
2629                     }
2630                 }))
2631             }
2632             _ => Ok(None),
2633         }
2634     }
2635     use ast::Expr_ as E_;
2636     match expr {
2637         E_::Id(id) => print_expr_id(w, env, &id.1),
2638         E_::Lvar(lid) => w.write(escaper::escape(&(lid.1).1)),
2639         E_::Float(f) => {
2640             if f.contains('E') || f.contains('e') {
2641                 let s = format!(
2642                     "{:.1E}",
2643                     f.parse::<f64>()
2644                         .map_err(|_| Error::fail(format!("ParseFloatError: {}", f)))?
2645                 )
2646                 // to_uppercase() here because s might be "inf" or "nan"
2647                 .to_uppercase();
2649                 lazy_static! {
2650                     static ref UNSIGNED_EXP : Regex = Regex::new(r"(?P<first>E)(?P<second>\d+)").unwrap();
2651                     static ref SIGNED_SINGLE_DIGIT_EXP: Regex = Regex::new(r"(?P<first>E[+-])(?P<second>\d$)").unwrap();
2652                 }
2653                 // turn mEn into mE+n
2654                 let s = UNSIGNED_EXP.replace(&s, "${first}+${second}");
2655                 // turn mE+n or mE-n into mE+0n or mE-0n (where n is a single digit)
2656                 let s = SIGNED_SINGLE_DIGIT_EXP.replace(&s, "${first}0${second}");
2657                 w.write(s)
2658             } else {
2659                 w.write(f)
2660             }
2661         }
2662         E_::Int(i) => print_expr_int(w, i),
2663         E_::String(s) => print_expr_string(w, s),
2664         E_::Null => w.write("NULL"),
2665         E_::True => w.write("true"),
2666         E_::False => w.write("false"),
2667         // For arrays and collections, we are making a conscious decision to not
2668         // match HHMV has HHVM's emitter has inconsistencies in the pretty printer
2669         // https://fburl.com/tzom2qoe
2670         E_::Array(afl) => wrap_by_(w, "array(", ")", |w| print_afields(ctx, w, env, afl)),
2671         E_::Collection(c) if (c.0).1 == "vec" || (c.0).1 == "dict" || (c.0).1 == "keyset" => {
2672             w.write(&(c.0).1)?;
2673             square(w, |w| print_afields(ctx, w, env, &c.2))
2674         }
2675         E_::Collection(c) => {
2676             let name = strip_ns((c.0).1.as_str());
2677             let name = types::fix_casing(&name);
2678             match name {
2679                 "Set" | "Pair" | "Vector" | "Map" | "ImmSet" | "ImmVector" | "ImmMap" => {
2680                     w.write("HH\\\\")?;
2681                     w.write(name)?;
2682                     wrap_by_(w, " {", "}", |w| {
2683                         Ok(if !c.2.is_empty() {
2684                             w.write(" ")?;
2685                             print_afields(ctx, w, env, &c.2)?;
2686                             w.write(" ")?;
2687                         })
2688                     })
2689                 }
2690                 _ => Err(Error::fail(format!(
2691                     "Default value for an unknow collection - {}",
2692                     name
2693                 ))),
2694             }
2695         }
2696         E_::Shape(fl) => print_expr_darray(ctx, w, env, print_shape_field_name, fl),
2697         E_::Binop(x) => {
2698             let (bop, e1, e2) = &**x;
2699             print_expr(ctx, w, env, e1)?;
2700             w.write(" ")?;
2701             print_bop(w, bop)?;
2702             w.write(" ")?;
2703             print_expr(ctx, w, env, e2)
2704         }
2705         E_::Call(c) => {
2706             let (_, e, _, es, unpacked_element) = &**c;
2707             match e.as_id() {
2708                 Some(ast_defs::Id(_, call_id)) => {
2709                     w.write(lstrip(adjust_id(env, &call_id).as_ref(), "\\\\"))?
2710                 }
2711                 None => {
2712                     let buf = print_expr_to_string::<W>(ctx, env, e)?;
2713                     w.write(lstrip(&buf, "\\\\"))?
2714                 }
2715             };
2716             paren(w, |w| {
2717                 concat_by(w, ", ", &es, |w, e| print_expr(ctx, w, env, e))?;
2718                 match unpacked_element {
2719                     None => Ok(()),
2720                     Some(e) => {
2721                         if es.len() > 0 {
2722                             w.write(", ")?;
2723                         }
2724                         // TODO: Should probably have ... also but we are not doing that in ocaml
2725                         print_expr(ctx, w, env, e)
2726                     }
2727                 }
2728             })
2729         }
2730         E_::New(x) => {
2731             let (cid, _, es, unpacked_element, _) = &**x;
2732             match cid.1.as_ciexpr() {
2733                 Some(ci_expr) => {
2734                     w.write("new ")?;
2735                     match ci_expr.1.as_id() {
2736                         Some(ast_defs::Id(_, cname)) => w.write(lstrip(
2737                             &adjust_id(
2738                                 env,
2739                                 &class::Type::from_ast_name(cname).to_raw_string().into(),
2740                             ),
2741                             "\\\\",
2742                         ))?,
2743                         None => {
2744                             let buf = print_expr_to_string::<W>(ctx, env, ci_expr)?;
2745                             w.write(lstrip(&buf, "\\\\"))?
2746                         }
2747                     }
2748                     paren(w, |w| {
2749                         concat_by(w, ", ", es, |w, e| print_expr(ctx, w, env, e))?;
2750                         match unpacked_element {
2751                             None => Ok(()),
2752                             Some(e) => {
2753                                 w.write(", ")?;
2754                                 print_expr(ctx, w, env, e)
2755                             }
2756                         }
2757                     })
2758                 }
2759                 None => not_impl!(),
2760             }
2761         }
2762         E_::Record(r) => {
2763             w.write(lstrip(adjust_id(env, &(r.0).1).as_ref(), "\\\\"))?;
2764             print_key_values(ctx, w, env, &r.1)
2765         }
2766         E_::ClassGet(cg) => {
2767             match &(cg.0).1 {
2768                 ast::ClassId_::CIexpr(e) => match e.as_id() {
2769                     Some(id) => w.write(&get_class_name_from_id(
2770                         ctx,
2771                         env.codegen_env,
2772                         true,  /* should_format */
2773                         false, /* is_class_constant */
2774                         &id.1,
2775                     ))?,
2776                     _ => print_expr(ctx, w, env, e)?,
2777                 },
2778                 _ => return Err(Error::fail("TODO Unimplemented unexpected non-CIexpr")),
2779             }
2780             w.write("::")?;
2781             match &cg.1 {
2782                 ast::ClassGetExpr::CGstring((_, litstr)) => w.write(escaper::escape(litstr)),
2783                 ast::ClassGetExpr::CGexpr(e) => print_expr(ctx, w, env, e),
2784             }
2785         }
2786         E_::ClassConst(cc) => {
2787             if let Some(e1) = (cc.0).1.as_ciexpr() {
2788                 handle_possible_colon_colon_class_expr(ctx, w, env, false, expr)?.map_or_else(
2789                     || {
2790                         let s2 = &(cc.1).1;
2791                         match e1.1.as_id() {
2792                             Some(ast_defs::Id(_, s1)) => {
2793                                 let s1 =
2794                                     get_class_name_from_id(ctx, env.codegen_env, true, true, s1);
2795                                 concat_str_by(w, "::", [&s1.into(), s2])
2796                             }
2797                             _ => {
2798                                 print_expr(ctx, w, env, e1)?;
2799                                 w.write("::")?;
2800                                 w.write(s2)
2801                             }
2802                         }
2803                     },
2804                     |x| Ok(x),
2805                 )
2806             } else {
2807                 Err(Error::fail("TODO: Only expected CIexpr in class_const"))
2808             }
2809         }
2810         E_::Unop(u) => match u.0 {
2811             ast::Uop::Upincr => {
2812                 print_expr(ctx, w, env, &u.1)?;
2813                 w.write("++")
2814             }
2815             ast::Uop::Updecr => {
2816                 print_expr(ctx, w, env, &u.1)?;
2817                 w.write("--")
2818             }
2819             _ => {
2820                 print_uop(w, u.0)?;
2821                 print_expr(ctx, w, env, &u.1)
2822             }
2823         },
2824         E_::ObjGet(og) => {
2825             print_expr(ctx, w, env, &og.0)?;
2826             w.write(match og.2 {
2827                 ast::OgNullFlavor::OGNullthrows => "->",
2828                 ast::OgNullFlavor::OGNullsafe => "\\?->",
2829             })?;
2830             print_expr(ctx, w, env, &og.1)
2831         }
2832         E_::Clone(e) => {
2833             w.write("clone ")?;
2834             print_expr(ctx, w, env, e)
2835         }
2836         E_::ArrayGet(ag) => {
2837             print_expr(ctx, w, env, &ag.0)?;
2838             square(w, |w| {
2839                 option(w, &ag.1, |w, e: &ast::Expr| {
2840                     handle_possible_colon_colon_class_expr(ctx, w, env, true, &e.1)
2841                         .transpose()
2842                         .unwrap_or_else(|| print_expr(ctx, w, env, e))
2843                 })
2844             })
2845         }
2846         E_::String2(ss) => concat_by(w, " . ", ss, |w, s| print_expr(ctx, w, env, s)),
2847         E_::PrefixedString(s) => {
2848             w.write(&s.0)?;
2849             w.write(" . ")?;
2850             print_expr(ctx, w, env, &s.1)
2851         }
2852         E_::Eif(eif) => {
2853             print_expr(ctx, w, env, &eif.0)?;
2854             w.write(" \\? ")?;
2855             option(w, &eif.1, |w, etrue| print_expr(ctx, w, env, etrue))?;
2856             w.write(" : ")?;
2857             print_expr(ctx, w, env, &eif.2)
2858         }
2859         E_::BracedExpr(e) => braces(w, |w| print_expr(ctx, w, env, e)),
2860         E_::ParenthesizedExpr(e) => paren(w, |w| print_expr(ctx, w, env, e)),
2861         E_::Cast(c) => {
2862             paren(w, |w| print_hint(w, false, &c.0))?;
2863             print_expr(ctx, w, env, &c.1)
2864         }
2865         E_::Pipe(p) => {
2866             print_expr(ctx, w, env, &p.1)?;
2867             w.write(" |> ")?;
2868             print_expr(ctx, w, env, &p.2)
2869         }
2870         E_::Is(i) => {
2871             print_expr(ctx, w, env, &i.0)?;
2872             w.write(" is ")?;
2873             print_hint(w, true, &i.1)
2874         }
2875         E_::As(a) => {
2876             print_expr(ctx, w, env, &a.0)?;
2877             w.write(if a.2 { " ?as " } else { " as " })?;
2878             print_hint(w, true, &a.1)
2879         }
2880         E_::Varray(va) => print_expr_varray(ctx, w, env, &va.1),
2881         E_::Darray(da) => print_expr_darray(ctx, w, env, print_expr, &da.1),
2882         E_::List(l) => wrap_by_(w, "list(", ")", |w| {
2883             concat_by(w, ", ", l, |w, i| print_expr(ctx, w, env, i))
2884         }),
2885         E_::Yield(y) => {
2886             w.write("yield ")?;
2887             print_afield(ctx, w, env, y)
2888         }
2889         E_::Await(e) => {
2890             w.write("await ")?;
2891             print_expr(ctx, w, env, e)
2892         }
2893         E_::YieldBreak => w.write("return"),
2894         E_::Import(i) => {
2895             print_import_flavor(w, &i.0)?;
2896             w.write(" ")?;
2897             print_expr(ctx, w, env, &i.1)
2898         }
2899         E_::Xml(x) => print_xml(ctx, w, env, &(x.0).1, &x.1, &x.2),
2900         E_::Efun(f) => print_efun(ctx, w, env, &f.0, &f.1),
2901         E_::Omitted => Ok(()),
2902         E_::Lfun(_) => Err(Error::fail(
2903             "expected Lfun to be converted to Efun during closure conversion print_expr",
2904         )),
2905         E_::Suspend(_) | E_::Callconv(_) | E_::ExprList(_) => {
2906             Err(Error::fail("illegal default value"))
2907         },
2908         _ => Err(Error::fail(
2909             "TODO Unimplemented: We are missing a lot of cases in the case match. Delete this catchall"
2910         ))
2911     }
2914 fn print_xml<W: Write>(
2915     ctx: &mut Context,
2916     w: &mut W,
2917     env: &ExprEnv,
2918     id: &str,
2919     attrs: &[ast::XhpAttribute],
2920     children: &[ast::Expr],
2921 ) -> Result<(), W::Error> {
2922     fn print_xhp_attr<W: Write>(
2923         ctx: &mut Context,
2924         w: &mut W,
2925         env: &ExprEnv,
2926         attr: &ast::XhpAttribute,
2927         spread_id: usize,
2928     ) -> Result<(), W::Error> {
2929         let key_printer = |_: &mut Context, w: &mut W, _: &ExprEnv, k| print_expr_string(w, k);
2930         match attr {
2931             ast::XhpAttribute::XhpSimple((_, s), e) => {
2932                 print_key_value_(ctx, w, env, s, key_printer, e)
2933             }
2934             ast::XhpAttribute::XhpSpread(e) => {
2935                 print_key_value_(ctx, w, env, &format!("...${}", spread_id), key_printer, e)
2936             }
2937         }
2938     }
2939     let env = ExprEnv {
2940         codegen_env: env.codegen_env,
2941         is_xhp: true,
2942     };
2943     write!(w, "new {}", mangle(id.into()))?;
2944     paren(w, |w| {
2945         wrap_by_(w, "darray[", "]", |w| {
2946             concat_by(w, ", ", attrs, |w, attr| {
2947                 print_xhp_attr(ctx, w, &env, attr, 0)
2948             })
2949         })?;
2950         w.write(", ")?;
2951         print_expr_varray(ctx, w, &env, children)?;
2952         w.write(", __FILE__, __LINE__")
2953     })
2956 fn print_efun<W: Write>(
2957     ctx: &mut Context,
2958     w: &mut W,
2959     env: &ExprEnv,
2960     f: &ast::Fun_,
2961     use_list: &[ast::Lid],
2962 ) -> Result<(), W::Error> {
2963     w.write_if(f.static_, "static ")?;
2964     w.write_if(
2965         f.fun_kind.is_fasync() || f.fun_kind.is_fasync_generator(),
2966         "async ",
2967     )?;
2968     w.write("function ")?;
2969     paren(w, |w| {
2970         concat_by(w, ", ", &f.params, |w, p| print_fparam(ctx, w, env, p))
2971     })?;
2972     w.write(" ")?;
2973     if !use_list.is_empty() {
2974         w.write("use ")?;
2975         paren(w, |w| {
2976             concat_by(w, ", ", use_list, |w: &mut W, ast::Lid(_, id)| {
2977                 w.write(local_id::get_name(id))
2978             })
2979         })?;
2980         w.write(" ")?;
2981     }
2982     print_block_(ctx, w, env, &f.body.ast, None)
2985 fn print_block<W: Write>(
2986     ctx: &mut Context,
2987     w: &mut W,
2988     env: &ExprEnv,
2989     block: &ast::Block,
2990     ident: Option<&str>,
2991 ) -> Result<(), W::Error> {
2992     match &block[..] {
2993         [] | [ast::Stmt(_, ast::Stmt_::Noop)] => Ok(()),
2994         [ast::Stmt(_, ast::Stmt_::Block(b))] if matches!(&b[..], [_]) => {
2995             print_block_(ctx, w, env, b, ident)
2996         }
2997         [_, _, ..] => print_block_(ctx, w, env, block, ident),
2998         [stmt] => print_statement(ctx, w, env, stmt, None),
2999     }
3002 fn print_block_<W: Write>(
3003     ctx: &mut Context,
3004     w: &mut W,
3005     env: &ExprEnv,
3006     block: &ast::Block,
3007     ident: Option<&str>,
3008 ) -> Result<(), W::Error> {
3009     wrap_by_(w, "{\\n", "}\\n", |w| {
3010         concat(w, block, |w, stmt| {
3011             option(w, ident, |w, i: &str| w.write(i))?;
3012             print_statement(ctx, w, env, stmt, Some("  "))
3013         })?;
3014         option(w, ident, |w, i: &str| w.write(i))
3015     })
3018 fn print_statement<W: Write>(
3019     ctx: &mut Context,
3020     w: &mut W,
3021     env: &ExprEnv,
3022     stmt: &ast::Stmt,
3023     ident: Option<&str>,
3024 ) -> Result<(), W::Error> {
3025     use ast::Stmt_ as S_;
3026     match &stmt.1 {
3027         S_::Return(expr) => {
3028             option(w, ident, |w, i: &str| w.write(i))?;
3029             wrap_by_(w, "return", ";\\n", |w| {
3030                 option(w, &**expr, |w, e| {
3031                     w.write(" ")?;
3032                     print_expr(ctx, w, env, e)
3033                 })
3034             })
3035         }
3036         S_::Expr(expr) => {
3037             option(w, ident, |w, i: &str| w.write(i))?;
3038             print_expr(ctx, w, env, &**expr)?;
3039             w.write(";\\n")
3040         }
3041         S_::Throw(expr) => {
3042             option(w, ident, |w, i: &str| w.write(i))?;
3043             wrap_by_(w, "throw ", ";\\n", |w| print_expr(ctx, w, env, &**expr))
3044         }
3045         S_::Break => {
3046             option(w, ident, |w, i: &str| w.write(i))?;
3047             w.write("break;\\n")
3048         }
3049         S_::Continue => {
3050             option(w, ident, |w, i: &str| w.write(i))?;
3051             w.write("continue;\\n")
3052         }
3053         S_::While(x) => {
3054             let (cond, block) = &**x;
3055             option(w, ident, |w, i: &str| w.write(i))?;
3056             wrap_by_(w, "while (", ") ", |w| print_expr(ctx, w, env, cond))?;
3057             print_block(ctx, w, env, block, ident)
3058         }
3059         S_::If(x) => {
3060             let (cond, if_block, else_block) = &**x;
3061             option(w, ident, |w, i: &str| w.write(i))?;
3062             wrap_by_(w, "if (", ") ", |w| print_expr(ctx, w, env, cond))?;
3063             print_block(ctx, w, env, if_block, ident)?;
3064             let mut buf = String::new();
3065             print_block(ctx, &mut buf, env, else_block, ident).map_err(|e| match e {
3066                 Error::NotImpl(m) => Error::NotImpl(m),
3067                 _ => Error::Fail(format!("Failed: {}", e)),
3068             })?;
3069             w.write_if(!buf.is_empty(), " else ")?;
3070             w.write_if(!buf.is_empty(), buf)
3071         }
3072         S_::Block(block) => {
3073             option(w, ident, |w, i: &str| w.write(i))?;
3074             print_block_(ctx, w, env, block, ident)
3075         }
3076         S_::Noop => Ok(()),
3077         /* TODO(T29869930) */
3078         _ => w.write("TODO Unimplemented NYI: Default value printing"),
3079     }
3082 fn print_fparam<W: Write>(
3083     ctx: &mut Context,
3084     w: &mut W,
3085     env: &ExprEnv,
3086     param: &ast::FunParam,
3087 ) -> Result<(), W::Error> {
3088     if let Some(ast_defs::ParamKind::Pinout) = param.callconv {
3089         w.write("inout ")?;
3090     }
3091     if param.is_variadic {
3092         w.write("...")?;
3093     }
3094     option(w, &(param.type_hint).1, |w, h| {
3095         print_hint(w, true, h)?;
3096         w.write(" ")
3097     })?;
3098     w.write(&param.name)?;
3099     option(w, &param.expr, |w, e| {
3100         w.write(" = ")?;
3101         print_expr(ctx, w, env, e)
3102     })
3105 fn print_bop<W: Write>(w: &mut W, bop: &ast_defs::Bop) -> Result<(), W::Error> {
3106     use ast_defs::Bop;
3107     match bop {
3108         Bop::Plus => w.write("+"),
3109         Bop::Minus => w.write("-"),
3110         Bop::Star => w.write("*"),
3111         Bop::Slash => w.write("/"),
3112         Bop::Eqeq => w.write("=="),
3113         Bop::Eqeqeq => w.write("==="),
3114         Bop::Starstar => w.write("**"),
3115         Bop::Eq(None) => w.write("="),
3116         Bop::Eq(Some(bop)) => {
3117             w.write("=")?;
3118             print_bop(w, bop)
3119         }
3120         Bop::Ampamp => w.write("&&"),
3121         Bop::Barbar => w.write("||"),
3122         Bop::Lt => w.write("<"),
3123         Bop::Lte => w.write("<="),
3124         Bop::Cmp => w.write("<=>"),
3125         Bop::Gt => w.write(">"),
3126         Bop::Gte => w.write(">="),
3127         Bop::Dot => w.write("."),
3128         Bop::Amp => w.write("&"),
3129         Bop::Bar => w.write("|"),
3130         Bop::Ltlt => w.write("<<"),
3131         Bop::Gtgt => w.write(">>"),
3132         Bop::Percent => w.write("%"),
3133         Bop::Xor => w.write("^"),
3134         Bop::LogXor => w.write("xor"),
3135         Bop::Diff => w.write("!="),
3136         Bop::Diff2 => w.write("!=="),
3137         Bop::QuestionQuestion => w.write("\\?\\?"),
3138     }
3141 fn print_hint<W: Write>(w: &mut W, ns: bool, hint: &ast::Hint) -> Result<(), W::Error> {
3142     let h = emit_type_hint::fmt_hint(&[], false, hint).map_err(|e| match e {
3143         Unrecoverable(s) => Error::fail(s),
3144         _ => Error::fail("Error printing hint"),
3145     })?;
3146     if ns {
3147         w.write(escaper::escape(h))
3148     } else {
3149         w.write(escaper::escape(strip_ns(&h)))
3150     }
3153 fn print_import_flavor<W: Write>(w: &mut W, flavor: &ast::ImportFlavor) -> Result<(), W::Error> {
3154     use ast::ImportFlavor as F;
3155     w.write(match flavor {
3156         F::Include => "include",
3157         F::Require => "require",
3158         F::IncludeOnce => "include_once",
3159         F::RequireOnce => "require_once",
3160     })
3163 fn print_param_user_attributes<W: Write>(
3164     ctx: &mut Context,
3165     w: &mut W,
3166     param: &HhasParam,
3167 ) -> Result<(), W::Error> {
3168     match &param.user_attributes[..] {
3169         [] => Ok(()),
3170         _ => square(w, |w| print_attributes(ctx, w, &param.user_attributes)),
3171     }
3174 fn print_span<W: Write>(w: &mut W, &Span(line_begin, line_end): &Span) -> Result<(), W::Error> {
3175     write!(w, "({},{})", line_begin, line_end)
3178 fn print_fun_attrs<W: Write>(
3179     ctx: &mut Context,
3180     w: &mut W,
3181     f: &HhasFunction,
3182 ) -> Result<(), W::Error> {
3183     use hhas_attribute::*;
3184     let user_attrs = &f.attributes;
3185     let mut special_attrs = vec![];
3186     if let Ok(attr) = f.rx_level.try_into() {
3187         special_attrs.push(attr);
3188     }
3189     if has_meth_caller(user_attrs) {
3190         special_attrs.push("builtin");
3191         special_attrs.push("is_meth_caller");
3192     }
3193     if has_no_context(user_attrs) {
3194         special_attrs.push("no_context");
3195     }
3196     if f.is_interceptable() {
3197         special_attrs.push("interceptable");
3198     }
3199     if has_foldable(user_attrs) {
3200         special_attrs.push("foldable");
3201     }
3202     if has_provenance_skip_frame(user_attrs) {
3203         special_attrs.push("prov_skip_frame");
3204     }
3205     if f.is_no_injection() {
3206         special_attrs.push("no_injection");
3207     }
3208     if !f.is_top() {
3209         special_attrs.push("nontop");
3210     }
3211     if ctx.is_system_lib() || (has_dynamically_callable(user_attrs) && !f.is_memoize_impl()) {
3212         special_attrs.push("dyn_callable")
3213     }
3214     if ctx.is_system_lib() {
3215         special_attrs.push("unique");
3216         special_attrs.push("builtin");
3217         special_attrs.push("persistent");
3218     }
3219     print_special_and_user_attrs(ctx, w, &special_attrs, user_attrs)
3222 fn print_special_and_user_attrs<W: Write>(
3223     ctx: &mut Context,
3224     w: &mut W,
3225     specials: &[&str],
3226     users: &[HhasAttribute],
3227 ) -> Result<(), W::Error> {
3228     if !users.is_empty() || !specials.is_empty() {
3229         square(w, |w| {
3230             concat_str_by(w, " ", specials)?;
3231             if !specials.is_empty() && !users.is_empty() {
3232                 w.write(" ")?;
3233             }
3234             print_attributes(ctx, w, users)
3235         })?;
3236         w.write(" ")?;
3237     }
3238     Ok(())
3241 fn print_upper_bounds<W: Write>(
3242     w: &mut W,
3243     ubs: impl AsRef<[(String, Vec<HhasTypeInfo>)]>,
3244 ) -> Result<(), W::Error> {
3245     braces(w, |w| concat_by(w, ", ", ubs, print_upper_bound))
3248 fn print_upper_bound<W: Write>(
3249     w: &mut W,
3250     (id, tys): &(String, Vec<HhasTypeInfo>),
3251 ) -> Result<(), W::Error> {
3252     paren(w, |w| {
3253         concat_str_by(w, " ", [id.as_str(), "as", ""])?;
3254         concat_by(w, ", ", &tys, print_type_info)
3255     })
3258 fn print_type_info<W: Write>(w: &mut W, ti: &HhasTypeInfo) -> Result<(), W::Error> {
3259     print_type_info_(w, false, ti)
3262 fn print_type_flags<W: Write>(w: &mut W, flag: constraint::Flags) -> Result<(), W::Error> {
3263     let mut first = true;
3264     let mut print_space = |w: &mut W| -> Result<(), W::Error> {
3265         if !first {
3266             w.write(" ")
3267         } else {
3268             Ok(first = false)
3269         }
3270     };
3271     use constraint::Flags as F;
3272     if flag.contains(F::DISPLAY_NULLABLE) {
3273         print_space(w)?;
3274         w.write("display_nullable")?;
3275     }
3276     if flag.contains(F::EXTENDED_HINT) {
3277         print_space(w)?;
3278         w.write("extended_hint")?;
3279     }
3280     if flag.contains(F::NULLABLE) {
3281         print_space(w)?;
3282         w.write("nullable")?;
3283     }
3285     if flag.contains(F::SOFT) {
3286         print_space(w)?;
3287         w.write("soft")?;
3288     }
3289     if flag.contains(F::TYPE_CONSTANT) {
3290         print_space(w)?;
3291         w.write("type_constant")?;
3292     }
3294     if flag.contains(F::TYPE_VAR) {
3295         print_space(w)?;
3296         w.write("type_var")?;
3297     }
3299     if flag.contains(F::UPPERBOUND) {
3300         print_space(w)?;
3301         w.write("upper_bound")?;
3302     }
3303     Ok(())
3306 fn print_type_info_<W: Write>(w: &mut W, is_enum: bool, ti: &HhasTypeInfo) -> Result<(), W::Error> {
3307     let print_quote_str = |w: &mut W, opt: &Option<String>| {
3308         option_or(
3309             w,
3310             opt,
3311             |w, s: &String| quotes(w, |w| w.write(escape(s))),
3312             "N",
3313         )
3314     };
3315     angle(w, |w| {
3316         print_quote_str(w, &ti.user_type)?;
3317         w.write(" ")?;
3318         if !is_enum {
3319             print_quote_str(w, &ti.type_constraint.name)?;
3320             w.write(" ")?;
3321         }
3322         print_type_flags(w, ti.type_constraint.flags)
3323     })
3326 fn print_typedef_info<W: Write>(w: &mut W, ti: &HhasTypeInfo) -> Result<(), W::Error> {
3327     angle(w, |w| {
3328         w.write(quote_string(
3329             ti.type_constraint.name.as_ref().map_or("", |n| n.as_str()),
3330         ))?;
3331         let flags = ti.type_constraint.flags & constraint::Flags::NULLABLE;
3332         if !flags.is_empty() {
3333             wrap_by(w, " ", |w| {
3334                 print_type_flags(w, ti.type_constraint.flags & constraint::Flags::NULLABLE)
3335             })?;
3336         }
3337         Ok(())
3338     })
3341 fn print_extends<W: Write>(w: &mut W, base: Option<&str>) -> Result<(), W::Error> {
3342     match base {
3343         None => Ok(()),
3344         Some(b) => concat_str_by(w, " ", [" extends", b]),
3345     }
3348 fn print_record_field<W: Write>(
3349     ctx: &mut Context,
3350     w: &mut W,
3351     Field(name, type_info, intial_value): &Field,
3352 ) -> Result<(), W::Error> {
3353     ctx.newline(w)?;
3354     w.write(".property ")?;
3355     match intial_value {
3356         Some(_) => w.write("[public] ")?,
3357         None => w.write("[public sys_initial_val] ")?,
3358     }
3359     print_type_info(w, type_info)?;
3360     concat_str_by(w, " ", ["", name, "="])?;
3362     ctx.block(w, |c, w| {
3363         c.newline(w)?;
3364         match intial_value {
3365             None => w.write("uninit")?,
3366             Some(value) => triple_quotes(w, |w| print_adata(c, w, value))?,
3367         }
3368         w.write(";")
3369     })
3372 fn print_record_def<W: Write>(
3373     ctx: &mut Context,
3374     w: &mut W,
3375     record: &HhasRecord,
3376 ) -> Result<(), W::Error> {
3377     newline(w)?;
3378     if record.is_abstract {
3379         concat_str_by(w, " ", [".record", record.name.to_raw_string()])?;
3380     } else {
3381         concat_str_by(w, " ", [".record", "[final]", record.name.to_raw_string()])?;
3382     }
3383     print_extends(w, record.base.as_ref().map(|b| b.to_raw_string()))?;
3384     w.write(" ")?;
3386     braces(w, |w| {
3387         ctx.block(w, |c, w| {
3388             concat(w, &record.fields, |w, rf| print_record_field(c, w, rf))
3389         })?;
3390         ctx.newline(w)
3391     })?;
3392     newline(w)