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