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.
6 mod special_class_resolver;
9 pub use context::Context;
10 pub use write::{Error, IoWrite, Result, Write};
12 use ffi::{Maybe, Maybe::*, Pair, Quadruple, Slice, Str, Triple};
14 use indexmap::IndexSet;
15 use itertools::Itertools;
17 use core_utils_rust::add_ns;
18 use escaper::{escape, escape_by, is_lit_printable};
19 use hhbc_by_ref_emit_type_hint as emit_type_hint;
20 use hhbc_by_ref_hhas_adata::{HhasAdata, DICT_PREFIX, KEYSET_PREFIX, VEC_PREFIX};
21 use hhbc_by_ref_hhas_attribute::{self as hhas_attribute, HhasAttribute};
22 use hhbc_by_ref_hhas_body::{HhasBody, HhasBodyEnv};
23 use hhbc_by_ref_hhas_class::{self as hhas_class, HhasClass};
24 use hhbc_by_ref_hhas_coeffects::{HhasCoeffects, HhasCtxConstant};
25 use hhbc_by_ref_hhas_constant::HhasConstant;
26 use hhbc_by_ref_hhas_function::HhasFunction;
27 use hhbc_by_ref_hhas_method::{HhasMethod, HhasMethodFlags};
28 use hhbc_by_ref_hhas_param::HhasParam;
29 use hhbc_by_ref_hhas_pos::{HhasPos, HhasSpan};
30 use hhbc_by_ref_hhas_program::HhasProgram;
31 use hhbc_by_ref_hhas_property::HhasProperty;
32 use hhbc_by_ref_hhas_record_def::{Field, HhasRecord};
33 use hhbc_by_ref_hhas_symbol_refs::{HhasSymbolRefs, IncludePath};
34 use hhbc_by_ref_hhas_type::{constraint, HhasTypeInfo};
35 use hhbc_by_ref_hhas_type_const::HhasTypeConstant;
36 use hhbc_by_ref_hhas_typedef::HhasTypedef;
37 use hhbc_by_ref_hhbc_ast::*;
38 use hhbc_by_ref_hhbc_id::{class::ClassType, Id};
39 use hhbc_by_ref_hhbc_string_utils::{
40 float, integer, is_class, is_parent, is_self, is_static, is_xhp, lstrip, mangle, quote_string,
41 quote_string_with_escape, strip_global_ns, strip_ns, triple_quote_string, types,
43 use hhbc_by_ref_instruction_sequence::{Error::Unrecoverable, InstrSeq};
44 use hhbc_by_ref_iterator::Id as IterId;
45 use hhbc_by_ref_label::Label;
46 use hhbc_by_ref_local::Local;
47 use hhbc_by_ref_runtime::TypedValue;
48 use lazy_static::lazy_static;
49 use naming_special_names_rust::classes;
50 use ocaml_helper::escaped;
51 use oxidized::{ast, ast_defs, local_id};
55 use std::{borrow::Cow, io::Write as _, path::Path, write};
57 pub struct ExprEnv<'arena, 'e> {
58 pub codegen_env: Option<&'e HhasBodyEnv<'arena>>,
61 pub fn print_program<W: Write>(
65 ) -> Result<(), W::Error> {
68 let abs = p.to_absolute();
69 let p = escape(abs.to_str().ok_or(Error::InvalidUTF8)?);
71 concat_str_by(w, " ", ["#", p.as_ref(), "starts here"])?;
76 concat_str(w, [".filepath ", format!("\"{}\"", p).as_str(), ";"])?;
79 handle_not_impl(|| print_program_(ctx, w, prog))?;
82 concat_str_by(w, " ", ["#", p.as_ref(), "ends here"])?;
87 w.write("#starts here")?;
90 handle_not_impl(|| print_program_(ctx, w, prog))?;
93 w.write("#ends here")?;
100 fn get_fatal_op(f: &FatalOp) -> &str {
102 FatalOp::Parse => "Parse",
103 FatalOp::Runtime => "Runtime",
104 FatalOp::RuntimeOmitFrame => "RuntimeOmitFrame",
108 fn print_program_<W: Write>(
112 ) -> Result<(), W::Error> {
113 if let Just(Triple(fop, p, msg)) = &prog.fatal {
121 let pos = format!("{}:{},{}:{}", line_begin, col_begin, line_end, col_end);
130 escape(msg).as_ref(),
137 concat(w, &prog.adata, |w, a| print_adata_region(ctx, w, a))?;
138 concat(w, &prog.functions, |w, f| print_fun_def(ctx, w, f))?;
139 concat(w, &prog.classes, |w, cd| print_class_def(ctx, w, cd))?;
140 concat(w, &prog.record_defs, |w, rd| print_record_def(ctx, w, rd))?;
141 concat(w, &prog.constants, |w, c| print_constant(ctx, w, c))?;
142 concat(w, &prog.typedefs, |w, td| print_typedef(ctx, w, td))?;
143 print_file_attributes(ctx, w, prog.file_attributes.as_ref())?;
145 if ctx.dump_symbol_refs() {
146 print_include_region(ctx, w, &prog.symbol_refs.includes)?;
147 print_symbol_ref_regions(ctx, w, &prog.symbol_refs)?;
152 fn print_include_region<W: Write>(
155 includes: &Slice<IncludePath>,
156 ) -> Result<(), W::Error> {
157 fn print_path<W: Write>(w: &mut W, p: &Path) -> Result<(), W::Error> {
158 option(w, p.to_str(), |w, p: &str| write!(w, "\n {}", p))
160 fn print_if_exists<W: Write>(w: &mut W, p: &Path) -> Result<(), W::Error> {
161 if p.exists() { print_path(w, p) } else { Ok(()) }
163 fn print_include<W: Write>(
167 ) -> Result<(), W::Error> {
168 let include_roots = ctx.include_roots;
169 let alloc = bumpalo::Bump::new();
170 match inc.into_doc_root_relative(&alloc, include_roots) {
171 IncludePath::Absolute(p) => print_if_exists(w, Path::new(&p.as_str())),
172 IncludePath::SearchPathRelative(p) => {
173 let path_from_cur_dirname = ctx
175 .and_then(|p| p.path().parent())
176 .unwrap_or_else(|| Path::new(""))
178 if path_from_cur_dirname.exists() {
179 print_path(w, &path_from_cur_dirname)
181 let search_paths = ctx.include_search_paths;
182 for prefix in search_paths.iter() {
183 let path = Path::new(prefix).join(&p.as_str());
185 return print_path(w, &path);
191 IncludePath::IncludeRootRelative(v, p) => {
194 .get(&v.as_str().to_owned())
197 let doc_root = ctx.doc_root;
198 let resolved = Path::new(doc_root).join(ir).join(&p.as_str());
199 print_if_exists(w, &resolved)
204 IncludePath::DocRootRelative(p) => {
205 let doc_root = ctx.doc_root;
206 let resolved = Path::new(doc_root).join(&p.as_str());
207 print_if_exists(w, &resolved)
211 if !includes.is_empty() {
212 w.write("\n.includes {")?;
213 for inc in includes.as_ref().iter() {
214 // TODO(hrust): avoid clone. Rethink onwership of inc in
215 // hhas_symbol_refs_rust::IncludePath::into_doc_root_relative
216 print_include(ctx, w, inc.clone())?;
224 fn print_symbol_ref_regions<'arena, W: Write>(
227 symbol_refs: &HhasSymbolRefs<'arena>,
228 ) -> Result<(), W::Error> {
229 let mut print_region = |name, refs: &Slice<'arena, Str<'arena>>| {
230 if !refs.is_empty() {
232 write!(w, ".{} {{", name)?;
233 ctx.block(w, |c, w| {
234 for s in refs.as_ref().iter() {
245 print_region("constant_refs", &symbol_refs.constants)?;
246 print_region("function_refs", &symbol_refs.functions)?;
247 print_region("class_refs", &symbol_refs.classes)
250 fn print_adata_region<W: Write>(
254 ) -> Result<(), W::Error> {
255 concat_str_by(w, " ", [".adata", adata.id.as_str(), "= "])?;
256 triple_quotes(w, |w| print_adata(ctx, w, &adata.value))?;
261 fn print_typedef<W: Write>(ctx: &mut Context, w: &mut W, td: &HhasTypedef) -> Result<(), W::Error> {
264 print_typedef_attributes(ctx, w, td)?;
265 w.write(td.name.to_raw_string())?;
267 print_typedef_info(w, &td.type_info)?;
269 print_span(w, &td.span)?;
271 triple_quotes(w, |w| print_adata(ctx, w, &td.type_structure))?;
275 fn print_typedef_attributes<W: Write>(
279 ) -> Result<(), W::Error> {
280 let mut specials = vec![];
281 if ctx.is_system_lib() {
282 specials.push("persistent");
284 print_special_and_user_attrs(ctx, w, &specials[..], td.attributes.as_ref())
287 fn handle_not_impl<E: std::fmt::Debug, F: FnOnce() -> Result<(), E>>(f: F) -> Result<(), E> {
290 Err(Error::NotImpl(msg)) => {
291 println!("#### NotImpl: {}", msg);
292 eprintln!("NotImpl: {}", msg);
299 fn print_fun_def<W: Write>(
302 fun_def: &HhasFunction,
303 ) -> Result<(), W::Error> {
304 let body = &fun_def.body;
306 w.write(".function ")?;
307 print_upper_bounds_(w, &body.upper_bounds)?;
309 print_fun_attrs(ctx, w, fun_def)?;
310 print_span(w, &fun_def.span)?;
314 &(Option::from(body.return_type_info.clone())),
316 print_type_info(w, ti)?;
320 w.write(fun_def.name.to_raw_string())?;
321 print_params(ctx, w, fun_def.params())?;
322 if fun_def.is_generator() {
323 w.write(" isGenerator")?;
325 if fun_def.is_async() {
326 w.write(" isAsync")?;
328 if fun_def.is_pair_generator() {
329 w.write(" isPairGenerator")?;
331 if fun_def.is_readonly_return() {
332 w.write(" isReadonlyReturn")?;
336 ctx.block(w, |c, w| print_body(c, w, body, &fun_def.coeffects))?;
343 fn print_requirement<W: Write>(
346 r: &Pair<ClassType<'_>, hhas_class::TraitReqKind>,
347 ) -> Result<(), W::Error> {
349 w.write(".require ")?;
351 Pair(name, hhas_class::TraitReqKind::MustExtend) => {
352 write!(w, "extends <{}>;", name.to_raw_string())
354 Pair(name, hhas_class::TraitReqKind::MustImplement) => {
355 write!(w, "implements <{}>;", name.to_raw_string())
360 fn print_type_constant<W: Write>(
363 c: &HhasTypeConstant,
364 ) -> Result<(), W::Error> {
366 concat_str_by(w, " ", [".const", c.name.as_str(), "isType"])?;
368 w.write(" isAbstract")?;
370 option(w, Option::from(c.initializer.as_ref()), |w, init| {
372 triple_quotes(w, |w| print_adata(ctx, w, init))
377 fn print_ctx_constant<W: Write>(
381 ) -> Result<(), W::Error> {
383 concat_str_by(w, " ", [".ctx", &c.name])?;
385 w.write(" isAbstract")?;
387 if let Some(coeffects) = HhasCoeffects::vec_to_string(&c.coeffects.0, |c| c.to_string()) {
391 if let Some(coeffects) = HhasCoeffects::vec_to_string(&c.coeffects.1, |c| c.to_string()) {
399 fn print_property_doc_comment<W: Write>(w: &mut W, p: &HhasProperty) -> Result<(), W::Error> {
400 if let Just(s) = p.doc_comment.as_ref() {
401 w.write(triple_quote_string(s.as_str()))?;
407 fn print_property_attributes<W: Write>(
410 property: &HhasProperty,
411 ) -> Result<(), W::Error> {
412 let mut special_attributes = vec![];
413 if property.is_late_init() {
414 special_attributes.push("late_init")
416 if property.is_no_bad_redeclare() {
417 special_attributes.push("no_bad_redeclare")
419 if property.initial_satisfies_tc() {
420 special_attributes.push("initial_satisfies_tc")
422 if property.no_implicit_null() {
423 special_attributes.push("no_implicit_null")
425 if property.has_system_initial() {
426 special_attributes.push("sys_initial_val")
428 if property.is_const() {
429 special_attributes.push("is_const")
431 if property.is_readonly() {
432 special_attributes.push("readonly")
434 if property.is_deep_init() {
435 special_attributes.push("deep_init")
437 if property.is_lsb() {
438 special_attributes.push("lsb")
440 if property.is_static() {
441 special_attributes.push("static")
443 special_attributes.push(property.visibility.as_ref());
444 special_attributes.reverse();
447 concat_by(w, " ", &special_attributes, |w, a| w.write(a))?;
448 if !special_attributes.is_empty() && !property.attributes.is_empty() {
451 print_attributes(ctx, w, &property.attributes)?;
455 fn print_property_type_info<W: Write>(w: &mut W, p: &HhasProperty) -> Result<(), W::Error> {
456 print_type_info(w, &p.type_info)?;
460 fn print_property<W: Write>(
463 class_def: &HhasClass,
464 property: &HhasProperty,
465 ) -> Result<(), W::Error> {
467 w.write(" .property ")?;
468 print_property_attributes(ctx, w, property)?;
469 print_property_doc_comment(w, property)?;
470 print_property_type_info(w, property)?;
471 w.write(property.name.to_raw_string())?;
473 let initial_value = property.initial_value.as_ref();
474 if class_def.is_closure() || initial_value == Just(&TypedValue::Uninit) {
477 triple_quotes(w, |w| match initial_value {
478 Nothing => w.write("N;"),
479 Just(value) => print_adata(ctx, w, &value),
485 fn print_constant<W: Write>(
489 ) -> Result<(), W::Error> {
492 w.write(c.name.to_raw_string())?;
494 w.write(" isAbstract")?;
496 match c.value.as_ref() {
497 Just(TypedValue::Uninit) => w.write(" = uninit")?,
500 triple_quotes(w, |w| print_adata(ctx, w, value))?;
507 fn print_enum_ty<W: Write>(ctx: &mut Context, w: &mut W, c: &HhasClass) -> Result<(), W::Error> {
508 if let Just(et) = c.enum_type.as_ref() {
510 w.write(".enum_ty ")?;
511 print_type_info_(w, true, et)?;
517 fn print_doc_comment<'arena, W: Write>(
520 doc_comment: &Maybe<Str<'arena>>,
521 ) -> Result<(), W::Error> {
522 if let Just(cmt) = doc_comment {
524 write!(w, ".doc {};", triple_quote_string(cmt.as_str()))?;
529 fn print_use_precedence<'arena, W: Write>(
532 Triple(id1, id2, ids): &Triple<
535 Slice<'arena, ClassType<'arena>>,
537 ) -> Result<(), W::Error> {
539 concat_str(w, [id1.to_raw_string(), "::", id2.to_raw_string()])?;
540 w.write(" insteadof ")?;
541 let unique_ids: IndexSet<&str> = ids.as_ref().iter().map(|i| i.to_raw_string()).collect();
542 concat_str_by(w, " ", unique_ids.iter().collect::<Vec<_>>())?;
546 fn print_use_as_visibility<W: Write>(w: &mut W, u: UseAsVisibility) -> Result<(), W::Error> {
548 UseAsVisibility::UseAsPublic => "public",
549 UseAsVisibility::UseAsPrivate => "private",
550 UseAsVisibility::UseAsProtected => "protected",
551 UseAsVisibility::UseAsFinal => "final",
555 fn print_use_alias<'arena, W: Write>(
558 Quadruple(ido1, id, ido2, kindl): &Quadruple<
559 Maybe<ClassType<'arena>>,
561 Maybe<ClassType<'arena>>,
562 Slice<'arena, UseAsVisibility>,
564 ) -> Result<(), W::Error> {
566 let id = id.to_raw_string();
570 |w, i: &ClassType<'arena>| concat_str(w, [i.to_raw_string(), "::", id]),
574 if !kindl.is_empty() {
576 concat_by(w, " ", kindl, |w, k| print_use_as_visibility(w, *k))
579 w.write_if(!kindl.is_empty() && ido2.is_just(), " ")?;
580 option(w, ido2.as_ref(), |w, i: &ClassType<'arena>| {
581 w.write(i.to_raw_string())
586 fn print_uses<'arena, W: Write>(
589 c: &HhasClass<'arena>,
590 ) -> Result<(), W::Error> {
591 if c.uses.is_empty() {
594 let unique_ids: IndexSet<&str> = c
598 .map(|e| strip_global_ns(e.as_str()))
600 let unique_ids: Vec<_> = unique_ids.into_iter().collect();
604 concat_by(w, " ", unique_ids, |w, id| w.write(id))?;
606 if c.use_aliases.is_empty() && c.use_precedences.is_empty() {
610 ctx.block(w, |ctx, w| {
614 Slice<'arena, ClassType<'arena>>,
615 >] = c.use_precedences.as_ref();
617 print_use_precedence(ctx, w, x)?;
619 let aliases: &[Quadruple<
620 Maybe<ClassType<'arena>>,
622 Maybe<ClassType<'arena>>,
623 Slice<'arena, UseAsVisibility>,
624 >] = c.use_aliases.as_ref();
626 print_use_alias(ctx, w, x)?;
636 fn print_class_special_attributes<W: Write>(
640 ) -> Result<(), W::Error> {
641 let user_attrs = c.attributes.as_ref();
642 let is_system_lib = ctx.is_system_lib();
644 let mut special_attributes: Vec<&str> = vec![];
645 if c.needs_no_reifiedinit() {
646 special_attributes.push("noreifiedinit")
648 if c.no_dynamic_props() {
649 special_attributes.push("no_dynamic_props")
652 special_attributes.push("is_const")
654 if hhas_attribute::has_foldable(user_attrs) {
655 special_attributes.push("foldable")
657 if hhas_attribute::has_enum_class(user_attrs) {
658 special_attributes.push("enum_class")
661 special_attributes.extend(&["persistent", "builtin", "unique"])
663 if hhas_attribute::has_dynamically_constructible(user_attrs) {
664 special_attributes.push("dyn_constructible");
666 if c.is_closure() && !is_system_lib {
667 special_attributes.push("unique");
670 special_attributes.push("no_override");
673 special_attributes.push("trait");
675 if c.is_interface() {
676 special_attributes.push("interface");
679 special_attributes.push("final");
682 special_attributes.push("sealed");
684 if c.enum_type.is_just() && !hhbc_by_ref_hhas_attribute::has_enum_class(user_attrs) {
685 special_attributes.push("enum");
688 special_attributes.push("abstract");
690 if special_attributes.is_empty() && user_attrs.is_empty() {
694 special_attributes.reverse();
695 wrap_by_(w, "[", "] ", |w| {
696 concat_by(w, " ", &special_attributes, |w, a| w.write(a))?;
698 !special_attributes.is_empty() && !user_attrs.is_empty(),
701 print_attributes(ctx, w, &user_attrs)
705 fn print_implements<W: Write>(w: &mut W, implements: &[ClassType<'_>]) -> Result<(), W::Error> {
706 if implements.is_empty() {
709 w.write(" implements (")?;
715 .map(|x| x.to_raw_string())
716 .collect::<Vec<_>>(),
721 fn print_enum_includes<W: Write>(
723 enum_includes: &[ClassType<'_>],
724 ) -> Result<(), W::Error> {
725 if enum_includes.is_empty() {
728 w.write(" enum_includes (")?;
734 .map(|x| x.to_raw_string())
735 .collect::<Vec<_>>(),
740 fn print_shadowed_tparams<'arena, W: Write>(
742 shadowed_tparams: impl AsRef<[Str<'arena>]>,
743 ) -> Result<(), W::Error> {
744 braces(w, |w| concat_str_by(w, ", ", shadowed_tparams))
747 fn print_method_def<W: Write>(
750 method_def: &HhasMethod,
751 ) -> Result<(), W::Error> {
752 let body = &method_def.body;
754 w.write(" .method ")?;
755 print_shadowed_tparams(w, &body.shadowed_tparams)?;
756 print_upper_bounds_(w, &body.upper_bounds)?;
758 print_method_attrs(ctx, w, method_def)?;
759 print_span(w, &method_def.span)?;
761 option(w, &(Option::from(body.return_type_info.clone())), |w, t| {
762 print_type_info(w, t)?;
765 w.write(method_def.name.to_raw_string())?;
766 print_params(ctx, w, &body.params)?;
767 if method_def.flags.contains(HhasMethodFlags::IS_GENERATOR) {
768 w.write(" isGenerator")?;
770 if method_def.flags.contains(HhasMethodFlags::IS_ASYNC) {
771 w.write(" isAsync")?;
775 .contains(HhasMethodFlags::IS_PAIR_GENERATOR)
777 w.write(" isPairGenerator")?;
779 if method_def.flags.contains(HhasMethodFlags::IS_CLOSURE_BODY) {
780 w.write(" isClosureBody")?;
784 .contains(HhasMethodFlags::IS_READONLY_RETURN)
786 w.write(" isReadonlyReturn")?;
790 ctx.block(w, |c, w| print_body(c, w, body, &method_def.coeffects))?;
796 fn print_method_attrs<W: Write>(
800 ) -> Result<(), W::Error> {
801 use hhas_attribute::*;
802 let user_attrs = m.attributes.as_ref();
803 let mut special_attrs = vec![];
804 if has_provenance_skip_frame(user_attrs) {
805 special_attrs.push("prov_skip_frame")
807 if m.is_interceptable() {
808 special_attrs.push("interceptable");
810 let visibility = m.visibility.as_ref().to_string();
811 special_attrs.push(&visibility);
812 if m.flags.contains(HhasMethodFlags::IS_STATIC) {
813 special_attrs.push("static");
815 if m.flags.contains(HhasMethodFlags::IS_FINAL) {
816 special_attrs.push("final");
818 if m.flags.contains(HhasMethodFlags::IS_ABSTRACT) {
819 special_attrs.push("abstract");
821 if has_foldable(user_attrs) {
822 special_attrs.push("foldable");
824 if m.is_no_injection() {
825 special_attrs.push("no_injection");
827 if ctx.is_system_lib() && has_native(user_attrs) && !is_native_opcode_impl(user_attrs) {
828 special_attrs.push("unique");
830 if ctx.is_system_lib() {
831 special_attrs.push("builtin");
833 if ctx.is_system_lib() && has_native(user_attrs) && !is_native_opcode_impl(user_attrs) {
834 special_attrs.push("persistent");
836 if ctx.is_system_lib() || (has_dynamically_callable(user_attrs) && !m.is_memoize_impl()) {
837 special_attrs.push("dyn_callable")
839 print_special_and_user_attrs(ctx, w, &special_attrs, user_attrs)
842 fn print_class_def<'arena, W: Write>(
845 class_def: &HhasClass<'arena>,
846 ) -> Result<(), W::Error> {
849 print_upper_bounds(w, class_def.upper_bounds.as_ref())?;
851 print_class_special_attributes(ctx, w, class_def)?;
852 w.write(class_def.name.to_raw_string())?;
854 print_span(w, &class_def.span)?;
857 Option::from(class_def.base.as_ref()).map(|x: &ClassType<'arena>| x.to_raw_string()),
859 print_implements(w, class_def.implements.as_ref())?;
860 print_enum_includes(w, class_def.enum_includes.as_ref())?;
862 ctx.block(w, |c, w| {
863 print_doc_comment(c, w, &class_def.doc_comment)?;
864 print_uses(c, w, class_def)?;
865 print_enum_ty(c, w, class_def)?;
866 for x in class_def.requirements.as_ref() {
867 print_requirement(c, w, x)?;
869 for x in class_def.constants.as_ref() {
870 print_constant(c, w, x)?;
872 for x in class_def.type_constants.as_ref() {
873 print_type_constant(c, w, x)?;
875 for x in class_def.ctx_constants.as_ref() {
876 print_ctx_constant(c, w, x)?;
878 for x in class_def.properties.as_ref() {
879 print_property(c, w, class_def, x)?;
881 for m in class_def.methods.as_ref() {
882 print_method_def(c, w, m)?;
891 fn print_pos_as_prov_tag<W: Write>(
894 loc: &Option<ast_defs::Pos>,
895 ) -> Result<(), W::Error> {
897 Some(l) if ctx.array_provenance => {
898 let (line, ..) = l.info_pos();
899 let filename = l.filename().to_absolute();
900 let filename = match filename.to_str().unwrap() {
901 "" => "(unknown hackc filename)",
909 quote_string_with_escape(filename)
916 fn print_hhbc_id<'a, W: Write>(w: &mut W, id: &impl Id<'a>) -> Result<(), W::Error> {
917 quotes(w, |w| w.write(escape(id.to_raw_string())))
920 fn print_function_id<W: Write>(w: &mut W, id: &FunctionId) -> Result<(), W::Error> {
924 fn print_class_id<W: Write>(w: &mut W, id: &ClassId) -> Result<(), W::Error> {
928 fn print_method_id<W: Write>(w: &mut W, id: &MethodId) -> Result<(), W::Error> {
932 fn print_const_id<W: Write>(w: &mut W, id: &ConstId) -> Result<(), W::Error> {
936 fn print_prop_id<W: Write>(w: &mut W, id: &PropId) -> Result<(), W::Error> {
940 fn print_adata_id<W: Write>(w: &mut W, id: &AdataId) -> Result<(), W::Error> {
941 concat_str(w, ["@", id.as_str()])
944 fn print_adata_mapped_argument<W: Write, F, V>(
948 loc: &Option<ast_defs::Pos>,
951 ) -> Result<(), W::Error>
953 F: Fn(&mut Context, &mut W, &V) -> Result<(), W::Error>,
955 write!(w, "{}:{}:{{", col_type, values.len(),)?;
956 print_pos_as_prov_tag(ctx, w, loc)?;
963 fn print_adata_collection_argument<W: Write>(
967 loc: &Option<ast_defs::Pos>,
968 values: &[TypedValue],
969 ) -> Result<(), W::Error> {
970 print_adata_mapped_argument(ctx, w, col_type, loc, values, &print_adata)
973 fn print_adata_dict_collection_argument<W: Write>(
977 loc: &Option<ast_defs::Pos>,
978 pairs: &[Pair<TypedValue, TypedValue>],
979 ) -> Result<(), W::Error> {
980 print_adata_mapped_argument(ctx, w, col_type, loc, pairs, |ctx, w, Pair(v1, v2)| {
981 print_adata(ctx, w, v1)?;
982 print_adata(ctx, w, v2)
986 fn print_adata<W: Write>(ctx: &mut Context, w: &mut W, tv: &TypedValue) -> Result<(), W::Error> {
988 TypedValue::Uninit => w.write("uninit"),
989 TypedValue::Null => w.write("N;"),
990 TypedValue::String(s) => {
991 write!(w, "s:{}:{};", s.len(), quote_string_with_escape(s.as_str()))
993 TypedValue::LazyClass(s) => {
994 write!(w, "l:{}:{};", s.len(), quote_string_with_escape(s.as_str()))
996 TypedValue::Float(f) => write!(w, "d:{};", float::to_string(*f)),
997 TypedValue::Int(i) => write!(w, "i:{};", i),
998 // TODO: The False case seems to sometimes be b:0 and sometimes i:0. Why?
999 TypedValue::Bool(false) => w.write("b:0;"),
1000 TypedValue::Bool(true) => w.write("b:1;"),
1001 TypedValue::Vec(values) => {
1002 print_adata_collection_argument(ctx, w, VEC_PREFIX, &None, values.as_ref())
1004 TypedValue::Dict(pairs) => {
1005 print_adata_dict_collection_argument(ctx, w, DICT_PREFIX, &None, pairs.as_ref())
1007 TypedValue::Keyset(values) => {
1008 print_adata_collection_argument(ctx, w, KEYSET_PREFIX, &None, values.as_ref())
1010 TypedValue::HhasAdata(s) => w.write(escaped(s.as_str())),
1014 fn print_attribute<W: Write>(
1018 ) -> Result<(), W::Error> {
1021 "\"{}\"(\"\"\"{}:{}:{{",
1026 concat(w, &a.arguments, |w, arg| print_adata(ctx, w, arg))?;
1030 fn print_attributes<'a, W: Write>(
1033 al: impl AsRef<[HhasAttribute<'a>]>,
1034 ) -> Result<(), W::Error> {
1035 // Adjust for underscore coming before alphabet
1036 let al: Vec<&HhasAttribute> = al
1039 .sorted_by_key(|a| (!a.name.as_str().starts_with("__"), a.name.as_str()))
1041 concat_by(w, " ", &al, |w, a| print_attribute(ctx, w, a))
1044 fn print_file_attributes<W: Write>(
1047 al: &[HhasAttribute],
1048 ) -> Result<(), W::Error> {
1053 w.write(".file_attributes [")?;
1054 print_attributes(ctx, w, al)?;
1059 fn is_bareword_char(c: &u8) -> bool {
1061 b'_' | b'.' | b'$' | b'\\' => true,
1062 c => (b'0'..=b'9').contains(&c) || (b'a'..=b'z').contains(&c) || (b'A'..=b'Z').contains(&c),
1066 fn print_body<W: Write>(
1070 coeffects: &HhasCoeffects,
1071 ) -> Result<(), W::Error> {
1072 print_doc_comment(ctx, w, &body.doc_comment)?;
1073 if body.is_memoize_wrapper {
1075 w.write(".ismemoizewrapper;")?;
1077 if body.is_memoize_wrapper_lsb {
1079 w.write(".ismemoizewrapperlsb;")?;
1081 if body.num_iters > 0 {
1083 write!(w, ".numiters {};", body.num_iters)?;
1085 if !body.decl_vars.is_empty() {
1087 w.write(".declvars ")?;
1088 concat_by(w, " ", &body.decl_vars, |w, var| {
1089 if var.as_str().as_bytes().iter().all(is_bareword_char) {
1092 quotes(w, |w| w.write(escaper::escape(var.as_str())))
1097 if body.num_closures > 0 {
1099 write!(w, ".numclosures {};", body.num_closures)?;
1101 for s in HhasCoeffects::coeffects_to_hhas(&coeffects).iter() {
1105 print_instructions(ctx, w, &body.body_instrs)
1108 fn print_instructions<W: Write>(
1111 instr_seq: &InstrSeq,
1112 ) -> Result<(), W::Error> {
1115 for instr in instr_seq.compact_iter() {
1117 ISpecialFlow(_) => return Err(Error::fail("Cannot break/continue 1 level")),
1121 print_instr(w, instr)?;
1123 ILabel(_) => ctx.unblock(w, |c, w| {
1125 print_instr(w, instr)
1127 ITry(TryCatchBegin) => {
1129 print_instr(w, instr)?;
1132 ITry(TryCatchMiddle) => ctx.unblock(w, |c, w| {
1134 print_instr(w, instr)
1136 ITry(TryCatchEnd) => {
1139 print_instr(w, instr)?;
1143 print_instr(w, instr)?;
1150 fn if_then<F: FnOnce() -> R, R>(cond: bool, f: F) -> Option<R> {
1151 if cond { Some(f()) } else { None }
1154 fn print_fcall_args<W: Write>(
1156 FcallArgs(fls, num_args, num_rets, inouts, readonly, async_eager_label, context): &FcallArgs,
1157 ) -> Result<(), W::Error> {
1158 use FcallFlags as F;
1159 let mut flags = vec![];
1160 if_then(fls.contains(F::HAS_UNPACK), || flags.push("Unpack"));
1161 if_then(fls.contains(F::HAS_GENERICS), || flags.push("Generics"));
1162 if_then(fls.contains(F::LOCK_WHILE_UNWINDING), || {
1163 flags.push("LockWhileUnwinding")
1165 if_then(fls.contains(F::ENFORCE_MUTABLE_RETURN), || {
1166 flags.push("EnforceMutableReturn")
1168 angle(w, |w| concat_str_by(w, " ", flags))?;
1170 print_int(w, num_args)?;
1172 print_int(w, num_rets)?;
1175 concat_by(w, "", inouts, |w, i| w.write(if *i { "1" } else { "0" }))
1179 concat_by(w, "", readonly, |w, i| w.write(if *i { "1" } else { "0" }))
1182 option_or(w, async_eager_label.as_ref(), print_label, "-")?;
1185 Just(s) => quotes(w, |w| w.write(s)),
1186 Nothing => w.write("\"\""),
1190 fn print_special_cls_ref<W: Write>(w: &mut W, cls_ref: &SpecialClsRef) -> Result<(), W::Error> {
1191 w.write(match cls_ref {
1192 SpecialClsRef::Static => "Static",
1193 SpecialClsRef::Self_ => "Self",
1194 SpecialClsRef::Parent => "Parent",
1198 fn print_null_flavor<W: Write>(w: &mut W, f: &ObjNullFlavor) -> Result<(), W::Error> {
1200 ObjNullFlavor::NullThrows => "NullThrows",
1201 ObjNullFlavor::NullSafe => "NullSafe",
1205 fn print_instr<W: Write>(w: &mut W, instr: &Instruct) -> Result<(), W::Error> {
1206 fn print_call<W: Write>(w: &mut W, call: &InstructCall) -> Result<(), W::Error> {
1207 use InstructCall as I;
1209 I::NewObj => w.write("NewObj"),
1210 I::NewObjR => w.write("NewObjR"),
1211 I::NewObjD(cid) => {
1212 w.write("NewObjD ")?;
1213 print_class_id(w, cid)
1215 I::NewObjRD(cid) => {
1216 w.write("NewObjRD ")?;
1217 print_class_id(w, cid)
1220 w.write("NewObjS ")?;
1221 print_special_cls_ref(w, r)
1223 I::FCall(fcall_args) => {
1225 print_fcall_args(w, &fcall_args)?;
1226 w.write(r#" "" """#)
1228 I::FCallClsMethod(fcall_args, is_log_as_dynamic_call) => {
1229 w.write("FCallClsMethod ")?;
1230 print_fcall_args(w, fcall_args)?;
1231 w.write(r#" "" "#)?;
1232 w.write(match is_log_as_dynamic_call {
1233 IsLogAsDynamicCallOp::LogAsDynamicCall => "LogAsDynamicCall",
1234 IsLogAsDynamicCallOp::DontLogAsDynamicCall => "DontLogAsDynamicCall",
1237 I::FCallClsMethodD(fcall_args, cid, mid) => {
1238 w.write("FCallClsMethodD ")?;
1239 print_fcall_args(w, fcall_args)?;
1240 w.write(r#" "" "#)?;
1241 print_class_id(w, cid)?;
1243 print_method_id(w, mid)
1245 I::FCallClsMethodS(fcall_args, r) => {
1246 w.write("FCallClsMethodS ")?;
1247 print_fcall_args(w, fcall_args)?;
1248 w.write(r#" "" "#)?;
1249 print_special_cls_ref(w, r)
1251 I::FCallClsMethodSD(fcall_args, r, mid) => {
1252 w.write("FCallClsMethodSD ")?;
1253 print_fcall_args(w, fcall_args)?;
1254 w.write(r#" "" "#)?;
1255 print_special_cls_ref(w, r)?;
1257 print_method_id(w, mid)
1259 I::FCallCtor(fcall_args) => {
1260 w.write("FCallCtor ")?;
1261 print_fcall_args(w, fcall_args)?;
1264 I::FCallFunc(fcall_args) => {
1265 w.write("FCallFunc ")?;
1266 print_fcall_args(w, fcall_args)
1268 I::FCallFuncD(fcall_args, id) => {
1269 w.write("FCallFuncD ")?;
1270 print_fcall_args(w, fcall_args)?;
1272 print_function_id(w, id)
1274 I::FCallObjMethod(fcall_args, nf) => {
1275 w.write("FCallObjMethod ")?;
1276 print_fcall_args(w, fcall_args)?;
1277 w.write(r#" "" "#)?;
1278 print_null_flavor(w, nf)
1280 I::FCallObjMethodD(fcall_args, nf, id) => {
1281 w.write("FCallObjMethodD ")?;
1282 print_fcall_args(w, fcall_args)?;
1283 w.write(r#" "" "#)?;
1284 print_null_flavor(w, nf)?;
1286 print_method_id(w, id)
1291 fn print_get<W: Write>(w: &mut W, get: &InstructGet) -> Result<(), W::Error> {
1292 use InstructGet as IG;
1298 IG::CGetQuietL(id) => {
1299 w.write("CGetQuietL ")?;
1303 w.write("CGetL2 ")?;
1307 w.write("CUGetL ")?;
1314 IG::CGetG => w.write("CGetG"),
1317 print_readonly_op(w, op)
1319 IG::ClassGetC => w.write("ClassGetC"),
1320 IG::ClassGetTS => w.write("ClassGetTS"),
1325 use InstructBasic as IB;
1327 IIterator(i) => print_iterator(w, i),
1328 IBasic(b) => w.write(match b {
1330 IB::EntryNop => "EntryNop",
1335 ILitConst(lit) => print_lit_const(w, lit),
1336 IOp(op) => print_op(w, op),
1337 IContFlow(cf) => print_control_flow(w, cf),
1338 ICall(c) => print_call(w, c),
1339 IMisc(misc) => print_misc(w, misc),
1340 IGet(get) => print_get(w, get),
1341 IMutator(mutator) => print_mutator(w, mutator),
1346 IIsset(i) => print_isset(w, i),
1347 IBase(i) => print_base(w, i),
1348 IFinal(i) => print_final(w, i),
1349 ITry(itry) => print_try(w, itry),
1350 IComment(s) => concat_str_by(w, " ", ["#", s.as_str()]),
1351 ISrcLoc(p) => write!(
1353 ".srcloc {}:{},{}:{};",
1354 p.line_begin, p.col_begin, p.line_end, p.col_end
1356 IAsync(a) => print_async(w, a),
1357 IGenerator(gen) => print_gen_creation_execution(w, gen),
1358 IIncludeEvalDefine(ed) => print_include_eval_define(w, ed),
1359 _ => Err(Error::fail("invalid instruction")),
1363 fn print_base<W: Write>(w: &mut W, i: &InstructBase) -> Result<(), W::Error> {
1364 use InstructBase as I;
1366 I::BaseGC(si, m) => {
1367 w.write("BaseGC ")?;
1368 print_stack_index(w, si)?;
1370 print_member_opmode(w, m)
1372 I::BaseGL(id, m) => {
1373 w.write("BaseGL ")?;
1374 print_local(w, id)?;
1376 print_member_opmode(w, m)
1378 I::BaseSC(si1, si2, m, op) => {
1379 w.write("BaseSC ")?;
1380 print_stack_index(w, si1)?;
1382 print_stack_index(w, si2)?;
1384 print_member_opmode(w, m)?;
1386 print_readonly_op(w, op)
1388 I::BaseL(id, m, op) => {
1390 print_local(w, id)?;
1392 print_member_opmode(w, m)?;
1394 print_readonly_op(w, op)
1396 I::BaseC(si, m) => {
1398 print_stack_index(w, si)?;
1400 print_member_opmode(w, m)
1402 I::BaseH => w.write("BaseH"),
1405 print_member_opmode(w, m)?;
1407 print_member_key(w, mk)
1412 fn print_stack_index<W: Write>(w: &mut W, si: &StackIndex) -> Result<(), W::Error> {
1413 w.write(si.to_string())
1416 fn print_member_opmode<W: Write>(w: &mut W, m: &MemberOpMode) -> Result<(), W::Error> {
1417 use MemberOpMode as M;
1419 M::ModeNone => "None",
1421 M::Define => "Define",
1422 M::Unset => "Unset",
1423 M::InOut => "InOut",
1427 fn print_member_key<W: Write>(w: &mut W, mk: &MemberKey) -> Result<(), W::Error> {
1432 print_stack_index(w, si)?;
1434 print_readonly_op(w, op)
1436 M::EL(local, op) => {
1438 print_local(w, local)?;
1440 print_readonly_op(w, op)
1444 quotes(w, |w| w.write(escape(s.as_str())))?;
1446 print_readonly_op(w, op)
1449 concat_str(w, ["EI:", i.to_string().as_ref()])?;
1451 print_readonly_op(w, op)
1455 print_stack_index(w, si)?;
1457 print_readonly_op(w, op)
1459 M::PL(local, op) => {
1461 print_local(w, local)?;
1463 print_readonly_op(w, op)
1467 print_prop_id(w, id)?;
1469 print_readonly_op(w, op)
1473 print_prop_id(w, id)?;
1475 print_readonly_op(w, op)
1477 M::W => w.write("W"),
1481 fn print_iterator<W: Write>(w: &mut W, i: &InstructIterator) -> Result<(), W::Error> {
1482 use InstructIterator as I;
1484 I::IterInit(iter_args, label) => {
1485 w.write("IterInit ")?;
1486 print_iter_args(w, iter_args)?;
1488 print_label(w, label)
1490 I::IterNext(iter_args, label) => {
1491 w.write("IterNext ")?;
1492 print_iter_args(w, iter_args)?;
1494 print_label(w, label)
1496 I::IterFree(id) => {
1497 w.write("IterFree ")?;
1498 print_iterator_id(w, id)
1503 fn print_iter_args<W: Write>(w: &mut W, iter_args: &IterArgs) -> Result<(), W::Error> {
1504 print_iterator_id(w, &iter_args.iter_id)?;
1506 match &iter_args.key_id {
1507 Nothing => w.write("NK")?,
1510 print_local(w, &k)?;
1515 print_local(w, &iter_args.val_id)
1518 fn print_iterator_id<W: Write>(w: &mut W, i: &IterId) -> Result<(), W::Error> {
1522 fn print_async<W: Write>(w: &mut W, a: &AsyncFunctions) -> Result<(), W::Error> {
1523 use AsyncFunctions as A;
1525 A::WHResult => w.write("WHResult"),
1526 A::Await => w.write("Await"),
1527 A::AwaitAll(Just(Pair(Local::Unnamed(id), count))) => {
1528 write!(w, "AwaitAll L:{}+{}", id, count)
1530 A::AwaitAll(Nothing) => w.write("AwaitAll L:0+0"),
1531 _ => Err(Error::fail("AwaitAll needs an unnamed local")),
1535 fn print_query_op<W: Write>(w: &mut W, q: QueryOp) -> Result<(), W::Error> {
1537 QueryOp::CGet => "CGet",
1538 QueryOp::CGetQuiet => "CGetQuiet",
1539 QueryOp::Isset => "Isset",
1540 QueryOp::InOut => "InOut",
1544 fn print_final<W: Write>(w: &mut W, f: &InstructFinal) -> Result<(), W::Error> {
1545 use InstructFinal as F;
1547 F::QueryM(n, op, mk) => {
1548 w.write("QueryM ")?;
1551 print_query_op(w, *op)?;
1553 print_member_key(w, mk)
1555 F::UnsetM(n, mk) => {
1556 w.write("UnsetM ")?;
1559 print_member_key(w, mk)
1565 print_member_key(w, mk)
1567 F::SetOpM(i, op, mk) => {
1568 w.write("SetOpM ")?;
1571 print_eq_op(w, &op)?;
1573 print_member_key(w, mk)
1575 F::IncDecM(i, op, mk) => {
1576 w.write("IncDecM ")?;
1579 print_incdec_op(w, &op)?;
1581 print_member_key(w, mk)
1583 F::SetRangeM(i, s, op) => {
1584 w.write("SetRangeM ")?;
1587 print_int(w, &(*s as usize))?;
1590 SetrangeOp::Forward => "Forward",
1591 SetrangeOp::Reverse => "Reverse",
1597 fn print_isset<W: Write>(w: &mut W, isset: &InstructIsset) -> Result<(), W::Error> {
1598 use InstructIsset as I;
1600 I::IssetC => w.write("IssetC"),
1601 I::IssetL(local) => {
1602 w.write("IssetL ")?;
1603 print_local(w, local)
1605 I::IsUnsetL(local) => {
1606 w.write("IsUnsetL ")?;
1607 print_local(w, local)
1609 I::IssetG => w.write("IssetG"),
1610 I::IssetS => w.write("IssetS"),
1612 w.write("IsTypeC ")?;
1613 print_istype_op(w, op)
1615 I::IsTypeL(local, op) => {
1616 w.write("IsTypeL ")?;
1617 print_local(w, local)?;
1619 print_istype_op(w, op)
1624 fn print_istype_op<W: Write>(w: &mut W, op: &IstypeOp) -> Result<(), W::Error> {
1627 Op::OpNull => w.write("Null"),
1628 Op::OpBool => w.write("Bool"),
1629 Op::OpInt => w.write("Int"),
1630 Op::OpDbl => w.write("Dbl"),
1631 Op::OpStr => w.write("Str"),
1632 Op::OpObj => w.write("Obj"),
1633 Op::OpRes => w.write("Res"),
1634 Op::OpScalar => w.write("Scalar"),
1635 Op::OpKeyset => w.write("Keyset"),
1636 Op::OpDict => w.write("Dict"),
1637 Op::OpVec => w.write("Vec"),
1638 Op::OpArrLike => w.write("ArrLike"),
1639 Op::OpLegacyArrLike => w.write("LegacyArrLike"),
1640 Op::OpClsMeth => w.write("ClsMeth"),
1641 Op::OpFunc => w.write("Func"),
1642 Op::OpClass => w.write("Class"),
1646 fn print_try<W: Write>(w: &mut W, itry: &InstructTry) -> Result<(), W::Error> {
1647 use InstructTry as T;
1649 T::TryCatchBegin => w.write(".try {"),
1650 T::TryCatchMiddle => w.write("} .catch {"),
1651 T::TryCatchEnd => w.write("}"),
1655 fn print_mutator<W: Write>(w: &mut W, mutator: &InstructMutator) -> Result<(), W::Error> {
1656 use InstructMutator as M;
1660 print_local(w, local)
1666 M::SetG => w.write("SetG"),
1669 print_readonly_op(w, op)
1671 M::SetOpL(id, op) => {
1672 w.write("SetOpL ")?;
1673 print_local(w, id)?;
1678 w.write("SetOpG ")?;
1682 w.write("SetOpS ")?;
1685 M::IncDecL(id, op) => {
1686 w.write("IncDecL ")?;
1687 print_local(w, id)?;
1689 print_incdec_op(w, op)
1692 w.write("IncDecG ")?;
1693 print_incdec_op(w, op)
1696 w.write("IncDecS ")?;
1697 print_incdec_op(w, op)
1700 w.write("UnsetL ")?;
1703 M::UnsetG => w.write("UnsetG"),
1704 M::CheckProp(id) => {
1705 w.write("CheckProp ")?;
1706 print_prop_id(w, id)
1708 M::InitProp(id, op) => {
1709 w.write("InitProp ")?;
1710 print_prop_id(w, id)?;
1713 InitpropOp::Static => w.write("Static"),
1714 InitpropOp::NonStatic => w.write("NonStatic"),
1721 fn print_eq_op<W: Write>(w: &mut W, op: &EqOp) -> Result<(), W::Error> {
1723 EqOp::PlusEqual => "PlusEqual",
1724 EqOp::MinusEqual => "MinusEqual",
1725 EqOp::MulEqual => "MulEqual",
1726 EqOp::ConcatEqual => "ConcatEqual",
1727 EqOp::DivEqual => "DivEqual",
1728 EqOp::PowEqual => "PowEqual",
1729 EqOp::ModEqual => "ModEqual",
1730 EqOp::AndEqual => "AndEqual",
1731 EqOp::OrEqual => "OrEqual",
1732 EqOp::XorEqual => "XorEqual",
1733 EqOp::SlEqual => "SlEqual",
1734 EqOp::SrEqual => "SrEqual",
1735 EqOp::PlusEqualO => "PlusEqualO",
1736 EqOp::MinusEqualO => "MinusEqualO",
1737 EqOp::MulEqualO => "MulEqualO",
1741 fn print_readonly_op<W: Write>(w: &mut W, op: &ReadonlyOp) -> Result<(), W::Error> {
1743 ReadonlyOp::Readonly => "Readonly",
1744 ReadonlyOp::Mutable => "Mutable",
1745 ReadonlyOp::Any => "Any",
1746 ReadonlyOp::CheckROCOW => "CheckROCOW",
1747 ReadonlyOp::CheckMutROCOW => "CheckMutROCOW",
1751 fn print_incdec_op<W: Write>(w: &mut W, op: &IncdecOp) -> Result<(), W::Error> {
1753 IncdecOp::PreInc => "PreInc",
1754 IncdecOp::PostInc => "PostInc",
1755 IncdecOp::PreDec => "PreDec",
1756 IncdecOp::PostDec => "PostDec",
1757 IncdecOp::PreIncO => "PreIncO",
1758 IncdecOp::PostIncO => "PostIncO",
1759 IncdecOp::PreDecO => "PreDecO",
1760 IncdecOp::PostDecO => "PostDecO",
1764 fn print_gen_creation_execution<W: Write>(
1766 gen: &GenCreationExecution,
1767 ) -> Result<(), W::Error> {
1768 use GenCreationExecution as G;
1770 G::CreateCont => w.write("CreateCont"),
1771 G::ContEnter => w.write("ContEnter"),
1772 G::ContRaise => w.write("ContRaise"),
1773 G::Yield => w.write("Yield"),
1774 G::YieldK => w.write("YieldK"),
1775 G::ContCheck(CheckStarted::IgnoreStarted) => w.write("ContCheck IgnoreStarted"),
1776 G::ContCheck(CheckStarted::CheckStarted) => w.write("ContCheck CheckStarted"),
1777 G::ContValid => w.write("ContValid"),
1778 G::ContKey => w.write("ContKey"),
1779 G::ContGetReturn => w.write("ContGetReturn"),
1780 G::ContCurrent => w.write("ContCurrent"),
1784 fn print_misc<W: Write>(w: &mut W, misc: &InstructMisc) -> Result<(), W::Error> {
1785 use InstructMisc as M;
1787 M::This => w.write("This"),
1788 M::CheckThis => w.write("CheckThis"),
1789 M::FuncNumArgs => w.write("FuncNumArgs"),
1790 M::ChainFaults => w.write("ChainFaults"),
1791 M::VerifyRetTypeC => w.write("VerifyRetTypeC"),
1792 M::VerifyRetTypeTS => w.write("VerifyRetTypeTS"),
1793 M::Self_ => w.write("Self"),
1794 M::Parent => w.write("Parent"),
1795 M::LateBoundCls => w.write("LateBoundCls"),
1796 M::ClassName => w.write("ClassName"),
1797 M::LazyClassFromClass => w.write("LazyClassFromClass"),
1798 M::RecordReifiedGeneric => w.write("RecordReifiedGeneric"),
1799 M::CheckReifiedGenericMismatch => w.write("CheckReifiedGenericMismatch"),
1800 M::NativeImpl => w.write("NativeImpl"),
1801 M::AKExists => w.write("AKExists"),
1802 M::Idx => w.write("Idx"),
1803 M::ArrayIdx => w.write("ArrayIdx"),
1804 M::ArrayMarkLegacy => w.write("ArrayMarkLegacy"),
1805 M::ArrayUnmarkLegacy => w.write("ArrayUnmarkLegacy"),
1806 M::BreakTraceHint => w.write("BreakTraceHint"),
1807 M::CGetCUNop => w.write("CGetCUNop"),
1808 M::UGetCUNop => w.write("UGetCUNop"),
1809 M::LockObj => w.write("LockObj"),
1810 M::ThrowNonExhaustiveSwitch => w.write("ThrowNonExhaustiveSwitch"),
1811 M::RaiseClassStringConversionWarning => w.write("RaiseClassStringConversionWarning"),
1812 M::VerifyParamType(id) => {
1813 w.write("VerifyParamType ")?;
1814 print_param_id(w, id)
1816 M::VerifyParamTypeTS(id) => {
1817 w.write("VerifyParamTypeTS ")?;
1818 print_param_id(w, id)
1820 M::Silence(local, op) => {
1821 w.write("Silence ")?;
1822 print_local(w, local)?;
1825 OpSilence::Start => w.write("Start"),
1826 OpSilence::End => w.write("End"),
1829 M::VerifyOutType(id) => {
1830 w.write("VerifyOutType ")?;
1831 print_param_id(w, id)
1833 M::CreateCl(n, cid) => concat_str_by(
1836 ["CreateCl", n.to_string().as_str(), cid.to_string().as_str()],
1838 M::BareThis(op) => concat_str_by(
1844 BareThisOp::Notice => "Notice",
1845 BareThisOp::NoNotice => "NoNotice",
1846 BareThisOp::NeverNull => "NeverNull",
1851 M::MemoGet(label, Just(Pair(Local::Unnamed(first), local_count))) => {
1852 w.write("MemoGet ")?;
1853 print_label(w, label)?;
1854 write!(w, " L:{}+{}", first, local_count)
1856 M::MemoGet(label, Nothing) => {
1857 w.write("MemoGet ")?;
1858 print_label(w, label)?;
1861 M::MemoGet(_, _) => Err(Error::fail("MemoGet needs an unnamed local")),
1863 M::MemoSet(Just(Pair(Local::Unnamed(first), local_count))) => {
1864 write!(w, "MemoSet L:{}+{}", first, local_count)
1866 M::MemoSet(Nothing) => w.write("MemoSet L:0+0"),
1867 M::MemoSet(_) => Err(Error::fail("MemoSet needs an unnamed local")),
1869 M::MemoGetEager(label1, label2, Just(Pair(Local::Unnamed(first), local_count))) => {
1870 w.write("MemoGetEager ")?;
1871 print_label(w, label1)?;
1873 print_label(w, label2)?;
1874 write!(w, " L:{}+{}", first, local_count)
1876 M::MemoGetEager(label1, label2, Nothing) => {
1877 w.write("MemoGetEager ")?;
1878 print_label(w, label1)?;
1880 print_label(w, label2)?;
1883 M::MemoGetEager(_, _, _) => Err(Error::fail("MemoGetEager needs an unnamed local")),
1885 M::MemoSetEager(Just(Pair(Local::Unnamed(first), local_count))) => {
1886 write!(w, "MemoSetEager L:{}+{}", first, local_count)
1888 M::MemoSetEager(Nothing) => w.write("MemoSetEager L:0+0"),
1889 M::MemoSetEager(_) => Err(Error::fail("MemoSetEager needs an unnamed local")),
1891 M::OODeclExists(k) => concat_str_by(
1897 ClassishKind::Class => "Class",
1898 ClassishKind::Interface => "Interface",
1899 ClassishKind::Trait => "Trait",
1900 ClassishKind::Enum => "Enum",
1901 ClassishKind::EnumClass => "EnumClass",
1905 M::AssertRATL(local, s) => {
1906 w.write("AssertRATL ")?;
1907 print_local(w, local)?;
1911 M::AssertRATStk(n, s) => {
1912 concat_str_by(w, " ", ["AssertRATStk", n.to_string().as_str(), s.as_str()])
1914 M::GetMemoKeyL(local) => {
1915 w.write("GetMemoKeyL ")?;
1916 print_local(w, local)
1921 fn print_include_eval_define<W: Write>(
1923 ed: &InstructIncludeEvalDefine,
1924 ) -> Result<(), W::Error> {
1925 use InstructIncludeEvalDefine::*;
1927 Incl => w.write("Incl"),
1928 InclOnce => w.write("InclOnce"),
1929 Req => w.write("Req"),
1930 ReqOnce => w.write("ReqOnce"),
1931 ReqDoc => w.write("ReqDoc"),
1932 Eval => w.write("Eval"),
1936 fn print_control_flow<W: Write>(w: &mut W, cf: &InstructControlFlow) -> Result<(), W::Error> {
1937 use InstructControlFlow as CF;
1955 CF::RetC => w.write("RetC"),
1956 CF::RetCSuspended => w.write("RetCSuspended"),
1957 CF::RetM(p) => concat_str_by(w, " ", ["RetM", p.to_string().as_str()]),
1958 CF::Throw => w.write("Throw"),
1959 CF::Switch(kind, base, labels) => print_switch(w, kind, base, labels.as_ref()),
1960 CF::SSwitch(cases) => match cases.as_ref() {
1961 [] => Err(Error::fail("sswitch should have at least one case")),
1962 [rest @ .., Pair(_, lastlabel)] => {
1963 w.write("SSwitch ")?;
1965 concat_by(w, " ", rest, |w, Pair(s, l)| {
1966 concat_str(w, [quote_string(s.as_ref()).as_str(), ":"])?;
1970 print_label(w, &lastlabel)
1977 fn print_switch<W: Write>(
1982 ) -> Result<(), W::Error> {
1983 w.write("Switch ")?;
1984 w.write(match kind {
1985 Switchkind::Bounded => "Bounded ",
1986 Switchkind::Unbounded => "Unbounded ",
1988 w.write(base.to_string())?;
1990 angle(w, |w| concat_by(w, " ", labels, print_label))
1993 fn print_lit_const<W: Write>(w: &mut W, lit: &InstructLitConst) -> Result<(), W::Error> {
1994 use InstructLitConst as LC;
1996 LC::Null => w.write("Null"),
1997 LC::Int(i) => concat_str_by(w, " ", ["Int", i.to_string().as_str()]),
1999 w.write("String ")?;
2000 quotes(w, |w| w.write(escape(s.as_str())))
2002 LC::LazyClass(id) => {
2003 w.write("LazyClass ")?;
2004 print_class_id(w, id)
2006 LC::True => w.write("True"),
2007 LC::False => w.write("False"),
2008 LC::Double(d) => concat_str_by(w, " ", ["Double", d.as_str()]),
2009 LC::AddElemC => w.write("AddElemC"),
2010 LC::AddNewElemC => w.write("AddNewElemC"),
2011 LC::NewPair => w.write("NewPair"),
2012 LC::File => w.write("File"),
2013 LC::Dir => w.write("Dir"),
2014 LC::Method => w.write("Method"),
2015 LC::FuncCred => w.write("FuncCred"),
2018 print_adata_id(w, id)
2021 w.write("Keyset ")?;
2022 print_adata_id(w, id)
2026 print_adata_id(w, id)
2028 LC::NewDictArray(i) => concat_str_by(w, " ", ["NewDictArray", i.to_string().as_str()]),
2029 LC::NewVec(i) => concat_str_by(w, " ", ["NewVec", i.to_string().as_str()]),
2030 LC::NewKeysetArray(i) => concat_str_by(w, " ", ["NewKeysetArray", i.to_string().as_str()]),
2031 LC::NewStructDict(l) => {
2032 let ls: Vec<&str> = l.as_ref().into_iter().map(|s| s.as_str()).collect();
2033 w.write("NewStructDict ")?;
2034 angle(w, |w| print_shape_fields(w, &ls[0..]))
2036 LC::NewRecord(cid, l) => {
2037 let ls: Vec<&str> = l.as_ref().into_iter().map(|s| s.as_str()).collect();
2038 w.write("NewRecord ")?;
2039 print_class_id(w, cid)?;
2041 angle(w, |w| print_shape_fields(w, &ls[0..]))
2045 print_const_id(w, id)
2048 w.write("ClsCns ")?;
2049 print_const_id(w, id)
2051 LC::ClsCnsD(const_id, cid) => {
2052 w.write("ClsCnsD ")?;
2053 print_const_id(w, const_id)?;
2055 print_class_id(w, cid)
2057 LC::ClsCnsL(id) => {
2058 w.write("ClsCnsL ")?;
2062 w.write("NewCol ")?;
2063 print_collection_type(w, ct)
2065 LC::ColFromArray(ct) => {
2066 w.write("ColFromArray ")?;
2067 print_collection_type(w, ct)
2069 LC::NullUninit => w.write("NullUninit"),
2070 LC::TypedValue(_) => Err(Error::fail("print_lit_const: TypedValue")),
2074 fn print_collection_type<W: Write>(w: &mut W, ct: &CollectionType) -> Result<(), W::Error> {
2075 use CollectionType as CT;
2077 CT::Vector => w.write("Vector"),
2078 CT::Map => w.write("Map"),
2079 CT::Set => w.write("Set"),
2080 CT::Pair => w.write("Pair"),
2081 CT::ImmVector => w.write("ImmVector"),
2082 CT::ImmMap => w.write("ImmMap"),
2083 CT::ImmSet => w.write("ImmSet"),
2084 CT::Dict => w.write("dict"),
2085 CT::Array => w.write("array"),
2086 CT::Keyset => w.write("keyset"),
2087 CT::Vec => w.write("vec"),
2091 fn print_shape_fields<W: Write>(w: &mut W, sf: &[&str]) -> Result<(), W::Error> {
2092 concat_by(w, " ", sf, |w, f| quotes(w, |w| w.write(escape(*f))))
2095 fn print_op<W: Write>(w: &mut W, op: &InstructOperator) -> Result<(), W::Error> {
2096 use InstructOperator as I;
2098 I::Concat => w.write("Concat"),
2099 I::ConcatN(n) => concat_str_by(w, " ", ["ConcatN", n.to_string().as_str()]),
2100 I::Add => w.write("Add"),
2101 I::Sub => w.write("Sub"),
2102 I::Mul => w.write("Mul"),
2103 I::AddO => w.write("AddO"),
2104 I::SubO => w.write("SubO"),
2105 I::MulO => w.write("MulO"),
2106 I::Div => w.write("Div"),
2107 I::Mod => w.write("Mod"),
2108 I::Pow => w.write("Pow"),
2109 I::Not => w.write("Not"),
2110 I::Same => w.write("Same"),
2111 I::NSame => w.write("NSame"),
2112 I::Eq => w.write("Eq"),
2113 I::Neq => w.write("Neq"),
2114 I::Lt => w.write("Lt"),
2115 I::Lte => w.write("Lte"),
2116 I::Gt => w.write("Gt"),
2117 I::Gte => w.write("Gte"),
2118 I::Cmp => w.write("Cmp"),
2119 I::BitAnd => w.write("BitAnd"),
2120 I::BitOr => w.write("BitOr"),
2121 I::BitXor => w.write("BitXor"),
2122 I::BitNot => w.write("BitNot"),
2123 I::Shl => w.write("Shl"),
2124 I::Shr => w.write("Shr"),
2125 I::CastBool => w.write("CastBool"),
2126 I::CastInt => w.write("CastInt"),
2127 I::CastDouble => w.write("CastDouble"),
2128 I::CastString => w.write("CastString"),
2129 I::CastVec => w.write("CastVec"),
2130 I::CastDict => w.write("CastDict"),
2131 I::CastKeyset => w.write("CastKeyset"),
2132 I::InstanceOf => w.write("InstanceOf"),
2133 I::InstanceOfD(id) => {
2134 w.write("InstanceOfD ")?;
2135 print_class_id(w, id)
2137 I::IsLateBoundCls => w.write("IsLateBoundCls"),
2138 I::IsTypeStructC(op) => concat_str_by(
2144 TypestructResolveOp::Resolve => "Resolve",
2145 TypestructResolveOp::DontResolve => "DontResolve",
2149 I::ThrowAsTypeStructException => w.write("ThrowAsTypeStructException"),
2150 I::CombineAndResolveTypeStruct(n) => concat_str_by(
2153 ["CombineAndResolveTypeStruct", n.to_string().as_str()],
2155 I::Print => w.write("Print"),
2156 I::Clone => w.write("Clone"),
2157 I::Exit => w.write("Exit"),
2158 I::ResolveFunc(id) => {
2159 w.write("ResolveFunc ")?;
2160 print_function_id(w, id)
2162 I::ResolveRFunc(id) => {
2163 w.write("ResolveRFunc ")?;
2164 print_function_id(w, id)
2166 I::ResolveMethCaller(id) => {
2167 w.write("ResolveMethCaller ")?;
2168 print_function_id(w, id)
2170 I::ResolveClsMethod(mid) => {
2171 w.write("ResolveClsMethod ")?;
2172 print_method_id(w, mid)
2174 I::ResolveClsMethodD(cid, mid) => {
2175 w.write("ResolveClsMethodD ")?;
2176 print_class_id(w, cid)?;
2178 print_method_id(w, mid)
2180 I::ResolveClsMethodS(r, mid) => {
2181 w.write("ResolveClsMethodS ")?;
2182 print_special_cls_ref(w, r)?;
2184 print_method_id(w, mid)
2186 I::ResolveRClsMethod(mid) => {
2187 w.write("ResolveRClsMethod ")?;
2188 print_method_id(w, mid)
2190 I::ResolveRClsMethodD(cid, mid) => {
2191 w.write("ResolveRClsMethodD ")?;
2192 print_class_id(w, cid)?;
2194 print_method_id(w, mid)
2196 I::ResolveRClsMethodS(r, mid) => {
2197 w.write("ResolveRClsMethodS ")?;
2198 print_special_cls_ref(w, r)?;
2200 print_method_id(w, mid)
2202 I::ResolveClass(id) => {
2203 w.write("ResolveClass ")?;
2204 print_class_id(w, id)
2206 I::Fatal(fatal_op) => print_fatal_op(w, fatal_op),
2210 fn print_fatal_op<W: Write>(w: &mut W, f: &FatalOp) -> Result<(), W::Error> {
2212 FatalOp::Parse => w.write("Fatal Parse"),
2213 FatalOp::Runtime => w.write("Fatal Runtime"),
2214 FatalOp::RuntimeOmitFrame => w.write("Fatal RuntimeOmitFrame"),
2218 fn print_params<'arena, W: Write>(
2221 params: impl AsRef<[HhasParam<'arena>]>,
2222 ) -> Result<(), W::Error> {
2224 concat_by(w, ", ", params, |w, i| print_param(ctx, w, i))
2228 fn print_param<'arena, W: Write>(
2231 param: &HhasParam<'arena>,
2232 ) -> Result<(), W::Error> {
2233 print_param_user_attributes(ctx, w, param)?;
2234 w.write_if(param.is_inout, "inout ")?;
2235 w.write_if(param.is_readonly, "readonly ")?;
2236 w.write_if(param.is_variadic, "...")?;
2237 option(w, &Option::from(param.type_info.clone()), |w, ty| {
2238 print_type_info(w, ty)?;
2241 w.write(¶m.name)?;
2244 &Option::from(param.default_value.map(|x| (x.0, x.1))),
2245 |w, i| print_param_default_value(w, i),
2249 fn print_param_id<W: Write>(w: &mut W, param_id: &ParamId) -> Result<(), W::Error> {
2251 ParamId::ParamUnnamed(i) => w.write(i.to_string()),
2252 ParamId::ParamNamed(s) => w.write(s),
2256 fn print_param_default_value<'arena, W: Write>(
2258 default_val: &(Label, Str<'arena>),
2259 ) -> Result<(), W::Error> {
2261 print_label(w, &default_val.0)?;
2262 paren(w, |w| triple_quotes(w, |w| w.write(default_val.1.as_str())))
2265 fn print_label<W: Write>(w: &mut W, label: &Label) -> Result<(), W::Error> {
2267 Label::Regular(id) => {
2271 Label::DefaultArg(id) => {
2278 fn print_local<W: Write>(w: &mut W, local: &Local) -> Result<(), W::Error> {
2280 Local::Unnamed(id) => {
2284 Local::Named(id) => w.write(id),
2288 fn print_int<W: Write>(w: &mut W, i: &usize) -> Result<(), W::Error> {
2292 fn print_key_value<W: Write>(
2298 ) -> Result<(), W::Error> {
2299 print_key_value_(ctx, w, env, k, print_expr, v)
2302 fn print_key_value_<W: Write, K, KeyPrinter>(
2309 ) -> Result<(), W::Error>
2311 KeyPrinter: FnMut(&mut Context, &mut W, &ExprEnv, K) -> Result<(), W::Error>,
2313 kp(ctx, w, env, k)?;
2315 print_expr(ctx, w, env, v)
2318 fn print_afield<W: Write>(
2322 afield: &ast::Afield,
2323 ) -> Result<(), W::Error> {
2324 use ast::Afield as A;
2326 A::AFvalue(e) => print_expr(ctx, w, env, &e),
2327 A::AFkvalue(k, v) => print_key_value(ctx, w, env, &k, &v),
2331 fn print_afields<W: Write>(
2335 afields: impl AsRef<[ast::Afield]>,
2336 ) -> Result<(), W::Error> {
2337 concat_by(w, ", ", afields, |w, i| print_afield(ctx, w, env, i))
2340 fn print_uop<W: Write>(w: &mut W, op: ast::Uop) -> Result<(), W::Error> {
2350 U::Upincr | U::Updecr => {
2351 return Err(Error::fail(
2352 "string_of_uop - should have been captures earlier",
2358 fn print_key_values<W: Write>(
2362 kvs: impl AsRef<[(ast::Expr, ast::Expr)]>,
2363 ) -> Result<(), W::Error> {
2364 print_key_values_(ctx, w, env, print_expr, kvs)
2367 fn print_key_values_<W: Write, K, KeyPrinter>(
2372 kvs: impl AsRef<[(K, ast::Expr)]>,
2373 ) -> Result<(), W::Error>
2375 KeyPrinter: Fn(&mut Context, &mut W, &ExprEnv, &K) -> Result<(), W::Error>,
2377 concat_by(w, ", ", kvs, |w, (k, v)| {
2378 print_key_value_(ctx, w, env, k, &mut kp, v)
2382 fn print_expr_darray<W: Write, K, KeyPrinter>(
2387 kvs: impl AsRef<[(K, ast::Expr)]>,
2388 ) -> Result<(), W::Error>
2390 KeyPrinter: Fn(&mut Context, &mut W, &ExprEnv, &K) -> Result<(), W::Error>,
2392 wrap_by_(w, "darray[", "]", |w| {
2393 print_key_values_(ctx, w, env, kp, kvs)
2397 fn print_expr_varray<W: Write>(
2401 varray: &[ast::Expr],
2402 ) -> Result<(), W::Error> {
2403 wrap_by_(w, "varray[", "]", |w| {
2404 concat_by(w, ", ", varray, |w, e| print_expr(ctx, w, env, e))
2408 fn print_shape_field_name<W: Write>(
2412 field: &ast::ShapeFieldName,
2413 ) -> Result<(), W::Error> {
2414 use ast::ShapeFieldName as S;
2416 S::SFlitInt((_, s)) => print_expr_int(w, s.as_ref()),
2417 S::SFlitStr((_, s)) => print_expr_string(w, s),
2418 S::SFclassConst(_, (_, s)) => print_expr_string(w, s.as_bytes()),
2422 fn print_expr_int<W: Write>(w: &mut W, i: &str) -> Result<(), W::Error> {
2423 match integer::to_decimal(i) {
2424 Ok(s) => w.write(s),
2425 Err(_) => Err(Error::fail("ParseIntError")),
2429 fn print_expr_string<W: Write>(w: &mut W, s: &[u8]) -> Result<(), W::Error> {
2430 fn escape_char(c: u8) -> Option<Cow<'static, [u8]>> {
2432 b'\n' => Some((&b"\\\\n"[..]).into()),
2433 b'\r' => Some((&b"\\\\r"[..]).into()),
2434 b'\t' => Some((&b"\\\\t"[..]).into()),
2435 b'\\' => Some((&b"\\\\\\\\"[..]).into()),
2436 b'"' => Some((&b"\\\\\\\""[..]).into()),
2437 b'$' => Some((&b"\\\\$"[..]).into()),
2438 c if is_lit_printable(c) => None,
2441 write!(r, "\\\\{:03o}", c).unwrap();
2446 // FIXME: This is not safe--string literals are binary strings.
2447 // There's no guarantee that they're valid UTF-8.
2448 let s = unsafe { std::str::from_utf8_unchecked(s) };
2449 wrap_by(w, "\\\"", |w| w.write(escape_by(s.into(), escape_char)))
2452 fn print_expr_to_string<W: Write>(
2456 ) -> Result<String, W::Error> {
2457 let mut buf = String::new();
2458 print_expr(ctx, &mut buf, env, expr).map_err(|e| match e {
2459 Error::NotImpl(m) => Error::NotImpl(m),
2460 _ => Error::Fail(format!("Failed: {}", e)),
2465 pub fn print_expr<W: Write>(
2469 ast::Expr(_, _, expr): &ast::Expr,
2470 ) -> Result<(), W::Error> {
2471 fn adjust_id<'a>(env: &ExprEnv, id: &'a str) -> Cow<'a, str> {
2472 let s: Cow<'a, str> = match env.codegen_env {
2474 if env.is_namespaced
2478 .rposition(|c| *c == b'\\')
2479 .map_or(true, |i| i < 1)
2481 strip_global_ns(id).into()
2490 fn print_expr_id<'a, W: Write>(w: &mut W, env: &ExprEnv, s: &'a str) -> Result<(), W::Error> {
2491 w.write(adjust_id(env, s))
2493 fn fmt_class_name<'a>(is_class_constant: bool, id: Cow<'a, str>) -> Cow<'a, str> {
2494 let cn: Cow<'a, str> = if is_xhp(strip_global_ns(&id)) {
2495 escaper::escape(strip_global_ns(&mangle(id.into())))
2499 escaper::escape(strip_global_ns(&id)).to_string().into()
2501 if is_class_constant {
2502 format!("\\\\{}", cn).into()
2507 fn get_class_name_from_id<'e>(
2509 env: Option<&'e HhasBodyEnv>,
2510 should_format: bool,
2511 is_class_constant: bool,
2514 if id == classes::SELF || id == classes::PARENT || id == classes::STATIC {
2515 let name = ctx.special_class_resolver.resolve(env, id);
2516 return fmt_class_name(is_class_constant, name);
2518 fn get<'a>(should_format: bool, is_class_constant: bool, id: &'a str) -> Cow<'a, str> {
2520 fmt_class_name(is_class_constant, id.into())
2527 let alloc = bumpalo::Bump::new();
2528 let class_id = ClassType::from_ast_name(&alloc, id);
2529 let id = class_id.to_raw_string();
2530 get(should_format, is_class_constant, id)
2534 get(should_format, is_class_constant, id)
2537 fn handle_possible_colon_colon_class_expr<W: Write>(
2543 ) -> Result<Option<()>, W::Error> {
2544 match e_.as_class_const() {
2546 ast::ClassId(_, _, ast::ClassId_::CIexpr(ast::Expr(_, _, ast::Expr_::Id(id)))),
2548 )) if is_class(&s2) && !(is_self(&id.1) || is_parent(&id.1) || is_static(&id.1)) => {
2550 let s1 = get_class_name_from_id(ctx, env.codegen_env, false, false, &id.1);
2552 print_expr_id(w, env, s1.as_ref())?
2554 print_expr_string(w, s1.as_bytes())?
2561 use ast::Expr_ as E_;
2563 E_::Id(id) => print_expr_id(w, env, id.1.as_ref()),
2564 E_::Lvar(lid) => w.write(escaper::escape(&(lid.1).1)),
2566 if f.contains('E') || f.contains('e') {
2570 .map_err(|_| Error::fail(format!("ParseFloatError: {}", f)))?
2572 // to_uppercase() here because s might be "inf" or "nan"
2576 static ref UNSIGNED_EXP: Regex =
2577 Regex::new(r"(?P<first>E)(?P<second>\d+)").unwrap();
2578 static ref SIGNED_SINGLE_DIGIT_EXP: Regex =
2579 Regex::new(r"(?P<first>E[+-])(?P<second>\d$)").unwrap();
2581 // turn mEn into mE+n
2582 let s = UNSIGNED_EXP.replace(&s, "${first}+${second}");
2583 // turn mE+n or mE-n into mE+0n or mE-0n (where n is a single digit)
2584 let s = SIGNED_SINGLE_DIGIT_EXP.replace(&s, "${first}0${second}");
2590 E_::Int(i) => print_expr_int(w, i.as_ref()),
2591 E_::String(s) => print_expr_string(w, s),
2592 E_::Null => w.write("NULL"),
2593 E_::True => w.write("true"),
2594 E_::False => w.write("false"),
2595 // For arrays and collections, we are making a conscious decision to not
2596 // match HHMV has HHVM's emitter has inconsistencies in the pretty printer
2597 // https://fburl.com/tzom2qoe
2598 E_::Collection(c) if (c.0).1 == "vec" || (c.0).1 == "dict" || (c.0).1 == "keyset" => {
2600 square(w, |w| print_afields(ctx, w, env, &c.2))
2602 E_::Collection(c) => {
2603 let name = strip_ns((c.0).1.as_str());
2604 let name = types::fix_casing(&name);
2606 "Set" | "Pair" | "Vector" | "Map" | "ImmSet" | "ImmVector" | "ImmMap" => {
2609 wrap_by_(w, " {", "}", |w| {
2610 Ok(if !c.2.is_empty() {
2612 print_afields(ctx, w, env, &c.2)?;
2617 _ => Err(Error::fail(format!(
2618 "Default value for an unknow collection - {}",
2623 E_::Shape(fl) => print_expr_darray(ctx, w, env, print_shape_field_name, fl),
2625 let (bop, e1, e2) = &**x;
2626 print_expr(ctx, w, env, e1)?;
2630 print_expr(ctx, w, env, e2)
2633 let (e, _, es, unpacked_element) = &**c;
2635 Some(ast_defs::Id(_, call_id)) => {
2636 w.write(lstrip(adjust_id(env, &call_id).as_ref(), "\\\\"))?
2639 let buf = print_expr_to_string::<W>(ctx, env, e)?;
2640 w.write(lstrip(&buf, "\\\\"))?
2644 concat_by(w, ", ", &es, |w, e| print_expr(ctx, w, env, e))?;
2645 match unpacked_element {
2651 // TODO: Should probably have ... also but we are not doing that in ocaml
2652 print_expr(ctx, w, env, e)
2658 let (cid, _, es, unpacked_element, _) = &**x;
2659 match cid.2.as_ciexpr() {
2662 match ci_expr.2.as_id() {
2663 Some(ast_defs::Id(_, cname)) => w.write(lstrip(
2666 &ClassType::from_ast_name(&bumpalo::Bump::new(), cname)
2672 let buf = print_expr_to_string::<W>(ctx, env, ci_expr)?;
2673 w.write(lstrip(&buf, "\\\\"))?
2677 concat_by(w, ", ", es, |w, e| print_expr(ctx, w, env, e))?;
2678 match unpacked_element {
2682 print_expr(ctx, w, env, e)
2688 match cid.2.as_ci() {
2690 // Xml exprs rewritten as New exprs come
2692 print_xml(ctx, w, env, &id.1, es)
2694 None => not_impl!(),
2700 w.write(lstrip(adjust_id(env, &(r.0).1).as_ref(), "\\\\"))?;
2701 print_key_values(ctx, w, env, &r.1)
2703 E_::ClassGet(cg) => {
2705 ast::ClassId_::CIexpr(e) => match e.as_id() {
2706 Some(id) => w.write(&get_class_name_from_id(
2709 true, /* should_format */
2710 false, /* is_class_constant */
2713 _ => print_expr(ctx, w, env, e)?,
2715 _ => return Err(Error::fail("TODO Unimplemented unexpected non-CIexpr")),
2719 ast::ClassGetExpr::CGstring((_, litstr)) => w.write(escaper::escape(litstr)),
2720 ast::ClassGetExpr::CGexpr(e) => print_expr(ctx, w, env, e),
2723 E_::ClassConst(cc) => {
2724 if let Some(e1) = (cc.0).2.as_ciexpr() {
2725 handle_possible_colon_colon_class_expr(ctx, w, env, false, expr)?.map_or_else(
2728 match e1.2.as_id() {
2729 Some(ast_defs::Id(_, s1)) => {
2731 get_class_name_from_id(ctx, env.codegen_env, true, true, s1);
2732 concat_str_by(w, "::", [&s1.into(), s2])
2735 print_expr(ctx, w, env, e1)?;
2744 Err(Error::fail("TODO: Only expected CIexpr in class_const"))
2747 E_::Unop(u) => match u.0 {
2748 ast::Uop::Upincr => {
2749 print_expr(ctx, w, env, &u.1)?;
2752 ast::Uop::Updecr => {
2753 print_expr(ctx, w, env, &u.1)?;
2758 print_expr(ctx, w, env, &u.1)
2762 print_expr(ctx, w, env, &og.0)?;
2763 w.write(match og.2 {
2764 ast::OgNullFlavor::OGNullthrows => "->",
2765 ast::OgNullFlavor::OGNullsafe => "?->",
2767 print_expr(ctx, w, env, &og.1)
2771 print_expr(ctx, w, env, e)
2773 E_::ArrayGet(ag) => {
2774 print_expr(ctx, w, env, &ag.0)?;
2776 option(w, &ag.1, |w, e: &ast::Expr| {
2777 handle_possible_colon_colon_class_expr(ctx, w, env, true, &e.2)
2779 .unwrap_or_else(|| print_expr(ctx, w, env, e))
2783 E_::String2(ss) => concat_by(w, " . ", ss, |w, s| print_expr(ctx, w, env, s)),
2784 E_::PrefixedString(s) => {
2787 print_expr(ctx, w, env, &s.1)
2790 print_expr(ctx, w, env, &eif.0)?;
2792 option(w, &eif.1, |w, etrue| print_expr(ctx, w, env, etrue))?;
2794 print_expr(ctx, w, env, &eif.2)
2797 paren(w, |w| print_hint(w, false, &c.0))?;
2798 print_expr(ctx, w, env, &c.1)
2801 print_expr(ctx, w, env, &p.1)?;
2803 print_expr(ctx, w, env, &p.2)
2806 print_expr(ctx, w, env, &i.0)?;
2808 print_hint(w, true, &i.1)
2811 print_expr(ctx, w, env, &a.0)?;
2812 w.write(if a.2 { " ?as " } else { " as " })?;
2813 print_hint(w, true, &a.1)
2815 E_::Varray(va) => print_expr_varray(ctx, w, env, &va.1),
2816 E_::Darray(da) => print_expr_darray(ctx, w, env, print_expr, &da.1),
2817 E_::Tuple(t) => wrap_by_(w, "varray[", "]", |w| {
2818 // A tuple is represented by a varray when using reflection.
2819 concat_by(w, ", ", t, |w, i| print_expr(ctx, w, env, i))
2821 E_::List(l) => wrap_by_(w, "list(", ")", |w| {
2822 concat_by(w, ", ", l, |w, i| print_expr(ctx, w, env, i))
2826 print_afield(ctx, w, env, y)
2830 print_expr(ctx, w, env, e)
2833 print_import_flavor(w, &i.0)?;
2835 print_expr(ctx, w, env, &i.1)
2837 E_::Xml(_) => Err(Error::fail(
2838 "expected Xml to be converted to New during rewriting",
2840 E_::Efun(f) => print_efun(ctx, w, env, &f.0, &f.1),
2841 E_::FunctionPointer(fp) => {
2842 let (fp_id, targs) = &**fp;
2844 ast::FunctionPtrId::FPId(ast::Id(_, sid)) => {
2845 w.write(lstrip(adjust_id(env, &sid).as_ref(), "\\\\"))?
2847 ast::FunctionPtrId::FPClassConst(ast::ClassId(_, _, class_id), (_, meth_name)) => {
2849 ast::ClassId_::CIexpr(e) => match e.as_id() {
2850 Some(id) => w.write(&get_class_name_from_id(
2853 true, /* should_format */
2854 false, /* is_class_constant */
2857 _ => print_expr(ctx, w, env, e)?,
2860 return Err(Error::fail(
2861 "TODO Unimplemented unexpected non-CIexpr in function pointer",
2869 wrap_by_(w, "<", ">", |w| {
2870 concat_by(w, ", ", targs, |w, _targ| w.write("_"))
2873 E_::Omitted => Ok(()),
2875 if ctx.dump_lambdas {
2879 concat_by(w, ", ", &fun_.params, |w, param| {
2880 print_fparam(ctx, w, env, param)
2884 print_block_(ctx, w, env, &fun_.body.fb_ast, None)
2888 "expected Lfun to be converted to Efun during closure conversion print_expr",
2892 E_::Callconv(_) => Err(Error::fail("illegal default value")),
2893 E_::ETSplice(splice) => {
2895 print_expr(ctx, w, env, splice)?;
2898 _ => Err(Error::fail(format!(
2899 "TODO Unimplemented: Cannot print: {:?}",
2905 fn print_xml<W: Write>(
2911 ) -> Result<(), W::Error> {
2912 use ast::{Expr as E, Expr_ as E_};
2914 fn syntax_error<W: Write>(_: &W) -> crate::write::Error<<W as crate::write::Write>::Error> {
2915 Error::NotImpl(String::from("print_xml: unexpected syntax"))
2917 fn print_xhp_attr<W: Write>(
2921 attr: &(ast_defs::ShapeFieldName, ast::Expr),
2922 ) -> Result<(), W::Error> {
2924 (ast_defs::ShapeFieldName::SFlitStr(s), e) => print_key_value_(
2929 |_, w, _, k| print_expr_string(w, k.as_slice()),
2932 _ => Err(syntax_error(w)),
2936 let (attrs, children) = if es.len() < 2 {
2937 Err(syntax_error(w))
2939 match (&es[0], &es[1]) {
2940 (E(_, _, E_::Shape(attrs)), E(_, _, E_::Varray(children))) => Ok((attrs, &children.1)),
2941 _ => Err(syntax_error(w)),
2945 codegen_env: env.codegen_env,
2947 write!(w, "new {}", mangle(id.into()))?;
2949 wrap_by_(w, "darray[", "]", |w| {
2950 concat_by(w, ", ", attrs, |w, attr| print_xhp_attr(ctx, w, &env, attr))
2953 print_expr_varray(ctx, w, &env, children)?;
2954 w.write(", __FILE__, __LINE__")
2958 fn print_efun<W: Write>(
2963 use_list: &[ast::Lid],
2964 ) -> Result<(), W::Error> {
2966 f.fun_kind.is_fasync() || f.fun_kind.is_fasync_generator(),
2969 w.write("function ")?;
2971 concat_by(w, ", ", &f.params, |w, p| print_fparam(ctx, w, env, p))
2974 if !use_list.is_empty() {
2977 concat_by(w, ", ", use_list, |w: &mut W, ast::Lid(_, id)| {
2978 w.write(local_id::get_name(id))
2983 print_block_(ctx, w, env, &f.body.fb_ast, None)
2986 fn print_block<W: Write>(
2990 block: &[ast::Stmt],
2991 ident: Option<&str>,
2992 ) -> Result<(), W::Error> {
2994 [] | [ast::Stmt(_, ast::Stmt_::Noop)] => Ok(()),
2995 [ast::Stmt(_, ast::Stmt_::Block(b))] if b.len() == 1 => print_block_(ctx, w, env, b, ident),
2996 [_, _, ..] => print_block_(ctx, w, env, block, ident),
2997 [stmt] => print_statement(ctx, w, env, stmt, None),
3001 fn print_block_<W: Write>(
3005 block: &[ast::Stmt],
3006 ident: Option<&str>,
3007 ) -> Result<(), W::Error> {
3008 wrap_by_(w, "{\\n", "}\\n", |w| {
3009 concat(w, block, |w, stmt| {
3010 option(w, ident, |w, i: &str| w.write(i))?;
3011 print_statement(ctx, w, env, stmt, Some(" "))
3013 option(w, ident, |w, i: &str| w.write(i))
3017 fn print_statement<W: Write>(
3022 ident: Option<&str>,
3023 ) -> Result<(), W::Error> {
3024 use ast::Stmt_ as S_;
3026 S_::Return(expr) => {
3027 option(w, ident, |w, i: &str| w.write(i))?;
3028 wrap_by_(w, "return", ";\\n", |w| {
3029 option(w, &**expr, |w, e| {
3031 print_expr(ctx, w, env, e)
3036 option(w, ident, |w, i: &str| w.write(i))?;
3037 print_expr(ctx, w, env, &**expr)?;
3040 S_::Throw(expr) => {
3041 option(w, ident, |w, i: &str| w.write(i))?;
3042 wrap_by_(w, "throw ", ";\\n", |w| print_expr(ctx, w, env, &**expr))
3045 option(w, ident, |w, i: &str| w.write(i))?;
3046 w.write("break;\\n")
3049 option(w, ident, |w, i: &str| w.write(i))?;
3050 w.write("continue;\\n")
3053 let (cond, block) = &**x;
3054 option(w, ident, |w, i: &str| w.write(i))?;
3055 wrap_by_(w, "while (", ") ", |w| print_expr(ctx, w, env, cond))?;
3056 print_block(ctx, w, env, block.as_ref(), ident)
3059 let (cond, if_block, else_block) = &**x;
3060 option(w, ident, |w, i: &str| w.write(i))?;
3061 wrap_by_(w, "if (", ") ", |w| print_expr(ctx, w, env, cond))?;
3062 print_block(ctx, w, env, if_block, ident)?;
3063 let mut buf = String::new();
3064 print_block(ctx, &mut buf, env, else_block, ident).map_err(|e| match e {
3065 Error::NotImpl(m) => Error::NotImpl(m),
3066 _ => Error::Fail(format!("Failed: {}", e)),
3068 w.write_if(!buf.is_empty(), " else ")?;
3069 w.write_if(!buf.is_empty(), buf)
3071 S_::Block(block) => {
3072 option(w, ident, |w, i: &str| w.write(i))?;
3073 print_block_(ctx, w, env, block, ident)
3076 /* TODO(T29869930) */
3077 _ => w.write("TODO Unimplemented NYI: Default value printing"),
3081 fn print_fparam<W: Write>(
3085 param: &ast::FunParam,
3086 ) -> Result<(), W::Error> {
3087 if let Some(ast_defs::ParamKind::Pinout) = param.callconv {
3090 if param.is_variadic {
3093 option(w, &(param.type_hint).1, |w, h| {
3094 print_hint(w, true, h)?;
3097 w.write(¶m.name)?;
3098 option(w, ¶m.expr, |w, e| {
3100 print_expr(ctx, w, env, e)
3104 fn print_bop<W: Write>(w: &mut W, bop: &ast_defs::Bop) -> Result<(), W::Error> {
3107 Bop::Plus => w.write("+"),
3108 Bop::Minus => w.write("-"),
3109 Bop::Star => w.write("*"),
3110 Bop::Slash => w.write("/"),
3111 Bop::Eqeq => w.write("=="),
3112 Bop::Eqeqeq => w.write("==="),
3113 Bop::Starstar => w.write("**"),
3114 Bop::Eq(None) => w.write("="),
3115 Bop::Eq(Some(bop)) => {
3119 Bop::Ampamp => w.write("&&"),
3120 Bop::Barbar => w.write("||"),
3121 Bop::Lt => w.write("<"),
3122 Bop::Lte => w.write("<="),
3123 Bop::Cmp => w.write("<=>"),
3124 Bop::Gt => w.write(">"),
3125 Bop::Gte => w.write(">="),
3126 Bop::Dot => w.write("."),
3127 Bop::Amp => w.write("&"),
3128 Bop::Bar => w.write("|"),
3129 Bop::Ltlt => w.write("<<"),
3130 Bop::Gtgt => w.write(">>"),
3131 Bop::Percent => w.write("%"),
3132 Bop::Xor => w.write("^"),
3133 Bop::Diff => w.write("!="),
3134 Bop::Diff2 => w.write("!=="),
3135 Bop::QuestionQuestion => w.write("??"),
3139 fn print_hint<W: Write>(w: &mut W, ns: bool, hint: &ast::Hint) -> Result<(), W::Error> {
3140 let alloc = bumpalo::Bump::new();
3141 let h = emit_type_hint::fmt_hint(&alloc, &[], false, hint).map_err(|e| match e {
3142 Unrecoverable(s) => Error::fail(s),
3143 _ => Error::fail("Error printing hint"),
3146 w.write(escaper::escape(h))
3148 w.write(escaper::escape(strip_ns(&h)))
3152 fn print_import_flavor<W: Write>(w: &mut W, flavor: &ast::ImportFlavor) -> Result<(), W::Error> {
3153 use ast::ImportFlavor as F;
3154 w.write(match flavor {
3155 F::Include => "include",
3156 F::Require => "require",
3157 F::IncludeOnce => "include_once",
3158 F::RequireOnce => "require_once",
3162 fn print_param_user_attributes<W: Write>(
3166 ) -> Result<(), W::Error> {
3167 match param.user_attributes.as_ref()[..] {
3169 _ => square(w, |w| print_attributes(ctx, w, ¶m.user_attributes)),
3173 fn print_span<W: Write>(
3175 &HhasSpan(line_begin, line_end): &HhasSpan,
3176 ) -> Result<(), W::Error> {
3177 write!(w, "({},{})", line_begin, line_end)
3180 fn print_fun_attrs<W: Write>(
3184 ) -> Result<(), W::Error> {
3185 use hhas_attribute::*;
3186 let user_attrs = f.attributes.as_ref();
3187 let mut special_attrs = vec![];
3188 if has_meth_caller(user_attrs) {
3189 special_attrs.push("builtin");
3190 special_attrs.push("is_meth_caller");
3192 if f.is_interceptable() {
3193 special_attrs.push("interceptable");
3195 if has_foldable(user_attrs) {
3196 special_attrs.push("foldable");
3198 if has_provenance_skip_frame(user_attrs) {
3199 special_attrs.push("prov_skip_frame");
3201 if f.is_no_injection() {
3202 special_attrs.push("no_injection");
3204 if ctx.is_system_lib() || (has_dynamically_callable(user_attrs) && !f.is_memoize_impl()) {
3205 special_attrs.push("dyn_callable")
3207 if ctx.is_system_lib() {
3208 special_attrs.push("unique");
3209 special_attrs.push("builtin");
3210 special_attrs.push("persistent");
3212 print_special_and_user_attrs(ctx, w, &special_attrs, user_attrs)
3215 fn print_special_and_user_attrs<W: Write>(
3219 users: &[HhasAttribute],
3220 ) -> Result<(), W::Error> {
3221 if !users.is_empty() || !specials.is_empty() {
3223 concat_str_by(w, " ", specials)?;
3224 if !specials.is_empty() && !users.is_empty() {
3227 print_attributes(ctx, w, users)
3234 fn print_upper_bounds<'arena, W: Write>(
3236 ubs: impl AsRef<[Pair<Str<'arena>, Slice<'arena, HhasTypeInfo<'arena>>>]>,
3237 ) -> Result<(), W::Error> {
3238 braces(w, |w| concat_by(w, ", ", ubs, print_upper_bound))
3241 fn print_upper_bound<'arena, W: Write>(
3243 Pair(id, tys): &Pair<Str<'arena>, Slice<'arena, HhasTypeInfo>>,
3244 ) -> Result<(), W::Error> {
3246 concat_str_by(w, " ", [id.as_str(), "as", ""])?;
3247 concat_by(w, ", ", &tys, print_type_info)
3251 fn print_upper_bounds_<'arena, W: Write>(
3253 ubs: impl AsRef<[Pair<Str<'arena>, Slice<'arena, HhasTypeInfo<'arena>>>]>,
3254 ) -> Result<(), W::Error> {
3255 braces(w, |w| concat_by(w, ", ", ubs, print_upper_bound_))
3258 fn print_upper_bound_<'arena, W: Write>(
3260 Pair(id, tys): &Pair<Str<'arena>, Slice<'arena, HhasTypeInfo<'arena>>>,
3261 ) -> Result<(), W::Error> {
3263 concat_str_by(w, " ", [id.as_str(), "as", ""])?;
3264 concat_by(w, ", ", &tys, print_type_info)
3268 fn print_type_info<W: Write>(w: &mut W, ti: &HhasTypeInfo) -> Result<(), W::Error> {
3269 print_type_info_(w, false, ti)
3272 fn print_type_flags<W: Write>(
3274 flag: constraint::ConstraintFlags,
3275 ) -> Result<(), W::Error> {
3276 let mut first = true;
3277 let mut print_space = |w: &mut W| -> Result<(), W::Error> {
3284 use constraint::ConstraintFlags as F;
3285 if flag.contains(F::DISPLAY_NULLABLE) {
3287 w.write("display_nullable")?;
3289 if flag.contains(F::EXTENDED_HINT) {
3291 w.write("extended_hint")?;
3293 if flag.contains(F::NULLABLE) {
3295 w.write("nullable")?;
3298 if flag.contains(F::SOFT) {
3302 if flag.contains(F::TYPE_CONSTANT) {
3304 w.write("type_constant")?;
3307 if flag.contains(F::TYPE_VAR) {
3309 w.write("type_var")?;
3312 if flag.contains(F::UPPERBOUND) {
3314 w.write("upper_bound")?;
3319 fn print_type_info_<W: Write>(w: &mut W, is_enum: bool, ti: &HhasTypeInfo) -> Result<(), W::Error> {
3320 let print_quote_str = |w: &mut W, opt: &Option<String>| {
3324 |w, s: &String| quotes(w, |w| w.write(escape(s))),
3331 &Option::from(ti.user_type.map(|n| n.as_str().to_owned())),
3337 &Option::from(ti.type_constraint.name.map(|n| n.as_str().to_owned())),
3341 print_type_flags(w, ti.type_constraint.flags)
3345 fn print_typedef_info<W: Write>(w: &mut W, ti: &HhasTypeInfo) -> Result<(), W::Error> {
3347 w.write(quote_string(
3348 ti.type_constraint.name.as_ref().map_or("", |n| n.as_str()),
3350 let flags = ti.type_constraint.flags & constraint::ConstraintFlags::NULLABLE;
3351 if !flags.is_empty() {
3352 wrap_by(w, " ", |w| {
3355 ti.type_constraint.flags & constraint::ConstraintFlags::NULLABLE,
3363 fn print_extends<W: Write>(w: &mut W, base: Option<&str>) -> Result<(), W::Error> {
3366 Some(b) => concat_str_by(w, " ", [" extends", b]),
3370 fn print_record_field<W: Write>(
3373 Field(name, type_info, intial_value): &Field,
3374 ) -> Result<(), W::Error> {
3376 w.write(".property ")?;
3377 match intial_value {
3378 Just(_) => w.write("[public] ")?,
3379 Nothing => w.write("[public sys_initial_val] ")?,
3381 print_type_info(w, type_info)?;
3382 concat_str_by(w, " ", ["", name.as_str(), "="])?;
3384 ctx.block(w, |c, w| {
3386 match intial_value {
3387 Nothing => w.write("uninit")?,
3388 Just(value) => triple_quotes(w, |w| print_adata(c, w, value))?,
3394 fn print_record_def<W: Write>(
3397 record: &HhasRecord,
3398 ) -> Result<(), W::Error> {
3400 if record.is_abstract {
3401 concat_str_by(w, " ", [".record", record.name.to_raw_string()])?;
3403 concat_str_by(w, " ", [".record", "[final]", record.name.to_raw_string()])?;
3406 print_span(w, &record.span)?;
3409 Option::from(record.base.as_ref().map(|b| b.to_raw_string())),
3414 ctx.block(w, |c, w| {
3415 concat(w, &record.fields, |w, rf| print_record_field(c, w, rf))
3422 /// Convert an `Expr` to a `String` of the equivalent source code.
3424 /// This is a debugging tool abusing a printer written for bytecode
3425 /// emission. It does not support all Hack expressions, and panics
3426 /// on unsupported syntax.
3428 /// If you have an `Expr` with positions, you are much better off
3429 /// getting the source code at those positions rather than using this.
3430 pub fn expr_to_string_lossy(mut ctx: Context, expr: &ast::Expr) -> String {
3431 ctx.dump_lambdas = true;
3433 let env = ExprEnv { codegen_env: None };
3434 let mut escaped_src = String::new();
3435 print_expr(&mut ctx, &mut escaped_src, &env, expr).expect("Printing failed");
3437 let bs = escaper::unescape_double(&escaped_src).expect("Unescaping failed");
3438 let s = String::from_utf8_lossy(&bs);