1 // Copyright (c) Facebook, Inc. and its affiliates.
3 // This source code is licensed under the MIT license found in the
4 // LICENSE file in the "hack" directory of this source tree.
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;
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;
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};
54 use runtime::TypedValue;
57 use std::{borrow::Cow, collections::BTreeSet, io::Write as _, path::Path, 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.
69 pub fn new() -> Self {
73 pub fn inc(&mut self) {
77 pub fn dec(&mut self) {
81 pub fn write<W: Write>(&self, w: &mut W) -> Result<(), W::Error> {
82 Ok(for _ in 0..self.0 {
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,
98 impl<'a> Context<'a> {
100 emitter: &'a mut Emitter,
101 path: Option<&'a RelativePath>,
102 dump_symbol_refs: bool,
110 indent: Indent::new(),
115 pub fn dump_symbol_refs(&self) -> bool {
116 self.dump_symbol_refs
119 /// Insert a newline with indentation
120 pub fn newline<W: Write>(&self, w: &mut W) -> Result<(), W::Error> {
125 /// Start a new indented block
126 pub fn block<W, F>(&mut self, w: &mut W, f: F) -> Result<(), W::Error>
129 F: FnOnce(&mut Self, &mut W) -> Result<(), W::Error>,
137 pub fn unblock<W, F>(&mut self, w: &mut W, f: F) -> Result<(), W::Error>
140 F: FnOnce(&mut Self, &mut W) -> Result<(), W::Error>,
148 /// Printing instruction list requies manually control indentation,
149 /// where indent_inc/indent_dec are called
150 pub fn indent_inc(&mut self) {
154 pub fn indent_dec(&mut self) {
158 pub fn is_system_lib(&self) -> bool {
165 pub codegen_env: Option<&'e BodyEnv<'e>>,
169 pub fn print_program<W: Write>(
173 ) -> Result<(), W::Error> {
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"])?;
184 concat_str(w, [".filepath ", format!("\"{}\"", p).as_str(), ";"])?;
187 handle_not_impl(|| print_program_(ctx, w, prog))?;
190 concat_str_by(w, " ", ["#", p.as_ref(), "ends here"])?;
195 w.write("#starts here")?;
198 handle_not_impl(|| print_program_(ctx, w, prog))?;
201 w.write("#ends here")?;
208 fn get_fatal_op(f: &FatalOp) -> &str {
210 FatalOp::Parse => "Parse",
211 FatalOp::Runtime => "Runtime",
212 FatalOp::RuntimeOmitFrame => "RuntimeOmitFrame",
216 fn print_program_<W: Write>(
220 ) -> Result<(), W::Error> {
221 if let Some((fop, p, msg)) = &prog.fatal {
223 let (line_begin, line_end, col_begin, col_end) = if p.is_none() || !p.is_valid() {
226 p.info_pos_extended()
228 let pos = format!("{}:{},{}:{}", line_begin, col_begin, line_end, col_end);
237 escape(msg).as_ref(),
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)?;
259 fn print_include_region<W: Write>(
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))
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(()) }
270 fn print_include<W: Write>(
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
281 .and_then(|p| p.path().parent())
282 .unwrap_or(Path::new(""))
284 if path_from_cur_dirname.exists() {
285 print_path(w, &path_from_cur_dirname)
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);
291 return print_path(w, &path);
297 IncludePath::IncludeRootRelative(v, p) => {
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)
307 .collect::<Result<_, _>>()?
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)
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())?;
331 fn print_symbol_ref_regions<W: Write>(
334 symbol_refs: &HhasSymbolRefs,
335 ) -> Result<(), W::Error> {
336 let mut print_region = |name, refs: &BTreeSet<String>| {
337 if !refs.is_empty() {
339 write!(w, ".{} {{", name)?;
340 ctx.block(w, |c, w| {
341 for s in refs.iter() {
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>(
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))?;
368 fn print_typedef<W: Write>(ctx: &mut Context, w: &mut W, td: &HhasTypedef) -> Result<(), W::Error> {
371 print_typedef_attributes(ctx, w, td)?;
372 w.write(td.name.to_raw_string())?;
374 print_typedef_info(w, &td.type_info)?;
376 print_span(w, &td.span)?;
378 triple_quotes(w, |w| print_adata(ctx, w, &td.type_structure))?;
382 fn print_typedef_attributes<W: Write>(
386 ) -> Result<(), W::Error> {
387 let mut specials = vec![];
388 if ctx.is_system_lib() {
389 specials.push("persistent");
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> {
397 Err(Error::NotImpl(msg)) => {
398 println!("#### NotImpl: {}", msg);
399 eprintln!("NotImpl: {}", msg);
406 fn print_fun_def<W: Write>(
409 fun_def: &HhasFunction,
410 ) -> Result<(), W::Error> {
411 let body = &fun_def.body;
413 w.write(".function ")?;
414 print_upper_bounds(w, &body.upper_bounds)?;
416 print_fun_attrs(ctx, w, fun_def)?;
417 print_span(w, &fun_def.span)?;
419 option(w, &body.return_type_info, |w, ti| {
420 print_type_info(w, ti)?;
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")?;
428 if fun_def.is_async() {
429 w.write(" isAsync")?;
431 if fun_def.is_pair_generator() {
432 w.write(" isPairGenerator")?;
434 if fun_def.rx_disabled() {
435 w.write(" isRxDisabled")?;
439 ctx.block(w, |c, w| {
440 print_body(c, w, body, &fun_def.attributes, &fun_def.coeffects)
448 fn print_requirement<W: Write>(
451 r: &(class::Type<'_>, hhas_class::TraitReqKind),
452 ) -> Result<(), W::Error> {
454 w.write(".require ")?;
456 (name, hhas_class::TraitReqKind::MustExtend) => {
457 write!(w, "extends <{}>;", name.to_raw_string())
459 (name, hhas_class::TraitReqKind::MustImplement) => {
460 write!(w, "implements <{}>;", name.to_raw_string())
465 fn print_type_constant<W: Write>(
468 c: &HhasTypeConstant,
469 ) -> Result<(), W::Error> {
471 concat_str_by(w, " ", [".const", &c.name, "isType"])?;
472 option(w, &c.initializer, |w, init| {
474 triple_quotes(w, |w| print_adata(ctx, w, init))
479 fn print_ctx_constant<W: Write>(
483 ) -> Result<(), W::Error> {
484 if let Some(coeffects) = HhasCoeffects::vec_to_string(&c.coeffects, |c| c.to_string()) {
486 concat_str_by(w, " ", [".ctx", &c.name, coeffects.as_ref()])?;
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))?;
500 fn print_property_attributes<W: Write>(
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")
509 if property.is_no_bad_redeclare() {
510 special_attributes.push("no_bad_redeclare")
512 if property.initial_satisfies_tc() {
513 special_attributes.push("initial_satisfies_tc")
515 if property.no_implicit_null() {
516 special_attributes.push("no_implicit_null")
518 if property.has_system_initial() {
519 special_attributes.push("sys_initial_val")
521 if property.is_const() {
522 special_attributes.push("is_const")
524 if property.is_deep_init() {
525 special_attributes.push("deep_init")
527 if property.is_lsb() {
528 special_attributes.push("lsb")
530 if property.is_static() {
531 special_attributes.push("static")
533 special_attributes.push(property.visibility.as_ref());
534 special_attributes.reverse();
537 concat_by(w, " ", &special_attributes, |w, a| w.write(a))?;
538 if !special_attributes.is_empty() && !property.attributes.is_empty() {
541 print_attributes(ctx, w, &property.attributes)?;
545 fn print_property_type_info<W: Write>(w: &mut W, p: &HhasProperty) -> Result<(), W::Error> {
546 print_type_info(w, &p.type_info)?;
550 fn print_property<W: Write>(
553 class_def: &HhasClass,
554 property: &HhasProperty,
555 ) -> Result<(), W::Error> {
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())?;
563 let initial_value = property.initial_value.as_ref();
564 if class_def.is_closure() || initial_value == Some(&TypedValue::Uninit) {
567 triple_quotes(w, |w| match initial_value {
568 None => w.write("N;"),
569 Some(value) => print_adata(ctx, w, &value),
575 fn print_constant<W: Write>(
579 ) -> Result<(), W::Error> {
582 w.write(c.name.to_raw_string())?;
583 match c.value.as_ref() {
584 Some(TypedValue::Uninit) => w.write(" = uninit")?,
587 triple_quotes(w, |w| print_adata(ctx, w, value))?;
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() {
597 w.write(".enum_ty ")?;
598 print_type_info_(w, true, et)?;
604 fn print_doc_comment<W: Write>(
607 doc_comment: &Option<DocComment>,
608 ) -> Result<(), W::Error> {
609 if let Some(cmt) = doc_comment {
611 write!(w, ".doc {};", triple_quote_string(&(cmt.0).1))?;
616 fn print_use_precedence<W: Write>(
619 (id1, id2, ids): &(class::Type, class::Type, Vec<class::Type>),
620 ) -> Result<(), W::Error> {
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<_>>())?;
629 fn print_use_as_visibility<W: Write>(w: &mut W, u: ast::UseAsVisibility) -> Result<(), W::Error> {
631 ast::UseAsVisibility::UseAsPublic => "public",
632 ast::UseAsVisibility::UseAsPrivate => "private",
633 ast::UseAsVisibility::UseAsProtected => "protected",
634 ast::UseAsVisibility::UseAsFinal => "final",
638 fn print_use_alias<W: Write>(
641 (ido1, id, ido2, kindl): &(
645 &Vec<ast::UseAsVisibility>,
647 ) -> Result<(), W::Error> {
649 let id = id.to_raw_string();
653 |w, i: &class::Type| concat_str(w, [i.to_raw_string(), "::", id]),
657 if !kindl.is_empty() {
659 concat_by(w, " ", kindl, |w, k| print_use_as_visibility(w, *k))
662 w.write_if(!kindl.is_empty() && ido2.is_some(), " ")?;
663 option(w, ido2, |w, i: &class::Type| w.write(i.to_raw_string()))?;
667 fn print_uses<W: Write>(ctx: &mut Context, w: &mut W, c: &HhasClass) -> Result<(), W::Error> {
668 if c.uses.is_empty() {
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();
676 concat_by(w, " ", unique_ids, |w, id| w.write(id))?;
678 if c.use_aliases.is_empty() && c.use_precedences.is_empty() {
682 ctx.block(w, |ctx, w| {
683 for x in &c.use_precedences {
684 print_use_precedence(ctx, w, x)?;
686 for x in &c.use_aliases {
687 print_use_alias(ctx, w, x)?;
697 fn print_class_special_attributes<W: Write>(
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")
709 if c.no_dynamic_props() {
710 special_attributes.push("no_dynamic_props")
713 special_attributes.push("is_const")
715 if hhas_attribute::has_foldable(user_attrs) {
716 special_attributes.push("foldable")
718 if hhas_attribute::has_enum_class(user_attrs) {
719 special_attributes.push("enum_class")
722 special_attributes.extend(&["persistent", "builtin", "unique"])
724 if hhas_attribute::has_dynamically_constructible(user_attrs) {
725 special_attributes.push("dyn_constructible");
727 if c.is_closure() && !is_system_lib {
728 special_attributes.push("unique");
731 special_attributes.push("no_override");
734 special_attributes.push("trait");
736 if c.is_interface() {
737 special_attributes.push("interface");
740 special_attributes.push("final");
743 special_attributes.push("sealed");
745 if c.enum_type.is_some() {
746 special_attributes.push("enum");
749 special_attributes.push("abstract");
751 if special_attributes.is_empty() && user_attrs.is_empty() {
755 special_attributes.reverse();
756 wrap_by_(w, "[", "] ", |w| {
757 concat_by(w, " ", &special_attributes, |w, a| w.write(a))?;
759 !special_attributes.is_empty() && !user_attrs.is_empty(),
762 print_attributes(ctx, w, &user_attrs)
766 fn print_implements<W: Write>(
768 implements: &Vec<class::Type<'_>>,
769 ) -> Result<(), W::Error> {
770 if implements.is_empty() {
773 w.write(" implements (")?;
779 .map(|x| x.to_raw_string())
780 .collect::<Vec<_>>(),
785 fn print_enum_includes<W: Write>(
787 enum_includes: &Vec<class::Type<'_>>,
788 ) -> Result<(), W::Error> {
789 if enum_includes.is_empty() {
792 w.write(" enum_includes (")?;
798 .map(|x| x.to_raw_string())
799 .collect::<Vec<_>>(),
804 fn print_shadowed_tparams<W: Write>(
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>(
814 method_def: &HhasMethod,
815 ) -> Result<(), W::Error> {
816 let body = &method_def.body;
818 w.write(" .method ")?;
819 print_shadowed_tparams(w, &body.shadowed_tparams)?;
820 print_upper_bounds(w, &body.upper_bounds)?;
822 print_method_attrs(ctx, w, method_def)?;
823 print_span(w, &method_def.span)?;
825 option(w, &body.return_type_info, |w, t| {
826 print_type_info(w, t)?;
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")?;
834 if method_def.flags.contains(HhasMethodFlags::IS_ASYNC) {
835 w.write(" isAsync")?;
839 .contains(HhasMethodFlags::IS_PAIR_GENERATOR)
841 w.write(" isPairGenerator")?;
843 if method_def.flags.contains(HhasMethodFlags::IS_CLOSURE_BODY) {
844 w.write(" isClosureBody")?;
846 if method_def.flags.contains(HhasMethodFlags::RX_DISABLED) {
847 w.write(" isRxDisabled")?;
851 ctx.block(w, |c, w| {
852 print_body(c, w, body, &method_def.attributes, &method_def.coeffects)
859 fn print_method_attrs<W: Write>(
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")
870 if m.is_interceptable() {
871 special_attrs.push("interceptable");
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");
878 if m.flags.contains(HhasMethodFlags::IS_FINAL) {
879 special_attrs.push("final");
881 if m.flags.contains(HhasMethodFlags::IS_ABSTRACT) {
882 special_attrs.push("abstract");
884 if has_foldable(user_attrs) {
885 special_attrs.push("foldable");
887 if m.is_no_injection() {
888 special_attrs.push("no_injection");
890 if ctx.is_system_lib() && has_native(user_attrs) && !is_native_opcode_impl(user_attrs) {
891 special_attrs.push("unique");
893 if ctx.is_system_lib() {
894 special_attrs.push("builtin");
896 if ctx.is_system_lib() && has_native(user_attrs) && !is_native_opcode_impl(user_attrs) {
897 special_attrs.push("persistent");
899 if ctx.is_system_lib() || (has_dynamically_callable(user_attrs) && !m.is_memoize_impl()) {
900 special_attrs.push("dyn_callable")
902 print_special_and_user_attrs(ctx, w, &special_attrs, user_attrs)
905 fn print_class_def<W: Write>(
908 class_def: &HhasClass,
909 ) -> Result<(), W::Error> {
912 print_upper_bounds(w, &class_def.upper_bounds)?;
914 print_class_special_attributes(ctx, w, class_def)?;
915 w.write(class_def.name.to_raw_string())?;
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)?;
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)?;
929 for x in &class_def.constants {
930 print_constant(c, w, x)?;
932 for x in &class_def.type_constants {
933 print_type_constant(c, w, x)?;
935 for x in &class_def.ctx_constants {
936 print_ctx_constant(c, w, x)?;
938 for x in &class_def.properties {
939 print_property(c, w, class_def, x)?;
941 for m in &class_def.methods {
942 print_method_def(c, w, m)?;
951 fn print_pos_as_prov_tag<W: Write>(
954 loc: &Option<ast_defs::Pos>,
955 ) -> Result<(), W::Error> {
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)",
969 quote_string_with_escape(filename)
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> {
984 fn print_class_id<W: Write>(w: &mut W, id: &ClassId) -> Result<(), W::Error> {
988 fn print_method_id<W: Write>(w: &mut W, id: &MethodId) -> Result<(), W::Error> {
992 fn print_const_id<W: Write>(w: &mut W, id: &ConstId) -> Result<(), W::Error> {
996 fn print_prop_id<W: Write>(w: &mut W, id: &PropId) -> Result<(), W::Error> {
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>(
1008 loc: &Option<ast_defs::Pos>,
1011 ) -> Result<(), W::Error>
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)?;
1023 fn print_adata_collection_argument<W: Write>(
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>(
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)
1046 fn print_adata<W: Write>(ctx: &mut Context, w: &mut W, tv: &TypedValue) -> Result<(), W::Error> {
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 {
1063 print_adata_dict_collection_argument(ctx, w, prefix, loc, pairs)
1065 TypedValue::Vec((values, loc, is_legacy)) => {
1066 let prefix = if *is_legacy {
1071 print_adata_collection_argument(ctx, w, prefix, loc, values)
1073 TypedValue::DArray((pairs, loc)) => {
1074 print_adata_dict_collection_argument(ctx, w, DARRAY_PREFIX, loc, pairs)
1076 TypedValue::Keyset(values) => {
1077 print_adata_collection_argument(ctx, w, KEYSET_PREFIX, &None, values)
1079 TypedValue::VArray((values, loc)) => {
1080 print_adata_collection_argument(ctx, w, VARRAY_PREFIX, loc, values)
1082 TypedValue::HhasAdata(s) => w.write(escaped(s)),
1086 fn print_attribute<W: Write>(
1090 ) -> Result<(), W::Error> {
1093 "\"{}\"(\"\"\"{}:{}:{{",
1098 concat(w, &a.arguments, |w, arg| print_adata(ctx, w, arg))?;
1102 fn print_attributes<W: Write>(
1105 al: impl AsRef<[HhasAttribute]>,
1106 ) -> Result<(), W::Error> {
1107 // Adjust for underscore coming before alphabet
1108 let al: Vec<&HhasAttribute> = al
1111 .sorted_by_key(|a| (!a.name.starts_with("__"), &a.name))
1113 concat_by(w, " ", &al, |w, a| print_attribute(ctx, w, a))
1116 fn print_file_attributes<W: Write>(
1119 al: &Vec<HhasAttribute>,
1120 ) -> Result<(), W::Error> {
1125 w.write(".file_attributes [")?;
1126 print_attributes(ctx, w, al)?;
1131 fn is_bareword_char(c: &u8) -> bool {
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'),
1138 fn print_body<W: Write>(
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 {
1148 w.write(".ismemoizewrapper;")?;
1150 if body.is_memoize_wrapper_lsb {
1152 w.write(".ismemoizewrapperlsb;")?;
1154 if body.num_iters > 0 {
1156 write!(w, ".numiters {};", body.num_iters)?;
1158 if !body.decl_vars.is_empty() {
1160 w.write(".declvars ")?;
1161 concat_by(w, " ", &body.decl_vars, |w, var| {
1162 if var.as_bytes().iter().all(is_bareword_char) {
1165 quotes(w, |w| w.write(escaper::escape(var)))
1170 for s in HhasCoeffects::coeffects_to_hhas(&coeffects).iter() {
1174 for i in body.rx_cond_rx_of_arg.iter() {
1176 concat_str(w, [".coeffects_fun_param ", i.to_string().as_ref(), ";"])?;
1178 if let Some((_, s)) = extract_rx_if_impl_attr(0, attrs) {
1180 concat_str(w, [".rx_cond_implements \"", escape(&s).as_ref(), "\";"])?;
1182 for (i, s) in body.rx_cond_arg_implements.iter() {
1187 ".rx_cond_arg_implements ",
1188 i.to_string().as_ref(),
1195 print_instructions(ctx, w, &body.body_instrs)
1198 fn print_instructions<W: Write>(
1201 instr_seq: &InstrSeq,
1202 ) -> Result<(), W::Error> {
1205 for instr in instr_seq.compact_iter() {
1207 ISpecialFlow(_) => return Err(Error::fail("Cannot break/continue 1 level")),
1211 print_instr(w, instr)?;
1213 ILabel(_) => ctx.unblock(w, |c, w| {
1215 print_instr(w, instr)
1217 ITry(TryCatchBegin) => {
1219 print_instr(w, instr)?;
1222 ITry(TryCatchMiddle) => ctx.unblock(w, |c, w| {
1224 print_instr(w, instr)
1226 ITry(TryCatchEnd) => {
1229 print_instr(w, instr)?;
1233 print_instr(w, instr)?;
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>(
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")
1255 angle(w, |w| concat_str_by(w, " ", flags))?;
1257 print_int(w, num_args)?;
1259 print_int(w, num_rets)?;
1262 concat_by(w, "", inouts, |w, i| w.write(if *i { "1" } else { "0" }))
1265 option_or(w, async_eager_label, print_label, "-")?;
1268 Some(s) => quotes(w, |w| w.write(s)),
1269 None => w.write("\"\""),
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",
1281 fn print_null_flavor<W: Write>(w: &mut W, f: &ObjNullFlavor) -> Result<(), W::Error> {
1283 ObjNullFlavor::NullThrows => "NullThrows",
1284 ObjNullFlavor::NullSafe => "NullSafe",
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;
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)
1298 I::NewObjRD(cid) => {
1299 w.write("NewObjRD ")?;
1300 print_class_id(w, cid)
1303 w.write("NewObjS ")?;
1304 print_special_cls_ref(w, r)
1306 I::FCall(fcall_args) => {
1308 print_fcall_args(w, &fcall_args)?;
1309 w.write(r#" "" """#)
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",
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)?;
1326 print_method_id(w, mid)
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)
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)?;
1340 print_method_id(w, mid)
1342 I::FCallCtor(fcall_args) => {
1343 w.write("FCallCtor ")?;
1344 print_fcall_args(w, fcall_args)?;
1347 I::FCallFunc(fcall_args) => {
1348 w.write("FCallFunc ")?;
1349 print_fcall_args(w, fcall_args)
1351 I::FCallFuncD(fcall_args, id) => {
1352 w.write("FCallFuncD ")?;
1353 print_fcall_args(w, fcall_args)?;
1355 print_function_id(w, id)
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)
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)?;
1369 print_method_id(w, id)
1374 fn print_get<W: Write>(w: &mut W, get: &InstructGet) -> Result<(), W::Error> {
1375 use InstructGet as IG;
1381 IG::CGetQuietL(id) => {
1382 w.write("CGetQuietL ")?;
1386 w.write("CGetL2 ")?;
1390 w.write("CUGetL ")?;
1397 IG::CGetG => w.write("CGetG"),
1400 print_readonly_op(w, op)
1402 IG::ClassGetC => w.write("ClassGetC"),
1403 IG::ClassGetTS => w.write("ClassGetTS"),
1408 use InstructBasic as IB;
1410 IIterator(i) => print_iterator(w, i),
1411 IBasic(b) => w.write(match b {
1413 IB::EntryNop => "EntryNop",
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),
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!(
1436 ".srcloc {}:{},{}:{};",
1437 p.line_begin, p.col_begin, p.line_end, p.col_end
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")),
1446 fn print_base<W: Write>(w: &mut W, i: &InstructBase) -> Result<(), W::Error> {
1447 use InstructBase as I;
1449 I::BaseGC(si, m) => {
1450 w.write("BaseGC ")?;
1451 print_stack_index(w, si)?;
1453 print_member_opmode(w, m)
1455 I::BaseGL(id, m) => {
1456 w.write("BaseGL ")?;
1457 print_local(w, id)?;
1459 print_member_opmode(w, m)
1461 I::BaseSC(si1, si2, m, op) => {
1462 w.write("BaseSC ")?;
1463 print_stack_index(w, si1)?;
1465 print_stack_index(w, si2)?;
1467 print_member_opmode(w, m)?;
1469 print_readonly_op(w, op)
1471 I::BaseL(id, m) => {
1473 print_local(w, id)?;
1475 print_member_opmode(w, m)
1477 I::BaseC(si, m) => {
1479 print_stack_index(w, si)?;
1481 print_member_opmode(w, m)
1483 I::BaseH => w.write("BaseH"),
1486 print_member_opmode(w, m)?;
1488 print_member_key(w, mk)
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;
1500 M::ModeNone => "None",
1502 M::Define => "Define",
1503 M::Unset => "Unset",
1504 M::InOut => "InOut",
1508 fn print_member_key<W: Write>(w: &mut W, mk: &MemberKey) -> Result<(), W::Error> {
1513 print_stack_index(w, si)?;
1515 print_readonly_op(w, op)
1517 M::EL(local, op) => {
1519 print_local(w, local)?;
1521 print_readonly_op(w, op)
1525 quotes(w, |w| w.write(escape(s)))?;
1527 print_readonly_op(w, op)
1530 concat_str(w, ["EI:", i.to_string().as_ref()])?;
1532 print_readonly_op(w, op)
1536 print_stack_index(w, si)?;
1538 print_readonly_op(w, op)
1540 M::PL(local, op) => {
1542 print_local(w, local)?;
1544 print_readonly_op(w, op)
1548 print_prop_id(w, id)?;
1550 print_readonly_op(w, op)
1554 print_prop_id(w, id)?;
1556 print_readonly_op(w, op)
1558 M::W => w.write("W"),
1562 fn print_iterator<W: Write>(w: &mut W, i: &InstructIterator) -> Result<(), W::Error> {
1563 use InstructIterator as I;
1565 I::IterInit(iter_args, label) => {
1566 w.write("IterInit ")?;
1567 print_iter_args(w, iter_args)?;
1569 print_label(w, label)
1571 I::IterNext(iter_args, label) => {
1572 w.write("IterNext ")?;
1573 print_iter_args(w, iter_args)?;
1575 print_label(w, label)
1577 I::IterFree(id) => {
1578 w.write("IterFree ")?;
1579 print_iterator_id(w, id)
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)?;
1587 match &iter_args.key_id {
1588 None => w.write("NK")?,
1591 print_local(w, &k)?;
1596 print_local(w, &iter_args.val_id)
1599 fn print_iterator_id<W: Write>(w: &mut W, i: &IterId) -> Result<(), W::Error> {
1603 fn print_async<W: Write>(w: &mut W, a: &AsyncFunctions) -> Result<(), W::Error> {
1604 use AsyncFunctions as 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")),
1614 fn print_query_op<W: Write>(w: &mut W, q: QueryOp) -> Result<(), W::Error> {
1616 QueryOp::CGet => "CGet",
1617 QueryOp::CGetQuiet => "CGetQuiet",
1618 QueryOp::Isset => "Isset",
1619 QueryOp::InOut => "InOut",
1623 fn print_final<W: Write>(w: &mut W, f: &InstructFinal) -> Result<(), W::Error> {
1624 use InstructFinal as F;
1626 F::QueryM(n, op, mk) => {
1627 w.write("QueryM ")?;
1630 print_query_op(w, *op)?;
1632 print_member_key(w, mk)
1634 F::UnsetM(n, mk) => {
1635 w.write("UnsetM ")?;
1638 print_member_key(w, mk)
1644 print_member_key(w, mk)
1646 F::SetOpM(i, op, mk) => {
1647 w.write("SetOpM ")?;
1650 print_eq_op(w, &op)?;
1652 print_member_key(w, mk)
1654 F::IncDecM(i, op, mk) => {
1655 w.write("IncDecM ")?;
1658 print_incdec_op(w, &op)?;
1660 print_member_key(w, mk)
1662 F::SetRangeM(i, s, op, rop) => {
1663 w.write("SetRangeM ")?;
1666 print_int(w, &(*s as usize))?;
1669 SetrangeOp::Forward => "Forward",
1670 SetrangeOp::Reverse => "Reverse",
1673 print_readonly_op(w, rop)
1678 fn print_isset<W: Write>(w: &mut W, isset: &InstructIsset) -> Result<(), W::Error> {
1679 use InstructIsset as I;
1681 I::IssetC => w.write("IssetC"),
1682 I::IssetL(local) => {
1683 w.write("IssetL ")?;
1684 print_local(w, local)
1686 I::IsUnsetL(local) => {
1687 w.write("IsUnsetL ")?;
1688 print_local(w, local)
1690 I::IssetG => w.write("IssetG"),
1691 I::IssetS => w.write("IssetS"),
1693 w.write("IsTypeC ")?;
1694 print_istype_op(w, op)
1696 I::IsTypeL(local, op) => {
1697 w.write("IsTypeL ")?;
1698 print_local(w, local)?;
1700 print_istype_op(w, op)
1705 fn print_istype_op<W: Write>(w: &mut W, op: &IstypeOp) -> Result<(), W::Error> {
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"),
1730 fn print_try<W: Write>(w: &mut W, itry: &InstructTry) -> Result<(), W::Error> {
1731 use InstructTry as T;
1733 T::TryCatchBegin => w.write(".try {"),
1734 T::TryCatchMiddle => w.write("} .catch {"),
1735 T::TryCatchEnd => w.write("}"),
1739 fn print_mutator<W: Write>(w: &mut W, mutator: &InstructMutator) -> Result<(), W::Error> {
1740 use InstructMutator as M;
1744 print_local(w, local)
1750 M::SetG => w.write("SetG"),
1753 print_readonly_op(w, op)
1755 M::SetOpL(id, op) => {
1756 w.write("SetOpL ")?;
1757 print_local(w, id)?;
1762 w.write("SetOpG ")?;
1765 M::SetOpS(op, rop) => {
1766 w.write("SetOpS ")?;
1767 print_eq_op(w, op)?;
1769 print_readonly_op(w, rop)
1771 M::IncDecL(id, op) => {
1772 w.write("IncDecL ")?;
1773 print_local(w, id)?;
1775 print_incdec_op(w, op)
1778 w.write("IncDecG ")?;
1779 print_incdec_op(w, op)
1781 M::IncDecS(op, rop) => {
1782 w.write("IncDecS ")?;
1783 print_incdec_op(w, op)?;
1785 print_readonly_op(w, rop)
1788 w.write("UnsetL ")?;
1791 M::UnsetG => w.write("UnsetG"),
1792 M::CheckProp(id) => {
1793 w.write("CheckProp ")?;
1794 print_prop_id(w, id)
1796 M::InitProp(id, op, rop) => {
1797 w.write("InitProp ")?;
1798 print_prop_id(w, id)?;
1801 InitpropOp::Static => w.write("Static"),
1802 InitpropOp::NonStatic => w.write("NonStatic"),
1805 print_readonly_op(w, rop)
1810 fn print_eq_op<W: Write>(w: &mut W, op: &EqOp) -> Result<(), W::Error> {
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",
1830 fn print_readonly_op<W: Write>(w: &mut W, op: &ReadOnlyOp) -> Result<(), W::Error> {
1832 ReadOnlyOp::ReadOnly => "ReadOnly",
1833 ReadOnlyOp::Mutable => "Mutable",
1834 ReadOnlyOp::Any => "Any",
1838 fn print_incdec_op<W: Write>(w: &mut W, op: &IncdecOp) -> Result<(), W::Error> {
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",
1851 fn print_gen_creation_execution<W: Write>(
1853 gen: &GenCreationExecution,
1854 ) -> Result<(), W::Error> {
1855 use GenCreationExecution as G;
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"),
1871 fn print_misc<W: Write>(w: &mut W, misc: &InstructMisc) -> Result<(), W::Error> {
1872 use InstructMisc as M;
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)
1903 M::VerifyParamTypeTS(id) => {
1904 w.write("VerifyParamTypeTS ")?;
1905 print_param_id(w, id)
1907 M::Silence(local, op) => {
1908 w.write("Silence ")?;
1909 print_local(w, local)?;
1912 OpSilence::Start => w.write("Start"),
1913 OpSilence::End => w.write("End"),
1916 M::VerifyOutType(id) => {
1917 w.write("VerifyOutType ")?;
1918 print_param_id(w, id)
1920 M::CreateCl(n, cid) => concat_str_by(
1923 ["CreateCl", n.to_string().as_str(), cid.to_string().as_str()],
1925 M::BareThis(op) => concat_str_by(
1931 BareThisOp::Notice => "Notice",
1932 BareThisOp::NoNotice => "NoNotice",
1933 BareThisOp::NeverNull => "NeverNull",
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)
1943 M::MemoGet(label, None) => {
1944 w.write("MemoGet ")?;
1945 print_label(w, label)?;
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)
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)?;
1960 print_label(w, label2)?;
1961 write!(w, " L:{}+{}", first, local_count)
1963 M::MemoGetEager(label1, label2, None) => {
1964 w.write("MemoGetEager ")?;
1965 print_label(w, label1)?;
1967 print_label(w, label2)?;
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)
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(
1984 ClassKind::Class => "Class",
1985 ClassKind::Interface => "Interface",
1986 ClassKind::Trait => "Trait",
1990 M::AssertRATL(local, s) => {
1991 w.write("AssertRATL ")?;
1992 print_local(w, local)?;
1996 M::AssertRATStk(n, s) => {
1997 concat_str_by(w, " ", ["AssertRATStk", n.to_string().as_str(), s.as_str()])
1999 M::GetMemoKeyL(local) => {
2000 w.write("GetMemoKeyL ")?;
2001 print_local(w, local)
2006 fn print_include_eval_define<W: Write>(
2008 ed: &InstructIncludeEvalDefine,
2009 ) -> Result<(), W::Error> {
2010 use InstructIncludeEvalDefine::*;
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"),
2021 fn print_control_flow<W: Write>(w: &mut W, cf: &InstructControlFlow) -> Result<(), W::Error> {
2022 use InstructControlFlow as CF;
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 ")?;
2050 concat_by(w, " ", rest, |w, (s, l)| {
2051 concat_str(w, [quote_string(s).as_str(), ":"])?;
2055 print_label(w, lastlabel)
2062 fn print_switch<W: Write>(
2067 ) -> Result<(), W::Error> {
2068 w.write("Switch ")?;
2069 w.write(match kind {
2070 Switchkind::Bounded => "Bounded ",
2071 Switchkind::Unbounded => "Unbounded ",
2073 w.write(base.to_string())?;
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;
2081 LC::Null => w.write("Null"),
2082 LC::Int(i) => concat_str_by(w, " ", ["Int", i.to_string().as_str()]),
2084 w.write("String ")?;
2085 quotes(w, |w| w.write(escape(s)))
2087 LC::LazyClass(id) => {
2088 w.write("LazyClass ")?;
2089 print_class_id(w, id)
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"),
2103 print_adata_id(w, id)
2107 print_adata_id(w, id)
2110 w.write("Keyset ")?;
2111 print_adata_id(w, id)
2115 print_adata_id(w, id)
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))
2126 LC::NewStructDict(l) => {
2127 w.write("NewStructDict ")?;
2128 angle(w, |w| print_shape_fields(w, l))
2130 LC::NewRecord(cid, l) => {
2131 w.write("NewRecord ")?;
2132 print_class_id(w, cid)?;
2134 angle(w, |w| print_shape_fields(w, l))
2138 print_const_id(w, id)
2141 w.write("ClsCns ")?;
2142 print_const_id(w, id)
2144 LC::ClsCnsD(const_id, cid) => {
2145 w.write("ClsCnsD ")?;
2146 print_const_id(w, const_id)?;
2148 print_class_id(w, cid)
2150 LC::ClsCnsL(id) => {
2151 w.write("ClsCnsL ")?;
2155 w.write("NewCol ")?;
2156 print_collection_type(w, ct)
2158 LC::ColFromArray(ct) => {
2159 w.write("ColFromArray ")?;
2160 print_collection_type(w, ct)
2162 LC::NullUninit => w.write("NullUninit"),
2163 LC::TypedValue(_) => Err(Error::fail("print_lit_const: TypedValue")),
2167 fn print_collection_type<W: Write>(w: &mut W, ct: &CollectionType) -> Result<(), W::Error> {
2168 use CollectionType as 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"),
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;
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)
2237 I::IsLateBoundCls => w.write("IsLateBoundCls"),
2238 I::IsTypeStructC(op) => concat_str_by(
2244 TypestructResolveOp::Resolve => "Resolve",
2245 TypestructResolveOp::DontResolve => "DontResolve",
2249 I::ThrowAsTypeStructException => w.write("ThrowAsTypeStructException"),
2250 I::CombineAndResolveTypeStruct(n) => concat_str_by(
2253 ["CombineAndResolveTypeStruct", n.to_string().as_str()],
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)
2262 I::ResolveRFunc(id) => {
2263 w.write("ResolveRFunc ")?;
2264 print_function_id(w, id)
2266 I::ResolveMethCaller(id) => {
2267 w.write("ResolveMethCaller ")?;
2268 print_function_id(w, id)
2270 I::ResolveObjMethod => w.write("ResolveObjMethod"),
2271 I::ResolveClsMethod(mid) => {
2272 w.write("ResolveClsMethod ")?;
2273 print_method_id(w, mid)
2275 I::ResolveClsMethodD(cid, mid) => {
2276 w.write("ResolveClsMethodD ")?;
2277 print_class_id(w, cid)?;
2279 print_method_id(w, mid)
2281 I::ResolveClsMethodS(r, mid) => {
2282 w.write("ResolveClsMethodS ")?;
2283 print_special_cls_ref(w, r)?;
2285 print_method_id(w, mid)
2287 I::ResolveRClsMethod(mid) => {
2288 w.write("ResolveRClsMethod ")?;
2289 print_method_id(w, mid)
2291 I::ResolveRClsMethodD(cid, mid) => {
2292 w.write("ResolveRClsMethodD ")?;
2293 print_class_id(w, cid)?;
2295 print_method_id(w, mid)
2297 I::ResolveRClsMethodS(r, mid) => {
2298 w.write("ResolveRClsMethodS ")?;
2299 print_special_cls_ref(w, r)?;
2301 print_method_id(w, mid)
2303 I::ResolveClass(id) => {
2304 w.write("ResolveClass ")?;
2305 print_class_id(w, id)
2307 I::Fatal(fatal_op) => print_fatal_op(w, fatal_op),
2311 fn print_fatal_op<W: Write>(w: &mut W, f: &FatalOp) -> Result<(), W::Error> {
2313 FatalOp::Parse => w.write("Fatal Parse"),
2314 FatalOp::Runtime => w.write("Fatal Runtime"),
2315 FatalOp::RuntimeOmitFrame => w.write("Fatal RuntimeOmitFrame"),
2319 fn print_params<W: Write>(
2322 body_env: Option<&BodyEnv>,
2323 params: &[HhasParam],
2324 ) -> Result<(), W::Error> {
2326 concat_by(w, ", ", params, |w, i| print_param(ctx, w, body_env, i))
2330 fn print_param<W: Write>(
2333 body_env: Option<&BodyEnv>,
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, ¶m.type_info, |w, ty| {
2340 print_type_info(w, ty)?;
2343 w.write(¶m.name)?;
2344 option(w, ¶m.default_value, |w, i| {
2345 print_param_default_value(ctx, w, body_env, i)
2349 fn print_param_id<W: Write>(w: &mut W, param_id: &ParamId) -> Result<(), W::Error> {
2351 ParamId::ParamUnnamed(i) => w.write(i.to_string()),
2352 ParamId::ParamNamed(s) => w.write(s),
2356 fn print_param_default_value<W: Write>(
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,
2367 print_label(w, &default_val.0)?;
2369 triple_quotes(w, |w| print_expr(ctx, w, &expr_env, &default_val.1))
2373 fn print_label<W: Write>(w: &mut W, label: &Label) -> Result<(), W::Error> {
2375 Label::Regular(id) => {
2379 Label::DefaultArg(id) => {
2383 Label::Named(id) => w.write(id),
2387 fn print_local<W: Write>(w: &mut W, local: &Local) -> Result<(), W::Error> {
2389 Local::Unnamed(id) => {
2393 Local::Named(id) => w.write(id),
2397 fn print_int<W: Write>(w: &mut W, i: &usize) -> Result<(), W::Error> {
2401 fn print_key_value<W: Write>(
2407 ) -> Result<(), W::Error> {
2408 print_key_value_(ctx, w, env, k, print_expr, v)
2411 fn print_key_value_<W: Write, K, KeyPrinter>(
2418 ) -> Result<(), W::Error>
2420 KeyPrinter: FnMut(&mut Context, &mut W, &ExprEnv, K) -> Result<(), W::Error>,
2422 kp(ctx, w, env, k)?;
2424 print_expr(ctx, w, env, v)
2427 fn print_afield<W: Write>(
2431 afield: &ast::Afield,
2432 ) -> Result<(), W::Error> {
2433 use ast::Afield as A;
2435 A::AFvalue(e) => print_expr(ctx, w, env, &e),
2436 A::AFkvalue(k, v) => print_key_value(ctx, w, env, &k, &v),
2440 fn print_afields<W: Write>(
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> {
2459 U::Upincr | U::Updecr => {
2460 return Err(Error::fail(
2461 "string_of_uop - should have been captures earlier",
2467 fn print_key_values<W: Write>(
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>(
2481 kvs: impl AsRef<[(K, ast::Expr)]>,
2482 ) -> Result<(), W::Error>
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)
2491 fn print_expr_darray<W: Write, K, KeyPrinter>(
2496 kvs: impl AsRef<[(K, ast::Expr)]>,
2497 ) -> Result<(), W::Error>
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)
2506 fn print_expr_varray<W: Write>(
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))
2517 fn print_shape_field_name<W: Write>(
2521 field: &ast::ShapeFieldName,
2522 ) -> Result<(), W::Error> {
2523 use ast::ShapeFieldName as S;
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()),
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")),
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]>> {
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,
2551 write!(r, "\\\\{:03o}", c).unwrap();
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>(
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)),
2575 fn print_expr<W: Write>(
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 {
2584 if env.namespace.name.is_none()
2588 .rposition(|c| *c == b'\\')
2589 .map_or(true, |i| i < 1)
2591 strip_global_ns(id).into()
2600 fn print_expr_id<'a, W: Write>(
2604 ) -> Result<(), W::Error> {
2605 w.write(adjust_id(env, s))
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())))
2613 escaper::escape(strip_global_ns(&id)).to_string().into()
2615 if is_class_constant {
2616 format!("\\\\{}", cn).into()
2621 fn get_class_name_from_id<'e>(
2623 env: Option<&'e BodyEnv<'e>>,
2624 should_format: bool,
2625 is_class_constant: bool,
2628 if id == classes::SELF || id == classes::PARENT || id == classes::STATIC {
2629 return get_special_class_name(ctx, env, is_class_constant, id);
2631 fn get<'a>(should_format: bool, is_class_constant: bool, id: &'a str) -> Cow<'a, str> {
2633 fmt_class_name(is_class_constant, id.into())
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)
2646 get(should_format, is_class_constant, id)
2649 fn get_special_class_name<'e>(
2651 env: Option<&'e BodyEnv<'e>>,
2652 is_class_constant: bool,
2655 let class_expr = match env {
2656 None => ClassExpr::expr_to_class_expr(
2660 &ast_scope::Scope::toplevel(),
2663 ast::Expr_::mk_id(ast_defs::Id(Pos::make_none(), id.into())),
2666 Some(body_env) => ClassExpr::expr_to_class_expr(
2673 ast::Expr_::mk_id(ast_defs::Id(Pos::make_none(), id.into())),
2677 let name = match class_expr {
2678 ClassExpr::Id(ast_defs::Id(_, name)) => Cow::Owned(name),
2679 _ => Cow::Borrowed(id),
2681 fmt_class_name(is_class_constant, name)
2683 fn handle_possible_colon_colon_class_expr<W: Write>(
2689 ) -> Result<Option<()>, W::Error> {
2690 match e_.as_class_const() {
2692 ast::ClassId(_, ast::ClassId_::CIexpr(ast::Expr(_, ast::Expr_::Id(id)))),
2694 )) if is_class(&s2) && !(is_self(&id.1) || is_parent(&id.1) || is_static(&id.1)) => {
2697 &get_class_name_from_id(ctx, env.codegen_env, false, false, &id.1).into();
2699 print_expr_id(w, env, s1)?
2701 print_expr_string(w, s1.as_bytes())?
2708 use ast::Expr_ as E_;
2710 E_::Id(id) => print_expr_id(w, env, &id.1),
2711 E_::Lvar(lid) => w.write(escaper::escape(&(lid.1).1)),
2713 if f.contains('E') || f.contains('e') {
2717 .map_err(|_| Error::fail(format!("ParseFloatError: {}", f)))?
2719 // to_uppercase() here because s might be "inf" or "nan"
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();
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}");
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" => {
2747 square(w, |w| print_afields(ctx, w, env, &c.2))
2749 E_::Collection(c) => {
2750 let name = strip_ns((c.0).1.as_str());
2751 let name = types::fix_casing(&name);
2753 "Set" | "Pair" | "Vector" | "Map" | "ImmSet" | "ImmVector" | "ImmMap" => {
2756 wrap_by_(w, " {", "}", |w| {
2757 Ok(if !c.2.is_empty() {
2759 print_afields(ctx, w, env, &c.2)?;
2764 _ => Err(Error::fail(format!(
2765 "Default value for an unknow collection - {}",
2770 E_::Shape(fl) => print_expr_darray(ctx, w, env, print_shape_field_name, fl),
2772 let (bop, e1, e2) = &**x;
2773 print_expr(ctx, w, env, e1)?;
2777 print_expr(ctx, w, env, e2)
2780 let (e, _, es, unpacked_element) = &**c;
2782 Some(ast_defs::Id(_, call_id)) => {
2783 w.write(lstrip(adjust_id(env, &call_id).as_ref(), "\\\\"))?
2786 let buf = print_expr_to_string::<W>(ctx, env, e)?;
2787 w.write(lstrip(&buf, "\\\\"))?
2791 concat_by(w, ", ", &es, |w, e| print_expr(ctx, w, env, e))?;
2792 match unpacked_element {
2798 // TODO: Should probably have ... also but we are not doing that in ocaml
2799 print_expr(ctx, w, env, e)
2805 let (cid, _, es, unpacked_element, _) = &**x;
2806 match cid.1.as_ciexpr() {
2809 match ci_expr.1.as_id() {
2810 Some(ast_defs::Id(_, cname)) => w.write(lstrip(
2813 &class::Type::from_ast_name(cname).to_raw_string().into(),
2818 let buf = print_expr_to_string::<W>(ctx, env, ci_expr)?;
2819 w.write(lstrip(&buf, "\\\\"))?
2823 concat_by(w, ", ", es, |w, e| print_expr(ctx, w, env, e))?;
2824 match unpacked_element {
2828 print_expr(ctx, w, env, e)
2834 match cid.1.as_ci() {
2836 // Xml exprs rewritten as New exprs come
2838 print_xml(ctx, w, env, &id.1, es)
2840 None => not_impl!(),
2846 w.write(lstrip(adjust_id(env, &(r.0).1).as_ref(), "\\\\"))?;
2847 print_key_values(ctx, w, env, &r.1)
2849 E_::ClassGet(cg) => {
2851 ast::ClassId_::CIexpr(e) => match e.as_id() {
2852 Some(id) => w.write(&get_class_name_from_id(
2855 true, /* should_format */
2856 false, /* is_class_constant */
2859 _ => print_expr(ctx, w, env, e)?,
2861 _ => return Err(Error::fail("TODO Unimplemented unexpected non-CIexpr")),
2865 ast::ClassGetExpr::CGstring((_, litstr)) => w.write(escaper::escape(litstr)),
2866 ast::ClassGetExpr::CGexpr(e) => print_expr(ctx, w, env, e),
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(
2874 match e1.1.as_id() {
2875 Some(ast_defs::Id(_, s1)) => {
2877 get_class_name_from_id(ctx, env.codegen_env, true, true, s1);
2878 concat_str_by(w, "::", [&s1.into(), s2])
2881 print_expr(ctx, w, env, e1)?;
2890 Err(Error::fail("TODO: Only expected CIexpr in class_const"))
2893 E_::Unop(u) => match u.0 {
2894 ast::Uop::Upincr => {
2895 print_expr(ctx, w, env, &u.1)?;
2898 ast::Uop::Updecr => {
2899 print_expr(ctx, w, env, &u.1)?;
2904 print_expr(ctx, w, env, &u.1)
2908 print_expr(ctx, w, env, &og.0)?;
2909 w.write(match og.2 {
2910 ast::OgNullFlavor::OGNullthrows => "->",
2911 ast::OgNullFlavor::OGNullsafe => "\\?->",
2913 print_expr(ctx, w, env, &og.1)
2917 print_expr(ctx, w, env, e)
2919 E_::ArrayGet(ag) => {
2920 print_expr(ctx, w, env, &ag.0)?;
2922 option(w, &ag.1, |w, e: &ast::Expr| {
2923 handle_possible_colon_colon_class_expr(ctx, w, env, true, &e.1)
2925 .unwrap_or_else(|| print_expr(ctx, w, env, e))
2929 E_::String2(ss) => concat_by(w, " . ", ss, |w, s| print_expr(ctx, w, env, s)),
2930 E_::PrefixedString(s) => {
2933 print_expr(ctx, w, env, &s.1)
2936 print_expr(ctx, w, env, &eif.0)?;
2938 option(w, &eif.1, |w, etrue| print_expr(ctx, w, env, etrue))?;
2940 print_expr(ctx, w, env, &eif.2)
2943 paren(w, |w| print_hint(w, false, &c.0))?;
2944 print_expr(ctx, w, env, &c.1)
2947 print_expr(ctx, w, env, &p.1)?;
2949 print_expr(ctx, w, env, &p.2)
2952 print_expr(ctx, w, env, &i.0)?;
2954 print_hint(w, true, &i.1)
2957 print_expr(ctx, w, env, &a.0)?;
2958 w.write(if a.2 { " ?as " } else { " as " })?;
2959 print_hint(w, true, &a.1)
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))
2968 print_afield(ctx, w, env, y)
2972 print_expr(ctx, w, env, e)
2975 print_import_flavor(w, &i.0)?;
2977 print_expr(ctx, w, env, &i.1)
2979 E_::Xml(_) => Err(Error::fail(
2980 "expected Xml to be converted to New during rewriting",
2982 E_::Efun(f) => print_efun(ctx, w, env, &f.0, &f.1),
2983 E_::FunctionPointer(fp) => {
2984 let (fp_id, targs) = &**fp;
2986 ast::FunctionPtrId::FPId(ast::Id(_, sid)) => {
2987 w.write(lstrip(adjust_id(env, &sid).as_ref(), "\\\\"))?
2989 ast::FunctionPtrId::FPClassConst(ast::ClassId(_, class_id), (_, meth_name)) => {
2991 ast::ClassId_::CIexpr(e) => match e.as_id() {
2992 Some(id) => w.write(&get_class_name_from_id(
2995 true, /* should_format */
2996 false, /* is_class_constant */
2999 _ => print_expr(ctx, w, env, e)?,
3002 return Err(Error::fail(
3003 "TODO Unimplemented unexpected non-CIexpr in function pointer",
3011 wrap_by_(w, "<", ">", |w| {
3012 concat_by(w, ", ", targs, |w, _targ| w.write("_"))
3015 E_::Omitted => Ok(()),
3017 if ctx.dump_lambdas {
3021 concat_by(w, ", ", &fun_.params, |w, param| {
3022 print_fparam(ctx, w, env, param)
3026 print_block_(ctx, w, env, &fun_.body.ast, None)
3030 "expected Lfun to be converted to Efun during closure conversion print_expr",
3034 E_::Callconv(_) => Err(Error::fail("illegal default value")),
3035 E_::ETSplice(splice) => {
3037 print_expr(ctx, w, env, splice)?;
3040 _ => Err(Error::fail(format!(
3041 "TODO Unimplemented: Cannot print: {:?}",
3047 fn print_xml<W: Write>(
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"))
3059 fn print_xhp_attr<W: Write>(
3063 attr: &(ast_defs::ShapeFieldName, ast::Expr),
3064 ) -> Result<(), W::Error> {
3066 (ast_defs::ShapeFieldName::SFlitStr(s), e) => print_key_value_(
3071 |_, w, _, k| print_expr_string(w, k.as_slice()),
3074 _ => Err(syntax_error(w)),
3078 let (attrs, children) = if es.len() < 2 {
3079 Err(syntax_error(w))
3081 match (&es[0], &es[1]) {
3082 (E(_, E_::Shape(attrs)), E(_, E_::Varray(children))) => Ok((attrs, &children.1)),
3083 _ => Err(syntax_error(w)),
3087 codegen_env: env.codegen_env,
3090 write!(w, "new {}", mangle(id.into()))?;
3092 wrap_by_(w, "darray[", "]", |w| {
3093 concat_by(w, ", ", attrs, |w, attr| print_xhp_attr(ctx, w, &env, attr))
3096 print_expr_varray(ctx, w, &env, children)?;
3097 w.write(", __FILE__, __LINE__")
3101 fn print_efun<W: Write>(
3106 use_list: &[ast::Lid],
3107 ) -> Result<(), W::Error> {
3108 w.write_if(f.static_, "static ")?;
3110 f.fun_kind.is_fasync() || f.fun_kind.is_fasync_generator(),
3113 w.write("function ")?;
3115 concat_by(w, ", ", &f.params, |w, p| print_fparam(ctx, w, env, p))
3118 if !use_list.is_empty() {
3121 concat_by(w, ", ", use_list, |w: &mut W, ast::Lid(_, id)| {
3122 w.write(local_id::get_name(id))
3127 print_block_(ctx, w, env, &f.body.ast, None)
3130 fn print_block<W: Write>(
3135 ident: Option<&str>,
3136 ) -> Result<(), W::Error> {
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)
3142 [_, _, ..] => print_block_(ctx, w, env, block, ident),
3143 [stmt] => print_statement(ctx, w, env, stmt, None),
3147 fn print_block_<W: Write>(
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(" "))
3159 option(w, ident, |w, i: &str| w.write(i))
3163 fn print_statement<W: Write>(
3168 ident: Option<&str>,
3169 ) -> Result<(), W::Error> {
3170 use ast::Stmt_ as S_;
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| {
3177 print_expr(ctx, w, env, e)
3182 option(w, ident, |w, i: &str| w.write(i))?;
3183 print_expr(ctx, w, env, &**expr)?;
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))
3191 option(w, ident, |w, i: &str| w.write(i))?;
3192 w.write("break;\\n")
3195 option(w, ident, |w, i: &str| w.write(i))?;
3196 w.write("continue;\\n")
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)
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)),
3214 w.write_if(!buf.is_empty(), " else ")?;
3215 w.write_if(!buf.is_empty(), buf)
3217 S_::Block(block) => {
3218 option(w, ident, |w, i: &str| w.write(i))?;
3219 print_block_(ctx, w, env, block, ident)
3222 /* TODO(T29869930) */
3223 _ => w.write("TODO Unimplemented NYI: Default value printing"),
3227 fn print_fparam<W: Write>(
3231 param: &ast::FunParam,
3232 ) -> Result<(), W::Error> {
3233 if let Some(ast_defs::ParamKind::Pinout) = param.callconv {
3236 if param.is_variadic {
3239 option(w, &(param.type_hint).1, |w, h| {
3240 print_hint(w, true, h)?;
3243 w.write(¶m.name)?;
3244 option(w, ¶m.expr, |w, e| {
3246 print_expr(ctx, w, env, e)
3250 fn print_bop<W: Write>(w: &mut W, bop: &ast_defs::Bop) -> Result<(), W::Error> {
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)) => {
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("\\?\\?"),
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"),
3292 w.write(escaper::escape(h))
3294 w.write(escaper::escape(strip_ns(&h)))
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",
3308 fn print_param_user_attributes<W: Write>(
3312 ) -> Result<(), W::Error> {
3313 match ¶m.user_attributes[..] {
3315 _ => square(w, |w| print_attributes(ctx, w, ¶m.user_attributes)),
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>(
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");
3335 if has_no_context(user_attrs) {
3336 special_attrs.push("no_context");
3338 if f.is_interceptable() {
3339 special_attrs.push("interceptable");
3341 if has_foldable(user_attrs) {
3342 special_attrs.push("foldable");
3344 if has_provenance_skip_frame(user_attrs) {
3345 special_attrs.push("prov_skip_frame");
3347 if f.is_no_injection() {
3348 special_attrs.push("no_injection");
3350 if ctx.is_system_lib() || (has_dynamically_callable(user_attrs) && !f.is_memoize_impl()) {
3351 special_attrs.push("dyn_callable")
3353 if ctx.is_system_lib() {
3354 special_attrs.push("unique");
3355 special_attrs.push("builtin");
3356 special_attrs.push("persistent");
3358 print_special_and_user_attrs(ctx, w, &special_attrs, user_attrs)
3361 fn print_special_and_user_attrs<W: Write>(
3365 users: &[HhasAttribute],
3366 ) -> Result<(), W::Error> {
3367 if !users.is_empty() || !specials.is_empty() {
3369 concat_str_by(w, " ", specials)?;
3370 if !specials.is_empty() && !users.is_empty() {
3373 print_attributes(ctx, w, users)
3380 fn print_upper_bounds<W: Write>(
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>(
3389 (id, tys): &(String, Vec<HhasTypeInfo>),
3390 ) -> Result<(), W::Error> {
3392 concat_str_by(w, " ", [id.as_str(), "as", ""])?;
3393 concat_by(w, ", ", &tys, print_type_info)
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> {
3410 use constraint::Flags as F;
3411 if flag.contains(F::DISPLAY_NULLABLE) {
3413 w.write("display_nullable")?;
3415 if flag.contains(F::EXTENDED_HINT) {
3417 w.write("extended_hint")?;
3419 if flag.contains(F::NULLABLE) {
3421 w.write("nullable")?;
3424 if flag.contains(F::SOFT) {
3428 if flag.contains(F::TYPE_CONSTANT) {
3430 w.write("type_constant")?;
3433 if flag.contains(F::TYPE_VAR) {
3435 w.write("type_var")?;
3438 if flag.contains(F::UPPERBOUND) {
3440 w.write("upper_bound")?;
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>| {
3450 |w, s: &String| quotes(w, |w| w.write(escape(s))),
3455 print_quote_str(w, &ti.user_type)?;
3458 print_quote_str(w, &ti.type_constraint.name)?;
3461 print_type_flags(w, ti.type_constraint.flags)
3465 fn print_typedef_info<W: Write>(w: &mut W, ti: &HhasTypeInfo) -> Result<(), W::Error> {
3467 w.write(quote_string(
3468 ti.type_constraint.name.as_ref().map_or("", |n| n.as_str()),
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)
3480 fn print_extends<W: Write>(w: &mut W, base: Option<&str>) -> Result<(), W::Error> {
3483 Some(b) => concat_str_by(w, " ", [" extends", b]),
3487 fn print_record_field<W: Write>(
3490 Field(name, type_info, intial_value): &Field,
3491 ) -> Result<(), W::Error> {
3493 w.write(".property ")?;
3494 match intial_value {
3495 Some(_) => w.write("[public] ")?,
3496 None => w.write("[public sys_initial_val] ")?,
3498 print_type_info(w, type_info)?;
3499 concat_str_by(w, " ", ["", name, "="])?;
3501 ctx.block(w, |c, w| {
3503 match intial_value {
3504 None => w.write("uninit")?,
3505 Some(value) => triple_quotes(w, |w| print_adata(c, w, value))?,
3511 fn print_record_def<W: Write>(
3514 record: &HhasRecord,
3515 ) -> Result<(), W::Error> {
3517 if record.is_abstract {
3518 concat_str_by(w, " ", [".record", record.name.to_raw_string()])?;
3520 concat_str_by(w, " ", [".record", "[final]", record.name.to_raw_string()])?;
3523 print_span(w, &record.span)?;
3524 print_extends(w, record.base.as_ref().map(|b| b.to_raw_string()))?;
3528 ctx.block(w, |c, w| {
3529 concat(w, &record.fields, |w, rf| print_record_field(c, w, rf))
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;
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);