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 hhbc_by_ref_emit_attribute as emit_attribute;
7 use hhbc_by_ref_emit_body as emit_body;
8 use hhbc_by_ref_emit_expression as emit_expression;
9 use hhbc_by_ref_emit_fatal as emit_fatal;
10 use hhbc_by_ref_emit_memoize_method as emit_memoize_method;
11 use hhbc_by_ref_emit_method as emit_method;
12 use hhbc_by_ref_emit_pos as emit_pos;
13 use hhbc_by_ref_emit_property as emit_property;
14 use hhbc_by_ref_emit_symbol_refs as emit_symbol_refs;
15 use hhbc_by_ref_emit_type_constant as emit_type_constant;
16 use hhbc_by_ref_emit_type_hint as emit_type_hint;
17 use hhbc_by_ref_emit_xhp as emit_xhp;
18 use hhbc_by_ref_env::{emitter::Emitter, Env};
19 use hhbc_by_ref_hhas_attribute as hhas_attribute;
20 use hhbc_by_ref_hhas_class::{HhasClass, HhasClassFlags, TraitReqKind};
21 use hhbc_by_ref_hhas_coeffects::{HhasCoeffects, HhasCtxConstant};
22 use hhbc_by_ref_hhas_constant::{self as hhas_constant, HhasConstant};
23 use hhbc_by_ref_hhas_method::{HhasMethod, HhasMethodFlags};
24 use hhbc_by_ref_hhas_param::HhasParam;
25 use hhbc_by_ref_hhas_pos::Span;
26 use hhbc_by_ref_hhas_property::HhasProperty;
27 use hhbc_by_ref_hhas_type_const::HhasTypeConstant;
28 use hhbc_by_ref_hhas_xhp_attribute::HhasXhpAttribute;
29 use hhbc_by_ref_hhbc_ast::{FatalOp, FcallArgs, FcallFlags, ReadOnlyOp, SpecialClsRef};
30 use hhbc_by_ref_hhbc_id::r#const;
31 use hhbc_by_ref_hhbc_id::{self as hhbc_id, class, method, prop, Id};
32 use hhbc_by_ref_hhbc_string_utils as string_utils;
33 use hhbc_by_ref_instruction_sequence::{instr, InstrSeq, Result};
34 use hhbc_by_ref_label as label;
35 use hhbc_by_ref_runtime::TypedValue;
36 use naming_special_names_rust as special_names;
38 ast::{self as tast, Hint, ReifyKind, Visibility},
43 use std::collections::BTreeMap;
45 fn add_symbol_refs<'arena>(
46 alloc: &'arena bumpalo::Bump,
47 emitter: &mut Emitter<'arena>,
48 base: &Option<class::Type<'arena>>,
49 implements: &[class::Type<'arena>],
51 requirements: &[(class::Type<'arena>, TraitReqKind)],
54 .for_each(|x| emit_symbol_refs::add_class(alloc, emitter, *x));
57 .for_each(|x| emit_symbol_refs::add_class(alloc, emitter, *x));
58 uses.iter().for_each(|x| {
59 emit_symbol_refs::add_class(alloc, emitter, class::Type::from_ast_name(alloc, x))
63 .for_each(|(x, _)| emit_symbol_refs::add_class(alloc, emitter, *x));
66 fn make_86method<'a, 'arena>(
67 alloc: &'arena bumpalo::Bump,
68 emitter: &mut Emitter<'arena>,
69 name: method::Type<'arena>,
70 params: Vec<HhasParam<'arena>>,
72 visibility: Visibility,
75 instrs: InstrSeq<'arena>,
76 ) -> Result<HhasMethod<'arena>> {
77 // TODO: move this. We just know that there are no iterators in 86methods
78 emitter.iterator_mut().reset();
80 let mut flags = HhasMethodFlags::empty();
81 flags.set(HhasMethodFlags::NO_INJECTION, true);
82 flags.set(HhasMethodFlags::IS_ABSTRACT, is_abstract);
83 flags.set(HhasMethodFlags::IS_STATIC, is_static);
85 let attributes = vec![];
86 let coeffects = HhasCoeffects::default();
88 let method_decl_vars = vec![];
89 let method_return_type = None;
90 let method_doc_comment = None;
91 let method_is_memoize_wrapper = false;
92 let method_is_memoize_wrapper_lsb = false;
93 let method_env = None;
95 let body = emit_body::make_body(
100 method_is_memoize_wrapper,
101 method_is_memoize_wrapper_lsb,
121 fn from_extends<'arena>(
122 alloc: &'arena bumpalo::Bump,
125 extends: &[tast::Hint],
126 ) -> Option<hhbc_id::class::Type<'arena>> {
128 // Do not use special_names:: as there's a prefix \ which breaks HHVM
130 Some(hhbc_id::class::from_raw_string(
132 "HH\\BuiltinEnumClass",
135 Some(hhbc_id::class::from_raw_string(alloc, "HH\\BuiltinEnum"))
140 .map(|x| emit_type_hint::hint_to_class(alloc, x))
144 fn from_implements<'arena>(
145 alloc: &'arena bumpalo::Bump,
146 implements: &[tast::Hint],
147 ) -> Vec<hhbc_id::class::Type<'arena>> {
150 .map(|x| emit_type_hint::hint_to_class(alloc, x))
154 fn from_includes<'arena>(
155 alloc: &'arena bumpalo::Bump,
156 includes: &[tast::Hint],
157 ) -> Vec<hhbc_id::class::Type<'arena>> {
160 .map(|x| emit_type_hint::hint_to_class(alloc, x))
164 fn from_type_constant<'a, 'arena>(
165 alloc: &'arena bumpalo::Bump,
166 emitter: &mut Emitter<'arena>,
167 tc: &'a tast::ClassTypeconst,
168 ) -> Result<HhasTypeConstant<'arena>> {
169 use tast::TypeconstAbstractKind::*;
170 let name = tc.name.1.to_string();
172 let initializer = match (&tc.abstract_, &tc.type_) {
173 (TCAbstract(None), _) | (TCPartiallyAbstract, None) | (TCConcrete, None) => None,
174 (TCAbstract(Some(init)), _)
175 | (TCPartiallyAbstract, Some(init))
176 | (TCConcrete, Some(init)) => {
177 // TODO: Deal with the constraint
178 // Type constants do not take type vars hence tparams:[]
179 Some(emit_type_constant::hint_to_type_constant(
191 let is_abstract = match &tc.abstract_ {
196 Ok(HhasTypeConstant {
203 fn from_ctx_constant(tc: &tast::ClassTypeconst) -> Result<HhasCtxConstant> {
204 use tast::TypeconstAbstractKind::*;
205 let name = tc.name.1.to_string();
206 let coeffects = match (&tc.abstract_, &tc.type_) {
207 (TCAbstract(Some(hint)), _) | (_, Some(hint)) => {
208 let result = HhasCoeffects::from_ctx_constant(hint);
209 if result.is_empty() {
210 vec![hhbc_by_ref_hhas_coeffects::Ctx::Pure]
217 let is_abstract = match &tc.abstract_ {
228 fn from_class_elt_classvars<'a, 'arena>(
229 alloc: &'arena bumpalo::Bump,
230 emitter: &mut Emitter<'arena>,
231 ast_class: &'a tast::Class_,
232 class_is_const: bool,
234 ) -> Result<Vec<HhasProperty<'arena>>> {
235 // TODO: we need to emit doc comments for each property,
236 // not one per all properties on the same line
237 // The doc comment is only for the first name in the list.
238 // Currently this is organized in the ast_to_nast module
243 let hint = if cv.is_promoted_variadic {
249 emit_property::from_ast(
255 emit_property::FromAstArgs {
256 user_attributes: &cv.user_attributes,
258 initial_value: &cv.expr,
260 // Doc comments are weird. T40098274
261 doc_comment: cv.doc_comment.clone(),
262 visibility: cv.visibility, // This used to be cv_kinds
263 is_static: cv.is_static,
264 is_abstract: cv.abstract_,
265 is_readonly: cv.readonly,
269 .collect::<Result<Vec<_>>>()
272 fn from_class_elt_constants<'a, 'arena>(
273 emitter: &mut Emitter<'arena>,
274 env: &Env<'a, 'arena>,
275 class_: &'a tast::Class_,
276 ) -> Result<Vec<HhasConstant<'arena>>> {
280 .map(|x| hhas_constant::from_ast(emitter, env, &x.id, x.expr.as_ref()))
284 fn from_class_elt_requirements<'a, 'arena>(
285 alloc: &'arena bumpalo::Bump,
286 class_: &'a tast::Class_,
287 ) -> Vec<(hhbc_id::class::Type<'arena>, TraitReqKind)> {
291 .map(|(h, is_extends)| {
292 let kind = if *is_extends {
293 TraitReqKind::MustExtend
295 TraitReqKind::MustImplement
297 (emit_type_hint::hint_to_class(alloc, h), kind)
302 fn from_enum_type(opt: Option<&tast::Enum_>) -> Result<Option<hhbc_by_ref_hhas_type::Info>> {
303 use hhbc_by_ref_hhas_type::constraint::*;
305 let alloc = bumpalo::Bump::new();
306 let type_info_user_type = Some(emit_type_hint::fmt_hint(&alloc, &[], true, &e.base)?);
307 let type_info_type_constraint = Type::make(None, Flags::EXTENDED_HINT);
308 Ok(hhbc_by_ref_hhas_type::Info::make(
310 type_info_type_constraint,
316 fn validate_class_name(ns: &namespace_env::Env, tast::Id(p, class_name): &tast::Id) -> Result<()> {
317 let is_global_namespace = |ns: &namespace_env::Env| ns.name.is_none();
318 let is_hh_namespace = |ns: &namespace_env::Env| {
321 .map_or(false, |x| x.eq_ignore_ascii_case("hh"))
324 // global names are always reserved in any namespace.
325 // hh_reserved names are checked either if
326 // - class is in global namespace
327 // - class is in HH namespace *)
328 let is_special_class = class_name.contains('$');
329 let check_hh_name = is_global_namespace(ns) || is_hh_namespace(ns);
330 let name = string_utils::strip_ns(class_name);
331 let lower_name = name.to_ascii_lowercase();
332 let is_reserved_global_name = special_names::typehints::is_reserved_global_name(&lower_name);
333 let name_is_reserved = !is_special_class
334 && (is_reserved_global_name
335 || (check_hh_name && special_names::typehints::is_reserved_hh_name(&lower_name)));
336 if name_is_reserved {
337 Err(emit_fatal::raise_fatal_parse(
340 "Cannot use '{}' as class name as it is reserved",
341 if is_reserved_global_name {
344 string_utils::strip_global_ns(class_name)
353 fn emit_reified_extends_params<'a, 'arena>(
354 e: &mut Emitter<'arena>,
355 env: &Env<'a, 'arena>,
356 ast_class: &'a tast::Class_,
357 ) -> Result<InstrSeq<'arena>> {
358 let alloc = env.arena;
359 match &ast_class.extends[..] {
360 [h, ..] => match h.1.as_happly() {
361 Some((_, l)) if !l.is_empty() => {
362 return Ok(InstrSeq::gather(
365 emit_expression::emit_reified_targs(
369 &l.iter().collect::<Vec<_>>(),
371 instr::record_reified_generic(alloc),
379 let tv = TypedValue::Vec(&[]);
380 Ok(instr::typedvalue(alloc, tv))
383 fn emit_reified_init_body<'a, 'arena>(
384 e: &mut Emitter<'arena>,
385 env: &Env<'a, 'arena>,
387 ast_class: &'a tast::Class_,
388 ) -> Result<InstrSeq<'arena>> {
389 use string_utils::reified::{INIT_METH_NAME, INIT_METH_PARAM_NAME, PROP_NAME};
391 let alloc = env.arena;
392 let check_length = InstrSeq::gather(
395 instr::cgetl(alloc, hhbc_by_ref_local::Type::Named(INIT_METH_PARAM_NAME)),
396 instr::check_reified_generic_mismatch(alloc),
399 let set_prop = if num_reified == 0 {
406 instr::checkthis(alloc),
407 instr::cgetl(alloc, hhbc_by_ref_local::Type::Named(INIT_METH_PARAM_NAME)),
412 prop::from_raw_string(alloc, PROP_NAME),
419 let return_instr = InstrSeq::gather(alloc, vec![instr::null(alloc), instr::retc(alloc)]);
420 Ok(if ast_class.extends.is_empty() {
421 InstrSeq::gather(alloc, vec![set_prop, return_instr])
423 let generic_arr = emit_reified_extends_params(e, env, ast_class)?;
424 let call_parent = InstrSeq::gather(
427 instr::nulluninit(alloc),
428 instr::nulluninit(alloc),
430 instr::fcallclsmethodsd(
432 FcallArgs::new(alloc, FcallFlags::default(), 1, &[], None, 1, None),
433 SpecialClsRef::Parent,
434 method::from_raw_string(alloc, INIT_METH_NAME),
439 InstrSeq::gather(alloc, vec![set_prop, call_parent, return_instr])
443 fn emit_reified_init_method<'a, 'arena>(
444 emitter: &mut Emitter<'arena>,
445 env: &Env<'a, 'arena>,
446 ast_class: &'a tast::Class_,
447 ) -> Result<Option<HhasMethod<'arena>>> {
448 use hhbc_by_ref_hhas_type::{constraint::*, Info};
450 let alloc = env.arena;
451 let num_reified = ast_class
454 .filter(|x| x.reified != ReifyKind::Erased)
456 let maybe_has_reified_parents = match ast_class.extends.first().as_ref() {
457 Some(Hint(_, h)) if h.as_happly().map_or(false, |(_, l)| !l.is_empty()) => true,
458 _ => false, // Hack classes can only extend a single parent
460 if num_reified == 0 && !maybe_has_reified_parents {
463 let tc = Type::make(Some("HH\\varray".into()), Flags::empty());
464 let params = vec![HhasParam {
465 name: string_utils::reified::INIT_METH_PARAM_NAME.to_string(),
468 user_attributes: vec![],
469 type_info: Some(Info::make(Some("HH\\varray".into()), tc)),
473 let body_instrs = emit_reified_init_body(emitter, env, num_reified, ast_class)?;
474 let instrs = emit_pos::emit_pos_then(alloc, &ast_class.span, body_instrs);
475 Ok(Some(make_86method(
478 (alloc, string_utils::reified::INIT_METH_NAME).into(),
481 Visibility::Protected,
482 false, // is_abstract
483 Span::from_pos(&ast_class.span),
489 fn make_init_method<'a, 'arena, F>(
490 alloc: &'arena bumpalo::Bump,
491 emitter: &mut Emitter<'arena>,
492 properties: &[HhasProperty<'arena>],
496 ) -> Result<Option<HhasMethod<'arena>>>
498 F: Fn(&HhasProperty<'arena>) -> bool,
502 .any(|p: &HhasProperty| p.initializer_instrs.is_some() && filter(p))
504 let instrs = InstrSeq::gather(
510 // TODO(hrust) this clone can be avoided by wrapping initializer_instrs by Rc
511 // and also support Rc in InstrSeq
514 .map(|i| InstrSeq::clone(alloc, i))
521 let instrs = InstrSeq::gather(alloc, vec![instrs, instr::null(alloc), instr::retc(alloc)]);
522 Ok(Some(make_86method(
525 (alloc, name).into(),
529 false, // is_abstract
538 pub fn emit_class<'a, 'arena>(
539 alloc: &'arena bumpalo::Bump,
540 emitter: &mut Emitter<'arena>,
541 ast_class: &'a tast::Class_,
542 ) -> Result<HhasClass<'a, 'arena>> {
543 let namespace = &ast_class.namespace;
544 validate_class_name(namespace, &ast_class.name)?;
545 let mut env = Env::make_class_env(alloc, ast_class);
546 // TODO: communicate this without looking at the name
547 let is_closure = ast_class.name.1.starts_with("Closure$");
549 let mut attributes = emit_attribute::from_asts(alloc, emitter, &ast_class.user_attributes)?;
551 attributes.extend(emit_attribute::add_reified_attribute(&ast_class.tparams).into_iter());
553 emit_attribute::add_reified_parent_attribute(&env, &ast_class.extends).into_iter(),
557 let is_const = hhas_attribute::has_const(&attributes);
558 // In the future, we intend to set class_no_dynamic_props independently from
559 // class_is_const, but for now class_is_const is the only thing that turns
561 let no_dynamic_props = is_const;
562 let name = class::Type::from_ast_name(alloc, &ast_class.name.1);
563 let is_trait = ast_class.kind == tast::ClassKind::Ctrait;
564 let is_interface = ast_class.kind == tast::ClassKind::Cinterface;
569 .filter_map(|x| match x.1.as_ref() {
570 tast::Hint_::Happly(tast::Id(_, name), _) => {
572 Some(Err(emit_fatal::raise_fatal_parse(
574 "Interfaces cannot use traits",
577 Some(Ok(name.as_str()))
582 .collect::<Result<Vec<_>>>()?;
584 let elaborate_namespace_id =
585 |x: &'a tast::Id| hhbc_id::class::Type::from_ast_name(alloc, x.name());
586 let use_aliases = ast_class
589 .map(|tast::UseAsAlias(ido1, id, ido2, vis)| {
590 let id1 = ido1.as_ref().map(|x| elaborate_namespace_id(x));
591 let id2 = ido2.as_ref().map(|x| (alloc, x.1.as_ref()).into());
592 (id1, (alloc, id.1.as_ref()).into(), id2, vis)
596 let use_precedences = ast_class
599 .map(|tast::InsteadofAlias(id1, id2, ids)| {
600 let id1 = elaborate_namespace_id(id1);
601 let id2: hhbc_by_ref_hhbc_id::class::Type<'arena> = (alloc, id2.1.as_ref()).into();
602 let ids = ids.iter().map(|x| elaborate_namespace_id(x)).collect();
607 let enum_type = if ast_class.kind == tast::ClassKind::Cenum {
608 from_enum_type(ast_class.enum_.as_ref())?
612 let is_enum_class = if ast_class.kind == tast::ClassKind::Cenum {
613 match &ast_class.enum_ {
614 Some(info) => info.enum_class,
620 let xhp_attributes: Vec<_> = ast_class
624 |tast::XhpAttr(type_, class_var, tag, maybe_enum)| HhasXhpAttribute {
625 type_: type_.1.as_ref(),
628 maybe_enum: maybe_enum.as_ref(),
633 let xhp_children = ast_class.xhp_children.first().map(|(p, sl)| (p, vec![sl]));
634 let xhp_categories: Option<(_, Vec<_>)> = ast_class
637 .map(|(p, c)| (p, c.iter().map(|x| &x.1).collect()));
639 let is_abstract = ast_class.kind == tast::ClassKind::Cabstract;
640 let is_final = ast_class.final_ || is_trait;
641 let is_sealed = hhas_attribute::has_sealed(&attributes);
643 let tparams: Vec<&str> = ast_class
646 .map(|x| x.name.1.as_ref())
649 let base = if is_interface {
661 && base.as_ref().map_or(false, |cls| {
662 cls.to_raw_string().eq_ignore_ascii_case("closure")
665 return Err(emit_fatal::raise_fatal_runtime(
667 "Class cannot extend Closure",
670 let implements = if is_interface {
673 &ast_class.implements
675 let implements = from_implements(alloc, implements);
676 let enum_includes = if ast_class.kind == tast::ClassKind::Cenum {
677 match &ast_class.enum_ {
679 Some(enum_) => from_includes(alloc, &enum_.includes),
684 let span = Span::from_pos(&ast_class.span);
685 let mut additional_methods: Vec<HhasMethod<'arena>> = vec![];
686 if let Some(cats) = xhp_categories {
687 additional_methods.push(emit_xhp::from_category_declaration(
688 alloc, emitter, ast_class, &cats,
691 if let Some(children) = xhp_children {
692 additional_methods.push(emit_xhp::from_children_declaration(
693 alloc, emitter, ast_class, &children,
696 let no_xhp_attributes = xhp_attributes.is_empty() && ast_class.xhp_attr_uses.is_empty();
697 if !no_xhp_attributes {
698 additional_methods.push(emit_xhp::from_attribute_declaration(
703 &ast_class.xhp_attr_uses,
706 emitter.label_gen_mut().reset();
707 let mut properties = from_class_elt_classvars(alloc, emitter, &ast_class, is_const, &tparams)?;
708 let constants = from_class_elt_constants(emitter, &env, ast_class)?;
710 let requirements = from_class_elt_requirements(alloc, ast_class);
712 let pinit_filter = |p: &HhasProperty| !p.is_static();
713 let sinit_filter = |p: &HhasProperty| p.is_static() && !p.is_lsb();
714 let linit_filter = |p: &HhasProperty| p.is_static() && p.is_lsb();
717 make_init_method(alloc, emitter, &properties, &pinit_filter, "86pinit", span)?;
719 make_init_method(alloc, emitter, &properties, &sinit_filter, "86sinit", span)?;
721 make_init_method(alloc, emitter, &properties, &linit_filter, "86linit", span)?;
723 let initialized_constants: Vec<_> = constants
729 ref initializer_instrs,
735 .map(|instrs| (name, emitter.label_gen_mut().next_regular(alloc), instrs))
739 let cinit_method = if initialized_constants.is_empty() {
742 fn make_cinit_instrs<'arena>(
743 alloc: &'arena bumpalo::Bump,
744 e: &mut Emitter<'arena>,
745 default_label: label::Label<'arena>,
748 &r#const::Type<'arena>,
749 label::Label<'arena>,
752 ) -> InstrSeq<'arena> {
754 [] => InstrSeq::gather(
757 instr::label(alloc, default_label),
758 emit_pos::emit_pos(alloc, pos),
759 instr::string(alloc, "Could not find initializer for "),
760 instr::cgetl(alloc, hhbc_by_ref_local::Type::Named("$constName")),
761 instr::string(alloc, " in 86cinit"),
762 instr::concatn(alloc, 3),
763 instr::fatal(alloc, FatalOp::Runtime),
766 [(_, label, instrs), cs @ ..] => InstrSeq::gather(
769 instr::label(alloc, *label),
770 InstrSeq::clone(alloc, *instrs),
771 emit_pos::emit_pos(alloc, pos),
773 make_cinit_instrs(alloc, e, default_label, pos, cs),
778 let default_label = emitter.label_gen_mut().next_regular(alloc);
782 bumpalo::collections::Vec::with_capacity_in(initialized_constants.len() + 1, alloc);
783 for (name, label, _) in &initialized_constants {
784 let n: &str = alloc.alloc_str((*name).to_raw_string());
785 cases.push((n, *label))
787 cases.push((alloc.alloc_str("default"), default_label));
791 instr::cgetl(alloc, hhbc_by_ref_local::Type::Named("$constName")),
792 instr::sswitch(alloc, cases),
798 &initialized_constants[..],
803 let instrs = emit_pos::emit_pos_then(alloc, &ast_class.span, body_instrs);
804 let params = vec![HhasParam {
805 name: "$constName".to_string(),
808 user_attributes: vec![],
816 (alloc, "86cinit").into(),
818 true, /* is_static */
820 is_interface, /* is_abstract */
826 let should_emit_reified_init = !(emitter.systemlib() || is_closure || is_interface || is_trait);
827 let reified_init_method = if should_emit_reified_init {
828 emit_reified_init_method(emitter, &env, ast_class)?
832 let needs_no_reifiedinit = reified_init_method.is_some() && ast_class.extends.is_empty();
833 additional_methods.extend(reified_init_method.into_iter());
834 additional_methods.extend(pinit_method.into_iter());
835 additional_methods.extend(sinit_method.into_iter());
836 additional_methods.extend(linit_method.into_iter());
837 additional_methods.extend(cinit_method.into_iter());
839 let mut methods = emit_method::from_asts(alloc, emitter, ast_class, &ast_class.methods)?;
840 methods.extend(additional_methods.into_iter());
841 let (ctxconsts, tconsts): (Vec<_>, Vec<_>) =
842 ast_class.typeconsts.iter().partition(|x| x.is_ctx);
843 let type_constants = tconsts
845 .map(|x| from_type_constant(alloc, emitter, x))
846 .collect::<Result<Vec<HhasTypeConstant>>>()?;
847 let ctx_constants = ctxconsts
849 .map(|x| from_ctx_constant(x))
850 .collect::<Result<Vec<HhasCtxConstant>>>()?;
851 let upper_bounds = emit_body::emit_generics_upper_bounds(alloc, &ast_class.tparams, &[], false);
853 if !no_xhp_attributes {
854 properties.extend(emit_xhp::properties_for_cache(
855 alloc, emitter, ast_class, is_const,
858 let info = emit_memoize_method::make_info(ast_class, name, &ast_class.methods)?;
859 methods.extend(emit_memoize_method::emit_wrapper_methods(
866 let doc_comment = ast_class.doc_comment.clone();
867 let is_xhp = ast_class.is_xhp || ast_class.has_xhp_keyword;
869 let mut flags = HhasClassFlags::empty();
870 flags.set(HhasClassFlags::IS_FINAL, is_final);
871 flags.set(HhasClassFlags::IS_SEALED, is_sealed);
872 flags.set(HhasClassFlags::IS_ABSTRACT, is_abstract);
873 flags.set(HhasClassFlags::IS_INTERFACE, is_interface);
874 flags.set(HhasClassFlags::IS_TRAIT, is_trait);
875 flags.set(HhasClassFlags::IS_XHP, is_xhp);
876 flags.set(HhasClassFlags::IS_CONST, is_const);
877 flags.set(HhasClassFlags::NO_DYNAMIC_PROPS, no_dynamic_props);
878 flags.set(HhasClassFlags::NEEDS_NO_REIFIEDINIT, needs_no_reifiedinit);
911 pub fn emit_classes_from_program<'a, 'arena>(
912 alloc: &'arena bumpalo::Bump,
913 emitter: &mut Emitter<'arena>,
914 tast: &'a [tast::Def],
915 ) -> Result<Vec<HhasClass<'a, 'arena>>> {
917 .filter_map(|class| {
918 if let tast::Def::Class(cd) = class {
919 Some(emit_class(alloc, emitter, cd))