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_type_hint_rust as emit_type_hint;
17 use env::{iterator::Id as IterId, local::Type as Local, Env as BodyEnv};
18 use escaper::{escape, escape_by, is_lit_printable};
19 use hhas_adata_rust::HhasAdata;
20 use hhas_adata_rust::{
21 DARRAY_PREFIX, DICT_PREFIX, KEYSET_PREFIX, LEGACY_DICT_PREFIX, LEGACY_VEC_PREFIX,
22 VARRAY_PREFIX, VEC_PREFIX,
24 use hhas_attribute_rust::{self as hhas_attribute, HhasAttribute};
25 use hhas_body_rust::HhasBody;
26 use hhas_class_rust::{self as hhas_class, HhasClass};
27 use hhas_constant_rust::HhasConstant;
28 use hhas_function_rust::HhasFunction;
29 use hhas_method_rust::{HhasMethod, HhasMethodFlags};
30 use hhas_param_rust::HhasParam;
31 use hhas_pos_rust::Span;
32 use hhas_program_rust::HhasProgram;
33 use hhas_property_rust::HhasProperty;
34 use hhas_record_def_rust::{Field, HhasRecord};
35 use hhas_symbol_refs_rust::{HhasSymbolRefs, IncludePath};
36 use hhas_type::{constraint, Info as HhasTypeInfo};
37 use hhas_type_const::HhasTypeConstant;
38 use hhas_typedef_rust::Typedef as HhasTypedef;
40 use hhbc_id_rust::{class, Id};
41 use hhbc_string_utils_rust::{
42 float, integer, is_class, is_parent, is_self, is_static, is_xhp, lstrip, mangle, quote_string,
43 quote_string_with_escape, strip_global_ns, strip_ns, triple_quote_string, types,
45 use instruction_sequence_rust::{Error::Unrecoverable, InstrSeq};
46 use label_rust::Label;
47 use lazy_static::lazy_static;
48 use naming_special_names_rust::classes;
49 use ocaml_helper::escaped;
50 use oxidized::{ast, ast_defs, doc_comment::DocComment, local_id, pos::Pos};
52 use runtime::TypedValue;
56 borrow::Cow, collections::BTreeSet, convert::TryInto, 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,
97 impl<'a> Context<'a> {
99 emitter: &'a mut Emitter,
100 path: Option<&'a RelativePath>,
101 dump_symbol_refs: bool,
108 indent: Indent::new(),
113 pub fn dump_symbol_refs(&self) -> bool {
114 self.dump_symbol_refs
117 /// Insert a newline with indentation
118 pub fn newline<W: Write>(&self, w: &mut W) -> Result<(), W::Error> {
123 /// Start a new indented block
124 pub fn block<W, F>(&mut self, w: &mut W, f: F) -> Result<(), W::Error>
127 F: FnOnce(&mut Self, &mut W) -> Result<(), W::Error>,
135 pub fn unblock<W, F>(&mut self, w: &mut W, f: F) -> Result<(), W::Error>
138 F: FnOnce(&mut Self, &mut W) -> Result<(), W::Error>,
146 /// Printing instruction list requies manually control indentation,
147 /// where indent_inc/indent_dec are called
148 pub fn indent_inc(&mut self) {
152 pub fn indent_dec(&mut self) {
156 pub fn is_system_lib(&self) -> bool {
163 pub codegen_env: Option<&'e BodyEnv<'e>>,
167 pub fn print_program<W: Write>(
171 ) -> Result<(), W::Error> {
174 let abs = p.to_absolute();
175 let p = escape(abs.to_str().ok_or(Error::InvalidUTF8)?);
177 concat_str_by(w, " ", ["#", p.as_ref(), "starts here"])?;
182 concat_str(w, [".filepath ", format!("\"{}\"", p).as_str(), ";"])?;
185 handle_not_impl(|| print_program_(ctx, w, prog))?;
188 concat_str_by(w, " ", ["#", p.as_ref(), "ends here"])?;
193 w.write("#starts here")?;
196 handle_not_impl(|| print_program_(ctx, w, prog))?;
199 w.write("#ends here")?;
206 fn get_fatal_op(f: &FatalOp) -> &str {
208 FatalOp::Parse => "Parse",
209 FatalOp::Runtime => "Runtime",
210 FatalOp::RuntimeOmitFrame => "RuntimeOmitFrame",
214 fn print_program_<W: Write>(
218 ) -> Result<(), W::Error> {
219 let is_hh = if prog.is_hh { "1" } else { "0" };
221 concat_str(w, [".hh_file ", is_hh, ";"])?;
223 if let Some((fop, p, msg)) = &prog.fatal {
225 let (line_begin, line_end, col_begin, col_end) = if p.is_none() {
228 p.info_pos_extended()
230 let pos = format!("{}:{},{}:{}", line_begin, col_begin, line_end, col_end);
239 escape(msg).as_ref(),
246 concat(w, &prog.adata, |w, a| print_adata_region(ctx, w, a))?;
248 print_main(ctx, w, &prog.main)?;
249 concat(w, &prog.functions, |w, f| print_fun_def(ctx, w, f))?;
250 concat(w, &prog.classes, |w, cd| print_class_def(ctx, w, cd))?;
251 concat(w, &prog.record_defs, |w, rd| print_record_def(ctx, w, rd))?;
252 concat(w, &prog.constants, |w, c| print_constant(ctx, w, c))?;
253 concat(w, &prog.typedefs, |w, td| print_typedef(ctx, w, td))?;
254 print_file_attributes(ctx, w, &prog.file_attributes)?;
256 if ctx.dump_symbol_refs() {
257 print_include_region(ctx, w, &prog.symbol_refs.includes)?;
258 print_symbol_ref_regions(ctx, w, &prog.symbol_refs)?;
263 fn print_include_region<W: Write>(
266 includes: &BTreeSet<IncludePath>,
267 ) -> Result<(), W::Error> {
268 fn print_path<W: Write>(w: &mut W, p: &Path) -> Result<(), W::Error> {
269 option(w, p.to_str(), |w, p: &str| write!(w, "\n {}", p))
271 fn print_if_exists<W: Write>(w: &mut W, p: &Path) -> Result<(), W::Error> {
278 fn print_include<W: Write>(
282 ) -> Result<(), W::Error> {
283 let include_roots = ctx.emitter.options().hhvm.include_roots.get();
284 match inc.into_doc_root_relative(include_roots) {
285 IncludePath::Absolute(p) => print_if_exists(w, Path::new(&p)),
286 IncludePath::SearchPathRelative(p) => {
287 let path_from_cur_dirname = ctx
289 .and_then(|p| p.path().parent())
290 .unwrap_or(Path::new(""))
292 if path_from_cur_dirname.exists() {
293 print_path(w, &path_from_cur_dirname)
295 let search_paths = ctx.emitter.options().server.include_search_paths.get();
296 for prefix in search_paths.iter() {
297 let path = Path::new(prefix).join(&p);
299 return print_path(w, &path);
305 IncludePath::IncludeRootRelative(v, p) => {
311 let doc_root = ctx.emitter.options().doc_root.get();
312 let resolved = Path::new(doc_root).join(ir).join(&p);
313 print_if_exists(w, &resolved)
315 .collect::<Result<_, _>>()?
319 IncludePath::DocRootRelative(p) => {
320 let doc_root = ctx.emitter.options().doc_root.get();
321 let resolved = Path::new(doc_root).join(&p);
322 print_if_exists(w, &resolved)
326 if !includes.is_empty() {
327 w.write("\n.includes {")?;
328 for inc in includes.into_iter() {
329 // TODO(hrust): avoid clone. Rethink onwership of inc in
330 // hhas_symbol_refs_rust::IncludePath::into_doc_root_relative
331 print_include(ctx, w, inc.clone())?;
339 fn print_symbol_ref_regions<W: Write>(
342 symbol_refs: &HhasSymbolRefs,
343 ) -> Result<(), W::Error> {
344 let mut print_region = |name, refs: &BTreeSet<String>| {
345 if !refs.is_empty() {
347 write!(w, ".{} {{", name)?;
348 ctx.block(w, |c, w| {
349 for s in refs.iter() {
360 print_region("constant_refs", &symbol_refs.constants)?;
361 print_region("function_refs", &symbol_refs.functions)?;
362 print_region("class_refs", &symbol_refs.classes)
365 fn print_adata_region<W: Write>(
369 ) -> Result<(), W::Error> {
370 concat_str_by(w, " ", [".adata", adata.id.as_str(), "= "])?;
371 triple_quotes(w, |w| print_adata(ctx, w, &adata.value))?;
376 fn print_typedef<W: Write>(ctx: &mut Context, w: &mut W, td: &HhasTypedef) -> Result<(), W::Error> {
379 print_typedef_attributes(ctx, w, td)?;
380 w.write(td.name.to_raw_string())?;
382 print_typedef_info(w, &td.type_info)?;
384 triple_quotes(w, |w| print_adata(ctx, w, &td.type_structure))?;
388 fn print_typedef_attributes<W: Write>(
392 ) -> Result<(), W::Error> {
393 let mut specials = vec![];
394 if ctx.is_system_lib() {
395 specials.push("persistent");
397 print_special_and_user_attrs(ctx, w, &specials[..], td.attributes.as_slice())
400 fn handle_not_impl<E: std::fmt::Debug, F: FnOnce() -> Result<(), E>>(f: F) -> Result<(), E> {
403 Err(Error::NotImpl(msg)) => {
404 println!("#### NotImpl: {}", msg);
405 eprintln!("NotImpl: {}", msg);
412 fn print_fun_def<W: Write>(
415 fun_def: &HhasFunction,
416 ) -> Result<(), W::Error> {
417 let body = &fun_def.body;
419 w.write(".function ")?;
420 print_upper_bounds(w, &body.upper_bounds)?;
422 print_fun_attrs(ctx, w, fun_def)?;
423 print_span(w, &fun_def.span)?;
425 option(w, &body.return_type_info, |w, ti| {
426 print_type_info(w, ti)?;
429 w.write(fun_def.name.to_raw_string())?;
430 print_params(ctx, w, fun_def.body.env.as_ref(), fun_def.params())?;
431 if fun_def.is_generator() {
432 w.write(" isGenerator")?;
434 if fun_def.is_async() {
435 w.write(" isAsync")?;
437 if fun_def.is_pair_generator() {
438 w.write(" isPairGenerator")?;
440 if fun_def.rx_disabled() {
441 w.write(" isRxDisabled")?;
445 ctx.block(w, |c, w| print_body(c, w, body))?;
451 fn print_requirement<W: Write>(
454 r: &(class::Type<'_>, hhas_class::TraitReqKind),
455 ) -> Result<(), W::Error> {
457 w.write(".require ")?;
459 (name, hhas_class::TraitReqKind::MustExtend) => {
460 write!(w, "extends <{}>;", name.to_raw_string())
462 (name, hhas_class::TraitReqKind::MustImplement) => {
463 write!(w, "implements <{}>;", name.to_raw_string())
468 fn print_type_constant<W: Write>(
471 c: &HhasTypeConstant,
472 ) -> Result<(), W::Error> {
474 concat_str_by(w, " ", [".const", &c.name, "isType"])?;
475 option(w, &c.initializer, |w, init| {
477 triple_quotes(w, |w| print_adata(ctx, w, init))
482 fn print_property_doc_comment<W: Write>(w: &mut W, p: &HhasProperty) -> Result<(), W::Error> {
483 if let Some(s) = p.doc_comment.as_ref() {
484 w.write(triple_quote_string(&s.0))?;
490 fn print_property_attributes<W: Write>(
493 property: &HhasProperty,
494 ) -> Result<(), W::Error> {
495 let mut special_attributes = vec![];
496 if property.is_late_init() {
497 special_attributes.push("late_init")
499 if property.is_no_bad_redeclare() {
500 special_attributes.push("no_bad_redeclare")
502 if property.initial_satisfies_tc() {
503 special_attributes.push("initial_satisfies_tc")
505 if property.no_implicit_null() {
506 special_attributes.push("no_implicit_null")
508 if property.has_system_initial() {
509 special_attributes.push("sys_initial_val")
511 if property.is_const() {
512 special_attributes.push("is_const")
514 if property.is_deep_init() {
515 special_attributes.push("deep_init")
517 if property.is_lsb() {
518 special_attributes.push("lsb")
520 if property.is_static() {
521 special_attributes.push("static")
523 special_attributes.push(property.visibility.as_ref());
524 special_attributes.reverse();
527 concat_by(w, " ", &special_attributes, |w, a| w.write(a))?;
528 if !special_attributes.is_empty() && !property.attributes.is_empty() {
531 print_attributes(ctx, w, &property.attributes)?;
535 fn print_property_type_info<W: Write>(w: &mut W, p: &HhasProperty) -> Result<(), W::Error> {
536 print_type_info(w, &p.type_info)?;
540 fn print_property<W: Write>(
543 class_def: &HhasClass,
544 property: &HhasProperty,
545 ) -> Result<(), W::Error> {
547 w.write(" .property ")?;
548 print_property_attributes(ctx, w, property)?;
549 print_property_doc_comment(w, property)?;
550 print_property_type_info(w, property)?;
551 w.write(property.name.to_raw_string())?;
553 let initial_value = property.initial_value.as_ref();
554 if class_def.is_closure() || initial_value == Some(&TypedValue::Uninit) {
557 triple_quotes(w, |w| match initial_value {
558 None => w.write("N;"),
559 Some(value) => print_adata(ctx, w, &value),
565 fn print_constant<W: Write>(
569 ) -> Result<(), W::Error> {
572 w.write(c.name.to_raw_string())?;
573 match c.value.as_ref() {
574 Some(TypedValue::Uninit) => w.write(" = uninit")?,
577 triple_quotes(w, |w| print_adata(ctx, w, value))?;
584 fn print_enum_ty<W: Write>(ctx: &mut Context, w: &mut W, c: &HhasClass) -> Result<(), W::Error> {
585 if let Some(et) = c.enum_type.as_ref() {
587 w.write(".enum_ty ")?;
588 print_type_info_(w, true, et)?;
594 fn print_doc_comment<W: Write>(
597 doc_comment: &Option<DocComment>,
598 ) -> Result<(), W::Error> {
599 if let Some(cmt) = doc_comment {
601 write!(w, ".doc {};", triple_quote_string(&cmt.0))?;
606 fn print_use_precedence<W: Write>(
609 (id1, id2, ids): &(class::Type, class::Type, Vec<class::Type>),
610 ) -> Result<(), W::Error> {
612 concat_str(w, [id1.to_raw_string(), "::", id2.to_raw_string()])?;
613 w.write(" insteadof ")?;
614 let unique_ids: IndexSet<&str> = ids.iter().map(|i| i.to_raw_string()).collect();
615 concat_str_by(w, " ", unique_ids.iter().collect::<Vec<_>>())?;
619 fn print_use_as_visibility<W: Write>(w: &mut W, u: ast::UseAsVisibility) -> Result<(), W::Error> {
621 ast::UseAsVisibility::UseAsPublic => "public",
622 ast::UseAsVisibility::UseAsPrivate => "private",
623 ast::UseAsVisibility::UseAsProtected => "protected",
624 ast::UseAsVisibility::UseAsFinal => "final",
628 fn print_use_alias<W: Write>(
631 (ido1, id, ido2, kindl): &(
635 &Vec<ast::UseAsVisibility>,
637 ) -> Result<(), W::Error> {
639 let id = id.to_raw_string();
643 |w, i: &class::Type| concat_str(w, [i.to_raw_string(), "::", id]),
647 if !kindl.is_empty() {
649 concat_by(w, " ", kindl, |w, k| print_use_as_visibility(w, *k))
652 w.write_if(!kindl.is_empty() && ido2.is_some(), " ")?;
653 option(w, ido2, |w, i: &class::Type| w.write(i.to_raw_string()))?;
657 fn print_method_trait_resolutions<W: Write>(
660 (mtr, kind_as_tring): &(&ast::MethodRedeclaration, class::Type),
661 ) -> Result<(), W::Error> {
666 kind_as_tring.to_raw_string(),
669 w.write_if(mtr.fun_kind.is_async(), "async ")?;
671 w.write_if(mtr.final_, "final ")?;
672 w.write(mtr.visibility.to_string())?;
673 w.write_if(mtr.abstract_, " abstract")?;
674 w.write_if(mtr.static_, " static")
676 write!(w, " {};", mtr.name.1)
679 fn print_uses<W: Write>(ctx: &mut Context, w: &mut W, c: &HhasClass) -> Result<(), W::Error> {
680 if c.uses.is_empty() && c.method_trait_resolutions.is_empty() {
683 let unique_ids: IndexSet<&str> = c.uses.iter().map(|e| strip_global_ns(e)).collect();
684 let unique_ids: Vec<_> = unique_ids.into_iter().collect();
688 concat_by(w, " ", unique_ids, |w, id| w.write(id))?;
690 if c.use_aliases.is_empty()
691 && c.use_precedences.is_empty()
692 && c.method_trait_resolutions.is_empty()
697 ctx.block(w, |ctx, w| {
698 for x in &c.use_precedences {
699 print_use_precedence(ctx, w, x)?;
701 for x in &c.use_aliases {
702 print_use_alias(ctx, w, x)?;
704 for x in &c.method_trait_resolutions {
705 print_method_trait_resolutions(ctx, w, x)?;
715 fn print_class_special_attributes<W: Write>(
719 ) -> Result<(), W::Error> {
720 let user_attrs = &c.attributes;
721 let is_system_lib = ctx.is_system_lib();
723 let mut special_attributes: Vec<&str> = vec![];
724 if c.needs_no_reifiedinit() {
725 special_attributes.push("noreifiedinit")
727 if c.no_dynamic_props() {
728 special_attributes.push("no_dynamic_props")
731 special_attributes.push("is_const")
733 if hhas_attribute::has_foldable(user_attrs) {
734 special_attributes.push("foldable")
737 special_attributes.extend(&["persistent", "builtin", "unique"])
739 if hhas_attribute::has_dynamically_constructible(user_attrs) {
740 special_attributes.push("dyn_constructible");
743 special_attributes.push("nontop");
745 if c.is_closure() && !is_system_lib {
746 special_attributes.push("unique");
749 special_attributes.push("no_override");
752 special_attributes.push("trait");
754 if c.is_interface() {
755 special_attributes.push("interface");
758 special_attributes.push("final");
761 special_attributes.push("sealed");
763 if c.enum_type.is_some() {
764 special_attributes.push("enum");
767 special_attributes.push("abstract");
769 if special_attributes.is_empty() && user_attrs.is_empty() {
773 special_attributes.reverse();
774 wrap_by_(w, "[", "] ", |w| {
775 concat_by(w, " ", &special_attributes, |w, a| w.write(a))?;
777 !special_attributes.is_empty() && !user_attrs.is_empty(),
780 print_attributes(ctx, w, &user_attrs)
784 fn print_implements<W: Write>(
786 implements: &Vec<class::Type<'_>>,
787 ) -> Result<(), W::Error> {
788 if implements.is_empty() {
791 w.write(" implements (")?;
797 .map(|x| x.to_raw_string())
798 .collect::<Vec<_>>(),
803 fn print_shadowed_tparams<W: Write>(
805 shadowed_tparams: &[String],
806 ) -> Result<(), W::Error> {
807 braces(w, |w| concat_str_by(w, ", ", shadowed_tparams))
810 fn print_method_def<W: Write>(
813 method_def: &HhasMethod,
814 ) -> Result<(), W::Error> {
815 let body = &method_def.body;
817 w.write(" .method ")?;
818 print_shadowed_tparams(w, &body.shadowed_tparams)?;
819 print_upper_bounds(w, &body.upper_bounds)?;
821 print_method_attrs(ctx, w, method_def)?;
822 print_span(w, &method_def.span)?;
824 option(w, &body.return_type_info, |w, t| {
825 print_type_info(w, t)?;
828 w.write(method_def.name.to_raw_string())?;
829 print_params(ctx, w, body.env.as_ref(), &body.params)?;
830 if method_def.flags.contains(HhasMethodFlags::IS_GENERATOR) {
831 w.write(" isGenerator")?;
833 if method_def.flags.contains(HhasMethodFlags::IS_ASYNC) {
834 w.write(" isAsync")?;
838 .contains(HhasMethodFlags::IS_PAIR_GENERATOR)
840 w.write(" isPairGenerator")?;
842 if method_def.flags.contains(HhasMethodFlags::IS_CLOSURE_BODY) {
843 w.write(" isClosureBody")?;
845 if method_def.flags.contains(HhasMethodFlags::RX_DISABLED) {
846 w.write(" isRxDisabled")?;
850 ctx.block(w, |c, w| print_body(c, w, body))?;
856 fn print_method_attrs<W: Write>(
860 ) -> Result<(), W::Error> {
861 use hhas_attribute::*;
862 let user_attrs = &m.attributes;
863 let mut special_attrs = vec![];
864 if let Ok(attr) = m.rx_level.try_into() {
865 special_attrs.push(attr);
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)?;
921 ctx.block(w, |c, w| {
922 print_doc_comment(c, w, &class_def.doc_comment)?;
923 print_uses(c, w, class_def)?;
924 print_enum_ty(c, w, class_def)?;
925 for x in &class_def.requirements {
926 print_requirement(c, w, x)?;
928 for x in &class_def.constants {
929 print_constant(c, w, x)?;
931 for x in &class_def.type_constants {
932 print_type_constant(c, w, x)?;
934 for x in &class_def.properties {
935 print_property(c, w, class_def, x)?;
937 for m in &class_def.methods {
938 print_method_def(c, w, m)?;
947 fn print_pos_as_prov_tag<W: Write>(
950 loc: &Option<ast_defs::Pos>,
951 ) -> Result<(), W::Error> {
953 Some(l) if ctx.emitter.options().array_provenance() => {
954 let (line, ..) = l.info_pos();
955 let filename = l.filename().to_absolute();
956 let filename = match filename.to_str().unwrap() {
957 "" => "(unknown hackc filename)",
965 quote_string_with_escape(filename)
972 fn print_hhbc_id<'a, W: Write>(w: &mut W, id: &impl Id<'a>) -> Result<(), W::Error> {
973 quotes(w, |w| w.write(escape(id.to_raw_string())))
976 fn print_function_id<W: Write>(w: &mut W, id: &FunctionId) -> Result<(), W::Error> {
980 fn print_class_id<W: Write>(w: &mut W, id: &ClassId) -> Result<(), W::Error> {
984 fn print_method_id<W: Write>(w: &mut W, id: &MethodId) -> Result<(), W::Error> {
988 fn print_const_id<W: Write>(w: &mut W, id: &ConstId) -> Result<(), W::Error> {
992 fn print_prop_id<W: Write>(w: &mut W, id: &PropId) -> Result<(), W::Error> {
996 fn print_adata_id<W: Write>(w: &mut W, id: &AdataId) -> Result<(), W::Error> {
997 concat_str(w, ["@", id.as_str()])
1000 fn print_adata_mapped_argument<W: Write, F, V>(
1004 loc: &Option<ast_defs::Pos>,
1007 ) -> Result<(), W::Error>
1009 F: Fn(&mut Context, &mut W, &V) -> Result<(), W::Error>,
1011 write!(w, "{}:{}:{{", col_type, values.len(),)?;
1012 print_pos_as_prov_tag(ctx, w, loc)?;
1019 fn print_adata_collection_argument<W: Write>(
1023 loc: &Option<ast_defs::Pos>,
1024 values: &Vec<TypedValue>,
1025 ) -> Result<(), W::Error> {
1026 print_adata_mapped_argument(ctx, w, col_type, loc, values, &print_adata)
1029 fn print_adata_dict_collection_argument<W: Write>(
1033 loc: &Option<ast_defs::Pos>,
1034 pairs: &Vec<(TypedValue, TypedValue)>,
1035 ) -> Result<(), W::Error> {
1036 print_adata_mapped_argument(ctx, w, col_type, loc, pairs, |ctx, w, (v1, v2)| {
1037 print_adata(ctx, w, v1)?;
1038 print_adata(ctx, w, v2)
1042 fn print_adata<W: Write>(ctx: &mut Context, w: &mut W, tv: &TypedValue) -> Result<(), W::Error> {
1044 TypedValue::Uninit => w.write("uninit"),
1045 TypedValue::Null => w.write("N;"),
1046 TypedValue::String(s) => write!(w, "s:{}:{};", s.len(), quote_string_with_escape(s)),
1047 TypedValue::Float(f) => write!(w, "d:{};", float::to_string(*f)),
1048 TypedValue::Int(i) => write!(w, "i:{};", i),
1049 // TODO: The False case seems to sometimes be b:0 and sometimes i:0. Why?
1050 TypedValue::Bool(false) => w.write("b:0;"),
1051 TypedValue::Bool(true) => w.write("b:1;"),
1052 TypedValue::Dict((pairs, loc, is_legacy)) => {
1053 let prefix = if *is_legacy {
1058 print_adata_dict_collection_argument(ctx, w, prefix, loc, pairs)
1060 TypedValue::Vec((values, loc, is_legacy)) => {
1061 let prefix = if *is_legacy {
1066 print_adata_collection_argument(ctx, w, prefix, loc, values)
1068 TypedValue::DArray((pairs, loc)) => {
1069 print_adata_dict_collection_argument(ctx, w, DARRAY_PREFIX, loc, pairs)
1071 TypedValue::Keyset(values) => {
1072 print_adata_collection_argument(ctx, w, KEYSET_PREFIX, &None, values)
1074 TypedValue::VArray((values, loc)) => {
1075 print_adata_collection_argument(ctx, w, VARRAY_PREFIX, loc, values)
1077 TypedValue::HhasAdata(s) => w.write(escaped(s)),
1081 fn print_attribute<W: Write>(
1085 ) -> Result<(), W::Error> {
1088 "\"{}\"(\"\"\"{}:{}:{{",
1093 concat(w, &a.arguments, |w, arg| print_adata(ctx, w, arg))?;
1097 fn print_attributes<W: Write>(
1100 al: impl AsRef<[HhasAttribute]>,
1101 ) -> Result<(), W::Error> {
1102 // Adjust for underscore coming before alphabet
1103 let al: Vec<&HhasAttribute> = al
1106 .sorted_by_key(|a| (!a.name.starts_with("__"), &a.name))
1108 concat_by(w, " ", &al, |w, a| print_attribute(ctx, w, a))
1111 fn print_file_attributes<W: Write>(
1114 al: &Vec<HhasAttribute>,
1115 ) -> Result<(), W::Error> {
1120 w.write(".file_attributes [")?;
1121 print_attributes(ctx, w, al)?;
1126 fn print_main<W: Write>(ctx: &mut Context, w: &mut W, body: &HhasBody) -> Result<(), W::Error> {
1130 ctx.block(w, |c, w| print_body(c, w, body))?;
1136 fn is_bareword_char(c: &u8) -> bool {
1138 b'_' | b'.' | b'$' | b'\\' => true,
1139 c => (c >= b'0' && c <= b'9') || (c >= b'a' && c <= b'z') || (c >= b'A' && c <= b'Z'),
1143 fn print_body<W: Write>(ctx: &mut Context, w: &mut W, body: &HhasBody) -> Result<(), W::Error> {
1144 print_doc_comment(ctx, w, &body.doc_comment)?;
1145 if body.is_memoize_wrapper {
1147 w.write(".ismemoizewrapper;")?;
1149 if body.is_memoize_wrapper_lsb {
1151 w.write(".ismemoizewrapperlsb;")?;
1153 if body.num_iters > 0 {
1155 write!(w, ".numiters {};", body.num_iters)?;
1157 if !body.decl_vars.is_empty() {
1159 w.write(".declvars ")?;
1160 concat_by(w, " ", &body.decl_vars, |w, var| {
1161 if var.as_bytes().iter().all(is_bareword_char) {
1164 quotes(w, |w| w.write(escaper::escape(var)))
1169 print_instructions(ctx, w, &body.body_instrs)
1172 fn print_instructions<W: Write>(
1175 instr_seq: &InstrSeq,
1176 ) -> Result<(), W::Error> {
1179 for instr in instr_seq.compact_iter() {
1181 ISpecialFlow(_) => return Err(Error::fail("Cannot break/continue 1 level")),
1185 print_instr(w, instr)?;
1187 ILabel(_) => ctx.unblock(w, |c, w| {
1189 print_instr(w, instr)
1191 ITry(TryCatchBegin) => {
1193 print_instr(w, instr)?;
1196 ITry(TryCatchMiddle) => ctx.unblock(w, |c, w| {
1198 print_instr(w, instr)
1200 ITry(TryCatchEnd) => {
1203 print_instr(w, instr)?;
1207 print_instr(w, instr)?;
1214 fn if_then<F: FnOnce() -> R, R>(cond: bool, f: F) -> Option<R> {
1222 fn print_fcall_args<W: Write>(
1224 FcallArgs(fls, num_args, num_rets, inouts, async_eager_label, context): &FcallArgs,
1225 ) -> Result<(), W::Error> {
1226 use FcallFlags as F;
1227 let mut flags = vec![];
1228 if_then(fls.contains(F::HAS_UNPACK), || flags.push("Unpack"));
1229 if_then(fls.contains(F::HAS_GENERICS), || flags.push("Generics"));
1230 if_then(fls.contains(F::SUPPORTS_ASYNC_EAGER_RETURN), || {
1231 flags.push("SupportsAER")
1233 if_then(fls.contains(F::LOCK_WHILE_UNWINDING), || {
1234 flags.push("LockWhileUnwinding")
1236 angle(w, |w| concat_str_by(w, " ", flags))?;
1238 print_int(w, num_args)?;
1240 print_int(w, num_rets)?;
1243 concat_by(w, "", inouts, |w, i| w.write(if *i { "1" } else { "0" }))
1246 option_or(w, async_eager_label, print_label, "-")?;
1249 Some(s) => quotes(w, |w| w.write(s)),
1250 None => w.write("\"\""),
1254 fn print_special_cls_ref<W: Write>(w: &mut W, cls_ref: &SpecialClsRef) -> Result<(), W::Error> {
1255 w.write(match cls_ref {
1256 SpecialClsRef::Static => "Static",
1257 SpecialClsRef::Self_ => "Self",
1258 SpecialClsRef::Parent => "Parent",
1262 fn print_null_flavor<W: Write>(w: &mut W, f: &ObjNullFlavor) -> Result<(), W::Error> {
1264 ObjNullFlavor::NullThrows => "NullThrows",
1265 ObjNullFlavor::NullSafe => "NullSafe",
1269 fn print_instr<W: Write>(w: &mut W, instr: &Instruct) -> Result<(), W::Error> {
1270 fn print_call<W: Write>(w: &mut W, call: &InstructCall) -> Result<(), W::Error> {
1271 use InstructCall as I;
1273 I::NewObj => w.write("NewObj"),
1274 I::NewObjR => w.write("NewObjR"),
1275 I::NewObjD(cid) => {
1276 w.write("NewObjD ")?;
1277 print_class_id(w, cid)
1279 I::NewObjRD(cid) => {
1280 w.write("NewObjRD ")?;
1281 print_class_id(w, cid)
1284 w.write("NewObjS ")?;
1285 print_special_cls_ref(w, r)
1287 I::FCall(fcall_args) => {
1289 print_fcall_args(w, &fcall_args)?;
1290 w.write(r#" "" """#)
1292 I::FCallClsMethod(fcall_args, is_log_as_dynamic_call) => {
1293 w.write("FCallClsMethod ")?;
1294 print_fcall_args(w, fcall_args)?;
1295 w.write(r#" "" "#)?;
1296 w.write(match is_log_as_dynamic_call {
1297 IsLogAsDynamicCallOp::LogAsDynamicCall => "LogAsDynamicCall",
1298 IsLogAsDynamicCallOp::DontLogAsDynamicCall => "DontLogAsDynamicCall",
1301 I::FCallClsMethodD(fcall_args, cid, mid) => {
1302 w.write("FCallClsMethodD ")?;
1303 print_fcall_args(w, fcall_args)?;
1304 w.write(r#" "" "#)?;
1305 print_class_id(w, cid)?;
1307 print_method_id(w, mid)
1309 I::FCallClsMethodS(fcall_args, r) => {
1310 w.write("FCallClsMethodS ")?;
1311 print_fcall_args(w, fcall_args)?;
1312 w.write(r#" "" "#)?;
1313 print_special_cls_ref(w, r)
1315 I::FCallClsMethodSD(fcall_args, r, mid) => {
1316 w.write("FCallClsMethodSD ")?;
1317 print_fcall_args(w, fcall_args)?;
1318 w.write(r#" "" "#)?;
1319 print_special_cls_ref(w, r)?;
1321 print_method_id(w, mid)
1323 I::FCallCtor(fcall_args) => {
1324 w.write("FCallCtor ")?;
1325 print_fcall_args(w, fcall_args)?;
1328 I::FCallFunc(fcall_args) => {
1329 w.write("FCallFunc ")?;
1330 print_fcall_args(w, fcall_args)
1332 I::FCallFuncD(fcall_args, id) => {
1333 w.write("FCallFuncD ")?;
1334 print_fcall_args(w, fcall_args)?;
1336 print_function_id(w, id)
1338 I::FCallObjMethod(fcall_args, nf) => {
1339 w.write("FCallObjMethod ")?;
1340 print_fcall_args(w, fcall_args)?;
1341 w.write(r#" "" "#)?;
1342 print_null_flavor(w, nf)
1344 I::FCallObjMethodD(fcall_args, nf, id) => {
1345 w.write("FCallObjMethodD ")?;
1346 print_fcall_args(w, fcall_args)?;
1347 w.write(r#" "" "#)?;
1348 print_null_flavor(w, nf)?;
1350 print_method_id(w, id)
1355 fn print_get<W: Write>(w: &mut W, get: &InstructGet) -> Result<(), W::Error> {
1356 use InstructGet as IG;
1362 IG::CGetQuietL(id) => {
1363 w.write("CGetQuietL ")?;
1367 w.write("CGetL2 ")?;
1371 w.write("CUGetL ")?;
1378 IG::CGetG => w.write("CGetG"),
1379 IG::CGetS => w.write("CGetS"),
1380 IG::ClassGetC => w.write("ClassGetC"),
1381 IG::ClassGetTS => w.write("ClassGetTS"),
1386 use InstructBasic as IB;
1388 IIterator(i) => print_iterator(w, i),
1389 IBasic(b) => w.write(match b {
1391 IB::EntryNop => "EntryNop",
1396 ILitConst(lit) => print_lit_const(w, lit),
1397 IOp(op) => print_op(w, op),
1398 IContFlow(cf) => print_control_flow(w, cf),
1399 ICall(c) => print_call(w, c),
1400 IMisc(misc) => print_misc(w, misc),
1401 IGet(get) => print_get(w, get),
1402 IMutator(mutator) => print_mutator(w, mutator),
1407 IIsset(i) => print_isset(w, i),
1408 IBase(i) => print_base(w, i),
1409 IFinal(i) => print_final(w, i),
1410 ITry(itry) => print_try(w, itry),
1411 IComment(s) => concat_str_by(w, " ", ["#", s.as_str()]),
1412 ISrcLoc(p) => write!(
1414 ".srcloc {}:{},{}:{};",
1415 p.line_begin, p.col_begin, p.line_end, p.col_end
1417 IAsync(a) => print_async(w, a),
1418 IGenerator(gen) => print_gen_creation_execution(w, gen),
1419 IIncludeEvalDefine(ed) => print_include_eval_define(w, ed),
1420 _ => Err(Error::fail("invalid instruction")),
1424 fn print_base<W: Write>(w: &mut W, i: &InstructBase) -> Result<(), W::Error> {
1425 use InstructBase as I;
1427 I::BaseGC(si, m) => {
1428 w.write("BaseGC ")?;
1429 print_stack_index(w, si)?;
1431 print_member_opmode(w, m)
1433 I::BaseGL(id, m) => {
1434 w.write("BaseGL ")?;
1435 print_local(w, id)?;
1437 print_member_opmode(w, m)
1439 I::BaseSC(si1, si2, m) => {
1440 w.write("BaseSC ")?;
1441 print_stack_index(w, si1)?;
1443 print_stack_index(w, si2)?;
1445 print_member_opmode(w, m)
1447 I::BaseL(id, m) => {
1449 print_local(w, id)?;
1451 print_member_opmode(w, m)
1453 I::BaseC(si, m) => {
1455 print_stack_index(w, si)?;
1457 print_member_opmode(w, m)
1459 I::BaseH => w.write("BaseH"),
1462 print_member_opmode(w, m)?;
1464 print_member_key(w, mk)
1469 fn print_stack_index<W: Write>(w: &mut W, si: &StackIndex) -> Result<(), W::Error> {
1470 w.write(si.to_string())
1473 fn print_member_opmode<W: Write>(w: &mut W, m: &MemberOpMode) -> Result<(), W::Error> {
1474 use MemberOpMode as M;
1476 M::ModeNone => "None",
1478 M::Define => "Define",
1479 M::Unset => "Unset",
1480 M::InOut => "InOut",
1484 fn print_member_key<W: Write>(w: &mut W, mk: &MemberKey) -> Result<(), W::Error> {
1489 print_stack_index(w, si)
1493 print_local(w, local)
1497 quotes(w, |w| w.write(escape(s)))
1499 M::EI(i) => concat_str(w, ["EI:", i.to_string().as_ref()]),
1502 print_stack_index(w, si)
1506 print_local(w, local)
1510 print_prop_id(w, id)
1514 print_prop_id(w, id)
1516 M::W => w.write("W"),
1520 fn print_iterator<W: Write>(w: &mut W, i: &InstructIterator) -> Result<(), W::Error> {
1521 use InstructIterator as I;
1523 I::IterInit(iter_args, label) => {
1524 w.write("IterInit ")?;
1525 print_iter_args(w, iter_args)?;
1527 print_label(w, label)
1529 I::IterNext(iter_args, label) => {
1530 w.write("IterNext ")?;
1531 print_iter_args(w, iter_args)?;
1533 print_label(w, label)
1535 I::IterFree(id) => {
1536 w.write("IterFree ")?;
1537 print_iterator_id(w, id)
1542 fn print_iter_args<W: Write>(w: &mut W, iter_args: &IterArgs) -> Result<(), W::Error> {
1543 print_iterator_id(w, &iter_args.iter_id)?;
1545 match &iter_args.key_id {
1546 None => w.write("NK")?,
1549 print_local(w, &k)?;
1554 print_local(w, &iter_args.val_id)
1557 fn print_iterator_id<W: Write>(w: &mut W, i: &IterId) -> Result<(), W::Error> {
1561 fn print_async<W: Write>(w: &mut W, a: &AsyncFunctions) -> Result<(), W::Error> {
1562 use AsyncFunctions as A;
1564 A::WHResult => w.write("WHResult"),
1565 A::Await => w.write("Await"),
1566 A::AwaitAll(Some((Local::Unnamed(id), count))) => write!(w, "AwaitAll L:{}+{}", id, count),
1567 A::AwaitAll(None) => w.write("AwaitAll L:0+0"),
1568 _ => Err(Error::fail("AwaitAll needs an unnamed local")),
1572 fn print_query_op<W: Write>(w: &mut W, q: QueryOp) -> Result<(), W::Error> {
1574 QueryOp::CGet => "CGet",
1575 QueryOp::CGetQuiet => "CGetQuiet",
1576 QueryOp::Isset => "Isset",
1577 QueryOp::InOut => "InOut",
1581 fn print_final<W: Write>(w: &mut W, f: &InstructFinal) -> Result<(), W::Error> {
1582 use InstructFinal as F;
1584 F::QueryM(n, op, mk) => {
1585 w.write("QueryM ")?;
1588 print_query_op(w, *op)?;
1590 print_member_key(w, mk)
1592 F::UnsetM(n, mk) => {
1593 w.write("UnsetM ")?;
1596 print_member_key(w, mk)
1602 print_member_key(w, mk)
1604 F::SetOpM(i, op, mk) => {
1605 w.write("SetOpM ")?;
1608 print_eq_op(w, &op)?;
1610 print_member_key(w, mk)
1612 F::IncDecM(i, op, mk) => {
1613 w.write("IncDecM ")?;
1616 print_incdec_op(w, &op)?;
1618 print_member_key(w, mk)
1620 F::SetRangeM(i, op, s) => {
1621 w.write("SetRangeM ")?;
1625 SetrangeOp::Forward => "Forward",
1626 SetrangeOp::Reverse => "Reverse",
1629 print_int(w, &(*s as usize))
1634 fn print_isset<W: Write>(w: &mut W, isset: &InstructIsset) -> Result<(), W::Error> {
1635 use InstructIsset as I;
1637 I::IssetC => w.write("IssetC"),
1638 I::IssetL(local) => {
1639 w.write("IssetL ")?;
1640 print_local(w, local)
1642 I::IsUnsetL(local) => {
1643 w.write("IsUnsetL ")?;
1644 print_local(w, local)
1646 I::IssetG => w.write("IssetG"),
1647 I::IssetS => w.write("IssetS"),
1649 w.write("IsTypeC ")?;
1650 print_istype_op(w, op)
1652 I::IsTypeL(local, op) => {
1653 w.write("IsTypeL ")?;
1654 print_local(w, local)?;
1656 print_istype_op(w, op)
1661 fn print_istype_op<W: Write>(w: &mut W, op: &IstypeOp) -> Result<(), W::Error> {
1664 Op::OpNull => w.write("Null"),
1665 Op::OpBool => w.write("Bool"),
1666 Op::OpInt => w.write("Int"),
1667 Op::OpDbl => w.write("Dbl"),
1668 Op::OpStr => w.write("Str"),
1669 Op::OpArr => w.write("Arr"),
1670 Op::OpObj => w.write("Obj"),
1671 Op::OpRes => w.write("Res"),
1672 Op::OpScalar => w.write("Scalar"),
1673 Op::OpKeyset => w.write("Keyset"),
1674 Op::OpDict => w.write("Dict"),
1675 Op::OpVec => w.write("Vec"),
1676 Op::OpArrLike => w.write("ArrLike"),
1677 Op::OpVArray => w.write("VArray"),
1678 Op::OpDArray => w.write("DArray"),
1679 Op::OpClsMeth => w.write("ClsMeth"),
1680 Op::OpFunc => w.write("Func"),
1681 Op::OpPHPArr => w.write("PHPArr"),
1685 fn print_try<W: Write>(w: &mut W, itry: &InstructTry) -> Result<(), W::Error> {
1686 use InstructTry as T;
1688 T::TryCatchBegin => w.write(".try {"),
1689 T::TryCatchMiddle => w.write("} .catch {"),
1690 T::TryCatchEnd => w.write("}"),
1694 fn print_mutator<W: Write>(w: &mut W, mutator: &InstructMutator) -> Result<(), W::Error> {
1695 use InstructMutator as M;
1699 print_local(w, local)
1705 M::SetG => w.write("SetG"),
1706 M::SetS => w.write("SetS"),
1707 M::SetOpL(id, op) => {
1708 w.write("SetOpL ")?;
1709 print_local(w, id)?;
1714 w.write("SetOpG ")?;
1718 w.write("SetOpS ")?;
1721 M::IncDecL(id, op) => {
1722 w.write("IncDecL ")?;
1723 print_local(w, id)?;
1725 print_incdec_op(w, op)
1728 w.write("IncDecG ")?;
1729 print_incdec_op(w, op)
1732 w.write("IncDecS ")?;
1733 print_incdec_op(w, op)
1736 w.write("UnsetL ")?;
1739 M::UnsetG => w.write("UnsetG"),
1740 M::CheckProp(id) => {
1741 w.write("CheckProp ")?;
1742 print_prop_id(w, id)
1744 M::InitProp(id, op) => {
1745 w.write("InitProp ")?;
1746 print_prop_id(w, id)?;
1749 InitpropOp::Static => w.write("Static"),
1750 InitpropOp::NonStatic => w.write("NonStatic"),
1756 fn print_eq_op<W: Write>(w: &mut W, op: &EqOp) -> Result<(), W::Error> {
1758 EqOp::PlusEqual => "PlusEqual",
1759 EqOp::MinusEqual => "MinusEqual",
1760 EqOp::MulEqual => "MulEqual",
1761 EqOp::ConcatEqual => "ConcatEqual",
1762 EqOp::DivEqual => "DivEqual",
1763 EqOp::PowEqual => "PowEqual",
1764 EqOp::ModEqual => "ModEqual",
1765 EqOp::AndEqual => "AndEqual",
1766 EqOp::OrEqual => "OrEqual",
1767 EqOp::XorEqual => "XorEqual",
1768 EqOp::SlEqual => "SlEqual",
1769 EqOp::SrEqual => "SrEqual",
1770 EqOp::PlusEqualO => "PlusEqualO",
1771 EqOp::MinusEqualO => "MinusEqualO",
1772 EqOp::MulEqualO => "MulEqualO",
1776 fn print_incdec_op<W: Write>(w: &mut W, op: &IncdecOp) -> Result<(), W::Error> {
1778 IncdecOp::PreInc => "PreInc",
1779 IncdecOp::PostInc => "PostInc",
1780 IncdecOp::PreDec => "PreDec",
1781 IncdecOp::PostDec => "PostDec",
1782 IncdecOp::PreIncO => "PreIncO",
1783 IncdecOp::PostIncO => "PostIncO",
1784 IncdecOp::PreDecO => "PreDecO",
1785 IncdecOp::PostDecO => "PostDecO",
1789 fn print_gen_creation_execution<W: Write>(
1791 gen: &GenCreationExecution,
1792 ) -> Result<(), W::Error> {
1793 use GenCreationExecution as G;
1795 G::CreateCont => w.write("CreateCont"),
1796 G::ContEnter => w.write("ContEnter"),
1797 G::ContRaise => w.write("ContRaise"),
1798 G::Yield => w.write("Yield"),
1799 G::YieldK => w.write("YieldK"),
1800 G::ContCheck(CheckStarted::IgnoreStarted) => w.write("ContCheck IgnoreStarted"),
1801 G::ContCheck(CheckStarted::CheckStarted) => w.write("ContCheck CheckStarted"),
1802 G::ContValid => w.write("ContValid"),
1803 G::ContKey => w.write("ContKey"),
1804 G::ContGetReturn => w.write("ContGetReturn"),
1805 G::ContCurrent => w.write("ContCurrent"),
1809 fn print_misc<W: Write>(w: &mut W, misc: &InstructMisc) -> Result<(), W::Error> {
1810 use InstructMisc as M;
1812 M::This => w.write("This"),
1813 M::CheckThis => w.write("CheckThis"),
1814 M::FuncNumArgs => w.write("FuncNumArgs"),
1815 M::ChainFaults => w.write("ChainFaults"),
1816 M::VerifyRetTypeC => w.write("VerifyRetTypeC"),
1817 M::VerifyRetTypeTS => w.write("VerifyRetTypeTS"),
1818 M::Self_ => w.write("Self"),
1819 M::Parent => w.write("Parent"),
1820 M::LateBoundCls => w.write("LateBoundCls"),
1821 M::ClassName => w.write("ClassName"),
1822 M::RecordReifiedGeneric => w.write("RecordReifiedGeneric"),
1823 M::CheckReifiedGenericMismatch => w.write("CheckReifiedGenericMismatch"),
1824 M::NativeImpl => w.write("NativeImpl"),
1825 M::AKExists => w.write("AKExists"),
1826 M::Idx => w.write("Idx"),
1827 M::ArrayIdx => w.write("ArrayIdx"),
1828 M::BreakTraceHint => w.write("BreakTraceHint"),
1829 M::CGetCUNop => w.write("CGetCUNop"),
1830 M::UGetCUNop => w.write("UGetCUNop"),
1831 M::LockObj => w.write("LockObj"),
1832 M::ThrowNonExhaustiveSwitch => w.write("ThrowNonExhaustiveSwitch"),
1833 M::VerifyParamType(id) => {
1834 w.write("VerifyParamType ")?;
1835 print_param_id(w, id)
1837 M::VerifyParamTypeTS(id) => {
1838 w.write("VerifyParamTypeTS ")?;
1839 print_param_id(w, id)
1841 M::Silence(local, op) => {
1842 w.write("Silence ")?;
1843 print_local(w, local)?;
1846 OpSilence::Start => w.write("Start"),
1847 OpSilence::End => w.write("End"),
1850 M::VerifyOutType(id) => {
1851 w.write("VerifyOutType ")?;
1852 print_param_id(w, id)
1854 M::CreateCl(n, cid) => concat_str_by(
1857 ["CreateCl", n.to_string().as_str(), cid.to_string().as_str()],
1859 M::BareThis(op) => concat_str_by(
1865 BareThisOp::Notice => "Notice",
1866 BareThisOp::NoNotice => "NoNotice",
1867 BareThisOp::NeverNull => "NeverNull",
1872 M::MemoGet(label, Some((Local::Unnamed(first), local_count))) => {
1873 w.write("MemoGet ")?;
1874 print_label(w, label)?;
1875 write!(w, " L:{}+{}", first, local_count)
1877 M::MemoGet(label, None) => {
1878 w.write("MemoGet ")?;
1879 print_label(w, label)?;
1882 M::MemoGet(_, _) => Err(Error::fail("MemoGet needs an unnamed local")),
1884 M::MemoSet(Some((Local::Unnamed(first), local_count))) => {
1885 write!(w, "MemoSet L:{}+{}", first, local_count)
1887 M::MemoSet(None) => w.write("MemoSet L:0+0"),
1888 M::MemoSet(_) => Err(Error::fail("MemoSet needs an unnamed local")),
1890 M::MemoGetEager(label1, label2, Some((Local::Unnamed(first), local_count))) => {
1891 w.write("MemoGetEager ")?;
1892 print_label(w, label1)?;
1894 print_label(w, label2)?;
1895 write!(w, " L:{}+{}", first, local_count)
1897 M::MemoGetEager(label1, label2, None) => {
1898 w.write("MemoGetEager ")?;
1899 print_label(w, label1)?;
1901 print_label(w, label2)?;
1904 M::MemoGetEager(_, _, _) => Err(Error::fail("MemoGetEager needs an unnamed local")),
1906 M::MemoSetEager(Some((Local::Unnamed(first), local_count))) => {
1907 write!(w, "MemoSetEager L:{}+{}", first, local_count)
1909 M::MemoSetEager(None) => w.write("MemoSetEager L:0+0"),
1910 M::MemoSetEager(_) => Err(Error::fail("MemoSetEager needs an unnamed local")),
1912 M::InitThisLoc(id) => {
1913 w.write("InitThisLoc ")?;
1916 M::OODeclExists(k) => concat_str_by(
1922 ClassKind::Class => "Class",
1923 ClassKind::Interface => "Interface",
1924 ClassKind::Trait => "Trait",
1928 M::AssertRATL(local, s) => {
1929 w.write("AssertRATL ")?;
1930 print_local(w, local)?;
1934 M::AssertRATStk(n, s) => {
1935 concat_str_by(w, " ", ["AssertRATStk", n.to_string().as_str(), s.as_str()])
1937 M::GetMemoKeyL(local) => {
1938 w.write("GetMemoKeyL ")?;
1939 print_local(w, local)
1944 fn print_include_eval_define<W: Write>(
1946 ed: &InstructIncludeEvalDefine,
1947 ) -> Result<(), W::Error> {
1948 use InstructIncludeEvalDefine::*;
1950 Incl => w.write("Incl"),
1951 InclOnce => w.write("InclOnce"),
1952 Req => w.write("Req"),
1953 ReqOnce => w.write("ReqOnce"),
1954 ReqDoc => w.write("ReqDoc"),
1955 Eval => w.write("Eval"),
1956 DefCls(n) => concat_str_by(w, " ", ["DefCls", n.to_string().as_str()]),
1957 DefClsNop(n) => concat_str_by(w, " ", ["DefClsNop", n.to_string().as_str()]),
1958 DefRecord(n) => concat_str_by(w, " ", ["DefRecord", n.to_string().as_str()]),
1959 DefCns(n) => concat_str_by(w, " ", ["DefCns", n.to_string().as_str()]),
1960 DefTypeAlias(id) => write!(w, "DefTypeAlias {}", id),
1964 fn print_control_flow<W: Write>(w: &mut W, cf: &InstructControlFlow) -> Result<(), W::Error> {
1965 use InstructControlFlow as CF;
1983 CF::RetC => w.write("RetC"),
1984 CF::RetCSuspended => w.write("RetCSuspended"),
1985 CF::RetM(p) => concat_str_by(w, " ", ["RetM", p.to_string().as_str()]),
1986 CF::Throw => w.write("Throw"),
1987 CF::Switch(kind, base, labels) => print_switch(w, kind, base, labels),
1988 CF::SSwitch(cases) => match &cases[..] {
1989 [] => Err(Error::fail("sswitch should have at least one case")),
1990 [rest @ .., (_, lastlabel)] => {
1991 w.write("SSwitch ")?;
1993 concat_by(w, " ", rest, |w, (s, l)| {
1994 concat_str(w, [quote_string(s).as_str(), ":"])?;
1998 print_label(w, lastlabel)
2005 fn print_switch<W: Write>(
2010 ) -> Result<(), W::Error> {
2011 w.write("Switch ")?;
2012 w.write(match kind {
2013 Switchkind::Bounded => "Bounded ",
2014 Switchkind::Unbounded => "Unbounded ",
2016 w.write(base.to_string())?;
2018 angle(w, |w| concat_by(w, " ", labels, print_label))
2021 fn print_lit_const<W: Write>(w: &mut W, lit: &InstructLitConst) -> Result<(), W::Error> {
2022 use InstructLitConst as LC;
2024 LC::Null => w.write("Null"),
2025 LC::Int(i) => concat_str_by(w, " ", ["Int", i.to_string().as_str()]),
2027 w.write("String ")?;
2028 quotes(w, |w| w.write(escape(s)))
2030 LC::True => w.write("True"),
2031 LC::False => w.write("False"),
2032 LC::Double(d) => concat_str_by(w, " ", ["Double", d.as_str()]),
2033 LC::AddElemC => w.write("AddElemC"),
2034 LC::AddNewElemC => w.write("AddNewElemC"),
2035 LC::NewPair => w.write("NewPair"),
2036 LC::File => w.write("File"),
2037 LC::Dir => w.write("Dir"),
2038 LC::Method => w.write("Method"),
2039 LC::FuncCred => w.write("FuncCred"),
2042 print_adata_id(w, id)
2046 print_adata_id(w, id)
2049 w.write("Keyset ")?;
2050 print_adata_id(w, id)
2054 print_adata_id(w, id)
2056 LC::NewDictArray(i) => concat_str_by(w, " ", ["NewDictArray", i.to_string().as_str()]),
2057 LC::NewDArray(i) => concat_str_by(w, " ", ["NewDArray", i.to_string().as_str()]),
2058 LC::NewVArray(i) => concat_str_by(w, " ", ["NewVArray", i.to_string().as_str()]),
2059 LC::NewVec(i) => concat_str_by(w, " ", ["NewVec", i.to_string().as_str()]),
2060 LC::NewKeysetArray(i) => concat_str_by(w, " ", ["NewKeysetArray", i.to_string().as_str()]),
2061 LC::NewStructDArray(l) => {
2062 w.write("NewStructDArray ")?;
2063 angle(w, |w| print_shape_fields(w, l))
2065 LC::NewStructDict(l) => {
2066 w.write("NewStructDict ")?;
2067 angle(w, |w| print_shape_fields(w, l))
2069 LC::NewRecord(cid, l) => {
2070 w.write("NewRecord ")?;
2071 print_class_id(w, cid)?;
2073 angle(w, |w| print_shape_fields(w, l))
2077 print_const_id(w, id)
2080 w.write("ClsCns ")?;
2081 print_const_id(w, id)
2083 LC::ClsCnsD(const_id, cid) => {
2084 w.write("ClsCnsD ")?;
2085 print_const_id(w, const_id)?;
2087 print_class_id(w, cid)
2090 w.write("NewCol ")?;
2091 print_collection_type(w, ct)
2093 LC::ColFromArray(ct) => {
2094 w.write("ColFromArray ")?;
2095 print_collection_type(w, ct)
2097 LC::NullUninit => w.write("NullUninit"),
2098 LC::TypedValue(_) => Err(Error::fail("print_lit_const: TypedValue")),
2102 fn print_collection_type<W: Write>(w: &mut W, ct: &CollectionType) -> Result<(), W::Error> {
2103 use CollectionType as CT;
2105 CT::Vector => w.write("Vector"),
2106 CT::Map => w.write("Map"),
2107 CT::Set => w.write("Set"),
2108 CT::Pair => w.write("Pair"),
2109 CT::ImmVector => w.write("ImmVector"),
2110 CT::ImmMap => w.write("ImmMap"),
2111 CT::ImmSet => w.write("ImmSet"),
2112 CT::Dict => w.write("dict"),
2113 CT::Array => w.write("array"),
2114 CT::Keyset => w.write("keyset"),
2115 CT::Vec => w.write("vec"),
2119 fn print_shape_fields<W: Write>(w: &mut W, sf: &Vec<String>) -> Result<(), W::Error> {
2120 concat_by(w, " ", sf, |w, f| quotes(w, |w| w.write(escape(f))))
2123 fn print_op<W: Write>(w: &mut W, op: &InstructOperator) -> Result<(), W::Error> {
2124 use InstructOperator as I;
2126 I::Concat => w.write("Concat"),
2127 I::ConcatN(n) => concat_str_by(w, " ", ["ConcatN", n.to_string().as_str()]),
2128 I::Abs => w.write("Abs"),
2129 I::Add => w.write("Add"),
2130 I::Sub => w.write("Sub"),
2131 I::Mul => w.write("Mul"),
2132 I::AddO => w.write("AddO"),
2133 I::SubO => w.write("SubO"),
2134 I::MulO => w.write("MulO"),
2135 I::Div => w.write("Div"),
2136 I::Mod => w.write("Mod"),
2137 I::Pow => w.write("Pow"),
2138 I::Sqrt => w.write("Sqrt"),
2139 I::Xor => w.write("Xor"),
2140 I::Not => w.write("Not"),
2141 I::Same => w.write("Same"),
2142 I::NSame => w.write("NSame"),
2143 I::Eq => w.write("Eq"),
2144 I::Neq => w.write("Neq"),
2145 I::Lt => w.write("Lt"),
2146 I::Lte => w.write("Lte"),
2147 I::Gt => w.write("Gt"),
2148 I::Gte => w.write("Gte"),
2149 I::Cmp => w.write("Cmp"),
2150 I::BitAnd => w.write("BitAnd"),
2151 I::BitOr => w.write("BitOr"),
2152 I::BitXor => w.write("BitXor"),
2153 I::BitNot => w.write("BitNot"),
2154 I::Shl => w.write("Shl"),
2155 I::Shr => w.write("Shr"),
2156 I::Floor => w.write("Floor"),
2157 I::Ceil => w.write("Ceil"),
2158 I::CastBool => w.write("CastBool"),
2159 I::CastInt => w.write("CastInt"),
2160 I::CastDouble => w.write("CastDouble"),
2161 I::CastString => w.write("CastString"),
2162 I::CastVec => w.write("CastVec"),
2163 I::CastDict => w.write("CastDict"),
2164 I::CastKeyset => w.write("CastKeyset"),
2165 I::CastVArray => w.write("CastVArray"),
2166 I::CastDArray => w.write("CastDArray"),
2167 I::InstanceOf => w.write("InstanceOf"),
2168 I::InstanceOfD(id) => {
2169 w.write("InstanceOfD ")?;
2170 print_class_id(w, id)
2172 I::IsLateBoundCls => w.write("IsLateBoundCls"),
2173 I::IsTypeStructC(op) => concat_str_by(
2179 TypestructResolveOp::Resolve => "Resolve",
2180 TypestructResolveOp::DontResolve => "DontResolve",
2184 I::ThrowAsTypeStructException => w.write("ThrowAsTypeStructException"),
2185 I::CombineAndResolveTypeStruct(n) => concat_str_by(
2188 ["CombineAndResolveTypeStruct", n.to_string().as_str()],
2190 I::Print => w.write("Print"),
2191 I::Clone => w.write("Clone"),
2192 I::Exit => w.write("Exit"),
2193 I::ResolveFunc(id) => {
2194 w.write("ResolveFunc ")?;
2195 print_function_id(w, id)
2197 I::ResolveRFunc(id) => {
2198 w.write("ResolveRFunc ")?;
2199 print_function_id(w, id)
2201 I::ResolveMethCaller(id) => {
2202 w.write("ResolveMethCaller ")?;
2203 print_function_id(w, id)
2205 I::ResolveObjMethod => w.write("ResolveObjMethod"),
2206 I::ResolveClsMethod(mid) => {
2207 w.write("ResolveClsMethod ")?;
2208 print_method_id(w, mid)
2210 I::ResolveClsMethodD(cid, mid) => {
2211 w.write("ResolveClsMethodD ")?;
2212 print_class_id(w, cid)?;
2214 print_method_id(w, mid)
2216 I::ResolveClsMethodS(r, mid) => {
2217 w.write("ResolveClsMethodS ")?;
2218 print_special_cls_ref(w, r)?;
2220 print_method_id(w, mid)
2222 I::ResolveRClsMethod(mid) => {
2223 w.write("ResolveRClsMethod ")?;
2224 print_method_id(w, mid)
2226 I::ResolveRClsMethodD(cid, mid) => {
2227 w.write("ResolveRClsMethodD ")?;
2228 print_class_id(w, cid)?;
2230 print_method_id(w, mid)
2232 I::ResolveRClsMethodS(r, mid) => {
2233 w.write("ResolveRClsMethodS ")?;
2234 print_special_cls_ref(w, r)?;
2236 print_method_id(w, mid)
2238 I::Fatal(fatal_op) => print_fatal_op(w, fatal_op),
2242 fn print_fatal_op<W: Write>(w: &mut W, f: &FatalOp) -> Result<(), W::Error> {
2244 FatalOp::Parse => w.write("Fatal Parse"),
2245 FatalOp::Runtime => w.write("Fatal Runtime"),
2246 FatalOp::RuntimeOmitFrame => w.write("Fatal RuntimeOmitFrame"),
2250 fn print_params<W: Write>(
2253 body_env: Option<&BodyEnv>,
2254 params: &[HhasParam],
2255 ) -> Result<(), W::Error> {
2257 concat_by(w, ", ", params, |w, i| print_param(ctx, w, body_env, i))
2261 fn print_param<W: Write>(
2264 body_env: Option<&BodyEnv>,
2266 ) -> Result<(), W::Error> {
2267 print_param_user_attributes(ctx, w, param)?;
2268 w.write_if(param.is_inout, "inout ")?;
2269 w.write_if(param.is_variadic, "...")?;
2270 option(w, ¶m.type_info, |w, ty| {
2271 print_type_info(w, ty)?;
2274 w.write(¶m.name)?;
2275 option(w, ¶m.default_value, |w, i| {
2276 print_param_default_value(ctx, w, body_env, i)
2280 fn print_param_id<W: Write>(w: &mut W, param_id: &ParamId) -> Result<(), W::Error> {
2282 ParamId::ParamUnnamed(i) => w.write(i.to_string()),
2283 ParamId::ParamNamed(s) => w.write(s),
2287 fn print_param_default_value<W: Write>(
2290 body_env: Option<&BodyEnv>,
2291 default_val: &(Label, ast::Expr),
2292 ) -> Result<(), W::Error> {
2293 let expr_env = ExprEnv {
2294 codegen_env: body_env,
2298 print_label(w, &default_val.0)?;
2300 triple_quotes(w, |w| print_expr(ctx, w, &expr_env, &default_val.1))
2304 fn print_label<W: Write>(w: &mut W, label: &Label) -> Result<(), W::Error> {
2306 Label::Regular(id) => {
2310 Label::DefaultArg(id) => {
2314 Label::Named(id) => w.write(id),
2318 fn print_local<W: Write>(w: &mut W, local: &Local) -> Result<(), W::Error> {
2320 Local::Unnamed(id) => {
2324 Local::Named(id) => w.write(id),
2328 fn print_int<W: Write>(w: &mut W, i: &usize) -> Result<(), W::Error> {
2332 fn print_key_value<W: Write>(
2338 ) -> Result<(), W::Error> {
2339 print_key_value_(ctx, w, env, k, print_expr, v)
2342 fn print_key_value_<W: Write, K, KeyPrinter>(
2349 ) -> Result<(), W::Error>
2351 KeyPrinter: FnMut(&mut Context, &mut W, &ExprEnv, K) -> Result<(), W::Error>,
2353 kp(ctx, w, env, k)?;
2355 print_expr(ctx, w, env, v)
2358 fn print_afield<W: Write>(
2362 afield: &ast::Afield,
2363 ) -> Result<(), W::Error> {
2364 use ast::Afield as A;
2366 A::AFvalue(e) => print_expr(ctx, w, env, &e),
2367 A::AFkvalue(k, v) => print_key_value(ctx, w, env, &k, &v),
2371 fn print_afields<W: Write>(
2375 afields: impl AsRef<[ast::Afield]>,
2376 ) -> Result<(), W::Error> {
2377 concat_by(w, ", ", afields, |w, i| print_afield(ctx, w, env, i))
2380 fn print_uop<W: Write>(w: &mut W, op: ast::Uop) -> Result<(), W::Error> {
2390 U::Upincr | U::Updecr => {
2391 return Err(Error::fail(
2392 "string_of_uop - should have been captures earlier",
2398 fn print_key_values<W: Write>(
2402 kvs: impl AsRef<[(ast::Expr, ast::Expr)]>,
2403 ) -> Result<(), W::Error> {
2404 print_key_values_(ctx, w, env, print_expr, kvs)
2407 fn print_key_values_<W: Write, K, KeyPrinter>(
2412 kvs: impl AsRef<[(K, ast::Expr)]>,
2413 ) -> Result<(), W::Error>
2415 KeyPrinter: Fn(&mut Context, &mut W, &ExprEnv, &K) -> Result<(), W::Error>,
2417 concat_by(w, ", ", kvs, |w, (k, v)| {
2418 print_key_value_(ctx, w, env, k, &mut kp, v)
2422 fn print_expr_darray<W: Write, K, KeyPrinter>(
2427 kvs: impl AsRef<[(K, ast::Expr)]>,
2428 ) -> Result<(), W::Error>
2430 KeyPrinter: Fn(&mut Context, &mut W, &ExprEnv, &K) -> Result<(), W::Error>,
2432 wrap_by_(w, "darray[", "]", |w| {
2433 print_key_values_(ctx, w, env, kp, kvs)
2437 fn print_expr_varray<W: Write>(
2441 varray: &[ast::Expr],
2442 ) -> Result<(), W::Error> {
2443 wrap_by_(w, "varray[", "]", |w| {
2444 concat_by(w, ", ", varray, |w, e| print_expr(ctx, w, env, e))
2448 fn print_shape_field_name<W: Write>(
2452 field: &ast::ShapeFieldName,
2453 ) -> Result<(), W::Error> {
2454 use ast::ShapeFieldName as S;
2456 S::SFlitInt((_, s)) => print_expr_int(w, s),
2457 S::SFlitStr((_, s)) | S::SFclassConst(_, (_, s)) => print_expr_string(w, s),
2461 fn print_expr_int<W: Write>(w: &mut W, i: &String) -> Result<(), W::Error> {
2462 match integer::to_decimal(i.as_str()) {
2463 Some(s) => w.write(s),
2464 None => Err(Error::fail("ParseIntError")),
2468 fn print_expr_string<W: Write>(w: &mut W, s: &String) -> Result<(), W::Error> {
2469 fn escape_char(c: u8) -> Option<Cow<'static, [u8]>> {
2471 b'\n' => Some((&b"\\\\n"[..]).into()),
2472 b'\r' => Some((&b"\\\\r"[..]).into()),
2473 b'\t' => Some((&b"\\\\t"[..]).into()),
2474 b'\\' => Some((&b"\\\\\\\\"[..]).into()),
2475 b'"' => Some((&b"\\\\\\\""[..]).into()),
2476 b'$' => Some((&b"\\\\$"[..]).into()),
2477 b'?' => Some((&b"\\?"[..]).into()),
2478 c if is_lit_printable(c) => None,
2481 write!(r, "\\\\{:03o}", c).unwrap();
2486 wrap_by(w, "\\\"", |w| w.write(escape_by(s.into(), escape_char)))
2489 fn print_expr_to_string<W: Write>(
2493 ) -> Result<String, W::Error> {
2494 let mut buf = String::new();
2495 print_expr(ctx, &mut buf, env, expr).map_err(|e| match e {
2496 Error::NotImpl(m) => Error::NotImpl(m),
2497 _ => Error::Fail(format!("Failed: {}", e)),
2502 fn print_expr<W: Write>(
2506 ast::Expr(_, expr): &ast::Expr,
2507 ) -> Result<(), W::Error> {
2508 fn adjust_id<'a>(env: &ExprEnv, id: &'a String) -> Cow<'a, str> {
2509 let s: Cow<'a, str> = match env.codegen_env {
2511 if env.namespace.name.is_none()
2515 .rposition(|c| *c == b'\\')
2516 .map_or(true, |i| i < 1)
2518 strip_global_ns(id).into()
2527 fn print_expr_id<'a, W: Write>(
2531 ) -> Result<(), W::Error> {
2532 w.write(adjust_id(env, s))
2534 fn fmt_class_name<'a>(is_class_constant: bool, id: Cow<'a, str>) -> Cow<'a, str> {
2535 let cn: Cow<'a, str> = if is_xhp(strip_global_ns(&id)) {
2536 escaper::escape(strip_global_ns(&mangle(id.into())))
2540 escaper::escape(strip_global_ns(&id)).to_string().into()
2542 if is_class_constant {
2543 format!("\\\\{}", cn).into()
2548 fn get_class_name_from_id<'e>(
2550 env: Option<&'e BodyEnv<'e>>,
2551 should_format: bool,
2552 is_class_constant: bool,
2555 if id == classes::SELF || id == classes::PARENT || id == classes::STATIC {
2556 return get_special_class_name(ctx, env, is_class_constant, id);
2558 fn get<'a>(should_format: bool, is_class_constant: bool, id: &'a str) -> Cow<'a, str> {
2560 fmt_class_name(is_class_constant, id.into())
2567 let class_id = class::Type::from_ast_name(id);
2568 let id = class_id.to_raw_string();
2569 get(should_format, is_class_constant, id)
2573 get(should_format, is_class_constant, id)
2576 fn get_special_class_name<'e>(
2578 env: Option<&'e BodyEnv<'e>>,
2579 is_class_constant: bool,
2582 let class_expr = match env {
2583 None => ClassExpr::expr_to_class_expr(
2587 &ast_scope::Scope::toplevel(),
2590 ast::Expr_::mk_id(ast_defs::Id(Pos::make_none(), id.into())),
2593 Some(body_env) => ClassExpr::expr_to_class_expr(
2600 ast::Expr_::mk_id(ast_defs::Id(Pos::make_none(), id.into())),
2604 let name = match class_expr {
2605 ClassExpr::Id(ast_defs::Id(_, name)) => Cow::Owned(name),
2606 _ => Cow::Borrowed(id),
2608 fmt_class_name(is_class_constant, name)
2610 fn handle_possible_colon_colon_class_expr<W: Write>(
2616 ) -> Result<Option<()>, W::Error> {
2617 match e_.as_class_const() {
2619 ast::ClassId(_, ast::ClassId_::CIexpr(ast::Expr(_, ast::Expr_::Id(id)))),
2621 )) if is_class(&s2) && !(is_self(&id.1) || is_parent(&id.1) || is_static(&id.1)) => {
2624 &get_class_name_from_id(ctx, env.codegen_env, false, false, &id.1).into();
2626 print_expr_id(w, env, s1)?
2628 print_expr_string(w, s1)?
2635 use ast::Expr_ as E_;
2637 E_::Id(id) => print_expr_id(w, env, &id.1),
2638 E_::Lvar(lid) => w.write(escaper::escape(&(lid.1).1)),
2640 if f.contains('E') || f.contains('e') {
2644 .map_err(|_| Error::fail(format!("ParseFloatError: {}", f)))?
2646 // to_uppercase() here because s might be "inf" or "nan"
2650 static ref UNSIGNED_EXP : Regex = Regex::new(r"(?P<first>E)(?P<second>\d+)").unwrap();
2651 static ref SIGNED_SINGLE_DIGIT_EXP: Regex = Regex::new(r"(?P<first>E[+-])(?P<second>\d$)").unwrap();
2653 // turn mEn into mE+n
2654 let s = UNSIGNED_EXP.replace(&s, "${first}+${second}");
2655 // turn mE+n or mE-n into mE+0n or mE-0n (where n is a single digit)
2656 let s = SIGNED_SINGLE_DIGIT_EXP.replace(&s, "${first}0${second}");
2662 E_::Int(i) => print_expr_int(w, i),
2663 E_::String(s) => print_expr_string(w, s),
2664 E_::Null => w.write("NULL"),
2665 E_::True => w.write("true"),
2666 E_::False => w.write("false"),
2667 // For arrays and collections, we are making a conscious decision to not
2668 // match HHMV has HHVM's emitter has inconsistencies in the pretty printer
2669 // https://fburl.com/tzom2qoe
2670 E_::Array(afl) => wrap_by_(w, "array(", ")", |w| print_afields(ctx, w, env, afl)),
2671 E_::Collection(c) if (c.0).1 == "vec" || (c.0).1 == "dict" || (c.0).1 == "keyset" => {
2673 square(w, |w| print_afields(ctx, w, env, &c.2))
2675 E_::Collection(c) => {
2676 let name = strip_ns((c.0).1.as_str());
2677 let name = types::fix_casing(&name);
2679 "Set" | "Pair" | "Vector" | "Map" | "ImmSet" | "ImmVector" | "ImmMap" => {
2682 wrap_by_(w, " {", "}", |w| {
2683 Ok(if !c.2.is_empty() {
2685 print_afields(ctx, w, env, &c.2)?;
2690 _ => Err(Error::fail(format!(
2691 "Default value for an unknow collection - {}",
2696 E_::Shape(fl) => print_expr_darray(ctx, w, env, print_shape_field_name, fl),
2698 let (bop, e1, e2) = &**x;
2699 print_expr(ctx, w, env, e1)?;
2703 print_expr(ctx, w, env, e2)
2706 let (_, e, _, es, unpacked_element) = &**c;
2708 Some(ast_defs::Id(_, call_id)) => {
2709 w.write(lstrip(adjust_id(env, &call_id).as_ref(), "\\\\"))?
2712 let buf = print_expr_to_string::<W>(ctx, env, e)?;
2713 w.write(lstrip(&buf, "\\\\"))?
2717 concat_by(w, ", ", &es, |w, e| print_expr(ctx, w, env, e))?;
2718 match unpacked_element {
2724 // TODO: Should probably have ... also but we are not doing that in ocaml
2725 print_expr(ctx, w, env, e)
2731 let (cid, _, es, unpacked_element, _) = &**x;
2732 match cid.1.as_ciexpr() {
2735 match ci_expr.1.as_id() {
2736 Some(ast_defs::Id(_, cname)) => w.write(lstrip(
2739 &class::Type::from_ast_name(cname).to_raw_string().into(),
2744 let buf = print_expr_to_string::<W>(ctx, env, ci_expr)?;
2745 w.write(lstrip(&buf, "\\\\"))?
2749 concat_by(w, ", ", es, |w, e| print_expr(ctx, w, env, e))?;
2750 match unpacked_element {
2754 print_expr(ctx, w, env, e)
2759 None => not_impl!(),
2763 w.write(lstrip(adjust_id(env, &(r.0).1).as_ref(), "\\\\"))?;
2764 print_key_values(ctx, w, env, &r.1)
2766 E_::ClassGet(cg) => {
2768 ast::ClassId_::CIexpr(e) => match e.as_id() {
2769 Some(id) => w.write(&get_class_name_from_id(
2772 true, /* should_format */
2773 false, /* is_class_constant */
2776 _ => print_expr(ctx, w, env, e)?,
2778 _ => return Err(Error::fail("TODO Unimplemented unexpected non-CIexpr")),
2782 ast::ClassGetExpr::CGstring((_, litstr)) => w.write(escaper::escape(litstr)),
2783 ast::ClassGetExpr::CGexpr(e) => print_expr(ctx, w, env, e),
2786 E_::ClassConst(cc) => {
2787 if let Some(e1) = (cc.0).1.as_ciexpr() {
2788 handle_possible_colon_colon_class_expr(ctx, w, env, false, expr)?.map_or_else(
2791 match e1.1.as_id() {
2792 Some(ast_defs::Id(_, s1)) => {
2794 get_class_name_from_id(ctx, env.codegen_env, true, true, s1);
2795 concat_str_by(w, "::", [&s1.into(), s2])
2798 print_expr(ctx, w, env, e1)?;
2807 Err(Error::fail("TODO: Only expected CIexpr in class_const"))
2810 E_::Unop(u) => match u.0 {
2811 ast::Uop::Upincr => {
2812 print_expr(ctx, w, env, &u.1)?;
2815 ast::Uop::Updecr => {
2816 print_expr(ctx, w, env, &u.1)?;
2821 print_expr(ctx, w, env, &u.1)
2825 print_expr(ctx, w, env, &og.0)?;
2826 w.write(match og.2 {
2827 ast::OgNullFlavor::OGNullthrows => "->",
2828 ast::OgNullFlavor::OGNullsafe => "\\?->",
2830 print_expr(ctx, w, env, &og.1)
2834 print_expr(ctx, w, env, e)
2836 E_::ArrayGet(ag) => {
2837 print_expr(ctx, w, env, &ag.0)?;
2839 option(w, &ag.1, |w, e: &ast::Expr| {
2840 handle_possible_colon_colon_class_expr(ctx, w, env, true, &e.1)
2842 .unwrap_or_else(|| print_expr(ctx, w, env, e))
2846 E_::String2(ss) => concat_by(w, " . ", ss, |w, s| print_expr(ctx, w, env, s)),
2847 E_::PrefixedString(s) => {
2850 print_expr(ctx, w, env, &s.1)
2853 print_expr(ctx, w, env, &eif.0)?;
2855 option(w, &eif.1, |w, etrue| print_expr(ctx, w, env, etrue))?;
2857 print_expr(ctx, w, env, &eif.2)
2859 E_::BracedExpr(e) => braces(w, |w| print_expr(ctx, w, env, e)),
2860 E_::ParenthesizedExpr(e) => paren(w, |w| print_expr(ctx, w, env, e)),
2862 paren(w, |w| print_hint(w, false, &c.0))?;
2863 print_expr(ctx, w, env, &c.1)
2866 print_expr(ctx, w, env, &p.1)?;
2868 print_expr(ctx, w, env, &p.2)
2871 print_expr(ctx, w, env, &i.0)?;
2873 print_hint(w, true, &i.1)
2876 print_expr(ctx, w, env, &a.0)?;
2877 w.write(if a.2 { " ?as " } else { " as " })?;
2878 print_hint(w, true, &a.1)
2880 E_::Varray(va) => print_expr_varray(ctx, w, env, &va.1),
2881 E_::Darray(da) => print_expr_darray(ctx, w, env, print_expr, &da.1),
2882 E_::List(l) => wrap_by_(w, "list(", ")", |w| {
2883 concat_by(w, ", ", l, |w, i| print_expr(ctx, w, env, i))
2887 print_afield(ctx, w, env, y)
2891 print_expr(ctx, w, env, e)
2893 E_::YieldBreak => w.write("return"),
2895 print_import_flavor(w, &i.0)?;
2897 print_expr(ctx, w, env, &i.1)
2899 E_::Xml(x) => print_xml(ctx, w, env, &(x.0).1, &x.1, &x.2),
2900 E_::Efun(f) => print_efun(ctx, w, env, &f.0, &f.1),
2901 E_::Omitted => Ok(()),
2902 E_::Lfun(_) => Err(Error::fail(
2903 "expected Lfun to be converted to Efun during closure conversion print_expr",
2905 E_::Suspend(_) | E_::Callconv(_) | E_::ExprList(_) => {
2906 Err(Error::fail("illegal default value"))
2908 _ => Err(Error::fail(
2909 "TODO Unimplemented: We are missing a lot of cases in the case match. Delete this catchall"
2914 fn print_xml<W: Write>(
2919 attrs: &[ast::XhpAttribute],
2920 children: &[ast::Expr],
2921 ) -> Result<(), W::Error> {
2922 fn print_xhp_attr<W: Write>(
2926 attr: &ast::XhpAttribute,
2928 ) -> Result<(), W::Error> {
2929 let key_printer = |_: &mut Context, w: &mut W, _: &ExprEnv, k| print_expr_string(w, k);
2931 ast::XhpAttribute::XhpSimple((_, s), e) => {
2932 print_key_value_(ctx, w, env, s, key_printer, e)
2934 ast::XhpAttribute::XhpSpread(e) => {
2935 print_key_value_(ctx, w, env, &format!("...${}", spread_id), key_printer, e)
2940 codegen_env: env.codegen_env,
2943 write!(w, "new {}", mangle(id.into()))?;
2945 wrap_by_(w, "darray[", "]", |w| {
2946 concat_by(w, ", ", attrs, |w, attr| {
2947 print_xhp_attr(ctx, w, &env, attr, 0)
2951 print_expr_varray(ctx, w, &env, children)?;
2952 w.write(", __FILE__, __LINE__")
2956 fn print_efun<W: Write>(
2961 use_list: &[ast::Lid],
2962 ) -> Result<(), W::Error> {
2963 w.write_if(f.static_, "static ")?;
2965 f.fun_kind.is_fasync() || f.fun_kind.is_fasync_generator(),
2968 w.write("function ")?;
2970 concat_by(w, ", ", &f.params, |w, p| print_fparam(ctx, w, env, p))
2973 if !use_list.is_empty() {
2976 concat_by(w, ", ", use_list, |w: &mut W, ast::Lid(_, id)| {
2977 w.write(local_id::get_name(id))
2982 print_block_(ctx, w, env, &f.body.ast, None)
2985 fn print_block<W: Write>(
2990 ident: Option<&str>,
2991 ) -> Result<(), W::Error> {
2993 [] | [ast::Stmt(_, ast::Stmt_::Noop)] => Ok(()),
2994 [ast::Stmt(_, ast::Stmt_::Block(b))] if matches!(&b[..], [_]) => {
2995 print_block_(ctx, w, env, b, ident)
2997 [_, _, ..] => print_block_(ctx, w, env, block, ident),
2998 [stmt] => print_statement(ctx, w, env, stmt, None),
3002 fn print_block_<W: Write>(
3007 ident: Option<&str>,
3008 ) -> Result<(), W::Error> {
3009 wrap_by_(w, "{\\n", "}\\n", |w| {
3010 concat(w, block, |w, stmt| {
3011 option(w, ident, |w, i: &str| w.write(i))?;
3012 print_statement(ctx, w, env, stmt, Some(" "))
3014 option(w, ident, |w, i: &str| w.write(i))
3018 fn print_statement<W: Write>(
3023 ident: Option<&str>,
3024 ) -> Result<(), W::Error> {
3025 use ast::Stmt_ as S_;
3027 S_::Return(expr) => {
3028 option(w, ident, |w, i: &str| w.write(i))?;
3029 wrap_by_(w, "return", ";\\n", |w| {
3030 option(w, &**expr, |w, e| {
3032 print_expr(ctx, w, env, e)
3037 option(w, ident, |w, i: &str| w.write(i))?;
3038 print_expr(ctx, w, env, &**expr)?;
3041 S_::Throw(expr) => {
3042 option(w, ident, |w, i: &str| w.write(i))?;
3043 wrap_by_(w, "throw ", ";\\n", |w| print_expr(ctx, w, env, &**expr))
3046 option(w, ident, |w, i: &str| w.write(i))?;
3047 w.write("break;\\n")
3050 option(w, ident, |w, i: &str| w.write(i))?;
3051 w.write("continue;\\n")
3054 let (cond, block) = &**x;
3055 option(w, ident, |w, i: &str| w.write(i))?;
3056 wrap_by_(w, "while (", ") ", |w| print_expr(ctx, w, env, cond))?;
3057 print_block(ctx, w, env, block, ident)
3060 let (cond, if_block, else_block) = &**x;
3061 option(w, ident, |w, i: &str| w.write(i))?;
3062 wrap_by_(w, "if (", ") ", |w| print_expr(ctx, w, env, cond))?;
3063 print_block(ctx, w, env, if_block, ident)?;
3064 let mut buf = String::new();
3065 print_block(ctx, &mut buf, env, else_block, ident).map_err(|e| match e {
3066 Error::NotImpl(m) => Error::NotImpl(m),
3067 _ => Error::Fail(format!("Failed: {}", e)),
3069 w.write_if(!buf.is_empty(), " else ")?;
3070 w.write_if(!buf.is_empty(), buf)
3072 S_::Block(block) => {
3073 option(w, ident, |w, i: &str| w.write(i))?;
3074 print_block_(ctx, w, env, block, ident)
3077 /* TODO(T29869930) */
3078 _ => w.write("TODO Unimplemented NYI: Default value printing"),
3082 fn print_fparam<W: Write>(
3086 param: &ast::FunParam,
3087 ) -> Result<(), W::Error> {
3088 if let Some(ast_defs::ParamKind::Pinout) = param.callconv {
3091 if param.is_variadic {
3094 option(w, &(param.type_hint).1, |w, h| {
3095 print_hint(w, true, h)?;
3098 w.write(¶m.name)?;
3099 option(w, ¶m.expr, |w, e| {
3101 print_expr(ctx, w, env, e)
3105 fn print_bop<W: Write>(w: &mut W, bop: &ast_defs::Bop) -> Result<(), W::Error> {
3108 Bop::Plus => w.write("+"),
3109 Bop::Minus => w.write("-"),
3110 Bop::Star => w.write("*"),
3111 Bop::Slash => w.write("/"),
3112 Bop::Eqeq => w.write("=="),
3113 Bop::Eqeqeq => w.write("==="),
3114 Bop::Starstar => w.write("**"),
3115 Bop::Eq(None) => w.write("="),
3116 Bop::Eq(Some(bop)) => {
3120 Bop::Ampamp => w.write("&&"),
3121 Bop::Barbar => w.write("||"),
3122 Bop::Lt => w.write("<"),
3123 Bop::Lte => w.write("<="),
3124 Bop::Cmp => w.write("<=>"),
3125 Bop::Gt => w.write(">"),
3126 Bop::Gte => w.write(">="),
3127 Bop::Dot => w.write("."),
3128 Bop::Amp => w.write("&"),
3129 Bop::Bar => w.write("|"),
3130 Bop::Ltlt => w.write("<<"),
3131 Bop::Gtgt => w.write(">>"),
3132 Bop::Percent => w.write("%"),
3133 Bop::Xor => w.write("^"),
3134 Bop::LogXor => w.write("xor"),
3135 Bop::Diff => w.write("!="),
3136 Bop::Diff2 => w.write("!=="),
3137 Bop::QuestionQuestion => w.write("\\?\\?"),
3141 fn print_hint<W: Write>(w: &mut W, ns: bool, hint: &ast::Hint) -> Result<(), W::Error> {
3142 let h = emit_type_hint::fmt_hint(&[], false, hint).map_err(|e| match e {
3143 Unrecoverable(s) => Error::fail(s),
3144 _ => Error::fail("Error printing hint"),
3147 w.write(escaper::escape(h))
3149 w.write(escaper::escape(strip_ns(&h)))
3153 fn print_import_flavor<W: Write>(w: &mut W, flavor: &ast::ImportFlavor) -> Result<(), W::Error> {
3154 use ast::ImportFlavor as F;
3155 w.write(match flavor {
3156 F::Include => "include",
3157 F::Require => "require",
3158 F::IncludeOnce => "include_once",
3159 F::RequireOnce => "require_once",
3163 fn print_param_user_attributes<W: Write>(
3167 ) -> Result<(), W::Error> {
3168 match ¶m.user_attributes[..] {
3170 _ => square(w, |w| print_attributes(ctx, w, ¶m.user_attributes)),
3174 fn print_span<W: Write>(w: &mut W, &Span(line_begin, line_end): &Span) -> Result<(), W::Error> {
3175 write!(w, "({},{})", line_begin, line_end)
3178 fn print_fun_attrs<W: Write>(
3182 ) -> Result<(), W::Error> {
3183 use hhas_attribute::*;
3184 let user_attrs = &f.attributes;
3185 let mut special_attrs = vec![];
3186 if let Ok(attr) = f.rx_level.try_into() {
3187 special_attrs.push(attr);
3189 if has_meth_caller(user_attrs) {
3190 special_attrs.push("builtin");
3191 special_attrs.push("is_meth_caller");
3193 if has_no_context(user_attrs) {
3194 special_attrs.push("no_context");
3196 if f.is_interceptable() {
3197 special_attrs.push("interceptable");
3199 if has_foldable(user_attrs) {
3200 special_attrs.push("foldable");
3202 if has_provenance_skip_frame(user_attrs) {
3203 special_attrs.push("prov_skip_frame");
3205 if f.is_no_injection() {
3206 special_attrs.push("no_injection");
3209 special_attrs.push("nontop");
3211 if ctx.is_system_lib() || (has_dynamically_callable(user_attrs) && !f.is_memoize_impl()) {
3212 special_attrs.push("dyn_callable")
3214 if ctx.is_system_lib() {
3215 special_attrs.push("unique");
3216 special_attrs.push("builtin");
3217 special_attrs.push("persistent");
3219 print_special_and_user_attrs(ctx, w, &special_attrs, user_attrs)
3222 fn print_special_and_user_attrs<W: Write>(
3226 users: &[HhasAttribute],
3227 ) -> Result<(), W::Error> {
3228 if !users.is_empty() || !specials.is_empty() {
3230 concat_str_by(w, " ", specials)?;
3231 if !specials.is_empty() && !users.is_empty() {
3234 print_attributes(ctx, w, users)
3241 fn print_upper_bounds<W: Write>(
3243 ubs: impl AsRef<[(String, Vec<HhasTypeInfo>)]>,
3244 ) -> Result<(), W::Error> {
3245 braces(w, |w| concat_by(w, ", ", ubs, print_upper_bound))
3248 fn print_upper_bound<W: Write>(
3250 (id, tys): &(String, Vec<HhasTypeInfo>),
3251 ) -> Result<(), W::Error> {
3253 concat_str_by(w, " ", [id.as_str(), "as", ""])?;
3254 concat_by(w, ", ", &tys, print_type_info)
3258 fn print_type_info<W: Write>(w: &mut W, ti: &HhasTypeInfo) -> Result<(), W::Error> {
3259 print_type_info_(w, false, ti)
3262 fn print_type_flags<W: Write>(w: &mut W, flag: constraint::Flags) -> Result<(), W::Error> {
3263 let mut first = true;
3264 let mut print_space = |w: &mut W| -> Result<(), W::Error> {
3271 use constraint::Flags as F;
3272 if flag.contains(F::DISPLAY_NULLABLE) {
3274 w.write("display_nullable")?;
3276 if flag.contains(F::EXTENDED_HINT) {
3278 w.write("extended_hint")?;
3280 if flag.contains(F::NULLABLE) {
3282 w.write("nullable")?;
3285 if flag.contains(F::SOFT) {
3289 if flag.contains(F::TYPE_CONSTANT) {
3291 w.write("type_constant")?;
3294 if flag.contains(F::TYPE_VAR) {
3296 w.write("type_var")?;
3299 if flag.contains(F::UPPERBOUND) {
3301 w.write("upper_bound")?;
3306 fn print_type_info_<W: Write>(w: &mut W, is_enum: bool, ti: &HhasTypeInfo) -> Result<(), W::Error> {
3307 let print_quote_str = |w: &mut W, opt: &Option<String>| {
3311 |w, s: &String| quotes(w, |w| w.write(escape(s))),
3316 print_quote_str(w, &ti.user_type)?;
3319 print_quote_str(w, &ti.type_constraint.name)?;
3322 print_type_flags(w, ti.type_constraint.flags)
3326 fn print_typedef_info<W: Write>(w: &mut W, ti: &HhasTypeInfo) -> Result<(), W::Error> {
3328 w.write(quote_string(
3329 ti.type_constraint.name.as_ref().map_or("", |n| n.as_str()),
3331 let flags = ti.type_constraint.flags & constraint::Flags::NULLABLE;
3332 if !flags.is_empty() {
3333 wrap_by(w, " ", |w| {
3334 print_type_flags(w, ti.type_constraint.flags & constraint::Flags::NULLABLE)
3341 fn print_extends<W: Write>(w: &mut W, base: Option<&str>) -> Result<(), W::Error> {
3344 Some(b) => concat_str_by(w, " ", [" extends", b]),
3348 fn print_record_field<W: Write>(
3351 Field(name, type_info, intial_value): &Field,
3352 ) -> Result<(), W::Error> {
3354 w.write(".property ")?;
3355 match intial_value {
3356 Some(_) => w.write("[public] ")?,
3357 None => w.write("[public sys_initial_val] ")?,
3359 print_type_info(w, type_info)?;
3360 concat_str_by(w, " ", ["", name, "="])?;
3362 ctx.block(w, |c, w| {
3364 match intial_value {
3365 None => w.write("uninit")?,
3366 Some(value) => triple_quotes(w, |w| print_adata(c, w, value))?,
3372 fn print_record_def<W: Write>(
3375 record: &HhasRecord,
3376 ) -> Result<(), W::Error> {
3378 if record.is_abstract {
3379 concat_str_by(w, " ", [".record", record.name.to_raw_string()])?;
3381 concat_str_by(w, " ", [".record", "[final]", record.name.to_raw_string()])?;
3383 print_extends(w, record.base.as_ref().map(|b| b.to_raw_string()))?;
3387 ctx.block(w, |c, w| {
3388 concat(w, &record.fields, |w, rf| print_record_field(c, w, rf))