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