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 use decl_provider::DeclProvider;
7 use ffi::{Maybe::*, Slice, Str};
8 use hhbc_by_ref_emit_attribute as emit_attribute;
9 use hhbc_by_ref_emit_body as emit_body;
10 use hhbc_by_ref_emit_expression as emit_expression;
11 use hhbc_by_ref_emit_fatal as emit_fatal;
12 use hhbc_by_ref_emit_memoize_method as emit_memoize_method;
13 use hhbc_by_ref_emit_method as emit_method;
14 use hhbc_by_ref_emit_pos as emit_pos;
15 use hhbc_by_ref_emit_property as emit_property;
16 use hhbc_by_ref_emit_symbol_refs as emit_symbol_refs;
17 use hhbc_by_ref_emit_type_constant as emit_type_constant;
18 use hhbc_by_ref_emit_type_hint as emit_type_hint;
19 use hhbc_by_ref_emit_xhp as emit_xhp;
20 use hhbc_by_ref_env::{emitter::Emitter, Env};
21 use hhbc_by_ref_hhas_attribute as hhas_attribute;
22 use hhbc_by_ref_hhas_class::{HhasClass, HhasClassFlags, TraitReqKind};
23 use hhbc_by_ref_hhas_coeffects::{HhasCoeffects, HhasCtxConstant};
24 use hhbc_by_ref_hhas_constant::{self as hhas_constant, HhasConstant};
25 use hhbc_by_ref_hhas_method::{HhasMethod, HhasMethodFlags};
26 use hhbc_by_ref_hhas_param::HhasParam;
27 use hhbc_by_ref_hhas_pos::Span;
28 use hhbc_by_ref_hhas_property::HhasProperty;
29 use hhbc_by_ref_hhas_type_const::HhasTypeConstant;
30 use hhbc_by_ref_hhas_xhp_attribute::HhasXhpAttribute;
31 use hhbc_by_ref_hhbc_ast::{FatalOp, FcallArgs, FcallFlags, ReadOnlyOp, SpecialClsRef};
32 use hhbc_by_ref_hhbc_id::r#const;
33 use hhbc_by_ref_hhbc_id::{self as hhbc_id, class, method, prop, Id};
34 use hhbc_by_ref_hhbc_string_utils as string_utils;
35 use hhbc_by_ref_instruction_sequence::{instr, InstrSeq, Result};
36 use hhbc_by_ref_label as label;
37 use hhbc_by_ref_local::Local;
38 use hhbc_by_ref_runtime::TypedValue;
39 use naming_special_names_rust as special_names;
41 ast::{self as tast, Hint, ReifyKind, Visibility},
46 use std::collections::BTreeMap;
48 fn add_symbol_refs<'arena, 'decl, D: DeclProvider<'decl>>(
49 alloc: &'arena bumpalo::Bump,
50 emitter: &mut Emitter<'arena, 'decl, D>,
51 base: &Option<class::ClassType<'arena>>,
52 implements: &[class::ClassType<'arena>],
54 requirements: &[(class::ClassType<'arena>, TraitReqKind)],
57 .for_each(|x| emit_symbol_refs::add_class(alloc, emitter, *x));
60 .for_each(|x| emit_symbol_refs::add_class(alloc, emitter, *x));
61 uses.iter().for_each(|x| {
62 emit_symbol_refs::add_class(alloc, emitter, class::ClassType::from_ast_name(alloc, x))
66 .for_each(|(x, _)| emit_symbol_refs::add_class(alloc, emitter, *x));
69 fn make_86method<'a, 'arena, 'decl, D: DeclProvider<'decl>>(
70 alloc: &'arena bumpalo::Bump,
71 emitter: &mut Emitter<'arena, 'decl, D>,
72 name: method::MethodType<'arena>,
73 params: Vec<HhasParam<'arena>>,
75 visibility: Visibility,
78 instrs: InstrSeq<'arena>,
79 ) -> Result<HhasMethod<'arena>> {
80 // TODO: move this. We just know that there are no iterators in 86methods
81 emitter.iterator_mut().reset();
83 let mut flags = HhasMethodFlags::empty();
84 flags.set(HhasMethodFlags::NO_INJECTION, true);
85 flags.set(HhasMethodFlags::IS_ABSTRACT, is_abstract);
86 flags.set(HhasMethodFlags::IS_STATIC, is_static);
88 let attributes = vec![];
89 let coeffects = HhasCoeffects::default();
91 let method_decl_vars = vec![];
92 let method_return_type = None;
93 let method_doc_comment = None;
94 let method_is_memoize_wrapper = false;
95 let method_is_memoize_wrapper_lsb = false;
96 let method_env = None;
98 let body = emit_body::make_body(
103 method_is_memoize_wrapper,
104 method_is_memoize_wrapper_lsb,
125 fn from_extends<'arena>(
126 alloc: &'arena bumpalo::Bump,
129 extends: &[tast::Hint],
130 ) -> Option<hhbc_id::class::ClassType<'arena>> {
132 // Do not use special_names:: as there's a prefix \ which breaks HHVM
134 Some(hhbc_id::class::from_raw_string(
136 "HH\\BuiltinEnumClass",
139 Some(hhbc_id::class::from_raw_string(alloc, "HH\\BuiltinEnum"))
144 .map(|x| emit_type_hint::hint_to_class(alloc, x))
148 fn from_implements<'arena>(
149 alloc: &'arena bumpalo::Bump,
150 implements: &[tast::Hint],
151 ) -> Vec<hhbc_id::class::ClassType<'arena>> {
154 .map(|x| emit_type_hint::hint_to_class(alloc, x))
158 fn from_includes<'arena>(
159 alloc: &'arena bumpalo::Bump,
160 includes: &[tast::Hint],
161 ) -> Vec<hhbc_id::class::ClassType<'arena>> {
164 .map(|x| emit_type_hint::hint_to_class(alloc, x))
168 fn from_type_constant<'a, 'arena, 'decl, D: DeclProvider<'decl>>(
169 alloc: &'arena bumpalo::Bump,
170 emitter: &mut Emitter<'arena, 'decl, D>,
171 tc: &'a tast::ClassTypeconstDef,
172 ) -> Result<HhasTypeConstant<'arena>> {
173 use tast::ClassTypeconst::*;
174 let name = tc.name.1.to_string();
176 let initializer = match &tc.kind {
177 TCAbstract(tast::ClassAbstractTypeconst { default: None, .. }) => None,
178 TCAbstract(tast::ClassAbstractTypeconst {
182 | TCPartiallyAbstract(tast::ClassPartiallyAbstractTypeconst { type_: init, .. })
183 | TCConcrete(tast::ClassConcreteTypeconst { c_tc_type: init }) => {
184 // TODO: Deal with the constraint
185 // Type constants do not take type vars hence tparams:[]
186 Some(emit_type_constant::hint_to_type_constant(
198 let is_abstract = match &tc.kind {
199 TCConcrete(_) => false,
203 Ok(HhasTypeConstant {
210 fn from_ctx_constant(tc: &tast::ClassTypeconstDef) -> Result<HhasCtxConstant> {
211 use tast::ClassTypeconst::*;
212 let name = tc.name.1.to_string();
213 let coeffects = match &tc.kind {
214 TCAbstract(tast::ClassAbstractTypeconst { default: None, .. }) => (vec![], vec![]),
215 TCPartiallyAbstract(_) => (vec![], vec![]), // does not parse
216 TCAbstract(tast::ClassAbstractTypeconst {
220 | TCConcrete(tast::ClassConcreteTypeconst { c_tc_type: hint }) => {
221 HhasCoeffects::from_ctx_constant(hint)
224 let is_abstract = match &tc.kind {
225 TCConcrete(_) => false,
235 fn from_class_elt_classvars<'a, 'arena, 'decl, D: DeclProvider<'decl>>(
236 alloc: &'arena bumpalo::Bump,
237 emitter: &mut Emitter<'arena, 'decl, D>,
238 ast_class: &'a tast::Class_,
239 class_is_const: bool,
241 ) -> Result<Vec<HhasProperty<'arena>>> {
242 // TODO: we need to emit doc comments for each property,
243 // not one per all properties on the same line
244 // The doc comment is only for the first name in the list.
245 // Currently this is organized in the ast_to_nast module
250 let hint = if cv.is_promoted_variadic {
256 emit_property::from_ast(
262 emit_property::FromAstArgs {
263 user_attributes: &cv.user_attributes,
265 initial_value: &cv.expr,
267 // Doc comments are weird. T40098274
268 doc_comment: cv.doc_comment.clone(),
269 visibility: cv.visibility, // This used to be cv_kinds
270 is_static: cv.is_static,
271 is_abstract: cv.abstract_,
272 is_readonly: cv.readonly,
276 .collect::<Result<Vec<_>>>()
279 fn from_class_elt_constants<'a, 'arena, 'decl, D: DeclProvider<'decl>>(
280 emitter: &mut Emitter<'arena, 'decl, D>,
281 env: &Env<'a, 'arena>,
282 class_: &'a tast::Class_,
283 ) -> Result<Vec<HhasConstant<'arena>>> {
284 use oxidized::aast::ClassConstKind::*;
289 let (is_abstract, init_opt) = match &x.kind {
290 CCAbstract(default) => (true, default.as_ref()),
291 CCConcrete(expr) => (false, Some(expr)),
293 hhas_constant::from_ast(emitter, env, &x.id, is_abstract, init_opt)
298 fn from_class_elt_requirements<'a, 'arena>(
299 alloc: &'arena bumpalo::Bump,
300 class_: &'a tast::Class_,
301 ) -> Vec<(hhbc_id::class::ClassType<'arena>, TraitReqKind)> {
305 .map(|(h, is_extends)| {
306 let kind = if *is_extends {
307 TraitReqKind::MustExtend
309 TraitReqKind::MustImplement
311 (emit_type_hint::hint_to_class(alloc, h), kind)
316 fn from_enum_type<'arena>(
317 alloc: &'arena bumpalo::Bump,
318 opt: Option<&tast::Enum_>,
319 ) -> Result<Option<hhbc_by_ref_hhas_type::Info<'arena>>> {
320 use hhbc_by_ref_hhas_type::constraint::*;
322 let type_info_user_type = Just(Str::new_str(
324 emit_type_hint::fmt_hint(alloc, &[], true, &e.base)?,
326 let type_info_type_constraint = Constraint::make(Nothing, ConstraintFlags::EXTENDED_HINT);
327 Ok(hhbc_by_ref_hhas_type::Info::make(
329 type_info_type_constraint,
335 fn validate_class_name(ns: &namespace_env::Env, tast::Id(p, class_name): &tast::Id) -> Result<()> {
336 let is_global_namespace = |ns: &namespace_env::Env| ns.name.is_none();
337 let is_hh_namespace = |ns: &namespace_env::Env| {
340 .map_or(false, |x| x.eq_ignore_ascii_case("hh"))
343 // global names are always reserved in any namespace.
344 // hh_reserved names are checked either if
345 // - class is in global namespace
346 // - class is in HH namespace *)
347 let is_special_class = class_name.contains('$');
348 let check_hh_name = is_global_namespace(ns) || is_hh_namespace(ns);
349 let name = string_utils::strip_ns(class_name);
350 let lower_name = name.to_ascii_lowercase();
351 let is_reserved_global_name = special_names::typehints::is_reserved_global_name(&lower_name);
352 let name_is_reserved = !is_special_class
353 && (is_reserved_global_name
354 || (check_hh_name && special_names::typehints::is_reserved_hh_name(&lower_name)));
355 if name_is_reserved {
356 Err(emit_fatal::raise_fatal_parse(
359 "Cannot use '{}' as class name as it is reserved",
360 if is_reserved_global_name {
363 string_utils::strip_global_ns(class_name)
372 fn emit_reified_extends_params<'a, 'arena, 'decl, D: DeclProvider<'decl>>(
373 e: &mut Emitter<'arena, 'decl, D>,
374 env: &Env<'a, 'arena>,
375 ast_class: &'a tast::Class_,
376 ) -> Result<InstrSeq<'arena>> {
377 let alloc = env.arena;
378 match &ast_class.extends[..] {
379 [h, ..] => match h.1.as_happly() {
380 Some((_, l)) if !l.is_empty() => {
381 return Ok(InstrSeq::gather(
384 emit_expression::emit_reified_targs(
388 &l.iter().collect::<Vec<_>>(),
390 instr::record_reified_generic(alloc),
398 let tv = TypedValue::Vec(Slice::new(&[]));
399 Ok(instr::typedvalue(alloc, tv))
402 fn emit_reified_init_body<'a, 'arena, 'decl, D: DeclProvider<'decl>>(
403 e: &mut Emitter<'arena, 'decl, D>,
404 env: &Env<'a, 'arena>,
406 ast_class: &'a tast::Class_,
407 ) -> Result<InstrSeq<'arena>> {
408 use string_utils::reified::{INIT_METH_NAME, INIT_METH_PARAM_NAME, PROP_NAME};
410 let alloc = env.arena;
411 let check_length = InstrSeq::gather(
416 Local::Named(Slice::new(INIT_METH_PARAM_NAME.as_bytes())),
418 instr::check_reified_generic_mismatch(alloc),
421 let set_prop = if num_reified == 0 {
428 instr::checkthis(alloc),
431 Local::Named(Slice::new(INIT_METH_PARAM_NAME.as_bytes())),
437 prop::from_raw_string(alloc, PROP_NAME),
444 let return_instr = InstrSeq::gather(alloc, vec![instr::null(alloc), instr::retc(alloc)]);
445 Ok(if ast_class.extends.is_empty() {
446 InstrSeq::gather(alloc, vec![set_prop, return_instr])
448 let generic_arr = emit_reified_extends_params(e, env, ast_class)?;
449 let call_parent = InstrSeq::gather(
452 instr::nulluninit(alloc),
453 instr::nulluninit(alloc),
455 instr::fcallclsmethodsd(
457 FcallArgs::new(FcallFlags::default(), 1, Slice::new(&[]), None, 1, None),
458 SpecialClsRef::Parent,
459 method::from_raw_string(alloc, INIT_METH_NAME),
464 InstrSeq::gather(alloc, vec![set_prop, call_parent, return_instr])
468 fn emit_reified_init_method<'a, 'arena, 'decl, D: DeclProvider<'decl>>(
469 emitter: &mut Emitter<'arena, 'decl, D>,
470 env: &Env<'a, 'arena>,
471 ast_class: &'a tast::Class_,
472 ) -> Result<Option<HhasMethod<'arena>>> {
473 use hhbc_by_ref_hhas_type::{constraint::*, Info};
475 let alloc = env.arena;
476 let num_reified = ast_class
479 .filter(|x| x.reified != ReifyKind::Erased)
481 let maybe_has_reified_parents = match ast_class.extends.first().as_ref() {
482 Some(Hint(_, h)) if h.as_happly().map_or(false, |(_, l)| !l.is_empty()) => true,
483 _ => false, // Hack classes can only extend a single parent
485 if num_reified == 0 && !maybe_has_reified_parents {
488 let tc = Constraint::make(Just("HH\\varray".into()), ConstraintFlags::empty());
489 let params = vec![HhasParam {
490 name: string_utils::reified::INIT_METH_PARAM_NAME.to_string(),
493 user_attributes: vec![],
494 type_info: Some(Info::make(Just("HH\\varray".into()), tc)),
498 let body_instrs = emit_reified_init_body(emitter, env, num_reified, ast_class)?;
499 let instrs = emit_pos::emit_pos_then(alloc, &ast_class.span, body_instrs);
500 Ok(Some(make_86method(
503 (alloc, string_utils::reified::INIT_METH_NAME).into(),
506 Visibility::Protected,
507 false, // is_abstract
508 Span::from_pos(&ast_class.span),
514 fn make_init_method<'a, 'arena, 'decl, D: DeclProvider<'decl>, F>(
515 alloc: &'arena bumpalo::Bump,
516 emitter: &mut Emitter<'arena, 'decl, D>,
517 properties: &[HhasProperty<'arena>],
521 ) -> Result<Option<HhasMethod<'arena>>>
523 F: Fn(&HhasProperty<'arena>) -> bool,
527 .any(|p: &HhasProperty| p.initializer_instrs.is_just() && filter(p))
529 let instrs = InstrSeq::gather(
535 // TODO(hrust) this clone can be avoided by wrapping initializer_instrs by Rc
536 // and also support Rc in InstrSeq
537 std::convert::Into::<Option<_>>::into(p.initializer_instrs.as_ref())
538 .map(|i| InstrSeq::clone(alloc, i))
545 let instrs = InstrSeq::gather(alloc, vec![instrs, instr::null(alloc), instr::retc(alloc)]);
546 Ok(Some(make_86method(
549 (alloc, name).into(),
553 false, // is_abstract
562 pub fn emit_class<'a, 'arena, 'decl, D: DeclProvider<'decl>>(
563 alloc: &'arena bumpalo::Bump,
564 emitter: &mut Emitter<'arena, 'decl, D>,
565 ast_class: &'a tast::Class_,
566 ) -> Result<HhasClass<'a, 'arena>> {
567 let namespace = &ast_class.namespace;
568 validate_class_name(namespace, &ast_class.name)?;
569 let mut env = Env::make_class_env(alloc, ast_class);
570 // TODO: communicate this without looking at the name
571 let is_closure = ast_class.name.1.starts_with("Closure$");
573 let mut attributes = emit_attribute::from_asts(alloc, emitter, &ast_class.user_attributes)?;
576 .extend(emit_attribute::add_reified_attribute(alloc, &ast_class.tparams).into_iter());
578 emit_attribute::add_reified_parent_attribute(&env, &ast_class.extends).into_iter(),
582 let is_const = hhas_attribute::has_const(&attributes);
583 // In the future, we intend to set class_no_dynamic_props independently from
584 // class_is_const, but for now class_is_const is the only thing that turns
586 let no_dynamic_props = is_const;
587 let name = class::ClassType::from_ast_name(alloc, &ast_class.name.1);
588 let is_trait = ast_class.kind == tast::ClassishKind::Ctrait;
589 let is_interface = ast_class.kind == tast::ClassishKind::Cinterface;
594 .filter_map(|x| match x.1.as_ref() {
595 tast::Hint_::Happly(tast::Id(_, name), _) => {
597 Some(Err(emit_fatal::raise_fatal_parse(
599 "Interfaces cannot use traits",
602 Some(Ok(name.as_str()))
607 .collect::<Result<Vec<_>>>()?;
609 let elaborate_namespace_id =
610 |x: &'a tast::Id| hhbc_id::class::ClassType::from_ast_name(alloc, x.name());
611 let use_aliases = ast_class
614 .map(|tast::UseAsAlias(ido1, id, ido2, vis)| {
615 let id1 = ido1.as_ref().map(|x| elaborate_namespace_id(x));
616 let id2 = ido2.as_ref().map(|x| (alloc, x.1.as_ref()).into());
617 (id1, (alloc, id.1.as_ref()).into(), id2, vis)
621 let use_precedences = ast_class
624 .map(|tast::InsteadofAlias(id1, id2, ids)| {
625 let id1 = elaborate_namespace_id(id1);
626 let id2: hhbc_by_ref_hhbc_id::class::ClassType<'arena> = (alloc, id2.1.as_ref()).into();
627 let ids = ids.iter().map(|x| elaborate_namespace_id(x)).collect();
632 let enum_type = if ast_class.kind == tast::ClassishKind::Cenum {
633 from_enum_type(alloc, ast_class.enum_.as_ref())?
637 let is_enum_class = if ast_class.kind == tast::ClassishKind::Cenum {
638 match &ast_class.enum_ {
639 Some(info) => info.enum_class,
645 let xhp_attributes: Vec<_> = ast_class
649 |tast::XhpAttr(type_, class_var, tag, maybe_enum)| HhasXhpAttribute {
650 type_: type_.1.as_ref(),
653 maybe_enum: maybe_enum.as_ref(),
658 let xhp_children = ast_class.xhp_children.first().map(|(p, sl)| (p, vec![sl]));
659 let xhp_categories: Option<(_, Vec<_>)> = ast_class
662 .map(|(p, c)| (p, c.iter().map(|x| &x.1).collect()));
664 let is_abstract = ast_class.kind == tast::ClassishKind::Cabstract;
665 let is_final = ast_class.final_ || is_trait;
666 let is_sealed = hhas_attribute::has_sealed(&attributes);
668 let tparams: Vec<&str> = ast_class
671 .map(|x| x.name.1.as_ref())
674 let base = if is_interface {
686 && base.as_ref().map_or(false, |cls| {
687 cls.to_raw_string().eq_ignore_ascii_case("closure")
690 return Err(emit_fatal::raise_fatal_runtime(
692 "Class cannot extend Closure",
695 let implements = if is_interface {
698 &ast_class.implements
700 let implements = from_implements(alloc, implements);
701 let enum_includes = if ast_class.kind == tast::ClassishKind::Cenum {
702 match &ast_class.enum_ {
704 Some(enum_) => from_includes(alloc, &enum_.includes),
709 let span = Span::from_pos(&ast_class.span);
710 let mut additional_methods: Vec<HhasMethod<'arena>> = vec![];
711 if let Some(cats) = xhp_categories {
712 additional_methods.push(emit_xhp::from_category_declaration(
713 alloc, emitter, ast_class, &cats,
716 if let Some(children) = xhp_children {
717 additional_methods.push(emit_xhp::from_children_declaration(
718 alloc, emitter, ast_class, &children,
721 let no_xhp_attributes = xhp_attributes.is_empty() && ast_class.xhp_attr_uses.is_empty();
722 if !no_xhp_attributes {
723 additional_methods.push(emit_xhp::from_attribute_declaration(
728 &ast_class.xhp_attr_uses,
731 emitter.label_gen_mut().reset();
732 let mut properties = from_class_elt_classvars(alloc, emitter, &ast_class, is_const, &tparams)?;
733 let constants = from_class_elt_constants(emitter, &env, ast_class)?;
735 let requirements = from_class_elt_requirements(alloc, ast_class);
737 let pinit_filter = |p: &HhasProperty| !p.is_static();
738 let sinit_filter = |p: &HhasProperty| p.is_static() && !p.is_lsb();
739 let linit_filter = |p: &HhasProperty| p.is_static() && p.is_lsb();
742 make_init_method(alloc, emitter, &properties, &pinit_filter, "86pinit", span)?;
744 make_init_method(alloc, emitter, &properties, &sinit_filter, "86sinit", span)?;
746 make_init_method(alloc, emitter, &properties, &linit_filter, "86linit", span)?;
748 let initialized_constants: Vec<_> = constants
754 ref initializer_instrs,
760 .map(|instrs| (name, emitter.label_gen_mut().next_regular(), instrs))
765 let cinit_method = if initialized_constants.is_empty() {
768 fn make_cinit_instrs<'arena, 'decl, D: DeclProvider<'decl>>(
769 alloc: &'arena bumpalo::Bump,
770 e: &mut Emitter<'arena, 'decl, D>,
771 default_label: label::Label,
773 consts: &[(&r#const::ConstType<'arena>, label::Label, &InstrSeq<'arena>)],
774 ) -> InstrSeq<'arena> {
776 [] => InstrSeq::gather(
779 instr::label(alloc, default_label),
780 emit_pos::emit_pos(alloc, pos),
781 instr::string(alloc, "Could not find initializer for "),
782 instr::cgetl(alloc, Local::Named(Slice::new("$constName".as_bytes()))),
783 instr::string(alloc, " in 86cinit"),
784 instr::concatn(alloc, 3),
785 instr::fatal(alloc, FatalOp::Runtime),
788 [(_, label, instrs), cs @ ..] => InstrSeq::gather(
791 instr::label(alloc, *label),
792 InstrSeq::clone(alloc, *instrs),
793 emit_pos::emit_pos(alloc, pos),
795 make_cinit_instrs(alloc, e, default_label, pos, cs),
800 let default_label = emitter.label_gen_mut().next_regular();
804 bumpalo::collections::Vec::with_capacity_in(initialized_constants.len() + 1, alloc);
805 for (name, label, _) in &initialized_constants {
806 let n: &str = alloc.alloc_str((*name).to_raw_string());
807 cases.push((n, *label))
809 cases.push((alloc.alloc_str("default"), default_label));
813 instr::cgetl(alloc, Local::Named(Slice::new("$constName".as_bytes()))),
814 instr::sswitch(alloc, cases),
820 &initialized_constants[..],
825 let instrs = emit_pos::emit_pos_then(alloc, &ast_class.span, body_instrs);
826 let params = vec![HhasParam {
827 name: "$constName".to_string(),
830 user_attributes: vec![],
838 (alloc, "86cinit").into(),
840 true, /* is_static */
842 is_interface, /* is_abstract */
848 let should_emit_reified_init = !(emitter.systemlib() || is_closure || is_interface || is_trait);
849 let reified_init_method = if should_emit_reified_init {
850 emit_reified_init_method(emitter, &env, ast_class)?
854 let needs_no_reifiedinit = reified_init_method.is_some() && ast_class.extends.is_empty();
855 additional_methods.extend(reified_init_method.into_iter());
856 additional_methods.extend(pinit_method.into_iter());
857 additional_methods.extend(sinit_method.into_iter());
858 additional_methods.extend(linit_method.into_iter());
859 additional_methods.extend(cinit_method.into_iter());
861 let mut methods = emit_method::from_asts(alloc, emitter, ast_class, &ast_class.methods)?;
862 methods.extend(additional_methods.into_iter());
863 let (ctxconsts, tconsts): (Vec<_>, Vec<_>) =
864 ast_class.typeconsts.iter().partition(|x| x.is_ctx);
865 let type_constants = tconsts
867 .map(|x| from_type_constant(alloc, emitter, x))
868 .collect::<Result<Vec<HhasTypeConstant>>>()?;
869 let ctx_constants = ctxconsts
871 .map(|x| from_ctx_constant(x))
872 .collect::<Result<Vec<HhasCtxConstant>>>()?;
873 let upper_bounds = emit_body::emit_generics_upper_bounds(alloc, &ast_class.tparams, &[], false);
875 if !no_xhp_attributes {
876 properties.extend(emit_xhp::properties_for_cache(
877 alloc, emitter, ast_class, is_const,
880 let info = emit_memoize_method::make_info(ast_class, name, &ast_class.methods)?;
881 methods.extend(emit_memoize_method::emit_wrapper_methods(
888 let doc_comment = ast_class.doc_comment.clone();
889 let is_xhp = ast_class.is_xhp || ast_class.has_xhp_keyword;
891 let mut flags = HhasClassFlags::empty();
892 flags.set(HhasClassFlags::IS_FINAL, is_final);
893 flags.set(HhasClassFlags::IS_SEALED, is_sealed);
894 flags.set(HhasClassFlags::IS_ABSTRACT, is_abstract);
895 flags.set(HhasClassFlags::IS_INTERFACE, is_interface);
896 flags.set(HhasClassFlags::IS_TRAIT, is_trait);
897 flags.set(HhasClassFlags::IS_XHP, is_xhp);
898 flags.set(HhasClassFlags::IS_CONST, is_const);
899 flags.set(HhasClassFlags::NO_DYNAMIC_PROPS, no_dynamic_props);
900 flags.set(HhasClassFlags::NEEDS_NO_REIFIEDINIT, needs_no_reifiedinit);
933 pub fn emit_classes_from_program<'a, 'arena, 'decl, D: DeclProvider<'decl>>(
934 alloc: &'arena bumpalo::Bump,
935 emitter: &mut Emitter<'arena, 'decl, D>,
936 tast: &'a [tast::Def],
937 ) -> Result<Vec<HhasClass<'a, 'arena>>> {
939 .filter_map(|class| {
940 if let tast::Def::Class(cd) = class {
941 Some(emit_class(alloc, emitter, cd))